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 *
obj_alloc(u_int type)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
obj_free(struct obj * obj)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 *
obj_lookup(int oid,u_int type)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 *
bd_tag_new(struct obj * ptag,int fd,u_long align,u_long bndry,u_long maxaddr,u_long maxsz,u_int nsegs,u_long maxsegsz,u_int datarate,u_int flags)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
bd_tag_create(const char * dev,u_long align,u_long bndry,u_long maxaddr,u_long maxsz,u_int nsegs,u_long maxsegsz,u_int datarate,u_int flags)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
bd_tag_derive(int ptid,u_long align,u_long bndry,u_long maxaddr,u_long maxsz,u_int nsegs,u_long maxsegsz,u_int datarate,u_int flags)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
bd_tag_destroy(int tid)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
bd_md_add_seg(struct obj * md,int type,u_long addr,u_long size)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
bd_md_del_segs(struct obj * md,int type,int unmap)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
bd_md_create(int tid,u_int flags)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
bd_md_destroy(int mdid)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
bd_md_load(int mdid,void * buf,u_long len,u_int flags)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
bd_md_unload(int mdid)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
bd_mem_alloc(int tid,u_int flags)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
bd_mem_free(int mdid)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
bd_md_first_seg(int mdid,int space)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
bd_md_next_seg(int mdid,int sid)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
bd_seg_get_addr(int sid,u_long * addr_p)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
bd_seg_get_size(int sid,u_long * size_p)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
bd_sync(int mdid,u_int op,u_long ofs,u_long len)539 bd_sync(int mdid, u_int op, u_long ofs, u_long len)
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 = ofs;
553 ioc.u.sync.size = len;
554 if (ioctl(md->fd, PROTO_IOC_BUSDMA, &ioc) == -1)
555 return (errno);
556
557 return (0);
558 }
559