source: mainline/uspace/drv/bus/usb/vhc/hub/hub.c@ a35b458

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since a35b458 was ae3a941, checked in by Ondřej Hlavatý <aearsis@…>, 8 years ago

usb: cstyle

  • 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 errno_t 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{
72 switch (state) {
73 case HUB_PORT_STATE_NOT_CONFIGURED:
74 return '-';
75 case HUB_PORT_STATE_POWERED_OFF:
76 return 'O';
77 case HUB_PORT_STATE_DISCONNECTED:
78 return 'X';
79 case HUB_PORT_STATE_DISABLED:
80 return 'D';
81 case HUB_PORT_STATE_RESETTING:
82 return 'R';
83 case HUB_PORT_STATE_ENABLED:
84 return 'E';
85 case HUB_PORT_STATE_SUSPENDED:
86 return 'S';
87 case HUB_PORT_STATE_RESUMING:
88 return 'F';
89 default:
90 return '?';
91 }
92}
93
94/** Initialize single hub port.
95 *
96 * @param port Port to be initialized.
97 * @param index Port index (one based).
98 */
99static void hub_init_port(hub_port_t *port, hub_t *hub, size_t index)
100{
101 port->connected_device = NULL;
102 port->index = index;
103 port->state = HUB_PORT_STATE_NOT_CONFIGURED;
104 port->status_change = 0;
105 port->hub = hub;
106}
107
108/** Initialize the hub.
109 *
110 * @param hub Hub to be initialized.
111 */
112void hub_init(hub_t *hub)
113{
114 size_t i;
115 for (i = 0; i < HUB_PORT_COUNT; i++) {
116 hub_init_port(&hub->ports[i], hub, i + 1);
117 }
118 hub->custom_data = NULL;
119 hub->signal_changes = true;
120 fibril_mutex_initialize(&hub->guard);
121}
122
123/** Connect a device to the hub.
124 *
125 * @param hub Hub to connect device to.
126 * @param device Device to be connected.
127 * @return Index of port the device was connected to.
128 * @retval -1 No free port available.
129 */
130size_t hub_connect_device(hub_t *hub, void *device)
131{
132 size_t i;
133 for (i = 0; i < HUB_PORT_COUNT; i++) {
134 hub_port_t *port = &hub->ports[i];
135
136 if (port->connected_device != NULL) {
137 continue;
138 }
139
140 port->connected_device = device;
141
142 /*
143 * TODO:
144 * If the hub was configured, we can normally
145 * announce the plug-in.
146 * Otherwise, we will wait until hub is configured
147 * and announce changes in single burst.
148 */
149 //if (port->state == HUB_PORT_STATE_DISCONNECTED) {
150 port->state = HUB_PORT_STATE_DISABLED;
151 set_port_status_change(port, HUB_STATUS_C_PORT_CONNECTION);
152 //}
153
154 return i;
155 }
156
157 return (size_t) -1;
158}
159
160/** Disconnects a device from a hub.
161 *
162 * @param hub Hub the device was connected to.
163 * @param device Device to be disconnected.
164 * @return Error code.
165 */
166errno_t hub_disconnect_device(hub_t *hub, void *device)
167{
168 size_t index = hub_find_device(hub, device);
169 if (index == (size_t) -1) {
170 return ENOENT;
171 }
172
173 hub_port_t *port = &hub->ports[index];
174
175 port->connected_device = NULL;
176 port->state = HUB_PORT_STATE_DISCONNECTED;
177 set_port_status_change(port, HUB_STATUS_C_PORT_CONNECTION);
178
179 return EOK;
180}
181
182/** Find port device is connected to.
183 *
184 * @param hub Hub in question.
185 * @param device Device in question.
186 * @return Port index (zero based).
187 * @retval -1 Device is not connected.
188 */
189size_t hub_find_device(hub_t *hub, void *device)
190{
191 size_t i;
192 for (i = 0; i < HUB_PORT_COUNT; i++) {
193 hub_port_t *port = &hub->ports[i];
194
195 if (port->connected_device == device) {
196 return i;
197 }
198 }
199
200 return -1;
201}
202
203/** Acquire exclusive access to the hub.
204 *
205 * @param hub Hub in question.
206 */
207void hub_acquire(hub_t *hub)
208{
209 fibril_mutex_lock(&hub->guard);
210}
211
212/** Give up exclusive access to the hub.
213 *
214 * @param hub Hub in question.
215 */
216void hub_release(hub_t *hub)
217{
218 fibril_mutex_unlock(&hub->guard);
219}
220
221/** Change port state.
222 *
223 * @param hub Hub the port belongs to.
224 * @param port_index Port index (zero based).
225 * @param state New port state.
226 */
227void hub_set_port_state(hub_t *hub, size_t port_index, hub_port_state_t state)
228{
229 hub_port_t *port = get_hub_port(hub, port_index);
230 if (port == NULL) {
231 return;
232 }
233
234 usb_log_debug("Setting port %zu to state %d.", port_index, state);
235
236 switch (state) {
237 case HUB_PORT_STATE_POWERED_OFF:
238 clear_port_status_change(port, HUB_STATUS_C_PORT_CONNECTION);
239 clear_port_status_change(port, HUB_STATUS_C_PORT_ENABLE);
240 clear_port_status_change(port, HUB_STATUS_C_PORT_RESET);
241 break;
242 case HUB_PORT_STATE_RESUMING:
243 port->state = state;
244 set_port_state_delayed(hub, port_index,
245 10, state, HUB_PORT_STATE_ENABLED);
246 break;
247 case HUB_PORT_STATE_RESETTING:
248 port->state = state;
249 set_port_state_delayed(hub, port_index,
250 10, state, HUB_PORT_STATE_ENABLED);
251 break;
252 case HUB_PORT_STATE_ENABLED:
253 if (port->state == HUB_PORT_STATE_RESETTING) {
254 set_port_status_change(port, HUB_STATUS_C_PORT_RESET);
255 }
256 break;
257 default:
258 break;
259 }
260
261 port->state = state;
262}
263
264/** Change state of all ports.
265 *
266 * @param hub Hub in question.
267 * @param state New state for all ports.
268 */
269void hub_set_port_state_all(hub_t *hub, hub_port_state_t state)
270{
271 size_t i;
272 for (i = 0; i < HUB_PORT_COUNT; i++) {
273 hub_set_port_state(hub, i, state);
274 }
275}
276
277/** Get port state.
278 *
279 * @param hub Hub the port belongs to.
280 * @param port_index Port index (zero based).
281 * @return Port state.
282 */
283hub_port_state_t hub_get_port_state(hub_t *hub, size_t port_index)
284{
285 hub_port_t *port = get_hub_port(hub, port_index);
286 if (port == NULL) {
287 return HUB_PORT_STATE_UNKNOWN;
288 }
289
290 return port->state;
291}
292
293/** Clear port status change bit.
294 *
295 * @param hub Hub the port belongs to.
296 * @param port_index Port index (zero based).
297 * @param change Change to be cleared.
298 */
299void hub_clear_port_status_change(hub_t *hub, size_t port_index,
300 hub_status_change_t change)
301{
302 hub_port_t *port = get_hub_port(hub, port_index);
303 if (port == NULL) {
304 return;
305 }
306
307 clear_port_status_change(port, change);
308}
309
310/** Get port status change bits.
311 *
312 * @param hub Hub the port belongs to.
313 * @param port_index Port index (zero based).
314 * @return Port status change bitmap in standard format.
315 */
316uint16_t hub_get_port_status_change(hub_t *hub, size_t port_index)
317{
318 hub_port_t *port = get_hub_port(hub, port_index);
319 if (port == NULL) {
320 return 0;
321 }
322
323 return port->status_change;
324}
325
326/** Get port status bits.
327 *
328 * @param hub Hub the port belongs to.
329 * @param port_index Port index (zero based).
330 * @return Port status bitmap in standard format.
331 */
332uint32_t hub_get_port_status(hub_t *hub, size_t port_index)
333{
334 hub_port_t *port = get_hub_port(hub, port_index);
335 if (port == NULL) {
336 return 0;
337 }
338
339 uint32_t 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 status |= 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",
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 errno_t 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 usb_log_error("Failed to create fibril.");
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.