aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNestor Lopez Casado <nlopezcasad@logitech.com>2013-07-18 09:21:30 -0400
committerJiri Kosina <jkosina@suse.cz>2013-07-22 10:32:24 -0400
commitc63e0e370028d7e4033bd40165f18499872b5183 (patch)
tree6c77869acd520b9a7688ac8ee7e883924662c669
parent3366dd9fa887ebbda4872e9554f853eaeda764be (diff)
HID: Revert "Revert "HID: Fix logitech-dj: missing Unifying device issue""
This reverts commit 8af6c08830b1ae114d1a8b548b1f8b056e068887. This patch re-adds the workaround introduced by 596264082f10dd4 which was reverted by 8af6c08830b1ae114. The original patch 596264 was needed to overcome a situation where the hid-core would drop incoming reports while probe() was being executed. This issue was solved by c849a6143bec520af which added hid_device_io_start() and hid_device_io_stop() that enable a specific hid driver to opt-in for input reports while its probe() is being executed. Commit a9dd22b730857347 modified hid-logitech-dj so as to use the functionality added to hid-core. Having done that, workaround 596264 was no longer necessary and was reverted by 8af6c08. We now encounter a different problem that ends up 'again' thwarting the Unifying receiver enumeration. The problem is time and usb controller dependent. Ocasionally the reports sent to the usb receiver to start the paired devices enumeration fail with -EPIPE and the receiver never gets to enumerate the paired devices. With dcd9006b1b053c7b1c the problem was "hidden" as the call to the usb driver became asynchronous and none was catching the error from the failing URB. As the root cause for this failing SET_REPORT is not understood yet, -possibly a race on the usb controller drivers or a problem with the Unifying receiver- reintroducing this workaround solves the problem. Overall what this workaround does is: If an input report from an unknown device is received, then a (re)enumeration is performed. related bug: https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1194649 Signed-off-by: Nestor Lopez Casado <nlopezcasad@logitech.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
-rw-r--r--drivers/hid/hid-logitech-dj.c45
-rw-r--r--drivers/hid/hid-logitech-dj.h1
2 files changed, 46 insertions, 0 deletions
diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c
index 5207591a598c..cd33084c7860 100644
--- a/drivers/hid/hid-logitech-dj.c
+++ b/drivers/hid/hid-logitech-dj.c
@@ -192,6 +192,7 @@ static struct hid_ll_driver logi_dj_ll_driver;
192static int logi_dj_output_hidraw_report(struct hid_device *hid, u8 * buf, 192static int logi_dj_output_hidraw_report(struct hid_device *hid, u8 * buf,
193 size_t count, 193 size_t count,
194 unsigned char report_type); 194 unsigned char report_type);
195static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev);
195 196
196static void logi_dj_recv_destroy_djhid_device(struct dj_receiver_dev *djrcv_dev, 197static void logi_dj_recv_destroy_djhid_device(struct dj_receiver_dev *djrcv_dev,
197 struct dj_report *dj_report) 198 struct dj_report *dj_report)
@@ -232,6 +233,7 @@ static void logi_dj_recv_add_djhid_device(struct dj_receiver_dev *djrcv_dev,
232 if (dj_report->report_params[DEVICE_PAIRED_PARAM_SPFUNCTION] & 233 if (dj_report->report_params[DEVICE_PAIRED_PARAM_SPFUNCTION] &
233 SPFUNCTION_DEVICE_LIST_EMPTY) { 234 SPFUNCTION_DEVICE_LIST_EMPTY) {
234 dbg_hid("%s: device list is empty\n", __func__); 235 dbg_hid("%s: device list is empty\n", __func__);
236 djrcv_dev->querying_devices = false;
235 return; 237 return;
236 } 238 }
237 239
@@ -242,6 +244,12 @@ static void logi_dj_recv_add_djhid_device(struct dj_receiver_dev *djrcv_dev,
242 return; 244 return;
243 } 245 }
244 246
247 if (djrcv_dev->paired_dj_devices[dj_report->device_index]) {
248 /* The device is already known. No need to reallocate it. */
249 dbg_hid("%s: device is already known\n", __func__);
250 return;
251 }
252
245 dj_hiddev = hid_allocate_device(); 253 dj_hiddev = hid_allocate_device();
246 if (IS_ERR(dj_hiddev)) { 254 if (IS_ERR(dj_hiddev)) {
247 dev_err(&djrcv_hdev->dev, "%s: hid_allocate_device failed\n", 255 dev_err(&djrcv_hdev->dev, "%s: hid_allocate_device failed\n",
@@ -305,6 +313,7 @@ static void delayedwork_callback(struct work_struct *work)
305 struct dj_report dj_report; 313 struct dj_report dj_report;
306 unsigned long flags; 314 unsigned long flags;
307 int count; 315 int count;
316 int retval;
308 317
309 dbg_hid("%s\n", __func__); 318 dbg_hid("%s\n", __func__);
310 319
@@ -337,6 +346,25 @@ static void delayedwork_callback(struct work_struct *work)
337 logi_dj_recv_destroy_djhid_device(djrcv_dev, &dj_report); 346 logi_dj_recv_destroy_djhid_device(djrcv_dev, &dj_report);
338 break; 347 break;
339 default: 348 default:
349 /* A normal report (i. e. not belonging to a pair/unpair notification)
350 * arriving here, means that the report arrived but we did not have a
351 * paired dj_device associated to the report's device_index, this
352 * means that the original "device paired" notification corresponding
353 * to this dj_device never arrived to this driver. The reason is that
354 * hid-core discards all packets coming from a device while probe() is
355 * executing. */
356 if (!djrcv_dev->paired_dj_devices[dj_report.device_index]) {
357 /* ok, we don't know the device, just re-ask the
358 * receiver for the list of connected devices. */
359 retval = logi_dj_recv_query_paired_devices(djrcv_dev);
360 if (!retval) {
361 /* everything went fine, so just leave */
362 break;
363 }
364 dev_err(&djrcv_dev->hdev->dev,
365 "%s:logi_dj_recv_query_paired_devices "
366 "error:%d\n", __func__, retval);
367 }
340 dbg_hid("%s: unexpected report type\n", __func__); 368 dbg_hid("%s: unexpected report type\n", __func__);
341 } 369 }
342} 370}
@@ -367,6 +395,12 @@ static void logi_dj_recv_forward_null_report(struct dj_receiver_dev *djrcv_dev,
367 if (!djdev) { 395 if (!djdev) {
368 dbg_hid("djrcv_dev->paired_dj_devices[dj_report->device_index]" 396 dbg_hid("djrcv_dev->paired_dj_devices[dj_report->device_index]"
369 " is NULL, index %d\n", dj_report->device_index); 397 " is NULL, index %d\n", dj_report->device_index);
398 kfifo_in(&djrcv_dev->notif_fifo, dj_report, sizeof(struct dj_report));
399
400 if (schedule_work(&djrcv_dev->work) == 0) {
401 dbg_hid("%s: did not schedule the work item, was already "
402 "queued\n", __func__);
403 }
370 return; 404 return;
371 } 405 }
372 406
@@ -397,6 +431,12 @@ static void logi_dj_recv_forward_report(struct dj_receiver_dev *djrcv_dev,
397 if (dj_device == NULL) { 431 if (dj_device == NULL) {
398 dbg_hid("djrcv_dev->paired_dj_devices[dj_report->device_index]" 432 dbg_hid("djrcv_dev->paired_dj_devices[dj_report->device_index]"
399 " is NULL, index %d\n", dj_report->device_index); 433 " is NULL, index %d\n", dj_report->device_index);
434 kfifo_in(&djrcv_dev->notif_fifo, dj_report, sizeof(struct dj_report));
435
436 if (schedule_work(&djrcv_dev->work) == 0) {
437 dbg_hid("%s: did not schedule the work item, was already "
438 "queued\n", __func__);
439 }
400 return; 440 return;
401 } 441 }
402 442
@@ -444,6 +484,10 @@ static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev)
444 struct dj_report *dj_report; 484 struct dj_report *dj_report;
445 int retval; 485 int retval;
446 486
487 /* no need to protect djrcv_dev->querying_devices */
488 if (djrcv_dev->querying_devices)
489 return 0;
490
447 dj_report = kzalloc(sizeof(struct dj_report), GFP_KERNEL); 491 dj_report = kzalloc(sizeof(struct dj_report), GFP_KERNEL);
448 if (!dj_report) 492 if (!dj_report)
449 return -ENOMEM; 493 return -ENOMEM;
@@ -455,6 +499,7 @@ static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev)
455 return retval; 499 return retval;
456} 500}
457 501
502
458static int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev, 503static int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev,
459 unsigned timeout) 504 unsigned timeout)
460{ 505{
diff --git a/drivers/hid/hid-logitech-dj.h b/drivers/hid/hid-logitech-dj.h
index fd28a5e0ca3b..4a4000340ce1 100644
--- a/drivers/hid/hid-logitech-dj.h
+++ b/drivers/hid/hid-logitech-dj.h
@@ -101,6 +101,7 @@ struct dj_receiver_dev {
101 struct work_struct work; 101 struct work_struct work;
102 struct kfifo notif_fifo; 102 struct kfifo notif_fifo;
103 spinlock_t lock; 103 spinlock_t lock;
104 bool querying_devices;
104}; 105};
105 106
106struct dj_device { 107struct dj_device {