12025cf9eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
20b28cb4bSSrinivas Pandruvada /*
30b28cb4bSSrinivas Pandruvada  * ISHTP client driver for HID (ISH)
40b28cb4bSSrinivas Pandruvada  *
50b28cb4bSSrinivas Pandruvada  * Copyright (c) 2014-2016, Intel Corporation.
60b28cb4bSSrinivas Pandruvada  */
70b28cb4bSSrinivas Pandruvada 
80b28cb4bSSrinivas Pandruvada #include <linux/module.h>
90b28cb4bSSrinivas Pandruvada #include <linux/hid.h>
107ab21842SSrinivas Pandruvada #include <linux/intel-ish-client-if.h>
110b28cb4bSSrinivas Pandruvada #include <linux/sched.h>
120b28cb4bSSrinivas Pandruvada #include "ishtp-hid.h"
130b28cb4bSSrinivas Pandruvada 
14a2e7aa05SLee Jones /* ISH Transport protocol (ISHTP in short) GUID */
15bf9167a8SArnd Bergmann static const struct ishtp_device_id hid_ishtp_id_table[] = {
16bf9167a8SArnd Bergmann 	{ .guid = GUID_INIT(0x33AECD58, 0xB679, 0x4E54,
17bf9167a8SArnd Bergmann 		  0x9B, 0xD9, 0xA0, 0x4D, 0x34, 0xF0, 0xC2, 0x26), },
18bf9167a8SArnd Bergmann 	{ }
19bf9167a8SArnd Bergmann };
20bf9167a8SArnd Bergmann MODULE_DEVICE_TABLE(ishtp, hid_ishtp_id_table);
21a2e7aa05SLee Jones 
220b28cb4bSSrinivas Pandruvada /* Rx ring buffer pool size */
230b28cb4bSSrinivas Pandruvada #define HID_CL_RX_RING_SIZE	32
240b28cb4bSSrinivas Pandruvada #define HID_CL_TX_RING_SIZE	16
250b28cb4bSSrinivas Pandruvada 
267ab21842SSrinivas Pandruvada #define cl_data_to_dev(client_data) ishtp_device(client_data->cl_device)
277ab21842SSrinivas Pandruvada 
280b28cb4bSSrinivas Pandruvada /**
29fb42b1daSLee Jones  * report_bad_packet() - Report bad packets
300b28cb4bSSrinivas Pandruvada  * @hid_ishtp_cl:	Client instance to get stats
310b28cb4bSSrinivas Pandruvada  * @recv_buf:		Raw received host interface message
320b28cb4bSSrinivas Pandruvada  * @cur_pos:		Current position index in payload
330b28cb4bSSrinivas Pandruvada  * @payload_len:	Length of payload expected
340b28cb4bSSrinivas Pandruvada  *
350b28cb4bSSrinivas Pandruvada  * Dumps error in case bad packet is received
360b28cb4bSSrinivas Pandruvada  */
report_bad_packet(struct ishtp_cl * hid_ishtp_cl,void * recv_buf,size_t cur_pos,size_t payload_len)370b28cb4bSSrinivas Pandruvada static void report_bad_packet(struct ishtp_cl *hid_ishtp_cl, void *recv_buf,
380b28cb4bSSrinivas Pandruvada 			      size_t cur_pos,  size_t payload_len)
390b28cb4bSSrinivas Pandruvada {
400b28cb4bSSrinivas Pandruvada 	struct hostif_msg *recv_msg = recv_buf;
4129b06d12SSrinivas Pandruvada 	struct ishtp_cl_data *client_data = ishtp_get_client_data(hid_ishtp_cl);
420b28cb4bSSrinivas Pandruvada 
437ab21842SSrinivas Pandruvada 	dev_err(cl_data_to_dev(client_data), "[hid-ish]: BAD packet %02X\n"
440b28cb4bSSrinivas Pandruvada 		"total_bad=%u cur_pos=%u\n"
450b28cb4bSSrinivas Pandruvada 		"[%02X %02X %02X %02X]\n"
460b28cb4bSSrinivas Pandruvada 		"payload_len=%u\n"
470b28cb4bSSrinivas Pandruvada 		"multi_packet_cnt=%u\n"
480b28cb4bSSrinivas Pandruvada 		"is_response=%02X\n",
490b28cb4bSSrinivas Pandruvada 		recv_msg->hdr.command, client_data->bad_recv_cnt,
500b28cb4bSSrinivas Pandruvada 		(unsigned int)cur_pos,
510b28cb4bSSrinivas Pandruvada 		((unsigned char *)recv_msg)[0], ((unsigned char *)recv_msg)[1],
520b28cb4bSSrinivas Pandruvada 		((unsigned char *)recv_msg)[2], ((unsigned char *)recv_msg)[3],
530b28cb4bSSrinivas Pandruvada 		(unsigned int)payload_len, client_data->multi_packet_cnt,
540b28cb4bSSrinivas Pandruvada 		recv_msg->hdr.command & ~CMD_MASK);
550b28cb4bSSrinivas Pandruvada }
560b28cb4bSSrinivas Pandruvada 
570b28cb4bSSrinivas Pandruvada /**
580b28cb4bSSrinivas Pandruvada  * process_recv() - Received and parse incoming packet
590b28cb4bSSrinivas Pandruvada  * @hid_ishtp_cl:	Client instance to get stats
600b28cb4bSSrinivas Pandruvada  * @recv_buf:		Raw received host interface message
610b28cb4bSSrinivas Pandruvada  * @data_len:		length of the message
620b28cb4bSSrinivas Pandruvada  *
630b28cb4bSSrinivas Pandruvada  * Parse the incoming packet. If it is a response packet then it will update
640b28cb4bSSrinivas Pandruvada  * per instance flags and wake up the caller waiting to for the response.
650b28cb4bSSrinivas Pandruvada  */
process_recv(struct ishtp_cl * hid_ishtp_cl,void * recv_buf,size_t data_len)660b28cb4bSSrinivas Pandruvada static void process_recv(struct ishtp_cl *hid_ishtp_cl, void *recv_buf,
670b28cb4bSSrinivas Pandruvada 			 size_t data_len)
680b28cb4bSSrinivas Pandruvada {
690b28cb4bSSrinivas Pandruvada 	struct hostif_msg *recv_msg;
700b28cb4bSSrinivas Pandruvada 	unsigned char *payload;
710b28cb4bSSrinivas Pandruvada 	struct device_info *dev_info;
720b28cb4bSSrinivas Pandruvada 	int i, j;
73e19595fcSHyungwoo Yang 	size_t	payload_len, total_len, cur_pos, raw_len;
740b28cb4bSSrinivas Pandruvada 	int report_type;
750b28cb4bSSrinivas Pandruvada 	struct report_list *reports_list;
760b28cb4bSSrinivas Pandruvada 	char *reports;
770b28cb4bSSrinivas Pandruvada 	size_t report_len;
7829b06d12SSrinivas Pandruvada 	struct ishtp_cl_data *client_data = ishtp_get_client_data(hid_ishtp_cl);
790b28cb4bSSrinivas Pandruvada 	int curr_hid_dev = client_data->cur_hid_dev;
80e19595fcSHyungwoo Yang 	struct ishtp_hid_data *hid_data = NULL;
81e19595fcSHyungwoo Yang 	struct hid_device *hid = NULL;
820b28cb4bSSrinivas Pandruvada 
8337ba3c35SHans de Goede 	payload = recv_buf + sizeof(struct hostif_msg_hdr);
8437ba3c35SHans de Goede 	total_len = data_len;
8537ba3c35SHans de Goede 	cur_pos = 0;
8637ba3c35SHans de Goede 
8737ba3c35SHans de Goede 	do {
8837ba3c35SHans de Goede 		if (cur_pos + sizeof(struct hostif_msg) > total_len) {
897ab21842SSrinivas Pandruvada 			dev_err(cl_data_to_dev(client_data),
900b28cb4bSSrinivas Pandruvada 				"[hid-ish]: error, received %u which is less than data header %u\n",
910b28cb4bSSrinivas Pandruvada 				(unsigned int)data_len,
920b28cb4bSSrinivas Pandruvada 				(unsigned int)sizeof(struct hostif_msg_hdr));
930b28cb4bSSrinivas Pandruvada 			++client_data->bad_recv_cnt;
9429b06d12SSrinivas Pandruvada 			ish_hw_reset(ishtp_get_ishtp_device(hid_ishtp_cl));
9537ba3c35SHans de Goede 			break;
960b28cb4bSSrinivas Pandruvada 		}
970b28cb4bSSrinivas Pandruvada 
980b28cb4bSSrinivas Pandruvada 		recv_msg = (struct hostif_msg *)(recv_buf + cur_pos);
990b28cb4bSSrinivas Pandruvada 		payload_len = recv_msg->hdr.size;
1000b28cb4bSSrinivas Pandruvada 
1010b28cb4bSSrinivas Pandruvada 		/* Sanity checks */
1020b28cb4bSSrinivas Pandruvada 		if (cur_pos + payload_len + sizeof(struct hostif_msg) >
1030b28cb4bSSrinivas Pandruvada 				total_len) {
1040b28cb4bSSrinivas Pandruvada 			++client_data->bad_recv_cnt;
1050b28cb4bSSrinivas Pandruvada 			report_bad_packet(hid_ishtp_cl, recv_msg, cur_pos,
1060b28cb4bSSrinivas Pandruvada 					  payload_len);
10729b06d12SSrinivas Pandruvada 			ish_hw_reset(ishtp_get_ishtp_device(hid_ishtp_cl));
1080b28cb4bSSrinivas Pandruvada 			break;
1090b28cb4bSSrinivas Pandruvada 		}
1100b28cb4bSSrinivas Pandruvada 
1110b28cb4bSSrinivas Pandruvada 		hid_ishtp_trace(client_data,  "%s %d\n",
1120b28cb4bSSrinivas Pandruvada 				__func__, recv_msg->hdr.command & CMD_MASK);
1130b28cb4bSSrinivas Pandruvada 
1140b28cb4bSSrinivas Pandruvada 		switch (recv_msg->hdr.command & CMD_MASK) {
1150b28cb4bSSrinivas Pandruvada 		case HOSTIF_DM_ENUM_DEVICES:
1160b28cb4bSSrinivas Pandruvada 			if ((!(recv_msg->hdr.command & ~CMD_MASK) ||
1170b28cb4bSSrinivas Pandruvada 					client_data->init_done)) {
1180b28cb4bSSrinivas Pandruvada 				++client_data->bad_recv_cnt;
1190b28cb4bSSrinivas Pandruvada 				report_bad_packet(hid_ishtp_cl, recv_msg,
1200b28cb4bSSrinivas Pandruvada 						  cur_pos,
1210b28cb4bSSrinivas Pandruvada 						  payload_len);
12229b06d12SSrinivas Pandruvada 				ish_hw_reset(ishtp_get_ishtp_device(hid_ishtp_cl));
1230b28cb4bSSrinivas Pandruvada 				break;
1240b28cb4bSSrinivas Pandruvada 			}
1250b28cb4bSSrinivas Pandruvada 			client_data->hid_dev_count = (unsigned int)*payload;
1260b28cb4bSSrinivas Pandruvada 			if (!client_data->hid_devices)
127a86854d0SKees Cook 				client_data->hid_devices = devm_kcalloc(
1287ab21842SSrinivas Pandruvada 						cl_data_to_dev(client_data),
129a86854d0SKees Cook 						client_data->hid_dev_count,
1300b28cb4bSSrinivas Pandruvada 						sizeof(struct device_info),
1310b28cb4bSSrinivas Pandruvada 						GFP_KERNEL);
1320b28cb4bSSrinivas Pandruvada 			if (!client_data->hid_devices) {
1337ab21842SSrinivas Pandruvada 				dev_err(cl_data_to_dev(client_data),
1340b28cb4bSSrinivas Pandruvada 				"Mem alloc failed for hid device info\n");
1350b28cb4bSSrinivas Pandruvada 				wake_up_interruptible(&client_data->init_wait);
1360b28cb4bSSrinivas Pandruvada 				break;
1370b28cb4bSSrinivas Pandruvada 			}
1380b28cb4bSSrinivas Pandruvada 			for (i = 0; i < client_data->hid_dev_count; ++i) {
1390b28cb4bSSrinivas Pandruvada 				if (1 + sizeof(struct device_info) * i >=
1400b28cb4bSSrinivas Pandruvada 						payload_len) {
1417ab21842SSrinivas Pandruvada 					dev_err(cl_data_to_dev(client_data),
142318fc2a8SArnd Bergmann 						"[hid-ish]: [ENUM_DEVICES]: content size %zu is bigger than payload_len %zu\n",
1430b28cb4bSSrinivas Pandruvada 						1 + sizeof(struct device_info)
144318fc2a8SArnd Bergmann 						* i, payload_len);
1450b28cb4bSSrinivas Pandruvada 				}
1460b28cb4bSSrinivas Pandruvada 
1470b28cb4bSSrinivas Pandruvada 				if (1 + sizeof(struct device_info) * i >=
1480b28cb4bSSrinivas Pandruvada 						data_len)
1490b28cb4bSSrinivas Pandruvada 					break;
1500b28cb4bSSrinivas Pandruvada 
1510b28cb4bSSrinivas Pandruvada 				dev_info = (struct device_info *)(payload + 1 +
1520b28cb4bSSrinivas Pandruvada 					sizeof(struct device_info) * i);
1530b28cb4bSSrinivas Pandruvada 				if (client_data->hid_devices)
1540b28cb4bSSrinivas Pandruvada 					memcpy(client_data->hid_devices + i,
1550b28cb4bSSrinivas Pandruvada 					       dev_info,
1560b28cb4bSSrinivas Pandruvada 					       sizeof(struct device_info));
1570b28cb4bSSrinivas Pandruvada 			}
1580b28cb4bSSrinivas Pandruvada 
1590b28cb4bSSrinivas Pandruvada 			client_data->enum_devices_done = true;
1600b28cb4bSSrinivas Pandruvada 			wake_up_interruptible(&client_data->init_wait);
1610b28cb4bSSrinivas Pandruvada 
1620b28cb4bSSrinivas Pandruvada 			break;
1630b28cb4bSSrinivas Pandruvada 
1640b28cb4bSSrinivas Pandruvada 		case HOSTIF_GET_HID_DESCRIPTOR:
1650b28cb4bSSrinivas Pandruvada 			if ((!(recv_msg->hdr.command & ~CMD_MASK) ||
1660b28cb4bSSrinivas Pandruvada 					client_data->init_done)) {
1670b28cb4bSSrinivas Pandruvada 				++client_data->bad_recv_cnt;
1680b28cb4bSSrinivas Pandruvada 				report_bad_packet(hid_ishtp_cl, recv_msg,
1690b28cb4bSSrinivas Pandruvada 						  cur_pos,
1700b28cb4bSSrinivas Pandruvada 						  payload_len);
17129b06d12SSrinivas Pandruvada 				ish_hw_reset(ishtp_get_ishtp_device(hid_ishtp_cl));
1720b28cb4bSSrinivas Pandruvada 				break;
1730b28cb4bSSrinivas Pandruvada 			}
1740b28cb4bSSrinivas Pandruvada 			if (!client_data->hid_descr[curr_hid_dev])
1750b28cb4bSSrinivas Pandruvada 				client_data->hid_descr[curr_hid_dev] =
1767ab21842SSrinivas Pandruvada 				devm_kmalloc(cl_data_to_dev(client_data),
1770b28cb4bSSrinivas Pandruvada 					     payload_len, GFP_KERNEL);
1780b28cb4bSSrinivas Pandruvada 			if (client_data->hid_descr[curr_hid_dev]) {
1790b28cb4bSSrinivas Pandruvada 				memcpy(client_data->hid_descr[curr_hid_dev],
1800b28cb4bSSrinivas Pandruvada 				       payload, payload_len);
1810b28cb4bSSrinivas Pandruvada 				client_data->hid_descr_size[curr_hid_dev] =
1820b28cb4bSSrinivas Pandruvada 					payload_len;
1830b28cb4bSSrinivas Pandruvada 				client_data->hid_descr_done = true;
1840b28cb4bSSrinivas Pandruvada 			}
1850b28cb4bSSrinivas Pandruvada 			wake_up_interruptible(&client_data->init_wait);
1860b28cb4bSSrinivas Pandruvada 
1870b28cb4bSSrinivas Pandruvada 			break;
1880b28cb4bSSrinivas Pandruvada 
1890b28cb4bSSrinivas Pandruvada 		case HOSTIF_GET_REPORT_DESCRIPTOR:
1900b28cb4bSSrinivas Pandruvada 			if ((!(recv_msg->hdr.command & ~CMD_MASK) ||
1910b28cb4bSSrinivas Pandruvada 					client_data->init_done)) {
1920b28cb4bSSrinivas Pandruvada 				++client_data->bad_recv_cnt;
1930b28cb4bSSrinivas Pandruvada 				report_bad_packet(hid_ishtp_cl, recv_msg,
1940b28cb4bSSrinivas Pandruvada 						  cur_pos,
1950b28cb4bSSrinivas Pandruvada 						  payload_len);
19629b06d12SSrinivas Pandruvada 				ish_hw_reset(ishtp_get_ishtp_device(hid_ishtp_cl));
1970b28cb4bSSrinivas Pandruvada 				break;
1980b28cb4bSSrinivas Pandruvada 			}
1990b28cb4bSSrinivas Pandruvada 			if (!client_data->report_descr[curr_hid_dev])
2000b28cb4bSSrinivas Pandruvada 				client_data->report_descr[curr_hid_dev] =
2017ab21842SSrinivas Pandruvada 				devm_kmalloc(cl_data_to_dev(client_data),
2020b28cb4bSSrinivas Pandruvada 					     payload_len, GFP_KERNEL);
2030b28cb4bSSrinivas Pandruvada 			if (client_data->report_descr[curr_hid_dev])  {
2040b28cb4bSSrinivas Pandruvada 				memcpy(client_data->report_descr[curr_hid_dev],
2050b28cb4bSSrinivas Pandruvada 				       payload,
2060b28cb4bSSrinivas Pandruvada 				       payload_len);
2070b28cb4bSSrinivas Pandruvada 				client_data->report_descr_size[curr_hid_dev] =
2080b28cb4bSSrinivas Pandruvada 					payload_len;
2090b28cb4bSSrinivas Pandruvada 				client_data->report_descr_done = true;
2100b28cb4bSSrinivas Pandruvada 			}
2110b28cb4bSSrinivas Pandruvada 			wake_up_interruptible(&client_data->init_wait);
2120b28cb4bSSrinivas Pandruvada 
2130b28cb4bSSrinivas Pandruvada 			break;
2140b28cb4bSSrinivas Pandruvada 
2150b28cb4bSSrinivas Pandruvada 		case HOSTIF_GET_FEATURE_REPORT:
2160b28cb4bSSrinivas Pandruvada 			report_type = HID_FEATURE_REPORT;
2170b28cb4bSSrinivas Pandruvada 			goto	do_get_report;
2180b28cb4bSSrinivas Pandruvada 
2190b28cb4bSSrinivas Pandruvada 		case HOSTIF_GET_INPUT_REPORT:
2200b28cb4bSSrinivas Pandruvada 			report_type = HID_INPUT_REPORT;
2210b28cb4bSSrinivas Pandruvada do_get_report:
2220b28cb4bSSrinivas Pandruvada 			/* Get index of device that matches this id */
2230b28cb4bSSrinivas Pandruvada 			for (i = 0; i < client_data->num_hid_devices; ++i) {
2240b28cb4bSSrinivas Pandruvada 				if (recv_msg->hdr.device_id ==
225e19595fcSHyungwoo Yang 					  client_data->hid_devices[i].dev_id) {
226e19595fcSHyungwoo Yang 					hid = client_data->hid_sensor_hubs[i];
227e19595fcSHyungwoo Yang 					if (!hid)
228e19595fcSHyungwoo Yang 						break;
229e19595fcSHyungwoo Yang 
230e19595fcSHyungwoo Yang 					hid_data = hid->driver_data;
231e19595fcSHyungwoo Yang 					if (hid_data->raw_get_req) {
232e19595fcSHyungwoo Yang 						raw_len =
233e19595fcSHyungwoo Yang 						  (hid_data->raw_buf_size <
234e19595fcSHyungwoo Yang 								payload_len) ?
235e19595fcSHyungwoo Yang 						  hid_data->raw_buf_size :
236e19595fcSHyungwoo Yang 						  payload_len;
237e19595fcSHyungwoo Yang 
238e19595fcSHyungwoo Yang 						memcpy(hid_data->raw_buf,
239e19595fcSHyungwoo Yang 						       payload, raw_len);
240e19595fcSHyungwoo Yang 					} else {
241e19595fcSHyungwoo Yang 						hid_input_report
242e19595fcSHyungwoo Yang 							(hid, report_type,
243e19595fcSHyungwoo Yang 							 payload, payload_len,
244e19595fcSHyungwoo Yang 							 0);
245e19595fcSHyungwoo Yang 					}
246e19595fcSHyungwoo Yang 
247e19595fcSHyungwoo Yang 					ishtp_hid_wakeup(hid);
2480b28cb4bSSrinivas Pandruvada 					break;
2490b28cb4bSSrinivas Pandruvada 				}
2500b28cb4bSSrinivas Pandruvada 			}
2510b28cb4bSSrinivas Pandruvada 			break;
2520b28cb4bSSrinivas Pandruvada 
2530b28cb4bSSrinivas Pandruvada 		case HOSTIF_SET_FEATURE_REPORT:
2540b28cb4bSSrinivas Pandruvada 			/* Get index of device that matches this id */
2550b28cb4bSSrinivas Pandruvada 			for (i = 0; i < client_data->num_hid_devices; ++i) {
2560b28cb4bSSrinivas Pandruvada 				if (recv_msg->hdr.device_id ==
2570b28cb4bSSrinivas Pandruvada 					client_data->hid_devices[i].dev_id)
2580b28cb4bSSrinivas Pandruvada 					if (client_data->hid_sensor_hubs[i]) {
2590b28cb4bSSrinivas Pandruvada 						ishtp_hid_wakeup(
2600b28cb4bSSrinivas Pandruvada 						client_data->hid_sensor_hubs[
2610b28cb4bSSrinivas Pandruvada 							i]);
2620b28cb4bSSrinivas Pandruvada 						break;
2630b28cb4bSSrinivas Pandruvada 					}
2640b28cb4bSSrinivas Pandruvada 			}
2650b28cb4bSSrinivas Pandruvada 			break;
2660b28cb4bSSrinivas Pandruvada 
2670b28cb4bSSrinivas Pandruvada 		case HOSTIF_PUBLISH_INPUT_REPORT:
2680b28cb4bSSrinivas Pandruvada 			report_type = HID_INPUT_REPORT;
2690b28cb4bSSrinivas Pandruvada 			for (i = 0; i < client_data->num_hid_devices; ++i)
2700b28cb4bSSrinivas Pandruvada 				if (recv_msg->hdr.device_id ==
2710b28cb4bSSrinivas Pandruvada 					client_data->hid_devices[i].dev_id)
2720b28cb4bSSrinivas Pandruvada 					if (client_data->hid_sensor_hubs[i])
2730b28cb4bSSrinivas Pandruvada 						hid_input_report(
2740b28cb4bSSrinivas Pandruvada 						client_data->hid_sensor_hubs[
2750b28cb4bSSrinivas Pandruvada 									i],
2760b28cb4bSSrinivas Pandruvada 						report_type, payload,
2770b28cb4bSSrinivas Pandruvada 						payload_len, 0);
2780b28cb4bSSrinivas Pandruvada 			break;
2790b28cb4bSSrinivas Pandruvada 
2800b28cb4bSSrinivas Pandruvada 		case HOSTIF_PUBLISH_INPUT_REPORT_LIST:
2810b28cb4bSSrinivas Pandruvada 			report_type = HID_INPUT_REPORT;
2820b28cb4bSSrinivas Pandruvada 			reports_list = (struct report_list *)payload;
2830b28cb4bSSrinivas Pandruvada 			reports = (char *)reports_list->reports;
2840b28cb4bSSrinivas Pandruvada 
2850b28cb4bSSrinivas Pandruvada 			for (j = 0; j < reports_list->num_of_reports; j++) {
2860b28cb4bSSrinivas Pandruvada 				recv_msg = (struct hostif_msg *)(reports +
2870b28cb4bSSrinivas Pandruvada 					sizeof(uint16_t));
2880b28cb4bSSrinivas Pandruvada 				report_len = *(uint16_t *)reports;
2890b28cb4bSSrinivas Pandruvada 				payload = reports + sizeof(uint16_t) +
2900b28cb4bSSrinivas Pandruvada 					sizeof(struct hostif_msg_hdr);
2910b28cb4bSSrinivas Pandruvada 				payload_len = report_len -
2920b28cb4bSSrinivas Pandruvada 					sizeof(struct hostif_msg_hdr);
2930b28cb4bSSrinivas Pandruvada 
2940b28cb4bSSrinivas Pandruvada 				for (i = 0; i < client_data->num_hid_devices;
2950b28cb4bSSrinivas Pandruvada 				     ++i)
2960b28cb4bSSrinivas Pandruvada 					if (recv_msg->hdr.device_id ==
2970b28cb4bSSrinivas Pandruvada 					client_data->hid_devices[i].dev_id &&
2980b28cb4bSSrinivas Pandruvada 					client_data->hid_sensor_hubs[i]) {
2990b28cb4bSSrinivas Pandruvada 						hid_input_report(
3000b28cb4bSSrinivas Pandruvada 						client_data->hid_sensor_hubs[
3010b28cb4bSSrinivas Pandruvada 									i],
3020b28cb4bSSrinivas Pandruvada 						report_type,
3030b28cb4bSSrinivas Pandruvada 						payload, payload_len,
3040b28cb4bSSrinivas Pandruvada 						0);
3050b28cb4bSSrinivas Pandruvada 					}
3060b28cb4bSSrinivas Pandruvada 
3070b28cb4bSSrinivas Pandruvada 				reports += sizeof(uint16_t) + report_len;
3080b28cb4bSSrinivas Pandruvada 			}
3090b28cb4bSSrinivas Pandruvada 			break;
3100b28cb4bSSrinivas Pandruvada 		default:
3110b28cb4bSSrinivas Pandruvada 			++client_data->bad_recv_cnt;
3120b28cb4bSSrinivas Pandruvada 			report_bad_packet(hid_ishtp_cl, recv_msg, cur_pos,
3130b28cb4bSSrinivas Pandruvada 					  payload_len);
31429b06d12SSrinivas Pandruvada 			ish_hw_reset(ishtp_get_ishtp_device(hid_ishtp_cl));
3150b28cb4bSSrinivas Pandruvada 			break;
3160b28cb4bSSrinivas Pandruvada 
3170b28cb4bSSrinivas Pandruvada 		}
3180b28cb4bSSrinivas Pandruvada 
3190b28cb4bSSrinivas Pandruvada 		if (!cur_pos && cur_pos + payload_len +
3200b28cb4bSSrinivas Pandruvada 				sizeof(struct hostif_msg) < total_len)
3210b28cb4bSSrinivas Pandruvada 			++client_data->multi_packet_cnt;
3220b28cb4bSSrinivas Pandruvada 
3230b28cb4bSSrinivas Pandruvada 		cur_pos += payload_len + sizeof(struct hostif_msg);
3240b28cb4bSSrinivas Pandruvada 		payload += payload_len + sizeof(struct hostif_msg);
3250b28cb4bSSrinivas Pandruvada 
3260b28cb4bSSrinivas Pandruvada 	} while (cur_pos < total_len);
3270b28cb4bSSrinivas Pandruvada }
3280b28cb4bSSrinivas Pandruvada 
3290b28cb4bSSrinivas Pandruvada /**
3300b28cb4bSSrinivas Pandruvada  * ish_cl_event_cb() - bus driver callback for incoming message/packet
331*92443a9fSJiang Jian  * @device:	Pointer to the ishtp client device for which this message
3320b28cb4bSSrinivas Pandruvada  *		is targeted
3330b28cb4bSSrinivas Pandruvada  *
3340b28cb4bSSrinivas Pandruvada  * Remove the packet from the list and process the message by calling
3350b28cb4bSSrinivas Pandruvada  * process_recv
3360b28cb4bSSrinivas Pandruvada  */
ish_cl_event_cb(struct ishtp_cl_device * device)3370b28cb4bSSrinivas Pandruvada static void ish_cl_event_cb(struct ishtp_cl_device *device)
3380b28cb4bSSrinivas Pandruvada {
339d174c666SEven Xu 	struct ishtp_cl	*hid_ishtp_cl = ishtp_get_drvdata(device);
3400b28cb4bSSrinivas Pandruvada 	struct ishtp_cl_rb *rb_in_proc;
3410b28cb4bSSrinivas Pandruvada 	size_t r_length;
3420b28cb4bSSrinivas Pandruvada 
3430b28cb4bSSrinivas Pandruvada 	if (!hid_ishtp_cl)
3440b28cb4bSSrinivas Pandruvada 		return;
3450b28cb4bSSrinivas Pandruvada 
346816e7ed1SEven Xu 	while ((rb_in_proc = ishtp_cl_rx_get_rb(hid_ishtp_cl)) != NULL) {
3470b28cb4bSSrinivas Pandruvada 		if (!rb_in_proc->buffer.data)
3480b28cb4bSSrinivas Pandruvada 			return;
3490b28cb4bSSrinivas Pandruvada 
3500b28cb4bSSrinivas Pandruvada 		r_length = rb_in_proc->buf_idx;
3510b28cb4bSSrinivas Pandruvada 
3520b28cb4bSSrinivas Pandruvada 		/* decide what to do with received data */
3530b28cb4bSSrinivas Pandruvada 		process_recv(hid_ishtp_cl, rb_in_proc->buffer.data, r_length);
3540b28cb4bSSrinivas Pandruvada 
3550b28cb4bSSrinivas Pandruvada 		ishtp_cl_io_rb_recycle(rb_in_proc);
3560b28cb4bSSrinivas Pandruvada 	}
3570b28cb4bSSrinivas Pandruvada }
3580b28cb4bSSrinivas Pandruvada 
3590b28cb4bSSrinivas Pandruvada /**
3600b28cb4bSSrinivas Pandruvada  * hid_ishtp_set_feature() - send request to ISH FW to set a feature request
3610b28cb4bSSrinivas Pandruvada  * @hid:	hid device instance for this request
3620b28cb4bSSrinivas Pandruvada  * @buf:	feature buffer
3630b28cb4bSSrinivas Pandruvada  * @len:	Length of feature buffer
3640b28cb4bSSrinivas Pandruvada  * @report_id:	Report id for the feature set request
3650b28cb4bSSrinivas Pandruvada  *
3660b28cb4bSSrinivas Pandruvada  * This is called from hid core .request() callback. This function doesn't wait
3670b28cb4bSSrinivas Pandruvada  * for response.
3680b28cb4bSSrinivas Pandruvada  */
hid_ishtp_set_feature(struct hid_device * hid,char * buf,unsigned int len,int report_id)3690b28cb4bSSrinivas Pandruvada void hid_ishtp_set_feature(struct hid_device *hid, char *buf, unsigned int len,
3700b28cb4bSSrinivas Pandruvada 			   int report_id)
3710b28cb4bSSrinivas Pandruvada {
3720b28cb4bSSrinivas Pandruvada 	struct ishtp_hid_data *hid_data =  hid->driver_data;
3730b28cb4bSSrinivas Pandruvada 	struct ishtp_cl_data *client_data = hid_data->client_data;
3740b28cb4bSSrinivas Pandruvada 	struct hostif_msg *msg = (struct hostif_msg *)buf;
3750b28cb4bSSrinivas Pandruvada 	int	rv;
3760b28cb4bSSrinivas Pandruvada 	int	i;
3770b28cb4bSSrinivas Pandruvada 
3780b28cb4bSSrinivas Pandruvada 	hid_ishtp_trace(client_data,  "%s hid %p\n", __func__, hid);
3790b28cb4bSSrinivas Pandruvada 
3800b28cb4bSSrinivas Pandruvada 	rv = ishtp_hid_link_ready_wait(client_data);
3810b28cb4bSSrinivas Pandruvada 	if (rv) {
3820b28cb4bSSrinivas Pandruvada 		hid_ishtp_trace(client_data,  "%s hid %p link not ready\n",
3830b28cb4bSSrinivas Pandruvada 				__func__, hid);
3840b28cb4bSSrinivas Pandruvada 		return;
3850b28cb4bSSrinivas Pandruvada 	}
3860b28cb4bSSrinivas Pandruvada 
3870b28cb4bSSrinivas Pandruvada 	memset(msg, 0, sizeof(struct hostif_msg));
3880b28cb4bSSrinivas Pandruvada 	msg->hdr.command = HOSTIF_SET_FEATURE_REPORT;
3890b28cb4bSSrinivas Pandruvada 	for (i = 0; i < client_data->num_hid_devices; ++i) {
3900b28cb4bSSrinivas Pandruvada 		if (hid == client_data->hid_sensor_hubs[i]) {
3910b28cb4bSSrinivas Pandruvada 			msg->hdr.device_id =
3920b28cb4bSSrinivas Pandruvada 				client_data->hid_devices[i].dev_id;
3930b28cb4bSSrinivas Pandruvada 			break;
3940b28cb4bSSrinivas Pandruvada 		}
3950b28cb4bSSrinivas Pandruvada 	}
3960b28cb4bSSrinivas Pandruvada 
3970b28cb4bSSrinivas Pandruvada 	if (i == client_data->num_hid_devices)
3980b28cb4bSSrinivas Pandruvada 		return;
3990b28cb4bSSrinivas Pandruvada 
4000b28cb4bSSrinivas Pandruvada 	rv = ishtp_cl_send(client_data->hid_ishtp_cl, buf, len);
4010b28cb4bSSrinivas Pandruvada 	if (rv)
4020b28cb4bSSrinivas Pandruvada 		hid_ishtp_trace(client_data,  "%s hid %p send failed\n",
4030b28cb4bSSrinivas Pandruvada 				__func__, hid);
4040b28cb4bSSrinivas Pandruvada }
4050b28cb4bSSrinivas Pandruvada 
4060b28cb4bSSrinivas Pandruvada /**
4070b28cb4bSSrinivas Pandruvada  * hid_ishtp_get_report() - request to get feature/input report
4080b28cb4bSSrinivas Pandruvada  * @hid:	hid device instance for this request
4090b28cb4bSSrinivas Pandruvada  * @report_id:	Report id for the get request
4100b28cb4bSSrinivas Pandruvada  * @report_type:	Report type for the this request
4110b28cb4bSSrinivas Pandruvada  *
4120b28cb4bSSrinivas Pandruvada  * This is called from hid core .request() callback. This function will send
4130b28cb4bSSrinivas Pandruvada  * request to FW and return without waiting for response.
4140b28cb4bSSrinivas Pandruvada  */
hid_ishtp_get_report(struct hid_device * hid,int report_id,int report_type)4150b28cb4bSSrinivas Pandruvada void hid_ishtp_get_report(struct hid_device *hid, int report_id,
4160b28cb4bSSrinivas Pandruvada 			  int report_type)
4170b28cb4bSSrinivas Pandruvada {
4180b28cb4bSSrinivas Pandruvada 	struct ishtp_hid_data *hid_data =  hid->driver_data;
4190b28cb4bSSrinivas Pandruvada 	struct ishtp_cl_data *client_data = hid_data->client_data;
420749ab300SHans de Goede 	struct hostif_msg_to_sensor msg = {};
4210b28cb4bSSrinivas Pandruvada 	int	rv;
4220b28cb4bSSrinivas Pandruvada 	int	i;
4230b28cb4bSSrinivas Pandruvada 
4240b28cb4bSSrinivas Pandruvada 	hid_ishtp_trace(client_data,  "%s hid %p\n", __func__, hid);
4250b28cb4bSSrinivas Pandruvada 	rv = ishtp_hid_link_ready_wait(client_data);
4260b28cb4bSSrinivas Pandruvada 	if (rv) {
4270b28cb4bSSrinivas Pandruvada 		hid_ishtp_trace(client_data,  "%s hid %p link not ready\n",
4280b28cb4bSSrinivas Pandruvada 				__func__, hid);
4290b28cb4bSSrinivas Pandruvada 		return;
4300b28cb4bSSrinivas Pandruvada 	}
4310b28cb4bSSrinivas Pandruvada 
432749ab300SHans de Goede 	msg.hdr.command = (report_type == HID_FEATURE_REPORT) ?
4330b28cb4bSSrinivas Pandruvada 		HOSTIF_GET_FEATURE_REPORT : HOSTIF_GET_INPUT_REPORT;
4340b28cb4bSSrinivas Pandruvada 	for (i = 0; i < client_data->num_hid_devices; ++i) {
4350b28cb4bSSrinivas Pandruvada 		if (hid == client_data->hid_sensor_hubs[i]) {
436749ab300SHans de Goede 			msg.hdr.device_id =
4370b28cb4bSSrinivas Pandruvada 				client_data->hid_devices[i].dev_id;
4380b28cb4bSSrinivas Pandruvada 			break;
4390b28cb4bSSrinivas Pandruvada 		}
4400b28cb4bSSrinivas Pandruvada 	}
4410b28cb4bSSrinivas Pandruvada 
4420b28cb4bSSrinivas Pandruvada 	if (i == client_data->num_hid_devices)
4430b28cb4bSSrinivas Pandruvada 		return;
4440b28cb4bSSrinivas Pandruvada 
445749ab300SHans de Goede 	msg.report_id = report_id;
446749ab300SHans de Goede 	rv = ishtp_cl_send(client_data->hid_ishtp_cl, (uint8_t *)&msg,
447749ab300SHans de Goede 			    sizeof(msg));
4480b28cb4bSSrinivas Pandruvada 	if (rv)
4490b28cb4bSSrinivas Pandruvada 		hid_ishtp_trace(client_data,  "%s hid %p send failed\n",
4500b28cb4bSSrinivas Pandruvada 				__func__, hid);
4510b28cb4bSSrinivas Pandruvada }
4520b28cb4bSSrinivas Pandruvada 
4530b28cb4bSSrinivas Pandruvada /**
4540b28cb4bSSrinivas Pandruvada  * ishtp_hid_link_ready_wait() - Wait for link ready
4550b28cb4bSSrinivas Pandruvada  * @client_data:	client data instance
4560b28cb4bSSrinivas Pandruvada  *
4570b28cb4bSSrinivas Pandruvada  * If the transport link started suspend process, then wait, till either
4580b28cb4bSSrinivas Pandruvada  * resumed or timeout
4590b28cb4bSSrinivas Pandruvada  *
4600b28cb4bSSrinivas Pandruvada  * Return: 0 on success, non zero on error
4610b28cb4bSSrinivas Pandruvada  */
ishtp_hid_link_ready_wait(struct ishtp_cl_data * client_data)4620b28cb4bSSrinivas Pandruvada int ishtp_hid_link_ready_wait(struct ishtp_cl_data *client_data)
4630b28cb4bSSrinivas Pandruvada {
4640b28cb4bSSrinivas Pandruvada 	int rc;
4650b28cb4bSSrinivas Pandruvada 
4660b28cb4bSSrinivas Pandruvada 	if (client_data->suspended) {
4670b28cb4bSSrinivas Pandruvada 		hid_ishtp_trace(client_data,  "wait for link ready\n");
4680b28cb4bSSrinivas Pandruvada 		rc = wait_event_interruptible_timeout(
4690b28cb4bSSrinivas Pandruvada 					client_data->ishtp_resume_wait,
4700b28cb4bSSrinivas Pandruvada 					!client_data->suspended,
4710b28cb4bSSrinivas Pandruvada 					5 * HZ);
4720b28cb4bSSrinivas Pandruvada 
4730b28cb4bSSrinivas Pandruvada 		if (rc == 0) {
4740b28cb4bSSrinivas Pandruvada 			hid_ishtp_trace(client_data,  "link not ready\n");
4750b28cb4bSSrinivas Pandruvada 			return -EIO;
4760b28cb4bSSrinivas Pandruvada 		}
4770b28cb4bSSrinivas Pandruvada 		hid_ishtp_trace(client_data,  "link ready\n");
4780b28cb4bSSrinivas Pandruvada 	}
4790b28cb4bSSrinivas Pandruvada 
4800b28cb4bSSrinivas Pandruvada 	return 0;
4810b28cb4bSSrinivas Pandruvada }
4820b28cb4bSSrinivas Pandruvada 
4830b28cb4bSSrinivas Pandruvada /**
4840b28cb4bSSrinivas Pandruvada  * ishtp_enum_enum_devices() - Enumerate hid devices
4850b28cb4bSSrinivas Pandruvada  * @hid_ishtp_cl:	client instance
4860b28cb4bSSrinivas Pandruvada  *
4870b28cb4bSSrinivas Pandruvada  * Helper function to send request to firmware to enumerate HID devices
4880b28cb4bSSrinivas Pandruvada  *
4890b28cb4bSSrinivas Pandruvada  * Return: 0 on success, non zero on error
4900b28cb4bSSrinivas Pandruvada  */
ishtp_enum_enum_devices(struct ishtp_cl * hid_ishtp_cl)4910b28cb4bSSrinivas Pandruvada static int ishtp_enum_enum_devices(struct ishtp_cl *hid_ishtp_cl)
4920b28cb4bSSrinivas Pandruvada {
4930b28cb4bSSrinivas Pandruvada 	struct hostif_msg msg;
49429b06d12SSrinivas Pandruvada 	struct ishtp_cl_data *client_data = ishtp_get_client_data(hid_ishtp_cl);
4950b28cb4bSSrinivas Pandruvada 	int retry_count;
4960b28cb4bSSrinivas Pandruvada 	int rv;
4970b28cb4bSSrinivas Pandruvada 
4980b28cb4bSSrinivas Pandruvada 	/* Send HOSTIF_DM_ENUM_DEVICES */
4990b28cb4bSSrinivas Pandruvada 	memset(&msg, 0, sizeof(struct hostif_msg));
5000b28cb4bSSrinivas Pandruvada 	msg.hdr.command = HOSTIF_DM_ENUM_DEVICES;
5010b28cb4bSSrinivas Pandruvada 	rv = ishtp_cl_send(hid_ishtp_cl, (unsigned char *)&msg,
5020b28cb4bSSrinivas Pandruvada 			   sizeof(struct hostif_msg));
5030b28cb4bSSrinivas Pandruvada 	if (rv)
5040b28cb4bSSrinivas Pandruvada 		return rv;
5050b28cb4bSSrinivas Pandruvada 
5060b28cb4bSSrinivas Pandruvada 	retry_count = 0;
5070b28cb4bSSrinivas Pandruvada 	while (!client_data->enum_devices_done &&
5080b28cb4bSSrinivas Pandruvada 	       retry_count < 10) {
5090b28cb4bSSrinivas Pandruvada 		wait_event_interruptible_timeout(client_data->init_wait,
5100b28cb4bSSrinivas Pandruvada 					 client_data->enum_devices_done,
5110b28cb4bSSrinivas Pandruvada 					 3 * HZ);
5120b28cb4bSSrinivas Pandruvada 		++retry_count;
5130b28cb4bSSrinivas Pandruvada 		if (!client_data->enum_devices_done)
5140b28cb4bSSrinivas Pandruvada 			/* Send HOSTIF_DM_ENUM_DEVICES */
5150b28cb4bSSrinivas Pandruvada 			rv = ishtp_cl_send(hid_ishtp_cl,
5160b28cb4bSSrinivas Pandruvada 					   (unsigned char *) &msg,
5170b28cb4bSSrinivas Pandruvada 					   sizeof(struct hostif_msg));
5180b28cb4bSSrinivas Pandruvada 	}
5190b28cb4bSSrinivas Pandruvada 	if (!client_data->enum_devices_done) {
5207ab21842SSrinivas Pandruvada 		dev_err(cl_data_to_dev(client_data),
5210b28cb4bSSrinivas Pandruvada 			"[hid-ish]: timed out waiting for enum_devices\n");
5220b28cb4bSSrinivas Pandruvada 		return -ETIMEDOUT;
5230b28cb4bSSrinivas Pandruvada 	}
5240b28cb4bSSrinivas Pandruvada 	if (!client_data->hid_devices) {
5257ab21842SSrinivas Pandruvada 		dev_err(cl_data_to_dev(client_data),
5260b28cb4bSSrinivas Pandruvada 			"[hid-ish]: failed to allocate HID dev structures\n");
5270b28cb4bSSrinivas Pandruvada 		return -ENOMEM;
5280b28cb4bSSrinivas Pandruvada 	}
5290b28cb4bSSrinivas Pandruvada 
5300b28cb4bSSrinivas Pandruvada 	client_data->num_hid_devices = client_data->hid_dev_count;
53129b06d12SSrinivas Pandruvada 	dev_info(ishtp_device(client_data->cl_device),
5320b28cb4bSSrinivas Pandruvada 		"[hid-ish]: enum_devices_done OK, num_hid_devices=%d\n",
5330b28cb4bSSrinivas Pandruvada 		client_data->num_hid_devices);
5340b28cb4bSSrinivas Pandruvada 
5350b28cb4bSSrinivas Pandruvada 	return	0;
5360b28cb4bSSrinivas Pandruvada }
5370b28cb4bSSrinivas Pandruvada 
5380b28cb4bSSrinivas Pandruvada /**
5390b28cb4bSSrinivas Pandruvada  * ishtp_get_hid_descriptor() - Get hid descriptor
5400b28cb4bSSrinivas Pandruvada  * @hid_ishtp_cl:	client instance
5410b28cb4bSSrinivas Pandruvada  * @index:		Index into the hid_descr array
5420b28cb4bSSrinivas Pandruvada  *
5430b28cb4bSSrinivas Pandruvada  * Helper function to send request to firmware get HID descriptor of a device
5440b28cb4bSSrinivas Pandruvada  *
5450b28cb4bSSrinivas Pandruvada  * Return: 0 on success, non zero on error
5460b28cb4bSSrinivas Pandruvada  */
ishtp_get_hid_descriptor(struct ishtp_cl * hid_ishtp_cl,int index)5470b28cb4bSSrinivas Pandruvada static int ishtp_get_hid_descriptor(struct ishtp_cl *hid_ishtp_cl, int index)
5480b28cb4bSSrinivas Pandruvada {
5490b28cb4bSSrinivas Pandruvada 	struct hostif_msg msg;
55029b06d12SSrinivas Pandruvada 	struct ishtp_cl_data *client_data = ishtp_get_client_data(hid_ishtp_cl);
5510b28cb4bSSrinivas Pandruvada 	int rv;
5520b28cb4bSSrinivas Pandruvada 
5530b28cb4bSSrinivas Pandruvada 	/* Get HID descriptor */
5540b28cb4bSSrinivas Pandruvada 	client_data->hid_descr_done = false;
5550b28cb4bSSrinivas Pandruvada 	memset(&msg, 0, sizeof(struct hostif_msg));
5560b28cb4bSSrinivas Pandruvada 	msg.hdr.command = HOSTIF_GET_HID_DESCRIPTOR;
5570b28cb4bSSrinivas Pandruvada 	msg.hdr.device_id = client_data->hid_devices[index].dev_id;
5580b28cb4bSSrinivas Pandruvada 	rv = ishtp_cl_send(hid_ishtp_cl, (unsigned char *) &msg,
5590b28cb4bSSrinivas Pandruvada 			   sizeof(struct hostif_msg));
5600b28cb4bSSrinivas Pandruvada 	if (rv)
5610b28cb4bSSrinivas Pandruvada 		return rv;
5620b28cb4bSSrinivas Pandruvada 
5630b28cb4bSSrinivas Pandruvada 	if (!client_data->hid_descr_done) {
5640b28cb4bSSrinivas Pandruvada 		wait_event_interruptible_timeout(client_data->init_wait,
5650b28cb4bSSrinivas Pandruvada 						 client_data->hid_descr_done,
5660b28cb4bSSrinivas Pandruvada 						 3 * HZ);
5670b28cb4bSSrinivas Pandruvada 		if (!client_data->hid_descr_done) {
5687ab21842SSrinivas Pandruvada 			dev_err(cl_data_to_dev(client_data),
5690b28cb4bSSrinivas Pandruvada 				"[hid-ish]: timed out for hid_descr_done\n");
5700b28cb4bSSrinivas Pandruvada 			return -EIO;
5710b28cb4bSSrinivas Pandruvada 		}
5720b28cb4bSSrinivas Pandruvada 
5730b28cb4bSSrinivas Pandruvada 		if (!client_data->hid_descr[index]) {
5747ab21842SSrinivas Pandruvada 			dev_err(cl_data_to_dev(client_data),
5750b28cb4bSSrinivas Pandruvada 				"[hid-ish]: allocation HID desc fail\n");
5760b28cb4bSSrinivas Pandruvada 			return -ENOMEM;
5770b28cb4bSSrinivas Pandruvada 		}
5780b28cb4bSSrinivas Pandruvada 	}
5790b28cb4bSSrinivas Pandruvada 
5800b28cb4bSSrinivas Pandruvada 	return 0;
5810b28cb4bSSrinivas Pandruvada }
5820b28cb4bSSrinivas Pandruvada 
5830b28cb4bSSrinivas Pandruvada /**
5840b28cb4bSSrinivas Pandruvada  * ishtp_get_report_descriptor() - Get report descriptor
5850b28cb4bSSrinivas Pandruvada  * @hid_ishtp_cl:	client instance
5860b28cb4bSSrinivas Pandruvada  * @index:		Index into the hid_descr array
5870b28cb4bSSrinivas Pandruvada  *
5880b28cb4bSSrinivas Pandruvada  * Helper function to send request to firmware get HID report descriptor of
5890b28cb4bSSrinivas Pandruvada  * a device
5900b28cb4bSSrinivas Pandruvada  *
5910b28cb4bSSrinivas Pandruvada  * Return: 0 on success, non zero on error
5920b28cb4bSSrinivas Pandruvada  */
ishtp_get_report_descriptor(struct ishtp_cl * hid_ishtp_cl,int index)5930b28cb4bSSrinivas Pandruvada static int ishtp_get_report_descriptor(struct ishtp_cl *hid_ishtp_cl,
5940b28cb4bSSrinivas Pandruvada 				       int index)
5950b28cb4bSSrinivas Pandruvada {
5960b28cb4bSSrinivas Pandruvada 	struct hostif_msg msg;
59729b06d12SSrinivas Pandruvada 	struct ishtp_cl_data *client_data = ishtp_get_client_data(hid_ishtp_cl);
5980b28cb4bSSrinivas Pandruvada 	int rv;
5990b28cb4bSSrinivas Pandruvada 
6000b28cb4bSSrinivas Pandruvada 	/* Get report descriptor */
6010b28cb4bSSrinivas Pandruvada 	client_data->report_descr_done = false;
6020b28cb4bSSrinivas Pandruvada 	memset(&msg, 0, sizeof(struct hostif_msg));
6030b28cb4bSSrinivas Pandruvada 	msg.hdr.command = HOSTIF_GET_REPORT_DESCRIPTOR;
6040b28cb4bSSrinivas Pandruvada 	msg.hdr.device_id = client_data->hid_devices[index].dev_id;
6050b28cb4bSSrinivas Pandruvada 	rv = ishtp_cl_send(hid_ishtp_cl, (unsigned char *) &msg,
6060b28cb4bSSrinivas Pandruvada 			   sizeof(struct hostif_msg));
6070b28cb4bSSrinivas Pandruvada 	if (rv)
6080b28cb4bSSrinivas Pandruvada 		return rv;
6090b28cb4bSSrinivas Pandruvada 
6100b28cb4bSSrinivas Pandruvada 	if (!client_data->report_descr_done)
6110b28cb4bSSrinivas Pandruvada 		wait_event_interruptible_timeout(client_data->init_wait,
6120b28cb4bSSrinivas Pandruvada 					 client_data->report_descr_done,
6130b28cb4bSSrinivas Pandruvada 					 3 * HZ);
6140b28cb4bSSrinivas Pandruvada 	if (!client_data->report_descr_done) {
6157ab21842SSrinivas Pandruvada 		dev_err(cl_data_to_dev(client_data),
6160b28cb4bSSrinivas Pandruvada 				"[hid-ish]: timed out for report descr\n");
6170b28cb4bSSrinivas Pandruvada 		return -EIO;
6180b28cb4bSSrinivas Pandruvada 	}
6190b28cb4bSSrinivas Pandruvada 	if (!client_data->report_descr[index]) {
6207ab21842SSrinivas Pandruvada 		dev_err(cl_data_to_dev(client_data),
6210b28cb4bSSrinivas Pandruvada 			"[hid-ish]: failed to alloc report descr\n");
6220b28cb4bSSrinivas Pandruvada 		return -ENOMEM;
6230b28cb4bSSrinivas Pandruvada 	}
6240b28cb4bSSrinivas Pandruvada 
6250b28cb4bSSrinivas Pandruvada 	return 0;
6260b28cb4bSSrinivas Pandruvada }
6270b28cb4bSSrinivas Pandruvada 
6280b28cb4bSSrinivas Pandruvada /**
6290b28cb4bSSrinivas Pandruvada  * hid_ishtp_cl_init() - Init function for ISHTP client
6300b28cb4bSSrinivas Pandruvada  * @hid_ishtp_cl:	ISHTP client instance
6310b28cb4bSSrinivas Pandruvada  * @reset:		true if called for init after reset
6320b28cb4bSSrinivas Pandruvada  *
6330b28cb4bSSrinivas Pandruvada  * This function complete the initializtion of the client. The summary of
6340b28cb4bSSrinivas Pandruvada  * processing:
6350b28cb4bSSrinivas Pandruvada  * - Send request to enumerate the hid clients
6360b28cb4bSSrinivas Pandruvada  *	Get the HID descriptor for each enumearated device
6370b28cb4bSSrinivas Pandruvada  *	Get report description of each device
6380b28cb4bSSrinivas Pandruvada  *	Register each device wik hid core by calling ishtp_hid_probe
6390b28cb4bSSrinivas Pandruvada  *
6400b28cb4bSSrinivas Pandruvada  * Return: 0 on success, non zero on error
6410b28cb4bSSrinivas Pandruvada  */
hid_ishtp_cl_init(struct ishtp_cl * hid_ishtp_cl,int reset)6420b28cb4bSSrinivas Pandruvada static int hid_ishtp_cl_init(struct ishtp_cl *hid_ishtp_cl, int reset)
6430b28cb4bSSrinivas Pandruvada {
6440b28cb4bSSrinivas Pandruvada 	struct ishtp_device *dev;
64529b06d12SSrinivas Pandruvada 	struct ishtp_cl_data *client_data = ishtp_get_client_data(hid_ishtp_cl);
646f26de33fSEven Xu 	struct ishtp_fw_client *fw_client;
6470b28cb4bSSrinivas Pandruvada 	int i;
6480b28cb4bSSrinivas Pandruvada 	int rv;
6490b28cb4bSSrinivas Pandruvada 
6507ab21842SSrinivas Pandruvada 	dev_dbg(cl_data_to_dev(client_data), "%s\n", __func__);
6510b28cb4bSSrinivas Pandruvada 	hid_ishtp_trace(client_data,  "%s reset flag: %d\n", __func__, reset);
6520b28cb4bSSrinivas Pandruvada 
653c2012ec0SSrinivas Pandruvada 	rv = ishtp_cl_link(hid_ishtp_cl);
6540b28cb4bSSrinivas Pandruvada 	if (rv) {
6557ab21842SSrinivas Pandruvada 		dev_err(cl_data_to_dev(client_data),
6560b28cb4bSSrinivas Pandruvada 			"ishtp_cl_link failed\n");
6570b28cb4bSSrinivas Pandruvada 		return	-ENOMEM;
6580b28cb4bSSrinivas Pandruvada 	}
6590b28cb4bSSrinivas Pandruvada 
6600b28cb4bSSrinivas Pandruvada 	client_data->init_done = 0;
6610b28cb4bSSrinivas Pandruvada 
66229b06d12SSrinivas Pandruvada 	dev = ishtp_get_ishtp_device(hid_ishtp_cl);
6630b28cb4bSSrinivas Pandruvada 
6640b28cb4bSSrinivas Pandruvada 	/* Connect to FW client */
66529b06d12SSrinivas Pandruvada 	ishtp_set_tx_ring_size(hid_ishtp_cl, HID_CL_TX_RING_SIZE);
66629b06d12SSrinivas Pandruvada 	ishtp_set_rx_ring_size(hid_ishtp_cl, HID_CL_RX_RING_SIZE);
6670b28cb4bSSrinivas Pandruvada 
668bf9167a8SArnd Bergmann 	fw_client = ishtp_fw_cl_get_client(dev, &hid_ishtp_id_table[0].guid);
669f26de33fSEven Xu 	if (!fw_client) {
6707ab21842SSrinivas Pandruvada 		dev_err(cl_data_to_dev(client_data),
6710b28cb4bSSrinivas Pandruvada 			"ish client uuid not found\n");
672f26de33fSEven Xu 		return -ENOENT;
6730b28cb4bSSrinivas Pandruvada 	}
67429b06d12SSrinivas Pandruvada 	ishtp_cl_set_fw_client_id(hid_ishtp_cl,
67529b06d12SSrinivas Pandruvada 				  ishtp_get_fw_client_id(fw_client));
67629b06d12SSrinivas Pandruvada 	ishtp_set_connection_state(hid_ishtp_cl, ISHTP_CL_CONNECTING);
6770b28cb4bSSrinivas Pandruvada 
6780b28cb4bSSrinivas Pandruvada 	rv = ishtp_cl_connect(hid_ishtp_cl);
6790b28cb4bSSrinivas Pandruvada 	if (rv) {
6807ab21842SSrinivas Pandruvada 		dev_err(cl_data_to_dev(client_data),
6810b28cb4bSSrinivas Pandruvada 			"client connect fail\n");
6820b28cb4bSSrinivas Pandruvada 		goto err_cl_unlink;
6830b28cb4bSSrinivas Pandruvada 	}
6840b28cb4bSSrinivas Pandruvada 
6850b28cb4bSSrinivas Pandruvada 	hid_ishtp_trace(client_data,  "%s client connected\n", __func__);
6860b28cb4bSSrinivas Pandruvada 
6870b28cb4bSSrinivas Pandruvada 	/* Register read callback */
68829b06d12SSrinivas Pandruvada 	ishtp_register_event_cb(client_data->cl_device, ish_cl_event_cb);
6890b28cb4bSSrinivas Pandruvada 
6900b28cb4bSSrinivas Pandruvada 	rv = ishtp_enum_enum_devices(hid_ishtp_cl);
6910b28cb4bSSrinivas Pandruvada 	if (rv)
6920b28cb4bSSrinivas Pandruvada 		goto err_cl_disconnect;
6930b28cb4bSSrinivas Pandruvada 
6940b28cb4bSSrinivas Pandruvada 	hid_ishtp_trace(client_data,  "%s enumerated device count %d\n",
6950b28cb4bSSrinivas Pandruvada 			__func__, client_data->num_hid_devices);
6960b28cb4bSSrinivas Pandruvada 
6970b28cb4bSSrinivas Pandruvada 	for (i = 0; i < client_data->num_hid_devices; ++i) {
6980b28cb4bSSrinivas Pandruvada 		client_data->cur_hid_dev = i;
6990b28cb4bSSrinivas Pandruvada 
7000b28cb4bSSrinivas Pandruvada 		rv = ishtp_get_hid_descriptor(hid_ishtp_cl, i);
7010b28cb4bSSrinivas Pandruvada 		if (rv)
7020b28cb4bSSrinivas Pandruvada 			goto err_cl_disconnect;
7030b28cb4bSSrinivas Pandruvada 
7040b28cb4bSSrinivas Pandruvada 		rv = ishtp_get_report_descriptor(hid_ishtp_cl, i);
7050b28cb4bSSrinivas Pandruvada 		if (rv)
7060b28cb4bSSrinivas Pandruvada 			goto err_cl_disconnect;
7070b28cb4bSSrinivas Pandruvada 
7080b28cb4bSSrinivas Pandruvada 		if (!reset) {
7090b28cb4bSSrinivas Pandruvada 			rv = ishtp_hid_probe(i, client_data);
7100b28cb4bSSrinivas Pandruvada 			if (rv) {
7117ab21842SSrinivas Pandruvada 				dev_err(cl_data_to_dev(client_data),
7120b28cb4bSSrinivas Pandruvada 				"[hid-ish]: HID probe for #%u failed: %d\n",
7130b28cb4bSSrinivas Pandruvada 				i, rv);
7140b28cb4bSSrinivas Pandruvada 				goto err_cl_disconnect;
7150b28cb4bSSrinivas Pandruvada 			}
7160b28cb4bSSrinivas Pandruvada 		}
7170b28cb4bSSrinivas Pandruvada 	} /* for() on all hid devices */
7180b28cb4bSSrinivas Pandruvada 
7190b28cb4bSSrinivas Pandruvada 	client_data->init_done = 1;
7200b28cb4bSSrinivas Pandruvada 	client_data->suspended = false;
7210b28cb4bSSrinivas Pandruvada 	wake_up_interruptible(&client_data->ishtp_resume_wait);
7220b28cb4bSSrinivas Pandruvada 	hid_ishtp_trace(client_data,  "%s successful init\n", __func__);
7230b28cb4bSSrinivas Pandruvada 	return 0;
7240b28cb4bSSrinivas Pandruvada 
7250b28cb4bSSrinivas Pandruvada err_cl_disconnect:
72629b06d12SSrinivas Pandruvada 	ishtp_set_connection_state(hid_ishtp_cl, ISHTP_CL_DISCONNECTING);
7270b28cb4bSSrinivas Pandruvada 	ishtp_cl_disconnect(hid_ishtp_cl);
7280b28cb4bSSrinivas Pandruvada err_cl_unlink:
7290b28cb4bSSrinivas Pandruvada 	ishtp_cl_unlink(hid_ishtp_cl);
7300b28cb4bSSrinivas Pandruvada 	return rv;
7310b28cb4bSSrinivas Pandruvada }
7320b28cb4bSSrinivas Pandruvada 
7330b28cb4bSSrinivas Pandruvada /**
7340b28cb4bSSrinivas Pandruvada  * hid_ishtp_cl_deinit() - Deinit function for ISHTP client
7350b28cb4bSSrinivas Pandruvada  * @hid_ishtp_cl:	ISHTP client instance
7360b28cb4bSSrinivas Pandruvada  *
7370b28cb4bSSrinivas Pandruvada  * Unlink and free hid client
7380b28cb4bSSrinivas Pandruvada  */
hid_ishtp_cl_deinit(struct ishtp_cl * hid_ishtp_cl)7390b28cb4bSSrinivas Pandruvada static void hid_ishtp_cl_deinit(struct ishtp_cl *hid_ishtp_cl)
7400b28cb4bSSrinivas Pandruvada {
7410b28cb4bSSrinivas Pandruvada 	ishtp_cl_unlink(hid_ishtp_cl);
7420b28cb4bSSrinivas Pandruvada 	ishtp_cl_flush_queues(hid_ishtp_cl);
7430b28cb4bSSrinivas Pandruvada 
7440b28cb4bSSrinivas Pandruvada 	/* disband and free all Tx and Rx client-level rings */
7450b28cb4bSSrinivas Pandruvada 	ishtp_cl_free(hid_ishtp_cl);
7460b28cb4bSSrinivas Pandruvada }
7470b28cb4bSSrinivas Pandruvada 
hid_ishtp_cl_reset_handler(struct work_struct * work)7480b28cb4bSSrinivas Pandruvada static void hid_ishtp_cl_reset_handler(struct work_struct *work)
7490b28cb4bSSrinivas Pandruvada {
7500b28cb4bSSrinivas Pandruvada 	struct ishtp_cl_data *client_data;
7510b28cb4bSSrinivas Pandruvada 	struct ishtp_cl *hid_ishtp_cl;
7520b28cb4bSSrinivas Pandruvada 	struct ishtp_cl_device *cl_device;
7530b28cb4bSSrinivas Pandruvada 	int retry;
7540b28cb4bSSrinivas Pandruvada 	int rv;
7550b28cb4bSSrinivas Pandruvada 
7560b28cb4bSSrinivas Pandruvada 	client_data = container_of(work, struct ishtp_cl_data, work);
7570b28cb4bSSrinivas Pandruvada 
7580b28cb4bSSrinivas Pandruvada 	hid_ishtp_cl = client_data->hid_ishtp_cl;
7590b28cb4bSSrinivas Pandruvada 	cl_device = client_data->cl_device;
7600b28cb4bSSrinivas Pandruvada 
7610b28cb4bSSrinivas Pandruvada 	hid_ishtp_trace(client_data, "%s hid_ishtp_cl %p\n", __func__,
7620b28cb4bSSrinivas Pandruvada 			hid_ishtp_cl);
76329b06d12SSrinivas Pandruvada 	dev_dbg(ishtp_device(client_data->cl_device), "%s\n", __func__);
7640b28cb4bSSrinivas Pandruvada 
7650b28cb4bSSrinivas Pandruvada 	hid_ishtp_cl_deinit(hid_ishtp_cl);
7660b28cb4bSSrinivas Pandruvada 
7677ab21842SSrinivas Pandruvada 	hid_ishtp_cl = ishtp_cl_allocate(cl_device);
7680b28cb4bSSrinivas Pandruvada 	if (!hid_ishtp_cl)
7690b28cb4bSSrinivas Pandruvada 		return;
7700b28cb4bSSrinivas Pandruvada 
771d174c666SEven Xu 	ishtp_set_drvdata(cl_device, hid_ishtp_cl);
77229b06d12SSrinivas Pandruvada 	ishtp_set_client_data(hid_ishtp_cl, client_data);
7730b28cb4bSSrinivas Pandruvada 	client_data->hid_ishtp_cl = hid_ishtp_cl;
7740b28cb4bSSrinivas Pandruvada 
7750b28cb4bSSrinivas Pandruvada 	client_data->num_hid_devices = 0;
7760b28cb4bSSrinivas Pandruvada 
7770b28cb4bSSrinivas Pandruvada 	for (retry = 0; retry < 3; ++retry) {
7780b28cb4bSSrinivas Pandruvada 		rv = hid_ishtp_cl_init(hid_ishtp_cl, 1);
7790b28cb4bSSrinivas Pandruvada 		if (!rv)
7800b28cb4bSSrinivas Pandruvada 			break;
7817ab21842SSrinivas Pandruvada 		dev_err(cl_data_to_dev(client_data), "Retry reset init\n");
7820b28cb4bSSrinivas Pandruvada 	}
7830b28cb4bSSrinivas Pandruvada 	if (rv) {
7847ab21842SSrinivas Pandruvada 		dev_err(cl_data_to_dev(client_data), "Reset Failed\n");
7850b28cb4bSSrinivas Pandruvada 		hid_ishtp_trace(client_data, "%s Failed hid_ishtp_cl %p\n",
7860b28cb4bSSrinivas Pandruvada 				__func__, hid_ishtp_cl);
7870b28cb4bSSrinivas Pandruvada 	}
7880b28cb4bSSrinivas Pandruvada }
7890b28cb4bSSrinivas Pandruvada 
hid_ishtp_cl_resume_handler(struct work_struct * work)790e48bf29cSYe Xiang static void hid_ishtp_cl_resume_handler(struct work_struct *work)
791e48bf29cSYe Xiang {
792e48bf29cSYe Xiang 	struct ishtp_cl_data *client_data = container_of(work, struct ishtp_cl_data, resume_work);
793e48bf29cSYe Xiang 	struct ishtp_cl *hid_ishtp_cl = client_data->hid_ishtp_cl;
794e48bf29cSYe Xiang 
795e48bf29cSYe Xiang 	if (ishtp_wait_resume(ishtp_get_ishtp_device(hid_ishtp_cl))) {
796e48bf29cSYe Xiang 		client_data->suspended = false;
797e48bf29cSYe Xiang 		wake_up_interruptible(&client_data->ishtp_resume_wait);
798e48bf29cSYe Xiang 	}
799e48bf29cSYe Xiang }
800e48bf29cSYe Xiang 
801c57179c7SLee Jones ishtp_print_log ishtp_hid_print_trace;
8027ab21842SSrinivas Pandruvada 
8030b28cb4bSSrinivas Pandruvada /**
8040b28cb4bSSrinivas Pandruvada  * hid_ishtp_cl_probe() - ISHTP client driver probe
8050b28cb4bSSrinivas Pandruvada  * @cl_device:		ISHTP client device instance
8060b28cb4bSSrinivas Pandruvada  *
8070b28cb4bSSrinivas Pandruvada  * This function gets called on device create on ISHTP bus
8080b28cb4bSSrinivas Pandruvada  *
8090b28cb4bSSrinivas Pandruvada  * Return: 0 on success, non zero on error
8100b28cb4bSSrinivas Pandruvada  */
hid_ishtp_cl_probe(struct ishtp_cl_device * cl_device)8110b28cb4bSSrinivas Pandruvada static int hid_ishtp_cl_probe(struct ishtp_cl_device *cl_device)
8120b28cb4bSSrinivas Pandruvada {
8130b28cb4bSSrinivas Pandruvada 	struct ishtp_cl *hid_ishtp_cl;
8140b28cb4bSSrinivas Pandruvada 	struct ishtp_cl_data *client_data;
8150b28cb4bSSrinivas Pandruvada 	int rv;
8160b28cb4bSSrinivas Pandruvada 
8170b28cb4bSSrinivas Pandruvada 	if (!cl_device)
8180b28cb4bSSrinivas Pandruvada 		return	-ENODEV;
8190b28cb4bSSrinivas Pandruvada 
8207ab21842SSrinivas Pandruvada 	client_data = devm_kzalloc(ishtp_device(cl_device),
8217ab21842SSrinivas Pandruvada 				   sizeof(*client_data),
8220b28cb4bSSrinivas Pandruvada 				   GFP_KERNEL);
8230b28cb4bSSrinivas Pandruvada 	if (!client_data)
8240b28cb4bSSrinivas Pandruvada 		return -ENOMEM;
8250b28cb4bSSrinivas Pandruvada 
8267ab21842SSrinivas Pandruvada 	hid_ishtp_cl = ishtp_cl_allocate(cl_device);
8270b28cb4bSSrinivas Pandruvada 	if (!hid_ishtp_cl)
8280b28cb4bSSrinivas Pandruvada 		return -ENOMEM;
8290b28cb4bSSrinivas Pandruvada 
830d174c666SEven Xu 	ishtp_set_drvdata(cl_device, hid_ishtp_cl);
83129b06d12SSrinivas Pandruvada 	ishtp_set_client_data(hid_ishtp_cl, client_data);
8320b28cb4bSSrinivas Pandruvada 	client_data->hid_ishtp_cl = hid_ishtp_cl;
8330b28cb4bSSrinivas Pandruvada 	client_data->cl_device = cl_device;
8340b28cb4bSSrinivas Pandruvada 
8350b28cb4bSSrinivas Pandruvada 	init_waitqueue_head(&client_data->init_wait);
8360b28cb4bSSrinivas Pandruvada 	init_waitqueue_head(&client_data->ishtp_resume_wait);
8370b28cb4bSSrinivas Pandruvada 
8380b28cb4bSSrinivas Pandruvada 	INIT_WORK(&client_data->work, hid_ishtp_cl_reset_handler);
839e48bf29cSYe Xiang 	INIT_WORK(&client_data->resume_work, hid_ishtp_cl_resume_handler);
840e48bf29cSYe Xiang 
8410b28cb4bSSrinivas Pandruvada 
842c57179c7SLee Jones 	ishtp_hid_print_trace = ishtp_trace_callback(cl_device);
8437ab21842SSrinivas Pandruvada 
8440b28cb4bSSrinivas Pandruvada 	rv = hid_ishtp_cl_init(hid_ishtp_cl, 0);
8450b28cb4bSSrinivas Pandruvada 	if (rv) {
8460b28cb4bSSrinivas Pandruvada 		ishtp_cl_free(hid_ishtp_cl);
8470b28cb4bSSrinivas Pandruvada 		return rv;
8480b28cb4bSSrinivas Pandruvada 	}
8490b28cb4bSSrinivas Pandruvada 	ishtp_get_device(cl_device);
8500b28cb4bSSrinivas Pandruvada 
8510b28cb4bSSrinivas Pandruvada 	return 0;
8520b28cb4bSSrinivas Pandruvada }
8530b28cb4bSSrinivas Pandruvada 
8540b28cb4bSSrinivas Pandruvada /**
8550b28cb4bSSrinivas Pandruvada  * hid_ishtp_cl_remove() - ISHTP client driver remove
8560b28cb4bSSrinivas Pandruvada  * @cl_device:		ISHTP client device instance
8570b28cb4bSSrinivas Pandruvada  *
8580b28cb4bSSrinivas Pandruvada  * This function gets called on device remove on ISHTP bus
8590b28cb4bSSrinivas Pandruvada  *
8600b28cb4bSSrinivas Pandruvada  * Return: 0
8610b28cb4bSSrinivas Pandruvada  */
hid_ishtp_cl_remove(struct ishtp_cl_device * cl_device)862e71da1fdSUwe Kleine-König static void hid_ishtp_cl_remove(struct ishtp_cl_device *cl_device)
8630b28cb4bSSrinivas Pandruvada {
864d174c666SEven Xu 	struct ishtp_cl *hid_ishtp_cl = ishtp_get_drvdata(cl_device);
86529b06d12SSrinivas Pandruvada 	struct ishtp_cl_data *client_data = ishtp_get_client_data(hid_ishtp_cl);
8660b28cb4bSSrinivas Pandruvada 
8670b28cb4bSSrinivas Pandruvada 	hid_ishtp_trace(client_data, "%s hid_ishtp_cl %p\n", __func__,
8680b28cb4bSSrinivas Pandruvada 			hid_ishtp_cl);
8690b28cb4bSSrinivas Pandruvada 
8707ab21842SSrinivas Pandruvada 	dev_dbg(ishtp_device(cl_device), "%s\n", __func__);
87129b06d12SSrinivas Pandruvada 	ishtp_set_connection_state(hid_ishtp_cl, ISHTP_CL_DISCONNECTING);
8720b28cb4bSSrinivas Pandruvada 	ishtp_cl_disconnect(hid_ishtp_cl);
8730b28cb4bSSrinivas Pandruvada 	ishtp_put_device(cl_device);
8740b28cb4bSSrinivas Pandruvada 	ishtp_hid_remove(client_data);
8750b28cb4bSSrinivas Pandruvada 	hid_ishtp_cl_deinit(hid_ishtp_cl);
8760b28cb4bSSrinivas Pandruvada 
8770b28cb4bSSrinivas Pandruvada 	hid_ishtp_cl = NULL;
8780b28cb4bSSrinivas Pandruvada 
8790b28cb4bSSrinivas Pandruvada 	client_data->num_hid_devices = 0;
8800b28cb4bSSrinivas Pandruvada }
8810b28cb4bSSrinivas Pandruvada 
8820b28cb4bSSrinivas Pandruvada /**
8830b28cb4bSSrinivas Pandruvada  * hid_ishtp_cl_reset() - ISHTP client driver reset
8840b28cb4bSSrinivas Pandruvada  * @cl_device:		ISHTP client device instance
8850b28cb4bSSrinivas Pandruvada  *
8860b28cb4bSSrinivas Pandruvada  * This function gets called on device reset on ISHTP bus
8870b28cb4bSSrinivas Pandruvada  *
8880b28cb4bSSrinivas Pandruvada  * Return: 0
8890b28cb4bSSrinivas Pandruvada  */
hid_ishtp_cl_reset(struct ishtp_cl_device * cl_device)8900b28cb4bSSrinivas Pandruvada static int hid_ishtp_cl_reset(struct ishtp_cl_device *cl_device)
8910b28cb4bSSrinivas Pandruvada {
892d174c666SEven Xu 	struct ishtp_cl *hid_ishtp_cl = ishtp_get_drvdata(cl_device);
89329b06d12SSrinivas Pandruvada 	struct ishtp_cl_data *client_data = ishtp_get_client_data(hid_ishtp_cl);
8940b28cb4bSSrinivas Pandruvada 
8950b28cb4bSSrinivas Pandruvada 	hid_ishtp_trace(client_data, "%s hid_ishtp_cl %p\n", __func__,
8960b28cb4bSSrinivas Pandruvada 			hid_ishtp_cl);
8970b28cb4bSSrinivas Pandruvada 
8980b28cb4bSSrinivas Pandruvada 	schedule_work(&client_data->work);
8990b28cb4bSSrinivas Pandruvada 
9000b28cb4bSSrinivas Pandruvada 	return 0;
9010b28cb4bSSrinivas Pandruvada }
9020b28cb4bSSrinivas Pandruvada 
9030b28cb4bSSrinivas Pandruvada /**
9040b28cb4bSSrinivas Pandruvada  * hid_ishtp_cl_suspend() - ISHTP client driver suspend
9050b28cb4bSSrinivas Pandruvada  * @device:	device instance
9060b28cb4bSSrinivas Pandruvada  *
9070b28cb4bSSrinivas Pandruvada  * This function gets called on system suspend
9080b28cb4bSSrinivas Pandruvada  *
9090b28cb4bSSrinivas Pandruvada  * Return: 0
9100b28cb4bSSrinivas Pandruvada  */
hid_ishtp_cl_suspend(struct device * device)9110b28cb4bSSrinivas Pandruvada static int hid_ishtp_cl_suspend(struct device *device)
9120b28cb4bSSrinivas Pandruvada {
913b12bbdc5SHyungwoo Yang 	struct ishtp_cl_device *cl_device = ishtp_dev_to_cl_device(device);
914d174c666SEven Xu 	struct ishtp_cl *hid_ishtp_cl = ishtp_get_drvdata(cl_device);
91529b06d12SSrinivas Pandruvada 	struct ishtp_cl_data *client_data = ishtp_get_client_data(hid_ishtp_cl);
9160b28cb4bSSrinivas Pandruvada 
9170b28cb4bSSrinivas Pandruvada 	hid_ishtp_trace(client_data, "%s hid_ishtp_cl %p\n", __func__,
9180b28cb4bSSrinivas Pandruvada 			hid_ishtp_cl);
9190b28cb4bSSrinivas Pandruvada 	client_data->suspended = true;
9200b28cb4bSSrinivas Pandruvada 
9210b28cb4bSSrinivas Pandruvada 	return 0;
9220b28cb4bSSrinivas Pandruvada }
9230b28cb4bSSrinivas Pandruvada 
9240b28cb4bSSrinivas Pandruvada /**
9250b28cb4bSSrinivas Pandruvada  * hid_ishtp_cl_resume() - ISHTP client driver resume
9260b28cb4bSSrinivas Pandruvada  * @device:	device instance
9270b28cb4bSSrinivas Pandruvada  *
9280b28cb4bSSrinivas Pandruvada  * This function gets called on system resume
9290b28cb4bSSrinivas Pandruvada  *
9300b28cb4bSSrinivas Pandruvada  * Return: 0
9310b28cb4bSSrinivas Pandruvada  */
hid_ishtp_cl_resume(struct device * device)9320b28cb4bSSrinivas Pandruvada static int hid_ishtp_cl_resume(struct device *device)
9330b28cb4bSSrinivas Pandruvada {
934b12bbdc5SHyungwoo Yang 	struct ishtp_cl_device *cl_device = ishtp_dev_to_cl_device(device);
935d174c666SEven Xu 	struct ishtp_cl *hid_ishtp_cl = ishtp_get_drvdata(cl_device);
93629b06d12SSrinivas Pandruvada 	struct ishtp_cl_data *client_data = ishtp_get_client_data(hid_ishtp_cl);
9370b28cb4bSSrinivas Pandruvada 
9380b28cb4bSSrinivas Pandruvada 	hid_ishtp_trace(client_data, "%s hid_ishtp_cl %p\n", __func__,
9390b28cb4bSSrinivas Pandruvada 			hid_ishtp_cl);
940e48bf29cSYe Xiang 	schedule_work(&client_data->resume_work);
9410b28cb4bSSrinivas Pandruvada 	return 0;
9420b28cb4bSSrinivas Pandruvada }
9430b28cb4bSSrinivas Pandruvada 
9440b28cb4bSSrinivas Pandruvada static const struct dev_pm_ops hid_ishtp_pm_ops = {
9450b28cb4bSSrinivas Pandruvada 	.suspend = hid_ishtp_cl_suspend,
9460b28cb4bSSrinivas Pandruvada 	.resume = hid_ishtp_cl_resume,
9470b28cb4bSSrinivas Pandruvada };
9480b28cb4bSSrinivas Pandruvada 
9490b28cb4bSSrinivas Pandruvada static struct ishtp_cl_driver	hid_ishtp_cl_driver = {
9500b28cb4bSSrinivas Pandruvada 	.name = "ish-hid",
951bf9167a8SArnd Bergmann 	.id = hid_ishtp_id_table,
9520b28cb4bSSrinivas Pandruvada 	.probe = hid_ishtp_cl_probe,
9530b28cb4bSSrinivas Pandruvada 	.remove = hid_ishtp_cl_remove,
9540b28cb4bSSrinivas Pandruvada 	.reset = hid_ishtp_cl_reset,
9550b28cb4bSSrinivas Pandruvada 	.driver.pm = &hid_ishtp_pm_ops,
9560b28cb4bSSrinivas Pandruvada };
9570b28cb4bSSrinivas Pandruvada 
ish_hid_init(void)9580b28cb4bSSrinivas Pandruvada static int __init ish_hid_init(void)
9590b28cb4bSSrinivas Pandruvada {
9600b28cb4bSSrinivas Pandruvada 	int	rv;
9610b28cb4bSSrinivas Pandruvada 
9620b28cb4bSSrinivas Pandruvada 	/* Register ISHTP client device driver with ISHTP Bus */
963e00a864fSSrinivas Pandruvada 	rv = ishtp_cl_driver_register(&hid_ishtp_cl_driver, THIS_MODULE);
9640b28cb4bSSrinivas Pandruvada 
9650b28cb4bSSrinivas Pandruvada 	return rv;
9660b28cb4bSSrinivas Pandruvada 
9670b28cb4bSSrinivas Pandruvada }
9680b28cb4bSSrinivas Pandruvada 
ish_hid_exit(void)9690b28cb4bSSrinivas Pandruvada static void __exit ish_hid_exit(void)
9700b28cb4bSSrinivas Pandruvada {
9710b28cb4bSSrinivas Pandruvada 	ishtp_cl_driver_unregister(&hid_ishtp_cl_driver);
9720b28cb4bSSrinivas Pandruvada }
9730b28cb4bSSrinivas Pandruvada 
9740b28cb4bSSrinivas Pandruvada late_initcall(ish_hid_init);
9750b28cb4bSSrinivas Pandruvada module_exit(ish_hid_exit);
9760b28cb4bSSrinivas Pandruvada 
9770b28cb4bSSrinivas Pandruvada MODULE_DESCRIPTION("ISH ISHTP HID client driver");
9780b28cb4bSSrinivas Pandruvada /* Primary author */
9790b28cb4bSSrinivas Pandruvada MODULE_AUTHOR("Daniel Drubin <daniel.drubin@intel.com>");
9800b28cb4bSSrinivas Pandruvada /*
9810b28cb4bSSrinivas Pandruvada  * Several modification for multi instance support
9820b28cb4bSSrinivas Pandruvada  * suspend/resume and clean up
9830b28cb4bSSrinivas Pandruvada  */
9840b28cb4bSSrinivas Pandruvada MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
9850b28cb4bSSrinivas Pandruvada 
9860b28cb4bSSrinivas Pandruvada MODULE_LICENSE("GPL");
987