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
41struct resource {
42	int	rid;
43	int	fd;
44	long	addr;
45	long	size;
46	off_t	ofs;
47	caddr_t	ptr;
48};
49
50static struct resource *ridtbl = NULL;
51static int nrids = 0;
52
53static int
54rid_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
76static struct resource *
77rid_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
93int
94bs_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
129int
130bs_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
172int
173bs_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
210int
211bs_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
227int
228bs_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