source: mainline/uspace/drv/vhc/hub/hub.c@ 6cb58e6

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

Virtual USB layer rewritten

Major changes include

  • IPC sends whole transfers (not transactions)
  • separate transfer queues for each device in host controller
  • possibility to return NAK from virtual device (handled by HC)
  • better implementation of callbacks for non-zero endpoints

Still missing

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