summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/hid/Kconfig3
-rw-r--r--drivers/hid/hid-alps.c6
-rw-r--r--drivers/hid/hid-apple.c141
-rw-r--r--drivers/hid/hid-elan.c6
-rw-r--r--drivers/hid/hid-ft260.c325
-rw-r--r--drivers/hid/hid-hyperv.c31
-rw-r--r--drivers/hid/hid-input.c6
-rw-r--r--drivers/hid/hid-logitech-hidpp.c2
-rw-r--r--drivers/hid/hid-mcp2221.c313
-rw-r--r--drivers/hid/hid-playstation.c1136
-rw-r--r--drivers/hid/hid-rmi.c2
-rw-r--r--drivers/hid/hid-sensor-custom.c4
-rw-r--r--drivers/hid/hid-uclogic-params-test.c4
-rw-r--r--drivers/hid/hid-uclogic-params.c73
-rw-r--r--drivers/hid/hid-uclogic-rdesc-test.c4
-rw-r--r--drivers/hid/hid-uclogic-rdesc.c34
-rw-r--r--drivers/hid/hid-uclogic-rdesc.h7
-rw-r--r--drivers/hid/hid-wiimote-core.c7
-rw-r--r--drivers/hid/hid-wiimote-modules.c225
-rw-r--r--drivers/hid/hid-wiimote.h1
-rw-r--r--drivers/hid/i2c-hid/i2c-hid-core.c3
-rw-r--r--drivers/hid/i2c-hid/i2c-hid-of-elan.c5
-rw-r--r--drivers/hid/i2c-hid/i2c-hid-of-goodix.c5
-rw-r--r--drivers/hid/i2c-hid/i2c-hid-of.c5
-rw-r--r--drivers/hid/intel-ish-hid/ishtp/client.c3
25 files changed, 2056 insertions, 295 deletions
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 185a077d59cd..17cce4c50e8d 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -1252,7 +1252,8 @@ config HID_ALPS
config HID_MCP2221
tristate "Microchip MCP2221 HID USB-to-I2C/SMbus host support"
depends on USB_HID && I2C
- depends on GPIOLIB
+ imply GPIOLIB
+ imply IIO
help
Provides I2C and SMBUS host adapter functionality over USB-HID
through MCP2221 device.
diff --git a/drivers/hid/hid-alps.c b/drivers/hid/hid-alps.c
index db146d0f7937..669d769ea1dc 100644
--- a/drivers/hid/hid-alps.c
+++ b/drivers/hid/hid-alps.c
@@ -820,11 +820,6 @@ static int alps_probe(struct hid_device *hdev, const struct hid_device_id *id)
return 0;
}
-static void alps_remove(struct hid_device *hdev)
-{
- hid_hw_stop(hdev);
-}
-
static const struct hid_device_id alps_id[] = {
{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY,
USB_VENDOR_ID_ALPS_JP, HID_DEVICE_ID_ALPS_U1_DUAL) },
@@ -842,7 +837,6 @@ static struct hid_driver alps_driver = {
.name = "hid-alps",
.id_table = alps_id,
.probe = alps_probe,
- .remove = alps_remove,
.raw_event = alps_raw_event,
.input_mapping = alps_input_mapping,
.input_configured = alps_input_configured,
diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c
index 6970797cdc56..1ccab8aa326c 100644
--- a/drivers/hid/hid-apple.c
+++ b/drivers/hid/hid-apple.c
@@ -59,6 +59,12 @@ MODULE_PARM_DESC(swap_opt_cmd, "Swap the Option (\"Alt\") and Command (\"Flag\")
"(For people who want to keep Windows PC keyboard muscle memory. "
"[0] = as-is, Mac layout. 1 = swapped, Windows layout.)");
+static unsigned int swap_ctrl_cmd;
+module_param(swap_ctrl_cmd, uint, 0644);
+MODULE_PARM_DESC(swap_ctrl_cmd, "Swap the Control (\"Ctrl\") and Command (\"Flag\") keys. "
+ "(For people who are used to Mac shortcuts involving Command instead of Control. "
+ "[0] = No change. 1 = Swapped.)");
+
static unsigned int swap_fn_leftctrl;
module_param(swap_fn_leftctrl, uint, 0644);
MODULE_PARM_DESC(swap_fn_leftctrl, "Swap the Fn and left Control keys. "
@@ -308,12 +314,21 @@ static const struct apple_key_translation swapped_option_cmd_keys[] = {
{ KEY_LEFTALT, KEY_LEFTMETA },
{ KEY_LEFTMETA, KEY_LEFTALT },
{ KEY_RIGHTALT, KEY_RIGHTMETA },
- { KEY_RIGHTMETA,KEY_RIGHTALT },
+ { KEY_RIGHTMETA, KEY_RIGHTALT },
+ { }
+};
+
+static const struct apple_key_translation swapped_ctrl_cmd_keys[] = {
+ { KEY_LEFTCTRL, KEY_LEFTMETA },
+ { KEY_LEFTMETA, KEY_LEFTCTRL },
+ { KEY_RIGHTCTRL, KEY_RIGHTMETA },
+ { KEY_RIGHTMETA, KEY_RIGHTCTRL },
{ }
};
static const struct apple_key_translation swapped_fn_leftctrl_keys[] = {
{ KEY_FN, KEY_LEFTCTRL },
+ { KEY_LEFTCTRL, KEY_FN },
{ }
};
@@ -375,24 +390,47 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input,
struct apple_sc *asc = hid_get_drvdata(hid);
const struct apple_key_translation *trans, *table;
bool do_translate;
- u16 code = 0;
+ u16 code = usage->code;
unsigned int real_fnmode;
- u16 fn_keycode = (swap_fn_leftctrl) ? (KEY_LEFTCTRL) : (KEY_FN);
-
- if (usage->code == fn_keycode) {
- asc->fn_on = !!value;
- input_event_with_scancode(input, usage->type, KEY_FN,
- usage->hid, value);
- return 1;
- }
-
if (fnmode == 3) {
real_fnmode = (asc->quirks & APPLE_IS_NON_APPLE) ? 2 : 1;
} else {
real_fnmode = fnmode;
}
+ if (swap_fn_leftctrl) {
+ trans = apple_find_translation(swapped_fn_leftctrl_keys, code);
+
+ if (trans)
+ code = trans->to;
+ }
+
+ if (iso_layout > 0 || (iso_layout < 0 && (asc->quirks & APPLE_ISO_TILDE_QUIRK) &&
+ hid->country == HID_COUNTRY_INTERNATIONAL_ISO)) {
+ trans = apple_find_translation(apple_iso_keyboard, code);
+
+ if (trans)
+ code = trans->to;
+ }
+
+ if (swap_opt_cmd) {
+ trans = apple_find_translation(swapped_option_cmd_keys, code);
+
+ if (trans)
+ code = trans->to;
+ }
+
+ if (swap_ctrl_cmd) {
+ trans = apple_find_translation(swapped_ctrl_cmd_keys, code);
+
+ if (trans)
+ code = trans->to;
+ }
+
+ if (code == KEY_FN)
+ asc->fn_on = !!value;
+
if (real_fnmode) {
if (hid->product == USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI ||
hid->product == USB_DEVICE_ID_APPLE_ALU_WIRELESS_ISO ||
@@ -430,15 +468,18 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input,
else
table = apple_fn_keys;
- trans = apple_find_translation (table, usage->code);
+ trans = apple_find_translation(table, code);
if (trans) {
- if (test_bit(trans->from, input->key))
+ bool from_is_set = test_bit(trans->from, input->key);
+ bool to_is_set = test_bit(trans->to, input->key);
+
+ if (from_is_set)
code = trans->from;
- else if (test_bit(trans->to, input->key))
+ else if (to_is_set)
code = trans->to;
- if (!code) {
+ if (!(from_is_set || to_is_set)) {
if (trans->flags & APPLE_FLAG_FKEY) {
switch (real_fnmode) {
case 1:
@@ -455,62 +496,31 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input,
do_translate = asc->fn_on;
}
- code = do_translate ? trans->to : trans->from;
+ if (do_translate)
+ code = trans->to;
}
-
- input_event_with_scancode(input, usage->type, code,
- usage->hid, value);
- return 1;
}
if (asc->quirks & APPLE_NUMLOCK_EMULATION &&
- (test_bit(usage->code, asc->pressed_numlock) ||
+ (test_bit(code, asc->pressed_numlock) ||
test_bit(LED_NUML, input->led))) {
- trans = apple_find_translation(powerbook_numlock_keys,
- usage->code);
+ trans = apple_find_translation(powerbook_numlock_keys, code);
if (trans) {
if (value)
- set_bit(usage->code,
- asc->pressed_numlock);
+ set_bit(code, asc->pressed_numlock);
else
- clear_bit(usage->code,
- asc->pressed_numlock);
+ clear_bit(code, asc->pressed_numlock);
- input_event_with_scancode(input, usage->type,
- trans->to, usage->hid, value);
+ code = trans->to;
}
-
- return 1;
- }
- }
-
- if (iso_layout > 0 || (iso_layout < 0 && (asc->quirks & APPLE_ISO_TILDE_QUIRK) &&
- hid->country == HID_COUNTRY_INTERNATIONAL_ISO)) {
- trans = apple_find_translation(apple_iso_keyboard, usage->code);
- if (trans) {
- input_event_with_scancode(input, usage->type,
- trans->to, usage->hid, value);
- return 1;
}
}
- if (swap_opt_cmd) {
- trans = apple_find_translation(swapped_option_cmd_keys, usage->code);
- if (trans) {
- input_event_with_scancode(input, usage->type,
- trans->to, usage->hid, value);
- return 1;
- }
- }
+ if (usage->code != code) {
+ input_event_with_scancode(input, usage->type, code, usage->hid, value);
- if (swap_fn_leftctrl) {
- trans = apple_find_translation(swapped_fn_leftctrl_keys, usage->code);
- if (trans) {
- input_event_with_scancode(input, usage->type,
- trans->to, usage->hid, value);
- return 1;
- }
+ return 1;
}
return 0;
@@ -640,9 +650,6 @@ static void apple_setup_input(struct input_dev *input)
apple_setup_key_translation(input, apple2021_fn_keys);
apple_setup_key_translation(input, macbookpro_no_esc_fn_keys);
apple_setup_key_translation(input, macbookpro_dedicated_esc_fn_keys);
-
- if (swap_fn_leftctrl)
- apple_setup_key_translation(input, swapped_fn_leftctrl_keys);
}
static int apple_input_mapping(struct hid_device *hdev, struct hid_input *hi,
@@ -1011,21 +1018,21 @@ static const struct hid_device_id apple_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING9_JIS),
.driver_data = APPLE_HAS_FN | APPLE_RDESC_JIS },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J140K),
- .driver_data = APPLE_HAS_FN | APPLE_BACKLIGHT_CTL },
+ .driver_data = APPLE_HAS_FN | APPLE_BACKLIGHT_CTL | APPLE_ISO_TILDE_QUIRK },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J132),
- .driver_data = APPLE_HAS_FN | APPLE_BACKLIGHT_CTL },
+ .driver_data = APPLE_HAS_FN | APPLE_BACKLIGHT_CTL | APPLE_ISO_TILDE_QUIRK },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J680),
- .driver_data = APPLE_HAS_FN | APPLE_BACKLIGHT_CTL },
+ .driver_data = APPLE_HAS_FN | APPLE_BACKLIGHT_CTL | APPLE_ISO_TILDE_QUIRK },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J213),
- .driver_data = APPLE_HAS_FN | APPLE_BACKLIGHT_CTL },
+ .driver_data = APPLE_HAS_FN | APPLE_BACKLIGHT_CTL | APPLE_ISO_TILDE_QUIRK },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J214K),
- .driver_data = APPLE_HAS_FN },
+ .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J223),
- .driver_data = APPLE_HAS_FN },
+ .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J230K),
- .driver_data = APPLE_HAS_FN },
+ .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J152F),
- .driver_data = APPLE_HAS_FN },
+ .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI),
.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO),
diff --git a/drivers/hid/hid-elan.c b/drivers/hid/hid-elan.c
index 8e4a5528e25d..76d93fc48f6a 100644
--- a/drivers/hid/hid-elan.c
+++ b/drivers/hid/hid-elan.c
@@ -507,11 +507,6 @@ err:
return ret;
}
-static void elan_remove(struct hid_device *hdev)
-{
- hid_hw_stop(hdev);
-}
-
static const struct hid_device_id elan_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_HP_X2),
.driver_data = ELAN_HAS_LED },
@@ -529,7 +524,6 @@ static struct hid_driver elan_driver = {
.input_configured = elan_input_configured,
.raw_event = elan_raw_event,
.probe = elan_probe,
- .remove = elan_remove,
};
module_hid_driver(elan_driver);
diff --git a/drivers/hid/hid-ft260.c b/drivers/hid/hid-ft260.c
index 79505c64dbfe..333341e80b0e 100644
--- a/drivers/hid/hid-ft260.c
+++ b/drivers/hid/hid-ft260.c
@@ -30,12 +30,21 @@ MODULE_PARM_DESC(debug, "Toggle FT260 debugging messages");
#define FT260_REPORT_MAX_LENGTH (64)
#define FT260_I2C_DATA_REPORT_ID(len) (FT260_I2C_REPORT_MIN + (len - 1) / 4)
+
+#define FT260_WAKEUP_NEEDED_AFTER_MS (4800) /* 5s minus 200ms margin */
+
/*
- * The input report format assigns 62 bytes for the data payload, but ft260
- * returns 60 and 2 in two separate transactions. To minimize transfer time
- * in reading chunks mode, set the maximum read payload length to 60 bytes.
- */
-#define FT260_RD_DATA_MAX (60)
+ * The ft260 input report format defines 62 bytes for the data payload, but
+ * when requested 62 bytes, the controller returns 60 and 2 in separate input
+ * reports. To achieve better performance with the multi-report read data
+ * transfers, we set the maximum read payload length to a multiple of 60.
+ * With a 100 kHz I2C clock, one 240 bytes read takes about 1/27 second,
+ * which is excessive; On the other hand, some higher layer drivers like at24
+ * or optoe limit the i2c reads to 128 bytes. To not block other drivers out
+ * of I2C for potentially troublesome amounts of time, we select the maximum
+ * read payload length to be 180 bytes.
+*/
+#define FT260_RD_DATA_MAX (180)
#define FT260_WR_DATA_MAX (60)
/*
@@ -230,6 +239,7 @@ struct ft260_device {
struct completion wait;
struct mutex lock;
u8 write_buf[FT260_REPORT_MAX_LENGTH];
+ unsigned long need_wakeup_at;
u8 *read_buf;
u16 read_idx;
u16 read_len;
@@ -293,12 +303,26 @@ static int ft260_i2c_reset(struct hid_device *hdev)
return ret;
}
-static int ft260_xfer_status(struct ft260_device *dev)
+static int ft260_xfer_status(struct ft260_device *dev, u8 bus_busy)
{
struct hid_device *hdev = dev->hdev;
struct ft260_get_i2c_status_report report;
int ret;
+ if (time_is_before_jiffies(dev->need_wakeup_at)) {
+ ret = ft260_hid_feature_report_get(hdev, FT260_I2C_STATUS,
+ (u8 *)&report, sizeof(report));
+ if (unlikely(ret < 0)) {
+ hid_err(hdev, "failed to retrieve status: %d, no wakeup\n",
+ ret);
+ } else {
+ dev->need_wakeup_at = jiffies +
+ msecs_to_jiffies(FT260_WAKEUP_NEEDED_AFTER_MS);
+ ft260_dbg("bus_status %#02x, wakeup\n",
+ report.bus_status);
+ }
+ }
+
ret = ft260_hid_feature_report_get(hdev, FT260_I2C_STATUS,
(u8 *)&report, sizeof(report));
if (unlikely(ret < 0)) {
@@ -310,30 +334,20 @@ static int ft260_xfer_status(struct ft260_device *dev)
ft260_dbg("bus_status %#02x, clock %u\n", report.bus_status,
dev->clock);
- if (report.bus_status & FT260_I2C_STATUS_CTRL_BUSY)
+ if (report.bus_status & (FT260_I2C_STATUS_CTRL_BUSY | bus_busy))
return -EAGAIN;
- if (report.bus_status & FT260_I2C_STATUS_BUS_BUSY)
- return -EBUSY;
-
- if (report.bus_status & FT260_I2C_STATUS_ERROR)
+ /*
+ * The error condition (bit 1) is a status bit reflecting any
+ * error conditions. When any of the bits 2, 3, or 4 are raised
+ * to 1, bit 1 is also set to 1.
+ */
+ if (report.bus_status & FT260_I2C_STATUS_ERROR) {
+ hid_err(hdev, "i2c bus error: %#02x\n", report.bus_status);
return -EIO;
+ }
- ret = -EIO;
-
- if (report.bus_status & FT260_I2C_STATUS_ADDR_NO_ACK)
- ft260_dbg("unacknowledged address\n");
-
- if (report.bus_status & FT260_I2C_STATUS_DATA_NO_ACK)
- ft260_dbg("unacknowledged data\n");
-
- if (report.bus_status & FT260_I2C_STATUS_ARBITR_LOST)
- ft260_dbg("arbitration loss\n");
-
- if (report.bus_status & FT260_I2C_STATUS_CTRL_IDLE)
- ret = 0;
-
- return ret;
+ return 0;
}
static int ft260_hid_output_report(struct hid_device *hdev, u8 *data,
@@ -355,8 +369,11 @@ static int ft260_hid_output_report(struct hid_device *hdev, u8 *data,
static int ft260_hid_output_report_check_status(struct ft260_device *dev,
u8 *data, int len)
{
- int ret, usec, try = 3;
+ u8 bus_busy;
+ int ret, usec, try = 100;
struct hid_device *hdev = dev->hdev;
+ struct ft260_i2c_write_request_report *rep =
+ (struct ft260_i2c_write_request_report *)data;
ret = ft260_hid_output_report(hdev, data, len);
if (ret < 0) {
@@ -366,17 +383,31 @@ static int ft260_hid_output_report_check_status(struct ft260_device *dev,
return ret;
}
- /* transfer time = 1 / clock(KHz) * 10 bits * bytes */
- usec = 10000 / dev->clock * len;
- usleep_range(usec, usec + 100);
- ft260_dbg("wait %d usec, len %d\n", usec, len);
+ /* transfer time = 1 / clock(KHz) * 9 bits * bytes */
+ usec = len * 9000 / dev->clock;
+ if (usec > 2000) {
+ usec -= 1500;
+ usleep_range(usec, usec + 100);
+ ft260_dbg("wait %d usec, len %d\n", usec, len);
+ }
+
+ /*
+ * Do not check the busy bit for combined transactions
+ * since the controller keeps the bus busy between writing
+ * and reading IOs to ensure an atomic operation.
+ */
+ if (rep->flag == FT260_FLAG_START)
+ bus_busy = 0;
+ else
+ bus_busy = FT260_I2C_STATUS_BUS_BUSY;
+
do {
- ret = ft260_xfer_status(dev);
+ ret = ft260_xfer_status(dev, bus_busy);
if (ret != -EAGAIN)
break;
} while (--try);
- if (ret == 0 || ret == -EBUSY)
+ if (ret == 0)
return 0;
ft260_i2c_reset(hdev);
@@ -384,41 +415,49 @@ static int ft260_hid_output_report_check_status(struct ft260_device *dev,
}
static int ft260_i2c_write(struct ft260_device *dev, u8 addr, u8 *data,
- int data_len, u8 flag)
+ int len, u8 flag)
{
- int len, ret, idx = 0;
+ int ret, wr_len, idx = 0;
struct hid_device *hdev = dev->hdev;
struct ft260_i2c_write_request_report *rep =
(struct ft260_i2c_write_request_report *)dev->write_buf;
+ if (len < 1)
+ return -EINVAL;
+
+ rep->flag = FT260_FLAG_START;
+
do {
- if (data_len <= FT260_WR_DATA_MAX)
- len = data_len;
- else
- len = FT260_WR_DATA_MAX;
+ if (len <= FT260_WR_DATA_MAX) {
+ wr_len = len;
+ if (flag == FT260_FLAG_START_STOP)
+ rep->flag |= FT260_FLAG_STOP;
+ } else {
+ wr_len = FT260_WR_DATA_MAX;
+ }
- rep->report = FT260_I2C_DATA_REPORT_ID(len);
+ rep->report = FT260_I2C_DATA_REPORT_ID(wr_len);
rep->address = addr;
- rep->length = len;
- rep->flag = flag;
+ rep->length = wr_len;
- memcpy(rep->data, &data[idx], len);
+ memcpy(rep->data, &data[idx], wr_len);
- ft260_dbg("rep %#02x addr %#02x off %d len %d d[0] %#02x\n",
- rep->report, addr, idx, len, data[0]);
+ ft260_dbg("rep %#02x addr %#02x off %d len %d wlen %d flag %#x d[0] %#02x\n",
+ rep->report, addr, idx, len, wr_len,
+ rep->flag, data[0]);
ret = ft260_hid_output_report_check_status(dev, (u8 *)rep,
- len + 4);
+ wr_len + 4);
if (ret < 0) {
- hid_err(hdev, "%s: failed to start transfer, ret %d\n",
- __func__, ret);
+ hid_err(hdev, "%s: failed with %d\n", __func__, ret);
return ret;
}
- data_len -= len;
- idx += len;
+ len -= wr_len;
+ idx += wr_len;
+ rep->flag = 0;
- } while (data_len > 0);
+ } while (len > 0);
return 0;
}
@@ -457,49 +496,74 @@ static int ft260_smbus_write(struct ft260_device *dev, u8 addr, u8 cmd,
static int ft260_i2c_read(struct ft260_device *dev, u8 addr, u8 *data,
u16 len, u8 flag)
{
+ u16 rd_len;
+ u16 rd_data_max = 60;
+ int timeout, ret = 0;
struct ft260_i2c_read_request_report rep;
struct hid_device *hdev = dev->hdev;
- int timeout;
- int ret;
+ u8 bus_busy = 0;
- if (len > FT260_RD_DATA_MAX) {
- hid_err(hdev, "%s: unsupported rd len: %d\n", __func__, len);
- return -EINVAL;
- }
+ if ((flag & FT260_FLAG_START_REPEATED) == FT260_FLAG_START_REPEATED)
+ flag = FT260_FLAG_START_REPEATED;
+ else
+ flag = FT260_FLAG_START;
+ do {
+ if (len <= rd_data_max) {
+ rd_len = len;
+ flag |= FT260_FLAG_STOP;
+ } else {
+ rd_len = rd_data_max;
+ }
+ rd_data_max = FT260_RD_DATA_MAX;
- dev->read_idx = 0;
- dev->read_buf = data;
- dev->read_len = len;
+ rep.report = FT260_I2C_READ_REQ;
+ rep.length = cpu_to_le16(rd_len);
+ rep.address = addr;
+ rep.flag = flag;
- rep.report = FT260_I2C_READ_REQ;
- rep.length = cpu_to_le16(len);
- rep.address = addr;
- rep.flag = flag;
+ ft260_dbg("rep %#02x addr %#02x len %d rlen %d flag %#x\n",
+ rep.report, rep.address, len, rd_len, flag);
- ft260_dbg("rep %#02x addr %#02x len %d\n", rep.report, rep.address,
- rep.length);
+ reinit_completion(&dev->wait);
- reinit_completion(&dev->wait);
+ dev->read_idx = 0;
+ dev->read_buf = data;
+ dev->read_len = rd_len;
- ret = ft260_hid_output_report(hdev, (u8 *)&rep, sizeof(rep));
- if (ret < 0) {
- hid_err(hdev, "%s: failed to start transaction, ret %d\n",
- __func__, ret);
- return ret;
- }
+ ret = ft260_hid_output_report(hdev, (u8 *)&rep, sizeof(rep));
+ if (ret < 0) {
+ hid_err(hdev, "%s: failed with %d\n", __func__, ret);
+ goto ft260_i2c_read_exit;
+ }
- timeout = msecs_to_jiffies(5000);
- if (!wait_for_completion_timeout(&dev->wait, timeout)) {
- ft260_i2c_reset(hdev);
- return -ETIMEDOUT;
- }
+ timeout = msecs_to_jiffies(5000);
+ if (!wait_for_completion_timeout(&dev->wait, timeout)) {
+ ret = -ETIMEDOUT;
+ ft260_i2c_reset(hdev);
+ goto ft260_i2c_read_exit;
+ }
- ret = ft260_xfer_status(dev);
- if (ret == 0)
- return 0;
+ dev->read_buf = NULL;
- ft260_i2c_reset(hdev);
- return -EIO;
+ if (flag & FT260_FLAG_STOP)
+ bus_busy = FT260_I2C_STATUS_BUS_BUSY;
+
+ ret = ft260_xfer_status(dev, bus_busy);
+ if (ret < 0) {
+ ret = -EIO;
+ ft260_i2c_reset(hdev);
+ goto ft260_i2c_read_exit;
+ }
+
+ len -= rd_len;
+ data += rd_len;
+ flag = 0;
+
+ } while (len > 0);
+
+ft260_i2c_read_exit:
+ dev->read_buf = NULL;
+ return ret;
}
/*
@@ -510,45 +574,37 @@ static int ft260_i2c_read(struct ft260_device *dev, u8 addr, u8 *data,
*/
static int ft260_i2c_write_read(struct ft260_device *dev, struct i2c_msg *msgs)
{
- int len, ret;
- u16 left_len = msgs[1].len;
- u8 *read_buf = msgs[1].buf;
+ int ret;
+ int wr_len = msgs[0].len;
+ int rd_len = msgs[1].len;
+ struct hid_device *hdev = dev->hdev;
u8 addr = msgs[0].addr;
u16 read_off = 0;
- struct hid_device *hdev = dev->hdev;
- if (msgs[0].len > 2) {
- hid_err(hdev, "%s: unsupported wr len: %d\n", __func__,
- msgs[0].len);
+ if (wr_len > 2) {
+ hid_err(hdev, "%s: invalid wr_len: %d\n", __func__, wr_len);
return -EOPNOTSUPP;
}
- memcpy(&read_off, msgs[0].buf, msgs[0].len);
-
- do {
- if (left_len <= FT260_RD_DATA_MAX)
- len = left_len;
+ if (ft260_debug) {
+ if (wr_len == 2)
+ read_off = be16_to_cpu(*(__be16 *)msgs[0].buf);
else
- len = FT260_RD_DATA_MAX;
-
- ft260_dbg("read_off %#x left_len %d len %d\n", read_off,
- left_len, len);
-
- ret = ft260_i2c_write(dev, addr, (u8 *)&read_off, msgs[0].len,
- FT260_FLAG_START);
- if (ret < 0)
- return ret;
+ read_off = *msgs[0].buf;
- ret = ft260_i2c_read(dev, addr, read_buf, len,
- FT260_FLAG_START_STOP);
- if (ret < 0)
- return ret;
+ pr_info("%s: off %#x rlen %d wlen %d\n", __func__,
+ read_off, rd_len, wr_len);
+ }
- left_len -= len;
- read_buf += len;
- read_off += len;
+ ret = ft260_i2c_write(dev, addr, msgs[0].buf, wr_len,
+ FT260_FLAG_START);
+ if (ret < 0)
+ return ret;
- } while (left_len > 0);
+ ret = ft260_i2c_read(dev, addr, msgs[1].buf, rd_len,
+ FT260_FLAG_START_STOP_REPEATED);
+ if (ret < 0)
+ return ret;
return 0;
}
@@ -613,14 +669,6 @@ static int ft260_smbus_xfer(struct i2c_adapter *adapter, u16 addr, u16 flags,
}
switch (size) {
- case I2C_SMBUS_QUICK:
- if (read_write == I2C_SMBUS_READ)
- ret = ft260_i2c_read(dev, addr, &data->byte, 0,
- FT260_FLAG_START_STOP);
- else
- ret = ft260_smbus_write(dev, addr, cmd, NULL, 0,
- FT260_FLAG_START_STOP);
- break;
case I2C_SMBUS_BYTE:
if (read_write == I2C_SMBUS_READ)
ret = ft260_i2c_read(dev, addr, &data->byte, 1,
@@ -703,7 +751,7 @@ smbus_exit:
static u32 ft260_functionality(struct i2c_adapter *adap)
{
- return I2C_FUNC_I2C | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_QUICK |
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_I2C_BLOCK;
}
@@ -782,7 +830,7 @@ static int ft260_byte_show(struct hid_device *hdev, int id, u8 *cfg, int len,
}
static int ft260_word_show(struct hid_device *hdev, int id, u8 *cfg, int len,
- u16 *field, u8 *buf)
+ __le16 *field, u8 *buf)
{
int ret;
@@ -811,9 +859,9 @@ static int ft260_word_show(struct hid_device *hdev, int id, u8 *cfg, int len,
#define FT260_I2CST_ATTR_SHOW(name) \
FT260_ATTR_SHOW(name, ft260_get_i2c_status_report, \
- FT260_I2C_STATUS, u16, ft260_word_show)
+ FT260_I2C_STATUS, __le16, ft260_word_show)
-#define FT260_ATTR_STORE(name, reptype, id, req, type, func) \
+#define FT260_ATTR_STORE(name, reptype, id, req, type, ctype, func) \
static ssize_t name##_store(struct device *kdev, \
struct device_attribute *attr, \
const char *buf, size_t count) \
@@ -823,7 +871,7 @@ static int ft260_word_show(struct hid_device *hdev, int id, u8 *cfg, int len,
type name; \
int ret; \
\
- if (!func(buf, 10, &name)) { \
+ if (!func(buf, 10, (ctype *)&name)) { \
rep.name = name; \
rep.report = id; \
rep.request = req; \
@@ -839,11 +887,11 @@ static int ft260_word_show(struct hid_device *hdev, int id, u8 *cfg, int len,
#define FT260_BYTE_ATTR_STORE(name, reptype, req) \
FT260_ATTR_STORE(name, reptype, FT260_SYSTEM_SETTINGS, req, \
- u8, kstrtou8)
+ u8, u8, kstrtou8)
#define FT260_WORD_ATTR_STORE(name, reptype, req) \
FT260_ATTR_STORE(name, reptype, FT260_SYSTEM_SETTINGS, req, \
- u16, kstrtou16)
+ __le16, u16, kstrtou16)
FT260_SSTAT_ATTR_SHOW(chip_mode);
static DEVICE_ATTR_RO(chip_mode);
@@ -928,7 +976,7 @@ static int ft260_probe(struct hid_device *hdev, const struct hid_device_id *id)
return ret;
}
- ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
+ ret = hid_hw_start(hdev, 0);
if (ret) {
hid_err(hdev, "failed to start HID HW\n");
return ret;
@@ -955,6 +1003,10 @@ static int ft260_probe(struct hid_device *hdev, const struct hid_device_id *id)
if (ret <= 0)
goto err_hid_close;
+ hid_info(hdev, "USB HID v%x.%02x Device [%s] on %s\n",
+ hdev->version >> 8, hdev->version & 0xff, hdev->name,
+ hdev->phys);
+
hid_set_drvdata(hdev, dev);
dev->hdev = hdev;
dev->adap.owner = THIS_MODULE;
@@ -963,13 +1015,12 @@ static int ft260_probe(struct hid_device *hdev, const struct hid_device_id *id)
dev->adap.quirks = &ft260_i2c_quirks;
dev->adap.dev.parent = &hdev->dev;
snprintf(dev->adap.name, sizeof(dev->adap.name),
- "FT260 usb-i2c bridge on hidraw%d",
- ((struct hidraw *)hdev->hidraw)->minor);
+ "FT260 usb-i2c bridge");
mutex_init(&dev->lock);
init_completion(&dev->wait);
- ret = ft260_xfer_status(dev);
+ ret = ft260_xfer_status(dev, FT260_I2C_STATUS_BUS_BUSY);
if (ret)
ft260_i2c_reset(hdev);
@@ -1022,6 +1073,13 @@ static int ft260_raw_event(struct hid_device *hdev, struct hid_report *report,
ft260_dbg("i2c resp: rep %#02x len %d\n", xfer->report,
xfer->length);
+ if ((dev->read_buf == NULL) ||
+ (xfer->length > dev->read_len - dev->read_idx)) {
+ hid_err(hdev, "unexpected report %#02x, length %d\n",
+ xfer->report, xfer->length);
+ return -1;
+ }
+
memcpy(&dev->read_buf[dev->read_idx], &xfer->data,
xfer->length);
dev->read_idx += xfer->length;
@@ -1030,10 +1088,9 @@ static int ft260_raw_event(struct hid_device *hdev, struct hid_report *report,
complete(&dev->wait);
} else {
- hid_err(hdev, "unknown report: %#02x\n", xfer->report);
- return 0;
+ hid_err(hdev, "unhandled report %#02x\n", xfer->report);
}
- return 1;
+ return 0;
}
static struct hid_driver ft260_driver = {
diff --git a/drivers/hid/hid-hyperv.c b/drivers/hid/hid-hyperv.c
index ab57b49a44ed..cf12f17e6533 100644
--- a/drivers/hid/hid-hyperv.c
+++ b/drivers/hid/hid-hyperv.c
@@ -22,9 +22,6 @@ struct hv_input_dev_info {
unsigned short reserved[11];
};
-/* The maximum size of a synthetic input message. */
-#define SYNTHHID_MAX_INPUT_REPORT_SIZE 16
-
/*
* Current version
*
@@ -59,11 +56,6 @@ struct synthhid_msg_hdr {
u32 size;
};
-struct synthhid_msg {
- struct synthhid_msg_hdr header;
- char data[1]; /* Enclosed message */
-};
-
union synthhid_version {
struct {
u16 minor_version;
@@ -99,7 +91,7 @@ struct synthhid_device_info_ack {
struct synthhid_input_report {
struct synthhid_msg_hdr header;
- char buffer[1];
+ char buffer[];
};
#pragma pack(pop)
@@ -118,7 +110,7 @@ enum pipe_prot_msg_type {
struct pipe_prt_msg {
enum pipe_prot_msg_type type;
u32 size;
- char data[1];
+ char data[];
};
struct mousevsc_prt_msg {
@@ -232,7 +224,7 @@ static void mousevsc_on_receive_device_info(struct mousevsc_dev *input_device,
ret = vmbus_sendpacket(input_device->device->channel,
&ack,
- sizeof(struct pipe_prt_msg) - sizeof(unsigned char) +
+ sizeof(struct pipe_prt_msg) +
sizeof(struct synthhid_device_info_ack),
(unsigned long)&ack,
VM_PKT_DATA_INBAND,
@@ -251,7 +243,7 @@ static void mousevsc_on_receive(struct hv_device *device,
struct vmpacket_descriptor *packet)
{
struct pipe_prt_msg *pipe_msg;
- struct synthhid_msg *hid_msg;
+ struct synthhid_msg_hdr *hid_msg_hdr;
struct mousevsc_dev *input_dev = hv_get_drvdata(device);
struct synthhid_input_report *input_report;
size_t len;
@@ -262,25 +254,23 @@ static void mousevsc_on_receive(struct hv_device *device,
if (pipe_msg->type != PIPE_MESSAGE_DATA)
return;
- hid_msg = (struct synthhid_msg *)pipe_msg->data;
+ hid_msg_hdr = (struct synthhid_msg_hdr *)pipe_msg->data;
- switch (hid_msg->header.type) {
+ switch (hid_msg_hdr->type) {
case SYNTH_HID_PROTOCOL_RESPONSE:
/*
* While it will be impossible for us to protect against
* malicious/buggy hypervisor/host, add a check here to
* ensure we don't corrupt memory.
*/
- if ((pipe_msg->size + sizeof(struct pipe_prt_msg)
- - sizeof(unsigned char))
+ if (struct_size(pipe_msg, data, pipe_msg->size)
> sizeof(struct mousevsc_prt_msg)) {
WARN_ON(1);
break;
}
memcpy(&input_dev->protocol_resp, pipe_msg,
- pipe_msg->size + sizeof(struct pipe_prt_msg) -
- sizeof(unsigned char));
+ struct_size(pipe_msg, data, pipe_msg->size));
complete(&input_dev->wait_event);
break;
@@ -311,7 +301,7 @@ static void mousevsc_on_receive(struct hv_device *device,
break;
default:
pr_err("unsupported hid msg type - type %d len %d\n",
- hid_msg->header.type, hid_msg->header.size);
+ hid_msg_hdr->type, hid_msg_hdr->size);
break;
}
@@ -359,8 +349,7 @@ static int mousevsc_connect_to_vsp(struct hv_device *device)
request->request.version_requested.version = SYNTHHID_INPUT_VERSION;
ret = vmbus_sendpacket(device->channel, request,
- sizeof(struct pipe_prt_msg) -
- sizeof(unsigned char) +
+ sizeof(struct pipe_prt_msg) +
sizeof(struct synthhid_protocol_request),
(unsigned long)request,
VM_PKT_DATA_INBAND,
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index c6259d9e5ff6..3a93cf04147d 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -340,6 +340,7 @@ static enum power_supply_property hidinput_battery_props[] = {
#define HID_BATTERY_QUIRK_PERCENT (1 << 0) /* always reports percent */
#define HID_BATTERY_QUIRK_FEATURE (1 << 1) /* ask for feature report */
#define HID_BATTERY_QUIRK_IGNORE (1 << 2) /* completely ignore the battery */
+#define HID_BATTERY_QUIRK_AVOID_QUERY (1 << 3) /* do not query the battery */
static const struct hid_device_id hid_battery_quirks[] = {
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
@@ -373,6 +374,8 @@ static const struct hid_device_id hid_battery_quirks[] = {
HID_BATTERY_QUIRK_IGNORE },
{ HID_USB_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ASUS_UX550VE_TOUCHSCREEN),
HID_BATTERY_QUIRK_IGNORE },
+ { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_L),
+ HID_BATTERY_QUIRK_AVOID_QUERY },
{ HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_HP_ENVY_X360_15),
HID_BATTERY_QUIRK_IGNORE },
{ HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_HP_ENVY_X360_15T_DR100),
@@ -554,6 +557,9 @@ static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type,
dev->battery_avoid_query = report_type == HID_INPUT_REPORT &&
field->physical == HID_DG_STYLUS;
+ if (quirks & HID_BATTERY_QUIRK_AVOID_QUERY)
+ dev->battery_avoid_query = true;
+
dev->battery = power_supply_register(&dev->dev, psy_desc, &psy_cfg);
if (IS_ERR(dev->battery)) {
error = PTR_ERR(dev->battery);
diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
index 8a2aac18dcc5..c6e4a96e882e 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -895,7 +895,7 @@ static int hidpp_root_get_protocol_version(struct hidpp_device *hidpp)
ret = hidpp_send_rap_command_sync(hidpp,
REPORT_ID_HIDPP_SHORT,
HIDPP_PAGE_ROOT_IDX,
- CMD_ROOT_GET_PROTOCOL_VERSION,
+ CMD_ROOT_GET_PROTOCOL_VERSION | LINUX_KERNEL_SW_ID,
ping_data, sizeof(ping_data), &response);
if (ret == HIDPP_ERROR_INVALID_SUBID) {
diff --git a/drivers/hid/hid-mcp2221.c b/drivers/hid/hid-mcp2221.c
index de52e9f7bb8c..5886543b17f3 100644
--- a/drivers/hid/hid-mcp2221.c
+++ b/drivers/hid/hid-mcp2221.c
@@ -10,12 +10,14 @@
#include <linux/module.h>
#include <linux/err.h>
#include <linux/mutex.h>
+#include <linux/bitfield.h>
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/hid.h>
#include <linux/hidraw.h>
#include <linux/i2c.h>
#include <linux/gpio/driver.h>
+#include <linux/iio/iio.h>
#include "hid-ids.h"
/* Commands codes in a raw output report */
@@ -30,6 +32,9 @@ enum {
MCP2221_I2C_CANCEL = 0x10,
MCP2221_GPIO_SET = 0x50,
MCP2221_GPIO_GET = 0x51,
+ MCP2221_SET_SRAM_SETTINGS = 0x60,
+ MCP2221_GET_SRAM_SETTINGS = 0x61,
+ MCP2221_READ_FLASH_DATA = 0xb0,
};
/* Response codes in a raw input report */
@@ -89,6 +94,7 @@ struct mcp2221 {
struct i2c_adapter adapter;
struct mutex lock;
struct completion wait_in_report;
+ struct delayed_work init_work;
u8 *rxbuf;
u8 txbuf[64];
int rxbuf_idx;
@@ -97,6 +103,18 @@ struct mcp2221 {
struct gpio_chip *gc;
u8 gp_idx;
u8 gpio_dir;
+ u8 mode[4];
+#if IS_REACHABLE(CONFIG_IIO)
+ struct iio_chan_spec iio_channels[3];
+ u16 adc_values[3];
+ u8 adc_scale;
+ u8 dac_value;
+ u16 dac_scale;
+#endif
+};
+
+struct mcp2221_iio {
+ struct mcp2221 *mcp;
};
/*
@@ -567,6 +585,7 @@ static const struct i2c_algorithm mcp_i2c_algo = {
.functionality = mcp_i2c_func,
};
+#if IS_REACHABLE(CONFIG_GPIOLIB)
static int mcp_gpio_get(struct gpio_chip *gc,
unsigned int offset)
{
@@ -670,6 +689,7 @@ static int mcp_gpio_get_direction(struct gpio_chip *gc,
return GPIO_LINE_DIRECTION_OUT;
}
+#endif
/* Gives current state of i2c engine inside mcp2221 */
static int mcp_get_i2c_eng_state(struct mcp2221 *mcp,
@@ -745,6 +765,9 @@ static int mcp2221_raw_event(struct hid_device *hdev,
break;
}
mcp->status = mcp_get_i2c_eng_state(mcp, data, 8);
+#if IS_REACHABLE(CONFIG_IIO)
+ memcpy(&mcp->adc_values, &data[50], sizeof(mcp->adc_values));
+#endif
break;
default:
mcp->status = -EIO;
@@ -816,6 +839,69 @@ static int mcp2221_raw_event(struct hid_device *hdev,
complete(&mcp->wait_in_report);
break;
+ case MCP2221_SET_SRAM_SETTINGS:
+ switch (data[1]) {
+ case MCP2221_SUCCESS:
+ mcp->status = 0;
+ break;
+ default:
+ mcp->status = -EAGAIN;
+ }
+ complete(&mcp->wait_in_report);
+ break;
+
+ case MCP2221_GET_SRAM_SETTINGS:
+ switch (data[1]) {
+ case MCP2221_SUCCESS:
+ memcpy(&mcp->mode, &data[22], 4);
+#if IS_REACHABLE(CONFIG_IIO)
+ mcp->dac_value = data[6] & GENMASK(4, 0);
+#endif
+ mcp->status = 0;
+ break;
+ default:
+ mcp->status = -EAGAIN;
+ }
+ complete(&mcp->wait_in_report);
+ break;
+
+ case MCP2221_READ_FLASH_DATA:
+ switch (data[1]) {
+ case MCP2221_SUCCESS:
+ mcp->status = 0;
+
+ /* Only handles CHIP SETTINGS subpage currently */
+ if (mcp->txbuf[1] != 0) {
+ mcp->status = -EIO;
+ break;
+ }
+
+#if IS_REACHABLE(CONFIG_IIO)
+ {
+ u8 tmp;
+ /* DAC scale value */
+ tmp = FIELD_GET(GENMASK(7, 6), data[6]);
+ if ((data[6] & BIT(5)) && tmp)
+ mcp->dac_scale = tmp + 4;
+ else
+ mcp->dac_scale = 5;
+
+ /* ADC scale value */
+ tmp = FIELD_GET(GENMASK(4, 3), data[7]);
+ if ((data[7] & BIT(2)) && tmp)
+ mcp->adc_scale = tmp - 1;
+ else
+ mcp->adc_scale = 0;
+ }
+#endif
+
+ break;
+ default:
+ mcp->status = -EAGAIN;
+ }
+ complete(&mcp->wait_in_report);
+ break;
+
default:
mcp->status = -EIO;
complete(&mcp->wait_in_report);
@@ -824,6 +910,190 @@ static int mcp2221_raw_event(struct hid_device *hdev,
return 1;
}
+/* Device resource managed function for HID unregistration */
+static void mcp2221_hid_unregister(void *ptr)
+{
+ struct hid_device *hdev = ptr;
+
+ hid_hw_close(hdev);
+ hid_hw_stop(hdev);
+}
+
+/* This is needed to be sure hid_hw_stop() isn't called twice by the subsystem */
+static void mcp2221_remove(struct hid_device *hdev)
+{
+}
+
+#if IS_REACHABLE(CONFIG_IIO)
+static int mcp2221_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *channel, int *val,
+ int *val2, long mask)
+{
+ struct mcp2221_iio *priv = iio_priv(indio_dev);
+ struct mcp2221 *mcp = priv->mcp;
+ int ret;
+
+ if (mask == IIO_CHAN_INFO_SCALE) {
+ if (channel->output)
+ *val = 1 << mcp->dac_scale;
+ else
+ *val = 1 << mcp->adc_scale;
+
+ return IIO_VAL_INT;
+ }
+
+ mutex_lock(&mcp->lock);
+
+ if (channel->output) {
+ *val = mcp->dac_value;
+ ret = IIO_VAL_INT;
+ } else {
+ /* Read ADC values */
+ ret = mcp_chk_last_cmd_status(mcp);
+
+ if (!ret) {
+ *val = le16_to_cpu((__force __le16) mcp->adc_values[channel->address]);
+ if (*val >= BIT(10))
+ ret = -EINVAL;
+ else
+ ret = IIO_VAL_INT;
+ }
+ }
+
+ mutex_unlock(&mcp->lock);
+
+ return ret;
+}
+
+static int mcp2221_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct mcp2221_iio *priv = iio_priv(indio_dev);
+ struct mcp2221 *mcp = priv->mcp;
+ int ret;
+
+ if (val < 0 || val >= BIT(5))
+ return -EINVAL;
+
+ mutex_lock(&mcp->lock);
+
+ memset(mcp->txbuf, 0, 12);
+ mcp->txbuf[0] = MCP2221_SET_SRAM_SETTINGS;
+ mcp->txbuf[4] = BIT(7) | val;
+
+ ret = mcp_send_data_req_status(mcp, mcp->txbuf, 12);
+ if (!ret)
+ mcp->dac_value = val;
+
+ mutex_unlock(&mcp->lock);
+
+ return ret;
+}
+
+static const struct iio_info mcp2221_info = {
+ .read_raw = &mcp2221_read_raw,
+ .write_raw = &mcp2221_write_raw,
+};
+
+static int mcp_iio_channels(struct mcp2221 *mcp)
+{
+ int idx, cnt = 0;
+ bool dac_created = false;
+
+ /* GP0 doesn't have ADC/DAC alternative function */
+ for (idx = 1; idx < MCP_NGPIO; idx++) {
+ struct iio_chan_spec *chan = &mcp->iio_channels[cnt];
+
+ switch (mcp->mode[idx]) {
+ case 2:
+ chan->address = idx - 1;
+ chan->channel = cnt++;
+ break;
+ case 3:
+ /* GP1 doesn't have DAC alternative function */
+ if (idx == 1 || dac_created)
+ continue;
+ /* DAC1 and DAC2 outputs are connected to the same DAC */
+ dac_created = true;
+ chan->output = 1;
+ cnt++;
+ break;
+ default:
+ continue;
+ };
+
+ chan->type = IIO_VOLTAGE;
+ chan->indexed = 1;
+ chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
+ chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
+ chan->scan_index = -1;
+ }
+
+ return cnt;
+}
+
+static void mcp_init_work(struct work_struct *work)
+{
+ struct iio_dev *indio_dev;
+ struct mcp2221 *mcp = container_of(work, struct mcp2221, init_work.work);
+ struct mcp2221_iio *data;
+ static int retries = 5;
+ int ret, num_channels;
+
+ hid_hw_power(mcp->hdev, PM_HINT_FULLON);
+ mutex_lock(&mcp->lock);
+
+ mcp->txbuf[0] = MCP2221_GET_SRAM_SETTINGS;
+ ret = mcp_send_data_req_status(mcp, mcp->txbuf, 1);
+
+ if (ret == -EAGAIN)
+ goto reschedule_task;
+
+ num_channels = mcp_iio_channels(mcp);
+ if (!num_channels)
+ goto unlock;
+
+ mcp->txbuf[0] = MCP2221_READ_FLASH_DATA;
+ mcp->txbuf[1] = 0;
+ ret = mcp_send_data_req_status(mcp, mcp->txbuf, 2);
+
+ if (ret == -EAGAIN)
+ goto reschedule_task;
+
+ indio_dev = devm_iio_device_alloc(&mcp->hdev->dev, sizeof(*data));
+ if (!indio_dev)
+ goto unlock;
+
+ data = iio_priv(indio_dev);
+ data->mcp = mcp;
+
+ indio_dev->name = "mcp2221";
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &mcp2221_info;
+ indio_dev->channels = mcp->iio_channels;
+ indio_dev->num_channels = num_channels;
+
+ devm_iio_device_register(&mcp->hdev->dev, indio_dev);
+
+unlock:
+ mutex_unlock(&mcp->lock);
+ hid_hw_power(mcp->hdev, PM_HINT_NORMAL);
+
+ return;
+
+reschedule_task:
+ mutex_unlock(&mcp->lock);
+ hid_hw_power(mcp->hdev, PM_HINT_NORMAL);
+
+ if (!retries--)
+ return;
+
+ /* Device is not ready to read SRAM or FLASH data, try again */
+ schedule_delayed_work(&mcp->init_work, msecs_to_jiffies(100));
+}
+#endif
+
static int mcp2221_probe(struct hid_device *hdev,
const struct hid_device_id *id)
{
@@ -849,7 +1119,8 @@ static int mcp2221_probe(struct hid_device *hdev,
ret = hid_hw_open(hdev);
if (ret) {
hid_err(hdev, "can't open device\n");
- goto err_hstop;
+ hid_hw_stop(hdev);
+ return ret;
}
mutex_init(&mcp->lock);
@@ -857,6 +1128,10 @@ static int mcp2221_probe(struct hid_device *hdev,
hid_set_drvdata(hdev, mcp);
mcp->hdev = hdev;
+ ret = devm_add_action_or_reset(&hdev->dev, mcp2221_hid_unregister, hdev);
+ if (ret)
+ return ret;
+
/* Set I2C bus clock diviser */
if (i2c_clk_freq > 400)
i2c_clk_freq = 400;
@@ -873,19 +1148,18 @@ static int mcp2221_probe(struct hid_device *hdev,
"MCP2221 usb-i2c bridge on hidraw%d",
((struct hidraw *)hdev->hidraw)->minor);
- ret = i2c_add_adapter(&mcp->adapter);
+ ret = devm_i2c_add_adapter(&hdev->dev, &mcp->adapter);
if (ret) {
hid_err(hdev, "can't add usb-i2c adapter: %d\n", ret);
- goto err_i2c;
+ return ret;
}
i2c_set_adapdata(&mcp->adapter, mcp);
+#if IS_REACHABLE(CONFIG_GPIOLIB)
/* Setup GPIO chip */
mcp->gc = devm_kzalloc(&hdev->dev, sizeof(*mcp->gc), GFP_KERNEL);
- if (!mcp->gc) {
- ret = -ENOMEM;
- goto err_gc;
- }
+ if (!mcp->gc)
+ return -ENOMEM;
mcp->gc->label = "mcp2221_gpio";
mcp->gc->direction_input = mcp_gpio_direction_input;
@@ -900,26 +1174,15 @@ static int mcp2221_probe(struct hid_device *hdev,
ret = devm_gpiochip_add_data(&hdev->dev, mcp->gc, mcp);
if (ret)
- goto err_gc;
-
- return 0;
-
-err_gc:
- i2c_del_adapter(&mcp->adapter);
-err_i2c:
- hid_hw_close(mcp->hdev);
-err_hstop:
- hid_hw_stop(mcp->hdev);
- return ret;
-}
+ return ret;
+#endif
-static void mcp2221_remove(struct hid_device *hdev)
-{
- struct mcp2221 *mcp = hid_get_drvdata(hdev);
+#if IS_REACHABLE(CONFIG_IIO)
+ INIT_DELAYED_WORK(&mcp->init_work, mcp_init_work);
+ schedule_delayed_work(&mcp->init_work, msecs_to_jiffies(100));
+#endif
- i2c_del_adapter(&mcp->adapter);
- hid_hw_close(mcp->hdev);
- hid_hw_stop(mcp->hdev);
+ return 0;
}
static const struct hid_device_id mcp2221_devices[] = {
diff --git a/drivers/hid/hid-playstation.c b/drivers/hid/hid-playstation.c
index 0b58763bfd30..7b5aef538044 100644
--- a/drivers/hid/hid-playstation.c
+++ b/drivers/hid/hid-playstation.c
@@ -2,7 +2,7 @@
/*
* HID driver for Sony DualSense(TM) controller.
*
- * Copyright (c) 2020 Sony Interactive Entertainment
+ * Copyright (c) 2020-2022 Sony Interactive Entertainment
*/
#include <linux/bits.h>
@@ -60,8 +60,10 @@ struct ps_calibration_data {
struct ps_led_info {
const char *name;
const char *color;
+ int max_brightness;
enum led_brightness (*brightness_get)(struct led_classdev *cdev);
int (*brightness_set)(struct led_classdev *cdev, enum led_brightness);
+ int (*blink_set)(struct led_classdev *led, unsigned long *on, unsigned long *off);
};
/* Seed values for DualShock4 / DualSense CRC32 for different report types. */
@@ -283,6 +285,225 @@ struct dualsense_output_report {
struct dualsense_output_report_common *common;
};
+#define DS4_INPUT_REPORT_USB 0x01
+#define DS4_INPUT_REPORT_USB_SIZE 64
+#define DS4_INPUT_REPORT_BT 0x11
+#define DS4_INPUT_REPORT_BT_SIZE 78
+#define DS4_OUTPUT_REPORT_USB 0x05
+#define DS4_OUTPUT_REPORT_USB_SIZE 32
+#define DS4_OUTPUT_REPORT_BT 0x11
+#define DS4_OUTPUT_REPORT_BT_SIZE 78
+
+#define DS4_FEATURE_REPORT_CALIBRATION 0x02
+#define DS4_FEATURE_REPORT_CALIBRATION_SIZE 37
+#define DS4_FEATURE_REPORT_CALIBRATION_BT 0x05
+#define DS4_FEATURE_REPORT_CALIBRATION_BT_SIZE 41
+#define DS4_FEATURE_REPORT_FIRMWARE_INFO 0xa3
+#define DS4_FEATURE_REPORT_FIRMWARE_INFO_SIZE 49
+#define DS4_FEATURE_REPORT_PAIRING_INFO 0x12
+#define DS4_FEATURE_REPORT_PAIRING_INFO_SIZE 16
+
+/*
+ * Status of a DualShock4 touch point contact.
+ * Contact IDs, with highest bit set are 'inactive'
+ * and any associated data is then invalid.
+ */
+#define DS4_TOUCH_POINT_INACTIVE BIT(7)
+
+/* Status field of DualShock4 input report. */
+#define DS4_STATUS0_BATTERY_CAPACITY GENMASK(3, 0)
+#define DS4_STATUS0_CABLE_STATE BIT(4)
+/* Battery status within batery_status field. */
+#define DS4_BATTERY_STATUS_FULL 11
+/* Status1 bit2 contains dongle connection state:
+ * 0 = connectd
+ * 1 = disconnected
+ */
+#define DS4_STATUS1_DONGLE_STATE BIT(2)
+
+/* The lower 6 bits of hw_control of the Bluetooth main output report
+ * control the interval at which Dualshock 4 reports data:
+ * 0x00 - 1ms
+ * 0x01 - 1ms
+ * 0x02 - 2ms
+ * 0x3E - 62ms
+ * 0x3F - disabled
+ */
+#define DS4_OUTPUT_HWCTL_BT_POLL_MASK 0x3F
+/* Default to 4ms poll interval, which is same as USB (not adjustable). */
+#define DS4_BT_DEFAULT_POLL_INTERVAL_MS 4
+#define DS4_OUTPUT_HWCTL_CRC32 0x40
+#define DS4_OUTPUT_HWCTL_HID 0x80
+
+/* Flags for DualShock4 output report. */
+#define DS4_OUTPUT_VALID_FLAG0_MOTOR 0x01
+#define DS4_OUTPUT_VALID_FLAG0_LED 0x02
+#define DS4_OUTPUT_VALID_FLAG0_LED_BLINK 0x04
+
+/* DualShock4 hardware limits */
+#define DS4_ACC_RES_PER_G 8192
+#define DS4_ACC_RANGE (4*DS_ACC_RES_PER_G)
+#define DS4_GYRO_RES_PER_DEG_S 1024
+#define DS4_GYRO_RANGE (2048*DS_GYRO_RES_PER_DEG_S)
+#define DS4_LIGHTBAR_MAX_BLINK 255 /* 255 centiseconds */
+#define DS4_TOUCHPAD_WIDTH 1920
+#define DS4_TOUCHPAD_HEIGHT 942
+
+enum dualshock4_dongle_state {
+ DONGLE_DISCONNECTED,
+ DONGLE_CALIBRATING,
+ DONGLE_CONNECTED,
+ DONGLE_DISABLED
+};
+
+struct dualshock4 {
+ struct ps_device base;
+ struct input_dev *gamepad;
+ struct input_dev *sensors;
+ struct input_dev *touchpad;
+
+ /* Calibration data for accelerometer and gyroscope. */
+ struct ps_calibration_data accel_calib_data[3];
+ struct ps_calibration_data gyro_calib_data[3];
+
+ /* Only used on dongle to track state transitions. */
+ enum dualshock4_dongle_state dongle_state;
+ /* Used during calibration. */
+ struct work_struct dongle_hotplug_worker;
+
+ /* Timestamp for sensor data */
+ bool sensor_timestamp_initialized;
+ uint32_t prev_sensor_timestamp;
+ uint32_t sensor_timestamp_us;
+
+ /* Bluetooth poll interval */
+ bool update_bt_poll_interval;
+ uint8_t bt_poll_interval;
+
+ bool update_rumble;
+ uint8_t motor_left;
+ uint8_t motor_right;
+
+ /* Lightbar leds */
+ bool update_lightbar;
+ bool update_lightbar_blink;
+ bool lightbar_enabled; /* For use by global LED control. */
+ uint8_t lightbar_red;
+ uint8_t lightbar_green;
+ uint8_t lightbar_blue;
+ uint8_t lightbar_blink_on; /* In increments of 10ms. */
+ uint8_t lightbar_blink_off; /* In increments of 10ms. */
+ struct led_classdev lightbar_leds[4];
+
+ struct work_struct output_worker;
+ bool output_worker_initialized;
+ void *output_report_dmabuf;
+};
+
+struct dualshock4_touch_point {
+ uint8_t contact;
+ uint8_t x_lo;
+ uint8_t x_hi:4, y_lo:4;
+ uint8_t y_hi;
+} __packed;
+static_assert(sizeof(struct dualshock4_touch_point) == 4);
+
+struct dualshock4_touch_report {
+ uint8_t timestamp;
+ struct dualshock4_touch_point points[2];
+} __packed;
+static_assert(sizeof(struct dualshock4_touch_report) == 9);
+
+/* Main DualShock4 input report excluding any BT/USB specific headers. */
+struct dualshock4_input_report_common {
+ uint8_t x, y;
+ uint8_t rx, ry;
+ uint8_t buttons[3];
+ uint8_t z, rz;
+
+ /* Motion sensors */
+ __le16 sensor_timestamp;
+ uint8_t sensor_temperature;
+ __le16 gyro[3]; /* x, y, z */
+ __le16 accel[3]; /* x, y, z */
+ uint8_t reserved2[5];
+
+ uint8_t status[2];
+ uint8_t reserved3;
+} __packed;
+static_assert(sizeof(struct dualshock4_input_report_common) == 32);
+
+struct dualshock4_input_report_usb {
+ uint8_t report_id; /* 0x01 */
+ struct dualshock4_input_report_common common;
+ uint8_t num_touch_reports;
+ struct dualshock4_touch_report touch_reports[3];
+ uint8_t reserved[3];
+} __packed;
+static_assert(sizeof(struct dualshock4_input_report_usb) == DS4_INPUT_REPORT_USB_SIZE);
+
+struct dualshock4_input_report_bt {
+ uint8_t report_id; /* 0x11 */
+ uint8_t reserved[2];
+ struct dualshock4_input_report_common common;
+ uint8_t num_touch_reports;
+ struct dualshock4_touch_report touch_reports[4]; /* BT has 4 compared to 3 for USB */
+ uint8_t reserved2[2];
+ __le32 crc32;
+} __packed;
+static_assert(sizeof(struct dualshock4_input_report_bt) == DS4_INPUT_REPORT_BT_SIZE);
+
+/* Common data between Bluetooth and USB DualShock4 output reports. */
+struct dualshock4_output_report_common {
+ uint8_t valid_flag0;
+ uint8_t valid_flag1;
+
+ uint8_t reserved;
+
+ uint8_t motor_right;
+ uint8_t motor_left;
+
+ uint8_t lightbar_red;
+ uint8_t lightbar_green;
+ uint8_t lightbar_blue;
+ uint8_t lightbar_blink_on;
+ uint8_t lightbar_blink_off;
+} __packed;
+
+struct dualshock4_output_report_usb {
+ uint8_t report_id; /* 0x5 */
+ struct dualshock4_output_report_common common;
+ uint8_t reserved[21];
+} __packed;
+static_assert(sizeof(struct dualshock4_output_report_usb) == DS4_OUTPUT_REPORT_USB_SIZE);
+
+struct dualshock4_output_report_bt {
+ uint8_t report_id; /* 0x11 */
+ uint8_t hw_control;
+ uint8_t audio_control;
+ struct dualshock4_output_report_common common;
+ uint8_t reserved[61];
+ __le32 crc32;
+} __packed;
+static_assert(sizeof(struct dualshock4_output_report_bt) == DS4_OUTPUT_REPORT_BT_SIZE);
+
+/*
+ * The DualShock4 has a main output report used to control most features. It is
+ * largely the same between Bluetooth and USB except for different headers and CRC.
+ * This structure hide the differences between the two to simplify sending output reports.
+ */
+struct dualshock4_output_report {
+ uint8_t *data; /* Start of data */
+ uint8_t len; /* Size of output report */
+
+ /* Points to Bluetooth data payload in case for a Bluetooth report else NULL. */
+ struct dualshock4_output_report_bt *bt;
+ /* Points to USB data payload in case for a USB report else NULL. */
+ struct dualshock4_output_report_usb *usb;
+ /* Points to common section of report, so past any headers. */
+ struct dualshock4_output_report_common *common;
+};
+
/*
* Common gamepad buttons across DualShock 3 / 4 and DualSense.
* Note: for device with a touchpad, touchpad button is not included
@@ -309,8 +530,11 @@ static const struct {int x; int y; } ps_gamepad_hat_mapping[] = {
{0, 0},
};
+static int dualshock4_get_calibration_data(struct dualshock4 *ds4);
static inline void dualsense_schedule_work(struct dualsense *ds);
+static inline void dualshock4_schedule_work(struct dualshock4 *ds4);
static void dualsense_set_lightbar(struct dualsense *ds, uint8_t red, uint8_t green, uint8_t blue);
+static void dualshock4_set_default_lightbar_colors(struct dualshock4 *ds4);
/*
* Add a new ps_device to ps_devices if it doesn't exist.
@@ -514,7 +738,8 @@ static struct input_dev *ps_gamepad_create(struct hid_device *hdev,
return gamepad;
}
-static int ps_get_report(struct hid_device *hdev, uint8_t report_id, uint8_t *buf, size_t size)
+static int ps_get_report(struct hid_device *hdev, uint8_t report_id, uint8_t *buf, size_t size,
+ bool check_crc)
{
int ret;
@@ -535,7 +760,7 @@ static int ps_get_report(struct hid_device *hdev, uint8_t report_id, uint8_t *bu
return -EINVAL;
}
- if (hdev->bus == BUS_BLUETOOTH) {
+ if (hdev->bus == BUS_BLUETOOTH && check_crc) {
/* Last 4 bytes contains crc32. */
uint8_t crc_offset = size - 4;
uint32_t report_crc = get_unaligned_le32(&buf[crc_offset]);
@@ -554,17 +779,24 @@ static int ps_led_register(struct ps_device *ps_dev, struct led_classdev *led,
{
int ret;
- led->name = devm_kasprintf(&ps_dev->hdev->dev, GFP_KERNEL,
- "%s:%s:%s", ps_dev->input_dev_name, led_info->color, led_info->name);
+ if (led_info->name) {
+ led->name = devm_kasprintf(&ps_dev->hdev->dev, GFP_KERNEL,
+ "%s:%s:%s", ps_dev->input_dev_name, led_info->color, led_info->name);
+ } else {
+ /* Backwards compatible mode for hid-sony, but not compliant with LED class spec. */
+ led->name = devm_kasprintf(&ps_dev->hdev->dev, GFP_KERNEL,
+ "%s:%s", ps_dev->input_dev_name, led_info->color);
+ }
if (!led->name)
return -ENOMEM;
led->brightness = 0;
- led->max_brightness = 1;
+ led->max_brightness = led_info->max_brightness;
led->flags = LED_CORE_SUSPENDRESUME;
led->brightness_get = led_info->brightness_get;
led->brightness_set_blocking = led_info->brightness_set;
+ led->blink_set = led_info->blink_set;
ret = devm_led_classdev_register(&ps_dev->hdev->dev, led);
if (ret) {
@@ -729,7 +961,7 @@ static int dualsense_get_calibration_data(struct dualsense *ds)
return -ENOMEM;
ret = ps_get_report(ds->base.hdev, DS_FEATURE_REPORT_CALIBRATION, buf,
- DS_FEATURE_REPORT_CALIBRATION_SIZE);
+ DS_FEATURE_REPORT_CALIBRATION_SIZE, true);
if (ret) {
hid_err(ds->base.hdev, "Failed to retrieve DualSense calibration info: %d\n", ret);
goto err_free;
@@ -811,7 +1043,7 @@ static int dualsense_get_firmware_info(struct dualsense *ds)
return -ENOMEM;
ret = ps_get_report(ds->base.hdev, DS_FEATURE_REPORT_FIRMWARE_INFO, buf,
- DS_FEATURE_REPORT_FIRMWARE_INFO_SIZE);
+ DS_FEATURE_REPORT_FIRMWARE_INFO_SIZE, true);
if (ret) {
hid_err(ds->base.hdev, "Failed to retrieve DualSense firmware info: %d\n", ret);
goto err_free;
@@ -844,7 +1076,7 @@ static int dualsense_get_mac_address(struct dualsense *ds)
return -ENOMEM;
ret = ps_get_report(ds->base.hdev, DS_FEATURE_REPORT_PAIRING_INFO, buf,
- DS_FEATURE_REPORT_PAIRING_INFO_SIZE);
+ DS_FEATURE_REPORT_PAIRING_INFO_SIZE, true);
if (ret) {
hid_err(ds->base.hdev, "Failed to retrieve DualSense pairing info: %d\n", ret);
goto err_free;
@@ -1317,15 +1549,15 @@ static struct ps_device *dualsense_create(struct hid_device *hdev)
int i, ret;
static const struct ps_led_info player_leds_info[] = {
- { LED_FUNCTION_PLAYER1, "white", dualsense_player_led_get_brightness,
+ { LED_FUNCTION_PLAYER1, "white", 1, dualsense_player_led_get_brightness,
dualsense_player_led_set_brightness },
- { LED_FUNCTION_PLAYER2, "white", dualsense_player_led_get_brightness,
+ { LED_FUNCTION_PLAYER2, "white", 1, dualsense_player_led_get_brightness,
dualsense_player_led_set_brightness },
- { LED_FUNCTION_PLAYER3, "white", dualsense_player_led_get_brightness,
+ { LED_FUNCTION_PLAYER3, "white", 1, dualsense_player_led_get_brightness,
dualsense_player_led_set_brightness },
- { LED_FUNCTION_PLAYER4, "white", dualsense_player_led_get_brightness,
+ { LED_FUNCTION_PLAYER4, "white", 1, dualsense_player_led_get_brightness,
dualsense_player_led_set_brightness },
- { LED_FUNCTION_PLAYER5, "white", dualsense_player_led_get_brightness,
+ { LED_FUNCTION_PLAYER5, "white", 1, dualsense_player_led_get_brightness,
dualsense_player_led_set_brightness }
};
@@ -1465,6 +1697,864 @@ err:
return ERR_PTR(ret);
}
+static void dualshock4_dongle_calibration_work(struct work_struct *work)
+{
+ struct dualshock4 *ds4 = container_of(work, struct dualshock4, dongle_hotplug_worker);
+ unsigned long flags;
+ enum dualshock4_dongle_state dongle_state;
+ int ret;
+
+ ret = dualshock4_get_calibration_data(ds4);
+ if (ret < 0) {
+ /* This call is very unlikely to fail for the dongle. When it
+ * fails we are probably in a very bad state, so mark the
+ * dongle as disabled. We will re-enable the dongle if a new
+ * DS4 hotplug is detect from sony_raw_event as any issues
+ * are likely resolved then (the dongle is quite stupid).
+ */
+ hid_err(ds4->base.hdev, "DualShock 4 USB dongle: calibration failed, disabling device\n");
+ dongle_state = DONGLE_DISABLED;
+ } else {
+ hid_info(ds4->base.hdev, "DualShock 4 USB dongle: calibration completed\n");
+ dongle_state = DONGLE_CONNECTED;
+ }
+
+ spin_lock_irqsave(&ds4->base.lock, flags);
+ ds4->dongle_state = dongle_state;
+ spin_unlock_irqrestore(&ds4->base.lock, flags);
+}
+
+static int dualshock4_get_calibration_data(struct dualshock4 *ds4)
+{
+ struct hid_device *hdev = ds4->base.hdev;
+ short gyro_pitch_bias, gyro_pitch_plus, gyro_pitch_minus;
+ short gyro_yaw_bias, gyro_yaw_plus, gyro_yaw_minus;
+ short gyro_roll_bias, gyro_roll_plus, gyro_roll_minus;
+ short gyro_speed_plus, gyro_speed_minus;
+ short acc_x_plus, acc_x_minus;
+ short acc_y_plus, acc_y_minus;
+ short acc_z_plus, acc_z_minus;
+ int speed_2x;
+ int range_2g;
+ int ret = 0;
+ uint8_t *buf;
+
+ if (ds4->base.hdev->bus == BUS_USB) {
+ int retries;
+
+ buf = kzalloc(DS4_FEATURE_REPORT_CALIBRATION_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ /* We should normally receive the feature report data we asked
+ * for, but hidraw applications such as Steam can issue feature
+ * reports as well. In particular for Dongle reconnects, Steam
+ * and this function are competing resulting in often receiving
+ * data for a different HID report, so retry a few times.
+ */
+ for (retries = 0; retries < 3; retries++) {
+ ret = ps_get_report(hdev, DS4_FEATURE_REPORT_CALIBRATION, buf,
+ DS4_FEATURE_REPORT_CALIBRATION_SIZE, true);
+ if (ret) {
+ if (retries < 2) {
+ hid_warn(hdev, "Retrying DualShock 4 get calibration report (0x02) request\n");
+ continue;
+ } else {
+ ret = -EILSEQ;
+ goto err_free;
+ }
+ hid_err(hdev, "Failed to retrieve DualShock4 calibration info: %d\n", ret);
+ goto err_free;
+ } else {
+ break;
+ }
+ }
+ } else { /* Bluetooth */
+ buf = kzalloc(DS4_FEATURE_REPORT_CALIBRATION_BT_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ ret = ps_get_report(hdev, DS4_FEATURE_REPORT_CALIBRATION_BT, buf,
+ DS4_FEATURE_REPORT_CALIBRATION_BT_SIZE, true);
+ if (ret) {
+ hid_err(hdev, "Failed to retrieve DualShock4 calibration info: %d\n", ret);
+ goto err_free;
+ }
+ }
+
+ gyro_pitch_bias = get_unaligned_le16(&buf[1]);
+ gyro_yaw_bias = get_unaligned_le16(&buf[3]);
+ gyro_roll_bias = get_unaligned_le16(&buf[5]);
+ if (ds4->base.hdev->bus == BUS_USB) {
+ gyro_pitch_plus = get_unaligned_le16(&buf[7]);
+ gyro_pitch_minus = get_unaligned_le16(&buf[9]);
+ gyro_yaw_plus = get_unaligned_le16(&buf[11]);
+ gyro_yaw_minus = get_unaligned_le16(&buf[13]);
+ gyro_roll_plus = get_unaligned_le16(&buf[15]);
+ gyro_roll_minus = get_unaligned_le16(&buf[17]);
+ } else {
+ /* BT + Dongle */
+ gyro_pitch_plus = get_unaligned_le16(&buf[7]);
+ gyro_yaw_plus = get_unaligned_le16(&buf[9]);
+ gyro_roll_plus = get_unaligned_le16(&buf[11]);
+ gyro_pitch_minus = get_unaligned_le16(&buf[13]);
+ gyro_yaw_minus = get_unaligned_le16(&buf[15]);
+ gyro_roll_minus = get_unaligned_le16(&buf[17]);
+ }
+ gyro_speed_plus = get_unaligned_le16(&buf[19]);
+ gyro_speed_minus = get_unaligned_le16(&buf[21]);
+ acc_x_plus = get_unaligned_le16(&buf[23]);
+ acc_x_minus = get_unaligned_le16(&buf[25]);
+ acc_y_plus = get_unaligned_le16(&buf[27]);
+ acc_y_minus = get_unaligned_le16(&buf[29]);
+ acc_z_plus = get_unaligned_le16(&buf[31]);
+ acc_z_minus = get_unaligned_le16(&buf[33]);
+
+ /*
+ * Set gyroscope calibration and normalization parameters.
+ * Data values will be normalized to 1/DS4_GYRO_RES_PER_DEG_S degree/s.
+ */
+ speed_2x = (gyro_speed_plus + gyro_speed_minus);
+ ds4->gyro_calib_data[0].abs_code = ABS_RX;
+ ds4->gyro_calib_data[0].bias = gyro_pitch_bias;
+ ds4->gyro_calib_data[0].sens_numer = speed_2x*DS4_GYRO_RES_PER_DEG_S;
+ ds4->gyro_calib_data[0].sens_denom = gyro_pitch_plus - gyro_pitch_minus;
+
+ ds4->gyro_calib_data[1].abs_code = ABS_RY;
+ ds4->gyro_calib_data[1].bias = gyro_yaw_bias;
+ ds4->gyro_calib_data[1].sens_numer = speed_2x*DS4_GYRO_RES_PER_DEG_S;
+ ds4->gyro_calib_data[1].sens_denom = gyro_yaw_plus - gyro_yaw_minus;
+
+ ds4->gyro_calib_data[2].abs_code = ABS_RZ;
+ ds4->gyro_calib_data[2].bias = gyro_roll_bias;
+ ds4->gyro_calib_data[2].sens_numer = speed_2x*DS4_GYRO_RES_PER_DEG_S;
+ ds4->gyro_calib_data[2].sens_denom = gyro_roll_plus - gyro_roll_minus;
+
+ /*
+ * Set accelerometer calibration and normalization parameters.
+ * Data values will be normalized to 1/DS4_ACC_RES_PER_G g.
+ */
+ range_2g = acc_x_plus - acc_x_minus;
+ ds4->accel_calib_data[0].abs_code = ABS_X;
+ ds4->accel_calib_data[0].bias = acc_x_plus - range_2g / 2;
+ ds4->accel_calib_data[0].sens_numer = 2*DS4_ACC_RES_PER_G;
+ ds4->accel_calib_data[0].sens_denom = range_2g;
+
+ range_2g = acc_y_plus - acc_y_minus;
+ ds4->accel_calib_data[1].abs_code = ABS_Y;
+ ds4->accel_calib_data[1].bias = acc_y_plus - range_2g / 2;
+ ds4->accel_calib_data[1].sens_numer = 2*DS4_ACC_RES_PER_G;
+ ds4->accel_calib_data[1].sens_denom = range_2g;
+
+ range_2g = acc_z_plus - acc_z_minus;
+ ds4->accel_calib_data[2].abs_code = ABS_Z;
+ ds4->accel_calib_data[2].bias = acc_z_plus - range_2g / 2;
+ ds4->accel_calib_data[2].sens_numer = 2*DS4_ACC_RES_PER_G;
+ ds4->accel_calib_data[2].sens_denom = range_2g;
+
+err_free:
+ kfree(buf);
+ return ret;
+}
+
+static int dualshock4_get_firmware_info(struct dualshock4 *ds4)
+{
+ uint8_t *buf;
+ int ret;
+
+ buf = kzalloc(DS4_FEATURE_REPORT_FIRMWARE_INFO_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ /* Note USB and BT support the same feature report, but this report
+ * lacks CRC support, so must be disabled in ps_get_report.
+ */
+ ret = ps_get_report(ds4->base.hdev, DS4_FEATURE_REPORT_FIRMWARE_INFO, buf,
+ DS4_FEATURE_REPORT_FIRMWARE_INFO_SIZE, false);
+ if (ret) {
+ hid_err(ds4->base.hdev, "Failed to retrieve DualShock4 firmware info: %d\n", ret);
+ goto err_free;
+ }
+
+ ds4->base.hw_version = get_unaligned_le16(&buf[35]);
+ ds4->base.fw_version = get_unaligned_le16(&buf[41]);
+
+err_free:
+ kfree(buf);
+ return ret;
+}
+
+static int dualshock4_get_mac_address(struct dualshock4 *ds4)
+{
+ struct hid_device *hdev = ds4->base.hdev;
+ uint8_t *buf;
+ int ret = 0;
+
+ if (hdev->bus == BUS_USB) {
+ buf = kzalloc(DS4_FEATURE_REPORT_PAIRING_INFO_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ ret = ps_get_report(hdev, DS4_FEATURE_REPORT_PAIRING_INFO, buf,
+ DS4_FEATURE_REPORT_PAIRING_INFO_SIZE, false);
+ if (ret) {
+ hid_err(hdev, "Failed to retrieve DualShock4 pairing info: %d\n", ret);
+ goto err_free;
+ }
+
+ memcpy(ds4->base.mac_address, &buf[1], sizeof(ds4->base.mac_address));
+ } else {
+ /* Rely on HIDP for Bluetooth */
+ if (strlen(hdev->uniq) != 17)
+ return -EINVAL;
+
+ ret = sscanf(hdev->uniq, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
+ &ds4->base.mac_address[5], &ds4->base.mac_address[4],
+ &ds4->base.mac_address[3], &ds4->base.mac_address[2],
+ &ds4->base.mac_address[1], &ds4->base.mac_address[0]);
+
+ if (ret != sizeof(ds4->base.mac_address))
+ return -EINVAL;
+
+ ret = 0;
+ }
+
+err_free:
+ kfree(buf);
+ return ret;
+}
+
+static enum led_brightness dualshock4_led_get_brightness(struct led_classdev *led)
+{
+ struct hid_device *hdev = to_hid_device(led->dev->parent);
+ struct dualshock4 *ds4 = hid_get_drvdata(hdev);
+ unsigned int led_index;
+
+ led_index = led - ds4->lightbar_leds;
+ switch (led_index) {
+ case 0:
+ return ds4->lightbar_red;
+ case 1:
+ return ds4->lightbar_green;
+ case 2:
+ return ds4->lightbar_blue;
+ case 3:
+ return ds4->lightbar_enabled;
+ }
+
+ return -1;
+}
+
+static int dualshock4_led_set_blink(struct led_classdev *led, unsigned long *delay_on,
+ unsigned long *delay_off)
+{
+ struct hid_device *hdev = to_hid_device(led->dev->parent);
+ struct dualshock4 *ds4 = hid_get_drvdata(hdev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&ds4->base.lock, flags);
+
+ if (!*delay_on && !*delay_off) {
+ /* Default to 1 Hz (50 centiseconds on, 50 centiseconds off). */
+ ds4->lightbar_blink_on = 50;
+ ds4->lightbar_blink_off = 50;
+ } else {
+ /* Blink delays in centiseconds. */
+ ds4->lightbar_blink_on = min_t(unsigned long, *delay_on/10, DS4_LIGHTBAR_MAX_BLINK);
+ ds4->lightbar_blink_off = min_t(unsigned long, *delay_off/10, DS4_LIGHTBAR_MAX_BLINK);
+ }
+
+ ds4->update_lightbar_blink = true;
+
+ spin_unlock_irqrestore(&ds4->base.lock, flags);
+
+ dualshock4_schedule_work(ds4);
+
+ *delay_on = ds4->lightbar_blink_on;
+ *delay_off = ds4->lightbar_blink_off;
+
+ return 0;
+}
+
+static int dualshock4_led_set_brightness(struct led_classdev *led, enum led_brightness value)
+{
+ struct hid_device *hdev = to_hid_device(led->dev->parent);
+ struct dualshock4 *ds4 = hid_get_drvdata(hdev);
+ unsigned long flags;
+ unsigned int led_index;
+
+ spin_lock_irqsave(&ds4->base.lock, flags);
+
+ led_index = led - ds4->lightbar_leds;
+ switch (led_index) {
+ case 0:
+ ds4->lightbar_red = value;
+ break;
+ case 1:
+ ds4->lightbar_green = value;
+ break;
+ case 2:
+ ds4->lightbar_blue = value;
+ break;
+ case 3:
+ ds4->lightbar_enabled = !!value;
+ }
+
+ ds4->update_lightbar = true;
+
+ spin_unlock_irqrestore(&ds4->base.lock, flags);
+
+ dualshock4_schedule_work(ds4);
+
+ return 0;
+}
+
+static void dualshock4_init_output_report(struct dualshock4 *ds4,
+ struct dualshock4_output_report *rp, void *buf)
+{
+ struct hid_device *hdev = ds4->base.hdev;
+
+ if (hdev->bus == BUS_BLUETOOTH) {
+ struct dualshock4_output_report_bt *bt = buf;
+
+ memset(bt, 0, sizeof(*bt));
+ bt->report_id = DS4_OUTPUT_REPORT_BT;
+
+ rp->data = buf;
+ rp->len = sizeof(*bt);
+ rp->bt = bt;
+ rp->usb = NULL;
+ rp->common = &bt->common;
+ } else { /* USB */
+ struct dualshock4_output_report_usb *usb = buf;
+
+ memset(usb, 0, sizeof(*usb));
+ usb->report_id = DS4_OUTPUT_REPORT_USB;
+
+ rp->data = buf;
+ rp->len = sizeof(*usb);
+ rp->bt = NULL;
+ rp->usb = usb;
+ rp->common = &usb->common;
+ }
+}
+
+static void dualshock4_output_worker(struct work_struct *work)
+{
+ struct dualshock4 *ds4 = container_of(work, struct dualshock4, output_worker);
+ struct dualshock4_output_report report;
+ struct dualshock4_output_report_common *common;
+ unsigned long flags;
+
+ dualshock4_init_output_report(ds4, &report, ds4->output_report_dmabuf);
+ common = report.common;
+
+ spin_lock_irqsave(&ds4->base.lock, flags);
+
+ if (ds4->update_rumble) {
+ /* Select classic rumble style haptics and enable it. */
+ common->valid_flag0 |= DS4_OUTPUT_VALID_FLAG0_MOTOR;
+ common->motor_left = ds4->motor_left;
+ common->motor_right = ds4->motor_right;
+ ds4->update_rumble = false;
+ }
+
+ if (ds4->update_lightbar) {
+ common->valid_flag0 |= DS4_OUTPUT_VALID_FLAG0_LED;
+ /* Comptabile behavior with hid-sony, which used a dummy global LED to
+ * allow enabling/disabling the lightbar. The global LED maps to
+ * lightbar_enabled.
+ */
+ common->lightbar_red = ds4->lightbar_enabled ? ds4->lightbar_red : 0;
+ common->lightbar_green = ds4->lightbar_enabled ? ds4->lightbar_green : 0;
+ common->lightbar_blue = ds4->lightbar_enabled ? ds4->lightbar_blue : 0;
+ ds4->update_lightbar = false;
+ }
+
+ if (ds4->update_lightbar_blink) {
+ common->valid_flag0 |= DS4_OUTPUT_VALID_FLAG0_LED_BLINK;
+ common->lightbar_blink_on = ds4->lightbar_blink_on;
+ common->lightbar_blink_off = ds4->lightbar_blink_off;
+ ds4->update_lightbar_blink = false;
+ }
+
+ spin_unlock_irqrestore(&ds4->base.lock, flags);
+
+ /* Bluetooth packets need additional flags as well as a CRC in the last 4 bytes. */
+ if (report.bt) {
+ uint32_t crc;
+ uint8_t seed = PS_OUTPUT_CRC32_SEED;
+
+ /* Hardware control flags need to set to let the device know
+ * there is HID data as well as CRC.
+ */
+ report.bt->hw_control = DS4_OUTPUT_HWCTL_HID | DS4_OUTPUT_HWCTL_CRC32;
+
+ if (ds4->update_bt_poll_interval) {
+ report.bt->hw_control |= ds4->bt_poll_interval;
+ ds4->update_bt_poll_interval = false;
+ }
+
+ crc = crc32_le(0xFFFFFFFF, &seed, 1);
+ crc = ~crc32_le(crc, report.data, report.len - 4);
+
+ report.bt->crc32 = cpu_to_le32(crc);
+ }
+
+ hid_hw_output_report(ds4->base.hdev, report.data, report.len);
+}
+
+static int dualshock4_parse_report(struct ps_device *ps_dev, struct hid_report *report,
+ u8 *data, int size)
+{
+ struct hid_device *hdev = ps_dev->hdev;
+ struct dualshock4 *ds4 = container_of(ps_dev, struct dualshock4, base);
+ struct dualshock4_input_report_common *ds4_report;
+ struct dualshock4_touch_report *touch_reports;
+ uint8_t battery_capacity, num_touch_reports, value;
+ int battery_status, i, j;
+ uint16_t sensor_timestamp;
+ unsigned long flags;
+
+ /*
+ * DualShock4 in USB uses the full HID report for reportID 1, but
+ * Bluetooth uses a minimal HID report for reportID 1 and reports
+ * the full report using reportID 17.
+ */
+ if (hdev->bus == BUS_USB && report->id == DS4_INPUT_REPORT_USB &&
+ size == DS4_INPUT_REPORT_USB_SIZE) {
+ struct dualshock4_input_report_usb *usb = (struct dualshock4_input_report_usb *)data;
+
+ ds4_report = &usb->common;
+ num_touch_reports = usb->num_touch_reports;
+ touch_reports = usb->touch_reports;
+ } else if (hdev->bus == BUS_BLUETOOTH && report->id == DS4_INPUT_REPORT_BT &&
+ size == DS4_INPUT_REPORT_BT_SIZE) {
+ struct dualshock4_input_report_bt *bt = (struct dualshock4_input_report_bt *)data;
+ uint32_t report_crc = get_unaligned_le32(&bt->crc32);
+
+ /* Last 4 bytes of input report contains CRC. */
+ if (!ps_check_crc32(PS_INPUT_CRC32_SEED, data, size - 4, report_crc)) {
+ hid_err(hdev, "DualShock4 input CRC's check failed\n");
+ return -EILSEQ;
+ }
+
+ ds4_report = &bt->common;
+ num_touch_reports = bt->num_touch_reports;
+ touch_reports = bt->touch_reports;
+ } else {
+ hid_err(hdev, "Unhandled reportID=%d\n", report->id);
+ return -1;
+ }
+
+ input_report_abs(ds4->gamepad, ABS_X, ds4_report->x);
+ input_report_abs(ds4->gamepad, ABS_Y, ds4_report->y);
+ input_report_abs(ds4->gamepad, ABS_RX, ds4_report->rx);
+ input_report_abs(ds4->gamepad, ABS_RY, ds4_report->ry);
+ input_report_abs(ds4->gamepad, ABS_Z, ds4_report->z);
+ input_report_abs(ds4->gamepad, ABS_RZ, ds4_report->rz);
+
+ value = ds4_report->buttons[0] & DS_BUTTONS0_HAT_SWITCH;
+ if (value >= ARRAY_SIZE(ps_gamepad_hat_mapping))
+ value = 8; /* center */
+ input_report_abs(ds4->gamepad, ABS_HAT0X, ps_gamepad_hat_mapping[value].x);
+ input_report_abs(ds4->gamepad, ABS_HAT0Y, ps_gamepad_hat_mapping[value].y);
+
+ input_report_key(ds4->gamepad, BTN_WEST, ds4_report->buttons[0] & DS_BUTTONS0_SQUARE);
+ input_report_key(ds4->gamepad, BTN_SOUTH, ds4_report->buttons[0] & DS_BUTTONS0_CROSS);
+ input_report_key(ds4->gamepad, BTN_EAST, ds4_report->buttons[0] & DS_BUTTONS0_CIRCLE);
+ input_report_key(ds4->gamepad, BTN_NORTH, ds4_report->buttons[0] & DS_BUTTONS0_TRIANGLE);
+ input_report_key(ds4->gamepad, BTN_TL, ds4_report->buttons[1] & DS_BUTTONS1_L1);
+ input_report_key(ds4->gamepad, BTN_TR, ds4_report->buttons[1] & DS_BUTTONS1_R1);
+ input_report_key(ds4->gamepad, BTN_TL2, ds4_report->buttons[1] & DS_BUTTONS1_L2);
+ input_report_key(ds4->gamepad, BTN_TR2, ds4_report->buttons[1] & DS_BUTTONS1_R2);
+ input_report_key(ds4->gamepad, BTN_SELECT, ds4_report->buttons[1] & DS_BUTTONS1_CREATE);
+ input_report_key(ds4->gamepad, BTN_START, ds4_report->buttons[1] & DS_BUTTONS1_OPTIONS);
+ input_report_key(ds4->gamepad, BTN_THUMBL, ds4_report->buttons[1] & DS_BUTTONS1_L3);
+ input_report_key(ds4->gamepad, BTN_THUMBR, ds4_report->buttons[1] & DS_BUTTONS1_R3);
+ input_report_key(ds4->gamepad, BTN_MODE, ds4_report->buttons[2] & DS_BUTTONS2_PS_HOME);
+ input_sync(ds4->gamepad);
+
+ /* Parse and calibrate gyroscope data. */
+ for (i = 0; i < ARRAY_SIZE(ds4_report->gyro); i++) {
+ int raw_data = (short)le16_to_cpu(ds4_report->gyro[i]);
+ int calib_data = mult_frac(ds4->gyro_calib_data[i].sens_numer,
+ raw_data - ds4->gyro_calib_data[i].bias,
+ ds4->gyro_calib_data[i].sens_denom);
+
+ input_report_abs(ds4->sensors, ds4->gyro_calib_data[i].abs_code, calib_data);
+ }
+
+ /* Parse and calibrate accelerometer data. */
+ for (i = 0; i < ARRAY_SIZE(ds4_report->accel); i++) {
+ int raw_data = (short)le16_to_cpu(ds4_report->accel[i]);
+ int calib_data = mult_frac(ds4->accel_calib_data[i].sens_numer,
+ raw_data - ds4->accel_calib_data[i].bias,
+ ds4->accel_calib_data[i].sens_denom);
+
+ input_report_abs(ds4->sensors, ds4->accel_calib_data[i].abs_code, calib_data);
+ }
+
+ /* Convert timestamp (in 5.33us unit) to timestamp_us */
+ sensor_timestamp = le16_to_cpu(ds4_report->sensor_timestamp);
+ if (!ds4->sensor_timestamp_initialized) {
+ ds4->sensor_timestamp_us = DIV_ROUND_CLOSEST(sensor_timestamp*16, 3);
+ ds4->sensor_timestamp_initialized = true;
+ } else {
+ uint16_t delta;
+
+ if (ds4->prev_sensor_timestamp > sensor_timestamp)
+ delta = (U16_MAX - ds4->prev_sensor_timestamp + sensor_timestamp + 1);
+ else
+ delta = sensor_timestamp - ds4->prev_sensor_timestamp;
+ ds4->sensor_timestamp_us += DIV_ROUND_CLOSEST(delta*16, 3);
+ }
+ ds4->prev_sensor_timestamp = sensor_timestamp;
+ input_event(ds4->sensors, EV_MSC, MSC_TIMESTAMP, ds4->sensor_timestamp_us);
+ input_sync(ds4->sensors);
+
+ for (i = 0; i < num_touch_reports; i++) {
+ struct dualshock4_touch_report *touch_report = &touch_reports[i];
+
+ for (j = 0; j < ARRAY_SIZE(touch_report->points); j++) {
+ struct dualshock4_touch_point *point = &touch_report->points[j];
+ bool active = (point->contact & DS4_TOUCH_POINT_INACTIVE) ? false : true;
+
+ input_mt_slot(ds4->touchpad, j);
+ input_mt_report_slot_state(ds4->touchpad, MT_TOOL_FINGER, active);
+
+ if (active) {
+ int x = (point->x_hi << 8) | point->x_lo;
+ int y = (point->y_hi << 4) | point->y_lo;
+
+ input_report_abs(ds4->touchpad, ABS_MT_POSITION_X, x);
+ input_report_abs(ds4->touchpad, ABS_MT_POSITION_Y, y);
+ }
+ }
+ input_mt_sync_frame(ds4->touchpad);
+ input_sync(ds4->touchpad);
+ }
+ input_report_key(ds4->touchpad, BTN_LEFT, ds4_report->buttons[2] & DS_BUTTONS2_TOUCHPAD);
+
+ /*
+ * Interpretation of the battery_capacity data depends on the cable state.
+ * When no cable is connected (bit4 is 0):
+ * - 0:10: percentage in units of 10%.
+ * When a cable is plugged in:
+ * - 0-10: percentage in units of 10%.
+ * - 11: battery is full
+ * - 14: not charging due to Voltage or temperature error
+ * - 15: charge error
+ */
+ if (ds4_report->status[0] & DS4_STATUS0_CABLE_STATE) {
+ uint8_t battery_data = ds4_report->status[0] & DS4_STATUS0_BATTERY_CAPACITY;
+
+ if (battery_data < 10) {
+ /* Take the mid-point for each battery capacity value,
+ * because on the hardware side 0 = 0-9%, 1=10-19%, etc.
+ * This matches official platform behavior, which does
+ * the same.
+ */
+ battery_capacity = battery_data * 10 + 5;
+ battery_status = POWER_SUPPLY_STATUS_CHARGING;
+ } else if (battery_data == 10) {
+ battery_capacity = 100;
+ battery_status = POWER_SUPPLY_STATUS_CHARGING;
+ } else if (battery_data == DS4_BATTERY_STATUS_FULL) {
+ battery_capacity = 100;
+ battery_status = POWER_SUPPLY_STATUS_FULL;
+ } else { /* 14, 15 and undefined values */
+ battery_capacity = 0;
+ battery_status = POWER_SUPPLY_STATUS_UNKNOWN;
+ }
+ } else {
+ uint8_t battery_data = ds4_report->status[0] & DS4_STATUS0_BATTERY_CAPACITY;
+
+ if (battery_data < 10)
+ battery_capacity = battery_data * 10 + 5;
+ else /* 10 */
+ battery_capacity = 100;
+
+ battery_status = POWER_SUPPLY_STATUS_DISCHARGING;
+ }
+
+ spin_lock_irqsave(&ps_dev->lock, flags);
+ ps_dev->battery_capacity = battery_capacity;
+ ps_dev->battery_status = battery_status;
+ spin_unlock_irqrestore(&ps_dev->lock, flags);
+
+ return 0;
+}
+
+static int dualshock4_dongle_parse_report(struct ps_device *ps_dev, struct hid_report *report,
+ u8 *data, int size)
+{
+ struct dualshock4 *ds4 = container_of(ps_dev, struct dualshock4, base);
+ bool connected = false;
+
+ /* The dongle reports data using the main USB report (0x1) no matter whether a controller
+ * is connected with mostly zeros. The report does contain dongle status, which we use to
+ * determine if a controller is connected and if so we forward to the regular DualShock4
+ * parsing code.
+ */
+ if (data[0] == DS4_INPUT_REPORT_USB && size == DS4_INPUT_REPORT_USB_SIZE) {
+ struct dualshock4_input_report_common *ds4_report = (struct dualshock4_input_report_common *)&data[1];
+ unsigned long flags;
+
+ connected = ds4_report->status[1] & DS4_STATUS1_DONGLE_STATE ? false : true;
+
+ if (ds4->dongle_state == DONGLE_DISCONNECTED && connected) {
+ hid_info(ps_dev->hdev, "DualShock 4 USB dongle: controller connected\n");
+
+ dualshock4_set_default_lightbar_colors(ds4);
+
+ spin_lock_irqsave(&ps_dev->lock, flags);
+ ds4->dongle_state = DONGLE_CALIBRATING;
+ spin_unlock_irqrestore(&ps_dev->lock, flags);
+
+ schedule_work(&ds4->dongle_hotplug_worker);
+
+ /* Don't process the report since we don't have
+ * calibration data, but let hidraw have it anyway.
+ */
+ return 0;
+ } else if ((ds4->dongle_state == DONGLE_CONNECTED ||
+ ds4->dongle_state == DONGLE_DISABLED) && !connected) {
+ hid_info(ps_dev->hdev, "DualShock 4 USB dongle: controller disconnected\n");
+
+ spin_lock_irqsave(&ps_dev->lock, flags);
+ ds4->dongle_state = DONGLE_DISCONNECTED;
+ spin_unlock_irqrestore(&ps_dev->lock, flags);
+
+ /* Return 0, so hidraw can get the report. */
+ return 0;
+ } else if (ds4->dongle_state == DONGLE_CALIBRATING ||
+ ds4->dongle_state == DONGLE_DISABLED ||
+ ds4->dongle_state == DONGLE_DISCONNECTED) {
+ /* Return 0, so hidraw can get the report. */
+ return 0;
+ }
+ }
+
+ if (connected)
+ return dualshock4_parse_report(ps_dev, report, data, size);
+
+ return 0;
+}
+
+static int dualshock4_play_effect(struct input_dev *dev, void *data, struct ff_effect *effect)
+{
+ struct hid_device *hdev = input_get_drvdata(dev);
+ struct dualshock4 *ds4 = hid_get_drvdata(hdev);
+ unsigned long flags;
+
+ if (effect->type != FF_RUMBLE)
+ return 0;
+
+ spin_lock_irqsave(&ds4->base.lock, flags);
+ ds4->update_rumble = true;
+ ds4->motor_left = effect->u.rumble.strong_magnitude / 256;
+ ds4->motor_right = effect->u.rumble.weak_magnitude / 256;
+ spin_unlock_irqrestore(&ds4->base.lock, flags);
+
+ dualshock4_schedule_work(ds4);
+ return 0;
+}
+
+static void dualshock4_remove(struct ps_device *ps_dev)
+{
+ struct dualshock4 *ds4 = container_of(ps_dev, struct dualshock4, base);
+ unsigned long flags;
+
+ spin_lock_irqsave(&ds4->base.lock, flags);
+ ds4->output_worker_initialized = false;
+ spin_unlock_irqrestore(&ds4->base.lock, flags);
+
+ cancel_work_sync(&ds4->output_worker);
+
+ if (ps_dev->hdev->product == USB_DEVICE_ID_SONY_PS4_CONTROLLER_DONGLE)
+ cancel_work_sync(&ds4->dongle_hotplug_worker);
+}
+
+static inline void dualshock4_schedule_work(struct dualshock4 *ds4)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ds4->base.lock, flags);
+ if (ds4->output_worker_initialized)
+ schedule_work(&ds4->output_worker);
+ spin_unlock_irqrestore(&ds4->base.lock, flags);
+}
+
+static void dualshock4_set_bt_poll_interval(struct dualshock4 *ds4, uint8_t interval)
+{
+ ds4->bt_poll_interval = interval;
+ ds4->update_bt_poll_interval = true;
+ dualshock4_schedule_work(ds4);
+}
+
+/* Set default lightbar color based on player. */
+static void dualshock4_set_default_lightbar_colors(struct dualshock4 *ds4)
+{
+ /* Use same player colors as PlayStation 4.
+ * Array of colors is in RGB.
+ */
+ static const int player_colors[4][3] = {
+ { 0x00, 0x00, 0x40 }, /* Blue */
+ { 0x40, 0x00, 0x00 }, /* Red */
+ { 0x00, 0x40, 0x00 }, /* Green */
+ { 0x20, 0x00, 0x20 } /* Pink */
+ };
+
+ uint8_t player_id = ds4->base.player_id % ARRAY_SIZE(player_colors);
+
+ ds4->lightbar_enabled = true;
+ ds4->lightbar_red = player_colors[player_id][0];
+ ds4->lightbar_green = player_colors[player_id][1];
+ ds4->lightbar_blue = player_colors[player_id][2];
+
+ ds4->update_lightbar = true;
+ dualshock4_schedule_work(ds4);
+}
+
+static struct ps_device *dualshock4_create(struct hid_device *hdev)
+{
+ struct dualshock4 *ds4;
+ struct ps_device *ps_dev;
+ uint8_t max_output_report_size;
+ int i, ret;
+
+ /* The DualShock4 has an RGB lightbar, which the original hid-sony driver
+ * exposed as a set of 4 LEDs for the 3 color channels and a global control.
+ * Ideally this should have used the multi-color LED class, which didn't exist
+ * yet. In addition the driver used a naming scheme not compliant with the LED
+ * naming spec by using "<mac_address>:<color>", which contained many colons.
+ * We use a more compliant by using "<device_name>:<color>" name now. Ideally
+ * would have been "<device_name>:<color>:indicator", but that would break
+ * existing applications (e.g. Android). Nothing matches against MAC address.
+ */
+ static const struct ps_led_info lightbar_leds_info[] = {
+ { NULL, "red", 255, dualshock4_led_get_brightness, dualshock4_led_set_brightness },
+ { NULL, "green", 255, dualshock4_led_get_brightness, dualshock4_led_set_brightness },
+ { NULL, "blue", 255, dualshock4_led_get_brightness, dualshock4_led_set_brightness },
+ { NULL, "global", 1, dualshock4_led_get_brightness, dualshock4_led_set_brightness,
+ dualshock4_led_set_blink },
+ };
+
+ ds4 = devm_kzalloc(&hdev->dev, sizeof(*ds4), GFP_KERNEL);
+ if (!ds4)
+ return ERR_PTR(-ENOMEM);
+
+ /*
+ * Patch version to allow userspace to distinguish between
+ * hid-generic vs hid-playstation axis and button mapping.
+ */
+ hdev->version |= HID_PLAYSTATION_VERSION_PATCH;
+
+ ps_dev = &ds4->base;
+ ps_dev->hdev = hdev;
+ spin_lock_init(&ps_dev->lock);
+ ps_dev->battery_capacity = 100; /* initial value until parse_report. */
+ ps_dev->battery_status = POWER_SUPPLY_STATUS_UNKNOWN;
+ ps_dev->parse_report = dualshock4_parse_report;
+ ps_dev->remove = dualshock4_remove;
+ INIT_WORK(&ds4->output_worker, dualshock4_output_worker);
+ ds4->output_worker_initialized = true;
+ hid_set_drvdata(hdev, ds4);
+
+ max_output_report_size = sizeof(struct dualshock4_output_report_bt);
+ ds4->output_report_dmabuf = devm_kzalloc(&hdev->dev, max_output_report_size, GFP_KERNEL);
+ if (!ds4->output_report_dmabuf)
+ return ERR_PTR(-ENOMEM);
+
+ if (hdev->product == USB_DEVICE_ID_SONY_PS4_CONTROLLER_DONGLE) {
+ ds4->dongle_state = DONGLE_DISCONNECTED;
+ INIT_WORK(&ds4->dongle_hotplug_worker, dualshock4_dongle_calibration_work);
+
+ /* Override parse report for dongle specific hotplug handling. */
+ ps_dev->parse_report = dualshock4_dongle_parse_report;
+ }
+
+ ret = dualshock4_get_mac_address(ds4);
+ if (ret) {
+ hid_err(hdev, "Failed to get MAC address from DualShock4\n");
+ return ERR_PTR(ret);
+ }
+ snprintf(hdev->uniq, sizeof(hdev->uniq), "%pMR", ds4->base.mac_address);
+
+ ret = dualshock4_get_firmware_info(ds4);
+ if (ret) {
+ hid_err(hdev, "Failed to get firmware info from DualShock4\n");
+ return ERR_PTR(ret);
+ }
+
+ ret = ps_devices_list_add(ps_dev);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ret = dualshock4_get_calibration_data(ds4);
+ if (ret) {
+ hid_err(hdev, "Failed to get calibration data from DualShock4\n");
+ goto err;
+ }
+
+ ds4->gamepad = ps_gamepad_create(hdev, dualshock4_play_effect);
+ if (IS_ERR(ds4->gamepad)) {
+ ret = PTR_ERR(ds4->gamepad);
+ goto err;
+ }
+
+ /* Use gamepad input device name as primary device name for e.g. LEDs */
+ ps_dev->input_dev_name = dev_name(&ds4->gamepad->dev);
+
+ ds4->sensors = ps_sensors_create(hdev, DS4_ACC_RANGE, DS4_ACC_RES_PER_G,
+ DS4_GYRO_RANGE, DS4_GYRO_RES_PER_DEG_S);
+ if (IS_ERR(ds4->sensors)) {
+ ret = PTR_ERR(ds4->sensors);
+ goto err;
+ }
+
+ ds4->touchpad = ps_touchpad_create(hdev, DS4_TOUCHPAD_WIDTH, DS4_TOUCHPAD_HEIGHT, 2);
+ if (IS_ERR(ds4->touchpad)) {
+ ret = PTR_ERR(ds4->touchpad);
+ goto err;
+ }
+
+ ret = ps_device_register_battery(ps_dev);
+ if (ret)
+ goto err;
+
+ for (i = 0; i < ARRAY_SIZE(lightbar_leds_info); i++) {
+ const struct ps_led_info *led_info = &lightbar_leds_info[i];
+
+ ret = ps_led_register(ps_dev, &ds4->lightbar_leds[i], led_info);
+ if (ret < 0)
+ goto err;
+ }
+
+ dualshock4_set_bt_poll_interval(ds4, DS4_BT_DEFAULT_POLL_INTERVAL_MS);
+
+ ret = ps_device_set_player_id(ps_dev);
+ if (ret) {
+ hid_err(hdev, "Failed to assign player id for DualShock4: %d\n", ret);
+ goto err;
+ }
+
+ dualshock4_set_default_lightbar_colors(ds4);
+
+ /*
+ * Reporting hardware and firmware is important as there are frequent updates, which
+ * can change behavior.
+ */
+ hid_info(hdev, "Registered DualShock4 controller hw_version=0x%08x fw_version=0x%08x\n",
+ ds4->base.hw_version, ds4->base.fw_version);
+ return &ds4->base;
+
+err:
+ ps_devices_list_remove(ps_dev);
+ return ERR_PTR(ret);
+}
+
static int ps_raw_event(struct hid_device *hdev, struct hid_report *report,
u8 *data, int size)
{
@@ -1499,7 +2589,16 @@ static int ps_probe(struct hid_device *hdev, const struct hid_device_id *id)
goto err_stop;
}
- if (hdev->product == USB_DEVICE_ID_SONY_PS5_CONTROLLER ||
+ if (hdev->product == USB_DEVICE_ID_SONY_PS4_CONTROLLER ||
+ hdev->product == USB_DEVICE_ID_SONY_PS4_CONTROLLER_2 ||
+ hdev->product == USB_DEVICE_ID_SONY_PS4_CONTROLLER_DONGLE) {
+ dev = dualshock4_create(hdev);
+ if (IS_ERR(dev)) {
+ hid_err(hdev, "Failed to create dualshock4.\n");
+ ret = PTR_ERR(dev);
+ goto err_close;
+ }
+ } else if (hdev->product == USB_DEVICE_ID_SONY_PS5_CONTROLLER ||
hdev->product == USB_DEVICE_ID_SONY_PS5_CONTROLLER_2) {
dev = dualsense_create(hdev);
if (IS_ERR(dev)) {
@@ -1533,6 +2632,13 @@ static void ps_remove(struct hid_device *hdev)
}
static const struct hid_device_id ps_devices[] = {
+ /* Sony DualShock 4 controllers for PS4 */
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER) },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER_2) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER_2) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER_DONGLE) },
+ /* Sony DualSense controllers for PS5 */
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS5_CONTROLLER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS5_CONTROLLER) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS5_CONTROLLER_2) },
diff --git a/drivers/hid/hid-rmi.c b/drivers/hid/hid-rmi.c
index bb1f423f4ace..84e7ba5314d3 100644
--- a/drivers/hid/hid-rmi.c
+++ b/drivers/hid/hid-rmi.c
@@ -326,6 +326,8 @@ static int rmi_input_event(struct hid_device *hdev, u8 *data, int size)
if (!(test_bit(RMI_STARTED, &hdata->flags)))
return 0;
+ pm_wakeup_event(hdev->dev.parent, 0);
+
local_irq_save(flags);
rmi_set_attn_data(rmi_dev, data[1], &data[2], size - 2);
diff --git a/drivers/hid/hid-sensor-custom.c b/drivers/hid/hid-sensor-custom.c
index 32c2306e240d..f444e63e9f36 100644
--- a/drivers/hid/hid-sensor-custom.c
+++ b/drivers/hid/hid-sensor-custom.c
@@ -62,7 +62,7 @@ struct hid_sensor_sample {
u32 raw_len;
} __packed;
-static struct attribute hid_custom_attrs[] = {
+static struct attribute hid_custom_attrs[HID_CUSTOM_TOTAL_ATTRS] = {
{.name = "name", .mode = S_IRUGO},
{.name = "units", .mode = S_IRUGO},
{.name = "unit-expo", .mode = S_IRUGO},
@@ -862,7 +862,7 @@ hid_sensor_register_platform_device(struct platform_device *pdev,
return ERR_PTR(-ENOMEM);
custom_pdev = platform_device_register_data(pdev->dev.parent, dev_name,
- PLATFORM_DEVID_NONE, hsdev,
+ PLATFORM_DEVID_AUTO, hsdev,
sizeof(*hsdev));
kfree(dev_name);
return custom_pdev;
diff --git a/drivers/hid/hid-uclogic-params-test.c b/drivers/hid/hid-uclogic-params-test.c
index 57ef5d3e4b74..bfa7ccb7d1e8 100644
--- a/drivers/hid/hid-uclogic-params-test.c
+++ b/drivers/hid/hid-uclogic-params-test.c
@@ -136,7 +136,7 @@ static void uclogic_parse_ugee_v2_desc_case_desc(struct uclogic_parse_ugee_v2_de
KUNIT_ARRAY_PARAM(uclogic_parse_ugee_v2_desc, uclogic_parse_ugee_v2_desc_cases,
uclogic_parse_ugee_v2_desc_case_desc);
-static void uclogic_parse_ugee_v2_desc_test(struct kunit *test)
+static void hid_test_uclogic_parse_ugee_v2_desc(struct kunit *test)
{
int res;
s32 desc_params[UCLOGIC_RDESC_PH_ID_NUM];
@@ -175,7 +175,7 @@ static void uclogic_parse_ugee_v2_desc_test(struct kunit *test)
}
static struct kunit_case hid_uclogic_params_test_cases[] = {
- KUNIT_CASE_PARAM(uclogic_parse_ugee_v2_desc_test,
+ KUNIT_CASE_PARAM(hid_test_uclogic_parse_ugee_v2_desc,
uclogic_parse_ugee_v2_desc_gen_params),
{}
};
diff --git a/drivers/hid/hid-uclogic-params.c b/drivers/hid/hid-uclogic-params.c
index 34fa991e6267..cd1233d7e253 100644
--- a/drivers/hid/hid-uclogic-params.c
+++ b/drivers/hid/hid-uclogic-params.c
@@ -18,6 +18,7 @@
#include "usbhid/usbhid.h"
#include "hid-ids.h"
#include <linux/ctype.h>
+#include <linux/string.h>
#include <asm/unaligned.h>
/**
@@ -1212,6 +1213,69 @@ static int uclogic_params_ugee_v2_init_frame_mouse(struct uclogic_params *p)
}
/**
+ * uclogic_params_ugee_v2_has_battery() - check whether a UGEE v2 device has
+ * battery or not.
+ * @hdev: The HID device of the tablet interface.
+ *
+ * Returns:
+ * True if the device has battery, false otherwise.
+ */
+static bool uclogic_params_ugee_v2_has_battery(struct hid_device *hdev)
+{
+ /* The XP-PEN Deco LW vendor, product and version are identical to the
+ * Deco L. The only difference reported by their firmware is the product
+ * name. Add a quirk to support battery reporting on the wireless
+ * version.
+ */
+ if (hdev->vendor == USB_VENDOR_ID_UGEE &&
+ hdev->product == USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_L) {
+ struct usb_device *udev = hid_to_usb_dev(hdev);
+
+ if (strstarts(udev->product, "Deco LW"))
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * uclogic_params_ugee_v2_init_battery() - initialize UGEE v2 battery reporting.
+ * @hdev: The HID device of the tablet interface, cannot be NULL.
+ * @p: Parameters to fill in, cannot be NULL.
+ *
+ * Returns:
+ * Zero, if successful. A negative errno code on error.
+ */
+static int uclogic_params_ugee_v2_init_battery(struct hid_device *hdev,
+ struct uclogic_params *p)
+{
+ int rc = 0;
+
+ if (!hdev || !p)
+ return -EINVAL;
+
+ /* Some tablets contain invalid characters in hdev->uniq, throwing a
+ * "hwmon: '<name>' is not a valid name attribute, please fix" error.
+ * Use the device vendor and product IDs instead.
+ */
+ snprintf(hdev->uniq, sizeof(hdev->uniq), "%x-%x", hdev->vendor,
+ hdev->product);
+
+ rc = uclogic_params_frame_init_with_desc(&p->frame_list[1],
+ uclogic_rdesc_ugee_v2_battery_template_arr,
+ uclogic_rdesc_ugee_v2_battery_template_size,
+ UCLOGIC_RDESC_UGEE_V2_BATTERY_ID);
+ if (rc)
+ return rc;
+
+ p->frame_list[1].suffix = "Battery";
+ p->pen.subreport_list[1].value = 0xf2;
+ p->pen.subreport_list[1].id = UCLOGIC_RDESC_UGEE_V2_BATTERY_ID;
+
+ return rc;
+}
+
+/**
* uclogic_params_ugee_v2_init() - initialize a UGEE graphics tablets by
* discovering their parameters.
*
@@ -1334,6 +1398,15 @@ static int uclogic_params_ugee_v2_init(struct uclogic_params *params,
if (rc)
goto cleanup;
+ /* Initialize the battery interface*/
+ if (uclogic_params_ugee_v2_has_battery(hdev)) {
+ rc = uclogic_params_ugee_v2_init_battery(hdev, &p);
+ if (rc) {
+ hid_err(hdev, "error initializing battery: %d\n", rc);
+ goto cleanup;
+ }
+ }
+
output:
/* Output parameters */
memcpy(params, &p, sizeof(*params));
diff --git a/drivers/hid/hid-uclogic-rdesc-test.c b/drivers/hid/hid-uclogic-rdesc-test.c
index 3971a0854c3e..b429c541bf2f 100644
--- a/drivers/hid/hid-uclogic-rdesc-test.c
+++ b/drivers/hid/hid-uclogic-rdesc-test.c
@@ -187,7 +187,7 @@ static void uclogic_template_case_desc(struct uclogic_template_case *t,
KUNIT_ARRAY_PARAM(uclogic_template, uclogic_template_cases,
uclogic_template_case_desc);
-static void uclogic_template_test(struct kunit *test)
+static void hid_test_uclogic_template(struct kunit *test)
{
__u8 *res;
const struct uclogic_template_case *params = test->param_value;
@@ -203,7 +203,7 @@ static void uclogic_template_test(struct kunit *test)
}
static struct kunit_case hid_uclogic_rdesc_test_cases[] = {
- KUNIT_CASE_PARAM(uclogic_template_test, uclogic_template_gen_params),
+ KUNIT_CASE_PARAM(hid_test_uclogic_template, uclogic_template_gen_params),
{}
};
diff --git a/drivers/hid/hid-uclogic-rdesc.c b/drivers/hid/hid-uclogic-rdesc.c
index 6b73eb0df6bd..fb40775f5f5b 100644
--- a/drivers/hid/hid-uclogic-rdesc.c
+++ b/drivers/hid/hid-uclogic-rdesc.c
@@ -1035,6 +1035,40 @@ const __u8 uclogic_rdesc_ugee_v2_frame_mouse_template_arr[] = {
const size_t uclogic_rdesc_ugee_v2_frame_mouse_template_size =
sizeof(uclogic_rdesc_ugee_v2_frame_mouse_template_arr);
+/* Fixed report descriptor template for UGEE v2 battery reports */
+const __u8 uclogic_rdesc_ugee_v2_battery_template_arr[] = {
+ 0x05, 0x01, /* Usage Page (Desktop), */
+ 0x09, 0x07, /* Usage (Keypad), */
+ 0xA1, 0x01, /* Collection (Application), */
+ 0x85, UCLOGIC_RDESC_UGEE_V2_BATTERY_ID,
+ /* Report ID, */
+ 0x75, 0x08, /* Report Size (8), */
+ 0x95, 0x02, /* Report Count (2), */
+ 0x81, 0x01, /* Input (Constant), */
+ 0x05, 0x84, /* Usage Page (Power Device), */
+ 0x05, 0x85, /* Usage Page (Battery System), */
+ 0x09, 0x65, /* Usage Page (AbsoluteStateOfCharge), */
+ 0x75, 0x08, /* Report Size (8), */
+ 0x95, 0x01, /* Report Count (1), */
+ 0x15, 0x00, /* Logical Minimum (0), */
+ 0x26, 0xff, 0x00, /* Logical Maximum (255), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x75, 0x01, /* Report Size (1), */
+ 0x95, 0x01, /* Report Count (1), */
+ 0x15, 0x00, /* Logical Minimum (0), */
+ 0x25, 0x01, /* Logical Maximum (1), */
+ 0x09, 0x44, /* Usage Page (Charging), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x95, 0x07, /* Report Count (7), */
+ 0x81, 0x01, /* Input (Constant), */
+ 0x75, 0x08, /* Report Size (8), */
+ 0x95, 0x07, /* Report Count (7), */
+ 0x81, 0x01, /* Input (Constant), */
+ 0xC0 /* End Collection */
+};
+const size_t uclogic_rdesc_ugee_v2_battery_template_size =
+ sizeof(uclogic_rdesc_ugee_v2_battery_template_arr);
+
/* Fixed report descriptor for Ugee EX07 frame */
const __u8 uclogic_rdesc_ugee_ex07_frame_arr[] = {
0x05, 0x01, /* Usage Page (Desktop), */
diff --git a/drivers/hid/hid-uclogic-rdesc.h b/drivers/hid/hid-uclogic-rdesc.h
index 0502a0656496..a1f78c07293f 100644
--- a/drivers/hid/hid-uclogic-rdesc.h
+++ b/drivers/hid/hid-uclogic-rdesc.h
@@ -161,6 +161,9 @@ extern const size_t uclogic_rdesc_v2_frame_dial_size;
/* Device ID byte offset in v2 frame dial reports */
#define UCLOGIC_RDESC_V2_FRAME_DIAL_DEV_ID_BYTE 0x4
+/* Report ID for tweaked UGEE v2 battery reports */
+#define UCLOGIC_RDESC_UGEE_V2_BATTERY_ID 0xba
+
/* Fixed report descriptor template for UGEE v2 pen reports */
extern const __u8 uclogic_rdesc_ugee_v2_pen_template_arr[];
extern const size_t uclogic_rdesc_ugee_v2_pen_template_size;
@@ -177,6 +180,10 @@ extern const size_t uclogic_rdesc_ugee_v2_frame_dial_template_size;
extern const __u8 uclogic_rdesc_ugee_v2_frame_mouse_template_arr[];
extern const size_t uclogic_rdesc_ugee_v2_frame_mouse_template_size;
+/* Fixed report descriptor template for UGEE v2 battery reports */
+extern const __u8 uclogic_rdesc_ugee_v2_battery_template_arr[];
+extern const size_t uclogic_rdesc_ugee_v2_battery_template_size;
+
/* Fixed report descriptor for Ugee EX07 frame */
extern const __u8 uclogic_rdesc_ugee_ex07_frame_arr[];
extern const size_t uclogic_rdesc_ugee_ex07_frame_size;
diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c
index 4399d6c6afef..09db8111dc26 100644
--- a/drivers/hid/hid-wiimote-core.c
+++ b/drivers/hid/hid-wiimote-core.c
@@ -458,6 +458,9 @@ static __u8 wiimote_cmd_read_ext(struct wiimote_data *wdata, __u8 *rmem)
if (rmem[0] == 0x00 && rmem[1] == 0x00 &&
rmem[4] == 0x01 && rmem[5] == 0x03)
return WIIMOTE_EXT_GUITAR;
+ if (rmem[0] == 0x03 && rmem[1] == 0x00 &&
+ rmem[4] == 0x01 && rmem[5] == 0x03)
+ return WIIMOTE_EXT_TURNTABLE;
return WIIMOTE_EXT_UNKNOWN;
}
@@ -495,6 +498,7 @@ static bool wiimote_cmd_map_mp(struct wiimote_data *wdata, __u8 exttype)
case WIIMOTE_EXT_GUITAR:
wmem = 0x07;
break;
+ case WIIMOTE_EXT_TURNTABLE:
case WIIMOTE_EXT_NUNCHUK:
wmem = 0x05;
break;
@@ -1082,6 +1086,7 @@ static const char *wiimote_exttype_names[WIIMOTE_EXT_NUM] = {
[WIIMOTE_EXT_PRO_CONTROLLER] = "Nintendo Wii U Pro Controller",
[WIIMOTE_EXT_DRUMS] = "Nintendo Wii Drums",
[WIIMOTE_EXT_GUITAR] = "Nintendo Wii Guitar",
+ [WIIMOTE_EXT_TURNTABLE] = "Nintendo Wii Turntable"
};
/*
@@ -1669,6 +1674,8 @@ static ssize_t wiimote_ext_show(struct device *dev,
return sprintf(buf, "drums\n");
case WIIMOTE_EXT_GUITAR:
return sprintf(buf, "guitar\n");
+ case WIIMOTE_EXT_TURNTABLE:
+ return sprintf(buf, "turntable\n");
case WIIMOTE_EXT_UNKNOWN:
default:
return sprintf(buf, "unknown\n");
diff --git a/drivers/hid/hid-wiimote-modules.c b/drivers/hid/hid-wiimote-modules.c
index 213c58bf2495..dbccdfa63916 100644
--- a/drivers/hid/hid-wiimote-modules.c
+++ b/drivers/hid/hid-wiimote-modules.c
@@ -2403,6 +2403,230 @@ static const struct wiimod_ops wiimod_guitar = {
.in_ext = wiimod_guitar_in_ext,
};
+/*
+ * Turntable
+ * DJ Hero came with a Turntable Controller that was plugged in
+ * as an extension.
+ * We create a separate device for turntables and report all information via this
+ * input device.
+*/
+
+enum wiimod_turntable_keys {
+ WIIMOD_TURNTABLE_KEY_G_RIGHT,
+ WIIMOD_TURNTABLE_KEY_R_RIGHT,
+ WIIMOD_TURNTABLE_KEY_B_RIGHT,
+ WIIMOD_TURNTABLE_KEY_G_LEFT,
+ WIIMOD_TURNTABLE_KEY_R_LEFT,
+ WIIMOD_TURNTABLE_KEY_B_LEFT,
+ WIIMOD_TURNTABLE_KEY_EUPHORIA,
+ WIIMOD_TURNTABLE_KEY_PLUS,
+ WIIMOD_TURNTABLE_KEY_MINUS,
+ WIIMOD_TURNTABLE_KEY_NUM
+};
+
+static const __u16 wiimod_turntable_map[] = {
+ BTN_1, /* WIIMOD_TURNTABLE_KEY_G_RIGHT */
+ BTN_2, /* WIIMOD_TURNTABLE_KEY_R_RIGHT */
+ BTN_3, /* WIIMOD_TURNTABLE_KEY_B_RIGHT */
+ BTN_4, /* WIIMOD_TURNTABLE_KEY_G_LEFT */
+ BTN_5, /* WIIMOD_TURNTABLE_KEY_R_LEFT */
+ BTN_6, /* WIIMOD_TURNTABLE_KEY_B_LEFT */
+ BTN_7, /* WIIMOD_TURNTABLE_KEY_EUPHORIA */
+ BTN_START, /* WIIMOD_TURNTABLE_KEY_PLUS */
+ BTN_SELECT, /* WIIMOD_TURNTABLE_KEY_MINUS */
+};
+
+static void wiimod_turntable_in_ext(struct wiimote_data *wdata, const __u8 *ext)
+{
+ __u8 be, cs, sx, sy, ed, rtt, rbg, rbr, rbb, ltt, lbg, lbr, lbb, bp, bm;
+ /*
+ * Byte | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+ *------+------+-----+-----+-----+-----+------+------+--------+
+ * 0 | RTT<4:3> | SX <5:0> |
+ * 1 | RTT<2:1> | SY <5:0> |
+ *------+------+-----+-----+-----+-----+------+------+--------+
+ * 2 |RTT<0>| ED<4:3> | CS<3:0> | RTT<5> |
+ *------+------+-----+-----+-----+-----+------+------+--------+
+ * 3 | ED<2:0> | LTT<4:0> |
+ *------+------+-----+-----+-----+-----+------+------+--------+
+ * 4 | 0 | 0 | LBR | B- | 0 | B+ | RBR | LTT<5> |
+ *------+------+-----+-----+-----+-----+------+------+--------+
+ * 5 | LBB | 0 | RBG | BE | LBG | RBB | 0 | 0 |
+ *------+------+-----+-----+-----+-----+------+------+--------+
+ * All pressed buttons are 0
+ *
+ * With Motion+ enabled, it will look like this:
+ * Byte | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |
+ *------+------+-----+-----+-----+-----+------+------+--------+
+ * 1 | RTT<4:3> | SX <5:1> | 0 |
+ * 2 | RTT<2:1> | SY <5:1> | 0 |
+ *------+------+-----+-----+-----+-----+------+------+--------+
+ * 3 |RTT<0>| ED<4:3> | CS<3:0> | RTT<5> |
+ *------+------+-----+-----+-----+-----+------+------+--------+
+ * 4 | ED<2:0> | LTT<4:0> |
+ *------+------+-----+-----+-----+-----+------+------+--------+
+ * 5 | 0 | 0 | LBR | B- | 0 | B+ | RBR | XXXX |
+ *------+------+-----+-----+-----+-----+------+------+--------+
+ * 6 | LBB | 0 | RBG | BE | LBG | RBB | XXXX | XXXX |
+ *------+------+-----+-----+-----+-----+------+------+--------+
+ */
+
+ be = !(ext[5] & 0x10);
+ cs = ((ext[2] & 0x1e));
+ sx = ext[0] & 0x3f;
+ sy = ext[1] & 0x3f;
+ ed = (ext[3] & 0xe0) >> 5;
+ rtt = ((ext[2] & 0x01) << 5 | (ext[0] & 0xc0) >> 3 | (ext[1] & 0xc0) >> 5 | ( ext[2] & 0x80 ) >> 7);
+ ltt = ((ext[4] & 0x01) << 5 | (ext[3] & 0x1f));
+ rbg = !(ext[5] & 0x20);
+ rbr = !(ext[4] & 0x02);
+ rbb = !(ext[5] & 0x04);
+ lbg = !(ext[5] & 0x08);
+ lbb = !(ext[5] & 0x80);
+ lbr = !(ext[4] & 0x20);
+ bm = !(ext[4] & 0x10);
+ bp = !(ext[4] & 0x04);
+
+ if (wdata->state.flags & WIIPROTO_FLAG_MP_ACTIVE) {
+ ltt = (ext[4] & 0x01) << 5;
+ sx &= 0x3e;
+ sy &= 0x3e;
+ }
+
+ input_report_abs(wdata->extension.input, ABS_X, sx);
+ input_report_abs(wdata->extension.input, ABS_Y, sy);
+ input_report_abs(wdata->extension.input, ABS_HAT0X, rtt);
+ input_report_abs(wdata->extension.input, ABS_HAT1X, ltt);
+ input_report_abs(wdata->extension.input, ABS_HAT2X, cs);
+ input_report_abs(wdata->extension.input, ABS_HAT3X, ed);
+ input_report_key(wdata->extension.input,
+ wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_G_RIGHT],
+ rbg);
+ input_report_key(wdata->extension.input,
+ wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_R_RIGHT],
+ rbr);
+ input_report_key(wdata->extension.input,
+ wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_B_RIGHT],
+ rbb);
+ input_report_key(wdata->extension.input,
+ wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_G_LEFT],
+ lbg);
+ input_report_key(wdata->extension.input,
+ wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_R_LEFT],
+ lbr);
+ input_report_key(wdata->extension.input,
+ wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_B_LEFT],
+ lbb);
+ input_report_key(wdata->extension.input,
+ wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_EUPHORIA],
+ be);
+ input_report_key(wdata->extension.input,
+ wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_PLUS],
+ bp);
+ input_report_key(wdata->extension.input,
+ wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_MINUS],
+ bm);
+
+ input_sync(wdata->extension.input);
+}
+
+static int wiimod_turntable_open(struct input_dev *dev)
+{
+ struct wiimote_data *wdata = input_get_drvdata(dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&wdata->state.lock, flags);
+ wdata->state.flags |= WIIPROTO_FLAG_EXT_USED;
+ wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
+ spin_unlock_irqrestore(&wdata->state.lock, flags);
+
+ return 0;
+}
+
+static void wiimod_turntable_close(struct input_dev *dev)
+{
+ struct wiimote_data *wdata = input_get_drvdata(dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&wdata->state.lock, flags);
+ wdata->state.flags &= ~WIIPROTO_FLAG_EXT_USED;
+ wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
+ spin_unlock_irqrestore(&wdata->state.lock, flags);
+}
+
+static int wiimod_turntable_probe(const struct wiimod_ops *ops,
+ struct wiimote_data *wdata)
+{
+ int ret, i;
+
+ wdata->extension.input = input_allocate_device();
+ if (!wdata->extension.input)
+ return -ENOMEM;
+
+ input_set_drvdata(wdata->extension.input, wdata);
+ wdata->extension.input->open = wiimod_turntable_open;
+ wdata->extension.input->close = wiimod_turntable_close;
+ wdata->extension.input->dev.parent = &wdata->hdev->dev;
+ wdata->extension.input->id.bustype = wdata->hdev->bus;
+ wdata->extension.input->id.vendor = wdata->hdev->vendor;
+ wdata->extension.input->id.product = wdata->hdev->product;
+ wdata->extension.input->id.version = wdata->hdev->version;
+ wdata->extension.input->name = WIIMOTE_NAME " Turntable";
+
+ set_bit(EV_KEY, wdata->extension.input->evbit);
+ for (i = 0; i < WIIMOD_TURNTABLE_KEY_NUM; ++i)
+ set_bit(wiimod_turntable_map[i],
+ wdata->extension.input->keybit);
+
+ set_bit(EV_ABS, wdata->extension.input->evbit);
+ set_bit(ABS_X, wdata->extension.input->absbit);
+ set_bit(ABS_Y, wdata->extension.input->absbit);
+ set_bit(ABS_HAT0X, wdata->extension.input->absbit);
+ set_bit(ABS_HAT1X, wdata->extension.input->absbit);
+ set_bit(ABS_HAT2X, wdata->extension.input->absbit);
+ set_bit(ABS_HAT3X, wdata->extension.input->absbit);
+ input_set_abs_params(wdata->extension.input,
+ ABS_X, 0, 63, 1, 0);
+ input_set_abs_params(wdata->extension.input,
+ ABS_Y, 63, 0, 1, 0);
+ input_set_abs_params(wdata->extension.input,
+ ABS_HAT0X, -8, 8, 0, 0);
+ input_set_abs_params(wdata->extension.input,
+ ABS_HAT1X, -8, 8, 0, 0);
+ input_set_abs_params(wdata->extension.input,
+ ABS_HAT2X, 0, 31, 1, 1);
+ input_set_abs_params(wdata->extension.input,
+ ABS_HAT3X, 0, 7, 0, 0);
+ ret = input_register_device(wdata->extension.input);
+ if (ret)
+ goto err_free;
+
+ return 0;
+
+err_free:
+ input_free_device(wdata->extension.input);
+ wdata->extension.input = NULL;
+ return ret;
+}
+
+static void wiimod_turntable_remove(const struct wiimod_ops *ops,
+ struct wiimote_data *wdata)
+{
+ if (!wdata->extension.input)
+ return;
+
+ input_unregister_device(wdata->extension.input);
+ wdata->extension.input = NULL;
+}
+
+static const struct wiimod_ops wiimod_turntable = {
+ .flags = 0,
+ .arg = 0,
+ .probe = wiimod_turntable_probe,
+ .remove = wiimod_turntable_remove,
+ .in_ext = wiimod_turntable_in_ext,
+};
+
/*
* Builtin Motion Plus
* This module simply sets the WIIPROTO_FLAG_BUILTIN_MP protocol flag which
@@ -2657,4 +2881,5 @@ const struct wiimod_ops *wiimod_ext_table[WIIMOTE_EXT_NUM] = {
[WIIMOTE_EXT_PRO_CONTROLLER] = &wiimod_pro,
[WIIMOTE_EXT_DRUMS] = &wiimod_drums,
[WIIMOTE_EXT_GUITAR] = &wiimod_guitar,
+ [WIIMOTE_EXT_TURNTABLE] = &wiimod_turntable,
};
diff --git a/drivers/hid/hid-wiimote.h b/drivers/hid/hid-wiimote.h
index ad4ff837f43e..9c12f63f6dd2 100644
--- a/drivers/hid/hid-wiimote.h
+++ b/drivers/hid/hid-wiimote.h
@@ -88,6 +88,7 @@ enum wiimote_exttype {
WIIMOTE_EXT_PRO_CONTROLLER,
WIIMOTE_EXT_DRUMS,
WIIMOTE_EXT_GUITAR,
+ WIIMOTE_EXT_TURNTABLE,
WIIMOTE_EXT_NUM,
};
diff --git a/drivers/hid/i2c-hid/i2c-hid-core.c b/drivers/hid/i2c-hid/i2c-hid-core.c
index 9050bd5e24ac..b86b62f97108 100644
--- a/drivers/hid/i2c-hid/i2c-hid-core.c
+++ b/drivers/hid/i2c-hid/i2c-hid-core.c
@@ -554,7 +554,8 @@ static void i2c_hid_get_input(struct i2c_hid *ihid)
i2c_hid_dbg(ihid, "input: %*ph\n", ret_size, ihid->inbuf);
if (test_bit(I2C_HID_STARTED, &ihid->flags)) {
- pm_wakeup_event(&ihid->client->dev, 0);
+ if (ihid->hid->group != HID_GROUP_RMI)
+ pm_wakeup_event(&ihid->client->dev, 0);
hid_input_report(ihid->hid, HID_INPUT_REPORT,
ihid->inbuf + sizeof(__le16),
diff --git a/drivers/hid/i2c-hid/i2c-hid-of-elan.c b/drivers/hid/i2c-hid/i2c-hid-of-elan.c
index 2d991325e734..76ddc8be1cbb 100644
--- a/drivers/hid/i2c-hid/i2c-hid-of-elan.c
+++ b/drivers/hid/i2c-hid/i2c-hid-of-elan.c
@@ -68,8 +68,7 @@ static void elan_i2c_hid_power_down(struct i2chid_ops *ops)
regulator_disable(ihid_elan->vcc33);
}
-static int i2c_hid_of_elan_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int i2c_hid_of_elan_probe(struct i2c_client *client)
{
struct i2c_hid_of_elan *ihid_elan;
@@ -119,7 +118,7 @@ static struct i2c_driver elan_i2c_hid_ts_driver = {
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = of_match_ptr(elan_i2c_hid_of_match),
},
- .probe = i2c_hid_of_elan_probe,
+ .probe_new = i2c_hid_of_elan_probe,
.remove = i2c_hid_core_remove,
.shutdown = i2c_hid_core_shutdown,
};
diff --git a/drivers/hid/i2c-hid/i2c-hid-of-goodix.c b/drivers/hid/i2c-hid/i2c-hid-of-goodix.c
index ec6c73f75ffe..29c6cb174032 100644
--- a/drivers/hid/i2c-hid/i2c-hid-of-goodix.c
+++ b/drivers/hid/i2c-hid/i2c-hid-of-goodix.c
@@ -87,8 +87,7 @@ static int ihid_goodix_vdd_notify(struct notifier_block *nb,
return ret;
}
-static int i2c_hid_of_goodix_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int i2c_hid_of_goodix_probe(struct i2c_client *client)
{
struct i2c_hid_of_goodix *ihid_goodix;
int ret;
@@ -167,7 +166,7 @@ static struct i2c_driver goodix_i2c_hid_ts_driver = {
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = of_match_ptr(goodix_i2c_hid_of_match),
},
- .probe = i2c_hid_of_goodix_probe,
+ .probe_new = i2c_hid_of_goodix_probe,
.remove = i2c_hid_core_remove,
.shutdown = i2c_hid_core_shutdown,
};
diff --git a/drivers/hid/i2c-hid/i2c-hid-of.c b/drivers/hid/i2c-hid/i2c-hid-of.c
index 97a27a803f58..10176568133a 100644
--- a/drivers/hid/i2c-hid/i2c-hid-of.c
+++ b/drivers/hid/i2c-hid/i2c-hid-of.c
@@ -66,8 +66,7 @@ static void i2c_hid_of_power_down(struct i2chid_ops *ops)
ihid_of->supplies);
}
-static int i2c_hid_of_probe(struct i2c_client *client,
- const struct i2c_device_id *dev_id)
+static int i2c_hid_of_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct i2c_hid_of *ihid_of;
@@ -138,7 +137,7 @@ static struct i2c_driver i2c_hid_of_driver = {
.of_match_table = of_match_ptr(i2c_hid_of_match),
},
- .probe = i2c_hid_of_probe,
+ .probe_new = i2c_hid_of_probe,
.remove = i2c_hid_core_remove,
.shutdown = i2c_hid_core_shutdown,
.id_table = i2c_hid_of_id_table,
diff --git a/drivers/hid/intel-ish-hid/ishtp/client.c b/drivers/hid/intel-ish-hid/ishtp/client.c
index df0a825694f5..2d92fc129ce4 100644
--- a/drivers/hid/intel-ish-hid/ishtp/client.c
+++ b/drivers/hid/intel-ish-hid/ishtp/client.c
@@ -841,7 +841,6 @@ void recv_ishtp_cl_msg(struct ishtp_device *dev,
unsigned char *buffer = NULL;
struct ishtp_cl_rb *complete_rb = NULL;
unsigned long flags;
- int rb_count;
if (ishtp_hdr->reserved) {
dev_err(dev->devc, "corrupted message header.\n");
@@ -855,9 +854,7 @@ void recv_ishtp_cl_msg(struct ishtp_device *dev,
}
spin_lock_irqsave(&dev->read_list_spinlock, flags);
- rb_count = -1;
list_for_each_entry(rb, &dev->read_list.list, list) {
- ++rb_count;
cl = rb->cl;
if (!cl || !(cl->host_client_id == ishtp_hdr->host_addr &&
cl->fw_client_id == ishtp_hdr->fw_addr) ||