The USB Serial Driver Layer, Part II

How can you create a USB device that works with the generic USB serial driver? Read and learn.
Life Cycle of a USB Serial Device

When a USB-to-serial device is plugged in, a long series of steps are taken to allow a specific USB-to-serial driver to control an individual tty device. The steps are as follows:

  • The USB hub driver detects a new device. It assigns a USB number to the device and reads the basic USB description from the device, which it then populates into a struct usb_device with a number of struct usb_interfaces that represent the whole USB device.

  • The USB core takes the device and registers the USB interfaces with the kernel driver core.

  • The kernel driver core looks through the currently registered list of USB drivers to determine if any of them will accept this device.

  • Because this is a USB-to-serial device, the USB serial core accepts control of the device from the kernel driver core.

  • The USB serial core builds up a single struct, usb_serial, and calls the specific USB serial driver's probe() function with this structure.

  • The USB serial driver's probe() function initializes the device if it should and then returns control back to the USB serial core.

  • The USB serial core creates the struct usb_serial_port structures depending on the number of serial ports on this specific device and then calls the USB serial driver's attach() function, if present.

  • After the attach() function returns, the individual struct usb_serial_port structures are registered with the kernel driver core.

  • The kernel driver core calls back into the USB serial core for every individual port.

  • The USB serial core calls the individual port_probe() function in the USB serial driver for the port, if present, and then registers the port with the tty layer, completing the initialization process.

After this process, the tty device node is bound to the individual USB serial port. When the device node is opened by a user, the following steps happen in the kernel:

  • The kernel looks up the device node and determines that the tty layer has registered this node, so it calls the tty layer's open function.

  • The tty layer looks up the device and determines that the USB serial core has registered this node with it, so it calls serial_open() in the drivers/usb/serial/usb-serial.c file.

  • The serial_open() function determines what specific USB serial driver is registered for this node.

  • The module count for the specified USB serial driver is incremented in order to prevent it from being unloaded while a user is talking to the device.

  • If the specified USB serial driver has an open() function, it is called with struct usb_serial_port for the specific port being passed to it.

  • The USB serial driver then can do any hardware-specific open functionality that is needed and send off any USB urbs that are necessary to start accepting data from the device.

When a user calls write() on the device node to send data to the specified serial port, the following steps happen in the kernel:

  • The kernel calls the tty_write() function within the tty core. It has previously set up this pointer during the open call, so it will not look it up again.

  • tty_write() calls the line discipline's write() function for this specific tty device.

  • The line discipline calls the USB serial core serial_write() function.

  • The serial_write() function determines the specific USB serial driver used by this file and calls the write() function of it.

  • The USB serial driver can then copy the data into a buffer and send it out the USB connection to the device, handling any special formatting issues the device might require.

  • After the data has been sent completely, the driver can wake up the tty device in order to send any buffered data to it. This should be done with the simple call:

schedule_work(&port->work);

When data is received by the USB serial driver for a specific port, it should place the data into the specific tty structure assigned to that port's flip buffer:

for (i = 0; i < data_size; ++i) {
    if (tty->flip.count >= TTY_FLIPBUF_SIZE)
        tty_flip_buffer_push(tty);
    tty_insert_flip_char(tty, data[i], 0);
}
tty_flip_buffer_push(tty);
When a user calls read() on the device node, any data in the tty flip buffer for this port is returned.

When the device node is closed by the user, the following steps occur within the kernel:

  • The tty_release() function is called in the tty core by the kernel.

  • tty_release() determines if this is the last reference held on this device node (remember, a device node can be opened by multiple programs at the same time). If it is, the USB serial core serial_close() function is called.

  • The serial_close() function calls the USB serial driver's close() function, allowing it to shut down any pending USB transfers and get into a quiet state.

  • The USB serial core then decrements the module count for the USB serial driver, possibly allowing it to be unloaded.

______________________

Comments

Comment viewing options

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

Re: The USB Serial Driver Layer, Part II

Anonymous's picture

thank you

Some doubts

Raghu's picture

Hi,

When data is received by the USB serial driver for a specific port, it should place the data into the specific tty structure assigned to that port's flip buffer:

for (i = 0; i < data_size; ++i) {
if (tty->flip.count >= TTY_FLIPBUF_SIZE)
tty_flip_buffer_push(tty);
tty_insert_flip_char(tty, data[i], 0);
}
tty_flip_buffer_push(tty);

u told that data received is stored in flip buffer is the data is read by usb serial or serial core or others .

please reply regarding the read of data from device to the PC. when the the read function called by user on serial which function executes in USB

White Paper
Linux Management with Red Hat Satellite: Measuring Business Impact and ROI

Linux has become a key foundation for supporting today's rapidly growing IT environments. Linux is being used to deploy business applications and databases, trading on its reputation as a low-cost operating environment. For many IT organizations, Linux is a mainstay for deploying Web servers and has evolved from handling basic file, print, and utility workloads to running mission-critical applications and databases, physically, virtually, and in the cloud. As Linux grows in importance in terms of value to the business, managing Linux environments to high standards of service quality — availability, security, and performance — becomes an essential requirement for business success.

Learn More

Sponsored by Red Hat

White Paper
Private PaaS for the Agile Enterprise

If you already use virtualized infrastructure, you are well on your way to leveraging the power of the cloud. Virtualization offers the promise of limitless resources, but how do you manage that scalability when your DevOps team doesn’t scale? In today’s hypercompetitive markets, fast results can make a difference between leading the pack vs. obsolescence. Organizations need more benefits from cloud computing than just raw resources. They need agility, flexibility, convenience, ROI, and control.

Stackato private Platform-as-a-Service technology from ActiveState extends your private cloud infrastructure by creating a private PaaS to provide on-demand availability, flexibility, control, and ultimately, faster time-to-market for your enterprise.

Learn More

Sponsored by ActiveState