struct urb { /* private: usb core and host controller only fields in the urb */ struct kref kref; /* kref,urb 的引用计数 ,每多一个使用者,它的这个引用计数就加1, 每减少一个使用者,引用计数就减一,如果连最后一个使用者都释放了 这个urb,宣称不再使用它了*/ void *hcpriv; /* private data for host controller */ atomic_t use_count; /* concurrent submissions counter */ atomic_t reject; /* submissions will fail */ int unlinked; /* unlink error code */ /* public: documented fields in the urb that can be used by drivers */ struct list_head urb_list; /* list head for use by the urb's //还记得每个端点都会有的那个urb 队列么?那个队列就是由这里的 //urb_list 一个一个的链接起来的。HCD 每收到一个urb,就会将它添加到这个urb 指定的 //那个端点的urb 队列里去 * current owner 它表示的是urb 要去的那个usb 设备*/ struct list_head anchor_list; /* the URB may be anchored */ struct usb_anchor *anchor; struct usb_device *dev; /* (in) pointer to associated device */ struct usb_host_endpoint *ep; /* (internal) pointer to endpoint */ unsigned int pipe; /* (in) pipe information. //urb 到达端点之前,需要经过一个通往端点的管道,就是这个pipe。管道有两端,一端是主机上的缓冲区,一端是设备上的端点,既 //然有两端,总要有个方向吧,确定一条管道至少要知道两端的地址、方向和类型了,这个整型值的构成,bit7 用来表示方向,bit8~ //14 表示设备地址,bit15~18 表示端点号,早先说过,设备地址用7 位来表示,端点号用4 位来表示,剩下来的bit30~31 表示管 //道类型。*/ unsigned int stream_id; /* (in) stream ID */ int status; /* (return) non-ISO status //urb 的当前状态*/ unsigned int transfer_flags; /* (in) URB_SHORT_NOT_OK | ...*/ void *transfer_buffer; /* (in) associated data buffer */ dma_addr_t transfer_dma; /* (in) dma addr for transfer_buffer */ struct scatterlist *sg; /* (in) scatter gather buffer list */ int num_sgs; /* (in) number of entries in the sg list */ u32 transfer_buffer_length; /* (in) data buffer length */ u32 actual_length; /* (return) actual transfer length */ unsigned char *setup_packet; /* (in) setup packet (control only) */ dma_addr_t setup_dma; /* (in) dma addr for setup_packet */ int start_frame; /* (modify) start frame (ISO) */ int number_of_packets; /* (in) number of ISO packets */ int interval; /* (modify) transfer interval * (INT/ISO) */ int error_count; /* (return) number of ISO errors */ void *context; /* (in) context for completion */ usb_complete_t complete; /* (in) completion routine */ struct usb_iso_packet_descriptor iso_frame_desc[0]; /* (in) ISO ONLY */ };
使用 urb 来完成一次完整的usb 通信都要经历哪些阶段,首先,驱动程序发现自己有与usb 设备通信的需要,于是创建一个urb,并指定它的目的地是设备上的哪个端点,
然后提交给usb core,usb core 将它修修补补的做些美化之后再移交给主机控制器的驱动程序HCD,HCD 会去解析这个urb,了解它的目的是什么,并与usb 设备进行相应的
交流,在交流结束,urb 的目的达到之后,HCD 再把这个urb 的所有权移交回驱动程序.
urb 驱动也创建了,提交也提交了,HCD 正处理着那,可驱动反悔了,它不想再继续这次通信了,想将这个urb 给终止掉。
一种是驱动只想通过usb core 告诉HCD 一声,说这个urb 我想终止掉,您就别费心再处理了,然后它不想在那里等着HCD 的处理,想忙别的事去,这就是俗称的异步,对应的是usb_unlink_urb 函数。当然对应的还有种同步的,驱动会在那里苦苦等候着HCD 的处理结果,等待着urb 被终止,对应的是usb_kill_urb 函数。而HCD 将这次通信终止后,同样会将urb 的所有权移交回驱动。那么驱动通过什么判断HCD 已经终止了这次通信?就是通过这里的use_count,驱动会在usb_kill_urb 里面一直等待着这个值变为0。
在include/linux/kref.h 里定义 struct kref { atomic_t refcount; }; void kref_init(struct kref *kref) { atomic_set(&kref->refcount, 1); smp_mb(); } void usb_init_urb(struct urb *urb) { if (urb) { memset(urb, 0, sizeof(*urb)); kref_init(&urb->kref); INIT_LIST_HEAD(&urb->anchor_list); } } struct urb *usb_get_urb(struct urb *urb) { if (urb) kref_get(&urb->kref); return urb; } void usb_free_urb(struct urb *urb) { if (urb) kref_put(&urb->kref, urb_destroy); }
usb_init_urb、usb_get_urb、usb_free_urb 这三个函数分别调用了前面看到的structkref 结构的三个操作函数来进行引用计数的初始化、加1、减一.
static void urb_destroy(struct kref *kref) { struct urb *urb = to_urb(kref); //先调用了to_urb,实际上就是一个container_of 来获得引用计数关联的那个urb,然后使用kfree 将它销毁。 if (urb->transfer_flags & URB_FREE_BUFFER) kfree(urb->transfer_buffer); kfree(urb); } void usb_kill_urb(struct urb *urb) { might_sleep();//因为usb_kill_urb函数要一直等候着HCD将urb终止掉,它必须是可以休眠的 if (!(urb && urb->dev && urb->ep))//这里就是判断一下urb,urb 要去的那个设备,还有那个设备在的总线有没有,如 //果哪个不存在,就还是返回吧。 return; atomic_inc(&urb->reject); usb_hcd_unlink_urb(urb, -ENOENT);//里告诉HCD驱动要终止这个urb了,usb_hcd_unlink_urb函数也只是告诉HCD //一声,然后不管HCD怎么处理就返回了 //上面的usb_hcd_unlink_urb是返回了,但并不代表HCD已经将urb给终止了, //HCD可能没那么快,所以这里usb_kill_urb要休息休息,等人通知它。这里使用了 //wait_event宏来实现休眠 wait_event(usb_kill_urb_queue, atomic_read(&urb->use_count) == 0); atomic_dec(&urb->reject); }