diff options
Diffstat (limited to 'drivers/hid/i2c-hid/i2c-hid.c')
| -rw-r--r-- | drivers/hid/i2c-hid/i2c-hid.c | 222 | 
1 files changed, 115 insertions, 107 deletions
diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index c1336193b04..21aafc8f48c 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -25,6 +25,7 @@  #include <linux/delay.h>  #include <linux/slab.h>  #include <linux/pm.h> +#include <linux/pm_runtime.h>  #include <linux/device.h>  #include <linux/wait.h>  #include <linux/err.h> @@ -256,18 +257,27 @@ static int i2c_hid_get_report(struct i2c_client *client, u8 reportType,  	return 0;  } -static int i2c_hid_set_report(struct i2c_client *client, u8 reportType, -		u8 reportID, unsigned char *buf, size_t data_len) +/** + * i2c_hid_set_or_send_report: forward an incoming report to the device + * @client: the i2c_client of the device + * @reportType: 0x03 for HID_FEATURE_REPORT ; 0x02 for HID_OUTPUT_REPORT + * @reportID: the report ID + * @buf: the actual data to transfer, without the report ID + * @len: size of buf + * @use_data: true: use SET_REPORT HID command, false: send plain OUTPUT report + */ +static int i2c_hid_set_or_send_report(struct i2c_client *client, u8 reportType, +		u8 reportID, unsigned char *buf, size_t data_len, bool use_data)  {  	struct i2c_hid *ihid = i2c_get_clientdata(client);  	u8 *args = ihid->argsbuf; -	const struct i2c_hid_cmd * hidcmd = &hid_set_report_cmd; +	const struct i2c_hid_cmd *hidcmd;  	int ret;  	u16 dataRegister = le16_to_cpu(ihid->hdesc.wDataRegister);  	u16 outputRegister = le16_to_cpu(ihid->hdesc.wOutputRegister);  	u16 maxOutputLength = le16_to_cpu(ihid->hdesc.wMaxOutputLength); -	/* hidraw already checked that data_len < HID_MAX_BUFFER_SIZE */ +	/* hid_hw_* already checked that data_len < HID_MAX_BUFFER_SIZE */  	u16 size =	2			/* size */ +  			(reportID ? 1 : 0)	/* reportID */ +  			data_len		/* buf */; @@ -278,6 +288,9 @@ static int i2c_hid_set_report(struct i2c_client *client, u8 reportType,  	i2c_hid_dbg(ihid, "%s\n", __func__); +	if (!use_data && maxOutputLength == 0) +		return -ENOSYS; +  	if (reportID >= 0x0F) {  		args[index++] = reportID;  		reportID = 0x0F; @@ -287,9 +300,10 @@ static int i2c_hid_set_report(struct i2c_client *client, u8 reportType,  	 * use the data register for feature reports or if the device does not  	 * support the output register  	 */ -	if (reportType == 0x03 || maxOutputLength == 0) { +	if (use_data) {  		args[index++] = dataRegister & 0xFF;  		args[index++] = dataRegister >> 8; +		hidcmd = &hid_set_report_cmd;  	} else {  		args[index++] = outputRegister & 0xFF;  		args[index++] = outputRegister >> 8; @@ -454,14 +468,18 @@ static void i2c_hid_init_reports(struct hid_device *hid)  		return;  	} -	list_for_each_entry(report, -		&hid->report_enum[HID_INPUT_REPORT].report_list, list) -		i2c_hid_init_report(report, inbuf, ihid->bufsize); +	/* +	 * The device must be powered on while we fetch initial reports +	 * from it. +	 */ +	pm_runtime_get_sync(&client->dev);  	list_for_each_entry(report,  		&hid->report_enum[HID_FEATURE_REPORT].report_list, list)  		i2c_hid_init_report(report, inbuf, ihid->bufsize); +	pm_runtime_put(&client->dev); +  	kfree(inbuf);  } @@ -554,7 +572,7 @@ static int i2c_hid_get_raw_report(struct hid_device *hid,  }  static int i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf, -		size_t count, unsigned char report_type) +		size_t count, unsigned char report_type, bool use_data)  {  	struct i2c_client *client = hid->driver_data;  	int report_id = buf[0]; @@ -568,9 +586,9 @@ static int i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf,  		count--;  	} -	ret = i2c_hid_set_report(client, +	ret = i2c_hid_set_or_send_report(client,  				report_type == HID_FEATURE_REPORT ? 0x03 : 0x02, -				report_id, buf, count); +				report_id, buf, count, use_data);  	if (report_id && ret >= 0)  		ret++; /* add report_id to the number of transfered bytes */ @@ -578,34 +596,27 @@ static int i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf,  	return ret;  } -static void i2c_hid_request(struct hid_device *hid, struct hid_report *rep, -		int reqtype) +static int i2c_hid_output_report(struct hid_device *hid, __u8 *buf, +		size_t count)  { -	struct i2c_client *client = hid->driver_data; -	char *buf; -	int ret; -	int len = i2c_hid_get_report_length(rep) - 2; - -	buf = kzalloc(len, GFP_KERNEL); -	if (!buf) -		return; +	return i2c_hid_output_raw_report(hid, buf, count, HID_OUTPUT_REPORT, +			false); +} +static int i2c_hid_raw_request(struct hid_device *hid, unsigned char reportnum, +			       __u8 *buf, size_t len, unsigned char rtype, +			       int reqtype) +{  	switch (reqtype) {  	case HID_REQ_GET_REPORT: -		ret = i2c_hid_get_raw_report(hid, rep->id, buf, len, rep->type); -		if (ret < 0) -			dev_err(&client->dev, "%s: unable to get report: %d\n", -				__func__, ret); -		else -			hid_input_report(hid, rep->type, buf, ret, 0); -		break; +		return i2c_hid_get_raw_report(hid, reportnum, buf, len, rtype);  	case HID_REQ_SET_REPORT: -		hid_output_report(rep, buf); -		i2c_hid_output_raw_report(hid, buf, len, rep->type); -		break; +		if (buf[0] != reportnum) +			return -EINVAL; +		return i2c_hid_output_raw_report(hid, buf, len, rtype, true); +	default: +		return -EIO;  	} - -	kfree(buf);  }  static int i2c_hid_parse(struct hid_device *hid) @@ -707,8 +718,8 @@ static int i2c_hid_open(struct hid_device *hid)  	mutex_lock(&i2c_hid_open_mut);  	if (!hid->open++) { -		ret = i2c_hid_set_power(client, I2C_HID_PWR_ON); -		if (ret) { +		ret = pm_runtime_get_sync(&client->dev); +		if (ret < 0) {  			hid->open--;  			goto done;  		} @@ -716,7 +727,7 @@ static int i2c_hid_open(struct hid_device *hid)  	}  done:  	mutex_unlock(&i2c_hid_open_mut); -	return ret; +	return ret < 0 ? ret : 0;  }  static void i2c_hid_close(struct hid_device *hid) @@ -733,7 +744,7 @@ static void i2c_hid_close(struct hid_device *hid)  		clear_bit(I2C_HID_STARTED, &ihid->flags);  		/* Save some power */ -		i2c_hid_set_power(client, I2C_HID_PWR_SLEEP); +		pm_runtime_put(&client->dev);  	}  	mutex_unlock(&i2c_hid_open_mut);  } @@ -742,19 +753,18 @@ static int i2c_hid_power(struct hid_device *hid, int lvl)  {  	struct i2c_client *client = hid->driver_data;  	struct i2c_hid *ihid = i2c_get_clientdata(client); -	int ret = 0;  	i2c_hid_dbg(ihid, "%s lvl:%d\n", __func__, lvl);  	switch (lvl) {  	case PM_HINT_FULLON: -		ret = i2c_hid_set_power(client, I2C_HID_PWR_ON); +		pm_runtime_get_sync(&client->dev);  		break;  	case PM_HINT_NORMAL: -		ret = i2c_hid_set_power(client, I2C_HID_PWR_SLEEP); +		pm_runtime_put(&client->dev);  		break;  	} -	return ret; +	return 0;  }  static struct hid_ll_driver i2c_hid_ll_driver = { @@ -764,7 +774,8 @@ static struct hid_ll_driver i2c_hid_ll_driver = {  	.open = i2c_hid_open,  	.close = i2c_hid_close,  	.power = i2c_hid_power, -	.request = i2c_hid_request, +	.output_report = i2c_hid_output_report, +	.raw_request = i2c_hid_raw_request,  };  static int i2c_hid_init_irq(struct i2c_client *client) @@ -796,34 +807,18 @@ static int i2c_hid_fetch_hid_descriptor(struct i2c_hid *ihid)  	unsigned int dsize;  	int ret; -	/* Fetch the length of HID description, retrieve the 4 first bytes: -	 * bytes 0-1 -> length -	 * bytes 2-3 -> bcdVersion (has to be 1.00) */ -	ret = i2c_hid_command(client, &hid_descr_cmd, ihid->hdesc_buffer, 4); - -	i2c_hid_dbg(ihid, "%s, ihid->hdesc_buffer: %4ph\n", __func__, -			ihid->hdesc_buffer); - +	/* i2c hid fetch using a fixed descriptor size (30 bytes) */ +	i2c_hid_dbg(ihid, "Fetching the HID descriptor\n"); +	ret = i2c_hid_command(client, &hid_descr_cmd, ihid->hdesc_buffer, +				sizeof(struct i2c_hid_desc));  	if (ret) { -		dev_err(&client->dev, -			"unable to fetch the size of HID descriptor (ret=%d)\n", -			ret); -		return -ENODEV; -	} - -	dsize = le16_to_cpu(hdesc->wHIDDescLength); -	/* -	 * the size of the HID descriptor should at least contain -	 * its size and the bcdVersion (4 bytes), and should not be greater -	 * than sizeof(struct i2c_hid_desc) as we directly fill this struct -	 * through i2c_hid_command. -	 */ -	if (dsize < 4 || dsize > sizeof(struct i2c_hid_desc)) { -		dev_err(&client->dev, "weird size of HID descriptor (%u)\n", -			dsize); +		dev_err(&client->dev, "hid_descr_cmd failed\n");  		return -ENODEV;  	} +	/* Validate the length of HID descriptor, the 4 first bytes: +	 * bytes 0-1 -> length +	 * bytes 2-3 -> bcdVersion (has to be 1.00) */  	/* check bcdVersion == 1.0 */  	if (le16_to_cpu(hdesc->bcdVersion) != 0x0100) {  		dev_err(&client->dev, @@ -832,17 +827,14 @@ static int i2c_hid_fetch_hid_descriptor(struct i2c_hid *ihid)  		return -ENODEV;  	} -	i2c_hid_dbg(ihid, "Fetching the HID descriptor\n"); - -	ret = i2c_hid_command(client, &hid_descr_cmd, ihid->hdesc_buffer, -				dsize); -	if (ret) { -		dev_err(&client->dev, "hid_descr_cmd Fail\n"); +	/* Descriptor length should be 30 bytes as per the specification */ +	dsize = le16_to_cpu(hdesc->wHIDDescLength); +	if (dsize != sizeof(struct i2c_hid_desc)) { +		dev_err(&client->dev, "weird size of HID descriptor (%u)\n", +			dsize);  		return -ENODEV;  	} -  	i2c_hid_dbg(ihid, "HID Descriptor: %*ph\n", dsize, ihid->hdesc_buffer); -  	return 0;  } @@ -854,9 +846,7 @@ static int i2c_hid_acpi_pdata(struct i2c_client *client,  		0xF7, 0xF6, 0xDF, 0x3C, 0x67, 0x42, 0x55, 0x45,  		0xAD, 0x05, 0xB3, 0x0A, 0x3D, 0x89, 0x38, 0xDE,  	}; -	struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL }; -	union acpi_object params[4], *obj; -	struct acpi_object_list input; +	union acpi_object *obj;  	struct acpi_device *adev;  	acpi_handle handle; @@ -864,36 +854,16 @@ static int i2c_hid_acpi_pdata(struct i2c_client *client,  	if (!handle || acpi_bus_get_device(handle, &adev))  		return -ENODEV; -	input.count = ARRAY_SIZE(params); -	input.pointer = params; - -	params[0].type = ACPI_TYPE_BUFFER; -	params[0].buffer.length = sizeof(i2c_hid_guid); -	params[0].buffer.pointer = i2c_hid_guid; -	params[1].type = ACPI_TYPE_INTEGER; -	params[1].integer.value = 1; -	params[2].type = ACPI_TYPE_INTEGER; -	params[2].integer.value = 1; /* HID function */ -	params[3].type = ACPI_TYPE_PACKAGE; -	params[3].package.count = 0; -	params[3].package.elements = NULL; - -	if (ACPI_FAILURE(acpi_evaluate_object(handle, "_DSM", &input, &buf))) { +	obj = acpi_evaluate_dsm_typed(handle, i2c_hid_guid, 1, 1, NULL, +				      ACPI_TYPE_INTEGER); +	if (!obj) {  		dev_err(&client->dev, "device _DSM execution failed\n");  		return -ENODEV;  	} -	obj = (union acpi_object *)buf.pointer; -	if (obj->type != ACPI_TYPE_INTEGER) { -		dev_err(&client->dev, "device _DSM returned invalid type: %d\n", -			obj->type); -		kfree(buf.pointer); -		return -EINVAL; -	} -  	pdata->hid_descriptor_address = obj->integer.value; +	ACPI_FREE(obj); -	kfree(buf.pointer);  	return 0;  } @@ -999,13 +969,17 @@ static int i2c_hid_probe(struct i2c_client *client,  	if (ret < 0)  		goto err; +	pm_runtime_get_noresume(&client->dev); +	pm_runtime_set_active(&client->dev); +	pm_runtime_enable(&client->dev); +  	ret = i2c_hid_fetch_hid_descriptor(ihid);  	if (ret < 0) -		goto err; +		goto err_pm;  	ret = i2c_hid_init_irq(client);  	if (ret < 0) -		goto err; +		goto err_pm;  	hid = hid_allocate_device();  	if (IS_ERR(hid)) { @@ -1017,10 +991,8 @@ static int i2c_hid_probe(struct i2c_client *client,  	hid->driver_data = client;  	hid->ll_driver = &i2c_hid_ll_driver; -	hid->hid_get_raw_report = i2c_hid_get_raw_report; -	hid->hid_output_raw_report = i2c_hid_output_raw_report;  	hid->dev.parent = &client->dev; -	ACPI_HANDLE_SET(&hid->dev, ACPI_HANDLE(&client->dev)); +	ACPI_COMPANION_SET(&hid->dev, ACPI_COMPANION(&client->dev));  	hid->bus = BUS_I2C;  	hid->version = le16_to_cpu(ihid->hdesc.bcdVersion);  	hid->vendor = le16_to_cpu(ihid->hdesc.wVendorID); @@ -1036,6 +1008,7 @@ static int i2c_hid_probe(struct i2c_client *client,  		goto err_mem_free;  	} +	pm_runtime_put(&client->dev);  	return 0;  err_mem_free: @@ -1044,6 +1017,10 @@ err_mem_free:  err_irq:  	free_irq(client->irq, ihid); +err_pm: +	pm_runtime_put_noidle(&client->dev); +	pm_runtime_disable(&client->dev); +  err:  	i2c_hid_free_buffers(ihid);  	kfree(ihid); @@ -1055,6 +1032,11 @@ static int i2c_hid_remove(struct i2c_client *client)  	struct i2c_hid *ihid = i2c_get_clientdata(client);  	struct hid_device *hid; +	pm_runtime_get_sync(&client->dev); +	pm_runtime_disable(&client->dev); +	pm_runtime_set_suspended(&client->dev); +	pm_runtime_put_noidle(&client->dev); +  	hid = ihid->hid;  	hid_destroy_device(hid); @@ -1073,6 +1055,7 @@ static int i2c_hid_suspend(struct device *dev)  {  	struct i2c_client *client = to_i2c_client(dev); +	disable_irq(client->irq);  	if (device_may_wakeup(&client->dev))  		enable_irq_wake(client->irq); @@ -1087,6 +1070,7 @@ static int i2c_hid_resume(struct device *dev)  	int ret;  	struct i2c_client *client = to_i2c_client(dev); +	enable_irq(client->irq);  	ret = i2c_hid_hwreset(client);  	if (ret)  		return ret; @@ -1098,7 +1082,31 @@ static int i2c_hid_resume(struct device *dev)  }  #endif -static SIMPLE_DEV_PM_OPS(i2c_hid_pm, i2c_hid_suspend, i2c_hid_resume); +#ifdef CONFIG_PM_RUNTIME +static int i2c_hid_runtime_suspend(struct device *dev) +{ +	struct i2c_client *client = to_i2c_client(dev); + +	i2c_hid_set_power(client, I2C_HID_PWR_SLEEP); +	disable_irq(client->irq); +	return 0; +} + +static int i2c_hid_runtime_resume(struct device *dev) +{ +	struct i2c_client *client = to_i2c_client(dev); + +	enable_irq(client->irq); +	i2c_hid_set_power(client, I2C_HID_PWR_ON); +	return 0; +} +#endif + +static const struct dev_pm_ops i2c_hid_pm = { +	SET_SYSTEM_SLEEP_PM_OPS(i2c_hid_suspend, i2c_hid_resume) +	SET_RUNTIME_PM_OPS(i2c_hid_runtime_suspend, i2c_hid_runtime_resume, +			   NULL) +};  static const struct i2c_device_id i2c_hid_id_table[] = {  	{ "hid", 0 },  | 
