source: mainline/uspace/drv/vhc/hubops.c@ 1840e0d

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

usbvirt: add callback when device changes state

The virtual root hub uses this to suspend power source to all
ports when entering `configured' state.

  • Property mode set to 100644
File size: 15.2 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 Virtual USB hub operations.
34 */
35#include <usb/classes/classes.h>
36#include <usb/classes/hub.h>
37#include <usbvirt/hub.h>
38#include <usbvirt/device.h>
39#include <errno.h>
40
41#include "vhcd.h"
42#include "hub.h"
43#include "hubintern.h"
44
45/** Produce a byte from bit values.
46 */
47#define MAKE_BYTE(b0, b1, b2, b3, b4, b5, b6, b7) \
48 (( \
49 ((b0) << 0) \
50 | ((b1) << 1) \
51 | ((b2) << 2) \
52 | ((b3) << 3) \
53 | ((b4) << 4) \
54 | ((b5) << 5) \
55 | ((b6) << 6) \
56 | ((b7) << 7) \
57 ))
58
59static int on_get_descriptor(struct usbvirt_device *dev,
60 usb_device_request_setup_packet_t *request, uint8_t *data);
61static int on_data_request(struct usbvirt_device *dev,
62 usb_endpoint_t endpoint,
63 void *buffer, size_t size, size_t *actual_size);
64static void set_port_state(hub_port_t *, hub_port_state_t);
65static void clear_port_status_change_nl(hub_port_t *, uint16_t);
66static void set_port_state_nl(hub_port_t *, hub_port_state_t);
67static int get_port_status(uint16_t portindex);
68
69/** Callback for GET_DESCRIPTOR request. */
70static int on_get_descriptor(struct usbvirt_device *dev,
71 usb_device_request_setup_packet_t *request, uint8_t *data)
72{
73 if (request->value_high == USB_DESCTYPE_HUB) {
74 int rc = dev->control_transfer_reply(dev, 0,
75 &hub_descriptor, hub_descriptor.length);
76
77 return rc;
78 }
79 /* Let the framework handle all the rest. */
80 return EFORWARD;
81}
82
83static void change_all_ports_state(hub_device_t *hub, hub_port_state_t state)
84{
85 size_t i;
86 for (i = 0; i < HUB_PORT_COUNT; i++) {
87 hub_port_t *port = &hub->ports[i];
88 set_port_state(port, state);
89 }
90}
91
92/** Callback when device changes states. */
93static void on_state_change(struct usbvirt_device *dev,
94 usbvirt_device_state_t old_state, usbvirt_device_state_t new_state)
95{
96 switch (new_state) {
97 case USBVIRT_STATE_CONFIGURED:
98 change_all_ports_state(&hub_dev,
99 HUB_PORT_STATE_POWERED_OFF);
100 break;
101 case USBVIRT_STATE_ADDRESS:
102 change_all_ports_state(&hub_dev,
103 HUB_PORT_STATE_NOT_CONFIGURED);
104 break;
105 default:
106 break;
107 }
108}
109
110struct delay_port_state_change {
111 suseconds_t delay;
112 hub_port_state_t old_state;
113 hub_port_state_t new_state;
114 hub_port_t *port;
115};
116
117static int set_port_state_delayed_fibril(void *arg)
118{
119 struct delay_port_state_change *change
120 = (struct delay_port_state_change *) arg;
121
122 async_usleep(change->delay);
123
124 fibril_mutex_lock(&change->port->guard);
125 if (change->port->state == change->old_state) {
126 set_port_state_nl(change->port, change->new_state);
127 }
128 fibril_mutex_unlock(&change->port->guard);
129
130 free(change);
131
132 return EOK;
133}
134
135static void set_port_state_delayed(hub_port_t *port,
136 suseconds_t delay_time,
137 hub_port_state_t old_state, hub_port_state_t new_state)
138{
139 struct delay_port_state_change *change
140 = malloc(sizeof(struct delay_port_state_change));
141 change->port = port;
142 change->delay = delay_time;
143 change->old_state = old_state;
144 change->new_state = new_state;
145 fid_t fibril = fibril_create(set_port_state_delayed_fibril, change);
146 if (fibril == 0) {
147 printf("Failed to create fibril\n");
148 return;
149 }
150 fibril_add_ready(fibril);
151}
152
153/** Change port status and updates status change status fields.
154 */
155void set_port_state(hub_port_t *port, hub_port_state_t state)
156{
157 fibril_mutex_lock(&port->guard);
158 set_port_state_nl(port, state);
159 fibril_mutex_unlock(&port->guard);
160}
161
162void set_port_state_nl(hub_port_t *port, hub_port_state_t state)
163{
164
165 dprintf(2, "setting port %d state to %d (%c) from %c (change=%u)",
166 port->index,
167 state, hub_port_state_as_char(state),
168 hub_port_state_as_char(port->state),
169 (unsigned int) port->status_change);
170
171 if (state == HUB_PORT_STATE_POWERED_OFF) {
172 clear_port_status_change_nl(port, HUB_STATUS_C_PORT_CONNECTION);
173 clear_port_status_change_nl(port, HUB_STATUS_C_PORT_ENABLE);
174 clear_port_status_change_nl(port, HUB_STATUS_C_PORT_RESET);
175 }
176 if (state == HUB_PORT_STATE_RESUMING) {
177 set_port_state_delayed(port, 10*1000,
178 HUB_PORT_STATE_RESUMING, HUB_PORT_STATE_ENABLED);
179 }
180 if (state == HUB_PORT_STATE_RESETTING) {
181 set_port_state_delayed(port, 10*1000,
182 HUB_PORT_STATE_RESETTING, HUB_PORT_STATE_ENABLED);
183 }
184 if ((port->state == HUB_PORT_STATE_RESETTING)
185 && (state == HUB_PORT_STATE_ENABLED)) {
186 set_port_status_change_nl(port, HUB_STATUS_C_PORT_RESET);
187 }
188
189 port->state = state;
190}
191
192/** Get access to a port or return with EINVAL. */
193#define _GET_PORT(portvar, index) \
194 do { \
195 if (virthub_dev.state != USBVIRT_STATE_CONFIGURED) { \
196 return EINVAL; \
197 } \
198 if (((index) == 0) || ((index) > HUB_PORT_COUNT)) { \
199 return EINVAL; \
200 } \
201 } while (false); \
202 hub_port_t *portvar = &hub_dev.ports[index-1]
203
204
205static int clear_hub_feature(uint16_t feature)
206{
207 return ENOTSUP;
208}
209
210static int clear_port_feature(uint16_t feature, uint16_t portindex)
211{
212 _GET_PORT(port, portindex);
213
214 fibril_mutex_lock(&port->guard);
215 int rc = ENOTSUP;
216
217 switch (feature) {
218 case USB_HUB_FEATURE_PORT_ENABLE:
219 if ((port->state != HUB_PORT_STATE_NOT_CONFIGURED)
220 && (port->state != HUB_PORT_STATE_POWERED_OFF)) {
221 set_port_state_nl(port, HUB_PORT_STATE_DISABLED);
222 }
223 rc = EOK;
224 break;
225
226 case USB_HUB_FEATURE_PORT_SUSPEND:
227 if (port->state != HUB_PORT_STATE_SUSPENDED) {
228 rc = EOK;
229 break;
230 }
231 set_port_state_nl(port, HUB_PORT_STATE_RESUMING);
232 rc = EOK;
233 break;
234
235 case USB_HUB_FEATURE_PORT_POWER:
236 if (port->state != HUB_PORT_STATE_NOT_CONFIGURED) {
237 set_port_state_nl(port, HUB_PORT_STATE_POWERED_OFF);
238 }
239 rc = EOK;
240 break;
241
242 case USB_HUB_FEATURE_C_PORT_CONNECTION:
243 clear_port_status_change_nl(port, HUB_STATUS_C_PORT_CONNECTION);
244 rc = EOK;
245 break;
246
247 case USB_HUB_FEATURE_C_PORT_ENABLE:
248 clear_port_status_change_nl(port, HUB_STATUS_C_PORT_ENABLE);
249 rc = EOK;
250 break;
251
252 case USB_HUB_FEATURE_C_PORT_SUSPEND:
253 clear_port_status_change_nl(port, HUB_STATUS_C_PORT_SUSPEND);
254 rc = EOK;
255 break;
256
257 case USB_HUB_FEATURE_C_PORT_OVER_CURRENT:
258 clear_port_status_change_nl(port, HUB_STATUS_C_PORT_OVER_CURRENT);
259 rc = EOK;
260 break;
261
262 case USB_HUB_FEATURE_C_PORT_RESET:
263 clear_port_status_change_nl(port, HUB_STATUS_C_PORT_RESET);
264 rc = EOK;
265 break;
266 }
267
268 fibril_mutex_unlock(&port->guard);
269
270 return rc;
271}
272
273static int get_bus_state(uint16_t portindex)
274{
275 return ENOTSUP;
276}
277
278static int get_hub_descriptor(struct usbvirt_device *dev,
279 uint8_t descriptor_index,
280 uint8_t descriptor_type, uint16_t length)
281{
282 if (descriptor_type == USB_DESCTYPE_HUB) {
283 int rc = dev->control_transfer_reply(dev, 0,
284 &hub_descriptor, hub_descriptor.length);
285
286 return rc;
287
288 }
289
290 return ENOTSUP;
291}
292
293static int get_hub_status(void)
294{
295 uint32_t hub_status = 0;
296
297 return virthub_dev.control_transfer_reply(&virthub_dev, 0,
298 &hub_status, 4);
299}
300
301int get_port_status(uint16_t portindex)
302{
303 _GET_PORT(port, portindex);
304
305 fibril_mutex_lock(&port->guard);
306
307 uint32_t status;
308 status = MAKE_BYTE(
309 /* Current connect status. */
310 port->device == NULL ? 0 : 1,
311 /* Port enabled/disabled. */
312 port->state == HUB_PORT_STATE_ENABLED ? 1 : 0,
313 /* Suspend. */
314 (port->state == HUB_PORT_STATE_SUSPENDED)
315 || (port->state == HUB_PORT_STATE_RESUMING) ? 1 : 0,
316 /* Over-current. */
317 0,
318 /* Reset. */
319 port->state == HUB_PORT_STATE_RESETTING ? 1 : 0,
320 /* Reserved. */
321 0, 0, 0)
322
323 | (MAKE_BYTE(
324 /* Port power. */
325 port->state == HUB_PORT_STATE_POWERED_OFF ? 0 : 1,
326 /* Full-speed device. */
327 0,
328 /* Reserved. */
329 0, 0, 0, 0, 0, 0
330 )) << 8;
331
332 status |= (port->status_change << 16);
333
334 fibril_mutex_unlock(&port->guard);
335
336 dprintf(2, "GetPortStatus(port=%d, status=%u)\n", (int)portindex,
337 (unsigned int) status);
338 return virthub_dev.control_transfer_reply(&virthub_dev, 0, &status, 4);
339}
340
341
342static int set_hub_feature(uint16_t feature)
343{
344 return ENOTSUP;
345}
346
347static int set_port_feature(uint16_t feature, uint16_t portindex)
348{
349 _GET_PORT(port, portindex);
350
351 fibril_mutex_lock(&port->guard);
352
353 int rc = ENOTSUP;
354
355 switch (feature) {
356 case USB_HUB_FEATURE_PORT_RESET:
357 if (port->state != HUB_PORT_STATE_POWERED_OFF) {
358 set_port_state_nl(port, HUB_PORT_STATE_RESETTING);
359 }
360 rc = EOK;
361 break;
362
363 case USB_HUB_FEATURE_PORT_SUSPEND:
364 if (port->state == HUB_PORT_STATE_ENABLED) {
365 set_port_state_nl(port, HUB_PORT_STATE_SUSPENDED);
366 }
367 rc = EOK;
368 break;
369
370 case USB_HUB_FEATURE_PORT_POWER:
371 if (port->state == HUB_PORT_STATE_POWERED_OFF) {
372 set_port_state_nl(port, HUB_PORT_STATE_DISCONNECTED);
373 }
374 rc = EOK;
375 break;
376 }
377
378 fibril_mutex_unlock(&port->guard);
379 return rc;
380}
381
382#undef _GET_PORT
383
384
385void clear_port_status_change_nl(hub_port_t *port, uint16_t change)
386{
387 port->status_change &= (~change);
388 dprintf(2, "cleared port %d status change %d (%u)", port->index,
389 (int)change, (unsigned int) port->status_change);
390}
391
392void set_port_status_change_nl(hub_port_t *port, uint16_t change)
393{
394 port->status_change |= change;
395 dprintf(2, "set port %d status change %d (%u)", port->index,
396 (int)change, (unsigned int) port->status_change);
397
398}
399
400void clear_port_status_change(hub_port_t *port, uint16_t change)
401{
402 fibril_mutex_lock(&port->guard);
403 clear_port_status_change_nl(port, change);
404 fibril_mutex_unlock(&port->guard);
405}
406
407void set_port_status_change(hub_port_t *port, uint16_t change)
408{
409 fibril_mutex_lock(&port->guard);
410 set_port_status_change_nl(port, change);
411 fibril_mutex_unlock(&port->guard);
412}
413
414/** Callback for data request. */
415static int on_data_request(struct usbvirt_device *dev,
416 usb_endpoint_t endpoint,
417 void *buffer, size_t size, size_t *actual_size)
418{
419 if (endpoint != HUB_STATUS_CHANGE_PIPE) {
420 return EINVAL;
421 }
422
423 uint8_t change_map = 0;
424
425 size_t i;
426 for (i = 0; i < HUB_PORT_COUNT; i++) {
427 hub_port_t *port = &hub_dev.ports[i];
428
429 fibril_mutex_lock(&port->guard);
430 if (port->status_change != 0) {
431 change_map |= (1 << (i + 1));
432 }
433 fibril_mutex_unlock(&port->guard);
434 }
435
436 uint8_t *b = (uint8_t *) buffer;
437 if (size > 0) {
438 *b = change_map;
439 *actual_size = 1;
440 }
441
442 return EOK;
443}
444
445
446
447static int req_clear_hub_feature(usbvirt_device_t *dev,
448 usb_device_request_setup_packet_t *request,
449 uint8_t *data)
450{
451 return clear_hub_feature(request->value);
452}
453
454static int req_clear_port_feature(usbvirt_device_t *dev,
455 usb_device_request_setup_packet_t *request,
456 uint8_t *data)
457{
458 return clear_port_feature(request->value, request->index);
459}
460
461static int req_get_bus_state(usbvirt_device_t *dev,
462 usb_device_request_setup_packet_t *request,
463 uint8_t *data)
464{
465 return get_bus_state(request->index);
466}
467
468static int req_get_hub_descriptor(usbvirt_device_t *dev,
469 usb_device_request_setup_packet_t *request,
470 uint8_t *data)
471{
472 return get_hub_descriptor(dev, request->value_low,
473 request->value_high, request->length);
474}
475
476static int req_get_hub_status(usbvirt_device_t *dev,
477 usb_device_request_setup_packet_t *request,
478 uint8_t *data)
479{
480 return get_hub_status();
481}
482
483static int req_get_port_status(usbvirt_device_t *dev,
484 usb_device_request_setup_packet_t *request,
485 uint8_t *data)
486{
487 return get_port_status(request->index);
488}
489
490static int req_set_hub_feature(usbvirt_device_t *dev,
491 usb_device_request_setup_packet_t *request,
492 uint8_t *data)
493{
494 return set_hub_feature(request->value);
495}
496
497static int req_set_port_feature(usbvirt_device_t *dev,
498 usb_device_request_setup_packet_t *request,
499 uint8_t *data)
500{
501 return set_port_feature(request->value, request->index);
502}
503
504#define CLASS_REQ_IN(recipient) \
505 USBVIRT_MAKE_CONTROL_REQUEST_TYPE(USB_DIRECTION_IN, \
506 USBVIRT_REQUEST_TYPE_CLASS, recipient)
507#define CLASS_REQ_OUT(recipient) \
508 USBVIRT_MAKE_CONTROL_REQUEST_TYPE(USB_DIRECTION_OUT, \
509 USBVIRT_REQUEST_TYPE_CLASS, recipient)
510
511#define REC_OTHER USBVIRT_REQUEST_RECIPIENT_OTHER
512#define REC_DEVICE USBVIRT_REQUEST_RECIPIENT_DEVICE
513#define DIR_IN USB_DIRECTION_IN
514#define DIR_OUT USB_DIRECTION_OUT
515
516#define CLASS_REQ(direction, recipient, req) \
517 .request_type = USBVIRT_MAKE_CONTROL_REQUEST_TYPE(direction, \
518 USBVIRT_REQUEST_TYPE_CLASS, recipient), \
519 .request = req
520
521#define STD_REQ(direction, recipient, req) \
522 .request_type = USBVIRT_MAKE_CONTROL_REQUEST_TYPE(direction, \
523 USBVIRT_REQUEST_TYPE_STANDARD, recipient), \
524 .request = req
525
526/** Hub operations on control endpoint zero. */
527static usbvirt_control_transfer_handler_t endpoint_zero_handlers[] = {
528 {
529 STD_REQ(DIR_IN, REC_DEVICE, USB_DEVREQ_GET_DESCRIPTOR),
530 .name = "GetDescriptor",
531 .callback = on_get_descriptor
532 },
533 {
534 CLASS_REQ(DIR_IN, REC_DEVICE, USB_DEVREQ_GET_DESCRIPTOR),
535 .name = "GetDescriptor",
536 .callback = on_get_descriptor
537 },
538 {
539 CLASS_REQ(DIR_IN, REC_OTHER, USB_HUB_REQUEST_GET_STATUS),
540 .name = "GetPortStatus",
541 .callback = req_get_port_status
542 },
543 {
544 CLASS_REQ(DIR_OUT, REC_DEVICE, USB_HUB_REQUEST_CLEAR_FEATURE),
545 .name = "ClearHubFeature",
546 .callback = req_clear_hub_feature
547 },
548 {
549 CLASS_REQ(DIR_OUT, REC_OTHER, USB_HUB_REQUEST_CLEAR_FEATURE),
550 .name = "ClearPortFeature",
551 .callback = req_clear_port_feature
552 },
553 {
554 CLASS_REQ(DIR_IN, REC_OTHER, USB_HUB_REQUEST_GET_STATE),
555 .name = "GetBusState",
556 .callback = req_get_bus_state
557 },
558 {
559 CLASS_REQ(DIR_IN, REC_DEVICE, USB_HUB_REQUEST_GET_DESCRIPTOR),
560 .name = "GetHubDescriptor",
561 .callback = req_get_hub_descriptor
562 },
563 {
564 CLASS_REQ(DIR_IN, REC_DEVICE, USB_HUB_REQUEST_GET_STATUS),
565 .name = "GetHubStatus",
566 .callback = req_get_hub_status
567 },
568 {
569 CLASS_REQ(DIR_IN, REC_OTHER, USB_HUB_REQUEST_GET_STATUS),
570 .name = "GetPortStatus",
571 .callback = req_get_port_status
572 },
573 {
574 CLASS_REQ(DIR_OUT, REC_DEVICE, USB_HUB_REQUEST_SET_FEATURE),
575 .name = "SetHubFeature",
576 .callback = req_set_hub_feature
577 },
578 {
579 CLASS_REQ(DIR_OUT, REC_OTHER, USB_HUB_REQUEST_SET_FEATURE),
580 .name = "SetPortFeature",
581 .callback = req_set_port_feature
582 },
583 USBVIRT_CONTROL_TRANSFER_HANDLER_LAST
584};
585
586
587/** Hub operations. */
588usbvirt_device_ops_t hub_ops = {
589 .control_transfer_handlers = endpoint_zero_handlers,
590 .on_data = NULL,
591 .on_data_request = on_data_request,
592 .on_state_change = on_state_change,
593};
594
595/**
596 * @}
597 */
Note: See TracBrowser for help on using the repository browser.