| /* SPDX-License-Identifier: GPL-2.0 */ | |
| /* Copyright (c) 2024 Intel Corporation */ | |
| #include <linux/hid.h> | |
| #include <linux/input.h> | |
| #include <linux/pm_runtime.h> | |
| #include "quicki2c-dev.h" | |
| #include "quicki2c-hid.h" | |
| #include "quicki2c-protocol.h" | |
| /** | |
| * quicki2c_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 quicki2c_hid_parse(struct hid_device *hid) | |
| { | |
| struct quicki2c_device *qcdev = hid->driver_data; | |
| if (qcdev->report_descriptor) | |
| return hid_parse_report(hid, qcdev->report_descriptor, | |
| le16_to_cpu(qcdev->dev_desc.report_desc_len)); | |
| dev_err_once(qcdev->dev, "invalid report descriptor\n"); | |
| return -EINVAL; | |
| } | |
| static int quicki2c_hid_start(struct hid_device *hid) | |
| { | |
| return 0; | |
| } | |
| static void quicki2c_hid_stop(struct hid_device *hid) | |
| { | |
| } | |
| static int quicki2c_hid_open(struct hid_device *hid) | |
| { | |
| return 0; | |
| } | |
| static void quicki2c_hid_close(struct hid_device *hid) | |
| { | |
| } | |
| static int quicki2c_hid_raw_request(struct hid_device *hid, | |
| unsigned char reportnum, | |
| __u8 *buf, size_t len, | |
| unsigned char rtype, int reqtype) | |
| { | |
| struct quicki2c_device *qcdev = hid->driver_data; | |
| int ret = 0; | |
| ret = pm_runtime_resume_and_get(qcdev->dev); | |
| if (ret) | |
| return ret; | |
| switch (reqtype) { | |
| case HID_REQ_GET_REPORT: | |
| ret = quicki2c_get_report(qcdev, rtype, reportnum, buf, len); | |
| break; | |
| case HID_REQ_SET_REPORT: | |
| ret = quicki2c_set_report(qcdev, rtype, reportnum, buf, len); | |
| break; | |
| default: | |
| dev_err(qcdev->dev, "Not supported request type %d\n", reqtype); | |
| break; | |
| } | |
| pm_runtime_put_autosuspend(qcdev->dev); | |
| return ret; | |
| } | |
| static int quicki2c_hid_power(struct hid_device *hid, int lvl) | |
| { | |
| return 0; | |
| } | |
| static int quicki2c_hid_output_report(struct hid_device *hid, u8 *buf, size_t count) | |
| { | |
| struct quicki2c_device *qcdev = hid->driver_data; | |
| return quicki2c_output_report(qcdev, buf, count); | |
| } | |
| static struct hid_ll_driver quicki2c_hid_ll_driver = { | |
| .parse = quicki2c_hid_parse, | |
| .start = quicki2c_hid_start, | |
| .stop = quicki2c_hid_stop, | |
| .open = quicki2c_hid_open, | |
| .close = quicki2c_hid_close, | |
| .power = quicki2c_hid_power, | |
| .raw_request = quicki2c_hid_raw_request, | |
| .output_report = quicki2c_hid_output_report, | |
| }; | |
| /** | |
| * quicki2c_hid_probe() - Register HID low level driver | |
| * | |
| * @qcdev: point to quicki2c device | |
| * | |
| * This function is used to allocate and add HID device. | |
| * | |
| * Return: 0 on success, non zero on error. | |
| */ | |
| int quicki2c_hid_probe(struct quicki2c_device *qcdev) | |
| { | |
| struct hid_device *hid; | |
| int ret; | |
| hid = hid_allocate_device(); | |
| if (IS_ERR(hid)) | |
| return PTR_ERR(hid); | |
| hid->ll_driver = &quicki2c_hid_ll_driver; | |
| hid->bus = BUS_PCI; | |
| hid->dev.parent = qcdev->dev; | |
| hid->driver_data = qcdev; | |
| hid->version = le16_to_cpu(qcdev->dev_desc.version_id); | |
| hid->vendor = le16_to_cpu(qcdev->dev_desc.vendor_id); | |
| hid->product = le16_to_cpu(qcdev->dev_desc.product_id); | |
| snprintf(hid->name, sizeof(hid->name), "%s %04X:%04X", "quicki2c-hid", | |
| hid->vendor, hid->product); | |
| strscpy(hid->phys, dev_name(qcdev->dev), sizeof(hid->phys)); | |
| ret = hid_add_device(hid); | |
| if (ret) { | |
| hid_destroy_device(hid); | |
| return ret; | |
| } | |
| qcdev->hid_dev = hid; | |
| return 0; | |
| } | |
| /** | |
| * quicki2c_hid_remove() - Destroy HID device | |
| * | |
| * @qcdev: point to quicki2c device | |
| * | |
| * Return: 0 on success, non zero on error. | |
| */ | |
| void quicki2c_hid_remove(struct quicki2c_device *qcdev) | |
| { | |
| hid_destroy_device(qcdev->hid_dev); | |
| } | |
| /** | |
| * quicki2c_hid_send_report() - Send HID input report data to HID core | |
| * | |
| * @qcdev: point to quicki2c device | |
| * @data: point to input report data buffer | |
| * @data_len: the length of input report data | |
| * | |
| * Return: 0 on success, non zero on error. | |
| */ | |
| int quicki2c_hid_send_report(struct quicki2c_device *qcdev, | |
| void *data, size_t data_len) | |
| { | |
| int ret; | |
| ret = hid_input_report(qcdev->hid_dev, HID_INPUT_REPORT, data, data_len, 1); | |
| if (ret) | |
| dev_err(qcdev->dev, "Failed to send HID input report, ret = %d.\n", ret); | |
| return ret; | |
| } |