【驱动】USB驱动实例·串口驱动·键盘驱动【转】
转自:http://www.cnblogs.com/lcw/p/3159370.html Preface USB体系支持多种类型的设备。 在 Linux内核,所有的USB设备都使用 usb_driver结构描述。 对于不同类型的 USB设备,内核使用传统的设备驱动模型建立设备驱动描述,然后映射到 USB设备驱动,最终完成特定类型的 USB设备驱动 USB驱动·入门:http://infohacker.blog.51cto.com/6751239/1226257 USB串口驱动 USB串口驱动关键是向内核注册串口设备结构,并且设置串口的操作。 下面是一个典型的USB设备驱动分析。 、驱动初始化函数 usb_serial_init()函数是一个典型的 USB设备驱动初始化函数,定义如下: static int __init usb_serial_init(void)
{
int i;
int result;
usb_serial_tty_driver = alloc_tty_driver(SERIAL_TTY_MINORS); //申请 tty设备驱动描述
if (!usb_serial_tty_driver)
return -ENOMEM;
/* Initialize our global data */
for (i = ; i < SERIAL_TTY_MINORS; ++i) {
serial_table[i] = NULL;
}
result = bus_register(&usb_serial_bus_type); //注册总线
if (result) {
err("%s - registering bus driver failed", __FUNCTION__);
goto exit_bus;
}
usb_serial_tty_driver->owner = THIS_MODULE; //初始化串口驱动描述
usb_serial_tty_driver->driver_name = "usbserial"; //串口驱动名称
usb_serial_tty_driver->name = "ttyUSB"; //设备文件系统存放路径
usb_serial_tty_driver->major = SERIAL_TTY_MAJOR; //串口设备主设备号
usb_serial_tty_driver->minor_start = ; //串口设备从设备号起始 ID
usb_serial_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; //设备类型
usb_serial_tty_driver->subtype = SERIAL_TYPE_NORMAL; //设备子类型
usb_serial_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; //设备初始化标志
usb_serial_tty_driver->init_termios = tty_std_termios; //串口设备描述
usb_serial_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; //串口设备初始化参数
tty_set_operations(usb_serial_tty_driver, &serial_ops); //串口设备操作函数
result = tty_register_driver(usb_serial_tty_driver); //注册串口驱动
if (result) {
err("%s - tty_register_driver failed", __FUNCTION__);
goto exit_reg_driver;
}
/* register the USB driver */
result = usb_register(&usb_serial_driver); //注册 USB驱动
if (result < ) {
err("%s - usb_register failed", __FUNCTION__);
goto exit_tty;
}
/* register the generic driver, if we should */
result = usb_serial_generic_register(debug);
if (result < ) {
err("%s - registering generic driver failed", __FUNCTION__);
goto exit_generic;
}
info(DRIVER_DESC);
return result;
exit_generic:
usb_deregister(&usb_serial_driver); //注销串口设备
exit_tty:
tty_unregister_driver(usb_serial_tty_driver); //注销 USB串口设备
exit_reg_driver:
bus_unregister(&usb_serial_bus_type); //注销总线
exit_bus:
err ("%s - returning with error %d", __FUNCTION__, result);
put_tty_driver(usb_serial_tty_driver);
return result;
}
函数首先调用 alloc_tty_driver()函数分配一个串口驱动描述符;然后设置串口驱动的属性,包括驱动的主从设备号、设备类型、串口初始化参数等;串口驱动描述符设置完毕后,调用 usb_register()函数注册 USB串口设备。 、驱动释放函数 驱动释放函数用来释放 USB串口设备驱动申请的内核资源,函数定义如下: static int __init usb_serial_init(void)
{
int i;
int result;
usb_serial_tty_driver = alloc_tty_driver(SERIAL_TTY_MINORS); //申请 tty设备驱动描述
if (!usb_serial_tty_driver)
return -ENOMEM;
/* Initialize our global data */
for (i = ; i < SERIAL_TTY_MINORS; ++i) {
serial_table[i] = NULL;
}
result = bus_register(&usb_serial_bus_type); //注册总线
if (result) {
err("%s - registering bus driver failed", __FUNCTION__);
goto exit_bus;
}
usb_serial_tty_driver->owner = THIS_MODULE; //初始化串口驱动描述
usb_serial_tty_driver->driver_name = "usbserial"; //串口驱动名称
usb_serial_tty_driver->name = "ttyUSB"; //设备文件系统存放路径
usb_serial_tty_driver->major = SERIAL_TTY_MAJOR; //串口设备主设备号
usb_serial_tty_driver->minor_start = ; //串口设备从设备号起始 ID
usb_serial_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; //设备类型
usb_serial_tty_driver->subtype = SERIAL_TYPE_NORMAL; //设备子类型
usb_serial_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; //设备初始化标志
usb_serial_tty_driver->init_termios = tty_std_termios; //串口设备描述
usb_serial_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; //串口设备初始化参数
tty_set_operations(usb_serial_tty_driver, &serial_ops); //串口设备操作函数
result = tty_register_driver(usb_serial_tty_driver); //注册串口驱动
if (result) {
err("%s - tty_register_driver failed", __FUNCTION__);
goto exit_reg_driver;
}
/* register the USB driver */
result = usb_register(&usb_serial_driver); //注册 USB驱动
if (result < ) {
err("%s - usb_register failed", __FUNCTION__);
goto exit_tty;
}
/* register the generic driver, if we should */
result = usb_serial_generic_register(debug);
if (result < ) {
err("%s - registering generic driver failed", __FUNCTION__);
goto exit_generic;
}
info(DRIVER_DESC);
return result;
exit_generic:
usb_deregister(&usb_serial_driver); //注销串口设备
exit_tty:
tty_unregister_driver(usb_serial_tty_driver); //注销 USB串口设备
exit_reg_driver:
bus_unregister(&usb_serial_bus_type); //注销总线
exit_bus:
err ("%s - returning with error %d", __FUNCTION__, result);
put_tty_driver(usb_serial_tty_driver);
return result;
} 、串口操作函数 USB串口设备驱动使用了一个 tty_operations类型的结构,该结构包含了串口的所有操作。定义如下: static struct tty_operations serial_ops = {
.open = serial_open, //打开串口
.close = serial_close, //关闭串口
.write = serial_write, //串口写操作
.write_room = serial_write_room,
.ioctl = serial_ioctl, // I/O控制操作
.set_termios = serial_set_termios, //设置串口参数
.throttle = serial_throttle,
.unthrottle = serial_unthrottle,
.break_ctl = serial_break, // break信号处理
.chars_in_buffer = serial_chars_in_buffer, //缓冲处理
.read_proc = serial_read_proc, //串口读操作
.tiocmget = serial_tiocmget, //获取 I/O控制参数
.tiocmset = serial_tiocmset, //设置 I/O控制参数
}; serial_ops结构变量设置的所有串口操作函数,均使用内核 USB核心提供的标准函数,定义在/drivers/usb/serial/generic.c文件中 USB键盘驱动 USB键盘驱动与串口驱动结构类似,不同是的使用USB设备核心提供的usb_keyboard_driver结构作为设备核心结构。下面是 USB键盘驱动的重点部分。 、驱动初始和注销 USB键盘驱动初始化和注销函数定义如下: static int __init usb_kbd_init(void)
{
int result = usb_register(&usb_kbd_driver); //注册 USB设备驱动
if (result == )
info(DRIVER_VERSION ":" DRIVER_DESC);
return result;
}
static void __exit usb_kbd_exit(void)
{
usb_deregister(&usb_kbd_driver); //注销 USB设备驱动
} usb_kbd_init()函数在驱动加载的时候调用,该函数使用 usb_register()函数向内核注册一个 USB设备驱动;usb_kbd_exit()函数在卸载驱动程序的时候调用,该函数使用 usb_deregister()函数注销 USB设备。初始化和注销函数使用了 usb_keyboard结构变量,用于描述 USB键盘驱动程序,定义如下: //usb_driver结构体
static struct usb_driver usb_keyboard =
{
.name = "usbkbd", //驱动名称
.probe = usb_kbd_probe, //检测设备函数
.disconnect = usb_kbd_disconnect, //断开连接函数
.id_table = usb_kbd_id_table, //设备 ID
}; 从 usb_keyboard结构定义看出,usb_kbd_probe()函数是设备检测函数; usb_kbd_disconnect()函数是断开设备连接。 、设备检测函数 设备检测函数在插入 USB设备的时候被USB文件系统调用,负责检测设备类型是否与驱动相符。如果设备类型与驱动匹配,则向 USB核心注册设备。 函数定义如下: static int usb_kbd_probe(struct usb_interface *iface,
const struct usb_device_id *id)
{
struct usb_device *dev = interface_to_usbdev(iface);
struct usb_host_interface *interface;
struct usb_endpoint_descriptor *endpoint;
struct usb_kbd *kbd;
struct input_dev *input_dev;
int i, pipe, maxp;
interface = iface->cur_altsetting;
if (interface->desc.bNumEndpoints != ) //检查设备是否符合
return -ENODEV;
endpoint = &interface->endpoint[].desc;
if (!(endpoint->bEndpointAddress & USB_DIR_IN))
return -ENODEV;
if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT)
return -ENODEV;
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); //创建端点的管道
maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
kbd = kzalloc(sizeof(struct usb_kbd), GFP_KERNEL);
input_dev = input_allocate_device(); //分配 input_dev结构体
if (!kbd || !input_dev) //分配设备结构占用的内存
goto fail1;
if (usb_kbd_alloc_mem(dev, kbd))
goto fail2;
kbd->usbdev = dev;
kbd->dev = input_dev;
if (dev->manufacturer) //检查制造商名称
strlcpy(kbd->name, dev->manufacturer, sizeof(kbd->name));
if (dev->product) { //检查产品名称
if (dev->manufacturer)
strlcat(kbd->name, " ", sizeof(kbd->name));
strlcat(kbd->name, dev->product, sizeof(kbd->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));
usb_make_path(dev, kbd->phys, sizeof(kbd->phys));
strlcpy(kbd->phys, "/input0", sizeof(kbd->phys));
//初始化输入设备
input_dev->name = kbd->name; //输入设备名称
input_dev->phys = kbd->phys; //输入设备物理地址
usb_to_input_id(dev, &input_dev->id); //输入设备 ID
input_dev->cdev.dev = &iface->dev;
input_dev->private = kbd;
input_dev->evbit[] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_REP);
input_dev->ledbit[] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL) | BIT(LED_COMPOSE) | BIT(LED_KANA);
for (i = ; i < ; i++)
set_bit(usb_kbd_keycode[i], input_dev->keybit);
clear_bit(, input_dev->keybit);
input_dev->event = usb_kbd_event;
input_dev->open = usb_kbd_open;
input_dev->close = usb_kbd_close;
//初始化中断 urb
usb_fill_int_urb(kbd->irq, dev, pipe,
kbd->new, (maxp > ? : maxp),
usb_kbd_irq, kbd, endpoint->bInterval);
kbd->irq->transfer_dma = kbd->new_dma;
kbd->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
kbd->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
kbd->cr->bRequest = 0x09;
kbd->cr->wValue = cpu_to_le16(0x200);
kbd->cr->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);
kbd->cr->wLength = cpu_to_le16();
//初始化中断 urb
usb_fill_control_urb(kbd->led, dev, usb_sndctrlpipe(dev, ),
(void *) kbd->cr, kbd->leds, ,
usb_kbd_led, kbd);
kbd->led->setup_dma = kbd->cr_dma;
kbd->led->transfer_dma = kbd->leds_dma;
kbd->led->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP);
input_register_device(kbd->dev); //注册输入设备
usb_set_intfdata(iface, kbd); //设置接口私有数据
return ;
fail2: usb_kbd_free_mem(dev, kbd);
fail1: input_free_device(input_dev);
kfree(kbd);
return -ENOMEM;
} 函数一开始检测设备类型,如果与驱动程序匹配,则创建 USB设备端点,分配设备驱动结构占用的内存。分配好设备驱动使用的结构后,申请一个键盘设备驱动节点,然后设置键盘驱动,最后设置 USB设备的中断 URB和控制 URB,供 USB设备核心使用。 、设备断开连接函数 在设备断开连接的时候,USB文件系统会调用 usb_kbd_disconnect()函数,释放设备占用的资源。 函数定义如下: static void usb_kbd_disconnect(struct usb_interface *intf)
{
struct usb_kbd *kbd = usb_get_intfdata (intf);
usb_set_intfdata(intf, NULL); //设置接口私有数据为 NULL
if (kbd) {
usb_kill_urb(kbd->irq); //终止 URB
input_unregister_device(kbd->dev); //注销输入设备
usb_kbd_free_mem(interface_to_usbdev(intf), kbd); //释放设备驱动占用的内存
kfree(kbd);
}
} usb_kbd_disconnect()函数释放 USB键盘设备占用的 URB资源,然后注销设备,最后调用usb_kbd_free_mem()函数,释放设备驱动结构变量占用的内存。
Preface
USB体系支持多种类型的设备。
在 Linux内核,所有的USB设备都使用 usb_driver结构描述。
对于不同类型的 USB设备,内核使用传统的设备驱动模型建立设备驱动描述,然后映射到 USB设备驱动,最终完成特定类型的 USB设备驱动
USB驱动·入门:http://infohacker.blog.51cto.com/6751239/1226257
USB串口驱动
USB串口驱动关键是向内核注册串口设备结构,并且设置串口的操作。
下面是一个典型的USB设备驱动分析。
1、驱动初始化函数
usb_serial_init()函数是一个典型的 USB设备驱动初始化函数,定义如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
static int __init usb_serial_init( void ) { int i; int result; usb_serial_tty_driver = alloc_tty_driver(SERIAL_TTY_MINORS); //申请 tty设备驱动描述 if (!usb_serial_tty_driver) return -ENOMEM; /* Initialize our global data */ for (i = 0; i < SERIAL_TTY_MINORS; ++i) { serial_table[i] = NULL; } result = bus_register(&usb_serial_bus_type); //注册总线 if (result) { err( "%s - registering bus driver failed" , __FUNCTION__); goto exit_bus; } usb_serial_tty_driver->owner = THIS_MODULE; //初始化串口驱动描述 usb_serial_tty_driver->driver_name = "usbserial" ; //串口驱动名称 usb_serial_tty_driver->name = "ttyUSB" ; //设备文件系统存放路径 usb_serial_tty_driver->major = SERIAL_TTY_MAJOR; //串口设备主设备号 usb_serial_tty_driver->minor_start = 0; //串口设备从设备号起始 ID usb_serial_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; //设备类型 usb_serial_tty_driver->subtype = SERIAL_TYPE_NORMAL; //设备子类型 usb_serial_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; //设备初始化标志 usb_serial_tty_driver->init_termios = tty_std_termios; //串口设备描述 usb_serial_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; //串口设备初始化参数 tty_set_operations(usb_serial_tty_driver, &serial_ops); //串口设备操作函数 result = tty_register_driver(usb_serial_tty_driver); //注册串口驱动 if (result) { err( "%s - tty_register_driver failed" , __FUNCTION__); goto exit_reg_driver; } /* register the USB driver */ result = usb_register(&usb_serial_driver); //注册 USB驱动 if (result < 0) { err( "%s - usb_register failed" , __FUNCTION__); goto exit_tty; } /* register the generic driver, if we should */ result = usb_serial_generic_register(debug); if (result < 0) { err( "%s - registering generic driver failed" , __FUNCTION__); goto exit_generic; } info(DRIVER_DESC); return result; exit_generic: usb_deregister(&usb_serial_driver); //注销串口设备 exit_tty: tty_unregister_driver(usb_serial_tty_driver); //注销 USB串口设备 exit_reg_driver: bus_unregister(&usb_serial_bus_type); //注销总线 exit_bus: err ( "%s - returning with error %d" , __FUNCTION__, result); put_tty_driver(usb_serial_tty_driver); return result; } |
函数首先调用 alloc_tty_driver()函数分配一个串口驱动描述符;然后设置串口驱动的属性,包括驱动的主从设备号、设备类型、串口初始化参数等;串口驱动描述符设置完毕后,调用 usb_register()函数注册 USB串口设备。
2、驱动释放函数
驱动释放函数用来释放 USB串口设备驱动申请的内核资源,函数定义如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
static int __init usb_serial_init( void ) { int i; int result; usb_serial_tty_driver = alloc_tty_driver(SERIAL_TTY_MINORS); //申请 tty设备驱动描述 if (!usb_serial_tty_driver) return -ENOMEM; /* Initialize our global data */ for (i = 0; i < SERIAL_TTY_MINORS; ++i) { serial_table[i] = NULL; } result = bus_register(&usb_serial_bus_type); //注册总线 if (result) { err( "%s - registering bus driver failed" , __FUNCTION__); goto exit_bus; } usb_serial_tty_driver->owner = THIS_MODULE; //初始化串口驱动描述 usb_serial_tty_driver->driver_name = "usbserial" ; //串口驱动名称 usb_serial_tty_driver->name = "ttyUSB" ; //设备文件系统存放路径 usb_serial_tty_driver->major = SERIAL_TTY_MAJOR; //串口设备主设备号 usb_serial_tty_driver->minor_start = 0; //串口设备从设备号起始 ID usb_serial_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; //设备类型 usb_serial_tty_driver->subtype = SERIAL_TYPE_NORMAL; //设备子类型 usb_serial_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; //设备初始化标志 usb_serial_tty_driver->init_termios = tty_std_termios; //串口设备描述 usb_serial_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; //串口设备初始化参数 tty_set_operations(usb_serial_tty_driver, &serial_ops); //串口设备操作函数 result = tty_register_driver(usb_serial_tty_driver); //注册串口驱动 if (result) { err( "%s - tty_register_driver failed" , __FUNCTION__); goto exit_reg_driver; } /* register the USB driver */ result = usb_register(&usb_serial_driver); //注册 USB驱动 if (result < 0) { err( "%s - usb_register failed" , __FUNCTION__); goto exit_tty; } /* register the generic driver, if we should */ result = usb_serial_generic_register(debug); if (result < 0) { err( "%s - registering generic driver failed" , __FUNCTION__); goto exit_generic; } info(DRIVER_DESC); return result; exit_generic: usb_deregister(&usb_serial_driver); //注销串口设备 exit_tty: tty_unregister_driver(usb_serial_tty_driver); //注销 USB串口设备 exit_reg_driver: bus_unregister(&usb_serial_bus_type); //注销总线 exit_bus: err ( "%s - returning with error %d" , __FUNCTION__, result); put_tty_driver(usb_serial_tty_driver); return result; } |
3、串口操作函数
USB串口设备驱动使用了一个 tty_operations类型的结构,该结构包含了串口的所有操作。定义如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
static struct tty_operations serial_ops = { .open = serial_open, //打开串口 .close = serial_close, //关闭串口 .write = serial_write, //串口写操作 .write_room = serial_write_room, .ioctl = serial_ioctl, // I/O控制操作 .set_termios = serial_set_termios, //设置串口参数 .throttle = serial_throttle, .unthrottle = serial_unthrottle, .break_ctl = serial_break, // break信号处理 .chars_in_buffer = serial_chars_in_buffer, //缓冲处理 .read_proc = serial_read_proc, //串口读操作 .tiocmget = serial_tiocmget, //获取 I/O控制参数 .tiocmset = serial_tiocmset, //设置 I/O控制参数 }; |
serial_ops结构变量设置的所有串口操作函数,均使用内核 USB核心提供的标准函数,定义在/drivers/usb/serial/generic.c文件中
USB键盘驱动
USB键盘驱动与串口驱动结构类似,不同是的使用USB设备核心提供的usb_keyboard_driver结构作为设备核心结构。下面是 USB键盘驱动的重点部分。
1、驱动初始和注销
USB键盘驱动初始化和注销函数定义如下:
1
2
3
4
5
6
7
8
9
10
11
|
static int __init usb_kbd_init( void ) { int result = usb_register(&usb_kbd_driver); //注册 USB设备驱动 if (result == 0) info(DRIVER_VERSION ":" DRIVER_DESC); return result; } static void __exit usb_kbd_exit( void ) { usb_deregister(&usb_kbd_driver); //注销 USB设备驱动 } |
usb_kbd_init()函数在驱动加载的时候调用,该函数使用 usb_register()函数向内核注册一个 USB设备驱动;usb_kbd_exit()函数在卸载驱动程序的时候调用,该函数使用 usb_deregister()函数注销 USB设备。初始化和注销函数使用了 usb_keyboard结构变量,用于描述 USB键盘驱动程序,定义如下:
1
2
3
4
5
6
7
8
|
//usb_driver结构体 static struct usb_driver usb_keyboard = { .name = "usbkbd" , //驱动名称 .probe = usb_kbd_probe, //检测设备函数 .disconnect = usb_kbd_disconnect, //断开连接函数 .id_table = usb_kbd_id_table, //设备 ID }; |
从 usb_keyboard结构定义看出,usb_kbd_probe()函数是设备检测函数;
usb_kbd_disconnect()函数是断开设备连接。
2、设备检测函数
设备检测函数在插入 USB设备的时候被USB文件系统调用,负责检测设备类型是否与驱动相符。如果设备类型与驱动匹配,则向 USB核心注册设备。
函数定义如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
|
static int usb_kbd_probe( struct usb_interface *iface, const struct usb_device_id *id) { struct usb_device *dev = interface_to_usbdev(iface); struct usb_host_interface *interface; struct usb_endpoint_descriptor *endpoint; struct usb_kbd *kbd; struct input_dev *input_dev; int i, pipe, maxp; interface = iface->cur_altsetting; if (interface->desc.bNumEndpoints != 1) //检查设备是否符合 return -ENODEV; endpoint = &interface->endpoint[0].desc; if (!(endpoint->bEndpointAddress & USB_DIR_IN)) return -ENODEV; if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT) return -ENODEV; pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); //创建端点的管道 maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); kbd = kzalloc( sizeof ( struct usb_kbd), GFP_KERNEL); input_dev = input_allocate_device(); //分配 input_dev结构体 if (!kbd || !input_dev) //分配设备结构占用的内存 goto fail1; if (usb_kbd_alloc_mem(dev, kbd)) goto fail2; kbd->usbdev = dev; kbd->dev = input_dev; if (dev->manufacturer) //检查制造商名称 strlcpy(kbd->name, dev->manufacturer, sizeof (kbd->name)); if (dev->product) { //检查产品名称 if (dev->manufacturer) strlcat(kbd->name, " " , sizeof (kbd->name)); strlcat(kbd->name, dev->product, sizeof (kbd->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)); usb_make_path(dev, kbd->phys, sizeof (kbd->phys)); strlcpy(kbd->phys, "/input0" , sizeof (kbd->phys)); //初始化输入设备 input_dev->name = kbd->name; //输入设备名称 input_dev->phys = kbd->phys; //输入设备物理地址 usb_to_input_id(dev, &input_dev->id); //输入设备 ID input_dev->cdev.dev = &iface->dev; input_dev-> private = kbd; input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_REP); input_dev->ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL) | BIT(LED_COMPOSE) | BIT(LED_KANA); for (i = 0; i < 255; i++) set_bit(usb_kbd_keycode[i], input_dev->keybit); clear_bit(0, input_dev->keybit); input_dev->event = usb_kbd_event; input_dev->open = usb_kbd_open; input_dev->close = usb_kbd_close; //初始化中断 urb usb_fill_int_urb(kbd->irq, dev, pipe, kbd-> new , (maxp > 8 ? 8 : maxp), usb_kbd_irq, kbd, endpoint->bInterval); kbd->irq->transfer_dma = kbd->new_dma; kbd->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; kbd->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE; kbd->cr->bRequest = 0x09; kbd->cr->wValue = cpu_to_le16(0x200); kbd->cr->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber); kbd->cr->wLength = cpu_to_le16(1); //初始化中断 urb usb_fill_control_urb(kbd->led, dev, usb_sndctrlpipe(dev, 0), ( void *) kbd->cr, kbd->leds, 1, usb_kbd_led, kbd); kbd->led->setup_dma = kbd->cr_dma; kbd->led->transfer_dma = kbd->leds_dma; kbd->led->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP); input_register_device(kbd->dev); //注册输入设备 usb_set_intfdata(iface, kbd); //设置接口私有数据 return 0; fail2: usb_kbd_free_mem(dev, kbd); fail1: input_free_device(input_dev); kfree(kbd); return -ENOMEM; } |
函数一开始检测设备类型,如果与驱动程序匹配,则创建 USB设备端点,分配设备驱动结构占用的内存。分配好设备驱动使用的结构后,申请一个键盘设备驱动节点,然后设置键盘驱动,最后设置 USB设备的中断 URB和控制 URB,供 USB设备核心使用。
3、设备断开连接函数
在设备断开连接的时候,USB文件系统会调用 usb_kbd_disconnect()函数,释放设备占用的资源。
函数定义如下:
1
2
3
4
5
6
7
8
9
10
11
|
static void usb_kbd_disconnect( struct usb_interface *intf) { struct usb_kbd *kbd = usb_get_intfdata (intf); usb_set_intfdata(intf, NULL); //设置接口私有数据为 NULL if (kbd) { usb_kill_urb(kbd->irq); //终止 URB input_unregister_device(kbd->dev); //注销输入设备 usb_kbd_free_mem(interface_to_usbdev(intf), kbd); //释放设备驱动占用的内存 kfree(kbd); } } |
usb_kbd_disconnect()函数释放 USB键盘设备占用的 URB资源,然后注销设备,最后调用usb_kbd_free_mem()函数,释放设备驱动结构变量占用的内存。
【驱动】USB驱动实例·串口驱动·键盘驱动【转】的更多相关文章
- Linux下的硬件驱动——USB设备(转载)
usb_bulk_msg函数 当对usb设备进行一次读或者写时,usb_bulk_msg 函数是非常有用的; 然而, 当你需要连续地对设备进行读/写时,建议你建立一个自己的urbs,同时将urbs 提 ...
- ARM-Linux S5PV210 UART驱动(5)----串口的open操作(tty_open、uart_open)
串口驱动初始化后,串口作为字符驱动也已经注册到系统了,/dev目录下也有设备文件节点了. 那接下来uart的操作是如何进行的呢? 操作硬件之前都是要先open设备,先来分析下这里的open函数具体做了 ...
- 【驱动】USB驱动实例·串口驱动·键盘驱动
Preface USB体系支持多种类型的设备. 在 Linux内核,所有的USB设备都使用 usb_driver结构描述. 对于不同类型的 USB设备,内核使用传统的设备驱动模型建立设备驱动 ...
- linux驱动基础系列--Linux 串口、usb转串口驱动分析
前言 主要是想对Linux 串口.usb转串口驱动框架有一个整体的把控,因此会忽略某些细节,同时里面涉及到的一些驱动基础,比如字符设备驱动.平台驱动等也不进行详细说明原理.如果有任何错误地方,请指出, ...
- 8.2 USB键盘驱动编写和测试
目标:根据USB驱动分析和上节的USB鼠标驱动,编写键盘驱动,并测试. 一.原理分析 1. 首先通过打印usb_buf[i]中的8字节数据,看一下按键按下之后会接收到什么. 1)通过按完所有键盘按键打 ...
- USB键盘驱动分析
简介 本文介绍USB驱动程序编写的流程,分析一个键盘的USB程序,基于linux-2.6.39 USB驱动概要 分层 主机层面的USB驱动的整体架构可以分成4层,自顶到下依次是 1.USB设备驱动:本 ...
- 《连载 | 物联网框架ServerSuperIO教程》- 14.配制工具介绍,以及设备驱动、视图驱动、服务实例的挂载
注:ServerSuperIO二次开发套件授权码申请---截止到:2016-12-09 1.C#跨平台物联网通讯框架ServerSuperIO(SSIO)介绍 <连载 | 物联网框架Server ...
- 《连载 | 物联网框架ServerSuperIO教程》-4.如开发一套设备驱动,同时支持串口和网络通讯。附:将来支持Windows 10 IOT
1.C#跨平台物联网通讯框架ServerSuperIO(SSIO)介绍 <连载 | 物联网框架ServerSuperIO教程>1.4种通讯模式机制. <连载 | 物联网框架Serve ...
- ARM-Linux S5PV210 UART驱动(3)----串口核心层、关键结构体、接口关系
尽管一个特定的UART设备驱动完全可以按照tty驱动的设计方法来设计,即定义tty_driver并实现tty_operations其中的成员函数,但是Linux已经在文件serial_core.c中实 ...
随机推荐
- 【BZOJ4991】我也不知道题目名字是什么(线段树)
[BZOJ4991]我也不知道题目名字是什么(线段树) 题面 BZOJ 题解 对于线段树维护的区间维护以下东西: 区间左(右)端开始(结束)的最长(短)子串的长度 左端右端的值,以及当前区间内的答案 ...
- 字典树(trie树)的指针简单实现pascal
问题1:给你一个单词集合,支持添加,删除,询问某个单词出现次数. ; maxn=; type dictree=^rec; rec=record next:array[..maxword]of dict ...
- BZOJ 1391 [Ceoi2008]order
1391: [Ceoi2008]order Description 有N个工作,M种机器,每种机器你可以租或者买过来. 每个工作包括若干道工序,每道工序需要某种机器来完成,你可以通过购买或租用机器来完 ...
- 2018-2019 ACM-ICPC ECfinal I. Misunderstood … Missing
题目链接 首先有两个个属性值:\(A,D\),其中\(A\)表示目前攻击力,\(D\)表示每回合攻击的增量. 现在一共有\(n\)个回合,每一回合\(i\),可以有以下三种操作: 1.进行攻击,造成\ ...
- php 中的错误处理机制
php 里有一套错误处理机制,可以使用 set_error_handler 接管 php 错误处理,也可以使用 trigger_error 函数主动抛出一个错误. set_error_handler( ...
- [python]python安装包错误
“UnicodeDecodeError: ‘ascii’ codec can’t decode : ordinal not )” 在windows XP上 解决方法: Solution: ====== ...
- python学习(20) 网络编程
原文链接:http://www.limerence2017.com/2018/01/02/python20/ python 网络编程和基本的C语言编程一样,效率不是很高,如果为了封装通信库建议采用C/ ...
- Laravel 限流中间件 throttle 简析
1. 在Laravel 中配置 在 app\Http\Kernel.php 中,默认添加到中间件组 api 下,1分钟60次. 2. 限流原理 获取唯一请求来源,进行唯一标识(key) 获取该请求请求 ...
- SVN报错:Node remains in conflict显示冲突的解决办法
如果是提示文件冲突: svn revert --depth=infinity 有冲突的文件名 如果提示目录有冲突: svn revert --depth=infinity 目录名 搞定.
- Service Fabric基本概念:Partition/Replicas示例
作者:张鼎松 (Dingsong Zhang) @ Microsoft 在上一节的结尾简单介绍了Service Fabric中分区Partitions和复制replicas的概念,本节主要以示例的形式 ...