Commit 2043f9a3 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'for-linus-2022121301' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid

Pull HID updates from Jiri Kosina:

 - iio support for the MCP2221 HID driver (Matt Ranostay)

 - support for more than one hinge sensor in hid-sensor-custom (Yauhen
   Kharuzhy)

 - PS DualShock 4 controller support (Roderick Colenbrander)

 - XP-PEN Deco LW support (José Expósito)

 - other assorted code cleanups and device ID/quirk addtions

* tag 'for-linus-2022121301' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid: (51 commits)
  HID: logitech HID++: Send SwID in GetProtocolVersion
  HID: hid-elan: use default remove for hid device
  HID: hid-alps: use default remove for hid device
  HID: hid-sensor-custom: set fixed size for custom attributes
  HID: i2c: let RMI devices decide what constitutes wakeup event
  HID: playstation: fix DualShock4 bluetooth CRC endian issue.
  HID: playstation: fix DualShock4 bluetooth memory corruption bug.
  HID: apple: Swap Control and Command keys on Apple keyboards
  HID: intel-ish-hid: ishtp: remove variable rb_count
  HID: uclogic: Standardize test name prefix
  HID: hid-sensor-custom: Allow more than one hinge angle sensor
  HID: ft260: fix 'cast to restricted' kernel CI bot warnings
  HID: ft260: missed NACK from busy device
  HID: ft260: fix a NULL pointer dereference in ft260_i2c_write
  HID: ft260: wake up device from power saving mode
  HID: ft260: missed NACK from big i2c read
  HID: ft260: remove SMBus Quick command support
  HID: ft260: skip unexpected HID input reports
  HID: ft260: do not populate /dev/hidraw device
  HID: ft260: improve i2c large reads performance
  ...
parents 86a0b425 f722052c
......@@ -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.
......
......@@ -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,
......
......@@ -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),
......
......@@ -507,11 +507,6 @@ static int elan_probe(struct hid_device *hdev, const struct hid_device_id *id)
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);
......
......@@ -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 @@ static int ft260_smbus_xfer(struct i2c_adapter *adapter, u16 addr, u16 flags,
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 = {
......
......@@ -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,
......
......@@ -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);
......
......@@ -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) {
......
......@@ -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[] = {
......
......@@ -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 @@ static struct ps_device *dualsense_create(struct hid_device *hdev)
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) },
......
......@@ -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);
......
......@@ -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;
......
......@@ -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),
{}
};
......
......@@ -18,6 +18,7 @@
#include "usbhid/usbhid.h"
#include "hid-ids.h"
#include <linux/ctype.h>
#include <linux/string.h>
#include <asm/unaligned.h>
/**
......@@ -1211,6 +1212,69 @@ static int uclogic_params_ugee_v2_init_frame_mouse(struct uclogic_params *p)
return rc;
}
/**
* 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));
......
......@@ -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),
{}
};
......
......@@ -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), */
......
......@@ -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;
......
......@@ -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");
......
......@@ -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,
};
......@@ -88,6 +88,7 @@ enum wiimote_exttype {
WIIMOTE_EXT_PRO_CONTROLLER,
WIIMOTE_EXT_DRUMS,
WIIMOTE_EXT_GUITAR,
WIIMOTE_EXT_TURNTABLE,
WIIMOTE_EXT_NUM,
};
......
......@@ -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),
......
......@@ -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,
};
......
......@@ -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,
};
......
......@@ -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,
......
......@@ -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) ||
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment