Linux下编写USB驱动实例
Posted DS小龙哥
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux下编写USB驱动实例相关的知识,希望对你有一定的参考价值。
1.1 Linux下USB驱动开发介绍
USB 是连接计算机系统与外部设备的一种串口总线标准,也是一种输入输出接口的技术规范,被广泛地应用于个人电脑和移动设备等信息通讯产品,USB 就是简写,中文叫通用串行总线。我们知道总线是用来通信的,所以USB总线就是一个种通信协议,你的设备支持什么协议就得使用哪一种总线协议与之通信。比如: EEPROM支持IIC协议,那么我们就得使用IIC总线与之通信,而EEPROM设备在制造过程中也必须遵循IIC协议设计。
在学习驱动开发实例之前,先了解几个USB总线通信的问题:
- 问题1: USB设备那么多,他们怎么分类的?USB设备按照传输类型分,主要分为4类: 控制传输,中断传输,等时传输,批量传输。
其中控制传输时每个USB设备都必须支持的,通常用来获取设备描述符,设置设备的状态等。从USB设备插入到拔出的过程中一定为产生控制传输,不管当前设备是否被主机支持。
中断传输的经典代表是USB鼠标和USB键盘,这里说的中断不是真正硬件发出的中断,而是一种轮询机制,
USB设备驱动程序里可以设置轮询时间的间隔,也就是主机可以按照这个间隔时间来轮询设备。
批量传输的经典代表是U盘,数据可靠,时间不可靠。
等时传输的经典代表是摄像头,数据不可靠,时间可靠。
- 问题2: 当USB设备插入系统时(USB主机),系统怎么知道这是什么设备?当USB设备插入系统之后,根据硬件设计的特性,会被USB主机控制器第一时间知道,然后主机控制器就会问当前插入的设备是什么设备。这里就引入了一个概念叫做描述符。
描述符有很多种,最基本的有4种:设备描述符、配置描述符、接口描述符、端点描述符。一个∪SB设备必须同时支持这四大描述符,一般这些描述符都存放在USB设备的EEPROM里。设备描述符包含了设备遵循的 USB 的版本号、设备类、设备子类、制造商、产品编号等信息,主机会通过控制传输的方式获取这个设备描述符,通过这个设备描述符就能知道当前是什么设备了。
1.2 USB鼠标与键盘驱动编写实例
Linux内核默认是支持鼠标驱动的,想要自己重新编写鼠标驱动,需要先将内核自带的鼠标驱动先去除掉。
[root@wbyq linux-3.5]# make menuconfig Device Drivers ---> HID support ---> USB HID support ---> < > USB HID transport layer //传输层 |
Linux内核里自带的鼠标驱动源码: \\drivers\\hid\\usbhid\\usbmouse.c
Linux内核里自带的键盘驱动:源码 \\drivers/usb/input/usbkbd.c
- USB键盘和USB鼠标都属于HID人机交互类,都使用的是中断方式传输数据。代码区别只是匹配的类型不一样而已,其他处理代码通用。
- 下面是鼠标和键盘的模板:
#include <linux/init.h> #include <linux/module.h> #include <linux/usb.h> #include <linux/usb/input.h> #include <linux/hid.h> /* 本程序为USB鼠标驱动程序,要安装本驱动,需要先将内核自带的USB驱动程序卸载掉 */ //定义USB的IDTAB 24ae:2002 static const struct usb_device_id tiny4412_usb_id[] = //148f:7601 USB_DEVICE(0x148f,0x7601),/*360WIFI的制造商ID和产品ID */ USB_DEVICE(0x1c4f,0x0051),/*当前鼠标的ID 1c4f:0051*/ , ; //USB鼠标的ID static struct usb_device_id usb_mouse_id[] = USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT, USB_INTERFACE_PROTOCOL_MOUSE) , /* 终止进入 */ ; //USB键盘的ID static struct usb_device_id usb_kbd_id_table [] = USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT, USB_INTERFACE_PROTOCOL_KEYBOARD) , ; int size; static unsigned char *buf =NULL; static struct urb *myurb=NULL; dma_addr_t buf_phy; /*USB中断处理程序*/ static void usb_complete(struct urb *urb) int i; for(i=0;i<size;i++) printk("0x%x ",buf[i]); printk("\\n"); /* 重新提交异步请求*/ usb_submit_urb(myurb, GFP_KERNEL); //USB设备信息与驱动端匹配成功的时候调用。 static int usb_probe(struct usb_interface *intf,const struct usb_device_id *id) printk("USB驱动匹配成功! ID: 0x%X,0x%X\\n",id->idVendor,id->idProduct); /*通过接口获取设备信息*/ struct usb_device *dev = interface_to_usbdev(intf); /*获取当前接口设置*/ struct usb_host_interface *interface=intf->cur_altsetting; /*获取端点描述符*/ struct usb_endpoint_descriptor *endpoint = &interface->endpoint[0].desc; /*中断传输:创建输入管道*/ int pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); /*从端点描述符中获取传输的数据大小 */ size = endpoint->wMaxPacketSize; printk("设备传输数据包大小:%d\\n",size); /*分配数据传输缓冲区*/ buf = usb_alloc_coherent(dev,size,GFP_ATOMIC,&buf_phy); /*分配新的urb,urb是usb设备驱动中用来描述与usb设备通信所用的基本载体和核心数据结构*/ myurb = usb_alloc_urb(0,GFP_KERNEL); /*中断方式初始化urb*/ usb_fill_int_urb(myurb,dev,pipe,buf,size,usb_complete,NULL,endpoint->bInterval); myurb->transfer_dma = buf_phy; myurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; /*为端点提交异步传输请求*/ usb_submit_urb(myurb, GFP_KERNEL); return 0; //USB断开的时候调用 static void usb_disconnect(struct usb_interface *intf) struct usb_device *dev = interface_to_usbdev(intf); usb_kill_urb(myurb); usb_free_urb(myurb); usb_free_coherent(dev,size,buf, buf_phy); printk("USB 设备释放成功!\\n"); //定义USB驱动结构体 static struct usb_driver tiny4412_usb_driver = .name = "tiny4412_usb", .id_table = usb_kbd_id_table, .probe = usb_probe, .disconnect = usb_disconnect ; static int __init tiny4412_usb_init(void) //注册USB设备驱动 usb_register(&tiny4412_usb_driver); return 0; static void __exit tiny4412_usb_exit(void) 注销USB设备驱动 usb_deregister(&tiny4412_usb_driver); module_init(tiny4412_usb_init); module_exit(tiny4412_usb_exit); MODULE_AUTHOR("xiaolong"); MODULE_LICENSE("GPL"); |
1.3 USB电子扫码枪驱动编写实例
USB电子扫码枪的驱动与USB键盘驱动通用,只是数据包的大小是64字节,匹配的类型也是使用键盘的类型。
说明: USB电子扫码枪和USB键盘输出的数据都是以掩码的值输出。
//USB键盘的ID static struct usb_device_id usb_kbd_id_table [] = USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT, USB_INTERFACE_PROTOCOL_KEYBOARD) , ; |
要测试电子扫码枪的驱动,也需要先将内核自带的USB键盘去掉先去掉在测试。
#include <linux/init.h> #include <linux/module.h> #include <linux/usb.h> #include <linux/usb/input.h> #include <linux/hid.h> /* 本程序为USB鼠标驱动程序,要安装本驱动,需要先将内核自带的USB驱动程序卸载掉 */ //定义USB的IDTAB 24ae:2002 static const struct usb_device_id tiny4412_usb_id[] = //148f:7601 USB_DEVICE(0x148f,0x7601),/*360WIFI的制造商ID和产品ID */ USB_DEVICE(0x1c4f,0x0051),/*鼠标的ID 1c4f:0051*/ USB_DEVICE(0x0483,0x0011),/*电子扫描枪的ID 1c4f:0051*/ , ; //USB鼠标的ID static struct usb_device_id usb_mouse_id[] = USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT, USB_INTERFACE_PROTOCOL_MOUSE) , /* 终止进入 */ ; //USB键盘的ID static struct usb_device_id usb_kbd_id_table [] = USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT, USB_INTERFACE_PROTOCOL_KEYBOARD) , ; int size; static unsigned char *buf =NULL; static struct urb *myurb=NULL; dma_addr_t buf_phy; /*USB中断处理程序*/ static void usb_complete(struct urb *urb) int i; /* for(i=0;i<size;i++) if(buf[i]!=0)printk("%d,%d\\n",buf[i],i); printk("\\n"); */ //每包数据都是存放在buf[2]里,并且以掩码的形式存放,如果需要得到真实的 //按键值,需要根据键盘的规则找到对应的码值 if(buf[2]!=0)printk("0x%x\\n",buf[2]); /* 重新提交异步请求*/ usb_submit_urb(myurb, GFP_KERNEL); //USB设备信息与驱动端匹配成功的时候调用。 static int usb_probe(struct usb_interface *intf,const struct usb_device_id *id) printk("USB驱动匹配成功! ID: 0x%X,0x%X\\n",id->idVendor,id->idProduct); /*通过接口获取设备信息*/ struct usb_device *dev = interface_to_usbdev(intf); /*获取当前接口设置*/ struct usb_host_interface *interface=intf->cur_altsetting; /*获取端点描述符*/ struct usb_endpoint_descriptor *endpoint = &interface->endpoint[0].desc; /*中断传输:创建输入管道*/ int pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); /*从端点描述符中获取传输的数据大小 */ size = endpoint->wMaxPacketSize; printk("设备传输数据包大小:%d\\n",size); /*分配数据传输缓冲区*/ buf = usb_alloc_coherent(dev,size,GFP_ATOMIC,&buf_phy); /*分配新的urb,urb是usb设备驱动中用来描述与usb设备通信所用的基本载体和核心数据结构*/ myurb = usb_alloc_urb(0,GFP_KERNEL); /*中断方式初始化urb*/ usb_fill_int_urb(myurb,dev,pipe,buf,size,usb_complete,NULL,endpoint->bInterval); myurb->transfer_dma = buf_phy; myurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; /*为端点提交异步传输请求*/ usb_submit_urb(myurb, GFP_KERNEL); return 0; //USB断开的时候调用 static void usb_disconnect(struct usb_interface *intf) struct usb_device *dev = interface_to_usbdev(intf); usb_kill_urb(myurb); usb_free_urb(myurb); usb_free_coherent(dev,size,buf, buf_phy); printk("USB 设备释放成功!\\n"); //定义USB驱动结构体 static struct usb_driver tiny4412_usb_driver = .name = "tiny4412_usb", .id_table =usb_kbd_id_table,//, .probe = usb_probe, .disconnect = usb_disconnect ; static int __init tiny4412_usb_init(void) //注册USB设备驱动 usb_register(&tiny4412_usb_driver); return 0; static void __exit tiny4412_usb_exit(void) 注销USB设备驱动 usb_deregister(&tiny4412_usb_driver); module_init(tiny4412_usb_init); module_exit(tiny4412_usb_exit); MODULE_AUTHOR("xiaolong"); MODULE_LICENSE("GPL"); |
1.4 USB摄像头编写实例
要自己编写自己的UVC摄像头驱动,需要先将内核自带的驱动去掉。
Device Drivers ---> [*] Video capture adapters ---> [] V4L USB devices ---> //将*号去掉即可 |
- 示例:
#include <linux/init.h> #include <linux/module.h> #include <linux/usb.h> #include <linux/usb/input.h> Linux驱动开发: USB驱动开发 |