1055 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1055 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*    -*- linux-c -*-
 | |
| 
 | |
| GTCO digitizer USB driver
 | |
| 
 | |
| Use the err() and dbg() macros from usb.h for system logging
 | |
| 
 | |
| TO CHECK:  Is pressure done right on report 5?
 | |
| 
 | |
| Copyright (C) 2006  GTCO CalComp
 | |
| 
 | |
| This program is free software; you can redistribute it and/or
 | |
| modify it under the terms of the GNU General Public License
 | |
| as published by the Free Software Foundation; version 2
 | |
| of the License.
 | |
| 
 | |
| This program is distributed in the hope that it will be useful,
 | |
| but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
| GNU General Public License for more details.
 | |
| 
 | |
| You should have received a copy of the GNU General Public License
 | |
| along with this program; if not, write to the Free Software
 | |
| Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 | |
| 
 | |
| Permission to use, copy, modify, distribute, and sell this software and its
 | |
| documentation for any purpose is hereby granted without fee, provided that
 | |
| the above copyright notice appear in all copies and that both that
 | |
| copyright notice and this permission notice appear in supporting
 | |
| documentation, and that the name of GTCO-CalComp not be used in advertising
 | |
| or publicity pertaining to distribution of the software without specific,
 | |
| written prior permission. GTCO-CalComp makes no representations about the
 | |
| suitability of this software for any purpose.  It is provided "as is"
 | |
| without express or implied warranty.
 | |
| 
 | |
| GTCO-CALCOMP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 | |
| INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 | |
| EVENT SHALL GTCO-CALCOMP BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 | |
| CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 | |
| DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 | |
| TORTIOUS ACTIONS, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 | |
| PERFORMANCE OF THIS SOFTWARE.
 | |
| 
 | |
| GTCO CalComp, Inc.
 | |
| 7125 Riverwood Drive
 | |
| Columbia, MD 21046
 | |
| 
 | |
| Jeremy Roberson jroberson@gtcocalcomp.com
 | |
| Scott Hill shill@gtcocalcomp.com
 | |
| */
 | |
| 
 | |
| 
 | |
| 
 | |
| /*#define DEBUG*/
 | |
| 
 | |
| #include <linux/kernel.h>
 | |
| #include <linux/module.h>
 | |
| #include <linux/errno.h>
 | |
| #include <linux/init.h>
 | |
| #include <linux/slab.h>
 | |
| #include <linux/input.h>
 | |
| #include <linux/usb.h>
 | |
| #include <asm/uaccess.h>
 | |
| #include <asm/unaligned.h>
 | |
| #include <asm/byteorder.h>
 | |
| 
 | |
| 
 | |
| #include <linux/usb/input.h>
 | |
| 
 | |
| /* Version with a Major number of 2 is for kernel inclusion only. */
 | |
| #define  GTCO_VERSION   "2.00.0006"
 | |
| 
 | |
| 
 | |
| /*   MACROS  */
 | |
| 
 | |
| #define VENDOR_ID_GTCO	      0x078C
 | |
| #define PID_400               0x400
 | |
| #define PID_401               0x401
 | |
| #define PID_1000              0x1000
 | |
| #define PID_1001              0x1001
 | |
| #define PID_1002              0x1002
 | |
| 
 | |
| /* Max size of a single report */
 | |
| #define REPORT_MAX_SIZE       10
 | |
| 
 | |
| 
 | |
| /* Bitmask whether pen is in range */
 | |
| #define MASK_INRANGE 0x20
 | |
| #define MASK_BUTTON  0x01F
 | |
| 
 | |
| #define  PATHLENGTH     64
 | |
| 
 | |
| /* DATA STRUCTURES */
 | |
| 
 | |
| /* Device table */
 | |
| static struct usb_device_id gtco_usbid_table [] = {
 | |
| 	{ USB_DEVICE(VENDOR_ID_GTCO, PID_400) },
 | |
| 	{ USB_DEVICE(VENDOR_ID_GTCO, PID_401) },
 | |
| 	{ USB_DEVICE(VENDOR_ID_GTCO, PID_1000) },
 | |
| 	{ USB_DEVICE(VENDOR_ID_GTCO, PID_1001) },
 | |
| 	{ USB_DEVICE(VENDOR_ID_GTCO, PID_1002) },
 | |
| 	{ }
 | |
| };
 | |
| MODULE_DEVICE_TABLE (usb, gtco_usbid_table);
 | |
| 
 | |
| 
 | |
| /* Structure to hold all of our device specific stuff */
 | |
| struct gtco {
 | |
| 
 | |
| 	struct input_dev  *inputdevice; /* input device struct pointer  */
 | |
| 	struct usb_device *usbdev; /* the usb device for this device */
 | |
| 	struct urb        *urbinfo;	 /* urb for incoming reports      */
 | |
| 	dma_addr_t        buf_dma;  /* dma addr of the data buffer*/
 | |
| 	unsigned char *   buffer;   /* databuffer for reports */
 | |
| 
 | |
| 	char  usbpath[PATHLENGTH];
 | |
| 	int   openCount;
 | |
| 
 | |
| 	/* Information pulled from Report Descriptor */
 | |
| 	u32  usage;
 | |
| 	u32  min_X;
 | |
| 	u32  max_X;
 | |
| 	u32  min_Y;
 | |
| 	u32  max_Y;
 | |
| 	s8   mintilt_X;
 | |
| 	s8   maxtilt_X;
 | |
| 	s8   mintilt_Y;
 | |
| 	s8   maxtilt_Y;
 | |
| 	u32  maxpressure;
 | |
| 	u32  minpressure;
 | |
| };
 | |
| 
 | |
| 
 | |
| 
 | |
| /*   Code for parsing the HID REPORT DESCRIPTOR          */
 | |
| 
 | |
| /* From HID1.11 spec */
 | |
| struct hid_descriptor
 | |
| {
 | |
| 	struct usb_descriptor_header header;
 | |
| 	__le16   bcdHID;
 | |
| 	u8       bCountryCode;
 | |
| 	u8       bNumDescriptors;
 | |
| 	u8       bDescriptorType;
 | |
| 	__le16   wDescriptorLength;
 | |
| } __attribute__ ((packed));
 | |
| 
 | |
| 
 | |
| #define HID_DESCRIPTOR_SIZE   9
 | |
| #define HID_DEVICE_TYPE       33
 | |
| #define REPORT_DEVICE_TYPE    34
 | |
| 
 | |
| 
 | |
| #define PREF_TAG(x)     ((x)>>4)
 | |
| #define PREF_TYPE(x)    ((x>>2)&0x03)
 | |
| #define PREF_SIZE(x)    ((x)&0x03)
 | |
| 
 | |
| #define TYPE_MAIN       0
 | |
| #define TYPE_GLOBAL     1
 | |
| #define TYPE_LOCAL      2
 | |
| #define TYPE_RESERVED   3
 | |
| 
 | |
| #define TAG_MAIN_INPUT        0x8
 | |
| #define TAG_MAIN_OUTPUT       0x9
 | |
| #define TAG_MAIN_FEATURE      0xB
 | |
| #define TAG_MAIN_COL_START    0xA
 | |
| #define TAG_MAIN_COL_END      0xC
 | |
| 
 | |
| #define TAG_GLOB_USAGE        0
 | |
| #define TAG_GLOB_LOG_MIN      1
 | |
| #define TAG_GLOB_LOG_MAX      2
 | |
| #define TAG_GLOB_PHYS_MIN     3
 | |
| #define TAG_GLOB_PHYS_MAX     4
 | |
| #define TAG_GLOB_UNIT_EXP     5
 | |
| #define TAG_GLOB_UNIT         6
 | |
| #define TAG_GLOB_REPORT_SZ    7
 | |
| #define TAG_GLOB_REPORT_ID    8
 | |
| #define TAG_GLOB_REPORT_CNT   9
 | |
| #define TAG_GLOB_PUSH         10
 | |
| #define TAG_GLOB_POP          11
 | |
| 
 | |
| #define TAG_GLOB_MAX          12
 | |
| 
 | |
| #define DIGITIZER_USAGE_TIP_PRESSURE   0x30
 | |
| #define DIGITIZER_USAGE_TILT_X         0x3D
 | |
| #define DIGITIZER_USAGE_TILT_Y         0x3E
 | |
| 
 | |
| 
 | |
| /*
 | |
|  *   This is an abbreviated parser for the HID Report Descriptor.  We
 | |
|  *   know what devices we are talking to, so this is by no means meant
 | |
|  *   to be generic.  We can make some safe assumptions:
 | |
|  *
 | |
|  *   - We know there are no LONG tags, all short
 | |
|  *   - We know that we have no MAIN Feature and MAIN Output items
 | |
|  *   - We know what the IRQ reports are supposed to look like.
 | |
|  *
 | |
|  *   The main purpose of this is to use the HID report desc to figure
 | |
|  *   out the mins and maxs of the fields in the IRQ reports.  The IRQ
 | |
|  *   reports for 400/401 change slightly if the max X is bigger than 64K.
 | |
|  *
 | |
|  */
 | |
| static void parse_hid_report_descriptor(struct gtco *device, char * report,
 | |
| 					int length)
 | |
| {
 | |
| 	int   x, i = 0;
 | |
| 
 | |
| 	/* Tag primitive vars */
 | |
| 	__u8   prefix;
 | |
| 	__u8   size;
 | |
| 	__u8   tag;
 | |
| 	__u8   type;
 | |
| 	__u8   data   = 0;
 | |
| 	__u16  data16 = 0;
 | |
| 	__u32  data32 = 0;
 | |
| 
 | |
| 	/* For parsing logic */
 | |
| 	int   inputnum = 0;
 | |
| 	__u32 usage = 0;
 | |
| 
 | |
| 	/* Global Values, indexed by TAG */
 | |
| 	__u32 globalval[TAG_GLOB_MAX];
 | |
| 	__u32 oldval[TAG_GLOB_MAX];
 | |
| 
 | |
| 	/* Debug stuff */
 | |
| 	char  maintype = 'x';
 | |
| 	char  globtype[12];
 | |
| 	int   indent = 0;
 | |
| 	char  indentstr[10] = "";
 | |
| 
 | |
| 
 | |
| 	dbg("======>>>>>>PARSE<<<<<<======");
 | |
| 
 | |
| 	/* Walk  this report and pull out the info we need */
 | |
| 	while (i < length) {
 | |
| 		prefix = report[i];
 | |
| 
 | |
| 		/* Skip over prefix */
 | |
| 		i++;
 | |
| 
 | |
| 		/* Determine data size and save the data in the proper variable */
 | |
| 		size = PREF_SIZE(prefix);
 | |
| 		switch (size) {
 | |
| 		case 1:
 | |
| 			data = report[i];
 | |
| 			break;
 | |
| 		case 2:
 | |
| 			data16 = get_unaligned_le16(&report[i]);
 | |
| 			break;
 | |
| 		case 3:
 | |
| 			size = 4;
 | |
| 			data32 = get_unaligned_le32(&report[i]);
 | |
| 			break;
 | |
| 		}
 | |
| 
 | |
| 		/* Skip size of data */
 | |
| 		i += size;
 | |
| 
 | |
| 		/* What we do depends on the tag type */
 | |
| 		tag  = PREF_TAG(prefix);
 | |
| 		type = PREF_TYPE(prefix);
 | |
| 		switch (type) {
 | |
| 		case TYPE_MAIN:
 | |
| 			strcpy(globtype, "");
 | |
| 			switch (tag) {
 | |
| 
 | |
| 			case TAG_MAIN_INPUT:
 | |
| 				/*
 | |
| 				 * The INPUT MAIN tag signifies this is
 | |
| 				 * information from a report.  We need to
 | |
| 				 * figure out what it is and store the
 | |
| 				 * min/max values
 | |
| 				 */
 | |
| 
 | |
| 				maintype = 'I';
 | |
| 				if (data == 2)
 | |
| 					strcpy(globtype, "Variable");
 | |
| 				else if (data == 3)
 | |
| 					strcpy(globtype, "Var|Const");
 | |
| 
 | |
| 				dbg("::::: Saving Report: %d input #%d Max: 0x%X(%d) Min:0x%X(%d) of %d bits",
 | |
| 				    globalval[TAG_GLOB_REPORT_ID], inputnum,
 | |
| 				    globalval[TAG_GLOB_LOG_MAX], globalval[TAG_GLOB_LOG_MAX],
 | |
| 				    globalval[TAG_GLOB_LOG_MIN], globalval[TAG_GLOB_LOG_MIN],
 | |
| 				    globalval[TAG_GLOB_REPORT_SZ] * globalval[TAG_GLOB_REPORT_CNT]);
 | |
| 
 | |
| 
 | |
| 				/*
 | |
| 				  We can assume that the first two input items
 | |
| 				  are always the X and Y coordinates.  After
 | |
| 				  that, we look for everything else by
 | |
| 				  local usage value
 | |
| 				 */
 | |
| 				switch (inputnum) {
 | |
| 				case 0:  /* X coord */
 | |
| 					dbg("GER: X Usage: 0x%x", usage);
 | |
| 					if (device->max_X == 0) {
 | |
| 						device->max_X = globalval[TAG_GLOB_LOG_MAX];
 | |
| 						device->min_X = globalval[TAG_GLOB_LOG_MIN];
 | |
| 					}
 | |
| 					break;
 | |
| 
 | |
| 				case 1:  /* Y coord */
 | |
| 					dbg("GER: Y Usage: 0x%x", usage);
 | |
| 					if (device->max_Y == 0) {
 | |
| 						device->max_Y = globalval[TAG_GLOB_LOG_MAX];
 | |
| 						device->min_Y = globalval[TAG_GLOB_LOG_MIN];
 | |
| 					}
 | |
| 					break;
 | |
| 
 | |
| 				default:
 | |
| 					/* Tilt X */
 | |
| 					if (usage == DIGITIZER_USAGE_TILT_X) {
 | |
| 						if (device->maxtilt_X == 0) {
 | |
| 							device->maxtilt_X = globalval[TAG_GLOB_LOG_MAX];
 | |
| 							device->mintilt_X = globalval[TAG_GLOB_LOG_MIN];
 | |
| 						}
 | |
| 					}
 | |
| 
 | |
| 					/* Tilt Y */
 | |
| 					if (usage == DIGITIZER_USAGE_TILT_Y) {
 | |
| 						if (device->maxtilt_Y == 0) {
 | |
| 							device->maxtilt_Y = globalval[TAG_GLOB_LOG_MAX];
 | |
| 							device->mintilt_Y = globalval[TAG_GLOB_LOG_MIN];
 | |
| 						}
 | |
| 					}
 | |
| 
 | |
| 					/* Pressure */
 | |
| 					if (usage == DIGITIZER_USAGE_TIP_PRESSURE) {
 | |
| 						if (device->maxpressure == 0) {
 | |
| 							device->maxpressure = globalval[TAG_GLOB_LOG_MAX];
 | |
| 							device->minpressure = globalval[TAG_GLOB_LOG_MIN];
 | |
| 						}
 | |
| 					}
 | |
| 
 | |
| 					break;
 | |
| 				}
 | |
| 
 | |
| 				inputnum++;
 | |
| 				break;
 | |
| 
 | |
| 			case TAG_MAIN_OUTPUT:
 | |
| 				maintype = 'O';
 | |
| 				break;
 | |
| 
 | |
| 			case TAG_MAIN_FEATURE:
 | |
| 				maintype = 'F';
 | |
| 				break;
 | |
| 
 | |
| 			case TAG_MAIN_COL_START:
 | |
| 				maintype = 'S';
 | |
| 
 | |
| 				if (data == 0) {
 | |
| 					dbg("======>>>>>> Physical");
 | |
| 					strcpy(globtype, "Physical");
 | |
| 				} else
 | |
| 					dbg("======>>>>>>");
 | |
| 
 | |
| 				/* Indent the debug output */
 | |
| 				indent++;
 | |
| 				for (x = 0; x < indent; x++)
 | |
| 					indentstr[x] = '-';
 | |
| 				indentstr[x] = 0;
 | |
| 
 | |
| 				/* Save global tags */
 | |
| 				for (x = 0; x < TAG_GLOB_MAX; x++)
 | |
| 					oldval[x] = globalval[x];
 | |
| 
 | |
| 				break;
 | |
| 
 | |
| 			case TAG_MAIN_COL_END:
 | |
| 				dbg("<<<<<<======");
 | |
| 				maintype = 'E';
 | |
| 				indent--;
 | |
| 				for (x = 0; x < indent; x++)
 | |
| 					indentstr[x] = '-';
 | |
| 				indentstr[x] = 0;
 | |
| 
 | |
| 				/* Copy global tags back */
 | |
| 				for (x = 0; x < TAG_GLOB_MAX; x++)
 | |
| 					globalval[x] = oldval[x];
 | |
| 
 | |
| 				break;
 | |
| 			}
 | |
| 
 | |
| 			switch (size) {
 | |
| 			case 1:
 | |
| 				dbg("%sMAINTAG:(%d) %c SIZE: %d Data: %s 0x%x",
 | |
| 				    indentstr, tag, maintype, size, globtype, data);
 | |
| 				break;
 | |
| 
 | |
| 			case 2:
 | |
| 				dbg("%sMAINTAG:(%d) %c SIZE: %d Data: %s 0x%x",
 | |
| 				    indentstr, tag, maintype, size, globtype, data16);
 | |
| 				break;
 | |
| 
 | |
| 			case 4:
 | |
| 				dbg("%sMAINTAG:(%d) %c SIZE: %d Data: %s 0x%x",
 | |
| 				    indentstr, tag, maintype, size, globtype, data32);
 | |
| 				break;
 | |
| 			}
 | |
| 			break;
 | |
| 
 | |
| 		case TYPE_GLOBAL:
 | |
| 			switch (tag) {
 | |
| 			case TAG_GLOB_USAGE:
 | |
| 				/*
 | |
| 				 * First time we hit the global usage tag,
 | |
| 				 * it should tell us the type of device
 | |
| 				 */
 | |
| 				if (device->usage == 0)
 | |
| 					device->usage = data;
 | |
| 
 | |
| 				strcpy(globtype, "USAGE");
 | |
| 				break;
 | |
| 
 | |
| 			case TAG_GLOB_LOG_MIN:
 | |
| 				strcpy(globtype, "LOG_MIN");
 | |
| 				break;
 | |
| 
 | |
| 			case TAG_GLOB_LOG_MAX:
 | |
| 				strcpy(globtype, "LOG_MAX");
 | |
| 				break;
 | |
| 
 | |
| 			case TAG_GLOB_PHYS_MIN:
 | |
| 				strcpy(globtype, "PHYS_MIN");
 | |
| 				break;
 | |
| 
 | |
| 			case TAG_GLOB_PHYS_MAX:
 | |
| 				strcpy(globtype, "PHYS_MAX");
 | |
| 				break;
 | |
| 
 | |
| 			case TAG_GLOB_UNIT_EXP:
 | |
| 				strcpy(globtype, "EXP");
 | |
| 				break;
 | |
| 
 | |
| 			case TAG_GLOB_UNIT:
 | |
| 				strcpy(globtype, "UNIT");
 | |
| 				break;
 | |
| 
 | |
| 			case TAG_GLOB_REPORT_SZ:
 | |
| 				strcpy(globtype, "REPORT_SZ");
 | |
| 				break;
 | |
| 
 | |
| 			case TAG_GLOB_REPORT_ID:
 | |
| 				strcpy(globtype, "REPORT_ID");
 | |
| 				/* New report, restart numbering */
 | |
| 				inputnum = 0;
 | |
| 				break;
 | |
| 
 | |
| 			case TAG_GLOB_REPORT_CNT:
 | |
| 				strcpy(globtype, "REPORT_CNT");
 | |
| 				break;
 | |
| 
 | |
| 			case TAG_GLOB_PUSH:
 | |
| 				strcpy(globtype, "PUSH");
 | |
| 				break;
 | |
| 
 | |
| 			case TAG_GLOB_POP:
 | |
| 				strcpy(globtype, "POP");
 | |
| 				break;
 | |
| 			}
 | |
| 
 | |
| 			/* Check to make sure we have a good tag number
 | |
| 			   so we don't overflow array */
 | |
| 			if (tag < TAG_GLOB_MAX) {
 | |
| 				switch (size) {
 | |
| 				case 1:
 | |
| 					dbg("%sGLOBALTAG:%s(%d) SIZE: %d Data: 0x%x",
 | |
| 					    indentstr, globtype, tag, size, data);
 | |
| 					globalval[tag] = data;
 | |
| 					break;
 | |
| 
 | |
| 				case 2:
 | |
| 					dbg("%sGLOBALTAG:%s(%d) SIZE: %d Data: 0x%x",
 | |
| 					    indentstr, globtype, tag, size, data16);
 | |
| 					globalval[tag] = data16;
 | |
| 					break;
 | |
| 
 | |
| 				case 4:
 | |
| 					dbg("%sGLOBALTAG:%s(%d) SIZE: %d Data: 0x%x",
 | |
| 					    indentstr, globtype, tag, size, data32);
 | |
| 					globalval[tag] = data32;
 | |
| 					break;
 | |
| 				}
 | |
| 			} else {
 | |
| 				dbg("%sGLOBALTAG: ILLEGAL TAG:%d SIZE: %d ",
 | |
| 				    indentstr, tag, size);
 | |
| 			}
 | |
| 			break;
 | |
| 
 | |
| 		case TYPE_LOCAL:
 | |
| 			switch (tag) {
 | |
| 			case TAG_GLOB_USAGE:
 | |
| 				strcpy(globtype, "USAGE");
 | |
| 				/* Always 1 byte */
 | |
| 				usage = data;
 | |
| 				break;
 | |
| 
 | |
| 			case TAG_GLOB_LOG_MIN:
 | |
| 				strcpy(globtype, "MIN");
 | |
| 				break;
 | |
| 
 | |
| 			case TAG_GLOB_LOG_MAX:
 | |
| 				strcpy(globtype, "MAX");
 | |
| 				break;
 | |
| 
 | |
| 			default:
 | |
| 				strcpy(globtype, "UNKNOWN");
 | |
| 				break;
 | |
| 			}
 | |
| 
 | |
| 			switch (size) {
 | |
| 			case 1:
 | |
| 				dbg("%sLOCALTAG:(%d) %s SIZE: %d Data: 0x%x",
 | |
| 				    indentstr, tag, globtype, size, data);
 | |
| 				break;
 | |
| 
 | |
| 			case 2:
 | |
| 				dbg("%sLOCALTAG:(%d) %s SIZE: %d Data: 0x%x",
 | |
| 				    indentstr, tag, globtype, size, data16);
 | |
| 				break;
 | |
| 
 | |
| 			case 4:
 | |
| 				dbg("%sLOCALTAG:(%d) %s SIZE: %d Data: 0x%x",
 | |
| 				    indentstr, tag, globtype, size, data32);
 | |
| 				break;
 | |
| 			}
 | |
| 
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /*   INPUT DRIVER Routines                               */
 | |
| 
 | |
| /*
 | |
|  * Called when opening the input device.  This will submit the URB to
 | |
|  * the usb system so we start getting reports
 | |
|  */
 | |
| static int gtco_input_open(struct input_dev *inputdev)
 | |
| {
 | |
| 	struct gtco *device = input_get_drvdata(inputdev);
 | |
| 
 | |
| 	device->urbinfo->dev = device->usbdev;
 | |
| 	if (usb_submit_urb(device->urbinfo, GFP_KERNEL))
 | |
| 		return -EIO;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Called when closing the input device.  This will unlink the URB
 | |
|  */
 | |
| static void gtco_input_close(struct input_dev *inputdev)
 | |
| {
 | |
| 	struct gtco *device = input_get_drvdata(inputdev);
 | |
| 
 | |
| 	usb_kill_urb(device->urbinfo);
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|  *  Setup input device capabilities.  Tell the input system what this
 | |
|  *  device is capable of generating.
 | |
|  *
 | |
|  *  This information is based on what is read from the HID report and
 | |
|  *  placed in the struct gtco structure
 | |
|  *
 | |
|  */
 | |
| static void gtco_setup_caps(struct input_dev *inputdev)
 | |
| {
 | |
| 	struct gtco *device = input_get_drvdata(inputdev);
 | |
| 
 | |
| 	/* Which events */
 | |
| 	inputdev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) |
 | |
| 		BIT_MASK(EV_MSC);
 | |
| 
 | |
| 	/* Misc event menu block */
 | |
| 	inputdev->mscbit[0] = BIT_MASK(MSC_SCAN) | BIT_MASK(MSC_SERIAL) |
 | |
| 		BIT_MASK(MSC_RAW);
 | |
| 
 | |
| 	/* Absolute values based on HID report info */
 | |
| 	input_set_abs_params(inputdev, ABS_X, device->min_X, device->max_X,
 | |
| 			     0, 0);
 | |
| 	input_set_abs_params(inputdev, ABS_Y, device->min_Y, device->max_Y,
 | |
| 			     0, 0);
 | |
| 
 | |
| 	/* Proximity */
 | |
| 	input_set_abs_params(inputdev, ABS_DISTANCE, 0, 1, 0, 0);
 | |
| 
 | |
| 	/* Tilt & pressure */
 | |
| 	input_set_abs_params(inputdev, ABS_TILT_X, device->mintilt_X,
 | |
| 			     device->maxtilt_X, 0, 0);
 | |
| 	input_set_abs_params(inputdev, ABS_TILT_Y, device->mintilt_Y,
 | |
| 			     device->maxtilt_Y, 0, 0);
 | |
| 	input_set_abs_params(inputdev, ABS_PRESSURE, device->minpressure,
 | |
| 			     device->maxpressure, 0, 0);
 | |
| 
 | |
| 	/* Transducer */
 | |
| 	input_set_abs_params(inputdev, ABS_MISC, 0, 0xFF, 0, 0);
 | |
| }
 | |
| 
 | |
| /*   USB Routines  */
 | |
| 
 | |
| /*
 | |
|  * URB callback routine.  Called when we get IRQ reports from the
 | |
|  *  digitizer.
 | |
|  *
 | |
|  *  This bridges the USB and input device worlds.  It generates events
 | |
|  *  on the input device based on the USB reports.
 | |
|  */
 | |
| static void gtco_urb_callback(struct urb *urbinfo)
 | |
| {
 | |
| 	struct gtco *device = urbinfo->context;
 | |
| 	struct input_dev  *inputdev;
 | |
| 	int               rc;
 | |
| 	u32               val = 0;
 | |
| 	s8                valsigned = 0;
 | |
| 	char              le_buffer[2];
 | |
| 
 | |
| 	inputdev = device->inputdevice;
 | |
| 
 | |
| 	/* Was callback OK? */
 | |
| 	if (urbinfo->status == -ECONNRESET ||
 | |
| 	    urbinfo->status == -ENOENT ||
 | |
| 	    urbinfo->status == -ESHUTDOWN) {
 | |
| 
 | |
| 		/* Shutdown is occurring. Return and don't queue up any more */
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	if (urbinfo->status != 0) {
 | |
| 		/*
 | |
| 		 * Some unknown error.  Hopefully temporary. Just go and
 | |
| 		 * requeue an URB
 | |
| 		 */
 | |
| 		goto resubmit;
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * Good URB, now process
 | |
| 	 */
 | |
| 
 | |
| 	/* PID dependent when we interpret the report */
 | |
| 	if (inputdev->id.product == PID_1000 ||
 | |
| 	    inputdev->id.product == PID_1001 ||
 | |
| 	    inputdev->id.product == PID_1002) {
 | |
| 
 | |
| 		/*
 | |
| 		 * Switch on the report ID
 | |
| 		 * Conveniently, the reports have more information, the higher
 | |
| 		 * the report number.  We can just fall through the case
 | |
| 		 * statements if we start with the highest number report
 | |
| 		 */
 | |
| 		switch (device->buffer[0]) {
 | |
| 		case 5:
 | |
| 			/* Pressure is 9 bits */
 | |
| 			val = ((u16)(device->buffer[8]) << 1);
 | |
| 			val |= (u16)(device->buffer[7] >> 7);
 | |
| 			input_report_abs(inputdev, ABS_PRESSURE,
 | |
| 					 device->buffer[8]);
 | |
| 
 | |
| 			/* Mask out the Y tilt value used for pressure */
 | |
| 			device->buffer[7] = (u8)((device->buffer[7]) & 0x7F);
 | |
| 
 | |
| 			/* Fall thru */
 | |
| 		case 4:
 | |
| 			/* Tilt */
 | |
| 
 | |
| 			/* Sign extend these 7 bit numbers.  */
 | |
| 			if (device->buffer[6] & 0x40)
 | |
| 				device->buffer[6] |= 0x80;
 | |
| 
 | |
| 			if (device->buffer[7] & 0x40)
 | |
| 				device->buffer[7] |= 0x80;
 | |
| 
 | |
| 
 | |
| 			valsigned = (device->buffer[6]);
 | |
| 			input_report_abs(inputdev, ABS_TILT_X, (s32)valsigned);
 | |
| 
 | |
| 			valsigned = (device->buffer[7]);
 | |
| 			input_report_abs(inputdev, ABS_TILT_Y, (s32)valsigned);
 | |
| 
 | |
| 			/* Fall thru */
 | |
| 		case 2:
 | |
| 		case 3:
 | |
| 			/* Convert buttons, only 5 bits possible */
 | |
| 			val = (device->buffer[5]) & MASK_BUTTON;
 | |
| 
 | |
| 			/* We don't apply any meaning to the bitmask,
 | |
| 			   just report */
 | |
| 			input_event(inputdev, EV_MSC, MSC_SERIAL, val);
 | |
| 
 | |
| 			/*  Fall thru */
 | |
| 		case 1:
 | |
| 			/* All reports have X and Y coords in the same place */
 | |
| 			val = get_unaligned_le16(&device->buffer[1]);
 | |
| 			input_report_abs(inputdev, ABS_X, val);
 | |
| 
 | |
| 			val = get_unaligned_le16(&device->buffer[3]);
 | |
| 			input_report_abs(inputdev, ABS_Y, val);
 | |
| 
 | |
| 			/* Ditto for proximity bit */
 | |
| 			val = device->buffer[5] & MASK_INRANGE ? 1 : 0;
 | |
| 			input_report_abs(inputdev, ABS_DISTANCE, val);
 | |
| 
 | |
| 			/* Report 1 is an exception to how we handle buttons */
 | |
| 			/* Buttons are an index, not a bitmask */
 | |
| 			if (device->buffer[0] == 1) {
 | |
| 
 | |
| 				/*
 | |
| 				 * Convert buttons, 5 bit index
 | |
| 				 * Report value of index set as one,
 | |
| 				 * the rest as 0
 | |
| 				 */
 | |
| 				val = device->buffer[5] & MASK_BUTTON;
 | |
| 				dbg("======>>>>>>REPORT 1: val 0x%X(%d)",
 | |
| 				    val, val);
 | |
| 
 | |
| 				/*
 | |
| 				 * We don't apply any meaning to the button
 | |
| 				 * index, just report it
 | |
| 				 */
 | |
| 				input_event(inputdev, EV_MSC, MSC_SERIAL, val);
 | |
| 			}
 | |
| 			break;
 | |
| 
 | |
| 		case 7:
 | |
| 			/* Menu blocks */
 | |
| 			input_event(inputdev, EV_MSC, MSC_SCAN,
 | |
| 				    device->buffer[1]);
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* Other pid class */
 | |
| 	if (inputdev->id.product == PID_400 ||
 | |
| 	    inputdev->id.product == PID_401) {
 | |
| 
 | |
| 		/* Report 2 */
 | |
| 		if (device->buffer[0] == 2) {
 | |
| 			/* Menu blocks */
 | |
| 			input_event(inputdev, EV_MSC, MSC_SCAN, device->buffer[1]);
 | |
| 		}
 | |
| 
 | |
| 		/*  Report 1 */
 | |
| 		if (device->buffer[0] == 1) {
 | |
| 			char buttonbyte;
 | |
| 
 | |
| 			/*  IF X max > 64K, we still a bit from the y report */
 | |
| 			if (device->max_X > 0x10000) {
 | |
| 
 | |
| 				val = (u16)(((u16)(device->buffer[2] << 8)) | (u8)device->buffer[1]);
 | |
| 				val |= (u32)(((u8)device->buffer[3] & 0x1) << 16);
 | |
| 
 | |
| 				input_report_abs(inputdev, ABS_X, val);
 | |
| 
 | |
| 				le_buffer[0]  = (u8)((u8)(device->buffer[3]) >> 1);
 | |
| 				le_buffer[0] |= (u8)((device->buffer[3] & 0x1) << 7);
 | |
| 
 | |
| 				le_buffer[1]  = (u8)(device->buffer[4] >> 1);
 | |
| 				le_buffer[1] |= (u8)((device->buffer[5] & 0x1) << 7);
 | |
| 
 | |
| 				val = get_unaligned_le16(le_buffer);
 | |
| 				input_report_abs(inputdev, ABS_Y, val);
 | |
| 
 | |
| 				/*
 | |
| 				 * Shift the button byte right by one to
 | |
| 				 * make it look like the standard report
 | |
| 				 */
 | |
| 				buttonbyte = device->buffer[5] >> 1;
 | |
| 			} else {
 | |
| 
 | |
| 				val = get_unaligned_le16(&device->buffer[1]);
 | |
| 				input_report_abs(inputdev, ABS_X, val);
 | |
| 
 | |
| 				val = get_unaligned_le16(&device->buffer[3]);
 | |
| 				input_report_abs(inputdev, ABS_Y, val);
 | |
| 
 | |
| 				buttonbyte = device->buffer[5];
 | |
| 			}
 | |
| 
 | |
| 			/* BUTTONS and PROXIMITY */
 | |
| 			val = buttonbyte & MASK_INRANGE ? 1 : 0;
 | |
| 			input_report_abs(inputdev, ABS_DISTANCE, val);
 | |
| 
 | |
| 			/* Convert buttons, only 4 bits possible */
 | |
| 			val = buttonbyte & 0x0F;
 | |
| #ifdef USE_BUTTONS
 | |
| 			for (i = 0; i < 5; i++)
 | |
| 				input_report_key(inputdev, BTN_DIGI + i, val & (1 << i));
 | |
| #else
 | |
| 			/* We don't apply any meaning to the bitmask, just report */
 | |
| 			input_event(inputdev, EV_MSC, MSC_SERIAL, val);
 | |
| #endif
 | |
| 
 | |
| 			/* TRANSDUCER */
 | |
| 			input_report_abs(inputdev, ABS_MISC, device->buffer[6]);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* Everybody gets report ID's */
 | |
| 	input_event(inputdev, EV_MSC, MSC_RAW,  device->buffer[0]);
 | |
| 
 | |
| 	/* Sync it up */
 | |
| 	input_sync(inputdev);
 | |
| 
 | |
|  resubmit:
 | |
| 	rc = usb_submit_urb(urbinfo, GFP_ATOMIC);
 | |
| 	if (rc != 0)
 | |
| 		err("usb_submit_urb failed rc=0x%x", rc);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  *  The probe routine.  This is called when the kernel find the matching USB
 | |
|  *   vendor/product.  We do the following:
 | |
|  *
 | |
|  *    - Allocate mem for a local structure to manage the device
 | |
|  *    - Request a HID Report Descriptor from the device and parse it to
 | |
|  *      find out the device parameters
 | |
|  *    - Create an input device and assign it attributes
 | |
|  *   - Allocate an URB so the device can talk to us when the input
 | |
|  *      queue is open
 | |
|  */
 | |
| static int gtco_probe(struct usb_interface *usbinterface,
 | |
| 		      const struct usb_device_id *id)
 | |
| {
 | |
| 
 | |
| 	struct gtco             *gtco;
 | |
| 	struct input_dev        *input_dev;
 | |
| 	struct hid_descriptor   *hid_desc;
 | |
| 	char                    *report;
 | |
| 	int                     result = 0, retry;
 | |
| 	int			error;
 | |
| 	struct usb_endpoint_descriptor *endpoint;
 | |
| 
 | |
| 	/* Allocate memory for device structure */
 | |
| 	gtco = kzalloc(sizeof(struct gtco), GFP_KERNEL);
 | |
| 	input_dev = input_allocate_device();
 | |
| 	if (!gtco || !input_dev) {
 | |
| 		err("No more memory");
 | |
| 		error = -ENOMEM;
 | |
| 		goto err_free_devs;
 | |
| 	}
 | |
| 
 | |
| 	/* Set pointer to the input device */
 | |
| 	gtco->inputdevice = input_dev;
 | |
| 
 | |
| 	/* Save interface information */
 | |
| 	gtco->usbdev = usb_get_dev(interface_to_usbdev(usbinterface));
 | |
| 
 | |
| 	/* Allocate some data for incoming reports */
 | |
| 	gtco->buffer = usb_buffer_alloc(gtco->usbdev, REPORT_MAX_SIZE,
 | |
| 					GFP_KERNEL, >co->buf_dma);
 | |
| 	if (!gtco->buffer) {
 | |
| 		err("No more memory for us buffers");
 | |
| 		error = -ENOMEM;
 | |
| 		goto err_free_devs;
 | |
| 	}
 | |
| 
 | |
| 	/* Allocate URB for reports */
 | |
| 	gtco->urbinfo = usb_alloc_urb(0, GFP_KERNEL);
 | |
| 	if (!gtco->urbinfo) {
 | |
| 		err("Failed to allocate URB");
 | |
| 		error = -ENOMEM;
 | |
| 		goto err_free_buf;
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * The endpoint is always altsetting 0, we know this since we know
 | |
| 	 * this device only has one interrupt endpoint
 | |
| 	 */
 | |
| 	endpoint = &usbinterface->altsetting[0].endpoint[0].desc;
 | |
| 
 | |
| 	/* Some debug */
 | |
| 	dbg("gtco # interfaces: %d", usbinterface->num_altsetting);
 | |
| 	dbg("num endpoints:     %d", usbinterface->cur_altsetting->desc.bNumEndpoints);
 | |
| 	dbg("interface class:   %d", usbinterface->cur_altsetting->desc.bInterfaceClass);
 | |
| 	dbg("endpoint: attribute:0x%x type:0x%x", endpoint->bmAttributes, endpoint->bDescriptorType);
 | |
| 	if (usb_endpoint_xfer_int(endpoint))
 | |
| 		dbg("endpoint: we have interrupt endpoint\n");
 | |
| 
 | |
| 	dbg("endpoint extra len:%d ", usbinterface->altsetting[0].extralen);
 | |
| 
 | |
| 	/*
 | |
| 	 * Find the HID descriptor so we can find out the size of the
 | |
| 	 * HID report descriptor
 | |
| 	 */
 | |
| 	if (usb_get_extra_descriptor(usbinterface->cur_altsetting,
 | |
| 				     HID_DEVICE_TYPE, &hid_desc) != 0){
 | |
| 		err("Can't retrieve exta USB descriptor to get hid report descriptor length");
 | |
| 		error = -EIO;
 | |
| 		goto err_free_urb;
 | |
| 	}
 | |
| 
 | |
| 	dbg("Extra descriptor success: type:%d  len:%d",
 | |
| 	    hid_desc->bDescriptorType,  hid_desc->wDescriptorLength);
 | |
| 
 | |
| 	report = kzalloc(le16_to_cpu(hid_desc->wDescriptorLength), GFP_KERNEL);
 | |
| 	if (!report) {
 | |
| 		err("No more memory for report");
 | |
| 		error = -ENOMEM;
 | |
| 		goto err_free_urb;
 | |
| 	}
 | |
| 
 | |
| 	/* Couple of tries to get reply */
 | |
| 	for (retry = 0; retry < 3; retry++) {
 | |
| 		result = usb_control_msg(gtco->usbdev,
 | |
| 					 usb_rcvctrlpipe(gtco->usbdev, 0),
 | |
| 					 USB_REQ_GET_DESCRIPTOR,
 | |
| 					 USB_RECIP_INTERFACE | USB_DIR_IN,
 | |
| 					 REPORT_DEVICE_TYPE << 8,
 | |
| 					 0, /* interface */
 | |
| 					 report,
 | |
| 					 le16_to_cpu(hid_desc->wDescriptorLength),
 | |
| 					 5000); /* 5 secs */
 | |
| 
 | |
| 		dbg("usb_control_msg result: %d", result);
 | |
| 		if (result == le16_to_cpu(hid_desc->wDescriptorLength)) {
 | |
| 			parse_hid_report_descriptor(gtco, report, result);
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	kfree(report);
 | |
| 
 | |
| 	/* If we didn't get the report, fail */
 | |
| 	if (result != le16_to_cpu(hid_desc->wDescriptorLength)) {
 | |
| 		err("Failed to get HID Report Descriptor of size: %d",
 | |
| 		    hid_desc->wDescriptorLength);
 | |
| 		error = -EIO;
 | |
| 		goto err_free_urb;
 | |
| 	}
 | |
| 
 | |
| 	/* Create a device file node */
 | |
| 	usb_make_path(gtco->usbdev, gtco->usbpath, sizeof(gtco->usbpath));
 | |
| 	strlcat(gtco->usbpath, "/input0", sizeof(gtco->usbpath));
 | |
| 
 | |
| 	/* Set Input device functions */
 | |
| 	input_dev->open = gtco_input_open;
 | |
| 	input_dev->close = gtco_input_close;
 | |
| 
 | |
| 	/* Set input device information */
 | |
| 	input_dev->name = "GTCO_CalComp";
 | |
| 	input_dev->phys = gtco->usbpath;
 | |
| 
 | |
| 	input_set_drvdata(input_dev, gtco);
 | |
| 
 | |
| 	/* Now set up all the input device capabilities */
 | |
| 	gtco_setup_caps(input_dev);
 | |
| 
 | |
| 	/* Set input device required ID information */
 | |
| 	usb_to_input_id(gtco->usbdev, &input_dev->id);
 | |
| 	input_dev->dev.parent = &usbinterface->dev;
 | |
| 
 | |
| 	/* Setup the URB, it will be posted later on open of input device */
 | |
| 	endpoint = &usbinterface->altsetting[0].endpoint[0].desc;
 | |
| 
 | |
| 	usb_fill_int_urb(gtco->urbinfo,
 | |
| 			 gtco->usbdev,
 | |
| 			 usb_rcvintpipe(gtco->usbdev,
 | |
| 					endpoint->bEndpointAddress),
 | |
| 			 gtco->buffer,
 | |
| 			 REPORT_MAX_SIZE,
 | |
| 			 gtco_urb_callback,
 | |
| 			 gtco,
 | |
| 			 endpoint->bInterval);
 | |
| 
 | |
| 	gtco->urbinfo->transfer_dma = gtco->buf_dma;
 | |
| 	gtco->urbinfo->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
 | |
| 
 | |
| 	/* Save gtco pointer in USB interface gtco */
 | |
| 	usb_set_intfdata(usbinterface, gtco);
 | |
| 
 | |
| 	/* All done, now register the input device */
 | |
| 	error = input_register_device(input_dev);
 | |
| 	if (error)
 | |
| 		goto err_free_urb;
 | |
| 
 | |
| 	return 0;
 | |
| 
 | |
|  err_free_urb:
 | |
| 	usb_free_urb(gtco->urbinfo);
 | |
|  err_free_buf:
 | |
| 	usb_buffer_free(gtco->usbdev, REPORT_MAX_SIZE,
 | |
| 			gtco->buffer, gtco->buf_dma);
 | |
|  err_free_devs:
 | |
| 	input_free_device(input_dev);
 | |
| 	kfree(gtco);
 | |
| 	return error;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  *  This function is a standard USB function called when the USB device
 | |
|  *  is disconnected.  We will get rid of the URV, de-register the input
 | |
|  *  device, and free up allocated memory
 | |
|  */
 | |
| static void gtco_disconnect(struct usb_interface *interface)
 | |
| {
 | |
| 	/* Grab private device ptr */
 | |
| 	struct gtco *gtco = usb_get_intfdata(interface);
 | |
| 
 | |
| 	/* Now reverse all the registration stuff */
 | |
| 	if (gtco) {
 | |
| 		input_unregister_device(gtco->inputdevice);
 | |
| 		usb_kill_urb(gtco->urbinfo);
 | |
| 		usb_free_urb(gtco->urbinfo);
 | |
| 		usb_buffer_free(gtco->usbdev, REPORT_MAX_SIZE,
 | |
| 				gtco->buffer, gtco->buf_dma);
 | |
| 		kfree(gtco);
 | |
| 	}
 | |
| 
 | |
| 	dev_info(&interface->dev, "gtco driver disconnected\n");
 | |
| }
 | |
| 
 | |
| /*   STANDARD MODULE LOAD ROUTINES  */
 | |
| 
 | |
| static struct usb_driver gtco_driverinfo_table = {
 | |
| 	.name		= "gtco",
 | |
| 	.id_table	= gtco_usbid_table,
 | |
| 	.probe		= gtco_probe,
 | |
| 	.disconnect	= gtco_disconnect,
 | |
| };
 | |
| 
 | |
| /*
 | |
|  *  Register this module with the USB subsystem
 | |
|  */
 | |
| static int __init gtco_init(void)
 | |
| {
 | |
| 	int error;
 | |
| 
 | |
| 	error = usb_register(>co_driverinfo_table);
 | |
| 	if (error) {
 | |
| 		err("usb_register() failed rc=0x%x", error);
 | |
| 		return error;
 | |
| 	}
 | |
| 
 | |
| 	printk("GTCO usb driver version: %s", GTCO_VERSION);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  *   Deregister this module with the USB subsystem
 | |
|  */
 | |
| static void __exit gtco_exit(void)
 | |
| {
 | |
| 	usb_deregister(>co_driverinfo_table);
 | |
| }
 | |
| 
 | |
| module_init(gtco_init);
 | |
| module_exit(gtco_exit);
 | |
| 
 | |
| MODULE_DESCRIPTION("GTCO digitizer USB driver");
 | |
| MODULE_LICENSE("GPL");
 |