xref: /linux/sound/soc/sdca/sdca_hid.c (revision 84318277d6334c6981ab326d4acc87c6a6ddc9b8)
1 // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
2 
3 /*
4  * The MIPI SDCA specification is available for public downloads at
5  * https://www.mipi.org/mipi-sdca-v1-0-download
6  */
7 
8 #include <linux/acpi.h>
9 #include <linux/byteorder/generic.h>
10 #include <linux/cleanup.h>
11 #include <linux/device.h>
12 #include <linux/dev_printk.h>
13 #include <linux/hid.h>
14 #include <linux/module.h>
15 #include <linux/property.h>
16 #include <linux/soundwire/sdw.h>
17 #include <linux/types.h>
18 #include <sound/sdca.h>
19 #include <sound/sdca_function.h>
20 #include <sound/sdca_hid.h>
21 #include <sound/sdca_interrupts.h>
22 #include <sound/sdca_ump.h>
23 
24 static int sdwhid_parse(struct hid_device *hid)
25 {
26 	struct sdca_entity *entity = hid->driver_data;
27 	unsigned int rsize;
28 	int ret;
29 
30 	rsize = le16_to_cpu(entity->hide.hid_desc.rpt_desc.wDescriptorLength);
31 
32 	if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) {
33 		dev_err(&hid->dev, "invalid size of report descriptor (%u)\n", rsize);
34 		return -EINVAL;
35 	}
36 
37 	ret = hid_parse_report(hid, entity->hide.hid_report_desc, rsize);
38 
39 	if (!ret)
40 		return 0;
41 
42 	dev_err(&hid->dev, "parsing report descriptor failed\n");
43 	return ret;
44 }
45 
46 static int sdwhid_start(struct hid_device *hid)
47 {
48 	return 0;
49 }
50 
51 static void sdwhid_stop(struct hid_device *hid)
52 {
53 }
54 
55 static int sdwhid_raw_request(struct hid_device *hid, unsigned char reportnum,
56 			      __u8 *buf, size_t len, unsigned char rtype, int reqtype)
57 {
58 	switch (reqtype) {
59 	case HID_REQ_GET_REPORT:
60 		/* not implemented yet */
61 		return 0;
62 	case HID_REQ_SET_REPORT:
63 		/* not implemented yet */
64 		return 0;
65 	default:
66 		return -EIO;
67 	}
68 }
69 
70 static int sdwhid_open(struct hid_device *hid)
71 {
72 	return 0;
73 }
74 
75 static void sdwhid_close(struct hid_device *hid)
76 {
77 }
78 
79 static const struct hid_ll_driver sdw_hid_driver = {
80 	.parse = sdwhid_parse,
81 	.start = sdwhid_start,
82 	.stop = sdwhid_stop,
83 	.open = sdwhid_open,
84 	.close = sdwhid_close,
85 	.raw_request = sdwhid_raw_request,
86 };
87 
88 int sdca_add_hid_device(struct device *dev, struct sdw_slave *sdw,
89 			struct sdca_entity *entity)
90 {
91 	struct sdw_bus *bus = sdw->bus;
92 	struct hid_device *hid;
93 	int ret;
94 
95 	hid = hid_allocate_device();
96 	if (IS_ERR(hid))
97 		return PTR_ERR(hid);
98 
99 	hid->ll_driver = &sdw_hid_driver;
100 
101 	hid->dev.parent = dev;
102 	hid->bus = BUS_SDW;
103 	hid->version = le16_to_cpu(entity->hide.hid_desc.bcdHID);
104 
105 	snprintf(hid->name, sizeof(hid->name),
106 		 "HID sdw:%01x:%01x:%04x:%04x:%02x",
107 		 bus->controller_id, bus->link_id, sdw->id.mfg_id,
108 		 sdw->id.part_id, sdw->id.class_id);
109 
110 	snprintf(hid->phys, sizeof(hid->phys), "%s", dev->bus->name);
111 
112 	hid->driver_data = entity;
113 
114 	ret = hid_add_device(hid);
115 	if (ret && ret != -ENODEV) {
116 		dev_err(dev, "can't add hid device: %d\n", ret);
117 		hid_destroy_device(hid);
118 		return ret;
119 	}
120 
121 	entity->hide.hid = hid;
122 
123 	return 0;
124 }
125 EXPORT_SYMBOL_NS(sdca_add_hid_device, "SND_SOC_SDCA");
126 
127 /**
128  * sdca_hid_process_report - read a HID event from the device and report
129  * @interrupt: Pointer to the SDCA interrupt information structure.
130  *
131  * Return: Zero on success, and a negative error code on failure.
132  */
133 int sdca_hid_process_report(struct sdca_interrupt *interrupt)
134 {
135 	struct device *dev = interrupt->dev;
136 	struct hid_device *hid = interrupt->entity->hide.hid;
137 	void *val __free(kfree) = NULL;
138 	int len, ret;
139 
140 	ret = sdca_ump_get_owner_host(dev, interrupt->function_regmap,
141 				      interrupt->function, interrupt->entity,
142 				      interrupt->control);
143 	if (ret)
144 		return ret;
145 
146 	len = sdca_ump_read_message(dev, interrupt->device_regmap,
147 				    interrupt->function_regmap,
148 				    interrupt->function, interrupt->entity,
149 				    SDCA_CTL_HIDE_HIDTX_MESSAGEOFFSET,
150 				    SDCA_CTL_HIDE_HIDTX_MESSAGELENGTH, &val);
151 	if (len < 0)
152 		return len;
153 
154 	ret = sdca_ump_set_owner_device(dev, interrupt->function_regmap,
155 					interrupt->function, interrupt->entity,
156 					interrupt->control);
157 	if (ret)
158 		return ret;
159 
160 	ret = hid_input_report(hid, HID_INPUT_REPORT, val, len, true);
161 	if (ret < 0) {
162 		dev_err(dev, "failed to report hid event: %d\n", ret);
163 		return ret;
164 	}
165 
166 	return 0;
167 }
168 EXPORT_SYMBOL_NS(sdca_hid_process_report, "SND_SOC_SDCA");
169