xref: /freebsd/tools/bus_space/busdma.c (revision 5f0216bd883edee71bf81051e3c20505e4820903)
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 <errno.h>
33 #include <fcntl.h>
34 #include <limits.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 	u_int	refcnt;
50 	int	fd;
51 	struct obj *parent;
52 	u_long	key;
53 	union {
54 		struct {
55 			unsigned long	align;
56 			unsigned long	bndry;
57 			unsigned long	maxaddr;
58 			unsigned long	maxsz;
59 			unsigned long	maxsegsz;
60 			unsigned long	nsegs;
61 			unsigned long	datarate;
62 		} tag;
63 		struct {
64 			unsigned long	physaddr;
65 			void		*virtaddr;
66 		} mem;
67 	} u;
68 };
69 
70 static struct obj **oidtbl = NULL;
71 static int noids = 0;
72 
73 static struct obj *
74 obj_alloc(u_int type)
75 {
76 	struct obj **newtbl, *obj;
77 	int oid;
78 
79 	obj = malloc(sizeof(struct obj));
80 	obj->type = type;
81 	obj->refcnt = 0;
82 
83 	for (oid = 0; oid < noids; oid++) {
84 		if (oidtbl[oid] == 0)
85 			break;
86 	}
87 	if (oid == noids) {
88 		newtbl = realloc(oidtbl, sizeof(struct obj *) * (noids + 1));
89 		if (newtbl == NULL) {
90 			free(obj);
91 			return (NULL);
92 		}
93 		oidtbl = newtbl;
94 		noids++;
95 	}
96 	oidtbl[oid] = obj;
97 	obj->oid = oid;
98 	return (obj);
99 }
100 
101 static int
102 obj_free(struct obj *obj)
103 {
104 
105 	oidtbl[obj->oid] = NULL;
106 	free(obj);
107 	return (0);
108 }
109 
110 static struct obj *
111 obj_lookup(int oid, u_int type)
112 {
113 	struct obj *obj;
114 
115 	if (oid < 0 || oid >= noids) {
116 		errno = EINVAL;
117 		return (NULL);
118 	}
119 	obj = oidtbl[oid];
120 	if (obj->refcnt == 0) {
121 		errno = ENXIO;
122 		return (NULL);
123 	}
124 	if (type != OBJ_TYPE_NONE && obj->type != type) {
125 		errno = ENODEV;
126 		return (NULL);
127 	}
128 	return (obj);
129 }
130 
131 struct obj *
132 bd_tag_new(struct obj *ptag, int fd, u_long align, u_long bndry,
133     u_long maxaddr, u_long maxsz, u_int nsegs, u_long maxsegsz,
134     u_int datarate, u_int flags)
135 {
136 	struct proto_ioc_busdma ioc;
137 	struct obj *tag;
138 
139 	tag = obj_alloc(OBJ_TYPE_TAG);
140 	if (tag == NULL)
141 		return (NULL);
142 
143 	memset(&ioc, 0, sizeof(ioc));
144 	ioc.request = (ptag != NULL) ? PROTO_IOC_BUSDMA_TAG_DERIVE :
145 	    PROTO_IOC_BUSDMA_TAG_CREATE;
146 	ioc.key = (ptag != NULL) ? ptag->key : 0;
147 	ioc.u.tag.align = align;
148 	ioc.u.tag.bndry = bndry;
149 	ioc.u.tag.maxaddr = maxaddr;
150 	ioc.u.tag.maxsz = maxsz;
151 	ioc.u.tag.nsegs = nsegs;
152 	ioc.u.tag.maxsegsz = maxsegsz;
153 	ioc.u.tag.datarate = datarate;
154 	ioc.u.tag.flags = flags;
155 	if (ioctl(fd, PROTO_IOC_BUSDMA, &ioc) == -1) {
156 		obj_free(tag);
157 		return (NULL);
158 	}
159 	tag->refcnt = 1;
160 	tag->fd = fd;
161 	tag->parent = ptag;
162 	tag->key = ioc.result;
163 	tag->u.tag.align = ioc.u.tag.align;
164 	tag->u.tag.bndry = ioc.u.tag.bndry;
165 	tag->u.tag.maxaddr = ioc.u.tag.maxaddr;
166 	tag->u.tag.maxsz = ioc.u.tag.maxsz;
167 	tag->u.tag.maxsegsz = ioc.u.tag.maxsegsz;
168 	tag->u.tag.nsegs = ioc.u.tag.nsegs;
169 	tag->u.tag.datarate = ioc.u.tag.datarate;
170 	return (tag);
171 }
172 
173 int
174 bd_tag_create(const char *dev, u_long align, u_long bndry, u_long maxaddr,
175     u_long maxsz, u_int nsegs, u_long maxsegsz, u_int datarate, u_int flags)
176 {
177 	struct obj *tag;
178 	int fd;
179 
180 	fd = open(dev, O_RDWR);
181 	if (fd == -1)
182 		return (-1);
183 
184 	tag = bd_tag_new(NULL, fd, align, bndry, maxaddr, maxsz, nsegs,
185 	    maxsegsz, datarate, flags);
186 	if (tag == NULL) {
187 		close(fd);
188 		return (-1);
189 	}
190 	return (tag->oid);
191 }
192 
193 int
194 bd_tag_derive(int ptid, u_long align, u_long bndry, u_long maxaddr,
195     u_long maxsz, u_int nsegs, u_long maxsegsz, u_int datarate, u_int flags)
196 {
197 	struct obj *ptag, *tag;
198 
199 	ptag = obj_lookup(ptid, OBJ_TYPE_TAG);
200 	if (ptag == NULL)
201 		return (-1);
202 
203 	tag = bd_tag_new(ptag, ptag->fd, align, bndry, maxaddr, maxsz, nsegs,
204 	    maxsegsz, datarate, flags);
205 	if (tag == NULL)
206 		return (-1);
207 	ptag->refcnt++;
208 	return (tag->oid);
209 }
210 
211 int
212 bd_tag_destroy(int tid)
213 {
214 	struct proto_ioc_busdma ioc;
215 	struct obj *ptag, *tag;
216 
217 	tag = obj_lookup(tid, OBJ_TYPE_TAG);
218 	if (tag == NULL)
219 		return (errno);
220 	if (tag->refcnt > 1)
221 		return (EBUSY);
222 
223 	memset(&ioc, 0, sizeof(ioc));
224 	ioc.request = PROTO_IOC_BUSDMA_TAG_DESTROY;
225 	ioc.key = tag->key;
226 	if (ioctl(tag->fd, PROTO_IOC_BUSDMA, &ioc) == -1)
227 		return (errno);
228 
229 	if (tag->parent != NULL)
230 		tag->parent->refcnt--;
231 	else
232 		close(tag->fd);
233 	obj_free(tag);
234 	return (0);
235 }
236 
237 int
238 bd_mem_alloc(int tid, u_int flags)
239 {
240 	struct proto_ioc_busdma ioc;
241 	struct obj *md, *tag;
242 
243 	tag = obj_lookup(tid, OBJ_TYPE_TAG);
244 	if (tag == NULL)
245 		return (-1);
246 
247 	md = obj_alloc(OBJ_TYPE_MD);
248 	if (md == NULL)
249 		return (-1);
250 
251 	memset(&ioc, 0, sizeof(ioc));
252 	ioc.request = PROTO_IOC_BUSDMA_MEM_ALLOC;
253 	ioc.u.mem.tag = tag->key;
254 	ioc.u.mem.flags = flags;
255 	if (ioctl(tag->fd, PROTO_IOC_BUSDMA, &ioc) == -1) {
256 		obj_free(md);
257 		return (-1);
258 	}
259 
260 	md->refcnt = 1;
261 	md->fd = tag->fd;
262 	md->parent = tag;
263 	tag->refcnt++;
264 	md->key = ioc.result;
265 	md->u.mem.physaddr = ioc.u.mem.physaddr;
266 	md->u.mem.virtaddr = mmap(NULL, tag->u.tag.maxsz,
267 	    PROT_READ | PROT_WRITE, MAP_NOCORE | MAP_SHARED, md->fd,
268 	    md->u.mem.physaddr);
269 	return (md->oid);
270 }
271 
272 int
273 bd_mem_free(int mdid)
274 {
275 	struct proto_ioc_busdma ioc;
276 	struct obj *md;
277 
278 	md = obj_lookup(mdid, OBJ_TYPE_MD);
279 	if (md == NULL)
280 		return (errno);
281 
282 	if (md->u.mem.virtaddr != MAP_FAILED)
283 		munmap(md->u.mem.virtaddr, md->parent->u.tag.maxsz);
284 	memset(&ioc, 0, sizeof(ioc));
285 	ioc.request = PROTO_IOC_BUSDMA_MEM_FREE;
286 	ioc.key = md->key;
287 	if (ioctl(md->fd, PROTO_IOC_BUSDMA, &ioc) == -1)
288 		return (errno);
289 
290 	md->parent->refcnt--;
291 	obj_free(md);
292 	return (0);
293 }
294