Newer
Older
Jeremy Roberson
committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
/* -*- linux-c -*-
GTCO digitizer USB driver
TO CHECK: Is pressure done right on report 5?
Copyright (C) 2006 GTCO CalComp
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Permission to use, copy, modify, distribute, and sell this software and its
documentation for any purpose is hereby granted without fee, provided that
the above copyright notice appear in all copies and that both that
copyright notice and this permission notice appear in supporting
documentation, and that the name of GTCO-CalComp not be used in advertising
or publicity pertaining to distribution of the software without specific,
written prior permission. GTCO-CalComp makes no representations about the
suitability of this software for any purpose. It is provided "as is"
without express or implied warranty.
GTCO-CALCOMP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
EVENT SHALL GTCO-CALCOMP BE LIABLE FOR ANY SPECIAL, INDIRECT OR
CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTIONS, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
GTCO CalComp, Inc.
7125 Riverwood Drive
Columbia, MD 21046
Jeremy Roberson jroberson@gtcocalcomp.com
Scott Hill shill@gtcocalcomp.com
*/
/*#define DEBUG*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/usb.h>
#include <asm/uaccess.h>
#include <asm/unaligned.h>
#include <asm/byteorder.h>
#include <linux/usb/input.h>
/* Version with a Major number of 2 is for kernel inclusion only. */
#define GTCO_VERSION "2.00.0006"
/* MACROS */
#define VENDOR_ID_GTCO 0x078C
#define PID_400 0x400
#define PID_401 0x401
#define PID_1000 0x1000
#define PID_1001 0x1001
#define PID_1002 0x1002
/* Max size of a single report */
#define REPORT_MAX_SIZE 10
/* Bitmask whether pen is in range */
#define MASK_INRANGE 0x20
#define MASK_BUTTON 0x01F
#define PATHLENGTH 64
/* DATA STRUCTURES */
/* Device table */
static const struct usb_device_id gtco_usbid_table[] = {
Jeremy Roberson
committed
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
{ 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;
Jeremy Roberson
committed
/* 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';
Jeremy Roberson
committed
char globtype[12];
int indent = 0;
char indentstr[10] = "";
Jeremy Roberson
committed
dbg("======>>>>>>PARSE<<<<<<======");
/* Walk this report and pull out the info we need */
while (i < length) {
prefix = report[i];
Jeremy Roberson
committed
/* Skip over prefix */
i++;
/* Determine data size and save the data in the proper variable */
size = PREF_SIZE(prefix);
switch (size) {
Jeremy Roberson
committed
case 1:
data = report[i];
break;
case 2:
data16 = get_unaligned_le16(&report[i]);
Jeremy Roberson
committed
break;
case 3:
size = 4;
data32 = get_unaligned_le32(&report[i]);
Jeremy Roberson
committed
}
/* Skip size of data */
Jeremy Roberson
committed
/* What we do depends on the tag type */
tag = PREF_TAG(prefix);
type = PREF_TYPE(prefix);
switch (type) {
Jeremy Roberson
committed
case TYPE_MAIN:
strcpy(globtype, "");
switch (tag) {
Jeremy Roberson
committed
case TAG_MAIN_INPUT:
/*
* The INPUT MAIN tag signifies this is
* information from a report. We need to
* figure out what it is and store the
* min/max values
*/
maintype = 'I';
if (data == 2)
strcpy(globtype, "Variable");
else if (data == 3)
strcpy(globtype, "Var|Const");
Jeremy Roberson
committed
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]);
Jeremy Roberson
committed
/*
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) {
Jeremy Roberson
committed
case 0: /* X coord */
dbg("GER: X Usage: 0x%x", usage);
if (device->max_X == 0) {
Jeremy Roberson
committed
device->max_X = globalval[TAG_GLOB_LOG_MAX];
device->min_X = globalval[TAG_GLOB_LOG_MIN];
}
break;
Jeremy Roberson
committed
case 1: /* Y coord */
dbg("GER: Y Usage: 0x%x", usage);
if (device->max_Y == 0) {
Jeremy Roberson
committed
device->max_Y = globalval[TAG_GLOB_LOG_MAX];
device->min_Y = globalval[TAG_GLOB_LOG_MIN];
}
break;
Jeremy Roberson
committed
default:
/* Tilt X */
if (usage == DIGITIZER_USAGE_TILT_X) {
if (device->maxtilt_X == 0) {
Jeremy Roberson
committed
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) {
Jeremy Roberson
committed
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) {
Jeremy Roberson
committed
device->maxpressure = globalval[TAG_GLOB_LOG_MAX];
device->minpressure = globalval[TAG_GLOB_LOG_MIN];
}
}
break;
}
inputnum++;
break;
Jeremy Roberson
committed
case TAG_MAIN_OUTPUT:
maintype = 'O';
Jeremy Roberson
committed
break;
Jeremy Roberson
committed
case TAG_MAIN_FEATURE:
maintype = 'F';
Jeremy Roberson
committed
break;
Jeremy Roberson
committed
case TAG_MAIN_COL_START:
maintype = 'S';
Jeremy Roberson
committed
if (data == 0) {
Jeremy Roberson
committed
dbg("======>>>>>> Physical");
strcpy(globtype, "Physical");
} else
Jeremy Roberson
committed
dbg("======>>>>>>");
/* Indent the debug output */
indent++;
for (x = 0; x < indent; x++)
indentstr[x] = '-';
indentstr[x] = 0;
Jeremy Roberson
committed
/* Save global tags */
for (x = 0; x < TAG_GLOB_MAX; x++)
Jeremy Roberson
committed
oldval[x] = globalval[x];
break;
Jeremy Roberson
committed
case TAG_MAIN_COL_END:
dbg("<<<<<<======");
maintype = 'E';
Jeremy Roberson
committed
indent--;
for (x = 0; x < indent; x++)
indentstr[x] = '-';
indentstr[x] = 0;
Jeremy Roberson
committed
/* Copy global tags back */
for (x = 0; x < TAG_GLOB_MAX; x++)
Jeremy Roberson
committed
globalval[x] = oldval[x];
break;
}
switch (size) {
Jeremy Roberson
committed
case 1:
dbg("%sMAINTAG:(%d) %c SIZE: %d Data: %s 0x%x",
indentstr, tag, maintype, size, globtype, data);
Jeremy Roberson
committed
break;
Jeremy Roberson
committed
case 2:
dbg("%sMAINTAG:(%d) %c SIZE: %d Data: %s 0x%x",
indentstr, tag, maintype, size, globtype, data16);
Jeremy Roberson
committed
break;
Jeremy Roberson
committed
case 4:
dbg("%sMAINTAG:(%d) %c SIZE: %d Data: %s 0x%x",
indentstr, tag, maintype, size, globtype, data32);
Jeremy Roberson
committed
break;
}
break;
Jeremy Roberson
committed
case TYPE_GLOBAL:
switch (tag) {
Jeremy Roberson
committed
case TAG_GLOB_USAGE:
/*
* First time we hit the global usage tag,
* it should tell us the type of device
*/
if (device->usage == 0)
Jeremy Roberson
committed
device->usage = data;
strcpy(globtype, "USAGE");
Jeremy Roberson
committed
break;
case TAG_GLOB_LOG_MIN:
strcpy(globtype, "LOG_MIN");
Jeremy Roberson
committed
break;
case TAG_GLOB_LOG_MAX:
strcpy(globtype, "LOG_MAX");
Jeremy Roberson
committed
break;
case TAG_GLOB_PHYS_MIN:
strcpy(globtype, "PHYS_MIN");
Jeremy Roberson
committed
break;
case TAG_GLOB_PHYS_MAX:
strcpy(globtype, "PHYS_MAX");
Jeremy Roberson
committed
break;
case TAG_GLOB_UNIT_EXP:
strcpy(globtype, "EXP");
Jeremy Roberson
committed
break;
case TAG_GLOB_UNIT:
strcpy(globtype, "UNIT");
Jeremy Roberson
committed
break;
case TAG_GLOB_REPORT_SZ:
strcpy(globtype, "REPORT_SZ");
Jeremy Roberson
committed
break;
case TAG_GLOB_REPORT_ID:
strcpy(globtype, "REPORT_ID");
Jeremy Roberson
committed
/* New report, restart numbering */
inputnum = 0;
Jeremy Roberson
committed
break;
Jeremy Roberson
committed
case TAG_GLOB_REPORT_CNT:
strcpy(globtype, "REPORT_CNT");
Jeremy Roberson
committed
break;
case TAG_GLOB_PUSH:
strcpy(globtype, "PUSH");
Jeremy Roberson
committed
break;
Jeremy Roberson
committed
case TAG_GLOB_POP:
strcpy(globtype, "POP");
Jeremy Roberson
committed
break;
}
/* Check to make sure we have a good tag number
so we don't overflow array */
if (tag < TAG_GLOB_MAX) {
switch (size) {
Jeremy Roberson
committed
case 1:
dbg("%sGLOBALTAG:%s(%d) SIZE: %d Data: 0x%x",
indentstr, globtype, tag, size, data);
globalval[tag] = data;
Jeremy Roberson
committed
break;
Jeremy Roberson
committed
case 2:
dbg("%sGLOBALTAG:%s(%d) SIZE: %d Data: 0x%x",
indentstr, globtype, tag, size, data16);
globalval[tag] = data16;
Jeremy Roberson
committed
break;
Jeremy Roberson
committed
case 4:
dbg("%sGLOBALTAG:%s(%d) SIZE: %d Data: 0x%x",
indentstr, globtype, tag, size, data32);
globalval[tag] = data32;
Jeremy Roberson
committed
break;
}
Jeremy Roberson
committed
dbg("%sGLOBALTAG: ILLEGAL TAG:%d SIZE: %d ",
indentstr, tag, size);
Jeremy Roberson
committed
}
break;
case TYPE_LOCAL:
switch (tag) {
Jeremy Roberson
committed
case TAG_GLOB_USAGE:
strcpy(globtype, "USAGE");
Jeremy Roberson
committed
/* Always 1 byte */
usage = data;
break;
case TAG_GLOB_LOG_MIN:
strcpy(globtype, "MIN");
Jeremy Roberson
committed
break;
case TAG_GLOB_LOG_MAX:
strcpy(globtype, "MAX");
Jeremy Roberson
committed
break;
Jeremy Roberson
committed
default:
strcpy(globtype, "UNKNOWN");
break;
Jeremy Roberson
committed
}
switch (size) {
Jeremy Roberson
committed
case 1:
dbg("%sLOCALTAG:(%d) %s SIZE: %d Data: 0x%x",
indentstr, tag, globtype, size, data);
Jeremy Roberson
committed
break;
Jeremy Roberson
committed
case 2:
dbg("%sLOCALTAG:(%d) %s SIZE: %d Data: 0x%x",
indentstr, tag, globtype, size, data16);
Jeremy Roberson
committed
break;
Jeremy Roberson
committed
case 4:
dbg("%sLOCALTAG:(%d) %s SIZE: %d Data: 0x%x",
indentstr, tag, globtype, size, data32);
Jeremy Roberson
committed
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
Jeremy Roberson
committed
*/
static int gtco_input_open(struct input_dev *inputdev)
{
struct gtco *device = input_get_drvdata(inputdev);
Jeremy Roberson
committed
device->urbinfo->dev = device->usbdev;
if (usb_submit_urb(device->urbinfo, GFP_KERNEL))
Jeremy Roberson
committed
return -EIO;
Jeremy Roberson
committed
return 0;
}
/*
* Called when closing the input device. This will unlink the URB
*/
Jeremy Roberson
committed
static void gtco_input_close(struct input_dev *inputdev)
{
struct gtco *device = input_get_drvdata(inputdev);
Jeremy Roberson
committed
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)
Jeremy Roberson
committed
{
struct gtco *device = input_get_drvdata(inputdev);
Jeremy Roberson
committed
/* Which events */
inputdev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) |
BIT_MASK(EV_MSC);
Jeremy Roberson
committed
/* Misc event menu block */
inputdev->mscbit[0] = BIT_MASK(MSC_SCAN) | BIT_MASK(MSC_SERIAL) |
BIT_MASK(MSC_RAW);
Jeremy Roberson
committed
/* 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);
Jeremy Roberson
committed
}
/* 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;
Jeremy Roberson
committed
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) {
Jeremy Roberson
committed
/* 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
*/
Jeremy Roberson
committed
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) {
Jeremy Roberson
committed
/*
* 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]) {
Jeremy Roberson
committed
case 5:
/* Pressure is 9 bits */
val = ((u16)(device->buffer[8]) << 1);
Jeremy Roberson
committed
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
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;
Jeremy Roberson
committed
/* We don't apply any meaning to the bitmask,
just report */
input_event(inputdev, EV_MSC, MSC_SERIAL, val);
/* Fall thru */
case 1:
/* All reports have X and Y coords in the same place */
val = get_unaligned_le16(&device->buffer[1]);
Jeremy Roberson
committed
input_report_abs(inputdev, ABS_X, val);
val = get_unaligned_le16(&device->buffer[3]);
Jeremy Roberson
committed
input_report_abs(inputdev, ABS_Y, val);
/* Ditto for proximity bit */
val = device->buffer[5] & MASK_INRANGE ? 1 : 0;
Jeremy Roberson
committed
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) {
Jeremy Roberson
committed
/*
* Convert buttons, 5 bit index
* Report value of index set as one,
* the rest as 0
*/
val = device->buffer[5] & MASK_BUTTON;
Jeremy Roberson
committed
dbg("======>>>>>>REPORT 1: val 0x%X(%d)",
Jeremy Roberson
committed
/*
* We don't apply any meaning to the button
* index, just report it
*/
input_event(inputdev, EV_MSC, MSC_SERIAL, val);
}
break;
Jeremy Roberson
committed
case 7:
/* Menu blocks */
input_event(inputdev, EV_MSC, MSC_SCAN,
device->buffer[1]);
break;
}
}
Jeremy Roberson
committed
/* Other pid class */
if (inputdev->id.product == PID_400 ||
inputdev->id.product == PID_401) {
Jeremy Roberson
committed
/* Report 2 */
if (device->buffer[0] == 2) {
Jeremy Roberson
committed
/* Menu blocks */
input_event(inputdev, EV_MSC, MSC_SCAN, device->buffer[1]);
Jeremy Roberson
committed
}
/* Report 1 */
if (device->buffer[0] == 1) {
Jeremy Roberson
committed
char buttonbyte;
/* IF X max > 64K, we still a bit from the y report */
if (device->max_X > 0x10000) {
Jeremy Roberson
committed
val = (u16)(((u16)(device->buffer[2] << 8)) | (u8)device->buffer[1]);
val |= (u32)(((u8)device->buffer[3] & 0x1) << 16);
Jeremy Roberson
committed
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);
Jeremy Roberson
committed
le_buffer[1] = (u8)(device->buffer[4] >> 1);
le_buffer[1] |= (u8)((device->buffer[5] & 0x1) << 7);
Jeremy Roberson
committed
val = get_unaligned_le16(le_buffer);
Jeremy Roberson
committed
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 {
Jeremy Roberson
committed
val = get_unaligned_le16(&device->buffer[1]);
Jeremy Roberson
committed
input_report_abs(inputdev, ABS_X, val);
val = get_unaligned_le16(&device->buffer[3]);
Jeremy Roberson
committed
input_report_abs(inputdev, ABS_Y, val);
buttonbyte = device->buffer[5];
}
/* BUTTONS and PROXIMITY */
val = buttonbyte & MASK_INRANGE ? 1 : 0;
Jeremy Roberson
committed
input_report_abs(inputdev, ABS_DISTANCE, val);
/* Convert buttons, only 4 bits possible */
val = buttonbyte & 0x0F;
Jeremy Roberson
committed
#ifdef USE_BUTTONS
for (i = 0; i < 5; i++)
input_report_key(inputdev, BTN_DIGI + i, val & (1 << i));
Jeremy Roberson
committed
#else
/* We don't apply any meaning to the bitmask, just report */
input_event(inputdev, EV_MSC, MSC_SERIAL, val);
#endif
Jeremy Roberson
committed
/* 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);
dev_err(&device->usbdev->dev,
"usb_submit_urb failed rc=0x%x\n", rc);
Jeremy Roberson
committed
}
/*
* The probe routine. This is called when the kernel find the matching USB
* vendor/product. We do the following:
*
* - Allocate mem for a local structure to manage the device
* - Request a HID Report Descriptor from the device and parse it to
* find out the device parameters
* - Create an input device and assign it attributes
* - Allocate an URB so the device can talk to us when the input
* queue is open
*/
static int gtco_probe(struct usb_interface *usbinterface,
const struct usb_device_id *id)
{
struct gtco *gtco;
struct input_dev *input_dev;
Jeremy Roberson
committed
struct hid_descriptor *hid_desc;
int result = 0, retry;
int error;
Jeremy Roberson
committed
struct usb_endpoint_descriptor *endpoint;
/* Allocate memory for device structure */
gtco = kzalloc(sizeof(struct gtco), GFP_KERNEL);
input_dev = input_allocate_device();
if (!gtco || !input_dev) {
dev_err(&usbinterface->dev, "No more memory\n");
error = -ENOMEM;
goto err_free_devs;
Jeremy Roberson
committed
}
/* Set pointer to the input device */
gtco->inputdevice = input_dev;
Jeremy Roberson
committed
/* Save interface information */
gtco->usbdev = usb_get_dev(interface_to_usbdev(usbinterface));
Jeremy Roberson
committed
/* Allocate some data for incoming reports */
gtco->buffer = usb_alloc_coherent(gtco->usbdev, REPORT_MAX_SIZE,
GFP_KERNEL, >co->buf_dma);
if (!gtco->buffer) {
dev_err(&usbinterface->dev, "No more memory for us buffers\n");
error = -ENOMEM;
goto err_free_devs;
Jeremy Roberson
committed
}
/* Allocate URB for reports */
gtco->urbinfo = usb_alloc_urb(0, GFP_KERNEL);
if (!gtco->urbinfo) {
dev_err(&usbinterface->dev, "Failed to allocate URB\n");
goto err_free_buf;
Jeremy Roberson
committed
}
/*
* 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);
Jeremy Roberson
committed
dbg("endpoint: we have interrupt endpoint\n");
dbg("endpoint extra len:%d ", usbinterface->altsetting[0].extralen);
Jeremy Roberson
committed
/*
* 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){
dev_err(&usbinterface->dev,
"Can't retrieve exta USB descriptor to get hid report descriptor length\n");
error = -EIO;
goto err_free_urb;
Jeremy Roberson
committed
}
dbg("Extra descriptor success: type:%d len:%d",
hid_desc->bDescriptorType, hid_desc->wDescriptorLength);
report = kzalloc(le16_to_cpu(hid_desc->wDescriptorLength), GFP_KERNEL);
if (!report) {
dev_err(&usbinterface->dev, "No more memory for report\n");
error = -ENOMEM;
goto err_free_urb;
Jeremy Roberson
committed
}
/* Couple of tries to get reply */
for (retry = 0; retry < 3; retry++) {
result = usb_control_msg(gtco->usbdev,
usb_rcvctrlpipe(gtco->usbdev, 0),
Jeremy Roberson
committed
USB_REQ_GET_DESCRIPTOR,
USB_RECIP_INTERFACE | USB_DIR_IN,
REPORT_DEVICE_TYPE << 8,
Jeremy Roberson
committed
0, /* interface */
report,
le16_to_cpu(hid_desc->wDescriptorLength),
Jeremy Roberson
committed
5000); /* 5 secs */
dbg("usb_control_msg result: %d", result);
if (result == le16_to_cpu(hid_desc->wDescriptorLength)) {
parse_hid_report_descriptor(gtco, report, result);
Jeremy Roberson
committed
break;
Jeremy Roberson
committed
}
kfree(report);
Jeremy Roberson
committed
/* If we didn't get the report, fail */
if (result != le16_to_cpu(hid_desc->wDescriptorLength)) {
dev_err(&usbinterface->dev,
"Failed to get HID Report Descriptor of size: %d\n",
hid_desc->wDescriptorLength);
error = -EIO;
goto err_free_urb;
Jeremy Roberson
committed
}
/* Create a device file node */
usb_make_path(gtco->usbdev, gtco->usbpath, sizeof(gtco->usbpath));
strlcat(gtco->usbpath, "/input0", sizeof(gtco->usbpath));
Jeremy Roberson
committed
/* Set Input device functions */
input_dev->open = gtco_input_open;
input_dev->close = gtco_input_close;
Jeremy Roberson
committed
/* Set input device information */
input_dev->name = "GTCO_CalComp";
input_dev->phys = gtco->usbpath;
input_set_drvdata(input_dev, gtco);
Jeremy Roberson
committed
/* Now set up all the input device capabilities */
gtco_setup_caps(input_dev);
Jeremy Roberson
committed
/* Set input device required ID information */
usb_to_input_id(gtco->usbdev, &input_dev->id);
input_dev->dev.parent = &usbinterface->dev;
Jeremy Roberson
committed
/* Setup the URB, it will be posted later on open of input device */
endpoint = &usbinterface->altsetting[0].endpoint[0].desc;
usb_fill_int_urb(gtco->urbinfo,
gtco->usbdev,
usb_rcvintpipe(gtco->usbdev,
Jeremy Roberson
committed
endpoint->bEndpointAddress),
gtco->buffer,
Jeremy Roberson
committed
REPORT_MAX_SIZE,
gtco_urb_callback,
Jeremy Roberson
committed
endpoint->bInterval);
gtco->urbinfo->transfer_dma = gtco->buf_dma;
gtco->urbinfo->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
Jeremy Roberson
committed
/* Save gtco pointer in USB interface gtco */
usb_set_intfdata(usbinterface, gtco);
Jeremy Roberson
committed
/* All done, now register the input device */
error = input_register_device(input_dev);
if (error)
goto err_free_urb;
Jeremy Roberson
committed
return 0;
err_free_urb:
usb_free_urb(gtco->urbinfo);
err_free_buf:
usb_free_coherent(gtco->usbdev, REPORT_MAX_SIZE,
gtco->buffer, gtco->buf_dma);
err_free_devs:
input_free_device(input_dev);
kfree(gtco);
return error;
Jeremy Roberson
committed
}
/*
* 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)
{