source: mainline/uspace/lib/usb/src/recognise.c@ 540e2d2

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 540e2d2 was 540e2d2, checked in by Vojtech Horky <vojtechhorky@…>, 14 years ago

Add creation of match ids from interface descriptor

  • Property mode set to 100644
File size: 12.3 KB
Line 
1/*
2 * Copyright (c) 2010 Vojtech Horky
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 libusb
30 * @{
31 */
32/** @file
33 * @brief Functions for recognising kind of attached devices.
34 */
35#include <sys/types.h>
36#include <usb_iface.h>
37#include <usb/usbdrv.h>
38#include <usb/pipes.h>
39#include <usb/recognise.h>
40#include <usb/request.h>
41#include <usb/classes/classes.h>
42#include <stdio.h>
43#include <errno.h>
44
45static size_t device_name_index = 0;
46static FIBRIL_MUTEX_INITIALIZE(device_name_index_mutex);
47
48/** Callback for getting host controller handle.
49 *
50 * @param dev Device in question.
51 * @param[out] handle Devman handle of the host controller.
52 * @return Error code.
53 */
54static int usb_iface_get_hc_handle(device_t *dev, devman_handle_t *handle)
55{
56 assert(dev);
57 assert(dev->parent != NULL);
58
59 device_t *parent = dev->parent;
60
61 if (parent->ops && parent->ops->interfaces[USB_DEV_IFACE]) {
62 usb_iface_t *usb_iface
63 = (usb_iface_t *) parent->ops->interfaces[USB_DEV_IFACE];
64 assert(usb_iface != NULL);
65 if (usb_iface->get_hc_handle) {
66 int rc = usb_iface->get_hc_handle(parent, handle);
67 return rc;
68 }
69 }
70
71 return ENOTSUP;
72}
73
74static usb_iface_t usb_iface = {
75 .get_hc_handle = usb_iface_get_hc_handle
76};
77
78device_ops_t child_ops = {
79 .interfaces[USB_DEV_IFACE] = &usb_iface
80};
81
82#define BCD_INT(a) (((unsigned int)(a)) / 256)
83#define BCD_FRAC(a) (((unsigned int)(a)) % 256)
84
85#define BCD_FMT "%x.%x"
86#define BCD_ARGS(a) BCD_INT((a)), BCD_FRAC((a))
87
88/* FIXME: make this dynamic */
89#define MATCH_STRING_MAX 256
90
91/** Add formatted match id.
92 *
93 * @param matches List of match ids where to add to.
94 * @param score Score of the match.
95 * @param format Printf-like format
96 * @return Error code.
97 */
98static int usb_add_match_id(match_id_list_t *matches, int score,
99 const char *format, ...)
100{
101 char *match_str = NULL;
102 match_id_t *match_id = NULL;
103 int rc;
104
105 match_str = malloc(MATCH_STRING_MAX + 1);
106 if (match_str == NULL) {
107 rc = ENOMEM;
108 goto failure;
109 }
110
111 /*
112 * FIXME: replace with dynamic allocation of exact size
113 */
114 va_list args;
115 va_start(args, format );
116 vsnprintf(match_str, MATCH_STRING_MAX, format, args);
117 match_str[MATCH_STRING_MAX] = 0;
118 va_end(args);
119
120 match_id = create_match_id();
121 if (match_id == NULL) {
122 rc = ENOMEM;
123 goto failure;
124 }
125
126 match_id->id = match_str;
127 match_id->score = score;
128 add_match_id(matches, match_id);
129
130 return EOK;
131
132failure:
133 if (match_str != NULL) {
134 free(match_str);
135 }
136 if (match_id != NULL) {
137 match_id->id = NULL;
138 delete_match_id(match_id);
139 }
140
141 return rc;
142}
143
144/** Create device match ids based on its interface.
145 *
146 * @param[in] descriptor Interface descriptor.
147 * @param[out] matches Initialized list of match ids.
148 * @return Error code (the two mentioned are not the only ones).
149 * @retval EINVAL Invalid input parameters (expects non NULL pointers).
150 * @retval ENOENT Interface does not specify class.
151 */
152int usb_device_create_match_ids_from_interface(
153 const usb_standard_interface_descriptor_t *descriptor,
154 match_id_list_t *matches)
155{
156 if (descriptor == NULL) {
157 return EINVAL;
158 }
159 if (matches == NULL) {
160 return EINVAL;
161 }
162
163 int rc;
164
165 if (descriptor->interface_class == USB_CLASS_USE_INTERFACE) {
166 return ENOENT;
167 }
168
169 const char *classname = usb_str_class(descriptor->interface_class);
170 assert(classname != NULL);
171
172 rc = usb_add_match_id(matches, 50,
173 "usb&interface&class=%s",
174 classname);
175 if (rc != EOK) {
176 return rc;
177 }
178
179 rc = usb_add_match_id(matches, 70,
180 "usb&interface&class=%s&subclass=0x%02x",
181 classname, descriptor->interface_subclass);
182 if (rc != EOK) {
183 return rc;
184 }
185
186 rc = usb_add_match_id(matches, 100,
187 "usb&interface&class=%s&subclass=0x%02x&protocol=0x%02x",
188 classname, descriptor->interface_subclass,
189 descriptor->interface_protocol);
190
191 return rc;
192}
193
194/** Create DDF match ids from USB device descriptor.
195 *
196 * @param matches List of match ids to extend.
197 * @param device_descriptor Device descriptor returned by given device.
198 * @return Error code.
199 */
200int usb_drv_create_match_ids_from_device_descriptor(
201 match_id_list_t *matches,
202 const usb_standard_device_descriptor_t *device_descriptor)
203{
204 int rc;
205
206 /*
207 * Unless the vendor id is 0, the pair idVendor-idProduct
208 * quite uniquely describes the device.
209 */
210 if (device_descriptor->vendor_id != 0) {
211 /* First, with release number. */
212 rc = usb_add_match_id(matches, 100,
213 "usb&vendor=0x%04x&product=0x%04x&release=" BCD_FMT,
214 (int) device_descriptor->vendor_id,
215 (int) device_descriptor->product_id,
216 BCD_ARGS(device_descriptor->device_version));
217 if (rc != EOK) {
218 return rc;
219 }
220
221 /* Next, without release number. */
222 rc = usb_add_match_id(matches, 90,
223 "usb&vendor=0x%04x&product=0x%04x",
224 (int) device_descriptor->vendor_id,
225 (int) device_descriptor->product_id);
226 if (rc != EOK) {
227 return rc;
228 }
229 }
230
231 /*
232 * If the device class points to interface we skip adding
233 * class directly.
234 */
235 if (device_descriptor->device_class != USB_CLASS_USE_INTERFACE) {
236 rc = usb_add_match_id(matches, 50, "usb&class=%s",
237 usb_str_class(device_descriptor->device_class));
238 if (rc != EOK) {
239 return rc;
240 }
241 }
242
243 return EOK;
244}
245
246/** Create DDF match ids from USB configuration descriptor.
247 * The configuration descriptor is expected to be in the complete form,
248 * i.e. including interface, endpoint etc. descriptors.
249 *
250 * @param matches List of match ids to extend.
251 * @param config_descriptor Configuration descriptor returned by given device.
252 * @param total_size Size of the @p config_descriptor.
253 * @return Error code.
254 */
255int usb_drv_create_match_ids_from_configuration_descriptor(
256 match_id_list_t *matches,
257 const void *config_descriptor, size_t total_size)
258{
259 /*
260 * Iterate through config descriptor to find the interface
261 * descriptors.
262 */
263 size_t position = sizeof(usb_standard_configuration_descriptor_t);
264 while (position + 1 < total_size) {
265 uint8_t *current_descriptor
266 = ((uint8_t *) config_descriptor) + position;
267 uint8_t cur_descr_len = current_descriptor[0];
268 uint8_t cur_descr_type = current_descriptor[1];
269
270 if (cur_descr_len == 0) {
271 return ENOENT;
272 }
273
274 position += cur_descr_len;
275
276 if (cur_descr_type != USB_DESCTYPE_INTERFACE) {
277 continue;
278 }
279
280 /*
281 * Finally, we found an interface descriptor.
282 */
283 usb_standard_interface_descriptor_t *interface
284 = (usb_standard_interface_descriptor_t *)
285 current_descriptor;
286
287 int rc = usb_add_match_id(matches, 50,
288 "usb&interface&class=%s",
289 usb_str_class(interface->interface_class));
290 if (rc != EOK) {
291 return rc;
292 }
293 }
294
295 return EOK;
296}
297
298/** Add match ids based on configuration descriptor.
299 *
300 * @param pipe Control pipe to the device.
301 * @param matches Match ids list to add matches to.
302 * @param config_count Number of configurations the device has.
303 * @return Error code.
304 */
305static int usb_add_config_descriptor_match_ids(usb_endpoint_pipe_t *pipe,
306 match_id_list_t *matches, int config_count)
307{
308 int final_rc = EOK;
309
310 int config_index;
311 for (config_index = 0; config_index < config_count; config_index++) {
312 int rc;
313 usb_standard_configuration_descriptor_t config_descriptor;
314 rc = usb_request_get_bare_configuration_descriptor(pipe,
315 config_index, &config_descriptor);
316 if (rc != EOK) {
317 final_rc = rc;
318 continue;
319 }
320
321 size_t full_config_descriptor_size;
322 void *full_config_descriptor
323 = malloc(config_descriptor.total_length);
324 rc = usb_request_get_full_configuration_descriptor(pipe,
325 config_index,
326 full_config_descriptor, config_descriptor.total_length,
327 &full_config_descriptor_size);
328 if (rc != EOK) {
329 final_rc = rc;
330 continue;
331 }
332 if (full_config_descriptor_size
333 != config_descriptor.total_length) {
334 final_rc = ERANGE;
335 continue;
336 }
337
338 rc = usb_drv_create_match_ids_from_configuration_descriptor(
339 matches,
340 full_config_descriptor, full_config_descriptor_size);
341 if (rc != EOK) {
342 final_rc = rc;
343 continue;
344 }
345
346 }
347
348 return final_rc;
349}
350
351/** Create match ids describing attached device.
352 *
353 * @warning The list of match ids @p matches may change even when
354 * function exits with error.
355 *
356 * @param ctrl_pipe Control pipe to given device (session must be already
357 * started).
358 * @param matches Initialized list of match ids.
359 * @return Error code.
360 */
361int usb_device_create_match_ids(usb_endpoint_pipe_t *ctrl_pipe,
362 match_id_list_t *matches)
363{
364 int rc;
365 /*
366 * Retrieve device descriptor and add matches from it.
367 */
368 usb_standard_device_descriptor_t device_descriptor;
369
370 rc = usb_request_get_device_descriptor(ctrl_pipe, &device_descriptor);
371 if (rc != EOK) {
372 return rc;
373 }
374
375 rc = usb_drv_create_match_ids_from_device_descriptor(matches,
376 &device_descriptor);
377 if (rc != EOK) {
378 return rc;
379 }
380
381 /*
382 * Go through all configurations and add matches
383 * based on interface class.
384 */
385 rc = usb_add_config_descriptor_match_ids(ctrl_pipe, matches,
386 device_descriptor.configuration_count);
387 if (rc != EOK) {
388 return rc;
389 }
390
391 /*
392 * As a fallback, provide the simplest match id possible.
393 */
394 rc = usb_add_match_id(matches, 1, "usb&fallback");
395 if (rc != EOK) {
396 return rc;
397 }
398
399 return EOK;
400}
401
402/** Probe for device kind and register it in devman.
403 *
404 * @param[in] address Address of the (unknown) attached device.
405 * @param[in] hc_handle Handle of the host controller.
406 * @param[in] parent Parent device.
407 * @param[out] child_handle Handle of the child device.
408 * @return Error code.
409 */
410int usb_device_register_child_in_devman(usb_address_t address,
411 devman_handle_t hc_handle,
412 device_t *parent, devman_handle_t *child_handle)
413{
414 size_t this_device_name_index;
415
416 fibril_mutex_lock(&device_name_index_mutex);
417 this_device_name_index = device_name_index;
418 device_name_index++;
419 fibril_mutex_unlock(&device_name_index_mutex);
420
421 device_t *child = NULL;
422 char *child_name = NULL;
423 int rc;
424 usb_device_connection_t dev_connection;
425 usb_endpoint_pipe_t ctrl_pipe;
426
427 rc = usb_device_connection_initialize(&dev_connection, hc_handle, address);
428 if (rc != EOK) {
429 goto failure;
430 }
431
432 rc = usb_endpoint_pipe_initialize_default_control(&ctrl_pipe,
433 &dev_connection);
434 if (rc != EOK) {
435 goto failure;
436 }
437
438 child = create_device();
439 if (child == NULL) {
440 rc = ENOMEM;
441 goto failure;
442 }
443
444 /*
445 * TODO: Once the device driver framework support persistent
446 * naming etc., something more descriptive could be created.
447 */
448 rc = asprintf(&child_name, "usbdev%02zu", this_device_name_index);
449 if (rc < 0) {
450 goto failure;
451 }
452 child->parent = parent;
453 child->name = child_name;
454 child->ops = &child_ops;
455
456 rc = usb_endpoint_pipe_start_session(&ctrl_pipe);
457 if (rc != EOK) {
458 goto failure;
459 }
460
461 rc = usb_device_create_match_ids(&ctrl_pipe, &child->match_ids);
462 if (rc != EOK) {
463 goto failure;
464 }
465
466 rc = usb_endpoint_pipe_end_session(&ctrl_pipe);
467 if (rc != EOK) {
468 goto failure;
469 }
470
471 rc = child_device_register(child, parent);
472 if (rc != EOK) {
473 goto failure;
474 }
475
476 if (child_handle != NULL) {
477 *child_handle = child->handle;
478 }
479
480 return EOK;
481
482failure:
483 if (child != NULL) {
484 child->name = NULL;
485 /* This takes care of match_id deallocation as well. */
486 delete_device(child);
487 }
488 if (child_name != NULL) {
489 free(child_name);
490 }
491
492 return rc;
493}
494
495
496/**
497 * @}
498 */
Note: See TracBrowser for help on using the repository browser.