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