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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since cb69854 was 41924f30, checked in by Ondřej Hlavatý <aearsis@…>, 8 years ago

WIP usbhost refactoring

This commit replaces callbacks with more systematic virtual-like inheritance-like solution. Currently breaks build of HelenOS, but both xhci and usbhost are buildable. More refactoring follows…

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