加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

Linux USB触摸屏驱动注解

(2012-10-10 10:54:26)
标签:

杂谈

分类: linux_driver
    Linux USB触摸屏驱动注解  参考2.6.31版本中的driver/usb/input/usbtouchscreen.c。驱动可分为几个部分:驱动加载部分、probe部分、open部分、urb回调函数处理部分。

 

一、 驱动加载部分

static int __init usbtouch _init(void)

{

    return usb_register(&usbtouch_driver);//注册触摸屏驱动

}

 

其中usbtouch _driver的定义为:

static struct usb_driver usbtouch _driver = {

      .name = "usbtouchscreen",

      .probe = usbtouch_probe,

      .disconnect = usbtouch_disconnect,

      .id_table = usbtouch_devices,

};

 

    如果注册成功的话,将会调用usbtouch_probe。那么什么时候才算注册成功呢?和其它驱动注册过程一样,只有在其对应的“总线”上发现匹配的“设备”才会调用probe。当设备的接口类、接口子类、接口协议匹配驱动时驱动才会调用probe方法。

 

static struct usb_device_id usbtouch_devices[] = {

#ifdef CONFIG_TOUCHSCREEN_USB_EGALAX

       // ignore the HID capable devices, handled by usbhid */

       {USB_DEVICE_HID_CLASS(0x0eef, 0x0001), .driver_info = DEVTYPE_IGNORE},

       {USB_DEVICE_HID_CLASS(0x0eef, 0x0002), .driver_info = DEVTYPE_IGNORE},

 

       // normal device IDs */

       {USB_DEVICE(0x3823, 0x0001), .driver_info = DEVTYPE_EGALAX},

       {USB_DEVICE(0x3823, 0x0002), .driver_info = DEVTYPE_EGALAX},

       {USB_DEVICE(0x0123, 0x0001), .driver_info = DEVTYPE_EGALAX},

       {USB_DEVICE(0x0eef, 0x0001), .driver_info = DEVTYPE_EGALAX},

       {USB_DEVICE(0x0eef, 0x0002), .driver_info = DEVTYPE_EGALAX},

       {USB_DEVICE(0x1234, 0x0001), .driver_info = DEVTYPE_EGALAX},

       {USB_DEVICE(0x1234, 0x0002), .driver_info = DEVTYPE_EGALAX},

#endif

}

 

// usbtouch driver支持的devices列表,

USB_DEVICE(0x1234, 0x0002), .driver_info = DEVTYPE_EGALAX

USB 设备的VID =  0x1234, PID  = 0x0002,

driver_info 的值是DEVTYPE_EGALAX,根据其值在usbtouch_dev_info中查找driver 的相关设置。

 

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

}

 

二、probe部分

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_host_interface *interface;

    struct usb_endpoint_descriptor *endpoint;

    struct usb_device *udev = interface_to_usbdev(intf);

    struct usbtouch_device_info *type;

    int err = -ENOMEM;

  

  // usb 设备有一个configuration 的概念,表示配置,一个设备可以有多个配置,但只能同时激活一个,如:一些设备可以下载固件,或可以设置不同的全局模式,cur_altsetting 就是表示的当前的这个setting,或者说设置。可以查看原码中usb_interface 结构定义的说明部分。从说明中可以看到一个接口可以有多种setting*/ 

        if (id->driver_info == DEVTYPE_IGNORE)

        return -ENODEV;

 

        interface = intf->cur_altsetting;

 

       //端点0描述符,此处的0表示中断端点

        endpoint = &interface->endpoint[0].desc;//端点0描述符,此处的0表示中断端点

 

  usbtouch = kzalloc(sizeof(struct usbtouch_usb), GFP_KERNEL);

    input_dev = input_allocate_device();

    if (!usbtouch || !input_dev)

       goto out_free;

    

    //usbtouch分配内存空间,申请input 设备*/

    type = &usbtouch_dev_info[id->driver_info];

    usbtouch->type = type;

    if (!type->process_pkt)

        type->process_pkt = usbtouch_process_pkt;

      
       
usbtouch->data = usb_buffer_alloc(udev, type->rept_size,GFP_KERNEL, &usbtouch->data_dma);

    if (!usbtouch->data)

        goto out_free;

 

    if (type->get_pkt_len) {

        usbtouch->buffer = kmalloc(type->rept_size, GFP_KERNEL);

        if (!usbtouch->buffer)

           goto out_free_buffers;

    }

 

  //申请用于urb用于数据传输的内存,注意:这里将返回“usbtouch->data

  //usbtouch->data:记录了用于普通传输用的内存指针

  //usbtouch->buffer:记录了用于存储读取到的数据的内存指针 

  usbtouch->irq = usb_alloc_urb(0, GFP_KERNEL);

  if (!usbtouch->irq) {

      dbg("%s - usb_alloc_urb failed: usbtouch->irq", __func__);

            goto out_free_buffers;

  }

  usbtouch->usbdev = udev;

   usbtouch->input = input_dev;

 

  //struct usb_device 中有一个成员struct usb_device_descriptor,struct usb_device_descriptor 中的成员__u16 bcdDevice,表示的是制造商指定的产品的版本号,制造商id 和产品id 来标志一个设备.bcdDevice 一共16 ,是以bcd码的方式保存的信息,也就是说,4 位代表一个十进制的数,比如0011 0110 1001 0111 就代表的3697.业内为每家公司编一个号,这样便于管理,比如三星的编号就是0x0839,那么三星的产品中就会在其设备描述符中idVendor 的烙上0x0839.而三星自己的每种产品也会有个编号,Digimax 410 对应的编号就是0x000a,bcdDevice_lo bcdDevice_hi 共同组成一个具体设备的编号(device release number),bcd 就意味着这个编号是二进制的格式.*/

   if (udev->manufacturer)

        strlcpy(usbtouch->name, udev->manufacturer, sizeof(usbtouch->name));

 

    if (udev->product) {

        if (udev->manufacturer)

            strlcat(usbtouch->name, " ", sizeof(usbtouch->name));

            strlcat(usbtouch->name, udev->product, sizeof(usbtouch->name));

    }

 

    if (!strlen(usbtouch->name))

        snprintf(usbtouch->name, sizeof(usbtouch->name),

              "USB Touchscreen x:x",

               le16_to_cpu(udev->descriptor.idVendor),

               le16_to_cpu(udev->descriptor.idProduct));

 

    usb_make_path(udev, usbtouch->phys, sizeof(usbtouch->phys));

    strlcat(usbtouch->phys, "/input0", sizeof(usbtouch->phys));

 

    input_dev->name = usbtouch->name;

    input_dev->phys = usbtouch->phys;

    usb_to_input_id(udev, &input_dev->id);

    input_dev->dev.parent = &intf->dev;

 

    input_set_drvdata(input_dev, usbtouch);

 

   //设置打开和关闭设备*/ 

        input_dev->open = usbtouch_open;

    input_dev->close = usbtouch_close;

 

     input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);

    input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);

 

       // 设置支持的输入子系统事件:botton,key,press*/

    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 (type->max_press)

        input_set_abs_params(input_dev, ABS_PRESSURE, type->min_press,type->max_press, 0, 0);

 

       //构建好一个urb,在open方法中会实现向usb core递交urb, usbtouch_irq 回调函数。这里是两个DMA 相关的flag,一个是URB_NO_SETUP_DMA_MAP,而另一个是URB_NO_TRANSFER_DMA_MAP.注意这两个是不一样的,前一个是专门为控制传输准备的,因为只有控制传输需要有这么一个setup 阶段需要准备一个setup packettransfer_buffer 是给各种传输方式中真正用来数据传输的,setup_packet 仅仅是在控制传输中发送setup 的包,控制传输除了setup 阶段之外,也会有数据传输阶段,这一阶段要传输数据还是得靠transfer_buffer,而如果使用dma 方式,那么就是使用transfer_dma.因为这里使用了mouse->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP,所以应该给urbtransfer_dma赋值。所以用了:usbtouch->irq->transfer_dma = usbtouch->data_dma;*/

         usb_fill_int_urb(usbtouch->irq, usbtouch->udev,

                 usb_rcvintpipe(usbtouch->udev, endpoint->bEndpointAddress),

                 usbtouch->data, type->rept_size,

                 usbtouch_irq, usbtouch, endpoint->bInterval);

 

     usbtouch->irq->dev = usbtouch->udev;

     usbtouch->irq->transfer_dma = usbtouch->data_dma;

     usbtouch->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;

 

    // device specific init */

      if (type->init) {

           err = type->init(usbtouch);

           if (err) {

                dbg("%s - type->init() failed, err: %d", __func__, err);

                goto out_free_buffers;

           }

       }

 

            //input系统注册input设备

       err = input_register_device(usbtouch->input);

       if (err) {

              dbg("%s - input_register_device failed, err: %d", __func__, err);

              goto out_free_buffers;

       }

 

       usb_set_intfdata(intf, usbtouch);

 

       return 0;

   

三、 open部分

当应用层打开触摸屏设备时,usbtouch_open将被调用

static int usbtouch_open(struct input_dev *input)

{

       struct usbtouch_usb *usbtouch = input_get_drvdata(input);

 

       usbtouch->irq->dev = usbtouch->udev;

 

             //usb core递交了在probe中构建好的中断urb,注意:此处是成功递交给usb core以后就返回,而不是等到从设备取得数据。

       if (usb_submit_urb(usbtouch->irq, GFP_KERNEL))

            return -EIO;

 

       return 0;

}

    

 

四、 urb回调函数处理部分

当出现传输错误或获取到触摸屏数据后,urb回调函数将被执行

static void usbtouch_irq(struct urb *urb)

{

       struct usbtouch_usb *usbtouch = urb->context;

       int retval;

 

       switch (urb->status) {

       case 0:

             

              break;

       case -ETIME:

             

              dbg("%s - urb timed out - was the device unplugged?",

                  __func__);

              return;

       case -ECONNRESET:

       case -ENOENT:

       case -ESHUTDOWN:

             

              dbg("%s - urb shutting down with status: %d",

                  __func__, urb->status);

              return;

       default:

              dbg("%s - nonzero urb status received: %d",

                  __func__, urb->status);

              goto exit;

       }

 

            //input系统报告坐标和按键事件

       usbtouch->type->process_pkt(usbtouch, usbtouch->data, urb->actual_length);

exit:

       retval = usb_submit_urb(urb, GFP_ATOMIC); //重新发送URB

       if (retval)

              err("%s - usb_submit_urb failed with result: %d",

                  __func__, retval);

}

 

static void usbtouch_process_pkt(struct usbtouch_usb *usbtouch, unsigned char *pkt, int len)
{
               struct usbtouch_device_info *type = usbtouch->type;

 

                if (!type->read_data(usbtouch, pkt))
                             return;

 

                input_report_key(usbtouch->input, BTN_TOUCH, usbtouch->touch);

                if (swap_xy) {
                              input_report_abs(usbtouch->input, ABS_X, usbtouch->y);
                              input_report_abs(usbtouch->input, ABS_Y, usbtouch->x);
                } else {
                              input_report_abs(usbtouch->input, ABS_X, usbtouch->x);
                              input_report_abs(usbtouch->input, ABS_Y, usbtouch->y);
                 }
                 if (type->max_press)
                               input_report_abs(usbtouch->input, ABS_PRESSURE, usbtouch->press);

 

                //最后需要向事件接受者发送一个完整的报告。这是input系统的要求。
                 input_sync(usbtouch->input);
}

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有