source: mainline/uspace/lib/usb/src/recognise.c@ 17aca1c

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

Merge mainline changes

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