aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Herrmann <dh.herrmann@gmail.com>2013-07-15 13:10:12 -0400
committerJiri Kosina <jkosina@suse.cz>2013-07-31 05:02:02 -0400
commit50c9d75b6f01a337aab728511bc1d2a0a3d7b800 (patch)
tree87f3ef456925f5bb0b65222a09b47a47fbc1d9c5
parentbdb829e1dd710029a075b5f86d4053e7715beb06 (diff)
HID: input: generic hidinput_input_event handler
The hidinput_input_event() callback converts input events written from userspace into HID reports and sends them to the device. We currently implement this in every HID transport driver, even though most of them do the same. This provides a generic hidinput_input_event() implementation which is mostly copied from usbhid. It uses a delayed worker to allow multiple LED events to be collected into a single output event. We use the custom ->request() transport driver callback to allow drivers to adjust the outgoing report and handle the request asynchronously. If no custom ->request() callback is available, we fall back to the generic raw output report handler (which is synchronous). Drivers can still provide custom hidinput_input_event() handlers (see logitech-dj) if the generic implementation doesn't fit their needs. Signed-off-by: David Herrmann <dh.herrmann@gmail.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
-rw-r--r--drivers/hid/hid-input.c80
-rw-r--r--include/linux/hid.h1
2 files changed, 80 insertions, 1 deletions
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index 7480799e535c..308eee8fc7c3 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -1137,6 +1137,74 @@ unsigned int hidinput_count_leds(struct hid_device *hid)
1137} 1137}
1138EXPORT_SYMBOL_GPL(hidinput_count_leds); 1138EXPORT_SYMBOL_GPL(hidinput_count_leds);
1139 1139
1140static void hidinput_led_worker(struct work_struct *work)
1141{
1142 struct hid_device *hid = container_of(work, struct hid_device,
1143 led_work);
1144 struct hid_field *field;
1145 struct hid_report *report;
1146 int len;
1147 __u8 *buf;
1148
1149 field = hidinput_get_led_field(hid);
1150 if (!field)
1151 return;
1152
1153 /*
1154 * field->report is accessed unlocked regarding HID core. So there might
1155 * be another incoming SET-LED request from user-space, which changes
1156 * the LED state while we assemble our outgoing buffer. However, this
1157 * doesn't matter as hid_output_report() correctly converts it into a
1158 * boolean value no matter what information is currently set on the LED
1159 * field (even garbage). So the remote device will always get a valid
1160 * request.
1161 * And in case we send a wrong value, a next led worker is spawned
1162 * for every SET-LED request so the following worker will send the
1163 * correct value, guaranteed!
1164 */
1165
1166 report = field->report;
1167
1168 /* use custom SET_REPORT request if possible (asynchronous) */
1169 if (hid->ll_driver->request)
1170 return hid->ll_driver->request(hid, report, HID_REQ_SET_REPORT);
1171
1172 /* fall back to generic raw-output-report */
1173 len = ((report->size - 1) >> 3) + 1 + (report->id > 0);
1174 buf = kmalloc(len, GFP_KERNEL);
1175 if (!buf)
1176 return;
1177
1178 hid_output_report(report, buf);
1179 /* synchronous output report */
1180 hid->hid_output_raw_report(hid, buf, len, HID_OUTPUT_REPORT);
1181 kfree(buf);
1182}
1183
1184static int hidinput_input_event(struct input_dev *dev, unsigned int type,
1185 unsigned int code, int value)
1186{
1187 struct hid_device *hid = input_get_drvdata(dev);
1188 struct hid_field *field;
1189 int offset;
1190
1191 if (type == EV_FF)
1192 return input_ff_event(dev, type, code, value);
1193
1194 if (type != EV_LED)
1195 return -1;
1196
1197 if ((offset = hidinput_find_field(hid, type, code, &field)) == -1) {
1198 hid_warn(dev, "event field not found\n");
1199 return -1;
1200 }
1201
1202 hid_set_field(field, offset, value);
1203
1204 schedule_work(&hid->led_work);
1205 return 0;
1206}
1207
1140static int hidinput_open(struct input_dev *dev) 1208static int hidinput_open(struct input_dev *dev)
1141{ 1209{
1142 struct hid_device *hid = input_get_drvdata(dev); 1210 struct hid_device *hid = input_get_drvdata(dev);
@@ -1183,7 +1251,10 @@ static struct hid_input *hidinput_allocate(struct hid_device *hid)
1183 } 1251 }
1184 1252
1185 input_set_drvdata(input_dev, hid); 1253 input_set_drvdata(input_dev, hid);
1186 input_dev->event = hid->ll_driver->hidinput_input_event; 1254 if (hid->ll_driver->hidinput_input_event)
1255 input_dev->event = hid->ll_driver->hidinput_input_event;
1256 else if (hid->ll_driver->request || hid->hid_output_raw_report)
1257 input_dev->event = hidinput_input_event;
1187 input_dev->open = hidinput_open; 1258 input_dev->open = hidinput_open;
1188 input_dev->close = hidinput_close; 1259 input_dev->close = hidinput_close;
1189 input_dev->setkeycode = hidinput_setkeycode; 1260 input_dev->setkeycode = hidinput_setkeycode;
@@ -1278,6 +1349,7 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)
1278 int i, j, k; 1349 int i, j, k;
1279 1350
1280 INIT_LIST_HEAD(&hid->inputs); 1351 INIT_LIST_HEAD(&hid->inputs);
1352 INIT_WORK(&hid->led_work, hidinput_led_worker);
1281 1353
1282 if (!force) { 1354 if (!force) {
1283 for (i = 0; i < hid->maxcollection; i++) { 1355 for (i = 0; i < hid->maxcollection; i++) {
@@ -1379,6 +1451,12 @@ void hidinput_disconnect(struct hid_device *hid)
1379 input_unregister_device(hidinput->input); 1451 input_unregister_device(hidinput->input);
1380 kfree(hidinput); 1452 kfree(hidinput);
1381 } 1453 }
1454
1455 /* led_work is spawned by input_dev callbacks, but doesn't access the
1456 * parent input_dev at all. Once all input devices are removed, we
1457 * know that led_work will never get restarted, so we can cancel it
1458 * synchronously and are safe. */
1459 cancel_work_sync(&hid->led_work);
1382} 1460}
1383EXPORT_SYMBOL_GPL(hidinput_disconnect); 1461EXPORT_SYMBOL_GPL(hidinput_disconnect);
1384 1462
diff --git a/include/linux/hid.h b/include/linux/hid.h
index b8058c5c5594..ea4b828cb9cd 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -456,6 +456,7 @@ struct hid_device { /* device report descriptor */
456 enum hid_type type; /* device type (mouse, kbd, ...) */ 456 enum hid_type type; /* device type (mouse, kbd, ...) */
457 unsigned country; /* HID country */ 457 unsigned country; /* HID country */
458 struct hid_report_enum report_enum[HID_REPORT_TYPES]; 458 struct hid_report_enum report_enum[HID_REPORT_TYPES];
459 struct work_struct led_work; /* delayed LED worker */
459 460
460 struct semaphore driver_lock; /* protects the current driver, except during input */ 461 struct semaphore driver_lock; /* protects the current driver, except during input */
461 struct semaphore driver_input_lock; /* protects the current driver */ 462 struct semaphore driver_input_lock; /* protects the current driver */