Commit bab19d1b authored by Linus Torvalds's avatar Linus Torvalds
Browse files

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
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -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 */
+81 −63
Original line number Diff line number Diff line
@@ -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;

err:
	ret = i2c_hid_core_power_up(ihid);
	if (ret)
		return ret;

	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);