summary refs log tree commit diff
path: root/drivers/hid/hid-input.c
diff options
context:
space:
mode:
authorJiri Kosina <jkosina@suse.cz>2007-07-04 16:45:59 +0200
committerJiri Kosina <jkosina@suse.cz>2007-07-09 14:13:37 +0200
commit2c1d8aea2ca76df1b1de2aed23e3ceda2a044ed1 (patch)
tree84441a52707b1239e8395b282b986dfb5a9cd3f1 /drivers/hid/hid-input.c
parent3c5f4b25f3552c9bcb52eb956753a3c8140480b7 (diff)
downloadlinux-2c1d8aea2ca76df1b1de2aed23e3ceda2a044ed1.tar.gz
HID: handle cases of volume knobs generating relative values
There are some devices (for example Dell Multimedia Keyboard SK-8135) that have
a volume control knob which generates relative events instead of absolute.
hid-input maps them to ABS_VOLUME. HUT pages don't restrict volume to absolute
values.

Adding REL_VOLUME doesn't seem feasible, nothing knows how to handle it. This
patch translates relative ABS_VOLUME events into appropriate number of series
of VOLUME_UP or VOLUME_DOWN events respectively, so that userspace sees the
correct values in the end.

kernel.org bugzilla 5233

Reported-by: Jochen Eisinger <jochen@penguin-breeder.org>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'drivers/hid/hid-input.c')
-rw-r--r--drivers/hid/hid-input.c25
1 files changed, 25 insertions, 0 deletions
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index 60de16a83c3e..bf7e307503cc 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -882,6 +882,16 @@ 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");
@@ -972,6 +982,21 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct
 	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))