aboutsummaryrefslogtreecommitdiff
path: root/lib/chibios-contrib/os/hal/src/usbh/hal_usbh_hid.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/chibios-contrib/os/hal/src/usbh/hal_usbh_hid.c')
-rw-r--r--lib/chibios-contrib/os/hal/src/usbh/hal_usbh_hid.c311
1 files changed, 311 insertions, 0 deletions
diff --git a/lib/chibios-contrib/os/hal/src/usbh/hal_usbh_hid.c b/lib/chibios-contrib/os/hal/src/usbh/hal_usbh_hid.c
new file mode 100644
index 000000000..f381b90ec
--- /dev/null
+++ b/lib/chibios-contrib/os/hal/src/usbh/hal_usbh_hid.c
@@ -0,0 +1,311 @@
1/*
2 ChibiOS - Copyright (C) 2006..2017 Giovanni Di Sirio
3 Copyright (C) 2015..2019 Diego Ismirlian, (dismirlian(at)google's mail)
4
5 Licensed under the Apache License, Version 2.0 (the "License");
6 you may not use this file except in compliance with the License.
7 You may obtain a copy of the License at
8
9 http://www.apache.org/licenses/LICENSE-2.0
10
11 Unless required by applicable law or agreed to in writing, software
12 distributed under the License is distributed on an "AS IS" BASIS,
13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 See the License for the specific language governing permissions and
15 limitations under the License.
16*/
17
18#include "hal.h"
19
20#if HAL_USBH_USE_HID
21
22#if !HAL_USE_USBH
23#error "USBHHID needs USBH"
24#endif
25
26#include <string.h>
27#include "usbh/dev/hid.h"
28#include "usbh/internal.h"
29
30#define _USBH_DEBUG_HELPER_CLASS_DRIVER hidp
31#define _USBH_DEBUG_HELPER_ENABLE_TRACE USBHHID_DEBUG_ENABLE_TRACE
32#define _USBH_DEBUG_HELPER_ENABLE_INFO USBHHID_DEBUG_ENABLE_INFO
33#define _USBH_DEBUG_HELPER_ENABLE_WARNINGS USBHHID_DEBUG_ENABLE_WARNINGS
34#define _USBH_DEBUG_HELPER_ENABLE_ERRORS USBHHID_DEBUG_ENABLE_ERRORS
35#include "usbh/debug_helpers.h"
36
37#define USBH_HID_REQ_GET_REPORT 0x01
38#define USBH_HID_REQ_GET_IDLE 0x02
39#define USBH_HID_REQ_GET_PROTOCOL 0x03
40#define USBH_HID_REQ_SET_REPORT 0x09
41#define USBH_HID_REQ_SET_IDLE 0x0A
42#define USBH_HID_REQ_SET_PROTOCOL 0x0B
43
44/*===========================================================================*/
45/* USB Class driver loader for HID */
46/*===========================================================================*/
47
48USBHHIDDriver USBHHIDD[HAL_USBHHID_MAX_INSTANCES];
49
50static void _hid_init(void);
51static usbh_baseclassdriver_t *_hid_load(usbh_device_t *dev, const uint8_t *descriptor, uint16_t rem);
52static void _hid_unload(usbh_baseclassdriver_t *drv);
53static void _stop_locked(USBHHIDDriver *hidp);
54
55static const usbh_classdriver_vmt_t class_driver_vmt = {
56 _hid_init,
57 _hid_load,
58 _hid_unload
59};
60
61const usbh_classdriverinfo_t usbhhidClassDriverInfo = {
62 "HID", &class_driver_vmt
63};
64
65static usbh_baseclassdriver_t *_hid_load(usbh_device_t *dev, const uint8_t *descriptor, uint16_t rem) {
66 int i;
67 USBHHIDDriver *hidp;
68
69 if (_usbh_match_descriptor(descriptor, rem, USBH_DT_INTERFACE,
70 0x03, -1, -1) != HAL_SUCCESS)
71 return NULL;
72
73 const usbh_interface_descriptor_t * const ifdesc = (const usbh_interface_descriptor_t *)descriptor;
74
75 if ((ifdesc->bAlternateSetting != 0)
76 || (ifdesc->bNumEndpoints < 1)) {
77 return NULL;
78 }
79
80
81 /* alloc driver */
82 for (i = 0; i < HAL_USBHHID_MAX_INSTANCES; i++) {
83 if (USBHHIDD[i].dev == NULL) {
84 hidp = &USBHHIDD[i];
85 goto alloc_ok;
86 }
87 }
88
89 udevwarn("Can't alloc HID driver");
90
91 /* can't alloc */
92 return NULL;
93
94alloc_ok:
95 /* initialize the driver's variables */
96 hidp->epin.status = USBH_EPSTATUS_UNINITIALIZED;
97#if HAL_USBHHID_USE_INTERRUPT_OUT
98 hidp->epout.status = USBH_EPSTATUS_UNINITIALIZED;
99#endif
100 hidp->ifnum = ifdesc->bInterfaceNumber;
101 usbhEPSetName(&dev->ctrl, "HID[CTRL]");
102
103 /* parse the configuration descriptor */
104 if_iterator_t iif;
105 generic_iterator_t iep;
106 iif.iad = 0;
107 iif.curr = descriptor;
108 iif.rem = rem;
109 for (ep_iter_init(&iep, &iif); iep.valid; ep_iter_next(&iep)) {
110 const usbh_endpoint_descriptor_t *const epdesc = ep_get(&iep);
111 if ((epdesc->bEndpointAddress & 0x80) && (epdesc->bmAttributes == USBH_EPTYPE_INT)) {
112 udevinfof("INT IN endpoint found: bEndpointAddress=%02x", epdesc->bEndpointAddress);
113 usbhEPObjectInit(&hidp->epin, dev, epdesc);
114 usbhEPSetName(&hidp->epin, "HID[IIN ]");
115#if HAL_USBHHID_USE_INTERRUPT_OUT
116 } else if (((epdesc->bEndpointAddress & 0x80) == 0)
117 && (epdesc->bmAttributes == USBH_EPTYPE_INT)) {
118 uinfof("INT OUT endpoint found: bEndpointAddress=%02x", epdesc->bEndpointAddress);
119 usbhEPObjectInit(&hidp->epout, dev, epdesc);
120 usbhEPSetName(&hidp->epout, "HID[IOUT]");
121#endif
122 } else {
123 udevinfof("unsupported endpoint found: bEndpointAddress=%02x, bmAttributes=%02x",
124 epdesc->bEndpointAddress, epdesc->bmAttributes);
125 }
126 }
127 if (hidp->epin.status != USBH_EPSTATUS_CLOSED) {
128 goto deinit;
129 }
130
131 if (ifdesc->bInterfaceSubClass != 0x01) {
132 hidp->type = USBHHID_DEVTYPE_GENERIC;
133 udevinfof("HID: bInterfaceSubClass=%02x, generic HID", ifdesc->bInterfaceSubClass);
134 if (ifdesc->bInterfaceSubClass != 0x00) {
135 udevinfof("HID: bInterfaceSubClass=%02x is an invalid bInterfaceSubClass value",
136 ifdesc->bInterfaceSubClass);
137 }
138 } else if (ifdesc->bInterfaceProtocol == 0x01) {
139 hidp->type = USBHHID_DEVTYPE_BOOT_KEYBOARD;
140 udevinfo("HID: BOOT protocol keyboard found");
141 } else if (ifdesc->bInterfaceProtocol == 0x02) {
142 hidp->type = USBHHID_DEVTYPE_BOOT_MOUSE;
143 udevinfo("HID: BOOT protocol mouse found");
144 } else {
145 udeverrf("HID: bInterfaceProtocol=%02x is an invalid boot protocol, abort",
146 ifdesc->bInterfaceProtocol);
147 goto deinit;
148 }
149
150 hidp->state = USBHHID_STATE_ACTIVE;
151
152 return (usbh_baseclassdriver_t *)hidp;
153
154deinit:
155 /* Here, the enpoints are closed, and the driver is unlinked */
156 return NULL;
157}
158
159static void _hid_unload(usbh_baseclassdriver_t *drv) {
160 USBHHIDDriver *const hidp = (USBHHIDDriver *)drv;
161 chSemWait(&hidp->sem);
162 _stop_locked(hidp);
163 hidp->state = USBHHID_STATE_STOP;
164 chSemSignal(&hidp->sem);
165}
166
167static void _in_cb(usbh_urb_t *urb) {
168 USBHHIDDriver *const hidp = (USBHHIDDriver *)urb->userData;
169 switch (urb->status) {
170 case USBH_URBSTATUS_OK:
171 if (hidp->config->cb_report) {
172 hidp->config->cb_report(hidp, urb->actualLength);
173 }
174 break;
175 case USBH_URBSTATUS_DISCONNECTED:
176 uurbwarn("HID: URB IN disconnected");
177
178 return;
179 case USBH_URBSTATUS_TIMEOUT:
180 //no data
181 break;
182 default:
183 uurberrf("HID: URB IN status unexpected = %d", urb->status);
184 break;
185 }
186 usbhURBObjectResetI(&hidp->in_urb);
187 usbhURBSubmitI(&hidp->in_urb);
188}
189
190void usbhhidStart(USBHHIDDriver *hidp, const USBHHIDConfig *cfg) {
191 osalDbgCheck(hidp && cfg);
192 osalDbgCheck(cfg->report_buffer && (cfg->protocol <= USBHHID_PROTOCOL_REPORT));
193
194 chSemWait(&hidp->sem);
195 if (hidp->state == USBHHID_STATE_READY) {
196 chSemSignal(&hidp->sem);
197 return;
198 }
199 osalDbgCheck(hidp->state == USBHHID_STATE_ACTIVE);
200
201 hidp->config = cfg;
202
203 /* init the URBs */
204 uint32_t report_len = hidp->epin.wMaxPacketSize;
205 if (report_len > cfg->report_len)
206 report_len = cfg->report_len;
207 usbhURBObjectInit(&hidp->in_urb, &hidp->epin, _in_cb, hidp,
208 cfg->report_buffer, report_len);
209
210 /* open the int IN/OUT endpoints */
211 usbhEPOpen(&hidp->epin);
212#if HAL_USBHHID_USE_INTERRUPT_OUT
213 if (hidp->epout.status == USBH_EPSTATUS_CLOSED) {
214 usbhEPOpen(&hidp->epout);
215 }
216#endif
217
218 usbhhidSetProtocol(hidp, cfg->protocol);
219
220 usbhURBSubmit(&hidp->in_urb);
221
222 hidp->state = USBHHID_STATE_READY;
223 chSemSignal(&hidp->sem);
224}
225
226static void _stop_locked(USBHHIDDriver *hidp) {
227 if (hidp->state == USBHHID_STATE_ACTIVE)
228 return;
229
230 osalDbgCheck(hidp->state == USBHHID_STATE_READY);
231
232 usbhEPClose(&hidp->epin);
233#if HAL_USBHHID_USE_INTERRUPT_OUT
234 if (hidp->epout.status != USBH_EPSTATUS_UNINITIALIZED) {
235 usbhEPClose(&hidp->epout);
236 }
237#endif
238 hidp->state = USBHHID_STATE_ACTIVE;
239}
240
241void usbhhidStop(USBHHIDDriver *hidp) {
242 chSemWait(&hidp->sem);
243 _stop_locked(hidp);
244 chSemSignal(&hidp->sem);
245}
246
247usbh_urbstatus_t usbhhidGetReport(USBHHIDDriver *hidp,
248 uint8_t report_id, usbhhid_reporttype_t report_type,
249 void *data, uint16_t len) {
250 osalDbgCheck(hidp);
251 osalDbgAssert((uint8_t)report_type <= USBHHID_REPORTTYPE_FEATURE, "wrong report type");
252 return usbhControlRequest(hidp->dev,
253 USBH_REQTYPE_CLASSIN(USBH_REQTYPE_RECIP_INTERFACE), USBH_HID_REQ_GET_REPORT,
254 ((uint8_t)report_type << 8) | report_id, hidp->ifnum, len, data);
255}
256
257usbh_urbstatus_t usbhhidSetReport(USBHHIDDriver *hidp,
258 uint8_t report_id, usbhhid_reporttype_t report_type,
259 const void *data, uint16_t len) {
260 osalDbgCheck(hidp);
261 osalDbgAssert((uint8_t)report_type <= USBHHID_REPORTTYPE_FEATURE, "wrong report type");
262 return usbhControlRequest(hidp->dev,
263 USBH_REQTYPE_CLASSOUT(USBH_REQTYPE_RECIP_INTERFACE), USBH_HID_REQ_SET_REPORT,
264 ((uint8_t)report_type << 8) | report_id, hidp->ifnum, len, (void *)data);
265}
266
267usbh_urbstatus_t usbhhidGetIdle(USBHHIDDriver *hidp, uint8_t report_id, uint8_t *duration) {
268 osalDbgCheck(hidp);
269 return usbhControlRequest(hidp->dev,
270 USBH_REQTYPE_CLASSIN(USBH_REQTYPE_RECIP_INTERFACE), USBH_HID_REQ_GET_IDLE,
271 report_id, hidp->ifnum, 1, duration);
272}
273
274usbh_urbstatus_t usbhhidSetIdle(USBHHIDDriver *hidp, uint8_t report_id, uint8_t duration) {
275 osalDbgCheck(hidp);
276 return usbhControlRequest(hidp->dev,
277 USBH_REQTYPE_CLASSOUT(USBH_REQTYPE_RECIP_INTERFACE), USBH_HID_REQ_SET_IDLE,
278 (duration << 8) | report_id, hidp->ifnum, 0, NULL);
279}
280
281usbh_urbstatus_t usbhhidGetProtocol(USBHHIDDriver *hidp, uint8_t *protocol) {
282 osalDbgCheck(hidp);
283 return usbhControlRequest(hidp->dev,
284 USBH_REQTYPE_CLASSIN(USBH_REQTYPE_RECIP_INTERFACE), USBH_HID_REQ_GET_PROTOCOL,
285 0, hidp->ifnum, 1, protocol);
286}
287
288usbh_urbstatus_t usbhhidSetProtocol(USBHHIDDriver *hidp, uint8_t protocol) {
289 osalDbgCheck(hidp);
290 osalDbgAssert(protocol <= 1, "invalid protocol");
291 return usbhControlRequest(hidp->dev,
292 USBH_REQTYPE_CLASSOUT(USBH_REQTYPE_RECIP_INTERFACE), USBH_HID_REQ_SET_PROTOCOL,
293 protocol, hidp->ifnum, 0, NULL);
294}
295
296static void _hid_object_init(USBHHIDDriver *hidp) {
297 osalDbgCheck(hidp != NULL);
298 memset(hidp, 0, sizeof(*hidp));
299 hidp->info = &usbhhidClassDriverInfo;
300 hidp->state = USBHHID_STATE_STOP;
301 chSemObjectInit(&hidp->sem, 1);
302}
303
304static void _hid_init(void) {
305 uint8_t i;
306 for (i = 0; i < HAL_USBHHID_MAX_INSTANCES; i++) {
307 _hid_object_init(&USBHHIDD[i]);
308 }
309}
310
311#endif