ChangeSet 1.1261, 2003/06/18 17:07:07-07:00, bwheadley@earthlink.net [PATCH] USB: Aiptek kernel driver 1.0 for Kernel 2.4 Documentation/Configure.help | 9 MAINTAINERS | 7 drivers/usb/aiptek.c | 1200 +++++++++++++++++++++++++++++++++++++++---- drivers/usb/hid-core.c | 16 4 files changed, 1139 insertions(+), 93 deletions(-) diff -Nru a/Documentation/Configure.help b/Documentation/Configure.help --- a/Documentation/Configure.help Wed Jun 18 17:34:45 2003 +++ b/Documentation/Configure.help Wed Jun 18 17:34:45 2003 @@ -13919,11 +13919,12 @@ The module will be called powermate.o. If you want to compile it as a module, say M here and read . -Aiptek 6000U/8000U tablet support +Aiptek HyperPen tablet support CONFIG_USB_AIPTEK - Say Y here if you want to use the USB version of the Aiptek 6000U/8000U - tablet. Make sure to say Y to "Event interface support" - (CONFIG_INPUT_EVDEV) as well. + Say Y here if you want to use the USB version of the Aiptek HyperPen + Digital Tablet (models 4000U, 5000U, 6000U, 8000U, and 12000U.) + Make sure to say Y to "Mouse support" (CONFIG_INPUT_MOUSEDEV) and/or + "Event interface support" (CONFIG_INPUT_EVDEV) as well. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). diff -Nru a/MAINTAINERS b/MAINTAINERS --- a/MAINTAINERS Wed Jun 18 17:34:45 2003 +++ b/MAINTAINERS Wed Jun 18 17:34:45 2003 @@ -214,6 +214,13 @@ W: http://www.linux-usb.org/SpeedTouch/ S: Maintained +AIPTEK USB TABLET DRIVER +P: Bryan W. Headley +M: Bryan W. Headley +W: http://aiptektablet.sourceforge.net +L: aiptektablet-users@sourceforge.net +S: Maintained + ALTERA EPXA1/EPXA10 DEVELOPMENT BOARD PORT P: Clive Davies M: cdavies@altera.com diff -Nru a/drivers/usb/aiptek.c b/drivers/usb/aiptek.c --- a/drivers/usb/aiptek.c Wed Jun 18 17:34:45 2003 +++ b/drivers/usb/aiptek.c Wed Jun 18 17:34:45 2003 @@ -1,7 +1,9 @@ /* - * Native support for the Aiptek 8000U + * Native support for the Aiptek HyperPen USB Tablets + * (4000U/5000U/6000U/8000U/12000U) * - * Copyright (c) 2001 Chris Atenasio + * Copyright (c) 2001 Chris Atenasio + * Copyright (c) 2002-2003 Bryan W. Headley * * based on wacom.c by * Vojtech Pavlik @@ -11,13 +13,28 @@ * James E. Blair * Daniel Egger * - * * Many thanks to Oliver Kuechemann for his support. * * ChangeLog: * v0.1 - Initial release - * v0.2 - Hack to get around fake event 28's. + * v0.2 - Hack to get around fake event 28's. (Bryan W. Headley) * v0.3 - Make URB dynamic (Bryan W. Headley, Jun-8-2002) + * Released to Linux 2.4.19 and 2.5.x + * v0.4 - Rewrote substantial portions of the code to deal with + * corrected control sequences, timing, dynamic configuration, + * support of 6000U - 12000U, procfs, and macro key support + * (Jan-1-2003 - Feb-5-2003, Bryan W. Headley) + * v1.0 - Added support for diagnostic messages, count of messages + * received from URB - Mar-8-2003, Bryan W. Headley + * + * NOTE: + * This kernel driver is augmented by the "Aiptek" XFree86 input + * driver for your X server, as well as a GUI Front-end "Tablet Manager". + * These three products are highly interactive with one another, + * so therefore it's easier to document them all as one subsystem. + * Please visit the project's "home page", located at, + * http://aiptektablet.sourceforge.net. + * */ /* @@ -42,13 +59,15 @@ #include #include #include +#include +#include /* * Version Information */ -#define DRIVER_VERSION "v0.3" -#define DRIVER_AUTHOR "Chris Atenasio " -#define DRIVER_DESC "USB Aiptek 6000U/8000U tablet driver (Linux 2.4.x)" +#define DRIVER_VERSION "v1.0 Mar-8-2003" +#define DRIVER_AUTHOR "Bryan W. Headley/Chris Atenasio" +#define DRIVER_DESC "Aiptek HyperPen USB Tablet Driver (Linux 2.4.x)" MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); @@ -56,6 +75,7 @@ /* * Aiptek status packet: + * (returned as Report 1) * * bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 * byte0 0 0 0 0 0 0 1 0 @@ -69,10 +89,14 @@ * * IR: In Range = Proximity on * DV = Data Valid + * BS = Barrel Switch (as in, macro keys) + * BS2 also referred to as Tablet Pick * - * * Command Summary: * + * Use report_type CONTROL (3) + * Use report_id 2 + * * Command/Data Description Return Bytes Return Value * 0x10/0x00 SwitchToMouse 0 * 0x10/0x01 SwitchToTablet 0 @@ -85,30 +109,278 @@ * 0x03/0x00 GetODMCode 2 ODMCode * 0x08/0x00 GetPressureLevels 2 =512 * 0x04/0x00 GetFirmwareVersion 2 Firmware Version + * 0x11/0x02 EnableMacroKeys 0 * * * To initialize the tablet: * - * (1) Send command Resolution500LPI - * (2) Option Commands (GetXExtension, GetYExtension) - * (3) Send command SwitchToTablet + * (1) Send Resolution500LPI (Command) + * (2) Query for Model code (Option Report) + * (3) Query for ODM code (Option Report) + * (4) Query for firmware (Option Report) + * (5) Query for GetXExtension (Option Report) + * (6) Query for GetYExtension (Option Report) + * (7) Query for GetPressureLevels (Option Report) + * (8) SwitchToTablet for Absolute coordinates, or + * SwitchToMouse for Relative coordinates (Command) + * (9) EnableMacroKeys (Command) + * (10) FilterOn (Command) + * (11) AutoGainOn (Command) + * + * (Step 9 can be omitted, but you'll then have no function keys.) + * + * The procfs interface + * -------------------- + * + * This driver supports delivering configuration/status reports + * through {procfs}/driver/usb/aiptek. ("procfs" is normally mounted + * to /proc.) Said file can be found while the driver is active in + * memory; it will be removed when the driver is removed, either + * through user intervention (rmmod aiptek) or through software + * such as "hotplug". + * + * Reading from the Procfs interface + * --------------------------------- + * + * The user may determine the status of the tablet by reading the + * report in the procfs interface, /proc/driver/usb/aiptek. + * The report as of driver version 1.0, looks like, + * + * Aiptek Tablet (3000x2250, 8.00x6.00", 202x152mm) + * (USB VendorID 0x08ca, ProductID 0x0020, ODMCode 0x0004 + * ModelCode: 0x64, FirmwareCode: 0x0400) + * on /dev/input/event0 + * pointer=either + * coordinate=absolute + * tool=pen + * xtilt=disable + * ytilt=disable + * jitter=50 + * diagnostic=none + * eventsReceived=0 + * + * (spurious ", for the benefit of vim's syntax highlighting.) + * + * This report indicates the tablet recognized. (Because Aiptek reuses + * the USB 'productID' over several tablets, it's pointless for us to + * guess which model you have: we'll instead tell you the size of + * the tablet's drawing area, which we indicate in coordinates, inches, + * and millimeters.) We also indicate datum read from the USB interface, + * such as vendorId, productId, ODMcode, etc. It's there "just in case." + * + * on /dev/input/event0 + * + * Linux supports HID-compliant USB devices (such as this tablet) by + * transposing their reports to the Linux Input Event System format. Which + * means, if you want to data from the tablet, that's where it will be + * made available from. For information on the Input Event System, see + * the docs in ./Documentation/input, in the kernel source tree. + * + * And yes, depending on the order in which other supported Input Event + * devices are recognized and configured, the tablet may be allocated + * to a different device driver name: it's all dynamic. Use of the devfs + * file system is a help. + * + * The keyword=value part of the report mostly shows what the programmable + * parameters have been set to. We describe those below, and how to + * program/reprogram them. Note: tablet parameters are to be programmed + * while the tablet is attached and active. They are not set as arguments + * to the kernel during bootup. + * + * Here are the "read-only" parameters, and what they mean: + * + * diagnostic=stringValue + * eventsReceived=numericValue + * + * diagnostic: The tablet driver attempts to explain why things are not + * working correctly. (To the best of it's insular abilities) + * + * By default, the tablet boots up in Relative Coordinate + * mode. This driver initially attempts to program it in Absolute + * Coordinate mode (and of course, the user can subsequently choose + * which mode they want.) So, therefore, the situation can arise + * where the tablet is in one mode, and the driver believes it + * is in the other mode. The driver, however, cannot divine + * this mismatch until input events are received. + * Two reports indicate such mode-mismatches between the tablet + * and the driver, and are, + * + * "tablet sending relative reports" + * "tablet sending absolute reports" + * + * The next diagnostic operates in conjunction with the "pointer=" + * programmable parameter. With it, you can indicate that you want + * the tablet to only accept reports from the stylus, or only from the + * mouse. (You can also specify to allow reports from either.) What + * happens when you specify that you only want mouse reports, yet + * the tablet keeps receiving reports from the stylus? Well, first, + * it's a "pilot error", but secondly, it tries to diagnose the issue + * with the following reports, + * + * "tablet seeing reports from stylus" + * "tablet seeing reports from mouse" + * + * What if there is nothing to report? The inference in the diagnostic + * reports is that something is happening which shouldn't: when things + * appear to be working right, the report is, + * + * "none" + * + * The error diagnostic report is dynamic: it only reports issues + * that are happening, or have happened as of the last event received. + * It will reset following any attempt to reprogram the tablet's mode. + * + * eventsReceived: Occasionally, your movements on the tablet are not being + * reported. Usually, this indicates that your tablet is out of sync + * with the USB interface driver, or itself is not sending reports + * out. To help diagnose this, we keep an active count of events + * received from the tablet. So, if you move the stylus, and yet + * your client application doesn't notice, make + * note of the eventsReceived, and then move the stylus again. If the + * event counter's number doesn't change, then the tablet indeed has + * "froze". + * + * We have found that sending the tablet a command sequence often + * will clear up "frozen" tablets. Which segues into the section + * about how to program your tablet through the procfs interface, + * + * Writing to the procfs interface + * ------------------------------- + * + * The user may configure the tablet by writing ASCII + * commands to the /proc/driver/usb/aiptek file. Commands which are + * accepted are, + * + * pointer=stringvalue {stylus|mouse|either} + * coordinate=stringvalue {absolute|relative} + * tool=stringvalue {mouse|rubber|pen|pencil|brush|airbrush} + * xtilt=string_or_numeric {disable|[-128..127]} + * ytilt=string_or_numeric {disable|[-128..127]} + * jitter=numericvalue {0..xxx} + * + * pointer: you can specify that reports are to be excepted ONLY from the + * stylus, or ONLY from the mouse. 'either' allows reports from either + * device to be accepted, and is the default. + * coordinate: you can specify that either absolute or relative coordinate + * reports are issued by the tablet. By default, absolute reports are + * sent. + * tool: The stylus by default prepends TOOL_BTN_PEN events with it's + * reports. But you may decide that you want your stylus to behave + * like an eraser (named 'rubber', following tablet conventions,) + * or a pencil, etc. The behavior is dependent upon the client software + * consuming the tablet's events, e.g., the XFree86 tablet driver. + * xtilt: By default this is disabled. However, other tablets have a notion + * of measuring the angle at which the stylus pen is held against the + * drawing surface, along the X axis. Aiptek tablets cannot sense this, + * but if you want to send "held-at-angle" reports, specify the value, + * an integer between -128 and 127 (inclusive) that you want to send. + * This data will be sent along with regular tablet input. Obviously, + * the inference here is that your hand does not change angles + * while drawing (until you go back to this procfs interface, and + * change the value)! + * + * When you consider actual drawing tools (real pens, brushes), + * knowing the tools' tip shape and the angle that you hold the tool + * becomes important, insofar as calculating the surface of the tip + * that actually touches the surface of the paper. Knowledge of what + * to do with xtilt reports is solely in the realm of your client + * software. + * + * Yes, there is a difference between xtilt=0 and xtilt=disable + * settings. The former sends a report that the angle is a 0; + * the other indicates that NO xtilt reports are to be sent at all. + * ytilt: By default this is disabled. This provides similar functionality + * to xtilt, except that we're measuring the angle the stylus pen is + * held against the drawing surface, along the Y axis. Same cavaets + * apply as for xtilt. + * jitter: By default, this is set to 50. When pressing a button on + * either the mouse or the stylus pen, you will probably notice that + * the tool moves slightly from it's original position, until your + * hand steadies it. During that period of time, the pen is "jittering", + * sending spurious movement events that perhaps you'd like it not to + * send. What we do is set a moratorium, measured in milliseconds, + * during which we do not send movement events. So, the default is 50ms; + * you obviously can set it to zero or incredibly unreasonable values + * (no reports for 4 seconds following the pressing of a stylus button!) + * + * Interesting Side-Note + * --------------------- + * + * The tablet has "frozen" and you'd like to send it a command to wake it + * up. But you don't want to change how the driver's currently configured. + * + * 1. Send a command to /proc/driver/usb/aiptek with the same setting + * already reported by the driver. + * 2. Send an illegal string to procfs file ("wakeup=now" is always good) + * 3. Because, the driver always attempts to reprogram the tablet to it's + * current settings following a write to the procfs interface. + * + * Hmm, still does not work. + * ------------------------- + * + * This is slightly harder to diagnose. You may be receiving frame errors + * from the USB interface driver (see /var/log/messages for any diagnostics). + * + * Alternatively, you may be running something like 'hotplug' that attempts + * to match discovered USB devices to it's list of device drivers. + * Unfortunately, because this is a tablet that can send relative X,Y events, + * it "looks like" a mouse! A usb mouse driver may have possession of + * input from the tablet. On the other hand, the tablet also supports + * absolute reports from barrel switches, which sounds a lot like a "joystick", + * and the software again can be fooled into loading the wrong driver for + * the tablet. The distinction is, USB HID devices tell you what they + * are capable of, rather than what they are. + * + * Come visit this driver's home page at http://aiptektablet.sourceforge.net + * for further assistance. */ #define USB_VENDOR_ID_AIPTEK 0x08ca +#define AIPTEK_POINTER_ONLY_MOUSE_MODE 0 +#define AIPTEK_POINTER_ONLY_STYLUS_MODE 1 +#define AIPTEK_POINTER_EITHER_MODE 2 + +#define AIPTEK_POINTER_ALLOW_MOUSE_MODE(a) \ + (a == AIPTEK_POINTER_ONLY_MOUSE_MODE || \ + a == AIPTEK_POINTER_EITHER_MODE) +#define AIPTEK_POINTER_ALLOW_STYLUS_MODE(a) \ + (a == AIPTEK_POINTER_ONLY_STYLUS_MODE || \ + a == AIPTEK_POINTER_EITHER_MODE) + +#define AIPTEK_COORDINATE_RELATIVE_MODE 0 +#define AIPTEK_COORDINATE_ABSOLUTE_MODE 1 + +#define AIPTEK_TILT_MIN (-128) +#define AIPTEK_TILT_MAX 127 +#define AIPTEK_TILT_DISABLE (-10101) + +#define AIPTEK_TOOL_BUTTON_PEN_MODE 0 +#define AIPTEK_TOOL_BUTTON_PENCIL_MODE 1 +#define AIPTEK_TOOL_BUTTON_BRUSH_MODE 2 +#define AIPTEK_TOOL_BUTTON_AIRBRUSH_MODE 3 +#define AIPTEK_TOOL_BUTTON_RUBBER_MODE 4 +#define AIPTEK_TOOL_BUTTON_MOUSE_MODE 5 + +#define AIPTEK_DIAGNOSTIC_NA 0 +#define AIPTEK_DIAGNOSTIC_SENDING_RELATIVE_IN_ABSOLUTE 1 +#define AIPTEK_DIAGNOSTIC_SENDING_ABSOLUTE_IN_RELATIVE 2 +#define AIPTEK_DIAGNOSTIC_TOOL_DISALLOWED 3 + + // Time to wait (in ms) to help mask hand jittering + // when pressing the stylus buttons. +#define AIPTEK_JITTER_DELAY_DEFAULT 50 + struct aiptek_features { char *name; int pktlen; int x_max; int y_max; - int pressure_min; int pressure_max; + int odmCode; + int modelCode; + int firmwareCode; void (*irq) (struct urb * urb); - unsigned long evbit; - unsigned long absbit; - unsigned long relbit; - unsigned long btnbit; - unsigned long digibit; }; struct aiptek { @@ -117,59 +389,381 @@ struct usb_device *usbdev; struct urb *irq; struct aiptek_features *features; - int tool; - int open; + unsigned int ifnum; + int open_count; + int pointer_mode; + int coordinate_mode; + int tool_mode; + int xTilt; + int yTilt; + int diagnostic; + unsigned long eventCount; + int jitterDelay; +#ifdef CONFIG_PROC_FS + struct proc_dir_entry *usbProcfsEntry; + struct proc_dir_entry *aiptekProcfsEntry; +#endif }; +/* + * Permit easy lookup of keyboard events to send, versus + * the bitmap which comes from the tablet. This hides the + * issue that the F_keys are not sequentially numbered. + */ +static int macroKeyEvents[] = { KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, + KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12, + KEY_F13, KEY_F14, KEY_F15, KEY_F16, KEY_F17, KEY_F18, + KEY_F19, KEY_F20, KEY_F21, KEY_F22, KEY_F23, KEY_F24, + KEY_STOP, KEY_AGAIN, KEY_PROPS, KEY_UNDO, KEY_FRONT, KEY_COPY, + KEY_OPEN, KEY_PASTE, 0 +}; + +#ifdef CONFIG_PROC_FS +extern struct proc_dir_entry *proc_root_driver; +#endif + +static int +aiptek_convert_from_2s_complement(unsigned char c) +{ + unsigned char b = c; + int negate = 0; + if (b & 0x80) { + b = ~b; + b--; + negate = 1; + } + int ret = b; + ret = (negate == 1) ? -ret : ret; + return ret; +} + +/* + * aiptek_irq can receive one of six potential reports. + * The documentation for each is in the body of the function. + * + * The tablet reports on several attributes per invocation of + * aiptek_irq. Because the Linux Input Event system allows the + * transmission of ONE attribute per input_report_xxx() call, + * collation has to be done on the other end to reconstitute + * a complete tablet report. Further, the number of Input Event reports + * submitted varies, depending on what USB report type, and circumstance. + * To deal with this, EV_MSC is used to indicate an 'end-of-report' + * message. This has been an undocumented convention understood by the kernel + * tablet driver and clients such as gpm and XFree86's tablet drivers. + * + * Of the information received from the tablet, the one piece I + * cannot transmit is the proximity bit (without resorting to an EV_MSC + * convention above.) I therefore have taken over REL_MISC and ABS_MISC + * (for relative and absolute reports, respectively) for communicating + * Proximity. Why two events? I thought it interesting to know if the + * Proximity event occured while the tablet was in absolute or relative + * mode. + * + * Other tablets use the notion of a certain minimum stylus pressure + * to infer proximity. While that could have been done, that is yet + * another 'by convention' behavior, the documentation for which + * would be spread between two (or more) pieces of software. + * + * EV_MSC usage is terminated in Linux 2.5.x. + */ + static void aiptek_irq(struct urb *urb) { struct aiptek *aiptek = urb->context; unsigned char *data = aiptek->data; struct input_dev *dev = &aiptek->dev; - int x; - int y; - int pressure; - int proximity; + int jitterable = 0; if (urb->status) return; - if ((data[0] & 2) == 0) { - dbg("received unknown report #%d", data[0]); - } - - proximity = data[5] & 0x01; - input_report_key(dev, BTN_TOOL_PEN, proximity); + aiptek->eventCount++; - x = ((__u32) data[1]) | ((__u32) data[2] << 8); - y = ((__u32) data[3]) | ((__u32) data[4] << 8); - pressure = ((__u32) data[6]) | ((__u32) data[7] << 8); - pressure -= aiptek->features->pressure_min; + // Report 1 delivers relative coordinates with either a stylus + // or the mouse. You do not know which tool generated the event. + if (data[0] == 1) { + if (aiptek->coordinate_mode == AIPTEK_COORDINATE_ABSOLUTE_MODE) { + aiptek->diagnostic = + AIPTEK_DIAGNOSTIC_SENDING_RELATIVE_IN_ABSOLUTE; + } else { + if (aiptek->tool_mode != AIPTEK_TOOL_BUTTON_MOUSE_MODE) { + aiptek->tool_mode = + AIPTEK_TOOL_BUTTON_MOUSE_MODE; + input_report_key(dev, BTN_TOOL_MOUSE, 1); + } + int x = aiptek_convert_from_2s_complement(data[2]); + int y = aiptek_convert_from_2s_complement(data[3]); + + int left = data[5] & 0x01; + int right = data[5] & 0x02; + int middle = data[5] & 0x04; + + jitterable = left | right | middle; + + input_report_key(dev, BTN_LEFT, left); + input_report_key(dev, BTN_MIDDLE, middle); + input_report_key(dev, BTN_RIGHT, right); + input_report_rel(dev, REL_X, x); + input_report_rel(dev, REL_Y, y); + input_report_rel(dev, REL_MISC, 1); - if (pressure < 0) { - pressure = 0; + input_event(dev, EV_MSC, MSC_SERIAL, 0); + } } - - if (proximity) { - input_report_abs(dev, ABS_X, x); - input_report_abs(dev, ABS_Y, y); - input_report_abs(dev, ABS_PRESSURE, pressure); - input_report_key(dev, BTN_TOUCH, data[5] & 0x04); - input_report_key(dev, BTN_STYLUS, data[5] & 0x08); - input_report_key(dev, BTN_STYLUS2, data[5] & 0x10); + // Report 2 is delivered only by the stylus, and delivers + // absolute coordinates. + else if (data[0] == 2) { + if (aiptek->coordinate_mode == AIPTEK_COORDINATE_RELATIVE_MODE) { + aiptek->diagnostic = + AIPTEK_DIAGNOSTIC_SENDING_ABSOLUTE_IN_RELATIVE; + } else + if (!AIPTEK_POINTER_ALLOW_STYLUS_MODE(aiptek->pointer_mode)) + { + aiptek->diagnostic = AIPTEK_DIAGNOSTIC_TOOL_DISALLOWED; + } else { + int x = ((__u32) data[1]) | ((__u32) data[2] << 8); + int y = ((__u32) data[3]) | ((__u32) data[4] << 8); + int z = ((__u32) data[6]) | ((__u32) data[7] << 8); + + int p = data[5] & 0x01; + int dv = data[5] & 0x02; + int tip = data[5] & 0x04; + int bs = data[5] & 0x08; + int pck = data[5] & 0x10; + + // dv indicates 'data valid' (e.g., the tablet is in sync + // and has delivered a "correct" report) We will ignore + // all 'bad' reports... + if (dv != 0) { + switch (aiptek->tool_mode) { + case AIPTEK_TOOL_BUTTON_PEN_MODE: + { + input_report_key(dev, + BTN_TOOL_PEN, + 1); + } + break; + + case AIPTEK_TOOL_BUTTON_PENCIL_MODE: + { + input_report_key(dev, + BTN_TOOL_PENCIL, + 1); + } + break; + + case AIPTEK_TOOL_BUTTON_BRUSH_MODE: + { + input_report_key(dev, + BTN_TOOL_BRUSH, + 1); + } + break; + + case AIPTEK_TOOL_BUTTON_AIRBRUSH_MODE: + { + input_report_key(dev, + BTN_TOOL_AIRBRUSH, + 1); + } + break; + + case AIPTEK_TOOL_BUTTON_RUBBER_MODE: + { + input_report_key(dev, + BTN_TOOL_RUBBER, + 1); + } + break; + + case AIPTEK_TOOL_BUTTON_MOUSE_MODE: + { + input_report_key(dev, + BTN_TOOL_MOUSE, + 1); + } + break; + } + + input_report_abs(dev, ABS_X, x); + input_report_abs(dev, ABS_Y, y); + + /* + * The user is allowed to switch from one of the + * stylus tools to the Mouse using the front-end GUI. + * An issue that will arise, however, is what happens + * when the user HAS issued a TOOL_BTN_MOUSE, but has not + * yet swapped tools. Well, we can "pretend" to be a mouse + * by sending overriding tip, barrelswitch and pick. + * This stupidity should not be used as an excuse not + * to physically move your Aiptek mouse into the tablet's + * active area -- it merely provides momentary convenience + * during that transition. + */ + if (aiptek->tool_mode == + AIPTEK_TOOL_BUTTON_MOUSE_MODE) { + input_report_key(dev, BTN_LEFT, tip); + input_report_key(dev, BTN_RIGHT, bs); + input_report_key(dev, BTN_MIDDLE, pck); + + jitterable = tip | bs | pck; + } else { + input_report_abs(dev, ABS_PRESSURE, z); + + input_report_key(dev, BTN_TOUCH, tip); + input_report_key(dev, BTN_STYLUS, bs); + input_report_key(dev, BTN_STYLUS2, pck); + + jitterable = tip | bs | pck; + + if (aiptek->xTilt != + AIPTEK_TILT_DISABLE) + input_report_abs(dev, + ABS_TILT_X, + aiptek->xTilt); + if (aiptek->yTilt != + AIPTEK_TILT_DISABLE) + input_report_abs(dev, + ABS_TILT_Y, + aiptek->yTilt); + } + input_report_abs(dev, ABS_MISC, p); + input_event(dev, EV_MSC, MSC_SERIAL, 0); + } + } + } + // Report 3's come from the mouse in absolute mode. + else if (data[0] == 3) { + if (aiptek->coordinate_mode == AIPTEK_COORDINATE_RELATIVE_MODE) { + aiptek->diagnostic = + AIPTEK_DIAGNOSTIC_SENDING_ABSOLUTE_IN_RELATIVE; + } else + if (!AIPTEK_POINTER_ALLOW_MOUSE_MODE(aiptek->pointer_mode)) + { + aiptek->diagnostic = AIPTEK_DIAGNOSTIC_TOOL_DISALLOWED; + } else { + int x = ((__u32) data[1]) | ((__u32) data[2] << 8); + int y = ((__u32) data[3]) | ((__u32) data[4] << 8); + int p = data[5] & 0x01; + int dv = data[5] & 0x02; + int left = data[5] & 0x04; + int right = data[5] & 0x08; + int middle = data[5] & 0x10; + + if (dv != 0) { + input_report_key(dev, BTN_TOOL_MOUSE, 1); + input_report_abs(dev, ABS_X, x); + input_report_abs(dev, ABS_Y, y); + + input_report_key(dev, BTN_LEFT, left); + input_report_key(dev, BTN_MIDDLE, middle); + input_report_key(dev, BTN_RIGHT, right); + + jitterable = left | middle | right; + + input_report_rel(dev, REL_MISC, p); + input_event(dev, EV_MSC, MSC_SERIAL, 0); + } + } + } + // Report 4s come from the macro keys when pressed by stylus + else if (data[0] == 4) { + int p = data[1] & 0x01; + int dv = data[1] & 0x02; + int tip = data[1] & 0x04; + int bs = data[1] & 0x08; + int pck = data[1] & 0x10; + + int m = data[3]; + int z = ((__u32) data[4]) | ((__u32) data[5] << 8); + + if (dv != 0) { + input_report_key(dev, BTN_TOUCH, tip); + input_report_key(dev, BTN_STYLUS, bs); + input_report_key(dev, BTN_STYLUS2, pck); + + jitterable = tip | bs | pck; + + input_report_key(dev, macroKeyEvents[m - 1], 1); + input_report_abs(dev, ABS_PRESSURE, z); + input_report_abs(dev, ABS_MISC, p); + input_event(dev, EV_MSC, MSC_SERIAL, 0); + } + } + // Report 5s come from the macro keys when pressed by mouse + else if (data[0] == 5) { + int p = data[1] & 0x01; + int dv = data[1] & 0x02; + int left = data[1] & 0x04; + int right = data[1] & 0x08; + int middle = data[1] & 0x10; + int macro = data[3]; + + if (dv != 0) { + input_report_key(dev, BTN_LEFT, left); + input_report_key(dev, BTN_MIDDLE, middle); + input_report_key(dev, BTN_RIGHT, right); + + jitterable = left | middle | right; + + input_report_key(dev, macroKeyEvents[macro - 1], 1); + input_report_rel(dev, ABS_MISC, p); + input_event(dev, EV_MSC, MSC_SERIAL, 0); + } + } + // We have no idea which tool can generate a report 6. Theoretically, + // neither need to, having been given reports 4 & 5 for such use. + // However, report 6 is the 'official-looking' report for macroKeys; + // reports 4 & 5 supposively are used to support unnamed, unknown + // hat switches (which just so happen to be the macroKeys.) + else if (data[0] == 6) { + int macro = ((__u32) data[1]) | ((__u32) data[2] << 8); + + input_report_key(dev, macroKeyEvents[macro - 1], 1); + input_report_abs(dev, ABS_MISC, 1); + input_event(dev, EV_MSC, MSC_SERIAL, 0); + } else { + dbg("Unknown report %d", data[0]); } + // Jitter may occur when the user presses a button on the stlyus + // or the mouse. What we do to prevent that is wait 'x' milliseconds + // following a 'jitterable' event, which should give the hand some time + // stabilize itself. + if (jitterable != 0 && aiptek->jitterDelay != 0) { + wait_ms(aiptek->jitterDelay); + } } +/* + * We are not able to reliably determine the tablet featureset by + * asking for the USB productID. Therefore, we will query the + * tablet dynamically and populate the struct in aiptek_probe(). + */ + struct aiptek_features aiptek_features[] = { - {"Aiptek 6000U/8000U", - 8, 3000, 2250, 26, 511, aiptek_irq, 0, 0, 0, 0}, + {"Aiptek", 8, 0, 0, 0, 0, 0, 0, aiptek_irq}, {NULL, 0} }; +/* + * These are the USB id's known so far. We do not identify them to + * specific Aiptek model numbers, because there has been overlaps, + * use, and reuse of id's in existing models. Certain models have + * been known to use more than one ID, indicative perhaps of + * manufacturing revisions. In any event, we consider these + * IDs to not be model-specific nor unique. + */ + struct usb_device_id aiptek_ids[] = { - {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x20), driver_info:0}, + {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x01), driver_info:0}, + {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x10), driver_info:0}, + {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x20), driver_info:0}, + {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x21), driver_info:0}, + {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x22), driver_info:0}, + {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x23), driver_info:0}, + {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x24), driver_info:0}, {} }; @@ -179,8 +773,7 @@ aiptek_open(struct input_dev *dev) { struct aiptek *aiptek = dev->private; - - if (aiptek->open++) + if (aiptek->open_count++) return 0; aiptek->irq->dev = aiptek->usbdev; @@ -195,31 +788,416 @@ { struct aiptek *aiptek = dev->private; - if (!--aiptek->open) + if (!--aiptek->open_count) usb_unlink_urb(aiptek->irq); } +/* + * Send a command to the tablet. No reply is expected. + */ static void aiptek_command(struct usb_device *dev, unsigned int ifnum, unsigned char command, unsigned char data) { __u8 buf[3]; - buf[0] = 4; + buf[0] = 2; + buf[1] = command; + buf[2] = data; + + if (usb_set_report(dev, ifnum, 3, 2, buf, sizeof (buf)) != sizeof (buf)) { + dbg("aiptek_command failed, sending: 0x%02x 0x%02x", command, + data); + } +} + +/* + * Send a query to the tablet. This is done by sending the query stream + * first as a command, waiting a few milliseconds, then submitting the + * same stream as a query. + */ +static unsigned int +aiptek_query(struct usb_device *dev, unsigned int ifnum, + unsigned char command, unsigned char data) +{ + unsigned int ret; + __u8 buf[8]; + buf[0] = 2; buf[1] = command; buf[2] = data; - if (usb_set_report(dev, ifnum, 3, 2, buf, 3) != 3) { - dbg("aiptek_command: 0x%x 0x%x\n", command, data); + aiptek_command(dev, ifnum, command, data); + wait_ms(400); + + if (usb_get_report(dev, ifnum, 3, 2, buf, 3) < 3) { + dbg("aiptek_query failed: returns 0x%02x 0x%02x 0x%02x", + buf[0], buf[1], buf[2]); + return 0; + } + ret = ((__u32) buf[1]) | ((__u32) buf[2] << 8); + return ret; +} + +/* + * Program the tablet into either absolute or relative mode. + * + * We also get information about the tablet's size. + */ +static void +aiptek_program_tablet(struct aiptek *aiptek) +{ + int modelCode, odmCode, firmwareCode; + int xResolution, yResolution, zResolution; + + aiptek->diagnostic = AIPTEK_DIAGNOSTIC_NA; + + // execute Resolution500LPI + aiptek_command(aiptek->usbdev, aiptek->ifnum, 0x18, 0x04); + // query getModelCode + modelCode = aiptek_query(aiptek->usbdev, aiptek->ifnum, 0x02, 0x00); + // query getODMCode + odmCode = aiptek_query(aiptek->usbdev, aiptek->ifnum, 0x03, 0x00); + // query getFirmwareCode + firmwareCode = aiptek_query(aiptek->usbdev, aiptek->ifnum, 0x04, 0x00); + // query getXextension + xResolution = aiptek_query(aiptek->usbdev, aiptek->ifnum, 0x01, 0x00); + // query getYextension + yResolution = aiptek_query(aiptek->usbdev, aiptek->ifnum, 0x01, 0x01); + // query getPressureLevels + zResolution = aiptek_query(aiptek->usbdev, aiptek->ifnum, 0x08, 0x00); + + // Depending on whether we are in absolute or relative mode, we will + // do a switchToTablet(absolute) or switchToMouse(relative) command. + if (aiptek->coordinate_mode == AIPTEK_COORDINATE_ABSOLUTE_MODE) { + // execute switchToTablet + aiptek_command(aiptek->usbdev, aiptek->ifnum, 0x10, 0x01); + } else { + // execute switchToMouse + aiptek_command(aiptek->usbdev, aiptek->ifnum, 0x10, 0x00); + } + // This command enables the macro keys + aiptek_command(aiptek->usbdev, aiptek->ifnum, 0x11, 0x02); + // execute FilterOn + aiptek_command(aiptek->usbdev, aiptek->ifnum, 0x17, 0x00); + // execute AutoGainOn + aiptek_command(aiptek->usbdev, aiptek->ifnum, 0x12, 0xff); + + aiptek->features->odmCode = odmCode; + aiptek->features->modelCode = modelCode & 0xff; + aiptek->features->firmwareCode = firmwareCode; + aiptek->features->pressure_max = zResolution; + aiptek->features->x_max = xResolution; + aiptek->features->y_max = yResolution; + + aiptek->eventCount = 0; +} + +#if defined(CONFIG_PROC_FS) +/* + * This routine determines keywords and their associated values, and + * maps them to supported modes in this driver. It's input comes from + * aiptek_procfs_write(). + */ +static void +aiptek_procfs_parse(struct aiptek *aiptek, char *keyword, char *value) +{ + if (strcmp(keyword, "pointer") == 0) { + if (strcmp(value, "stylus") == 0) { + aiptek->pointer_mode = AIPTEK_POINTER_ONLY_STYLUS_MODE; + } else if (strcmp(value, "mouse") == 0) { + aiptek->pointer_mode = AIPTEK_POINTER_ONLY_MOUSE_MODE; + } else if (strcmp(value, "either") == 0) { + aiptek->pointer_mode = AIPTEK_POINTER_EITHER_MODE; + } + } else if (strcmp(keyword, "coordinate") == 0) { + if (strcmp(value, "relative") == 0) { + aiptek->coordinate_mode = + AIPTEK_COORDINATE_RELATIVE_MODE; + } else if (strcmp(value, "absolute") == 0) { + aiptek->coordinate_mode = + AIPTEK_COORDINATE_ABSOLUTE_MODE; + } + } else if (strcmp(keyword, "xtilt") == 0) { + if (strcmp(value, "disable") == 0) { + aiptek->xTilt = AIPTEK_TILT_DISABLE; + } else { + int x = (int) simple_strtol(value, 0, 10); + if (x >= AIPTEK_TILT_MIN && x <= AIPTEK_TILT_MAX) + aiptek->xTilt = x; + } + } else if (strcmp(keyword, "ytilt") == 0) { + if (strcmp(value, "disable") == 0) { + aiptek->yTilt = AIPTEK_TILT_DISABLE; + } else { + int y = (int) simple_strtol(value, 0, 10); + if (y >= AIPTEK_TILT_MIN && y <= AIPTEK_TILT_MAX) + aiptek->yTilt = y; + } + } else if (strcmp(keyword, "jitter") == 0) { + aiptek->jitterDelay = (int) simple_strtol(value, 0, 10); + } else if (strcmp(keyword, "tool") == 0) { + if (strcmp(value, "mouse") == 0) { + aiptek->tool_mode = AIPTEK_TOOL_BUTTON_MOUSE_MODE; + } else if (strcmp(value, "rubber") == 0) { + aiptek->tool_mode = AIPTEK_TOOL_BUTTON_RUBBER_MODE; + } else if (strcmp(value, "pencil") == 0) { + aiptek->tool_mode = AIPTEK_TOOL_BUTTON_PENCIL_MODE; + } else if (strcmp(value, "pen") == 0) { + aiptek->tool_mode = AIPTEK_TOOL_BUTTON_PEN_MODE; + } else if (strcmp(value, "brush") == 0) { + aiptek->tool_mode = AIPTEK_TOOL_BUTTON_BRUSH_MODE; + } else if (strcmp(value, "airbrush") == 0) { + aiptek->tool_mode = AIPTEK_TOOL_BUTTON_AIRBRUSH_MODE; + } + } +} + +/* + * This routine reads the status of the aiptek driver, and makes it + * available as a procfs file. The description of the procfs file + * is at the top of this driver source code. + */ +static int +aiptek_procfs_read(char *page, char **start, off_t offset, int count, + int *eof, void *data) +{ + int len; + char *out = page; + struct aiptek *aiptek = data; + + int inchX = aiptek->features->x_max / 375 * 100; + int inchY = aiptek->features->y_max / 375 * 100; + int mmX = (int) ((double) aiptek->features->x_max / 14.8); + int mmY = (int) ((double) aiptek->features->y_max / 14.8); + + out += + sprintf(out, "Aiptek Tablet (%dx%d, %d.%02dx%d.%02d\", %dx%dmm)\n", + aiptek->features->x_max, aiptek->features->y_max, + inchX / 100, inchX % 100, inchY / 100, inchY % 100, mmX, + mmY); + + out += + sprintf(out, + "(USB VendorID 0x%04x, ProductID 0x%04x, ODMCode 0x%04x\n", + aiptek->dev.idvendor, aiptek->dev.idproduct, + aiptek->features->odmCode); + out += + sprintf(out, " ModelCode: 0x%02x, FirmwareCode: 0x%04x)\n", + aiptek->features->modelCode, + aiptek->features->firmwareCode); + + out += sprintf(out, "on /dev/input/event%d\n", aiptek->dev.number); + out += sprintf(out, "pointer=%s\n", + (aiptek->pointer_mode == AIPTEK_POINTER_ONLY_MOUSE_MODE + ? "mouse" + : (aiptek->pointer_mode == + AIPTEK_POINTER_ONLY_STYLUS_MODE ? "stylus" : + "either"))); + out += + sprintf(out, "coordinate=%s\n", + (aiptek->coordinate_mode == + AIPTEK_COORDINATE_RELATIVE_MODE ? "relative" : + "absolute")); + + out += sprintf(out, "tool="); + switch (aiptek->tool_mode) { + case AIPTEK_TOOL_BUTTON_MOUSE_MODE: + out += sprintf(out, "mouse\n"); + break; + + case AIPTEK_TOOL_BUTTON_RUBBER_MODE: + out += sprintf(out, "rubber\n"); + break; + + case AIPTEK_TOOL_BUTTON_PEN_MODE: + out += sprintf(out, "pen\n"); + break; + + case AIPTEK_TOOL_BUTTON_PENCIL_MODE: + out += sprintf(out, "pencil\n"); + break; + + case AIPTEK_TOOL_BUTTON_BRUSH_MODE: + out += sprintf(out, "brush\n"); + break; + + case AIPTEK_TOOL_BUTTON_AIRBRUSH_MODE: + out += sprintf(out, "airbrush\n"); + break; } + + out += sprintf(out, "xtilt="); + if (aiptek->xTilt == AIPTEK_TILT_DISABLE) { + out += sprintf(out, "disable\n"); + } else { + out += sprintf(out, "%d\n", aiptek->xTilt); + } + + out += sprintf(out, "ytilt="); + if (aiptek->yTilt == AIPTEK_TILT_DISABLE) { + out += sprintf(out, "disable\n"); + } else { + out += sprintf(out, "%d\n", aiptek->yTilt); + } + + out += sprintf(out, "jitter=%d\n", aiptek->jitterDelay); + + out += sprintf(out, "diagnostic="); + switch (aiptek->diagnostic) { + case AIPTEK_DIAGNOSTIC_NA: + out += sprintf(out, "none\n"); + break; + case AIPTEK_DIAGNOSTIC_SENDING_RELATIVE_IN_ABSOLUTE: + out += sprintf(out, "tablet sending relative reports\n"); + break; + case AIPTEK_DIAGNOSTIC_SENDING_ABSOLUTE_IN_RELATIVE: + out += sprintf(out, "tablet sending absolute reports\n"); + break; + case AIPTEK_DIAGNOSTIC_TOOL_DISALLOWED: + out += sprintf(out, "tablet seeing reports from "); + if (aiptek->pointer_mode == AIPTEK_POINTER_ONLY_MOUSE_MODE) + out += sprintf(out, "stylus\n"); + else + out += sprintf(out, "mouse\n"); + break; + } + + out += sprintf(out, "eventsReceived=%lu\n", aiptek->eventCount); + + len = out - page; + len -= offset; + if (len < count) { + *eof = 1; + if (len <= 0) { + return 0; + } + } else { + len = count; + } + + *start = page + offset; + + return len; } -static void* +/* + * This routine permits the setting of driver parameters through a + * procfs file. Writing to the procfs file (/proc/driver/usb/aiptek), + * you can program the tablet's behavior. Parameters that can be programmed + * (and their legal values) are described at the top of this driver. + * + * + * This parser is order-insensitive, and supports one or many parameters + * to be sent in one write request. As many parameters as you may fit + * in 64 bytes; we only require that you separate them with \n's. + * + * Any command that is not understood by the parser is silently ignored. + */ +static int +aiptek_procfs_write(struct file *file, const char *buffer, unsigned long count, + void *data) +{ + char buf[64]; + char *scan; + char *keyword = NULL; + char *value = NULL; + struct aiptek *aiptek = data; + int num; + + num = (count < 64) ? count : 64; + copy_from_user(buf, buffer, num); + buf[num] = '\0'; + + scan = buf; + while (*scan) { + if (*scan == '\n' || *scan == '\0') { + if (*scan == '\n') { + *scan = '\0'; + scan++; + } + if (keyword && value) { + aiptek_procfs_parse(aiptek, keyword, value); + } + keyword = NULL; + value = NULL; + continue; + } + + if (*scan != '=' && keyword == NULL) { + keyword = scan; + } else if (*scan == '=') { + *scan++ = '\0'; + value = scan; + } + scan++; + } + // We're insensitive as to whether the buffer ended in a \n or not. + if (keyword && value) { + aiptek_procfs_parse(aiptek, keyword, value); + } + + aiptek_program_tablet(aiptek); + + return num; +} + +/* + * This routine destroys our procfs device interface. This will occur + * when you remove the driver, either through rmmod or the hotplug system. + */ +static void +destroy_procfs_file(struct aiptek *aiptek) +{ + if (aiptek->aiptekProcfsEntry) + remove_proc_entry("aiptek", aiptek->usbProcfsEntry); + if (aiptek->usbProcfsEntry) + remove_proc_entry("usb", proc_root_driver); + + aiptek->usbProcfsEntry = NULL; + aiptek->aiptekProcfsEntry = NULL; +} + +/* + * This routine builds the procfs file. The file is located at, + * procfs/driver/usb/aiptek. + */ +static void +create_procfs_file(struct aiptek *aiptek) +{ + // Make procfs/driver/usb directory + aiptek->usbProcfsEntry = create_proc_entry("usb", S_IFDIR, + proc_root_driver); + if (!aiptek->usbProcfsEntry) { + dbg("create_procfs_file failed; no procfs/driver/usb control file."); + destroy_procfs_file(aiptek); + return; + } + aiptek->usbProcfsEntry->owner = THIS_MODULE; + + // Make procfs/driver/usb/aiptek file + aiptek->aiptekProcfsEntry = create_proc_entry("aiptek", + S_IFREG | S_IRUGO | + S_IWUGO, + aiptek->usbProcfsEntry); + if (!aiptek->aiptekProcfsEntry) { + dbg("create_procfs_file failed; no procfs/driver/usb control file."); + destroy_procfs_file(aiptek); + return; + } + aiptek->aiptekProcfsEntry->owner = THIS_MODULE; + aiptek->aiptekProcfsEntry->data = aiptek; + aiptek->aiptekProcfsEntry->read_proc = aiptek_procfs_read; + aiptek->aiptekProcfsEntry->write_proc = aiptek_procfs_write; +} +#endif + +static void * aiptek_probe(struct usb_device *dev, unsigned int ifnum, const struct usb_device_id *id) { struct usb_endpoint_descriptor *endpoint; struct aiptek *aiptek; + int i; if (!(aiptek = kmalloc(sizeof (struct aiptek), GFP_KERNEL))) return NULL; @@ -231,37 +1209,78 @@ kfree(aiptek); return NULL; } + // This used to be meaningful, when we had a matrix of + // different models with statically-assigned different + // features. Now we ask the tablet about everything. + + aiptek->features = aiptek_features; + + // Reset the tablet. The tablet boots up in 'SwitchtoMouse' + // mode, which indicates relative coordinates. 'SwitchToTablet' + // infers absolute coordinates. (Ergo, mice are inferred to be + // relative-only devices, which is not true. A misnomer.) + // The routine we use, aiptek_program_tablet, has been generalized + // enough such that it's callable through the procfs interface. + // This is why we use struct aiptek throughout. + aiptek->usbdev = dev; + aiptek->ifnum = ifnum; + aiptek->pointer_mode = AIPTEK_POINTER_EITHER_MODE; + aiptek->coordinate_mode = AIPTEK_COORDINATE_ABSOLUTE_MODE; + aiptek->tool_mode = AIPTEK_TOOL_BUTTON_PEN_MODE; + aiptek->xTilt = AIPTEK_TILT_DISABLE; + aiptek->yTilt = AIPTEK_TILT_DISABLE; + aiptek->jitterDelay = AIPTEK_JITTER_DELAY_DEFAULT; + +#ifdef CONFIG_PROC_FS + create_procfs_file(aiptek); +#endif + + aiptek_program_tablet(aiptek); + + aiptek->dev.evbit[0] |= BIT(EV_KEY) + | BIT(EV_ABS) + | BIT(EV_MSC); + + aiptek->dev.absbit[0] |= BIT(ABS_X) + | BIT(ABS_Y) + | BIT(ABS_PRESSURE) + | BIT(ABS_TILT_X) + | BIT(ABS_TILT_Y) + | BIT(ABS_MISC); + + aiptek->dev.relbit[0] |= BIT(REL_X) + | BIT(REL_Y) + | BIT(REL_MISC); + + // Set the macro keys up. They are discontiguous, so it's better + // to set the bitmask this way. + + for (i = 0; i < sizeof (macroKeyEvents) / sizeof (macroKeyEvents[0]); + ++i) { + set_bit(macroKeyEvents[i], aiptek->dev.keybit); + } - // Resolution500LPI - aiptek_command(dev, ifnum, 0x18, 0x04); - - // SwitchToTablet - aiptek_command(dev, ifnum, 0x10, 0x01); - - aiptek->features = aiptek_features + id->driver_info; - - aiptek->dev.evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_MSC) | - aiptek->features->evbit; - - aiptek->dev.absbit[0] |= BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE) | - BIT(ABS_MISC) | aiptek->features->absbit; - - aiptek->dev.relbit[0] |= aiptek->features->relbit; - - aiptek->dev.keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_RIGHT) | - BIT(BTN_MIDDLE) | aiptek->features->btnbit; - - aiptek->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_PEN) | - BIT(BTN_TOOL_MOUSE) | BIT(BTN_TOUCH) | - BIT(BTN_STYLUS) | BIT(BTN_STYLUS2) | aiptek->features->digibit; + aiptek->dev.keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) + | BIT(BTN_RIGHT) + | BIT(BTN_MIDDLE); + + aiptek->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_PEN) + | BIT(BTN_TOOL_RUBBER) + | BIT(BTN_TOOL_PENCIL) + | BIT(BTN_TOOL_AIRBRUSH) + | BIT(BTN_TOOL_BRUSH) + | BIT(BTN_TOOL_MOUSE) + | BIT(BTN_TOUCH) + | BIT(BTN_STYLUS) + | BIT(BTN_STYLUS2); aiptek->dev.mscbit[0] = BIT(MSC_SERIAL); aiptek->dev.absmax[ABS_X] = aiptek->features->x_max; aiptek->dev.absmax[ABS_Y] = aiptek->features->y_max; - aiptek->dev.absmax[ABS_PRESSURE] = aiptek->features->pressure_max - - aiptek->features->pressure_min; - + aiptek->dev.absmax[ABS_PRESSURE] = aiptek->features->pressure_max; + aiptek->dev.absmax[ABS_TILT_X] = AIPTEK_TILT_MAX; + aiptek->dev.absmax[ABS_TILT_Y] = AIPTEK_TILT_MAX; aiptek->dev.absfuzz[ABS_X] = 0; aiptek->dev.absfuzz[ABS_Y] = 0; @@ -278,28 +1297,31 @@ endpoint = dev->config[0].interface[ifnum].altsetting[0].endpoint + 0; - FILL_INT_URB(aiptek->irq, - dev, - usb_rcvintpipe(dev, endpoint->bEndpointAddress), - aiptek->data, - aiptek->features->pktlen, - aiptek->features->irq, - aiptek, - endpoint->bInterval); + usb_fill_int_urb(aiptek->irq, + dev, + usb_rcvintpipe(dev, endpoint->bEndpointAddress), + aiptek->data, + aiptek->features->pktlen, + aiptek->features->irq, aiptek, endpoint->bInterval); input_register_device(&aiptek->dev); printk(KERN_INFO "input%d: %s on usb%d:%d.%d\n", - aiptek->dev.number, aiptek->features->name, dev->bus->busnum, - dev->devnum, ifnum); + aiptek->dev.number, + aiptek->features->name, dev->bus->busnum, dev->devnum, ifnum); return aiptek; } +static struct usb_driver aiptek_driver; + static void aiptek_disconnect(struct usb_device *dev, void *ptr) { struct aiptek *aiptek = ptr; +#ifdef CONFIG_PROC_FS + destroy_procfs_file(aiptek); +#endif usb_unlink_urb(aiptek->irq); input_unregister_device(&aiptek->dev); usb_free_urb(aiptek->irq); @@ -317,7 +1339,7 @@ aiptek_init(void) { usb_register(&aiptek_driver); - info(DRIVER_VERSION " " DRIVER_AUTHOR); + info(DRIVER_VERSION ": " DRIVER_AUTHOR); info(DRIVER_DESC); return 0; } diff -Nru a/drivers/usb/hid-core.c b/drivers/usb/hid-core.c --- a/drivers/usb/hid-core.c Wed Jun 18 17:34:45 2003 +++ b/drivers/usb/hid-core.c Wed Jun 18 17:34:45 2003 @@ -1140,6 +1140,15 @@ #define USB_VENDOR_ID_KBGEAR 0x084e #define USB_DEVICE_ID_KBGEAR_JAMSTUDIO 0x1001 +#define USB_VENDOR_ID_AIPTEK 0x08ca +#define USB_DEVICE_ID_AIPTEK_01 0x0001 +#define USB_DEVICE_ID_AIPTEK_10 0x0010 +#define USB_DEVICE_ID_AIPTEK_20 0x0020 +#define USB_DEVICE_ID_AIPTEK_21 0x0021 +#define USB_DEVICE_ID_AIPTEK_22 0x0022 +#define USB_DEVICE_ID_AIPTEK_23 0x0023 +#define USB_DEVICE_ID_AIPTEK_24 0x0024 + #define USB_VENDOR_ID_ATEN 0x0557 #define USB_DEVICE_ID_ATEN_UC100KM 0x2004 #define USB_DEVICE_ID_ATEN_CS124U 0x2202 @@ -1204,6 +1213,13 @@ { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS124U, HID_QUIRK_NOGET }, { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_2PORTKVM, HID_QUIRK_NOGET }, { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVM, HID_QUIRK_NOGET }, + { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_01, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_10, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_20, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_21, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_22, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_23, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_24, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_POWERMATE, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_SOUNDKNOB, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_TOPMAX, USB_DEVICE_ID_TOPMAX_COBRAPAD, HID_QUIRK_BADPAD },