1800 lines · 44.5 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/****************************************************************************** * usbtouchscreen.c * Driver for USB Touchscreens, supporting those devices: *  - eGalax Touchkit *    includes eTurboTouch CT-410/510/700 *  - 3M/Microtouch  EX II series *  - ITM *  - PanJit TouchSet *  - eTurboTouch *  - Gunze AHL61 *  - DMC TSC-10/25 *  - IRTOUCHSYSTEMS/UNITOP *  - IdealTEK URTC1000 *  - General Touch *  - GoTop Super_Q2/GogoPen/PenPower tablets *  - JASTEC USB touch controller/DigiTech DTR-02U *  - Zytronic capacitive touchscreen *  - NEXIO/iNexio *  - Elo TouchSystems 2700 IntelliTouch *  - EasyTouch USB Dual/Multi touch controller from Data Modul * * Copyright (C) 2004-2007 by Daniel Ritz <daniel.ritz@gmx.ch> * Copyright (C) by Todd E. Johnson (mtouchusb.c) * * Driver is based on touchkitusb.c * - ITM parts are from itmtouch.c * - 3M parts are from mtouchusb.c * - PanJit parts are from an unmerged driver by Lanslott Gish * - DMC TSC 10/25 are from Holger Schurig, with ideas from an unmerged *   driver from Marius Vollmer * *****************************************************************************/ //#define DEBUG #include <linux/kernel.h>#include <linux/slab.h>#include <linux/input.h>#include <linux/module.h>#include <linux/usb.h>#include <linux/usb/input.h>#include <linux/hid.h>#include <linux/mutex.h> static bool swap_xy;module_param(swap_xy, bool, 0644);MODULE_PARM_DESC(swap_xy, "If set X and Y axes are swapped."); static bool hwcalib_xy;module_param(hwcalib_xy, bool, 0644);MODULE_PARM_DESC(hwcalib_xy, "If set hw-calibrated X/Y are used if available"); /* device specifc data/functions */struct usbtouch_usb;struct usbtouch_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; 	/*	 * 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 usbtouch_usb *usbtouch, unsigned char *pkt);	int  (*alloc)       (struct usbtouch_usb *usbtouch);	int  (*init)        (struct usbtouch_usb *usbtouch);	void (*exit)	    (struct usbtouch_usb *usbtouch);}; /* a usbtouch device */struct usbtouch_usb {	unsigned char *data;	dma_addr_t data_dma;	int data_size;	unsigned char *buffer;	int buf_len;	struct urb *irq;	struct usb_interface *interface;	struct input_dev *input;	const struct usbtouch_device_info *type;	struct mutex pm_mutex;  /* serialize access to open/suspend */	bool is_open;	char name[128];	char phys[64];	void *priv; 	int x, y;	int touch, press; 	void (*process_pkt)(struct usbtouch_usb *usbtouch, unsigned char *pkt, int len);};  /***************************************************************************** * e2i Part */ #ifdef CONFIG_TOUCHSCREEN_USB_E2Istatic int e2i_init(struct usbtouch_usb *usbtouch){	int ret;	struct usb_device *udev = interface_to_usbdev(usbtouch->interface); 	ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),	                      0x01, 0x02, 0x0000, 0x0081,	                      NULL, 0, USB_CTRL_SET_TIMEOUT); 	dev_dbg(&usbtouch->interface->dev,		"%s - usb_control_msg - E2I_RESET - bytes|err: %d\n",		__func__, ret);	return ret;} static int e2i_read_data(struct usbtouch_usb *dev, unsigned char *pkt){	int tmp = (pkt[0] << 8) | pkt[1];	dev->x  = (pkt[2] << 8) | pkt[3];	dev->y  = (pkt[4] << 8) | pkt[5]; 	tmp = tmp - 0xA000;	dev->touch = (tmp > 0);	dev->press = (tmp > 0 ? tmp : 0); 	return 1;} static const struct usbtouch_device_info e2i_dev_info = {	.min_xc		= 0x0,	.max_xc		= 0x7fff,	.min_yc		= 0x0,	.max_yc		= 0x7fff,	.rept_size	= 6,	.init		= e2i_init,	.read_data	= e2i_read_data,};#endif  /***************************************************************************** * eGalax part */ #ifdef CONFIG_TOUCHSCREEN_USB_EGALAX #ifndef MULTI_PACKET#define MULTI_PACKET#endif #define EGALAX_PKT_TYPE_MASK		0xFE#define EGALAX_PKT_TYPE_REPT		0x80#define EGALAX_PKT_TYPE_DIAG		0x0A static int egalax_init(struct usbtouch_usb *usbtouch){	struct usb_device *udev = interface_to_usbdev(usbtouch->interface);	int ret, i; 	/*	 * An eGalax diagnostic packet kicks the device into using the right	 * protocol.  We send a "check active" packet.  The response will be	 * read later and ignored.	 */ 	u8 *buf __free(kfree) = kmalloc(3, GFP_KERNEL);	if (!buf)		return -ENOMEM; 	buf[0] = EGALAX_PKT_TYPE_DIAG;	buf[1] = 1;	/* length */	buf[2] = 'A';	/* command - check active */ 	for (i = 0; i < 3; i++) {		ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),				      0,				      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,				      0, 0, buf, 3,				      USB_CTRL_SET_TIMEOUT);		if (ret != -EPIPE)			break;	} 	return ret < 0 ? ret : 0;} static int egalax_read_data(struct usbtouch_usb *dev, unsigned char *pkt){	if ((pkt[0] & EGALAX_PKT_TYPE_MASK) != EGALAX_PKT_TYPE_REPT)		return 0; 	dev->x = ((pkt[3] & 0x0F) << 7) | (pkt[4] & 0x7F);	dev->y = ((pkt[1] & 0x0F) << 7) | (pkt[2] & 0x7F);	dev->touch = pkt[0] & 0x01; 	return 1;} static int egalax_get_pkt_len(unsigned char *buf, int len){	switch (buf[0] & EGALAX_PKT_TYPE_MASK) {	case EGALAX_PKT_TYPE_REPT:		return 5; 	case EGALAX_PKT_TYPE_DIAG:		if (len < 2)			return -1; 		return buf[1] + 2;	} 	return 0;} static const struct usbtouch_device_info egalax_dev_info = {	.min_xc		= 0x0,	.max_xc		= 0x07ff,	.min_yc		= 0x0,	.max_yc		= 0x07ff,	.rept_size	= 16,	.get_pkt_len	= egalax_get_pkt_len,	.read_data	= egalax_read_data,	.init		= egalax_init,};#endif /***************************************************************************** * EasyTouch part */ #ifdef CONFIG_TOUCHSCREEN_USB_EASYTOUCH #ifndef MULTI_PACKET#define MULTI_PACKET#endif #define ETOUCH_PKT_TYPE_MASK		0xFE#define ETOUCH_PKT_TYPE_REPT		0x80#define ETOUCH_PKT_TYPE_REPT2		0xB0#define ETOUCH_PKT_TYPE_DIAG		0x0A static int etouch_read_data(struct usbtouch_usb *dev, unsigned char *pkt){	if ((pkt[0] & ETOUCH_PKT_TYPE_MASK) != ETOUCH_PKT_TYPE_REPT &&		(pkt[0] & ETOUCH_PKT_TYPE_MASK) != ETOUCH_PKT_TYPE_REPT2)		return 0; 	dev->x = ((pkt[1] & 0x1F) << 7) | (pkt[2] & 0x7F);	dev->y = ((pkt[3] & 0x1F) << 7) | (pkt[4] & 0x7F);	dev->touch = pkt[0] & 0x01; 	return 1;} static int etouch_get_pkt_len(unsigned char *buf, int len){	switch (buf[0] & ETOUCH_PKT_TYPE_MASK) {	case ETOUCH_PKT_TYPE_REPT:	case ETOUCH_PKT_TYPE_REPT2:		return 5; 	case ETOUCH_PKT_TYPE_DIAG:		if (len < 2)			return -1; 		return buf[1] + 2;	} 	return 0;} static const struct usbtouch_device_info etouch_dev_info = {	.min_xc		= 0x0,	.max_xc		= 0x07ff,	.min_yc		= 0x0,	.max_yc		= 0x07ff,	.rept_size	= 16,	.get_pkt_len	= etouch_get_pkt_len,	.read_data	= etouch_read_data,};#endif /***************************************************************************** * PanJit Part */#ifdef CONFIG_TOUCHSCREEN_USB_PANJITstatic int panjit_read_data(struct usbtouch_usb *dev, unsigned char *pkt){	dev->x = ((pkt[2] & 0x0F) << 8) | pkt[1];	dev->y = ((pkt[4] & 0x0F) << 8) | pkt[3];	dev->touch = pkt[0] & 0x01; 	return 1;} static const struct usbtouch_device_info panjit_dev_info = {	.min_xc		= 0x0,	.max_xc		= 0x0fff,	.min_yc		= 0x0,	.max_yc		= 0x0fff,	.rept_size	= 8,	.read_data	= panjit_read_data,};#endif  /***************************************************************************** * 3M/Microtouch Part */#ifdef CONFIG_TOUCHSCREEN_USB_3M #define MTOUCHUSB_ASYNC_REPORT          1#define MTOUCHUSB_RESET                 7#define MTOUCHUSB_REQ_CTRLLR_ID         10 #define MTOUCHUSB_REQ_CTRLLR_ID_LEN	16 static int mtouch_read_data(struct usbtouch_usb *dev, unsigned char *pkt){	if (hwcalib_xy) {		dev->x = (pkt[4] << 8) | pkt[3];		dev->y = 0xffff - ((pkt[6] << 8) | pkt[5]);	} else {		dev->x = (pkt[8] << 8) | pkt[7];		dev->y = (pkt[10] << 8) | pkt[9];	}	dev->touch = (pkt[2] & 0x40) ? 1 : 0; 	return 1;} struct mtouch_priv {	u8 fw_rev_major;	u8 fw_rev_minor;}; static int mtouch_get_fw_revision(struct usbtouch_usb *usbtouch){	struct usb_device *udev = interface_to_usbdev(usbtouch->interface);	struct mtouch_priv *priv = usbtouch->priv;	int ret; 	u8 *buf __free(kfree) = kzalloc(MTOUCHUSB_REQ_CTRLLR_ID_LEN, GFP_NOIO);	if (!buf)		return -ENOMEM; 	ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),			      MTOUCHUSB_REQ_CTRLLR_ID,			      USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,			      0, 0, buf, MTOUCHUSB_REQ_CTRLLR_ID_LEN,			      USB_CTRL_SET_TIMEOUT);	if (ret != MTOUCHUSB_REQ_CTRLLR_ID_LEN) {		dev_warn(&usbtouch->interface->dev,			 "Failed to read FW rev: %d\n", ret);		return ret < 0 ? ret : -EIO;	} 	priv->fw_rev_major = buf[3];	priv->fw_rev_minor = buf[4]; 	return 0;} static int mtouch_alloc(struct usbtouch_usb *usbtouch){	struct mtouch_priv *priv; 	priv = kmalloc_obj(*priv);	if (!priv)		return -ENOMEM; 	usbtouch->priv = priv;	return 0;} static int mtouch_init(struct usbtouch_usb *usbtouch){	int ret, i;	struct usb_device *udev = interface_to_usbdev(usbtouch->interface); 	ret = mtouch_get_fw_revision(usbtouch);	if (ret)		return ret; 	ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),	                      MTOUCHUSB_RESET,	                      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,	                      1, 0, NULL, 0, USB_CTRL_SET_TIMEOUT);	dev_dbg(&usbtouch->interface->dev,		"%s - usb_control_msg - MTOUCHUSB_RESET - bytes|err: %d\n",		__func__, ret);	if (ret < 0)		return ret;	msleep(150); 	for (i = 0; i < 3; i++) {		ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),				      MTOUCHUSB_ASYNC_REPORT,				      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,				      1, 1, NULL, 0, USB_CTRL_SET_TIMEOUT);		dev_dbg(&usbtouch->interface->dev,			"%s - usb_control_msg - MTOUCHUSB_ASYNC_REPORT - bytes|err: %d\n",			__func__, ret);		if (ret >= 0)			break;		if (ret != -EPIPE)			return ret;	} 	/* Default min/max xy are the raw values, override if using hw-calib */	if (hwcalib_xy) {		input_set_abs_params(usbtouch->input, ABS_X, 0, 0xffff, 0, 0);		input_set_abs_params(usbtouch->input, ABS_Y, 0, 0xffff, 0, 0);	} 	return 0;} static void mtouch_exit(struct usbtouch_usb *usbtouch){	struct mtouch_priv *priv = usbtouch->priv; 	kfree(priv);} static struct usbtouch_device_info mtouch_dev_info = {	.min_xc		= 0x0,	.max_xc		= 0x4000,	.min_yc		= 0x0,	.max_yc		= 0x4000,	.rept_size	= 11,	.read_data	= mtouch_read_data,	.alloc		= mtouch_alloc,	.init		= mtouch_init,	.exit		= mtouch_exit,}; static ssize_t mtouch_firmware_rev_show(struct device *dev,				struct device_attribute *attr, char *output){	struct usb_interface *intf = to_usb_interface(dev);	struct usbtouch_usb *usbtouch = usb_get_intfdata(intf);	struct mtouch_priv *priv = usbtouch->priv; 	return sysfs_emit(output, "%1x.%1x\n",			  priv->fw_rev_major, priv->fw_rev_minor);}static DEVICE_ATTR(firmware_rev, 0444, mtouch_firmware_rev_show, NULL); static struct attribute *mtouch_attrs[] = {	&dev_attr_firmware_rev.attr,	NULL}; static bool mtouch_group_visible(struct kobject *kobj){	struct device *dev = kobj_to_dev(kobj);	struct usb_interface *intf = to_usb_interface(dev);	struct usbtouch_usb *usbtouch = usb_get_intfdata(intf); 	return usbtouch->type == &mtouch_dev_info;} DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(mtouch); static const struct attribute_group mtouch_attr_group = {	.is_visible = SYSFS_GROUP_VISIBLE(mtouch),	.attrs = mtouch_attrs,};#endif  /***************************************************************************** * ITM Part */#ifdef CONFIG_TOUCHSCREEN_USB_ITMstatic int itm_read_data(struct usbtouch_usb *dev, unsigned char *pkt){	int touch;	/*	 * ITM devices report invalid x/y data if not touched.	 * if the screen was touched before but is not touched any more	 * report touch as 0 with the last valid x/y data once. then stop	 * reporting data until touched again.	 */	dev->press = ((pkt[2] & 0x01) << 7) | (pkt[5] & 0x7F); 

Lines 1–500 of 1800.