Analysis of USB Device Driver

Analysis of USB Device Driver

Click the above blue text to follow us!

Introduction

In previous articles, we have discussed the USB device driver framework. In this article, we will look at a specific device driver to further familiarize ourselves with these concepts. We will take the USB mouse as an example.

Analysis of USB Device Driver

Kernel Version: 4.20.12

Driver Path: /drivers/hid/usbhid/usbmouse.c

(1) Load and Unload Functions

static const struct usb_device_id usb_mouse_id_table[] = {  { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,    USB_INTERFACE_PROTOCOL_MOUSE) },  { }  /* Terminating entry */};
MODULE_DEVICE_TABLE (usb, usb_mouse_id_table);
static struct usb_driver usb_mouse_driver = {  .name    = "usbmouse",  .probe    = usb_mouse_probe,  .disconnect  = usb_mouse_disconnect,  .id_table  = usb_mouse_id_table,};
module_usb_driver(usb_mouse_driver);

The mouse driver is loaded based on the USB interface information, so it does not require PID and VID.

(2) probe()

static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id){  struct usb_device *dev = interface_to_usbdev(intf);  struct usb_host_interface *interface;  struct usb_endpoint_descriptor *endpoint;  struct usb_mouse *mouse;  struct input_dev *input_dev;  int pipe, maxp;  int error = -ENOMEM;
  interface = intf->cur_altsetting;
  if (interface->desc.bNumEndpoints != 1)    return -ENODEV;    // Get endpoint descriptor  endpoint = &interface->endpoint[0].desc;    // Check if the endpoint is interrupt input  if (!usb_endpoint_is_int_in(endpoint))    return -ENODEV;   // Get communication pipe via endpoint address  pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);   // Get maximum data packet size  maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
  mouse = kzalloc(sizeof(struct usb_mouse), GFP_KERNEL);  input_dev = input_allocate_device(); // Allocate an input device
    // Allocate a buffer with the same size as DMA    // mouse->data_dma: the DMA address corresponding to the buffer, which is of type u32  mouse->data = usb_alloc_coherent(dev, 8, GFP_ATOMIC, &mouse->data_dma);
   // Allocate URB  mouse->irq = usb_alloc_urb(0, GFP_KERNEL);
  mouse->usbdev = dev; // Save usb_device  mouse->dev = input_dev; // Save input_device    // Device manufacturer and product name  if (dev->manufacturer)    strlcpy(mouse->name, dev->manufacturer, sizeof(mouse->name));
  if (dev->product) {    if (dev->manufacturer)      strlcat(mouse->name, " ", sizeof(mouse->name));    strlcat(mouse->name, dev->product, sizeof(mouse->name));  }
  if (!strlen(mouse->name))    snprintf(mouse->name, sizeof(mouse->name),       "USB HIDBP Mouse %04x:%04x",       le16_to_cpu(dev->descriptor.idVendor),       le16_to_cpu(dev->descriptor.idProduct));
  usb_make_path(dev, mouse->phys, sizeof(mouse->phys));  strlcat(mouse->phys, "/input0", sizeof(mouse->phys));    // Set input device  input_dev->name = mouse->name;  input_dev->phys = mouse->phys;  usb_to_input_id(dev, &input_dev->id);  input_dev->dev.parent = &intf->dev;
  input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);  input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |    BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);  input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);  input_dev->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_SIDE) |    BIT_MASK(BTN_EXTRA);  input_dev->relbit[0] |= BIT_MASK(REL_WHEEL);
  input_set_drvdata(input_dev, mouse);    // Set the open and close functions for the input device  input_dev->open = usb_mouse_open;  input_dev->close = usb_mouse_close;    // Initialize URB  usb_fill_int_urb(mouse->irq, dev, pipe, mouse->data,       (maxp > 8 ? 8 : maxp),       usb_mouse_irq, mouse, endpoint->bInterval);  mouse->irq->transfer_dma = mouse->data_dma; // DMA address  mouse->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;    // Register input device  error = input_register_device(mouse->dev);    // Save private data  usb_set_intfdata(intf, mouse);  return 0; // Omitted error handling...}

The above code initializes the struct mouse. Since the mouse is an input device, it uses the input subsystem to initialize an input_dev.

The mouse is an input device, so it only needs to use the input endpoint. The data volume for the mouse is relatively small, so it uses interrupt transfer. As mentioned before, this type of transfer means that the USB host will continuously poll the device at specified intervals to check for data transmission.

  • disconnect()

static void usb_mouse_disconnect(struct usb_interface *intf){  struct usb_mouse *mouse = usb_get_intfdata (intf);    // Release various resources  usb_set_intfdata(intf, NULL);  if (mouse) {    usb_kill_urb(mouse->irq);    input_unregister_device(mouse->dev); // Unregister input device    usb_free_urb(mouse->irq);    usb_free_coherent(interface_to_usbdev(intf), 8, mouse->data, mouse->data_dma);    kfree(mouse);  }}

(3) Input Device Open and Close

static int usb_mouse_open(struct input_dev *dev){  struct usb_mouse *mouse = input_get_drvdata(dev);
  mouse->irq->dev = mouse->usbdev;    // Submit URB  if (usb_submit_urb(mouse->irq, GFP_KERNEL))    return -EIO;
  return 0;}
static void usb_mouse_close(struct input_dev *dev){  struct usb_mouse *mouse = input_get_drvdata(dev);    // Cancel URB  usb_kill_urb(mouse->irq);}

URB is submitted to the endpoint only when the input device is opened, which officially starts communication.

(4) URB Transfer Completion Callback

static void usb_mouse_irq(struct urb *urb){  struct usb_mouse *mouse = urb->context;  signed char *data = mouse->data;  struct input_dev *dev = mouse->dev;  int status;
  switch (urb->status) {  case 0:      /* success */    break;  case -ECONNRESET:  /* unlink */  case -ENOENT:  case -ESHUTDOWN:    return;  /* -EPIPE:  should clear the halt */  default:    /* error */    goto resubmit;  }    // Report events to upper layers  input_report_key(dev, BTN_LEFT,   data[0] & 0x01);  input_report_key(dev, BTN_RIGHT,  data[0] & 0x02);  input_report_key(dev, BTN_MIDDLE, data[0] & 0x04);  input_report_key(dev, BTN_SIDE,   data[0] & 0x08);  input_report_key(dev, BTN_EXTRA,  data[0] & 0x10);
  input_report_rel(dev, REL_X,     data[1]);  input_report_rel(dev, REL_Y,     data[2]);  input_report_rel(dev, REL_WHEEL, data[3]);
  input_sync(dev); resubmit:    // Resubmit URB  status = usb_submit_urb (urb, GFP_ATOMIC);  //......}

After reporting the input events, the URB is submitted to the endpoint again, forming a loop listening process.

Conclusion

The USB mouse is a relatively simple device, consisting of the USB device driver framework combined with the input subsystem. It is relatively easy to understand for those just learning about USB device drivers. Our main focus should be on understanding the overall framework, while more detailed aspects are related to specific devices.

Analysis of USB Device Driver

END

Previous Recommendations

  • Analysis of USB Host Controller Driver

  • USB Driver Framework (V)

  • USB Driver Framework (IV)

  • USB Driver Framework (III)

Analysis of USB Device Driver

Long press to recognize the QR code in the image to follow us

Great article! Click to like!

Leave a Comment