From: Jeremy Roberson <[EMAIL PROTECTED]> Added a kernel module (gtco) to the USB Input subsystem. This kernel module adds support for all GTCO CalComp USB InterWrite School products.
Signed-off-by: Jeremy A. Roberson <[EMAIL PROTECTED]> --- diff -uprN a/drivers/usb/input/gtco.c b/drivers/usb/input/gtco.c --- a/drivers/usb/input/gtco.c 1969-12-31 17:00:00.000000000 -0700 +++ b/drivers/usb/input/gtco.c 2006-10-08 22:09:02.000000000 -0700 @@ -0,0 +1,1164 @@ +/* [EMAIL PROTECTED] gtco.c + +GTCO digitizer USB driver + +Use the err(), dbg() and info() 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; either version 2 +of the License, or (at your option) any later version. + +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 [EMAIL PROTECTED] +Scott Hill [EMAIL PROTECTED] +*/ + + + +//#define DEBUG + +#include <linux/config.h> +#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/version.h> +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,14)) +/** This define is for kernels 2.6.15 and up where the input driver + * interface has changed + */ +#define USE_INPUT_ALLOCATE +#include <linux/usb_input.h> +#endif + + +#ifdef USE_INPUT_ALLOCATE +#include <linux/usb_input.h> +#endif + +#define GTCO_VERSION "1.00.0005" + +/********************************************************************** *******/ +// 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 + +/** + In kernel 2.6.15+ the input device must be explicitedly allocated, + not part of our own structure. We use this macro to get the pointer + to the input device from our structure, which is allocated + differently depending on the kernel version +*/ +#ifdef USE_INPUT_ALLOCATE +#define GET_INPUT_DEV_PTR(x) (x)->pInputDev +#define FREE_INPUT_DEV(x) input_free_device((x)->pInputDev); +#else +#define GET_INPUT_DEV_PTR(x) &((x)->inputDev) +#define FREE_INPUT_DEV(x) +#endif + + + +/********************************************************************** *******/ +// 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 */ +typedef struct gtco_dev { + +#ifdef USE_INPUT_ALLOCATE + struct input_dev *pInputDev; /**< input device struct pointer */ +#else + struct input_dev inputDev; /**< input device struct */ +#endif + + struct usb_device *pUSBdev; /**< the usb device for this device */ + struct urb *pUrb; /**< 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; + + +} GTCO_DEVICE_T; + + + +/********************************************************************** *******/ +// 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 save 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(GTCO_DEVICE_T *apDevice, char * apReport, int aSize) +{ + 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 globalValue[TAG_GLOB_MAX]; + __u32 oldValue[TAG_GLOB_MAX]; + + // Debug stuff + char mainType='x'; + char gType[12]; + int indent=0; + char indentstr[10]=""; + + + + dbg("======>>>>>>PARSE<<<<<<======"); + + // Walk this report and pull out the info we need + while (i<aSize){ + prefix=apReport[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 = apReport[i]; + break; + case 2: + data16 = le16_to_cpu(get_unaligned((__le16*)(&(apReport[i])))); + break; + case 3: + size = 4; + data32 = le32_to_cpu(get_unaligned((__le32*)(&(apReport[i])))); + } + + // 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(gType,""); + 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(gType,"Variable"); + } + if (data==3){ + strcpy(gType,"Var|Const"); + } + + dbg("::::: Saving Report: %d input #%d Max: 0x%X(%d) Min:0x%X(%d) of %d bits", + globalValue[TAG_GLOB_REPORT_ID],inputNum, + globalValue[TAG_GLOB_LOG_MAX],globalValue[TAG_GLOB_LOG_MAX], + globalValue[TAG_GLOB_LOG_MIN],globalValue[TAG_GLOB_LOG_MIN], + (globalValue[TAG_GLOB_REPORT_SZ] * globalValue[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 (apDevice->max_X == 0){ + apDevice->max_X = globalValue[TAG_GLOB_LOG_MAX]; + apDevice->min_X = globalValue[TAG_GLOB_LOG_MIN]; + } + + break; + case 1: // Y coord + dbg("GER: Y Usage: 0x%x",usage); + if (apDevice->max_Y == 0){ + apDevice->max_Y = globalValue[TAG_GLOB_LOG_MAX]; + apDevice->min_Y = globalValue[TAG_GLOB_LOG_MIN]; + } + break; + default: + // Tilt X + if (usage == DIGITIZER_USAGE_TILT_X){ + if (apDevice->maxTilt_X == 0){ + apDevice->maxTilt_X = globalValue[TAG_GLOB_LOG_MAX]; + apDevice->minTilt_X = globalValue[TAG_GLOB_LOG_MIN]; + } + } + + // Tilt Y + if (usage == DIGITIZER_USAGE_TILT_Y){ + if (apDevice->maxTilt_Y == 0){ + apDevice->maxTilt_Y = globalValue[TAG_GLOB_LOG_MAX]; + apDevice->minTilt_Y = globalValue[TAG_GLOB_LOG_MIN]; + } + } + + + // Pressure + if (usage == DIGITIZER_USAGE_TIP_PRESSURE){ + if (apDevice->maxPressure == 0){ + apDevice->maxPressure = globalValue[TAG_GLOB_LOG_MAX]; + apDevice->minPressure = globalValue[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(gType,"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++){ + oldValue[x] = globalValue[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++){ + globalValue[x] = oldValue[x]; + } + + break; + } + + switch (size){ + case 1: + dbg("%sMAINTAG:(%d) %c SIZE: %d Data: %s 0x%x",indentstr,tag,mainType,size,gType,data); + break; + case 2: + dbg("%sMAINTAG:(%d) %c SIZE: %d Data: %s 0x%x",indentstr,tag,mainType,size,gType, data16); + break; + case 4: + dbg("%sMAINTAG:(%d) %c SIZE: %d Data: %s 0x%x",indentstr,tag,mainType,size,gType,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 (apDevice->usage == 0){ + apDevice->usage = data; + } + strcpy(gType,"USAGE"); + break; + case TAG_GLOB_LOG_MIN : + strcpy(gType,"LOG_MIN"); + break; + case TAG_GLOB_LOG_MAX : + strcpy(gType,"LOG_MAX"); + break; + case TAG_GLOB_PHYS_MIN : + strcpy(gType,"PHYS_MIN"); + break; + case TAG_GLOB_PHYS_MAX : + strcpy(gType,"PHYS_MAX"); + break; + case TAG_GLOB_UNIT_EXP : + strcpy(gType,"EXP"); + break; + case TAG_GLOB_UNIT : + strcpy(gType,"UNIT"); + break; + case TAG_GLOB_REPORT_SZ : + strcpy(gType,"REPORT_SZ"); + break; + case TAG_GLOB_REPORT_ID : + strcpy(gType,"REPORT_ID"); + // New report, restart numbering + inputNum=0; + break; + case TAG_GLOB_REPORT_CNT: + strcpy(gType,"REPORT_CNT"); + break; + case TAG_GLOB_PUSH : + strcpy(gType,"PUSH"); + break; + case TAG_GLOB_POP: + strcpy(gType,"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,gType,tag,size,data); + globalValue[tag]=data; + break; + case 2: + dbg("%sGLOBALTAG:%s(%d) SIZE: %d Data: 0x%x",indentstr,gType,tag,size,data16); + globalValue[tag]=data16; + break; + case 4: + dbg("%sGLOBALTAG:%s(%d) SIZE: %d Data: 0x%x",indentstr,gType,tag,size,data32); + globalValue[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(gType,"USAGE"); + // Always 1 byte + usage = data; + break; + case TAG_GLOB_LOG_MIN : + strcpy(gType,"MIN"); + break; + case TAG_GLOB_LOG_MAX : + strcpy(gType,"MAX"); + break; + default: + strcpy(gType,"UNKNOWN"); + } + + switch (size){ + case 1: + dbg("%sLOCALTAG:(%d) %s SIZE: %d Data: 0x%x",indentstr,tag,gType,size,data); + break; + case 2: + dbg("%sLOCALTAG:(%d) %s SIZE: %d Data: 0x%x",indentstr,tag,gType,size,data16); + break; + case 4: + dbg("%sLOCALTAG:(%d) %s SIZE: %d Data: 0x%x",indentstr,tag,gType,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_InputOpen(struct input_dev *inputdev) +{ + struct gtco_dev *pDevice; + pDevice = inputdev->private; + + // Prevent us from submitting the one Urb more than once + if (pDevice->openCount++) + return 0; + + pDevice->pUrb->dev = pDevice->pUSBdev; + if (usb_submit_urb(pDevice->pUrb, GFP_KERNEL)) { + pDevice->openCount--; + return -EIO; + } + return 0; +} + +/** + Called when closing the input device. This will unlink the URB +*/ +static void GTCO_InputClose(struct input_dev *apDev) +{ + GTCO_DEVICE_T *pDevice = apDev->private; + + // Decrement the open count and release the urb + pDevice->openCount--; + if (pDevice->openCount==0){ + usb_unlink_urb(pDevice->pUrb); + } +} + + +/** + 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 GTCO_DEVICE_T structure + [EMAIL PROTECTED] apInputDev pointer to input device +*/ +static void GTCO_SetupInputCaps(struct input_dev *apInputDev) +{ + GTCO_DEVICE_T *pDevice = apInputDev->private; + u16 pid = pDevice->pUSBdev->descriptor.idProduct; + int i; + + + // Which events + apInputDev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_MSC); + + + // Misc event menu block + apInputDev->mscbit[0] = BIT(MSC_SCAN)|BIT(MSC_SERIAL)|BIT(MSC_RAW) ; + +#ifdef USE_BUTTONS + + // Buttons + // Set for the 16 digitizer buttons starting at TOOL_PEN + for (i=0;i<16;i++){ + apInputDev->keybit[LONG(BTN_DIGI)] |= BIT((BTN_TOOL_PEN+i)) ; + } +#endif + + // Absolute values based on HID report info + input_set_abs_params(apInputDev, ABS_X, pDevice->min_X, pDevice->max_X, 0, 0); + input_set_abs_params(apInputDev, ABS_Y, pDevice->min_Y, pDevice->max_Y, 0, 0); + + // Proximity + input_set_abs_params(apInputDev, ABS_DISTANCE, 0, 1, 0, 0); + + // Tilt & pressure + input_set_abs_params(apInputDev, ABS_TILT_X, pDevice->minTilt_X, pDevice->maxTilt_X, 0, 0); + input_set_abs_params(apInputDev, ABS_TILT_Y, pDevice->minTilt_Y, pDevice->maxTilt_Y, 0, 0); + input_set_abs_params(apInputDev, ABS_PRESSURE, pDevice->minPressure, pDevice->maxPressure, 0, 0); + + + // Transducer + input_set_abs_params(apInputDev, 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 gtcoUrbCallback(struct urb *apUrb, struct pt_regs *apRegs) +{ + + + GTCO_DEVICE_T *pDevice = apUrb->context; + struct input_dev *pInput; + int rc,i; + u32 val = 0; + s8 valSigned = 0; + char le_buffer[2]; + + pInput = GET_INPUT_DEV_PTR(pDevice); + + + // Was callback OK? + if ((apUrb->status == -ECONNRESET ) || + (apUrb->status == -ENOENT ) || + (apUrb->status == -ESHUTDOWN )){ + + // Shutdown is occurring. Return and don't queue up any more + return; + } + + if (apUrb->status != 0 ) { + // Some unknown error. Hopefully temporary. Just go and + // requeue an URB + goto resubmit; + } + + // + // Good URB, now process + // + input_regs(pInput, apRegs); + + // PID dependent when we interpret the report + if ((pInput->id.product == PID_1000 )|| + (pInput->id.product == PID_1001 )|| + (pInput->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(pDevice->buffer[0]){ + case 5: + // Pressure is 9 bits + val = ((u16)(pDevice->buffer[8]) << 1); + val |= (u16)(pDevice->buffer[7] >> 7); + input_report_abs(pInput, ABS_PRESSURE, pDevice->buffer[8]); + + // Mask out the Y tilt value used for pressure + pDevice->buffer[7] = (u8)((pDevice->buffer[7]) & 0x7F); + + + // Fall thru + case 4: + // Tilt + + // Sign extend these 7 bit numbers. + if (pDevice->buffer[6] & 0x40) + pDevice->buffer[6] |= 0x80; + + if (pDevice->buffer[7] & 0x40) + pDevice->buffer[7] |= 0x80; + + + valSigned = (pDevice->buffer[6]); + input_report_abs(pInput, ABS_TILT_X, (s32)valSigned); + + valSigned = (pDevice->buffer[7]); + input_report_abs(pInput, ABS_TILT_Y, (s32)valSigned); + + // Fall thru + + case 2: + case 3: + // Convert buttons, only 5 bits possible + val = (pDevice->buffer[5])&MASK_BUTTON; +#ifdef USE_BUTTONS + for ( i=0;i<5;i++){ + input_report_key(pInput, BTN_DIGI+i,val&(1<<i)); + } +#else + // We don't apply any meaning to the bitmask, just report + input_event(pInput, EV_MSC, MSC_SERIAL, val); +#endif + // Fall thru + case 1: + + // All reports have X and Y coords in the same place + val = le16_to_cpu(*(__le16 *) &(pDevice->buffer[1])); + input_report_abs(pInput, ABS_X, val); + + val = le16_to_cpu(*(__le16 *) &(pDevice->buffer[3])); + input_report_abs(pInput, ABS_Y, val); + + + // Ditto for proximity bit + if (pDevice->buffer[5]& MASK_INRANGE){ + val = 1; + }else{ + val=0; + } + input_report_abs(pInput, ABS_DISTANCE, val); + + + // Report 1 is an exception to how we handle buttons + // Buttons are an index, not a bitmask + if (pDevice->buffer[0] == 1){ + int pressed=0; + + // Convert buttons, 5 bit index + // Report value of index set as one, the rest as 0 + val = pDevice->buffer[5]& MASK_BUTTON; + dbg("======>>>>>>REPORT 1: val 0x%X(%d)",val,val); + +#ifdef USE_BUTTONS + for ( i=0;i<16;i++){ + pressed = 0; + // Is a button active + if (val != 0){ + if (i==(val-1)){ + pressed = 1; + dbg("======>>>>>>pressed 1: val (%d) i:%d",val,i); + } + } + input_report_key(pInput, BTN_DIGI+i,pressed); + } +#else + // We don't apply any meaning to the button index, just report + // it + input_event(pInput, EV_MSC, MSC_SERIAL, val); +#endif + + + } + + break; + case 7: + // Menu blocks + input_event(pInput, EV_MSC, MSC_SCAN, pDevice->buffer[1]); + + + break; + + } + + + } + // Other pid class + if ((pInput->id.product == PID_400 )|| + (pInput->id.product == PID_401 )) + { + + // Report 2 + if (pDevice->buffer[0] == 2){ + // Menu blocks + input_event(pInput, EV_MSC, MSC_SCAN, pDevice->buffer[1]); + } + + // Report 1 + if (pDevice->buffer[0] == 1){ + char buttonbyte; + + + // IF X max > 64K, we still a bit from the y report + if (pDevice->max_X > 0x10000){ + + val = (u16)(((u16)(pDevice->buffer[2]<<8))|((u8)(pDevice->buffer[1]))); + val |= (u32)(((u8)pDevice->buffer[3]&0x1)<< 16); + + input_report_abs(pInput, ABS_X, val); + + le_buffer[0] = (u8)((u8)(pDevice->buffer[3])>>1); + le_buffer[0] |= (u8)((pDevice->buffer[3]&0x1)<<7); + + le_buffer[1] = (u8)(pDevice->buffer[4]>>1); + le_buffer[1] |= (u8)((pDevice->buffer[5]&0x1)<<7); + + val = le16_to_cpu(*(__le16 *)(le_buffer)); + + input_report_abs(pInput, ABS_Y, val); + + + // Shift the button byte right by one to make it look like + // the standard report + buttonbyte = (pDevice->buffer[5])>>1; + }else{ + + val = le16_to_cpu(*(__le16 *) (&(pDevice->buffer[1]))); + input_report_abs(pInput, ABS_X, val); + + val = le16_to_cpu(*(__le16 *) (&(pDevice->buffer[3]))); + input_report_abs(pInput, ABS_Y, val); + + buttonbyte = pDevice->buffer[5]; + + } + + + // BUTTONS and PROXIMITY + if (buttonbyte& MASK_INRANGE){ + val = 1; + }else{ + val=0; + } + input_report_abs(pInput, ABS_DISTANCE, val); + + // Convert buttons, only 4 bits possible + val = buttonbyte&0x0F; +#ifdef USE_BUTTONS + for ( i=0;i<5;i++){ + input_report_key(pInput, BTN_DIGI+i,val&(1<<i)); + } +#else + // We don't apply any meaning to the bitmask, just report + input_event(pInput, EV_MSC, MSC_SERIAL, val); +#endif + // TRANSDUCER + input_report_abs(pInput, ABS_MISC, pDevice->buffer[6]); + + } + } + + // Everybody gets report ID's + input_event(pInput, EV_MSC, MSC_RAW, pDevice->buffer[0]); + + // Sync it up + input_sync(pInput); + +resubmit: + rc = usb_submit_urb(apUrb, 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 *apInterface, + const struct usb_device_id *apID) +{ + + GTCO_DEVICE_T *pDevice = NULL; + char path[PATHLENGTH]; + struct input_dev *pInput; + struct hid_descriptor *pHIDdesc; + char *pReport; + int result=0, retry; + struct usb_endpoint_descriptor *endpoint; + + // Allocate memory for device structure + pDevice = kmalloc(sizeof(GTCO_DEVICE_T), GFP_KERNEL); + if (pDevice == NULL) { + err("No more memory"); + return -ENOMEM; + } + memset(pDevice, 0x00, sizeof (GTCO_DEVICE_T)); + +#ifdef USE_INPUT_ALLOCATE + + // New interface, we must allocate an input device, not declare one + // in our own data space + pDevice->pInputDev = input_allocate_device(); + if (!pDevice->pInputDev){ + kfree(pDevice); + err("No more memory"); + return -ENOMEM; + } +#endif + // Get pointer to the input device + pInput = GET_INPUT_DEV_PTR(pDevice); + + // Save interface information + pDevice->pUSBdev = usb_get_dev(interface_to_usbdev(apInterface)); + + + // Allocate some data for incoming reports + pDevice->buffer = usb_buffer_alloc(pDevice->pUSBdev, REPORT_MAX_SIZE, + SLAB_ATOMIC, &(pDevice->buf_dma)); + if (!pDevice->buffer){ + FREE_INPUT_DEV(pDevice); + kfree(pDevice); + err("No more memory"); + return -ENOMEM; + } + + // Allocate URB for reports + pDevice->pUrb = usb_alloc_urb(0, GFP_KERNEL); + if (!pDevice->pUrb) { + usb_buffer_free(pDevice->pUSBdev, REPORT_MAX_SIZE, pDevice->buffer, + pDevice->buf_dma); + FREE_INPUT_DEV(pDevice); + kfree(pDevice); + err("No more memory"); + return -ENOMEM; + } + + + // The endpoint is always altsetting 0, we know this since we know + // this device only has one interrupt endpoint + endpoint = &apInterface->altsetting[0].endpoint[0].desc; + + // Some debug + dbg("gtco # interfaces: %d",apInterface->num_altsetting); + dbg("num endpoints: %d",apInterface->cur_altsetting->desc.bNumEndpoints); + dbg("interface class: %d",apInterface->cur_altsetting->desc.bInterfaceClass); + dbg("endpoint: attribute:0x%x type:0x%x",endpoint->bmAttributes,endpoint->bDescriptorType); + if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) + dbg("endpoint: we have interrupt endpoint\n"); + + dbg("endpoint extra len:%d ",apInterface->altsetting[0].extralen); + + + + // Find the HID descriptor so we can find out the size of the + // HID report descriptor + if (usb_get_extra_descriptor(apInterface->cur_altsetting, HID_DEVICE_TYPE,&pHIDdesc) != 0){ + err("Can't retrieve exta USB descriptor to get hid report descriptor length"); + usb_buffer_free(pDevice->pUSBdev, REPORT_MAX_SIZE, pDevice->buffer, + pDevice->buf_dma); + FREE_INPUT_DEV(pDevice); + kfree(pDevice); + return -EIO; + } + + dbg("Extra descriptor success: type:%d len:%d", pHIDdesc->bDescriptorType, + pHIDdesc->wDescriptorLength); + + if (!(pReport = kmalloc(pHIDdesc->wDescriptorLength, GFP_KERNEL))) { + usb_buffer_free(pDevice->pUSBdev, REPORT_MAX_SIZE, pDevice->buffer, + pDevice->buf_dma); + + FREE_INPUT_DEV(pDevice); + kfree(pDevice); + err("No more memory"); + return -ENOMEM; + } + memset(pReport,0,pHIDdesc->wDescriptorLength); + + // Couple of tries to get reply + for (retry=0;retry<3;retry++) { + result = usb_control_msg(pDevice->pUSBdev, + usb_rcvctrlpipe(pDevice->pUSBdev, 0), + USB_REQ_GET_DESCRIPTOR, USB_RECIP_INTERFACE | USB_DIR_IN, + (REPORT_DEVICE_TYPE << 8), + 0, // interface + pReport, + pHIDdesc->wDescriptorLength, + 5000); // 5 secs + + if (result == pHIDdesc->wDescriptorLength) + break; + } + + // If we didn't get the report, fail + dbg("usb_control_msg result: :%d", result); + if (result != pHIDdesc->wDescriptorLength){ + kfree(pReport); + usb_buffer_free(pDevice->pUSBdev, REPORT_MAX_SIZE, pDevice->buffer, + pDevice->buf_dma); + FREE_INPUT_DEV(pDevice); + kfree(pDevice); + err("Failed to get HID Report Descriptor of size: %d",pHIDdesc->wDescriptorLength); + return -EIO; + } + + + // Now we parse the report + parse_hid_report_descriptor(pDevice,pReport,result); + + // Now we delete it + kfree(pReport); + + // Create a device file node + usb_make_path(pDevice->pUSBdev, path, PATHLENGTH); + sprintf(pDevice->usbpath, "%s/input0", path); + + + // Set Input device functions + pInput->open = GTCO_InputOpen; + pInput->close = GTCO_InputClose; + + // Set input device information + pInput->name = "GTCO_CalComp"; + pInput->phys = pDevice->usbpath; + pInput->private = pDevice; + + + // Now set up all the input device capabilities + GTCO_SetupInputCaps(pInput); + +#ifdef USE_INPUT_ALLOCATE + // Set input device required ID information + usb_to_input_id(pDevice->pUSBdev, &pDevice->pInputDev->id); + pInput->cdev.dev = &apInterface->dev; +#else + pInput->id.bustype = BUS_USB; + pInput->id.vendor = pDevice->pUSBdev->descriptor.idVendor; + pInput->id.product = pDevice->pUSBdev->descriptor.idProduct; + pInput->id.version = pDevice->pUSBdev->descriptor.bcdDevice; + + pInput->dev = &apInterface->dev; +#endif + + // Setup the URB, it will be posted later on open of input device + endpoint = &apInterface->altsetting[0].endpoint[0].desc; + + usb_fill_int_urb(pDevice->pUrb, + pDevice->pUSBdev, + usb_rcvintpipe(pDevice->pUSBdev, + endpoint->bEndpointAddress), + pDevice->buffer, + REPORT_MAX_SIZE, + gtcoUrbCallback, + pDevice, + endpoint->bInterval); + + pDevice->pUrb->transfer_dma = pDevice->buf_dma; + pDevice->pUrb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + + // Save device pointer in USB interface device */ + usb_set_intfdata(apInterface, pDevice); + + // All done, now register the input device + input_register_device(pInput); + + info( "gtco driver created usb: %s\n", path); + return 0; + +} + +/** + 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 + [EMAIL PROTECTED] apInterface Pointer to usb_interface structure [EMAIL PROTECTED] None +*/ +static void GTCO_Disconnect(struct usb_interface *apInterface) +{ + + // Grab private device ptr + GTCO_DEVICE_T *pDevice = usb_get_intfdata (apInterface); + struct input_dev *pInput; + + pInput = GET_INPUT_DEV_PTR(pDevice); + + // Now reverse all the registration stuff + if (pDevice) { + usb_unlink_urb(pDevice->pUrb); + input_unregister_device(pInput); + usb_free_urb(pDevice->pUrb); + usb_buffer_free(pDevice->pUSBdev, REPORT_MAX_SIZE, pDevice->buffer, + pDevice->buf_dma); + kfree(pDevice); + } + + info("gtco driver disconnected"); +} + +/********************************************************************** ********/ +// STANDARD MODULE LOAD ROUTINES +/********************************************************************** ********/ +static struct usb_driver gtco_driverinfo_table = { +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16)) + .owner = THIS_MODULE, +#endif + .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 rc; + rc = usb_register(>co_driverinfo_table); + if (rc) { + err("usb_register() failed rc=0x%x", rc); + } + printk("GTCO usb driver version: %s",GTCO_VERSION); + return rc; +} + +/** + 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_LICENSE("Proprietary"); +MODULE_LICENSE("GPL"); diff -uprN a/drivers/usb/input/Kconfig b/drivers/usb/input/Kconfig --- a/drivers/usb/input/Kconfig 2006-12-11 12:32:53.000000000 -0700 +++ b/drivers/usb/input/Kconfig 2007-01-02 11:09:59.000000000 -0700 @@ -348,3 +348,15 @@ config USB_APPLETOUCH To compile this driver as a module, choose M here: the module will be called appletouch. + +config USB_GTCO + tristate "GTCO CalComp/InterWrite USB Support" + depends on USB && INPUT + help + Say Y here if you want to use the USB version of the GTCO + CalComp/InterWrite Tablet. Make sure to say Y to "Mouse support" + (CONFIG_INPUT_MOUSEDEV) and/or "Event interface support" + (CONFIG_INPUT_EVDEV) as well. + + To compile this driver as a module, choose M here: the + module will be called gtco. diff -uprN a/drivers/usb/input/Makefile b/drivers/usb/input/Makefile --- a/drivers/usb/input/Makefile 2006-12-11 12:32:53.000000000 -0700 +++ b/drivers/usb/input/Makefile 2007-01-02 11:08:06.000000000 -0700 @@ -48,6 +48,7 @@ obj-$(CONFIG_USB_ACECAD) += acecad.o obj-$(CONFIG_USB_YEALINK) += yealink.o obj-$(CONFIG_USB_XPAD) += xpad.o obj-$(CONFIG_USB_APPLETOUCH) += appletouch.o +obj-$(CONFIG_USB_GTCO) += gtco.o ifeq ($(CONFIG_USB_DEBUG),y) EXTRA_CFLAGS += -DDEBUG ------------------------------------------------------------------------- Take Surveys. Earn Cash. Influence the Future of IT Join SourceForge.net's Techsay panel and you'll get the chance to share your opinions on IT & business topics through brief surveys - and earn cash http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV _______________________________________________ linux-usb-devel@lists.sourceforge.net To unsubscribe, use the last form field at: https://lists.sourceforge.net/lists/listinfo/linux-usb-devel