source: mainline/uspace/drv/vhc/hub/hub.c@ fa48ebe

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

Doxygen group for USB virtualization

Virtual host controller and libusbvirt has separate Doxygen groups because
the usb group was polluted way too much.

  • Property mode set to 100644
File size: 12.0 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 drvusbvhc
30 * @{
31 */
32/** @file
33 * @brief Representation of an USB hub (implementation).
34 */
35#include <usb/classes/classes.h>
36#include <usbvirt/hub.h>
37#include <usbvirt/device.h>
38#include <errno.h>
39#include <str_error.h>
40#include <stdlib.h>
41#include <driver.h>
42#include <usb/usbdrv.h>
43
44#include "hub.h"
45
46
47/** Produce a byte from bit values.
48 */
49#define MAKE_BYTE(b0, b1, b2, b3, b4, b5, b6, b7) \
50 (( \
51 ((b0) << 0) \
52 | ((b1) << 1) \
53 | ((b2) << 2) \
54 | ((b3) << 3) \
55 | ((b4) << 4) \
56 | ((b5) << 5) \
57 | ((b6) << 6) \
58 | ((b7) << 7) \
59 ))
60
61/* Static functions. */
62static hub_port_t *get_hub_port(hub_t *, size_t);
63static void set_port_status_change(hub_port_t *, hub_status_change_t);
64static void clear_port_status_change(hub_port_t *, uint16_t);
65static int set_port_state_delayed_fibril(void *);
66static void set_port_state_delayed(hub_t *, size_t, suseconds_t,
67 hub_port_state_t, hub_port_state_t);
68
69/** Convert hub port state to a char. */
70char hub_port_state_to_char(hub_port_state_t state) {
71 switch (state) {
72 case HUB_PORT_STATE_NOT_CONFIGURED:
73 return '-';
74 case HUB_PORT_STATE_POWERED_OFF:
75 return 'O';
76 case HUB_PORT_STATE_DISCONNECTED:
77 return 'X';
78 case HUB_PORT_STATE_DISABLED:
79 return 'D';
80 case HUB_PORT_STATE_RESETTING:
81 return 'R';
82 case HUB_PORT_STATE_ENABLED:
83 return 'E';
84 case HUB_PORT_STATE_SUSPENDED:
85 return 'S';
86 case HUB_PORT_STATE_RESUMING:
87 return 'F';
88 default:
89 return '?';
90 }
91}
92
93/** Initialize single hub port.
94 *
95 * @param port Port to be initialized.
96 * @param index Port index (one based).
97 */
98static void hub_init_port(hub_port_t *port, size_t index)
99{
100 port->connected_device = NULL;
101 port->index = index;
102 port->state = HUB_PORT_STATE_NOT_CONFIGURED;
103 port->status_change = 0;
104}
105
106/** Initialize the hub.
107 *
108 * @param hub Hub to be initialized.
109 */
110void hub_init(hub_t *hub)
111{
112 size_t i;
113 for (i = 0; i < HUB_PORT_COUNT; i++) {
114 hub_init_port(&hub->ports[i], i + 1);
115 }
116 hub->custom_data = NULL;
117 fibril_mutex_initialize(&hub->guard);
118}
119
120/** Connect a device to the hub.
121 *
122 * @param hub Hub to connect device to.
123 * @param device Device to be connected.
124 * @return Index of port the device was connected to.
125 * @retval -1 No free port available.
126 */
127size_t hub_connect_device(hub_t *hub, void *device)
128{
129 size_t i;
130 for (i = 0; i < HUB_PORT_COUNT; i++) {
131 hub_port_t *port = &hub->ports[i];
132
133 if (port->connected_device != NULL) {
134 continue;
135 }
136
137 port->connected_device = device;
138
139 /*
140 * TODO:
141 * If the hub was configured, we can normally
142 * announce the plug-in.
143 * Otherwise, we will wait until hub is configured
144 * and announce changes in single burst.
145 */
146 //if (port->state == HUB_PORT_STATE_DISCONNECTED) {
147 port->state = HUB_PORT_STATE_DISABLED;
148 set_port_status_change(port, HUB_STATUS_C_PORT_CONNECTION);
149 //}
150
151 return i;
152 }
153
154 return (size_t) -1;
155}
156
157/** Disconnects a device from a hub.
158 *
159 * @param hub Hub the device was connected to.
160 * @param device Device to be disconnected.
161 * @return Error code.
162 */
163int hub_disconnect_device(hub_t *hub, void *device)
164{
165 size_t index = hub_find_device(hub, device);
166 if (index == (size_t) -1) {
167 return ENOENT;
168 }
169
170 hub_port_t *port = &hub->ports[index];
171
172 port->connected_device = NULL;
173 port->state = HUB_PORT_STATE_DISCONNECTED;
174 set_port_status_change(port, HUB_STATUS_C_PORT_CONNECTION);
175
176 return EOK;
177}
178
179/** Find port device is connected to.
180 *
181 * @param hub Hub in question.
182 * @param device Device in question.
183 * @return Port index (zero based).
184 * @retval -1 Device is not connected.
185 */
186size_t hub_find_device(hub_t *hub, void *device)
187{
188 size_t i;
189 for (i = 0; i < HUB_PORT_COUNT; i++) {
190 hub_port_t *port = &hub->ports[i];
191
192 if (port->connected_device == device) {
193 return i;
194 }
195 }
196
197 return -1;
198}
199
200/** Acquire exclusive access to the hub.
201 *
202 * @param hub Hub in question.
203 */
204void hub_acquire(hub_t *hub)
205{
206 fibril_mutex_lock(&hub->guard);
207}
208
209/** Give up exclusive access to the hub.
210 *
211 * @param hub Hub in question.
212 */
213void hub_release(hub_t *hub)
214{
215 fibril_mutex_unlock(&hub->guard);
216}
217
218/** Change port state.
219 *
220 * @param hub Hub the port belongs to.
221 * @param port_index Port index (zero based).
222 * @param state New port state.
223 */
224void hub_set_port_state(hub_t *hub, size_t port_index, hub_port_state_t state)
225{
226 hub_port_t *port = get_hub_port(hub, port_index);
227 if (port == NULL) {
228 return;
229 }
230
231 switch (state) {
232 case HUB_PORT_STATE_POWERED_OFF:
233 clear_port_status_change(port, HUB_STATUS_C_PORT_CONNECTION);
234 clear_port_status_change(port, HUB_STATUS_C_PORT_ENABLE);
235 clear_port_status_change(port, HUB_STATUS_C_PORT_RESET);
236 break;
237 case HUB_PORT_STATE_RESUMING:
238 set_port_state_delayed(hub, port_index,
239 10, state, HUB_PORT_STATE_ENABLED);
240 break;
241 case HUB_PORT_STATE_RESETTING:
242 set_port_state_delayed(hub, port_index,
243 10, state, HUB_PORT_STATE_ENABLED);
244 break;
245 case HUB_PORT_STATE_ENABLED:
246 if (port->state == HUB_PORT_STATE_RESETTING) {
247 set_port_status_change(port, HUB_STATUS_C_PORT_RESET);
248 }
249 break;
250 default:
251 break;
252 }
253
254 port->state = state;
255}
256
257/** Change state of all ports.
258 *
259 * @param hub Hub in question.
260 * @param state New state for all ports.
261 */
262void hub_set_port_state_all(hub_t *hub, hub_port_state_t state)
263{
264 size_t i;
265 for (i = 0; i < HUB_PORT_COUNT; i++) {
266 hub_set_port_state(hub, i, state);
267 }
268}
269
270/** Get port state.
271 *
272 * @param hub Hub the port belongs to.
273 * @param port_index Port index (zero based).
274 * @return Port state.
275 */
276hub_port_state_t hub_get_port_state(hub_t *hub, size_t port_index)
277{
278 hub_port_t *port = get_hub_port(hub, port_index);
279 if (port == NULL) {
280 return HUB_PORT_STATE_UNKNOWN;
281 }
282
283 return port->state;
284}
285
286/** Clear port status change bit.
287 *
288 * @param hub Hub the port belongs to.
289 * @param port_index Port index (zero based).
290 * @param change Change to be cleared.
291 */
292void hub_clear_port_status_change(hub_t *hub, size_t port_index,
293 hub_status_change_t change)
294{
295 hub_port_t *port = get_hub_port(hub, port_index);
296 if (port == NULL) {
297 return;
298 }
299
300 clear_port_status_change(port, change);
301}
302
303/** Get port status change bits.
304 *
305 * @param hub Hub the port belongs to.
306 * @param port_index Port index (zero based).
307 * @return Port status change bitmap in standard format.
308 */
309uint16_t hub_get_port_status_change(hub_t *hub, size_t port_index)
310{
311 hub_port_t *port = get_hub_port(hub, port_index);
312 if (port == NULL) {
313 return 0;
314 }
315
316 return port->status_change;
317}
318
319/** Get port status bits.
320 *
321 * @param hub Hub the port belongs to.
322 * @param port_index Port index (zero based).
323 * @return Port status bitmap in standard format.
324 */
325uint32_t hub_get_port_status(hub_t *hub, size_t port_index)
326{
327 hub_port_t *port = get_hub_port(hub, port_index);
328 if (port == NULL) {
329 return 0;
330 }
331
332 uint32_t status;
333 status = MAKE_BYTE(
334 /* Current connect status. */
335 port->connected_device == NULL ? 0 : 1,
336 /* Port enabled/disabled. */
337 port->state == HUB_PORT_STATE_ENABLED ? 1 : 0,
338 /* Suspend. */
339 (port->state == HUB_PORT_STATE_SUSPENDED)
340 || (port->state == HUB_PORT_STATE_RESUMING) ? 1 : 0,
341 /* Over-current. */
342 0,
343 /* Reset. */
344 port->state == HUB_PORT_STATE_RESETTING ? 1 : 0,
345 /* Reserved. */
346 0, 0, 0)
347
348 | (MAKE_BYTE(
349 /* Port power. */
350 port->state == HUB_PORT_STATE_POWERED_OFF ? 0 : 1,
351 /* Full-speed device. */
352 0,
353 /* Reserved. */
354 0, 0, 0, 0, 0, 0
355 )) << 8;
356
357 status |= (port->status_change << 16);
358
359 return status;
360}
361
362/** Create hub status change bitmap.
363 *
364 * @warning This function assumes that the whole bitmap fits into 8 bits.
365 *
366 * @param hub Hub in question.
367 * @return Hub status change bitmap.
368 */
369uint8_t hub_get_status_change_bitmap(hub_t *hub)
370{
371 uint8_t change_map = 0;
372
373 size_t i;
374 for (i = 0; i < HUB_PORT_COUNT; i++) {
375 hub_port_t *port = &hub->ports[i];
376
377 if (port->status_change != 0) {
378 change_map |= (1 << port->index);
379 }
380 }
381
382 return change_map;
383}
384
385
386/*
387 *
388 * STATIC (HELPER) FUNCTIONS
389 *
390 */
391
392/** Find a port in a hub.
393 *
394 * @param hub Hub in question.
395 * @param port Port index (zero based).
396 * @return Port structure.
397 * @retval NULL Invalid port index.
398 */
399static hub_port_t *get_hub_port(hub_t *hub, size_t port)
400{
401 if (port >= HUB_PORT_COUNT) {
402 return NULL;
403 }
404
405 return &hub->ports[port];
406}
407
408/** Adds a port status change to a port.
409 *
410 * @param port The port with status change.
411 * @param change Change to be added to the status.
412 */
413static void set_port_status_change(hub_port_t *port,
414 hub_status_change_t change)
415{
416 assert(port != NULL);
417 port->status_change |= change;
418}
419
420/** Clears a port status change on a port.
421 *
422 * @param port The port with status change.
423 * @param change Change to be removed from the status.
424 */
425static void clear_port_status_change(hub_port_t *port,
426 uint16_t change)
427{
428 assert(port != NULL);
429 port->status_change &= (~change);
430}
431
432/** Structure for automatic (delayed) port state change. */
433struct delay_port_state_change {
434 /** Delay in microseconds. */
435 suseconds_t delay;
436 /** Old state of the port. */
437 hub_port_state_t old_state;
438 /** New state of the port. */
439 hub_port_state_t new_state;
440 /** Port index (zero based). */
441 size_t port;
442 /** Hub. */
443 hub_t *hub;
444};
445
446/** Fibril responsible for delayed port state change.
447 *
448 * @param arg Pointer to delay_port_state_change.
449 * @return Always EOK.
450 */
451static int set_port_state_delayed_fibril(void *arg)
452{
453 struct delay_port_state_change *change
454 = (struct delay_port_state_change *) arg;
455
456 async_usleep(change->delay);
457
458 hub_acquire(change->hub);
459
460 hub_port_t *port = get_hub_port(change->hub, change->port);
461 assert(port != NULL);
462
463 if (port->state == change->old_state) {
464 hub_set_port_state(change->hub, change->port,
465 change->new_state);
466 }
467
468 hub_release(change->hub);
469
470 free(change);
471
472 return EOK;
473}
474
475/** Change port state with a delay.
476 *
477 * @warning If the port state changes during the waiting phase, the state
478 * is not changed.
479 *
480 * @param hub Hub in question.
481 * @param port_index Port index (zero based).
482 * @param delay_time_ms Delay time in miliseconds.
483 * @param old_state Old (current) state of the port.
484 * @param new_state New state of the port.
485 */
486static void set_port_state_delayed(hub_t *hub, size_t port_index,
487 suseconds_t delay_time_ms,
488 hub_port_state_t old_state, hub_port_state_t new_state)
489{
490 struct delay_port_state_change *change
491 = malloc(sizeof(struct delay_port_state_change));
492
493 change->hub = hub;
494 change->port = port_index;
495 change->delay = delay_time_ms * 1000;
496 change->old_state = old_state;
497 change->new_state = new_state;
498 fid_t fibril = fibril_create(set_port_state_delayed_fibril, change);
499 if (fibril == 0) {
500 printf("Failed to create fibril\n");
501 free(change);
502 return;
503 }
504 fibril_add_ready(fibril);
505}
506
507
508
509/**
510 * @}
511 */
Note: See TracBrowser for help on using the repository browser.