summaryrefslogtreecommitdiff
path: root/sys/dev/firewire/fwdma.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/firewire/fwdma.c')
-rw-r--r--sys/dev/firewire/fwdma.c233
1 files changed, 233 insertions, 0 deletions
diff --git a/sys/dev/firewire/fwdma.c b/sys/dev/firewire/fwdma.c
new file mode 100644
index 000000000000..afb1a09b1155
--- /dev/null
+++ b/sys/dev/firewire/fwdma.c
@@ -0,0 +1,233 @@
+/*
+ * Copyright (c) 2003
+ * Hidetoshi Shimokawa. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ *
+ * This product includes software developed by Hidetoshi Shimokawa.
+ *
+ * 4. Neither the name of the author nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#ifdef __FreeBSD__
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+#endif
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/types.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#if defined(__FreeBSD__) && __FreeBSD_version >= 501102
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#endif
+
+#include <sys/bus.h>
+#include <machine/bus.h>
+
+#ifdef __DragonFly__
+#include <bus/firewire/firewire.h>
+#include <bus/firewire/firewirereg.h>
+#include <bus/firewire/fwdma.h>
+#else
+#include <dev/firewire/firewire.h>
+#include <dev/firewire/firewirereg.h>
+#include <dev/firewire/fwdma.h>
+#endif
+
+static void
+fwdma_map_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error)
+{
+ bus_addr_t *baddr;
+
+ if (error)
+ printf("fwdma_map_cb: error=%d\n", error);
+ baddr = (bus_addr_t *)arg;
+ *baddr = segs->ds_addr;
+}
+
+void *
+fwdma_malloc(struct firewire_comm *fc, int alignment, bus_size_t size,
+ struct fwdma_alloc *dma, int flag)
+{
+ int err;
+
+ dma->v_addr = NULL;
+ err = bus_dma_tag_create(
+ /*parent*/ fc->dmat,
+ /*alignment*/ alignment,
+ /*boundary*/ 0,
+ /*lowaddr*/ BUS_SPACE_MAXADDR_32BIT,
+ /*highaddr*/ BUS_SPACE_MAXADDR,
+ /*filter*/NULL, /*filterarg*/NULL,
+ /*maxsize*/ size,
+ /*nsegments*/ 1,
+ /*maxsegsz*/ BUS_SPACE_MAXSIZE_32BIT,
+ /*flags*/ BUS_DMA_ALLOCNOW,
+#if defined(__FreeBSD__) && __FreeBSD_version >= 501102
+ /*lockfunc*/busdma_lock_mutex,
+ /*lockarg*/&Giant,
+#endif
+ &dma->dma_tag);
+ if (err) {
+ printf("fwdma_malloc: failed(1)\n");
+ return(NULL);
+ }
+
+ err = bus_dmamem_alloc(dma->dma_tag, &dma->v_addr,
+ flag, &dma->dma_map);
+ if (err) {
+ printf("fwdma_malloc: failed(2)\n");
+ /* XXX destory tag */
+ return(NULL);
+ }
+
+ bus_dmamap_load(dma->dma_tag, dma->dma_map, dma->v_addr,
+ size, fwdma_map_cb, &dma->bus_addr, /*flags*/0);
+
+ return(dma->v_addr);
+}
+
+void
+fwdma_free(struct firewire_comm *fc, struct fwdma_alloc *dma)
+{
+ bus_dmamap_unload(dma->dma_tag, dma->dma_map);
+ bus_dmamem_free(dma->dma_tag, dma->v_addr, dma->dma_map);
+ bus_dma_tag_destroy(dma->dma_tag);
+}
+
+
+void *
+fwdma_malloc_size(bus_dma_tag_t dmat, bus_dmamap_t *dmamap,
+ bus_size_t size, bus_addr_t *bus_addr, int flag)
+{
+ void *v_addr;
+
+ if (bus_dmamem_alloc(dmat, &v_addr, flag, dmamap)) {
+ printf("fwdma_malloc_size: failed(1)\n");
+ return(NULL);
+ }
+ bus_dmamap_load(dmat, *dmamap, v_addr, size,
+ fwdma_map_cb, bus_addr, /*flags*/0);
+ return(v_addr);
+}
+
+void
+fwdma_free_size(bus_dma_tag_t dmat, bus_dmamap_t dmamap,
+ void *vaddr, bus_size_t size)
+{
+ bus_dmamap_unload(dmat, dmamap);
+ bus_dmamem_free(dmat, vaddr, dmamap);
+}
+
+/*
+ * Allocate multisegment dma buffers
+ * each segment size is eqaul to ssize except last segment.
+ */
+struct fwdma_alloc_multi *
+fwdma_malloc_multiseg(struct firewire_comm *fc, int alignment,
+ int esize, int n, int flag)
+{
+ struct fwdma_alloc_multi *am;
+ struct fwdma_seg *seg;
+ bus_size_t ssize;
+ int nseg;
+
+ if (esize > PAGE_SIZE) {
+ /* round up to PAGE_SIZE */
+ esize = ssize = roundup2(esize, PAGE_SIZE);
+ nseg = n;
+ } else {
+ /* allocate PAGE_SIZE segment for small elements */
+ ssize = rounddown(PAGE_SIZE, esize);
+ nseg = howmany(n, ssize / esize);
+ }
+ am = (struct fwdma_alloc_multi *)malloc(sizeof(struct fwdma_alloc_multi)
+ + sizeof(struct fwdma_seg)*nseg, M_FW, M_WAITOK);
+ if (am == NULL) {
+ printf("fwdma_malloc_multiseg: malloc failed\n");
+ return(NULL);
+ }
+ am->ssize = ssize;
+ am->esize = esize;
+ am->nseg = 0;
+ if (bus_dma_tag_create(
+ /*parent*/ fc->dmat,
+ /*alignment*/ alignment,
+ /*boundary*/ 0,
+ /*lowaddr*/ BUS_SPACE_MAXADDR_32BIT,
+ /*highaddr*/ BUS_SPACE_MAXADDR,
+ /*filter*/NULL, /*filterarg*/NULL,
+ /*maxsize*/ ssize,
+ /*nsegments*/ 1,
+ /*maxsegsz*/ BUS_SPACE_MAXSIZE_32BIT,
+ /*flags*/ BUS_DMA_ALLOCNOW,
+#if defined(__FreeBSD__) && __FreeBSD_version >= 501102
+ /*lockfunc*/busdma_lock_mutex,
+ /*lockarg*/&Giant,
+#endif
+ &am->dma_tag)) {
+ printf("fwdma_malloc_multiseg: tag_create failed\n");
+ free(am, M_FW);
+ return(NULL);
+ }
+
+#if 0
+#if defined(__DragonFly__) || __FreeBSD_version < 500000
+ printf("malloc_multi: ssize=%d nseg=%d\n", ssize, nseg);
+#else
+ printf("malloc_multi: ssize=%td nseg=%d\n", ssize, nseg);
+#endif
+#endif
+ for (seg = &am->seg[0]; nseg --; seg ++) {
+ seg->v_addr = fwdma_malloc_size(am->dma_tag, &seg->dma_map,
+ ssize, &seg->bus_addr, flag);
+ if (seg->v_addr == NULL) {
+ printf("fwdma_malloc_multi: malloc_size failed %d\n",
+ am->nseg);
+ fwdma_free_multiseg(am);
+ return(NULL);
+ }
+ am->nseg++;
+ }
+ return(am);
+}
+
+void
+fwdma_free_multiseg(struct fwdma_alloc_multi *am)
+{
+ struct fwdma_seg *seg;
+
+ for (seg = &am->seg[0]; am->nseg --; seg ++) {
+ fwdma_free_size(am->dma_tag, seg->dma_map,
+ seg->v_addr, am->ssize);
+ }
+ bus_dma_tag_destroy(am->dma_tag);
+ free(am, M_FW);
+}