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

Commit 36b77410 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid:
  HID: handle cases of volume knobs generating relative values
  HID: Logitech keyboard 0xc311 needs reset leds quirk
  HID: support for logitech cordless desktop LX500 special mapping
  HID: fix autocentering of PID devices
  HID: separate quirks for report descriptor fixup
  HID: Add NOGET quirk for all NCR devices
  HID: support for Petalynx Maxter remote control
  HID: fix mismatch between hid-input HUT find/search mapping and the HUT
  HID: support for Gameron dual psx adaptor
  USB HID: avoid flush_scheduled_work()
  HID: Use menuconfig objects
  HID: force hid-input for Microsoft SideWinder GameVoice device
  HID: input mapping for Chicony KU-0418 tactical pad
  HID: make debugging output runtime-configurable
parents 71ba22fa feb485d4
Loading
Loading
Loading
Loading
+7 −3
Original line number Diff line number Diff line
#
# HID driver configuration
#
menu "HID Devices"
menuconfig HID_SUPPORT
	bool "HID Devices"
	depends on INPUT
	default y

if HID_SUPPORT

config HID
	tristate "Generic HID support"
@@ -24,6 +28,7 @@ config HID

config HID_DEBUG
	bool "HID debugging support"
	default y if !EMBEDDED
	depends on HID
	---help---
	This option lets the HID layer output diagnostics about its internal
@@ -38,5 +43,4 @@ config HID_DEBUG

source "drivers/hid/usbhid/Kconfig"

endmenu
endif # HID_SUPPORT
+47 −46
Original line number Diff line number Diff line
@@ -40,6 +40,13 @@
#define DRIVER_DESC "HID core driver"
#define DRIVER_LICENSE "GPL"

#ifdef CONFIG_HID_DEBUG
int hid_debug = 0;
module_param_named(debug, hid_debug, bool, 0600);
MODULE_PARM_DESC(debug, "Turn HID debugging mode on and off");
EXPORT_SYMBOL_GPL(hid_debug);
#endif

/*
 * Register a new report for a device.
 */
@@ -78,7 +85,7 @@ static struct hid_field *hid_register_field(struct hid_report *report, unsigned
	struct hid_field *field;

	if (report->maxfield == HID_MAX_FIELDS) {
		dbg("too many fields in report");
		dbg_hid("too many fields in report\n");
		return NULL;
	}

@@ -106,7 +113,7 @@ static int open_collection(struct hid_parser *parser, unsigned type)
	usage = parser->local.usage[0];

	if (parser->collection_stack_ptr == HID_COLLECTION_STACK_SIZE) {
		dbg("collection stack overflow");
		dbg_hid("collection stack overflow\n");
		return -1;
	}

@@ -114,7 +121,7 @@ static int open_collection(struct hid_parser *parser, unsigned type)
		collection = kmalloc(sizeof(struct hid_collection) *
				parser->device->collection_size * 2, GFP_KERNEL);
		if (collection == NULL) {
			dbg("failed to reallocate collection array");
			dbg_hid("failed to reallocate collection array\n");
			return -1;
		}
		memcpy(collection, parser->device->collection,
@@ -150,7 +157,7 @@ static int open_collection(struct hid_parser *parser, unsigned type)
static int close_collection(struct hid_parser *parser)
{
	if (!parser->collection_stack_ptr) {
		dbg("collection stack underflow");
		dbg_hid("collection stack underflow\n");
		return -1;
	}
	parser->collection_stack_ptr--;
@@ -178,7 +185,7 @@ static unsigned hid_lookup_collection(struct hid_parser *parser, unsigned type)
static int hid_add_usage(struct hid_parser *parser, unsigned usage)
{
	if (parser->local.usage_index >= HID_MAX_USAGES) {
		dbg("usage index exceeded");
		dbg_hid("usage index exceeded\n");
		return -1;
	}
	parser->local.usage[parser->local.usage_index] = usage;
@@ -202,12 +209,12 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign
	int i;

	if (!(report = hid_register_report(parser->device, report_type, parser->global.report_id))) {
		dbg("hid_register_report failed");
		dbg_hid("hid_register_report failed\n");
		return -1;
	}

	if (parser->global.logical_maximum < parser->global.logical_minimum) {
		dbg("logical range invalid %d %d", parser->global.logical_minimum, parser->global.logical_maximum);
		dbg_hid("logical range invalid %d %d\n", parser->global.logical_minimum, parser->global.logical_maximum);
		return -1;
	}

@@ -287,7 +294,7 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item)
		case HID_GLOBAL_ITEM_TAG_PUSH:

			if (parser->global_stack_ptr == HID_GLOBAL_STACK_SIZE) {
				dbg("global enviroment stack overflow");
				dbg_hid("global enviroment stack overflow\n");
				return -1;
			}

@@ -298,7 +305,7 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item)
		case HID_GLOBAL_ITEM_TAG_POP:

			if (!parser->global_stack_ptr) {
				dbg("global enviroment stack underflow");
				dbg_hid("global enviroment stack underflow\n");
				return -1;
			}

@@ -342,27 +349,27 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item)

		case HID_GLOBAL_ITEM_TAG_REPORT_SIZE:
			if ((parser->global.report_size = item_udata(item)) > 32) {
				dbg("invalid report_size %d", parser->global.report_size);
				dbg_hid("invalid report_size %d\n", parser->global.report_size);
				return -1;
			}
			return 0;

		case HID_GLOBAL_ITEM_TAG_REPORT_COUNT:
			if ((parser->global.report_count = item_udata(item)) > HID_MAX_USAGES) {
				dbg("invalid report_count %d", parser->global.report_count);
				dbg_hid("invalid report_count %d\n", parser->global.report_count);
				return -1;
			}
			return 0;

		case HID_GLOBAL_ITEM_TAG_REPORT_ID:
			if ((parser->global.report_id = item_udata(item)) == 0) {
				dbg("report_id 0 is invalid");
				dbg_hid("report_id 0 is invalid\n");
				return -1;
			}
			return 0;

		default:
			dbg("unknown global tag 0x%x", item->tag);
			dbg_hid("unknown global tag 0x%x\n", item->tag);
			return -1;
	}
}
@@ -377,7 +384,7 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item)
	unsigned n;

	if (item->size == 0) {
		dbg("item data expected for local item");
		dbg_hid("item data expected for local item\n");
		return -1;
	}

@@ -395,14 +402,14 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item)
				 * items and the first delimiter set.
				 */
				if (parser->local.delimiter_depth != 0) {
					dbg("nested delimiters");
					dbg_hid("nested delimiters\n");
					return -1;
				}
				parser->local.delimiter_depth++;
				parser->local.delimiter_branch++;
			} else {
				if (parser->local.delimiter_depth < 1) {
					dbg("bogus close delimiter");
					dbg_hid("bogus close delimiter\n");
					return -1;
				}
				parser->local.delimiter_depth--;
@@ -412,7 +419,7 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item)
		case HID_LOCAL_ITEM_TAG_USAGE:

			if (parser->local.delimiter_branch > 1) {
				dbg("alternative usage ignored");
				dbg_hid("alternative usage ignored\n");
				return 0;
			}

@@ -424,7 +431,7 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item)
		case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM:

			if (parser->local.delimiter_branch > 1) {
				dbg("alternative usage ignored");
				dbg_hid("alternative usage ignored\n");
				return 0;
			}

@@ -437,7 +444,7 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item)
		case HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM:

			if (parser->local.delimiter_branch > 1) {
				dbg("alternative usage ignored");
				dbg_hid("alternative usage ignored\n");
				return 0;
			}

@@ -446,14 +453,14 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item)

			for (n = parser->local.usage_minimum; n <= data; n++)
				if (hid_add_usage(parser, n)) {
					dbg("hid_add_usage failed\n");
					dbg_hid("hid_add_usage failed\n");
					return -1;
				}
			return 0;

		default:

			dbg("unknown local item tag 0x%x", item->tag);
			dbg_hid("unknown local item tag 0x%x\n", item->tag);
			return 0;
	}
	return 0;
@@ -487,7 +494,7 @@ static int hid_parser_main(struct hid_parser *parser, struct hid_item *item)
			ret = hid_add_field(parser, HID_FEATURE_REPORT, data);
			break;
		default:
			dbg("unknown main item tag 0x%x", item->tag);
			dbg_hid("unknown main item tag 0x%x\n", item->tag);
			ret = 0;
	}

@@ -502,7 +509,7 @@ static int hid_parser_main(struct hid_parser *parser, struct hid_item *item)

static int hid_parser_reserved(struct hid_parser *parser, struct hid_item *item)
{
	dbg("reserved item type, tag 0x%x", item->tag);
	dbg_hid("reserved item type, tag 0x%x\n", item->tag);
	return 0;
}

@@ -667,14 +674,14 @@ struct hid_device *hid_parse_report(__u8 *start, unsigned size)
	while ((start = fetch_item(start, end, &item)) != NULL) {

		if (item.format != HID_ITEM_FORMAT_SHORT) {
			dbg("unexpected long global item");
			dbg_hid("unexpected long global item\n");
			hid_free_device(device);
			vfree(parser);
			return NULL;
		}

		if (dispatch_type[item.type](parser, &item)) {
			dbg("item %u %u %u %u parsing failed\n",
			dbg_hid("item %u %u %u %u parsing failed\n",
				item.format, (unsigned)item.size, (unsigned)item.type, (unsigned)item.tag);
			hid_free_device(device);
			vfree(parser);
@@ -683,13 +690,13 @@ struct hid_device *hid_parse_report(__u8 *start, unsigned size)

		if (start == end) {
			if (parser->collection_stack_ptr) {
				dbg("unbalanced collection at end of report description");
				dbg_hid("unbalanced collection at end of report description\n");
				hid_free_device(device);
				vfree(parser);
				return NULL;
			}
			if (parser->local.delimiter_depth) {
				dbg("unbalanced delimiter at end of report description");
				dbg_hid("unbalanced delimiter at end of report description\n");
				hid_free_device(device);
				vfree(parser);
				return NULL;
@@ -699,7 +706,7 @@ struct hid_device *hid_parse_report(__u8 *start, unsigned size)
		}
	}

	dbg("item fetching failed at offset %d\n", (int)(end - start));
	dbg_hid("item fetching failed at offset %d\n", (int)(end - start));
	hid_free_device(device);
	vfree(parser);
	return NULL;
@@ -915,13 +922,13 @@ int hid_set_field(struct hid_field *field, unsigned offset, __s32 value)
	hid_dump_input(field->usage + offset, value);

	if (offset >= field->report_count) {
		dbg("offset (%d) exceeds report_count (%d)", offset, field->report_count);
		dbg_hid("offset (%d) exceeds report_count (%d)\n", offset, field->report_count);
		hid_dump_field(field, 8);
		return -1;
	}
	if (field->logical_minimum < 0) {
		if (value != snto32(s32ton(value, size), size)) {
			dbg("value %d is out of range", value);
			dbg_hid("value %d is out of range\n", value);
			return -1;
		}
	}
@@ -934,19 +941,17 @@ int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int i
{
	struct hid_report_enum *report_enum = hid->report_enum + type;
	struct hid_report *report;
	int n, rsize;
	int n, rsize, i;

	if (!hid)
		return -ENODEV;

	if (!size) {
		dbg("empty report");
		dbg_hid("empty report\n");
		return -1;
	}

#ifdef CONFIG_HID_DEBUG
	printk(KERN_DEBUG __FILE__ ": report (size %u) (%snumbered)\n", size, report_enum->numbered ? "" : "un");
#endif
	dbg_hid("report (size %u) (%snumbered)\n", size, report_enum->numbered ? "" : "un");

	n = 0;                          /* Normally report number is 0 */
	if (report_enum->numbered) {    /* Device uses numbered reports, data[0] is report number */
@@ -954,25 +959,21 @@ int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int i
		size--;
	}

#ifdef CONFIG_HID_DEBUG
	{
		int i;
		printk(KERN_DEBUG __FILE__ ": report %d (size %u) = ", n, size);
	/* dump the report descriptor */
	dbg_hid("report %d (size %u) = ", n, size);
	for (i = 0; i < size; i++)
			printk(" %02x", data[i]);
		printk("\n");
	}
#endif
		dbg_hid_line(" %02x", data[i]);
	dbg_hid_line("\n");

	if (!(report = report_enum->report_id_hash[n])) {
		dbg("undefined report_id %d received", n);
		dbg_hid("undefined report_id %d received\n", n);
		return -1;
	}

	rsize = ((report->size - 1) >> 3) + 1;

	if (size < rsize) {
		dbg("report %d is too short, (%d < %d)", report->id, size, rsize);
		dbg_hid("report %d is too short, (%d < %d)\n", report->id, size, rsize);
		memset(data + size, 0, rsize - size);
	}

+15 −0
Original line number Diff line number Diff line
@@ -347,6 +347,9 @@ static void resolv_usage_page(unsigned page) {
void hid_resolv_usage(unsigned usage) {
	const struct hid_usage_entry *p;

	if (!hid_debug)
		return;

	resolv_usage_page(usage >> 16);
	printk(".");
	for (p = hid_usage_table; p->description; p++)
@@ -369,6 +372,9 @@ __inline__ static void tab(int n) {
void hid_dump_field(struct hid_field *field, int n) {
	int j;

	if (!hid_debug)
		return;

	if (field->physical) {
		tab(n);
		printk("Physical(");
@@ -466,6 +472,9 @@ void hid_dump_device(struct hid_device *device) {
	unsigned i,k;
	static char *table[] = {"INPUT", "OUTPUT", "FEATURE"};

	if (!hid_debug)
		return;

	for (i = 0; i < HID_REPORT_TYPES; i++) {
		report_enum = device->report_enum + i;
		list = report_enum->report_list.next;
@@ -489,6 +498,9 @@ void hid_dump_device(struct hid_device *device) {
EXPORT_SYMBOL_GPL(hid_dump_device);

void hid_dump_input(struct hid_usage *usage, __s32 value) {
	if (!hid_debug)
		return;

	printk("hid-debug: input ");
	hid_resolv_usage(usage->hid);
	printk(" = %d\n", value);
@@ -758,6 +770,9 @@ static char **names[EV_MAX + 1] = {

void hid_resolv_event(__u8 type, __u16 code) {

	if (!hid_debug)
		return;

	printk("%s.%s", events[type] ? events[type] : "?",
		names[type] ? (names[type][code] ? names[type][code] : "?") : "?");
}
+104 −21
Original line number Diff line number Diff line
@@ -60,6 +60,19 @@ static const unsigned char hid_keyboard[256] = {
	150,158,159,128,136,177,178,176,142,152,173,140,unk,unk,unk,unk
};

/* extended mapping for certain Logitech hardware (Logitech cordless desktop LX500) */
#define LOGITECH_EXPANDED_KEYMAP_SIZE 80
static int logitech_expanded_keymap[LOGITECH_EXPANDED_KEYMAP_SIZE] = {
	  0,216,  0,213,175,156,  0,  0,  0,  0,
	144,  0,  0,  0,  0,  0,  0,  0,  0,212,
	174,167,152,161,112,  0,  0,  0,154,  0,
	  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
	  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
	  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
	  0,  0,  0,  0,  0,183,184,185,186,187,
	188,189,190,191,192,193,194,  0,  0,  0
};

static const struct {
	__s32 x;
	__s32 y;
@@ -308,9 +321,7 @@ static int hidinput_setkeycode(struct input_dev *dev, int scancode,
		
		clear_bit(old_keycode, dev->keybit);
		set_bit(usage->code, dev->keybit);
#ifdef CONFIG_HID_DEBUG
		printk (KERN_DEBUG "Assigned keycode %d to HID usage code %x\n", keycode, scancode);
#endif
		dbg_hid(KERN_DEBUG "Assigned keycode %d to HID usage code %x\n", keycode, scancode);
		/* Set the keybit for the old keycode if the old keycode is used
		 * by another key */
		if (hidinput_find_key (hid, 0, old_keycode))
@@ -333,11 +344,9 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel

	field->hidinput = hidinput;

#ifdef CONFIG_HID_DEBUG
	printk(KERN_DEBUG "Mapping: ");
	dbg_hid("Mapping: ");
	hid_resolv_usage(usage->hid);
	printk(" ---> ");
#endif
	dbg_hid_line(" ---> ");

	if (field->flags & HID_MAIN_ITEM_CONSTANT)
		goto ignore;
@@ -378,6 +387,21 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
					}
			}

			/* Special handling for Logitech Cordless Desktop */
			if (field->application != HID_GD_MOUSE) {
				if (device->quirks & HID_QUIRK_LOGITECH_EXPANDED_KEYMAP) {
					int hid = usage->hid & HID_USAGE;
					if (hid < LOGITECH_EXPANDED_KEYMAP_SIZE && logitech_expanded_keymap[hid] != 0)
						code = logitech_expanded_keymap[hid];
				}
			} else {
				if (device->quirks & HID_QUIRK_LOGITECH_IGNORE_DOUBLED_WHEEL) {
					int hid = usage->hid & HID_USAGE;
					if (hid == 7 || hid == 8)
						goto ignore;
				}
			}

			map_key(code);
			break;

@@ -566,6 +590,11 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
				case 0x0e5: map_key_clear(KEY_BASSBOOST);	break;
				case 0x0e9: map_key_clear(KEY_VOLUMEUP);	break;
				case 0x0ea: map_key_clear(KEY_VOLUMEDOWN);	break;

				/* reserved in HUT 1.12. Reported on Petalynx remote */
				case 0x0f6: map_key_clear(KEY_NEXT);		break;
				case 0x0fa: map_key_clear(KEY_BACK);		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;
@@ -598,7 +627,9 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
				case 0x21b: map_key_clear(KEY_COPY);		break;
				case 0x21c: map_key_clear(KEY_CUT);		break;
				case 0x21d: map_key_clear(KEY_PASTE);		break;
				case 0x221: map_key_clear(KEY_FIND);		break;
				case 0x21f: map_key_clear(KEY_FIND);		break;
				case 0x221: map_key_clear(KEY_SEARCH);		break;
				case 0x222: map_key_clear(KEY_GOTO);		break;
				case 0x223: map_key_clear(KEY_HOMEPAGE);	break;
				case 0x224: map_key_clear(KEY_BACK);		break;
				case 0x225: map_key_clear(KEY_FORWARD);		break;
@@ -688,7 +719,28 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
			break;

		case HID_UP_MSVENDOR:

			/* special case - Chicony Chicony KU-0418 tactical pad */
			if (device->vendor == 0x04f2 && device->product == 0x0418) {
				set_bit(EV_REP, input->evbit);
				switch(usage->hid & HID_USAGE) {
					case 0xff01: map_key_clear(BTN_1);		break;
					case 0xff02: map_key_clear(BTN_2);		break;
					case 0xff03: map_key_clear(BTN_3);		break;
					case 0xff04: map_key_clear(BTN_4);		break;
					case 0xff05: map_key_clear(BTN_5);		break;
					case 0xff06: map_key_clear(BTN_6);		break;
					case 0xff07: map_key_clear(BTN_7);		break;
					case 0xff08: map_key_clear(BTN_8);		break;
					case 0xff09: map_key_clear(BTN_9);		break;
					case 0xff0a: map_key_clear(BTN_A);		break;
					case 0xff0b: map_key_clear(BTN_B);		break;
					default:    goto ignore;
				}
			} else {
				goto ignore;
			}
			break;

		case HID_UP_CUSTOM: /* Reported on Logitech and Powerbook USB keyboards */

@@ -704,10 +756,10 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
			}
			break;

		case HID_UP_LOGIVENDOR: /* Reported on Logitech Ultra X Media Remote */

		case HID_UP_LOGIVENDOR:
			set_bit(EV_REP, input->evbit);
			switch(usage->hid & HID_USAGE) {
				/* Reported on Logitech Ultra X Media Remote */
				case 0x004: map_key_clear(KEY_AGAIN);		break;
				case 0x00d: map_key_clear(KEY_HOME);		break;
				case 0x024: map_key_clear(KEY_SHUFFLE);		break;
@@ -725,6 +777,14 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
				case 0x04d: map_key_clear(KEY_SUBTITLE);	break;
				case 0x051: map_key_clear(KEY_RED);		break;
				case 0x052: map_key_clear(KEY_CLOSE);		break;

				/* Reported on Petalynx Maxter remote */
				case 0x05a: map_key_clear(KEY_TEXT);		break;
				case 0x05b: map_key_clear(KEY_RED);		break;
				case 0x05c: map_key_clear(KEY_GREEN);		break;
				case 0x05d: map_key_clear(KEY_YELLOW);		break;
				case 0x05e: map_key_clear(KEY_BLUE);		break;

				default:    goto ignore;
			}
			break;
@@ -818,16 +878,24 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
			field->dpad = usage->code;
	}

	/* for those devices which produce Consumer volume usage as relative,
	 * we emulate pressing volumeup/volumedown appropriate number of times
	 * in hidinput_hid_event()
	 */
	if ((usage->type == EV_ABS) && (field->flags & HID_MAIN_ITEM_RELATIVE) &&
			(usage->code == ABS_VOLUME)) {
		set_bit(KEY_VOLUMEUP, input->keybit);
		set_bit(KEY_VOLUMEDOWN, input->keybit);
	}

	hid_resolv_event(usage->type, usage->code);
#ifdef CONFIG_HID_DEBUG
	printk("\n");
#endif

	dbg_hid_line("\n");

	return;

ignore:
#ifdef CONFIG_HID_DEBUG
	printk("IGNORED\n");
#endif
	dbg_hid_line("IGNORED\n");
	return;
}

@@ -896,18 +964,33 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct
	}

	if (usage->hid == (HID_UP_PID | 0x83UL)) { /* Simultaneous Effects Max */
		dbg("Maximum Effects - %d",value);
		dbg_hid("Maximum Effects - %d\n",value);
		return;
	}

	if (usage->hid == (HID_UP_PID | 0x7fUL)) {
		dbg("PID Pool Report\n");
		dbg_hid("PID Pool Report\n");
		return;
	}

	if ((usage->type == EV_KEY) && (usage->code == 0)) /* Key 0 is "unassigned", not KEY_UNKNOWN */
		return;

	if ((usage->type == EV_ABS) && (field->flags & HID_MAIN_ITEM_RELATIVE) &&
			(usage->code == ABS_VOLUME)) {
		int count = abs(value);
		int direction = value > 0 ? KEY_VOLUMEUP : KEY_VOLUMEDOWN;
		int i;

		for (i = 0; i < count; i++) {
			input_event(input, EV_KEY, direction, 1);
			input_sync(input);
			input_event(input, EV_KEY, direction, 0);
			input_sync(input);
		}
		return;
	}

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

	if ((field->flags & HID_MAIN_ITEM_RELATIVE) && (usage->type == EV_KEY))
@@ -976,7 +1059,7 @@ int hidinput_connect(struct hid_device *hid)
			if (IS_INPUT_APPLICATION(hid->collection[i].usage))
				break;

	if (i == hid->maxcollection)
	if (i == hid->maxcollection && (hid->quirks & HID_QUIRK_HIDINPUT) == 0)
		return -1;

	if (hid->quirks & HID_QUIRK_SKIP_OUTPUT_REPORTS)
@@ -994,7 +1077,7 @@ int hidinput_connect(struct hid_device *hid)
				if (!hidinput || !input_dev) {
					kfree(hidinput);
					input_free_device(input_dev);
					err("Out of memory during hid input probe");
					err_hid("Out of memory during hid input probe");
					return -1;
				}

+28 −83
Original line number Diff line number Diff line
@@ -60,6 +60,12 @@ MODULE_PARM_DESC(quirks, "Add/modify USB HID quirks by specifying "
		" quirks=vendorID:productID:quirks"
		" where vendorID, productID, and quirks are all in"
		" 0x-prefixed hex");
static char *rdesc_quirks_param[MAX_USBHID_BOOT_QUIRKS] = { [ 0 ... (MAX_USBHID_BOOT_QUIRKS - 1) ] = NULL };
module_param_array_named(rdesc_quirks, rdesc_quirks_param, charp, NULL, 0444);
MODULE_PARM_DESC(rdesc_quirks, "Add/modify report descriptor quirks by specifying "
		" rdesc_quirks=vendorID:productID:rdesc_quirks"
		" where vendorID, productID, and rdesc_quirks are all in"
		" 0x-prefixed hex");
/*
 * Input submission and I/O error handler.
 */
@@ -127,7 +133,7 @@ static void hid_reset(struct work_struct *work)
			hid_io_error(hid);
		break;
	default:
		err("can't reset device, %s-%s/input%d, status %d",
		err_hid("can't reset device, %s-%s/input%d, status %d",
				hid_to_usb_dev(hid)->bus->bus_name,
				hid_to_usb_dev(hid)->devpath,
				usbhid->ifnum, rc);
@@ -220,7 +226,7 @@ static void hid_irq_in(struct urb *urb)
	if (status) {
		clear_bit(HID_IN_RUNNING, &usbhid->iofl);
		if (status != -EPERM) {
			err("can't resubmit intr, %s-%s/input%d, status %d",
			err_hid("can't resubmit intr, %s-%s/input%d, status %d",
					hid_to_usb_dev(hid)->bus->bus_name,
					hid_to_usb_dev(hid)->devpath,
					usbhid->ifnum, status);
@@ -240,10 +246,10 @@ static int hid_submit_out(struct hid_device *hid)
	usbhid->urbout->transfer_buffer_length = ((report->size - 1) >> 3) + 1 + (report->id > 0);
	usbhid->urbout->dev = hid_to_usb_dev(hid);

	dbg("submitting out urb");
	dbg_hid("submitting out urb\n");

	if (usb_submit_urb(usbhid->urbout, GFP_ATOMIC)) {
		err("usb_submit_urb(out) failed");
		err_hid("usb_submit_urb(out) failed");
		return -1;
	}

@@ -287,12 +293,12 @@ static int hid_submit_ctrl(struct hid_device *hid)
	usbhid->cr->wIndex = cpu_to_le16(usbhid->ifnum);
	usbhid->cr->wLength = cpu_to_le16(len);

	dbg("submitting ctrl urb: %s wValue=0x%04x wIndex=0x%04x wLength=%u",
	dbg_hid("submitting ctrl urb: %s wValue=0x%04x wIndex=0x%04x wLength=%u\n",
		usbhid->cr->bRequest == HID_REQ_SET_REPORT ? "Set_Report" : "Get_Report",
		usbhid->cr->wValue, usbhid->cr->wIndex, usbhid->cr->wLength);

	if (usb_submit_urb(usbhid->urbctrl, GFP_ATOMIC)) {
		err("usb_submit_urb(ctrl) failed");
		err_hid("usb_submit_urb(ctrl) failed");
		return -1;
	}

@@ -474,7 +480,7 @@ int usbhid_wait_io(struct hid_device *hid)
	if (!wait_event_timeout(hid->wait, (!test_bit(HID_CTRL_RUNNING, &usbhid->iofl) &&
					!test_bit(HID_OUT_RUNNING, &usbhid->iofl)),
					10*HZ)) {
		dbg("timeout waiting for ctrl or out queue to clear");
		dbg_hid("timeout waiting for ctrl or out queue to clear\n");
		return -1;
	}

@@ -632,20 +638,6 @@ static void hid_free_buffers(struct usb_device *dev, struct hid_device *hid)
	usb_buffer_free(dev, usbhid->bufsize, usbhid->ctrlbuf, usbhid->ctrlbuf_dma);
}

/*
 * Cherry Cymotion keyboard have an invalid HID report descriptor,
 * that needs fixing before we can parse it.
 */

static void hid_fixup_cymotion_descriptor(char *rdesc, int rsize)
{
	if (rsize >= 17 && rdesc[11] == 0x3c && rdesc[12] == 0x02) {
		info("Fixing up Cherry Cymotion report descriptor");
		rdesc[11] = rdesc[16] = 0xff;
		rdesc[12] = rdesc[17] = 0x03;
	}
}

/*
 * Sending HID_REQ_GET_REPORT changes the operation mode of the ps3 controller
 * to "operational".  Without this, the ps3 controller will not report any
@@ -667,51 +659,11 @@ static void hid_fixup_sony_ps3_controller(struct usb_device *dev, int ifnum)
				 USB_CTRL_GET_TIMEOUT);

	if (result < 0)
		err("%s failed: %d\n", __func__, result);
		err_hid("%s failed: %d\n", __func__, result);

	kfree(buf);
}

/*
 * Certain Logitech keyboards send in report #3 keys which are far
 * above the logical maximum described in descriptor. This extends
 * the original value of 0x28c of logical maximum to 0x104d
 */
static void hid_fixup_logitech_descriptor(unsigned char *rdesc, int rsize)
{
	if (rsize >= 90 && rdesc[83] == 0x26
			&& rdesc[84] == 0x8c
			&& rdesc[85] == 0x02) {
		info("Fixing up Logitech keyboard report descriptor");
		rdesc[84] = rdesc[89] = 0x4d;
		rdesc[85] = rdesc[90] = 0x10;
	}
}

/*
 * Some USB barcode readers from cypress have usage min and usage max in
 * the wrong order
 */
static void hid_fixup_cypress_descriptor(unsigned char *rdesc, int rsize)
{
	short fixed = 0;
	int i;

	for (i = 0; i < rsize - 4; i++) {
		if (rdesc[i] == 0x29 && rdesc [i+2] == 0x19) {
			unsigned char tmp;

			rdesc[i] = 0x19; rdesc[i+2] = 0x29;
			tmp = rdesc[i+3];
			rdesc[i+3] = rdesc[i+1];
			rdesc[i+1] = tmp;
		}
	}

	if (fixed)
		info("Fixing up Cypress report descriptor");
}

static struct hid_device *usb_hid_configure(struct usb_interface *intf)
{
	struct usb_host_interface *interface = intf->cur_altsetting;
@@ -746,7 +698,7 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf)
	if (usb_get_extra_descriptor(interface, HID_DT_HID, &hdesc) &&
	    (!interface->desc.bNumEndpoints ||
	     usb_get_extra_descriptor(&interface->endpoint[0], HID_DT_HID, &hdesc))) {
		dbg("class descriptor not present\n");
		dbg_hid("class descriptor not present\n");
		return NULL;
	}

@@ -755,41 +707,34 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf)
			rsize = le16_to_cpu(hdesc->desc[n].wDescriptorLength);

	if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) {
		dbg("weird size of report descriptor (%u)", rsize);
		dbg_hid("weird size of report descriptor (%u)\n", rsize);
		return NULL;
	}

	if (!(rdesc = kmalloc(rsize, GFP_KERNEL))) {
		dbg("couldn't allocate rdesc memory");
		dbg_hid("couldn't allocate rdesc memory\n");
		return NULL;
	}

	hid_set_idle(dev, interface->desc.bInterfaceNumber, 0, 0);

	if ((n = hid_get_class_descriptor(dev, interface->desc.bInterfaceNumber, HID_DT_REPORT, rdesc, rsize)) < 0) {
		dbg("reading report descriptor failed");
		dbg_hid("reading report descriptor failed\n");
		kfree(rdesc);
		return NULL;
	}

	if ((quirks & HID_QUIRK_CYMOTION))
		hid_fixup_cymotion_descriptor(rdesc, rsize);
	usbhid_fixup_report_descriptor(le16_to_cpu(dev->descriptor.idVendor),
			le16_to_cpu(dev->descriptor.idProduct), rdesc,
			rsize, rdesc_quirks_param);

	if (quirks & HID_QUIRK_LOGITECH_DESCRIPTOR)
		hid_fixup_logitech_descriptor(rdesc, rsize);

	if (quirks & HID_QUIRK_SWAPPED_MIN_MAX)
		hid_fixup_cypress_descriptor(rdesc, rsize);

#ifdef CONFIG_HID_DEBUG
	printk(KERN_DEBUG __FILE__ ": report descriptor (size %u, read %d) = ", rsize, n);
	dbg_hid("report descriptor (size %u, read %d) = ", rsize, n);
	for (n = 0; n < rsize; n++)
		printk(" %02x", (unsigned char) rdesc[n]);
	printk("\n");
#endif
		dbg_hid_line(" %02x", (unsigned char) rdesc[n]);
	dbg_hid_line("\n");

	if (!(hid = hid_parse_report(rdesc, n))) {
		dbg("parsing report descriptor failed");
		dbg_hid("parsing report descriptor failed\n");
		kfree(rdesc);
		return NULL;
	}
@@ -861,7 +806,7 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf)
	}

	if (!usbhid->urbin) {
		err("couldn't find an input interrupt endpoint");
		err_hid("couldn't find an input interrupt endpoint");
		goto fail;
	}

@@ -956,7 +901,7 @@ static void hid_disconnect(struct usb_interface *intf)
	usb_kill_urb(usbhid->urbctrl);

	del_timer_sync(&usbhid->io_retry);
	flush_scheduled_work();
	cancel_work_sync(&usbhid->reset_work);

	if (hid->claimed & HID_CLAIMED_INPUT)
		hidinput_disconnect(hid);
@@ -978,7 +923,7 @@ static int hid_probe(struct usb_interface *intf, const struct usb_device_id *id)
	int i;
	char *c;

	dbg("HID probe called for ifnum %d",
	dbg_hid("HID probe called for ifnum %d\n",
			intf->altsetting->desc.bInterfaceNumber);

	if (!(hid = usb_hid_configure(intf)))
Loading