Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 52d4e661 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid: (21 commits)
  HID: hidraw_connect() memleak fix
  HID: add hidraw interface
  USB HID: provide hook for hidraw write()
  HID: hiddev: Add 32bit ioctl compatibilty
  HID: Add GeneralTouch touchscreen to the blacklist
  HID: add support for Microsoft Wireless Laser Keyboard 6000
  Input: add KEY_LOGOFF
  USBHID: report descriptor fix for MacBook JIS keyboard
  HID: trivial fixes in hid-debug
  HID: fix input mapping for Microsoft Ergonomic Keyboard
  HID: use hid-plff driver for GreenAsia 0e8f:0003 devices
  USBHID: Add HID_QUIRK_NOGET for ELO Touch Screen 2700 display
  HID: enable hiddev for the SantaRosa MacBookPro IR receiver
  USBHID: add CM109 device to blacklist
  HID: Report usage codes of keys as EV_MSC scancode events
  HID: ignore all non-LED usages in output fields in hid-input
  HID: fix whitespace damage
  HID: add support for Thrustmaster FGT Force Feedback wheel
  HID: minimal autosuspend support for USB HID devices
  HID: add support for Microsoft Natural Ergonomic Keyboard 4000
  ...
parents f248488b d057fd4c
Loading
Loading
Loading
Loading
+19 −0
Original line number Diff line number Diff line
@@ -46,6 +46,25 @@ config HID_DEBUG

	If unsure, say N

config HIDRAW
	bool "/dev/hidraw raw HID device support"
	depends on HID
	---help---
	Say Y here if you want to support HID devices (from the USB
	specification standpoint) that aren't strictly user interface
	devices, like monitor controls and Uninterruptable Power Supplies.

	This module supports these devices separately using a separate
	event interface on /dev/hidraw.

	There is also a /dev/hiddev configuration option in the USB HID
	configuration menu. In comparison to hiddev, this device does not process
	the hid events at all (no parsing, no lookups). This lets applications
	to work on raw hid events when they want to, and avoid using transport-specific
	userspace libhid/libusb libraries.

	If unsure, say Y.

source "drivers/hid/usbhid/Kconfig"

endif # HID_SUPPORT
+2 −0
Original line number Diff line number Diff line
@@ -4,7 +4,9 @@
hid-objs			:= hid-core.o hid-input.o

obj-$(CONFIG_HID)		+= hid.o

hid-$(CONFIG_HID_DEBUG)		+= hid-debug.o
hid-$(CONFIG_HIDRAW)		+= hidraw.o

obj-$(CONFIG_USB_HID)		+= usbhid/
obj-$(CONFIG_USB_MOUSE)		+= usbhid/
+16 −0
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@
#include <linux/hid.h>
#include <linux/hiddev.h>
#include <linux/hid-debug.h>
#include <linux/hidraw.h>

/*
 * Version Information
@@ -979,6 +980,8 @@ int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int i

	if ((hid->claimed & HID_CLAIMED_HIDDEV) && hid->hiddev_report_event)
		hid->hiddev_report_event(hid, report);
	if (hid->claimed & HID_CLAIMED_HIDRAW)
		hidraw_report_event(hid, data, size);

	for (n = 0; n < report->maxfield; n++)
		hid_input_field(hid, report->field[n], data, interrupt);
@@ -990,5 +993,18 @@ int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int i
}
EXPORT_SYMBOL_GPL(hid_input_report);

static int __init hid_init(void)
{
	return hidraw_init();
}

static void __exit hid_exit(void)
{
	hidraw_exit();
}

module_init(hid_init);
module_exit(hid_exit);

MODULE_LICENSE(DRIVER_LICENSE);
+20 −20
Original line number Diff line number Diff line
@@ -34,7 +34,7 @@
struct hid_usage_entry {
	unsigned  page;
	unsigned  usage;
	char     *description;
	const char     *description;
};

static const struct hid_usage_entry hid_usage_table[] = {
@@ -365,8 +365,8 @@ void hid_resolv_usage(unsigned usage) {
}
EXPORT_SYMBOL_GPL(hid_resolv_usage);

__inline__ static void tab(int n) {
	while (n--) printk(" ");
static void tab(int n) {
	printk(KERN_DEBUG "%*s", n, "");
}

void hid_dump_field(struct hid_field *field, int n) {
@@ -401,8 +401,8 @@ void hid_dump_field(struct hid_field *field, int n) {
		tab(n); printk("Unit Exponent(%d)\n", field->unit_exponent);
	}
	if (field->unit) {
		char *systems[5] = { "None", "SI Linear", "SI Rotation", "English Linear", "English Rotation" };
		char *units[5][8] = {
		static const char *systems[5] = { "None", "SI Linear", "SI Rotation", "English Linear", "English Rotation" };
		static const char *units[5][8] = {
			{ "None", "None", "None", "None", "None", "None", "None", "None" },
			{ "None", "Centimeter", "Gram", "Seconds", "Kelvin",     "Ampere", "Candela", "None" },
			{ "None", "Radians",    "Gram", "Seconds", "Kelvin",     "Ampere", "Candela", "None" },
@@ -457,7 +457,7 @@ void hid_dump_field(struct hid_field *field, int n) {
	printk("%s", HID_MAIN_ITEM_RELATIVE & j ? "Relative " : "Absolute ");
	printk("%s", HID_MAIN_ITEM_WRAP & j ? "Wrap " : "");
	printk("%s", HID_MAIN_ITEM_NONLINEAR & j ? "NonLinear " : "");
	printk("%s", HID_MAIN_ITEM_NO_PREFERRED & j ? "NoPrefferedState " : "");
	printk("%s", HID_MAIN_ITEM_NO_PREFERRED & j ? "NoPreferredState " : "");
	printk("%s", HID_MAIN_ITEM_NULL_STATE & j ? "NullState " : "");
	printk("%s", HID_MAIN_ITEM_VOLATILE & j ? "Volatile " : "");
	printk("%s", HID_MAIN_ITEM_BUFFERED_BYTE & j ? "BufferedByte " : "");
@@ -470,7 +470,7 @@ void hid_dump_device(struct hid_device *device) {
	struct hid_report *report;
	struct list_head *list;
	unsigned i,k;
	static char *table[] = {"INPUT", "OUTPUT", "FEATURE"};
	static const char *table[] = {"INPUT", "OUTPUT", "FEATURE"};

	if (!hid_debug)
		return;
@@ -501,13 +501,13 @@ void hid_dump_input(struct hid_usage *usage, __s32 value) {
	if (!hid_debug)
		return;

	printk("hid-debug: input ");
	printk(KERN_DEBUG "hid-debug: input ");
	hid_resolv_usage(usage->hid);
	printk(" = %d\n", value);
}
EXPORT_SYMBOL_GPL(hid_dump_input);

static char *events[EV_MAX + 1] = {
static const char *events[EV_MAX + 1] = {
	[EV_SYN] = "Sync",			[EV_KEY] = "Key",
	[EV_REL] = "Relative",			[EV_ABS] = "Absolute",
	[EV_MSC] = "Misc",			[EV_LED] = "LED",
@@ -516,10 +516,10 @@ static char *events[EV_MAX + 1] = {
	[EV_FF_STATUS] = "ForceFeedbackStatus",
};

static char *syncs[2] = {
static const char *syncs[2] = {
	[SYN_REPORT] = "Report",		[SYN_CONFIG] = "Config",
};
static char *keys[KEY_MAX + 1] = {
static const char *keys[KEY_MAX + 1] = {
	[KEY_RESERVED] = "Reserved",		[KEY_ESC] = "Esc",
	[KEY_1] = "1",				[KEY_2] = "2",
	[KEY_3] = "3",				[KEY_4] = "4",
@@ -697,7 +697,8 @@ static char *keys[KEY_MAX + 1] = {
	[KEY_DEL_LINE] = "DeleteLine",
	[KEY_SEND] = "Send",			[KEY_REPLY] = "Reply",
	[KEY_FORWARDMAIL] = "ForwardMail",	[KEY_SAVE] = "Save",
	[KEY_DOCUMENTS] = "Documents",
	[KEY_DOCUMENTS] = "Documents",		[KEY_SPELLCHECK] = "SpellCheck",
	[KEY_LOGOFF] = "Logoff",
	[KEY_FN] = "Fn",			[KEY_FN_ESC] = "Fn+ESC",
	[KEY_FN_1] = "Fn+1",			[KEY_FN_2] = "Fn+2",
	[KEY_FN_B] = "Fn+B",			[KEY_FN_D] = "Fn+D",
@@ -715,7 +716,7 @@ static char *keys[KEY_MAX + 1] = {
	[KEY_SWITCHVIDEOMODE] = "SwitchVideoMode",
};

static char *relatives[REL_MAX + 1] = {
static const char *relatives[REL_MAX + 1] = {
	[REL_X] = "X",			[REL_Y] = "Y",
	[REL_Z] = "Z",			[REL_RX] = "Rx",
	[REL_RY] = "Ry",		[REL_RZ] = "Rz",
@@ -723,7 +724,7 @@ static char *relatives[REL_MAX + 1] = {
	[REL_WHEEL] = "Wheel",		[REL_MISC] = "Misc",
};

static char *absolutes[ABS_MAX + 1] = {
static const char *absolutes[ABS_MAX + 1] = {
	[ABS_X] = "X",			[ABS_Y] = "Y",
	[ABS_Z] = "Z",			[ABS_RX] = "Rx",
	[ABS_RY] = "Ry",		[ABS_RZ] = "Rz",
@@ -739,12 +740,12 @@ static char *absolutes[ABS_MAX + 1] = {
	[ABS_VOLUME] = "Volume",	[ABS_MISC] = "Misc",
};

static char *misc[MSC_MAX + 1] = {
static const char *misc[MSC_MAX + 1] = {
	[MSC_SERIAL] = "Serial",	[MSC_PULSELED] = "Pulseled",
	[MSC_GESTURE] = "Gesture",	[MSC_RAW] = "RawData"
};

static char *leds[LED_MAX + 1] = {
static const char *leds[LED_MAX + 1] = {
	[LED_NUML] = "NumLock",		[LED_CAPSL] = "CapsLock",
	[LED_SCROLLL] = "ScrollLock",	[LED_COMPOSE] = "Compose",
	[LED_KANA] = "Kana",		[LED_SLEEP] = "Sleep",
@@ -752,16 +753,16 @@ static char *leds[LED_MAX + 1] = {
	[LED_MISC] = "Misc",
};

static char *repeats[REP_MAX + 1] = {
static const char *repeats[REP_MAX + 1] = {
	[REP_DELAY] = "Delay",		[REP_PERIOD] = "Period"
};

static char *sounds[SND_MAX + 1] = {
static const char *sounds[SND_MAX + 1] = {
	[SND_CLICK] = "Click",		[SND_BELL] = "Bell",
	[SND_TONE] = "Tone"
};

static char **names[EV_MAX + 1] = {
static const char **names[EV_MAX + 1] = {
	[EV_SYN] = syncs,			[EV_KEY] = keys,
	[EV_REL] = relatives,			[EV_ABS] = absolutes,
	[EV_MSC] = misc,			[EV_LED] = leds,
@@ -777,4 +778,3 @@ void hid_resolv_event(__u8 type, __u16 code) {
		names[type] ? (names[type][code] ? names[type][code] : "?") : "?");
}
EXPORT_SYMBOL_GPL(hid_resolv_event);
+83 −9
Original line number Diff line number Diff line
@@ -53,7 +53,7 @@ static const unsigned char hid_keyboard[256] = {
	115,114,unk,unk,unk,121,unk, 89, 93,124, 92, 94, 95,unk,unk,unk,
	122,123, 90, 91, 85,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,
	unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,
	unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,
	unk,unk,unk,unk,unk,unk,179,180,unk,unk,unk,unk,unk,unk,unk,unk,
	unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,
	unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,
	 29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113,
@@ -86,6 +86,10 @@ static const struct {
#define map_abs_clear(c)	do { map_abs(c); clear_bit(c, bit); } while (0)
#define map_key_clear(c)	do { map_key(c); clear_bit(c, bit); } while (0)

/* hardware needing special handling due to colliding MSVENDOR page usages */
#define IS_CHICONY_TACTICAL_PAD(x) (x->vendor == 0x04f2 && device->product == 0x0418)
#define IS_MS_KB(x) (x->vendor == 0x045e && (x->product == 0x00db || x->product == 0x00f9))

#ifdef CONFIG_USB_HIDINPUT_POWERBOOK

struct hidinput_key_translation {
@@ -351,6 +355,13 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
	if (field->flags & HID_MAIN_ITEM_CONSTANT)
		goto ignore;

	/* only LED usages are supported in output fields */
	if (field->report_type == HID_OUTPUT_REPORT &&
			(usage->hid & HID_USAGE_PAGE) != HID_UP_LED) {
		dbg_hid_line(" [non-LED output field] ");
		goto ignore;
	}

	switch (usage->hid & HID_USAGE_PAGE) {

		case HID_UP_UNDEFINED:
@@ -595,6 +606,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
				case 0x0f6: map_key_clear(KEY_NEXT);		break;
				case 0x0fa: map_key_clear(KEY_BACK);		break;

				case 0x182: map_key_clear(KEY_BOOKMARKS);	break;
				case 0x183: map_key_clear(KEY_CONFIG);		break;
				case 0x184: map_key_clear(KEY_WORDPROCESSOR);	break;
				case 0x185: map_key_clear(KEY_EDITOR);		break;
@@ -611,9 +623,13 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
				case 0x192: map_key_clear(KEY_CALC);		break;
				case 0x194: map_key_clear(KEY_FILE);		break;
				case 0x196: map_key_clear(KEY_WWW);		break;
				case 0x19c: map_key_clear(KEY_LOGOFF);		break;
				case 0x19e: map_key_clear(KEY_COFFEE);		break;
				case 0x1a6: map_key_clear(KEY_HELP);		break;
				case 0x1a7: map_key_clear(KEY_DOCUMENTS);	break;
				case 0x1ab: map_key_clear(KEY_SPELLCHECK);	break;
				case 0x1b6: map_key_clear(KEY_MEDIA);		break;
				case 0x1b7: map_key_clear(KEY_SOUND);		break;
				case 0x1bc: map_key_clear(KEY_MESSENGER);	break;
				case 0x1bd: map_key_clear(KEY_INFO);		break;
				case 0x201: map_key_clear(KEY_NEW);		break;
@@ -720,8 +736,15 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel

		case HID_UP_MSVENDOR:

			/* special case - Chicony Chicony KU-0418 tactical pad */
			if (device->vendor == 0x04f2 && device->product == 0x0418) {
			/* Unfortunately, there are multiple devices which
			 * emit usages from MSVENDOR page that require different
			 * handling. If this list grows too much in the future,
			 * more general handling will have to be introduced here
			 * (i.e. another blacklist).
			 */

			/* Chicony Chicony KU-0418 tactical pad */
			if (IS_CHICONY_TACTICAL_PAD(device)) {
				set_bit(EV_REP, input->evbit);
				switch(usage->hid & HID_USAGE) {
					case 0xff01: map_key_clear(BTN_1);		break;
@@ -737,6 +760,26 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
					case 0xff0b: map_key_clear(BTN_B);		break;
					default:    goto ignore;
				}

			/* Microsoft Natural Ergonomic Keyboard 4000 */
			} else if (IS_MS_KB(device)) {
				switch(usage->hid & HID_USAGE) {
					case 0xfd06:
						map_key_clear(KEY_CHAT);
						break;
					case 0xfd07:
						map_key_clear(KEY_PHONE);
						break;
					case 0xff05:
						set_bit(EV_REP, input->evbit);
						map_key_clear(KEY_F13);
						set_bit(KEY_F14, input->keybit);
						set_bit(KEY_F15, input->keybit);
						set_bit(KEY_F16, input->keybit);
						set_bit(KEY_F17, input->keybit);
						set_bit(KEY_F18, input->keybit);
					default:	goto ignore;
				}
			} else {
				goto ignore;
			}
@@ -888,6 +931,11 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
		set_bit(KEY_VOLUMEDOWN, input->keybit);
	}

	if (usage->type == EV_KEY) {
		set_bit(EV_MSC, input->evbit);
		set_bit(MSC_SCAN, input->mscbit);
	}

	hid_resolv_event(usage->type, usage->code);

	dbg_hid_line("\n");
@@ -991,6 +1039,29 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct
		return;
	}

	/* Handling MS keyboards special buttons */
	if (IS_MS_KB(hid) && usage->hid == (HID_UP_MSVENDOR | 0xff05)) {
		int key = 0;
		static int last_key = 0;
		switch (value) {
			case 0x01: key = KEY_F14; break;
			case 0x02: key = KEY_F15; break;
			case 0x04: key = KEY_F16; break;
			case 0x08: key = KEY_F17; break;
			case 0x10: key = KEY_F18; break;
			default: break;
		}
		if (key) {
			input_event(input, usage->type, key, 1);
			last_key = key;
		} else {
			input_event(input, usage->type, last_key, 0);
		}
	}
	/* report the usage code as scancode if the key status has changed */
	if (usage->type == EV_KEY && !!test_bit(usage->code, input->key) != value)
		input_event(input, EV_MSC, MSC_SCAN, usage->hid);

	input_event(input, usage->type, usage->code, value);

	if ((field->flags & HID_MAIN_ITEM_RELATIVE) && (usage->type == EV_KEY))
@@ -1051,6 +1122,9 @@ int hidinput_connect(struct hid_device *hid)
	int i, j, k;
	int max_report_type = HID_OUTPUT_REPORT;

	if (hid->quirks & HID_QUIRK_IGNORE_HIDINPUT)
		return -1;

	INIT_LIST_HEAD(&hid->inputs);

	for (i = 0; i < hid->maxcollection; i++)
Loading