]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/hid/hid-magicmouse.c
Merge branch 'misc' of git://git.kernel.org/pub/scm/linux/kernel/git/mmarek/kbuild-2.6
[karo-tx-linux.git] / drivers / hid / hid-magicmouse.c
index f10d56a15f2103699003e8cf13d2c07f0ac6e192..319b0e57ee41f1cdbebafb833989b7a3507c7440 100644 (file)
@@ -30,6 +30,21 @@ static bool emulate_scroll_wheel = true;
 module_param(emulate_scroll_wheel, bool, 0644);
 MODULE_PARM_DESC(emulate_scroll_wheel, "Emulate a scroll wheel");
 
+static unsigned int scroll_speed = 32;
+static int param_set_scroll_speed(const char *val, struct kernel_param *kp) {
+       unsigned long speed;
+       if (!val || strict_strtoul(val, 0, &speed) || speed > 63)
+               return -EINVAL;
+       scroll_speed = speed;
+       return 0;
+}
+module_param_call(scroll_speed, param_set_scroll_speed, param_get_uint, &scroll_speed, 0644);
+MODULE_PARM_DESC(scroll_speed, "Scroll speed, value from 0 (slow) to 63 (fast)");
+
+static bool scroll_acceleration = false;
+module_param(scroll_acceleration, bool, 0644);
+MODULE_PARM_DESC(scroll_acceleration, "Accelerate sequential scroll events");
+
 static bool report_touches = true;
 module_param(report_touches, bool, 0644);
 MODULE_PARM_DESC(report_touches, "Emit touch records (otherwise, only use them for emulation)");
@@ -50,6 +65,8 @@ MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state fie
 #define TOUCH_STATE_START 0x30
 #define TOUCH_STATE_DRAG  0x40
 
+#define SCROLL_ACCEL_DEFAULT 7
+
 /**
  * struct magicmouse_sc - Tracks Magic Mouse-specific data.
  * @input: Input device through which we report events.
@@ -78,8 +95,10 @@ struct magicmouse_sc {
        struct {
                short x;
                short y;
+               short scroll_x;
                short scroll_y;
                u8 size;
+               u8 down;
        } touches[16];
        int tracking_ids[16];
 };
@@ -141,7 +160,7 @@ static void magicmouse_emit_buttons(struct magicmouse_sc *msc, int state)
        input_report_key(msc->input, BTN_RIGHT, state & 2);
 
        if (state != last_state)
-               msc->scroll_accel = 0;
+               msc->scroll_accel = SCROLL_ACCEL_DEFAULT;
 }
 
 static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tdata)
@@ -152,6 +171,7 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda
        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;
 
        /* Store tracking ID and other fields. */
        msc->tracking_ids[raw_id] = id;
@@ -160,42 +180,54 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda
        msc->touches[id].size = misc & 63;
 
        /* If requested, emulate a scroll wheel by detecting small
-        * vertical touch motions along the middle of the mouse.
+        * vertical touch motions.
         */
-       if (emulate_scroll_wheel &&
-           middle_button_start < x && x < middle_button_stop) {
-               static const int accel_profile[] = {
-                       256, 228, 192, 160, 128, 96, 64, 32,
-               };
+       if (emulate_scroll_wheel) {
                unsigned long now = jiffies;
-               int step = msc->touches[id].scroll_y - y;
-
-               /* Reset acceleration after half a second. */
-               if (time_after(now, msc->scroll_jiffies + HZ / 2))
-                       msc->scroll_accel = 0;
+               int step_x = msc->touches[id].scroll_x - x;
+               int step_y = msc->touches[id].scroll_y - y;
 
                /* Calculate and apply the scroll motion. */
                switch (tdata[7] & TOUCH_STATE_MASK) {
                case TOUCH_STATE_START:
+                       msc->touches[id].scroll_x = x;
                        msc->touches[id].scroll_y = y;
-                       msc->scroll_accel = min_t(int, msc->scroll_accel + 1,
-                                               ARRAY_SIZE(accel_profile) - 1);
+
+                       /* Reset acceleration after half a second. */
+                       if (scroll_acceleration && time_before(now,
+                                               msc->scroll_jiffies + HZ / 2))
+                               msc->scroll_accel = max_t(int,
+                                               msc->scroll_accel - 1, 1);
+                       else
+                               msc->scroll_accel = SCROLL_ACCEL_DEFAULT;
+
                        break;
                case TOUCH_STATE_DRAG:
-                       step = step / accel_profile[msc->scroll_accel];
-                       if (step != 0) {
-                               msc->touches[id].scroll_y = y;
+                       step_x /= (64 - (int)scroll_speed) * msc->scroll_accel;
+                       if (step_x != 0) {
+                               msc->touches[id].scroll_x -= step_x *
+                                       (64 - scroll_speed) * msc->scroll_accel;
                                msc->scroll_jiffies = now;
-                               input_report_rel(input, REL_WHEEL, step);
+                               input_report_rel(input, REL_HWHEEL, -step_x);
+                       }
+
+                       step_y /= (64 - (int)scroll_speed) * msc->scroll_accel;
+                       if (step_y != 0) {
+                               msc->touches[id].scroll_y -= step_y *
+                                       (64 - scroll_speed) * msc->scroll_accel;
+                               msc->scroll_jiffies = now;
+                               input_report_rel(input, REL_WHEEL, step_y);
                        }
                        break;
                }
        }
 
        /* Generate the input events for this touch. */
-       if (report_touches) {
+       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]);
@@ -215,7 +247,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;
+       int x, y, ts, ii, clicks, last_up;
 
        switch (data[0]) {
        case 0x10:
@@ -235,12 +267,26 @@ static int magicmouse_raw_event(struct hid_device *hdev,
                msc->ntouches = (size - 6) / 8;
                for (ii = 0; ii < msc->ntouches; 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);
+                       }
+               }
+
                /* When emulating three-button mode, it is important
                 * to have the current touch information before
                 * generating a click event.
                 */
-               x = (signed char)data[1];
-               y = (signed char)data[2];
+               x = (int)(((data[3] & 0x0c) << 28) | (data[1] << 22)) >> 22;
+               y = (int)(((data[3] & 0x30) << 26) | (data[2] << 22)) >> 22;
                clicks = data[3];
                break;
        case 0x20: /* Theoretically battery status (0-100), but I have
@@ -301,8 +347,10 @@ static void magicmouse_setup_input(struct input_dev *input, struct hid_device *h
        __set_bit(EV_REL, input->evbit);
        __set_bit(REL_X, input->relbit);
        __set_bit(REL_Y, input->relbit);
-       if (emulate_scroll_wheel)
+       if (emulate_scroll_wheel) {
                __set_bit(REL_WHEEL, input->relbit);
+               __set_bit(REL_HWHEEL, input->relbit);
+       }
 
        if (report_touches) {
                __set_bit(EV_ABS, input->evbit);
@@ -345,6 +393,8 @@ static int magicmouse_probe(struct hid_device *hdev,
                return -ENOMEM;
        }
 
+       msc->scroll_accel = SCROLL_ACCEL_DEFAULT;
+
        msc->quirks = id->driver_data;
        hid_set_drvdata(hdev, msc);