mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-07 13:53:24 +00:00
for-linus-2022121301
-----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIVAwUAY5iCHqZi849r7WBJAQKEhRAAqy2xfeg/DSGDhQItEWggL0H+2dPK7KWD Rw0ZimOVdRdMENGyoDxyFQ3VFd0/FJBVF7AtALs32zW40S/eAwuds81sklMsr6D1 9LDalDlkCCyBKDSdNfPTEVgnIhJPiSuHX7HjJdoPUXdiRzEf+TxKaEQuGuhVbizw cfKChyKI/o173glvGVkzmA1iqCFPY+/tM6HJCHxxL51wko47wJZ9eubdZoxFpDU7 mHyqyJYrcjaFh7iPOda77WAIT6gqQFJi9pbH5SBP5Hs/yZPvkw9OZQLrCGx8Zehe Trdh0GTBXo67D0UBHiOafqQSj9CqDunBLBOOD4uf5K9CfzIBhyHS7C2rtrY85Lz2 6/WevKgZ0MuLma87DcDkb2nNEnjQbV8OPcD10qk7k2yDl8SezoOiS+g7rOTo42P1 80zt68znsEQCJBAqG3X8x+bU767r73FIJlFSERvRz1j0N05EpScIs5NR9cRK/iGB 1oAxj0H04jOR0K+1mHhPWxIQpE2ypHOLhYlR9RROWbvM9ilJpaCaESHTZYMv83U/ QuZR4ytH9uqqUIsweK9ExUiu3RK5YEC/H/GuFSORYFl40jB/iRmbRGTKiLNOGraH oYE/sIWYUtsPCOLFeIPGG8pd5I9spImlRUkSqBfx803I9SQPG/rx4q7rcol6YOlS FwnRlMSGz0Y= =KkQc -----END PGP SIGNATURE----- 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 ...
This commit is contained in:
commit
2043f9a37d
@ -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 (usage->code != code) {
|
||||
input_event_with_scancode(input, usage->type, code, usage->hid, value);
|
||||
|
||||
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 (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;
|
||||
read_off = *msgs[0].buf;
|
||||
|
||||
ft260_dbg("read_off %#x left_len %d len %d\n", read_off,
|
||||
left_len, len);
|
||||
pr_info("%s: off %#x rlen %d wlen %d\n", __func__,
|
||||
read_off, rd_len, wr_len);
|
||||
}
|
||||
|
||||
ret = ft260_i2c_write(dev, addr, (u8 *)&read_off, msgs[0].len,
|
||||
FT260_FLAG_START);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = ft260_i2c_write(dev, addr, msgs[0].buf, wr_len,
|
||||
FT260_FLAG_START);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = ft260_i2c_read(dev, addr, read_buf, len,
|
||||
FT260_FLAG_START_STOP);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
left_len -= len;
|
||||
read_buf += len;
|
||||
read_off += len;
|
||||
|
||||
} 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 ret;
|
||||
#endif
|
||||
|
||||
#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
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static void mcp2221_remove(struct hid_device *hdev)
|
||||
{
|
||||
struct mcp2221 *mcp = hid_get_drvdata(hdev);
|
||||
|
||||
i2c_del_adapter(&mcp->adapter);
|
||||
hid_hw_close(mcp->hdev);
|
||||
hid_hw_stop(mcp->hdev);
|
||||
}
|
||||
|
||||
static const struct hid_device_id mcp2221_devices[] = {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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) ||
|
||||
|
Loading…
Reference in New Issue
Block a user