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

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

Connecting to "parent" host controller

USB drivers can now connect to host controller drivers they physically
belong to (so far, everything connected to VHC).

Each USB device must implement the USB interface of libdrv to allow
this (it is absolutely neccesary for hubs).

USB drivers can use usb_drv_hc_connect() to connect to "their" host
controller.

Child devices created with usb_drv_register_child_in_devman()are set
to handle this connection automatically.

UHCI and VHC drivers were updated.

  • Property mode set to 100644
File size: 8.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 usb
30 * @{
31 */
32/** @file
33 * @brief Functions for recognising kind of attached devices.
34 */
35#include <usb_iface.h>
36#include <usb/usbdrv.h>
37#include <usb/classes/classes.h>
38#include <stdio.h>
39#include <errno.h>
40
41static int usb_iface_get_hc_handle(device_t *dev, devman_handle_t *handle)
42{
43 assert(dev);
44 assert(dev->parent != NULL);
45
46 device_t *parent = dev->parent;
47
48 if (parent->ops && parent->ops->interfaces[USB_DEV_IFACE]) {
49 usb_iface_t *usb_iface
50 = (usb_iface_t *) parent->ops->interfaces[USB_DEV_IFACE];
51 assert(usb_iface != NULL);
52 if (usb_iface->get_hc_handle) {
53 int rc = usb_iface->get_hc_handle(parent, handle);
54 return rc;
55 }
56 }
57
58 return ENOTSUP;
59}
60
61static usb_iface_t usb_iface = {
62 .get_hc_handle = usb_iface_get_hc_handle
63};
64
65static device_ops_t child_ops = {
66 .interfaces[USB_DEV_IFACE] = &usb_iface
67};
68
69#define BCD_INT(a) (((unsigned int)(a)) / 256)
70#define BCD_FRAC(a) (((unsigned int)(a)) % 256)
71
72#define BCD_FMT "%x.%x"
73#define BCD_ARGS(a) BCD_INT((a)), BCD_FRAC((a))
74
75/* FIXME: make this dynamic */
76#define MATCH_STRING_MAX 256
77
78/** Add formatted match id.
79 *
80 * @param matches List of match ids where to add to.
81 * @param score Score of the match.
82 * @param format Printf-like format
83 * @return Error code.
84 */
85static int usb_add_match_id(match_id_list_t *matches, int score,
86 const char *format, ...)
87{
88 char *match_str = NULL;
89 match_id_t *match_id = NULL;
90 int rc;
91
92 match_str = malloc(MATCH_STRING_MAX + 1);
93 if (match_str == NULL) {
94 rc = ENOMEM;
95 goto failure;
96 }
97
98 /*
99 * FIXME: replace with dynamic allocation of exact size
100 */
101 va_list args;
102 va_start(args, format );
103 vsnprintf(match_str, MATCH_STRING_MAX, format, args);
104 match_str[MATCH_STRING_MAX] = 0;
105 va_end(args);
106
107 match_id = create_match_id();
108 if (match_id == NULL) {
109 rc = ENOMEM;
110 goto failure;
111 }
112
113 match_id->id = match_str;
114 match_id->score = score;
115 add_match_id(matches, match_id);
116
117 return EOK;
118
119failure:
120 if (match_str != NULL) {
121 free(match_str);
122 }
123 if (match_id != NULL) {
124 match_id->id = NULL;
125 delete_match_id(match_id);
126 }
127
128 return rc;
129}
130
131/** Add match ids based on configuration descriptor.
132 *
133 * @param hc Open phone to host controller.
134 * @param matches Match ids list to add matches to.
135 * @param address USB address of the attached device.
136 * @return Error code.
137 */
138static int usb_add_config_descriptor_match_ids(int hc,
139 match_id_list_t *matches, usb_address_t address,
140 int config_count)
141{
142 int final_rc = EOK;
143
144 int config_index;
145 for (config_index = 0; config_index < config_count; config_index++) {
146 int rc;
147 usb_standard_configuration_descriptor_t config_descriptor;
148 rc = usb_drv_req_get_bare_configuration_descriptor(hc,
149 address, config_index, &config_descriptor);
150 if (rc != EOK) {
151 final_rc = rc;
152 continue;
153 }
154
155 size_t full_config_descriptor_size;
156 void *full_config_descriptor
157 = malloc(config_descriptor.total_length);
158 rc = usb_drv_req_get_full_configuration_descriptor(hc,
159 address, config_index,
160 full_config_descriptor, config_descriptor.total_length,
161 &full_config_descriptor_size);
162 if (rc != EOK) {
163 final_rc = rc;
164 continue;
165 }
166 if (full_config_descriptor_size
167 != config_descriptor.total_length) {
168 final_rc = ERANGE;
169 continue;
170 }
171
172 /*
173 * Iterate through config descriptor to find the interface
174 * descriptors.
175 */
176 size_t position = sizeof(config_descriptor);
177 while (position + 1 < full_config_descriptor_size) {
178 uint8_t *current_descriptor
179 = ((uint8_t *) full_config_descriptor) + position;
180 uint8_t cur_descr_len = current_descriptor[0];
181 uint8_t cur_descr_type = current_descriptor[1];
182
183 position += cur_descr_len;
184
185 if (cur_descr_type != USB_DESCTYPE_INTERFACE) {
186 continue;
187 }
188 /*
189 * Finally, we found an interface descriptor.
190 */
191 usb_standard_interface_descriptor_t *interface
192 = (usb_standard_interface_descriptor_t *)
193 current_descriptor;
194
195 rc = usb_add_match_id(matches, 50,
196 "usb&interface&class=%s",
197 usb_str_class(interface->interface_class));
198 if (rc != EOK) {
199 final_rc = rc;
200 break;
201 }
202 }
203 }
204
205 return final_rc;
206}
207
208/** Create match ids describing attached device.
209 *
210 * @warning The list of match ids @p matches may change even when
211 * function exits with error.
212 *
213 * @param hc Open phone to host controller.
214 * @param matches Initialized list of match ids.
215 * @param address USB address of the attached device.
216 * @return Error code.
217 */
218int usb_drv_create_device_match_ids(int hc, match_id_list_t *matches,
219 usb_address_t address)
220{
221 int rc;
222 usb_standard_device_descriptor_t device_descriptor;
223
224 rc = usb_drv_req_get_device_descriptor(hc, address,
225 &device_descriptor);
226 if (rc != EOK) {
227 return rc;
228 }
229
230 /*
231 * Unless the vendor id is 0, the pair idVendor-idProduct
232 * quite uniquely describes the device.
233 */
234 if (device_descriptor.vendor_id != 0) {
235 /* First, with release number. */
236 rc = usb_add_match_id(matches, 100,
237 "usb&vendor=%d&product=%d&release=" BCD_FMT,
238 (int) device_descriptor.vendor_id,
239 (int) device_descriptor.product_id,
240 BCD_ARGS(device_descriptor.device_version));
241 if (rc != EOK) {
242 return rc;
243 }
244
245 /* Next, without release number. */
246 rc = usb_add_match_id(matches, 90, "usb&vendor=%d&product=%d",
247 (int) device_descriptor.vendor_id,
248 (int) device_descriptor.product_id);
249 if (rc != EOK) {
250 return rc;
251 }
252
253 }
254
255 /*
256 * If the device class points to interface we skip adding
257 * class directly.
258 */
259 if (device_descriptor.device_class != USB_CLASS_USE_INTERFACE) {
260 rc = usb_add_match_id(matches, 50, "usb&class=%s",
261 usb_str_class(device_descriptor.device_class));
262 if (rc != EOK) {
263 return rc;
264 }
265 }
266 /*
267 * Go through all configurations and add matches
268 * based on interface class.
269 */
270 rc = usb_add_config_descriptor_match_ids(hc, matches,
271 address, device_descriptor.configuration_count);
272 if (rc != EOK) {
273 return rc;
274 }
275
276 /*
277 * As a fallback, provide the simplest match id possible.
278 */
279 rc = usb_add_match_id(matches, 1, "usb&fallback");
280 if (rc != EOK) {
281 return rc;
282 }
283
284 return EOK;
285}
286
287
288/** Probe for device kind and register it in devman.
289 *
290 * @param hc Open phone to the host controller.
291 * @param parent Parent device.
292 * @param address Address of the (unknown) attached device.
293 * @return Error code.
294 */
295int usb_drv_register_child_in_devman(int hc, device_t *parent,
296 usb_address_t address, devman_handle_t *child_handle)
297{
298 device_t *child = NULL;
299 char *child_name = NULL;
300 int rc;
301
302 child = create_device();
303 if (child == NULL) {
304 rc = ENOMEM;
305 goto failure;
306 }
307
308 /*
309 * TODO: some better child naming
310 */
311 rc = asprintf(&child_name, "usb%p", child);
312 if (rc < 0) {
313 goto failure;
314 }
315 child->parent = parent;
316 child->name = child_name;
317 child->ops = &child_ops;
318
319 rc = usb_drv_create_device_match_ids(hc, &child->match_ids, address);
320 if (rc != EOK) {
321 goto failure;
322 }
323
324 rc = child_device_register(child, parent);
325 if (rc != EOK) {
326 goto failure;
327 }
328
329 if (child_handle != NULL) {
330 *child_handle = child->handle;
331 }
332
333 return EOK;
334
335failure:
336 if (child != NULL) {
337 child->name = NULL;
338 /* This takes care of match_id deallocation as well. */
339 delete_device(child);
340 }
341 if (child_name != NULL) {
342 free(child_name);
343 }
344
345 return rc;
346
347}
348
349
350/**
351 * @}
352 */
Note: See TracBrowser for help on using the repository browser.