]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - tools/bus_space/busdma.c
Merge ACPICA 20150619.
[FreeBSD/FreeBSD.git] / tools / bus_space / busdma.c
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 }