162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Apple "Magic" Wireless Mouse driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2010 Michael Poole <mdpoole@troilus.org> 662306a36Sopenharmony_ci * Copyright (c) 2010 Chase Douglas <chase.douglas@canonical.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci/* 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/device.h> 1562306a36Sopenharmony_ci#include <linux/hid.h> 1662306a36Sopenharmony_ci#include <linux/input/mt.h> 1762306a36Sopenharmony_ci#include <linux/module.h> 1862306a36Sopenharmony_ci#include <linux/slab.h> 1962306a36Sopenharmony_ci#include <linux/workqueue.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include "hid-ids.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic bool emulate_3button = true; 2462306a36Sopenharmony_cimodule_param(emulate_3button, bool, 0644); 2562306a36Sopenharmony_ciMODULE_PARM_DESC(emulate_3button, "Emulate a middle button"); 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic int middle_button_start = -350; 2862306a36Sopenharmony_cistatic int middle_button_stop = +350; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic bool emulate_scroll_wheel = true; 3162306a36Sopenharmony_cimodule_param(emulate_scroll_wheel, bool, 0644); 3262306a36Sopenharmony_ciMODULE_PARM_DESC(emulate_scroll_wheel, "Emulate a scroll wheel"); 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic unsigned int scroll_speed = 32; 3562306a36Sopenharmony_cistatic int param_set_scroll_speed(const char *val, 3662306a36Sopenharmony_ci const struct kernel_param *kp) { 3762306a36Sopenharmony_ci unsigned long speed; 3862306a36Sopenharmony_ci if (!val || kstrtoul(val, 0, &speed) || speed > 63) 3962306a36Sopenharmony_ci return -EINVAL; 4062306a36Sopenharmony_ci scroll_speed = speed; 4162306a36Sopenharmony_ci return 0; 4262306a36Sopenharmony_ci} 4362306a36Sopenharmony_cimodule_param_call(scroll_speed, param_set_scroll_speed, param_get_uint, &scroll_speed, 0644); 4462306a36Sopenharmony_ciMODULE_PARM_DESC(scroll_speed, "Scroll speed, value from 0 (slow) to 63 (fast)"); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic bool scroll_acceleration = false; 4762306a36Sopenharmony_cimodule_param(scroll_acceleration, bool, 0644); 4862306a36Sopenharmony_ciMODULE_PARM_DESC(scroll_acceleration, "Accelerate sequential scroll events"); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic bool report_undeciphered; 5162306a36Sopenharmony_cimodule_param(report_undeciphered, bool, 0644); 5262306a36Sopenharmony_ciMODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state field using a MSC_RAW event"); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci#define TRACKPAD2_2021_BT_VERSION 0x110 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci#define TRACKPAD_REPORT_ID 0x28 5762306a36Sopenharmony_ci#define TRACKPAD2_USB_REPORT_ID 0x02 5862306a36Sopenharmony_ci#define TRACKPAD2_BT_REPORT_ID 0x31 5962306a36Sopenharmony_ci#define MOUSE_REPORT_ID 0x29 6062306a36Sopenharmony_ci#define MOUSE2_REPORT_ID 0x12 6162306a36Sopenharmony_ci#define DOUBLE_REPORT_ID 0xf7 6262306a36Sopenharmony_ci#define USB_BATTERY_TIMEOUT_MS 60000 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci/* These definitions are not precise, but they're close enough. (Bits 6562306a36Sopenharmony_ci * 0x03 seem to indicate the aspect ratio of the touch, bits 0x70 seem 6662306a36Sopenharmony_ci * to be some kind of bit mask -- 0x20 may be a near-field reading, 6762306a36Sopenharmony_ci * and 0x40 is actual contact, and 0x10 may be a start/stop or change 6862306a36Sopenharmony_ci * indication.) 6962306a36Sopenharmony_ci */ 7062306a36Sopenharmony_ci#define TOUCH_STATE_MASK 0xf0 7162306a36Sopenharmony_ci#define TOUCH_STATE_NONE 0x00 7262306a36Sopenharmony_ci#define TOUCH_STATE_START 0x30 7362306a36Sopenharmony_ci#define TOUCH_STATE_DRAG 0x40 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci/* Number of high-resolution events for each low-resolution detent. */ 7662306a36Sopenharmony_ci#define SCROLL_HR_STEPS 10 7762306a36Sopenharmony_ci#define SCROLL_HR_MULT (120 / SCROLL_HR_STEPS) 7862306a36Sopenharmony_ci#define SCROLL_HR_THRESHOLD 90 /* units */ 7962306a36Sopenharmony_ci#define SCROLL_ACCEL_DEFAULT 7 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci/* Touch surface information. Dimension is in hundredths of a mm, min and max 8262306a36Sopenharmony_ci * are in units. */ 8362306a36Sopenharmony_ci#define MOUSE_DIMENSION_X (float)9056 8462306a36Sopenharmony_ci#define MOUSE_MIN_X -1100 8562306a36Sopenharmony_ci#define MOUSE_MAX_X 1258 8662306a36Sopenharmony_ci#define MOUSE_RES_X ((MOUSE_MAX_X - MOUSE_MIN_X) / (MOUSE_DIMENSION_X / 100)) 8762306a36Sopenharmony_ci#define MOUSE_DIMENSION_Y (float)5152 8862306a36Sopenharmony_ci#define MOUSE_MIN_Y -1589 8962306a36Sopenharmony_ci#define MOUSE_MAX_Y 2047 9062306a36Sopenharmony_ci#define MOUSE_RES_Y ((MOUSE_MAX_Y - MOUSE_MIN_Y) / (MOUSE_DIMENSION_Y / 100)) 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci#define TRACKPAD_DIMENSION_X (float)13000 9362306a36Sopenharmony_ci#define TRACKPAD_MIN_X -2909 9462306a36Sopenharmony_ci#define TRACKPAD_MAX_X 3167 9562306a36Sopenharmony_ci#define TRACKPAD_RES_X \ 9662306a36Sopenharmony_ci ((TRACKPAD_MAX_X - TRACKPAD_MIN_X) / (TRACKPAD_DIMENSION_X / 100)) 9762306a36Sopenharmony_ci#define TRACKPAD_DIMENSION_Y (float)11000 9862306a36Sopenharmony_ci#define TRACKPAD_MIN_Y -2456 9962306a36Sopenharmony_ci#define TRACKPAD_MAX_Y 2565 10062306a36Sopenharmony_ci#define TRACKPAD_RES_Y \ 10162306a36Sopenharmony_ci ((TRACKPAD_MAX_Y - TRACKPAD_MIN_Y) / (TRACKPAD_DIMENSION_Y / 100)) 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci#define TRACKPAD2_DIMENSION_X (float)16000 10462306a36Sopenharmony_ci#define TRACKPAD2_MIN_X -3678 10562306a36Sopenharmony_ci#define TRACKPAD2_MAX_X 3934 10662306a36Sopenharmony_ci#define TRACKPAD2_RES_X \ 10762306a36Sopenharmony_ci ((TRACKPAD2_MAX_X - TRACKPAD2_MIN_X) / (TRACKPAD2_DIMENSION_X / 100)) 10862306a36Sopenharmony_ci#define TRACKPAD2_DIMENSION_Y (float)11490 10962306a36Sopenharmony_ci#define TRACKPAD2_MIN_Y -2478 11062306a36Sopenharmony_ci#define TRACKPAD2_MAX_Y 2587 11162306a36Sopenharmony_ci#define TRACKPAD2_RES_Y \ 11262306a36Sopenharmony_ci ((TRACKPAD2_MAX_Y - TRACKPAD2_MIN_Y) / (TRACKPAD2_DIMENSION_Y / 100)) 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci/** 11562306a36Sopenharmony_ci * struct magicmouse_sc - Tracks Magic Mouse-specific data. 11662306a36Sopenharmony_ci * @input: Input device through which we report events. 11762306a36Sopenharmony_ci * @quirks: Currently unused. 11862306a36Sopenharmony_ci * @ntouches: Number of touches in most recent touch report. 11962306a36Sopenharmony_ci * @scroll_accel: Number of consecutive scroll motions. 12062306a36Sopenharmony_ci * @scroll_jiffies: Time of last scroll motion. 12162306a36Sopenharmony_ci * @touches: Most recent data for a touch, indexed by tracking ID. 12262306a36Sopenharmony_ci * @tracking_ids: Mapping of current touch input data to @touches. 12362306a36Sopenharmony_ci */ 12462306a36Sopenharmony_cistruct magicmouse_sc { 12562306a36Sopenharmony_ci struct input_dev *input; 12662306a36Sopenharmony_ci unsigned long quirks; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci int ntouches; 12962306a36Sopenharmony_ci int scroll_accel; 13062306a36Sopenharmony_ci unsigned long scroll_jiffies; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci struct { 13362306a36Sopenharmony_ci short x; 13462306a36Sopenharmony_ci short y; 13562306a36Sopenharmony_ci short scroll_x; 13662306a36Sopenharmony_ci short scroll_y; 13762306a36Sopenharmony_ci short scroll_x_hr; 13862306a36Sopenharmony_ci short scroll_y_hr; 13962306a36Sopenharmony_ci u8 size; 14062306a36Sopenharmony_ci bool scroll_x_active; 14162306a36Sopenharmony_ci bool scroll_y_active; 14262306a36Sopenharmony_ci } touches[16]; 14362306a36Sopenharmony_ci int tracking_ids[16]; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci struct hid_device *hdev; 14662306a36Sopenharmony_ci struct delayed_work work; 14762306a36Sopenharmony_ci struct timer_list battery_timer; 14862306a36Sopenharmony_ci}; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic int magicmouse_firm_touch(struct magicmouse_sc *msc) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci int touch = -1; 15362306a36Sopenharmony_ci int ii; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci /* If there is only one "firm" touch, set touch to its 15662306a36Sopenharmony_ci * tracking ID. 15762306a36Sopenharmony_ci */ 15862306a36Sopenharmony_ci for (ii = 0; ii < msc->ntouches; ii++) { 15962306a36Sopenharmony_ci int idx = msc->tracking_ids[ii]; 16062306a36Sopenharmony_ci if (msc->touches[idx].size < 8) { 16162306a36Sopenharmony_ci /* Ignore this touch. */ 16262306a36Sopenharmony_ci } else if (touch >= 0) { 16362306a36Sopenharmony_ci touch = -1; 16462306a36Sopenharmony_ci break; 16562306a36Sopenharmony_ci } else { 16662306a36Sopenharmony_ci touch = idx; 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci return touch; 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic void magicmouse_emit_buttons(struct magicmouse_sc *msc, int state) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci int last_state = test_bit(BTN_LEFT, msc->input->key) << 0 | 17662306a36Sopenharmony_ci test_bit(BTN_RIGHT, msc->input->key) << 1 | 17762306a36Sopenharmony_ci test_bit(BTN_MIDDLE, msc->input->key) << 2; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci if (emulate_3button) { 18062306a36Sopenharmony_ci int id; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci /* If some button was pressed before, keep it held 18362306a36Sopenharmony_ci * down. Otherwise, if there's exactly one firm 18462306a36Sopenharmony_ci * touch, use that to override the mouse's guess. 18562306a36Sopenharmony_ci */ 18662306a36Sopenharmony_ci if (state == 0) { 18762306a36Sopenharmony_ci /* The button was released. */ 18862306a36Sopenharmony_ci } else if (last_state != 0) { 18962306a36Sopenharmony_ci state = last_state; 19062306a36Sopenharmony_ci } else if ((id = magicmouse_firm_touch(msc)) >= 0) { 19162306a36Sopenharmony_ci int x = msc->touches[id].x; 19262306a36Sopenharmony_ci if (x < middle_button_start) 19362306a36Sopenharmony_ci state = 1; 19462306a36Sopenharmony_ci else if (x > middle_button_stop) 19562306a36Sopenharmony_ci state = 2; 19662306a36Sopenharmony_ci else 19762306a36Sopenharmony_ci state = 4; 19862306a36Sopenharmony_ci } /* else: we keep the mouse's guess */ 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci input_report_key(msc->input, BTN_MIDDLE, state & 4); 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci input_report_key(msc->input, BTN_LEFT, state & 1); 20462306a36Sopenharmony_ci input_report_key(msc->input, BTN_RIGHT, state & 2); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci if (state != last_state) 20762306a36Sopenharmony_ci msc->scroll_accel = SCROLL_ACCEL_DEFAULT; 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tdata) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci struct input_dev *input = msc->input; 21362306a36Sopenharmony_ci int id, x, y, size, orientation, touch_major, touch_minor, state, down; 21462306a36Sopenharmony_ci int pressure = 0; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE || 21762306a36Sopenharmony_ci input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE2) { 21862306a36Sopenharmony_ci id = (tdata[6] << 2 | tdata[5] >> 6) & 0xf; 21962306a36Sopenharmony_ci x = (tdata[1] << 28 | tdata[0] << 20) >> 20; 22062306a36Sopenharmony_ci y = -((tdata[2] << 24 | tdata[1] << 16) >> 20); 22162306a36Sopenharmony_ci size = tdata[5] & 0x3f; 22262306a36Sopenharmony_ci orientation = (tdata[6] >> 2) - 32; 22362306a36Sopenharmony_ci touch_major = tdata[3]; 22462306a36Sopenharmony_ci touch_minor = tdata[4]; 22562306a36Sopenharmony_ci state = tdata[7] & TOUCH_STATE_MASK; 22662306a36Sopenharmony_ci down = state != TOUCH_STATE_NONE; 22762306a36Sopenharmony_ci } else if (input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) { 22862306a36Sopenharmony_ci id = tdata[8] & 0xf; 22962306a36Sopenharmony_ci x = (tdata[1] << 27 | tdata[0] << 19) >> 19; 23062306a36Sopenharmony_ci y = -((tdata[3] << 30 | tdata[2] << 22 | tdata[1] << 14) >> 19); 23162306a36Sopenharmony_ci size = tdata[6]; 23262306a36Sopenharmony_ci orientation = (tdata[8] >> 5) - 4; 23362306a36Sopenharmony_ci touch_major = tdata[4]; 23462306a36Sopenharmony_ci touch_minor = tdata[5]; 23562306a36Sopenharmony_ci pressure = tdata[7]; 23662306a36Sopenharmony_ci state = tdata[3] & 0xC0; 23762306a36Sopenharmony_ci down = state == 0x80; 23862306a36Sopenharmony_ci } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */ 23962306a36Sopenharmony_ci id = (tdata[7] << 2 | tdata[6] >> 6) & 0xf; 24062306a36Sopenharmony_ci x = (tdata[1] << 27 | tdata[0] << 19) >> 19; 24162306a36Sopenharmony_ci y = -((tdata[3] << 30 | tdata[2] << 22 | tdata[1] << 14) >> 19); 24262306a36Sopenharmony_ci size = tdata[6] & 0x3f; 24362306a36Sopenharmony_ci orientation = (tdata[7] >> 2) - 32; 24462306a36Sopenharmony_ci touch_major = tdata[4]; 24562306a36Sopenharmony_ci touch_minor = tdata[5]; 24662306a36Sopenharmony_ci state = tdata[8] & TOUCH_STATE_MASK; 24762306a36Sopenharmony_ci down = state != TOUCH_STATE_NONE; 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci /* Store tracking ID and other fields. */ 25162306a36Sopenharmony_ci msc->tracking_ids[raw_id] = id; 25262306a36Sopenharmony_ci msc->touches[id].x = x; 25362306a36Sopenharmony_ci msc->touches[id].y = y; 25462306a36Sopenharmony_ci msc->touches[id].size = size; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci /* If requested, emulate a scroll wheel by detecting small 25762306a36Sopenharmony_ci * vertical touch motions. 25862306a36Sopenharmony_ci */ 25962306a36Sopenharmony_ci if (emulate_scroll_wheel && (input->id.product != 26062306a36Sopenharmony_ci USB_DEVICE_ID_APPLE_MAGICTRACKPAD2)) { 26162306a36Sopenharmony_ci unsigned long now = jiffies; 26262306a36Sopenharmony_ci int step_x = msc->touches[id].scroll_x - x; 26362306a36Sopenharmony_ci int step_y = msc->touches[id].scroll_y - y; 26462306a36Sopenharmony_ci int step_hr = 26562306a36Sopenharmony_ci max_t(int, 26662306a36Sopenharmony_ci ((64 - (int)scroll_speed) * msc->scroll_accel) / 26762306a36Sopenharmony_ci SCROLL_HR_STEPS, 26862306a36Sopenharmony_ci 1); 26962306a36Sopenharmony_ci int step_x_hr = msc->touches[id].scroll_x_hr - x; 27062306a36Sopenharmony_ci int step_y_hr = msc->touches[id].scroll_y_hr - y; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci /* Calculate and apply the scroll motion. */ 27362306a36Sopenharmony_ci switch (state) { 27462306a36Sopenharmony_ci case TOUCH_STATE_START: 27562306a36Sopenharmony_ci msc->touches[id].scroll_x = x; 27662306a36Sopenharmony_ci msc->touches[id].scroll_y = y; 27762306a36Sopenharmony_ci msc->touches[id].scroll_x_hr = x; 27862306a36Sopenharmony_ci msc->touches[id].scroll_y_hr = y; 27962306a36Sopenharmony_ci msc->touches[id].scroll_x_active = false; 28062306a36Sopenharmony_ci msc->touches[id].scroll_y_active = false; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci /* Reset acceleration after half a second. */ 28362306a36Sopenharmony_ci if (scroll_acceleration && time_before(now, 28462306a36Sopenharmony_ci msc->scroll_jiffies + HZ / 2)) 28562306a36Sopenharmony_ci msc->scroll_accel = max_t(int, 28662306a36Sopenharmony_ci msc->scroll_accel - 1, 1); 28762306a36Sopenharmony_ci else 28862306a36Sopenharmony_ci msc->scroll_accel = SCROLL_ACCEL_DEFAULT; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci break; 29162306a36Sopenharmony_ci case TOUCH_STATE_DRAG: 29262306a36Sopenharmony_ci step_x /= (64 - (int)scroll_speed) * msc->scroll_accel; 29362306a36Sopenharmony_ci if (step_x != 0) { 29462306a36Sopenharmony_ci msc->touches[id].scroll_x -= step_x * 29562306a36Sopenharmony_ci (64 - scroll_speed) * msc->scroll_accel; 29662306a36Sopenharmony_ci msc->scroll_jiffies = now; 29762306a36Sopenharmony_ci input_report_rel(input, REL_HWHEEL, -step_x); 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci step_y /= (64 - (int)scroll_speed) * msc->scroll_accel; 30162306a36Sopenharmony_ci if (step_y != 0) { 30262306a36Sopenharmony_ci msc->touches[id].scroll_y -= step_y * 30362306a36Sopenharmony_ci (64 - scroll_speed) * msc->scroll_accel; 30462306a36Sopenharmony_ci msc->scroll_jiffies = now; 30562306a36Sopenharmony_ci input_report_rel(input, REL_WHEEL, step_y); 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci if (!msc->touches[id].scroll_x_active && 30962306a36Sopenharmony_ci abs(step_x_hr) > SCROLL_HR_THRESHOLD) { 31062306a36Sopenharmony_ci msc->touches[id].scroll_x_active = true; 31162306a36Sopenharmony_ci msc->touches[id].scroll_x_hr = x; 31262306a36Sopenharmony_ci step_x_hr = 0; 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci step_x_hr /= step_hr; 31662306a36Sopenharmony_ci if (step_x_hr != 0 && 31762306a36Sopenharmony_ci msc->touches[id].scroll_x_active) { 31862306a36Sopenharmony_ci msc->touches[id].scroll_x_hr -= step_x_hr * 31962306a36Sopenharmony_ci step_hr; 32062306a36Sopenharmony_ci input_report_rel(input, 32162306a36Sopenharmony_ci REL_HWHEEL_HI_RES, 32262306a36Sopenharmony_ci -step_x_hr * SCROLL_HR_MULT); 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci if (!msc->touches[id].scroll_y_active && 32662306a36Sopenharmony_ci abs(step_y_hr) > SCROLL_HR_THRESHOLD) { 32762306a36Sopenharmony_ci msc->touches[id].scroll_y_active = true; 32862306a36Sopenharmony_ci msc->touches[id].scroll_y_hr = y; 32962306a36Sopenharmony_ci step_y_hr = 0; 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci step_y_hr /= step_hr; 33362306a36Sopenharmony_ci if (step_y_hr != 0 && 33462306a36Sopenharmony_ci msc->touches[id].scroll_y_active) { 33562306a36Sopenharmony_ci msc->touches[id].scroll_y_hr -= step_y_hr * 33662306a36Sopenharmony_ci step_hr; 33762306a36Sopenharmony_ci input_report_rel(input, 33862306a36Sopenharmony_ci REL_WHEEL_HI_RES, 33962306a36Sopenharmony_ci step_y_hr * SCROLL_HR_MULT); 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci break; 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci if (down) 34662306a36Sopenharmony_ci msc->ntouches++; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci input_mt_slot(input, id); 34962306a36Sopenharmony_ci input_mt_report_slot_state(input, MT_TOOL_FINGER, down); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci /* Generate the input events for this touch. */ 35262306a36Sopenharmony_ci if (down) { 35362306a36Sopenharmony_ci input_report_abs(input, ABS_MT_TOUCH_MAJOR, touch_major << 2); 35462306a36Sopenharmony_ci input_report_abs(input, ABS_MT_TOUCH_MINOR, touch_minor << 2); 35562306a36Sopenharmony_ci input_report_abs(input, ABS_MT_ORIENTATION, -orientation); 35662306a36Sopenharmony_ci input_report_abs(input, ABS_MT_POSITION_X, x); 35762306a36Sopenharmony_ci input_report_abs(input, ABS_MT_POSITION_Y, y); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci if (input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) 36062306a36Sopenharmony_ci input_report_abs(input, ABS_MT_PRESSURE, pressure); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci if (report_undeciphered) { 36362306a36Sopenharmony_ci if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE || 36462306a36Sopenharmony_ci input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE2) 36562306a36Sopenharmony_ci input_event(input, EV_MSC, MSC_RAW, tdata[7]); 36662306a36Sopenharmony_ci else if (input->id.product != 36762306a36Sopenharmony_ci USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) 36862306a36Sopenharmony_ci input_event(input, EV_MSC, MSC_RAW, tdata[8]); 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci } 37162306a36Sopenharmony_ci} 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_cistatic int magicmouse_raw_event(struct hid_device *hdev, 37462306a36Sopenharmony_ci struct hid_report *report, u8 *data, int size) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci struct magicmouse_sc *msc = hid_get_drvdata(hdev); 37762306a36Sopenharmony_ci struct input_dev *input = msc->input; 37862306a36Sopenharmony_ci int x = 0, y = 0, ii, clicks = 0, npoints; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci switch (data[0]) { 38162306a36Sopenharmony_ci case TRACKPAD_REPORT_ID: 38262306a36Sopenharmony_ci case TRACKPAD2_BT_REPORT_ID: 38362306a36Sopenharmony_ci /* Expect four bytes of prefix, and N*9 bytes of touch data. */ 38462306a36Sopenharmony_ci if (size < 4 || ((size - 4) % 9) != 0) 38562306a36Sopenharmony_ci return 0; 38662306a36Sopenharmony_ci npoints = (size - 4) / 9; 38762306a36Sopenharmony_ci if (npoints > 15) { 38862306a36Sopenharmony_ci hid_warn(hdev, "invalid size value (%d) for TRACKPAD_REPORT_ID\n", 38962306a36Sopenharmony_ci size); 39062306a36Sopenharmony_ci return 0; 39162306a36Sopenharmony_ci } 39262306a36Sopenharmony_ci msc->ntouches = 0; 39362306a36Sopenharmony_ci for (ii = 0; ii < npoints; ii++) 39462306a36Sopenharmony_ci magicmouse_emit_touch(msc, ii, data + ii * 9 + 4); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci clicks = data[1]; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci /* The following bits provide a device specific timestamp. They 39962306a36Sopenharmony_ci * are unused here. 40062306a36Sopenharmony_ci * 40162306a36Sopenharmony_ci * ts = data[1] >> 6 | data[2] << 2 | data[3] << 10; 40262306a36Sopenharmony_ci */ 40362306a36Sopenharmony_ci break; 40462306a36Sopenharmony_ci case TRACKPAD2_USB_REPORT_ID: 40562306a36Sopenharmony_ci /* Expect twelve bytes of prefix and N*9 bytes of touch data. */ 40662306a36Sopenharmony_ci if (size < 12 || ((size - 12) % 9) != 0) 40762306a36Sopenharmony_ci return 0; 40862306a36Sopenharmony_ci npoints = (size - 12) / 9; 40962306a36Sopenharmony_ci if (npoints > 15) { 41062306a36Sopenharmony_ci hid_warn(hdev, "invalid size value (%d) for TRACKPAD2_USB_REPORT_ID\n", 41162306a36Sopenharmony_ci size); 41262306a36Sopenharmony_ci return 0; 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci msc->ntouches = 0; 41562306a36Sopenharmony_ci for (ii = 0; ii < npoints; ii++) 41662306a36Sopenharmony_ci magicmouse_emit_touch(msc, ii, data + ii * 9 + 12); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci clicks = data[1]; 41962306a36Sopenharmony_ci break; 42062306a36Sopenharmony_ci case MOUSE_REPORT_ID: 42162306a36Sopenharmony_ci /* Expect six bytes of prefix, and N*8 bytes of touch data. */ 42262306a36Sopenharmony_ci if (size < 6 || ((size - 6) % 8) != 0) 42362306a36Sopenharmony_ci return 0; 42462306a36Sopenharmony_ci npoints = (size - 6) / 8; 42562306a36Sopenharmony_ci if (npoints > 15) { 42662306a36Sopenharmony_ci hid_warn(hdev, "invalid size value (%d) for MOUSE_REPORT_ID\n", 42762306a36Sopenharmony_ci size); 42862306a36Sopenharmony_ci return 0; 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci msc->ntouches = 0; 43162306a36Sopenharmony_ci for (ii = 0; ii < npoints; ii++) 43262306a36Sopenharmony_ci magicmouse_emit_touch(msc, ii, data + ii * 8 + 6); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci /* When emulating three-button mode, it is important 43562306a36Sopenharmony_ci * to have the current touch information before 43662306a36Sopenharmony_ci * generating a click event. 43762306a36Sopenharmony_ci */ 43862306a36Sopenharmony_ci x = (int)(((data[3] & 0x0c) << 28) | (data[1] << 22)) >> 22; 43962306a36Sopenharmony_ci y = (int)(((data[3] & 0x30) << 26) | (data[2] << 22)) >> 22; 44062306a36Sopenharmony_ci clicks = data[3]; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci /* The following bits provide a device specific timestamp. They 44362306a36Sopenharmony_ci * are unused here. 44462306a36Sopenharmony_ci * 44562306a36Sopenharmony_ci * ts = data[3] >> 6 | data[4] << 2 | data[5] << 10; 44662306a36Sopenharmony_ci */ 44762306a36Sopenharmony_ci break; 44862306a36Sopenharmony_ci case MOUSE2_REPORT_ID: 44962306a36Sopenharmony_ci /* Size is either 8 or (14 + 8 * N) */ 45062306a36Sopenharmony_ci if (size != 8 && (size < 14 || (size - 14) % 8 != 0)) 45162306a36Sopenharmony_ci return 0; 45262306a36Sopenharmony_ci npoints = (size - 14) / 8; 45362306a36Sopenharmony_ci if (npoints > 15) { 45462306a36Sopenharmony_ci hid_warn(hdev, "invalid size value (%d) for MOUSE2_REPORT_ID\n", 45562306a36Sopenharmony_ci size); 45662306a36Sopenharmony_ci return 0; 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci msc->ntouches = 0; 45962306a36Sopenharmony_ci for (ii = 0; ii < npoints; ii++) 46062306a36Sopenharmony_ci magicmouse_emit_touch(msc, ii, data + ii * 8 + 14); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci /* When emulating three-button mode, it is important 46362306a36Sopenharmony_ci * to have the current touch information before 46462306a36Sopenharmony_ci * generating a click event. 46562306a36Sopenharmony_ci */ 46662306a36Sopenharmony_ci x = (int)((data[3] << 24) | (data[2] << 16)) >> 16; 46762306a36Sopenharmony_ci y = (int)((data[5] << 24) | (data[4] << 16)) >> 16; 46862306a36Sopenharmony_ci clicks = data[1]; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci /* The following bits provide a device specific timestamp. They 47162306a36Sopenharmony_ci * are unused here. 47262306a36Sopenharmony_ci * 47362306a36Sopenharmony_ci * ts = data[11] >> 6 | data[12] << 2 | data[13] << 10; 47462306a36Sopenharmony_ci */ 47562306a36Sopenharmony_ci break; 47662306a36Sopenharmony_ci case DOUBLE_REPORT_ID: 47762306a36Sopenharmony_ci /* Sometimes the trackpad sends two touch reports in one 47862306a36Sopenharmony_ci * packet. 47962306a36Sopenharmony_ci */ 48062306a36Sopenharmony_ci magicmouse_raw_event(hdev, report, data + 2, data[1]); 48162306a36Sopenharmony_ci magicmouse_raw_event(hdev, report, data + 2 + data[1], 48262306a36Sopenharmony_ci size - 2 - data[1]); 48362306a36Sopenharmony_ci return 0; 48462306a36Sopenharmony_ci default: 48562306a36Sopenharmony_ci return 0; 48662306a36Sopenharmony_ci } 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE || 48962306a36Sopenharmony_ci input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE2) { 49062306a36Sopenharmony_ci magicmouse_emit_buttons(msc, clicks & 3); 49162306a36Sopenharmony_ci input_report_rel(input, REL_X, x); 49262306a36Sopenharmony_ci input_report_rel(input, REL_Y, y); 49362306a36Sopenharmony_ci } else if (input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) { 49462306a36Sopenharmony_ci input_mt_sync_frame(input); 49562306a36Sopenharmony_ci input_report_key(input, BTN_MOUSE, clicks & 1); 49662306a36Sopenharmony_ci } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */ 49762306a36Sopenharmony_ci input_report_key(input, BTN_MOUSE, clicks & 1); 49862306a36Sopenharmony_ci input_mt_report_pointer_emulation(input, true); 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci input_sync(input); 50262306a36Sopenharmony_ci return 1; 50362306a36Sopenharmony_ci} 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_cistatic int magicmouse_event(struct hid_device *hdev, struct hid_field *field, 50662306a36Sopenharmony_ci struct hid_usage *usage, __s32 value) 50762306a36Sopenharmony_ci{ 50862306a36Sopenharmony_ci struct magicmouse_sc *msc = hid_get_drvdata(hdev); 50962306a36Sopenharmony_ci if (msc->input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE2 && 51062306a36Sopenharmony_ci field->report->id == MOUSE2_REPORT_ID) { 51162306a36Sopenharmony_ci /* 51262306a36Sopenharmony_ci * magic_mouse_raw_event has done all the work. Skip hidinput. 51362306a36Sopenharmony_ci * 51462306a36Sopenharmony_ci * Specifically, hidinput may modify BTN_LEFT and BTN_RIGHT, 51562306a36Sopenharmony_ci * breaking emulate_3button. 51662306a36Sopenharmony_ci */ 51762306a36Sopenharmony_ci return 1; 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci return 0; 52062306a36Sopenharmony_ci} 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_cistatic int magicmouse_setup_input(struct input_dev *input, struct hid_device *hdev) 52362306a36Sopenharmony_ci{ 52462306a36Sopenharmony_ci int error; 52562306a36Sopenharmony_ci int mt_flags = 0; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci __set_bit(EV_KEY, input->evbit); 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE || 53062306a36Sopenharmony_ci input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE2) { 53162306a36Sopenharmony_ci __set_bit(BTN_LEFT, input->keybit); 53262306a36Sopenharmony_ci __set_bit(BTN_RIGHT, input->keybit); 53362306a36Sopenharmony_ci if (emulate_3button) 53462306a36Sopenharmony_ci __set_bit(BTN_MIDDLE, input->keybit); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci __set_bit(EV_REL, input->evbit); 53762306a36Sopenharmony_ci __set_bit(REL_X, input->relbit); 53862306a36Sopenharmony_ci __set_bit(REL_Y, input->relbit); 53962306a36Sopenharmony_ci if (emulate_scroll_wheel) { 54062306a36Sopenharmony_ci __set_bit(REL_WHEEL, input->relbit); 54162306a36Sopenharmony_ci __set_bit(REL_HWHEEL, input->relbit); 54262306a36Sopenharmony_ci __set_bit(REL_WHEEL_HI_RES, input->relbit); 54362306a36Sopenharmony_ci __set_bit(REL_HWHEEL_HI_RES, input->relbit); 54462306a36Sopenharmony_ci } 54562306a36Sopenharmony_ci } else if (input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) { 54662306a36Sopenharmony_ci /* If the trackpad has been connected to a Mac, the name is 54762306a36Sopenharmony_ci * automatically personalized, e.g., "José Expósito's Trackpad". 54862306a36Sopenharmony_ci * When connected through Bluetooth, the personalized name is 54962306a36Sopenharmony_ci * reported, however, when connected through USB the generic 55062306a36Sopenharmony_ci * name is reported. 55162306a36Sopenharmony_ci * Set the device name to ensure the same driver settings get 55262306a36Sopenharmony_ci * loaded, whether connected through bluetooth or USB. 55362306a36Sopenharmony_ci */ 55462306a36Sopenharmony_ci if (hdev->vendor == BT_VENDOR_ID_APPLE) { 55562306a36Sopenharmony_ci if (input->id.version == TRACKPAD2_2021_BT_VERSION) 55662306a36Sopenharmony_ci input->name = "Apple Inc. Magic Trackpad"; 55762306a36Sopenharmony_ci else 55862306a36Sopenharmony_ci input->name = "Apple Inc. Magic Trackpad 2"; 55962306a36Sopenharmony_ci } else { /* USB_VENDOR_ID_APPLE */ 56062306a36Sopenharmony_ci input->name = hdev->name; 56162306a36Sopenharmony_ci } 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci __clear_bit(EV_MSC, input->evbit); 56462306a36Sopenharmony_ci __clear_bit(BTN_0, input->keybit); 56562306a36Sopenharmony_ci __clear_bit(BTN_RIGHT, input->keybit); 56662306a36Sopenharmony_ci __clear_bit(BTN_MIDDLE, input->keybit); 56762306a36Sopenharmony_ci __set_bit(BTN_MOUSE, input->keybit); 56862306a36Sopenharmony_ci __set_bit(INPUT_PROP_BUTTONPAD, input->propbit); 56962306a36Sopenharmony_ci __set_bit(BTN_TOOL_FINGER, input->keybit); 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci mt_flags = INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED | 57262306a36Sopenharmony_ci INPUT_MT_TRACK; 57362306a36Sopenharmony_ci } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */ 57462306a36Sopenharmony_ci /* input->keybit is initialized with incorrect button info 57562306a36Sopenharmony_ci * for Magic Trackpad. There really is only one physical 57662306a36Sopenharmony_ci * button (BTN_LEFT == BTN_MOUSE). Make sure we don't 57762306a36Sopenharmony_ci * advertise buttons that don't exist... 57862306a36Sopenharmony_ci */ 57962306a36Sopenharmony_ci __clear_bit(BTN_RIGHT, input->keybit); 58062306a36Sopenharmony_ci __clear_bit(BTN_MIDDLE, input->keybit); 58162306a36Sopenharmony_ci __set_bit(BTN_MOUSE, input->keybit); 58262306a36Sopenharmony_ci __set_bit(BTN_TOOL_FINGER, input->keybit); 58362306a36Sopenharmony_ci __set_bit(BTN_TOOL_DOUBLETAP, input->keybit); 58462306a36Sopenharmony_ci __set_bit(BTN_TOOL_TRIPLETAP, input->keybit); 58562306a36Sopenharmony_ci __set_bit(BTN_TOOL_QUADTAP, input->keybit); 58662306a36Sopenharmony_ci __set_bit(BTN_TOOL_QUINTTAP, input->keybit); 58762306a36Sopenharmony_ci __set_bit(BTN_TOUCH, input->keybit); 58862306a36Sopenharmony_ci __set_bit(INPUT_PROP_POINTER, input->propbit); 58962306a36Sopenharmony_ci __set_bit(INPUT_PROP_BUTTONPAD, input->propbit); 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci __set_bit(EV_ABS, input->evbit); 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci error = input_mt_init_slots(input, 16, mt_flags); 59662306a36Sopenharmony_ci if (error) 59762306a36Sopenharmony_ci return error; 59862306a36Sopenharmony_ci input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 255 << 2, 59962306a36Sopenharmony_ci 4, 0); 60062306a36Sopenharmony_ci input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 255 << 2, 60162306a36Sopenharmony_ci 4, 0); 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci /* Note: Touch Y position from the device is inverted relative 60462306a36Sopenharmony_ci * to how pointer motion is reported (and relative to how USB 60562306a36Sopenharmony_ci * HID recommends the coordinates work). This driver keeps 60662306a36Sopenharmony_ci * the origin at the same position, and just uses the additive 60762306a36Sopenharmony_ci * inverse of the reported Y. 60862306a36Sopenharmony_ci */ 60962306a36Sopenharmony_ci if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE || 61062306a36Sopenharmony_ci input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE2) { 61162306a36Sopenharmony_ci input_set_abs_params(input, ABS_MT_ORIENTATION, -31, 32, 1, 0); 61262306a36Sopenharmony_ci input_set_abs_params(input, ABS_MT_POSITION_X, 61362306a36Sopenharmony_ci MOUSE_MIN_X, MOUSE_MAX_X, 4, 0); 61462306a36Sopenharmony_ci input_set_abs_params(input, ABS_MT_POSITION_Y, 61562306a36Sopenharmony_ci MOUSE_MIN_Y, MOUSE_MAX_Y, 4, 0); 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci input_abs_set_res(input, ABS_MT_POSITION_X, 61862306a36Sopenharmony_ci MOUSE_RES_X); 61962306a36Sopenharmony_ci input_abs_set_res(input, ABS_MT_POSITION_Y, 62062306a36Sopenharmony_ci MOUSE_RES_Y); 62162306a36Sopenharmony_ci } else if (input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) { 62262306a36Sopenharmony_ci input_set_abs_params(input, ABS_MT_PRESSURE, 0, 253, 0, 0); 62362306a36Sopenharmony_ci input_set_abs_params(input, ABS_PRESSURE, 0, 253, 0, 0); 62462306a36Sopenharmony_ci input_set_abs_params(input, ABS_MT_ORIENTATION, -3, 4, 0, 0); 62562306a36Sopenharmony_ci input_set_abs_params(input, ABS_X, TRACKPAD2_MIN_X, 62662306a36Sopenharmony_ci TRACKPAD2_MAX_X, 0, 0); 62762306a36Sopenharmony_ci input_set_abs_params(input, ABS_Y, TRACKPAD2_MIN_Y, 62862306a36Sopenharmony_ci TRACKPAD2_MAX_Y, 0, 0); 62962306a36Sopenharmony_ci input_set_abs_params(input, ABS_MT_POSITION_X, 63062306a36Sopenharmony_ci TRACKPAD2_MIN_X, TRACKPAD2_MAX_X, 0, 0); 63162306a36Sopenharmony_ci input_set_abs_params(input, ABS_MT_POSITION_Y, 63262306a36Sopenharmony_ci TRACKPAD2_MIN_Y, TRACKPAD2_MAX_Y, 0, 0); 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci input_abs_set_res(input, ABS_X, TRACKPAD2_RES_X); 63562306a36Sopenharmony_ci input_abs_set_res(input, ABS_Y, TRACKPAD2_RES_Y); 63662306a36Sopenharmony_ci input_abs_set_res(input, ABS_MT_POSITION_X, TRACKPAD2_RES_X); 63762306a36Sopenharmony_ci input_abs_set_res(input, ABS_MT_POSITION_Y, TRACKPAD2_RES_Y); 63862306a36Sopenharmony_ci } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */ 63962306a36Sopenharmony_ci input_set_abs_params(input, ABS_MT_ORIENTATION, -31, 32, 1, 0); 64062306a36Sopenharmony_ci input_set_abs_params(input, ABS_X, TRACKPAD_MIN_X, 64162306a36Sopenharmony_ci TRACKPAD_MAX_X, 4, 0); 64262306a36Sopenharmony_ci input_set_abs_params(input, ABS_Y, TRACKPAD_MIN_Y, 64362306a36Sopenharmony_ci TRACKPAD_MAX_Y, 4, 0); 64462306a36Sopenharmony_ci input_set_abs_params(input, ABS_MT_POSITION_X, 64562306a36Sopenharmony_ci TRACKPAD_MIN_X, TRACKPAD_MAX_X, 4, 0); 64662306a36Sopenharmony_ci input_set_abs_params(input, ABS_MT_POSITION_Y, 64762306a36Sopenharmony_ci TRACKPAD_MIN_Y, TRACKPAD_MAX_Y, 4, 0); 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci input_abs_set_res(input, ABS_X, TRACKPAD_RES_X); 65062306a36Sopenharmony_ci input_abs_set_res(input, ABS_Y, TRACKPAD_RES_Y); 65162306a36Sopenharmony_ci input_abs_set_res(input, ABS_MT_POSITION_X, 65262306a36Sopenharmony_ci TRACKPAD_RES_X); 65362306a36Sopenharmony_ci input_abs_set_res(input, ABS_MT_POSITION_Y, 65462306a36Sopenharmony_ci TRACKPAD_RES_Y); 65562306a36Sopenharmony_ci } 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci input_set_events_per_packet(input, 60); 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci if (report_undeciphered && 66062306a36Sopenharmony_ci input->id.product != USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) { 66162306a36Sopenharmony_ci __set_bit(EV_MSC, input->evbit); 66262306a36Sopenharmony_ci __set_bit(MSC_RAW, input->mscbit); 66362306a36Sopenharmony_ci } 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci /* 66662306a36Sopenharmony_ci * hid-input may mark device as using autorepeat, but neither 66762306a36Sopenharmony_ci * the trackpad, nor the mouse actually want it. 66862306a36Sopenharmony_ci */ 66962306a36Sopenharmony_ci __clear_bit(EV_REP, input->evbit); 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci return 0; 67262306a36Sopenharmony_ci} 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_cistatic int magicmouse_input_mapping(struct hid_device *hdev, 67562306a36Sopenharmony_ci struct hid_input *hi, struct hid_field *field, 67662306a36Sopenharmony_ci struct hid_usage *usage, unsigned long **bit, int *max) 67762306a36Sopenharmony_ci{ 67862306a36Sopenharmony_ci struct magicmouse_sc *msc = hid_get_drvdata(hdev); 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci if (!msc->input) 68162306a36Sopenharmony_ci msc->input = hi->input; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci /* Magic Trackpad does not give relative data after switching to MT */ 68462306a36Sopenharmony_ci if ((hi->input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD || 68562306a36Sopenharmony_ci hi->input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) && 68662306a36Sopenharmony_ci field->flags & HID_MAIN_ITEM_RELATIVE) 68762306a36Sopenharmony_ci return -1; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci return 0; 69062306a36Sopenharmony_ci} 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_cistatic int magicmouse_input_configured(struct hid_device *hdev, 69362306a36Sopenharmony_ci struct hid_input *hi) 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci{ 69662306a36Sopenharmony_ci struct magicmouse_sc *msc = hid_get_drvdata(hdev); 69762306a36Sopenharmony_ci int ret; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci ret = magicmouse_setup_input(msc->input, hdev); 70062306a36Sopenharmony_ci if (ret) { 70162306a36Sopenharmony_ci hid_err(hdev, "magicmouse setup input failed (%d)\n", ret); 70262306a36Sopenharmony_ci /* clean msc->input to notify probe() of the failure */ 70362306a36Sopenharmony_ci msc->input = NULL; 70462306a36Sopenharmony_ci return ret; 70562306a36Sopenharmony_ci } 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci return 0; 70862306a36Sopenharmony_ci} 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_cistatic int magicmouse_enable_multitouch(struct hid_device *hdev) 71162306a36Sopenharmony_ci{ 71262306a36Sopenharmony_ci const u8 *feature; 71362306a36Sopenharmony_ci const u8 feature_mt[] = { 0xD7, 0x01 }; 71462306a36Sopenharmony_ci const u8 feature_mt_mouse2[] = { 0xF1, 0x02, 0x01 }; 71562306a36Sopenharmony_ci const u8 feature_mt_trackpad2_usb[] = { 0x02, 0x01 }; 71662306a36Sopenharmony_ci const u8 feature_mt_trackpad2_bt[] = { 0xF1, 0x02, 0x01 }; 71762306a36Sopenharmony_ci u8 *buf; 71862306a36Sopenharmony_ci int ret; 71962306a36Sopenharmony_ci int feature_size; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci if (hdev->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) { 72262306a36Sopenharmony_ci if (hdev->vendor == BT_VENDOR_ID_APPLE) { 72362306a36Sopenharmony_ci feature_size = sizeof(feature_mt_trackpad2_bt); 72462306a36Sopenharmony_ci feature = feature_mt_trackpad2_bt; 72562306a36Sopenharmony_ci } else { /* USB_VENDOR_ID_APPLE */ 72662306a36Sopenharmony_ci feature_size = sizeof(feature_mt_trackpad2_usb); 72762306a36Sopenharmony_ci feature = feature_mt_trackpad2_usb; 72862306a36Sopenharmony_ci } 72962306a36Sopenharmony_ci } else if (hdev->product == USB_DEVICE_ID_APPLE_MAGICMOUSE2) { 73062306a36Sopenharmony_ci feature_size = sizeof(feature_mt_mouse2); 73162306a36Sopenharmony_ci feature = feature_mt_mouse2; 73262306a36Sopenharmony_ci } else { 73362306a36Sopenharmony_ci feature_size = sizeof(feature_mt); 73462306a36Sopenharmony_ci feature = feature_mt; 73562306a36Sopenharmony_ci } 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci buf = kmemdup(feature, feature_size, GFP_KERNEL); 73862306a36Sopenharmony_ci if (!buf) 73962306a36Sopenharmony_ci return -ENOMEM; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci ret = hid_hw_raw_request(hdev, buf[0], buf, feature_size, 74262306a36Sopenharmony_ci HID_FEATURE_REPORT, HID_REQ_SET_REPORT); 74362306a36Sopenharmony_ci kfree(buf); 74462306a36Sopenharmony_ci return ret; 74562306a36Sopenharmony_ci} 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_cistatic void magicmouse_enable_mt_work(struct work_struct *work) 74862306a36Sopenharmony_ci{ 74962306a36Sopenharmony_ci struct magicmouse_sc *msc = 75062306a36Sopenharmony_ci container_of(work, struct magicmouse_sc, work.work); 75162306a36Sopenharmony_ci int ret; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci ret = magicmouse_enable_multitouch(msc->hdev); 75462306a36Sopenharmony_ci if (ret < 0) 75562306a36Sopenharmony_ci hid_err(msc->hdev, "unable to request touch data (%d)\n", ret); 75662306a36Sopenharmony_ci} 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_cistatic int magicmouse_fetch_battery(struct hid_device *hdev) 75962306a36Sopenharmony_ci{ 76062306a36Sopenharmony_ci#ifdef CONFIG_HID_BATTERY_STRENGTH 76162306a36Sopenharmony_ci struct hid_report_enum *report_enum; 76262306a36Sopenharmony_ci struct hid_report *report; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci if (!hdev->battery || hdev->vendor != USB_VENDOR_ID_APPLE || 76562306a36Sopenharmony_ci (hdev->product != USB_DEVICE_ID_APPLE_MAGICMOUSE2 && 76662306a36Sopenharmony_ci hdev->product != USB_DEVICE_ID_APPLE_MAGICTRACKPAD2)) 76762306a36Sopenharmony_ci return -1; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci report_enum = &hdev->report_enum[hdev->battery_report_type]; 77062306a36Sopenharmony_ci report = report_enum->report_id_hash[hdev->battery_report_id]; 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci if (!report || report->maxfield < 1) 77362306a36Sopenharmony_ci return -1; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci if (hdev->battery_capacity == hdev->battery_max) 77662306a36Sopenharmony_ci return -1; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci hid_hw_request(hdev, report, HID_REQ_GET_REPORT); 77962306a36Sopenharmony_ci return 0; 78062306a36Sopenharmony_ci#else 78162306a36Sopenharmony_ci return -1; 78262306a36Sopenharmony_ci#endif 78362306a36Sopenharmony_ci} 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_cistatic void magicmouse_battery_timer_tick(struct timer_list *t) 78662306a36Sopenharmony_ci{ 78762306a36Sopenharmony_ci struct magicmouse_sc *msc = from_timer(msc, t, battery_timer); 78862306a36Sopenharmony_ci struct hid_device *hdev = msc->hdev; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci if (magicmouse_fetch_battery(hdev) == 0) { 79162306a36Sopenharmony_ci mod_timer(&msc->battery_timer, 79262306a36Sopenharmony_ci jiffies + msecs_to_jiffies(USB_BATTERY_TIMEOUT_MS)); 79362306a36Sopenharmony_ci } 79462306a36Sopenharmony_ci} 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_cistatic int magicmouse_probe(struct hid_device *hdev, 79762306a36Sopenharmony_ci const struct hid_device_id *id) 79862306a36Sopenharmony_ci{ 79962306a36Sopenharmony_ci struct magicmouse_sc *msc; 80062306a36Sopenharmony_ci struct hid_report *report; 80162306a36Sopenharmony_ci int ret; 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci msc = devm_kzalloc(&hdev->dev, sizeof(*msc), GFP_KERNEL); 80462306a36Sopenharmony_ci if (msc == NULL) { 80562306a36Sopenharmony_ci hid_err(hdev, "can't alloc magicmouse descriptor\n"); 80662306a36Sopenharmony_ci return -ENOMEM; 80762306a36Sopenharmony_ci } 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci msc->scroll_accel = SCROLL_ACCEL_DEFAULT; 81062306a36Sopenharmony_ci msc->hdev = hdev; 81162306a36Sopenharmony_ci INIT_DEFERRABLE_WORK(&msc->work, magicmouse_enable_mt_work); 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci msc->quirks = id->driver_data; 81462306a36Sopenharmony_ci hid_set_drvdata(hdev, msc); 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci ret = hid_parse(hdev); 81762306a36Sopenharmony_ci if (ret) { 81862306a36Sopenharmony_ci hid_err(hdev, "magicmouse hid parse failed\n"); 81962306a36Sopenharmony_ci return ret; 82062306a36Sopenharmony_ci } 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); 82362306a36Sopenharmony_ci if (ret) { 82462306a36Sopenharmony_ci hid_err(hdev, "magicmouse hw start failed\n"); 82562306a36Sopenharmony_ci return ret; 82662306a36Sopenharmony_ci } 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci timer_setup(&msc->battery_timer, magicmouse_battery_timer_tick, 0); 82962306a36Sopenharmony_ci mod_timer(&msc->battery_timer, 83062306a36Sopenharmony_ci jiffies + msecs_to_jiffies(USB_BATTERY_TIMEOUT_MS)); 83162306a36Sopenharmony_ci magicmouse_fetch_battery(hdev); 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci if (id->vendor == USB_VENDOR_ID_APPLE && 83462306a36Sopenharmony_ci (id->product == USB_DEVICE_ID_APPLE_MAGICMOUSE2 || 83562306a36Sopenharmony_ci (id->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2 && hdev->type != HID_TYPE_USBMOUSE))) 83662306a36Sopenharmony_ci return 0; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci if (!msc->input) { 83962306a36Sopenharmony_ci hid_err(hdev, "magicmouse input not registered\n"); 84062306a36Sopenharmony_ci ret = -ENOMEM; 84162306a36Sopenharmony_ci goto err_stop_hw; 84262306a36Sopenharmony_ci } 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci if (id->product == USB_DEVICE_ID_APPLE_MAGICMOUSE) 84562306a36Sopenharmony_ci report = hid_register_report(hdev, HID_INPUT_REPORT, 84662306a36Sopenharmony_ci MOUSE_REPORT_ID, 0); 84762306a36Sopenharmony_ci else if (id->product == USB_DEVICE_ID_APPLE_MAGICMOUSE2) 84862306a36Sopenharmony_ci report = hid_register_report(hdev, HID_INPUT_REPORT, 84962306a36Sopenharmony_ci MOUSE2_REPORT_ID, 0); 85062306a36Sopenharmony_ci else if (id->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) { 85162306a36Sopenharmony_ci if (id->vendor == BT_VENDOR_ID_APPLE) 85262306a36Sopenharmony_ci report = hid_register_report(hdev, HID_INPUT_REPORT, 85362306a36Sopenharmony_ci TRACKPAD2_BT_REPORT_ID, 0); 85462306a36Sopenharmony_ci else /* USB_VENDOR_ID_APPLE */ 85562306a36Sopenharmony_ci report = hid_register_report(hdev, HID_INPUT_REPORT, 85662306a36Sopenharmony_ci TRACKPAD2_USB_REPORT_ID, 0); 85762306a36Sopenharmony_ci } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */ 85862306a36Sopenharmony_ci report = hid_register_report(hdev, HID_INPUT_REPORT, 85962306a36Sopenharmony_ci TRACKPAD_REPORT_ID, 0); 86062306a36Sopenharmony_ci report = hid_register_report(hdev, HID_INPUT_REPORT, 86162306a36Sopenharmony_ci DOUBLE_REPORT_ID, 0); 86262306a36Sopenharmony_ci } 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci if (!report) { 86562306a36Sopenharmony_ci hid_err(hdev, "unable to register touch report\n"); 86662306a36Sopenharmony_ci ret = -ENOMEM; 86762306a36Sopenharmony_ci goto err_stop_hw; 86862306a36Sopenharmony_ci } 86962306a36Sopenharmony_ci report->size = 6; 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci /* 87262306a36Sopenharmony_ci * Some devices repond with 'invalid report id' when feature 87362306a36Sopenharmony_ci * report switching it into multitouch mode is sent to it. 87462306a36Sopenharmony_ci * 87562306a36Sopenharmony_ci * This results in -EIO from the _raw low-level transport callback, 87662306a36Sopenharmony_ci * but there seems to be no other way of switching the mode. 87762306a36Sopenharmony_ci * Thus the super-ugly hacky success check below. 87862306a36Sopenharmony_ci */ 87962306a36Sopenharmony_ci ret = magicmouse_enable_multitouch(hdev); 88062306a36Sopenharmony_ci if (ret != -EIO && ret < 0) { 88162306a36Sopenharmony_ci hid_err(hdev, "unable to request touch data (%d)\n", ret); 88262306a36Sopenharmony_ci goto err_stop_hw; 88362306a36Sopenharmony_ci } 88462306a36Sopenharmony_ci if (ret == -EIO && id->product == USB_DEVICE_ID_APPLE_MAGICMOUSE2) { 88562306a36Sopenharmony_ci schedule_delayed_work(&msc->work, msecs_to_jiffies(500)); 88662306a36Sopenharmony_ci } 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci return 0; 88962306a36Sopenharmony_cierr_stop_hw: 89062306a36Sopenharmony_ci del_timer_sync(&msc->battery_timer); 89162306a36Sopenharmony_ci hid_hw_stop(hdev); 89262306a36Sopenharmony_ci return ret; 89362306a36Sopenharmony_ci} 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_cistatic void magicmouse_remove(struct hid_device *hdev) 89662306a36Sopenharmony_ci{ 89762306a36Sopenharmony_ci struct magicmouse_sc *msc = hid_get_drvdata(hdev); 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci if (msc) { 90062306a36Sopenharmony_ci cancel_delayed_work_sync(&msc->work); 90162306a36Sopenharmony_ci del_timer_sync(&msc->battery_timer); 90262306a36Sopenharmony_ci } 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci hid_hw_stop(hdev); 90562306a36Sopenharmony_ci} 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_cistatic __u8 *magicmouse_report_fixup(struct hid_device *hdev, __u8 *rdesc, 90862306a36Sopenharmony_ci unsigned int *rsize) 90962306a36Sopenharmony_ci{ 91062306a36Sopenharmony_ci /* 91162306a36Sopenharmony_ci * Change the usage from: 91262306a36Sopenharmony_ci * 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 1) 0 91362306a36Sopenharmony_ci * 0x09, 0x0b, // Usage (Vendor Usage 0x0b) 3 91462306a36Sopenharmony_ci * To: 91562306a36Sopenharmony_ci * 0x05, 0x01, // Usage Page (Generic Desktop) 0 91662306a36Sopenharmony_ci * 0x09, 0x02, // Usage (Mouse) 2 91762306a36Sopenharmony_ci */ 91862306a36Sopenharmony_ci if (hdev->vendor == USB_VENDOR_ID_APPLE && 91962306a36Sopenharmony_ci (hdev->product == USB_DEVICE_ID_APPLE_MAGICMOUSE2 || 92062306a36Sopenharmony_ci hdev->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) && 92162306a36Sopenharmony_ci *rsize == 83 && rdesc[46] == 0x84 && rdesc[58] == 0x85) { 92262306a36Sopenharmony_ci hid_info(hdev, 92362306a36Sopenharmony_ci "fixing up magicmouse battery report descriptor\n"); 92462306a36Sopenharmony_ci *rsize = *rsize - 1; 92562306a36Sopenharmony_ci rdesc = kmemdup(rdesc + 1, *rsize, GFP_KERNEL); 92662306a36Sopenharmony_ci if (!rdesc) 92762306a36Sopenharmony_ci return NULL; 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci rdesc[0] = 0x05; 93062306a36Sopenharmony_ci rdesc[1] = 0x01; 93162306a36Sopenharmony_ci rdesc[2] = 0x09; 93262306a36Sopenharmony_ci rdesc[3] = 0x02; 93362306a36Sopenharmony_ci } 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci return rdesc; 93662306a36Sopenharmony_ci} 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_cistatic const struct hid_device_id magic_mice[] = { 93962306a36Sopenharmony_ci { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, 94062306a36Sopenharmony_ci USB_DEVICE_ID_APPLE_MAGICMOUSE), .driver_data = 0 }, 94162306a36Sopenharmony_ci { HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE, 94262306a36Sopenharmony_ci USB_DEVICE_ID_APPLE_MAGICMOUSE2), .driver_data = 0 }, 94362306a36Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, 94462306a36Sopenharmony_ci USB_DEVICE_ID_APPLE_MAGICMOUSE2), .driver_data = 0 }, 94562306a36Sopenharmony_ci { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, 94662306a36Sopenharmony_ci USB_DEVICE_ID_APPLE_MAGICTRACKPAD), .driver_data = 0 }, 94762306a36Sopenharmony_ci { HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE, 94862306a36Sopenharmony_ci USB_DEVICE_ID_APPLE_MAGICTRACKPAD2), .driver_data = 0 }, 94962306a36Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, 95062306a36Sopenharmony_ci USB_DEVICE_ID_APPLE_MAGICTRACKPAD2), .driver_data = 0 }, 95162306a36Sopenharmony_ci { } 95262306a36Sopenharmony_ci}; 95362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(hid, magic_mice); 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_cistatic struct hid_driver magicmouse_driver = { 95662306a36Sopenharmony_ci .name = "magicmouse", 95762306a36Sopenharmony_ci .id_table = magic_mice, 95862306a36Sopenharmony_ci .probe = magicmouse_probe, 95962306a36Sopenharmony_ci .remove = magicmouse_remove, 96062306a36Sopenharmony_ci .report_fixup = magicmouse_report_fixup, 96162306a36Sopenharmony_ci .raw_event = magicmouse_raw_event, 96262306a36Sopenharmony_ci .event = magicmouse_event, 96362306a36Sopenharmony_ci .input_mapping = magicmouse_input_mapping, 96462306a36Sopenharmony_ci .input_configured = magicmouse_input_configured, 96562306a36Sopenharmony_ci}; 96662306a36Sopenharmony_cimodule_hid_driver(magicmouse_driver); 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 969