Commit bab19d1b authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'for-linus-2023101101' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid

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
parents 4524565e dac50139
...@@ -4515,7 +4515,8 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id) ...@@ -4515,7 +4515,8 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
goto hid_hw_init_fail; goto hid_hw_init_fail;
} }
hidpp_connect_event(hidpp); schedule_work(&hidpp->work);
flush_work(&hidpp->work);
if (will_restart) { if (will_restart) {
/* Reset the HID node state */ /* Reset the HID node state */
......
...@@ -998,45 +998,29 @@ static int i2c_hid_core_resume(struct i2c_hid *ihid) ...@@ -998,45 +998,29 @@ static int i2c_hid_core_resume(struct i2c_hid *ihid)
return hid_driver_reset_resume(hid); return hid_driver_reset_resume(hid);
} }
/** /*
* __do_i2c_hid_core_initial_power_up() - First time power up of the i2c-hid device. * Check that the device exists and parse the HID descriptor.
* @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.
*/ */
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 i2c_client *client = ihid->client;
struct hid_device *hid = ihid->hid; struct hid_device *hid = ihid->hid;
int ret; int ret;
ret = i2c_hid_core_power_up(ihid);
if (ret)
return ret;
/* Make sure there is something at this address */ /* Make sure there is something at this address */
ret = i2c_smbus_read_byte(client); ret = i2c_smbus_read_byte(client);
if (ret < 0) { if (ret < 0) {
i2c_hid_dbg(ihid, "nothing at this address: %d\n", ret); i2c_hid_dbg(ihid, "nothing at this address: %d\n", ret);
ret = -ENXIO; return -ENXIO;
goto err;
} }
ret = i2c_hid_fetch_hid_descriptor(ihid); ret = i2c_hid_fetch_hid_descriptor(ihid);
if (ret < 0) { if (ret < 0) {
dev_err(&client->dev, dev_err(&client->dev,
"Failed to fetch the HID Descriptor\n"); "Failed to fetch the HID Descriptor\n");
goto err; return ret;
} }
enable_irq(client->irq);
hid->version = le16_to_cpu(ihid->hdesc.bcdVersion); hid->version = le16_to_cpu(ihid->hdesc.bcdVersion);
hid->vendor = le16_to_cpu(ihid->hdesc.wVendorID); hid->vendor = le16_to_cpu(ihid->hdesc.wVendorID);
hid->product = le16_to_cpu(ihid->hdesc.wProductID); 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) ...@@ -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); 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); ret = hid_add_device(hid);
if (ret) { if (ret) {
if (ret != -ENODEV) if (ret != -ENODEV)
hid_err(client, "can't add hid device: %d\n", ret); hid_err(client, "can't add hid device: %d\n", ret);
goto err; disable_irq(client->irq);
return ret;
} }
return 0; 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); i2c_hid_core_power_down(ihid);
return ret; return ret;
} }
...@@ -1077,7 +1093,7 @@ static void ihid_core_panel_prepare_work(struct work_struct *work) ...@@ -1077,7 +1093,7 @@ static void ihid_core_panel_prepare_work(struct work_struct *work)
* steps. * steps.
*/ */
if (!hid->version) if (!hid->version)
ret = __do_i2c_hid_core_initial_power_up(ihid); ret = i2c_hid_core_probe_panel_follower(ihid);
else else
ret = i2c_hid_core_resume(ihid); ret = i2c_hid_core_resume(ihid);
...@@ -1136,7 +1152,6 @@ static int i2c_hid_core_register_panel_follower(struct i2c_hid *ihid) ...@@ -1136,7 +1152,6 @@ static int i2c_hid_core_register_panel_follower(struct i2c_hid *ihid)
struct device *dev = &ihid->client->dev; struct device *dev = &ihid->client->dev;
int ret; int ret;
ihid->is_panel_follower = true;
ihid->panel_follower.funcs = &i2c_hid_core_panel_follower_funcs; 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) ...@@ -1156,30 +1171,6 @@ static int i2c_hid_core_register_panel_follower(struct i2c_hid *ihid)
return 0; 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, int i2c_hid_core_probe(struct i2c_client *client, struct i2chid_ops *ops,
u16 hid_descriptor_address, u32 quirks) u16 hid_descriptor_address, u32 quirks)
{ {
...@@ -1211,6 +1202,7 @@ int i2c_hid_core_probe(struct i2c_client *client, struct i2chid_ops *ops, ...@@ -1211,6 +1202,7 @@ int i2c_hid_core_probe(struct i2c_client *client, struct i2chid_ops *ops,
ihid->ops = ops; ihid->ops = ops;
ihid->client = client; ihid->client = client;
ihid->wHIDDescRegister = cpu_to_le16(hid_descriptor_address); ihid->wHIDDescRegister = cpu_to_le16(hid_descriptor_address);
ihid->is_panel_follower = drm_is_panel_follower(&client->dev);
init_waitqueue_head(&ihid->wait); init_waitqueue_head(&ihid->wait);
mutex_init(&ihid->reset_lock); mutex_init(&ihid->reset_lock);
...@@ -1224,14 +1216,10 @@ int i2c_hid_core_probe(struct i2c_client *client, struct i2chid_ops *ops, ...@@ -1224,14 +1216,10 @@ int i2c_hid_core_probe(struct i2c_client *client, struct i2chid_ops *ops,
return ret; return ret;
device_enable_async_suspend(&client->dev); device_enable_async_suspend(&client->dev);
ret = i2c_hid_init_irq(client);
if (ret < 0)
goto err_buffers_allocated;
hid = hid_allocate_device(); hid = hid_allocate_device();
if (IS_ERR(hid)) { if (IS_ERR(hid)) {
ret = PTR_ERR(hid); ret = PTR_ERR(hid);
goto err_irq; goto err_free_buffers;
} }
ihid->hid = hid; ihid->hid = hid;
...@@ -1242,19 +1230,42 @@ int i2c_hid_core_probe(struct i2c_client *client, struct i2chid_ops *ops, ...@@ -1242,19 +1230,42 @@ int i2c_hid_core_probe(struct i2c_client *client, struct i2chid_ops *ops,
hid->bus = BUS_I2C; hid->bus = BUS_I2C;
hid->initial_quirks = quirks; 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) if (ret)
goto err_mem_free; goto err_free_irq;
return 0; return 0;
err_mem_free: err_free_irq:
hid_destroy_device(hid);
err_irq:
free_irq(client->irq, ihid); free_irq(client->irq, ihid);
err_power_down:
err_buffers_allocated: 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); i2c_hid_free_buffers(ihid);
return ret; return ret;
...@@ -1266,7 +1277,14 @@ void i2c_hid_core_remove(struct i2c_client *client) ...@@ -1266,7 +1277,14 @@ void i2c_hid_core_remove(struct i2c_client *client)
struct i2c_hid *ihid = i2c_get_clientdata(client); struct i2c_hid *ihid = i2c_get_clientdata(client);
struct hid_device *hid; 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 = ihid->hid;
hid_destroy_device(hid); hid_destroy_device(hid);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment