diff options
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.c | 311 |
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 | |||
48 | USBHHIDDriver USBHHIDD[HAL_USBHHID_MAX_INSTANCES]; | ||
49 | |||
50 | static void _hid_init(void); | ||
51 | static usbh_baseclassdriver_t *_hid_load(usbh_device_t *dev, const uint8_t *descriptor, uint16_t rem); | ||
52 | static void _hid_unload(usbh_baseclassdriver_t *drv); | ||
53 | static void _stop_locked(USBHHIDDriver *hidp); | ||
54 | |||
55 | static const usbh_classdriver_vmt_t class_driver_vmt = { | ||
56 | _hid_init, | ||
57 | _hid_load, | ||
58 | _hid_unload | ||
59 | }; | ||
60 | |||
61 | const usbh_classdriverinfo_t usbhhidClassDriverInfo = { | ||
62 | "HID", &class_driver_vmt | ||
63 | }; | ||
64 | |||
65 | static 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 | |||
94 | alloc_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 | |||
154 | deinit: | ||
155 | /* Here, the enpoints are closed, and the driver is unlinked */ | ||
156 | return NULL; | ||
157 | } | ||
158 | |||
159 | static 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 | |||
167 | static 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 | |||
190 | void 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 | |||
226 | static 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 | |||
241 | void usbhhidStop(USBHHIDDriver *hidp) { | ||
242 | chSemWait(&hidp->sem); | ||
243 | _stop_locked(hidp); | ||
244 | chSemSignal(&hidp->sem); | ||
245 | } | ||
246 | |||
247 | usbh_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 | |||
257 | usbh_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 | |||
267 | usbh_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 | |||
274 | usbh_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 | |||
281 | usbh_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 | |||
288 | usbh_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 | |||
296 | static 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 | |||
304 | static 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 | ||