IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> usb host 驱动之 urb -> 正文阅读

[嵌入式]usb host 驱动之 urb

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)
// iso_packets: urb 所包含的等时数据包的个数,若不是等时传输,则为0.
// mem_flags: 内存分配标识(如GFP_KERNEL),参考kmalloc

1.2 初始化URB

对于中断urb,使用usb_fill_int_urb函数来初始化:

static inline void usb_fill_int_urb (struct urb *urb,	//要初始化的urb指针。
                     struct usb_device *dev,	//所要访问的设备
                     unsigned int      pipe,	//要访问的端点所对应的管道,使用usb_sndintpipe()或usb_rcvintpipe()创建
                     void              *transfer_buffer,	//要传输的数据缓冲区
                     int               buffer_length,		//缓冲区长度
                     usb_complete_t    complete_fn,			//当完成该urb所请求的操作时,要调用的回调函数
                     void              *context,			//complet_fn函数所需的上下文,通常取值为dev
                     int               interval)			//urb被调度的时间间隔

管道:驱动程序的数据缓冲区与一个端点的连接,它代表了一种在两者之间移动数据的能力。

/*
 * For various legacy reasons, Linux has a small cookie that's paired with
 * a struct usb_device to identify an endpoint queue.  Queue characteristics
 * are defined by the endpoint's descriptor.  This cookie is called a "pipe",
 * an unsigned int encoded as:
 *
 *  - direction:	bit 7		(0 = Host-to-Device [Out],
 *					 1 = Device-to-Host [In] ...
 *					like endpoint bEndpointAddress)
 *  - device address:	bits 8-14       ... bit positions known to uhci-hcd
 *  - endpoint:		bits 15-18      ... bit positions known to uhci-hcd
 *  - pipe type:	bits 30-31	(00 = isochronous, 01 = interrupt,
 *					 10 = control, 11 = bulk)
 *
 * Given the device address and endpoint descriptor, pipes are redundant.
 */

端点:设备的固有属性,用来与主机通信。
对于批量 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)
// urb:指向urb的指针;
// mem_flags:内存分配标识,它用于告知usb核心如何分配内存缓冲区。

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()		//add an URB to its endpoint queue
							== 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);	//queueing a TRB on a ring
			== inc_enq()		trace_xhci_inc_enq(ring);
		== giveback_first_trb()		//Pass all the TRBs to the hardware at once and make sure this write isn't reordered.
			== 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()		//Process bulk and interrupt tds, update urb status and actual_length.
				== 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)
			   //Remove the URB's TD from the endpoint ring.
			   == list_add_tail(&td->cancelled_td_list, &ep->cancelled_td_list);
			   
//如果dequeue 失败,则直接giveback urb,不用等xhci_irq
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()		//发送一个stop ep 的cmd

//dequeue 成功之后,产生中断cmd stop ep
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 初始化

   //root hub(3-1:1.0 2-1:1.0)作为struct usb_device *hdev的一个interface注册后:
== hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
	   //配置hub,interface的端点0:
	== hub_configure(hub, &desc->endpoint[0].desc);
		== usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq,
			hub, endpoint->bInterval);			//初始化hub->urb和完成函数hub_irq
		== hub_activate(hub, HUB_INIT);			//启用hub
			== usb_submit_urb(hub->urb, GFP_NOIO);		//提交hub->urb
			
   //提交后若hub上有设备插入等端口状态变化事件发生,则会调用完成函数hub_irq
== hub_irq(struct urb *urb)
	== kick_hub_wq(hub);	/* Something happened, let hub_wq figure it out */
		== queue_work(hub_wq, &hub->events)
			== hub_event(struct work_struct *work)
				== port_event(struct usb_hub *hub, int port1)
//根据hub->change_bits、hub->event_bits、hub->wakeup_bits检测是哪个port发生了事件,并根据portchange和portstatus判断发生了什么事件。

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);		//提交hub->urb
	== usb_hcd_submit_urb()
		== rh_urb_enqueue(hcd, urb);		//root hub
			== rh_call_control()			//control urb
				== hcd->driver->hub_control()
					.hub_control =		xhci_hub_control,
					== xhci_hub_control()
						== xhci_get_port_status()
			== rh_queue_status(hcd, urb);	//int urb 循环检测hub 的状态
				== 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.c
    ->hcd->driver->hub_status_data(hcd, buffer)			//xhci_hub_status
    ->usb_hcd_unlink_urb_from_ep(hcd, urb);
    ->usb_hcd_giveback_urb(hcd, urb, 0)
         ->usb_giveback_urb_bh();//tasklet_hi_schedule(&bh->bh);
            ->__usb_hcd_giveback_urb(urb);
              ->urb->complete(urb);//hub_irq
                ->hub_irq //hub.c  usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq,
                  ->kick_hub_wq(hub);
                    ->hub_event //INIT_WORK(&hub->events, 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);//枚举完毕后加载设备驱动
  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-03-21 21:07:25  更:2022-03-21 21:11:42 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 6:50:05-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码