Merge tag 'for-linus-2023101101' of git://git.kernel.org/pub/scm/linux/kernel/git...
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 11 Oct 2023 20:27:44 +0000 (13:27 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 11 Oct 2023 20:27:44 +0000 (13:27 -0700)
Pull HID fixes from Benjamin Tissoires:

 - regression fix for i2c-hid when used on DT platforms (Johan Hovold)

 - kernel crash fix on removal of the Logitech USB receiver (Hans de
   Goede)

* tag 'for-linus-2023101101' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid:
  HID: logitech-hidpp: Fix kernel crash on receiver USB disconnect
  HID: i2c-hid: fix handling of unpopulated devices

drivers/hid/hid-logitech-hidpp.c
drivers/hid/i2c-hid/i2c-hid-core.c

index ff077df0babf48eae7d7e731b9c5c531aa3f19a1..a209d51bd2476b3421fb18dcf2485bda1ce09505 100644 (file)
@@ -4515,7 +4515,8 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
                        goto hid_hw_init_fail;
        }
 
-       hidpp_connect_event(hidpp);
+       schedule_work(&hidpp->work);
+       flush_work(&hidpp->work);
 
        if (will_restart) {
                /* Reset the HID node state */
index 9601c0605fd9e6c1cfbd4a85754d3c45dc545da5..2735cd585af0df846d7430bac643cfd0ce81fc9b 100644 (file)
@@ -998,45 +998,29 @@ static int i2c_hid_core_resume(struct i2c_hid *ihid)
        return hid_driver_reset_resume(hid);
 }
 
-/**
- * __do_i2c_hid_core_initial_power_up() - First time power up of the i2c-hid device.
- * @ihid: The ihid object created during probe.
- *
- * This function is called at probe time.
- *
- * The initial power on is where we do some basic validation that the device
- * exists, where we fetch the HID descriptor, and where we create the actual
- * HID devices.
- *
- * Return: 0 or error code.
+/*
+ * Check that the device exists and parse the HID descriptor.
  */
-static int __do_i2c_hid_core_initial_power_up(struct i2c_hid *ihid)
+static int __i2c_hid_core_probe(struct i2c_hid *ihid)
 {
        struct i2c_client *client = ihid->client;
        struct hid_device *hid = ihid->hid;
        int ret;
 
-       ret = i2c_hid_core_power_up(ihid);
-       if (ret)
-               return ret;
-
        /* Make sure there is something at this address */
        ret = i2c_smbus_read_byte(client);
        if (ret < 0) {
                i2c_hid_dbg(ihid, "nothing at this address: %d\n", ret);
-               ret = -ENXIO;
-               goto err;
+               return -ENXIO;
        }
 
        ret = i2c_hid_fetch_hid_descriptor(ihid);
        if (ret < 0) {
                dev_err(&client->dev,
                        "Failed to fetch the HID Descriptor\n");
-               goto err;
+               return ret;
        }
 
-       enable_irq(client->irq);
-
        hid->version = le16_to_cpu(ihid->hdesc.bcdVersion);
        hid->vendor = le16_to_cpu(ihid->hdesc.wVendorID);
        hid->product = le16_to_cpu(ihid->hdesc.wProductID);
@@ -1050,17 +1034,49 @@ static int __do_i2c_hid_core_initial_power_up(struct i2c_hid *ihid)
 
        ihid->quirks = i2c_hid_lookup_quirk(hid->vendor, hid->product);
 
+       return 0;
+}
+
+static int i2c_hid_core_register_hid(struct i2c_hid *ihid)
+{
+       struct i2c_client *client = ihid->client;
+       struct hid_device *hid = ihid->hid;
+       int ret;
+
+       enable_irq(client->irq);
+
        ret = hid_add_device(hid);
        if (ret) {
                if (ret != -ENODEV)
                        hid_err(client, "can't add hid device: %d\n", ret);
-               goto err;
+               disable_irq(client->irq);
+               return ret;
        }
 
        return 0;
+}
+
+static int i2c_hid_core_probe_panel_follower(struct i2c_hid *ihid)
+{
+       int ret;
+
+       ret = i2c_hid_core_power_up(ihid);
+       if (ret)
+               return ret;
 
-err:
+       ret = __i2c_hid_core_probe(ihid);
+       if (ret)
+               goto err_power_down;
+
+       ret = i2c_hid_core_register_hid(ihid);
+       if (ret)
+               goto err_power_down;
+
+       return 0;
+
+err_power_down:
        i2c_hid_core_power_down(ihid);
+
        return ret;
 }
 
@@ -1077,7 +1093,7 @@ static void ihid_core_panel_prepare_work(struct work_struct *work)
         * steps.
         */
        if (!hid->version)
-               ret = __do_i2c_hid_core_initial_power_up(ihid);
+               ret = i2c_hid_core_probe_panel_follower(ihid);
        else
                ret = i2c_hid_core_resume(ihid);
 
@@ -1136,7 +1152,6 @@ static int i2c_hid_core_register_panel_follower(struct i2c_hid *ihid)
        struct device *dev = &ihid->client->dev;
        int ret;
 
-       ihid->is_panel_follower = true;
        ihid->panel_follower.funcs = &i2c_hid_core_panel_follower_funcs;
 
        /*
@@ -1156,30 +1171,6 @@ static int i2c_hid_core_register_panel_follower(struct i2c_hid *ihid)
        return 0;
 }
 
-static int i2c_hid_core_initial_power_up(struct i2c_hid *ihid)
-{
-       /*
-        * If we're a panel follower, we'll register and do our initial power
-        * up when the panel turns on; otherwise we do it right away.
-        */
-       if (drm_is_panel_follower(&ihid->client->dev))
-               return i2c_hid_core_register_panel_follower(ihid);
-       else
-               return __do_i2c_hid_core_initial_power_up(ihid);
-}
-
-static void i2c_hid_core_final_power_down(struct i2c_hid *ihid)
-{
-       /*
-        * If we're a follower, the act of unfollowing will cause us to be
-        * powered down. Otherwise we need to manually do it.
-        */
-       if (ihid->is_panel_follower)
-               drm_panel_remove_follower(&ihid->panel_follower);
-       else
-               i2c_hid_core_suspend(ihid, true);
-}
-
 int i2c_hid_core_probe(struct i2c_client *client, struct i2chid_ops *ops,
                       u16 hid_descriptor_address, u32 quirks)
 {
@@ -1211,6 +1202,7 @@ int i2c_hid_core_probe(struct i2c_client *client, struct i2chid_ops *ops,
        ihid->ops = ops;
        ihid->client = client;
        ihid->wHIDDescRegister = cpu_to_le16(hid_descriptor_address);
+       ihid->is_panel_follower = drm_is_panel_follower(&client->dev);
 
        init_waitqueue_head(&ihid->wait);
        mutex_init(&ihid->reset_lock);
@@ -1224,14 +1216,10 @@ int i2c_hid_core_probe(struct i2c_client *client, struct i2chid_ops *ops,
                return ret;
        device_enable_async_suspend(&client->dev);
 
-       ret = i2c_hid_init_irq(client);
-       if (ret < 0)
-               goto err_buffers_allocated;
-
        hid = hid_allocate_device();
        if (IS_ERR(hid)) {
                ret = PTR_ERR(hid);
-               goto err_irq;
+               goto err_free_buffers;
        }
 
        ihid->hid = hid;
@@ -1242,19 +1230,42 @@ int i2c_hid_core_probe(struct i2c_client *client, struct i2chid_ops *ops,
        hid->bus = BUS_I2C;
        hid->initial_quirks = quirks;
 
-       ret = i2c_hid_core_initial_power_up(ihid);
+       /* Power on and probe unless device is a panel follower. */
+       if (!ihid->is_panel_follower) {
+               ret = i2c_hid_core_power_up(ihid);
+               if (ret < 0)
+                       goto err_destroy_device;
+
+               ret = __i2c_hid_core_probe(ihid);
+               if (ret < 0)
+                       goto err_power_down;
+       }
+
+       ret = i2c_hid_init_irq(client);
+       if (ret < 0)
+               goto err_power_down;
+
+       /*
+        * If we're a panel follower, we'll register when the panel turns on;
+        * otherwise we do it right away.
+        */
+       if (ihid->is_panel_follower)
+               ret = i2c_hid_core_register_panel_follower(ihid);
+       else
+               ret = i2c_hid_core_register_hid(ihid);
        if (ret)
-               goto err_mem_free;
+               goto err_free_irq;
 
        return 0;
 
-err_mem_free:
-       hid_destroy_device(hid);
-
-err_irq:
+err_free_irq:
        free_irq(client->irq, ihid);
-
-err_buffers_allocated:
+err_power_down:
+       if (!ihid->is_panel_follower)
+               i2c_hid_core_power_down(ihid);
+err_destroy_device:
+       hid_destroy_device(hid);
+err_free_buffers:
        i2c_hid_free_buffers(ihid);
 
        return ret;
@@ -1266,7 +1277,14 @@ void i2c_hid_core_remove(struct i2c_client *client)
        struct i2c_hid *ihid = i2c_get_clientdata(client);
        struct hid_device *hid;
 
-       i2c_hid_core_final_power_down(ihid);
+       /*
+        * If we're a follower, the act of unfollowing will cause us to be
+        * powered down. Otherwise we need to manually do it.
+        */
+       if (ihid->is_panel_follower)
+               drm_panel_remove_follower(&ihid->panel_follower);
+       else
+               i2c_hid_core_suspend(ihid, true);
 
        hid = ihid->hid;
        hid_destroy_device(hid);