xref: /freebsd/sys/dev/proto/proto_core.c (revision 2b15cb3d0922bd70ea592f0da9b4a5b167f4d53f)
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/param.h>
31 #include <sys/systm.h>
32 #include <sys/bus.h>
33 #include <sys/conf.h>
34 #include <sys/cons.h>
35 #include <sys/fcntl.h>
36 #include <sys/interrupt.h>
37 #include <sys/kdb.h>
38 #include <sys/kernel.h>
39 #include <sys/malloc.h>
40 #include <sys/mman.h>
41 #include <sys/proc.h>
42 #include <sys/queue.h>
43 #include <sys/reboot.h>
44 #include <machine/bus.h>
45 #include <sys/rman.h>
46 #include <sys/uio.h>
47 #include <machine/resource.h>
48 #include <machine/stdarg.h>
49 
50 #include <dev/pci/pcivar.h>
51 
52 #include <dev/proto/proto.h>
53 #include <dev/proto/proto_dev.h>
54 
55 CTASSERT(SYS_RES_IRQ != PROTO_RES_UNUSED &&
56     SYS_RES_MEMORY != PROTO_RES_UNUSED &&
57     SYS_RES_IOPORT != PROTO_RES_UNUSED);
58 CTASSERT(SYS_RES_IRQ != PROTO_RES_PCICFG &&
59     SYS_RES_MEMORY != PROTO_RES_PCICFG &&
60     SYS_RES_IOPORT != PROTO_RES_PCICFG);
61 
62 devclass_t proto_devclass;
63 char proto_driver_name[] = "proto";
64 
65 static d_open_t proto_open;
66 static d_close_t proto_close;
67 static d_read_t proto_read;
68 static d_write_t proto_write;
69 static d_ioctl_t proto_ioctl;
70 static d_mmap_t proto_mmap;
71 
72 struct cdevsw proto_devsw = {
73 	.d_version = D_VERSION,
74 	.d_flags = 0,
75 	.d_name = proto_driver_name,
76 	.d_open = proto_open,
77 	.d_close = proto_close,
78 	.d_read = proto_read,
79 	.d_write = proto_write,
80 	.d_ioctl = proto_ioctl,
81 	.d_mmap = proto_mmap,
82 };
83 
84 static MALLOC_DEFINE(M_PROTO, "PROTO", "PROTO driver");
85 
86 int
87 proto_add_resource(struct proto_softc *sc, int type, int rid,
88     struct resource *res)
89 {
90 	struct proto_res *r;
91 
92 	if (type == PROTO_RES_UNUSED)
93 		return (EINVAL);
94 	if (sc->sc_rescnt == PROTO_RES_MAX)
95 		return (ENOSPC);
96 
97 	r = sc->sc_res + sc->sc_rescnt++;
98 	r->r_type = type;
99 	r->r_rid = rid;
100 	r->r_res = res;
101 	return (0);
102 }
103 
104 #ifdef notyet
105 static int
106 proto_intr(void *arg)
107 {
108 	struct proto_softc *sc = arg;
109 
110 	/* XXX TODO */
111 	return (FILTER_HANDLED);
112 }
113 #endif
114 
115 int
116 proto_attach(device_t dev)
117 {
118 	struct proto_softc *sc;
119 	struct proto_res *r;
120 	u_int res;
121 
122 	sc = device_get_softc(dev);
123 	sc->sc_dev = dev;
124 
125 	for (res = 0; res < sc->sc_rescnt; res++) {
126 		r = sc->sc_res + res;
127 		switch (r->r_type) {
128 		case SYS_RES_IRQ:
129 			/* XXX TODO */
130 			break;
131 		case SYS_RES_MEMORY:
132 		case SYS_RES_IOPORT:
133 			r->r_size = rman_get_size(r->r_res);
134 			r->r_u.cdev = make_dev(&proto_devsw, res, 0, 0, 0666,
135 			    "proto/%s/%02x.%s", device_get_desc(dev), r->r_rid,
136 			    (r->r_type == SYS_RES_IOPORT) ? "io" : "mem");
137 			r->r_u.cdev->si_drv1 = sc;
138 			r->r_u.cdev->si_drv2 = r;
139 			break;
140 		case PROTO_RES_PCICFG:
141 			r->r_size = 4096;
142 			r->r_u.cdev = make_dev(&proto_devsw, res, 0, 0, 0666,
143 			    "proto/%s/pcicfg", device_get_desc(dev));
144 			r->r_u.cdev->si_drv1 = sc;
145 			r->r_u.cdev->si_drv2 = r;
146 			break;
147 		}
148 	}
149 	return (0);
150 }
151 
152 int
153 proto_detach(device_t dev)
154 {
155 	struct proto_softc *sc;
156 	struct proto_res *r;
157 	u_int res;
158 
159 	sc = device_get_softc(dev);
160 
161 	/* Don't detach if we have open device filess. */
162 	for (res = 0; res < sc->sc_rescnt; res++) {
163 		r = sc->sc_res + res;
164 		if (r->r_opened)
165 			return (EBUSY);
166 	}
167 
168 	for (res = 0; res < sc->sc_rescnt; res++) {
169 		r = sc->sc_res + res;
170 		switch (r->r_type) {
171 		case SYS_RES_IRQ:
172 			/* XXX TODO */
173 			break;
174 		case SYS_RES_MEMORY:
175 		case SYS_RES_IOPORT:
176 		case PROTO_RES_PCICFG:
177 			destroy_dev(r->r_u.cdev);
178 			break;
179 		}
180 		if (r->r_res != NULL) {
181 			bus_release_resource(dev, r->r_type, r->r_rid,
182 			    r->r_res);
183 			r->r_res = NULL;
184 		}
185 		r->r_type = PROTO_RES_UNUSED;
186 	}
187 	sc->sc_rescnt = 0;
188 	return (0);
189 }
190 
191 /*
192  * Device functions
193  */
194 
195 static int
196 proto_open(struct cdev *cdev, int oflags, int devtype, struct thread *td)
197 {
198 	struct proto_res *r;
199 
200 	r = cdev->si_drv2;
201 	if (!atomic_cmpset_acq_ptr(&r->r_opened, 0UL, (uintptr_t)td->td_proc))
202 		return (EBUSY);
203 	return (0);
204 }
205 
206 static int
207 proto_close(struct cdev *cdev, int fflag, int devtype, struct thread *td)
208 {
209 	struct proto_res *r;
210 
211 	r = cdev->si_drv2;
212 	if (!atomic_cmpset_acq_ptr(&r->r_opened, (uintptr_t)td->td_proc, 0UL))
213 		return (ENXIO);
214 	return (0);
215 }
216 
217 static int
218 proto_read(struct cdev *cdev, struct uio *uio, int ioflag)
219 {
220 	union {
221 		uint8_t	x1[8];
222 		uint16_t x2[4];
223 		uint32_t x4[2];
224 		uint64_t x8[1];
225 	} buf;
226 	struct proto_softc *sc;
227 	struct proto_res *r;
228 	device_t dev;
229 	off_t ofs;
230 	u_long width;
231 	int error;
232 
233 	sc = cdev->si_drv1;
234 	dev = sc->sc_dev;
235 	r = cdev->si_drv2;
236 
237 	width = uio->uio_resid;
238 	if (width < 1 || width > 8 || bitcount16(width) > 1)
239 		return (EIO);
240 	ofs = uio->uio_offset;
241 	if (ofs + width > r->r_size)
242 		return (EIO);
243 
244 	switch (width) {
245 	case 1:
246 		buf.x1[0] = (r->r_type == PROTO_RES_PCICFG) ?
247 		    pci_read_config(dev, ofs, 1) : bus_read_1(r->r_res, ofs);
248 		break;
249 	case 2:
250 		buf.x2[0] = (r->r_type == PROTO_RES_PCICFG) ?
251 		    pci_read_config(dev, ofs, 2) : bus_read_2(r->r_res, ofs);
252 		break;
253 	case 4:
254 		buf.x4[0] = (r->r_type == PROTO_RES_PCICFG) ?
255 		    pci_read_config(dev, ofs, 4) : bus_read_4(r->r_res, ofs);
256 		break;
257 #ifndef __i386__
258 	case 8:
259 		if (r->r_type == PROTO_RES_PCICFG)
260 			return (EINVAL);
261 		buf.x8[0] = bus_read_8(r->r_res, ofs);
262 		break;
263 #endif
264 	default:
265 		return (EIO);
266 	}
267 
268 	error = uiomove(&buf, width, uio);
269 	return (error);
270 }
271 
272 static int
273 proto_write(struct cdev *cdev, struct uio *uio, int ioflag)
274 {
275 	union {
276 		uint8_t	x1[8];
277 		uint16_t x2[4];
278 		uint32_t x4[2];
279 		uint64_t x8[1];
280 	} buf;
281 	struct proto_softc *sc;
282 	struct proto_res *r;
283 	device_t dev;
284 	off_t ofs;
285 	u_long width;
286 	int error;
287 
288 	sc = cdev->si_drv1;
289 	dev = sc->sc_dev;
290 	r = cdev->si_drv2;
291 
292 	width = uio->uio_resid;
293 	if (width < 1 || width > 8 || bitcount16(width) > 1)
294 		return (EIO);
295 	ofs = uio->uio_offset;
296 	if (ofs + width > r->r_size)
297 		return (EIO);
298 
299 	error = uiomove(&buf, width, uio);
300 	if (error)
301 		return (error);
302 
303 	switch (width) {
304 	case 1:
305 		if (r->r_type == PROTO_RES_PCICFG)
306 			pci_write_config(dev, ofs, buf.x1[0], 1);
307 		else
308 			bus_write_1(r->r_res, ofs, buf.x1[0]);
309 		break;
310 	case 2:
311 		if (r->r_type == PROTO_RES_PCICFG)
312 			pci_write_config(dev, ofs, buf.x2[0], 2);
313 		else
314 			bus_write_2(r->r_res, ofs, buf.x2[0]);
315 		break;
316 	case 4:
317 		if (r->r_type == PROTO_RES_PCICFG)
318 			pci_write_config(dev, ofs, buf.x4[0], 4);
319 		else
320 			bus_write_4(r->r_res, ofs, buf.x4[0]);
321 		break;
322 #ifndef __i386__
323 	case 8:
324 		if (r->r_type == PROTO_RES_PCICFG)
325 			return (EINVAL);
326 		bus_write_8(r->r_res, ofs, buf.x8[0]);
327 		break;
328 #endif
329 	default:
330 		return (EIO);
331 	}
332 
333 	return (0);
334 }
335 
336 static int
337 proto_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag,
338     struct thread *td)
339 {
340 	struct proto_ioc_region *region;
341 	struct proto_res *r;
342 	int error;
343 
344 	r = cdev->si_drv2;
345 
346 	error = 0;
347 	switch (cmd) {
348 	case PROTO_IOC_REGION:
349 		region = (struct proto_ioc_region *)data;
350 		region->size = r->r_size;
351 		if (r->r_type == PROTO_RES_PCICFG)
352 			region->address = 0;
353 		else
354 			region->address = rman_get_start(r->r_res);
355 		break;
356 	default:
357 		error = ENOIOCTL;
358 		break;
359 	}
360 	return (error);
361 }
362 
363 static int
364 proto_mmap(struct cdev *cdev, vm_ooffset_t offset, vm_paddr_t *paddr,
365     int prot, vm_memattr_t *memattr)
366 {
367 	struct proto_res *r;
368 
369 	r = cdev->si_drv2;
370 
371 	if (r->r_type != SYS_RES_MEMORY)
372 		return (ENXIO);
373 	if (offset & PAGE_MASK)
374 		return (EINVAL);
375 	if (prot & PROT_EXEC)
376 		return (EACCES);
377 	if (offset >= r->r_size)
378 		return (EINVAL);
379 	*paddr = rman_get_start(r->r_res) + offset;
380 #ifndef __sparc64__
381 	*memattr = VM_MEMATTR_UNCACHEABLE;
382 #endif
383 	return (0);
384 }
385