source: mainline/uspace/drv/bus/usb/ohci/hc.c@ 320bd52

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 320bd52 was b5111c46, checked in by Jiri Svoboda <jiri@…>, 11 years ago

Convert OHCI and UHCI away from DDF_DATA_IMPLANT.

  • Property mode set to 100644
File size: 19.6 KB
Line 
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 */
28
29/** @addtogroup drvusbohcihc
30 * @{
31 */
32/** @file
33 * @brief OHCI Host controller driver routines
34 */
35
36#include <errno.h>
37#include <stdbool.h>
38#include <str_error.h>
39#include <adt/list.h>
40#include <libarch/ddi.h>
41
42#include <usb/debug.h>
43#include <usb/usb.h>
44#include <usb/ddfiface.h>
45
46#include "hc.h"
47#include "ohci_endpoint.h"
48
49#define OHCI_USED_INTERRUPTS \
50 (I_SO | I_WDH | I_UE | I_RHSC)
51
52static const irq_pio_range_t ohci_pio_ranges[] = {
53 {
54 .base = 0,
55 .size = sizeof(ohci_regs_t)
56 }
57};
58
59static const irq_cmd_t ohci_irq_commands[] = {
60 {
61 .cmd = CMD_PIO_READ_32,
62 .dstarg = 1,
63 .addr = NULL
64 },
65 {
66 .cmd = CMD_AND,
67 .srcarg = 1,
68 .dstarg = 2,
69 .value = 0
70 },
71 {
72 .cmd = CMD_PREDICATE,
73 .srcarg = 2,
74 .value = 2
75 },
76 {
77 .cmd = CMD_PIO_WRITE_A_32,
78 .srcarg = 1,
79 .addr = NULL
80 },
81 {
82 .cmd = CMD_ACCEPT
83 }
84};
85
86enum {
87 /** Number of PIO ranges used in IRQ code */
88 hc_irq_pio_range_count =
89 sizeof(ohci_pio_ranges) / sizeof(irq_pio_range_t),
90
91 /** Number of commands used in IRQ code */
92 hc_irq_cmd_count =
93 sizeof(ohci_irq_commands) / sizeof(irq_cmd_t)
94};
95
96static void hc_gain_control(hc_t *instance);
97static void hc_start(hc_t *instance);
98static int hc_init_transfer_lists(hc_t *instance);
99static int hc_init_memory(hc_t *instance);
100static int interrupt_emulator(hc_t *instance);
101static int hc_schedule(hcd_t *hcd, usb_transfer_batch_t *batch);
102
103/** Generate IRQ code.
104 * @param[out] ranges PIO ranges buffer.
105 * @param[in] ranges_size Size of the ranges buffer (bytes).
106 * @param[out] cmds Commands buffer.
107 * @param[in] cmds_size Size of the commands buffer (bytes).
108 * @param[in] regs Device's register range.
109 *
110 * @return Error code.
111 */
112int
113hc_get_irq_code(irq_pio_range_t ranges[], size_t ranges_size, irq_cmd_t cmds[],
114 size_t cmds_size, addr_range_t *regs)
115{
116 if ((ranges_size < sizeof(ohci_pio_ranges)) ||
117 (cmds_size < sizeof(ohci_irq_commands)) ||
118 (RNGSZ(*regs) < sizeof(ohci_regs_t)))
119 return EOVERFLOW;
120
121 memcpy(ranges, ohci_pio_ranges, sizeof(ohci_pio_ranges));
122 ranges[0].base = RNGABS(*regs);
123
124 memcpy(cmds, ohci_irq_commands, sizeof(ohci_irq_commands));
125 ohci_regs_t *registers = (ohci_regs_t *) RNGABSPTR(*regs);
126 cmds[0].addr = (void *) &registers->interrupt_status;
127 cmds[3].addr = (void *) &registers->interrupt_status;
128 OHCI_WR(cmds[1].value, OHCI_USED_INTERRUPTS);
129
130 return EOK;
131}
132
133/** Register interrupt handler.
134 *
135 * @param[in] device Host controller DDF device
136 * @param[in] regs Register range
137 * @param[in] irq Interrupt number
138 * @paran[in] handler Interrupt handler
139 *
140 * @return EOK on success or negative error code
141 */
142int hc_register_irq_handler(ddf_dev_t *device, addr_range_t *regs, int irq,
143 interrupt_handler_t handler)
144{
145 int rc;
146
147 irq_pio_range_t irq_ranges[hc_irq_pio_range_count];
148 irq_cmd_t irq_cmds[hc_irq_cmd_count];
149
150 irq_code_t irq_code = {
151 .rangecount = hc_irq_pio_range_count,
152 .ranges = irq_ranges,
153 .cmdcount = hc_irq_cmd_count,
154 .cmds = irq_cmds
155 };
156
157 rc = hc_get_irq_code(irq_ranges, sizeof(irq_ranges), irq_cmds,
158 sizeof(irq_cmds), regs);
159 if (rc != EOK) {
160 usb_log_error("Failed to generate IRQ code: %s.\n",
161 str_error(rc));
162 return rc;
163 }
164
165 /* Register handler to avoid interrupt lockup */
166 rc = register_interrupt_handler(device, irq, handler, &irq_code);
167 if (rc != EOK) {
168 usb_log_error("Failed to register interrupt handler: %s.\n",
169 str_error(rc));
170 return rc;
171 }
172
173 return EOK;
174}
175
176/** Announce OHCI root hub to the DDF
177 *
178 * @param[in] instance OHCI driver intance
179 * @param[in] hub_fun DDF fuction representing OHCI root hub
180 * @return Error code
181 */
182int hc_register_hub(hc_t *instance, ddf_fun_t *hub_fun)
183{
184 bool addr_reqd = false;
185 bool ep_added = false;
186 bool fun_bound = false;
187 int rc;
188
189 assert(instance);
190 assert(hub_fun);
191
192 /* Try to get address 1 for root hub. */
193 instance->rh.address = 1;
194 rc = usb_device_manager_request_address(
195 &instance->generic->dev_manager, &instance->rh.address, false,
196 USB_SPEED_FULL);
197 if (rc != EOK) {
198 usb_log_error("Failed to get OHCI root hub address: %s\n",
199 str_error(rc));
200 goto error;
201 }
202
203 addr_reqd = true;
204
205 rc = usb_endpoint_manager_add_ep(
206 &instance->generic->ep_manager, instance->rh.address, 0,
207 USB_DIRECTION_BOTH, USB_TRANSFER_CONTROL, USB_SPEED_FULL, 64,
208 0, NULL, NULL);
209 if (rc != EOK) {
210 usb_log_error("Failed to register root hub control endpoint: %s.\n",
211 str_error(rc));
212 goto error;
213 }
214
215 ep_added = true;
216
217 rc = ddf_fun_add_match_id(hub_fun, "usb&class=hub", 100);
218 if (rc != EOK) {
219 usb_log_error("Failed to add root hub match-id: %s.\n",
220 str_error(rc));
221 goto error;
222 }
223
224 rc = ddf_fun_bind(hub_fun);
225 if (rc != EOK) {
226 usb_log_error("Failed to bind root hub function: %s.\n",
227 str_error(rc));
228 goto error;
229 }
230
231 fun_bound = true;
232
233 rc = usb_device_manager_bind_address(&instance->generic->dev_manager,
234 instance->rh.address, ddf_fun_get_handle(hub_fun));
235 if (rc != EOK) {
236 usb_log_warning("Failed to bind root hub address: %s.\n",
237 str_error(rc));
238 }
239
240 return EOK;
241error:
242 if (fun_bound)
243 ddf_fun_unbind(hub_fun);
244 if (ep_added) {
245 usb_endpoint_manager_remove_ep(
246 &instance->generic->ep_manager, instance->rh.address, 0,
247 USB_DIRECTION_BOTH, NULL, NULL);
248 }
249 if (addr_reqd) {
250 usb_device_manager_release_address(
251 &instance->generic->dev_manager, instance->rh.address);
252 }
253 return rc;
254}
255
256/** Initialize OHCI hc driver structure
257 *
258 * @param[in] instance Memory place for the structure.
259 * @param[in] HC function node
260 * @param[in] regs Device's I/O registers range.
261 * @param[in] interrupts True if w interrupts should be used
262 * @return Error code
263 */
264int hc_init(hc_t *instance, ddf_fun_t *fun, addr_range_t *regs, bool interrupts)
265{
266 assert(instance);
267
268 int rc = pio_enable_range(regs, (void **) &instance->registers);
269 if (rc != EOK) {
270 usb_log_error("Failed to gain access to device registers: %s.\n",
271 str_error(rc));
272 return rc;
273 }
274
275 list_initialize(&instance->pending_batches);
276
277 instance->generic = ddf_fun_data_alloc(fun, sizeof(hcd_t));
278 if (instance->generic == NULL) {
279 usb_log_error("Out of memory.\n");
280 return ENOMEM;
281 }
282
283 hcd_init(instance->generic, USB_SPEED_FULL,
284 BANDWIDTH_AVAILABLE_USB11, bandwidth_count_usb11);
285 instance->generic->private_data = instance;
286 instance->generic->schedule = hc_schedule;
287 instance->generic->ep_add_hook = ohci_endpoint_init;
288 instance->generic->ep_remove_hook = ohci_endpoint_fini;
289
290 rc = hc_init_memory(instance);
291 if (rc != EOK) {
292 usb_log_error("Failed to create OHCI memory structures: %s.\n",
293 str_error(rc));
294 return rc;
295 }
296
297 fibril_mutex_initialize(&instance->guard);
298
299 hc_gain_control(instance);
300
301 if (!interrupts) {
302 instance->interrupt_emulator =
303 fibril_create((int(*)(void*))interrupt_emulator, instance);
304 fibril_add_ready(instance->interrupt_emulator);
305 }
306
307 rh_init(&instance->rh, instance->registers);
308 hc_start(instance);
309
310 return EOK;
311}
312
313void hc_enqueue_endpoint(hc_t *instance, const endpoint_t *ep)
314{
315 assert(instance);
316 assert(ep);
317
318 endpoint_list_t *list = &instance->lists[ep->transfer_type];
319 ohci_endpoint_t *ohci_ep = ohci_endpoint_get(ep);
320 assert(list);
321 assert(ohci_ep);
322
323 /* Enqueue ep */
324 switch (ep->transfer_type) {
325 case USB_TRANSFER_CONTROL:
326 OHCI_CLR(instance->registers->control, C_CLE);
327 endpoint_list_add_ep(list, ohci_ep);
328 OHCI_WR(instance->registers->control_current, 0);
329 OHCI_SET(instance->registers->control, C_CLE);
330 break;
331 case USB_TRANSFER_BULK:
332 OHCI_CLR(instance->registers->control, C_BLE);
333 endpoint_list_add_ep(list, ohci_ep);
334 OHCI_WR(instance->registers->bulk_current, 0);
335 OHCI_SET(instance->registers->control, C_BLE);
336 break;
337 case USB_TRANSFER_ISOCHRONOUS:
338 case USB_TRANSFER_INTERRUPT:
339 OHCI_CLR(instance->registers->control, C_PLE | C_IE);
340 endpoint_list_add_ep(list, ohci_ep);
341 OHCI_SET(instance->registers->control, C_PLE | C_IE);
342 break;
343 }
344}
345
346void hc_dequeue_endpoint(hc_t *instance, const endpoint_t *ep)
347{
348 assert(instance);
349 assert(ep);
350
351 /* Dequeue ep */
352 endpoint_list_t *list = &instance->lists[ep->transfer_type];
353 ohci_endpoint_t *ohci_ep = ohci_endpoint_get(ep);
354
355 assert(list);
356 assert(ohci_ep);
357 switch (ep->transfer_type) {
358 case USB_TRANSFER_CONTROL:
359 OHCI_CLR(instance->registers->control, C_CLE);
360 endpoint_list_remove_ep(list, ohci_ep);
361 OHCI_WR(instance->registers->control_current, 0);
362 OHCI_SET(instance->registers->control, C_CLE);
363 break;
364 case USB_TRANSFER_BULK:
365 OHCI_CLR(instance->registers->control, C_BLE);
366 endpoint_list_remove_ep(list, ohci_ep);
367 OHCI_WR(instance->registers->bulk_current, 0);
368 OHCI_SET(instance->registers->control, C_BLE);
369 break;
370 case USB_TRANSFER_ISOCHRONOUS:
371 case USB_TRANSFER_INTERRUPT:
372 OHCI_CLR(instance->registers->control, C_PLE | C_IE);
373 endpoint_list_remove_ep(list, ohci_ep);
374 OHCI_SET(instance->registers->control, C_PLE | C_IE);
375 break;
376 default:
377 break;
378 }
379}
380
381/** Add USB transfer to the schedule.
382 *
383 * @param[in] instance OHCI hc driver structure.
384 * @param[in] batch Batch representing the transfer.
385 * @return Error code.
386 */
387int hc_schedule(hcd_t *hcd, usb_transfer_batch_t *batch)
388{
389 assert(hcd);
390 hc_t *instance = hcd->private_data;
391 assert(instance);
392
393 /* Check for root hub communication */
394 if (batch->ep->address == instance->rh.address) {
395 usb_log_debug("OHCI root hub request.\n");
396 rh_request(&instance->rh, batch);
397 return EOK;
398 }
399 ohci_transfer_batch_t *ohci_batch = ohci_transfer_batch_get(batch);
400 if (!ohci_batch)
401 return ENOMEM;
402
403 fibril_mutex_lock(&instance->guard);
404 list_append(&ohci_batch->link, &instance->pending_batches);
405 ohci_transfer_batch_commit(ohci_batch);
406
407 /* Control and bulk schedules need a kick to start working */
408 switch (batch->ep->transfer_type)
409 {
410 case USB_TRANSFER_CONTROL:
411 OHCI_SET(instance->registers->command_status, CS_CLF);
412 break;
413 case USB_TRANSFER_BULK:
414 OHCI_SET(instance->registers->command_status, CS_BLF);
415 break;
416 default:
417 break;
418 }
419 fibril_mutex_unlock(&instance->guard);
420 return EOK;
421}
422
423/** Interrupt handling routine
424 *
425 * @param[in] instance OHCI hc driver structure.
426 * @param[in] status Value of the status register at the time of interrupt.
427 */
428void hc_interrupt(hc_t *instance, uint32_t status)
429{
430 status = OHCI_RD(status);
431 assert(instance);
432 if ((status & ~I_SF) == 0) /* ignore sof status */
433 return;
434 usb_log_debug2("OHCI(%p) interrupt: %x.\n", instance, status);
435 if (status & I_RHSC)
436 rh_interrupt(&instance->rh);
437
438 if (status & I_WDH) {
439 fibril_mutex_lock(&instance->guard);
440 usb_log_debug2("HCCA: %p-%#" PRIx32 " (%p).\n", instance->hcca,
441 OHCI_RD(instance->registers->hcca),
442 (void *) addr_to_phys(instance->hcca));
443 usb_log_debug2("Periodic current: %#" PRIx32 ".\n",
444 OHCI_RD(instance->registers->periodic_current));
445
446 link_t *current = list_first(&instance->pending_batches);
447 while (current && current != &instance->pending_batches.head) {
448 link_t *next = current->next;
449 ohci_transfer_batch_t *batch =
450 ohci_transfer_batch_from_link(current);
451
452 if (ohci_transfer_batch_is_complete(batch)) {
453 list_remove(current);
454 ohci_transfer_batch_finish_dispose(batch);
455 }
456
457 current = next;
458 }
459 fibril_mutex_unlock(&instance->guard);
460 }
461
462 if (status & I_UE) {
463 usb_log_fatal("Error like no other!\n");
464 hc_start(instance);
465 }
466
467}
468
469/** Check status register regularly
470 *
471 * @param[in] instance OHCI hc driver structure.
472 * @return Error code
473 */
474int interrupt_emulator(hc_t *instance)
475{
476 assert(instance);
477 usb_log_info("Started interrupt emulator.\n");
478 while (1) {
479 const uint32_t status = instance->registers->interrupt_status;
480 instance->registers->interrupt_status = status;
481 hc_interrupt(instance, status);
482 async_usleep(10000);
483 }
484 return EOK;
485}
486
487/** Turn off any (BIOS)driver that might be in control of the device.
488 *
489 * This function implements routines described in chapter 5.1.1.3 of the OHCI
490 * specification (page 40, pdf page 54).
491 *
492 * @param[in] instance OHCI hc driver structure.
493 */
494void hc_gain_control(hc_t *instance)
495{
496 assert(instance);
497
498 usb_log_debug("Requesting OHCI control.\n");
499 if (OHCI_RD(instance->registers->revision) & R_LEGACY_FLAG) {
500 /* Turn off legacy emulation, it should be enough to zero
501 * the lowest bit, but it caused problems. Thus clear all
502 * except GateA20 (causes restart on some hw).
503 * See page 145 of the specs for details.
504 */
505 volatile uint32_t *ohci_emulation_reg =
506 (uint32_t*)((char*)instance->registers + LEGACY_REGS_OFFSET);
507 usb_log_debug("OHCI legacy register %p: %x.\n",
508 ohci_emulation_reg, OHCI_RD(*ohci_emulation_reg));
509 /* Zero everything but A20State */
510 OHCI_CLR(*ohci_emulation_reg, ~0x100);
511 usb_log_debug(
512 "OHCI legacy register (should be 0 or 0x100) %p: %x.\n",
513 ohci_emulation_reg, OHCI_RD(*ohci_emulation_reg));
514 }
515
516 /* Interrupt routing enabled => smm driver is active */
517 if (OHCI_RD(instance->registers->control) & C_IR) {
518 usb_log_debug("SMM driver: request ownership change.\n");
519 OHCI_SET(instance->registers->command_status, CS_OCR);
520 /* Hope that SMM actually knows its stuff or we can hang here */
521 while (OHCI_RD(instance->registers->control & C_IR)) {
522 async_usleep(1000);
523 }
524 usb_log_info("SMM driver: Ownership taken.\n");
525 C_HCFS_SET(instance->registers->control, C_HCFS_RESET);
526 async_usleep(50000);
527 return;
528 }
529
530 const unsigned hc_status = C_HCFS_GET(instance->registers->control);
531 /* Interrupt routing disabled && status != USB_RESET => BIOS active */
532 if (hc_status != C_HCFS_RESET) {
533 usb_log_debug("BIOS driver found.\n");
534 if (hc_status == C_HCFS_OPERATIONAL) {
535 usb_log_info("BIOS driver: HC operational.\n");
536 return;
537 }
538 /* HC is suspended assert resume for 20ms */
539 C_HCFS_SET(instance->registers->control, C_HCFS_RESUME);
540 async_usleep(20000);
541 usb_log_info("BIOS driver: HC resumed.\n");
542 return;
543 }
544
545 /* HC is in reset (hw startup) => no other driver
546 * maintain reset for at least the time specified in USB spec (50 ms)*/
547 usb_log_debug("Host controller found in reset state.\n");
548 async_usleep(50000);
549}
550
551/** OHCI hw initialization routine.
552 *
553 * @param[in] instance OHCI hc driver structure.
554 */
555void hc_start(hc_t *instance)
556{
557 /* OHCI guide page 42 */
558 assert(instance);
559 usb_log_debug2("Started hc initialization routine.\n");
560
561 /* Save contents of fm_interval register */
562 const uint32_t fm_interval = OHCI_RD(instance->registers->fm_interval);
563 usb_log_debug2("Old value of HcFmInterval: %x.\n", fm_interval);
564
565 /* Reset hc */
566 usb_log_debug2("HC reset.\n");
567 size_t time = 0;
568 OHCI_WR(instance->registers->command_status, CS_HCR);
569 while (OHCI_RD(instance->registers->command_status) & CS_HCR) {
570 async_usleep(10);
571 time += 10;
572 }
573 usb_log_debug2("HC reset complete in %zu us.\n", time);
574
575 /* Restore fm_interval */
576 OHCI_WR(instance->registers->fm_interval, fm_interval);
577 assert((OHCI_RD(instance->registers->command_status) & CS_HCR) == 0);
578
579 /* hc is now in suspend state */
580 usb_log_debug2("HC should be in suspend state(%x).\n",
581 OHCI_RD(instance->registers->control));
582
583 /* Use HCCA */
584 OHCI_WR(instance->registers->hcca, addr_to_phys(instance->hcca));
585
586 /* Use queues */
587 OHCI_WR(instance->registers->bulk_head,
588 instance->lists[USB_TRANSFER_BULK].list_head_pa);
589 usb_log_debug2("Bulk HEAD set to: %p (%#" PRIx32 ").\n",
590 instance->lists[USB_TRANSFER_BULK].list_head,
591 instance->lists[USB_TRANSFER_BULK].list_head_pa);
592
593 OHCI_WR(instance->registers->control_head,
594 instance->lists[USB_TRANSFER_CONTROL].list_head_pa);
595 usb_log_debug2("Control HEAD set to: %p (%#" PRIx32 ").\n",
596 instance->lists[USB_TRANSFER_CONTROL].list_head,
597 instance->lists[USB_TRANSFER_CONTROL].list_head_pa);
598
599 /* Enable queues */
600 OHCI_SET(instance->registers->control, (C_PLE | C_IE | C_CLE | C_BLE));
601 usb_log_debug("Queues enabled(%x).\n",
602 OHCI_RD(instance->registers->control));
603
604 /* Enable interrupts */
605 OHCI_WR(instance->registers->interrupt_enable, OHCI_USED_INTERRUPTS);
606 usb_log_debug("Enabled interrupts: %x.\n",
607 OHCI_RD(instance->registers->interrupt_enable));
608 OHCI_WR(instance->registers->interrupt_enable, I_MI);
609
610 /* Set periodic start to 90% */
611 const uint32_t frame_length =
612 (fm_interval >> FMI_FI_SHIFT) & FMI_FI_MASK;
613 OHCI_WR(instance->registers->periodic_start,
614 ((frame_length / 10) * 9) & PS_MASK << PS_SHIFT);
615 usb_log_debug2("All periodic start set to: %x(%u - 90%% of %d).\n",
616 OHCI_RD(instance->registers->periodic_start),
617 OHCI_RD(instance->registers->periodic_start), frame_length);
618 C_HCFS_SET(instance->registers->control, C_HCFS_OPERATIONAL);
619 usb_log_debug("OHCI HC up and running (ctl_reg=0x%x).\n",
620 OHCI_RD(instance->registers->control));
621}
622
623/** Initialize schedule queues
624 *
625 * @param[in] instance OHCI hc driver structure
626 * @return Error code
627 */
628int hc_init_transfer_lists(hc_t *instance)
629{
630 assert(instance);
631#define SETUP_ENDPOINT_LIST(type) \
632do { \
633 const char *name = usb_str_transfer_type(type); \
634 int ret = endpoint_list_init(&instance->lists[type], name); \
635 if (ret != EOK) { \
636 usb_log_error("Failed to setup %s endpoint list: %s.\n", \
637 name, str_error(ret)); \
638 endpoint_list_fini(&instance->lists[USB_TRANSFER_ISOCHRONOUS]);\
639 endpoint_list_fini(&instance->lists[USB_TRANSFER_INTERRUPT]); \
640 endpoint_list_fini(&instance->lists[USB_TRANSFER_CONTROL]); \
641 endpoint_list_fini(&instance->lists[USB_TRANSFER_BULK]); \
642 return ret; \
643 } \
644} while (0)
645
646 SETUP_ENDPOINT_LIST(USB_TRANSFER_ISOCHRONOUS);
647 SETUP_ENDPOINT_LIST(USB_TRANSFER_INTERRUPT);
648 SETUP_ENDPOINT_LIST(USB_TRANSFER_CONTROL);
649 SETUP_ENDPOINT_LIST(USB_TRANSFER_BULK);
650#undef SETUP_ENDPOINT_LIST
651 endpoint_list_set_next(&instance->lists[USB_TRANSFER_INTERRUPT],
652 &instance->lists[USB_TRANSFER_ISOCHRONOUS]);
653
654 return EOK;
655}
656
657/** Initialize memory structures used by the OHCI hcd.
658 *
659 * @param[in] instance OHCI hc driver structure.
660 * @return Error code.
661 */
662int hc_init_memory(hc_t *instance)
663{
664 assert(instance);
665
666 memset(&instance->rh, 0, sizeof(instance->rh));
667 /* Init queues */
668 const int ret = hc_init_transfer_lists(instance);
669 if (ret != EOK) {
670 return ret;
671 }
672
673 /*Init HCCA */
674 instance->hcca = hcca_get();
675 if (instance->hcca == NULL)
676 return ENOMEM;
677 usb_log_debug2("OHCI HCCA initialized at %p.\n", instance->hcca);
678
679 for (unsigned i = 0; i < HCCA_INT_EP_COUNT; ++i) {
680 hcca_set_int_ep(instance->hcca, i,
681 instance->lists[USB_TRANSFER_INTERRUPT].list_head_pa);
682 }
683 usb_log_debug2("Interrupt HEADs set to: %p (%#" PRIx32 ").\n",
684 instance->lists[USB_TRANSFER_INTERRUPT].list_head,
685 instance->lists[USB_TRANSFER_INTERRUPT].list_head_pa);
686
687 return EOK;
688}
689
690/**
691 * @}
692 */
Note: See TracBrowser for help on using the repository browser.