一、usb设备驱动编写
前面分析了USB驱动框架和鼠标键盘驱动源码,那么我们要学以致用。自己模仿写一个USB设备驱动。
第一步先写驱动框架:
#include<linux/kernel>
#include<linux/slab.h>
#include<linux/module.h>
#include<linux/init.h>
#include<linux/usb/input.h>
#include<linux/hid.h>//分配和设置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 __init usbmouse_as_key_init(void)
{
usb_register(&usbmouse_as_key_driver); //注册驱动return 0;
}static void __exit 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");
第二步:编写 id_table
static const 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设备的厂家和产品ID,可以添加{USB_DEVICE(0x1234,0x5678)},
{}
};
注意声明id_table时,用 static const
第三步:编写 probe函数
static int usbmouse_as_key_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct usb_device *dev = interface_to_usbdev(intf); //获得usb设备信息
struct usb_host_interface *interface;
struct usb_endpoint_descriptor *endpoint;
int pipe;
struct input_dev * uk_dev;interface = intf->cur_altsetting;
if(interface->desc.nNumEndpoints != 1) //除了端点0之外,鼠标的端点数如果不是1的话,则返回错误
return -ENODEV;
endpoint = &interface->endpoint[0].desc;//得到第一个非零endpoint的描述符if(!usb_endpoint_is_int_in(endpoint)) //如果不是中断输入型端点的话,返回错误
return -ENODEV;
uk_dev = input_allocate_device(); 分配一个input_devset_bit(EV_KEY,uk_dev->evbit);;//能产生按键类事件
set_bit(EV_REP,uk_dev->evbit);//能产生按键的重复类事件// 能产生哪些事件
set_bit(KEY_L,uk_dev->keybit);
set_bit(KEY_S,uk_dev->keybit);
set_bit(KEY_ENTER,uk_dev->keybit);input_register_device(uk_dev);//注册
/* 数据传输“三要素”: 源,目的,长度 */
pipe = usb_rcvintpipe(dev,endpoint->bEndpointAddress);//源: USB设备的某个端点
len = endpoint->wMaxPacketSize;//长度usb_buf = usb_alloc_coherent(dev, len, GFP_ATOMIC,&usb_buf_phys);//目的
uk_urb = usb_alloc_urb(0,GFP_KERNEL);//分配urb
usb_fill_int_urb(uk_urb, dev, pipe, usb_buf, len, usbmouse_as_key_irq, NULL, endpoint->bInterval);//使用之前的三要素设置urbuk_urb->transfer_dma = usb_buf_phys;
uk_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;usb_submit_urb(uk_urb, GFP_KERNEL);//使用URB
return 0 ;
}
上面有一些变量为声明,需要修正的,只是初版。
第四步:编写urb 完成中断函数
当USB主机周期性(j间隔endpoint->bInterval)的从设备获得数据,并存入到usb buffer中,同时USB主机控制器驱动注册的时钟中断会周期性的检查是否有数据到来,并调用usb中断处理函数:usbmouse_as_key_irq。
这个上面说的中断,之前分析过,是tasklet 的软中断,是被定时timer周期性唤醒的。看不懂这句话,请看前面几章。
在usb设备驱动程序中,需要对数据进行解析。即usb总线获取到数据不会解析数据的含义,需要在usb设备驱动中进行解析数据。
解析usb_buf 中的值
static void usbmouse_as_key_irq(struct urb *urb)
{
#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
#if 1//上报事件(USB总线驱动是不知道usb数据含义的,只有在USB设备驱动中加以解析)
static unsinged char pre_val;
if( (pre_val & (1<<0)) != (usb_buf[0]&(1<<0)) )/如果上次数据的Bit0不等于现在的值
{/* 左键发生了变化 */
input_event(uk_dev, EV_KEY, KEY_L, (usb_buf[0] & (1<<0)?1:0));
input_sync(uk_dev);
}if((pre_val &(1<<1)) != (usb_buf[0] & (1<<1) ))
{/* 右键发生了变化 */
input_event(uk_dev, EV_KEY, KEY_S, (usb_buf[0] & (1<<1)?1:0));
input_sync(uk_dev);}
if(pre_val & (1<<2) != (usb_buf[0]& (1<<2)?1:0))
{/* 中键发生了变化 */
input_event(uk_dev, EV_KEY, KEY_ENTER, (usb_buf[0] & (1<<2)?1:0));
input_sync(uk_dev);}
pre_val = usb_buf[0];
#endif/* 重新提交urb */
usb_submit_urb(uk_urb,GFP_KERNEL);}
第五步:编写 disconnect 函数
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_free_coherent(dev, len, usb_buf, usb_buf_phys);
input_unregister_device(uk_dev);
input_free_device(uk_dev);
}
第一个总的版本:通过上面的5步,基本上完成了USB设备驱动的编写,下面是整个代码:
#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 int len;
static char* usb_buf;
static dma_addr_t usb_buf_phys;
static struct urb *uk_urb;
static unsigned char pre_val;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_free_coherent(dev, len, usb_buf, usb_buf_phys);
input_unregister_device(uk_dev);
input_free_device(uk_dev);
}static void usbmouse_as_key_irq(struct urb *urb)
{
#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
#if 1if( (pre_val & (1<<0)) != (usb_buf[0]&(1<<0)) )
{
input_event(uk_dev, EV_KEY, KEY_L, (usb_buf[0] & (1<<0)?1:0));
input_sync(uk_dev);
}if((pre_val &(1<<1)) != (usb_buf[0] & (1<<1) ))
{
input_event(uk_dev, EV_KEY, KEY_S, (usb_buf[0] & (1<<1)?1:0));
input_sync(uk_dev);}
if((pre_val & (1<<2)) != (usb_buf[0]& (1<<2)?1:0))
{
input_event(uk_dev, EV_KEY, KEY_ENTER, (usb_buf[0] & (1<<2)?1:0));
input_sync(uk_dev);}
pre_val = usb_buf[0];
#endif
usb_submit_urb(uk_urb,GFP_KERNEL);}
static int usbmouse_as_key_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;
int erro;
interface = intf->cur_altsetting;
if(interface->desc.bNumEndpoints != 1)
return -ENODEV;
endpoint = &interface->endpoint[0].desc;if(!usb_endpoint_is_int_in(endpoint))
return -ENODEV;
uk_dev = input_allocate_device();set_bit(EV_KEY,uk_dev->evbit);
set_bit(EV_REP,uk_dev->evbit);set_bit(KEY_L,uk_dev->keybit);
set_bit(KEY_S,uk_dev->keybit);
set_bit(KEY_ENTER,uk_dev->keybit);erro = input_register_device(uk_dev);
pipe = usb_rcvintpipe(dev,endpoint->bEndpointAddress);
len = endpoint->wMaxPacketSize;usb_buf = usb_alloc_coherent(dev, len, GFP_ATOMIC,&usb_buf_phys);
uk_urb = usb_alloc_urb(0,GFP_KERNEL);
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;usb_submit_urb(uk_urb, GFP_KERNEL);
return 0 ;
}
static const struct usb_device_id usbmouse_as_key_id_table[] = {
{USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID,
USB_INTERFACE_SUBCLASS_BOOT,USB_INTERFACE_PROTOCOL_MOUSE)},
{}
};
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 __init usbmouse_as_key_init(void)
{
usb_register(&usbmouse_as_key_driver);return 0;
}static void __exit 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");
上面的版本可以编过的,付 Makefile
KERNEL :=/lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
obj-m := usbmouse_as_key_driver.o
all:
make -C $(KERNEL) M=$(PWD) modulesclean:
make -C $(KERNEL) M=$(PWD) clean
二、第二个版本
USB设备驱动可以使用input输入子系统框架来完成数据的读入即上报。和传统的input子系统存在差异,传统的input输入子系统是在中断函数里面获取数据然后input_event上报数据,在usb设备中,数据的读取由usb总线驱动完成,这个可以直接使用urb模块来实现。然后再urb的中断中上报数据
#include<linux/kernel>
#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 *input_dev;
static struct urb *urb;
static dma_addr_t data_dma;
static unsigned char *data;
static unsigned len;
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_free_coherent(dev, len, usb_buf, usb_buf_phys);
input_unregister_device(uk_dev);
input_free_device(uk_dev);
}static void usbtouch_irq(struct urb *urb)
{
#if 1
int status;
switch (urb->status) {
case 0:/* success */
break;
case -ECONNRESET:/* unlink */
case -ENOENT:
case -ESHUTDOWN:
return;
/* -EPIPE: should clear the halt */
default:/* error */
goto resubmit;
}
/*在usb设备驱动程序中,需要对数据进行解析。即usb总线获取到数据不会解析数据的含义
*需要在usb设备驱动中进行解析数据,我的usb数百data[1]为按键的值
*data[2]为鼠标在x方向上的相对位移值,data[3]为鼠标在y方向上的相对位移值
*data[4]为鼠标滚轮滑动产生的值
*/
input_report_key(input_dev, BTN_LEFT, data[0] & 0x01);
input_report_key(input_dev, BTN_RIGHT, data[0] & 0x02);
input_report_key(input_dev, BTN_MIDDLE, data[0] & 0x04);
input_report_key(input_dev, BTN_SIDE, data[0] & 0x08);
input_report_key(input_dev, BTN_EXTRA, data[0] & 0x10);
input_report_rel(input_dev, REL_X, data[1]);
input_report_rel(input_dev, REL_Y, data[2]);
input_report_rel(input_dev, REL_WHEEL, data[3】);
input_sync(input_dev);
resubmit:
/*上报后,重新提交urb请求*/
status = usb_submit_urb (urb, GFP_ATOMIC);
#else
/*打印USB总线驱动获取到的数据,分析数据。解析其数据含义*/
int i =0,status;
for(i = 0; i < len;i++){
printk("%x ",data[i]);
}
printk("\n");
status = usb_submit_urb (urb, GFP_ATOMIC);
#endif
}
static int usbmouse_as_key_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;
struct input_dev * uk_dev;interface = intf->cur_altsetting;
if(interface->desc.nNumEndpoints != 1)
return -ENODEV;
endpoint = &interface->endpoint[0].desc;if(!usb_endpoint_is_int_in(endpoint))
return -ENODEV;
uk_dev = input_allocate_device();set_bit(EV_KEY,uk_dev->evbit);
set_bit(EV_REP,uk_dev->evbit);set_bit(KEY_L,uk_dev->keybit);
set_bit(KEY_S,uk_dev->keybit);
set_bit(KEY_ENTER,uk_dev->keybit);input_register_device(uk_dev);
pipe = usb_rcvintpipe(dev,endpoint->bEndpointAddress);
len = endpoint->wMaxPacketSize;usb_buf = usb_alloc_coherent(dev, len, GFP_ATOMIC,&usb_buf_phys);
uk_urb = usb_alloc_urb(0,GFP_KERNEL);
usb_fill_int_urb(uk_urb, dev, pipe, usb_buf, len, usbtouch_irq, NULL, endpoint->bInterval);uk_urb->transfer_dma = usb_buf_phys;
uk_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;usb_submit_urb(uk_urb, GFP_KERNEL);
return 0 ;
}
static const struct usb_device_id usbmouse_as_key_id_table[] = {
{USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,USB_INTERFACE_PROTOCOL_MOUSE)},
{}
};
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 __init usbmouse_as_key_init(void)
{
usb_register(&usbmouse_as_key_driver);return 0;
}static void __exit 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_mouse;
static struct input_dev *input_dev;
static struct urb *urb;
static dma_addr_t data_dma;
static unsigned char *data;
static unsigned len;
上面可以封装,来看一下 usb_mouse
struct usb_mouse {
char name[128];
char phys[64];
struct usb_device *usbdev;
struct input_dev *dev;
struct urb *irq;signed char *data;
dma_addr_t data_dma;
};
ok 了,自己慢慢体会USB设备驱动的编写吧。