1 /*- 2 * Copyright (c) 2015 Marcel Moolenaar 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 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 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 #include <sys/ioctl.h> 31 #include <sys/mman.h> 32 #include <errno.h> 33 #include <fcntl.h> 34 #include <limits.h> 35 #include <stdlib.h> 36 #include <string.h> 37 #include <unistd.h> 38 39 #include "busdma.h" 40 41 #include "../../sys/dev/proto/proto_dev.h" 42 43 struct obj { 44 int oid; 45 u_int type; 46 #define OBJ_TYPE_NONE 0 47 #define OBJ_TYPE_TAG 1 48 #define OBJ_TYPE_MD 2 49 u_int refcnt; 50 int fd; 51 struct obj *parent; 52 u_long key; 53 union { 54 struct { 55 unsigned long align; 56 unsigned long bndry; 57 unsigned long maxaddr; 58 unsigned long maxsz; 59 unsigned long maxsegsz; 60 unsigned long nsegs; 61 unsigned long datarate; 62 } tag; 63 struct { 64 unsigned long physaddr; 65 void *virtaddr; 66 } mem; 67 } u; 68 }; 69 70 static struct obj **oidtbl = NULL; 71 static int noids = 0; 72 73 static struct obj * 74 obj_alloc(u_int type) 75 { 76 struct obj **newtbl, *obj; 77 int oid; 78 79 obj = malloc(sizeof(struct obj)); 80 obj->type = type; 81 obj->refcnt = 0; 82 83 for (oid = 0; oid < noids; oid++) { 84 if (oidtbl[oid] == 0) 85 break; 86 } 87 if (oid == noids) { 88 newtbl = realloc(oidtbl, sizeof(struct obj *) * (noids + 1)); 89 if (newtbl == NULL) { 90 free(obj); 91 return (NULL); 92 } 93 oidtbl = newtbl; 94 noids++; 95 } 96 oidtbl[oid] = obj; 97 obj->oid = oid; 98 return (obj); 99 } 100 101 static int 102 obj_free(struct obj *obj) 103 { 104 105 oidtbl[obj->oid] = NULL; 106 free(obj); 107 return (0); 108 } 109 110 static struct obj * 111 obj_lookup(int oid, u_int type) 112 { 113 struct obj *obj; 114 115 if (oid < 0 || oid >= noids) { 116 errno = EINVAL; 117 return (NULL); 118 } 119 obj = oidtbl[oid]; 120 if (obj->refcnt == 0) { 121 errno = ENXIO; 122 return (NULL); 123 } 124 if (type != OBJ_TYPE_NONE && obj->type != type) { 125 errno = ENODEV; 126 return (NULL); 127 } 128 return (obj); 129 } 130 131 struct obj * 132 bd_tag_new(struct obj *ptag, int fd, u_long align, u_long bndry, 133 u_long maxaddr, u_long maxsz, u_int nsegs, u_long maxsegsz, 134 u_int datarate, u_int flags) 135 { 136 struct proto_ioc_busdma ioc; 137 struct obj *tag; 138 139 tag = obj_alloc(OBJ_TYPE_TAG); 140 if (tag == NULL) 141 return (NULL); 142 143 memset(&ioc, 0, sizeof(ioc)); 144 ioc.request = (ptag != NULL) ? PROTO_IOC_BUSDMA_TAG_DERIVE : 145 PROTO_IOC_BUSDMA_TAG_CREATE; 146 ioc.key = (ptag != NULL) ? ptag->key : 0; 147 ioc.u.tag.align = align; 148 ioc.u.tag.bndry = bndry; 149 ioc.u.tag.maxaddr = maxaddr; 150 ioc.u.tag.maxsz = maxsz; 151 ioc.u.tag.nsegs = nsegs; 152 ioc.u.tag.maxsegsz = maxsegsz; 153 ioc.u.tag.datarate = datarate; 154 ioc.u.tag.flags = flags; 155 if (ioctl(fd, PROTO_IOC_BUSDMA, &ioc) == -1) { 156 obj_free(tag); 157 return (NULL); 158 } 159 tag->refcnt = 1; 160 tag->fd = fd; 161 tag->parent = ptag; 162 tag->key = ioc.result; 163 tag->u.tag.align = ioc.u.tag.align; 164 tag->u.tag.bndry = ioc.u.tag.bndry; 165 tag->u.tag.maxaddr = ioc.u.tag.maxaddr; 166 tag->u.tag.maxsz = ioc.u.tag.maxsz; 167 tag->u.tag.maxsegsz = ioc.u.tag.maxsegsz; 168 tag->u.tag.nsegs = ioc.u.tag.nsegs; 169 tag->u.tag.datarate = ioc.u.tag.datarate; 170 return (tag); 171 } 172 173 int 174 bd_tag_create(const char *dev, u_long align, u_long bndry, u_long maxaddr, 175 u_long maxsz, u_int nsegs, u_long maxsegsz, u_int datarate, u_int flags) 176 { 177 struct obj *tag; 178 int fd; 179 180 fd = open(dev, O_RDWR); 181 if (fd == -1) 182 return (-1); 183 184 tag = bd_tag_new(NULL, fd, align, bndry, maxaddr, maxsz, nsegs, 185 maxsegsz, datarate, flags); 186 if (tag == NULL) { 187 close(fd); 188 return (-1); 189 } 190 return (tag->oid); 191 } 192 193 int 194 bd_tag_derive(int ptid, u_long align, u_long bndry, u_long maxaddr, 195 u_long maxsz, u_int nsegs, u_long maxsegsz, u_int datarate, u_int flags) 196 { 197 struct obj *ptag, *tag; 198 199 ptag = obj_lookup(ptid, OBJ_TYPE_TAG); 200 if (ptag == NULL) 201 return (-1); 202 203 tag = bd_tag_new(ptag, ptag->fd, align, bndry, maxaddr, maxsz, nsegs, 204 maxsegsz, datarate, flags); 205 if (tag == NULL) 206 return (-1); 207 ptag->refcnt++; 208 return (tag->oid); 209 } 210 211 int 212 bd_tag_destroy(int tid) 213 { 214 struct proto_ioc_busdma ioc; 215 struct obj *ptag, *tag; 216 217 tag = obj_lookup(tid, OBJ_TYPE_TAG); 218 if (tag == NULL) 219 return (errno); 220 if (tag->refcnt > 1) 221 return (EBUSY); 222 223 memset(&ioc, 0, sizeof(ioc)); 224 ioc.request = PROTO_IOC_BUSDMA_TAG_DESTROY; 225 ioc.key = tag->key; 226 if (ioctl(tag->fd, PROTO_IOC_BUSDMA, &ioc) == -1) 227 return (errno); 228 229 if (tag->parent != NULL) 230 tag->parent->refcnt--; 231 else 232 close(tag->fd); 233 obj_free(tag); 234 return (0); 235 } 236 237 int 238 bd_mem_alloc(int tid, u_int flags) 239 { 240 struct proto_ioc_busdma ioc; 241 struct obj *md, *tag; 242 243 tag = obj_lookup(tid, OBJ_TYPE_TAG); 244 if (tag == NULL) 245 return (-1); 246 247 md = obj_alloc(OBJ_TYPE_MD); 248 if (md == NULL) 249 return (-1); 250 251 memset(&ioc, 0, sizeof(ioc)); 252 ioc.request = PROTO_IOC_BUSDMA_MEM_ALLOC; 253 ioc.u.mem.tag = tag->key; 254 ioc.u.mem.flags = flags; 255 if (ioctl(tag->fd, PROTO_IOC_BUSDMA, &ioc) == -1) { 256 obj_free(md); 257 return (-1); 258 } 259 260 md->refcnt = 1; 261 md->fd = tag->fd; 262 md->parent = tag; 263 tag->refcnt++; 264 md->key = ioc.result; 265 md->u.mem.physaddr = ioc.u.mem.physaddr; 266 md->u.mem.virtaddr = mmap(NULL, tag->u.tag.maxsz, 267 PROT_READ | PROT_WRITE, MAP_NOCORE | MAP_SHARED, md->fd, 268 md->u.mem.physaddr); 269 return (md->oid); 270 } 271 272 int 273 bd_mem_free(int mdid) 274 { 275 struct proto_ioc_busdma ioc; 276 struct obj *md; 277 278 md = obj_lookup(mdid, OBJ_TYPE_MD); 279 if (md == NULL) 280 return (errno); 281 282 if (md->u.mem.virtaddr != MAP_FAILED) 283 munmap(md->u.mem.virtaddr, md->parent->u.tag.maxsz); 284 memset(&ioc, 0, sizeof(ioc)); 285 ioc.request = PROTO_IOC_BUSDMA_MEM_FREE; 286 ioc.key = md->key; 287 if (ioctl(md->fd, PROTO_IOC_BUSDMA, &ioc) == -1) 288 return (errno); 289 290 md->parent->refcnt--; 291 obj_free(md); 292 return (0); 293 } 294