source: mainline/uspace/drv/vhc/hubops.c@ 7feeb84

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

usbvirt: more flexible callbacks for endpoint zero

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