1/*
2 * Copyright (c) 2007-2013 ETH Zurich.
3 * All rights reserved.
4 *
5 * This file is distributed under the terms in the attached LICENSE file.
6 * If you do not find this file, copies can be found by writing to:
7 * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
8 */
9
10#include <stdlib.h>
11#include <stdio.h>
12#include <barrelfish/barrelfish.h>
13
14#include "ohci_device.h"
15
16#include <usb/usb.h>
17
18#include <usb_xfer.h>
19#include <usb_device.h>
20#include <usb_controller.h>
21#include <usb_hub.h>
22
23#include <usb_memory.h>
24
25#include "usb_ohci.h"
26#include "usb_ohci_root_hub.h"
27#include "usb_ohci_bus.h"
28
29
30#include "usb_ohci_xfer.h"
31#include "usb_ohci_pipe.h"
32#include "usb_ohci_queue.h"
33
34
35
36/*
37 * ------------------------------------------------------------------------
38 * OHCI Bus Functions
39 * ------------------------------------------------------------------------
40 */
41
42/*
43 * \brief   this function polls the interrupt queue and checks for each element
44 *          if the transfer has been completed. If so the transfer is
45 *          removed from the list
46 *
47 * \param   hc  pointer to the host controller
48 */
49void usb_ohci_do_poll(struct usb_host_controller *hc)
50{
51    struct usb_xfer *xfer;
52
53    uint8_t repeat = 1;
54
55    while (repeat) {
56        repeat = 0;
57
58        for ((xfer) = (((&hc->intr_queue.head))->first); (xfer); (xfer) =
59                (((xfer))->wait_entry.next)) {
60            /*
61             * check if transfer is transferred
62             */
63            if (usb_ohci_xfer_is_finished(xfer)) {
64                /* queue has been modified */
65                repeat = 1;
66            }
67        }
68    }
69}
70
71/*
72 * \brief   this function initializes the endpoint with the correct
73 *          pipe functions
74 *
75 * \param   device  the device the endpoint belongs to
76 * \param   ep_desc the description of the endpoint
77 * \param   ep      the endpoint
78 */
79static void usb_ohci_ep_init(struct usb_device *device,
80        struct usb_endpoint_descriptor *ep_desc, struct usb_endpoint *ep)
81{
82    if (device->flags.usb_mode != USB_MODE_HOST) {
83        /* this usb device mode is not supported */
84        return;
85    }
86
87    usb_ohci_hc_t *hc = (usb_ohci_hc_t *) device->controller->hc_control;
88
89    /*
90     * we can only initialize endpoints for function devices
91     */
92    if (device->device_index != hc->root_hub_address) {
93        switch (ep_desc->bmAttributes.xfer_type) {
94            case USB_ENDPOINT_TYPE_CONTROL:
95                ep->pipe_fn = usb_ohci_get_ctrl_pipe_fn();
96                break;
97            case USB_ENDPOINT_TYPE_INTR:
98                ep->pipe_fn = usb_ohci_get_intr_pipe_fn();
99                break;
100            case USB_ENDPOINT_TYPE_ISOCHR:
101                if (device->speed == USB_SPEED_FULL) {
102                    ep->pipe_fn = usb_ohci_get_isoc_pipe_fn();
103                }
104                break;
105            case USB_ENDPOINT_TYPE_BULK:
106                ep->pipe_fn = usb_ohci_get_bulk_pipe_fn();
107                break;
108            default:
109                /* transfer type unkown, do nothing */
110                break;
111        }
112    }
113
114}
115
116/**
117 * \brief   this function resumes an USB device, this function takes
118 *          inserts the USB transfers to their respective transfer lists
119 *
120 * \param   device  the device to suspend
121 */
122static void usb_ohci_device_resume(struct usb_device *device)
123{
124    struct usb_xfer *xfer;
125    usb_ohci_hc_t *hc;
126    usb_ohci_ed_t *ed;
127
128    hc = (usb_ohci_hc_t *) (device->controller->hc_control);
129
130    for ((xfer) = (((&device->controller->intr_queue.head))->first); (xfer);
131            (xfer) = (((xfer))->wait_entry.next)) {
132        ed = xfer->hcd_qh_start[xfer->flags_internal.curr_dma_set];
133        switch (xfer->type) {
134            case USB_TYPE_BULK:
135                usb_ohci_append_qh(ed, hc->qh_bulk_last);
136                ohci_cmdstatus_blf_wrf(hc->ohci_base, 0x1);
137                break;
138            case USB_TYPE_CTRL:
139                usb_ohci_append_qh(ed, hc->qh_ctrl_last);
140                ohci_cmdstatus_clf_wrf(hc->ohci_base, 0x1);
141                break;
142
143            case USB_TYPE_INTR:
144                usb_ohci_append_qh(ed, hc->qh_intr_last[xfer->intr_qh_pos]);
145                break;
146            default:
147                /* noop */
148                break;
149        }
150    }
151
152}
153
154/**
155 * \brief   this function suspends an USB device. the outstanding transfers
156 *          for this device are descheduled and inserted into the wait list
157 *
158 * \param   device the device to resume
159 */
160static void usb_ohci_device_suspend(struct usb_device *device)
161{
162    struct usb_xfer *xfer;
163    usb_ohci_hc_t *hc;
164    usb_ohci_ed_t *ed;
165
166    hc = (usb_ohci_hc_t *) (device->controller->hc_control);
167
168    for ((xfer) = (((&device->controller->intr_queue.head))->first); (xfer);
169            (xfer) = (((xfer))->wait_entry.next)) {
170        /*
171         * only remove those transfers that belong to the given device
172         */
173        if (xfer->device == device) {
174            ed = xfer->hcd_qh_start[xfer->flags_internal.curr_dma_set];
175            switch (xfer->type) {
176                case USB_TYPE_BULK:
177                    usb_ohci_remove_qh(ed, hc->qh_bulk_last);
178                    break;
179                case USB_TYPE_CTRL:
180                    usb_ohci_remove_qh(ed, hc->qh_ctrl_last);
181                    break;
182
183                case USB_TYPE_INTR:
184                    usb_ohci_remove_qh(ed, hc->qh_intr_last[xfer->intr_qh_pos]);
185                    break;
186                default:
187                    /* noop */
188                    break;
189            }
190        }
191
192    }
193}
194
195static void usb_ohci_set_hw_power(struct usb_host_controller *controller)
196{
197    /*
198         * TODO: implement
199         */
200        assert(!"NYI: Power control not implemented");
201}
202
203static void usb_ohci_set_hw_power_sleep(struct usb_host_controller *controller, uint32_t state)
204{
205    /*
206     * TODO: implement
207     */
208    assert(!"NYI: Power control not implemented");
209}
210
211
212
213static struct usb_hcdi_bus_fn usb_ohci_bus_fn = {
214        .roothub_exec = usb_ohci_roothub_exec,
215        .endpoint_init = usb_ohci_ep_init,
216            .xfer_setup = usb_ohci_xfer_setup,
217            .xfer_unsetup = usb_ohci_xfer_unsetup,
218            .device_resume = usb_ohci_device_resume,
219            .device_suspend = usb_ohci_device_suspend,
220            .set_hw_power = usb_ohci_set_hw_power,
221            .set_hw_power_sleep = usb_ohci_set_hw_power_sleep,
222            .xfer_poll = usb_ohci_do_poll
223};
224
225struct usb_hcdi_bus_fn *usb_ohci_get_bus_fn(void)
226{
227    return &usb_ohci_bus_fn;
228}
229
230