zoukankan      html  css  js  c++  java
  • 【驱动】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设备驱动分析。 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()函数,释放设备驱动结构变量占用的内存。

    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()函数,释放设备驱动结构变量占用的内存。

  • 相关阅读:
    穷举
    菱形
    docker安装cloudera manager,切换cloudera-scm用户报错can not open session
    修改cloudera manager的端口号
    postgresql拓展if、ifnull、group_concat函数
    clion调试postgresql
    Java面向切面原理与实践
    Spring-boot非Mock测试MVC,调试启动tomcat容器
    spring-cloud-feign 使用@RequetParam报错QueryMap parameter must be a Map: class java.lang.String
    linux虚拟机拓展大小
  • 原文地址:https://www.cnblogs.com/sky-heaven/p/5066666.html
Copyright © 2011-2022 走看看