source: mainline/uspace/drv/bus/usb/ehci/ehci_rh.c@ e141281

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since e141281 was 691130cf, checked in by Jan Vesely <jano.vesely@…>, 10 years ago

ehci/rh: Make debug messages more informative

  • Property mode set to 100644
File size: 20.1 KB
Line 
1/*
2 * Copyright (c) 2013 Jan Vesely
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/** @addtogroup drvusbehci
29 * @{
30 */
31/** @file
32 * @brief EHCI driver
33 */
34
35#include <assert.h>
36#include <errno.h>
37#include <mem.h>
38#include <str_error.h>
39#include <sys/types.h>
40
41#include <usb/classes/hub.h>
42#include <usb/debug.h>
43#include <usb/descriptor.h>
44#include <usb/request.h>
45#include <usb/usb.h>
46
47#include <usb/host/endpoint.h>
48#include <usbvirt/device.h>
49
50#include "ehci_rh.h"
51
52enum {
53 HUB_STATUS_CHANGE_PIPE = 1,
54};
55
56static usbvirt_device_ops_t ops;
57
58/** Initialize internal USB HUB class descriptor.
59 * @param instance EHCI root hub.
60 * Use register based info to create accurate descriptor.
61 */
62static void ehci_rh_hub_desc_init(ehci_rh_t *instance, unsigned hcs)
63{
64 assert(instance);
65 const unsigned dsize = sizeof(usb_hub_descriptor_header_t) +
66 STATUS_BYTES(instance->port_count) * 2;
67 assert(dsize <= sizeof(instance->hub_descriptor));
68
69 instance->hub_descriptor.header.length = dsize;
70 instance->hub_descriptor.header.descriptor_type = USB_DESCTYPE_HUB;
71 instance->hub_descriptor.header.port_count = instance->port_count;
72 /* Bits 0,1 indicate power switching mode
73 * Bit 2 indicates device type (compound device)
74 * Bits 3,4 indicate over-current protection mode */
75 instance->hub_descriptor.header.characteristics = 0 |
76 ((hcs & EHCI_CAPS_HCS_PPC_FLAG) ? 0x09 : 0x12) |
77 ((hcs & EHCI_CAPS_HCS_INDICATORS_FLAG) ? 0x80 : 0) |
78 (0x3 << 5); /* Need 32 FS bit times */ // TODO Implement
79 instance->hub_descriptor.header.characteristics_reserved = 0;
80 instance->hub_descriptor.header.power_good_time = 50;
81 /* bHubContrCurrent, root hubs don't need no power. */
82 instance->hub_descriptor.header.max_current = 0;
83
84 /* Device removable and some legacy 1.0 stuff*/
85 instance->hub_descriptor.rempow[0] = 0xff;
86 instance->hub_descriptor.rempow[1] = 0xff;
87 instance->hub_descriptor.rempow[2] = 0xff;
88 instance->hub_descriptor.rempow[3] = 0xff;
89
90}
91/** Initialize EHCI root hub.
92 * @param instance Place to initialize.
93 * @param regs EHCI device registers.
94 * @param name Device name.
95 * return Error code, EOK on success.
96 *
97 * Selects preconfigured port powering mode, sets up descriptor, and
98 * initializes internal virtual hub.
99 */
100int ehci_rh_init(ehci_rh_t *instance, ehci_caps_regs_t *caps, ehci_regs_t *regs,
101 const char *name)
102{
103 assert(instance);
104 instance->registers = regs;
105 instance->port_count =
106 (EHCI_RD(caps->hcsparams) >> EHCI_CAPS_HCS_N_PORTS_SHIFT) &
107 EHCI_CAPS_HCS_N_PORTS_MASK;
108 usb_log_debug2("RH(%p): hcsparams: %x.\n", instance,
109 EHCI_RD(caps->hcsparams));
110 usb_log_info("RH(%p): Found %u ports.\n", instance,
111 instance->port_count);
112
113 if (EHCI_RD(caps->hcsparams) & EHCI_CAPS_HCS_PPC_FLAG) {
114 usb_log_info("RH(%p): Per-port power switching.", instance);
115 } else {
116 usb_log_info("RH(%p): No power switching.", instance);
117 }
118 for (unsigned i = 0; i < instance->port_count; ++i)
119 usb_log_debug2("RH(%p-%u): status: %"PRIx32, instance, i,
120 EHCI_RD(regs->portsc[i]));
121
122 for (unsigned i = 0; i < EHCI_MAX_PORTS; ++i) {
123 instance->reset_flag[i] = false;
124 instance->resume_flag[i] = false;
125 }
126
127 ehci_rh_hub_desc_init(instance, EHCI_RD(caps->hcsparams));
128 instance->unfinished_interrupt_transfer = NULL;
129
130 return virthub_base_init(&instance->base, name, &ops, instance,
131 NULL, &instance->hub_descriptor.header, HUB_STATUS_CHANGE_PIPE);
132}
133
134/** Schedule USB request.
135 * @param instance OCHI root hub instance.
136 * @param batch USB requst batch to schedule.
137 * @return Always EOK.
138 * Most requests complete even before this function returns,
139 * status change requests might be postponed until there is something to report.
140 */
141int ehci_rh_schedule(ehci_rh_t *instance, usb_transfer_batch_t *batch)
142{
143 assert(instance);
144 assert(batch);
145 const usb_target_t target = {{
146 .address = batch->ep->address,
147 .endpoint = batch->ep->endpoint,
148 }};
149 batch->error = virthub_base_request(&instance->base, target,
150 usb_transfer_batch_direction(batch), (void*)batch->setup_buffer,
151 batch->buffer, batch->buffer_size, &batch->transfered_size);
152 if (batch->error == ENAK) {
153 usb_log_debug("RH(%p): BATCH(%p) adding as unfinished",
154 instance, batch);
155 /* This is safe because only status change interrupt transfers
156 * return NAK. The assertion holds true because the batch
157 * existence prevents communication with that ep */
158 assert(instance->unfinished_interrupt_transfer == NULL);
159 instance->unfinished_interrupt_transfer = batch;
160 } else {
161 usb_transfer_batch_finish(batch, NULL);
162 usb_transfer_batch_destroy(batch);
163 usb_log_debug("RH(%p): BATCH(%p) virtual request complete: %s",
164 instance, batch, str_error(batch->error));
165 }
166 return EOK;
167}
168
169/** Handle EHCI RHSC interrupt.
170 * @param instance EHCI root hub isntance.
171 * @return Always EOK.
172 *
173 * Interrupt means there is a change of status to report. It may trigger
174 * processing of a postponed request.
175 */
176int ehci_rh_interrupt(ehci_rh_t *instance)
177{
178 //TODO atomic swap needed
179 usb_transfer_batch_t *batch = instance->unfinished_interrupt_transfer;
180 instance->unfinished_interrupt_transfer = NULL;
181 usb_log_debug2("RH(%p): Interrupt. Processing batch: %p",
182 instance, batch);
183 if (batch) {
184 const usb_target_t target = {{
185 .address = batch->ep->address,
186 .endpoint = batch->ep->endpoint,
187 }};
188 batch->error = virthub_base_request(&instance->base, target,
189 usb_transfer_batch_direction(batch),
190 (void*)batch->setup_buffer,
191 batch->buffer, batch->buffer_size, &batch->transfered_size);
192 usb_transfer_batch_finish(batch, NULL);
193 usb_transfer_batch_destroy(batch);
194 }
195 return EOK;
196}
197
198/* HUB ROUTINES IMPLEMENTATION */
199#define TEST_SIZE_INIT(size, port, hub) \
200do { \
201 hub = virthub_get_data(device); \
202 assert(hub);\
203 if (uint16_usb2host(setup_packet->length) != size) \
204 return ESTALL; \
205 port = uint16_usb2host(setup_packet->index) - 1; \
206 if (port > hub->port_count) \
207 return EINVAL; \
208} while (0)
209
210/** Hub status request handler.
211 * @param device Virtual hub device
212 * @param setup_packet USB setup stage data.
213 * @param[out] data destination data buffer, size must be at least
214 * setup_packet->length bytes
215 * @param[out] act_size Sized of the valid response part of the buffer.
216 * @return Error code.
217 */
218static int req_get_status(usbvirt_device_t *device,
219 const usb_device_request_setup_packet_t *setup_packet,
220 uint8_t *data, size_t *act_size)
221{
222 ehci_rh_t *hub = virthub_get_data(device);
223 assert(hub);
224 if (uint16_usb2host(setup_packet->length) != 4)
225 return ESTALL;
226 /* ECHI RH does not report global OC, and local power is always good */
227 const uint32_t val = 0;
228 memcpy(data, &val, sizeof(val));
229 *act_size = sizeof(val);
230 return EOK;
231}
232
233/** Hub set feature request handler.
234 * @param device Virtual hub device
235 * @param setup_packet USB setup stage data.
236 * @param[out] data destination data buffer, size must be at least
237 * setup_packet->length bytes
238 * @param[out] act_size Sized of the valid response part of the buffer.
239 * @return Error code.
240 */
241static int req_clear_hub_feature(usbvirt_device_t *device,
242 const usb_device_request_setup_packet_t *setup_packet,
243 uint8_t *data, size_t *act_size)
244{
245 ehci_rh_t *hub = virthub_get_data(device);
246 assert(hub);
247
248 /*
249 * Chapter 11.16.2 specifies that only C_HUB_LOCAL_POWER and
250 * C_HUB_OVER_CURRENT are supported.
251 * C_HUB_LOCAL_POWER is not supported because root hubs do not support
252 * local power status feature.
253 * EHCI RH does not report global OC condition either
254 */
255 return ESTALL;
256}
257
258#define BIT_VAL(val, bit) ((val & bit) ? 1 : 0)
259#define EHCI2USB(val, bit, feat) (BIT_VAL(val, bit) << feat)
260
261/** Port status request handler.
262 * @param device Virtual hub device
263 * @param setup_packet USB setup stage data.
264 * @param[out] data destination data buffer, size must be at least
265 * setup_packet->length bytes
266 * @param[out] act_size Sized of the valid response part of the buffer.
267 * @return Error code.
268 */
269static int req_get_port_status(usbvirt_device_t *device,
270 const usb_device_request_setup_packet_t *setup_packet,
271 uint8_t *data, size_t *act_size)
272{
273 ehci_rh_t *hub;
274 unsigned port;
275 TEST_SIZE_INIT(4, port, hub);
276 if (setup_packet->value != 0)
277 return EINVAL;
278
279 const uint32_t reg = EHCI_RD(hub->registers->portsc[port]);
280 const uint32_t status = uint32_host2usb(
281 EHCI2USB(reg, USB_PORTSC_CONNECT_FLAG, USB_HUB_FEATURE_PORT_CONNECTION) |
282 EHCI2USB(reg, USB_PORTSC_ENABLED_FLAG, USB_HUB_FEATURE_PORT_ENABLE) |
283 EHCI2USB(reg, USB_PORTSC_SUSPEND_FLAG, USB_HUB_FEATURE_PORT_SUSPEND) |
284 EHCI2USB(reg, USB_PORTSC_OC_ACTIVE_FLAG, USB_HUB_FEATURE_PORT_OVER_CURRENT) |
285 EHCI2USB(reg, USB_PORTSC_PORT_RESET_FLAG, USB_HUB_FEATURE_PORT_RESET) |
286 EHCI2USB(reg, USB_PORTSC_PORT_POWER_FLAG, USB_HUB_FEATURE_PORT_POWER) |
287 (((reg & USB_PORTSC_LINE_STATUS_MASK) == USB_PORTSC_LINE_STATUS_K) ?
288 (1 << USB_HUB_FEATURE_PORT_LOW_SPEED) : 0) |
289 ((reg & USB_PORTSC_PORT_OWNER_FLAG) ? 0 : (1 << USB_HUB_FEATURE_PORT_HIGH_SPEED)) |
290 EHCI2USB(reg, USB_PORTSC_PORT_TEST_MASK, 11) |
291 EHCI2USB(reg, USB_PORTSC_INDICATOR_MASK, 12) |
292 EHCI2USB(reg, USB_PORTSC_CONNECT_CH_FLAG, USB_HUB_FEATURE_C_PORT_CONNECTION) |
293 EHCI2USB(reg, USB_PORTSC_EN_CHANGE_FLAG, USB_HUB_FEATURE_C_PORT_ENABLE) |
294 (hub->resume_flag[port] ? (1 << USB_HUB_FEATURE_C_PORT_SUSPEND) : 0) |
295 EHCI2USB(reg, USB_PORTSC_OC_CHANGE_FLAG, USB_HUB_FEATURE_C_PORT_OVER_CURRENT) |
296 (hub->reset_flag[port] ? (1 << USB_HUB_FEATURE_C_PORT_RESET): 0)
297 );
298 /* Note feature numbers for test and indicator feature do not
299 * correspond to the port status bit locations */
300 usb_log_debug2("RH(%p-%u) port status: %"PRIx32"(%"PRIx32")", hub, port,
301 status, reg);
302 memcpy(data, &status, sizeof(status));
303 *act_size = sizeof(status);
304 return EOK;
305}
306
307typedef struct {
308 ehci_rh_t *hub;
309 unsigned port;
310} ehci_rh_job_t;
311
312static int stop_reset(void *arg)
313{
314 ehci_rh_job_t *job = arg;
315 async_usleep(50000);
316 usb_log_debug("RH(%p-%u): Clearing reset", job->hub, job->port);
317 EHCI_CLR(job->hub->registers->portsc[job->port],
318 USB_PORTSC_PORT_RESET_FLAG);
319 /* wait for reset to complete */
320 while (EHCI_RD(job->hub->registers->portsc[job->port]) &
321 USB_PORTSC_PORT_RESET_FLAG) {
322 async_usleep(1);
323 };
324 usb_log_debug("RH(%p-%u): Reset complete", job->hub, job->port);
325 /* Handle port ownership, if the port is not enabled
326 * after reset it's a full speed device */
327 if (!(EHCI_RD(job->hub->registers->portsc[job->port]) &
328 USB_PORTSC_ENABLED_FLAG)) {
329 usb_log_info("RH(%p-%u): Port not enabled after reset (%"PRIX32
330 "), giving up ownership", job->hub, job->port,
331 EHCI_RD(job->hub->registers->portsc[job->port]));
332 EHCI_SET(job->hub->registers->portsc[job->port],
333 USB_PORTSC_PORT_OWNER_FLAG);
334 }
335 job->hub->reset_flag[job->port] = true;
336 ehci_rh_interrupt(job->hub);
337 free(job);
338 return 0;
339}
340
341static int stop_resume(void *arg)
342{
343 ehci_rh_job_t *job = arg;
344 async_usleep(20000);
345 usb_log_debug("RH(%p-%u): Stopping resume", job->hub, job->port);
346 EHCI_CLR(job->hub->registers->portsc[job->port],
347 USB_PORTSC_RESUME_FLAG);
348 job->hub->resume_flag[job->port] = true;
349 ehci_rh_interrupt(job->hub);
350 free(job);
351 return 0;
352}
353
354static int delayed_job(int (*func)(void*), ehci_rh_t *rh, unsigned port)
355{
356 ehci_rh_job_t *job = malloc(sizeof(*job));
357 if (!job)
358 return ENOMEM;
359 job->hub = rh;
360 job->port = port;
361 fid_t fib = fibril_create(func, job);
362 if (!fib) {
363 free(job);
364 return ENOMEM;
365 }
366 fibril_add_ready(fib);
367 usb_log_debug2("RH(%p-%u): Scheduled delayed stop job.", rh, port);
368 return EOK;
369}
370
371
372/** Port clear feature request handler.
373 * @param device Virtual hub device
374 * @param setup_packet USB setup stage data.
375 * @param[out] data destination data buffer, size must be at least
376 * setup_packet->length bytes
377 * @param[out] act_size Sized of the valid response part of the buffer.
378 * @return Error code.
379 */
380static int req_clear_port_feature(usbvirt_device_t *device,
381 const usb_device_request_setup_packet_t *setup_packet,
382 uint8_t *data, size_t *act_size)
383{
384 ehci_rh_t *hub;
385 unsigned port;
386 TEST_SIZE_INIT(0, port, hub);
387 const unsigned feature = uint16_usb2host(setup_packet->value);
388 /* Enabled features to clear: see page 269 of USB specs */
389 switch (feature)
390 {
391 case USB_HUB_FEATURE_PORT_POWER: /*8*/
392 usb_log_debug2("RH(%p-%u): Clear port power.", hub, port);
393 EHCI_CLR(hub->registers->portsc[port],
394 USB_PORTSC_PORT_POWER_FLAG);
395 return EOK;
396
397 case USB_HUB_FEATURE_PORT_ENABLE: /*1*/
398 usb_log_debug2("RH(%p-%u): Clear port enable.", hub, port);
399 EHCI_CLR(hub->registers->portsc[port],
400 USB_PORTSC_ENABLED_FLAG);
401 return EOK;
402
403 case USB_HUB_FEATURE_PORT_SUSPEND: /*2*/
404 usb_log_debug2("RH(%p-%u): Clear port suspend.", hub, port);
405 /* If not in suspend it's noop */
406 if ((EHCI_RD(hub->registers->portsc[port]) &
407 USB_PORTSC_SUSPEND_FLAG) == 0)
408 return EOK;
409 /* Host driven resume */
410 EHCI_SET(hub->registers->portsc[port],
411 USB_PORTSC_RESUME_FLAG);
412 //TODO: What if creating the delayed job fails?
413 return delayed_job(stop_resume, hub, port);
414
415 case USB_HUB_FEATURE_C_PORT_CONNECTION: /*16*/
416 usb_log_debug2("RH(%p-%u): Clear port connection change.",
417 hub, port);
418 EHCI_SET(hub->registers->portsc[port],
419 USB_PORTSC_CONNECT_CH_FLAG);
420 return EOK;
421 case USB_HUB_FEATURE_C_PORT_ENABLE: /*17*/
422 usb_log_debug2("RH(%p-%u): Clear port enable change.",
423 hub, port);
424 EHCI_SET(hub->registers->portsc[port],
425 USB_PORTSC_CONNECT_CH_FLAG);
426 return EOK;
427 case USB_HUB_FEATURE_C_PORT_OVER_CURRENT: /*19*/
428 usb_log_debug2("RH(%p-%u): Clear port OC change.",
429 hub, port);
430 EHCI_SET(hub->registers->portsc[port],
431 USB_PORTSC_OC_CHANGE_FLAG);
432 return EOK;
433 case USB_HUB_FEATURE_C_PORT_SUSPEND: /*18*/
434 usb_log_debug2("RH(%p-%u): Clear port suspend change.",
435 hub, port);
436 hub->resume_flag[port] = false;
437 return EOK;
438 case USB_HUB_FEATURE_C_PORT_RESET: /*20*/
439 usb_log_debug2("RH(%p-%u): Clear port reset change.",
440 hub, port);
441 hub->reset_flag[port] = false;
442 return EOK;
443
444 default:
445 usb_log_warning("RH(%p-%u): Clear unknown feature: %u",
446 hub, port, feature);
447 return ENOTSUP;
448 }
449}
450
451/** Port set feature request handler.
452 * @param device Virtual hub device
453 * @param setup_packet USB setup stage data.
454 * @param[out] data destination data buffer, size must be at least
455 * setup_packet->length bytes
456 * @param[out] act_size Sized of the valid response part of the buffer.
457 * @return Error code.
458 */
459static int req_set_port_feature(usbvirt_device_t *device,
460 const usb_device_request_setup_packet_t *setup_packet,
461 uint8_t *data, size_t *act_size)
462{
463 ehci_rh_t *hub;
464 unsigned port;
465 TEST_SIZE_INIT(0, port, hub);
466 const unsigned feature = uint16_usb2host(setup_packet->value);
467 switch (feature) {
468 case USB_HUB_FEATURE_PORT_ENABLE: /*1*/
469 usb_log_debug2("RH(%p-%u): Set port enable.", hub, port);
470 EHCI_SET(hub->registers->portsc[port],
471 USB_PORTSC_ENABLED_FLAG);
472 return EOK;
473 case USB_HUB_FEATURE_PORT_SUSPEND: /*2*/
474 usb_log_debug2("RH(%p-%u): Set port suspend.", hub, port);
475 EHCI_SET(hub->registers->portsc[port],
476 USB_PORTSC_SUSPEND_FLAG);
477 return EOK;
478 case USB_HUB_FEATURE_PORT_RESET: /*4*/
479 usb_log_debug2("RH(%p-%u): Set port reset.", hub, port);
480 EHCI_SET(hub->registers->portsc[port],
481 USB_PORTSC_PORT_RESET_FLAG);
482 //TODO: What if creating the delayed job fails?
483 return delayed_job(stop_reset, hub, port);
484 case USB_HUB_FEATURE_PORT_POWER: /*8*/
485 usb_log_debug2("RH(%p-%u): Set port power.", hub, port);
486 EHCI_SET(hub->registers->portsc[port],
487 USB_PORTSC_PORT_POWER_FLAG);
488 return EOK;
489 default:
490 usb_log_warning("RH(%p-%u): Set unknown feature: %u",
491 hub, port, feature);
492 return ENOTSUP;
493 }
494}
495
496/** Status change handler.
497 * @param device Virtual hub device
498 * @param endpoint Endpoint number
499 * @param tr_type Transfer type
500 * @param buffer Response destination
501 * @param buffer_size Bytes available in buffer
502 * @param actual_size Size us the used part of the dest buffer.
503 *
504 * Produces status mask. Bit 0 indicates hub status change the other bits
505 * represent port status change. Endian does not matter as UHCI root hubs
506 * only need 1 byte.
507 */
508static int req_status_change_handler(usbvirt_device_t *device,
509 usb_endpoint_t endpoint, usb_transfer_type_t tr_type,
510 void *buffer, size_t buffer_size, size_t *actual_size)
511{
512 ehci_rh_t *hub = virthub_get_data(device);
513 assert(hub);
514
515 if (buffer_size < STATUS_BYTES(hub->port_count))
516 return ESTALL;
517
518 uint16_t mask = 0;
519 for (unsigned port = 0; port < hub->port_count; ++port) {
520 /* Write-clean bits are those that indicate change */
521 uint32_t status = EHCI_RD(hub->registers->portsc[port]);
522 if ((status & USB_PORTSC_WC_MASK) || hub->reset_flag[port]) {
523 /* Ignore new LS device */
524 if ((status & USB_PORTSC_CONNECT_CH_FLAG) &&
525 (status & USB_PORTSC_LINE_STATUS_MASK) ==
526 USB_PORTSC_LINE_STATUS_K)
527 EHCI_SET(hub->registers->portsc[port],
528 USB_PORTSC_PORT_OWNER_FLAG);
529 else
530 mask |= (2 << port);
531 }
532 }
533
534 usb_log_debug2("RH(%p): root hub interrupt mask: %"PRIx16, hub, mask);
535
536 if (mask == 0)
537 return ENAK;
538 mask = uint16_host2usb(mask);
539 memcpy(buffer, &mask, STATUS_BYTES(hub->port_count));
540 *actual_size = STATUS_BYTES(hub->port_count);
541 return EOK;
542}
543
544/** EHCI root hub request handlers */
545static const usbvirt_control_request_handler_t control_transfer_handlers[] = {
546 {
547 STD_REQ_IN(USB_REQUEST_RECIPIENT_DEVICE, USB_DEVREQ_GET_DESCRIPTOR),
548 .name = "GetDescriptor",
549 .callback = virthub_base_get_hub_descriptor,
550 },
551 {
552 CLASS_REQ_IN(USB_REQUEST_RECIPIENT_DEVICE, USB_DEVREQ_GET_DESCRIPTOR),
553 .name = "GetDescriptor",
554 .callback = virthub_base_get_hub_descriptor,
555 },
556 {
557 CLASS_REQ_IN(USB_REQUEST_RECIPIENT_DEVICE, USB_HUB_REQUEST_GET_DESCRIPTOR),
558 .name = "GetHubDescriptor",
559 .callback = virthub_base_get_hub_descriptor,
560 },
561 {
562 CLASS_REQ_IN(USB_REQUEST_RECIPIENT_OTHER, USB_HUB_REQUEST_GET_STATUS),
563 .name = "GetPortStatus",
564 .callback = req_get_port_status,
565 },
566 {
567 CLASS_REQ_OUT(USB_REQUEST_RECIPIENT_DEVICE, USB_HUB_REQUEST_CLEAR_FEATURE),
568 .name = "ClearHubFeature",
569 .callback = req_clear_hub_feature,
570 },
571 {
572 CLASS_REQ_OUT(USB_REQUEST_RECIPIENT_OTHER, USB_HUB_REQUEST_CLEAR_FEATURE),
573 .name = "ClearPortFeature",
574 .callback = req_clear_port_feature,
575 },
576 {
577 CLASS_REQ_IN(USB_REQUEST_RECIPIENT_DEVICE, USB_HUB_REQUEST_GET_STATUS),
578 .name = "GetHubStatus",
579 .callback = req_get_status,
580 },
581 {
582 CLASS_REQ_IN(USB_REQUEST_RECIPIENT_OTHER, USB_HUB_REQUEST_GET_STATUS),
583 .name = "GetPortStatus",
584 .callback = req_get_port_status,
585 },
586 {
587 CLASS_REQ_OUT(USB_REQUEST_RECIPIENT_DEVICE, USB_HUB_REQUEST_SET_FEATURE),
588 .name = "SetHubFeature",
589 .callback = req_nop,
590 },
591 {
592 CLASS_REQ_OUT(USB_REQUEST_RECIPIENT_OTHER, USB_HUB_REQUEST_SET_FEATURE),
593 .name = "SetPortFeature",
594 .callback = req_set_port_feature,
595 },
596 {
597 .callback = NULL
598 }
599};
600
601/** Virtual EHCI root hub ops */
602static usbvirt_device_ops_t ops = {
603 .control = control_transfer_handlers,
604 .data_in[HUB_STATUS_CHANGE_PIPE] = req_status_change_handler,
605};
Note: See TracBrowser for help on using the repository browser.