本文转载自:
     
      http://blog.csdn.net/jscese/article/details/41827495
     
    
最近需要往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:
- 
       
 static
 
 int __init usbtouch_init(
 
 void)
 
 
 
- {
- 
       
 return usb_register(&usbtouch_driver);
 
 //调用了usb 核心的注册函数,传入的是一个usb_driver结构体指针
 
 
- }
usb_register实现在/kernel/include/linux/usb.h中:
- 
       
 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使用了部分接口:
- 
       
 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列表,数据类型为:
- 
       
 struct usb_device_id {
 
- 
       
 /* which fields to match against? */
 
- 
       __u
 
 1
 
 6 match_flags;
 
 
- 
       
 /* Used for product specific matches; range is inclusive */
 
- 
       __u
 
 1
 
 6 idVendor;
 
 
- 
       __u
 
 1
 
 6 idProduct;
 
 
- 
       __u
 
 1
 
 6 bcdDevice_lo;
 
 
- 
       __u
 
 1
 
 6 bcdDevice_hi;
 
 
- 
       
 /* Used for device class matches */
 
- 
       __u
 
 8 bDeviceClass;
 
- 
       __u
 
 8 bDeviceSubClass;
 
- 
       __u
 
 8 bDeviceProtocol;
 
- 
       
 /* Used for interface class matches */
 
- 
       __u
 
 8 bInterfaceClass;
 
- 
       __u
 
 8 bInterfaceSubClass;
 
- 
       __u
 
 8 bInterfaceProtocol;
 
- 
       
 /* not matched against */
 
- kernel_ulong_t driver_info;
- };
     
     这些设备信息会被上面说到的usb bus 来匹配对应的驱动,只有这里的信息跟usb设备驱动那边收集到的设备信息匹配上,才会调用进这个驱动.
    
     目前已有的
     
      id_table
     
     :
    
- 
       
 static
 
 const
 
 struct usb_device_id usbtouch_devices[] = {
 
 
 
- 
       
 #ifdef CONFIG_TOUCHSCREEN_USB_EGALAX
 
- 
       
 /* ignore the HID capable devices, handled by usbhid */
 
- 
       {USB_DEVICE_HID_CLASS(
 
 0x
 
 0eef,
 
 0x
 
 0
 
 0
 
 0
 
 1),
 
 .driver_info = DEVTYPE_IGNORE},
 
 
 
 
 
 
 
 
- 
       {USB_DEVICE_HID_CLASS(
 
 0x
 
 0eef,
 
 0x
 
 0
 
 0
 
 0
 
 2),
 
 .driver_info = DEVTYPE_IGNORE},
 
 
 
 
 
 
 
 
- …
- 
       
 #endif
 
- …
- };
其中可以看到 两个字节的十六进制数字,第一个代表idVendor 厂商ID,idProduct 产品ID ,这两个一般作为设备的标识.
     
     
     driver_info:
    
     像上面的
     
      usbtouch_devices
     
     的数组中driver_info 设置为枚举值:
    
- 
       
 /* device types */
 
- 
       
 enum {
 
- 
       DEVTYPE_IGNORE = –
 
 1,
 
- DEVTYPE_EGALAX,
- DEVTYPE_PANJIT,
- 
       DEVTYPE_
 
 3M,
 
- DEVTYPE_ITM,
- DEVTYPE_ETURBO,
- DEVTYPE_GUNZE,
- 
       DEVTYPE_DMC_TSC
 
 1
 
 0,
 
 
- DEVTYPE_IRTOUCH,
- DEVTYPE_IDEALTEK,
- DEVTYPE_GENERAL_TOUCH,
- DEVTYPE_GOTOP,
- DEVTYPE_JASTEC,
- 
       DEVTYPE_E
 
 2I,
 
- DEVTYPE_ZYTRONIC,
- 
       DEVTYPE_TC
 
 4
 
 5USB,
 
 
- DEVTYPE_NEXIO,
- };
     
     那么这些driver 的真正的info保存在哪里呢? 在注册的时候,现在只是注册上去一个枚举数字而已,
    
     真正有设备识别到的时候这些个枚举值就起到作用了! 在下面的
     
      usbtouch_probe
     
     会介绍!
    
     
     
     
      usbtouch_probe
     
     :
    
在前面有稍微提到,usbtouchscreen驱动是怎么被映射到的,这个过程暂时不做深入,作为这个驱动中的第一个接入点就是usbtouch_probe.
- 
       
 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
     
     数组的第几元素.
    
- 
       
 struct usbtouch_device_info {
 
- 
       
 int min_xc, max_xc;
 
- 
       
 int min_yc, max_yc;
 
- 
       
 int min_press, max_press;
 
- 
       
 int rept_size;
 
- 
       
 /*
 
- 
       
 * Always service the USB devices irq not just when the input device is
 
- 
       
 * open. This is useful when devices have a watchdog which prevents us
 
- 
       
 * from periodically polling the device. Leave this unset unless your
 
- 
       
 * touchscreen device requires it, as it does consume more of the USB
 
- 
       
 * bandwidth.
 
- 
       
 */
 
- 
       
 bool irq_always;
 
- 
       
 void (*process_pkt) (
 
 struct
 
 usbtouch_usb *usbtouch, unsigned
 
 char
 
 char *pkt,
 
 int len);
 
 //这个函数指针是用来接收处理中断的。
 
 
 
 
 
 
 
- 
       
 /*
 
- 
       
 * used to get the packet len. possible return values:
 
- 
       
 * > 0: packet len
 
- 
       
 * = 0: skip one byte
 
- 
       
 * < 0: -return value more bytes needed
 
- 
       
 */
 
- 
       
 int (*get_pkt_len) (unsigned
 
 char
 
 char *pkt,
 
 int len);
 
 
 
 
- 
       
 int (*read_data) (
 
 struct
 
 usbtouch_usb *usbtouch, unsigned
 
 char
 
 char *pkt);
 
 
 
 
 
- 
       
 int (*alloc) (
 
 struct
 
 usbtouch_usb *usbtouch);
 
 
 
- 
       
 int (*init) (
 
 struct
 
 usbtouch_usb *usbtouch);
 
 
 
- 
       
 void (*exit) (
 
 struct
 
 usbtouch_usb *usbtouch);
 
 
 
- };
     
      usbtouch_dev_info
     
     数组:
    
- 
       
 static
 
 struct usbtouch_device_info usbtouch_dev_info[] = {
 
 
- 
       
 #ifdef CONFIG_TOUCHSCREEN_USB_EGALAX
 
- [DEVTYPE_EGALAX] = {
- 
       
 .min_xc =
 
 0x
 
 0,
 
 
 
- 
       
 .max_xc =
 
 0x
 
 0
 
 7ff,
 
 
 
 
- 
       
 .min_yc =
 
 0x
 
 0,
 
 
 
- 
       
 .max_yc =
 
 0x
 
 0
 
 7ff,
 
 
 
 
- 
       
 .rept_size =
 
 1
 
 6,
 
 
 
- 
       
 .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 =
 
 0x
 
 0,
 
 
 
- 
       
 .max_xc =
 
 0x
 
 0fff,
 
 
 
- 
       
 .min_yc =
 
 0x
 
 0,
 
 
 
- 
       
 .max_yc =
 
 0x
 
 0fff,
 
 
 
- 
       
 .rept_size =
 
 8,
 
 
- 
       
 .read_data = irtouch_read_data,
 
- },
- 
       
 #endif
 
- …
- };
     
      可以看到这个数组的成员都是以前面说到的注册枚举值来区分的!这些x,y 参数以及回调函数,都在上面说到的 usbtouch_probe 中被抽离出来使用.
     
    
     
     
     
      usbtouch_irq:
     
    
     这个函数作为中断响应函数,在上面的
     
      usbtouch_probe
     
     中初始化,看下函数主要实现:
    
- 
       
 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的驱动部分就分析到这里。
 
