概述
1、分配、设置注册usb_driver结构体。(usb_device结构体在USB总线会帮我们完成,具体见前面)
设置usb_driver主要有哪些要设置:
id_table--------USB总线匹配驱动和设备时候就依靠获取的设备接口描述符和驱动的id_table进行匹配来判断是否支持;
probe----------USB总线匹配成功会通过usb_probe_interface(driver的probe函数)来调用USB驱动的probe函数;
disconnect----这个函数前面没有提到,这个当USB设备拔出来时候,USB总线会调用。
对于分配设置usb_driver也很简单,分配就直接分配内存,注册就是通过usb_register函数。
所以对于USB框架驱动是十分简单的,核心的事情有两件事:
1、USB总线驱动在USB设备接入HUB时候会通信,获取USB设备的各种描述符,那么驱动第一件事就是通过接口直接把主机控制器之前读到的描述符给
读取出来,然后进行解析。这个解析就是USB驱动的最核心的工作:
2、就是根据获取的描述符来设置和操作USB设备,这部分核心工作就是通过urb来完成。
3、首先讨论id_table:
下面参考文章:http://linux.chinaunix.net/techdoc/install/2009/08/05/1128379.shtml
struct usb_device_id {
/* 确定设备信息去和结构体中哪几个字段匹配来判断驱动的适用性 */
__u16 match_flags;
/* Used for product specific matches; range is inclusive */
__u16 idVendor; //USB设备的制造商ID,
__u16 idProduct; //USB设备的产品ID,有制造商自定
__u16 bcdDevice_lo; /* USB设备的产品版本号最低值*/
__u16 bcdDevice_hi; /* 和最高值,以BCD码来表示。*/
/* 分别定义设备的类,子类和协议,他们由 USB 论坛分配并定义在 USB 规范中. 这些值指定这个设备的行为, 包括设备上所有的接口 */
__u8 bDeviceClass;
__u8 bDeviceSubClass;
__u8 bDeviceProtocol;
/* 分别定义单个接口的类,子类和协议,他们由 USB 论坛分配并定义在 USB 规范中 */
__u8 bInterfaceClass;
__u8 bInterfaceSubClass;
__u8 bInterfaceProtocol;
/* 这个值不用来匹配驱动的, 驱动用它来在 USB 驱动的探测回调函数中区分不同的设备 */
kernel_ulong_t driver_info;
};
结构体的成员一般不直接访问,而是通过通过一系列宏:
#define USB_DEVICE(vend,prod) //仅和指定的制造商和产品ID匹配,用于需要特定驱动的设备
#define USB_DEVICE_VER(vend, prod, lo, hi)//仅和某版本范围内的指定的制造商和产品ID匹配
#define USB_DEVICE_INTERFACE_PROTOCOL(vend, prod, pr) //仅和指定的接口协议、制造商和产品ID匹配
#define USB_DEVICE_INFO(cl, sc, pr)//仅和指定的设备类型相匹配
#define USB_INTERFACE_INFO(cl, sc, pr)//仅和指定的接口类型相匹配
#define USB_DEVICE_AND_INTERFACE_INFO(vend, prod, cl, sc, pr)//仅和指定的制造商、产品ID和接口类型相匹配
等等
例如:
static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x1CBE, 0x0103) },
{ }, //表示结束
};
4、讨论usb_driver的peobe函数:
重复一下:USB主机控制器在USB设备接入HUB的时候就会和USB设备通信,然后获取他的各种描述符,这些描述符都是在内存,并且是USB_DEVICE结构体中存放。所以我们在probe函数就可以把描述符给读取出来:
struct usb_device *dev = interface_to_usbdev(intf); //夺取usb_deivce结构体,这个结构体就有各种描述符。具体如下:
看看usb_device结构体:
struct usb_device {
struct usb_device_descriptor descriptor; //设备描述符
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
}
5、USB数据传输,基于urb结构体。
usb基于端点来传输的,我们一般用管道来封装端点的详细信息,如下:
管道的创建:
#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)
其中
static inline unsigned int __create_pipe(struct usb_device *dev,
unsigned int endpoint)
{
return (dev->devnum << 8) | (endpoint << 15);
}
总结:管道实际就是端点的详细信息的封装:包括端点类型是控制还是批量还是终端又还是同步类型,端点所在设备的地址和端点的地址(这样主机才找的到端点),端点的方向(输入还是输出);
再者就是USB数据传输,需要内存来存放数据,使用函数usb_alloc_coherent(struct usb_device *dev, size_t size, gfp_t mem_flags,dma_addr_t *dma)函数,其中返回值为虚拟地址,参数dma得到分配的物理地址。
相反函数:
usb_free_coherent(struct usb_device *dev, size_t size, void *addr, dma_addr_t dma)
上面都是urb操作的前提准备,现在可以开始用urb来操作数据了:
第一:分配urb:
usb_alloc_urb(int iso_packets, gfp_t mem_flags) //相反函数usb_free_urb(struct urb *urb)
第二:设置urb:
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外,其他参数都是用来设置urb。pipe就是操作端点,transfer_buffer就是刚刚分配用于存放传输数据内存存放区,buffer_length传输长度,complete_fn完成调用函数,interval用于中断传输,就是轮询的时间间隔(所谓中断实际是轮询,而非我们平常所得中断)。
还要设置:
urb->transfer_dma =dma; //刚才的分配内存的物理地址
urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
第三:提交urb:
usb_submit_urb(struct urb *urb, gfp_t mem_flags) //这样USB总线驱动就会帮我们处理urb了。
相反函数:
usb_kill_urb(struct urb *urb)
第四:传输完成,除了处理得到的数据外,核心事件就是要重新处理上交urb!!!
处理完之后,那么刚才分配的内存就有传输完之后所需要的数据了。我们刚才有说过usb_fill_int_urb有个传输完成处理函数,我们现在就通过这个函数来处理我们操作之后的到数据(如果发送,那么就不用),在这个函数里,有一件事情必须做就是重新处理和上交urb
最后一点,USB总线只是负责数据传输,具体数据什么意思,需要我们设备驱动来完成!!
控制传输函数解析: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设备请求,是控制传输类型。所以需要我们构造USB设备请求(USB协议):
格式:bmRequestType+bRequest+wValue+wIndex+wLength+数据存放区。
看看如何发送请求:
构造请求
struct usb_ctrlrequest *dr;
dr->bRequestType = requesttype;
dr->bRequest = request;
dr->wValue = cpu_to_le16(value);
dr->wIndex = cpu_to_le16(index);
dr->wLength = cpu_to_le16(size);
发送请求
usb_internal_control_msg(dev, pipe, dr, data, size, timeout);
usb_alloc_urb(0, GFP_NOIO);
//比usb_fill_urb函数多个一个参数setup_packet(cmd),用于存放构造好的请求
usb_fill_control_urb(urb, usb_dev, pipe, (unsigned char *)cmd, data, len, usb_api_blocking_completion, NULL);
usb_start_wait_urb(urb, timeout, &length);
retval = usb_submit_urb(urb, GFP_NOIO); //总线发送
wait_for_completion_timeout(&ctx.done, expire) //等待完成(使用定时器),如果超时间没有完成就usb_kill_urb(urb);
所以对于这个函数而言,这些参数设置要根据具体USB设备的协议手册里的请求来决定。
最后
以上就是害怕画板为你收集整理的linux 如何重启usb驱动程序,如何在linux写USB驱动的全部内容,希望文章能够帮你解决linux 如何重启usb驱动程序,如何在linux写USB驱动所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复