June 20th, 2019Linux+USB
Introduction
Recently I needed to write some drivers for the specific USB-scanner. This scanner is working in mode of uninterruptible scan-out with the frequency of 10 shots per second and provides the speed of stream for reading of 3.2Mb\sec.
First the drivers were developed for Linux core version 2.6.15, but later, under the client’s demand, they were adapted for the core version 2.4.26. Surprisingly, it wasn’t hard at all to write drivers fro both core versions. For this purpose you need just to know clearly how USB works and which functions provide interoperation of the driver and USB-device. All the other things are instantiated in core of linux.
The following tasks I see before writing down the following article:
1. telling about basic properties of devices change by USB
2. describing programming interface of the interoperation between driver and USB-device
3. give examples of core functions usage
Hope, the following information is interesting for the system level programmers.
Introduction to USB
Probably, there is no sense to speak about such obvious things, as wide
spread of USB, high speed of an exchange on USB, an opportunity of
hot connection of devices. Already each PC user anyhow has valued the advantages and pluses of USB.
Therefore we shall pass to less obvious things at once: how is USB
arranged inside.
Who is the boss?
One of main concepts of USB is that
there can be only one master in USB-system. It is host-computer. USB-
devices always answer inquiries of a host-computer – they can never
send the information independently.
There is only one exception: after the host has transferred the device in
the suspend-mode, the device can send inquiry remote wakeup. In all
other cases the host forms inquiries, and devices answer on
them.
Direction
Host is always the master, and data exchange should be carried out in
both directions:
* OUT – sending packet with flag OUT, host is sending data to device
* IN - sending packet with flag IN, host is sending enquiry for receipt of data from device.
You just need to adjust to it. Host is sending packet with flag IN just for receiving the data from device. smile
Packets classification in USB
Few packet types could by transferred through USB:
1. Token - enquiry, contains driving information: direction of the operation(IN, OUT), number of the endpoint
2. Data – data packet
3. Handshake – utility packets, may contain confirmation (ACK),
error message, abruption (NACK)
4. Special – service packets, such as PING
More detailed information about packets, mentioned in USB specification, you may read in [2,3]. Let’s view few examples of these packets usage.
Example: sending data to device
Host sends packet Token “OUT”, then packet Data in order to send data to device. If device is ready to process received data, it sends packet Handshake “ACK”, which confirms transaction. If it is busy, it sends refusal – Handshake “NACK”. If any mistake took place, device may not send Handshake.
Example: sending data to host
As told before, device never sends data itself. Only on-request. In order to except data, host sends packet Handshake “IN”. Device may send packet Data on-request, then Handshake “ACK”. Or it may send Handshake “NACK” without sending Data.
Types of data transfer
USB specification defines 4 types of data streams:
1. bulk transfer - assigned for packet data transfer with the packet sizes of 8, 16, 32, 32 for USB 1.1 and 512 for USB 2.0. Algorithm of resending is used (in case of mistakes appearing), and the stream driving is conducted with handshake packets usage. That’s why this type is legal. Both directions IN and OUT are supported.
2. control transfer – assigned for configuring and driving the device. As well as bulk, algorithms of confirmation and resending, that’s why this type provides with guaranteed data exchange. Directions – IN (status) and OUT (setup, control).
3. interrupt transfer - similar to bulk. Packet size - from 1 to 64
byte for USB 1.1 and up to 1024 byte for USB 2.0. This type guarantees, that device is inquired by host in specified interval (this means that host will send it token “IN”). Direction – IN.
4. isochronous transfer - assigned for data transfer without stream driving (without confirmation). Field of application -
audio-streams, video-streams. Packet size – up to 1023 byte for USB
1.1 and up to 1024 byte for USB 2.0. Mistake control is provided (for the excepting side) on CRC16. Directions - IN and OUT.
Endpoint - source/receiver of the data
USB specification defines endpoint (EP), as data source of data receiver. Device may have up to 32 EP: 16 for input and 16 for output.
Access to this or that endpoint happens according to its address.
For example,let us suppose, that host is intended to read data packet from EP4 (fourth endpoint of the device) with the help of “bulk transfer” type. It sends packet token “IN”, where it indicates the address of the endpoint. Corresponding source in the device sends data packet to host. Data packet transfer happens the same way.
Endpoint No.0
EP0 has a special value for USB. It is Control EP. It has to be in each USB-device. This EP uses token “setup”, in order to signalize that sending data coming after it are intended to drive the device. In most cases setup-packet transfer is enough. Nevertheless, device may use data transfer through EP0 as well, e.g. for components’ insertions change, or for receiving of expanded information about the device.
Let’s view setup-packet in details.
EP0: setup-packet
Contents of setup-packet is represented in the following table
Byte (No.) Name Mapping 0 bmRequestType Field for indicating request type, direction, receiver 1 bRequest Request identifier 2 wValueL 16-byte value wValue, depends on request. 3 wValueH 16-byte value wValue, depends on request. 4 wIndexL 16-byte value wIndex, depends on request. 5 wIndexH 16-byte value wIndex, depends on request. 6 wLengthL byte quantity, sent after setup-packet. 7 wLengthH byte quantity, sent after setup-packet.
As you may see in the table above, setup-packet contains 5 fields. bmRequestType and
bRequest specify request, and wValue, wIndex and wLength – its values.
USB specification preserves value range bRequest according to standard requires. Each device is obliged to reply all the standard requires. There are few standard requires in the following table.
bRequest Name Description 0×05 Set Address Installation of the unique address of device in the system 0×06 Get Descriptor Receipt of information about device. Type of information depends on field wValue.
Other range device may use as it thinks fit. Detailed data is available in [2,3].
Detection of the device
How is device detected when it is freshly connected to system? It was mentioned already, that each device has to provide access to REP0. But besides it has to reply the enquiries indicated in USB specification for EP0. Detection of the device in system happens due to these enquires.
Algorithm of new device detection is as follows:
1. host sends setup-packet “Get Descriptor” (wValue = “device”).
2. host receives identifying information about device
3. host sends setup-packet “Set address”, then device receives unique address in system
4. host sends other setup-packets “Get Descriptor” and receives additional information about device: quantity of EP, power requirements, etc.
USB support in Linux core
Programming interface for interaction with USB devices in Linux core is very simple. Under the simple interface all the algorithms of enquiries sending, confirmation tracking, mistake control, etc. are hidden. All the details, described in previous part are instantiated in Linux core.
In the core all the program files are located in drivers/usb/, and heading files are located in include/linux/. Information represented in this directories is enough to write driver for any USB-device individually.
Driver, interacting to USB-device usually takes the following actions:
1. registration\unloading of the driver
2. registration\removal of the device
3. data exchange: driving and informational.
Let’s examine them in details relating to embedding under the core 2.6.15. For better neatness we need to understand basic principles of UDM [4, 5] which appeared in core 2.6.
Registration\uploading of the driver.
Registration of the USB driver means:
1. filling the structure usb_driver
2. registration of the structure in the system
The structure usb_driver is described in include/linux/usb.h Let’s examine the most important fields of this structure.
struct usb_driver {
// …
const char *name;
int (*probe) (struct usb_interface *intf,
const struct usb_device_id *id);
void (*disconnect) (struct usb_interface *intf);
const struct usb_device_id *id_table;
struct device_driver driver;
// …
};
Evidently, name is the name of driver. id_table is the array of structures
usb_device_id. This list is assigned for definition of connected devices’ conformance to certain parameters. Only those devices, which conform to the listed parameters may be connected to driver. If the array is empty, system tries to connect each device to the driver.
Field driver indicates, that usb_driver is derived from device_driver.
In the most simple case each element of id_table[i] contains few identifiers:
* identifier of manufacturer (Vendor ID)
* identifier of the device (Device ID).
Definition of the structure usb_device_id may be seen in
include/linux/mod_devicetable.h
probe and disconnect; these are callback-functions, fetched by system while connecting and disconnecting of the USB-device. probe will be fetched for each device, if the list id_table is empty, or only for those, which correspond to the listed parameters.
Example.
#include
#define MY_DEV_NAME “my_usb_device”
#define PRODUCT_ID 0×1
#define VENDOR_ID 0×1234static struct usb_device_id my_table [] = {
{ USB_DEVICE(VENDOR_ID, PRODUCT_ID) },
{ } // terming list element
};static struct usb_driver my_driver = {
.name = MY_DEV_NAME,
.probe = my_probe,
.disconnect = my_disconnect,
.id_table = my_table,
};static int __init my_module_init(void)
{
// registering driver
return usb_register(&my_driver);
}static void __exit my_module_exit(void)
{
// unloading driver
usb_deregister(&my_driver);
}
In this example the driver of USB-device, which has fields’ meanings
PRODUCT_ID = 0×1, VENDOR_ID = 0×1234 is registered. Function my_probe will be fetched only for device with such parameters.
Fetching my_probe in fact means registration of the device in driver
my_driver, and fetching my_disconnect means removal of device. So let’s pass on the next stage – registration\removal of the device.
Registration of the device.
One registered driver is able to “connect” few devices. For connecting device to the driver system fetches driver’s function probe, which transfers 2 parameters:
static int my_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
// …
}
interface is the interface of the USB-device. Usually USB-driver
interacts directly not with the device, but with its interface. id -
contains information about the device. If the function retrieves 0, this means that device is registered successfully, otherwise system tries to “attach” device to any other driver.
To disconnect device from the driver system fetches function
disconnect, to which the only parameter is send – interface:
static void my_disconnect(struct usb_interface *interface)
{
// …
}
In generic case, the structure in memory for each connecting device is allocated in function probe, it is filled and then registered, e.g. symbolic device, and the registration of the device in sysfs is carried out. Also verification for the necessary EP could be carried out in function probe.
As an example of registration\removal of the device you’d better apply to example from the linux core, which is located in drivers/usb/usb-skeleton.c.
Usage of USB Major
Let’s examine the registration of symbolic device in details. As it is already known, for the registration of symbolic device we need to receive number major, whether statically (apply to maintainer of the core and insert it into include/linux/major.h), or dinamically (fetching
register_chrdev with parameter major = 0). When symbolic device is registered, it is necessary to create file in directory /dev. For this purpose we may use whether command mknod (from user-space) or
functions devfs, if this core supports devfs.
In the core version 2.6 the operation described above is much simplified due to appearing of udev and sysfs. Program-daemon udevd is working in the system, which tracks appearance of files in sysfs (/sys/class/). On the basis of information read from these files, it creates necessary files automatically in dev, using the rules udev for the current device.
For these purposes there is function usb_register_dev in programming interface of USB. It processes all the necessary for udev to conduct the listed above operations:
extern int usb_register_dev(struct usb_interface *intf,
struct usb_class_driver *class_driver);
extern void usb_deregister_dev(struct usb_interface *intf,
struct usb_class_driver *class_driver);usb_register_dev excepts for input interface è class_driver. Structure
usb_class_driver looks as follows:struct usb_class_driver {
char *name;
struct file_operations *fops;
int minor_base;
};name - is the name of the device. Direcory with this name will appear in sysfs.
fops - file operations of the symbolic device. minor_base - basic
minor number.
Function usb_register_dev carries out the following actions:
* registeres symbolic device with major number 180 (include/linux/major.h) and backups the range from 16 minor numbers
That’s why minor_base needs to have junior half-byte = 0.* points out one number for the current device from the backuped range of minor numbers. This number is written in â interface->minor.
* creates all the necessary files in sysfs: after this udev creates files in /dev/
Fetching of usb_deregister_dev carries out opposite options, that is why it has to be fetched in function disconnect.
Data exchange with the device
Let us examine data exchange for the most frequently used types:
control and bulk. Corresponding functions are defined in
drivers/usb/core/message.c.
Control transfer
For the sending\receiving of the data in zero EP function
usb_control_msg is used:
extern int usb_control_msg(struct usb_device *dev, unsigned int pipe,
__u8 request, __u8 requesttype, __u16 value, __u16 index,
void *data, __u16 size, int timeout);
dev - reference to usb_device. This indicator can be received by fetching of the function interface_to_usbdev(interface). pipe - EP pipe.
This parameters contains: type of data transfer(bulk, control, …),
direction, number EP. For specifying pipe in include/linux/usb.h
macros are defined. Below are few of them:
#define usb_sndctrlpipe(dev,endpoint) \
((PIPE_CONTROL << 30) | __create_pipe(dev,endpoint))
#define usb_rcvctrlpipe(dev,endpoint) \
((PIPE_CONTROL << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN)#define usb_sndbulkpipe(dev,endpoint) \
((PIPE_BULK << 30) | __create_pipe(dev,endpoint))
#define usb_rcvbulkpipe(dev,endpoint) \
((PIPE_BULK << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN)
Parameters request, request_type, value, index are the fields setup-packet.
Their contents depends on application. data, size are the array for sending\receiving. Look above [2,3].
timeout is parameter, specifying how much time is given for sending\receiving.
It is necessary to keep in mind that function usb_control_msg retrieves the value only after setup-packet and data are delivered or if a mistake of time-out take place. This function uses fetching interruptible_sleep_on, that is why this function shouldn’t be used in breakup.
Example. Sending of setup-packet with specified bRequest and wValue can look ad follows:
// …
int ret;
struct usb_device* udev = interface_to_usbdev(interface);ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
bRequest, USB_TYPE_VENDOR,
wValue, 0, udev, 0, HZ);
// …
In this example only setup-package is sent. That is why data = udev,
size = 0.
Bulk transfer
For usage of bulk transfer function usb_bulk_msg is used:
extern int usb_bulk_msg(struct usb_device *dev, unsigned int pipe,
void *data, int size, int *actual_size,
int timeout);
Attributes dev, pipe, data, size, timeout have the same meaning as in
usb_control_msg. actual_size - quantity of actually transferred data. actual_size <= size.
usb_bulk_msg has the same attribute with usb_control_msg – it cannot be fetched if breakup.
Example. Accepting of 100 byte from 5th EP can look as follows:
// …
struct usb_device* udev = interface_to_usbdev(interface);
u8 buf[[ ;]]
int ret, actual_size;
ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, 5), buf, 100, &actual_size, HZ)
;
// …
Conclusion
USB specification defines large quantity of data transfer types, packets, algorithms. All the USB algorithms are instantiated in linux core. Convenient and simple interface is represented to drivers programmer in terms of set of functions, macros, structures. In linux core 2.6 subsystem of USB is fit well into the model UDM[3,4], this makes it flexible for interaction with udev, sysfs.