source: mainline/uspace/drv/bus/usb/uhci/hc.c@ 629255a

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

usb2_bus: no longer be a bus

As the number of implemented functions got to 3, it's not so beneficial
to inherit usb2 bus to get the functionality. Overall, four trampolines
needed to be added, which is an acceptable number.

Now, the usb2_bus has become a usb2_bus_helper, to be used as
a companion to the common bus.

This is mostly a preparation to remove the runtime binding of the bus
methods.

  • Property mode set to 100644
File size: 18.5 KB
RevLine 
[9351353]1/*
2 * Copyright (c) 2011 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 */
[58563585]28
[17ceb72]29/** @addtogroup drvusbuhcihc
[9351353]30 * @{
31 */
32/** @file
[17ceb72]33 * @brief UHCI Host controller driver routines
[9351353]34 */
[8064c2f6]35
[9351353]36#include <adt/list.h>
[8064c2f6]37#include <assert.h>
38#include <async.h>
[1ae74c6]39#include <ddi.h>
[8064c2f6]40#include <device/hw_res_parsed.h>
41#include <fibril.h>
42#include <errno.h>
43#include <macros.h>
44#include <mem.h>
45#include <stdlib.h>
[8d2dd7f2]46#include <stdint.h>
[8064c2f6]47#include <str_error.h>
[9351353]48
49#include <usb/debug.h>
50#include <usb/usb.h>
[8fc61c8]51#include <usb/host/utils/malloc32.h>
[e6b9182]52#include <usb/host/bandwidth.h>
[129b821f]53#include <usb/host/utility.h>
[9351353]54
[07f49ae]55#include "uhci_batch.h"
[929599a8]56#include "transfer_list.h"
[8064c2f6]57#include "hc.h"
[9351353]58
[8986412]59#define UHCI_INTR_ALLOW_INTERRUPTS \
[af81980]60 (UHCI_INTR_CRC | UHCI_INTR_COMPLETE | UHCI_INTR_SHORT_PACKET)
[8986412]61#define UHCI_STATUS_USED_INTERRUPTS \
62 (UHCI_STATUS_INTERRUPT | UHCI_STATUS_ERROR_INTERRUPT)
[af81980]63
[d57122c]64static const irq_pio_range_t uhci_irq_pio_ranges[] = {
65 {
[8486c07]66 .base = 0,
[d57122c]67 .size = sizeof(uhci_regs_t)
68 }
69};
[5fe0a697]70
[d57122c]71static const irq_cmd_t uhci_irq_commands[] = {
[8486c07]72 {
73 .cmd = CMD_PIO_READ_16,
74 .dstarg = 1,
75 .addr = NULL
76 },
77 {
78 .cmd = CMD_AND,
79 .srcarg = 1,
80 .dstarg = 2,
81 .value = UHCI_STATUS_USED_INTERRUPTS | UHCI_STATUS_NM_INTERRUPTS
82 },
83 {
84 .cmd = CMD_PREDICATE,
85 .srcarg = 2,
86 .value = 2
87 },
88 {
89 .cmd = CMD_PIO_WRITE_A_16,
90 .srcarg = 1,
91 .addr = NULL
92 },
93 {
94 .cmd = CMD_ACCEPT
95 }
[dfe4955]96};
[302a4b6]97
[3afb758]98static void hc_init_hw(const hc_t *instance);
[4db49344]99static int hc_init_mem_structures(hc_t *instance);
[3afb758]100static int hc_init_transfer_lists(hc_t *instance);
[9351353]101
[c01cd32]102static int hc_debug_checker(void *arg);
[dfe4955]103
[76fbd9a]104
[d57122c]105/** Generate IRQ code.
[6210a333]106 * @param[out] code IRQ code structure.
[ba4a03a5]107 * @param[in] hw_res Device's resources.
[dfe4955]108 *
109 * @return Error code.
110 */
[32fb6bce]111int hc_gen_irq_code(irq_code_t *code, hc_device_t *hcd, const hw_res_list_parsed_t *hw_res)
[dfe4955]112{
[6210a333]113 assert(code);
[ba4a03a5]114 assert(hw_res);
[6210a333]115
[ba4a03a5]116 if (hw_res->irqs.count != 1 || hw_res->io_ranges.count != 1)
117 return EINVAL;
118 const addr_range_t regs = hw_res->io_ranges.ranges[0];
119
120 if (RNGSZ(regs) < sizeof(uhci_regs_t))
[dfe4955]121 return EOVERFLOW;
122
[6210a333]123 code->ranges = malloc(sizeof(uhci_irq_pio_ranges));
124 if (code->ranges == NULL)
125 return ENOMEM;
126
127 code->cmds = malloc(sizeof(uhci_irq_commands));
128 if (code->cmds == NULL) {
129 free(code->ranges);
130 return ENOMEM;
131 }
132
133 code->rangecount = ARRAY_SIZE(uhci_irq_pio_ranges);
134 code->cmdcount = ARRAY_SIZE(uhci_irq_commands);
[dfe4955]135
[6210a333]136 memcpy(code->ranges, uhci_irq_pio_ranges, sizeof(uhci_irq_pio_ranges));
[ba4a03a5]137 code->ranges[0].base = RNGABS(regs);
[6210a333]138
139 memcpy(code->cmds, uhci_irq_commands, sizeof(uhci_irq_commands));
[ba4a03a5]140 uhci_regs_t *registers = (uhci_regs_t *) RNGABSPTR(regs);
[6210a333]141 code->cmds[0].addr = (void*)&registers->usbsts;
142 code->cmds[3].addr = (void*)&registers->usbsts;
[dfe4955]143
[a1732929]144 usb_log_debug("I/O regs at %p (size %zu), IRQ %d.",
[ba4a03a5]145 RNGABSPTR(regs), RNGSZ(regs), hw_res->irqs.irqs[0]);
146
147 return hw_res->irqs.irqs[0];
[dfe4955]148}
[76fbd9a]149
[3afb758]150/** Take action based on the interrupt cause.
151 *
[4bfcf22]152 * @param[in] hcd HCD structure to use.
[3afb758]153 * @param[in] status Value of the status register at the time of interrupt.
154 *
155 * Interrupt might indicate:
156 * - transaction completed, either by triggering IOC, SPD, or an error
157 * - some kind of device error
158 * - resume from suspend state (not implemented)
159 */
[32fb6bce]160static void hc_interrupt(bus_t *bus, uint32_t status)
[3afb758]161{
[32fb6bce]162 hc_t *instance = bus_to_hc(bus);
163
[3afb758]164 /* Lower 2 bits are transaction error and transaction complete */
165 if (status & (UHCI_STATUS_INTERRUPT | UHCI_STATUS_ERROR_INTERRUPT)) {
[4db49344]166 transfer_list_check_finished(&instance->transfers_interrupt);
167 transfer_list_check_finished(&instance->transfers_control_slow);
168 transfer_list_check_finished(&instance->transfers_control_full);
169 transfer_list_check_finished(&instance->transfers_bulk_full);
[3afb758]170 }
[4db49344]171
[3afb758]172 /* Resume interrupts are not supported */
173 if (status & UHCI_STATUS_RESUME) {
[a1732929]174 usb_log_error("Resume interrupt!");
[3afb758]175 }
176
177 /* Bits 4 and 5 indicate hc error */
178 if (status & (UHCI_STATUS_PROCESS_ERROR | UHCI_STATUS_SYSTEM_ERROR)) {
[a1732929]179 usb_log_error("UHCI hardware failure!.");
[3afb758]180 ++instance->hw_failures;
181 transfer_list_abort_all(&instance->transfers_interrupt);
182 transfer_list_abort_all(&instance->transfers_control_slow);
183 transfer_list_abort_all(&instance->transfers_control_full);
184 transfer_list_abort_all(&instance->transfers_bulk_full);
185
186 if (instance->hw_failures < UHCI_ALLOWED_HW_FAIL) {
187 /* reinitialize hw, this triggers virtual disconnect*/
188 hc_init_hw(instance);
189 } else {
[a1732929]190 usb_log_fatal("Too many UHCI hardware failures!.");
[32fb6bce]191 hc_gone(&instance->base);
[3afb758]192 }
193 }
194}
[76fbd9a]195
[02cacce]196/** Initialize UHCI hc driver structure
[9351353]197 *
198 * @param[in] instance Memory place to initialize.
[7de1988c]199 * @param[in] regs Range of device's I/O control registers.
[23f40280]200 * @param[in] interrupts True if hw interrupts should be used.
[9351353]201 * @return Error code.
202 * @note Should be called only once on any structure.
[17ceb72]203 *
204 * Initializes memory structures, starts up hw, and launches debugger and
205 * interrupt fibrils.
[9351353]206 */
[32fb6bce]207int hc_add(hc_device_t *hcd, const hw_res_list_parsed_t *hw_res)
[9351353]208{
[32fb6bce]209 hc_t *instance = hcd_to_hc(hcd);
[7813516]210 assert(hw_res);
211 if (hw_res->io_ranges.count != 1 ||
212 hw_res->io_ranges.ranges[0].size < sizeof(uhci_regs_t))
213 return EINVAL;
[9351353]214
[fcc525d]215 instance->hw_failures = 0;
216
[9351353]217 /* allow access to hc control registers */
[7813516]218 int ret = pio_enable_range(&hw_res->io_ranges.ranges[0],
219 (void **) &instance->registers);
[e0d8b740]220 if (ret != EOK) {
[a1732929]221 usb_log_error("Failed to gain access to registers: %s.",
[7813516]222 str_error(ret));
[e0d8b740]223 return ret;
224 }
225
[a1732929]226 usb_log_debug("Device registers at %" PRIx64 " (%zuB) accessible.",
[7813516]227 hw_res->io_ranges.ranges[0].address.absolute,
228 hw_res->io_ranges.ranges[0].size);
[3afb758]229
[4db49344]230 ret = hc_init_mem_structures(instance);
[e0d8b740]231 if (ret != EOK) {
[a1732929]232 usb_log_error("Failed to init UHCI memory structures: %s.",
[e0d8b740]233 str_error(ret));
234 // TODO: we should disable pio here
235 return ret;
236 }
[7265558]237
[e4d7363]238 return EOK;
239}
240
[32fb6bce]241int hc_start(hc_device_t *hcd)
[e4d7363]242{
[32fb6bce]243 hc_t *instance = hcd_to_hc(hcd);
[c01cd32]244 hc_init_hw(instance);
[ea993d18]245 (void)hc_debug_checker;
[9351353]246
[32fb6bce]247 return uhci_rh_init(&instance->rh, instance->registers->ports, "uhci");
[9351353]248}
[76fbd9a]249
[129b821f]250int hc_setup_roothub(hc_device_t *hcd)
251{
252 return hc_setup_virtual_root_hub(hcd, USB_SPEED_FULL);
253}
254
[7813516]255/** Safely dispose host controller internal structures
256 *
257 * @param[in] instance Host controller structure to use.
258 */
[32fb6bce]259int hc_gone(hc_device_t *instance)
[7813516]260{
261 assert(instance);
262 //TODO Implement
[32fb6bce]263 return ENOTSUP;
[7813516]264}
265
[17ceb72]266/** Initialize UHCI hc hw resources.
[9351353]267 *
268 * @param[in] instance UHCI structure to use.
[17ceb72]269 * For magic values see UHCI Design Guide
[9351353]270 */
[3afb758]271void hc_init_hw(const hc_t *instance)
[9351353]272{
273 assert(instance);
[dfe4955]274 uhci_regs_t *registers = instance->registers;
[9351353]275
276 /* Reset everything, who knows what touched it before us */
277 pio_write_16(&registers->usbcmd, UHCI_CMD_GLOBAL_RESET);
[26858040]278 async_usleep(50000); /* 50ms according to USB spec(root hub reset) */
[9351353]279 pio_write_16(&registers->usbcmd, 0);
280
[26858040]281 /* Reset hc, all states and counters. Hope that hw is not broken */
[9351353]282 pio_write_16(&registers->usbcmd, UHCI_CMD_HCRESET);
283 do { async_usleep(10); }
284 while ((pio_read_16(&registers->usbcmd) & UHCI_CMD_HCRESET) != 0);
285
[eb2a48a]286 /* Set frame to exactly 1ms */
287 pio_write_8(&registers->sofmod, 64);
288
289 /* Set frame list pointer */
[9351353]290 const uint32_t pa = addr_to_phys(instance->frame_list);
291 pio_write_32(&registers->flbaseadd, pa);
292
[32fb6bce]293 if (instance->base.irq_cap >= 0) {
[ff34e5a]294 /* Enable all interrupts, but resume interrupt */
295 pio_write_16(&instance->registers->usbintr,
[8986412]296 UHCI_INTR_ALLOW_INTERRUPTS);
[ff34e5a]297 }
[9351353]298
[26858040]299 const uint16_t cmd = pio_read_16(&registers->usbcmd);
300 if (cmd != 0)
[a1732929]301 usb_log_warning("Previous command value: %x.", cmd);
[9351353]302
303 /* Start the hc with large(64B) packet FSBR */
304 pio_write_16(&registers->usbcmd,
305 UHCI_CMD_RUN_STOP | UHCI_CMD_MAX_PACKET | UHCI_CMD_CONFIGURE);
306}
[76fbd9a]307
[6832245]308static usb_transfer_batch_t *create_transfer_batch(endpoint_t *ep)
[5fd9c30]309{
310 uhci_transfer_batch_t *batch = uhci_transfer_batch_create(ep);
311 return &batch->base;
312}
313
314static void destroy_transfer_batch(usb_transfer_batch_t *batch)
315{
316 uhci_transfer_batch_destroy(uhci_transfer_batch_get(batch));
317}
318
[e8277c0]319static endpoint_t *endpoint_create(device_t *device, const usb_endpoint_descriptors_t *desc)
320{
321 endpoint_t *ep = calloc(1, sizeof(uhci_endpoint_t));
322 if (ep)
323 endpoint_init(ep, device, desc);
324 return ep;
325}
326
[4db49344]327static int endpoint_register(endpoint_t *ep)
328{
329 hc_t * const hc = bus_to_hc(endpoint_get_bus(ep));
330
[d369b3b]331 const int err = usb2_bus_endpoint_register(&hc->bus_helper, ep);
[4db49344]332 if (err)
333 return err;
334
335 transfer_list_t *list = hc->transfers[ep->device->speed][ep->transfer_type];
336 if (!list)
337 /*
338 * We don't support this combination (e.g. isochronous). Do not
339 * fail early, because that would block any device with these
340 * endpoints from connecting. Instead, make sure these transfers
341 * are denied soon enough with ENOTSUP not to fail on asserts.
342 */
343 return EOK;
344
345 endpoint_set_online(ep, &list->guard);
346 return EOK;
347}
348
[0892663a]349static void endpoint_unregister(endpoint_t *ep)
[5dfb70c9]350{
[929599a8]351 hc_t * const hc = bus_to_hc(endpoint_get_bus(ep));
[d369b3b]352 usb2_bus_endpoint_unregister(&hc->bus_helper, ep);
[c913f71e]353
[2755a622]354 // Check for the roothub, as it does not schedule into lists
[129b821f]355 if (ep->device->address == uhci_rh_get_address(&hc->rh)) {
[2755a622]356 // FIXME: We shall check the roothub for active transfer. But
357 // as it is polling, there is no way to make it stop doing so.
358 // Return after rewriting uhci rh.
359 return;
360 }
361
362 transfer_list_t *list = hc->transfers[ep->device->speed][ep->transfer_type];
[3ac86a4]363 if (!list)
364 /*
365 * We don't support this combination (e.g. isochronous),
366 * so no transfer can be active.
367 */
368 return;
[2755a622]369
370 fibril_mutex_lock(&list->guard);
[4db49344]371
372 endpoint_set_offline_locked(ep);
373 /* From now on, no other transfer will be scheduled. */
374
375 if (!ep->active_batch) {
376 fibril_mutex_unlock(&list->guard);
377 return;
[5dfb70c9]378 }
[929599a8]379
[4db49344]380 /* First, offer the batch a short chance to be finished. */
381 endpoint_wait_timeout_locked(ep, 10000);
382
383 if (!ep->active_batch) {
384 fibril_mutex_unlock(&list->guard);
385 return;
[929599a8]386 }
[4db49344]387
388 uhci_transfer_batch_t * const batch =
389 uhci_transfer_batch_get(ep->active_batch);
390
391 /* Remove the batch from the schedule to stop it from being finished. */
392 endpoint_deactivate_locked(ep);
393 transfer_list_remove_batch(list, batch);
394
395 fibril_mutex_unlock(&list->guard);
396
397 /*
398 * We removed the batch from software schedule only, it's still possible
399 * that HC has it in its caches. Better wait a while before we release
400 * the buffers.
401 */
402 async_usleep(20000);
403 batch->base.error = EINTR;
404 batch->base.transferred_size = 0;
405 usb_transfer_batch_finish(&batch->base);
[5dfb70c9]406}
407
[d369b3b]408static int device_enumerate(device_t *dev)
409{
410 hc_t * const hc = bus_to_hc(dev->bus);
411 return usb2_bus_device_enumerate(&hc->bus_helper, dev);
412}
413
[32fb6bce]414static int hc_status(bus_t *, uint32_t *);
415static int hc_schedule(usb_transfer_batch_t *);
416
[6832245]417static const bus_ops_t uhci_bus_ops = {
[32fb6bce]418 .interrupt = hc_interrupt,
419 .status = hc_status,
420
[d369b3b]421 .device_enumerate = device_enumerate,
422
[e8277c0]423 .endpoint_create = endpoint_create,
[4db49344]424 .endpoint_register = endpoint_register,
[0892663a]425 .endpoint_unregister = endpoint_unregister,
426
[6832245]427 .batch_create = create_transfer_batch,
[32fb6bce]428 .batch_schedule = hc_schedule,
[6832245]429 .batch_destroy = destroy_transfer_batch,
430};
431
[17ceb72]432/** Initialize UHCI hc memory structures.
[9351353]433 *
434 * @param[in] instance UHCI structure to use.
435 * @return Error code
436 * @note Should be called only once on any structure.
[17ceb72]437 *
438 * Structures:
439 * - transfer lists (queue heads need to be accessible by the hw)
440 * - frame list page (needs to be one UHCI hw accessible 4K page)
[9351353]441 */
[4db49344]442int hc_init_mem_structures(hc_t *instance)
[9351353]443{
444 assert(instance);
445
[d369b3b]446 usb2_bus_helper_init(&instance->bus_helper, &bandwidth_accounting_usb11);
[e6b9182]447
[d369b3b]448 bus_init(&instance->bus, sizeof(device_t));
449 instance->bus.ops = &uhci_bus_ops;
[5fd9c30]450
[d369b3b]451 hc_device_setup(&instance->base, &instance->bus);
[32fb6bce]452
[3afb758]453 /* Init USB frame list page */
[9351353]454 instance->frame_list = get_page();
[26858040]455 if (!instance->frame_list) {
456 return ENOMEM;
457 }
[a1732929]458 usb_log_debug("Initialized frame list at %p.", instance->frame_list);
[9351353]459
[3afb758]460 /* Init transfer lists */
461 int ret = hc_init_transfer_lists(instance);
462 if (ret != EOK) {
[a1732929]463 usb_log_error("Failed to initialize transfer lists.");
[3afb758]464 return_page(instance->frame_list);
465 return ENOMEM;
466 }
[4db49344]467 list_initialize(&instance->pending_endpoints);
[a1732929]468 usb_log_debug("Initialized transfer lists.");
[3afb758]469
470
[9351353]471 /* Set all frames to point to the first queue head */
[302a4b6]472 const uint32_t queue = LINK_POINTER_QH(
473 addr_to_phys(instance->transfers_interrupt.queue_head));
[75f9dcd]474
475 for (unsigned i = 0; i < UHCI_FRAME_LIST_COUNT; ++i) {
[9351353]476 instance->frame_list[i] = queue;
477 }
478
479 return EOK;
480}
[76fbd9a]481
[17ceb72]482/** Initialize UHCI hc transfer lists.
[9351353]483 *
484 * @param[in] instance UHCI structure to use.
485 * @return Error code
486 * @note Should be called only once on any structure.
[17ceb72]487 *
488 * Initializes transfer lists and sets them in one chain to support proper
489 * USB scheduling. Sets pointer table for quick access.
[9351353]490 */
[c01cd32]491int hc_init_transfer_lists(hc_t *instance)
[9351353]492{
493 assert(instance);
[27205841]494#define SETUP_TRANSFER_LIST(type, name) \
495do { \
496 int ret = transfer_list_init(&instance->transfers_##type, name); \
[9351353]497 if (ret != EOK) { \
[a1732929]498 usb_log_error("Failed to setup %s transfer list: %s.", \
[26858040]499 name, str_error(ret)); \
[9351353]500 transfer_list_fini(&instance->transfers_bulk_full); \
501 transfer_list_fini(&instance->transfers_control_full); \
502 transfer_list_fini(&instance->transfers_control_slow); \
503 transfer_list_fini(&instance->transfers_interrupt); \
504 return ret; \
[27205841]505 } \
506} while (0)
507
508 SETUP_TRANSFER_LIST(bulk_full, "BULK FULL");
509 SETUP_TRANSFER_LIST(control_full, "CONTROL FULL");
510 SETUP_TRANSFER_LIST(control_slow, "CONTROL LOW");
511 SETUP_TRANSFER_LIST(interrupt, "INTERRUPT");
512#undef SETUP_TRANSFER_LIST
513 /* Connect lists into one schedule */
[9351353]514 transfer_list_set_next(&instance->transfers_control_full,
515 &instance->transfers_bulk_full);
516 transfer_list_set_next(&instance->transfers_control_slow,
517 &instance->transfers_control_full);
518 transfer_list_set_next(&instance->transfers_interrupt,
519 &instance->transfers_control_slow);
520
[e247d83]521 /*FSBR, This feature is not needed (adds no benefit) and is supposedly
522 * buggy on certain hw, enable at your own risk. */
[9351353]523#ifdef FSBR
524 transfer_list_set_next(&instance->transfers_bulk_full,
[302a4b6]525 &instance->transfers_control_full);
[9351353]526#endif
527
528 /* Assign pointers to be used during scheduling */
529 instance->transfers[USB_SPEED_FULL][USB_TRANSFER_INTERRUPT] =
530 &instance->transfers_interrupt;
531 instance->transfers[USB_SPEED_LOW][USB_TRANSFER_INTERRUPT] =
532 &instance->transfers_interrupt;
533 instance->transfers[USB_SPEED_FULL][USB_TRANSFER_CONTROL] =
534 &instance->transfers_control_full;
535 instance->transfers[USB_SPEED_LOW][USB_TRANSFER_CONTROL] =
536 &instance->transfers_control_slow;
537 instance->transfers[USB_SPEED_FULL][USB_TRANSFER_BULK] =
538 &instance->transfers_bulk_full;
539
540 return EOK;
541}
[76fbd9a]542
[32fb6bce]543static int hc_status(bus_t *bus, uint32_t *status)
[e26a9d95]544{
[32fb6bce]545 hc_t *instance = bus_to_hc(bus);
[e26a9d95]546 assert(status);
547
548 *status = 0;
549 if (instance->registers) {
550 uint16_t s = pio_read_16(&instance->registers->usbsts);
551 pio_write_16(&instance->registers->usbsts, s);
552 *status = s;
553 }
554 return EOK;
555}
556
[4db49344]557/**
558 * Schedule batch for execution.
[9351353]559 *
560 * @param[in] instance UHCI structure to use.
561 * @param[in] batch Transfer batch to schedule.
562 * @return Error code
563 */
[32fb6bce]564static int hc_schedule(usb_transfer_batch_t *batch)
[9351353]565{
[929599a8]566 uhci_transfer_batch_t *uhci_batch = uhci_transfer_batch_get(batch);
567 endpoint_t *ep = batch->ep;
568 hc_t *hc = bus_to_hc(endpoint_get_bus(ep));
[c95c00e]569
[929599a8]570 if (batch->target.address == uhci_rh_get_address(&hc->rh))
571 return uhci_rh_schedule(&hc->rh, batch);
[c95c00e]572
[4db49344]573 transfer_list_t * const list =
574 hc->transfers[ep->device->speed][ep->transfer_type];
[9351353]575
[4db49344]576 if (!list)
577 return ENOTSUP;
[76fbd9a]578
[4db49344]579 int err;
580 if ((err = uhci_transfer_batch_prepare(uhci_batch)))
581 return err;
[929599a8]582
[4db49344]583 return transfer_list_add_batch(list, uhci_batch);
[929599a8]584}
585
[9351353]586/** Debug function, checks consistency of memory structures.
587 *
588 * @param[in] arg UHCI structure to use.
[17ceb72]589 * @return EOK (should never return)
[9351353]590 */
[c01cd32]591int hc_debug_checker(void *arg)
[9351353]592{
[6f122df]593 hc_t *instance = arg;
[9351353]594 assert(instance);
595
596#define QH(queue) \
597 instance->transfers_##queue.queue_head
598
599 while (1) {
600 const uint16_t cmd = pio_read_16(&instance->registers->usbcmd);
601 const uint16_t sts = pio_read_16(&instance->registers->usbsts);
602 const uint16_t intr =
603 pio_read_16(&instance->registers->usbintr);
604
605 if (((cmd & UHCI_CMD_RUN_STOP) != 1) || (sts != 0)) {
[a1732929]606 usb_log_debug2("Command: %X Status: %X Intr: %x",
[9351353]607 cmd, sts, intr);
608 }
609
[e247d83]610 const uintptr_t frame_list =
[9351353]611 pio_read_32(&instance->registers->flbaseadd) & ~0xfff;
612 if (frame_list != addr_to_phys(instance->frame_list)) {
[a1732929]613 usb_log_debug("Framelist address: %p vs. %p.",
[4125b7d]614 (void *) frame_list,
615 (void *) addr_to_phys(instance->frame_list));
[9351353]616 }
617
618 int frnum = pio_read_16(&instance->registers->frnum) & 0x3ff;
619
620 uintptr_t expected_pa = instance->frame_list[frnum]
621 & LINK_POINTER_ADDRESS_MASK;
622 uintptr_t real_pa = addr_to_phys(QH(interrupt));
623 if (expected_pa != real_pa) {
[a1732929]624 usb_log_debug("Interrupt QH: %p (frame %d) vs. %p.",
[4125b7d]625 (void *) expected_pa, frnum, (void *) real_pa);
[9351353]626 }
627
628 expected_pa = QH(interrupt)->next & LINK_POINTER_ADDRESS_MASK;
629 real_pa = addr_to_phys(QH(control_slow));
630 if (expected_pa != real_pa) {
[a1732929]631 usb_log_debug("Control Slow QH: %p vs. %p.",
[4125b7d]632 (void *) expected_pa, (void *) real_pa);
[9351353]633 }
634
635 expected_pa = QH(control_slow)->next & LINK_POINTER_ADDRESS_MASK;
636 real_pa = addr_to_phys(QH(control_full));
637 if (expected_pa != real_pa) {
[a1732929]638 usb_log_debug("Control Full QH: %p vs. %p.",
[4125b7d]639 (void *) expected_pa, (void *) real_pa);
[9351353]640 }
641
642 expected_pa = QH(control_full)->next & LINK_POINTER_ADDRESS_MASK;
643 real_pa = addr_to_phys(QH(bulk_full));
644 if (expected_pa != real_pa ) {
[a1732929]645 usb_log_debug("Bulk QH: %p vs. %p.",
[4125b7d]646 (void *) expected_pa, (void *) real_pa);
[9351353]647 }
648 async_usleep(UHCI_DEBUGER_TIMEOUT);
649 }
650 return EOK;
651#undef QH
652}
653/**
654 * @}
655 */
Note: See TracBrowser for help on using the repository browser.