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

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

Removal of useless function

It makes no sens to create match ids from configuration descriptor at all.

Also, it is very dangerous to add interface entries to match ids of the
whole device. Thus removed.

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