source: mainline/uspace/drv/vhc/hubops.c@ 138a7fd

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

Fixes & improvements in virtual hub

The changes includes:

  • unified (a bit) debugging output
  • port is put into power-off state after SET_CONFIGURATION request
  • less ports on the hub
  • delayed port changes run in separate fibril
  • shorter waiting for transactions in HC scheduling manager
  • Property mode set to 100644
File size: 11.8 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_class_request(struct usbvirt_device *dev,
64 usb_device_request_setup_packet_t *request, uint8_t *data);
65static int on_data_request(struct usbvirt_device *dev,
66 usb_endpoint_t endpoint,
67 void *buffer, size_t size, size_t *actual_size);
68static void set_port_state(hub_port_t *, hub_port_state_t);
69
70/** Standard USB requests. */
71static usbvirt_standard_device_request_ops_t standard_request_ops = {
72 .on_get_status = NULL,
73 .on_clear_feature = NULL,
74 .on_set_feature = NULL,
75 .on_set_address = NULL,
76 .on_get_descriptor = on_get_descriptor,
77 .on_set_descriptor = NULL,
78 .on_get_configuration = NULL,
79 .on_set_configuration = on_set_configuration,
80 .on_get_interface = NULL,
81 .on_set_interface = NULL,
82 .on_synch_frame = NULL
83};
84
85/** Hub operations. */
86usbvirt_device_ops_t hub_ops = {
87 .standard_request_ops = &standard_request_ops,
88 .on_class_device_request = on_class_request,
89 .on_data = NULL,
90 .on_data_request = on_data_request
91};
92
93/** Callback for GET_DESCRIPTOR request. */
94static int on_get_descriptor(struct usbvirt_device *dev,
95 usb_device_request_setup_packet_t *request, uint8_t *data)
96{
97 if (request->value_high == USB_DESCTYPE_HUB) {
98 int rc = dev->control_transfer_reply(dev, 0,
99 &hub_descriptor, hub_descriptor.length);
100
101 return rc;
102 }
103 /* Let the framework handle all the rest. */
104 return EFORWARD;
105}
106
107/** Callback for SET_CONFIGURATION request. */
108int on_set_configuration(struct usbvirt_device *dev,
109 usb_device_request_setup_packet_t *request, uint8_t *data)
110{
111 /* We must suspend power source to all ports. */
112 size_t i;
113 for (i = 0; i < HUB_PORT_COUNT; i++) {
114 hub_port_t *port = &hub_dev.ports[i];
115
116 set_port_state(port, HUB_PORT_STATE_POWERED_OFF);
117 }
118
119 /* Let the framework handle the rest of the job. */
120 return EFORWARD;
121}
122
123struct delay_port_state_change {
124 suseconds_t delay;
125 hub_port_state_t old_state;
126 hub_port_state_t new_state;
127 hub_port_t *port;
128};
129
130static int set_port_state_delayed_fibril(void *arg)
131{
132 struct delay_port_state_change *change
133 = (struct delay_port_state_change *) arg;
134
135 async_usleep(change->delay);
136
137 if (change->port->state == change->old_state) {
138 set_port_state(change->port, change->new_state);
139 }
140
141 free(change);
142
143 return EOK;
144}
145
146static void set_port_state_delayed(hub_port_t *port,
147 suseconds_t delay_time,
148 hub_port_state_t old_state, hub_port_state_t new_state)
149{
150 struct delay_port_state_change *change
151 = malloc(sizeof(struct delay_port_state_change));
152 change->port = port;
153 change->delay = delay_time;
154 change->old_state = old_state;
155 change->new_state = new_state;
156 fid_t fibril = fibril_create(set_port_state_delayed_fibril, change);
157 if (fibril == 0) {
158 printf("Failed to create fibril\n");
159 return;
160 }
161 fibril_add_ready(fibril);
162}
163
164/** Change port status and updates status change status fields.
165 */
166void set_port_state(hub_port_t *port, hub_port_state_t state)
167{
168 dprintf(1, "setting port %d state to %d (%c)", port->index,
169 state, hub_port_state_as_char(state));
170
171 if (state == HUB_PORT_STATE_POWERED_OFF) {
172 clear_port_status_change(port, HUB_STATUS_C_PORT_CONNECTION);
173 clear_port_status_change(port, HUB_STATUS_C_PORT_ENABLE);
174 clear_port_status_change(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(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]
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 switch (feature) {
215 case USB_HUB_FEATURE_PORT_ENABLE:
216 if ((port->state != HUB_PORT_STATE_NOT_CONFIGURED)
217 && (port->state != HUB_PORT_STATE_POWERED_OFF)) {
218 set_port_state(port, HUB_PORT_STATE_DISABLED);
219 }
220 return EOK;
221
222 case USB_HUB_FEATURE_PORT_SUSPEND:
223 if (port->state != HUB_PORT_STATE_SUSPENDED) {
224 return EOK;
225 }
226 set_port_state(port, HUB_PORT_STATE_RESUMING);
227 return EOK;
228
229 case USB_HUB_FEATURE_PORT_POWER:
230 if (port->state != HUB_PORT_STATE_NOT_CONFIGURED) {
231 set_port_state(port, HUB_PORT_STATE_POWERED_OFF);
232 }
233 return EOK;
234
235 case USB_HUB_FEATURE_C_PORT_CONNECTION:
236 clear_port_status_change(port, HUB_STATUS_C_PORT_CONNECTION);
237 return EOK;
238
239 case USB_HUB_FEATURE_C_PORT_ENABLE:
240 clear_port_status_change(port, HUB_STATUS_C_PORT_ENABLE);
241 return EOK;
242
243 case USB_HUB_FEATURE_C_PORT_SUSPEND:
244 clear_port_status_change(port, HUB_STATUS_C_PORT_SUSPEND);
245 return EOK;
246
247 case USB_HUB_FEATURE_C_PORT_OVER_CURRENT:
248 clear_port_status_change(port, HUB_STATUS_C_PORT_OVER_CURRENT);
249 return EOK;
250 }
251
252 return ENOTSUP;
253}
254
255static int get_bus_state(uint16_t portindex)
256{
257 return ENOTSUP;
258}
259
260static int get_hub_descriptor(struct usbvirt_device *dev,
261 uint8_t descriptor_index,
262 uint8_t descriptor_type, uint16_t length)
263{
264 if (descriptor_type == USB_DESCTYPE_HUB) {
265 int rc = dev->control_transfer_reply(dev, 0,
266 &hub_descriptor, hub_descriptor.length);
267
268 return rc;
269
270 }
271
272 return ENOTSUP;
273}
274
275static int get_hub_status(void)
276{
277 uint32_t hub_status = 0;
278
279 return virthub_dev.control_transfer_reply(&virthub_dev, 0,
280 &hub_status, 4);
281}
282
283static int get_port_status(uint16_t portindex)
284{
285 _GET_PORT(port, portindex);
286
287 uint32_t status;
288 status = MAKE_BYTE(
289 /* Current connect status. */
290 port->device == NULL ? 0 : 1,
291 /* Port enabled/disabled. */
292 port->state == HUB_PORT_STATE_ENABLED ? 1 : 0,
293 /* Suspend. */
294 (port->state == HUB_PORT_STATE_SUSPENDED)
295 || (port->state == HUB_PORT_STATE_RESUMING) ? 1 : 0,
296 /* Over-current. */
297 0,
298 /* Reset. */
299 port->state == HUB_PORT_STATE_RESETTING ? 1 : 0,
300 /* Reserved. */
301 0, 0, 0)
302
303 | (MAKE_BYTE(
304 /* Port power. */
305 port->state == HUB_PORT_STATE_POWERED_OFF ? 0 : 1,
306 /* Full-speed device. */
307 0,
308 /* Reserved. */
309 0, 0, 0, 0, 0, 0
310 )) << 8;
311
312 status |= (port->status_change << 16);
313
314 dprintf(2, "GetPortStatus(port=%d, status=%u)\n", (int)portindex,
315 (unsigned int) status);
316 return virthub_dev.control_transfer_reply(&virthub_dev, 0, &status, 4);
317}
318
319
320static int set_hub_feature(uint16_t feature)
321{
322 return ENOTSUP;
323}
324
325static int set_port_feature(uint16_t feature, uint16_t portindex)
326{
327 _GET_PORT(port, portindex);
328
329 switch (feature) {
330 case USB_HUB_FEATURE_PORT_RESET:
331 if (port->state != HUB_PORT_STATE_POWERED_OFF) {
332 set_port_state(port, HUB_PORT_STATE_RESETTING);
333 }
334 return EOK;
335
336 case USB_HUB_FEATURE_PORT_SUSPEND:
337 if (port->state == HUB_PORT_STATE_ENABLED) {
338 set_port_state(port, HUB_PORT_STATE_SUSPENDED);
339 }
340 return EOK;
341
342 case USB_HUB_FEATURE_PORT_POWER:
343 if (port->state == HUB_PORT_STATE_POWERED_OFF) {
344 set_port_state(port, HUB_PORT_STATE_DISCONNECTED);
345 }
346 return EOK;
347 }
348 return ENOTSUP;
349}
350
351#undef _GET_PORT
352
353
354/** Callback for class request. */
355static int on_class_request(struct usbvirt_device *dev,
356 usb_device_request_setup_packet_t *request, uint8_t *data)
357{
358 dprintf(2, "hub class request (%d)", (int) request->request);
359
360 uint8_t recipient = request->request_type & 31;
361 uint8_t direction = request->request_type >> 7;
362
363#define _VERIFY(cond) \
364 do { \
365 if (!(cond)) { \
366 dprintf(0, "WARN: invalid class request (%s not met).\n", \
367 NAME, #cond); \
368 return EINVAL; \
369 } \
370 } while (0)
371
372 switch (request->request) {
373 case USB_HUB_REQUEST_CLEAR_FEATURE:
374 _VERIFY(direction == 0);
375 _VERIFY(request->length == 0);
376 if (recipient == 0) {
377 _VERIFY(request->index == 0);
378 return clear_hub_feature(request->value);
379 } else {
380 _VERIFY(recipient == 3);
381 return clear_port_feature(request->value,
382 request->index);
383 }
384
385 case USB_HUB_REQUEST_GET_STATE:
386 return get_bus_state(request->index);
387
388 case USB_HUB_REQUEST_GET_DESCRIPTOR:
389 return get_hub_descriptor(dev, request->value_low,
390 request->value_high, request->length);
391
392 case USB_HUB_REQUEST_GET_STATUS:
393 if (recipient == 0) {
394 return get_hub_status();
395 } else {
396 return get_port_status(request->index);
397 }
398
399 case USB_HUB_REQUEST_SET_FEATURE:
400 if (recipient == 0) {
401 return set_hub_feature(request->value);
402 } else {
403 return set_port_feature(request->value, request->index);
404 }
405
406 default:
407 dprintf(0, "WARN: unknown request (%d)!\n",
408 request->request);
409 break;
410 }
411
412#undef _VERIFY
413
414
415 return EOK;
416}
417
418void clear_port_status_change(hub_port_t *port, uint16_t change)
419{
420 port->status_change &= (~change);
421}
422
423void set_port_status_change(hub_port_t *port, uint16_t change)
424{
425 port->status_change |= change;
426}
427
428/** Callback for data request. */
429static int on_data_request(struct usbvirt_device *dev,
430 usb_endpoint_t endpoint,
431 void *buffer, size_t size, size_t *actual_size)
432{
433 if (endpoint != HUB_STATUS_CHANGE_PIPE) {
434 return EINVAL;
435 }
436
437 uint8_t change_map = 0;
438
439 size_t i;
440 for (i = 0; i < HUB_PORT_COUNT; i++) {
441 hub_port_t *port = &hub_dev.ports[i];
442
443 if (port->status_change != 0) {
444 change_map |= (1 << (i + 1));
445 }
446 }
447
448 uint8_t *b = (uint8_t *) buffer;
449 if (size > 0) {
450 *b = change_map;
451 *actual_size = 1;
452 }
453
454 return EOK;
455}
456
457
458/**
459 * @}
460 */
Note: See TracBrowser for help on using the repository browser.