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(&gtco_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(&gtco_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

Reply via email to