1 /* $NetBSD: fwdma.c,v 1.18 2021/07/24 21:31:37 andvar Exp $ */ 2 /*- 3 * Copyright (c) 2003 4 * Hidetoshi Shimokawa. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. All advertising materials mentioning features or use of this software 15 * must display the following acknowledgement: 16 * 17 * This product includes software developed by Hidetoshi Shimokawa. 18 * 19 * 4. Neither the name of the author nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 * 35 */ 36 37 #include <sys/cdefs.h> 38 __KERNEL_RCSID(0, "$NetBSD: fwdma.c,v 1.18 2021/07/24 21:31:37 andvar Exp $"); 39 #if defined(__FreeBSD__) 40 __FBSDID("$FreeBSD: src/sys/dev/firewire/fwdma.c,v 1.9 2007/06/06 14:31:36 simokawa Exp $"); 41 #endif 42 43 #include <sys/param.h> 44 #include <sys/bus.h> 45 #include <sys/device.h> 46 #include <sys/systm.h> 47 #include <sys/types.h> 48 #include <sys/kernel.h> 49 #include <sys/malloc.h> 50 #include <sys/kmem.h> 51 #include <sys/select.h> 52 53 #include <machine/vmparam.h> 54 55 #include <dev/ieee1394/firewire.h> 56 #include <dev/ieee1394/firewirereg.h> 57 #include <dev/ieee1394/fwdma.h> 58 59 #define BUS_SPACE_MAXSIZE_32BIT 0xffffffff 60 61 62 void * 63 fwdma_malloc(device_t dev, bus_dma_tag_t dmat, bus_dmamap_t *dmamap, 64 bus_size_t size, int alignment, int flags) 65 { 66 bus_dma_segment_t segs; 67 int nsegs; 68 int err; 69 void *v_addr; 70 71 err = bus_dmamem_alloc(dmat, size, alignment, 0, &segs, 1, 72 &nsegs, flags); 73 if (err) { 74 aprint_error_dev(dev, "DMA memory allocation failed %d\n", err); 75 return NULL; 76 } 77 78 err = bus_dmamem_map(dmat, &segs, nsegs, size, &v_addr, flags); 79 if (err) { 80 aprint_error_dev(dev, "DMA memory map failed %d\n", err); 81 bus_dmamem_free(dmat, &segs, nsegs); 82 return NULL; 83 } 84 85 if (*dmamap == NULL) { 86 err = bus_dmamap_create(dmat, size, nsegs, 87 BUS_SPACE_MAXSIZE_32BIT, 0, flags, dmamap); 88 if (err) { 89 aprint_error_dev(dev, 90 "DMA map create failed %d\n", err); 91 bus_dmamem_unmap(dmat, v_addr, size); 92 bus_dmamem_free(dmat, &segs, nsegs); 93 return NULL; 94 } 95 } 96 97 err = bus_dmamap_load(dmat, *dmamap, v_addr, size, NULL, flags); 98 if (err != 0) { 99 aprint_error_dev(dev, "DMA map load failed %d\n", err); 100 bus_dmamap_destroy(dmat, *dmamap); 101 bus_dmamem_unmap(dmat, v_addr, size); 102 bus_dmamem_free(dmat, &segs, nsegs); 103 return NULL; 104 } 105 106 return v_addr; 107 } 108 109 void 110 fwdma_free(bus_dma_tag_t dmat, bus_dmamap_t dmamap, void *vaddr) 111 { 112 bus_dma_segment_t *segs; 113 114 /* XXX we shouldn't pass around the segs in the dmamap */ 115 const bus_size_t mapsize = dmamap->dm_mapsize; 116 const int nsegs = dmamap->dm_nsegs; 117 const size_t segssz = sizeof(bus_dma_segment_t) * nsegs; 118 segs = kmem_alloc(segssz, KM_SLEEP); 119 memcpy(segs, dmamap->dm_segs, segssz); 120 121 bus_dmamap_unload(dmat, dmamap); 122 bus_dmamem_unmap(dmat, vaddr, mapsize); 123 bus_dmamem_free(dmat, segs, nsegs); 124 bus_dmamap_destroy(dmat, dmamap); 125 126 kmem_free(segs, segssz); 127 } 128 129 130 void * 131 fwdma_alloc_setup(device_t dev, bus_dma_tag_t dmat, bus_size_t size, 132 struct fwdma_alloc *dma, int alignment, int flags) 133 { 134 135 dma->v_addr = 136 fwdma_malloc(dev, dmat, &dma->dma_map, size, alignment, flags); 137 if (dma->v_addr != NULL) { 138 dma->dma_tag = dmat; 139 dma->bus_addr = dma->dma_map->dm_segs[0].ds_addr; 140 } 141 return dma->v_addr; 142 } 143 144 /* 145 * Allocate multisegment dma buffers 146 * each segment size is equal to ssize except last segment. 147 */ 148 struct fwdma_alloc_multi * 149 fwdma_malloc_multiseg(struct firewire_comm *fc, int alignment, int esize, int n, 150 int flags) 151 { 152 struct fwdma_alloc_multi *am; 153 struct fwdma_seg *seg; 154 bus_size_t ssize; 155 size_t size; 156 int nseg; 157 158 if (esize > PAGE_SIZE) { 159 /* round up to PAGE_SIZE */ 160 esize = ssize = roundup2(esize, PAGE_SIZE); 161 nseg = n; 162 } else { 163 /* allocate PAGE_SIZE segment for small elements */ 164 ssize = rounddown(PAGE_SIZE, esize); 165 nseg = howmany(n, ssize / esize); 166 } 167 size = sizeof(struct fwdma_alloc_multi) + 168 sizeof(struct fwdma_seg) * nseg; 169 am = (struct fwdma_alloc_multi *)malloc(size, M_FW, M_WAITOK | M_ZERO); 170 if (am == NULL) { 171 aprint_error_dev(fc->dev, "malloc failed\n"); 172 return NULL; 173 } 174 am->ssize = ssize; 175 am->esize = esize; 176 am->nseg = 0; 177 am->dma_tag = fc->dmat; 178 179 for (seg = am->seg; nseg--; seg++) { 180 seg->v_addr = fwdma_malloc(fc->dev, am->dma_tag, &seg->dma_map, 181 ssize, alignment, flags); 182 if (seg->v_addr == NULL) { 183 aprint_error_dev(fc->dev, "malloc_size failed %d\n", 184 am->nseg); 185 fwdma_free_multiseg(am); 186 return NULL; 187 } 188 seg->bus_addr = seg->dma_map->dm_segs[0].ds_addr; 189 am->nseg++; 190 } 191 return am; 192 } 193 194 void 195 fwdma_free_multiseg(struct fwdma_alloc_multi *am) 196 { 197 struct fwdma_seg *seg; 198 199 for (seg = am->seg; am->nseg--; seg++) 200 fwdma_free(am->dma_tag, seg->dma_map, seg->v_addr); 201 free(am, M_FW); 202 } 203