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