赞
踩
本文基于S3C2440开发板。
USB 总线驱动程序,在接入 USB 设备时,会帮我们构造一个新的
usb_dev
注册到“usb_bus_type
”里去。这部分是内核做好的。我们要做的是,构造一个usb_driver
结
构体,注册到“usb_bus_type
”中去。在“usb_driver
”结构体中有“id_table”表示他能支持哪些设备,当 USB 设备能匹配 id_table 中某一个设备时,就会调用“usb_driver
”结构体中的“.probe
”(自已确定在 probe 中做的事情)等函数,如当 拔掉USB 设备时,就会调用其中的“.disconnect
”函数。 “usb_bus_type
”USB 总线驱动设备模型只是提供了这一种框架而已。在".probe
"函数里面,注册“字符设备”也好,注册一个“input_dev
”结构体也好,再或注册一个块设备也好。再或只是加了句打印也好,都可以由自已确定。
/* * drivers\hid\usbhid\usbmouse.c */ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/init.h> #include <linux/usb/input.h> #include <linux/hid.h> static struct input_dev *uk_dev; static char *usb_buf; //如果有数据产生,usb驱动总线会把数据放到这个字符指针里面, static dma_addr_t usb_buf_phys; //物理地址 static int len; static struct urb *uk_urb; //usb的三要素使用包结构体 /* 下面这个结构体主要是用来与usb总线驱动中的device进行比较,相同就调用.probe*/ static struct usb_device_id usbmouse_as_key_id_table [] = { { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT, USB_INTERFACE_PROTOCOL_MOUSE) }, //{USB_DEVICE(0x1234,0x5678)}, { } /* Terminating entry */ }; static void usbmouse_as_key_irq(struct urb *urb) //当usb设备有数据产生时,就会调用这个中断,在这里判断是什么事件并进行事件的上报 { static unsigned char pre_val; #if 0 int i; static int cnt = 0; printk("data cnt %d: ", ++cnt); for (i = 0; i < len; i++) { printk("%02x ", usb_buf[i]); } printk("\n"); #endif /* USB鼠标数据含义 * data[0]: bit0-左键, 1-按下, 0-松开 * bit1-右键, 1-按下, 0-松开 * bit2-中键, 1-按下, 0-松开 * */ if ((pre_val & (1<<0)) != (usb_buf[1] & (1<<0))) { /* 左键发生了变化 */ input_event(uk_dev, EV_KEY, KEY_L, (usb_buf[1] & (1<<0)) ? 1 : 0); input_sync(uk_dev); } if ((pre_val & (1<<1)) != (usb_buf[1] & (1<<1))) { /* 右键发生了变化 */ input_event(uk_dev, EV_KEY, KEY_S, (usb_buf[1] & (1<<1)) ? 1 : 0); input_sync(uk_dev); } if ((pre_val & (1<<2)) != (usb_buf[1] & (1<<2))) { /* 中键发生了变化 */ input_event(uk_dev, EV_KEY, KEY_ENTER, (usb_buf[1] & (1<<2)) ? 1 : 0); input_sync(uk_dev); } pre_val = usb_buf[1]; /* 重新提交urb */ usb_submit_urb(uk_urb, GFP_KERNEL); } static int usbmouse_as_key_probe(struct usb_interface *intf, const struct usb_device_id *id) //如果device和driver匹配成功就调用这个函数 { struct usb_device *dev = interface_to_usbdev(intf); //把接口描述符转换为设备描述符 struct usb_host_interface *interface; //声明一个接口描述符结构体 struct usb_endpoint_descriptor *endpoint; //声明一个端点描述符结构体 int pipe; interface = intf->cur_altsetting; endpoint = &interface->endpoint[0].desc; /* a. 分配一个input_dev */ uk_dev = input_allocate_device(); /* b. 设置 */ /* b.1 能产生哪类事件 */ set_bit(EV_KEY, uk_dev->evbit); set_bit(EV_REP, uk_dev->evbit); /* b.2 能产生哪些事件 */ set_bit(KEY_L, uk_dev->keybit); set_bit(KEY_S, uk_dev->keybit); set_bit(KEY_ENTER, uk_dev->keybit); /* c. 注册 */ input_register_device(uk_dev); /* d. 硬件相关操作 */ /* 数据传输3要素: 源,目的,长度 */ /* 源: USB设备的某个端点 */ pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); /* 长度: */ len = endpoint->wMaxPacketSize; /* 目的: */ usb_buf = usb_buffer_alloc(dev, len, GFP_ATOMIC, &usb_buf_phys); /* 使用"3要素" */ /* 分配usb request block */ uk_urb = usb_alloc_urb(0, GFP_KERNEL); /* 使用"3要素设置urb" */ usb_fill_int_urb(uk_urb, dev, pipe, usb_buf, len, usbmouse_as_key_irq, NULL, endpoint->bInterval); uk_urb->transfer_dma = usb_buf_phys; uk_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; /* 使用URB */ usb_submit_urb(uk_urb, GFP_KERNEL); return 0; } static void usbmouse_as_key_disconnect(struct usb_interface *intf) //拔出就调用这个函数 { struct usb_device *dev = interface_to_usbdev(intf); //printk("disconnect usbmouse!\n"); usb_kill_urb(uk_urb); usb_free_urb(uk_urb); usb_buffer_free(dev, len, usb_buf, usb_buf_phys); input_unregister_device(uk_dev); input_free_device(uk_dev); } /* 1. 分配/设置usb_driver */ static struct usb_driver usbmouse_as_key_driver = { .name = "usbmouse_as_key_", .probe = usbmouse_as_key_probe, .disconnect = usbmouse_as_key_disconnect, .id_table = usbmouse_as_key_id_table, }; static int usbmouse_as_key_init(void) { /* 2. 注册 */ usb_register(&usbmouse_as_key_driver); return 0; } static void usbmouse_as_key_exit(void) { usb_deregister(&usbmouse_as_key_driver); } module_init(usbmouse_as_key_init); module_exit(usbmouse_as_key_exit); MODULE_LICENSE("GPL");
根据“usb_bus_type”总线驱动设备驱动模型,里面有个“
.match
”函数,左边是由“USB 总线驱动程序”帮我们来发现这个新“USB 设备”,会注册"usb_new_device()
“一个"USB 设备”,并且从右边“driver
”链表里找了一个个 USB 驱动程序和左边注册进来的“USB 设备”比较.所谓的比较,是“usb_bus_type
”中的“.match
”函数,把左边“usb_interface”结构中的“接口”与右边“usb_driver”结构中的“id_table”比较。若能吻合,则调用“usb_driver”中的“.probe
”函数。“usb_bus_type
”提供了这套机制。 在“.probe
”函数中,可以只是打印,也可以注册字符设备,或注册“input_dev
”结构。完全由自已确定。以前的驱动程序,数据是从“中断”(按键中断,ADC 中断)里面读寄存器得到。现在“USB 设备驱动程序”中的数据从“USB 总线”来,是 USB 总线驱动程序提供的函数(读写等)发起 USB 传输,从 USB 传输里得到那些数据。(数据传输三要素:源,目的,长度。再构造一个“usb_urb = usb_alloc_urb(0,GFP_KERNEL)
”后,接接着把“源,目的,长度”填充到“usb_urb
”中,使用就是提交"usb_urb",提交usb_sunmit_urb
函数是 USB 总线驱动程序提供的)。当“USB 主机控制器”接收到数据后,“usb_as_key_irq
”函数(complete_fn
完成函数)被调用。在这个函数里根据"USB 设备"数据的含义去上报(input_event()
)。
/ * 左键发生了变化 */
input_event(uk_dev, EV_KEY, KEY_L, (usb_buf[1] & (1<<0)) ? 1 : 0);
input_sync(uk_dev);
0000000 0073 0000 ba91 000a 0001 0026 0001 0000 鼠标左键按下
0000010 0073 0000 ba9c 000a 0000 0000 0000 0000 上报同步数据
0000020 0073 0000 cdc9 000c 0001 0026 0000 0000 鼠标左键松开
0000030 0073 0000 cdd2 000c 0000 0000 0000 0000 上报同步数据
0000000 //序号
0073 0000 //时间
ba91 000a //时间
0001 //按键类型
0026 //按下的值为0x26,代表l
0001 0000 //1 代表按下,0 代表松开
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。