109308562SRahul Rameshbabu // SPDX-License-Identifier: GPL-2.0-or-later
209308562SRahul Rameshbabu /*
309308562SRahul Rameshbabu  *  Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES.  All rights reserved.
409308562SRahul Rameshbabu  *
509308562SRahul Rameshbabu  *  HID driver for NVIDIA SHIELD peripherals.
609308562SRahul Rameshbabu  */
709308562SRahul Rameshbabu 
809308562SRahul Rameshbabu #include <linux/hid.h>
909308562SRahul Rameshbabu #include <linux/input-event-codes.h>
1009308562SRahul Rameshbabu #include <linux/input.h>
1109308562SRahul Rameshbabu #include <linux/module.h>
1209308562SRahul Rameshbabu #include <linux/spinlock.h>
1309308562SRahul Rameshbabu #include <linux/workqueue.h>
1409308562SRahul Rameshbabu 
1509308562SRahul Rameshbabu #include "hid-ids.h"
1609308562SRahul Rameshbabu 
1709308562SRahul Rameshbabu #define NOT_INIT_STR "NOT INITIALIZED"
18*13d02c69SRahul Rameshbabu #define android_map_key(c) hid_map_usage(hi, usage, bit, max, EV_KEY, (c))
19*13d02c69SRahul Rameshbabu 
20*13d02c69SRahul Rameshbabu enum {
21*13d02c69SRahul Rameshbabu 	HID_USAGE_ANDROID_PLAYPAUSE_BTN = 0xcd, /* Double-tap volume slider */
22*13d02c69SRahul Rameshbabu 	HID_USAGE_ANDROID_VOLUMEUP_BTN = 0xe9,
23*13d02c69SRahul Rameshbabu 	HID_USAGE_ANDROID_VOLUMEDOWN_BTN = 0xea,
24*13d02c69SRahul Rameshbabu 	HID_USAGE_ANDROID_SEARCH_BTN = 0x221, /* NVIDIA btn on Thunderstrike */
25*13d02c69SRahul Rameshbabu 	HID_USAGE_ANDROID_HOME_BTN = 0x223,
26*13d02c69SRahul Rameshbabu 	HID_USAGE_ANDROID_BACK_BTN = 0x224,
27*13d02c69SRahul Rameshbabu };
2809308562SRahul Rameshbabu 
2909308562SRahul Rameshbabu enum {
3009308562SRahul Rameshbabu 	SHIELD_FW_VERSION_INITIALIZED = 0,
3109308562SRahul Rameshbabu 	SHIELD_BOARD_INFO_INITIALIZED,
3209308562SRahul Rameshbabu };
3309308562SRahul Rameshbabu 
3409308562SRahul Rameshbabu enum {
3509308562SRahul Rameshbabu 	THUNDERSTRIKE_FW_VERSION_UPDATE = 0,
3609308562SRahul Rameshbabu 	THUNDERSTRIKE_BOARD_INFO_UPDATE,
3709308562SRahul Rameshbabu 	THUNDERSTRIKE_HAPTICS_UPDATE,
3809308562SRahul Rameshbabu };
3909308562SRahul Rameshbabu 
4009308562SRahul Rameshbabu enum {
4109308562SRahul Rameshbabu 	THUNDERSTRIKE_HOSTCMD_REPORT_SIZE = 33,
4209308562SRahul Rameshbabu 	THUNDERSTRIKE_HOSTCMD_REQ_REPORT_ID = 0x4,
4309308562SRahul Rameshbabu 	THUNDERSTRIKE_HOSTCMD_RESP_REPORT_ID = 0x3,
4409308562SRahul Rameshbabu };
4509308562SRahul Rameshbabu 
4609308562SRahul Rameshbabu enum {
4709308562SRahul Rameshbabu 	THUNDERSTRIKE_HOSTCMD_ID_FW_VERSION = 1,
4809308562SRahul Rameshbabu 	THUNDERSTRIKE_HOSTCMD_ID_BOARD_INFO = 16,
4909308562SRahul Rameshbabu 	THUNDERSTRIKE_HOSTCMD_ID_USB_INIT = 53,
5009308562SRahul Rameshbabu 	THUNDERSTRIKE_HOSTCMD_ID_HAPTICS = 57,
5109308562SRahul Rameshbabu 	THUNDERSTRIKE_HOSTCMD_ID_BLUETOOTH_INIT = 58,
5209308562SRahul Rameshbabu };
5309308562SRahul Rameshbabu 
5409308562SRahul Rameshbabu struct thunderstrike_hostcmd_board_info {
5509308562SRahul Rameshbabu 	__le16 revision;
5609308562SRahul Rameshbabu 	__le16 serial[7];
5709308562SRahul Rameshbabu };
5809308562SRahul Rameshbabu 
5909308562SRahul Rameshbabu struct thunderstrike_hostcmd_haptics {
6009308562SRahul Rameshbabu 	u8 motor_left;
6109308562SRahul Rameshbabu 	u8 motor_right;
6209308562SRahul Rameshbabu };
6309308562SRahul Rameshbabu 
6409308562SRahul Rameshbabu struct thunderstrike_hostcmd_resp_report {
6509308562SRahul Rameshbabu 	u8 report_id; /* THUNDERSTRIKE_HOSTCMD_RESP_REPORT_ID */
6609308562SRahul Rameshbabu 	u8 cmd_id;
6709308562SRahul Rameshbabu 	u8 reserved_at_10;
6809308562SRahul Rameshbabu 
6909308562SRahul Rameshbabu 	union {
7009308562SRahul Rameshbabu 		struct thunderstrike_hostcmd_board_info board_info;
7109308562SRahul Rameshbabu 		struct thunderstrike_hostcmd_haptics motors;
7209308562SRahul Rameshbabu 		__le16 fw_version;
7309308562SRahul Rameshbabu 		u8 payload[30];
7409308562SRahul Rameshbabu 	};
7509308562SRahul Rameshbabu } __packed;
7609308562SRahul Rameshbabu static_assert(sizeof(struct thunderstrike_hostcmd_resp_report) ==
7709308562SRahul Rameshbabu 	      THUNDERSTRIKE_HOSTCMD_REPORT_SIZE);
7809308562SRahul Rameshbabu 
7909308562SRahul Rameshbabu struct thunderstrike_hostcmd_req_report {
8009308562SRahul Rameshbabu 	u8 report_id; /* THUNDERSTRIKE_HOSTCMD_REQ_REPORT_ID */
8109308562SRahul Rameshbabu 	u8 cmd_id;
8209308562SRahul Rameshbabu 	u8 reserved_at_10;
8309308562SRahul Rameshbabu 
8409308562SRahul Rameshbabu 	struct {
8509308562SRahul Rameshbabu 		u8 update;
8609308562SRahul Rameshbabu 		struct thunderstrike_hostcmd_haptics motors;
8709308562SRahul Rameshbabu 	} haptics;
8809308562SRahul Rameshbabu 	u8 reserved_at_30[27];
8909308562SRahul Rameshbabu } __packed;
9009308562SRahul Rameshbabu static_assert(sizeof(struct thunderstrike_hostcmd_req_report) ==
9109308562SRahul Rameshbabu 	      THUNDERSTRIKE_HOSTCMD_REPORT_SIZE);
9209308562SRahul Rameshbabu 
9309308562SRahul Rameshbabu /* Common struct for shield accessories. */
9409308562SRahul Rameshbabu struct shield_device {
9509308562SRahul Rameshbabu 	struct hid_device *hdev;
9609308562SRahul Rameshbabu 
9709308562SRahul Rameshbabu 	unsigned long initialized_flags;
9809308562SRahul Rameshbabu 	const char *codename;
9909308562SRahul Rameshbabu 	u16 fw_version;
10009308562SRahul Rameshbabu 	struct {
10109308562SRahul Rameshbabu 		u16 revision;
10209308562SRahul Rameshbabu 		char serial_number[15];
10309308562SRahul Rameshbabu 	} board_info;
10409308562SRahul Rameshbabu };
10509308562SRahul Rameshbabu 
10609308562SRahul Rameshbabu struct thunderstrike {
10709308562SRahul Rameshbabu 	struct shield_device base;
10809308562SRahul Rameshbabu 
10909308562SRahul Rameshbabu 	/* Sub-devices */
11009308562SRahul Rameshbabu 	struct input_dev *haptics_dev;
11109308562SRahul Rameshbabu 
11209308562SRahul Rameshbabu 	/* Resources */
11309308562SRahul Rameshbabu 	void *req_report_dmabuf;
11409308562SRahul Rameshbabu 	unsigned long update_flags;
11509308562SRahul Rameshbabu 	struct thunderstrike_hostcmd_haptics haptics_val;
11609308562SRahul Rameshbabu 	spinlock_t haptics_update_lock;
11709308562SRahul Rameshbabu 	struct work_struct hostcmd_req_work;
11809308562SRahul Rameshbabu };
11909308562SRahul Rameshbabu 
12009308562SRahul Rameshbabu static inline void thunderstrike_hostcmd_req_report_init(
12109308562SRahul Rameshbabu 	struct thunderstrike_hostcmd_req_report *report, u8 cmd_id)
12209308562SRahul Rameshbabu {
12309308562SRahul Rameshbabu 	memset(report, 0, sizeof(*report));
12409308562SRahul Rameshbabu 	report->report_id = THUNDERSTRIKE_HOSTCMD_REQ_REPORT_ID;
12509308562SRahul Rameshbabu 	report->cmd_id = cmd_id;
12609308562SRahul Rameshbabu }
12709308562SRahul Rameshbabu 
12809308562SRahul Rameshbabu static inline void shield_strrev(char *dest, size_t len, u16 rev)
12909308562SRahul Rameshbabu {
13009308562SRahul Rameshbabu 	dest[0] = ('A' - 1) + (rev >> 8);
13109308562SRahul Rameshbabu 	snprintf(&dest[1], len - 1, "%02X", 0xff & rev);
13209308562SRahul Rameshbabu }
13309308562SRahul Rameshbabu 
13409308562SRahul Rameshbabu static struct input_dev *shield_allocate_input_dev(struct hid_device *hdev,
13509308562SRahul Rameshbabu 						   const char *name_suffix)
13609308562SRahul Rameshbabu {
13709308562SRahul Rameshbabu 	struct input_dev *idev;
13809308562SRahul Rameshbabu 
13909308562SRahul Rameshbabu 	idev = input_allocate_device();
14009308562SRahul Rameshbabu 	if (!idev)
14109308562SRahul Rameshbabu 		goto err_device;
14209308562SRahul Rameshbabu 
14309308562SRahul Rameshbabu 	idev->id.bustype = hdev->bus;
14409308562SRahul Rameshbabu 	idev->id.vendor = hdev->vendor;
14509308562SRahul Rameshbabu 	idev->id.product = hdev->product;
14609308562SRahul Rameshbabu 	idev->id.version = hdev->version;
14709308562SRahul Rameshbabu 	idev->uniq = hdev->uniq;
14809308562SRahul Rameshbabu 	idev->name = devm_kasprintf(&idev->dev, GFP_KERNEL, "%s %s", hdev->name,
14909308562SRahul Rameshbabu 				    name_suffix);
15009308562SRahul Rameshbabu 	if (!idev->name)
15109308562SRahul Rameshbabu 		goto err_name;
15209308562SRahul Rameshbabu 
15309308562SRahul Rameshbabu 	input_set_drvdata(idev, hdev);
15409308562SRahul Rameshbabu 
15509308562SRahul Rameshbabu 	return idev;
15609308562SRahul Rameshbabu 
15709308562SRahul Rameshbabu err_name:
15809308562SRahul Rameshbabu 	input_free_device(idev);
15909308562SRahul Rameshbabu err_device:
16009308562SRahul Rameshbabu 	return ERR_PTR(-ENOMEM);
16109308562SRahul Rameshbabu }
16209308562SRahul Rameshbabu 
16309308562SRahul Rameshbabu static struct input_dev *shield_haptics_create(
16409308562SRahul Rameshbabu 	struct shield_device *dev,
16509308562SRahul Rameshbabu 	int (*play_effect)(struct input_dev *, void *, struct ff_effect *))
16609308562SRahul Rameshbabu {
16709308562SRahul Rameshbabu 	struct input_dev *haptics;
16809308562SRahul Rameshbabu 	int ret;
16909308562SRahul Rameshbabu 
17009308562SRahul Rameshbabu 	if (!IS_ENABLED(CONFIG_NVIDIA_SHIELD_FF))
17109308562SRahul Rameshbabu 		return NULL;
17209308562SRahul Rameshbabu 
17309308562SRahul Rameshbabu 	haptics = shield_allocate_input_dev(dev->hdev, "Haptics");
17409308562SRahul Rameshbabu 	if (IS_ERR(haptics))
17509308562SRahul Rameshbabu 		return haptics;
17609308562SRahul Rameshbabu 
17709308562SRahul Rameshbabu 	input_set_capability(haptics, EV_FF, FF_RUMBLE);
17809308562SRahul Rameshbabu 	input_ff_create_memless(haptics, NULL, play_effect);
17909308562SRahul Rameshbabu 
18009308562SRahul Rameshbabu 	ret = input_register_device(haptics);
18109308562SRahul Rameshbabu 	if (ret)
18209308562SRahul Rameshbabu 		goto err;
18309308562SRahul Rameshbabu 
18409308562SRahul Rameshbabu 	return haptics;
18509308562SRahul Rameshbabu 
18609308562SRahul Rameshbabu err:
18709308562SRahul Rameshbabu 	input_free_device(haptics);
18809308562SRahul Rameshbabu 	return ERR_PTR(ret);
18909308562SRahul Rameshbabu }
19009308562SRahul Rameshbabu 
19109308562SRahul Rameshbabu static inline void thunderstrike_send_hostcmd_request(struct thunderstrike *ts)
19209308562SRahul Rameshbabu {
19309308562SRahul Rameshbabu 	struct thunderstrike_hostcmd_req_report *report = ts->req_report_dmabuf;
19409308562SRahul Rameshbabu 	struct shield_device *shield_dev = &ts->base;
19509308562SRahul Rameshbabu 	int ret;
19609308562SRahul Rameshbabu 
19709308562SRahul Rameshbabu 	ret = hid_hw_raw_request(shield_dev->hdev, report->report_id,
19809308562SRahul Rameshbabu 				 ts->req_report_dmabuf,
19909308562SRahul Rameshbabu 				 THUNDERSTRIKE_HOSTCMD_REPORT_SIZE,
20009308562SRahul Rameshbabu 				 HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
20109308562SRahul Rameshbabu 
20209308562SRahul Rameshbabu 	if (ret < 0) {
20309308562SRahul Rameshbabu 		hid_err(shield_dev->hdev,
20409308562SRahul Rameshbabu 			"Failed to output Thunderstrike HOSTCMD request HID report due to %pe\n",
20509308562SRahul Rameshbabu 			ERR_PTR(ret));
20609308562SRahul Rameshbabu 	}
20709308562SRahul Rameshbabu }
20809308562SRahul Rameshbabu 
20909308562SRahul Rameshbabu static void thunderstrike_hostcmd_req_work_handler(struct work_struct *work)
21009308562SRahul Rameshbabu {
21109308562SRahul Rameshbabu 	struct thunderstrike *ts =
21209308562SRahul Rameshbabu 		container_of(work, struct thunderstrike, hostcmd_req_work);
21309308562SRahul Rameshbabu 	struct thunderstrike_hostcmd_req_report *report;
21409308562SRahul Rameshbabu 	unsigned long flags;
21509308562SRahul Rameshbabu 
21609308562SRahul Rameshbabu 	report = ts->req_report_dmabuf;
21709308562SRahul Rameshbabu 
21809308562SRahul Rameshbabu 	if (test_and_clear_bit(THUNDERSTRIKE_FW_VERSION_UPDATE, &ts->update_flags)) {
21909308562SRahul Rameshbabu 		thunderstrike_hostcmd_req_report_init(
22009308562SRahul Rameshbabu 			report, THUNDERSTRIKE_HOSTCMD_ID_FW_VERSION);
22109308562SRahul Rameshbabu 		thunderstrike_send_hostcmd_request(ts);
22209308562SRahul Rameshbabu 	}
22309308562SRahul Rameshbabu 
22409308562SRahul Rameshbabu 	if (test_and_clear_bit(THUNDERSTRIKE_BOARD_INFO_UPDATE, &ts->update_flags)) {
22509308562SRahul Rameshbabu 		thunderstrike_hostcmd_req_report_init(
22609308562SRahul Rameshbabu 			report, THUNDERSTRIKE_HOSTCMD_ID_BOARD_INFO);
22709308562SRahul Rameshbabu 		thunderstrike_send_hostcmd_request(ts);
22809308562SRahul Rameshbabu 	}
22909308562SRahul Rameshbabu 
23009308562SRahul Rameshbabu 	if (test_and_clear_bit(THUNDERSTRIKE_HAPTICS_UPDATE, &ts->update_flags)) {
23109308562SRahul Rameshbabu 		thunderstrike_hostcmd_req_report_init(
23209308562SRahul Rameshbabu 			report, THUNDERSTRIKE_HOSTCMD_ID_HAPTICS);
23309308562SRahul Rameshbabu 
23409308562SRahul Rameshbabu 		report->haptics.update = 1;
23509308562SRahul Rameshbabu 		spin_lock_irqsave(&ts->haptics_update_lock, flags);
23609308562SRahul Rameshbabu 		report->haptics.motors = ts->haptics_val;
23709308562SRahul Rameshbabu 		spin_unlock_irqrestore(&ts->haptics_update_lock, flags);
23809308562SRahul Rameshbabu 
23909308562SRahul Rameshbabu 		thunderstrike_send_hostcmd_request(ts);
24009308562SRahul Rameshbabu 	}
24109308562SRahul Rameshbabu }
24209308562SRahul Rameshbabu 
24309308562SRahul Rameshbabu static inline void thunderstrike_request_firmware_version(struct thunderstrike *ts)
24409308562SRahul Rameshbabu {
24509308562SRahul Rameshbabu 	set_bit(THUNDERSTRIKE_FW_VERSION_UPDATE, &ts->update_flags);
24609308562SRahul Rameshbabu 	schedule_work(&ts->hostcmd_req_work);
24709308562SRahul Rameshbabu }
24809308562SRahul Rameshbabu 
24909308562SRahul Rameshbabu static inline void thunderstrike_request_board_info(struct thunderstrike *ts)
25009308562SRahul Rameshbabu {
25109308562SRahul Rameshbabu 	set_bit(THUNDERSTRIKE_BOARD_INFO_UPDATE, &ts->update_flags);
25209308562SRahul Rameshbabu 	schedule_work(&ts->hostcmd_req_work);
25309308562SRahul Rameshbabu }
25409308562SRahul Rameshbabu 
25509308562SRahul Rameshbabu static inline int
25609308562SRahul Rameshbabu thunderstrike_update_haptics(struct thunderstrike *ts,
25709308562SRahul Rameshbabu 			     struct thunderstrike_hostcmd_haptics *motors)
25809308562SRahul Rameshbabu {
25909308562SRahul Rameshbabu 	unsigned long flags;
26009308562SRahul Rameshbabu 
26109308562SRahul Rameshbabu 	spin_lock_irqsave(&ts->haptics_update_lock, flags);
26209308562SRahul Rameshbabu 	ts->haptics_val = *motors;
26309308562SRahul Rameshbabu 	spin_unlock_irqrestore(&ts->haptics_update_lock, flags);
26409308562SRahul Rameshbabu 
26509308562SRahul Rameshbabu 	set_bit(THUNDERSTRIKE_HAPTICS_UPDATE, &ts->update_flags);
26609308562SRahul Rameshbabu 	schedule_work(&ts->hostcmd_req_work);
26709308562SRahul Rameshbabu 
26809308562SRahul Rameshbabu 	return 0;
26909308562SRahul Rameshbabu }
27009308562SRahul Rameshbabu 
27109308562SRahul Rameshbabu static int thunderstrike_play_effect(struct input_dev *idev, void *data,
27209308562SRahul Rameshbabu 				     struct ff_effect *effect)
27309308562SRahul Rameshbabu {
27409308562SRahul Rameshbabu 	struct hid_device *hdev = input_get_drvdata(idev);
27509308562SRahul Rameshbabu 	struct thunderstrike_hostcmd_haptics motors;
27609308562SRahul Rameshbabu 	struct shield_device *shield_dev;
27709308562SRahul Rameshbabu 	struct thunderstrike *ts;
27809308562SRahul Rameshbabu 
27909308562SRahul Rameshbabu 	if (effect->type != FF_RUMBLE)
28009308562SRahul Rameshbabu 		return 0;
28109308562SRahul Rameshbabu 
28209308562SRahul Rameshbabu 	shield_dev = hid_get_drvdata(hdev);
28309308562SRahul Rameshbabu 	ts = container_of(shield_dev, struct thunderstrike, base);
28409308562SRahul Rameshbabu 
28509308562SRahul Rameshbabu 	/* Thunderstrike motor values range from 0 to 32 inclusively */
28609308562SRahul Rameshbabu 	motors.motor_left = effect->u.rumble.strong_magnitude / 2047;
28709308562SRahul Rameshbabu 	motors.motor_right = effect->u.rumble.weak_magnitude / 2047;
28809308562SRahul Rameshbabu 
28909308562SRahul Rameshbabu 	hid_dbg(hdev, "Thunderstrike FF_RUMBLE request, left: %u right: %u\n",
29009308562SRahul Rameshbabu 		motors.motor_left, motors.motor_right);
29109308562SRahul Rameshbabu 
29209308562SRahul Rameshbabu 	return thunderstrike_update_haptics(ts, &motors);
29309308562SRahul Rameshbabu }
29409308562SRahul Rameshbabu 
29509308562SRahul Rameshbabu static void
29609308562SRahul Rameshbabu thunderstrike_parse_fw_version_payload(struct shield_device *shield_dev,
29709308562SRahul Rameshbabu 				       __le16 fw_version)
29809308562SRahul Rameshbabu {
29909308562SRahul Rameshbabu 	shield_dev->fw_version = le16_to_cpu(fw_version);
30009308562SRahul Rameshbabu 
30109308562SRahul Rameshbabu 	set_bit(SHIELD_FW_VERSION_INITIALIZED, &shield_dev->initialized_flags);
30209308562SRahul Rameshbabu 
30309308562SRahul Rameshbabu 	hid_dbg(shield_dev->hdev, "Thunderstrike firmware version 0x%04X\n",
30409308562SRahul Rameshbabu 		shield_dev->fw_version);
30509308562SRahul Rameshbabu }
30609308562SRahul Rameshbabu 
30709308562SRahul Rameshbabu static void
30809308562SRahul Rameshbabu thunderstrike_parse_board_info_payload(struct shield_device *shield_dev,
30909308562SRahul Rameshbabu 				       struct thunderstrike_hostcmd_board_info *board_info)
31009308562SRahul Rameshbabu {
31109308562SRahul Rameshbabu 	char board_revision_str[4];
31209308562SRahul Rameshbabu 	int i;
31309308562SRahul Rameshbabu 
31409308562SRahul Rameshbabu 	shield_dev->board_info.revision = le16_to_cpu(board_info->revision);
31509308562SRahul Rameshbabu 	for (i = 0; i < 7; ++i) {
31609308562SRahul Rameshbabu 		u16 val = le16_to_cpu(board_info->serial[i]);
31709308562SRahul Rameshbabu 
31809308562SRahul Rameshbabu 		shield_dev->board_info.serial_number[2 * i] = val & 0xFF;
31909308562SRahul Rameshbabu 		shield_dev->board_info.serial_number[2 * i + 1] = val >> 8;
32009308562SRahul Rameshbabu 	}
32109308562SRahul Rameshbabu 	shield_dev->board_info.serial_number[14] = '\0';
32209308562SRahul Rameshbabu 
32309308562SRahul Rameshbabu 	set_bit(SHIELD_BOARD_INFO_INITIALIZED, &shield_dev->initialized_flags);
32409308562SRahul Rameshbabu 
32509308562SRahul Rameshbabu 	shield_strrev(board_revision_str, 4, shield_dev->board_info.revision);
32609308562SRahul Rameshbabu 	hid_dbg(shield_dev->hdev,
32709308562SRahul Rameshbabu 		"Thunderstrike BOARD_REVISION_%s (0x%04X) S/N: %s\n",
32809308562SRahul Rameshbabu 		board_revision_str, shield_dev->board_info.revision,
32909308562SRahul Rameshbabu 		shield_dev->board_info.serial_number);
33009308562SRahul Rameshbabu }
33109308562SRahul Rameshbabu 
33209308562SRahul Rameshbabu static inline void
33309308562SRahul Rameshbabu thunderstrike_parse_haptics_payload(struct shield_device *shield_dev,
33409308562SRahul Rameshbabu 				    struct thunderstrike_hostcmd_haptics *haptics)
33509308562SRahul Rameshbabu {
33609308562SRahul Rameshbabu 	hid_dbg(shield_dev->hdev,
33709308562SRahul Rameshbabu 		"Thunderstrike haptics HOSTCMD response, left: %u right: %u\n",
33809308562SRahul Rameshbabu 		haptics->motor_left, haptics->motor_right);
33909308562SRahul Rameshbabu }
34009308562SRahul Rameshbabu 
34109308562SRahul Rameshbabu static int thunderstrike_parse_report(struct shield_device *shield_dev,
34209308562SRahul Rameshbabu 				      struct hid_report *report, u8 *data,
34309308562SRahul Rameshbabu 				      int size)
34409308562SRahul Rameshbabu {
34509308562SRahul Rameshbabu 	struct thunderstrike_hostcmd_resp_report *hostcmd_resp_report;
34609308562SRahul Rameshbabu 	struct thunderstrike *ts =
34709308562SRahul Rameshbabu 		container_of(shield_dev, struct thunderstrike, base);
34809308562SRahul Rameshbabu 	struct hid_device *hdev = shield_dev->hdev;
34909308562SRahul Rameshbabu 
35009308562SRahul Rameshbabu 	switch (report->id) {
35109308562SRahul Rameshbabu 	case THUNDERSTRIKE_HOSTCMD_RESP_REPORT_ID:
35209308562SRahul Rameshbabu 		if (size != THUNDERSTRIKE_HOSTCMD_REPORT_SIZE) {
35309308562SRahul Rameshbabu 			hid_err(hdev,
35409308562SRahul Rameshbabu 				"Encountered Thunderstrike HOSTCMD HID report with unexpected size %d\n",
35509308562SRahul Rameshbabu 				size);
35609308562SRahul Rameshbabu 			return -EINVAL;
35709308562SRahul Rameshbabu 		}
35809308562SRahul Rameshbabu 
35909308562SRahul Rameshbabu 		hostcmd_resp_report =
36009308562SRahul Rameshbabu 			(struct thunderstrike_hostcmd_resp_report *)data;
36109308562SRahul Rameshbabu 
36209308562SRahul Rameshbabu 		switch (hostcmd_resp_report->cmd_id) {
36309308562SRahul Rameshbabu 		case THUNDERSTRIKE_HOSTCMD_ID_FW_VERSION:
36409308562SRahul Rameshbabu 			thunderstrike_parse_fw_version_payload(
36509308562SRahul Rameshbabu 				shield_dev, hostcmd_resp_report->fw_version);
36609308562SRahul Rameshbabu 			break;
36709308562SRahul Rameshbabu 		case THUNDERSTRIKE_HOSTCMD_ID_BOARD_INFO:
36809308562SRahul Rameshbabu 			thunderstrike_parse_board_info_payload(
36909308562SRahul Rameshbabu 				shield_dev, &hostcmd_resp_report->board_info);
37009308562SRahul Rameshbabu 			break;
37109308562SRahul Rameshbabu 		case THUNDERSTRIKE_HOSTCMD_ID_HAPTICS:
37209308562SRahul Rameshbabu 			thunderstrike_parse_haptics_payload(
37309308562SRahul Rameshbabu 				shield_dev, &hostcmd_resp_report->motors);
37409308562SRahul Rameshbabu 			break;
37509308562SRahul Rameshbabu 
37609308562SRahul Rameshbabu 		case THUNDERSTRIKE_HOSTCMD_ID_USB_INIT:
37709308562SRahul Rameshbabu 		case THUNDERSTRIKE_HOSTCMD_ID_BLUETOOTH_INIT:
37809308562SRahul Rameshbabu 			/* May block HOSTCMD requests till received initially */
37909308562SRahul Rameshbabu 			thunderstrike_request_firmware_version(ts);
38009308562SRahul Rameshbabu 			thunderstrike_request_board_info(ts);
38109308562SRahul Rameshbabu 			/* Only HOSTCMD that can be triggered without a request */
38209308562SRahul Rameshbabu 			return 0;
38309308562SRahul Rameshbabu 		default:
38409308562SRahul Rameshbabu 			hid_warn(hdev,
38509308562SRahul Rameshbabu 				 "Unhandled Thunderstrike HOSTCMD id %d\n",
38609308562SRahul Rameshbabu 				 hostcmd_resp_report->cmd_id);
38709308562SRahul Rameshbabu 			return -ENOENT;
38809308562SRahul Rameshbabu 		}
38909308562SRahul Rameshbabu 
39009308562SRahul Rameshbabu 		break;
39109308562SRahul Rameshbabu 	default:
39209308562SRahul Rameshbabu 		return 0;
39309308562SRahul Rameshbabu 	}
39409308562SRahul Rameshbabu 
39509308562SRahul Rameshbabu 	return 0;
39609308562SRahul Rameshbabu }
39709308562SRahul Rameshbabu 
39809308562SRahul Rameshbabu static struct shield_device *thunderstrike_create(struct hid_device *hdev)
39909308562SRahul Rameshbabu {
40009308562SRahul Rameshbabu 	struct shield_device *shield_dev;
40109308562SRahul Rameshbabu 	struct thunderstrike *ts;
40209308562SRahul Rameshbabu 
40309308562SRahul Rameshbabu 	ts = devm_kzalloc(&hdev->dev, sizeof(*ts), GFP_KERNEL);
40409308562SRahul Rameshbabu 	if (!ts)
40509308562SRahul Rameshbabu 		return ERR_PTR(-ENOMEM);
40609308562SRahul Rameshbabu 
40709308562SRahul Rameshbabu 	ts->req_report_dmabuf = devm_kzalloc(
40809308562SRahul Rameshbabu 		&hdev->dev, THUNDERSTRIKE_HOSTCMD_REPORT_SIZE, GFP_KERNEL);
40909308562SRahul Rameshbabu 	if (!ts->req_report_dmabuf)
41009308562SRahul Rameshbabu 		return ERR_PTR(-ENOMEM);
41109308562SRahul Rameshbabu 
41209308562SRahul Rameshbabu 	shield_dev = &ts->base;
41309308562SRahul Rameshbabu 	shield_dev->hdev = hdev;
41409308562SRahul Rameshbabu 	shield_dev->codename = "Thunderstrike";
41509308562SRahul Rameshbabu 
41609308562SRahul Rameshbabu 	spin_lock_init(&ts->haptics_update_lock);
41709308562SRahul Rameshbabu 	INIT_WORK(&ts->hostcmd_req_work, thunderstrike_hostcmd_req_work_handler);
41809308562SRahul Rameshbabu 
41909308562SRahul Rameshbabu 	hid_set_drvdata(hdev, shield_dev);
42009308562SRahul Rameshbabu 
42109308562SRahul Rameshbabu 	ts->haptics_dev = shield_haptics_create(shield_dev, thunderstrike_play_effect);
42209308562SRahul Rameshbabu 	if (IS_ERR(ts->haptics_dev))
42309308562SRahul Rameshbabu 		return ERR_CAST(ts->haptics_dev);
42409308562SRahul Rameshbabu 
42509308562SRahul Rameshbabu 	hid_info(hdev, "Registered Thunderstrike controller\n");
42609308562SRahul Rameshbabu 	return shield_dev;
42709308562SRahul Rameshbabu }
42809308562SRahul Rameshbabu 
429*13d02c69SRahul Rameshbabu static int android_input_mapping(struct hid_device *hdev, struct hid_input *hi,
430*13d02c69SRahul Rameshbabu 				 struct hid_field *field,
431*13d02c69SRahul Rameshbabu 				 struct hid_usage *usage, unsigned long **bit,
432*13d02c69SRahul Rameshbabu 				 int *max)
433*13d02c69SRahul Rameshbabu {
434*13d02c69SRahul Rameshbabu 	if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
435*13d02c69SRahul Rameshbabu 		return 0;
436*13d02c69SRahul Rameshbabu 
437*13d02c69SRahul Rameshbabu 	switch (usage->hid & HID_USAGE) {
438*13d02c69SRahul Rameshbabu 	case HID_USAGE_ANDROID_PLAYPAUSE_BTN:
439*13d02c69SRahul Rameshbabu 		android_map_key(KEY_PLAYPAUSE);
440*13d02c69SRahul Rameshbabu 		break;
441*13d02c69SRahul Rameshbabu 	case HID_USAGE_ANDROID_VOLUMEUP_BTN:
442*13d02c69SRahul Rameshbabu 		android_map_key(KEY_VOLUMEUP);
443*13d02c69SRahul Rameshbabu 		break;
444*13d02c69SRahul Rameshbabu 	case HID_USAGE_ANDROID_VOLUMEDOWN_BTN:
445*13d02c69SRahul Rameshbabu 		android_map_key(KEY_VOLUMEDOWN);
446*13d02c69SRahul Rameshbabu 		break;
447*13d02c69SRahul Rameshbabu 	case HID_USAGE_ANDROID_SEARCH_BTN:
448*13d02c69SRahul Rameshbabu 		android_map_key(BTN_Z);
449*13d02c69SRahul Rameshbabu 		break;
450*13d02c69SRahul Rameshbabu 	case HID_USAGE_ANDROID_HOME_BTN:
451*13d02c69SRahul Rameshbabu 		android_map_key(BTN_MODE);
452*13d02c69SRahul Rameshbabu 		break;
453*13d02c69SRahul Rameshbabu 	case HID_USAGE_ANDROID_BACK_BTN:
454*13d02c69SRahul Rameshbabu 		android_map_key(BTN_SELECT);
455*13d02c69SRahul Rameshbabu 		break;
456*13d02c69SRahul Rameshbabu 	default:
457*13d02c69SRahul Rameshbabu 		return 0;
458*13d02c69SRahul Rameshbabu 	}
459*13d02c69SRahul Rameshbabu 
460*13d02c69SRahul Rameshbabu 	return 1;
461*13d02c69SRahul Rameshbabu }
462*13d02c69SRahul Rameshbabu 
46309308562SRahul Rameshbabu static ssize_t firmware_version_show(struct device *dev,
46409308562SRahul Rameshbabu 				     struct device_attribute *attr, char *buf)
46509308562SRahul Rameshbabu {
46609308562SRahul Rameshbabu 	struct hid_device *hdev = to_hid_device(dev);
46709308562SRahul Rameshbabu 	struct shield_device *shield_dev;
46809308562SRahul Rameshbabu 	int ret;
46909308562SRahul Rameshbabu 
47009308562SRahul Rameshbabu 	shield_dev = hid_get_drvdata(hdev);
47109308562SRahul Rameshbabu 
47209308562SRahul Rameshbabu 	if (test_bit(SHIELD_FW_VERSION_INITIALIZED, &shield_dev->initialized_flags))
47309308562SRahul Rameshbabu 		ret = sysfs_emit(buf, "0x%04X\n", shield_dev->fw_version);
47409308562SRahul Rameshbabu 	else
47509308562SRahul Rameshbabu 		ret = sysfs_emit(buf, NOT_INIT_STR "\n");
47609308562SRahul Rameshbabu 
47709308562SRahul Rameshbabu 	return ret;
47809308562SRahul Rameshbabu }
47909308562SRahul Rameshbabu 
48009308562SRahul Rameshbabu static DEVICE_ATTR_RO(firmware_version);
48109308562SRahul Rameshbabu 
48209308562SRahul Rameshbabu static ssize_t hardware_version_show(struct device *dev,
48309308562SRahul Rameshbabu 				     struct device_attribute *attr, char *buf)
48409308562SRahul Rameshbabu {
48509308562SRahul Rameshbabu 	struct hid_device *hdev = to_hid_device(dev);
48609308562SRahul Rameshbabu 	struct shield_device *shield_dev;
48709308562SRahul Rameshbabu 	char board_revision_str[4];
48809308562SRahul Rameshbabu 	int ret;
48909308562SRahul Rameshbabu 
49009308562SRahul Rameshbabu 	shield_dev = hid_get_drvdata(hdev);
49109308562SRahul Rameshbabu 
49209308562SRahul Rameshbabu 	if (test_bit(SHIELD_BOARD_INFO_INITIALIZED, &shield_dev->initialized_flags)) {
49309308562SRahul Rameshbabu 		shield_strrev(board_revision_str, 4, shield_dev->board_info.revision);
49409308562SRahul Rameshbabu 		ret = sysfs_emit(buf, "%s BOARD_REVISION_%s (0x%04X)\n",
49509308562SRahul Rameshbabu 				 shield_dev->codename, board_revision_str,
49609308562SRahul Rameshbabu 				 shield_dev->board_info.revision);
49709308562SRahul Rameshbabu 	} else
49809308562SRahul Rameshbabu 		ret = sysfs_emit(buf, NOT_INIT_STR "\n");
49909308562SRahul Rameshbabu 
50009308562SRahul Rameshbabu 	return ret;
50109308562SRahul Rameshbabu }
50209308562SRahul Rameshbabu 
50309308562SRahul Rameshbabu static DEVICE_ATTR_RO(hardware_version);
50409308562SRahul Rameshbabu 
50509308562SRahul Rameshbabu static ssize_t serial_number_show(struct device *dev,
50609308562SRahul Rameshbabu 				  struct device_attribute *attr, char *buf)
50709308562SRahul Rameshbabu {
50809308562SRahul Rameshbabu 	struct hid_device *hdev = to_hid_device(dev);
50909308562SRahul Rameshbabu 	struct shield_device *shield_dev;
51009308562SRahul Rameshbabu 	int ret;
51109308562SRahul Rameshbabu 
51209308562SRahul Rameshbabu 	shield_dev = hid_get_drvdata(hdev);
51309308562SRahul Rameshbabu 
51409308562SRahul Rameshbabu 	if (test_bit(SHIELD_BOARD_INFO_INITIALIZED, &shield_dev->initialized_flags))
51509308562SRahul Rameshbabu 		ret = sysfs_emit(buf, "%s\n", shield_dev->board_info.serial_number);
51609308562SRahul Rameshbabu 	else
51709308562SRahul Rameshbabu 		ret = sysfs_emit(buf, NOT_INIT_STR "\n");
51809308562SRahul Rameshbabu 
51909308562SRahul Rameshbabu 	return ret;
52009308562SRahul Rameshbabu }
52109308562SRahul Rameshbabu 
52209308562SRahul Rameshbabu static DEVICE_ATTR_RO(serial_number);
52309308562SRahul Rameshbabu 
52409308562SRahul Rameshbabu static struct attribute *shield_device_attrs[] = {
52509308562SRahul Rameshbabu 	&dev_attr_firmware_version.attr,
52609308562SRahul Rameshbabu 	&dev_attr_hardware_version.attr,
52709308562SRahul Rameshbabu 	&dev_attr_serial_number.attr,
52809308562SRahul Rameshbabu 	NULL,
52909308562SRahul Rameshbabu };
53009308562SRahul Rameshbabu ATTRIBUTE_GROUPS(shield_device);
53109308562SRahul Rameshbabu 
53209308562SRahul Rameshbabu static int shield_raw_event(struct hid_device *hdev, struct hid_report *report,
53309308562SRahul Rameshbabu 			    u8 *data, int size)
53409308562SRahul Rameshbabu {
53509308562SRahul Rameshbabu 	struct shield_device *dev = hid_get_drvdata(hdev);
53609308562SRahul Rameshbabu 
53709308562SRahul Rameshbabu 	return thunderstrike_parse_report(dev, report, data, size);
53809308562SRahul Rameshbabu }
53909308562SRahul Rameshbabu 
54009308562SRahul Rameshbabu static int shield_probe(struct hid_device *hdev, const struct hid_device_id *id)
54109308562SRahul Rameshbabu {
54209308562SRahul Rameshbabu 	struct shield_device *shield_dev = NULL;
54309308562SRahul Rameshbabu 	struct thunderstrike *ts;
54409308562SRahul Rameshbabu 	int ret;
54509308562SRahul Rameshbabu 
54609308562SRahul Rameshbabu 	ret = hid_parse(hdev);
54709308562SRahul Rameshbabu 	if (ret) {
54809308562SRahul Rameshbabu 		hid_err(hdev, "Parse failed\n");
54909308562SRahul Rameshbabu 		return ret;
55009308562SRahul Rameshbabu 	}
55109308562SRahul Rameshbabu 
55209308562SRahul Rameshbabu 	switch (id->product) {
55309308562SRahul Rameshbabu 	case USB_DEVICE_ID_NVIDIA_THUNDERSTRIKE_CONTROLLER:
55409308562SRahul Rameshbabu 		shield_dev = thunderstrike_create(hdev);
55509308562SRahul Rameshbabu 		break;
55609308562SRahul Rameshbabu 	}
55709308562SRahul Rameshbabu 
55809308562SRahul Rameshbabu 	if (unlikely(!shield_dev)) {
55909308562SRahul Rameshbabu 		hid_err(hdev, "Failed to identify SHIELD device\n");
56009308562SRahul Rameshbabu 		return -ENODEV;
56109308562SRahul Rameshbabu 	}
56209308562SRahul Rameshbabu 	if (IS_ERR(shield_dev)) {
56309308562SRahul Rameshbabu 		hid_err(hdev, "Failed to create SHIELD device\n");
56409308562SRahul Rameshbabu 		return PTR_ERR(shield_dev);
56509308562SRahul Rameshbabu 	}
56609308562SRahul Rameshbabu 
56709308562SRahul Rameshbabu 	ts = container_of(shield_dev, struct thunderstrike, base);
56809308562SRahul Rameshbabu 
56909308562SRahul Rameshbabu 	ret = hid_hw_start(hdev, HID_CONNECT_HIDINPUT);
57009308562SRahul Rameshbabu 	if (ret) {
57109308562SRahul Rameshbabu 		hid_err(hdev, "Failed to start HID device\n");
57209308562SRahul Rameshbabu 		goto err_haptics;
57309308562SRahul Rameshbabu 	}
57409308562SRahul Rameshbabu 
57509308562SRahul Rameshbabu 	ret = hid_hw_open(hdev);
57609308562SRahul Rameshbabu 	if (ret) {
57709308562SRahul Rameshbabu 		hid_err(hdev, "Failed to open HID device\n");
57809308562SRahul Rameshbabu 		goto err_stop;
57909308562SRahul Rameshbabu 	}
58009308562SRahul Rameshbabu 
58109308562SRahul Rameshbabu 	thunderstrike_request_firmware_version(ts);
58209308562SRahul Rameshbabu 	thunderstrike_request_board_info(ts);
58309308562SRahul Rameshbabu 
58409308562SRahul Rameshbabu 	return ret;
58509308562SRahul Rameshbabu 
58609308562SRahul Rameshbabu err_stop:
58709308562SRahul Rameshbabu 	hid_hw_stop(hdev);
58809308562SRahul Rameshbabu err_haptics:
58909308562SRahul Rameshbabu 	if (ts->haptics_dev)
59009308562SRahul Rameshbabu 		input_unregister_device(ts->haptics_dev);
59109308562SRahul Rameshbabu 	return ret;
59209308562SRahul Rameshbabu }
59309308562SRahul Rameshbabu 
59409308562SRahul Rameshbabu static void shield_remove(struct hid_device *hdev)
59509308562SRahul Rameshbabu {
59609308562SRahul Rameshbabu 	struct shield_device *dev = hid_get_drvdata(hdev);
59709308562SRahul Rameshbabu 	struct thunderstrike *ts;
59809308562SRahul Rameshbabu 
59909308562SRahul Rameshbabu 	ts = container_of(dev, struct thunderstrike, base);
60009308562SRahul Rameshbabu 
60109308562SRahul Rameshbabu 	hid_hw_close(hdev);
60209308562SRahul Rameshbabu 	if (ts->haptics_dev)
60309308562SRahul Rameshbabu 		input_unregister_device(ts->haptics_dev);
60409308562SRahul Rameshbabu 	cancel_work_sync(&ts->hostcmd_req_work);
60509308562SRahul Rameshbabu 	hid_hw_stop(hdev);
60609308562SRahul Rameshbabu }
60709308562SRahul Rameshbabu 
60809308562SRahul Rameshbabu static const struct hid_device_id shield_devices[] = {
60909308562SRahul Rameshbabu 	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NVIDIA,
61009308562SRahul Rameshbabu 			       USB_DEVICE_ID_NVIDIA_THUNDERSTRIKE_CONTROLLER) },
61109308562SRahul Rameshbabu 	{ HID_USB_DEVICE(USB_VENDOR_ID_NVIDIA,
61209308562SRahul Rameshbabu 			 USB_DEVICE_ID_NVIDIA_THUNDERSTRIKE_CONTROLLER) },
61309308562SRahul Rameshbabu 	{ }
61409308562SRahul Rameshbabu };
61509308562SRahul Rameshbabu MODULE_DEVICE_TABLE(hid, shield_devices);
61609308562SRahul Rameshbabu 
61709308562SRahul Rameshbabu static struct hid_driver shield_driver = {
61809308562SRahul Rameshbabu 	.name          = "shield",
61909308562SRahul Rameshbabu 	.id_table      = shield_devices,
620*13d02c69SRahul Rameshbabu 	.input_mapping = android_input_mapping,
62109308562SRahul Rameshbabu 	.probe         = shield_probe,
62209308562SRahul Rameshbabu 	.remove        = shield_remove,
62309308562SRahul Rameshbabu 	.raw_event     = shield_raw_event,
62409308562SRahul Rameshbabu 	.driver = {
62509308562SRahul Rameshbabu 		.dev_groups = shield_device_groups,
62609308562SRahul Rameshbabu 	},
62709308562SRahul Rameshbabu };
62809308562SRahul Rameshbabu module_hid_driver(shield_driver);
62909308562SRahul Rameshbabu 
63009308562SRahul Rameshbabu MODULE_AUTHOR("Rahul Rameshbabu <rrameshbabu@nvidia.com>");
63109308562SRahul Rameshbabu MODULE_DESCRIPTION("HID Driver for NVIDIA SHIELD peripherals.");
63209308562SRahul Rameshbabu MODULE_LICENSE("GPL");
633