溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

【驅(qū)動(dòng)】USB驅(qū)動(dòng)實(shí)例·串口驅(qū)動(dòng)·鍵盤驅(qū)動(dòng)

發(fā)布時(shí)間:2020-06-24 13:00:30 來源:網(wǎng)絡(luò) 閱讀:966 作者:infohacker 欄目:移動(dòng)開發(fā)

Preface


   USB體系支持多種類型的設(shè)備。

   在 Linux內(nèi)核,所有的USB設(shè)備都使用 usb_driver結(jié)構(gòu)描述。

   對于不同類型的 USB設(shè)備,內(nèi)核使用傳統(tǒng)的設(shè)備驅(qū)動(dòng)模型建立設(shè)備驅(qū)動(dòng)描述,然后映射到 USB設(shè)備驅(qū)動(dòng),最終完成特定類型的 USB設(shè)備驅(qū)動(dòng)

   USB驅(qū)動(dòng)·入門http://infohacker.blog.51cto.com/6751239/1226257



USB串口驅(qū)動(dòng)


   USB串口驅(qū)動(dòng)關(guān)鍵是向內(nèi)核注冊串口設(shè)備結(jié)構(gòu),并且設(shè)置串口的操作。

   下面是一個(gè)典型的USB設(shè)備驅(qū)動(dòng)分析。



1、驅(qū)動(dòng)初始化函數(shù)


   usb_serial_init()函數(shù)是一個(gè)典型的 USB設(shè)備驅(qū)動(dòng)初始化函數(shù),定義如下:

static int __init usb_serial_init(void)
{
    int i;
    int result;
    usb_serial_tty_driver = alloc_tty_driver(SERIAL_TTY_MINORS);    //申請 tty設(shè)備驅(qū)動(dòng)描述
    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;  //初始化串口驅(qū)動(dòng)描述
    usb_serial_tty_driver->driver_name = "usbserial";    //串口驅(qū)動(dòng)名稱
    usb_serial_tty_driver->name =    "ttyUSB";   //設(shè)備文件系統(tǒng)存放路徑
    usb_serial_tty_driver->major = SERIAL_TTY_MAJOR; //串口設(shè)備主設(shè)備號(hào)
    usb_serial_tty_driver->minor_start = 0;  //串口設(shè)備從設(shè)備號(hào)起始 ID
    usb_serial_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;    //設(shè)備類型
    usb_serial_tty_driver->subtype = SERIAL_TYPE_NORMAL; //設(shè)備子類型
    usb_serial_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; //設(shè)備初始化標(biāo)志
    usb_serial_tty_driver->init_termios = tty_std_termios;   //串口設(shè)備描述
    usb_serial_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;  //串口設(shè)備初始化參數(shù)
    tty_set_operations(usb_serial_tty_driver, &serial_ops); //串口設(shè)備操作函數(shù)
    result = tty_register_driver(usb_serial_tty_driver);    //注冊串口驅(qū)動(dòng)
    if (result) {
        err("%s - tty_register_driver failed", __FUNCTION__);
        goto exit_reg_driver;
    }
    /* register the USB driver */
    result = usb_register(&usb_serial_driver);  //注冊 USB驅(qū)動(dòng)
    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); //注銷串口設(shè)備
exit_tty:
    tty_unregister_driver(usb_serial_tty_driver);   //注銷 USB串口設(shè)備
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;
}

   函數(shù)首先調(diào)用 alloc_tty_driver()函數(shù)分配一個(gè)串口驅(qū)動(dòng)描述符;然后設(shè)置串口驅(qū)動(dòng)的屬性,包括驅(qū)動(dòng)的主從設(shè)備號(hào)、設(shè)備類型、串口初始化參數(shù)等;串口驅(qū)動(dòng)描述符設(shè)置完畢后,調(diào)用 usb_register()函數(shù)注冊 USB串口設(shè)備。



2、驅(qū)動(dòng)釋放函數(shù)


   驅(qū)動(dòng)釋放函數(shù)用來釋放 USB串口設(shè)備驅(qū)動(dòng)申請的內(nèi)核資源,函數(shù)定義如下:

static int __init usb_serial_init(void)
{
    int i;
    int result;
    usb_serial_tty_driver = alloc_tty_driver(SERIAL_TTY_MINORS);    //申請 tty設(shè)備驅(qū)動(dòng)描述
    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;  //初始化串口驅(qū)動(dòng)描述
    usb_serial_tty_driver->driver_name = "usbserial";    //串口驅(qū)動(dòng)名稱
    usb_serial_tty_driver->name =    "ttyUSB";   //設(shè)備文件系統(tǒng)存放路徑
    usb_serial_tty_driver->major = SERIAL_TTY_MAJOR; //串口設(shè)備主設(shè)備號(hào)
    usb_serial_tty_driver->minor_start = 0;  //串口設(shè)備從設(shè)備號(hào)起始 ID
    usb_serial_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;    //設(shè)備類型
    usb_serial_tty_driver->subtype = SERIAL_TYPE_NORMAL; //設(shè)備子類型
    usb_serial_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; //設(shè)備初始化標(biāo)志
    usb_serial_tty_driver->init_termios = tty_std_termios;   //串口設(shè)備描述
    usb_serial_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;  //串口設(shè)備初始化參數(shù)
    tty_set_operations(usb_serial_tty_driver, &serial_ops); //串口設(shè)備操作函數(shù)
    result = tty_register_driver(usb_serial_tty_driver);    //注冊串口驅(qū)動(dòng)
    if (result) {
        err("%s - tty_register_driver failed", __FUNCTION__);
        goto exit_reg_driver;
    }
    /* register the USB driver */
    result = usb_register(&usb_serial_driver);  //注冊 USB驅(qū)動(dòng)
    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); //注銷串口設(shè)備
exit_tty:
    tty_unregister_driver(usb_serial_tty_driver);   //注銷 USB串口設(shè)備
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、串口操作函數(shù)


   USB串口設(shè)備驅(qū)動(dòng)使用了一個(gè) tty_operations類型的結(jié)構(gòu),該結(jié)構(gòu)包含了串口的所有操作。定義如下:


static struct tty_operations serial_ops = {
    .open =         serial_open,    //打開串口
    .close =        serial_close,   //關(guān)閉串口
    .write =        serial_write,   //串口寫操作
    .write_room =       serial_write_room,
    .ioctl =        serial_ioctl,   // I/O控制操作
    .set_termios =      serial_set_termios, //設(shè)置串口參數(shù)
    .throttle =     serial_throttle,
    .unthrottle =       serial_unthrottle,
    .break_ctl =        serial_break,   // break信號(hào)處理
    .chars_in_buffer =  serial_chars_in_buffer, //緩沖處理
    .read_proc =        serial_read_proc,   //串口讀操作
    .tiocmget =     serial_tiocmget,    //獲取 I/O控制參數(shù)
    .tiocmset =     serial_tiocmset,    //設(shè)置 I/O控制參數(shù)
};

   serial_ops結(jié)構(gòu)變量設(shè)置的所有串口操作函數(shù),均使用內(nèi)核 USB核心提供的標(biāo)準(zhǔn)函數(shù),定義在/drivers/usb/serial/generic.c文件中





USB鍵盤驅(qū)動(dòng)


   USB鍵盤驅(qū)動(dòng)與串口驅(qū)動(dòng)結(jié)構(gòu)類似,不同是的使用USB設(shè)備核心提供的usb_keyboard_driver結(jié)構(gòu)作為設(shè)備核心結(jié)構(gòu)。下面是 USB鍵盤驅(qū)動(dòng)的重點(diǎn)部分。



1、驅(qū)動(dòng)初始和注銷


   USB鍵盤驅(qū)動(dòng)初始化和注銷函數(shù)定義如下:

static int __init usb_kbd_init(void)
{
    int result = usb_register(&usb_kbd_driver); //注冊 USB設(shè)備驅(qū)動(dòng)
    if (result == 0)
        info(DRIVER_VERSION ":" DRIVER_DESC);
    return result;
}
static void __exit usb_kbd_exit(void)
{
    usb_deregister(&usb_kbd_driver);    //注銷 USB設(shè)備驅(qū)動(dòng)
}


   usb_kbd_init()函數(shù)在驅(qū)動(dòng)加載的時(shí)候調(diào)用,該函數(shù)使用 usb_register()函數(shù)向內(nèi)核注冊一個(gè) USB設(shè)備驅(qū)動(dòng);usb_kbd_exit()函數(shù)在卸載驅(qū)動(dòng)程序的時(shí)候調(diào)用,該函數(shù)使用 usb_deregister()函數(shù)注銷 USB設(shè)備。初始化和注銷函數(shù)使用了 usb_keyboard結(jié)構(gòu)變量,用于描述 USB鍵盤驅(qū)動(dòng)程序,定義如下:


//usb_driver結(jié)構(gòu)體
static struct usb_driver usb_keyboard =
{
    .name = "usbkbd",   //驅(qū)動(dòng)名稱
    .probe = usb_kbd_probe, //檢測設(shè)備函數(shù)
    .disconnect = usb_kbd_disconnect,   //斷開連接函數(shù)
    .id_table = usb_kbd_id_table,   //設(shè)備 ID
};


   從 usb_keyboard結(jié)構(gòu)定義看出,usb_kbd_probe()函數(shù)是設(shè)備檢測函數(shù);

   usb_kbd_disconnect()函數(shù)是斷開設(shè)備連接。



2、設(shè)備檢測函數(shù)


   設(shè)備檢測函數(shù)在插入 USB設(shè)備的時(shí)候被USB文件系統(tǒng)調(diào)用,負(fù)責(zé)檢測設(shè)備類型是否與驅(qū)動(dòng)相符。如果設(shè)備類型與驅(qū)動(dòng)匹配,則向 USB核心注冊設(shè)備。

   函數(shù)定義如下:


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)  //檢查設(shè)備是否符合
        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);  //創(chuàng)建端點(diǎn)的管道
    maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
    kbd = kzalloc(sizeof(struct usb_kbd), GFP_KERNEL);
    input_dev = input_allocate_device();    //分配 input_dev結(jié)構(gòu)體
    if (!kbd || !input_dev) //分配設(shè)備結(jié)構(gòu)占用的內(nèi)存
        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) {  //檢查產(chǎn)品名稱
        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));
    //初始化輸入設(shè)備
    input_dev->name = kbd->name;  //輸入設(shè)備名稱
    input_dev->phys = kbd->phys;  //輸入設(shè)備物理地址
    usb_to_input_id(dev, &input_dev->id);    //輸入設(shè)備 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); //注冊輸入設(shè)備
    usb_set_intfdata(iface, kbd);   //設(shè)置接口私有數(shù)據(jù)
    return 0;
fail2:  usb_kbd_free_mem(dev, kbd);
fail1:  input_free_device(input_dev);
    kfree(kbd);
    return -ENOMEM;
}


   函數(shù)一開始檢測設(shè)備類型,如果與驅(qū)動(dòng)程序匹配,則創(chuàng)建 USB設(shè)備端點(diǎn),分配設(shè)備驅(qū)動(dòng)結(jié)構(gòu)占用的內(nèi)存。分配好設(shè)備驅(qū)動(dòng)使用的結(jié)構(gòu)后,申請一個(gè)鍵盤設(shè)備驅(qū)動(dòng)節(jié)點(diǎn),然后設(shè)置鍵盤驅(qū)動(dòng),最后設(shè)置 USB設(shè)備的中斷 URB和控制 URB,供 USB設(shè)備核心使用。



3、設(shè)備斷開連接函數(shù)


   在設(shè)備斷開連接的時(shí)候,USB文件系統(tǒng)會(huì)調(diào)用 usb_kbd_disconnect()函數(shù),釋放設(shè)備占用的資源。

   函數(shù)定義如下:


static void usb_kbd_disconnect(struct usb_interface *intf)
{
    struct usb_kbd *kbd = usb_get_intfdata (intf);
    usb_set_intfdata(intf, NULL);   //設(shè)置接口私有數(shù)據(jù)為 NULL
    if (kbd) {
        usb_kill_urb(kbd->irq);  //終止 URB
        input_unregister_device(kbd->dev);   //注銷輸入設(shè)備
        usb_kbd_free_mem(interface_to_usbdev(intf), kbd);   //釋放設(shè)備驅(qū)動(dòng)占用的內(nèi)存
        kfree(kbd);
    }
}

   usb_kbd_disconnect()函數(shù)釋放 USB鍵盤設(shè)備占用的 URB資源,然后注銷設(shè)備,最后調(diào)用usb_kbd_free_mem()函數(shù),釋放設(shè)備驅(qū)動(dòng)結(jié)構(gòu)變量占用的內(nèi)存。


向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI