📄 mousedrivers.tmpl
字号:
<!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 <= delta <= 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 0x300static 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;} </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 MODULEint 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 </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 = { 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 */}; </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 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. </para> <para> 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. </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; 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;} </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> 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); 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 stops interrupts from the mouse from using our CPU time, and ensures that the mouse can now be unloaded. </para>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -