source: mainline/uspace/drv/usbhid/hiddev.c@ 98807e16

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

Using polling interval from endpoint descriptor

  • Property mode set to 100644
File size: 8.8 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
56static int usbhid_dev_get_report_descriptor(usbhid_dev_t *hid_dev,
57 uint8_t *config_desc, size_t config_desc_size, uint8_t *iface_desc)
58{
59 assert(hid_dev != NULL);
60 assert(config_desc != NULL);
61 assert(config_desc_size != 0);
62 assert(iface_desc != NULL);
63
64 usb_dp_parser_t parser = {
65 .nesting = usb_dp_standard_descriptor_nesting
66 };
67
68 usb_dp_parser_data_t parser_data = {
69 .data = config_desc,
70 .size = config_desc_size,
71 .arg = NULL
72 };
73
74 /*
75 * First nested descriptor of interface descriptor.
76 */
77 uint8_t *d =
78 usb_dp_get_nested_descriptor(&parser, &parser_data, iface_desc);
79
80 /*
81 * Search through siblings until the HID descriptor is found.
82 */
83 while (d != NULL && *(d + 1) != USB_DESCTYPE_HID) {
84 d = usb_dp_get_sibling_descriptor(&parser, &parser_data,
85 iface_desc, d);
86 }
87
88 if (d == NULL) {
89 usb_log_fatal("No HID descriptor found!\n");
90 return ENOENT;
91 }
92
93 if (*d != sizeof(usb_standard_hid_descriptor_t)) {
94 usb_log_fatal("HID descriptor hass wrong size (%u, expected %u"
95 ")\n", *d, sizeof(usb_standard_hid_descriptor_t));
96 return EINVAL;
97 }
98
99 usb_standard_hid_descriptor_t *hid_desc =
100 (usb_standard_hid_descriptor_t *)d;
101
102 uint16_t length = hid_desc->report_desc_info.length;
103 size_t actual_size = 0;
104
105 /*
106 * Allocate space for the report descriptor.
107 */
108 hid_dev->report_desc = (uint8_t *)malloc(length);
109 if (hid_dev->report_desc == NULL) {
110 usb_log_fatal("Failed to allocate space for Report descriptor."
111 "\n");
112 return ENOMEM;
113 }
114
115 usb_log_debug("Getting Report descriptor, expected size: %u\n", length);
116
117 /*
118 * Get the descriptor from the device.
119 */
120 int rc = usb_request_get_descriptor(&hid_dev->ctrl_pipe,
121 USB_REQUEST_TYPE_STANDARD, USB_REQUEST_RECIPIENT_INTERFACE,
122 USB_DESCTYPE_HID_REPORT, 0,
123 hid_dev->iface, hid_dev->report_desc, length, &actual_size);
124
125 if (rc != EOK) {
126 return rc;
127 }
128
129 if (actual_size != length) {
130 free(hid_dev->report_desc);
131 hid_dev->report_desc = NULL;
132 usb_log_fatal("Report descriptor has wrong size (%u, expected "
133 "%u)\n", actual_size, length);
134 return EINVAL;
135 }
136
137 usb_log_debug("Done.\n");
138
139 return EOK;
140}
141
142/*----------------------------------------------------------------------------*/
143
144static int usbhid_dev_process_descriptors(usbhid_dev_t *hid_dev,
145 usb_endpoint_description_t *poll_ep_desc)
146{
147 assert(hid_dev != NULL);
148
149 usb_log_info("Processing descriptors...\n");
150
151 int rc;
152
153 uint8_t *descriptors = NULL;
154 size_t descriptors_size;
155 rc = usb_request_get_full_configuration_descriptor_alloc(
156 &hid_dev->ctrl_pipe, 0, (void **) &descriptors, &descriptors_size);
157 if (rc != EOK) {
158 usb_log_error("Failed to retrieve config descriptor: %s.\n",
159 str_error(rc));
160 return rc;
161 }
162
163 /*
164 * Initialize the interrupt in endpoint.
165 */
166 usb_endpoint_mapping_t endpoint_mapping[1] = {
167 {
168 .pipe = &hid_dev->poll_pipe,
169 .description = poll_ep_desc,
170 .interface_no =
171 usb_device_get_assigned_interface(hid_dev->device)
172 }
173 };
174
175 rc = usb_endpoint_pipe_initialize_from_configuration(
176 endpoint_mapping, 1, descriptors, descriptors_size,
177 &hid_dev->wire);
178
179 if (rc != EOK) {
180 usb_log_error("Failed to initialize poll pipe: %s.\n",
181 str_error(rc));
182 free(descriptors);
183 return rc;
184 }
185
186 if (!endpoint_mapping[0].present) {
187 usb_log_warning("Not accepting device.\n");
188 free(descriptors);
189 return EREFUSED; // probably not very good return value
190 }
191
192 usb_log_debug("Accepted device. Saving interface, and getting Report"
193 " descriptor.\n");
194
195 /*
196 * Save assigned interface number.
197 */
198 if (endpoint_mapping[0].interface_no < 0) {
199 usb_log_error("Bad interface number.\n");
200 free(descriptors);
201 return EINVAL;
202 }
203
204 hid_dev->iface = endpoint_mapping[0].interface_no;
205
206 assert(endpoint_mapping[0].interface != NULL);
207
208 /*
209 * Save polling interval
210 */
211 hid_dev->poll_interval = endpoint_mapping[0].descriptor->poll_interval;
212 assert(hid_dev->poll_interval > 0);
213
214 rc = usbhid_dev_get_report_descriptor(hid_dev,
215 descriptors, descriptors_size,
216 (uint8_t *)endpoint_mapping[0].interface);
217
218 free(descriptors);
219
220 if (rc != EOK) {
221 usb_log_warning("Problem with parsing Report descriptor: %s.\n",
222 str_error(rc));
223 return rc;
224 }
225
226 return EOK;
227}
228
229/*----------------------------------------------------------------------------*/
230/* API functions */
231/*----------------------------------------------------------------------------*/
232
233usbhid_dev_t *usbhid_dev_new(void)
234{
235 usbhid_dev_t *dev =
236 (usbhid_dev_t *)malloc(sizeof(usbhid_dev_t));
237
238 if (dev == NULL) {
239 usb_log_fatal("No memory!\n");
240 return NULL;
241 }
242
243 memset(dev, 0, sizeof(usbhid_dev_t));
244
245 dev->initialized = 0;
246
247 return dev;
248}
249
250/*----------------------------------------------------------------------------*/
251
252void usbhid_dev_free(usbhid_dev_t **hid_dev)
253{
254 if (hid_dev == NULL || *hid_dev == NULL) {
255 return;
256 }
257
258 // free the report descriptor
259 if ((*hid_dev)->report_desc != NULL) {
260 free((*hid_dev)->report_desc);
261 }
262 // destroy the parser
263 if ((*hid_dev)->parser != NULL) {
264 usb_hid_free_report_parser((*hid_dev)->parser);
265 }
266
267 // TODO: cleanup pipes
268
269 free(*hid_dev);
270 *hid_dev = NULL;
271}
272
273/*----------------------------------------------------------------------------*/
274
275int usbhid_dev_init(usbhid_dev_t *hid_dev, ddf_dev_t *dev,
276 usb_endpoint_description_t *poll_ep_desc)
277{
278 usb_log_info("Initializing HID device structure.\n");
279
280 if (hid_dev == NULL) {
281 usb_log_error("Failed to init HID device structure: no "
282 "structure given.\n");
283 return EINVAL;
284 }
285
286 if (dev == NULL) {
287 usb_log_error("Failed to init HID device structure: no device"
288 " given.\n");
289 return EINVAL;
290 }
291
292 if (poll_ep_desc == NULL) {
293 usb_log_error("No poll endpoint description given.\n");
294 return EINVAL;
295 }
296
297 hid_dev->device = dev;
298
299 int rc;
300
301 /*
302 * Initialize the backing connection to the host controller.
303 */
304 rc = usb_device_connection_initialize_from_device(&hid_dev->wire, dev);
305 if (rc != EOK) {
306 usb_log_error("Problem initializing connection to device: %s."
307 "\n", str_error(rc));
308 return rc;
309 }
310
311 /*
312 * Initialize device pipes.
313 */
314 rc = usb_endpoint_pipe_initialize_default_control(&hid_dev->ctrl_pipe,
315 &hid_dev->wire);
316 if (rc != EOK) {
317 usb_log_error("Failed to initialize default control pipe: %s."
318 "\n", str_error(rc));
319 return rc;
320 }
321
322 /*
323 * Get descriptors, parse descriptors and save endpoints.
324 */
325 usb_endpoint_pipe_start_session(&hid_dev->ctrl_pipe);
326
327 rc = usbhid_dev_process_descriptors(hid_dev, poll_ep_desc);
328
329 usb_endpoint_pipe_end_session(&hid_dev->ctrl_pipe);
330 if (rc != EOK) {
331 usb_log_error("Failed to process descriptors: %s.\n",
332 str_error(rc));
333 return rc;
334 }
335
336 hid_dev->initialized = 1;
337 usb_log_info("HID device structure initialized.\n");
338
339 return EOK;
340}
341
342/**
343 * @}
344 */
Note: See TracBrowser for help on using the repository browser.