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 #include <sys/ioctl.h> 29 #include <sys/mman.h> 30 #include <assert.h> 31 #include <errno.h> 32 #include <fcntl.h> 33 #include <limits.h> 34 #include <stdio.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 #define OBJ_TYPE_SEG 3 50 u_int refcnt; 51 int fd; 52 struct obj *parent; 53 u_long key; 54 union { 55 struct { 56 unsigned long align; 57 unsigned long bndry; 58 unsigned long maxaddr; 59 unsigned long maxsz; 60 unsigned long maxsegsz; 61 unsigned long nsegs; 62 unsigned long datarate; 63 } tag; 64 struct { 65 struct obj *seg[3]; 66 int nsegs[3]; 67 #define BUSDMA_MD_BUS 0 68 #define BUSDMA_MD_PHYS 1 69 #define BUSDMA_MD_VIRT 2 70 } md; 71 struct { 72 struct obj *next; 73 unsigned long address; 74 unsigned long size; 75 } seg; 76 } u; 77 }; 78 79 static struct obj **oidtbl = NULL; 80 static int noids = 0; 81 82 static struct obj * 83 obj_alloc(u_int type) 84 { 85 struct obj **newtbl, *obj; 86 int oid; 87 88 obj = calloc(1, sizeof(struct obj)); 89 obj->type = type; 90 91 for (oid = 0; oid < noids; oid++) { 92 if (oidtbl[oid] == 0) 93 break; 94 } 95 if (oid == noids) { 96 newtbl = realloc(oidtbl, sizeof(struct obj *) * (noids + 1)); 97 if (newtbl == NULL) { 98 free(obj); 99 return (NULL); 100 } 101 oidtbl = newtbl; 102 noids++; 103 } 104 oidtbl[oid] = obj; 105 obj->oid = oid; 106 return (obj); 107 } 108 109 static int 110 obj_free(struct obj *obj) 111 { 112 113 oidtbl[obj->oid] = NULL; 114 free(obj); 115 return (0); 116 } 117 118 static struct obj * 119 obj_lookup(int oid, u_int type) 120 { 121 struct obj *obj; 122 123 if (oid < 0 || oid >= noids) { 124 errno = EINVAL; 125 return (NULL); 126 } 127 obj = oidtbl[oid]; 128 if (obj->refcnt == 0) { 129 errno = ENXIO; 130 return (NULL); 131 } 132 if (type != OBJ_TYPE_NONE && obj->type != type) { 133 errno = ENODEV; 134 return (NULL); 135 } 136 return (obj); 137 } 138 139 static struct obj * 140 bd_tag_new(struct obj *ptag, int fd, u_long align, u_long bndry, 141 u_long maxaddr, u_long maxsz, u_int nsegs, u_long maxsegsz, 142 u_int datarate, u_int flags) 143 { 144 struct proto_ioc_busdma ioc; 145 struct obj *tag; 146 147 tag = obj_alloc(OBJ_TYPE_TAG); 148 if (tag == NULL) 149 return (NULL); 150 151 memset(&ioc, 0, sizeof(ioc)); 152 ioc.request = (ptag != NULL) ? PROTO_IOC_BUSDMA_TAG_DERIVE : 153 PROTO_IOC_BUSDMA_TAG_CREATE; 154 ioc.key = (ptag != NULL) ? ptag->key : 0; 155 ioc.u.tag.align = align; 156 ioc.u.tag.bndry = bndry; 157 ioc.u.tag.maxaddr = maxaddr; 158 ioc.u.tag.maxsz = maxsz; 159 ioc.u.tag.nsegs = nsegs; 160 ioc.u.tag.maxsegsz = maxsegsz; 161 ioc.u.tag.datarate = datarate; 162 ioc.u.tag.flags = flags; 163 if (ioctl(fd, PROTO_IOC_BUSDMA, &ioc) == -1) { 164 obj_free(tag); 165 return (NULL); 166 } 167 tag->refcnt = 1; 168 tag->fd = fd; 169 tag->parent = ptag; 170 tag->key = ioc.result; 171 tag->u.tag.align = ioc.u.tag.align; 172 tag->u.tag.bndry = ioc.u.tag.bndry; 173 tag->u.tag.maxaddr = ioc.u.tag.maxaddr; 174 tag->u.tag.maxsz = ioc.u.tag.maxsz; 175 tag->u.tag.maxsegsz = ioc.u.tag.maxsegsz; 176 tag->u.tag.nsegs = ioc.u.tag.nsegs; 177 tag->u.tag.datarate = ioc.u.tag.datarate; 178 return (tag); 179 } 180 181 int 182 bd_tag_create(const char *dev, u_long align, u_long bndry, u_long maxaddr, 183 u_long maxsz, u_int nsegs, u_long maxsegsz, u_int datarate, u_int flags) 184 { 185 char path[PATH_MAX]; 186 struct obj *tag; 187 int fd, len; 188 189 len = snprintf(path, PATH_MAX, "/dev/proto/%s/busdma", dev); 190 if (len >= PATH_MAX) { 191 errno = EINVAL; 192 return (-1); 193 } 194 fd = open(path, O_RDWR); 195 if (fd == -1) 196 return (-1); 197 198 tag = bd_tag_new(NULL, fd, align, bndry, maxaddr, maxsz, nsegs, 199 maxsegsz, datarate, flags); 200 if (tag == NULL) { 201 close(fd); 202 return (-1); 203 } 204 return (tag->oid); 205 } 206 207 int 208 bd_tag_derive(int ptid, u_long align, u_long bndry, u_long maxaddr, 209 u_long maxsz, u_int nsegs, u_long maxsegsz, u_int datarate, u_int flags) 210 { 211 struct obj *ptag, *tag; 212 213 ptag = obj_lookup(ptid, OBJ_TYPE_TAG); 214 if (ptag == NULL) 215 return (-1); 216 217 tag = bd_tag_new(ptag, ptag->fd, align, bndry, maxaddr, maxsz, nsegs, 218 maxsegsz, datarate, flags); 219 if (tag == NULL) 220 return (-1); 221 ptag->refcnt++; 222 return (tag->oid); 223 } 224 225 int 226 bd_tag_destroy(int tid) 227 { 228 struct proto_ioc_busdma ioc; 229 struct obj *ptag, *tag; 230 231 tag = obj_lookup(tid, OBJ_TYPE_TAG); 232 if (tag == NULL) 233 return (errno); 234 if (tag->refcnt > 1) 235 return (EBUSY); 236 237 memset(&ioc, 0, sizeof(ioc)); 238 ioc.request = PROTO_IOC_BUSDMA_TAG_DESTROY; 239 ioc.key = tag->key; 240 if (ioctl(tag->fd, PROTO_IOC_BUSDMA, &ioc) == -1) 241 return (errno); 242 243 if (tag->parent != NULL) 244 tag->parent->refcnt--; 245 else 246 close(tag->fd); 247 obj_free(tag); 248 return (0); 249 } 250 251 static int 252 bd_md_add_seg(struct obj *md, int type, u_long addr, u_long size) 253 { 254 struct obj *seg; 255 256 seg = obj_alloc(OBJ_TYPE_SEG); 257 if (seg == NULL) 258 return (errno); 259 seg->refcnt = 1; 260 seg->parent = md; 261 seg->u.seg.address = addr; 262 seg->u.seg.size = size; 263 264 md->u.md.seg[type] = seg; 265 md->u.md.nsegs[type] = 1; 266 return (0); 267 } 268 269 static int 270 bd_md_del_segs(struct obj *md, int type, int unmap) 271 { 272 struct obj *seg, *seg0; 273 274 for (seg = md->u.md.seg[type]; seg != NULL; seg = seg0) { 275 if (unmap) 276 munmap((void *)seg->u.seg.address, seg->u.seg.size); 277 seg0 = seg->u.seg.next; 278 obj_free(seg); 279 } 280 return (0); 281 } 282 283 int 284 bd_md_create(int tid, u_int flags) 285 { 286 struct proto_ioc_busdma ioc; 287 struct obj *md, *tag; 288 289 tag = obj_lookup(tid, OBJ_TYPE_TAG); 290 if (tag == NULL) 291 return (-1); 292 293 md = obj_alloc(OBJ_TYPE_MD); 294 if (md == NULL) 295 return (-1); 296 297 memset(&ioc, 0, sizeof(ioc)); 298 ioc.request = PROTO_IOC_BUSDMA_MD_CREATE; 299 ioc.u.md.tag = tag->key; 300 ioc.u.md.flags = flags; 301 if (ioctl(tag->fd, PROTO_IOC_BUSDMA, &ioc) == -1) { 302 obj_free(md); 303 return (-1); 304 } 305 306 md->refcnt = 1; 307 md->fd = tag->fd; 308 md->parent = tag; 309 tag->refcnt++; 310 md->key = ioc.result; 311 return (md->oid); 312 } 313 314 int 315 bd_md_destroy(int mdid) 316 { 317 struct proto_ioc_busdma ioc; 318 struct obj *md; 319 320 md = obj_lookup(mdid, OBJ_TYPE_MD); 321 if (md == NULL) 322 return (errno); 323 324 memset(&ioc, 0, sizeof(ioc)); 325 ioc.request = PROTO_IOC_BUSDMA_MD_DESTROY; 326 ioc.key = md->key; 327 if (ioctl(md->fd, PROTO_IOC_BUSDMA, &ioc) == -1) 328 return (errno); 329 330 md->parent->refcnt--; 331 obj_free(md); 332 return (0); 333 } 334 335 int 336 bd_md_load(int mdid, void *buf, u_long len, u_int flags) 337 { 338 struct proto_ioc_busdma ioc; 339 struct obj *md; 340 int error; 341 342 md = obj_lookup(mdid, OBJ_TYPE_MD); 343 if (md == NULL) 344 return (errno); 345 346 memset(&ioc, 0, sizeof(ioc)); 347 ioc.request = PROTO_IOC_BUSDMA_MD_LOAD; 348 ioc.key = md->key; 349 ioc.u.md.flags = flags; 350 ioc.u.md.virt_addr = (uintptr_t)buf; 351 ioc.u.md.virt_size = len; 352 if (ioctl(md->fd, PROTO_IOC_BUSDMA, &ioc) == -1) 353 return (errno); 354 355 error = bd_md_add_seg(md, BUSDMA_MD_VIRT, ioc.u.md.virt_addr, len); 356 error = bd_md_add_seg(md, BUSDMA_MD_PHYS, ioc.u.md.phys_addr, len); 357 error = bd_md_add_seg(md, BUSDMA_MD_BUS, ioc.u.md.bus_addr, len); 358 return (error); 359 } 360 361 int 362 bd_md_unload(int mdid) 363 { 364 struct proto_ioc_busdma ioc; 365 struct obj *md; 366 int error; 367 368 md = obj_lookup(mdid, OBJ_TYPE_MD); 369 if (md == NULL) 370 return (errno); 371 372 memset(&ioc, 0, sizeof(ioc)); 373 ioc.request = PROTO_IOC_BUSDMA_MD_UNLOAD; 374 ioc.key = md->key; 375 if (ioctl(md->fd, PROTO_IOC_BUSDMA, &ioc) == -1) 376 return (errno); 377 378 bd_md_del_segs(md, BUSDMA_MD_VIRT, 0); 379 bd_md_del_segs(md, BUSDMA_MD_PHYS, 0); 380 bd_md_del_segs(md, BUSDMA_MD_BUS, 0); 381 return (0); 382 } 383 384 int 385 bd_mem_alloc(int tid, u_int flags) 386 { 387 struct proto_ioc_busdma ioc; 388 struct obj *md, *tag; 389 uintptr_t addr; 390 int error; 391 392 tag = obj_lookup(tid, OBJ_TYPE_TAG); 393 if (tag == NULL) 394 return (-1); 395 396 md = obj_alloc(OBJ_TYPE_MD); 397 if (md == NULL) 398 return (-1); 399 400 memset(&ioc, 0, sizeof(ioc)); 401 ioc.request = PROTO_IOC_BUSDMA_MEM_ALLOC; 402 ioc.u.md.tag = tag->key; 403 ioc.u.md.flags = flags; 404 if (ioctl(tag->fd, PROTO_IOC_BUSDMA, &ioc) == -1) { 405 obj_free(md); 406 return (-1); 407 } 408 409 md->refcnt = 1; 410 md->fd = tag->fd; 411 md->parent = tag; 412 tag->refcnt++; 413 md->key = ioc.result; 414 415 /* XXX we need to support multiple segments */ 416 assert(ioc.u.md.phys_nsegs == 1); 417 assert(ioc.u.md.bus_nsegs == 1); 418 error = bd_md_add_seg(md, BUSDMA_MD_PHYS, ioc.u.md.phys_addr, 419 tag->u.tag.maxsz); 420 error = bd_md_add_seg(md, BUSDMA_MD_BUS, ioc.u.md.bus_addr, 421 tag->u.tag.maxsz); 422 423 addr = (uintptr_t)mmap(NULL, tag->u.tag.maxsz, PROT_READ | PROT_WRITE, 424 MAP_NOCORE | MAP_SHARED, md->fd, ioc.u.md.phys_addr); 425 if (addr == (uintptr_t)MAP_FAILED) 426 goto fail; 427 error = bd_md_add_seg(md, BUSDMA_MD_VIRT, addr, tag->u.tag.maxsz); 428 429 return (md->oid); 430 431 fail: 432 memset(&ioc, 0, sizeof(ioc)); 433 ioc.request = PROTO_IOC_BUSDMA_MEM_FREE; 434 ioc.key = md->key; 435 ioctl(md->fd, PROTO_IOC_BUSDMA, &ioc); 436 md->parent->refcnt--; 437 obj_free(md); 438 return (-1); 439 } 440 441 int 442 bd_mem_free(int mdid) 443 { 444 struct proto_ioc_busdma ioc; 445 struct obj *md; 446 447 md = obj_lookup(mdid, OBJ_TYPE_MD); 448 if (md == NULL) 449 return (errno); 450 451 memset(&ioc, 0, sizeof(ioc)); 452 ioc.request = PROTO_IOC_BUSDMA_MEM_FREE; 453 ioc.key = md->key; 454 if (ioctl(md->fd, PROTO_IOC_BUSDMA, &ioc) == -1) 455 return (errno); 456 457 bd_md_del_segs(md, BUSDMA_MD_VIRT, 1); 458 bd_md_del_segs(md, BUSDMA_MD_PHYS, 0); 459 bd_md_del_segs(md, BUSDMA_MD_BUS, 0); 460 md->parent->refcnt--; 461 obj_free(md); 462 return (0); 463 } 464 465 int 466 bd_md_first_seg(int mdid, int space) 467 { 468 struct obj *md, *seg; 469 470 md = obj_lookup(mdid, OBJ_TYPE_MD); 471 if (md == NULL) 472 return (-1); 473 474 if (space != BUSDMA_MD_BUS && space != BUSDMA_MD_PHYS && 475 space != BUSDMA_MD_VIRT) { 476 errno = EINVAL; 477 return (-1); 478 } 479 seg = md->u.md.seg[space]; 480 if (seg == NULL) { 481 errno = ENXIO; 482 return (-1); 483 } 484 return (seg->oid); 485 } 486 487 int 488 bd_md_next_seg(int mdid, int sid) 489 { 490 struct obj *seg; 491 492 seg = obj_lookup(sid, OBJ_TYPE_SEG); 493 if (seg == NULL) 494 return (-1); 495 496 seg = seg->u.seg.next; 497 if (seg == NULL) { 498 errno = ENXIO; 499 return (-1); 500 } 501 return (seg->oid); 502 } 503 504 int 505 bd_seg_get_addr(int sid, u_long *addr_p) 506 { 507 struct obj *seg; 508 509 if (addr_p == NULL) 510 return (EINVAL); 511 512 seg = obj_lookup(sid, OBJ_TYPE_SEG); 513 if (seg == NULL) 514 return (errno); 515 516 *addr_p = seg->u.seg.address; 517 return (0); 518 } 519 520 int 521 bd_seg_get_size(int sid, u_long *size_p) 522 { 523 struct obj *seg; 524 525 if (size_p == NULL) 526 return (EINVAL); 527 528 seg = obj_lookup(sid, OBJ_TYPE_SEG); 529 if (seg == NULL) 530 return (errno); 531 532 *size_p = seg->u.seg.size; 533 return (0); 534 } 535 536 int 537 bd_sync(int mdid, u_int op, u_long ofs, u_long len) 538 { 539 struct proto_ioc_busdma ioc; 540 struct obj *md; 541 542 md = obj_lookup(mdid, OBJ_TYPE_MD); 543 if (md == NULL) 544 return (errno); 545 546 memset(&ioc, 0, sizeof(ioc)); 547 ioc.request = PROTO_IOC_BUSDMA_SYNC; 548 ioc.key = md->key; 549 ioc.u.sync.op = op; 550 ioc.u.sync.base = ofs; 551 ioc.u.sync.size = len; 552 if (ioctl(md->fd, PROTO_IOC_BUSDMA, &ioc) == -1) 553 return (errno); 554 555 return (0); 556 } 557