以前写的 USB 接口的触摸屏驱动,那段时间简单的看了下 USB 协议的一些东西,主要是 HID 相关的,代码记录:
/* Created by_fire 2012.2.13 */ #include <linux/kernel.h> #include <linux/errno.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/kref.h> #include <linux/uaccess.h> #include <linux/usb.h> #include <linux/mutex.h> #include <linux/input.h> #include <linux/usb/input.h> #define DEBUG 0 #if DEBUG #define usb_ts_dbg(fmt, arg...) printk(fmt, ##arg) #else #define usb_ts_dbg(arg...) #endif /* Define these values to match your devices */ #define USB_SKEL_VENDOR_ID 0x0eef #define USB_SKEL_PRODUCT_ID 0x0001 /* table of devices that work with this driver */ static const struct usb_device_id usb_ts_table[] = { { USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, usb_ts_table); static int swap_xy; module_param(swap_xy, bool, 0644); MODULE_PARM_DESC(swap_xy, "If set X and Y axes are swapped."); struct usb_ts; struct usb_ts_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 usb_ts *usbtouch, unsigned 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 *pkt, int len); int (*read_data) (struct usb_ts *usbtouch, unsigned char *pkt); int (*init) (struct usb_ts *usbtouch); void (*exit) (struct usb_ts *usbtouch); }; /* a usbtouch device */ struct usb_ts { unsigned char *data; dma_addr_t data_dma; unsigned char *buffer; int buf_len; struct urb *int_urb; struct usb_interface *interface; struct input_dev *input; struct usb_ts_device_info *dev_info; char name[128]; char phys[64]; void *priv; int x, y; int touch, press; }; static int usb_ts_read_data(struct usb_ts *dev, unsigned char *pkt) { usb_ts_dbg("%s\n", __func__); dev->x = ((pkt[3] & 0x0F) << 7) | (pkt[4] & 0x7F); dev->y = ((pkt[1] & 0x0F) << 7) | (pkt[2] & 0x7F); dev->touch = pkt[0] & 0x01; dev->press = 0x01; return 1; } static void usb_ts_process_pkt(struct usb_ts *usbtouch, unsigned char *pkt, int len) { usb_ts_dbg("%s\n", __func__); struct usb_ts_device_info *dev_info = usbtouch->dev_info; int i; if(!(pkt[0] & 0x80)) return; #if 0 for(i = 0; i < len; i++) printk("pkt[%d] = %02x ", i, pkt[i]); printk("\n"); #endif if (!dev_info->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); } usb_ts_dbg("x = %d, y = %d, p = %d\n", usbtouch->x, usbtouch->y, usbtouch->touch); if (dev_info->max_press) input_report_abs(usbtouch->input, ABS_PRESSURE, usbtouch->press); input_sync(usbtouch->input); } static struct usb_ts_device_info usb_ts_dev_info[] = { [0] = { .min_xc = 50, .max_xc = 1960, .min_yc = 140, .max_yc = 1900, .min_press = 0x0, .max_press = 0x1, .rept_size = 16, .process_pkt = usb_ts_process_pkt, .read_data = usb_ts_read_data, }, }; static void usb_ts_irq(struct urb *urb) { usb_ts_dbg("%s\n", __func__); struct usb_ts *usbtouch = urb->context; int retval; switch (urb->status) { case 0: /* success */ break; case -ETIME: /* this urb is timing out */ usb_ts_dbg("%s - urb timed out - was the device unplugged?", __func__); return; case -ECONNRESET: case -ENOENT: case -ESHUTDOWN: case -EPIPE: /* this urb is terminated, clean up */ usb_ts_dbg("%s - urb shutting down with status: %d", __func__, urb->status); return; default: usb_ts_dbg("%s - nonzero urb status received: %d", __func__, urb->status); goto exit; } usbtouch->dev_info->process_pkt(usbtouch, usbtouch->data, urb->actual_length); exit: retval = usb_submit_urb(urb, GFP_ATOMIC); if (retval) err("%s - usb_submit_urb failed with result: %d", __func__, retval); } static int usb_ts_open(struct input_dev *input) { usb_ts_dbg("%s\n", __func__); struct usb_ts *usbtouch = input_get_drvdata(input); usbtouch->int_urb->dev = interface_to_usbdev(usbtouch->interface); if (!usbtouch->dev_info->irq_always) { if (usb_submit_urb(usbtouch->int_urb, GFP_KERNEL)) return -EIO; } return 0; } static void usb_ts_close(struct input_dev *input) { usb_ts_dbg("%s\n", __func__); struct usb_ts *usbtouch = input_get_drvdata(input); if (!usbtouch->dev_info->irq_always) usb_kill_urb(usbtouch->int_urb); } static void usb_ts_free_buffers(struct usb_device *udev, struct usb_ts *usbtouch) { usb_ts_dbg("%s\n", __func__); usb_free_coherent(udev, usbtouch->dev_info->rept_size, usbtouch->data, usbtouch->data_dma); kfree(usbtouch->buffer); } static struct usb_endpoint_descriptor *usb_ts_get_input_endpoint(struct usb_host_interface *interface) { usb_ts_dbg("%s\n", __func__); int i; for (i = 0; i < interface->desc.bNumEndpoints; i++) if (usb_endpoint_dir_in(&interface->endpoint[i].desc)) return &interface->endpoint[i].desc; return NULL; } static int usb_ts_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct usb_ts *usbtouch; struct input_dev *input_dev; struct usb_endpoint_descriptor *endpoint; struct usb_device *udev = interface_to_usbdev(intf); struct usb_ts_device_info *dev_info; int err = -ENOMEM; usb_ts_dbg("%s 1\n", __func__); endpoint = usb_ts_get_input_endpoint(intf->cur_altsetting); if (!endpoint) return -ENXIO; usbtouch = kzalloc(sizeof(struct usb_ts), GFP_KERNEL); input_dev = input_allocate_device(); if (!usbtouch || !input_dev) goto out_free; dev_info = &usb_ts_dev_info[0]; usbtouch->dev_info = dev_info; if (!dev_info->process_pkt) dev_info->process_pkt = usb_ts_process_pkt; usbtouch->data = usb_alloc_coherent(udev, dev_info->rept_size, GFP_KERNEL, &usbtouch->data_dma); if (!usbtouch->data) goto out_free; if (dev_info->get_pkt_len) { usbtouch->buffer = kmalloc(dev_info->rept_size, GFP_KERNEL); if (!usbtouch->buffer) goto out_free_buffers; } usbtouch->int_urb = usb_alloc_urb(0, GFP_KERNEL); if (!usbtouch->int_urb) { usb_ts_dbg("%s - usb_alloc_urb failed: usbtouch->irq", __func__); goto out_free_buffers; } usb_ts_dbg("%s 2\n", __func__); usbtouch->interface = intf; usbtouch->input = input_dev; 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 = usb_ts_open; input_dev->close = usb_ts_close; usb_ts_dbg("%s 3\n", __func__); input_dev->evbit[0] = BIT_MASK(EV_SYN) | 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, dev_info->min_xc, dev_info->max_xc, 0, 0); input_set_abs_params(input_dev, ABS_Y, dev_info->min_yc, dev_info->max_yc, 0, 0); if (dev_info->max_press) input_set_abs_params(input_dev, ABS_PRESSURE, dev_info->min_press, dev_info->max_press, 0, 0); if (usb_endpoint_type(endpoint) == USB_ENDPOINT_XFER_INT) usb_fill_int_urb(usbtouch->int_urb, udev, usb_rcvintpipe(udev, endpoint->bEndpointAddress), usbtouch->data, dev_info->rept_size, usb_ts_irq, usbtouch, endpoint->bInterval); else usb_fill_bulk_urb(usbtouch->int_urb, udev, usb_rcvbulkpipe(udev, endpoint->bEndpointAddress), usbtouch->data, dev_info->rept_size, usb_ts_irq, usbtouch); usbtouch->int_urb->dev = udev; usbtouch->int_urb->transfer_dma = usbtouch->data_dma; usbtouch->int_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; /* device specific init */ if (dev_info->init) { err = dev_info->init(usbtouch); if (err) { usb_ts_dbg("%s - type->init() failed, err: %d", __func__, err); goto out_free_urb; } } err = input_register_device(usbtouch->input); if (err) { usb_ts_dbg("%s - input_register_device failed, err: %d", __func__, err); goto out_do_exit; } usb_set_intfdata(intf, usbtouch); if (usbtouch->dev_info->irq_always) { err = usb_submit_urb(usbtouch->int_urb, GFP_KERNEL); if (err) { err("%s - usb_submit_urb failed with result: %d", __func__, err); goto out_unregister_input; } } usb_ts_dbg("%s 4\n", __func__); return 0; out_unregister_input: input_unregister_device(input_dev); input_dev = NULL; out_do_exit: if (dev_info->exit) dev_info->exit(usbtouch); out_free_urb: usb_free_urb(usbtouch->int_urb); out_free_buffers: usb_ts_free_buffers(udev, usbtouch); out_free: input_free_device(input_dev); kfree(usbtouch); return err; } static void usb_ts_disconnect(struct usb_interface *intf) { struct usb_ts *usbtouch = usb_get_intfdata(intf); usb_ts_dbg("%s - called", __func__); if (!usbtouch) return; usb_ts_dbg("%s - usbtouch is initialized, cleaning up", __func__); usb_set_intfdata(intf, NULL); /* this will stop IO via close */ input_unregister_device(usbtouch->input); usb_free_urb(usbtouch->int_urb); if (usbtouch->dev_info->exit) usbtouch->dev_info->exit(usbtouch); usb_ts_free_buffers(interface_to_usbdev(intf), usbtouch); kfree(usbtouch); } static struct usb_driver usb_ts_driver = { .name = "usb_ts", .probe = usb_ts_probe, .disconnect = usb_ts_disconnect, .id_table = usb_ts_table, }; static int __init usb_ts_init(void) { int result; /* register this driver with the USB subsystem */ result = usb_register(&usb_ts_driver); if (result) err("usb_register failed. Error number %d", result); return result; } static void __exit usb_ts_exit(void) { /* deregister this driver with the USB subsystem */ usb_deregister(&usb_ts_driver); } module_init(usb_ts_init); module_exit(usb_ts_exit); MODULE_AUTHOR("fire <[email protected]>"); MODULE_DESCRIPTION("Gochen USB Touchscreen driver"); MODULE_LICENSE("GPL");