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

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

Add USB fallback driver

This driver is launched when no other driver is found for USB device.

Currently, it is launched on device level (support for interface level will
appear in next revision, probably).

The crippled name `usbflbk' is because of FAT16 limitations.

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