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_memory.h>
16
17static struct usb_page *free_pages = NULL;
18
19static struct usb_dma_page *free_dma_buffers = NULL;
20
21/**
22 * \brief   allocates a chunk of memory from a given usb_page with the given
23 *          size and alignment constraints.
24 *
25 * \param size      the minimum size of the block
26 * \param align     the alignment requirement of the block (physical memory)
27 * \param page      the given usb_page to allocate from
28 * \param ret_mem   the filled usb_mem_block structure with all the information
29 *
30 * \return  size of the requested block
31 */uint32_t usb_mem_next_block(uint32_t size, uint32_t align,
32        struct usb_page *page, struct usb_memory_block *ret_mem)
33{
34    // check if there is enough free space on this usb page
35    struct usb_memory_block *free_pg = &page->free;
36
37    uint32_t size_req = size;
38
39    uint32_t offset = free_pg->phys_addr % align;
40
41    // calculate the required size
42    if (offset) {
43        size_req += (align - offset);
44    }
45
46    // check if we have enough free space, otherwise return
47    if (free_pg->size < size_req) {
48        ret_mem->buffer = 0;
49        ret_mem->phys_addr = 0;
50        ret_mem->size = 0;
51        return 0;
52    }
53
54    ret_mem->buffer = free_pg->buffer + offset;
55    ret_mem->phys_addr = free_pg->phys_addr + offset;
56    ret_mem->size = size;
57
58    // update free memory in page
59    free_pg->buffer += size_req;
60    free_pg->phys_addr += size_req;
61    free_pg->size -= size_req;
62
63    assert(free_pg->size >= 0);
64
65    return size;
66}
67
68/*
69 * \brief   allocates a fresh usb_page for hardware descriptors of size 4k
70 *
71 * \return  pointer to struct usb_page or NULL
72 */
73struct usb_page *usb_mem_page_alloc(void)
74{
75    struct usb_page *ret;
76
77    // check if we have a free page left
78    if (free_pages != NULL) {
79        ret = free_pages;
80        free_pages = free_pages->next;
81        ret->next = NULL;
82        return (ret);
83    }
84
85    ret = (struct usb_page *) malloc(sizeof(struct usb_page));
86    memset(ret, 0, sizeof(struct usb_page));
87
88    errval_t err = frame_alloc(&ret->cap, USB_PAGE_SIZE, NULL);
89
90    if (err) {
91        return NULL;
92    }
93
94    err = invoke_frame_identify(ret->cap, &ret->frame_id);
95
96    if (err) {
97        return NULL;
98    }
99
100    err = vspace_map_one_frame_attr(&ret->page.buffer, USB_PAGE_SIZE, ret->cap,
101            VREGION_FLAGS_READ_WRITE_NOCACHE, NULL, NULL);
102
103    if (err) {
104        return NULL;
105    }
106    ret->page.phys_addr = ret->frame_id.base;
107    ret->page.size = USB_PAGE_SIZE;
108    ret->free.size = USB_PAGE_SIZE;
109    ret->free.phys_addr = ret->page.phys_addr;
110    ret->free.buffer = ret->page.buffer;
111
112    return ret;
113}
114
115
116/**
117 * \brief frees an USB page for later reuse
118 */
119void usb_mem_page_free(struct usb_page *mem)
120{
121    if (free_pages != NULL) {
122        mem->next = free_pages;
123    } else {
124        mem->next = NULL;
125    }
126    free_pages = mem;
127}
128
129/**
130 * \brief allocates a new memory page for DMA access
131 */
132struct usb_dma_page *usb_mem_dma_alloc(uint32_t size, uint32_t align)
133{
134    struct usb_dma_page *ret, *prev;
135
136    /* round up */
137    size = size + (USB_PAGE_SIZE - (size & 0xFFF));
138
139    ret = free_dma_buffers;
140    prev = NULL;
141
142    // check if we have a free page left
143    while (ret) {
144        if (ret->size >= size) {
145            if (prev) {
146                prev->next = ret->next;
147
148            } else {
149                free_dma_buffers = ret->next;
150                ret->next = NULL;
151
152            }
153            return (ret);
154        }
155        prev = ret;
156        ret = ret->next;
157    }
158
159    ret = (struct usb_dma_page *) malloc(sizeof(struct usb_dma_page));
160    memset(ret, 0, sizeof(struct usb_dma_page));
161
162    errval_t err = frame_alloc(&ret->cap, size, NULL);
163
164    if (err) {
165        return NULL;
166    }
167
168    err = invoke_frame_identify(ret->cap, &ret->frame_id);
169
170    if (err) {
171        return NULL;
172    }
173
174    err = vspace_map_one_frame_attr(&ret->buffer, size, ret->cap,
175            VREGION_FLAGS_READ_WRITE_NOCACHE, NULL, NULL);
176
177    if (err) {
178        return NULL;
179    }
180    ret->phys_addr = ret->frame_id.base;
181    ret->size = size;
182
183    return (ret);
184}
185
186/**
187 * \brief frees up an usb dma page for later reuse
188 */
189void usb_mem_dma_free(struct usb_dma_page *page)
190{
191    if (free_dma_buffers != NULL) {
192        page->next = free_dma_buffers;
193    } else {
194        page->next = NULL;
195    }
196    free_dma_buffers = page;
197}
198
199
200/**
201 * \brief copies data into the DMA memory
202 */
203void usb_mem_copy_in(struct usb_dma_page *pg, uint32_t offset, const void *data,
204        uint32_t length)
205{
206
207    if (pg == NULL || data == NULL) {
208        debug_printf("WARNING: wrong data structures\n");
209        return;
210    }
211
212    if ((offset + length) > pg->size) {
213        debug_printf("WARNING: offset+length > pg->size\n");
214        return;
215    }
216
217    memcpy(pg->buffer + offset, data, length);
218
219    return;
220}
221
222/**
223 * \brief copies data from the DMA memory region out to a buffer.
224 */
225void usb_mem_copy_out(struct usb_dma_page *pg, uint32_t offset, void *data,
226        uint32_t length)
227{
228    if (pg == NULL || data == NULL) {
229        return;
230    }
231    if ((offset + length) > pg->size) {
232        return;
233    }
234    memcpy(data, (pg->buffer) + offset, length);
235}
236
237