Linux内核urb接口函数详解

1. urb简介

        usb总线是一种轮询式总线,协议规定所有的数据传输都必须由主机发起,usb主机与设备之间是通过管道(pipe)传输的,管道两边分别对应主机中的数据缓冲区和设备侧的端点(endpoint),端点是通信的发送和接收点,要发送数据,只要把数据发到对应的端点就可以,而这个数据发送的动作由usb主机实现,驱动中只需确定接收端点,然后把数据提交给主机控制器,主机会把数据发送给接收端点,原理同i2c,uart类似。每个usb设备中都存在一个特殊端点endpoint0,在usb设备枚举过程里,就是通过endpoint0来获取usb设备信息。

        USB按传输类型分可以分为控制传输(control),中断传输(interrupt),等时传输(isochronous),批量传输(bulk),其中控制传输和批量传输又称为非周期性传输方式(nonperiodic),而中断传输和等时传输称为周期性传输方式(periodic);

        一个urb 用来发送或接受数据到或者从一个特定 USB 设备上的特定的 USB 端点, 以一种异步的方式.一个 USB 设备驱动可能分配许多 urb 给一个端点或者可能重用单个 urb 给多个不同的端点, 根据驱动的需要. 设备中的每个端点都处理一个 urb 队列, 以至于多个 urb 可被发送到相同的端点。

        USB控制器和设备之间的传输数据结构由urb表示,urb具体内容如下所示:

struct urb
{
	/* private: usb core and host controller only fields in the urb */
	struct kref kref;		               /* reference count of the URB */
	spinlock_t lock;		               /* lock for the URB */
	void *hcpriv;			               /* private data for host controller */
	atomic_t use_count;		               /* concurrent submissions counter */
	u8 reject;			                   /* submissions will fail */

	/* 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
					                        * current owner */
	struct usb_device *dev; 	           /* (in) pointer to associated device */
	unsigned int pipe;		               /* (in) pipe information */
	int status;			                   /* (return) non-ISO status */
	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 */
	int transfer_buffer_length;	           /* (in) data buffer length */
	int 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 */
};                   
struct usb_device *dev;     
//指向这个urb要发送的struct usb_device 的指针,在urb发送到usb核心之前,这个变量必须被usb驱动初始化。
unsigned int pipe;        
//可以称之为管道,也可以称为端点消息,是给这个urb发送到的特定struct usb_device,同理,在urb发送到usb核心之前,这个变量必须被usb驱动初始化

        为了设置这个pipe,驱动有一些初始化函数,这里要注意,每个pipe只可能是其中一种类型:

		unsigned int usb_sndctrlpipe(struct usb_device *dev, unsigned int endpoint)
		//指定一个控制 OUT 端点给特定的带有特定端点号的 USB 设备.
		
		unsigned int usb_rcvctrlpipe(struct usb_device *dev, unsigned int endpoint)
		//指定一个控制 IN 端点给带有特定端点号的特定 USB 设备.
		
		unsigned int usb_sndbulkpipe(struct usb_device *dev, unsigned int endpoint)
		//指定一个块 OUT 端点给带有特定端点号的特定 USB 设备
		
		unsigned int usb_rcvbulkpipe(struct usb_device *dev, unsigned int endpoint)
		//指定一个块 IN 端点给带有特定端点号的特定 USB 设备
		
		unsigned int usb_sndintpipe(struct usb_device *dev, unsigned int endpoint)
		//指定一个中断 OUT 端点给带有特定端点号的特定 USB 设备
		
		unsigned int usb_rcvintpipe(struct usb_device *dev, unsigned int endpoint)
		//指定一个中断 IN 端点给带有特定端点号的特定 USB 设备
		
		unsigned int usb_sndisocpipe(struct usb_device *dev, unsigned int endpoint)
		//指定一个同步 OUT 端点给带有特定端点号的特定 USB 设备
		
		unsigned int usb_rcvisocpipe(struct usb_device *dev, unsigned int endpoint)
		//指定一个同步 IN 端点给带有特定端点号的特定 USB 设备
unsigned int transfer_flags;    
//这个变量可被设置为不同的位值,根据这个位,usb驱动可以设置urb传输的状态,具体可用值详见ldd3.
void *transfer_buffer;        
//指向缓冲区的指针,这个指针可以是OUT urb 或者是In urb。主机控制器为了正确使用这个缓冲区,必须使用kmalloc调用来创建它,而不是堆栈或者静态数据区。对于控制端点,这个缓冲是给发送的数据。
dma_addr_t transfer_dma;    
//用来使用DMA传送数据到usb设备的缓冲。
u32 transfer_buffer_length;    
//缓冲区的长度,由于urb只会使用buffer或者dma其中一种来传输数据,所以这个长度可以被它们共用。如果这是0,表示没有传送缓冲被usb核心所使用。
unsigned char *setup_packet;    
//指向一个控制urb的setup报文的指针,在位于正常传送缓冲的数据之前被传送,这个变量只对控制urb有效。
dma_addr_t setup_dma;        
//给控制 urb 的 setupt 报文的 DMA 缓冲. 在位于正常传送缓冲的数据之前被传送. 这个变量只对控制 urb 有效

.

u32 actual_length;        

//当urb完成处理后,这个变量被设置为数据的真实长度,或者由这个urb(OUT)发送,或者由这个urb(IN)接受。对于IN urb,这个值必须被用来替代taansfer_buffer_length,因为接收的数据可能比整个缓冲区的大小小。
int status;           
//当这个 urb 被结束, 或者开始由 USB 核心处理, 这个变量被设置为 urb 的当前状态. 一个 USB 驱动可安全存取这个变量的唯一时间是在 urb 完成处理者函数中(在"CompletingUrbs: 完成回调处理者"一节中描述). 这个限制是阻止竞争情况, 发生在这个 urb 被 USB 核心处理当中. 对于同步 urb, 在这个变量中的一个成功的值(0)只指示是否这个 urb 已被去链. 为获得在同步 urb 上的详细状态, 应当检查 iso_frame_desc 变量.

这个变量的有效值包括:

				 0            //这个 urb 传送是成功的.
				-ENOENT      //这个 urb 被对 usb_kill_urb 的调用停止.
				-ECONNRESET  //urb 被对 usb_unlink_urb 的调用去链, 并且 transfer_flags 变量被设置为 URB_ASYNC_UNLINK.
				-EINPROGRESS
				-EPROTO
				-EILSEQ     //在这个 urb 传送中有一个 CRC 不匹配.
				-EPIPE
				-ECOMM      //在传送中数据接收快于能被写入系统内存. 这个错误值只对 IN urb.
				-ENOSR
				-EOVERFLOW
				-EREMOTEIO
				-ENODEV     //这个 USB 设备现在从系统中消失.
				-EXDEV
				-EINVAL
				-ESHUTDOWN
33     usb_complete_t complete;    
//指向完成处理者函数的指针, 它被 USB 核心调用当这个 urb 被完全传送或者当 urb 发生一个错误. 在这个函数中, USB 驱动可检查这个 urb, 释放它, 或者重新提交它给另一次传送.

32     void *context;            
//指向数据点的指针, 它可被 USB 驱动设置. 它可在完成处理者中使用当 urb 被返回到驱动. 可能是在回调函数使用的数据。

27     int start_frame;        

//设置或者返回同步传送要使用的初始帧号。

29     int interval;            
//只对同步 urb 有效, 并且指定这个 urb 要处理的同步传送缓冲的编号. 在这个 urb 发送给 USB 核心之前,这个值必须被 USB 驱动设置给同步 urb .

31     int error_count;        
//被 USB 核心设置, 只给同步 urb 在它们完成之后. 它指定报告任何类型错误的同步传送的号码.

34     struct usb_iso_packet_descriptor iso_frame_desc[0];      
//只对同步 urb 有效. 这个变量是组成这个 urb 的一个 struct usb_iso_packet_descriptor 结构数组. 这个结构允许单个 urb 来一次定义多个同步传送. 它也用来收集每个单独传送的传送状态

2. urb接口函数

2.1 申请urb
usb_alloc_urb(int iso_packets, gfp_t mem_flags);
  1. iso_packets:等时传输的包数 mem_flags:
  2. 开辟urb空间的mem大小,一般为GFP_KERNEL
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;
}
2.2 填充urb
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_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_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)

        对于usb的四种不同传输方式(控制传输(control),中断传输(interrupt),批量传输(bulk),等时传输),usb驱动中已经实现control,bulk,interrupt三种传输类型传输填充函数。
        其中urb为要初始化的控制类型urb,dev为USB设备,pipe为管道,setup_packet专门用于发送control的usb_contrlrequest的setup包,transfer_buffer发送缓冲区, buffer_length传输长度, complete_fn为urb递交给hcd后将会运行的回调函数,comtext为回调函数参数, 对于interrupt而言,还需设置定时查询时间间隔interval。

2.3 提交urb

       经过前面的创建、初始化和填充数据后,urb需要提交给usb core,让它移交给主机控制器驱动进行处理,然后等待hcd的反馈结果,用于提交urb函数接口为:

usb_submit_urb(struct urb * urb, gfp_t mem_flags)

        其中urb即为提交给hcd的urb,mem_flags为申请urb中的hcprv,endpoint descripotr和 transfer descriptor时要用到的申请内存标志。
        将urb提交给控制器后,由控制器进行处理,并通过回调函数返回urb发送结果

2.4 取消urb

       如果想取消之前提交的urb,可以用usb_unlink_urb来实现:

usb_kill_urb(struct urb * urb)
2.5 销毁urb
usb_free_urb(struct urb * urb)
2.6 urb其他接口

       用前面的方式提交urb或取消urb时,程序不会阻塞,属于异步方式。除了异步方式外,usb还可用同步方式来提交和取消urb。同样由于isochronous(实时传输)中发送数据包个数不确定性,驱动只实现了control,interrupt和bulk三种方式 的同步方式操作urb接口。

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_control_msg用于发送control类数据,对于control类型,除发送正常数据外,还要发送一个setup transaction, request, requesttype指定请求包的类型和属性,data为要发送的数据,size为发送数据长度,timeout为发送超时时间。

usb_interrupt_msg(struct usb_device * usb_dev, unsigned int pipe, 
				  void * data, int len, int * actual_length, int timeout)
usb_bulk_msg(struct usb_device * usb_dev, unsigned int pipe,
			 void * data, int len, int * actual_length, int timeout)

       上面三个接口函数都已经将之前提到过的申请urb,填充urb和提交urb过程封装在一起,在使用时只要指定对应该数据,数据长度及超时时间就可以。在使用上面三个接口提交urb时,程序阻塞,直到超时或urb提交成功并通过回调函数返回结果并唤醒等待队列。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值