// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
*
* HID driver for NVIDIA SHIELD peripherals.
*/
#include <linux/hid.h>
#include <linux/idr.h>
#include <linux/input-event-codes.h>
#include <linux/input.h>
#include <linux/jiffies.h>
#include <linux/leds.h>
#include <linux/module.h>
#include <linux/power_supply.h>
#include <linux/spinlock.h>
#include <linux/timer.h>
#include <linux/workqueue.h>
#include "hid-ids.h"
#define NOT_INIT_STR "NOT INITIALIZED"
#define android_map_key(c) hid_map_usage(hi, usage, bit, max, EV_KEY, (c))
enum {
HID_USAGE_ANDROID_PLAYPAUSE_BTN = 0xcd, /* Double-tap volume slider */
HID_USAGE_ANDROID_VOLUMEUP_BTN = 0xe9,
HID_USAGE_ANDROID_VOLUMEDOWN_BTN = 0xea,
HID_USAGE_ANDROID_SEARCH_BTN = 0x221, /* NVIDIA btn on Thunderstrike */
HID_USAGE_ANDROID_HOME_BTN = 0x223,
HID_USAGE_ANDROID_BACK_BTN = 0x224,
};
enum {
SHIELD_FW_VERSION_INITIALIZED = 0,
SHIELD_BOARD_INFO_INITIALIZED,
SHIELD_BATTERY_STATS_INITIALIZED,
SHIELD_CHARGER_STATE_INITIALIZED,
};
enum {
THUNDERSTRIKE_FW_VERSION_UPDATE = 0,
THUNDERSTRIKE_BOARD_INFO_UPDATE,
THUNDERSTRIKE_HAPTICS_UPDATE,
THUNDERSTRIKE_LED_UPDATE,
THUNDERSTRIKE_POWER_SUPPLY_STATS_UPDATE,
};
enum {
THUNDERSTRIKE_HOSTCMD_REPORT_SIZE = 33,
THUNDERSTRIKE_HOSTCMD_REQ_REPORT_ID = 0x4,
THUNDERSTRIKE_HOSTCMD_RESP_REPORT_ID = 0x3,
};
enum {
THUNDERSTRIKE_HOSTCMD_ID_FW_VERSION = 1,
THUNDERSTRIKE_HOSTCMD_ID_LED = 6,
THUNDERSTRIKE_HOSTCMD_ID_BATTERY,
THUNDERSTRIKE_HOSTCMD_ID_BOARD_INFO = 16,
THUNDERSTRIKE_HOSTCMD_ID_USB_INIT = 53,
THUNDERSTRIKE_HOSTCMD_ID_HAPTICS = 57,
THUNDERSTRIKE_HOSTCMD_ID_CHARGER,
};
struct power_supply_dev {
struct power_supply *psy;
struct power_supply_desc desc;
};
struct thunderstrike_psy_prop_values {
int voltage_min;
int voltage_now;
int voltage_avg;
int voltage_boot;
int capacity;
int status;
int charge_type;
int temp;
};
static const enum power_supply_property thunderstrike_battery_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_CHARGE_TYPE,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_VOLTAGE_MIN,
POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_VOLTAGE_AVG,
POWER_SUPPLY_PROP_VOLTAGE_BOOT,
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_SCOPE,
POWER_SUPPLY_PROP_TEMP,
POWER_SUPPLY_PROP_TEMP_MIN,
POWER_SUPPLY_PROP_TEMP_MAX,
POWER_SUPPLY_PROP_TEMP_ALERT_MIN,
POWER_SUPPLY_PROP_TEMP_ALERT_MAX,
};
enum thunderstrike_led_state {
THUNDERSTRIKE_LED_OFF = 1,
THUNDERSTRIKE_LED_ON = 8,
} __packed;
static_assert(sizeof(enum thunderstrike_led_state) == 1);
struct thunderstrike_hostcmd_battery {
__le16 voltage_avg;
u8 reserved_at_10;
__le16 thermistor;
__le16 voltage_min;
__le16 voltage_boot;
__le16 voltage_now;
u8 capacity;
} __packed;
enum thunderstrike_charger_type {
THUNDERSTRIKE_CHARGER_TYPE_NONE = 0,
THUNDERSTRIKE_CHARGER_TYPE_TRICKLE,
THUNDERSTRIKE_CHARGER_TYPE_NORMAL,
} __packed;
static_assert(sizeof(enum thunderstrike_charger_type) == 1);
enum thunderstrike_charger_state {
THUNDERSTRIKE_CHARGER_STATE_UNKNOWN = 0,
THUNDERSTRIKE_CHARGER_STATE_DISABLED,
THUNDERSTRIKE_CHARGER_STATE_CHARGING,
THUNDERSTRIKE_CHARGER_STATE_FULL,
THUNDERSTRIKE_CHARGER_STATE_FAILED = 8,
} __packed;
static_assert(sizeof(enum thunderstrike_charger_state) == 1);
struct thunderstrike_hostcmd_charger {
u8 connected;
enum thunderstrike_charger_type type;
enum thunderstrike_charger_state state;
} __packed;
struct thunderstrike_hostcmd_board_info {
__le16 revision;
__le16 serial[7];
} __packed;
struct thunderstrike_hostcmd_haptics {
u8 motor_left;
u8 motor_right;
} __packed;
struct thunderstrike_hostcmd_resp_report {
u8 report_id; /* THUNDERSTRIKE_HOSTCMD_RESP_REPORT_ID */
u8 cmd_id;
u8 reserved_at_10;
union {
struct thunderstrike_hostcmd_board_info board_info;
struct thunderstrike_hostcmd_haptics motors;
__le16 fw_version;
enum thunderstrike_led_state led_state;
struct thunderstrike_hostcmd_battery battery;
struct thunderstrike_hostcmd_charger charger;
u8 payload[30];
} __packed;
} __packed;
static_assert(sizeof(struct thunderstrike_hostcmd_resp_report) ==
THUNDERSTRIKE_HOSTCMD_REPORT_SIZE);
struct thunderstrike_hostcmd_req_report {
u8 report_id; /* THUNDERSTRIKE_HOSTCMD_REQ_REPORT_ID */
u8 cmd_id;
u8 reserved_at_10;
union {
struct __packed {
u8 update;
enum thunderstrike_led_state state;
} led;
struct __packed {
u8 update;
struct thunderstrike_hostcmd_haptics motors;
} haptics;
} __packed;
u8 reserved_at_30[27];
} __packed;
static_assert(sizeof(struct thunderstrike_hostcmd_req_report) ==
THUNDERSTRIKE_HOSTCMD_REPORT_SIZE);
/* Common struct for shield accessories. */
struct shield_device {
struct hid_device *hdev;
struct power_supply_dev battery_dev;
unsigned long initialized_flags;
const char *codename;
u16 fw_version;
struct {
u16 revision;
char serial_number[15];
} board_info;
};
/*
* Non-trivial to uniquely identify Thunderstrike controllers at initialization
* time. Use an ID allocator to help with this.
*/
static DEFINE_IDA(thunderstr