source: mainline/uspace/lib/usb/src/hcdhubd.c@ 4144630

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 4144630 was 4144630, checked in by smekideki@…>, 15 years ago

usb adresses assigning

  • Property mode set to 100644
File size: 8.2 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 Common stuff for both HC driver and hub driver.
34 */
35#include <usb/hcdhubd.h>
36#include <usb/devreq.h>
37#include <usbhc_iface.h>
38#include <usb/descriptor.h>
39#include <driver.h>
40#include <bool.h>
41#include <errno.h>
42#include <str_error.h>
43#include <usb/classes/hub.h>
44
45#include "hcdhubd_private.h"
46
47/** Callback when new device is detected and must be handled by this driver.
48 *
49 * @param dev New device.
50 * @return Error code.
51 */
52static int add_device(device_t *dev) {
53 bool is_hc = str_cmp(dev->name, USB_HUB_DEVICE_NAME) != 0;
54 printf("%s: add_device(name=\"%s\")\n", hc_driver->name, dev->name);
55
56 if (is_hc) {
57 /*
58 * We are the HC itself.
59 */
60 return usb_add_hc_device(dev);
61 } else {
62 /*
63 * We are some (maybe deeply nested) hub.
64 * Thus, assign our own operations and explore already
65 * connected devices.
66 */
67 return usb_add_hub_device(dev);
68 }
69}
70
71/** Operations for combined HC and HUB driver. */
72static driver_ops_t hc_driver_generic_ops = {
73 .add_device = add_device
74};
75
76/** Combined HC and HUB driver. */
77static driver_t hc_driver_generic = {
78 .driver_ops = &hc_driver_generic_ops
79};
80
81/** Main USB host controller driver routine.
82 *
83 * @see driver_main
84 *
85 * @param hc Host controller driver.
86 * @return Error code.
87 */
88int usb_hcd_main(usb_hc_driver_t *hc) {
89 hc_driver = hc;
90 hc_driver_generic.name = hc->name;
91
92 /*
93 * Run the device driver framework.
94 */
95 return driver_main(&hc_driver_generic);
96}
97
98/** Add a root hub for given host controller.
99 * This function shall be called only once for each host controller driven
100 * by this driver.
101 * It takes care of creating child device - hub - that will be driven by
102 * this task.
103 *
104 * @param dev Host controller device.
105 * @return Error code.
106 */
107int usb_hcd_add_root_hub(usb_hc_device_t *dev) {
108 char *id;
109 int rc = asprintf(&id, "usb&hc=%s&hub", dev->generic->name);
110 if (rc <= 0) {
111 return rc;
112 }
113
114 rc = usb_hc_add_child_device(dev->generic, USB_HUB_DEVICE_NAME, id, true);
115 if (rc != EOK) {
116 free(id);
117 }
118
119 return rc;
120}
121
122/** Info about child device. */
123struct child_device_info {
124 device_t *parent;
125 const char *name;
126 const char *match_id;
127};
128
129/** Adds a child device fibril worker. */
130static int fibril_add_child_device(void *arg) {
131 struct child_device_info *child_info
132 = (struct child_device_info *) arg;
133 int rc;
134
135 device_t *child = create_device();
136 match_id_t *match_id = NULL;
137
138 if (child == NULL) {
139 rc = ENOMEM;
140 goto failure;
141 }
142 child->name = child_info->name;
143
144 match_id = create_match_id();
145 if (match_id == NULL) {
146 rc = ENOMEM;
147 goto failure;
148 }
149 match_id->id = child_info->match_id;
150 match_id->score = 10;
151 add_match_id(&child->match_ids, match_id);
152
153 printf("%s: adding child device `%s' with match \"%s\"\n",
154 hc_driver->name, child->name, match_id->id);
155 rc = child_device_register(child, child_info->parent);
156 printf("%s: child device `%s' registration: %s\n",
157 hc_driver->name, child->name, str_error(rc));
158
159 if (rc != EOK) {
160 goto failure;
161 }
162
163 goto leave;
164
165failure:
166 if (child != NULL) {
167 child->name = NULL;
168 delete_device(child);
169 }
170
171 if (match_id != NULL) {
172 match_id->id = NULL;
173 delete_match_id(match_id);
174 }
175
176leave:
177 free(arg);
178 return EOK;
179}
180
181/** Adds a child.
182 * Due to deadlock in devman when parent registers child that oughts to be
183 * driven by the same task, the child adding is done in separate fibril.
184 * Not optimal, but it works.
185 * Update: not under all circumstances the new fibril is successful either.
186 * Thus the last parameter to let the caller choose.
187 *
188 * @param parent Parent device.
189 * @param name Device name.
190 * @param match_id Match id.
191 * @param create_fibril Whether to run the addition in new fibril.
192 * @return Error code.
193 */
194int usb_hc_add_child_device(device_t *parent, const char *name,
195 const char *match_id, bool create_fibril) {
196 printf("%s: about to add child device `%s' (%s)\n", hc_driver->name,
197 name, match_id);
198
199 struct child_device_info *child_info
200 = malloc(sizeof (struct child_device_info));
201
202 child_info->parent = parent;
203 child_info->name = name;
204 child_info->match_id = match_id;
205
206 if (create_fibril) {
207 fid_t fibril = fibril_create(fibril_add_child_device, child_info);
208 if (!fibril) {
209 return ENOMEM;
210 }
211 fibril_add_ready(fibril);
212 } else {
213 fibril_add_child_device(child_info);
214 }
215
216 return EOK;
217}
218
219/** Tell USB address of given device.
220 *
221 * @param handle Devman handle of the device.
222 * @return USB device address or error code.
223 */
224usb_address_t usb_get_address_by_handle(devman_handle_t handle) {
225 /* TODO: search list of attached devices. */
226 return ENOENT;
227}
228
229usb_address_t usb_use_free_address(usb_hc_device_t * this_hcd) {
230 //is there free address?
231 link_t * addresses = &this_hcd->addresses;
232 if (list_empty(addresses)) return -1;
233 link_t * link_addr = addresses;
234 bool found = false;
235 usb_address_list_t * range = NULL;
236 while (!found) {
237 link_addr = link_addr->next;
238 if (link_addr == addresses) return -2;
239 range = list_get_instance(link_addr,
240 usb_address_list_t, link);
241 if (range->upper_bound - range->lower_bound > 0) {
242 found = true;
243 }
244 }
245 //now we have interval
246 int result = range->lower_bound;
247 ++(range->lower_bound);
248 if (range->upper_bound - range->lower_bound == 0) {
249 list_remove(&range->link);
250 free(range);
251 }
252 return result;
253}
254
255void usb_free_used_address(usb_hc_device_t * this_hcd, usb_address_t addr) {
256 //check range
257 if (addr < usb_lowest_address || addr > usb_highest_address)
258 return;
259 link_t * addresses = &this_hcd->addresses;
260 link_t * link_addr = addresses;
261 //find 'good' interval
262 usb_address_list_t * found_range = NULL;
263 bool found = false;
264 while (!found) {
265 link_addr = link_addr->next;
266 if (link_addr == addresses) {
267 found = true;
268 } else {
269 usb_address_list_t * range = list_get_instance(link_addr,
270 usb_address_list_t, link);
271 if ( (range->lower_bound - 1 == addr) ||
272 (range->upper_bound == addr)) {
273 found = true;
274 found_range = range;
275 }
276 if (range->lower_bound - 1 > addr) {
277 found = true;
278 }
279
280 }
281 }
282 if (found_range == NULL) {
283 //no suitable range found
284 usb_address_list_t * result_range =
285 (usb_address_list_t*) malloc(sizeof (usb_address_list_t));
286 result_range->lower_bound = addr;
287 result_range->upper_bound = addr + 1;
288 list_insert_before(&result_range->link, link_addr);
289 } else {
290 //we have good range
291 if (found_range->lower_bound - 1 == addr) {
292 --found_range->lower_bound;
293 } else {
294 //only one possible case
295 ++found_range->upper_bound;
296 if (found_range->link.next != addresses) {
297 usb_address_list_t * next_range =
298 list_get_instance( &found_range->link.next,
299 usb_address_list_t, link);
300 //check neighbour range
301 if (next_range->lower_bound == addr + 1) {
302 //join ranges
303 found_range->upper_bound = next_range->upper_bound;
304 list_remove(&next_range->link);
305 free(next_range);
306 }
307 }
308 }
309 }
310
311}
312
313/**
314 * @}
315 */
Note: See TracBrowser for help on using the repository browser.