1、URB 处理流程
(1)usb 设备驱动程序创建并初始化一个访问特定usb设备特定端点的 urb,并提交给 usb core; (2)usb core 提交该 urb 到 usb 主控制器驱动程序; (3)usb 主控制器驱动程序根据 urb 描述的信息,来访问 usb 设备; (4)当设备访问结束后,usb 主控制器驱动程序通知 usb core(调用这个函数usb_complete_t complete; )然后其再通知usb设备驱动程序。
1.1 创建URB
struct urb *usb_alloc_urb(int iso_packets, int mem_flags)
1.2 初始化URB
对于中断urb,使用usb_fill_int_urb函数来初始化:
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,使用usb_fill_bulk_urb 函数来初始化。 对于控制 urb,使用usb_fill_control_urb 函数来初始化。 等时 urb 没有像中断,控制和批量 urb 那样的初始化函数,我们只能手动的初始化 urb。
1.3 提交URB
在完成urb的创建和初始化后,urb 便可以通过usb_submit_urb 函数来提交给usb核心:
int usb_submit_urb(struct urb *urb,gfp_t mem_flags)
1.4 处理URB
URB 被提交到 USB 核心后,usb 核心指定 usb 主控制器驱动程序来处理该 urb,在 3 种情况下,urb会被认为处理完成: (1)urb 被成功发送给设备,并且设备返回成功确认。如果urb->status 为0,意味着对于一个输出urb,数据被成功发送;对于一个输入urb,请求的数据被成功收到。 (2)如果发送数据到设备或从设备接受数据时发生了错误,urb->status 将记录错误值。 (3)urb 被“取消”,这发生在驱动通过usb_unlink_urb() 或usb_kill_urb() 函数取消 urb,或 urb 虽已提交,而 usb 设备被拔出的情况下。
当urb处理完成后,urb 完成函数将被调用。
2、urb submit and giveback
2.1 submit urb
usb_submit_urb() 是异步函数,usb_fill_bulk_urb() 有注册完成函数,urb 提交usb_submit_urb() 立马返回,usb 设备处理完后 usb core 就会回调urb->complete() 。 手机内存 cp 数据到外设 U盘 urb 过程:
2.1.1 设备驱动submit urb 到hcd
SUBMIT_CMD_URB :
== uas_submit_urbs(struct scsi_cmnd *cmnd, struct uas_dev_info *devinfo)
== usb_submit_urb(cmdinfo->cmd_urb, GFP_ATOMIC);
== usb_hcd_submit_urb(struct urb *urb, gfp_t mem_flags)
hcd->driver->urb_enqueue(hcd, urb, mem_flags);
== xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags)
== xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
struct urb *urb, int slot_id, unsigned int ep_index)
== prepare_transfer()
== usb_hcd_link_urb_to_ep()
== list_add_tail(&urb->urb_list, &urb->ep->urb_list);
SUBMIT_DATA_IN_URB:
== uas_submit_urbs(struct scsi_cmnd *cmnd, struct uas_dev_info *devinfo)
== usb_submit_urb(cmdinfo->data_in_urb, GFP_ATOMIC);
usb_hcd_link_urb_to_ep() 是 usb core 的 api,目的是 hcd 主机控制器把 urb 放入端点队列中,等待底层发送。 调用usb_hcd_unlink_urb_from_ep() 和usb_hcd_giveback_urb() 将 urb 从 usb core 归还给设备驱动,完成一次 urb 的处理。
2.1.2 hcd 发送urb
== xhci_urb_enqueue() trace_xhci_urb_enqueue(urb);
== xhci_queue_bulk_tx()
== prepare_transfer()
== queue_trb() trace_xhci_queue_trb(ring, trb);
== inc_enq() trace_xhci_inc_enq(ring);
== giveback_first_trb()
== xhci_ring_ep_doorbell() trace_xhci_ring_ep_doorbell(slot_id, DB_VALUE(ep_index, stream_id));
== writel(DB_VALUE(ep_index, stream_id), db_addr);
2.1.3 hcd 接收中断 giveback urb 给设备驱动
data urb:
inc_deq trace_xhci_inc_deq(ring);
== xhci_irq(struct usb_hcd *hcd)
== xhci_handle_event(struct xhci_hcd *xhci) trace_xhci_handle_event(xhci->event_ring, &event->generic);
== handle_tx_event(struct xhci_hcd *xhci,
struct xhci_transfer_event *event) trace_xhci_handle_transfer(ep_ring, (struct xhci_generic_trb *) ep_trb)
== process_bulk_intr_td()
== finish_td(struct xhci_hcd *xhci, struct xhci_td *td,
struct xhci_transfer_event *event,
struct xhci_virt_ep *ep, int *status)
== xhci_td_cleanup()
== xhci_giveback_urb_in_irq() trace_xhci_urb_giveback(urb);
== usb_hcd_unlink_urb_from_ep(hcd, urb);
== usb_hcd_giveback_urb(hcd, urb, status);
cmd urb:
inc_deq() trace_xhci_inc_deq(ring);
== xhci_irq()
== xhci_handle_event() trace_xhci_handle_event(xhci->event_ring, &event->generic);
== handle_cmd_completion() trace_xhci_handle_command(xhci->cmd_ring, &cmd_trb->generic);
== xhci_handle_cmd_set_deq() trace_xhci_handle_cmd_set_deq(slot_ctx);
trace_xhci_handle_cmd_set_deq_ep(ep_ctx);
== xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
"Successful Set TR Deq Ptr cmd, deq = @%08llx", deq);
xhci_td_cleanup()
xhci_giveback_urb_in_irq() trace_xhci_urb_giveback(urb);
2.2 kill urb
== usb_kill_urb(struct urb *urb)
== usb_hcd_unlink_urb(struct urb *urb, int status)
== unlink1(hcd, urb, status);
hcd->driver->urb_dequeue(hcd, urb, status);
== xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
== list_add_tail(&td->cancelled_td_list, &ep->cancelled_td_list);
usb_hcd_unlink_urb_from_ep()——usb_hcd_giveback_urb()——tasklet_hi_schedule(&bh->bh);——usb_giveback_urb_bh()——__usb_hcd_giveback_urb()
usb_add_hcd()——init_giveback_urb_bh()——tasklet_setup(&bh->bh);——tasklet_schedule(&bh->bh);
2.3 dequeue urb
== xhci_urb_dequeue() trace_xhci_urb_dequeue(urb);
== xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
"Cancel URB %p, dev %s, ep 0x%x, "
"starting at offset 0x%llx", ...)
== xhci_queue_stop_endpoint()
inc_deq trace_xhci_inc_deq(ring);
== xhci_irq()
== xhci_handle_event() trace_xhci_handle_event(xhci->event_ring, &event->generic);
== handle_cmd_completion() trace_xhci_handle_command(xhci->cmd_ring, &cmd_trb->generic);
== xhci_handle_cmd_stop_ep() trace_xhci_handle_cmd_stop_ep(ep_ctx);
== xhci_invalidate_cancelled_tds() xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
"Removing canceled TD starting at 0x%llx (dma).",
== xhci_move_dequeue_past_td() xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
"Set TR Deq ptr 0x%llx, cycle %u\n", addr, new_cycle);
== xhci_ring_cmd_db() trace_xhci_ring_host_doorbell(0, DB_VALUE_HOST);
== xhci_giveback_invalidated_tds(ep);
3、hub urb
3.1 hub urb 初始化
== hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
== hub_configure(hub, &desc->endpoint[0].desc);
== usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq,
hub, endpoint->bInterval);
== hub_activate(hub, HUB_INIT);
== usb_submit_urb(hub->urb, GFP_NOIO);
== hub_irq(struct urb *urb)
== kick_hub_wq(hub);
== queue_work(hub_wq, &hub->events)
== hub_event(struct work_struct *work)
== port_event(struct usb_hub *hub, int port1)
3.2 USB设备插入调用urb
当 root hub 检测到有 usb 设备插入,最后是怎么加载U盘驱动的?我做了简单的代码走读,通过枚举到的 PID/VID 信息匹配到 U盘驱动或者 HID 键鼠驱动等,就回调相应驱动的 probe 驱动入口了,最后就能看到 /dev/sda 或者 /dev/input/even0 了:
== usb_submit_urb(hub->urb, GFP_NOIO);
== usb_hcd_submit_urb()
== rh_urb_enqueue(hcd, urb);
== rh_call_control()
== hcd->driver->hub_control()
.hub_control = xhci_hub_control,
== xhci_hub_control()
== xhci_get_port_status()
== rh_queue_status(hcd, urb);
== usb_hcd_link_urb_to_ep(hcd, urb);
== mod_timer(&hcd->rh_timer, jiffies);
timer_setup(&hcd->rh_timer, rh_timer_func, 0);
== rh_timer_func()
== usb_hcd_poll_rh_status(_hcd);
->usb_hcd_poll_rh_status()
->hcd->driver->hub_status_data(hcd, buffer)
->usb_hcd_unlink_urb_from_ep(hcd, urb);
->usb_hcd_giveback_urb(hcd, urb, 0)
->usb_giveback_urb_bh();
->__usb_hcd_giveback_urb(urb);
->urb->complete(urb);
->hub_irq
->kick_hub_wq(hub);
->hub_event
->port_event(hub, i);
->hub_port_connect_change
->hub_port_connect
->hub_port_init
->usb_new_device(udev);
->usb_enumerate_device(udev);
->device_add(&udev->dev);
|