patch-2.4.0-test2 linux/Documentation/DocBook/mousedrivers.tmpl

Next file: linux/Documentation/DocBook/via-audio.tmpl
Previous file: linux/Documentation/DocBook/kernel-locking.tmpl
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.0-test1/linux/Documentation/DocBook/mousedrivers.tmpl linux/Documentation/DocBook/mousedrivers.tmpl
@@ -0,0 +1,1022 @@
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V3.1//EN"[]>
+
+<book id="MouseGuide">
+ <bookinfo>
+  <title>Mouse Drivers</title>
+  
+  <authorgroup>
+   <author>
+    <firstname>Alan</firstname>
+    <surname>Cox</surname>
+    <affiliation>
+     <address>
+      <email>alan@redhat.com</email>
+     </address>
+    </affiliation>
+   </author>
+  </authorgroup>
+
+  <copyright>
+   <year>2000</year>
+   <holder>Alan Cox</holder>
+  </copyright>
+
+  <legalnotice>
+   <para>
+     This documentation 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.
+   </para>
+      
+   <para>
+     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.
+   </para>
+      
+   <para>
+     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
+   </para>
+      
+   <para>
+     For more details see the file COPYING in the source
+     distribution of Linux.
+   </para>
+  </legalnotice>
+ </bookinfo>
+
+ <toc></toc>
+
+ <chapter id="intro">
+  <title>Introduction</title>
+  <note>
+   <title>Earlier publication</title>
+    <para>
+      Parts of this document first appeared in Linux Magazine under a
+      ninety day exclusivity.
+   </para>
+  </note> 
+
+  <para>
+    Mice are conceptually one of the simplest device interfaces in the 
+    Linux operating system. Not all mice are handled by the kernel. 
+    Instead there is a two layer abstraction.
+  </para>
+
+  <para>
+    The kernel mouse drivers and userspace drivers for the serial mice are
+    all managed by a system daemon called <application>gpm</application> 
+    - the general purpose mouse driver. <application>gpm</application> 
+    handles cutting and pasting on the text consoles. It provides a 
+    general library for mouse-aware applications and it handles the 
+    sharing of mouse services with the 
+    <application>X Window System</application> user interface.
+  </para>
+  <para>
+    Sometimes a mouse speaks a sufficiently convoluted protocol that the
+    protocol is handled by <application>Gpm</application> itself. Most 
+    of the mouse drivers follow a common interface called the bus mouse 
+    protocol.
+  </para>
+  <para>
+    Each read from a bus mouse interface device returns a block of data. 
+    The first three bytes of each read are defined as follows: 
+
+   <table frame=all>
+    <title>Mouse Data Encoding</title>
+    <tgroup cols=2 align=left>
+     <tbody>
+      <row>
+       <entry>Byte 0</entry>
+       <entry>0x80 + the buttons currently down.</entry>
+      </row>
+      <row>
+       <entry>Byte 1</entry>
+       <entry>A signed value for the shift in X position</entry>
+      </row>
+      <row>
+       <entry>Byte 2</entry>
+       <entry>A signed value for the shift in Y position</entry>
+      </row>
+     </tbody>
+    </tgroup>
+   </table>
+
+    An application can choose to read more than 3 bytes. The rest of the 
+    bytes will be zero, or may optionally return some additional 
+    device-specific information.
+  </para>
+  <para>
+    The position values are truncated if they exceed the 8bit range (that
+    is -127 &lt;= delta &lt;= 127). While the value -128 does fit into a 
+    byte is not allowed.
+  </para>
+  <para>
+    The <mousebutton>buttons</mousebutton> are numbered left to right as 
+    0, 1, 2, 3.. and each button sets the relevant bit. So a user pressing 
+    the left and right button of a three button mouse will set bits 0 and 2.
+  </para>
+  <para>
+    All mice are required to support the <function>poll</function> 
+    operation. Indeed pretty much every user of a mouse device uses 
+    <function>poll</function> to wait for mouse events to occur.
+  </para>
+  <para>
+    Finally the mice support asynchronous I/O. This is a topic we have not 
+    yet covered but which I will explain after looking at a simple mouse 
+    driver.
+  </para>
+ </chapter>
+
+ <chapter id="driver">
+  <title>A simple mouse driver</title>
+  <para>
+    First we will need the set up functions for our mouse device. To keep 
+    this simple our imaginary mouse device has three I/O ports fixed at I/O 
+    address 0x300 and always lives on interrupt 5.  The ports will be the X 
+    position, the Y position and the buttons in that order.
+  </para>
+
+  <programlisting>
+#define OURMOUSE_BASE        0x300
+
+static struct miscdevice our_mouse = {
+        OURMOUSE_MINOR, "ourmouse", &amp;our_mouse_fops
+};
+
+__init ourmouse_init(void)
+{
+
+        if(check_region(OURMOUSE_BASE, 3))
+                return -ENODEV;
+        request_region(OURMOUSE_BASE, 3, "ourmouse");
+
+        misc_register(&amp;our_mouse);
+        return 0;
+}
+  </programlisting>
+
+  <para>
+    The <structname>miscdevice</structname> is new here. Linux normally 
+    parcels devices out by major number, and each device has 256 units. 
+    For things like mice this is extremely wasteful so a device exists 
+    which is used to accumulate all the odd individual devices that 
+    computers tend to have.
+  </para>
+  <para>
+    Minor numbers in this space are allocated by a central source, although 
+    you can look in the kernel <filename>Documentation/devices.txt</filename>
+    file and pick a free one for development use. This kernel file also 
+    carries instructions for registering a device. This may change over time 
+    so it is a good idea to obtain a current copy of this file first.
+  </para>
+  <para>
+    Our code then is fairly simple. We check nobody else has taken our 
+    address space. Having done so we reserve it to ensure nobody stamps 
+    on our device while probing for other ISA bus devices. Such a probe 
+    might confuse our device.
+  </para>
+  <para>
+    Then we tell the misc driver that we wish to own a minor number. We also
+    hand it our name (which is used in 
+    <filename class="directory">/proc/misc</filename>) and a set of file 
+    operations that are to be used. The file operations work exactly like the 
+    file operations you would register for a normal character device. The misc 
+    device itself is simply acting as a redirector for requests.
+  </para>
+  <para>
+    Next, in order to be able to use and test our code we need to add some 
+    module code to support it. This too is fairly simple:
+  </para>
+  <programlisting>
+#ifdef MODULE
+
+int init_module(void)
+{
+        if(ourmouse_init()&lt;0)
+                return -ENODEV:
+        return 0;
+}
+
+void cleanup_module(void)
+{
+        misc_deregister(&amp;our_mouse);
+        free_region(OURMOUSE_BASE, 3);
+}
+
+
+#endif
+  </programlisting>
+
+  <para>
+    The module code provides the normal two functions. The 
+    <function>init_module</function> function is called when the module is 
+    loaded. In our case it simply calls the initialising function we wrote 
+    and returns an error if this fails. This ensures the module will only 
+    be loaded if it was successfully set up.
+  </para>
+  <para>
+    The <function>cleanup_module</function> function is called when the 
+    module is unloaded. We give the miscellaneous device entry back, and 
+    then free our I/O resources. If we didn't free the I/O resources then 
+    the next time the module loaded it would think someone else had its I/O 
+    space.
+  </para>
+  <para>
+    Once the <function>misc_deregister</function> has been called any 
+    attempts to open the mouse device will fail with the error  
+    <errorcode>ENODEV</errorcode> (<errorname>No such device</errorname>).
+  </para>
+  <para>
+    Next we need to fill in our file operations. A mouse doesn't need many 
+    of these. We need to provide open, release, read and poll. That makes 
+    for a nice simple structure:
+  </para>
+
+  <programlisting>
+struct file_operations our_mouse_fops = {
+        NULL,                   /* Mice don't seek */
+        read_mouse,             /* You can read a mouse */
+        write_mouse,            /* This won't do a lot */
+        NULL,                   /* No readdir - not a directory */
+        poll_mouse,             /* Poll */
+        NULL,                   /* No ioctl calls */
+        NULL,                   /* No mmap */
+        open_mouse,             /* Called on open */
+        NULL,                   /* Flush - 2.2+ only */
+        close_mouse,            /* Called on close */
+};
+  </programlisting>
+
+  <para>
+    There is nothing particularly special needed here. We provide functions 
+    for all the relevant or required operations and little else. There is 
+    nothing stopping us providing an ioctl function for this mouse. Indeed 
+    if you have a configurable mouse it may be very appropriate to provide 
+    configuration interfaces via ioctl calls.
+  </para>
+  <para>
+    The open and close routines need to manage enabling and disabling the 
+    interrupts for the mouse as well as stopping the mouse being unloaded
+    when it is no longer required. 
+  </para>
+
+  <programlisting>
+static int mouse_users = 0;                /* User count */
+static int mouse_dx = 0;                   /* Position changes */
+static int mouse_dy = 0;
+static int mouse_event = 0;                /* Mouse has moved */
+
+static int open_mouse(struct inode *inode, struct file *file)
+{
+        if(mouse_users++)
+                return 0;
+
+	MOD_INC_USE_COUNT;
+
+        if(request_irq(mouse_intr, OURMOUSE_IRQ, 0, "ourmouse", NULL))
+        {
+                mouse_users--;
+	        MOD_DEC_USE_COUNT;
+                return -EBUSY;
+        }
+        mouse_dx = 0;
+        mouse_dy = 0;
+        mouse_event = 0;
+        mouse_buttons = 0;
+	return 0;
+}
+  </programlisting>
+  <para>
+    The open function has to do a small amount of housework. We keep a count 
+    of the number of times the mouse is open. This is because we do not want 
+    to request the interrupt multiple times. If the mouse has at least one 
+    user then it is set up and we simply add to the user count and return
+    <returnvalue>0</returnvalue> for success.
+  </para>
+  <para>
+    Firstly we use <function>MOD_INC_USE_COUNT</function> to ensure that 
+    while the mouse is open nobody will unload it and cause a nasty crash.
+    We must do this before we sleep - and grabbing the interrupt might sleep.
+  </para>
+  <para>
+    We grab the interrupt and thus start mouse interrupts. If the interrupt 
+    has been borrowed by some other driver then <function>request_irq</function>
+    will fail and we will return an error. If we were capable of sharing an 
+    interrupt line we would specify <constant>SA_SHIRQ</constant> instead of 
+    <constant>zero</constant>. Provided that everyone claiming an interrupt 
+    sets this flag, they get to share the line. <hardware>PCI</hardware> can 
+    share interrupts, <hardware>ISA</hardware> normally however cannot. 
+  </para>
+  <para>
+    We do the housekeeping. We make the current mouse position the starting
+    point for accumulated changes and declare that nothing has happened
+    since the mouse driver was opened.
+  </para>
+  <para>
+    The release function needs to unwind all these:
+  </para>
+  <programlisting>
+static int close_mouse(struct inode *inode, struct file *file)
+{
+        if(--mouse_users)
+                return 0;
+        free_irq(OURMOUSE_IRQ, NULL);
+        MOD_DEC_USE_COUNT;
+        return 0;
+}
+  </programlisting>
+  <para>
+    We count off a user and provided that there are still other users need 
+    take no further action. The last person closing the mouse causes us to 
+    free up the interrupt. This stopps interrupts from the mouse from using 
+    our CPU time, and lets us use <function>MOD_DEC_USE_COUNT</function> so 
+    that the mouse can now be unloaded.
+  </para>
+  <para>
+    We can fill in the write handler at this point as the write function for 
+    our mouse simply declines to allow writes:
+  </para>
+
+  <programlisting>
+static ssize_t write_mouse(struct file *file, const char *buffer, size_t
+                                count, loff_t *ppos)
+{
+        return -EINVAL;
+}
+  </programlisting>
+
+  <para>
+    This is pretty much self-explanatory. Whenever you write you get told 
+    it was an invalid function.
+  </para>
+  <para>
+    To make the poll and read functions work we have to consider how we 
+    handle the mouse interrupt. 
+  </para>
+
+  <programlisting>
+static struct wait_queue *mouse_wait;
+static spinlock_t mouse_lock = SPIN_LOCK_UNLOCKED;
+
+static void ourmouse_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+        char delta_x;
+        char delta_y;
+        unsigned char new_buttons;
+
+        delta_x = inb(OURMOUSE_BASE);
+        delta_y = inb(OURMOUSE_BASE+1);
+        new_buttons = inb(OURMOUSE_BASE+2);
+
+        if(delta_x || delta_y || new_buttons != mouse_buttons)
+        {
+                /* Something happened */
+
+                spin_lock(&amp;mouse_lock);
+                mouse_event = 1;
+                mouse_dx += delta_x;
+                mouse_dy += delta_y;
+                mouse_buttons = new_buttons;
+                spin_unlock(&amp;mouse_lock);
+                
+                wake_up_interruptible(&amp;mouse_wait);
+        }
+}
+  </programlisting>
+
+  <para>
+    The interrupt handler reads the mouse status. The next thing we do is 
+    to check whether something has changed. If the mouse was smart it would
+    only interrupt us if something had changed, but let's assume our mouse 
+    is stupid as most mice actually tend to be. 
+  </para>
+  <para>
+    If the mouse has changed we need to update the status variables. What we
+    don't want is the mouse functions reading these variables to read them
+    during a change. We add a spinlock that protects these variables while we
+    play with them.
+  </para>
+  <para>
+    If a change has occured we also need to wake sleeping processes, so we 
+    add a wakeup call and a <structname>wait_queue</structname> to use when 
+    we wish to await a mouse event.
+  </para>
+  <para>
+    Now we have the wait queue we can implement the poll function for the 
+    mouse relatively easily:
+  </para>
+
+  <programlisting>
+static unsigned int mouse_poll(struct file *file, poll_table *wait)
+{
+        poll_wait(file, &amp;mouse_wait, wait);
+        if(mouse_event)
+                return POLLIN | POLLRDNORM;
+        return 0;
+}
+  </programlisting>
+
+  <para>
+    This is fairly standard poll code. First we add the wait queue to the 
+    list of queues we want to monitor for an event. Secondly we check if an 
+    event has occured. We only have one kind of event - the 
+    <varname>mouse_event</varname> flag tells us that something happened. 
+    We know that this something can only be mouse data. We return the flags 
+    indicating input and normal reading will succeed.
+  </para>
+  <para>
+    You may be wondering what happens if the function returns saying 'no 
+    event yet'. In this case the wake up from the wait queue we added to 
+    the poll table will cause the function to be called again. Eventually 
+    we will be woken up and have an event ready. At this point the 
+    <function>poll</function> call will exit back to the user.
+  </para>
+  <para>
+    After the poll completes the user will want to read the data. We now 
+    need to think about how our <function>mouse_read</function> function 
+    will work:
+  </para>
+  <programlisting>
+static ssize_t mouse_read(struct file *file, char *buffer, 
+                size_t count, loff_t *pos)
+{
+        int dx, dy;
+        unsigned char button;
+        unsigned long flags;
+        int n;
+
+        if(count&lt;3)
+                return -EINVAL;
+
+        /*
+          *        Wait for an event
+         */
+
+        while(!mouse_event)
+        {
+                if(file-&gt;f_flags&amp;O_NDELAY)
+                        return -EAGAIN;
+                interruptible_sleep_on(&amp;mouse_wait);
+                if(signal_pending(current))
+                        return -ERESTARTSYS;
+        }
+  </programlisting>
+
+  <para>
+    We start by validating that the user is reading enough data. We could 
+    handle partial reads if we wanted but it isn't terribly useful and the 
+    mouse drivers don't bother to try.
+  </para>
+  <para>
+    Next we wait for an event to occur. The loop is fairly standard event
+    waiting in Linux. Having checked that the event has not yet occured, we
+    then check if an event is pending and if not we need to sleep. 
+  </para>
+  <para>
+    A user process can set the <constant>O_NDELAY</constant> flag on a file 
+    to indicate that it wishes to be told immediately if no event is 
+    pending. We check this and give the appropriate error if so. 
+  </para>
+  <para>
+    Next we sleep until the mouse or a signal awakens us. A signal will 
+    awaken us as we have used <function>wakeup_interruptible</function>. 
+    This is important as it means a user can kill processes waiting for 
+    the mouse - clearly a desireable property. If we are interrupted we 
+    exit the call and the kernel will then process signals and maybe 
+    restart the call again - from the beginning.
+  </para>
+  <para>
+    This code contains a classic Linux bug. All will be revealed later in this
+    article as well as explanations for how to avoid it.
+  </para>
+  <programlisting>
+        /* Grab the event */
+
+        spinlock_irqsave(&amp;mouse_lock, flags);
+
+        dx = mouse_dx;
+        dy = mouse_dy;
+        button = mouse_buttons;
+
+        if(dx&lt;=-127)
+                dx=-127;
+        if(dx&gt;=127)
+                dx=127;
+        if(dy&lt;=-127)
+                dy=-127;
+        if(dy&gt;=127)
+                dy=127;
+
+        mouse_dx -= dx;
+        mouse_dy -= dy;
+        
+        if(mouse_dx == 0 &amp;&amp; mouse_dy == 0)
+                mouse_event = 0;
+
+        spin_unlock_irqrestore(&amp;mouse_lock, flags);
+  </programlisting>
+  <para>
+    This is the next stage. Having established that there is an event 
+    going, we capture it. To be sure that the event is not being updated 
+    as we capture it we also take the spinlock and thus prevent parallel 
+    updates. Note here we use <function>spinlock_irqsave</function>. We 
+    need to disable interrupts on the local processor otherwise bad things 
+    will happen.
+  </para>
+  <para>
+    What will occur is that we take the spinlock. While we hold the lock 
+    an interrupt will occur. At this point our interrupt handler will try 
+    and take the spinlock. It will sit in a loop waiting for the read 
+    routine to release the lock. However because we are sitting in a loop 
+    in the interrupt handler we will never release the lock. The machine 
+    hangs and the user gets upset.
+  </para>
+  <para>
+    By blocking the interrupt on this processor we ensure that the lock 
+    holder will always give the lock back without deadlocking.
+  </para>
+  <para>
+    There is a little cleverness in the reporting mechanism too. We can 
+    only report a move of 127 per read. We don't however want to lose 
+    information by throwing away further movement. Instead we keep 
+    returning as much information as possible. Each time we return a 
+    report we remove the amount from the pending movement in 
+    <varname>mouse_dx</varname> and <varname>mouse_dy</varname>. Eventually 
+    when these counts hit zero we clear the <varname>mouse_event</varname>
+    flag as there is nothing else left to report.
+  </para>
+
+  <programlisting>
+        if(put_user(button|0x80, buffer))
+                return -EFAULT;
+        if(put_user((char)dx, buffer+1))
+                return -EFAULT;
+        if(put_user((char)dy, buffer+2))
+                return -EFAULT;
+
+        for(n=3; n < count; n++)
+                if(put_user(0x00, buffer+n))
+                        return -EFAULT;
+
+        return count;
+}
+  </programlisting>
+
+  <para>
+    Finally we must put the results in the user supplied buffer. We cannot 
+    do this while holding the lock as a write to user memory may sleep. 
+    For example the user memory may be residing on disk at this instant. 
+    Thus we did our computation beforehand and now copy the data. Each 
+    <function>put_user call</function> is filling in one byte of the buffer. 
+    If it returns an error we inform the program that it passed us an 
+    invalid buffer and abort.
+  </para>
+  <para>
+    Having written the data we blank the rest of the buffer that was read 
+    and report the read as being successful.
+  </para>
+ </chapter>
+
+ <chapter id="debugging">
+  <title>Debugging the mouse driver</title>
+
+  <para>
+    We now have an almost perfectly usable mouse driver. If you were to 
+    actually try and use it however you would eventually find a couple of 
+    problems with it. A few programs will also not work with as it does not 
+    yet support asynchronous I/O.
+  </para>
+  <para>
+    First let us look at the bugs. The most obvious one isn't really a driver
+    bug but a failure to consider the consequences. Imagine you bumped the 
+    mouse hard by accident and sent it skittering across the desk. The mouse 
+    interrupt routine will add up all that movement and report it in steps of 
+    127 until it has reported all of it. Clearly there is a point beyond 
+    which mouse movement isn't worth reporting. We need to add this as a 
+    limit to the interrupt handler:
+  </para>
+
+  <programlisting>
+static void ourmouse_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+        char delta_x;
+        char delta_y;
+        unsigned char new_buttons;
+
+        delta_x = inb(OURMOUSE_BASE);
+        delta_y = inb(OURMOUSE_BASE+1);
+        new_buttons = inb(OURMOUSE_BASE+2);
+
+        if(delta_x || delta_y || new_buttons != mouse_buttons)
+        {
+                /* Something happened */
+
+                spin_lock(&amp;mouse_lock);
+                mouse_event = 1;
+                mouse_dx += delta_x;
+                mouse_dy += delta_y;
+
+                if(mouse_dx &lt; -4096)
+                        mouse_dx = -4096;
+                if(mouse_dx &gt; 4096)
+                        mouse_dx = 4096;
+
+                if(mouse_dy &lt; -4096)
+                        mouse_dy = -4096;
+                if(mouse_dy &gt; 4096)
+                        mouse_dy = 4096;
+
+                mouse_buttons = new_buttons;
+                spin_unlock(&amp;mouse_lock);
+                
+                wake_up_interruptible(&amp;mouse_wait);
+        }
+}
+  </programlisting>
+
+  <para>
+    By adding these checks we limit the range of accumulated movement to
+    something sensible. 
+  </para>
+  <para>
+    The second bug is a bit more subtle, and that is perhaps why this is 
+    such a common mistake. Remember, I said the waiting loop for the read 
+    handler had a bug in it. Think about what happens when we execute:
+  </para>
+
+  <programlisting>
+        while(!mouse_event)
+        {
+  </programlisting>
+
+  <para>
+    and an interrupt occurs at this point here. This causes a mouse movement
+    and wakes up the queue. 
+  </para>
+
+  <programlisting>
+                interruptible_sleep_on(&amp;mouse_wait);
+  </programlisting>
+
+  <para>
+    Now we sleep on the queue. We missed the wake up and the application 
+    will not see an event until the next mouse event occurs. This will 
+    lead to just the odd instance when a mouse button gets delayed. The 
+    consequences to the user will probably be almost undetectable with a 
+    mouse driver. With other drivers this bug could be a lot more severe.
+  </para>
+  <para>
+    There are two ways to solve this. The first is to disable interrupts 
+    during the testing and the sleep. This works because when a task sleeps 
+    it ceases to disable interrupts, and when it resumes it disables them 
+    again. Our code thus becomes:
+  </para>
+
+  <programlisting>
+        save_flags(flags);
+        cli();
+
+        while(!mouse_event)
+        {
+                if(file-&gt;f_flags&amp;O_NDELAY)
+                {
+                        restore_flags(flags);
+                        return -EAGAIN;
+                }
+                interruptible_sleep_on(&amp;mouse_wait);
+                if(signal_pending(current))
+                {
+                        restore_flags(flags);
+                        return -ERESTARTSYS;
+                }
+        }
+        restore_flags(flags);
+  </programlisting>
+
+  <para>
+    This is the sledgehammer approach. It works but it means we spend a 
+    lot more time turning interrupts on and off. It also affects 
+    interrupts globally and has bad properties on multiprocessor machines 
+    where turning interrupts off globally is not a simple operation, but 
+    instead involves kicking each processor, waiting for them to disable 
+    interrupts and reply.
+  </para>
+  <para>
+    The real problem is the race between the event testing and the sleeping. 
+    We can avoid that by using the scheduling functions more directly. 
+    Indeed this is the way they generally should be used for an interrupt.
+  </para>
+
+  <programlisting>
+        struct wait_queue wait = { current, NULL };
+
+        add_wait_queue(&amp;mouse_wait, &amp;wait);
+        current-&gt;state = TASK_INTERRUPTIBLE;
+        
+        while(!mouse_event)
+        {
+                if(file-&gt;f_flags&amp;O_NDELAY)
+                {
+                        remove_wait_queue(&amp;mouse_wait, &amp;wait);
+                        current-&gt;state = TASK_RUNNING;
+                        return -EWOULDBLOCK;
+                }
+                if(signal_pending(current))
+                {
+                        remove_wait_queue(&amp;mouse_wait, &amp;wait);
+                        current-&gt;state = TASK_RUNNING;
+                        return -ERESTARTSYS;
+                }
+                schedule();
+                current-&gt;state = TASK_INTERRUPTIBLE;
+        }
+        
+        remove_wait_wait(&amp;mouse_wait, &amp;wait);
+        current-&gt;state = TASK_RUNNING;
+  </programlisting>
+
+  <para>
+    At first sight this probably looks like deep magic. To understand how 
+    this works you need to understand how scheduling and events work on 
+    Linux. Having a good grasp of this is one of the keys to writing clean 
+    efficient device drivers.
+  </para>
+  <para>
+    <function>add_wait_queue</function> does what its name suggests. It adds 
+    an entry to the <varname>mouse_wait</varname> list. The entry in this 
+    case is the entry for our current process (<varname>current</varname>
+    is the current task pointer). 
+  </para>
+  <para>
+    So we start by adding an entry for ourself onto the 
+    <varname>mouse_wait</varname> list. This does not put us to sleep 
+    however. We are merely tagged onto the list. 
+  </para>
+  <para>
+    Next we set our status to <constant>TASK_INTERRUPTIBLE</constant>. Again 
+    this does not mean we are now asleep. This flag says what should happen 
+    next time the process sleeps. <constant>TASK_INTERRUPTIBLE</constant> says 
+    that the process should not be rescheduled. It will run from now until it 
+    sleeps and then will need to be woken up.
+  </para>
+  <para>
+    The <function>wakeup_interruptible</function> call in the interrupt 
+    handler can now be explained in more detail. This function is also very 
+    simple. It goes along the list of processes on the queue it is given and 
+    any that are marked as <constant>TASK_INTERRUPTIBLE</constant> it changes 
+    to <constant>TASK_RUNNING</constant> and tells the kernel that new 
+    processes are runnable.
+  </para>
+  <para>
+    Behind all the wrappers in the original code what is happening is this
+  </para>
+
+  <procedure>
+   <step>
+    <para>
+      We add ourself to the mouse wait queue
+    </para>
+   </step>
+   <step>
+    <para>
+      We mark ourself as sleeping
+    </para>
+   </step>
+   <step>
+    <para>
+      We ask the kernel to schedule tasks again
+    </para>
+   </step>
+   <step>
+    <para>
+      The kernel sees we are asleep and schedules someone else.
+    </para>
+   </step>
+   <step>
+    <para>
+      The mouse interrupt sets our state to <constant>TASK_RUNNING</constant> 
+      and makes a note that the kernel should reschedule tasks
+    </para>
+   </step>
+   <step>
+    <para>
+      The kernel sees we are running again and continues our execution
+    </para>
+   </step>
+  </procedure>
+  <para>
+    This is why the apparent magic works. Because we mark ourself as
+    <constant>TASK_INTERRUPTIBLE</constant> and as we add ourselves 
+    to the queue before we check if there are events pending, the race 
+    condition is removed.
+  </para>
+  <para>
+    Now if an interrupt occurs after we check the queue status and before 
+    we call the <function>schedule</function> function in order to sleep, 
+    things work out. Instead of missing an event, we are set back to 
+    <constant>TASK_RUNNING</constant> by the mouse interrupt. We still call 
+    <function>schedule</function> but it will continue running our task. 
+    We go back around the loop and this time there may be an event.
+  </para>
+  <para>
+    There will not always be an event. Thus we set ourselves back to
+    <constant>TASK_INTERRUPTIBLE</constant> before resuming the loop. 
+    Another process doing a read may already have cleared the event flag, 
+    and if so we will need to go back to sleep again. Eventually we will 
+    get our event and escape.
+  </para>
+  <para>
+    Finally when we exit the loop we remove ourselves from the 
+    <varname>mouse_wait</varname> queue as we are no longer interested
+    in mouse events, and we set ourself back to 
+    <constant>TASK_RUNNABLE</constant> as we do not wish to go to sleep 
+    again just yet.
+  </para>
+  <note>
+   <title>Note</title> 
+   <para>
+     This isn't an easy topic. Don't be afraid to reread the description a 
+     few times and also look at other device drivers to see how it works. 
+     Finally if you can't grasp it just yet, you can use the code as 
+     boilerplate to write other drivers and trust me instead.
+   </para>
+  </note>
+ </chapter>
+
+ <chapter id="asyncio">
+  <title>Asynchronous I/O</title>
+  <para>
+    This leaves the missing feature - Asynchronous I/O. Normally UNIX 
+    programs use the <function>poll</function> call (or its variant form 
+    <function>select</function>) to wait for an event to occur on one of 
+    multiple input or output devices. This model works well for most tasks 
+    but because <function>poll</function> and <function>select</function> 
+    wait for an event isn't suitable for tasks that are also continually 
+    doing computation work. Such programs really want the kernel to kick 
+    them when something happens rather than watch for events.
+  </para>
+  <para>
+    Poll is akin to having a row of lights in front of you. You can see at a
+    glance which ones if any are lit. You cannot however get anything useful
+    done while watching them. Asynchronous I/O uses signals which work more 
+    like a door bell. Instead of you watching, it tells you that something 
+    is up.
+  </para>
+  <para>
+    Asynchronous I/O sends the signal SIGIO to a user process when the I/O 
+    events occur. In this case that means when people move the mouse. The 
+    SIGIO signal causes the user process to jump to its signal handler and 
+    execute code in that handler before returning to whatever was going on 
+    previously. It is the application equivalent of an interrupt handler.
+  </para>
+  <para>
+    Most of the code needed for this operation is common to all its users. 
+    The kernel provides a simple set of functions for managing asynchronous 
+    I/O.
+  </para>
+  <para>
+    Our first job is to allow users to set asynchronous I/O on file handles. 
+    To do that we need to add a new function to the file operations table for 
+    our mouse:
+  </para>
+
+  <programlisting>
+struct file_operations our_mouse_fops = {
+        NULL,                   /* Mice don't seek */
+        read_mouse,             /* You can read a mouse */
+        write_mouse,            /* This won't do a lot */
+        NULL,                   /* No readdir - not a directory */
+        poll_mouse,             /* Poll */
+        NULL,                   /* No ioctl calls */
+        NULL,                   /* No mmap */
+        open_mouse,             /* Called on open */
+        NULL,                   /* Flush */
+        close_mouse,            /* Called on close */
+        NULL,                   /* No fsync on a mouse */
+        fasync_mouse,           /* Asynchronous I/O */
+};
+  </programlisting>
+
+  <para>
+    Once we have installed this entry the kernel knows we support 
+    asynchronous I/O and will allow all the relevant operations on the 
+    device. Whenever a user adds or removes asynchronous I/O notification 
+    on a file handle it calls our <function>fasync_mouse</function> routine 
+    we just added. This routine uses the helper functions to keep the queue 
+    of handles up to date:
+  </para>
+
+  <programlisting>
+static struct fasync_struct *mouse_fasync = NULL;
+
+static int fasync_mouse(int fd, struct file *filp, int on)
+{
+         int retval = fasync_helper(fd, filp, on, &amp;mouse_fasync);
+
+         if (retval &lt; 0)
+                 return retval;
+        return 0;
+}
+  </programlisting>
+
+  <para>
+    The fasync helper adds and deletes entries by managing the supplied 
+    list. We also need to remove entries from this list when the file is 
+    closed. This requires we add one line to our close function:
+  </para>
+
+  <programlisting>
+static int close_mouse(struct inode *inode, struct file *file)
+{
+        fasync_mouse(-1, file, 0)
+        if(--mouse_users)
+                return 0;
+        free_irq(OURMOUSE_IRQ, NULL);
+        MOD_DEC_USE_COUNT;
+        return 0;
+}
+  </programlisting>
+
+  <para>
+    When we close the file we now call our own fasync handler as if the 
+    user had requested that this file cease to be used for asynchronous 
+    I/O. This rather neatly cleans up any loose ends. We certainly don't 
+    wait to deliver a signal for a file that no longer exists.
+  </para>
+  <para>
+    At this point the mouse driver supports all the asynchronous I/O 
+    operations, and applications using them will not error. They won't 
+    however work yet. We need to actually send the signals. Again the 
+    kernel provides a function for handling this.
+  </para>
+  <para>
+    We update our interrupt handler a little:
+  </para>
+
+  <programlisting>
+static void ourmouse_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+        char delta_x;
+        char delta_y;
+        unsigned char new_buttons;
+
+        delta_x = inb(OURMOUSE_BASE);
+        delta_y = inb(OURMOUSE_BASE+1);
+        new_buttons = inb(OURMOUSE_BASE+2);
+
+        if(delta_x || delta_y || new_buttons != mouse_buttons)
+        {
+                /* Something happened */
+
+                spin_lock(&amp;mouse_lock);
+                mouse_event = 1;
+                mouse_dx += delta_x;
+                mouse_dy += delta_y;
+
+                if(mouse_dx &lt; -4096)
+                        mouse_dx = -4096;
+                if(mouse_dx &gt; 4096)
+                        mouse_dx = 4096;
+
+                if(mouse_dy &lt; -4096)
+                        mouse_dy = -4096;
+                if(mouse_dy &gt; 4096)
+                        mouse_dy = 4096;
+
+                mouse_buttons = new_buttons;
+                spin_unlock(&amp;mouse_lock);
+
+                /* Now we do asynchronous I/O */
+                kill_fasync(&amp;mouse_fasync, SIGIO); 
+                
+                wake_up_interruptible(&amp;mouse_wait);
+        }
+}
+  </programlisting>
+
+  <para>
+    The new code simply calls the <function>kill_fasync</function> routine
+    provided by the kernel if the queue is non-empty. This sends the 
+    required signal (SIGIO in this case) to the process each file handle 
+    says should be informed about the exciting new mouse movement that 
+    just happened.
+  </para>
+  <para>
+    With this in place and the bugs in the original version fixed, you now 
+    have a fully functional mouse driver using the bus mouse protocol. It 
+    will work with the <application>X window system</application>, will work 
+    with <application>GPM</application> and should work with every other 
+    application you need. <application>Doom</application> is of course the 
+    ideal way to test your new mouse driver is functioning properly. Be sure 
+    to test it thoroughly.
+  </para>
+ </chapter>
+</book>
+

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)