source: mainline/uspace/drv/usbhid/hiddev.c@ 5d1db18

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

Fixed destroying of KBD structure when polling ended.

The structure must be destroyed in the autorepeat fibril, as there is
no nice way to stop the fibril on demand.

  • Property mode set to 100644
File size: 13.0 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 * Generic USB HID device structure and API.
35 */
36
37#include <assert.h>
38#include <errno.h>
39#include <str_error.h>
40
41#include <ddf/driver.h>
42
43#include <usb/dp.h>
44#include <usb/debug.h>
45#include <usb/request.h>
46#include <usb/descriptor.h>
47#include <usb/classes/hid.h>
48#include <usb/pipes.h>
49
50#include "hiddev.h"
51
52/*----------------------------------------------------------------------------*/
53/* Non-API functions */
54/*----------------------------------------------------------------------------*/
55/**
56 * Retreives HID Report descriptor from the device.
57 *
58 * This function first parses the HID descriptor from the Interface descriptor
59 * to get the size of the Report descriptor and then requests the Report
60 * descriptor from the device.
61 *
62 * @param hid_dev HID device structure.
63 * @param config_desc Full configuration descriptor (including all nested
64 * descriptors).
65 * @param config_desc_size Size of the full configuration descriptor (in bytes).
66 * @param iface_desc Pointer to the interface descriptor inside the full
67 * configuration descriptor (@a config_desc) for the interface
68 * assigned with this device (@a hid_dev).
69 *
70 * @retval EOK if successful.
71 * @retval ENOENT if no HID descriptor could be found.
72 * @retval EINVAL if the HID descriptor or HID report descriptor have different
73 * size than expected.
74 * @retval ENOMEM if some allocation failed.
75 * @return Other value inherited from function usb_request_get_descriptor().
76 *
77 * @sa usb_request_get_descriptor()
78 */
79static int usbhid_dev_get_report_descriptor(usbhid_dev_t *hid_dev,
80 uint8_t *config_desc, size_t config_desc_size, uint8_t *iface_desc)
81{
82 assert(hid_dev != NULL);
83 assert(config_desc != NULL);
84 assert(config_desc_size != 0);
85 assert(iface_desc != NULL);
86
87 usb_dp_parser_t parser = {
88 .nesting = usb_dp_standard_descriptor_nesting
89 };
90
91 usb_dp_parser_data_t parser_data = {
92 .data = config_desc,
93 .size = config_desc_size,
94 .arg = NULL
95 };
96
97 /*
98 * First nested descriptor of interface descriptor.
99 */
100 uint8_t *d =
101 usb_dp_get_nested_descriptor(&parser, &parser_data, iface_desc);
102
103 /*
104 * Search through siblings until the HID descriptor is found.
105 */
106 while (d != NULL && *(d + 1) != USB_DESCTYPE_HID) {
107 d = usb_dp_get_sibling_descriptor(&parser, &parser_data,
108 iface_desc, d);
109 }
110
111 if (d == NULL) {
112 usb_log_fatal("No HID descriptor found!\n");
113 return ENOENT;
114 }
115
116 if (*d != sizeof(usb_standard_hid_descriptor_t)) {
117 usb_log_fatal("HID descriptor hass wrong size (%u, expected %u"
118 ")\n", *d, sizeof(usb_standard_hid_descriptor_t));
119 return EINVAL;
120 }
121
122 usb_standard_hid_descriptor_t *hid_desc =
123 (usb_standard_hid_descriptor_t *)d;
124
125 uint16_t length = hid_desc->report_desc_info.length;
126 size_t actual_size = 0;
127
128 /*
129 * Allocate space for the report descriptor.
130 */
131 hid_dev->report_desc = (uint8_t *)malloc(length);
132 if (hid_dev->report_desc == NULL) {
133 usb_log_fatal("Failed to allocate space for Report descriptor."
134 "\n");
135 return ENOMEM;
136 }
137
138 usb_log_debug("Getting Report descriptor, expected size: %u\n", length);
139
140 /*
141 * Get the descriptor from the device.
142 */
143 int rc = usb_request_get_descriptor(&hid_dev->ctrl_pipe,
144 USB_REQUEST_TYPE_STANDARD, USB_REQUEST_RECIPIENT_INTERFACE,
145 USB_DESCTYPE_HID_REPORT, 0,
146 hid_dev->iface, hid_dev->report_desc, length, &actual_size);
147
148 if (rc != EOK) {
149 return rc;
150 }
151
152 if (actual_size != length) {
153 free(hid_dev->report_desc);
154 hid_dev->report_desc = NULL;
155 usb_log_fatal("Report descriptor has wrong size (%u, expected "
156 "%u)\n", actual_size, length);
157 return EINVAL;
158 }
159
160 hid_dev->report_desc_size = length;
161
162 usb_log_debug("Done.\n");
163
164 return EOK;
165}
166
167/*----------------------------------------------------------------------------*/
168/**
169 * Retreives descriptors from the device, initializes pipes and stores
170 * important information from descriptors.
171 *
172 * Initializes the polling pipe described by the given endpoint description
173 * (@a poll_ep_desc).
174 *
175 * Information retreived from descriptors and stored in the HID device structure:
176 * - Assigned interface number (the interface controlled by this instance of
177 * the driver)
178 * - Polling interval (from the interface descriptor)
179 * - Report descriptor
180 *
181 * @param hid_dev HID device structure to be initialized.
182 * @param poll_ep_desc Description of the polling (Interrupt In) endpoint
183 * that has to be present in the device in order to
184 * successfuly initialize the structure.
185 *
186 * @sa usb_endpoint_pipe_initialize_from_configuration(),
187 * usbhid_dev_get_report_descriptor()
188 */
189static int usbhid_dev_process_descriptors(usbhid_dev_t *hid_dev,
190 usb_endpoint_description_t *poll_ep_desc)
191{
192 assert(hid_dev != NULL);
193
194 usb_log_info("Processing descriptors...\n");
195
196 int rc;
197
198 uint8_t *descriptors = NULL;
199 size_t descriptors_size;
200 rc = usb_request_get_full_configuration_descriptor_alloc(
201 &hid_dev->ctrl_pipe, 0, (void **) &descriptors, &descriptors_size);
202 if (rc != EOK) {
203 usb_log_error("Failed to retrieve config descriptor: %s.\n",
204 str_error(rc));
205 return rc;
206 }
207
208 /*
209 * Initialize the interrupt in endpoint.
210 */
211 usb_endpoint_mapping_t endpoint_mapping[1] = {
212 {
213 .pipe = &hid_dev->poll_pipe,
214 .description = poll_ep_desc,
215 .interface_no =
216 usb_device_get_assigned_interface(hid_dev->device)
217 }
218 };
219
220 rc = usb_endpoint_pipe_initialize_from_configuration(
221 endpoint_mapping, 1, descriptors, descriptors_size,
222 &hid_dev->wire);
223
224 if (rc != EOK) {
225 usb_log_error("Failed to initialize poll pipe: %s.\n",
226 str_error(rc));
227 free(descriptors);
228 return rc;
229 }
230
231 if (!endpoint_mapping[0].present) {
232 usb_log_warning("Not accepting device.\n");
233 free(descriptors);
234 return EREFUSED; // probably not very good return value
235 }
236
237 usb_log_debug("Accepted device. Saving interface, and getting Report"
238 " descriptor.\n");
239
240 /*
241 * Save assigned interface number.
242 */
243 if (endpoint_mapping[0].interface_no < 0) {
244 usb_log_error("Bad interface number.\n");
245 free(descriptors);
246 return EINVAL;
247 }
248
249 hid_dev->iface = endpoint_mapping[0].interface_no;
250
251 assert(endpoint_mapping[0].interface != NULL);
252
253 /*
254 * Save polling interval
255 */
256 hid_dev->poll_interval = endpoint_mapping[0].descriptor->poll_interval;
257 assert(hid_dev->poll_interval > 0);
258
259 rc = usbhid_dev_get_report_descriptor(hid_dev,
260 descriptors, descriptors_size,
261 (uint8_t *)endpoint_mapping[0].interface);
262
263 free(descriptors);
264
265 if (rc != EOK) {
266 usb_log_warning("Problem with getting Report descriptor: %s.\n",
267 str_error(rc));
268 return rc;
269 }
270
271 rc = usb_hid_parse_report_descriptor(hid_dev->parser,
272 hid_dev->report_desc, hid_dev->report_desc_size);
273 if (rc != EOK) {
274 usb_log_warning("Problem parsing Report descriptor: %s.\n",
275 str_error(rc));
276 return rc;
277 }
278
279 usb_hid_descriptor_print(hid_dev->parser);
280
281 return EOK;
282}
283
284/*----------------------------------------------------------------------------*/
285/* API functions */
286/*----------------------------------------------------------------------------*/
287/**
288 * Creates new uninitialized HID device structure.
289 *
290 * @return Pointer to the new HID device structure, or NULL if an error occured.
291 */
292usbhid_dev_t *usbhid_dev_new(void)
293{
294 usbhid_dev_t *dev =
295 (usbhid_dev_t *)malloc(sizeof(usbhid_dev_t));
296
297 if (dev == NULL) {
298 usb_log_fatal("No memory!\n");
299 return NULL;
300 }
301
302 memset(dev, 0, sizeof(usbhid_dev_t));
303
304 dev->parser = (usb_hid_report_parser_t *)(malloc(sizeof(
305 usb_hid_report_parser_t)));
306 if (dev->parser == NULL) {
307 usb_log_fatal("No memory!\n");
308 free(dev);
309 return NULL;
310 }
311
312 dev->initialized = 0;
313
314 return dev;
315}
316
317/*----------------------------------------------------------------------------*/
318/**
319 * Properly destroys the HID device structure.
320 *
321 * @note Currently does not clean-up the used pipes, as there are no functions
322 * offering such functionality.
323 *
324 * @param hid_dev Pointer to the structure to be destroyed.
325 */
326void usbhid_dev_free(usbhid_dev_t **hid_dev)
327{
328 if (hid_dev == NULL || *hid_dev == NULL) {
329 return;
330 }
331
332 // free the report descriptor
333 if ((*hid_dev)->report_desc != NULL) {
334 free((*hid_dev)->report_desc);
335 }
336 // destroy the parser
337 if ((*hid_dev)->parser != NULL) {
338 usb_hid_free_report_parser((*hid_dev)->parser);
339 }
340
341 // TODO: cleanup pipes
342
343 free(*hid_dev);
344 *hid_dev = NULL;
345}
346
347/*----------------------------------------------------------------------------*/
348/**
349 * Initializes HID device structure.
350 *
351 * @param hid_dev HID device structure to be initialized.
352 * @param dev DDF device representing the HID device.
353 * @param poll_ep_desc Description of the polling (Interrupt In) endpoint
354 * that has to be present in the device in order to
355 * successfuly initialize the structure.
356 *
357 * @retval EOK if successful.
358 * @retval EINVAL if some argument is missing.
359 * @return Other value inherited from one of functions
360 * usb_device_connection_initialize_from_device(),
361 * usb_endpoint_pipe_initialize_default_control(),
362 * usb_endpoint_pipe_start_session(), usb_endpoint_pipe_end_session(),
363 * usbhid_dev_process_descriptors().
364 *
365 * @sa usbhid_dev_process_descriptors()
366 */
367int usbhid_dev_init(usbhid_dev_t *hid_dev, ddf_dev_t *dev,
368 usb_endpoint_description_t *poll_ep_desc)
369{
370 usb_log_info("Initializing HID device structure.\n");
371
372 if (hid_dev == NULL) {
373 usb_log_error("Failed to init HID device structure: no "
374 "structure given.\n");
375 return EINVAL;
376 }
377
378 if (dev == NULL) {
379 usb_log_error("Failed to init HID device structure: no device"
380 " given.\n");
381 return EINVAL;
382 }
383
384 if (poll_ep_desc == NULL) {
385 usb_log_error("No poll endpoint description given.\n");
386 return EINVAL;
387 }
388
389 hid_dev->device = dev;
390
391 int rc;
392
393 /*
394 * Initialize the backing connection to the host controller.
395 */
396 rc = usb_device_connection_initialize_from_device(&hid_dev->wire, dev);
397 if (rc != EOK) {
398 usb_log_error("Problem initializing connection to device: %s."
399 "\n", str_error(rc));
400 return rc;
401 }
402
403 /*
404 * Initialize device pipes.
405 */
406 rc = usb_endpoint_pipe_initialize_default_control(&hid_dev->ctrl_pipe,
407 &hid_dev->wire);
408 if (rc != EOK) {
409 usb_log_error("Failed to initialize default control pipe: %s."
410 "\n", str_error(rc));
411 return rc;
412 }
413 rc = usb_endpoint_pipe_probe_default_control(&hid_dev->ctrl_pipe);
414 if (rc != EOK) {
415 usb_log_error("Probing default control pipe failed: %s.\n",
416 str_error(rc));
417 return rc;
418 }
419
420 /*
421 * Initialize the report parser.
422 */
423 rc = usb_hid_parser_init(hid_dev->parser);
424 if (rc != EOK) {
425 usb_log_error("Failed to initialize report parser.\n");
426 return rc;
427 }
428
429 /*
430 * Get descriptors, parse descriptors and save endpoints.
431 */
432 rc = usb_endpoint_pipe_start_session(&hid_dev->ctrl_pipe);
433 if (rc != EOK) {
434 usb_log_error("Failed to start session on the control pipe: %s"
435 ".\n", str_error(rc));
436 return rc;
437 }
438
439 rc = usbhid_dev_process_descriptors(hid_dev, poll_ep_desc);
440 if (rc != EOK) {
441 /* TODO: end session?? */
442 usb_endpoint_pipe_end_session(&hid_dev->ctrl_pipe);
443 usb_log_error("Failed to process descriptors: %s.\n",
444 str_error(rc));
445 return rc;
446 }
447
448 rc = usb_endpoint_pipe_end_session(&hid_dev->ctrl_pipe);
449 if (rc != EOK) {
450 usb_log_warning("Failed to start session on the control pipe: "
451 "%s.\n", str_error(rc));
452 return rc;
453 }
454
455 hid_dev->initialized = 1;
456 usb_log_info("HID device structure initialized.\n");
457
458 return EOK;
459}
460
461/**
462 * @}
463 */
Note: See TracBrowser for help on using the repository browser.