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 *
obj_alloc(u_int type)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
obj_free(struct obj * obj)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 *
obj_lookup(int oid,u_int type)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 *
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)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
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)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
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)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
bd_tag_destroy(int tid)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
bd_md_add_seg(struct obj * md,int type,u_long addr,u_long size)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
bd_md_del_segs(struct obj * md,int type,int unmap)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
bd_md_create(int tid,u_int flags)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
bd_md_destroy(int mdid)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
bd_md_load(int mdid,void * buf,u_long len,u_int flags)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
bd_md_unload(int mdid)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
bd_mem_alloc(int tid,u_int flags)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
bd_mem_free(int mdid)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
bd_md_first_seg(int mdid,int space)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
bd_md_next_seg(int mdid,int sid)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
bd_seg_get_addr(int sid,u_long * addr_p)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
bd_seg_get_size(int sid,u_long * size_p)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
bd_sync(int mdid,u_int op,u_long ofs,u_long len)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