xref: /linux/drivers/hid/intel-ish-hid/ishtp-hid.c (revision 762f99f4f3cb41a775b5157dd761217beba65873)
12025cf9eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
20b28cb4bSSrinivas Pandruvada /*
30b28cb4bSSrinivas Pandruvada  * ISHTP-HID glue driver.
40b28cb4bSSrinivas Pandruvada  *
50b28cb4bSSrinivas Pandruvada  * Copyright (c) 2012-2016, Intel Corporation.
60b28cb4bSSrinivas Pandruvada  */
70b28cb4bSSrinivas Pandruvada 
80b28cb4bSSrinivas Pandruvada #include <linux/hid.h>
97ab21842SSrinivas Pandruvada #include <linux/intel-ish-client-if.h>
100b28cb4bSSrinivas Pandruvada #include <uapi/linux/input.h>
110b28cb4bSSrinivas Pandruvada #include "ishtp-hid.h"
120b28cb4bSSrinivas Pandruvada 
130b28cb4bSSrinivas Pandruvada /**
140b28cb4bSSrinivas Pandruvada  * ishtp_hid_parse() - hid-core .parse() callback
150b28cb4bSSrinivas Pandruvada  * @hid:	hid device instance
160b28cb4bSSrinivas Pandruvada  *
170b28cb4bSSrinivas Pandruvada  * This function gets called during call to hid_add_device
180b28cb4bSSrinivas Pandruvada  *
190b28cb4bSSrinivas Pandruvada  * Return: 0 on success and non zero on error
200b28cb4bSSrinivas Pandruvada  */
210b28cb4bSSrinivas Pandruvada static int ishtp_hid_parse(struct hid_device *hid)
220b28cb4bSSrinivas Pandruvada {
230b28cb4bSSrinivas Pandruvada 	struct ishtp_hid_data *hid_data =  hid->driver_data;
240b28cb4bSSrinivas Pandruvada 	struct ishtp_cl_data *client_data = hid_data->client_data;
250b28cb4bSSrinivas Pandruvada 	int rv;
260b28cb4bSSrinivas Pandruvada 
270b28cb4bSSrinivas Pandruvada 	rv = hid_parse_report(hid, client_data->report_descr[hid_data->index],
280b28cb4bSSrinivas Pandruvada 			      client_data->report_descr_size[hid_data->index]);
290b28cb4bSSrinivas Pandruvada 	if (rv)
300b28cb4bSSrinivas Pandruvada 		return	rv;
310b28cb4bSSrinivas Pandruvada 
320b28cb4bSSrinivas Pandruvada 	return 0;
330b28cb4bSSrinivas Pandruvada }
340b28cb4bSSrinivas Pandruvada 
350b28cb4bSSrinivas Pandruvada /* Empty callbacks with success return code */
360b28cb4bSSrinivas Pandruvada static int ishtp_hid_start(struct hid_device *hid)
370b28cb4bSSrinivas Pandruvada {
380b28cb4bSSrinivas Pandruvada 	return 0;
390b28cb4bSSrinivas Pandruvada }
400b28cb4bSSrinivas Pandruvada 
410b28cb4bSSrinivas Pandruvada static void ishtp_hid_stop(struct hid_device *hid)
420b28cb4bSSrinivas Pandruvada {
430b28cb4bSSrinivas Pandruvada }
440b28cb4bSSrinivas Pandruvada 
450b28cb4bSSrinivas Pandruvada static int ishtp_hid_open(struct hid_device *hid)
460b28cb4bSSrinivas Pandruvada {
470b28cb4bSSrinivas Pandruvada 	return 0;
480b28cb4bSSrinivas Pandruvada }
490b28cb4bSSrinivas Pandruvada 
500b28cb4bSSrinivas Pandruvada static void ishtp_hid_close(struct hid_device *hid)
510b28cb4bSSrinivas Pandruvada {
520b28cb4bSSrinivas Pandruvada }
530b28cb4bSSrinivas Pandruvada 
54e19595fcSHyungwoo Yang static int ishtp_raw_request(struct hid_device *hid, unsigned char reportnum,
55e19595fcSHyungwoo Yang 			     __u8 *buf, size_t len, unsigned char rtype,
56e19595fcSHyungwoo Yang 			     int reqtype)
570b28cb4bSSrinivas Pandruvada {
58e19595fcSHyungwoo Yang 	struct ishtp_hid_data *hid_data =  hid->driver_data;
59e19595fcSHyungwoo Yang 	char *ishtp_buf = NULL;
60e19595fcSHyungwoo Yang 	size_t ishtp_buf_len;
61e19595fcSHyungwoo Yang 	unsigned int header_size = sizeof(struct hostif_msg);
62e19595fcSHyungwoo Yang 
63e19595fcSHyungwoo Yang 	if (rtype == HID_OUTPUT_REPORT)
64e19595fcSHyungwoo Yang 		return -EINVAL;
65e19595fcSHyungwoo Yang 
66e19595fcSHyungwoo Yang 	hid_data->request_done = false;
67e19595fcSHyungwoo Yang 	switch (reqtype) {
68e19595fcSHyungwoo Yang 	case HID_REQ_GET_REPORT:
69e19595fcSHyungwoo Yang 		hid_data->raw_buf = buf;
70e19595fcSHyungwoo Yang 		hid_data->raw_buf_size = len;
71e19595fcSHyungwoo Yang 		hid_data->raw_get_req = true;
72e19595fcSHyungwoo Yang 
73e19595fcSHyungwoo Yang 		hid_ishtp_get_report(hid, reportnum, rtype);
74e19595fcSHyungwoo Yang 		break;
75e19595fcSHyungwoo Yang 	case HID_REQ_SET_REPORT:
76e19595fcSHyungwoo Yang 		/*
77e19595fcSHyungwoo Yang 		 * Spare 7 bytes for 64b accesses through
78e19595fcSHyungwoo Yang 		 * get/put_unaligned_le64()
79e19595fcSHyungwoo Yang 		 */
80e19595fcSHyungwoo Yang 		ishtp_buf_len = len + header_size;
81e19595fcSHyungwoo Yang 		ishtp_buf = kzalloc(ishtp_buf_len + 7, GFP_KERNEL);
82e19595fcSHyungwoo Yang 		if (!ishtp_buf)
83e19595fcSHyungwoo Yang 			return -ENOMEM;
84e19595fcSHyungwoo Yang 
85e19595fcSHyungwoo Yang 		memcpy(ishtp_buf + header_size, buf, len);
86e19595fcSHyungwoo Yang 		hid_ishtp_set_feature(hid, ishtp_buf, ishtp_buf_len, reportnum);
87e19595fcSHyungwoo Yang 		kfree(ishtp_buf);
88e19595fcSHyungwoo Yang 		break;
89e19595fcSHyungwoo Yang 	}
90e19595fcSHyungwoo Yang 
91e19595fcSHyungwoo Yang 	hid_hw_wait(hid);
92e19595fcSHyungwoo Yang 
93e19595fcSHyungwoo Yang 	return len;
940b28cb4bSSrinivas Pandruvada }
950b28cb4bSSrinivas Pandruvada 
960b28cb4bSSrinivas Pandruvada /**
970b28cb4bSSrinivas Pandruvada  * ishtp_hid_request() - hid-core .request() callback
980b28cb4bSSrinivas Pandruvada  * @hid:	hid device instance
990b28cb4bSSrinivas Pandruvada  * @rep:	pointer to hid_report
1000b28cb4bSSrinivas Pandruvada  * @reqtype:	type of req. [GET|SET]_REPORT
1010b28cb4bSSrinivas Pandruvada  *
1020b28cb4bSSrinivas Pandruvada  * This function is used to set/get feaure/input report.
1030b28cb4bSSrinivas Pandruvada  */
1040b28cb4bSSrinivas Pandruvada static void ishtp_hid_request(struct hid_device *hid, struct hid_report *rep,
1050b28cb4bSSrinivas Pandruvada 	int reqtype)
1060b28cb4bSSrinivas Pandruvada {
1070b28cb4bSSrinivas Pandruvada 	struct ishtp_hid_data *hid_data =  hid->driver_data;
1080b28cb4bSSrinivas Pandruvada 	/* the specific report length, just HID part of it */
1090b28cb4bSSrinivas Pandruvada 	unsigned int len = ((rep->size - 1) >> 3) + 1 + (rep->id > 0);
1100b28cb4bSSrinivas Pandruvada 	char *buf;
1110b28cb4bSSrinivas Pandruvada 	unsigned int header_size = sizeof(struct hostif_msg);
1120b28cb4bSSrinivas Pandruvada 
1130b28cb4bSSrinivas Pandruvada 	len += header_size;
1140b28cb4bSSrinivas Pandruvada 
1150b28cb4bSSrinivas Pandruvada 	hid_data->request_done = false;
1160b28cb4bSSrinivas Pandruvada 	switch (reqtype) {
1170b28cb4bSSrinivas Pandruvada 	case HID_REQ_GET_REPORT:
118e19595fcSHyungwoo Yang 		hid_data->raw_get_req = false;
1190b28cb4bSSrinivas Pandruvada 		hid_ishtp_get_report(hid, rep->id, rep->type);
1200b28cb4bSSrinivas Pandruvada 		break;
1210b28cb4bSSrinivas Pandruvada 	case HID_REQ_SET_REPORT:
1220b28cb4bSSrinivas Pandruvada 		/*
1230b28cb4bSSrinivas Pandruvada 		 * Spare 7 bytes for 64b accesses through
1240b28cb4bSSrinivas Pandruvada 		 * get/put_unaligned_le64()
1250b28cb4bSSrinivas Pandruvada 		 */
1260b28cb4bSSrinivas Pandruvada 		buf = kzalloc(len + 7, GFP_KERNEL);
1270b28cb4bSSrinivas Pandruvada 		if (!buf)
1280b28cb4bSSrinivas Pandruvada 			return;
1290b28cb4bSSrinivas Pandruvada 
1300b28cb4bSSrinivas Pandruvada 		hid_output_report(rep, buf + header_size);
1310b28cb4bSSrinivas Pandruvada 		hid_ishtp_set_feature(hid, buf, len, rep->id);
1320b28cb4bSSrinivas Pandruvada 		kfree(buf);
1330b28cb4bSSrinivas Pandruvada 		break;
1340b28cb4bSSrinivas Pandruvada 	}
1350b28cb4bSSrinivas Pandruvada }
1360b28cb4bSSrinivas Pandruvada 
1370b28cb4bSSrinivas Pandruvada /**
1380b28cb4bSSrinivas Pandruvada  * ishtp_wait_for_response() - hid-core .wait() callback
1390b28cb4bSSrinivas Pandruvada  * @hid:	hid device instance
1400b28cb4bSSrinivas Pandruvada  *
1410b28cb4bSSrinivas Pandruvada  * This function is used to wait after get feaure/input report.
1420b28cb4bSSrinivas Pandruvada  *
1430b28cb4bSSrinivas Pandruvada  * Return: 0 on success and non zero on error
1440b28cb4bSSrinivas Pandruvada  */
1450b28cb4bSSrinivas Pandruvada static int ishtp_wait_for_response(struct hid_device *hid)
1460b28cb4bSSrinivas Pandruvada {
1470b28cb4bSSrinivas Pandruvada 	struct ishtp_hid_data *hid_data =  hid->driver_data;
1480b28cb4bSSrinivas Pandruvada 	int rv;
1490b28cb4bSSrinivas Pandruvada 
1500b28cb4bSSrinivas Pandruvada 	hid_ishtp_trace(client_data,  "%s hid %p\n", __func__, hid);
1510b28cb4bSSrinivas Pandruvada 
1520b28cb4bSSrinivas Pandruvada 	rv = ishtp_hid_link_ready_wait(hid_data->client_data);
1530b28cb4bSSrinivas Pandruvada 	if (rv)
1540b28cb4bSSrinivas Pandruvada 		return rv;
1550b28cb4bSSrinivas Pandruvada 
1560b28cb4bSSrinivas Pandruvada 	if (!hid_data->request_done)
1570b28cb4bSSrinivas Pandruvada 		wait_event_interruptible_timeout(hid_data->hid_wait,
1580b28cb4bSSrinivas Pandruvada 					hid_data->request_done, 3 * HZ);
1590b28cb4bSSrinivas Pandruvada 
1600b28cb4bSSrinivas Pandruvada 	if (!hid_data->request_done) {
1610b28cb4bSSrinivas Pandruvada 		hid_err(hid,
1620b28cb4bSSrinivas Pandruvada 			"timeout waiting for response from ISHTP device\n");
1630b28cb4bSSrinivas Pandruvada 		return -ETIMEDOUT;
1640b28cb4bSSrinivas Pandruvada 	}
1650b28cb4bSSrinivas Pandruvada 	hid_ishtp_trace(client_data,  "%s hid %p done\n", __func__, hid);
1660b28cb4bSSrinivas Pandruvada 
1670b28cb4bSSrinivas Pandruvada 	hid_data->request_done = false;
1680b28cb4bSSrinivas Pandruvada 
1690b28cb4bSSrinivas Pandruvada 	return 0;
1700b28cb4bSSrinivas Pandruvada }
1710b28cb4bSSrinivas Pandruvada 
1720b28cb4bSSrinivas Pandruvada /**
1730b28cb4bSSrinivas Pandruvada  * ishtp_hid_wakeup() - Wakeup caller
1740b28cb4bSSrinivas Pandruvada  * @hid:	hid device instance
1750b28cb4bSSrinivas Pandruvada  *
1760b28cb4bSSrinivas Pandruvada  * This function will wakeup caller waiting for Get/Set feature report
1770b28cb4bSSrinivas Pandruvada  */
1780b28cb4bSSrinivas Pandruvada void ishtp_hid_wakeup(struct hid_device *hid)
1790b28cb4bSSrinivas Pandruvada {
1800b28cb4bSSrinivas Pandruvada 	struct ishtp_hid_data *hid_data = hid->driver_data;
1810b28cb4bSSrinivas Pandruvada 
1820b28cb4bSSrinivas Pandruvada 	hid_data->request_done = true;
1830b28cb4bSSrinivas Pandruvada 	wake_up_interruptible(&hid_data->hid_wait);
1840b28cb4bSSrinivas Pandruvada }
1850b28cb4bSSrinivas Pandruvada 
1860b28cb4bSSrinivas Pandruvada static struct hid_ll_driver ishtp_hid_ll_driver = {
1870b28cb4bSSrinivas Pandruvada 	.parse = ishtp_hid_parse,
1880b28cb4bSSrinivas Pandruvada 	.start = ishtp_hid_start,
1890b28cb4bSSrinivas Pandruvada 	.stop = ishtp_hid_stop,
1900b28cb4bSSrinivas Pandruvada 	.open = ishtp_hid_open,
1910b28cb4bSSrinivas Pandruvada 	.close = ishtp_hid_close,
1920b28cb4bSSrinivas Pandruvada 	.request = ishtp_hid_request,
1930b28cb4bSSrinivas Pandruvada 	.wait = ishtp_wait_for_response,
1940b28cb4bSSrinivas Pandruvada 	.raw_request = ishtp_raw_request
1950b28cb4bSSrinivas Pandruvada };
1960b28cb4bSSrinivas Pandruvada 
1970b28cb4bSSrinivas Pandruvada /**
1980b28cb4bSSrinivas Pandruvada  * ishtp_hid_probe() - hid register ll driver
1990b28cb4bSSrinivas Pandruvada  * @cur_hid_dev:	Index of hid device calling to register
2000b28cb4bSSrinivas Pandruvada  * @client_data:	Client data pointer
2010b28cb4bSSrinivas Pandruvada  *
2020b28cb4bSSrinivas Pandruvada  * This function is used to allocate and add HID device.
2030b28cb4bSSrinivas Pandruvada  *
2040b28cb4bSSrinivas Pandruvada  * Return: 0 on success, non zero on error
2050b28cb4bSSrinivas Pandruvada  */
2060b28cb4bSSrinivas Pandruvada int ishtp_hid_probe(unsigned int cur_hid_dev,
2070b28cb4bSSrinivas Pandruvada 		    struct ishtp_cl_data *client_data)
2080b28cb4bSSrinivas Pandruvada {
2090b28cb4bSSrinivas Pandruvada 	int rv;
2100b28cb4bSSrinivas Pandruvada 	struct hid_device *hid;
2110b28cb4bSSrinivas Pandruvada 	struct ishtp_hid_data *hid_data;
2120b28cb4bSSrinivas Pandruvada 
2130b28cb4bSSrinivas Pandruvada 	hid = hid_allocate_device();
2149735d9deSJing Xiangfeng 	if (IS_ERR(hid))
2159735d9deSJing Xiangfeng 		return PTR_ERR(hid);
2160b28cb4bSSrinivas Pandruvada 
2170b28cb4bSSrinivas Pandruvada 	hid_data = kzalloc(sizeof(*hid_data), GFP_KERNEL);
2180b28cb4bSSrinivas Pandruvada 	if (!hid_data) {
2190b28cb4bSSrinivas Pandruvada 		rv = -ENOMEM;
2200b28cb4bSSrinivas Pandruvada 		goto err_hid_data;
2210b28cb4bSSrinivas Pandruvada 	}
2220b28cb4bSSrinivas Pandruvada 
2230b28cb4bSSrinivas Pandruvada 	hid_data->index = cur_hid_dev;
2240b28cb4bSSrinivas Pandruvada 	hid_data->client_data = client_data;
2250b28cb4bSSrinivas Pandruvada 	init_waitqueue_head(&hid_data->hid_wait);
2260b28cb4bSSrinivas Pandruvada 
2270b28cb4bSSrinivas Pandruvada 	hid->driver_data = hid_data;
2280b28cb4bSSrinivas Pandruvada 
2290b28cb4bSSrinivas Pandruvada 	client_data->hid_sensor_hubs[cur_hid_dev] = hid;
2300b28cb4bSSrinivas Pandruvada 
2310b28cb4bSSrinivas Pandruvada 	hid->ll_driver = &ishtp_hid_ll_driver;
2320b28cb4bSSrinivas Pandruvada 	hid->bus = BUS_INTEL_ISHTP;
2337ab21842SSrinivas Pandruvada 	hid->dev.parent = ishtp_device(client_data->cl_device);
2347ab21842SSrinivas Pandruvada 
2350b28cb4bSSrinivas Pandruvada 	hid->version = le16_to_cpu(ISH_HID_VERSION);
2361578461aSSrinivas Pandruvada 	hid->vendor = le16_to_cpu(client_data->hid_devices[cur_hid_dev].vid);
2371578461aSSrinivas Pandruvada 	hid->product = le16_to_cpu(client_data->hid_devices[cur_hid_dev].pid);
2385299a92aSNicolas Iooss 	snprintf(hid->name, sizeof(hid->name), "%s %04X:%04X", "hid-ishtp",
2390b28cb4bSSrinivas Pandruvada 		hid->vendor, hid->product);
2400b28cb4bSSrinivas Pandruvada 
2410b28cb4bSSrinivas Pandruvada 	rv = hid_add_device(hid);
2420b28cb4bSSrinivas Pandruvada 	if (rv)
2430b28cb4bSSrinivas Pandruvada 		goto err_hid_device;
2440b28cb4bSSrinivas Pandruvada 
2450b28cb4bSSrinivas Pandruvada 	hid_ishtp_trace(client_data,  "%s allocated hid %p\n", __func__, hid);
2460b28cb4bSSrinivas Pandruvada 
2470b28cb4bSSrinivas Pandruvada 	return 0;
2480b28cb4bSSrinivas Pandruvada 
2490b28cb4bSSrinivas Pandruvada err_hid_device:
2500b28cb4bSSrinivas Pandruvada 	kfree(hid_data);
2510b28cb4bSSrinivas Pandruvada err_hid_data:
2526e0856d3SPan Bian 	hid_destroy_device(hid);
2530b28cb4bSSrinivas Pandruvada 	return rv;
2540b28cb4bSSrinivas Pandruvada }
2550b28cb4bSSrinivas Pandruvada 
2560b28cb4bSSrinivas Pandruvada /**
257*99c6f965SLee Jones  * ishtp_hid_remove() - Remove registered hid device
2580b28cb4bSSrinivas Pandruvada  * @client_data:	client data pointer
2590b28cb4bSSrinivas Pandruvada  *
2600b28cb4bSSrinivas Pandruvada  * This function is used to destroy allocatd HID device.
2610b28cb4bSSrinivas Pandruvada  */
2620b28cb4bSSrinivas Pandruvada void ishtp_hid_remove(struct ishtp_cl_data *client_data)
2630b28cb4bSSrinivas Pandruvada {
2640b28cb4bSSrinivas Pandruvada 	int i;
2650b28cb4bSSrinivas Pandruvada 
2660b28cb4bSSrinivas Pandruvada 	for (i = 0; i < client_data->num_hid_devices; ++i) {
2670b28cb4bSSrinivas Pandruvada 		if (client_data->hid_sensor_hubs[i]) {
2680b28cb4bSSrinivas Pandruvada 			kfree(client_data->hid_sensor_hubs[i]->driver_data);
2690b28cb4bSSrinivas Pandruvada 			hid_destroy_device(client_data->hid_sensor_hubs[i]);
2700b28cb4bSSrinivas Pandruvada 			client_data->hid_sensor_hubs[i] = NULL;
2710b28cb4bSSrinivas Pandruvada 		}
2720b28cb4bSSrinivas Pandruvada 	}
2730b28cb4bSSrinivas Pandruvada }
274