xref: /freebsd/tools/bus_space/busdma.c (revision b78ee15e9f04ae15c3e1200df974473167524d17)
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 <stdlib.h>
37 #include <string.h>
38 #include <unistd.h>
39 
40 #include "busdma.h"
41 
42 #include "../../sys/dev/proto/proto_dev.h"
43 
44 struct obj {
45 	int	oid;
46 	u_int	type;
47 #define	OBJ_TYPE_NONE	0
48 #define	OBJ_TYPE_TAG	1
49 #define	OBJ_TYPE_MD	2
50 #define	OBJ_TYPE_SEG	3
51 	u_int	refcnt;
52 	int	fd;
53 	struct obj *parent;
54 	u_long	key;
55 	union {
56 		struct {
57 			unsigned long	align;
58 			unsigned long	bndry;
59 			unsigned long	maxaddr;
60 			unsigned long	maxsz;
61 			unsigned long	maxsegsz;
62 			unsigned long	nsegs;
63 			unsigned long	datarate;
64 		} tag;
65 		struct {
66 			struct obj	*seg[3];
67 			int		nsegs[3];
68 #define	BUSDMA_MD_BUS	0
69 #define	BUSDMA_MD_PHYS	1
70 #define	BUSDMA_MD_VIRT	2
71 		} md;
72 		struct {
73 			struct obj	*next;
74 			unsigned long	address;
75 			unsigned long	size;
76 		} seg;
77 	} u;
78 };
79 
80 static struct obj **oidtbl = NULL;
81 static int noids = 0;
82 
83 static struct obj *
84 obj_alloc(u_int type)
85 {
86 	struct obj **newtbl, *obj;
87 	int oid;
88 
89 	obj = calloc(1, sizeof(struct obj));
90 	obj->type = type;
91 
92 	for (oid = 0; oid < noids; oid++) {
93 		if (oidtbl[oid] == 0)
94 			break;
95 	}
96 	if (oid == noids) {
97 		newtbl = realloc(oidtbl, sizeof(struct obj *) * (noids + 1));
98 		if (newtbl == NULL) {
99 			free(obj);
100 			return (NULL);
101 		}
102 		oidtbl = newtbl;
103 		noids++;
104 	}
105 	oidtbl[oid] = obj;
106 	obj->oid = oid;
107 	return (obj);
108 }
109 
110 static int
111 obj_free(struct obj *obj)
112 {
113 
114 	oidtbl[obj->oid] = NULL;
115 	free(obj);
116 	return (0);
117 }
118 
119 static struct obj *
120 obj_lookup(int oid, u_int type)
121 {
122 	struct obj *obj;
123 
124 	if (oid < 0 || oid >= noids) {
125 		errno = EINVAL;
126 		return (NULL);
127 	}
128 	obj = oidtbl[oid];
129 	if (obj->refcnt == 0) {
130 		errno = ENXIO;
131 		return (NULL);
132 	}
133 	if (type != OBJ_TYPE_NONE && obj->type != type) {
134 		errno = ENODEV;
135 		return (NULL);
136 	}
137 	return (obj);
138 }
139 
140 struct obj *
141 bd_tag_new(struct obj *ptag, int fd, u_long align, u_long bndry,
142     u_long maxaddr, u_long maxsz, u_int nsegs, u_long maxsegsz,
143     u_int datarate, u_int flags)
144 {
145 	struct proto_ioc_busdma ioc;
146 	struct obj *tag;
147 
148 	tag = obj_alloc(OBJ_TYPE_TAG);
149 	if (tag == NULL)
150 		return (NULL);
151 
152 	memset(&ioc, 0, sizeof(ioc));
153 	ioc.request = (ptag != NULL) ? PROTO_IOC_BUSDMA_TAG_DERIVE :
154 	    PROTO_IOC_BUSDMA_TAG_CREATE;
155 	ioc.key = (ptag != NULL) ? ptag->key : 0;
156 	ioc.u.tag.align = align;
157 	ioc.u.tag.bndry = bndry;
158 	ioc.u.tag.maxaddr = maxaddr;
159 	ioc.u.tag.maxsz = maxsz;
160 	ioc.u.tag.nsegs = nsegs;
161 	ioc.u.tag.maxsegsz = maxsegsz;
162 	ioc.u.tag.datarate = datarate;
163 	ioc.u.tag.flags = flags;
164 	if (ioctl(fd, PROTO_IOC_BUSDMA, &ioc) == -1) {
165 		obj_free(tag);
166 		return (NULL);
167 	}
168 	tag->refcnt = 1;
169 	tag->fd = fd;
170 	tag->parent = ptag;
171 	tag->key = ioc.result;
172 	tag->u.tag.align = ioc.u.tag.align;
173 	tag->u.tag.bndry = ioc.u.tag.bndry;
174 	tag->u.tag.maxaddr = ioc.u.tag.maxaddr;
175 	tag->u.tag.maxsz = ioc.u.tag.maxsz;
176 	tag->u.tag.maxsegsz = ioc.u.tag.maxsegsz;
177 	tag->u.tag.nsegs = ioc.u.tag.nsegs;
178 	tag->u.tag.datarate = ioc.u.tag.datarate;
179 	return (tag);
180 }
181 
182 int
183 bd_tag_create(const char *dev, u_long align, u_long bndry, u_long maxaddr,
184     u_long maxsz, u_int nsegs, u_long maxsegsz, u_int datarate, u_int flags)
185 {
186 	struct obj *tag;
187 	int fd;
188 
189 	fd = open(dev, O_RDWR);
190 	if (fd == -1)
191 		return (-1);
192 
193 	tag = bd_tag_new(NULL, fd, align, bndry, maxaddr, maxsz, nsegs,
194 	    maxsegsz, datarate, flags);
195 	if (tag == NULL) {
196 		close(fd);
197 		return (-1);
198 	}
199 	return (tag->oid);
200 }
201 
202 int
203 bd_tag_derive(int ptid, u_long align, u_long bndry, u_long maxaddr,
204     u_long maxsz, u_int nsegs, u_long maxsegsz, u_int datarate, u_int flags)
205 {
206 	struct obj *ptag, *tag;
207 
208 	ptag = obj_lookup(ptid, OBJ_TYPE_TAG);
209 	if (ptag == NULL)
210 		return (-1);
211 
212 	tag = bd_tag_new(ptag, ptag->fd, align, bndry, maxaddr, maxsz, nsegs,
213 	    maxsegsz, datarate, flags);
214 	if (tag == NULL)
215 		return (-1);
216 	ptag->refcnt++;
217 	return (tag->oid);
218 }
219 
220 int
221 bd_tag_destroy(int tid)
222 {
223 	struct proto_ioc_busdma ioc;
224 	struct obj *ptag, *tag;
225 
226 	tag = obj_lookup(tid, OBJ_TYPE_TAG);
227 	if (tag == NULL)
228 		return (errno);
229 	if (tag->refcnt > 1)
230 		return (EBUSY);
231 
232 	memset(&ioc, 0, sizeof(ioc));
233 	ioc.request = PROTO_IOC_BUSDMA_TAG_DESTROY;
234 	ioc.key = tag->key;
235 	if (ioctl(tag->fd, PROTO_IOC_BUSDMA, &ioc) == -1)
236 		return (errno);
237 
238 	if (tag->parent != NULL)
239 		tag->parent->refcnt--;
240 	else
241 		close(tag->fd);
242 	obj_free(tag);
243 	return (0);
244 }
245 
246 int
247 bd_mem_alloc(int tid, u_int flags)
248 {
249 	struct proto_ioc_busdma ioc;
250 	struct obj *md, *tag;
251 	struct obj *bseg, *pseg, *vseg;
252 
253 	tag = obj_lookup(tid, OBJ_TYPE_TAG);
254 	if (tag == NULL)
255 		return (-1);
256 
257 	md = obj_alloc(OBJ_TYPE_MD);
258 	if (md == NULL)
259 		return (-1);
260 
261 	memset(&ioc, 0, sizeof(ioc));
262 	ioc.request = PROTO_IOC_BUSDMA_MEM_ALLOC;
263 	ioc.u.mem.tag = tag->key;
264 	ioc.u.mem.flags = flags;
265 	if (ioctl(tag->fd, PROTO_IOC_BUSDMA, &ioc) == -1) {
266 		obj_free(md);
267 		return (-1);
268 	}
269 
270 	md->refcnt = 1;
271 	md->fd = tag->fd;
272 	md->parent = tag;
273 	tag->refcnt++;
274 	md->key = ioc.result;
275 
276 	/* XXX we need to support multiple segments */
277 	assert(ioc.u.mem.phys_nsegs == 1);
278 	assert(ioc.u.mem.bus_nsegs == 1);
279 
280 	bseg = pseg = vseg = NULL;
281 
282 	bseg = obj_alloc(OBJ_TYPE_SEG);
283 	if (bseg == NULL)
284 		goto fail;
285 	bseg->refcnt = 1;
286 	bseg->parent = md;
287 	bseg->u.seg.address = ioc.u.mem.bus_addr;
288 	bseg->u.seg.size = tag->u.tag.maxsz;
289 	md->u.md.seg[BUSDMA_MD_BUS] = bseg;
290 	md->u.md.nsegs[BUSDMA_MD_BUS] = ioc.u.mem.bus_nsegs;
291 
292 	pseg = obj_alloc(OBJ_TYPE_SEG);
293 	if (pseg == NULL)
294 		goto fail;
295 	pseg->refcnt = 1;
296 	pseg->parent = md;
297 	pseg->u.seg.address = ioc.u.mem.phys_addr;
298 	pseg->u.seg.size = tag->u.tag.maxsz;
299 	md->u.md.seg[BUSDMA_MD_PHYS] = pseg;
300 	md->u.md.nsegs[BUSDMA_MD_PHYS] = ioc.u.mem.phys_nsegs;
301 
302 	vseg = obj_alloc(OBJ_TYPE_SEG);
303 	if (vseg == NULL)
304 		goto fail;
305 	vseg->refcnt = 1;
306 	vseg->parent = md;
307 	vseg->u.seg.address = (uintptr_t)mmap(NULL, pseg->u.seg.size,
308 	    PROT_READ | PROT_WRITE, MAP_NOCORE | MAP_SHARED, md->fd,
309 	    pseg->u.seg.address);
310 	if (vseg->u.seg.address == (uintptr_t)MAP_FAILED)
311 		goto fail;
312 	vseg->u.seg.size = pseg->u.seg.size;
313 	md->u.md.seg[BUSDMA_MD_VIRT] = vseg;
314 	md->u.md.nsegs[BUSDMA_MD_VIRT] = 1;
315 
316 	return (md->oid);
317 
318  fail:
319 	if (vseg != NULL)
320 		obj_free(vseg);
321 	if (pseg != NULL)
322 		obj_free(pseg);
323 	if (bseg != NULL)
324 		obj_free(bseg);
325 	memset(&ioc, 0, sizeof(ioc));
326 	ioc.request = PROTO_IOC_BUSDMA_MEM_FREE;
327 	ioc.key = md->key;
328 	ioctl(md->fd, PROTO_IOC_BUSDMA, &ioc);
329 	md->parent->refcnt--;
330 	obj_free(md);
331 	return (-1);
332 }
333 
334 int
335 bd_mem_free(int mdid)
336 {
337 	struct proto_ioc_busdma ioc;
338 	struct obj *md, *seg, *seg0;
339 
340 	md = obj_lookup(mdid, OBJ_TYPE_MD);
341 	if (md == NULL)
342 		return (errno);
343 
344 	for (seg = md->u.md.seg[BUSDMA_MD_VIRT]; seg != NULL; seg = seg0) {
345 		munmap((void *)seg->u.seg.address, seg->u.seg.size);
346 		seg0 = seg->u.seg.next;
347 		obj_free(seg);
348 	}
349 	for (seg = md->u.md.seg[BUSDMA_MD_PHYS]; seg != NULL; seg = seg0) {
350 		seg0 = seg->u.seg.next;
351 		obj_free(seg);
352 	}
353 	for (seg = md->u.md.seg[BUSDMA_MD_BUS]; seg != NULL; seg = seg0) {
354 		seg0 = seg->u.seg.next;
355 		obj_free(seg);
356 	}
357 	memset(&ioc, 0, sizeof(ioc));
358 	ioc.request = PROTO_IOC_BUSDMA_MEM_FREE;
359 	ioc.key = md->key;
360 	if (ioctl(md->fd, PROTO_IOC_BUSDMA, &ioc) == -1)
361 		return (errno);
362 
363 	md->parent->refcnt--;
364 	obj_free(md);
365 	return (0);
366 }
367 
368 int
369 bd_md_first_seg(int mdid, int space)
370 {
371 	struct obj *md, *seg;
372 
373 	md = obj_lookup(mdid, OBJ_TYPE_MD);
374 	if (md == NULL)
375 		return (-1);
376 
377 	if (space != BUSDMA_MD_BUS && space != BUSDMA_MD_PHYS &&
378 	    space != BUSDMA_MD_VIRT) {
379 		errno = EINVAL;
380 		return (-1);
381 	}
382 	seg = md->u.md.seg[space];
383 	if (seg == NULL) {
384 		errno = ENXIO;
385 		return (-1);
386 	}
387 	return (seg->oid);
388 }
389 
390 int
391 bd_md_next_seg(int mdid, int sid)
392 {
393 	struct obj *seg;
394 
395 	seg = obj_lookup(sid, OBJ_TYPE_SEG);
396 	if (seg == NULL)
397 		return (-1);
398 
399 	seg = seg->u.seg.next;
400 	if (seg == NULL) {
401 		errno = ENXIO;
402 		return (-1);
403 	}
404 	return (seg->oid);
405 }
406 
407 int
408 bd_seg_get_addr(int sid, u_long *addr_p)
409 {
410 	struct obj *seg;
411 
412 	if (addr_p == NULL)
413 		return (EINVAL);
414 
415 	seg = obj_lookup(sid, OBJ_TYPE_SEG);
416 	if (seg == NULL)
417 		return (errno);
418 
419 	*addr_p = seg->u.seg.address;
420 	return (0);
421 }
422 
423 int
424 bd_seg_get_size(int sid, u_long *size_p)
425 {
426 	struct obj *seg;
427 
428 	if (size_p == NULL)
429 		return (EINVAL);
430 
431 	seg = obj_lookup(sid, OBJ_TYPE_SEG);
432 	if (seg == NULL)
433 		return (errno);
434 
435 	*size_p = seg->u.seg.size;
436 	return (0);
437 }
438