基于ok6410的韦东山驱动视频简要分析--USB驱动
2024-11-06 来源:cnblogs
注意:本篇讲的鼠标驱动仅能实现鼠标左右键跟滑轮这三个按键类似button的功能,按下左键则打出'l',右键打出“s”,滑轮打出“enter”。如果要实现正常的鼠标驱动,参考内核的鼠标驱动,修改input的一些参数即可。
一、写驱动的步骤(新手稍微看下即可,内容有点搞)
1、复制头文件;
2、写入口函数,出口函数,再加上协议;
3、分配注册usb_driver结构体(拷别人的),
static struct usb_driver usb_mk_driver = {};
在init中注册该结构体:usb_register(&usb_mk_driver);
在exit中注销该结构体:usb_deregister(&usb_mk_driver);
4、写id_table函数,static struct usb_device_id usb_mk_id_table [] = {},只有满足id_table中的各种类别,子类号,协议,才能调用;
5、写probe函数:static int usb_mk_probe(struct usb_interface *intf, const struct usb_device_id *id);
6、写disconnect函数:static void usb_mk_disconnect(struct usb_interface *intf)
7、如果想在开发板插入usb设备的时候打印设备信息(可以不用),可以在probe中加入:
printk('VID = 0x%x, PID = 0x%xn', dev->descriptor.idVendor, dev->descriptor.idProduct);
printk('USB VERS = 0x%x, PID = 0x%xn', dev->descriptor.bcdUSB);
8、probe之后要进一步判断你这个是不是鼠标:
1、分配一个input_dev结构体:在头文件下定义;static struct input_dev *mk_dev; 在probe内:mk_dev = input_allocate_device();
2、设置:
1、能产生哪类事件
set_bit(EV_KEY, mk_dev->evbit);
set_bit(EV_REP, mk_dev->evbit);
2、能产生哪些事件
set_bit(KEY_L, mk_dev->keybit);
set_bit(KEY_S, mk_dev->keybit);
set_bit(KEY_ENTER, mk_dev->keybit);
3、注册:input_register_device(mk_dev);
4、设置:数据传输3要素: 源, 目的, 长度
先从usbmouse.c中拷入几行代码:
struct usb_device *dev = interface_to_usbdev(intf);
struct usb_host_interface *interface;
struct usb_endpoint_descriptor *endpoint;
int pipe;
endpoint = &interface->endpoint[0].desc;
源:usb设备的某个端点
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
目的:需要设置缓冲区buffer:
buf = usb_alloc_coherent(dev, len, GFP_ATOMIC, &buf_phys);
在头文件下定义一个缓冲区:
static char *buf;
static dma_addr_t buf_phys;
长度:static int len(作为全局变量,等会有用)
len = endpoint->wMaxPacketSize;
5、怎么用那三要素呢?
在probe中;
1、分配一个urb,mk_urb = usb_alloc_urb(0, GFP_KERNEL);
在头文件下定义urb:static struct urb *mk_urb;
2、使用三要素填充urb:
usb_fill_int_urb(mk_urb, dev, pipe, buf,
len,
uk_callback, NULL, endpoint->bInterval);
mk_urb->transfer_dma = buf_phys;
mk_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
3、使用URB:usb_submit_urb(mk_urb, GFP_KERNEL);
6、要写一个中断函数,原因是usb控制器收到从usb传来的数据后,需要一个中断来通知cpu有信号啦!static void uk_callback(struct urb *urb)
在中断函数中定义一些唧唧歪歪的东西后看,重新提交urb:
usb_submit_urb(mk_urb, GFP_KERNEL);
7,提交urb后当然还要做杀掉urb,在disconnect中杀掉它!usb_kill_urb(mk_urb);
顺便再disconnect中注销或卸载其他的函数;
8、在中断函数里上报事件。
**************************************************************************************************************
二、驱动程序
#include #include #include #include #include #include #include /* 参考drivershidusbhidusbmouse.c */ static struct input_dev *mk_dev; static int len; static char *buf; //定义一个缓冲区 static dma_addr_t buf_phys; //dma_addr实际上是一个物理地址 static struct urb *mk_urb; static struct usb_device_id usb_mk_id_table [] = { { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT, USB_INTERFACE_PROTOCOL_MOUSE) }, /*只要它的类是class_hid,子类是boot,协议是mouse,那么就可以匹配*/ *{USB_DEVICE(0x46d, 0xc52f)或者直接指定自己的usb设备只支持这种厂家的设备。*/ { } /* Terminating entry */ }; /* 当USB主机控制器获得鼠标数据后, * 会调用这个函数 */ static void uk_callback(struct urb *urb)//这是一个中断函数! { int i; static char pre_val; #if 0 //这一段都用不到了,因为if = 0 ;这一段的作用就是用来看鼠标按下之后都显示什么数值,最后通过这些数值来设置上报事件。 printk('Get datas:n'); for (i = 0; i < len; i++) { printk('%02x ', buf[i]); } printk('n'); #endif /* 鼠标数据含义: * buf[0]: bit0-左键, 0-松开, 1-按下 * bit1-右键, 0-松开, 1-按下 * bit2-中键, 0-松开, 1-按下 * buf[1],buf[2]构成一个整数, 表示X方向的相对位移 * >0 : 右移 * <0 : 左移 * buf[3],buf[4]构成一个整数, 表示Y方向的相对位移 * >0 : 下移 * <0 : 上移 * buf[6]: 滚轮 */ /* 确定按键值 */ /* 上报数据 */ if ((pre_val & (1<<0)) != (buf[0] & (1<<0))) //如果上次数据的bit0不等于这次数据的bit0,那么就是左键发生变化 { /* 左键按下或松开 */ input_event(mk_dev, EV_KEY, KEY_L, (buf[0] & (1<<0)) ? 1 : 0);//如果buf0=1的话那就是按下,既是1,否则为0; input_sync(mk_dev); } if ((pre_val & (1<<1)) != (buf[0] & (1<<1))) { /* 右键按下或松开 */ input_event(mk_dev, EV_KEY, KEY_S, (buf[0] & (1<<1)) ? 1 : 0); input_sync(mk_dev); } if ((pre_val & (1<<2)) != (buf[0] & (1<<2))) { /* 中键按下或松开 */ input_event(mk_dev, EV_KEY, KEY_ENTER, (buf[0] & (1<<2)) ? 1 : 0); input_sync(mk_dev); } pre_val = buf[0]; /* 重新提交URB */ usb_submit_urb(mk_urb, GFP_KERNEL); } /*interface是指接口,一个usb设备可能有多个逻辑接口,这个逻辑接口就是用下面的usb_interface来表示的*/ static int usb_mk_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct usb_device *dev = interface_to_usbdev(intf); struct usb_host_interface *interface; struct usb_endpoint_descriptor *endpoint; int pipe; static int first = 1; if (!first) return -EIO; first = 0; /* 每一个设备都有端点0 * interface->endpoint[]数组里放'除了端点0外的其他端点' * interface->endpoint[0]表示'除端点0外的第1个端点' * interface->endpoint[1]表示'除端点0外的第2个端点' */ interface = intf->cur_altsetting; endpoint = &interface->endpoint[0].desc; /* 1. 分配inputd_dev */ mk_dev = input_allocate_device(); /* 2. 设置 */ /* 2.1 能产生哪类事件 */ set_bit(EV_KEY, mk_dev->evbit); //按键类事件 set_bit(EV_REP, mk_dev->evbit); //重复类事件,例如一直按着L,则会显示LLLLLLL。。。 /* 2.2 能产生这类事件里的哪些事件 */ set_bit(KEY_L, mk_dev->keybit); set_bit(KEY_S, mk_dev->keybit); set_bit(KEY_ENTER, mk_dev->keybit); /* 3. 注册 */ input_register_device(mk_dev); /* 4. 硬件相关的操作: * 对于GPIO按键, 是request_irq, 在中断处理函数里上报按键 * 对于USB设备, 是使用'USB主机驱动程序提供的函数'发起USB传输获得数据 */ /* 数据传输3要素: 源, 目的, 长度 */ /* A. 源: USB设备的某个端点 */ /* ((PIPE_INTERRUPT << 30) | (dev->devnum << 8) | (endpoint << 15) | USB_DIR_IN) */ pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);//pipe是源,源是一个整数,这个整数里有端点的类型,端点的方向 /* C. 长度: 这个端点描述符的wMaxPacketSize */ len = endpoint->wMaxPacketSize; /* B. 目的: 分配buffer */ buf = usb_alloc_coherent(dev, len, GFP_ATOMIC, &buf_phys);//从usb_buffer_alloc换成usb_alloc_coherent /* D. 怎么使用这3要素 ? */ /* 分配URB: USB Reqeust Block ,usb请求块*/ mk_urb = usb_alloc_urb(0, GFP_KERNEL); /* 用3要素填充URB *实际上usb设备没有中断cpu的能力,但是电脑的usb主机有中断cpu的能力,所以usb主机不断查询,有信号便中断cpu。 */ usb_fill_int_urb(mk_urb, dev, pipe, buf, len, uk_callback, NULL, endpoint->bInterval);//bInterval:查询频率,uk_callback是一个中断函数; mk_urb->transfer_dma = buf_phys; //得到数据后要往某个内存里写,它没那么聪明,需要一个物理地址 mk_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;//还有设置某些标记,这标记什么意思伟哥也不懂 /* 使用URB ,看看别人怎么做*/ usb_submit_urb(mk_urb, GFP_KERNEL);//提交urb return 0; } static void usb_mk_disconnect(struct usb_interface *intf) { struct usb_device *dev = interface_to_usbdev(intf); printk('disconnect usb mouse!!!!!n'); usb_kill_urb(mk_urb); usb_free_urb(mk_urb); usb_free_coherent(dev,len, buf, buf_phys);//应该是usb_free_coherent input_unregister_device(mk_dev); input_free_device(mk_dev); } /* 1. 分配usb_driver */ /* 2. 设置 */ static struct usb_driver usb_mk_driver = { .name = 'usbmk', .probe = usb_mk_probe, .disconnect = usb_mk_disconnect, .id_table = usb_mk_id_table, }; static int usb_mk_init(void) { /* 3. 注册 */ usb_register(&usb_mk_driver); return 0; } static void usb_mk_exit(void) { usb_deregister(&usb_mk_driver); } module_init(usb_mk_init); module_exit(usb_mk_exit); MODULE_LICENSE('GPL');