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 <assert.h> 33 #include <errno.h> 34 #include <fcntl.h> 35 #include <limits.h> 36 #include <stdlib.h> 37 #include <string.h> 38 #include <unistd.h> 39 40 #include "busdma.h" 41 42 #include "../../sys/dev/proto/proto_dev.h" 43 44 struct obj { 45 int oid; 46 u_int type; 47 #define OBJ_TYPE_NONE 0 48 #define OBJ_TYPE_TAG 1 49 #define OBJ_TYPE_MD 2 50 #define OBJ_TYPE_SEG 3 51 u_int refcnt; 52 int fd; 53 struct obj *parent; 54 u_long key; 55 union { 56 struct { 57 unsigned long align; 58 unsigned long bndry; 59 unsigned long maxaddr; 60 unsigned long maxsz; 61 unsigned long maxsegsz; 62 unsigned long nsegs; 63 unsigned long datarate; 64 } tag; 65 struct { 66 struct obj *seg[3]; 67 int nsegs[3]; 68 #define BUSDMA_MD_BUS 0 69 #define BUSDMA_MD_PHYS 1 70 #define BUSDMA_MD_VIRT 2 71 } md; 72 struct { 73 struct obj *next; 74 unsigned long address; 75 unsigned long size; 76 } seg; 77 } u; 78 }; 79 80 static struct obj **oidtbl = NULL; 81 static int noids = 0; 82 83 static struct obj * 84 obj_alloc(u_int type) 85 { 86 struct obj **newtbl, *obj; 87 int oid; 88 89 obj = calloc(1, sizeof(struct obj)); 90 obj->type = type; 91 92 for (oid = 0; oid < noids; oid++) { 93 if (oidtbl[oid] == 0) 94 break; 95 } 96 if (oid == noids) { 97 newtbl = realloc(oidtbl, sizeof(struct obj *) * (noids + 1)); 98 if (newtbl == NULL) { 99 free(obj); 100 return (NULL); 101 } 102 oidtbl = newtbl; 103 noids++; 104 } 105 oidtbl[oid] = obj; 106 obj->oid = oid; 107 return (obj); 108 } 109 110 static int 111 obj_free(struct obj *obj) 112 { 113 114 oidtbl[obj->oid] = NULL; 115 free(obj); 116 return (0); 117 } 118 119 static struct obj * 120 obj_lookup(int oid, u_int type) 121 { 122 struct obj *obj; 123 124 if (oid < 0 || oid >= noids) { 125 errno = EINVAL; 126 return (NULL); 127 } 128 obj = oidtbl[oid]; 129 if (obj->refcnt == 0) { 130 errno = ENXIO; 131 return (NULL); 132 } 133 if (type != OBJ_TYPE_NONE && obj->type != type) { 134 errno = ENODEV; 135 return (NULL); 136 } 137 return (obj); 138 } 139 140 struct obj * 141 bd_tag_new(struct obj *ptag, int fd, u_long align, u_long bndry, 142 u_long maxaddr, u_long maxsz, u_int nsegs, u_long maxsegsz, 143 u_int datarate, u_int flags) 144 { 145 struct proto_ioc_busdma ioc; 146 struct obj *tag; 147 148 tag = obj_alloc(OBJ_TYPE_TAG); 149 if (tag == NULL) 150 return (NULL); 151 152 memset(&ioc, 0, sizeof(ioc)); 153 ioc.request = (ptag != NULL) ? PROTO_IOC_BUSDMA_TAG_DERIVE : 154 PROTO_IOC_BUSDMA_TAG_CREATE; 155 ioc.key = (ptag != NULL) ? ptag->key : 0; 156 ioc.u.tag.align = align; 157 ioc.u.tag.bndry = bndry; 158 ioc.u.tag.maxaddr = maxaddr; 159 ioc.u.tag.maxsz = maxsz; 160 ioc.u.tag.nsegs = nsegs; 161 ioc.u.tag.maxsegsz = maxsegsz; 162 ioc.u.tag.datarate = datarate; 163 ioc.u.tag.flags = flags; 164 if (ioctl(fd, PROTO_IOC_BUSDMA, &ioc) == -1) { 165 obj_free(tag); 166 return (NULL); 167 } 168 tag->refcnt = 1; 169 tag->fd = fd; 170 tag->parent = ptag; 171 tag->key = ioc.result; 172 tag->u.tag.align = ioc.u.tag.align; 173 tag->u.tag.bndry = ioc.u.tag.bndry; 174 tag->u.tag.maxaddr = ioc.u.tag.maxaddr; 175 tag->u.tag.maxsz = ioc.u.tag.maxsz; 176 tag->u.tag.maxsegsz = ioc.u.tag.maxsegsz; 177 tag->u.tag.nsegs = ioc.u.tag.nsegs; 178 tag->u.tag.datarate = ioc.u.tag.datarate; 179 return (tag); 180 } 181 182 int 183 bd_tag_create(const char *dev, u_long align, u_long bndry, u_long maxaddr, 184 u_long maxsz, u_int nsegs, u_long maxsegsz, u_int datarate, u_int flags) 185 { 186 struct obj *tag; 187 int fd; 188 189 fd = open(dev, O_RDWR); 190 if (fd == -1) 191 return (-1); 192 193 tag = bd_tag_new(NULL, fd, align, bndry, maxaddr, maxsz, nsegs, 194 maxsegsz, datarate, flags); 195 if (tag == NULL) { 196 close(fd); 197 return (-1); 198 } 199 return (tag->oid); 200 } 201 202 int 203 bd_tag_derive(int ptid, u_long align, u_long bndry, u_long maxaddr, 204 u_long maxsz, u_int nsegs, u_long maxsegsz, u_int datarate, u_int flags) 205 { 206 struct obj *ptag, *tag; 207 208 ptag = obj_lookup(ptid, OBJ_TYPE_TAG); 209 if (ptag == NULL) 210 return (-1); 211 212 tag = bd_tag_new(ptag, ptag->fd, align, bndry, maxaddr, maxsz, nsegs, 213 maxsegsz, datarate, flags); 214 if (tag == NULL) 215 return (-1); 216 ptag->refcnt++; 217 return (tag->oid); 218 } 219 220 int 221 bd_tag_destroy(int tid) 222 { 223 struct proto_ioc_busdma ioc; 224 struct obj *ptag, *tag; 225 226 tag = obj_lookup(tid, OBJ_TYPE_TAG); 227 if (tag == NULL) 228 return (errno); 229 if (tag->refcnt > 1) 230 return (EBUSY); 231 232 memset(&ioc, 0, sizeof(ioc)); 233 ioc.request = PROTO_IOC_BUSDMA_TAG_DESTROY; 234 ioc.key = tag->key; 235 if (ioctl(tag->fd, PROTO_IOC_BUSDMA, &ioc) == -1) 236 return (errno); 237 238 if (tag->parent != NULL) 239 tag->parent->refcnt--; 240 else 241 close(tag->fd); 242 obj_free(tag); 243 return (0); 244 } 245 246 int 247 bd_mem_alloc(int tid, u_int flags) 248 { 249 struct proto_ioc_busdma ioc; 250 struct obj *md, *tag; 251 struct obj *bseg, *pseg, *vseg; 252 253 tag = obj_lookup(tid, OBJ_TYPE_TAG); 254 if (tag == NULL) 255 return (-1); 256 257 md = obj_alloc(OBJ_TYPE_MD); 258 if (md == NULL) 259 return (-1); 260 261 memset(&ioc, 0, sizeof(ioc)); 262 ioc.request = PROTO_IOC_BUSDMA_MEM_ALLOC; 263 ioc.u.mem.tag = tag->key; 264 ioc.u.mem.flags = flags; 265 if (ioctl(tag->fd, PROTO_IOC_BUSDMA, &ioc) == -1) { 266 obj_free(md); 267 return (-1); 268 } 269 270 md->refcnt = 1; 271 md->fd = tag->fd; 272 md->parent = tag; 273 tag->refcnt++; 274 md->key = ioc.result; 275 276 /* XXX we need to support multiple segments */ 277 assert(ioc.u.mem.phys_nsegs == 1); 278 assert(ioc.u.mem.bus_nsegs == 1); 279 280 bseg = pseg = vseg = NULL; 281 282 bseg = obj_alloc(OBJ_TYPE_SEG); 283 if (bseg == NULL) 284 goto fail; 285 bseg->refcnt = 1; 286 bseg->parent = md; 287 bseg->u.seg.address = ioc.u.mem.bus_addr; 288 bseg->u.seg.size = tag->u.tag.maxsz; 289 md->u.md.seg[BUSDMA_MD_BUS] = bseg; 290 md->u.md.nsegs[BUSDMA_MD_BUS] = ioc.u.mem.bus_nsegs; 291 292 pseg = obj_alloc(OBJ_TYPE_SEG); 293 if (pseg == NULL) 294 goto fail; 295 pseg->refcnt = 1; 296 pseg->parent = md; 297 pseg->u.seg.address = ioc.u.mem.phys_addr; 298 pseg->u.seg.size = tag->u.tag.maxsz; 299 md->u.md.seg[BUSDMA_MD_PHYS] = pseg; 300 md->u.md.nsegs[BUSDMA_MD_PHYS] = ioc.u.mem.phys_nsegs; 301 302 vseg = obj_alloc(OBJ_TYPE_SEG); 303 if (vseg == NULL) 304 goto fail; 305 vseg->refcnt = 1; 306 vseg->parent = md; 307 vseg->u.seg.address = (uintptr_t)mmap(NULL, pseg->u.seg.size, 308 PROT_READ | PROT_WRITE, MAP_NOCORE | MAP_SHARED, md->fd, 309 pseg->u.seg.address); 310 if (vseg->u.seg.address == (uintptr_t)MAP_FAILED) 311 goto fail; 312 vseg->u.seg.size = pseg->u.seg.size; 313 md->u.md.seg[BUSDMA_MD_VIRT] = vseg; 314 md->u.md.nsegs[BUSDMA_MD_VIRT] = 1; 315 316 return (md->oid); 317 318 fail: 319 if (vseg != NULL) 320 obj_free(vseg); 321 if (pseg != NULL) 322 obj_free(pseg); 323 if (bseg != NULL) 324 obj_free(bseg); 325 memset(&ioc, 0, sizeof(ioc)); 326 ioc.request = PROTO_IOC_BUSDMA_MEM_FREE; 327 ioc.key = md->key; 328 ioctl(md->fd, PROTO_IOC_BUSDMA, &ioc); 329 md->parent->refcnt--; 330 obj_free(md); 331 return (-1); 332 } 333 334 int 335 bd_mem_free(int mdid) 336 { 337 struct proto_ioc_busdma ioc; 338 struct obj *md, *seg, *seg0; 339 340 md = obj_lookup(mdid, OBJ_TYPE_MD); 341 if (md == NULL) 342 return (errno); 343 344 for (seg = md->u.md.seg[BUSDMA_MD_VIRT]; seg != NULL; seg = seg0) { 345 munmap((void *)seg->u.seg.address, seg->u.seg.size); 346 seg0 = seg->u.seg.next; 347 obj_free(seg); 348 } 349 for (seg = md->u.md.seg[BUSDMA_MD_PHYS]; seg != NULL; seg = seg0) { 350 seg0 = seg->u.seg.next; 351 obj_free(seg); 352 } 353 for (seg = md->u.md.seg[BUSDMA_MD_BUS]; seg != NULL; seg = seg0) { 354 seg0 = seg->u.seg.next; 355 obj_free(seg); 356 } 357 memset(&ioc, 0, sizeof(ioc)); 358 ioc.request = PROTO_IOC_BUSDMA_MEM_FREE; 359 ioc.key = md->key; 360 if (ioctl(md->fd, PROTO_IOC_BUSDMA, &ioc) == -1) 361 return (errno); 362 363 md->parent->refcnt--; 364 obj_free(md); 365 return (0); 366 } 367 368 int 369 bd_md_first_seg(int mdid, int space) 370 { 371 struct obj *md, *seg; 372 373 md = obj_lookup(mdid, OBJ_TYPE_MD); 374 if (md == NULL) 375 return (-1); 376 377 if (space != BUSDMA_MD_BUS && space != BUSDMA_MD_PHYS && 378 space != BUSDMA_MD_VIRT) { 379 errno = EINVAL; 380 return (-1); 381 } 382 seg = md->u.md.seg[space]; 383 if (seg == NULL) { 384 errno = ENXIO; 385 return (-1); 386 } 387 return (seg->oid); 388 } 389 390 int 391 bd_md_next_seg(int mdid, int sid) 392 { 393 struct obj *seg; 394 395 seg = obj_lookup(sid, OBJ_TYPE_SEG); 396 if (seg == NULL) 397 return (-1); 398 399 seg = seg->u.seg.next; 400 if (seg == NULL) { 401 errno = ENXIO; 402 return (-1); 403 } 404 return (seg->oid); 405 } 406 407 int 408 bd_seg_get_addr(int sid, u_long *addr_p) 409 { 410 struct obj *seg; 411 412 if (addr_p == NULL) 413 return (EINVAL); 414 415 seg = obj_lookup(sid, OBJ_TYPE_SEG); 416 if (seg == NULL) 417 return (errno); 418 419 *addr_p = seg->u.seg.address; 420 return (0); 421 } 422 423 int 424 bd_seg_get_size(int sid, u_long *size_p) 425 { 426 struct obj *seg; 427 428 if (size_p == NULL) 429 return (EINVAL); 430 431 seg = obj_lookup(sid, OBJ_TYPE_SEG); 432 if (seg == NULL) 433 return (errno); 434 435 *size_p = seg->u.seg.size; 436 return (0); 437 } 438