Skip to content

Commit

Permalink
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel…
Browse files Browse the repository at this point in the history
…/git/jikos/hid

Pull HID updates from Jiri Kosina:

 - functionally equivalent cleanups for wacom driver, making the code
   more readable, from Benjamin Tissoires

 - a bunch of improvements and fixes for thingm driver from Heiner
   Kallweit

 - bugfixes to out-of-bound access for generic parsing functions (which
   have been there since ever) extract() and implement(), from Dmitry
   Torokhov

 - a lot of added / improved device support in sony, wacom, microsoft,
   multitouch and logitech driver, from various people

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid: (44 commits)
  HID: microsoft: Add ID for MS Wireless Comfort Keyboard
  hid: thingm: reorder calls in thingm_probe
  HID: i2c-hid: fix OOB write in i2c_hid_set_or_send_report()
  HID: multitouch: Release all touch slots on reset_resume
  HID: usbhid: enable NO_INIT_REPORTS quirk for Semico USB Keykoard2
  HID: penmount: report only one button for PenMount 6000 USB touchscreen controller
  HID: i2c-hid: Fix suspend/resume when already runtime suspended
  HID: i2c-hid: Add hid-over-i2c name to i2c id table
  HID: multitouch: force retrieving of Win8 signature blob
  HID: Support for CMedia CM6533 HID audio jack controls
  HID: thingm: improve locking
  HID: thingm: switch to managed version of led_classdev_register
  HID: thingm: remove workqueue
  HID: corsair: fix mapping of non-keyboard usages
  HID: wacom: close the wireless receiver on remove()
  HID: wacom: cleanup input devices
  HID: wacom: reuse wacom_parse_and_register() in wireless_work
  HID: wacom: move down wireless_work()
  HID: wacom: break out parsing of device and registering of input
  HID: wacom: break out wacom_intuos_get_tool_type
  ...
  • Loading branch information
Linus Torvalds committed Mar 18, 2016
2 parents 1a46712 + e1c9b9f commit d66435c
Show file tree
Hide file tree
Showing 19 changed files with 1,422 additions and 630 deletions.
6 changes: 6 additions & 0 deletions drivers/hid/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,12 @@ config HID_PRODIKEYS
multimedia keyboard, but will lack support for the musical keyboard
and some additional multimedia keys.

config HID_CMEDIA
tristate "CMedia CM6533 HID audio jack controls"
depends on HID
---help---
Support for CMedia CM6533 HID audio jack controls.

config HID_CP2112
tristate "Silicon Labs CP2112 HID USB-to-SMBus Bridge support"
depends on USB_HID && I2C && GPIOLIB
Expand Down
1 change: 1 addition & 0 deletions drivers/hid/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ obj-$(CONFIG_HID_BELKIN) += hid-belkin.o
obj-$(CONFIG_HID_BETOP_FF) += hid-betopff.o
obj-$(CONFIG_HID_CHERRY) += hid-cherry.o
obj-$(CONFIG_HID_CHICONY) += hid-chicony.o
obj-$(CONFIG_HID_CMEDIA) += hid-cmedia.o
obj-$(CONFIG_HID_CORSAIR) += hid-corsair.o
obj-$(CONFIG_HID_CP2112) += hid-cp2112.o
obj-$(CONFIG_HID_CYPRESS) += hid-cypress.o
Expand Down
168 changes: 168 additions & 0 deletions drivers/hid/hid-cmedia.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
/*
* HID driver for CMedia CM6533 audio jack controls
*
* Copyright (C) 2015 Ben Chen <ben_chen@bizlinktech.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/

#include <linux/device.h>
#include <linux/hid.h>
#include <linux/module.h>
#include "hid-ids.h"

MODULE_AUTHOR("Ben Chen");
MODULE_DESCRIPTION("CM6533 HID jack controls");
MODULE_LICENSE("GPL");

#define CM6533_JD_TYPE_COUNT 1
#define CM6533_JD_RAWEV_LEN 16
#define CM6533_JD_SFX_OFFSET 8

/*
*
*CM6533 audio jack HID raw events:
*
*Plug in:
*01000600 002083xx 080008c0 10000000
*about 3 seconds later...
*01000a00 002083xx 08000380 10000000
*01000600 002083xx 08000380 10000000
*
*Plug out:
*01000400 002083xx 080008c0 x0000000
*/

static const u8 ji_sfx[] = { 0x08, 0x00, 0x08, 0xc0 };
static const u8 ji_in[] = { 0x01, 0x00, 0x06, 0x00 };
static const u8 ji_out[] = { 0x01, 0x00, 0x04, 0x00 };

static int jack_switch_types[CM6533_JD_TYPE_COUNT] = {
SW_HEADPHONE_INSERT,
};

struct cmhid {
struct input_dev *input_dev;
struct hid_device *hid;
unsigned short switch_map[CM6533_JD_TYPE_COUNT];
};

static void hp_ev(struct hid_device *hid, struct cmhid *cm, int value)
{
input_report_switch(cm->input_dev, SW_HEADPHONE_INSERT, value);
input_sync(cm->input_dev);
}

static int cmhid_raw_event(struct hid_device *hid, struct hid_report *report,
u8 *data, int len)
{
struct cmhid *cm = hid_get_drvdata(hid);

if (len != CM6533_JD_RAWEV_LEN)
goto out;
if (memcmp(data+CM6533_JD_SFX_OFFSET, ji_sfx, sizeof(ji_sfx)))
goto out;

if (!memcmp(data, ji_out, sizeof(ji_out))) {
hp_ev(hid, cm, 0);
goto out;
}
if (!memcmp(data, ji_in, sizeof(ji_in))) {
hp_ev(hid, cm, 1);
goto out;
}

out:
return 0;
}

static int cmhid_input_configured(struct hid_device *hid,
struct hid_input *hidinput)
{
struct input_dev *input_dev = hidinput->input;
struct cmhid *cm = hid_get_drvdata(hid);
int i;

cm->input_dev = input_dev;
memcpy(cm->switch_map, jack_switch_types, sizeof(cm->switch_map));
input_dev->evbit[0] = BIT(EV_SW);
for (i = 0; i < CM6533_JD_TYPE_COUNT; i++)
input_set_capability(cm->input_dev,
EV_SW, jack_switch_types[i]);
return 0;
}

static int cmhid_input_mapping(struct hid_device *hid,
struct hid_input *hi, struct hid_field *field,
struct hid_usage *usage, unsigned long **bit, int *max)
{
return -1;
}

static int cmhid_probe(struct hid_device *hid, const struct hid_device_id *id)
{
int ret;
struct cmhid *cm;

cm = kzalloc(sizeof(struct cmhid), GFP_KERNEL);
if (!cm) {
ret = -ENOMEM;
goto allocfail;
}

cm->hid = hid;

hid->quirks |= HID_QUIRK_HIDINPUT_FORCE;
hid_set_drvdata(hid, cm);

ret = hid_parse(hid);
if (ret) {
hid_err(hid, "parse failed\n");
goto fail;
}

ret = hid_hw_start(hid, HID_CONNECT_DEFAULT | HID_CONNECT_HIDDEV_FORCE);
if (ret) {
hid_err(hid, "hw start failed\n");
goto fail;
}

return 0;
fail:
kfree(cm);
allocfail:
return ret;
}

static void cmhid_remove(struct hid_device *hid)
{
struct cmhid *cm = hid_get_drvdata(hid);

hid_hw_stop(hid);
kfree(cm);
}

static const struct hid_device_id cmhid_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_CMEDIA, USB_DEVICE_ID_CM6533) },
{ }
};
MODULE_DEVICE_TABLE(hid, cmhid_devices);

static struct hid_driver cmhid_driver = {
.name = "cm6533_jd",
.id_table = cmhid_devices,
.raw_event = cmhid_raw_event,
.input_configured = cmhid_input_configured,
.probe = cmhid_probe,
.remove = cmhid_remove,
.input_mapping = cmhid_input_mapping,
};
module_hid_driver(cmhid_driver);

104 changes: 77 additions & 27 deletions drivers/hid/hid-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -1075,28 +1075,45 @@ static u32 s32ton(__s32 value, unsigned n)
* Extract/implement a data field from/to a little endian report (bit array).
*
* Code sort-of follows HID spec:
* http://www.usb.org/developers/devclass_docs/HID1_11.pdf
* http://www.usb.org/developers/hidpage/HID1_11.pdf
*
* While the USB HID spec allows unlimited length bit fields in "report
* descriptors", most devices never use more than 16 bits.
* One model of UPS is claimed to report "LINEV" as a 32-bit field.
* Search linux-kernel and linux-usb-devel archives for "hid-core extract".
*/

__u32 hid_field_extract(const struct hid_device *hid, __u8 *report,
unsigned offset, unsigned n)
{
u64 x;
static u32 __extract(u8 *report, unsigned offset, int n)
{
unsigned int idx = offset / 8;
unsigned int bit_nr = 0;
unsigned int bit_shift = offset % 8;
int bits_to_copy = 8 - bit_shift;
u32 value = 0;
u32 mask = n < 32 ? (1U << n) - 1 : ~0U;

while (n > 0) {
value |= ((u32)report[idx] >> bit_shift) << bit_nr;
n -= bits_to_copy;
bit_nr += bits_to_copy;
bits_to_copy = 8;
bit_shift = 0;
idx++;
}

return value & mask;
}

if (n > 32)
u32 hid_field_extract(const struct hid_device *hid, u8 *report,
unsigned offset, unsigned n)
{
if (n > 32) {
hid_warn(hid, "hid_field_extract() called with n (%d) > 32! (%s)\n",
n, current->comm);
n = 32;
}

report += offset >> 3; /* adjust byte index */
offset &= 7; /* now only need bit offset into one byte */
x = get_unaligned_le64(report);
x = (x >> offset) & ((1ULL << n) - 1); /* extract bit field */
return (u32) x;
return __extract(report, offset, n);
}
EXPORT_SYMBOL_GPL(hid_field_extract);

Expand All @@ -1106,31 +1123,56 @@ EXPORT_SYMBOL_GPL(hid_field_extract);
* The data mangled in the bit stream remains in little endian
* order the whole time. It make more sense to talk about
* endianness of register values by considering a register
* a "cached" copy of the little endiad bit stream.
* a "cached" copy of the little endian bit stream.
*/
static void implement(const struct hid_device *hid, __u8 *report,
unsigned offset, unsigned n, __u32 value)

static void __implement(u8 *report, unsigned offset, int n, u32 value)
{
unsigned int idx = offset / 8;
unsigned int size = offset + n;
unsigned int bit_shift = offset % 8;
int bits_to_set = 8 - bit_shift;
u8 bit_mask = 0xff << bit_shift;

while (n - bits_to_set >= 0) {
report[idx] &= ~bit_mask;
report[idx] |= value << bit_shift;
value >>= bits_to_set;
n -= bits_to_set;
bits_to_set = 8;
bit_mask = 0xff;
bit_shift = 0;
idx++;
}

/* last nibble */
if (n) {
if (size % 8)
bit_mask &= (1U << (size % 8)) - 1;
report[idx] &= ~bit_mask;
report[idx] |= (value << bit_shift) & bit_mask;
}
}

static void implement(const struct hid_device *hid, u8 *report,
unsigned offset, unsigned n, u32 value)
{
u64 x;
u64 m = (1ULL << n) - 1;
u64 m;

if (n > 32)
if (n > 32) {
hid_warn(hid, "%s() called with n (%d) > 32! (%s)\n",
__func__, n, current->comm);
n = 32;
}

m = (1ULL << n) - 1;
if (value > m)
hid_warn(hid, "%s() called with too large value %d! (%s)\n",
__func__, value, current->comm);
WARN_ON(value > m);
value &= m;

report += offset >> 3;
offset &= 7;

x = get_unaligned_le64(report);
x &= ~(m << offset);
x |= ((u64)value) << offset;
put_unaligned_le64(x, report);
__implement(report, offset, n, value);
}

/*
Expand Down Expand Up @@ -1251,6 +1293,7 @@ static void hid_input_field(struct hid_device *hid, struct hid_field *field,
/* Ignore report if ErrorRollOver */
if (!(field->flags & HID_MAIN_ITEM_VARIABLE) &&
value[n] >= min && value[n] <= max &&
value[n] - min < field->maxusage &&
field->usage[value[n] - min].hid == HID_UP_KEYBOARD + 1)
goto exit;
}
Expand All @@ -1263,11 +1306,13 @@ static void hid_input_field(struct hid_device *hid, struct hid_field *field,
}

if (field->value[n] >= min && field->value[n] <= max
&& field->value[n] - min < field->maxusage
&& field->usage[field->value[n] - min].hid
&& search(value, field->value[n], count))
hid_process_event(hid, field, &field->usage[field->value[n] - min], 0, interrupt);

if (value[n] >= min && value[n] <= max
&& value[n] - min < field->maxusage
&& field->usage[value[n] - min].hid
&& search(field->value, value[n], count))
hid_process_event(hid, field, &field->usage[value[n] - min], 1, interrupt);
Expand Down Expand Up @@ -1891,6 +1936,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_ELITE_KBD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DUAL_ACTION) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD) },
Expand Down Expand Up @@ -1919,6 +1965,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD_BOOTLOADER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_MOUSE_4500) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_KEYBOARD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K_JP) },
Expand Down Expand Up @@ -2003,6 +2050,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGP_MOUSE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SINO_LITE, USB_DEVICE_ID_SINO_LITE_CONTROLLER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_SRWS1) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) },
{ HID_USB_DEVICE(USB_VENDOR_ID_THINGM, USB_DEVICE_ID_BLINK1) },
Expand Down Expand Up @@ -2051,6 +2099,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_WIIMOTE) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_WIIMOTE2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_RAZER, USB_DEVICE_ID_RAZER_BLADE_14) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CMEDIA, USB_DEVICE_ID_CM6533) },
{ }
};

Expand Down Expand Up @@ -2615,9 +2664,10 @@ int hid_add_device(struct hid_device *hdev)
/*
* Scan generic devices for group information
*/
if (hid_ignore_special_drivers ||
(!hdev->group &&
!hid_match_id(hdev, hid_have_special_driver))) {
if (hid_ignore_special_drivers) {
hdev->group = HID_GROUP_GENERIC;
} else if (!hdev->group &&
!hid_match_id(hdev, hid_have_special_driver)) {
ret = hid_scan_report(hdev);
if (ret)
hid_warn(hdev, "bad device descriptor (%d)\n", ret);
Expand Down
3 changes: 3 additions & 0 deletions drivers/hid/hid-corsair.c
Original file line number Diff line number Diff line change
Expand Up @@ -595,6 +595,9 @@ static int corsair_input_mapping(struct hid_device *dev,
{
int gkey;

if ((usage->hid & HID_USAGE_PAGE) != HID_UP_KEYBOARD)
return 0;

gkey = corsair_usage_to_gkey(usage->hid & HID_USAGE);
if (gkey != 0) {
hid_map_usage_clear(input, usage, bit, max, EV_KEY,
Expand Down
Loading

0 comments on commit d66435c

Please sign in to comment.