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 <string.h>
13#include <barrelfish/barrelfish.h>
14
15#include <usb/usb.h>
16
17#include <usb_memory.h>
18
19#include <usb_controller.h>
20#include "usb_ehci.h"
21#include "usb_ehci_memory.h"
22
23/// which version of data structures we have to use
24static usb_ds_size_t ds_size = USB_EHCI_DS_32BIT;
25
26/*
27 * the free lists
28 */
29static struct usb_ehci_itd *free_itd = NULL;
30static struct usb_ehci_sitd *free_sitd = NULL;
31static struct usb_ehci_qtd *free_qtd = NULL;
32static struct usb_ehci_qh *free_qh = NULL;
33
34static struct usb_page *used_pages = NULL;
35static struct usb_page *free_pages = NULL;
36
37/**
38 * \brief this function sets the used ehci data structure size
39 *        to 32 bit or 64 bit
40 */
41void usb_ehci_set_datastruct_size(usb_ds_size_t size)
42{
43    switch (size) {
44        case USB_EHCI_DS_32BIT:
45            ds_size = size;
46            break;
47        case USB_EHCI_DS_64BIT:
48            //ds_size = size;
49            assert( !"NYI: Support of 64 bit data structures "
50            "not yet supported\n");
51            break;
52        default:
53            debug_printf("ERROR: Unknown data structure size\n");
54            break;
55    }
56}
57
58/*
59 * \brief   prints out the sizes of the used data structures
60 */
61void usb_ehci_print_datastruct_sizes(void)
62{
63    printf("sizeof(struct usb_ehci_itd) = %zu\n", sizeof(struct usb_ehci_itd));
64    printf("sizeof(struct usb_ehci_sitd) = %zu\n", sizeof(struct usb_ehci_sitd));
65    printf("sizeof(struct usb_ehci_qh) = %zu\n", sizeof(struct usb_ehci_qh));
66    printf("sizeof(struct usb_ehci_qtd) = %zu\n", sizeof(struct usb_ehci_qtd));
67    printf("sizeof(struct usb_ehci_fstn) = %zu\n", sizeof(struct usb_ehci_fstn));
68}
69
70/*
71 * \brief allocates an empty queue head descriptors without buffer pages
72 *
73 * \return pointer to a correctly aligned queue head pointer
74 */
75struct usb_ehci_qh *usb_ehci_qh_alloc(void)
76{
77    usb_ehci_qh_t *qh;
78
79    /*
80     * we have a free queue head left, so we can use it
81     */
82    if (free_qh != NULL) {
83        qh = free_qh;
84        free_qh = qh->obj_next;
85        return (qh);
86    }
87
88    struct usb_page *page;
89
90    /* get a free page or allocate a new one*/
91    if (free_pages == NULL) {
92        page = usb_mem_page_alloc();
93    } else {
94        page = free_pages;
95        free_pages = page->next;
96    }
97
98    uint32_t size_qh = sizeof(struct usb_ehci_qh);
99    uint32_t num_qh = page->free.size / size_qh;
100
101    /* initiate the variables with the start addresses */
102    qh = page->free.buffer;
103    usb_paddr_t qh_self = page->free.phys_addr;
104
105    for (uint32_t i = 0; i < num_qh; i++) {
106        assert(!(qh_self % USB_EHCI_QH_ALIGN));
107        USB_DEBUG_MEM(" Allocating QH: vaddr=%p, phys=%p (%x) [%x] (%u of %u)\n", qh,
108                qh_self, qh_self % USB_EHCI_QH_ALIGN, size_qh, i, num_qh);
109        /* reset memory */
110        memset(qh, 0, size_qh);
111
112        /* set the physical address */
113        qh->qh_self = qh_self;
114
115        /* put it into the free list */
116        qh->obj_next = free_qh;
117        free_qh = qh;
118
119        qh++;
120        qh_self += size_qh;
121    }
122
123    /* update free information */
124    page->free.size -= (num_qh * size_qh);
125    page->free.buffer += (num_qh * size_qh);
126    page->free.phys_addr += (num_qh * size_qh);
127
128    /* put page into free list */
129    page->next = used_pages;
130    used_pages = page;
131
132    /* return a fresh qh */
133    qh = free_qh;
134    free_qh = qh->obj_next;
135
136    return (qh);
137}
138
139/**
140 * \brief   frees up a queue head descriptor but keeps the associated
141 *          buffer pages
142 *
143 * \param   qh  the queue head to be freed
144 */
145void usb_ehci_qh_free(struct usb_ehci_qh *qh)
146{
147    /*
148     * clear out all physical fields of this queue head
149     * and set the link address to terminated
150     */
151    memset(qh, 0, 0x1B);
152    qh->qh_link |= USB_EHCI_LINK_TERMINATE;
153    qh->qh_alt_next_qtd |= USB_EHCI_LINK_TERMINATE;
154    qh->qh_next_qtd |= USB_EHCI_LINK_TERMINATE;
155
156    qh->bp_list[0].bp &= 0xFFFFF000;
157    qh->bp_list[1].bp &= 0xFFFFF000;
158    qh->bp_list[2].bp &= 0xFFFFF000;
159    qh->bp_list[3].bp &= 0xFFFFF000;
160    qh->bp_list[4].bp &= 0xFFFFF000;
161
162    /*
163     * clear out virtual pointers and add it to the free list
164     */
165    qh->next = NULL;
166    qh->prev = NULL;
167    qh->obj_next = free_qh;
168    free_qh = qh;
169}
170
171struct usb_ehci_qtd *usb_ehci_qtd_alloc(void)
172{
173    usb_ehci_qtd_t *qtd;
174
175    /*
176     * we have a free queue transfer descriptor left, so we can use it
177     */
178    if (free_qtd != NULL) {
179        qtd = free_qtd;
180        free_qtd = qtd->obj_next;
181        return (qtd);
182    }
183
184    /*
185     * we have to populate our free queue element transfer descriptor
186     */
187    struct usb_page *page;
188
189    if (free_pages == NULL) {
190        page = usb_mem_page_alloc();
191    } else {
192        page = free_pages;
193        free_pages = page->next;
194    }
195
196    uint32_t size_qtd = sizeof(struct usb_ehci_qtd);
197    uint32_t num_qtd = page->free.size / size_qtd;
198
199    qtd = page->free.buffer;
200    usb_paddr_t qtd_self = page->free.phys_addr;
201
202    for (uint32_t i = 0; i < num_qtd; i++) {
203        assert(!(qtd_self % USB_EHCI_QTD_ALIGN));
204        USB_DEBUG_MEM(" Allocating QTD: vaddr=%p, phys=%p (%x) [%x]\n", qtd,
205                qtd_self, qtd_self % USB_EHCI_QTD_ALIGN, size_qtd);
206        memset(qtd, 0, size_qtd);
207        qtd->qtd_self = qtd_self;
208        qtd->obj_next = free_qtd;
209        free_qtd = qtd;
210
211        qtd++;
212        qtd_self += size_qtd;
213    }
214
215    page->free.size -= (num_qtd * size_qtd);
216    page->free.buffer += (num_qtd * size_qtd);
217    page->free.phys_addr += (num_qtd * size_qtd);
218
219    page->next = used_pages;
220    used_pages = page;
221
222    qtd = free_qtd;
223    free_qtd = qtd->obj_next;
224
225    return (qtd);
226
227}
228
229void usb_ehci_qtd_free(struct usb_ehci_qtd *qtd)
230{
231    /*
232     * clear out all physical fields except the buffer pointers
233     */
234    memset(qtd, 0, 0xB);
235    qtd->qtd_alt_next |= USB_EHCI_LINK_TERMINATE;
236    qtd->qtd_next |= USB_EHCI_LINK_TERMINATE;
237    qtd->qtd_bp[0].address &= 0xFFFFF000;
238    qtd->qtd_bp[1].address &= 0xFFFFF000;
239    qtd->qtd_bp[2].address &= 0xFFFFF000;
240    qtd->qtd_bp[3].address &= 0xFFFFF000;
241    qtd->qtd_bp[4].address &= 0xFFFFF000;
242
243    /*
244     * clear out all virtual information
245     */
246    qtd->len = 0;
247    qtd->alt_next = NULL;
248
249    qtd->obj_next = free_qtd;
250    free_qtd = qtd;
251}
252
253struct usb_ehci_sitd *usb_ehci_sitd_alloc(void)
254{
255    usb_ehci_sitd_t *sitd;
256
257    /*
258     * we have a free queue transfer descriptor left, so we can use it
259     */
260    if (free_sitd != NULL) {
261        sitd = free_sitd;
262        free_sitd = sitd->obj_next;
263        return (sitd);
264    }
265
266    /*
267     * we have to populate our free queue element transfer descriptor
268     */
269    struct usb_page *page;
270
271    if (free_pages == NULL) {
272        page = usb_mem_page_alloc();
273    } else {
274        page = free_pages;
275        free_pages = page->next;
276    }
277
278    uint32_t size_sitd = sizeof(struct usb_ehci_sitd);
279    uint32_t num_sitd = page->free.size / size_sitd;
280
281    sitd = page->free.buffer;
282    usb_paddr_t sitd_self = page->free.phys_addr;
283
284    for (uint32_t i = 0; i < num_sitd; i++) {
285        assert(!(sitd_self % USB_EHCI_SITD_ALIGN));
286        USB_DEBUG_MEM(" Allocating SITD: vaddr=%p, phys=%p (%x) [%x]\n", sitd,
287                sitd_self, sitd_self % USB_EHCI_SITD_ALIGN, size_sitd);
288        memset(sitd, 0, size_sitd);
289        sitd->sitd_self = sitd_self;
290        sitd->obj_next = free_sitd;
291        free_sitd = sitd;
292
293        sitd++;
294        sitd_self += size_sitd;
295    }
296
297    page->free.size -= (num_sitd * size_sitd);
298    page->free.buffer += (num_sitd * size_sitd);
299    page->free.phys_addr += (num_sitd * size_sitd);
300
301    page->next = used_pages;
302    used_pages = page;
303
304    sitd = free_sitd;
305    free_sitd = sitd->obj_next;
306
307    return (sitd);
308}
309void usb_ehci_sitd_free(struct usb_ehci_sitd *sitd)
310{
311    memset(sitd, 0, 0xF);
312    sitd->sitd_next |= USB_EHCI_LINK_TERMINATE;
313    sitd->sitd_bp[0].address &= 0xFFFFF000;
314    sitd->sitd_bp[0].address &= 0xFFFFF000;
315    sitd->sitd_back_link |= USB_EHCI_LINK_TERMINATE;
316
317    sitd->prev = NULL;
318    sitd->next = NULL;
319
320    sitd->obj_next = free_sitd;
321    free_sitd = sitd;
322}
323
324struct usb_ehci_itd *usb_ehci_itd_alloc(void)
325{
326    usb_ehci_itd_t *itd;
327
328    /*
329     * we have a free queue transfer descriptor left, so we can use it
330     */
331    if (free_itd != NULL) {
332        itd = free_itd;
333        free_itd = itd->obj_next;
334        return (itd);
335    }
336
337    /*
338     * we have to populate our free queue element transfer descriptor
339     */
340    struct usb_page *page;
341
342    if (free_pages == NULL) {
343        page = usb_mem_page_alloc();
344    } else {
345        page = free_pages;
346        free_pages = page->next;
347    }
348
349    uint32_t size_itd = sizeof(struct usb_ehci_itd);
350    uint32_t num_itd = page->free.size / size_itd;
351
352    itd = page->free.buffer;
353    usb_paddr_t itd_self = page->free.phys_addr;
354
355    for (uint32_t i = 0; i < num_itd; i++) {
356        USB_DEBUG_MEM(" Allocating ITD: vaddr=%p, phys=%p (%x) [%x]\n", itd,
357                itd_self, itd_self % USB_EHCI_ITD_ALIGN, size_itd);
358        assert(!(itd_self % USB_EHCI_ITD_ALIGN));
359        memset(itd, 0, size_itd);
360        itd->itd_self = itd_self;
361        itd->obj_next = free_itd;
362        free_itd = itd;
363
364        itd++;
365        itd_self += size_itd;
366    }
367
368    page->free.size -= (num_itd * size_itd);
369    page->free.buffer += (num_itd * size_itd);
370    page->free.phys_addr += (num_itd * size_itd);
371
372    page->next = used_pages;
373    used_pages = page;
374
375    itd = free_itd;
376    free_itd = itd->obj_next;
377
378    return (itd);
379}
380
381void usb_ehci_itd_free(struct usb_ehci_itd *itd)
382{
383    memset(itd, 0, 0x23);
384    itd->itd_next |= USB_EHCI_LINK_TERMINATE;
385    itd->itd_bp[0].address &= 0xFFFFF000;
386    itd->itd_bp[1].address &= 0xFFFFF000;
387    itd->itd_bp[2].address &= 0xFFFFF000;
388    itd->itd_bp[3].address &= 0xFFFFF000;
389    itd->itd_bp[4].address &= 0xFFFFF000;
390    itd->itd_bp[5].address &= 0xFFFFF000;
391    itd->itd_bp[6].address &= 0xFFFFF000;
392
393    itd->next = NULL;
394    itd->prev = NULL;
395
396    itd->obj_next = free_itd;
397    free_itd = itd;
398}
399
400usb_paddr_t usb_ehci_buffer_page_alloc(void)
401{
402    struct usb_page *page;
403    if (free_pages != NULL) {
404        /* get the free apge */
405        page = free_pages;
406        free_pages = page->next;
407
408        /* put it into the used page */
409        page->next = used_pages;
410        used_pages = page;
411
412        /* return address */
413        return (page->page.phys_addr);
414    }
415
416    page = usb_mem_page_alloc();
417
418    // page has to be 4k aligned
419    assert(!(page->page.phys_addr & 0xFFF));
420
421    page->next = used_pages;
422    used_pages = page;
423
424    return (page->page.phys_addr);
425}
426
427void usb_ehci_buffer_page_free(usb_paddr_t buf)
428{
429    struct usb_page *page = used_pages;
430    struct usb_page *prev = page;
431    while (page->next != NULL) {
432        if (page->page.phys_addr == buf) {
433            break;
434        }
435        prev = page;
436        page = page->next;
437    }
438
439    if (page->page.phys_addr != buf) {
440        /* page not found */
441        debug_printf("WARNING: freeing up a page that was not allocated");
442        return;
443    }
444
445    if (prev == page) {
446        used_pages = page->next;
447    } else {
448        prev->next = page->next;
449    }
450
451    page->next = free_pages;
452    free_pages = page;
453}
454
455void usb_ehci_pframes_alloc(usb_ehci_hc_t *hc)
456{
457    struct usb_page *page;
458
459    page = usb_mem_page_alloc();
460
461    assert(!(page->page.phys_addr % USB_EHCI_FRAMELIST_ALIGN));
462
463    hc->pframes = page->page.buffer;
464    hc->pframes_phys = page->page.phys_addr;
465
466    page->next = used_pages;
467    used_pages = page;
468}
469
470