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 struct obj *tag; 188 int fd; 189 190 fd = open(dev, O_RDWR); 191 if (fd == -1) 192 return (-1); 193 194 tag = bd_tag_new(NULL, fd, align, bndry, maxaddr, maxsz, nsegs, 195 maxsegsz, datarate, flags); 196 if (tag == NULL) { 197 close(fd); 198 return (-1); 199 } 200 return (tag->oid); 201 } 202 203 int 204 bd_tag_derive(int ptid, u_long align, u_long bndry, u_long maxaddr, 205 u_long maxsz, u_int nsegs, u_long maxsegsz, u_int datarate, u_int flags) 206 { 207 struct obj *ptag, *tag; 208 209 ptag = obj_lookup(ptid, OBJ_TYPE_TAG); 210 if (ptag == NULL) 211 return (-1); 212 213 tag = bd_tag_new(ptag, ptag->fd, align, bndry, maxaddr, maxsz, nsegs, 214 maxsegsz, datarate, flags); 215 if (tag == NULL) 216 return (-1); 217 ptag->refcnt++; 218 return (tag->oid); 219 } 220 221 int 222 bd_tag_destroy(int tid) 223 { 224 struct proto_ioc_busdma ioc; 225 struct obj *ptag, *tag; 226 227 tag = obj_lookup(tid, OBJ_TYPE_TAG); 228 if (tag == NULL) 229 return (errno); 230 if (tag->refcnt > 1) 231 return (EBUSY); 232 233 memset(&ioc, 0, sizeof(ioc)); 234 ioc.request = PROTO_IOC_BUSDMA_TAG_DESTROY; 235 ioc.key = tag->key; 236 if (ioctl(tag->fd, PROTO_IOC_BUSDMA, &ioc) == -1) 237 return (errno); 238 239 if (tag->parent != NULL) 240 tag->parent->refcnt--; 241 else 242 close(tag->fd); 243 obj_free(tag); 244 return (0); 245 } 246 247 static int 248 bd_md_add_seg(struct obj *md, int type, u_long addr, u_long size) 249 { 250 struct obj *seg; 251 252 seg = obj_alloc(OBJ_TYPE_SEG); 253 if (seg == NULL) 254 return (errno); 255 seg->refcnt = 1; 256 seg->parent = md; 257 seg->u.seg.address = addr; 258 seg->u.seg.size = size; 259 260 md->u.md.seg[type] = seg; 261 md->u.md.nsegs[type] = 1; 262 return (0); 263 } 264 265 static int 266 bd_md_del_segs(struct obj *md, int type, int unmap) 267 { 268 struct obj *seg, *seg0; 269 270 for (seg = md->u.md.seg[type]; seg != NULL; seg = seg0) { 271 if (unmap) 272 munmap((void *)seg->u.seg.address, seg->u.seg.size); 273 seg0 = seg->u.seg.next; 274 obj_free(seg); 275 } 276 return (0); 277 } 278 279 int 280 bd_md_create(int tid, u_int flags) 281 { 282 struct proto_ioc_busdma ioc; 283 struct obj *md, *tag; 284 285 tag = obj_lookup(tid, OBJ_TYPE_TAG); 286 if (tag == NULL) 287 return (-1); 288 289 md = obj_alloc(OBJ_TYPE_MD); 290 if (md == NULL) 291 return (-1); 292 293 memset(&ioc, 0, sizeof(ioc)); 294 ioc.request = PROTO_IOC_BUSDMA_MD_CREATE; 295 ioc.u.md.tag = tag->key; 296 ioc.u.md.flags = flags; 297 if (ioctl(tag->fd, PROTO_IOC_BUSDMA, &ioc) == -1) { 298 obj_free(md); 299 return (-1); 300 } 301 302 md->refcnt = 1; 303 md->fd = tag->fd; 304 md->parent = tag; 305 tag->refcnt++; 306 md->key = ioc.result; 307 return (md->oid); 308 } 309 310 int 311 bd_md_destroy(int mdid) 312 { 313 struct proto_ioc_busdma ioc; 314 struct obj *md; 315 316 md = obj_lookup(mdid, OBJ_TYPE_MD); 317 if (md == NULL) 318 return (errno); 319 320 memset(&ioc, 0, sizeof(ioc)); 321 ioc.request = PROTO_IOC_BUSDMA_MD_DESTROY; 322 ioc.key = md->key; 323 if (ioctl(md->fd, PROTO_IOC_BUSDMA, &ioc) == -1) 324 return (errno); 325 326 md->parent->refcnt--; 327 obj_free(md); 328 return (0); 329 } 330 331 int 332 bd_md_load(int mdid, void *buf, u_long len, u_int flags) 333 { 334 struct proto_ioc_busdma ioc; 335 struct obj *md; 336 int error; 337 338 md = obj_lookup(mdid, OBJ_TYPE_MD); 339 if (md == NULL) 340 return (errno); 341 342 memset(&ioc, 0, sizeof(ioc)); 343 ioc.request = PROTO_IOC_BUSDMA_MD_LOAD; 344 ioc.key = md->key; 345 ioc.u.md.flags = flags; 346 ioc.u.md.virt_addr = (uintptr_t)buf; 347 ioc.u.md.virt_size = len; 348 if (ioctl(md->fd, PROTO_IOC_BUSDMA, &ioc) == -1) 349 return (errno); 350 351 printf("XXX: %s: phys(%d, %#lx), bus(%d, %#lx)\n", __func__, 352 ioc.u.md.phys_nsegs, ioc.u.md.phys_addr, 353 ioc.u.md.bus_nsegs, ioc.u.md.bus_addr); 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 printf("XXX: %s: phys(%d, %#lx), bus(%d, %#lx)\n", __func__, 416 ioc.u.md.phys_nsegs, ioc.u.md.phys_addr, 417 ioc.u.md.bus_nsegs, ioc.u.md.bus_addr); 418 419 /* XXX we need to support multiple segments */ 420 assert(ioc.u.md.phys_nsegs == 1); 421 assert(ioc.u.md.bus_nsegs == 1); 422 error = bd_md_add_seg(md, BUSDMA_MD_PHYS, ioc.u.md.phys_addr, 423 tag->u.tag.maxsz); 424 error = bd_md_add_seg(md, BUSDMA_MD_BUS, ioc.u.md.bus_addr, 425 tag->u.tag.maxsz); 426 427 addr = (uintptr_t)mmap(NULL, tag->u.tag.maxsz, PROT_READ | PROT_WRITE, 428 MAP_NOCORE | MAP_SHARED, md->fd, ioc.u.md.phys_addr); 429 if (addr == (uintptr_t)MAP_FAILED) 430 goto fail; 431 error = bd_md_add_seg(md, BUSDMA_MD_VIRT, addr, tag->u.tag.maxsz); 432 433 return (md->oid); 434 435 fail: 436 memset(&ioc, 0, sizeof(ioc)); 437 ioc.request = PROTO_IOC_BUSDMA_MEM_FREE; 438 ioc.key = md->key; 439 ioctl(md->fd, PROTO_IOC_BUSDMA, &ioc); 440 md->parent->refcnt--; 441 obj_free(md); 442 return (-1); 443 } 444 445 int 446 bd_mem_free(int mdid) 447 { 448 struct proto_ioc_busdma ioc; 449 struct obj *md; 450 451 md = obj_lookup(mdid, OBJ_TYPE_MD); 452 if (md == NULL) 453 return (errno); 454 455 memset(&ioc, 0, sizeof(ioc)); 456 ioc.request = PROTO_IOC_BUSDMA_MEM_FREE; 457 ioc.key = md->key; 458 if (ioctl(md->fd, PROTO_IOC_BUSDMA, &ioc) == -1) 459 return (errno); 460 461 bd_md_del_segs(md, BUSDMA_MD_VIRT, 1); 462 bd_md_del_segs(md, BUSDMA_MD_PHYS, 0); 463 bd_md_del_segs(md, BUSDMA_MD_BUS, 0); 464 md->parent->refcnt--; 465 obj_free(md); 466 return (0); 467 } 468 469 int 470 bd_md_first_seg(int mdid, int space) 471 { 472 struct obj *md, *seg; 473 474 md = obj_lookup(mdid, OBJ_TYPE_MD); 475 if (md == NULL) 476 return (-1); 477 478 if (space != BUSDMA_MD_BUS && space != BUSDMA_MD_PHYS && 479 space != BUSDMA_MD_VIRT) { 480 errno = EINVAL; 481 return (-1); 482 } 483 seg = md->u.md.seg[space]; 484 if (seg == NULL) { 485 errno = ENXIO; 486 return (-1); 487 } 488 return (seg->oid); 489 } 490 491 int 492 bd_md_next_seg(int mdid, int sid) 493 { 494 struct obj *seg; 495 496 seg = obj_lookup(sid, OBJ_TYPE_SEG); 497 if (seg == NULL) 498 return (-1); 499 500 seg = seg->u.seg.next; 501 if (seg == NULL) { 502 errno = ENXIO; 503 return (-1); 504 } 505 return (seg->oid); 506 } 507 508 int 509 bd_seg_get_addr(int sid, u_long *addr_p) 510 { 511 struct obj *seg; 512 513 if (addr_p == NULL) 514 return (EINVAL); 515 516 seg = obj_lookup(sid, OBJ_TYPE_SEG); 517 if (seg == NULL) 518 return (errno); 519 520 *addr_p = seg->u.seg.address; 521 return (0); 522 } 523 524 int 525 bd_seg_get_size(int sid, u_long *size_p) 526 { 527 struct obj *seg; 528 529 if (size_p == NULL) 530 return (EINVAL); 531 532 seg = obj_lookup(sid, OBJ_TYPE_SEG); 533 if (seg == NULL) 534 return (errno); 535 536 *size_p = seg->u.seg.size; 537 return (0); 538 } 539 540 int 541 bd_sync(int mdid, u_int op, u_long base, u_long size) 542 { 543 struct proto_ioc_busdma ioc; 544 struct obj *md; 545 546 md = obj_lookup(mdid, OBJ_TYPE_MD); 547 if (md == NULL) 548 return (errno); 549 550 memset(&ioc, 0, sizeof(ioc)); 551 ioc.request = PROTO_IOC_BUSDMA_SYNC; 552 ioc.key = md->key; 553 ioc.u.sync.op = op; 554 ioc.u.sync.base = base; 555 ioc.u.sync.size = size; 556 if (ioctl(md->fd, PROTO_IOC_BUSDMA, &ioc) == -1) 557 return (errno); 558 559 return (0); 560 } 561