Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid
Pull HID updates from Jiri Kosina: - rework of generic input handling which ultimately makes the processing of tablet events more generic and reliable (Benjamin Tissoires) - fixes for handling unnumbered reports fully correctly in i2c-hid (Angela Czubak, Dmitry Torokhov) - untangling of intermingled code for sending and handling output reports in i2c-hid (Dmitry Torokhov) - Apple magic keyboard support improvements for newer models (José Expósito) - Apple T2 Macs support improvements (Aun-Ali Zaidi, Paul Pawlowski) - driver for Razer Blackwidow keyboards (Jelle van der Waa) - driver for SiGma Micro keyboards (Desmond Lim) - integration of first part of DIGImend patches in order to ultimately vastly improve Linux support of tablets (Nikolai Kondrashov, José Expósito) * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid: (55 commits) HID: intel-ish-hid: Use dma_alloc_coherent for firmware update Input: docs: add more details on the use of BTN_TOOL HID: input: accommodate priorities for slotted devices HID: input: remove the need for HID_QUIRK_INVERT HID: input: enforce Invert usage to be processed before InRange HID: core: for input reports, process the usages by priority list HID: compute an ordered list of input fields to process HID: input: move up out-of-range processing of input values HID: input: rework spaghetti code with switch statements HID: input: tag touchscreens as such if the physical is not there HID: core: split data fetching from processing in hid_input_field() HID: core: de-duplicate some code in hid_input_field() HID: core: statically allocate read buffers HID: uclogic: Support multiple frame input devices HID: uclogic: Define report IDs before their descriptors HID: uclogic: Put version first in rdesc namespace HID: uclogic: Use "frame" instead of "buttonpad" HID: uclogic: Use different constants for frame report IDs HID: uclogic: Specify total report size to buttonpad macro HID: uclogic: Switch to matching subreport bytes ...
This commit is contained in:
commit
5e206459f6
@ -137,7 +137,11 @@ A few EV_KEY codes have special meanings:
|
||||
code should be set to a value of 1. When the tool is no longer interacting
|
||||
with the input device, the BTN_TOOL_<name> code should be reset to 0. All
|
||||
trackpads, tablets, and touchscreens should use at least one BTN_TOOL_<name>
|
||||
code when events are generated.
|
||||
code when events are generated. Likewise all trackpads, tablets, and
|
||||
touchscreens should export only one BTN_TOOL_<name> at a time. To not break
|
||||
existing userspace, it is recommended to not switch tool in one EV_SYN frame
|
||||
but first emitting the old BTN_TOOL_<name> at 0, then emit one SYN_REPORT
|
||||
and then set the new BTN_TOOL_<name> at 1.
|
||||
|
||||
* BTN_TOUCH:
|
||||
|
||||
|
@ -128,6 +128,8 @@ config HID_ACRUX_FF
|
||||
config HID_APPLE
|
||||
tristate "Apple {i,Power,Mac}Books"
|
||||
depends on HID
|
||||
depends on LEDS_CLASS
|
||||
depends on NEW_LEDS
|
||||
default !EXPERT
|
||||
help
|
||||
Support for some Apple devices which less or more break
|
||||
@ -929,6 +931,13 @@ config PLAYSTATION_FF
|
||||
Say Y here if you would like to enable force feedback support for
|
||||
PlayStation game controllers.
|
||||
|
||||
config HID_RAZER
|
||||
tristate "Razer non-fully HID-compliant devices"
|
||||
depends on HID
|
||||
help
|
||||
Support for Razer devices that are not fully compliant with the
|
||||
HID standard.
|
||||
|
||||
config HID_PRIMAX
|
||||
tristate "Primax non-fully HID-compliant devices"
|
||||
depends on HID
|
||||
@ -984,6 +993,16 @@ config HID_SEMITEK
|
||||
- Woo-dy
|
||||
- X-Bows Nature/Knight
|
||||
|
||||
config HID_SIGMAMICRO
|
||||
tristate "SiGma Micro-based keyboards"
|
||||
depends on USB_HID
|
||||
help
|
||||
Support for keyboards that use the SiGma Micro (a.k.a SigmaChip) IC.
|
||||
|
||||
Supported devices:
|
||||
- Landslides KR-700
|
||||
- Rapoo V500
|
||||
|
||||
config HID_SONY
|
||||
tristate "Sony PS2/3/4 accessories"
|
||||
depends on USB_HID
|
||||
|
@ -99,6 +99,7 @@ hid-picolcd-$(CONFIG_DEBUG_FS) += hid-picolcd_debugfs.o
|
||||
obj-$(CONFIG_HID_PLANTRONICS) += hid-plantronics.o
|
||||
obj-$(CONFIG_HID_PLAYSTATION) += hid-playstation.o
|
||||
obj-$(CONFIG_HID_PRIMAX) += hid-primax.o
|
||||
obj-$(CONFIG_HID_RAZER) += hid-razer.o
|
||||
obj-$(CONFIG_HID_REDRAGON) += hid-redragon.o
|
||||
obj-$(CONFIG_HID_RETRODE) += hid-retrode.o
|
||||
obj-$(CONFIG_HID_ROCCAT) += hid-roccat.o hid-roccat-common.o \
|
||||
@ -109,6 +110,7 @@ obj-$(CONFIG_HID_RMI) += hid-rmi.o
|
||||
obj-$(CONFIG_HID_SAITEK) += hid-saitek.o
|
||||
obj-$(CONFIG_HID_SAMSUNG) += hid-samsung.o
|
||||
obj-$(CONFIG_HID_SEMITEK) += hid-semitek.o
|
||||
obj-$(CONFIG_HID_SIGMAMICRO) += hid-sigmamicro.o
|
||||
obj-$(CONFIG_HID_SMARTJOYPLUS) += hid-sjoy.o
|
||||
obj-$(CONFIG_HID_SONY) += hid-sony.o
|
||||
obj-$(CONFIG_HID_SPEEDLINK) += hid-speedlink.o
|
||||
|
@ -301,11 +301,8 @@ static int amd_mp2_pci_probe(struct pci_dev *pdev, const struct pci_device_id *i
|
||||
pci_set_master(pdev);
|
||||
rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
|
||||
if (rc) {
|
||||
rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
|
||||
if (rc) {
|
||||
dev_err(&pdev->dev, "failed to set DMA mask\n");
|
||||
return rc;
|
||||
}
|
||||
dev_err(&pdev->dev, "failed to set DMA mask\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
privdata->cl_data = devm_kzalloc(&pdev->dev, sizeof(struct amdtp_cl_data), GFP_KERNEL);
|
||||
|
@ -7,6 +7,7 @@
|
||||
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
|
||||
* Copyright (c) 2006-2007 Jiri Kosina
|
||||
* Copyright (c) 2008 Jiri Slaby <jirislaby@gmail.com>
|
||||
* Copyright (c) 2019 Paul Pawlowski <paul@mrarm.io>
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -33,6 +34,7 @@
|
||||
/* BIT(7) reserved, was: APPLE_IGNORE_HIDINPUT */
|
||||
#define APPLE_NUMLOCK_EMULATION BIT(8)
|
||||
#define APPLE_RDESC_BATTERY BIT(9)
|
||||
#define APPLE_BACKLIGHT_CTL BIT(10)
|
||||
|
||||
#define APPLE_FLAG_FKEY 0x01
|
||||
|
||||
@ -61,6 +63,12 @@ MODULE_PARM_DESC(swap_fn_leftctrl, "Swap the Fn and left Control keys. "
|
||||
"(For people who want to keep PC keyboard muscle memory. "
|
||||
"[0] = as-is, Mac layout, 1 = swapped, PC layout)");
|
||||
|
||||
struct apple_sc_backlight {
|
||||
struct led_classdev cdev;
|
||||
struct hid_device *hdev;
|
||||
unsigned short backlight_off, backlight_on_min, backlight_on_max;
|
||||
};
|
||||
|
||||
struct apple_sc {
|
||||
struct hid_device *hdev;
|
||||
unsigned long quirks;
|
||||
@ -68,6 +76,7 @@ struct apple_sc {
|
||||
unsigned int fn_found;
|
||||
DECLARE_BITMAP(pressed_numlock, KEY_CNT);
|
||||
struct timer_list battery_timer;
|
||||
struct apple_sc_backlight *backlight;
|
||||
};
|
||||
|
||||
struct apple_key_translation {
|
||||
@ -76,6 +85,61 @@ struct apple_key_translation {
|
||||
u8 flags;
|
||||
};
|
||||
|
||||
static const struct apple_key_translation magic_keyboard_alu_fn_keys[] = {
|
||||
{ KEY_BACKSPACE, KEY_DELETE },
|
||||
{ KEY_ENTER, KEY_INSERT },
|
||||
{ KEY_F1, KEY_BRIGHTNESSDOWN, APPLE_FLAG_FKEY },
|
||||
{ KEY_F2, KEY_BRIGHTNESSUP, APPLE_FLAG_FKEY },
|
||||
{ KEY_F3, KEY_SCALE, APPLE_FLAG_FKEY },
|
||||
{ KEY_F4, KEY_DASHBOARD, APPLE_FLAG_FKEY },
|
||||
{ KEY_F6, KEY_NUMLOCK, APPLE_FLAG_FKEY },
|
||||
{ KEY_F7, KEY_PREVIOUSSONG, APPLE_FLAG_FKEY },
|
||||
{ KEY_F8, KEY_PLAYPAUSE, APPLE_FLAG_FKEY },
|
||||
{ KEY_F9, KEY_NEXTSONG, APPLE_FLAG_FKEY },
|
||||
{ KEY_F10, KEY_MUTE, APPLE_FLAG_FKEY },
|
||||
{ KEY_F11, KEY_VOLUMEDOWN, APPLE_FLAG_FKEY },
|
||||
{ KEY_F12, KEY_VOLUMEUP, APPLE_FLAG_FKEY },
|
||||
{ KEY_UP, KEY_PAGEUP },
|
||||
{ KEY_DOWN, KEY_PAGEDOWN },
|
||||
{ KEY_LEFT, KEY_HOME },
|
||||
{ KEY_RIGHT, KEY_END },
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct apple_key_translation magic_keyboard_2015_fn_keys[] = {
|
||||
{ KEY_BACKSPACE, KEY_DELETE },
|
||||
{ KEY_ENTER, KEY_INSERT },
|
||||
{ KEY_F1, KEY_BRIGHTNESSDOWN, APPLE_FLAG_FKEY },
|
||||
{ KEY_F2, KEY_BRIGHTNESSUP, APPLE_FLAG_FKEY },
|
||||
{ KEY_F3, KEY_SCALE, APPLE_FLAG_FKEY },
|
||||
{ KEY_F4, KEY_DASHBOARD, APPLE_FLAG_FKEY },
|
||||
{ KEY_F7, KEY_PREVIOUSSONG, APPLE_FLAG_FKEY },
|
||||
{ KEY_F8, KEY_PLAYPAUSE, APPLE_FLAG_FKEY },
|
||||
{ KEY_F9, KEY_NEXTSONG, APPLE_FLAG_FKEY },
|
||||
{ KEY_F10, KEY_MUTE, APPLE_FLAG_FKEY },
|
||||
{ KEY_F11, KEY_VOLUMEDOWN, APPLE_FLAG_FKEY },
|
||||
{ KEY_F12, KEY_VOLUMEUP, APPLE_FLAG_FKEY },
|
||||
{ KEY_UP, KEY_PAGEUP },
|
||||
{ KEY_DOWN, KEY_PAGEDOWN },
|
||||
{ KEY_LEFT, KEY_HOME },
|
||||
{ KEY_RIGHT, KEY_END },
|
||||
{ }
|
||||
};
|
||||
|
||||
struct apple_backlight_config_report {
|
||||
u8 report_id;
|
||||
u8 version;
|
||||
u16 backlight_off, backlight_on_min, backlight_on_max;
|
||||
};
|
||||
|
||||
struct apple_backlight_set_report {
|
||||
u8 report_id;
|
||||
u8 version;
|
||||
u16 backlight;
|
||||
u16 rate;
|
||||
};
|
||||
|
||||
|
||||
static const struct apple_key_translation apple2021_fn_keys[] = {
|
||||
{ KEY_BACKSPACE, KEY_DELETE },
|
||||
{ KEY_ENTER, KEY_INSERT },
|
||||
@ -119,6 +183,51 @@ static const struct apple_key_translation macbookair_fn_keys[] = {
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct apple_key_translation macbookpro_no_esc_fn_keys[] = {
|
||||
{ KEY_BACKSPACE, KEY_DELETE },
|
||||
{ KEY_ENTER, KEY_INSERT },
|
||||
{ KEY_GRAVE, KEY_ESC },
|
||||
{ KEY_1, KEY_F1 },
|
||||
{ KEY_2, KEY_F2 },
|
||||
{ KEY_3, KEY_F3 },
|
||||
{ KEY_4, KEY_F4 },
|
||||
{ KEY_5, KEY_F5 },
|
||||
{ KEY_6, KEY_F6 },
|
||||
{ KEY_7, KEY_F7 },
|
||||
{ KEY_8, KEY_F8 },
|
||||
{ KEY_9, KEY_F9 },
|
||||
{ KEY_0, KEY_F10 },
|
||||
{ KEY_MINUS, KEY_F11 },
|
||||
{ KEY_EQUAL, KEY_F12 },
|
||||
{ KEY_UP, KEY_PAGEUP },
|
||||
{ KEY_DOWN, KEY_PAGEDOWN },
|
||||
{ KEY_LEFT, KEY_HOME },
|
||||
{ KEY_RIGHT, KEY_END },
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct apple_key_translation macbookpro_dedicated_esc_fn_keys[] = {
|
||||
{ KEY_BACKSPACE, KEY_DELETE },
|
||||
{ KEY_ENTER, KEY_INSERT },
|
||||
{ KEY_1, KEY_F1 },
|
||||
{ KEY_2, KEY_F2 },
|
||||
{ KEY_3, KEY_F3 },
|
||||
{ KEY_4, KEY_F4 },
|
||||
{ KEY_5, KEY_F5 },
|
||||
{ KEY_6, KEY_F6 },
|
||||
{ KEY_7, KEY_F7 },
|
||||
{ KEY_8, KEY_F8 },
|
||||
{ KEY_9, KEY_F9 },
|
||||
{ KEY_0, KEY_F10 },
|
||||
{ KEY_MINUS, KEY_F11 },
|
||||
{ KEY_EQUAL, KEY_F12 },
|
||||
{ KEY_UP, KEY_PAGEUP },
|
||||
{ KEY_DOWN, KEY_PAGEDOWN },
|
||||
{ KEY_LEFT, KEY_HOME },
|
||||
{ KEY_RIGHT, KEY_END },
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct apple_key_translation apple_fn_keys[] = {
|
||||
{ KEY_BACKSPACE, KEY_DELETE },
|
||||
{ KEY_ENTER, KEY_INSERT },
|
||||
@ -202,6 +311,15 @@ static const struct apple_key_translation swapped_fn_leftctrl_keys[] = {
|
||||
{ }
|
||||
};
|
||||
|
||||
static inline void apple_setup_key_translation(struct input_dev *input,
|
||||
const struct apple_key_translation *table)
|
||||
{
|
||||
const struct apple_key_translation *trans;
|
||||
|
||||
for (trans = table; trans->from; trans++)
|
||||
set_bit(trans->to, input->keybit);
|
||||
}
|
||||
|
||||
static const struct apple_key_translation *apple_find_translation(
|
||||
const struct apple_key_translation *table, u16 from)
|
||||
{
|
||||
@ -242,10 +360,34 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input,
|
||||
}
|
||||
|
||||
if (fnmode) {
|
||||
if (hid->product == USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_2021 ||
|
||||
hid->product == USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_FINGERPRINT_2021 ||
|
||||
hid->product == USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_NUMPAD_2021)
|
||||
if (hid->product == USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI ||
|
||||
hid->product == USB_DEVICE_ID_APPLE_ALU_WIRELESS_ISO ||
|
||||
hid->product == USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS ||
|
||||
hid->product == USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI ||
|
||||
hid->product == USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO ||
|
||||
hid->product == USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS ||
|
||||
hid->product == USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ANSI ||
|
||||
hid->product == USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ISO ||
|
||||
hid->product == USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_JIS)
|
||||
table = magic_keyboard_alu_fn_keys;
|
||||
else if (hid->product == USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_2015 ||
|
||||
hid->product == USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_NUMPAD_2015)
|
||||
table = magic_keyboard_2015_fn_keys;
|
||||
else if (hid->product == USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_2021 ||
|
||||
hid->product == USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_FINGERPRINT_2021 ||
|
||||
hid->product == USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_NUMPAD_2021)
|
||||
table = apple2021_fn_keys;
|
||||
else if (hid->product == USB_DEVICE_ID_APPLE_WELLSPRINGT2_J132 ||
|
||||
hid->product == USB_DEVICE_ID_APPLE_WELLSPRINGT2_J680 ||
|
||||
hid->product == USB_DEVICE_ID_APPLE_WELLSPRINGT2_J213)
|
||||
table = macbookpro_no_esc_fn_keys;
|
||||
else if (hid->product == USB_DEVICE_ID_APPLE_WELLSPRINGT2_J214K ||
|
||||
hid->product == USB_DEVICE_ID_APPLE_WELLSPRINGT2_J223 ||
|
||||
hid->product == USB_DEVICE_ID_APPLE_WELLSPRINGT2_J152F)
|
||||
table = macbookpro_dedicated_esc_fn_keys;
|
||||
else if (hid->product == USB_DEVICE_ID_APPLE_WELLSPRINGT2_J140K ||
|
||||
hid->product == USB_DEVICE_ID_APPLE_WELLSPRINGT2_J230K)
|
||||
table = apple_fn_keys;
|
||||
else if (hid->product >= USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI &&
|
||||
hid->product <= USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS)
|
||||
table = macbookair_fn_keys;
|
||||
@ -452,30 +594,21 @@ static __u8 *apple_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
|
||||
static void apple_setup_input(struct input_dev *input)
|
||||
{
|
||||
const struct apple_key_translation *trans;
|
||||
|
||||
set_bit(KEY_NUMLOCK, input->keybit);
|
||||
|
||||
/* Enable all needed keys */
|
||||
for (trans = apple_fn_keys; trans->from; trans++)
|
||||
set_bit(trans->to, input->keybit);
|
||||
apple_setup_key_translation(input, apple_fn_keys);
|
||||
apple_setup_key_translation(input, powerbook_fn_keys);
|
||||
apple_setup_key_translation(input, powerbook_numlock_keys);
|
||||
apple_setup_key_translation(input, apple_iso_keyboard);
|
||||
apple_setup_key_translation(input, magic_keyboard_alu_fn_keys);
|
||||
apple_setup_key_translation(input, magic_keyboard_2015_fn_keys);
|
||||
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);
|
||||
|
||||
for (trans = powerbook_fn_keys; trans->from; trans++)
|
||||
set_bit(trans->to, input->keybit);
|
||||
|
||||
for (trans = powerbook_numlock_keys; trans->from; trans++)
|
||||
set_bit(trans->to, input->keybit);
|
||||
|
||||
for (trans = apple_iso_keyboard; trans->from; trans++)
|
||||
set_bit(trans->to, input->keybit);
|
||||
|
||||
for (trans = apple2021_fn_keys; trans->from; trans++)
|
||||
set_bit(trans->to, input->keybit);
|
||||
|
||||
if (swap_fn_leftctrl) {
|
||||
for (trans = swapped_fn_leftctrl_keys; trans->from; trans++)
|
||||
set_bit(trans->to, input->keybit);
|
||||
}
|
||||
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,
|
||||
@ -530,6 +663,105 @@ static int apple_input_configured(struct hid_device *hdev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool apple_backlight_check_support(struct hid_device *hdev)
|
||||
{
|
||||
int i;
|
||||
unsigned int hid;
|
||||
struct hid_report *report;
|
||||
|
||||
list_for_each_entry(report, &hdev->report_enum[HID_INPUT_REPORT].report_list, list) {
|
||||
for (i = 0; i < report->maxfield; i++) {
|
||||
hid = report->field[i]->usage->hid;
|
||||
if ((hid & HID_USAGE_PAGE) == HID_UP_MSVENDOR && (hid & HID_USAGE) == 0xf)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int apple_backlight_set(struct hid_device *hdev, u16 value, u16 rate)
|
||||
{
|
||||
int ret = 0;
|
||||
struct apple_backlight_set_report *rep;
|
||||
|
||||
rep = kmalloc(sizeof(*rep), GFP_KERNEL);
|
||||
if (rep == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
rep->report_id = 0xB0;
|
||||
rep->version = 1;
|
||||
rep->backlight = value;
|
||||
rep->rate = rate;
|
||||
|
||||
ret = hid_hw_raw_request(hdev, 0xB0u, (u8 *) rep, sizeof(*rep),
|
||||
HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
|
||||
|
||||
kfree(rep);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int apple_backlight_led_set(struct led_classdev *led_cdev,
|
||||
enum led_brightness brightness)
|
||||
{
|
||||
struct apple_sc_backlight *backlight = container_of(led_cdev,
|
||||
struct apple_sc_backlight, cdev);
|
||||
|
||||
return apple_backlight_set(backlight->hdev, brightness, 0);
|
||||
}
|
||||
|
||||
static int apple_backlight_init(struct hid_device *hdev)
|
||||
{
|
||||
int ret;
|
||||
struct apple_sc *asc = hid_get_drvdata(hdev);
|
||||
struct apple_backlight_config_report *rep;
|
||||
|
||||
if (!apple_backlight_check_support(hdev))
|
||||
return -EINVAL;
|
||||
|
||||
rep = kmalloc(0x200, GFP_KERNEL);
|
||||
if (rep == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = hid_hw_raw_request(hdev, 0xBFu, (u8 *) rep, sizeof(*rep),
|
||||
HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
|
||||
if (ret < 0) {
|
||||
hid_err(hdev, "backlight request failed: %d\n", ret);
|
||||
goto cleanup_and_exit;
|
||||
}
|
||||
if (ret < 8 || rep->version != 1) {
|
||||
hid_err(hdev, "backlight config struct: bad version %i\n", rep->version);
|
||||
ret = -EINVAL;
|
||||
goto cleanup_and_exit;
|
||||
}
|
||||
|
||||
hid_dbg(hdev, "backlight config: off=%u, on_min=%u, on_max=%u\n",
|
||||
rep->backlight_off, rep->backlight_on_min, rep->backlight_on_max);
|
||||
|
||||
asc->backlight = devm_kzalloc(&hdev->dev, sizeof(*asc->backlight), GFP_KERNEL);
|
||||
if (!asc->backlight) {
|
||||
ret = -ENOMEM;
|
||||
goto cleanup_and_exit;
|
||||
}
|
||||
|
||||
asc->backlight->hdev = hdev;
|
||||
asc->backlight->cdev.name = "apple::kbd_backlight";
|
||||
asc->backlight->cdev.max_brightness = rep->backlight_on_max;
|
||||
asc->backlight->cdev.brightness_set_blocking = apple_backlight_led_set;
|
||||
|
||||
ret = apple_backlight_set(hdev, 0, 0);
|
||||
if (ret < 0) {
|
||||
hid_err(hdev, "backlight set request failed: %d\n", ret);
|
||||
goto cleanup_and_exit;
|
||||
}
|
||||
|
||||
ret = devm_led_classdev_register(&hdev->dev, &asc->backlight->cdev);
|
||||
|
||||
cleanup_and_exit:
|
||||
kfree(rep);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int apple_probe(struct hid_device *hdev,
|
||||
const struct hid_device_id *id)
|
||||
{
|
||||
@ -565,6 +797,9 @@ static int apple_probe(struct hid_device *hdev,
|
||||
jiffies + msecs_to_jiffies(APPLE_BATTERY_TIMEOUT_MS));
|
||||
apple_fetch_battery(hdev);
|
||||
|
||||
if (quirks & APPLE_BACKLIGHT_CTL)
|
||||
apple_backlight_init(hdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -736,6 +971,22 @@ static const struct hid_device_id apple_devices[] = {
|
||||
.driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK },
|
||||
{ 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 },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J132),
|
||||
.driver_data = APPLE_HAS_FN | APPLE_BACKLIGHT_CTL },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J680),
|
||||
.driver_data = APPLE_HAS_FN | APPLE_BACKLIGHT_CTL },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J213),
|
||||
.driver_data = APPLE_HAS_FN | APPLE_BACKLIGHT_CTL },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J214K),
|
||||
.driver_data = APPLE_HAS_FN },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J223),
|
||||
.driver_data = APPLE_HAS_FN },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J230K),
|
||||
.driver_data = APPLE_HAS_FN },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J152F),
|
||||
.driver_data = APPLE_HAS_FN },
|
||||
{ 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),
|
||||
@ -748,15 +999,15 @@ static const struct hid_device_id apple_devices[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY),
|
||||
.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_2021),
|
||||
.driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK },
|
||||
.driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK | APPLE_RDESC_BATTERY },
|
||||
{ HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_2021),
|
||||
.driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_FINGERPRINT_2021),
|
||||
.driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK },
|
||||
.driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK | APPLE_RDESC_BATTERY },
|
||||
{ HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_FINGERPRINT_2021),
|
||||
.driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_NUMPAD_2021),
|
||||
.driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK },
|
||||
.driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK | APPLE_RDESC_BATTERY },
|
||||
{ HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_NUMPAD_2021),
|
||||
.driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK },
|
||||
|
||||
|
@ -81,6 +81,7 @@ struct hid_report *hid_register_report(struct hid_device *device,
|
||||
report_enum->report_id_hash[id] = report;
|
||||
|
||||
list_add_tail(&report->list, &report_enum->report_list);
|
||||
INIT_LIST_HEAD(&report->field_entry_list);
|
||||
|
||||
return report;
|
||||
}
|
||||
@ -101,7 +102,7 @@ static struct hid_field *hid_register_field(struct hid_report *report, unsigned
|
||||
|
||||
field = kzalloc((sizeof(struct hid_field) +
|
||||
usages * sizeof(struct hid_usage) +
|
||||
usages * sizeof(unsigned)), GFP_KERNEL);
|
||||
3 * usages * sizeof(unsigned int)), GFP_KERNEL);
|
||||
if (!field)
|
||||
return NULL;
|
||||
|
||||
@ -109,6 +110,8 @@ static struct hid_field *hid_register_field(struct hid_report *report, unsigned
|
||||
report->field[field->index] = field;
|
||||
field->usage = (struct hid_usage *)(field + 1);
|
||||
field->value = (s32 *)(field->usage + usages);
|
||||
field->new_value = (s32 *)(field->value + usages);
|
||||
field->usages_priorities = (s32 *)(field->new_value + usages);
|
||||
field->report = report;
|
||||
|
||||
return field;
|
||||
@ -656,6 +659,8 @@ static void hid_free_report(struct hid_report *report)
|
||||
{
|
||||
unsigned n;
|
||||
|
||||
kfree(report->field_entries);
|
||||
|
||||
for (n = 0; n < report->maxfield; n++)
|
||||
kfree(report->field[n]);
|
||||
kfree(report);
|
||||
@ -1525,25 +1530,41 @@ static void hid_process_event(struct hid_device *hid, struct hid_field *field,
|
||||
}
|
||||
|
||||
/*
|
||||
* Analyse a received field, and fetch the data from it. The field
|
||||
* content is stored for next report processing (we do differential
|
||||
* reporting to the layer).
|
||||
* Checks if the given value is valid within this field
|
||||
*/
|
||||
static inline int hid_array_value_is_valid(struct hid_field *field,
|
||||
__s32 value)
|
||||
{
|
||||
__s32 min = field->logical_minimum;
|
||||
|
||||
static void hid_input_field(struct hid_device *hid, struct hid_field *field,
|
||||
__u8 *data, int interrupt)
|
||||
/*
|
||||
* Value needs to be between logical min and max, and
|
||||
* (value - min) is used as an index in the usage array.
|
||||
* This array is of size field->maxusage
|
||||
*/
|
||||
return value >= min &&
|
||||
value <= field->logical_maximum &&
|
||||
value - min < field->maxusage;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fetch the field from the data. The field content is stored for next
|
||||
* report processing (we do differential reporting to the layer).
|
||||
*/
|
||||
static void hid_input_fetch_field(struct hid_device *hid,
|
||||
struct hid_field *field,
|
||||
__u8 *data)
|
||||
{
|
||||
unsigned n;
|
||||
unsigned count = field->report_count;
|
||||
unsigned offset = field->report_offset;
|
||||
unsigned size = field->report_size;
|
||||
__s32 min = field->logical_minimum;
|
||||
__s32 max = field->logical_maximum;
|
||||
__s32 *value;
|
||||
|
||||
value = kmalloc_array(count, sizeof(__s32), GFP_ATOMIC);
|
||||
if (!value)
|
||||
return;
|
||||
value = field->new_value;
|
||||
memset(value, 0, count * sizeof(__s32));
|
||||
field->ignored = false;
|
||||
|
||||
for (n = 0; n < count; n++) {
|
||||
|
||||
@ -1554,35 +1575,228 @@ static void hid_input_field(struct hid_device *hid, struct hid_field *field,
|
||||
|
||||
/* Ignore report if ErrorRollOver */
|
||||
if (!(field->flags & HID_MAIN_ITEM_VARIABLE) &&
|
||||
value[n] >= min && value[n] <= max &&
|
||||
value[n] - min < field->maxusage &&
|
||||
field->usage[value[n] - min].hid == HID_UP_KEYBOARD + 1)
|
||||
goto exit;
|
||||
hid_array_value_is_valid(field, value[n]) &&
|
||||
field->usage[value[n] - min].hid == HID_UP_KEYBOARD + 1) {
|
||||
field->ignored = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Process a received variable field.
|
||||
*/
|
||||
|
||||
static void hid_input_var_field(struct hid_device *hid,
|
||||
struct hid_field *field,
|
||||
int interrupt)
|
||||
{
|
||||
unsigned int count = field->report_count;
|
||||
__s32 *value = field->new_value;
|
||||
unsigned int n;
|
||||
|
||||
for (n = 0; n < count; n++)
|
||||
hid_process_event(hid,
|
||||
field,
|
||||
&field->usage[n],
|
||||
value[n],
|
||||
interrupt);
|
||||
|
||||
memcpy(field->value, value, count * sizeof(__s32));
|
||||
}
|
||||
|
||||
/*
|
||||
* Process a received array field. The field content is stored for
|
||||
* next report processing (we do differential reporting to the layer).
|
||||
*/
|
||||
|
||||
static void hid_input_array_field(struct hid_device *hid,
|
||||
struct hid_field *field,
|
||||
int interrupt)
|
||||
{
|
||||
unsigned int n;
|
||||
unsigned int count = field->report_count;
|
||||
__s32 min = field->logical_minimum;
|
||||
__s32 *value;
|
||||
|
||||
value = field->new_value;
|
||||
|
||||
/* ErrorRollOver */
|
||||
if (field->ignored)
|
||||
return;
|
||||
|
||||
for (n = 0; n < count; n++) {
|
||||
if (hid_array_value_is_valid(field, field->value[n]) &&
|
||||
search(value, field->value[n], count))
|
||||
hid_process_event(hid,
|
||||
field,
|
||||
&field->usage[field->value[n] - min],
|
||||
0,
|
||||
interrupt);
|
||||
|
||||
if (HID_MAIN_ITEM_VARIABLE & field->flags) {
|
||||
hid_process_event(hid, field, &field->usage[n], value[n], interrupt);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (field->value[n] >= min && field->value[n] <= max
|
||||
&& field->value[n] - min < field->maxusage
|
||||
&& field->usage[field->value[n] - min].hid
|
||||
&& search(value, field->value[n], count))
|
||||
hid_process_event(hid, field, &field->usage[field->value[n] - min], 0, interrupt);
|
||||
|
||||
if (value[n] >= min && value[n] <= max
|
||||
&& value[n] - min < field->maxusage
|
||||
&& field->usage[value[n] - min].hid
|
||||
&& search(field->value, value[n], count))
|
||||
hid_process_event(hid, field, &field->usage[value[n] - min], 1, interrupt);
|
||||
if (hid_array_value_is_valid(field, value[n]) &&
|
||||
search(field->value, value[n], count))
|
||||
hid_process_event(hid,
|
||||
field,
|
||||
&field->usage[value[n] - min],
|
||||
1,
|
||||
interrupt);
|
||||
}
|
||||
|
||||
memcpy(field->value, value, count * sizeof(__s32));
|
||||
exit:
|
||||
kfree(value);
|
||||
}
|
||||
|
||||
/*
|
||||
* Analyse a received report, and fetch the data from it. The field
|
||||
* content is stored for next report processing (we do differential
|
||||
* reporting to the layer).
|
||||
*/
|
||||
static void hid_process_report(struct hid_device *hid,
|
||||
struct hid_report *report,
|
||||
__u8 *data,
|
||||
int interrupt)
|
||||
{
|
||||
unsigned int a;
|
||||
struct hid_field_entry *entry;
|
||||
struct hid_field *field;
|
||||
|
||||
/* first retrieve all incoming values in data */
|
||||
for (a = 0; a < report->maxfield; a++)
|
||||
hid_input_fetch_field(hid, field = report->field[a], data);
|
||||
|
||||
if (!list_empty(&report->field_entry_list)) {
|
||||
/* INPUT_REPORT, we have a priority list of fields */
|
||||
list_for_each_entry(entry,
|
||||
&report->field_entry_list,
|
||||
list) {
|
||||
field = entry->field;
|
||||
|
||||
if (field->flags & HID_MAIN_ITEM_VARIABLE)
|
||||
hid_process_event(hid,
|
||||
field,
|
||||
&field->usage[entry->index],
|
||||
field->new_value[entry->index],
|
||||
interrupt);
|
||||
else
|
||||
hid_input_array_field(hid, field, interrupt);
|
||||
}
|
||||
|
||||
/* we need to do the memcpy at the end for var items */
|
||||
for (a = 0; a < report->maxfield; a++) {
|
||||
field = report->field[a];
|
||||
|
||||
if (field->flags & HID_MAIN_ITEM_VARIABLE)
|
||||
memcpy(field->value, field->new_value,
|
||||
field->report_count * sizeof(__s32));
|
||||
}
|
||||
} else {
|
||||
/* FEATURE_REPORT, regular processing */
|
||||
for (a = 0; a < report->maxfield; a++) {
|
||||
field = report->field[a];
|
||||
|
||||
if (field->flags & HID_MAIN_ITEM_VARIABLE)
|
||||
hid_input_var_field(hid, field, interrupt);
|
||||
else
|
||||
hid_input_array_field(hid, field, interrupt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Insert a given usage_index in a field in the list
|
||||
* of processed usages in the report.
|
||||
*
|
||||
* The elements of lower priority score are processed
|
||||
* first.
|
||||
*/
|
||||
static void __hid_insert_field_entry(struct hid_device *hid,
|
||||
struct hid_report *report,
|
||||
struct hid_field_entry *entry,
|
||||
struct hid_field *field,
|
||||
unsigned int usage_index)
|
||||
{
|
||||
struct hid_field_entry *next;
|
||||
|
||||
entry->field = field;
|
||||
entry->index = usage_index;
|
||||
entry->priority = field->usages_priorities[usage_index];
|
||||
|
||||
/* insert the element at the correct position */
|
||||
list_for_each_entry(next,
|
||||
&report->field_entry_list,
|
||||
list) {
|
||||
/*
|
||||
* the priority of our element is strictly higher
|
||||
* than the next one, insert it before
|
||||
*/
|
||||
if (entry->priority > next->priority) {
|
||||
list_add_tail(&entry->list, &next->list);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* lowest priority score: insert at the end */
|
||||
list_add_tail(&entry->list, &report->field_entry_list);
|
||||
}
|
||||
|
||||
static void hid_report_process_ordering(struct hid_device *hid,
|
||||
struct hid_report *report)
|
||||
{
|
||||
struct hid_field *field;
|
||||
struct hid_field_entry *entries;
|
||||
unsigned int a, u, usages;
|
||||
unsigned int count = 0;
|
||||
|
||||
/* count the number of individual fields in the report */
|
||||
for (a = 0; a < report->maxfield; a++) {
|
||||
field = report->field[a];
|
||||
|
||||
if (field->flags & HID_MAIN_ITEM_VARIABLE)
|
||||
count += field->report_count;
|
||||
else
|
||||
count++;
|
||||
}
|
||||
|
||||
/* allocate the memory to process the fields */
|
||||
entries = kcalloc(count, sizeof(*entries), GFP_KERNEL);
|
||||
if (!entries)
|
||||
return;
|
||||
|
||||
report->field_entries = entries;
|
||||
|
||||
/*
|
||||
* walk through all fields in the report and
|
||||
* store them by priority order in report->field_entry_list
|
||||
*
|
||||
* - Var elements are individualized (field + usage_index)
|
||||
* - Arrays are taken as one, we can not chose an order for them
|
||||
*/
|
||||
usages = 0;
|
||||
for (a = 0; a < report->maxfield; a++) {
|
||||
field = report->field[a];
|
||||
|
||||
if (field->flags & HID_MAIN_ITEM_VARIABLE) {
|
||||
for (u = 0; u < field->report_count; u++) {
|
||||
__hid_insert_field_entry(hid, report,
|
||||
&entries[usages],
|
||||
field, u);
|
||||
usages++;
|
||||
}
|
||||
} else {
|
||||
__hid_insert_field_entry(hid, report, &entries[usages],
|
||||
field, 0);
|
||||
usages++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void hid_process_ordering(struct hid_device *hid)
|
||||
{
|
||||
struct hid_report *report;
|
||||
struct hid_report_enum *report_enum = &hid->report_enum[HID_INPUT_REPORT];
|
||||
|
||||
list_for_each_entry(report, &report_enum->report_list, list)
|
||||
hid_report_process_ordering(hid, report);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1746,7 +1960,6 @@ int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, u32 size,
|
||||
struct hid_report_enum *report_enum = hid->report_enum + type;
|
||||
struct hid_report *report;
|
||||
struct hid_driver *hdrv;
|
||||
unsigned int a;
|
||||
u32 rsize, csize = size;
|
||||
u8 *cdata = data;
|
||||
int ret = 0;
|
||||
@ -1782,8 +1995,7 @@ int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, u32 size,
|
||||
}
|
||||
|
||||
if (hid->claimed != HID_CLAIMED_HIDRAW && report->maxfield) {
|
||||
for (a = 0; a < report->maxfield; a++)
|
||||
hid_input_field(hid, report->field[a], cdata, interrupt);
|
||||
hid_process_report(hid, report, cdata, interrupt);
|
||||
hdrv = hid->driver;
|
||||
if (hdrv && hdrv->report)
|
||||
hdrv->report(hid, report);
|
||||
@ -1970,6 +2182,8 @@ int hid_connect(struct hid_device *hdev, unsigned int connect_mask)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
hid_process_ordering(hdev);
|
||||
|
||||
if ((hdev->claimed & HID_CLAIMED_INPUT) &&
|
||||
(connect_mask & HID_CONNECT_FF) && hdev->ff_init)
|
||||
hdev->ff_init(hdev);
|
||||
|
@ -58,7 +58,7 @@ static int cbas_ec_query_base(struct cros_ec_device *ec_dev, bool get_state,
|
||||
struct cros_ec_command *msg;
|
||||
int ret;
|
||||
|
||||
msg = kzalloc(sizeof(*msg) + max(sizeof(u32), sizeof(*params)),
|
||||
msg = kzalloc(struct_size(msg, data, max(sizeof(u32), sizeof(*params))),
|
||||
GFP_KERNEL);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
@ -167,6 +167,14 @@
|
||||
#define USB_DEVICE_ID_APPLE_WELLSPRING9_ANSI 0x0272
|
||||
#define USB_DEVICE_ID_APPLE_WELLSPRING9_ISO 0x0273
|
||||
#define USB_DEVICE_ID_APPLE_WELLSPRING9_JIS 0x0274
|
||||
#define USB_DEVICE_ID_APPLE_WELLSPRINGT2_J140K 0x027a
|
||||
#define USB_DEVICE_ID_APPLE_WELLSPRINGT2_J132 0x027b
|
||||
#define USB_DEVICE_ID_APPLE_WELLSPRINGT2_J680 0x027c
|
||||
#define USB_DEVICE_ID_APPLE_WELLSPRINGT2_J213 0x027d
|
||||
#define USB_DEVICE_ID_APPLE_WELLSPRINGT2_J214K 0x027e
|
||||
#define USB_DEVICE_ID_APPLE_WELLSPRINGT2_J223 0x027f
|
||||
#define USB_DEVICE_ID_APPLE_WELLSPRINGT2_J230K 0x0280
|
||||
#define USB_DEVICE_ID_APPLE_WELLSPRINGT2_J152F 0x0340
|
||||
#define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY 0x030a
|
||||
#define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY 0x030b
|
||||
#define USB_DEVICE_ID_APPLE_IRCONTROL 0x8240
|
||||
@ -606,7 +614,7 @@
|
||||
|
||||
#define USB_VENDOR_ID_HUION 0x256c
|
||||
#define USB_DEVICE_ID_HUION_TABLET 0x006e
|
||||
#define USB_DEVICE_ID_HUION_HS64 0x006d
|
||||
#define USB_DEVICE_ID_HUION_TABLET2 0x006d
|
||||
|
||||
#define USB_VENDOR_ID_IBM 0x04b3
|
||||
#define USB_DEVICE_ID_IBM_SCROLLPOINT_III 0x3100
|
||||
@ -1030,6 +1038,9 @@
|
||||
#define I2C_PRODUCT_ID_RAYDIUM_3118 0x3118
|
||||
|
||||
#define USB_VENDOR_ID_RAZER 0x1532
|
||||
#define USB_DEVICE_ID_RAZER_BLACKWIDOW_ULTIMATE 0x010D
|
||||
#define USB_DEVICE_ID_RAZER_BLACKWIDOW 0x010e
|
||||
#define USB_DEVICE_ID_RAZER_BLACKWIDOW_CLASSIC 0x011b
|
||||
#define USB_DEVICE_ID_RAZER_BLADE_14 0x011D
|
||||
|
||||
#define USB_VENDOR_ID_REALTEK 0x0bda
|
||||
@ -1092,6 +1103,7 @@
|
||||
|
||||
#define USB_VENDOR_ID_SIGMA_MICRO 0x1c4f
|
||||
#define USB_DEVICE_ID_SIGMA_MICRO_KEYBOARD 0x0002
|
||||
#define USB_DEVICE_ID_SIGMA_MICRO_KEYBOARD2 0x0059
|
||||
|
||||
#define USB_VENDOR_ID_SIGMATEL 0x066F
|
||||
#define USB_DEVICE_ID_SIGMATEL_STMP3780 0x3780
|
||||
|
@ -48,6 +48,51 @@ static const struct {
|
||||
__s32 y;
|
||||
} hid_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
|
||||
|
||||
struct usage_priority {
|
||||
__u32 usage; /* the HID usage associated */
|
||||
bool global; /* we assume all usages to be slotted,
|
||||
* unless global
|
||||
*/
|
||||
unsigned int slot_overwrite; /* for globals: allows to set the usage
|
||||
* before or after the slots
|
||||
*/
|
||||
};
|
||||
|
||||
/*
|
||||
* hid-input will convert this list into priorities:
|
||||
* the first element will have the highest priority
|
||||
* (the length of the following array) and the last
|
||||
* element the lowest (1).
|
||||
*
|
||||
* hid-input will then shift the priority by 8 bits to leave some space
|
||||
* in case drivers want to interleave other fields.
|
||||
*
|
||||
* To accommodate slotted devices, the slot priority is
|
||||
* defined in the next 8 bits (defined by 0xff - slot).
|
||||
*
|
||||
* If drivers want to add fields before those, hid-input will
|
||||
* leave out the first 8 bits of the priority value.
|
||||
*
|
||||
* This still leaves us 65535 individual priority values.
|
||||
*/
|
||||
static const struct usage_priority hidinput_usages_priorities[] = {
|
||||
{ /* Eraser (eraser touching) must always come before tipswitch */
|
||||
.usage = HID_DG_ERASER,
|
||||
},
|
||||
{ /* Invert must always come before In Range */
|
||||
.usage = HID_DG_INVERT,
|
||||
},
|
||||
{ /* Is the tip of the tool touching? */
|
||||
.usage = HID_DG_TIPSWITCH,
|
||||
},
|
||||
{ /* Tip Pressure might emulate tip switch */
|
||||
.usage = HID_DG_TIPPRESSURE,
|
||||
},
|
||||
{ /* In Range needs to come after the other tool states */
|
||||
.usage = HID_DG_INRANGE,
|
||||
},
|
||||
};
|
||||
|
||||
#define map_abs(c) hid_map_usage(hidinput, usage, &bit, &max, EV_ABS, (c))
|
||||
#define map_rel(c) hid_map_usage(hidinput, usage, &bit, &max, EV_REL, (c))
|
||||
#define map_key(c) hid_map_usage(hidinput, usage, &bit, &max, EV_KEY, (c))
|
||||
@ -586,11 +631,13 @@ static bool hidinput_field_in_collection(struct hid_device *device, struct hid_f
|
||||
}
|
||||
|
||||
static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_field *field,
|
||||
struct hid_usage *usage)
|
||||
struct hid_usage *usage, unsigned int usage_index)
|
||||
{
|
||||
struct input_dev *input = hidinput->input;
|
||||
struct hid_device *device = input_get_drvdata(input);
|
||||
const struct usage_priority *usage_priority = NULL;
|
||||
int max = 0, code;
|
||||
unsigned int i = 0;
|
||||
unsigned long *bit = NULL;
|
||||
|
||||
field->hidinput = hidinput;
|
||||
@ -608,6 +655,28 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
|
||||
goto ignore;
|
||||
}
|
||||
|
||||
/* assign a priority based on the static list declared here */
|
||||
for (i = 0; i < ARRAY_SIZE(hidinput_usages_priorities); i++) {
|
||||
if (usage->hid == hidinput_usages_priorities[i].usage) {
|
||||
usage_priority = &hidinput_usages_priorities[i];
|
||||
|
||||
field->usages_priorities[usage_index] =
|
||||
(ARRAY_SIZE(hidinput_usages_priorities) - i) << 8;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* For slotted devices, we need to also add the slot index
|
||||
* in the priority.
|
||||
*/
|
||||
if (usage_priority && usage_priority->global)
|
||||
field->usages_priorities[usage_index] |=
|
||||
usage_priority->slot_overwrite;
|
||||
else
|
||||
field->usages_priorities[usage_index] |=
|
||||
(0xff - field->slot_idx) << 16;
|
||||
|
||||
if (device->driver->input_mapping) {
|
||||
int ret = device->driver->input_mapping(device, hidinput, field,
|
||||
usage, &bit, &max);
|
||||
@ -828,10 +897,31 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
|
||||
break;
|
||||
|
||||
case 0x32: /* InRange */
|
||||
switch (field->physical & 0xff) {
|
||||
case 0x21: map_key(BTN_TOOL_MOUSE); break;
|
||||
case 0x22: map_key(BTN_TOOL_FINGER); break;
|
||||
default: map_key(BTN_TOOL_PEN); break;
|
||||
switch (field->physical) {
|
||||
case HID_DG_PUCK:
|
||||
map_key(BTN_TOOL_MOUSE);
|
||||
break;
|
||||
case HID_DG_FINGER:
|
||||
map_key(BTN_TOOL_FINGER);
|
||||
break;
|
||||
default:
|
||||
/*
|
||||
* If the physical is not given,
|
||||
* rely on the application.
|
||||
*/
|
||||
if (!field->physical) {
|
||||
switch (field->application) {
|
||||
case HID_DG_TOUCHSCREEN:
|
||||
case HID_DG_TOUCHPAD:
|
||||
map_key_clear(BTN_TOOL_FINGER);
|
||||
break;
|
||||
default:
|
||||
map_key_clear(BTN_TOOL_PEN);
|
||||
}
|
||||
} else {
|
||||
map_key(BTN_TOOL_PEN);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
@ -1318,9 +1408,38 @@ static void hidinput_handle_scroll(struct hid_usage *usage,
|
||||
input_event(input, EV_REL, usage->code, hi_res);
|
||||
}
|
||||
|
||||
static void hid_report_release_tool(struct hid_report *report, struct input_dev *input,
|
||||
unsigned int tool)
|
||||
{
|
||||
/* if the given tool is not currently reported, ignore */
|
||||
if (!test_bit(tool, input->key))
|
||||
return;
|
||||
|
||||
/*
|
||||
* if the given tool was previously set, release it,
|
||||
* release any TOUCH and send an EV_SYN
|
||||
*/
|
||||
input_event(input, EV_KEY, BTN_TOUCH, 0);
|
||||
input_event(input, EV_KEY, tool, 0);
|
||||
input_event(input, EV_SYN, SYN_REPORT, 0);
|
||||
|
||||
report->tool = 0;
|
||||
}
|
||||
|
||||
static void hid_report_set_tool(struct hid_report *report, struct input_dev *input,
|
||||
unsigned int new_tool)
|
||||
{
|
||||
if (report->tool != new_tool)
|
||||
hid_report_release_tool(report, input, report->tool);
|
||||
|
||||
input_event(input, EV_KEY, new_tool, 1);
|
||||
report->tool = new_tool;
|
||||
}
|
||||
|
||||
void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value)
|
||||
{
|
||||
struct input_dev *input;
|
||||
struct hid_report *report = field->report;
|
||||
unsigned *quirks = &hid->quirks;
|
||||
|
||||
if (!usage->type)
|
||||
@ -1336,12 +1455,6 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct
|
||||
|
||||
input = field->hidinput->input;
|
||||
|
||||
if (usage->type == EV_ABS &&
|
||||
(((*quirks & HID_QUIRK_X_INVERT) && usage->code == ABS_X) ||
|
||||
((*quirks & HID_QUIRK_Y_INVERT) && usage->code == ABS_Y))) {
|
||||
value = field->logical_maximum - value;
|
||||
}
|
||||
|
||||
if (usage->hat_min < usage->hat_max || usage->hat_dir) {
|
||||
int hat_dir = usage->hat_dir;
|
||||
if (!hat_dir)
|
||||
@ -1352,61 +1465,6 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct
|
||||
return;
|
||||
}
|
||||
|
||||
if (usage->hid == HID_DG_INVERT) {
|
||||
*quirks = value ? (*quirks | HID_QUIRK_INVERT) : (*quirks & ~HID_QUIRK_INVERT);
|
||||
return;
|
||||
}
|
||||
|
||||
if (usage->hid == HID_DG_INRANGE) {
|
||||
if (value) {
|
||||
input_event(input, usage->type, (*quirks & HID_QUIRK_INVERT) ? BTN_TOOL_RUBBER : usage->code, 1);
|
||||
return;
|
||||
}
|
||||
input_event(input, usage->type, usage->code, 0);
|
||||
input_event(input, usage->type, BTN_TOOL_RUBBER, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (usage->hid == HID_DG_TIPPRESSURE && (*quirks & HID_QUIRK_NOTOUCH)) {
|
||||
int a = field->logical_minimum;
|
||||
int b = field->logical_maximum;
|
||||
input_event(input, EV_KEY, BTN_TOUCH, value > a + ((b - a) >> 3));
|
||||
}
|
||||
|
||||
if (usage->hid == (HID_UP_PID | 0x83UL)) { /* Simultaneous Effects Max */
|
||||
dbg_hid("Maximum Effects - %d\n",value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (usage->hid == (HID_UP_PID | 0x7fUL)) {
|
||||
dbg_hid("PID Pool Report\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((usage->type == EV_KEY) && (usage->code == 0)) /* Key 0 is "unassigned", not KEY_UNKNOWN */
|
||||
return;
|
||||
|
||||
if ((usage->type == EV_REL) && (usage->code == REL_WHEEL_HI_RES ||
|
||||
usage->code == REL_HWHEEL_HI_RES)) {
|
||||
hidinput_handle_scroll(usage, input, value);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((usage->type == EV_ABS) && (field->flags & HID_MAIN_ITEM_RELATIVE) &&
|
||||
(usage->code == ABS_VOLUME)) {
|
||||
int count = abs(value);
|
||||
int direction = value > 0 ? KEY_VOLUMEUP : KEY_VOLUMEDOWN;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
input_event(input, EV_KEY, direction, 1);
|
||||
input_sync(input);
|
||||
input_event(input, EV_KEY, direction, 0);
|
||||
input_sync(input);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ignore out-of-range values as per HID specification,
|
||||
* section 5.10 and 6.2.25, when NULL state bit is present.
|
||||
@ -1419,7 +1477,7 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct
|
||||
* don't specify logical min and max.
|
||||
*/
|
||||
if ((field->flags & HID_MAIN_ITEM_VARIABLE) &&
|
||||
(field->logical_minimum < field->logical_maximum)) {
|
||||
field->logical_minimum < field->logical_maximum) {
|
||||
if (field->flags & HID_MAIN_ITEM_NULL_STATE &&
|
||||
(value < field->logical_minimum ||
|
||||
value > field->logical_maximum)) {
|
||||
@ -1431,6 +1489,123 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct
|
||||
field->logical_maximum);
|
||||
}
|
||||
|
||||
switch (usage->hid) {
|
||||
case HID_DG_ERASER:
|
||||
report->tool_active |= !!value;
|
||||
|
||||
/*
|
||||
* if eraser is set, we must enforce BTN_TOOL_RUBBER
|
||||
* to accommodate for devices not following the spec.
|
||||
*/
|
||||
if (value)
|
||||
hid_report_set_tool(report, input, BTN_TOOL_RUBBER);
|
||||
else if (report->tool != BTN_TOOL_RUBBER)
|
||||
/* value is off, tool is not rubber, ignore */
|
||||
return;
|
||||
|
||||
/* let hid-input set BTN_TOUCH */
|
||||
break;
|
||||
|
||||
case HID_DG_INVERT:
|
||||
report->tool_active |= !!value;
|
||||
|
||||
/*
|
||||
* If invert is set, we store BTN_TOOL_RUBBER.
|
||||
*/
|
||||
if (value)
|
||||
hid_report_set_tool(report, input, BTN_TOOL_RUBBER);
|
||||
else if (!report->tool_active)
|
||||
/* tool_active not set means Invert and Eraser are not set */
|
||||
hid_report_release_tool(report, input, BTN_TOOL_RUBBER);
|
||||
|
||||
/* no further processing */
|
||||
return;
|
||||
|
||||
case HID_DG_INRANGE:
|
||||
report->tool_active |= !!value;
|
||||
|
||||
if (report->tool_active) {
|
||||
/*
|
||||
* if tool is not set but is marked as active,
|
||||
* assume ours
|
||||
*/
|
||||
if (!report->tool)
|
||||
hid_report_set_tool(report, input, usage->code);
|
||||
} else {
|
||||
hid_report_release_tool(report, input, usage->code);
|
||||
}
|
||||
|
||||
/* reset tool_active for the next event */
|
||||
report->tool_active = false;
|
||||
|
||||
/* no further processing */
|
||||
return;
|
||||
|
||||
case HID_DG_TIPSWITCH:
|
||||
report->tool_active |= !!value;
|
||||
|
||||
/* if tool is set to RUBBER we should ignore the current value */
|
||||
if (report->tool == BTN_TOOL_RUBBER)
|
||||
return;
|
||||
|
||||
break;
|
||||
|
||||
case HID_DG_TIPPRESSURE:
|
||||
if (*quirks & HID_QUIRK_NOTOUCH) {
|
||||
int a = field->logical_minimum;
|
||||
int b = field->logical_maximum;
|
||||
|
||||
if (value > a + ((b - a) >> 3)) {
|
||||
input_event(input, EV_KEY, BTN_TOUCH, 1);
|
||||
report->tool_active = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case HID_UP_PID | 0x83UL: /* Simultaneous Effects Max */
|
||||
dbg_hid("Maximum Effects - %d\n",value);
|
||||
return;
|
||||
|
||||
case HID_UP_PID | 0x7fUL:
|
||||
dbg_hid("PID Pool Report\n");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (usage->type) {
|
||||
case EV_KEY:
|
||||
if (usage->code == 0) /* Key 0 is "unassigned", not KEY_UNKNOWN */
|
||||
return;
|
||||
break;
|
||||
|
||||
case EV_REL:
|
||||
if (usage->code == REL_WHEEL_HI_RES ||
|
||||
usage->code == REL_HWHEEL_HI_RES) {
|
||||
hidinput_handle_scroll(usage, input, value);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case EV_ABS:
|
||||
if ((field->flags & HID_MAIN_ITEM_RELATIVE) &&
|
||||
usage->code == ABS_VOLUME) {
|
||||
int count = abs(value);
|
||||
int direction = value > 0 ? KEY_VOLUMEUP : KEY_VOLUMEDOWN;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
input_event(input, EV_KEY, direction, 1);
|
||||
input_sync(input);
|
||||
input_event(input, EV_KEY, direction, 0);
|
||||
input_sync(input);
|
||||
}
|
||||
return;
|
||||
|
||||
} else if (((*quirks & HID_QUIRK_X_INVERT) && usage->code == ABS_X) ||
|
||||
((*quirks & HID_QUIRK_Y_INVERT) && usage->code == ABS_Y))
|
||||
value = field->logical_maximum - value;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ignore reports for absolute data if the data didn't change. This is
|
||||
* not only an optimization but also fixes 'dead' key reports. Some
|
||||
@ -1933,12 +2108,63 @@ static struct hid_input *hidinput_match_application(struct hid_report *report)
|
||||
static inline void hidinput_configure_usages(struct hid_input *hidinput,
|
||||
struct hid_report *report)
|
||||
{
|
||||
int i, j;
|
||||
int i, j, k;
|
||||
int first_field_index = 0;
|
||||
int slot_collection_index = -1;
|
||||
int prev_collection_index = -1;
|
||||
unsigned int slot_idx = 0;
|
||||
struct hid_field *field;
|
||||
|
||||
/*
|
||||
* First tag all the fields that are part of a slot,
|
||||
* a slot needs to have one Contact ID in the collection
|
||||
*/
|
||||
for (i = 0; i < report->maxfield; i++) {
|
||||
field = report->field[i];
|
||||
|
||||
/* ignore fields without usage */
|
||||
if (field->maxusage < 1)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* janitoring when collection_index changes
|
||||
*/
|
||||
if (prev_collection_index != field->usage->collection_index) {
|
||||
prev_collection_index = field->usage->collection_index;
|
||||
first_field_index = i;
|
||||
}
|
||||
|
||||
/*
|
||||
* if we already found a Contact ID in the collection,
|
||||
* tag and continue to the next.
|
||||
*/
|
||||
if (slot_collection_index == field->usage->collection_index) {
|
||||
field->slot_idx = slot_idx;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* check if the current field has Contact ID */
|
||||
for (j = 0; j < field->maxusage; j++) {
|
||||
if (field->usage[j].hid == HID_DG_CONTACTID) {
|
||||
slot_collection_index = field->usage->collection_index;
|
||||
slot_idx++;
|
||||
|
||||
/*
|
||||
* mark all previous fields and this one in the
|
||||
* current collection to be slotted.
|
||||
*/
|
||||
for (k = first_field_index; k <= i; k++)
|
||||
report->field[k]->slot_idx = slot_idx;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < report->maxfield; i++)
|
||||
for (j = 0; j < report->field[i]->maxusage; j++)
|
||||
hidinput_configure_usage(hidinput, report->field[i],
|
||||
report->field[i]->usage + j);
|
||||
report->field[i]->usage + j,
|
||||
j);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -295,6 +295,14 @@ static const struct hid_device_id hid_have_special_driver[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING9_ANSI) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING9_ISO) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING9_JIS) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J140K) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J132) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J680) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J213) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J214K) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J223) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J230K) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J152F) },
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI) },
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO) },
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS) },
|
||||
@ -930,6 +938,14 @@ static const struct hid_device_id hid_mouse_ignore_list[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING9_ANSI) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING9_ISO) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING9_JIS) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J140K) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J132) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J680) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J213) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J214K) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J223) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J230K) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J152F) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) },
|
||||
{ }
|
||||
|
125
drivers/hid/hid-razer.c
Normal file
125
drivers/hid/hid-razer.c
Normal file
@ -0,0 +1,125 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* HID driver for gaming keys on Razer Blackwidow gaming keyboards
|
||||
* Macro Key Keycodes: M1 = 191, M2 = 192, M3 = 193, M4 = 194, M5 = 195
|
||||
*
|
||||
* Copyright (c) 2021 Jelle van der Waa <jvanderwaa@redhat.com>
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/hid.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/wait.h>
|
||||
|
||||
#include "hid-ids.h"
|
||||
|
||||
#define map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, EV_KEY, (c))
|
||||
|
||||
#define RAZER_BLACKWIDOW_TRANSFER_BUF_SIZE 91
|
||||
|
||||
static bool macro_key_remapping = 1;
|
||||
module_param(macro_key_remapping, bool, 0644);
|
||||
MODULE_PARM_DESC(macro_key_remapping, " on (Y) off (N)");
|
||||
|
||||
|
||||
static unsigned char blackwidow_init[RAZER_BLACKWIDOW_TRANSFER_BUF_SIZE] = {
|
||||
0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x04,
|
||||
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x04, 0x00
|
||||
};
|
||||
|
||||
static int razer_input_mapping(struct hid_device *hdev,
|
||||
struct hid_input *hi, struct hid_field *field,
|
||||
struct hid_usage *usage, unsigned long **bit, int *max)
|
||||
{
|
||||
|
||||
if (!macro_key_remapping)
|
||||
return 0;
|
||||
|
||||
if ((usage->hid & HID_UP_KEYBOARD) != HID_UP_KEYBOARD)
|
||||
return 0;
|
||||
|
||||
switch (usage->hid & ~HID_UP_KEYBOARD) {
|
||||
case 0x68:
|
||||
map_key_clear(KEY_MACRO1);
|
||||
return 1;
|
||||
case 0x69:
|
||||
map_key_clear(KEY_MACRO2);
|
||||
return 1;
|
||||
case 0x6a:
|
||||
map_key_clear(KEY_MACRO3);
|
||||
return 1;
|
||||
case 0x6b:
|
||||
map_key_clear(KEY_MACRO4);
|
||||
return 1;
|
||||
case 0x6c:
|
||||
map_key_clear(KEY_MACRO5);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int razer_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
{
|
||||
char *buf;
|
||||
int ret = 0;
|
||||
|
||||
ret = hid_parse(hdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Only send the enable macro keys command for the third device
|
||||
* identified as mouse input.
|
||||
*/
|
||||
if (hdev->type == HID_TYPE_USBMOUSE) {
|
||||
buf = kmemdup(blackwidow_init, RAZER_BLACKWIDOW_TRANSFER_BUF_SIZE, GFP_KERNEL);
|
||||
if (buf == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = hid_hw_raw_request(hdev, 0, buf, RAZER_BLACKWIDOW_TRANSFER_BUF_SIZE,
|
||||
HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
|
||||
if (ret != RAZER_BLACKWIDOW_TRANSFER_BUF_SIZE)
|
||||
hid_err(hdev, "failed to enable macro keys: %d\n", ret);
|
||||
|
||||
kfree(buf);
|
||||
}
|
||||
|
||||
return hid_hw_start(hdev, HID_CONNECT_DEFAULT);
|
||||
}
|
||||
|
||||
static const struct hid_device_id razer_devices[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_RAZER,
|
||||
USB_DEVICE_ID_RAZER_BLACKWIDOW) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_RAZER,
|
||||
USB_DEVICE_ID_RAZER_BLACKWIDOW_CLASSIC) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_RAZER,
|
||||
USB_DEVICE_ID_RAZER_BLACKWIDOW_ULTIMATE) },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(hid, razer_devices);
|
||||
|
||||
static struct hid_driver razer_driver = {
|
||||
.name = "razer",
|
||||
.id_table = razer_devices,
|
||||
.input_mapping = razer_input_mapping,
|
||||
.probe = razer_probe,
|
||||
};
|
||||
module_hid_driver(razer_driver);
|
||||
|
||||
MODULE_AUTHOR("Jelle van der Waa <jvanderwaa@redhat.com>");
|
||||
MODULE_LICENSE("GPL");
|
130
drivers/hid/hid-sigmamicro.c
Normal file
130
drivers/hid/hid-sigmamicro.c
Normal file
@ -0,0 +1,130 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* HID driver for SiGma Micro-based keyboards
|
||||
*
|
||||
* Copyright (c) 2016 Kinglong Mee
|
||||
* Copyright (c) 2021 Desmond Lim
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/hid.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include "hid-ids.h"
|
||||
|
||||
static const __u8 sm_0059_rdesc[] = {
|
||||
0x05, 0x0c, /* Usage Page (Consumer Devices) 0 */
|
||||
0x09, 0x01, /* Usage (Consumer Control) 2 */
|
||||
0xa1, 0x01, /* Collection (Application) 4 */
|
||||
0x85, 0x01, /* Report ID (1) 6 */
|
||||
0x19, 0x00, /* Usage Minimum (0) 8 */
|
||||
0x2a, 0x3c, 0x02, /* Usage Maximum (572) 10 */
|
||||
0x15, 0x00, /* Logical Minimum (0) 13 */
|
||||
0x26, 0x3c, 0x02, /* Logical Maximum (572) 15 */
|
||||
0x95, 0x01, /* Report Count (1) 18 */
|
||||
0x75, 0x10, /* Report Size (16) 20 */
|
||||
0x81, 0x00, /* Input (Data,Arr,Abs) 22 */
|
||||
0xc0, /* End Collection 24 */
|
||||
0x05, 0x01, /* Usage Page (Generic Desktop) 25 */
|
||||
0x09, 0x80, /* Usage (System Control) 27 */
|
||||
0xa1, 0x01, /* Collection (Application) 29 */
|
||||
0x85, 0x02, /* Report ID (2) 31 */
|
||||
0x19, 0x81, /* Usage Minimum (129) 33 */
|
||||
0x29, 0x83, /* Usage Maximum (131) 35 */
|
||||
0x25, 0x01, /* Logical Maximum (1) 37 */
|
||||
0x75, 0x01, /* Report Size (1) 39 */
|
||||
0x95, 0x03, /* Report Count (3) 41 */
|
||||
0x81, 0x02, /* Input (Data,Var,Abs) 43 */
|
||||
0x95, 0x05, /* Report Count (5) 45 */
|
||||
0x81, 0x01, /* Input (Cnst,Arr,Abs) 47 */
|
||||
0xc0, /* End Collection 49 */
|
||||
0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) 50 */
|
||||
0x09, 0x01, /* Usage (Vendor Usage 1) 53 */
|
||||
0xa1, 0x01, /* Collection (Application) 55 */
|
||||
0x85, 0x03, /* Report ID (3) 57 */
|
||||
0x1a, 0xf1, 0x00, /* Usage Minimum (241) 59 */
|
||||
0x2a, 0xf8, 0x00, /* Usage Maximum (248) 62 */
|
||||
0x15, 0x00, /* Logical Minimum (0) 65 */
|
||||
0x25, 0x01, /* Logical Maximum (1) 67 */
|
||||
0x75, 0x01, /* Report Size (1) 69 */
|
||||
0x95, 0x08, /* Report Count (8) 71 */
|
||||
0x81, 0x02, /* Input (Data,Var,Abs) 73 */
|
||||
0xc0, /* End Collection 75 */
|
||||
0x05, 0x01, /* Usage Page (Generic Desktop) 76 */
|
||||
0x09, 0x06, /* Usage (Keyboard) 78 */
|
||||
0xa1, 0x01, /* Collection (Application) 80 */
|
||||
0x85, 0x04, /* Report ID (4) 82 */
|
||||
0x05, 0x07, /* Usage Page (Keyboard) 84 */
|
||||
0x19, 0xe0, /* Usage Minimum (224) 86 */
|
||||
0x29, 0xe7, /* Usage Maximum (231) 88 */
|
||||
0x15, 0x00, /* Logical Minimum (0) 90 */
|
||||
0x25, 0x01, /* Logical Maximum (1) 92 */
|
||||
0x75, 0x01, /* Report Size (1) 94 */
|
||||
0x95, 0x08, /* Report Count (8) 96 */
|
||||
0x81, 0x00, /* Input (Data,Arr,Abs) 98 */
|
||||
0x95, 0x30, /* Report Count (48) 100 */
|
||||
0x75, 0x01, /* Report Size (1) 102 */
|
||||
0x15, 0x00, /* Logical Minimum (0) 104 */
|
||||
0x25, 0x01, /* Logical Maximum (1) 106 */
|
||||
0x05, 0x07, /* Usage Page (Keyboard) 108 */
|
||||
0x19, 0x00, /* Usage Minimum (0) 110 */
|
||||
0x29, 0x2f, /* Usage Maximum (47) 112 */
|
||||
0x81, 0x02, /* Input (Data,Var,Abs) 114 */
|
||||
0xc0, /* End Collection 116 */
|
||||
0x05, 0x01, /* Usage Page (Generic Desktop) 117 */
|
||||
0x09, 0x06, /* Usage (Keyboard) 119 */
|
||||
0xa1, 0x01, /* Collection (Application) 121 */
|
||||
0x85, 0x05, /* Report ID (5) 123 */
|
||||
0x95, 0x38, /* Report Count (56) 125 */
|
||||
0x75, 0x01, /* Report Size (1) 127 */
|
||||
0x15, 0x00, /* Logical Minimum (0) 129 */
|
||||
0x25, 0x01, /* Logical Maximum (1) 131 */
|
||||
0x05, 0x07, /* Usage Page (Keyboard) 133 */
|
||||
0x19, 0x30, /* Usage Minimum (48) 135 */
|
||||
0x29, 0x67, /* Usage Maximum (103) 137 */
|
||||
0x81, 0x02, /* Input (Data,Var,Abs) 139 */
|
||||
0xc0, /* End Collection 141 */
|
||||
0x05, 0x01, /* Usage Page (Generic Desktop) 142 */
|
||||
0x09, 0x06, /* Usage (Keyboard) 144 */
|
||||
0xa1, 0x01, /* Collection (Application) 146 */
|
||||
0x85, 0x06, /* Report ID (6) 148 */
|
||||
0x95, 0x38, /* Report Count (56) 150 */
|
||||
0x75, 0x01, /* Report Size (1) 152 */
|
||||
0x15, 0x00, /* Logical Minimum (0) 154 */
|
||||
0x25, 0x01, /* Logical Maximum (1) 156 */
|
||||
0x05, 0x07, /* Usage Page (Keyboard) 158 */
|
||||
0x19, 0x68, /* Usage Minimum (104) 160 */
|
||||
0x29, 0x9f, /* Usage Maximum (159) 162 */
|
||||
0x81, 0x02, /* Input (Data,Var,Abs) 164 */
|
||||
0xc0, /* End Collection 166 */
|
||||
};
|
||||
|
||||
static __u8 *sm_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
if (*rsize == sizeof(sm_0059_rdesc) &&
|
||||
!memcmp(sm_0059_rdesc, rdesc, *rsize)) {
|
||||
hid_info(hdev, "Fixing up SiGma Micro report descriptor\n");
|
||||
rdesc[99] = 0x02;
|
||||
}
|
||||
return rdesc;
|
||||
}
|
||||
|
||||
static const struct hid_device_id sm_devices[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SIGMA_MICRO,
|
||||
USB_DEVICE_ID_SIGMA_MICRO_KEYBOARD2) },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(hid, sm_devices);
|
||||
|
||||
static struct hid_driver sm_driver = {
|
||||
.name = "sigmamicro",
|
||||
.id_table = sm_devices,
|
||||
.report_fixup = sm_report_fixup,
|
||||
};
|
||||
module_hid_driver(sm_driver);
|
||||
|
||||
MODULE_AUTHOR("Kinglong Mee <kinglongmee@gmail.com>");
|
||||
MODULE_AUTHOR("Desmond Lim <peckishrine@gmail.com>");
|
||||
MODULE_DESCRIPTION("SiGma Micro HID driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -81,24 +81,6 @@ static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
return rdesc;
|
||||
}
|
||||
|
||||
static int uclogic_input_mapping(struct hid_device *hdev,
|
||||
struct hid_input *hi,
|
||||
struct hid_field *field,
|
||||
struct hid_usage *usage,
|
||||
unsigned long **bit,
|
||||
int *max)
|
||||
{
|
||||
struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev);
|
||||
struct uclogic_params *params = &drvdata->params;
|
||||
|
||||
/* discard the unused pen interface */
|
||||
if (params->pen_unused && (field->application == HID_DG_PEN))
|
||||
return -1;
|
||||
|
||||
/* let hid-core decide what to do */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uclogic_input_configured(struct hid_device *hdev,
|
||||
struct hid_input *hi)
|
||||
{
|
||||
@ -246,100 +228,171 @@ static int uclogic_resume(struct hid_device *hdev)
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* uclogic_raw_event_pen - handle raw pen events (pen HID reports).
|
||||
*
|
||||
* @drvdata: Driver data.
|
||||
* @data: Report data buffer, can be modified.
|
||||
* @size: Report data size, bytes.
|
||||
*
|
||||
* Returns:
|
||||
* Negative value on error (stops event delivery), zero for success.
|
||||
*/
|
||||
static int uclogic_raw_event_pen(struct uclogic_drvdata *drvdata,
|
||||
u8 *data, int size)
|
||||
{
|
||||
struct uclogic_params_pen *pen = &drvdata->params.pen;
|
||||
|
||||
WARN_ON(drvdata == NULL);
|
||||
WARN_ON(data == NULL && size != 0);
|
||||
|
||||
/* If in-range reports are inverted */
|
||||
if (pen->inrange ==
|
||||
UCLOGIC_PARAMS_PEN_INRANGE_INVERTED) {
|
||||
/* Invert the in-range bit */
|
||||
data[1] ^= 0x40;
|
||||
}
|
||||
/*
|
||||
* If report contains fragmented high-resolution pen
|
||||
* coordinates
|
||||
*/
|
||||
if (size >= 10 && pen->fragmented_hires) {
|
||||
u8 pressure_low_byte;
|
||||
u8 pressure_high_byte;
|
||||
|
||||
/* Lift pressure bytes */
|
||||
pressure_low_byte = data[6];
|
||||
pressure_high_byte = data[7];
|
||||
/*
|
||||
* Move Y coord to make space for high-order X
|
||||
* coord byte
|
||||
*/
|
||||
data[6] = data[5];
|
||||
data[5] = data[4];
|
||||
/* Move high-order X coord byte */
|
||||
data[4] = data[8];
|
||||
/* Move high-order Y coord byte */
|
||||
data[7] = data[9];
|
||||
/* Place pressure bytes */
|
||||
data[8] = pressure_low_byte;
|
||||
data[9] = pressure_high_byte;
|
||||
}
|
||||
/* If we need to emulate in-range detection */
|
||||
if (pen->inrange == UCLOGIC_PARAMS_PEN_INRANGE_NONE) {
|
||||
/* Set in-range bit */
|
||||
data[1] |= 0x40;
|
||||
/* (Re-)start in-range timeout */
|
||||
mod_timer(&drvdata->inrange_timer,
|
||||
jiffies + msecs_to_jiffies(100));
|
||||
}
|
||||
/* If we report tilt and Y direction is flipped */
|
||||
if (size >= 12 && pen->tilt_y_flipped)
|
||||
data[11] = -data[11];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* uclogic_raw_event_frame - handle raw frame events (frame HID reports).
|
||||
*
|
||||
* @drvdata: Driver data.
|
||||
* @frame: The parameters of the frame controls to handle.
|
||||
* @data: Report data buffer, can be modified.
|
||||
* @size: Report data size, bytes.
|
||||
*
|
||||
* Returns:
|
||||
* Negative value on error (stops event delivery), zero for success.
|
||||
*/
|
||||
static int uclogic_raw_event_frame(
|
||||
struct uclogic_drvdata *drvdata,
|
||||
const struct uclogic_params_frame *frame,
|
||||
u8 *data, int size)
|
||||
{
|
||||
WARN_ON(drvdata == NULL);
|
||||
WARN_ON(data == NULL && size != 0);
|
||||
|
||||
/* If need to, and can, set pad device ID for Wacom drivers */
|
||||
if (frame->dev_id_byte > 0 && frame->dev_id_byte < size) {
|
||||
data[frame->dev_id_byte] = 0xf;
|
||||
}
|
||||
/* If need to, and can, read rotary encoder state change */
|
||||
if (frame->re_lsb > 0 && frame->re_lsb / 8 < size) {
|
||||
unsigned int byte = frame->re_lsb / 8;
|
||||
unsigned int bit = frame->re_lsb % 8;
|
||||
|
||||
u8 change;
|
||||
u8 prev_state = drvdata->re_state;
|
||||
/* Read Gray-coded state */
|
||||
u8 state = (data[byte] >> bit) & 0x3;
|
||||
/* Encode state change into 2-bit signed integer */
|
||||
if ((prev_state == 1 && state == 0) ||
|
||||
(prev_state == 2 && state == 3)) {
|
||||
change = 1;
|
||||
} else if ((prev_state == 2 && state == 0) ||
|
||||
(prev_state == 1 && state == 3)) {
|
||||
change = 3;
|
||||
} else {
|
||||
change = 0;
|
||||
}
|
||||
/* Write change */
|
||||
data[byte] = (data[byte] & ~((u8)3 << bit)) |
|
||||
(change << bit);
|
||||
/* Remember state */
|
||||
drvdata->re_state = state;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uclogic_raw_event(struct hid_device *hdev,
|
||||
struct hid_report *report,
|
||||
u8 *data, int size)
|
||||
{
|
||||
unsigned int report_id = report->id;
|
||||
struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev);
|
||||
struct uclogic_params *params = &drvdata->params;
|
||||
struct uclogic_params_pen_subreport *subreport;
|
||||
struct uclogic_params_pen_subreport *subreport_list_end;
|
||||
size_t i;
|
||||
|
||||
/* Tweak pen reports, if necessary */
|
||||
if (!params->pen_unused &&
|
||||
(report->type == HID_INPUT_REPORT) &&
|
||||
(report->id == params->pen.id) &&
|
||||
(size >= 2)) {
|
||||
/* If it's the "virtual" frame controls report */
|
||||
if (params->frame.id != 0 &&
|
||||
data[1] & params->pen_frame_flag) {
|
||||
/* Change to virtual frame controls report ID */
|
||||
data[0] = params->frame.id;
|
||||
return 0;
|
||||
}
|
||||
/* If in-range reports are inverted */
|
||||
if (params->pen.inrange ==
|
||||
UCLOGIC_PARAMS_PEN_INRANGE_INVERTED) {
|
||||
/* Invert the in-range bit */
|
||||
data[1] ^= 0x40;
|
||||
}
|
||||
/*
|
||||
* If report contains fragmented high-resolution pen
|
||||
* coordinates
|
||||
*/
|
||||
if (size >= 10 && params->pen.fragmented_hires) {
|
||||
u8 pressure_low_byte;
|
||||
u8 pressure_high_byte;
|
||||
/* Do not handle anything but input reports */
|
||||
if (report->type != HID_INPUT_REPORT)
|
||||
return 0;
|
||||
|
||||
/* Lift pressure bytes */
|
||||
pressure_low_byte = data[6];
|
||||
pressure_high_byte = data[7];
|
||||
/*
|
||||
* Move Y coord to make space for high-order X
|
||||
* coord byte
|
||||
*/
|
||||
data[6] = data[5];
|
||||
data[5] = data[4];
|
||||
/* Move high-order X coord byte */
|
||||
data[4] = data[8];
|
||||
/* Move high-order Y coord byte */
|
||||
data[7] = data[9];
|
||||
/* Place pressure bytes */
|
||||
data[8] = pressure_low_byte;
|
||||
data[9] = pressure_high_byte;
|
||||
}
|
||||
/* If we need to emulate in-range detection */
|
||||
if (params->pen.inrange == UCLOGIC_PARAMS_PEN_INRANGE_NONE) {
|
||||
/* Set in-range bit */
|
||||
data[1] |= 0x40;
|
||||
/* (Re-)start in-range timeout */
|
||||
mod_timer(&drvdata->inrange_timer,
|
||||
jiffies + msecs_to_jiffies(100));
|
||||
}
|
||||
}
|
||||
|
||||
/* Tweak frame control reports, if necessary */
|
||||
if ((report->type == HID_INPUT_REPORT) &&
|
||||
(report->id == params->frame.id)) {
|
||||
/* If need to, and can, set pad device ID for Wacom drivers */
|
||||
if (params->frame.dev_id_byte > 0 &&
|
||||
params->frame.dev_id_byte < size) {
|
||||
data[params->frame.dev_id_byte] = 0xf;
|
||||
}
|
||||
/* If need to, and can, read rotary encoder state change */
|
||||
if (params->frame.re_lsb > 0 &&
|
||||
params->frame.re_lsb / 8 < size) {
|
||||
unsigned int byte = params->frame.re_lsb / 8;
|
||||
unsigned int bit = params->frame.re_lsb % 8;
|
||||
|
||||
u8 change;
|
||||
u8 prev_state = drvdata->re_state;
|
||||
/* Read Gray-coded state */
|
||||
u8 state = (data[byte] >> bit) & 0x3;
|
||||
/* Encode state change into 2-bit signed integer */
|
||||
if ((prev_state == 1 && state == 0) ||
|
||||
(prev_state == 2 && state == 3)) {
|
||||
change = 1;
|
||||
} else if ((prev_state == 2 && state == 0) ||
|
||||
(prev_state == 1 && state == 3)) {
|
||||
change = 3;
|
||||
} else {
|
||||
change = 0;
|
||||
while (true) {
|
||||
/* Tweak pen reports, if necessary */
|
||||
if ((report_id == params->pen.id) && (size >= 2)) {
|
||||
subreport_list_end =
|
||||
params->pen.subreport_list +
|
||||
ARRAY_SIZE(params->pen.subreport_list);
|
||||
/* Try to match a subreport */
|
||||
for (subreport = params->pen.subreport_list;
|
||||
subreport < subreport_list_end; subreport++) {
|
||||
if (subreport->value != 0 &&
|
||||
subreport->value == data[1]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* If a subreport matched */
|
||||
if (subreport < subreport_list_end) {
|
||||
/* Change to subreport ID, and restart */
|
||||
report_id = data[0] = subreport->id;
|
||||
continue;
|
||||
} else {
|
||||
return uclogic_raw_event_pen(drvdata, data, size);
|
||||
}
|
||||
/* Write change */
|
||||
data[byte] = (data[byte] & ~((u8)3 << bit)) |
|
||||
(change << bit);
|
||||
/* Remember state */
|
||||
drvdata->re_state = state;
|
||||
}
|
||||
|
||||
/* Tweak frame control reports, if necessary */
|
||||
for (i = 0; i < ARRAY_SIZE(params->frame_list); i++) {
|
||||
if (report_id == params->frame_list[i].id) {
|
||||
return uclogic_raw_event_frame(
|
||||
drvdata, ¶ms->frame_list[i],
|
||||
data, size);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -373,7 +426,7 @@ static const struct hid_device_id uclogic_devices[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_HUION,
|
||||
USB_DEVICE_ID_HUION_TABLET) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_HUION,
|
||||
USB_DEVICE_ID_HUION_HS64) },
|
||||
USB_DEVICE_ID_HUION_TABLET2) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_TRUST,
|
||||
USB_DEVICE_ID_TRUST_PANORA_TABLET) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
|
||||
@ -415,7 +468,6 @@ static struct hid_driver uclogic_driver = {
|
||||
.remove = uclogic_remove,
|
||||
.report_fixup = uclogic_report_fixup,
|
||||
.raw_event = uclogic_raw_event,
|
||||
.input_mapping = uclogic_input_mapping,
|
||||
.input_configured = uclogic_input_configured,
|
||||
#ifdef CONFIG_PM
|
||||
.resume = uclogic_resume,
|
||||
|
@ -207,8 +207,8 @@ static int uclogic_params_pen_init_v1(struct uclogic_params_pen *pen,
|
||||
* Generate pen report descriptor
|
||||
*/
|
||||
desc_ptr = uclogic_rdesc_template_apply(
|
||||
uclogic_rdesc_pen_v1_template_arr,
|
||||
uclogic_rdesc_pen_v1_template_size,
|
||||
uclogic_rdesc_v1_pen_template_arr,
|
||||
uclogic_rdesc_v1_pen_template_size,
|
||||
desc_params, ARRAY_SIZE(desc_params));
|
||||
if (desc_ptr == NULL) {
|
||||
rc = -ENOMEM;
|
||||
@ -221,8 +221,8 @@ static int uclogic_params_pen_init_v1(struct uclogic_params_pen *pen,
|
||||
memset(pen, 0, sizeof(*pen));
|
||||
pen->desc_ptr = desc_ptr;
|
||||
desc_ptr = NULL;
|
||||
pen->desc_size = uclogic_rdesc_pen_v1_template_size;
|
||||
pen->id = UCLOGIC_RDESC_PEN_V1_ID;
|
||||
pen->desc_size = uclogic_rdesc_v1_pen_template_size;
|
||||
pen->id = UCLOGIC_RDESC_V1_PEN_ID;
|
||||
pen->inrange = UCLOGIC_PARAMS_PEN_INRANGE_INVERTED;
|
||||
found = true;
|
||||
finish:
|
||||
@ -351,8 +351,8 @@ static int uclogic_params_pen_init_v2(struct uclogic_params_pen *pen,
|
||||
* Generate pen report descriptor
|
||||
*/
|
||||
desc_ptr = uclogic_rdesc_template_apply(
|
||||
uclogic_rdesc_pen_v2_template_arr,
|
||||
uclogic_rdesc_pen_v2_template_size,
|
||||
uclogic_rdesc_v2_pen_template_arr,
|
||||
uclogic_rdesc_v2_pen_template_size,
|
||||
desc_params, ARRAY_SIZE(desc_params));
|
||||
if (desc_ptr == NULL) {
|
||||
rc = -ENOMEM;
|
||||
@ -365,10 +365,11 @@ static int uclogic_params_pen_init_v2(struct uclogic_params_pen *pen,
|
||||
memset(pen, 0, sizeof(*pen));
|
||||
pen->desc_ptr = desc_ptr;
|
||||
desc_ptr = NULL;
|
||||
pen->desc_size = uclogic_rdesc_pen_v2_template_size;
|
||||
pen->id = UCLOGIC_RDESC_PEN_V2_ID;
|
||||
pen->desc_size = uclogic_rdesc_v2_pen_template_size;
|
||||
pen->id = UCLOGIC_RDESC_V2_PEN_ID;
|
||||
pen->inrange = UCLOGIC_PARAMS_PEN_INRANGE_NONE;
|
||||
pen->fragmented_hires = true;
|
||||
pen->tilt_y_flipped = true;
|
||||
found = true;
|
||||
finish:
|
||||
*pfound = found;
|
||||
@ -430,8 +431,8 @@ static int uclogic_params_frame_init_with_desc(
|
||||
}
|
||||
|
||||
/**
|
||||
* uclogic_params_frame_init_v1_buttonpad() - initialize abstract buttonpad
|
||||
* on a v1 tablet interface.
|
||||
* uclogic_params_frame_init_v1() - initialize v1 tablet interface frame
|
||||
* controls.
|
||||
*
|
||||
* @frame: Pointer to the frame parameters to initialize (to be cleaned
|
||||
* up with uclogic_params_frame_cleanup()). Not modified in case
|
||||
@ -445,8 +446,7 @@ static int uclogic_params_frame_init_with_desc(
|
||||
* Returns:
|
||||
* Zero, if successful. A negative errno code on error.
|
||||
*/
|
||||
static int uclogic_params_frame_init_v1_buttonpad(
|
||||
struct uclogic_params_frame *frame,
|
||||
static int uclogic_params_frame_init_v1(struct uclogic_params_frame *frame,
|
||||
bool *pfound,
|
||||
struct hid_device *hdev)
|
||||
{
|
||||
@ -487,9 +487,9 @@ static int uclogic_params_frame_init_v1_buttonpad(
|
||||
hid_dbg(hdev, "generic buttons enabled\n");
|
||||
rc = uclogic_params_frame_init_with_desc(
|
||||
frame,
|
||||
uclogic_rdesc_buttonpad_v1_arr,
|
||||
uclogic_rdesc_buttonpad_v1_size,
|
||||
UCLOGIC_RDESC_BUTTONPAD_V1_ID);
|
||||
uclogic_rdesc_v1_frame_arr,
|
||||
uclogic_rdesc_v1_frame_size,
|
||||
UCLOGIC_RDESC_V1_FRAME_ID);
|
||||
if (rc != 0)
|
||||
goto cleanup;
|
||||
found = true;
|
||||
@ -512,10 +512,12 @@ cleanup:
|
||||
void uclogic_params_cleanup(struct uclogic_params *params)
|
||||
{
|
||||
if (!params->invalid) {
|
||||
size_t i;
|
||||
kfree(params->desc_ptr);
|
||||
if (!params->pen_unused)
|
||||
uclogic_params_pen_cleanup(¶ms->pen);
|
||||
uclogic_params_frame_cleanup(¶ms->frame);
|
||||
uclogic_params_pen_cleanup(¶ms->pen);
|
||||
for (i = 0; i < ARRAY_SIZE(params->frame_list); i++)
|
||||
uclogic_params_frame_cleanup(¶ms->frame_list[i]);
|
||||
|
||||
memset(params, 0, sizeof(*params));
|
||||
}
|
||||
}
|
||||
@ -543,60 +545,53 @@ int uclogic_params_get_desc(const struct uclogic_params *params,
|
||||
__u8 **pdesc,
|
||||
unsigned int *psize)
|
||||
{
|
||||
bool common_present;
|
||||
bool pen_present;
|
||||
bool frame_present;
|
||||
unsigned int size;
|
||||
int rc = -ENOMEM;
|
||||
bool present = false;
|
||||
unsigned int size = 0;
|
||||
__u8 *desc = NULL;
|
||||
size_t i;
|
||||
|
||||
/* Check arguments */
|
||||
if (params == NULL || pdesc == NULL || psize == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
size = 0;
|
||||
/* Concatenate descriptors */
|
||||
#define ADD_DESC(_desc_ptr, _desc_size) \
|
||||
do { \
|
||||
unsigned int new_size; \
|
||||
__u8 *new_desc; \
|
||||
if ((_desc_ptr) == NULL) { \
|
||||
break; \
|
||||
} \
|
||||
new_size = size + (_desc_size); \
|
||||
new_desc = krealloc(desc, new_size, GFP_KERNEL); \
|
||||
if (new_desc == NULL) { \
|
||||
goto cleanup; \
|
||||
} \
|
||||
memcpy(new_desc + size, (_desc_ptr), (_desc_size)); \
|
||||
desc = new_desc; \
|
||||
size = new_size; \
|
||||
present = true; \
|
||||
} while (0)
|
||||
|
||||
common_present = (params->desc_ptr != NULL);
|
||||
pen_present = (!params->pen_unused && params->pen.desc_ptr != NULL);
|
||||
frame_present = (params->frame.desc_ptr != NULL);
|
||||
|
||||
if (common_present)
|
||||
size += params->desc_size;
|
||||
if (pen_present)
|
||||
size += params->pen.desc_size;
|
||||
if (frame_present)
|
||||
size += params->frame.desc_size;
|
||||
|
||||
if (common_present || pen_present || frame_present) {
|
||||
__u8 *p;
|
||||
|
||||
desc = kmalloc(size, GFP_KERNEL);
|
||||
if (desc == NULL)
|
||||
return -ENOMEM;
|
||||
p = desc;
|
||||
|
||||
if (common_present) {
|
||||
memcpy(p, params->desc_ptr,
|
||||
params->desc_size);
|
||||
p += params->desc_size;
|
||||
}
|
||||
if (pen_present) {
|
||||
memcpy(p, params->pen.desc_ptr,
|
||||
params->pen.desc_size);
|
||||
p += params->pen.desc_size;
|
||||
}
|
||||
if (frame_present) {
|
||||
memcpy(p, params->frame.desc_ptr,
|
||||
params->frame.desc_size);
|
||||
p += params->frame.desc_size;
|
||||
}
|
||||
|
||||
WARN_ON(p != desc + size);
|
||||
|
||||
*psize = size;
|
||||
ADD_DESC(params->desc_ptr, params->desc_size);
|
||||
ADD_DESC(params->pen.desc_ptr, params->pen.desc_size);
|
||||
for (i = 0; i < ARRAY_SIZE(params->frame_list); i++) {
|
||||
ADD_DESC(params->frame_list[i].desc_ptr,
|
||||
params->frame_list[i].desc_size);
|
||||
}
|
||||
|
||||
*pdesc = desc;
|
||||
return 0;
|
||||
#undef ADD_DESC
|
||||
|
||||
if (present) {
|
||||
*pdesc = desc;
|
||||
*psize = size;
|
||||
desc = NULL;
|
||||
}
|
||||
rc = 0;
|
||||
cleanup:
|
||||
kfree(desc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -679,21 +674,6 @@ cleanup:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* uclogic_params_init_with_pen_unused() - initialize tablet interface
|
||||
* parameters preserving original reports and generic HID processing, but
|
||||
* disabling pen usage.
|
||||
*
|
||||
* @params: Parameters to initialize (to be cleaned with
|
||||
* uclogic_params_cleanup()). Not modified in case of
|
||||
* error. Cannot be NULL.
|
||||
*/
|
||||
static void uclogic_params_init_with_pen_unused(struct uclogic_params *params)
|
||||
{
|
||||
memset(params, 0, sizeof(*params));
|
||||
params->pen_unused = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* uclogic_params_huion_init() - initialize a Huion tablet interface and discover
|
||||
* its parameters.
|
||||
@ -733,8 +713,7 @@ static int uclogic_params_huion_init(struct uclogic_params *params,
|
||||
|
||||
/* If it's not a pen interface */
|
||||
if (bInterfaceNumber != 0) {
|
||||
/* TODO: Consider marking the interface invalid */
|
||||
uclogic_params_init_with_pen_unused(&p);
|
||||
uclogic_params_init_invalid(&p);
|
||||
goto output;
|
||||
}
|
||||
|
||||
@ -766,20 +745,22 @@ static int uclogic_params_huion_init(struct uclogic_params *params,
|
||||
goto cleanup;
|
||||
} else if (found) {
|
||||
hid_dbg(hdev, "pen v2 parameters found\n");
|
||||
/* Create v2 buttonpad parameters */
|
||||
/* Create v2 frame parameters */
|
||||
rc = uclogic_params_frame_init_with_desc(
|
||||
&p.frame,
|
||||
uclogic_rdesc_buttonpad_v2_arr,
|
||||
uclogic_rdesc_buttonpad_v2_size,
|
||||
UCLOGIC_RDESC_BUTTONPAD_V2_ID);
|
||||
&p.frame_list[0],
|
||||
uclogic_rdesc_v2_frame_arr,
|
||||
uclogic_rdesc_v2_frame_size,
|
||||
UCLOGIC_RDESC_V2_FRAME_ID);
|
||||
if (rc != 0) {
|
||||
hid_err(hdev,
|
||||
"failed creating v2 buttonpad parameters: %d\n",
|
||||
"failed creating v2 frame parameters: %d\n",
|
||||
rc);
|
||||
goto cleanup;
|
||||
}
|
||||
/* Set bitmask marking frame reports in pen reports */
|
||||
p.pen_frame_flag = 0x20;
|
||||
/* Link frame button subreports from pen reports */
|
||||
p.pen.subreport_list[0].value = 0xe0;
|
||||
p.pen.subreport_list[0].id =
|
||||
UCLOGIC_RDESC_V2_FRAME_ID;
|
||||
goto output;
|
||||
}
|
||||
hid_dbg(hdev, "pen v2 parameters not found\n");
|
||||
@ -793,19 +774,20 @@ static int uclogic_params_huion_init(struct uclogic_params *params,
|
||||
goto cleanup;
|
||||
} else if (found) {
|
||||
hid_dbg(hdev, "pen v1 parameters found\n");
|
||||
/* Try to probe v1 buttonpad */
|
||||
rc = uclogic_params_frame_init_v1_buttonpad(
|
||||
&p.frame,
|
||||
&found, hdev);
|
||||
/* Try to probe v1 frame */
|
||||
rc = uclogic_params_frame_init_v1(&p.frame_list[0],
|
||||
&found, hdev);
|
||||
if (rc != 0) {
|
||||
hid_err(hdev, "v1 buttonpad probing failed: %d\n", rc);
|
||||
hid_err(hdev, "v1 frame probing failed: %d\n", rc);
|
||||
goto cleanup;
|
||||
}
|
||||
hid_dbg(hdev, "buttonpad v1 parameters%s found\n",
|
||||
hid_dbg(hdev, "frame v1 parameters%s found\n",
|
||||
(found ? "" : " not"));
|
||||
if (found) {
|
||||
/* Set bitmask marking frame reports */
|
||||
p.pen_frame_flag = 0x20;
|
||||
/* Link frame button subreports from pen reports */
|
||||
p.pen.subreport_list[0].value = 0xe0;
|
||||
p.pen.subreport_list[0].id =
|
||||
UCLOGIC_RDESC_V1_FRAME_ID;
|
||||
}
|
||||
goto output;
|
||||
}
|
||||
@ -992,7 +974,7 @@ int uclogic_params_init(struct uclogic_params *params,
|
||||
case VID_PID(USB_VENDOR_ID_HUION,
|
||||
USB_DEVICE_ID_HUION_TABLET):
|
||||
case VID_PID(USB_VENDOR_ID_HUION,
|
||||
USB_DEVICE_ID_HUION_HS64):
|
||||
USB_DEVICE_ID_HUION_TABLET2):
|
||||
case VID_PID(USB_VENDOR_ID_UCLOGIC,
|
||||
USB_DEVICE_ID_HUION_TABLET):
|
||||
case VID_PID(USB_VENDOR_ID_UCLOGIC,
|
||||
@ -1032,8 +1014,7 @@ int uclogic_params_init(struct uclogic_params *params,
|
||||
uclogic_params_init_invalid(&p);
|
||||
}
|
||||
} else {
|
||||
/* TODO: Consider marking the interface invalid */
|
||||
uclogic_params_init_with_pen_unused(&p);
|
||||
uclogic_params_init_invalid(&p);
|
||||
}
|
||||
break;
|
||||
case VID_PID(USB_VENDOR_ID_UGEE,
|
||||
@ -1048,15 +1029,14 @@ int uclogic_params_init(struct uclogic_params *params,
|
||||
}
|
||||
/* Initialize frame parameters */
|
||||
rc = uclogic_params_frame_init_with_desc(
|
||||
&p.frame,
|
||||
&p.frame_list[0],
|
||||
uclogic_rdesc_xppen_deco01_frame_arr,
|
||||
uclogic_rdesc_xppen_deco01_frame_size,
|
||||
0);
|
||||
if (rc != 0)
|
||||
goto cleanup;
|
||||
} else {
|
||||
/* TODO: Consider marking the interface invalid */
|
||||
uclogic_params_init_with_pen_unused(&p);
|
||||
uclogic_params_init_invalid(&p);
|
||||
}
|
||||
break;
|
||||
case VID_PID(USB_VENDOR_ID_TRUST,
|
||||
@ -1075,19 +1055,19 @@ int uclogic_params_init(struct uclogic_params *params,
|
||||
goto cleanup;
|
||||
} else if (found) {
|
||||
rc = uclogic_params_frame_init_with_desc(
|
||||
&p.frame,
|
||||
&p.frame_list[0],
|
||||
uclogic_rdesc_ugee_g5_frame_arr,
|
||||
uclogic_rdesc_ugee_g5_frame_size,
|
||||
UCLOGIC_RDESC_UGEE_G5_FRAME_ID);
|
||||
if (rc != 0) {
|
||||
hid_err(hdev,
|
||||
"failed creating buttonpad parameters: %d\n",
|
||||
"failed creating frame parameters: %d\n",
|
||||
rc);
|
||||
goto cleanup;
|
||||
}
|
||||
p.frame.re_lsb =
|
||||
p.frame_list[0].re_lsb =
|
||||
UCLOGIC_RDESC_UGEE_G5_FRAME_RE_LSB;
|
||||
p.frame.dev_id_byte =
|
||||
p.frame_list[0].dev_id_byte =
|
||||
UCLOGIC_RDESC_UGEE_G5_FRAME_DEV_ID_BYTE;
|
||||
} else {
|
||||
hid_warn(hdev, "pen parameters not found");
|
||||
@ -1109,13 +1089,13 @@ int uclogic_params_init(struct uclogic_params *params,
|
||||
goto cleanup;
|
||||
} else if (found) {
|
||||
rc = uclogic_params_frame_init_with_desc(
|
||||
&p.frame,
|
||||
uclogic_rdesc_ugee_ex07_buttonpad_arr,
|
||||
uclogic_rdesc_ugee_ex07_buttonpad_size,
|
||||
&p.frame_list[0],
|
||||
uclogic_rdesc_ugee_ex07_frame_arr,
|
||||
uclogic_rdesc_ugee_ex07_frame_size,
|
||||
0);
|
||||
if (rc != 0) {
|
||||
hid_err(hdev,
|
||||
"failed creating buttonpad parameters: %d\n",
|
||||
"failed creating frame parameters: %d\n",
|
||||
rc);
|
||||
goto cleanup;
|
||||
}
|
||||
|
@ -33,6 +33,25 @@ enum uclogic_params_pen_inrange {
|
||||
extern const char *uclogic_params_pen_inrange_to_str(
|
||||
enum uclogic_params_pen_inrange inrange);
|
||||
|
||||
|
||||
/*
|
||||
* Pen report's subreport data.
|
||||
*/
|
||||
struct uclogic_params_pen_subreport {
|
||||
/*
|
||||
* The value of the second byte of the pen report indicating this
|
||||
* subreport. If zero, the subreport should be considered invalid and
|
||||
* not matched.
|
||||
*/
|
||||
__u8 value;
|
||||
|
||||
/*
|
||||
* The ID to be assigned to the report, if the second byte of the pen
|
||||
* report is equal to "value". Only valid if "value" is not zero.
|
||||
*/
|
||||
__u8 id;
|
||||
};
|
||||
|
||||
/*
|
||||
* Tablet interface's pen input parameters.
|
||||
*
|
||||
@ -54,6 +73,8 @@ struct uclogic_params_pen {
|
||||
unsigned int desc_size;
|
||||
/* Report ID, if reports should be tweaked, zero if not */
|
||||
unsigned int id;
|
||||
/* The list of subreports */
|
||||
struct uclogic_params_pen_subreport subreport_list[1];
|
||||
/* Type of in-range reporting, only valid if "id" is not zero */
|
||||
enum uclogic_params_pen_inrange inrange;
|
||||
/*
|
||||
@ -62,6 +83,12 @@ struct uclogic_params_pen {
|
||||
* Only valid if "id" is not zero.
|
||||
*/
|
||||
bool fragmented_hires;
|
||||
/*
|
||||
* True if the pen reports tilt in bytes at offset 10 (X) and 11 (Y),
|
||||
* and the Y tilt direction is flipped.
|
||||
* Only valid if "id" is not zero.
|
||||
*/
|
||||
bool tilt_y_flipped;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -132,28 +159,16 @@ struct uclogic_params {
|
||||
* Only valid, if "desc_ptr" is not NULL.
|
||||
*/
|
||||
unsigned int desc_size;
|
||||
/*
|
||||
* True, if pen usage in report descriptor is invalid, when present.
|
||||
* Only valid, if "invalid" is false.
|
||||
*/
|
||||
bool pen_unused;
|
||||
/*
|
||||
* Pen parameters and optional report descriptor part.
|
||||
* Only valid if "pen_unused" is valid and false.
|
||||
* Only valid, if "invalid" is false.
|
||||
*/
|
||||
struct uclogic_params_pen pen;
|
||||
/*
|
||||
* Frame control parameters and optional report descriptor part.
|
||||
* Only valid, if "invalid" is false.
|
||||
* The list of frame control parameters and optional report descriptor
|
||||
* parts. Only valid, if "invalid" is false.
|
||||
*/
|
||||
struct uclogic_params_frame frame;
|
||||
/*
|
||||
* Bitmask matching frame controls "sub-report" flag in the second
|
||||
* byte of the pen report, or zero if it's not expected.
|
||||
* Only valid if both "pen" and "frame" are valid, and "frame.id" is
|
||||
* not zero.
|
||||
*/
|
||||
__u8 pen_frame_flag;
|
||||
struct uclogic_params_frame frame_list[1];
|
||||
};
|
||||
|
||||
/* Initialize a tablet interface and discover its parameters */
|
||||
@ -162,39 +177,40 @@ extern int uclogic_params_init(struct uclogic_params *params,
|
||||
|
||||
/* Tablet interface parameters *printf format string */
|
||||
#define UCLOGIC_PARAMS_FMT_STR \
|
||||
".invalid = %s\n" \
|
||||
".desc_ptr = %p\n" \
|
||||
".desc_size = %u\n" \
|
||||
".pen_unused = %s\n" \
|
||||
".pen.desc_ptr = %p\n" \
|
||||
".pen.desc_size = %u\n" \
|
||||
".pen.id = %u\n" \
|
||||
".pen.inrange = %s\n" \
|
||||
".pen.fragmented_hires = %s\n" \
|
||||
".frame.desc_ptr = %p\n" \
|
||||
".frame.desc_size = %u\n" \
|
||||
".frame.id = %u\n" \
|
||||
".frame.re_lsb = %u\n" \
|
||||
".frame.dev_id_byte = %u\n" \
|
||||
".pen_frame_flag = 0x%02x\n"
|
||||
".invalid = %s\n" \
|
||||
".desc_ptr = %p\n" \
|
||||
".desc_size = %u\n" \
|
||||
".pen.desc_ptr = %p\n" \
|
||||
".pen.desc_size = %u\n" \
|
||||
".pen.id = %u\n" \
|
||||
".pen.subreport_list[0] = {0x%02hhx, %hhu}\n" \
|
||||
".pen.inrange = %s\n" \
|
||||
".pen.fragmented_hires = %s\n" \
|
||||
".pen.tilt_y_flipped = %s\n" \
|
||||
".frame_list[0].desc_ptr = %p\n" \
|
||||
".frame_list[0].desc_size = %u\n" \
|
||||
".frame_list[0].id = %u\n" \
|
||||
".frame_list[0].re_lsb = %u\n" \
|
||||
".frame_list[0].dev_id_byte = %u\n"
|
||||
|
||||
/* Tablet interface parameters *printf format arguments */
|
||||
#define UCLOGIC_PARAMS_FMT_ARGS(_params) \
|
||||
((_params)->invalid ? "true" : "false"), \
|
||||
(_params)->desc_ptr, \
|
||||
(_params)->desc_size, \
|
||||
((_params)->pen_unused ? "true" : "false"), \
|
||||
(_params)->pen.desc_ptr, \
|
||||
(_params)->pen.desc_size, \
|
||||
(_params)->pen.id, \
|
||||
(_params)->pen.subreport_list[0].value, \
|
||||
(_params)->pen.subreport_list[0].id, \
|
||||
uclogic_params_pen_inrange_to_str((_params)->pen.inrange), \
|
||||
((_params)->pen.fragmented_hires ? "true" : "false"), \
|
||||
(_params)->frame.desc_ptr, \
|
||||
(_params)->frame.desc_size, \
|
||||
(_params)->frame.id, \
|
||||
(_params)->frame.re_lsb, \
|
||||
(_params)->frame.dev_id_byte, \
|
||||
(_params)->pen_frame_flag
|
||||
((_params)->pen.tilt_y_flipped ? "true" : "false"), \
|
||||
(_params)->frame_list[0].desc_ptr, \
|
||||
(_params)->frame_list[0].desc_size, \
|
||||
(_params)->frame_list[0].id, \
|
||||
(_params)->frame_list[0].re_lsb, \
|
||||
(_params)->frame_list[0].dev_id_byte
|
||||
|
||||
/* Get a replacement report descriptor for a tablet's interface. */
|
||||
extern int uclogic_params_get_desc(const struct uclogic_params *params,
|
||||
|
@ -532,7 +532,7 @@ const size_t uclogic_rdesc_twha60_fixed1_size =
|
||||
sizeof(uclogic_rdesc_twha60_fixed1_arr);
|
||||
|
||||
/* Fixed report descriptor template for (tweaked) v1 pen reports */
|
||||
const __u8 uclogic_rdesc_pen_v1_template_arr[] = {
|
||||
const __u8 uclogic_rdesc_v1_pen_template_arr[] = {
|
||||
0x05, 0x0D, /* Usage Page (Digitizer), */
|
||||
0x09, 0x02, /* Usage (Pen), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
@ -582,11 +582,11 @@ const __u8 uclogic_rdesc_pen_v1_template_arr[] = {
|
||||
0xC0 /* End Collection */
|
||||
};
|
||||
|
||||
const size_t uclogic_rdesc_pen_v1_template_size =
|
||||
sizeof(uclogic_rdesc_pen_v1_template_arr);
|
||||
const size_t uclogic_rdesc_v1_pen_template_size =
|
||||
sizeof(uclogic_rdesc_v1_pen_template_arr);
|
||||
|
||||
/* Fixed report descriptor template for (tweaked) v2 pen reports */
|
||||
const __u8 uclogic_rdesc_pen_v2_template_arr[] = {
|
||||
const __u8 uclogic_rdesc_v2_pen_template_arr[] = {
|
||||
0x05, 0x0D, /* Usage Page (Digitizer), */
|
||||
0x09, 0x02, /* Usage (Pen), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
@ -633,25 +633,35 @@ const __u8 uclogic_rdesc_pen_v2_template_arr[] = {
|
||||
0x27, UCLOGIC_RDESC_PEN_PH(PRESSURE_LM),
|
||||
/* Logical Maximum (PLACEHOLDER), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x81, 0x03, /* Input (Constant, Variable), */
|
||||
0x54, /* Unit Exponent (0), */
|
||||
0x65, 0x14, /* Unit (Degrees), */
|
||||
0x35, 0xC4, /* Physical Minimum (-60), */
|
||||
0x45, 0x3C, /* Physical Maximum (60), */
|
||||
0x15, 0xC4, /* Logical Minimum (-60), */
|
||||
0x25, 0x3C, /* Logical Maximum (60), */
|
||||
0x75, 0x08, /* Report Size (8), */
|
||||
0x95, 0x02, /* Report Count (2), */
|
||||
0x09, 0x3D, /* Usage (X Tilt), */
|
||||
0x09, 0x3E, /* Usage (Y Tilt), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0xC0, /* End Collection, */
|
||||
0xC0 /* End Collection */
|
||||
};
|
||||
|
||||
const size_t uclogic_rdesc_pen_v2_template_size =
|
||||
sizeof(uclogic_rdesc_pen_v2_template_arr);
|
||||
const size_t uclogic_rdesc_v2_pen_template_size =
|
||||
sizeof(uclogic_rdesc_v2_pen_template_arr);
|
||||
|
||||
/*
|
||||
* Expand to the contents of a generic buttonpad report descriptor.
|
||||
* Expand to the contents of a generic frame report descriptor.
|
||||
*
|
||||
* @_padding: Padding from the end of button bits at bit 44, until
|
||||
* the end of the report, in bits.
|
||||
* @_id: The report ID to use.
|
||||
* @_size: Size of the report to pad to, including report ID, bytes.
|
||||
*/
|
||||
#define UCLOGIC_RDESC_BUTTONPAD_BYTES(_padding) \
|
||||
#define UCLOGIC_RDESC_FRAME_BYTES(_id, _size) \
|
||||
0x05, 0x01, /* Usage Page (Desktop), */ \
|
||||
0x09, 0x07, /* Usage (Keypad), */ \
|
||||
0xA1, 0x01, /* Collection (Application), */ \
|
||||
0x85, 0xF7, /* Report ID (247), */ \
|
||||
0x85, (_id), /* Report ID (_id), */ \
|
||||
0x14, /* Logical Minimum (0), */ \
|
||||
0x25, 0x01, /* Logical Maximum (1), */ \
|
||||
0x75, 0x01, /* Report Size (1), */ \
|
||||
@ -679,30 +689,31 @@ const size_t uclogic_rdesc_pen_v2_template_size =
|
||||
0xA0, /* Collection (Physical), */ \
|
||||
0x05, 0x09, /* Usage Page (Button), */ \
|
||||
0x19, 0x01, /* Usage Minimum (01h), */ \
|
||||
0x29, 0x02, /* Usage Maximum (02h), */ \
|
||||
0x95, 0x02, /* Report Count (2), */ \
|
||||
0x29, 0x03, /* Usage Maximum (03h), */ \
|
||||
0x95, 0x03, /* Report Count (3), */ \
|
||||
0x81, 0x02, /* Input (Variable), */ \
|
||||
0x95, _padding, /* Report Count (_padding), */ \
|
||||
0x95, ((_size) * 8 - 45), \
|
||||
/* Report Count (padding), */ \
|
||||
0x81, 0x01, /* Input (Constant), */ \
|
||||
0xC0, /* End Collection, */ \
|
||||
0xC0 /* End Collection */
|
||||
|
||||
/* Fixed report descriptor for (tweaked) v1 buttonpad reports */
|
||||
const __u8 uclogic_rdesc_buttonpad_v1_arr[] = {
|
||||
UCLOGIC_RDESC_BUTTONPAD_BYTES(20)
|
||||
/* Fixed report descriptor for (tweaked) v1 frame reports */
|
||||
const __u8 uclogic_rdesc_v1_frame_arr[] = {
|
||||
UCLOGIC_RDESC_FRAME_BYTES(UCLOGIC_RDESC_V1_FRAME_ID, 8)
|
||||
};
|
||||
const size_t uclogic_rdesc_buttonpad_v1_size =
|
||||
sizeof(uclogic_rdesc_buttonpad_v1_arr);
|
||||
const size_t uclogic_rdesc_v1_frame_size =
|
||||
sizeof(uclogic_rdesc_v1_frame_arr);
|
||||
|
||||
/* Fixed report descriptor for (tweaked) v2 buttonpad reports */
|
||||
const __u8 uclogic_rdesc_buttonpad_v2_arr[] = {
|
||||
UCLOGIC_RDESC_BUTTONPAD_BYTES(52)
|
||||
/* Fixed report descriptor for (tweaked) v2 frame reports */
|
||||
const __u8 uclogic_rdesc_v2_frame_arr[] = {
|
||||
UCLOGIC_RDESC_FRAME_BYTES(UCLOGIC_RDESC_V2_FRAME_ID, 12)
|
||||
};
|
||||
const size_t uclogic_rdesc_buttonpad_v2_size =
|
||||
sizeof(uclogic_rdesc_buttonpad_v2_arr);
|
||||
const size_t uclogic_rdesc_v2_frame_size =
|
||||
sizeof(uclogic_rdesc_v2_frame_arr);
|
||||
|
||||
/* Fixed report descriptor for Ugee EX07 buttonpad */
|
||||
const __u8 uclogic_rdesc_ugee_ex07_buttonpad_arr[] = {
|
||||
/* Fixed report descriptor for Ugee EX07 frame */
|
||||
const __u8 uclogic_rdesc_ugee_ex07_frame_arr[] = {
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x09, 0x07, /* Usage (Keypad), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
@ -725,8 +736,8 @@ const __u8 uclogic_rdesc_ugee_ex07_buttonpad_arr[] = {
|
||||
0xC0, /* End Collection, */
|
||||
0xC0 /* End Collection */
|
||||
};
|
||||
const size_t uclogic_rdesc_ugee_ex07_buttonpad_size =
|
||||
sizeof(uclogic_rdesc_ugee_ex07_buttonpad_arr);
|
||||
const size_t uclogic_rdesc_ugee_ex07_frame_size =
|
||||
sizeof(uclogic_rdesc_ugee_ex07_frame_arr);
|
||||
|
||||
/* Fixed report descriptor for Ugee G5 frame controls */
|
||||
const __u8 uclogic_rdesc_ugee_g5_frame_arr[] = {
|
||||
|
@ -104,36 +104,36 @@ enum uclogic_rdesc_pen_ph_id {
|
||||
UCLOGIC_RDESC_PH_HEAD, UCLOGIC_RDESC_PEN_PH_ID_##_ID
|
||||
|
||||
/* Report ID for v1 pen reports */
|
||||
#define UCLOGIC_RDESC_PEN_V1_ID 0x07
|
||||
#define UCLOGIC_RDESC_V1_PEN_ID 0x07
|
||||
|
||||
/* Fixed report descriptor template for (tweaked) v1 pen reports */
|
||||
extern const __u8 uclogic_rdesc_pen_v1_template_arr[];
|
||||
extern const size_t uclogic_rdesc_pen_v1_template_size;
|
||||
extern const __u8 uclogic_rdesc_v1_pen_template_arr[];
|
||||
extern const size_t uclogic_rdesc_v1_pen_template_size;
|
||||
|
||||
/* Report ID for v2 pen reports */
|
||||
#define UCLOGIC_RDESC_PEN_V2_ID 0x08
|
||||
#define UCLOGIC_RDESC_V2_PEN_ID 0x08
|
||||
|
||||
/* Fixed report descriptor template for (tweaked) v2 pen reports */
|
||||
extern const __u8 uclogic_rdesc_pen_v2_template_arr[];
|
||||
extern const size_t uclogic_rdesc_pen_v2_template_size;
|
||||
extern const __u8 uclogic_rdesc_v2_pen_template_arr[];
|
||||
extern const size_t uclogic_rdesc_v2_pen_template_size;
|
||||
|
||||
/* Fixed report descriptor for (tweaked) v1 buttonpad reports */
|
||||
extern const __u8 uclogic_rdesc_buttonpad_v1_arr[];
|
||||
extern const size_t uclogic_rdesc_buttonpad_v1_size;
|
||||
/* Report ID for tweaked v1 frame reports */
|
||||
#define UCLOGIC_RDESC_V1_FRAME_ID 0xf7
|
||||
|
||||
/* Report ID for tweaked v1 buttonpad reports */
|
||||
#define UCLOGIC_RDESC_BUTTONPAD_V1_ID 0xf7
|
||||
/* Fixed report descriptor for (tweaked) v1 frame reports */
|
||||
extern const __u8 uclogic_rdesc_v1_frame_arr[];
|
||||
extern const size_t uclogic_rdesc_v1_frame_size;
|
||||
|
||||
/* Fixed report descriptor for (tweaked) v2 buttonpad reports */
|
||||
extern const __u8 uclogic_rdesc_buttonpad_v2_arr[];
|
||||
extern const size_t uclogic_rdesc_buttonpad_v2_size;
|
||||
/* Report ID for tweaked v2 frame reports */
|
||||
#define UCLOGIC_RDESC_V2_FRAME_ID 0xf7
|
||||
|
||||
/* Report ID for tweaked v2 buttonpad reports */
|
||||
#define UCLOGIC_RDESC_BUTTONPAD_V2_ID 0xf7
|
||||
/* Fixed report descriptor for (tweaked) v2 frame reports */
|
||||
extern const __u8 uclogic_rdesc_v2_frame_arr[];
|
||||
extern const size_t uclogic_rdesc_v2_frame_size;
|
||||
|
||||
/* Fixed report descriptor for Ugee EX07 buttonpad */
|
||||
extern const __u8 uclogic_rdesc_ugee_ex07_buttonpad_arr[];
|
||||
extern const size_t uclogic_rdesc_ugee_ex07_buttonpad_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;
|
||||
|
||||
/* Fixed report descriptor for XP-Pen Deco 01 frame controls */
|
||||
extern const __u8 uclogic_rdesc_xppen_deco01_frame_arr[];
|
||||
|
@ -35,6 +35,7 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/hid.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#include "../hid-ids.h"
|
||||
#include "i2c-hid.h"
|
||||
@ -47,6 +48,15 @@
|
||||
#define I2C_HID_QUIRK_BAD_INPUT_SIZE BIT(6)
|
||||
#define I2C_HID_QUIRK_NO_WAKEUP_AFTER_RESET BIT(7)
|
||||
|
||||
/* Command opcodes */
|
||||
#define I2C_HID_OPCODE_RESET 0x01
|
||||
#define I2C_HID_OPCODE_GET_REPORT 0x02
|
||||
#define I2C_HID_OPCODE_SET_REPORT 0x03
|
||||
#define I2C_HID_OPCODE_GET_IDLE 0x04
|
||||
#define I2C_HID_OPCODE_SET_IDLE 0x05
|
||||
#define I2C_HID_OPCODE_GET_PROTOCOL 0x06
|
||||
#define I2C_HID_OPCODE_SET_PROTOCOL 0x07
|
||||
#define I2C_HID_OPCODE_SET_POWER 0x08
|
||||
|
||||
/* flags */
|
||||
#define I2C_HID_STARTED 0
|
||||
@ -84,60 +94,11 @@ struct i2c_hid_desc {
|
||||
__le32 reserved;
|
||||
} __packed;
|
||||
|
||||
struct i2c_hid_cmd {
|
||||
unsigned int registerIndex;
|
||||
__u8 opcode;
|
||||
unsigned int length;
|
||||
bool wait;
|
||||
};
|
||||
|
||||
union command {
|
||||
u8 data[0];
|
||||
struct cmd {
|
||||
__le16 reg;
|
||||
__u8 reportTypeID;
|
||||
__u8 opcode;
|
||||
} __packed c;
|
||||
};
|
||||
|
||||
#define I2C_HID_CMD(opcode_) \
|
||||
.opcode = opcode_, .length = 4, \
|
||||
.registerIndex = offsetof(struct i2c_hid_desc, wCommandRegister)
|
||||
|
||||
/* fetch HID descriptor */
|
||||
static const struct i2c_hid_cmd hid_descr_cmd = { .length = 2 };
|
||||
/* fetch report descriptors */
|
||||
static const struct i2c_hid_cmd hid_report_descr_cmd = {
|
||||
.registerIndex = offsetof(struct i2c_hid_desc,
|
||||
wReportDescRegister),
|
||||
.opcode = 0x00,
|
||||
.length = 2 };
|
||||
/* commands */
|
||||
static const struct i2c_hid_cmd hid_reset_cmd = { I2C_HID_CMD(0x01),
|
||||
.wait = true };
|
||||
static const struct i2c_hid_cmd hid_get_report_cmd = { I2C_HID_CMD(0x02) };
|
||||
static const struct i2c_hid_cmd hid_set_report_cmd = { I2C_HID_CMD(0x03) };
|
||||
static const struct i2c_hid_cmd hid_set_power_cmd = { I2C_HID_CMD(0x08) };
|
||||
static const struct i2c_hid_cmd hid_no_cmd = { .length = 0 };
|
||||
|
||||
/*
|
||||
* These definitions are not used here, but are defined by the spec.
|
||||
* Keeping them here for documentation purposes.
|
||||
*
|
||||
* static const struct i2c_hid_cmd hid_get_idle_cmd = { I2C_HID_CMD(0x04) };
|
||||
* static const struct i2c_hid_cmd hid_set_idle_cmd = { I2C_HID_CMD(0x05) };
|
||||
* static const struct i2c_hid_cmd hid_get_protocol_cmd = { I2C_HID_CMD(0x06) };
|
||||
* static const struct i2c_hid_cmd hid_set_protocol_cmd = { I2C_HID_CMD(0x07) };
|
||||
*/
|
||||
|
||||
/* The main device structure */
|
||||
struct i2c_hid {
|
||||
struct i2c_client *client; /* i2c client */
|
||||
struct hid_device *hid; /* pointer to corresponding HID dev */
|
||||
union {
|
||||
__u8 hdesc_buffer[sizeof(struct i2c_hid_desc)];
|
||||
struct i2c_hid_desc hdesc; /* the HID Descriptor */
|
||||
};
|
||||
struct i2c_hid_desc hdesc; /* the HID Descriptor */
|
||||
__le16 wHIDDescRegister; /* location of the i2c
|
||||
* register of the HID
|
||||
* descriptor. */
|
||||
@ -145,7 +106,6 @@ struct i2c_hid {
|
||||
u8 *inbuf; /* Input buffer */
|
||||
u8 *rawbuf; /* Raw Input buffer */
|
||||
u8 *cmdbuf; /* Command buffer */
|
||||
u8 *argsbuf; /* Command arguments buffer */
|
||||
|
||||
unsigned long flags; /* device flags */
|
||||
unsigned long quirks; /* Various quirks */
|
||||
@ -207,196 +167,228 @@ static u32 i2c_hid_lookup_quirk(const u16 idVendor, const u16 idProduct)
|
||||
return quirks;
|
||||
}
|
||||
|
||||
static int __i2c_hid_command(struct i2c_client *client,
|
||||
const struct i2c_hid_cmd *command, u8 reportID,
|
||||
u8 reportType, u8 *args, int args_len,
|
||||
unsigned char *buf_recv, int data_len)
|
||||
static int i2c_hid_xfer(struct i2c_hid *ihid,
|
||||
u8 *send_buf, int send_len, u8 *recv_buf, int recv_len)
|
||||
{
|
||||
struct i2c_hid *ihid = i2c_get_clientdata(client);
|
||||
union command *cmd = (union command *)ihid->cmdbuf;
|
||||
struct i2c_client *client = ihid->client;
|
||||
struct i2c_msg msgs[2] = { 0 };
|
||||
int n = 0;
|
||||
int ret;
|
||||
struct i2c_msg msg[2];
|
||||
int msg_num = 1;
|
||||
|
||||
int length = command->length;
|
||||
bool wait = command->wait;
|
||||
unsigned int registerIndex = command->registerIndex;
|
||||
if (send_len) {
|
||||
i2c_hid_dbg(ihid, "%s: cmd=%*ph\n",
|
||||
__func__, send_len, send_buf);
|
||||
|
||||
/* special case for hid_descr_cmd */
|
||||
if (command == &hid_descr_cmd) {
|
||||
cmd->c.reg = ihid->wHIDDescRegister;
|
||||
} else {
|
||||
cmd->data[0] = ihid->hdesc_buffer[registerIndex];
|
||||
cmd->data[1] = ihid->hdesc_buffer[registerIndex + 1];
|
||||
msgs[n].addr = client->addr;
|
||||
msgs[n].flags = (client->flags & I2C_M_TEN) | I2C_M_DMA_SAFE;
|
||||
msgs[n].len = send_len;
|
||||
msgs[n].buf = send_buf;
|
||||
n++;
|
||||
}
|
||||
|
||||
if (length > 2) {
|
||||
cmd->c.opcode = command->opcode;
|
||||
cmd->c.reportTypeID = reportID | reportType << 4;
|
||||
}
|
||||
if (recv_len) {
|
||||
msgs[n].addr = client->addr;
|
||||
msgs[n].flags = (client->flags & I2C_M_TEN) |
|
||||
I2C_M_RD | I2C_M_DMA_SAFE;
|
||||
msgs[n].len = recv_len;
|
||||
msgs[n].buf = recv_buf;
|
||||
n++;
|
||||
|
||||
memcpy(cmd->data + length, args, args_len);
|
||||
length += args_len;
|
||||
|
||||
i2c_hid_dbg(ihid, "%s: cmd=%*ph\n", __func__, length, cmd->data);
|
||||
|
||||
msg[0].addr = client->addr;
|
||||
msg[0].flags = client->flags & I2C_M_TEN;
|
||||
msg[0].len = length;
|
||||
msg[0].buf = cmd->data;
|
||||
if (data_len > 0) {
|
||||
msg[1].addr = client->addr;
|
||||
msg[1].flags = client->flags & I2C_M_TEN;
|
||||
msg[1].flags |= I2C_M_RD;
|
||||
msg[1].len = data_len;
|
||||
msg[1].buf = buf_recv;
|
||||
msg_num = 2;
|
||||
set_bit(I2C_HID_READ_PENDING, &ihid->flags);
|
||||
}
|
||||
|
||||
if (wait)
|
||||
set_bit(I2C_HID_RESET_PENDING, &ihid->flags);
|
||||
ret = i2c_transfer(client->adapter, msgs, n);
|
||||
|
||||
ret = i2c_transfer(client->adapter, msg, msg_num);
|
||||
|
||||
if (data_len > 0)
|
||||
if (recv_len)
|
||||
clear_bit(I2C_HID_READ_PENDING, &ihid->flags);
|
||||
|
||||
if (ret != msg_num)
|
||||
if (ret != n)
|
||||
return ret < 0 ? ret : -EIO;
|
||||
|
||||
ret = 0;
|
||||
|
||||
if (wait && (ihid->quirks & I2C_HID_QUIRK_NO_IRQ_AFTER_RESET)) {
|
||||
msleep(100);
|
||||
} else if (wait) {
|
||||
i2c_hid_dbg(ihid, "%s: waiting...\n", __func__);
|
||||
if (!wait_event_timeout(ihid->wait,
|
||||
!test_bit(I2C_HID_RESET_PENDING, &ihid->flags),
|
||||
msecs_to_jiffies(5000)))
|
||||
ret = -ENODATA;
|
||||
i2c_hid_dbg(ihid, "%s: finished.\n", __func__);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int i2c_hid_command(struct i2c_client *client,
|
||||
const struct i2c_hid_cmd *command,
|
||||
unsigned char *buf_recv, int data_len)
|
||||
{
|
||||
return __i2c_hid_command(client, command, 0, 0, NULL, 0,
|
||||
buf_recv, data_len);
|
||||
}
|
||||
|
||||
static int i2c_hid_get_report(struct i2c_client *client, u8 reportType,
|
||||
u8 reportID, unsigned char *buf_recv, int data_len)
|
||||
{
|
||||
struct i2c_hid *ihid = i2c_get_clientdata(client);
|
||||
u8 args[3];
|
||||
int ret;
|
||||
int args_len = 0;
|
||||
u16 readRegister = le16_to_cpu(ihid->hdesc.wDataRegister);
|
||||
|
||||
i2c_hid_dbg(ihid, "%s\n", __func__);
|
||||
|
||||
if (reportID >= 0x0F) {
|
||||
args[args_len++] = reportID;
|
||||
reportID = 0x0F;
|
||||
}
|
||||
|
||||
args[args_len++] = readRegister & 0xFF;
|
||||
args[args_len++] = readRegister >> 8;
|
||||
|
||||
ret = __i2c_hid_command(client, &hid_get_report_cmd, reportID,
|
||||
reportType, args, args_len, buf_recv, data_len);
|
||||
if (ret) {
|
||||
dev_err(&client->dev,
|
||||
"failed to retrieve report from device.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2c_hid_read_register(struct i2c_hid *ihid, __le16 reg,
|
||||
void *buf, size_t len)
|
||||
{
|
||||
*(__le16 *)ihid->cmdbuf = reg;
|
||||
|
||||
return i2c_hid_xfer(ihid, ihid->cmdbuf, sizeof(__le16), buf, len);
|
||||
}
|
||||
|
||||
static size_t i2c_hid_encode_command(u8 *buf, u8 opcode,
|
||||
int report_type, int report_id)
|
||||
{
|
||||
size_t length = 0;
|
||||
|
||||
if (report_id < 0x0F) {
|
||||
buf[length++] = report_type << 4 | report_id;
|
||||
buf[length++] = opcode;
|
||||
} else {
|
||||
buf[length++] = report_type << 4 | 0x0F;
|
||||
buf[length++] = opcode;
|
||||
buf[length++] = report_id;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
static int i2c_hid_get_report(struct i2c_hid *ihid,
|
||||
u8 report_type, u8 report_id,
|
||||
u8 *recv_buf, size_t recv_len)
|
||||
{
|
||||
size_t length = 0;
|
||||
size_t ret_count;
|
||||
int error;
|
||||
|
||||
i2c_hid_dbg(ihid, "%s\n", __func__);
|
||||
|
||||
/* Command register goes first */
|
||||
*(__le16 *)ihid->cmdbuf = ihid->hdesc.wCommandRegister;
|
||||
length += sizeof(__le16);
|
||||
/* Next is GET_REPORT command */
|
||||
length += i2c_hid_encode_command(ihid->cmdbuf + length,
|
||||
I2C_HID_OPCODE_GET_REPORT,
|
||||
report_type, report_id);
|
||||
/*
|
||||
* Device will send report data through data register. Because
|
||||
* command can be either 2 or 3 bytes destination for the data
|
||||
* register may be not aligned.
|
||||
*/
|
||||
put_unaligned_le16(le16_to_cpu(ihid->hdesc.wDataRegister),
|
||||
ihid->cmdbuf + length);
|
||||
length += sizeof(__le16);
|
||||
|
||||
/*
|
||||
* In addition to report data device will supply data length
|
||||
* in the first 2 bytes of the response, so adjust .
|
||||
*/
|
||||
error = i2c_hid_xfer(ihid, ihid->cmdbuf, length,
|
||||
ihid->rawbuf, recv_len + sizeof(__le16));
|
||||
if (error) {
|
||||
dev_err(&ihid->client->dev,
|
||||
"failed to set a report to device: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
/* The buffer is sufficiently aligned */
|
||||
ret_count = le16_to_cpup((__le16 *)ihid->rawbuf);
|
||||
|
||||
/* Check for empty report response */
|
||||
if (ret_count <= sizeof(__le16))
|
||||
return 0;
|
||||
|
||||
recv_len = min(recv_len, ret_count - sizeof(__le16));
|
||||
memcpy(recv_buf, ihid->rawbuf + sizeof(__le16), recv_len);
|
||||
|
||||
if (report_id && recv_len != 0 && recv_buf[0] != report_id) {
|
||||
dev_err(&ihid->client->dev,
|
||||
"device returned incorrect report (%d vs %d expected)\n",
|
||||
recv_buf[0], report_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return recv_len;
|
||||
}
|
||||
|
||||
static size_t i2c_hid_format_report(u8 *buf, int report_id,
|
||||
const u8 *data, size_t size)
|
||||
{
|
||||
size_t length = sizeof(__le16); /* reserve space to store size */
|
||||
|
||||
if (report_id)
|
||||
buf[length++] = report_id;
|
||||
|
||||
memcpy(buf + length, data, size);
|
||||
length += size;
|
||||
|
||||
/* Store overall size in the beginning of the buffer */
|
||||
put_unaligned_le16(length, buf);
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
/**
|
||||
* i2c_hid_set_or_send_report: forward an incoming report to the device
|
||||
* @client: the i2c_client of the device
|
||||
* @reportType: 0x03 for HID_FEATURE_REPORT ; 0x02 for HID_OUTPUT_REPORT
|
||||
* @reportID: the report ID
|
||||
* @ihid: the i2c hid device
|
||||
* @report_type: 0x03 for HID_FEATURE_REPORT ; 0x02 for HID_OUTPUT_REPORT
|
||||
* @report_id: the report ID
|
||||
* @buf: the actual data to transfer, without the report ID
|
||||
* @data_len: size of buf
|
||||
* @use_data: true: use SET_REPORT HID command, false: send plain OUTPUT report
|
||||
* @do_set: true: use SET_REPORT HID command, false: send plain OUTPUT report
|
||||
*/
|
||||
static int i2c_hid_set_or_send_report(struct i2c_client *client, u8 reportType,
|
||||
u8 reportID, unsigned char *buf, size_t data_len, bool use_data)
|
||||
static int i2c_hid_set_or_send_report(struct i2c_hid *ihid,
|
||||
u8 report_type, u8 report_id,
|
||||
const u8 *buf, size_t data_len,
|
||||
bool do_set)
|
||||
{
|
||||
struct i2c_hid *ihid = i2c_get_clientdata(client);
|
||||
u8 *args = ihid->argsbuf;
|
||||
const struct i2c_hid_cmd *hidcmd;
|
||||
int ret;
|
||||
u16 dataRegister = le16_to_cpu(ihid->hdesc.wDataRegister);
|
||||
u16 outputRegister = le16_to_cpu(ihid->hdesc.wOutputRegister);
|
||||
u16 maxOutputLength = le16_to_cpu(ihid->hdesc.wMaxOutputLength);
|
||||
u16 size;
|
||||
int args_len;
|
||||
int index = 0;
|
||||
size_t length = 0;
|
||||
int error;
|
||||
|
||||
i2c_hid_dbg(ihid, "%s\n", __func__);
|
||||
|
||||
if (data_len > ihid->bufsize)
|
||||
return -EINVAL;
|
||||
|
||||
size = 2 /* size */ +
|
||||
(reportID ? 1 : 0) /* reportID */ +
|
||||
data_len /* buf */;
|
||||
args_len = (reportID >= 0x0F ? 1 : 0) /* optional third byte */ +
|
||||
2 /* dataRegister */ +
|
||||
size /* args */;
|
||||
|
||||
if (!use_data && maxOutputLength == 0)
|
||||
if (!do_set && le16_to_cpu(ihid->hdesc.wMaxOutputLength) == 0)
|
||||
return -ENOSYS;
|
||||
|
||||
if (reportID >= 0x0F) {
|
||||
args[index++] = reportID;
|
||||
reportID = 0x0F;
|
||||
}
|
||||
|
||||
/*
|
||||
* use the data register for feature reports or if the device does not
|
||||
* support the output register
|
||||
*/
|
||||
if (use_data) {
|
||||
args[index++] = dataRegister & 0xFF;
|
||||
args[index++] = dataRegister >> 8;
|
||||
hidcmd = &hid_set_report_cmd;
|
||||
if (do_set) {
|
||||
/* Command register goes first */
|
||||
*(__le16 *)ihid->cmdbuf = ihid->hdesc.wCommandRegister;
|
||||
length += sizeof(__le16);
|
||||
/* Next is SET_REPORT command */
|
||||
length += i2c_hid_encode_command(ihid->cmdbuf + length,
|
||||
I2C_HID_OPCODE_SET_REPORT,
|
||||
report_type, report_id);
|
||||
/*
|
||||
* Report data will go into the data register. Because
|
||||
* command can be either 2 or 3 bytes destination for
|
||||
* the data register may be not aligned.
|
||||
*/
|
||||
put_unaligned_le16(le16_to_cpu(ihid->hdesc.wDataRegister),
|
||||
ihid->cmdbuf + length);
|
||||
length += sizeof(__le16);
|
||||
} else {
|
||||
args[index++] = outputRegister & 0xFF;
|
||||
args[index++] = outputRegister >> 8;
|
||||
hidcmd = &hid_no_cmd;
|
||||
/*
|
||||
* With simple "send report" all data goes into the output
|
||||
* register.
|
||||
*/
|
||||
*(__le16 *)ihid->cmdbuf = ihid->hdesc.wOutputRegister;
|
||||
length += sizeof(__le16);
|
||||
}
|
||||
|
||||
args[index++] = size & 0xFF;
|
||||
args[index++] = size >> 8;
|
||||
length += i2c_hid_format_report(ihid->cmdbuf + length,
|
||||
report_id, buf, data_len);
|
||||
|
||||
if (reportID)
|
||||
args[index++] = reportID;
|
||||
|
||||
memcpy(&args[index], buf, data_len);
|
||||
|
||||
ret = __i2c_hid_command(client, hidcmd, reportID,
|
||||
reportType, args, args_len, NULL, 0);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "failed to set a report to device.\n");
|
||||
return ret;
|
||||
error = i2c_hid_xfer(ihid, ihid->cmdbuf, length, NULL, 0);
|
||||
if (error) {
|
||||
dev_err(&ihid->client->dev,
|
||||
"failed to set a report to device: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
return data_len;
|
||||
}
|
||||
|
||||
static int i2c_hid_set_power(struct i2c_client *client, int power_state)
|
||||
static int i2c_hid_set_power_command(struct i2c_hid *ihid, int power_state)
|
||||
{
|
||||
size_t length;
|
||||
|
||||
/* SET_POWER uses command register */
|
||||
*(__le16 *)ihid->cmdbuf = ihid->hdesc.wCommandRegister;
|
||||
length = sizeof(__le16);
|
||||
|
||||
/* Now the command itself */
|
||||
length += i2c_hid_encode_command(ihid->cmdbuf + length,
|
||||
I2C_HID_OPCODE_SET_POWER,
|
||||
0, power_state);
|
||||
|
||||
return i2c_hid_xfer(ihid, ihid->cmdbuf, length, NULL, 0);
|
||||
}
|
||||
|
||||
static int i2c_hid_set_power(struct i2c_hid *ihid, int power_state)
|
||||
{
|
||||
struct i2c_hid *ihid = i2c_get_clientdata(client);
|
||||
int ret;
|
||||
|
||||
i2c_hid_dbg(ihid, "%s\n", __func__);
|
||||
@ -408,18 +400,17 @@ static int i2c_hid_set_power(struct i2c_client *client, int power_state)
|
||||
*/
|
||||
if (power_state == I2C_HID_PWR_ON &&
|
||||
ihid->quirks & I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV) {
|
||||
ret = i2c_hid_command(client, &hid_set_power_cmd, NULL, 0);
|
||||
ret = i2c_hid_set_power_command(ihid, I2C_HID_PWR_ON);
|
||||
|
||||
/* Device was already activated */
|
||||
if (!ret)
|
||||
goto set_pwr_exit;
|
||||
}
|
||||
|
||||
ret = __i2c_hid_command(client, &hid_set_power_cmd, power_state,
|
||||
0, NULL, 0, NULL, 0);
|
||||
|
||||
ret = i2c_hid_set_power_command(ihid, power_state);
|
||||
if (ret)
|
||||
dev_err(&client->dev, "failed to change power setting.\n");
|
||||
dev_err(&ihid->client->dev,
|
||||
"failed to change power setting.\n");
|
||||
|
||||
set_pwr_exit:
|
||||
|
||||
@ -438,9 +429,49 @@ set_pwr_exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int i2c_hid_hwreset(struct i2c_client *client)
|
||||
static int i2c_hid_execute_reset(struct i2c_hid *ihid)
|
||||
{
|
||||
size_t length = 0;
|
||||
int ret;
|
||||
|
||||
i2c_hid_dbg(ihid, "resetting...\n");
|
||||
|
||||
/* Prepare reset command. Command register goes first. */
|
||||
*(__le16 *)ihid->cmdbuf = ihid->hdesc.wCommandRegister;
|
||||
length += sizeof(__le16);
|
||||
/* Next is RESET command itself */
|
||||
length += i2c_hid_encode_command(ihid->cmdbuf + length,
|
||||
I2C_HID_OPCODE_RESET, 0, 0);
|
||||
|
||||
set_bit(I2C_HID_RESET_PENDING, &ihid->flags);
|
||||
|
||||
ret = i2c_hid_xfer(ihid, ihid->cmdbuf, length, NULL, 0);
|
||||
if (ret) {
|
||||
dev_err(&ihid->client->dev, "failed to reset device.\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ihid->quirks & I2C_HID_QUIRK_NO_IRQ_AFTER_RESET) {
|
||||
msleep(100);
|
||||
goto out;
|
||||
}
|
||||
|
||||
i2c_hid_dbg(ihid, "%s: waiting...\n", __func__);
|
||||
if (!wait_event_timeout(ihid->wait,
|
||||
!test_bit(I2C_HID_RESET_PENDING, &ihid->flags),
|
||||
msecs_to_jiffies(5000))) {
|
||||
ret = -ENODATA;
|
||||
goto out;
|
||||
}
|
||||
i2c_hid_dbg(ihid, "%s: finished.\n", __func__);
|
||||
|
||||
out:
|
||||
clear_bit(I2C_HID_RESET_PENDING, &ihid->flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int i2c_hid_hwreset(struct i2c_hid *ihid)
|
||||
{
|
||||
struct i2c_hid *ihid = i2c_get_clientdata(client);
|
||||
int ret;
|
||||
|
||||
i2c_hid_dbg(ihid, "%s\n", __func__);
|
||||
@ -452,22 +483,21 @@ static int i2c_hid_hwreset(struct i2c_client *client)
|
||||
*/
|
||||
mutex_lock(&ihid->reset_lock);
|
||||
|
||||
ret = i2c_hid_set_power(client, I2C_HID_PWR_ON);
|
||||
ret = i2c_hid_set_power(ihid, I2C_HID_PWR_ON);
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
|
||||
i2c_hid_dbg(ihid, "resetting...\n");
|
||||
|
||||
ret = i2c_hid_command(client, &hid_reset_cmd, NULL, 0);
|
||||
ret = i2c_hid_execute_reset(ihid);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "failed to reset device.\n");
|
||||
i2c_hid_set_power(client, I2C_HID_PWR_SLEEP);
|
||||
dev_err(&ihid->client->dev,
|
||||
"failed to reset device: %d\n", ret);
|
||||
i2c_hid_set_power(ihid, I2C_HID_PWR_SLEEP);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
/* At least some SIS devices need this after reset */
|
||||
if (!(ihid->quirks & I2C_HID_QUIRK_NO_WAKEUP_AFTER_RESET))
|
||||
ret = i2c_hid_set_power(client, I2C_HID_PWR_ON);
|
||||
ret = i2c_hid_set_power(ihid, I2C_HID_PWR_ON);
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&ihid->reset_lock);
|
||||
@ -476,9 +506,9 @@ out_unlock:
|
||||
|
||||
static void i2c_hid_get_input(struct i2c_hid *ihid)
|
||||
{
|
||||
u16 size = le16_to_cpu(ihid->hdesc.wMaxInputLength);
|
||||
u16 ret_size;
|
||||
int ret;
|
||||
u32 ret_size;
|
||||
int size = le16_to_cpu(ihid->hdesc.wMaxInputLength);
|
||||
|
||||
if (size > ihid->bufsize)
|
||||
size = ihid->bufsize;
|
||||
@ -493,8 +523,8 @@ static void i2c_hid_get_input(struct i2c_hid *ihid)
|
||||
return;
|
||||
}
|
||||
|
||||
ret_size = ihid->inbuf[0] | ihid->inbuf[1] << 8;
|
||||
|
||||
/* Receiving buffer is properly aligned */
|
||||
ret_size = le16_to_cpup((__le16 *)ihid->inbuf);
|
||||
if (!ret_size) {
|
||||
/* host or device initiated RESET completed */
|
||||
if (test_and_clear_bit(I2C_HID_RESET_PENDING, &ihid->flags))
|
||||
@ -502,19 +532,20 @@ static void i2c_hid_get_input(struct i2c_hid *ihid)
|
||||
return;
|
||||
}
|
||||
|
||||
if (ihid->quirks & I2C_HID_QUIRK_BOGUS_IRQ && ret_size == 0xffff) {
|
||||
dev_warn_once(&ihid->client->dev, "%s: IRQ triggered but "
|
||||
"there's no data\n", __func__);
|
||||
if ((ihid->quirks & I2C_HID_QUIRK_BOGUS_IRQ) && ret_size == 0xffff) {
|
||||
dev_warn_once(&ihid->client->dev,
|
||||
"%s: IRQ triggered but there's no data\n",
|
||||
__func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((ret_size > size) || (ret_size < 2)) {
|
||||
if (ret_size > size || ret_size < sizeof(__le16)) {
|
||||
if (ihid->quirks & I2C_HID_QUIRK_BAD_INPUT_SIZE) {
|
||||
ihid->inbuf[0] = size & 0xff;
|
||||
ihid->inbuf[1] = size >> 8;
|
||||
*(__le16 *)ihid->inbuf = cpu_to_le16(size);
|
||||
ret_size = size;
|
||||
} else {
|
||||
dev_err(&ihid->client->dev, "%s: incomplete report (%d/%d)\n",
|
||||
dev_err(&ihid->client->dev,
|
||||
"%s: incomplete report (%d/%d)\n",
|
||||
__func__, size, ret_size);
|
||||
return;
|
||||
}
|
||||
@ -525,8 +556,9 @@ static void i2c_hid_get_input(struct i2c_hid *ihid)
|
||||
if (test_bit(I2C_HID_STARTED, &ihid->flags)) {
|
||||
pm_wakeup_event(&ihid->client->dev, 0);
|
||||
|
||||
hid_input_report(ihid->hid, HID_INPUT_REPORT, ihid->inbuf + 2,
|
||||
ret_size - 2, 1);
|
||||
hid_input_report(ihid->hid, HID_INPUT_REPORT,
|
||||
ihid->inbuf + sizeof(__le16),
|
||||
ret_size - sizeof(__le16), 1);
|
||||
}
|
||||
|
||||
return;
|
||||
@ -572,31 +604,33 @@ static void i2c_hid_free_buffers(struct i2c_hid *ihid)
|
||||
{
|
||||
kfree(ihid->inbuf);
|
||||
kfree(ihid->rawbuf);
|
||||
kfree(ihid->argsbuf);
|
||||
kfree(ihid->cmdbuf);
|
||||
ihid->inbuf = NULL;
|
||||
ihid->rawbuf = NULL;
|
||||
ihid->cmdbuf = NULL;
|
||||
ihid->argsbuf = NULL;
|
||||
ihid->bufsize = 0;
|
||||
}
|
||||
|
||||
static int i2c_hid_alloc_buffers(struct i2c_hid *ihid, size_t report_size)
|
||||
{
|
||||
/* the worst case is computed from the set_report command with a
|
||||
* reportID > 15 and the maximum report length */
|
||||
int args_len = sizeof(__u8) + /* ReportID */
|
||||
sizeof(__u8) + /* optional ReportID byte */
|
||||
sizeof(__u16) + /* data register */
|
||||
sizeof(__u16) + /* size of the report */
|
||||
report_size; /* report */
|
||||
/*
|
||||
* The worst case is computed from the set_report command with a
|
||||
* reportID > 15 and the maximum report length.
|
||||
*/
|
||||
int cmd_len = sizeof(__le16) + /* command register */
|
||||
sizeof(u8) + /* encoded report type/ID */
|
||||
sizeof(u8) + /* opcode */
|
||||
sizeof(u8) + /* optional 3rd byte report ID */
|
||||
sizeof(__le16) + /* data register */
|
||||
sizeof(__le16) + /* report data size */
|
||||
sizeof(u8) + /* report ID if numbered report */
|
||||
report_size;
|
||||
|
||||
ihid->inbuf = kzalloc(report_size, GFP_KERNEL);
|
||||
ihid->rawbuf = kzalloc(report_size, GFP_KERNEL);
|
||||
ihid->argsbuf = kzalloc(args_len, GFP_KERNEL);
|
||||
ihid->cmdbuf = kzalloc(sizeof(union command) + args_len, GFP_KERNEL);
|
||||
ihid->cmdbuf = kzalloc(cmd_len, GFP_KERNEL);
|
||||
|
||||
if (!ihid->inbuf || !ihid->rawbuf || !ihid->argsbuf || !ihid->cmdbuf) {
|
||||
if (!ihid->inbuf || !ihid->rawbuf || !ihid->cmdbuf) {
|
||||
i2c_hid_free_buffers(ihid);
|
||||
return -ENOMEM;
|
||||
}
|
||||
@ -607,43 +641,39 @@ static int i2c_hid_alloc_buffers(struct i2c_hid *ihid, size_t report_size)
|
||||
}
|
||||
|
||||
static int i2c_hid_get_raw_report(struct hid_device *hid,
|
||||
unsigned char report_number, __u8 *buf, size_t count,
|
||||
unsigned char report_type)
|
||||
u8 report_type, u8 report_id,
|
||||
u8 *buf, size_t count)
|
||||
{
|
||||
struct i2c_client *client = hid->driver_data;
|
||||
struct i2c_hid *ihid = i2c_get_clientdata(client);
|
||||
size_t ret_count, ask_count;
|
||||
int ret;
|
||||
int ret_count;
|
||||
|
||||
if (report_type == HID_OUTPUT_REPORT)
|
||||
return -EINVAL;
|
||||
|
||||
/* +2 bytes to include the size of the reply in the query buffer */
|
||||
ask_count = min(count + 2, (size_t)ihid->bufsize);
|
||||
/*
|
||||
* In case of unnumbered reports the response from the device will
|
||||
* not have the report ID that the upper layers expect, so we need
|
||||
* to stash it the buffer ourselves and adjust the data size.
|
||||
*/
|
||||
if (!report_id) {
|
||||
buf[0] = 0;
|
||||
buf++;
|
||||
count--;
|
||||
}
|
||||
|
||||
ret = i2c_hid_get_report(client,
|
||||
ret_count = i2c_hid_get_report(ihid,
|
||||
report_type == HID_FEATURE_REPORT ? 0x03 : 0x01,
|
||||
report_number, ihid->rawbuf, ask_count);
|
||||
report_id, buf, count);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret_count > 0 && !report_id)
|
||||
ret_count++;
|
||||
|
||||
ret_count = ihid->rawbuf[0] | (ihid->rawbuf[1] << 8);
|
||||
|
||||
if (ret_count <= 2)
|
||||
return 0;
|
||||
|
||||
ret_count = min(ret_count, ask_count);
|
||||
|
||||
/* The query buffer contains the size, dropping it in the reply */
|
||||
count = min(count, ret_count - 2);
|
||||
memcpy(buf, ihid->rawbuf + 2, count);
|
||||
|
||||
return count;
|
||||
return ret_count;
|
||||
}
|
||||
|
||||
static int i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf,
|
||||
size_t count, unsigned char report_type, bool use_data)
|
||||
static int i2c_hid_output_raw_report(struct hid_device *hid, u8 report_type,
|
||||
const u8 *buf, size_t count, bool do_set)
|
||||
{
|
||||
struct i2c_client *client = hid->driver_data;
|
||||
struct i2c_hid *ihid = i2c_get_clientdata(client);
|
||||
@ -655,28 +685,29 @@ static int i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf,
|
||||
|
||||
mutex_lock(&ihid->reset_lock);
|
||||
|
||||
if (report_id) {
|
||||
buf++;
|
||||
count--;
|
||||
}
|
||||
|
||||
ret = i2c_hid_set_or_send_report(client,
|
||||
/*
|
||||
* Note that both numbered and unnumbered reports passed here
|
||||
* are supposed to have report ID stored in the 1st byte of the
|
||||
* buffer, so we strip it off unconditionally before passing payload
|
||||
* to i2c_hid_set_or_send_report which takes care of encoding
|
||||
* everything properly.
|
||||
*/
|
||||
ret = i2c_hid_set_or_send_report(ihid,
|
||||
report_type == HID_FEATURE_REPORT ? 0x03 : 0x02,
|
||||
report_id, buf, count, use_data);
|
||||
report_id, buf + 1, count - 1, do_set);
|
||||
|
||||
if (report_id && ret >= 0)
|
||||
ret++; /* add report_id to the number of transfered bytes */
|
||||
if (ret >= 0)
|
||||
ret++; /* add report_id to the number of transferred bytes */
|
||||
|
||||
mutex_unlock(&ihid->reset_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int i2c_hid_output_report(struct hid_device *hid, __u8 *buf,
|
||||
size_t count)
|
||||
static int i2c_hid_output_report(struct hid_device *hid, u8 *buf, size_t count)
|
||||
{
|
||||
return i2c_hid_output_raw_report(hid, buf, count, HID_OUTPUT_REPORT,
|
||||
false);
|
||||
return i2c_hid_output_raw_report(hid, HID_OUTPUT_REPORT, buf, count,
|
||||
false);
|
||||
}
|
||||
|
||||
static int i2c_hid_raw_request(struct hid_device *hid, unsigned char reportnum,
|
||||
@ -685,11 +716,11 @@ static int i2c_hid_raw_request(struct hid_device *hid, unsigned char reportnum,
|
||||
{
|
||||
switch (reqtype) {
|
||||
case HID_REQ_GET_REPORT:
|
||||
return i2c_hid_get_raw_report(hid, reportnum, buf, len, rtype);
|
||||
return i2c_hid_get_raw_report(hid, rtype, reportnum, buf, len);
|
||||
case HID_REQ_SET_REPORT:
|
||||
if (buf[0] != reportnum)
|
||||
return -EINVAL;
|
||||
return i2c_hid_output_raw_report(hid, buf, len, rtype, true);
|
||||
return i2c_hid_output_raw_report(hid, rtype, buf, len, true);
|
||||
default:
|
||||
return -EIO;
|
||||
}
|
||||
@ -715,7 +746,7 @@ static int i2c_hid_parse(struct hid_device *hid)
|
||||
}
|
||||
|
||||
do {
|
||||
ret = i2c_hid_hwreset(client);
|
||||
ret = i2c_hid_hwreset(ihid);
|
||||
if (ret)
|
||||
msleep(1000);
|
||||
} while (tries-- > 0 && ret);
|
||||
@ -739,8 +770,9 @@ static int i2c_hid_parse(struct hid_device *hid)
|
||||
|
||||
i2c_hid_dbg(ihid, "asking HID report descriptor\n");
|
||||
|
||||
ret = i2c_hid_command(client, &hid_report_descr_cmd,
|
||||
rdesc, rsize);
|
||||
ret = i2c_hid_read_register(ihid,
|
||||
ihid->hdesc.wReportDescRegister,
|
||||
rdesc, rsize);
|
||||
if (ret) {
|
||||
hid_err(hid, "reading report descriptor failed\n");
|
||||
kfree(rdesc);
|
||||
@ -850,7 +882,7 @@ static int i2c_hid_fetch_hid_descriptor(struct i2c_hid *ihid)
|
||||
struct i2c_client *client = ihid->client;
|
||||
struct i2c_hid_desc *hdesc = &ihid->hdesc;
|
||||
unsigned int dsize;
|
||||
int ret;
|
||||
int error;
|
||||
|
||||
/* i2c hid fetch using a fixed descriptor size (30 bytes) */
|
||||
if (i2c_hid_get_dmi_i2c_hid_desc_override(client->name)) {
|
||||
@ -859,11 +891,14 @@ static int i2c_hid_fetch_hid_descriptor(struct i2c_hid *ihid)
|
||||
*i2c_hid_get_dmi_i2c_hid_desc_override(client->name);
|
||||
} else {
|
||||
i2c_hid_dbg(ihid, "Fetching the HID descriptor\n");
|
||||
ret = i2c_hid_command(client, &hid_descr_cmd,
|
||||
ihid->hdesc_buffer,
|
||||
sizeof(struct i2c_hid_desc));
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "hid_descr_cmd failed\n");
|
||||
error = i2c_hid_read_register(ihid,
|
||||
ihid->wHIDDescRegister,
|
||||
&ihid->hdesc,
|
||||
sizeof(ihid->hdesc));
|
||||
if (error) {
|
||||
dev_err(&ihid->client->dev,
|
||||
"failed to fetch HID descriptor: %d\n",
|
||||
error);
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
@ -873,7 +908,7 @@ static int i2c_hid_fetch_hid_descriptor(struct i2c_hid *ihid)
|
||||
* bytes 2-3 -> bcdVersion (has to be 1.00) */
|
||||
/* check bcdVersion == 1.0 */
|
||||
if (le16_to_cpu(hdesc->bcdVersion) != 0x0100) {
|
||||
dev_err(&client->dev,
|
||||
dev_err(&ihid->client->dev,
|
||||
"unexpected HID descriptor bcdVersion (0x%04hx)\n",
|
||||
le16_to_cpu(hdesc->bcdVersion));
|
||||
return -ENODEV;
|
||||
@ -882,11 +917,11 @@ static int i2c_hid_fetch_hid_descriptor(struct i2c_hid *ihid)
|
||||
/* Descriptor length should be 30 bytes as per the specification */
|
||||
dsize = le16_to_cpu(hdesc->wHIDDescLength);
|
||||
if (dsize != sizeof(struct i2c_hid_desc)) {
|
||||
dev_err(&client->dev, "weird size of HID descriptor (%u)\n",
|
||||
dsize);
|
||||
dev_err(&ihid->client->dev,
|
||||
"weird size of HID descriptor (%u)\n", dsize);
|
||||
return -ENODEV;
|
||||
}
|
||||
i2c_hid_dbg(ihid, "HID Descriptor: %*ph\n", dsize, ihid->hdesc_buffer);
|
||||
i2c_hid_dbg(ihid, "HID Descriptor: %*ph\n", dsize, &ihid->hdesc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1052,7 +1087,7 @@ void i2c_hid_core_shutdown(struct i2c_client *client)
|
||||
{
|
||||
struct i2c_hid *ihid = i2c_get_clientdata(client);
|
||||
|
||||
i2c_hid_set_power(client, I2C_HID_PWR_SLEEP);
|
||||
i2c_hid_set_power(ihid, I2C_HID_PWR_SLEEP);
|
||||
free_irq(client->irq, ihid);
|
||||
|
||||
i2c_hid_core_shutdown_tail(ihid);
|
||||
@ -1073,7 +1108,7 @@ static int i2c_hid_core_suspend(struct device *dev)
|
||||
return ret;
|
||||
|
||||
/* Save some power */
|
||||
i2c_hid_set_power(client, I2C_HID_PWR_SLEEP);
|
||||
i2c_hid_set_power(ihid, I2C_HID_PWR_SLEEP);
|
||||
|
||||
disable_irq(client->irq);
|
||||
|
||||
@ -1121,9 +1156,9 @@ static int i2c_hid_core_resume(struct device *dev)
|
||||
* let's still reset them here.
|
||||
*/
|
||||
if (ihid->quirks & I2C_HID_QUIRK_RESET_ON_RESUME)
|
||||
ret = i2c_hid_hwreset(client);
|
||||
ret = i2c_hid_hwreset(ihid);
|
||||
else
|
||||
ret = i2c_hid_set_power(client, I2C_HID_PWR_ON);
|
||||
ret = i2c_hid_set_power(ihid, I2C_HID_PWR_ON);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -661,21 +661,12 @@ static int ish_fw_xfer_direct_dma(struct ishtp_cl_data *client_data,
|
||||
*/
|
||||
payload_max_size &= ~(L1_CACHE_BYTES - 1);
|
||||
|
||||
dma_buf = kmalloc(payload_max_size, GFP_KERNEL | GFP_DMA32);
|
||||
dma_buf = dma_alloc_coherent(devc, payload_max_size, &dma_buf_phy, GFP_KERNEL);
|
||||
if (!dma_buf) {
|
||||
client_data->flag_retry = true;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dma_buf_phy = dma_map_single(devc, dma_buf, payload_max_size,
|
||||
DMA_TO_DEVICE);
|
||||
if (dma_mapping_error(devc, dma_buf_phy)) {
|
||||
dev_err(cl_data_to_dev(client_data), "DMA map failed\n");
|
||||
client_data->flag_retry = true;
|
||||
rv = -ENOMEM;
|
||||
goto end_err_dma_buf_release;
|
||||
}
|
||||
|
||||
ldr_xfer_dma_frag.fragment.hdr.command = LOADER_CMD_XFER_FRAGMENT;
|
||||
ldr_xfer_dma_frag.fragment.xfer_mode = LOADER_XFER_MODE_DIRECT_DMA;
|
||||
ldr_xfer_dma_frag.ddr_phys_addr = (u64)dma_buf_phy;
|
||||
@ -695,14 +686,7 @@ static int ish_fw_xfer_direct_dma(struct ishtp_cl_data *client_data,
|
||||
ldr_xfer_dma_frag.fragment.size = fragment_size;
|
||||
memcpy(dma_buf, &fw->data[fragment_offset], fragment_size);
|
||||
|
||||
dma_sync_single_for_device(devc, dma_buf_phy,
|
||||
payload_max_size,
|
||||
DMA_TO_DEVICE);
|
||||
|
||||
/*
|
||||
* Flush cache here because the dma_sync_single_for_device()
|
||||
* does not do for x86.
|
||||
*/
|
||||
/* Flush cache to be sure the data is in main memory. */
|
||||
clflush_cache_range(dma_buf, payload_max_size);
|
||||
|
||||
dev_dbg(cl_data_to_dev(client_data),
|
||||
@ -725,15 +709,8 @@ static int ish_fw_xfer_direct_dma(struct ishtp_cl_data *client_data,
|
||||
fragment_offset += fragment_size;
|
||||
}
|
||||
|
||||
dma_unmap_single(devc, dma_buf_phy, payload_max_size, DMA_TO_DEVICE);
|
||||
kfree(dma_buf);
|
||||
return 0;
|
||||
|
||||
end_err_resp_buf_release:
|
||||
/* Free ISH buffer if not done already, in error case */
|
||||
dma_unmap_single(devc, dma_buf_phy, payload_max_size, DMA_TO_DEVICE);
|
||||
end_err_dma_buf_release:
|
||||
kfree(dma_buf);
|
||||
dma_free_coherent(devc, payload_max_size, dma_buf, dma_buf_phy);
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
@ -342,12 +342,12 @@ struct hid_item {
|
||||
* HID device quirks.
|
||||
*/
|
||||
|
||||
/*
|
||||
/*
|
||||
* Increase this if you need to configure more HID quirks at module load time
|
||||
*/
|
||||
#define MAX_USBHID_BOOT_QUIRKS 4
|
||||
|
||||
#define HID_QUIRK_INVERT BIT(0)
|
||||
/* BIT(0) reserved for backward compatibility, was HID_QUIRK_INVERT */
|
||||
#define HID_QUIRK_NOTOUCH BIT(1)
|
||||
#define HID_QUIRK_IGNORE BIT(2)
|
||||
#define HID_QUIRK_NOGET BIT(3)
|
||||
@ -476,31 +476,50 @@ struct hid_field {
|
||||
unsigned report_count; /* number of this field in the report */
|
||||
unsigned report_type; /* (input,output,feature) */
|
||||
__s32 *value; /* last known value(s) */
|
||||
__s32 *new_value; /* newly read value(s) */
|
||||
__s32 *usages_priorities; /* priority of each usage when reading the report
|
||||
* bits 8-16 are reserved for hid-input usage
|
||||
*/
|
||||
__s32 logical_minimum;
|
||||
__s32 logical_maximum;
|
||||
__s32 physical_minimum;
|
||||
__s32 physical_maximum;
|
||||
__s32 unit_exponent;
|
||||
unsigned unit;
|
||||
bool ignored; /* this field is ignored in this event */
|
||||
struct hid_report *report; /* associated report */
|
||||
unsigned index; /* index into report->field[] */
|
||||
/* hidinput data */
|
||||
struct hid_input *hidinput; /* associated input structure */
|
||||
__u16 dpad; /* dpad input code */
|
||||
unsigned int slot_idx; /* slot index in a report */
|
||||
};
|
||||
|
||||
#define HID_MAX_FIELDS 256
|
||||
|
||||
struct hid_field_entry {
|
||||
struct list_head list;
|
||||
struct hid_field *field;
|
||||
unsigned int index;
|
||||
__s32 priority;
|
||||
};
|
||||
|
||||
struct hid_report {
|
||||
struct list_head list;
|
||||
struct list_head hidinput_list;
|
||||
struct list_head field_entry_list; /* ordered list of input fields */
|
||||
unsigned int id; /* id of this report */
|
||||
unsigned int type; /* report type */
|
||||
unsigned int application; /* application usage for this report */
|
||||
struct hid_field *field[HID_MAX_FIELDS]; /* fields of the report */
|
||||
struct hid_field_entry *field_entries; /* allocated memory of input field_entry */
|
||||
unsigned maxfield; /* maximum valid field index */
|
||||
unsigned size; /* size of the report (bits) */
|
||||
struct hid_device *device; /* associated device */
|
||||
|
||||
/* tool related state */
|
||||
bool tool_active; /* whether the current tool is active */
|
||||
unsigned int tool; /* BTN_TOOL_* */
|
||||
};
|
||||
|
||||
#define HID_MAX_IDS 256
|
||||
|
Loading…
Reference in New Issue
Block a user