source: mainline/uspace/drv/bus/usb/xhci/rh.c@ 916991b

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 916991b was 916991b, checked in by Petr Manek <petr.manek@…>, 8 years ago

Implemented SetStatusFeature request.

  • Property mode set to 100644
File size: 17.5 KB
Line 
1/*
2 * Copyright (c) 2017 Michal Staruch
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 drvusbxhci
30 * @{
31 */
32/** @file
33 * @brief The roothub structures abstraction.
34 */
35
36#include <errno.h>
37#include <str_error.h>
38#include <usb/debug.h>
39#include <usb/host/utils/malloc32.h>
40#include "debug.h"
41#include "commands.h"
42#include "endpoint.h"
43#include "hc.h"
44#include "hw_struct/trb.h"
45#include "rh.h"
46
47#define USB_MAP_VALUE(a, b) [USB_HUB_FEATURE_##a] = b
48#define USB_MAP_XHCI(a, b) USB_MAP_VALUE(a, XHCI_REG_MASK(XHCI_PORT_##b))
49
50enum {
51 HUB_STATUS_CHANGE_PIPE = 1,
52};
53
54static usbvirt_device_ops_t ops;
55
56/* This mask only lists registers, which imply port change. */
57static const uint32_t port_change_mask =
58 XHCI_REG_MASK(XHCI_PORT_CSC) |
59 XHCI_REG_MASK(XHCI_PORT_PEC) |
60 XHCI_REG_MASK(XHCI_PORT_WRC) |
61 XHCI_REG_MASK(XHCI_PORT_OCC) |
62 XHCI_REG_MASK(XHCI_PORT_PRC) |
63 XHCI_REG_MASK(XHCI_PORT_PLC) |
64 XHCI_REG_MASK(XHCI_PORT_CEC);
65
66int xhci_rh_init(xhci_rh_t *rh, xhci_hc_t *hc)
67{
68 assert(rh);
69 assert(hc);
70
71 rh->hc = hc;
72 rh->max_ports = XHCI_REG_RD(hc->cap_regs, XHCI_CAP_MAX_PORTS);
73
74 usb_hub_descriptor_header_t *header = &rh->hub_descriptor.header;
75 header->length = sizeof(usb_hub_descriptor_header_t);
76 header->descriptor_type = USB_DESCTYPE_HUB;
77 header->port_count = rh->max_ports;
78 header->characteristics =
79 HUB_CHAR_NO_POWER_SWITCH_FLAG | HUB_CHAR_NO_OC_FLAG;
80 header->power_good_time = 10; /* XHCI section 5.4.9 says 20ms max */
81 header->max_current = 0;
82
83 return virthub_base_init(&rh->base, "xhci", &ops, rh, NULL,
84 header, HUB_STATUS_CHANGE_PIPE);
85}
86
87// TODO: Check device deallocation, we free device_ctx in hc.c, not
88// sure about the other structs.
89// static int alloc_dev(xhci_hc_t *hc, uint8_t port, uint32_t route_str)
90// {
91// int err;
92//
93// xhci_cmd_t cmd;
94// xhci_cmd_init(&cmd);
95//
96// xhci_send_enable_slot_command(hc, &cmd);
97// if ((err = xhci_cmd_wait(&cmd, 100000)) != EOK)
98// return err;
99//
100// uint32_t slot_id = cmd.slot_id;
101//
102// usb_log_debug2("Obtained slot ID: %u.\n", slot_id);
103// xhci_cmd_fini(&cmd);
104//
105// xhci_input_ctx_t *ictx = malloc32(sizeof(xhci_input_ctx_t));
106// if (!ictx) {
107// return ENOMEM;
108// }
109//
110// memset(ictx, 0, sizeof(xhci_input_ctx_t));
111//
112// XHCI_INPUT_CTRL_CTX_ADD_SET(ictx->ctrl_ctx, 0);
113// XHCI_INPUT_CTRL_CTX_ADD_SET(ictx->ctrl_ctx, 1);
114//
115// /* Initialize slot_ctx according to section 4.3.3 point 3. */
116// /* Attaching to root hub port, root string equals to 0. */
117// XHCI_SLOT_ROOT_HUB_PORT_SET(ictx->slot_ctx, port);
118// XHCI_SLOT_CTX_ENTRIES_SET(ictx->slot_ctx, 1);
119// XHCI_SLOT_ROUTE_STRING_SET(ictx->slot_ctx, route_str);
120//
121// xhci_trb_ring_t *ep_ring = malloc32(sizeof(xhci_trb_ring_t));
122// if (!ep_ring) {
123// err = ENOMEM;
124// goto err_ictx;
125// }
126//
127// err = xhci_trb_ring_init(ep_ring, hc);
128// if (err)
129// goto err_ring;
130//
131// XHCI_EP_TYPE_SET(ictx->endpoint_ctx[0], EP_TYPE_CONTROL);
132// // TODO: must be changed with a command after USB descriptor is read
133// // See 4.6.5 in XHCI specification, first note
134// XHCI_EP_MAX_PACKET_SIZE_SET(ictx->endpoint_ctx[0],
135// xhci_is_usb3_port(&hc->rh, port) ? 512 : 8);
136// XHCI_EP_MAX_BURST_SIZE_SET(ictx->endpoint_ctx[0], 0);
137// /* FIXME physical pointer? */
138// XHCI_EP_TR_DPTR_SET(ictx->endpoint_ctx[0], ep_ring->dequeue);
139// XHCI_EP_DCS_SET(ictx->endpoint_ctx[0], 1);
140// XHCI_EP_INTERVAL_SET(ictx->endpoint_ctx[0], 0);
141// XHCI_EP_MAX_P_STREAMS_SET(ictx->endpoint_ctx[0], 0);
142// XHCI_EP_MULT_SET(ictx->endpoint_ctx[0], 0);
143// XHCI_EP_ERROR_COUNT_SET(ictx->endpoint_ctx[0], 3);
144//
145// // TODO: What's the alignment?
146// xhci_device_ctx_t *dctx = malloc32(sizeof(xhci_device_ctx_t));
147// if (!dctx) {
148// err = ENOMEM;
149// goto err_ring;
150// }
151// memset(dctx, 0, sizeof(xhci_device_ctx_t));
152//
153// hc->dcbaa[slot_id] = addr_to_phys(dctx);
154//
155// memset(&hc->dcbaa_virt[slot_id], 0, sizeof(xhci_virt_device_ctx_t));
156// hc->dcbaa_virt[slot_id].dev_ctx = dctx;
157// hc->dcbaa_virt[slot_id].trs[0] = ep_ring;
158//
159// xhci_cmd_init(&cmd);
160// cmd.slot_id = slot_id;
161// xhci_send_address_device_command(hc, &cmd, ictx);
162// if ((err = xhci_cmd_wait(&cmd, 100000)) != EOK)
163// goto err_dctx;
164//
165// xhci_cmd_fini(&cmd);
166//
167// // TODO: Issue configure endpoint commands (sec 4.3.5).
168//
169// return EOK;
170//
171// err_dctx:
172// if (dctx) {
173// free32(dctx);
174// hc->dcbaa[slot_id] = 0;
175// memset(&hc->dcbaa_virt[slot_id], 0, sizeof(xhci_virt_device_ctx_t));
176// }
177// err_ring:
178// if (ep_ring) {
179// xhci_trb_ring_fini(ep_ring);
180// free32(ep_ring);
181// }
182// err_ictx:
183// free32(ictx);
184// return err;
185// }
186
187// static int handle_connected_device(xhci_hc_t* hc, xhci_port_regs_t* regs, uint8_t port_id)
188// {
189// uint8_t link_state = XHCI_REG_RD(regs, XHCI_PORT_PLS);
190// const xhci_port_speed_t *speed = xhci_get_port_speed(&hc->rh, port_id);
191//
192// usb_log_info("Detected new %.4s%u.%u device on port %u.", speed->name, speed->major, speed->minor, port_id);
193//
194// if (speed->major == 3) {
195// if(link_state == 0) {
196// /* USB3 is automatically advanced to enabled. */
197// return alloc_dev(hc, port_id, 0);
198// }
199// else if (link_state == 5) {
200// /* USB 3 failed to enable. */
201// usb_log_error("USB 3 port couldn't be enabled.");
202// return EAGAIN;
203// }
204// else {
205// usb_log_error("USB 3 port is in invalid state %u.", link_state);
206// return EINVAL;
207// }
208// }
209// else {
210// usb_log_debug("USB 2 device attached, issuing reset.");
211// xhci_reset_hub_port(hc, port_id);
212// /*
213// FIXME: we need to wait for the event triggered by the reset
214// and then alloc_dev()... can't it be done directly instead of
215// going around?
216// */
217// return EOK;
218// }
219// }
220
221int xhci_handle_port_status_change_event(xhci_hc_t *hc, xhci_trb_t *trb)
222{
223 int err;
224
225 uint8_t port_id = xhci_get_hub_port(trb);
226 usb_log_debug("Port status change event detected for port %u.", port_id);
227
228 // Interrupt on the virtual hub status change pipe.
229 err = xhci_rh_interrupt(&hc->rh);
230 if (err != EOK) {
231 usb_log_warning("Invoking interrupt on virtual hub failed: %s",
232 str_error(err));
233 }
234
235 return EOK;
236}
237
238const xhci_port_speed_t *xhci_get_port_speed(xhci_rh_t *rh, uint8_t port)
239{
240 xhci_port_regs_t *port_regs = &rh->hc->op_regs->portrs[port - 1];
241
242 unsigned psiv = XHCI_REG_RD(port_regs, XHCI_PORT_PS);
243 return &rh->speeds[psiv];
244}
245
246int xhci_get_hub_port(xhci_trb_t *trb)
247{
248 assert(trb);
249 uint8_t port_id = XHCI_QWORD_EXTRACT(trb->parameter, 31, 24);
250
251 return port_id;
252}
253
254int xhci_reset_hub_port(xhci_hc_t* hc, uint8_t port)
255{
256 usb_log_debug2("Resetting port %u.", port);
257 xhci_port_regs_t *regs = &hc->op_regs->portrs[port-1];
258 XHCI_REG_WR(regs, XHCI_PORT_PR, 1);
259
260 return EOK;
261}
262
263int xhci_rh_schedule(xhci_rh_t *rh, usb_transfer_batch_t *batch)
264{
265 assert(rh);
266 assert(batch);
267 const usb_target_t target = {{
268 .address = batch->ep->address,
269 .endpoint = batch->ep->endpoint,
270 }};
271 batch->error = virthub_base_request(&rh->base, target,
272 usb_transfer_batch_direction(batch), (void*)batch->setup_buffer,
273 batch->buffer, batch->buffer_size, &batch->transfered_size);
274 if (batch->error == ENAK) {
275 /* This is safe because only status change interrupt transfers
276 * return NAK. The assertion holds true because the batch
277 * existence prevents communication with that ep */
278 assert(rh->unfinished_interrupt_transfer == NULL);
279 rh->unfinished_interrupt_transfer = batch;
280 } else {
281 usb_transfer_batch_finish(batch, NULL);
282 usb_transfer_batch_destroy(batch);
283 }
284 return EOK;
285}
286
287int xhci_rh_interrupt(xhci_rh_t *rh)
288{
289 usb_log_debug2("Called xhci_rh_interrupt().");
290
291 /* TODO: atomic swap needed */
292 usb_transfer_batch_t *batch = rh->unfinished_interrupt_transfer;
293 rh->unfinished_interrupt_transfer = NULL;
294 if (batch) {
295 const usb_target_t target = {{
296 .address = batch->ep->address,
297 .endpoint = batch->ep->endpoint,
298 }};
299 batch->error = virthub_base_request(&rh->base, target,
300 usb_transfer_batch_direction(batch),
301 (void*)batch->setup_buffer,
302 batch->buffer, batch->buffer_size, &batch->transfered_size);
303 usb_transfer_batch_finish(batch, NULL);
304 usb_transfer_batch_destroy(batch);
305 }
306 return EOK;
307}
308
309/** Hub set feature request handler.
310 * @param device Virtual hub device
311 * @param setup_packet USB setup stage data.
312 * @param[out] data destination data buffer, size must be at least
313 * setup_packet->length bytes
314 * @param[out] act_size Sized of the valid response part of the buffer.
315 * @return Error code.
316 */
317static int req_clear_hub_feature(usbvirt_device_t *device,
318 const usb_device_request_setup_packet_t *setup_packet,
319 uint8_t *data, size_t *act_size)
320{
321 /* TODO: Implement me! */
322 usb_log_debug2("Called req_clear_hub_feature().");
323 return EOK;
324}
325
326#define XHCI_TO_USB(usb_feat, reg_set, ...) \
327 (((XHCI_REG_RD(reg_set, ##__VA_ARGS__)) ? 1 : 0) << (usb_feat))
328
329/** Port status request handler.
330 * @param device Virtual hub device
331 * @param setup_packet USB setup stage data.
332 * @param[out] data destination data buffer, size must be at least
333 * setup_packet->length bytes
334 * @param[out] act_size Sized of the valid response part of the buffer.
335 * @return Error code.
336 */
337static int req_get_port_status(usbvirt_device_t *device,
338 const usb_device_request_setup_packet_t *setup_packet,
339 uint8_t *data, size_t *act_size)
340{
341 xhci_rh_t *hub = virthub_get_data(device);
342 assert(hub);
343
344 if (!setup_packet->index || setup_packet->index > hub->max_ports) {
345 return ESTALL;
346 }
347
348 /* The index is 1-based. */
349 xhci_port_regs_t* regs = &hub->hc->op_regs->portrs[setup_packet->index - 1];
350
351 const uint32_t status = uint32_host2usb(
352 XHCI_TO_USB(USB_HUB_FEATURE_C_PORT_CONNECTION, regs, XHCI_PORT_CSC) |
353 XHCI_TO_USB(USB_HUB_FEATURE_C_PORT_ENABLE, regs, XHCI_PORT_PEC) |
354 XHCI_TO_USB(USB_HUB_FEATURE_C_PORT_OVER_CURRENT, regs, XHCI_PORT_OCC) |
355 XHCI_TO_USB(USB_HUB_FEATURE_C_PORT_RESET, regs, XHCI_PORT_PRC) |
356 XHCI_TO_USB(USB_HUB_FEATURE_PORT_CONNECTION, regs, XHCI_PORT_CCS) |
357 XHCI_TO_USB(USB_HUB_FEATURE_PORT_ENABLE, regs, XHCI_PORT_PED) |
358 XHCI_TO_USB(USB_HUB_FEATURE_PORT_OVER_CURRENT, regs, XHCI_PORT_OCA) |
359 XHCI_TO_USB(USB_HUB_FEATURE_PORT_RESET, regs, XHCI_PORT_PR) |
360 XHCI_TO_USB(USB_HUB_FEATURE_PORT_POWER, regs, XHCI_PORT_PP)
361 );
362
363 usb_log_debug2("RH: GetPortStatus(%hu) = %u.", setup_packet->index,
364 uint32_usb2host(status));
365
366 memcpy(data, &status, sizeof(status));
367 *act_size = sizeof(status);
368
369 return EOK;
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 xhci_rh_t *hub = virthub_get_data(device);
385 assert(hub);
386
387 if (!setup_packet->index || setup_packet->index > hub->max_ports) {
388 return ESTALL;
389 }
390
391 /* The index is 1-based. */
392 xhci_port_regs_t* regs = &hub->hc->op_regs->portrs[setup_packet->index - 1];
393
394 const usb_hub_class_feature_t feature = uint16_usb2host(setup_packet->value);
395 static const ioport32_t masks[] = {
396 USB_MAP_XHCI(C_PORT_CONNECTION, CSC),
397 USB_MAP_XHCI(C_PORT_ENABLE, PEC),
398 USB_MAP_XHCI(C_PORT_OVER_CURRENT, OCC),
399 USB_MAP_XHCI(C_PORT_RESET, PRC),
400 USB_MAP_XHCI(PORT_ENABLE, PED),
401 USB_MAP_XHCI(PORT_RESET, PR),
402 USB_MAP_XHCI(PORT_POWER, PP)
403 };
404
405 static const bool is_change[] = {
406 USB_MAP_VALUE(C_PORT_CONNECTION, true),
407 USB_MAP_VALUE(C_PORT_ENABLE, true),
408 USB_MAP_VALUE(C_PORT_OVER_CURRENT, true),
409 USB_MAP_VALUE(C_PORT_RESET, true),
410 USB_MAP_VALUE(PORT_ENABLE, false),
411 USB_MAP_VALUE(PORT_RESET, false),
412 USB_MAP_VALUE(PORT_POWER, false)
413 };
414
415 usb_log_debug2("RH: ClearPortFeature(%hu) = %d.", setup_packet->index,
416 feature);
417
418 if (is_change[feature]) {
419 /* Clear the register by writing 1. */
420 XHCI_REG_SET_FIELD(&regs->portsc, masks[feature], 32);
421 } else {
422 /* Clear the register by writing 0. */
423 XHCI_REG_CLR_FIELD(&regs->portsc, masks[feature], 32);
424 }
425
426 return EOK;
427}
428
429/** Port set feature request handler.
430 * @param device Virtual hub device
431 * @param setup_packet USB setup stage data.
432 * @param[out] data destination data buffer, size must be at least
433 * setup_packet->length bytes
434 * @param[out] act_size Sized of the valid response part of the buffer.
435 * @return Error code.
436 */
437static int req_set_port_feature(usbvirt_device_t *device,
438 const usb_device_request_setup_packet_t *setup_packet,
439 uint8_t *data, size_t *act_size)
440{
441 xhci_rh_t *hub = virthub_get_data(device);
442 assert(hub);
443
444 if (!setup_packet->index || setup_packet->index > hub->max_ports) {
445 return ESTALL;
446 }
447
448 /* The index is 1-based. */
449 xhci_port_regs_t* regs = &hub->hc->op_regs->portrs[setup_packet->index - 1];
450
451 const usb_hub_class_feature_t feature = uint16_usb2host(setup_packet->value);
452 static const ioport32_t masks[] = {
453 USB_MAP_XHCI(PORT_ENABLE, PED),
454 USB_MAP_XHCI(PORT_RESET, PR),
455 USB_MAP_XHCI(PORT_POWER, PP)
456 };
457
458 usb_log_debug2("RH: SetPortFeature(%hu) = %d.", setup_packet->index,
459 feature);
460
461 /* Set the feature in the PIO register. */
462 XHCI_REG_SET_FIELD(&regs->portsc, masks[feature], 32);
463
464 return EOK;
465}
466
467/** Status change handler.
468 * @param device Virtual hub device
469 * @param endpoint Endpoint number
470 * @param tr_type Transfer type
471 * @param buffer Response destination
472 * @param buffer_size Bytes available in buffer
473 * @param actual_size Size us the used part of the dest buffer.
474 *
475 * Produces status mask. Bit 0 indicates hub status change the other bits
476 * represent port status change.
477 */
478static int req_status_change_handler(usbvirt_device_t *device,
479 usb_endpoint_t endpoint, usb_transfer_type_t tr_type,
480 void *buffer, size_t buffer_size, size_t *actual_size)
481{
482 usb_log_debug2("Called req_status_change_handler().");
483 xhci_rh_t *hub = virthub_get_data(device);
484 assert(hub);
485
486 uint8_t status[STATUS_BYTES(hub->max_ports)];
487 memset(status, 0, sizeof(status));
488
489 if (buffer_size < sizeof(status))
490 return ESTALL;
491
492 bool change = false;
493 for (size_t i = 1; i <= hub->max_ports; ++i) {
494 xhci_port_regs_t *regs = &hub->hc->op_regs->portrs[i - 1];
495
496 if (XHCI_REG_RD_FIELD(&regs->portsc, 32) & port_change_mask) {
497 status[i / 8] |= (1 << (i % 8));
498 change = true;
499 }
500 }
501
502 memcpy(buffer, &status, sizeof(status));
503 *actual_size = sizeof(status);
504 return change ? EOK : ENAK;
505}
506
507int xhci_rh_fini(xhci_rh_t *rh)
508{
509 /* TODO: Implement me! */
510 usb_log_debug2("Called xhci_rh_fini().");
511 return EOK;
512}
513
514/** XHCI root hub request handlers */
515static const usbvirt_control_request_handler_t control_transfer_handlers[] = {
516 {
517 STD_REQ_IN(USB_REQUEST_RECIPIENT_DEVICE, USB_DEVREQ_GET_DESCRIPTOR),
518 .name = "GetDescriptor",
519 .callback = virthub_base_get_hub_descriptor,
520 },
521 {
522 CLASS_REQ_IN(USB_REQUEST_RECIPIENT_DEVICE, USB_DEVREQ_GET_DESCRIPTOR),
523 .name = "GetDescriptor",
524 .callback = virthub_base_get_hub_descriptor,
525 },
526 {
527 CLASS_REQ_IN(USB_REQUEST_RECIPIENT_DEVICE, USB_HUB_REQUEST_GET_DESCRIPTOR),
528 .name = "GetHubDescriptor",
529 .callback = virthub_base_get_hub_descriptor,
530 },
531 {
532 CLASS_REQ_IN(USB_REQUEST_RECIPIENT_OTHER, USB_HUB_REQUEST_GET_STATUS),
533 .name = "GetPortStatus",
534 .callback = req_get_port_status,
535 },
536 {
537 CLASS_REQ_OUT(USB_REQUEST_RECIPIENT_DEVICE, USB_HUB_REQUEST_CLEAR_FEATURE),
538 .name = "ClearHubFeature",
539 .callback = req_clear_hub_feature,
540 },
541 {
542 CLASS_REQ_OUT(USB_REQUEST_RECIPIENT_OTHER, USB_HUB_REQUEST_CLEAR_FEATURE),
543 .name = "ClearPortFeature",
544 .callback = req_clear_port_feature,
545 },
546 {
547 CLASS_REQ_IN(USB_REQUEST_RECIPIENT_DEVICE, USB_HUB_REQUEST_GET_STATUS),
548 .name = "GetHubStatus",
549 /* XHCI root hub has no power source,
550 * over-current is reported by port */
551 .callback = virthub_base_get_null_status,
552 },
553 {
554 CLASS_REQ_IN(USB_REQUEST_RECIPIENT_OTHER, USB_HUB_REQUEST_GET_STATUS),
555 .name = "GetPortStatus",
556 .callback = req_get_port_status,
557 },
558 {
559 CLASS_REQ_OUT(USB_REQUEST_RECIPIENT_DEVICE, USB_HUB_REQUEST_SET_FEATURE),
560 .name = "SetHubFeature",
561 .callback = req_nop,
562 },
563 {
564 CLASS_REQ_OUT(USB_REQUEST_RECIPIENT_OTHER, USB_HUB_REQUEST_SET_FEATURE),
565 .name = "SetPortFeature",
566 .callback = req_set_port_feature,
567 },
568 {
569 .callback = NULL
570 }
571};
572
573/** Virtual XHCI root hub ops */
574static usbvirt_device_ops_t ops = {
575 .control = control_transfer_handlers,
576 .data_in[HUB_STATUS_CHANGE_PIPE] = req_status_change_handler,
577};
578
579
580/**
581 * @}
582 */
Note: See TracBrowser for help on using the repository browser.