Analysis of USB Gadget Device Driver (3)

Source: Internet
Author: User

 

Author:Liu Hongtao, a lecturer at Huaqing vision embedded College.

A Linux USB driver consists of two parts. Some are USB device controller (UDC) drivers and others are hardware-independent function drivers (such as mouse, USB flash drive, USB serial port, USB network, etc ); it can also be divided into three layers: controller drivers, Gadget drivers, and upper layers.

I. Controller (USB device controller, UDC) Driver

The Gadget Framework proposes a set of standard APIs. At the underlying layer, the USB device controller driver implements this set of APIs. Different UDC requires different drivers, code modifications are even required for different boards based on the same UDC. This layer is related to hardware.

The Linux standard kernel supports various mainstream soc udc drivers, such as S3C2410 and PXA270. You can directly configure the support through the kernel. You can also improve efficiency by modifying them. For example, the DMA function of the controller is not used in s3c2410_uda.c. You can modify it as needed.
To understand the UDC driver code, you must be familiar with the corresponding hardware controller. Of course, if you are not interested or have no time to familiarize yourself with this, you can also temporarily skip the hardware-related part. This article also focuses on the description of the software structure and does not care about the hardware details.

The following describes some key data structures and APIs involved in the UDC driver. For more information, see s3c2410_uda.c.

1. Key data structures and APIs

The Gadget API provides interfaces for the interaction between the USB device controller driver and the upper-layer gadget driver. The following lists some key data structures.

Struct usb_gadget {// represents a UDC Device
/* Readonly to gadget driver */
Const struct usb_gadget_ops * OPS; // operation set of the device
Struct usb_ep * ep0; // ep0 (endpoint 0 in USB protocol), process setup () Request
Struct list_head ep_list;/* of usb_ep */Endpoint linked list supported by this device
Enum usb_device_speed speed; // For example, usb_speed_low and usb_speed_full
Unsigned is_dualspeed: 1; // full/High Speed is supported.
Unsigned is_otg: 1; // OTG features
Unsigned is_a_peripheral: 1; // The current value is a-peripheral, not a-host.
Unsigned B _hnp_enable: 1;
Unsigned a_hnp_support: 1;
Unsigned a_alt_hnp_support: 1;
Const char * Name;
Struct device dev;
};

Struct usb_gadget_driver {// represents a gadget Device Driver, such as fsg_driver in file_storage.c.
// Another example is: zero_driver in zero. C.
Char * function; // a string, such as "gadget zero"
Enum usb_device_speed speed;
INT (* bind) (struct usb_gadget *);
Void (* unbind) (struct usb_gadget *);
INT (* setup) (struct usb_gadget *,
Const struct usb_ctrlrequest *);
Void (* disconnect) (struct usb_gadget *);
Void (* suspend) (struct usb_gadget *);
Void (* resume) (struct usb_gadget *)

/* Fixme support safe rmmod */
Struct device_driver driver;
};

Struct usb_gadget_ops {// represents the operation set of the device
INT (* get_frame) (struct usb_gadget *);
INT (* wakeup) (struct usb_gadget *);
INT (* set_selfpowered) (struct usb_gadget *, int is_selfpowered );
NT (* vbus_session) (struct usb_gadget *, int is_active );
INT (* vbus_draw) (struct usb_gadget *, unsigned Ma );
INT (* pullup) (struct usb_gadget *, int is_on );
INT (* IOCTL) (struct usb_gadget *,
Unsigned code, unsigned Long Param );
};

Struct usb_ep {// represents an endpoint
Void * driver_data //
...
Const struct usb_ep_ops * OPS; // The operation set of the endpoint, as shown above
Struct list_head ep_list; // list of all the EP of the gadget
...
};
Struct usb_ep_ops {// indicates the operation set of the endpoint
...
INT (* Queue) (struct usb_ep * EP, struct usb_request * req,
Gfp_t gfp_flags); // submit a usb_request to the endpoint.
// Is a key function of data transmission.
...
};

Struct usb_request {// indicates a transfer request, which is similar to the urb of the USB host.
Void * Buf;
Unsigned length;
Dma_addr_t DMA;
Unsigned no_interrupt: 1;
Unsigned zero: 1;
Unsigned short_not_ OK: 1;
Void (* Complete) (struct usb_ep * EP,
Struct usb_request * req );
Void * context;
Struct list_head list;
Int status;
Unsigned actual;
};

The specific meaning of each item in the above structure can refer to the http://tali.admingilde.org/linux-docbook/gadget/
For example, struct usb_request

In http://tali.admingilde.org/linux-docbook/gadget/re02.html

Name

Struct usb_request-describes one I/O Request

Synopsis

Struct usb_request {
Void * Buf;
Unsigned length;
Dma_addr_t DMA;
Unsigned no_interrupt: 1;
Unsigned zero: 1;
Unsigned short_not_ OK: 1;
Void (* Complete) (struct usb_ep * EP, struct usb_request * req );
Void * context;
Struct list_head list;
Int status;
Unsigned actual;
};

Members

Buf

Buffer Used for data. Always provide this; some controllers only use PIO, or don't use DMA for some endpoints.

Length

Length of that data

DMA

DMA address corresponding to 'buf'. If you don't set this field, and the USB controller needs one, it is responsible for mapping and unmapping the buffer.

No_interrupt

If true, hints that no completion IRQ is needed. Helpful sometimes with deep request queues that are handled directly by DMA controllers.

Zero

If true, when writing data, makes the last packet be "short" by adding a zero length packet as needed;

Short_not_ OK

When reading data, makes short packets be treated as errors (queue stops advancing till cleanup ).

Complete

Function called when request completes, so this request and its buffer may be re-used. reads terminate with a short packet, or when the buffer fills, whichever comes first. when writes terminate, some data bytes will be usually still be in flight (often in a hardware FIFO ). errors (for reads or writes) Stop the queue from advancing until the completion function returns, so that any transfers invalidated by the error may first be dequeued.

Context

For use by the completion callback

List

For use by the gadget driver.

Status

Reports completion code, zero or a negative errno. normally, faults block the transfer queue from advancing until the completion callback returns. code "-eshudown" indicates completion caused by device disconnect, or when the driver disabled the endpoint.

Actual

Reports bytes transferred to/from the buffer. for reads (out transfers) This may be less than the requested length. if the short_not_ OK flag is set, short reads are treated as errors even when status otherwise indicates successful completion. note that for writes (in transfers) Some data bytes may still reside in a device-side FIFO when the request is reported as complete.

Description

These are allocated/freed through the endpoint they're used. the hardware's driver can add extra per-request data to the memory it returns, whichoften avoids separate memory allocations (potential failures), later when the request is queued.

Request flags affect Request Handling, such as whether a zero length packet is written (the "zero" flag), whether a short read shoshould be treated as anerror (blocking request queue advance, the "short_not_ OK" flag), or hinting that an interrupt is not required (the "no_interrupt" flag, for use with deeprequest queues ).

Bulk endpoints can use any size buffers, and can also be used for interrupt transfers. Interrupt-only endpoints can be much less functional.

2. registration and cancellation functions provided for the USB Gadget driver

Export_symbol (usb_gadget_unregister_driver); // cancel a USB Gadget driver.

Export_symbol (usb_gadget_register_driver); // register a USB Gadget driver

Ii. USB Gadget function driver

If the kernel already supports the soc udc driver, we can focus only on writing this part of the code. So how do we compile a driver similar to the USB function?

The USB driver must at least implement the following functions:

.Implement Part 0 of the endpoint in the USB Protocol and related to specific functions (the part that the UDC driver cannot help us complete ). For example, usb_req_get_descriptor and usb_req_get_configuration;
After this function is completed, the USB host system will determine what kind of device we are.
.Implement Data Interaction
That is, how to send read and write requests to the terminal of the hardware controller to complete data interaction;
.For example, how to implement a USB net driver or a USB storage driver.
The following uses zero. C as an example to illustrate how these three aspects are implemented.

1. Zero device Introduction

As a simple gadget driver, zero implements a simple input and output function based on two bulk endpoints. It can be used as an instance for writing a new gadget driver.
The two bulk endpoints are one in endpoint and one out endpoint. Based on the two endpoints (provided by the underlying layer), The g_zero driver implements two configurations. The first configuration provides the sink/source function: one of the two endpoints is responsible for input and the other is responsible for output. The output content can be set to 0, it can also be data generated based on a certain algorithm. Another configuration provides the loopback interface. The in endpoint is responsible for feeding back the data received from the out endpoint to the host.

2. registration and cancellation of zero Devices

Static int _ init Init (void)
{
Return usb_gadget_register_driver (& zero_driver );
}
Module_init (init );

Static struct usb_gadget_driver zero_driver = {
# Ifdef config_usb_gadget_dualspee
. Speed = usb_speed_high,
# Else
. Speed = usb_speed_full,
# Endif
. Function = (char *) longname,
. Bind = zero_bind,
. Unbind = _ exit_p (zero_unbind ),
. Setup = zero_setup,
. Disconnect = zero_disconnect,
. Suspend = zero_suspend,
. Resume = zero_resume,
. Driver = {
. Name = (char *) shortname,
. Owner = this_module,
},
};

Build a usb_gadget_driver and call the usb_gadget_register_driver registration function to register a USB Gadget driver. Note that currently, only one gadget driver can be registered for the host controller of S3c2410. This is mainly determined by the Protocol. Refer to this code in s3c2410_udc.c.

Int usb_gadget_register_driver (struct usb_gadget_driver * driver)
{......
If (UDC-> driver) // if you have already registered
Return-ebusy;
......
}

3. usb_gadget_driver Structure

In fact, our job is to build this usb_gadget_driver structure. So this structure is linked to the three goals we have achieved above.

.Setup (zero_setup)

Process requests sent from the host, for example, get_descriptor requests sent from the host. This implements the first feature that must be implemented as mentioned above.

.BIND (zero_bind)

Dev and driver are bound. When the player registers the driver, it is called by usb_gadget_register_driver. After the driver is bound, the driver can process the Setup request.
In addition, the usb_ep_autoconfig function can be used to allocate two endpoints named ep_in_name and ep_out_name. Later, you can initiate data transmission requests to the two endpoints, which is very similar to the urb requests on the USB host. You can compare them with urb.
To initiate a data request, follow these steps:

Struct usb_request * req;
Req = alloc_ep_req (Ep, buflen); // allocate the request. The data transmission direction is determined by the EP.
REQ-> complete = source_sink_complete; // processing function after the request is complete
Status = usb_ep_queue (Ep, req, gfp_atomic); // submit the request
Free_ep_req (Ep, req); // release a request, which is usually called in the request processing function complete.

.A specific function driver is usually registered in BIND and unbind functions.

If you need to register characters, blocks, and network device drivers on the device side to implement a specific function, select the field
Usually registered in BIND and uninstalled in unbind. For example, in the ether. c file:
Static int _ init
Eth_bind (struct usb_gadget * gadget)
{
......
Status = register_netdev (Dev-> net); // register the NIC Driver
......
}

Static void/* _ init_or_exit */
Eth_unbind (struct usb_gadget * gadget)
{
......
Unregister_netdev (Dev-> net); // deregister the NIC Driver
......
}

This also gives us some knowledge about how to implement a character, block, and network-driven structure on the device side.

Summary

This article briefly introduces the driver structure of gadget. The next article describes how to compile a simple gadget driver and application testing program.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.