<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> <HTML ><HEAD ><TITLE >Asynchronous I/O</TITLE ><META NAME="GENERATOR" CONTENT="Modular DocBook HTML Stylesheet Version 1.7"><LINK REL="HOME" TITLE="Mouse Drivers" HREF="book1.html"><LINK REL="PREVIOUS" TITLE="Debugging the mouse driver" HREF="c131.html"></HEAD ><BODY CLASS="CHAPTER" BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#0000FF" VLINK="#840084" ALINK="#0000FF" ><DIV CLASS="NAVHEADER" ><TABLE SUMMARY="Header navigation table" WIDTH="100%" BORDER="0" CELLPADDING="0" CELLSPACING="0" ><TR ><TH COLSPAN="3" ALIGN="center" >Mouse Drivers</TH ></TR ><TR ><TD WIDTH="10%" ALIGN="left" VALIGN="bottom" ><A HREF="c131.html" ACCESSKEY="P" ><<< Previous</A ></TD ><TD WIDTH="80%" ALIGN="center" VALIGN="bottom" ></TD ><TD WIDTH="10%" ALIGN="right" VALIGN="bottom" > </TD ></TR ></TABLE ><HR ALIGN="LEFT" WIDTH="100%"></DIV ><DIV CLASS="CHAPTER" ><H1 ><A NAME="ASYNCIO" ></A >Asynchronous I/O</H1 ><P > This leaves the missing feature - Asynchronous I/O. Normally UNIX programs use the <TT CLASS="FUNCTION" >poll</TT > call (or its variant form <TT CLASS="FUNCTION" >select</TT >) to wait for an event to occur on one of multiple input or output devices. This model works well for most tasks but because <TT CLASS="FUNCTION" >poll</TT > and <TT CLASS="FUNCTION" >select</TT > 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. </P ><P > 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. </P ><P > 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. </P ><P > 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. </P ><P > 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: </P ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="PROGRAMLISTING" >struct file_operations our_mouse_fops = { owner: THIS_MODULE read: read_mouse, /* You can read a mouse */ write: write_mouse, /* This won't do a lot */ poll: poll_mouse, /* Poll */ open: open_mouse, /* Called on open */ release: close_mouse, /* Called on close */ fasync: fasync_mouse, /* Asynchronous I/O */ }; </PRE ></TD ></TR ></TABLE ><P > 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 <TT CLASS="FUNCTION" >fasync_mouse</TT > routine we just added. This routine uses the helper functions to keep the queue of handles up to date: </P ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="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, &mouse_fasync); if (retval < 0) return retval; return 0; } </PRE ></TD ></TR ></TABLE ><P > 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: </P ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="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; } </PRE ></TD ></TR ></TABLE ><P > 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. </P ><P > 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. </P ><P > We update our interrupt handler a little: </P ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="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(&mouse_lock); mouse_event = 1; mouse_dx += delta_x; mouse_dy += delta_y; if(mouse_dx < -4096) mouse_dx = -4096; if(mouse_dx > 4096) mouse_dx = 4096; if(mouse_dy < -4096) mouse_dy = -4096; if(mouse_dy > 4096) mouse_dy = 4096; mouse_buttons = new_buttons; spin_unlock(&mouse_lock); /* Now we do asynchronous I/O */ kill_fasync(&mouse_fasync, SIGIO); wake_up_interruptible(&mouse_wait); } } </PRE ></TD ></TR ></TABLE ><P > The new code simply calls the <TT CLASS="FUNCTION" >kill_fasync</TT > 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. </P ><P > 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 <TT CLASS="APPLICATION" >X window system</TT >, will work with <TT CLASS="APPLICATION" >GPM</TT > and should work with every other application you need. <TT CLASS="APPLICATION" >Doom</TT > is of course the ideal way to test your new mouse driver is functioning properly. Be sure to test it thoroughly. </P ></DIV ><DIV CLASS="NAVFOOTER" ><HR ALIGN="LEFT" WIDTH="100%"><TABLE SUMMARY="Footer navigation table" WIDTH="100%" BORDER="0" CELLPADDING="0" CELLSPACING="0" ><TR ><TD WIDTH="33%" ALIGN="left" VALIGN="top" ><A HREF="c131.html" ACCESSKEY="P" ><<< Previous</A ></TD ><TD WIDTH="34%" ALIGN="center" VALIGN="top" ><A HREF="book1.html" ACCESSKEY="H" >Home</A ></TD ><TD WIDTH="33%" ALIGN="right" VALIGN="top" > </TD ></TR ><TR ><TD WIDTH="33%" ALIGN="left" VALIGN="top" >Debugging the mouse driver</TD ><TD WIDTH="34%" ALIGN="center" VALIGN="top" > </TD ><TD WIDTH="33%" ALIGN="right" VALIGN="top" > </TD ></TR ></TABLE ></DIV ></BODY ></HTML >