source: mainline/uspace/lib/usbdev/src/recognise.c@ 67f55e7b

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 67f55e7b was 59c163c, checked in by Jan Vesely <jano.vesely@…>, 14 years ago

libusbdev: Automatically create attached device info for new devices.

  • Property mode set to 100644
File size: 13.0 KB
RevLine 
[02ccfcd]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
[160b75e]29/** @addtogroup libusbdev
[02ccfcd]30 * @{
31 */
32/** @file
[a6add7a]33 * Functions for recognition of attached devices.
[02ccfcd]34 */
[17aca1c]35#include <sys/types.h>
[eb1a2f4]36#include <fibril_synch.h>
[59c163c]37#include <usb/debug.h>
38#include <usb/dev/hub.h>
[7d521e24]39#include <usb/dev/pipes.h>
40#include <usb/dev/recognise.h>
[357a302]41#include <usb/ddfiface.h>
[7d521e24]42#include <usb/dev/request.h>
[02ccfcd]43#include <usb/classes/classes.h>
44#include <stdio.h>
45#include <errno.h>
[eb1a2f4]46#include <assert.h>
[02ccfcd]47
[a6add7a]48/** Index to append after device name for uniqueness. */
[4e8e1f5]49static size_t device_name_index = 0;
[a6add7a]50/** Mutex guard for device_name_index. */
[4e8e1f5]51static FIBRIL_MUTEX_INITIALIZE(device_name_index_mutex);
52
[a6add7a]53/** DDF operations of child devices. */
[eea3e39]54static ddf_dev_ops_t child_ops = {
[357a302]55 .interfaces[USB_DEV_IFACE] = &usb_iface_hub_child_impl
[71ed4849]56};
[02ccfcd]57
[a6add7a]58/** Get integer part from BCD coded number. */
[02ccfcd]59#define BCD_INT(a) (((unsigned int)(a)) / 256)
[a6add7a]60/** Get fraction part from BCD coded number (as an integer, no less). */
[02ccfcd]61#define BCD_FRAC(a) (((unsigned int)(a)) % 256)
62
[a6add7a]63/** Format for BCD coded number to be used in printf. */
[02ccfcd]64#define BCD_FMT "%x.%x"
[a6add7a]65/** Arguments to printf for BCD coded number. */
[02ccfcd]66#define BCD_ARGS(a) BCD_INT((a)), BCD_FRAC((a))
67
68/* FIXME: make this dynamic */
69#define MATCH_STRING_MAX 256
70
71/** Add formatted match id.
72 *
73 * @param matches List of match ids where to add to.
74 * @param score Score of the match.
75 * @param format Printf-like format
76 * @return Error code.
77 */
78static int usb_add_match_id(match_id_list_t *matches, int score,
79 const char *format, ...)
80{
81 char *match_str = NULL;
82 match_id_t *match_id = NULL;
83 int rc;
84
85 match_str = malloc(MATCH_STRING_MAX + 1);
86 if (match_str == NULL) {
87 rc = ENOMEM;
88 goto failure;
89 }
90
91 /*
92 * FIXME: replace with dynamic allocation of exact size
93 */
94 va_list args;
95 va_start(args, format );
96 vsnprintf(match_str, MATCH_STRING_MAX, format, args);
97 match_str[MATCH_STRING_MAX] = 0;
98 va_end(args);
99
100 match_id = create_match_id();
101 if (match_id == NULL) {
102 rc = ENOMEM;
103 goto failure;
104 }
105
106 match_id->id = match_str;
107 match_id->score = score;
108 add_match_id(matches, match_id);
109
110 return EOK;
111
112failure:
113 if (match_str != NULL) {
114 free(match_str);
115 }
116 if (match_id != NULL) {
117 match_id->id = NULL;
118 delete_match_id(match_id);
119 }
120
121 return rc;
122}
123
[a6add7a]124/** Add match id to list or return with error code.
125 *
126 * @param match_ids List of match ids.
127 * @param score Match id score.
128 * @param format Format of the matching string
129 * @param ... Arguments for the format.
130 */
[6e6dc7d]131#define ADD_MATCHID_OR_RETURN(match_ids, score, format, ...) \
132 do { \
133 int __rc = usb_add_match_id((match_ids), (score), \
134 format, ##__VA_ARGS__); \
135 if (__rc != EOK) { \
136 return __rc; \
137 } \
138 } while (0)
139
[540e2d2]140/** Create device match ids based on its interface.
141 *
[bc1c6fb]142 * @param[in] desc_device Device descriptor.
143 * @param[in] desc_interface Interface descriptor.
[540e2d2]144 * @param[out] matches Initialized list of match ids.
145 * @return Error code (the two mentioned are not the only ones).
146 * @retval EINVAL Invalid input parameters (expects non NULL pointers).
[bc1c6fb]147 * @retval ENOENT Device class is not "use interface".
[540e2d2]148 */
149int usb_device_create_match_ids_from_interface(
[51f0e410]150 const usb_standard_device_descriptor_t *desc_device,
151 const usb_standard_interface_descriptor_t *desc_interface,
[540e2d2]152 match_id_list_t *matches)
153{
[51f0e410]154 if (desc_interface == NULL) {
[540e2d2]155 return EINVAL;
156 }
157 if (matches == NULL) {
158 return EINVAL;
159 }
160
[51f0e410]161 if (desc_interface->interface_class == USB_CLASS_USE_INTERFACE) {
[540e2d2]162 return ENOENT;
163 }
164
[51f0e410]165 const char *classname = usb_str_class(desc_interface->interface_class);
[540e2d2]166 assert(classname != NULL);
167
[51f0e410]168#define IFACE_PROTOCOL_FMT "interface&class=%s&subclass=0x%02x&protocol=0x%02x"
169#define IFACE_PROTOCOL_ARGS classname, desc_interface->interface_subclass, \
170 desc_interface->interface_protocol
171
172#define IFACE_SUBCLASS_FMT "interface&class=%s&subclass=0x%02x"
173#define IFACE_SUBCLASS_ARGS classname, desc_interface->interface_subclass
174
175#define IFACE_CLASS_FMT "interface&class=%s"
176#define IFACE_CLASS_ARGS classname
177
178#define VENDOR_RELEASE_FMT "vendor=0x%04x&product=0x%04x&release=" BCD_FMT
179#define VENDOR_RELEASE_ARGS desc_device->vendor_id, desc_device->product_id, \
180 BCD_ARGS(desc_device->device_version)
181
182#define VENDOR_PRODUCT_FMT "vendor=0x%04x&product=0x%04x"
183#define VENDOR_PRODUCT_ARGS desc_device->vendor_id, desc_device->product_id
184
185#define VENDOR_ONLY_FMT "vendor=0x%04x"
186#define VENDOR_ONLY_ARGS desc_device->vendor_id
187
188 /*
189 * If the vendor is specified, create match ids with vendor with
190 * higher score.
191 * Then the same ones without the vendor part.
192 */
193 if ((desc_device != NULL) && (desc_device->vendor_id != 0)) {
194 /* First, interface matches with device release number. */
195 ADD_MATCHID_OR_RETURN(matches, 250,
196 "usb&" VENDOR_RELEASE_FMT "&" IFACE_PROTOCOL_FMT,
197 VENDOR_RELEASE_ARGS, IFACE_PROTOCOL_ARGS);
198 ADD_MATCHID_OR_RETURN(matches, 240,
199 "usb&" VENDOR_RELEASE_FMT "&" IFACE_SUBCLASS_FMT,
200 VENDOR_RELEASE_ARGS, IFACE_SUBCLASS_ARGS);
201 ADD_MATCHID_OR_RETURN(matches, 230,
202 "usb&" VENDOR_RELEASE_FMT "&" IFACE_CLASS_FMT,
203 VENDOR_RELEASE_ARGS, IFACE_CLASS_ARGS);
204
205 /* Next, interface matches without release number. */
206 ADD_MATCHID_OR_RETURN(matches, 220,
207 "usb&" VENDOR_PRODUCT_FMT "&" IFACE_PROTOCOL_FMT,
208 VENDOR_PRODUCT_ARGS, IFACE_PROTOCOL_ARGS);
209 ADD_MATCHID_OR_RETURN(matches, 210,
210 "usb&" VENDOR_PRODUCT_FMT "&" IFACE_SUBCLASS_FMT,
211 VENDOR_PRODUCT_ARGS, IFACE_SUBCLASS_ARGS);
212 ADD_MATCHID_OR_RETURN(matches, 200,
213 "usb&" VENDOR_PRODUCT_FMT "&" IFACE_CLASS_FMT,
214 VENDOR_PRODUCT_ARGS, IFACE_CLASS_ARGS);
215
216 /* Finally, interface matches with only vendor. */
217 ADD_MATCHID_OR_RETURN(matches, 190,
218 "usb&" VENDOR_ONLY_FMT "&" IFACE_PROTOCOL_FMT,
219 VENDOR_ONLY_ARGS, IFACE_PROTOCOL_ARGS);
220 ADD_MATCHID_OR_RETURN(matches, 180,
221 "usb&" VENDOR_ONLY_FMT "&" IFACE_SUBCLASS_FMT,
222 VENDOR_ONLY_ARGS, IFACE_SUBCLASS_ARGS);
223 ADD_MATCHID_OR_RETURN(matches, 170,
224 "usb&" VENDOR_ONLY_FMT "&" IFACE_CLASS_FMT,
225 VENDOR_ONLY_ARGS, IFACE_CLASS_ARGS);
226 }
227
228 /* Now, the same but without any vendor specification. */
229 ADD_MATCHID_OR_RETURN(matches, 160,
230 "usb&" IFACE_PROTOCOL_FMT,
231 IFACE_PROTOCOL_ARGS);
232 ADD_MATCHID_OR_RETURN(matches, 150,
233 "usb&" IFACE_SUBCLASS_FMT,
234 IFACE_SUBCLASS_ARGS);
235 ADD_MATCHID_OR_RETURN(matches, 140,
236 "usb&" IFACE_CLASS_FMT,
237 IFACE_CLASS_ARGS);
238
239#undef IFACE_PROTOCOL_FMT
240#undef IFACE_PROTOCOL_ARGS
241#undef IFACE_SUBCLASS_FMT
242#undef IFACE_SUBCLASS_ARGS
243#undef IFACE_CLASS_FMT
244#undef IFACE_CLASS_ARGS
245#undef VENDOR_RELEASE_FMT
246#undef VENDOR_RELEASE_ARGS
247#undef VENDOR_PRODUCT_FMT
248#undef VENDOR_PRODUCT_ARGS
249#undef VENDOR_ONLY_FMT
250#undef VENDOR_ONLY_ARGS
[540e2d2]251
[2211125e]252 /* As a last resort, try fallback driver. */
253 ADD_MATCHID_OR_RETURN(matches, 10, "usb&interface&fallback");
254
[6e6dc7d]255 return EOK;
[540e2d2]256}
257
[692c0d3e]258/** Create DDF match ids from USB device descriptor.
259 *
260 * @param matches List of match ids to extend.
261 * @param device_descriptor Device descriptor returned by given device.
262 * @return Error code.
263 */
[0f21c0c]264int usb_device_create_match_ids_from_device_descriptor(
265 const usb_standard_device_descriptor_t *device_descriptor,
266 match_id_list_t *matches)
[692c0d3e]267{
268 /*
269 * Unless the vendor id is 0, the pair idVendor-idProduct
270 * quite uniquely describes the device.
271 */
272 if (device_descriptor->vendor_id != 0) {
273 /* First, with release number. */
[6e6dc7d]274 ADD_MATCHID_OR_RETURN(matches, 100,
[345ea18]275 "usb&vendor=0x%04x&product=0x%04x&release=" BCD_FMT,
[692c0d3e]276 (int) device_descriptor->vendor_id,
277 (int) device_descriptor->product_id,
278 BCD_ARGS(device_descriptor->device_version));
279
280 /* Next, without release number. */
[6e6dc7d]281 ADD_MATCHID_OR_RETURN(matches, 90,
[345ea18]282 "usb&vendor=0x%04x&product=0x%04x",
[692c0d3e]283 (int) device_descriptor->vendor_id,
284 (int) device_descriptor->product_id);
285 }
286
287 /*
288 * If the device class points to interface we skip adding
[3ae93a8]289 * class directly but we add a multi interface device.
[692c0d3e]290 */
291 if (device_descriptor->device_class != USB_CLASS_USE_INTERFACE) {
[6e6dc7d]292 ADD_MATCHID_OR_RETURN(matches, 50, "usb&class=%s",
[692c0d3e]293 usb_str_class(device_descriptor->device_class));
[3ae93a8]294 } else {
[6e6dc7d]295 ADD_MATCHID_OR_RETURN(matches, 50, "usb&mid");
[692c0d3e]296 }
297
[93855d4]298 /* As a last resort, try fallback driver. */
299 ADD_MATCHID_OR_RETURN(matches, 10, "usb&fallback");
300
[692c0d3e]301 return EOK;
302}
303
[a66225f3]304
[02ccfcd]305/** Create match ids describing attached device.
306 *
307 * @warning The list of match ids @p matches may change even when
308 * function exits with error.
309 *
[4e8e1f5]310 * @param ctrl_pipe Control pipe to given device (session must be already
311 * started).
[02ccfcd]312 * @param matches Initialized list of match ids.
313 * @return Error code.
314 */
[a372663]315int usb_device_create_match_ids(usb_pipe_t *ctrl_pipe,
[4e8e1f5]316 match_id_list_t *matches)
[02ccfcd]317{
318 int rc;
[692c0d3e]319 /*
320 * Retrieve device descriptor and add matches from it.
321 */
[02ccfcd]322 usb_standard_device_descriptor_t device_descriptor;
323
[4e8e1f5]324 rc = usb_request_get_device_descriptor(ctrl_pipe, &device_descriptor);
[02ccfcd]325 if (rc != EOK) {
326 return rc;
327 }
[4e8e1f5]328
[0f21c0c]329 rc = usb_device_create_match_ids_from_device_descriptor(
330 &device_descriptor, matches);
[692c0d3e]331 if (rc != EOK) {
332 return rc;
[a66225f3]333 }
[4e8e1f5]334
[02ccfcd]335 return EOK;
336}
337
338/** Probe for device kind and register it in devman.
339 *
[3b77628]340 * @param[in] address Address of the (unknown) attached device.
[4e8e1f5]341 * @param[in] hc_handle Handle of the host controller.
342 * @param[in] parent Parent device.
[eea3e39]343 * @param[in] dev_ops Child device ops. Default child_ops will be used if NULL.
[bc1c6fb]344 * @param[in] dev_data Arbitrary pointer to be stored in the child
345 * as @c driver_data.
346 * @param[out] child_fun Storage where pointer to allocated child function
347 * will be written.
[02ccfcd]348 * @return Error code.
349 */
[4e8e1f5]350int usb_device_register_child_in_devman(usb_address_t address,
[162726b]351 devman_handle_t hc_handle, ddf_dev_t *parent,
[eb1a2f4]352 ddf_dev_ops_t *dev_ops, void *dev_data, ddf_fun_t **child_fun)
[02ccfcd]353{
[ae754e5f]354 if (child_fun == NULL)
355 return EINVAL;
356
[59c163c]357 if (!dev_ops && dev_data) {
358 usb_log_warning("Using standard fun ops with arbitrary "
359 "driver data. This does not have to work.\n");
360 }
361
[38648f0]362 size_t this_device_name_index;
363
364 fibril_mutex_lock(&device_name_index_mutex);
365 this_device_name_index = device_name_index;
366 device_name_index++;
367 fibril_mutex_unlock(&device_name_index_mutex);
368
[eb1a2f4]369 ddf_fun_t *child = NULL;
[7ed5b576]370 char *child_name = NULL;
371 int rc;
[4e8e1f5]372 usb_device_connection_t dev_connection;
[a372663]373 usb_pipe_t ctrl_pipe;
[4e8e1f5]374
[59c163c]375 rc = usb_device_connection_initialize(
376 &dev_connection, hc_handle, address);
[4e8e1f5]377 if (rc != EOK) {
378 goto failure;
379 }
380
[59c163c]381 rc = usb_pipe_initialize_default_control(&ctrl_pipe, &dev_connection);
[4e8e1f5]382 if (rc != EOK) {
383 goto failure;
384 }
[3954a63b]385 rc = usb_pipe_probe_default_control(&ctrl_pipe);
[206f71a]386 if (rc != EOK) {
387 goto failure;
388 }
[7ed5b576]389
390 /*
[b207803]391 * TODO: Once the device driver framework support persistent
392 * naming etc., something more descriptive could be created.
[7ed5b576]393 */
[189cd4e]394 rc = asprintf(&child_name, "usb%02zu_a%d",
395 this_device_name_index, address);
[7ed5b576]396 if (rc < 0) {
397 goto failure;
398 }
[eb1a2f4]399
400 child = ddf_fun_create(parent, fun_inner, child_name);
[ae754e5f]401 free(child_name);
[eb1a2f4]402 if (child == NULL) {
403 rc = ENOMEM;
404 goto failure;
405 }
406
407 if (dev_ops != NULL) {
408 child->ops = dev_ops;
409 } else {
410 child->ops = &child_ops;
411 }
412
413 child->driver_data = dev_data;
[59c163c]414 /* Store the attached device in fun driver data if there is no
415 * other data */
416 if (!dev_data) {
417 usb_hub_attached_device_t *new_device = ddf_fun_data_alloc(
418 child, sizeof(usb_hub_attached_device_t));
419 if (!new_device) {
420 rc = ENOMEM;
421 goto failure;
422 }
423 new_device->address = address;
424 new_device->fun = child;
425 }
426
[4e8e1f5]427
428 rc = usb_device_create_match_ids(&ctrl_pipe, &child->match_ids);
429 if (rc != EOK) {
430 goto failure;
431 }
432
[eb1a2f4]433 rc = ddf_fun_bind(child);
[7ed5b576]434 if (rc != EOK) {
435 goto failure;
436 }
437
[ae754e5f]438 *child_fun = child;
[7ed5b576]439 return EOK;
440
441failure:
442 if (child != NULL) {
[59c163c]443 /* We know nothing about the data if it came from outside. */
444 if (dev_data) {
445 child->driver_data = NULL;
446 }
[7ed5b576]447 /* This takes care of match_id deallocation as well. */
[eb1a2f4]448 ddf_fun_destroy(child);
[7ed5b576]449 }
450
451 return rc;
[02ccfcd]452}
[7ed5b576]453
[02ccfcd]454
455/**
456 * @}
457 */
Note: See TracBrowser for help on using the repository browser.