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