<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> <HTML ><HEAD ><TITLE >A simple mouse driver</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="Introduction" HREF="c20.html"><LINK REL="NEXT" 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="c20.html" ACCESSKEY="P" ><<< Previous</A ></TD ><TD WIDTH="80%" ALIGN="center" VALIGN="bottom" ></TD ><TD WIDTH="10%" ALIGN="right" VALIGN="bottom" ><A HREF="c131.html" ACCESSKEY="N" >Next >>></A ></TD ></TR ></TABLE ><HR ALIGN="LEFT" WIDTH="100%"></DIV ><DIV CLASS="CHAPTER" ><H1 ><A NAME="DRIVER" ></A >A simple mouse driver</H1 ><P > 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. </P ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="PROGRAMLISTING" >#define OURMOUSE_BASE 0x300 static struct miscdevice our_mouse = { OURMOUSE_MINOR, "ourmouse", &our_mouse_fops }; __init ourmouse_init(void) { if(check_region(OURMOUSE_BASE, 3)) return -ENODEV; request_region(OURMOUSE_BASE, 3, "ourmouse"); misc_register(&our_mouse); return 0; } </PRE ></TD ></TR ></TABLE ><P > The <SPAN CLASS="STRUCTNAME" >miscdevice</SPAN > 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. </P ><P > Minor numbers in this space are allocated by a central source, although you can look in the kernel <TT CLASS="FILENAME" >Documentation/devices.txt</TT > 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. </P ><P > 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. </P ><P > Then we tell the misc driver that we wish to own a minor number. We also hand it our name (which is used in <TT CLASS="FILENAME" >/proc/misc</TT >) 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. </P ><P > 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: </P ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="PROGRAMLISTING" >#ifdef MODULE int init_module(void) { if(ourmouse_init()<0) return -ENODEV: return 0; } void cleanup_module(void) { misc_deregister(&our_mouse); free_region(OURMOUSE_BASE, 3); } #endif </PRE ></TD ></TR ></TABLE ><P > The module code provides the normal two functions. The <TT CLASS="FUNCTION" >init_module</TT > 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. </P ><P > The <TT CLASS="FUNCTION" >cleanup_module</TT > 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. </P ><P > Once the <TT CLASS="FUNCTION" >misc_deregister</TT > has been called any attempts to open the mouse device will fail with the error <SPAN CLASS="ERRORCODE" >ENODEV</SPAN > (<SPAN CLASS="ERRORNAME" >No such device</SPAN >). </P ><P > 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: </P ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="PROGRAMLISTING" >struct file_operations our_mouse_fops = { owner: THIS_MODULE, /* Automatic usage management */ 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 */ }; </PRE ></TD ></TR ></TABLE ><P > 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. </P ><P > The syntax we use is not standard C as such. GCC provides the ability to initialise fields by name, and this generally makes the method table much easier to read than counting through NULL pointers and remembering the order by hand. </P ><P > The owner field is used to manage the locking of module load an unloading. It is obviously important that a module is not unloaded while in use. When your device is opened the module specified by "owner" is locked. When it is finally released the module is unlocked. </P ><P > 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. </P ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="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; if(request_irq(mouse_intr, OURMOUSE_IRQ, 0, "ourmouse", NULL)) { mouse_users--; return -EBUSY; } mouse_dx = 0; mouse_dy = 0; mouse_event = 0; mouse_buttons = 0; return 0; } </PRE ></TD ></TR ></TABLE ><P > 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 <SPAN CLASS="RETURNVALUE" >0</SPAN > for success. </P ><P > We grab the interrupt and thus start mouse interrupts. If the interrupt has been borrowed by some other driver then <TT CLASS="FUNCTION" >request_irq</TT > will fail and we will return an error. If we were capable of sharing an interrupt line we would specify <TT CLASS="CONSTANT" >SA_SHIRQ</TT > instead of <TT CLASS="CONSTANT" >zero</TT >. Provided that everyone claiming an interrupt sets this flag, they get to share the line. <TT CLASS="HARDWARE" ><B >PCI</B ></TT > can share interrupts, <TT CLASS="HARDWARE" ><B >ISA</B ></TT > normally however cannot. </P ><P > 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. </P ><P > The release function needs to unwind all these: </P ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="PROGRAMLISTING" >static int close_mouse(struct inode *inode, struct file *file) { if(--mouse_users) return 0; free_irq(OURMOUSE_IRQ, NULL); return 0; } </PRE ></TD ></TR ></TABLE ><P > 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 stops interrupts from the mouse from using our CPU time, and ensures that the mouse can now be unloaded. </P ><P > We can fill in the write handler at this point as the write function for our mouse simply declines to allow writes: </P ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="PROGRAMLISTING" >static ssize_t write_mouse(struct file *file, const char *buffer, size_t count, loff_t *ppos) { return -EINVAL; } </PRE ></TD ></TR ></TABLE ><P > This is pretty much self-explanatory. Whenever you write you get told it was an invalid function. </P ><P > To make the poll and read functions work we have to consider how we handle the mouse interrupt. </P ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="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(&mouse_lock); mouse_event = 1; mouse_dx += delta_x; mouse_dy += delta_y; mouse_buttons = new_buttons; spin_unlock(&mouse_lock); wake_up_interruptible(&mouse_wait); } } </PRE ></TD ></TR ></TABLE ><P > 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. </P ><P > 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. </P ><P > If a change has occurred we also need to wake sleeping processes, so we add a wakeup call and a <SPAN CLASS="STRUCTNAME" >wait_queue</SPAN > to use when we wish to await a mouse event. </P ><P > Now we have the wait queue we can implement the poll function for the mouse relatively easily: </P ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="PROGRAMLISTING" >static unsigned int mouse_poll(struct file *file, poll_table *wait) { poll_wait(file, &mouse_wait, wait); if(mouse_event) return POLLIN | POLLRDNORM; return 0; } </PRE ></TD ></TR ></TABLE ><P > 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 occurred. We only have one kind of event - the <TT CLASS="VARNAME" >mouse_event</TT > 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. </P ><P > 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 <TT CLASS="FUNCTION" >poll</TT > call will exit back to the user. </P ><P > After the poll completes the user will want to read the data. We now need to think about how our <TT CLASS="FUNCTION" >mouse_read</TT > function will work: </P ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="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<3) return -EINVAL; /* * Wait for an event */ while(!mouse_event) { if(file->f_flags&O_NDELAY) return -EAGAIN; interruptible_sleep_on(&mouse_wait); if(signal_pending(current)) return -ERESTARTSYS; } </PRE ></TD ></TR ></TABLE ><P > 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. </P ><P > 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 occurred, we then check if an event is pending and if not we need to sleep. </P ><P > A user process can set the <TT CLASS="CONSTANT" >O_NDELAY</TT > 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. </P ><P > Next we sleep until the mouse or a signal awakens us. A signal will awaken us as we have used <TT CLASS="FUNCTION" >wakeup_interruptible</TT >. This is important as it means a user can kill processes waiting for the mouse - clearly a desirable 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. </P ><P > This code contains a classic Linux bug. All will be revealed later in this article as well as explanations for how to avoid it. </P ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="PROGRAMLISTING" > /* Grab the event */ spinlock_irqsave(&mouse_lock, flags); dx = mouse_dx; dy = mouse_dy; button = mouse_buttons; if(dx<=-127) dx=-127; if(dx>=127) dx=127; if(dy<=-127) dy=-127; if(dy>=127) dy=127; mouse_dx -= dx; mouse_dy -= dy; if(mouse_dx == 0 && mouse_dy == 0) mouse_event = 0; spin_unlock_irqrestore(&mouse_lock, flags); </PRE ></TD ></TR ></TABLE ><P > 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 <TT CLASS="FUNCTION" >spinlock_irqsave</TT >. We need to disable interrupts on the local processor otherwise bad things will happen. </P ><P > 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. </P ><P > By blocking the interrupt on this processor we ensure that the lock holder will always give the lock back without deadlocking. </P ><P > 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 <TT CLASS="VARNAME" >mouse_dx</TT > and <TT CLASS="VARNAME" >mouse_dy</TT >. Eventually when these counts hit zero we clear the <TT CLASS="VARNAME" >mouse_event</TT > flag as there is nothing else left to report. </P ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="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; } </PRE ></TD ></TR ></TABLE ><P > 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 <TT CLASS="FUNCTION" >put_user call</TT > 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. </P ><P > Having written the data we blank the rest of the buffer that was read and report the read as being successful. </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="c20.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" ><A HREF="c131.html" ACCESSKEY="N" >Next >>></A ></TD ></TR ><TR ><TD WIDTH="33%" ALIGN="left" VALIGN="top" >Introduction</TD ><TD WIDTH="34%" ALIGN="center" VALIGN="top" > </TD ><TD WIDTH="33%" ALIGN="right" VALIGN="top" >Debugging the mouse driver</TD ></TR ></TABLE ></DIV ></BODY ></HTML >