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

Last change on this file was 09ab0a9a, checked in by Jiri Svoboda <jiri@…>, 7 years ago

Fix vertical spacing with new Ccheck revision.

  • 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/** Produce a byte from bit values.
47 */
48#define MAKE_BYTE(b0, b1, b2, b3, b4, b5, b6, b7) \
49 (( \
50 ((b0) << 0) \
51 | ((b1) << 1) \
52 | ((b2) << 2) \
53 | ((b3) << 3) \
54 | ((b4) << 4) \
55 | ((b5) << 5) \
56 | ((b6) << 6) \
57 | ((b7) << 7) \
58 ))
59
60/* Static functions. */
61static hub_port_t *get_hub_port(hub_t *, size_t);
62static void set_port_status_change(hub_port_t *, hub_status_change_t);
63static void clear_port_status_change(hub_port_t *, uint16_t);
64static errno_t set_port_state_delayed_fibril(void *);
65static void set_port_state_delayed(hub_t *, size_t, usec_t, hub_port_state_t,
66 hub_port_state_t);
67
68/** Convert hub port state to a char. */
69char hub_port_state_to_char(hub_port_state_t state)
70{
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 */
165errno_t 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.", 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 = MAKE_BYTE(
339 /* Current connect status. */
340 port->connected_device == NULL ? 0 : 1,
341 /* Port enabled/disabled. */
342 port->state == HUB_PORT_STATE_ENABLED ? 1 : 0,
343 /* Suspend. */
344 (port->state == HUB_PORT_STATE_SUSPENDED) ||
345 (port->state == HUB_PORT_STATE_RESUMING) ? 1 : 0,
346 /* Over-current. */
347 0,
348 /* Reset. */
349 port->state == HUB_PORT_STATE_RESETTING ? 1 : 0,
350 /* Reserved. */
351 0, 0, 0);
352
353 status |= MAKE_BYTE(
354 /* Port power. */
355 port->state == HUB_PORT_STATE_POWERED_OFF ? 0 : 1,
356 /* Full-speed device. */
357 0,
358 /* Reserved. */
359 0, 0, 0, 0, 0, 0) << 8;
360
361 status |= (port->status_change << 16);
362
363 return status;
364}
365
366/** Create hub status change bitmap.
367 *
368 * @warning This function assumes that the whole bitmap fits into 8 bits.
369 *
370 * @param hub Hub in question.
371 * @return Hub status change bitmap.
372 */
373uint8_t hub_get_status_change_bitmap(hub_t *hub)
374{
375 uint8_t change_map = 0;
376
377 size_t i;
378 for (i = 0; i < HUB_PORT_COUNT; i++) {
379 hub_port_t *port = &hub->ports[i];
380
381 if (port->status_change != 0) {
382 change_map |= (1 << port->index);
383 }
384 }
385
386 return change_map;
387}
388
389/*
390 *
391 * STATIC (HELPER) FUNCTIONS
392 *
393 */
394
395/** Find a port in a hub.
396 *
397 * @param hub Hub in question.
398 * @param port Port index (zero based).
399 * @return Port structure.
400 * @retval NULL Invalid port index.
401 */
402static hub_port_t *get_hub_port(hub_t *hub, size_t port)
403{
404 if (port >= HUB_PORT_COUNT) {
405 return NULL;
406 }
407
408 return &hub->ports[port];
409}
410
411/** Adds a port status change to a port.
412 *
413 * @param port The port with status change.
414 * @param change Change to be added to the status.
415 */
416static void set_port_status_change(hub_port_t *port,
417 hub_status_change_t change)
418{
419 assert(port != NULL);
420 uint16_t old_value = port->status_change;
421 port->status_change |= change;
422 usb_log_debug("Changing status change on %zu: %04x => %04x",
423 port->index,
424 (unsigned int) old_value, (unsigned int) port->status_change);
425 port->hub->signal_changes = true;
426}
427
428/** Clears a port status change on a port.
429 *
430 * @param port The port with status change.
431 * @param change Change to be removed from the status.
432 */
433static void clear_port_status_change(hub_port_t *port,
434 uint16_t change)
435{
436 assert(port != NULL);
437 port->status_change &= (~change);
438 port->hub->signal_changes = true;
439}
440
441/** Structure for automatic (delayed) port state change. */
442struct delay_port_state_change {
443 /** Delay in microseconds. */
444 usec_t delay;
445 /** Old state of the port. */
446 hub_port_state_t old_state;
447 /** New state of the port. */
448 hub_port_state_t new_state;
449 /** Port index (zero based). */
450 size_t port;
451 /** Hub. */
452 hub_t *hub;
453};
454
455/** Fibril responsible for delayed port state change.
456 *
457 * @param arg Pointer to delay_port_state_change.
458 * @return Always EOK.
459 */
460static errno_t set_port_state_delayed_fibril(void *arg)
461{
462 struct delay_port_state_change *change =
463 (struct delay_port_state_change *) arg;
464
465 fibril_usleep(change->delay);
466
467 hub_acquire(change->hub);
468
469 hub_port_t *port = get_hub_port(change->hub, change->port);
470 assert(port != NULL);
471
472 if (port->state == change->old_state) {
473 hub_set_port_state(change->hub, change->port,
474 change->new_state);
475 }
476
477 hub_release(change->hub);
478
479 free(change);
480
481 return EOK;
482}
483
484/** Change port state with a delay.
485 *
486 * @warning If the port state changes during the waiting phase, the state
487 * is not changed.
488 *
489 * @param hub Hub in question.
490 * @param port_index Port index (zero based).
491 * @param delay_time_ms Delay time in miliseconds.
492 * @param old_state Old (current) state of the port.
493 * @param new_state New state of the port.
494 */
495static void set_port_state_delayed(hub_t *hub, size_t port_index,
496 usec_t delay_time_ms, hub_port_state_t old_state,
497 hub_port_state_t new_state)
498{
499 struct delay_port_state_change *change =
500 malloc(sizeof(struct delay_port_state_change));
501
502 change->hub = hub;
503 change->port = port_index;
504 change->delay = MSEC2USEC(delay_time_ms);
505 change->old_state = old_state;
506 change->new_state = new_state;
507 fid_t fibril = fibril_create(set_port_state_delayed_fibril, change);
508 if (fibril == 0) {
509 usb_log_error("Failed to create fibril.");
510 free(change);
511 return;
512 }
513 fibril_add_ready(fibril);
514}
515
516/**
517 * @}
518 */
Note: See TracBrowser for help on using the repository browser.