生活随笔
收集整理的這篇文章主要介紹了
Linux USB 驱动开发实例(二)—— USB 鼠标驱动注解及测试
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
參考2.6.14版本中的driver/usb/input/usbmouse.c。鼠標驅(qū)動可分為幾個部分:驅(qū)動加載部分、probe部分、open部分、urb回調(diào)函數(shù)處理部分。?
一、驅(qū)動加載部分
[cpp]?view plaincopy
static?int?__init?usb_mouse_init(void)?? {????? ????int?retval?=?usb_register(&usb_mouse_driver);?? ????if?(retval?==?0)?? ????info(DRIVER_VERSION?":"?DRIVER_DESC);?? ????return?retval;?? }??
其中usb_mouse_driver的定義為:
[cpp]?view plaincopy
static?struct?usb_driver?usb_mouse_driver?=?{?? ????.owner?=?THIS_MODULE,?? ????.name?=?"usbmouse",?? ????.probe?=?usb_mouse_probe,?? ????.disconnect?=?usb_mouse_disconnect,?? ????.id_table?=?usb_mouse_id_table,?? };??
?
?? ? ?如果注冊成功的話,將會調(diào)用usb_mouse_probe。那么什么時候才算注冊成功呢?
? ? ??和其它驅(qū)動注冊過程一樣,只有在其對應的“總線”上發(fā)現(xiàn)匹配的“設備”才會調(diào)用probe??偩€匹配的方法和具體總線相關,如:platform_bus_type中是判斷驅(qū)動名稱和平臺設備名稱是否相同;那如何確認usb總線的匹配方法呢?
? ? ? ?Usb設備是注冊在usb_bus_type總線下的。查看usb_bus_type的匹配方法。
[cpp]?view plaincopy
struct?bus_type?usb_bus_type?=?{?? ????.name?=?"usb",?? ????.match?=?usb_device_match,?? ????????.hotplug?=?usb_hotplug,?? ????.suspend?=?usb_generic_suspend,?? ????.resume?=?usb_generic_resume,?? };??
其中usb_device_match定義了匹配方法
[cpp]?view plaincopy
static?int?usb_device_match?(struct?device?*dev,?struct?device_driver?*drv)?? {?? ????????????????????struct?usb_interface?*intf;?? ????????????????????struct?usb_driver?*usb_drv;?? ????????????????????const?struct?usb_device_id?*id;?? ?????????????????????? ????????????????????if?(drv?==?&usb_generic_driver)?? ????????????????????return?0;?? ????????????????????intf?=?to_usb_interface(dev);?? ????????????????????usb_drv?=?to_usb_driver(drv);?? ????????????????????id?=?usb_match_id?(intf,?usb_drv->id_table);?? ????????????????????if?(id)?? ??????????????????????????????return?1;?? ????????????????????return?0;?? }??
可以看出usb的匹配方法是usb_match_id (intf, usb_drv->id_table),也就是說通過比對“dev中intf信息”和“usb_drv->id_table信息”,如果匹配則說明驅(qū)動所對應的設備已經(jīng)添加到總線上了,所以接下了就會調(diào)用drv中的probe方法注冊usb設備驅(qū)動。
usb_mouse_id_table的定義為:
[cpp]?view plaincopy
static?struct?usb_device_id?usb_mouse_id_table[]?=?{?? ????????????????????{?USB_INTERFACE_INFO(3,?1,?2)?},?? ????????????????????{?}???????????????????????????????? ??????????};?? ?? #define?USB_INTERFACE_INFO(cl,sc,pr)?/?? ??????????.match_flags?=?USB_DEVICE_ID_MATCH_INT_INFO,?/?? ??????????.bInterfaceClass?=?(cl),?/?? ??????????.bInterfaceSubClass?=?(sc),?/?? ??????????.bInterfaceProtocol?=?(pr)??
鼠標設備遵循USB人機接口設備(HID),在HID規(guī)范中規(guī)定鼠標接口類碼為:
接口類:0x03
接口子類:0x01
接口協(xié)議:0x02
這樣分類的好處是設備廠商可以直接利用標準的驅(qū)動程序。除了HID類以外還有Mass storage、printer、audio等
[cpp]?view plaincopy
#define?USB_DEVICE_ID_MATCH_INT_INFO?/?? ????????????????????(USB_DEVICE_ID_MATCH_INT_CLASS?|?USB_DEVICE_ID_MATCH_INT_SUBCLASS?|?USB_DEVICE_ID_MATCH_INT_PROTOCOL)??
匹配的過程為:
[cpp]?view plaincopy
usb_match_id(struct?usb_interface?*interface,?const?struct?usb_device_id?*id)?? ??????????{?? ????????????????????struct?usb_host_interface?*intf;?? ????????????????????struct?usb_device?*dev;?? ?? ???????????? ????????????????????if?(id?==?NULL)?? ??????????????????????????????return?NULL;?? ?? ??????????intf?=?interface->cur_altsetting;?? ??????????????????dev?=?interface_to_usbdev(interface);?? ?? ??????????? ? ? ? ?? ??????????????????for?(;?id->idVendor?||?id->bDeviceClass?||?id->bInterfaceClass?||?? ???????????????????????????id->driver_info;?id++)?{?? ?? ????????????????????if?((id->match_flags?&?USB_DEVICE_ID_MATCH_VENDOR)?&&?? ?????????????????????????????????id->idVendor?!=?le16_to_cpu(dev->descriptor.idVendor))?? ?????????????????????????????????continue;?? ?? ????????????????????if?((id->match_flags?&?USB_DEVICE_ID_MATCH_PRODUCT)?&&?? ?????????????????????????????????id->idProduct?!=?le16_to_cpu(dev->descriptor.idProduct))?? ?????????????????????????????????continue;?? ?? ????????????????????? ????????????????????????????if?((id->match_flags?&?USB_DEVICE_ID_MATCH_DEV_LO)?&&?? ????????????????????????????????(id->bcdDevice_lo?>?le16_to_cpu(dev->descriptor.bcdDevice)))?? ???????????????????????????????continue;?? ?? ????????????????????if?((id->match_flags?&?USB_DEVICE_ID_MATCH_DEV_HI)?&&?? ?????????????????????????????????(id->bcdDevice_hi?<?le16_to_cpu(dev->descriptor.bcdDevice)))?? ?????????????????????????????????continue;?? ?? ????????????????????if?((id->match_flags?&?USB_DEVICE_ID_MATCH_DEV_CLASS)?&&?? ?????????????????????????????????(id->bDeviceClass?!=?dev->descriptor.bDeviceClass))?? ?????????????????????????????????continue;?? ?? ????????????????????if?((id->match_flags?&?USB_DEVICE_ID_MATCH_DEV_SUBCLASS)?&&?? ?????????????????????????????????(id->bDeviceSubClass!=?dev->descriptor.bDeviceSubClass))?? ?????????????????????????????????continue;?? ?? ????????????????????if?((id->match_flags?&?USB_DEVICE_ID_MATCH_DEV_PROTOCOL)?&&?? ?????????????????????????????????(id->bDeviceProtocol?!=?dev->descriptor.bDeviceProtocol))?? ?????????????????????????????????continue;?? ?? ?????????????????????? ?? ????????????????????if?((id->match_flags?&?USB_DEVICE_ID_MATCH_INT_CLASS)?&&?? ????????????????????????????????(id->bInterfaceClass?!=?intf->desc.bInterfaceClass))?? ????????????????????????????????continue;?? ?? ?????????????????????? ?? ????????????????????if?((id->match_flags?&?USB_DEVICE_ID_MATCH_INT_SUBCLASS)?&&?? ????????????????????????????????(id->bInterfaceSubClass?!=?intf->desc.bInterfaceSubClass))?? ????????????????????????????????continue;?? ?? ???????????????????? ?? ????????????????????if?((id->match_flags?&?USB_DEVICE_ID_MATCH_INT_PROTOCOL)?&&?? ????????????????????????????????(id->bInterfaceProtocol?!=?intf->desc.bInterfaceProtocol))?? ????????????????????????????????continue;?? ?? ??????????????????return?id;?? ??????????????????????}?? ??????????????????????return?NULL;?? ???????????}??
從中可以看出,只有當設備的接口類、接口子類、接口協(xié)議匹配鼠標驅(qū)動時鼠標驅(qū)動才會調(diào)用probe方法。
二、probe部分
[cpp]?view plaincopy
static?int?usb_mouse_probe(struct?usb_interface?*?intf,?const?struct?usb_device_id?*?id)?? {?? ?????????struct?usb_device?*?dev?=?interface_to_usbdev(intf);?? ?????????struct?usb_host_interface?*interface;?? ?????????struct?usb_endpoint_descriptor?*endpoint;?? ?????????struct?usb_mouse?*mouse;?? ?????????int?pipe,?maxp;?? ?????????char?path[64];?? ?? ?????????interface?=?intf->cur_altsetting;?? ?? ?? ?? ?????????if?(interface->desc.bNumEndpoints?!=?1)?? ????????return?-ENODEV;?? ?? ? ?? ?? ?????????endpoint?=?&interface->endpoint[0].desc;?? ?????????if?(!(endpoint->bEndpointAddress?&?0x80))?? ????????return?-ENODEV;?? ?? ? ? ? ? ? ? ?? ?*#define?USB_DIR_IN?0x80???? ?*/?? ?? ????if?((endpoint->bmAttributes?&?3)?!=?3)???? ????????return?-ENODEV;?? ?? ?? ?? ????pipe?=?usb_rcvintpipe(dev,?endpoint->bEndpointAddress);?? ?? ????maxp?=?usb_maxpacket(dev,?pipe,?usb_pipeout(pipe));?? ?? ? ? ? ? ? ? ? ?? ????????if?(is_out)?{?? ????????????WARN_ON(usb_pipein(pipe));?? ????????????ep?=?udev->ep_out[epnum];?? ????????}?else?{?? ????????????WARN_ON(usb_pipeout(pipe));?? ????????????ep?=?udev->ep_in[epnum];?? ????????}?? ????????if?(!ep)?? ????????????return?0;?? ???????????????????? ????????????return?le16_to_cpu(ep->desc.wMaxPacketSize);?? ?????????}?? ?????????*/?? ??????????? ?????????第0個字節(jié):bit?0、1、2、3、4分別代表左、右、中、SIDE、EXTRA鍵的按下情況??? ?????????第1個字節(jié):表示鼠標的水平位移??? ?????????第2個字節(jié):表示鼠標的垂直位移??? ?????????第3個字節(jié):REL_WHEEL位移?? ?? ????if?(!(mouse?=?kmalloc(sizeof(struct?usb_mouse),?GFP_KERNEL)))?? ????????return?-ENOMEM;?? ????memset(mouse,?0,?sizeof(struct?usb_mouse));?? ????mouse->data?=?usb_buffer_alloc(dev,?8,?SLAB_ATOMIC,?&mouse->data_dma);?? ?? ????? ? ? ? ? ?? ?? ????if?(!mouse->data)?{?? ????????kfree(mouse);?? ????????return?-ENOMEM;?? ?????????}?? ?????????mouse->irq?=?usb_alloc_urb(0,?GFP_KERNEL);?? ?????????if?(!mouse->irq)?{?? ????????usb_buffer_free(dev,?8,?mouse->data,?mouse->data_dma);?? ????????kfree(mouse);?? ????????return?-ENODEV;?? ?????????}?? ?????????mouse->usbdev?=?dev;?? ?????????mouse->dev.evbit[0]?=?BIT(EV_KEY)?|?BIT(EV_REL);?? ?? ?????? ?? ????mouse->dev.keybit[LONG(BTN_MOUSE)]?=?BIT(BTN_LEFT)?|?BIT(BTN_RIGHT)?|?BIT(BTN_MIDDLE);?? ????mouse->dev.relbit[0]?=?BIT(REL_X)?|?BIT(REL_Y);?? ????mouse->dev.keybit[LONG(BTN_MOUSE)]?|=?BIT(BTN_SIDE)?|?BIT(BTN_EXTRA);?? ????mouse->dev.relbit[0]?|=?BIT(REL_WHEEL);?? ?? ?????? ?? ????mouse->dev.private?=?mouse;?? ????mouse->dev.open?=?usb_mouse_open;?? ????mouse->dev.close?=?usb_mouse_close;?? ?? ????usb_make_path(dev,?path,?64);?? ????sprintf(mouse->phys,?"%s/input0",?path);?? ?? ????mouse->dev.name?=?mouse->name;?? ????mouse->dev.phys?=?mouse->phys;?? ????usb_to_input_id(dev,?&mouse->dev.id);?? ?? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ?? ????mouse->dev.dev?=?&intf->dev;?? ?? ????if?(dev->manufacturer)?? ????????strcat(mouse->name,?dev->manufacturer);?? ????if?(dev->product)?? ????????sprintf(mouse->name,?"%s?%s",?mouse->name,?dev->product);?? ?? ????if?(!strlen(mouse->name))?? ????????sprintf(mouse->name,?"USB?HIDBP?Mouse?%04x:%04x",?? ???????????????????????????mouse->dev.id.vendor,?mouse->dev.id.product);?? ?? ????usb_fill_int_urb(mouse->irq,?dev,?pipe,?mouse->data,?? ????????????????????????????????????(maxp?>?8???8?:?maxp),?? ????????????????????????????????????usb_mouse_irq,?mouse,?endpoint->bInterval);?? ?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ?? ????mouse->irq->transfer_dma?=?mouse->data_dma;?? ????mouse->irq->transfer_flags?|=?URB_NO_TRANSFER_DMA_MAP;?? ?? ? ? ? ? ? ? ? ? ?? ?? ????input_register_device(&mouse->dev);?? ?? ?????? ?? ????printk(KERN_INFO?"input:?%s?on?%s/n",?mouse->name,?path);?? ?? ????usb_set_intfdata(intf,?mouse);?? ?? ? ? ? ?? ?? ????return?0;?? }??
三、open部分
當應用層打開鼠標設備時,usb_mouse_open將被調(diào)用
[cpp]?view plaincopy
static?int?usb_mouse_open(struct?input_dev?*dev)?? {?? ?????????struct?usb_mouse?*mouse?=?dev->private;?? ?? ?????????mouse->irq->dev?=?mouse->usbdev;?? ?????????if?(usb_submit_urb(mouse->irq,?GFP_KERNEL))?? ??????????????????return?-EIO;?? ?? ?? ?? ?????????return?0;?? }??
四、urb回調(diào)函數(shù)處理部分
當出現(xiàn)傳輸錯誤或獲取到鼠標數(shù)據(jù)后,urb回調(diào)函數(shù)將被執(zhí)行?
[cpp]?view plaincopy
static?void?usb_mouse_irq(struct?urb?*urb,?struct?pt_regs?*regs)?? {?? ?????????struct?usb_mouse?*mouse?=?urb->context;?? ?? ?? ?? ?????????signed?char?*data?=?mouse->data;?? ?????????struct?input_dev?*dev?=?&mouse->dev;?? ?????????int?status;?? ?????????switch?(urb->status)?{?? ?????????case?0:???????????????????? ??????????????????break;?? ?????????case?-ECONNRESET:??????????? ?????????case?-ENOENT:?? ?????????case?-ESHUTDOWN:?? ??????????????????return;?? ??????????? ?????????default:??????????? ??????????????????goto?resubmit;?? }?? ?? input_regs(dev,?regs);?? ?? 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);?? ??????????? ?????????static?inline?void?input_report_key(struct?input_dev?*dev,?unsigned?int?code,?int?value)中的value非0時表示按下,0表示釋放??? ?????????input_report_rel(dev,?REL_X,?????????data[1]);?? ?????????input_report_rel(dev,?REL_Y,?????????data[2]);?? ?????????input_report_rel(dev,?REL_WHEEL,?data[3]);?? ??????????? ?????????input_sync(dev);?? ??????????? ?????????resubmit:?? ?????????status?=?usb_submit_urb?(urb,?SLAB_ATOMIC);?? ??????????? ?????????if?(status)?? ??????????????????err?("can't?resubmit?intr,?%s-%s/input0,?status?%d",?? ???????????????????????????mouse->usbdev->bus->bus_name,?? ???????????????????????????mouse->usbdev->devpath,?status);?? }??
五、應用層測試代碼編寫
在應用層編寫測試鼠標的測試程序,在我的系統(tǒng)中,鼠標設備為/dev/input/event3. 測試代碼如下:
[cpp]?view plaincopy
? ? ? ?? ?? #include?<stdio.h>?? #include?<sys/types.h>?? #include?<unistd.h>?? #include?<fcntl.h>?? #include?<linux/input.h>?? ?? int?main?(void)??? {?? ????int?fd,i,count;?? ????struct?input_event?ev_mouse[2];?? ????fd?=?open?("/dev/input/event3",O_RDWR);?? ????if?(fd?<?0)?{?? ????????printf?("fd?open?failed/n");?? ????????exit(0);?? ????}?? ????printf?("/nmouse?opened,?fd=%d/n",fd);?? ????while(1)?? ????{?? ????????printf(".............................................../n");?? ????????count=read(fd,?ev_mouse,?sizeof(struct?input_event));?? ????????for(i=0;i<(int)count/sizeof(struct?input_event);i++)?? ????????{?? ????????????printf("type=%d/n",ev_mouse[i].type);?? ????????????if(EV_REL==ev_mouse[i].type)?? ????????????{?? ????????????????printf("time:%ld.%d",ev_mouse[i].time.tv_sec,ev_mouse[i].time.tv_usec);?? ????????????????printf("?type:%d?code:%d?value:%d/n",ev_mouse[i].type,ev_mouse[i].code,ev_mouse[i].value);?? ????????????}?? ????????????if(EV_KEY==ev_mouse[i].type)?? ????????????{?? ????????????????printf("time:%ld.%d",ev_mouse[i].time.tv_sec,ev_mouse[i].time.tv_usec);?? ????????????????printf("?type:%d?code:%d?value:%d/n",ev_mouse[i].type,ev_mouse[i].code,ev_mouse[i].value);?? ????????????}?? ????????}?? ????}?? ????close?(fd);?? ????return?0;?? } ?
總結(jié)
以上是生活随笔為你收集整理的Linux USB 驱动开发实例(二)—— USB 鼠标驱动注解及测试的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。