Linux USB触摸屏驱动注解


Linux USB触摸屏驱动注解

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;

       /* some devices are ignored */
       if (id->driver_info == DEVTYPE_IGNORE)
              return -ENODEV;
       interface = intf->cur_altsetting;
     /* usb 设备有一个configuration 的概念,表示配置,一个设备可以有多个配置,但只能同时激活一个,如:一些设备可以下载固件,或可以设置不同的全局模式,cur_altsetting 就是表示的当前的这个setting,或者说设置。可以查看原码中usb_interface 结构定义的说明部分。从说明中可以看到一个接口可以有多种setting*/
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 %04x:%04x",
                      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);
       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);
/* 设置支持的输入子系统事件:botton,key,press*/
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;
/*
构建好一个urb,在open方法中会实现向usb core递交urb, usbtouch_irq 回调函数。
这里是两个DMA 相关的flag,一个是URB_NO_SETUP_DMA_MAP,而另一个是
URB_NO_TRANSFER_DMA_MAP.注意这两个是不一样的,前一个是专门为控制传输准备的,因为只有控制传输需要有这么一个setup 阶段需要准备一个setup packet。
transfer_buffer 是给各种传输方式中真正用来数据传输的,而setup_packet 仅仅是在控制传输中发送setup 的包,控制传输除了setup 阶段之外,也会有数据传输阶段,这一阶段要传输数据还是得靠transfer_buffer,而如果使用dma 方式,那么就是使用transfer_dma.
因为这里使用了mouse->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP,所以应该给urb的transfer_dma赋值。所以用了:usbtouch->irq->transfer_dma = usbtouch->data_dma;
*/
  /* 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;
              }
       }
       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;
  //向input系统注册input设备

  三、 open部分
  当应用层打开触摸屏设备时,usbtouch_open将被调用
  static int usbtouch_open(struct input_dev *input)
{
       struct usbtouch_usb *usbtouch = input_get_drvdata(input);
       usbtouch->irq->dev = usbtouch->udev;
       if (usb_submit_urb(usbtouch->irq, GFP_KERNEL))
              return -EIO;
       return 0;
}
  //向usb core递交了在probe中构建好的中断urb,注意:此处是成功递交给usb core以后就返回,而不是等到从设备取得数据。

  四、 urb回调函数处理部分
  当出现传输错误或获取到触摸屏数据后,urb回调函数将被执行
static void usbtouch_irq(struct urb *urb)
{
       struct usbtouch_usb *usbtouch = urb->context;
       int retval;
       switch (urb->status) {
       case 0:
              /* success */
              break;
       case -ETIME:
              /* this urb is timing out */
              dbg("%s - urb timed out - was the device unplugged?",
                  __func__);
              return;
       case -ECONNRESET:
       case -ENOENT:
       case -ESHUTDOWN:
              /* this urb is terminated, clean up */
              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;
       }
       usbtouch->type->process_pkt(usbtouch, usbtouch->data, urb->actual_length);
//向input系统报告坐标和按键事件
exit:
       retval = usb_submit_urb(urb, GFP_ATOMIC); //重新发送URB
       if (retval)
              err("%s - usb_submit_urb failed with result: %d",
                  __func__, retval);
}
  input_sync(dev);
  //最后需要向事件接受者发送一个完整的报告。这是input系统的要求。

你可能感兴趣的:(Linux USB触摸屏驱动注解)