]> Git Repo - linux.git/blob - drivers/hid/surface-hid/surface_hid_core.c
slab: remove __alloc_size attribute from __kmalloc_track_caller
[linux.git] / drivers / hid / surface-hid / surface_hid_core.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Common/core components for the Surface System Aggregator Module (SSAM) HID
4  * transport driver. Provides support for integrated HID devices on Microsoft
5  * Surface models.
6  *
7  * Copyright (C) 2019-2021 Maximilian Luz <[email protected]>
8  */
9
10 #include <asm/unaligned.h>
11 #include <linux/hid.h>
12 #include <linux/kernel.h>
13 #include <linux/module.h>
14 #include <linux/types.h>
15 #include <linux/usb/ch9.h>
16
17 #include <linux/surface_aggregator/controller.h>
18
19 #include "surface_hid_core.h"
20
21
22 /* -- Device descriptor access. --------------------------------------------- */
23
24 static int surface_hid_load_hid_descriptor(struct surface_hid_device *shid)
25 {
26         int status;
27
28         status = shid->ops.get_descriptor(shid, SURFACE_HID_DESC_HID,
29                         (u8 *)&shid->hid_desc, sizeof(shid->hid_desc));
30         if (status)
31                 return status;
32
33         if (shid->hid_desc.desc_len != sizeof(shid->hid_desc)) {
34                 dev_err(shid->dev, "unexpected HID descriptor length: got %u, expected %zu\n",
35                         shid->hid_desc.desc_len, sizeof(shid->hid_desc));
36                 return -EPROTO;
37         }
38
39         if (shid->hid_desc.desc_type != HID_DT_HID) {
40                 dev_err(shid->dev, "unexpected HID descriptor type: got %#04x, expected %#04x\n",
41                         shid->hid_desc.desc_type, HID_DT_HID);
42                 return -EPROTO;
43         }
44
45         if (shid->hid_desc.num_descriptors != 1) {
46                 dev_err(shid->dev, "unexpected number of descriptors: got %u, expected 1\n",
47                         shid->hid_desc.num_descriptors);
48                 return -EPROTO;
49         }
50
51         if (shid->hid_desc.report_desc_type != HID_DT_REPORT) {
52                 dev_err(shid->dev, "unexpected report descriptor type: got %#04x, expected %#04x\n",
53                         shid->hid_desc.report_desc_type, HID_DT_REPORT);
54                 return -EPROTO;
55         }
56
57         return 0;
58 }
59
60 static int surface_hid_load_device_attributes(struct surface_hid_device *shid)
61 {
62         int status;
63
64         status = shid->ops.get_descriptor(shid, SURFACE_HID_DESC_ATTRS,
65                         (u8 *)&shid->attrs, sizeof(shid->attrs));
66         if (status)
67                 return status;
68
69         if (get_unaligned_le32(&shid->attrs.length) != sizeof(shid->attrs)) {
70                 dev_err(shid->dev, "unexpected attribute length: got %u, expected %zu\n",
71                         get_unaligned_le32(&shid->attrs.length), sizeof(shid->attrs));
72                 return -EPROTO;
73         }
74
75         return 0;
76 }
77
78
79 /* -- Transport driver (common). -------------------------------------------- */
80
81 static int surface_hid_start(struct hid_device *hid)
82 {
83         struct surface_hid_device *shid = hid->driver_data;
84
85         return ssam_notifier_register(shid->ctrl, &shid->notif);
86 }
87
88 static void surface_hid_stop(struct hid_device *hid)
89 {
90         struct surface_hid_device *shid = hid->driver_data;
91
92         /* Note: This call will log errors for us, so ignore them here. */
93         ssam_notifier_unregister(shid->ctrl, &shid->notif);
94 }
95
96 static int surface_hid_open(struct hid_device *hid)
97 {
98         return 0;
99 }
100
101 static void surface_hid_close(struct hid_device *hid)
102 {
103 }
104
105 static int surface_hid_parse(struct hid_device *hid)
106 {
107         struct surface_hid_device *shid = hid->driver_data;
108         size_t len = get_unaligned_le16(&shid->hid_desc.report_desc_len);
109         u8 *buf;
110         int status;
111
112         buf = kzalloc(len, GFP_KERNEL);
113         if (!buf)
114                 return -ENOMEM;
115
116         status = shid->ops.get_descriptor(shid, SURFACE_HID_DESC_REPORT, buf, len);
117         if (!status)
118                 status = hid_parse_report(hid, buf, len);
119
120         kfree(buf);
121         return status;
122 }
123
124 static int surface_hid_raw_request(struct hid_device *hid, unsigned char reportnum, u8 *buf,
125                                    size_t len, unsigned char rtype, int reqtype)
126 {
127         struct surface_hid_device *shid = hid->driver_data;
128
129         if (rtype == HID_OUTPUT_REPORT && reqtype == HID_REQ_SET_REPORT)
130                 return shid->ops.output_report(shid, reportnum, buf, len);
131
132         else if (rtype == HID_FEATURE_REPORT && reqtype == HID_REQ_GET_REPORT)
133                 return shid->ops.get_feature_report(shid, reportnum, buf, len);
134
135         else if (rtype == HID_FEATURE_REPORT && reqtype == HID_REQ_SET_REPORT)
136                 return shid->ops.set_feature_report(shid, reportnum, buf, len);
137
138         return -EIO;
139 }
140
141 static struct hid_ll_driver surface_hid_ll_driver = {
142         .start       = surface_hid_start,
143         .stop        = surface_hid_stop,
144         .open        = surface_hid_open,
145         .close       = surface_hid_close,
146         .parse       = surface_hid_parse,
147         .raw_request = surface_hid_raw_request,
148 };
149
150
151 /* -- Common device setup. -------------------------------------------------- */
152
153 int surface_hid_device_add(struct surface_hid_device *shid)
154 {
155         int status;
156
157         status = surface_hid_load_hid_descriptor(shid);
158         if (status)
159                 return status;
160
161         status = surface_hid_load_device_attributes(shid);
162         if (status)
163                 return status;
164
165         shid->hid = hid_allocate_device();
166         if (IS_ERR(shid->hid))
167                 return PTR_ERR(shid->hid);
168
169         shid->hid->dev.parent = shid->dev;
170         shid->hid->bus = BUS_HOST;
171         shid->hid->vendor = get_unaligned_le16(&shid->attrs.vendor);
172         shid->hid->product = get_unaligned_le16(&shid->attrs.product);
173         shid->hid->version = get_unaligned_le16(&shid->hid_desc.hid_version);
174         shid->hid->country = shid->hid_desc.country_code;
175
176         snprintf(shid->hid->name, sizeof(shid->hid->name), "Microsoft Surface %04X:%04X",
177                  shid->hid->vendor, shid->hid->product);
178
179         strscpy(shid->hid->phys, dev_name(shid->dev), sizeof(shid->hid->phys));
180
181         shid->hid->driver_data = shid;
182         shid->hid->ll_driver = &surface_hid_ll_driver;
183
184         status = hid_add_device(shid->hid);
185         if (status)
186                 hid_destroy_device(shid->hid);
187
188         return status;
189 }
190 EXPORT_SYMBOL_GPL(surface_hid_device_add);
191
192 void surface_hid_device_destroy(struct surface_hid_device *shid)
193 {
194         hid_destroy_device(shid->hid);
195 }
196 EXPORT_SYMBOL_GPL(surface_hid_device_destroy);
197
198
199 /* -- PM ops. --------------------------------------------------------------- */
200
201 #ifdef CONFIG_PM_SLEEP
202
203 static int surface_hid_suspend(struct device *dev)
204 {
205         struct surface_hid_device *d = dev_get_drvdata(dev);
206
207         return hid_driver_suspend(d->hid, PMSG_SUSPEND);
208 }
209
210 static int surface_hid_resume(struct device *dev)
211 {
212         struct surface_hid_device *d = dev_get_drvdata(dev);
213
214         return hid_driver_resume(d->hid);
215 }
216
217 static int surface_hid_freeze(struct device *dev)
218 {
219         struct surface_hid_device *d = dev_get_drvdata(dev);
220
221         return hid_driver_suspend(d->hid, PMSG_FREEZE);
222 }
223
224 static int surface_hid_poweroff(struct device *dev)
225 {
226         struct surface_hid_device *d = dev_get_drvdata(dev);
227
228         return hid_driver_suspend(d->hid, PMSG_HIBERNATE);
229 }
230
231 static int surface_hid_restore(struct device *dev)
232 {
233         struct surface_hid_device *d = dev_get_drvdata(dev);
234
235         return hid_driver_reset_resume(d->hid);
236 }
237
238 const struct dev_pm_ops surface_hid_pm_ops = {
239         .freeze   = surface_hid_freeze,
240         .thaw     = surface_hid_resume,
241         .suspend  = surface_hid_suspend,
242         .resume   = surface_hid_resume,
243         .poweroff = surface_hid_poweroff,
244         .restore  = surface_hid_restore,
245 };
246 EXPORT_SYMBOL_GPL(surface_hid_pm_ops);
247
248 #else /* CONFIG_PM_SLEEP */
249
250 const struct dev_pm_ops surface_hid_pm_ops = { };
251 EXPORT_SYMBOL_GPL(surface_hid_pm_ops);
252
253 #endif /* CONFIG_PM_SLEEP */
254
255 MODULE_AUTHOR("Maximilian Luz <[email protected]>");
256 MODULE_DESCRIPTION("HID transport driver core for Surface System Aggregator Module");
257 MODULE_LICENSE("GPL");
This page took 0.051838 seconds and 4 git commands to generate.