2007-01-18 16:10:25 +01:00
|
|
|
/* -*- 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)
|
|
|
|
{
|
2007-04-12 07:33:00 +02:00
|
|
|
int x, i = 0;
|
2007-01-18 16:10:25 +01:00
|
|
|
|
|
|
|
/* 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 */
|
2007-05-01 06:24:54 +02:00
|
|
|
char maintype = 'x';
|
2007-01-18 16:10:25 +01:00
|
|
|
char globtype[12];
|
2007-04-12 07:33:00 +02:00
|
|
|
int indent = 0;
|
|
|
|
char indentstr[10] = "";
|
2007-01-18 16:10:25 +01:00
|
|
|
|
|
|
|
|
|
|
|
dbg("======>>>>>>PARSE<<<<<<======");
|
|
|
|
|
|
|
|
/* Walk this report and pull out the info we need */
|
2007-04-12 07:33:00 +02:00
|
|
|
while (i < length) {
|
|
|
|
prefix = report[i];
|
2007-01-18 16:10:25 +01:00
|
|
|
|
|
|
|
/* Skip over prefix */
|
|
|
|
i++;
|
|
|
|
|
|
|
|
/* Determine data size and save the data in the proper variable */
|
|
|
|
size = PREF_SIZE(prefix);
|
2007-04-12 07:33:00 +02:00
|
|
|
switch (size) {
|
2007-01-18 16:10:25 +01:00
|
|
|
case 1:
|
|
|
|
data = report[i];
|
|
|
|
break;
|
|
|
|
case 2:
|
2007-04-12 07:33:00 +02:00
|
|
|
data16 = le16_to_cpu(get_unaligned((__le16 *)&report[i]));
|
2007-01-18 16:10:25 +01:00
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
size = 4;
|
2007-04-12 07:33:00 +02:00
|
|
|
data32 = le32_to_cpu(get_unaligned((__le32 *)&report[i]));
|
|
|
|
break;
|
2007-01-18 16:10:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Skip size of data */
|
2007-04-12 07:33:00 +02:00
|
|
|
i += size;
|
2007-01-18 16:10:25 +01:00
|
|
|
|
|
|
|
/* What we do depends on the tag type */
|
|
|
|
tag = PREF_TAG(prefix);
|
|
|
|
type = PREF_TYPE(prefix);
|
2007-04-12 07:33:00 +02:00
|
|
|
switch (type) {
|
2007-01-18 16:10:25 +01:00
|
|
|
case TYPE_MAIN:
|
2007-04-12 07:33:00 +02:00
|
|
|
strcpy(globtype, "");
|
|
|
|
switch (tag) {
|
2007-01-18 16:10:25 +01:00
|
|
|
|
|
|
|
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
|
|
|
|
*/
|
|
|
|
|
2007-04-12 07:33:00 +02:00
|
|
|
maintype = 'I';
|
|
|
|
if (data == 2)
|
|
|
|
strcpy(globtype, "Variable");
|
|
|
|
else if (data == 3)
|
|
|
|
strcpy(globtype, "Var|Const");
|
2007-01-18 16:10:25 +01:00
|
|
|
|
|
|
|
dbg("::::: Saving Report: %d input #%d Max: 0x%X(%d) Min:0x%X(%d) of %d bits",
|
2007-04-12 07:33:00 +02:00
|
|
|
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]);
|
2007-01-18 16:10:25 +01:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
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
|
|
|
|
*/
|
2007-04-12 07:33:00 +02:00
|
|
|
switch (inputnum) {
|
2007-01-18 16:10:25 +01:00
|
|
|
case 0: /* X coord */
|
2007-04-12 07:33:00 +02:00
|
|
|
dbg("GER: X Usage: 0x%x", usage);
|
|
|
|
if (device->max_X == 0) {
|
2007-01-18 16:10:25 +01:00
|
|
|
device->max_X = globalval[TAG_GLOB_LOG_MAX];
|
|
|
|
device->min_X = globalval[TAG_GLOB_LOG_MIN];
|
|
|
|
}
|
|
|
|
break;
|
2007-04-12 07:33:00 +02:00
|
|
|
|
2007-01-18 16:10:25 +01:00
|
|
|
case 1: /* Y coord */
|
2007-04-12 07:33:00 +02:00
|
|
|
dbg("GER: Y Usage: 0x%x", usage);
|
|
|
|
if (device->max_Y == 0) {
|
2007-01-18 16:10:25 +01:00
|
|
|
device->max_Y = globalval[TAG_GLOB_LOG_MAX];
|
|
|
|
device->min_Y = globalval[TAG_GLOB_LOG_MIN];
|
|
|
|
}
|
|
|
|
break;
|
2007-04-12 07:33:00 +02:00
|
|
|
|
2007-01-18 16:10:25 +01:00
|
|
|
default:
|
|
|
|
/* Tilt X */
|
2007-04-12 07:33:00 +02:00
|
|
|
if (usage == DIGITIZER_USAGE_TILT_X) {
|
|
|
|
if (device->maxtilt_X == 0) {
|
2007-01-18 16:10:25 +01:00
|
|
|
device->maxtilt_X = globalval[TAG_GLOB_LOG_MAX];
|
|
|
|
device->mintilt_X = globalval[TAG_GLOB_LOG_MIN];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Tilt Y */
|
2007-04-12 07:33:00 +02:00
|
|
|
if (usage == DIGITIZER_USAGE_TILT_Y) {
|
|
|
|
if (device->maxtilt_Y == 0) {
|
2007-01-18 16:10:25 +01:00
|
|
|
device->maxtilt_Y = globalval[TAG_GLOB_LOG_MAX];
|
|
|
|
device->mintilt_Y = globalval[TAG_GLOB_LOG_MIN];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Pressure */
|
2007-04-12 07:33:00 +02:00
|
|
|
if (usage == DIGITIZER_USAGE_TIP_PRESSURE) {
|
|
|
|
if (device->maxpressure == 0) {
|
2007-01-18 16:10:25 +01:00
|
|
|
device->maxpressure = globalval[TAG_GLOB_LOG_MAX];
|
|
|
|
device->minpressure = globalval[TAG_GLOB_LOG_MIN];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
inputnum++;
|
|
|
|
break;
|
2007-04-12 07:33:00 +02:00
|
|
|
|
2007-01-18 16:10:25 +01:00
|
|
|
case TAG_MAIN_OUTPUT:
|
2007-04-12 07:33:00 +02:00
|
|
|
maintype = 'O';
|
2007-01-18 16:10:25 +01:00
|
|
|
break;
|
2007-04-12 07:33:00 +02:00
|
|
|
|
2007-01-18 16:10:25 +01:00
|
|
|
case TAG_MAIN_FEATURE:
|
2007-04-12 07:33:00 +02:00
|
|
|
maintype = 'F';
|
2007-01-18 16:10:25 +01:00
|
|
|
break;
|
2007-04-12 07:33:00 +02:00
|
|
|
|
2007-01-18 16:10:25 +01:00
|
|
|
case TAG_MAIN_COL_START:
|
2007-04-12 07:33:00 +02:00
|
|
|
maintype = 'S';
|
2007-01-18 16:10:25 +01:00
|
|
|
|
2007-04-12 07:33:00 +02:00
|
|
|
if (data == 0) {
|
2007-01-18 16:10:25 +01:00
|
|
|
dbg("======>>>>>> Physical");
|
2007-04-12 07:33:00 +02:00
|
|
|
strcpy(globtype, "Physical");
|
|
|
|
} else
|
2007-01-18 16:10:25 +01:00
|
|
|
dbg("======>>>>>>");
|
|
|
|
|
|
|
|
/* Indent the debug output */
|
|
|
|
indent++;
|
2007-04-12 07:33:00 +02:00
|
|
|
for (x = 0; x < indent; x++)
|
|
|
|
indentstr[x] = '-';
|
|
|
|
indentstr[x] = 0;
|
2007-01-18 16:10:25 +01:00
|
|
|
|
|
|
|
/* Save global tags */
|
2007-04-12 07:33:00 +02:00
|
|
|
for (x = 0; x < TAG_GLOB_MAX; x++)
|
2007-01-18 16:10:25 +01:00
|
|
|
oldval[x] = globalval[x];
|
|
|
|
|
|
|
|
break;
|
2007-04-12 07:33:00 +02:00
|
|
|
|
2007-01-18 16:10:25 +01:00
|
|
|
case TAG_MAIN_COL_END:
|
|
|
|
dbg("<<<<<<======");
|
2007-04-12 07:33:00 +02:00
|
|
|
maintype = 'E';
|
2007-01-18 16:10:25 +01:00
|
|
|
indent--;
|
2007-04-12 07:33:00 +02:00
|
|
|
for (x = 0; x < indent; x++)
|
|
|
|
indentstr[x] = '-';
|
|
|
|
indentstr[x] = 0;
|
2007-01-18 16:10:25 +01:00
|
|
|
|
|
|
|
/* Copy global tags back */
|
2007-04-12 07:33:00 +02:00
|
|
|
for (x = 0; x < TAG_GLOB_MAX; x++)
|
2007-01-18 16:10:25 +01:00
|
|
|
globalval[x] = oldval[x];
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2007-04-12 07:33:00 +02:00
|
|
|
switch (size) {
|
2007-01-18 16:10:25 +01:00
|
|
|
case 1:
|
|
|
|
dbg("%sMAINTAG:(%d) %c SIZE: %d Data: %s 0x%x",
|
2007-04-12 07:33:00 +02:00
|
|
|
indentstr, tag, maintype, size, globtype, data);
|
2007-01-18 16:10:25 +01:00
|
|
|
break;
|
2007-04-12 07:33:00 +02:00
|
|
|
|
2007-01-18 16:10:25 +01:00
|
|
|
case 2:
|
|
|
|
dbg("%sMAINTAG:(%d) %c SIZE: %d Data: %s 0x%x",
|
2007-04-12 07:33:00 +02:00
|
|
|
indentstr, tag, maintype, size, globtype, data16);
|
2007-01-18 16:10:25 +01:00
|
|
|
break;
|
2007-04-12 07:33:00 +02:00
|
|
|
|
2007-01-18 16:10:25 +01:00
|
|
|
case 4:
|
|
|
|
dbg("%sMAINTAG:(%d) %c SIZE: %d Data: %s 0x%x",
|
2007-04-12 07:33:00 +02:00
|
|
|
indentstr, tag, maintype, size, globtype, data32);
|
2007-01-18 16:10:25 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
2007-04-12 07:33:00 +02:00
|
|
|
|
2007-01-18 16:10:25 +01:00
|
|
|
case TYPE_GLOBAL:
|
2007-04-12 07:33:00 +02:00
|
|
|
switch (tag) {
|
2007-01-18 16:10:25 +01:00
|
|
|
case TAG_GLOB_USAGE:
|
|
|
|
/*
|
|
|
|
* First time we hit the global usage tag,
|
|
|
|
* it should tell us the type of device
|
|
|
|
*/
|
2007-04-12 07:33:00 +02:00
|
|
|
if (device->usage == 0)
|
2007-01-18 16:10:25 +01:00
|
|
|
device->usage = data;
|
2007-04-12 07:33:00 +02:00
|
|
|
|
|
|
|
strcpy(globtype, "USAGE");
|
2007-01-18 16:10:25 +01:00
|
|
|
break;
|
2007-04-12 07:33:00 +02:00
|
|
|
|
|
|
|
case TAG_GLOB_LOG_MIN:
|
|
|
|
strcpy(globtype, "LOG_MIN");
|
2007-01-18 16:10:25 +01:00
|
|
|
break;
|
2007-04-12 07:33:00 +02:00
|
|
|
|
|
|
|
case TAG_GLOB_LOG_MAX:
|
|
|
|
strcpy(globtype, "LOG_MAX");
|
2007-01-18 16:10:25 +01:00
|
|
|
break;
|
2007-04-12 07:33:00 +02:00
|
|
|
|
|
|
|
case TAG_GLOB_PHYS_MIN:
|
|
|
|
strcpy(globtype, "PHYS_MIN");
|
2007-01-18 16:10:25 +01:00
|
|
|
break;
|
2007-04-12 07:33:00 +02:00
|
|
|
|
|
|
|
case TAG_GLOB_PHYS_MAX:
|
|
|
|
strcpy(globtype, "PHYS_MAX");
|
2007-01-18 16:10:25 +01:00
|
|
|
break;
|
2007-04-12 07:33:00 +02:00
|
|
|
|
|
|
|
case TAG_GLOB_UNIT_EXP:
|
|
|
|
strcpy(globtype, "EXP");
|
2007-01-18 16:10:25 +01:00
|
|
|
break;
|
2007-04-12 07:33:00 +02:00
|
|
|
|
|
|
|
case TAG_GLOB_UNIT:
|
|
|
|
strcpy(globtype, "UNIT");
|
2007-01-18 16:10:25 +01:00
|
|
|
break;
|
2007-04-12 07:33:00 +02:00
|
|
|
|
|
|
|
case TAG_GLOB_REPORT_SZ:
|
|
|
|
strcpy(globtype, "REPORT_SZ");
|
2007-01-18 16:10:25 +01:00
|
|
|
break;
|
2007-04-12 07:33:00 +02:00
|
|
|
|
|
|
|
case TAG_GLOB_REPORT_ID:
|
|
|
|
strcpy(globtype, "REPORT_ID");
|
2007-01-18 16:10:25 +01:00
|
|
|
/* New report, restart numbering */
|
2007-04-12 07:33:00 +02:00
|
|
|
inputnum = 0;
|
2007-01-18 16:10:25 +01:00
|
|
|
break;
|
2007-04-12 07:33:00 +02:00
|
|
|
|
2007-01-18 16:10:25 +01:00
|
|
|
case TAG_GLOB_REPORT_CNT:
|
2007-04-12 07:33:00 +02:00
|
|
|
strcpy(globtype, "REPORT_CNT");
|
2007-01-18 16:10:25 +01:00
|
|
|
break;
|
2007-04-12 07:33:00 +02:00
|
|
|
|
|
|
|
case TAG_GLOB_PUSH:
|
|
|
|
strcpy(globtype, "PUSH");
|
2007-01-18 16:10:25 +01:00
|
|
|
break;
|
2007-04-12 07:33:00 +02:00
|
|
|
|
2007-01-18 16:10:25 +01:00
|
|
|
case TAG_GLOB_POP:
|
2007-04-12 07:33:00 +02:00
|
|
|
strcpy(globtype, "POP");
|
2007-01-18 16:10:25 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check to make sure we have a good tag number
|
|
|
|
so we don't overflow array */
|
2007-04-12 07:33:00 +02:00
|
|
|
if (tag < TAG_GLOB_MAX) {
|
|
|
|
switch (size) {
|
2007-01-18 16:10:25 +01:00
|
|
|
case 1:
|
2007-04-12 07:33:00 +02:00
|
|
|
dbg("%sGLOBALTAG:%s(%d) SIZE: %d Data: 0x%x",
|
|
|
|
indentstr, globtype, tag, size, data);
|
|
|
|
globalval[tag] = data;
|
2007-01-18 16:10:25 +01:00
|
|
|
break;
|
2007-04-12 07:33:00 +02:00
|
|
|
|
2007-01-18 16:10:25 +01:00
|
|
|
case 2:
|
2007-04-12 07:33:00 +02:00
|
|
|
dbg("%sGLOBALTAG:%s(%d) SIZE: %d Data: 0x%x",
|
|
|
|
indentstr, globtype, tag, size, data16);
|
|
|
|
globalval[tag] = data16;
|
2007-01-18 16:10:25 +01:00
|
|
|
break;
|
2007-04-12 07:33:00 +02:00
|
|
|
|
2007-01-18 16:10:25 +01:00
|
|
|
case 4:
|
2007-04-12 07:33:00 +02:00
|
|
|
dbg("%sGLOBALTAG:%s(%d) SIZE: %d Data: 0x%x",
|
|
|
|
indentstr, globtype, tag, size, data32);
|
|
|
|
globalval[tag] = data32;
|
2007-01-18 16:10:25 +01:00
|
|
|
break;
|
|
|
|
}
|
2007-04-12 07:33:00 +02:00
|
|
|
} else {
|
2007-01-18 16:10:25 +01:00
|
|
|
dbg("%sGLOBALTAG: ILLEGAL TAG:%d SIZE: %d ",
|
2007-04-12 07:33:00 +02:00
|
|
|
indentstr, tag, size);
|
2007-01-18 16:10:25 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TYPE_LOCAL:
|
2007-04-12 07:33:00 +02:00
|
|
|
switch (tag) {
|
2007-01-18 16:10:25 +01:00
|
|
|
case TAG_GLOB_USAGE:
|
2007-04-12 07:33:00 +02:00
|
|
|
strcpy(globtype, "USAGE");
|
2007-01-18 16:10:25 +01:00
|
|
|
/* Always 1 byte */
|
|
|
|
usage = data;
|
|
|
|
break;
|
2007-04-12 07:33:00 +02:00
|
|
|
|
|
|
|
case TAG_GLOB_LOG_MIN:
|
|
|
|
strcpy(globtype, "MIN");
|
2007-01-18 16:10:25 +01:00
|
|
|
break;
|
2007-04-12 07:33:00 +02:00
|
|
|
|
|
|
|
case TAG_GLOB_LOG_MAX:
|
|
|
|
strcpy(globtype, "MAX");
|
2007-01-18 16:10:25 +01:00
|
|
|
break;
|
2007-04-12 07:33:00 +02:00
|
|
|
|
2007-01-18 16:10:25 +01:00
|
|
|
default:
|
2007-04-12 07:33:00 +02:00
|
|
|
strcpy(globtype, "UNKNOWN");
|
|
|
|
break;
|
2007-01-18 16:10:25 +01:00
|
|
|
}
|
|
|
|
|
2007-04-12 07:33:00 +02:00
|
|
|
switch (size) {
|
2007-01-18 16:10:25 +01:00
|
|
|
case 1:
|
|
|
|
dbg("%sLOCALTAG:(%d) %s SIZE: %d Data: 0x%x",
|
2007-04-12 07:33:00 +02:00
|
|
|
indentstr, tag, globtype, size, data);
|
2007-01-18 16:10:25 +01:00
|
|
|
break;
|
2007-04-12 07:33:00 +02:00
|
|
|
|
2007-01-18 16:10:25 +01:00
|
|
|
case 2:
|
|
|
|
dbg("%sLOCALTAG:(%d) %s SIZE: %d Data: 0x%x",
|
2007-04-12 07:33:00 +02:00
|
|
|
indentstr, tag, globtype, size, data16);
|
2007-01-18 16:10:25 +01:00
|
|
|
break;
|
2007-04-12 07:33:00 +02:00
|
|
|
|
2007-01-18 16:10:25 +01:00
|
|
|
case 4:
|
|
|
|
dbg("%sLOCALTAG:(%d) %s SIZE: %d Data: 0x%x",
|
2007-04-12 07:33:00 +02:00
|
|
|
indentstr, tag, globtype, size, data32);
|
2007-01-18 16:10:25 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* INPUT DRIVER Routines */
|
|
|
|
|
|
|
|
/*
|
2007-04-12 07:33:00 +02:00
|
|
|
* Called when opening the input device. This will submit the URB to
|
|
|
|
* the usb system so we start getting reports
|
2007-01-18 16:10:25 +01:00
|
|
|
*/
|
|
|
|
static int gtco_input_open(struct input_dev *inputdev)
|
|
|
|
{
|
2007-04-12 07:34:39 +02:00
|
|
|
struct gtco *device = input_get_drvdata(inputdev);
|
2007-01-18 16:10:25 +01:00
|
|
|
|
|
|
|
device->urbinfo->dev = device->usbdev;
|
2007-04-12 07:33:00 +02:00
|
|
|
if (usb_submit_urb(device->urbinfo, GFP_KERNEL))
|
2007-01-18 16:10:25 +01:00
|
|
|
return -EIO;
|
2007-04-12 07:33:00 +02:00
|
|
|
|
2007-01-18 16:10:25 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-04-12 07:33:00 +02:00
|
|
|
/*
|
|
|
|
* Called when closing the input device. This will unlink the URB
|
|
|
|
*/
|
2007-01-18 16:10:25 +01:00
|
|
|
static void gtco_input_close(struct input_dev *inputdev)
|
|
|
|
{
|
2007-04-12 07:34:39 +02:00
|
|
|
struct gtco *device = input_get_drvdata(inputdev);
|
2007-01-18 16:10:25 +01:00
|
|
|
|
|
|
|
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
|
|
|
|
*
|
|
|
|
*/
|
2007-04-12 07:34:39 +02:00
|
|
|
static void gtco_setup_caps(struct input_dev *inputdev)
|
2007-01-18 16:10:25 +01:00
|
|
|
{
|
2007-04-12 07:34:39 +02:00
|
|
|
struct gtco *device = input_get_drvdata(inputdev);
|
2007-01-18 16:10:25 +01:00
|
|
|
|
|
|
|
/* Which events */
|
2007-10-19 08:40:32 +02:00
|
|
|
inputdev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) |
|
|
|
|
BIT_MASK(EV_MSC);
|
2007-01-18 16:10:25 +01:00
|
|
|
|
|
|
|
/* Misc event menu block */
|
2007-10-19 08:40:32 +02:00
|
|
|
inputdev->mscbit[0] = BIT_MASK(MSC_SCAN) | BIT_MASK(MSC_SERIAL) |
|
|
|
|
BIT_MASK(MSC_RAW);
|
2007-01-18 16:10:25 +01:00
|
|
|
|
|
|
|
/* 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 */
|
2007-04-12 07:33:00 +02:00
|
|
|
input_set_abs_params(inputdev, ABS_MISC, 0, 0xFF, 0, 0);
|
2007-01-18 16:10:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 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)
|
|
|
|
{
|
2007-04-12 07:33:00 +02:00
|
|
|
struct gtco *device = urbinfo->context;
|
2007-01-18 16:10:25 +01:00
|
|
|
struct input_dev *inputdev;
|
|
|
|
int rc;
|
|
|
|
u32 val = 0;
|
|
|
|
s8 valsigned = 0;
|
|
|
|
char le_buffer[2];
|
|
|
|
|
|
|
|
inputdev = device->inputdevice;
|
|
|
|
|
|
|
|
/* Was callback OK? */
|
2007-04-12 07:33:00 +02:00
|
|
|
if (urbinfo->status == -ECONNRESET ||
|
|
|
|
urbinfo->status == -ENOENT ||
|
|
|
|
urbinfo->status == -ESHUTDOWN) {
|
2007-01-18 16:10:25 +01:00
|
|
|
|
|
|
|
/* Shutdown is occurring. Return and don't queue up any more */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2007-04-12 07:33:00 +02:00
|
|
|
if (urbinfo->status != 0) {
|
|
|
|
/*
|
|
|
|
* Some unknown error. Hopefully temporary. Just go and
|
|
|
|
* requeue an URB
|
|
|
|
*/
|
2007-01-18 16:10:25 +01:00
|
|
|
goto resubmit;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Good URB, now process
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* PID dependent when we interpret the report */
|
2007-04-12 07:33:00 +02:00
|
|
|
if (inputdev->id.product == PID_1000 ||
|
|
|
|
inputdev->id.product == PID_1001 ||
|
|
|
|
inputdev->id.product == PID_1002) {
|
2007-01-18 16:10:25 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
*/
|
2007-04-12 07:33:00 +02:00
|
|
|
switch (device->buffer[0]) {
|
2007-01-18 16:10:25 +01:00
|
|
|
case 5:
|
|
|
|
/* Pressure is 9 bits */
|
2007-04-12 07:33:00 +02:00
|
|
|
val = ((u16)(device->buffer[8]) << 1);
|
2007-01-18 16:10:25 +01:00
|
|
|
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 */
|
2007-04-12 07:33:00 +02:00
|
|
|
val = (device->buffer[5]) & MASK_BUTTON;
|
2007-01-18 16:10:25 +01:00
|
|
|
|
|
|
|
/* 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 */
|
2007-04-12 07:33:00 +02:00
|
|
|
val = le16_to_cpu(get_unaligned((__le16 *)&device->buffer[1]));
|
2007-01-18 16:10:25 +01:00
|
|
|
input_report_abs(inputdev, ABS_X, val);
|
|
|
|
|
2007-04-12 07:33:00 +02:00
|
|
|
val = le16_to_cpu(get_unaligned((__le16 *)&device->buffer[3]));
|
2007-01-18 16:10:25 +01:00
|
|
|
input_report_abs(inputdev, ABS_Y, val);
|
|
|
|
|
|
|
|
/* Ditto for proximity bit */
|
2007-04-12 07:33:00 +02:00
|
|
|
val = device->buffer[5] & MASK_INRANGE ? 1 : 0;
|
2007-01-18 16:10:25 +01:00
|
|
|
input_report_abs(inputdev, ABS_DISTANCE, val);
|
|
|
|
|
|
|
|
/* Report 1 is an exception to how we handle buttons */
|
|
|
|
/* Buttons are an index, not a bitmask */
|
2007-04-12 07:33:00 +02:00
|
|
|
if (device->buffer[0] == 1) {
|
2007-01-18 16:10:25 +01:00
|
|
|
|
2007-04-12 07:33:00 +02:00
|
|
|
/*
|
|
|
|
* Convert buttons, 5 bit index
|
|
|
|
* Report value of index set as one,
|
|
|
|
* the rest as 0
|
|
|
|
*/
|
|
|
|
val = device->buffer[5] & MASK_BUTTON;
|
2007-01-18 16:10:25 +01:00
|
|
|
dbg("======>>>>>>REPORT 1: val 0x%X(%d)",
|
2007-04-12 07:33:00 +02:00
|
|
|
val, val);
|
2007-01-18 16:10:25 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We don't apply any meaning to the button
|
|
|
|
* index, just report it
|
|
|
|
*/
|
|
|
|
input_event(inputdev, EV_MSC, MSC_SERIAL, val);
|
|
|
|
}
|
|
|
|
break;
|
2007-04-12 07:33:00 +02:00
|
|
|
|
2007-01-18 16:10:25 +01:00
|
|
|
case 7:
|
|
|
|
/* Menu blocks */
|
|
|
|
input_event(inputdev, EV_MSC, MSC_SCAN,
|
|
|
|
device->buffer[1]);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2007-04-12 07:33:00 +02:00
|
|
|
|
2007-01-18 16:10:25 +01:00
|
|
|
/* Other pid class */
|
2007-04-12 07:33:00 +02:00
|
|
|
if (inputdev->id.product == PID_400 ||
|
|
|
|
inputdev->id.product == PID_401) {
|
2007-01-18 16:10:25 +01:00
|
|
|
|
|
|
|
/* Report 2 */
|
2007-04-12 07:33:00 +02:00
|
|
|
if (device->buffer[0] == 2) {
|
2007-01-18 16:10:25 +01:00
|
|
|
/* Menu blocks */
|
2007-04-12 07:33:00 +02:00
|
|
|
input_event(inputdev, EV_MSC, MSC_SCAN, device->buffer[1]);
|
2007-01-18 16:10:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Report 1 */
|
2007-04-12 07:33:00 +02:00
|
|
|
if (device->buffer[0] == 1) {
|
2007-01-18 16:10:25 +01:00
|
|
|
char buttonbyte;
|
|
|
|
|
|
|
|
/* IF X max > 64K, we still a bit from the y report */
|
2007-04-12 07:33:00 +02:00
|
|
|
if (device->max_X > 0x10000) {
|
2007-01-18 16:10:25 +01:00
|
|
|
|
2007-04-12 07:33:00 +02:00
|
|
|
val = (u16)(((u16)(device->buffer[2] << 8)) | (u8)device->buffer[1]);
|
|
|
|
val |= (u32)(((u8)device->buffer[3] & 0x1) << 16);
|
2007-01-18 16:10:25 +01:00
|
|
|
|
|
|
|
input_report_abs(inputdev, ABS_X, val);
|
|
|
|
|
2007-04-12 07:33:00 +02:00
|
|
|
le_buffer[0] = (u8)((u8)(device->buffer[3]) >> 1);
|
|
|
|
le_buffer[0] |= (u8)((device->buffer[3] & 0x1) << 7);
|
2007-01-18 16:10:25 +01:00
|
|
|
|
2007-04-12 07:33:00 +02:00
|
|
|
le_buffer[1] = (u8)(device->buffer[4] >> 1);
|
|
|
|
le_buffer[1] |= (u8)((device->buffer[5] & 0x1) << 7);
|
2007-01-18 16:10:25 +01:00
|
|
|
|
2007-04-12 07:33:00 +02:00
|
|
|
val = le16_to_cpu(get_unaligned((__le16 *)le_buffer));
|
2007-01-18 16:10:25 +01:00
|
|
|
input_report_abs(inputdev, ABS_Y, val);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Shift the button byte right by one to
|
|
|
|
* make it look like the standard report
|
|
|
|
*/
|
2007-04-12 07:33:00 +02:00
|
|
|
buttonbyte = device->buffer[5] >> 1;
|
|
|
|
} else {
|
2007-01-18 16:10:25 +01:00
|
|
|
|
2007-04-12 07:33:00 +02:00
|
|
|
val = le16_to_cpu(get_unaligned((__le16 *)&device->buffer[1]));
|
2007-01-18 16:10:25 +01:00
|
|
|
input_report_abs(inputdev, ABS_X, val);
|
|
|
|
|
2007-04-12 07:33:00 +02:00
|
|
|
val = le16_to_cpu(get_unaligned((__le16 *)&device->buffer[3]));
|
2007-01-18 16:10:25 +01:00
|
|
|
input_report_abs(inputdev, ABS_Y, val);
|
|
|
|
|
|
|
|
buttonbyte = device->buffer[5];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* BUTTONS and PROXIMITY */
|
2007-04-12 07:33:00 +02:00
|
|
|
val = buttonbyte & MASK_INRANGE ? 1 : 0;
|
2007-01-18 16:10:25 +01:00
|
|
|
input_report_abs(inputdev, ABS_DISTANCE, val);
|
|
|
|
|
|
|
|
/* Convert buttons, only 4 bits possible */
|
2007-04-12 07:33:00 +02:00
|
|
|
val = buttonbyte & 0x0F;
|
2007-01-18 16:10:25 +01:00
|
|
|
#ifdef USE_BUTTONS
|
2007-04-12 07:33:00 +02:00
|
|
|
for (i = 0; i < 5; i++)
|
|
|
|
input_report_key(inputdev, BTN_DIGI + i, val & (1 << i));
|
2007-01-18 16:10:25 +01:00
|
|
|
#else
|
|
|
|
/* We don't apply any meaning to the bitmask, just report */
|
|
|
|
input_event(inputdev, EV_MSC, MSC_SERIAL, val);
|
|
|
|
#endif
|
2007-04-12 07:33:00 +02:00
|
|
|
|
2007-01-18 16:10:25 +01:00
|
|
|
/* 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);
|
2007-04-12 07:33:00 +02:00
|
|
|
if (rc != 0)
|
|
|
|
err("usb_submit_urb failed rc=0x%x", rc);
|
2007-01-18 16:10:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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)
|
|
|
|
{
|
|
|
|
|
2007-04-12 07:33:00 +02:00
|
|
|
struct gtco *gtco;
|
|
|
|
struct input_dev *input_dev;
|
2007-01-18 16:10:25 +01:00
|
|
|
struct hid_descriptor *hid_desc;
|
2007-04-12 07:33:00 +02:00
|
|
|
char *report = NULL;
|
|
|
|
int result = 0, retry;
|
|
|
|
int error;
|
2007-01-18 16:10:25 +01:00
|
|
|
struct usb_endpoint_descriptor *endpoint;
|
|
|
|
|
|
|
|
/* Allocate memory for device structure */
|
2007-04-12 07:33:00 +02:00
|
|
|
gtco = kzalloc(sizeof(struct gtco), GFP_KERNEL);
|
|
|
|
input_dev = input_allocate_device();
|
|
|
|
if (!gtco || !input_dev) {
|
2007-01-18 16:10:25 +01:00
|
|
|
err("No more memory");
|
2007-04-12 07:33:00 +02:00
|
|
|
error = -ENOMEM;
|
|
|
|
goto err_free_devs;
|
2007-01-18 16:10:25 +01:00
|
|
|
}
|
|
|
|
|
2007-04-12 07:33:00 +02:00
|
|
|
/* Set pointer to the input device */
|
|
|
|
gtco->inputdevice = input_dev;
|
2007-01-18 16:10:25 +01:00
|
|
|
|
|
|
|
/* Save interface information */
|
2007-04-12 07:33:00 +02:00
|
|
|
gtco->usbdev = usb_get_dev(interface_to_usbdev(usbinterface));
|
2007-01-18 16:10:25 +01:00
|
|
|
|
|
|
|
/* Allocate some data for incoming reports */
|
2007-04-12 07:33:00 +02:00
|
|
|
gtco->buffer = usb_buffer_alloc(gtco->usbdev, REPORT_MAX_SIZE,
|
|
|
|
GFP_KERNEL, >co->buf_dma);
|
|
|
|
if (!gtco->buffer) {
|
|
|
|
err("No more memory for us buffers");
|
|
|
|
error = -ENOMEM;
|
|
|
|
goto err_free_devs;
|
2007-01-18 16:10:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Allocate URB for reports */
|
2007-04-12 07:33:00 +02:00
|
|
|
gtco->urbinfo = usb_alloc_urb(0, GFP_KERNEL);
|
|
|
|
if (!gtco->urbinfo) {
|
|
|
|
err("Failed to allocate URB");
|
2007-01-18 16:10:25 +01:00
|
|
|
return -ENOMEM;
|
2007-04-12 07:33:00 +02:00
|
|
|
goto err_free_buf;
|
2007-01-18 16:10:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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 */
|
2007-04-12 07:33:00 +02:00
|
|
|
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);
|
2007-01-18 16:10:25 +01:00
|
|
|
if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)
|
|
|
|
dbg("endpoint: we have interrupt endpoint\n");
|
|
|
|
|
2007-04-12 07:33:00 +02:00
|
|
|
dbg("endpoint extra len:%d ", usbinterface->altsetting[0].extralen);
|
2007-01-18 16:10:25 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Find the HID descriptor so we can find out the size of the
|
|
|
|
* HID report descriptor
|
|
|
|
*/
|
|
|
|
if (usb_get_extra_descriptor(usbinterface->cur_altsetting,
|
2007-04-12 07:33:00 +02:00
|
|
|
HID_DEVICE_TYPE, &hid_desc) != 0){
|
2007-01-18 16:10:25 +01:00
|
|
|
err("Can't retrieve exta USB descriptor to get hid report descriptor length");
|
2007-04-12 07:33:00 +02:00
|
|
|
error = -EIO;
|
|
|
|
goto err_free_urb;
|
2007-01-18 16:10:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
dbg("Extra descriptor success: type:%d len:%d",
|
|
|
|
hid_desc->bDescriptorType, hid_desc->wDescriptorLength);
|
|
|
|
|
2007-04-12 07:33:00 +02:00
|
|
|
report = kzalloc(hid_desc->wDescriptorLength, GFP_KERNEL);
|
|
|
|
if (!report) {
|
|
|
|
err("No more memory for report");
|
|
|
|
error = -ENOMEM;
|
|
|
|
goto err_free_urb;
|
2007-01-18 16:10:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Couple of tries to get reply */
|
2007-04-12 07:33:00 +02:00
|
|
|
for (retry = 0; retry < 3; retry++) {
|
|
|
|
result = usb_control_msg(gtco->usbdev,
|
|
|
|
usb_rcvctrlpipe(gtco->usbdev, 0),
|
2007-01-18 16:10:25 +01:00
|
|
|
USB_REQ_GET_DESCRIPTOR,
|
|
|
|
USB_RECIP_INTERFACE | USB_DIR_IN,
|
2007-04-12 07:33:00 +02:00
|
|
|
REPORT_DEVICE_TYPE << 8,
|
2007-01-18 16:10:25 +01:00
|
|
|
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);
|
2007-04-12 07:33:00 +02:00
|
|
|
if (result != hid_desc->wDescriptorLength) {
|
2007-01-18 16:10:25 +01:00
|
|
|
err("Failed to get HID Report Descriptor of size: %d",
|
|
|
|
hid_desc->wDescriptorLength);
|
2007-04-12 07:33:00 +02:00
|
|
|
error = -EIO;
|
|
|
|
goto err_free_urb;
|
2007-01-18 16:10:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Now we parse the report */
|
2007-04-12 07:33:00 +02:00
|
|
|
parse_hid_report_descriptor(gtco, report, result);
|
2007-01-18 16:10:25 +01:00
|
|
|
|
|
|
|
/* Now we delete it */
|
|
|
|
kfree(report);
|
|
|
|
|
|
|
|
/* Create a device file node */
|
2007-04-12 07:33:00 +02:00
|
|
|
usb_make_path(gtco->usbdev, gtco->usbpath, sizeof(gtco->usbpath));
|
|
|
|
strlcat(gtco->usbpath, "/input0", sizeof(gtco->usbpath));
|
2007-01-18 16:10:25 +01:00
|
|
|
|
|
|
|
/* Set Input device functions */
|
2007-04-12 07:33:00 +02:00
|
|
|
input_dev->open = gtco_input_open;
|
|
|
|
input_dev->close = gtco_input_close;
|
2007-01-18 16:10:25 +01:00
|
|
|
|
|
|
|
/* Set input device information */
|
2007-04-12 07:33:00 +02:00
|
|
|
input_dev->name = "GTCO_CalComp";
|
|
|
|
input_dev->phys = gtco->usbpath;
|
2007-04-12 07:34:39 +02:00
|
|
|
|
|
|
|
input_set_drvdata(input_dev, gtco);
|
2007-01-18 16:10:25 +01:00
|
|
|
|
|
|
|
/* Now set up all the input device capabilities */
|
2007-04-12 07:33:00 +02:00
|
|
|
gtco_setup_caps(input_dev);
|
2007-01-18 16:10:25 +01:00
|
|
|
|
|
|
|
/* Set input device required ID information */
|
2007-04-12 07:33:00 +02:00
|
|
|
usb_to_input_id(gtco->usbdev, &input_dev->id);
|
2007-04-12 07:35:03 +02:00
|
|
|
input_dev->dev.parent = &usbinterface->dev;
|
2007-01-18 16:10:25 +01:00
|
|
|
|
|
|
|
/* Setup the URB, it will be posted later on open of input device */
|
|
|
|
endpoint = &usbinterface->altsetting[0].endpoint[0].desc;
|
|
|
|
|
2007-04-12 07:33:00 +02:00
|
|
|
usb_fill_int_urb(gtco->urbinfo,
|
|
|
|
gtco->usbdev,
|
|
|
|
usb_rcvintpipe(gtco->usbdev,
|
2007-01-18 16:10:25 +01:00
|
|
|
endpoint->bEndpointAddress),
|
2007-04-12 07:33:00 +02:00
|
|
|
gtco->buffer,
|
2007-01-18 16:10:25 +01:00
|
|
|
REPORT_MAX_SIZE,
|
|
|
|
gtco_urb_callback,
|
2007-04-12 07:33:00 +02:00
|
|
|
gtco,
|
2007-01-18 16:10:25 +01:00
|
|
|
endpoint->bInterval);
|
|
|
|
|
2007-04-12 07:33:00 +02:00
|
|
|
gtco->urbinfo->transfer_dma = gtco->buf_dma;
|
|
|
|
gtco->urbinfo->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
2007-01-18 16:10:25 +01:00
|
|
|
|
2007-04-12 07:33:00 +02:00
|
|
|
/* Save gtco pointer in USB interface gtco */
|
|
|
|
usb_set_intfdata(usbinterface, gtco);
|
2007-01-18 16:10:25 +01:00
|
|
|
|
|
|
|
/* All done, now register the input device */
|
2007-04-12 07:33:00 +02:00
|
|
|
error = input_register_device(input_dev);
|
|
|
|
if (error)
|
|
|
|
goto err_free_urb;
|
2007-01-18 16:10:25 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
2007-04-12 07:33:00 +02:00
|
|
|
err_free_urb:
|
|
|
|
usb_free_urb(gtco->urbinfo);
|
|
|
|
err_free_buf:
|
|
|
|
usb_buffer_free(gtco->usbdev, REPORT_MAX_SIZE,
|
|
|
|
gtco->buffer, gtco->buf_dma);
|
|
|
|
err_free_devs:
|
|
|
|
kfree(report);
|
|
|
|
input_free_device(input_dev);
|
|
|
|
kfree(gtco);
|
|
|
|
return error;
|
2007-01-18 16:10:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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 */
|
2007-04-12 07:33:00 +02:00
|
|
|
struct gtco *gtco = usb_get_intfdata(interface);
|
2007-01-18 16:10:25 +01:00
|
|
|
|
|
|
|
/* Now reverse all the registration stuff */
|
2007-04-12 07:33:00 +02:00
|
|
|
if (gtco) {
|
|
|
|
input_unregister_device(gtco->inputdevice);
|
|
|
|
usb_kill_urb(gtco->urbinfo);
|
|
|
|
usb_free_urb(gtco->urbinfo);
|
|
|
|
usb_buffer_free(gtco->usbdev, REPORT_MAX_SIZE,
|
|
|
|
gtco->buffer, gtco->buf_dma);
|
|
|
|
kfree(gtco);
|
2007-01-18 16:10:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
info("gtco driver disconnected");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* STANDARD MODULE LOAD ROUTINES */
|
|
|
|
|
|
|
|
static struct usb_driver gtco_driverinfo_table = {
|
2007-04-12 07:33:00 +02:00
|
|
|
.name = "gtco",
|
|
|
|
.id_table = gtco_usbid_table,
|
|
|
|
.probe = gtco_probe,
|
|
|
|
.disconnect = gtco_disconnect,
|
2007-01-18 16:10:25 +01:00
|
|
|
};
|
2007-04-12 07:33:00 +02:00
|
|
|
|
2007-01-18 16:10:25 +01:00
|
|
|
/*
|
|
|
|
* Register this module with the USB subsystem
|
|
|
|
*/
|
|
|
|
static int __init gtco_init(void)
|
|
|
|
{
|
2007-04-12 07:33:00 +02:00
|
|
|
int error;
|
|
|
|
|
|
|
|
error = usb_register(>co_driverinfo_table);
|
|
|
|
if (error) {
|
|
|
|
err("usb_register() failed rc=0x%x", error);
|
|
|
|
return error;
|
2007-01-18 16:10:25 +01:00
|
|
|
}
|
2007-04-12 07:33:00 +02:00
|
|
|
|
|
|
|
printk("GTCO usb driver version: %s", GTCO_VERSION);
|
|
|
|
return 0;
|
2007-01-18 16:10:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Deregister this module with the USB subsystem
|
|
|
|
*/
|
|
|
|
static void __exit gtco_exit(void)
|
|
|
|
{
|
|
|
|
usb_deregister(>co_driverinfo_table);
|
|
|
|
}
|
|
|
|
|
2007-04-12 07:33:00 +02:00
|
|
|
module_init(gtco_init);
|
|
|
|
module_exit(gtco_exit);
|
2007-01-18 16:10:25 +01:00
|
|
|
|
|
|
|
MODULE_LICENSE("GPL");
|