source: mainline/uspace/drv/vhc/hub/hub.c@ 875e7227

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

Fix wrong return value

  • Property mode set to 100644
File size: 11.5 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 usb
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/** Find port device is connected to.
158 *
159 * @param hub Hub in question.
160 * @param device Device in question.
161 * @return Port index (zero based).
162 * @retval -1 Device is not connected.
163 */
164size_t hub_find_device(hub_t *hub, void *device)
165{
166 size_t i;
167 for (i = 0; i < HUB_PORT_COUNT; i++) {
168 hub_port_t *port = &hub->ports[i];
169
170 if (port->connected_device == device) {
171 return i;
172 }
173 }
174
175 return -1;
176}
177
178/** Acquire exclusive access to the hub.
179 *
180 * @param hub Hub in question.
181 */
182void hub_acquire(hub_t *hub)
183{
184 fibril_mutex_lock(&hub->guard);
185}
186
187/** Give up exclusive access to the hub.
188 *
189 * @param hub Hub in question.
190 */
191void hub_release(hub_t *hub)
192{
193 fibril_mutex_unlock(&hub->guard);
194}
195
196/** Change port state.
197 *
198 * @param hub Hub the port belongs to.
199 * @param port_index Port index (zero based).
200 * @param state New port state.
201 */
202void hub_set_port_state(hub_t *hub, size_t port_index, hub_port_state_t state)
203{
204 hub_port_t *port = get_hub_port(hub, port_index);
205 if (port == NULL) {
206 return;
207 }
208
209 switch (state) {
210 case HUB_PORT_STATE_POWERED_OFF:
211 clear_port_status_change(port, HUB_STATUS_C_PORT_CONNECTION);
212 clear_port_status_change(port, HUB_STATUS_C_PORT_ENABLE);
213 clear_port_status_change(port, HUB_STATUS_C_PORT_RESET);
214 break;
215 case HUB_PORT_STATE_RESUMING:
216 set_port_state_delayed(hub, port_index,
217 10, state, HUB_PORT_STATE_ENABLED);
218 break;
219 case HUB_PORT_STATE_RESETTING:
220 set_port_state_delayed(hub, port_index,
221 10, state, HUB_PORT_STATE_ENABLED);
222 break;
223 case HUB_PORT_STATE_ENABLED:
224 if (port->state == HUB_PORT_STATE_RESETTING) {
225 set_port_status_change(port, HUB_STATUS_C_PORT_RESET);
226 }
227 break;
228 default:
229 break;
230 }
231
232 port->state = state;
233}
234
235/** Change state of all ports.
236 *
237 * @param hub Hub in question.
238 * @param state New state for all ports.
239 */
240void hub_set_port_state_all(hub_t *hub, hub_port_state_t state)
241{
242 size_t i;
243 for (i = 0; i < HUB_PORT_COUNT; i++) {
244 hub_set_port_state(hub, i, state);
245 }
246}
247
248/** Get port state.
249 *
250 * @param hub Hub the port belongs to.
251 * @param port_index Port index (zero based).
252 * @return Port state.
253 */
254hub_port_state_t hub_get_port_state(hub_t *hub, size_t port_index)
255{
256 hub_port_t *port = get_hub_port(hub, port_index);
257 if (port == NULL) {
258 return HUB_PORT_STATE_UNKNOWN;
259 }
260
261 return port->state;
262}
263
264/** Clear port status change bit.
265 *
266 * @param hub Hub the port belongs to.
267 * @param port_index Port index (zero based).
268 * @param change Change to be cleared.
269 */
270void hub_clear_port_status_change(hub_t *hub, size_t port_index,
271 hub_status_change_t change)
272{
273 hub_port_t *port = get_hub_port(hub, port_index);
274 if (port == NULL) {
275 return;
276 }
277
278 clear_port_status_change(port, change);
279}
280
281/** Get port status change bits.
282 *
283 * @param hub Hub the port belongs to.
284 * @param port_index Port index (zero based).
285 * @return Port status change bitmap in standard format.
286 */
287uint16_t hub_get_port_status_change(hub_t *hub, size_t port_index)
288{
289 hub_port_t *port = get_hub_port(hub, port_index);
290 if (port == NULL) {
291 return 0;
292 }
293
294 return port->status_change;
295}
296
297/** Get port status bits.
298 *
299 * @param hub Hub the port belongs to.
300 * @param port_index Port index (zero based).
301 * @return Port status bitmap in standard format.
302 */
303uint32_t hub_get_port_status(hub_t *hub, size_t port_index)
304{
305 hub_port_t *port = get_hub_port(hub, port_index);
306 if (port == NULL) {
307 return 0;
308 }
309
310 uint32_t status;
311 status = MAKE_BYTE(
312 /* Current connect status. */
313 port->connected_device == NULL ? 0 : 1,
314 /* Port enabled/disabled. */
315 port->state == HUB_PORT_STATE_ENABLED ? 1 : 0,
316 /* Suspend. */
317 (port->state == HUB_PORT_STATE_SUSPENDED)
318 || (port->state == HUB_PORT_STATE_RESUMING) ? 1 : 0,
319 /* Over-current. */
320 0,
321 /* Reset. */
322 port->state == HUB_PORT_STATE_RESETTING ? 1 : 0,
323 /* Reserved. */
324 0, 0, 0)
325
326 | (MAKE_BYTE(
327 /* Port power. */
328 port->state == HUB_PORT_STATE_POWERED_OFF ? 0 : 1,
329 /* Full-speed device. */
330 0,
331 /* Reserved. */
332 0, 0, 0, 0, 0, 0
333 )) << 8;
334
335 status |= (port->status_change << 16);
336
337 return status;
338}
339
340/** Create hub status change bitmap.
341 *
342 * @warning This function assumes that the whole bitmap fits into 8 bits.
343 *
344 * @param hub Hub in question.
345 * @return Hub status change bitmap.
346 */
347uint8_t hub_get_status_change_bitmap(hub_t *hub)
348{
349 uint8_t change_map = 0;
350
351 size_t i;
352 for (i = 0; i < HUB_PORT_COUNT; i++) {
353 hub_port_t *port = &hub->ports[i];
354
355 if (port->status_change != 0) {
356 change_map |= (1 << port->index);
357 }
358 }
359
360 return change_map;
361}
362
363
364/*
365 *
366 * STATIC (HELPER) FUNCTIONS
367 *
368 */
369
370/** Find a port in a hub.
371 *
372 * @param hub Hub in question.
373 * @param port Port index (zero based).
374 * @return Port structure.
375 * @retval NULL Invalid port index.
376 */
377static hub_port_t *get_hub_port(hub_t *hub, size_t port)
378{
379 if (port >= HUB_PORT_COUNT) {
380 return NULL;
381 }
382
383 return &hub->ports[port];
384}
385
386/** Adds a port status change to a port.
387 *
388 * @param port The port with status change.
389 * @param change Change to be added to the status.
390 */
391static void set_port_status_change(hub_port_t *port,
392 hub_status_change_t change)
393{
394 assert(port != NULL);
395 port->status_change |= change;
396}
397
398/** Clears a port status change on a port.
399 *
400 * @param port The port with status change.
401 * @param change Change to be removed from the status.
402 */
403static void clear_port_status_change(hub_port_t *port,
404 uint16_t change)
405{
406 assert(port != NULL);
407 port->status_change &= (~change);
408}
409
410/** Structure for automatic (delayed) port state change. */
411struct delay_port_state_change {
412 /** Delay in microseconds. */
413 suseconds_t delay;
414 /** Old state of the port. */
415 hub_port_state_t old_state;
416 /** New state of the port. */
417 hub_port_state_t new_state;
418 /** Port index (zero based). */
419 size_t port;
420 /** Hub. */
421 hub_t *hub;
422};
423
424/** Fibril responsible for delayed port state change.
425 *
426 * @param arg Pointer to delay_port_state_change.
427 * @return Always EOK.
428 */
429static int set_port_state_delayed_fibril(void *arg)
430{
431 struct delay_port_state_change *change
432 = (struct delay_port_state_change *) arg;
433
434 async_usleep(change->delay);
435
436 hub_acquire(change->hub);
437
438 hub_port_t *port = get_hub_port(change->hub, change->port);
439 assert(port != NULL);
440
441 if (port->state == change->old_state) {
442 hub_set_port_state(change->hub, change->port,
443 change->new_state);
444 }
445
446 hub_release(change->hub);
447
448 free(change);
449
450 return EOK;
451}
452
453/** Change port state with a delay.
454 *
455 * @warning If the port state changes during the waiting phase, the state
456 * is not changed.
457 *
458 * @param hub Hub in question.
459 * @param port_index Port index (zero based).
460 * @param delay_time_ms Delay time in miliseconds.
461 * @param old_state Old (current) state of the port.
462 * @param new_state New state of the port.
463 */
464static void set_port_state_delayed(hub_t *hub, size_t port_index,
465 suseconds_t delay_time_ms,
466 hub_port_state_t old_state, hub_port_state_t new_state)
467{
468 struct delay_port_state_change *change
469 = malloc(sizeof(struct delay_port_state_change));
470
471 change->hub = hub;
472 change->port = port_index;
473 change->delay = delay_time_ms * 1000;
474 change->old_state = old_state;
475 change->new_state = new_state;
476 fid_t fibril = fibril_create(set_port_state_delayed_fibril, change);
477 if (fibril == 0) {
478 printf("Failed to create fibril\n");
479 free(change);
480 return;
481 }
482 fibril_add_ready(fibril);
483}
484
485
486
487/**
488 * @}
489 */
Note: See TracBrowser for help on using the repository browser.