The USB Serial Driver Layer

Understanding the USB-to-serial layer and how to get devices into it.

In my last column [see LJ December 2002], we covered the serial layer in the 2.5 (hopefully soon to be 2.6) kernel tree. We mentioned in passing that a USB-to-serial driver layer in the kernel helps out in working with those types of device drivers. This time we discuss that layer in more depth.

USB Serial Layer's History

A long time ago (in kernel development time, at least), a single USB-to-serial device driver was written and accepted into the kernel tree. It barely worked for one type of device and didn't work at all on SMP machines. As no standard USB-to-serial protocol existed, all devices used a custom protocol created by the individual vendors. The reason why there is no standard protocol is a long and sordid story; check the archives of the linux-usb-devel mailing list for the details. Soon a second type of USB-to-serial device was implemented within the first driver, sharing the reserved major and minor numbers. Over time, more and more devices were added to the driver until it was becoming an unwieldy mess. With the help of Peter Berger and Al Borchers, the original author of the driver rewrote the infrastructure and created what is now known as the USB-to-serial layer. This bit of code allows different USB-to-serial drivers to be written with a minimal amount of code, all sharing the same major and minor number range. It insulates the individual drivers from some of the complexity in the tty layer and the USB layer. It also allows the drivers to be compiled as individual modules and loaded only when they are needed.

In the 2.5 development cycle, the serial layer was created in order to provide an easier way to write serial port drivers, so as not to have to deal with the tty layer directly. Hopefully, someday the USB and serial layers will be merged. Both maintainers want to see this happen, but they do not have the time to do it. (They would gladly accept patches to accomplish this, if someone is looking for a project.)

In this article we cover the basics of the USB-to-serial layer, detailing how to register and unregister a driver and how to set up the main structures needed for a driver.

Registering and Unregistering a USB Serial Driver

All of the code and examples in this article are for the 2.5/2.6 kernel tree. The 2.4 and 2.2 kernel trees also support USB-to-serial drivers, but their interfaces are a bit different in places. For ease of use, we focus on only one kernel tree. If you have any problems porting a USB-to-serial driver to these older trees (once it is running on 2.5), please let me know.

To register a USB-to-serial driver with the kernel, the driver has to do two things: register with the USB-to-serial core and register with the USB core. Registering with the USB-to-serial core tells it to call your driver when new devices are found by the USB subsystem, and registering with the USB core is needed to tell it what kind of devices your driver can accept.

To register with the USB core, all you need is a list of USB devices that your driver will work for, in traditional USB device ID format:

static struct usb_device_id id_table [] = {
        {}     /* Terminating entry */
MODULE_DEVICE_TABLE (usb, id_table);

This table is needed so the USB core knows what devices the driver can accept and the user-space hot-plug code knows what kind of devices are used. See my article “How the PCI Hot Plug Driver Filesystem Works”, LJ May 2002, for more information about this table and how the hot-plug code uses it.

Then, a simple USB device-driver structure is created with this ID information:

static struct usb_driver tiny_driver = {
        .name =         "tiny",
        .probe =        usb_serial_probe,
        .disconnect =   usb_serial_disconnect,
        .id_table =     id_table,

The .probe and .disconnect fields must be set to point to the USB serial core's functions because that type of logic is handled by it and not by your driver.

Then, a simple call registers this driver with the USB core:

usb_register (&tiny_driver);

After this, the USB serial driver must be notified of the driver with a call to:

usb_serial_register (&tiny_device);
This function takes a pointer to a struct usb_serial_driver_type, which will be explained in the following section.

To unregister a driver, the same steps have to happen, but in reverse order. First, we unregister with the USB serial core:

usb_serial_unregister (&tiny_device);

Then, we unregister with the USB core:

usb_unregister (&tiny_driver);



Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

Interesting article, when

Anonymous's picture

Interesting article, when searching for Linux compatible USB serial adapters
this is a good source.