上一篇博文只是usb总线驱动程序的框架,下面来真正写一个usb驱动程序。
USB鼠标驱动,鼠标输入HID类型,其数据传输采用中断URB,鼠标端点类型为IN
目的:usb鼠标按键的驱动代码编写:
框架:
- 分配一个input_dev结构体
- 设置
- 注册
- 硬件相关的操作
思路:
1、分配/设置usb_driver结构体
static struct usb_driver usb_mouse_driver = {
.name = "usbmouse",
.probe = usb_mouse_probe,
.disconnect = usb_mouse_disconnect,
.id_table = usb_mouse_id_table,
};
a.这个结构体中有两个函数:1、dev和drv匹配之后会调用probe函数,2、usb鼠标拔出后会调用disconnect函数3、id_table 支持项
支持项中有三个匹配项:接口类,接口子类,接口协议
usb鼠标使用的支持项为:
static struct usb_device_id usb_mouse_id_table [] = {
{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
USB_INTERFACE_PROTOCOL_MOUSE) },
{ } /* Terminating entry */
};
b.大部分工作都在probe函数中完成,先写出usb_mouse_probe函数原型,逐渐完善它,我们truct usb_interface *intf, const struct usb_device_id *id)
static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
return 0;
}
以上就是基本的框架,只要usb鼠标插上之后,就会调用 probe函数
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
下面完成具体的操作:
c、还是用输入子系统的套路:
- 分配
- 设置
- 注册
- 硬件的相关操作
c.1分配
static struct input_dev *um_dev;
um_dev = input_allocate_device();
这样就分配了一个input_dev结构体。
c.2设置
需要设置两方面的内容:1能产生哪些类事件,2能产生这个这些类里面的具体哪个事件。
/*b.1能产生哪类事件*/
set_bit(EV_KEY,um_dev->evbit);
set_bit(EV_REP,um_dev->evbit);
/*b.2能产生这类里面的哪些事件*/
set_bit(KEY_L,um_dev->keybit);
set_bit(KEY_S,um_dev->keybit);
set_bit(KEY_ENTER,um_dev->keybit);
EV_KEY:按键事件。EV_REP:重复事件。
c.3注册
只需要注册 input_dev 结构体:
input_register_device(um_dev);
c.4硬件的相关操作
这个部分主要是设置数据的传输。数据传输围绕三个要素:源、目的、长度
源:
pipe = usb_rcvintpipe(dev,endpoint->bEndpointAddress);
根据中断端点获取源。
长度:
length = endpoint->wMaxPacketSize;
目的:
usb_buf = usb_buffer_alloc(dev,length,GFP_ATOMIC,&usb_buf_phys);
usb数据会存储在 usb_buf中。
d。使用这三个要素,完成最终的驱动程序-----》使用urb
urb处理流程:
- USB设备驱动程序创建并初始化一个访问特定USB设备特定端点的urb,并提交给USB core
- USB core提交该urb到到USB主控制器驱动程序。
- USB主控制器驱动程序根据该urb描述的信息,来访问usb设备
- 当设备访问结束后,USB的主控制器驱动程序通知USB设备驱动程序。
um_urb = usb_alloc_urb(0,GFP_KERNEL);
/*使用三要素设置 urb*/
usb_fill_int_urb(um_urb, dev, pipe,usb_buf,length,
usb_mouse_irq, NULL, endpoint->bInterval);
um_urb->transfer_dma = usb_buf_phys;
um_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
/*使用URB( 提交 urb)*/
usb_submit_urb(um_urb,GFP_KERNEL);
说明1:
usb_alloc_urb(int iso_packets, gfp_t mem_flags);
iso_packets :是这个urb应当包含的等时数据包数目,若为0:表示不创建等时数据包。
mem_flags :是分配内存的标志。
说明2:
对于中断urb,使用usb_fill_int_urb来初始化urb。函数定义如下:
static inline void usb_fill_int_urb(struct urb *urb,
struct usb_device *dev, //指向这个urb要被发送的usb设备
unsigned int pipe, //源
void *transfer_buffer, //目的
int buffer_length, //长度
usb_complete_t complete_fn, //这个urb完成时,被调用的完成处理函数
void *context, //完成处理函数的“上下文”
int interval) //urb应当被调用的间隔
那么在完成处理函数中就可以完成我们想做的东西:
例如,打印 usb鼠标传回的数据
static void usb_mouse_irq(struct urb *urb)
{
static unsigned char pre_val;
int i;
static int cnt =0;
printk("data cnt %d ",++cnt);
for(i=0;i<length;i++)
{
printk("%02x ",usb_buf[i]);
}
printk("\n");
}
这样,一个完整的驱动程序基本就写好了。完整的驱动代码,我会在后面使用csdncode 地址的形式给出
测试:
1、make menuconfig 去掉原来的usb鼠标驱动
-> Device Drivers
-> HID Devices (HID_SUPPORT [=y])
2、make
3、使用新内核
4、加载驱动
5、可以使用 hexdump /dev/event1 查看 插入USB鼠标新生成的event
6、或者使用 cat /dev/tty1
完整驱动程序地址:csdncode 源码