190075Sobrien/*-
290075Sobrien * Copyright (c) 2014 Marcel Moolenaar
390075Sobrien * All rights reserved.
490075Sobrien *
590075Sobrien * Redistribution and use in source and binary forms, with or without
690075Sobrien * modification, are permitted provided that the following conditions
790075Sobrien * are met:
890075Sobrien *
990075Sobrien * 1. Redistributions of source code must retain the above copyright
1090075Sobrien *    notice, this list of conditions and the following disclaimer.
1190075Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1290075Sobrien *    notice, this list of conditions and the following disclaimer in the
1390075Sobrien *    documentation and/or other materials provided with the distribution.
1490075Sobrien *
1590075Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1690075Sobrien * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1790075Sobrien * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1890075Sobrien * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1990075Sobrien * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2090075Sobrien * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2190075Sobrien * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2290075Sobrien * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2390075Sobrien * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2490075Sobrien * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2590075Sobrien */
2690075Sobrien
2790075Sobrien#include <sys/cdefs.h>
2890075Sobrien__FBSDID("$FreeBSD$");
2990075Sobrien
3090075Sobrien#include <sys/ioctl.h>
3190075Sobrien#include <sys/mman.h>
3290075Sobrien#include <errno.h>
3390075Sobrien#include <fcntl.h>
3490075Sobrien#include <limits.h>
3590075Sobrien#include <stdio.h>
3690075Sobrien#include <stdlib.h>
3790075Sobrien#include <unistd.h>
3890075Sobrien
3990075Sobrien#include "bus.h"
4090075Sobrien
4190075Sobrien#include "../../sys/dev/proto/proto_dev.h"
4290075Sobrien
4390075Sobrienstruct resource {
4490075Sobrien	int	rid;
4590075Sobrien	int	fd;
4690075Sobrien	long	addr;
4790075Sobrien	long	size;
4890075Sobrien	off_t	ofs;
4990075Sobrien	caddr_t	ptr;
5090075Sobrien};
5190075Sobrien
5290075Sobrienstatic struct resource *ridtbl = NULL;
5390075Sobrienstatic int nrids = 0;
5490075Sobrien
5590075Sobrienstatic int
5690075Sobrienrid_alloc(void)
5790075Sobrien{
5890075Sobrien	void *newtbl;
5990075Sobrien	int rid;
6090075Sobrien
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
78static struct resource *
79rid_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
95int
96bs_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
131int
132bs_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
174int
175bs_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
212int
213bs_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
229int
230bs_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