From 8383c6bf9356cfd7093f7afbf16d2b8b4e1c2772 Mon Sep 17 00:00:00 2001 From: Thomas Meyer Date: Tue, 8 Nov 2011 20:14:14 +0100 Subject: HID: hid-lg4ff: Casting (void *) value returned by kmalloc is useless Casting (void *) value returned by kmalloc is useless as mentioned in Documentation/CodingStyle, Chap 14. The semantic patch that makes this change is available in scripts/coccinelle/api/alloc/drop_kmalloc_cast.cocci. Signed-off-by: Thomas Meyer Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c index 103f30d..6ecc9e2 100644 --- a/drivers/hid/hid-lg4ff.c +++ b/drivers/hid/hid-lg4ff.c @@ -430,7 +430,7 @@ int lg4ff_init(struct hid_device *hid) } /* Add the device to device_list */ - entry = (struct lg4ff_device_entry *)kzalloc(sizeof(struct lg4ff_device_entry), GFP_KERNEL); + entry = kzalloc(sizeof(struct lg4ff_device_entry), GFP_KERNEL); if (!entry) { hid_err(hid, "Cannot add device, insufficient memory.\n"); return -ENOMEM; -- cgit v0.10.2 From f6b7efc162caed555264cd73cd00103701fddbc0 Mon Sep 17 00:00:00 2001 From: Przemo Firszt Date: Sat, 5 Nov 2011 11:28:21 +0000 Subject: HID: wacom: Move parsing to a separate function This patch doesn't change the way driver works. Parsing logic is now in a separate function. It's a first step to add Intuos4 Wireless support to hid-wacom driver. Signed-off-by: Przemo Firszt Acked-by: Ping Cheng Reviewed-by: Chris Bagwell Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-wacom.c b/drivers/hid/hid-wacom.c index 17bb88f..db23223 100644 --- a/drivers/hid/hid-wacom.c +++ b/drivers/hid/hid-wacom.c @@ -177,26 +177,13 @@ static ssize_t wacom_store_speed(struct device *dev, static DEVICE_ATTR(speed, S_IRUGO | S_IWUSR | S_IWGRP, wacom_show_speed, wacom_store_speed); -static int wacom_raw_event(struct hid_device *hdev, struct hid_report *report, - u8 *raw_data, int size) +static int wacom_gr_parse_report(struct hid_device *hdev, + struct wacom_data *wdata, + struct input_dev *input, unsigned char *data) { - struct wacom_data *wdata = hid_get_drvdata(hdev); - struct hid_input *hidinput; - struct input_dev *input; - unsigned char *data = (unsigned char *) raw_data; int tool, x, y, rw; - if (!(hdev->claimed & HID_CLAIMED_INPUT)) - return 0; - tool = 0; - hidinput = list_entry(hdev->inputs.next, struct hid_input, list); - input = hidinput->input; - - /* Check if this is a tablet report */ - if (data[0] != 0x03) - return 0; - /* Get X & Y positions */ x = le16_to_cpu(*(__le16 *) &data[2]); y = le16_to_cpu(*(__le16 *) &data[4]); @@ -303,6 +290,26 @@ static int wacom_raw_event(struct hid_device *hdev, struct hid_report *report, #endif return 1; } +static int wacom_raw_event(struct hid_device *hdev, struct hid_report *report, + u8 *raw_data, int size) +{ + struct wacom_data *wdata = hid_get_drvdata(hdev); + struct hid_input *hidinput; + struct input_dev *input; + unsigned char *data = (unsigned char *) raw_data; + + if (!(hdev->claimed & HID_CLAIMED_INPUT)) + return 0; + + hidinput = list_entry(hdev->inputs.next, struct hid_input, list); + input = hidinput->input; + + /* Check if this is a tablet report */ + if (data[0] != 0x03) + return 0; + + return wacom_gr_parse_report(hdev, wdata, input, data); +} static int wacom_input_mapped(struct hid_device *hdev, struct hid_input *hi, struct hid_field *field, struct hid_usage *usage, unsigned long **bit, -- cgit v0.10.2 From 78761ff9bc4e944e0b4e5df1e7eedcfdbb1a9a1a Mon Sep 17 00:00:00 2001 From: Przemo Firszt Date: Sat, 5 Nov 2011 11:28:22 +0000 Subject: HID: wacom: Initial driver for Wacom Intuos4 Wireless (Bluetooth) This is very basic driver for Wacom Intuos4 Wireless tablet. It supports only position, pressure and pen buttons. More features will be added in the future. Signed-off-by: Przemo Firszt Acked-by: Ping Cheng Reviewed-by: Chris Bagwell Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 848a56c..76a5ce5 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1544,6 +1544,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_DUAL_BOX_PRO) }, { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_JOY_BOX_5_PRO) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH) }, { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SLIM_TABLET_5_8_INCH) }, { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SLIM_TABLET_12_1_INCH) }, { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_MEDIA_TABLET_10_6_INCH) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 06ce996..6832a4c 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -679,6 +679,7 @@ #define USB_VENDOR_ID_WACOM 0x056a #define USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH 0x81 +#define USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH 0x00BD #define USB_VENDOR_ID_WALTOP 0x172f #define USB_DEVICE_ID_WALTOP_SLIM_TABLET_5_8_INCH 0x0032 diff --git a/drivers/hid/hid-wacom.c b/drivers/hid/hid-wacom.c index db23223..f218348 100644 --- a/drivers/hid/hid-wacom.c +++ b/drivers/hid/hid-wacom.c @@ -9,6 +9,7 @@ * Copyright (c) 2008 Jiri Slaby * Copyright (c) 2006 Andrew Zabolotny * Copyright (c) 2009 Bastien Nocera + * Copyright (c) 2011 Przemysław Firszt */ /* @@ -33,6 +34,7 @@ struct wacom_data { __u16 tool; unsigned char butstate; + __u8 features; unsigned char high_speed; #ifdef CONFIG_HID_WACOM_POWER_SUPPLY int battery_capacity; @@ -107,6 +109,19 @@ static int wacom_ac_get_property(struct power_supply *psy, } #endif +static void wacom_set_features(struct hid_device *hdev) +{ + int ret; + __u8 rep_data[2]; + + /*set high speed, tablet mode*/ + rep_data[0] = 0x03; + rep_data[1] = 0x20; + ret = hdev->hid_output_raw_report(hdev, rep_data, 2, + HID_FEATURE_REPORT); + return; +} + static void wacom_poke(struct hid_device *hdev, u8 speed) { struct wacom_data *wdata = hid_get_drvdata(hdev); @@ -290,6 +305,77 @@ static int wacom_gr_parse_report(struct hid_device *hdev, #endif return 1; } + +static void wacom_i4_parse_pen_report(struct wacom_data *wdata, + struct input_dev *input, unsigned char *data) +{ + __u16 x, y, pressure; + __u32 id; + + switch (data[1]) { + case 0x80: /* Out of proximity report */ + wdata->tool = 0; + input_report_key(input, BTN_TOUCH, 0); + input_report_abs(input, ABS_PRESSURE, 0); + input_report_key(input, wdata->tool, 0); + input_sync(input); + break; + case 0xC2: /* Tool report */ + id = ((data[2] << 4) | (data[3] >> 4) | + ((data[7] & 0x0f) << 20) | + ((data[8] & 0xf0) << 12)) & 0xfffff; + + switch (id) { + case 0x802: + wdata->tool = BTN_TOOL_PEN; + break; + case 0x80A: + wdata->tool = BTN_TOOL_RUBBER; + break; + } + break; + default: /* Position/pressure report */ + x = data[2] << 9 | data[3] << 1 | ((data[9] & 0x02) >> 1); + y = data[4] << 9 | data[5] << 1 | (data[9] & 0x01); + pressure = (data[6] << 3) | ((data[7] & 0xC0) >> 5) + | (data[1] & 0x01); + + input_report_key(input, BTN_TOUCH, pressure > 1); + + input_report_key(input, BTN_STYLUS, data[1] & 0x02); + input_report_key(input, BTN_STYLUS2, data[1] & 0x04); + input_report_key(input, wdata->tool, 1); + input_report_abs(input, ABS_X, x); + input_report_abs(input, ABS_Y, y); + input_report_abs(input, ABS_PRESSURE, pressure); + input_sync(input); + break; + } + + return; +} + +static void wacom_i4_parse_report(struct hid_device *hdev, + struct wacom_data *wdata, + struct input_dev *input, unsigned char *data) +{ + switch (data[0]) { + case 0x00: /* Empty report */ + break; + case 0x02: /* Pen report */ + wacom_i4_parse_pen_report(wdata, input, data); + break; + case 0x03: /* Features Report */ + wdata->features = data[2]; + break; + case 0x0C: /* Button report */ + break; + default: + hid_err(hdev, "Unknown report: %d,%d\n", data[0], data[1]); + break; + } +} + static int wacom_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *raw_data, int size) { @@ -297,6 +383,7 @@ static int wacom_raw_event(struct hid_device *hdev, struct hid_report *report, struct hid_input *hidinput; struct input_dev *input; unsigned char *data = (unsigned char *) raw_data; + int i; if (!(hdev->claimed & HID_CLAIMED_INPUT)) return 0; @@ -308,7 +395,30 @@ static int wacom_raw_event(struct hid_device *hdev, struct hid_report *report, if (data[0] != 0x03) return 0; - return wacom_gr_parse_report(hdev, wdata, input, data); + switch (hdev->product) { + case USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH: + return wacom_gr_parse_report(hdev, wdata, input, data); + break; + case USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH: + i = 1; + + switch (data[0]) { + case 0x04: + wacom_i4_parse_report(hdev, wdata, input, data + i); + i += 10; + /* fall through */ + case 0x03: + wacom_i4_parse_report(hdev, wdata, input, data + i); + i += 10; + wacom_i4_parse_report(hdev, wdata, input, data + i); + break; + default: + hid_err(hdev, "Unknown report: %d,%d size:%d\n", + data[0], data[1], size); + return 0; + } + } + return 1; } static int wacom_input_mapped(struct hid_device *hdev, struct hid_input *hi, @@ -345,10 +455,19 @@ static int wacom_input_mapped(struct hid_device *hdev, struct hid_input *hi, __set_bit(BTN_TOOL_RUBBER, input->keybit); __set_bit(BTN_TOOL_MOUSE, input->keybit); - input_set_abs_params(input, ABS_X, 0, 16704, 4, 0); - input_set_abs_params(input, ABS_Y, 0, 12064, 4, 0); - input_set_abs_params(input, ABS_PRESSURE, 0, 511, 0, 0); - input_set_abs_params(input, ABS_DISTANCE, 0, 32, 0, 0); + switch (hdev->product) { + case USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH: + input_set_abs_params(input, ABS_X, 0, 16704, 4, 0); + input_set_abs_params(input, ABS_Y, 0, 12064, 4, 0); + input_set_abs_params(input, ABS_PRESSURE, 0, 511, 0, 0); + input_set_abs_params(input, ABS_DISTANCE, 0, 32, 0, 0); + break; + case USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH: + input_set_abs_params(input, ABS_X, 0, 40640, 4, 0); + input_set_abs_params(input, ABS_Y, 0, 25400, 4, 0); + input_set_abs_params(input, ABS_PRESSURE, 0, 2047, 0, 0); + break; + } return 0; } @@ -385,8 +504,16 @@ static int wacom_probe(struct hid_device *hdev, hid_warn(hdev, "can't create sysfs speed attribute err: %d\n", ret); - /* Set Wacom mode 2 with high reporting speed */ - wacom_poke(hdev, 1); + switch (hdev->product) { + case USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH: + /* Set Wacom mode 2 with high reporting speed */ + wacom_poke(hdev, 1); + break; + case USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH: + wdata->features = 0; + wacom_set_features(hdev); + break; + } #ifdef CONFIG_HID_WACOM_POWER_SUPPLY wdata->battery.properties = wacom_battery_props; @@ -448,6 +575,7 @@ static void wacom_remove(struct hid_device *hdev) static const struct hid_device_id wacom_devices[] = { { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH) }, { } }; -- cgit v0.10.2 From e46e927b9b7e8d95526e69322855243882b7e1a3 Mon Sep 17 00:00:00 2001 From: Chase Douglas Date: Mon, 7 Nov 2011 11:08:05 -0800 Subject: HID: bump maximum global item tag report size to 96 bytes This allows the latest N-Trig devices to function properly. BugLink: https://bugs.launchpad.net/bugs/724831 Cc: stable@vger.kernel.org Signed-off-by: Chase Douglas Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 848a56c..6113996 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -362,7 +362,7 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item) case HID_GLOBAL_ITEM_TAG_REPORT_SIZE: parser->global.report_size = item_udata(item); - if (parser->global.report_size > 32) { + if (parser->global.report_size > 96) { dbg_hid("invalid report_size %d\n", parser->global.report_size); return -1; -- cgit v0.10.2 From 6da7066906e977d42104a859c490f5f9a300488c Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Fri, 28 Oct 2011 18:15:02 +0200 Subject: HID: ignore absolute values which don't fit between logical min and max MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Linux should ignore values outside logical min/max range, as they are not meaningful. This is what at least some of other OSes do, and it also makes sense (currently the value gets misinterpreted larger up the stack). Reported-by: Denilson Figueiredo de Sá Tested-by: Denilson Figueiredo de Sá Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index f333139..b7b0d55 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -822,6 +822,13 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct return; } + /* Ignore absolute values that are out of bounds */ + if ((usage->type == EV_ABS && (value < field->logical_minimum || + value > field->logical_maximum))) { + dbg_hid("Ignoring out-of-range value %x\n", value); + return; + } + /* 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); -- cgit v0.10.2 From b4b583d4e9a5ff28c4a150bb25a4fff5cd4dfbbd Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Mon, 31 Oct 2011 16:26:22 +0100 Subject: HID: be more strict when ignoring out-of-range fields MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit HID 1.11 specification, section 5.10 tells us: HID class devices support the ability to ignore selected fields in a report at run- time. This is accomplished by declaring bit field in a report that is capable of containing a range of values larger than those actually generated by the control. If the host or the device receives an out-of-range value then the current value for the respective control will not be modified. So we shouldn't be restricted to EV_ABS only. Reported-by: Denilson Figueiredo de Sá Tested-by: Denilson Figueiredo de Sá Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index b7b0d55..6e32526 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -822,9 +822,8 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct return; } - /* Ignore absolute values that are out of bounds */ - if ((usage->type == EV_ABS && (value < field->logical_minimum || - value > field->logical_maximum))) { + /* Ignore out-of-range values as per HID specification, section 5.10 */ + if (value < field->logical_minimum || value > field->logical_maximum) { dbg_hid("Ignoring out-of-range value %x\n", value); return; } -- cgit v0.10.2 From 1f59169e191c62e66dda86ac2bc953c915c8dddf Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Sat, 19 Nov 2011 12:19:00 +0100 Subject: HID: debugfs: decode Generic Device Controls Usage Page The USB HID Usage Tables spec defines page 6 for Generic Device Controls, the most useful of which (to me) is Battery Strength. Signed-off-by: Jeremy Fitzhardinge Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c index 9a243ca..940af3c 100644 --- a/drivers/hid/hid-debug.c +++ b/drivers/hid/hid-debug.c @@ -113,6 +113,14 @@ static const struct hid_usage_entry hid_usage_table[] = { {0, 0xbd, "FlareRelease"}, {0, 0xbe, "LandingGear"}, {0, 0xbf, "ToeBrake"}, + { 6, 0, "GenericDeviceControls" }, + {0, 0x20, "BatteryStrength" }, + {0, 0x21, "WirelessChannel" }, + {0, 0x22, "WirelessID" }, + {0, 0x23, "DiscoverWirelessControl" }, + {0, 0x24, "SecurityCodeCharacterEntered" }, + {0, 0x25, "SecurityCodeCharactedErased" }, + {0, 0x26, "SecurityCodeCleared" }, { 7, 0, "Keyboard" }, { 8, 0, "LED" }, {0, 0x01, "NumLock"}, -- cgit v0.10.2 From 4c33a885a5c8a9ad573249fa4ee4fb39af866599 Mon Sep 17 00:00:00 2001 From: Thomas Meyer Date: Thu, 17 Nov 2011 23:43:40 +0100 Subject: HID: roccat: Use kmemdup rather than duplicating its implementation Signed-off-by: Thomas Meyer Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-roccat-common.c b/drivers/hid/hid-roccat-common.c index edf898d..f933221 100644 --- a/drivers/hid/hid-roccat-common.c +++ b/drivers/hid/hid-roccat-common.c @@ -48,12 +48,10 @@ int roccat_common_send(struct usb_device *usb_dev, uint report_id, char *buf; int len; - buf = kmalloc(size, GFP_KERNEL); + buf = kmemdup(data, size, GFP_KERNEL); if (buf == NULL) return -ENOMEM; - memcpy(buf, data, size); - len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), HID_REQ_SET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, diff --git a/drivers/hid/hid-roccat-kone.c b/drivers/hid/hid-roccat-kone.c index e2072af..40090d6 100644 --- a/drivers/hid/hid-roccat-kone.c +++ b/drivers/hid/hid-roccat-kone.c @@ -78,12 +78,10 @@ static int kone_send(struct usb_device *usb_dev, uint usb_command, char *buf; int len; - buf = kmalloc(size, GFP_KERNEL); + buf = kmemdup(data, size, GFP_KERNEL); if (buf == NULL) return -ENOMEM; - memcpy(buf, data, size); - len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), HID_REQ_SET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, -- cgit v0.10.2 From 789aaa2ee0af1a0fba4c73f1874ad524d7be0771 Mon Sep 17 00:00:00 2001 From: Dan Delaney Date: Sun, 20 Nov 2011 10:21:30 +0100 Subject: HID/usbled: add support for Dream Cheeky DL100B Mailbox Friends Alert Adding support for Dream Cheeky DL1800B Friend Alert device. Signed-off-by: Dan Delaney Acked-by: Greg Kroah-Hartman Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 6113996..c0ef2b4 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1768,6 +1768,7 @@ static const struct hid_device_id hid_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EARTHMATE) }, { HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EM_LT20) }, { HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY, 0x0004) }, + { HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY, 0x000a) }, { HID_USB_DEVICE(USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5) }, { HID_USB_DEVICE(USB_VENDOR_ID_ETT, USB_DEVICE_ID_TC5UH) }, { HID_USB_DEVICE(USB_VENDOR_ID_ETT, USB_DEVICE_ID_TC4UM) }, diff --git a/drivers/usb/misc/usbled.c b/drivers/usb/misc/usbled.c index 43f84e5..7ab97ee 100644 --- a/drivers/usb/misc/usbled.c +++ b/drivers/usb/misc/usbled.c @@ -31,6 +31,8 @@ static const struct usb_device_id id_table[] = { .driver_info = DELCOM_VISUAL_SIGNAL_INDICATOR }, { USB_DEVICE(0x1d34, 0x0004), .driver_info = DREAM_CHEEKY_WEBMAIL_NOTIFIER }, + { USB_DEVICE(0x1d34, 0x000a), + .driver_info = DREAM_CHEEKY_WEBMAIL_NOTIFIER }, { }, }; MODULE_DEVICE_TABLE(usb, id_table); -- cgit v0.10.2 From b95f5bcb811e3905b5376f87789da8d097fee682 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Thu, 17 Nov 2011 08:47:29 -0800 Subject: HID: Move the hid-hyperv driver out of staging The file hid-hyperv.c implements a hid compliant mouse driver for use on a Hyper-V based system. This driver is currently in the staging area and as part of the effort to move this driver out of staging, I had posted the driver code for community review a few weeks ago. This current patch addresses all the review comments I have gotten to date. All the relevant patches have already been submitted to the staging tree as well. As per Greg's suggestion, this patch does not get rid of the code from the staging area. Once the mouse driver lands under the hid directory, we will cleanup the staging directory. Signed-off-by: K. Y. Srinivasan Signed-off-by: Jiri Kosina diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 22a4a05..387bb53 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -643,6 +643,12 @@ config HID_ZYDACRON ---help--- Support for Zydacron remote control. +config HYPERV_MOUSE + tristate "Microsoft Hyper-V mouse driver" + depends on HYPERV + ---help--- + Select this option to enable the Hyper-V mouse driver. + endmenu endif # HID_SUPPORT diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 1e0d2a6..fbb1ee6 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -78,6 +78,7 @@ obj-$(CONFIG_HID_ZYDACRON) += hid-zydacron.o obj-$(CONFIG_HID_WACOM) += hid-wacom.o obj-$(CONFIG_HID_WALTOP) += hid-waltop.o obj-$(CONFIG_HID_WIIMOTE) += hid-wiimote.o +obj-$(CONFIG_HYPERV_MOUSE) += hid-hyperv.o obj-$(CONFIG_USB_HID) += usbhid/ obj-$(CONFIG_USB_MOUSE) += usbhid/ diff --git a/drivers/hid/hid-hyperv.c b/drivers/hid/hid-hyperv.c new file mode 100644 index 0000000..d503cbb --- /dev/null +++ b/drivers/hid/hid-hyperv.c @@ -0,0 +1,582 @@ +/* + * Copyright (c) 2009, Citrix Systems, Inc. + * Copyright (c) 2010, Microsoft Corporation. + * Copyright (c) 2011, Novell Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 +#include +#include +#include +#include +#include +#include +#include + + +struct hv_input_dev_info { + unsigned int size; + unsigned short vendor; + unsigned short product; + unsigned short version; + unsigned short reserved[11]; +}; + +/* The maximum size of a synthetic input message. */ +#define SYNTHHID_MAX_INPUT_REPORT_SIZE 16 + +/* + * Current version + * + * History: + * Beta, RC < 2008/1/22 1,0 + * RC > 2008/1/22 2,0 + */ +#define SYNTHHID_INPUT_VERSION_MAJOR 2 +#define SYNTHHID_INPUT_VERSION_MINOR 0 +#define SYNTHHID_INPUT_VERSION (SYNTHHID_INPUT_VERSION_MINOR | \ + (SYNTHHID_INPUT_VERSION_MAJOR << 16)) + + +#pragma pack(push, 1) +/* + * Message types in the synthetic input protocol + */ +enum synthhid_msg_type { + SYNTH_HID_PROTOCOL_REQUEST, + SYNTH_HID_PROTOCOL_RESPONSE, + SYNTH_HID_INITIAL_DEVICE_INFO, + SYNTH_HID_INITIAL_DEVICE_INFO_ACK, + SYNTH_HID_INPUT_REPORT, + SYNTH_HID_MAX +}; + +/* + * Basic message structures. + */ +struct synthhid_msg_hdr { + enum synthhid_msg_type type; + u32 size; +}; + +struct synthhid_msg { + struct synthhid_msg_hdr header; + char data[1]; /* Enclosed message */ +}; + +union synthhid_version { + struct { + u16 minor_version; + u16 major_version; + }; + u32 version; +}; + +/* + * Protocol messages + */ +struct synthhid_protocol_request { + struct synthhid_msg_hdr header; + union synthhid_version version_requested; +}; + +struct synthhid_protocol_response { + struct synthhid_msg_hdr header; + union synthhid_version version_requested; + unsigned char approved; +}; + +struct synthhid_device_info { + struct synthhid_msg_hdr header; + struct hv_input_dev_info hid_dev_info; + struct hid_descriptor hid_descriptor; +}; + +struct synthhid_device_info_ack { + struct synthhid_msg_hdr header; + unsigned char reserved; +}; + +struct synthhid_input_report { + struct synthhid_msg_hdr header; + char buffer[1]; +}; + +#pragma pack(pop) + +#define INPUTVSC_SEND_RING_BUFFER_SIZE (10*PAGE_SIZE) +#define INPUTVSC_RECV_RING_BUFFER_SIZE (10*PAGE_SIZE) + + +enum pipe_prot_msg_type { + PIPE_MESSAGE_INVALID, + PIPE_MESSAGE_DATA, + PIPE_MESSAGE_MAXIMUM +}; + + +struct pipe_prt_msg { + enum pipe_prot_msg_type type; + u32 size; + char data[1]; +}; + +struct mousevsc_prt_msg { + enum pipe_prot_msg_type type; + u32 size; + union { + struct synthhid_protocol_request request; + struct synthhid_protocol_response response; + struct synthhid_device_info_ack ack; + }; +}; + +/* + * Represents an mousevsc device + */ +struct mousevsc_dev { + struct hv_device *device; + bool init_complete; + bool connected; + struct mousevsc_prt_msg protocol_req; + struct mousevsc_prt_msg protocol_resp; + /* Synchronize the request/response if needed */ + struct completion wait_event; + int dev_info_status; + + struct hid_descriptor *hid_desc; + unsigned char *report_desc; + u32 report_desc_size; + struct hv_input_dev_info hid_dev_info; + struct hid_device *hid_device; +}; + + +static struct mousevsc_dev *mousevsc_alloc_device(struct hv_device *device) +{ + struct mousevsc_dev *input_dev; + + input_dev = kzalloc(sizeof(struct mousevsc_dev), GFP_KERNEL); + + if (!input_dev) + return NULL; + + input_dev->device = device; + hv_set_drvdata(device, input_dev); + init_completion(&input_dev->wait_event); + input_dev->init_complete = false; + + return input_dev; +} + +static void mousevsc_free_device(struct mousevsc_dev *device) +{ + kfree(device->hid_desc); + kfree(device->report_desc); + hv_set_drvdata(device->device, NULL); + kfree(device); +} + +static void mousevsc_on_receive_device_info(struct mousevsc_dev *input_device, + struct synthhid_device_info *device_info) +{ + int ret = 0; + struct hid_descriptor *desc; + struct mousevsc_prt_msg ack; + + input_device->dev_info_status = -ENOMEM; + + input_device->hid_dev_info = device_info->hid_dev_info; + desc = &device_info->hid_descriptor; + if (desc->bLength == 0) + goto cleanup; + + input_device->hid_desc = kzalloc(desc->bLength, GFP_ATOMIC); + + if (!input_device->hid_desc) + goto cleanup; + + memcpy(input_device->hid_desc, desc, desc->bLength); + + input_device->report_desc_size = desc->desc[0].wDescriptorLength; + if (input_device->report_desc_size == 0) { + input_device->dev_info_status = -EINVAL; + goto cleanup; + } + + input_device->report_desc = kzalloc(input_device->report_desc_size, + GFP_ATOMIC); + + if (!input_device->report_desc) { + input_device->dev_info_status = -ENOMEM; + goto cleanup; + } + + memcpy(input_device->report_desc, + ((unsigned char *)desc) + desc->bLength, + desc->desc[0].wDescriptorLength); + + /* Send the ack */ + memset(&ack, 0, sizeof(struct mousevsc_prt_msg)); + + ack.type = PIPE_MESSAGE_DATA; + ack.size = sizeof(struct synthhid_device_info_ack); + + ack.ack.header.type = SYNTH_HID_INITIAL_DEVICE_INFO_ACK; + ack.ack.header.size = 1; + ack.ack.reserved = 0; + + ret = vmbus_sendpacket(input_device->device->channel, + &ack, + sizeof(struct pipe_prt_msg) - sizeof(unsigned char) + + sizeof(struct synthhid_device_info_ack), + (unsigned long)&ack, + VM_PKT_DATA_INBAND, + VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); + + if (!ret) + input_device->dev_info_status = 0; + +cleanup: + complete(&input_device->wait_event); + + return; +} + +static void mousevsc_on_receive(struct hv_device *device, + struct vmpacket_descriptor *packet) +{ + struct pipe_prt_msg *pipe_msg; + struct synthhid_msg *hid_msg; + struct mousevsc_dev *input_dev = hv_get_drvdata(device); + struct synthhid_input_report *input_report; + + pipe_msg = (struct pipe_prt_msg *)((unsigned long)packet + + (packet->offset8 << 3)); + + if (pipe_msg->type != PIPE_MESSAGE_DATA) + return; + + hid_msg = (struct synthhid_msg *)pipe_msg->data; + + switch (hid_msg->header.type) { + case SYNTH_HID_PROTOCOL_RESPONSE: + /* + * While it will be impossible for us to protect against + * malicious/buggy hypervisor/host, add a check here to + * ensure we don't corrupt memory. + */ + if ((pipe_msg->size + sizeof(struct pipe_prt_msg) + - sizeof(unsigned char)) + > sizeof(struct mousevsc_prt_msg)) { + WARN_ON(1); + break; + } + + memcpy(&input_dev->protocol_resp, pipe_msg, + pipe_msg->size + sizeof(struct pipe_prt_msg) - + sizeof(unsigned char)); + complete(&input_dev->wait_event); + break; + + case SYNTH_HID_INITIAL_DEVICE_INFO: + WARN_ON(pipe_msg->size < sizeof(struct hv_input_dev_info)); + + /* + * Parse out the device info into device attr, + * hid desc and report desc + */ + mousevsc_on_receive_device_info(input_dev, + (struct synthhid_device_info *)pipe_msg->data); + break; + case SYNTH_HID_INPUT_REPORT: + input_report = + (struct synthhid_input_report *)pipe_msg->data; + if (!input_dev->init_complete) + break; + hid_input_report(input_dev->hid_device, + HID_INPUT_REPORT, input_report->buffer, + input_report->header.size, 1); + break; + default: + pr_err("unsupported hid msg type - type %d len %d", + hid_msg->header.type, hid_msg->header.size); + break; + } + +} + +static void mousevsc_on_channel_callback(void *context) +{ + const int packet_size = 0x100; + int ret; + struct hv_device *device = context; + u32 bytes_recvd; + u64 req_id; + struct vmpacket_descriptor *desc; + unsigned char *buffer; + int bufferlen = packet_size; + + buffer = kmalloc(bufferlen, GFP_ATOMIC); + if (!buffer) + return; + + do { + ret = vmbus_recvpacket_raw(device->channel, buffer, + bufferlen, &bytes_recvd, &req_id); + + switch (ret) { + case 0: + if (bytes_recvd <= 0) { + kfree(buffer); + return; + } + desc = (struct vmpacket_descriptor *)buffer; + + switch (desc->type) { + case VM_PKT_COMP: + break; + + case VM_PKT_DATA_INBAND: + mousevsc_on_receive(device, desc); + break; + + default: + pr_err("unhandled packet type %d, tid %llx len %d\n", + desc->type, req_id, bytes_recvd); + break; + } + + break; + + case -ENOBUFS: + kfree(buffer); + /* Handle large packet */ + bufferlen = bytes_recvd; + buffer = kmalloc(bytes_recvd, GFP_ATOMIC); + + if (!buffer) + return; + + break; + } + } while (1); + +} + +static int mousevsc_connect_to_vsp(struct hv_device *device) +{ + int ret = 0; + int t; + struct mousevsc_dev *input_dev = hv_get_drvdata(device); + struct mousevsc_prt_msg *request; + struct mousevsc_prt_msg *response; + + request = &input_dev->protocol_req; + memset(request, 0, sizeof(struct mousevsc_prt_msg)); + + request->type = PIPE_MESSAGE_DATA; + request->size = sizeof(struct synthhid_protocol_request); + request->request.header.type = SYNTH_HID_PROTOCOL_REQUEST; + request->request.header.size = sizeof(unsigned int); + request->request.version_requested.version = SYNTHHID_INPUT_VERSION; + + ret = vmbus_sendpacket(device->channel, request, + sizeof(struct pipe_prt_msg) - + sizeof(unsigned char) + + sizeof(struct synthhid_protocol_request), + (unsigned long)request, + VM_PKT_DATA_INBAND, + VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); + if (ret) + goto cleanup; + + t = wait_for_completion_timeout(&input_dev->wait_event, 5*HZ); + if (!t) { + ret = -ETIMEDOUT; + goto cleanup; + } + + response = &input_dev->protocol_resp; + + if (!response->response.approved) { + pr_err("synthhid protocol request failed (version %d)\n", + SYNTHHID_INPUT_VERSION); + ret = -ENODEV; + goto cleanup; + } + + t = wait_for_completion_timeout(&input_dev->wait_event, 5*HZ); + if (!t) { + ret = -ETIMEDOUT; + goto cleanup; + } + + /* + * We should have gotten the device attr, hid desc and report + * desc at this point + */ + ret = input_dev->dev_info_status; + +cleanup: + return ret; +} + +static int mousevsc_hid_open(struct hid_device *hid) +{ + return 0; +} + +static int mousevsc_hid_start(struct hid_device *hid) +{ + return 0; +} + +static void mousevsc_hid_close(struct hid_device *hid) +{ +} + +static void mousevsc_hid_stop(struct hid_device *hid) +{ +} + +static struct hid_ll_driver mousevsc_ll_driver = { + .open = mousevsc_hid_open, + .close = mousevsc_hid_close, + .start = mousevsc_hid_start, + .stop = mousevsc_hid_stop, +}; + +static struct hid_driver mousevsc_hid_driver; + +static int mousevsc_probe(struct hv_device *device, + const struct hv_vmbus_device_id *dev_id) +{ + int ret; + struct mousevsc_dev *input_dev; + struct hid_device *hid_dev; + + input_dev = mousevsc_alloc_device(device); + + if (!input_dev) + return -ENOMEM; + + ret = vmbus_open(device->channel, + INPUTVSC_SEND_RING_BUFFER_SIZE, + INPUTVSC_RECV_RING_BUFFER_SIZE, + NULL, + 0, + mousevsc_on_channel_callback, + device + ); + + if (ret) + goto probe_err0; + + ret = mousevsc_connect_to_vsp(device); + + if (ret) + goto probe_err1; + + /* workaround SA-167 */ + if (input_dev->report_desc[14] == 0x25) + input_dev->report_desc[14] = 0x29; + + hid_dev = hid_allocate_device(); + if (IS_ERR(hid_dev)) { + ret = PTR_ERR(hid_dev); + goto probe_err1; + } + + hid_dev->ll_driver = &mousevsc_ll_driver; + hid_dev->driver = &mousevsc_hid_driver; + hid_dev->bus = BUS_VIRTUAL; + hid_dev->vendor = input_dev->hid_dev_info.vendor; + hid_dev->product = input_dev->hid_dev_info.product; + hid_dev->version = input_dev->hid_dev_info.version; + input_dev->hid_device = hid_dev; + + sprintf(hid_dev->name, "%s", "Microsoft Vmbus HID-compliant Mouse"); + + ret = hid_parse_report(hid_dev, input_dev->report_desc, + input_dev->report_desc_size); + + if (ret) { + hid_err(hid_dev, "parse failed\n"); + goto probe_err2; + } + + ret = hid_hw_start(hid_dev, HID_CONNECT_HIDINPUT | HID_CONNECT_HIDDEV); + + if (ret) { + hid_err(hid_dev, "hw start failed\n"); + goto probe_err2; + } + + input_dev->connected = true; + input_dev->init_complete = true; + + return ret; + +probe_err2: + hid_destroy_device(hid_dev); + +probe_err1: + vmbus_close(device->channel); + +probe_err0: + mousevsc_free_device(input_dev); + + return ret; +} + + +static int mousevsc_remove(struct hv_device *dev) +{ + struct mousevsc_dev *input_dev = hv_get_drvdata(dev); + + vmbus_close(dev->channel); + hid_destroy_device(input_dev->hid_device); + mousevsc_free_device(input_dev); + + return 0; +} + +static const struct hv_vmbus_device_id id_table[] = { + /* Mouse guid */ + { VMBUS_DEVICE(0x9E, 0xB6, 0xA8, 0xCF, 0x4A, 0x5B, 0xc0, 0x4c, + 0xB9, 0x8B, 0x8B, 0xA1, 0xA1, 0xF3, 0xF9, 0x5A) }, + { }, +}; + +MODULE_DEVICE_TABLE(vmbus, id_table); + +static struct hv_driver mousevsc_drv = { + .name = KBUILD_MODNAME, + .id_table = id_table, + .probe = mousevsc_probe, + .remove = mousevsc_remove, +}; + +static int __init mousevsc_init(void) +{ + return vmbus_driver_register(&mousevsc_drv); +} + +static void __exit mousevsc_exit(void) +{ + vmbus_driver_unregister(&mousevsc_drv); +} + +MODULE_LICENSE("GPL"); +MODULE_VERSION(HV_DRV_VERSION); +module_init(mousevsc_init); +module_exit(mousevsc_exit); -- cgit v0.10.2 From 6cf851d803a17aa43edc23f9ec57378c041fddfb Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Tue, 22 Nov 2011 22:52:15 +0100 Subject: HID: hyperv: fixup Kconfig / Makefile entries Rename the Kconfig entry for hyperv mouse driver so that it has HID_ prefix as all the other drivers; while at it, place the entry for this driver to properly ordered place in Makefile and Kconfig. Signed-off-by: Jiri Kosina diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 387bb53..f5aa4a0 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -560,6 +560,12 @@ config GREENASIA_FF (like MANTA Warrior MM816 and SpeedLink Strike2 SL-6635) or adapter and want to enable force feedback support for it. +config HID_HYPERV_MOUSE + tristate "Microsoft Hyper-V mouse driver" + depends on HYPERV + ---help--- + Select this option to enable the Hyper-V mouse driver. + config HID_SMARTJOYPLUS tristate "SmartJoy PLUS PS2/USB adapter support" depends on USB_HID @@ -643,12 +649,6 @@ config HID_ZYDACRON ---help--- Support for Zydacron remote control. -config HYPERV_MOUSE - tristate "Microsoft Hyper-V mouse driver" - depends on HYPERV - ---help--- - Select this option to enable the Hyper-V mouse driver. - endmenu endif # HID_SUPPORT diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index fbb1ee6..eed64f4 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -38,6 +38,7 @@ obj-$(CONFIG_HID_ELECOM) += hid-elecom.o obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o obj-$(CONFIG_HID_GYRATION) += hid-gyration.o obj-$(CONFIG_HID_HOLTEK) += hid-holtekff.o +obj-$(CONFIG_HID_HYPERV_MOUSE) += hid-hyperv.o obj-$(CONFIG_HID_KENSINGTON) += hid-kensington.o obj-$(CONFIG_HID_KEYTOUCH) += hid-keytouch.o obj-$(CONFIG_HID_KYE) += hid-kye.o @@ -78,7 +79,6 @@ obj-$(CONFIG_HID_ZYDACRON) += hid-zydacron.o obj-$(CONFIG_HID_WACOM) += hid-wacom.o obj-$(CONFIG_HID_WALTOP) += hid-waltop.o obj-$(CONFIG_HID_WIIMOTE) += hid-wiimote.o -obj-$(CONFIG_HYPERV_MOUSE) += hid-hyperv.o obj-$(CONFIG_USB_HID) += usbhid/ obj-$(CONFIG_USB_MOUSE) += usbhid/ -- cgit v0.10.2 From 192a1acfbd600fea8a596b7d92572b70131b7738 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Thu, 17 Nov 2011 14:11:58 +0100 Subject: HID: wiimote: Rename driver to allow multiple source files Extension and sound support for the wiimote are quite complex and will be implemented in separate source files. Hence rename the current driver to "-core" suffix so multiple files can be linked into this module. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 1e0d2a6..96d33ad 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -25,6 +25,8 @@ ifdef CONFIG_LOGIWHEELS_FF hid-logitech-y += hid-lg4ff.o endif +hid-wiimote-y := hid-wiimote-core.o + obj-$(CONFIG_HID_A4TECH) += hid-a4tech.o obj-$(CONFIG_HID_ACRUX) += hid-axff.o obj-$(CONFIG_HID_APPLE) += hid-apple.o diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c new file mode 100644 index 0000000..76739c0 --- /dev/null +++ b/drivers/hid/hid-wiimote-core.c @@ -0,0 +1,1346 @@ +/* + * HID driver for Nintendo Wiimote devices + * Copyright (c) 2011 David Herrmann + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "hid-ids.h" + +#define WIIMOTE_VERSION "0.2" +#define WIIMOTE_NAME "Nintendo Wii Remote" +#define WIIMOTE_BUFSIZE 32 + +struct wiimote_buf { + __u8 data[HID_MAX_BUFFER_SIZE]; + size_t size; +}; + +struct wiimote_state { + spinlock_t lock; + __u8 flags; + __u8 accel_split[2]; + + /* synchronous cmd requests */ + struct mutex sync; + struct completion ready; + int cmd; + __u32 opt; + + /* results of synchronous requests */ + __u8 cmd_battery; + __u8 cmd_err; +}; + +struct wiimote_data { + struct hid_device *hdev; + struct input_dev *input; + struct led_classdev *leds[4]; + struct input_dev *accel; + struct input_dev *ir; + struct power_supply battery; + + spinlock_t qlock; + __u8 head; + __u8 tail; + struct wiimote_buf outq[WIIMOTE_BUFSIZE]; + struct work_struct worker; + + struct wiimote_state state; +}; + +#define WIIPROTO_FLAG_LED1 0x01 +#define WIIPROTO_FLAG_LED2 0x02 +#define WIIPROTO_FLAG_LED3 0x04 +#define WIIPROTO_FLAG_LED4 0x08 +#define WIIPROTO_FLAG_RUMBLE 0x10 +#define WIIPROTO_FLAG_ACCEL 0x20 +#define WIIPROTO_FLAG_IR_BASIC 0x40 +#define WIIPROTO_FLAG_IR_EXT 0x80 +#define WIIPROTO_FLAG_IR_FULL 0xc0 /* IR_BASIC | IR_EXT */ +#define WIIPROTO_FLAGS_LEDS (WIIPROTO_FLAG_LED1 | WIIPROTO_FLAG_LED2 | \ + WIIPROTO_FLAG_LED3 | WIIPROTO_FLAG_LED4) +#define WIIPROTO_FLAGS_IR (WIIPROTO_FLAG_IR_BASIC | WIIPROTO_FLAG_IR_EXT | \ + WIIPROTO_FLAG_IR_FULL) + +/* return flag for led \num */ +#define WIIPROTO_FLAG_LED(num) (WIIPROTO_FLAG_LED1 << (num - 1)) + +enum wiiproto_reqs { + WIIPROTO_REQ_NULL = 0x0, + WIIPROTO_REQ_RUMBLE = 0x10, + WIIPROTO_REQ_LED = 0x11, + WIIPROTO_REQ_DRM = 0x12, + WIIPROTO_REQ_IR1 = 0x13, + WIIPROTO_REQ_SREQ = 0x15, + WIIPROTO_REQ_WMEM = 0x16, + WIIPROTO_REQ_RMEM = 0x17, + WIIPROTO_REQ_IR2 = 0x1a, + WIIPROTO_REQ_STATUS = 0x20, + WIIPROTO_REQ_DATA = 0x21, + WIIPROTO_REQ_RETURN = 0x22, + WIIPROTO_REQ_DRM_K = 0x30, + WIIPROTO_REQ_DRM_KA = 0x31, + WIIPROTO_REQ_DRM_KE = 0x32, + WIIPROTO_REQ_DRM_KAI = 0x33, + WIIPROTO_REQ_DRM_KEE = 0x34, + WIIPROTO_REQ_DRM_KAE = 0x35, + WIIPROTO_REQ_DRM_KIE = 0x36, + WIIPROTO_REQ_DRM_KAIE = 0x37, + WIIPROTO_REQ_DRM_E = 0x3d, + WIIPROTO_REQ_DRM_SKAI1 = 0x3e, + WIIPROTO_REQ_DRM_SKAI2 = 0x3f, +}; + +enum wiiproto_keys { + WIIPROTO_KEY_LEFT, + WIIPROTO_KEY_RIGHT, + WIIPROTO_KEY_UP, + WIIPROTO_KEY_DOWN, + WIIPROTO_KEY_PLUS, + WIIPROTO_KEY_MINUS, + WIIPROTO_KEY_ONE, + WIIPROTO_KEY_TWO, + WIIPROTO_KEY_A, + WIIPROTO_KEY_B, + WIIPROTO_KEY_HOME, + WIIPROTO_KEY_COUNT +}; + +static __u16 wiiproto_keymap[] = { + KEY_LEFT, /* WIIPROTO_KEY_LEFT */ + KEY_RIGHT, /* WIIPROTO_KEY_RIGHT */ + KEY_UP, /* WIIPROTO_KEY_UP */ + KEY_DOWN, /* WIIPROTO_KEY_DOWN */ + KEY_NEXT, /* WIIPROTO_KEY_PLUS */ + KEY_PREVIOUS, /* WIIPROTO_KEY_MINUS */ + BTN_1, /* WIIPROTO_KEY_ONE */ + BTN_2, /* WIIPROTO_KEY_TWO */ + BTN_A, /* WIIPROTO_KEY_A */ + BTN_B, /* WIIPROTO_KEY_B */ + BTN_MODE, /* WIIPROTO_KEY_HOME */ +}; + +static enum power_supply_property wiimote_battery_props[] = { + POWER_SUPPLY_PROP_CAPACITY +}; + +/* requires the state.lock spinlock to be held */ +static inline bool wiimote_cmd_pending(struct wiimote_data *wdata, int cmd, + __u32 opt) +{ + return wdata->state.cmd == cmd && wdata->state.opt == opt; +} + +/* requires the state.lock spinlock to be held */ +static inline void wiimote_cmd_complete(struct wiimote_data *wdata) +{ + wdata->state.cmd = WIIPROTO_REQ_NULL; + complete(&wdata->state.ready); +} + +static inline int wiimote_cmd_acquire(struct wiimote_data *wdata) +{ + return mutex_lock_interruptible(&wdata->state.sync) ? -ERESTARTSYS : 0; +} + +/* requires the state.lock spinlock to be held */ +static inline void wiimote_cmd_set(struct wiimote_data *wdata, int cmd, + __u32 opt) +{ + INIT_COMPLETION(wdata->state.ready); + wdata->state.cmd = cmd; + wdata->state.opt = opt; +} + +static inline void wiimote_cmd_release(struct wiimote_data *wdata) +{ + mutex_unlock(&wdata->state.sync); +} + +static inline int wiimote_cmd_wait(struct wiimote_data *wdata) +{ + int ret; + + ret = wait_for_completion_interruptible_timeout(&wdata->state.ready, HZ); + if (ret < 0) + return -ERESTARTSYS; + else if (ret == 0) + return -EIO; + else + return 0; +} + +static ssize_t wiimote_hid_send(struct hid_device *hdev, __u8 *buffer, + size_t count) +{ + __u8 *buf; + ssize_t ret; + + if (!hdev->hid_output_raw_report) + return -ENODEV; + + buf = kmemdup(buffer, count, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = hdev->hid_output_raw_report(hdev, buf, count, HID_OUTPUT_REPORT); + + kfree(buf); + return ret; +} + +static void wiimote_worker(struct work_struct *work) +{ + struct wiimote_data *wdata = container_of(work, struct wiimote_data, + worker); + unsigned long flags; + + spin_lock_irqsave(&wdata->qlock, flags); + + while (wdata->head != wdata->tail) { + spin_unlock_irqrestore(&wdata->qlock, flags); + wiimote_hid_send(wdata->hdev, wdata->outq[wdata->tail].data, + wdata->outq[wdata->tail].size); + spin_lock_irqsave(&wdata->qlock, flags); + + wdata->tail = (wdata->tail + 1) % WIIMOTE_BUFSIZE; + } + + spin_unlock_irqrestore(&wdata->qlock, flags); +} + +static void wiimote_queue(struct wiimote_data *wdata, const __u8 *buffer, + size_t count) +{ + unsigned long flags; + __u8 newhead; + + if (count > HID_MAX_BUFFER_SIZE) { + hid_warn(wdata->hdev, "Sending too large output report\n"); + return; + } + + /* + * Copy new request into our output queue and check whether the + * queue is full. If it is full, discard this request. + * If it is empty we need to start a new worker that will + * send out the buffer to the hid device. + * If the queue is not empty, then there must be a worker + * that is currently sending out our buffer and this worker + * will reschedule itself until the queue is empty. + */ + + spin_lock_irqsave(&wdata->qlock, flags); + + memcpy(wdata->outq[wdata->head].data, buffer, count); + wdata->outq[wdata->head].size = count; + newhead = (wdata->head + 1) % WIIMOTE_BUFSIZE; + + if (wdata->head == wdata->tail) { + wdata->head = newhead; + schedule_work(&wdata->worker); + } else if (newhead != wdata->tail) { + wdata->head = newhead; + } else { + hid_warn(wdata->hdev, "Output queue is full"); + } + + spin_unlock_irqrestore(&wdata->qlock, flags); +} + +/* + * This sets the rumble bit on the given output report if rumble is + * currently enabled. + * \cmd1 must point to the second byte in the output report => &cmd[1] + * This must be called on nearly every output report before passing it + * into the output queue! + */ +static inline void wiiproto_keep_rumble(struct wiimote_data *wdata, __u8 *cmd1) +{ + if (wdata->state.flags & WIIPROTO_FLAG_RUMBLE) + *cmd1 |= 0x01; +} + +static void wiiproto_req_rumble(struct wiimote_data *wdata, __u8 rumble) +{ + __u8 cmd[2]; + + rumble = !!rumble; + if (rumble == !!(wdata->state.flags & WIIPROTO_FLAG_RUMBLE)) + return; + + if (rumble) + wdata->state.flags |= WIIPROTO_FLAG_RUMBLE; + else + wdata->state.flags &= ~WIIPROTO_FLAG_RUMBLE; + + cmd[0] = WIIPROTO_REQ_RUMBLE; + cmd[1] = 0; + + wiiproto_keep_rumble(wdata, &cmd[1]); + wiimote_queue(wdata, cmd, sizeof(cmd)); +} + +static void wiiproto_req_leds(struct wiimote_data *wdata, int leds) +{ + __u8 cmd[2]; + + leds &= WIIPROTO_FLAGS_LEDS; + if ((wdata->state.flags & WIIPROTO_FLAGS_LEDS) == leds) + return; + wdata->state.flags = (wdata->state.flags & ~WIIPROTO_FLAGS_LEDS) | leds; + + cmd[0] = WIIPROTO_REQ_LED; + cmd[1] = 0; + + if (leds & WIIPROTO_FLAG_LED1) + cmd[1] |= 0x10; + if (leds & WIIPROTO_FLAG_LED2) + cmd[1] |= 0x20; + if (leds & WIIPROTO_FLAG_LED3) + cmd[1] |= 0x40; + if (leds & WIIPROTO_FLAG_LED4) + cmd[1] |= 0x80; + + wiiproto_keep_rumble(wdata, &cmd[1]); + wiimote_queue(wdata, cmd, sizeof(cmd)); +} + +/* + * Check what peripherals of the wiimote are currently + * active and select a proper DRM that supports all of + * the requested data inputs. + */ +static __u8 select_drm(struct wiimote_data *wdata) +{ + __u8 ir = wdata->state.flags & WIIPROTO_FLAGS_IR; + + if (ir == WIIPROTO_FLAG_IR_BASIC) { + if (wdata->state.flags & WIIPROTO_FLAG_ACCEL) + return WIIPROTO_REQ_DRM_KAIE; + else + return WIIPROTO_REQ_DRM_KIE; + } else if (ir == WIIPROTO_FLAG_IR_EXT) { + return WIIPROTO_REQ_DRM_KAI; + } else if (ir == WIIPROTO_FLAG_IR_FULL) { + return WIIPROTO_REQ_DRM_SKAI1; + } else { + if (wdata->state.flags & WIIPROTO_FLAG_ACCEL) + return WIIPROTO_REQ_DRM_KA; + else + return WIIPROTO_REQ_DRM_K; + } +} + +static void wiiproto_req_drm(struct wiimote_data *wdata, __u8 drm) +{ + __u8 cmd[3]; + + if (drm == WIIPROTO_REQ_NULL) + drm = select_drm(wdata); + + cmd[0] = WIIPROTO_REQ_DRM; + cmd[1] = 0; + cmd[2] = drm; + + wiiproto_keep_rumble(wdata, &cmd[1]); + wiimote_queue(wdata, cmd, sizeof(cmd)); +} + +static void wiiproto_req_status(struct wiimote_data *wdata) +{ + __u8 cmd[2]; + + cmd[0] = WIIPROTO_REQ_SREQ; + cmd[1] = 0; + + wiiproto_keep_rumble(wdata, &cmd[1]); + wiimote_queue(wdata, cmd, sizeof(cmd)); +} + +static void wiiproto_req_accel(struct wiimote_data *wdata, __u8 accel) +{ + accel = !!accel; + if (accel == !!(wdata->state.flags & WIIPROTO_FLAG_ACCEL)) + return; + + if (accel) + wdata->state.flags |= WIIPROTO_FLAG_ACCEL; + else + wdata->state.flags &= ~WIIPROTO_FLAG_ACCEL; + + wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL); +} + +static void wiiproto_req_ir1(struct wiimote_data *wdata, __u8 flags) +{ + __u8 cmd[2]; + + cmd[0] = WIIPROTO_REQ_IR1; + cmd[1] = flags; + + wiiproto_keep_rumble(wdata, &cmd[1]); + wiimote_queue(wdata, cmd, sizeof(cmd)); +} + +static void wiiproto_req_ir2(struct wiimote_data *wdata, __u8 flags) +{ + __u8 cmd[2]; + + cmd[0] = WIIPROTO_REQ_IR2; + cmd[1] = flags; + + wiiproto_keep_rumble(wdata, &cmd[1]); + wiimote_queue(wdata, cmd, sizeof(cmd)); +} + +#define wiiproto_req_wreg(wdata, os, buf, sz) \ + wiiproto_req_wmem((wdata), false, (os), (buf), (sz)) + +#define wiiproto_req_weeprom(wdata, os, buf, sz) \ + wiiproto_req_wmem((wdata), true, (os), (buf), (sz)) + +static void wiiproto_req_wmem(struct wiimote_data *wdata, bool eeprom, + __u32 offset, const __u8 *buf, __u8 size) +{ + __u8 cmd[22]; + + if (size > 16 || size == 0) { + hid_warn(wdata->hdev, "Invalid length %d wmem request\n", size); + return; + } + + memset(cmd, 0, sizeof(cmd)); + cmd[0] = WIIPROTO_REQ_WMEM; + cmd[2] = (offset >> 16) & 0xff; + cmd[3] = (offset >> 8) & 0xff; + cmd[4] = offset & 0xff; + cmd[5] = size; + memcpy(&cmd[6], buf, size); + + if (!eeprom) + cmd[1] |= 0x04; + + wiiproto_keep_rumble(wdata, &cmd[1]); + wiimote_queue(wdata, cmd, sizeof(cmd)); +} + +/* requries the cmd-mutex to be held */ +static int wiimote_cmd_write(struct wiimote_data *wdata, __u32 offset, + const __u8 *wmem, __u8 size) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&wdata->state.lock, flags); + wiimote_cmd_set(wdata, WIIPROTO_REQ_WMEM, 0); + wiiproto_req_wreg(wdata, offset, wmem, size); + spin_unlock_irqrestore(&wdata->state.lock, flags); + + ret = wiimote_cmd_wait(wdata); + if (!ret && wdata->state.cmd_err) + ret = -EIO; + + return ret; +} + +static int wiimote_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct wiimote_data *wdata = container_of(psy, + struct wiimote_data, battery); + int ret = 0, state; + unsigned long flags; + + ret = wiimote_cmd_acquire(wdata); + if (ret) + return ret; + + spin_lock_irqsave(&wdata->state.lock, flags); + wiimote_cmd_set(wdata, WIIPROTO_REQ_SREQ, 0); + wiiproto_req_status(wdata); + spin_unlock_irqrestore(&wdata->state.lock, flags); + + ret = wiimote_cmd_wait(wdata); + state = wdata->state.cmd_battery; + wiimote_cmd_release(wdata); + + if (ret) + return ret; + + switch (psp) { + case POWER_SUPPLY_PROP_CAPACITY: + val->intval = state * 100 / 255; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int wiimote_init_ir(struct wiimote_data *wdata, __u16 mode) +{ + int ret; + unsigned long flags; + __u8 format = 0; + static const __u8 data_enable[] = { 0x01 }; + static const __u8 data_sens1[] = { 0x02, 0x00, 0x00, 0x71, 0x01, + 0x00, 0xaa, 0x00, 0x64 }; + static const __u8 data_sens2[] = { 0x63, 0x03 }; + static const __u8 data_fin[] = { 0x08 }; + + spin_lock_irqsave(&wdata->state.lock, flags); + + if (mode == (wdata->state.flags & WIIPROTO_FLAGS_IR)) { + spin_unlock_irqrestore(&wdata->state.lock, flags); + return 0; + } + + if (mode == 0) { + wdata->state.flags &= ~WIIPROTO_FLAGS_IR; + wiiproto_req_ir1(wdata, 0); + wiiproto_req_ir2(wdata, 0); + wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL); + spin_unlock_irqrestore(&wdata->state.lock, flags); + return 0; + } + + spin_unlock_irqrestore(&wdata->state.lock, flags); + + ret = wiimote_cmd_acquire(wdata); + if (ret) + return ret; + + /* send PIXEL CLOCK ENABLE cmd first */ + spin_lock_irqsave(&wdata->state.lock, flags); + wiimote_cmd_set(wdata, WIIPROTO_REQ_IR1, 0); + wiiproto_req_ir1(wdata, 0x06); + spin_unlock_irqrestore(&wdata->state.lock, flags); + + ret = wiimote_cmd_wait(wdata); + if (ret) + goto unlock; + if (wdata->state.cmd_err) { + ret = -EIO; + goto unlock; + } + + /* enable IR LOGIC */ + spin_lock_irqsave(&wdata->state.lock, flags); + wiimote_cmd_set(wdata, WIIPROTO_REQ_IR2, 0); + wiiproto_req_ir2(wdata, 0x06); + spin_unlock_irqrestore(&wdata->state.lock, flags); + + ret = wiimote_cmd_wait(wdata); + if (ret) + goto unlock; + if (wdata->state.cmd_err) { + ret = -EIO; + goto unlock; + } + + /* enable IR cam but do not make it send data, yet */ + ret = wiimote_cmd_write(wdata, 0xb00030, data_enable, + sizeof(data_enable)); + if (ret) + goto unlock; + + /* write first sensitivity block */ + ret = wiimote_cmd_write(wdata, 0xb00000, data_sens1, + sizeof(data_sens1)); + if (ret) + goto unlock; + + /* write second sensitivity block */ + ret = wiimote_cmd_write(wdata, 0xb0001a, data_sens2, + sizeof(data_sens2)); + if (ret) + goto unlock; + + /* put IR cam into desired state */ + switch (mode) { + case WIIPROTO_FLAG_IR_FULL: + format = 5; + break; + case WIIPROTO_FLAG_IR_EXT: + format = 3; + break; + case WIIPROTO_FLAG_IR_BASIC: + format = 1; + break; + } + ret = wiimote_cmd_write(wdata, 0xb00033, &format, sizeof(format)); + if (ret) + goto unlock; + + /* make IR cam send data */ + ret = wiimote_cmd_write(wdata, 0xb00030, data_fin, sizeof(data_fin)); + if (ret) + goto unlock; + + /* request new DRM mode compatible to IR mode */ + spin_lock_irqsave(&wdata->state.lock, flags); + wdata->state.flags &= ~WIIPROTO_FLAGS_IR; + wdata->state.flags |= mode & WIIPROTO_FLAGS_IR; + wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL); + spin_unlock_irqrestore(&wdata->state.lock, flags); + +unlock: + wiimote_cmd_release(wdata); + return ret; +} + +static enum led_brightness wiimote_leds_get(struct led_classdev *led_dev) +{ + struct wiimote_data *wdata; + struct device *dev = led_dev->dev->parent; + int i; + unsigned long flags; + bool value = false; + + wdata = hid_get_drvdata(container_of(dev, struct hid_device, dev)); + + for (i = 0; i < 4; ++i) { + if (wdata->leds[i] == led_dev) { + spin_lock_irqsave(&wdata->state.lock, flags); + value = wdata->state.flags & WIIPROTO_FLAG_LED(i + 1); + spin_unlock_irqrestore(&wdata->state.lock, flags); + break; + } + } + + return value ? LED_FULL : LED_OFF; +} + +static void wiimote_leds_set(struct led_classdev *led_dev, + enum led_brightness value) +{ + struct wiimote_data *wdata; + struct device *dev = led_dev->dev->parent; + int i; + unsigned long flags; + __u8 state, flag; + + wdata = hid_get_drvdata(container_of(dev, struct hid_device, dev)); + + for (i = 0; i < 4; ++i) { + if (wdata->leds[i] == led_dev) { + flag = WIIPROTO_FLAG_LED(i + 1); + spin_lock_irqsave(&wdata->state.lock, flags); + state = wdata->state.flags; + if (value == LED_OFF) + wiiproto_req_leds(wdata, state & ~flag); + else + wiiproto_req_leds(wdata, state | flag); + spin_unlock_irqrestore(&wdata->state.lock, flags); + break; + } + } +} + +static int wiimote_ff_play(struct input_dev *dev, void *data, + struct ff_effect *eff) +{ + struct wiimote_data *wdata = input_get_drvdata(dev); + __u8 value; + unsigned long flags; + + /* + * The wiimote supports only a single rumble motor so if any magnitude + * is set to non-zero then we start the rumble motor. If both are set to + * zero, we stop the rumble motor. + */ + + if (eff->u.rumble.strong_magnitude || eff->u.rumble.weak_magnitude) + value = 1; + else + value = 0; + + spin_lock_irqsave(&wdata->state.lock, flags); + wiiproto_req_rumble(wdata, value); + spin_unlock_irqrestore(&wdata->state.lock, flags); + + return 0; +} + +static int wiimote_input_open(struct input_dev *dev) +{ + struct wiimote_data *wdata = input_get_drvdata(dev); + + return hid_hw_open(wdata->hdev); +} + +static void wiimote_input_close(struct input_dev *dev) +{ + struct wiimote_data *wdata = input_get_drvdata(dev); + + hid_hw_close(wdata->hdev); +} + +static int wiimote_accel_open(struct input_dev *dev) +{ + struct wiimote_data *wdata = input_get_drvdata(dev); + int ret; + unsigned long flags; + + ret = hid_hw_open(wdata->hdev); + if (ret) + return ret; + + spin_lock_irqsave(&wdata->state.lock, flags); + wiiproto_req_accel(wdata, true); + spin_unlock_irqrestore(&wdata->state.lock, flags); + + return 0; +} + +static void wiimote_accel_close(struct input_dev *dev) +{ + struct wiimote_data *wdata = input_get_drvdata(dev); + unsigned long flags; + + spin_lock_irqsave(&wdata->state.lock, flags); + wiiproto_req_accel(wdata, false); + spin_unlock_irqrestore(&wdata->state.lock, flags); + + hid_hw_close(wdata->hdev); +} + +static int wiimote_ir_open(struct input_dev *dev) +{ + struct wiimote_data *wdata = input_get_drvdata(dev); + int ret; + + ret = hid_hw_open(wdata->hdev); + if (ret) + return ret; + + ret = wiimote_init_ir(wdata, WIIPROTO_FLAG_IR_BASIC); + if (ret) { + hid_hw_close(wdata->hdev); + return ret; + } + + return 0; +} + +static void wiimote_ir_close(struct input_dev *dev) +{ + struct wiimote_data *wdata = input_get_drvdata(dev); + + wiimote_init_ir(wdata, 0); + hid_hw_close(wdata->hdev); +} + +static void handler_keys(struct wiimote_data *wdata, const __u8 *payload) +{ + input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_LEFT], + !!(payload[0] & 0x01)); + input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_RIGHT], + !!(payload[0] & 0x02)); + input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_DOWN], + !!(payload[0] & 0x04)); + input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_UP], + !!(payload[0] & 0x08)); + input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_PLUS], + !!(payload[0] & 0x10)); + input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_TWO], + !!(payload[1] & 0x01)); + input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_ONE], + !!(payload[1] & 0x02)); + input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_B], + !!(payload[1] & 0x04)); + input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_A], + !!(payload[1] & 0x08)); + input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_MINUS], + !!(payload[1] & 0x10)); + input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_HOME], + !!(payload[1] & 0x80)); + input_sync(wdata->input); +} + +static void handler_accel(struct wiimote_data *wdata, const __u8 *payload) +{ + __u16 x, y, z; + + if (!(wdata->state.flags & WIIPROTO_FLAG_ACCEL)) + return; + + /* + * payload is: BB BB XX YY ZZ + * Accelerometer data is encoded into 3 10bit values. XX, YY and ZZ + * contain the upper 8 bits of each value. The lower 2 bits are + * contained in the buttons data BB BB. + * Bits 6 and 7 of the first buttons byte BB is the lower 2 bits of the + * X accel value. Bit 5 of the second buttons byte is the 2nd bit of Y + * accel value and bit 6 is the second bit of the Z value. + * The first bit of Y and Z values is not available and always set to 0. + * 0x200 is returned on no movement. + */ + + x = payload[2] << 2; + y = payload[3] << 2; + z = payload[4] << 2; + + x |= (payload[0] >> 5) & 0x3; + y |= (payload[1] >> 4) & 0x2; + z |= (payload[1] >> 5) & 0x2; + + input_report_abs(wdata->accel, ABS_RX, x - 0x200); + input_report_abs(wdata->accel, ABS_RY, y - 0x200); + input_report_abs(wdata->accel, ABS_RZ, z - 0x200); + input_sync(wdata->accel); +} + +#define ir_to_input0(wdata, ir, packed) __ir_to_input((wdata), (ir), (packed), \ + ABS_HAT0X, ABS_HAT0Y) +#define ir_to_input1(wdata, ir, packed) __ir_to_input((wdata), (ir), (packed), \ + ABS_HAT1X, ABS_HAT1Y) +#define ir_to_input2(wdata, ir, packed) __ir_to_input((wdata), (ir), (packed), \ + ABS_HAT2X, ABS_HAT2Y) +#define ir_to_input3(wdata, ir, packed) __ir_to_input((wdata), (ir), (packed), \ + ABS_HAT3X, ABS_HAT3Y) + +static void __ir_to_input(struct wiimote_data *wdata, const __u8 *ir, + bool packed, __u8 xid, __u8 yid) +{ + __u16 x, y; + + if (!(wdata->state.flags & WIIPROTO_FLAGS_IR)) + return; + + /* + * Basic IR data is encoded into 3 bytes. The first two bytes are the + * upper 8 bit of the X/Y data, the 3rd byte contains the lower 2 bits + * of both. + * If data is packed, then the 3rd byte is put first and slightly + * reordered. This allows to interleave packed and non-packed data to + * have two IR sets in 5 bytes instead of 6. + * The resulting 10bit X/Y values are passed to the ABS_HATXY input dev. + */ + + if (packed) { + x = ir[1] << 2; + y = ir[2] << 2; + + x |= ir[0] & 0x3; + y |= (ir[0] >> 2) & 0x3; + } else { + x = ir[0] << 2; + y = ir[1] << 2; + + x |= (ir[2] >> 4) & 0x3; + y |= (ir[2] >> 6) & 0x3; + } + + input_report_abs(wdata->ir, xid, x); + input_report_abs(wdata->ir, yid, y); +} + +static void handler_status(struct wiimote_data *wdata, const __u8 *payload) +{ + handler_keys(wdata, payload); + + /* on status reports the drm is reset so we need to resend the drm */ + wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL); + + if (wiimote_cmd_pending(wdata, WIIPROTO_REQ_SREQ, 0)) { + wdata->state.cmd_battery = payload[5]; + wiimote_cmd_complete(wdata); + } +} + +static void handler_data(struct wiimote_data *wdata, const __u8 *payload) +{ + handler_keys(wdata, payload); +} + +static void handler_return(struct wiimote_data *wdata, const __u8 *payload) +{ + __u8 err = payload[3]; + __u8 cmd = payload[2]; + + handler_keys(wdata, payload); + + if (wiimote_cmd_pending(wdata, cmd, 0)) { + wdata->state.cmd_err = err; + wiimote_cmd_complete(wdata); + } else if (err) { + hid_warn(wdata->hdev, "Remote error %hhu on req %hhu\n", err, + cmd); + } +} + +static void handler_drm_KA(struct wiimote_data *wdata, const __u8 *payload) +{ + handler_keys(wdata, payload); + handler_accel(wdata, payload); +} + +static void handler_drm_KE(struct wiimote_data *wdata, const __u8 *payload) +{ + handler_keys(wdata, payload); +} + +static void handler_drm_KAI(struct wiimote_data *wdata, const __u8 *payload) +{ + handler_keys(wdata, payload); + handler_accel(wdata, payload); + ir_to_input0(wdata, &payload[5], false); + ir_to_input1(wdata, &payload[8], false); + ir_to_input2(wdata, &payload[11], false); + ir_to_input3(wdata, &payload[14], false); + input_sync(wdata->ir); +} + +static void handler_drm_KEE(struct wiimote_data *wdata, const __u8 *payload) +{ + handler_keys(wdata, payload); +} + +static void handler_drm_KIE(struct wiimote_data *wdata, const __u8 *payload) +{ + handler_keys(wdata, payload); + ir_to_input0(wdata, &payload[2], false); + ir_to_input1(wdata, &payload[4], true); + ir_to_input2(wdata, &payload[7], false); + ir_to_input3(wdata, &payload[9], true); + input_sync(wdata->ir); +} + +static void handler_drm_KAE(struct wiimote_data *wdata, const __u8 *payload) +{ + handler_keys(wdata, payload); + handler_accel(wdata, payload); +} + +static void handler_drm_KAIE(struct wiimote_data *wdata, const __u8 *payload) +{ + handler_keys(wdata, payload); + handler_accel(wdata, payload); + ir_to_input0(wdata, &payload[5], false); + ir_to_input1(wdata, &payload[7], true); + ir_to_input2(wdata, &payload[10], false); + ir_to_input3(wdata, &payload[12], true); + input_sync(wdata->ir); +} + +static void handler_drm_E(struct wiimote_data *wdata, const __u8 *payload) +{ +} + +static void handler_drm_SKAI1(struct wiimote_data *wdata, const __u8 *payload) +{ + handler_keys(wdata, payload); + + wdata->state.accel_split[0] = payload[2]; + wdata->state.accel_split[1] = (payload[0] >> 1) & (0x10 | 0x20); + wdata->state.accel_split[1] |= (payload[1] << 1) & (0x40 | 0x80); + + ir_to_input0(wdata, &payload[3], false); + ir_to_input1(wdata, &payload[12], false); + input_sync(wdata->ir); +} + +static void handler_drm_SKAI2(struct wiimote_data *wdata, const __u8 *payload) +{ + __u8 buf[5]; + + handler_keys(wdata, payload); + + wdata->state.accel_split[1] |= (payload[0] >> 5) & (0x01 | 0x02); + wdata->state.accel_split[1] |= (payload[1] >> 3) & (0x04 | 0x08); + + buf[0] = 0; + buf[1] = 0; + buf[2] = wdata->state.accel_split[0]; + buf[3] = payload[2]; + buf[4] = wdata->state.accel_split[1]; + handler_accel(wdata, buf); + + ir_to_input2(wdata, &payload[3], false); + ir_to_input3(wdata, &payload[12], false); + input_sync(wdata->ir); +} + +struct wiiproto_handler { + __u8 id; + size_t size; + void (*func)(struct wiimote_data *wdata, const __u8 *payload); +}; + +static struct wiiproto_handler handlers[] = { + { .id = WIIPROTO_REQ_STATUS, .size = 6, .func = handler_status }, + { .id = WIIPROTO_REQ_DATA, .size = 21, .func = handler_data }, + { .id = WIIPROTO_REQ_RETURN, .size = 4, .func = handler_return }, + { .id = WIIPROTO_REQ_DRM_K, .size = 2, .func = handler_keys }, + { .id = WIIPROTO_REQ_DRM_KA, .size = 5, .func = handler_drm_KA }, + { .id = WIIPROTO_REQ_DRM_KE, .size = 10, .func = handler_drm_KE }, + { .id = WIIPROTO_REQ_DRM_KAI, .size = 17, .func = handler_drm_KAI }, + { .id = WIIPROTO_REQ_DRM_KEE, .size = 21, .func = handler_drm_KEE }, + { .id = WIIPROTO_REQ_DRM_KAE, .size = 21, .func = handler_drm_KAE }, + { .id = WIIPROTO_REQ_DRM_KIE, .size = 21, .func = handler_drm_KIE }, + { .id = WIIPROTO_REQ_DRM_KAIE, .size = 21, .func = handler_drm_KAIE }, + { .id = WIIPROTO_REQ_DRM_E, .size = 21, .func = handler_drm_E }, + { .id = WIIPROTO_REQ_DRM_SKAI1, .size = 21, .func = handler_drm_SKAI1 }, + { .id = WIIPROTO_REQ_DRM_SKAI2, .size = 21, .func = handler_drm_SKAI2 }, + { .id = 0 } +}; + +static int wiimote_hid_event(struct hid_device *hdev, struct hid_report *report, + u8 *raw_data, int size) +{ + struct wiimote_data *wdata = hid_get_drvdata(hdev); + struct wiiproto_handler *h; + int i; + unsigned long flags; + bool handled = false; + + if (size < 1) + return -EINVAL; + + spin_lock_irqsave(&wdata->state.lock, flags); + + for (i = 0; handlers[i].id; ++i) { + h = &handlers[i]; + if (h->id == raw_data[0] && h->size < size) { + h->func(wdata, &raw_data[1]); + handled = true; + } + } + + if (!handled) + hid_warn(hdev, "Unhandled report %hhu size %d\n", raw_data[0], + size); + + spin_unlock_irqrestore(&wdata->state.lock, flags); + + return 0; +} + +static void wiimote_leds_destroy(struct wiimote_data *wdata) +{ + int i; + struct led_classdev *led; + + for (i = 0; i < 4; ++i) { + if (wdata->leds[i]) { + led = wdata->leds[i]; + wdata->leds[i] = NULL; + led_classdev_unregister(led); + kfree(led); + } + } +} + +static int wiimote_leds_create(struct wiimote_data *wdata) +{ + int i, ret; + struct device *dev = &wdata->hdev->dev; + size_t namesz = strlen(dev_name(dev)) + 9; + struct led_classdev *led; + char *name; + + for (i = 0; i < 4; ++i) { + led = kzalloc(sizeof(struct led_classdev) + namesz, GFP_KERNEL); + if (!led) { + ret = -ENOMEM; + goto err; + } + name = (void*)&led[1]; + snprintf(name, namesz, "%s:blue:p%d", dev_name(dev), i); + led->name = name; + led->brightness = 0; + led->max_brightness = 1; + led->brightness_get = wiimote_leds_get; + led->brightness_set = wiimote_leds_set; + + ret = led_classdev_register(dev, led); + if (ret) { + kfree(led); + goto err; + } + wdata->leds[i] = led; + } + + return 0; + +err: + wiimote_leds_destroy(wdata); + return ret; +} + +static struct wiimote_data *wiimote_create(struct hid_device *hdev) +{ + struct wiimote_data *wdata; + int i; + + wdata = kzalloc(sizeof(*wdata), GFP_KERNEL); + if (!wdata) + return NULL; + + wdata->input = input_allocate_device(); + if (!wdata->input) + goto err; + + wdata->hdev = hdev; + hid_set_drvdata(hdev, wdata); + + input_set_drvdata(wdata->input, wdata); + wdata->input->open = wiimote_input_open; + wdata->input->close = wiimote_input_close; + wdata->input->dev.parent = &wdata->hdev->dev; + wdata->input->id.bustype = wdata->hdev->bus; + wdata->input->id.vendor = wdata->hdev->vendor; + wdata->input->id.product = wdata->hdev->product; + wdata->input->id.version = wdata->hdev->version; + wdata->input->name = WIIMOTE_NAME; + + set_bit(EV_KEY, wdata->input->evbit); + for (i = 0; i < WIIPROTO_KEY_COUNT; ++i) + set_bit(wiiproto_keymap[i], wdata->input->keybit); + + set_bit(FF_RUMBLE, wdata->input->ffbit); + if (input_ff_create_memless(wdata->input, NULL, wiimote_ff_play)) + goto err_input; + + wdata->accel = input_allocate_device(); + if (!wdata->accel) + goto err_input; + + input_set_drvdata(wdata->accel, wdata); + wdata->accel->open = wiimote_accel_open; + wdata->accel->close = wiimote_accel_close; + wdata->accel->dev.parent = &wdata->hdev->dev; + wdata->accel->id.bustype = wdata->hdev->bus; + wdata->accel->id.vendor = wdata->hdev->vendor; + wdata->accel->id.product = wdata->hdev->product; + wdata->accel->id.version = wdata->hdev->version; + wdata->accel->name = WIIMOTE_NAME " Accelerometer"; + + set_bit(EV_ABS, wdata->accel->evbit); + set_bit(ABS_RX, wdata->accel->absbit); + set_bit(ABS_RY, wdata->accel->absbit); + set_bit(ABS_RZ, wdata->accel->absbit); + input_set_abs_params(wdata->accel, ABS_RX, -500, 500, 2, 4); + input_set_abs_params(wdata->accel, ABS_RY, -500, 500, 2, 4); + input_set_abs_params(wdata->accel, ABS_RZ, -500, 500, 2, 4); + + wdata->ir = input_allocate_device(); + if (!wdata->ir) + goto err_ir; + + input_set_drvdata(wdata->ir, wdata); + wdata->ir->open = wiimote_ir_open; + wdata->ir->close = wiimote_ir_close; + wdata->ir->dev.parent = &wdata->hdev->dev; + wdata->ir->id.bustype = wdata->hdev->bus; + wdata->ir->id.vendor = wdata->hdev->vendor; + wdata->ir->id.product = wdata->hdev->product; + wdata->ir->id.version = wdata->hdev->version; + wdata->ir->name = WIIMOTE_NAME " IR"; + + set_bit(EV_ABS, wdata->ir->evbit); + set_bit(ABS_HAT0X, wdata->ir->absbit); + set_bit(ABS_HAT0Y, wdata->ir->absbit); + set_bit(ABS_HAT1X, wdata->ir->absbit); + set_bit(ABS_HAT1Y, wdata->ir->absbit); + set_bit(ABS_HAT2X, wdata->ir->absbit); + set_bit(ABS_HAT2Y, wdata->ir->absbit); + set_bit(ABS_HAT3X, wdata->ir->absbit); + set_bit(ABS_HAT3Y, wdata->ir->absbit); + input_set_abs_params(wdata->ir, ABS_HAT0X, 0, 1023, 2, 4); + input_set_abs_params(wdata->ir, ABS_HAT0Y, 0, 767, 2, 4); + input_set_abs_params(wdata->ir, ABS_HAT1X, 0, 1023, 2, 4); + input_set_abs_params(wdata->ir, ABS_HAT1Y, 0, 767, 2, 4); + input_set_abs_params(wdata->ir, ABS_HAT2X, 0, 1023, 2, 4); + input_set_abs_params(wdata->ir, ABS_HAT2Y, 0, 767, 2, 4); + input_set_abs_params(wdata->ir, ABS_HAT3X, 0, 1023, 2, 4); + input_set_abs_params(wdata->ir, ABS_HAT3Y, 0, 767, 2, 4); + + spin_lock_init(&wdata->qlock); + INIT_WORK(&wdata->worker, wiimote_worker); + + spin_lock_init(&wdata->state.lock); + init_completion(&wdata->state.ready); + mutex_init(&wdata->state.sync); + + return wdata; + +err_ir: + input_free_device(wdata->accel); +err_input: + input_free_device(wdata->input); +err: + kfree(wdata); + return NULL; +} + +static void wiimote_destroy(struct wiimote_data *wdata) +{ + wiimote_leds_destroy(wdata); + + power_supply_unregister(&wdata->battery); + input_unregister_device(wdata->accel); + input_unregister_device(wdata->ir); + input_unregister_device(wdata->input); + cancel_work_sync(&wdata->worker); + hid_hw_stop(wdata->hdev); + + kfree(wdata); +} + +static int wiimote_hid_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + struct wiimote_data *wdata; + int ret; + + wdata = wiimote_create(hdev); + if (!wdata) { + hid_err(hdev, "Can't alloc device\n"); + return -ENOMEM; + } + + ret = hid_parse(hdev); + if (ret) { + hid_err(hdev, "HID parse failed\n"); + goto err; + } + + ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW); + if (ret) { + hid_err(hdev, "HW start failed\n"); + goto err; + } + + ret = input_register_device(wdata->accel); + if (ret) { + hid_err(hdev, "Cannot register input device\n"); + goto err_stop; + } + + ret = input_register_device(wdata->ir); + if (ret) { + hid_err(hdev, "Cannot register input device\n"); + goto err_ir; + } + + ret = input_register_device(wdata->input); + if (ret) { + hid_err(hdev, "Cannot register input device\n"); + goto err_input; + } + + wdata->battery.properties = wiimote_battery_props; + wdata->battery.num_properties = ARRAY_SIZE(wiimote_battery_props); + wdata->battery.get_property = wiimote_battery_get_property; + wdata->battery.name = "wiimote_battery"; + wdata->battery.type = POWER_SUPPLY_TYPE_BATTERY; + wdata->battery.use_for_apm = 0; + + ret = power_supply_register(&wdata->hdev->dev, &wdata->battery); + if (ret) { + hid_err(hdev, "Cannot register battery device\n"); + goto err_battery; + } + + ret = wiimote_leds_create(wdata); + if (ret) + goto err_free; + + hid_info(hdev, "New device registered\n"); + + /* by default set led1 after device initialization */ + spin_lock_irq(&wdata->state.lock); + wiiproto_req_leds(wdata, WIIPROTO_FLAG_LED1); + spin_unlock_irq(&wdata->state.lock); + + return 0; + +err_free: + wiimote_destroy(wdata); + return ret; + +err_battery: + input_unregister_device(wdata->input); + wdata->input = NULL; +err_input: + input_unregister_device(wdata->ir); + wdata->ir = NULL; +err_ir: + input_unregister_device(wdata->accel); + wdata->accel = NULL; +err_stop: + hid_hw_stop(hdev); +err: + input_free_device(wdata->ir); + input_free_device(wdata->accel); + input_free_device(wdata->input); + kfree(wdata); + return ret; +} + +static void wiimote_hid_remove(struct hid_device *hdev) +{ + struct wiimote_data *wdata = hid_get_drvdata(hdev); + + hid_info(hdev, "Device removed\n"); + wiimote_destroy(wdata); +} + +static const struct hid_device_id wiimote_hid_devices[] = { + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, + USB_DEVICE_ID_NINTENDO_WIIMOTE) }, + { } +}; +MODULE_DEVICE_TABLE(hid, wiimote_hid_devices); + +static struct hid_driver wiimote_hid_driver = { + .name = "wiimote", + .id_table = wiimote_hid_devices, + .probe = wiimote_hid_probe, + .remove = wiimote_hid_remove, + .raw_event = wiimote_hid_event, +}; + +static int __init wiimote_init(void) +{ + int ret; + + ret = hid_register_driver(&wiimote_hid_driver); + if (ret) + pr_err("Can't register wiimote hid driver\n"); + + return ret; +} + +static void __exit wiimote_exit(void) +{ + hid_unregister_driver(&wiimote_hid_driver); +} + +module_init(wiimote_init); +module_exit(wiimote_exit); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Herrmann "); +MODULE_DESCRIPTION(WIIMOTE_NAME " Device Driver"); +MODULE_VERSION(WIIMOTE_VERSION); diff --git a/drivers/hid/hid-wiimote.c b/drivers/hid/hid-wiimote.c deleted file mode 100644 index 76739c0..0000000 --- a/drivers/hid/hid-wiimote.c +++ /dev/null @@ -1,1346 +0,0 @@ -/* - * HID driver for Nintendo Wiimote devices - * Copyright (c) 2011 David Herrmann - */ - -/* - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "hid-ids.h" - -#define WIIMOTE_VERSION "0.2" -#define WIIMOTE_NAME "Nintendo Wii Remote" -#define WIIMOTE_BUFSIZE 32 - -struct wiimote_buf { - __u8 data[HID_MAX_BUFFER_SIZE]; - size_t size; -}; - -struct wiimote_state { - spinlock_t lock; - __u8 flags; - __u8 accel_split[2]; - - /* synchronous cmd requests */ - struct mutex sync; - struct completion ready; - int cmd; - __u32 opt; - - /* results of synchronous requests */ - __u8 cmd_battery; - __u8 cmd_err; -}; - -struct wiimote_data { - struct hid_device *hdev; - struct input_dev *input; - struct led_classdev *leds[4]; - struct input_dev *accel; - struct input_dev *ir; - struct power_supply battery; - - spinlock_t qlock; - __u8 head; - __u8 tail; - struct wiimote_buf outq[WIIMOTE_BUFSIZE]; - struct work_struct worker; - - struct wiimote_state state; -}; - -#define WIIPROTO_FLAG_LED1 0x01 -#define WIIPROTO_FLAG_LED2 0x02 -#define WIIPROTO_FLAG_LED3 0x04 -#define WIIPROTO_FLAG_LED4 0x08 -#define WIIPROTO_FLAG_RUMBLE 0x10 -#define WIIPROTO_FLAG_ACCEL 0x20 -#define WIIPROTO_FLAG_IR_BASIC 0x40 -#define WIIPROTO_FLAG_IR_EXT 0x80 -#define WIIPROTO_FLAG_IR_FULL 0xc0 /* IR_BASIC | IR_EXT */ -#define WIIPROTO_FLAGS_LEDS (WIIPROTO_FLAG_LED1 | WIIPROTO_FLAG_LED2 | \ - WIIPROTO_FLAG_LED3 | WIIPROTO_FLAG_LED4) -#define WIIPROTO_FLAGS_IR (WIIPROTO_FLAG_IR_BASIC | WIIPROTO_FLAG_IR_EXT | \ - WIIPROTO_FLAG_IR_FULL) - -/* return flag for led \num */ -#define WIIPROTO_FLAG_LED(num) (WIIPROTO_FLAG_LED1 << (num - 1)) - -enum wiiproto_reqs { - WIIPROTO_REQ_NULL = 0x0, - WIIPROTO_REQ_RUMBLE = 0x10, - WIIPROTO_REQ_LED = 0x11, - WIIPROTO_REQ_DRM = 0x12, - WIIPROTO_REQ_IR1 = 0x13, - WIIPROTO_REQ_SREQ = 0x15, - WIIPROTO_REQ_WMEM = 0x16, - WIIPROTO_REQ_RMEM = 0x17, - WIIPROTO_REQ_IR2 = 0x1a, - WIIPROTO_REQ_STATUS = 0x20, - WIIPROTO_REQ_DATA = 0x21, - WIIPROTO_REQ_RETURN = 0x22, - WIIPROTO_REQ_DRM_K = 0x30, - WIIPROTO_REQ_DRM_KA = 0x31, - WIIPROTO_REQ_DRM_KE = 0x32, - WIIPROTO_REQ_DRM_KAI = 0x33, - WIIPROTO_REQ_DRM_KEE = 0x34, - WIIPROTO_REQ_DRM_KAE = 0x35, - WIIPROTO_REQ_DRM_KIE = 0x36, - WIIPROTO_REQ_DRM_KAIE = 0x37, - WIIPROTO_REQ_DRM_E = 0x3d, - WIIPROTO_REQ_DRM_SKAI1 = 0x3e, - WIIPROTO_REQ_DRM_SKAI2 = 0x3f, -}; - -enum wiiproto_keys { - WIIPROTO_KEY_LEFT, - WIIPROTO_KEY_RIGHT, - WIIPROTO_KEY_UP, - WIIPROTO_KEY_DOWN, - WIIPROTO_KEY_PLUS, - WIIPROTO_KEY_MINUS, - WIIPROTO_KEY_ONE, - WIIPROTO_KEY_TWO, - WIIPROTO_KEY_A, - WIIPROTO_KEY_B, - WIIPROTO_KEY_HOME, - WIIPROTO_KEY_COUNT -}; - -static __u16 wiiproto_keymap[] = { - KEY_LEFT, /* WIIPROTO_KEY_LEFT */ - KEY_RIGHT, /* WIIPROTO_KEY_RIGHT */ - KEY_UP, /* WIIPROTO_KEY_UP */ - KEY_DOWN, /* WIIPROTO_KEY_DOWN */ - KEY_NEXT, /* WIIPROTO_KEY_PLUS */ - KEY_PREVIOUS, /* WIIPROTO_KEY_MINUS */ - BTN_1, /* WIIPROTO_KEY_ONE */ - BTN_2, /* WIIPROTO_KEY_TWO */ - BTN_A, /* WIIPROTO_KEY_A */ - BTN_B, /* WIIPROTO_KEY_B */ - BTN_MODE, /* WIIPROTO_KEY_HOME */ -}; - -static enum power_supply_property wiimote_battery_props[] = { - POWER_SUPPLY_PROP_CAPACITY -}; - -/* requires the state.lock spinlock to be held */ -static inline bool wiimote_cmd_pending(struct wiimote_data *wdata, int cmd, - __u32 opt) -{ - return wdata->state.cmd == cmd && wdata->state.opt == opt; -} - -/* requires the state.lock spinlock to be held */ -static inline void wiimote_cmd_complete(struct wiimote_data *wdata) -{ - wdata->state.cmd = WIIPROTO_REQ_NULL; - complete(&wdata->state.ready); -} - -static inline int wiimote_cmd_acquire(struct wiimote_data *wdata) -{ - return mutex_lock_interruptible(&wdata->state.sync) ? -ERESTARTSYS : 0; -} - -/* requires the state.lock spinlock to be held */ -static inline void wiimote_cmd_set(struct wiimote_data *wdata, int cmd, - __u32 opt) -{ - INIT_COMPLETION(wdata->state.ready); - wdata->state.cmd = cmd; - wdata->state.opt = opt; -} - -static inline void wiimote_cmd_release(struct wiimote_data *wdata) -{ - mutex_unlock(&wdata->state.sync); -} - -static inline int wiimote_cmd_wait(struct wiimote_data *wdata) -{ - int ret; - - ret = wait_for_completion_interruptible_timeout(&wdata->state.ready, HZ); - if (ret < 0) - return -ERESTARTSYS; - else if (ret == 0) - return -EIO; - else - return 0; -} - -static ssize_t wiimote_hid_send(struct hid_device *hdev, __u8 *buffer, - size_t count) -{ - __u8 *buf; - ssize_t ret; - - if (!hdev->hid_output_raw_report) - return -ENODEV; - - buf = kmemdup(buffer, count, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - ret = hdev->hid_output_raw_report(hdev, buf, count, HID_OUTPUT_REPORT); - - kfree(buf); - return ret; -} - -static void wiimote_worker(struct work_struct *work) -{ - struct wiimote_data *wdata = container_of(work, struct wiimote_data, - worker); - unsigned long flags; - - spin_lock_irqsave(&wdata->qlock, flags); - - while (wdata->head != wdata->tail) { - spin_unlock_irqrestore(&wdata->qlock, flags); - wiimote_hid_send(wdata->hdev, wdata->outq[wdata->tail].data, - wdata->outq[wdata->tail].size); - spin_lock_irqsave(&wdata->qlock, flags); - - wdata->tail = (wdata->tail + 1) % WIIMOTE_BUFSIZE; - } - - spin_unlock_irqrestore(&wdata->qlock, flags); -} - -static void wiimote_queue(struct wiimote_data *wdata, const __u8 *buffer, - size_t count) -{ - unsigned long flags; - __u8 newhead; - - if (count > HID_MAX_BUFFER_SIZE) { - hid_warn(wdata->hdev, "Sending too large output report\n"); - return; - } - - /* - * Copy new request into our output queue and check whether the - * queue is full. If it is full, discard this request. - * If it is empty we need to start a new worker that will - * send out the buffer to the hid device. - * If the queue is not empty, then there must be a worker - * that is currently sending out our buffer and this worker - * will reschedule itself until the queue is empty. - */ - - spin_lock_irqsave(&wdata->qlock, flags); - - memcpy(wdata->outq[wdata->head].data, buffer, count); - wdata->outq[wdata->head].size = count; - newhead = (wdata->head + 1) % WIIMOTE_BUFSIZE; - - if (wdata->head == wdata->tail) { - wdata->head = newhead; - schedule_work(&wdata->worker); - } else if (newhead != wdata->tail) { - wdata->head = newhead; - } else { - hid_warn(wdata->hdev, "Output queue is full"); - } - - spin_unlock_irqrestore(&wdata->qlock, flags); -} - -/* - * This sets the rumble bit on the given output report if rumble is - * currently enabled. - * \cmd1 must point to the second byte in the output report => &cmd[1] - * This must be called on nearly every output report before passing it - * into the output queue! - */ -static inline void wiiproto_keep_rumble(struct wiimote_data *wdata, __u8 *cmd1) -{ - if (wdata->state.flags & WIIPROTO_FLAG_RUMBLE) - *cmd1 |= 0x01; -} - -static void wiiproto_req_rumble(struct wiimote_data *wdata, __u8 rumble) -{ - __u8 cmd[2]; - - rumble = !!rumble; - if (rumble == !!(wdata->state.flags & WIIPROTO_FLAG_RUMBLE)) - return; - - if (rumble) - wdata->state.flags |= WIIPROTO_FLAG_RUMBLE; - else - wdata->state.flags &= ~WIIPROTO_FLAG_RUMBLE; - - cmd[0] = WIIPROTO_REQ_RUMBLE; - cmd[1] = 0; - - wiiproto_keep_rumble(wdata, &cmd[1]); - wiimote_queue(wdata, cmd, sizeof(cmd)); -} - -static void wiiproto_req_leds(struct wiimote_data *wdata, int leds) -{ - __u8 cmd[2]; - - leds &= WIIPROTO_FLAGS_LEDS; - if ((wdata->state.flags & WIIPROTO_FLAGS_LEDS) == leds) - return; - wdata->state.flags = (wdata->state.flags & ~WIIPROTO_FLAGS_LEDS) | leds; - - cmd[0] = WIIPROTO_REQ_LED; - cmd[1] = 0; - - if (leds & WIIPROTO_FLAG_LED1) - cmd[1] |= 0x10; - if (leds & WIIPROTO_FLAG_LED2) - cmd[1] |= 0x20; - if (leds & WIIPROTO_FLAG_LED3) - cmd[1] |= 0x40; - if (leds & WIIPROTO_FLAG_LED4) - cmd[1] |= 0x80; - - wiiproto_keep_rumble(wdata, &cmd[1]); - wiimote_queue(wdata, cmd, sizeof(cmd)); -} - -/* - * Check what peripherals of the wiimote are currently - * active and select a proper DRM that supports all of - * the requested data inputs. - */ -static __u8 select_drm(struct wiimote_data *wdata) -{ - __u8 ir = wdata->state.flags & WIIPROTO_FLAGS_IR; - - if (ir == WIIPROTO_FLAG_IR_BASIC) { - if (wdata->state.flags & WIIPROTO_FLAG_ACCEL) - return WIIPROTO_REQ_DRM_KAIE; - else - return WIIPROTO_REQ_DRM_KIE; - } else if (ir == WIIPROTO_FLAG_IR_EXT) { - return WIIPROTO_REQ_DRM_KAI; - } else if (ir == WIIPROTO_FLAG_IR_FULL) { - return WIIPROTO_REQ_DRM_SKAI1; - } else { - if (wdata->state.flags & WIIPROTO_FLAG_ACCEL) - return WIIPROTO_REQ_DRM_KA; - else - return WIIPROTO_REQ_DRM_K; - } -} - -static void wiiproto_req_drm(struct wiimote_data *wdata, __u8 drm) -{ - __u8 cmd[3]; - - if (drm == WIIPROTO_REQ_NULL) - drm = select_drm(wdata); - - cmd[0] = WIIPROTO_REQ_DRM; - cmd[1] = 0; - cmd[2] = drm; - - wiiproto_keep_rumble(wdata, &cmd[1]); - wiimote_queue(wdata, cmd, sizeof(cmd)); -} - -static void wiiproto_req_status(struct wiimote_data *wdata) -{ - __u8 cmd[2]; - - cmd[0] = WIIPROTO_REQ_SREQ; - cmd[1] = 0; - - wiiproto_keep_rumble(wdata, &cmd[1]); - wiimote_queue(wdata, cmd, sizeof(cmd)); -} - -static void wiiproto_req_accel(struct wiimote_data *wdata, __u8 accel) -{ - accel = !!accel; - if (accel == !!(wdata->state.flags & WIIPROTO_FLAG_ACCEL)) - return; - - if (accel) - wdata->state.flags |= WIIPROTO_FLAG_ACCEL; - else - wdata->state.flags &= ~WIIPROTO_FLAG_ACCEL; - - wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL); -} - -static void wiiproto_req_ir1(struct wiimote_data *wdata, __u8 flags) -{ - __u8 cmd[2]; - - cmd[0] = WIIPROTO_REQ_IR1; - cmd[1] = flags; - - wiiproto_keep_rumble(wdata, &cmd[1]); - wiimote_queue(wdata, cmd, sizeof(cmd)); -} - -static void wiiproto_req_ir2(struct wiimote_data *wdata, __u8 flags) -{ - __u8 cmd[2]; - - cmd[0] = WIIPROTO_REQ_IR2; - cmd[1] = flags; - - wiiproto_keep_rumble(wdata, &cmd[1]); - wiimote_queue(wdata, cmd, sizeof(cmd)); -} - -#define wiiproto_req_wreg(wdata, os, buf, sz) \ - wiiproto_req_wmem((wdata), false, (os), (buf), (sz)) - -#define wiiproto_req_weeprom(wdata, os, buf, sz) \ - wiiproto_req_wmem((wdata), true, (os), (buf), (sz)) - -static void wiiproto_req_wmem(struct wiimote_data *wdata, bool eeprom, - __u32 offset, const __u8 *buf, __u8 size) -{ - __u8 cmd[22]; - - if (size > 16 || size == 0) { - hid_warn(wdata->hdev, "Invalid length %d wmem request\n", size); - return; - } - - memset(cmd, 0, sizeof(cmd)); - cmd[0] = WIIPROTO_REQ_WMEM; - cmd[2] = (offset >> 16) & 0xff; - cmd[3] = (offset >> 8) & 0xff; - cmd[4] = offset & 0xff; - cmd[5] = size; - memcpy(&cmd[6], buf, size); - - if (!eeprom) - cmd[1] |= 0x04; - - wiiproto_keep_rumble(wdata, &cmd[1]); - wiimote_queue(wdata, cmd, sizeof(cmd)); -} - -/* requries the cmd-mutex to be held */ -static int wiimote_cmd_write(struct wiimote_data *wdata, __u32 offset, - const __u8 *wmem, __u8 size) -{ - unsigned long flags; - int ret; - - spin_lock_irqsave(&wdata->state.lock, flags); - wiimote_cmd_set(wdata, WIIPROTO_REQ_WMEM, 0); - wiiproto_req_wreg(wdata, offset, wmem, size); - spin_unlock_irqrestore(&wdata->state.lock, flags); - - ret = wiimote_cmd_wait(wdata); - if (!ret && wdata->state.cmd_err) - ret = -EIO; - - return ret; -} - -static int wiimote_battery_get_property(struct power_supply *psy, - enum power_supply_property psp, - union power_supply_propval *val) -{ - struct wiimote_data *wdata = container_of(psy, - struct wiimote_data, battery); - int ret = 0, state; - unsigned long flags; - - ret = wiimote_cmd_acquire(wdata); - if (ret) - return ret; - - spin_lock_irqsave(&wdata->state.lock, flags); - wiimote_cmd_set(wdata, WIIPROTO_REQ_SREQ, 0); - wiiproto_req_status(wdata); - spin_unlock_irqrestore(&wdata->state.lock, flags); - - ret = wiimote_cmd_wait(wdata); - state = wdata->state.cmd_battery; - wiimote_cmd_release(wdata); - - if (ret) - return ret; - - switch (psp) { - case POWER_SUPPLY_PROP_CAPACITY: - val->intval = state * 100 / 255; - break; - default: - ret = -EINVAL; - break; - } - - return ret; -} - -static int wiimote_init_ir(struct wiimote_data *wdata, __u16 mode) -{ - int ret; - unsigned long flags; - __u8 format = 0; - static const __u8 data_enable[] = { 0x01 }; - static const __u8 data_sens1[] = { 0x02, 0x00, 0x00, 0x71, 0x01, - 0x00, 0xaa, 0x00, 0x64 }; - static const __u8 data_sens2[] = { 0x63, 0x03 }; - static const __u8 data_fin[] = { 0x08 }; - - spin_lock_irqsave(&wdata->state.lock, flags); - - if (mode == (wdata->state.flags & WIIPROTO_FLAGS_IR)) { - spin_unlock_irqrestore(&wdata->state.lock, flags); - return 0; - } - - if (mode == 0) { - wdata->state.flags &= ~WIIPROTO_FLAGS_IR; - wiiproto_req_ir1(wdata, 0); - wiiproto_req_ir2(wdata, 0); - wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL); - spin_unlock_irqrestore(&wdata->state.lock, flags); - return 0; - } - - spin_unlock_irqrestore(&wdata->state.lock, flags); - - ret = wiimote_cmd_acquire(wdata); - if (ret) - return ret; - - /* send PIXEL CLOCK ENABLE cmd first */ - spin_lock_irqsave(&wdata->state.lock, flags); - wiimote_cmd_set(wdata, WIIPROTO_REQ_IR1, 0); - wiiproto_req_ir1(wdata, 0x06); - spin_unlock_irqrestore(&wdata->state.lock, flags); - - ret = wiimote_cmd_wait(wdata); - if (ret) - goto unlock; - if (wdata->state.cmd_err) { - ret = -EIO; - goto unlock; - } - - /* enable IR LOGIC */ - spin_lock_irqsave(&wdata->state.lock, flags); - wiimote_cmd_set(wdata, WIIPROTO_REQ_IR2, 0); - wiiproto_req_ir2(wdata, 0x06); - spin_unlock_irqrestore(&wdata->state.lock, flags); - - ret = wiimote_cmd_wait(wdata); - if (ret) - goto unlock; - if (wdata->state.cmd_err) { - ret = -EIO; - goto unlock; - } - - /* enable IR cam but do not make it send data, yet */ - ret = wiimote_cmd_write(wdata, 0xb00030, data_enable, - sizeof(data_enable)); - if (ret) - goto unlock; - - /* write first sensitivity block */ - ret = wiimote_cmd_write(wdata, 0xb00000, data_sens1, - sizeof(data_sens1)); - if (ret) - goto unlock; - - /* write second sensitivity block */ - ret = wiimote_cmd_write(wdata, 0xb0001a, data_sens2, - sizeof(data_sens2)); - if (ret) - goto unlock; - - /* put IR cam into desired state */ - switch (mode) { - case WIIPROTO_FLAG_IR_FULL: - format = 5; - break; - case WIIPROTO_FLAG_IR_EXT: - format = 3; - break; - case WIIPROTO_FLAG_IR_BASIC: - format = 1; - break; - } - ret = wiimote_cmd_write(wdata, 0xb00033, &format, sizeof(format)); - if (ret) - goto unlock; - - /* make IR cam send data */ - ret = wiimote_cmd_write(wdata, 0xb00030, data_fin, sizeof(data_fin)); - if (ret) - goto unlock; - - /* request new DRM mode compatible to IR mode */ - spin_lock_irqsave(&wdata->state.lock, flags); - wdata->state.flags &= ~WIIPROTO_FLAGS_IR; - wdata->state.flags |= mode & WIIPROTO_FLAGS_IR; - wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL); - spin_unlock_irqrestore(&wdata->state.lock, flags); - -unlock: - wiimote_cmd_release(wdata); - return ret; -} - -static enum led_brightness wiimote_leds_get(struct led_classdev *led_dev) -{ - struct wiimote_data *wdata; - struct device *dev = led_dev->dev->parent; - int i; - unsigned long flags; - bool value = false; - - wdata = hid_get_drvdata(container_of(dev, struct hid_device, dev)); - - for (i = 0; i < 4; ++i) { - if (wdata->leds[i] == led_dev) { - spin_lock_irqsave(&wdata->state.lock, flags); - value = wdata->state.flags & WIIPROTO_FLAG_LED(i + 1); - spin_unlock_irqrestore(&wdata->state.lock, flags); - break; - } - } - - return value ? LED_FULL : LED_OFF; -} - -static void wiimote_leds_set(struct led_classdev *led_dev, - enum led_brightness value) -{ - struct wiimote_data *wdata; - struct device *dev = led_dev->dev->parent; - int i; - unsigned long flags; - __u8 state, flag; - - wdata = hid_get_drvdata(container_of(dev, struct hid_device, dev)); - - for (i = 0; i < 4; ++i) { - if (wdata->leds[i] == led_dev) { - flag = WIIPROTO_FLAG_LED(i + 1); - spin_lock_irqsave(&wdata->state.lock, flags); - state = wdata->state.flags; - if (value == LED_OFF) - wiiproto_req_leds(wdata, state & ~flag); - else - wiiproto_req_leds(wdata, state | flag); - spin_unlock_irqrestore(&wdata->state.lock, flags); - break; - } - } -} - -static int wiimote_ff_play(struct input_dev *dev, void *data, - struct ff_effect *eff) -{ - struct wiimote_data *wdata = input_get_drvdata(dev); - __u8 value; - unsigned long flags; - - /* - * The wiimote supports only a single rumble motor so if any magnitude - * is set to non-zero then we start the rumble motor. If both are set to - * zero, we stop the rumble motor. - */ - - if (eff->u.rumble.strong_magnitude || eff->u.rumble.weak_magnitude) - value = 1; - else - value = 0; - - spin_lock_irqsave(&wdata->state.lock, flags); - wiiproto_req_rumble(wdata, value); - spin_unlock_irqrestore(&wdata->state.lock, flags); - - return 0; -} - -static int wiimote_input_open(struct input_dev *dev) -{ - struct wiimote_data *wdata = input_get_drvdata(dev); - - return hid_hw_open(wdata->hdev); -} - -static void wiimote_input_close(struct input_dev *dev) -{ - struct wiimote_data *wdata = input_get_drvdata(dev); - - hid_hw_close(wdata->hdev); -} - -static int wiimote_accel_open(struct input_dev *dev) -{ - struct wiimote_data *wdata = input_get_drvdata(dev); - int ret; - unsigned long flags; - - ret = hid_hw_open(wdata->hdev); - if (ret) - return ret; - - spin_lock_irqsave(&wdata->state.lock, flags); - wiiproto_req_accel(wdata, true); - spin_unlock_irqrestore(&wdata->state.lock, flags); - - return 0; -} - -static void wiimote_accel_close(struct input_dev *dev) -{ - struct wiimote_data *wdata = input_get_drvdata(dev); - unsigned long flags; - - spin_lock_irqsave(&wdata->state.lock, flags); - wiiproto_req_accel(wdata, false); - spin_unlock_irqrestore(&wdata->state.lock, flags); - - hid_hw_close(wdata->hdev); -} - -static int wiimote_ir_open(struct input_dev *dev) -{ - struct wiimote_data *wdata = input_get_drvdata(dev); - int ret; - - ret = hid_hw_open(wdata->hdev); - if (ret) - return ret; - - ret = wiimote_init_ir(wdata, WIIPROTO_FLAG_IR_BASIC); - if (ret) { - hid_hw_close(wdata->hdev); - return ret; - } - - return 0; -} - -static void wiimote_ir_close(struct input_dev *dev) -{ - struct wiimote_data *wdata = input_get_drvdata(dev); - - wiimote_init_ir(wdata, 0); - hid_hw_close(wdata->hdev); -} - -static void handler_keys(struct wiimote_data *wdata, const __u8 *payload) -{ - input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_LEFT], - !!(payload[0] & 0x01)); - input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_RIGHT], - !!(payload[0] & 0x02)); - input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_DOWN], - !!(payload[0] & 0x04)); - input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_UP], - !!(payload[0] & 0x08)); - input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_PLUS], - !!(payload[0] & 0x10)); - input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_TWO], - !!(payload[1] & 0x01)); - input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_ONE], - !!(payload[1] & 0x02)); - input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_B], - !!(payload[1] & 0x04)); - input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_A], - !!(payload[1] & 0x08)); - input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_MINUS], - !!(payload[1] & 0x10)); - input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_HOME], - !!(payload[1] & 0x80)); - input_sync(wdata->input); -} - -static void handler_accel(struct wiimote_data *wdata, const __u8 *payload) -{ - __u16 x, y, z; - - if (!(wdata->state.flags & WIIPROTO_FLAG_ACCEL)) - return; - - /* - * payload is: BB BB XX YY ZZ - * Accelerometer data is encoded into 3 10bit values. XX, YY and ZZ - * contain the upper 8 bits of each value. The lower 2 bits are - * contained in the buttons data BB BB. - * Bits 6 and 7 of the first buttons byte BB is the lower 2 bits of the - * X accel value. Bit 5 of the second buttons byte is the 2nd bit of Y - * accel value and bit 6 is the second bit of the Z value. - * The first bit of Y and Z values is not available and always set to 0. - * 0x200 is returned on no movement. - */ - - x = payload[2] << 2; - y = payload[3] << 2; - z = payload[4] << 2; - - x |= (payload[0] >> 5) & 0x3; - y |= (payload[1] >> 4) & 0x2; - z |= (payload[1] >> 5) & 0x2; - - input_report_abs(wdata->accel, ABS_RX, x - 0x200); - input_report_abs(wdata->accel, ABS_RY, y - 0x200); - input_report_abs(wdata->accel, ABS_RZ, z - 0x200); - input_sync(wdata->accel); -} - -#define ir_to_input0(wdata, ir, packed) __ir_to_input((wdata), (ir), (packed), \ - ABS_HAT0X, ABS_HAT0Y) -#define ir_to_input1(wdata, ir, packed) __ir_to_input((wdata), (ir), (packed), \ - ABS_HAT1X, ABS_HAT1Y) -#define ir_to_input2(wdata, ir, packed) __ir_to_input((wdata), (ir), (packed), \ - ABS_HAT2X, ABS_HAT2Y) -#define ir_to_input3(wdata, ir, packed) __ir_to_input((wdata), (ir), (packed), \ - ABS_HAT3X, ABS_HAT3Y) - -static void __ir_to_input(struct wiimote_data *wdata, const __u8 *ir, - bool packed, __u8 xid, __u8 yid) -{ - __u16 x, y; - - if (!(wdata->state.flags & WIIPROTO_FLAGS_IR)) - return; - - /* - * Basic IR data is encoded into 3 bytes. The first two bytes are the - * upper 8 bit of the X/Y data, the 3rd byte contains the lower 2 bits - * of both. - * If data is packed, then the 3rd byte is put first and slightly - * reordered. This allows to interleave packed and non-packed data to - * have two IR sets in 5 bytes instead of 6. - * The resulting 10bit X/Y values are passed to the ABS_HATXY input dev. - */ - - if (packed) { - x = ir[1] << 2; - y = ir[2] << 2; - - x |= ir[0] & 0x3; - y |= (ir[0] >> 2) & 0x3; - } else { - x = ir[0] << 2; - y = ir[1] << 2; - - x |= (ir[2] >> 4) & 0x3; - y |= (ir[2] >> 6) & 0x3; - } - - input_report_abs(wdata->ir, xid, x); - input_report_abs(wdata->ir, yid, y); -} - -static void handler_status(struct wiimote_data *wdata, const __u8 *payload) -{ - handler_keys(wdata, payload); - - /* on status reports the drm is reset so we need to resend the drm */ - wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL); - - if (wiimote_cmd_pending(wdata, WIIPROTO_REQ_SREQ, 0)) { - wdata->state.cmd_battery = payload[5]; - wiimote_cmd_complete(wdata); - } -} - -static void handler_data(struct wiimote_data *wdata, const __u8 *payload) -{ - handler_keys(wdata, payload); -} - -static void handler_return(struct wiimote_data *wdata, const __u8 *payload) -{ - __u8 err = payload[3]; - __u8 cmd = payload[2]; - - handler_keys(wdata, payload); - - if (wiimote_cmd_pending(wdata, cmd, 0)) { - wdata->state.cmd_err = err; - wiimote_cmd_complete(wdata); - } else if (err) { - hid_warn(wdata->hdev, "Remote error %hhu on req %hhu\n", err, - cmd); - } -} - -static void handler_drm_KA(struct wiimote_data *wdata, const __u8 *payload) -{ - handler_keys(wdata, payload); - handler_accel(wdata, payload); -} - -static void handler_drm_KE(struct wiimote_data *wdata, const __u8 *payload) -{ - handler_keys(wdata, payload); -} - -static void handler_drm_KAI(struct wiimote_data *wdata, const __u8 *payload) -{ - handler_keys(wdata, payload); - handler_accel(wdata, payload); - ir_to_input0(wdata, &payload[5], false); - ir_to_input1(wdata, &payload[8], false); - ir_to_input2(wdata, &payload[11], false); - ir_to_input3(wdata, &payload[14], false); - input_sync(wdata->ir); -} - -static void handler_drm_KEE(struct wiimote_data *wdata, const __u8 *payload) -{ - handler_keys(wdata, payload); -} - -static void handler_drm_KIE(struct wiimote_data *wdata, const __u8 *payload) -{ - handler_keys(wdata, payload); - ir_to_input0(wdata, &payload[2], false); - ir_to_input1(wdata, &payload[4], true); - ir_to_input2(wdata, &payload[7], false); - ir_to_input3(wdata, &payload[9], true); - input_sync(wdata->ir); -} - -static void handler_drm_KAE(struct wiimote_data *wdata, const __u8 *payload) -{ - handler_keys(wdata, payload); - handler_accel(wdata, payload); -} - -static void handler_drm_KAIE(struct wiimote_data *wdata, const __u8 *payload) -{ - handler_keys(wdata, payload); - handler_accel(wdata, payload); - ir_to_input0(wdata, &payload[5], false); - ir_to_input1(wdata, &payload[7], true); - ir_to_input2(wdata, &payload[10], false); - ir_to_input3(wdata, &payload[12], true); - input_sync(wdata->ir); -} - -static void handler_drm_E(struct wiimote_data *wdata, const __u8 *payload) -{ -} - -static void handler_drm_SKAI1(struct wiimote_data *wdata, const __u8 *payload) -{ - handler_keys(wdata, payload); - - wdata->state.accel_split[0] = payload[2]; - wdata->state.accel_split[1] = (payload[0] >> 1) & (0x10 | 0x20); - wdata->state.accel_split[1] |= (payload[1] << 1) & (0x40 | 0x80); - - ir_to_input0(wdata, &payload[3], false); - ir_to_input1(wdata, &payload[12], false); - input_sync(wdata->ir); -} - -static void handler_drm_SKAI2(struct wiimote_data *wdata, const __u8 *payload) -{ - __u8 buf[5]; - - handler_keys(wdata, payload); - - wdata->state.accel_split[1] |= (payload[0] >> 5) & (0x01 | 0x02); - wdata->state.accel_split[1] |= (payload[1] >> 3) & (0x04 | 0x08); - - buf[0] = 0; - buf[1] = 0; - buf[2] = wdata->state.accel_split[0]; - buf[3] = payload[2]; - buf[4] = wdata->state.accel_split[1]; - handler_accel(wdata, buf); - - ir_to_input2(wdata, &payload[3], false); - ir_to_input3(wdata, &payload[12], false); - input_sync(wdata->ir); -} - -struct wiiproto_handler { - __u8 id; - size_t size; - void (*func)(struct wiimote_data *wdata, const __u8 *payload); -}; - -static struct wiiproto_handler handlers[] = { - { .id = WIIPROTO_REQ_STATUS, .size = 6, .func = handler_status }, - { .id = WIIPROTO_REQ_DATA, .size = 21, .func = handler_data }, - { .id = WIIPROTO_REQ_RETURN, .size = 4, .func = handler_return }, - { .id = WIIPROTO_REQ_DRM_K, .size = 2, .func = handler_keys }, - { .id = WIIPROTO_REQ_DRM_KA, .size = 5, .func = handler_drm_KA }, - { .id = WIIPROTO_REQ_DRM_KE, .size = 10, .func = handler_drm_KE }, - { .id = WIIPROTO_REQ_DRM_KAI, .size = 17, .func = handler_drm_KAI }, - { .id = WIIPROTO_REQ_DRM_KEE, .size = 21, .func = handler_drm_KEE }, - { .id = WIIPROTO_REQ_DRM_KAE, .size = 21, .func = handler_drm_KAE }, - { .id = WIIPROTO_REQ_DRM_KIE, .size = 21, .func = handler_drm_KIE }, - { .id = WIIPROTO_REQ_DRM_KAIE, .size = 21, .func = handler_drm_KAIE }, - { .id = WIIPROTO_REQ_DRM_E, .size = 21, .func = handler_drm_E }, - { .id = WIIPROTO_REQ_DRM_SKAI1, .size = 21, .func = handler_drm_SKAI1 }, - { .id = WIIPROTO_REQ_DRM_SKAI2, .size = 21, .func = handler_drm_SKAI2 }, - { .id = 0 } -}; - -static int wiimote_hid_event(struct hid_device *hdev, struct hid_report *report, - u8 *raw_data, int size) -{ - struct wiimote_data *wdata = hid_get_drvdata(hdev); - struct wiiproto_handler *h; - int i; - unsigned long flags; - bool handled = false; - - if (size < 1) - return -EINVAL; - - spin_lock_irqsave(&wdata->state.lock, flags); - - for (i = 0; handlers[i].id; ++i) { - h = &handlers[i]; - if (h->id == raw_data[0] && h->size < size) { - h->func(wdata, &raw_data[1]); - handled = true; - } - } - - if (!handled) - hid_warn(hdev, "Unhandled report %hhu size %d\n", raw_data[0], - size); - - spin_unlock_irqrestore(&wdata->state.lock, flags); - - return 0; -} - -static void wiimote_leds_destroy(struct wiimote_data *wdata) -{ - int i; - struct led_classdev *led; - - for (i = 0; i < 4; ++i) { - if (wdata->leds[i]) { - led = wdata->leds[i]; - wdata->leds[i] = NULL; - led_classdev_unregister(led); - kfree(led); - } - } -} - -static int wiimote_leds_create(struct wiimote_data *wdata) -{ - int i, ret; - struct device *dev = &wdata->hdev->dev; - size_t namesz = strlen(dev_name(dev)) + 9; - struct led_classdev *led; - char *name; - - for (i = 0; i < 4; ++i) { - led = kzalloc(sizeof(struct led_classdev) + namesz, GFP_KERNEL); - if (!led) { - ret = -ENOMEM; - goto err; - } - name = (void*)&led[1]; - snprintf(name, namesz, "%s:blue:p%d", dev_name(dev), i); - led->name = name; - led->brightness = 0; - led->max_brightness = 1; - led->brightness_get = wiimote_leds_get; - led->brightness_set = wiimote_leds_set; - - ret = led_classdev_register(dev, led); - if (ret) { - kfree(led); - goto err; - } - wdata->leds[i] = led; - } - - return 0; - -err: - wiimote_leds_destroy(wdata); - return ret; -} - -static struct wiimote_data *wiimote_create(struct hid_device *hdev) -{ - struct wiimote_data *wdata; - int i; - - wdata = kzalloc(sizeof(*wdata), GFP_KERNEL); - if (!wdata) - return NULL; - - wdata->input = input_allocate_device(); - if (!wdata->input) - goto err; - - wdata->hdev = hdev; - hid_set_drvdata(hdev, wdata); - - input_set_drvdata(wdata->input, wdata); - wdata->input->open = wiimote_input_open; - wdata->input->close = wiimote_input_close; - wdata->input->dev.parent = &wdata->hdev->dev; - wdata->input->id.bustype = wdata->hdev->bus; - wdata->input->id.vendor = wdata->hdev->vendor; - wdata->input->id.product = wdata->hdev->product; - wdata->input->id.version = wdata->hdev->version; - wdata->input->name = WIIMOTE_NAME; - - set_bit(EV_KEY, wdata->input->evbit); - for (i = 0; i < WIIPROTO_KEY_COUNT; ++i) - set_bit(wiiproto_keymap[i], wdata->input->keybit); - - set_bit(FF_RUMBLE, wdata->input->ffbit); - if (input_ff_create_memless(wdata->input, NULL, wiimote_ff_play)) - goto err_input; - - wdata->accel = input_allocate_device(); - if (!wdata->accel) - goto err_input; - - input_set_drvdata(wdata->accel, wdata); - wdata->accel->open = wiimote_accel_open; - wdata->accel->close = wiimote_accel_close; - wdata->accel->dev.parent = &wdata->hdev->dev; - wdata->accel->id.bustype = wdata->hdev->bus; - wdata->accel->id.vendor = wdata->hdev->vendor; - wdata->accel->id.product = wdata->hdev->product; - wdata->accel->id.version = wdata->hdev->version; - wdata->accel->name = WIIMOTE_NAME " Accelerometer"; - - set_bit(EV_ABS, wdata->accel->evbit); - set_bit(ABS_RX, wdata->accel->absbit); - set_bit(ABS_RY, wdata->accel->absbit); - set_bit(ABS_RZ, wdata->accel->absbit); - input_set_abs_params(wdata->accel, ABS_RX, -500, 500, 2, 4); - input_set_abs_params(wdata->accel, ABS_RY, -500, 500, 2, 4); - input_set_abs_params(wdata->accel, ABS_RZ, -500, 500, 2, 4); - - wdata->ir = input_allocate_device(); - if (!wdata->ir) - goto err_ir; - - input_set_drvdata(wdata->ir, wdata); - wdata->ir->open = wiimote_ir_open; - wdata->ir->close = wiimote_ir_close; - wdata->ir->dev.parent = &wdata->hdev->dev; - wdata->ir->id.bustype = wdata->hdev->bus; - wdata->ir->id.vendor = wdata->hdev->vendor; - wdata->ir->id.product = wdata->hdev->product; - wdata->ir->id.version = wdata->hdev->version; - wdata->ir->name = WIIMOTE_NAME " IR"; - - set_bit(EV_ABS, wdata->ir->evbit); - set_bit(ABS_HAT0X, wdata->ir->absbit); - set_bit(ABS_HAT0Y, wdata->ir->absbit); - set_bit(ABS_HAT1X, wdata->ir->absbit); - set_bit(ABS_HAT1Y, wdata->ir->absbit); - set_bit(ABS_HAT2X, wdata->ir->absbit); - set_bit(ABS_HAT2Y, wdata->ir->absbit); - set_bit(ABS_HAT3X, wdata->ir->absbit); - set_bit(ABS_HAT3Y, wdata->ir->absbit); - input_set_abs_params(wdata->ir, ABS_HAT0X, 0, 1023, 2, 4); - input_set_abs_params(wdata->ir, ABS_HAT0Y, 0, 767, 2, 4); - input_set_abs_params(wdata->ir, ABS_HAT1X, 0, 1023, 2, 4); - input_set_abs_params(wdata->ir, ABS_HAT1Y, 0, 767, 2, 4); - input_set_abs_params(wdata->ir, ABS_HAT2X, 0, 1023, 2, 4); - input_set_abs_params(wdata->ir, ABS_HAT2Y, 0, 767, 2, 4); - input_set_abs_params(wdata->ir, ABS_HAT3X, 0, 1023, 2, 4); - input_set_abs_params(wdata->ir, ABS_HAT3Y, 0, 767, 2, 4); - - spin_lock_init(&wdata->qlock); - INIT_WORK(&wdata->worker, wiimote_worker); - - spin_lock_init(&wdata->state.lock); - init_completion(&wdata->state.ready); - mutex_init(&wdata->state.sync); - - return wdata; - -err_ir: - input_free_device(wdata->accel); -err_input: - input_free_device(wdata->input); -err: - kfree(wdata); - return NULL; -} - -static void wiimote_destroy(struct wiimote_data *wdata) -{ - wiimote_leds_destroy(wdata); - - power_supply_unregister(&wdata->battery); - input_unregister_device(wdata->accel); - input_unregister_device(wdata->ir); - input_unregister_device(wdata->input); - cancel_work_sync(&wdata->worker); - hid_hw_stop(wdata->hdev); - - kfree(wdata); -} - -static int wiimote_hid_probe(struct hid_device *hdev, - const struct hid_device_id *id) -{ - struct wiimote_data *wdata; - int ret; - - wdata = wiimote_create(hdev); - if (!wdata) { - hid_err(hdev, "Can't alloc device\n"); - return -ENOMEM; - } - - ret = hid_parse(hdev); - if (ret) { - hid_err(hdev, "HID parse failed\n"); - goto err; - } - - ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW); - if (ret) { - hid_err(hdev, "HW start failed\n"); - goto err; - } - - ret = input_register_device(wdata->accel); - if (ret) { - hid_err(hdev, "Cannot register input device\n"); - goto err_stop; - } - - ret = input_register_device(wdata->ir); - if (ret) { - hid_err(hdev, "Cannot register input device\n"); - goto err_ir; - } - - ret = input_register_device(wdata->input); - if (ret) { - hid_err(hdev, "Cannot register input device\n"); - goto err_input; - } - - wdata->battery.properties = wiimote_battery_props; - wdata->battery.num_properties = ARRAY_SIZE(wiimote_battery_props); - wdata->battery.get_property = wiimote_battery_get_property; - wdata->battery.name = "wiimote_battery"; - wdata->battery.type = POWER_SUPPLY_TYPE_BATTERY; - wdata->battery.use_for_apm = 0; - - ret = power_supply_register(&wdata->hdev->dev, &wdata->battery); - if (ret) { - hid_err(hdev, "Cannot register battery device\n"); - goto err_battery; - } - - ret = wiimote_leds_create(wdata); - if (ret) - goto err_free; - - hid_info(hdev, "New device registered\n"); - - /* by default set led1 after device initialization */ - spin_lock_irq(&wdata->state.lock); - wiiproto_req_leds(wdata, WIIPROTO_FLAG_LED1); - spin_unlock_irq(&wdata->state.lock); - - return 0; - -err_free: - wiimote_destroy(wdata); - return ret; - -err_battery: - input_unregister_device(wdata->input); - wdata->input = NULL; -err_input: - input_unregister_device(wdata->ir); - wdata->ir = NULL; -err_ir: - input_unregister_device(wdata->accel); - wdata->accel = NULL; -err_stop: - hid_hw_stop(hdev); -err: - input_free_device(wdata->ir); - input_free_device(wdata->accel); - input_free_device(wdata->input); - kfree(wdata); - return ret; -} - -static void wiimote_hid_remove(struct hid_device *hdev) -{ - struct wiimote_data *wdata = hid_get_drvdata(hdev); - - hid_info(hdev, "Device removed\n"); - wiimote_destroy(wdata); -} - -static const struct hid_device_id wiimote_hid_devices[] = { - { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, - USB_DEVICE_ID_NINTENDO_WIIMOTE) }, - { } -}; -MODULE_DEVICE_TABLE(hid, wiimote_hid_devices); - -static struct hid_driver wiimote_hid_driver = { - .name = "wiimote", - .id_table = wiimote_hid_devices, - .probe = wiimote_hid_probe, - .remove = wiimote_hid_remove, - .raw_event = wiimote_hid_event, -}; - -static int __init wiimote_init(void) -{ - int ret; - - ret = hid_register_driver(&wiimote_hid_driver); - if (ret) - pr_err("Can't register wiimote hid driver\n"); - - return ret; -} - -static void __exit wiimote_exit(void) -{ - hid_unregister_driver(&wiimote_hid_driver); -} - -module_init(wiimote_init); -module_exit(wiimote_exit); -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("David Herrmann "); -MODULE_DESCRIPTION(WIIMOTE_NAME " Device Driver"); -MODULE_VERSION(WIIMOTE_VERSION); -- cgit v0.10.2 From 7e274400629fbf8eab294fef39c3efde463966a9 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Thu, 17 Nov 2011 14:11:59 +0100 Subject: HID: wiimote: Move common symbols into header Wiimote extension and sound support need access to several symbols so move them into a new header. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c index 76739c0..c955d50 100644 --- a/drivers/hid/hid-wiimote-core.c +++ b/drivers/hid/hid-wiimote-core.c @@ -20,91 +20,9 @@ #include #include #include "hid-ids.h" +#include "hid-wiimote.h" #define WIIMOTE_VERSION "0.2" -#define WIIMOTE_NAME "Nintendo Wii Remote" -#define WIIMOTE_BUFSIZE 32 - -struct wiimote_buf { - __u8 data[HID_MAX_BUFFER_SIZE]; - size_t size; -}; - -struct wiimote_state { - spinlock_t lock; - __u8 flags; - __u8 accel_split[2]; - - /* synchronous cmd requests */ - struct mutex sync; - struct completion ready; - int cmd; - __u32 opt; - - /* results of synchronous requests */ - __u8 cmd_battery; - __u8 cmd_err; -}; - -struct wiimote_data { - struct hid_device *hdev; - struct input_dev *input; - struct led_classdev *leds[4]; - struct input_dev *accel; - struct input_dev *ir; - struct power_supply battery; - - spinlock_t qlock; - __u8 head; - __u8 tail; - struct wiimote_buf outq[WIIMOTE_BUFSIZE]; - struct work_struct worker; - - struct wiimote_state state; -}; - -#define WIIPROTO_FLAG_LED1 0x01 -#define WIIPROTO_FLAG_LED2 0x02 -#define WIIPROTO_FLAG_LED3 0x04 -#define WIIPROTO_FLAG_LED4 0x08 -#define WIIPROTO_FLAG_RUMBLE 0x10 -#define WIIPROTO_FLAG_ACCEL 0x20 -#define WIIPROTO_FLAG_IR_BASIC 0x40 -#define WIIPROTO_FLAG_IR_EXT 0x80 -#define WIIPROTO_FLAG_IR_FULL 0xc0 /* IR_BASIC | IR_EXT */ -#define WIIPROTO_FLAGS_LEDS (WIIPROTO_FLAG_LED1 | WIIPROTO_FLAG_LED2 | \ - WIIPROTO_FLAG_LED3 | WIIPROTO_FLAG_LED4) -#define WIIPROTO_FLAGS_IR (WIIPROTO_FLAG_IR_BASIC | WIIPROTO_FLAG_IR_EXT | \ - WIIPROTO_FLAG_IR_FULL) - -/* return flag for led \num */ -#define WIIPROTO_FLAG_LED(num) (WIIPROTO_FLAG_LED1 << (num - 1)) - -enum wiiproto_reqs { - WIIPROTO_REQ_NULL = 0x0, - WIIPROTO_REQ_RUMBLE = 0x10, - WIIPROTO_REQ_LED = 0x11, - WIIPROTO_REQ_DRM = 0x12, - WIIPROTO_REQ_IR1 = 0x13, - WIIPROTO_REQ_SREQ = 0x15, - WIIPROTO_REQ_WMEM = 0x16, - WIIPROTO_REQ_RMEM = 0x17, - WIIPROTO_REQ_IR2 = 0x1a, - WIIPROTO_REQ_STATUS = 0x20, - WIIPROTO_REQ_DATA = 0x21, - WIIPROTO_REQ_RETURN = 0x22, - WIIPROTO_REQ_DRM_K = 0x30, - WIIPROTO_REQ_DRM_KA = 0x31, - WIIPROTO_REQ_DRM_KE = 0x32, - WIIPROTO_REQ_DRM_KAI = 0x33, - WIIPROTO_REQ_DRM_KEE = 0x34, - WIIPROTO_REQ_DRM_KAE = 0x35, - WIIPROTO_REQ_DRM_KIE = 0x36, - WIIPROTO_REQ_DRM_KAIE = 0x37, - WIIPROTO_REQ_DRM_E = 0x3d, - WIIPROTO_REQ_DRM_SKAI1 = 0x3e, - WIIPROTO_REQ_DRM_SKAI2 = 0x3f, -}; enum wiiproto_keys { WIIPROTO_KEY_LEFT, @@ -139,52 +57,6 @@ static enum power_supply_property wiimote_battery_props[] = { POWER_SUPPLY_PROP_CAPACITY }; -/* requires the state.lock spinlock to be held */ -static inline bool wiimote_cmd_pending(struct wiimote_data *wdata, int cmd, - __u32 opt) -{ - return wdata->state.cmd == cmd && wdata->state.opt == opt; -} - -/* requires the state.lock spinlock to be held */ -static inline void wiimote_cmd_complete(struct wiimote_data *wdata) -{ - wdata->state.cmd = WIIPROTO_REQ_NULL; - complete(&wdata->state.ready); -} - -static inline int wiimote_cmd_acquire(struct wiimote_data *wdata) -{ - return mutex_lock_interruptible(&wdata->state.sync) ? -ERESTARTSYS : 0; -} - -/* requires the state.lock spinlock to be held */ -static inline void wiimote_cmd_set(struct wiimote_data *wdata, int cmd, - __u32 opt) -{ - INIT_COMPLETION(wdata->state.ready); - wdata->state.cmd = cmd; - wdata->state.opt = opt; -} - -static inline void wiimote_cmd_release(struct wiimote_data *wdata) -{ - mutex_unlock(&wdata->state.sync); -} - -static inline int wiimote_cmd_wait(struct wiimote_data *wdata) -{ - int ret; - - ret = wait_for_completion_interruptible_timeout(&wdata->state.ready, HZ); - if (ret < 0) - return -ERESTARTSYS; - else if (ret == 0) - return -EIO; - else - return 0; -} - static ssize_t wiimote_hid_send(struct hid_device *hdev, __u8 *buffer, size_t count) { @@ -347,7 +219,7 @@ static __u8 select_drm(struct wiimote_data *wdata) } } -static void wiiproto_req_drm(struct wiimote_data *wdata, __u8 drm) +void wiiproto_req_drm(struct wiimote_data *wdata, __u8 drm) { __u8 cmd[3]; @@ -441,7 +313,7 @@ static void wiiproto_req_wmem(struct wiimote_data *wdata, bool eeprom, } /* requries the cmd-mutex to be held */ -static int wiimote_cmd_write(struct wiimote_data *wdata, __u32 offset, +int wiimote_cmd_write(struct wiimote_data *wdata, __u32 offset, const __u8 *wmem, __u8 size) { unsigned long flags; diff --git a/drivers/hid/hid-wiimote.h b/drivers/hid/hid-wiimote.h new file mode 100644 index 0000000..d78f79c --- /dev/null +++ b/drivers/hid/hid-wiimote.h @@ -0,0 +1,163 @@ +#ifndef __HID_WIIMOTE_H +#define __HID_WIIMOTE_H + +/* + * HID driver for Nintendo Wiimote devices + * Copyright (c) 2011 David Herrmann + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define WIIMOTE_NAME "Nintendo Wii Remote" +#define WIIMOTE_BUFSIZE 32 + +#define WIIPROTO_FLAG_LED1 0x01 +#define WIIPROTO_FLAG_LED2 0x02 +#define WIIPROTO_FLAG_LED3 0x04 +#define WIIPROTO_FLAG_LED4 0x08 +#define WIIPROTO_FLAG_RUMBLE 0x10 +#define WIIPROTO_FLAG_ACCEL 0x20 +#define WIIPROTO_FLAG_IR_BASIC 0x40 +#define WIIPROTO_FLAG_IR_EXT 0x80 +#define WIIPROTO_FLAG_IR_FULL 0xc0 /* IR_BASIC | IR_EXT */ +#define WIIPROTO_FLAGS_LEDS (WIIPROTO_FLAG_LED1 | WIIPROTO_FLAG_LED2 | \ + WIIPROTO_FLAG_LED3 | WIIPROTO_FLAG_LED4) +#define WIIPROTO_FLAGS_IR (WIIPROTO_FLAG_IR_BASIC | WIIPROTO_FLAG_IR_EXT | \ + WIIPROTO_FLAG_IR_FULL) + +/* return flag for led \num */ +#define WIIPROTO_FLAG_LED(num) (WIIPROTO_FLAG_LED1 << (num - 1)) + +struct wiimote_buf { + __u8 data[HID_MAX_BUFFER_SIZE]; + size_t size; +}; + +struct wiimote_state { + spinlock_t lock; + __u8 flags; + __u8 accel_split[2]; + + /* synchronous cmd requests */ + struct mutex sync; + struct completion ready; + int cmd; + __u32 opt; + + /* results of synchronous requests */ + __u8 cmd_battery; + __u8 cmd_err; +}; + +struct wiimote_data { + struct hid_device *hdev; + struct input_dev *input; + struct led_classdev *leds[4]; + struct input_dev *accel; + struct input_dev *ir; + struct power_supply battery; + + spinlock_t qlock; + __u8 head; + __u8 tail; + struct wiimote_buf outq[WIIMOTE_BUFSIZE]; + struct work_struct worker; + + struct wiimote_state state; +}; + +enum wiiproto_reqs { + WIIPROTO_REQ_NULL = 0x0, + WIIPROTO_REQ_RUMBLE = 0x10, + WIIPROTO_REQ_LED = 0x11, + WIIPROTO_REQ_DRM = 0x12, + WIIPROTO_REQ_IR1 = 0x13, + WIIPROTO_REQ_SREQ = 0x15, + WIIPROTO_REQ_WMEM = 0x16, + WIIPROTO_REQ_RMEM = 0x17, + WIIPROTO_REQ_IR2 = 0x1a, + WIIPROTO_REQ_STATUS = 0x20, + WIIPROTO_REQ_DATA = 0x21, + WIIPROTO_REQ_RETURN = 0x22, + WIIPROTO_REQ_DRM_K = 0x30, + WIIPROTO_REQ_DRM_KA = 0x31, + WIIPROTO_REQ_DRM_KE = 0x32, + WIIPROTO_REQ_DRM_KAI = 0x33, + WIIPROTO_REQ_DRM_KEE = 0x34, + WIIPROTO_REQ_DRM_KAE = 0x35, + WIIPROTO_REQ_DRM_KIE = 0x36, + WIIPROTO_REQ_DRM_KAIE = 0x37, + WIIPROTO_REQ_DRM_E = 0x3d, + WIIPROTO_REQ_DRM_SKAI1 = 0x3e, + WIIPROTO_REQ_DRM_SKAI2 = 0x3f, +}; + +#define dev_to_wii(pdev) hid_get_drvdata(container_of(pdev, struct hid_device, \ + dev)) + +extern void wiiproto_req_drm(struct wiimote_data *wdata, __u8 drm); +extern int wiimote_cmd_write(struct wiimote_data *wdata, __u32 offset, + const __u8 *wmem, __u8 size); + +/* requires the state.lock spinlock to be held */ +static inline bool wiimote_cmd_pending(struct wiimote_data *wdata, int cmd, + __u32 opt) +{ + return wdata->state.cmd == cmd && wdata->state.opt == opt; +} + +/* requires the state.lock spinlock to be held */ +static inline void wiimote_cmd_complete(struct wiimote_data *wdata) +{ + wdata->state.cmd = WIIPROTO_REQ_NULL; + complete(&wdata->state.ready); +} + +static inline int wiimote_cmd_acquire(struct wiimote_data *wdata) +{ + return mutex_lock_interruptible(&wdata->state.sync) ? -ERESTARTSYS : 0; +} + +/* requires the state.lock spinlock to be held */ +static inline void wiimote_cmd_set(struct wiimote_data *wdata, int cmd, + __u32 opt) +{ + INIT_COMPLETION(wdata->state.ready); + wdata->state.cmd = cmd; + wdata->state.opt = opt; +} + +static inline void wiimote_cmd_release(struct wiimote_data *wdata) +{ + mutex_unlock(&wdata->state.sync); +} + +static inline int wiimote_cmd_wait(struct wiimote_data *wdata) +{ + int ret; + + ret = wait_for_completion_interruptible_timeout(&wdata->state.ready, HZ); + if (ret < 0) + return -ERESTARTSYS; + else if (ret == 0) + return -EIO; + else + return 0; +} + +#endif -- cgit v0.10.2 From fad8c0e34323eb7789f93750258a2cf02dc6cf69 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Thu, 17 Nov 2011 14:12:00 +0100 Subject: HID: wiimote: Add read-mem helpers Add helper functions similar to the write-mem helpers but for reading wiimote memory and eeprom. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c index c955d50..f7f8b7f 100644 --- a/drivers/hid/hid-wiimote-core.c +++ b/drivers/hid/hid-wiimote-core.c @@ -312,6 +312,37 @@ static void wiiproto_req_wmem(struct wiimote_data *wdata, bool eeprom, wiimote_queue(wdata, cmd, sizeof(cmd)); } +#define wiiproto_req_rreg(wdata, os, sz) \ + wiiproto_req_rmem((wdata), false, (os), (sz)) + +#define wiiproto_req_reeprom(wdata, os, sz) \ + wiiproto_req_rmem((wdata), true, (os), (sz)) + +static void wiiproto_req_rmem(struct wiimote_data *wdata, bool eeprom, + __u32 offset, __u16 size) +{ + __u8 cmd[7]; + + if (size == 0) { + hid_warn(wdata->hdev, "Invalid length %d rmem request\n", size); + return; + } + + cmd[0] = WIIPROTO_REQ_RMEM; + cmd[1] = 0; + cmd[2] = (offset >> 16) & 0xff; + cmd[3] = (offset >> 8) & 0xff; + cmd[4] = offset & 0xff; + cmd[5] = (size >> 8) & 0xff; + cmd[6] = size & 0xff; + + if (!eeprom) + cmd[1] |= 0x04; + + wiiproto_keep_rumble(wdata, &cmd[1]); + wiimote_queue(wdata, cmd, sizeof(cmd)); +} + /* requries the cmd-mutex to be held */ int wiimote_cmd_write(struct wiimote_data *wdata, __u32 offset, const __u8 *wmem, __u8 size) @@ -331,6 +362,36 @@ int wiimote_cmd_write(struct wiimote_data *wdata, __u32 offset, return ret; } +/* requries the cmd-mutex to be held */ +ssize_t wiimote_cmd_read(struct wiimote_data *wdata, __u32 offset, __u8 *rmem, + __u8 size) +{ + unsigned long flags; + ssize_t ret; + + spin_lock_irqsave(&wdata->state.lock, flags); + wdata->state.cmd_read_size = size; + wdata->state.cmd_read_buf = rmem; + wiimote_cmd_set(wdata, WIIPROTO_REQ_RMEM, offset & 0xffff); + wiiproto_req_rreg(wdata, offset, size); + spin_unlock_irqrestore(&wdata->state.lock, flags); + + ret = wiimote_cmd_wait(wdata); + + spin_lock_irqsave(&wdata->state.lock, flags); + wdata->state.cmd_read_buf = NULL; + spin_unlock_irqrestore(&wdata->state.lock, flags); + + if (!ret) { + if (wdata->state.cmd_read_size == 0) + ret = -EIO; + else + ret = wdata->state.cmd_read_size; + } + + return ret; +} + static int wiimote_battery_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) @@ -742,7 +803,23 @@ static void handler_status(struct wiimote_data *wdata, const __u8 *payload) static void handler_data(struct wiimote_data *wdata, const __u8 *payload) { + __u16 offset = payload[3] << 8 | payload[4]; + __u8 size = (payload[2] >> 4) + 1; + __u8 err = payload[2] & 0x0f; + handler_keys(wdata, payload); + + if (wiimote_cmd_pending(wdata, WIIPROTO_REQ_RMEM, offset)) { + if (err) + size = 0; + else if (size > wdata->state.cmd_read_size) + size = wdata->state.cmd_read_size; + + wdata->state.cmd_read_size = size; + if (wdata->state.cmd_read_buf) + memcpy(wdata->state.cmd_read_buf, &payload[5], size); + wiimote_cmd_complete(wdata); + } } static void handler_return(struct wiimote_data *wdata, const __u8 *payload) diff --git a/drivers/hid/hid-wiimote.h b/drivers/hid/hid-wiimote.h index d78f79c..865740d 100644 --- a/drivers/hid/hid-wiimote.h +++ b/drivers/hid/hid-wiimote.h @@ -62,6 +62,8 @@ struct wiimote_state { /* results of synchronous requests */ __u8 cmd_battery; __u8 cmd_err; + __u8 *cmd_read_buf; + __u8 cmd_read_size; }; struct wiimote_data { @@ -113,6 +115,8 @@ enum wiiproto_reqs { extern void wiiproto_req_drm(struct wiimote_data *wdata, __u8 drm); extern int wiimote_cmd_write(struct wiimote_data *wdata, __u32 offset, const __u8 *wmem, __u8 size); +extern ssize_t wiimote_cmd_read(struct wiimote_data *wdata, __u32 offset, + __u8 *rmem, __u8 size); /* requires the state.lock spinlock to be held */ static inline bool wiimote_cmd_pending(struct wiimote_data *wdata, int cmd, -- cgit v0.10.2 From cb99221ba74bb16576a9c3b7e49357b6b12ff3ea Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Thu, 17 Nov 2011 14:12:01 +0100 Subject: HID: wiimote: Add extension support stub The wiimote supports several extensions. This adds a separate source file which handles all extensions and can be disabled at compile-time. The driver reacts on "plug"-events on the extension port and starts a worker which initializes or deinitializes the extensions. Currently, the initialization logic is not fully understood and we can only detect and enable all extensions when all extensions are deactivated. Therefore, we need to disable all extensions, then detect and activate them again to react on "plug"-events. However, deactivating extensions will generate a new "plug"-event and we will never leave that loop. Hence, we only support extensions if they are plugged before the wiimote is connected (or before the ext-input device is opened). In the future we may support full extension hotplug support, but reverse-engineering this may take a while. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 22a4a05..7a0c6f9 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -623,6 +623,15 @@ config HID_WIIMOTE ---help--- Support for the Nintendo Wii Remote bluetooth device. +config HID_WIIMOTE_EXT + bool "Nintendo Wii Remote Extension support" + depends on HID_WIIMOTE + default HID_WIIMOTE + ---help--- + Support for extension controllers of the Nintendo Wii Remote. Say yes + here if you want to use the Nintendo Motion+, Nunchuck or Classic + extension controllers with your Wii Remote. + config HID_ZEROPLUS tristate "Zeroplus based game controller support" depends on USB_HID diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 96d33ad..5fab4e63 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -26,6 +26,9 @@ ifdef CONFIG_LOGIWHEELS_FF endif hid-wiimote-y := hid-wiimote-core.o +ifdef CONFIG_HID_WIIMOTE_EXT + hid-wiimote-y += hid-wiimote-ext.o +endif obj-$(CONFIG_HID_A4TECH) += hid-a4tech.o obj-$(CONFIG_HID_ACRUX) += hid-axff.o diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c index f7f8b7f..2ca8bfd 100644 --- a/drivers/hid/hid-wiimote-core.c +++ b/drivers/hid/hid-wiimote-core.c @@ -201,6 +201,7 @@ static void wiiproto_req_leds(struct wiimote_data *wdata, int leds) static __u8 select_drm(struct wiimote_data *wdata) { __u8 ir = wdata->state.flags & WIIPROTO_FLAGS_IR; + bool ext = wiiext_active(wdata); if (ir == WIIPROTO_FLAG_IR_BASIC) { if (wdata->state.flags & WIIPROTO_FLAG_ACCEL) @@ -212,10 +213,17 @@ static __u8 select_drm(struct wiimote_data *wdata) } else if (ir == WIIPROTO_FLAG_IR_FULL) { return WIIPROTO_REQ_DRM_SKAI1; } else { - if (wdata->state.flags & WIIPROTO_FLAG_ACCEL) - return WIIPROTO_REQ_DRM_KA; - else - return WIIPROTO_REQ_DRM_K; + if (wdata->state.flags & WIIPROTO_FLAG_ACCEL) { + if (ext) + return WIIPROTO_REQ_DRM_KAE; + else + return WIIPROTO_REQ_DRM_KA; + } else { + if (ext) + return WIIPROTO_REQ_DRM_KE; + else + return WIIPROTO_REQ_DRM_K; + } } } @@ -795,6 +803,8 @@ static void handler_status(struct wiimote_data *wdata, const __u8 *payload) /* on status reports the drm is reset so we need to resend the drm */ wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL); + wiiext_event(wdata, payload[2] & 0x02); + if (wiimote_cmd_pending(wdata, WIIPROTO_REQ_SREQ, 0)) { wdata->state.cmd_battery = payload[5]; wiimote_cmd_complete(wdata); @@ -1145,6 +1155,7 @@ err: static void wiimote_destroy(struct wiimote_data *wdata) { + wiiext_deinit(wdata); wiimote_leds_destroy(wdata); power_supply_unregister(&wdata->battery); @@ -1216,6 +1227,10 @@ static int wiimote_hid_probe(struct hid_device *hdev, if (ret) goto err_free; + ret = wiiext_init(wdata); + if (ret) + goto err_free; + hid_info(hdev, "New device registered\n"); /* by default set led1 after device initialization */ diff --git a/drivers/hid/hid-wiimote-ext.c b/drivers/hid/hid-wiimote-ext.c new file mode 100644 index 0000000..fa9c677 --- /dev/null +++ b/drivers/hid/hid-wiimote-ext.c @@ -0,0 +1,130 @@ +/* + * HID driver for Nintendo Wiimote extension devices + * Copyright (c) 2011 David Herrmann + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include +#include +#include +#include +#include "hid-wiimote.h" + +struct wiimote_ext { + struct wiimote_data *wdata; + struct work_struct worker; + + atomic_t opened; + atomic_t mp_opened; + bool plugged; + bool motionp; + __u8 ext_type; +}; + +enum wiiext_type { + WIIEXT_NONE, /* placeholder */ + WIIEXT_CLASSIC, /* Nintendo classic controller */ + WIIEXT_NUNCHUCK, /* Nintendo nunchuck controller */ +}; + +static void wiiext_worker(struct work_struct *work) +{ + struct wiimote_ext *ext = container_of(work, struct wiimote_ext, + worker); +} + +/* schedule work only once, otherwise mark for reschedule */ +static void wiiext_schedule(struct wiimote_ext *ext) +{ + queue_work(system_nrt_wq, &ext->worker); +} + +/* + * Reacts on extension port events + * Whenever the driver gets an event from the wiimote that an extension has been + * plugged or unplugged, this funtion shall be called. It checks what extensions + * are connected and initializes and activates them. + * This can be called in atomic context. The initialization is done in a + * separate worker thread. The state.lock spinlock must be held by the caller. + */ +void wiiext_event(struct wiimote_data *wdata, bool plugged) +{ + if (!wdata->ext) + return; + + if (wdata->ext->plugged == plugged) + return; + + wdata->ext->plugged = plugged; + /* + * We need to call wiiext_schedule(wdata->ext) here, however, the + * extension initialization logic is not fully understood and so + * automatic initialization is not supported, yet. + */ +} + +/* + * Returns true if the current DRM mode should contain extension data and false + * if there is no interest in extension data. + * All supported extensions send 6 byte extension data so any DRM that contains + * extension bytes is fine. + * The caller must hold the state.lock spinlock. + */ +bool wiiext_active(struct wiimote_data *wdata) +{ + if (!wdata->ext) + return false; + + return wdata->ext->motionp || wdata->ext->ext_type; +} + +/* Initializes the extension driver of a wiimote */ +int wiiext_init(struct wiimote_data *wdata) +{ + struct wiimote_ext *ext; + unsigned long flags; + + ext = kzalloc(sizeof(*ext), GFP_KERNEL); + if (!ext) + return -ENOMEM; + + ext->wdata = wdata; + INIT_WORK(&ext->worker, wiiext_worker); + + spin_lock_irqsave(&wdata->state.lock, flags); + wdata->ext = ext; + spin_unlock_irqrestore(&wdata->state.lock, flags); + + return 0; +} + +/* Deinitializes the extension driver of a wiimote */ +void wiiext_deinit(struct wiimote_data *wdata) +{ + struct wiimote_ext *ext = wdata->ext; + unsigned long flags; + + if (!ext) + return; + + /* + * We first unset wdata->ext to avoid further input from the wiimote + * core. The worker thread does not access this pointer so it is not + * affected by this. + * We kill the worker after this so it does not get respawned during + * deinitialization. + */ + + spin_lock_irqsave(&wdata->state.lock, flags); + wdata->ext = NULL; + spin_unlock_irqrestore(&wdata->state.lock, flags); + + cancel_work_sync(&ext->worker); + kfree(ext); +} diff --git a/drivers/hid/hid-wiimote.h b/drivers/hid/hid-wiimote.h index 865740d..abbfab8 100644 --- a/drivers/hid/hid-wiimote.h +++ b/drivers/hid/hid-wiimote.h @@ -73,6 +73,7 @@ struct wiimote_data { struct input_dev *accel; struct input_dev *ir; struct power_supply battery; + struct wiimote_ext *ext; spinlock_t qlock; __u8 head; @@ -118,6 +119,22 @@ extern int wiimote_cmd_write(struct wiimote_data *wdata, __u32 offset, extern ssize_t wiimote_cmd_read(struct wiimote_data *wdata, __u32 offset, __u8 *rmem, __u8 size); +#ifdef CONFIG_HID_WIIMOTE_EXT + +extern int wiiext_init(struct wiimote_data *wdata); +extern void wiiext_deinit(struct wiimote_data *wdata); +extern void wiiext_event(struct wiimote_data *wdata, bool plugged); +extern bool wiiext_active(struct wiimote_data *wdata); + +#else + +static inline int wiiext_init(void *u) { return 0; } +static inline void wiiext_deinit(void *u) { } +static inline void wiiext_event(void *u, bool p) { } +static inline bool wiiext_active(void *u) { return false; } + +#endif + /* requires the state.lock spinlock to be held */ static inline bool wiimote_cmd_pending(struct wiimote_data *wdata, int cmd, __u32 opt) -- cgit v0.10.2 From 82fb1b39581e7cdd71a6ce3cf12996711a583df2 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Thu, 17 Nov 2011 14:12:02 +0100 Subject: HID: wiimote: Add extension initializer stubs Add stub functions to read and identify extensions and then initialize all connected extensions. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-wiimote-ext.c b/drivers/hid/hid-wiimote-ext.c index fa9c677..3e3e1fc 100644 --- a/drivers/hid/hid-wiimote-ext.c +++ b/drivers/hid/hid-wiimote-ext.c @@ -33,10 +33,48 @@ enum wiiext_type { WIIEXT_NUNCHUCK, /* Nintendo nunchuck controller */ }; +/* diable all extensions */ +static void ext_disable(struct wiimote_ext *ext) +{ + unsigned long flags; + + spin_lock_irqsave(&ext->wdata->state.lock, flags); + ext->motionp = false; + ext->ext_type = WIIEXT_NONE; + spin_unlock_irqrestore(&ext->wdata->state.lock, flags); +} + +static bool motionp_read(struct wiimote_ext *ext) +{ + return false; +} + +static __u8 ext_read(struct wiimote_ext *ext) +{ + return WIIEXT_NONE; +} + +static void ext_enable(struct wiimote_ext *ext, bool motionp, __u8 ext_type) +{ + unsigned long flags; + + spin_lock_irqsave(&ext->wdata->state.lock, flags); + ext->motionp = motionp; + ext->ext_type = ext_type; + spin_unlock_irqrestore(&ext->wdata->state.lock, flags); +} + static void wiiext_worker(struct work_struct *work) { struct wiimote_ext *ext = container_of(work, struct wiimote_ext, worker); + bool motionp; + __u8 ext_type; + + ext_disable(ext); + motionp = motionp_read(ext); + ext_type = ext_read(ext); + ext_enable(ext, motionp, ext_type); } /* schedule work only once, otherwise mark for reschedule */ -- cgit v0.10.2 From 492ba955c1f7b8fdc3d87b6e4765c7a5db5f7657 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Thu, 17 Nov 2011 14:12:03 +0100 Subject: HID: wiimote: Add extension initializers The wiimote extension registers are not fully understood, so we always disable all extensions on extension-port events. Then we reinitialize and reidentify them and activate all requested extensions. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-wiimote-ext.c b/drivers/hid/hid-wiimote-ext.c index 3e3e1fc..233bdfe 100644 --- a/drivers/hid/hid-wiimote-ext.c +++ b/drivers/hid/hid-wiimote-ext.c @@ -37,30 +37,107 @@ enum wiiext_type { static void ext_disable(struct wiimote_ext *ext) { unsigned long flags; + __u8 wmem = 0x55; + + if (!wiimote_cmd_acquire(ext->wdata)) { + wiimote_cmd_write(ext->wdata, 0xa400f0, &wmem, sizeof(wmem)); + wiimote_cmd_release(ext->wdata); + } spin_lock_irqsave(&ext->wdata->state.lock, flags); ext->motionp = false; ext->ext_type = WIIEXT_NONE; + wiiproto_req_drm(ext->wdata, WIIPROTO_REQ_NULL); spin_unlock_irqrestore(&ext->wdata->state.lock, flags); } static bool motionp_read(struct wiimote_ext *ext) { - return false; + __u8 rmem[2], wmem; + ssize_t ret; + bool avail = false; + + if (wiimote_cmd_acquire(ext->wdata)) + return false; + + /* initialize motion plus */ + wmem = 0x55; + ret = wiimote_cmd_write(ext->wdata, 0xa600f0, &wmem, sizeof(wmem)); + if (ret) + goto error; + + /* read motion plus ID */ + ret = wiimote_cmd_read(ext->wdata, 0xa600fe, rmem, 2); + if (ret == 2 || rmem[1] == 0x5) + avail = true; + +error: + wiimote_cmd_release(ext->wdata); + return avail; } static __u8 ext_read(struct wiimote_ext *ext) { - return WIIEXT_NONE; + ssize_t ret; + __u8 rmem[2], wmem; + __u8 type = WIIEXT_NONE; + + if (!ext->plugged) + return WIIEXT_NONE; + + if (wiimote_cmd_acquire(ext->wdata)) + return WIIEXT_NONE; + + /* initialize extension */ + wmem = 0x55; + ret = wiimote_cmd_write(ext->wdata, 0xa400f0, &wmem, sizeof(wmem)); + if (!ret) { + /* disable encryption */ + wmem = 0x0; + wiimote_cmd_write(ext->wdata, 0xa400fb, &wmem, sizeof(wmem)); + } + + /* read extension ID */ + ret = wiimote_cmd_read(ext->wdata, 0xa400fe, rmem, 2); + if (ret == 2) { + if (rmem[0] == 0 && rmem[1] == 0) + type = WIIEXT_NUNCHUCK; + else if (rmem[0] == 0x01 && rmem[1] == 0x01) + type = WIIEXT_CLASSIC; + } + + wiimote_cmd_release(ext->wdata); + + return type; } static void ext_enable(struct wiimote_ext *ext, bool motionp, __u8 ext_type) { unsigned long flags; + __u8 wmem; + int ret; + + if (motionp) { + if (wiimote_cmd_acquire(ext->wdata)) + return; + + if (ext_type == WIIEXT_CLASSIC) + wmem = 0x07; + else if (ext_type == WIIEXT_NUNCHUCK) + wmem = 0x05; + else + wmem = 0x04; + + ret = wiimote_cmd_write(ext->wdata, 0xa600fe, &wmem, sizeof(wmem)); + wiimote_cmd_release(ext->wdata); + if (ret) + return; + } spin_lock_irqsave(&ext->wdata->state.lock, flags); ext->motionp = motionp; ext->ext_type = ext_type; + wiiproto_req_drm(ext->wdata, WIIPROTO_REQ_NULL); spin_unlock_irqrestore(&ext->wdata->state.lock, flags); } -- cgit v0.10.2 From c1e51398a14bd74c58a838e9e76e8f726c5643b9 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Thu, 17 Nov 2011 14:12:04 +0100 Subject: HID: wiimote: Add extension sysfs attribute Add new sysfs attribute "extension" which returns the currently connected and initialized extensions. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina diff --git a/Documentation/ABI/testing/sysfs-driver-hid-wiimote b/Documentation/ABI/testing/sysfs-driver-hid-wiimote index 5d5a16e..3d98009 100644 --- a/Documentation/ABI/testing/sysfs-driver-hid-wiimote +++ b/Documentation/ABI/testing/sysfs-driver-hid-wiimote @@ -8,3 +8,15 @@ Contact: David Herrmann Description: Make it possible to set/get current led state. Reading from it returns 0 if led is off and 1 if it is on. Writing 0 to it disables the led, writing 1 enables it. + +What: /sys/bus/hid/drivers/wiimote//extension +Date: August 2011 +KernelVersion: 3.2 +Contact: David Herrmann +Description: This file contains the currently connected and initialized + extensions. It can be one of: none, motionp, nunchuck, classic, + motionp+nunchuck, motionp+classic + motionp is the official Nintendo Motion+ extension, nunchuck is + the official Nintendo Nunchuck extension and classic is the + Nintendo Classic Controller extension. The motionp extension can + be combined with the other two. diff --git a/drivers/hid/hid-wiimote-ext.c b/drivers/hid/hid-wiimote-ext.c index 233bdfe..477513d 100644 --- a/drivers/hid/hid-wiimote-ext.c +++ b/drivers/hid/hid-wiimote-ext.c @@ -199,11 +199,47 @@ bool wiiext_active(struct wiimote_data *wdata) return wdata->ext->motionp || wdata->ext->ext_type; } +static ssize_t wiiext_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct wiimote_data *wdata = dev_to_wii(dev); + __u8 type = WIIEXT_NONE; + bool motionp = false; + unsigned long flags; + + spin_lock_irqsave(&wdata->state.lock, flags); + if (wdata->ext) { + motionp = wdata->ext->motionp; + type = wdata->ext->ext_type; + } + spin_unlock_irqrestore(&wdata->state.lock, flags); + + if (type == WIIEXT_NUNCHUCK) { + if (motionp) + return sprintf(buf, "motionp+nunchuck\n"); + else + return sprintf(buf, "nunchuck\n"); + } else if (type == WIIEXT_CLASSIC) { + if (motionp) + return sprintf(buf, "motionp+classic\n"); + else + return sprintf(buf, "classic\n"); + } else { + if (motionp) + return sprintf(buf, "motionp\n"); + else + return sprintf(buf, "none\n"); + } +} + +static DEVICE_ATTR(extension, S_IRUGO, wiiext_show, NULL); + /* Initializes the extension driver of a wiimote */ int wiiext_init(struct wiimote_data *wdata) { struct wiimote_ext *ext; unsigned long flags; + int ret; ext = kzalloc(sizeof(*ext), GFP_KERNEL); if (!ext) @@ -212,11 +248,19 @@ int wiiext_init(struct wiimote_data *wdata) ext->wdata = wdata; INIT_WORK(&ext->worker, wiiext_worker); + ret = device_create_file(&wdata->hdev->dev, &dev_attr_extension); + if (ret) + goto err; + spin_lock_irqsave(&wdata->state.lock, flags); wdata->ext = ext; spin_unlock_irqrestore(&wdata->state.lock, flags); return 0; + +err: + kfree(ext); + return ret; } /* Deinitializes the extension driver of a wiimote */ @@ -240,6 +284,8 @@ void wiiext_deinit(struct wiimote_data *wdata) wdata->ext = NULL; spin_unlock_irqrestore(&wdata->state.lock, flags); + device_remove_file(&wdata->hdev->dev, &dev_attr_extension); + cancel_work_sync(&ext->worker); kfree(ext); } -- cgit v0.10.2 From 479901ba1847902623cc348b1d09c7d8979a9683 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Thu, 17 Nov 2011 14:12:05 +0100 Subject: HID: wiimote: Register input devices for extensions Motion+ and regular extensions are physical adapters for the wiimote so create one input device for each of them. This also allows to enable only opened extensions and turn unused extenions off to save battery power. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-wiimote-ext.c b/drivers/hid/hid-wiimote-ext.c index 477513d..b925401 100644 --- a/drivers/hid/hid-wiimote-ext.c +++ b/drivers/hid/hid-wiimote-ext.c @@ -19,6 +19,8 @@ struct wiimote_ext { struct wiimote_data *wdata; struct work_struct worker; + struct input_dev *input; + struct input_dev *mp_input; atomic_t opened; atomic_t mp_opened; @@ -57,6 +59,9 @@ static bool motionp_read(struct wiimote_ext *ext) ssize_t ret; bool avail = false; + if (!atomic_read(&ext->mp_opened)) + return false; + if (wiimote_cmd_acquire(ext->wdata)) return false; @@ -82,7 +87,7 @@ static __u8 ext_read(struct wiimote_ext *ext) __u8 rmem[2], wmem; __u8 type = WIIEXT_NONE; - if (!ext->plugged) + if (!ext->plugged || !atomic_read(&ext->opened)) return WIIEXT_NONE; if (wiimote_cmd_acquire(ext->wdata)) @@ -234,6 +239,54 @@ static ssize_t wiiext_show(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR(extension, S_IRUGO, wiiext_show, NULL); +static int wiiext_input_open(struct input_dev *dev) +{ + struct wiimote_ext *ext = input_get_drvdata(dev); + int ret; + + ret = hid_hw_open(ext->wdata->hdev); + if (ret) + return ret; + + atomic_inc(&ext->opened); + wiiext_schedule(ext); + + return 0; +} + +static void wiiext_input_close(struct input_dev *dev) +{ + struct wiimote_ext *ext = input_get_drvdata(dev); + + atomic_dec(&ext->opened); + wiiext_schedule(ext); + hid_hw_close(ext->wdata->hdev); +} + +static int wiiext_mp_open(struct input_dev *dev) +{ + struct wiimote_ext *ext = input_get_drvdata(dev); + int ret; + + ret = hid_hw_open(ext->wdata->hdev); + if (ret) + return ret; + + atomic_inc(&ext->mp_opened); + wiiext_schedule(ext); + + return 0; +} + +static void wiiext_mp_close(struct input_dev *dev) +{ + struct wiimote_ext *ext = input_get_drvdata(dev); + + atomic_dec(&ext->mp_opened); + wiiext_schedule(ext); + hid_hw_close(ext->wdata->hdev); +} + /* Initializes the extension driver of a wiimote */ int wiiext_init(struct wiimote_data *wdata) { @@ -248,9 +301,53 @@ int wiiext_init(struct wiimote_data *wdata) ext->wdata = wdata; INIT_WORK(&ext->worker, wiiext_worker); + ext->input = input_allocate_device(); + if (!ext->input) { + ret = -ENOMEM; + goto err_input; + } + + input_set_drvdata(ext->input, ext); + ext->input->open = wiiext_input_open; + ext->input->close = wiiext_input_close; + ext->input->dev.parent = &wdata->hdev->dev; + ext->input->id.bustype = wdata->hdev->bus; + ext->input->id.vendor = wdata->hdev->vendor; + ext->input->id.product = wdata->hdev->product; + ext->input->id.version = wdata->hdev->version; + ext->input->name = WIIMOTE_NAME " Extension"; + + ret = input_register_device(ext->input); + if (ret) { + input_free_device(ext->input); + goto err_input; + } + + ext->mp_input = input_allocate_device(); + if (!ext->mp_input) { + ret = -ENOMEM; + goto err_mp; + } + + input_set_drvdata(ext->mp_input, ext); + ext->mp_input->open = wiiext_mp_open; + ext->mp_input->close = wiiext_mp_close; + ext->mp_input->dev.parent = &wdata->hdev->dev; + ext->mp_input->id.bustype = wdata->hdev->bus; + ext->mp_input->id.vendor = wdata->hdev->vendor; + ext->mp_input->id.product = wdata->hdev->product; + ext->mp_input->id.version = wdata->hdev->version; + ext->mp_input->name = WIIMOTE_NAME " Motion+"; + + ret = input_register_device(ext->mp_input); + if (ret) { + input_free_device(ext->mp_input); + goto err_mp; + } + ret = device_create_file(&wdata->hdev->dev, &dev_attr_extension); if (ret) - goto err; + goto err_dev; spin_lock_irqsave(&wdata->state.lock, flags); wdata->ext = ext; @@ -258,7 +355,11 @@ int wiiext_init(struct wiimote_data *wdata) return 0; -err: +err_dev: + input_unregister_device(ext->mp_input); +err_mp: + input_unregister_device(ext->input); +err_input: kfree(ext); return ret; } @@ -285,6 +386,8 @@ void wiiext_deinit(struct wiimote_data *wdata) spin_unlock_irqrestore(&wdata->state.lock, flags); device_remove_file(&wdata->hdev->dev, &dev_attr_extension); + input_unregister_device(ext->mp_input); + input_unregister_device(ext->input); cancel_work_sync(&ext->worker); kfree(ext); -- cgit v0.10.2 From 0b6815d75d8bf214998455d94061a40f3b4a77f3 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Thu, 17 Nov 2011 14:12:06 +0100 Subject: HID: wiimote: Add extension handler stubs All supported extensions report data as 6 byte block. All DRMs with extension data provide at least 6 extension bytes. Hence a generic handler for all extension bytes is sufficient and can be called on all DRMs. The handler distinguishes the input and passes it to the right handler. Motion+ passes data interleaved so we can have Motion+ and a regular extension enabled simultaneously. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c index 2ca8bfd..d8ed1ec 100644 --- a/drivers/hid/hid-wiimote-core.c +++ b/drivers/hid/hid-wiimote-core.c @@ -857,6 +857,7 @@ static void handler_drm_KA(struct wiimote_data *wdata, const __u8 *payload) static void handler_drm_KE(struct wiimote_data *wdata, const __u8 *payload) { handler_keys(wdata, payload); + wiiext_handle(wdata, &payload[2]); } static void handler_drm_KAI(struct wiimote_data *wdata, const __u8 *payload) @@ -873,6 +874,7 @@ static void handler_drm_KAI(struct wiimote_data *wdata, const __u8 *payload) static void handler_drm_KEE(struct wiimote_data *wdata, const __u8 *payload) { handler_keys(wdata, payload); + wiiext_handle(wdata, &payload[2]); } static void handler_drm_KIE(struct wiimote_data *wdata, const __u8 *payload) @@ -883,12 +885,14 @@ static void handler_drm_KIE(struct wiimote_data *wdata, const __u8 *payload) ir_to_input2(wdata, &payload[7], false); ir_to_input3(wdata, &payload[9], true); input_sync(wdata->ir); + wiiext_handle(wdata, &payload[12]); } static void handler_drm_KAE(struct wiimote_data *wdata, const __u8 *payload) { handler_keys(wdata, payload); handler_accel(wdata, payload); + wiiext_handle(wdata, &payload[5]); } static void handler_drm_KAIE(struct wiimote_data *wdata, const __u8 *payload) @@ -900,10 +904,12 @@ static void handler_drm_KAIE(struct wiimote_data *wdata, const __u8 *payload) ir_to_input2(wdata, &payload[10], false); ir_to_input3(wdata, &payload[12], true); input_sync(wdata->ir); + wiiext_handle(wdata, &payload[15]); } static void handler_drm_E(struct wiimote_data *wdata, const __u8 *payload) { + wiiext_handle(wdata, payload); } static void handler_drm_SKAI1(struct wiimote_data *wdata, const __u8 *payload) diff --git a/drivers/hid/hid-wiimote-ext.c b/drivers/hid/hid-wiimote-ext.c index b925401..ecc7b7b 100644 --- a/drivers/hid/hid-wiimote-ext.c +++ b/drivers/hid/hid-wiimote-ext.c @@ -204,6 +204,35 @@ bool wiiext_active(struct wiimote_data *wdata) return wdata->ext->motionp || wdata->ext->ext_type; } +static void handler_motionp(struct wiimote_ext *ext, const __u8 *payload) +{ +} + +static void handler_nunchuck(struct wiimote_ext *ext, const __u8 *payload) +{ +} + +static void handler_classic(struct wiimote_ext *ext, const __u8 *payload) +{ +} + +/* call this with state.lock spinlock held */ +void wiiext_handle(struct wiimote_data *wdata, const __u8 *payload) +{ + struct wiimote_ext *ext = wdata->ext; + + if (!ext) + return; + + if (ext->motionp && (payload[5] & 0x02)) { + handler_motionp(ext, payload); + } else if (ext->ext_type == WIIEXT_NUNCHUCK) { + handler_nunchuck(ext, payload); + } else if (ext->ext_type == WIIEXT_CLASSIC) { + handler_classic(ext, payload); + } +} + static ssize_t wiiext_show(struct device *dev, struct device_attribute *attr, char *buf) { diff --git a/drivers/hid/hid-wiimote.h b/drivers/hid/hid-wiimote.h index abbfab8..1f3e53a 100644 --- a/drivers/hid/hid-wiimote.h +++ b/drivers/hid/hid-wiimote.h @@ -125,6 +125,7 @@ extern int wiiext_init(struct wiimote_data *wdata); extern void wiiext_deinit(struct wiimote_data *wdata); extern void wiiext_event(struct wiimote_data *wdata, bool plugged); extern bool wiiext_active(struct wiimote_data *wdata); +extern void wiiext_handle(struct wiimote_data *wdata, const __u8 *payload); #else @@ -132,6 +133,7 @@ static inline int wiiext_init(void *u) { return 0; } static inline void wiiext_deinit(void *u) { } static inline void wiiext_event(void *u, bool p) { } static inline bool wiiext_active(void *u) { return false; } +static inline void wiiext_handle(void *u, const __u8 *p) { } #endif -- cgit v0.10.2 From b17b57a5d0fcfc1d6ba582a086b3a22510aef03d Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Thu, 17 Nov 2011 14:12:07 +0100 Subject: HID: wiimote: Parse motion+ data Motion+ reports rotation gyro data which we report to userspace as ABS_RX/Y/Z values. The device reports them either in fast or slow mode. We adjust the values to get a linear scale so userspace does not need to know about slow and fast mode. The motion+ also reports whether an extension is connected to it. We keep track of this value and reinitialize the extensions if an extension is plugged or unplugged. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-wiimote-ext.c b/drivers/hid/hid-wiimote-ext.c index ecc7b7b..ceec0ce 100644 --- a/drivers/hid/hid-wiimote-ext.c +++ b/drivers/hid/hid-wiimote-ext.c @@ -25,6 +25,7 @@ struct wiimote_ext { atomic_t opened; atomic_t mp_opened; bool plugged; + bool mp_plugged; bool motionp; __u8 ext_type; }; @@ -182,6 +183,10 @@ void wiiext_event(struct wiimote_data *wdata, bool plugged) return; wdata->ext->plugged = plugged; + + if (!plugged) + wdata->ext->mp_plugged = false; + /* * We need to call wiiext_schedule(wdata->ext) here, however, the * extension initialization logic is not fully understood and so @@ -206,6 +211,63 @@ bool wiiext_active(struct wiimote_data *wdata) static void handler_motionp(struct wiimote_ext *ext, const __u8 *payload) { + __s32 x, y, z; + bool plugged; + + /* | 8 7 6 5 4 3 | 2 | 1 | + * -----+------------------------------+-----+-----+ + * 1 | Yaw Speed <7:0> | + * 2 | Roll Speed <7:0> | + * 3 | Pitch Speed <7:0> | + * -----+------------------------------+-----+-----+ + * 4 | Yaw Speed <13:8> | Yaw |Pitch| + * -----+------------------------------+-----+-----+ + * 5 | Roll Speed <13:8> |Roll | Ext | + * -----+------------------------------+-----+-----+ + * 6 | Pitch Speed <13:8> | 1 | 0 | + * -----+------------------------------+-----+-----+ + * The single bits Yaw, Roll, Pitch in the lower right corner specify + * whether the wiimote is rotating fast (0) or slow (1). Speed for slow + * roation is 440 deg/s and for fast rotation 2000 deg/s. To get a + * linear scale we multiply by 2000/440 = ~4.5454 which is 18 for fast + * and 9 for slow. + * If the wiimote is not rotating the sensor reports 2^13 = 8192. + * Ext specifies whether an extension is connected to the motionp. + */ + + x = payload[0]; + y = payload[1]; + z = payload[2]; + + x |= (((__u16)payload[3]) << 6) & 0xff00; + y |= (((__u16)payload[4]) << 6) & 0xff00; + z |= (((__u16)payload[5]) << 6) & 0xff00; + + x -= 8192; + y -= 8192; + z -= 8192; + + if (!(payload[3] & 0x02)) + x *= 18; + else + x *= 9; + if (!(payload[4] & 0x02)) + y *= 18; + else + y *= 9; + if (!(payload[3] & 0x01)) + z *= 18; + else + z *= 9; + + input_report_abs(ext->mp_input, ABS_RX, x); + input_report_abs(ext->mp_input, ABS_RY, y); + input_report_abs(ext->mp_input, ABS_RZ, z); + input_sync(ext->mp_input); + + plugged = payload[5] & 0x01; + if (plugged != ext->mp_plugged) + ext->mp_plugged = plugged; } static void handler_nunchuck(struct wiimote_ext *ext, const __u8 *payload) @@ -368,6 +430,14 @@ int wiiext_init(struct wiimote_data *wdata) ext->mp_input->id.version = wdata->hdev->version; ext->mp_input->name = WIIMOTE_NAME " Motion+"; + set_bit(EV_ABS, ext->mp_input->evbit); + set_bit(ABS_RX, ext->mp_input->absbit); + set_bit(ABS_RY, ext->mp_input->absbit); + set_bit(ABS_RZ, ext->mp_input->absbit); + input_set_abs_params(ext->mp_input, ABS_RX, -160000, 160000, 4, 8); + input_set_abs_params(ext->mp_input, ABS_RY, -160000, 160000, 4, 8); + input_set_abs_params(ext->mp_input, ABS_RZ, -160000, 160000, 4, 8); + ret = input_register_device(ext->mp_input); if (ret) { input_free_device(ext->mp_input); -- cgit v0.10.2 From a53535014b7af750df3d8eda471dce21b2aa339c Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Thu, 17 Nov 2011 14:12:08 +0100 Subject: HID: wiimote: Parse nunchuck data The Nintendo Nunchuck extension reports accelerometer values, one analog stick and two buttons. See inline comments for data layout. We report all data to userspace through extension input device. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-wiimote-ext.c b/drivers/hid/hid-wiimote-ext.c index ceec0ce..f05f154 100644 --- a/drivers/hid/hid-wiimote-ext.c +++ b/drivers/hid/hid-wiimote-ext.c @@ -36,6 +36,17 @@ enum wiiext_type { WIIEXT_NUNCHUCK, /* Nintendo nunchuck controller */ }; +enum wiiext_keys { + WIIEXT_KEY_C, + WIIEXT_KEY_Z, + WIIEXT_KEY_COUNT +}; + +static __u16 wiiext_keymap[] = { + BTN_C, /* WIIEXT_KEY_C */ + BTN_Z, /* WIIEXT_KEY_Z */ +}; + /* diable all extensions */ static void ext_disable(struct wiimote_ext *ext) { @@ -272,6 +283,82 @@ static void handler_motionp(struct wiimote_ext *ext, const __u8 *payload) static void handler_nunchuck(struct wiimote_ext *ext, const __u8 *payload) { + __s16 x, y, z, bx, by; + + /* Byte | 8 7 | 6 5 | 4 3 | 2 | 1 | + * -----+----------+---------+---------+----+-----+ + * 1 | Button X <7:0> | + * 2 | Button Y <7:0> | + * -----+----------+---------+---------+----+-----+ + * 3 | Speed X <9:2> | + * 4 | Speed Y <9:2> | + * 5 | Speed Z <9:2> | + * -----+----------+---------+---------+----+-----+ + * 6 | Z <1:0> | Y <1:0> | X <1:0> | BC | BZ | + * -----+----------+---------+---------+----+-----+ + * Button X/Y is the analog stick. Speed X, Y and Z are the + * accelerometer data in the same format as the wiimote's accelerometer. + * The 6th byte contains the LSBs of the accelerometer data. + * BC and BZ are the C and Z buttons: 0 means pressed + * + * If reported interleaved with motionp, then the layout changes. The + * 5th and 6th byte changes to: + * -----+-----------------------------------+-----+ + * 5 | Speed Z <9:3> | EXT | + * -----+--------+-----+-----+----+----+----+-----+ + * 6 |Z <2:1> |Y <1>|X <1>| BC | BZ | 0 | 0 | + * -----+--------+-----+-----+----+----+----+-----+ + * All three accelerometer values lose their LSB. The other data is + * still available but slightly moved. + * + * Center data for button values is 128. Center value for accelerometer + * values it 512 / 0x200 + */ + + bx = payload[0]; + by = payload[1]; + bx -= 128; + by -= 128; + + x = payload[2] << 2; + y = payload[3] << 2; + z = payload[4] << 2; + + if (ext->motionp) { + x |= (payload[5] >> 3) & 0x02; + y |= (payload[5] >> 4) & 0x02; + z &= ~0x4; + z |= (payload[5] >> 5) & 0x06; + } else { + x |= (payload[5] >> 2) & 0x03; + y |= (payload[5] >> 4) & 0x03; + z |= (payload[5] >> 6) & 0x03; + } + + x -= 0x200; + y -= 0x200; + z -= 0x200; + + input_report_abs(ext->input, ABS_HAT0X, bx); + input_report_abs(ext->input, ABS_HAT0Y, by); + + input_report_abs(ext->input, ABS_RX, x); + input_report_abs(ext->input, ABS_RY, y); + input_report_abs(ext->input, ABS_RZ, z); + + if (ext->motionp) { + input_report_key(ext->input, + wiiext_keymap[WIIEXT_KEY_Z], !!(payload[5] & 0x04)); + input_report_key(ext->input, + wiiext_keymap[WIIEXT_KEY_C], !!(payload[5] & 0x08)); + } else { + input_report_key(ext->input, + wiiext_keymap[WIIEXT_KEY_Z], !!(payload[5] & 0x01)); + input_report_key(ext->input, + wiiext_keymap[WIIEXT_KEY_C], !!(payload[5] & 0x02)); + } + + input_sync(ext->input); } static void handler_classic(struct wiimote_ext *ext, const __u8 *payload) @@ -383,7 +470,7 @@ int wiiext_init(struct wiimote_data *wdata) { struct wiimote_ext *ext; unsigned long flags; - int ret; + int ret, i; ext = kzalloc(sizeof(*ext), GFP_KERNEL); if (!ext) @@ -408,6 +495,22 @@ int wiiext_init(struct wiimote_data *wdata) ext->input->id.version = wdata->hdev->version; ext->input->name = WIIMOTE_NAME " Extension"; + set_bit(EV_KEY, ext->input->evbit); + for (i = 0; i < WIIEXT_KEY_COUNT; ++i) + set_bit(wiiext_keymap[i], ext->input->keybit); + + set_bit(EV_ABS, ext->input->evbit); + set_bit(ABS_HAT0X, ext->input->absbit); + set_bit(ABS_HAT0Y, ext->input->absbit); + input_set_abs_params(ext->input, ABS_HAT0X, -120, 120, 2, 4); + input_set_abs_params(ext->input, ABS_HAT0Y, -120, 120, 2, 4); + set_bit(ABS_RX, ext->input->absbit); + set_bit(ABS_RY, ext->input->absbit); + set_bit(ABS_RZ, ext->input->absbit); + input_set_abs_params(ext->input, ABS_RX, -500, 500, 2, 4); + input_set_abs_params(ext->input, ABS_RY, -500, 500, 2, 4); + input_set_abs_params(ext->input, ABS_RZ, -500, 500, 2, 4); + ret = input_register_device(ext->input); if (ret) { input_free_device(ext->input); -- cgit v0.10.2 From 5906215bab9fccf7aa2c4305accf0716c4634d69 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Thu, 17 Nov 2011 14:12:09 +0100 Subject: HID: wiimote: Parse classic controller data Nintendo Classic Controller extension reports lots of keys, two analog sticks and two analog buttons. We report all data through extension input device to userspace. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-wiimote-ext.c b/drivers/hid/hid-wiimote-ext.c index f05f154..aa95870 100644 --- a/drivers/hid/hid-wiimote-ext.c +++ b/drivers/hid/hid-wiimote-ext.c @@ -39,12 +39,42 @@ enum wiiext_type { enum wiiext_keys { WIIEXT_KEY_C, WIIEXT_KEY_Z, + WIIEXT_KEY_A, + WIIEXT_KEY_B, + WIIEXT_KEY_X, + WIIEXT_KEY_Y, + WIIEXT_KEY_ZL, + WIIEXT_KEY_ZR, + WIIEXT_KEY_PLUS, + WIIEXT_KEY_MINUS, + WIIEXT_KEY_HOME, + WIIEXT_KEY_LEFT, + WIIEXT_KEY_RIGHT, + WIIEXT_KEY_UP, + WIIEXT_KEY_DOWN, + WIIEXT_KEY_LT, + WIIEXT_KEY_RT, WIIEXT_KEY_COUNT }; static __u16 wiiext_keymap[] = { BTN_C, /* WIIEXT_KEY_C */ BTN_Z, /* WIIEXT_KEY_Z */ + BTN_A, /* WIIEXT_KEY_A */ + BTN_B, /* WIIEXT_KEY_B */ + BTN_X, /* WIIEXT_KEY_X */ + BTN_Y, /* WIIEXT_KEY_Y */ + BTN_TL2, /* WIIEXT_KEY_ZL */ + BTN_TR2, /* WIIEXT_KEY_ZR */ + KEY_NEXT, /* WIIEXT_KEY_PLUS */ + KEY_PREVIOUS, /* WIIEXT_KEY_MINUS */ + BTN_MODE, /* WIIEXT_KEY_HOME */ + KEY_LEFT, /* WIIEXT_KEY_LEFT */ + KEY_RIGHT, /* WIIEXT_KEY_RIGHT */ + KEY_UP, /* WIIEXT_KEY_UP */ + KEY_DOWN, /* WIIEXT_KEY_DOWN */ + BTN_TL, /* WIIEXT_KEY_LT */ + BTN_TR, /* WIIEXT_KEY_RT */ }; /* diable all extensions */ @@ -363,6 +393,120 @@ static void handler_nunchuck(struct wiimote_ext *ext, const __u8 *payload) static void handler_classic(struct wiimote_ext *ext, const __u8 *payload) { + __s8 rx, ry, lx, ly, lt, rt; + + /* Byte | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + * 1 | RX <5:4> | LX <5:0> | + * 2 | RX <3:2> | LY <5:0> | + * -----+-----+-----+-----+-----------------------------+ + * 3 |RX<1>| LT <5:4> | RY <5:1> | + * -----+-----+-----------+-----------------------------+ + * 4 | LT <3:1> | RT <5:1> | + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + * 5 | BDR | BDD | BLT | B- | BH | B+ | BRT | 1 | + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + * 6 | BZL | BB | BY | BA | BX | BZR | BDL | BDU | + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + * All buttons are 0 if pressed + * RX and RY are right analog stick + * LX and LY are left analog stick + * LT is left trigger, RT is right trigger + * BLT is 0 if left trigger is fully pressed + * BRT is 0 if right trigger is fully pressed + * BDR, BDD, BDL, BDU form the D-Pad with right, down, left, up buttons + * BZL is left Z button and BZR is right Z button + * B-, BH, B+ are +, HOME and - buttons + * BB, BY, BA, BX are A, B, X, Y buttons + * LSB of RX, RY, LT, and RT are not transmitted and always 0. + * + * With motionp enabled it changes slightly to this: + * Byte | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + * 1 | RX <4:3> | LX <5:1> | BDU | + * 2 | RX <2:1> | LY <5:1> | BDL | + * -----+-----+-----+-----+-----------------------+-----+ + * 3 |RX<0>| LT <4:3> | RY <4:0> | + * -----+-----+-----------+-----------------------------+ + * 4 | LT <2:0> | RT <4:0> | + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + * 5 | BDR | BDD | BLT | B- | BH | B+ | BRT | EXT | + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + * 6 | BZL | BB | BY | BA | BX | BZR | 0 | 0 | + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + * Only the LSBs of LX and LY are lost. BDU and BDL are moved, the rest + * is the same as before. + */ + + if (ext->motionp) { + lx = payload[0] & 0x3e; + ly = payload[0] & 0x3e; + } else { + lx = payload[0] & 0x3f; + ly = payload[0] & 0x3f; + } + + rx = (payload[0] >> 3) & 0x14; + rx |= (payload[1] >> 5) & 0x06; + rx |= (payload[2] >> 7) & 0x01; + ry = payload[2] & 0x1f; + + rt = payload[3] & 0x1f; + lt = (payload[2] >> 2) & 0x18; + lt |= (payload[3] >> 5) & 0x07; + + rx <<= 1; + ry <<= 1; + rt <<= 1; + lt <<= 1; + + input_report_abs(ext->input, ABS_HAT1X, lx - 0x20); + input_report_abs(ext->input, ABS_HAT1Y, ly - 0x20); + input_report_abs(ext->input, ABS_HAT2X, rx - 0x20); + input_report_abs(ext->input, ABS_HAT2Y, ry - 0x20); + input_report_abs(ext->input, ABS_HAT3X, rt - 0x20); + input_report_abs(ext->input, ABS_HAT3Y, lt - 0x20); + + input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_RIGHT], + !!(payload[4] & 0x80)); + input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_DOWN], + !!(payload[4] & 0x40)); + input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_LT], + !!(payload[4] & 0x20)); + input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_MINUS], + !!(payload[4] & 0x10)); + input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_HOME], + !!(payload[4] & 0x08)); + input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_PLUS], + !!(payload[4] & 0x04)); + input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_RT], + !!(payload[4] & 0x02)); + input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_ZL], + !!(payload[5] & 0x80)); + input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_B], + !!(payload[5] & 0x40)); + input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_Y], + !!(payload[5] & 0x20)); + input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_A], + !!(payload[5] & 0x10)); + input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_X], + !!(payload[5] & 0x08)); + input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_ZR], + !!(payload[5] & 0x04)); + + if (ext->motionp) { + input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_UP], + !!(payload[0] & 0x01)); + input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_LEFT], + !!(payload[1] & 0x01)); + } else { + input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_UP], + !!(payload[5] & 0x01)); + input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_LEFT], + !!(payload[5] & 0x02)); + } + + input_sync(ext->input); } /* call this with state.lock spinlock held */ @@ -502,8 +646,20 @@ int wiiext_init(struct wiimote_data *wdata) set_bit(EV_ABS, ext->input->evbit); set_bit(ABS_HAT0X, ext->input->absbit); set_bit(ABS_HAT0Y, ext->input->absbit); + set_bit(ABS_HAT1X, ext->input->absbit); + set_bit(ABS_HAT1Y, ext->input->absbit); + set_bit(ABS_HAT2X, ext->input->absbit); + set_bit(ABS_HAT2Y, ext->input->absbit); + set_bit(ABS_HAT3X, ext->input->absbit); + set_bit(ABS_HAT3Y, ext->input->absbit); input_set_abs_params(ext->input, ABS_HAT0X, -120, 120, 2, 4); input_set_abs_params(ext->input, ABS_HAT0Y, -120, 120, 2, 4); + input_set_abs_params(ext->input, ABS_HAT1X, -30, 30, 1, 1); + input_set_abs_params(ext->input, ABS_HAT1Y, -30, 30, 1, 1); + input_set_abs_params(ext->input, ABS_HAT2X, -30, 30, 1, 1); + input_set_abs_params(ext->input, ABS_HAT2Y, -30, 30, 1, 1); + input_set_abs_params(ext->input, ABS_HAT3X, -30, 30, 1, 1); + input_set_abs_params(ext->input, ABS_HAT3Y, -30, 30, 1, 1); set_bit(ABS_RX, ext->input->absbit); set_bit(ABS_RY, ext->input->absbit); set_bit(ABS_RZ, ext->input->absbit); -- cgit v0.10.2 From 43e5e7c60ee7039f538ccfaaa4e99829719d9bea Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Thu, 17 Nov 2011 14:12:10 +0100 Subject: HID: wiimote: Add debugfs support stubs Add initializer and deinitializer for debugfs support. This will later allow raw eeprom access and direct DRM modifications to debug wiimote behaviour and further protocol reverse-engineerings. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 5fab4e63..15c1a84 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -29,6 +29,9 @@ hid-wiimote-y := hid-wiimote-core.o ifdef CONFIG_HID_WIIMOTE_EXT hid-wiimote-y += hid-wiimote-ext.o endif +ifdef CONFIG_DEBUG_FS + hid-wiimote-y += hid-wiimote-debug.o +endif obj-$(CONFIG_HID_A4TECH) += hid-a4tech.o obj-$(CONFIG_HID_ACRUX) += hid-axff.o diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c index d8ed1ec..919abba 100644 --- a/drivers/hid/hid-wiimote-core.c +++ b/drivers/hid/hid-wiimote-core.c @@ -1161,6 +1161,7 @@ err: static void wiimote_destroy(struct wiimote_data *wdata) { + wiidebug_deinit(wdata); wiiext_deinit(wdata); wiimote_leds_destroy(wdata); @@ -1237,6 +1238,10 @@ static int wiimote_hid_probe(struct hid_device *hdev, if (ret) goto err_free; + ret = wiidebug_init(wdata); + if (ret) + goto err_free; + hid_info(hdev, "New device registered\n"); /* by default set led1 after device initialization */ diff --git a/drivers/hid/hid-wiimote-debug.c b/drivers/hid/hid-wiimote-debug.c new file mode 100644 index 0000000..6282e3c --- /dev/null +++ b/drivers/hid/hid-wiimote-debug.c @@ -0,0 +1,52 @@ +/* + * Debug support for HID Nintendo Wiimote devices + * Copyright (c) 2011 David Herrmann + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include +#include +#include "hid-wiimote.h" + +struct wiimote_debug { + struct wiimote_data *wdata; +}; + +int wiidebug_init(struct wiimote_data *wdata) +{ + struct wiimote_debug *dbg; + unsigned long flags; + + dbg = kzalloc(sizeof(*dbg), GFP_KERNEL); + if (!dbg) + return -ENOMEM; + + dbg->wdata = wdata; + + spin_lock_irqsave(&wdata->state.lock, flags); + wdata->debug = dbg; + spin_unlock_irqrestore(&wdata->state.lock, flags); + + return 0; +} + +void wiidebug_deinit(struct wiimote_data *wdata) +{ + struct wiimote_debug *dbg = wdata->debug; + unsigned long flags; + + if (!dbg) + return; + + spin_lock_irqsave(&wdata->state.lock, flags); + wdata->debug = NULL; + spin_unlock_irqrestore(&wdata->state.lock, flags); + + kfree(dbg); +} diff --git a/drivers/hid/hid-wiimote.h b/drivers/hid/hid-wiimote.h index 1f3e53a..89b8851 100644 --- a/drivers/hid/hid-wiimote.h +++ b/drivers/hid/hid-wiimote.h @@ -74,6 +74,7 @@ struct wiimote_data { struct input_dev *ir; struct power_supply battery; struct wiimote_ext *ext; + struct wiimote_debug *debug; spinlock_t qlock; __u8 head; @@ -137,6 +138,18 @@ static inline void wiiext_handle(void *u, const __u8 *p) { } #endif +#ifdef CONFIG_DEBUG_FS + +extern int wiidebug_init(struct wiimote_data *wdata); +extern void wiidebug_deinit(struct wiimote_data *wdata); + +#else + +static inline int wiidebug_init(void *u) { return 0; } +static inline void wiidebug_deinit(void *u) { } + +#endif + /* requires the state.lock spinlock to be held */ static inline bool wiimote_cmd_pending(struct wiimote_data *wdata, int cmd, __u32 opt) -- cgit v0.10.2 From 1d3452c63d4b62329d34d7634f67a3dbec21ca87 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Thu, 17 Nov 2011 14:12:11 +0100 Subject: HID: wiimote: Allow direct eeprom access The wiimote provides direct access to parts of its eeprom. This implements read support for small chunks of the eeprom. This isn't very fast but prevents the reader from blocking the wiimote stream for too long. Write support is not yet supported as the wiimote breaks if we overwrite its memory. Use hidraw to reverse-engineer the eeprom before implementing write support here. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c index 919abba..2fd2f03 100644 --- a/drivers/hid/hid-wiimote-core.c +++ b/drivers/hid/hid-wiimote-core.c @@ -320,14 +320,8 @@ static void wiiproto_req_wmem(struct wiimote_data *wdata, bool eeprom, wiimote_queue(wdata, cmd, sizeof(cmd)); } -#define wiiproto_req_rreg(wdata, os, sz) \ - wiiproto_req_rmem((wdata), false, (os), (sz)) - -#define wiiproto_req_reeprom(wdata, os, sz) \ - wiiproto_req_rmem((wdata), true, (os), (sz)) - -static void wiiproto_req_rmem(struct wiimote_data *wdata, bool eeprom, - __u32 offset, __u16 size) +void wiiproto_req_rmem(struct wiimote_data *wdata, bool eeprom, __u32 offset, + __u16 size) { __u8 cmd[7]; diff --git a/drivers/hid/hid-wiimote-debug.c b/drivers/hid/hid-wiimote-debug.c index 6282e3c..f81243c 100644 --- a/drivers/hid/hid-wiimote-debug.c +++ b/drivers/hid/hid-wiimote-debug.c @@ -10,12 +10,80 @@ * any later version. */ +#include #include #include +#include #include "hid-wiimote.h" struct wiimote_debug { struct wiimote_data *wdata; + struct dentry *eeprom; +}; + +static int wiidebug_eeprom_open(struct inode *i, struct file *f) +{ + f->private_data = i->i_private; + return 0; +} + +static ssize_t wiidebug_eeprom_read(struct file *f, char __user *u, size_t s, + loff_t *off) +{ + struct wiimote_debug *dbg = f->private_data; + struct wiimote_data *wdata = dbg->wdata; + unsigned long flags; + ssize_t ret; + char buf[16]; + __u16 size; + + if (s == 0) + return -EINVAL; + if (*off > 0xffffff) + return 0; + if (s > 16) + s = 16; + + ret = wiimote_cmd_acquire(wdata); + if (ret) + return ret; + + spin_lock_irqsave(&wdata->state.lock, flags); + wdata->state.cmd_read_size = s; + wdata->state.cmd_read_buf = buf; + wiimote_cmd_set(wdata, WIIPROTO_REQ_RMEM, *off & 0xffff); + wiiproto_req_reeprom(wdata, *off, s); + spin_unlock_irqrestore(&wdata->state.lock, flags); + + ret = wiimote_cmd_wait(wdata); + if (!ret) + size = wdata->state.cmd_read_size; + + spin_lock_irqsave(&wdata->state.lock, flags); + wdata->state.cmd_read_buf = NULL; + spin_unlock_irqrestore(&wdata->state.lock, flags); + + wiimote_cmd_release(wdata); + + if (ret) + return ret; + else if (size == 0) + return -EIO; + + if (copy_to_user(u, buf, size)) + return -EFAULT; + + *off += size; + ret = size; + + return ret; +} + +static const struct file_operations wiidebug_eeprom_fops = { + .owner = THIS_MODULE, + .open = wiidebug_eeprom_open, + .read = wiidebug_eeprom_read, + .llseek = generic_file_llseek, }; int wiidebug_init(struct wiimote_data *wdata) @@ -29,6 +97,13 @@ int wiidebug_init(struct wiimote_data *wdata) dbg->wdata = wdata; + dbg->eeprom = debugfs_create_file("eeprom", S_IRUSR, + dbg->wdata->hdev->debug_dir, dbg, &wiidebug_eeprom_fops); + if (!dbg->eeprom) { + kfree(dbg); + return -ENOMEM; + } + spin_lock_irqsave(&wdata->state.lock, flags); wdata->debug = dbg; spin_unlock_irqrestore(&wdata->state.lock, flags); @@ -48,5 +123,6 @@ void wiidebug_deinit(struct wiimote_data *wdata) wdata->debug = NULL; spin_unlock_irqrestore(&wdata->state.lock, flags); + debugfs_remove(dbg->eeprom); kfree(dbg); } diff --git a/drivers/hid/hid-wiimote.h b/drivers/hid/hid-wiimote.h index 89b8851..7b67657 100644 --- a/drivers/hid/hid-wiimote.h +++ b/drivers/hid/hid-wiimote.h @@ -120,6 +120,13 @@ extern int wiimote_cmd_write(struct wiimote_data *wdata, __u32 offset, extern ssize_t wiimote_cmd_read(struct wiimote_data *wdata, __u32 offset, __u8 *rmem, __u8 size); +#define wiiproto_req_rreg(wdata, os, sz) \ + wiiproto_req_rmem((wdata), false, (os), (sz)) +#define wiiproto_req_reeprom(wdata, os, sz) \ + wiiproto_req_rmem((wdata), true, (os), (sz)) +extern void wiiproto_req_rmem(struct wiimote_data *wdata, bool eeprom, + __u32 offset, __u16 size); + #ifdef CONFIG_HID_WIIMOTE_EXT extern int wiiext_init(struct wiimote_data *wdata); -- cgit v0.10.2 From 43d782ae80b82667d66010d0d82aa80893a48d12 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Thu, 17 Nov 2011 14:12:12 +0100 Subject: HID: wiimote: Allow direct DRM debug access Keep track of current drm and add new debugfs file which reads or writes the current DRM. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c index 2fd2f03..745667e 100644 --- a/drivers/hid/hid-wiimote-core.c +++ b/drivers/hid/hid-wiimote-core.c @@ -238,6 +238,7 @@ void wiiproto_req_drm(struct wiimote_data *wdata, __u8 drm) cmd[1] = 0; cmd[2] = drm; + wdata->state.drm = drm; wiiproto_keep_rumble(wdata, &cmd[1]); wiimote_queue(wdata, cmd, sizeof(cmd)); } @@ -1141,6 +1142,7 @@ static struct wiimote_data *wiimote_create(struct hid_device *hdev) spin_lock_init(&wdata->state.lock); init_completion(&wdata->state.ready); mutex_init(&wdata->state.sync); + wdata->state.drm = WIIPROTO_REQ_DRM_K; return wdata; diff --git a/drivers/hid/hid-wiimote-debug.c b/drivers/hid/hid-wiimote-debug.c index f81243c..17dabc1 100644 --- a/drivers/hid/hid-wiimote-debug.c +++ b/drivers/hid/hid-wiimote-debug.c @@ -12,6 +12,7 @@ #include #include +#include #include #include #include "hid-wiimote.h" @@ -19,6 +20,7 @@ struct wiimote_debug { struct wiimote_data *wdata; struct dentry *eeprom; + struct dentry *drm; }; static int wiidebug_eeprom_open(struct inode *i, struct file *f) @@ -86,10 +88,97 @@ static const struct file_operations wiidebug_eeprom_fops = { .llseek = generic_file_llseek, }; +static const char *wiidebug_drmmap[] = { + [WIIPROTO_REQ_NULL] = "NULL", + [WIIPROTO_REQ_DRM_K] = "K", + [WIIPROTO_REQ_DRM_KA] = "KA", + [WIIPROTO_REQ_DRM_KE] = "KE", + [WIIPROTO_REQ_DRM_KAI] = "KAI", + [WIIPROTO_REQ_DRM_KEE] = "KEE", + [WIIPROTO_REQ_DRM_KAE] = "KAE", + [WIIPROTO_REQ_DRM_KIE] = "KIE", + [WIIPROTO_REQ_DRM_KAIE] = "KAIE", + [WIIPROTO_REQ_DRM_E] = "E", + [WIIPROTO_REQ_DRM_SKAI1] = "SKAI1", + [WIIPROTO_REQ_DRM_SKAI2] = "SKAI2", + [WIIPROTO_REQ_MAX] = NULL +}; + +static int wiidebug_drm_show(struct seq_file *f, void *p) +{ + struct wiimote_debug *dbg = f->private; + const char *str = NULL; + unsigned long flags; + __u8 drm; + + spin_lock_irqsave(&dbg->wdata->state.lock, flags); + drm = dbg->wdata->state.drm; + spin_unlock_irqrestore(&dbg->wdata->state.lock, flags); + + if (drm < WIIPROTO_REQ_MAX) + str = wiidebug_drmmap[drm]; + if (!str) + str = "unknown"; + + seq_printf(f, "%s\n", str); + + return 0; +} + +static int wiidebug_drm_open(struct inode *i, struct file *f) +{ + return single_open(f, wiidebug_drm_show, i->i_private); +} + +static ssize_t wiidebug_drm_write(struct file *f, const char __user *u, + size_t s, loff_t *off) +{ + struct wiimote_debug *dbg = f->private_data; + unsigned long flags; + char buf[16]; + ssize_t len; + int i; + + if (s == 0) + return -EINVAL; + + len = min((size_t) 15, s); + if (copy_from_user(buf, u, len)) + return -EFAULT; + + buf[15] = 0; + + for (i = 0; i < WIIPROTO_REQ_MAX; ++i) { + if (!wiidebug_drmmap[i]) + continue; + if (!strcasecmp(buf, wiidebug_drmmap[i])) + break; + } + + if (i == WIIPROTO_REQ_MAX) + i = simple_strtoul(buf, NULL, 10); + + spin_lock_irqsave(&dbg->wdata->state.lock, flags); + wiiproto_req_drm(dbg->wdata, (__u8) i); + spin_unlock_irqrestore(&dbg->wdata->state.lock, flags); + + return len; +} + +static const struct file_operations wiidebug_drm_fops = { + .owner = THIS_MODULE, + .open = wiidebug_drm_open, + .read = seq_read, + .llseek = seq_lseek, + .write = wiidebug_drm_write, + .release = single_release, +}; + int wiidebug_init(struct wiimote_data *wdata) { struct wiimote_debug *dbg; unsigned long flags; + int ret = -ENOMEM; dbg = kzalloc(sizeof(*dbg), GFP_KERNEL); if (!dbg) @@ -99,16 +188,25 @@ int wiidebug_init(struct wiimote_data *wdata) dbg->eeprom = debugfs_create_file("eeprom", S_IRUSR, dbg->wdata->hdev->debug_dir, dbg, &wiidebug_eeprom_fops); - if (!dbg->eeprom) { - kfree(dbg); - return -ENOMEM; - } + if (!dbg->eeprom) + goto err; + + dbg->drm = debugfs_create_file("drm", S_IRUSR, + dbg->wdata->hdev->debug_dir, dbg, &wiidebug_drm_fops); + if (!dbg->drm) + goto err_drm; spin_lock_irqsave(&wdata->state.lock, flags); wdata->debug = dbg; spin_unlock_irqrestore(&wdata->state.lock, flags); return 0; + +err_drm: + debugfs_remove(dbg->eeprom); +err: + kfree(dbg); + return ret; } void wiidebug_deinit(struct wiimote_data *wdata) @@ -123,6 +221,7 @@ void wiidebug_deinit(struct wiimote_data *wdata) wdata->debug = NULL; spin_unlock_irqrestore(&wdata->state.lock, flags); + debugfs_remove(dbg->drm); debugfs_remove(dbg->eeprom); kfree(dbg); } diff --git a/drivers/hid/hid-wiimote.h b/drivers/hid/hid-wiimote.h index 7b67657..c81dbeb 100644 --- a/drivers/hid/hid-wiimote.h +++ b/drivers/hid/hid-wiimote.h @@ -52,6 +52,7 @@ struct wiimote_state { spinlock_t lock; __u8 flags; __u8 accel_split[2]; + __u8 drm; /* synchronous cmd requests */ struct mutex sync; @@ -109,6 +110,7 @@ enum wiiproto_reqs { WIIPROTO_REQ_DRM_E = 0x3d, WIIPROTO_REQ_DRM_SKAI1 = 0x3e, WIIPROTO_REQ_DRM_SKAI2 = 0x3f, + WIIPROTO_REQ_MAX }; #define dev_to_wii(pdev) hid_get_drvdata(container_of(pdev, struct hid_device, \ -- cgit v0.10.2 From 130a69b9eb385566d861035b47779f4f2b0df14c Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Thu, 17 Nov 2011 14:12:13 +0100 Subject: HID: wiimote: Remove module version number The version number is not needed at all for in-tree drivers. Upstream git is used to track module versions. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c index 745667e..0cb391d 100644 --- a/drivers/hid/hid-wiimote-core.c +++ b/drivers/hid/hid-wiimote-core.c @@ -22,8 +22,6 @@ #include "hid-ids.h" #include "hid-wiimote.h" -#define WIIMOTE_VERSION "0.2" - enum wiiproto_keys { WIIPROTO_KEY_LEFT, WIIPROTO_KEY_RIGHT, @@ -1314,4 +1312,3 @@ module_exit(wiimote_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("David Herrmann "); MODULE_DESCRIPTION(WIIMOTE_NAME " Device Driver"); -MODULE_VERSION(WIIMOTE_VERSION); -- cgit v0.10.2 From 90120d66974ecf22c32d8cbb2347097ce4144403 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Thu, 17 Nov 2011 14:12:14 +0100 Subject: HID: wiimote: Enable NO_INIT_REPORTS quirk Newer bluetooth stack supports the NO_INIT_REPORTS quirk. The wiimote does not support report initialization so enable it by default. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c index 0cb391d..61881b3 100644 --- a/drivers/hid/hid-wiimote-core.c +++ b/drivers/hid/hid-wiimote-core.c @@ -1175,6 +1175,8 @@ static int wiimote_hid_probe(struct hid_device *hdev, struct wiimote_data *wdata; int ret; + hdev->quirks |= HID_QUIRK_NO_INIT_REPORTS; + wdata = wiimote_create(hdev); if (!wdata) { hid_err(hdev, "Can't alloc device\n"); -- cgit v0.10.2 From b3c21d2cadd568d31db72f37c52bd1d501d1be13 Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Tue, 22 Nov 2011 23:23:37 +0100 Subject: HID: multitouch: make struct mt_classess static Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index f1c909f..b32beb9 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -133,7 +133,7 @@ static int find_slot_from_contactid(struct mt_device *td) return -1; } -struct mt_class mt_classes[] = { +static struct mt_class mt_classes[] = { { .name = MT_CLS_DEFAULT, .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP }, { .name = MT_CLS_SERIAL, -- cgit v0.10.2 From f2c4826c685b1ad9afdcef3649e3e60a3348491c Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Tue, 22 Nov 2011 23:25:28 +0100 Subject: HID: picolcd: make fb_pending_lock and picolcd_fb_cleanup static Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-picolcd.c b/drivers/hid/hid-picolcd.c index 1782693..530cd30 100644 --- a/drivers/hid/hid-picolcd.c +++ b/drivers/hid/hid-picolcd.c @@ -632,7 +632,7 @@ struct picolcd_fb_cleanup_item { struct picolcd_fb_cleanup_item *next; }; static struct picolcd_fb_cleanup_item *fb_pending; -DEFINE_SPINLOCK(fb_pending_lock); +static DEFINE_SPINLOCK(fb_pending_lock); static void picolcd_fb_do_cleanup(struct work_struct *data) { @@ -657,7 +657,7 @@ static void picolcd_fb_do_cleanup(struct work_struct *data) } while (item); } -DECLARE_WORK(picolcd_fb_cleanup, picolcd_fb_do_cleanup); +static DECLARE_WORK(picolcd_fb_cleanup, picolcd_fb_do_cleanup); static int picolcd_fb_open(struct fb_info *info, int u) { -- cgit v0.10.2 From c196adf87514560f867492978ae350d4bbced0bd Mon Sep 17 00:00:00 2001 From: Willem Penninckx Date: Wed, 23 Nov 2011 11:25:34 +0100 Subject: HID: usbkbd: synchronize LED URB submission usb_kbd_event() and usb_kbd_led() can be called concurrently, but they are not synchronized. They both readwrite kbd->leds, and usb_kbd_event() originally just checked the URB status field, while urb.h states that "It [status field] should not be examined before the URB is returned to the completion handler." To fix this unsynchronized behavior, this patch introduces a boolean representing whether the URB is submitted, and a spinlock. Signed-off-by: Willem Penninckx Signed-off-by: Jiri Kosina diff --git a/drivers/hid/usbhid/usbkbd.c b/drivers/hid/usbhid/usbkbd.c index 0658173..052346f 100644 --- a/drivers/hid/usbhid/usbkbd.c +++ b/drivers/hid/usbhid/usbkbd.c @@ -64,6 +64,32 @@ static const unsigned char usb_kbd_keycode[256] = { 150,158,159,128,136,177,178,176,142,152,173,140 }; + +/** + * struct usb_kbd - state of each attached keyboard + * @dev: input device associated with this keyboard + * @usbdev: usb device associated with this keyboard + * @old: data received in the past from the @irq URB representing which + * keys were pressed. By comparing with the current list of keys + * that are pressed, we are able to see key releases. + * @irq: URB for receiving a list of keys that are pressed when a + * new key is pressed or a key that was pressed is released. + * @led: URB for sending LEDs (e.g. numlock, ...) + * @newleds: data that will be sent with the @led URB representing which LEDs + should be on + * @name: Name of the keyboard. @dev's name field points to this buffer + * @phys: Physical path of the keyboard. @dev's phys field points to this + * buffer + * @new: Buffer for the @irq URB + * @cr: Control request for @led URB + * @leds: Buffer for the @led URB + * @new_dma: DMA address for @irq URB + * @leds_dma: DMA address for @led URB + * @leds_lock: spinlock that protects @leds, @newleds, and @led_urb_submitted + * @led_urb_submitted: indicates whether @led is in progress, i.e. it has been + * submitted and its completion handler has not returned yet + * without resubmitting @led + */ struct usb_kbd { struct input_dev *dev; struct usb_device *usbdev; @@ -78,6 +104,10 @@ struct usb_kbd { unsigned char *leds; dma_addr_t new_dma; dma_addr_t leds_dma; + + spinlock_t leds_lock; + bool led_urb_submitted; + }; static void usb_kbd_irq(struct urb *urb) @@ -136,44 +166,66 @@ resubmit: static int usb_kbd_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { + unsigned long flags; struct usb_kbd *kbd = input_get_drvdata(dev); if (type != EV_LED) return -1; + spin_lock_irqsave(&kbd->leds_lock, flags); kbd->newleds = (!!test_bit(LED_KANA, dev->led) << 3) | (!!test_bit(LED_COMPOSE, dev->led) << 3) | (!!test_bit(LED_SCROLLL, dev->led) << 2) | (!!test_bit(LED_CAPSL, dev->led) << 1) | (!!test_bit(LED_NUML, dev->led)); - if (kbd->led->status == -EINPROGRESS) + if (kbd->led_urb_submitted){ + spin_unlock_irqrestore(&kbd->leds_lock, flags); return 0; + } - if (*(kbd->leds) == kbd->newleds) + if (*(kbd->leds) == kbd->newleds){ + spin_unlock_irqrestore(&kbd->leds_lock, flags); return 0; + } *(kbd->leds) = kbd->newleds; + kbd->led->dev = kbd->usbdev; if (usb_submit_urb(kbd->led, GFP_ATOMIC)) pr_err("usb_submit_urb(leds) failed\n"); - + else + kbd->led_urb_submitted = true; + + spin_unlock_irqrestore(&kbd->leds_lock, flags); + return 0; } static void usb_kbd_led(struct urb *urb) { + unsigned long flags; struct usb_kbd *kbd = urb->context; if (urb->status) hid_warn(urb->dev, "led urb status %d received\n", urb->status); - if (*(kbd->leds) == kbd->newleds) + spin_lock_irqsave(&kbd->leds_lock, flags); + + if (*(kbd->leds) == kbd->newleds){ + kbd->led_urb_submitted = false; + spin_unlock_irqrestore(&kbd->leds_lock, flags); return; + } *(kbd->leds) = kbd->newleds; + kbd->led->dev = kbd->usbdev; - if (usb_submit_urb(kbd->led, GFP_ATOMIC)) + if (usb_submit_urb(kbd->led, GFP_ATOMIC)){ hid_err(urb->dev, "usb_submit_urb(leds) failed\n"); + kbd->led_urb_submitted = false; + } + spin_unlock_irqrestore(&kbd->leds_lock, flags); + } static int usb_kbd_open(struct input_dev *dev) @@ -252,6 +304,7 @@ static int usb_kbd_probe(struct usb_interface *iface, kbd->usbdev = dev; kbd->dev = input_dev; + spin_lock_init(&kbd->leds_lock); if (dev->manufacturer) strlcpy(kbd->name, dev->manufacturer, sizeof(kbd->name)); -- cgit v0.10.2 From a2b2c20ba2f6e22c065f401d63f7f883779cf642 Mon Sep 17 00:00:00 2001 From: Willem Penninckx Date: Wed, 23 Nov 2011 11:26:45 +0100 Subject: HID: usbkbd: kill LED URB on disconnect The LED URB was left unkilled when the USB device is disconnected. Signed-off-by: Willem Penninckx Signed-off-by: Jiri Kosina diff --git a/drivers/hid/usbhid/usbkbd.c b/drivers/hid/usbhid/usbkbd.c index 052346f..a7b925a 100644 --- a/drivers/hid/usbhid/usbkbd.c +++ b/drivers/hid/usbhid/usbkbd.c @@ -387,6 +387,7 @@ static void usb_kbd_disconnect(struct usb_interface *intf) if (kbd) { usb_kill_urb(kbd->irq); input_unregister_device(kbd->dev); + usb_kill_urb(kbd->led); usb_kbd_free_mem(interface_to_usbdev(intf), kbd); kfree(kbd); } -- cgit v0.10.2 From 1fd8f047490dd0ec4e4db710fcbc1bd4798d944c Mon Sep 17 00:00:00 2001 From: Chris Bagwell Date: Wed, 23 Nov 2011 10:54:27 +0100 Subject: HID: hid-multitouch - add another eGalax id This allows ASUS Eee Slate touchscreens to work. Signed-off-by: Chris Bagwell Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 848a56c..f973d33 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1409,6 +1409,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH2) }, { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH3) }, { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH4) }, + { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH5) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084) }, { HID_USB_DEVICE(USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2515) }, { HID_USB_DEVICE(USB_VENDOR_ID_EMS, USB_DEVICE_ID_EMS_TRIO_LINKER_PLUS_II) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 06ce996..7bb5227 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -235,6 +235,7 @@ #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH2 0x72a1 #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH3 0x480e #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH4 0x726b +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH5 0xa001 #define USB_VENDOR_ID_ELECOM 0x056e #define USB_DEVICE_ID_ELECOM_BM084 0x0061 diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index f1c909f..a59d939 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -662,6 +662,9 @@ static const struct hid_device_id mt_devices[] = { { .driver_data = MT_CLS_EGALAX, HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH4) }, + { .driver_data = MT_CLS_EGALAX, + HID_USB_DEVICE(USB_VENDOR_ID_DWAV, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH5) }, /* Elo TouchSystems IntelliTouch Plus panel */ { .driver_data = MT_CLS_DUAL_NSMU_CONTACTID, -- cgit v0.10.2 From eec29e3dab483a5d9a742a6fa68db1ec1f0f7504 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 23 Nov 2011 10:54:28 +0100 Subject: HID: multitouch: create sysfs attribute to control quirks from user-space Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina diff --git a/Documentation/ABI/testing/sysfs-driver-hid-multitouch b/Documentation/ABI/testing/sysfs-driver-hid-multitouch new file mode 100644 index 0000000..f79839d --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-hid-multitouch @@ -0,0 +1,9 @@ +What: /sys/bus/usb/devices/-:./::./quirks +Date: November 2011 +Contact: Benjamin Tissoires +Description: The integer value of this attribute corresponds to the + quirks actually in place to handle the device's protocol. + When read, this attribute returns the current settings (see + MT_QUIRKS_* in hid-multitouch.c). + When written this attribute change on the fly the quirks, then + the protocol to handle the device. diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index a59d939..a51e62e 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -60,9 +60,19 @@ struct mt_slot { bool seen_in_this_frame;/* has this slot been updated */ }; +struct mt_class { + __s32 name; /* MT_CLS */ + __s32 quirks; + __s32 sn_move; /* Signal/noise ratio for move events */ + __s32 sn_width; /* Signal/noise ratio for width events */ + __s32 sn_height; /* Signal/noise ratio for height events */ + __s32 sn_pressure; /* Signal/noise ratio for pressure events */ + __u8 maxcontacts; +}; + struct mt_device { struct mt_slot curdata; /* placeholder of incoming data */ - struct mt_class *mtclass; /* our mt device class */ + struct mt_class mtclass; /* our mt device class */ unsigned last_field_index; /* last field index of the report */ unsigned last_slot_field; /* the last field of a slot */ int last_mt_collection; /* last known mt-related collection */ @@ -74,16 +84,6 @@ struct mt_device { struct mt_slot *slots; }; -struct mt_class { - __s32 name; /* MT_CLS */ - __s32 quirks; - __s32 sn_move; /* Signal/noise ratio for move events */ - __s32 sn_width; /* Signal/noise ratio for width events */ - __s32 sn_height; /* Signal/noise ratio for height events */ - __s32 sn_pressure; /* Signal/noise ratio for pressure events */ - __u8 maxcontacts; -}; - /* classes of device behavior */ #define MT_CLS_DEFAULT 0x0001 @@ -181,6 +181,44 @@ struct mt_class mt_classes[] = { { } }; +static ssize_t mt_show_quirks(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct hid_device *hdev = container_of(dev, struct hid_device, dev); + struct mt_device *td = hid_get_drvdata(hdev); + + return sprintf(buf, "%u\n", td->mtclass.quirks); +} + +static ssize_t mt_set_quirks(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct hid_device *hdev = container_of(dev, struct hid_device, dev); + struct mt_device *td = hid_get_drvdata(hdev); + + unsigned long val; + + if (kstrtoul(buf, 0, &val)) + return -EINVAL; + + td->mtclass.quirks = val; + + return count; +} + +static DEVICE_ATTR(quirks, S_IWUSR | S_IRUGO, mt_show_quirks, mt_set_quirks); + +static struct attribute *sysfs_attrs[] = { + &dev_attr_quirks.attr, + NULL +}; + +static struct attribute_group mt_attribute_group = { + .attrs = sysfs_attrs +}; + static void mt_feature_mapping(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage) { @@ -192,9 +230,9 @@ static void mt_feature_mapping(struct hid_device *hdev, break; case HID_DG_CONTACTMAX: td->maxcontacts = field->value[0]; - if (td->mtclass->maxcontacts) + if (td->mtclass.maxcontacts) /* check if the maxcontacts is given by the class */ - td->maxcontacts = td->mtclass->maxcontacts; + td->maxcontacts = td->mtclass.maxcontacts; break; } @@ -214,7 +252,7 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, unsigned long **bit, int *max) { struct mt_device *td = hid_get_drvdata(hdev); - struct mt_class *cls = td->mtclass; + struct mt_class *cls = &td->mtclass; __s32 quirks = cls->quirks; /* Only map fields from TouchScreen or TouchPad collections. @@ -363,7 +401,7 @@ static int mt_input_mapped(struct hid_device *hdev, struct hid_input *hi, static int mt_compute_slot(struct mt_device *td) { - __s32 quirks = td->mtclass->quirks; + __s32 quirks = td->mtclass.quirks; if (quirks & MT_QUIRK_SLOT_IS_CONTACTID) return td->curdata.contactid; @@ -407,7 +445,7 @@ static void mt_emit_event(struct mt_device *td, struct input_dev *input) for (i = 0; i < td->maxcontacts; ++i) { struct mt_slot *s = &(td->slots[i]); - if ((td->mtclass->quirks & MT_QUIRK_NOT_SEEN_MEANS_UP) && + if ((td->mtclass.quirks & MT_QUIRK_NOT_SEEN_MEANS_UP) && !s->seen_in_this_frame) { s->touch_state = false; } @@ -444,7 +482,7 @@ static int mt_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value) { struct mt_device *td = hid_get_drvdata(hid); - __s32 quirks = td->mtclass->quirks; + __s32 quirks = td->mtclass.quirks; if (hid->claimed & HID_CLAIMED_INPUT && td->slots) { switch (usage->hid) { @@ -552,7 +590,7 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) dev_err(&hdev->dev, "cannot allocate multitouch data\n"); return -ENOMEM; } - td->mtclass = mtclass; + td->mtclass = *mtclass; td->inputmode = -1; td->last_mt_collection = -1; hid_set_drvdata(hdev, td); @@ -574,6 +612,8 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) goto fail; } + ret = sysfs_create_group(&hdev->dev.kobj, &mt_attribute_group); + mt_set_input_mode(hdev); return 0; @@ -594,6 +634,7 @@ static int mt_reset_resume(struct hid_device *hdev) static void mt_remove(struct hid_device *hdev) { struct mt_device *td = hid_get_drvdata(hdev); + sysfs_remove_group(&hdev->dev.kobj, &mt_attribute_group); hid_hw_stop(hdev); kfree(td->slots); kfree(td); -- cgit v0.10.2 From 2261bb9ff0dc38e1d5f35af08f75ec3b37ba6335 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 23 Nov 2011 10:54:29 +0100 Subject: HID: multitouch: cleanup eGalax quirks The previous implementation of eGalax protocol was not satisfying as we had to manually set x/y ranges as they were corrupted after reading the report descriptor. Indeed, the report descriptor provided a stylus input interface which override the correct values. This patch omits this input, thus leaving the correct value untouched, and the MT_QUIRK_EGALAX_XYZ_FIXUP not required anymore. Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index a51e62e..94756d2 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -50,7 +50,6 @@ MODULE_LICENSE("GPL"); #define MT_QUIRK_ALWAYS_VALID (1 << 4) #define MT_QUIRK_VALID_IS_INRANGE (1 << 5) #define MT_QUIRK_VALID_IS_CONFIDENCE (1 << 6) -#define MT_QUIRK_EGALAX_XYZ_FIXUP (1 << 7) #define MT_QUIRK_SLOT_IS_CONTACTID_MINUS_ONE (1 << 8) struct mt_slot { @@ -171,8 +170,7 @@ struct mt_class mt_classes[] = { .maxcontacts = 10 }, { .name = MT_CLS_EGALAX, .quirks = MT_QUIRK_SLOT_IS_CONTACTID | - MT_QUIRK_VALID_IS_INRANGE | - MT_QUIRK_EGALAX_XYZ_FIXUP, + MT_QUIRK_VALID_IS_INRANGE, .maxcontacts = 2, .sn_move = 4096, .sn_pressure = 32, @@ -253,7 +251,6 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, { struct mt_device *td = hid_get_drvdata(hdev); struct mt_class *cls = &td->mtclass; - __s32 quirks = cls->quirks; /* Only map fields from TouchScreen or TouchPad collections. * We need to ignore fields that belong to other collections @@ -265,13 +262,17 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, else return 0; + /* eGalax devices provide a Digitizer.Stylus input which overrides + * the correct Digitizers.Finger X/Y ranges. + * Let's just ignore this input. */ + if (field->physical == HID_DG_STYLUS) + return -1; + switch (usage->hid & HID_USAGE_PAGE) { case HID_UP_GENDESK: switch (usage->hid) { case HID_GD_X: - if (quirks & MT_QUIRK_EGALAX_XYZ_FIXUP) - field->logical_maximum = 32760; hid_map_usage(hi, usage, bit, max, EV_ABS, ABS_MT_POSITION_X); set_abs(hi->input, ABS_MT_POSITION_X, field, @@ -284,8 +285,6 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, } return 1; case HID_GD_Y: - if (quirks & MT_QUIRK_EGALAX_XYZ_FIXUP) - field->logical_maximum = 32760; hid_map_usage(hi, usage, bit, max, EV_ABS, ABS_MT_POSITION_Y); set_abs(hi->input, ABS_MT_POSITION_Y, field, @@ -353,8 +352,6 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, } return 1; case HID_DG_TIPPRESSURE: - if (quirks & MT_QUIRK_EGALAX_XYZ_FIXUP) - field->logical_minimum = 0; hid_map_usage(hi, usage, bit, max, EV_ABS, ABS_MT_PRESSURE); set_abs(hi->input, ABS_MT_PRESSURE, field, -- cgit v0.10.2 From 26a2abe1e1076bf32658fe540eb4cf1c33f481bb Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 23 Nov 2011 10:54:30 +0100 Subject: HID: multitouch: remove .maxcontacts field for eGalax Some eGalax devices are 4 or 5 fingers touches, wereas others are 2. This patch removes the limit in which all eGalax presents 2 touches. Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 94756d2..b3b632e 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -171,7 +171,6 @@ struct mt_class mt_classes[] = { { .name = MT_CLS_EGALAX, .quirks = MT_QUIRK_SLOT_IS_CONTACTID | MT_QUIRK_VALID_IS_INRANGE, - .maxcontacts = 2, .sn_move = 4096, .sn_pressure = 32, }, -- cgit v0.10.2 From e36f690b37945e0a9bb1554e1546eeec93f7d1f6 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 23 Nov 2011 10:54:31 +0100 Subject: HID: multitouch: cleanup with eGalax PID definitions This is just a renaming of USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH{N} to USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_{PID} to handle more eGalax devices. Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index f973d33..20c1955 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1404,12 +1404,12 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_TRUETOUCH) }, { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0006) }, { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0011) }, - { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH) }, - { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH1) }, - { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH2) }, - { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH3) }, - { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH4) }, - { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH5) }, + { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_480D) }, + { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_480E) }, + { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_720C) }, + { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_726B) }, + { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72A1) }, + { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084) }, { HID_USB_DEVICE(USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2515) }, { HID_USB_DEVICE(USB_VENDOR_ID_EMS, USB_DEVICE_ID_EMS_TRIO_LINKER_PLUS_II) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 7bb5227..af95aa5 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -230,12 +230,12 @@ #define USB_VENDOR_ID_DWAV 0x0eef #define USB_DEVICE_ID_EGALAX_TOUCHCONTROLLER 0x0001 -#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH 0x480d -#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH1 0x720c -#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH2 0x72a1 -#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH3 0x480e -#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH4 0x726b -#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH5 0xa001 +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_480D 0x480d +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_480E 0x480e +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_720C 0x720c +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_726B 0x726b +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72A1 0x72a1 +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001 0xa001 #define USB_VENDOR_ID_ELECOM 0x056e #define USB_DEVICE_ID_ELECOM_BM084 0x0061 diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index b3b632e..f318c5c 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -682,26 +682,26 @@ static const struct hid_device_id mt_devices[] = { USB_DEVICE_ID_CYPRESS_TRUETOUCH) }, /* eGalax devices (resistive) */ - { .driver_data = MT_CLS_EGALAX, + { .driver_data = MT_CLS_EGALAX, HID_USB_DEVICE(USB_VENDOR_ID_DWAV, - USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH) }, - { .driver_data = MT_CLS_EGALAX, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_480D) }, + { .driver_data = MT_CLS_EGALAX, HID_USB_DEVICE(USB_VENDOR_ID_DWAV, - USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH3) }, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_480E) }, /* eGalax devices (capacitive) */ - { .driver_data = MT_CLS_EGALAX, + { .driver_data = MT_CLS_EGALAX, HID_USB_DEVICE(USB_VENDOR_ID_DWAV, - USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH1) }, - { .driver_data = MT_CLS_EGALAX, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_720C) }, + { .driver_data = MT_CLS_EGALAX, HID_USB_DEVICE(USB_VENDOR_ID_DWAV, - USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH2) }, - { .driver_data = MT_CLS_EGALAX, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_726B) }, + { .driver_data = MT_CLS_EGALAX, HID_USB_DEVICE(USB_VENDOR_ID_DWAV, - USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH4) }, - { .driver_data = MT_CLS_EGALAX, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72A1) }, + { .driver_data = MT_CLS_EGALAX, HID_USB_DEVICE(USB_VENDOR_ID_DWAV, - USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH5) }, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001) }, /* Elo TouchSystems IntelliTouch Plus panel */ { .driver_data = MT_CLS_DUAL_NSMU_CONTACTID, -- cgit v0.10.2 From bb9ff21072043634f147c05ac65dbf8185d4af6d Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 23 Nov 2011 10:54:32 +0100 Subject: HID: multitouch: Add egalax ID for Acer Iconia W500 This patch adds USB ID for the touchpanel in Acer Iconia W500. The panel supports up to five fingers, therefore the need for a new addition of panel types. Signed-off-by: Marek Vasut Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 20c1955..dd6ec7b 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1409,6 +1409,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_720C) }, { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_726B) }, { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72A1) }, + { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7302) }, { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084) }, { HID_USB_DEVICE(USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2515) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index af95aa5..8a65a01 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -235,6 +235,7 @@ #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_720C 0x720c #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_726B 0x726b #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72A1 0x72a1 +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7302 0x7302 #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001 0xa001 #define USB_VENDOR_ID_ELECOM 0x056e diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index f318c5c..ded16e6 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -701,6 +701,9 @@ static const struct hid_device_id mt_devices[] = { USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72A1) }, { .driver_data = MT_CLS_EGALAX, HID_USB_DEVICE(USB_VENDOR_ID_DWAV, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7302) }, + { .driver_data = MT_CLS_EGALAX, + HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001) }, /* Elo TouchSystems IntelliTouch Plus panel */ -- cgit v0.10.2 From 66f06127f34ad6e8a1b24a2c03144b694d19f99f Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 23 Nov 2011 10:54:33 +0100 Subject: HID: multitouch: add support for the MSI Windpad 110W Just another eGalax device. Please note that adding this device to have_special_driver in hid-core.c is not required anymore. Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 8a65a01..3f5e7d6 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -235,6 +235,7 @@ #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_720C 0x720c #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_726B 0x726b #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72A1 0x72a1 +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72FA 0x72fa #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7302 0x7302 #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001 0xa001 diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index ded16e6..fd2fa59 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -701,6 +701,9 @@ static const struct hid_device_id mt_devices[] = { USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72A1) }, { .driver_data = MT_CLS_EGALAX, HID_USB_DEVICE(USB_VENDOR_ID_DWAV, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72FA) }, + { .driver_data = MT_CLS_EGALAX, + HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7302) }, { .driver_data = MT_CLS_EGALAX, HID_USB_DEVICE(USB_VENDOR_ID_DWAV, -- cgit v0.10.2 From 1b723e8dc81b23141bfb8991e002073b17fd0199 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 23 Nov 2011 10:54:34 +0100 Subject: HID: multitouch: correct eGalax a001 protocol This device use another protocol while sending the events. It's the same as the one described as "serial" by Microsoft. We are keeping here the sn_move and sn_pressure parameters for egalax devices. CC: Cedric Sodhi CC: Chris Bagwell Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index fd2fa59..26e9706 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -97,6 +97,7 @@ struct mt_device { #define MT_CLS_3M 0x0101 #define MT_CLS_CYPRESS 0x0102 #define MT_CLS_EGALAX 0x0103 +#define MT_CLS_EGALAX_SERIAL 0x0104 #define MT_DEFAULT_MAXCONTACT 10 @@ -174,6 +175,12 @@ struct mt_class mt_classes[] = { .sn_move = 4096, .sn_pressure = 32, }, + { .name = MT_CLS_EGALAX_SERIAL, + .quirks = MT_QUIRK_SLOT_IS_CONTACTID | + MT_QUIRK_ALWAYS_VALID, + .sn_move = 4096, + .sn_pressure = 32, + }, { } }; @@ -705,7 +712,7 @@ static const struct hid_device_id mt_devices[] = { { .driver_data = MT_CLS_EGALAX, HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7302) }, - { .driver_data = MT_CLS_EGALAX, + { .driver_data = MT_CLS_EGALAX_SERIAL, HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001) }, -- cgit v0.10.2 From 045b1684bc21575793a099490938d197555eb128 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Fri, 25 Nov 2011 21:28:28 -0800 Subject: Staging: hv: mousevsc: Remove the mouse driver from the staging tree The mouse driver for Hyper-V is a HID compliant mouse driver. Now that all relevant review comments have been addressed, Jiri has agreed to merge this mouse driver into 3.3 hid.git. This patch takes care of removing the mouse driver from the staging area. Greg, if you can ack this patch, Jiri will proceed with his merge. Signed-off-by: K. Y. Srinivasan Acked-by: Greg Kroah-Hartman Signed-off-by: Jiri Kosina diff --git a/drivers/staging/hv/Kconfig b/drivers/staging/hv/Kconfig index 072185e..6c0dc30 100644 --- a/drivers/staging/hv/Kconfig +++ b/drivers/staging/hv/Kconfig @@ -9,9 +9,3 @@ config HYPERV_NET depends on HYPERV && NET help Select this option to enable the Hyper-V virtual network driver. - -config HYPERV_MOUSE - tristate "Microsoft Hyper-V mouse driver" - depends on HYPERV && HID - help - Select this option to enable the Hyper-V mouse driver. diff --git a/drivers/staging/hv/Makefile b/drivers/staging/hv/Makefile index 0f55cee..fbe9a42 100644 --- a/drivers/staging/hv/Makefile +++ b/drivers/staging/hv/Makefile @@ -1,6 +1,5 @@ obj-$(CONFIG_HYPERV_STORAGE) += hv_storvsc.o obj-$(CONFIG_HYPERV_NET) += hv_netvsc.o -obj-$(CONFIG_HYPERV_MOUSE) += hv_mouse.o hv_storvsc-y := storvsc_drv.o hv_netvsc-y := netvsc_drv.o netvsc.o rndis_filter.o diff --git a/drivers/staging/hv/hv_mouse.c b/drivers/staging/hv/hv_mouse.c deleted file mode 100644 index ccd39c7..0000000 --- a/drivers/staging/hv/hv_mouse.c +++ /dev/null @@ -1,599 +0,0 @@ -/* - * Copyright (c) 2009, Citrix Systems, Inc. - * Copyright (c) 2010, Microsoft Corporation. - * Copyright (c) 2011, Novell Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -struct hv_input_dev_info { - unsigned int size; - unsigned short vendor; - unsigned short product; - unsigned short version; - unsigned short reserved[11]; -}; - -/* The maximum size of a synthetic input message. */ -#define SYNTHHID_MAX_INPUT_REPORT_SIZE 16 - -/* - * Current version - * - * History: - * Beta, RC < 2008/1/22 1,0 - * RC > 2008/1/22 2,0 - */ -#define SYNTHHID_INPUT_VERSION_MAJOR 2 -#define SYNTHHID_INPUT_VERSION_MINOR 0 -#define SYNTHHID_INPUT_VERSION (SYNTHHID_INPUT_VERSION_MINOR | \ - (SYNTHHID_INPUT_VERSION_MAJOR << 16)) - - -#pragma pack(push, 1) -/* - * Message types in the synthetic input protocol - */ -enum synthhid_msg_type { - SYNTH_HID_PROTOCOL_REQUEST, - SYNTH_HID_PROTOCOL_RESPONSE, - SYNTH_HID_INITIAL_DEVICE_INFO, - SYNTH_HID_INITIAL_DEVICE_INFO_ACK, - SYNTH_HID_INPUT_REPORT, - SYNTH_HID_MAX -}; - -/* - * Basic message structures. - */ -struct synthhid_msg_hdr { - enum synthhid_msg_type type; - u32 size; -}; - -struct synthhid_msg { - struct synthhid_msg_hdr header; - char data[1]; /* Enclosed message */ -}; - -union synthhid_version { - struct { - u16 minor_version; - u16 major_version; - }; - u32 version; -}; - -/* - * Protocol messages - */ -struct synthhid_protocol_request { - struct synthhid_msg_hdr header; - union synthhid_version version_requested; -}; - -struct synthhid_protocol_response { - struct synthhid_msg_hdr header; - union synthhid_version version_requested; - unsigned char approved; -}; - -struct synthhid_device_info { - struct synthhid_msg_hdr header; - struct hv_input_dev_info hid_dev_info; - struct hid_descriptor hid_descriptor; -}; - -struct synthhid_device_info_ack { - struct synthhid_msg_hdr header; - unsigned char reserved; -}; - -struct synthhid_input_report { - struct synthhid_msg_hdr header; - char buffer[1]; -}; - -#pragma pack(pop) - -#define INPUTVSC_SEND_RING_BUFFER_SIZE (10*PAGE_SIZE) -#define INPUTVSC_RECV_RING_BUFFER_SIZE (10*PAGE_SIZE) - -#define NBITS(x) (((x)/BITS_PER_LONG)+1) - -enum pipe_prot_msg_type { - PIPE_MESSAGE_INVALID, - PIPE_MESSAGE_DATA, - PIPE_MESSAGE_MAXIMUM -}; - - -struct pipe_prt_msg { - enum pipe_prot_msg_type type; - u32 size; - char data[1]; -}; - -struct mousevsc_prt_msg { - enum pipe_prot_msg_type type; - u32 size; - union { - struct synthhid_protocol_request request; - struct synthhid_protocol_response response; - struct synthhid_device_info_ack ack; - }; -}; - -/* - * Represents an mousevsc device - */ -struct mousevsc_dev { - struct hv_device *device; - unsigned char init_complete; - struct mousevsc_prt_msg protocol_req; - struct mousevsc_prt_msg protocol_resp; - /* Synchronize the request/response if needed */ - struct completion wait_event; - int dev_info_status; - - struct hid_descriptor *hid_desc; - unsigned char *report_desc; - u32 report_desc_size; - struct hv_input_dev_info hid_dev_info; - int connected; - struct hid_device *hid_device; -}; - - -static struct mousevsc_dev *alloc_input_device(struct hv_device *device) -{ - struct mousevsc_dev *input_dev; - - input_dev = kzalloc(sizeof(struct mousevsc_dev), GFP_KERNEL); - - if (!input_dev) - return NULL; - - input_dev->device = device; - hv_set_drvdata(device, input_dev); - init_completion(&input_dev->wait_event); - - return input_dev; -} - -static void free_input_device(struct mousevsc_dev *device) -{ - kfree(device->hid_desc); - kfree(device->report_desc); - hv_set_drvdata(device->device, NULL); - kfree(device); -} - - -static void mousevsc_on_receive_device_info(struct mousevsc_dev *input_device, - struct synthhid_device_info *device_info) -{ - int ret = 0; - struct hid_descriptor *desc; - struct mousevsc_prt_msg ack; - - /* Assume success for now */ - input_device->dev_info_status = 0; - - memcpy(&input_device->hid_dev_info, &device_info->hid_dev_info, - sizeof(struct hv_input_dev_info)); - - desc = &device_info->hid_descriptor; - WARN_ON(desc->bLength == 0); - - input_device->hid_desc = kzalloc(desc->bLength, GFP_ATOMIC); - - if (!input_device->hid_desc) - goto cleanup; - - memcpy(input_device->hid_desc, desc, desc->bLength); - - input_device->report_desc_size = desc->desc[0].wDescriptorLength; - if (input_device->report_desc_size == 0) - goto cleanup; - input_device->report_desc = kzalloc(input_device->report_desc_size, - GFP_ATOMIC); - - if (!input_device->report_desc) - goto cleanup; - - memcpy(input_device->report_desc, - ((unsigned char *)desc) + desc->bLength, - desc->desc[0].wDescriptorLength); - - /* Send the ack */ - memset(&ack, 0, sizeof(struct mousevsc_prt_msg)); - - ack.type = PIPE_MESSAGE_DATA; - ack.size = sizeof(struct synthhid_device_info_ack); - - ack.ack.header.type = SYNTH_HID_INITIAL_DEVICE_INFO_ACK; - ack.ack.header.size = 1; - ack.ack.reserved = 0; - - ret = vmbus_sendpacket(input_device->device->channel, - &ack, - sizeof(struct pipe_prt_msg) - sizeof(unsigned char) + - sizeof(struct synthhid_device_info_ack), - (unsigned long)&ack, - VM_PKT_DATA_INBAND, - VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); - if (ret != 0) - goto cleanup; - - complete(&input_device->wait_event); - - return; - -cleanup: - kfree(input_device->hid_desc); - input_device->hid_desc = NULL; - - kfree(input_device->report_desc); - input_device->report_desc = NULL; - - input_device->dev_info_status = -1; - complete(&input_device->wait_event); -} - -static void mousevsc_on_receive(struct hv_device *device, - struct vmpacket_descriptor *packet) -{ - struct pipe_prt_msg *pipe_msg; - struct synthhid_msg *hid_msg; - struct mousevsc_dev *input_dev = hv_get_drvdata(device); - struct synthhid_input_report *input_report; - - pipe_msg = (struct pipe_prt_msg *)((unsigned long)packet + - (packet->offset8 << 3)); - - if (pipe_msg->type != PIPE_MESSAGE_DATA) - return; - - hid_msg = (struct synthhid_msg *)&pipe_msg->data[0]; - - switch (hid_msg->header.type) { - case SYNTH_HID_PROTOCOL_RESPONSE: - memcpy(&input_dev->protocol_resp, pipe_msg, - pipe_msg->size + sizeof(struct pipe_prt_msg) - - sizeof(unsigned char)); - complete(&input_dev->wait_event); - break; - - case SYNTH_HID_INITIAL_DEVICE_INFO: - WARN_ON(pipe_msg->size < sizeof(struct hv_input_dev_info)); - - /* - * Parse out the device info into device attr, - * hid desc and report desc - */ - mousevsc_on_receive_device_info(input_dev, - (struct synthhid_device_info *)&pipe_msg->data[0]); - break; - case SYNTH_HID_INPUT_REPORT: - input_report = - (struct synthhid_input_report *)&pipe_msg->data[0]; - if (!input_dev->init_complete) - break; - hid_input_report(input_dev->hid_device, - HID_INPUT_REPORT, input_report->buffer, - input_report->header.size, 1); - break; - default: - pr_err("unsupported hid msg type - type %d len %d", - hid_msg->header.type, hid_msg->header.size); - break; - } - -} - -static void mousevsc_on_channel_callback(void *context) -{ - const int packetSize = 0x100; - int ret = 0; - struct hv_device *device = (struct hv_device *)context; - - u32 bytes_recvd; - u64 req_id; - unsigned char packet[0x100]; - struct vmpacket_descriptor *desc; - unsigned char *buffer = packet; - int bufferlen = packetSize; - - - do { - ret = vmbus_recvpacket_raw(device->channel, buffer, - bufferlen, &bytes_recvd, &req_id); - - if (ret == 0) { - if (bytes_recvd > 0) { - desc = (struct vmpacket_descriptor *)buffer; - - switch (desc->type) { - case VM_PKT_COMP: - break; - - case VM_PKT_DATA_INBAND: - mousevsc_on_receive( - device, desc); - break; - - default: - pr_err("unhandled packet type %d, tid %llx len %d\n", - desc->type, - req_id, - bytes_recvd); - break; - } - - /* reset */ - if (bufferlen > packetSize) { - kfree(buffer); - - buffer = packet; - bufferlen = packetSize; - } - } else { - if (bufferlen > packetSize) { - kfree(buffer); - - buffer = packet; - bufferlen = packetSize; - } - break; - } - } else if (ret == -ENOBUFS) { - /* Handle large packet */ - bufferlen = bytes_recvd; - buffer = kzalloc(bytes_recvd, GFP_ATOMIC); - - if (buffer == NULL) { - buffer = packet; - bufferlen = packetSize; - break; - } - } - } while (1); - - return; -} - -static int mousevsc_connect_to_vsp(struct hv_device *device) -{ - int ret = 0; - int t; - struct mousevsc_dev *input_dev = hv_get_drvdata(device); - struct mousevsc_prt_msg *request; - struct mousevsc_prt_msg *response; - - - request = &input_dev->protocol_req; - - memset(request, 0, sizeof(struct mousevsc_prt_msg)); - - request->type = PIPE_MESSAGE_DATA; - request->size = sizeof(struct synthhid_protocol_request); - - request->request.header.type = SYNTH_HID_PROTOCOL_REQUEST; - request->request.header.size = sizeof(unsigned int); - request->request.version_requested.version = SYNTHHID_INPUT_VERSION; - - - ret = vmbus_sendpacket(device->channel, request, - sizeof(struct pipe_prt_msg) - - sizeof(unsigned char) + - sizeof(struct synthhid_protocol_request), - (unsigned long)request, - VM_PKT_DATA_INBAND, - VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); - if (ret != 0) - goto cleanup; - - t = wait_for_completion_timeout(&input_dev->wait_event, 5*HZ); - if (t == 0) { - ret = -ETIMEDOUT; - goto cleanup; - } - - response = &input_dev->protocol_resp; - - if (!response->response.approved) { - pr_err("synthhid protocol request failed (version %d)", - SYNTHHID_INPUT_VERSION); - ret = -ENODEV; - goto cleanup; - } - - t = wait_for_completion_timeout(&input_dev->wait_event, 5*HZ); - if (t == 0) { - ret = -ETIMEDOUT; - goto cleanup; - } - - /* - * We should have gotten the device attr, hid desc and report - * desc at this point - */ - if (input_dev->dev_info_status) - ret = -ENOMEM; - -cleanup: - - return ret; -} - -static int mousevsc_hid_open(struct hid_device *hid) -{ - return 0; -} - -static void mousevsc_hid_close(struct hid_device *hid) -{ -} - -static struct hid_ll_driver mousevsc_ll_driver = { - .open = mousevsc_hid_open, - .close = mousevsc_hid_close, -}; - -static struct hid_driver mousevsc_hid_driver; - -static void reportdesc_callback(struct hv_device *dev, void *packet, u32 len) -{ - struct hid_device *hid_dev; - struct mousevsc_dev *input_device = hv_get_drvdata(dev); - - hid_dev = hid_allocate_device(); - if (IS_ERR(hid_dev)) - return; - - hid_dev->ll_driver = &mousevsc_ll_driver; - hid_dev->driver = &mousevsc_hid_driver; - - if (hid_parse_report(hid_dev, packet, len)) - return; - - hid_dev->bus = BUS_VIRTUAL; - hid_dev->vendor = input_device->hid_dev_info.vendor; - hid_dev->product = input_device->hid_dev_info.product; - hid_dev->version = input_device->hid_dev_info.version; - - sprintf(hid_dev->name, "%s", "Microsoft Vmbus HID-compliant Mouse"); - - if (!hidinput_connect(hid_dev, 0)) { - hid_dev->claimed |= HID_CLAIMED_INPUT; - - input_device->connected = 1; - - } - - input_device->hid_device = hid_dev; -} - -static int mousevsc_on_device_add(struct hv_device *device) -{ - int ret = 0; - struct mousevsc_dev *input_dev; - - input_dev = alloc_input_device(device); - - if (!input_dev) - return -ENOMEM; - - input_dev->init_complete = false; - - ret = vmbus_open(device->channel, - INPUTVSC_SEND_RING_BUFFER_SIZE, - INPUTVSC_RECV_RING_BUFFER_SIZE, - NULL, - 0, - mousevsc_on_channel_callback, - device - ); - - if (ret != 0) { - free_input_device(input_dev); - return ret; - } - - - ret = mousevsc_connect_to_vsp(device); - - if (ret != 0) { - vmbus_close(device->channel); - free_input_device(input_dev); - return ret; - } - - - /* workaround SA-167 */ - if (input_dev->report_desc[14] == 0x25) - input_dev->report_desc[14] = 0x29; - - reportdesc_callback(device, input_dev->report_desc, - input_dev->report_desc_size); - - input_dev->init_complete = true; - - return ret; -} - -static int mousevsc_probe(struct hv_device *dev, - const struct hv_vmbus_device_id *dev_id) -{ - - return mousevsc_on_device_add(dev); - -} - -static int mousevsc_remove(struct hv_device *dev) -{ - struct mousevsc_dev *input_dev = hv_get_drvdata(dev); - - vmbus_close(dev->channel); - - if (input_dev->connected) { - hidinput_disconnect(input_dev->hid_device); - input_dev->connected = 0; - hid_destroy_device(input_dev->hid_device); - } - - free_input_device(input_dev); - - return 0; -} - -static const struct hv_vmbus_device_id id_table[] = { - /* Mouse guid */ - { VMBUS_DEVICE(0x9E, 0xB6, 0xA8, 0xCF, 0x4A, 0x5B, 0xc0, 0x4c, - 0xB9, 0x8B, 0x8B, 0xA1, 0xA1, 0xF3, 0xF9, 0x5A) }, - { }, -}; - -MODULE_DEVICE_TABLE(vmbus, id_table); - -static struct hv_driver mousevsc_drv = { - .name = "mousevsc", - .id_table = id_table, - .probe = mousevsc_probe, - .remove = mousevsc_remove, -}; - -static int __init mousevsc_init(void) -{ - return vmbus_driver_register(&mousevsc_drv); -} - -static void __exit mousevsc_exit(void) -{ - vmbus_driver_unregister(&mousevsc_drv); -} - -MODULE_LICENSE("GPL"); -MODULE_VERSION(HV_DRV_VERSION); -module_init(mousevsc_init); -module_exit(mousevsc_exit); -- cgit v0.10.2 From 4f5ca836bef3dd3eb602152d5d712a513998264e Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Wed, 23 Nov 2011 00:49:14 -0800 Subject: HID: hid-input: add support for HID devices reporting Battery Strength Some HID devices, such as my Bluetooth mouse, report their battery strength as an event. Rather than passing it through as a strange absolute input event, this patch registers it with the power_supply subsystem as a battery, so that the device's Battery Strength can be reported to usermode. The battery appears in sysfs names /sys/class/power_supply/hid--battery, and it is a child of the battery-containing device, so it should be clear what it's the battery of. Unfortunately on my current Fedora 16 system, while the battery does appear in the UI, it is listed as a Laptop Battery with 0% charge (since it ignores the "capacity" property of the battery and instead computes it from the "energy*" fields, which we can't supply given the limited information contained within the HID Report). Still, this patch is the first step. Signed-off-by: Jeremy Fitzhardinge Signed-off-by: Jiri Kosina diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 22a4a05..3a97f1f 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -31,6 +31,11 @@ config HID If unsure, say Y. +config HID_BATTERY_STRENGTH + bool + depends on POWER_SUPPLY + default y + config HIDRAW bool "/dev/hidraw raw HID device support" depends on HID diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 6e32526..2d96b78 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -271,6 +271,97 @@ static __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code) return logical_extents / physical_extents; } +#ifdef CONFIG_HID_BATTERY_STRENGTH +static enum power_supply_property hidinput_battery_props[] = { + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_MODEL_NAME, +}; + +static int hidinput_get_battery_property(struct power_supply *psy, + enum power_supply_property prop, + union power_supply_propval *val) +{ + struct hid_device *dev = container_of(psy, struct hid_device, battery); + int ret = 0; + + switch (prop) { + case POWER_SUPPLY_PROP_PRESENT: + case POWER_SUPPLY_PROP_ONLINE: + val->intval = 1; + break; + + case POWER_SUPPLY_PROP_CAPACITY: + if (dev->battery_min < dev->battery_max && + dev->battery_val >= dev->battery_min && + dev->battery_val <= dev->battery_max) + val->intval = (100 * (dev->battery_val - dev->battery_min)) / + (dev->battery_max - dev->battery_min); + else + ret = -EINVAL; + break; + + case POWER_SUPPLY_PROP_MODEL_NAME: + val->strval = dev->name; + break; + + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static void hidinput_setup_battery(struct hid_device *dev, s32 min, s32 max) +{ + struct power_supply *battery = &dev->battery; + int ret; + + if (battery->name != NULL) + return; /* already initialized? */ + + battery->name = kasprintf(GFP_KERNEL, "hid-%s-battery", dev->uniq); + if (battery->name == NULL) + return; + + battery->type = POWER_SUPPLY_TYPE_BATTERY; + battery->properties = hidinput_battery_props; + battery->num_properties = ARRAY_SIZE(hidinput_battery_props); + battery->use_for_apm = 0; + battery->get_property = hidinput_get_battery_property; + + dev->battery_min = min; + dev->battery_max = max; + + ret = power_supply_register(&dev->dev, battery); + if (ret != 0) { + hid_warn(dev, "can't register power supply: %d\n", ret); + kfree(battery->name); + battery->name = NULL; + } +} + +static void hidinput_cleanup_battery(struct hid_device *dev) +{ + if (!dev->battery.name) + return; + + power_supply_unregister(&dev->battery); + kfree(dev->battery.name); + dev->battery.name = NULL; +} +#else /* !CONFIG_HID_BATTERY_STRENGTH */ +static void hidinput_setup_battery(struct hid_device *dev, s32 min, s32 max) +{ +} + +static void hidinput_cleanup_battery(struct hid_device *dev) +{ +} +#endif /* CONFIG_HID_BATTERY_STRENGTH */ + static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_field *field, struct hid_usage *usage) { @@ -629,6 +720,16 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel } break; + case HID_UP_GENDEVCTRLS: + if ((usage->hid & HID_USAGE) == 0x20) { /* Battery Strength */ + hidinput_setup_battery(device, + field->logical_minimum, + field->logical_maximum); + goto ignore; + } else + goto unknown; + break; + case HID_UP_HPVENDOR: /* Reported on a Dutch layout HP5308 */ set_bit(EV_REP, input->evbit); switch (usage->hid & HID_USAGE) { @@ -760,6 +861,13 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct input = field->hidinput->input; + if (usage->hid == HID_DC_BATTERYSTRENGTH) { + hid->battery_val = value; + hid_dbg(hid, "battery value is %d (range %d-%d)\n", + value, hid->battery_min, hid->battery_max); + return; + } + if (!usage->type) return; @@ -1016,6 +1124,8 @@ void hidinput_disconnect(struct hid_device *hid) { struct hid_input *hidinput, *next; + hidinput_cleanup_battery(hid); + list_for_each_entry_safe(hidinput, next, &hid->inputs, list) { list_del(&hidinput->list); input_unregister_device(hidinput->input); diff --git a/include/linux/hid.h b/include/linux/hid.h index deed5f9..7f344c3 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -72,6 +72,7 @@ #include #include #include +#include /* * We parse each description item into this structure. Short items data @@ -190,6 +191,7 @@ struct hid_item { #define HID_UP_UNDEFINED 0x00000000 #define HID_UP_GENDESK 0x00010000 #define HID_UP_SIMULATION 0x00020000 +#define HID_UP_GENDEVCTRLS 0x00060000 #define HID_UP_KEYBOARD 0x00070000 #define HID_UP_LED 0x00080000 #define HID_UP_BUTTON 0x00090000 @@ -239,6 +241,8 @@ struct hid_item { #define HID_GD_RIGHT 0x00010092 #define HID_GD_LEFT 0x00010093 +#define HID_DC_BATTERYSTRENGTH 0x00060020 + #define HID_DG_DIGITIZER 0x000d0001 #define HID_DG_PEN 0x000d0002 #define HID_DG_LIGHTPEN 0x000d0003 @@ -482,6 +486,18 @@ struct hid_device { /* device report descriptor */ struct hid_driver *driver; struct hid_ll_driver *ll_driver; +#ifdef CONFIG_HID_BATTERY_STRENGTH + /* + * Power supply information for HID devices which report + * battery strength. power_supply is registered iff + * battery.name is non-NULL. + */ + struct power_supply battery; + __s32 battery_min; + __s32 battery_max; + __s32 battery_val; +#endif + unsigned int status; /* see STAT flags above */ unsigned claimed; /* Claimed by hidinput, hiddev? */ unsigned quirks; /* Various quirks the device can pull on us */ -- cgit v0.10.2 From 672007957846c3d556165bab635a9c9b855261fa Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Wed, 30 Nov 2011 14:42:04 +0100 Subject: HID: hid-input: fix compile for !HID_BATTERY_STRENGTH As reported by Stephen Rothwell: drivers/hid/hid-input.c: In function 'hidinput_hid_event': drivers/hid/hid-input.c:865:6: error: 'struct hid_device' has no member named 'battery_val' drivers/hid/hid-input.c:866:3: error: 'struct hid_device' has no member named 'battery_min' drivers/hid/hid-input.c:866:3: error: 'struct hid_device' has no member named 'battery_max' Signed-off-by: Jeremy Fitzhardinge Reported-by: Stephen Rothwell Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 2d96b78..b9b8c75 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -861,13 +861,14 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct input = field->hidinput->input; +#ifdef CONFIG_HID_BATTERY_STRENGTH if (usage->hid == HID_DC_BATTERYSTRENGTH) { hid->battery_val = value; hid_dbg(hid, "battery value is %d (range %d-%d)\n", value, hid->battery_min, hid->battery_max); return; } - +#endif if (!usage->type) return; -- cgit v0.10.2 From 5e7ea11f603a0aeb77fd1bff0b242931ffe139de Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 29 Nov 2011 13:13:10 +0100 Subject: HID: multitouch: merge quanta driver into hid-multitouch This patch merge the last old-style hid multitouch driver to the generic one. It also adds 2 more quanta pids. Signed-off-by: Benjamin Tissoires Acked-by: Henrik Rydberg Signed-off-by: Jiri Kosina diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 22a4a05..ee14f78 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -350,6 +350,7 @@ config HID_MULTITOUCH - MosArt dual-touch panels - PenMount dual touch panels - Pixcir dual touch panels + - Quanta panels - eGalax dual-touch panels, including the Joojoo and Wetab tablets - Stantum multitouch panels - Touch International Panels @@ -466,12 +467,6 @@ config HID_PRIMAX Support for Primax devices that are not fully compliant with the HID standard. -config HID_QUANTA - tristate "Quanta Optical Touch panels" - depends on USB_HID - ---help--- - Support for Quanta Optical Touch dual-touch panels. - config HID_ROCCAT tristate "Roccat special event support" depends on USB_HID diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 1e0d2a6..032d59d 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -51,7 +51,6 @@ obj-$(CONFIG_HID_MULTITOUCH) += hid-multitouch.o obj-$(CONFIG_HID_NTRIG) += hid-ntrig.o obj-$(CONFIG_HID_ORTEK) += hid-ortek.o obj-$(CONFIG_HID_PRODIKEYS) += hid-prodikeys.o -obj-$(CONFIG_HID_QUANTA) += hid-quanta.o obj-$(CONFIG_HID_PANTHERLORD) += hid-pl.o obj-$(CONFIG_HID_PETALYNX) += hid-petalynx.o obj-$(CONFIG_HID_PICOLCD) += hid-picolcd.o diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 3f5e7d6..afa1d87 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -584,7 +584,9 @@ #define USB_DEVICE_ID_PRODIGE_CORDLESS 0x3062 #define USB_VENDOR_ID_QUANTA 0x0408 -#define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH 0x3000 +#define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH 0x3000 +#define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3001 0x3001 +#define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3008 0x3008 #define USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN 0x3001 #define USB_VENDOR_ID_ROCCAT 0x1e7d diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 26e9706..3540d32 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -88,10 +88,11 @@ struct mt_device { #define MT_CLS_SERIAL 0x0002 #define MT_CLS_CONFIDENCE 0x0003 -#define MT_CLS_CONFIDENCE_MINUS_ONE 0x0004 -#define MT_CLS_DUAL_INRANGE_CONTACTID 0x0005 -#define MT_CLS_DUAL_INRANGE_CONTACTNUMBER 0x0006 -#define MT_CLS_DUAL_NSMU_CONTACTID 0x0007 +#define MT_CLS_CONFIDENCE_CONTACT_ID 0x0004 +#define MT_CLS_CONFIDENCE_MINUS_ONE 0x0005 +#define MT_CLS_DUAL_INRANGE_CONTACTID 0x0006 +#define MT_CLS_DUAL_INRANGE_CONTACTNUMBER 0x0007 +#define MT_CLS_DUAL_NSMU_CONTACTID 0x0008 /* vendor specific classes */ #define MT_CLS_3M 0x0101 @@ -140,6 +141,9 @@ struct mt_class mt_classes[] = { .quirks = MT_QUIRK_ALWAYS_VALID}, { .name = MT_CLS_CONFIDENCE, .quirks = MT_QUIRK_VALID_IS_CONFIDENCE }, + { .name = MT_CLS_CONFIDENCE_CONTACT_ID, + .quirks = MT_QUIRK_VALID_IS_CONFIDENCE | + MT_QUIRK_SLOT_IS_CONTACTID }, { .name = MT_CLS_CONFIDENCE_MINUS_ONE, .quirks = MT_QUIRK_VALID_IS_CONFIDENCE | MT_QUIRK_SLOT_IS_CONTACTID_MINUS_ONE }, @@ -783,6 +787,17 @@ static const struct hid_device_id mt_devices[] = { HID_USB_DEVICE(USB_VENDOR_ID_CANDO, USB_DEVICE_ID_CANDO_PIXCIR_MULTI_TOUCH) }, + /* Quanta-based panels */ + { .driver_data = MT_CLS_CONFIDENCE_CONTACT_ID, + HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, + USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH) }, + { .driver_data = MT_CLS_CONFIDENCE_CONTACT_ID, + HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, + USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3001) }, + { .driver_data = MT_CLS_CONFIDENCE_CONTACT_ID, + HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, + USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3008) }, + /* Stantum panels */ { .driver_data = MT_CLS_CONFIDENCE, HID_USB_DEVICE(USB_VENDOR_ID_STANTUM, diff --git a/drivers/hid/hid-quanta.c b/drivers/hid/hid-quanta.c deleted file mode 100644 index 87a54df..0000000 --- a/drivers/hid/hid-quanta.c +++ /dev/null @@ -1,261 +0,0 @@ -/* - * HID driver for Quanta Optical Touch dual-touch panels - * - * Copyright (c) 2009-2010 Stephane Chatty - * - */ - -/* - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - */ - -#include -#include -#include -#include - -MODULE_AUTHOR("Stephane Chatty "); -MODULE_DESCRIPTION("Quanta dual-touch panel"); -MODULE_LICENSE("GPL"); - -#include "hid-ids.h" - -struct quanta_data { - __u16 x, y; - __u8 id; - bool valid; /* valid finger data, or just placeholder? */ - bool first; /* is this the first finger in this frame? */ - bool activity_now; /* at least one active finger in this frame? */ - bool activity; /* at least one active finger previously? */ -}; - -static int quanta_input_mapping(struct hid_device *hdev, struct hid_input *hi, - struct hid_field *field, struct hid_usage *usage, - unsigned long **bit, int *max) -{ - switch (usage->hid & HID_USAGE_PAGE) { - - case HID_UP_GENDESK: - switch (usage->hid) { - case HID_GD_X: - hid_map_usage(hi, usage, bit, max, - EV_ABS, ABS_MT_POSITION_X); - /* touchscreen emulation */ - input_set_abs_params(hi->input, ABS_X, - field->logical_minimum, - field->logical_maximum, 0, 0); - return 1; - case HID_GD_Y: - hid_map_usage(hi, usage, bit, max, - EV_ABS, ABS_MT_POSITION_Y); - /* touchscreen emulation */ - input_set_abs_params(hi->input, ABS_Y, - field->logical_minimum, - field->logical_maximum, 0, 0); - return 1; - } - return 0; - - case HID_UP_DIGITIZER: - switch (usage->hid) { - case HID_DG_CONFIDENCE: - case HID_DG_TIPSWITCH: - case HID_DG_INPUTMODE: - case HID_DG_DEVICEINDEX: - case HID_DG_CONTACTCOUNT: - case HID_DG_CONTACTMAX: - case HID_DG_TIPPRESSURE: - case HID_DG_WIDTH: - case HID_DG_HEIGHT: - return -1; - case HID_DG_INRANGE: - /* touchscreen emulation */ - hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH); - return 1; - case HID_DG_CONTACTID: - hid_map_usage(hi, usage, bit, max, - EV_ABS, ABS_MT_TRACKING_ID); - return 1; - } - return 0; - - case 0xff000000: - /* ignore vendor-specific features */ - return -1; - } - - return 0; -} - -static int quanta_input_mapped(struct hid_device *hdev, struct hid_input *hi, - struct hid_field *field, struct hid_usage *usage, - unsigned long **bit, int *max) -{ - if (usage->type == EV_KEY || usage->type == EV_ABS) - clear_bit(usage->code, *bit); - - return 0; -} - -/* - * this function is called when a whole finger has been parsed, - * so that it can decide what to send to the input layer. - */ -static void quanta_filter_event(struct quanta_data *td, struct input_dev *input) -{ - - td->first = !td->first; /* touchscreen emulation */ - - if (!td->valid) { - /* - * touchscreen emulation: if no finger in this frame is valid - * and there previously was finger activity, this is a release - */ - if (!td->first && !td->activity_now && td->activity) { - input_event(input, EV_KEY, BTN_TOUCH, 0); - td->activity = false; - } - return; - } - - input_event(input, EV_ABS, ABS_MT_TRACKING_ID, td->id); - input_event(input, EV_ABS, ABS_MT_POSITION_X, td->x); - input_event(input, EV_ABS, ABS_MT_POSITION_Y, td->y); - - input_mt_sync(input); - td->valid = false; - - /* touchscreen emulation: if first active finger in this frame... */ - if (!td->activity_now) { - /* if there was no previous activity, emit touch event */ - if (!td->activity) { - input_event(input, EV_KEY, BTN_TOUCH, 1); - td->activity = true; - } - td->activity_now = true; - /* and in any case this is our preferred finger */ - input_event(input, EV_ABS, ABS_X, td->x); - input_event(input, EV_ABS, ABS_Y, td->y); - } -} - - -static int quanta_event(struct hid_device *hid, struct hid_field *field, - struct hid_usage *usage, __s32 value) -{ - struct quanta_data *td = hid_get_drvdata(hid); - - if (hid->claimed & HID_CLAIMED_INPUT) { - struct input_dev *input = field->hidinput->input; - - switch (usage->hid) { - case HID_DG_INRANGE: - td->valid = !!value; - break; - case HID_GD_X: - td->x = value; - break; - case HID_GD_Y: - td->y = value; - quanta_filter_event(td, input); - break; - case HID_DG_CONTACTID: - td->id = value; - break; - case HID_DG_CONTACTCOUNT: - /* touch emulation: this is the last field in a frame */ - td->first = false; - td->activity_now = false; - break; - case HID_DG_CONFIDENCE: - case HID_DG_TIPSWITCH: - /* avoid interference from generic hidinput handling */ - break; - - default: - /* fallback to the generic hidinput handling */ - return 0; - } - } - - /* we have handled the hidinput part, now remains hiddev */ - if (hid->claimed & HID_CLAIMED_HIDDEV && hid->hiddev_hid_event) - hid->hiddev_hid_event(hid, field, usage, value); - - return 1; -} - -static int quanta_probe(struct hid_device *hdev, const struct hid_device_id *id) -{ - int ret; - struct quanta_data *td; - - td = kmalloc(sizeof(struct quanta_data), GFP_KERNEL); - if (!td) { - hid_err(hdev, "cannot allocate Quanta Touch data\n"); - return -ENOMEM; - } - td->valid = false; - td->activity = false; - td->activity_now = false; - td->first = false; - hid_set_drvdata(hdev, td); - - ret = hid_parse(hdev); - if (!ret) - ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); - - if (ret) - kfree(td); - - return ret; -} - -static void quanta_remove(struct hid_device *hdev) -{ - hid_hw_stop(hdev); - kfree(hid_get_drvdata(hdev)); - hid_set_drvdata(hdev, NULL); -} - -static const struct hid_device_id quanta_devices[] = { - { HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, - USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH) }, - { HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, - USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN) }, - { } -}; -MODULE_DEVICE_TABLE(hid, quanta_devices); - -static const struct hid_usage_id quanta_grabbed_usages[] = { - { HID_ANY_ID, HID_ANY_ID, HID_ANY_ID }, - { HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1} -}; - -static struct hid_driver quanta_driver = { - .name = "quanta-touch", - .id_table = quanta_devices, - .probe = quanta_probe, - .remove = quanta_remove, - .input_mapping = quanta_input_mapping, - .input_mapped = quanta_input_mapped, - .usage_table = quanta_grabbed_usages, - .event = quanta_event, -}; - -static int __init quanta_init(void) -{ - return hid_register_driver(&quanta_driver); -} - -static void __exit quanta_exit(void) -{ - hid_unregister_driver(&quanta_driver); -} - -module_init(quanta_init); -module_exit(quanta_exit); - -- cgit v0.10.2 From 545803651da8dde248eeb8ce3ed1e547e9e4ac0a Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 29 Nov 2011 13:13:12 +0100 Subject: HID: hid-multitouch: add support for new Hanvon panels Signed-off-by: Benjamin Tissoires Acked-by: Henrik Rydberg Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index dd6ec7b..972f604 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1425,6 +1425,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE_2) }, { HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE_3) }, { HID_USB_DEVICE(USB_VENDOR_ID_HANVON, USB_DEVICE_ID_HANVON_MULTITOUCH) }, + { HID_USB_DEVICE(USB_VENDOR_ID_HANVON_ALT, USB_DEVICE_ID_HANVON_ALT_MULTITOUCH) }, { HID_USB_DEVICE(USB_VENDOR_ID_IDEACOM, USB_DEVICE_ID_IDEACOM_IDC6650) }, { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK, USB_DEVICE_ID_HOLTEK_ON_LINE_GRIP) }, { HID_USB_DEVICE(USB_VENDOR_ID_ILITEK, USB_DEVICE_ID_ILITEK_MULTITOUCH) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index afa1d87..cddb639 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -359,6 +359,9 @@ #define USB_VENDOR_ID_HANVON 0x20b3 #define USB_DEVICE_ID_HANVON_MULTITOUCH 0x0a18 +#define USB_VENDOR_ID_HANVON_ALT 0x22ed +#define USB_DEVICE_ID_HANVON_ALT_MULTITOUCH 0x1010 + #define USB_VENDOR_ID_HAPP 0x078b #define USB_DEVICE_ID_UGCI_DRIVING 0x0010 #define USB_DEVICE_ID_UGCI_FLYING 0x0020 diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 3540d32..de447d7 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -735,6 +735,11 @@ static const struct hid_device_id mt_devices[] = { HID_USB_DEVICE(USB_VENDOR_ID_GOODTOUCH, USB_DEVICE_ID_GOODTOUCH_000f) }, + /* Hanvon panels */ + { .driver_data = MT_CLS_DUAL_INRANGE_CONTACTID, + HID_USB_DEVICE(USB_VENDOR_ID_HANVON_ALT, + USB_DEVICE_ID_HANVON_ALT_MULTITOUCH) }, + /* Ideacom panel */ { .driver_data = MT_CLS_SERIAL, HID_USB_DEVICE(USB_VENDOR_ID_IDEACOM, -- cgit v0.10.2 From 74c4fb058083b47571a4f76dcfce95085f2d8098 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Wed, 30 Nov 2011 08:52:23 -0800 Subject: HID: hv_mouse: Properly add the hid device We need to properly add the hid device to correctly initialize the sysfs state. Signed-off-by: K. Y. Srinivasan Signed-off-by: Haiyang Zhang Reported-by: Fuzhou Chen Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-hyperv.c b/drivers/hid/hid-hyperv.c index d503cbb..0c33ae9 100644 --- a/drivers/hid/hid-hyperv.c +++ b/drivers/hid/hid-hyperv.c @@ -506,6 +506,10 @@ static int mousevsc_probe(struct hv_device *device, sprintf(hid_dev->name, "%s", "Microsoft Vmbus HID-compliant Mouse"); + ret = hid_add_device(hid_dev); + if (ret) + goto probe_err1; + ret = hid_parse_report(hid_dev, input_dev->report_desc, input_dev->report_desc_size); -- cgit v0.10.2 From d41c2a7011dffc60571eab8dc4e2a297ef106f44 Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Thu, 24 Nov 2011 17:46:24 +0100 Subject: HID: roccat: Add support for Isku keyboard This patch adds support for Roccat Isku keyboard. Userland tools can be found at http://sourceforge.net/projects/roccat Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina diff --git a/Documentation/ABI/testing/sysfs-driver-hid-roccat-isku b/Documentation/ABI/testing/sysfs-driver-hid-roccat-isku new file mode 100644 index 0000000..189dc43 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-hid-roccat-isku @@ -0,0 +1,135 @@ +What: /sys/bus/usb/devices/-:./::./isku/roccatisku/actual_profile +Date: June 2011 +Contact: Stefan Achatz +Description: The integer value of this attribute ranges from 0-4. + When read, this attribute returns the number of the actual + profile. This value is persistent, so its equivalent to the + profile that's active when the device is powered on next time. + When written, this file sets the number of the startup profile + and the device activates this profile immediately. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./isku/roccatisku/info +Date: June 2011 +Contact: Stefan Achatz +Description: When read, this file returns general data like firmware version. + The data is 6 bytes long. + This file is readonly. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./isku/roccatisku/key_mask +Date: June 2011 +Contact: Stefan Achatz +Description: When written, this file lets one deactivate certain keys like + windows and application keys, to prevent accidental presses. + Profile number for which this settings occur is included in + written data. The data has to be 6 bytes long. + Before reading this file, control has to be written to select + which profile to read. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./isku/roccatisku/keys_capslock +Date: June 2011 +Contact: Stefan Achatz +Description: When written, this file lets one set the function of the + capslock key for a specific profile. Profile number is included + in written data. The data has to be 6 bytes long. + Before reading this file, control has to be written to select + which profile to read. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./isku/roccatisku/keys_easyzone +Date: June 2011 +Contact: Stefan Achatz +Description: When written, this file lets one set the function of the + easyzone keys for a specific profile. Profile number is included + in written data. The data has to be 65 bytes long. + Before reading this file, control has to be written to select + which profile to read. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./isku/roccatisku/keys_function +Date: June 2011 +Contact: Stefan Achatz +Description: When written, this file lets one set the function of the + function keys for a specific profile. Profile number is included + in written data. The data has to be 41 bytes long. + Before reading this file, control has to be written to select + which profile to read. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./isku/roccatisku/keys_macro +Date: June 2011 +Contact: Stefan Achatz +Description: When written, this file lets one set the function of the macro + keys for a specific profile. Profile number is included in + written data. The data has to be 35 bytes long. + Before reading this file, control has to be written to select + which profile to read. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./isku/roccatisku/keys_media +Date: June 2011 +Contact: Stefan Achatz +Description: When written, this file lets one set the function of the media + keys for a specific profile. Profile number is included in + written data. The data has to be 29 bytes long. + Before reading this file, control has to be written to select + which profile to read. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./isku/roccatisku/keys_thumbster +Date: June 2011 +Contact: Stefan Achatz +Description: When written, this file lets one set the function of the + thumbster keys for a specific profile. Profile number is included + in written data. The data has to be 23 bytes long. + Before reading this file, control has to be written to select + which profile to read. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./isku/roccatisku/last_set +Date: June 2011 +Contact: Stefan Achatz +Description: When written, this file lets one set the time in secs since + epoch in which the last configuration took place. + The data has to be 20 bytes long. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./isku/roccatisku/light +Date: June 2011 +Contact: Stefan Achatz +Description: When written, this file lets one set the backlight intensity for + a specific profile. Profile number is included in written data. + The data has to be 10 bytes long. + Before reading this file, control has to be written to select + which profile to read. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./isku/roccatisku/macro +Date: June 2011 +Contact: Stefan Achatz +Description: When written, this file lets one store macros with max 500 + keystrokes for a specific button for a specific profile. + Button and profile numbers are included in written data. + The data has to be 2083 bytes long. + Before reading this file, control has to be written to select + which profile and key to read. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./isku/roccatisku/control +Date: June 2011 +Contact: Stefan Achatz +Description: When written, this file lets one select which data from which + profile will be read next. The data has to be 3 bytes long. + This file is writeonly. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./isku/roccatisku/talk +Date: June 2011 +Contact: Stefan Achatz +Description: When written, this file lets one trigger easyshift functionality + from the host. + The data has to be 16 bytes long. + This file is writeonly. +Users: http://roccat.sourceforge.net diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 22a4a05..7f11907 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -492,6 +492,13 @@ config HID_ROCCAT_ARVO ---help--- Support for Roccat Arvo keyboard. +config HID_ROCCAT_ISKU + tristate "Roccat Isku keyboard support" + depends on USB_HID + depends on HID_ROCCAT + ---help--- + Support for Roccat Isku keyboard. + config HID_ROCCAT_KONE tristate "Roccat Kone Mouse support" depends on USB_HID diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 1e0d2a6..88660f7f3 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -59,6 +59,7 @@ obj-$(CONFIG_HID_PRIMAX) += hid-primax.o obj-$(CONFIG_HID_ROCCAT) += hid-roccat.o obj-$(CONFIG_HID_ROCCAT_COMMON) += hid-roccat-common.o obj-$(CONFIG_HID_ROCCAT_ARVO) += hid-roccat-arvo.o +obj-$(CONFIG_HID_ROCCAT_ISKU) += hid-roccat-isku.o obj-$(CONFIG_HID_ROCCAT_KONE) += hid-roccat-kone.o obj-$(CONFIG_HID_ROCCAT_KONEPLUS) += hid-roccat-koneplus.o obj-$(CONFIG_HID_ROCCAT_KOVAPLUS) += hid-roccat-kovaplus.o diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index af35384..2f1672a 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1503,6 +1503,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONE) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ARVO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ISKU) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPLUS) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KOVAPLUS) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRED) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 4a441a6..7d2fae1 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -586,6 +586,7 @@ #define USB_VENDOR_ID_ROCCAT 0x1e7d #define USB_DEVICE_ID_ROCCAT_ARVO 0x30d4 +#define USB_DEVICE_ID_ROCCAT_ISKU 0x319c #define USB_DEVICE_ID_ROCCAT_KONE 0x2ced #define USB_DEVICE_ID_ROCCAT_KONEPLUS 0x2d51 #define USB_DEVICE_ID_ROCCAT_KOVAPLUS 0x2d50 diff --git a/drivers/hid/hid-roccat-isku.c b/drivers/hid/hid-roccat-isku.c new file mode 100644 index 0000000..0e4a0ab --- /dev/null +++ b/drivers/hid/hid-roccat-isku.c @@ -0,0 +1,487 @@ +/* + * Roccat Isku driver for Linux + * + * Copyright (c) 2011 Stefan Achatz + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +/* + * Roccat Isku is a gamer keyboard with macro keys that can be configured in + * 5 profiles. + */ + +#include +#include +#include +#include +#include +#include +#include "hid-ids.h" +#include "hid-roccat-common.h" +#include "hid-roccat-isku.h" + +static struct class *isku_class; + +static void isku_profile_activated(struct isku_device *isku, uint new_profile) +{ + isku->actual_profile = new_profile; +} + +static int isku_receive(struct usb_device *usb_dev, uint command, + void *buf, uint size) +{ + return roccat_common_receive(usb_dev, command, buf, size); +} + +static int isku_receive_control_status(struct usb_device *usb_dev) +{ + int retval; + struct isku_control control; + + do { + msleep(50); + retval = isku_receive(usb_dev, ISKU_COMMAND_CONTROL, + &control, sizeof(struct isku_control)); + + if (retval) + return retval; + + switch (control.value) { + case ISKU_CONTROL_VALUE_STATUS_OK: + return 0; + case ISKU_CONTROL_VALUE_STATUS_WAIT: + continue; + case ISKU_CONTROL_VALUE_STATUS_INVALID: + /* seems to be critical - replug necessary */ + case ISKU_CONTROL_VALUE_STATUS_OVERLOAD: + return -EINVAL; + default: + hid_err(usb_dev, "isku_receive_control_status: " + "unknown response value 0x%x\n", + control.value); + return -EINVAL; + } + + } while (1); +} + +static int isku_send(struct usb_device *usb_dev, uint command, + void const *buf, uint size) +{ + int retval; + + retval = roccat_common_send(usb_dev, command, buf, size); + if (retval) + return retval; + + return isku_receive_control_status(usb_dev); +} + +static int isku_get_actual_profile(struct usb_device *usb_dev) +{ + struct isku_actual_profile buf; + int retval; + + retval = isku_receive(usb_dev, ISKU_COMMAND_ACTUAL_PROFILE, + &buf, sizeof(struct isku_actual_profile)); + return retval ? retval : buf.actual_profile; +} + +static int isku_set_actual_profile(struct usb_device *usb_dev, int new_profile) +{ + struct isku_actual_profile buf; + + buf.command = ISKU_COMMAND_ACTUAL_PROFILE; + buf.size = sizeof(struct isku_actual_profile); + buf.actual_profile = new_profile; + return isku_send(usb_dev, ISKU_COMMAND_ACTUAL_PROFILE, &buf, + sizeof(struct isku_actual_profile)); +} + +static ssize_t isku_sysfs_show_actual_profile(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct isku_device *isku = + hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); + return snprintf(buf, PAGE_SIZE, "%d\n", isku->actual_profile); +} + +static ssize_t isku_sysfs_set_actual_profile(struct device *dev, + struct device_attribute *attr, char const *buf, size_t size) +{ + struct isku_device *isku; + struct usb_device *usb_dev; + unsigned long profile; + int retval; + struct isku_roccat_report roccat_report; + + dev = dev->parent->parent; + isku = hid_get_drvdata(dev_get_drvdata(dev)); + usb_dev = interface_to_usbdev(to_usb_interface(dev)); + + retval = strict_strtoul(buf, 10, &profile); + if (retval) + return retval; + + if (profile > 4) + return -EINVAL; + + mutex_lock(&isku->isku_lock); + + retval = isku_set_actual_profile(usb_dev, profile); + if (retval) { + mutex_unlock(&isku->isku_lock); + return retval; + } + + isku_profile_activated(isku, profile); + + roccat_report.event = ISKU_REPORT_BUTTON_EVENT_PROFILE; + roccat_report.data1 = profile + 1; + roccat_report.data2 = 0; + roccat_report.profile = profile + 1; + roccat_report_event(isku->chrdev_minor, (uint8_t const *)&roccat_report); + + mutex_unlock(&isku->isku_lock); + + return size; +} + +static struct device_attribute isku_attributes[] = { + __ATTR(actual_profile, 0660, + isku_sysfs_show_actual_profile, + isku_sysfs_set_actual_profile), + __ATTR_NULL +}; + +static ssize_t isku_sysfs_read(struct file *fp, struct kobject *kobj, + char *buf, loff_t off, size_t count, + size_t real_size, uint command) +{ + struct device *dev = + container_of(kobj, struct device, kobj)->parent->parent; + struct isku_device *isku = hid_get_drvdata(dev_get_drvdata(dev)); + struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); + int retval; + + if (off >= real_size) + return 0; + + if (off != 0 || count != real_size) + return -EINVAL; + + mutex_lock(&isku->isku_lock); + retval = isku_receive(usb_dev, command, buf, real_size); + mutex_unlock(&isku->isku_lock); + + return retval ? retval : real_size; +} + +static ssize_t isku_sysfs_write(struct file *fp, struct kobject *kobj, + void const *buf, loff_t off, size_t count, + size_t real_size, uint command) +{ + struct device *dev = + container_of(kobj, struct device, kobj)->parent->parent; + struct isku_device *isku = hid_get_drvdata(dev_get_drvdata(dev)); + struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); + int retval; + + if (off != 0 || count != real_size) + return -EINVAL; + + mutex_lock(&isku->isku_lock); + retval = isku_send(usb_dev, command, (void *)buf, real_size); + mutex_unlock(&isku->isku_lock); + + return retval ? retval : real_size; +} + +#define ISKU_SYSFS_W(thingy, THINGY) \ +static ssize_t isku_sysfs_write_ ## thingy(struct file *fp, struct kobject *kobj, \ + struct bin_attribute *attr, char *buf, \ + loff_t off, size_t count) \ +{ \ + return isku_sysfs_write(fp, kobj, buf, off, count, \ + sizeof(struct isku_ ## thingy), ISKU_COMMAND_ ## THINGY); \ +} + +#define ISKU_SYSFS_R(thingy, THINGY) \ +static ssize_t isku_sysfs_read_ ## thingy(struct file *fp, struct kobject *kobj, \ + struct bin_attribute *attr, char *buf, \ + loff_t off, size_t count) \ +{ \ + return isku_sysfs_read(fp, kobj, buf, off, count, \ + sizeof(struct isku_ ## thingy), ISKU_COMMAND_ ## THINGY); \ +} + +#define ISKU_SYSFS_RW(thingy, THINGY) \ +ISKU_SYSFS_R(thingy, THINGY) \ +ISKU_SYSFS_W(thingy, THINGY) + +#define ISKU_BIN_ATTR_RW(thingy) \ +{ \ + .attr = { .name = #thingy, .mode = 0660 }, \ + .size = sizeof(struct isku_ ## thingy), \ + .read = isku_sysfs_read_ ## thingy, \ + .write = isku_sysfs_write_ ## thingy \ +} + +#define ISKU_BIN_ATTR_R(thingy) \ +{ \ + .attr = { .name = #thingy, .mode = 0440 }, \ + .size = sizeof(struct isku_ ## thingy), \ + .read = isku_sysfs_read_ ## thingy, \ +} + +#define ISKU_BIN_ATTR_W(thingy) \ +{ \ + .attr = { .name = #thingy, .mode = 0220 }, \ + .size = sizeof(struct isku_ ## thingy), \ + .write = isku_sysfs_write_ ## thingy \ +} + +ISKU_SYSFS_RW(macro, MACRO) +ISKU_SYSFS_RW(keys_function, KEYS_FUNCTION) +ISKU_SYSFS_RW(keys_easyzone, KEYS_EASYZONE) +ISKU_SYSFS_RW(keys_media, KEYS_MEDIA) +ISKU_SYSFS_RW(keys_thumbster, KEYS_THUMBSTER) +ISKU_SYSFS_RW(keys_macro, KEYS_MACRO) +ISKU_SYSFS_RW(keys_capslock, KEYS_CAPSLOCK) +ISKU_SYSFS_RW(light, LIGHT) +ISKU_SYSFS_RW(key_mask, KEY_MASK) +ISKU_SYSFS_RW(last_set, LAST_SET) +ISKU_SYSFS_W(talk, TALK) +ISKU_SYSFS_R(info, INFO) +ISKU_SYSFS_W(control, CONTROL) + +static struct bin_attribute isku_bin_attributes[] = { + ISKU_BIN_ATTR_RW(macro), + ISKU_BIN_ATTR_RW(keys_function), + ISKU_BIN_ATTR_RW(keys_easyzone), + ISKU_BIN_ATTR_RW(keys_media), + ISKU_BIN_ATTR_RW(keys_thumbster), + ISKU_BIN_ATTR_RW(keys_macro), + ISKU_BIN_ATTR_RW(keys_capslock), + ISKU_BIN_ATTR_RW(light), + ISKU_BIN_ATTR_RW(key_mask), + ISKU_BIN_ATTR_RW(last_set), + ISKU_BIN_ATTR_W(talk), + ISKU_BIN_ATTR_R(info), + ISKU_BIN_ATTR_W(control), + __ATTR_NULL +}; + +static int isku_init_isku_device_struct(struct usb_device *usb_dev, + struct isku_device *isku) +{ + int retval; + + mutex_init(&isku->isku_lock); + + retval = isku_get_actual_profile(usb_dev); + if (retval < 0) + return retval; + isku_profile_activated(isku, retval); + + return 0; +} + +static int isku_init_specials(struct hid_device *hdev) +{ + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + struct usb_device *usb_dev = interface_to_usbdev(intf); + struct isku_device *isku; + int retval; + + if (intf->cur_altsetting->desc.bInterfaceProtocol + != ISKU_USB_INTERFACE_PROTOCOL) { + hid_set_drvdata(hdev, NULL); + return 0; + } + + isku = kzalloc(sizeof(*isku), GFP_KERNEL); + if (!isku) { + hid_err(hdev, "can't alloc device descriptor\n"); + return -ENOMEM; + } + hid_set_drvdata(hdev, isku); + + retval = isku_init_isku_device_struct(usb_dev, isku); + if (retval) { + hid_err(hdev, "couldn't init struct isku_device\n"); + goto exit_free; + } + + retval = roccat_connect(isku_class, hdev, + sizeof(struct isku_roccat_report)); + if (retval < 0) { + hid_err(hdev, "couldn't init char dev\n"); + } else { + isku->chrdev_minor = retval; + isku->roccat_claimed = 1; + } + + return 0; +exit_free: + kfree(isku); + return retval; +} + +static void isku_remove_specials(struct hid_device *hdev) +{ + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + struct isku_device *isku; + + if (intf->cur_altsetting->desc.bInterfaceProtocol + != ISKU_USB_INTERFACE_PROTOCOL) + return; + + isku = hid_get_drvdata(hdev); + if (isku->roccat_claimed) + roccat_disconnect(isku->chrdev_minor); + kfree(isku); +} + +static int isku_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + int retval; + + retval = hid_parse(hdev); + if (retval) { + hid_err(hdev, "parse failed\n"); + goto exit; + } + + retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + if (retval) { + hid_err(hdev, "hw start failed\n"); + goto exit; + } + + retval = isku_init_specials(hdev); + if (retval) { + hid_err(hdev, "couldn't install keyboard\n"); + goto exit_stop; + } + + return 0; + +exit_stop: + hid_hw_stop(hdev); +exit: + return retval; +} + +static void isku_remove(struct hid_device *hdev) +{ + isku_remove_specials(hdev); + hid_hw_stop(hdev); +} + +static void isku_keep_values_up_to_date(struct isku_device *isku, + u8 const *data) +{ + struct isku_report_button const *button_report; + + switch (data[0]) { + case ISKU_REPORT_NUMBER_BUTTON: + button_report = (struct isku_report_button const *)data; + switch (button_report->event) { + case ISKU_REPORT_BUTTON_EVENT_PROFILE: + isku_profile_activated(isku, button_report->data1 - 1); + break; + } + break; + } +} + +static void isku_report_to_chrdev(struct isku_device const *isku, + u8 const *data) +{ + struct isku_roccat_report roccat_report; + struct isku_report_button const *button_report; + + if (data[0] != ISKU_REPORT_NUMBER_BUTTON) + return; + + button_report = (struct isku_report_button const *)data; + + roccat_report.event = button_report->event; + roccat_report.data1 = button_report->data1; + roccat_report.data2 = button_report->data2; + roccat_report.profile = isku->actual_profile + 1; + roccat_report_event(isku->chrdev_minor, + (uint8_t const *)&roccat_report); +} + +static int isku_raw_event(struct hid_device *hdev, + struct hid_report *report, u8 *data, int size) +{ + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + struct isku_device *isku = hid_get_drvdata(hdev); + + if (intf->cur_altsetting->desc.bInterfaceProtocol + != ISKU_USB_INTERFACE_PROTOCOL) + return 0; + + if (isku == NULL) + return 0; + + isku_keep_values_up_to_date(isku, data); + + if (isku->roccat_claimed) + isku_report_to_chrdev(isku, data); + + return 0; +} + +static const struct hid_device_id isku_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ISKU) }, + { } +}; + +MODULE_DEVICE_TABLE(hid, isku_devices); + +static struct hid_driver isku_driver = { + .name = "isku", + .id_table = isku_devices, + .probe = isku_probe, + .remove = isku_remove, + .raw_event = isku_raw_event +}; + +static int __init isku_init(void) +{ + int retval; + isku_class = class_create(THIS_MODULE, "isku"); + if (IS_ERR(isku_class)) + return PTR_ERR(isku_class); + isku_class->dev_attrs = isku_attributes; + isku_class->dev_bin_attrs = isku_bin_attributes; + + retval = hid_register_driver(&isku_driver); + if (retval) + class_destroy(isku_class); + return retval; +} + +static void __exit isku_exit(void) +{ + hid_unregister_driver(&isku_driver); + class_destroy(isku_class); +} + +module_init(isku_init); +module_exit(isku_exit); + +MODULE_AUTHOR("Stefan Achatz"); +MODULE_DESCRIPTION("USB Roccat Isku driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/hid/hid-roccat-isku.h b/drivers/hid/hid-roccat-isku.h new file mode 100644 index 0000000..075f6ef --- /dev/null +++ b/drivers/hid/hid-roccat-isku.h @@ -0,0 +1,147 @@ +#ifndef __HID_ROCCAT_ISKU_H +#define __HID_ROCCAT_ISKU_H + +/* + * Copyright (c) 2011 Stefan Achatz + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include + +enum { + ISKU_PROFILE_NUM = 5, + ISKU_USB_INTERFACE_PROTOCOL = 0, +}; + +struct isku_control { + uint8_t command; /* ISKU_COMMAND_CONTROL */ + uint8_t value; + uint8_t request; +} __packed; + +enum isku_control_values { + ISKU_CONTROL_VALUE_STATUS_OVERLOAD = 0, + ISKU_CONTROL_VALUE_STATUS_OK = 1, + ISKU_CONTROL_VALUE_STATUS_INVALID = 2, + ISKU_CONTROL_VALUE_STATUS_WAIT = 3, +}; + +struct isku_actual_profile { + uint8_t command; /* ISKU_COMMAND_ACTUAL_PROFILE */ + uint8_t size; /* always 3 */ + uint8_t actual_profile; +} __packed; + +struct isku_key_mask { + uint8_t command; /* ISKU_COMMAND_KEY_MASK */ + uint8_t size; /* 6 */ + uint8_t profile_number; /* 0-4 */ + uint8_t mask; + uint16_t checksum; +} __packed; + +struct isku_keys_function { + uint8_t data[0x29]; +} __packed; + +struct isku_keys_easyzone { + uint8_t data[0x41]; +} __packed; + +struct isku_keys_media { + uint8_t data[0x1d]; +} __packed; + +struct isku_keys_thumbster { + uint8_t data[0x17]; +} __packed; + +struct isku_keys_macro { + uint8_t data[0x23]; +} __packed; + +struct isku_keys_capslock { + uint8_t data[0x6]; +} __packed; + +struct isku_macro { + uint8_t data[0x823]; +} __packed; + +struct isku_light { + uint8_t data[0xa]; +} __packed; + +struct isku_info { + uint8_t data[2]; + uint8_t firmware_version; + uint8_t unknown[3]; +} __packed; + +struct isku_talk { + uint8_t data[0x10]; +} __packed; + +struct isku_last_set { + uint8_t data[0x14]; +} __packed; + +enum isku_commands { + ISKU_COMMAND_CONTROL = 0x4, + ISKU_COMMAND_ACTUAL_PROFILE = 0x5, + ISKU_COMMAND_KEY_MASK = 0x7, + ISKU_COMMAND_KEYS_FUNCTION = 0x8, + ISKU_COMMAND_KEYS_EASYZONE = 0x9, + ISKU_COMMAND_KEYS_MEDIA = 0xa, + ISKU_COMMAND_KEYS_THUMBSTER = 0xb, + ISKU_COMMAND_KEYS_MACRO = 0xd, + ISKU_COMMAND_MACRO = 0xe, + ISKU_COMMAND_INFO = 0xf, + ISKU_COMMAND_LIGHT = 0x10, + ISKU_COMMAND_KEYS_CAPSLOCK = 0x13, + ISKU_COMMAND_LAST_SET = 0x14, + ISKU_COMMAND_15 = 0x15, + ISKU_COMMAND_TALK = 0x16, + ISKU_COMMAND_FIRMWARE_WRITE = 0x1b, + ISKU_COMMAND_FIRMWARE_WRITE_CONTROL = 0x1c, +}; + +struct isku_report_button { + uint8_t number; /* ISKU_REPORT_NUMBER_BUTTON */ + uint8_t zero; + uint8_t event; + uint8_t data1; + uint8_t data2; +}; + +enum isku_report_numbers { + ISKU_REPORT_NUMBER_BUTTON = 3, +}; + +enum isku_report_button_events { + ISKU_REPORT_BUTTON_EVENT_PROFILE = 0x2, +}; + +struct isku_roccat_report { + uint8_t event; + uint8_t data1; + uint8_t data2; + uint8_t profile; +} __packed; + +struct isku_device { + int roccat_claimed; + int chrdev_minor; + + struct mutex isku_lock; + + int actual_profile; +}; + +#endif -- cgit v0.10.2 From ef6f41157f3864d9bf42671b2ed66062dcafb72e Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Wed, 7 Dec 2011 21:33:59 +0100 Subject: HID: wiimote: Select INPUT_FF_MEMLESS We depend on memless force-feedback support, therefore correctly select the related config options. Reported-by: Randy Dunlap Signed-off-by: David Herrmann Cc: stable@kernel.org Signed-off-by: Jiri Kosina diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 22a4a05..332c22a 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -620,6 +620,7 @@ config HID_WIIMOTE depends on BT_HIDP depends on LEDS_CLASS select POWER_SUPPLY + select INPUT_FF_MEMLESS ---help--- Support for the Nintendo Wii Remote bluetooth device. -- cgit v0.10.2 From 8c3d52fc393b5be8b14dad67053ed83195f0d002 Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Thu, 15 Dec 2011 11:00:38 +0100 Subject: HID: make parser more verbose about parsing errors by default Most of the parsing errors (typically resulting in device not being claimed by HID subsystem at all) are reported only in debugging mode, which makes root-causing problems with buggy devices unnecessarily more difficult. Convert reporting of important HID report descriptor parsing errors to be reported through hid_err() / hid_warn() instead of dbg_hid(). Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index c0ef2b4..ebcc7a2 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -90,7 +90,7 @@ static struct hid_field *hid_register_field(struct hid_report *report, unsigned struct hid_field *field; if (report->maxfield == HID_MAX_FIELDS) { - dbg_hid("too many fields in report\n"); + hid_err(report->device, "too many fields in report\n"); return NULL; } @@ -121,7 +121,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_hid("collection stack overflow\n"); + hid_err(parser->device, "collection stack overflow\n"); return -1; } @@ -129,7 +129,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_hid("failed to reallocate collection array\n"); + hid_err(parser->device, "failed to reallocate collection array\n"); return -1; } memcpy(collection, parser->device->collection, @@ -165,7 +165,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_hid("collection stack underflow\n"); + hid_err(parser->device, "collection stack underflow\n"); return -1; } parser->collection_stack_ptr--; @@ -197,7 +197,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_hid("usage index exceeded\n"); + hid_err(parser->device, "usage index exceeded\n"); return -1; } parser->local.usage[parser->local.usage_index] = usage; @@ -222,12 +222,13 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign report = hid_register_report(parser->device, report_type, parser->global.report_id); if (!report) { - dbg_hid("hid_register_report failed\n"); + hid_err(parser->device, "hid_register_report failed\n"); return -1; } if (parser->global.logical_maximum < parser->global.logical_minimum) { - dbg_hid("logical range invalid %d %d\n", parser->global.logical_minimum, parser->global.logical_maximum); + hid_err(parser->device, "logical range invalid %d %d\n", + parser->global.logical_minimum, parser->global.logical_maximum); return -1; } @@ -307,7 +308,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_hid("global environment stack overflow\n"); + hid_err(parser->device, "global environment stack overflow\n"); return -1; } @@ -318,7 +319,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_hid("global environment stack underflow\n"); + hid_err(parser->device, "global environment stack underflow\n"); return -1; } @@ -363,7 +364,7 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item) case HID_GLOBAL_ITEM_TAG_REPORT_SIZE: parser->global.report_size = item_udata(item); if (parser->global.report_size > 96) { - dbg_hid("invalid report_size %d\n", + hid_err(parser->device, "invalid report_size %d\n", parser->global.report_size); return -1; } @@ -372,7 +373,7 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item) case HID_GLOBAL_ITEM_TAG_REPORT_COUNT: parser->global.report_count = item_udata(item); if (parser->global.report_count > HID_MAX_USAGES) { - dbg_hid("invalid report_count %d\n", + hid_err(parser->device, "invalid report_count %d\n", parser->global.report_count); return -1; } @@ -381,13 +382,13 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item) case HID_GLOBAL_ITEM_TAG_REPORT_ID: parser->global.report_id = item_udata(item); if (parser->global.report_id == 0) { - dbg_hid("report_id 0 is invalid\n"); + hid_err(parser->device, "report_id 0 is invalid\n"); return -1; } return 0; default: - dbg_hid("unknown global tag 0x%x\n", item->tag); + hid_err(parser->device, "unknown global tag 0x%x\n", item->tag); return -1; } } @@ -414,14 +415,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_hid("nested delimiters\n"); + hid_err(parser->device, "nested delimiters\n"); return -1; } parser->local.delimiter_depth++; parser->local.delimiter_branch++; } else { if (parser->local.delimiter_depth < 1) { - dbg_hid("bogus close delimiter\n"); + hid_err(parser->device, "bogus close delimiter\n"); return -1; } parser->local.delimiter_depth--; @@ -506,7 +507,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_hid("unknown main item tag 0x%x\n", item->tag); + hid_err(parser->device, "unknown main item tag 0x%x\n", item->tag); ret = 0; } @@ -678,12 +679,12 @@ int hid_parse_report(struct hid_device *device, __u8 *start, while ((start = fetch_item(start, end, &item)) != NULL) { if (item.format != HID_ITEM_FORMAT_SHORT) { - dbg_hid("unexpected long global item\n"); + hid_err(device, "unexpected long global item\n"); goto err; } if (dispatch_type[item.type](parser, &item)) { - dbg_hid("item %u %u %u %u parsing failed\n", + hid_err(device, "item %u %u %u %u parsing failed\n", item.format, (unsigned)item.size, (unsigned)item.type, (unsigned)item.tag); goto err; @@ -691,11 +692,11 @@ int hid_parse_report(struct hid_device *device, __u8 *start, if (start == end) { if (parser->collection_stack_ptr) { - dbg_hid("unbalanced collection at end of report description\n"); + hid_err(device, "unbalanced collection at end of report description\n"); goto err; } if (parser->local.delimiter_depth) { - dbg_hid("unbalanced delimiter at end of report description\n"); + hid_err(device, "unbalanced delimiter at end of report description\n"); goto err; } vfree(parser); @@ -703,7 +704,7 @@ int hid_parse_report(struct hid_device *device, __u8 *start, } } - dbg_hid("item fetching failed at offset %d\n", (int)(end - start)); + hid_err(device, "item fetching failed at offset %d\n", (int)(end - start)); err: vfree(parser); return ret; @@ -873,7 +874,7 @@ static void hid_process_event(struct hid_device *hid, struct hid_field *field, ret = hdrv->event(hid, field, usage, value); if (ret != 0) { if (ret < 0) - dbg_hid("%s's event failed with %d\n", + hid_err(hid, "%s's event failed with %d\n", hdrv->name, ret); return; } @@ -995,12 +996,13 @@ int hid_set_field(struct hid_field *field, unsigned offset, __s32 value) hid_dump_input(field->report->device, field->usage + offset, value); if (offset >= field->report_count) { - dbg_hid("offset (%d) exceeds report_count (%d)\n", offset, field->report_count); + hid_err(field->report->device, "offset (%d) exceeds report_count (%d)\n", + offset, field->report_count); return -1; } if (field->logical_minimum < 0) { if (value != snto32(s32ton(value, size), size)) { - dbg_hid("value %d is out of range\n", value); + hid_err(field->report->device, "value %d is out of range\n", value); return -1; } } -- cgit v0.10.2 From b7ea95ff9baab144dacdc30d752307938c5ab6bf Mon Sep 17 00:00:00 2001 From: Aaron Tian Date: Thu, 15 Dec 2011 11:09:06 +0800 Subject: HID: multitouch: support PixArt optical touch screen This patch modifies hid-multitouch driver for supporting PixArt optical touch screen. Because of the device does not have to set initial report, we apply "HID_QUIRK_NO_INIT_REPORTS" quirk and add the device into hid_blacklist[] Signed-off-by: Aaron Tian Signed-off-by: Jiri Kosina diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index ee14f78..5d6eb4e 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -349,6 +349,7 @@ config HID_MULTITOUCH - Lumio CrystalTouch panels - MosArt dual-touch panels - PenMount dual touch panels + - PixArt optical touch screen - Pixcir dual touch panels - Quanta panels - eGalax dual-touch panels, including the Joojoo and Wetab tablets diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 972f604..2f8ad34 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1501,6 +1501,9 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_WKB2000) }, { HID_USB_DEVICE(USB_VENDOR_ID_PENMOUNT, USB_DEVICE_ID_PENMOUNT_PCI) }, { HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN) }, + { HID_USB_DEVICE(USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN1) }, + { HID_USB_DEVICE(USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN2) }, { HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_KEYBOARD) }, { HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH) }, { HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index cddb639..96e139f 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -577,6 +577,11 @@ #define USB_VENDOR_ID_PI_ENGINEERING 0x05f3 #define USB_DEVICE_ID_PI_ENGINEERING_VEC_USB_FOOTPEDAL 0xff +#define USB_VENDOR_ID_PIXART 0x093a +#define USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN 0x8001 +#define USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN1 0x8002 +#define USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN2 0x8003 + #define USB_VENDOR_ID_PLAYDOTCOM 0x0b43 #define USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII 0x0003 diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index de447d7..513329d 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -93,6 +93,7 @@ struct mt_device { #define MT_CLS_DUAL_INRANGE_CONTACTID 0x0006 #define MT_CLS_DUAL_INRANGE_CONTACTNUMBER 0x0007 #define MT_CLS_DUAL_NSMU_CONTACTID 0x0008 +#define MT_CLS_INRANGE_CONTACTNUMBER 0x0009 /* vendor specific classes */ #define MT_CLS_3M 0x0101 @@ -159,6 +160,9 @@ struct mt_class mt_classes[] = { .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP | MT_QUIRK_SLOT_IS_CONTACTID, .maxcontacts = 2 }, + { .name = MT_CLS_INRANGE_CONTACTNUMBER, + .quirks = MT_QUIRK_VALID_IS_INRANGE | + MT_QUIRK_SLOT_IS_CONTACTNUMBER }, /* * vendor specific classes @@ -784,6 +788,17 @@ static const struct hid_device_id mt_devices[] = { HID_USB_DEVICE(USB_VENDOR_ID_PENMOUNT, USB_DEVICE_ID_PENMOUNT_PCI) }, + /* PixArt optical touch screen */ + { .driver_data = MT_CLS_INRANGE_CONTACTNUMBER, + HID_USB_DEVICE(USB_VENDOR_ID_PIXART, + USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN) }, + { .driver_data = MT_CLS_INRANGE_CONTACTNUMBER, + HID_USB_DEVICE(USB_VENDOR_ID_PIXART, + USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN1) }, + { .driver_data = MT_CLS_INRANGE_CONTACTNUMBER, + HID_USB_DEVICE(USB_VENDOR_ID_PIXART, + USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN2) }, + /* PixCir-based panels */ { .driver_data = MT_CLS_DUAL_INRANGE_CONTACTID, HID_USB_DEVICE(USB_VENDOR_ID_HANVON, diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 4ea4641..b7962af 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -66,6 +66,9 @@ static const struct hid_blacklist { { USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_AXIS_295, HID_QUIRK_NOGET }, { USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC, HID_QUIRK_NOGET }, { USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET }, + { USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN, HID_QUIRK_NO_INIT_REPORTS }, + { USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN1, HID_QUIRK_NO_INIT_REPORTS }, + { USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN2, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_PRODIGE, USB_DEVICE_ID_PRODIGE_CORDLESS, HID_QUIRK_NOGET }, { USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN, HID_QUIRK_NOGET }, { USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET }, -- cgit v0.10.2 From 7e69ba7caeb00babd40c69fea7f0628c307d7b99 Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Fri, 16 Dec 2011 23:35:55 +0100 Subject: HID: introduce proper dependency of HID_BATTERY on POWER_SUPPLY ppc6xx_defconfig reveals this: drivers/built-in.o: In function `hidinput_cleanup_battery': drivers/hid/hid-input.c:351: undefined reference to`power_supply_unregister' drivers/built-in.o: In function `hidinput_setup_battery': drivers/hid/hid-input.c:338: undefined reference to `power_supply_register' make[1]: *** [.tmp_vmlinux1] Error 1 The defconfig in question doens't mention either option and kbuild is genertaing CONFIG_HID_BATTERY_STRENGTH=y CONFIG_POWER_SUPPLY=m which is wrong. Put a proper dependency in place. Reported-by: Tony Breeds Signed-off-by: Jiri Kosina diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 3a97f1f..7745c3e 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -33,7 +33,7 @@ config HID config HID_BATTERY_STRENGTH bool - depends on POWER_SUPPLY + depends on HID && POWER_SUPPLY && HID == POWER_SUPPLY default y config HIDRAW -- cgit v0.10.2 From d66c266ad40e92a5c24fd0c5bbcf08c04ae41b6c Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Mon, 19 Dec 2011 09:16:29 +0100 Subject: HID: Kconfig: fix syntax Replace mistakenly used '==' by '='. Signed-off-by: Jiri Kosina diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 7745c3e..a459159 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -33,7 +33,7 @@ config HID config HID_BATTERY_STRENGTH bool - depends on HID && POWER_SUPPLY && HID == POWER_SUPPLY + depends on HID && POWER_SUPPLY && HID = POWER_SUPPLY default y config HIDRAW -- cgit v0.10.2 From cd07655e732b265eb12a5804ddf7ffae945c9ab9 Mon Sep 17 00:00:00 2001 From: Ignaz Forster Date: Sat, 17 Dec 2011 13:16:57 +0100 Subject: HID: Enable HID_QUIRK_MULTI_INPUT for Trio Linker Plus II Add quirk for the Trio Linker Plus II - the adapter supports several controllers simultaneously, generating a new HID entry for each connected device. Signed-off-by: Ignaz Forster Signed-off-by: Jiri Kosina diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 5028d60..4d256a7 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -47,6 +47,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_AFATECH, USB_DEVICE_ID_AFATECH_AF9016, HID_QUIRK_FULLSPEED_INTERVAL }, + { USB_VENDOR_ID_EMS, USB_DEVICE_ID_EMS_TRIO_LINKER_PLUS_II, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_ETURBOTOUCH, USB_DEVICE_ID_ETURBOTOUCH, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_GREENASIA, USB_DEVICE_ID_GREENASIA_DUAL_USB_JOYPAD, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_PANTHERLORD, USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK, HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS }, -- cgit v0.10.2 From 05ee28387946ca9936956a6e45f822c0c41dfc87 Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Wed, 21 Dec 2011 00:29:19 +0100 Subject: HID: emsff: use symbolic name instead of hardcoded PID constant Use macro instead of 0x118 PID in device table. Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-emsff.c b/drivers/hid/hid-emsff.c index a5dc13f..4e02ec7 100644 --- a/drivers/hid/hid-emsff.c +++ b/drivers/hid/hid-emsff.c @@ -139,7 +139,7 @@ err: } static const struct hid_device_id ems_devices[] = { - { HID_USB_DEVICE(USB_VENDOR_ID_EMS, 0x118) }, + { HID_USB_DEVICE(USB_VENDOR_ID_EMS, USB_DEVICE_ID_EMS_TRIO_LINKER_PLUS_II) }, { } }; MODULE_DEVICE_TABLE(hid, ems_devices); -- cgit v0.10.2 From ede6a8b239736acd55ad8a219b2bd2ae7f551fb7 Mon Sep 17 00:00:00 2001 From: Daniel Kurtz Date: Thu, 17 Nov 2011 19:23:48 +0800 Subject: HID: usbhid: remove LED_ON LED_ON was defined in the original version of the hid-core autosuspend patch. However, during review, the setting and clearing of it was redone using ledcount. The test was left in accidentally. Signed-off-by: Daniel Kurtz Acked-by: Oliver Neukum Signed-off-by: Jiri Kosina diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index b403fce..6606134 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -1367,16 +1367,6 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message) return -EIO; } - if (!ignoreled && PMSG_IS_AUTO(message)) { - spin_lock_irq(&usbhid->lock); - if (test_bit(HID_LED_ON, &usbhid->iofl)) { - spin_unlock_irq(&usbhid->lock); - usbhid_mark_busy(usbhid); - return -EBUSY; - } - spin_unlock_irq(&usbhid->lock); - } - hid_cancel_delayed_stuff(usbhid); hid_cease_io(usbhid); diff --git a/drivers/hid/usbhid/usbhid.h b/drivers/hid/usbhid/usbhid.h index 1673cac..2d8957c 100644 --- a/drivers/hid/usbhid/usbhid.h +++ b/drivers/hid/usbhid/usbhid.h @@ -55,7 +55,6 @@ struct usb_interface *usbhid_find_interface(int minor); #define HID_STARTED 8 #define HID_REPORTED_IDLE 9 #define HID_KEYS_PRESSED 10 -#define HID_LED_ON 11 /* * USB-specific HID struct, to be pointed to -- cgit v0.10.2 From f0befcd64bc57e6a0b7a96c37c55f79e6b999af7 Mon Sep 17 00:00:00 2001 From: Daniel Kurtz Date: Thu, 17 Nov 2011 19:23:49 +0800 Subject: HID: usbhid: hid-core: submit queued urbs before suspend If any userspace program has opened a keyboard device, the input core de-activates the keyboard's LEDs upon suspend(). It does this by sending individual EV_LED[LED_X]=0 events to the underlying device driver by directly calling the driver's registered event() handler. The usb-hid driver event() handler processes each request by immediately attempting to submit a CTRL URB to turn off the LED. USB URB submission is asynchronous. First the URB is added to the head of the ctrl queue. Then, if the CTRL_RUNNING flag is false, the URB is submitted immediately (and CTRL_RUNNING is set). If the CTRL_RUNNING flag was already true, then the newly queued URB is submitted in the ctrl completion handler when all previously submitted URBs have completed. When all queued URBs have been submitted, the completion handler clears the CTRL_RUNNING flag. In the 2-LED suspend case, at input suspend(), 2 LED event CTRL URBs get queued, with only the first actually submitted. Soon after input suspend() handler finishes, the usb-hid suspend() handler gets called. Since this is NOT a PM_EVENT_AUTO suspend, the handler sets REPORTED_IDLE, then waits for io to complete. Unfortunately, this usually happens while the first LED request is actually still being processed. Thus when the completion handler tries to submit the second LED request it fails, since REPORTED_IDLE is already set! This REPORTED_IDLE check failure causes the completion handler to complete, however without clearing the CTRL_RUNNING flag. This, in turn, means that the suspend() handler's wait_io() condition is never satisfied, and instead it times out after 10 seconds, aborting the original system suspend. This patch changes the behavior to the following: (1) allow completion handler to finish submitting all queued URBs, even if REPORTED_IDLE is set. This guarantees that all URBs queued before the hid-core suspend() call will be submitted before the system is suspended. (2) if REPORTED_IDLE is set and the URB queue is empty, queue, but don't submit, new URB submission requests. These queued requests get submitted when resume() flushes the URB queue. This is similar to the existing behavior, however, any requests that arrive while the queue is not yet empty will still get submitted before suspend. (3) set the RUNNING flag when flushing the URB queue in resume(). This keeps URBs that were queued in (2) from colliding with any new URBs that are being submitted during the resume process. The new URB submission requests upon resume get properly queued behind the ones being flushed instead of the current situation where they collide, causing memory corruption and oopses. Signed-off-by: Daniel Kurtz Acked-by: Oliver Neukum Signed-off-by: Jiri Kosina diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 6606134..719f6b0 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -197,16 +197,24 @@ static int usbhid_restart_out_queue(struct usbhid_device *usbhid) { struct hid_device *hid = usb_get_intfdata(usbhid->intf); int kicked; + int r; if (!hid) return 0; if ((kicked = (usbhid->outhead != usbhid->outtail))) { dbg("Kicking head %d tail %d", usbhid->outhead, usbhid->outtail); + + r = usb_autopm_get_interface_async(usbhid->intf); + if (r < 0) + return r; + /* Asynchronously flush queue. */ + set_bit(HID_OUT_RUNNING, &usbhid->iofl); if (hid_submit_out(hid)) { clear_bit(HID_OUT_RUNNING, &usbhid->iofl); - wake_up(&usbhid->wait); + usb_autopm_put_interface_async(usbhid->intf); } + wake_up(&usbhid->wait); } return kicked; } @@ -215,6 +223,7 @@ static int usbhid_restart_ctrl_queue(struct usbhid_device *usbhid) { struct hid_device *hid = usb_get_intfdata(usbhid->intf); int kicked; + int r; WARN_ON(hid == NULL); if (!hid) @@ -222,10 +231,17 @@ static int usbhid_restart_ctrl_queue(struct usbhid_device *usbhid) if ((kicked = (usbhid->ctrlhead != usbhid->ctrltail))) { dbg("Kicking head %d tail %d", usbhid->ctrlhead, usbhid->ctrltail); + + r = usb_autopm_get_interface_async(usbhid->intf); + if (r < 0) + return r; + /* Asynchronously flush queue. */ + set_bit(HID_CTRL_RUNNING, &usbhid->iofl); if (hid_submit_ctrl(hid)) { clear_bit(HID_CTRL_RUNNING, &usbhid->iofl); - wake_up(&usbhid->wait); + usb_autopm_put_interface_async(usbhid->intf); } + wake_up(&usbhid->wait); } return kicked; } @@ -304,30 +320,21 @@ static int hid_submit_out(struct hid_device *hid) report = usbhid->out[usbhid->outtail].report; raw_report = usbhid->out[usbhid->outtail].raw_report; - r = usb_autopm_get_interface_async(usbhid->intf); - if (r < 0) - return -1; + usbhid->urbout->transfer_buffer_length = ((report->size - 1) >> 3) + + 1 + (report->id > 0); + usbhid->urbout->dev = hid_to_usb_dev(hid); + memcpy(usbhid->outbuf, raw_report, + usbhid->urbout->transfer_buffer_length); + kfree(raw_report); - /* - * if the device hasn't been woken, we leave the output - * to resume() - */ - if (!test_bit(HID_REPORTED_IDLE, &usbhid->iofl)) { - usbhid->urbout->transfer_buffer_length = ((report->size - 1) >> 3) + 1 + (report->id > 0); - usbhid->urbout->dev = hid_to_usb_dev(hid); - memcpy(usbhid->outbuf, raw_report, usbhid->urbout->transfer_buffer_length); - kfree(raw_report); - - dbg_hid("submitting out urb\n"); + dbg_hid("submitting out urb\n"); - if (usb_submit_urb(usbhid->urbout, GFP_ATOMIC)) { - hid_err(hid, "usb_submit_urb(out) failed\n"); - usb_autopm_put_interface_async(usbhid->intf); - return -1; - } - usbhid->last_out = jiffies; + r = usb_submit_urb(usbhid->urbout, GFP_ATOMIC); + if (r < 0) { + hid_err(hid, "usb_submit_urb(out) failed: %d\n", r); + return r; } - + usbhid->last_out = jiffies; return 0; } @@ -343,50 +350,48 @@ static int hid_submit_ctrl(struct hid_device *hid) raw_report = usbhid->ctrl[usbhid->ctrltail].raw_report; dir = usbhid->ctrl[usbhid->ctrltail].dir; - r = usb_autopm_get_interface_async(usbhid->intf); - if (r < 0) - return -1; - if (!test_bit(HID_REPORTED_IDLE, &usbhid->iofl)) { - len = ((report->size - 1) >> 3) + 1 + (report->id > 0); - if (dir == USB_DIR_OUT) { - usbhid->urbctrl->pipe = usb_sndctrlpipe(hid_to_usb_dev(hid), 0); - usbhid->urbctrl->transfer_buffer_length = len; - memcpy(usbhid->ctrlbuf, raw_report, len); - kfree(raw_report); - } else { - int maxpacket, padlen; - - usbhid->urbctrl->pipe = usb_rcvctrlpipe(hid_to_usb_dev(hid), 0); - maxpacket = usb_maxpacket(hid_to_usb_dev(hid), usbhid->urbctrl->pipe, 0); - if (maxpacket > 0) { - padlen = DIV_ROUND_UP(len, maxpacket); - padlen *= maxpacket; - if (padlen > usbhid->bufsize) - padlen = usbhid->bufsize; - } else - padlen = 0; - usbhid->urbctrl->transfer_buffer_length = padlen; - } - usbhid->urbctrl->dev = hid_to_usb_dev(hid); - - usbhid->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE | dir; - usbhid->cr->bRequest = (dir == USB_DIR_OUT) ? HID_REQ_SET_REPORT : HID_REQ_GET_REPORT; - usbhid->cr->wValue = cpu_to_le16(((report->type + 1) << 8) | report->id); - usbhid->cr->wIndex = cpu_to_le16(usbhid->ifnum); - usbhid->cr->wLength = cpu_to_le16(len); - - 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)) { - usb_autopm_put_interface_async(usbhid->intf); - hid_err(hid, "usb_submit_urb(ctrl) failed\n"); - return -1; - } - usbhid->last_ctrl = jiffies; + len = ((report->size - 1) >> 3) + 1 + (report->id > 0); + if (dir == USB_DIR_OUT) { + usbhid->urbctrl->pipe = usb_sndctrlpipe(hid_to_usb_dev(hid), 0); + usbhid->urbctrl->transfer_buffer_length = len; + memcpy(usbhid->ctrlbuf, raw_report, len); + kfree(raw_report); + } else { + int maxpacket, padlen; + + usbhid->urbctrl->pipe = usb_rcvctrlpipe(hid_to_usb_dev(hid), 0); + maxpacket = usb_maxpacket(hid_to_usb_dev(hid), + usbhid->urbctrl->pipe, 0); + if (maxpacket > 0) { + padlen = DIV_ROUND_UP(len, maxpacket); + padlen *= maxpacket; + if (padlen > usbhid->bufsize) + padlen = usbhid->bufsize; + } else + padlen = 0; + usbhid->urbctrl->transfer_buffer_length = padlen; } - + usbhid->urbctrl->dev = hid_to_usb_dev(hid); + + usbhid->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE | dir; + usbhid->cr->bRequest = (dir == USB_DIR_OUT) ? HID_REQ_SET_REPORT : + HID_REQ_GET_REPORT; + usbhid->cr->wValue = cpu_to_le16(((report->type + 1) << 8) | + report->id); + usbhid->cr->wIndex = cpu_to_le16(usbhid->ifnum); + usbhid->cr->wLength = cpu_to_le16(len); + + 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); + + r = usb_submit_urb(usbhid->urbctrl, GFP_ATOMIC); + if (r < 0) { + hid_err(hid, "usb_submit_urb(ctrl) failed: %d\n", r); + return r; + } + usbhid->last_ctrl = jiffies; return 0; } @@ -423,11 +428,8 @@ static void hid_irq_out(struct urb *urb) else usbhid->outtail = (usbhid->outtail + 1) & (HID_OUTPUT_FIFO_SIZE - 1); - if (usbhid->outhead != usbhid->outtail) { - if (hid_submit_out(hid)) { - clear_bit(HID_OUT_RUNNING, &usbhid->iofl); - wake_up(&usbhid->wait); - } + if (usbhid->outhead != usbhid->outtail && !hid_submit_out(hid)) { + /* Successfully submitted next urb in queue */ spin_unlock_irqrestore(&usbhid->lock, flags); return; } @@ -474,13 +476,9 @@ static void hid_ctrl(struct urb *urb) else usbhid->ctrltail = (usbhid->ctrltail + 1) & (HID_CONTROL_FIFO_SIZE - 1); - if (usbhid->ctrlhead != usbhid->ctrltail) { - if (hid_submit_ctrl(hid)) { - clear_bit(HID_CTRL_RUNNING, &usbhid->iofl); - wake_up(&usbhid->wait); - } + if (usbhid->ctrlhead != usbhid->ctrltail && !hid_submit_ctrl(hid)) { + /* Successfully submitted next urb in queue */ spin_unlock(&usbhid->lock); - usb_autopm_put_interface_async(usbhid->intf); return; } @@ -515,9 +513,23 @@ static void __usbhid_submit_report(struct hid_device *hid, struct hid_report *re usbhid->out[usbhid->outhead].report = report; usbhid->outhead = head; + /* Try to awake from autosuspend... */ + if (usb_autopm_get_interface_async(usbhid->intf) < 0) + return; + + /* + * But if still suspended, leave urb enqueued, don't submit. + * Submission will occur if/when resume() drains the queue. + */ + if (test_bit(HID_REPORTED_IDLE, &usbhid->iofl)) + return; + if (!test_and_set_bit(HID_OUT_RUNNING, &usbhid->iofl)) { - if (hid_submit_out(hid)) + if (hid_submit_out(hid)) { clear_bit(HID_OUT_RUNNING, &usbhid->iofl); + usb_autopm_put_interface_async(usbhid->intf); + } + wake_up(&usbhid->wait); } else { /* * the queue is known to run @@ -549,9 +561,23 @@ static void __usbhid_submit_report(struct hid_device *hid, struct hid_report *re usbhid->ctrl[usbhid->ctrlhead].dir = dir; usbhid->ctrlhead = head; + /* Try to awake from autosuspend... */ + if (usb_autopm_get_interface_async(usbhid->intf) < 0) + return; + + /* + * If already suspended, leave urb enqueued, but don't submit. + * Submission will occur if/when resume() drains the queue. + */ + if (test_bit(HID_REPORTED_IDLE, &usbhid->iofl)) + return; + if (!test_and_set_bit(HID_CTRL_RUNNING, &usbhid->iofl)) { - if (hid_submit_ctrl(hid)) + if (hid_submit_ctrl(hid)) { clear_bit(HID_CTRL_RUNNING, &usbhid->iofl); + usb_autopm_put_interface_async(usbhid->intf); + } + wake_up(&usbhid->wait); } else { /* * the queue is known to run -- cgit v0.10.2 From 4371ea8202e98c8ef77ca887de3b19affbb3498f Mon Sep 17 00:00:00 2001 From: Daniel Kurtz Date: Thu, 17 Nov 2011 19:23:50 +0800 Subject: HID: usbhid: defer LED setting to a workqueue Defer LED setting action to a workqueue. This is more likely to send all LED change events in a single URB. Signed-off-by: Daniel Kurtz Acked-by: Oliver Neukum Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index b9b8c75..c6ee632 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -976,6 +976,48 @@ int hidinput_find_field(struct hid_device *hid, unsigned int type, unsigned int } EXPORT_SYMBOL_GPL(hidinput_find_field); +struct hid_field *hidinput_get_led_field(struct hid_device *hid) +{ + struct hid_report *report; + struct hid_field *field; + int i, j; + + list_for_each_entry(report, + &hid->report_enum[HID_OUTPUT_REPORT].report_list, + list) { + for (i = 0; i < report->maxfield; i++) { + field = report->field[i]; + for (j = 0; j < field->maxusage; j++) + if (field->usage[j].type == EV_LED) + return field; + } + } + return NULL; +} +EXPORT_SYMBOL_GPL(hidinput_get_led_field); + +unsigned int hidinput_count_leds(struct hid_device *hid) +{ + struct hid_report *report; + struct hid_field *field; + int i, j; + unsigned int count = 0; + + list_for_each_entry(report, + &hid->report_enum[HID_OUTPUT_REPORT].report_list, + list) { + for (i = 0; i < report->maxfield; i++) { + field = report->field[i]; + for (j = 0; j < field->maxusage; j++) + if (field->usage[j].type == EV_LED && + field->value[j]) + count += 1; + } + } + return count; +} +EXPORT_SYMBOL_GPL(hidinput_count_leds); + static int hidinput_open(struct input_dev *dev) { struct hid_device *hid = input_get_drvdata(dev); diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 719f6b0..5bf91db 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -602,6 +602,30 @@ void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, uns } EXPORT_SYMBOL_GPL(usbhid_submit_report); +/* Workqueue routine to send requests to change LEDs */ +static void hid_led(struct work_struct *work) +{ + struct usbhid_device *usbhid = + container_of(work, struct usbhid_device, led_work); + struct hid_device *hid = usbhid->hid; + struct hid_field *field; + unsigned long flags; + + field = hidinput_get_led_field(hid); + if (!field) { + hid_warn(hid, "LED event field not found\n"); + return; + } + + spin_lock_irqsave(&usbhid->lock, flags); + if (!test_bit(HID_DISCONNECTED, &usbhid->iofl)) { + usbhid->ledcount = hidinput_count_leds(hid); + hid_dbg(usbhid->hid, "New ledcount = %u\n", usbhid->ledcount); + __usbhid_submit_report(hid, field->report, USB_DIR_OUT); + } + spin_unlock_irqrestore(&usbhid->lock, flags); +} + static int usb_hidinput_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { struct hid_device *hid = input_get_drvdata(dev); @@ -621,17 +645,15 @@ static int usb_hidinput_input_event(struct input_dev *dev, unsigned int type, un return -1; } + spin_lock_irqsave(&usbhid->lock, flags); hid_set_field(field, offset, value); - if (value) { - spin_lock_irqsave(&usbhid->lock, flags); - usbhid->ledcount++; - spin_unlock_irqrestore(&usbhid->lock, flags); - } else { - spin_lock_irqsave(&usbhid->lock, flags); - usbhid->ledcount--; - spin_unlock_irqrestore(&usbhid->lock, flags); - } - usbhid_submit_report(hid, field->report, USB_DIR_OUT); + spin_unlock_irqrestore(&usbhid->lock, flags); + + /* + * Defer performing requested LED action. + * This is more likely gather all LED changes into a single URB. + */ + schedule_work(&usbhid->led_work); return 0; } @@ -1126,7 +1148,7 @@ static void usbhid_stop(struct hid_device *hid) return; clear_bit(HID_STARTED, &usbhid->iofl); - spin_lock_irq(&usbhid->lock); /* Sync with error handler */ + spin_lock_irq(&usbhid->lock); /* Sync with error and led handlers */ set_bit(HID_DISCONNECTED, &usbhid->iofl); spin_unlock_irq(&usbhid->lock); usb_kill_urb(usbhid->urbin); @@ -1260,6 +1282,8 @@ static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id * setup_timer(&usbhid->io_retry, hid_retry_timeout, (unsigned long) hid); spin_lock_init(&usbhid->lock); + INIT_WORK(&usbhid->led_work, hid_led); + ret = hid_add_device(hid); if (ret) { if (ret != -ENODEV) @@ -1292,6 +1316,7 @@ static void hid_cancel_delayed_stuff(struct usbhid_device *usbhid) { del_timer_sync(&usbhid->io_retry); cancel_work_sync(&usbhid->reset_work); + cancel_work_sync(&usbhid->led_work); } static void hid_cease_io(struct usbhid_device *usbhid) diff --git a/drivers/hid/usbhid/usbhid.h b/drivers/hid/usbhid/usbhid.h index 2d8957c..cb8f703 100644 --- a/drivers/hid/usbhid/usbhid.h +++ b/drivers/hid/usbhid/usbhid.h @@ -96,6 +96,8 @@ struct usbhid_device { struct work_struct reset_work; /* Task context for resets */ wait_queue_head_t wait; /* For sleeping */ int ledcount; /* counting the number of active leds */ + + struct work_struct led_work; /* Task context for setting LEDs */ }; #define hid_to_usb_dev(hid_dev) \ diff --git a/include/linux/hid.h b/include/linux/hid.h index 7f344c3..999a54c 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -727,6 +727,8 @@ extern void hidinput_disconnect(struct hid_device *); int hid_set_field(struct hid_field *, unsigned, __s32); int hid_input_report(struct hid_device *, int type, u8 *, int, int); int hidinput_find_field(struct hid_device *hid, unsigned int type, unsigned int code, struct hid_field **field); +struct hid_field *hidinput_get_led_field(struct hid_device *hid); +unsigned int hidinput_count_leds(struct hid_device *hid); void hid_output_report(struct hid_report *report, __u8 *data); struct hid_device *hid_allocate_device(void); struct hid_report *hid_register_report(struct hid_device *device, unsigned type, unsigned id); -- cgit v0.10.2 From b105712469d957cf1ab223c1ea72b7ba88edb926 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Fri, 23 Dec 2011 15:40:59 +0100 Subject: HID: multitouch: add support of Atmel multitouch panels Signed-off-by: Benjamin Tissoires Acked-by: Henrik Rydberg Signed-off-by: Jiri Kosina diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 5d6eb4e..e95b07c 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -335,6 +335,7 @@ config HID_MULTITOUCH Say Y here if you have one of the following devices: - 3M PCT touch screens - ActionStar dual touch panels + - Atmel panels - Cando dual touch panels - Chunghwa panels - CVTouch panels diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 96e139f..8705249 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -145,6 +145,9 @@ #define USB_DEVICE_ID_ATEN_4PORTKVM 0x2205 #define USB_DEVICE_ID_ATEN_4PORTKVMC 0x2208 +#define USB_VENDOR_ID_ATMEL 0x03eb +#define USB_DEVICE_ID_ATMEL_MULTITOUCH 0x211c + #define USB_VENDOR_ID_AVERMEDIA 0x07ca #define USB_DEVICE_ID_AVER_FM_MR800 0xb800 diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 513329d..d038864 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -667,6 +667,11 @@ static const struct hid_device_id mt_devices[] = { HID_USB_DEVICE(USB_VENDOR_ID_ACTIONSTAR, USB_DEVICE_ID_ACTIONSTAR_1011) }, + /* Atmel panels */ + { .driver_data = MT_CLS_SERIAL, + HID_USB_DEVICE(USB_VENDOR_ID_ATMEL, + USB_DEVICE_ID_ATMEL_MULTITOUCH) }, + /* Cando panels */ { .driver_data = MT_CLS_DUAL_INRANGE_CONTACTNUMBER, HID_USB_DEVICE(USB_VENDOR_ID_CANDO, -- cgit v0.10.2 From c4fad877cd0efb51d8180ae2eaa791c99c92051c Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Fri, 23 Dec 2011 15:41:00 +0100 Subject: HID: multitouch: add support for 3M 32" Signed-off-by: Benjamin Tissoires Acked-by: Henrik Rydberg Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 8705249..efd2fda 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -21,6 +21,7 @@ #define USB_VENDOR_ID_3M 0x0596 #define USB_DEVICE_ID_3M1968 0x0500 #define USB_DEVICE_ID_3M2256 0x0502 +#define USB_DEVICE_ID_3M3266 0x0506 #define USB_VENDOR_ID_A4TECH 0x09da #define USB_DEVICE_ID_A4TECH_WCP32PU 0x0006 diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index d038864..c3154af 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -661,6 +661,9 @@ static const struct hid_device_id mt_devices[] = { { .driver_data = MT_CLS_3M, HID_USB_DEVICE(USB_VENDOR_ID_3M, USB_DEVICE_ID_3M2256) }, + { .driver_data = MT_CLS_3M, + HID_USB_DEVICE(USB_VENDOR_ID_3M, + USB_DEVICE_ID_3M3266) }, /* ActionStar panels */ { .driver_data = MT_CLS_DEFAULT, -- cgit v0.10.2 From 11576c6114c3b6505aea2e0c988bedb856a0e20c Mon Sep 17 00:00:00 2001 From: Masatoshi Hoshikawa Date: Thu, 5 Jan 2012 11:53:46 +0900 Subject: HID: hid-multitouch: add support 9 new Xiroku devices This patch adds support for the Xiroku Inc. panels (SPX/MPX/CSR/etc.). Signed-off-by: Masatoshi Hoshikawa Signed-off-by: Jiri Kosina diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index e95b07c..ef111b4 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -358,6 +358,7 @@ config HID_MULTITOUCH - Touch International Panels - Unitec Panels - XAT optical touch panels + - Xiroku optical touch panels If unsure, say N. diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 2f8ad34..2b87609 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1555,6 +1555,15 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_MEDIA_TABLET_10_6_INCH) }, { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_MEDIA_TABLET_14_1_INCH) }, { HID_USB_DEVICE(USB_VENDOR_ID_XAT, USB_DEVICE_ID_XAT_CSR) }, + { HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_SPX) }, + { HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_MPX) }, + { HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_CSR) }, + { HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_SPX1) }, + { HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_MPX1) }, + { HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_CSR1) }, + { HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_SPX2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_MPX2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_CSR2) }, { HID_USB_DEVICE(USB_VENDOR_ID_X_TENSIONS, USB_DEVICE_ID_SPEEDLINK_VAD_CEZANNE) }, { HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0005) }, { HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0030) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index efd2fda..f140edf 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -724,6 +724,17 @@ #define USB_VENDOR_ID_XAT 0x2505 #define USB_DEVICE_ID_XAT_CSR 0x0220 +#define USB_VENDOR_ID_XIROKU 0x1477 +#define USB_DEVICE_ID_XIROKU_SPX 0x1006 +#define USB_DEVICE_ID_XIROKU_MPX 0x1007 +#define USB_DEVICE_ID_XIROKU_CSR 0x100e +#define USB_DEVICE_ID_XIROKU_SPX1 0x1021 +#define USB_DEVICE_ID_XIROKU_CSR1 0x1022 +#define USB_DEVICE_ID_XIROKU_MPX1 0x1023 +#define USB_DEVICE_ID_XIROKU_SPX2 0x1024 +#define USB_DEVICE_ID_XIROKU_CSR2 0x1025 +#define USB_DEVICE_ID_XIROKU_MPX2 0x1026 + #define USB_VENDOR_ID_YEALINK 0x6993 #define USB_DEVICE_ID_YEALINK_P1K_P4K_B2K 0xb001 diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index c3154af..5aada12 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -854,6 +854,35 @@ static const struct hid_device_id mt_devices[] = { HID_USB_DEVICE(USB_VENDOR_ID_XAT, USB_DEVICE_ID_XAT_CSR) }, + /* Xiroku */ + { .driver_data = MT_CLS_DEFAULT, + HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, + USB_DEVICE_ID_XIROKU_SPX) }, + { .driver_data = MT_CLS_DEFAULT, + HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, + USB_DEVICE_ID_XIROKU_MPX) }, + { .driver_data = MT_CLS_DEFAULT, + HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, + USB_DEVICE_ID_XIROKU_CSR) }, + { .driver_data = MT_CLS_DEFAULT, + HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, + USB_DEVICE_ID_XIROKU_SPX1) }, + { .driver_data = MT_CLS_DEFAULT, + HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, + USB_DEVICE_ID_XIROKU_MPX1) }, + { .driver_data = MT_CLS_DEFAULT, + HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, + USB_DEVICE_ID_XIROKU_CSR1) }, + { .driver_data = MT_CLS_DEFAULT, + HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, + USB_DEVICE_ID_XIROKU_SPX2) }, + { .driver_data = MT_CLS_DEFAULT, + HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, + USB_DEVICE_ID_XIROKU_MPX2) }, + { .driver_data = MT_CLS_DEFAULT, + HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, + USB_DEVICE_ID_XIROKU_CSR2) }, + { } }; MODULE_DEVICE_TABLE(hid, mt_devices); -- cgit v0.10.2 From c5a92aa3eb7425da68797a820d208edad36551f7 Mon Sep 17 00:00:00 2001 From: Daniel Nicoletti Date: Fri, 2 Dec 2011 03:52:22 -0200 Subject: hid-input: add support for HID devices reporting Battery Strength MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I've sent an email earlier asking for help with a GetFeature code, and now I have a second patch on top of Jeremy's to provide the battery functionality for devices that support reporting it. If I understood correctly when talking to Jeremy he said his device never actually reported the status as an input event (sorry if I didn't understand it correctly), and after reading HID specs I believe it's really because it was meant to be probed, I have an Apple Keyboard and Magic Trackpad both bluetooth batteries operated, so using PacketLogger I saw that Mac OSX always ask the battery status using the so called GetFeature. What my patch does is basically: - store the report id that matches the battery_strength - setup the battery if 0x6.0x20 is found, even if that is reported as a feature (as it was meant to be but only the MagicTrackpad does) - when upower or someone access /sys/class/power_supply/hid-*/capacity it will probe the device and return it's status. It works great for both devices, but I have two concerns: - the report_features function has a duplicated code - it would be nice if it was possible for specific drivers to provide their own probe as there might be some strange devices... (but maybe it's already possible) I've talked to the upower dev and he fixed it to be able to show the right percentage. Here how the uevent file (in /sys/class/power_supply/hid-*/) looks like: POWER_SUPPLY_NAME=hid-00:22:41:D9:18:E7-battery POWER_SUPPLY_PRESENT=1 POWER_SUPPLY_ONLINE=1 POWER_SUPPLY_CAPACITY=66 POWER_SUPPLY_MODEL_NAME=MacAdmin’s keyboard POWER_SUPPLY_STATUS=Discharging POWER_SUPPLY_NAME=hid-70:CD:60:F5:FF:3F-battery POWER_SUPPLY_PRESENT=1 POWER_SUPPLY_ONLINE=1 POWER_SUPPLY_CAPACITY=62 POWER_SUPPLY_MODEL_NAME=nexx’s Trackpad POWER_SUPPLY_STATUS=Discharging Signed-off-by: Daniel Nicoletti diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index b9b8c75..8fac47c 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -277,6 +277,7 @@ static enum power_supply_property hidinput_battery_props[] = { POWER_SUPPLY_PROP_ONLINE, POWER_SUPPLY_PROP_CAPACITY, POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_STATUS }; static int hidinput_get_battery_property(struct power_supply *psy, @@ -285,6 +286,9 @@ static int hidinput_get_battery_property(struct power_supply *psy, { struct hid_device *dev = container_of(psy, struct hid_device, battery); int ret = 0; + int ret_rep; + __u8 *buf = NULL; + unsigned char report_number = dev->battery_report_id; switch (prop) { case POWER_SUPPLY_PROP_PRESENT: @@ -293,28 +297,45 @@ static int hidinput_get_battery_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_CAPACITY: - if (dev->battery_min < dev->battery_max && - dev->battery_val >= dev->battery_min && - dev->battery_val <= dev->battery_max) - val->intval = (100 * (dev->battery_val - dev->battery_min)) / - (dev->battery_max - dev->battery_min); - else + buf = kmalloc(2 * sizeof(__u8), GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + break; + } + + memset(buf, 0, sizeof(buf)); + ret_rep = dev->hid_get_raw_report(dev, report_number, buf, sizeof(buf), HID_FEATURE_REPORT); + if (ret_rep != 2) { ret = -EINVAL; + break; + } + + /* store the returned value */ + /* I'm not calculating this using the logical_minimum and maximum */ + /* because my device returns 0-100 even though the min and max are 0-255 */ + val->intval = buf[1]; break; case POWER_SUPPLY_PROP_MODEL_NAME: val->strval = dev->name; break; + case POWER_SUPPLY_PROP_STATUS: + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + break; + default: ret = -EINVAL; break; } + if (buf) { + kfree(buf); + } return ret; } -static void hidinput_setup_battery(struct hid_device *dev, s32 min, s32 max) +static void hidinput_setup_battery(struct hid_device *dev, unsigned id, s32 min, s32 max) { struct power_supply *battery = &dev->battery; int ret; @@ -326,7 +347,7 @@ static void hidinput_setup_battery(struct hid_device *dev, s32 min, s32 max) if (battery->name == NULL) return; - battery->type = POWER_SUPPLY_TYPE_BATTERY; + battery->type = POWER_SUPPLY_TYPE_USB; battery->properties = hidinput_battery_props; battery->num_properties = ARRAY_SIZE(hidinput_battery_props); battery->use_for_apm = 0; @@ -334,6 +355,7 @@ static void hidinput_setup_battery(struct hid_device *dev, s32 min, s32 max) dev->battery_min = min; dev->battery_max = max; + dev->battery_report_id = id; ret = power_supply_register(&dev->dev, battery); if (ret != 0) { @@ -353,7 +375,7 @@ static void hidinput_cleanup_battery(struct hid_device *dev) dev->battery.name = NULL; } #else /* !CONFIG_HID_BATTERY_STRENGTH */ -static void hidinput_setup_battery(struct hid_device *dev, s32 min, s32 max) +static void hidinput_setup_battery(struct hid_device *dev, unsigned id, s32 min, s32 max) { } @@ -723,6 +745,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case HID_UP_GENDEVCTRLS: if ((usage->hid & HID_USAGE) == 0x20) { /* Battery Strength */ hidinput_setup_battery(device, + field->report->id, field->logical_minimum, field->logical_maximum); goto ignore; @@ -997,15 +1020,23 @@ static void report_features(struct hid_device *hid) struct hid_report *rep; int i, j; - if (!drv->feature_mapping) - return; - rep_enum = &hid->report_enum[HID_FEATURE_REPORT]; list_for_each_entry(rep, &rep_enum->report_list, list) for (i = 0; i < rep->maxfield; i++) - for (j = 0; j < rep->field[i]->maxusage; j++) - drv->feature_mapping(hid, rep->field[i], - rep->field[i]->usage + j); + for (j = 0; j < rep->field[i]->maxusage; j++) { + /* Verify if Battery Strength feature is available */ + if (((rep->field[i]->usage + j)->hid & HID_USAGE_PAGE) == HID_UP_GENDEVCTRLS && + ((rep->field[i]->usage + j)->hid & HID_USAGE) == 0x20) { + hidinput_setup_battery(hid, + rep->id, + rep->field[i]->logical_minimum, + rep->field[i]->logical_maximum); + } + + if (drv->feature_mapping) + drv->feature_mapping(hid, rep->field[i], + rep->field[i]->usage + j); + } } /* diff --git a/include/linux/hid.h b/include/linux/hid.h index 7f344c3..b5df198 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -496,6 +496,7 @@ struct hid_device { /* device report descriptor */ __s32 battery_min; __s32 battery_max; __s32 battery_val; + __s32 battery_report_id; #endif unsigned int status; /* see STAT flags above */ -- cgit v0.10.2 From ef5251993c3597a88b893fe22ee830092dff35f9 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Fri, 2 Dec 2011 10:04:04 -0800 Subject: hid-input/battery: remove apparently redundant kmalloc Signed-off-by: Jeremy Fitzhardinge diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 8fac47c..b108ce7 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -287,7 +287,7 @@ static int hidinput_get_battery_property(struct power_supply *psy, struct hid_device *dev = container_of(psy, struct hid_device, battery); int ret = 0; int ret_rep; - __u8 *buf = NULL; + __u8 buf[2] = {}; unsigned char report_number = dev->battery_report_id; switch (prop) { @@ -297,14 +297,8 @@ static int hidinput_get_battery_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_CAPACITY: - buf = kmalloc(2 * sizeof(__u8), GFP_KERNEL); - if (!buf) { - ret = -ENOMEM; - break; - } - - memset(buf, 0, sizeof(buf)); - ret_rep = dev->hid_get_raw_report(dev, report_number, buf, sizeof(buf), HID_FEATURE_REPORT); + ret_rep = dev->hid_get_raw_report(dev, report_number, + buf, sizeof(buf), HID_FEATURE_REPORT); if (ret_rep != 2) { ret = -EINVAL; break; @@ -329,9 +323,6 @@ static int hidinput_get_battery_property(struct power_supply *psy, break; } - if (buf) { - kfree(buf); - } return ret; } -- cgit v0.10.2 From bbc21cfd55858d7c3e55bfaa91fa934b0b13ad4d Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Fri, 2 Dec 2011 11:12:36 -0800 Subject: hid-input/battery: add quirks for battery Some devices always report percentage, despite having 0/255 as their min/max, so add a quirk for them. Signed-off-by: Jeremy Fitzhardinge diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index c0ef2b4..aa4a30b 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1157,7 +1157,7 @@ static bool hid_match_one_id(struct hid_device *hdev, (id->product == HID_ANY_ID || id->product == hdev->product); } -static const struct hid_device_id *hid_match_id(struct hid_device *hdev, +const struct hid_device_id *hid_match_id(struct hid_device *hdev, const struct hid_device_id *id) { for (; id->bus; id++) diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index b108ce7..69dec47 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -32,6 +32,8 @@ #include #include +#include "hid-ids.h" + #define unk KEY_UNKNOWN static const unsigned char hid_keyboard[256] = { @@ -280,6 +282,28 @@ static enum power_supply_property hidinput_battery_props[] = { POWER_SUPPLY_PROP_STATUS }; +#define HID_BATTERY_QUIRK_PERCENT (1 << 0) /* always reports percent */ + +static const struct hid_device_id hid_battery_quirks[] = { + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICMOUSE), + HID_BATTERY_QUIRK_PERCENT }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICTRACKPAD), + HID_BATTERY_QUIRK_PERCENT }, + {} +}; + +static unsigned find_battery_quirk(struct hid_device *hdev) +{ + unsigned quirks = 0; + const struct hid_device_id *match; + + match = hid_match_id(hdev, hid_battery_quirks); + if (match != NULL) + quirks = match->driver_data; + + return quirks; +} + static int hidinput_get_battery_property(struct power_supply *psy, enum power_supply_property prop, union power_supply_propval *val) @@ -304,10 +328,11 @@ static int hidinput_get_battery_property(struct power_supply *psy, break; } - /* store the returned value */ - /* I'm not calculating this using the logical_minimum and maximum */ - /* because my device returns 0-100 even though the min and max are 0-255 */ - val->intval = buf[1]; + if (dev->battery_min < dev->battery_max && + buf[1] >= dev->battery_min && + buf[1] <= dev->battery_max) + val->intval = (100 * (buf[1] - dev->battery_min)) / + (dev->battery_max - dev->battery_min); break; case POWER_SUPPLY_PROP_MODEL_NAME: @@ -330,6 +355,7 @@ static void hidinput_setup_battery(struct hid_device *dev, unsigned id, s32 min, { struct power_supply *battery = &dev->battery; int ret; + unsigned quirks; if (battery->name != NULL) return; /* already initialized? */ @@ -344,6 +370,13 @@ static void hidinput_setup_battery(struct hid_device *dev, unsigned id, s32 min, battery->use_for_apm = 0; battery->get_property = hidinput_get_battery_property; + quirks = find_battery_quirk(dev); + + if (quirks & HID_BATTERY_QUIRK_PERCENT) { + min = 0; + max = 100; + } + dev->battery_min = min; dev->battery_max = max; dev->battery_report_id = id; diff --git a/include/linux/hid.h b/include/linux/hid.h index b5df198..fa772c8 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -735,6 +735,8 @@ int hid_parse_report(struct hid_device *hid, __u8 *start, unsigned size); int hid_check_keys_pressed(struct hid_device *hid); int hid_connect(struct hid_device *hid, unsigned int connect_mask); void hid_disconnect(struct hid_device *hid); +const struct hid_device_id *hid_match_id(struct hid_device *hdev, + const struct hid_device_id *id); /** * hid_map_usage - map usage input bits -- cgit v0.10.2 From fb8ac91b4dccbdda0ad51d499079d05143783ba4 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Fri, 2 Dec 2011 11:18:45 -0800 Subject: hid-input/battery: deal with both FEATURE and INPUT report batteries Some devices seem to report batteries as FEATUREs, others as INPUTs. Signed-off-by: Jeremy Fitzhardinge diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 69dec47..f5c3efc 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -312,7 +312,6 @@ static int hidinput_get_battery_property(struct power_supply *psy, int ret = 0; int ret_rep; __u8 buf[2] = {}; - unsigned char report_number = dev->battery_report_id; switch (prop) { case POWER_SUPPLY_PROP_PRESENT: @@ -321,8 +320,9 @@ static int hidinput_get_battery_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_CAPACITY: - ret_rep = dev->hid_get_raw_report(dev, report_number, - buf, sizeof(buf), HID_FEATURE_REPORT); + ret_rep = dev->hid_get_raw_report(dev, dev->battery_report_id, + buf, sizeof(buf), + dev->battery_report_type); if (ret_rep != 2) { ret = -EINVAL; break; @@ -351,7 +351,9 @@ static int hidinput_get_battery_property(struct power_supply *psy, return ret; } -static void hidinput_setup_battery(struct hid_device *dev, unsigned id, s32 min, s32 max) +static void hidinput_setup_battery(struct hid_device *dev, + unsigned report_type, unsigned report_id, + s32 min, s32 max) { struct power_supply *battery = &dev->battery; int ret; @@ -379,7 +381,8 @@ static void hidinput_setup_battery(struct hid_device *dev, unsigned id, s32 min, dev->battery_min = min; dev->battery_max = max; - dev->battery_report_id = id; + dev->battery_report_type = report_type; + dev->battery_report_id = report_id; ret = power_supply_register(&dev->dev, battery); if (ret != 0) { @@ -399,7 +402,9 @@ static void hidinput_cleanup_battery(struct hid_device *dev) dev->battery.name = NULL; } #else /* !CONFIG_HID_BATTERY_STRENGTH */ -static void hidinput_setup_battery(struct hid_device *dev, unsigned id, s32 min, s32 max) +static void hidinput_setup_battery(struct hid_device *dev, + unsigned report_type, unsigned report_id, + s32 min, s32 max) { } @@ -769,6 +774,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case HID_UP_GENDEVCTRLS: if ((usage->hid & HID_USAGE) == 0x20) { /* Battery Strength */ hidinput_setup_battery(device, + HID_INPUT_REPORT, field->report->id, field->logical_minimum, field->logical_maximum); @@ -1052,7 +1058,7 @@ static void report_features(struct hid_device *hid) if (((rep->field[i]->usage + j)->hid & HID_USAGE_PAGE) == HID_UP_GENDEVCTRLS && ((rep->field[i]->usage + j)->hid & HID_USAGE) == 0x20) { hidinput_setup_battery(hid, - rep->id, + HID_FEATURE_REPORT, rep->id, rep->field[i]->logical_minimum, rep->field[i]->logical_maximum); } diff --git a/include/linux/hid.h b/include/linux/hid.h index fa772c8..9351d3d 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -496,6 +496,7 @@ struct hid_device { /* device report descriptor */ __s32 battery_min; __s32 battery_max; __s32 battery_val; + __s32 battery_report_type; __s32 battery_report_id; #endif -- cgit v0.10.2 From 2f2e3f6d7a24e7fceb0fef514767025cddb64dc2 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Fri, 2 Dec 2011 19:05:07 -0800 Subject: hid-input/battery: make the battery setup common for INPUTs and FEATUREs Signed-off-by: Jeremy Fitzhardinge diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index f5c3efc..771ff5d 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -351,20 +351,22 @@ static int hidinput_get_battery_property(struct power_supply *psy, return ret; } -static void hidinput_setup_battery(struct hid_device *dev, - unsigned report_type, unsigned report_id, - s32 min, s32 max) +static bool hidinput_setup_battery(struct hid_device *dev, unsigned report_type, struct hid_field *field) { struct power_supply *battery = &dev->battery; int ret; unsigned quirks; + s32 min, max; + + if (field->usage->hid != HID_DC_BATTERYSTRENGTH) + return false; /* no match */ if (battery->name != NULL) - return; /* already initialized? */ + goto out; /* already initialized? */ battery->name = kasprintf(GFP_KERNEL, "hid-%s-battery", dev->uniq); if (battery->name == NULL) - return; + goto out; battery->type = POWER_SUPPLY_TYPE_USB; battery->properties = hidinput_battery_props; @@ -374,6 +376,9 @@ static void hidinput_setup_battery(struct hid_device *dev, quirks = find_battery_quirk(dev); + min = field->logical_minimum; + max = field->logical_maximum; + if (quirks & HID_BATTERY_QUIRK_PERCENT) { min = 0; max = 100; @@ -382,7 +387,7 @@ static void hidinput_setup_battery(struct hid_device *dev, dev->battery_min = min; dev->battery_max = max; dev->battery_report_type = report_type; - dev->battery_report_id = report_id; + dev->battery_report_id = field->report->id; ret = power_supply_register(&dev->dev, battery); if (ret != 0) { @@ -390,6 +395,9 @@ static void hidinput_setup_battery(struct hid_device *dev, kfree(battery->name); battery->name = NULL; } + +out: + return true; } static void hidinput_cleanup_battery(struct hid_device *dev) @@ -402,10 +410,10 @@ static void hidinput_cleanup_battery(struct hid_device *dev) dev->battery.name = NULL; } #else /* !CONFIG_HID_BATTERY_STRENGTH */ -static void hidinput_setup_battery(struct hid_device *dev, - unsigned report_type, unsigned report_id, - s32 min, s32 max) +static bool hidinput_setup_battery(struct hid_device *dev, unsigned report_type, + struct hid_field *field) { + return false; } static void hidinput_cleanup_battery(struct hid_device *dev) @@ -772,14 +780,9 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel break; case HID_UP_GENDEVCTRLS: - if ((usage->hid & HID_USAGE) == 0x20) { /* Battery Strength */ - hidinput_setup_battery(device, - HID_INPUT_REPORT, - field->report->id, - field->logical_minimum, - field->logical_maximum); + if (hidinput_setup_battery(device, HID_INPUT_REPORT, field)) goto ignore; - } else + else goto unknown; break; @@ -1055,13 +1058,7 @@ static void report_features(struct hid_device *hid) for (i = 0; i < rep->maxfield; i++) for (j = 0; j < rep->field[i]->maxusage; j++) { /* Verify if Battery Strength feature is available */ - if (((rep->field[i]->usage + j)->hid & HID_USAGE_PAGE) == HID_UP_GENDEVCTRLS && - ((rep->field[i]->usage + j)->hid & HID_USAGE) == 0x20) { - hidinput_setup_battery(hid, - HID_FEATURE_REPORT, rep->id, - rep->field[i]->logical_minimum, - rep->field[i]->logical_maximum); - } + hidinput_setup_battery(hid, HID_FEATURE_REPORT, rep->field[i]); if (drv->feature_mapping) drv->feature_mapping(hid, rep->field[i], -- cgit v0.10.2 From b3ca3839f344aa469e6f53c8bbb633e5ab9b96c8 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Fri, 2 Dec 2011 19:06:10 -0800 Subject: hid-input/battery: power-supply type really *is* a battery It just isn't a battery which is powering the computer. upower needs a more nuanced understanding of this. Signed-off-by: Jeremy Fitzhardinge diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 771ff5d..ceade58 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -368,7 +368,7 @@ static bool hidinput_setup_battery(struct hid_device *dev, unsigned report_type, if (battery->name == NULL) goto out; - battery->type = POWER_SUPPLY_TYPE_USB; + battery->type = POWER_SUPPLY_TYPE_BATTERY; battery->properties = hidinput_battery_props; battery->num_properties = ARRAY_SIZE(hidinput_battery_props); battery->use_for_apm = 0; -- cgit v0.10.2 From ce63920b395f1476e2d28cca16a56919289f0b62 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Fri, 2 Dec 2011 21:57:50 -0800 Subject: hid-input/battery: remove battery_val hidinput_get_battery_property() now directly polls the device for the current battery strength, so there's no need for battery_val, or the code to set it on the input event path. Signed-off-by: Jeremy Fitzhardinge diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index ceade58..48785db 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -917,14 +917,6 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct input = field->hidinput->input; -#ifdef CONFIG_HID_BATTERY_STRENGTH - if (usage->hid == HID_DC_BATTERYSTRENGTH) { - hid->battery_val = value; - hid_dbg(hid, "battery value is %d (range %d-%d)\n", - value, hid->battery_min, hid->battery_max); - return; - } -#endif if (!usage->type) return; diff --git a/include/linux/hid.h b/include/linux/hid.h index 9351d3d..0e76f0c 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -495,7 +495,6 @@ struct hid_device { /* device report descriptor */ struct power_supply battery; __s32 battery_min; __s32 battery_max; - __s32 battery_val; __s32 battery_report_type; __s32 battery_report_id; #endif -- cgit v0.10.2 From 652aa6a9ac4a5f8d3e1fa3f6466646519e83c01e Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Fri, 9 Dec 2011 00:10:28 -0800 Subject: hid-input/battery: add FEATURE quirk Apple keyboards require a FEATURE report to query the battery state, even though they list as an input. Without this, it returns an error. Signed-off-by: Jeremy Fitzhardinge diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 06ce996..b63beb8 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -124,6 +124,7 @@ #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI 0x0239 #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO 0x023a #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS 0x023b +#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ANSI 0x0255 #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ISO 0x0256 #define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY 0x030a #define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY 0x030b diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 48785db..0f1250d 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -283,12 +283,12 @@ static enum power_supply_property hidinput_battery_props[] = { }; #define HID_BATTERY_QUIRK_PERCENT (1 << 0) /* always reports percent */ +#define HID_BATTERY_QUIRK_FEATURE (1 << 1) /* ask for feature report */ static const struct hid_device_id hid_battery_quirks[] = { - { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICMOUSE), - HID_BATTERY_QUIRK_PERCENT }, - { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICTRACKPAD), - HID_BATTERY_QUIRK_PERCENT }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, + USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ANSI), + HID_BATTERY_QUIRK_PERCENT | HID_BATTERY_QUIRK_FEATURE }, {} }; @@ -310,7 +310,6 @@ static int hidinput_get_battery_property(struct power_supply *psy, { struct hid_device *dev = container_of(psy, struct hid_device, battery); int ret = 0; - int ret_rep; __u8 buf[2] = {}; switch (prop) { @@ -320,11 +319,13 @@ static int hidinput_get_battery_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_CAPACITY: - ret_rep = dev->hid_get_raw_report(dev, dev->battery_report_id, - buf, sizeof(buf), - dev->battery_report_type); - if (ret_rep != 2) { - ret = -EINVAL; + ret = dev->hid_get_raw_report(dev, dev->battery_report_id, + buf, sizeof(buf), + dev->battery_report_type); + + if (ret != 2) { + if (ret >= 0) + ret = -EINVAL; break; } @@ -376,6 +377,9 @@ static bool hidinput_setup_battery(struct hid_device *dev, unsigned report_type, quirks = find_battery_quirk(dev); + hid_dbg(dev, "device %x:%x:%x %d quirks %d\n", + dev->bus, dev->vendor, dev->product, dev->version, quirks); + min = field->logical_minimum; max = field->logical_maximum; @@ -384,6 +388,9 @@ static bool hidinput_setup_battery(struct hid_device *dev, unsigned report_type, max = 100; } + if (quirks & HID_BATTERY_QUIRK_FEATURE) + report_type = HID_FEATURE_REPORT; + dev->battery_min = min; dev->battery_max = max; dev->battery_report_type = report_type; -- cgit v0.10.2