18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  HID driver for some microsoft "special" devices
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (c) 1999 Andreas Gal
68c2ecf20Sopenharmony_ci *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
78c2ecf20Sopenharmony_ci *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
88c2ecf20Sopenharmony_ci *  Copyright (c) 2006-2007 Jiri Kosina
98c2ecf20Sopenharmony_ci *  Copyright (c) 2008 Jiri Slaby
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci/*
138c2ecf20Sopenharmony_ci */
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <linux/device.h>
168c2ecf20Sopenharmony_ci#include <linux/input.h>
178c2ecf20Sopenharmony_ci#include <linux/hid.h>
188c2ecf20Sopenharmony_ci#include <linux/module.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include "hid-ids.h"
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#define MS_HIDINPUT		BIT(0)
238c2ecf20Sopenharmony_ci#define MS_ERGONOMY		BIT(1)
248c2ecf20Sopenharmony_ci#define MS_PRESENTER		BIT(2)
258c2ecf20Sopenharmony_ci#define MS_RDESC		BIT(3)
268c2ecf20Sopenharmony_ci#define MS_NOGET		BIT(4)
278c2ecf20Sopenharmony_ci#define MS_DUPLICATE_USAGES	BIT(5)
288c2ecf20Sopenharmony_ci#define MS_SURFACE_DIAL		BIT(6)
298c2ecf20Sopenharmony_ci#define MS_QUIRK_FF		BIT(7)
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistruct ms_data {
328c2ecf20Sopenharmony_ci	unsigned long quirks;
338c2ecf20Sopenharmony_ci	struct hid_device *hdev;
348c2ecf20Sopenharmony_ci	struct work_struct ff_worker;
358c2ecf20Sopenharmony_ci	__u8 strong;
368c2ecf20Sopenharmony_ci	__u8 weak;
378c2ecf20Sopenharmony_ci	void *output_report_dmabuf;
388c2ecf20Sopenharmony_ci};
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#define XB1S_FF_REPORT		3
418c2ecf20Sopenharmony_ci#define ENABLE_WEAK		BIT(0)
428c2ecf20Sopenharmony_ci#define ENABLE_STRONG		BIT(1)
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cienum {
458c2ecf20Sopenharmony_ci	MAGNITUDE_STRONG = 2,
468c2ecf20Sopenharmony_ci	MAGNITUDE_WEAK,
478c2ecf20Sopenharmony_ci	MAGNITUDE_NUM
488c2ecf20Sopenharmony_ci};
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistruct xb1s_ff_report {
518c2ecf20Sopenharmony_ci	__u8	report_id;
528c2ecf20Sopenharmony_ci	__u8	enable;
538c2ecf20Sopenharmony_ci	__u8	magnitude[MAGNITUDE_NUM];
548c2ecf20Sopenharmony_ci	__u8	duration_10ms;
558c2ecf20Sopenharmony_ci	__u8	start_delay_10ms;
568c2ecf20Sopenharmony_ci	__u8	loop_count;
578c2ecf20Sopenharmony_ci} __packed;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistatic __u8 *ms_report_fixup(struct hid_device *hdev, __u8 *rdesc,
608c2ecf20Sopenharmony_ci		unsigned int *rsize)
618c2ecf20Sopenharmony_ci{
628c2ecf20Sopenharmony_ci	struct ms_data *ms = hid_get_drvdata(hdev);
638c2ecf20Sopenharmony_ci	unsigned long quirks = ms->quirks;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	/*
668c2ecf20Sopenharmony_ci	 * Microsoft Wireless Desktop Receiver (Model 1028) has
678c2ecf20Sopenharmony_ci	 * 'Usage Min/Max' where it ought to have 'Physical Min/Max'
688c2ecf20Sopenharmony_ci	 */
698c2ecf20Sopenharmony_ci	if ((quirks & MS_RDESC) && *rsize == 571 && rdesc[557] == 0x19 &&
708c2ecf20Sopenharmony_ci			rdesc[559] == 0x29) {
718c2ecf20Sopenharmony_ci		hid_info(hdev, "fixing up Microsoft Wireless Receiver Model 1028 report descriptor\n");
728c2ecf20Sopenharmony_ci		rdesc[557] = 0x35;
738c2ecf20Sopenharmony_ci		rdesc[559] = 0x45;
748c2ecf20Sopenharmony_ci	}
758c2ecf20Sopenharmony_ci	return rdesc;
768c2ecf20Sopenharmony_ci}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci#define ms_map_key_clear(c)	hid_map_usage_clear(hi, usage, bit, max, \
798c2ecf20Sopenharmony_ci					EV_KEY, (c))
808c2ecf20Sopenharmony_cistatic int ms_ergonomy_kb_quirk(struct hid_input *hi, struct hid_usage *usage,
818c2ecf20Sopenharmony_ci		unsigned long **bit, int *max)
828c2ecf20Sopenharmony_ci{
838c2ecf20Sopenharmony_ci	struct input_dev *input = hi->input;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	if ((usage->hid & HID_USAGE_PAGE) == HID_UP_CONSUMER) {
868c2ecf20Sopenharmony_ci		switch (usage->hid & HID_USAGE) {
878c2ecf20Sopenharmony_ci		/*
888c2ecf20Sopenharmony_ci		 * Microsoft uses these 2 reserved usage ids for 2 keys on
898c2ecf20Sopenharmony_ci		 * the MS office kb labelled "Office Home" and "Task Pane".
908c2ecf20Sopenharmony_ci		 */
918c2ecf20Sopenharmony_ci		case 0x29d:
928c2ecf20Sopenharmony_ci			ms_map_key_clear(KEY_PROG1);
938c2ecf20Sopenharmony_ci			return 1;
948c2ecf20Sopenharmony_ci		case 0x29e:
958c2ecf20Sopenharmony_ci			ms_map_key_clear(KEY_PROG2);
968c2ecf20Sopenharmony_ci			return 1;
978c2ecf20Sopenharmony_ci		}
988c2ecf20Sopenharmony_ci		return 0;
998c2ecf20Sopenharmony_ci	}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	if ((usage->hid & HID_USAGE_PAGE) != HID_UP_MSVENDOR)
1028c2ecf20Sopenharmony_ci		return 0;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	switch (usage->hid & HID_USAGE) {
1058c2ecf20Sopenharmony_ci	case 0xfd06: ms_map_key_clear(KEY_CHAT);	break;
1068c2ecf20Sopenharmony_ci	case 0xfd07: ms_map_key_clear(KEY_PHONE);	break;
1078c2ecf20Sopenharmony_ci	case 0xff00:
1088c2ecf20Sopenharmony_ci		/* Special keypad keys */
1098c2ecf20Sopenharmony_ci		ms_map_key_clear(KEY_KPEQUAL);
1108c2ecf20Sopenharmony_ci		set_bit(KEY_KPLEFTPAREN, input->keybit);
1118c2ecf20Sopenharmony_ci		set_bit(KEY_KPRIGHTPAREN, input->keybit);
1128c2ecf20Sopenharmony_ci		break;
1138c2ecf20Sopenharmony_ci	case 0xff01:
1148c2ecf20Sopenharmony_ci		/* Scroll wheel */
1158c2ecf20Sopenharmony_ci		hid_map_usage_clear(hi, usage, bit, max, EV_REL, REL_WHEEL);
1168c2ecf20Sopenharmony_ci		break;
1178c2ecf20Sopenharmony_ci	case 0xff02:
1188c2ecf20Sopenharmony_ci		/*
1198c2ecf20Sopenharmony_ci		 * This byte contains a copy of the modifier keys byte of a
1208c2ecf20Sopenharmony_ci		 * standard hid keyboard report, as send by interface 0
1218c2ecf20Sopenharmony_ci		 * (this usage is found on interface 1).
1228c2ecf20Sopenharmony_ci		 *
1238c2ecf20Sopenharmony_ci		 * This byte only gets send when another key in the same report
1248c2ecf20Sopenharmony_ci		 * changes state, and as such is useless, ignore it.
1258c2ecf20Sopenharmony_ci		 */
1268c2ecf20Sopenharmony_ci		return -1;
1278c2ecf20Sopenharmony_ci	case 0xff05:
1288c2ecf20Sopenharmony_ci		set_bit(EV_REP, input->evbit);
1298c2ecf20Sopenharmony_ci		ms_map_key_clear(KEY_F13);
1308c2ecf20Sopenharmony_ci		set_bit(KEY_F14, input->keybit);
1318c2ecf20Sopenharmony_ci		set_bit(KEY_F15, input->keybit);
1328c2ecf20Sopenharmony_ci		set_bit(KEY_F16, input->keybit);
1338c2ecf20Sopenharmony_ci		set_bit(KEY_F17, input->keybit);
1348c2ecf20Sopenharmony_ci		set_bit(KEY_F18, input->keybit);
1358c2ecf20Sopenharmony_ci		break;
1368c2ecf20Sopenharmony_ci	default:
1378c2ecf20Sopenharmony_ci		return 0;
1388c2ecf20Sopenharmony_ci	}
1398c2ecf20Sopenharmony_ci	return 1;
1408c2ecf20Sopenharmony_ci}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_cistatic int ms_presenter_8k_quirk(struct hid_input *hi, struct hid_usage *usage,
1438c2ecf20Sopenharmony_ci		unsigned long **bit, int *max)
1448c2ecf20Sopenharmony_ci{
1458c2ecf20Sopenharmony_ci	if ((usage->hid & HID_USAGE_PAGE) != HID_UP_MSVENDOR)
1468c2ecf20Sopenharmony_ci		return 0;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	set_bit(EV_REP, hi->input->evbit);
1498c2ecf20Sopenharmony_ci	switch (usage->hid & HID_USAGE) {
1508c2ecf20Sopenharmony_ci	case 0xfd08: ms_map_key_clear(KEY_FORWARD);	break;
1518c2ecf20Sopenharmony_ci	case 0xfd09: ms_map_key_clear(KEY_BACK);	break;
1528c2ecf20Sopenharmony_ci	case 0xfd0b: ms_map_key_clear(KEY_PLAYPAUSE);	break;
1538c2ecf20Sopenharmony_ci	case 0xfd0e: ms_map_key_clear(KEY_CLOSE);	break;
1548c2ecf20Sopenharmony_ci	case 0xfd0f: ms_map_key_clear(KEY_PLAY);	break;
1558c2ecf20Sopenharmony_ci	default:
1568c2ecf20Sopenharmony_ci		return 0;
1578c2ecf20Sopenharmony_ci	}
1588c2ecf20Sopenharmony_ci	return 1;
1598c2ecf20Sopenharmony_ci}
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_cistatic int ms_surface_dial_quirk(struct hid_input *hi, struct hid_field *field,
1628c2ecf20Sopenharmony_ci		struct hid_usage *usage, unsigned long **bit, int *max)
1638c2ecf20Sopenharmony_ci{
1648c2ecf20Sopenharmony_ci	switch (usage->hid & HID_USAGE_PAGE) {
1658c2ecf20Sopenharmony_ci	case 0xff070000:
1668c2ecf20Sopenharmony_ci	case HID_UP_DIGITIZER:
1678c2ecf20Sopenharmony_ci		/* ignore those axis */
1688c2ecf20Sopenharmony_ci		return -1;
1698c2ecf20Sopenharmony_ci	case HID_UP_GENDESK:
1708c2ecf20Sopenharmony_ci		switch (usage->hid) {
1718c2ecf20Sopenharmony_ci		case HID_GD_X:
1728c2ecf20Sopenharmony_ci		case HID_GD_Y:
1738c2ecf20Sopenharmony_ci		case HID_GD_RFKILL_BTN:
1748c2ecf20Sopenharmony_ci			/* ignore those axis */
1758c2ecf20Sopenharmony_ci			return -1;
1768c2ecf20Sopenharmony_ci		}
1778c2ecf20Sopenharmony_ci	}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	return 0;
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_cistatic int ms_input_mapping(struct hid_device *hdev, struct hid_input *hi,
1838c2ecf20Sopenharmony_ci		struct hid_field *field, struct hid_usage *usage,
1848c2ecf20Sopenharmony_ci		unsigned long **bit, int *max)
1858c2ecf20Sopenharmony_ci{
1868c2ecf20Sopenharmony_ci	struct ms_data *ms = hid_get_drvdata(hdev);
1878c2ecf20Sopenharmony_ci	unsigned long quirks = ms->quirks;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	if (quirks & MS_ERGONOMY) {
1908c2ecf20Sopenharmony_ci		int ret = ms_ergonomy_kb_quirk(hi, usage, bit, max);
1918c2ecf20Sopenharmony_ci		if (ret)
1928c2ecf20Sopenharmony_ci			return ret;
1938c2ecf20Sopenharmony_ci	}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	if ((quirks & MS_PRESENTER) &&
1968c2ecf20Sopenharmony_ci			ms_presenter_8k_quirk(hi, usage, bit, max))
1978c2ecf20Sopenharmony_ci		return 1;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	if (quirks & MS_SURFACE_DIAL) {
2008c2ecf20Sopenharmony_ci		int ret = ms_surface_dial_quirk(hi, field, usage, bit, max);
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci		if (ret)
2038c2ecf20Sopenharmony_ci			return ret;
2048c2ecf20Sopenharmony_ci	}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	return 0;
2078c2ecf20Sopenharmony_ci}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_cistatic int ms_input_mapped(struct hid_device *hdev, struct hid_input *hi,
2108c2ecf20Sopenharmony_ci		struct hid_field *field, struct hid_usage *usage,
2118c2ecf20Sopenharmony_ci		unsigned long **bit, int *max)
2128c2ecf20Sopenharmony_ci{
2138c2ecf20Sopenharmony_ci	struct ms_data *ms = hid_get_drvdata(hdev);
2148c2ecf20Sopenharmony_ci	unsigned long quirks = ms->quirks;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	if (quirks & MS_DUPLICATE_USAGES)
2178c2ecf20Sopenharmony_ci		clear_bit(usage->code, *bit);
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	return 0;
2208c2ecf20Sopenharmony_ci}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_cistatic int ms_event(struct hid_device *hdev, struct hid_field *field,
2238c2ecf20Sopenharmony_ci		struct hid_usage *usage, __s32 value)
2248c2ecf20Sopenharmony_ci{
2258c2ecf20Sopenharmony_ci	struct ms_data *ms = hid_get_drvdata(hdev);
2268c2ecf20Sopenharmony_ci	unsigned long quirks = ms->quirks;
2278c2ecf20Sopenharmony_ci	struct input_dev *input;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	if (!(hdev->claimed & HID_CLAIMED_INPUT) || !field->hidinput ||
2308c2ecf20Sopenharmony_ci			!usage->type)
2318c2ecf20Sopenharmony_ci		return 0;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	input = field->hidinput->input;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	/* Handling MS keyboards special buttons */
2368c2ecf20Sopenharmony_ci	if (quirks & MS_ERGONOMY && usage->hid == (HID_UP_MSVENDOR | 0xff00)) {
2378c2ecf20Sopenharmony_ci		/* Special keypad keys */
2388c2ecf20Sopenharmony_ci		input_report_key(input, KEY_KPEQUAL, value & 0x01);
2398c2ecf20Sopenharmony_ci		input_report_key(input, KEY_KPLEFTPAREN, value & 0x02);
2408c2ecf20Sopenharmony_ci		input_report_key(input, KEY_KPRIGHTPAREN, value & 0x04);
2418c2ecf20Sopenharmony_ci		return 1;
2428c2ecf20Sopenharmony_ci	}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	if (quirks & MS_ERGONOMY && usage->hid == (HID_UP_MSVENDOR | 0xff01)) {
2458c2ecf20Sopenharmony_ci		/* Scroll wheel */
2468c2ecf20Sopenharmony_ci		int step = ((value & 0x60) >> 5) + 1;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci		switch (value & 0x1f) {
2498c2ecf20Sopenharmony_ci		case 0x01:
2508c2ecf20Sopenharmony_ci			input_report_rel(input, REL_WHEEL, step);
2518c2ecf20Sopenharmony_ci			break;
2528c2ecf20Sopenharmony_ci		case 0x1f:
2538c2ecf20Sopenharmony_ci			input_report_rel(input, REL_WHEEL, -step);
2548c2ecf20Sopenharmony_ci			break;
2558c2ecf20Sopenharmony_ci		}
2568c2ecf20Sopenharmony_ci		return 1;
2578c2ecf20Sopenharmony_ci	}
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	if (quirks & MS_ERGONOMY && usage->hid == (HID_UP_MSVENDOR | 0xff05)) {
2608c2ecf20Sopenharmony_ci		static unsigned int last_key = 0;
2618c2ecf20Sopenharmony_ci		unsigned int key = 0;
2628c2ecf20Sopenharmony_ci		switch (value) {
2638c2ecf20Sopenharmony_ci		case 0x01: key = KEY_F14; break;
2648c2ecf20Sopenharmony_ci		case 0x02: key = KEY_F15; break;
2658c2ecf20Sopenharmony_ci		case 0x04: key = KEY_F16; break;
2668c2ecf20Sopenharmony_ci		case 0x08: key = KEY_F17; break;
2678c2ecf20Sopenharmony_ci		case 0x10: key = KEY_F18; break;
2688c2ecf20Sopenharmony_ci		}
2698c2ecf20Sopenharmony_ci		if (key) {
2708c2ecf20Sopenharmony_ci			input_event(input, usage->type, key, 1);
2718c2ecf20Sopenharmony_ci			last_key = key;
2728c2ecf20Sopenharmony_ci		} else
2738c2ecf20Sopenharmony_ci			input_event(input, usage->type, last_key, 0);
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci		return 1;
2768c2ecf20Sopenharmony_ci	}
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	return 0;
2798c2ecf20Sopenharmony_ci}
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_cistatic void ms_ff_worker(struct work_struct *work)
2828c2ecf20Sopenharmony_ci{
2838c2ecf20Sopenharmony_ci	struct ms_data *ms = container_of(work, struct ms_data, ff_worker);
2848c2ecf20Sopenharmony_ci	struct hid_device *hdev = ms->hdev;
2858c2ecf20Sopenharmony_ci	struct xb1s_ff_report *r = ms->output_report_dmabuf;
2868c2ecf20Sopenharmony_ci	int ret;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	memset(r, 0, sizeof(*r));
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	r->report_id = XB1S_FF_REPORT;
2918c2ecf20Sopenharmony_ci	r->enable = ENABLE_WEAK | ENABLE_STRONG;
2928c2ecf20Sopenharmony_ci	/*
2938c2ecf20Sopenharmony_ci	 * Specifying maximum duration and maximum loop count should
2948c2ecf20Sopenharmony_ci	 * cover maximum duration of a single effect, which is 65536
2958c2ecf20Sopenharmony_ci	 * ms
2968c2ecf20Sopenharmony_ci	 */
2978c2ecf20Sopenharmony_ci	r->duration_10ms = U8_MAX;
2988c2ecf20Sopenharmony_ci	r->loop_count = U8_MAX;
2998c2ecf20Sopenharmony_ci	r->magnitude[MAGNITUDE_STRONG] = ms->strong; /* left actuator */
3008c2ecf20Sopenharmony_ci	r->magnitude[MAGNITUDE_WEAK] = ms->weak;     /* right actuator */
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	ret = hid_hw_output_report(hdev, (__u8 *)r, sizeof(*r));
3038c2ecf20Sopenharmony_ci	if (ret < 0)
3048c2ecf20Sopenharmony_ci		hid_warn(hdev, "failed to send FF report\n");
3058c2ecf20Sopenharmony_ci}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_cistatic int ms_play_effect(struct input_dev *dev, void *data,
3088c2ecf20Sopenharmony_ci			  struct ff_effect *effect)
3098c2ecf20Sopenharmony_ci{
3108c2ecf20Sopenharmony_ci	struct hid_device *hid = input_get_drvdata(dev);
3118c2ecf20Sopenharmony_ci	struct ms_data *ms = hid_get_drvdata(hid);
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	if (effect->type != FF_RUMBLE)
3148c2ecf20Sopenharmony_ci		return 0;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	/*
3178c2ecf20Sopenharmony_ci	 * Magnitude is 0..100 so scale the 16-bit input here
3188c2ecf20Sopenharmony_ci	 */
3198c2ecf20Sopenharmony_ci	ms->strong = ((u32) effect->u.rumble.strong_magnitude * 100) / U16_MAX;
3208c2ecf20Sopenharmony_ci	ms->weak = ((u32) effect->u.rumble.weak_magnitude * 100) / U16_MAX;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	schedule_work(&ms->ff_worker);
3238c2ecf20Sopenharmony_ci	return 0;
3248c2ecf20Sopenharmony_ci}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_cistatic int ms_init_ff(struct hid_device *hdev)
3278c2ecf20Sopenharmony_ci{
3288c2ecf20Sopenharmony_ci	struct hid_input *hidinput;
3298c2ecf20Sopenharmony_ci	struct input_dev *input_dev;
3308c2ecf20Sopenharmony_ci	struct ms_data *ms = hid_get_drvdata(hdev);
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	if (list_empty(&hdev->inputs)) {
3338c2ecf20Sopenharmony_ci		hid_err(hdev, "no inputs found\n");
3348c2ecf20Sopenharmony_ci		return -ENODEV;
3358c2ecf20Sopenharmony_ci	}
3368c2ecf20Sopenharmony_ci	hidinput = list_entry(hdev->inputs.next, struct hid_input, list);
3378c2ecf20Sopenharmony_ci	input_dev = hidinput->input;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	if (!(ms->quirks & MS_QUIRK_FF))
3408c2ecf20Sopenharmony_ci		return 0;
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	ms->hdev = hdev;
3438c2ecf20Sopenharmony_ci	INIT_WORK(&ms->ff_worker, ms_ff_worker);
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	ms->output_report_dmabuf = devm_kzalloc(&hdev->dev,
3468c2ecf20Sopenharmony_ci						sizeof(struct xb1s_ff_report),
3478c2ecf20Sopenharmony_ci						GFP_KERNEL);
3488c2ecf20Sopenharmony_ci	if (ms->output_report_dmabuf == NULL)
3498c2ecf20Sopenharmony_ci		return -ENOMEM;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	input_set_capability(input_dev, EV_FF, FF_RUMBLE);
3528c2ecf20Sopenharmony_ci	return input_ff_create_memless(input_dev, NULL, ms_play_effect);
3538c2ecf20Sopenharmony_ci}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_cistatic void ms_remove_ff(struct hid_device *hdev)
3568c2ecf20Sopenharmony_ci{
3578c2ecf20Sopenharmony_ci	struct ms_data *ms = hid_get_drvdata(hdev);
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	if (!(ms->quirks & MS_QUIRK_FF))
3608c2ecf20Sopenharmony_ci		return;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	cancel_work_sync(&ms->ff_worker);
3638c2ecf20Sopenharmony_ci}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_cistatic int ms_probe(struct hid_device *hdev, const struct hid_device_id *id)
3668c2ecf20Sopenharmony_ci{
3678c2ecf20Sopenharmony_ci	unsigned long quirks = id->driver_data;
3688c2ecf20Sopenharmony_ci	struct ms_data *ms;
3698c2ecf20Sopenharmony_ci	int ret;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	ms = devm_kzalloc(&hdev->dev, sizeof(*ms), GFP_KERNEL);
3728c2ecf20Sopenharmony_ci	if (ms == NULL)
3738c2ecf20Sopenharmony_ci		return -ENOMEM;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	ms->quirks = quirks;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	hid_set_drvdata(hdev, ms);
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	if (quirks & MS_NOGET)
3808c2ecf20Sopenharmony_ci		hdev->quirks |= HID_QUIRK_NOGET;
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	if (quirks & MS_SURFACE_DIAL)
3838c2ecf20Sopenharmony_ci		hdev->quirks |= HID_QUIRK_INPUT_PER_APP;
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	ret = hid_parse(hdev);
3868c2ecf20Sopenharmony_ci	if (ret) {
3878c2ecf20Sopenharmony_ci		hid_err(hdev, "parse failed\n");
3888c2ecf20Sopenharmony_ci		goto err_free;
3898c2ecf20Sopenharmony_ci	}
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT | ((quirks & MS_HIDINPUT) ?
3928c2ecf20Sopenharmony_ci				HID_CONNECT_HIDINPUT_FORCE : 0));
3938c2ecf20Sopenharmony_ci	if (ret) {
3948c2ecf20Sopenharmony_ci		hid_err(hdev, "hw start failed\n");
3958c2ecf20Sopenharmony_ci		goto err_free;
3968c2ecf20Sopenharmony_ci	}
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	ret = ms_init_ff(hdev);
3998c2ecf20Sopenharmony_ci	if (ret)
4008c2ecf20Sopenharmony_ci		hid_err(hdev, "could not initialize ff, continuing anyway");
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	return 0;
4038c2ecf20Sopenharmony_cierr_free:
4048c2ecf20Sopenharmony_ci	return ret;
4058c2ecf20Sopenharmony_ci}
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_cistatic void ms_remove(struct hid_device *hdev)
4088c2ecf20Sopenharmony_ci{
4098c2ecf20Sopenharmony_ci	hid_hw_stop(hdev);
4108c2ecf20Sopenharmony_ci	ms_remove_ff(hdev);
4118c2ecf20Sopenharmony_ci}
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_cistatic const struct hid_device_id ms_devices[] = {
4148c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV),
4158c2ecf20Sopenharmony_ci		.driver_data = MS_HIDINPUT },
4168c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_OFFICE_KB),
4178c2ecf20Sopenharmony_ci		.driver_data = MS_ERGONOMY },
4188c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K),
4198c2ecf20Sopenharmony_ci		.driver_data = MS_ERGONOMY },
4208c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K_JP),
4218c2ecf20Sopenharmony_ci		.driver_data = MS_ERGONOMY },
4228c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE7K),
4238c2ecf20Sopenharmony_ci		.driver_data = MS_ERGONOMY },
4248c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_LK6K),
4258c2ecf20Sopenharmony_ci		.driver_data = MS_ERGONOMY | MS_RDESC },
4268c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_USB),
4278c2ecf20Sopenharmony_ci		.driver_data = MS_PRESENTER },
4288c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K),
4298c2ecf20Sopenharmony_ci		.driver_data = MS_ERGONOMY },
4308c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_7K),
4318c2ecf20Sopenharmony_ci		.driver_data = MS_ERGONOMY },
4328c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_600),
4338c2ecf20Sopenharmony_ci		.driver_data = MS_ERGONOMY },
4348c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_3KV1),
4358c2ecf20Sopenharmony_ci		.driver_data = MS_ERGONOMY },
4368c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0),
4378c2ecf20Sopenharmony_ci		.driver_data = MS_NOGET },
4388c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_MOUSE_4500),
4398c2ecf20Sopenharmony_ci		.driver_data = MS_DUPLICATE_USAGES },
4408c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_POWER_COVER),
4418c2ecf20Sopenharmony_ci		.driver_data = MS_HIDINPUT },
4428c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_KEYBOARD),
4438c2ecf20Sopenharmony_ci		.driver_data = MS_ERGONOMY},
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT),
4468c2ecf20Sopenharmony_ci		.driver_data = MS_PRESENTER },
4478c2ecf20Sopenharmony_ci	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, 0x091B),
4488c2ecf20Sopenharmony_ci		.driver_data = MS_SURFACE_DIAL },
4498c2ecf20Sopenharmony_ci	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_XBOX_ONE_S_CONTROLLER),
4508c2ecf20Sopenharmony_ci		.driver_data = MS_QUIRK_FF },
4518c2ecf20Sopenharmony_ci	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_8BITDO_SN30_PRO_PLUS),
4528c2ecf20Sopenharmony_ci		.driver_data = MS_QUIRK_FF },
4538c2ecf20Sopenharmony_ci	{ }
4548c2ecf20Sopenharmony_ci};
4558c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(hid, ms_devices);
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_cistatic struct hid_driver ms_driver = {
4588c2ecf20Sopenharmony_ci	.name = "microsoft",
4598c2ecf20Sopenharmony_ci	.id_table = ms_devices,
4608c2ecf20Sopenharmony_ci	.report_fixup = ms_report_fixup,
4618c2ecf20Sopenharmony_ci	.input_mapping = ms_input_mapping,
4628c2ecf20Sopenharmony_ci	.input_mapped = ms_input_mapped,
4638c2ecf20Sopenharmony_ci	.event = ms_event,
4648c2ecf20Sopenharmony_ci	.probe = ms_probe,
4658c2ecf20Sopenharmony_ci	.remove = ms_remove,
4668c2ecf20Sopenharmony_ci};
4678c2ecf20Sopenharmony_cimodule_hid_driver(ms_driver);
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
470