版權聲明:免責聲明: 本人在此發文(包括但不限于漢字、拼音、拉丁字母)均為随意敲擊鍵盤所出,用于檢驗本人電腦鍵盤錄入、螢幕顯示的機械、光電性能,并不代表本人局部或全部同意、支援或者反對觀點。如需要詳查請直接與鍵盤生産廠商法人代表聯系 .挖井挑水無水表,不會網購無快遞
目錄(?)[+]
最近需要往TV上裝一個觸摸屏裝置,現在比較常見的就是使用usb接口的觸摸框,适用于各種平台,這裡大體記錄一下在Android上kernel中的usbtouchscreen驅動.
撰寫不易,轉載需注明出處:http://blog.csdn.net/jscese/article/details/41827495
驅動編譯:
目前的kernel中都是自帶了usbtouchscreen驅動的,我的版本3.1.10
源碼位于:/kernel/drivers/input/touchscreen/usbtouchscreen.c
從這個路徑可以看出所屬驅動分支,我這邊平台本身是沒放開的,并沒有編譯進kernel,誰會想到觸摸電視呢~
可以在make menuconfig之後,通過Device Drivers——>Input device support——>Touchscreens——>USB Touchscreen Driver 然後選取需要的touchscreen類型
通過檢視相關目錄下的的Kconfig Makefile,可參考:Kernel 編譯配置機制
注冊usb驅動:
熟悉Linux驅動的都知道子產品入口:module_init(usbtouch_init) ,這裡看下這個init:
[objc] view plain copy print ?
- static int __init usbtouch_init(void)
- {
- return usb_register(&usbtouch_driver); //調用了usb 核心的注冊函數,傳入的是一個usb_driver結構體指針
- }
usb_register實作在/kernel/include/linux/usb.h中:
[objc] view plain copy print ?
- static inline int usb_register(struct usb_driver *driver)
- {
- return usb_register_driver(driver, THIS_MODULE, KBUILD_MODNAME);//這裡再往後就是usb核心驅動的事,注冊這個module驅動到usb總線上
- }
這裡必須是要先注冊的總線,當一個USB裝置被插入的時候,USB裝置驅動,也就是usb_generic_driver會跟USB裝置互動,得到其所有的各種描述符,并為每個接口都定義成為一個device,之後再加載到usb_bus上,讓其去比對其對應的接口驅動程式,有興趣可以去看下/kernel/drivers/base/bus.c中的 bus_for_each_drv函數。
這裡注冊到總線的接口驅動就是 usbtouch_driver
usbtouch_driver:
這個usb_driver類型的變量usbtouch_driver 就是整個usbtouchscreen的靈魂核心,可以在上面說到的usb.h中檢視usb_driver結構原型,
這裡usbtouch_driver使用了部分接口:
[objc] view plain copy print ?
- static struct usb_driver usbtouch_driver = {
- .name = "usbtouchscreen", //driver name
- .probe = usbtouch_probe, //probe接口,用于總線上比對檢測到這個驅動對應的裝置之後,/kernel/drivers/usb/core/driver.c中的usb_probe_interface調用到我們這個驅動的接口
- .disconnect = usbtouch_disconnect, //與probe相反,斷開的時候調用
- .suspend = usbtouch_suspend, //usb 裝置挂起
- .resume = usbtouch_resume, // 和上面挂起相反,喚醒
- .reset_resume = usbtouch_reset_resume, // 重置喚醒
- .id_table = usbtouch_devices, //支援的裝置ID表
- .supports_autosuspend = 1,
- };
id_table:
首先可以關注一下 id_table 這個變量,代表支援的裝置id清單,資料類型為:
[objc] view plain copy print ?
- struct usb_device_id {
- __u16 match_flags;
- __u16 idVendor;
- __u16 idProduct;
- __u16 bcdDevice_lo;
- __u16 bcdDevice_hi;
- __u8 bDeviceClass;
- __u8 bDeviceSubClass;
- __u8 bDeviceProtocol;
- __u8 bInterfaceClass;
- __u8 bInterfaceSubClass;
- __u8 bInterfaceProtocol;
- kernel_ulong_t driver_info;
- };
這些裝置資訊會被上面說到的usb bus 來比對對應的驅動,隻有這裡的資訊跟usb裝置驅動那邊收集到的裝置資訊比對上,才會調用進這個驅動.
目前已有的id_table:
[objc] view plain copy print ?
- static const struct usb_device_id usbtouch_devices[] = {
- #ifdef CONFIG_TOUCHSCREEN_USB_EGALAX
- {USB_DEVICE_HID_CLASS(0x0eef, 0x0001), .driver_info = DEVTYPE_IGNORE},
- {USB_DEVICE_HID_CLASS(0x0eef, 0x0002), .driver_info = DEVTYPE_IGNORE},
- ...
- #endif
- ...
- };
其中可以看到 兩個位元組的十六進制數字,第一個代表idVendor 廠商ID,idProduct 産品ID ,這兩個一般作為裝置的辨別.
driver_info:
像上面的usbtouch_devices的數組中driver_info 設定為枚舉值:
[objc] view plain copy print ?
- enum {
- DEVTYPE_IGNORE = -1,
- DEVTYPE_EGALAX,
- DEVTYPE_PANJIT,
- DEVTYPE_3M,
- DEVTYPE_ITM,
- DEVTYPE_ETURBO,
- DEVTYPE_GUNZE,
- DEVTYPE_DMC_TSC10,
- DEVTYPE_IRTOUCH,
- DEVTYPE_IDEALTEK,
- DEVTYPE_GENERAL_TOUCH,
- DEVTYPE_GOTOP,
- DEVTYPE_JASTEC,
- DEVTYPE_E2I,
- DEVTYPE_ZYTRONIC,
- DEVTYPE_TC45USB,
- DEVTYPE_NEXIO,
- };
那麼這些driver 的真正的info儲存在哪裡呢? 在注冊的時候,現在隻是注冊上去一個枚舉數字而已,
真正有裝置識别到的時候這些個枚舉值就起到作用了! 在下面的 usbtouch_probe 會介紹!
usbtouch_probe:
在前面有稍微提到,usbtouchscreen驅動是怎麼被映射到的,這個過程暫時不做深入,作為這個驅動中的第一個接入點就是usbtouch_probe.
[objc] view plain copy print ?
- static int usbtouch_probe(struct usb_interface *intf,
- const struct usb_device_id *id)
- {
- struct usbtouch_usb *usbtouch; //usbtouch 裝置
- struct input_dev *input_dev; //輸入裝置
- struct usb_endpoint_descriptor *endpoint; //usb 的端點
- struct usb_device *udev = interface_to_usbdev(intf); //從usb接口擷取對應的裝置
- struct usbtouch_device_info *type; //這個就是上面說的真正的 driver info了
- endpoint = usbtouch_get_input_endpoint(intf->cur_altsetting); //擷取端點
- if (!endpoint)
- return -ENXIO;
- usbtouch = kzalloc(sizeof(struct usbtouch_usb), GFP_KERNEL);
- input_dev = input_allocate_device(); //配置設定記憶體,申請input 裝置結構
- ...
- type = &usbtouch_dev_info[id->driver_info]; // 這裡就用到了 上面說到的枚舉值了, 真正的info 是放在這個數組裡面的!
- ...
- usbtouch->irq = usb_alloc_urb(0, GFP_KERNEL); //配置設定了一個urb 用于 獲得觸摸屏裝置傳回的觸摸事件的資料,urb的概念可參考usb driver
- if (!usbtouch->irq) {
- dbg("%s - usb_alloc_urb failed: usbtouch->irq", __func__);
- goto out_free_buffers;
- }
- ...
- //往下都是一些配置設定記憶體,input注冊,初始化操作了
- input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); //這裡是就是input裝置觸摸坐标的初始化指派了,為ABS 絕對坐标
- input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
- input_set_abs_params(input_dev, ABS_X, type->min_xc, type->max_xc, 0, 0);
- input_set_abs_params(input_dev, ABS_Y, type->min_yc, type->max_yc, 0, 0);
- ...
- if (usb_endpoint_type(endpoint) == USB_ENDPOINT_XFER_INT)
- usb_fill_int_urb(usbtouch->irq, udev,
- usb_rcvintpipe(udev, endpoint->bEndpointAddress),
- usbtouch->data, type->rept_size,
- usbtouch_irq, usbtouch, endpoint->bInterval);
- else
- usb_fill_bulk_urb(usbtouch->irq, udev,
- usb_rcvbulkpipe(udev, endpoint->bEndpointAddress),
- usbtouch->data, type->rept_size,
- usbtouch_irq, usbtouch); //初始化urb的回調函數為 usbtouch_irq
- usbtouch->irq->dev = udev;
- usbtouch->irq->transfer_dma = usbtouch->data_dma;
- usbtouch->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
- ...
- }
usbtouch_device_info:
這個就是上面driver_info 以及usbtouch_probe 中抽取的驅動子產品的info數組,不同的usbtouchscreen 注冊的時候就是注冊了一個枚舉值,這個值就是usbtouch_dev_info 數組的第幾元素.
[objc] view plain copy print ?
- struct usbtouch_device_info {
- int min_xc, max_xc;
- int min_yc, max_yc;
- int min_press, max_press;
- int rept_size;
- bool irq_always;
- void (*process_pkt) (struct usbtouch_usb *usbtouch, unsigned charchar *pkt, int len); //這個函數指針是用來接收進行中斷的。
- int (*get_pkt_len) (unsigned charchar *pkt, int len);
- int (*read_data) (struct usbtouch_usb *usbtouch, unsigned charchar *pkt);
- int (*alloc) (struct usbtouch_usb *usbtouch);
- int (*init) (struct usbtouch_usb *usbtouch);
- void (*exit) (struct usbtouch_usb *usbtouch);
- };
usbtouch_dev_info 數組:
[objc] view plain copy print ?
- static struct usbtouch_device_info usbtouch_dev_info[] = {
- #ifdef CONFIG_TOUCHSCREEN_USB_EGALAX
- [DEVTYPE_EGALAX] = {
- .min_xc = 0x0,
- .max_xc = 0x07ff,
- .min_yc = 0x0,
- .max_yc = 0x07ff,
- .rept_size = 16,
- .process_pkt = usbtouch_process_multi,//用于中斷回調函數,用于進行中斷,得到input的event,上傳資料
- .get_pkt_len = egalax_get_pkt_len,
- .read_data = egalax_read_data, //用于中斷回調函數,用于讀取資料
- },
- #endif
- ...
- #ifdef CONFIG_TOUCHSCREEN_USB_IRTOUCH
- [DEVTYPE_IRTOUCH] = {
- .min_xc = 0x0,
- .max_xc = 0x0fff,
- .min_yc = 0x0,
- .max_yc = 0x0fff,
- .rept_size = 8,
- .read_data = irtouch_read_data,
- },
- #endif
- ...
- };
可以看到這個數組的成員都是以前面說到的注冊枚舉值來區分的!這些x,y 參數以及回調函數,都在上面說到的 usbtouch_probe 中被抽離出來使用.
usbtouch_irq:
這個函數作為中斷響應函數,在上面的 usbtouch_probe中初始化,看下函數主要實作:
[objc] view plain copy print ?
- static void usbtouch_irq(struct urb *urb)
- {
- ...
- usbtouch->type->process_pkt(usbtouch, usbtouch->data, urb->actual_length);
- //這個type的類型就是 usbtouch_device_info,此時的process_pkt指針自然指向的是上面對應的函數,如果此時是觸發的裝置type為 DEVTYPE_EGALAX,那麼這裡調用的 usbtouch_process_multi
- //如果此時是DEVTYPE_IRTOUCH 那麼就是執行 usbtouch_process_pkt函數,因為usbtouch_probe中:
- // if (!type->process_pkt)
- // type->process_pkt = usbtouch_process_pkt;
- ...
- }
接下來的都會調用到usbtouch_process_pkt中,通過type->read_data,和上面一樣的指針讀取,然後調用input_report_key發送,input_sync用于同步.
關于usbtouchscreen的驅動部分就分析到這裡。