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