10b28cb4bSSrinivas Pandruvada /*
20b28cb4bSSrinivas Pandruvada  * ISHTP client driver for HID (ISH)
30b28cb4bSSrinivas Pandruvada  *
40b28cb4bSSrinivas Pandruvada  * Copyright (c) 2014-2016, Intel Corporation.
50b28cb4bSSrinivas Pandruvada  *
60b28cb4bSSrinivas Pandruvada  * This program is free software; you can redistribute it and/or modify it
70b28cb4bSSrinivas Pandruvada  * under the terms and conditions of the GNU General Public License,
80b28cb4bSSrinivas Pandruvada  * version 2, as published by the Free Software Foundation.
90b28cb4bSSrinivas Pandruvada  *
100b28cb4bSSrinivas Pandruvada  * This program is distributed in the hope it will be useful, but WITHOUT
110b28cb4bSSrinivas Pandruvada  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
120b28cb4bSSrinivas Pandruvada  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
130b28cb4bSSrinivas Pandruvada  * more details.
140b28cb4bSSrinivas Pandruvada  */
150b28cb4bSSrinivas Pandruvada 
160b28cb4bSSrinivas Pandruvada #include <linux/module.h>
170b28cb4bSSrinivas Pandruvada #include <linux/hid.h>
180b28cb4bSSrinivas Pandruvada #include <linux/sched.h>
190b28cb4bSSrinivas Pandruvada #include "ishtp/ishtp-dev.h"
200b28cb4bSSrinivas Pandruvada #include "ishtp/client.h"
210b28cb4bSSrinivas Pandruvada #include "ishtp-hid.h"
220b28cb4bSSrinivas Pandruvada 
230b28cb4bSSrinivas Pandruvada /* Rx ring buffer pool size */
240b28cb4bSSrinivas Pandruvada #define HID_CL_RX_RING_SIZE	32
250b28cb4bSSrinivas Pandruvada #define HID_CL_TX_RING_SIZE	16
260b28cb4bSSrinivas Pandruvada 
270b28cb4bSSrinivas Pandruvada /**
280b28cb4bSSrinivas Pandruvada  * report_bad_packets() - Report bad packets
290b28cb4bSSrinivas Pandruvada  * @hid_ishtp_cl:	Client instance to get stats
300b28cb4bSSrinivas Pandruvada  * @recv_buf:		Raw received host interface message
310b28cb4bSSrinivas Pandruvada  * @cur_pos:		Current position index in payload
320b28cb4bSSrinivas Pandruvada  * @payload_len:	Length of payload expected
330b28cb4bSSrinivas Pandruvada  *
340b28cb4bSSrinivas Pandruvada  * Dumps error in case bad packet is received
350b28cb4bSSrinivas Pandruvada  */
360b28cb4bSSrinivas Pandruvada static void report_bad_packet(struct ishtp_cl *hid_ishtp_cl, void *recv_buf,
370b28cb4bSSrinivas Pandruvada 			      size_t cur_pos,  size_t payload_len)
380b28cb4bSSrinivas Pandruvada {
390b28cb4bSSrinivas Pandruvada 	struct hostif_msg *recv_msg = recv_buf;
400b28cb4bSSrinivas Pandruvada 	struct ishtp_cl_data *client_data = hid_ishtp_cl->client_data;
410b28cb4bSSrinivas Pandruvada 
420b28cb4bSSrinivas Pandruvada 	dev_err(&client_data->cl_device->dev, "[hid-ish]: BAD packet %02X\n"
430b28cb4bSSrinivas Pandruvada 		"total_bad=%u cur_pos=%u\n"
440b28cb4bSSrinivas Pandruvada 		"[%02X %02X %02X %02X]\n"
450b28cb4bSSrinivas Pandruvada 		"payload_len=%u\n"
460b28cb4bSSrinivas Pandruvada 		"multi_packet_cnt=%u\n"
470b28cb4bSSrinivas Pandruvada 		"is_response=%02X\n",
480b28cb4bSSrinivas Pandruvada 		recv_msg->hdr.command, client_data->bad_recv_cnt,
490b28cb4bSSrinivas Pandruvada 		(unsigned int)cur_pos,
500b28cb4bSSrinivas Pandruvada 		((unsigned char *)recv_msg)[0], ((unsigned char *)recv_msg)[1],
510b28cb4bSSrinivas Pandruvada 		((unsigned char *)recv_msg)[2], ((unsigned char *)recv_msg)[3],
520b28cb4bSSrinivas Pandruvada 		(unsigned int)payload_len, client_data->multi_packet_cnt,
530b28cb4bSSrinivas Pandruvada 		recv_msg->hdr.command & ~CMD_MASK);
540b28cb4bSSrinivas Pandruvada }
550b28cb4bSSrinivas Pandruvada 
560b28cb4bSSrinivas Pandruvada /**
570b28cb4bSSrinivas Pandruvada  * process_recv() - Received and parse incoming packet
580b28cb4bSSrinivas Pandruvada  * @hid_ishtp_cl:	Client instance to get stats
590b28cb4bSSrinivas Pandruvada  * @recv_buf:		Raw received host interface message
600b28cb4bSSrinivas Pandruvada  * @data_len:		length of the message
610b28cb4bSSrinivas Pandruvada  *
620b28cb4bSSrinivas Pandruvada  * Parse the incoming packet. If it is a response packet then it will update
630b28cb4bSSrinivas Pandruvada  * per instance flags and wake up the caller waiting to for the response.
640b28cb4bSSrinivas Pandruvada  */
650b28cb4bSSrinivas Pandruvada static void process_recv(struct ishtp_cl *hid_ishtp_cl, void *recv_buf,
660b28cb4bSSrinivas Pandruvada 			 size_t data_len)
670b28cb4bSSrinivas Pandruvada {
680b28cb4bSSrinivas Pandruvada 	struct hostif_msg *recv_msg;
690b28cb4bSSrinivas Pandruvada 	unsigned char *payload;
700b28cb4bSSrinivas Pandruvada 	struct device_info *dev_info;
710b28cb4bSSrinivas Pandruvada 	int i, j;
720b28cb4bSSrinivas Pandruvada 	size_t	payload_len, total_len, cur_pos;
730b28cb4bSSrinivas Pandruvada 	int report_type;
740b28cb4bSSrinivas Pandruvada 	struct report_list *reports_list;
750b28cb4bSSrinivas Pandruvada 	char *reports;
760b28cb4bSSrinivas Pandruvada 	size_t report_len;
770b28cb4bSSrinivas Pandruvada 	struct ishtp_cl_data *client_data = hid_ishtp_cl->client_data;
780b28cb4bSSrinivas Pandruvada 	int curr_hid_dev = client_data->cur_hid_dev;
790b28cb4bSSrinivas Pandruvada 
800b28cb4bSSrinivas Pandruvada 	if (data_len < sizeof(struct hostif_msg_hdr)) {
810b28cb4bSSrinivas Pandruvada 		dev_err(&client_data->cl_device->dev,
820b28cb4bSSrinivas Pandruvada 			"[hid-ish]: error, received %u which is less than data header %u\n",
830b28cb4bSSrinivas Pandruvada 			(unsigned int)data_len,
840b28cb4bSSrinivas Pandruvada 			(unsigned int)sizeof(struct hostif_msg_hdr));
850b28cb4bSSrinivas Pandruvada 		++client_data->bad_recv_cnt;
860b28cb4bSSrinivas Pandruvada 		ish_hw_reset(hid_ishtp_cl->dev);
870b28cb4bSSrinivas Pandruvada 		return;
880b28cb4bSSrinivas Pandruvada 	}
890b28cb4bSSrinivas Pandruvada 
900b28cb4bSSrinivas Pandruvada 	payload = recv_buf + sizeof(struct hostif_msg_hdr);
910b28cb4bSSrinivas Pandruvada 	total_len = data_len;
920b28cb4bSSrinivas Pandruvada 	cur_pos = 0;
930b28cb4bSSrinivas Pandruvada 
940b28cb4bSSrinivas Pandruvada 	do {
950b28cb4bSSrinivas Pandruvada 		recv_msg = (struct hostif_msg *)(recv_buf + cur_pos);
960b28cb4bSSrinivas Pandruvada 		payload_len = recv_msg->hdr.size;
970b28cb4bSSrinivas Pandruvada 
980b28cb4bSSrinivas Pandruvada 		/* Sanity checks */
990b28cb4bSSrinivas Pandruvada 		if (cur_pos + payload_len + sizeof(struct hostif_msg) >
1000b28cb4bSSrinivas Pandruvada 				total_len) {
1010b28cb4bSSrinivas Pandruvada 			++client_data->bad_recv_cnt;
1020b28cb4bSSrinivas Pandruvada 			report_bad_packet(hid_ishtp_cl, recv_msg, cur_pos,
1030b28cb4bSSrinivas Pandruvada 					  payload_len);
1040b28cb4bSSrinivas Pandruvada 			ish_hw_reset(hid_ishtp_cl->dev);
1050b28cb4bSSrinivas Pandruvada 			break;
1060b28cb4bSSrinivas Pandruvada 		}
1070b28cb4bSSrinivas Pandruvada 
1080b28cb4bSSrinivas Pandruvada 		hid_ishtp_trace(client_data,  "%s %d\n",
1090b28cb4bSSrinivas Pandruvada 				__func__, recv_msg->hdr.command & CMD_MASK);
1100b28cb4bSSrinivas Pandruvada 
1110b28cb4bSSrinivas Pandruvada 		switch (recv_msg->hdr.command & CMD_MASK) {
1120b28cb4bSSrinivas Pandruvada 		case HOSTIF_DM_ENUM_DEVICES:
1130b28cb4bSSrinivas Pandruvada 			if ((!(recv_msg->hdr.command & ~CMD_MASK) ||
1140b28cb4bSSrinivas Pandruvada 					client_data->init_done)) {
1150b28cb4bSSrinivas Pandruvada 				++client_data->bad_recv_cnt;
1160b28cb4bSSrinivas Pandruvada 				report_bad_packet(hid_ishtp_cl, recv_msg,
1170b28cb4bSSrinivas Pandruvada 						  cur_pos,
1180b28cb4bSSrinivas Pandruvada 						  payload_len);
1190b28cb4bSSrinivas Pandruvada 				ish_hw_reset(hid_ishtp_cl->dev);
1200b28cb4bSSrinivas Pandruvada 				break;
1210b28cb4bSSrinivas Pandruvada 			}
1220b28cb4bSSrinivas Pandruvada 			client_data->hid_dev_count = (unsigned int)*payload;
1230b28cb4bSSrinivas Pandruvada 			if (!client_data->hid_devices)
1240b28cb4bSSrinivas Pandruvada 				client_data->hid_devices = devm_kzalloc(
1250b28cb4bSSrinivas Pandruvada 						&client_data->cl_device->dev,
1260b28cb4bSSrinivas Pandruvada 						client_data->hid_dev_count *
1270b28cb4bSSrinivas Pandruvada 						sizeof(struct device_info),
1280b28cb4bSSrinivas Pandruvada 						GFP_KERNEL);
1290b28cb4bSSrinivas Pandruvada 			if (!client_data->hid_devices) {
1300b28cb4bSSrinivas Pandruvada 				dev_err(&client_data->cl_device->dev,
1310b28cb4bSSrinivas Pandruvada 				"Mem alloc failed for hid device info\n");
1320b28cb4bSSrinivas Pandruvada 				wake_up_interruptible(&client_data->init_wait);
1330b28cb4bSSrinivas Pandruvada 				break;
1340b28cb4bSSrinivas Pandruvada 			}
1350b28cb4bSSrinivas Pandruvada 			for (i = 0; i < client_data->hid_dev_count; ++i) {
1360b28cb4bSSrinivas Pandruvada 				if (1 + sizeof(struct device_info) * i >=
1370b28cb4bSSrinivas Pandruvada 						payload_len) {
1380b28cb4bSSrinivas Pandruvada 					dev_err(&client_data->cl_device->dev,
139318fc2a8SArnd Bergmann 						"[hid-ish]: [ENUM_DEVICES]: content size %zu is bigger than payload_len %zu\n",
1400b28cb4bSSrinivas Pandruvada 						1 + sizeof(struct device_info)
141318fc2a8SArnd Bergmann 						* i, payload_len);
1420b28cb4bSSrinivas Pandruvada 				}
1430b28cb4bSSrinivas Pandruvada 
1440b28cb4bSSrinivas Pandruvada 				if (1 + sizeof(struct device_info) * i >=
1450b28cb4bSSrinivas Pandruvada 						data_len)
1460b28cb4bSSrinivas Pandruvada 					break;
1470b28cb4bSSrinivas Pandruvada 
1480b28cb4bSSrinivas Pandruvada 				dev_info = (struct device_info *)(payload + 1 +
1490b28cb4bSSrinivas Pandruvada 					sizeof(struct device_info) * i);
1500b28cb4bSSrinivas Pandruvada 				if (client_data->hid_devices)
1510b28cb4bSSrinivas Pandruvada 					memcpy(client_data->hid_devices + i,
1520b28cb4bSSrinivas Pandruvada 					       dev_info,
1530b28cb4bSSrinivas Pandruvada 					       sizeof(struct device_info));
1540b28cb4bSSrinivas Pandruvada 			}
1550b28cb4bSSrinivas Pandruvada 
1560b28cb4bSSrinivas Pandruvada 			client_data->enum_devices_done = true;
1570b28cb4bSSrinivas Pandruvada 			wake_up_interruptible(&client_data->init_wait);
1580b28cb4bSSrinivas Pandruvada 
1590b28cb4bSSrinivas Pandruvada 			break;
1600b28cb4bSSrinivas Pandruvada 
1610b28cb4bSSrinivas Pandruvada 		case HOSTIF_GET_HID_DESCRIPTOR:
1620b28cb4bSSrinivas Pandruvada 			if ((!(recv_msg->hdr.command & ~CMD_MASK) ||
1630b28cb4bSSrinivas Pandruvada 					client_data->init_done)) {
1640b28cb4bSSrinivas Pandruvada 				++client_data->bad_recv_cnt;
1650b28cb4bSSrinivas Pandruvada 				report_bad_packet(hid_ishtp_cl, recv_msg,
1660b28cb4bSSrinivas Pandruvada 						  cur_pos,
1670b28cb4bSSrinivas Pandruvada 						  payload_len);
1680b28cb4bSSrinivas Pandruvada 				ish_hw_reset(hid_ishtp_cl->dev);
1690b28cb4bSSrinivas Pandruvada 				break;
1700b28cb4bSSrinivas Pandruvada 			}
1710b28cb4bSSrinivas Pandruvada 			if (!client_data->hid_descr[curr_hid_dev])
1720b28cb4bSSrinivas Pandruvada 				client_data->hid_descr[curr_hid_dev] =
1730b28cb4bSSrinivas Pandruvada 				devm_kmalloc(&client_data->cl_device->dev,
1740b28cb4bSSrinivas Pandruvada 					     payload_len, GFP_KERNEL);
1750b28cb4bSSrinivas Pandruvada 			if (client_data->hid_descr[curr_hid_dev]) {
1760b28cb4bSSrinivas Pandruvada 				memcpy(client_data->hid_descr[curr_hid_dev],
1770b28cb4bSSrinivas Pandruvada 				       payload, payload_len);
1780b28cb4bSSrinivas Pandruvada 				client_data->hid_descr_size[curr_hid_dev] =
1790b28cb4bSSrinivas Pandruvada 					payload_len;
1800b28cb4bSSrinivas Pandruvada 				client_data->hid_descr_done = true;
1810b28cb4bSSrinivas Pandruvada 			}
1820b28cb4bSSrinivas Pandruvada 			wake_up_interruptible(&client_data->init_wait);
1830b28cb4bSSrinivas Pandruvada 
1840b28cb4bSSrinivas Pandruvada 			break;
1850b28cb4bSSrinivas Pandruvada 
1860b28cb4bSSrinivas Pandruvada 		case HOSTIF_GET_REPORT_DESCRIPTOR:
1870b28cb4bSSrinivas Pandruvada 			if ((!(recv_msg->hdr.command & ~CMD_MASK) ||
1880b28cb4bSSrinivas Pandruvada 					client_data->init_done)) {
1890b28cb4bSSrinivas Pandruvada 				++client_data->bad_recv_cnt;
1900b28cb4bSSrinivas Pandruvada 				report_bad_packet(hid_ishtp_cl, recv_msg,
1910b28cb4bSSrinivas Pandruvada 						  cur_pos,
1920b28cb4bSSrinivas Pandruvada 						  payload_len);
1930b28cb4bSSrinivas Pandruvada 				ish_hw_reset(hid_ishtp_cl->dev);
1940b28cb4bSSrinivas Pandruvada 				break;
1950b28cb4bSSrinivas Pandruvada 			}
1960b28cb4bSSrinivas Pandruvada 			if (!client_data->report_descr[curr_hid_dev])
1970b28cb4bSSrinivas Pandruvada 				client_data->report_descr[curr_hid_dev] =
1980b28cb4bSSrinivas Pandruvada 				devm_kmalloc(&client_data->cl_device->dev,
1990b28cb4bSSrinivas Pandruvada 					     payload_len, GFP_KERNEL);
2000b28cb4bSSrinivas Pandruvada 			if (client_data->report_descr[curr_hid_dev])  {
2010b28cb4bSSrinivas Pandruvada 				memcpy(client_data->report_descr[curr_hid_dev],
2020b28cb4bSSrinivas Pandruvada 				       payload,
2030b28cb4bSSrinivas Pandruvada 				       payload_len);
2040b28cb4bSSrinivas Pandruvada 				client_data->report_descr_size[curr_hid_dev] =
2050b28cb4bSSrinivas Pandruvada 					payload_len;
2060b28cb4bSSrinivas Pandruvada 				client_data->report_descr_done = true;
2070b28cb4bSSrinivas Pandruvada 			}
2080b28cb4bSSrinivas Pandruvada 			wake_up_interruptible(&client_data->init_wait);
2090b28cb4bSSrinivas Pandruvada 
2100b28cb4bSSrinivas Pandruvada 			break;
2110b28cb4bSSrinivas Pandruvada 
2120b28cb4bSSrinivas Pandruvada 		case HOSTIF_GET_FEATURE_REPORT:
2130b28cb4bSSrinivas Pandruvada 			report_type = HID_FEATURE_REPORT;
2140b28cb4bSSrinivas Pandruvada 			goto	do_get_report;
2150b28cb4bSSrinivas Pandruvada 
2160b28cb4bSSrinivas Pandruvada 		case HOSTIF_GET_INPUT_REPORT:
2170b28cb4bSSrinivas Pandruvada 			report_type = HID_INPUT_REPORT;
2180b28cb4bSSrinivas Pandruvada do_get_report:
2190b28cb4bSSrinivas Pandruvada 			/* Get index of device that matches this id */
2200b28cb4bSSrinivas Pandruvada 			for (i = 0; i < client_data->num_hid_devices; ++i) {
2210b28cb4bSSrinivas Pandruvada 				if (recv_msg->hdr.device_id ==
2220b28cb4bSSrinivas Pandruvada 					client_data->hid_devices[i].dev_id)
2230b28cb4bSSrinivas Pandruvada 					if (client_data->hid_sensor_hubs[i]) {
2240b28cb4bSSrinivas Pandruvada 						hid_input_report(
2250b28cb4bSSrinivas Pandruvada 						client_data->hid_sensor_hubs[
2260b28cb4bSSrinivas Pandruvada 									i],
2270b28cb4bSSrinivas Pandruvada 						report_type, payload,
2280b28cb4bSSrinivas Pandruvada 						payload_len, 0);
2290b28cb4bSSrinivas Pandruvada 						ishtp_hid_wakeup(
2300b28cb4bSSrinivas Pandruvada 						client_data->hid_sensor_hubs[
2310b28cb4bSSrinivas Pandruvada 							i]);
2320b28cb4bSSrinivas Pandruvada 						break;
2330b28cb4bSSrinivas Pandruvada 					}
2340b28cb4bSSrinivas Pandruvada 			}
2350b28cb4bSSrinivas Pandruvada 			break;
2360b28cb4bSSrinivas Pandruvada 
2370b28cb4bSSrinivas Pandruvada 		case HOSTIF_SET_FEATURE_REPORT:
2380b28cb4bSSrinivas Pandruvada 			/* Get index of device that matches this id */
2390b28cb4bSSrinivas Pandruvada 			for (i = 0; i < client_data->num_hid_devices; ++i) {
2400b28cb4bSSrinivas Pandruvada 				if (recv_msg->hdr.device_id ==
2410b28cb4bSSrinivas Pandruvada 					client_data->hid_devices[i].dev_id)
2420b28cb4bSSrinivas Pandruvada 					if (client_data->hid_sensor_hubs[i]) {
2430b28cb4bSSrinivas Pandruvada 						ishtp_hid_wakeup(
2440b28cb4bSSrinivas Pandruvada 						client_data->hid_sensor_hubs[
2450b28cb4bSSrinivas Pandruvada 							i]);
2460b28cb4bSSrinivas Pandruvada 						break;
2470b28cb4bSSrinivas Pandruvada 					}
2480b28cb4bSSrinivas Pandruvada 			}
2490b28cb4bSSrinivas Pandruvada 			break;
2500b28cb4bSSrinivas Pandruvada 
2510b28cb4bSSrinivas Pandruvada 		case HOSTIF_PUBLISH_INPUT_REPORT:
2520b28cb4bSSrinivas Pandruvada 			report_type = HID_INPUT_REPORT;
2530b28cb4bSSrinivas Pandruvada 			for (i = 0; i < client_data->num_hid_devices; ++i)
2540b28cb4bSSrinivas Pandruvada 				if (recv_msg->hdr.device_id ==
2550b28cb4bSSrinivas Pandruvada 					client_data->hid_devices[i].dev_id)
2560b28cb4bSSrinivas Pandruvada 					if (client_data->hid_sensor_hubs[i])
2570b28cb4bSSrinivas Pandruvada 						hid_input_report(
2580b28cb4bSSrinivas Pandruvada 						client_data->hid_sensor_hubs[
2590b28cb4bSSrinivas Pandruvada 									i],
2600b28cb4bSSrinivas Pandruvada 						report_type, payload,
2610b28cb4bSSrinivas Pandruvada 						payload_len, 0);
2620b28cb4bSSrinivas Pandruvada 			break;
2630b28cb4bSSrinivas Pandruvada 
2640b28cb4bSSrinivas Pandruvada 		case HOSTIF_PUBLISH_INPUT_REPORT_LIST:
2650b28cb4bSSrinivas Pandruvada 			report_type = HID_INPUT_REPORT;
2660b28cb4bSSrinivas Pandruvada 			reports_list = (struct report_list *)payload;
2670b28cb4bSSrinivas Pandruvada 			reports = (char *)reports_list->reports;
2680b28cb4bSSrinivas Pandruvada 
2690b28cb4bSSrinivas Pandruvada 			for (j = 0; j < reports_list->num_of_reports; j++) {
2700b28cb4bSSrinivas Pandruvada 				recv_msg = (struct hostif_msg *)(reports +
2710b28cb4bSSrinivas Pandruvada 					sizeof(uint16_t));
2720b28cb4bSSrinivas Pandruvada 				report_len = *(uint16_t *)reports;
2730b28cb4bSSrinivas Pandruvada 				payload = reports + sizeof(uint16_t) +
2740b28cb4bSSrinivas Pandruvada 					sizeof(struct hostif_msg_hdr);
2750b28cb4bSSrinivas Pandruvada 				payload_len = report_len -
2760b28cb4bSSrinivas Pandruvada 					sizeof(struct hostif_msg_hdr);
2770b28cb4bSSrinivas Pandruvada 
2780b28cb4bSSrinivas Pandruvada 				for (i = 0; i < client_data->num_hid_devices;
2790b28cb4bSSrinivas Pandruvada 				     ++i)
2800b28cb4bSSrinivas Pandruvada 					if (recv_msg->hdr.device_id ==
2810b28cb4bSSrinivas Pandruvada 					client_data->hid_devices[i].dev_id &&
2820b28cb4bSSrinivas Pandruvada 					client_data->hid_sensor_hubs[i]) {
2830b28cb4bSSrinivas Pandruvada 						hid_input_report(
2840b28cb4bSSrinivas Pandruvada 						client_data->hid_sensor_hubs[
2850b28cb4bSSrinivas Pandruvada 									i],
2860b28cb4bSSrinivas Pandruvada 						report_type,
2870b28cb4bSSrinivas Pandruvada 						payload, payload_len,
2880b28cb4bSSrinivas Pandruvada 						0);
2890b28cb4bSSrinivas Pandruvada 					}
2900b28cb4bSSrinivas Pandruvada 
2910b28cb4bSSrinivas Pandruvada 				reports += sizeof(uint16_t) + report_len;
2920b28cb4bSSrinivas Pandruvada 			}
2930b28cb4bSSrinivas Pandruvada 			break;
2940b28cb4bSSrinivas Pandruvada 		default:
2950b28cb4bSSrinivas Pandruvada 			++client_data->bad_recv_cnt;
2960b28cb4bSSrinivas Pandruvada 			report_bad_packet(hid_ishtp_cl, recv_msg, cur_pos,
2970b28cb4bSSrinivas Pandruvada 					  payload_len);
2980b28cb4bSSrinivas Pandruvada 			ish_hw_reset(hid_ishtp_cl->dev);
2990b28cb4bSSrinivas Pandruvada 			break;
3000b28cb4bSSrinivas Pandruvada 
3010b28cb4bSSrinivas Pandruvada 		}
3020b28cb4bSSrinivas Pandruvada 
3030b28cb4bSSrinivas Pandruvada 		if (!cur_pos && cur_pos + payload_len +
3040b28cb4bSSrinivas Pandruvada 				sizeof(struct hostif_msg) < total_len)
3050b28cb4bSSrinivas Pandruvada 			++client_data->multi_packet_cnt;
3060b28cb4bSSrinivas Pandruvada 
3070b28cb4bSSrinivas Pandruvada 		cur_pos += payload_len + sizeof(struct hostif_msg);
3080b28cb4bSSrinivas Pandruvada 		payload += payload_len + sizeof(struct hostif_msg);
3090b28cb4bSSrinivas Pandruvada 
3100b28cb4bSSrinivas Pandruvada 	} while (cur_pos < total_len);
3110b28cb4bSSrinivas Pandruvada }
3120b28cb4bSSrinivas Pandruvada 
3130b28cb4bSSrinivas Pandruvada /**
3140b28cb4bSSrinivas Pandruvada  * ish_cl_event_cb() - bus driver callback for incoming message/packet
3150b28cb4bSSrinivas Pandruvada  * @device:	Pointer to the the ishtp client device for which this message
3160b28cb4bSSrinivas Pandruvada  *		is targeted
3170b28cb4bSSrinivas Pandruvada  *
3180b28cb4bSSrinivas Pandruvada  * Remove the packet from the list and process the message by calling
3190b28cb4bSSrinivas Pandruvada  * process_recv
3200b28cb4bSSrinivas Pandruvada  */
3210b28cb4bSSrinivas Pandruvada static void ish_cl_event_cb(struct ishtp_cl_device *device)
3220b28cb4bSSrinivas Pandruvada {
3230b28cb4bSSrinivas Pandruvada 	struct ishtp_cl	*hid_ishtp_cl = device->driver_data;
3240b28cb4bSSrinivas Pandruvada 	struct ishtp_cl_rb *rb_in_proc;
3250b28cb4bSSrinivas Pandruvada 	size_t r_length;
3260b28cb4bSSrinivas Pandruvada 	unsigned long flags;
3270b28cb4bSSrinivas Pandruvada 
3280b28cb4bSSrinivas Pandruvada 	if (!hid_ishtp_cl)
3290b28cb4bSSrinivas Pandruvada 		return;
3300b28cb4bSSrinivas Pandruvada 
3310b28cb4bSSrinivas Pandruvada 	spin_lock_irqsave(&hid_ishtp_cl->in_process_spinlock, flags);
3320b28cb4bSSrinivas Pandruvada 	while (!list_empty(&hid_ishtp_cl->in_process_list.list)) {
3330b28cb4bSSrinivas Pandruvada 		rb_in_proc = list_entry(
3340b28cb4bSSrinivas Pandruvada 			hid_ishtp_cl->in_process_list.list.next,
3350b28cb4bSSrinivas Pandruvada 			struct ishtp_cl_rb, list);
3360b28cb4bSSrinivas Pandruvada 		list_del_init(&rb_in_proc->list);
3370b28cb4bSSrinivas Pandruvada 		spin_unlock_irqrestore(&hid_ishtp_cl->in_process_spinlock,
3380b28cb4bSSrinivas Pandruvada 			flags);
3390b28cb4bSSrinivas Pandruvada 
3400b28cb4bSSrinivas Pandruvada 		if (!rb_in_proc->buffer.data)
3410b28cb4bSSrinivas Pandruvada 			return;
3420b28cb4bSSrinivas Pandruvada 
3430b28cb4bSSrinivas Pandruvada 		r_length = rb_in_proc->buf_idx;
3440b28cb4bSSrinivas Pandruvada 
3450b28cb4bSSrinivas Pandruvada 		/* decide what to do with received data */
3460b28cb4bSSrinivas Pandruvada 		process_recv(hid_ishtp_cl, rb_in_proc->buffer.data, r_length);
3470b28cb4bSSrinivas Pandruvada 
3480b28cb4bSSrinivas Pandruvada 		ishtp_cl_io_rb_recycle(rb_in_proc);
3490b28cb4bSSrinivas Pandruvada 		spin_lock_irqsave(&hid_ishtp_cl->in_process_spinlock, flags);
3500b28cb4bSSrinivas Pandruvada 	}
3510b28cb4bSSrinivas Pandruvada 	spin_unlock_irqrestore(&hid_ishtp_cl->in_process_spinlock, flags);
3520b28cb4bSSrinivas Pandruvada }
3530b28cb4bSSrinivas Pandruvada 
3540b28cb4bSSrinivas Pandruvada /**
3550b28cb4bSSrinivas Pandruvada  * hid_ishtp_set_feature() - send request to ISH FW to set a feature request
3560b28cb4bSSrinivas Pandruvada  * @hid:	hid device instance for this request
3570b28cb4bSSrinivas Pandruvada  * @buf:	feature buffer
3580b28cb4bSSrinivas Pandruvada  * @len:	Length of feature buffer
3590b28cb4bSSrinivas Pandruvada  * @report_id:	Report id for the feature set request
3600b28cb4bSSrinivas Pandruvada  *
3610b28cb4bSSrinivas Pandruvada  * This is called from hid core .request() callback. This function doesn't wait
3620b28cb4bSSrinivas Pandruvada  * for response.
3630b28cb4bSSrinivas Pandruvada  */
3640b28cb4bSSrinivas Pandruvada void hid_ishtp_set_feature(struct hid_device *hid, char *buf, unsigned int len,
3650b28cb4bSSrinivas Pandruvada 			   int report_id)
3660b28cb4bSSrinivas Pandruvada {
3670b28cb4bSSrinivas Pandruvada 	struct ishtp_hid_data *hid_data =  hid->driver_data;
3680b28cb4bSSrinivas Pandruvada 	struct ishtp_cl_data *client_data = hid_data->client_data;
3690b28cb4bSSrinivas Pandruvada 	struct hostif_msg *msg = (struct hostif_msg *)buf;
3700b28cb4bSSrinivas Pandruvada 	int	rv;
3710b28cb4bSSrinivas Pandruvada 	int	i;
3720b28cb4bSSrinivas Pandruvada 
3730b28cb4bSSrinivas Pandruvada 	hid_ishtp_trace(client_data,  "%s hid %p\n", __func__, hid);
3740b28cb4bSSrinivas Pandruvada 
3750b28cb4bSSrinivas Pandruvada 	rv = ishtp_hid_link_ready_wait(client_data);
3760b28cb4bSSrinivas Pandruvada 	if (rv) {
3770b28cb4bSSrinivas Pandruvada 		hid_ishtp_trace(client_data,  "%s hid %p link not ready\n",
3780b28cb4bSSrinivas Pandruvada 				__func__, hid);
3790b28cb4bSSrinivas Pandruvada 		return;
3800b28cb4bSSrinivas Pandruvada 	}
3810b28cb4bSSrinivas Pandruvada 
3820b28cb4bSSrinivas Pandruvada 	memset(msg, 0, sizeof(struct hostif_msg));
3830b28cb4bSSrinivas Pandruvada 	msg->hdr.command = HOSTIF_SET_FEATURE_REPORT;
3840b28cb4bSSrinivas Pandruvada 	for (i = 0; i < client_data->num_hid_devices; ++i) {
3850b28cb4bSSrinivas Pandruvada 		if (hid == client_data->hid_sensor_hubs[i]) {
3860b28cb4bSSrinivas Pandruvada 			msg->hdr.device_id =
3870b28cb4bSSrinivas Pandruvada 				client_data->hid_devices[i].dev_id;
3880b28cb4bSSrinivas Pandruvada 			break;
3890b28cb4bSSrinivas Pandruvada 		}
3900b28cb4bSSrinivas Pandruvada 	}
3910b28cb4bSSrinivas Pandruvada 
3920b28cb4bSSrinivas Pandruvada 	if (i == client_data->num_hid_devices)
3930b28cb4bSSrinivas Pandruvada 		return;
3940b28cb4bSSrinivas Pandruvada 
3950b28cb4bSSrinivas Pandruvada 	rv = ishtp_cl_send(client_data->hid_ishtp_cl, buf, len);
3960b28cb4bSSrinivas Pandruvada 	if (rv)
3970b28cb4bSSrinivas Pandruvada 		hid_ishtp_trace(client_data,  "%s hid %p send failed\n",
3980b28cb4bSSrinivas Pandruvada 				__func__, hid);
3990b28cb4bSSrinivas Pandruvada }
4000b28cb4bSSrinivas Pandruvada 
4010b28cb4bSSrinivas Pandruvada /**
4020b28cb4bSSrinivas Pandruvada  * hid_ishtp_get_report() - request to get feature/input report
4030b28cb4bSSrinivas Pandruvada  * @hid:	hid device instance for this request
4040b28cb4bSSrinivas Pandruvada  * @report_id:	Report id for the get request
4050b28cb4bSSrinivas Pandruvada  * @report_type:	Report type for the this request
4060b28cb4bSSrinivas Pandruvada  *
4070b28cb4bSSrinivas Pandruvada  * This is called from hid core .request() callback. This function will send
4080b28cb4bSSrinivas Pandruvada  * request to FW and return without waiting for response.
4090b28cb4bSSrinivas Pandruvada  */
4100b28cb4bSSrinivas Pandruvada void hid_ishtp_get_report(struct hid_device *hid, int report_id,
4110b28cb4bSSrinivas Pandruvada 			  int report_type)
4120b28cb4bSSrinivas Pandruvada {
4130b28cb4bSSrinivas Pandruvada 	struct ishtp_hid_data *hid_data =  hid->driver_data;
4140b28cb4bSSrinivas Pandruvada 	struct ishtp_cl_data *client_data = hid_data->client_data;
4150b28cb4bSSrinivas Pandruvada 	static unsigned char	buf[10];
4160b28cb4bSSrinivas Pandruvada 	unsigned int	len;
4170b28cb4bSSrinivas Pandruvada 	struct hostif_msg_to_sensor *msg = (struct hostif_msg_to_sensor *)buf;
4180b28cb4bSSrinivas Pandruvada 	int	rv;
4190b28cb4bSSrinivas Pandruvada 	int	i;
4200b28cb4bSSrinivas Pandruvada 
4210b28cb4bSSrinivas Pandruvada 	hid_ishtp_trace(client_data,  "%s hid %p\n", __func__, hid);
4220b28cb4bSSrinivas Pandruvada 	rv = ishtp_hid_link_ready_wait(client_data);
4230b28cb4bSSrinivas Pandruvada 	if (rv) {
4240b28cb4bSSrinivas Pandruvada 		hid_ishtp_trace(client_data,  "%s hid %p link not ready\n",
4250b28cb4bSSrinivas Pandruvada 				__func__, hid);
4260b28cb4bSSrinivas Pandruvada 		return;
4270b28cb4bSSrinivas Pandruvada 	}
4280b28cb4bSSrinivas Pandruvada 
4290b28cb4bSSrinivas Pandruvada 	len = sizeof(struct hostif_msg_to_sensor);
4300b28cb4bSSrinivas Pandruvada 
4310b28cb4bSSrinivas Pandruvada 	memset(msg, 0, sizeof(struct hostif_msg_to_sensor));
4320b28cb4bSSrinivas Pandruvada 	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]) {
4360b28cb4bSSrinivas Pandruvada 			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 
4450b28cb4bSSrinivas Pandruvada 	msg->report_id = report_id;
4460b28cb4bSSrinivas Pandruvada 	rv = ishtp_cl_send(client_data->hid_ishtp_cl, buf, len);
4470b28cb4bSSrinivas Pandruvada 	if (rv)
4480b28cb4bSSrinivas Pandruvada 		hid_ishtp_trace(client_data,  "%s hid %p send failed\n",
4490b28cb4bSSrinivas Pandruvada 				__func__, hid);
4500b28cb4bSSrinivas Pandruvada }
4510b28cb4bSSrinivas Pandruvada 
4520b28cb4bSSrinivas Pandruvada /**
4530b28cb4bSSrinivas Pandruvada  * ishtp_hid_link_ready_wait() - Wait for link ready
4540b28cb4bSSrinivas Pandruvada  * @client_data:	client data instance
4550b28cb4bSSrinivas Pandruvada  *
4560b28cb4bSSrinivas Pandruvada  * If the transport link started suspend process, then wait, till either
4570b28cb4bSSrinivas Pandruvada  * resumed or timeout
4580b28cb4bSSrinivas Pandruvada  *
4590b28cb4bSSrinivas Pandruvada  * Return: 0 on success, non zero on error
4600b28cb4bSSrinivas Pandruvada  */
4610b28cb4bSSrinivas Pandruvada int ishtp_hid_link_ready_wait(struct ishtp_cl_data *client_data)
4620b28cb4bSSrinivas Pandruvada {
4630b28cb4bSSrinivas Pandruvada 	int rc;
4640b28cb4bSSrinivas Pandruvada 
4650b28cb4bSSrinivas Pandruvada 	if (client_data->suspended) {
4660b28cb4bSSrinivas Pandruvada 		hid_ishtp_trace(client_data,  "wait for link ready\n");
4670b28cb4bSSrinivas Pandruvada 		rc = wait_event_interruptible_timeout(
4680b28cb4bSSrinivas Pandruvada 					client_data->ishtp_resume_wait,
4690b28cb4bSSrinivas Pandruvada 					!client_data->suspended,
4700b28cb4bSSrinivas Pandruvada 					5 * HZ);
4710b28cb4bSSrinivas Pandruvada 
4720b28cb4bSSrinivas Pandruvada 		if (rc == 0) {
4730b28cb4bSSrinivas Pandruvada 			hid_ishtp_trace(client_data,  "link not ready\n");
4740b28cb4bSSrinivas Pandruvada 			return -EIO;
4750b28cb4bSSrinivas Pandruvada 		}
4760b28cb4bSSrinivas Pandruvada 		hid_ishtp_trace(client_data,  "link ready\n");
4770b28cb4bSSrinivas Pandruvada 	}
4780b28cb4bSSrinivas Pandruvada 
4790b28cb4bSSrinivas Pandruvada 	return 0;
4800b28cb4bSSrinivas Pandruvada }
4810b28cb4bSSrinivas Pandruvada 
4820b28cb4bSSrinivas Pandruvada /**
4830b28cb4bSSrinivas Pandruvada  * ishtp_enum_enum_devices() - Enumerate hid devices
4840b28cb4bSSrinivas Pandruvada  * @hid_ishtp_cl:	client instance
4850b28cb4bSSrinivas Pandruvada  *
4860b28cb4bSSrinivas Pandruvada  * Helper function to send request to firmware to enumerate HID devices
4870b28cb4bSSrinivas Pandruvada  *
4880b28cb4bSSrinivas Pandruvada  * Return: 0 on success, non zero on error
4890b28cb4bSSrinivas Pandruvada  */
4900b28cb4bSSrinivas Pandruvada static int ishtp_enum_enum_devices(struct ishtp_cl *hid_ishtp_cl)
4910b28cb4bSSrinivas Pandruvada {
4920b28cb4bSSrinivas Pandruvada 	struct hostif_msg msg;
4930b28cb4bSSrinivas Pandruvada 	struct ishtp_cl_data *client_data = hid_ishtp_cl->client_data;
4940b28cb4bSSrinivas Pandruvada 	int retry_count;
4950b28cb4bSSrinivas Pandruvada 	int rv;
4960b28cb4bSSrinivas Pandruvada 
4970b28cb4bSSrinivas Pandruvada 	/* Send HOSTIF_DM_ENUM_DEVICES */
4980b28cb4bSSrinivas Pandruvada 	memset(&msg, 0, sizeof(struct hostif_msg));
4990b28cb4bSSrinivas Pandruvada 	msg.hdr.command = HOSTIF_DM_ENUM_DEVICES;
5000b28cb4bSSrinivas Pandruvada 	rv = ishtp_cl_send(hid_ishtp_cl, (unsigned char *)&msg,
5010b28cb4bSSrinivas Pandruvada 			   sizeof(struct hostif_msg));
5020b28cb4bSSrinivas Pandruvada 	if (rv)
5030b28cb4bSSrinivas Pandruvada 		return rv;
5040b28cb4bSSrinivas Pandruvada 
5050b28cb4bSSrinivas Pandruvada 	retry_count = 0;
5060b28cb4bSSrinivas Pandruvada 	while (!client_data->enum_devices_done &&
5070b28cb4bSSrinivas Pandruvada 	       retry_count < 10) {
5080b28cb4bSSrinivas Pandruvada 		wait_event_interruptible_timeout(client_data->init_wait,
5090b28cb4bSSrinivas Pandruvada 					 client_data->enum_devices_done,
5100b28cb4bSSrinivas Pandruvada 					 3 * HZ);
5110b28cb4bSSrinivas Pandruvada 		++retry_count;
5120b28cb4bSSrinivas Pandruvada 		if (!client_data->enum_devices_done)
5130b28cb4bSSrinivas Pandruvada 			/* Send HOSTIF_DM_ENUM_DEVICES */
5140b28cb4bSSrinivas Pandruvada 			rv = ishtp_cl_send(hid_ishtp_cl,
5150b28cb4bSSrinivas Pandruvada 					   (unsigned char *) &msg,
5160b28cb4bSSrinivas Pandruvada 					   sizeof(struct hostif_msg));
5170b28cb4bSSrinivas Pandruvada 	}
5180b28cb4bSSrinivas Pandruvada 	if (!client_data->enum_devices_done) {
5190b28cb4bSSrinivas Pandruvada 		dev_err(&client_data->cl_device->dev,
5200b28cb4bSSrinivas Pandruvada 			"[hid-ish]: timed out waiting for enum_devices\n");
5210b28cb4bSSrinivas Pandruvada 		return -ETIMEDOUT;
5220b28cb4bSSrinivas Pandruvada 	}
5230b28cb4bSSrinivas Pandruvada 	if (!client_data->hid_devices) {
5240b28cb4bSSrinivas Pandruvada 		dev_err(&client_data->cl_device->dev,
5250b28cb4bSSrinivas Pandruvada 			"[hid-ish]: failed to allocate HID dev structures\n");
5260b28cb4bSSrinivas Pandruvada 		return -ENOMEM;
5270b28cb4bSSrinivas Pandruvada 	}
5280b28cb4bSSrinivas Pandruvada 
5290b28cb4bSSrinivas Pandruvada 	client_data->num_hid_devices = client_data->hid_dev_count;
5300b28cb4bSSrinivas Pandruvada 	dev_info(&hid_ishtp_cl->device->dev,
5310b28cb4bSSrinivas Pandruvada 		"[hid-ish]: enum_devices_done OK, num_hid_devices=%d\n",
5320b28cb4bSSrinivas Pandruvada 		client_data->num_hid_devices);
5330b28cb4bSSrinivas Pandruvada 
5340b28cb4bSSrinivas Pandruvada 	return	0;
5350b28cb4bSSrinivas Pandruvada }
5360b28cb4bSSrinivas Pandruvada 
5370b28cb4bSSrinivas Pandruvada /**
5380b28cb4bSSrinivas Pandruvada  * ishtp_get_hid_descriptor() - Get hid descriptor
5390b28cb4bSSrinivas Pandruvada  * @hid_ishtp_cl:	client instance
5400b28cb4bSSrinivas Pandruvada  * @index:		Index into the hid_descr array
5410b28cb4bSSrinivas Pandruvada  *
5420b28cb4bSSrinivas Pandruvada  * Helper function to send request to firmware get HID descriptor of a device
5430b28cb4bSSrinivas Pandruvada  *
5440b28cb4bSSrinivas Pandruvada  * Return: 0 on success, non zero on error
5450b28cb4bSSrinivas Pandruvada  */
5460b28cb4bSSrinivas Pandruvada static int ishtp_get_hid_descriptor(struct ishtp_cl *hid_ishtp_cl, int index)
5470b28cb4bSSrinivas Pandruvada {
5480b28cb4bSSrinivas Pandruvada 	struct hostif_msg msg;
5490b28cb4bSSrinivas Pandruvada 	struct ishtp_cl_data *client_data = hid_ishtp_cl->client_data;
5500b28cb4bSSrinivas Pandruvada 	int rv;
5510b28cb4bSSrinivas Pandruvada 
5520b28cb4bSSrinivas Pandruvada 	/* Get HID descriptor */
5530b28cb4bSSrinivas Pandruvada 	client_data->hid_descr_done = false;
5540b28cb4bSSrinivas Pandruvada 	memset(&msg, 0, sizeof(struct hostif_msg));
5550b28cb4bSSrinivas Pandruvada 	msg.hdr.command = HOSTIF_GET_HID_DESCRIPTOR;
5560b28cb4bSSrinivas Pandruvada 	msg.hdr.device_id = client_data->hid_devices[index].dev_id;
5570b28cb4bSSrinivas Pandruvada 	rv = ishtp_cl_send(hid_ishtp_cl, (unsigned char *) &msg,
5580b28cb4bSSrinivas Pandruvada 			   sizeof(struct hostif_msg));
5590b28cb4bSSrinivas Pandruvada 	if (rv)
5600b28cb4bSSrinivas Pandruvada 		return rv;
5610b28cb4bSSrinivas Pandruvada 
5620b28cb4bSSrinivas Pandruvada 	if (!client_data->hid_descr_done) {
5630b28cb4bSSrinivas Pandruvada 		wait_event_interruptible_timeout(client_data->init_wait,
5640b28cb4bSSrinivas Pandruvada 						 client_data->hid_descr_done,
5650b28cb4bSSrinivas Pandruvada 						 3 * HZ);
5660b28cb4bSSrinivas Pandruvada 		if (!client_data->hid_descr_done) {
5670b28cb4bSSrinivas Pandruvada 			dev_err(&client_data->cl_device->dev,
5680b28cb4bSSrinivas Pandruvada 				"[hid-ish]: timed out for hid_descr_done\n");
5690b28cb4bSSrinivas Pandruvada 			return -EIO;
5700b28cb4bSSrinivas Pandruvada 		}
5710b28cb4bSSrinivas Pandruvada 
5720b28cb4bSSrinivas Pandruvada 		if (!client_data->hid_descr[index]) {
5730b28cb4bSSrinivas Pandruvada 			dev_err(&client_data->cl_device->dev,
5740b28cb4bSSrinivas Pandruvada 				"[hid-ish]: allocation HID desc fail\n");
5750b28cb4bSSrinivas Pandruvada 			return -ENOMEM;
5760b28cb4bSSrinivas Pandruvada 		}
5770b28cb4bSSrinivas Pandruvada 	}
5780b28cb4bSSrinivas Pandruvada 
5790b28cb4bSSrinivas Pandruvada 	return 0;
5800b28cb4bSSrinivas Pandruvada }
5810b28cb4bSSrinivas Pandruvada 
5820b28cb4bSSrinivas Pandruvada /**
5830b28cb4bSSrinivas Pandruvada  * ishtp_get_report_descriptor() - Get report descriptor
5840b28cb4bSSrinivas Pandruvada  * @hid_ishtp_cl:	client instance
5850b28cb4bSSrinivas Pandruvada  * @index:		Index into the hid_descr array
5860b28cb4bSSrinivas Pandruvada  *
5870b28cb4bSSrinivas Pandruvada  * Helper function to send request to firmware get HID report descriptor of
5880b28cb4bSSrinivas Pandruvada  * a device
5890b28cb4bSSrinivas Pandruvada  *
5900b28cb4bSSrinivas Pandruvada  * Return: 0 on success, non zero on error
5910b28cb4bSSrinivas Pandruvada  */
5920b28cb4bSSrinivas Pandruvada static int ishtp_get_report_descriptor(struct ishtp_cl *hid_ishtp_cl,
5930b28cb4bSSrinivas Pandruvada 				       int index)
5940b28cb4bSSrinivas Pandruvada {
5950b28cb4bSSrinivas Pandruvada 	struct hostif_msg msg;
5960b28cb4bSSrinivas Pandruvada 	struct ishtp_cl_data *client_data = hid_ishtp_cl->client_data;
5970b28cb4bSSrinivas Pandruvada 	int rv;
5980b28cb4bSSrinivas Pandruvada 
5990b28cb4bSSrinivas Pandruvada 	/* Get report descriptor */
6000b28cb4bSSrinivas Pandruvada 	client_data->report_descr_done = false;
6010b28cb4bSSrinivas Pandruvada 	memset(&msg, 0, sizeof(struct hostif_msg));
6020b28cb4bSSrinivas Pandruvada 	msg.hdr.command = HOSTIF_GET_REPORT_DESCRIPTOR;
6030b28cb4bSSrinivas Pandruvada 	msg.hdr.device_id = client_data->hid_devices[index].dev_id;
6040b28cb4bSSrinivas Pandruvada 	rv = ishtp_cl_send(hid_ishtp_cl, (unsigned char *) &msg,
6050b28cb4bSSrinivas Pandruvada 			   sizeof(struct hostif_msg));
6060b28cb4bSSrinivas Pandruvada 	if (rv)
6070b28cb4bSSrinivas Pandruvada 		return rv;
6080b28cb4bSSrinivas Pandruvada 
6090b28cb4bSSrinivas Pandruvada 	if (!client_data->report_descr_done)
6100b28cb4bSSrinivas Pandruvada 		wait_event_interruptible_timeout(client_data->init_wait,
6110b28cb4bSSrinivas Pandruvada 					 client_data->report_descr_done,
6120b28cb4bSSrinivas Pandruvada 					 3 * HZ);
6130b28cb4bSSrinivas Pandruvada 	if (!client_data->report_descr_done) {
6140b28cb4bSSrinivas Pandruvada 		dev_err(&client_data->cl_device->dev,
6150b28cb4bSSrinivas Pandruvada 				"[hid-ish]: timed out for report descr\n");
6160b28cb4bSSrinivas Pandruvada 		return -EIO;
6170b28cb4bSSrinivas Pandruvada 	}
6180b28cb4bSSrinivas Pandruvada 	if (!client_data->report_descr[index]) {
6190b28cb4bSSrinivas Pandruvada 		dev_err(&client_data->cl_device->dev,
6200b28cb4bSSrinivas Pandruvada 			"[hid-ish]: failed to alloc report descr\n");
6210b28cb4bSSrinivas Pandruvada 		return -ENOMEM;
6220b28cb4bSSrinivas Pandruvada 	}
6230b28cb4bSSrinivas Pandruvada 
6240b28cb4bSSrinivas Pandruvada 	return 0;
6250b28cb4bSSrinivas Pandruvada }
6260b28cb4bSSrinivas Pandruvada 
6270b28cb4bSSrinivas Pandruvada /**
6280b28cb4bSSrinivas Pandruvada  * hid_ishtp_cl_init() - Init function for ISHTP client
6290b28cb4bSSrinivas Pandruvada  * @hid_ishtp_cl:	ISHTP client instance
6300b28cb4bSSrinivas Pandruvada  * @reset:		true if called for init after reset
6310b28cb4bSSrinivas Pandruvada  *
6320b28cb4bSSrinivas Pandruvada  * This function complete the initializtion of the client. The summary of
6330b28cb4bSSrinivas Pandruvada  * processing:
6340b28cb4bSSrinivas Pandruvada  * - Send request to enumerate the hid clients
6350b28cb4bSSrinivas Pandruvada  *	Get the HID descriptor for each enumearated device
6360b28cb4bSSrinivas Pandruvada  *	Get report description of each device
6370b28cb4bSSrinivas Pandruvada  *	Register each device wik hid core by calling ishtp_hid_probe
6380b28cb4bSSrinivas Pandruvada  *
6390b28cb4bSSrinivas Pandruvada  * Return: 0 on success, non zero on error
6400b28cb4bSSrinivas Pandruvada  */
6410b28cb4bSSrinivas Pandruvada static int hid_ishtp_cl_init(struct ishtp_cl *hid_ishtp_cl, int reset)
6420b28cb4bSSrinivas Pandruvada {
6430b28cb4bSSrinivas Pandruvada 	struct ishtp_device *dev;
6440b28cb4bSSrinivas Pandruvada 	unsigned long flags;
6450b28cb4bSSrinivas Pandruvada 	struct ishtp_cl_data *client_data = hid_ishtp_cl->client_data;
6460b28cb4bSSrinivas Pandruvada 	int i;
6470b28cb4bSSrinivas Pandruvada 	int rv;
6480b28cb4bSSrinivas Pandruvada 
6490b28cb4bSSrinivas Pandruvada 	dev_dbg(&client_data->cl_device->dev, "%s\n", __func__);
6500b28cb4bSSrinivas Pandruvada 	hid_ishtp_trace(client_data,  "%s reset flag: %d\n", __func__, reset);
6510b28cb4bSSrinivas Pandruvada 
6520b28cb4bSSrinivas Pandruvada 	rv = ishtp_cl_link(hid_ishtp_cl, ISHTP_HOST_CLIENT_ID_ANY);
6530b28cb4bSSrinivas Pandruvada 	if (rv) {
6540b28cb4bSSrinivas Pandruvada 		dev_err(&client_data->cl_device->dev,
6550b28cb4bSSrinivas Pandruvada 			"ishtp_cl_link failed\n");
6560b28cb4bSSrinivas Pandruvada 		return	-ENOMEM;
6570b28cb4bSSrinivas Pandruvada 	}
6580b28cb4bSSrinivas Pandruvada 
6590b28cb4bSSrinivas Pandruvada 	client_data->init_done = 0;
6600b28cb4bSSrinivas Pandruvada 
6610b28cb4bSSrinivas Pandruvada 	dev = hid_ishtp_cl->dev;
6620b28cb4bSSrinivas Pandruvada 
6630b28cb4bSSrinivas Pandruvada 	/* Connect to FW client */
6640b28cb4bSSrinivas Pandruvada 	hid_ishtp_cl->rx_ring_size = HID_CL_RX_RING_SIZE;
6650b28cb4bSSrinivas Pandruvada 	hid_ishtp_cl->tx_ring_size = HID_CL_TX_RING_SIZE;
6660b28cb4bSSrinivas Pandruvada 
6670b28cb4bSSrinivas Pandruvada 	spin_lock_irqsave(&dev->fw_clients_lock, flags);
6680b28cb4bSSrinivas Pandruvada 	i = ishtp_fw_cl_by_uuid(dev, &hid_ishtp_guid);
6690b28cb4bSSrinivas Pandruvada 	if (i < 0) {
6700b28cb4bSSrinivas Pandruvada 		spin_unlock_irqrestore(&dev->fw_clients_lock, flags);
6710b28cb4bSSrinivas Pandruvada 		dev_err(&client_data->cl_device->dev,
6720b28cb4bSSrinivas Pandruvada 			"ish client uuid not found\n");
6730b28cb4bSSrinivas Pandruvada 		return i;
6740b28cb4bSSrinivas Pandruvada 	}
6750b28cb4bSSrinivas Pandruvada 	hid_ishtp_cl->fw_client_id = dev->fw_clients[i].client_id;
6760b28cb4bSSrinivas Pandruvada 	spin_unlock_irqrestore(&dev->fw_clients_lock, flags);
6770b28cb4bSSrinivas Pandruvada 	hid_ishtp_cl->state = ISHTP_CL_CONNECTING;
6780b28cb4bSSrinivas Pandruvada 
6790b28cb4bSSrinivas Pandruvada 	rv = ishtp_cl_connect(hid_ishtp_cl);
6800b28cb4bSSrinivas Pandruvada 	if (rv) {
6810b28cb4bSSrinivas Pandruvada 		dev_err(&client_data->cl_device->dev,
6820b28cb4bSSrinivas Pandruvada 			"client connect fail\n");
6830b28cb4bSSrinivas Pandruvada 		goto err_cl_unlink;
6840b28cb4bSSrinivas Pandruvada 	}
6850b28cb4bSSrinivas Pandruvada 
6860b28cb4bSSrinivas Pandruvada 	hid_ishtp_trace(client_data,  "%s client connected\n", __func__);
6870b28cb4bSSrinivas Pandruvada 
6880b28cb4bSSrinivas Pandruvada 	/* Register read callback */
6890b28cb4bSSrinivas Pandruvada 	ishtp_register_event_cb(hid_ishtp_cl->device, ish_cl_event_cb);
6900b28cb4bSSrinivas Pandruvada 
6910b28cb4bSSrinivas Pandruvada 	rv = ishtp_enum_enum_devices(hid_ishtp_cl);
6920b28cb4bSSrinivas Pandruvada 	if (rv)
6930b28cb4bSSrinivas Pandruvada 		goto err_cl_disconnect;
6940b28cb4bSSrinivas Pandruvada 
6950b28cb4bSSrinivas Pandruvada 	hid_ishtp_trace(client_data,  "%s enumerated device count %d\n",
6960b28cb4bSSrinivas Pandruvada 			__func__, client_data->num_hid_devices);
6970b28cb4bSSrinivas Pandruvada 
6980b28cb4bSSrinivas Pandruvada 	for (i = 0; i < client_data->num_hid_devices; ++i) {
6990b28cb4bSSrinivas Pandruvada 		client_data->cur_hid_dev = i;
7000b28cb4bSSrinivas Pandruvada 
7010b28cb4bSSrinivas Pandruvada 		rv = ishtp_get_hid_descriptor(hid_ishtp_cl, i);
7020b28cb4bSSrinivas Pandruvada 		if (rv)
7030b28cb4bSSrinivas Pandruvada 			goto err_cl_disconnect;
7040b28cb4bSSrinivas Pandruvada 
7050b28cb4bSSrinivas Pandruvada 		rv = ishtp_get_report_descriptor(hid_ishtp_cl, i);
7060b28cb4bSSrinivas Pandruvada 		if (rv)
7070b28cb4bSSrinivas Pandruvada 			goto err_cl_disconnect;
7080b28cb4bSSrinivas Pandruvada 
7090b28cb4bSSrinivas Pandruvada 		if (!reset) {
7100b28cb4bSSrinivas Pandruvada 			rv = ishtp_hid_probe(i, client_data);
7110b28cb4bSSrinivas Pandruvada 			if (rv) {
7120b28cb4bSSrinivas Pandruvada 				dev_err(&client_data->cl_device->dev,
7130b28cb4bSSrinivas Pandruvada 				"[hid-ish]: HID probe for #%u failed: %d\n",
7140b28cb4bSSrinivas Pandruvada 				i, rv);
7150b28cb4bSSrinivas Pandruvada 				goto err_cl_disconnect;
7160b28cb4bSSrinivas Pandruvada 			}
7170b28cb4bSSrinivas Pandruvada 		}
7180b28cb4bSSrinivas Pandruvada 	} /* for() on all hid devices */
7190b28cb4bSSrinivas Pandruvada 
7200b28cb4bSSrinivas Pandruvada 	client_data->init_done = 1;
7210b28cb4bSSrinivas Pandruvada 	client_data->suspended = false;
7220b28cb4bSSrinivas Pandruvada 	wake_up_interruptible(&client_data->ishtp_resume_wait);
7230b28cb4bSSrinivas Pandruvada 	hid_ishtp_trace(client_data,  "%s successful init\n", __func__);
7240b28cb4bSSrinivas Pandruvada 	return 0;
7250b28cb4bSSrinivas Pandruvada 
7260b28cb4bSSrinivas Pandruvada err_cl_disconnect:
7270b28cb4bSSrinivas Pandruvada 	hid_ishtp_cl->state = ISHTP_CL_DISCONNECTING;
7280b28cb4bSSrinivas Pandruvada 	ishtp_cl_disconnect(hid_ishtp_cl);
7290b28cb4bSSrinivas Pandruvada err_cl_unlink:
7300b28cb4bSSrinivas Pandruvada 	ishtp_cl_unlink(hid_ishtp_cl);
7310b28cb4bSSrinivas Pandruvada 	return rv;
7320b28cb4bSSrinivas Pandruvada }
7330b28cb4bSSrinivas Pandruvada 
7340b28cb4bSSrinivas Pandruvada /**
7350b28cb4bSSrinivas Pandruvada  * hid_ishtp_cl_deinit() - Deinit function for ISHTP client
7360b28cb4bSSrinivas Pandruvada  * @hid_ishtp_cl:	ISHTP client instance
7370b28cb4bSSrinivas Pandruvada  *
7380b28cb4bSSrinivas Pandruvada  * Unlink and free hid client
7390b28cb4bSSrinivas Pandruvada  */
7400b28cb4bSSrinivas Pandruvada static void hid_ishtp_cl_deinit(struct ishtp_cl *hid_ishtp_cl)
7410b28cb4bSSrinivas Pandruvada {
7420b28cb4bSSrinivas Pandruvada 	ishtp_cl_unlink(hid_ishtp_cl);
7430b28cb4bSSrinivas Pandruvada 	ishtp_cl_flush_queues(hid_ishtp_cl);
7440b28cb4bSSrinivas Pandruvada 
7450b28cb4bSSrinivas Pandruvada 	/* disband and free all Tx and Rx client-level rings */
7460b28cb4bSSrinivas Pandruvada 	ishtp_cl_free(hid_ishtp_cl);
7470b28cb4bSSrinivas Pandruvada }
7480b28cb4bSSrinivas Pandruvada 
7490b28cb4bSSrinivas Pandruvada static void hid_ishtp_cl_reset_handler(struct work_struct *work)
7500b28cb4bSSrinivas Pandruvada {
7510b28cb4bSSrinivas Pandruvada 	struct ishtp_cl_data *client_data;
7520b28cb4bSSrinivas Pandruvada 	struct ishtp_cl *hid_ishtp_cl;
7530b28cb4bSSrinivas Pandruvada 	struct ishtp_cl_device *cl_device;
7540b28cb4bSSrinivas Pandruvada 	int retry;
7550b28cb4bSSrinivas Pandruvada 	int rv;
7560b28cb4bSSrinivas Pandruvada 
7570b28cb4bSSrinivas Pandruvada 	client_data = container_of(work, struct ishtp_cl_data, work);
7580b28cb4bSSrinivas Pandruvada 
7590b28cb4bSSrinivas Pandruvada 	hid_ishtp_cl = client_data->hid_ishtp_cl;
7600b28cb4bSSrinivas Pandruvada 	cl_device = client_data->cl_device;
7610b28cb4bSSrinivas Pandruvada 
7620b28cb4bSSrinivas Pandruvada 	hid_ishtp_trace(client_data, "%s hid_ishtp_cl %p\n", __func__,
7630b28cb4bSSrinivas Pandruvada 			hid_ishtp_cl);
7640b28cb4bSSrinivas Pandruvada 	dev_dbg(&cl_device->dev, "%s\n", __func__);
7650b28cb4bSSrinivas Pandruvada 
7660b28cb4bSSrinivas Pandruvada 	hid_ishtp_cl_deinit(hid_ishtp_cl);
7670b28cb4bSSrinivas Pandruvada 
7680b28cb4bSSrinivas Pandruvada 	hid_ishtp_cl = ishtp_cl_allocate(cl_device->ishtp_dev);
7690b28cb4bSSrinivas Pandruvada 	if (!hid_ishtp_cl)
7700b28cb4bSSrinivas Pandruvada 		return;
7710b28cb4bSSrinivas Pandruvada 
7720b28cb4bSSrinivas Pandruvada 	cl_device->driver_data = hid_ishtp_cl;
7730b28cb4bSSrinivas Pandruvada 	hid_ishtp_cl->client_data = client_data;
7740b28cb4bSSrinivas Pandruvada 	client_data->hid_ishtp_cl = hid_ishtp_cl;
7750b28cb4bSSrinivas Pandruvada 
7760b28cb4bSSrinivas Pandruvada 	client_data->num_hid_devices = 0;
7770b28cb4bSSrinivas Pandruvada 
7780b28cb4bSSrinivas Pandruvada 	for (retry = 0; retry < 3; ++retry) {
7790b28cb4bSSrinivas Pandruvada 		rv = hid_ishtp_cl_init(hid_ishtp_cl, 1);
7800b28cb4bSSrinivas Pandruvada 		if (!rv)
7810b28cb4bSSrinivas Pandruvada 			break;
7820b28cb4bSSrinivas Pandruvada 		dev_err(&client_data->cl_device->dev, "Retry reset init\n");
7830b28cb4bSSrinivas Pandruvada 	}
7840b28cb4bSSrinivas Pandruvada 	if (rv) {
7850b28cb4bSSrinivas Pandruvada 		dev_err(&client_data->cl_device->dev, "Reset Failed\n");
7860b28cb4bSSrinivas Pandruvada 		hid_ishtp_trace(client_data, "%s Failed hid_ishtp_cl %p\n",
7870b28cb4bSSrinivas Pandruvada 				__func__, hid_ishtp_cl);
7880b28cb4bSSrinivas Pandruvada 	}
7890b28cb4bSSrinivas Pandruvada }
7900b28cb4bSSrinivas Pandruvada 
7910b28cb4bSSrinivas Pandruvada /**
7920b28cb4bSSrinivas Pandruvada  * hid_ishtp_cl_probe() - ISHTP client driver probe
7930b28cb4bSSrinivas Pandruvada  * @cl_device:		ISHTP client device instance
7940b28cb4bSSrinivas Pandruvada  *
7950b28cb4bSSrinivas Pandruvada  * This function gets called on device create on ISHTP bus
7960b28cb4bSSrinivas Pandruvada  *
7970b28cb4bSSrinivas Pandruvada  * Return: 0 on success, non zero on error
7980b28cb4bSSrinivas Pandruvada  */
7990b28cb4bSSrinivas Pandruvada static int hid_ishtp_cl_probe(struct ishtp_cl_device *cl_device)
8000b28cb4bSSrinivas Pandruvada {
8010b28cb4bSSrinivas Pandruvada 	struct ishtp_cl *hid_ishtp_cl;
8020b28cb4bSSrinivas Pandruvada 	struct ishtp_cl_data *client_data;
8030b28cb4bSSrinivas Pandruvada 	int rv;
8040b28cb4bSSrinivas Pandruvada 
8050b28cb4bSSrinivas Pandruvada 	if (!cl_device)
8060b28cb4bSSrinivas Pandruvada 		return	-ENODEV;
8070b28cb4bSSrinivas Pandruvada 
8080b28cb4bSSrinivas Pandruvada 	if (uuid_le_cmp(hid_ishtp_guid,
8090b28cb4bSSrinivas Pandruvada 			cl_device->fw_client->props.protocol_name) != 0)
8100b28cb4bSSrinivas Pandruvada 		return	-ENODEV;
8110b28cb4bSSrinivas Pandruvada 
8120b28cb4bSSrinivas Pandruvada 	client_data = devm_kzalloc(&cl_device->dev, sizeof(*client_data),
8130b28cb4bSSrinivas Pandruvada 				   GFP_KERNEL);
8140b28cb4bSSrinivas Pandruvada 	if (!client_data)
8150b28cb4bSSrinivas Pandruvada 		return -ENOMEM;
8160b28cb4bSSrinivas Pandruvada 
8170b28cb4bSSrinivas Pandruvada 	hid_ishtp_cl = ishtp_cl_allocate(cl_device->ishtp_dev);
8180b28cb4bSSrinivas Pandruvada 	if (!hid_ishtp_cl)
8190b28cb4bSSrinivas Pandruvada 		return -ENOMEM;
8200b28cb4bSSrinivas Pandruvada 
8210b28cb4bSSrinivas Pandruvada 	cl_device->driver_data = hid_ishtp_cl;
8220b28cb4bSSrinivas Pandruvada 	hid_ishtp_cl->client_data = client_data;
8230b28cb4bSSrinivas Pandruvada 	client_data->hid_ishtp_cl = hid_ishtp_cl;
8240b28cb4bSSrinivas Pandruvada 	client_data->cl_device = cl_device;
8250b28cb4bSSrinivas Pandruvada 
8260b28cb4bSSrinivas Pandruvada 	init_waitqueue_head(&client_data->init_wait);
8270b28cb4bSSrinivas Pandruvada 	init_waitqueue_head(&client_data->ishtp_resume_wait);
8280b28cb4bSSrinivas Pandruvada 
8290b28cb4bSSrinivas Pandruvada 	INIT_WORK(&client_data->work, hid_ishtp_cl_reset_handler);
8300b28cb4bSSrinivas Pandruvada 
8310b28cb4bSSrinivas Pandruvada 	rv = hid_ishtp_cl_init(hid_ishtp_cl, 0);
8320b28cb4bSSrinivas Pandruvada 	if (rv) {
8330b28cb4bSSrinivas Pandruvada 		ishtp_cl_free(hid_ishtp_cl);
8340b28cb4bSSrinivas Pandruvada 		return rv;
8350b28cb4bSSrinivas Pandruvada 	}
8360b28cb4bSSrinivas Pandruvada 	ishtp_get_device(cl_device);
8370b28cb4bSSrinivas Pandruvada 
8380b28cb4bSSrinivas Pandruvada 	return 0;
8390b28cb4bSSrinivas Pandruvada }
8400b28cb4bSSrinivas Pandruvada 
8410b28cb4bSSrinivas Pandruvada /**
8420b28cb4bSSrinivas Pandruvada  * hid_ishtp_cl_remove() - ISHTP client driver remove
8430b28cb4bSSrinivas Pandruvada  * @cl_device:		ISHTP client device instance
8440b28cb4bSSrinivas Pandruvada  *
8450b28cb4bSSrinivas Pandruvada  * This function gets called on device remove on ISHTP bus
8460b28cb4bSSrinivas Pandruvada  *
8470b28cb4bSSrinivas Pandruvada  * Return: 0
8480b28cb4bSSrinivas Pandruvada  */
8490b28cb4bSSrinivas Pandruvada static int hid_ishtp_cl_remove(struct ishtp_cl_device *cl_device)
8500b28cb4bSSrinivas Pandruvada {
8510b28cb4bSSrinivas Pandruvada 	struct ishtp_cl *hid_ishtp_cl = cl_device->driver_data;
8520b28cb4bSSrinivas Pandruvada 	struct ishtp_cl_data *client_data = hid_ishtp_cl->client_data;
8530b28cb4bSSrinivas Pandruvada 
8540b28cb4bSSrinivas Pandruvada 	hid_ishtp_trace(client_data, "%s hid_ishtp_cl %p\n", __func__,
8550b28cb4bSSrinivas Pandruvada 			hid_ishtp_cl);
8560b28cb4bSSrinivas Pandruvada 
8570b28cb4bSSrinivas Pandruvada 	dev_dbg(&cl_device->dev, "%s\n", __func__);
8580b28cb4bSSrinivas Pandruvada 	hid_ishtp_cl->state = ISHTP_CL_DISCONNECTING;
8590b28cb4bSSrinivas Pandruvada 	ishtp_cl_disconnect(hid_ishtp_cl);
8600b28cb4bSSrinivas Pandruvada 	ishtp_put_device(cl_device);
8610b28cb4bSSrinivas Pandruvada 	ishtp_hid_remove(client_data);
8620b28cb4bSSrinivas Pandruvada 	hid_ishtp_cl_deinit(hid_ishtp_cl);
8630b28cb4bSSrinivas Pandruvada 
8640b28cb4bSSrinivas Pandruvada 	hid_ishtp_cl = NULL;
8650b28cb4bSSrinivas Pandruvada 
8660b28cb4bSSrinivas Pandruvada 	client_data->num_hid_devices = 0;
8670b28cb4bSSrinivas Pandruvada 
8680b28cb4bSSrinivas Pandruvada 	return 0;
8690b28cb4bSSrinivas Pandruvada }
8700b28cb4bSSrinivas Pandruvada 
8710b28cb4bSSrinivas Pandruvada /**
8720b28cb4bSSrinivas Pandruvada  * hid_ishtp_cl_reset() - ISHTP client driver reset
8730b28cb4bSSrinivas Pandruvada  * @cl_device:		ISHTP client device instance
8740b28cb4bSSrinivas Pandruvada  *
8750b28cb4bSSrinivas Pandruvada  * This function gets called on device reset on ISHTP bus
8760b28cb4bSSrinivas Pandruvada  *
8770b28cb4bSSrinivas Pandruvada  * Return: 0
8780b28cb4bSSrinivas Pandruvada  */
8790b28cb4bSSrinivas Pandruvada static int hid_ishtp_cl_reset(struct ishtp_cl_device *cl_device)
8800b28cb4bSSrinivas Pandruvada {
8810b28cb4bSSrinivas Pandruvada 	struct ishtp_cl *hid_ishtp_cl = cl_device->driver_data;
8820b28cb4bSSrinivas Pandruvada 	struct ishtp_cl_data *client_data = hid_ishtp_cl->client_data;
8830b28cb4bSSrinivas Pandruvada 
8840b28cb4bSSrinivas Pandruvada 	hid_ishtp_trace(client_data, "%s hid_ishtp_cl %p\n", __func__,
8850b28cb4bSSrinivas Pandruvada 			hid_ishtp_cl);
8860b28cb4bSSrinivas Pandruvada 
8870b28cb4bSSrinivas Pandruvada 	schedule_work(&client_data->work);
8880b28cb4bSSrinivas Pandruvada 
8890b28cb4bSSrinivas Pandruvada 	return 0;
8900b28cb4bSSrinivas Pandruvada }
8910b28cb4bSSrinivas Pandruvada 
8920b28cb4bSSrinivas Pandruvada #define to_ishtp_cl_device(d) container_of(d, struct ishtp_cl_device, dev)
8930b28cb4bSSrinivas Pandruvada 
8940b28cb4bSSrinivas Pandruvada /**
8950b28cb4bSSrinivas Pandruvada  * hid_ishtp_cl_suspend() - ISHTP client driver suspend
8960b28cb4bSSrinivas Pandruvada  * @device:	device instance
8970b28cb4bSSrinivas Pandruvada  *
8980b28cb4bSSrinivas Pandruvada  * This function gets called on system suspend
8990b28cb4bSSrinivas Pandruvada  *
9000b28cb4bSSrinivas Pandruvada  * Return: 0
9010b28cb4bSSrinivas Pandruvada  */
9020b28cb4bSSrinivas Pandruvada static int hid_ishtp_cl_suspend(struct device *device)
9030b28cb4bSSrinivas Pandruvada {
9040b28cb4bSSrinivas Pandruvada 	struct ishtp_cl_device *cl_device = to_ishtp_cl_device(device);
9050b28cb4bSSrinivas Pandruvada 	struct ishtp_cl *hid_ishtp_cl = cl_device->driver_data;
9060b28cb4bSSrinivas Pandruvada 	struct ishtp_cl_data *client_data = hid_ishtp_cl->client_data;
9070b28cb4bSSrinivas Pandruvada 
9080b28cb4bSSrinivas Pandruvada 	hid_ishtp_trace(client_data, "%s hid_ishtp_cl %p\n", __func__,
9090b28cb4bSSrinivas Pandruvada 			hid_ishtp_cl);
9100b28cb4bSSrinivas Pandruvada 	client_data->suspended = true;
9110b28cb4bSSrinivas Pandruvada 
9120b28cb4bSSrinivas Pandruvada 	return 0;
9130b28cb4bSSrinivas Pandruvada }
9140b28cb4bSSrinivas Pandruvada 
9150b28cb4bSSrinivas Pandruvada /**
9160b28cb4bSSrinivas Pandruvada  * hid_ishtp_cl_resume() - ISHTP client driver resume
9170b28cb4bSSrinivas Pandruvada  * @device:	device instance
9180b28cb4bSSrinivas Pandruvada  *
9190b28cb4bSSrinivas Pandruvada  * This function gets called on system resume
9200b28cb4bSSrinivas Pandruvada  *
9210b28cb4bSSrinivas Pandruvada  * Return: 0
9220b28cb4bSSrinivas Pandruvada  */
9230b28cb4bSSrinivas Pandruvada static int hid_ishtp_cl_resume(struct device *device)
9240b28cb4bSSrinivas Pandruvada {
9250b28cb4bSSrinivas Pandruvada 	struct ishtp_cl_device *cl_device = to_ishtp_cl_device(device);
9260b28cb4bSSrinivas Pandruvada 	struct ishtp_cl *hid_ishtp_cl = cl_device->driver_data;
9270b28cb4bSSrinivas Pandruvada 	struct ishtp_cl_data *client_data = hid_ishtp_cl->client_data;
9280b28cb4bSSrinivas Pandruvada 
9290b28cb4bSSrinivas Pandruvada 	hid_ishtp_trace(client_data, "%s hid_ishtp_cl %p\n", __func__,
9300b28cb4bSSrinivas Pandruvada 			hid_ishtp_cl);
9310b28cb4bSSrinivas Pandruvada 	client_data->suspended = false;
9320b28cb4bSSrinivas Pandruvada 	return 0;
9330b28cb4bSSrinivas Pandruvada }
9340b28cb4bSSrinivas Pandruvada 
9350b28cb4bSSrinivas Pandruvada static const struct dev_pm_ops hid_ishtp_pm_ops = {
9360b28cb4bSSrinivas Pandruvada 	.suspend = hid_ishtp_cl_suspend,
9370b28cb4bSSrinivas Pandruvada 	.resume = hid_ishtp_cl_resume,
9380b28cb4bSSrinivas Pandruvada };
9390b28cb4bSSrinivas Pandruvada 
9400b28cb4bSSrinivas Pandruvada static struct ishtp_cl_driver	hid_ishtp_cl_driver = {
9410b28cb4bSSrinivas Pandruvada 	.name = "ish-hid",
9420b28cb4bSSrinivas Pandruvada 	.probe = hid_ishtp_cl_probe,
9430b28cb4bSSrinivas Pandruvada 	.remove = hid_ishtp_cl_remove,
9440b28cb4bSSrinivas Pandruvada 	.reset = hid_ishtp_cl_reset,
9450b28cb4bSSrinivas Pandruvada 	.driver.pm = &hid_ishtp_pm_ops,
9460b28cb4bSSrinivas Pandruvada };
9470b28cb4bSSrinivas Pandruvada 
9480b28cb4bSSrinivas Pandruvada static int __init ish_hid_init(void)
9490b28cb4bSSrinivas Pandruvada {
9500b28cb4bSSrinivas Pandruvada 	int	rv;
9510b28cb4bSSrinivas Pandruvada 
9520b28cb4bSSrinivas Pandruvada 	/* Register ISHTP client device driver with ISHTP Bus */
9530b28cb4bSSrinivas Pandruvada 	rv = ishtp_cl_driver_register(&hid_ishtp_cl_driver);
9540b28cb4bSSrinivas Pandruvada 
9550b28cb4bSSrinivas Pandruvada 	return rv;
9560b28cb4bSSrinivas Pandruvada 
9570b28cb4bSSrinivas Pandruvada }
9580b28cb4bSSrinivas Pandruvada 
9590b28cb4bSSrinivas Pandruvada static void __exit ish_hid_exit(void)
9600b28cb4bSSrinivas Pandruvada {
9610b28cb4bSSrinivas Pandruvada 	ishtp_cl_driver_unregister(&hid_ishtp_cl_driver);
9620b28cb4bSSrinivas Pandruvada }
9630b28cb4bSSrinivas Pandruvada 
9640b28cb4bSSrinivas Pandruvada late_initcall(ish_hid_init);
9650b28cb4bSSrinivas Pandruvada module_exit(ish_hid_exit);
9660b28cb4bSSrinivas Pandruvada 
9670b28cb4bSSrinivas Pandruvada MODULE_DESCRIPTION("ISH ISHTP HID client driver");
9680b28cb4bSSrinivas Pandruvada /* Primary author */
9690b28cb4bSSrinivas Pandruvada MODULE_AUTHOR("Daniel Drubin <daniel.drubin@intel.com>");
9700b28cb4bSSrinivas Pandruvada /*
9710b28cb4bSSrinivas Pandruvada  * Several modification for multi instance support
9720b28cb4bSSrinivas Pandruvada  * suspend/resume and clean up
9730b28cb4bSSrinivas Pandruvada  */
9740b28cb4bSSrinivas Pandruvada MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
9750b28cb4bSSrinivas Pandruvada 
9760b28cb4bSSrinivas Pandruvada MODULE_LICENSE("GPL");
9770b28cb4bSSrinivas Pandruvada MODULE_ALIAS("ishtp:*");
978