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
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_set_configuration(struct usbvirt_device *dev,
62 usb_device_request_setup_packet_t *request, uint8_t *data);
63static int on_data_request(struct usbvirt_device *dev,
64 usb_endpoint_t endpoint,
65 void *buffer, size_t size, size_t *actual_size);
66static void set_port_state(hub_port_t *, hub_port_state_t);
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);
69static int get_port_status(uint16_t portindex);
70
71/** Callback for GET_DESCRIPTOR request. */
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) {
76 int rc = dev->control_transfer_reply(dev, 0,
77 &hub_descriptor, hub_descriptor.length);
78
79 return rc;
80 }
81 /* Let the framework handle all the rest. */
82 return EFORWARD;
83}
84
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
115 fibril_mutex_lock(&change->port->guard);
116 if (change->port->state == change->old_state) {
117 set_port_state_nl(change->port, change->new_state);
118 }
119 fibril_mutex_unlock(&change->port->guard);
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
144/** Change port status and updates status change status fields.
145 */
146void set_port_state(hub_port_t *port, hub_port_state_t state)
147{
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);
161
162 if (state == HUB_PORT_STATE_POWERED_OFF) {
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);
166 }
167 if (state == HUB_PORT_STATE_RESUMING) {
168 set_port_state_delayed(port, 10*1000,
169 HUB_PORT_STATE_RESUMING, HUB_PORT_STATE_ENABLED);
170 }
171 if (state == HUB_PORT_STATE_RESETTING) {
172 set_port_state_delayed(port, 10*1000,
173 HUB_PORT_STATE_RESETTING, HUB_PORT_STATE_ENABLED);
174 }
175 if ((port->state == HUB_PORT_STATE_RESETTING)
176 && (state == HUB_PORT_STATE_ENABLED)) {
177 set_port_status_change_nl(port, HUB_STATUS_C_PORT_RESET);
178 }
179
180 port->state = state;
181}
182
183/** Get access to a port or return with EINVAL. */
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); \
193 hub_port_t *portvar = &hub_dev.ports[index-1]
194
195
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)
202{
203 _GET_PORT(port, portindex);
204
205 fibril_mutex_lock(&port->guard);
206 int rc = ENOTSUP;
207
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)) {
212 set_port_state_nl(port, HUB_PORT_STATE_DISABLED);
213 }
214 rc = EOK;
215 break;
216
217 case USB_HUB_FEATURE_PORT_SUSPEND:
218 if (port->state != HUB_PORT_STATE_SUSPENDED) {
219 rc = EOK;
220 break;
221 }
222 set_port_state_nl(port, HUB_PORT_STATE_RESUMING);
223 rc = EOK;
224 break;
225
226 case USB_HUB_FEATURE_PORT_POWER:
227 if (port->state != HUB_PORT_STATE_NOT_CONFIGURED) {
228 set_port_state_nl(port, HUB_PORT_STATE_POWERED_OFF);
229 }
230 rc = EOK;
231 break;
232
233 case USB_HUB_FEATURE_C_PORT_CONNECTION:
234 clear_port_status_change_nl(port, HUB_STATUS_C_PORT_CONNECTION);
235 rc = EOK;
236 break;
237
238 case USB_HUB_FEATURE_C_PORT_ENABLE:
239 clear_port_status_change_nl(port, HUB_STATUS_C_PORT_ENABLE);
240 rc = EOK;
241 break;
242
243 case USB_HUB_FEATURE_C_PORT_SUSPEND:
244 clear_port_status_change_nl(port, HUB_STATUS_C_PORT_SUSPEND);
245 rc = EOK;
246 break;
247
248 case USB_HUB_FEATURE_C_PORT_OVER_CURRENT:
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;
257 }
258
259 fibril_mutex_unlock(&port->guard);
260
261 return rc;
262}
263
264static int get_bus_state(uint16_t portindex)
265{
266 return ENOTSUP;
267}
268
269static int get_hub_descriptor(struct usbvirt_device *dev,
270 uint8_t descriptor_index,
271 uint8_t descriptor_type, uint16_t length)
272{
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
281 return ENOTSUP;
282}
283
284static int get_hub_status(void)
285{
286 uint32_t hub_status = 0;
287
288 return virthub_dev.control_transfer_reply(&virthub_dev, 0,
289 &hub_status, 4);
290}
291
292int get_port_status(uint16_t portindex)
293{
294 _GET_PORT(port, portindex);
295
296 fibril_mutex_lock(&port->guard);
297
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
325 fibril_mutex_unlock(&port->guard);
326
327 dprintf(2, "GetPortStatus(port=%d, status=%u)\n", (int)portindex,
328 (unsigned int) status);
329 return virthub_dev.control_transfer_reply(&virthub_dev, 0, &status, 4);
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{
340 _GET_PORT(port, portindex);
341
342 fibril_mutex_lock(&port->guard);
343
344 int rc = ENOTSUP;
345
346 switch (feature) {
347 case USB_HUB_FEATURE_PORT_RESET:
348 if (port->state != HUB_PORT_STATE_POWERED_OFF) {
349 set_port_state_nl(port, HUB_PORT_STATE_RESETTING);
350 }
351 rc = EOK;
352 break;
353
354 case USB_HUB_FEATURE_PORT_SUSPEND:
355 if (port->state == HUB_PORT_STATE_ENABLED) {
356 set_port_state_nl(port, HUB_PORT_STATE_SUSPENDED);
357 }
358 rc = EOK;
359 break;
360
361 case USB_HUB_FEATURE_PORT_POWER:
362 if (port->state == HUB_PORT_STATE_POWERED_OFF) {
363 set_port_state_nl(port, HUB_PORT_STATE_DISCONNECTED);
364 }
365 rc = EOK;
366 break;
367 }
368
369 fibril_mutex_unlock(&port->guard);
370 return rc;
371}
372
373#undef _GET_PORT
374
375
376void clear_port_status_change_nl(hub_port_t *port, uint16_t change)
377{
378 port->status_change &= (~change);
379 dprintf(2, "cleared port %d status change %d (%u)", port->index,
380 (int)change, (unsigned int) port->status_change);
381}
382
383void set_port_status_change_nl(hub_port_t *port, uint16_t change)
384{
385 port->status_change |= change;
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);
403}
404
405/** Callback for data request. */
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{
410 if (endpoint != HUB_STATUS_CHANGE_PIPE) {
411 return EINVAL;
412 }
413
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
420 fibril_mutex_lock(&port->guard);
421 if (port->status_change != 0) {
422 change_map |= (1 << (i + 1));
423 }
424 fibril_mutex_unlock(&port->guard);
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
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
578/**
579 * @}
580 */
Note: See TracBrowser for help on using the repository browser.