Commit 5e206459 authored by Linus Torvalds's avatar Linus Torvalds

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
  ...
parents 14646776 b146dbbd
...@@ -137,7 +137,11 @@ A few EV_KEY codes have special meanings: ...@@ -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 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 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> 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: * BTN_TOUCH:
......
...@@ -128,6 +128,8 @@ config HID_ACRUX_FF ...@@ -128,6 +128,8 @@ config HID_ACRUX_FF
config HID_APPLE config HID_APPLE
tristate "Apple {i,Power,Mac}Books" tristate "Apple {i,Power,Mac}Books"
depends on HID depends on HID
depends on LEDS_CLASS
depends on NEW_LEDS
default !EXPERT default !EXPERT
help help
Support for some Apple devices which less or more break Support for some Apple devices which less or more break
...@@ -929,6 +931,13 @@ config PLAYSTATION_FF ...@@ -929,6 +931,13 @@ config PLAYSTATION_FF
Say Y here if you would like to enable force feedback support for Say Y here if you would like to enable force feedback support for
PlayStation game controllers. 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 config HID_PRIMAX
tristate "Primax non-fully HID-compliant devices" tristate "Primax non-fully HID-compliant devices"
depends on HID depends on HID
...@@ -984,6 +993,16 @@ config HID_SEMITEK ...@@ -984,6 +993,16 @@ config HID_SEMITEK
- Woo-dy - Woo-dy
- X-Bows Nature/Knight - 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 config HID_SONY
tristate "Sony PS2/3/4 accessories" tristate "Sony PS2/3/4 accessories"
depends on USB_HID depends on USB_HID
......
...@@ -99,6 +99,7 @@ hid-picolcd-$(CONFIG_DEBUG_FS) += hid-picolcd_debugfs.o ...@@ -99,6 +99,7 @@ hid-picolcd-$(CONFIG_DEBUG_FS) += hid-picolcd_debugfs.o
obj-$(CONFIG_HID_PLANTRONICS) += hid-plantronics.o obj-$(CONFIG_HID_PLANTRONICS) += hid-plantronics.o
obj-$(CONFIG_HID_PLAYSTATION) += hid-playstation.o obj-$(CONFIG_HID_PLAYSTATION) += hid-playstation.o
obj-$(CONFIG_HID_PRIMAX) += hid-primax.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_REDRAGON) += hid-redragon.o
obj-$(CONFIG_HID_RETRODE) += hid-retrode.o obj-$(CONFIG_HID_RETRODE) += hid-retrode.o
obj-$(CONFIG_HID_ROCCAT) += hid-roccat.o hid-roccat-common.o \ obj-$(CONFIG_HID_ROCCAT) += hid-roccat.o hid-roccat-common.o \
...@@ -109,6 +110,7 @@ obj-$(CONFIG_HID_RMI) += hid-rmi.o ...@@ -109,6 +110,7 @@ obj-$(CONFIG_HID_RMI) += hid-rmi.o
obj-$(CONFIG_HID_SAITEK) += hid-saitek.o obj-$(CONFIG_HID_SAITEK) += hid-saitek.o
obj-$(CONFIG_HID_SAMSUNG) += hid-samsung.o obj-$(CONFIG_HID_SAMSUNG) += hid-samsung.o
obj-$(CONFIG_HID_SEMITEK) += hid-semitek.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_SMARTJOYPLUS) += hid-sjoy.o
obj-$(CONFIG_HID_SONY) += hid-sony.o obj-$(CONFIG_HID_SONY) += hid-sony.o
obj-$(CONFIG_HID_SPEEDLINK) += hid-speedlink.o obj-$(CONFIG_HID_SPEEDLINK) += hid-speedlink.o
......
...@@ -300,13 +300,10 @@ static int amd_mp2_pci_probe(struct pci_dev *pdev, const struct pci_device_id *i ...@@ -300,13 +300,10 @@ static int amd_mp2_pci_probe(struct pci_dev *pdev, const struct pci_device_id *i
privdata->mmio = pcim_iomap_table(pdev)[2]; privdata->mmio = pcim_iomap_table(pdev)[2];
pci_set_master(pdev); pci_set_master(pdev);
rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); 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) { if (rc) {
dev_err(&pdev->dev, "failed to set DMA mask\n"); dev_err(&pdev->dev, "failed to set DMA mask\n");
return rc; return rc;
} }
}
privdata->cl_data = devm_kzalloc(&pdev->dev, sizeof(struct amdtp_cl_data), GFP_KERNEL); privdata->cl_data = devm_kzalloc(&pdev->dev, sizeof(struct amdtp_cl_data), GFP_KERNEL);
if (!privdata->cl_data) if (!privdata->cl_data)
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2006-2007 Jiri Kosina * Copyright (c) 2006-2007 Jiri Kosina
* Copyright (c) 2008 Jiri Slaby <jirislaby@gmail.com> * Copyright (c) 2008 Jiri Slaby <jirislaby@gmail.com>
* Copyright (c) 2019 Paul Pawlowski <paul@mrarm.io>
*/ */
/* /*
...@@ -33,6 +34,7 @@ ...@@ -33,6 +34,7 @@
/* BIT(7) reserved, was: APPLE_IGNORE_HIDINPUT */ /* BIT(7) reserved, was: APPLE_IGNORE_HIDINPUT */
#define APPLE_NUMLOCK_EMULATION BIT(8) #define APPLE_NUMLOCK_EMULATION BIT(8)
#define APPLE_RDESC_BATTERY BIT(9) #define APPLE_RDESC_BATTERY BIT(9)
#define APPLE_BACKLIGHT_CTL BIT(10)
#define APPLE_FLAG_FKEY 0x01 #define APPLE_FLAG_FKEY 0x01
...@@ -61,6 +63,12 @@ MODULE_PARM_DESC(swap_fn_leftctrl, "Swap the Fn and left Control keys. " ...@@ -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. " "(For people who want to keep PC keyboard muscle memory. "
"[0] = as-is, Mac layout, 1 = swapped, PC layout)"); "[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 apple_sc {
struct hid_device *hdev; struct hid_device *hdev;
unsigned long quirks; unsigned long quirks;
...@@ -68,6 +76,7 @@ struct apple_sc { ...@@ -68,6 +76,7 @@ struct apple_sc {
unsigned int fn_found; unsigned int fn_found;
DECLARE_BITMAP(pressed_numlock, KEY_CNT); DECLARE_BITMAP(pressed_numlock, KEY_CNT);
struct timer_list battery_timer; struct timer_list battery_timer;
struct apple_sc_backlight *backlight;
}; };
struct apple_key_translation { struct apple_key_translation {
...@@ -76,6 +85,61 @@ struct apple_key_translation { ...@@ -76,6 +85,61 @@ struct apple_key_translation {
u8 flags; 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[] = { static const struct apple_key_translation apple2021_fn_keys[] = {
{ KEY_BACKSPACE, KEY_DELETE }, { KEY_BACKSPACE, KEY_DELETE },
{ KEY_ENTER, KEY_INSERT }, { KEY_ENTER, KEY_INSERT },
...@@ -119,6 +183,51 @@ static const struct apple_key_translation macbookair_fn_keys[] = { ...@@ -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[] = { static const struct apple_key_translation apple_fn_keys[] = {
{ KEY_BACKSPACE, KEY_DELETE }, { KEY_BACKSPACE, KEY_DELETE },
{ KEY_ENTER, KEY_INSERT }, { KEY_ENTER, KEY_INSERT },
...@@ -202,6 +311,15 @@ static const struct apple_key_translation swapped_fn_leftctrl_keys[] = { ...@@ -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( static const struct apple_key_translation *apple_find_translation(
const struct apple_key_translation *table, u16 from) 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, ...@@ -242,10 +360,34 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input,
} }
if (fnmode) { if (fnmode) {
if (hid->product == USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_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_FINGERPRINT_2021 ||
hid->product == USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_NUMPAD_2021) hid->product == USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_NUMPAD_2021)
table = apple2021_fn_keys; 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 && else if (hid->product >= USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI &&
hid->product <= USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS) hid->product <= USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS)
table = macbookair_fn_keys; table = macbookair_fn_keys;
...@@ -452,30 +594,21 @@ static __u8 *apple_report_fixup(struct hid_device *hdev, __u8 *rdesc, ...@@ -452,30 +594,21 @@ static __u8 *apple_report_fixup(struct hid_device *hdev, __u8 *rdesc,
static void apple_setup_input(struct input_dev *input) static void apple_setup_input(struct input_dev *input)
{ {
const struct apple_key_translation *trans;
set_bit(KEY_NUMLOCK, input->keybit); set_bit(KEY_NUMLOCK, input->keybit);
/* Enable all needed keys */ /* Enable all needed keys */
for (trans = apple_fn_keys; trans->from; trans++) apple_setup_key_translation(input, apple_fn_keys);
set_bit(trans->to, input->keybit); apple_setup_key_translation(input, powerbook_fn_keys);
apple_setup_key_translation(input, powerbook_numlock_keys);
for (trans = powerbook_fn_keys; trans->from; trans++) apple_setup_key_translation(input, apple_iso_keyboard);
set_bit(trans->to, input->keybit); apple_setup_key_translation(input, magic_keyboard_alu_fn_keys);
apple_setup_key_translation(input, magic_keyboard_2015_fn_keys);
for (trans = powerbook_numlock_keys; trans->from; trans++) apple_setup_key_translation(input, apple2021_fn_keys);
set_bit(trans->to, input->keybit); apple_setup_key_translation(input, macbookpro_no_esc_fn_keys);
apple_setup_key_translation(input, macbookpro_dedicated_esc_fn_keys);
for (trans = apple_iso_keyboard; trans->from; trans++)
set_bit(trans->to, input->keybit); if (swap_fn_leftctrl)
apple_setup_key_translation(input, swapped_fn_leftctrl_keys);
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);
}
} }
static int apple_input_mapping(struct hid_device *hdev, struct hid_input *hi, 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, ...@@ -530,6 +663,105 @@ static int apple_input_configured(struct hid_device *hdev,
return 0; 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, static int apple_probe(struct hid_device *hdev,
const struct hid_device_id *id) const struct hid_device_id *id)
{ {
...@@ -565,6 +797,9 @@ static int apple_probe(struct hid_device *hdev, ...@@ -565,6 +797,9 @@ static int apple_probe(struct hid_device *hdev,
jiffies + msecs_to_jiffies(APPLE_BATTERY_TIMEOUT_MS)); jiffies + msecs_to_jiffies(APPLE_BATTERY_TIMEOUT_MS));
apple_fetch_battery(hdev); apple_fetch_battery(hdev);
if (quirks & APPLE_BACKLIGHT_CTL)
apple_backlight_init(hdev);
return 0; return 0;
} }
...@@ -736,6 +971,22 @@ static const struct hid_device_id apple_devices[] = { ...@@ -736,6 +971,22 @@ static const struct hid_device_id apple_devices[] = {
.driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK }, .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING9_JIS), { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING9_JIS),
.driver_data = APPLE_HAS_FN | APPLE_RDESC_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), { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI),
.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN },
{ 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_ISO),
...@@ -748,15 +999,15 @@ static const struct hid_device_id apple_devices[] = { ...@@ -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), { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY),
.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_2021), { 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), { HID_BLUETOOTH_DEVICE(BT_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 },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_FINGERPRINT_2021), { 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), { HID_BLUETOOTH_DEVICE(BT_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 },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_NUMPAD_2021), { 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), { HID_BLUETOOTH_DEVICE(BT_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 },
......
...@@ -81,6 +81,7 @@ struct hid_report *hid_register_report(struct hid_device *device, ...@@ -81,6 +81,7 @@ struct hid_report *hid_register_report(struct hid_device *device,
report_enum->report_id_hash[id] = report; report_enum->report_id_hash[id] = report;
list_add_tail(&report->list, &report_enum->report_list); list_add_tail(&report->list, &report_enum->report_list);
INIT_LIST_HEAD(&report->field_entry_list);
return report; return report;
} }
...@@ -101,7 +102,7 @@ static struct hid_field *hid_register_field(struct hid_report *report, unsigned ...@@ -101,7 +102,7 @@ static struct hid_field *hid_register_field(struct hid_report *report, unsigned
field = kzalloc((sizeof(struct hid_field) + field = kzalloc((sizeof(struct hid_field) +
usages * sizeof(struct hid_usage) + usages * sizeof(struct hid_usage) +
usages * sizeof(unsigned)), GFP_KERNEL); 3 * usages * sizeof(unsigned int)), GFP_KERNEL);
if (!field) if (!field)
return NULL; return NULL;
...@@ -109,6 +110,8 @@ static struct hid_field *hid_register_field(struct hid_report *report, unsigned ...@@ -109,6 +110,8 @@ static struct hid_field *hid_register_field(struct hid_report *report, unsigned
report->field[field->index] = field; report->field[field->index] = field;
field->usage = (struct hid_usage *)(field + 1); field->usage = (struct hid_usage *)(field + 1);
field->value = (s32 *)(field->usage + usages); field->value = (s32 *)(field->usage + usages);
field->new_value = (s32 *)(field->value + usages);
field->usages_priorities = (s32 *)(field->new_value + usages);
field->report = report; field->report = report;
return field; return field;
...@@ -656,6 +659,8 @@ static void hid_free_report(struct hid_report *report) ...@@ -656,6 +659,8 @@ static void hid_free_report(struct hid_report *report)
{ {
unsigned n; unsigned n;
kfree(report->field_entries);
for (n = 0; n < report->maxfield; n++) for (n = 0; n < report->maxfield; n++)
kfree(report->field[n]); kfree(report->field[n]);
kfree(report); kfree(report);
...@@ -1525,25 +1530,41 @@ static void hid_process_event(struct hid_device *hid, struct hid_field *field, ...@@ -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 * Checks if the given value is valid within this field
* content is stored for next report processing (we do differential
* reporting to the layer).
*/ */
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 n;
unsigned count = field->report_count; unsigned count = field->report_count;
unsigned offset = field->report_offset; unsigned offset = field->report_offset;
unsigned size = field->report_size; unsigned size = field->report_size;
__s32 min = field->logical_minimum; __s32 min = field->logical_minimum;
__s32 max = field->logical_maximum;
__s32 *value; __s32 *value;
value = kmalloc_array(count, sizeof(__s32), GFP_ATOMIC); value = field->new_value;
if (!value) memset(value, 0, count * sizeof(__s32));
return; field->ignored = false;
for (n = 0; n < count; n++) { for (n = 0; n < count; n++) {
...@@ -1554,35 +1575,228 @@ static void hid_input_field(struct hid_device *hid, struct hid_field *field, ...@@ -1554,35 +1575,228 @@ static void hid_input_field(struct hid_device *hid, struct hid_field *field,
/* Ignore report if ErrorRollOver */ /* Ignore report if ErrorRollOver */
if (!(field->flags & HID_MAIN_ITEM_VARIABLE) && if (!(field->flags & HID_MAIN_ITEM_VARIABLE) &&
value[n] >= min && value[n] <= max && hid_array_value_is_valid(field, value[n]) &&
value[n] - min < field->maxusage && field->usage[value[n] - min].hid == HID_UP_KEYBOARD + 1) {
field->usage[value[n] - min].hid == HID_UP_KEYBOARD + 1) field->ignored = true;
goto exit; 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++) { 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_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);
}
if (HID_MAIN_ITEM_VARIABLE & field->flags) { memcpy(field->value, value, count * sizeof(__s32));
hid_process_event(hid, field, &field->usage[n], value[n], interrupt); }
continue;
/*
* 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);
} }
if (field->value[n] >= min && field->value[n] <= max /* we need to do the memcpy at the end for var items */
&& field->value[n] - min < field->maxusage for (a = 0; a < report->maxfield; a++) {
&& field->usage[field->value[n] - min].hid field = report->field[a];
&& 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 if (field->flags & HID_MAIN_ITEM_VARIABLE)
&& value[n] - min < field->maxusage memcpy(field->value, field->new_value,
&& field->usage[value[n] - min].hid field->report_count * sizeof(__s32));
&& search(field->value, value[n], count))
hid_process_event(hid, field, &field->usage[value[n] - min], 1, interrupt);
} }
} else {
/* FEATURE_REPORT, regular processing */
for (a = 0; a < report->maxfield; a++) {
field = report->field[a];
memcpy(field->value, value, count * sizeof(__s32)); if (field->flags & HID_MAIN_ITEM_VARIABLE)
exit: hid_input_var_field(hid, field, interrupt);
kfree(value); 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, ...@@ -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_enum *report_enum = hid->report_enum + type;
struct hid_report *report; struct hid_report *report;
struct hid_driver *hdrv; struct hid_driver *hdrv;
unsigned int a;
u32 rsize, csize = size; u32 rsize, csize = size;
u8 *cdata = data; u8 *cdata = data;
int ret = 0; int ret = 0;
...@@ -1782,8 +1995,7 @@ int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, u32 size, ...@@ -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) { if (hid->claimed != HID_CLAIMED_HIDRAW && report->maxfield) {
for (a = 0; a < report->maxfield; a++) hid_process_report(hid, report, cdata, interrupt);
hid_input_field(hid, report->field[a], cdata, interrupt);
hdrv = hid->driver; hdrv = hid->driver;
if (hdrv && hdrv->report) if (hdrv && hdrv->report)
hdrv->report(hid, report); hdrv->report(hid, report);
...@@ -1970,6 +2182,8 @@ int hid_connect(struct hid_device *hdev, unsigned int connect_mask) ...@@ -1970,6 +2182,8 @@ int hid_connect(struct hid_device *hdev, unsigned int connect_mask)
return -ENODEV; return -ENODEV;
} }
hid_process_ordering(hdev);
if ((hdev->claimed & HID_CLAIMED_INPUT) && if ((hdev->claimed & HID_CLAIMED_INPUT) &&
(connect_mask & HID_CONNECT_FF) && hdev->ff_init) (connect_mask & HID_CONNECT_FF) && hdev->ff_init)
hdev->ff_init(hdev); hdev->ff_init(hdev);
......
...@@ -58,7 +58,7 @@ static int cbas_ec_query_base(struct cros_ec_device *ec_dev, bool get_state, ...@@ -58,7 +58,7 @@ static int cbas_ec_query_base(struct cros_ec_device *ec_dev, bool get_state,
struct cros_ec_command *msg; struct cros_ec_command *msg;
int ret; int ret;
msg = kzalloc(sizeof(*msg) + max(sizeof(u32), sizeof(*params)), msg = kzalloc(struct_size(msg, data, max(sizeof(u32), sizeof(*params))),
GFP_KERNEL); GFP_KERNEL);
if (!msg) if (!msg)
return -ENOMEM; return -ENOMEM;
......
...@@ -167,6 +167,14 @@ ...@@ -167,6 +167,14 @@
#define USB_DEVICE_ID_APPLE_WELLSPRING9_ANSI 0x0272 #define USB_DEVICE_ID_APPLE_WELLSPRING9_ANSI 0x0272
#define USB_DEVICE_ID_APPLE_WELLSPRING9_ISO 0x0273 #define USB_DEVICE_ID_APPLE_WELLSPRING9_ISO 0x0273
#define USB_DEVICE_ID_APPLE_WELLSPRING9_JIS 0x0274 #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_FOUNTAIN_TP_ONLY 0x030a
#define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY 0x030b #define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY 0x030b
#define USB_DEVICE_ID_APPLE_IRCONTROL 0x8240 #define USB_DEVICE_ID_APPLE_IRCONTROL 0x8240
...@@ -606,7 +614,7 @@ ...@@ -606,7 +614,7 @@
#define USB_VENDOR_ID_HUION 0x256c #define USB_VENDOR_ID_HUION 0x256c
#define USB_DEVICE_ID_HUION_TABLET 0x006e #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_VENDOR_ID_IBM 0x04b3
#define USB_DEVICE_ID_IBM_SCROLLPOINT_III 0x3100 #define USB_DEVICE_ID_IBM_SCROLLPOINT_III 0x3100
...@@ -1030,6 +1038,9 @@ ...@@ -1030,6 +1038,9 @@
#define I2C_PRODUCT_ID_RAYDIUM_3118 0x3118 #define I2C_PRODUCT_ID_RAYDIUM_3118 0x3118
#define USB_VENDOR_ID_RAZER 0x1532 #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_DEVICE_ID_RAZER_BLADE_14 0x011D
#define USB_VENDOR_ID_REALTEK 0x0bda #define USB_VENDOR_ID_REALTEK 0x0bda
...@@ -1092,6 +1103,7 @@ ...@@ -1092,6 +1103,7 @@
#define USB_VENDOR_ID_SIGMA_MICRO 0x1c4f #define USB_VENDOR_ID_SIGMA_MICRO 0x1c4f
#define USB_DEVICE_ID_SIGMA_MICRO_KEYBOARD 0x0002 #define USB_DEVICE_ID_SIGMA_MICRO_KEYBOARD 0x0002
#define USB_DEVICE_ID_SIGMA_MICRO_KEYBOARD2 0x0059
#define USB_VENDOR_ID_SIGMATEL 0x066F #define USB_VENDOR_ID_SIGMATEL 0x066F
#define USB_DEVICE_ID_SIGMATEL_STMP3780 0x3780 #define USB_DEVICE_ID_SIGMATEL_STMP3780 0x3780
......
...@@ -48,6 +48,51 @@ static const struct { ...@@ -48,6 +48,51 @@ static const struct {
__s32 y; __s32 y;
} hid_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}}; } 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_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_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)) #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 ...@@ -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, 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 input_dev *input = hidinput->input;
struct hid_device *device = input_get_drvdata(input); struct hid_device *device = input_get_drvdata(input);
const struct usage_priority *usage_priority = NULL;
int max = 0, code; int max = 0, code;
unsigned int i = 0;
unsigned long *bit = NULL; unsigned long *bit = NULL;
field->hidinput = hidinput; field->hidinput = hidinput;
...@@ -608,6 +655,28 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel ...@@ -608,6 +655,28 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
goto ignore; 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) { if (device->driver->input_mapping) {
int ret = device->driver->input_mapping(device, hidinput, field, int ret = device->driver->input_mapping(device, hidinput, field,
usage, &bit, &max); usage, &bit, &max);
...@@ -828,10 +897,31 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel ...@@ -828,10 +897,31 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
break; break;
case 0x32: /* InRange */ case 0x32: /* InRange */
switch (field->physical & 0xff) { switch (field->physical) {
case 0x21: map_key(BTN_TOOL_MOUSE); break; case HID_DG_PUCK:
case 0x22: map_key(BTN_TOOL_FINGER); break; map_key(BTN_TOOL_MOUSE);
default: map_key(BTN_TOOL_PEN); break; 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; break;
...@@ -1318,9 +1408,38 @@ static void hidinput_handle_scroll(struct hid_usage *usage, ...@@ -1318,9 +1408,38 @@ static void hidinput_handle_scroll(struct hid_usage *usage,
input_event(input, EV_REL, usage->code, hi_res); 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) void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value)
{ {
struct input_dev *input; struct input_dev *input;
struct hid_report *report = field->report;
unsigned *quirks = &hid->quirks; unsigned *quirks = &hid->quirks;
if (!usage->type) if (!usage->type)
...@@ -1336,12 +1455,6 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct ...@@ -1336,12 +1455,6 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct
input = field->hidinput->input; 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) { if (usage->hat_min < usage->hat_max || usage->hat_dir) {
int hat_dir = usage->hat_dir; int hat_dir = usage->hat_dir;
if (!hat_dir) if (!hat_dir)
...@@ -1352,48 +1465,129 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct ...@@ -1352,48 +1465,129 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct
return; return;
} }
if (usage->hid == HID_DG_INVERT) { /*
*quirks = value ? (*quirks | HID_QUIRK_INVERT) : (*quirks & ~HID_QUIRK_INVERT); * Ignore out-of-range values as per HID specification,
* section 5.10 and 6.2.25, when NULL state bit is present.
* When it's not, clamp the value to match Microsoft's input
* driver as mentioned in "Required HID usages for digitizers":
* https://msdn.microsoft.com/en-us/library/windows/hardware/dn672278(v=vs.85).asp
*
* The logical_minimum < logical_maximum check is done so that we
* don't unintentionally discard values sent by devices which
* don't specify logical min and max.
*/
if ((field->flags & HID_MAIN_ITEM_VARIABLE) &&
field->logical_minimum < field->logical_maximum) {
if (field->flags & HID_MAIN_ITEM_NULL_STATE &&
(value < field->logical_minimum ||
value > field->logical_maximum)) {
dbg_hid("Ignoring out-of-range value %x\n", value);
return; return;
} }
value = clamp(value,
field->logical_minimum,
field->logical_maximum);
}
if (usage->hid == HID_DG_INRANGE) { switch (usage->hid) {
if (value) { case HID_DG_ERASER:
input_event(input, usage->type, (*quirks & HID_QUIRK_INVERT) ? BTN_TOOL_RUBBER : usage->code, 1); 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; return;
}
input_event(input, usage->type, usage->code, 0); /* let hid-input set BTN_TOUCH */
input_event(input, usage->type, BTN_TOOL_RUBBER, 0); 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; 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);
} }
if (usage->hid == HID_DG_TIPPRESSURE && (*quirks & HID_QUIRK_NOTOUCH)) { /* 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 a = field->logical_minimum;
int b = field->logical_maximum; int b = field->logical_maximum;
input_event(input, EV_KEY, BTN_TOUCH, value > a + ((b - a) >> 3));
if (value > a + ((b - a) >> 3)) {
input_event(input, EV_KEY, BTN_TOUCH, 1);
report->tool_active = true;
} }
}
break;
if (usage->hid == (HID_UP_PID | 0x83UL)) { /* Simultaneous Effects Max */ case HID_UP_PID | 0x83UL: /* Simultaneous Effects Max */
dbg_hid("Maximum Effects - %d\n",value); dbg_hid("Maximum Effects - %d\n",value);
return; return;
}
if (usage->hid == (HID_UP_PID | 0x7fUL)) { case HID_UP_PID | 0x7fUL:
dbg_hid("PID Pool Report\n"); dbg_hid("PID Pool Report\n");
return; return;
} }
if ((usage->type == EV_KEY) && (usage->code == 0)) /* Key 0 is "unassigned", not KEY_UNKNOWN */ switch (usage->type) {
case EV_KEY:
if (usage->code == 0) /* Key 0 is "unassigned", not KEY_UNKNOWN */
return; return;
break;
if ((usage->type == EV_REL) && (usage->code == REL_WHEEL_HI_RES || case EV_REL:
usage->code == REL_HWHEEL_HI_RES)) { if (usage->code == REL_WHEEL_HI_RES ||
usage->code == REL_HWHEEL_HI_RES) {
hidinput_handle_scroll(usage, input, value); hidinput_handle_scroll(usage, input, value);
return; return;
} }
break;
if ((usage->type == EV_ABS) && (field->flags & HID_MAIN_ITEM_RELATIVE) && case EV_ABS:
(usage->code == ABS_VOLUME)) { if ((field->flags & HID_MAIN_ITEM_RELATIVE) &&
usage->code == ABS_VOLUME) {
int count = abs(value); int count = abs(value);
int direction = value > 0 ? KEY_VOLUMEUP : KEY_VOLUMEDOWN; int direction = value > 0 ? KEY_VOLUMEUP : KEY_VOLUMEDOWN;
int i; int i;
...@@ -1405,30 +1599,11 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct ...@@ -1405,30 +1599,11 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct
input_sync(input); input_sync(input);
} }
return; return;
}
/* } else if (((*quirks & HID_QUIRK_X_INVERT) && usage->code == ABS_X) ||
* Ignore out-of-range values as per HID specification, ((*quirks & HID_QUIRK_Y_INVERT) && usage->code == ABS_Y))
* section 5.10 and 6.2.25, when NULL state bit is present. value = field->logical_maximum - value;
* When it's not, clamp the value to match Microsoft's input break;
* driver as mentioned in "Required HID usages for digitizers":
* https://msdn.microsoft.com/en-us/library/windows/hardware/dn672278(v=vs.85).asp
*
* The logical_minimum < logical_maximum check is done so that we
* don't unintentionally discard values sent by devices which
* don't specify logical min and max.
*/
if ((field->flags & HID_MAIN_ITEM_VARIABLE) &&
(field->logical_minimum < field->logical_maximum)) {
if (field->flags & HID_MAIN_ITEM_NULL_STATE &&
(value < field->logical_minimum ||
value > field->logical_maximum)) {
dbg_hid("Ignoring out-of-range value %x\n", value);
return;
}
value = clamp(value,
field->logical_minimum,
field->logical_maximum);
} }
/* /*
...@@ -1933,12 +2108,63 @@ static struct hid_input *hidinput_match_application(struct hid_report *report) ...@@ -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, static inline void hidinput_configure_usages(struct hid_input *hidinput,
struct hid_report *report) 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 (i = 0; i < report->maxfield; i++)
for (j = 0; j < report->field[i]->maxusage; j++) for (j = 0; j < report->field[i]->maxusage; j++)
hidinput_configure_usage(hidinput, report->field[i], 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[] = { ...@@ -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_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_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_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_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_ISO) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS) }, { 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[] = { ...@@ -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_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_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_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_FOUNTAIN_TP_ONLY) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) },
{ } { }
......
// 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");
// 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, ...@@ -81,24 +81,6 @@ static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc,
return 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, static int uclogic_input_configured(struct hid_device *hdev,
struct hid_input *hi) struct hid_input *hi)
{ {
...@@ -246,27 +228,26 @@ static int uclogic_resume(struct hid_device *hdev) ...@@ -246,27 +228,26 @@ static int uclogic_resume(struct hid_device *hdev)
} }
#endif #endif
static int uclogic_raw_event(struct hid_device *hdev, /**
struct hid_report *report, * 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) u8 *data, int size)
{ {
struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); struct uclogic_params_pen *pen = &drvdata->params.pen;
struct uclogic_params *params = &drvdata->params;
WARN_ON(drvdata == NULL);
WARN_ON(data == NULL && size != 0);
/* 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 in-range reports are inverted */
if (params->pen.inrange == if (pen->inrange ==
UCLOGIC_PARAMS_PEN_INRANGE_INVERTED) { UCLOGIC_PARAMS_PEN_INRANGE_INVERTED) {
/* Invert the in-range bit */ /* Invert the in-range bit */
data[1] ^= 0x40; data[1] ^= 0x40;
...@@ -275,7 +256,7 @@ static int uclogic_raw_event(struct hid_device *hdev, ...@@ -275,7 +256,7 @@ static int uclogic_raw_event(struct hid_device *hdev,
* If report contains fragmented high-resolution pen * If report contains fragmented high-resolution pen
* coordinates * coordinates
*/ */
if (size >= 10 && params->pen.fragmented_hires) { if (size >= 10 && pen->fragmented_hires) {
u8 pressure_low_byte; u8 pressure_low_byte;
u8 pressure_high_byte; u8 pressure_high_byte;
...@@ -297,28 +278,47 @@ static int uclogic_raw_event(struct hid_device *hdev, ...@@ -297,28 +278,47 @@ static int uclogic_raw_event(struct hid_device *hdev,
data[9] = pressure_high_byte; data[9] = pressure_high_byte;
} }
/* If we need to emulate in-range detection */ /* If we need to emulate in-range detection */
if (params->pen.inrange == UCLOGIC_PARAMS_PEN_INRANGE_NONE) { if (pen->inrange == UCLOGIC_PARAMS_PEN_INRANGE_NONE) {
/* Set in-range bit */ /* Set in-range bit */
data[1] |= 0x40; data[1] |= 0x40;
/* (Re-)start in-range timeout */ /* (Re-)start in-range timeout */
mod_timer(&drvdata->inrange_timer, mod_timer(&drvdata->inrange_timer,
jiffies + msecs_to_jiffies(100)); 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);
/* 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 need to, and can, set pad device ID for Wacom drivers */
if (params->frame.dev_id_byte > 0 && if (frame->dev_id_byte > 0 && frame->dev_id_byte < size) {
params->frame.dev_id_byte < size) { data[frame->dev_id_byte] = 0xf;
data[params->frame.dev_id_byte] = 0xf;
} }
/* If need to, and can, read rotary encoder state change */ /* If need to, and can, read rotary encoder state change */
if (params->frame.re_lsb > 0 && if (frame->re_lsb > 0 && frame->re_lsb / 8 < size) {
params->frame.re_lsb / 8 < size) { unsigned int byte = frame->re_lsb / 8;
unsigned int byte = params->frame.re_lsb / 8; unsigned int bit = frame->re_lsb % 8;
unsigned int bit = params->frame.re_lsb % 8;
u8 change; u8 change;
u8 prev_state = drvdata->re_state; u8 prev_state = drvdata->re_state;
...@@ -340,6 +340,59 @@ static int uclogic_raw_event(struct hid_device *hdev, ...@@ -340,6 +340,59 @@ static int uclogic_raw_event(struct hid_device *hdev,
/* Remember state */ /* Remember state */
drvdata->re_state = 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;
/* Do not handle anything but input reports */
if (report->type != HID_INPUT_REPORT)
return 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);
}
}
/* 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, &params->frame_list[i],
data, size);
}
}
break;
} }
return 0; return 0;
...@@ -373,7 +426,7 @@ static const struct hid_device_id uclogic_devices[] = { ...@@ -373,7 +426,7 @@ static const struct hid_device_id uclogic_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_HUION, { HID_USB_DEVICE(USB_VENDOR_ID_HUION,
USB_DEVICE_ID_HUION_TABLET) }, USB_DEVICE_ID_HUION_TABLET) },
{ HID_USB_DEVICE(USB_VENDOR_ID_HUION, { HID_USB_DEVICE(USB_VENDOR_ID_HUION,
USB_DEVICE_ID_HUION_HS64) }, USB_DEVICE_ID_HUION_TABLET2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_TRUST, { HID_USB_DEVICE(USB_VENDOR_ID_TRUST,
USB_DEVICE_ID_TRUST_PANORA_TABLET) }, USB_DEVICE_ID_TRUST_PANORA_TABLET) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
...@@ -415,7 +468,6 @@ static struct hid_driver uclogic_driver = { ...@@ -415,7 +468,6 @@ static struct hid_driver uclogic_driver = {
.remove = uclogic_remove, .remove = uclogic_remove,
.report_fixup = uclogic_report_fixup, .report_fixup = uclogic_report_fixup,
.raw_event = uclogic_raw_event, .raw_event = uclogic_raw_event,
.input_mapping = uclogic_input_mapping,
.input_configured = uclogic_input_configured, .input_configured = uclogic_input_configured,
#ifdef CONFIG_PM #ifdef CONFIG_PM
.resume = uclogic_resume, .resume = uclogic_resume,
......
...@@ -207,8 +207,8 @@ static int uclogic_params_pen_init_v1(struct uclogic_params_pen *pen, ...@@ -207,8 +207,8 @@ static int uclogic_params_pen_init_v1(struct uclogic_params_pen *pen,
* Generate pen report descriptor * Generate pen report descriptor
*/ */
desc_ptr = uclogic_rdesc_template_apply( desc_ptr = uclogic_rdesc_template_apply(
uclogic_rdesc_pen_v1_template_arr, uclogic_rdesc_v1_pen_template_arr,
uclogic_rdesc_pen_v1_template_size, uclogic_rdesc_v1_pen_template_size,
desc_params, ARRAY_SIZE(desc_params)); desc_params, ARRAY_SIZE(desc_params));
if (desc_ptr == NULL) { if (desc_ptr == NULL) {
rc = -ENOMEM; rc = -ENOMEM;
...@@ -221,8 +221,8 @@ static int uclogic_params_pen_init_v1(struct uclogic_params_pen *pen, ...@@ -221,8 +221,8 @@ static int uclogic_params_pen_init_v1(struct uclogic_params_pen *pen,
memset(pen, 0, sizeof(*pen)); memset(pen, 0, sizeof(*pen));
pen->desc_ptr = desc_ptr; pen->desc_ptr = desc_ptr;
desc_ptr = NULL; desc_ptr = NULL;
pen->desc_size = uclogic_rdesc_pen_v1_template_size; pen->desc_size = uclogic_rdesc_v1_pen_template_size;
pen->id = UCLOGIC_RDESC_PEN_V1_ID; pen->id = UCLOGIC_RDESC_V1_PEN_ID;
pen->inrange = UCLOGIC_PARAMS_PEN_INRANGE_INVERTED; pen->inrange = UCLOGIC_PARAMS_PEN_INRANGE_INVERTED;
found = true; found = true;
finish: finish:
...@@ -351,8 +351,8 @@ static int uclogic_params_pen_init_v2(struct uclogic_params_pen *pen, ...@@ -351,8 +351,8 @@ static int uclogic_params_pen_init_v2(struct uclogic_params_pen *pen,
* Generate pen report descriptor * Generate pen report descriptor
*/ */
desc_ptr = uclogic_rdesc_template_apply( desc_ptr = uclogic_rdesc_template_apply(
uclogic_rdesc_pen_v2_template_arr, uclogic_rdesc_v2_pen_template_arr,
uclogic_rdesc_pen_v2_template_size, uclogic_rdesc_v2_pen_template_size,
desc_params, ARRAY_SIZE(desc_params)); desc_params, ARRAY_SIZE(desc_params));
if (desc_ptr == NULL) { if (desc_ptr == NULL) {
rc = -ENOMEM; rc = -ENOMEM;
...@@ -365,10 +365,11 @@ static int uclogic_params_pen_init_v2(struct uclogic_params_pen *pen, ...@@ -365,10 +365,11 @@ static int uclogic_params_pen_init_v2(struct uclogic_params_pen *pen,
memset(pen, 0, sizeof(*pen)); memset(pen, 0, sizeof(*pen));
pen->desc_ptr = desc_ptr; pen->desc_ptr = desc_ptr;
desc_ptr = NULL; desc_ptr = NULL;
pen->desc_size = uclogic_rdesc_pen_v2_template_size; pen->desc_size = uclogic_rdesc_v2_pen_template_size;
pen->id = UCLOGIC_RDESC_PEN_V2_ID; pen->id = UCLOGIC_RDESC_V2_PEN_ID;
pen->inrange = UCLOGIC_PARAMS_PEN_INRANGE_NONE; pen->inrange = UCLOGIC_PARAMS_PEN_INRANGE_NONE;
pen->fragmented_hires = true; pen->fragmented_hires = true;
pen->tilt_y_flipped = true;
found = true; found = true;
finish: finish:
*pfound = found; *pfound = found;
...@@ -430,8 +431,8 @@ static int uclogic_params_frame_init_with_desc( ...@@ -430,8 +431,8 @@ static int uclogic_params_frame_init_with_desc(
} }
/** /**
* uclogic_params_frame_init_v1_buttonpad() - initialize abstract buttonpad * uclogic_params_frame_init_v1() - initialize v1 tablet interface frame
* on a v1 tablet interface. * controls.
* *
* @frame: Pointer to the frame parameters to initialize (to be cleaned * @frame: Pointer to the frame parameters to initialize (to be cleaned
* up with uclogic_params_frame_cleanup()). Not modified in case * up with uclogic_params_frame_cleanup()). Not modified in case
...@@ -445,8 +446,7 @@ static int uclogic_params_frame_init_with_desc( ...@@ -445,8 +446,7 @@ static int uclogic_params_frame_init_with_desc(
* Returns: * Returns:
* Zero, if successful. A negative errno code on error. * Zero, if successful. A negative errno code on error.
*/ */
static int uclogic_params_frame_init_v1_buttonpad( static int uclogic_params_frame_init_v1(struct uclogic_params_frame *frame,
struct uclogic_params_frame *frame,
bool *pfound, bool *pfound,
struct hid_device *hdev) struct hid_device *hdev)
{ {
...@@ -487,9 +487,9 @@ static int uclogic_params_frame_init_v1_buttonpad( ...@@ -487,9 +487,9 @@ static int uclogic_params_frame_init_v1_buttonpad(
hid_dbg(hdev, "generic buttons enabled\n"); hid_dbg(hdev, "generic buttons enabled\n");
rc = uclogic_params_frame_init_with_desc( rc = uclogic_params_frame_init_with_desc(
frame, frame,
uclogic_rdesc_buttonpad_v1_arr, uclogic_rdesc_v1_frame_arr,
uclogic_rdesc_buttonpad_v1_size, uclogic_rdesc_v1_frame_size,
UCLOGIC_RDESC_BUTTONPAD_V1_ID); UCLOGIC_RDESC_V1_FRAME_ID);
if (rc != 0) if (rc != 0)
goto cleanup; goto cleanup;
found = true; found = true;
...@@ -512,10 +512,12 @@ static int uclogic_params_frame_init_v1_buttonpad( ...@@ -512,10 +512,12 @@ static int uclogic_params_frame_init_v1_buttonpad(
void uclogic_params_cleanup(struct uclogic_params *params) void uclogic_params_cleanup(struct uclogic_params *params)
{ {
if (!params->invalid) { if (!params->invalid) {
size_t i;
kfree(params->desc_ptr); kfree(params->desc_ptr);
if (!params->pen_unused)
uclogic_params_pen_cleanup(&params->pen); uclogic_params_pen_cleanup(&params->pen);
uclogic_params_frame_cleanup(&params->frame); for (i = 0; i < ARRAY_SIZE(params->frame_list); i++)
uclogic_params_frame_cleanup(&params->frame_list[i]);
memset(params, 0, sizeof(*params)); memset(params, 0, sizeof(*params));
} }
} }
...@@ -543,60 +545,53 @@ int uclogic_params_get_desc(const struct uclogic_params *params, ...@@ -543,60 +545,53 @@ int uclogic_params_get_desc(const struct uclogic_params *params,
__u8 **pdesc, __u8 **pdesc,
unsigned int *psize) unsigned int *psize)
{ {
bool common_present; int rc = -ENOMEM;
bool pen_present; bool present = false;
bool frame_present; unsigned int size = 0;
unsigned int size;
__u8 *desc = NULL; __u8 *desc = NULL;
size_t i;
/* Check arguments */ /* Check arguments */
if (params == NULL || pdesc == NULL || psize == NULL) if (params == NULL || pdesc == NULL || psize == NULL)
return -EINVAL; return -EINVAL;
size = 0; /* Concatenate descriptors */
#define ADD_DESC(_desc_ptr, _desc_size) \
common_present = (params->desc_ptr != NULL); do { \
pen_present = (!params->pen_unused && params->pen.desc_ptr != NULL); unsigned int new_size; \
frame_present = (params->frame.desc_ptr != NULL); __u8 *new_desc; \
if ((_desc_ptr) == NULL) { \
if (common_present) break; \
size += params->desc_size; } \
if (pen_present) new_size = size + (_desc_size); \
size += params->pen.desc_size; new_desc = krealloc(desc, new_size, GFP_KERNEL); \
if (frame_present) if (new_desc == NULL) { \
size += params->frame.desc_size; goto cleanup; \
} \
if (common_present || pen_present || frame_present) { memcpy(new_desc + size, (_desc_ptr), (_desc_size)); \
__u8 *p; desc = new_desc; \
size = new_size; \
desc = kmalloc(size, GFP_KERNEL); present = true; \
if (desc == NULL) } while (0)
return -ENOMEM;
p = desc; ADD_DESC(params->desc_ptr, params->desc_size);
ADD_DESC(params->pen.desc_ptr, params->pen.desc_size);
if (common_present) { for (i = 0; i < ARRAY_SIZE(params->frame_list); i++) {
memcpy(p, params->desc_ptr, ADD_DESC(params->frame_list[i].desc_ptr,
params->desc_size); params->frame_list[i].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); #undef ADD_DESC
if (present) {
*pdesc = desc;
*psize = size; *psize = size;
desc = NULL;
} }
rc = 0;
*pdesc = desc; cleanup:
return 0; kfree(desc);
return rc;
} }
/** /**
...@@ -679,21 +674,6 @@ static int uclogic_params_init_with_opt_desc(struct uclogic_params *params, ...@@ -679,21 +674,6 @@ static int uclogic_params_init_with_opt_desc(struct uclogic_params *params,
return rc; 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 * uclogic_params_huion_init() - initialize a Huion tablet interface and discover
* its parameters. * its parameters.
...@@ -733,8 +713,7 @@ static int uclogic_params_huion_init(struct uclogic_params *params, ...@@ -733,8 +713,7 @@ static int uclogic_params_huion_init(struct uclogic_params *params,
/* If it's not a pen interface */ /* If it's not a pen interface */
if (bInterfaceNumber != 0) { if (bInterfaceNumber != 0) {
/* TODO: Consider marking the interface invalid */ uclogic_params_init_invalid(&p);
uclogic_params_init_with_pen_unused(&p);
goto output; goto output;
} }
...@@ -766,20 +745,22 @@ static int uclogic_params_huion_init(struct uclogic_params *params, ...@@ -766,20 +745,22 @@ static int uclogic_params_huion_init(struct uclogic_params *params,
goto cleanup; goto cleanup;
} else if (found) { } else if (found) {
hid_dbg(hdev, "pen v2 parameters found\n"); hid_dbg(hdev, "pen v2 parameters found\n");
/* Create v2 buttonpad parameters */ /* Create v2 frame parameters */
rc = uclogic_params_frame_init_with_desc( rc = uclogic_params_frame_init_with_desc(
&p.frame, &p.frame_list[0],
uclogic_rdesc_buttonpad_v2_arr, uclogic_rdesc_v2_frame_arr,
uclogic_rdesc_buttonpad_v2_size, uclogic_rdesc_v2_frame_size,
UCLOGIC_RDESC_BUTTONPAD_V2_ID); UCLOGIC_RDESC_V2_FRAME_ID);
if (rc != 0) { if (rc != 0) {
hid_err(hdev, hid_err(hdev,
"failed creating v2 buttonpad parameters: %d\n", "failed creating v2 frame parameters: %d\n",
rc); rc);
goto cleanup; goto cleanup;
} }
/* Set bitmask marking frame reports in pen reports */ /* Link frame button subreports from pen reports */
p.pen_frame_flag = 0x20; p.pen.subreport_list[0].value = 0xe0;
p.pen.subreport_list[0].id =
UCLOGIC_RDESC_V2_FRAME_ID;
goto output; goto output;
} }
hid_dbg(hdev, "pen v2 parameters not found\n"); hid_dbg(hdev, "pen v2 parameters not found\n");
...@@ -793,19 +774,20 @@ static int uclogic_params_huion_init(struct uclogic_params *params, ...@@ -793,19 +774,20 @@ static int uclogic_params_huion_init(struct uclogic_params *params,
goto cleanup; goto cleanup;
} else if (found) { } else if (found) {
hid_dbg(hdev, "pen v1 parameters found\n"); hid_dbg(hdev, "pen v1 parameters found\n");
/* Try to probe v1 buttonpad */ /* Try to probe v1 frame */
rc = uclogic_params_frame_init_v1_buttonpad( rc = uclogic_params_frame_init_v1(&p.frame_list[0],
&p.frame,
&found, hdev); &found, hdev);
if (rc != 0) { 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; goto cleanup;
} }
hid_dbg(hdev, "buttonpad v1 parameters%s found\n", hid_dbg(hdev, "frame v1 parameters%s found\n",
(found ? "" : " not")); (found ? "" : " not"));
if (found) { if (found) {
/* Set bitmask marking frame reports */ /* Link frame button subreports from pen reports */
p.pen_frame_flag = 0x20; p.pen.subreport_list[0].value = 0xe0;
p.pen.subreport_list[0].id =
UCLOGIC_RDESC_V1_FRAME_ID;
} }
goto output; goto output;
} }
...@@ -992,7 +974,7 @@ int uclogic_params_init(struct uclogic_params *params, ...@@ -992,7 +974,7 @@ int uclogic_params_init(struct uclogic_params *params,
case VID_PID(USB_VENDOR_ID_HUION, case VID_PID(USB_VENDOR_ID_HUION,
USB_DEVICE_ID_HUION_TABLET): USB_DEVICE_ID_HUION_TABLET):
case VID_PID(USB_VENDOR_ID_HUION, case VID_PID(USB_VENDOR_ID_HUION,
USB_DEVICE_ID_HUION_HS64): USB_DEVICE_ID_HUION_TABLET2):
case VID_PID(USB_VENDOR_ID_UCLOGIC, case VID_PID(USB_VENDOR_ID_UCLOGIC,
USB_DEVICE_ID_HUION_TABLET): USB_DEVICE_ID_HUION_TABLET):
case VID_PID(USB_VENDOR_ID_UCLOGIC, case VID_PID(USB_VENDOR_ID_UCLOGIC,
...@@ -1032,8 +1014,7 @@ int uclogic_params_init(struct uclogic_params *params, ...@@ -1032,8 +1014,7 @@ int uclogic_params_init(struct uclogic_params *params,
uclogic_params_init_invalid(&p); uclogic_params_init_invalid(&p);
} }
} else { } else {
/* TODO: Consider marking the interface invalid */ uclogic_params_init_invalid(&p);
uclogic_params_init_with_pen_unused(&p);
} }
break; break;
case VID_PID(USB_VENDOR_ID_UGEE, case VID_PID(USB_VENDOR_ID_UGEE,
...@@ -1048,15 +1029,14 @@ int uclogic_params_init(struct uclogic_params *params, ...@@ -1048,15 +1029,14 @@ int uclogic_params_init(struct uclogic_params *params,
} }
/* Initialize frame parameters */ /* Initialize frame parameters */
rc = uclogic_params_frame_init_with_desc( rc = uclogic_params_frame_init_with_desc(
&p.frame, &p.frame_list[0],
uclogic_rdesc_xppen_deco01_frame_arr, uclogic_rdesc_xppen_deco01_frame_arr,
uclogic_rdesc_xppen_deco01_frame_size, uclogic_rdesc_xppen_deco01_frame_size,
0); 0);
if (rc != 0) if (rc != 0)
goto cleanup; goto cleanup;
} else { } else {
/* TODO: Consider marking the interface invalid */ uclogic_params_init_invalid(&p);
uclogic_params_init_with_pen_unused(&p);
} }
break; break;
case VID_PID(USB_VENDOR_ID_TRUST, case VID_PID(USB_VENDOR_ID_TRUST,
...@@ -1075,19 +1055,19 @@ int uclogic_params_init(struct uclogic_params *params, ...@@ -1075,19 +1055,19 @@ int uclogic_params_init(struct uclogic_params *params,
goto cleanup; goto cleanup;
} else if (found) { } else if (found) {
rc = uclogic_params_frame_init_with_desc( rc = uclogic_params_frame_init_with_desc(
&p.frame, &p.frame_list[0],
uclogic_rdesc_ugee_g5_frame_arr, uclogic_rdesc_ugee_g5_frame_arr,
uclogic_rdesc_ugee_g5_frame_size, uclogic_rdesc_ugee_g5_frame_size,
UCLOGIC_RDESC_UGEE_G5_FRAME_ID); UCLOGIC_RDESC_UGEE_G5_FRAME_ID);
if (rc != 0) { if (rc != 0) {
hid_err(hdev, hid_err(hdev,
"failed creating buttonpad parameters: %d\n", "failed creating frame parameters: %d\n",
rc); rc);
goto cleanup; goto cleanup;
} }
p.frame.re_lsb = p.frame_list[0].re_lsb =
UCLOGIC_RDESC_UGEE_G5_FRAME_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; UCLOGIC_RDESC_UGEE_G5_FRAME_DEV_ID_BYTE;
} else { } else {
hid_warn(hdev, "pen parameters not found"); hid_warn(hdev, "pen parameters not found");
...@@ -1109,13 +1089,13 @@ int uclogic_params_init(struct uclogic_params *params, ...@@ -1109,13 +1089,13 @@ int uclogic_params_init(struct uclogic_params *params,
goto cleanup; goto cleanup;
} else if (found) { } else if (found) {
rc = uclogic_params_frame_init_with_desc( rc = uclogic_params_frame_init_with_desc(
&p.frame, &p.frame_list[0],
uclogic_rdesc_ugee_ex07_buttonpad_arr, uclogic_rdesc_ugee_ex07_frame_arr,
uclogic_rdesc_ugee_ex07_buttonpad_size, uclogic_rdesc_ugee_ex07_frame_size,
0); 0);
if (rc != 0) { if (rc != 0) {
hid_err(hdev, hid_err(hdev,
"failed creating buttonpad parameters: %d\n", "failed creating frame parameters: %d\n",
rc); rc);
goto cleanup; goto cleanup;
} }
......
...@@ -33,6 +33,25 @@ enum uclogic_params_pen_inrange { ...@@ -33,6 +33,25 @@ enum uclogic_params_pen_inrange {
extern const char *uclogic_params_pen_inrange_to_str( extern const char *uclogic_params_pen_inrange_to_str(
enum uclogic_params_pen_inrange inrange); 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. * Tablet interface's pen input parameters.
* *
...@@ -54,6 +73,8 @@ struct uclogic_params_pen { ...@@ -54,6 +73,8 @@ struct uclogic_params_pen {
unsigned int desc_size; unsigned int desc_size;
/* Report ID, if reports should be tweaked, zero if not */ /* Report ID, if reports should be tweaked, zero if not */
unsigned int id; 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 */ /* Type of in-range reporting, only valid if "id" is not zero */
enum uclogic_params_pen_inrange inrange; enum uclogic_params_pen_inrange inrange;
/* /*
...@@ -62,6 +83,12 @@ struct uclogic_params_pen { ...@@ -62,6 +83,12 @@ struct uclogic_params_pen {
* Only valid if "id" is not zero. * Only valid if "id" is not zero.
*/ */
bool fragmented_hires; 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 { ...@@ -132,28 +159,16 @@ struct uclogic_params {
* Only valid, if "desc_ptr" is not NULL. * Only valid, if "desc_ptr" is not NULL.
*/ */
unsigned int desc_size; 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. * Pen parameters and optional report descriptor part.
* Only valid if "pen_unused" is valid and false.
*/
struct uclogic_params_pen pen;
/*
* Frame control parameters and optional report descriptor part.
* Only valid, if "invalid" is false. * Only valid, if "invalid" is false.
*/ */
struct uclogic_params_frame frame; struct uclogic_params_pen pen;
/* /*
* Bitmask matching frame controls "sub-report" flag in the second * The list of frame control parameters and optional report descriptor
* byte of the pen report, or zero if it's not expected. * parts. Only valid, if "invalid" is false.
* 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 */ /* Initialize a tablet interface and discover its parameters */
...@@ -165,36 +180,37 @@ extern int uclogic_params_init(struct uclogic_params *params, ...@@ -165,36 +180,37 @@ extern int uclogic_params_init(struct uclogic_params *params,
".invalid = %s\n" \ ".invalid = %s\n" \
".desc_ptr = %p\n" \ ".desc_ptr = %p\n" \
".desc_size = %u\n" \ ".desc_size = %u\n" \
".pen_unused = %s\n" \
".pen.desc_ptr = %p\n" \ ".pen.desc_ptr = %p\n" \
".pen.desc_size = %u\n" \ ".pen.desc_size = %u\n" \
".pen.id = %u\n" \ ".pen.id = %u\n" \
".pen.subreport_list[0] = {0x%02hhx, %hhu}\n" \
".pen.inrange = %s\n" \ ".pen.inrange = %s\n" \
".pen.fragmented_hires = %s\n" \ ".pen.fragmented_hires = %s\n" \
".frame.desc_ptr = %p\n" \ ".pen.tilt_y_flipped = %s\n" \
".frame.desc_size = %u\n" \ ".frame_list[0].desc_ptr = %p\n" \
".frame.id = %u\n" \ ".frame_list[0].desc_size = %u\n" \
".frame.re_lsb = %u\n" \ ".frame_list[0].id = %u\n" \
".frame.dev_id_byte = %u\n" \ ".frame_list[0].re_lsb = %u\n" \
".pen_frame_flag = 0x%02x\n" ".frame_list[0].dev_id_byte = %u\n"
/* Tablet interface parameters *printf format arguments */ /* Tablet interface parameters *printf format arguments */
#define UCLOGIC_PARAMS_FMT_ARGS(_params) \ #define UCLOGIC_PARAMS_FMT_ARGS(_params) \
((_params)->invalid ? "true" : "false"), \ ((_params)->invalid ? "true" : "false"), \
(_params)->desc_ptr, \ (_params)->desc_ptr, \
(_params)->desc_size, \ (_params)->desc_size, \
((_params)->pen_unused ? "true" : "false"), \
(_params)->pen.desc_ptr, \ (_params)->pen.desc_ptr, \
(_params)->pen.desc_size, \ (_params)->pen.desc_size, \
(_params)->pen.id, \ (_params)->pen.id, \
(_params)->pen.subreport_list[0].value, \
(_params)->pen.subreport_list[0].id, \
uclogic_params_pen_inrange_to_str((_params)->pen.inrange), \ uclogic_params_pen_inrange_to_str((_params)->pen.inrange), \
((_params)->pen.fragmented_hires ? "true" : "false"), \ ((_params)->pen.fragmented_hires ? "true" : "false"), \
(_params)->frame.desc_ptr, \ ((_params)->pen.tilt_y_flipped ? "true" : "false"), \
(_params)->frame.desc_size, \ (_params)->frame_list[0].desc_ptr, \
(_params)->frame.id, \ (_params)->frame_list[0].desc_size, \
(_params)->frame.re_lsb, \ (_params)->frame_list[0].id, \
(_params)->frame.dev_id_byte, \ (_params)->frame_list[0].re_lsb, \
(_params)->pen_frame_flag (_params)->frame_list[0].dev_id_byte
/* Get a replacement report descriptor for a tablet's interface. */ /* Get a replacement report descriptor for a tablet's interface. */
extern int uclogic_params_get_desc(const struct uclogic_params *params, extern int uclogic_params_get_desc(const struct uclogic_params *params,
......
...@@ -532,7 +532,7 @@ const size_t uclogic_rdesc_twha60_fixed1_size = ...@@ -532,7 +532,7 @@ const size_t uclogic_rdesc_twha60_fixed1_size =
sizeof(uclogic_rdesc_twha60_fixed1_arr); sizeof(uclogic_rdesc_twha60_fixed1_arr);
/* Fixed report descriptor template for (tweaked) v1 pen reports */ /* 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), */ 0x05, 0x0D, /* Usage Page (Digitizer), */
0x09, 0x02, /* Usage (Pen), */ 0x09, 0x02, /* Usage (Pen), */
0xA1, 0x01, /* Collection (Application), */ 0xA1, 0x01, /* Collection (Application), */
...@@ -582,11 +582,11 @@ const __u8 uclogic_rdesc_pen_v1_template_arr[] = { ...@@ -582,11 +582,11 @@ const __u8 uclogic_rdesc_pen_v1_template_arr[] = {
0xC0 /* End Collection */ 0xC0 /* End Collection */
}; };
const size_t uclogic_rdesc_pen_v1_template_size = const size_t uclogic_rdesc_v1_pen_template_size =
sizeof(uclogic_rdesc_pen_v1_template_arr); sizeof(uclogic_rdesc_v1_pen_template_arr);
/* Fixed report descriptor template for (tweaked) v2 pen reports */ /* 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), */ 0x05, 0x0D, /* Usage Page (Digitizer), */
0x09, 0x02, /* Usage (Pen), */ 0x09, 0x02, /* Usage (Pen), */
0xA1, 0x01, /* Collection (Application), */ 0xA1, 0x01, /* Collection (Application), */
...@@ -633,25 +633,35 @@ const __u8 uclogic_rdesc_pen_v2_template_arr[] = { ...@@ -633,25 +633,35 @@ const __u8 uclogic_rdesc_pen_v2_template_arr[] = {
0x27, UCLOGIC_RDESC_PEN_PH(PRESSURE_LM), 0x27, UCLOGIC_RDESC_PEN_PH(PRESSURE_LM),
/* Logical Maximum (PLACEHOLDER), */ /* Logical Maximum (PLACEHOLDER), */
0x81, 0x02, /* Input (Variable), */ 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, */
0xC0 /* End Collection */ 0xC0 /* End Collection */
}; };
const size_t uclogic_rdesc_pen_v2_template_size = const size_t uclogic_rdesc_v2_pen_template_size =
sizeof(uclogic_rdesc_pen_v2_template_arr); 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 * @_id: The report ID to use.
* the end of the report, in bits. * @_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), */ \ 0x05, 0x01, /* Usage Page (Desktop), */ \
0x09, 0x07, /* Usage (Keypad), */ \ 0x09, 0x07, /* Usage (Keypad), */ \
0xA1, 0x01, /* Collection (Application), */ \ 0xA1, 0x01, /* Collection (Application), */ \
0x85, 0xF7, /* Report ID (247), */ \ 0x85, (_id), /* Report ID (_id), */ \
0x14, /* Logical Minimum (0), */ \ 0x14, /* Logical Minimum (0), */ \
0x25, 0x01, /* Logical Maximum (1), */ \ 0x25, 0x01, /* Logical Maximum (1), */ \
0x75, 0x01, /* Report Size (1), */ \ 0x75, 0x01, /* Report Size (1), */ \
...@@ -679,30 +689,31 @@ const size_t uclogic_rdesc_pen_v2_template_size = ...@@ -679,30 +689,31 @@ const size_t uclogic_rdesc_pen_v2_template_size =
0xA0, /* Collection (Physical), */ \ 0xA0, /* Collection (Physical), */ \
0x05, 0x09, /* Usage Page (Button), */ \ 0x05, 0x09, /* Usage Page (Button), */ \
0x19, 0x01, /* Usage Minimum (01h), */ \ 0x19, 0x01, /* Usage Minimum (01h), */ \
0x29, 0x02, /* Usage Maximum (02h), */ \ 0x29, 0x03, /* Usage Maximum (03h), */ \
0x95, 0x02, /* Report Count (2), */ \ 0x95, 0x03, /* Report Count (3), */ \
0x81, 0x02, /* Input (Variable), */ \ 0x81, 0x02, /* Input (Variable), */ \
0x95, _padding, /* Report Count (_padding), */ \ 0x95, ((_size) * 8 - 45), \
/* Report Count (padding), */ \
0x81, 0x01, /* Input (Constant), */ \ 0x81, 0x01, /* Input (Constant), */ \
0xC0, /* End Collection, */ \ 0xC0, /* End Collection, */ \
0xC0 /* End Collection */ 0xC0 /* End Collection */
/* Fixed report descriptor for (tweaked) v1 buttonpad reports */ /* Fixed report descriptor for (tweaked) v1 frame reports */
const __u8 uclogic_rdesc_buttonpad_v1_arr[] = { const __u8 uclogic_rdesc_v1_frame_arr[] = {
UCLOGIC_RDESC_BUTTONPAD_BYTES(20) UCLOGIC_RDESC_FRAME_BYTES(UCLOGIC_RDESC_V1_FRAME_ID, 8)
}; };
const size_t uclogic_rdesc_buttonpad_v1_size = const size_t uclogic_rdesc_v1_frame_size =
sizeof(uclogic_rdesc_buttonpad_v1_arr); sizeof(uclogic_rdesc_v1_frame_arr);
/* Fixed report descriptor for (tweaked) v2 buttonpad reports */ /* Fixed report descriptor for (tweaked) v2 frame reports */
const __u8 uclogic_rdesc_buttonpad_v2_arr[] = { const __u8 uclogic_rdesc_v2_frame_arr[] = {
UCLOGIC_RDESC_BUTTONPAD_BYTES(52) UCLOGIC_RDESC_FRAME_BYTES(UCLOGIC_RDESC_V2_FRAME_ID, 12)
}; };
const size_t uclogic_rdesc_buttonpad_v2_size = const size_t uclogic_rdesc_v2_frame_size =
sizeof(uclogic_rdesc_buttonpad_v2_arr); sizeof(uclogic_rdesc_v2_frame_arr);
/* Fixed report descriptor for Ugee EX07 buttonpad */ /* Fixed report descriptor for Ugee EX07 frame */
const __u8 uclogic_rdesc_ugee_ex07_buttonpad_arr[] = { const __u8 uclogic_rdesc_ugee_ex07_frame_arr[] = {
0x05, 0x01, /* Usage Page (Desktop), */ 0x05, 0x01, /* Usage Page (Desktop), */
0x09, 0x07, /* Usage (Keypad), */ 0x09, 0x07, /* Usage (Keypad), */
0xA1, 0x01, /* Collection (Application), */ 0xA1, 0x01, /* Collection (Application), */
...@@ -725,8 +736,8 @@ const __u8 uclogic_rdesc_ugee_ex07_buttonpad_arr[] = { ...@@ -725,8 +736,8 @@ const __u8 uclogic_rdesc_ugee_ex07_buttonpad_arr[] = {
0xC0, /* End Collection, */ 0xC0, /* End Collection, */
0xC0 /* End Collection */ 0xC0 /* End Collection */
}; };
const size_t uclogic_rdesc_ugee_ex07_buttonpad_size = const size_t uclogic_rdesc_ugee_ex07_frame_size =
sizeof(uclogic_rdesc_ugee_ex07_buttonpad_arr); sizeof(uclogic_rdesc_ugee_ex07_frame_arr);
/* Fixed report descriptor for Ugee G5 frame controls */ /* Fixed report descriptor for Ugee G5 frame controls */
const __u8 uclogic_rdesc_ugee_g5_frame_arr[] = { const __u8 uclogic_rdesc_ugee_g5_frame_arr[] = {
......
...@@ -104,36 +104,36 @@ enum uclogic_rdesc_pen_ph_id { ...@@ -104,36 +104,36 @@ enum uclogic_rdesc_pen_ph_id {
UCLOGIC_RDESC_PH_HEAD, UCLOGIC_RDESC_PEN_PH_ID_##_ID UCLOGIC_RDESC_PH_HEAD, UCLOGIC_RDESC_PEN_PH_ID_##_ID
/* Report ID for v1 pen reports */ /* 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 */ /* Fixed report descriptor template for (tweaked) v1 pen reports */
extern const __u8 uclogic_rdesc_pen_v1_template_arr[]; extern const __u8 uclogic_rdesc_v1_pen_template_arr[];
extern const size_t uclogic_rdesc_pen_v1_template_size; extern const size_t uclogic_rdesc_v1_pen_template_size;
/* Report ID for v2 pen reports */ /* 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 */ /* Fixed report descriptor template for (tweaked) v2 pen reports */
extern const __u8 uclogic_rdesc_pen_v2_template_arr[]; extern const __u8 uclogic_rdesc_v2_pen_template_arr[];
extern const size_t uclogic_rdesc_pen_v2_template_size; extern const size_t uclogic_rdesc_v2_pen_template_size;
/* Fixed report descriptor for (tweaked) v1 buttonpad reports */ /* Report ID for tweaked v1 frame reports */
extern const __u8 uclogic_rdesc_buttonpad_v1_arr[]; #define UCLOGIC_RDESC_V1_FRAME_ID 0xf7
extern const size_t uclogic_rdesc_buttonpad_v1_size;
/* Report ID for tweaked v1 buttonpad reports */ /* Fixed report descriptor for (tweaked) v1 frame reports */
#define UCLOGIC_RDESC_BUTTONPAD_V1_ID 0xf7 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 */ /* Report ID for tweaked v2 frame reports */
extern const __u8 uclogic_rdesc_buttonpad_v2_arr[]; #define UCLOGIC_RDESC_V2_FRAME_ID 0xf7
extern const size_t uclogic_rdesc_buttonpad_v2_size;
/* Report ID for tweaked v2 buttonpad reports */ /* Fixed report descriptor for (tweaked) v2 frame reports */
#define UCLOGIC_RDESC_BUTTONPAD_V2_ID 0xf7 extern const __u8 uclogic_rdesc_v2_frame_arr[];
extern const size_t uclogic_rdesc_v2_frame_size;
/* Fixed report descriptor for Ugee EX07 buttonpad */ /* Fixed report descriptor for Ugee EX07 frame */
extern const __u8 uclogic_rdesc_ugee_ex07_buttonpad_arr[]; extern const __u8 uclogic_rdesc_ugee_ex07_frame_arr[];
extern const size_t uclogic_rdesc_ugee_ex07_buttonpad_size; extern const size_t uclogic_rdesc_ugee_ex07_frame_size;
/* Fixed report descriptor for XP-Pen Deco 01 frame controls */ /* Fixed report descriptor for XP-Pen Deco 01 frame controls */
extern const __u8 uclogic_rdesc_xppen_deco01_frame_arr[]; extern const __u8 uclogic_rdesc_xppen_deco01_frame_arr[];
......
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/hid.h> #include <linux/hid.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <asm/unaligned.h>
#include "../hid-ids.h" #include "../hid-ids.h"
#include "i2c-hid.h" #include "i2c-hid.h"
...@@ -47,6 +48,15 @@ ...@@ -47,6 +48,15 @@
#define I2C_HID_QUIRK_BAD_INPUT_SIZE BIT(6) #define I2C_HID_QUIRK_BAD_INPUT_SIZE BIT(6)
#define I2C_HID_QUIRK_NO_WAKEUP_AFTER_RESET BIT(7) #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 */ /* flags */
#define I2C_HID_STARTED 0 #define I2C_HID_STARTED 0
...@@ -84,60 +94,11 @@ struct i2c_hid_desc { ...@@ -84,60 +94,11 @@ struct i2c_hid_desc {
__le32 reserved; __le32 reserved;
} __packed; } __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 */ /* The main device structure */
struct i2c_hid { struct i2c_hid {
struct i2c_client *client; /* i2c client */ struct i2c_client *client; /* i2c client */
struct hid_device *hid; /* pointer to corresponding HID dev */ 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 __le16 wHIDDescRegister; /* location of the i2c
* register of the HID * register of the HID
* descriptor. */ * descriptor. */
...@@ -145,7 +106,6 @@ struct i2c_hid { ...@@ -145,7 +106,6 @@ struct i2c_hid {
u8 *inbuf; /* Input buffer */ u8 *inbuf; /* Input buffer */
u8 *rawbuf; /* Raw Input buffer */ u8 *rawbuf; /* Raw Input buffer */
u8 *cmdbuf; /* Command buffer */ u8 *cmdbuf; /* Command buffer */
u8 *argsbuf; /* Command arguments buffer */
unsigned long flags; /* device flags */ unsigned long flags; /* device flags */
unsigned long quirks; /* Various quirks */ unsigned long quirks; /* Various quirks */
...@@ -207,196 +167,228 @@ static u32 i2c_hid_lookup_quirk(const u16 idVendor, const u16 idProduct) ...@@ -207,196 +167,228 @@ static u32 i2c_hid_lookup_quirk(const u16 idVendor, const u16 idProduct)
return quirks; return quirks;
} }
static int __i2c_hid_command(struct i2c_client *client, static int i2c_hid_xfer(struct i2c_hid *ihid,
const struct i2c_hid_cmd *command, u8 reportID, u8 *send_buf, int send_len, u8 *recv_buf, int recv_len)
u8 reportType, u8 *args, int args_len,
unsigned char *buf_recv, int data_len)
{ {
struct i2c_hid *ihid = i2c_get_clientdata(client); struct i2c_client *client = ihid->client;
union command *cmd = (union command *)ihid->cmdbuf; struct i2c_msg msgs[2] = { 0 };
int n = 0;
int ret; int ret;
struct i2c_msg msg[2];
int msg_num = 1;
int length = command->length; if (send_len) {
bool wait = command->wait; i2c_hid_dbg(ihid, "%s: cmd=%*ph\n",
unsigned int registerIndex = command->registerIndex; __func__, send_len, send_buf);
/* special case for hid_descr_cmd */ msgs[n].addr = client->addr;
if (command == &hid_descr_cmd) { msgs[n].flags = (client->flags & I2C_M_TEN) | I2C_M_DMA_SAFE;
cmd->c.reg = ihid->wHIDDescRegister; msgs[n].len = send_len;
} else { msgs[n].buf = send_buf;
cmd->data[0] = ihid->hdesc_buffer[registerIndex]; n++;
cmd->data[1] = ihid->hdesc_buffer[registerIndex + 1];
} }
if (length > 2) { if (recv_len) {
cmd->c.opcode = command->opcode; msgs[n].addr = client->addr;
cmd->c.reportTypeID = reportID | reportType << 4; 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); set_bit(I2C_HID_READ_PENDING, &ihid->flags);
} }
if (wait) ret = i2c_transfer(client->adapter, msgs, n);
set_bit(I2C_HID_RESET_PENDING, &ihid->flags);
ret = i2c_transfer(client->adapter, msg, msg_num);
if (data_len > 0) if (recv_len)
clear_bit(I2C_HID_READ_PENDING, &ihid->flags); clear_bit(I2C_HID_READ_PENDING, &ihid->flags);
if (ret != msg_num) if (ret != n)
return ret < 0 ? ret : -EIO; return ret < 0 ? ret : -EIO;
ret = 0; return 0;
}
if (wait && (ihid->quirks & I2C_HID_QUIRK_NO_IRQ_AFTER_RESET)) { static int i2c_hid_read_register(struct i2c_hid *ihid, __le16 reg,
msleep(100); void *buf, size_t len)
} else if (wait) { {
i2c_hid_dbg(ihid, "%s: waiting...\n", __func__); *(__le16 *)ihid->cmdbuf = reg;
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; return i2c_hid_xfer(ihid, ihid->cmdbuf, sizeof(__le16), buf, len);
} }
static int i2c_hid_command(struct i2c_client *client, static size_t i2c_hid_encode_command(u8 *buf, u8 opcode,
const struct i2c_hid_cmd *command, int report_type, int report_id)
unsigned char *buf_recv, int data_len)
{ {
return __i2c_hid_command(client, command, 0, 0, NULL, 0, size_t length = 0;
buf_recv, data_len);
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_client *client, u8 reportType, static int i2c_hid_get_report(struct i2c_hid *ihid,
u8 reportID, unsigned char *buf_recv, int data_len) u8 report_type, u8 report_id,
u8 *recv_buf, size_t recv_len)
{ {
struct i2c_hid *ihid = i2c_get_clientdata(client); size_t length = 0;
u8 args[3]; size_t ret_count;
int ret; int error;
int args_len = 0;
u16 readRegister = le16_to_cpu(ihid->hdesc.wDataRegister);
i2c_hid_dbg(ihid, "%s\n", __func__); i2c_hid_dbg(ihid, "%s\n", __func__);
if (reportID >= 0x0F) { /* Command register goes first */
args[args_len++] = reportID; *(__le16 *)ihid->cmdbuf = ihid->hdesc.wCommandRegister;
reportID = 0x0F; 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;
} }
args[args_len++] = readRegister & 0xFF; /* The buffer is sufficiently aligned */
args[args_len++] = readRegister >> 8; ret_count = le16_to_cpup((__le16 *)ihid->rawbuf);
ret = __i2c_hid_command(client, &hid_get_report_cmd, reportID, /* Check for empty report response */
reportType, args, args_len, buf_recv, data_len); if (ret_count <= sizeof(__le16))
if (ret) { return 0;
dev_err(&client->dev,
"failed to retrieve report from device.\n"); recv_len = min(recv_len, ret_count - sizeof(__le16));
return ret; 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 0; 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 * i2c_hid_set_or_send_report: forward an incoming report to the device
* @client: the i2c_client of the device * @ihid: the i2c hid device
* @reportType: 0x03 for HID_FEATURE_REPORT ; 0x02 for HID_OUTPUT_REPORT * @report_type: 0x03 for HID_FEATURE_REPORT ; 0x02 for HID_OUTPUT_REPORT
* @reportID: the report ID * @report_id: the report ID
* @buf: the actual data to transfer, without the report ID * @buf: the actual data to transfer, without the report ID
* @data_len: size of buf * @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, static int i2c_hid_set_or_send_report(struct i2c_hid *ihid,
u8 reportID, unsigned char *buf, size_t data_len, bool use_data) u8 report_type, u8 report_id,
const u8 *buf, size_t data_len,
bool do_set)
{ {
struct i2c_hid *ihid = i2c_get_clientdata(client); size_t length = 0;
u8 *args = ihid->argsbuf; int error;
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;
i2c_hid_dbg(ihid, "%s\n", __func__); i2c_hid_dbg(ihid, "%s\n", __func__);
if (data_len > ihid->bufsize) if (data_len > ihid->bufsize)
return -EINVAL; return -EINVAL;
size = 2 /* size */ + if (!do_set && le16_to_cpu(ihid->hdesc.wMaxOutputLength) == 0)
(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)
return -ENOSYS; return -ENOSYS;
if (reportID >= 0x0F) { if (do_set) {
args[index++] = reportID; /* Command register goes first */
reportID = 0x0F; *(__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);
/* /*
* use the data register for feature reports or if the device does not * Report data will go into the data register. Because
* support the output register * command can be either 2 or 3 bytes destination for
* the data register may be not aligned.
*/ */
if (use_data) { put_unaligned_le16(le16_to_cpu(ihid->hdesc.wDataRegister),
args[index++] = dataRegister & 0xFF; ihid->cmdbuf + length);
args[index++] = dataRegister >> 8; length += sizeof(__le16);
hidcmd = &hid_set_report_cmd;
} else { } else {
args[index++] = outputRegister & 0xFF; /*
args[index++] = outputRegister >> 8; * With simple "send report" all data goes into the output
hidcmd = &hid_no_cmd; * register.
*/
*(__le16 *)ihid->cmdbuf = ihid->hdesc.wOutputRegister;
length += sizeof(__le16);
} }
args[index++] = size & 0xFF; length += i2c_hid_format_report(ihid->cmdbuf + length,
args[index++] = size >> 8; report_id, buf, data_len);
if (reportID) error = i2c_hid_xfer(ihid, ihid->cmdbuf, length, NULL, 0);
args[index++] = reportID; if (error) {
dev_err(&ihid->client->dev,
memcpy(&args[index], buf, data_len); "failed to set a report to device: %d\n", error);
return error;
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;
} }
return data_len; 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; int ret;
i2c_hid_dbg(ihid, "%s\n", __func__); i2c_hid_dbg(ihid, "%s\n", __func__);
...@@ -408,18 +400,17 @@ static int i2c_hid_set_power(struct i2c_client *client, int power_state) ...@@ -408,18 +400,17 @@ static int i2c_hid_set_power(struct i2c_client *client, int power_state)
*/ */
if (power_state == I2C_HID_PWR_ON && if (power_state == I2C_HID_PWR_ON &&
ihid->quirks & I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV) { 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 */ /* Device was already activated */
if (!ret) if (!ret)
goto set_pwr_exit; goto set_pwr_exit;
} }
ret = __i2c_hid_command(client, &hid_set_power_cmd, power_state, ret = i2c_hid_set_power_command(ihid, power_state);
0, NULL, 0, NULL, 0);
if (ret) 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: set_pwr_exit:
...@@ -438,9 +429,49 @@ static int i2c_hid_set_power(struct i2c_client *client, int power_state) ...@@ -438,9 +429,49 @@ static int i2c_hid_set_power(struct i2c_client *client, int power_state)
return ret; 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; int ret;
i2c_hid_dbg(ihid, "%s\n", __func__); i2c_hid_dbg(ihid, "%s\n", __func__);
...@@ -452,22 +483,21 @@ static int i2c_hid_hwreset(struct i2c_client *client) ...@@ -452,22 +483,21 @@ static int i2c_hid_hwreset(struct i2c_client *client)
*/ */
mutex_lock(&ihid->reset_lock); 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) if (ret)
goto out_unlock; goto out_unlock;
i2c_hid_dbg(ihid, "resetting...\n"); ret = i2c_hid_execute_reset(ihid);
ret = i2c_hid_command(client, &hid_reset_cmd, NULL, 0);
if (ret) { if (ret) {
dev_err(&client->dev, "failed to reset device.\n"); dev_err(&ihid->client->dev,
i2c_hid_set_power(client, I2C_HID_PWR_SLEEP); "failed to reset device: %d\n", ret);
i2c_hid_set_power(ihid, I2C_HID_PWR_SLEEP);
goto out_unlock; goto out_unlock;
} }
/* At least some SIS devices need this after reset */ /* At least some SIS devices need this after reset */
if (!(ihid->quirks & I2C_HID_QUIRK_NO_WAKEUP_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: out_unlock:
mutex_unlock(&ihid->reset_lock); mutex_unlock(&ihid->reset_lock);
...@@ -476,9 +506,9 @@ static int i2c_hid_hwreset(struct i2c_client *client) ...@@ -476,9 +506,9 @@ static int i2c_hid_hwreset(struct i2c_client *client)
static void i2c_hid_get_input(struct i2c_hid *ihid) static void i2c_hid_get_input(struct i2c_hid *ihid)
{ {
u16 size = le16_to_cpu(ihid->hdesc.wMaxInputLength);
u16 ret_size;
int ret; int ret;
u32 ret_size;
int size = le16_to_cpu(ihid->hdesc.wMaxInputLength);
if (size > ihid->bufsize) if (size > ihid->bufsize)
size = ihid->bufsize; size = ihid->bufsize;
...@@ -493,8 +523,8 @@ static void i2c_hid_get_input(struct i2c_hid *ihid) ...@@ -493,8 +523,8 @@ static void i2c_hid_get_input(struct i2c_hid *ihid)
return; 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) { if (!ret_size) {
/* host or device initiated RESET completed */ /* host or device initiated RESET completed */
if (test_and_clear_bit(I2C_HID_RESET_PENDING, &ihid->flags)) 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) ...@@ -502,19 +532,20 @@ static void i2c_hid_get_input(struct i2c_hid *ihid)
return; return;
} }
if (ihid->quirks & I2C_HID_QUIRK_BOGUS_IRQ && ret_size == 0xffff) { if ((ihid->quirks & I2C_HID_QUIRK_BOGUS_IRQ) && ret_size == 0xffff) {
dev_warn_once(&ihid->client->dev, "%s: IRQ triggered but " dev_warn_once(&ihid->client->dev,
"there's no data\n", __func__); "%s: IRQ triggered but there's no data\n",
__func__);
return; 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) { if (ihid->quirks & I2C_HID_QUIRK_BAD_INPUT_SIZE) {
ihid->inbuf[0] = size & 0xff; *(__le16 *)ihid->inbuf = cpu_to_le16(size);
ihid->inbuf[1] = size >> 8;
ret_size = size; ret_size = size;
} else { } 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); __func__, size, ret_size);
return; return;
} }
...@@ -525,8 +556,9 @@ static void i2c_hid_get_input(struct i2c_hid *ihid) ...@@ -525,8 +556,9 @@ static void i2c_hid_get_input(struct i2c_hid *ihid)
if (test_bit(I2C_HID_STARTED, &ihid->flags)) { if (test_bit(I2C_HID_STARTED, &ihid->flags)) {
pm_wakeup_event(&ihid->client->dev, 0); pm_wakeup_event(&ihid->client->dev, 0);
hid_input_report(ihid->hid, HID_INPUT_REPORT, ihid->inbuf + 2, hid_input_report(ihid->hid, HID_INPUT_REPORT,
ret_size - 2, 1); ihid->inbuf + sizeof(__le16),
ret_size - sizeof(__le16), 1);
} }
return; return;
...@@ -572,31 +604,33 @@ static void i2c_hid_free_buffers(struct i2c_hid *ihid) ...@@ -572,31 +604,33 @@ static void i2c_hid_free_buffers(struct i2c_hid *ihid)
{ {
kfree(ihid->inbuf); kfree(ihid->inbuf);
kfree(ihid->rawbuf); kfree(ihid->rawbuf);
kfree(ihid->argsbuf);
kfree(ihid->cmdbuf); kfree(ihid->cmdbuf);
ihid->inbuf = NULL; ihid->inbuf = NULL;
ihid->rawbuf = NULL; ihid->rawbuf = NULL;
ihid->cmdbuf = NULL; ihid->cmdbuf = NULL;
ihid->argsbuf = NULL;
ihid->bufsize = 0; ihid->bufsize = 0;
} }
static int i2c_hid_alloc_buffers(struct i2c_hid *ihid, size_t report_size) 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 */ * The worst case is computed from the set_report command with a
int args_len = sizeof(__u8) + /* ReportID */ * reportID > 15 and the maximum report length.
sizeof(__u8) + /* optional ReportID byte */ */
sizeof(__u16) + /* data register */ int cmd_len = sizeof(__le16) + /* command register */
sizeof(__u16) + /* size of the report */ sizeof(u8) + /* encoded report type/ID */
report_size; /* report */ 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->inbuf = kzalloc(report_size, GFP_KERNEL);
ihid->rawbuf = kzalloc(report_size, GFP_KERNEL); ihid->rawbuf = kzalloc(report_size, GFP_KERNEL);
ihid->argsbuf = kzalloc(args_len, GFP_KERNEL); ihid->cmdbuf = kzalloc(cmd_len, GFP_KERNEL);
ihid->cmdbuf = kzalloc(sizeof(union command) + args_len, GFP_KERNEL);
if (!ihid->inbuf || !ihid->rawbuf || !ihid->argsbuf || !ihid->cmdbuf) { if (!ihid->inbuf || !ihid->rawbuf || !ihid->cmdbuf) {
i2c_hid_free_buffers(ihid); i2c_hid_free_buffers(ihid);
return -ENOMEM; return -ENOMEM;
} }
...@@ -607,43 +641,39 @@ static int i2c_hid_alloc_buffers(struct i2c_hid *ihid, size_t report_size) ...@@ -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, static int i2c_hid_get_raw_report(struct hid_device *hid,
unsigned char report_number, __u8 *buf, size_t count, u8 report_type, u8 report_id,
unsigned char report_type) u8 *buf, size_t count)
{ {
struct i2c_client *client = hid->driver_data; struct i2c_client *client = hid->driver_data;
struct i2c_hid *ihid = i2c_get_clientdata(client); struct i2c_hid *ihid = i2c_get_clientdata(client);
size_t ret_count, ask_count; int ret_count;
int ret;
if (report_type == HID_OUTPUT_REPORT) if (report_type == HID_OUTPUT_REPORT)
return -EINVAL; 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_type == HID_FEATURE_REPORT ? 0x03 : 0x01,
report_number, ihid->rawbuf, ask_count); report_id, buf, count);
if (ret < 0)
return ret;
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 */ if (ret_count > 0 && !report_id)
count = min(count, ret_count - 2); ret_count++;
memcpy(buf, ihid->rawbuf + 2, count);
return count; return ret_count;
} }
static int i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf, static int i2c_hid_output_raw_report(struct hid_device *hid, u8 report_type,
size_t count, unsigned char report_type, bool use_data) const u8 *buf, size_t count, bool do_set)
{ {
struct i2c_client *client = hid->driver_data; struct i2c_client *client = hid->driver_data;
struct i2c_hid *ihid = i2c_get_clientdata(client); struct i2c_hid *ihid = i2c_get_clientdata(client);
...@@ -655,27 +685,28 @@ static int i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf, ...@@ -655,27 +685,28 @@ static int i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf,
mutex_lock(&ihid->reset_lock); mutex_lock(&ihid->reset_lock);
if (report_id) { /*
buf++; * Note that both numbered and unnumbered reports passed here
count--; * 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
ret = i2c_hid_set_or_send_report(client, * everything properly.
*/
ret = i2c_hid_set_or_send_report(ihid,
report_type == HID_FEATURE_REPORT ? 0x03 : 0x02, 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) if (ret >= 0)
ret++; /* add report_id to the number of transfered bytes */ ret++; /* add report_id to the number of transferred bytes */
mutex_unlock(&ihid->reset_lock); mutex_unlock(&ihid->reset_lock);
return ret; return ret;
} }
static int i2c_hid_output_report(struct hid_device *hid, __u8 *buf, static int i2c_hid_output_report(struct hid_device *hid, u8 *buf, size_t count)
size_t count)
{ {
return i2c_hid_output_raw_report(hid, buf, count, HID_OUTPUT_REPORT, return i2c_hid_output_raw_report(hid, HID_OUTPUT_REPORT, buf, count,
false); false);
} }
...@@ -685,11 +716,11 @@ 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) { switch (reqtype) {
case HID_REQ_GET_REPORT: 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: case HID_REQ_SET_REPORT:
if (buf[0] != reportnum) if (buf[0] != reportnum)
return -EINVAL; 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: default:
return -EIO; return -EIO;
} }
...@@ -715,7 +746,7 @@ static int i2c_hid_parse(struct hid_device *hid) ...@@ -715,7 +746,7 @@ static int i2c_hid_parse(struct hid_device *hid)
} }
do { do {
ret = i2c_hid_hwreset(client); ret = i2c_hid_hwreset(ihid);
if (ret) if (ret)
msleep(1000); msleep(1000);
} while (tries-- > 0 && ret); } while (tries-- > 0 && ret);
...@@ -739,7 +770,8 @@ static int i2c_hid_parse(struct hid_device *hid) ...@@ -739,7 +770,8 @@ static int i2c_hid_parse(struct hid_device *hid)
i2c_hid_dbg(ihid, "asking HID report descriptor\n"); i2c_hid_dbg(ihid, "asking HID report descriptor\n");
ret = i2c_hid_command(client, &hid_report_descr_cmd, ret = i2c_hid_read_register(ihid,
ihid->hdesc.wReportDescRegister,
rdesc, rsize); rdesc, rsize);
if (ret) { if (ret) {
hid_err(hid, "reading report descriptor failed\n"); hid_err(hid, "reading report descriptor failed\n");
...@@ -850,7 +882,7 @@ static int i2c_hid_fetch_hid_descriptor(struct i2c_hid *ihid) ...@@ -850,7 +882,7 @@ static int i2c_hid_fetch_hid_descriptor(struct i2c_hid *ihid)
struct i2c_client *client = ihid->client; struct i2c_client *client = ihid->client;
struct i2c_hid_desc *hdesc = &ihid->hdesc; struct i2c_hid_desc *hdesc = &ihid->hdesc;
unsigned int dsize; unsigned int dsize;
int ret; int error;
/* i2c hid fetch using a fixed descriptor size (30 bytes) */ /* i2c hid fetch using a fixed descriptor size (30 bytes) */
if (i2c_hid_get_dmi_i2c_hid_desc_override(client->name)) { 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) ...@@ -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); *i2c_hid_get_dmi_i2c_hid_desc_override(client->name);
} else { } else {
i2c_hid_dbg(ihid, "Fetching the HID descriptor\n"); i2c_hid_dbg(ihid, "Fetching the HID descriptor\n");
ret = i2c_hid_command(client, &hid_descr_cmd, error = i2c_hid_read_register(ihid,
ihid->hdesc_buffer, ihid->wHIDDescRegister,
sizeof(struct i2c_hid_desc)); &ihid->hdesc,
if (ret) { sizeof(ihid->hdesc));
dev_err(&client->dev, "hid_descr_cmd failed\n"); if (error) {
dev_err(&ihid->client->dev,
"failed to fetch HID descriptor: %d\n",
error);
return -ENODEV; return -ENODEV;
} }
} }
...@@ -873,7 +908,7 @@ static int i2c_hid_fetch_hid_descriptor(struct i2c_hid *ihid) ...@@ -873,7 +908,7 @@ static int i2c_hid_fetch_hid_descriptor(struct i2c_hid *ihid)
* bytes 2-3 -> bcdVersion (has to be 1.00) */ * bytes 2-3 -> bcdVersion (has to be 1.00) */
/* check bcdVersion == 1.0 */ /* check bcdVersion == 1.0 */
if (le16_to_cpu(hdesc->bcdVersion) != 0x0100) { if (le16_to_cpu(hdesc->bcdVersion) != 0x0100) {
dev_err(&client->dev, dev_err(&ihid->client->dev,
"unexpected HID descriptor bcdVersion (0x%04hx)\n", "unexpected HID descriptor bcdVersion (0x%04hx)\n",
le16_to_cpu(hdesc->bcdVersion)); le16_to_cpu(hdesc->bcdVersion));
return -ENODEV; return -ENODEV;
...@@ -882,11 +917,11 @@ static int i2c_hid_fetch_hid_descriptor(struct i2c_hid *ihid) ...@@ -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 */ /* Descriptor length should be 30 bytes as per the specification */
dsize = le16_to_cpu(hdesc->wHIDDescLength); dsize = le16_to_cpu(hdesc->wHIDDescLength);
if (dsize != sizeof(struct i2c_hid_desc)) { if (dsize != sizeof(struct i2c_hid_desc)) {
dev_err(&client->dev, "weird size of HID descriptor (%u)\n", dev_err(&ihid->client->dev,
dsize); "weird size of HID descriptor (%u)\n", dsize);
return -ENODEV; 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; return 0;
} }
...@@ -1052,7 +1087,7 @@ void i2c_hid_core_shutdown(struct i2c_client *client) ...@@ -1052,7 +1087,7 @@ void i2c_hid_core_shutdown(struct i2c_client *client)
{ {
struct i2c_hid *ihid = i2c_get_clientdata(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); free_irq(client->irq, ihid);
i2c_hid_core_shutdown_tail(ihid); i2c_hid_core_shutdown_tail(ihid);
...@@ -1073,7 +1108,7 @@ static int i2c_hid_core_suspend(struct device *dev) ...@@ -1073,7 +1108,7 @@ static int i2c_hid_core_suspend(struct device *dev)
return ret; return ret;
/* Save some power */ /* 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); disable_irq(client->irq);
...@@ -1121,9 +1156,9 @@ static int i2c_hid_core_resume(struct device *dev) ...@@ -1121,9 +1156,9 @@ static int i2c_hid_core_resume(struct device *dev)
* let's still reset them here. * let's still reset them here.
*/ */
if (ihid->quirks & I2C_HID_QUIRK_RESET_ON_RESUME) if (ihid->quirks & I2C_HID_QUIRK_RESET_ON_RESUME)
ret = i2c_hid_hwreset(client); ret = i2c_hid_hwreset(ihid);
else else
ret = i2c_hid_set_power(client, I2C_HID_PWR_ON); ret = i2c_hid_set_power(ihid, I2C_HID_PWR_ON);
if (ret) if (ret)
return ret; return ret;
......
...@@ -661,21 +661,12 @@ static int ish_fw_xfer_direct_dma(struct ishtp_cl_data *client_data, ...@@ -661,21 +661,12 @@ static int ish_fw_xfer_direct_dma(struct ishtp_cl_data *client_data,
*/ */
payload_max_size &= ~(L1_CACHE_BYTES - 1); 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) { if (!dma_buf) {
client_data->flag_retry = true; client_data->flag_retry = true;
return -ENOMEM; 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.hdr.command = LOADER_CMD_XFER_FRAGMENT;
ldr_xfer_dma_frag.fragment.xfer_mode = LOADER_XFER_MODE_DIRECT_DMA; ldr_xfer_dma_frag.fragment.xfer_mode = LOADER_XFER_MODE_DIRECT_DMA;
ldr_xfer_dma_frag.ddr_phys_addr = (u64)dma_buf_phy; 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, ...@@ -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; ldr_xfer_dma_frag.fragment.size = fragment_size;
memcpy(dma_buf, &fw->data[fragment_offset], fragment_size); memcpy(dma_buf, &fw->data[fragment_offset], fragment_size);
dma_sync_single_for_device(devc, dma_buf_phy, /* Flush cache to be sure the data is in main memory. */
payload_max_size,
DMA_TO_DEVICE);
/*
* Flush cache here because the dma_sync_single_for_device()
* does not do for x86.
*/
clflush_cache_range(dma_buf, payload_max_size); clflush_cache_range(dma_buf, payload_max_size);
dev_dbg(cl_data_to_dev(client_data), 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, ...@@ -725,15 +709,8 @@ static int ish_fw_xfer_direct_dma(struct ishtp_cl_data *client_data,
fragment_offset += fragment_size; 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: end_err_resp_buf_release:
/* Free ISH buffer if not done already, in error case */ dma_free_coherent(devc, payload_max_size, dma_buf, dma_buf_phy);
dma_unmap_single(devc, dma_buf_phy, payload_max_size, DMA_TO_DEVICE);
end_err_dma_buf_release:
kfree(dma_buf);
return rv; return rv;
} }
......
...@@ -347,7 +347,7 @@ struct hid_item { ...@@ -347,7 +347,7 @@ struct hid_item {
*/ */
#define MAX_USBHID_BOOT_QUIRKS 4 #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_NOTOUCH BIT(1)
#define HID_QUIRK_IGNORE BIT(2) #define HID_QUIRK_IGNORE BIT(2)
#define HID_QUIRK_NOGET BIT(3) #define HID_QUIRK_NOGET BIT(3)
...@@ -476,31 +476,50 @@ struct hid_field { ...@@ -476,31 +476,50 @@ struct hid_field {
unsigned report_count; /* number of this field in the report */ unsigned report_count; /* number of this field in the report */
unsigned report_type; /* (input,output,feature) */ unsigned report_type; /* (input,output,feature) */
__s32 *value; /* last known value(s) */ __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_minimum;
__s32 logical_maximum; __s32 logical_maximum;
__s32 physical_minimum; __s32 physical_minimum;
__s32 physical_maximum; __s32 physical_maximum;
__s32 unit_exponent; __s32 unit_exponent;
unsigned unit; unsigned unit;
bool ignored; /* this field is ignored in this event */
struct hid_report *report; /* associated report */ struct hid_report *report; /* associated report */
unsigned index; /* index into report->field[] */ unsigned index; /* index into report->field[] */
/* hidinput data */ /* hidinput data */
struct hid_input *hidinput; /* associated input structure */ struct hid_input *hidinput; /* associated input structure */
__u16 dpad; /* dpad input code */ __u16 dpad; /* dpad input code */
unsigned int slot_idx; /* slot index in a report */
}; };
#define HID_MAX_FIELDS 256 #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 hid_report {
struct list_head list; struct list_head list;
struct list_head hidinput_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 id; /* id of this report */
unsigned int type; /* report type */ unsigned int type; /* report type */
unsigned int application; /* application usage for this report */ unsigned int application; /* application usage for this report */
struct hid_field *field[HID_MAX_FIELDS]; /* fields of the 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 maxfield; /* maximum valid field index */
unsigned size; /* size of the report (bits) */ unsigned size; /* size of the report (bits) */
struct hid_device *device; /* associated device */ 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 #define HID_MAX_IDS 256
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment