summaryrefslogtreecommitdiffstats
path: root/tools/bus_space/busdma.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/bus_space/busdma.c')
-rw-r--r--tools/bus_space/busdma.c218
1 files changed, 218 insertions, 0 deletions
diff --git a/tools/bus_space/busdma.c b/tools/bus_space/busdma.c
new file mode 100644
index 0000000..1c502e1
--- /dev/null
+++ b/tools/bus_space/busdma.c
@@ -0,0 +1,218 @@
+/*-
+ * Copyright (c) 2015 Marcel Moolenaar
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "busdma.h"
+
+#include "../../sys/dev/proto/proto_dev.h"
+
+struct tag {
+ int tid;
+ u_int refcnt;
+ int fd;
+ struct tag *ptag;
+ u_long key;
+ u_long align;
+ u_long bndry;
+ u_long maxaddr;
+};
+
+static struct tag **tidtbl = NULL;
+static int ntids = 0;
+
+static struct tag *
+tag_alloc(void)
+{
+ struct tag **newtbl, *tag;
+ int tid;
+
+ tag = malloc(sizeof(struct tag));
+ tag->refcnt = 0;
+
+ for (tid = 0; tid < ntids; tid++) {
+ if (tidtbl[tid] == 0)
+ break;
+ }
+ if (tid == ntids) {
+ newtbl = realloc(tidtbl, sizeof(struct tag *) * (ntids + 1));
+ if (newtbl == NULL) {
+ free(tag);
+ return (NULL);
+ }
+ tidtbl = newtbl;
+ ntids++;
+ }
+ tidtbl[tid] = tag;
+ tag->tid = tid;
+ return (tag);
+}
+
+static int
+tag_free(struct tag *tag)
+{
+
+ tidtbl[tag->tid] = NULL;
+ free(tag);
+ return (0);
+}
+
+static struct tag *
+tid_lookup(int tid)
+{
+ struct tag *tag;
+
+ if (tid < 0 || tid >= ntids) {
+ errno = EINVAL;
+ return (NULL);
+ }
+ tag = tidtbl[tid];
+ if (tag->refcnt == 0) {
+ errno = ENXIO;
+ return (NULL);
+ }
+ return (tag);
+}
+
+struct tag *
+bd_tag_new(struct tag *ptag, int fd, u_long align, u_long bndry,
+ u_long maxaddr, u_long maxsz, u_int nsegs, u_long maxsegsz,
+ u_int datarate, u_int flags)
+{
+ struct proto_ioc_busdma ioc;
+ struct tag *tag;
+
+ tag = tag_alloc();
+ if (tag == NULL)
+ return (NULL);
+
+ memset(&ioc, 0, sizeof(ioc));
+ ioc.request = (ptag != NULL) ? PROTO_IOC_BUSDMA_TAG_DERIVE :
+ PROTO_IOC_BUSDMA_TAG_CREATE;
+ ioc.key = (ptag != NULL) ? ptag->key : 0;
+ ioc.u.tag.align = align;
+ ioc.u.tag.bndry = bndry;
+ ioc.u.tag.maxaddr = maxaddr;
+ ioc.u.tag.maxsz = maxsz;
+ ioc.u.tag.nsegs = nsegs;
+ ioc.u.tag.maxsegsz = maxsegsz;
+ ioc.u.tag.datarate = datarate;
+ ioc.u.tag.flags = flags;
+ if (ioctl(fd, PROTO_IOC_BUSDMA, &ioc) == -1) {
+ tag_free(tag);
+ return (NULL);
+ }
+ tag->refcnt = 1;
+ tag->fd = fd;
+ tag->ptag = ptag;
+ tag->key = ioc.key;
+ tag->align = ioc.u.tag.align;
+ tag->bndry = ioc.u.tag.bndry;
+ tag->maxaddr = ioc.u.tag.maxaddr;
+ return (tag);
+}
+
+int
+bd_tag_create(const char *dev, u_long align, u_long bndry, u_long maxaddr,
+ u_long maxsz, u_int nsegs, u_long maxsegsz, u_int datarate, u_int flags)
+{
+ struct tag *tag;
+ int fd;
+
+ fd = open(dev, O_RDWR);
+ if (fd == -1)
+ return (-1);
+
+ tag = bd_tag_new(NULL, fd, align, bndry, maxaddr, maxsz, nsegs,
+ maxsegsz, datarate, flags);
+ if (tag == NULL) {
+ close(fd);
+ return (-1);
+ }
+ return (tag->tid);
+}
+
+int
+bd_tag_derive(int ptid, u_long align, u_long bndry, u_long maxaddr,
+ u_long maxsz, u_int nsegs, u_long maxsegsz, u_int datarate, u_int flags)
+{
+ struct tag *ptag, *tag;
+
+ ptag = tid_lookup(ptid);
+ if (ptag == NULL)
+ return (-1);
+
+ tag = bd_tag_new(ptag, ptag->fd, align, bndry, maxaddr, maxsz, nsegs,
+ maxsegsz, datarate, flags);
+ if (tag == NULL)
+ return (-1);
+ while (ptag != NULL) {
+ ptag->refcnt++;
+ ptag = ptag->ptag;
+ }
+ return (tag->tid);
+}
+
+int
+bd_tag_destroy(int tid)
+{
+ struct proto_ioc_busdma ioc;
+ struct tag *ptag, *tag;
+
+ tag = tid_lookup(tid);
+ if (tag == NULL)
+ return (errno);
+ if (tag->refcnt > 1)
+ return (EBUSY);
+
+ memset(&ioc, 0, sizeof(ioc));
+ ioc.request = PROTO_IOC_BUSDMA_TAG_DESTROY;
+ ioc.key = tag->key;
+ if (ioctl(tag->fd, PROTO_IOC_BUSDMA, &ioc) == -1)
+ return (errno);
+
+ ptag = tag->ptag;
+ if (ptag == NULL)
+ close(tag->fd);
+ else {
+ do {
+ ptag->refcnt--;
+ ptag = ptag->ptag;
+ } while (ptag != NULL);
+ }
+ tag_free(tag);
+ return (0);
+}
OpenPOWER on IntegriCloud