forked from luck/tmp_suning_uos_patched
a19ceb56cb
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 <jroberson@gtcocalcomp.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
1105 lines
27 KiB
C
1105 lines
27 KiB
C
/* -*- 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");
|