source: mainline/uspace/drv/bus/usb/ohci/hc.c@ 64e63ce1

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

Remove remaining CHECK_RETxxx in ohci driver.

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