"Reverse engineering Windows USB device drivers for the purpose of creating compatible device drivers for Linux" http://www.toth.demon.co.uk/usb/reverse-0.2.txt Version : 0.2 Author : steve@toth.demon.co.uk PURPOSE ******* The Author has written this document to help promote Linux USB device driver development. It's aimed at first time (newbie) developers and tries to highlight obvious development issues and tries to highlight key issues and problem areas. FEEDBACK ******** The author welcomes any feedback. If you feel this document is missing something, just say it. If you're unsure of something, just ask. TODO **** Convert this document into a formal HOWTO. Walkthough the skeleton driver, describing the contents etc. DISCLAIMER ********** The process of reverse engineering device drivers by capturing the data, creating new software and re-applying that data may or may not be illegal in your country. Copyright theft is a broad subject and the legal status varies from country to country. Consult your legal councellor. This document is not a guide to "ripping off commercial software companies" or "depriving copyright owners of their royalties". Be sensible, don't rip off firmware or intellectual ideas or concepts. The author is not liable for any actions or work produced as a result of this document. COPYRIGHT LAW ************* TODO, try: http://www.lgu.com/publications/softcopy/index.shtml ONLINE RESOURCES **************** If you do nothing else, you'll need these: oops diags http://www-miaif.lip6.fr/willy/kmsgdump// A patch called kmsgdump can be applied to your kernel to record oops messages to floppy for later diagnosis. This is usefull stuff when you're drivers starts crashing and taking your kernel down with it. Recommend you install it and learn how to use it. linux docs http://usb.cs.tum.edu/download/usbdoc/ This example the basic infrastructure in Linux for writing drivers. It should be read hand-in-hand with the formal USB SPECIFICATION. usb specs http://www.usb.org/developers/data/usb_20.zip The formal specification of the Universal Serial Bus v2.0. Chapters five and nine are most helpfull. sniffer http://home.jps.net/~koma/sniffusb/sniffusb-0.13.zip A program to sit inbetween a windows OS and the USB driver. It records all packets. spike http://www.aracnet.com/~seagull/NJB/tools/ A perl program to translate the output of the sniffer into a more readable form. Read the primer document for details of how to read the output. usbview http://www.kroah.com/linux-usb/ A handy utility (shipped with RH7.2 by default) to show the standard configuration of ANY USB device connected to linux. You need CONFIG_USB_DEVICEFS=y compiled into your kernel for this to work. usb-skeleton.c /usr/src/linux/drivers/usb/usb-skeleton.c A skeleton structure of a USB device driver for linux. This shows the interaction between a generic device driver and the kernel. You should also check http://www.linux-usb.org for other reference documents and links to the development mailing lists etc. ASSUMPTIONS *********** 1. You've got a stable windows platform (preferably win2k - although win98 should work), and the device your writing the driver for is connected and is functioning properly. 2. You've downloaded all the software and documents above. 3. You've read the Linux USB Programming guide (highlevel skim) 4. You've read the Linux 2.0 Specification (highlevel skim) INSTALLING ********** Install sniffusb by unzipping it into a directory, copying the UsbSnoop.sys file into \\windows\system32\drivers (or whatever it's called on you box), run SniffUSB.exe and select your USB device, choose install then reboot the system. Once restarted, the DBGVIEW.EXE tool will collect all debugging events from the sniffer and show you any packets sent to/from the device. You can use this tool to save the contents as a txt log file. It's worth noting that performance will suffer while you're sniffing the device. If the device your debugging is time sensitive then these delays may affect the normal operations. Beware. My experience with webcams says that this wasn't a problem. DATA CAPTURE ************ Start your USB application, use the device for a few seconds, then stop the application. Save the log file and ftp it to your Linux box. On you Linux box us "spike" to interpret the logfile. $ cat test.log | spike After a little tidying up, you should get output something like this: 000002 CS 40 00 20 00 12 81 00 00 000003 CS 40 00 0f 00 02 84 00 00 000004 CS 40 00 00 00 03 84 00 00 000005 CS 40 00 01 00 14 81 00 00 000006 CS 40 00 01 00 14 81 00 00 000007 CS 40 00 01 00 14 81 00 00 000008 CS 40 00 92 00 10 81 00 00 000009 CS c0 00 00 00 21 86 01 00 C< 0000: 1f 000010 CS 40 00 03 00 14 81 00 00 000011 CS 40 00 00 00 01 87 00 00 000012 CS 40 00 00 00 02 87 00 00 000013 CS 40 00 50 00 03 87 00 00 000014 CS 40 00 02 00 04 87 00 00 000015 CS 40 00 01 00 0c 87 00 00 000016 CS c0 00 00 00 21 86 01 00 C< 0000: 00 The SPIKE primer document describes the output. In summary, the output columns are, transaction count, transaction type, and data. So we're showing the output for transactions 2 thru 16, these are control transactions. The USB Specification chapter nine describes how to decode control packets but, in summary, the first byte is an 8 bit bitmap with each bit representing various flags to the USB subsystem. The bits range from left to right and are known as bit7,6,5,4,3,2,1,0. Bit 1 represents the direction of the control packet, where 0 = downstream (outbound) and 1 = upstream (inbound). Now we can see that transactions 2 thru 8 and 10 thru 15 are control messages out of the driver towards to USB device. By the same token, transactions 9 and 16 have bit 7 set to 1 so these control frames are incoming to the device driver from the USB device. You should examine the full sniffusb logfile to find out exactly how much data (if any) was transferred with each control message. For example, let's look at the full log message for transaction number 14. 00000111 0.03535680 >>>>>>> URB 786 going down... 00000112 0.03536880 -- URB_FUNCTION_VENDOR_DEVICE: 00000113 0.03538320 TransferFlags = 00000000 (USBD_TRANSFER_DIRECTION_OUT, ~USBD_SHORT_TRANSFER_OK) 00000114 0.03539520 TransferBufferLength = 00000000 00000115 0.03540480 TransferBuffer = 00000000 00000116 0.03541520 TransferBufferMDL = 00000000 00000117 0.03542400 00000118 0.03543040 no data supplied 00000119 0.03544080 UrbLink = 00000000 00000120 0.03545120 RequestTypeReservedBits = 00 00000121 0.03546160 Request = 00 00000122 0.03547200 Value = 0001 00000123 0.03548160 Index = 8114 00000124 0.03776400 00000125 0.03777040 <<<<<<< URB 786 coming back... 00000126 0.03778480 -- URB_FUNCTION_CONTROL_TRANSFER: 00000127 0.03779600 PipeHandle = cc5dc70c 00000128 0.03781040 TransferFlags = 00000002 (USBD_TRANSFER_DIRECTION_OUT, USBD_SHORT_TRANSFER_OK) 00000129 0.03782240 TransferBufferLength = 00000000 00000130 0.03783280 TransferBuffer = 00000000 00000131 0.03784240 TransferBufferMDL = 00000000 00000132 0.03785360 UrbLink = 00000000 00000133 0.03787840 SetupPacket : 40 00 01 00 14 81 00 00 Line 111 also tells us the packet directioni (outbound/downstream). Line 115 is a pointer to a data transfer buffer (null - unused) Line 114 is the number of bytes to be transferred from the buffer with the request. Lines 120 thru 123 describe what parameters are being passed in the control message, these are vendor specific and will be configuring the USB device. Lines 125 thru 113 simply indicate that the message was successfully received by the USB device. NOTE: the sniffusb program simply echoes the request in it's way back up the device driver. I have yet to see a control message fail via sniffusb. So what did this frame mean? It's hard to say but in summary, the device driver passed a command to the device. Command# was 8114 with a parameter value of 0001. Here's another trace for transaction number 9. 00000211 0.06781040 >>>>>>> URB 790 going down... 00000212 0.06782240 -- URB_FUNCTION_VENDOR_DEVICE: 00000213 0.06783680 TransferFlags = 00000001 (USBD_TRANSFER_DIRECTION_IN, ~USBD_SHORT_TRANSFER_OK) 00000214 0.06784880 TransferBufferLength = 00000001 00000215 0.06786000 TransferBuffer = cc6a4d80 00000216 0.06786960 TransferBufferMDL = 00000000 00000217 0.06788000 UrbLink = 00000000 00000218 0.06789120 RequestTypeReservedBits = 00 00000219 0.06790080 Request = 00 00000220 0.06791200 Value = 0000 00000221 0.06792160 Index = 8621 00000222 0.07117280 00000223 0.07117840 <<<<<<< URB 790 coming back... 00000224 0.07119280 -- URB_FUNCTION_CONTROL_TRANSFER: 00000225 0.07120400 PipeHandle = cc5dc70c 00000226 0.07121920 TransferFlags = 00000003 (USBD_TRANSFER_DIRECTION_IN, USBD_SHORT_TRANSFER_OK) 00000227 0.07123040 TransferBufferLength = 00000001 00000228 0.07124160 TransferBuffer = cc6a4d80 00000229 0.07125280 TransferBufferMDL = cc6612b0 00000230 0.07126240 00000231 0.07126800 0000: 00000232 0.07127840 1f 00000233 0.07128880 UrbLink = 00000000 00000234 0.07131360 SetupPacket : c0 00 00 00 21 86 01 00 00000235 0.07139040 UsbSnoop - IRP_MJ_INTERNAL_DEVICE_CONTROL, IOCTL_INTERNAL_USB_SUBMIT_URB This is an inbound request for data from the device driver to the USB device. IE, the driver is asking the device to send 1 byte of data. You'll notice this logs reads a little strange. You'll notice that the request goes out with bit 7(1) (ie inbound), which was different from before where bit7(0) = outbound. You'd think that an outbound request for data would have bit7 = 0. Wrong. The bit indicates the direction of data flow just to confuse you. Check the specs for more info. In this case, the request for data went from the device driver to the USB device, the USB device then replied by sending 1 byte of data (1f) which was placed in our data buffer by the lower usb functions. How do these two requests translate into C? You'll need to read the USB Programming Guide for more information on the usb_control_msg function. // Example of a simplified downstream(outbound) control message ret=usb_control_msg(s->usbdev, usb_sndctrlpipe( s->usbdev, 0 ) , 0x40, 0x00 , 0x0001, 0x8114, NULL, 0, HZ); if(ret < 0) { err("icam_control_start, urb_control_msg(out) ret=%d",ret); do something... } // Example of a simplified upstream(inbound) control message ret=usb_control_msg(s->usbdev, usb_rcvctrlpipe( s->usbdev, 0 ) , 0xc0, 0x00 , 0x0000, 0x8621, &buffer, 0x01, HZ); if(ret < 0) { err("icam_control_start, urb_control_msg(in) ret=%d",ret); do something... } ISO/BULK TRANSFERS ****************** A number of other frames will probably appear in the sniffusb logfile. These are used for different kinds of bulk and time critical transfers between either the device or the driver. The type of transfer being used by the application depends on the nature of the USB device and how much bandwidth (and what priority) it gets on the USB bus. The specs give decribe the mechanisms (chapter 8) in more detail. YOUR FIRST LINES OF CODE ************************ You should probably base your driver on an existing Linux driver. IE, if you're developing a webcam driver, check the existing webcam drivers for how they buffer, control and deal with interrupts. You'll actually spend a large amount of time reading the source for many other drivers, don't be surprised at this. The USB internals of Linux are "pixie magic" and reading though existing stable code is a rare treat, you won't be able to live without it. On the flip side, you could use the formal usb-skeleton.c driver as a base. It's fairly limited and it's buffering abilities may need beefing up, depending on your requirements, but it's written to be easy to understand.