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

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

usb: update copyrights

The data was generated by a script, guided manually. If you feel your
name is missing somewhere, please add it!

The semi-automated process was roughly:

1) Changes per file and author (limited to our team) were counted
2) Trivial numbers were thrown away
3) Authors were sorted by lines added to file
4) All previous copyrights were replaced by the newly generated one
5) Hunks changing only year were discarded

It seems that a lot of my copyrights were added. It is due to me being
both sticking my nose everywhere and lazy to update the copyright right
away :)

  • 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.