source: mainline/uspace/drv/vhc/hubops.c@ 10096231

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 10096231 was 10096231, checked in by Matus Dekanek <smekideki@…>, 15 years ago

hub is successfully initialized

  • Property mode set to 100644
File size: 9.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_class_request(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);
66
67/** Standard USB requests. */
68static usbvirt_standard_device_request_ops_t standard_request_ops = {
69 .on_get_status = NULL,
70 .on_clear_feature = NULL,
71 .on_set_feature = NULL,
72 .on_set_address = NULL,
73 .on_get_descriptor = on_get_descriptor,
74 .on_set_descriptor = NULL,
75 .on_get_configuration = NULL,
76 .on_set_configuration = NULL,
77 .on_get_interface = NULL,
78 .on_set_interface = NULL,
79 .on_synch_frame = NULL
80};
81
82/** Hub operations. */
83usbvirt_device_ops_t hub_ops = {
84 .standard_request_ops = &standard_request_ops,
85 .on_class_device_request = on_class_request,
86 .on_data = NULL,
87 .on_data_request = on_data_request
88};
89
90/** Callback for GET_DESCRIPTOR request. */
91static int on_get_descriptor(struct usbvirt_device *dev,
92 usb_device_request_setup_packet_t *request, uint8_t *data)
93{
94 if (request->value_high == USB_DESCTYPE_HUB) {
95 int rc = dev->control_transfer_reply(dev, 0,
96 &hub_descriptor, hub_descriptor.length);
97
98 return rc;
99 }
100 /* Let the framework handle all the rest. */
101 return EFORWARD;
102}
103
104/** Change port status and updates status change status fields.
105 */
106static void set_port_state(hub_port_t *port, hub_port_state_t state)
107{
108 port->state = state;
109 if (state == HUB_PORT_STATE_POWERED_OFF) {
110 clear_port_status_change(port, HUB_STATUS_C_PORT_CONNECTION);
111 clear_port_status_change(port, HUB_STATUS_C_PORT_ENABLE);
112 clear_port_status_change(port, HUB_STATUS_C_PORT_RESET);
113 }
114 if (state == HUB_PORT_STATE_RESUMING) {
115 async_usleep(10*1000);
116 if (port->state == state) {
117 set_port_state(port, HUB_PORT_STATE_ENABLED);
118 }
119 }
120 if (state == HUB_PORT_STATE_RESETTING) {
121 async_usleep(10*1000);
122 if (port->state == state) {
123 set_port_status_change(port, HUB_STATUS_C_PORT_RESET);
124 set_port_state(port, HUB_PORT_STATE_ENABLED);
125 }
126 }
127}
128
129/** Get access to a port or return with EINVAL. */
130#define _GET_PORT(portvar, index) \
131 do { \
132 if (virthub_dev.state != USBVIRT_STATE_CONFIGURED) { \
133 return EINVAL; \
134 } \
135 if (((index) == 0) || ((index) > HUB_PORT_COUNT)) { \
136 return EINVAL; \
137 } \
138 } while (false); \
139 hub_port_t *portvar = &hub_dev.ports[index]
140
141
142static int clear_hub_feature(uint16_t feature)
143{
144 return ENOTSUP;
145}
146
147static int clear_port_feature(uint16_t feature, uint16_t portindex)
148{
149 _GET_PORT(port, portindex);
150
151 switch (feature) {
152 case USB_HUB_FEATURE_PORT_ENABLE:
153 if ((port->state != HUB_PORT_STATE_NOT_CONFIGURED)
154 && (port->state != HUB_PORT_STATE_POWERED_OFF)) {
155 set_port_state(port, HUB_PORT_STATE_DISABLED);
156 }
157 return EOK;
158
159 case USB_HUB_FEATURE_PORT_SUSPEND:
160 if (port->state != HUB_PORT_STATE_SUSPENDED) {
161 return EOK;
162 }
163 set_port_state(port, HUB_PORT_STATE_RESUMING);
164 return EOK;
165
166 case USB_HUB_FEATURE_PORT_POWER:
167 if (port->state != HUB_PORT_STATE_NOT_CONFIGURED) {
168 set_port_state(port, HUB_PORT_STATE_POWERED_OFF);
169 }
170 return EOK;
171
172 case USB_HUB_FEATURE_C_PORT_CONNECTION:
173 clear_port_status_change(port, HUB_STATUS_C_PORT_CONNECTION);
174 return EOK;
175
176 case USB_HUB_FEATURE_C_PORT_ENABLE:
177 clear_port_status_change(port, HUB_STATUS_C_PORT_ENABLE);
178 return EOK;
179
180 case USB_HUB_FEATURE_C_PORT_SUSPEND:
181 clear_port_status_change(port, HUB_STATUS_C_PORT_SUSPEND);
182 return EOK;
183
184 case USB_HUB_FEATURE_C_PORT_OVER_CURRENT:
185 clear_port_status_change(port, HUB_STATUS_C_PORT_OVER_CURRENT);
186 return EOK;
187 }
188
189 return ENOTSUP;
190}
191
192static int get_bus_state(uint16_t portindex)
193{
194 return ENOTSUP;
195}
196
197static int get_hub_descriptor(struct usbvirt_device *dev,
198 uint8_t descriptor_index,
199 uint8_t descriptor_type, uint16_t length)
200{
201 if (descriptor_type == USB_DESCTYPE_HUB) {
202 int rc = dev->control_transfer_reply(dev, 0,
203 &hub_descriptor, hub_descriptor.length);
204
205 return rc;
206
207 }
208
209 return ENOTSUP;
210}
211
212static int get_hub_status(void)
213{
214 uint32_t hub_status = 0;
215
216 return virthub_dev.control_transfer_reply(&virthub_dev, 0,
217 &hub_status, 4);
218}
219
220static int get_port_status(uint16_t portindex)
221{
222 _GET_PORT(port, portindex);
223
224 uint32_t status;
225 status = MAKE_BYTE(
226 /* Current connect status. */
227 port->device == NULL ? 0 : 1,
228 /* Port enabled/disabled. */
229 port->state == HUB_PORT_STATE_ENABLED ? 1 : 0,
230 /* Suspend. */
231 (port->state == HUB_PORT_STATE_SUSPENDED)
232 || (port->state == HUB_PORT_STATE_RESUMING) ? 1 : 0,
233 /* Over-current. */
234 0,
235 /* Reset. */
236 port->state == HUB_PORT_STATE_RESETTING ? 1 : 0,
237 /* Reserved. */
238 0, 0, 0)
239
240 | (MAKE_BYTE(
241 /* Port power. */
242 port->state == HUB_PORT_STATE_POWERED_OFF ? 0 : 1,
243 /* Full-speed device. */
244 0,
245 /* Reserved. */
246 0, 0, 0, 0, 0, 0
247 )) << 8;
248
249 status |= (port->status_change << 16);
250
251 return virthub_dev.control_transfer_reply(&virthub_dev, 0, &status, 4);
252}
253
254
255static int set_hub_feature(uint16_t feature)
256{
257 return ENOTSUP;
258}
259
260static int set_port_feature(uint16_t feature, uint16_t portindex)
261{
262 _GET_PORT(port, portindex);
263
264 switch (feature) {
265 case USB_HUB_FEATURE_PORT_RESET:
266 if (port->state != HUB_PORT_STATE_POWERED_OFF) {
267 set_port_state(port, HUB_PORT_STATE_RESETTING);
268 }
269 return EOK;
270
271 case USB_HUB_FEATURE_PORT_SUSPEND:
272 if (port->state == HUB_PORT_STATE_ENABLED) {
273 set_port_state(port, HUB_PORT_STATE_SUSPENDED);
274 }
275 return EOK;
276
277 case USB_HUB_FEATURE_PORT_POWER:
278 if (port->state == HUB_PORT_STATE_POWERED_OFF) {
279 set_port_state(port, HUB_PORT_STATE_DISCONNECTED);
280 }
281 return EOK;
282 }
283 return ENOTSUP;
284}
285
286#undef _GET_PORT
287
288
289/** Callback for class request. */
290static int on_class_request(struct usbvirt_device *dev,
291 usb_device_request_setup_packet_t *request, uint8_t *data)
292{
293 dprintf(2, "hub class request (%d)\n", (int) request->request);
294
295 uint8_t recipient = request->request_type & 31;
296 uint8_t direction = request->request_type >> 7;
297
298#define _VERIFY(cond) \
299 do { \
300 if (!(cond)) { \
301 dprintf(0, "WARN: invalid class request (%s not met).\n", \
302 NAME, #cond); \
303 return EINVAL; \
304 } \
305 } while (0)
306
307 switch (request->request) {
308 case USB_HUB_REQUEST_CLEAR_FEATURE:
309 _VERIFY(direction == 0);
310 _VERIFY(request->length == 0);
311 if (recipient == 0) {
312 _VERIFY(request->index == 0);
313 return clear_hub_feature(request->value);
314 } else {
315 _VERIFY(recipient == 3);
316 return clear_port_feature(request->value,
317 request->index);
318 }
319
320 case USB_HUB_REQUEST_GET_STATE:
321 return get_bus_state(request->index);
322
323 case USB_HUB_REQUEST_GET_DESCRIPTOR:
324 return get_hub_descriptor(dev, request->value_low,
325 request->value_high, request->length);
326
327 case USB_HUB_REQUEST_GET_STATUS:
328 if (recipient == 0) {
329 return get_hub_status();
330 } else {
331 return get_port_status(request->index);
332 }
333
334 case USB_HUB_REQUEST_SET_FEATURE:
335 if (recipient == 0) {
336 return set_hub_feature(request->value);
337 } else {
338 return set_port_feature(request->value, request->index);
339 }
340
341 default:
342 break;
343 }
344
345#undef _VERIFY
346
347
348 return EOK;
349}
350
351void clear_port_status_change(hub_port_t *port, uint16_t change)
352{
353 port->status_change &= (~change);
354}
355
356void set_port_status_change(hub_port_t *port, uint16_t change)
357{
358 port->status_change |= change;
359}
360
361/** Callback for data request. */
362static int on_data_request(struct usbvirt_device *dev,
363 usb_endpoint_t endpoint,
364 void *buffer, size_t size, size_t *actual_size)
365{
366 if (endpoint != HUB_STATUS_CHANGE_PIPE) {
367 return EINVAL;
368 }
369
370 uint8_t change_map = 0;
371
372 size_t i;
373 for (i = 0; i < HUB_PORT_COUNT; i++) {
374 hub_port_t *port = &hub_dev.ports[i];
375
376 if (port->status_change != 0) {
377 change_map |= (1 << (i + 1));
378 }
379 }
380
381 uint8_t *b = (uint8_t *) buffer;
382 if (size > 0) {
383 *b = change_map;
384 *actual_size = 1;
385 }
386
387 return EOK;
388}
389
390
391/**
392 * @}
393 */
Note: See TracBrowser for help on using the repository browser.