source: mainline/uspace/drv/usbhid/hiddev.c@ 77ab674

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

Comments to hiddev, added some return value checks.

  • Property mode set to 100644
File size: 12.1 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 usb_log_debug("Done.\n");
161
162 return EOK;
163}
164
165/*----------------------------------------------------------------------------*/
166/**
167 * Retreives descriptors from the device, initializes pipes and stores
168 * important information from descriptors.
169 *
170 * Initializes the polling pipe described by the given endpoint description
171 * (@a poll_ep_desc).
172 *
173 * Information retreived from descriptors and stored in the HID device structure:
174 * - Assigned interface number (the interface controlled by this instance of
175 * the driver)
176 * - Polling interval (from the interface descriptor)
177 * - Report descriptor
178 *
179 * @param hid_dev HID device structure to be initialized.
180 * @param poll_ep_desc Description of the polling (Interrupt In) endpoint
181 * that has to be present in the device in order to
182 * successfuly initialize the structure.
183 *
184 * @sa usb_endpoint_pipe_initialize_from_configuration(),
185 * usbhid_dev_get_report_descriptor()
186 */
187static int usbhid_dev_process_descriptors(usbhid_dev_t *hid_dev,
188 usb_endpoint_description_t *poll_ep_desc)
189{
190 assert(hid_dev != NULL);
191
192 usb_log_info("Processing descriptors...\n");
193
194 int rc;
195
196 uint8_t *descriptors = NULL;
197 size_t descriptors_size;
198 rc = usb_request_get_full_configuration_descriptor_alloc(
199 &hid_dev->ctrl_pipe, 0, (void **) &descriptors, &descriptors_size);
200 if (rc != EOK) {
201 usb_log_error("Failed to retrieve config descriptor: %s.\n",
202 str_error(rc));
203 return rc;
204 }
205
206 /*
207 * Initialize the interrupt in endpoint.
208 */
209 usb_endpoint_mapping_t endpoint_mapping[1] = {
210 {
211 .pipe = &hid_dev->poll_pipe,
212 .description = poll_ep_desc,
213 .interface_no =
214 usb_device_get_assigned_interface(hid_dev->device)
215 }
216 };
217
218 rc = usb_endpoint_pipe_initialize_from_configuration(
219 endpoint_mapping, 1, descriptors, descriptors_size,
220 &hid_dev->wire);
221
222 if (rc != EOK) {
223 usb_log_error("Failed to initialize poll pipe: %s.\n",
224 str_error(rc));
225 free(descriptors);
226 return rc;
227 }
228
229 if (!endpoint_mapping[0].present) {
230 usb_log_warning("Not accepting device.\n");
231 free(descriptors);
232 return EREFUSED; // probably not very good return value
233 }
234
235 usb_log_debug("Accepted device. Saving interface, and getting Report"
236 " descriptor.\n");
237
238 /*
239 * Save assigned interface number.
240 */
241 if (endpoint_mapping[0].interface_no < 0) {
242 usb_log_error("Bad interface number.\n");
243 free(descriptors);
244 return EINVAL;
245 }
246
247 hid_dev->iface = endpoint_mapping[0].interface_no;
248
249 assert(endpoint_mapping[0].interface != NULL);
250
251 /*
252 * Save polling interval
253 */
254 hid_dev->poll_interval = endpoint_mapping[0].descriptor->poll_interval;
255 assert(hid_dev->poll_interval > 0);
256
257 rc = usbhid_dev_get_report_descriptor(hid_dev,
258 descriptors, descriptors_size,
259 (uint8_t *)endpoint_mapping[0].interface);
260
261 free(descriptors);
262
263 if (rc != EOK) {
264 usb_log_warning("Problem with parsing Report descriptor: %s.\n",
265 str_error(rc));
266 return rc;
267 }
268
269 return EOK;
270}
271
272/*----------------------------------------------------------------------------*/
273/* API functions */
274/*----------------------------------------------------------------------------*/
275/**
276 * Creates new uninitialized HID device structure.
277 *
278 * @return Pointer to the new HID device structure, or NULL if an error occured.
279 */
280usbhid_dev_t *usbhid_dev_new(void)
281{
282 usbhid_dev_t *dev =
283 (usbhid_dev_t *)malloc(sizeof(usbhid_dev_t));
284
285 if (dev == NULL) {
286 usb_log_fatal("No memory!\n");
287 return NULL;
288 }
289
290 memset(dev, 0, sizeof(usbhid_dev_t));
291
292 dev->initialized = 0;
293
294 return dev;
295}
296
297/*----------------------------------------------------------------------------*/
298/**
299 * Properly destroys the HID device structure.
300 *
301 * @note Currently does not clean-up the used pipes, as there are no functions
302 * offering such functionality.
303 *
304 * @param hid_dev Pointer to the structure to be destroyed.
305 */
306void usbhid_dev_free(usbhid_dev_t **hid_dev)
307{
308 if (hid_dev == NULL || *hid_dev == NULL) {
309 return;
310 }
311
312 // free the report descriptor
313 if ((*hid_dev)->report_desc != NULL) {
314 free((*hid_dev)->report_desc);
315 }
316 // destroy the parser
317 if ((*hid_dev)->parser != NULL) {
318 usb_hid_free_report_parser((*hid_dev)->parser);
319 }
320
321 // TODO: cleanup pipes
322
323 free(*hid_dev);
324 *hid_dev = NULL;
325}
326
327/*----------------------------------------------------------------------------*/
328/**
329 * Initializes HID device structure.
330 *
331 * @param hid_dev HID device structure to be initialized.
332 * @param dev DDF device representing the HID device.
333 * @param poll_ep_desc Description of the polling (Interrupt In) endpoint
334 * that has to be present in the device in order to
335 * successfuly initialize the structure.
336 *
337 * @retval EOK if successful.
338 * @retval EINVAL if some argument is missing.
339 * @return Other value inherited from one of functions
340 * usb_device_connection_initialize_from_device(),
341 * usb_endpoint_pipe_initialize_default_control(),
342 * usb_endpoint_pipe_start_session(), usb_endpoint_pipe_end_session(),
343 * usbhid_dev_process_descriptors().
344 *
345 * @sa usbhid_dev_process_descriptors()
346 */
347int usbhid_dev_init(usbhid_dev_t *hid_dev, ddf_dev_t *dev,
348 usb_endpoint_description_t *poll_ep_desc)
349{
350 usb_log_info("Initializing HID device structure.\n");
351
352 if (hid_dev == NULL) {
353 usb_log_error("Failed to init HID device structure: no "
354 "structure given.\n");
355 return EINVAL;
356 }
357
358 if (dev == NULL) {
359 usb_log_error("Failed to init HID device structure: no device"
360 " given.\n");
361 return EINVAL;
362 }
363
364 if (poll_ep_desc == NULL) {
365 usb_log_error("No poll endpoint description given.\n");
366 return EINVAL;
367 }
368
369 hid_dev->device = dev;
370
371 int rc;
372
373 /*
374 * Initialize the backing connection to the host controller.
375 */
376 rc = usb_device_connection_initialize_from_device(&hid_dev->wire, dev);
377 if (rc != EOK) {
378 usb_log_error("Problem initializing connection to device: %s."
379 "\n", str_error(rc));
380 return rc;
381 }
382
383 /*
384 * Initialize device pipes.
385 */
386 rc = usb_endpoint_pipe_initialize_default_control(&hid_dev->ctrl_pipe,
387 &hid_dev->wire);
388 if (rc != EOK) {
389 usb_log_error("Failed to initialize default control pipe: %s."
390 "\n", str_error(rc));
391 return rc;
392 }
393
394 /*
395 * Get descriptors, parse descriptors and save endpoints.
396 */
397 rc = usb_endpoint_pipe_start_session(&hid_dev->ctrl_pipe);
398 if (rc != EOK) {
399 usb_log_error("Failed to start session on the control pipe: %s"
400 ".\n", str_error(rc));
401 return rc;
402 }
403
404 rc = usbhid_dev_process_descriptors(hid_dev, poll_ep_desc);
405 if (rc != EOK) {
406 /* TODO: end session?? */
407 usb_log_error("Failed to process descriptors: %s.\n",
408 str_error(rc));
409 return rc;
410 }
411
412 rc = usb_endpoint_pipe_end_session(&hid_dev->ctrl_pipe);
413 if (rc != EOK) {
414 usb_log_warning("Failed to start session on the control pipe: "
415 "%s.\n", str_error(rc));
416 return rc;
417 }
418
419 hid_dev->initialized = 1;
420 usb_log_info("HID device structure initialized.\n");
421
422 return EOK;
423}
424
425/**
426 * @}
427 */
Note: See TracBrowser for help on using the repository browser.