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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 3f932a7e was 84239b1, checked in by Jiri Svoboda <jiri@…>, 7 years ago

And there was much fixing.

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