2010-09-01 01:56:32

by Chase Douglas

[permalink] [raw]
Subject: [PATCH 0/7 v3] HID: magicmouse: fixes and support for the Magic Trackpad (v3)

The following patches are a reworking of my previous submission to
incorporate all the comments to date. The only patch of contention, removing
the fuzz factor, has been removed for now.

Thanks,

-- Chase


2010-09-01 01:56:41

by Chase Douglas

[permalink] [raw]
Subject: [PATCH 4/7 v3] HID: magicmouse: simplify touch down logic

From: Chase Douglas <[email protected]>

For the MT protocol, we need to properly keep track of each down touch.
This change simplifies the logic, and should make things easier when
support for the Magic Trackpad is added.

Signed-off-by: Chase Douglas <[email protected]>
---
drivers/hid/hid-magicmouse.c | 27 +++++++++------------------
1 files changed, 9 insertions(+), 18 deletions(-)

diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c
index 2ee59a8..0fbca59 100644
--- a/drivers/hid/hid-magicmouse.c
+++ b/drivers/hid/hid-magicmouse.c
@@ -98,7 +98,6 @@ struct magicmouse_sc {
short scroll_x;
short scroll_y;
u8 size;
- u8 down;
} touches[16];
int tracking_ids[16];
};
@@ -227,8 +226,6 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda

/* Generate the input events for this touch. */
if (report_touches && down) {
- msc->touches[id].down = 1;
-
input_report_abs(input, ABS_MT_TRACKING_ID, id);
input_report_abs(input, ABS_MT_TOUCH_MAJOR, touch_major);
input_report_abs(input, ABS_MT_TOUCH_MINOR, touch_minor);
@@ -241,6 +238,9 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda

input_mt_sync(input);
}
+
+ if (down)
+ msc->ntouches++;
}

static int magicmouse_raw_event(struct hid_device *hdev,
@@ -248,7 +248,7 @@ static int magicmouse_raw_event(struct hid_device *hdev,
{
struct magicmouse_sc *msc = hid_get_drvdata(hdev);
struct input_dev *input = msc->input;
- int x, y, ts, ii, clicks, last_up;
+ int x, y, ts, ii, clicks, npoints;

switch (data[0]) {
case 0x10:
@@ -265,22 +265,13 @@ static int magicmouse_raw_event(struct hid_device *hdev,
ts = data[3] >> 6 | data[4] << 2 | data[5] << 10;
msc->delta_time = (ts - msc->last_timestamp) & 0x3ffff;
msc->last_timestamp = ts;
- msc->ntouches = (size - 6) / 8;
- for (ii = 0; ii < msc->ntouches; ii++)
+ npoints = (size - 6) / 8;
+ msc->ntouches = 0;
+ for (ii = 0; ii < npoints; ii++)
magicmouse_emit_touch(msc, ii, data + ii * 8 + 6);

- if (report_touches) {
- last_up = 1;
- for (ii = 0; ii < ARRAY_SIZE(msc->touches); ii++) {
- if (msc->touches[ii].down) {
- last_up = 0;
- msc->touches[ii].down = 0;
- }
- }
- if (last_up) {
- input_mt_sync(input);
- }
- }
+ if (report_touches && msc->ntouches == 0)
+ input_mt_sync(input);

/* When emulating three-button mode, it is important
* to have the current touch information before
--
1.7.1

2010-09-01 01:56:50

by Chase Douglas

[permalink] [raw]
Subject: [PATCH 7/7 v3] HID: magicmouse: Adjust major / minor axes to scale

From: Henrik Rydberg <[email protected]>

By visual inspection, the reported touch_major and touch_minor axes
are roughly a factor of four too small. The factor is approximate,
since the protocol is not known and the HID report encodes touch size
with fewer bits than positions. This patch scales the reported values
by a factor of four.

Signed-off-by: Henrik Rydberg <[email protected]>
Signed-off-by: Chase Douglas <[email protected]>
Acked-by: Michael Poole <[email protected]>
---
drivers/hid/hid-magicmouse.c | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c
index 6f13e56..0792b16 100644
--- a/drivers/hid/hid-magicmouse.c
+++ b/drivers/hid/hid-magicmouse.c
@@ -254,8 +254,8 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda
/* Generate the input events for this touch. */
if (report_touches && down) {
input_report_abs(input, ABS_MT_TRACKING_ID, id);
- input_report_abs(input, ABS_MT_TOUCH_MAJOR, touch_major);
- input_report_abs(input, ABS_MT_TOUCH_MINOR, touch_minor);
+ input_report_abs(input, ABS_MT_TOUCH_MAJOR, touch_major << 2);
+ input_report_abs(input, ABS_MT_TOUCH_MINOR, touch_minor << 2);
input_report_abs(input, ABS_MT_ORIENTATION, orientation);
input_report_abs(input, ABS_MT_POSITION_X, x);
input_report_abs(input, ABS_MT_POSITION_Y, y);
--
1.7.1

2010-09-01 01:56:47

by Chase Douglas

[permalink] [raw]
Subject: [PATCH 5/7 v3] HID: magicmouse: remove timestamp logic

From: Chase Douglas <[email protected]>

The timestamps from the device are currently stored in the private data
structure. These aren't used, so remove them. I've left a comment
detailing the protocol for future reference.

Signed-off-by: Chase Douglas <[email protected]>
---
drivers/hid/hid-magicmouse.c | 18 +++++++-----------
1 files changed, 7 insertions(+), 11 deletions(-)

diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c
index 0fbca59..9fc272f 100644
--- a/drivers/hid/hid-magicmouse.c
+++ b/drivers/hid/hid-magicmouse.c
@@ -71,11 +71,6 @@ MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state fie
* struct magicmouse_sc - Tracks Magic Mouse-specific data.
* @input: Input device through which we report events.
* @quirks: Currently unused.
- * @last_timestamp: Timestamp from most recent (18-bit) touch report
- * (units of milliseconds over short windows, but seems to
- * increase faster when there are no touches).
- * @delta_time: 18-bit difference between the two most recent touch
- * reports from the mouse.
* @ntouches: Number of touches in most recent touch report.
* @scroll_accel: Number of consecutive scroll motions.
* @scroll_jiffies: Time of last scroll motion.
@@ -86,8 +81,6 @@ struct magicmouse_sc {
struct input_dev *input;
unsigned long quirks;

- int last_timestamp;
- int delta_time;
int ntouches;
int scroll_accel;
unsigned long scroll_jiffies;
@@ -248,7 +241,7 @@ static int magicmouse_raw_event(struct hid_device *hdev,
{
struct magicmouse_sc *msc = hid_get_drvdata(hdev);
struct input_dev *input = msc->input;
- int x, y, ts, ii, clicks, npoints;
+ int x, y, ii, clicks, npoints;

switch (data[0]) {
case 0x10:
@@ -262,9 +255,6 @@ static int magicmouse_raw_event(struct hid_device *hdev,
/* Expect six bytes of prefix, and N*8 bytes of touch data. */
if (size < 6 || ((size - 6) % 8) != 0)
return 0;
- ts = data[3] >> 6 | data[4] << 2 | data[5] << 10;
- msc->delta_time = (ts - msc->last_timestamp) & 0x3ffff;
- msc->last_timestamp = ts;
npoints = (size - 6) / 8;
msc->ntouches = 0;
for (ii = 0; ii < npoints; ii++)
@@ -280,6 +270,12 @@ static int magicmouse_raw_event(struct hid_device *hdev,
x = (int)(((data[3] & 0x0c) << 28) | (data[1] << 22)) >> 22;
y = (int)(((data[3] & 0x30) << 26) | (data[2] << 22)) >> 22;
clicks = data[3];
+
+ /* The following bits provide a device specific timestamp. They
+ * are unused here.
+ *
+ * ts = data[3] >> 6 | data[4] << 2 | data[5] << 10;
+ */
break;
case 0x20: /* Theoretically battery status (0-100), but I have
* never seen it -- maybe it is only upon request.
--
1.7.1

2010-09-01 01:56:39

by Chase Douglas

[permalink] [raw]
Subject: [PATCH 1/7 v3] HID: magicmouse: don't allow hidinput to initialize the device

From: Chase Douglas <[email protected]>

The driver listens only for raw events from the device. If we allow
the hidinput layer to initialize, we can hit NULL pointer dereferences
in the hidinput layer because disconnecting only removes the hidinput
devices from the hid device while leaving the hid fields configured.

Signed-off-by: Chase Douglas <[email protected]>
Acked-by: Michael Poole <[email protected]>
---
drivers/hid/hid-magicmouse.c | 13 +++++++++----
1 files changed, 9 insertions(+), 4 deletions(-)

diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c
index 319b0e5..d38b529 100644
--- a/drivers/hid/hid-magicmouse.c
+++ b/drivers/hid/hid-magicmouse.c
@@ -404,15 +404,20 @@ static int magicmouse_probe(struct hid_device *hdev,
goto err_free;
}

- ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+ /* When registering a hid device, one of hidinput, hidraw, or hiddev
+ * subsystems must claim the device. We are bypassing hidinput due to
+ * our raw event processing, and hidraw and hiddev may not claim the
+ * device. We get around this by telling hid_hw_start that input has
+ * claimed the device already, and then flipping the bit back.
+ */
+ hdev->claimed = HID_CLAIMED_INPUT;
+ ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_HIDINPUT);
+ hdev->claimed &= ~HID_CLAIMED_INPUT;
if (ret) {
dev_err(&hdev->dev, "magicmouse hw start failed\n");
goto err_free;
}

- /* we are handling the input ourselves */
- hidinput_disconnect(hdev);
-
report = hid_register_report(hdev, HID_INPUT_REPORT, TOUCH_REPORT_ID);
if (!report) {
dev_err(&hdev->dev, "unable to register touch report\n");
--
1.7.1

2010-09-01 01:57:35

by Chase Douglas

[permalink] [raw]
Subject: [PATCH 6/7 v3] HID: magicmouse: enable Magic Trackpad support

The trackpad speaks a similar, but different, protocol from the magic
mouse. However, only small code tweaks here and there are needed to make
basic multitouch work.

Extra logic is required for single-touch emulation of the touchpad. The
changes made here take the approach that only one finger may emulate the
single pointer when multiple fingers have touched the screen. Once that
finger is raised, all touches must be raised before any further single
touch events can be sent.

Sometimes the magic trackpad sends two distinct touch reports as one big
report. Simply splitting the packet in two and resending them through
magicmouse_raw_event ensures they are handled properly.

I also added myself to the copyright statement.

Signed-off-by: Chase Douglas <[email protected]>
Acked-by: Michael Poole <[email protected]>
---
drivers/hid/hid-core.c | 1 +
drivers/hid/hid-ids.h | 1 +
drivers/hid/hid-magicmouse.c | 192 +++++++++++++++++++++++++++++++++---------
3 files changed, 155 insertions(+), 39 deletions(-)

diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 616bddc..5226fd1 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1248,6 +1248,7 @@ static const struct hid_device_id hid_blacklist[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICMOUSE) },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICTRACKPAD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ISO) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ANSI) },
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 7b3ca1d..9204cac 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -63,6 +63,7 @@
#define USB_VENDOR_ID_APPLE 0x05ac
#define USB_DEVICE_ID_APPLE_MIGHTYMOUSE 0x0304
#define USB_DEVICE_ID_APPLE_MAGICMOUSE 0x030d
+#define USB_DEVICE_ID_APPLE_MAGICTRACKPAD 0x030e
#define USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI 0x020e
#define USB_DEVICE_ID_APPLE_FOUNTAIN_ISO 0x020f
#define USB_DEVICE_ID_APPLE_GEYSER_ANSI 0x0214
diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c
index 9fc272f..6f13e56 100644
--- a/drivers/hid/hid-magicmouse.c
+++ b/drivers/hid/hid-magicmouse.c
@@ -2,6 +2,7 @@
* Apple "Magic" Wireless Mouse driver
*
* Copyright (c) 2010 Michael Poole <[email protected]>
+ * Copyright (c) 2010 Chase Douglas <[email protected]>
*/

/*
@@ -53,7 +54,9 @@ static bool report_undeciphered;
module_param(report_undeciphered, bool, 0644);
MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state field using a MSC_RAW event");

-#define TOUCH_REPORT_ID 0x29
+#define TRACKPAD_REPORT_ID 0x28
+#define MOUSE_REPORT_ID 0x29
+#define DOUBLE_REPORT_ID 0xf7
/* These definitions are not precise, but they're close enough. (Bits
* 0x03 seem to indicate the aspect ratio of the touch, bits 0x70 seem
* to be some kind of bit mask -- 0x20 may be a near-field reading,
@@ -67,6 +70,15 @@ MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state fie

#define SCROLL_ACCEL_DEFAULT 7

+/* Single touch emulation should only begin when no touches are currently down.
+ * This is true when single_touch_id is equal to NO_TOUCHES. If multiple touches
+ * are down and the touch providing for single touch emulation is lifted,
+ * single_touch_id is equal to SINGLE_TOUCH_UP. While single touch emulation is
+ * occuring, single_touch_id corresponds with the tracking id of the touch used.
+ */
+#define NO_TOUCHES -1
+#define SINGLE_TOUCH_UP -2
+
/**
* struct magicmouse_sc - Tracks Magic Mouse-specific data.
* @input: Input device through which we report events.
@@ -93,6 +105,7 @@ struct magicmouse_sc {
u8 size;
} touches[16];
int tracking_ids[16];
+ int single_touch_id;
};

static int magicmouse_firm_touch(struct magicmouse_sc *msc)
@@ -158,15 +171,29 @@ static void magicmouse_emit_buttons(struct magicmouse_sc *msc, int state)
static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tdata)
{
struct input_dev *input = msc->input;
- int id = (tdata[6] << 2 | tdata[5] >> 6) & 0xf;
- int x = (tdata[1] << 28 | tdata[0] << 20) >> 20;
- int y = -((tdata[2] << 24 | tdata[1] << 16) >> 20);
- int size = tdata[5] & 0x3f;
- int orientation = (tdata[6] >> 2) - 32;
- int touch_major = tdata[3];
- int touch_minor = tdata[4];
- int state = tdata[7] & TOUCH_STATE_MASK;
- int down = state != TOUCH_STATE_NONE;
+ int id, x, y, size, orientation, touch_major, touch_minor, state, down;
+
+ if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) {
+ id = (tdata[6] << 2 | tdata[5] >> 6) & 0xf;
+ x = (tdata[1] << 28 | tdata[0] << 20) >> 20;
+ y = -((tdata[2] << 24 | tdata[1] << 16) >> 20);
+ size = tdata[5] & 0x3f;
+ orientation = (tdata[6] >> 2) - 32;
+ touch_major = tdata[3];
+ touch_minor = tdata[4];
+ state = tdata[7] & TOUCH_STATE_MASK;
+ down = state != TOUCH_STATE_NONE;
+ } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
+ id = (tdata[7] << 2 | tdata[6] >> 6) & 0xf;
+ x = (tdata[1] << 27 | tdata[0] << 19) >> 19;
+ y = -((tdata[3] << 30 | tdata[2] << 22 | tdata[1] << 14) >> 19);
+ size = tdata[6] & 0x3f;
+ orientation = (tdata[7] >> 2) - 32;
+ touch_major = tdata[4];
+ touch_minor = tdata[5];
+ state = tdata[8] & TOUCH_STATE_MASK;
+ down = state != TOUCH_STATE_NONE;
+ }

/* Store tracking ID and other fields. */
msc->tracking_ids[raw_id] = id;
@@ -217,6 +244,13 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda
}
}

+ if (down) {
+ msc->ntouches++;
+ if (msc->single_touch_id == NO_TOUCHES)
+ msc->single_touch_id = id;
+ } else if (msc->single_touch_id == id)
+ msc->single_touch_id = SINGLE_TOUCH_UP;
+
/* Generate the input events for this touch. */
if (report_touches && down) {
input_report_abs(input, ABS_MT_TRACKING_ID, id);
@@ -226,14 +260,15 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda
input_report_abs(input, ABS_MT_POSITION_X, x);
input_report_abs(input, ABS_MT_POSITION_Y, y);

- if (report_undeciphered)
- input_event(input, EV_MSC, MSC_RAW, tdata[7]);
+ if (report_undeciphered) {
+ if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE)
+ input_event(input, EV_MSC, MSC_RAW, tdata[7]);
+ else /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
+ input_event(input, EV_MSC, MSC_RAW, tdata[8]);
+ }

input_mt_sync(input);
}
-
- if (down)
- msc->ntouches++;
}

static int magicmouse_raw_event(struct hid_device *hdev,
@@ -241,7 +276,7 @@ static int magicmouse_raw_event(struct hid_device *hdev,
{
struct magicmouse_sc *msc = hid_get_drvdata(hdev);
struct input_dev *input = msc->input;
- int x, y, ii, clicks, npoints;
+ int x = 0, y = 0, ii, clicks = 0, npoints;

switch (data[0]) {
case 0x10:
@@ -251,7 +286,30 @@ static int magicmouse_raw_event(struct hid_device *hdev,
y = (__s16)(data[4] | data[5] << 8);
clicks = data[1];
break;
- case TOUCH_REPORT_ID:
+ case TRACKPAD_REPORT_ID:
+ /* Expect four bytes of prefix, and N*9 bytes of touch data. */
+ if (size < 4 || ((size - 4) % 9) != 0)
+ return 0;
+ npoints = (size - 4) / 9;
+ msc->ntouches = 0;
+ for (ii = 0; ii < npoints; ii++)
+ magicmouse_emit_touch(msc, ii, data + ii * 9 + 4);
+
+ /* We don't need an MT sync here because trackpad emits a
+ * BTN_TOUCH event in a new frame when all touches are released.
+ */
+ if (msc->ntouches == 0)
+ msc->single_touch_id = NO_TOUCHES;
+
+ clicks = data[1];
+
+ /* The following bits provide a device specific timestamp. They
+ * are unused here.
+ *
+ * ts = data[1] >> 6 | data[2] << 2 | data[3] << 10;
+ */
+ break;
+ case MOUSE_REPORT_ID:
/* Expect six bytes of prefix, and N*8 bytes of touch data. */
if (size < 6 || ((size - 6) % 8) != 0)
return 0;
@@ -277,6 +335,14 @@ static int magicmouse_raw_event(struct hid_device *hdev,
* ts = data[3] >> 6 | data[4] << 2 | data[5] << 10;
*/
break;
+ case DOUBLE_REPORT_ID:
+ /* Sometimes the trackpad sends two touch reports in one
+ * packet.
+ */
+ magicmouse_raw_event(hdev, report, data + 2, data[1]);
+ magicmouse_raw_event(hdev, report, data + 2 + data[1],
+ size - 2 - data[1]);
+ break;
case 0x20: /* Theoretically battery status (0-100), but I have
* never seen it -- maybe it is only upon request.
*/
@@ -288,9 +354,25 @@ static int magicmouse_raw_event(struct hid_device *hdev,
return 0;
}

- magicmouse_emit_buttons(msc, clicks & 3);
- input_report_rel(input, REL_X, x);
- input_report_rel(input, REL_Y, y);
+ if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) {
+ magicmouse_emit_buttons(msc, clicks & 3);
+ input_report_rel(input, REL_X, x);
+ input_report_rel(input, REL_Y, y);
+ } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
+ input_report_key(input, BTN_MOUSE, clicks & 1);
+ input_report_key(input, BTN_TOUCH, msc->ntouches > 0);
+ input_report_key(input, BTN_TOOL_FINGER, msc->ntouches == 1);
+ input_report_key(input, BTN_TOOL_DOUBLETAP, msc->ntouches == 2);
+ input_report_key(input, BTN_TOOL_TRIPLETAP, msc->ntouches == 3);
+ input_report_key(input, BTN_TOOL_QUADTAP, msc->ntouches == 4);
+ if (msc->single_touch_id >= 0) {
+ input_report_abs(input, ABS_X,
+ msc->touches[msc->single_touch_id].x);
+ input_report_abs(input, ABS_Y,
+ msc->touches[msc->single_touch_id].y);
+ }
+ }
+
input_sync(input);
return 1;
}
@@ -326,18 +408,27 @@ static void magicmouse_setup_input(struct input_dev *input, struct hid_device *h
input->dev.parent = hdev->dev.parent;

__set_bit(EV_KEY, input->evbit);
- __set_bit(BTN_LEFT, input->keybit);
- __set_bit(BTN_RIGHT, input->keybit);
- if (emulate_3button)
- __set_bit(BTN_MIDDLE, input->keybit);
- __set_bit(BTN_TOOL_FINGER, input->keybit);
-
- __set_bit(EV_REL, input->evbit);
- __set_bit(REL_X, input->relbit);
- __set_bit(REL_Y, input->relbit);
- if (emulate_scroll_wheel) {
- __set_bit(REL_WHEEL, input->relbit);
- __set_bit(REL_HWHEEL, input->relbit);
+
+ if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) {
+ __set_bit(BTN_LEFT, input->keybit);
+ __set_bit(BTN_RIGHT, input->keybit);
+ if (emulate_3button)
+ __set_bit(BTN_MIDDLE, input->keybit);
+
+ __set_bit(EV_REL, input->evbit);
+ __set_bit(REL_X, input->relbit);
+ __set_bit(REL_Y, input->relbit);
+ if (emulate_scroll_wheel) {
+ __set_bit(REL_WHEEL, input->relbit);
+ __set_bit(REL_HWHEEL, input->relbit);
+ }
+ } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
+ __set_bit(BTN_MOUSE, input->keybit);
+ __set_bit(BTN_TOOL_FINGER, input->keybit);
+ __set_bit(BTN_TOOL_DOUBLETAP, input->keybit);
+ __set_bit(BTN_TOOL_TRIPLETAP, input->keybit);
+ __set_bit(BTN_TOOL_QUADTAP, input->keybit);
+ __set_bit(BTN_TOUCH, input->keybit);
}

if (report_touches) {
@@ -347,16 +438,26 @@ static void magicmouse_setup_input(struct input_dev *input, struct hid_device *h
input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 255, 4, 0);
input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 255, 4, 0);
input_set_abs_params(input, ABS_MT_ORIENTATION, -32, 31, 1, 0);
- input_set_abs_params(input, ABS_MT_POSITION_X, -1100, 1358,
- 4, 0);
+
/* Note: Touch Y position from the device is inverted relative
* to how pointer motion is reported (and relative to how USB
* HID recommends the coordinates work). This driver keeps
* the origin at the same position, and just uses the additive
* inverse of the reported Y.
*/
- input_set_abs_params(input, ABS_MT_POSITION_Y, -1589, 2047,
- 4, 0);
+ if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) {
+ input_set_abs_params(input, ABS_MT_POSITION_X, -1100,
+ 1358, 4, 0);
+ input_set_abs_params(input, ABS_MT_POSITION_Y, -1589,
+ 2047, 4, 0);
+ } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
+ input_set_abs_params(input, ABS_X, -2909, 3167, 4, 0);
+ input_set_abs_params(input, ABS_Y, -2456, 2565, 4, 0);
+ input_set_abs_params(input, ABS_MT_POSITION_X, -2909,
+ 3167, 4, 0);
+ input_set_abs_params(input, ABS_MT_POSITION_Y, -2456,
+ 2565, 4, 0);
+ }
}

if (report_undeciphered) {
@@ -385,6 +486,8 @@ static int magicmouse_probe(struct hid_device *hdev,
msc->quirks = id->driver_data;
hid_set_drvdata(hdev, msc);

+ msc->single_touch_id = NO_TOUCHES;
+
ret = hid_parse(hdev);
if (ret) {
dev_err(&hdev->dev, "magicmouse hid parse failed\n");
@@ -405,7 +508,16 @@ static int magicmouse_probe(struct hid_device *hdev,
goto err_free;
}

- report = hid_register_report(hdev, HID_INPUT_REPORT, TOUCH_REPORT_ID);
+ if (id->product == USB_DEVICE_ID_APPLE_MAGICMOUSE)
+ report = hid_register_report(hdev, HID_INPUT_REPORT,
+ MOUSE_REPORT_ID);
+ else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
+ report = hid_register_report(hdev, HID_INPUT_REPORT,
+ TRACKPAD_REPORT_ID);
+ report = hid_register_report(hdev, HID_INPUT_REPORT,
+ DOUBLE_REPORT_ID);
+ }
+
if (!report) {
dev_err(&hdev->dev, "unable to register touch report\n");
ret = -ENOMEM;
@@ -456,8 +568,10 @@ static void magicmouse_remove(struct hid_device *hdev)
}

static const struct hid_device_id magic_mice[] = {
- { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICMOUSE),
- .driver_data = 0 },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
+ USB_DEVICE_ID_APPLE_MAGICMOUSE), .driver_data = 0 },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
+ USB_DEVICE_ID_APPLE_MAGICTRACKPAD), .driver_data = 0 },
{ }
};
MODULE_DEVICE_TABLE(hid, magic_mice);
--
1.7.1

2010-09-01 01:56:37

by Chase Douglas

[permalink] [raw]
Subject: [PATCH 2/7 v3] HID: magicmouse: simplify multitouch feature request

From: Chase Douglas <[email protected]>

Only the first feature request is required to put the Magic Mouse into
multitouch mode. This is also the case for the Magic Trackpad, for which
support will be added in a later commit.

Signed-off-by: Chase Douglas <[email protected]>
Acked-by: Michael Poole <[email protected]>
---
drivers/hid/hid-magicmouse.c | 16 ++++------------
1 files changed, 4 insertions(+), 12 deletions(-)

diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c
index d38b529..8a2fe78 100644
--- a/drivers/hid/hid-magicmouse.c
+++ b/drivers/hid/hid-magicmouse.c
@@ -380,8 +380,7 @@ static void magicmouse_setup_input(struct input_dev *input, struct hid_device *h
static int magicmouse_probe(struct hid_device *hdev,
const struct hid_device_id *id)
{
- __u8 feature_1[] = { 0xd7, 0x01 };
- __u8 feature_2[] = { 0xf8, 0x01, 0x32 };
+ __u8 feature[] = { 0xd7, 0x01 };
struct input_dev *input;
struct magicmouse_sc *msc;
struct hid_report *report;
@@ -426,17 +425,10 @@ static int magicmouse_probe(struct hid_device *hdev,
}
report->size = 6;

- ret = hdev->hid_output_raw_report(hdev, feature_1, sizeof(feature_1),
+ ret = hdev->hid_output_raw_report(hdev, feature, sizeof(feature),
HID_FEATURE_REPORT);
- if (ret != sizeof(feature_1)) {
- dev_err(&hdev->dev, "unable to request touch data (1:%d)\n",
- ret);
- goto err_stop_hw;
- }
- ret = hdev->hid_output_raw_report(hdev, feature_2,
- sizeof(feature_2), HID_FEATURE_REPORT);
- if (ret != sizeof(feature_2)) {
- dev_err(&hdev->dev, "unable to request touch data (2:%d)\n",
+ if (ret != sizeof(feature)) {
+ dev_err(&hdev->dev, "unable to request touch data (%d)\n",
ret);
goto err_stop_hw;
}
--
1.7.1

2010-09-01 01:58:05

by Chase Douglas

[permalink] [raw]
Subject: [PATCH 3/7 v3] HID: magicmouse: simplify touch data bit manipulation

The new format should be easier to read to determine which bits
correspond to which data. It also brings all the manipulation logic to
the top of the function. This makes size and orientation reading more
clear.

Note that the impetus for this change is the forthcoming support for the
Magic Trackpad, which has a different touch data protocol.

Signed-off-by: Chase Douglas <[email protected]>
Acked-by: Michael Poole <[email protected]>
---
drivers/hid/hid-magicmouse.c | 25 +++++++++++++------------
1 files changed, 13 insertions(+), 12 deletions(-)

diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c
index 8a2fe78..2ee59a8 100644
--- a/drivers/hid/hid-magicmouse.c
+++ b/drivers/hid/hid-magicmouse.c
@@ -166,18 +166,21 @@ static void magicmouse_emit_buttons(struct magicmouse_sc *msc, int state)
static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tdata)
{
struct input_dev *input = msc->input;
- __s32 x_y = tdata[0] << 8 | tdata[1] << 16 | tdata[2] << 24;
- int misc = tdata[5] | tdata[6] << 8;
- int id = (misc >> 6) & 15;
- int x = x_y << 12 >> 20;
- int y = -(x_y >> 20);
- int down = (tdata[7] & TOUCH_STATE_MASK) != TOUCH_STATE_NONE;
+ int id = (tdata[6] << 2 | tdata[5] >> 6) & 0xf;
+ int x = (tdata[1] << 28 | tdata[0] << 20) >> 20;
+ int y = -((tdata[2] << 24 | tdata[1] << 16) >> 20);
+ int size = tdata[5] & 0x3f;
+ int orientation = (tdata[6] >> 2) - 32;
+ int touch_major = tdata[3];
+ int touch_minor = tdata[4];
+ int state = tdata[7] & TOUCH_STATE_MASK;
+ int down = state != TOUCH_STATE_NONE;

/* Store tracking ID and other fields. */
msc->tracking_ids[raw_id] = id;
msc->touches[id].x = x;
msc->touches[id].y = y;
- msc->touches[id].size = misc & 63;
+ msc->touches[id].size = size;

/* If requested, emulate a scroll wheel by detecting small
* vertical touch motions.
@@ -188,7 +191,7 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda
int step_y = msc->touches[id].scroll_y - y;

/* Calculate and apply the scroll motion. */
- switch (tdata[7] & TOUCH_STATE_MASK) {
+ switch (state) {
case TOUCH_STATE_START:
msc->touches[id].scroll_x = x;
msc->touches[id].scroll_y = y;
@@ -224,13 +227,11 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda

/* Generate the input events for this touch. */
if (report_touches && down) {
- int orientation = (misc >> 10) - 32;
-
msc->touches[id].down = 1;

input_report_abs(input, ABS_MT_TRACKING_ID, id);
- input_report_abs(input, ABS_MT_TOUCH_MAJOR, tdata[3]);
- input_report_abs(input, ABS_MT_TOUCH_MINOR, tdata[4]);
+ input_report_abs(input, ABS_MT_TOUCH_MAJOR, touch_major);
+ input_report_abs(input, ABS_MT_TOUCH_MINOR, touch_minor);
input_report_abs(input, ABS_MT_ORIENTATION, orientation);
input_report_abs(input, ABS_MT_POSITION_X, x);
input_report_abs(input, ABS_MT_POSITION_Y, y);
--
1.7.1

2010-09-01 02:23:33

by Michael Poole

[permalink] [raw]
Subject: Re: [PATCH 4/7 v3] HID: magicmouse: simplify touch down logic

On Tue, 2010-08-31 at 21:56 -0400, Chase Douglas wrote:
> From: Chase Douglas <[email protected]>
>
> For the MT protocol, we need to properly keep track of each down touch.
> This change simplifies the logic, and should make things easier when
> support for the Magic Trackpad is added.
>
> Signed-off-by: Chase Douglas <[email protected]>

Thanks, this looks slightly cleaner than what I had in mind.

Acked-by: Michael Poole <[email protected]>

> ---
> drivers/hid/hid-magicmouse.c | 27 +++++++++------------------
> 1 files changed, 9 insertions(+), 18 deletions(-)
>
> diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c
> index 2ee59a8..0fbca59 100644
> --- a/drivers/hid/hid-magicmouse.c
> +++ b/drivers/hid/hid-magicmouse.c
> @@ -98,7 +98,6 @@ struct magicmouse_sc {
> short scroll_x;
> short scroll_y;
> u8 size;
> - u8 down;
> } touches[16];
> int tracking_ids[16];
> };
> @@ -227,8 +226,6 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda
>
> /* Generate the input events for this touch. */
> if (report_touches && down) {
> - msc->touches[id].down = 1;
> -
> input_report_abs(input, ABS_MT_TRACKING_ID, id);
> input_report_abs(input, ABS_MT_TOUCH_MAJOR, touch_major);
> input_report_abs(input, ABS_MT_TOUCH_MINOR, touch_minor);
> @@ -241,6 +238,9 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda
>
> input_mt_sync(input);
> }
> +
> + if (down)
> + msc->ntouches++;
> }
>
> static int magicmouse_raw_event(struct hid_device *hdev,
> @@ -248,7 +248,7 @@ static int magicmouse_raw_event(struct hid_device *hdev,
> {
> struct magicmouse_sc *msc = hid_get_drvdata(hdev);
> struct input_dev *input = msc->input;
> - int x, y, ts, ii, clicks, last_up;
> + int x, y, ts, ii, clicks, npoints;
>
> switch (data[0]) {
> case 0x10:
> @@ -265,22 +265,13 @@ static int magicmouse_raw_event(struct hid_device *hdev,
> ts = data[3] >> 6 | data[4] << 2 | data[5] << 10;
> msc->delta_time = (ts - msc->last_timestamp) & 0x3ffff;
> msc->last_timestamp = ts;
> - msc->ntouches = (size - 6) / 8;
> - for (ii = 0; ii < msc->ntouches; ii++)
> + npoints = (size - 6) / 8;
> + msc->ntouches = 0;
> + for (ii = 0; ii < npoints; ii++)
> magicmouse_emit_touch(msc, ii, data + ii * 8 + 6);
>
> - if (report_touches) {
> - last_up = 1;
> - for (ii = 0; ii < ARRAY_SIZE(msc->touches); ii++) {
> - if (msc->touches[ii].down) {
> - last_up = 0;
> - msc->touches[ii].down = 0;
> - }
> - }
> - if (last_up) {
> - input_mt_sync(input);
> - }
> - }
> + if (report_touches && msc->ntouches == 0)
> + input_mt_sync(input);
>
> /* When emulating three-button mode, it is important
> * to have the current touch information before

2010-09-01 02:24:58

by Michael Poole

[permalink] [raw]
Subject: Re: [PATCH 5/7 v3] HID: magicmouse: remove timestamp logic

On Tue, 2010-08-31 at 21:56 -0400, Chase Douglas wrote:
> From: Chase Douglas <[email protected]>
>
> The timestamps from the device are currently stored in the private data
> structure. These aren't used, so remove them. I've left a comment
> detailing the protocol for future reference.
>
> Signed-off-by: Chase Douglas <[email protected]>

Acked-by: Michael Poole <[email protected]>

> ---
> drivers/hid/hid-magicmouse.c | 18 +++++++-----------
> 1 files changed, 7 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c
> index 0fbca59..9fc272f 100644
> --- a/drivers/hid/hid-magicmouse.c
> +++ b/drivers/hid/hid-magicmouse.c
> @@ -71,11 +71,6 @@ MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state fie
> * struct magicmouse_sc - Tracks Magic Mouse-specific data.
> * @input: Input device through which we report events.
> * @quirks: Currently unused.
> - * @last_timestamp: Timestamp from most recent (18-bit) touch report
> - * (units of milliseconds over short windows, but seems to
> - * increase faster when there are no touches).
> - * @delta_time: 18-bit difference between the two most recent touch
> - * reports from the mouse.
> * @ntouches: Number of touches in most recent touch report.
> * @scroll_accel: Number of consecutive scroll motions.
> * @scroll_jiffies: Time of last scroll motion.
> @@ -86,8 +81,6 @@ struct magicmouse_sc {
> struct input_dev *input;
> unsigned long quirks;
>
> - int last_timestamp;
> - int delta_time;
> int ntouches;
> int scroll_accel;
> unsigned long scroll_jiffies;
> @@ -248,7 +241,7 @@ static int magicmouse_raw_event(struct hid_device *hdev,
> {
> struct magicmouse_sc *msc = hid_get_drvdata(hdev);
> struct input_dev *input = msc->input;
> - int x, y, ts, ii, clicks, npoints;
> + int x, y, ii, clicks, npoints;
>
> switch (data[0]) {
> case 0x10:
> @@ -262,9 +255,6 @@ static int magicmouse_raw_event(struct hid_device *hdev,
> /* Expect six bytes of prefix, and N*8 bytes of touch data. */
> if (size < 6 || ((size - 6) % 8) != 0)
> return 0;
> - ts = data[3] >> 6 | data[4] << 2 | data[5] << 10;
> - msc->delta_time = (ts - msc->last_timestamp) & 0x3ffff;
> - msc->last_timestamp = ts;
> npoints = (size - 6) / 8;
> msc->ntouches = 0;
> for (ii = 0; ii < npoints; ii++)
> @@ -280,6 +270,12 @@ static int magicmouse_raw_event(struct hid_device *hdev,
> x = (int)(((data[3] & 0x0c) << 28) | (data[1] << 22)) >> 22;
> y = (int)(((data[3] & 0x30) << 26) | (data[2] << 22)) >> 22;
> clicks = data[3];
> +
> + /* The following bits provide a device specific timestamp. They
> + * are unused here.
> + *
> + * ts = data[3] >> 6 | data[4] << 2 | data[5] << 10;
> + */
> break;
> case 0x20: /* Theoretically battery status (0-100), but I have
> * never seen it -- maybe it is only upon request.