/* PMAC.C a Linux modular char driver for an ISA PMAC servo controller. Copyright (C) 1999 Michael Ashley. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA You can contact the author at mcba@newt.phys.unsw.edu.au and Dr. Michael Ashley, School of Physics, University of New South Wales, Sydney NSW 2052, Australia. This is a Linux modular char driver for the ISA bus implemention of a PMAC servo controller from Delta Tau Systems, Inc. The driver has the following properties: - Its uses interrupt-driver communication, with ring buffers for reading/writing to/from the PMAC. - It provides autodetection of the PMAC's IO base and IRQ number, with the ability to explicitly set each, if desired, when the module is loaded. NOTE that autodetection of the IO base is not easy, since the PMAC can be set to a wide variety of addresses; so, you need to set a list of the possible IO bases in the source code (search for "pmac_possible_port_bases"). - It dynamically allocates its major device number. - It fully implements the char operations read/write/select/open/release/fsync, and provides a minimal lseek. - read/write are provided with blocking and non-blocking interfaces. Compile with: cc -D__KERNEL__ -DMODULE -DNDEBUG -O -Wall -Wstrict-prototypes -I/usr/include -c pmac.c -o pmac.o Compile-time defines: NDEBUG if not defined, the source includes additional "assert" tests. For the production code, this should be defined. Here is a Makefile: INCLUDEDIR = /usr/include CFLAGS = -D__KERNEL__ -DMODULE -O -Wall -Wstrict-prototypes -I$(INCLUDEDIR) # Extract version number from headers. VER = $(shell awk -F\" '/REL/ {print $$2}' $(INCLUDEDIR)/linux/version.h) OBJS = pmac.o all: $(OBJS) Usage: To load the module, e.g., /sbin/insmod pmac.o /sbin/insmod pmac.o io=0x210 /sbin/insmod pmac.o io=0x210 irq=10 To unload the module: /sbin/rmmod pmac To examine messages from the module: /bin/dmesg - io= does not work on insmod line, you need to use pmac_port_base=, should change this... - should check the low-level stuff against the Delta Tau code. - As a last-ditch attempt to avoid ring_full, could wake-up read/writers before a byte is lost... - Hm... shouldn't really allow two processes to read/write at once. - Need to be able to trigger and wait for a interrupt, for finding the line. - Need a way of probing the IO ports to see if a PMAC is present. - Need ioctl to change the interrupts that are handled. - Need to enforce access?: only one open at a time? - reentrant code when "schedule()" used - check select operation for blocking - check DANGER - attribute sources (e.g., kernel book, pmac manual) - give info on how to change for different kernels Change log: 06-Apr-2002 / Michael Ashley / Replaced inteeruptible_sleep_on with wait_event_interruptible, as per http://uwsg.iu.edu/hypermail/linux/kernel/0204.0/0709.html 02-Apr-2002 / Michael Ashley / Removed simulator and pre-2.4 support 26-May-2001 / Michael Ashley / Ported to kernel 2.4 19-Jun-2000 / Michael Ashley / Moved pmac_initialize inside the IRQ testing loop. */ #define PMAC2 // DANGER #include #define VERSION_CODE(vers,rel,seq) (((vers)<<16) | ((rel)<<8) | (seq)) #if LINUX_VERSION_CODE < VERSION_CODE(2,4,0) #error This module will not work with kernels older than 2.4.0. #endif #if LINUX_VERSION_CODE > VERSION_CODE(2,5,0) #error This module requires modification to work with kernels newer than 2.4. #endif // Here is an implementation of "assert" for modules. It does not // abort the program, like a real one should, but does leave messages // in the system log. Use "dmesg" occasionally to check for these. #ifdef NDEBUG #define assert(expr) ((void) 0) #else #define assert(expr) ((void) ((expr) ? 0 : \ printk (KERN_ALERT "ASSERTION FAILED: %s, %s at line %i in %s\n", \ #expr, __FILE__, __LINE__, \ __PRETTY_FUNCTION__))) #endif // OPERATING SYSTEM DATA TYPES THAT WILL CHANGE IN FUTURE KERNELS ------------ #define lseek_t loff_t #define read_write_t ssize_t #define count_t long #define release_t int #define release_return(value) return(value) // "#include" FILES ---------------------------------------------------------- #include #include #include #include #include #include #include #include #include void pmac_test_isr (void); void pmac_bottom_half (unsigned long unused); DECLARE_TASKLET (pmac_task, pmac_bottom_half, 0); #ifdef PMAC1 static char *pmac_name = "PMAC"; #endif #ifdef PMAC2 static char *pmac_name = "PMAC2"; #endif static char *pmac_version = "020406a"; static int io; static int irq; // "#defines" THAT THE USER MAY WANT TO CHANGE ------------------------------- // WARNING: both ring buffers must be at least 2 bytes long. #define PMAC_RING_I_BUFFER_LENGTH 4096 #define PMAC_RING_O_BUFFER_LENGTH 4096 #define PMAC_RING_MAX_BYTES_BEFORE_YIELDING 20 #define PMAC_START_CHAR '<' #define PMAC_STOP_CHAR '>' #define PMAC_LOOKING_FOR_START 0 #define PMAC_LOOKING_FOR_STOP 1 static int pmac_receive_state = PMAC_LOOKING_FOR_START; static char pmac_status[2][120]; static int pmac_status_i; static int pmac_status_j; // "#defines" FOR IMMUTABLE SOFTWARE USE ------------------------------------- #define PMAC_RING_POLICY_DROP 0 #define PMAC_RING_POLICY_OVERWRITE 1 #define PMAC_RING_POLICY_WAIT 2 #define PMAC_RING_I_EMPTY \ (pmac_ring_i_head == pmac_ring_i_tail) #define PMAC_RING_I_FULL \ (1 == (pmac_ring_i_tail + PMAC_RING_I_BUFFER_LENGTH - pmac_ring_i_head) % \ PMAC_RING_I_BUFFER_LENGTH) #define PMAC_RING_O_EMPTY \ (pmac_ring_o_head == pmac_ring_o_tail) #define PMAC_RING_O_FULL \ (1 == (pmac_ring_o_tail + PMAC_RING_O_BUFFER_LENGTH - pmac_ring_o_head) % \ PMAC_RING_O_BUFFER_LENGTH) // "#defines" FOR IMMUTABLE PMAC PROPERTIES ---------------------------------- #define PMAC_IO_EXTENT (0x10) /* The range in bytes of IO space used by the PMAC */ #define PMAC (io) /* The PMAC's IO base address */ #define PMAC_STATUS (io + 2) /* Bus status byte */ #define PMAC_HI_BYTE (io + 5) /* High byte transmit/receive data */ #define PMAC_MI_BYTE (io + 6) /* Middle byte transmit/receive data */ #define PMAC_DATA (io + 7) /* Transmit/receive data */ #ifdef PMAC1 #define PMAC_PIC00 (io + 8) /* 8259 interrupt controller word 0 */ #define PMAC_PIC01 (io + 9) /* " " " word 1 */ #define PMAC_PIC02 (io + 10) /* Interrupt acknowledge word */ #endif // DANGER #ifdef PMAC2 #define PMAC_PIC00 (io + 8) /* 8259 interrupt controller word 0 */ #define PMAC_PIC01 (io + 9) /* " " " word 1 */ #define PMAC_PIC02 (io + 10) /* Mask */ #define PMAC_PIC03 (io + 11) /* Edge/level */ #endif #define PMAC_RECEIVE 2 /* Ready to receive bit */ #define PMAC_SEND 1 /* Ready to send bit */ #define PMAC_INTERNAL_IRQ 4 /* 0-7 IRQ0-IRQ7 */ #define PMAC_MASK (~(1<= initial_jiffy) { initial_jiffy = -1; // no unsigned long is greater than this number } // Loop until either the PMAC is ready, or the timeout has expired. // Note that we call "schedule" in the loop, so as not to hog the CPU. // We also allow for overflow of "jiffies", a very unlikely event... do { if (PMAC_IS_READY_TO_RECEIVE) { outb_p (outchar, PMAC_DATA); return 0; } schedule (); time = jiffies; } while ((time < timeout) || (time > initial_jiffy)); return -EWOULDBLOCK; } int pmac_receive_char (unsigned char *inchar) { // Only called by "pmac_process_ring_buffers ()". assert (have_registered_device && have_requested_region && have_irq); if (PMAC_IS_SENDING) { *inchar = inb_p (PMAC_DATA); switch (pmac_receive_state) { case PMAC_LOOKING_FOR_START: if (*inchar == PMAC_START_CHAR) { pmac_receive_state = PMAC_LOOKING_FOR_STOP; pmac_status_j = 0; } break; case PMAC_LOOKING_FOR_STOP: if (*inchar == PMAC_STOP_CHAR) { pmac_receive_state = PMAC_LOOKING_FOR_START; pmac_status[pmac_status_i][pmac_status_j] = 0; pmac_status_i = (pmac_status_i + 1) % 2; // Notify any processes that have requested asynchronous // notification. DANGER: is this OK at interrupt time? // DANGER: sometime in kernel 2.2, a third argument // "band" was added to this function. I don't know what it // does! See /usr/src/linux/fs/fcntl.c, and // "grep kill_fasync /usr/src/linux/drivers/*/*.c" if (pmac_async_queue) { kill_fasync (&pmac_async_queue, SIGIO, POLL_IN); } } else if (*inchar == PMAC_START_CHAR) { pmac_status_j = 0; } else { if (pmac_status_j < sizeof (pmac_status) / 2 - 1) { pmac_status[pmac_status_i][pmac_status_j] = *inchar; pmac_status_j++; } else { // assert (0); // DANGER: should handle this in a nicer way. } } break; default: assert (0); } return 0; } return -EWOULDBLOCK; } void pmac_process_ring_buffers (void) { // WARNING: this routine is not reentrant. It should only be called // by "pmac_bottom_half ()", and only by being scheduled on the // IMMEDIATE queue. This guarantees (I hope) that only one copy of // it will be running at any instant time. Note that we check this // using an assertion. // When this routine runs, the CPU can only handle hardware // interrupts. "schedule ()" can not be called. We therefore should // ensure that this routine is very fast, and does not wait for // events. // DANGER: to avoid race conditions this routine should not alter // pmac_ring_i_tail or pmac_ring_o_head. However, we do not follow // this rule at one point... a cause for concern. unsigned char byte; int i; int ring_was_full; assert (!processing_ring_buffers); processing_ring_buffers = 1; assert (have_registered_device && have_requested_region && have_irq); assert (pmac_setup_to_use_interrupts); // Note that we only allow up to PMAC_RING_MAX_BYTES_BEFORE_YIELDING // bytes of data from the PMAC to be placed into the input ring // buffer during a single invocation of this routine. The reason for // doing this is to avoid a potential endless loop, in the event // that the PMAC is sending bytes faster than we can consume them. // There are three "policies" implemented for what to do when the // input ring buffer becomes full: // PMAC_RING_POLICY_DROP: lose the new data. // PMAC_RING_POLICY_OVERWRITE: overwrite the oldest data. // PMAC_RING_POLICY_WAIT: refuse to accept any bytes from the PMAC // until the buffer has room. // The last policy will gain us some breathing space, since the // PMAC's own internal output buffer will hold the overflow for a // while. However, since we could have achieved the same effect by // adding 256 bytes to our ring buffer, it probably makes sense to use // the OVERWRITE policy. switch (pmac_ring_i_policy) { case PMAC_RING_POLICY_DROP: // Loop, obtaining one byte at a time, until either the maximum // number of bytes has been obtained, or there are no more bytes // immediately available from the PMAC. for (i = 0; i < PMAC_RING_MAX_BYTES_BEFORE_YIELDING; i++) { if (pmac_receive_char (&byte)) break; // Only store the byte if the ring is not full. if (!PMAC_RING_I_FULL) { pmac_ring_i_buffer[pmac_ring_i_head] = byte; pmac_ring_i_head = (pmac_ring_i_head + 1) % PMAC_RING_I_BUFFER_LENGTH; // If the ring is now full, we increment an error counter. if (PMAC_RING_I_FULL) { pmac_errors.ring_i_full++; printk (KERN_ALERT "pmac: ring full\n"); // DANGER } } } break; case PMAC_RING_POLICY_OVERWRITE: // Loop, obtaining one byte at a time, until either the maximum // number of bytes has been obtained, or there are no more bytes // immediately available from the PMAC. for (i = 0; i < PMAC_RING_MAX_BYTES_BEFORE_YIELDING; i++) { // We immediately store the byte in the ring buffer, since even // if the buffer is full, we will be overwriting the old data. if (pmac_receive_char (&(pmac_ring_i_buffer[pmac_ring_i_head]))) { break; } pmac_ring_i_head = (pmac_ring_i_head + 1) % PMAC_RING_I_BUFFER_LENGTH; // If the ring now appears to be empty, it means that the byte just // written has over-filled the buffer, so we need to adjust the tail // pointer. if (PMAC_RING_I_EMPTY) { pmac_ring_i_tail = (pmac_ring_i_tail + 1) % PMAC_RING_I_BUFFER_LENGTH; } else { // The first time that the ring is full, we increment the error // counter. if (PMAC_RING_I_FULL) { pmac_errors.ring_i_full++; printk (KERN_ALERT "pmac: ring full\n"); // DANGER } } } break; case PMAC_RING_POLICY_WAIT: // Leave immediately if the input ring buffer is full. if (PMAC_RING_I_FULL) break; // Now loop, obtaining one byte at a time, until either the // maximum number of bytes has been obtained, there are no more // bytes immediately available from the PMAC, or the ring buffer // becomes full. for (i = 0; i < PMAC_RING_MAX_BYTES_BEFORE_YIELDING; i++) { // Note that we immediately store the byte in the ring buffer, // since we know that it is not full. if (pmac_receive_char (&(pmac_ring_i_buffer[pmac_ring_i_head]))) { break; } // Increment the ring buffer pointer, and check if the buffer is // now full (and if so, increment the error counter). pmac_ring_i_head = (pmac_ring_i_head + 1) % PMAC_RING_I_BUFFER_LENGTH; if (PMAC_RING_I_FULL) { pmac_errors.ring_i_full++; printk (KERN_ALERT "pmac: ring full\n"); // DANGER break; } } break; // Control should never reach here. default: assert (0); } // Now process the output ring buffer. // printk (KERN_ALERT "pmac: process ring\n"); ring_was_full = PMAC_RING_O_FULL; for (i = 0; i < PMAC_RING_MAX_BYTES_BEFORE_YIELDING; i++) { //printk (KERN_ALERT "pmac: hello\n"); if (PMAC_RING_O_EMPTY) break; //printk (KERN_ALERT "pmac: hello 2\n"); if (pmac_send_char (pmac_ring_o_buffer[pmac_ring_o_tail])) { break; } //printk (KERN_ALERT "pmac: tail %ld\n", pmac_ring_o_tail); pmac_ring_o_tail = (pmac_ring_o_tail + 1) % PMAC_RING_O_BUFFER_LENGTH; } // If the output ring was previously full, and at least 1 byte has been // sent, then mark the ring as no longer being full, and wake up any // writing processes. if (ring_was_full && i > 0) { wake_up_interruptible (&pmac_outq); } // If the input ring has data in it, wake up any reading processes. if (!PMAC_RING_I_EMPTY) { wake_up_interruptible (&pmac_inq); } // If we were not able to empty the output ring, reschedule this routine. if (!PMAC_RING_O_EMPTY) { // printk (KERN_ALERT "pmac: rescheduling\n"); tasklet_schedule(&pmac_task); } processing_ring_buffers = 0; } read_write_t pmac_consume_ring_i_buffer (unsigned char *buf, count_t max_bytes) { /* Attempts to read up to "max_bytes" from the PMAC input ring buffer. The bytes are placed into the array "buf". The special case of "max_bytes" equal to zero is handled correctly. Returns: The number of bytes actually placed into "buf". This may be less than "max_bytes", if there was insufficient data in the ring buffer. Michael Ashley / UNSW / 08-Jan-1999 */ read_write_t ul; assert (have_registered_device && have_requested_region && have_irq); // Prepare to extract at most "max_bytes" from the ring buffer. for (ul = 0; ul < max_bytes; ul++) { // If the buffer is empty, leave the loop. Note that an interrupt // may occur during the calculation of PMAC_RING_I_EMPTY, with the // result that the ring buffer is no longer empty. This does not // matter, provided that the calling function realizes this. if (PMAC_RING_I_EMPTY) { break; } // Extract a byte from the ring buffer, and update the tail pointer. buf[ul] = pmac_ring_i_buffer[pmac_ring_i_tail]; // During the update of "pmac_ring_i_tail" we have to disable // interrupts, since "pmac_process_ring_buffers" (which runs at // interrupt time) can alter this variable if the ring buffer // becomes full. Note that the contents of "buf" will be // scrambled, but that is an inevitable consequence of filling the // buffer. cli (); pmac_ring_i_tail = (pmac_ring_i_tail + 1) % PMAC_RING_I_BUFFER_LENGTH; sti (); } // Return the number of bytes actually extracted. return ul; } int pmac_send_line (unsigned char *line) { /* Sends a (null-terminated) string of bytes to the PMAC. It is the user's responsibility to add a \r byte if desired (to indicate the end of the PMAC command). This function returns zero on success, and a negative number on failure (a timeout from "pmac_send_char_wait"). Michael Ashley / UNSW / 02-Jan-1999 */ int ret; assert (have_registered_device && have_requested_region && !have_irq); while (*line != 0) { if ((ret = pmac_send_char_wait (*(line++))) != 0) return ret; } return 0; } int pmac_purge_buffer (void) { int i, j, ret; assert (have_registered_device && have_requested_region && !have_irq); // Send a control-X (ASCII code 24 decimal) to purge the PMAC buffers: if ((ret = pmac_send_char_wait (24)) != 0) return ret; // We now wait for the control-X to have an effect. // DANGER: I am not sure if the scheduler copes correctly with jiffy overflow. for (i = 0; i < 2; i++) { schedule_timeout (PMAC_CONTROLX_TIMEOUT * HZ); // Now perform some dummy reads to clear the two ASCII characters that may // be buffered, but unable to be cleared with control-X. for (j = 0; j < 3; j++) { if (PMAC_IS_SENDING) { inb_p (PMAC_DATA); } } } return 0; } int pmac_initialize (void) { /* Returns 0 on success, a negative number on error. */ int ret; assert (have_registered_device && have_requested_region && !have_irq); // Here we initialise the PMAC, following the guidelines in the PMAC FAQ. // Step 1. Write a value of zero to the high and middle byte I/O ports. // We use the "pausing" versions of the "outb" functions, to be // conservative. outb_p (0, PMAC_HI_BYTE); outb_p (0, PMAC_MI_BYTE); // Steps 2 through 4 are handled by the following function: if ((ret = pmac_purge_buffer ()) != 0) return ret; // Step 5. Issue a test to confirm communications to PMAC have been // established. // DANGER: The return string is not tested. if ((ret = pmac_send_line ("RHL:$720\r")) != 0) return ret; // Step 6. If the test failed, increase the timeout, issue a control-d // and go back to step 5. // DANGER: Not implemented. // Now select mode 1 communications. if ((ret = pmac_send_line ("i3=1\r")) != 0) return ret; //----------- At this point, the PMAC is initialised. ---------------// have_initialized_pmac = 1; return 0; } void pmac_isr (int irq, void *dev_id, struct pt_regs *regs) { // This is the interrupt service routine to handle interrupts // generated by the PMAC. // It is based on the example programs in chapter 9 of the PMAC // Software Reference, August 96 version. Note that it does not // agree with the description in chapter 8 of the PMAC User's // Manual, 1 Nov 97 version (e.g., the order in which the operations // are performed is different, and the bytes written are different). // All this routine does is to acknowledge the interrupt, ask the // PMAC what the interrupt was for, and then schedule a bottom half // to handle any further processing. #ifdef PMAC1 outb_p (PMAC_NOP, PMAC_PIC02); // Acknowledge the interrupt via INTA. outb_p ( 0x0A, PMAC_PIC00); // Prepare to read the IRR register. pmac_irr |= inb_p (PMAC_PIC00); // Read the IRR register. NOTE that we // OR it with the previous value, // to ensure that earlier events aren't // lost. outb_p (PMAC_NOP, PMAC_PIC02); // Acknowledge the interrupt again, why? outb_p (PMAC_EOI, PMAC_PIC00); // Send end-of-interrupt to PMAC's 8259. #endif #ifdef PMAC2 pmac_irr |= inb_p (PMAC_PIC00); // Read the ISR register. NOTE that we // OR it with the previous value, // to ensure that earlier events aren't // lost. outb_p ( 0x00, PMAC_PIC00); // Clear the ISR register. #endif // Schedule a tasklet to perform the more time-consuming tasks. //printk (KERN_ALERT "pmac: interrupt\n"); tasklet_schedule(&pmac_task); } void pmac_bottom_half (unsigned long unused) { // The following helps with shutting down this module. We do not // want to release the module until all tasks are complete. // assert (task_scheduled); //task_scheduled = 0; if (!pmac_setup_to_use_interrupts) return; // WARNING: interrupts are enabled for the duration of this routine. // This has some important consequences. Specifically, it is // possible for bits to be set in "pmac_irr" at any time. This is // why we explicitly clear individual bits after testing them rather // than clearing all the bits after testing all of them. With this // approach we can still miss multiple occurences of the same bit, // but this is not important for our application. // DANGER the above is wrong since I've added the following: disable_irq (irq); // printk (KERN_ALERT "pmac: %x\n", pmac_irr); assert (have_registered_device && have_requested_region && have_irq); // Note: we are not doing a clever job looking at the reason for the // interrupts. We call pmac_process_ring_buffer regardless. if (pmac_irr & PMAC_HREQ) { pmac_irr &= ~PMAC_HREQ; // Clear the flag. pmac_process_ring_buffers (); } else { pmac_process_ring_buffers (); // DANGER, added Apr 2, 2002 // WARNING: if you change this, you need to change pmac_select as well. pmac_irr &= ~PMAC_HREQ; // EXTREME DANGER: what is going on here?? wake_up_interruptible (&pmac_exq); } enable_irq (irq); } void pmac_test_isr (void) { unsigned long flags; save_flags (flags); cli (); pmac_isr (0, NULL, NULL); restore_flags (flags); } int pmac_read_procmem(char *buf, char **start, off_t offset, int len) { #if 0 int i; i = PMAC_RING_O_EMPTY; len = sprintf (buf, "%s device driver, version %s,%d, %ld, %ld, %ld, %ld, %d, %d, %d; <%s>\n", pmac_name, pmac_version, i, pmac_errors.chars_out, pmac_errors.out,pmac_errors.out2, pmac_errors.busy, pmac_errors.ring_i_full, pmac_errors.ring_o_full, pmac_errors.ring_o_full2, pmac_status[(pmac_status_i+1)%2]); #endif len = sprintf (buf, "%s device driver, version %s; <%s>\n", pmac_name, pmac_version, pmac_status[(pmac_status_i+1)%2]); return len; } int pmac_open (struct inode *inode, struct file *filp) { assert (have_registered_device && have_requested_region && have_irq); MOD_INC_USE_COUNT; return 0; } release_t pmac_release (struct inode *inode, struct file *filp) { assert (have_registered_device && have_requested_region && have_irq); // Remove this filp from the asynchronously notified filp's. pmac_fasync (-1, filp, 0); // DANGER: is "-1" correct? MOD_DEC_USE_COUNT; release_return(0); } read_write_t pmac_read (struct file *filp, char *buf, size_t count, loff_t *ppos) { unsigned char local_buf[256]; read_write_t bytes; int ret; assert (have_registered_device && have_requested_region && have_irq); if (ppos != &filp->f_pos) { return -ESPIPE; } // Reduce "count" if it exceeds the size of "local_buf". if (count > sizeof local_buf) { count = sizeof local_buf; } // Attempt to retrieve the requested number of bytes from the ring. while ((bytes = pmac_consume_ring_i_buffer (local_buf, count)) == 0) { if (filp->f_flags & O_NONBLOCK) { return -EAGAIN; } ret = wait_event_interruptible(pmac_inq, !PMAC_RING_I_EMPTY); if (ret) { return ret; } } // Copy the data to user space, and return the number of bytes copied. if (copy_to_user (buf, &local_buf, bytes)) { return -EFAULT; } return bytes; } read_write_t pmac_write (struct file *filp, const char *buf, size_t count, loff_t *ppos) { unsigned char local_buf[PMAC_RING_MAX_BYTES_BEFORE_YIELDING]; count_t i; int ret; assert (have_registered_device && have_requested_region && have_irq); if (ppos != &filp->f_pos) { return -ESPIPE; } while (PMAC_RING_O_FULL) { if (filp->f_flags & O_NONBLOCK) { return -EAGAIN; } ret = wait_event_interruptible(pmac_outq, !PMAC_RING_O_FULL); if (ret) { return ret; } } // At this point, we have room for at least one byte in the output // ring buffer. if (count > sizeof local_buf) { count = sizeof local_buf; } if (copy_from_user (local_buf, buf, count)) { return -EFAULT; } // Note that the output ring buffer may be being consumed through // interrupts while this routine is executing, i.e., // "pmac_ring_o_tail" may be changing. This isn't a problem. Also // note that this is the only place that "pmac_ring_o_head" is // altered. for (i = 0; i < count; i++) { pmac_ring_o_buffer[pmac_ring_o_head] = local_buf[i]; pmac_ring_o_head = (pmac_ring_o_head + 1) % PMAC_RING_O_BUFFER_LENGTH; pmac_errors.out2++; if (PMAC_RING_O_FULL) { pmac_errors.ring_o_full2++; i++; break; } } cli (); pmac_irr |= PMAC_HREQ; sti (); pmac_test_isr (); // DANGER: for testing only. // printk (KERN_ALERT "pmac: %ld bytes put in out buf from %i, %ld\n", i, count, // pmac_ring_o_head-pmac_ring_o_tail); pmac_errors.out += i; return i; } int pmac_fsync(struct file *filp, struct dentry *d, int datasync) { /* Guarantees that all buffered data has been sent to the PMAC. Michael Ashley / UNSW / 09-Jan-1999 */ // EXTREME DANGER: this may not be right yet, we probably need another wait // queue entry, to be triggered when the output ring is empty? // Alternatively, do we really need an fsync? return wait_event_interruptible(pmac_outq, PMAC_RING_O_EMPTY); } int pmac_fasync (int fd, struct file *filp, int mode) { /* Michael Ashley / UNSW / 26-Mar-1999 */ return fasync_helper (fd, filp, mode, &pmac_async_queue); } unsigned int pmac_poll (struct file *filp, poll_table *table) { unsigned int result = 0; assert (have_registered_device && have_requested_region && have_irq); poll_wait (filp, &pmac_inq, table); poll_wait (filp, &pmac_outq, table); poll_wait (filp, &pmac_exq, table); cli (); // DANGER: I'm not sure if this is necessary, see comments in // pmac_select above. if (!PMAC_RING_I_EMPTY) { result = result || POLLIN || POLLRDNORM; } if (!PMAC_RING_O_FULL) { result = result || POLLOUT || POLLWRNORM; } if (pmac_irr & ~PMAC_HREQ) { result = result || POLLERR; } sti (); return (result); } char junk[120]; #define PMAC_IOC_MAGIC 'v' #define PMAC_IOCGSTATUS _IOR(PMAC_IOC_MAGIC, 0, junk) #define PMAC_IOC_MAXNR 0 int pmac_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { int err = 0, size; size = _IOC_SIZE(cmd); if (_IOC_TYPE(cmd) != PMAC_IOC_MAGIC) return -EINVAL; if (_IOC_NR(cmd) > PMAC_IOC_MAXNR) return -EINVAL; if (_IOC_DIR(cmd) & _IOC_READ) { err = verify_area (VERIFY_WRITE, (void *)arg, size); } else if (_IOC_DIR(cmd) & _IOC_WRITE) { err = verify_area (VERIFY_READ, (void *)arg, size); } if (err) return err; switch (cmd) { case PMAC_IOCGSTATUS: if (copy_to_user ((void *)arg, pmac_status[(pmac_status_i+1)%2], size)) { return -EFAULT; } break; default: assert (0); } return 0; } lseek_t pmac_llseek (struct file *filp, loff_t off, int whence) { return -ESPIPE; // Unseekable. } static int pmac_find_hardware (void) { /* Returns zero for success, and a negative number on error. */ int result; int port; int i; int count; assert (have_registered_device && !have_requested_region && !have_irq); result = 0; io = 0; if (pmac_port_base != 0) { pmac_possible_port_bases[0] = pmac_port_base; pmac_possible_port_bases[1] = 0; } printk (KERN_NOTICE "pmac: / module loading, version %s\n", pmac_version); printk (KERN_NOTICE "pmac: | probing for io base..."); for (i = 0; pmac_possible_port_bases[i]; i++) { port = pmac_possible_port_bases[i]; if (!check_region (port, PMAC_IO_EXTENT)) { //DANGER: this fails under // kernel 2.4 for some reason (e.g, it fails to reject 03c0, which is // already used by vga+ printk ("trying 0x%x, ", port); /* Do something to check whether there is a PMAC at "port". For the time being, we assume that there is one. */ io = port; printk ("%s found\n", pmac_name); break; } else { printk ("skipping 0x%x, ", port); } } if (io == 0) { printk ("%s not found\n", pmac_name); return -ENODEV; } #ifdef PMAC1 request_region (io, PMAC_IO_EXTENT, "pmac1"); #endif #ifdef PMAC2 request_region (io, PMAC_IO_EXTENT, "pmac2"); #endif have_requested_region = 1; // We now go searching for the IRQ. printk (KERN_NOTICE "pmac: | probing for IRQ "); count = 0; do { unsigned long mask; // Note: I moved pmac_initialize inside this loop, since the // pmac would sometimes fail to generate an IRQ. have_initialized_pmac = 0; result = pmac_initialize (); if (result < 0) { printk (KERN_CRIT "pmac: | initialization failure\n"); return result; } mask = probe_irq_on (); // Now force the PMAC to generate an interrupt. We identify which // one it was with the help of the probe ISR. We try up to five // times before giving up. pmac_setup_interrupt_controller (); pmac_trigger_interrupt (); pmac_wait_for_interrupt (); pmac_disable_interrupt_controller (); irq = probe_irq_off (mask); // The following "printk ()" gives some information as to how many // times the IRQ had to be searched for. Normally, you would not see // any dashes. if (irq == 0) { printk ("-"); } } while (irq <= 0 && count++ < 5); // irq = 10; // EXTREME DANGER! if (irq <= 0) { printk (" IRQ not found\n"); return -ENODEV; } // Now that we have identified the IRQ line, we request access to // the IRQ from the operating system, and then setup the PMAC // interrupt controller. #ifdef PMAC1 have_irq = 0 == request_irq (irq, pmac_isr, SA_INTERRUPT || SA_SAMPLE_RANDOM, "pmac1", NULL); #endif #ifdef PMAC2 have_irq = 0 == request_irq (irq, pmac_isr, SA_INTERRUPT || SA_SAMPLE_RANDOM, "pmac2", NULL); #endif if (!have_irq) { printk (KERN_INFO "pmac: | can't get assigned irq %i\n", irq); return -ENODEV; } pmac_setup_interrupt_controller (); printk (", found at %i\n", irq); printk (KERN_NOTICE "pmac: | %s successfully initialized\n", pmac_name); return 0; } void cleanup_module (void) { printk (KERN_NOTICE "pmac: \\ module unloading, "); flush_scheduled_tasks(); remove_proc_entry("pmac", NULL); if (have_requested_region) { // Stop the PMAC from causing interrupts. Note that we can only do // this if we have requested an IO region (else we don't know what // the PMAC IO base address is). pmac_disable_interrupt_controller (); // Ensure that all queued tasks have completed. if (task_scheduled) { printk ("waiting, "); } while (task_scheduled) { schedule (); } // Release the IO address registers. release_region (io, PMAC_IO_EXTENT); printk ("io region, "); } // Release the PC's IRQ. if (have_irq) { free_irq (irq, NULL); printk ("irq, "); } // Release the major device number. if (have_registered_device) { unregister_chrdev (pmac_major, "pmac"); printk ("device number, "); } printk ("shutdown complete\n"); } int init_module (void) { int result; init_waitqueue_head(&pmac_inq); init_waitqueue_head(&pmac_outq); init_waitqueue_head(&pmac_exq); // Attempt to register the major node number of the PMAC. If // "pmac_major" is zero, we obtain a dynamically allocated major // node number. result = register_chrdev (pmac_major, "pmac", &pmac_fops); if (result < 0) { printk (KERN_WARNING "pmac: | can't get major number %d\n", pmac_major); cleanup_module (); return result; } if (pmac_major == 0) pmac_major = result; have_registered_device = 1; // Now try to identify the PMAC's io base address and hardware IRQ, and // then initialize the PMAC. result = pmac_find_hardware (); if (result < 0) { cleanup_module (); return result; } create_proc_info_entry("pmac", 0, NULL, pmac_read_procmem); // Success! return 0; } MODULE_LICENSE("GPL"); MODULE_AUTHOR("Michael Ashley "); MODULE_DESCRIPTION("Driver for Delta Tau PMAC");