xref: /freebsd/tools/bus_space/busdma.c (revision 119b75925c562202145d7bac7b676b98029c6cb9)
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