最近需要往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_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设备被插入的时候,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",
- .probe = usbtouch_probe,
- .disconnect = usbtouch_disconnect,
- .suspend = usbtouch_suspend,
- .resume = usbtouch_resume,
- .reset_resume = usbtouch_reset_resume,
- .id_table = usbtouch_devices,
- .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;
- struct input_dev *input_dev;
- struct usb_endpoint_descriptor *endpoint;
- struct usb_device *udev = interface_to_usbdev(intf);
- struct usbtouch_device_info *type;
-
-
- 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();
-
- ...
-
- type = &usbtouch_dev_info[id->driver_info];
-
- ...
-
- usbtouch->irq = usb_alloc_urb(0, GFP_KERNEL);
- if (!usbtouch->irq) {
- dbg("%s - usb_alloc_urb failed: usbtouch->irq", __func__);
- goto out_free_buffers;
- }
-
- ...
-
-
-
- input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_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);
-
- 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,
- .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);
-
-
-
-
-
-
-
-
- ...
-
- }
接下来的都会调用到usbtouch_process_pkt中,通过type->read_data,和上面一样的指针读取,然后调用input_report_key发送,input_sync用于同步.
关于usbtouchscreen的驱动部分就分析到这里。