/*
* Version Information
*/
-#define DRIVER_VERSION "v1.5 (May-15-2004)"
-#define DRIVER_AUTHOR "Bryan W. Headley/Chris Atenasio"
+#define DRIVER_VERSION "v2.3 (May 2, 2007)"
+#define DRIVER_AUTHOR "Bryan W. Headley/Chris Atenasio/Cedric Brun/Rene van Paassen"
#define DRIVER_DESC "Aiptek HyperPen USB Tablet Driver (Linux 2.6.x)"
/*
* (returned as Report 3 - absolute coordinates from the mouse)
*
* bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0
- * byte0 0 0 0 0 0 0 1 0
+ * byte0 0 0 0 0 0 0 1 1
* byte1 X7 X6 X5 X4 X3 X2 X1 X0
* byte2 X15 X14 X13 X12 X11 X10 X9 X8
* byte3 Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0
* (returned as Report 5 - macrokeys from the mouse)
*
* bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0
- * byte0 0 0 0 0 0 1 0 0
+ * byte0 0 0 0 0 0 1 0 1
* byte1 0 0 0 BS2 BS Tip IR DV
* byte2 0 0 0 0 0 0 1 0
* byte3 0 0 0 K4 K3 K2 K1 K0
#define AIPTEK_WHEEL_DISABLE (-10101)
/* ToolCode values, which BTW are 0x140 .. 0x14f
- * We have things set up such that if TOOL_BUTTON_FIRED_BIT is
- * not set, we'll send one instance of AIPTEK_TOOL_BUTTON_xxx.
- *
- * Whenever the user resets the value, TOOL_BUTTON_FIRED_BIT will
- * get reset.
+ * We have things set up such that if the tool button has changed,
+ * the tools get reset.
*/
-#define TOOL_BUTTON(x) ((x) & 0x14f)
-#define TOOL_BUTTON_FIRED(x) ((x) & 0x200)
-#define TOOL_BUTTON_FIRED_BIT 0x200
/* toolMode codes
*/
#define AIPTEK_TOOL_BUTTON_PEN_MODE BTN_TOOL_PEN
/* Mouse button programming
*/
-#define AIPTEK_MOUSE_LEFT_BUTTON 0x01
-#define AIPTEK_MOUSE_RIGHT_BUTTON 0x02
-#define AIPTEK_MOUSE_MIDDLE_BUTTON 0x04
+#define AIPTEK_MOUSE_LEFT_BUTTON 0x04
+#define AIPTEK_MOUSE_RIGHT_BUTTON 0x08
+#define AIPTEK_MOUSE_MIDDLE_BUTTON 0x10
/* Stylus button programming
*/
int inDelay; /* jitter: in jitter delay? */
unsigned long endDelay; /* jitter: time when delay ends */
int previousJitterable; /* jitterable prev value */
+
+ int lastMacro; /* macro key to reset */
+ int previousToolMode; /* pen, pencil, brush, etc. tool */
unsigned char *data; /* incoming packet data */
};
+static const int eventTypes[] = {
+ EV_KEY, EV_ABS, EV_REL, EV_MSC,
+};
+
+static const int absEvents[] = {
+ ABS_X, ABS_Y, ABS_PRESSURE, ABS_TILT_X, ABS_TILT_Y,
+ ABS_WHEEL, ABS_MISC,
+};
+
+static const int relEvents[] = {
+ REL_X, REL_Y, REL_WHEEL,
+};
+
+static const int buttonEvents[] = {
+ BTN_LEFT, BTN_RIGHT, BTN_MIDDLE,
+ BTN_TOOL_PEN, BTN_TOOL_RUBBER, BTN_TOOL_PENCIL, BTN_TOOL_AIRBRUSH,
+ BTN_TOOL_BRUSH, BTN_TOOL_MOUSE, BTN_TOOL_LENS, BTN_TOUCH,
+ BTN_STYLUS, BTN_STYLUS2,
+};
+
/*
* Permit easy lookup of keyboard events to send, versus
* the bitmap which comes from the tablet. This hides the
{
const struct aiptek_map *p;
+ if (str[count - 1] == '\n')
+ count--;
+
for (p = map; p->string; p++)
- if (!strncmp(str, p->string, count))
+ if (!strncmp(str, p->string, count))
return p->value;
return AIPTEK_INVALID_VALUE;
* Proximity. Why two events? I thought it interesting to know if the
* Proximity event occurred while the tablet was in absolute or relative
* mode.
+ * Update: REL_MISC proved not to be such a good idea. With REL_MISC you
+ * get an event transmitted each time. ABS_MISC works better, since it
+ * can be set and re-set. Thus, only using ABS_MISC from now on.
*
* Other tablets use the notion of a certain minimum stylus pressure
* to infer proximity. While that could have been done, that is yet
* that a non-zero value indicates that one or more
* mouse button was pressed.)
*/
- jitterable = data[5] & 0x07;
+ jitterable = data[1] & 0x07;
- left = (data[5] & aiptek->curSetting.mouseButtonLeft) != 0 ? 1 : 0;
- right = (data[5] & aiptek->curSetting.mouseButtonRight) != 0 ? 1 : 0;
- middle = (data[5] & aiptek->curSetting.mouseButtonMiddle) != 0 ? 1 : 0;
+ left = (data[1] & aiptek->curSetting.mouseButtonLeft >> 2) != 0 ? 1 : 0;
+ right = (data[1] & aiptek->curSetting.mouseButtonRight >> 2) != 0 ? 1 : 0;
+ middle = (data[1] & aiptek->curSetting.mouseButtonMiddle >> 2) != 0 ? 1 : 0;
input_report_key(inputdev, BTN_LEFT, left);
input_report_key(inputdev, BTN_MIDDLE, middle);
input_report_key(inputdev, BTN_RIGHT, right);
+
+ input_report_abs(inputdev, ABS_MISC,
+ 1 | AIPTEK_REPORT_TOOL_UNKNOWN);
input_report_rel(inputdev, REL_X, x);
input_report_rel(inputdev, REL_Y, y);
- input_report_rel(inputdev, REL_MISC, 1 | AIPTEK_REPORT_TOOL_UNKNOWN);
/* Wheel support is in the form of a single-event
* firing.
aiptek->curSetting.wheel);
aiptek->curSetting.wheel = AIPTEK_WHEEL_DISABLE;
}
+ if (aiptek->lastMacro != -1) {
+ input_report_key(inputdev,
+ macroKeyEvents[aiptek->lastMacro], 0);
+ aiptek->lastMacro = -1;
+ }
input_sync(inputdev);
}
}
y = le16_to_cpu(get_unaligned((__le16 *) (data + 3)));
z = le16_to_cpu(get_unaligned((__le16 *) (data + 6)));
- p = (data[5] & 0x01) != 0 ? 1 : 0;
- dv = (data[5] & 0x02) != 0 ? 1 : 0;
+ dv = (data[5] & 0x01) != 0 ? 1 : 0;
+ p = (data[5] & 0x02) != 0 ? 1 : 0;
tip = (data[5] & 0x04) != 0 ? 1 : 0;
/* Use jitterable to re-arrange button masks
* all 'bad' reports...
*/
if (dv != 0) {
- /* If we've not already sent a tool_button_?? code, do
- * so now. Then set FIRED_BIT so it won't be resent unless
- * the user forces FIRED_BIT off.
+ /* If the selected tool changed, reset the old
+ * tool key, and set the new one.
*/
- if (TOOL_BUTTON_FIRED
- (aiptek->curSetting.toolMode) == 0) {
+ if (aiptek->previousToolMode !=
+ aiptek->curSetting.toolMode) {
+ input_report_key(inputdev,
+ aiptek->previousToolMode, 0);
input_report_key(inputdev,
- TOOL_BUTTON(aiptek->curSetting.toolMode),
+ aiptek->curSetting.toolMode,
1);
- aiptek->curSetting.toolMode |= TOOL_BUTTON_FIRED_BIT;
+ aiptek->previousToolMode =
+ aiptek->curSetting.toolMode;
}
if (p != 0) {
}
}
input_report_abs(inputdev, ABS_MISC, p | AIPTEK_REPORT_TOOL_STYLUS);
+ if (aiptek->lastMacro != -1) {
+ input_report_key(inputdev,
+ macroKeyEvents[aiptek->lastMacro], 0);
+ aiptek->lastMacro = -1;
+ }
input_sync(inputdev);
}
}
jitterable = data[5] & 0x1c;
- p = (data[5] & 0x01) != 0 ? 1 : 0;
- dv = (data[5] & 0x02) != 0 ? 1 : 0;
+ dv = (data[5] & 0x01) != 0 ? 1 : 0;
+ p = (data[5] & 0x02) != 0 ? 1 : 0;
left = (data[5] & aiptek->curSetting.mouseButtonLeft) != 0 ? 1 : 0;
right = (data[5] & aiptek->curSetting.mouseButtonRight) != 0 ? 1 : 0;
middle = (data[5] & aiptek->curSetting.mouseButtonMiddle) != 0 ? 1 : 0;
if (dv != 0) {
- /* If we've not already sent a tool_button_?? code, do
- * so now. Then set FIRED_BIT so it won't be resent unless
- * the user forces FIRED_BIT off.
+ /* If the selected tool changed, reset the old
+ * tool key, and set the new one.
*/
- if (TOOL_BUTTON_FIRED
- (aiptek->curSetting.toolMode) == 0) {
+ if (aiptek->previousToolMode !=
+ aiptek->curSetting.toolMode) {
+ input_report_key(inputdev,
+ aiptek->previousToolMode, 0);
input_report_key(inputdev,
- TOOL_BUTTON(aiptek->curSetting.toolMode),
+ aiptek->curSetting.toolMode,
1);
- aiptek->curSetting.toolMode |= TOOL_BUTTON_FIRED_BIT;
+ aiptek->previousToolMode =
+ aiptek->curSetting.toolMode;
}
if (p != 0) {
aiptek->curSetting.wheel = AIPTEK_WHEEL_DISABLE;
}
}
- input_report_rel(inputdev, REL_MISC, p | AIPTEK_REPORT_TOOL_MOUSE);
+ input_report_abs(inputdev, ABS_MISC, p | AIPTEK_REPORT_TOOL_MOUSE);
+ if (aiptek->lastMacro != -1) {
+ input_report_key(inputdev,
+ macroKeyEvents[aiptek->lastMacro], 0);
+ aiptek->lastMacro = -1;
+ }
input_sync(inputdev);
}
}
else if (data[0] == 4) {
jitterable = data[1] & 0x18;
- p = (data[1] & 0x01) != 0 ? 1 : 0;
- dv = (data[1] & 0x02) != 0 ? 1 : 0;
+ dv = (data[1] & 0x01) != 0 ? 1 : 0;
+ p = (data[1] & 0x02) != 0 ? 1 : 0;
tip = (data[1] & 0x04) != 0 ? 1 : 0;
bs = (data[1] & aiptek->curSetting.stylusButtonLower) != 0 ? 1 : 0;
pck = (data[1] & aiptek->curSetting.stylusButtonUpper) != 0 ? 1 : 0;
- macro = data[3];
+ macro = dv && p && tip && !(data[3] & 1) ? (data[3] >> 1) : -1;
z = le16_to_cpu(get_unaligned((__le16 *) (data + 4)));
- if (dv != 0) {
- /* If we've not already sent a tool_button_?? code, do
- * so now. Then set FIRED_BIT so it won't be resent unless
- * the user forces FIRED_BIT off.
+ if (dv) {
+ /* If the selected tool changed, reset the old
+ * tool key, and set the new one.
*/
- if (TOOL_BUTTON_FIRED(aiptek->curSetting.toolMode) == 0) {
+ if (aiptek->previousToolMode !=
+ aiptek->curSetting.toolMode) {
+ input_report_key(inputdev,
+ aiptek->previousToolMode, 0);
input_report_key(inputdev,
- TOOL_BUTTON(aiptek->curSetting.toolMode),
+ aiptek->curSetting.toolMode,
1);
- aiptek->curSetting.toolMode |= TOOL_BUTTON_FIRED_BIT;
+ aiptek->previousToolMode =
+ aiptek->curSetting.toolMode;
}
+ }
- if (p != 0) {
- input_report_key(inputdev, BTN_TOUCH, tip);
- input_report_key(inputdev, BTN_STYLUS, bs);
- input_report_key(inputdev, BTN_STYLUS2, pck);
- input_report_abs(inputdev, ABS_PRESSURE, z);
- }
+ if (aiptek->lastMacro != -1 && aiptek->lastMacro != macro) {
+ input_report_key(inputdev, macroKeyEvents[aiptek->lastMacro], 0);
+ aiptek->lastMacro = -1;
+ }
- /* For safety, we're sending key 'break' codes for the
- * neighboring macro keys.
- */
- if (macro > 0) {
- input_report_key(inputdev,
- macroKeyEvents[macro - 1], 0);
- }
- if (macro < 25) {
- input_report_key(inputdev,
- macroKeyEvents[macro + 1], 0);
- }
- input_report_key(inputdev, macroKeyEvents[macro], p);
- input_report_abs(inputdev, ABS_MISC,
- p | AIPTEK_REPORT_TOOL_STYLUS);
- input_sync(inputdev);
+ if (macro != -1 && macro != aiptek->lastMacro) {
+ input_report_key(inputdev, macroKeyEvents[macro], 1);
+ aiptek->lastMacro = macro;
}
+ input_report_abs(inputdev, ABS_MISC,
+ p | AIPTEK_REPORT_TOOL_STYLUS);
+ input_sync(inputdev);
}
/* Report 5s come from the macro keys when pressed by mouse
*/
else if (data[0] == 5) {
jitterable = data[1] & 0x1c;
- p = (data[1] & 0x01) != 0 ? 1 : 0;
- dv = (data[1] & 0x02) != 0 ? 1 : 0;
+ dv = (data[1] & 0x01) != 0 ? 1 : 0;
+ p = (data[1] & 0x02) != 0 ? 1 : 0;
left = (data[1]& aiptek->curSetting.mouseButtonLeft) != 0 ? 1 : 0;
right = (data[1] & aiptek->curSetting.mouseButtonRight) != 0 ? 1 : 0;
middle = (data[1] & aiptek->curSetting.mouseButtonMiddle) != 0 ? 1 : 0;
- macro = data[3];
+ macro = dv && p && left && !(data[3] & 1) ? (data[3] >> 1) : 0;
- if (dv != 0) {
- /* If we've not already sent a tool_button_?? code, do
- * so now. Then set FIRED_BIT so it won't be resent unless
- * the user forces FIRED_BIT off.
+ if (dv) {
+ /* If the selected tool changed, reset the old
+ * tool key, and set the new one.
*/
- if (TOOL_BUTTON_FIRED(aiptek->curSetting.toolMode) == 0) {
- input_report_key(inputdev,
- TOOL_BUTTON(aiptek->curSetting.toolMode),
- 1);
- aiptek->curSetting.toolMode |= TOOL_BUTTON_FIRED_BIT;
- }
-
- if (p != 0) {
- input_report_key(inputdev, BTN_LEFT, left);
- input_report_key(inputdev, BTN_MIDDLE, middle);
- input_report_key(inputdev, BTN_RIGHT, right);
+ if (aiptek->previousToolMode !=
+ aiptek->curSetting.toolMode) {
+ input_report_key(inputdev,
+ aiptek->previousToolMode, 0);
+ input_report_key(inputdev,
+ aiptek->curSetting.toolMode, 1);
+ aiptek->previousToolMode = aiptek->curSetting.toolMode;
}
+ }
- /* For safety, we're sending key 'break' codes for the
- * neighboring macro keys.
- */
- if (macro > 0) {
- input_report_key(inputdev,
- macroKeyEvents[macro - 1], 0);
- }
- if (macro < 25) {
- input_report_key(inputdev,
- macroKeyEvents[macro + 1], 0);
- }
+ if (aiptek->lastMacro != -1 && aiptek->lastMacro != macro) {
+ input_report_key(inputdev, macroKeyEvents[aiptek->lastMacro], 0);
+ aiptek->lastMacro = -1;
+ }
+ if (macro != -1 && macro != aiptek->lastMacro) {
input_report_key(inputdev, macroKeyEvents[macro], 1);
- input_report_rel(inputdev, ABS_MISC,
- p | AIPTEK_REPORT_TOOL_MOUSE);
- input_sync(inputdev);
+ aiptek->lastMacro = macro;
}
+
+ input_report_abs(inputdev, ABS_MISC,
+ p | AIPTEK_REPORT_TOOL_MOUSE);
+ input_sync(inputdev);
}
/* We have no idea which tool can generate a report 6. Theoretically,
* neither need to, having been given reports 4 & 5 for such use.
0);
}
- /* If we've not already sent a tool_button_?? code, do
- * so now. Then set FIRED_BIT so it won't be resent unless
- * the user forces FIRED_BIT off.
- */
- if (TOOL_BUTTON_FIRED(aiptek->curSetting.toolMode) == 0) {
+ /* If the selected tool changed, reset the old
+ tool key, and set the new one.
+ */
+ if (aiptek->previousToolMode !=
+ aiptek->curSetting.toolMode) {
+ input_report_key(inputdev,
+ aiptek->previousToolMode, 0);
input_report_key(inputdev,
- TOOL_BUTTON(aiptek->curSetting.
- toolMode), 1);
- aiptek->curSetting.toolMode |= TOOL_BUTTON_FIRED_BIT;
+ aiptek->curSetting.toolMode,
+ 1);
+ aiptek->previousToolMode =
+ aiptek->curSetting.toolMode;
}
input_report_key(inputdev, macroKeyEvents[macro], 1);
aiptek = kzalloc(sizeof(struct aiptek), GFP_KERNEL);
inputdev = input_allocate_device();
- if (!aiptek || !inputdev)
+ if (!aiptek || !inputdev) {
+ warn("aiptek: cannot allocate memory or input device");
goto fail1;
+ }
aiptek->data = usb_buffer_alloc(usbdev, AIPTEK_PACKET_LENGTH,
GFP_ATOMIC, &aiptek->data_dma);
- if (!aiptek->data)
+ if (!aiptek->data) {
+ warn("aiptek: cannot allocate usb buffer");
goto fail1;
+ }
aiptek->urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!aiptek->urb)
+ if (!aiptek->urb) {
+ warn("aiptek: cannot allocate urb");
goto fail2;
+ }
aiptek->inputdev = inputdev;
aiptek->usbdev = usbdev;
aiptek->inDelay = 0;
aiptek->endDelay = 0;
aiptek->previousJitterable = 0;
+ aiptek->lastMacro = -1;
/* Set up the curSettings struct. Said struct contains the current
* programmable parameters. The newSetting struct contains changes
/* Now program the capacities of the tablet, in terms of being
* an input device.
*/
- inputdev->evbit[0] |= BIT(EV_KEY)
- | BIT(EV_ABS)
- | BIT(EV_REL)
- | BIT(EV_MSC);
-
- inputdev->absbit[0] |= BIT(ABS_MISC);
+ for (i = 0; i < ARRAY_SIZE(eventTypes); ++i)
+ __set_bit(eventTypes[i], inputdev->evbit);
- inputdev->relbit[0] |=
- (BIT(REL_X) | BIT(REL_Y) | BIT(REL_WHEEL) | BIT(REL_MISC));
+ for (i = 0; i < ARRAY_SIZE(absEvents); ++i)
+ __set_bit(absEvents[i], inputdev->absbit);
- inputdev->keybit[LONG(BTN_LEFT)] |=
- (BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE));
+ for (i = 0; i < ARRAY_SIZE(relEvents); ++i)
+ __set_bit(relEvents[i], inputdev->relbit);
- inputdev->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_TOOL_LENS) |
- BIT(BTN_TOUCH) | BIT(BTN_STYLUS) | BIT(BTN_STYLUS2));
+ __set_bit(MSC_SERIAL, inputdev->mscbit);
- inputdev->mscbit[0] = BIT(MSC_SERIAL);
+ /* Set up key and button codes */
+ for (i = 0; i < ARRAY_SIZE(buttonEvents); ++i)
+ __set_bit(buttonEvents[i], inputdev->keybit);
- /* Programming the tablet macro keys needs to be done with a for loop
- * as the keycodes are discontiguous.
- */
for (i = 0; i < ARRAY_SIZE(macroKeyEvents); ++i)
- set_bit(macroKeyEvents[i], inputdev->keybit);
+ __set_bit(macroKeyEvents[i], inputdev->keybit);
/*
* Program the input device coordinate capacities. We do not yet
}
}
+ /* Murphy says that some day someone will have a tablet that fails the
+ above test. That's you, Frederic Rodrigo */
+ if (i == ARRAY_SIZE(speeds)) {
+ info("input: Aiptek tried all speeds, no sane response");
+ goto fail2;
+ }
+
/* Associate this driver's struct with the usb interface.
*/
usb_set_intfdata(intf, aiptek);
/* Set up the sysfs files
*/
err = sysfs_create_group(&intf->dev.kobj, &aiptek_attribute_group);
- if (err)
+ if (err) {
+ warn("aiptek: cannot create sysfs group err: %d", err);
goto fail3;
+ }
/* Register the tablet as an Input Device
*/
err = input_register_device(aiptek->inputdev);
- if (err)
+ if (err) {
+ warn("aiptek: input_register_device returned err: %d", err);
goto fail4;
-
+ }
return 0;
fail4: sysfs_remove_group(&intf->dev.kobj, &aiptek_attribute_group);