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