HID: Revert "Revert "HID: Fix logitech-dj: missing Unifying device issue""
This reverts commit8af6c08830
. This patch re-adds the workaround introduced by596264082f
which was reverted by8af6c08830
. 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 byc849a6143b
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. Commita9dd22b730
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 by8af6c08
. 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. Withdcd9006b1b
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>
This commit is contained in:
parent
3366dd9fa8
commit
c63e0e3700
2 changed files with 46 additions and 0 deletions
|
@ -192,6 +192,7 @@ static struct hid_ll_driver logi_dj_ll_driver;
|
||||||
static int logi_dj_output_hidraw_report(struct hid_device *hid, u8 * buf,
|
static int logi_dj_output_hidraw_report(struct hid_device *hid, u8 * buf,
|
||||||
size_t count,
|
size_t count,
|
||||||
unsigned char report_type);
|
unsigned char report_type);
|
||||||
|
static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev);
|
||||||
|
|
||||||
static void logi_dj_recv_destroy_djhid_device(struct dj_receiver_dev *djrcv_dev,
|
static void logi_dj_recv_destroy_djhid_device(struct dj_receiver_dev *djrcv_dev,
|
||||||
struct dj_report *dj_report)
|
struct dj_report *dj_report)
|
||||||
|
@ -232,6 +233,7 @@ static void logi_dj_recv_add_djhid_device(struct dj_receiver_dev *djrcv_dev,
|
||||||
if (dj_report->report_params[DEVICE_PAIRED_PARAM_SPFUNCTION] &
|
if (dj_report->report_params[DEVICE_PAIRED_PARAM_SPFUNCTION] &
|
||||||
SPFUNCTION_DEVICE_LIST_EMPTY) {
|
SPFUNCTION_DEVICE_LIST_EMPTY) {
|
||||||
dbg_hid("%s: device list is empty\n", __func__);
|
dbg_hid("%s: device list is empty\n", __func__);
|
||||||
|
djrcv_dev->querying_devices = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -242,6 +244,12 @@ static void logi_dj_recv_add_djhid_device(struct dj_receiver_dev *djrcv_dev,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (djrcv_dev->paired_dj_devices[dj_report->device_index]) {
|
||||||
|
/* The device is already known. No need to reallocate it. */
|
||||||
|
dbg_hid("%s: device is already known\n", __func__);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
dj_hiddev = hid_allocate_device();
|
dj_hiddev = hid_allocate_device();
|
||||||
if (IS_ERR(dj_hiddev)) {
|
if (IS_ERR(dj_hiddev)) {
|
||||||
dev_err(&djrcv_hdev->dev, "%s: hid_allocate_device failed\n",
|
dev_err(&djrcv_hdev->dev, "%s: hid_allocate_device failed\n",
|
||||||
|
@ -305,6 +313,7 @@ static void delayedwork_callback(struct work_struct *work)
|
||||||
struct dj_report dj_report;
|
struct dj_report dj_report;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
int count;
|
int count;
|
||||||
|
int retval;
|
||||||
|
|
||||||
dbg_hid("%s\n", __func__);
|
dbg_hid("%s\n", __func__);
|
||||||
|
|
||||||
|
@ -337,6 +346,25 @@ static void delayedwork_callback(struct work_struct *work)
|
||||||
logi_dj_recv_destroy_djhid_device(djrcv_dev, &dj_report);
|
logi_dj_recv_destroy_djhid_device(djrcv_dev, &dj_report);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
/* A normal report (i. e. not belonging to a pair/unpair notification)
|
||||||
|
* arriving here, means that the report arrived but we did not have a
|
||||||
|
* paired dj_device associated to the report's device_index, this
|
||||||
|
* means that the original "device paired" notification corresponding
|
||||||
|
* to this dj_device never arrived to this driver. The reason is that
|
||||||
|
* hid-core discards all packets coming from a device while probe() is
|
||||||
|
* executing. */
|
||||||
|
if (!djrcv_dev->paired_dj_devices[dj_report.device_index]) {
|
||||||
|
/* ok, we don't know the device, just re-ask the
|
||||||
|
* receiver for the list of connected devices. */
|
||||||
|
retval = logi_dj_recv_query_paired_devices(djrcv_dev);
|
||||||
|
if (!retval) {
|
||||||
|
/* everything went fine, so just leave */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
dev_err(&djrcv_dev->hdev->dev,
|
||||||
|
"%s:logi_dj_recv_query_paired_devices "
|
||||||
|
"error:%d\n", __func__, retval);
|
||||||
|
}
|
||||||
dbg_hid("%s: unexpected report type\n", __func__);
|
dbg_hid("%s: unexpected report type\n", __func__);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -367,6 +395,12 @@ static void logi_dj_recv_forward_null_report(struct dj_receiver_dev *djrcv_dev,
|
||||||
if (!djdev) {
|
if (!djdev) {
|
||||||
dbg_hid("djrcv_dev->paired_dj_devices[dj_report->device_index]"
|
dbg_hid("djrcv_dev->paired_dj_devices[dj_report->device_index]"
|
||||||
" is NULL, index %d\n", dj_report->device_index);
|
" is NULL, index %d\n", dj_report->device_index);
|
||||||
|
kfifo_in(&djrcv_dev->notif_fifo, dj_report, sizeof(struct dj_report));
|
||||||
|
|
||||||
|
if (schedule_work(&djrcv_dev->work) == 0) {
|
||||||
|
dbg_hid("%s: did not schedule the work item, was already "
|
||||||
|
"queued\n", __func__);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -397,6 +431,12 @@ static void logi_dj_recv_forward_report(struct dj_receiver_dev *djrcv_dev,
|
||||||
if (dj_device == NULL) {
|
if (dj_device == NULL) {
|
||||||
dbg_hid("djrcv_dev->paired_dj_devices[dj_report->device_index]"
|
dbg_hid("djrcv_dev->paired_dj_devices[dj_report->device_index]"
|
||||||
" is NULL, index %d\n", dj_report->device_index);
|
" is NULL, index %d\n", dj_report->device_index);
|
||||||
|
kfifo_in(&djrcv_dev->notif_fifo, dj_report, sizeof(struct dj_report));
|
||||||
|
|
||||||
|
if (schedule_work(&djrcv_dev->work) == 0) {
|
||||||
|
dbg_hid("%s: did not schedule the work item, was already "
|
||||||
|
"queued\n", __func__);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -444,6 +484,10 @@ static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev)
|
||||||
struct dj_report *dj_report;
|
struct dj_report *dj_report;
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
|
/* no need to protect djrcv_dev->querying_devices */
|
||||||
|
if (djrcv_dev->querying_devices)
|
||||||
|
return 0;
|
||||||
|
|
||||||
dj_report = kzalloc(sizeof(struct dj_report), GFP_KERNEL);
|
dj_report = kzalloc(sizeof(struct dj_report), GFP_KERNEL);
|
||||||
if (!dj_report)
|
if (!dj_report)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
@ -455,6 +499,7 @@ static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev)
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev,
|
static int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev,
|
||||||
unsigned timeout)
|
unsigned timeout)
|
||||||
{
|
{
|
||||||
|
|
|
@ -101,6 +101,7 @@ struct dj_receiver_dev {
|
||||||
struct work_struct work;
|
struct work_struct work;
|
||||||
struct kfifo notif_fifo;
|
struct kfifo notif_fifo;
|
||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
|
bool querying_devices;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct dj_device {
|
struct dj_device {
|
||||||
|
|
Loading…
Reference in a new issue