usb 接口触摸屏驱动

以前写的 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");

你可能感兴趣的:(usb 接口触摸屏驱动)