Writing a Simple USB Driver
Driver on

Writing a Simple USB Driver

Since this column started, it has mentioned how a Linux driver author
can create varied kinds of kernel drivers, by explaining the
totally different kernel driver interfaces together with TTY, serial, I2C
and the driving force core. It’s time to transfer on now and
concentrate on writing actual drivers for actual {hardware}. We begin by explaining methods to
decide what sort of kernel driver interface to make use of, tips to
assist work out how the {hardware} truly works and quite a lot of different
real-world data.

Let’s start with a aim of constructing a easy USB lamp gadget
work effectively with Linux. Editor Don Marti identified a neat gadget,
the USB Visible Sign Indicator, manufactured by Delcom Engineering and
proven in Determine 1.
I’ve no relationship with this firm; I simply suppose they make
good merchandise. This gadget may be ordered on-line from the Delcom Net
website, www.delcom-eng.com. Don challenged me to get the gadget
engaged on Linux, and this text explains how I did it.

Determine 1. Delcom’s USB Visible Sign Indicator is an easy first
USB programming challenge.

The primary aim in making an attempt to jot down a driver for a tool is to
decide methods to management the gadget. Delcom Engineering
is good sufficient to ship all the USB protocol specification
their units use with the product, and it additionally is offered
on-line without cost. This documentation reveals what instructions the USB
controller chip accepts and methods to use them. In addition they present
a Microsoft Home windows DLL to assist customers of different working methods
write code to manage the gadget.

The documentation for this gadget is simply the documentation for the USB
controller within the lamp. It doesn’t explicitly say methods to activate the
totally different colour LEDs. For this, we’ve got to do a little bit of analysis.


No Docs? Reverse Engineer It!

If the USB protocol for this gadget had not been documented
or accessible to me, I might have needed to reverse
engineer this info from the gadget itself. A
helpful instrument for this sort of work is a free program referred to as USB Snoopy,
www.wingmanteam.com/usbsnoopy; one other model of
it’s SnoopyPro, usbsnoop.sourceforge.internet.
These applications are each Home windows applications that enable customers to seize
the USB information that’s despatched to and acquired from any USB gadget on
a Home windows system. All a developer must do is discover a Home windows
machine, set up the Home windows driver offered by the producer for
the gadget and run the snoop program. The information is captured
to a file to be analyzed later. Perl scripts can
assist filter a few of the additional noise within the output of those snoop
applications into a better format to know.

One other technique a couple of individuals have used to reverse engineer the
USB protocol of a tool is to run a Home windows occasion utilizing VMware
on high of Linux. VMware allows the Home windows occasion to speak to
the entire USB units plugged in to the Linux machine by sending
information to Linux although the usbfs. A easy modification
to the usbfs causes all information flowing although it to be
logged to the kernel log. Utilizing this, the complete USB visitors stream
may be captured and later analyzed.

After opening up the lamp gadget, ensuring to not lose the spring
that simply pops out when unscrewing the gadget, the circuit board
may be inspected (Determine 2). Utilizing an ohmmeter, or any
form of gadget for detecting a closed circuit, it was decided that
the three totally different LEDs are linked to the primary three pins of
port 1 on the principle controller chip.

In studying the documentation, the USB command to manage the degrees
of the port 1 pins is Main 10, Minor 2, Size 0. The command
writes the least vital byte of the USB command packet to port
1, and port 1 is defaulted excessive after reset.
So, that’s the USB command we have to ship to the gadget to vary
the totally different LEDs.

Determine 2. The three LEDs are linked to the primary three pins of
the controller chip.

Now that we all know the command to allow a port pin, we have to
decide which LED colour is linked to which pin. That is straightforward to do
with
a easy program that runs by way of all doable combos
of various values for the three port pins after which sends the worth
to the gadget. This program enabled me to create a desk of
values and LED colours (Desk 1).

So, if all pins on the port are enabled (a worth of 0x07 hex), no LEDs
are on. This matches up with the observe within the information sheet that said, “Port 1 is defaulted excessive after reset.” It could make sense
to not have any LEDs enabled when the gadget is first plugged in.
This implies we have to flip port pins low (off) as a way to
activate the LED for that pin. Utilizing the desk, we will decide
that the blue LED is managed by pin 2, the purple LED by pin 1
and the inexperienced LED by pin 0.

Armed with our new-found info, we set off to whip up a fast
kernel driver. It ought to be a USB driver, however what sort of interface
to person area ought to we use? A block gadget doesn’t make sense, as
this gadget doesn’t have to retailer filesystem information, however a personality
gadget would work. If we use a personality gadget driver, nonetheless,
a serious and minor quantity must be reserved for it. And the way
many minor numbers would we’d like for this driver? What if somebody
needed to plug 100 totally different USB lamp units in to this technique?
To anticipate this, we would want to order a minimum of 100 minor
numbers, which might be a complete waste if all anybody ever used was
one gadget at a time. If we make a personality driver, we additionally
would want to invent some solution to inform the driving force to activate and off the
totally different colours individually. Historically, that may very well be carried out
utilizing totally different ioctl instructions on the character driver, however we all know
significantly better than ever to create a brand new ioctl command within the kernel.

As all USB units present up in their very own listing within the sysfs
tree, so why not use sysfs and create three recordsdata within the USB
gadget listing, blue, purple and inexperienced? This may enable any
user-space program, be it a C program or a shell script, to vary
the colours on our LED gadget. This additionally would preserve us from having to
write a personality driver and beg for a piece of minor numbers for
our gadget.

To begin out our USB driver, we have to present the USB subsystem with
5 issues:

  • A pointer to the module proprietor of this driver: this permits the USB core
    to manage the module reference rely of the driving force correctly.

  • The title of the USB driver.

  • A listing of the USB IDs this driver ought to
    present: this desk is utilized by the USB core to find out
    which driver ought to be matched as much as which gadget; the hot-plug
    user-space scripts use it to load that driver mechanically when a tool is
    plugged in to the system.

  • A probe() perform referred to as by the USB
    core when a tool is discovered that matches the USB ID desk.

  • A disconnect() perform referred to as when the
    gadget is faraway from the system.

The driving force retrieves this info with the next little bit of code:

static struct usb_driver led_driver = {
	.proprietor =	THIS_MODULE,
	.title =		"usbled",
	.probe =	led_probe,
	.disconnect =	led_disconnect,
	.id_table =	id_table,
};

The id_table variable is outlined as:

static struct usb_device_id id_table [] = {
	{ USB_DEVICE(VENDOR_ID, PRODUCT_ID) },
	{ },
};
MODULE_DEVICE_TABLE (usb, id_table);

The led_probe() and led_disconnect() capabilities are described later.

When the driving force module is loaded, this led_driver construction have to be
registered with the USB core. That is completed with a single name to the
usb_register() perform:

retval = usb_register(&led_driver);
if (retval)
        err("usb_register failed. "
            "Error quantity %d", retval);

Likewise, when the driving force is unloaded from the system, it should
unregister itself from the USB core:

usb_deregister(&led_driver);

The led_probe() perform known as when the USB core has discovered our
USB lamp gadget. All it must do is initialize the gadget and create
the three sysfs recordsdata, within the correct location. That is carried out with the
following code:

/* Initialize our native gadget construction */
dev = kmalloc(sizeof(struct usb_led), GFP_KERNEL);
memset (dev, 0x00, sizeof (*dev));

dev->udev = usb_get_dev(udev);
usb_set_intfdata (interface, dev);

/* Create our three sysfs recordsdata within the USB
* gadget listing */
device_create_file(&interface->dev, &dev_attr_blue);
device_create_file(&interface->dev, &dev_attr_red);
device_create_file(&interface->dev, &dev_attr_green);

dev_info(&interface->dev,
    "USB LED gadget now attachedn");
return 0;

The led_disconnect() perform is equally as easy, as we’d like
solely to free our allotted reminiscence and take away the sysfs recordsdata:

dev = usb_get_intfdata (interface);
usb_set_intfdata (interface, NULL);

device_remove_file(&interface->dev, &dev_attr_blue);
device_remove_file(&interface->dev, &dev_attr_red);
device_remove_file(&interface->dev, &dev_attr_green);

usb_put_dev(dev->udev);
kfree(dev);

dev_info(&interface->dev,
         "USB LED now disconnectedn");

When the sysfs recordsdata are learn from, we need to present the present worth of
that LED; when it’s written to, we need to set that particular LED.
To do that, the next macro creates two capabilities for every colour LED
and declares a sysfs gadget attribute file:

#outline show_set(worth)	                           
static ssize_t                                     
show_##worth(struct gadget *dev, char *buf)        
{                                                  
   struct usb_interface *intf =                    
      to_usb_interface(dev);                       
   struct usb_led *led = usb_get_intfdata(intf);   
                                                   
   return sprintf(buf, "%dn", led->worth);        
}                                                  
                                                   
static ssize_t                                     
set_##worth(struct gadget *dev, const char *buf,   
            size_t rely)                          
{                                                  
   struct usb_interface *intf =                    
      to_usb_interface(dev);                       
   struct usb_led *led = usb_get_intfdata(intf);   
   int temp = simple_strtoul(buf, NULL, 10);       
                                                   
   led->worth = temp;                              
   change_color(led);                              
   return rely;                                   
}                                                  

static DEVICE_ATTR(worth, S_IWUGO | S_IRUGO,
                   show_##worth, set_##worth);
show_set(blue);
show_set(purple);
show_set(inexperienced);

This creates six capabilities, show_blue(), set_blue(), show_red(), set_red(),
show_green() and set_green(); and three attribute buildings, dev_attr_blue,
dev_attr_red and dev_attr_green. Because of the easy nature of the sysfs
file callbacks and the truth that we have to do the identical factor for
each totally different worth (blue, purple and inexperienced), a macro was used to scale back
typing. This can be a widespread prevalence for sysfs file capabilities;
an instance of this within the kernel supply tree is the I2C chip drivers
in drivers/i2c/chips.

So, to allow the purple LED, a person writes a 1 to the purple file in
sysfs, which calls the set_red() perform within the driver, which calls
the change_color() perform. The change_color() perform appears to be like like:

#outline BLUE	0x04
#outline RED	0x02
#outline GREEN	0x01
   buffer = kmalloc(8, GFP_KERNEL);

   colour = 0x07;
   if (led->blue)
      colour &= ~(BLUE);
   if (led->purple)
      colour &= ~(RED);
   if (led->inexperienced)
      colour &= ~(GREEN);
   retval =
      usb_control_msg(led->udev,
                      usb_sndctrlpipe(led->udev, 0),
                      0x12,
                      0xc8,
                      (0x02 * 0x100) + 0x0a,
                      (0x00 * 0x100) + colour,
                      buffer,
                      8,
                      2 * HZ);
   kfree(buffer);

With this kernel driver created, constructed and loaded, when the USB
lamp gadget is plugged in, the driving force is sure to it. All USB units
sure to this driver may be discovered within the sysfs listing for the driving force:

$ tree /sys/bus/usb/drivers/usbled/
/sys/bus/usb/drivers/usbled/
`-- 4-1.4:1.0 ->
../../../../units/pci0000:00/0000:00:0d.0/usb4/4-1/4-1.4/4-1.4:1.0

The file in that listing is a symlink again to the true location in
the sysfs tree for that USB gadget. If we glance into that listing we
can see the recordsdata the driving force has created for the LEDs:

$ tree /sys/bus/usb/drivers/usbled/4-1.4:1.0/
/sys/bus/usb/drivers/usbled/4-1.4:1.0/
|-- bAlternateSetting
|-- bInterfaceClass
|-- bInterfaceNumber
|-- bInterfaceProtocol
|-- bInterfaceSubClass
|-- bNumEndpoints
|-- blue
|-- detach_state
|-- inexperienced
|-- iInterface
|-- energy
|   `-- state
`-- purple

Then, by writing both Zero or 1 to the blue, inexperienced and purple recordsdata in that
listing, the LEDs change colour:

$ cd /sys/bus/usb/drivers/usbled/4-1.4:1.0/
$ cat inexperienced purple blue
0
0
0
$ echo 1 > purple
[[email protected] 4-1.4:1.0]$ echo 1 > blue
[[email protected] 4-1.4:1.0]$ cat inexperienced purple blue
0
1
1

Now that we’ve got created a easy kernel driver for this gadget,
which may be seen within the 2.6 kernel tree at drivers/usb/misc/usbled.c
or on the Linux Journal FTP website at (ftp.linuxjournal.com/pub/lj/listings/concern120/7353.tgz), is that this actually the very best
solution to discuss to the gadget? What about utilizing one thing like
usbfs or libusb to manage the gadget from person area with none
particular gadget drivers? In my subsequent column, I’ll present how to do that
and supply some shell scripts to manage the USB lamp
units plugged in to the system simply.

If you want to see kernel drivers written for another kinds of
units, inside cause—I am not
going to attempt to write an NVIDIA video card driver from scratch—please let me know.

Because of Don Marti for bugging me to get this gadget working
on Linux. With out his prodding it might have by no means gotten completed.

Leave a Reply

Your email address will not be published. Required fields are marked *