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