summaryrefslogtreecommitdiff
path: root/drivers/hid/amd-sfh-hid/amd_sfh_hid.c
diff options
context:
space:
mode:
authorSandeep Singh <sandeep.singh@amd.com>2020-10-09 23:01:37 +0300
committerJiri Kosina <jkosina@suse.cz>2020-10-22 13:05:03 +0300
commit4b2c53d93a4bc9d52cc0ec354629cfc9dc217f93 (patch)
tree8d5e1218a0d3cd5b310737e05a2190aa2f754d32 /drivers/hid/amd-sfh-hid/amd_sfh_hid.c
parent4f567b9f8141a86c7d878fadf136e5d1408e3e61 (diff)
downloadlinux-4b2c53d93a4bc9d52cc0ec354629cfc9dc217f93.tar.xz
SFH:Transport Driver to add support of AMD Sensor Fusion Hub (SFH)
To support AMD Sensor Fusion Hub (SFH) via the HID client's we need to register the client with the HID framework. Here we mostly address on how to register the client with the framework and define the interfaces for communication. Co-developed-by: Nehal Shah <Nehal-bakulchandra.Shah@amd.com> Signed-off-by: Nehal Shah <Nehal-bakulchandra.Shah@amd.com> Signed-off-by: Sandeep Singh <sandeep.singh@amd.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'drivers/hid/amd-sfh-hid/amd_sfh_hid.c')
-rw-r--r--drivers/hid/amd-sfh-hid/amd_sfh_hid.c174
1 files changed, 174 insertions, 0 deletions
diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_hid.c b/drivers/hid/amd-sfh-hid/amd_sfh_hid.c
new file mode 100644
index 000000000000..a471079a3bd0
--- /dev/null
+++ b/drivers/hid/amd-sfh-hid/amd_sfh_hid.c
@@ -0,0 +1,174 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * AMD MP2 Sensors transport driver
+ *
+ * Authors: Nehal Bakulchandra Shah <Nehal-bakulchandra.shah@amd.com>
+ * Sandeep Singh <sandeep.singh@amd.com>
+ */
+#include <linux/hid.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+
+#include "amd_sfh_hid.h"
+
+#define AMD_SFH_RESPONSE_TIMEOUT 1500
+
+/**
+ * amdtp_hid_parse() - hid-core .parse() callback
+ * @hid: hid device instance
+ *
+ * This function gets called during call to hid_add_device
+ *
+ * Return: 0 on success and non zero on error
+ */
+static int amdtp_hid_parse(struct hid_device *hid)
+{
+ struct amdtp_hid_data *hid_data = hid->driver_data;
+ struct amdtp_cl_data *cli_data = hid_data->cli_data;
+
+ return hid_parse_report(hid, cli_data->report_descr[hid_data->index],
+ cli_data->report_descr_sz[hid_data->index]);
+}
+
+/* Empty callbacks with success return code */
+static int amdtp_hid_start(struct hid_device *hid)
+{
+ return 0;
+}
+
+static void amdtp_hid_stop(struct hid_device *hid)
+{
+}
+
+static int amdtp_hid_open(struct hid_device *hid)
+{
+ return 0;
+}
+
+static void amdtp_hid_close(struct hid_device *hid)
+{
+}
+
+static int amdtp_raw_request(struct hid_device *hdev, u8 reportnum,
+ u8 *buf, size_t len, u8 rtype, int reqtype)
+{
+ return 0;
+}
+
+static void amdtp_hid_request(struct hid_device *hid, struct hid_report *rep, int reqtype)
+{
+ int rc;
+
+ switch (reqtype) {
+ case HID_REQ_GET_REPORT:
+ rc = amd_sfh_get_report(hid, rep->id, rep->type);
+ if (rc)
+ dev_err(&hid->dev, "AMDSFH get report error\n");
+ break;
+ case HID_REQ_SET_REPORT:
+ amd_sfh_set_report(hid, rep->id, reqtype);
+ break;
+ default:
+ break;
+ }
+}
+
+static int amdtp_wait_for_response(struct hid_device *hid)
+{
+ struct amdtp_hid_data *hid_data = hid->driver_data;
+ struct amdtp_cl_data *cli_data = hid_data->cli_data;
+ int i, ret = 0;
+
+ for (i = 0; i < cli_data->num_hid_devices; i++) {
+ if (cli_data->hid_sensor_hubs[i] == hid)
+ break;
+ }
+
+ if (!cli_data->request_done[i])
+ ret = wait_event_interruptible_timeout(hid_data->hid_wait,
+ cli_data->request_done[i],
+ msecs_to_jiffies(AMD_SFH_RESPONSE_TIMEOUT));
+ if (ret < 0)
+ return -ETIMEDOUT;
+ else if (ret == -ERESTARTSYS)
+ return -ERESTARTSYS;
+ else
+ return 0;
+}
+
+void amdtp_hid_wakeup(struct hid_device *hid)
+{
+ struct amdtp_hid_data *hid_data = hid->driver_data;
+ struct amdtp_cl_data *cli_data = hid_data->cli_data;
+
+ cli_data->request_done[cli_data->cur_hid_dev] = true;
+ wake_up_interruptible(&hid_data->hid_wait);
+}
+
+static struct hid_ll_driver amdtp_hid_ll_driver = {
+ .parse = amdtp_hid_parse,
+ .start = amdtp_hid_start,
+ .stop = amdtp_hid_stop,
+ .open = amdtp_hid_open,
+ .close = amdtp_hid_close,
+ .request = amdtp_hid_request,
+ .wait = amdtp_wait_for_response,
+ .raw_request = amdtp_raw_request,
+};
+
+int amdtp_hid_probe(u32 cur_hid_dev, struct amdtp_cl_data *cli_data)
+{
+ struct hid_device *hid;
+ struct amdtp_hid_data *hid_data;
+ int rc;
+
+ hid = hid_allocate_device();
+ if (IS_ERR(hid))
+ return PTR_ERR(hid);
+
+ hid_data = kzalloc(sizeof(*hid_data), GFP_KERNEL);
+ if (!hid_data) {
+ rc = -ENOMEM;
+ goto err_hid_data;
+ }
+
+ hid->ll_driver = &amdtp_hid_ll_driver;
+ hid_data->index = cur_hid_dev;
+ hid_data->cli_data = cli_data;
+ init_waitqueue_head(&hid_data->hid_wait);
+
+ hid->driver_data = hid_data;
+ cli_data->hid_sensor_hubs[cur_hid_dev] = hid;
+ hid->bus = BUS_AMD_AMDTP;
+ hid->vendor = AMD_SFH_HID_VENDOR;
+ hid->product = AMD_SFH_HID_PRODUCT;
+ snprintf(hid->name, sizeof(hid->name), "%s %04X:%04X", "hid-amdtp",
+ hid->vendor, hid->product);
+
+ rc = hid_add_device(hid);
+ if (rc)
+ goto err_hid_device;
+ return 0;
+
+err_hid_device:
+ kfree(hid_data);
+err_hid_data:
+ hid_destroy_device(hid);
+ return rc;
+}
+
+void amdtp_hid_remove(struct amdtp_cl_data *cli_data)
+{
+ int i;
+
+ for (i = 0; i < cli_data->num_hid_devices; ++i) {
+ kfree(cli_data->feature_report[i]);
+ kfree(cli_data->input_report[i]);
+ kfree(cli_data->report_descr[i]);
+ if (cli_data->hid_sensor_hubs[i]) {
+ kfree(cli_data->hid_sensor_hubs[i]->driver_data);
+ hid_destroy_device(cli_data->hid_sensor_hubs[i]);
+ cli_data->hid_sensor_hubs[i] = NULL;
+ }
+ }
+}