xref: /linux/sound/soc/sdca/sdca_hid.c (revision 67f8bc848ee31831336bd478e57d2f993551902e)
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/string.h>
18 #include <linux/types.h>
19 #include <sound/sdca.h>
20 #include <sound/sdca_function.h>
21 #include <sound/sdca_hid.h>
22 #include <sound/sdca_interrupts.h>
23 #include <sound/sdca_ump.h>
24 
25 static int sdwhid_parse(struct hid_device *hid)
26 {
27 	struct sdca_function_data *function = hid->driver_data;
28 	unsigned int rsize;
29 	int ret;
30 
31 	rsize = le16_to_cpu(function->hid.desc.rpt_desc.wDescriptorLength);
32 
33 	if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) {
34 		dev_err(&hid->dev, "invalid size of report descriptor (%u)\n", rsize);
35 		return -EINVAL;
36 	}
37 
38 	ret = hid_parse_report(hid, function->hid.report_desc, rsize);
39 
40 	if (!ret)
41 		return 0;
42 
43 	dev_err(&hid->dev, "parsing report descriptor failed\n");
44 	return ret;
45 }
46 
47 static int sdwhid_start(struct hid_device *hid)
48 {
49 	return 0;
50 }
51 
52 static void sdwhid_stop(struct hid_device *hid)
53 {
54 }
55 
56 static int sdwhid_raw_request(struct hid_device *hid, unsigned char reportnum,
57 			      __u8 *buf, size_t len, unsigned char rtype, int reqtype)
58 {
59 	switch (reqtype) {
60 	case HID_REQ_GET_REPORT:
61 		/* not implemented yet */
62 		return 0;
63 	case HID_REQ_SET_REPORT:
64 		/* not implemented yet */
65 		return 0;
66 	default:
67 		return -EIO;
68 	}
69 }
70 
71 static int sdwhid_open(struct hid_device *hid)
72 {
73 	return 0;
74 }
75 
76 static void sdwhid_close(struct hid_device *hid)
77 {
78 }
79 
80 static const struct hid_ll_driver sdw_hid_driver = {
81 	.parse = sdwhid_parse,
82 	.start = sdwhid_start,
83 	.stop = sdwhid_stop,
84 	.open = sdwhid_open,
85 	.close = sdwhid_close,
86 	.raw_request = sdwhid_raw_request,
87 };
88 
89 /**
90  * sdca_add_hid_device - create a new SDCA HID device
91  * @interrupt: Pointer to the SDCA interrupt information structure.
92  *
93  * Return: Zero on success, and a negative error code on failure.
94  */
95 int sdca_add_hid_device(struct sdca_interrupt *interrupt)
96 {
97 	struct device *dev = interrupt->dev;
98 	struct sdca_function_data *function = interrupt->function;
99 	struct hid_device *hid;
100 	int ret;
101 
102 	hid = hid_allocate_device();
103 	if (IS_ERR(hid))
104 		return PTR_ERR(hid);
105 
106 	hid->ll_driver = &sdw_hid_driver;
107 
108 	hid->dev.parent = dev;
109 	hid->bus = BUS_SDW;
110 	hid->version = le16_to_cpu(function->hid.desc.bcdHID);
111 
112 	strscpy(hid->phys, dev_name(dev));
113 	snprintf(hid->name, sizeof(hid->name), "SDCA %s:%02x",
114 		 function->desc->name, function->desc->adr);
115 
116 	hid->driver_data = function;
117 
118 	ret = hid_add_device(hid);
119 	if (ret && ret != -ENODEV) {
120 		dev_err(dev, "can't add hid device: %d\n", ret);
121 		hid_destroy_device(hid);
122 		return ret;
123 	}
124 
125 	interrupt->priv = hid;
126 
127 	return 0;
128 }
129 EXPORT_SYMBOL_NS(sdca_add_hid_device, "SND_SOC_SDCA");
130 
131 /**
132  * sdca_destroy_hid_device - destroy the HID device
133  * @interrupt: Pointer to the SDCA interrupt information structure.
134  */
135 void sdca_destroy_hid_device(struct sdca_interrupt *interrupt)
136 {
137 	struct hid_device *hid = interrupt->priv;
138 
139 	hid_destroy_device(hid);
140 }
141 EXPORT_SYMBOL_NS(sdca_destroy_hid_device, "SND_SOC_SDCA");
142 
143 /**
144  * sdca_hid_process_report - read a HID event from the device and report
145  * @interrupt: Pointer to the SDCA interrupt information structure.
146  *
147  * Return: Zero on success, and a negative error code on failure.
148  */
149 int sdca_hid_process_report(struct sdca_interrupt *interrupt)
150 {
151 	struct device *dev = interrupt->dev;
152 	struct hid_device *hid = interrupt->priv;
153 	void *val __free(kfree) = NULL;
154 	int len, ret;
155 
156 	ret = sdca_ump_get_owner_host(dev, interrupt->function_regmap,
157 				      interrupt->function, interrupt->entity,
158 				      interrupt->control);
159 	if (ret)
160 		return ret;
161 
162 	len = sdca_ump_read_message(dev, interrupt->device_regmap,
163 				    interrupt->function_regmap,
164 				    interrupt->function, interrupt->entity,
165 				    SDCA_CTL_HIDE_HIDTX_MESSAGEOFFSET,
166 				    SDCA_CTL_HIDE_HIDTX_MESSAGELENGTH, &val);
167 	if (len < 0)
168 		return len;
169 
170 	ret = sdca_ump_set_owner_device(dev, interrupt->function_regmap,
171 					interrupt->function, interrupt->entity,
172 					interrupt->control);
173 	if (ret)
174 		return ret;
175 
176 	ret = hid_input_report(hid, HID_INPUT_REPORT, val, len, true);
177 	if (ret < 0) {
178 		dev_err(dev, "failed to report hid event: %d\n", ret);
179 		return ret;
180 	}
181 
182 	return 0;
183 }
184 EXPORT_SYMBOL_NS(sdca_hid_process_report, "SND_SOC_SDCA");
185