1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * AMD MP2 Sensors transport driver 4 * 5 * Copyright 2020-2021 Advanced Micro Devices, Inc. 6 * Authors: Nehal Bakulchandra Shah <Nehal-bakulchandra.shah@amd.com> 7 * Sandeep Singh <sandeep.singh@amd.com> 8 * Basavaraj Natikar <Basavaraj.Natikar@amd.com> 9 */ 10 #include <linux/hid.h> 11 #include <linux/wait.h> 12 #include <linux/sched.h> 13 14 #include "amd_sfh_hid.h" 15 16 #define AMD_SFH_RESPONSE_TIMEOUT 1500 17 18 /** 19 * amdtp_hid_parse() - hid-core .parse() callback 20 * @hid: hid device instance 21 * 22 * This function gets called during call to hid_add_device 23 * 24 * Return: 0 on success and non zero on error 25 */ 26 static int amdtp_hid_parse(struct hid_device *hid) 27 { 28 struct amdtp_hid_data *hid_data = hid->driver_data; 29 struct amdtp_cl_data *cli_data = hid_data->cli_data; 30 31 return hid_parse_report(hid, cli_data->report_descr[hid_data->index], 32 cli_data->report_descr_sz[hid_data->index]); 33 } 34 35 /* Empty callbacks with success return code */ 36 static int amdtp_hid_start(struct hid_device *hid) 37 { 38 return 0; 39 } 40 41 static void amdtp_hid_stop(struct hid_device *hid) 42 { 43 } 44 45 static int amdtp_hid_open(struct hid_device *hid) 46 { 47 return 0; 48 } 49 50 static void amdtp_hid_close(struct hid_device *hid) 51 { 52 } 53 54 static int amdtp_raw_request(struct hid_device *hdev, u8 reportnum, 55 u8 *buf, size_t len, u8 rtype, int reqtype) 56 { 57 return 0; 58 } 59 60 static void amdtp_hid_request(struct hid_device *hid, struct hid_report *rep, int reqtype) 61 { 62 int rc; 63 64 switch (reqtype) { 65 case HID_REQ_GET_REPORT: 66 rc = amd_sfh_get_report(hid, rep->id, rep->type); 67 if (rc) 68 dev_err(&hid->dev, "AMDSFH get report error\n"); 69 break; 70 case HID_REQ_SET_REPORT: 71 amd_sfh_set_report(hid, rep->id, reqtype); 72 break; 73 default: 74 break; 75 } 76 } 77 78 static int amdtp_wait_for_response(struct hid_device *hid) 79 { 80 struct amdtp_hid_data *hid_data = hid->driver_data; 81 struct amdtp_cl_data *cli_data = hid_data->cli_data; 82 int i, ret = 0; 83 84 for (i = 0; i < cli_data->num_hid_devices; i++) { 85 if (cli_data->hid_sensor_hubs[i] == hid) 86 break; 87 } 88 89 if (!cli_data->request_done[i]) 90 ret = wait_event_interruptible_timeout(hid_data->hid_wait, 91 cli_data->request_done[i], 92 msecs_to_jiffies(AMD_SFH_RESPONSE_TIMEOUT)); 93 if (ret == -ERESTARTSYS) 94 return -ERESTARTSYS; 95 else if (ret < 0) 96 return -ETIMEDOUT; 97 else 98 return 0; 99 } 100 101 void amdtp_hid_wakeup(struct hid_device *hid) 102 { 103 struct amdtp_hid_data *hid_data = hid->driver_data; 104 struct amdtp_cl_data *cli_data = hid_data->cli_data; 105 106 cli_data->request_done[cli_data->cur_hid_dev] = true; 107 wake_up_interruptible(&hid_data->hid_wait); 108 } 109 110 static struct hid_ll_driver amdtp_hid_ll_driver = { 111 .parse = amdtp_hid_parse, 112 .start = amdtp_hid_start, 113 .stop = amdtp_hid_stop, 114 .open = amdtp_hid_open, 115 .close = amdtp_hid_close, 116 .request = amdtp_hid_request, 117 .wait = amdtp_wait_for_response, 118 .raw_request = amdtp_raw_request, 119 }; 120 121 int amdtp_hid_probe(u32 cur_hid_dev, struct amdtp_cl_data *cli_data) 122 { 123 struct hid_device *hid; 124 struct amdtp_hid_data *hid_data; 125 int rc; 126 127 hid = hid_allocate_device(); 128 if (IS_ERR(hid)) 129 return PTR_ERR(hid); 130 131 hid_data = kzalloc(sizeof(*hid_data), GFP_KERNEL); 132 if (!hid_data) { 133 rc = -ENOMEM; 134 goto err_hid_data; 135 } 136 137 hid->ll_driver = &amdtp_hid_ll_driver; 138 hid_data->index = cur_hid_dev; 139 hid_data->cli_data = cli_data; 140 init_waitqueue_head(&hid_data->hid_wait); 141 142 hid->driver_data = hid_data; 143 cli_data->hid_sensor_hubs[cur_hid_dev] = hid; 144 hid->bus = BUS_AMD_AMDTP; 145 hid->vendor = AMD_SFH_HID_VENDOR; 146 hid->product = AMD_SFH_HID_PRODUCT; 147 snprintf(hid->name, sizeof(hid->name), "%s %04X:%04X", "hid-amdtp", 148 hid->vendor, hid->product); 149 150 rc = hid_add_device(hid); 151 if (rc) 152 goto err_hid_device; 153 return 0; 154 155 err_hid_device: 156 kfree(hid_data); 157 err_hid_data: 158 hid_destroy_device(hid); 159 return rc; 160 } 161 162 void amdtp_hid_remove(struct amdtp_cl_data *cli_data) 163 { 164 int i; 165 166 for (i = 0; i < cli_data->num_hid_devices; ++i) { 167 if (cli_data->hid_sensor_hubs[i]) { 168 kfree(cli_data->hid_sensor_hubs[i]->driver_data); 169 hid_destroy_device(cli_data->hid_sensor_hubs[i]); 170 cli_data->hid_sensor_hubs[i] = NULL; 171 } 172 } 173 } 174