xref: /freebsd/tools/bus_space/bus.c (revision 783d3ff6d7fae619db8a7990b8a6387de0c677b5)
1 /*-
2  * Copyright (c) 2014 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 <errno.h>
31 #include <fcntl.h>
32 #include <limits.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <unistd.h>
36 
37 #include "bus.h"
38 
39 #include "../../sys/dev/proto/proto_dev.h"
40 
41 struct resource {
42 	int	rid;
43 	int	fd;
44 	long	addr;
45 	long	size;
46 	off_t	ofs;
47 	caddr_t	ptr;
48 };
49 
50 static struct resource *ridtbl = NULL;
51 static int nrids = 0;
52 
53 static int
54 rid_alloc(void)
55 {
56 	void *newtbl;
57 	int rid;
58 
59 	for (rid = 0; rid < nrids; rid++) {
60 		if (ridtbl[rid].fd == -1)
61 			break;
62 	}
63 	if (rid == nrids) {
64 		nrids++;
65 		newtbl = realloc(ridtbl, sizeof(struct resource) * nrids);
66 		if (newtbl == NULL) {
67 			nrids--;
68 			return (-1);
69 		} else
70 			ridtbl = newtbl;
71 	}
72 	ridtbl[rid].fd = INT_MAX;
73 	return (rid);
74 }
75 
76 static struct resource *
77 rid_lookup(int rid)
78 {
79 	struct resource *r;
80 
81 	if (rid < 0 || rid >= nrids) {
82 		errno = EINVAL;
83 		return (NULL);
84 	}
85 	r = ridtbl + rid;
86 	if (r->fd == -1) {
87 		errno = ENXIO;
88 		return (NULL);
89 	}
90 	return (r);
91 }
92 
93 int
94 bs_map(const char *dev, const char *res)
95 {
96 	char path[PATH_MAX];
97 	struct proto_ioc_region region;
98 	struct resource *r;
99 	int len, rid;
100 
101 	len = snprintf(path, PATH_MAX, "/dev/proto/%s/%s", dev, res);
102 	if (len >= PATH_MAX) {
103 		errno = EINVAL;
104 		return (-1);
105 	}
106 	rid = rid_alloc();
107 	if (rid == -1)
108 		return (-1);
109 	r = rid_lookup(rid);
110 	if (r == NULL)
111 		return (-1);
112 	r->fd = open(path, O_RDWR);
113 	if (r->fd == -1)
114 		return (-1);
115 	r->rid = -1;
116 	if (ioctl(r->fd, PROTO_IOC_REGION, &region) == -1) {
117 		close(r->fd);
118 		r->fd = -1;
119 		return (-1);
120 	}
121 	r->addr = region.address;
122 	r->size = region.size;
123 	r->ofs = 0;
124 	r->ptr = mmap(NULL, r->size, PROT_READ | PROT_WRITE,
125 	    MAP_NOCORE | MAP_SHARED, r->fd, r->ofs);
126 	return (rid);
127 }
128 
129 int
130 bs_read(int rid, off_t ofs, void *buf, ssize_t bufsz)
131 {
132 	struct resource *r;
133 	volatile void *ptr;
134 	off_t o;
135 	ssize_t s;
136 
137 	r = rid_lookup(rid);
138 	if (r == NULL)
139 		return (0);
140 	if (ofs < 0 || ofs > r->size - bufsz) {
141 		errno = ESPIPE;
142 		return (0);
143 	}
144 	ofs += r->ofs;
145 	if (r->ptr != MAP_FAILED) {
146 		ptr = r->ptr + ofs;
147 		switch (bufsz) {
148 		case 1:
149 			*((uint8_t *)buf) = *((volatile uint8_t *)ptr);
150 			break;
151 		case 2:
152 			*((uint16_t *)buf) = *((volatile uint16_t *)ptr);
153 			break;
154 		case 4:
155 			*((uint32_t *)buf) = *((volatile uint32_t *)ptr);
156 			break;
157 		default:
158 			errno = EIO;
159 			return (0);
160 		}
161 	} else {
162 		o = lseek(r->fd, ofs, SEEK_SET);
163 		if (o != ofs)
164 			return (0);
165 		s = read(r->fd, buf, bufsz);
166 		if (s != bufsz)
167 			return (0);
168 	}
169 	return (1);
170 }
171 
172 int
173 bs_subregion(int rid0, long ofs, long sz)
174 {
175 	struct resource *r;
176 	void *ptr0;
177 	long addr0, ofs0;
178 	int fd0, rid;
179 
180 	r = rid_lookup(rid0);
181 	if (r == NULL)
182 		return (-1);
183 	if (ofs < 0 || sz < 1) {
184 		errno = EINVAL;
185 		return (-1);
186 	}
187 	if (ofs + sz > r->size) {
188 		errno = ENOSPC;
189 		return (-1);
190 	}
191 	fd0 = r->fd;
192 	addr0 = r->addr;
193 	ofs0 = r->ofs;
194 	ptr0 = r->ptr;
195 	rid = rid_alloc();
196 	if (rid == -1)
197 		return (-1);
198 	r = rid_lookup(rid);
199 	if (r == NULL)
200 		return (-1);
201 	r->rid = rid0;
202 	r->fd = fd0;
203 	r->addr = addr0 + ofs;
204 	r->size = sz;
205 	r->ofs = ofs0 + ofs;
206 	r->ptr = ptr0;
207 	return (rid);
208 }
209 
210 int
211 bs_unmap(int rid)
212 {
213 	struct resource *r;
214 
215 	r = rid_lookup(rid);
216 	if (r == NULL)
217 		return (0);
218 	if (r->rid == -1) {
219 		if (r->ptr != MAP_FAILED)
220 			munmap(r->ptr, r->size);
221 		close(r->fd);
222 	}
223 	r->fd = -1;
224 	return (1);
225 }
226 
227 int
228 bs_write(int rid, off_t ofs, void *buf, ssize_t bufsz)
229 {
230 	struct resource *r;
231 	volatile void *ptr;
232 	off_t o;
233 	ssize_t s;
234 
235 	r = rid_lookup(rid);
236 	if (r == NULL)
237 		return (0);
238 	if (ofs < 0 || ofs > r->size - bufsz) {
239 		errno = ESPIPE;
240 		return (0);
241 	}
242 	ofs += r->ofs;
243 	if (r->ptr != MAP_FAILED) {
244 		ptr = r->ptr + ofs;
245 		switch (bufsz) {
246 		case 1:
247 			*((volatile uint8_t *)ptr) = *((uint8_t *)buf);
248 			break;
249 		case 2:
250 			*((volatile uint16_t *)ptr) = *((uint16_t *)buf);
251 			break;
252 		case 4:
253 			*((volatile uint32_t *)ptr) = *((uint32_t *)buf);
254 			break;
255 		default:
256 			errno = EIO;
257 			return (0);
258 		}
259 	} else {
260 		o = lseek(r->fd, ofs, SEEK_SET);
261 		if (o != ofs)
262 			return (0);
263 		s = write(r->fd, buf, bufsz);
264 		if (s != bufsz)
265 			return (0);
266 	}
267 	return (1);
268 }
269