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