162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * ISHTP client driver for HID (ISH)
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2014-2016, Intel Corporation.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/module.h>
962306a36Sopenharmony_ci#include <linux/hid.h>
1062306a36Sopenharmony_ci#include <linux/intel-ish-client-if.h>
1162306a36Sopenharmony_ci#include <linux/sched.h>
1262306a36Sopenharmony_ci#include "ishtp-hid.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci/* ISH Transport protocol (ISHTP in short) GUID */
1562306a36Sopenharmony_cistatic const struct ishtp_device_id hid_ishtp_id_table[] = {
1662306a36Sopenharmony_ci	{ .guid = GUID_INIT(0x33AECD58, 0xB679, 0x4E54,
1762306a36Sopenharmony_ci		  0x9B, 0xD9, 0xA0, 0x4D, 0x34, 0xF0, 0xC2, 0x26), },
1862306a36Sopenharmony_ci	{ }
1962306a36Sopenharmony_ci};
2062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(ishtp, hid_ishtp_id_table);
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci/* Rx ring buffer pool size */
2362306a36Sopenharmony_ci#define HID_CL_RX_RING_SIZE	32
2462306a36Sopenharmony_ci#define HID_CL_TX_RING_SIZE	16
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#define cl_data_to_dev(client_data) ishtp_device(client_data->cl_device)
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci/**
2962306a36Sopenharmony_ci * report_bad_packet() - Report bad packets
3062306a36Sopenharmony_ci * @hid_ishtp_cl:	Client instance to get stats
3162306a36Sopenharmony_ci * @recv_buf:		Raw received host interface message
3262306a36Sopenharmony_ci * @cur_pos:		Current position index in payload
3362306a36Sopenharmony_ci * @payload_len:	Length of payload expected
3462306a36Sopenharmony_ci *
3562306a36Sopenharmony_ci * Dumps error in case bad packet is received
3662306a36Sopenharmony_ci */
3762306a36Sopenharmony_cistatic void report_bad_packet(struct ishtp_cl *hid_ishtp_cl, void *recv_buf,
3862306a36Sopenharmony_ci			      size_t cur_pos,  size_t payload_len)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	struct hostif_msg *recv_msg = recv_buf;
4162306a36Sopenharmony_ci	struct ishtp_cl_data *client_data = ishtp_get_client_data(hid_ishtp_cl);
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	dev_err(cl_data_to_dev(client_data), "[hid-ish]: BAD packet %02X\n"
4462306a36Sopenharmony_ci		"total_bad=%u cur_pos=%u\n"
4562306a36Sopenharmony_ci		"[%02X %02X %02X %02X]\n"
4662306a36Sopenharmony_ci		"payload_len=%u\n"
4762306a36Sopenharmony_ci		"multi_packet_cnt=%u\n"
4862306a36Sopenharmony_ci		"is_response=%02X\n",
4962306a36Sopenharmony_ci		recv_msg->hdr.command, client_data->bad_recv_cnt,
5062306a36Sopenharmony_ci		(unsigned int)cur_pos,
5162306a36Sopenharmony_ci		((unsigned char *)recv_msg)[0], ((unsigned char *)recv_msg)[1],
5262306a36Sopenharmony_ci		((unsigned char *)recv_msg)[2], ((unsigned char *)recv_msg)[3],
5362306a36Sopenharmony_ci		(unsigned int)payload_len, client_data->multi_packet_cnt,
5462306a36Sopenharmony_ci		recv_msg->hdr.command & ~CMD_MASK);
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci/**
5862306a36Sopenharmony_ci * process_recv() - Received and parse incoming packet
5962306a36Sopenharmony_ci * @hid_ishtp_cl:	Client instance to get stats
6062306a36Sopenharmony_ci * @recv_buf:		Raw received host interface message
6162306a36Sopenharmony_ci * @data_len:		length of the message
6262306a36Sopenharmony_ci *
6362306a36Sopenharmony_ci * Parse the incoming packet. If it is a response packet then it will update
6462306a36Sopenharmony_ci * per instance flags and wake up the caller waiting to for the response.
6562306a36Sopenharmony_ci */
6662306a36Sopenharmony_cistatic void process_recv(struct ishtp_cl *hid_ishtp_cl, void *recv_buf,
6762306a36Sopenharmony_ci			 size_t data_len)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	struct hostif_msg *recv_msg;
7062306a36Sopenharmony_ci	unsigned char *payload;
7162306a36Sopenharmony_ci	struct device_info *dev_info;
7262306a36Sopenharmony_ci	int i, j;
7362306a36Sopenharmony_ci	size_t	payload_len, total_len, cur_pos, raw_len;
7462306a36Sopenharmony_ci	int report_type;
7562306a36Sopenharmony_ci	struct report_list *reports_list;
7662306a36Sopenharmony_ci	char *reports;
7762306a36Sopenharmony_ci	size_t report_len;
7862306a36Sopenharmony_ci	struct ishtp_cl_data *client_data = ishtp_get_client_data(hid_ishtp_cl);
7962306a36Sopenharmony_ci	int curr_hid_dev = client_data->cur_hid_dev;
8062306a36Sopenharmony_ci	struct ishtp_hid_data *hid_data = NULL;
8162306a36Sopenharmony_ci	struct hid_device *hid = NULL;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	payload = recv_buf + sizeof(struct hostif_msg_hdr);
8462306a36Sopenharmony_ci	total_len = data_len;
8562306a36Sopenharmony_ci	cur_pos = 0;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	do {
8862306a36Sopenharmony_ci		if (cur_pos + sizeof(struct hostif_msg) > total_len) {
8962306a36Sopenharmony_ci			dev_err(cl_data_to_dev(client_data),
9062306a36Sopenharmony_ci				"[hid-ish]: error, received %u which is less than data header %u\n",
9162306a36Sopenharmony_ci				(unsigned int)data_len,
9262306a36Sopenharmony_ci				(unsigned int)sizeof(struct hostif_msg_hdr));
9362306a36Sopenharmony_ci			++client_data->bad_recv_cnt;
9462306a36Sopenharmony_ci			ish_hw_reset(ishtp_get_ishtp_device(hid_ishtp_cl));
9562306a36Sopenharmony_ci			break;
9662306a36Sopenharmony_ci		}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci		recv_msg = (struct hostif_msg *)(recv_buf + cur_pos);
9962306a36Sopenharmony_ci		payload_len = recv_msg->hdr.size;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci		/* Sanity checks */
10262306a36Sopenharmony_ci		if (cur_pos + payload_len + sizeof(struct hostif_msg) >
10362306a36Sopenharmony_ci				total_len) {
10462306a36Sopenharmony_ci			++client_data->bad_recv_cnt;
10562306a36Sopenharmony_ci			report_bad_packet(hid_ishtp_cl, recv_msg, cur_pos,
10662306a36Sopenharmony_ci					  payload_len);
10762306a36Sopenharmony_ci			ish_hw_reset(ishtp_get_ishtp_device(hid_ishtp_cl));
10862306a36Sopenharmony_ci			break;
10962306a36Sopenharmony_ci		}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci		hid_ishtp_trace(client_data,  "%s %d\n",
11262306a36Sopenharmony_ci				__func__, recv_msg->hdr.command & CMD_MASK);
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci		switch (recv_msg->hdr.command & CMD_MASK) {
11562306a36Sopenharmony_ci		case HOSTIF_DM_ENUM_DEVICES:
11662306a36Sopenharmony_ci			if ((!(recv_msg->hdr.command & ~CMD_MASK) ||
11762306a36Sopenharmony_ci					client_data->init_done)) {
11862306a36Sopenharmony_ci				++client_data->bad_recv_cnt;
11962306a36Sopenharmony_ci				report_bad_packet(hid_ishtp_cl, recv_msg,
12062306a36Sopenharmony_ci						  cur_pos,
12162306a36Sopenharmony_ci						  payload_len);
12262306a36Sopenharmony_ci				ish_hw_reset(ishtp_get_ishtp_device(hid_ishtp_cl));
12362306a36Sopenharmony_ci				break;
12462306a36Sopenharmony_ci			}
12562306a36Sopenharmony_ci			client_data->hid_dev_count = (unsigned int)*payload;
12662306a36Sopenharmony_ci			if (!client_data->hid_devices)
12762306a36Sopenharmony_ci				client_data->hid_devices = devm_kcalloc(
12862306a36Sopenharmony_ci						cl_data_to_dev(client_data),
12962306a36Sopenharmony_ci						client_data->hid_dev_count,
13062306a36Sopenharmony_ci						sizeof(struct device_info),
13162306a36Sopenharmony_ci						GFP_KERNEL);
13262306a36Sopenharmony_ci			if (!client_data->hid_devices) {
13362306a36Sopenharmony_ci				dev_err(cl_data_to_dev(client_data),
13462306a36Sopenharmony_ci				"Mem alloc failed for hid device info\n");
13562306a36Sopenharmony_ci				wake_up_interruptible(&client_data->init_wait);
13662306a36Sopenharmony_ci				break;
13762306a36Sopenharmony_ci			}
13862306a36Sopenharmony_ci			for (i = 0; i < client_data->hid_dev_count; ++i) {
13962306a36Sopenharmony_ci				if (1 + sizeof(struct device_info) * i >=
14062306a36Sopenharmony_ci						payload_len) {
14162306a36Sopenharmony_ci					dev_err(cl_data_to_dev(client_data),
14262306a36Sopenharmony_ci						"[hid-ish]: [ENUM_DEVICES]: content size %zu is bigger than payload_len %zu\n",
14362306a36Sopenharmony_ci						1 + sizeof(struct device_info)
14462306a36Sopenharmony_ci						* i, payload_len);
14562306a36Sopenharmony_ci				}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci				if (1 + sizeof(struct device_info) * i >=
14862306a36Sopenharmony_ci						data_len)
14962306a36Sopenharmony_ci					break;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci				dev_info = (struct device_info *)(payload + 1 +
15262306a36Sopenharmony_ci					sizeof(struct device_info) * i);
15362306a36Sopenharmony_ci				if (client_data->hid_devices)
15462306a36Sopenharmony_ci					memcpy(client_data->hid_devices + i,
15562306a36Sopenharmony_ci					       dev_info,
15662306a36Sopenharmony_ci					       sizeof(struct device_info));
15762306a36Sopenharmony_ci			}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci			client_data->enum_devices_done = true;
16062306a36Sopenharmony_ci			wake_up_interruptible(&client_data->init_wait);
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci			break;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci		case HOSTIF_GET_HID_DESCRIPTOR:
16562306a36Sopenharmony_ci			if ((!(recv_msg->hdr.command & ~CMD_MASK) ||
16662306a36Sopenharmony_ci					client_data->init_done)) {
16762306a36Sopenharmony_ci				++client_data->bad_recv_cnt;
16862306a36Sopenharmony_ci				report_bad_packet(hid_ishtp_cl, recv_msg,
16962306a36Sopenharmony_ci						  cur_pos,
17062306a36Sopenharmony_ci						  payload_len);
17162306a36Sopenharmony_ci				ish_hw_reset(ishtp_get_ishtp_device(hid_ishtp_cl));
17262306a36Sopenharmony_ci				break;
17362306a36Sopenharmony_ci			}
17462306a36Sopenharmony_ci			if (!client_data->hid_descr[curr_hid_dev])
17562306a36Sopenharmony_ci				client_data->hid_descr[curr_hid_dev] =
17662306a36Sopenharmony_ci				devm_kmalloc(cl_data_to_dev(client_data),
17762306a36Sopenharmony_ci					     payload_len, GFP_KERNEL);
17862306a36Sopenharmony_ci			if (client_data->hid_descr[curr_hid_dev]) {
17962306a36Sopenharmony_ci				memcpy(client_data->hid_descr[curr_hid_dev],
18062306a36Sopenharmony_ci				       payload, payload_len);
18162306a36Sopenharmony_ci				client_data->hid_descr_size[curr_hid_dev] =
18262306a36Sopenharmony_ci					payload_len;
18362306a36Sopenharmony_ci				client_data->hid_descr_done = true;
18462306a36Sopenharmony_ci			}
18562306a36Sopenharmony_ci			wake_up_interruptible(&client_data->init_wait);
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci			break;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci		case HOSTIF_GET_REPORT_DESCRIPTOR:
19062306a36Sopenharmony_ci			if ((!(recv_msg->hdr.command & ~CMD_MASK) ||
19162306a36Sopenharmony_ci					client_data->init_done)) {
19262306a36Sopenharmony_ci				++client_data->bad_recv_cnt;
19362306a36Sopenharmony_ci				report_bad_packet(hid_ishtp_cl, recv_msg,
19462306a36Sopenharmony_ci						  cur_pos,
19562306a36Sopenharmony_ci						  payload_len);
19662306a36Sopenharmony_ci				ish_hw_reset(ishtp_get_ishtp_device(hid_ishtp_cl));
19762306a36Sopenharmony_ci				break;
19862306a36Sopenharmony_ci			}
19962306a36Sopenharmony_ci			if (!client_data->report_descr[curr_hid_dev])
20062306a36Sopenharmony_ci				client_data->report_descr[curr_hid_dev] =
20162306a36Sopenharmony_ci				devm_kmalloc(cl_data_to_dev(client_data),
20262306a36Sopenharmony_ci					     payload_len, GFP_KERNEL);
20362306a36Sopenharmony_ci			if (client_data->report_descr[curr_hid_dev])  {
20462306a36Sopenharmony_ci				memcpy(client_data->report_descr[curr_hid_dev],
20562306a36Sopenharmony_ci				       payload,
20662306a36Sopenharmony_ci				       payload_len);
20762306a36Sopenharmony_ci				client_data->report_descr_size[curr_hid_dev] =
20862306a36Sopenharmony_ci					payload_len;
20962306a36Sopenharmony_ci				client_data->report_descr_done = true;
21062306a36Sopenharmony_ci			}
21162306a36Sopenharmony_ci			wake_up_interruptible(&client_data->init_wait);
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci			break;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci		case HOSTIF_GET_FEATURE_REPORT:
21662306a36Sopenharmony_ci			report_type = HID_FEATURE_REPORT;
21762306a36Sopenharmony_ci			goto	do_get_report;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci		case HOSTIF_GET_INPUT_REPORT:
22062306a36Sopenharmony_ci			report_type = HID_INPUT_REPORT;
22162306a36Sopenharmony_cido_get_report:
22262306a36Sopenharmony_ci			/* Get index of device that matches this id */
22362306a36Sopenharmony_ci			for (i = 0; i < client_data->num_hid_devices; ++i) {
22462306a36Sopenharmony_ci				if (recv_msg->hdr.device_id ==
22562306a36Sopenharmony_ci					  client_data->hid_devices[i].dev_id) {
22662306a36Sopenharmony_ci					hid = client_data->hid_sensor_hubs[i];
22762306a36Sopenharmony_ci					if (!hid)
22862306a36Sopenharmony_ci						break;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci					hid_data = hid->driver_data;
23162306a36Sopenharmony_ci					if (hid_data->raw_get_req) {
23262306a36Sopenharmony_ci						raw_len =
23362306a36Sopenharmony_ci						  (hid_data->raw_buf_size <
23462306a36Sopenharmony_ci								payload_len) ?
23562306a36Sopenharmony_ci						  hid_data->raw_buf_size :
23662306a36Sopenharmony_ci						  payload_len;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci						memcpy(hid_data->raw_buf,
23962306a36Sopenharmony_ci						       payload, raw_len);
24062306a36Sopenharmony_ci					} else {
24162306a36Sopenharmony_ci						hid_input_report
24262306a36Sopenharmony_ci							(hid, report_type,
24362306a36Sopenharmony_ci							 payload, payload_len,
24462306a36Sopenharmony_ci							 0);
24562306a36Sopenharmony_ci					}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci					ishtp_hid_wakeup(hid);
24862306a36Sopenharmony_ci					break;
24962306a36Sopenharmony_ci				}
25062306a36Sopenharmony_ci			}
25162306a36Sopenharmony_ci			break;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci		case HOSTIF_SET_FEATURE_REPORT:
25462306a36Sopenharmony_ci			/* Get index of device that matches this id */
25562306a36Sopenharmony_ci			for (i = 0; i < client_data->num_hid_devices; ++i) {
25662306a36Sopenharmony_ci				if (recv_msg->hdr.device_id ==
25762306a36Sopenharmony_ci					client_data->hid_devices[i].dev_id)
25862306a36Sopenharmony_ci					if (client_data->hid_sensor_hubs[i]) {
25962306a36Sopenharmony_ci						ishtp_hid_wakeup(
26062306a36Sopenharmony_ci						client_data->hid_sensor_hubs[
26162306a36Sopenharmony_ci							i]);
26262306a36Sopenharmony_ci						break;
26362306a36Sopenharmony_ci					}
26462306a36Sopenharmony_ci			}
26562306a36Sopenharmony_ci			break;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci		case HOSTIF_PUBLISH_INPUT_REPORT:
26862306a36Sopenharmony_ci			report_type = HID_INPUT_REPORT;
26962306a36Sopenharmony_ci			for (i = 0; i < client_data->num_hid_devices; ++i)
27062306a36Sopenharmony_ci				if (recv_msg->hdr.device_id ==
27162306a36Sopenharmony_ci					client_data->hid_devices[i].dev_id)
27262306a36Sopenharmony_ci					if (client_data->hid_sensor_hubs[i])
27362306a36Sopenharmony_ci						hid_input_report(
27462306a36Sopenharmony_ci						client_data->hid_sensor_hubs[
27562306a36Sopenharmony_ci									i],
27662306a36Sopenharmony_ci						report_type, payload,
27762306a36Sopenharmony_ci						payload_len, 0);
27862306a36Sopenharmony_ci			break;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci		case HOSTIF_PUBLISH_INPUT_REPORT_LIST:
28162306a36Sopenharmony_ci			report_type = HID_INPUT_REPORT;
28262306a36Sopenharmony_ci			reports_list = (struct report_list *)payload;
28362306a36Sopenharmony_ci			reports = (char *)reports_list->reports;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci			for (j = 0; j < reports_list->num_of_reports; j++) {
28662306a36Sopenharmony_ci				recv_msg = (struct hostif_msg *)(reports +
28762306a36Sopenharmony_ci					sizeof(uint16_t));
28862306a36Sopenharmony_ci				report_len = *(uint16_t *)reports;
28962306a36Sopenharmony_ci				payload = reports + sizeof(uint16_t) +
29062306a36Sopenharmony_ci					sizeof(struct hostif_msg_hdr);
29162306a36Sopenharmony_ci				payload_len = report_len -
29262306a36Sopenharmony_ci					sizeof(struct hostif_msg_hdr);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci				for (i = 0; i < client_data->num_hid_devices;
29562306a36Sopenharmony_ci				     ++i)
29662306a36Sopenharmony_ci					if (recv_msg->hdr.device_id ==
29762306a36Sopenharmony_ci					client_data->hid_devices[i].dev_id &&
29862306a36Sopenharmony_ci					client_data->hid_sensor_hubs[i]) {
29962306a36Sopenharmony_ci						hid_input_report(
30062306a36Sopenharmony_ci						client_data->hid_sensor_hubs[
30162306a36Sopenharmony_ci									i],
30262306a36Sopenharmony_ci						report_type,
30362306a36Sopenharmony_ci						payload, payload_len,
30462306a36Sopenharmony_ci						0);
30562306a36Sopenharmony_ci					}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci				reports += sizeof(uint16_t) + report_len;
30862306a36Sopenharmony_ci			}
30962306a36Sopenharmony_ci			break;
31062306a36Sopenharmony_ci		default:
31162306a36Sopenharmony_ci			++client_data->bad_recv_cnt;
31262306a36Sopenharmony_ci			report_bad_packet(hid_ishtp_cl, recv_msg, cur_pos,
31362306a36Sopenharmony_ci					  payload_len);
31462306a36Sopenharmony_ci			ish_hw_reset(ishtp_get_ishtp_device(hid_ishtp_cl));
31562306a36Sopenharmony_ci			break;
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci		}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci		if (!cur_pos && cur_pos + payload_len +
32062306a36Sopenharmony_ci				sizeof(struct hostif_msg) < total_len)
32162306a36Sopenharmony_ci			++client_data->multi_packet_cnt;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci		cur_pos += payload_len + sizeof(struct hostif_msg);
32462306a36Sopenharmony_ci		payload += payload_len + sizeof(struct hostif_msg);
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	} while (cur_pos < total_len);
32762306a36Sopenharmony_ci}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci/**
33062306a36Sopenharmony_ci * ish_cl_event_cb() - bus driver callback for incoming message/packet
33162306a36Sopenharmony_ci * @device:	Pointer to the ishtp client device for which this message
33262306a36Sopenharmony_ci *		is targeted
33362306a36Sopenharmony_ci *
33462306a36Sopenharmony_ci * Remove the packet from the list and process the message by calling
33562306a36Sopenharmony_ci * process_recv
33662306a36Sopenharmony_ci */
33762306a36Sopenharmony_cistatic void ish_cl_event_cb(struct ishtp_cl_device *device)
33862306a36Sopenharmony_ci{
33962306a36Sopenharmony_ci	struct ishtp_cl	*hid_ishtp_cl = ishtp_get_drvdata(device);
34062306a36Sopenharmony_ci	struct ishtp_cl_rb *rb_in_proc;
34162306a36Sopenharmony_ci	size_t r_length;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	if (!hid_ishtp_cl)
34462306a36Sopenharmony_ci		return;
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	while ((rb_in_proc = ishtp_cl_rx_get_rb(hid_ishtp_cl)) != NULL) {
34762306a36Sopenharmony_ci		if (!rb_in_proc->buffer.data)
34862306a36Sopenharmony_ci			return;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci		r_length = rb_in_proc->buf_idx;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci		/* decide what to do with received data */
35362306a36Sopenharmony_ci		process_recv(hid_ishtp_cl, rb_in_proc->buffer.data, r_length);
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci		ishtp_cl_io_rb_recycle(rb_in_proc);
35662306a36Sopenharmony_ci	}
35762306a36Sopenharmony_ci}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci/**
36062306a36Sopenharmony_ci * hid_ishtp_set_feature() - send request to ISH FW to set a feature request
36162306a36Sopenharmony_ci * @hid:	hid device instance for this request
36262306a36Sopenharmony_ci * @buf:	feature buffer
36362306a36Sopenharmony_ci * @len:	Length of feature buffer
36462306a36Sopenharmony_ci * @report_id:	Report id for the feature set request
36562306a36Sopenharmony_ci *
36662306a36Sopenharmony_ci * This is called from hid core .request() callback. This function doesn't wait
36762306a36Sopenharmony_ci * for response.
36862306a36Sopenharmony_ci */
36962306a36Sopenharmony_civoid hid_ishtp_set_feature(struct hid_device *hid, char *buf, unsigned int len,
37062306a36Sopenharmony_ci			   int report_id)
37162306a36Sopenharmony_ci{
37262306a36Sopenharmony_ci	struct ishtp_hid_data *hid_data =  hid->driver_data;
37362306a36Sopenharmony_ci	struct ishtp_cl_data *client_data = hid_data->client_data;
37462306a36Sopenharmony_ci	struct hostif_msg *msg = (struct hostif_msg *)buf;
37562306a36Sopenharmony_ci	int	rv;
37662306a36Sopenharmony_ci	int	i;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	hid_ishtp_trace(client_data,  "%s hid %p\n", __func__, hid);
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	rv = ishtp_hid_link_ready_wait(client_data);
38162306a36Sopenharmony_ci	if (rv) {
38262306a36Sopenharmony_ci		hid_ishtp_trace(client_data,  "%s hid %p link not ready\n",
38362306a36Sopenharmony_ci				__func__, hid);
38462306a36Sopenharmony_ci		return;
38562306a36Sopenharmony_ci	}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	memset(msg, 0, sizeof(struct hostif_msg));
38862306a36Sopenharmony_ci	msg->hdr.command = HOSTIF_SET_FEATURE_REPORT;
38962306a36Sopenharmony_ci	for (i = 0; i < client_data->num_hid_devices; ++i) {
39062306a36Sopenharmony_ci		if (hid == client_data->hid_sensor_hubs[i]) {
39162306a36Sopenharmony_ci			msg->hdr.device_id =
39262306a36Sopenharmony_ci				client_data->hid_devices[i].dev_id;
39362306a36Sopenharmony_ci			break;
39462306a36Sopenharmony_ci		}
39562306a36Sopenharmony_ci	}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	if (i == client_data->num_hid_devices)
39862306a36Sopenharmony_ci		return;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	rv = ishtp_cl_send(client_data->hid_ishtp_cl, buf, len);
40162306a36Sopenharmony_ci	if (rv)
40262306a36Sopenharmony_ci		hid_ishtp_trace(client_data,  "%s hid %p send failed\n",
40362306a36Sopenharmony_ci				__func__, hid);
40462306a36Sopenharmony_ci}
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci/**
40762306a36Sopenharmony_ci * hid_ishtp_get_report() - request to get feature/input report
40862306a36Sopenharmony_ci * @hid:	hid device instance for this request
40962306a36Sopenharmony_ci * @report_id:	Report id for the get request
41062306a36Sopenharmony_ci * @report_type:	Report type for the this request
41162306a36Sopenharmony_ci *
41262306a36Sopenharmony_ci * This is called from hid core .request() callback. This function will send
41362306a36Sopenharmony_ci * request to FW and return without waiting for response.
41462306a36Sopenharmony_ci */
41562306a36Sopenharmony_civoid hid_ishtp_get_report(struct hid_device *hid, int report_id,
41662306a36Sopenharmony_ci			  int report_type)
41762306a36Sopenharmony_ci{
41862306a36Sopenharmony_ci	struct ishtp_hid_data *hid_data =  hid->driver_data;
41962306a36Sopenharmony_ci	struct ishtp_cl_data *client_data = hid_data->client_data;
42062306a36Sopenharmony_ci	struct hostif_msg_to_sensor msg = {};
42162306a36Sopenharmony_ci	int	rv;
42262306a36Sopenharmony_ci	int	i;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	hid_ishtp_trace(client_data,  "%s hid %p\n", __func__, hid);
42562306a36Sopenharmony_ci	rv = ishtp_hid_link_ready_wait(client_data);
42662306a36Sopenharmony_ci	if (rv) {
42762306a36Sopenharmony_ci		hid_ishtp_trace(client_data,  "%s hid %p link not ready\n",
42862306a36Sopenharmony_ci				__func__, hid);
42962306a36Sopenharmony_ci		return;
43062306a36Sopenharmony_ci	}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	msg.hdr.command = (report_type == HID_FEATURE_REPORT) ?
43362306a36Sopenharmony_ci		HOSTIF_GET_FEATURE_REPORT : HOSTIF_GET_INPUT_REPORT;
43462306a36Sopenharmony_ci	for (i = 0; i < client_data->num_hid_devices; ++i) {
43562306a36Sopenharmony_ci		if (hid == client_data->hid_sensor_hubs[i]) {
43662306a36Sopenharmony_ci			msg.hdr.device_id =
43762306a36Sopenharmony_ci				client_data->hid_devices[i].dev_id;
43862306a36Sopenharmony_ci			break;
43962306a36Sopenharmony_ci		}
44062306a36Sopenharmony_ci	}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	if (i == client_data->num_hid_devices)
44362306a36Sopenharmony_ci		return;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	msg.report_id = report_id;
44662306a36Sopenharmony_ci	rv = ishtp_cl_send(client_data->hid_ishtp_cl, (uint8_t *)&msg,
44762306a36Sopenharmony_ci			    sizeof(msg));
44862306a36Sopenharmony_ci	if (rv)
44962306a36Sopenharmony_ci		hid_ishtp_trace(client_data,  "%s hid %p send failed\n",
45062306a36Sopenharmony_ci				__func__, hid);
45162306a36Sopenharmony_ci}
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci/**
45462306a36Sopenharmony_ci * ishtp_hid_link_ready_wait() - Wait for link ready
45562306a36Sopenharmony_ci * @client_data:	client data instance
45662306a36Sopenharmony_ci *
45762306a36Sopenharmony_ci * If the transport link started suspend process, then wait, till either
45862306a36Sopenharmony_ci * resumed or timeout
45962306a36Sopenharmony_ci *
46062306a36Sopenharmony_ci * Return: 0 on success, non zero on error
46162306a36Sopenharmony_ci */
46262306a36Sopenharmony_ciint ishtp_hid_link_ready_wait(struct ishtp_cl_data *client_data)
46362306a36Sopenharmony_ci{
46462306a36Sopenharmony_ci	int rc;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	if (client_data->suspended) {
46762306a36Sopenharmony_ci		hid_ishtp_trace(client_data,  "wait for link ready\n");
46862306a36Sopenharmony_ci		rc = wait_event_interruptible_timeout(
46962306a36Sopenharmony_ci					client_data->ishtp_resume_wait,
47062306a36Sopenharmony_ci					!client_data->suspended,
47162306a36Sopenharmony_ci					5 * HZ);
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci		if (rc == 0) {
47462306a36Sopenharmony_ci			hid_ishtp_trace(client_data,  "link not ready\n");
47562306a36Sopenharmony_ci			return -EIO;
47662306a36Sopenharmony_ci		}
47762306a36Sopenharmony_ci		hid_ishtp_trace(client_data,  "link ready\n");
47862306a36Sopenharmony_ci	}
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	return 0;
48162306a36Sopenharmony_ci}
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci/**
48462306a36Sopenharmony_ci * ishtp_enum_enum_devices() - Enumerate hid devices
48562306a36Sopenharmony_ci * @hid_ishtp_cl:	client instance
48662306a36Sopenharmony_ci *
48762306a36Sopenharmony_ci * Helper function to send request to firmware to enumerate HID devices
48862306a36Sopenharmony_ci *
48962306a36Sopenharmony_ci * Return: 0 on success, non zero on error
49062306a36Sopenharmony_ci */
49162306a36Sopenharmony_cistatic int ishtp_enum_enum_devices(struct ishtp_cl *hid_ishtp_cl)
49262306a36Sopenharmony_ci{
49362306a36Sopenharmony_ci	struct hostif_msg msg;
49462306a36Sopenharmony_ci	struct ishtp_cl_data *client_data = ishtp_get_client_data(hid_ishtp_cl);
49562306a36Sopenharmony_ci	int retry_count;
49662306a36Sopenharmony_ci	int rv;
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	/* Send HOSTIF_DM_ENUM_DEVICES */
49962306a36Sopenharmony_ci	memset(&msg, 0, sizeof(struct hostif_msg));
50062306a36Sopenharmony_ci	msg.hdr.command = HOSTIF_DM_ENUM_DEVICES;
50162306a36Sopenharmony_ci	rv = ishtp_cl_send(hid_ishtp_cl, (unsigned char *)&msg,
50262306a36Sopenharmony_ci			   sizeof(struct hostif_msg));
50362306a36Sopenharmony_ci	if (rv)
50462306a36Sopenharmony_ci		return rv;
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	retry_count = 0;
50762306a36Sopenharmony_ci	while (!client_data->enum_devices_done &&
50862306a36Sopenharmony_ci	       retry_count < 10) {
50962306a36Sopenharmony_ci		wait_event_interruptible_timeout(client_data->init_wait,
51062306a36Sopenharmony_ci					 client_data->enum_devices_done,
51162306a36Sopenharmony_ci					 3 * HZ);
51262306a36Sopenharmony_ci		++retry_count;
51362306a36Sopenharmony_ci		if (!client_data->enum_devices_done)
51462306a36Sopenharmony_ci			/* Send HOSTIF_DM_ENUM_DEVICES */
51562306a36Sopenharmony_ci			rv = ishtp_cl_send(hid_ishtp_cl,
51662306a36Sopenharmony_ci					   (unsigned char *) &msg,
51762306a36Sopenharmony_ci					   sizeof(struct hostif_msg));
51862306a36Sopenharmony_ci	}
51962306a36Sopenharmony_ci	if (!client_data->enum_devices_done) {
52062306a36Sopenharmony_ci		dev_err(cl_data_to_dev(client_data),
52162306a36Sopenharmony_ci			"[hid-ish]: timed out waiting for enum_devices\n");
52262306a36Sopenharmony_ci		return -ETIMEDOUT;
52362306a36Sopenharmony_ci	}
52462306a36Sopenharmony_ci	if (!client_data->hid_devices) {
52562306a36Sopenharmony_ci		dev_err(cl_data_to_dev(client_data),
52662306a36Sopenharmony_ci			"[hid-ish]: failed to allocate HID dev structures\n");
52762306a36Sopenharmony_ci		return -ENOMEM;
52862306a36Sopenharmony_ci	}
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	client_data->num_hid_devices = client_data->hid_dev_count;
53162306a36Sopenharmony_ci	dev_info(ishtp_device(client_data->cl_device),
53262306a36Sopenharmony_ci		"[hid-ish]: enum_devices_done OK, num_hid_devices=%d\n",
53362306a36Sopenharmony_ci		client_data->num_hid_devices);
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	return	0;
53662306a36Sopenharmony_ci}
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci/**
53962306a36Sopenharmony_ci * ishtp_get_hid_descriptor() - Get hid descriptor
54062306a36Sopenharmony_ci * @hid_ishtp_cl:	client instance
54162306a36Sopenharmony_ci * @index:		Index into the hid_descr array
54262306a36Sopenharmony_ci *
54362306a36Sopenharmony_ci * Helper function to send request to firmware get HID descriptor of a device
54462306a36Sopenharmony_ci *
54562306a36Sopenharmony_ci * Return: 0 on success, non zero on error
54662306a36Sopenharmony_ci */
54762306a36Sopenharmony_cistatic int ishtp_get_hid_descriptor(struct ishtp_cl *hid_ishtp_cl, int index)
54862306a36Sopenharmony_ci{
54962306a36Sopenharmony_ci	struct hostif_msg msg;
55062306a36Sopenharmony_ci	struct ishtp_cl_data *client_data = ishtp_get_client_data(hid_ishtp_cl);
55162306a36Sopenharmony_ci	int rv;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	/* Get HID descriptor */
55462306a36Sopenharmony_ci	client_data->hid_descr_done = false;
55562306a36Sopenharmony_ci	memset(&msg, 0, sizeof(struct hostif_msg));
55662306a36Sopenharmony_ci	msg.hdr.command = HOSTIF_GET_HID_DESCRIPTOR;
55762306a36Sopenharmony_ci	msg.hdr.device_id = client_data->hid_devices[index].dev_id;
55862306a36Sopenharmony_ci	rv = ishtp_cl_send(hid_ishtp_cl, (unsigned char *) &msg,
55962306a36Sopenharmony_ci			   sizeof(struct hostif_msg));
56062306a36Sopenharmony_ci	if (rv)
56162306a36Sopenharmony_ci		return rv;
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	if (!client_data->hid_descr_done) {
56462306a36Sopenharmony_ci		wait_event_interruptible_timeout(client_data->init_wait,
56562306a36Sopenharmony_ci						 client_data->hid_descr_done,
56662306a36Sopenharmony_ci						 3 * HZ);
56762306a36Sopenharmony_ci		if (!client_data->hid_descr_done) {
56862306a36Sopenharmony_ci			dev_err(cl_data_to_dev(client_data),
56962306a36Sopenharmony_ci				"[hid-ish]: timed out for hid_descr_done\n");
57062306a36Sopenharmony_ci			return -EIO;
57162306a36Sopenharmony_ci		}
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci		if (!client_data->hid_descr[index]) {
57462306a36Sopenharmony_ci			dev_err(cl_data_to_dev(client_data),
57562306a36Sopenharmony_ci				"[hid-ish]: allocation HID desc fail\n");
57662306a36Sopenharmony_ci			return -ENOMEM;
57762306a36Sopenharmony_ci		}
57862306a36Sopenharmony_ci	}
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	return 0;
58162306a36Sopenharmony_ci}
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci/**
58462306a36Sopenharmony_ci * ishtp_get_report_descriptor() - Get report descriptor
58562306a36Sopenharmony_ci * @hid_ishtp_cl:	client instance
58662306a36Sopenharmony_ci * @index:		Index into the hid_descr array
58762306a36Sopenharmony_ci *
58862306a36Sopenharmony_ci * Helper function to send request to firmware get HID report descriptor of
58962306a36Sopenharmony_ci * a device
59062306a36Sopenharmony_ci *
59162306a36Sopenharmony_ci * Return: 0 on success, non zero on error
59262306a36Sopenharmony_ci */
59362306a36Sopenharmony_cistatic int ishtp_get_report_descriptor(struct ishtp_cl *hid_ishtp_cl,
59462306a36Sopenharmony_ci				       int index)
59562306a36Sopenharmony_ci{
59662306a36Sopenharmony_ci	struct hostif_msg msg;
59762306a36Sopenharmony_ci	struct ishtp_cl_data *client_data = ishtp_get_client_data(hid_ishtp_cl);
59862306a36Sopenharmony_ci	int rv;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	/* Get report descriptor */
60162306a36Sopenharmony_ci	client_data->report_descr_done = false;
60262306a36Sopenharmony_ci	memset(&msg, 0, sizeof(struct hostif_msg));
60362306a36Sopenharmony_ci	msg.hdr.command = HOSTIF_GET_REPORT_DESCRIPTOR;
60462306a36Sopenharmony_ci	msg.hdr.device_id = client_data->hid_devices[index].dev_id;
60562306a36Sopenharmony_ci	rv = ishtp_cl_send(hid_ishtp_cl, (unsigned char *) &msg,
60662306a36Sopenharmony_ci			   sizeof(struct hostif_msg));
60762306a36Sopenharmony_ci	if (rv)
60862306a36Sopenharmony_ci		return rv;
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	if (!client_data->report_descr_done)
61162306a36Sopenharmony_ci		wait_event_interruptible_timeout(client_data->init_wait,
61262306a36Sopenharmony_ci					 client_data->report_descr_done,
61362306a36Sopenharmony_ci					 3 * HZ);
61462306a36Sopenharmony_ci	if (!client_data->report_descr_done) {
61562306a36Sopenharmony_ci		dev_err(cl_data_to_dev(client_data),
61662306a36Sopenharmony_ci				"[hid-ish]: timed out for report descr\n");
61762306a36Sopenharmony_ci		return -EIO;
61862306a36Sopenharmony_ci	}
61962306a36Sopenharmony_ci	if (!client_data->report_descr[index]) {
62062306a36Sopenharmony_ci		dev_err(cl_data_to_dev(client_data),
62162306a36Sopenharmony_ci			"[hid-ish]: failed to alloc report descr\n");
62262306a36Sopenharmony_ci		return -ENOMEM;
62362306a36Sopenharmony_ci	}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	return 0;
62662306a36Sopenharmony_ci}
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci/**
62962306a36Sopenharmony_ci * hid_ishtp_cl_init() - Init function for ISHTP client
63062306a36Sopenharmony_ci * @hid_ishtp_cl:	ISHTP client instance
63162306a36Sopenharmony_ci * @reset:		true if called for init after reset
63262306a36Sopenharmony_ci *
63362306a36Sopenharmony_ci * This function complete the initializtion of the client. The summary of
63462306a36Sopenharmony_ci * processing:
63562306a36Sopenharmony_ci * - Send request to enumerate the hid clients
63662306a36Sopenharmony_ci *	Get the HID descriptor for each enumearated device
63762306a36Sopenharmony_ci *	Get report description of each device
63862306a36Sopenharmony_ci *	Register each device wik hid core by calling ishtp_hid_probe
63962306a36Sopenharmony_ci *
64062306a36Sopenharmony_ci * Return: 0 on success, non zero on error
64162306a36Sopenharmony_ci */
64262306a36Sopenharmony_cistatic int hid_ishtp_cl_init(struct ishtp_cl *hid_ishtp_cl, int reset)
64362306a36Sopenharmony_ci{
64462306a36Sopenharmony_ci	struct ishtp_device *dev;
64562306a36Sopenharmony_ci	struct ishtp_cl_data *client_data = ishtp_get_client_data(hid_ishtp_cl);
64662306a36Sopenharmony_ci	struct ishtp_fw_client *fw_client;
64762306a36Sopenharmony_ci	int i;
64862306a36Sopenharmony_ci	int rv;
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	dev_dbg(cl_data_to_dev(client_data), "%s\n", __func__);
65162306a36Sopenharmony_ci	hid_ishtp_trace(client_data,  "%s reset flag: %d\n", __func__, reset);
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	rv = ishtp_cl_link(hid_ishtp_cl);
65462306a36Sopenharmony_ci	if (rv) {
65562306a36Sopenharmony_ci		dev_err(cl_data_to_dev(client_data),
65662306a36Sopenharmony_ci			"ishtp_cl_link failed\n");
65762306a36Sopenharmony_ci		return	-ENOMEM;
65862306a36Sopenharmony_ci	}
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	client_data->init_done = 0;
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	dev = ishtp_get_ishtp_device(hid_ishtp_cl);
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	/* Connect to FW client */
66562306a36Sopenharmony_ci	ishtp_set_tx_ring_size(hid_ishtp_cl, HID_CL_TX_RING_SIZE);
66662306a36Sopenharmony_ci	ishtp_set_rx_ring_size(hid_ishtp_cl, HID_CL_RX_RING_SIZE);
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	fw_client = ishtp_fw_cl_get_client(dev, &hid_ishtp_id_table[0].guid);
66962306a36Sopenharmony_ci	if (!fw_client) {
67062306a36Sopenharmony_ci		dev_err(cl_data_to_dev(client_data),
67162306a36Sopenharmony_ci			"ish client uuid not found\n");
67262306a36Sopenharmony_ci		return -ENOENT;
67362306a36Sopenharmony_ci	}
67462306a36Sopenharmony_ci	ishtp_cl_set_fw_client_id(hid_ishtp_cl,
67562306a36Sopenharmony_ci				  ishtp_get_fw_client_id(fw_client));
67662306a36Sopenharmony_ci	ishtp_set_connection_state(hid_ishtp_cl, ISHTP_CL_CONNECTING);
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	rv = ishtp_cl_connect(hid_ishtp_cl);
67962306a36Sopenharmony_ci	if (rv) {
68062306a36Sopenharmony_ci		dev_err(cl_data_to_dev(client_data),
68162306a36Sopenharmony_ci			"client connect fail\n");
68262306a36Sopenharmony_ci		goto err_cl_unlink;
68362306a36Sopenharmony_ci	}
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	hid_ishtp_trace(client_data,  "%s client connected\n", __func__);
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	/* Register read callback */
68862306a36Sopenharmony_ci	ishtp_register_event_cb(client_data->cl_device, ish_cl_event_cb);
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	rv = ishtp_enum_enum_devices(hid_ishtp_cl);
69162306a36Sopenharmony_ci	if (rv)
69262306a36Sopenharmony_ci		goto err_cl_disconnect;
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	hid_ishtp_trace(client_data,  "%s enumerated device count %d\n",
69562306a36Sopenharmony_ci			__func__, client_data->num_hid_devices);
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	for (i = 0; i < client_data->num_hid_devices; ++i) {
69862306a36Sopenharmony_ci		client_data->cur_hid_dev = i;
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci		rv = ishtp_get_hid_descriptor(hid_ishtp_cl, i);
70162306a36Sopenharmony_ci		if (rv)
70262306a36Sopenharmony_ci			goto err_cl_disconnect;
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci		rv = ishtp_get_report_descriptor(hid_ishtp_cl, i);
70562306a36Sopenharmony_ci		if (rv)
70662306a36Sopenharmony_ci			goto err_cl_disconnect;
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci		if (!reset) {
70962306a36Sopenharmony_ci			rv = ishtp_hid_probe(i, client_data);
71062306a36Sopenharmony_ci			if (rv) {
71162306a36Sopenharmony_ci				dev_err(cl_data_to_dev(client_data),
71262306a36Sopenharmony_ci				"[hid-ish]: HID probe for #%u failed: %d\n",
71362306a36Sopenharmony_ci				i, rv);
71462306a36Sopenharmony_ci				goto err_cl_disconnect;
71562306a36Sopenharmony_ci			}
71662306a36Sopenharmony_ci		}
71762306a36Sopenharmony_ci	} /* for() on all hid devices */
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	client_data->init_done = 1;
72062306a36Sopenharmony_ci	client_data->suspended = false;
72162306a36Sopenharmony_ci	wake_up_interruptible(&client_data->ishtp_resume_wait);
72262306a36Sopenharmony_ci	hid_ishtp_trace(client_data,  "%s successful init\n", __func__);
72362306a36Sopenharmony_ci	return 0;
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_cierr_cl_disconnect:
72662306a36Sopenharmony_ci	ishtp_set_connection_state(hid_ishtp_cl, ISHTP_CL_DISCONNECTING);
72762306a36Sopenharmony_ci	ishtp_cl_disconnect(hid_ishtp_cl);
72862306a36Sopenharmony_cierr_cl_unlink:
72962306a36Sopenharmony_ci	ishtp_cl_unlink(hid_ishtp_cl);
73062306a36Sopenharmony_ci	return rv;
73162306a36Sopenharmony_ci}
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci/**
73462306a36Sopenharmony_ci * hid_ishtp_cl_deinit() - Deinit function for ISHTP client
73562306a36Sopenharmony_ci * @hid_ishtp_cl:	ISHTP client instance
73662306a36Sopenharmony_ci *
73762306a36Sopenharmony_ci * Unlink and free hid client
73862306a36Sopenharmony_ci */
73962306a36Sopenharmony_cistatic void hid_ishtp_cl_deinit(struct ishtp_cl *hid_ishtp_cl)
74062306a36Sopenharmony_ci{
74162306a36Sopenharmony_ci	ishtp_cl_unlink(hid_ishtp_cl);
74262306a36Sopenharmony_ci	ishtp_cl_flush_queues(hid_ishtp_cl);
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	/* disband and free all Tx and Rx client-level rings */
74562306a36Sopenharmony_ci	ishtp_cl_free(hid_ishtp_cl);
74662306a36Sopenharmony_ci}
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_cistatic void hid_ishtp_cl_reset_handler(struct work_struct *work)
74962306a36Sopenharmony_ci{
75062306a36Sopenharmony_ci	struct ishtp_cl_data *client_data;
75162306a36Sopenharmony_ci	struct ishtp_cl *hid_ishtp_cl;
75262306a36Sopenharmony_ci	struct ishtp_cl_device *cl_device;
75362306a36Sopenharmony_ci	int retry;
75462306a36Sopenharmony_ci	int rv;
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci	client_data = container_of(work, struct ishtp_cl_data, work);
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	hid_ishtp_cl = client_data->hid_ishtp_cl;
75962306a36Sopenharmony_ci	cl_device = client_data->cl_device;
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	hid_ishtp_trace(client_data, "%s hid_ishtp_cl %p\n", __func__,
76262306a36Sopenharmony_ci			hid_ishtp_cl);
76362306a36Sopenharmony_ci	dev_dbg(ishtp_device(client_data->cl_device), "%s\n", __func__);
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	hid_ishtp_cl_deinit(hid_ishtp_cl);
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	hid_ishtp_cl = ishtp_cl_allocate(cl_device);
76862306a36Sopenharmony_ci	if (!hid_ishtp_cl)
76962306a36Sopenharmony_ci		return;
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	ishtp_set_drvdata(cl_device, hid_ishtp_cl);
77262306a36Sopenharmony_ci	ishtp_set_client_data(hid_ishtp_cl, client_data);
77362306a36Sopenharmony_ci	client_data->hid_ishtp_cl = hid_ishtp_cl;
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	client_data->num_hid_devices = 0;
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	for (retry = 0; retry < 3; ++retry) {
77862306a36Sopenharmony_ci		rv = hid_ishtp_cl_init(hid_ishtp_cl, 1);
77962306a36Sopenharmony_ci		if (!rv)
78062306a36Sopenharmony_ci			break;
78162306a36Sopenharmony_ci		dev_err(cl_data_to_dev(client_data), "Retry reset init\n");
78262306a36Sopenharmony_ci	}
78362306a36Sopenharmony_ci	if (rv) {
78462306a36Sopenharmony_ci		dev_err(cl_data_to_dev(client_data), "Reset Failed\n");
78562306a36Sopenharmony_ci		hid_ishtp_trace(client_data, "%s Failed hid_ishtp_cl %p\n",
78662306a36Sopenharmony_ci				__func__, hid_ishtp_cl);
78762306a36Sopenharmony_ci	}
78862306a36Sopenharmony_ci}
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_cistatic void hid_ishtp_cl_resume_handler(struct work_struct *work)
79162306a36Sopenharmony_ci{
79262306a36Sopenharmony_ci	struct ishtp_cl_data *client_data = container_of(work, struct ishtp_cl_data, resume_work);
79362306a36Sopenharmony_ci	struct ishtp_cl *hid_ishtp_cl = client_data->hid_ishtp_cl;
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci	if (ishtp_wait_resume(ishtp_get_ishtp_device(hid_ishtp_cl))) {
79662306a36Sopenharmony_ci		client_data->suspended = false;
79762306a36Sopenharmony_ci		wake_up_interruptible(&client_data->ishtp_resume_wait);
79862306a36Sopenharmony_ci	}
79962306a36Sopenharmony_ci}
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ciishtp_print_log ishtp_hid_print_trace;
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci/**
80462306a36Sopenharmony_ci * hid_ishtp_cl_probe() - ISHTP client driver probe
80562306a36Sopenharmony_ci * @cl_device:		ISHTP client device instance
80662306a36Sopenharmony_ci *
80762306a36Sopenharmony_ci * This function gets called on device create on ISHTP bus
80862306a36Sopenharmony_ci *
80962306a36Sopenharmony_ci * Return: 0 on success, non zero on error
81062306a36Sopenharmony_ci */
81162306a36Sopenharmony_cistatic int hid_ishtp_cl_probe(struct ishtp_cl_device *cl_device)
81262306a36Sopenharmony_ci{
81362306a36Sopenharmony_ci	struct ishtp_cl *hid_ishtp_cl;
81462306a36Sopenharmony_ci	struct ishtp_cl_data *client_data;
81562306a36Sopenharmony_ci	int rv;
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	if (!cl_device)
81862306a36Sopenharmony_ci		return	-ENODEV;
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	client_data = devm_kzalloc(ishtp_device(cl_device),
82162306a36Sopenharmony_ci				   sizeof(*client_data),
82262306a36Sopenharmony_ci				   GFP_KERNEL);
82362306a36Sopenharmony_ci	if (!client_data)
82462306a36Sopenharmony_ci		return -ENOMEM;
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	hid_ishtp_cl = ishtp_cl_allocate(cl_device);
82762306a36Sopenharmony_ci	if (!hid_ishtp_cl)
82862306a36Sopenharmony_ci		return -ENOMEM;
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	ishtp_set_drvdata(cl_device, hid_ishtp_cl);
83162306a36Sopenharmony_ci	ishtp_set_client_data(hid_ishtp_cl, client_data);
83262306a36Sopenharmony_ci	client_data->hid_ishtp_cl = hid_ishtp_cl;
83362306a36Sopenharmony_ci	client_data->cl_device = cl_device;
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	init_waitqueue_head(&client_data->init_wait);
83662306a36Sopenharmony_ci	init_waitqueue_head(&client_data->ishtp_resume_wait);
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	INIT_WORK(&client_data->work, hid_ishtp_cl_reset_handler);
83962306a36Sopenharmony_ci	INIT_WORK(&client_data->resume_work, hid_ishtp_cl_resume_handler);
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	ishtp_hid_print_trace = ishtp_trace_callback(cl_device);
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	rv = hid_ishtp_cl_init(hid_ishtp_cl, 0);
84562306a36Sopenharmony_ci	if (rv) {
84662306a36Sopenharmony_ci		ishtp_cl_free(hid_ishtp_cl);
84762306a36Sopenharmony_ci		return rv;
84862306a36Sopenharmony_ci	}
84962306a36Sopenharmony_ci	ishtp_get_device(cl_device);
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	return 0;
85262306a36Sopenharmony_ci}
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci/**
85562306a36Sopenharmony_ci * hid_ishtp_cl_remove() - ISHTP client driver remove
85662306a36Sopenharmony_ci * @cl_device:		ISHTP client device instance
85762306a36Sopenharmony_ci *
85862306a36Sopenharmony_ci * This function gets called on device remove on ISHTP bus
85962306a36Sopenharmony_ci *
86062306a36Sopenharmony_ci * Return: 0
86162306a36Sopenharmony_ci */
86262306a36Sopenharmony_cistatic void hid_ishtp_cl_remove(struct ishtp_cl_device *cl_device)
86362306a36Sopenharmony_ci{
86462306a36Sopenharmony_ci	struct ishtp_cl *hid_ishtp_cl = ishtp_get_drvdata(cl_device);
86562306a36Sopenharmony_ci	struct ishtp_cl_data *client_data = ishtp_get_client_data(hid_ishtp_cl);
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	hid_ishtp_trace(client_data, "%s hid_ishtp_cl %p\n", __func__,
86862306a36Sopenharmony_ci			hid_ishtp_cl);
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	dev_dbg(ishtp_device(cl_device), "%s\n", __func__);
87162306a36Sopenharmony_ci	ishtp_set_connection_state(hid_ishtp_cl, ISHTP_CL_DISCONNECTING);
87262306a36Sopenharmony_ci	ishtp_cl_disconnect(hid_ishtp_cl);
87362306a36Sopenharmony_ci	ishtp_put_device(cl_device);
87462306a36Sopenharmony_ci	ishtp_hid_remove(client_data);
87562306a36Sopenharmony_ci	hid_ishtp_cl_deinit(hid_ishtp_cl);
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	hid_ishtp_cl = NULL;
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci	client_data->num_hid_devices = 0;
88062306a36Sopenharmony_ci}
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci/**
88362306a36Sopenharmony_ci * hid_ishtp_cl_reset() - ISHTP client driver reset
88462306a36Sopenharmony_ci * @cl_device:		ISHTP client device instance
88562306a36Sopenharmony_ci *
88662306a36Sopenharmony_ci * This function gets called on device reset on ISHTP bus
88762306a36Sopenharmony_ci *
88862306a36Sopenharmony_ci * Return: 0
88962306a36Sopenharmony_ci */
89062306a36Sopenharmony_cistatic int hid_ishtp_cl_reset(struct ishtp_cl_device *cl_device)
89162306a36Sopenharmony_ci{
89262306a36Sopenharmony_ci	struct ishtp_cl *hid_ishtp_cl = ishtp_get_drvdata(cl_device);
89362306a36Sopenharmony_ci	struct ishtp_cl_data *client_data = ishtp_get_client_data(hid_ishtp_cl);
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	hid_ishtp_trace(client_data, "%s hid_ishtp_cl %p\n", __func__,
89662306a36Sopenharmony_ci			hid_ishtp_cl);
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	schedule_work(&client_data->work);
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	return 0;
90162306a36Sopenharmony_ci}
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci/**
90462306a36Sopenharmony_ci * hid_ishtp_cl_suspend() - ISHTP client driver suspend
90562306a36Sopenharmony_ci * @device:	device instance
90662306a36Sopenharmony_ci *
90762306a36Sopenharmony_ci * This function gets called on system suspend
90862306a36Sopenharmony_ci *
90962306a36Sopenharmony_ci * Return: 0
91062306a36Sopenharmony_ci */
91162306a36Sopenharmony_cistatic int hid_ishtp_cl_suspend(struct device *device)
91262306a36Sopenharmony_ci{
91362306a36Sopenharmony_ci	struct ishtp_cl_device *cl_device = ishtp_dev_to_cl_device(device);
91462306a36Sopenharmony_ci	struct ishtp_cl *hid_ishtp_cl = ishtp_get_drvdata(cl_device);
91562306a36Sopenharmony_ci	struct ishtp_cl_data *client_data = ishtp_get_client_data(hid_ishtp_cl);
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	hid_ishtp_trace(client_data, "%s hid_ishtp_cl %p\n", __func__,
91862306a36Sopenharmony_ci			hid_ishtp_cl);
91962306a36Sopenharmony_ci	client_data->suspended = true;
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	return 0;
92262306a36Sopenharmony_ci}
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci/**
92562306a36Sopenharmony_ci * hid_ishtp_cl_resume() - ISHTP client driver resume
92662306a36Sopenharmony_ci * @device:	device instance
92762306a36Sopenharmony_ci *
92862306a36Sopenharmony_ci * This function gets called on system resume
92962306a36Sopenharmony_ci *
93062306a36Sopenharmony_ci * Return: 0
93162306a36Sopenharmony_ci */
93262306a36Sopenharmony_cistatic int hid_ishtp_cl_resume(struct device *device)
93362306a36Sopenharmony_ci{
93462306a36Sopenharmony_ci	struct ishtp_cl_device *cl_device = ishtp_dev_to_cl_device(device);
93562306a36Sopenharmony_ci	struct ishtp_cl *hid_ishtp_cl = ishtp_get_drvdata(cl_device);
93662306a36Sopenharmony_ci	struct ishtp_cl_data *client_data = ishtp_get_client_data(hid_ishtp_cl);
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci	hid_ishtp_trace(client_data, "%s hid_ishtp_cl %p\n", __func__,
93962306a36Sopenharmony_ci			hid_ishtp_cl);
94062306a36Sopenharmony_ci	schedule_work(&client_data->resume_work);
94162306a36Sopenharmony_ci	return 0;
94262306a36Sopenharmony_ci}
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_cistatic const struct dev_pm_ops hid_ishtp_pm_ops = {
94562306a36Sopenharmony_ci	.suspend = hid_ishtp_cl_suspend,
94662306a36Sopenharmony_ci	.resume = hid_ishtp_cl_resume,
94762306a36Sopenharmony_ci};
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_cistatic struct ishtp_cl_driver	hid_ishtp_cl_driver = {
95062306a36Sopenharmony_ci	.name = "ish-hid",
95162306a36Sopenharmony_ci	.id = hid_ishtp_id_table,
95262306a36Sopenharmony_ci	.probe = hid_ishtp_cl_probe,
95362306a36Sopenharmony_ci	.remove = hid_ishtp_cl_remove,
95462306a36Sopenharmony_ci	.reset = hid_ishtp_cl_reset,
95562306a36Sopenharmony_ci	.driver.pm = &hid_ishtp_pm_ops,
95662306a36Sopenharmony_ci};
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_cistatic int __init ish_hid_init(void)
95962306a36Sopenharmony_ci{
96062306a36Sopenharmony_ci	int	rv;
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci	/* Register ISHTP client device driver with ISHTP Bus */
96362306a36Sopenharmony_ci	rv = ishtp_cl_driver_register(&hid_ishtp_cl_driver, THIS_MODULE);
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci	return rv;
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci}
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_cistatic void __exit ish_hid_exit(void)
97062306a36Sopenharmony_ci{
97162306a36Sopenharmony_ci	ishtp_cl_driver_unregister(&hid_ishtp_cl_driver);
97262306a36Sopenharmony_ci}
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_cilate_initcall(ish_hid_init);
97562306a36Sopenharmony_cimodule_exit(ish_hid_exit);
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ciMODULE_DESCRIPTION("ISH ISHTP HID client driver");
97862306a36Sopenharmony_ci/* Primary author */
97962306a36Sopenharmony_ciMODULE_AUTHOR("Daniel Drubin <daniel.drubin@intel.com>");
98062306a36Sopenharmony_ci/*
98162306a36Sopenharmony_ci * Several modification for multi instance support
98262306a36Sopenharmony_ci * suspend/resume and clean up
98362306a36Sopenharmony_ci */
98462306a36Sopenharmony_ciMODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ciMODULE_LICENSE("GPL");
987