Analysis of USB data transmission of Linux device driver

Publisher:JoyfulSpiritLatest update time:2024-07-18 Source: cnblogsKeywords:linux Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere

3: Implementation of the transmission process
Speaking of the transmission process, we must start with URB. This structure is like skb in the network subsystem and bio in I/O. The information transmission of the USB system is to be typed into a URB structure and then transmitted.
The full name of URB is USB request block. Let's start with its interface.
3.1: URB related interfaces
1: URB creation
URB creation is completed by usb_alloc_urb(). This function will complete the allocation of URB memory and the initialization of basic members. The code is as follows:
struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags)
{
struct urb *urb;

urb = kmalloc(sizeof(struct urb) +
iso_packets * sizeof(struct usb_iso_packet_descriptor),
mem_flags);
if (!urb) {
err("alloc_urb: kmalloc failed");
return NULL;
}
usb_init_urb(urb);
return urb;
}
This function has two parameters, one is iso_packets. It is only used for ISO transmission. It represents the number of ISO data packets. If it is used for other types of transmission, this parameter is 0. The other is mem_flags. It is the parameter for allocating memory.
Usb_init_urb() is as follows:
void usb_init_urb(struct urb *urb)
{
if (urb) {
memset(urb, 0, sizeof(*urb));
kref_init(&urb->kref);
INIT_LIST_HEAD(&urb->anchor_list);
}
}
From this we can see that its initialization only initializes the reference count and the ahchor_list linked list. This linked list will be used when the URB is locked.

2: Initialization of URB The
USB2.0 spec defines four types of transmission, namely ISO, INTER, BULK, and CONTORL. The linux kernel provides some APIs for URB initialization of INTER, BULK, and CONTORL. ISO transmission can only be initialized manually. These APIs are as follows:
static inline void usb_fill_control_urb(struct urb *urb,
struct usb_device *dev,
unsigned int pipe,
unsigned char *setup_packet,
void *transfer_buffer,
int buffer_length,
usb_complete_t complete_fn,
void *context)
static inline void usb_fill_bulk_urb(struct urb *urb,
struct usb_device *dev,
unsigned int pipe,
void * transfer_buffer, int buffer_length
, usb_complete_t complete_fn
,
void *context)
static inline void usb_fill_int_urb(struct urb *urb,
struct usb_device *dev,
unsigned int pipe,
void *transfer_buffer,
int buffer_length,
usb_complete_t complete_fn,
void *context,
int interval)
are used to fill CONTORL, BULK, and INT type URBs respectively.
Observing their function prototypes, we can find that there are many similar parameters. Let me explain these parameters first:
Urb: is the urb to be initialized
Dev: indicates the USB device to which the message is to be sent
Pipe: indicates the endpoint to which the message is to be sent
transfer_buffer: indicates the buffer for sending data
length: is the size of the buffer indicated by transfer_buffer
context: the context of the completion processing function
complete_fn: the function to be called after the transfer is completed.
setup_packet in usb_fill_control_urb(): the device data packet to be sent to the endpoint
interval in usb_fill_int_urb(): the interval at which this urb should be scheduled.
The actual functions are similar. Take usb_fill_control_urb() as an example:
static inline void usb_fill_control_urb(struct urb *urb,
struct usb_device *dev,
unsigned int pipe,
unsigned char *setup_packet,
void *transfer_buffer,
int buffer_length,
usb_complete_t complete_fn,
void *context)
{
urb->dev = dev;
urb->pipe = pipe;
urb->setup_packet = setup_packet;
urb->transfer_buffer = transfer_buffer;
urb->transfer_buffer_length = buffer_length;
urb->complete = complete_fn;
urb->context = context;
}
As shown above, the function parameters are simply assigned to the URB-related members.
In addition, although there is no callable API for ISO's URB initialization, its initialization is also very simple, and the corresponding operation is just to fill in a few members.
In addition, for the pipe parameters, there are a series of auxiliary macros, as shown below:
/* Create various pipes... */
#define usb_sndctrlpipe(dev,endpoint) (
(PIPE_CONTROL #define usb_rcvctrlpipe(dev,endpoint) ((PIPE_CONTROL #define usb_sndisocpipe(dev,endpoint) ((PIPE_ISOCHRONOUS #define usb_rcvisocpipe(dev,endpoint) ((PIPE_ISOCHRONOUS #define usb_sndbulkpipe(dev, endpoint ) (( PIPE_BULK #define usb_rcvbulkpipe(dev,endpoint) ((PIPE_BULK #define usb_sndintpipe(dev,endpoint) ((PIPE_INTERRUPT #define usb_rcvintpipe(dev,endpoint) ((PIPE_INTERRUPT This macro is designed according to the USB 2.0 spec. 3: Submit URB The interface for submitting urb is usb_submit_urb(). The code is as follows: int usb_submit_urb(struct urb *urb, gfp_t mem_flags) { int xfertype, max; struct usb_device *dev; struct usb_host_endpoint *ep; int is_out; if (!urb || urb->hcpriv || !urb->complete) return -EINVAL; dev = urb->dev; if ((!dev) || (dev->state return -ENODEV; /* For now, get the endpoint from the pipe. Eventually drivers * will be required to set urb->ep directly and we will eliminate * urb->pipe. */ //Get the port to be transmitted. The peer address is composed of direction + dev address + port number ep = (usb_pipein(urb->pipe) ? dev->ep_in : dev->ep_out) [usb_pipeendpoint(urb->pipe)]; if (!ep) return -ENOENT; urb->ep = ep; urb->status = -EINPROGRESS; urb->actual_length = 0; /* Lots of sanity checks, so HCDs can rely on clean data * and don't need to duplicate tests */ //Get the transmission type of ep xfertype = usb_endpoint_type(&ep->desc); //If it is a control transfer. Endpoint 0 defaults to control transfer




















































if (xfertype == USB_ENDPOINT_XFER_CONTROL) {
//If the urb for control transfer does not have setup_packet, it is illegal
struct usb_ctrlrequest *setup =
(struct usb_ctrlrequest *) urb->setup_packet;

if (!setup)
return -ENOEXEC;
//Judge whether it is the out direction of transmission
is_out = !(setup->bRequestType & USB_DIR_IN) ||
!setup->wLength;
} else {
//If it is not a control transfer, the bit7 of bEndportAddress in the endpoint descriptor contains the transfer direction of the endpoint
is_out = usb_endpoint_dir_out(&ep->desc);
}

/* Cache the direction for later use */
//According to the transmission direction, set the direction bit of urb->transfer_flags
urb->transfer_flags = (urb->transfer_flags & ~URB_DIR_MASK) |
(is_out ? URB_DIR_OUT : URB_DIR_IN);

//According to the USB2.0 spec, except for control transfers, other transfers can only be performed in the config state
if (xfertype != USB_ENDPOINT_XFER_CONTROL &&
dev->state
return -ENODEV;

//Maximum byte transmitted/received. If this maximum byte is less than 0, it is illegal
max = le16_to_cpu(ep->desc.wMaxPacketSize);
if (max
dev_dbg(&dev->dev,
"bogus endpoint ep%d%s in %s (bad maxpacket %d)n",
usb_endpoint_num(&ep->desc), is_out ? "out" : "in",
__FUNCTION__, max);
return -EMSGSIZE;
}

/* periodic transfers limit size per frame/uframe,
* but drivers only control those sizes for ISO.
* while we're checking, initialize return status.
*/
//If it is real-time transmission
if (xfertype == USB_ENDPOINT_XFER_ISOC) {
int n, len;

/* "high bandwidth" mode, 1-3 packets/uframe? */
//If it is high-speed transmission, then you need to correct its MAX value.
//In high-speed transmission, multiple data can be transmitted in one microframe. Bit 11~bit12 are used to indicate
the number of transmission packets in a microframe.
//HIGH is not supported in USB1.1
if (dev->speed == USB_SPEED_HIGH) {
int mult = 1 + ((max >> 11) & 0x03);
max &= 0x07ff;
max *= mult;
}

//The number of packets transmitted cannot be less than or equal to 0
if (urb->number_of_packets
return -EINVAL;
//urb->number_of_packets: number of real-time packets. Each real-time packet corresponds to an item in urb->iso_frame_desc[]
for (n = 0; n number_of_packets; n++) {
len = urb->iso_frame_desc[n].length;
if (len max)
return -EMSGSIZE;
urb->iso_frame_desc[n].status = -EXDEV;
urb->iso_frame_desc[n].actual_length = 0;
}
}

/* the I/O buffer must be mapped/unmapped, except when length=0 */
//If the buffer size to be transferred is less than 0. Illegalif
(urb->transfer_buffer_length
return -EMSGSIZE;

#ifdef DEBUG
/* stuff that drivers shouldn't do, but which shouldn't
* cause problems in HCDs if they get it wrong.
*/
{
unsigned int orig_flags = urb->transfer_flags;
unsigned int allowed;

/* enforce simple/standard policy */
allowed = (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP |
URB_NO_INTERRUPT | URB_DIR_MASK | URB_FREE_BUFFER);
switch (xfertype) {
case USB_ENDPOINT_XFER_BULK:
if (is_out)
allowed |= URB_ZERO_PACKET;
/* FALLTHROUGH */
case USB_ENDPOINT_XFER_CONTROL:
allowed |= URB_NO_FSBR; /* only affects UHCI */
/* FALLTHROUGH */
default: /* all non-iso endpoints */
if (!is_out)
allowed |= URB_SHORT_NOT_OK;
break;
case USB_ENDPOINT_XFER_ISOC:
allowed |= URB_ISO_ASAP;
break;
}
urb->transfer_flags &= allowed;

/* fail if submitter gave bogus flags */
if (urb->transfer_flags != orig_flags) {
err("BOGUS urb flags, %x -- > %x",
orig_flags, urb->transfer_flags);
return -EINVAL;
}
}
#endif
/*
* Force periodic transfer intervals to be legal values ​​that are
* a power of two (so HCDs don't need to).
*
* FIXME want bus->{intr,iso}_sched_horizon values ​​here. Each HC
* supports different values... this uses EHCI/UHCI defaults (and
* EHCI can use smaller non-default values).
*/

//About real-time transmission and interrupt transmission interval processing
switch (xfertype) {
case USB_ENDPOINT_XFER_ISOC:
case USB_ENDPOINT_XFER_INT:
/* too small? */
//interval cannot be less than or equal to 0
if (urb->interval
return -EINVAL;
/* too big? */
switch (dev->speed) {
case USB_SPEED_HIGH: /* units are microframes */
/* NOTE usb handles 2^15 */
if (urb->interval > (1024 * 8))
urb->interval = 1024 * 8;
max = 1024 * 8;
break;
case USB_SPEED_FULL: /* units are frames/msec */
case USB_SPEED_LOW:
if (xfertype == USB_ENDPOINT_XFER_INT) {
if (urb->interval > 255)
return -EINVAL;
/* NOTE ohci only handles up to 32 */
max = 128;
} else {
if (urb->interval > 1024)
urb->interval = 1024;
/* NOTE usb and ohci handle up to 2^15 */
max = 1024;
}
break;
default:
return -EINVAL;
}
/* Round down to a power of 2, no more than max */
urb->interval = min(max, 1 interval));
}

return usb_hcd_submit_urb(urb, mem_flags);
}
Although this code is long, the logic is very clear. It should be no problem to understand it by referring to the comments in the code. It should be noted here that UHCI belongs to USB1.1 and does not support HIGH transmission.
After a series of processing on URB, the urb will be thrown to hcd for processing. The code of usb_hcd_submit_urb() is as follows:
int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags)
{
int status;
//Get the address of usb_hcd from the address of usb_bus
struct usb_hcd *hcd = bus_to_hcd(urb->dev->bus);

/* increment urb's reference count as part of giving it to the HCD
* (which will control it). HCD guarantees that it either returns
* an error or calls giveback(), but not both.
*/
//Increase the relevant reference counts. The usbmon* family of functions are compile-time optional. Ignored
usb_get_urb(urb);
atomic_inc(&urb->use_count);
atomic_inc(&urb->dev->urbnum);
usbmon_urb_submit(&hcd->self, urb);

/* NOTE requirements on root-hub callers (usbfs and the hub
* driver, for now): URBs' urb->transfer_buffer must be
* valid and usb_buffer_{sync,unmap}() not be needed, since
* they could clobber root hub response data. Also, control
* URBs must be submitted in process context with interrupts
* enabled.
*/
//Map the transfer buffer by DMA
status = map_urb_for_dma(hcd, urb, mem_flags);
//Error, return
if (unlikely(status)) {
usbmon_urb_submit_error(&hcd->self, urb, status);
goto error;
}

//If it is a root hub
if (is_root_hub(urb->dev))
status = rh_urb_enqueue(hcd, urb);
else
//If it is a general device
status = hcd->driver->urb_enqueue(hcd, urb, mem_flags);

if (unlikely(status)) {
usbmon_ urb_submit_error(&hcd->self, urb, status);
unmap_urb_for_dma(hcd, urb);
error:
urb->hcpriv = NULL;
INIT_LIST_HEAD(&urb->urb_list);
atomic_dec(&urb->use_count);
atomic_dec(&urb->dev->urbnum);
if (urb->reject)
wake_up(&usb_kill_urb_queue);
usb_put_urb(urb);
}
return status;
}
In this function, it should be noted that urb->transfer_buffer is a virtual address. When used for UHCI, it must be mapped to a physical address for the device to use. This is what map_urb_for_dma() does. The map_urb_for_dma() function is relatively simple, so we will not analyze it in detail here.
Some people may wonder why we don't use DMA mapping for the transfer buffer in the case of the root hub?
In the following processing, we can see that, in fact, for the root hub, it does not need actual physical transmission. Linux places it statically in the memory according to the provisions in the spec. When performing related operations, just copy it directly.
Secondly, please note that this function cannot be used in the interrupt context. Because this function is synchronous, it will cause sleep.
Here, we can see that the process finally enters the following code snippet:
//If it is a root hub
if (is_root_hub(urb->dev))
status = rh_urb_enqueue(hcd, urb);
else
//If it is a general device
status = hcd->driver->urb_enqueue(hcd, urb, mem_flags);
Next, we will analyze how various transmissions are completed according to different situations.

[1] [1]
Keywords:linux Reference address:Analysis of USB data transmission of Linux device driver

Previous article:USB device driver (II)
Next article:Analysis of USB data transmission of Linux device driver 2

Latest Microcontroller Articles
Change More Related Popular Components

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

About Us Customer Service Contact Information Datasheet Sitemap LatestNews


Room 1530, 15th Floor, Building B, No.18 Zhongguancun Street, Haidian District, Beijing, Postal Code: 100190 China Telephone: 008610 8235 0740

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号