一、鼠标

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. optics matlab实现

    关于optics算法的一些基本概念,在此一一忽略. 先求得所有节点的核心距离,用cd矩阵表示: 然后对每个节点进行处理,这个时候不需要考虑该节点是不是核心对象,按顺序取节点,如果该拓展点是核心对象,处 ...

  2. #include <list>

    clear();删除向量中的所有对象 erase(iterator it);删除it所指向的容器对象 insert(iterator it,const T&);向it所指的向量位置前插入一个对 ...

  3. 多进程用户并发处理Demo(C#版)

    这个示例主要演示的是在多进程操作数据库时,如何避免并发重复数据入库的例子. 过多的线程理论不再阐述,网上.书上皆有. 项目采用 Asp.Net Framework 4.5 / Mysql 5.4 数据 ...

  4. taobao面试要点

    第一: 其中有几个点必问,JVMGC深层机制.类加载,包括Tomcat和Jboss的.线程相关的如离线锁,互斥同步,java主线程和工作线程机制,concurrent包下的锁和sync关键字一些区别, ...

  5. 移动端的几款jq插件

    移动手机用户的数量每日都在增长,人们现在都习惯于使用手机来浏览网页,看小说,读新闻.如何确保你的网站对移动用户友好,是目前你需要解决的最重要的问 题之一.这里给大家介绍10款在移动手机上使用的jQue ...

  6. .Net资源文件全球化

    I:本博文代码示例效果图 好久没来写随笔了.不多说先上大饼!  跟着直接上 [代码下载地址] II:ASP.NET 资源文件介绍 在ASP.NET特殊文件夹内有那么两个不太引人注意,他们分别是App_ ...

  7. maven将jar包安装到本地仓库的命令

    进入cmd 执行以下命令: mvn install:install-file -Dfile=E:\sqljdbc4.jar -DgroupId=com.microsoft.sqlserver -Dar ...

  8. SSH框架常会出现的一些错误

    1.jquery datatable插件报JSON数据错误 错误原因:hql语句拼接有问题,前一个字符串与后一个字符串之间缺少空格,导致数据库查询失败. 2.addInput页面中input内容不为空 ...

  9. (转)OpenGL中位图的操作(glReadPixels,glDrawPixels和glCopyPixels应用举例)

    (一)BMP文件格式简单介绍 BMP文件是一种像素文件,它保存了一幅图象中所有的像素.这种文件格式可以保存单色位图.16色或256色索引模式像素图.24位真彩色图象,每种模式种单一像素的大小分别为1/ ...

  10. VB execl文件后台代码,基础语法

    Excel宏与VBA 程序设计实验指导1 实验1 Excel宏与VBA 语法基础 一.实验目的 1.熟练掌握录制宏.执行宏.加载宏的方法: 2.熟练使用Excel VBA编辑环境,掌握VBA的编辑工具 ...