102ac6454SAndrew Thompson /* $FreeBSD$ */ 202ac6454SAndrew Thompson /*- 302ac6454SAndrew Thompson * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. 402ac6454SAndrew Thompson * 502ac6454SAndrew Thompson * Redistribution and use in source and binary forms, with or without 602ac6454SAndrew Thompson * modification, are permitted provided that the following conditions 702ac6454SAndrew Thompson * are met: 802ac6454SAndrew Thompson * 1. Redistributions of source code must retain the above copyright 902ac6454SAndrew Thompson * notice, this list of conditions and the following disclaimer. 1002ac6454SAndrew Thompson * 2. Redistributions in binary form must reproduce the above copyright 1102ac6454SAndrew Thompson * notice, this list of conditions and the following disclaimer in the 1202ac6454SAndrew Thompson * documentation and/or other materials provided with the distribution. 1302ac6454SAndrew Thompson * 1402ac6454SAndrew Thompson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1502ac6454SAndrew Thompson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1602ac6454SAndrew Thompson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1702ac6454SAndrew Thompson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1802ac6454SAndrew Thompson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1902ac6454SAndrew Thompson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2002ac6454SAndrew Thompson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2102ac6454SAndrew Thompson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2202ac6454SAndrew Thompson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2302ac6454SAndrew Thompson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2402ac6454SAndrew Thompson * SUCH DAMAGE. 2502ac6454SAndrew Thompson */ 2602ac6454SAndrew Thompson 2702ac6454SAndrew Thompson #include <dev/usb/usb_mfunc.h> 2802ac6454SAndrew Thompson #include <dev/usb/usb_error.h> 2902ac6454SAndrew Thompson #include <dev/usb/usb.h> 3002ac6454SAndrew Thompson 31a593f6b8SAndrew Thompson #define USB_DEBUG_VAR usb_debug 3202ac6454SAndrew Thompson 3302ac6454SAndrew Thompson #include <dev/usb/usb_core.h> 3402ac6454SAndrew Thompson #include <dev/usb/usb_busdma.h> 3502ac6454SAndrew Thompson #include <dev/usb/usb_process.h> 3602ac6454SAndrew Thompson #include <dev/usb/usb_transfer.h> 3702ac6454SAndrew Thompson #include <dev/usb/usb_device.h> 3802ac6454SAndrew Thompson #include <dev/usb/usb_util.h> 3902ac6454SAndrew Thompson #include <dev/usb/usb_debug.h> 4002ac6454SAndrew Thompson 4102ac6454SAndrew Thompson #include <dev/usb/usb_controller.h> 4202ac6454SAndrew Thompson #include <dev/usb/usb_bus.h> 4302ac6454SAndrew Thompson 44bdc081c6SAndrew Thompson #if USB_HAVE_BUSDMA 45a593f6b8SAndrew Thompson static void usb_dma_tag_create(struct usb_dma_tag *, usb_size_t, usb_size_t); 46a593f6b8SAndrew Thompson static void usb_dma_tag_destroy(struct usb_dma_tag *); 47a593f6b8SAndrew Thompson static void usb_dma_lock_cb(void *, bus_dma_lock_op_t); 48a593f6b8SAndrew Thompson static void usb_pc_alloc_mem_cb(void *, bus_dma_segment_t *, int, int); 49a593f6b8SAndrew Thompson static void usb_pc_load_mem_cb(void *, bus_dma_segment_t *, int, int); 50a593f6b8SAndrew Thompson static void usb_pc_common_mem_cb(void *, bus_dma_segment_t *, int, int, 5102ac6454SAndrew Thompson uint8_t); 5202ac6454SAndrew Thompson #endif 5302ac6454SAndrew Thompson 5402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 55a593f6b8SAndrew Thompson * usbd_get_page - lookup DMA-able memory for the given offset 5602ac6454SAndrew Thompson * 5702ac6454SAndrew Thompson * NOTE: Only call this function when the "page_cache" structure has 5802ac6454SAndrew Thompson * been properly initialized ! 5902ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 6002ac6454SAndrew Thompson void 61a593f6b8SAndrew Thompson usbd_get_page(struct usb_page_cache *pc, usb_frlength_t offset, 62760bc48eSAndrew Thompson struct usb_page_search *res) 6302ac6454SAndrew Thompson { 64760bc48eSAndrew Thompson struct usb_page *page; 6502ac6454SAndrew Thompson 66bdc081c6SAndrew Thompson #if USB_HAVE_BUSDMA 6702ac6454SAndrew Thompson if (pc->page_start) { 6802ac6454SAndrew Thompson 6902ac6454SAndrew Thompson /* Case 1 - something has been loaded into DMA */ 7002ac6454SAndrew Thompson 7102ac6454SAndrew Thompson if (pc->buffer) { 7202ac6454SAndrew Thompson 7302ac6454SAndrew Thompson /* Case 1a - Kernel Virtual Address */ 7402ac6454SAndrew Thompson 7502ac6454SAndrew Thompson res->buffer = USB_ADD_BYTES(pc->buffer, offset); 7602ac6454SAndrew Thompson } 7702ac6454SAndrew Thompson offset += pc->page_offset_buf; 7802ac6454SAndrew Thompson 7902ac6454SAndrew Thompson /* compute destination page */ 8002ac6454SAndrew Thompson 8102ac6454SAndrew Thompson page = pc->page_start; 8202ac6454SAndrew Thompson 8302ac6454SAndrew Thompson if (pc->ismultiseg) { 8402ac6454SAndrew Thompson 8502ac6454SAndrew Thompson page += (offset / USB_PAGE_SIZE); 8602ac6454SAndrew Thompson 8702ac6454SAndrew Thompson offset %= USB_PAGE_SIZE; 8802ac6454SAndrew Thompson 8902ac6454SAndrew Thompson res->length = USB_PAGE_SIZE - offset; 9002ac6454SAndrew Thompson res->physaddr = page->physaddr + offset; 9102ac6454SAndrew Thompson } else { 9202ac6454SAndrew Thompson res->length = 0 - 1; 9302ac6454SAndrew Thompson res->physaddr = page->physaddr + offset; 9402ac6454SAndrew Thompson } 9502ac6454SAndrew Thompson if (!pc->buffer) { 9602ac6454SAndrew Thompson 9702ac6454SAndrew Thompson /* Case 1b - Non Kernel Virtual Address */ 9802ac6454SAndrew Thompson 9902ac6454SAndrew Thompson res->buffer = USB_ADD_BYTES(page->buffer, offset); 10002ac6454SAndrew Thompson } 101bdc081c6SAndrew Thompson return; 102bdc081c6SAndrew Thompson } 103bdc081c6SAndrew Thompson #endif 10402ac6454SAndrew Thompson /* Case 2 - Plain PIO */ 10502ac6454SAndrew Thompson 10602ac6454SAndrew Thompson res->buffer = USB_ADD_BYTES(pc->buffer, offset); 10702ac6454SAndrew Thompson res->length = 0 - 1; 108bdc081c6SAndrew Thompson #if USB_HAVE_BUSDMA 10902ac6454SAndrew Thompson res->physaddr = 0; 110bdc081c6SAndrew Thompson #endif 11102ac6454SAndrew Thompson } 11202ac6454SAndrew Thompson 11302ac6454SAndrew Thompson /*------------------------------------------------------------------------* 114a593f6b8SAndrew Thompson * usbd_copy_in - copy directly to DMA-able memory 11502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 11602ac6454SAndrew Thompson void 117a593f6b8SAndrew Thompson usbd_copy_in(struct usb_page_cache *cache, usb_frlength_t offset, 118e0a69b51SAndrew Thompson const void *ptr, usb_frlength_t len) 11902ac6454SAndrew Thompson { 120760bc48eSAndrew Thompson struct usb_page_search buf_res; 12102ac6454SAndrew Thompson 12202ac6454SAndrew Thompson while (len != 0) { 12302ac6454SAndrew Thompson 124a593f6b8SAndrew Thompson usbd_get_page(cache, offset, &buf_res); 12502ac6454SAndrew Thompson 12602ac6454SAndrew Thompson if (buf_res.length > len) { 12702ac6454SAndrew Thompson buf_res.length = len; 12802ac6454SAndrew Thompson } 12902ac6454SAndrew Thompson bcopy(ptr, buf_res.buffer, buf_res.length); 13002ac6454SAndrew Thompson 13102ac6454SAndrew Thompson offset += buf_res.length; 13202ac6454SAndrew Thompson len -= buf_res.length; 13302ac6454SAndrew Thompson ptr = USB_ADD_BYTES(ptr, buf_res.length); 13402ac6454SAndrew Thompson } 13502ac6454SAndrew Thompson } 13602ac6454SAndrew Thompson 13702ac6454SAndrew Thompson /*------------------------------------------------------------------------* 138a593f6b8SAndrew Thompson * usbd_copy_in_user - copy directly to DMA-able memory from userland 13902ac6454SAndrew Thompson * 14002ac6454SAndrew Thompson * Return values: 14102ac6454SAndrew Thompson * 0: Success 14202ac6454SAndrew Thompson * Else: Failure 14302ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 144bdc081c6SAndrew Thompson #if USB_HAVE_USER_IO 14502ac6454SAndrew Thompson int 146a593f6b8SAndrew Thompson usbd_copy_in_user(struct usb_page_cache *cache, usb_frlength_t offset, 147e0a69b51SAndrew Thompson const void *ptr, usb_frlength_t len) 14802ac6454SAndrew Thompson { 149760bc48eSAndrew Thompson struct usb_page_search buf_res; 15002ac6454SAndrew Thompson int error; 15102ac6454SAndrew Thompson 15202ac6454SAndrew Thompson while (len != 0) { 15302ac6454SAndrew Thompson 154a593f6b8SAndrew Thompson usbd_get_page(cache, offset, &buf_res); 15502ac6454SAndrew Thompson 15602ac6454SAndrew Thompson if (buf_res.length > len) { 15702ac6454SAndrew Thompson buf_res.length = len; 15802ac6454SAndrew Thompson } 15902ac6454SAndrew Thompson error = copyin(ptr, buf_res.buffer, buf_res.length); 16002ac6454SAndrew Thompson if (error) 16102ac6454SAndrew Thompson return (error); 16202ac6454SAndrew Thompson 16302ac6454SAndrew Thompson offset += buf_res.length; 16402ac6454SAndrew Thompson len -= buf_res.length; 16502ac6454SAndrew Thompson ptr = USB_ADD_BYTES(ptr, buf_res.length); 16602ac6454SAndrew Thompson } 16702ac6454SAndrew Thompson return (0); /* success */ 16802ac6454SAndrew Thompson } 169bdc081c6SAndrew Thompson #endif 17002ac6454SAndrew Thompson 17102ac6454SAndrew Thompson /*------------------------------------------------------------------------* 172a593f6b8SAndrew Thompson * usbd_m_copy_in - copy a mbuf chain directly into DMA-able memory 17302ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 174bdc081c6SAndrew Thompson #if USB_HAVE_MBUF 175a593f6b8SAndrew Thompson struct usb_m_copy_in_arg { 176760bc48eSAndrew Thompson struct usb_page_cache *cache; 177e0a69b51SAndrew Thompson usb_frlength_t dst_offset; 17802ac6454SAndrew Thompson }; 17902ac6454SAndrew Thompson 180578d0effSAndrew Thompson static int 181a593f6b8SAndrew Thompson usbd_m_copy_in_cb(void *arg, void *src, uint32_t count) 18202ac6454SAndrew Thompson { 183a593f6b8SAndrew Thompson register struct usb_m_copy_in_arg *ua = arg; 18402ac6454SAndrew Thompson 185a593f6b8SAndrew Thompson usbd_copy_in(ua->cache, ua->dst_offset, src, count); 18602ac6454SAndrew Thompson ua->dst_offset += count; 18702ac6454SAndrew Thompson return (0); 18802ac6454SAndrew Thompson } 18902ac6454SAndrew Thompson 19002ac6454SAndrew Thompson void 191a593f6b8SAndrew Thompson usbd_m_copy_in(struct usb_page_cache *cache, usb_frlength_t dst_offset, 192f9cb546cSAndrew Thompson struct mbuf *m, usb_size_t src_offset, usb_frlength_t src_len) 19302ac6454SAndrew Thompson { 194a593f6b8SAndrew Thompson struct usb_m_copy_in_arg arg = {cache, dst_offset}; 195578d0effSAndrew Thompson int error; 19602ac6454SAndrew Thompson 197a593f6b8SAndrew Thompson error = m_apply(m, src_offset, src_len, &usbd_m_copy_in_cb, &arg); 19802ac6454SAndrew Thompson } 199bdc081c6SAndrew Thompson #endif 20002ac6454SAndrew Thompson 20102ac6454SAndrew Thompson /*------------------------------------------------------------------------* 202a593f6b8SAndrew Thompson * usb_uiomove - factored out code 20302ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 204bdc081c6SAndrew Thompson #if USB_HAVE_USER_IO 20502ac6454SAndrew Thompson int 206a593f6b8SAndrew Thompson usb_uiomove(struct usb_page_cache *pc, struct uio *uio, 207e0a69b51SAndrew Thompson usb_frlength_t pc_offset, usb_frlength_t len) 20802ac6454SAndrew Thompson { 209760bc48eSAndrew Thompson struct usb_page_search res; 21002ac6454SAndrew Thompson int error = 0; 21102ac6454SAndrew Thompson 21202ac6454SAndrew Thompson while (len != 0) { 21302ac6454SAndrew Thompson 214a593f6b8SAndrew Thompson usbd_get_page(pc, pc_offset, &res); 21502ac6454SAndrew Thompson 21602ac6454SAndrew Thompson if (res.length > len) { 21702ac6454SAndrew Thompson res.length = len; 21802ac6454SAndrew Thompson } 21902ac6454SAndrew Thompson /* 22002ac6454SAndrew Thompson * "uiomove()" can sleep so one needs to make a wrapper, 22102ac6454SAndrew Thompson * exiting the mutex and checking things 22202ac6454SAndrew Thompson */ 22302ac6454SAndrew Thompson error = uiomove(res.buffer, res.length, uio); 22402ac6454SAndrew Thompson 22502ac6454SAndrew Thompson if (error) { 22602ac6454SAndrew Thompson break; 22702ac6454SAndrew Thompson } 22802ac6454SAndrew Thompson pc_offset += res.length; 22902ac6454SAndrew Thompson len -= res.length; 23002ac6454SAndrew Thompson } 23102ac6454SAndrew Thompson return (error); 23202ac6454SAndrew Thompson } 233bdc081c6SAndrew Thompson #endif 23402ac6454SAndrew Thompson 23502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 236a593f6b8SAndrew Thompson * usbd_copy_out - copy directly from DMA-able memory 23702ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 23802ac6454SAndrew Thompson void 239a593f6b8SAndrew Thompson usbd_copy_out(struct usb_page_cache *cache, usb_frlength_t offset, 240e0a69b51SAndrew Thompson void *ptr, usb_frlength_t len) 24102ac6454SAndrew Thompson { 242760bc48eSAndrew Thompson struct usb_page_search res; 24302ac6454SAndrew Thompson 24402ac6454SAndrew Thompson while (len != 0) { 24502ac6454SAndrew Thompson 246a593f6b8SAndrew Thompson usbd_get_page(cache, offset, &res); 24702ac6454SAndrew Thompson 24802ac6454SAndrew Thompson if (res.length > len) { 24902ac6454SAndrew Thompson res.length = len; 25002ac6454SAndrew Thompson } 25102ac6454SAndrew Thompson bcopy(res.buffer, ptr, res.length); 25202ac6454SAndrew Thompson 25302ac6454SAndrew Thompson offset += res.length; 25402ac6454SAndrew Thompson len -= res.length; 25502ac6454SAndrew Thompson ptr = USB_ADD_BYTES(ptr, res.length); 25602ac6454SAndrew Thompson } 25702ac6454SAndrew Thompson } 25802ac6454SAndrew Thompson 25902ac6454SAndrew Thompson /*------------------------------------------------------------------------* 260a593f6b8SAndrew Thompson * usbd_copy_out_user - copy directly from DMA-able memory to userland 26102ac6454SAndrew Thompson * 26202ac6454SAndrew Thompson * Return values: 26302ac6454SAndrew Thompson * 0: Success 26402ac6454SAndrew Thompson * Else: Failure 26502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 266bdc081c6SAndrew Thompson #if USB_HAVE_USER_IO 26702ac6454SAndrew Thompson int 268a593f6b8SAndrew Thompson usbd_copy_out_user(struct usb_page_cache *cache, usb_frlength_t offset, 269e0a69b51SAndrew Thompson void *ptr, usb_frlength_t len) 27002ac6454SAndrew Thompson { 271760bc48eSAndrew Thompson struct usb_page_search res; 27202ac6454SAndrew Thompson int error; 27302ac6454SAndrew Thompson 27402ac6454SAndrew Thompson while (len != 0) { 27502ac6454SAndrew Thompson 276a593f6b8SAndrew Thompson usbd_get_page(cache, offset, &res); 27702ac6454SAndrew Thompson 27802ac6454SAndrew Thompson if (res.length > len) { 27902ac6454SAndrew Thompson res.length = len; 28002ac6454SAndrew Thompson } 28102ac6454SAndrew Thompson error = copyout(res.buffer, ptr, res.length); 28202ac6454SAndrew Thompson if (error) 28302ac6454SAndrew Thompson return (error); 28402ac6454SAndrew Thompson 28502ac6454SAndrew Thompson offset += res.length; 28602ac6454SAndrew Thompson len -= res.length; 28702ac6454SAndrew Thompson ptr = USB_ADD_BYTES(ptr, res.length); 28802ac6454SAndrew Thompson } 28902ac6454SAndrew Thompson return (0); /* success */ 29002ac6454SAndrew Thompson } 291bdc081c6SAndrew Thompson #endif 29202ac6454SAndrew Thompson 29302ac6454SAndrew Thompson /*------------------------------------------------------------------------* 294a593f6b8SAndrew Thompson * usbd_frame_zero - zero DMA-able memory 29502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 29602ac6454SAndrew Thompson void 297a593f6b8SAndrew Thompson usbd_frame_zero(struct usb_page_cache *cache, usb_frlength_t offset, 298e0a69b51SAndrew Thompson usb_frlength_t len) 29902ac6454SAndrew Thompson { 300760bc48eSAndrew Thompson struct usb_page_search res; 30102ac6454SAndrew Thompson 30202ac6454SAndrew Thompson while (len != 0) { 30302ac6454SAndrew Thompson 304a593f6b8SAndrew Thompson usbd_get_page(cache, offset, &res); 30502ac6454SAndrew Thompson 30602ac6454SAndrew Thompson if (res.length > len) { 30702ac6454SAndrew Thompson res.length = len; 30802ac6454SAndrew Thompson } 30902ac6454SAndrew Thompson bzero(res.buffer, res.length); 31002ac6454SAndrew Thompson 31102ac6454SAndrew Thompson offset += res.length; 31202ac6454SAndrew Thompson len -= res.length; 31302ac6454SAndrew Thompson } 31402ac6454SAndrew Thompson } 31502ac6454SAndrew Thompson 3163e873976SAndrew Thompson #if USB_HAVE_BUSDMA 31702ac6454SAndrew Thompson 31802ac6454SAndrew Thompson /*------------------------------------------------------------------------* 319a593f6b8SAndrew Thompson * usb_dma_lock_cb - dummy callback 32002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 32102ac6454SAndrew Thompson static void 322a593f6b8SAndrew Thompson usb_dma_lock_cb(void *arg, bus_dma_lock_op_t op) 32302ac6454SAndrew Thompson { 32402ac6454SAndrew Thompson /* we use "mtx_owned()" instead of this function */ 32502ac6454SAndrew Thompson } 32602ac6454SAndrew Thompson 32702ac6454SAndrew Thompson /*------------------------------------------------------------------------* 328a593f6b8SAndrew Thompson * usb_dma_tag_create - allocate a DMA tag 32902ac6454SAndrew Thompson * 33002ac6454SAndrew Thompson * NOTE: If the "align" parameter has a value of 1 the DMA-tag will 33102ac6454SAndrew Thompson * allow multi-segment mappings. Else all mappings are single-segment. 33202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 33302ac6454SAndrew Thompson static void 334a593f6b8SAndrew Thompson usb_dma_tag_create(struct usb_dma_tag *udt, 335f9cb546cSAndrew Thompson usb_size_t size, usb_size_t align) 33602ac6454SAndrew Thompson { 33702ac6454SAndrew Thompson bus_dma_tag_t tag; 33802ac6454SAndrew Thompson 33902ac6454SAndrew Thompson if (bus_dma_tag_create 34002ac6454SAndrew Thompson ( /* parent */ udt->tag_parent->tag, 34102ac6454SAndrew Thompson /* alignment */ align, 34202ac6454SAndrew Thompson /* boundary */ USB_PAGE_SIZE, 34302ac6454SAndrew Thompson /* lowaddr */ (2ULL << (udt->tag_parent->dma_bits - 1)) - 1, 34402ac6454SAndrew Thompson /* highaddr */ BUS_SPACE_MAXADDR, 34502ac6454SAndrew Thompson /* filter */ NULL, 34602ac6454SAndrew Thompson /* filterarg */ NULL, 34702ac6454SAndrew Thompson /* maxsize */ size, 34802ac6454SAndrew Thompson /* nsegments */ (align == 1) ? 34902ac6454SAndrew Thompson (2 + (size / USB_PAGE_SIZE)) : 1, 35002ac6454SAndrew Thompson /* maxsegsz */ (align == 1) ? 35102ac6454SAndrew Thompson USB_PAGE_SIZE : size, 35202ac6454SAndrew Thompson /* flags */ BUS_DMA_KEEP_PG_OFFSET, 353a593f6b8SAndrew Thompson /* lockfn */ &usb_dma_lock_cb, 35402ac6454SAndrew Thompson /* lockarg */ NULL, 35502ac6454SAndrew Thompson &tag)) { 35602ac6454SAndrew Thompson tag = NULL; 35702ac6454SAndrew Thompson } 35802ac6454SAndrew Thompson udt->tag = tag; 35902ac6454SAndrew Thompson } 36002ac6454SAndrew Thompson 36102ac6454SAndrew Thompson /*------------------------------------------------------------------------* 362a593f6b8SAndrew Thompson * usb_dma_tag_free - free a DMA tag 36302ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 36402ac6454SAndrew Thompson static void 365a593f6b8SAndrew Thompson usb_dma_tag_destroy(struct usb_dma_tag *udt) 36602ac6454SAndrew Thompson { 36702ac6454SAndrew Thompson bus_dma_tag_destroy(udt->tag); 36802ac6454SAndrew Thompson } 36902ac6454SAndrew Thompson 37002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 371a593f6b8SAndrew Thompson * usb_pc_alloc_mem_cb - BUS-DMA callback function 37202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 37302ac6454SAndrew Thompson static void 374a593f6b8SAndrew Thompson usb_pc_alloc_mem_cb(void *arg, bus_dma_segment_t *segs, 37502ac6454SAndrew Thompson int nseg, int error) 37602ac6454SAndrew Thompson { 377a593f6b8SAndrew Thompson usb_pc_common_mem_cb(arg, segs, nseg, error, 0); 37802ac6454SAndrew Thompson } 37902ac6454SAndrew Thompson 38002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 381a593f6b8SAndrew Thompson * usb_pc_load_mem_cb - BUS-DMA callback function 38202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 38302ac6454SAndrew Thompson static void 384a593f6b8SAndrew Thompson usb_pc_load_mem_cb(void *arg, bus_dma_segment_t *segs, 38502ac6454SAndrew Thompson int nseg, int error) 38602ac6454SAndrew Thompson { 387a593f6b8SAndrew Thompson usb_pc_common_mem_cb(arg, segs, nseg, error, 1); 38802ac6454SAndrew Thompson } 38902ac6454SAndrew Thompson 39002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 391a593f6b8SAndrew Thompson * usb_pc_common_mem_cb - BUS-DMA callback function 39202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 39302ac6454SAndrew Thompson static void 394a593f6b8SAndrew Thompson usb_pc_common_mem_cb(void *arg, bus_dma_segment_t *segs, 39502ac6454SAndrew Thompson int nseg, int error, uint8_t isload) 39602ac6454SAndrew Thompson { 397760bc48eSAndrew Thompson struct usb_dma_parent_tag *uptag; 398760bc48eSAndrew Thompson struct usb_page_cache *pc; 399760bc48eSAndrew Thompson struct usb_page *pg; 400f9cb546cSAndrew Thompson usb_size_t rem; 40102ac6454SAndrew Thompson uint8_t owned; 40202ac6454SAndrew Thompson 40302ac6454SAndrew Thompson pc = arg; 40402ac6454SAndrew Thompson uptag = pc->tag_parent; 40502ac6454SAndrew Thompson 40602ac6454SAndrew Thompson /* 40702ac6454SAndrew Thompson * XXX There is sometimes recursive locking here. 40802ac6454SAndrew Thompson * XXX We should try to find a better solution. 40902ac6454SAndrew Thompson * XXX Until further the "owned" variable does 41002ac6454SAndrew Thompson * XXX the trick. 41102ac6454SAndrew Thompson */ 41202ac6454SAndrew Thompson 41302ac6454SAndrew Thompson if (error) { 41402ac6454SAndrew Thompson goto done; 41502ac6454SAndrew Thompson } 41602ac6454SAndrew Thompson pg = pc->page_start; 41702ac6454SAndrew Thompson pg->physaddr = segs->ds_addr & ~(USB_PAGE_SIZE - 1); 41802ac6454SAndrew Thompson rem = segs->ds_addr & (USB_PAGE_SIZE - 1); 41902ac6454SAndrew Thompson pc->page_offset_buf = rem; 42002ac6454SAndrew Thompson pc->page_offset_end += rem; 42102ac6454SAndrew Thompson nseg--; 42202ac6454SAndrew Thompson #if (USB_DEBUG != 0) 42302ac6454SAndrew Thompson if (rem != (USB_P2U(pc->buffer) & (USB_PAGE_SIZE - 1))) { 42402ac6454SAndrew Thompson /* 42502ac6454SAndrew Thompson * This check verifies that the physical address is correct: 42602ac6454SAndrew Thompson */ 42702ac6454SAndrew Thompson DPRINTFN(0, "Page offset was not preserved!\n"); 42802ac6454SAndrew Thompson error = 1; 42902ac6454SAndrew Thompson goto done; 43002ac6454SAndrew Thompson } 43102ac6454SAndrew Thompson #endif 43202ac6454SAndrew Thompson while (nseg > 0) { 43302ac6454SAndrew Thompson nseg--; 43402ac6454SAndrew Thompson segs++; 43502ac6454SAndrew Thompson pg++; 43602ac6454SAndrew Thompson pg->physaddr = segs->ds_addr & ~(USB_PAGE_SIZE - 1); 43702ac6454SAndrew Thompson } 43802ac6454SAndrew Thompson 43902ac6454SAndrew Thompson done: 44002ac6454SAndrew Thompson owned = mtx_owned(uptag->mtx); 44102ac6454SAndrew Thompson if (!owned) 44202ac6454SAndrew Thompson mtx_lock(uptag->mtx); 44302ac6454SAndrew Thompson 44402ac6454SAndrew Thompson uptag->dma_error = (error ? 1 : 0); 44502ac6454SAndrew Thompson if (isload) { 44602ac6454SAndrew Thompson (uptag->func) (uptag); 44702ac6454SAndrew Thompson } else { 4488437751dSAndrew Thompson cv_broadcast(uptag->cv); 44902ac6454SAndrew Thompson } 45002ac6454SAndrew Thompson if (!owned) 45102ac6454SAndrew Thompson mtx_unlock(uptag->mtx); 45202ac6454SAndrew Thompson } 45302ac6454SAndrew Thompson 45402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 455a593f6b8SAndrew Thompson * usb_pc_alloc_mem - allocate DMA'able memory 45602ac6454SAndrew Thompson * 45702ac6454SAndrew Thompson * Returns: 45802ac6454SAndrew Thompson * 0: Success 45902ac6454SAndrew Thompson * Else: Failure 46002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 46102ac6454SAndrew Thompson uint8_t 462a593f6b8SAndrew Thompson usb_pc_alloc_mem(struct usb_page_cache *pc, struct usb_page *pg, 463f9cb546cSAndrew Thompson usb_size_t size, usb_size_t align) 46402ac6454SAndrew Thompson { 465760bc48eSAndrew Thompson struct usb_dma_parent_tag *uptag; 466760bc48eSAndrew Thompson struct usb_dma_tag *utag; 46702ac6454SAndrew Thompson bus_dmamap_t map; 46802ac6454SAndrew Thompson void *ptr; 46902ac6454SAndrew Thompson int err; 47002ac6454SAndrew Thompson 47102ac6454SAndrew Thompson uptag = pc->tag_parent; 47202ac6454SAndrew Thompson 47302ac6454SAndrew Thompson if (align != 1) { 47402ac6454SAndrew Thompson /* 47502ac6454SAndrew Thompson * The alignment must be greater or equal to the 47602ac6454SAndrew Thompson * "size" else the object can be split between two 47702ac6454SAndrew Thompson * memory pages and we get a problem! 47802ac6454SAndrew Thompson */ 47902ac6454SAndrew Thompson while (align < size) { 48002ac6454SAndrew Thompson align *= 2; 48102ac6454SAndrew Thompson if (align == 0) { 48202ac6454SAndrew Thompson goto error; 48302ac6454SAndrew Thompson } 48402ac6454SAndrew Thompson } 48502ac6454SAndrew Thompson #if 1 48602ac6454SAndrew Thompson /* 48702ac6454SAndrew Thompson * XXX BUS-DMA workaround - FIXME later: 48802ac6454SAndrew Thompson * 48902ac6454SAndrew Thompson * We assume that that the aligment at this point of 49002ac6454SAndrew Thompson * the code is greater than or equal to the size and 49102ac6454SAndrew Thompson * less than two times the size, so that if we double 49202ac6454SAndrew Thompson * the size, the size will be greater than the 49302ac6454SAndrew Thompson * alignment. 49402ac6454SAndrew Thompson * 49502ac6454SAndrew Thompson * The bus-dma system has a check for "alignment" 49602ac6454SAndrew Thompson * being less than "size". If that check fails we end 49702ac6454SAndrew Thompson * up using contigmalloc which is page based even for 49802ac6454SAndrew Thompson * small allocations. Try to avoid that to save 49902ac6454SAndrew Thompson * memory, hence we sometimes to a large number of 50002ac6454SAndrew Thompson * small allocations! 50102ac6454SAndrew Thompson */ 50202ac6454SAndrew Thompson if (size <= (USB_PAGE_SIZE / 2)) { 50302ac6454SAndrew Thompson size *= 2; 50402ac6454SAndrew Thompson } 50502ac6454SAndrew Thompson #endif 50602ac6454SAndrew Thompson } 50702ac6454SAndrew Thompson /* get the correct DMA tag */ 508a593f6b8SAndrew Thompson utag = usb_dma_tag_find(uptag, size, align); 50902ac6454SAndrew Thompson if (utag == NULL) { 51002ac6454SAndrew Thompson goto error; 51102ac6454SAndrew Thompson } 51202ac6454SAndrew Thompson /* allocate memory */ 51302ac6454SAndrew Thompson if (bus_dmamem_alloc( 51402ac6454SAndrew Thompson utag->tag, &ptr, (BUS_DMA_WAITOK | BUS_DMA_COHERENT), &map)) { 51502ac6454SAndrew Thompson goto error; 51602ac6454SAndrew Thompson } 51702ac6454SAndrew Thompson /* setup page cache */ 51802ac6454SAndrew Thompson pc->buffer = ptr; 51902ac6454SAndrew Thompson pc->page_start = pg; 52002ac6454SAndrew Thompson pc->page_offset_buf = 0; 52102ac6454SAndrew Thompson pc->page_offset_end = size; 52202ac6454SAndrew Thompson pc->map = map; 52302ac6454SAndrew Thompson pc->tag = utag->tag; 52402ac6454SAndrew Thompson pc->ismultiseg = (align == 1); 52502ac6454SAndrew Thompson 52602ac6454SAndrew Thompson mtx_lock(uptag->mtx); 52702ac6454SAndrew Thompson 52802ac6454SAndrew Thompson /* load memory into DMA */ 52902ac6454SAndrew Thompson err = bus_dmamap_load( 530a593f6b8SAndrew Thompson utag->tag, map, ptr, size, &usb_pc_alloc_mem_cb, 53102ac6454SAndrew Thompson pc, (BUS_DMA_WAITOK | BUS_DMA_COHERENT)); 53202ac6454SAndrew Thompson 53302ac6454SAndrew Thompson if (err == EINPROGRESS) { 5348437751dSAndrew Thompson cv_wait(uptag->cv, uptag->mtx); 53502ac6454SAndrew Thompson err = 0; 53602ac6454SAndrew Thompson } 53702ac6454SAndrew Thompson mtx_unlock(uptag->mtx); 53802ac6454SAndrew Thompson 53902ac6454SAndrew Thompson if (err || uptag->dma_error) { 54002ac6454SAndrew Thompson bus_dmamem_free(utag->tag, ptr, map); 54102ac6454SAndrew Thompson goto error; 54202ac6454SAndrew Thompson } 54302ac6454SAndrew Thompson bzero(ptr, size); 54402ac6454SAndrew Thompson 545a593f6b8SAndrew Thompson usb_pc_cpu_flush(pc); 54602ac6454SAndrew Thompson 54702ac6454SAndrew Thompson return (0); 54802ac6454SAndrew Thompson 54902ac6454SAndrew Thompson error: 55002ac6454SAndrew Thompson /* reset most of the page cache */ 55102ac6454SAndrew Thompson pc->buffer = NULL; 55202ac6454SAndrew Thompson pc->page_start = NULL; 55302ac6454SAndrew Thompson pc->page_offset_buf = 0; 55402ac6454SAndrew Thompson pc->page_offset_end = 0; 55502ac6454SAndrew Thompson pc->map = NULL; 55602ac6454SAndrew Thompson pc->tag = NULL; 55702ac6454SAndrew Thompson return (1); 55802ac6454SAndrew Thompson } 55902ac6454SAndrew Thompson 56002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 561a593f6b8SAndrew Thompson * usb_pc_free_mem - free DMA memory 56202ac6454SAndrew Thompson * 56302ac6454SAndrew Thompson * This function is NULL safe. 56402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 56502ac6454SAndrew Thompson void 566a593f6b8SAndrew Thompson usb_pc_free_mem(struct usb_page_cache *pc) 56702ac6454SAndrew Thompson { 56802ac6454SAndrew Thompson if (pc && pc->buffer) { 56902ac6454SAndrew Thompson 57002ac6454SAndrew Thompson bus_dmamap_unload(pc->tag, pc->map); 57102ac6454SAndrew Thompson 57202ac6454SAndrew Thompson bus_dmamem_free(pc->tag, pc->buffer, pc->map); 57302ac6454SAndrew Thompson 57402ac6454SAndrew Thompson pc->buffer = NULL; 57502ac6454SAndrew Thompson } 57602ac6454SAndrew Thompson } 57702ac6454SAndrew Thompson 57802ac6454SAndrew Thompson /*------------------------------------------------------------------------* 579a593f6b8SAndrew Thompson * usb_pc_load_mem - load virtual memory into DMA 58002ac6454SAndrew Thompson * 58102ac6454SAndrew Thompson * Return values: 58202ac6454SAndrew Thompson * 0: Success 58302ac6454SAndrew Thompson * Else: Error 58402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 58502ac6454SAndrew Thompson uint8_t 586a593f6b8SAndrew Thompson usb_pc_load_mem(struct usb_page_cache *pc, usb_size_t size, uint8_t sync) 58702ac6454SAndrew Thompson { 58802ac6454SAndrew Thompson /* setup page cache */ 58902ac6454SAndrew Thompson pc->page_offset_buf = 0; 59002ac6454SAndrew Thompson pc->page_offset_end = size; 59102ac6454SAndrew Thompson pc->ismultiseg = 1; 59202ac6454SAndrew Thompson 59302ac6454SAndrew Thompson mtx_assert(pc->tag_parent->mtx, MA_OWNED); 59402ac6454SAndrew Thompson 59502ac6454SAndrew Thompson if (size > 0) { 59602ac6454SAndrew Thompson if (sync) { 597760bc48eSAndrew Thompson struct usb_dma_parent_tag *uptag; 59802ac6454SAndrew Thompson int err; 59902ac6454SAndrew Thompson 60002ac6454SAndrew Thompson uptag = pc->tag_parent; 60102ac6454SAndrew Thompson 60202ac6454SAndrew Thompson /* 60302ac6454SAndrew Thompson * We have to unload the previous loaded DMA 60402ac6454SAndrew Thompson * pages before trying to load a new one! 60502ac6454SAndrew Thompson */ 60602ac6454SAndrew Thompson bus_dmamap_unload(pc->tag, pc->map); 60702ac6454SAndrew Thompson 60802ac6454SAndrew Thompson /* 60902ac6454SAndrew Thompson * Try to load memory into DMA. 61002ac6454SAndrew Thompson */ 61102ac6454SAndrew Thompson err = bus_dmamap_load( 61202ac6454SAndrew Thompson pc->tag, pc->map, pc->buffer, size, 613a593f6b8SAndrew Thompson &usb_pc_alloc_mem_cb, pc, BUS_DMA_WAITOK); 61402ac6454SAndrew Thompson if (err == EINPROGRESS) { 6158437751dSAndrew Thompson cv_wait(uptag->cv, uptag->mtx); 61602ac6454SAndrew Thompson err = 0; 61702ac6454SAndrew Thompson } 61802ac6454SAndrew Thompson if (err || uptag->dma_error) { 61902ac6454SAndrew Thompson return (1); 62002ac6454SAndrew Thompson } 62102ac6454SAndrew Thompson } else { 62202ac6454SAndrew Thompson 62302ac6454SAndrew Thompson /* 62402ac6454SAndrew Thompson * We have to unload the previous loaded DMA 62502ac6454SAndrew Thompson * pages before trying to load a new one! 62602ac6454SAndrew Thompson */ 62702ac6454SAndrew Thompson bus_dmamap_unload(pc->tag, pc->map); 62802ac6454SAndrew Thompson 62902ac6454SAndrew Thompson /* 63002ac6454SAndrew Thompson * Try to load memory into DMA. The callback 63102ac6454SAndrew Thompson * will be called in all cases: 63202ac6454SAndrew Thompson */ 63302ac6454SAndrew Thompson if (bus_dmamap_load( 63402ac6454SAndrew Thompson pc->tag, pc->map, pc->buffer, size, 635a593f6b8SAndrew Thompson &usb_pc_load_mem_cb, pc, BUS_DMA_WAITOK)) { 63602ac6454SAndrew Thompson } 63702ac6454SAndrew Thompson } 63802ac6454SAndrew Thompson } else { 63902ac6454SAndrew Thompson if (!sync) { 64002ac6454SAndrew Thompson /* 64102ac6454SAndrew Thompson * Call callback so that refcount is decremented 64202ac6454SAndrew Thompson * properly: 64302ac6454SAndrew Thompson */ 64402ac6454SAndrew Thompson pc->tag_parent->dma_error = 0; 64502ac6454SAndrew Thompson (pc->tag_parent->func) (pc->tag_parent); 64602ac6454SAndrew Thompson } 64702ac6454SAndrew Thompson } 64802ac6454SAndrew Thompson return (0); 64902ac6454SAndrew Thompson } 65002ac6454SAndrew Thompson 65102ac6454SAndrew Thompson /*------------------------------------------------------------------------* 652a593f6b8SAndrew Thompson * usb_pc_cpu_invalidate - invalidate CPU cache 65302ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 65402ac6454SAndrew Thompson void 655a593f6b8SAndrew Thompson usb_pc_cpu_invalidate(struct usb_page_cache *pc) 65602ac6454SAndrew Thompson { 65702ac6454SAndrew Thompson if (pc->page_offset_end == pc->page_offset_buf) { 65802ac6454SAndrew Thompson /* nothing has been loaded into this page cache! */ 65902ac6454SAndrew Thompson return; 66002ac6454SAndrew Thompson } 66102ac6454SAndrew Thompson bus_dmamap_sync(pc->tag, pc->map, 66202ac6454SAndrew Thompson BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); 66302ac6454SAndrew Thompson } 66402ac6454SAndrew Thompson 66502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 666a593f6b8SAndrew Thompson * usb_pc_cpu_flush - flush CPU cache 66702ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 66802ac6454SAndrew Thompson void 669a593f6b8SAndrew Thompson usb_pc_cpu_flush(struct usb_page_cache *pc) 67002ac6454SAndrew Thompson { 67102ac6454SAndrew Thompson if (pc->page_offset_end == pc->page_offset_buf) { 67202ac6454SAndrew Thompson /* nothing has been loaded into this page cache! */ 67302ac6454SAndrew Thompson return; 67402ac6454SAndrew Thompson } 67502ac6454SAndrew Thompson bus_dmamap_sync(pc->tag, pc->map, 67602ac6454SAndrew Thompson BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); 67702ac6454SAndrew Thompson } 67802ac6454SAndrew Thompson 67902ac6454SAndrew Thompson /*------------------------------------------------------------------------* 680a593f6b8SAndrew Thompson * usb_pc_dmamap_create - create a DMA map 68102ac6454SAndrew Thompson * 68202ac6454SAndrew Thompson * Returns: 68302ac6454SAndrew Thompson * 0: Success 68402ac6454SAndrew Thompson * Else: Failure 68502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 68602ac6454SAndrew Thompson uint8_t 687a593f6b8SAndrew Thompson usb_pc_dmamap_create(struct usb_page_cache *pc, usb_size_t size) 68802ac6454SAndrew Thompson { 689760bc48eSAndrew Thompson struct usb_xfer_root *info; 690760bc48eSAndrew Thompson struct usb_dma_tag *utag; 69102ac6454SAndrew Thompson 69202ac6454SAndrew Thompson /* get info */ 693bdc081c6SAndrew Thompson info = USB_DMATAG_TO_XROOT(pc->tag_parent); 69402ac6454SAndrew Thompson 69502ac6454SAndrew Thompson /* sanity check */ 69602ac6454SAndrew Thompson if (info == NULL) { 69702ac6454SAndrew Thompson goto error; 69802ac6454SAndrew Thompson } 699a593f6b8SAndrew Thompson utag = usb_dma_tag_find(pc->tag_parent, size, 1); 70002ac6454SAndrew Thompson if (utag == NULL) { 70102ac6454SAndrew Thompson goto error; 70202ac6454SAndrew Thompson } 70302ac6454SAndrew Thompson /* create DMA map */ 70402ac6454SAndrew Thompson if (bus_dmamap_create(utag->tag, 0, &pc->map)) { 70502ac6454SAndrew Thompson goto error; 70602ac6454SAndrew Thompson } 70702ac6454SAndrew Thompson pc->tag = utag->tag; 70802ac6454SAndrew Thompson return 0; /* success */ 70902ac6454SAndrew Thompson 71002ac6454SAndrew Thompson error: 71102ac6454SAndrew Thompson pc->map = NULL; 71202ac6454SAndrew Thompson pc->tag = NULL; 71302ac6454SAndrew Thompson return 1; /* failure */ 71402ac6454SAndrew Thompson } 71502ac6454SAndrew Thompson 71602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 717a593f6b8SAndrew Thompson * usb_pc_dmamap_destroy 71802ac6454SAndrew Thompson * 71902ac6454SAndrew Thompson * This function is NULL safe. 72002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 72102ac6454SAndrew Thompson void 722a593f6b8SAndrew Thompson usb_pc_dmamap_destroy(struct usb_page_cache *pc) 72302ac6454SAndrew Thompson { 72402ac6454SAndrew Thompson if (pc && pc->tag) { 72502ac6454SAndrew Thompson bus_dmamap_destroy(pc->tag, pc->map); 72602ac6454SAndrew Thompson pc->tag = NULL; 72702ac6454SAndrew Thompson pc->map = NULL; 72802ac6454SAndrew Thompson } 72902ac6454SAndrew Thompson } 73002ac6454SAndrew Thompson 73102ac6454SAndrew Thompson /*------------------------------------------------------------------------* 732a593f6b8SAndrew Thompson * usb_dma_tag_find - factored out code 73302ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 734760bc48eSAndrew Thompson struct usb_dma_tag * 735a593f6b8SAndrew Thompson usb_dma_tag_find(struct usb_dma_parent_tag *udpt, 736f9cb546cSAndrew Thompson usb_size_t size, usb_size_t align) 73702ac6454SAndrew Thompson { 738760bc48eSAndrew Thompson struct usb_dma_tag *udt; 73902ac6454SAndrew Thompson uint8_t nudt; 74002ac6454SAndrew Thompson 74102ac6454SAndrew Thompson USB_ASSERT(align > 0, ("Invalid parameter align = 0!\n")); 74202ac6454SAndrew Thompson USB_ASSERT(size > 0, ("Invalid parameter size = 0!\n")); 74302ac6454SAndrew Thompson 74402ac6454SAndrew Thompson udt = udpt->utag_first; 74502ac6454SAndrew Thompson nudt = udpt->utag_max; 74602ac6454SAndrew Thompson 74702ac6454SAndrew Thompson while (nudt--) { 74802ac6454SAndrew Thompson 74902ac6454SAndrew Thompson if (udt->align == 0) { 750a593f6b8SAndrew Thompson usb_dma_tag_create(udt, size, align); 75102ac6454SAndrew Thompson if (udt->tag == NULL) { 75202ac6454SAndrew Thompson return (NULL); 75302ac6454SAndrew Thompson } 75402ac6454SAndrew Thompson udt->align = align; 75502ac6454SAndrew Thompson udt->size = size; 75602ac6454SAndrew Thompson return (udt); 75702ac6454SAndrew Thompson } 75802ac6454SAndrew Thompson if ((udt->align == align) && (udt->size == size)) { 75902ac6454SAndrew Thompson return (udt); 76002ac6454SAndrew Thompson } 76102ac6454SAndrew Thompson udt++; 76202ac6454SAndrew Thompson } 76302ac6454SAndrew Thompson return (NULL); 76402ac6454SAndrew Thompson } 76502ac6454SAndrew Thompson 76602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 767a593f6b8SAndrew Thompson * usb_dma_tag_setup - initialise USB DMA tags 76802ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 76902ac6454SAndrew Thompson void 770a593f6b8SAndrew Thompson usb_dma_tag_setup(struct usb_dma_parent_tag *udpt, 771760bc48eSAndrew Thompson struct usb_dma_tag *udt, bus_dma_tag_t dmat, 772e0a69b51SAndrew Thompson struct mtx *mtx, usb_dma_callback_t *func, 773bdc081c6SAndrew Thompson uint8_t ndmabits, uint8_t nudt) 77402ac6454SAndrew Thompson { 77502ac6454SAndrew Thompson bzero(udpt, sizeof(*udpt)); 77602ac6454SAndrew Thompson 77702ac6454SAndrew Thompson /* sanity checking */ 77802ac6454SAndrew Thompson if ((nudt == 0) || 77902ac6454SAndrew Thompson (ndmabits == 0) || 78002ac6454SAndrew Thompson (mtx == NULL)) { 78102ac6454SAndrew Thompson /* something is corrupt */ 78202ac6454SAndrew Thompson return; 78302ac6454SAndrew Thompson } 78402ac6454SAndrew Thompson /* initialise condition variable */ 7858437751dSAndrew Thompson cv_init(udpt->cv, "USB DMA CV"); 78602ac6454SAndrew Thompson 78702ac6454SAndrew Thompson /* store some information */ 78802ac6454SAndrew Thompson udpt->mtx = mtx; 78902ac6454SAndrew Thompson udpt->func = func; 79002ac6454SAndrew Thompson udpt->tag = dmat; 79102ac6454SAndrew Thompson udpt->utag_first = udt; 79202ac6454SAndrew Thompson udpt->utag_max = nudt; 79302ac6454SAndrew Thompson udpt->dma_bits = ndmabits; 79402ac6454SAndrew Thompson 79502ac6454SAndrew Thompson while (nudt--) { 79602ac6454SAndrew Thompson bzero(udt, sizeof(*udt)); 79702ac6454SAndrew Thompson udt->tag_parent = udpt; 79802ac6454SAndrew Thompson udt++; 79902ac6454SAndrew Thompson } 80002ac6454SAndrew Thompson } 80102ac6454SAndrew Thompson 80202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 803a593f6b8SAndrew Thompson * usb_bus_tag_unsetup - factored out code 80402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 80502ac6454SAndrew Thompson void 806a593f6b8SAndrew Thompson usb_dma_tag_unsetup(struct usb_dma_parent_tag *udpt) 80702ac6454SAndrew Thompson { 808760bc48eSAndrew Thompson struct usb_dma_tag *udt; 80902ac6454SAndrew Thompson uint8_t nudt; 81002ac6454SAndrew Thompson 81102ac6454SAndrew Thompson udt = udpt->utag_first; 81202ac6454SAndrew Thompson nudt = udpt->utag_max; 81302ac6454SAndrew Thompson 81402ac6454SAndrew Thompson while (nudt--) { 81502ac6454SAndrew Thompson 81602ac6454SAndrew Thompson if (udt->align) { 81702ac6454SAndrew Thompson /* destroy the USB DMA tag */ 818a593f6b8SAndrew Thompson usb_dma_tag_destroy(udt); 81902ac6454SAndrew Thompson udt->align = 0; 82002ac6454SAndrew Thompson } 82102ac6454SAndrew Thompson udt++; 82202ac6454SAndrew Thompson } 82302ac6454SAndrew Thompson 82402ac6454SAndrew Thompson if (udpt->utag_max) { 82502ac6454SAndrew Thompson /* destroy the condition variable */ 8268437751dSAndrew Thompson cv_destroy(udpt->cv); 82702ac6454SAndrew Thompson } 82802ac6454SAndrew Thompson } 82902ac6454SAndrew Thompson 83002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 831a593f6b8SAndrew Thompson * usb_bdma_work_loop 83202ac6454SAndrew Thompson * 83302ac6454SAndrew Thompson * This function handles loading of virtual buffers into DMA and is 83402ac6454SAndrew Thompson * only called when "dma_refcount" is zero. 83502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 83602ac6454SAndrew Thompson void 837a593f6b8SAndrew Thompson usb_bdma_work_loop(struct usb_xfer_queue *pq) 83802ac6454SAndrew Thompson { 839760bc48eSAndrew Thompson struct usb_xfer_root *info; 840760bc48eSAndrew Thompson struct usb_xfer *xfer; 841e0a69b51SAndrew Thompson usb_frcount_t nframes; 84202ac6454SAndrew Thompson 84302ac6454SAndrew Thompson xfer = pq->curr; 84402ac6454SAndrew Thompson info = xfer->xroot; 84502ac6454SAndrew Thompson 84602ac6454SAndrew Thompson mtx_assert(info->xfer_mtx, MA_OWNED); 84702ac6454SAndrew Thompson 84802ac6454SAndrew Thompson if (xfer->error) { 84902ac6454SAndrew Thompson /* some error happened */ 85002ac6454SAndrew Thompson USB_BUS_LOCK(info->bus); 851a593f6b8SAndrew Thompson usbd_transfer_done(xfer, 0); 85202ac6454SAndrew Thompson USB_BUS_UNLOCK(info->bus); 85302ac6454SAndrew Thompson return; 85402ac6454SAndrew Thompson } 85502ac6454SAndrew Thompson if (!xfer->flags_int.bdma_setup) { 856760bc48eSAndrew Thompson struct usb_page *pg; 857e0a69b51SAndrew Thompson usb_frlength_t frlength_0; 85802ac6454SAndrew Thompson uint8_t isread; 85902ac6454SAndrew Thompson 86002ac6454SAndrew Thompson xfer->flags_int.bdma_setup = 1; 86102ac6454SAndrew Thompson 86202ac6454SAndrew Thompson /* reset BUS-DMA load state */ 86302ac6454SAndrew Thompson 86402ac6454SAndrew Thompson info->dma_error = 0; 86502ac6454SAndrew Thompson 86602ac6454SAndrew Thompson if (xfer->flags_int.isochronous_xfr) { 86702ac6454SAndrew Thompson /* only one frame buffer */ 86802ac6454SAndrew Thompson nframes = 1; 86902ac6454SAndrew Thompson frlength_0 = xfer->sumlen; 87002ac6454SAndrew Thompson } else { 87102ac6454SAndrew Thompson /* can be multiple frame buffers */ 87202ac6454SAndrew Thompson nframes = xfer->nframes; 87302ac6454SAndrew Thompson frlength_0 = xfer->frlengths[0]; 87402ac6454SAndrew Thompson } 87502ac6454SAndrew Thompson 87602ac6454SAndrew Thompson /* 87702ac6454SAndrew Thompson * Set DMA direction first. This is needed to 87802ac6454SAndrew Thompson * select the correct cache invalidate and cache 87902ac6454SAndrew Thompson * flush operations. 88002ac6454SAndrew Thompson */ 88102ac6454SAndrew Thompson isread = USB_GET_DATA_ISREAD(xfer); 88202ac6454SAndrew Thompson pg = xfer->dma_page_ptr; 88302ac6454SAndrew Thompson 88402ac6454SAndrew Thompson if (xfer->flags_int.control_xfr && 88502ac6454SAndrew Thompson xfer->flags_int.control_hdr) { 88602ac6454SAndrew Thompson /* special case */ 887f29a0724SAndrew Thompson if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) { 88802ac6454SAndrew Thompson /* The device controller writes to memory */ 88902ac6454SAndrew Thompson xfer->frbuffers[0].isread = 1; 89002ac6454SAndrew Thompson } else { 89102ac6454SAndrew Thompson /* The host controller reads from memory */ 89202ac6454SAndrew Thompson xfer->frbuffers[0].isread = 0; 89302ac6454SAndrew Thompson } 89402ac6454SAndrew Thompson } else { 89502ac6454SAndrew Thompson /* default case */ 89602ac6454SAndrew Thompson xfer->frbuffers[0].isread = isread; 89702ac6454SAndrew Thompson } 89802ac6454SAndrew Thompson 89902ac6454SAndrew Thompson /* 90002ac6454SAndrew Thompson * Setup the "page_start" pointer which points to an array of 90102ac6454SAndrew Thompson * USB pages where information about the physical address of a 90202ac6454SAndrew Thompson * page will be stored. Also initialise the "isread" field of 90302ac6454SAndrew Thompson * the USB page caches. 90402ac6454SAndrew Thompson */ 90502ac6454SAndrew Thompson xfer->frbuffers[0].page_start = pg; 90602ac6454SAndrew Thompson 90702ac6454SAndrew Thompson info->dma_nframes = nframes; 90802ac6454SAndrew Thompson info->dma_currframe = 0; 90902ac6454SAndrew Thompson info->dma_frlength_0 = frlength_0; 91002ac6454SAndrew Thompson 91102ac6454SAndrew Thompson pg += (frlength_0 / USB_PAGE_SIZE); 91202ac6454SAndrew Thompson pg += 2; 91302ac6454SAndrew Thompson 91402ac6454SAndrew Thompson while (--nframes > 0) { 91502ac6454SAndrew Thompson xfer->frbuffers[nframes].isread = isread; 91602ac6454SAndrew Thompson xfer->frbuffers[nframes].page_start = pg; 91702ac6454SAndrew Thompson 91802ac6454SAndrew Thompson pg += (xfer->frlengths[nframes] / USB_PAGE_SIZE); 91902ac6454SAndrew Thompson pg += 2; 92002ac6454SAndrew Thompson } 92102ac6454SAndrew Thompson 92202ac6454SAndrew Thompson } 92302ac6454SAndrew Thompson if (info->dma_error) { 92402ac6454SAndrew Thompson USB_BUS_LOCK(info->bus); 925a593f6b8SAndrew Thompson usbd_transfer_done(xfer, USB_ERR_DMA_LOAD_FAILED); 92602ac6454SAndrew Thompson USB_BUS_UNLOCK(info->bus); 92702ac6454SAndrew Thompson return; 92802ac6454SAndrew Thompson } 92902ac6454SAndrew Thompson if (info->dma_currframe != info->dma_nframes) { 93002ac6454SAndrew Thompson 93102ac6454SAndrew Thompson if (info->dma_currframe == 0) { 93202ac6454SAndrew Thompson /* special case */ 933a593f6b8SAndrew Thompson usb_pc_load_mem(xfer->frbuffers, 93402ac6454SAndrew Thompson info->dma_frlength_0, 0); 93502ac6454SAndrew Thompson } else { 93602ac6454SAndrew Thompson /* default case */ 93702ac6454SAndrew Thompson nframes = info->dma_currframe; 938a593f6b8SAndrew Thompson usb_pc_load_mem(xfer->frbuffers + nframes, 93902ac6454SAndrew Thompson xfer->frlengths[nframes], 0); 94002ac6454SAndrew Thompson } 94102ac6454SAndrew Thompson 94202ac6454SAndrew Thompson /* advance frame index */ 94302ac6454SAndrew Thompson info->dma_currframe++; 94402ac6454SAndrew Thompson 94502ac6454SAndrew Thompson return; 94602ac6454SAndrew Thompson } 94702ac6454SAndrew Thompson /* go ahead */ 948a593f6b8SAndrew Thompson usb_bdma_pre_sync(xfer); 94902ac6454SAndrew Thompson 95002ac6454SAndrew Thompson /* start loading next USB transfer, if any */ 951a593f6b8SAndrew Thompson usb_command_wrapper(pq, NULL); 95202ac6454SAndrew Thompson 95302ac6454SAndrew Thompson /* finally start the hardware */ 954a593f6b8SAndrew Thompson usbd_pipe_enter(xfer); 95502ac6454SAndrew Thompson } 95602ac6454SAndrew Thompson 95702ac6454SAndrew Thompson /*------------------------------------------------------------------------* 958a593f6b8SAndrew Thompson * usb_bdma_done_event 95902ac6454SAndrew Thompson * 96002ac6454SAndrew Thompson * This function is called when the BUS-DMA has loaded virtual memory 96102ac6454SAndrew Thompson * into DMA, if any. 96202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 96302ac6454SAndrew Thompson void 964a593f6b8SAndrew Thompson usb_bdma_done_event(struct usb_dma_parent_tag *udpt) 96502ac6454SAndrew Thompson { 966760bc48eSAndrew Thompson struct usb_xfer_root *info; 96702ac6454SAndrew Thompson 968bdc081c6SAndrew Thompson info = USB_DMATAG_TO_XROOT(udpt); 96902ac6454SAndrew Thompson 97002ac6454SAndrew Thompson mtx_assert(info->xfer_mtx, MA_OWNED); 97102ac6454SAndrew Thompson 97202ac6454SAndrew Thompson /* copy error */ 97302ac6454SAndrew Thompson info->dma_error = udpt->dma_error; 97402ac6454SAndrew Thompson 97502ac6454SAndrew Thompson /* enter workloop again */ 976a593f6b8SAndrew Thompson usb_command_wrapper(&info->dma_q, 97702ac6454SAndrew Thompson info->dma_q.curr); 97802ac6454SAndrew Thompson } 97902ac6454SAndrew Thompson 98002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 981a593f6b8SAndrew Thompson * usb_bdma_pre_sync 98202ac6454SAndrew Thompson * 98302ac6454SAndrew Thompson * This function handles DMA synchronisation that must be done before 98402ac6454SAndrew Thompson * an USB transfer is started. 98502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 98602ac6454SAndrew Thompson void 987a593f6b8SAndrew Thompson usb_bdma_pre_sync(struct usb_xfer *xfer) 98802ac6454SAndrew Thompson { 989760bc48eSAndrew Thompson struct usb_page_cache *pc; 990e0a69b51SAndrew Thompson usb_frcount_t nframes; 99102ac6454SAndrew Thompson 99202ac6454SAndrew Thompson if (xfer->flags_int.isochronous_xfr) { 99302ac6454SAndrew Thompson /* only one frame buffer */ 99402ac6454SAndrew Thompson nframes = 1; 99502ac6454SAndrew Thompson } else { 99602ac6454SAndrew Thompson /* can be multiple frame buffers */ 99702ac6454SAndrew Thompson nframes = xfer->nframes; 99802ac6454SAndrew Thompson } 99902ac6454SAndrew Thompson 100002ac6454SAndrew Thompson pc = xfer->frbuffers; 100102ac6454SAndrew Thompson 100202ac6454SAndrew Thompson while (nframes--) { 100302ac6454SAndrew Thompson 100402ac6454SAndrew Thompson if (pc->isread) { 1005a593f6b8SAndrew Thompson usb_pc_cpu_invalidate(pc); 100602ac6454SAndrew Thompson } else { 1007a593f6b8SAndrew Thompson usb_pc_cpu_flush(pc); 100802ac6454SAndrew Thompson } 100902ac6454SAndrew Thompson pc++; 101002ac6454SAndrew Thompson } 101102ac6454SAndrew Thompson } 101202ac6454SAndrew Thompson 101302ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1014a593f6b8SAndrew Thompson * usb_bdma_post_sync 101502ac6454SAndrew Thompson * 101602ac6454SAndrew Thompson * This function handles DMA synchronisation that must be done after 101702ac6454SAndrew Thompson * an USB transfer is complete. 101802ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 101902ac6454SAndrew Thompson void 1020a593f6b8SAndrew Thompson usb_bdma_post_sync(struct usb_xfer *xfer) 102102ac6454SAndrew Thompson { 1022760bc48eSAndrew Thompson struct usb_page_cache *pc; 1023e0a69b51SAndrew Thompson usb_frcount_t nframes; 102402ac6454SAndrew Thompson 102502ac6454SAndrew Thompson if (xfer->flags_int.isochronous_xfr) { 102602ac6454SAndrew Thompson /* only one frame buffer */ 102702ac6454SAndrew Thompson nframes = 1; 102802ac6454SAndrew Thompson } else { 102902ac6454SAndrew Thompson /* can be multiple frame buffers */ 103002ac6454SAndrew Thompson nframes = xfer->nframes; 103102ac6454SAndrew Thompson } 103202ac6454SAndrew Thompson 103302ac6454SAndrew Thompson pc = xfer->frbuffers; 103402ac6454SAndrew Thompson 103502ac6454SAndrew Thompson while (nframes--) { 103602ac6454SAndrew Thompson if (pc->isread) { 1037a593f6b8SAndrew Thompson usb_pc_cpu_invalidate(pc); 103802ac6454SAndrew Thompson } 103902ac6454SAndrew Thompson pc++; 104002ac6454SAndrew Thompson } 104102ac6454SAndrew Thompson } 1042bdc081c6SAndrew Thompson 1043bdc081c6SAndrew Thompson #endif 1044