Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/tools/bus_space/busdma.c
39475 views
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 *
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
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 *
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 *
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
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
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
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
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
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
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
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
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
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
/* 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
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
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
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
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
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
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
558