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 <usb/usb.h>
15
16
17#include <usb_controller.h>
18#include <usb_xfer.h>
19
20#include "usb_ehci.h"
21#include "usb_ehci_xfer.h"
22#include "usb_ehci_queue.h"
23
24
25/**
26 * \brief this function enqueues a transfer on the interrupt queue
27 *        transfers on this queue are to be scheduled by the host controller
28 */
29void usb_ehci_enqueue_xfer_intrq(struct usb_xfer *xfer)
30{
31    USB_DEBUG_TR_ENTER;
32
33    /*
34     * check if the transfer is already finished
35     */
36    if (usb_ehci_xfer_is_finished(xfer)) {
37        USB_DEBUG("NOTICE: transfer already finished...\n");
38        return;
39    }
40
41    /*
42     * enqueue it on the transfer interrupt queue
43     */
44    usb_xfer_enqueue(&xfer->host_controller->intr_queue, xfer);
45
46    /*
47     * TODO: start timeout handler, if the transfer takes too long to be executed
48     *       then it should be removed with a timeout condition
49     *
50     * if (xfer->timeout != 0) {
51     *
52     * }
53     */
54}
55
56/**
57 * \brief enqueues a new split isochronus transaction descriptor into the queue
58 *
59 * \param sitd the siTD to insert into the list
60 * \param last the last element of the list
61 */
62usb_ehci_sitd_t *usb_ehci_enq_fs_td(usb_ehci_sitd_t *sitd,
63        usb_ehci_sitd_t *last)
64{
65    /*
66     * update the virtual links
67     */
68    sitd->next = last->next;
69    sitd->prev = last;
70    last->next = sitd;
71
72    /*
73     * update the physical links
74     */
75    sitd->sitd_next = last->sitd_next;
76    last->sitd_next = sitd->sitd_self;
77
78    return (sitd);
79}
80
81/**
82 * \brief removes the element from the list
83 *
84 * \param sitd the siTD element to be removed
85 * \param last the last element of the list
86 */
87usb_ehci_sitd_t *usb_ehci_deq_fs_td(usb_ehci_sitd_t *sitd,
88        usb_ehci_sitd_t *last)
89{
90    /*
91     * update virtual pointers
92     */
93    sitd->prev->next = sitd->next;
94
95    /*
96     * update the physical links
97     */
98    sitd->prev->sitd_next = sitd->sitd_next;
99
100    if (sitd->next) {
101        sitd->next->prev = sitd->prev;
102    }
103
104    return ((last == sitd) ? sitd->prev : last);
105}
106
107/*
108 * \brief enqueues a new isochronus transaction descriptor into the queue
109 *
110 * \param std   the iTD to insert into the list
111 * \param last  the last element of the list
112 */
113usb_ehci_itd_t *usb_ehci_enq_hs_td(usb_ehci_itd_t *std, usb_ehci_itd_t *last)
114{
115    /*
116     * update the virtual links
117     */
118    std->next = last->next;
119    std->prev = last;
120    last->next = std;
121
122    /*
123     * update the physical links
124     */
125    std->itd_next = last->itd_next;
126    last->itd_next = std->itd_self;
127
128    return (std);
129
130}
131
132/**
133 * \brief removes a high speed isochronus transfer descriptor from the list
134 *
135 * \param std   the iTD to be removed
136 * \param last  the last element of the list
137 */
138usb_ehci_itd_t *usb_ehci_deq_hs_td(usb_ehci_itd_t *std, usb_ehci_itd_t *last)
139{
140    /*
141     * update virtual pointers
142     */
143    std->prev->next = std->next;
144
145    /*
146     * update the physical links
147     */
148    std->prev->itd_next = std->itd_next;
149
150    if (std->next) {
151        std->next->prev = std->prev;
152    }
153
154    return ((last == std) ? std->prev : last);
155}
156
157/**
158 * \brief enqueues a queue head descriptor into the qh list
159 *
160 * \param qh    the queue head descriptor to insert
161 * \param last  the last element fo the list
162 */
163usb_ehci_qh_t *usb_ehci_enq_qh(usb_ehci_qh_t *qh, usb_ehci_qh_t *last)
164{
165    USB_DEBUG_TR_ENTER;
166
167    /*
168     * a queue head can only be linked once
169     */
170    if (qh->prev != NULL) {
171        USB_DEBUG("NOTICE: Tried to double enqueue a queue head\n");
172        return (last);
173    }
174
175    qh->next = last->next;
176    qh->qh_link = last->qh_link;
177
178    qh->prev = last;
179
180    last->next = qh;
181    last->qh_link = qh->qh_self | USB_EHCI_LINKTYPE_QH;
182
183    return (qh);
184}
185
186/**
187 * \brief removes a queue head from the qh list
188 *
189 * \param qh    the queue head to be removed
190 * \param last  the last element of the list
191 */
192usb_ehci_qh_t *usb_ehci_deq_qh(usb_ehci_qh_t *qh, usb_ehci_qh_t *last)
193{
194    /*
195     * check if the queue head is in fact on a list
196     */
197    if (qh->prev == NULL) {
198        return (last);
199    }
200
201    /*
202     * update virtual pointers
203     */
204    qh->prev->next = qh->next;
205
206    /*
207     * update physical pointer
208     */
209    qh->prev->qh_link = qh->qh_link;
210
211    if (qh->next) {
212        qh->next->prev = qh->prev;
213    }
214
215    last = ((last == qh) ? qh->prev : last);
216
217    qh->prev = NULL;
218
219    return (last);
220
221}
222