您好,登錄后才能下訂單哦!
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設備驅動初始化函數,定義如下:
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串口設備驅動申請的內核資源,函數定義如下:
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類型的結構,該結構包含了串口的所有操作。定義如下:
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鍵盤驅動初始化和注銷函數定義如下:
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鍵盤驅動程序,定義如下:
//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核心注冊設備。
函數定義如下:
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()函數,釋放設備占用的資源。
函數定義如下:
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()函數,釋放設備驅動結構變量占用的內存。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。