Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 2 Aug 2013 21:22:15 +0000 (14:22 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 2 Aug 2013 21:22:15 +0000 (14:22 -0700)
Pull HID fixes from Jiri Kosina:
 - fix hid-sony PS3 sixaxxis breakage from Benjamin Tissories
 - fix hidraw race condition from Yonghua Zheng
 - fix/bandaid for rare device enumeration problems of Logitech Unifying
   receivers from Nestor Lopez Casado

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid:
  HID: hidraw: fix improper mutex release
  HID: sony: fix HID mapping for PS3 sixaxis controller
  HID: hid-logitech-dj: querying_devices was never set
  HID: Revert "Revert "HID: Fix logitech-dj: missing Unifying device issue""

drivers/hid/hid-logitech-dj.c
drivers/hid/hid-logitech-dj.h
drivers/hid/hid-sony.c
drivers/hid/hidraw.c

index 5207591..7a57648 100644 (file)
@@ -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,
                                        size_t count,
                                        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,
                                                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] &
            SPFUNCTION_DEVICE_LIST_EMPTY) {
                dbg_hid("%s: device list is empty\n", __func__);
+               djrcv_dev->querying_devices = false;
                return;
        }
 
@@ -242,6 +244,12 @@ static void logi_dj_recv_add_djhid_device(struct dj_receiver_dev *djrcv_dev,
                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();
        if (IS_ERR(dj_hiddev)) {
                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;
        unsigned long flags;
        int count;
+       int retval;
 
        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);
                break;
        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__);
        }
 }
@@ -367,6 +395,12 @@ static void logi_dj_recv_forward_null_report(struct dj_receiver_dev *djrcv_dev,
        if (!djdev) {
                dbg_hid("djrcv_dev->paired_dj_devices[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;
        }
 
@@ -397,6 +431,12 @@ static void logi_dj_recv_forward_report(struct dj_receiver_dev *djrcv_dev,
        if (dj_device == NULL) {
                dbg_hid("djrcv_dev->paired_dj_devices[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;
        }
 
@@ -444,6 +484,12 @@ static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev)
        struct dj_report *dj_report;
        int retval;
 
+       /* no need to protect djrcv_dev->querying_devices */
+       if (djrcv_dev->querying_devices)
+               return 0;
+
+       djrcv_dev->querying_devices = true;
+
        dj_report = kzalloc(sizeof(struct dj_report), GFP_KERNEL);
        if (!dj_report)
                return -ENOMEM;
@@ -455,6 +501,7 @@ static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev)
        return retval;
 }
 
+
 static int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev,
                                          unsigned timeout)
 {
index fd28a5e..4a40003 100644 (file)
@@ -101,6 +101,7 @@ struct dj_receiver_dev {
        struct work_struct work;
        struct kfifo notif_fifo;
        spinlock_t lock;
+       bool querying_devices;
 };
 
 struct dj_device {
index ecbc749..87fbe29 100644 (file)
@@ -369,7 +369,8 @@ static int sony_mapping(struct hid_device *hdev, struct hid_input *hi,
        if (sc->quirks & PS3REMOTE)
                return ps3remote_mapping(hdev, hi, field, usage, bit, max);
 
-       return -1;
+       /* Let hid-core decide for the others */
+       return 0;
 }
 
 /*
index a745163..6f1feb2 100644 (file)
@@ -518,7 +518,6 @@ int hidraw_connect(struct hid_device *hid)
                goto out;
        }
 
-       mutex_unlock(&minors_lock);
        init_waitqueue_head(&dev->wait);
        INIT_LIST_HEAD(&dev->list);
 
@@ -528,6 +527,7 @@ int hidraw_connect(struct hid_device *hid)
        dev->exist = 1;
        hid->hidraw = dev;
 
+       mutex_unlock(&minors_lock);
 out:
        return result;