diff --git a/IMX6ULL/doc_pic/12_USB/08_USB设备驱动模型.md b/IMX6ULL/doc_pic/12_USB/08_USB设备驱动模型.md new file mode 100644 index 0000000..63e5ae7 --- /dev/null +++ b/IMX6ULL/doc_pic/12_USB/08_USB设备驱动模型.md @@ -0,0 +1,632 @@ +# USB设备驱动模型 # + +参考资料: + +* Linux内核源码:`include\linux\usb.h` + +* Linux内核源码:`drivers\hid\usbhid\usbmouse.c` + + +## 1. BUS/DEV/DRV模型 + +![image-20221010144529823](pic/62_usb_bus_dev_drv.png) + +* "USB接口"是逻辑上的USB设备,我们编写的usb_driver驱动程序,支持的是"USB接口": + + ![image-20221010145250687](pic/63_usb_driver.png) + +* USB控制器或Hub识别出USB设备后,会创建、注册usb_deive + +* usb_device被"drivers\usb\core\generic.c"驱动认领后,会选择、设置某个配置 + +* 这个配置下面的接口,都会分配、设置、注册一个usb_interface + +* 左边的usb_driver和右边的usb_interface如果匹配,则掉用usb_driver.probe + + + +## 2. 接口函数 + +在USB设备驱动程序中,能使用的USB函数都在这个头文件里:`include\linux\usb.h`。 + +### 2.1 pipe + +使用这些接口函数的主要目的是传输数据,传输数据的对象是USB设备里的某个endpoint,这被称为pipe: + +```c +/* Create various pipes... */ +#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_sndisocpipe(dev, endpoint) \ + ((PIPE_ISOCHRONOUS << 30) | __create_pipe(dev, endpoint)) +#define usb_rcvisocpipe(dev, endpoint) \ + ((PIPE_ISOCHRONOUS << 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) +#define usb_sndintpipe(dev, endpoint) \ + ((PIPE_INTERRUPT << 30) | __create_pipe(dev, endpoint)) +#define usb_rcvintpipe(dev, endpoint) \ + ((PIPE_INTERRUPT << 30) | __create_pipe(dev, endpoint) | USB_DIR_IN) +``` + + + +### 2.2 同步传输函数 + +对于控制传输、批量传输、中断传输,有3个同步函数可以用来直接发起传输。这些函数内部会创建、填充、提交一个URB("usb request block"),并等待它完成或超时。 + +函数原型如下: + +```c +/** + * usb_control_msg - Builds a control urb, sends it off and waits for completion + * @dev: pointer to the usb device to send the message to + * @pipe: endpoint "pipe" to send the message to + * @request: USB message request value + * @requesttype: USB message request type value + * @value: USB message value + * @index: USB message index value + * @data: pointer to the data to send + * @size: length in bytes of the data to send + * @timeout: time in msecs to wait for the message to complete before timing + * out (if 0 the wait is forever) + * + * Context: !in_interrupt () + * + * This function sends a simple control message to a specified endpoint and + * waits for the message to complete, or timeout. + * + * Don't use this function from within an interrupt context, like a bottom half + * handler. If you need an asynchronous message, or need to send a message + * from within interrupt context, use usb_submit_urb(). + * If a thread in your driver uses this call, make sure your disconnect() + * method can wait for it to complete. Since you don't have a handle on the + * URB used, you can't cancel the request. + * + * Return: If successful, the number of bytes transferred. Otherwise, a negative + * error number. + */ +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); + +/** + * usb_bulk_msg - Builds a bulk urb, sends it off and waits for completion + * @usb_dev: pointer to the usb device to send the message to + * @pipe: endpoint "pipe" to send the message to + * @data: pointer to the data to send + * @len: length in bytes of the data to send + * @actual_length: pointer to a location to put the actual length transferred + * in bytes + * @timeout: time in msecs to wait for the message to complete before + * timing out (if 0 the wait is forever) + * + * Context: !in_interrupt () + * + * This function sends a simple bulk message to a specified endpoint + * and waits for the message to complete, or timeout. + * + * Don't use this function from within an interrupt context, like a bottom half + * handler. If you need an asynchronous message, or need to send a message + * from within interrupt context, use usb_submit_urb() If a thread in your + * driver uses this call, make sure your disconnect() method can wait for it to + * complete. Since you don't have a handle on the URB used, you can't cancel + * the request. + * + * Because there is no usb_interrupt_msg() and no USBDEVFS_INTERRUPT ioctl, + * users are forced to abuse this routine by using it to submit URBs for + * interrupt endpoints. We will take the liberty of creating an interrupt URB + * (with the default interval) if the target is an interrupt endpoint. + * + * Return: + * If successful, 0. Otherwise a negative error number. The number of actual + * bytes transferred will be stored in the @actual_length parameter. + * + */ +int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, + void *data, int len, int *actual_length, int timeout); + + +/** + * usb_interrupt_msg - Builds an interrupt urb, sends it off and waits for completion + * @usb_dev: pointer to the usb device to send the message to + * @pipe: endpoint "pipe" to send the message to + * @data: pointer to the data to send + * @len: length in bytes of the data to send + * @actual_length: pointer to a location to put the actual length transferred + * in bytes + * @timeout: time in msecs to wait for the message to complete before + * timing out (if 0 the wait is forever) + * + * Context: !in_interrupt () + * + * This function sends a simple interrupt message to a specified endpoint and + * waits for the message to complete, or timeout. + * + * Don't use this function from within an interrupt context, like a bottom half + * handler. If you need an asynchronous message, or need to send a message + * from within interrupt context, use usb_submit_urb() If a thread in your + * driver uses this call, make sure your disconnect() method can wait for it to + * complete. Since you don't have a handle on the URB used, you can't cancel + * the request. + * + * Return: + * If successful, 0. Otherwise a negative error number. The number of actual + * bytes transferred will be stored in the @actual_length parameter. + */ +int usb_interrupt_msg(struct usb_device *usb_dev, unsigned int pipe, + void *data, int len, int *actual_length, int timeout); +``` + + + +### 2.3 异步传输函数 + +使用URB进行传输时,它是异步方式:需要先分配、构造、提交一个URB("usb request block"),当传输完成后,它的回调函数被调用。 + +关键就在于需要填充URB: + +* dev:跟谁传输数据 +* pipe:跟哪个pipe传输数据 +* buffer:里面存有要发送的数据,或者用来接收要读取的数据 +* 数据长度 +* 回调函数 + + + +#### 2.3.1 分配和释放URB + +函数原型如下: + +```c +/** + * usb_alloc_urb - creates a new urb for a USB driver to use + * @iso_packets: number of iso packets for this urb + * @mem_flags: the type of memory to allocate, see kmalloc() for a list of + * valid options for this. + * + * Creates an urb for the USB driver to use, initializes a few internal + * structures, increments the usage counter, and returns a pointer to it. + * + * If the driver want to use this urb for interrupt, control, or bulk + * endpoints, pass '0' as the number of iso packets. + * + * The driver must call usb_free_urb() when it is finished with the urb. + * + * Return: A pointer to the new urb, or %NULL if no memory is available. + */ +struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags); + + +/** + * usb_free_urb - frees the memory used by a urb when all users of it are finished + * @urb: pointer to the urb to free, may be NULL + * + * Must be called when a user of a urb is finished with it. When the last user + * of the urb calls this function, the memory of the urb is freed. + * + * Note: The transfer buffer associated with the urb is not freed unless the + * URB_FREE_BUFFER transfer flag is set. + */ +void usb_free_urb(struct urb *urb); +``` + + + +#### 2.3.2 分配/释放DMA Buffer + +发起USB传输时,数据保存在buffer里。这个buffer可以是一般的buffer,也可以是DMA Buffer。 + +对于一般的buffer,在提交URB时会临时分配一个DMA Buffer: + +* 发送数据时:函数内部会先从一般buffer中把数据复制到DMA Buffer,在提交给USB控制器 +* 读取数据时:USB控制器先把数据传到DMA Buffer,函数内部在把DMA Buffer的数据复制到一般buffer +* 中间增加了一次数据的拷贝,效率低 + +我们可以直接使用DMA Buffer,函数原型如下: + +```c +/** + * usb_alloc_coherent - allocate dma-consistent buffer for URB_NO_xxx_DMA_MAP + * @dev: device the buffer will be used with + * @size: requested buffer size + * @mem_flags: affect whether allocation may block + * @dma: used to return DMA address of buffer + * + * Return: Either null (indicating no buffer could be allocated), or the + * cpu-space pointer to a buffer that may be used to perform DMA to the + * specified device. Such cpu-space buffers are returned along with the DMA + * address (through the pointer provided). + * + * Note: + * These buffers are used with URB_NO_xxx_DMA_MAP set in urb->transfer_flags + * to avoid behaviors like using "DMA bounce buffers", or thrashing IOMMU + * hardware during URB completion/resubmit. The implementation varies between + * platforms, depending on details of how DMA will work to this device. + * Using these buffers also eliminates cacheline sharing problems on + * architectures where CPU caches are not DMA-coherent. On systems without + * bus-snooping caches, these buffers are uncached. + * + * When the buffer is no longer used, free it with usb_free_coherent(). + */ +void *usb_alloc_coherent(struct usb_device *dev, size_t size, gfp_t mem_flags, + dma_addr_t *dma); + + +/** + * usb_free_coherent - free memory allocated with usb_alloc_coherent() + * @dev: device the buffer was used with + * @size: requested buffer size + * @addr: CPU address of buffer + * @dma: DMA address of buffer + * + * This reclaims an I/O buffer, letting it be reused. The memory must have + * been allocated using usb_alloc_coherent(), and the parameters must match + * those provided in that allocation request. + */ +void usb_free_coherent(struct usb_device *dev, size_t size, void *addr, + dma_addr_t dma); +``` + + + +#### 2.3.3 填充URB + +对于控制传输、批量传输、中断传输,分别有如下函数: + +```c +/** + * usb_fill_control_urb - initializes a control urb + * @urb: pointer to the urb to initialize. + * @dev: pointer to the struct usb_device for this urb. + * @pipe: the endpoint pipe + * @setup_packet: pointer to the setup_packet buffer + * @transfer_buffer: pointer to the transfer buffer + * @buffer_length: length of the transfer buffer + * @complete_fn: pointer to the usb_complete_t function + * @context: what to set the urb context to. + * + * Initializes a control urb with the proper information needed to submit + * it to a device. + */ +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); + + +/** + * usb_fill_bulk_urb - macro to help initialize a bulk urb + * @urb: pointer to the urb to initialize. + * @dev: pointer to the struct usb_device for this urb. + * @pipe: the endpoint pipe + * @transfer_buffer: pointer to the transfer buffer + * @buffer_length: length of the transfer buffer + * @complete_fn: pointer to the usb_complete_t function + * @context: what to set the urb context to. + * + * Initializes a bulk urb with the proper information needed to submit it + * to a device. + */ +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); + +/** + * usb_fill_int_urb - macro to help initialize a interrupt urb + * @urb: pointer to the urb to initialize. + * @dev: pointer to the struct usb_device for this urb. + * @pipe: the endpoint pipe + * @transfer_buffer: pointer to the transfer buffer + * @buffer_length: length of the transfer buffer + * @complete_fn: pointer to the usb_complete_t function + * @context: what to set the urb context to. + * @interval: what to set the urb interval to, encoded like + * the endpoint descriptor's bInterval value. + * + * Initializes a interrupt urb with the proper information needed to submit + * it to a device. + * + * Note that High Speed and SuperSpeed(+) interrupt endpoints use a logarithmic + * encoding of the endpoint interval, and express polling intervals in + * microframes (eight per millisecond) rather than in frames (one per + * millisecond). + * + * Wireless USB also uses the logarithmic encoding, but specifies it in units of + * 128us instead of 125us. For Wireless USB devices, the interval is passed + * through to the host controller, rather than being translated into microframe + * units. + */ +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); +``` + + + +如果URB使用DMA Buffer,那么还需要设置一个flag表明这点: + +```c +urb->transfer_dma = DMA address of buffer; // usb_alloc_coherent的输出参数 +urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; +``` + + + +#### 2.3.4 提交URB + +构造好URB后,需要提交到USB系统里,才能启动传输。 + +```c +/** + * usb_submit_urb - issue an asynchronous transfer request for an endpoint + * @urb: pointer to the urb describing the request + * @mem_flags: the type of memory to allocate, see kmalloc() for a list + * of valid options for this. + * + * This submits a transfer request, and transfers control of the URB + * describing that request to the USB subsystem. Request completion will + * be indicated later, asynchronously, by calling the completion handler. + * The three types of completion are success, error, and unlink + * (a software-induced fault, also called "request cancellation"). + * + * URBs may be submitted in interrupt context. + * + * The caller must have correctly initialized the URB before submitting + * it. Functions such as usb_fill_bulk_urb() and usb_fill_control_urb() are + * available to ensure that most fields are correctly initialized, for + * the particular kind of transfer, although they will not initialize + * any transfer flags. + * + * If the submission is successful, the complete() callback from the URB + * will be called exactly once, when the USB core and Host Controller Driver + * (HCD) are finished with the URB. When the completion function is called, + * control of the URB is returned to the device driver which issued the + * request. The completion handler may then immediately free or reuse that + * URB. + * + * With few exceptions, USB device drivers should never access URB fields + * provided by usbcore or the HCD until its complete() is called. + * The exceptions relate to periodic transfer scheduling. For both + * interrupt and isochronous urbs, as part of successful URB submission + * urb->interval is modified to reflect the actual transfer period used + * (normally some power of two units). And for isochronous urbs, + * urb->start_frame is modified to reflect when the URB's transfers were + * scheduled to start. + * + * Not all isochronous transfer scheduling policies will work, but most + * host controller drivers should easily handle ISO queues going from now + * until 10-200 msec into the future. Drivers should try to keep at + * least one or two msec of data in the queue; many controllers require + * that new transfers start at least 1 msec in the future when they are + * added. If the driver is unable to keep up and the queue empties out, + * the behavior for new submissions is governed by the URB_ISO_ASAP flag. + * If the flag is set, or if the queue is idle, then the URB is always + * assigned to the first available (and not yet expired) slot in the + * endpoint's schedule. If the flag is not set and the queue is active + * then the URB is always assigned to the next slot in the schedule + * following the end of the endpoint's previous URB, even if that slot is + * in the past. When a packet is assigned in this way to a slot that has + * already expired, the packet is not transmitted and the corresponding + * usb_iso_packet_descriptor's status field will return -EXDEV. If this + * would happen to all the packets in the URB, submission fails with a + * -EXDEV error code. + * + * For control endpoints, the synchronous usb_control_msg() call is + * often used (in non-interrupt context) instead of this call. + * That is often used through convenience wrappers, for the requests + * that are standardized in the USB 2.0 specification. For bulk + * endpoints, a synchronous usb_bulk_msg() call is available. + * + * Return: + * 0 on successful submissions. A negative error number otherwise. + * + * Request Queuing: + * + * URBs may be submitted to endpoints before previous ones complete, to + * minimize the impact of interrupt latencies and system overhead on data + * throughput. With that queuing policy, an endpoint's queue would never + * be empty. This is required for continuous isochronous data streams, + * and may also be required for some kinds of interrupt transfers. Such + * queuing also maximizes bandwidth utilization by letting USB controllers + * start work on later requests before driver software has finished the + * completion processing for earlier (successful) requests. + * + * As of Linux 2.6, all USB endpoint transfer queues support depths greater + * than one. This was previously a HCD-specific behavior, except for ISO + * transfers. Non-isochronous endpoint queues are inactive during cleanup + * after faults (transfer errors or cancellation). + * + * Reserved Bandwidth Transfers: + * + * Periodic transfers (interrupt or isochronous) are performed repeatedly, + * using the interval specified in the urb. Submitting the first urb to + * the endpoint reserves the bandwidth necessary to make those transfers. + * If the USB subsystem can't allocate sufficient bandwidth to perform + * the periodic request, submitting such a periodic request should fail. + * + * For devices under xHCI, the bandwidth is reserved at configuration time, or + * when the alt setting is selected. If there is not enough bus bandwidth, the + * configuration/alt setting request will fail. Therefore, submissions to + * periodic endpoints on devices under xHCI should never fail due to bandwidth + * constraints. + * + * Device drivers must explicitly request that repetition, by ensuring that + * some URB is always on the endpoint's queue (except possibly for short + * periods during completion callbacks). When there is no longer an urb + * queued, the endpoint's bandwidth reservation is canceled. This means + * drivers can use their completion handlers to ensure they keep bandwidth + * they need, by reinitializing and resubmitting the just-completed urb + * until the driver longer needs that periodic bandwidth. + * + * Memory Flags: + * + * The general rules for how to decide which mem_flags to use + * are the same as for kmalloc. There are four + * different possible values; GFP_KERNEL, GFP_NOFS, GFP_NOIO and + * GFP_ATOMIC. + * + * GFP_NOFS is not ever used, as it has not been implemented yet. + * + * GFP_ATOMIC is used when + * (a) you are inside a completion handler, an interrupt, bottom half, + * tasklet or timer, or + * (b) you are holding a spinlock or rwlock (does not apply to + * semaphores), or + * (c) current->state != TASK_RUNNING, this is the case only after + * you've changed it. + * + * GFP_NOIO is used in the block io path and error handling of storage + * devices. + * + * All other situations use GFP_KERNEL. + * + * Some more specific rules for mem_flags can be inferred, such as + * (1) start_xmit, timeout, and receive methods of network drivers must + * use GFP_ATOMIC (they are called with a spinlock held); + * (2) queuecommand methods of scsi drivers must use GFP_ATOMIC (also + * called with a spinlock held); + * (3) If you use a kernel thread with a network driver you must use + * GFP_NOIO, unless (b) or (c) apply; + * (4) after you have done a down() you can use GFP_KERNEL, unless (b) or (c) + * apply or your are in a storage driver's block io path; + * (5) USB probe and disconnect can use GFP_KERNEL unless (b) or (c) apply; and + * (6) changing firmware on a running storage or net device uses + * GFP_NOIO, unless b) or c) apply + * + */ +int usb_submit_urb(struct urb *urb, gfp_t mem_flags); +``` + + + +#### 2.3.5 取消URB + +已经提交的URB,可以取消它,有2个函数: + +* usb_kill_urb:这是一个同步函数,它会等待URB结束 +* usb_unlink_urb:这是一个异步函数,它不会等待URB结束,USB控制器驱动会调用它的回调函数 + +```c +/** + * usb_kill_urb - cancel a transfer request and wait for it to finish + * @urb: pointer to URB describing a previously submitted request, + * may be NULL + * + * This routine cancels an in-progress request. It is guaranteed that + * upon return all completion handlers will have finished and the URB + * will be totally idle and available for reuse. These features make + * this an ideal way to stop I/O in a disconnect() callback or close() + * function. If the request has not already finished or been unlinked + * the completion handler will see urb->status == -ENOENT. + * + * While the routine is running, attempts to resubmit the URB will fail + * with error -EPERM. Thus even if the URB's completion handler always + * tries to resubmit, it will not succeed and the URB will become idle. + * + * The URB must not be deallocated while this routine is running. In + * particular, when a driver calls this routine, it must insure that the + * completion handler cannot deallocate the URB. + * + * This routine may not be used in an interrupt context (such as a bottom + * half or a completion handler), or when holding a spinlock, or in other + * situations where the caller can't schedule(). + * + * This routine should not be called by a driver after its disconnect + * method has returned. + */ +void usb_kill_urb(struct urb *urb); + +/** + * usb_unlink_urb - abort/cancel a transfer request for an endpoint + * @urb: pointer to urb describing a previously submitted request, + * may be NULL + * + * This routine cancels an in-progress request. URBs complete only once + * per submission, and may be canceled only once per submission. + * Successful cancellation means termination of @urb will be expedited + * and the completion handler will be called with a status code + * indicating that the request has been canceled (rather than any other + * code). + * + * Drivers should not call this routine or related routines, such as + * usb_kill_urb() or usb_unlink_anchored_urbs(), after their disconnect + * method has returned. The disconnect function should synchronize with + * a driver's I/O routines to insure that all URB-related activity has + * completed before it returns. + * + * This request is asynchronous, however the HCD might call the ->complete() + * callback during unlink. Therefore when drivers call usb_unlink_urb(), they + * must not hold any locks that may be taken by the completion function. + * Success is indicated by returning -EINPROGRESS, at which time the URB will + * probably not yet have been given back to the device driver. When it is + * eventually called, the completion function will see @urb->status == + * -ECONNRESET. + * Failure is indicated by usb_unlink_urb() returning any other value. + * Unlinking will fail when @urb is not currently "linked" (i.e., it was + * never submitted, or it was unlinked before, or the hardware is already + * finished with it), even if the completion handler has not yet run. + * + * The URB must not be deallocated while this routine is running. In + * particular, when a driver calls this routine, it must insure that the + * completion handler cannot deallocate the URB. + * + * Return: -EINPROGRESS on success. See description for other values on + * failure. + * + * Unlinking and Endpoint Queues: + * + * [The behaviors and guarantees described below do not apply to virtual + * root hubs but only to endpoint queues for physical USB devices.] + * + * Host Controller Drivers (HCDs) place all the URBs for a particular + * endpoint in a queue. Normally the queue advances as the controller + * hardware processes each request. But when an URB terminates with an + * error its queue generally stops (see below), at least until that URB's + * completion routine returns. It is guaranteed that a stopped queue + * will not restart until all its unlinked URBs have been fully retired, + * with their completion routines run, even if that's not until some time + * after the original completion handler returns. The same behavior and + * guarantee apply when an URB terminates because it was unlinked. + * + * Bulk and interrupt endpoint queues are guaranteed to stop whenever an + * URB terminates with any sort of error, including -ECONNRESET, -ENOENT, + * and -EREMOTEIO. Control endpoint queues behave the same way except + * that they are not guaranteed to stop for -EREMOTEIO errors. Queues + * for isochronous endpoints are treated differently, because they must + * advance at fixed rates. Such queues do not stop when an URB + * encounters an error or is unlinked. An unlinked isochronous URB may + * leave a gap in the stream of packets; it is undefined whether such + * gaps can be filled in. + * + * Note that early termination of an URB because a short packet was + * received will generate a -EREMOTEIO error if and only if the + * URB_SHORT_NOT_OK flag is set. By setting this flag, USB device + * drivers can build deep queues for large or complex bulk transfers + * and clean them up reliably after any sort of aborted transfer by + * unlinking all pending URBs at the first fault. + * + * When a control URB terminates with an error other than -EREMOTEIO, it + * is quite likely that the status stage of the transfer will not take + * place. + */ +int usb_unlink_urb(struct urb *urb); +``` + diff --git a/IMX6ULL/doc_pic/12_USB/08_USB设备驱动模型.tif b/IMX6ULL/doc_pic/12_USB/08_USB设备驱动模型.tif new file mode 100644 index 0000000..c282621 Binary files /dev/null and b/IMX6ULL/doc_pic/12_USB/08_USB设备驱动模型.tif differ diff --git a/IMX6ULL/doc_pic/12_USB/pic/62_usb_bus_dev_drv.png b/IMX6ULL/doc_pic/12_USB/pic/62_usb_bus_dev_drv.png new file mode 100644 index 0000000..cfd4b5d Binary files /dev/null and b/IMX6ULL/doc_pic/12_USB/pic/62_usb_bus_dev_drv.png differ diff --git a/IMX6ULL/doc_pic/12_USB/pic/63_usb_driver.png b/IMX6ULL/doc_pic/12_USB/pic/63_usb_driver.png new file mode 100644 index 0000000..58b796a Binary files /dev/null and b/IMX6ULL/doc_pic/12_USB/pic/63_usb_driver.png differ diff --git a/IMX6ULL/doc_pic/12_USB/其他参考资料.md b/IMX6ULL/doc_pic/12_USB/其他参考资料.md index 5c9a52f..108a8f9 100644 --- a/IMX6ULL/doc_pic/12_USB/其他参考资料.md +++ b/IMX6ULL/doc_pic/12_USB/其他参考资料.md @@ -17,4 +17,22 @@ https://programmerall.com/article/42562063276/ -韦老师,USB里面休眠唤醒suspend/resueme/autosuspend/remote-wakeup这些也挺常用的,希望老师后面也可以讲一下 \ No newline at end of file +韦老师,USB里面休眠唤醒suspend/resueme/autosuspend/remote-wakeup这些也挺常用的,希望老师后面也可以讲一下 + + + +```shell +L:\kernel_projects\Linux-4.9.88\drivers\usb\core\message.c + usb_set_configuration + nintf = cp->desc.bNumInterfaces; + new_interfaces = kmalloc(nintf * sizeof(*new_interfaces), + GFP_NOIO); + + intf->dev.bus = &usb_bus_type; + intf->dev.type = &usb_if_device_type; + intf->dev.groups = usb_interface_groups; + + ret = device_add(&intf->dev); + +``` + diff --git a/README.md b/README.md index 8c488f2..3fbceb5 100644 --- a/README.md +++ b/README.md @@ -682,6 +682,12 @@ git clone https://e.coding.net/weidongshan/linux/doc_and_source_for_drivers.git 07.7_异步方式上机实验 ``` +* 2022.10.10 发布"USB子系统" + + ```shell + 08_USB设备驱动模型 + ``` + diff --git a/STM32MP157/doc_pic/12_USB/08_USB设备驱动模型.md b/STM32MP157/doc_pic/12_USB/08_USB设备驱动模型.md new file mode 100644 index 0000000..63e5ae7 --- /dev/null +++ b/STM32MP157/doc_pic/12_USB/08_USB设备驱动模型.md @@ -0,0 +1,632 @@ +# USB设备驱动模型 # + +参考资料: + +* Linux内核源码:`include\linux\usb.h` + +* Linux内核源码:`drivers\hid\usbhid\usbmouse.c` + + +## 1. BUS/DEV/DRV模型 + +![image-20221010144529823](pic/62_usb_bus_dev_drv.png) + +* "USB接口"是逻辑上的USB设备,我们编写的usb_driver驱动程序,支持的是"USB接口": + + ![image-20221010145250687](pic/63_usb_driver.png) + +* USB控制器或Hub识别出USB设备后,会创建、注册usb_deive + +* usb_device被"drivers\usb\core\generic.c"驱动认领后,会选择、设置某个配置 + +* 这个配置下面的接口,都会分配、设置、注册一个usb_interface + +* 左边的usb_driver和右边的usb_interface如果匹配,则掉用usb_driver.probe + + + +## 2. 接口函数 + +在USB设备驱动程序中,能使用的USB函数都在这个头文件里:`include\linux\usb.h`。 + +### 2.1 pipe + +使用这些接口函数的主要目的是传输数据,传输数据的对象是USB设备里的某个endpoint,这被称为pipe: + +```c +/* Create various pipes... */ +#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_sndisocpipe(dev, endpoint) \ + ((PIPE_ISOCHRONOUS << 30) | __create_pipe(dev, endpoint)) +#define usb_rcvisocpipe(dev, endpoint) \ + ((PIPE_ISOCHRONOUS << 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) +#define usb_sndintpipe(dev, endpoint) \ + ((PIPE_INTERRUPT << 30) | __create_pipe(dev, endpoint)) +#define usb_rcvintpipe(dev, endpoint) \ + ((PIPE_INTERRUPT << 30) | __create_pipe(dev, endpoint) | USB_DIR_IN) +``` + + + +### 2.2 同步传输函数 + +对于控制传输、批量传输、中断传输,有3个同步函数可以用来直接发起传输。这些函数内部会创建、填充、提交一个URB("usb request block"),并等待它完成或超时。 + +函数原型如下: + +```c +/** + * usb_control_msg - Builds a control urb, sends it off and waits for completion + * @dev: pointer to the usb device to send the message to + * @pipe: endpoint "pipe" to send the message to + * @request: USB message request value + * @requesttype: USB message request type value + * @value: USB message value + * @index: USB message index value + * @data: pointer to the data to send + * @size: length in bytes of the data to send + * @timeout: time in msecs to wait for the message to complete before timing + * out (if 0 the wait is forever) + * + * Context: !in_interrupt () + * + * This function sends a simple control message to a specified endpoint and + * waits for the message to complete, or timeout. + * + * Don't use this function from within an interrupt context, like a bottom half + * handler. If you need an asynchronous message, or need to send a message + * from within interrupt context, use usb_submit_urb(). + * If a thread in your driver uses this call, make sure your disconnect() + * method can wait for it to complete. Since you don't have a handle on the + * URB used, you can't cancel the request. + * + * Return: If successful, the number of bytes transferred. Otherwise, a negative + * error number. + */ +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); + +/** + * usb_bulk_msg - Builds a bulk urb, sends it off and waits for completion + * @usb_dev: pointer to the usb device to send the message to + * @pipe: endpoint "pipe" to send the message to + * @data: pointer to the data to send + * @len: length in bytes of the data to send + * @actual_length: pointer to a location to put the actual length transferred + * in bytes + * @timeout: time in msecs to wait for the message to complete before + * timing out (if 0 the wait is forever) + * + * Context: !in_interrupt () + * + * This function sends a simple bulk message to a specified endpoint + * and waits for the message to complete, or timeout. + * + * Don't use this function from within an interrupt context, like a bottom half + * handler. If you need an asynchronous message, or need to send a message + * from within interrupt context, use usb_submit_urb() If a thread in your + * driver uses this call, make sure your disconnect() method can wait for it to + * complete. Since you don't have a handle on the URB used, you can't cancel + * the request. + * + * Because there is no usb_interrupt_msg() and no USBDEVFS_INTERRUPT ioctl, + * users are forced to abuse this routine by using it to submit URBs for + * interrupt endpoints. We will take the liberty of creating an interrupt URB + * (with the default interval) if the target is an interrupt endpoint. + * + * Return: + * If successful, 0. Otherwise a negative error number. The number of actual + * bytes transferred will be stored in the @actual_length parameter. + * + */ +int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, + void *data, int len, int *actual_length, int timeout); + + +/** + * usb_interrupt_msg - Builds an interrupt urb, sends it off and waits for completion + * @usb_dev: pointer to the usb device to send the message to + * @pipe: endpoint "pipe" to send the message to + * @data: pointer to the data to send + * @len: length in bytes of the data to send + * @actual_length: pointer to a location to put the actual length transferred + * in bytes + * @timeout: time in msecs to wait for the message to complete before + * timing out (if 0 the wait is forever) + * + * Context: !in_interrupt () + * + * This function sends a simple interrupt message to a specified endpoint and + * waits for the message to complete, or timeout. + * + * Don't use this function from within an interrupt context, like a bottom half + * handler. If you need an asynchronous message, or need to send a message + * from within interrupt context, use usb_submit_urb() If a thread in your + * driver uses this call, make sure your disconnect() method can wait for it to + * complete. Since you don't have a handle on the URB used, you can't cancel + * the request. + * + * Return: + * If successful, 0. Otherwise a negative error number. The number of actual + * bytes transferred will be stored in the @actual_length parameter. + */ +int usb_interrupt_msg(struct usb_device *usb_dev, unsigned int pipe, + void *data, int len, int *actual_length, int timeout); +``` + + + +### 2.3 异步传输函数 + +使用URB进行传输时,它是异步方式:需要先分配、构造、提交一个URB("usb request block"),当传输完成后,它的回调函数被调用。 + +关键就在于需要填充URB: + +* dev:跟谁传输数据 +* pipe:跟哪个pipe传输数据 +* buffer:里面存有要发送的数据,或者用来接收要读取的数据 +* 数据长度 +* 回调函数 + + + +#### 2.3.1 分配和释放URB + +函数原型如下: + +```c +/** + * usb_alloc_urb - creates a new urb for a USB driver to use + * @iso_packets: number of iso packets for this urb + * @mem_flags: the type of memory to allocate, see kmalloc() for a list of + * valid options for this. + * + * Creates an urb for the USB driver to use, initializes a few internal + * structures, increments the usage counter, and returns a pointer to it. + * + * If the driver want to use this urb for interrupt, control, or bulk + * endpoints, pass '0' as the number of iso packets. + * + * The driver must call usb_free_urb() when it is finished with the urb. + * + * Return: A pointer to the new urb, or %NULL if no memory is available. + */ +struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags); + + +/** + * usb_free_urb - frees the memory used by a urb when all users of it are finished + * @urb: pointer to the urb to free, may be NULL + * + * Must be called when a user of a urb is finished with it. When the last user + * of the urb calls this function, the memory of the urb is freed. + * + * Note: The transfer buffer associated with the urb is not freed unless the + * URB_FREE_BUFFER transfer flag is set. + */ +void usb_free_urb(struct urb *urb); +``` + + + +#### 2.3.2 分配/释放DMA Buffer + +发起USB传输时,数据保存在buffer里。这个buffer可以是一般的buffer,也可以是DMA Buffer。 + +对于一般的buffer,在提交URB时会临时分配一个DMA Buffer: + +* 发送数据时:函数内部会先从一般buffer中把数据复制到DMA Buffer,在提交给USB控制器 +* 读取数据时:USB控制器先把数据传到DMA Buffer,函数内部在把DMA Buffer的数据复制到一般buffer +* 中间增加了一次数据的拷贝,效率低 + +我们可以直接使用DMA Buffer,函数原型如下: + +```c +/** + * usb_alloc_coherent - allocate dma-consistent buffer for URB_NO_xxx_DMA_MAP + * @dev: device the buffer will be used with + * @size: requested buffer size + * @mem_flags: affect whether allocation may block + * @dma: used to return DMA address of buffer + * + * Return: Either null (indicating no buffer could be allocated), or the + * cpu-space pointer to a buffer that may be used to perform DMA to the + * specified device. Such cpu-space buffers are returned along with the DMA + * address (through the pointer provided). + * + * Note: + * These buffers are used with URB_NO_xxx_DMA_MAP set in urb->transfer_flags + * to avoid behaviors like using "DMA bounce buffers", or thrashing IOMMU + * hardware during URB completion/resubmit. The implementation varies between + * platforms, depending on details of how DMA will work to this device. + * Using these buffers also eliminates cacheline sharing problems on + * architectures where CPU caches are not DMA-coherent. On systems without + * bus-snooping caches, these buffers are uncached. + * + * When the buffer is no longer used, free it with usb_free_coherent(). + */ +void *usb_alloc_coherent(struct usb_device *dev, size_t size, gfp_t mem_flags, + dma_addr_t *dma); + + +/** + * usb_free_coherent - free memory allocated with usb_alloc_coherent() + * @dev: device the buffer was used with + * @size: requested buffer size + * @addr: CPU address of buffer + * @dma: DMA address of buffer + * + * This reclaims an I/O buffer, letting it be reused. The memory must have + * been allocated using usb_alloc_coherent(), and the parameters must match + * those provided in that allocation request. + */ +void usb_free_coherent(struct usb_device *dev, size_t size, void *addr, + dma_addr_t dma); +``` + + + +#### 2.3.3 填充URB + +对于控制传输、批量传输、中断传输,分别有如下函数: + +```c +/** + * usb_fill_control_urb - initializes a control urb + * @urb: pointer to the urb to initialize. + * @dev: pointer to the struct usb_device for this urb. + * @pipe: the endpoint pipe + * @setup_packet: pointer to the setup_packet buffer + * @transfer_buffer: pointer to the transfer buffer + * @buffer_length: length of the transfer buffer + * @complete_fn: pointer to the usb_complete_t function + * @context: what to set the urb context to. + * + * Initializes a control urb with the proper information needed to submit + * it to a device. + */ +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); + + +/** + * usb_fill_bulk_urb - macro to help initialize a bulk urb + * @urb: pointer to the urb to initialize. + * @dev: pointer to the struct usb_device for this urb. + * @pipe: the endpoint pipe + * @transfer_buffer: pointer to the transfer buffer + * @buffer_length: length of the transfer buffer + * @complete_fn: pointer to the usb_complete_t function + * @context: what to set the urb context to. + * + * Initializes a bulk urb with the proper information needed to submit it + * to a device. + */ +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); + +/** + * usb_fill_int_urb - macro to help initialize a interrupt urb + * @urb: pointer to the urb to initialize. + * @dev: pointer to the struct usb_device for this urb. + * @pipe: the endpoint pipe + * @transfer_buffer: pointer to the transfer buffer + * @buffer_length: length of the transfer buffer + * @complete_fn: pointer to the usb_complete_t function + * @context: what to set the urb context to. + * @interval: what to set the urb interval to, encoded like + * the endpoint descriptor's bInterval value. + * + * Initializes a interrupt urb with the proper information needed to submit + * it to a device. + * + * Note that High Speed and SuperSpeed(+) interrupt endpoints use a logarithmic + * encoding of the endpoint interval, and express polling intervals in + * microframes (eight per millisecond) rather than in frames (one per + * millisecond). + * + * Wireless USB also uses the logarithmic encoding, but specifies it in units of + * 128us instead of 125us. For Wireless USB devices, the interval is passed + * through to the host controller, rather than being translated into microframe + * units. + */ +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); +``` + + + +如果URB使用DMA Buffer,那么还需要设置一个flag表明这点: + +```c +urb->transfer_dma = DMA address of buffer; // usb_alloc_coherent的输出参数 +urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; +``` + + + +#### 2.3.4 提交URB + +构造好URB后,需要提交到USB系统里,才能启动传输。 + +```c +/** + * usb_submit_urb - issue an asynchronous transfer request for an endpoint + * @urb: pointer to the urb describing the request + * @mem_flags: the type of memory to allocate, see kmalloc() for a list + * of valid options for this. + * + * This submits a transfer request, and transfers control of the URB + * describing that request to the USB subsystem. Request completion will + * be indicated later, asynchronously, by calling the completion handler. + * The three types of completion are success, error, and unlink + * (a software-induced fault, also called "request cancellation"). + * + * URBs may be submitted in interrupt context. + * + * The caller must have correctly initialized the URB before submitting + * it. Functions such as usb_fill_bulk_urb() and usb_fill_control_urb() are + * available to ensure that most fields are correctly initialized, for + * the particular kind of transfer, although they will not initialize + * any transfer flags. + * + * If the submission is successful, the complete() callback from the URB + * will be called exactly once, when the USB core and Host Controller Driver + * (HCD) are finished with the URB. When the completion function is called, + * control of the URB is returned to the device driver which issued the + * request. The completion handler may then immediately free or reuse that + * URB. + * + * With few exceptions, USB device drivers should never access URB fields + * provided by usbcore or the HCD until its complete() is called. + * The exceptions relate to periodic transfer scheduling. For both + * interrupt and isochronous urbs, as part of successful URB submission + * urb->interval is modified to reflect the actual transfer period used + * (normally some power of two units). And for isochronous urbs, + * urb->start_frame is modified to reflect when the URB's transfers were + * scheduled to start. + * + * Not all isochronous transfer scheduling policies will work, but most + * host controller drivers should easily handle ISO queues going from now + * until 10-200 msec into the future. Drivers should try to keep at + * least one or two msec of data in the queue; many controllers require + * that new transfers start at least 1 msec in the future when they are + * added. If the driver is unable to keep up and the queue empties out, + * the behavior for new submissions is governed by the URB_ISO_ASAP flag. + * If the flag is set, or if the queue is idle, then the URB is always + * assigned to the first available (and not yet expired) slot in the + * endpoint's schedule. If the flag is not set and the queue is active + * then the URB is always assigned to the next slot in the schedule + * following the end of the endpoint's previous URB, even if that slot is + * in the past. When a packet is assigned in this way to a slot that has + * already expired, the packet is not transmitted and the corresponding + * usb_iso_packet_descriptor's status field will return -EXDEV. If this + * would happen to all the packets in the URB, submission fails with a + * -EXDEV error code. + * + * For control endpoints, the synchronous usb_control_msg() call is + * often used (in non-interrupt context) instead of this call. + * That is often used through convenience wrappers, for the requests + * that are standardized in the USB 2.0 specification. For bulk + * endpoints, a synchronous usb_bulk_msg() call is available. + * + * Return: + * 0 on successful submissions. A negative error number otherwise. + * + * Request Queuing: + * + * URBs may be submitted to endpoints before previous ones complete, to + * minimize the impact of interrupt latencies and system overhead on data + * throughput. With that queuing policy, an endpoint's queue would never + * be empty. This is required for continuous isochronous data streams, + * and may also be required for some kinds of interrupt transfers. Such + * queuing also maximizes bandwidth utilization by letting USB controllers + * start work on later requests before driver software has finished the + * completion processing for earlier (successful) requests. + * + * As of Linux 2.6, all USB endpoint transfer queues support depths greater + * than one. This was previously a HCD-specific behavior, except for ISO + * transfers. Non-isochronous endpoint queues are inactive during cleanup + * after faults (transfer errors or cancellation). + * + * Reserved Bandwidth Transfers: + * + * Periodic transfers (interrupt or isochronous) are performed repeatedly, + * using the interval specified in the urb. Submitting the first urb to + * the endpoint reserves the bandwidth necessary to make those transfers. + * If the USB subsystem can't allocate sufficient bandwidth to perform + * the periodic request, submitting such a periodic request should fail. + * + * For devices under xHCI, the bandwidth is reserved at configuration time, or + * when the alt setting is selected. If there is not enough bus bandwidth, the + * configuration/alt setting request will fail. Therefore, submissions to + * periodic endpoints on devices under xHCI should never fail due to bandwidth + * constraints. + * + * Device drivers must explicitly request that repetition, by ensuring that + * some URB is always on the endpoint's queue (except possibly for short + * periods during completion callbacks). When there is no longer an urb + * queued, the endpoint's bandwidth reservation is canceled. This means + * drivers can use their completion handlers to ensure they keep bandwidth + * they need, by reinitializing and resubmitting the just-completed urb + * until the driver longer needs that periodic bandwidth. + * + * Memory Flags: + * + * The general rules for how to decide which mem_flags to use + * are the same as for kmalloc. There are four + * different possible values; GFP_KERNEL, GFP_NOFS, GFP_NOIO and + * GFP_ATOMIC. + * + * GFP_NOFS is not ever used, as it has not been implemented yet. + * + * GFP_ATOMIC is used when + * (a) you are inside a completion handler, an interrupt, bottom half, + * tasklet or timer, or + * (b) you are holding a spinlock or rwlock (does not apply to + * semaphores), or + * (c) current->state != TASK_RUNNING, this is the case only after + * you've changed it. + * + * GFP_NOIO is used in the block io path and error handling of storage + * devices. + * + * All other situations use GFP_KERNEL. + * + * Some more specific rules for mem_flags can be inferred, such as + * (1) start_xmit, timeout, and receive methods of network drivers must + * use GFP_ATOMIC (they are called with a spinlock held); + * (2) queuecommand methods of scsi drivers must use GFP_ATOMIC (also + * called with a spinlock held); + * (3) If you use a kernel thread with a network driver you must use + * GFP_NOIO, unless (b) or (c) apply; + * (4) after you have done a down() you can use GFP_KERNEL, unless (b) or (c) + * apply or your are in a storage driver's block io path; + * (5) USB probe and disconnect can use GFP_KERNEL unless (b) or (c) apply; and + * (6) changing firmware on a running storage or net device uses + * GFP_NOIO, unless b) or c) apply + * + */ +int usb_submit_urb(struct urb *urb, gfp_t mem_flags); +``` + + + +#### 2.3.5 取消URB + +已经提交的URB,可以取消它,有2个函数: + +* usb_kill_urb:这是一个同步函数,它会等待URB结束 +* usb_unlink_urb:这是一个异步函数,它不会等待URB结束,USB控制器驱动会调用它的回调函数 + +```c +/** + * usb_kill_urb - cancel a transfer request and wait for it to finish + * @urb: pointer to URB describing a previously submitted request, + * may be NULL + * + * This routine cancels an in-progress request. It is guaranteed that + * upon return all completion handlers will have finished and the URB + * will be totally idle and available for reuse. These features make + * this an ideal way to stop I/O in a disconnect() callback or close() + * function. If the request has not already finished or been unlinked + * the completion handler will see urb->status == -ENOENT. + * + * While the routine is running, attempts to resubmit the URB will fail + * with error -EPERM. Thus even if the URB's completion handler always + * tries to resubmit, it will not succeed and the URB will become idle. + * + * The URB must not be deallocated while this routine is running. In + * particular, when a driver calls this routine, it must insure that the + * completion handler cannot deallocate the URB. + * + * This routine may not be used in an interrupt context (such as a bottom + * half or a completion handler), or when holding a spinlock, or in other + * situations where the caller can't schedule(). + * + * This routine should not be called by a driver after its disconnect + * method has returned. + */ +void usb_kill_urb(struct urb *urb); + +/** + * usb_unlink_urb - abort/cancel a transfer request for an endpoint + * @urb: pointer to urb describing a previously submitted request, + * may be NULL + * + * This routine cancels an in-progress request. URBs complete only once + * per submission, and may be canceled only once per submission. + * Successful cancellation means termination of @urb will be expedited + * and the completion handler will be called with a status code + * indicating that the request has been canceled (rather than any other + * code). + * + * Drivers should not call this routine or related routines, such as + * usb_kill_urb() or usb_unlink_anchored_urbs(), after their disconnect + * method has returned. The disconnect function should synchronize with + * a driver's I/O routines to insure that all URB-related activity has + * completed before it returns. + * + * This request is asynchronous, however the HCD might call the ->complete() + * callback during unlink. Therefore when drivers call usb_unlink_urb(), they + * must not hold any locks that may be taken by the completion function. + * Success is indicated by returning -EINPROGRESS, at which time the URB will + * probably not yet have been given back to the device driver. When it is + * eventually called, the completion function will see @urb->status == + * -ECONNRESET. + * Failure is indicated by usb_unlink_urb() returning any other value. + * Unlinking will fail when @urb is not currently "linked" (i.e., it was + * never submitted, or it was unlinked before, or the hardware is already + * finished with it), even if the completion handler has not yet run. + * + * The URB must not be deallocated while this routine is running. In + * particular, when a driver calls this routine, it must insure that the + * completion handler cannot deallocate the URB. + * + * Return: -EINPROGRESS on success. See description for other values on + * failure. + * + * Unlinking and Endpoint Queues: + * + * [The behaviors and guarantees described below do not apply to virtual + * root hubs but only to endpoint queues for physical USB devices.] + * + * Host Controller Drivers (HCDs) place all the URBs for a particular + * endpoint in a queue. Normally the queue advances as the controller + * hardware processes each request. But when an URB terminates with an + * error its queue generally stops (see below), at least until that URB's + * completion routine returns. It is guaranteed that a stopped queue + * will not restart until all its unlinked URBs have been fully retired, + * with their completion routines run, even if that's not until some time + * after the original completion handler returns. The same behavior and + * guarantee apply when an URB terminates because it was unlinked. + * + * Bulk and interrupt endpoint queues are guaranteed to stop whenever an + * URB terminates with any sort of error, including -ECONNRESET, -ENOENT, + * and -EREMOTEIO. Control endpoint queues behave the same way except + * that they are not guaranteed to stop for -EREMOTEIO errors. Queues + * for isochronous endpoints are treated differently, because they must + * advance at fixed rates. Such queues do not stop when an URB + * encounters an error or is unlinked. An unlinked isochronous URB may + * leave a gap in the stream of packets; it is undefined whether such + * gaps can be filled in. + * + * Note that early termination of an URB because a short packet was + * received will generate a -EREMOTEIO error if and only if the + * URB_SHORT_NOT_OK flag is set. By setting this flag, USB device + * drivers can build deep queues for large or complex bulk transfers + * and clean them up reliably after any sort of aborted transfer by + * unlinking all pending URBs at the first fault. + * + * When a control URB terminates with an error other than -EREMOTEIO, it + * is quite likely that the status stage of the transfer will not take + * place. + */ +int usb_unlink_urb(struct urb *urb); +``` + diff --git a/STM32MP157/doc_pic/12_USB/08_USB设备驱动模型.tif b/STM32MP157/doc_pic/12_USB/08_USB设备驱动模型.tif new file mode 100644 index 0000000..c282621 Binary files /dev/null and b/STM32MP157/doc_pic/12_USB/08_USB设备驱动模型.tif differ diff --git a/STM32MP157/doc_pic/12_USB/pic/62_usb_bus_dev_drv.png b/STM32MP157/doc_pic/12_USB/pic/62_usb_bus_dev_drv.png new file mode 100644 index 0000000..cfd4b5d Binary files /dev/null and b/STM32MP157/doc_pic/12_USB/pic/62_usb_bus_dev_drv.png differ diff --git a/STM32MP157/doc_pic/12_USB/pic/63_usb_driver.png b/STM32MP157/doc_pic/12_USB/pic/63_usb_driver.png new file mode 100644 index 0000000..58b796a Binary files /dev/null and b/STM32MP157/doc_pic/12_USB/pic/63_usb_driver.png differ diff --git a/STM32MP157/doc_pic/12_USB/其他参考资料.md b/STM32MP157/doc_pic/12_USB/其他参考资料.md index 5c9a52f..108a8f9 100644 --- a/STM32MP157/doc_pic/12_USB/其他参考资料.md +++ b/STM32MP157/doc_pic/12_USB/其他参考资料.md @@ -17,4 +17,22 @@ https://programmerall.com/article/42562063276/ -韦老师,USB里面休眠唤醒suspend/resueme/autosuspend/remote-wakeup这些也挺常用的,希望老师后面也可以讲一下 \ No newline at end of file +韦老师,USB里面休眠唤醒suspend/resueme/autosuspend/remote-wakeup这些也挺常用的,希望老师后面也可以讲一下 + + + +```shell +L:\kernel_projects\Linux-4.9.88\drivers\usb\core\message.c + usb_set_configuration + nintf = cp->desc.bNumInterfaces; + new_interfaces = kmalloc(nintf * sizeof(*new_interfaces), + GFP_NOIO); + + intf->dev.bus = &usb_bus_type; + intf->dev.type = &usb_if_device_type; + intf->dev.groups = usb_interface_groups; + + ret = device_add(&intf->dev); + +``` +