一、鼠标

linux下的usb鼠标驱动在/drivers/hid/usbhid/usbmouse.c中实现

1.加载初始化过程

1.1模块入口

module_init(usb_mouse_init);

1.2初始化函数

static int __init usb_mouse_init(void)	//初始化
{
int retval = usb_register(&usb_mouse_driver); //注册usb鼠标驱动
if (retval == 0)
printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"DRIVER_DESC "\n");
return retval;
}

1.3初始化函数注册了一个usb驱动usb_mouse_driver

static struct usb_driver usb_mouse_driver = {	//usb鼠标驱动
.name = "usbmouse", //驱动名
.probe = usb_mouse_probe, //匹配方法
.disconnect = usb_mouse_disconnect, //拔出方法
.id_table = usb_mouse_id_table, //支持设备id表
};

1.4当插入鼠标时会根据usb_mouse_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 */
};

它的匹配方式是接口id匹配.接口类USB_INTERFACE_CLASS_HID

usb插入枚举时候会获取usb鼠标的接口类型,获取其接口类信息,匹配成功的话会动态创建一个usb_device.

在分析probe和disconnect方法之前先介绍下驱动用来描述usb鼠标对象的结构体usb_mouse

struct usb_mouse {
char name[128];//usb鼠标设备名
char phys[64];//路径
struct usb_device *usbdev;//usb设备
struct input_dev *dev;//输入设备
struct urb *irq;//urb结构体
signed char *data; //数据传输缓冲区指针
dma_addr_t data_dma;
};

usb鼠标既包含usb设备(usb_device)的属性也包含input输入设备(input_dev)的属性

1.5 匹配成功了就会调用probe方法

static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct usb_device *dev = interface_to_usbdev(intf); //根据usb接口获取动态创建的usb_device
struct usb_host_interface *interface;
struct usb_endpoint_descriptor *endpoint;
struct usb_mouse *mouse;
struct input_dev *input_dev;
int pipe, maxp;
int error = -ENOMEM; interface = intf->cur_altsetting; //获取usb_host_interface
if (interface->desc.bNumEndpoints != 1) //鼠标的端点有且仅有1个控制端点
return -ENODEV;
endpoint = &interface->endpoint[0].desc; //获取端点描述符
if (!usb_endpoint_is_int_in(endpoint)) //判断该端点是否中断端点
return -ENODEV;
//上面判断了usb鼠标的属性,有且仅有1个控制端点(0号端点不算进来的) pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); //设置端点为中断输入端点
maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); //获取包数据最大值
mouse = kzalloc(sizeof(struct usb_mouse), GFP_KERNEL); //分配usb_mouse对象
input_dev = input_allocate_device(); //初始化输入设备
if (!mouse || !input_dev)
goto fail1;
mouse->data = usb_alloc_coherent(dev, 8, GFP_ATOMIC, &mouse->data_dma);//分配初始化usb鼠标数据缓冲区内存(默认8位数据)
if (!mouse->data)
goto fail1;
mouse->irq = usb_alloc_urb(0, GFP_KERNEL);//分配初始化urb
if (!mouse->irq)
goto fail2;
mouse->usbdev = dev; //设置usb鼠标设备的usb设备对象
mouse->dev = input_dev; //设备usb鼠标设备的input设备对象 if (dev->manufacturer) //枚举时候有获取到有效的厂商名
strlcpy(mouse->name, dev->manufacturer, sizeof(mouse->name)); //复制厂商名到name
if (dev->product) { //枚举时候有获取到有效的产品名
if (dev->manufacturer) //如果也有厂商名
strlcat(mouse->name, " ", sizeof(mouse->name)); //则用空格将厂商名和产品名隔开
strlcat(mouse->name, dev->product, sizeof(mouse->name)); //追加产品名到name
}
if (!strlen(mouse->name)) //如果厂商和产品名都没有
snprintf(mouse->name, sizeof(mouse->name),"USB HIDBP Mouse %04x:%04x",
le16_to_cpu(dev->descriptor.idVendor),le16_to_cpu(dev->descriptor.idProduct));
//则直接根据厂商id和产品id给name赋值
usb_make_path(dev, mouse->phys, sizeof(mouse->phys)); //设置设备路径名
strlcat(mouse->phys, "/input0", sizeof(mouse->phys)); //追加/input0
input_dev->name = mouse->name; //输入设备的名字设置成usb鼠标的名字
input_dev->phys = mouse->phys; //输入设备的路径设置成usb鼠标的路径
usb_to_input_id(dev, &input_dev->id); //设置输入设备的bustype,vendor,product,version
input_dev->dev.parent = &intf->dev; //usb接口设备为输入设备的父设备 input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); //输入事件类型按键+相对位移
input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);
//按键类型 鼠标:左键,右键,中键
input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); //相对位移x方向+y方向
input_dev->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_SIDE) | BIT_MASK(BTN_EXTRA);
//按键类型 鼠标:旁键,外部键
input_dev->relbit[0] |= BIT_MASK(REL_WHEEL); //相对位移 鼠标滚轮事件 input_set_drvdata(input_dev, mouse); //usb鼠标驱动文件作为输入设备的设备文件的驱动数据
input_dev->open = usb_mouse_open; //设置输入事件的打开方法
input_dev->close = usb_mouse_close; //设置输入事件的关闭方法 usb_fill_int_urb(mouse->irq, dev, pipe, mouse->data,(maxp > 8 ? 8 : maxp),usb_mouse_irq, mouse, endpoint->bInterval);
//填充中断类型urb 指定了urb的回调函数是usb_mouse_irq
mouse->irq->transfer_dma = mouse->data_dma;//dma数据缓冲区指向usb鼠标设备的data_dma成员
mouse->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;//没DMA映射
error = input_register_device(mouse->dev);
if (error)
goto fail3;
usb_set_intfdata(intf, mouse); ////usb鼠标驱动文件作为usb接口设备的设备文件的驱动数据
return 0;
fail3:
usb_free_urb(mouse->irq);
fail2:
usb_free_coherent(dev, 8, mouse->data, mouse->data_dma);
fail1:
input_free_device(input_dev);
kfree(mouse);
return error;
}

1.6 拔掉usb鼠标就会调用disconnect方法

static void usb_mouse_disconnect(struct usb_interface *intf)
{
struct usb_mouse *mouse = usb_get_intfdata (intf); //根据usb接口设备的设备文件的驱动数据,获取usb鼠标设备 usb_set_intfdata(intf, NULL); //清空usb接口设备的设备文件的驱动数据
if (mouse) {
usb_kill_urb(mouse->irq); //断掉urb传输
input_unregister_device(mouse->dev); //注销输入设备
usb_free_urb(mouse->irq); //释放urb
usb_free_coherent(interface_to_usbdev(intf), 8, mouse->data, mouse->data_dma); //清除传输数据缓冲区
kfree(mouse); //释放usb鼠标设备
}
}

基本上disconnect只是probe的一个逆操作而已

经过probe过程,注册了输入设备则会在/dev/input/目录下会产生对应的鼠标设备节点,应用程序可以打开该节点来控制usb鼠标设备

此时会调用usb_mouse_open方法

1.7打开鼠标

static int usb_mouse_open(struct input_dev *dev)
{
struct usb_mouse *mouse = input_get_drvdata(dev); //通过输入设备获取usb鼠标设备
mouse->irq->dev = mouse->usbdev; //设置urb设备对应的usb设备
if (usb_submit_urb(mouse->irq, GFP_KERNEL)) //提交urb
return -EIO;
return 0;
}

通过urb提交之后,鼠标动作通过usb传输数据就会交由urb去处理了

1.8.urb数据传输

当操作鼠标的时候,会引起urb数据传输在数据传输之后会调用usb_mouse_irq

static void usb_mouse_irq(struct urb *urb)
{
struct usb_mouse *mouse = urb->context; //获取usb鼠标设备
signed char *data = mouse->data; //数据传输缓冲区指针
struct input_dev *dev = mouse->dev; //输入设备
int status;
switch (urb->status) { //判断urb传输的状态
case 0: /* success */ //传输成功跳出switch
break;
case -ECONNRESET: /* unlink */
case -ENOENT:
case -ESHUTDOWN:
return;
/* -EPIPE: should clear the halt */
default: /* error */
goto resubmit;
}
input_report_key(dev, BTN_LEFT, data[0] & 0x01); //右键
input_report_key(dev, BTN_RIGHT, data[0] & 0x02); //左键
input_report_key(dev, BTN_MIDDLE, data[0] & 0x04); //中键
input_report_key(dev, BTN_SIDE, data[0] & 0x08); //边键
input_report_key(dev, BTN_EXTRA, data[0] & 0x10); //外部键
input_report_rel(dev, REL_X, data[1]); //相对x坐标位移
input_report_rel(dev, REL_Y, data[2]); //相对y坐标位移
input_report_rel(dev, REL_WHEEL, data[3]); //相对滚轮位移
input_sync(dev); //同步事件
resubmit:
status = usb_submit_urb (urb, GFP_ATOMIC); //继续提交urb
if (status)
err ("can't resubmit intr, %s-%s/input0, status %d",mouse->usbdev->bus->bus_name,mouse->usbdev->devpath, status);
}

usb接口传来的数据会保存在usb鼠标data指针成员指向的缓冲区中

这里可以看出usb鼠标传输的每次数据基本是4个字节

第0个字节的第1位表示右键,第2位表示左键,第3位表示中键,第4位表示边键,第5为表示外部键
而第1个字节表示相对x坐标的位移,第2个字节表示相对y坐标的位移,第3个字节表示相对滚轮的位移


当输入设备上报完usb接口接收来的数据后,需要调用input_sync同步事件消息,并调用usb_submit_urb提交urb

使其继续监视处理usb鼠标设备传递的新数据.

应用程序要获取鼠标操作信息可以打开对应的输入设备节点,并通过输入设备的读接口,获取到usb鼠标通过usb接口传递并交由输入设备上报过来的数据

漏掉的函数

1.应用程序关闭鼠标设备

static void usb_mouse_close(struct input_dev *dev)
{
struct usb_mouse *mouse = input_get_drvdata(dev); //通过输入设备获取usb鼠标设备
usb_kill_urb(mouse->irq); //当关闭鼠标设备时候,需要断掉urb传输
}

2.模块移除调用的函数

module_exit(usb_mouse_exit);
static void __exit usb_mouse_exit(void)
{
usb_deregister(&usb_mouse_driver); //注销掉usb鼠标设备
}

 二、键盘

linux下的usb键盘驱动在/drivers/hid/usbhid/usbkbd.c中实现

1.加载初始化过程

1.1 模块入口

module_init(usb_kbd_init);

1.2 初始化函数

static int __init usb_kbd_init(void)
{
int result = usb_register(&usb_kbd_driver); //注册usb键盘
if (result == 0)
printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"DRIVER_DESC "\n");
return result;
}

1.3 初始化函数注册了一个usb驱动usb_kbd_driver

static struct usb_driver usb_kbd_driver = {	//usb键盘驱动
.name = "usbkbd", //驱动名
.probe = usb_kbd_probe, //匹配方法
.disconnect = usb_kbd_disconnect, //拔出方法
.id_table = usb_kbd_id_table, //支持设备id
};

1.4 当插入鼠标时会根据usb_kbd_id_table去匹配创建usb设备

static struct usb_device_id usb_kbd_id_table [] = {
{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,USB_INTERFACE_PROTOCOL_KEYBOARD) },
{ } /* Terminating entry */
};

它的匹配方式是接口id匹配.接口类USB_INTERFACE_CLASS_HID

usb插入枚举时候会获取usb键盘的接口类型,获取其接口类信息,匹配成功的话会动态创建一个usb_device.

在分析probe和disconnect方法之前先介绍下驱动用来描述usb键盘对象的结构体usb_kbd

struct usb_kbd {
struct input_dev *dev; //输入设备
struct usb_device *usbdev; //usb设备
unsigned char old[8]; //旧的键盘按键数据
struct urb *irq, *led; //键盘urb,led urb
unsigned char newleds; //新的led数据
char name[128]; //usb键盘设备名字
char phys[64]; //usb键盘设备路径
unsigned char *new; //usb键盘按键 数据传输缓冲区指针
struct usb_ctrlrequest *cr; //setup数据包控制请求描述符
unsigned char *leds; //usb键盘led 数据传输缓冲区指针
dma_addr_t new_dma; //usb键盘按键DMA映射总线地址
dma_addr_t leds_dma; //usb键盘led DMA映射总线地址
};

usb键盘既包含usb设备(usb_device)的属性也包含input输入设备(input_dev)的属性

1.5 匹配成功了就会调用probe方法

static int usb_kbd_probe(struct usb_interface *iface,const struct usb_device_id *id)
{
struct usb_device *dev = interface_to_usbdev(iface); //根据usb接口获取动态创建的usb_device
struct usb_host_interface *interface;
struct usb_endpoint_descriptor *endpoint;
struct usb_kbd *kbd;
struct input_dev *input_dev;
int i, pipe, maxp;
int error = -ENOMEM; interface = iface->cur_altsetting; //获取usb_host_interface
if (interface->desc.bNumEndpoints != 1) //键盘的端点有且仅有1个控制端点
return -ENODEV;
endpoint = &interface->endpoint[0].desc; //获取端点描述符
if (!usb_endpoint_is_int_in(endpoint)) //判断该端点是否中断端点
return -ENODEV;
//上面判断了usb键盘的属性,有且仅有1个控制端点(0号端点不算进来的) pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); //设置端点为中断输入端点
maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); //获取包数据最大值
kbd = kzalloc(sizeof(struct usb_kbd), GFP_KERNEL); //分配usb_kbd对象
input_dev = input_allocate_device(); //初始化输入设备
if (!kbd || !input_dev)
goto fail1;
if (usb_kbd_alloc_mem(dev, kbd)) //分配usb键盘需要的内存
goto fail2;
kbd->usbdev = dev; //设置usb键盘设备的usb设备对象
kbd->dev = input_dev; //设备usb键盘设备的input设备对象 if (dev->manufacturer) //枚举时候有获取到有效的厂商名
strlcpy(kbd->name, dev->manufacturer, sizeof(kbd->name)); //复制厂商名到name
if (dev->product) { //枚举时候有获取到有效的产品名
if (dev->manufacturer) //如果也有厂商名
strlcat(kbd->name, " ", sizeof(kbd->name)); //则用空格将厂商名和产品名隔开
strlcat(kbd->name, dev->product, sizeof(kbd->name)); //追加产品名到name
}
if (!strlen(kbd->name)) //如果厂商和产品名都没有
snprintf(kbd->name, sizeof(kbd->name),"USB HIDBP Keyboard %04x:%04x",
le16_to_cpu(dev->descriptor.idVendor),le16_to_cpu(dev->descriptor.idProduct));
//则直接根据厂商id和产品id给name赋值
usb_make_path(dev, kbd->phys, sizeof(kbd->phys)); //设置设备路径名
strlcat(kbd->phys, "/input0", sizeof(kbd->phys)); //追加/input0
input_dev->name = kbd->name; //输入设备的名字设置成usb键盘的名字
input_dev->phys = kbd->phys; //输入设备的路径设置成usb键盘的路径
usb_to_input_id(dev, &input_dev->id); //设置输入设备的bustype,vendor,product,version
input_dev->dev.parent = &iface->dev; //usb接口设备为输入设备的父设备
input_set_drvdata(input_dev, kbd); //usb键盘驱动文件作为输入设备的设备文件的驱动数据 input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_LED) | BIT_MASK(EV_REP); //输入事件类型 按键+led+重复
input_dev->ledbit[0] = BIT_MASK(LED_NUML) | BIT_MASK(LED_CAPSL) | BIT_MASK(LED_SCROLLL) | BIT_MASK(LED_COMPOSE) | BIT_MASK(LED_KANA);
//键盘led事件:小键盘,大小写,滚动锁定,组合键,KANA
for (i = 0; i < 255; i++)
set_bit(usb_kbd_keycode[i], input_dev->keybit);
clear_bit(0, input_dev->keybit); //清除无效的0位
//键盘按键事件:遍历全局usb_kbd_keycode数组设置
input_dev->event = usb_kbd_event; //设置输入事件的event方法
input_dev->open = usb_kbd_open; //设置输入事件的open方法
input_dev->close = usb_kbd_close; //设置输入事件的close方法
usb_fill_int_urb(kbd->irq, dev, pipe,kbd->new, (maxp > 8 ? 8 : maxp),usb_kbd_irq, kbd, endpoint->bInterval);
//填充中断类型urb 指定了urb的回调函数是usb_kbd_irq
kbd->irq->transfer_dma = kbd->new_dma; //usb键盘按键设备DMA映射总线地址
kbd->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; //没DMA映射
//设置usb setup传输数据包控制请求结构体
kbd->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
kbd->cr->bRequest = 0x09;//SET_IDLE?
kbd->cr->wValue = cpu_to_le16(0x200);
kbd->cr->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);
kbd->cr->wLength = cpu_to_le16(1);
usb_fill_control_urb(kbd->led, dev, usb_sndctrlpipe(dev, 0),(void *) kbd->cr, kbd->leds, 1,usb_kbd_led, kbd);
//设置为控制输出端点,填充控制类型urb,回调函数usb_kbd_led
kbd->led->transfer_dma = kbd->leds_dma; //usb键盘led设备DMA映射总线地址
kbd->led->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; //没DMA映射
error = input_register_device(kbd->dev); //注册输入设备
if (error)
goto fail2;
usb_set_intfdata(iface, kbd); //usb键盘驱动文件作为usb接口设备的设备文件的驱动数据
device_set_wakeup_enable(&dev->dev, 1); //使能系统唤醒
return 0;
fail2:
usb_kbd_free_mem(dev, kbd); //分配失败则释放相关内存
fail1:
input_free_device(input_dev); //释放输入设备
kfree(kbd); //释放usb_kbd
return error;
}

probe方法中调用的内存分配释放函数

分配内存

static int usb_kbd_alloc_mem(struct usb_device *dev, struct usb_kbd *kbd)
{
if (!(kbd->irq = usb_alloc_urb(0, GFP_KERNEL))) //分配按键urb
return -1;
if (!(kbd->led = usb_alloc_urb(0, GFP_KERNEL))) //分配led灯urb
return -1;
if (!(kbd->new = usb_alloc_coherent(dev, 8, GFP_ATOMIC, &kbd->new_dma))) //分配初始化usb键盘数据缓冲区内存(默认8位数据)
return -1;
if (!(kbd->cr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL))) //分配setup包的控制请求描述符
return -1;
if (!(kbd->leds = usb_alloc_coherent(dev, 1, GFP_ATOMIC, &kbd->leds_dma))) //分配初始化usb键盘led数据缓冲区内存
return -1;
return 0;
}

释放内存

static void usb_kbd_free_mem(struct usb_device *dev, struct usb_kbd *kbd)
{
usb_free_urb(kbd->irq); //释放键盘按键urb
usb_free_urb(kbd->led); //释放键盘led urb
usb_free_coherent(dev, 8, kbd->new, kbd->new_dma); //释放usb键盘数据缓冲区
kfree(kbd->cr); //释放setup包的控制请求描述符
usb_free_coherent(dev, 1, kbd->leds, kbd->leds_dma); //释放urb键盘led数据缓冲区内存
}

配置用到的全局键值数组

static const unsigned char usb_kbd_keycode[256] = {	//键值
0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38,
50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2, 3,
4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26,
27, 43, 43, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64,
65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106,
105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71,
72, 73, 82, 83, 86,127,116,117,183,184,185,186,187,188,189,190,
191,192,193,194,134,138,130,132,128,129,131,137,133,135,136,113,
115,114, 0, 0, 0,121, 0, 89, 93,124, 92, 94, 95, 0, 0, 0,
122,123, 90, 91, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113,
150,158,159,128,136,177,178,176,142,152,173,140
};

1.6 拔掉usb鼠标就会调用disconnect方法

static void usb_kbd_disconnect(struct usb_interface *intf)
{
struct usb_kbd *kbd = usb_get_intfdata (intf); //根据usb接口设备的设备文件的驱动数据,获取usb键盘设备
usb_set_intfdata(intf, NULL); //清空usb接口设备的设备文件的驱动数据
if (kbd) {
usb_kill_urb(kbd->irq); //断掉urb传输
input_unregister_device(kbd->dev); //注销输入设备
usb_kbd_free_mem(interface_to_usbdev(intf), kbd); //释放usb键盘需要的内存
kfree(kbd); //释放usb键盘设备
}
}

基本上disconnect只是probe的一个逆操作而已

经过probe过程,注册了输入设备则会在/dev/input/目录下会产生对应的键盘设备节点,应用程序可以打开该节点来控制usb键盘设备

此时会调用usb_kbd_open方法

1.7打开键盘

static int usb_kbd_open(struct input_dev *dev)
{
struct usb_kbd *kbd = input_get_drvdata(dev); //通过输入设备获取usb键盘设备
kbd->irq->dev = kbd->usbdev; //usb键盘按键urb捆绑usb设备
if (usb_submit_urb(kbd->irq, GFP_KERNEL)) //提交usb键盘按键urb
return -EIO;
return 0;
}

关闭键盘调用usb_kbd_close

static void usb_kbd_close(struct input_dev *dev)
{
struct usb_kbd *kbd = input_get_drvdata(dev); //通过输入设备获取usb键盘设备
usb_kill_urb(kbd->irq); //断开usb键盘按键urb
}

通过urb提交之后,键盘动作通过usb传输数据就会交由urb去处理了

1.8.urb数据传输

static void usb_kbd_irq(struct urb *urb)
{
struct usb_kbd *kbd = urb->context; //获取usb键盘设备
int i; switch (urb->status) { //判断urb传输的状态
case 0: /* success */ //传输成功跳出switch
break;
case -ECONNRESET: /* unlink */
case -ENOENT:
case -ESHUTDOWN:
return;
/* -EPIPE: should clear the halt */
default: /* error */
goto resubmit;
}
//L-ctrl,L-shift,L-alt,L-gui,R-ctrl,R-shift,R-alt,R-gui
for (i = 0; i < 8; i++) //(224~231)判断新按下的是否组合键
input_report_key(kbd->dev, usb_kbd_keycode[i + 224], (kbd->new[0] >> i) & 1); //组合键 //memscan(kbd->new + 2, kbd->old[i], 6)表示从kbd->new[2]扫描6个单位到kbd->new[7],
//查找kbd->old[i]一样的字符,返回扫描到的单位地址"==kbd->new+8"表示没找到
//键盘扫描码0-No Event 1-Overrun Error 2-POST Fail 3-ErrorUndefined所以kbd->old[i] > 3
//键值usb_kbd_keycode[i]和扫描码new[i]/old[i]要区分好别乱了
//键盘扫描码和数据格式见函数下面图片
for (i = 2; i < 8; i++) {
//新数据中没有查找到旧数据一样的码值--表示新的按键按下,旧的按键释放
if (kbd->old[i] > 3 && memscan(kbd->new + 2, kbd->old[i], 6) == kbd->new + 8) {
if (usb_kbd_keycode[kbd->old[i]]) //松开的按键是正常的按键
input_report_key(kbd->dev, usb_kbd_keycode[kbd->old[i]], 0); //上报释放按键事件
else
dev_info(&urb->dev->dev,"Unknown key (scancode %#x) released.\n", kbd->old[i]);
}
//旧数据中没有查找到新数据一样的码值--表示新的按键按下,就的按键按下
if (kbd->new[i] > 3 && memscan(kbd->old + 2, kbd->new[i], 6) == kbd->old + 8) {
if (usb_kbd_keycode[kbd->new[i]]) //按下的按键是正常的按键
input_report_key(kbd->dev, usb_kbd_keycode[kbd->new[i]], 1); //上报按下按键事件
else
dev_info(&urb->dev->dev,"Unknown key (scancode %#x) released.\n", kbd->new[i]);
}
}
//数据的第2~7字节用于存放键码,分别可以存放6个,也就是可以支持同时6个按键按下
//如果一直按住键盘的某个按键,则usb接收到的数据会都是一样的也就是kbd->old==kbd->new,则按下的时候会上报按下事件,一直按着的时候不会继续上报按下或释放按键
//若有新的按键按下,则所有的kdb->old的值可以在kdb->new中找到,而kdb->new中代表新按键键码的值在kdb->old中会找不到,所以触发第二个if条件成立,上报按下按键事件
//若之前的按键松开,则所有的kdb->new的值可以在kdb->old中找到,而kdb->old中代表旧按键键码的值在kdb->new中会找不到,所以触发第一个if条件成立,上报释放按键事件
input_sync(kbd->dev); //同步事件
memcpy(kbd->old, kbd->new, 8); //新的键值存放在旧的键值
resubmit:
i = usb_submit_urb (urb, GFP_ATOMIC); //提交urb
if (i)
err_hid ("can't resubmit intr, %s-%s/input0, status %d",kbd->usbdev->bus->bus_name,kbd->usbdev->devpath, i);
}

Usage
index
(dec)

Usage
Index
(hex)

Usage

Ref:typical
AT-101
position

PC-AT

Mac-
intosh

UNIX

Boot

Reserved (no event indicated) 9

N/A

Ö

Ö

Ö

84/101/104

Keyboard ErrorRollOver

N/A

Ö

Ö

Ö

84/101/104

Keyboard POSTFail

N/A

Ö

Ö

Ö

84/101/104

Keyboard ErrorUndefined

N/A

Ö

Ö

Ö

84/101/104

Keyboard a and A

Ö

Ö

Ö

84/101/104

Keyboard b and B

Ö

Ö

Ö

84/101/104

Keyboard c and C

Ö

Ö

Ö

84/101/104

Keyboard d and D

Ö

Ö

Ö

84/101/104

Keyboard e and E

Ö

Ö

Ö

84/101/104

Keyboard f and F

Ö

Ö

Ö

84/101/104

0A

Keyboard g and G

Ö

Ö

Ö

84/101/104

0B

Keyboard h and H

Ö

Ö

Ö

84/101/104

0C

Keyboard i and I

Ö

Ö

Ö

84/101/104

0D

Keyboard j and J

Ö

Ö

Ö

84/101/104

0E

Keyboard k and K

Ö

Ö

Ö

84/101/104

0F

Keyboard l and L

Ö

Ö

Ö

84/101/104

Keyboard m and M

Ö

Ö

Ö

84/101/104

Keyboard n and N

Ö

Ö

Ö

84/101/104

Keyboard o and O

Ö

Ö

Ö

84/101/104

Keyboard p and P

Ö

Ö

Ö

84/101/104

Keyboard q and Q

Ö

Ö

Ö

84/101/104

Keyboard r and R

Ö

Ö

Ö

84/101/104

Keyboard s and S

Ö

Ö

Ö

84/101/104

Keyboard t and T

Ö

Ö

Ö

84/101/104

Keyboard u and U

Ö

Ö

Ö

84/101/104

Keyboard v and V

Ö

Ö

Ö

84/101/104

1A

Keyboard w and W

Ö

Ö

Ö

84/101/104

1B

Keyboard x and X

Ö

Ö

Ö

84/101/104

1C

Keyboard y and Y

Ö

Ö

Ö

84/101/104

1D

Keyboard z and Z

Ö

Ö

Ö

84/101/104

1E

Keyboard 1 and ! 4

Ö

Ö

Ö

84/101/104

1F

Keyboard 2 and @

Ö

Ö

Ö

84/101/104

Keyboard 3 and #

Ö

Ö

Ö

84/101/104

Keyboard 4 and $

Ö

Ö

Ö

84/101/104

Keyboard 5 and %

Ö

Ö

Ö

84/101/104

Keyboard 6 and ^

Ö

Ö

Ö

84/101/104

Keyboard 7 and &

Ö

Ö

Ö

84/101/104

Keyboard 8 and *

Ö

Ö

Ö

84/101/104

Keyboard 9 and (

Ö

Ö

Ö

84/101/104

Keyboard 0 and ) 4

Ö

Ö

Ö

84/101/104

Keyboard Return(ENTER) 5

Ö

Ö

Ö

84/101/104

Keyboard ESCAPE

Ö

Ö

Ö

84/101/104

2A

Keyboard DELETE
(Backspace) 13

Ö

Ö

Ö

84/101/104

2B

Keyboard Tab

Ö

Ö

Ö

84/101/104

2C

Keyboard Spacebar

Ö

Ö

Ö

84/101/104

2D

Keyboard - and (underscore) 4

Ö

Ö

Ö

84/101/104

2E

Keyboard = and+

Ö

Ö

Ö

84/101/104

2F

Keyboard [ and {

Ö

Ö

Ö

84/101/104

Keyboard ] and }

Ö

Ö

Ö

84/101/104

Keyboard \ and |

Ö

Ö

Ö

84/101/104

Keyboard Non-US# and ~

Ö

Ö

Ö

84/101/104

Keyboard

Ö

Ö

Ö

84/101/104

Keyboard ‘ and “

Ö

Ö

Ö

84/101/104

Keyboard Grave Accent and

Tilde

Ö

Ö

Ö

84/101/104

Keyboard , and <

Ö

Ö

Ö

84/101/104

Keyboard . and >

Ö

Ö

Ö

84/101/104

Keyboard / and ? 4

Ö

Ö

Ö

84/101/104

Keyboard CapsLock

Ö

Ö

Ö

84/101/104

3A

Keyboard F1

Ö

Ö

Ö

84/101/104

3B

Keyboard F2

Ö

Ö

Ö

84/101/104

3C

Keyboard F3

Ö

Ö

Ö

84/101/104

3D

Keyboard F4

Ö

Ö

Ö

84/101/104

3E

Keyboard F5

Ö

Ö

Ö

84/101/104

3F

Keyboard F6

Ö

Ö

Ö

84/101/104

Keyboard F7

Ö

Ö

Ö

84/101/104

Keyboard F8

Ö

Ö

Ö

84/101/104

Keyboard F9

Ö

Ö

Ö

84/101/104

Keyboard F10

Ö

Ö

Ö

84/101/104

Keyboard F11

Ö

Ö

Ö

101/104

Keyboard F12

Ö

Ö

Ö

101/104

Keyboard PrintScreen

Ö

Ö

Ö

101/104

Keyboard ScrollLock

Ö

Ö

Ö

84/101/104

Keyboard Pause

Ö

Ö

Ö

101/104

Keyboard Insert

Ö

Ö

Ö

101/104

4A

Keyboard Home

Ö

Ö

Ö

101/104

4B

Keyboard PageUp

Ö

Ö

Ö

101/104

4C

Keyboard Delete Forward

Ö

Ö

Ö

101/104

4D

Keyboard End

Ö

Ö

Ö

101/104

4E

Keyboard PageDown

Ö

Ö

Ö

101/104

4F

Keyboard RightArrow

Ö

Ö

Ö

101/104

Keyboard LeftArrow

Ö

Ö

Ö

101/104

Keyboard DownArrow

Ö

Ö

Ö

101/104

Keyboard UpArrow

Ö

Ö

Ö

101/104

Keypad NumLock and Clear

Ö

Ö

Ö

101/104

Keypad /

Ö

Ö

Ö

101/104

Keypad *

Ö

Ö

Ö

84/101/104

Keypad -

Ö

Ö

Ö

84/101/104

Keypad +

Ö

Ö

Ö

84/101/104

Keypad ENTER5

Ö

Ö

Ö

101/104

Keypad 1 and End

Ö

Ö

Ö

84/101/104

5A

Keypad 2 and Down Arrow

Ö

Ö

Ö

84/101/104

5B

Keypad 3 and PageDn

Ö

Ö

Ö

84/101/104

5C

Keypad 4 and Left Arrow

Ö

Ö

Ö

84/101/104

5D

Keypad 5

Ö

Ö

Ö

84/101/104

5E

Keypad 6 and Righ tArrow

Ö

Ö

Ö

84/101/104

5F

Keypad 7 and Home

Ö

Ö

Ö

84/101/104

Keypad 8 and Up Arrow

Ö

Ö

Ö

84/101/104

Keypad 9 and PageUp

Ö

Ö

Ö

84/101/104

Keypad 0 and Insert

Ö

Ö

Ö

84/101/104

Keypad . and Delete

Ö

Ö

Ö

84/101/104

Keyboard Non-US\ and |3;6

Ö

Ö

Ö

84/101/104

Keyboard Application

Ö

Ö

Keyboard Power

Ö

Ö

Keypad =

Ö

Keyboard F13

Ö

Keyboard F14

Ö

6A

Keyboard F15

Ö

6B

Keyboard F16

6C

Keyboard F17

6D

Keyboard F18

6E

Keyboard F19

6F

Keyboard F20

Keyboard F21

Keyboard F22

Keyboard F23

Keyboard F24

Keyboard Execute

Ö

Keyboard Help

Ö

Keyboard Menu

Ö

Keyboard Select

Ö

Keyboard Stop

Ö

Keyboard Again

Ö

7A

Keyboard Undo

Ö

7B

Keyboard Cut

Ö

7C

Keyboard Copy

Ö

7D

Keyboard Paste

Ö

7E

Keyboard Find

Ö

7F

Keyboard Mute

Ö

Keyboard Volume Up

Ö

Keyboard Volume Down

Ö

Keyboard Locking Caps Lock

Ö

Keyboard Locking Num Lock

Ö

Keyboard Locking Scroll

Ö

Lock

Keypad Comma

Keypad Equal Sign

Keyboard Kanji1

Keyboard Kanji2

Keyboard Kanji3

8A

Keyboard Kanji4

8B

Keyboard Kanji5

8C

Keyboard Kanji6

8D

Keyboard Kanji7

8E

Keyboard Kanji8

8F

Keyboard Kanji9

Keyboard LANG1

Keyboard LANG2

Keyboard LANG3

Keyboard LANG4

Keyboard LANG5

Keyboard LANG6

Keyboard LANG7

Keyboard LANG8

Keyboard LANG9

Keyboard AlternateErase

9A

Keyboard SysReq/Attenti

9B

Keyboard Cancel

9C

Keyboard Clear

9D

Keyboard Prior

9E

Keyboard Return

9F

Keyboard Separator

A0

Keyboard Out

A1

Keyboard Oper

A2

Keyboard Clear/Again

A3

Keyboard CrSel/Props

A4

Keyboard ExSel

165-223

A5-DF

Reserved

E0

Keyboard LeftControl

58

Ö

Ö

Ö

84/101/104

E1

Keyboard LeftShift

44

Ö

Ö

Ö

84/101/104

E2

Keyboard LeftAlt

60

Ö

Ö

Ö

84/101/104

E3

Keyboard Left GUI10;23

127

Ö

Ö

Ö

E4

Keyboard RightControl

64

Ö

Ö

Ö

101/104

E5

Keyboard RightShift

57

Ö

Ö

Ö

84/101/104

E6

Keyboard RightAlt

62

Ö

Ö

Ö

101/104

E7

Keyboard Right GUI10;24

128

Ö

Ö

Ö

232-255

E8-FF

Reserved

 

1.9 usb键盘的led指示灯

当按下小键盘,大小写,滚动锁定,组合键,KANA控制按键的时候,usb键盘按键urb会处理usb数据并上报数据给输入子系统处理

输入子系统对键值为小键盘,大小写,滚动锁定,组合键,KANA的事件做处理,处理后会调用输入设备的event方法也就是usb_kbd_event

static int usb_kbd_event(struct input_dev *dev, unsigned int type,unsigned int code, int value)
{
struct usb_kbd *kbd = input_get_drvdata(dev); //通过输入设备获取usb键盘设备 if (type != EV_LED)
return -1;
kbd->newleds = (!!test_bit(LED_KANA,dev->led) << 3)|(!!test_bit(LED_COMPOSE, dev->led) << 3)|
(!!test_bit(LED_SCROLLL,dev->led) << 2)|(!!test_bit(LED_CAPSL,dev->led) << 1)|(!!test_bit(LED_NUML,dev->led));
//判断是否有 小键盘,大小写,滚动锁定,组合键,KANA事件
if (kbd->led->status == -EINPROGRESS)
return 0;
if (*(kbd->leds) == kbd->newleds) //比较新旧指示灯状态,跟目前状态一致,则返回
return 0;
*(kbd->leds) = kbd->newleds; //填充usb键盘led数据传输缓冲区
kbd->led->dev = kbd->usbdev; //捆绑usb设备
if (usb_submit_urb(kbd->led, GFP_ATOMIC)) //跟目前状态不一致,则提交usb键盘led urb 会通过控制输出端口发送setup包设置led灯状态
err_hid("usb_submit_urb(leds) failed");
return 0;
}

usb键盘led灯urb的回调函数

static void usb_kbd_led(struct urb *urb)
{
struct usb_kbd *kbd = urb->context; //通过urb获取usb键盘设备
if (urb->status)
dev_warn(&urb->dev->dev, "led urb status %d received\n",urb->status);
if (*(kbd->leds) == kbd->newleds) //比较新旧指示灯状态,跟目前状态一致,则返回
return;
*(kbd->leds) = kbd->newleds; //填充usb键盘led数据传输缓冲区
kbd->led->dev = kbd->usbdev; //捆绑usb设备
if (usb_submit_urb(kbd->led, GFP_ATOMIC)) //跟目前状态不一致,提交usb键盘led urb 会通过控制输出端口发送setup包设置led灯状态
err_hid("usb_submit_urb(leds) failed");
}

urb会发送setup包,Set_Report请求包通过控制端点0,紧接着是个2字节的数据输出包,第一个字节对应报告id,第二个字节是led数据信息(上图)

2.0 后话 关于usb_kbd_event函数调用的流程

首先定义了一个键盘任务,任务会循环执行kbd_bh函数
这里定义的时候是禁用了,在后面的执行的kbd_init函数中会使能,和调度keyboard_tasklet任务

DECLARE_TASKLET_DISABLED(keyboard_tasklet, kbd_bh, 0);	//创建keyboard_tasklet执行kbd_bh

kbd_bh函数获取通过getleds函数获取led状态标志,然后最终会调用kbd_update_leds_helper函数

static void kbd_bh(unsigned long dummy)
{
unsigned char leds = getleds(); //获取led状态标志
if (leds != ledstate) {
input_handler_for_each_handle(&kbd_handler, &leds,kbd_update_leds_helper); //会调用kbd_update_leds_helper
ledstate = leds;
}
}

getleds函数获取kbd->ledflagstate这个值,处理并返回.

static inline unsigned char getleds(void)
{
struct kbd_struct *kbd = kbd_table + fg_console;
unsigned char leds;
int i; if (kbd->ledmode == LED_SHOW_IOCTL)
return ledioctl;
leds = kbd->ledflagstate; //获取led标志状态
if (kbd->ledmode == LED_SHOW_MEM) {
for (i = 0; i < 3; i++)
if (ledptrs[i].valid) {
if (*ledptrs[i].addr & ledptrs[i].mask)
leds |= (1 << i);
else
leds &= ~(1 << i);
}
}
return leds;
}

ldeflagstate的值可以由以下三个函数来设置

static inline void set_vc_kbd_led(struct kbd_struct * kbd, int flag)
{
kbd->ledflagstate |= 1 << flag;
} static inline void clr_vc_kbd_led(struct kbd_struct * kbd, int flag)
{
kbd->ledflagstate &= ~(1 << flag);
} static inline void chg_vc_kbd_led(struct kbd_struct * kbd, int flag)
{
kbd->ledflagstate ^= 1 << flag;
}

而这三个函数的调用情况如下,键盘按键处理事件

fn_caps_on 		>>> set_vc_kbd_led(kbd, VC_CAPSLOCK);	//大小写led
k_shift >>> clr_vc_kbd_led(kbd, VC_CAPSLOCK); //大小写led
fn_caps_toggle >>> chg_vc_kbd_led(kbd, VC_CAPSLOCK); //大小写led fn_bare_num >>> chg_vc_kbd_led(kbd, VC_NUMLOCK); //小键盘led con_stop >>> set_vc_kbd_led(kbd_table + console_num, VC_SCROLLOCK); //滚轮锁定led
con_start >>> clr_vc_kbd_led(kbd_table + console_num, VC_SCROLLOCK); //滚轮锁定led

获取led状态标志后,调用kbd_update_leds_helper函数,上报led事件

static int kbd_update_leds_helper(struct input_handle *handle, void *data)
{
unsigned char leds = *(unsigned char *)data;
if (test_bit(EV_LED, handle->dev->evbit)) {
input_inject_event(handle, EV_LED, LED_SCROLLL, !!(leds & 0x01)); //上报滚轮锁定事件
input_inject_event(handle, EV_LED, LED_NUML, !!(leds & 0x02)); //上报数字小键盘事件
input_inject_event(handle, EV_LED, LED_CAPSL, !!(leds & 0x04)); //上报大小写事件
input_inject_event(handle, EV_SYN, SYN_REPORT, 0); //同步事件
}
return 0;
}

调用input_inject_event上报led事件,最终调用input_handle_event函数

void input_inject_event(struct input_handle *handle,unsigned int type, unsigned int code, int value)
{
struct input_dev *dev = handle->dev;
struct input_handle *grab;
unsigned long flags; if (is_event_supported(type, dev->evbit, EV_MAX)) {
spin_lock_irqsave(&dev->event_lock, flags);
rcu_read_lock();
grab = rcu_dereference(dev->grab);
if (!grab || grab == handle)
input_handle_event(dev, handle->handler,type, code, value); //调用input_handle_event函数
rcu_read_unlock();
spin_unlock_irqrestore(&dev->event_lock, flags);
}
}
EXPORT_SYMBOL(input_inject_event);

input_handle_event函数处理各种事件分支,最终就会调用到input设备的event方法(usb_kbd_event)

static void input_handle_event(struct input_dev *dev,struct input_handler *src_handler,unsigned int type, unsigned int code, int value)
{
int disposition = INPUT_IGNORE_EVENT; switch (type) { case EV_SYN: //同步事件
switch (code) {
case SYN_CONFIG:
disposition = INPUT_PASS_TO_ALL;
break;
case SYN_REPORT: //led同步事件分支
if (!dev->sync) {
dev->sync = true;
disposition = INPUT_PASS_TO_HANDLERS;
}
break;
case SYN_MT_REPORT:
dev->sync = false;
disposition = INPUT_PASS_TO_HANDLERS;
break;
}
break;
case EV_KEY:
if (is_event_supported(code, dev->keybit, KEY_MAX) &&!!test_bit(code, dev->key) != value) {
if (value != 2) {
__change_bit(code, dev->key);
if (value)
input_start_autorepeat(dev, code);
else
input_stop_autorepeat(dev);
}
disposition = INPUT_PASS_TO_HANDLERS;
}
break;
case EV_SW:
if (is_event_supported(code, dev->swbit, SW_MAX) &&
!!test_bit(code, dev->sw) != value) {
__change_bit(code, dev->sw);
disposition = INPUT_PASS_TO_HANDLERS;
}
break;
case EV_ABS:
if (is_event_supported(code, dev->absbit, ABS_MAX))
disposition = input_handle_abs_event(dev, src_handler,code, &value);
break;
case EV_REL:
if (is_event_supported(code, dev->relbit, REL_MAX) && value)
disposition = INPUT_PASS_TO_HANDLERS;
break;
case EV_MSC:
if (is_event_supported(code, dev->mscbit, MSC_MAX))
disposition = INPUT_PASS_TO_ALL; break; case EV_LED: //led处理
if (is_event_supported(code, dev->ledbit, LED_MAX) &&
!!test_bit(code, dev->led) != value) {
__change_bit(code, dev->led); //修改input设备的led标志位
disposition = INPUT_PASS_TO_ALL;
}
break;
case EV_SND:
if (is_event_supported(code, dev->sndbit, SND_MAX)) {
if (!!test_bit(code, dev->snd) != !!value)
__change_bit(code, dev->snd);
disposition = INPUT_PASS_TO_ALL;
}
break;
case EV_REP:
if (code <= REP_MAX && value >= 0 && dev->rep[code] != value) {
dev->rep[code] = value;
disposition = INPUT_PASS_TO_ALL;
}
break;
case EV_FF:
if (value >= 0)
disposition = INPUT_PASS_TO_ALL;
break;
case EV_PWR:
disposition = INPUT_PASS_TO_ALL;
break;
}
if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
dev->sync = false;
if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event) //led事件
dev->event(dev, type, code, value); //调用input设备的event方法(usb_kbd_event)
if (disposition & INPUT_PASS_TO_HANDLERS) //led同步事件
input_pass_event(dev, src_handler, type, code, value); //会调用input_handler的event方法(kbd_event)
}  

usb键鼠标驱动分析的更多相关文章

  1. usb键鼠驱动分析【钻】

    本文转载自:http://blog.csdn.net/orz415678659/article/details/9197859 一.鼠标 Linux下的usb鼠标驱动在/drivers/hid/usb ...

  2. Linux下 USB设备驱动分析(原创)

    之前做过STM32的usb HID复合设备,闲来看看linux下USB设备驱动是怎么一回事, 参考资料基于韦东山JZ2440开发板,以下,有错误欢迎指出. 1.准备知识 1.1USB相关概念: USB ...

  3. 八、USB驱动分析

    学习目标:分析USB驱动源码结构. 一.Windows下USB驱动理论问题 1. 当usb设备接入PC时,右下角弹出"发现AAA",并弹出对话框,提示安装驱动程序.没有驱动程序,W ...

  4. Linux usb子系统(一) _写一个usb鼠标驱动

    USB总线是一种典型的热插拔的总线标准,由于其优异的性能几乎成为了当下大小设备中的标配. USB的驱动可以分为3类:SoC的USB控制器的驱动,主机端USB设备的驱动,设备上的USB Gadget驱动 ...

  5. Linux 串口、usb转串口驱动分析(2-2) 【转】

    转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=26807463&id=4186852 Linux 串口.usb转 ...

  6. Linux 串口、usb转串口驱动分析(2-1) 【转】

    转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=26807463&id=4186851 Linux 串口.usb转 ...

  7. linux下usb转串口驱动分析【转】

    转自:http://blog.csdn.net/txxm520/article/details/8934706 首先说一下linux的风格,个人理解 1. linux大小结构体其实是面向对象的方法,( ...

  8. linux驱动基础系列--Linux 串口、usb转串口驱动分析

    前言 主要是想对Linux 串口.usb转串口驱动框架有一个整体的把控,因此会忽略某些细节,同时里面涉及到的一些驱动基础,比如字符设备驱动.平台驱动等也不进行详细说明原理.如果有任何错误地方,请指出, ...

  9. 20.Linux-USB鼠标驱动

    在上一章分析完USB总线驱动程序后, 接下来开始写一个USB驱动: 本节目的: 将USB鼠标的左键当作L按键,将USB鼠标的右键当作S按键,中键当作回车按键 参考/drivers/hid/usbhid ...

随机推荐

  1. 14.3.5 LOCK TABLES and UNLOCK TABLES Syntax

    14.3.5 LOCK TABLES and UNLOCK TABLES Syntax LOCK TABLES tbl_name [[AS] alias] lock_type [, tbl_name ...

  2. 利用COM组件IPicture读取jpg、gif、bmp图片文件数据和显示图片

    1.读取图片数据 函数原型:bool LoadImage(const char *pName, unsigned char *pBitData); 函数功能,读取pName指向的图片文件的位图数据 b ...

  3. wireshark 过滤条件汇总

    原文地址:http://blog.const.net.cn/a/9340.htm 一.针对wireshark最常用的自然是针对IP地址的过滤.其中有几种情况: (1)对源地址为192.168.0.1的 ...

  4. 移动前端之viewport

    在移动设备上进行网页的重构或开发,首先得搞明白的就是移动设备上的viewport了,只有明白了viewport的概念以及弄清楚了跟viewport有关的meta标签的使用,才能更好地让我们的网页适配或 ...

  5. poj 3253 Fence Repair(模拟huffman树 + 优先队列)

    题意:如果要切断一个长度为a的木条需要花费代价a, 问要切出要求的n个木条所需的最小代价. 思路:模拟huffman树,每次选取最小的两个数加入结果,再将这两个数的和加入队列. 注意priority_ ...

  6. Rescue(bfs)

    Rescue Time Limit : 2000/1000ms (Java/Other)   Memory Limit : 65536/32768K (Java/Other) Total Submis ...

  7. JS正则表达式大全【转】

    正则表达式中的特殊字符 字符 含意 \ 做为转意,即通常在"\"后面的字符不按原来意义解释,如/b/匹配字符"b",当b前面加了反斜杆后/\b/,转意为匹配一个 ...

  8. Segment FRAM_DATA must be defined in a segment definition option (-Z, -b or -P)

    1. 网上说这个回答是 协议栈和IAR版本号不一样,这算什么神马问题 2. 网上的解决的方法是改动 options-> link -> config -> 改动里面的连接文件,可是怎 ...

  9. uva 11524 - InCircle (二分法)

    题意:三角形ABC的内切圆把它的三边分别划分成 m1:n1,m2:n2 和 m3:n3 的比例.另外已知内切圆的半径 r ,求三角形ABC 的面积. #include<iostream> ...

  10. C#获取变量名的扩展方法

    // </summary> /// <param name="var_name"></param> /// <param name=&qu ...