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

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

usbhost refactoring: let transfer_batch be initialized by bus

Currently makes older HCs fail, work in progress.

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