1/**
2 * \brief This file contains EHCI specific functions for the USB host controller
3 *        driver interface.
4 */
5
6/*
7 * Copyright (c) 2007-2013 ETH Zurich.
8 * All rights reserved.
9 *
10 * This file is distributed under the terms in the attached LICENSE file.
11 * If you do not find this file, copies can be found by writing to:
12 * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
13 */
14
15#include <stdlib.h>
16#include <stdio.h>
17#include <barrelfish/barrelfish.h>
18
19// Mackerel device
20#include "ehci_device.h"
21
22// common USB declarations
23#include <usb/usb.h>
24
25// USB manager specific includes
26#include <usb_xfer.h>
27#include <usb_device.h>
28#include <usb_controller.h>
29#include <usb_hub.h>
30#include <usb_memory.h>
31
32// host controller specific includes
33#include "usb_ehci.h"
34#include "usb_ehci_root_hub.h"
35#include "usb_ehci_bus.h"
36#include "usb_ehci_xfer.h"
37#include "usb_ehci_pipe.h"
38#include "usb_ehci_queue.h"
39
40/**
41 * \brief wrapper function to perform the polling of the USB transfers
42 *
43 * \param hostc pointer to the generic host controller of type ehci
44 *
45 * Note: this functions is needed to translate the generic poll request
46 *       into a host controller specific poll request.
47 */
48static void usb_ehci_do_poll(usb_host_controller_t *hostc)
49{
50    assert(hostc->hc_type == USB_EHCI);
51
52    usb_ehci_hc_t *hc = (usb_ehci_hc_t *) (hostc->hc_control);
53
54    usb_ehci_poll(hc);
55}
56
57/**
58 * \brief executes the polling of the usb transfers
59 *
60 * \param hc host ehci host controller to poll
61 *
62 * Note: this function iteratively polls all transfers currently added to the
63 *       interrupt queue of this host controller. If a transfer changes its
64 *       state to finished, the process is repeated
65 */
66void usb_ehci_poll(usb_ehci_hc_t *hc)
67{
68
69    USB_DEBUG_TR_ENTER;
70
71    struct usb_xfer *xfer = hc->controller->intr_queue.head.first;
72    uint8_t repeat = 0;
73
74    /* loop over all transferstill no changes */
75    do {
76        repeat = 0;
77        while (xfer != NULL) {
78            /* TODO: Check if needed */
79            if (xfer == ((xfer))->wait_entry.next) {
80                break;
81            }
82
83            if (usb_ehci_xfer_is_finished(xfer)) {
84                repeat = 1;  // repeat the polling process
85            }
86            xfer = xfer->wait_entry.next;
87        }
88    } while (repeat);
89
90    USB_DEBUG_TR_RETURN;
91}
92
93/**
94 * \brief stores function pointers to the host controller driver interface
95 */
96static struct usb_hcdi_bus_fn usb_ehci_bus_fn = {
97    .endpoint_init = usb_ehci_endpoint_init,
98    .xfer_setup = usb_ehci_xfer_setup,
99    .xfer_unsetup = usb_ehci_xfer_unsetup,
100    .device_resume = usb_ehci_device_resume,
101    .device_suspend = usb_ehci_device_suspend,
102    .set_hw_power = usb_ehci_set_power,
103    .set_hw_power_sleep = usb_ehci_sleep,
104    .roothub_exec = usb_ehci_roothub_exec,
105    .xfer_poll = usb_ehci_do_poll,
106    .xfer_finished = usb_ehci_xfer_is_finished,
107};
108
109/**
110 * \brief this function returns a pointer to the ehci hcdi functions
111 *
112 * \return pointer to struct of function pointers
113 */
114struct usb_hcdi_bus_fn *usb_ehci_get_bus_fn(void)
115{
116    return (&usb_ehci_bus_fn);
117}
118
119/**
120 * \brief sets the ehci controller into sleep mode or wakes it up
121 *
122 * \param hc    the host controller to sleep / wakeup
123 * \param state the state to put the host controller into
124 *
125 * Upon resuming all the connected devices are also resumed
126 */
127void usb_ehci_sleep(struct usb_host_controller *hc, uint32_t state)
128{
129    assert(hc->hc_type == USB_EHCI);
130
131    usb_ehci_hc_t *ehci_hc = hc->hc_control;
132
133    switch (state) {
134        case USB_POWER_MODE_SUSPEND:
135        case USB_POWER_MODE_OFF:
136            /* to suspend the hardware this is just a reset of the
137             * host controller
138             */
139            usb_ehci_hc_reset(ehci_hc);
140            break;
141        case USB_POWER_MODE_RESUME:
142            /* resume: first reset the host controller hardware */
143            usb_ehci_hc_reset(ehci_hc);
144
145            /* re-initialize the registers */
146            usb_ehci_initialize_controller(ehci_hc);
147
148            /* poll the transfers */
149            usb_ehci_do_poll(hc);
150            break;
151        default:
152            /* noop */
153            break;
154    }
155}
156
157/**
158 * \brief sets the hardware to a specific power mode
159 *
160 * \param hc the host controller to change power mode
161 *
162 * This functio updates the ehci_usbcmd register to its a new value, if there
163 * are periodic or asynchronous transfers happening, these are finished first
164 */
165void usb_ehci_set_power(struct usb_host_controller *hc)
166{
167    assert(!"NYI: set power");
168}
169
170/**
171 * \brief resumes a suspended USB device on this host controller
172 *
173 * \param device the usb devices to to resume
174 *
175 * All of the already set up usb xfers are also resumed
176 */
177void usb_ehci_device_resume(struct usb_device *device)
178{
179    assert(device->controller->hc_type == USB_EHCI);
180
181    struct usb_xfer_queue *queue = &device->controller->intr_queue;
182    struct usb_xfer *xfer = queue->head.first;
183
184    while (xfer) {
185        /* just handle the xfers that are belonging to this device */
186        if (xfer->device == device) {
187            usb_ehci_hc_t *hc = device->controller->hc_control;
188            switch (xfer->type) {
189                case USB_TYPE_BULK:
190                case USB_TYPE_CTRL:
191                    usb_ehci_enq_qh(
192                            xfer->hcd_qh_start[xfer->flags_internal.curr_dma_set],
193                            hc->qh_async_last);
194                    break;
195                case USB_TYPE_INTR:
196                    usb_ehci_enq_qh(
197                            xfer->hcd_qh_start[xfer->flags_internal.curr_dma_set],
198                            hc->qh_intr_last[xfer->intr_qh_pos]);
199                    break;
200                default:
201                    /* noop */
202                    break;
203            }
204        }
205        xfer = xfer->wait_entry.next;
206    }
207}
208
209/**
210 * \brief suspends a attached USB device by pausing all its transfers
211 *
212 * \param device the usb device to suspend
213 */
214void usb_ehci_device_suspend(struct usb_device *device)
215{
216    assert(device->controller->hc_type == USB_EHCI);
217
218    struct usb_xfer_queue *queue = &device->controller->intr_queue;
219    struct usb_xfer *xfer = queue->head.first;
220
221    while (xfer) {
222        if (xfer->device == device) {
223            usb_ehci_hc_t *hc = device->controller->hc_control;
224            switch (xfer->type) {
225                case USB_TYPE_BULK:
226                case USB_TYPE_CTRL:
227                    usb_ehci_deq_qh(
228                            xfer->hcd_qh_start[xfer->flags_internal.curr_dma_set],
229                            hc->qh_async_last);
230                    break;
231                case USB_TYPE_INTR:
232                    usb_ehci_deq_qh(
233                            xfer->hcd_qh_start[xfer->flags_internal.curr_dma_set],
234                            hc->qh_intr_last[xfer->intr_qh_pos]);
235                    break;
236                default:
237                    /* noop */
238                    break;
239            }
240
241        }
242        xfer = xfer->wait_entry.next;
243    }
244
245}
246
247
248/**
249 * \brief initializes a device endpoint with the values given by the descriptor
250 *
251 * \param device  the device, this endpoint belongs
252 * \param ep_desc the descriptor of this endpoing
253 * \param ep      the endpoint to initialize
254 */
255void usb_ehci_endpoint_init(struct usb_device *device,
256        struct usb_endpoint_descriptor *ep_desc, struct usb_endpoint *ep)
257{
258    assert(device->controller->hc_type == USB_EHCI);
259
260    usb_ehci_hc_t *hc = (usb_ehci_hc_t *) device->controller->hc_control;
261
262    /*
263     * CASE 1: The device is the root hub. Setting up endpoints for the root
264     *         hub is not allowed, thus return
265     */
266    if (device->device_index == hc->rh_device_address) {
267        return;
268    }
269
270    /*
271     * CASE 2: the attached device is not a high speed device and is directly
272     *         connected to the root hub, this is not possible, since non
273     *         full speed devices are handled by the companion controller.
274     *         (root hub does not have a transaction translator)
275     */
276    if (device->speed != USB_SPEED_HIGH) {
277        if ((device->hs_hub_address == 0) || (device->hs_hub_port_number == 0)
278                || (device->parent_hs_hub == NULL)
279                || (device->parent_hs_hub->hub == NULL)) {
280            return;
281        }
282    }
283
284    /* CASE 3: all fine setup the pipe functions depending on the EP type */
285    switch (ep_desc->bmAttributes.xfer_type) {
286        case USB_ENDPOINT_TYPE_CONTROL:
287            ep->pipe_fn = usb_ehci_get_ctrl_pipe_fn();
288            break;
289        case USB_ENDPOINT_TYPE_INTR:
290            ep->pipe_fn = usb_ehci_get_intr_pipe_fn();
291            break;
292        case USB_ENDPOINT_TYPE_ISOCHR:
293            /*
294             * isochronus endpoints need differentiated handling depending on
295             * the device speed
296             */
297            if (device->speed == USB_SPEED_HIGH) {
298                ep->pipe_fn = usb_ehci_get_hs_isoc_pipe_fn();
299            } else if (device->speed == USB_SPEED_FULL) {
300                ep->pipe_fn = usb_ehci_get_fs_isoc_pipe_fn();
301            }
302            break;
303        case USB_ENDPOINT_TYPE_BULK:
304            ep->pipe_fn = usb_ehci_get_bulk_pipe_fn();
305            break;
306        default:
307            /* no-op */
308            break;
309    }
310}
311
312