| /* -*- linux-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; 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/version.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 = le16_to_cpu(get_unaligned((__le16*)(&(report[i])))); |
| break; |
| case 3: |
| size = 4; |
| data32 = le32_to_cpu(get_unaligned((__le32*)(&(report[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(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"); |
| } |
| 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"); |
| } |
| |
| 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; |
| device = inputdev->private; |
| |
| 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 = inputdev->private; |
| |
| 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 = inputdev->private; |
| |
| |
| /* Which events */ |
| inputdev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_MSC); |
| |
| |
| /* Misc event menu block */ |
| inputdev->mscbit[0] = BIT(MSC_SCAN)|BIT(MSC_SERIAL)|BIT(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 = le16_to_cpu(get_unaligned((__le16 *) &(device->buffer[1]))); |
| input_report_abs(inputdev, ABS_X, val); |
| |
| val = le16_to_cpu(get_unaligned((__le16 *) &(device->buffer[3]))); |
| input_report_abs(inputdev, ABS_Y, val); |
| |
| |
| /* Ditto for proximity bit */ |
| if (device->buffer[5]& MASK_INRANGE){ |
| val = 1; |
| }else{ |
| val=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 = le16_to_cpu(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 = le16_to_cpu(get_unaligned((__le16 *) (&(device->buffer[1])))); |
| input_report_abs(inputdev, ABS_X, val); |
| |
| val = le16_to_cpu(get_unaligned((__le16 *) (&(device->buffer[3])))); |
| input_report_abs(inputdev, ABS_Y, val); |
| |
| buttonbyte = device->buffer[5]; |
| |
| } |
| |
| |
| /* BUTTONS and PROXIMITY */ |
| if (buttonbyte& MASK_INRANGE){ |
| val = 1; |
| }else{ |
| val=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 *device = NULL; |
| char path[PATHLENGTH]; |
| struct input_dev *inputdev; |
| struct hid_descriptor *hid_desc; |
| char *report; |
| int result=0, retry; |
| struct usb_endpoint_descriptor *endpoint; |
| |
| /* Allocate memory for device structure */ |
| device = kzalloc(sizeof(struct gtco), GFP_KERNEL); |
| if (device == NULL) { |
| err("No more memory"); |
| return -ENOMEM; |
| } |
| |
| |
| device->inputdevice = input_allocate_device(); |
| if (!device->inputdevice){ |
| kfree(device); |
| err("No more memory"); |
| return -ENOMEM; |
| } |
| |
| /* Get pointer to the input device */ |
| inputdev = device->inputdevice; |
| |
| /* Save interface information */ |
| device->usbdev = usb_get_dev(interface_to_usbdev(usbinterface)); |
| |
| |
| /* Allocate some data for incoming reports */ |
| device->buffer = usb_buffer_alloc(device->usbdev, REPORT_MAX_SIZE, |
| GFP_KERNEL, &(device->buf_dma)); |
| if (!device->buffer){ |
| input_free_device(device->inputdevice); |
| kfree(device); |
| err("No more memory"); |
| return -ENOMEM; |
| } |
| |
| /* Allocate URB for reports */ |
| device->urbinfo = usb_alloc_urb(0, GFP_KERNEL); |
| if (!device->urbinfo) { |
| usb_buffer_free(device->usbdev, REPORT_MAX_SIZE, |
| device->buffer, device->buf_dma); |
| input_free_device(device->inputdevice); |
| kfree(device); |
| 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 = &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 ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) |
| 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"); |
| usb_buffer_free(device->usbdev, REPORT_MAX_SIZE, |
| device->buffer, device->buf_dma); |
| input_free_device(device->inputdevice); |
| kfree(device); |
| return -EIO; |
| } |
| |
| dbg("Extra descriptor success: type:%d len:%d", |
| hid_desc->bDescriptorType, hid_desc->wDescriptorLength); |
| |
| if (!(report = kzalloc(hid_desc->wDescriptorLength, GFP_KERNEL))) { |
| usb_buffer_free(device->usbdev, REPORT_MAX_SIZE, |
| device->buffer, device->buf_dma); |
| |
| input_free_device(device->inputdevice); |
| kfree(device); |
| err("No more memory"); |
| return -ENOMEM; |
| } |
| |
| /* Couple of tries to get reply */ |
| for (retry=0;retry<3;retry++) { |
| result = usb_control_msg(device->usbdev, |
| usb_rcvctrlpipe(device->usbdev, 0), |
| USB_REQ_GET_DESCRIPTOR, |
| USB_RECIP_INTERFACE | USB_DIR_IN, |
| (REPORT_DEVICE_TYPE << 8), |
| 0, /* interface */ |
| report, |
| hid_desc->wDescriptorLength, |
| 5000); /* 5 secs */ |
| |
| if (result == hid_desc->wDescriptorLength) |
| break; |
| } |
| |
| /* If we didn't get the report, fail */ |
| dbg("usb_control_msg result: :%d", result); |
| if (result != hid_desc->wDescriptorLength){ |
| kfree(report); |
| usb_buffer_free(device->usbdev, REPORT_MAX_SIZE, |
| device->buffer, device->buf_dma); |
| input_free_device(device->inputdevice); |
| kfree(device); |
| err("Failed to get HID Report Descriptor of size: %d", |
| hid_desc->wDescriptorLength); |
| return -EIO; |
| } |
| |
| |
| /* Now we parse the report */ |
| parse_hid_report_descriptor(device,report,result); |
| |
| /* Now we delete it */ |
| kfree(report); |
| |
| /* Create a device file node */ |
| usb_make_path(device->usbdev, path, PATHLENGTH); |
| sprintf(device->usbpath, "%s/input0", path); |
| |
| |
| /* Set Input device functions */ |
| inputdev->open = gtco_input_open; |
| inputdev->close = gtco_input_close; |
| |
| /* Set input device information */ |
| inputdev->name = "GTCO_CalComp"; |
| inputdev->phys = device->usbpath; |
| inputdev->private = device; |
| |
| |
| /* Now set up all the input device capabilities */ |
| gtco_setup_caps(inputdev); |
| |
| /* Set input device required ID information */ |
| usb_to_input_id(device->usbdev, &device->inputdevice->id); |
| inputdev->cdev.dev = &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(device->urbinfo, |
| device->usbdev, |
| usb_rcvintpipe(device->usbdev, |
| endpoint->bEndpointAddress), |
| device->buffer, |
| REPORT_MAX_SIZE, |
| gtco_urb_callback, |
| device, |
| endpoint->bInterval); |
| |
| device->urbinfo->transfer_dma = device->buf_dma; |
| device->urbinfo->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; |
| |
| |
| /* Save device pointer in USB interface device */ |
| usb_set_intfdata(usbinterface, device); |
| |
| /* All done, now register the input device */ |
| input_register_device(inputdev); |
| |
| 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 |
| */ |
| static void gtco_disconnect(struct usb_interface *interface) |
| { |
| |
| /* Grab private device ptr */ |
| struct gtco *device = usb_get_intfdata (interface); |
| struct input_dev *inputdev; |
| |
| inputdev = device->inputdevice; |
| |
| /* Now reverse all the registration stuff */ |
| if (device) { |
| input_unregister_device(inputdev); |
| usb_kill_urb(device->urbinfo); |
| usb_free_urb(device->urbinfo); |
| usb_buffer_free(device->usbdev, REPORT_MAX_SIZE, |
| device->buffer, device->buf_dma); |
| kfree(device); |
| } |
| |
| 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("GPL"); |