source: mainline/uspace/drv/usbhid/usbhid.c@ 60c0573

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 60c0573 was 60c0573, checked in by Lubos Slovak <lubos.slovak@…>, 14 years ago

Preparation for HID subdrivers.

  • Preparation for registering subdriver callbacks based on some device identifiers.
  • HID driver now uses one general callback which calls all the registered subdriver callbacks.
  • Other callbacks for init, deinit and polling ended.
  • Setting boot keyboard and mouse callbacks separately (special cases).

TODO maybe also functions and function classes may be per-subdriver

(so a hybrid keyboard/pointing device may register itself as

both keyboard and mouse).

TODO the current keyboard and mouse subdrivers should fall back to the

booot protocol by default.

  • Property mode set to 100644
File size: 11.2 KB
Line 
1/*
2 * Copyright (c) 2011 Lubos Slovak
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup drvusbhid
30 * @{
31 */
32/**
33 * @file
34 * USB HID driver API.
35 */
36
37#include <usb/debug.h>
38#include <usb/classes/classes.h>
39#include <usb/classes/hid.h>
40#include <usb/classes/hidparser.h>
41#include <usb/classes/hidreport.h>
42#include <usb/classes/hidreq.h>
43#include <errno.h>
44
45#include "usbhid.h"
46
47#include "kbd/kbddev.h"
48#include "generic/hiddev.h"
49#include "mouse/mousedev.h"
50
51/*----------------------------------------------------------------------------*/
52
53typedef struct usb_hid_callback_mapping {
54 usb_hid_report_path_t *path;
55 char *vendor_id;
56 char *product_id;
57} usb_hid_callback_mapping;
58
59/*----------------------------------------------------------------------------*/
60
61/* Array of endpoints expected on the device, NULL terminated. */
62usb_endpoint_description_t *usb_hid_endpoints[USB_HID_POLL_EP_COUNT + 1] = {
63 &usb_hid_kbd_poll_endpoint_description,
64 &usb_hid_mouse_poll_endpoint_description,
65 &usb_hid_generic_poll_endpoint_description,
66 NULL
67};
68
69/*----------------------------------------------------------------------------*/
70
71static int usb_hid_set_boot_kbd_subdriver(usb_hid_dev_t *hid_dev)
72{
73 assert(hid_dev->subdriver_count == 0);
74
75 hid_dev->subdrivers = (usb_hid_subdriver_t *)malloc(
76 sizeof(usb_hid_subdriver_t));
77 if (hid_dev->subdrivers == NULL) {
78 return ENOMEM;
79 }
80
81 // set the init callback
82 hid_dev->subdrivers[0].init = usb_kbd_init;
83
84 // set the polling callback
85 hid_dev->subdrivers[0].poll = usb_kbd_polling_callback;
86
87 // set the polling ended callback
88 hid_dev->subdrivers[0].poll_end = NULL;
89
90 // set the deinit callback
91 hid_dev->subdrivers[0].deinit = usb_kbd_deinit;
92
93 // set subdriver count
94 hid_dev->subdriver_count = 1;
95
96 return EOK;
97}
98
99/*----------------------------------------------------------------------------*/
100
101static int usb_hid_set_boot_mouse_subdriver(usb_hid_dev_t *hid_dev)
102{
103 assert(hid_dev->subdriver_count == 0);
104
105 hid_dev->subdrivers = (usb_hid_subdriver_t *)malloc(
106 sizeof(usb_hid_subdriver_t));
107 if (hid_dev->subdrivers == NULL) {
108 return ENOMEM;
109 }
110
111 // set the init callback
112 hid_dev->subdrivers[0].init = usb_mouse_init;
113
114 // set the polling callback
115 hid_dev->subdrivers[0].poll = usb_mouse_polling_callback;
116
117 // set the polling ended callback
118 hid_dev->subdrivers[0].poll_end = NULL;
119
120 // set the deinit callback
121 hid_dev->subdrivers[0].deinit = usb_mouse_deinit;
122
123 // set subdriver count
124 hid_dev->subdriver_count = 1;
125
126 return EOK;
127}
128
129/*----------------------------------------------------------------------------*/
130
131static int usb_hid_set_generic_hid_subdriver(usb_hid_dev_t *hid_dev)
132{
133 assert(hid_dev->subdriver_count == 0);
134
135 hid_dev->subdrivers = (usb_hid_subdriver_t *)malloc(
136 sizeof(usb_hid_subdriver_t));
137 if (hid_dev->subdrivers == NULL) {
138 return ENOMEM;
139 }
140
141 // set the init callback
142 hid_dev->subdrivers[0].init = NULL;
143
144 // set the polling callback
145 hid_dev->subdrivers[0].poll = usb_generic_hid_polling_callback;
146
147 // set the polling ended callback
148 hid_dev->subdrivers[0].poll_end = NULL;
149
150 // set the deinit callback
151 hid_dev->subdrivers[0].deinit = NULL;
152
153 return EOK;
154}
155
156/*----------------------------------------------------------------------------*/
157
158static int usb_hid_find_subdrivers(usb_hid_dev_t *hid_dev)
159{
160 return EOK;
161}
162
163/*----------------------------------------------------------------------------*/
164
165static int usb_hid_check_pipes(usb_hid_dev_t *hid_dev, usb_device_t *dev)
166{
167 // first try to find subdrivers that may want to handle this device
168 int rc = usb_hid_find_subdrivers(hid_dev);
169
170 if (dev->pipes[USB_HID_KBD_POLL_EP_NO].present) {
171 usb_log_debug("Found keyboard endpoint.\n");
172
173 // save the pipe index
174 hid_dev->poll_pipe_index = USB_HID_KBD_POLL_EP_NO;
175
176 // if no subdrivers registered, use the boot kbd subdriver
177 if (hid_dev->subdriver_count == 0) {
178 rc = usb_hid_set_boot_kbd_subdriver(hid_dev);
179 }
180 } else if (dev->pipes[USB_HID_MOUSE_POLL_EP_NO].present) {
181 usb_log_debug("Found mouse endpoint.\n");
182
183 // save the pipe index
184 hid_dev->poll_pipe_index = USB_HID_MOUSE_POLL_EP_NO;
185 //hid_dev->device_type = USB_HID_PROTOCOL_MOUSE;
186
187 // if no subdrivers registered, use the boot kbd subdriver
188 if (hid_dev->subdriver_count == 0) {
189 rc = usb_hid_set_boot_mouse_subdriver(hid_dev);
190 }
191 } else if (dev->pipes[USB_HID_GENERIC_POLL_EP_NO].present) {
192 usb_log_debug("Found generic HID endpoint.\n");
193
194 // save the pipe index
195 hid_dev->poll_pipe_index = USB_HID_GENERIC_POLL_EP_NO;
196
197 if (hid_dev->subdriver_count == 0) {
198 usb_log_warning("Found no subdriver for handling this"
199 " HID device. Setting generic HID subdriver.\n");
200 usb_hid_set_generic_hid_subdriver(hid_dev);
201 return EOK;
202 }
203 } else {
204 usb_log_error("None of supported endpoints found - probably"
205 " not a supported device.\n");
206 rc = ENOTSUP;
207 }
208
209 return rc;
210}
211
212/*----------------------------------------------------------------------------*/
213
214static int usb_hid_init_parser(usb_hid_dev_t *hid_dev)
215{
216 /* Initialize the report parser. */
217 int rc = usb_hid_parser_init(hid_dev->parser);
218 if (rc != EOK) {
219 usb_log_error("Failed to initialize report parser.\n");
220 return rc;
221 }
222
223 /* Get the report descriptor and parse it. */
224 rc = usb_hid_process_report_descriptor(hid_dev->usb_dev,
225 hid_dev->parser);
226
227 // TODO: remove the hack
228 if (rc != EOK || hid_dev->poll_pipe_index == USB_HID_MOUSE_POLL_EP_NO) {
229 usb_log_warning("Could not process report descriptor.\n");
230
231 if (hid_dev->poll_pipe_index == USB_HID_KBD_POLL_EP_NO) {
232 usb_log_warning("Falling back to boot protocol.\n");
233 rc = usb_kbd_set_boot_protocol(hid_dev);
234 } else if (hid_dev->poll_pipe_index
235 == USB_HID_MOUSE_POLL_EP_NO) {
236 usb_log_warning("Falling back to boot protocol.\n");
237 rc = usb_mouse_set_boot_protocol(hid_dev);
238 }
239 }
240
241 return rc;
242}
243
244/*----------------------------------------------------------------------------*/
245
246usb_hid_dev_t *usb_hid_new(void)
247{
248 usb_hid_dev_t *hid_dev = (usb_hid_dev_t *)calloc(1,
249 sizeof(usb_hid_dev_t));
250
251 if (hid_dev == NULL) {
252 usb_log_fatal("No memory!\n");
253 return NULL;
254 }
255
256 hid_dev->parser = (usb_hid_report_parser_t *)(malloc(sizeof(
257 usb_hid_report_parser_t)));
258 if (hid_dev->parser == NULL) {
259 usb_log_fatal("No memory!\n");
260 free(hid_dev);
261 return NULL;
262 }
263
264 return hid_dev;
265}
266
267/*----------------------------------------------------------------------------*/
268
269int usb_hid_init(usb_hid_dev_t *hid_dev, usb_device_t *dev)
270{
271 int rc, i;
272
273 usb_log_debug("Initializing HID structure...\n");
274
275 if (hid_dev == NULL) {
276 usb_log_error("Failed to init HID structure: no structure given"
277 ".\n");
278 return EINVAL;
279 }
280
281 if (dev == NULL) {
282 usb_log_error("Failed to init HID structure: no USB device"
283 " given.\n");
284 return EINVAL;
285 }
286
287 /* The USB device should already be initialized, save it in structure */
288 hid_dev->usb_dev = dev;
289
290 rc = usb_hid_check_pipes(hid_dev, dev);
291 if (rc != EOK) {
292 return rc;
293 }
294
295 rc = usb_hid_init_parser(hid_dev);
296 if (rc != EOK) {
297 usb_log_error("Failed to initialize HID parser.\n");
298 return rc;
299 }
300
301 for (i = 0; i < hid_dev->subdriver_count; ++i) {
302 if (hid_dev->subdrivers[i].init != NULL) {
303 rc = hid_dev->subdrivers[i].init(hid_dev);
304 if (rc != EOK) {
305 usb_log_warning("Failed to initialize HID"
306 " subdriver structure.\n");
307 }
308 }
309 }
310
311 return rc;
312}
313
314/*----------------------------------------------------------------------------*/
315
316bool usb_hid_polling_callback(usb_device_t *dev, uint8_t *buffer,
317 size_t buffer_size, void *arg)
318{
319 int i;
320
321 if (dev == NULL || arg == NULL || buffer == NULL) {
322 usb_log_error("Missing arguments to polling callback.\n");
323 return false;
324 }
325
326 usb_hid_dev_t *hid_dev = (usb_hid_dev_t *)arg;
327
328 bool cont = false;
329
330 // continue if at least one of the subdrivers want to continue
331 for (i = 0; i < hid_dev->subdriver_count; ++i) {
332 if (hid_dev->subdrivers[i].poll != NULL
333 && hid_dev->subdrivers[i].poll(hid_dev, buffer,
334 buffer_size)) {
335 cont = true;
336 }
337 }
338
339 return cont;
340}
341
342/*----------------------------------------------------------------------------*/
343
344void usb_hid_polling_ended_callback(usb_device_t *dev, bool reason,
345 void *arg)
346{
347 int i;
348
349 if (dev == NULL || arg == NULL) {
350 return;
351 }
352
353 usb_hid_dev_t *hid_dev = (usb_hid_dev_t *)arg;
354
355 for (i = 0; i < hid_dev->subdriver_count; ++i) {
356 if (hid_dev->subdrivers[i].poll_end != NULL) {
357 hid_dev->subdrivers[i].poll_end(hid_dev, reason);
358 }
359 }
360
361 usb_hid_free(&hid_dev);
362}
363
364/*----------------------------------------------------------------------------*/
365
366const char *usb_hid_get_function_name(const usb_hid_dev_t *hid_dev)
367{
368 switch (hid_dev->poll_pipe_index) {
369 case USB_HID_KBD_POLL_EP_NO:
370 return HID_KBD_FUN_NAME;
371 break;
372 case USB_HID_MOUSE_POLL_EP_NO:
373 return HID_MOUSE_FUN_NAME;
374 break;
375 default:
376 return HID_GENERIC_FUN_NAME;
377 }
378}
379
380/*----------------------------------------------------------------------------*/
381
382const char *usb_hid_get_class_name(const usb_hid_dev_t *hid_dev)
383{
384 // this means that only boot protocol keyboards will be connected
385 // to the console; there is probably no better way to do this
386
387 switch (hid_dev->poll_pipe_index) {
388 case USB_HID_KBD_POLL_EP_NO:
389 return HID_KBD_CLASS_NAME;
390 break;
391 case USB_HID_MOUSE_POLL_EP_NO:
392 return HID_MOUSE_CLASS_NAME;
393 break;
394 default:
395 return HID_GENERIC_CLASS_NAME;
396 }
397}
398
399/*----------------------------------------------------------------------------*/
400
401void usb_hid_free(usb_hid_dev_t **hid_dev)
402{
403 int i;
404
405 if (hid_dev == NULL || *hid_dev == NULL) {
406 return;
407 }
408
409 for (i = 0; i < (*hid_dev)->subdriver_count; ++i) {
410 if ((*hid_dev)->subdrivers[i].deinit != NULL) {
411 (*hid_dev)->subdrivers[i].deinit(*hid_dev);
412 }
413 }
414
415 // destroy the parser
416 if ((*hid_dev)->parser != NULL) {
417 usb_hid_free_report_parser((*hid_dev)->parser);
418 }
419
420 if ((*hid_dev)->report_desc != NULL) {
421 free((*hid_dev)->report_desc);
422 }
423
424 free(*hid_dev);
425 *hid_dev = NULL;
426}
427
428/**
429 * @}
430 */
Note: See TracBrowser for help on using the repository browser.