Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 | <!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 0x300 static struct miscdevice our_mouse = { OURMOUSE_MINOR, "ourmouse", &our_mouse_fops }; __init ourmouse_init(void) { if (request_region(OURMOUSE_BASE, 3, "ourmouse") < 0) { printk(KERN_ERR "ourmouse: request_region failed.\n"); return -ENODEV; } if (misc_register(&our_mouse) < 0) { printk(KERN_ERR "ourmouse: cannot register misc device.\n"); release_region(OURMOUSE_BASE, 3); return -EBUSY; } 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 reserve our I/O address space with request_region, checking to make sure that it succeeded (i.e. the space wasn't reserved by anyone else). </para> <para> Then we ask the misc driver to allocate our minor device 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. Since misc_register can fail, it is important to check for failure and act accordingly (which in the case of a mouse driver is to abort, since you can't use the mouse without a working device node). </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()<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> <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(&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); } } </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 occurred 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, &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 occurred. 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<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; } </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 occurred, 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 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. </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(&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); </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(&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); wake_up_interruptible(&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(&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->f_flags&O_NDELAY) { restore_flags(flags); return -EAGAIN; } interruptible_sleep_on(&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(&mouse_wait, &wait); set_current_state(TASK_INTERRUPTIBLE); while(!mouse_event) { if(file->f_flags&O_NDELAY) { remove_wait_queue(&mouse_wait, &wait); set_current_state(TASK_RUNNING); return -EWOULDBLOCK; } if(signal_pending(current)) { remove_wait_queue(&mouse_wait, &wait); current->state = TASK_RUNNING; return -ERESTARTSYS; } schedule(); set_current_state(TASK_INTERRUPTIBLE); } remove_wait_wait(&mouse_wait, &wait); set_current_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 = { 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 */ }; </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, &mouse_fasync); if (retval < 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(&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); } } </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> |