USB Emulation Layer Demo

 

Written by: Han Yidong,Neil (PKU)

 

For the source could be divided into three parts, which are USB abridged driver, USB Emulation Layer sample code and T-Kernel application code. So this report will describe them separately.

1. USB abridged driver

The source code refers to Linux USB scanner driver. The aim of the abridged driver is only to verify the rightness of Emulation Layer, so only open and close functions remain. It is notable that only part of source code was abridged and I didn’t modify anything.

Simple USB driver principle will be introduced as follows.

1.1 What Devices Does the Driver Support?

The struct usb_device_id structure provides a list of different types of USB devices that this driver supports. This list is used by the USB core to decide which driver to give a device to, and by the hotplug scripts to decide which driver to automatically load when a specific device is plugged into the system.

 

The struct usb_device_id structure is defined with the following fields:

 

_ _u16 match_flags

Determines which of the following fields in the structure the device should be matched against. This is a bit field defined by the different USB_DEVICE_ID_MATCH_* values specified in the include/linux/mod_devicetable.h file. This field is usually never set directly but is initialized by the USB_DEVICE type macros described later.

 

_ _u16 idVendor

The USB vendor ID for the device. This number is assigned by the USB forum to its members and cannot be made up by anyone else.

 

_ _u16 idProduct

The USB product ID for the device. All vendors that have a vendor ID assigned to them can manage their product IDs however they choose to.

 

_ _u16 bcdDevice_lo

_ _u16 bcdDevice_hi

Define the low and high ends of the range of the vendor-assigned product version number. The bcdDevice_hi value is inclusive; its value is the number of the highest-numbered device. Both of these values are expressed in binary-coded decimal (BCD) form. These variables, combined with the idVendor and idProduct, are used to define a specific version of a device.

 

_ _u8 bDeviceClass

_ _u8 bDeviceSubClass

_ _u8 bDeviceProtocol

Define the class, subclass, and protocol of the device, respectively. These numbers are assigned by the USB forum and are defined in the USB specification. These values specify the behavior for the whole device, including all interfaces on this device.

 

_ _u8 bInterfaceClass

_ _u8 bInterfaceSubClass

_ _u8 bInterfaceProtocol

Much like the device-specific values above, these define the class, subclass, and protocol of the individual interface, respectively. These numbers are assigned by the USB forum and are defined in the USB specification.

 

kernel_ulong_t driver_info

This value is not used to match against, but it holds information that the driver can use to differentiate the different devices from each other in the probe callback function to the USB driver.

 

As with PCI devices, there are a number of macros that are used to initialize this structure:

 

USB_DEVICE(vendor, product)

Creates a struct usb_device_id that can be used to match only the specified vendor and product ID values. This is very commonly used for USB devices that need a specific driver.

 

USB_DEVICE_VER(vendor, product, lo, hi)

Creates a struct usb_device_id that can be used to match only the specified vendor and product ID values within a version range.

 

USB_DEVICE_INFO(class, subclass, protocol)

Creates a struct usb_device_id that can be used to match a specific class of USB devices.

 

USB_INTERFACE_INFO(class, subclass, protocol)

Creates a struct usb_device_id that can be used to match a specific class of USB interfaces.

 

So, for a simple USB device driver that controls only a single USB device from a single vendor, the struct usb_device_id table would be defined as:

 

/* table of devices that work with this driver */

static struct usb_device_id skel_table [  ] = {

    { USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) },

    { }                 /* Terminating entry */

};

 

MODULE_DEVICE_TABLE (usb, skel_table);

As with a PCI driver, the MODULE_DEVICE_TABLE macro is necessary to allow user-space tools to figure out what devices this driver can control. But for USB drivers, the string usb must be the first value in the macro.

1.2 Registering a USB Driver

The main structure that all USB drivers must create is a struct usb_driver. This structure must be filled out by the USB driver and consists of a number of function callbacks and variables that describe the USB driver to the USB core code:

 

struct module *owner

Pointer to the module owner of this driver. The USB core uses it to properly reference count this USB driver so that it is not unloaded at inopportune moments. The variable should be set to the THIS_MODULE macro.

 

const char *name

Pointer to the name of the driver. It must be unique among all USB drivers in the kernel and is normally set to the same name as the module name of the driver. It shows up in sysfs under /sys/bus/usb/drivers/ when the driver is in the kernel.

 

const struct usb_device_id *id_table

Pointer to the struct usb_device_id table that contains a list of all of the different kinds of USB devices this driver can accept. If this variable is not set, the probe function callback in the USB driver is never called. If you want your driver always to be called for every USB device in the system, create a entry that sets only the driver_info field:

 

static struct usb_device_id usb_ids[  ] = {

    {.driver_info = 42},

    {  }

};

 

int (*probe) (struct usb_interface *intf, const struct usb_device_id *id)

Pointer to the probe function in the USB driver. This function (described in Section 13.4.3) is called by the USB core when it thinks it has a struct usb_interface that this driver can handle. A pointer to the struct usb_device_id that the USB core used to make this decision is also passed to this function. If the USB driver claims the struct usb_interface that is passed to it, it should initialize the device properly and return 0. If the driver does not want to claim the device, or an error occurs, it should return a negative error value.

 

void (*disconnect) (struct usb_interface *intf)

Pointer to the disconnect function in the USB driver. This function (described in Section 13.4.3) is called by the USB core when the struct usb_interface has been removed from the system or when the driver is being unloaded from the USB core.

 

So, to create a value struct usb_driver structure, only five fields need to be initialized:

 

static struct usb_driver skel_driver = {

    .owner = THIS_MODULE,

    .name = "skeleton",

    .id_table = skel_table,

    .probe = skel_probe,

    .disconnect = skel_disconnect,

};

 

The struct usb_driver does contain a few more callbacks, which are generally not used very often, and are not required in order for a USB driver to work properly:

 

int (*ioctl) (struct usb_interface *intf, unsigned int code, void *buf)

Pointer to an ioctl function in the USB driver. If it is present, it is called when a user-space program makes a ioctl call on the usbfs filesystem device entry associated with a USB device attached to this USB driver. In practice, only the USB hub driver uses this ioctl, as there is no other real need for any other USB driver to use it.

 

int (*suspend) (struct usb_interface *intf, u32 state)

Pointer to a suspend function in the USB driver. It is called when the device is to be suspended by the USB core.

 

int (*resume) (struct usb_interface *intf)

Pointer to a resume function in the USB driver. It is called when the device is being resumed by the USB core.

 

To register the struct usb_driver with the USB core, a call to usb_register_driver is made with a pointer to the struct usb_driver. This is traditionally done in the module initialization code for the USB driver:

 

static int _ _init usb_skel_init(void)

{

    int result;

 

    /* register this driver with the USB subsystem */

    result = usb_register(&skel_driver);

    if (result)

        err("usb_register failed. Error number %d", result);

 

    return result;

}

 

 

When the USB driver is to be unloaded, the struct usb_driver needs to be unregistered from the kernel. This is done with a call to usb_deregister_driver. When this call happens, any USB interfaces that were currently bound to this driver are disconnected, and the disconnect function is called for them.

 

static void _ _exit usb_skel_exit(void)

{

    /* deregister this driver with the USB subsystem */

    usb_deregister(&skel_driver);

}

2. USB Emulation Layer

 

Figure 1 Process of USB Core Emulation

  1. Emulation runs at the start of T-Engine
  2. Search all existing USB devices and init its structures (not integrate)
  3. Figure out which USB drivers need to load, which is compiled by T-Kernel compiler. Then register their drivers in T-Kernel way.
  4. Init event structure and buffer to wait for the USB event, such as attachment and detachment, etc.
  5. When USB device attach, attachment event is notified to USB Core Emulation Layer, to finish related structures and associate the device with its driver.

 

Through structure, we could figure out the work principle.

struct usb_driver {

       const char *name;

       void *(*probe)(

           struct usb_device *dev,         /* the device */

           unsigned intf,                /* what interface */

           const struct usb_device_id *id      /* from id_table */

           );

       void (*disconnect)(struct usb_device *, void *);

       struct list_head driver_list;

       struct file_operations *fops;

       int minor;

       struct semaphore serialize;    

       int (*ioctl)(struct usb_device *dev, unsigned int code, void *buf);

const struct usb_device_id *id_table;

       /*Needed by T-Kernel*/

       GDI Gdi;               /* driver I/F handler              */

       T_IDEV  idev;           /*device initialization information: */

       GDefDev ddev;

};

For Linux USB driver registration, it is a little easier and just an valuation operation likes

ub_minors[new_driver->minor/16] = new_driver;

For T-Kernel, however, things become complex:

ddev in struct usb_driver has to be initialized, also should register a specific interface with event parameters and has to create message buffer to receive and send USB data.

The implementation is in T_InitUsb() function.

usbEventFn was registered in ddev, which could receive USB event such as USB device attachment and detachment.

After all such initialization and registration, the task would wait for USB device attachment or USB device access operation by T-Kernel application.

3. T-Kernel application

On the point view of T-Kernel application, For Linux USB drivers have been registered in T-Kernel device drivers’ way, the application could operate device in T-Kernel way.

Our source code only tries to open and close USB device.

The call process like the followings:

eucstotcs(devnm, "usbscann");

opn_dev(devnm, D_UPDATE, NULL);

Then opn_dev would invoke usbOpenFn defined in ddev, then usbOpenFn in USB Core Emulation could figure out the address of usb_driver defined in exinf of ddev. It is now natural that open_scanner would be invoked.

The procedure of cls_dev() is the same as that of opn_dev().

4. Procedure to run executable program and test
4.1 Procedure

a.       copy file ./ sh7727/usbemu to T-Kernel as the name usbemu for example

b.       copy file ./ tst/sh7727/usbtst to T-Kernel as the name usbtst for example

c.       Type command lodspg usbemu in T-Kernel

d.       Type usbtst directly in T-Kernel

4.2 Test Report

[/SYS]% lodspg usbemu

registered new driver usbscann

USB Scanner support registered.

USB Initialization Done!

SYSPRG usbemu [6] 40229000 – 40231000

 

[/SYS]% usbtst

USB Open Function is invoked!

open_scanner

USB Close Function is invoked!

close_scanner

 

After one USB device is attached, the Tera Term would display as follows.

[/SYS]% USB-event: evt=1 usbid=0x3 [0xff]

USB device attached!

 

These prove all basic ideas about how to map Linux USB drivers to T-Kernel are right.

 

5. Problem(s)

 

In the test procedure, when USB device detached, USB emulation layer could not catch event of USB_DETACH. Maybe because there is no corresponding USB driver to claim the USB device, so the USB manager of T-Kernel would not notify such event. It would be solved with the development of USB Emulation Layer.

 

6. Plans

 

I would spend about two weeks on porting additional USB Core. Then I will turn into port Linux-related files.

 

 

 

 

 

 

References

[1] “T-Kernel Specification” T-Kernel 1.B0.01 (Aug.2002). This specification describes the mechanism of T-Kernel and defines interfaces for which T-Kernel provide.

[2] “Device Driver Manual” T-Engine/SH7727 Development Kit (August 2002), p. 42-57. This manual is useful if you want to know the system calls T-Kernel could provide.

[3] Mao DC, Hu XM; “Linux Kernel: Scenarios and Analyses”. Zhejiang, CHINA, 2001. ISBN: 7-308-02703-1. The book has detailed explanation of the implementation of USB core. If you want to port USB core, it is really helpful.

[4] SH7727 T-Engine Board User’s Manual (MS7727CP01) 1st Edition. P. 39-41. The manual details the specific hardware features of SH7727 board.

[5] Detlef Fliegl, “Programming Guide for Linux USB Device Drivers”. http://usb.cs.tum.edu/download/usbdoc.  2000. This document give detailed information about the current state of the USB subsystem and its API for USB device drivers.

[6] “Open Host Controller Interface Specification for USB”. http://h18000.www1.hp.com/productinfo/development/openhci.html.1999. This specification is a register-level description of a Host Controller for the USB, which describes how a software driver manages the Host Controller and its data structures.

[7] Wu Hui, “Insmod Source Code Analysis in Modultils”, 2005. This article analyzes the insmod source code, which is helpful to simulate the function of insmod.

[8] Corbet, Jonathan, “Linux device drivers (3rd Edition)”, O’Reilly & Associates, 2005.

 

Note:

 

If you find any information to be incorrect, please inform me by emailing anec@pub.ss.pku.edu.cn.

 

 

 

Copyright © 2007 Neil Han

Advertisements