Linux/Android——usb触摸屏驱动 – usbtouchscreen (一)【转】

  • Post author:
  • Post category:linux


本文转载自:

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:


  1. static

    int __init usbtouch_init(

    void)


  2. {

  3. return usb_register(&usbtouch_driver);

    //调用了usb 核心的注册函数,传入的是一个usb_driver结构体指针

  4. }

usb_register实现在/kernel/include/linux/usb.h中:


  1. static

    inline

    int usb_register(

    struct

    usb_driver *driver)




  2. {

  3. return usb_register_driver(driver, THIS_MODULE, KBUILD_MODNAME);

    //这里再往后就是usb核心驱动的事,注册这个module驱动到usb总线上

  4. }


这里必须是要先注册的总线,当一个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使用了部分接口:


  1. static

    struct usb_driver usbtouch_driver = {


  2. .name        =

    “usbtouchscreen”,

    //driver name



  3. .probe        = usbtouch_probe,

    //probe接口,用于总线上匹配检测到这个驱动对应的设备之后,/kernel/drivers/usb/core/driver.c中的usb_probe_interface调用到我们这个驱动的接口


  4. .disconnect    = usbtouch_disconnect,

    //与probe相反,断开的时候调用


  5. .suspend    = usbtouch_suspend,

    //usb 设备挂起


  6. .resume        = usbtouch_resume,

    // 和上面挂起相反,唤醒


  7. .reset_resume    = usbtouch_reset_resume,

    // 重置唤醒


  8. .id_table    = usbtouch_devices,

    //支持的设备ID表


  9. .supports_autosuspend =

    1,

  10. };



id_table:

首先可以关注一下 id_table 这个变量,代表支持的设备id列表,数据类型为:


  1. struct usb_device_id {

  2. /* which fields to match against? */
  3. __u

    1

    6       match_flags;


  4. /* Used for product specific matches; range is inclusive */
  5. __u

    1

    6       idVendor;

  6. __u

    1

    6       idProduct;

  7. __u

    1

    6       bcdDevice_lo;

  8. __u

    1

    6       bcdDevice_hi;


  9. /* Used for device class matches */
  10. __u

    8        bDeviceClass;
  11. __u

    8        bDeviceSubClass;
  12. __u

    8        bDeviceProtocol;

  13. /* Used for interface class matches */
  14. __u

    8        bInterfaceClass;
  15. __u

    8        bInterfaceSubClass;
  16. __u

    8        bInterfaceProtocol;

  17. /* not matched against */
  18. kernel_ulong_t  driver_info;
  19. };


这些设备信息会被上面说到的usb bus 来匹配对应的驱动,只有这里的信息跟usb设备驱动那边收集到的设备信息匹配上,才会调用进这个驱动.

目前已有的

id_table

:


  1. static

    const

    struct usb_device_id usbtouch_devices[] = {



  2. #ifdef CONFIG_TOUCHSCREEN_USB_EGALAX

  3. /* ignore the HID capable devices, handled by usbhid */
  4. {USB_DEVICE_HID_CLASS(

    0x

    0eef,

    0x

    0

    0

    0

    1),

    .driver_info = DEVTYPE_IGNORE},







  5. {USB_DEVICE_HID_CLASS(

    0x

    0eef,

    0x

    0

    0

    0

    2),

    .driver_info = DEVTYPE_IGNORE},








  6. #endif
  7. };

其中可以看到 两个字节的十六进制数字,第一个代表idVendor 厂商ID,idProduct 产品ID ,这两个一般作为设备的标识.



driver_info:

像上面的

usbtouch_devices

的数组中driver_info 设置为枚举值:


  1. /* device types */

  2. enum {
  3. DEVTYPE_IGNORE = –

    1,
  4. DEVTYPE_EGALAX,
  5. DEVTYPE_PANJIT,
  6. DEVTYPE_

    3M,
  7. DEVTYPE_ITM,
  8. DEVTYPE_ETURBO,
  9. DEVTYPE_GUNZE,
  10. DEVTYPE_DMC_TSC

    1

    0,

  11. DEVTYPE_IRTOUCH,
  12. DEVTYPE_IDEALTEK,
  13. DEVTYPE_GENERAL_TOUCH,
  14. DEVTYPE_GOTOP,
  15. DEVTYPE_JASTEC,
  16. DEVTYPE_E

    2I,
  17. DEVTYPE_ZYTRONIC,
  18. DEVTYPE_TC

    4

    5USB,

  19. DEVTYPE_NEXIO,
  20. };


那么这些driver 的真正的info保存在哪里呢? 在注册的时候,现在只是注册上去一个枚举数字而已,

真正有设备识别到的时候这些个枚举值就起到作用了! 在下面的

usbtouch_probe

会介绍!




usbtouch_probe


在前面有稍微提到,usbtouchscreen驱动是怎么被映射到的,这个过程暂时不做深入,作为这个驱动中的第一个接入点就是usbtouch_probe.


  1. static

    int usbtouch_probe(

    struct

    usb_interface *intf,




  2. const

    struct

    usb_device_id *

    id)



  3. {

  4. struct

    usbtouch_usb *usbtouch;

    //usbtouch 设备



  5. struct

    input_dev *input_dev;

    //输入设备



  6. struct

    usb_endpoint_descriptor *endpoint;

    //usb 的端点



  7. struct

    usb_device *udev = interface_to_usbdev(intf);

    //从usb接口获取对应的设备



  8. struct

    usbtouch_device_info *type;

    //这个就是上面说的真正的 driver info了


  9. endpoint = usbtouch_get_input_endpoint(intf->cur_altsetting);

    //获取端点

  10. if (!endpoint)

  11. return -ENXIO;
  12. usbtouch = kzalloc(

    sizeof(

    struct usbtouch_usb), GFP_KERNEL);

  13. input_dev = input_allocate_device();

    //分配内存,申请input 设备结构
  14. type = &usbtouch_dev_info[

    id->driver_info];

    // 这里就用到了 上面说到的枚举值了, 真正的info 是放在这个数组里面的!

  15. usbtouch->irq = usb_alloc_urb(

    0, GFP_KERNEL);

    //分配了一个urb 用于 获得触摸屏设备返回的触摸事件的数据,urb的概念可参考usb driver


  16. if (!usbtouch->irq) {
  17. dbg(

    “%s – usb_alloc_urb failed: usbtouch->irq”, __func__);

  18. goto out_free_buffers;
  19. }

  20. //往下都是一些分配内存,input注册,初始化操作了
  21. input_dev->evbit[

    0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);

    //这里是就是input设备触摸坐标的初始化赋值了,为ABS 绝对坐标

  22. input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
  23. input_set_abs_params(input_dev, ABS_X, type->min_xc, type->max_xc,

    0,

    0);

  24. input_set_abs_params(input_dev, ABS_Y, type->min_yc, type->max_yc,

    0,

    0);


  25. if (usb_endpoint_type(endpoint) == USB_ENDPOINT_XFER_INT)
  26. usb_fill_int_urb(usbtouch->irq, udev,
  27. usb_rcvintpipe(udev, endpoint->bEndpointAddress),
  28. usbtouch->data, type->rept_size,
  29. usbtouch_irq, usbtouch, endpoint->bInterval);

  30. else
  31. usb_fill_bulk_urb(usbtouch->irq, udev,
  32. usb_rcvbulkpipe(udev, endpoint->bEndpointAddress),
  33. usbtouch->data, type->rept_size,
  34. usbtouch_irq, usbtouch);

    //初始化urb的回调函数为 usbtouch_irq
  35. usbtouch->irq->dev = udev;
  36. usbtouch->irq->transfer_dma = usbtouch->data_dma;
  37. usbtouch->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
  38. }



usbtouch_device_info:

这个就是上面driver_info 以及usbtouch_probe 中抽取的驱动模块的info数组,不同的usbtouchscreen 注册的时候就是注册了一个枚举值,这个值就是

usbtouch_dev_info

数组的第几元素.


  1. struct usbtouch_device_info {

  2. int min_xc, max_xc;

  3. int min_yc, max_yc;

  4. int min_press, max_press;

  5. int rept_size;

  6. /*

  7. * Always service the USB devices irq not just when the input device is

  8. * open. This is useful when devices have a watchdog which prevents us

  9. * from periodically polling the device. Leave this unset unless your

  10. * touchscreen device requires it, as it does consume more of the USB

  11. * bandwidth.

  12. */

  13. bool irq_always;

  14. void (*process_pkt) (

    struct

    usbtouch_usb *usbtouch, unsigned

    char

    char *pkt,

    int len);

    //这个函数指针是用来接收处理中断的。







  15. /*

  16. * used to get the packet len. possible return values:

  17. * > 0: packet len

  18. * = 0: skip one byte

  19. * < 0: -return value more bytes needed

  20. */

  21. int  (*get_pkt_len) (unsigned

    char

    char *pkt,

    int len);




  22. int  (*read_data)   (

    struct

    usbtouch_usb *usbtouch, unsigned

    char

    char *pkt);





  23. int  (*alloc)       (

    struct

    usbtouch_usb *usbtouch);



  24. int  (*init)        (

    struct

    usbtouch_usb *usbtouch);



  25. void (*exit)        (

    struct

    usbtouch_usb *usbtouch);


  26. };


usbtouch_dev_info

数组:


  1. static

    struct usbtouch_device_info usbtouch_dev_info[] = {


  2. #ifdef CONFIG_TOUCHSCREEN_USB_EGALAX
  3. [DEVTYPE_EGALAX] = {

  4. .min_xc        =

    0x

    0,



  5. .max_xc        =

    0x

    0

    7ff,




  6. .min_yc        =

    0x

    0,



  7. .max_yc        =

    0x

    0

    7ff,




  8. .rept_size    =

    1

    6,



  9. .process_pkt    = usbtouch_process_multi,

    //用于中断回调函数,用于处理中断,得到input的event,上传数据


  10. .get_pkt_len    = egalax_get_pkt_len,

  11. .read_data    = egalax_read_data,

    //用于中断回调函数,用于读取数据

  12. },

  13. #endif

  14. #ifdef CONFIG_TOUCHSCREEN_USB_IRTOUCH
  15. [DEVTYPE_IRTOUCH] = {

  16. .min_xc        =

    0x

    0,



  17. .max_xc        =

    0x

    0fff,



  18. .min_yc        =

    0x

    0,



  19. .max_yc        =

    0x

    0fff,



  20. .rept_size    =

    8,


  21. .read_data    = irtouch_read_data,
  22. },

  23. #endif
  24. };


可以看到这个数组的成员都是以前面说到的注册枚举值来区分的!这些x,y 参数以及回调函数,都在上面说到的 usbtouch_probe 中被抽离出来使用.




usbtouch_irq:

这个函数作为中断响应函数,在上面的

usbtouch_probe

中初始化,看下函数主要实现:


  1. static

    void usbtouch_irq(

    struct

    urb *urb)



  2. {
  3. usbtouch->type->process_pkt(usbtouch, usbtouch->data, urb->actual_length);

  4. //这个type的类型就是 usbtouch_device_info,此时的process_pkt指针自然指向的是上面对应的函数,如果此时是触发的设备type为 DEVTYPE_EGALAX,那么这里调用的 usbtouch_process_multi

  5. //如果此时是DEVTYPE_IRTOUCH 那么就是执行 usbtouch_process_pkt函数,因为usbtouch_probe中:

  6. //    if (!type->process_pkt)

  7. //        type->process_pkt = usbtouch_process_pkt;
  8. }


接下来的都会调用到usbtouch_process_pkt中,通过type->read_data,和上面一样的指针读取,然后调用input_report_key发送,input_sync用于同步.

关于usbtouchscreen的驱动部分就分析到这里。



版权声明:本文为zzb2760715357原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。