xref: /freebsd/sys/dev/pbio/pbio.c (revision f7c32ed617858bcd22f8d1b03199099d50125721)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  *  Copyright (c) 2000-2004
5  *          Diomidis D. Spinellis, Athens, Greece
6  *      All rights reserved.
7  *
8  *  Redistribution and use in source and binary forms, with or without
9  *  modification, are permitted provided that the following conditions
10  *  are met:
11  *  1. Redistributions of source code must retain the above copyright
12  *     notice, this list of conditions and the following disclaimer as
13  *     the first lines of this file unmodified.
14  *  2. Redistributions in binary form must reproduce the above copyright
15  *     notice, this list of conditions and the following disclaimer in the
16  *     documentation and/or other materials provided with the distribution.
17  *
18  *  THIS SOFTWARE IS PROVIDED BY Diomidis D. Spinellis ``AS IS'' AND ANY
19  *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21  *  PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL Diomidis D. Spinellis BE
22  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
25  *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26  *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
27  *  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28  *  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  *
30  * $Id: pbio.c,v 1.12 2003/10/11 13:05:08 dds Exp dds $
31  */
32 
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
35 
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/kernel.h>		/* SYSINIT stuff */
39 #include <sys/bus.h>
40 #include <sys/resource.h>
41 #include <sys/syslog.h>
42 #include <sys/sysctl.h>
43 #include <sys/conf.h>		/* cdevsw stuff */
44 #include <sys/malloc.h>		/* malloc region definitions */
45 #include <sys/module.h>
46 #include <machine/bus.h>
47 #include <machine/resource.h>
48 #include <sys/rman.h>
49 #include <dev/pbio/pbioio.h>		/* pbio IOCTL definitions */
50 #include <sys/uio.h>
51 #include <sys/fcntl.h>
52 #include <isa/isavar.h>
53 
54 /* Function prototypes (these should all be static) */
55 static	d_open_t	pbioopen;
56 static	d_close_t	pbioclose;
57 static	d_read_t	pbioread;
58 static	d_write_t	pbiowrite;
59 static	d_ioctl_t	pbioioctl;
60 static	d_poll_t	pbiopoll;
61 static	int		pbioprobe(device_t);
62 static	int		pbioattach(device_t);
63 
64 /* Device registers */
65 #define	PBIO_PORTA	0
66 #define	PBIO_PORTB	1
67 #define	PBIO_PORTC	2
68 #define	PBIO_CFG	3
69 #define	PBIO_IOSIZE	4
70 
71 /* Per-port buffer size */
72 #define	PBIO_BUFSIZ 64
73 
74 /* Number of /dev entries */
75 #define	PBIO_NPORTS 4
76 
77 /* I/O port range */
78 #define	IO_PBIOSIZE 4
79 
80 static char *port_names[] = {"a", "b", "ch", "cl"};
81 
82 #define	PBIO_PNAME(n)		(port_names[(n)])
83 
84 #define	UNIT(dev)		(dev2unit(dev) >> 2)
85 #define	PORT(dev)		(dev2unit(dev) & 0x3)
86 
87 #define	PBIOPRI	((PZERO + 5) | PCATCH)
88 
89 static struct cdevsw pbio_cdevsw = {
90 	.d_version = D_VERSION,
91 	.d_flags = D_NEEDGIANT,
92 	.d_open = pbioopen,
93 	.d_close = pbioclose,
94 	.d_read = pbioread,
95 	.d_write = pbiowrite,
96 	.d_ioctl = pbioioctl,
97 	.d_poll = pbiopoll,
98 	.d_name = "pbio"
99 };
100 
101 /*
102  * Data specific to each I/O port
103  */
104 struct portdata {
105 	struct cdev *port;
106 	int	diff;			/* When true read only differences */
107 	int	ipace;			/* Input pace */
108 	int	opace;			/* Output pace */
109 	char	oldval;			/* Last value read */
110 	char	buff[PBIO_BUFSIZ];	/* Per-port data buffer */
111 };
112 
113 /*
114  * One of these per allocated device
115  */
116 struct pbio_softc {
117 	struct portdata pd[PBIO_NPORTS];/* Per port data */
118 	int	iomode;			/* Virtualized I/O mode port value */
119 					/* The real port is write-only */
120 	struct resource *res;
121 	bus_space_tag_t bst;
122 	bus_space_handle_t bsh;
123 };
124 
125 typedef	struct pbio_softc *sc_p;
126 
127 static device_method_t pbio_methods[] = {
128 	/* Device interface */
129 	DEVMETHOD(device_probe,		pbioprobe),
130 	DEVMETHOD(device_attach,	pbioattach),
131 	{ 0, 0 }
132 };
133 
134 static	devclass_t	pbio_devclass;
135 #define	pbio_addr(unit) \
136 	    ((struct pbio_softc *) devclass_get_softc(pbio_devclass, unit))
137 
138 static char driver_name[] = "pbio";
139 
140 static driver_t pbio_driver = {
141 	driver_name,
142 	pbio_methods,
143 	sizeof(struct pbio_softc),
144 };
145 
146 DRIVER_MODULE(pbio, isa, pbio_driver, pbio_devclass, 0, 0);
147 
148 static __inline uint8_t
149 pbinb(struct pbio_softc *scp, int off)
150 {
151 
152 	return bus_space_read_1(scp->bst, scp->bsh, off);
153 }
154 
155 static __inline void
156 pboutb(struct pbio_softc *scp, int off, uint8_t val)
157 {
158 
159 	bus_space_write_1(scp->bst, scp->bsh, off, val);
160 }
161 
162 static int
163 pbioprobe(device_t dev)
164 {
165 	int		rid;
166 	struct pbio_softc *scp = device_get_softc(dev);
167 #ifdef GENERIC_PBIO_PROBE
168 	unsigned char val;
169 #endif
170 
171 	if (isa_get_logicalid(dev))		/* skip PnP probes */
172 		return (ENXIO);
173 	rid = 0;
174 	scp->res = bus_alloc_resource_anywhere(dev, SYS_RES_IOPORT, &rid,
175 	    IO_PBIOSIZE, RF_ACTIVE);
176 	if (scp->res == NULL)
177 		return (ENXIO);
178 
179 #ifdef GENERIC_PBIO_PROBE
180 	scp->bst = rman_get_bustag(scp->res);
181 	scp->bsh = rman_get_bushandle(scp->res);
182 	/*
183 	 * try see if the device is there.
184 	 * This probe works only if the device has no I/O attached to it
185 	 * XXX Better allow flags to abort testing
186 	 */
187 	/* Set all ports to output */
188 	pboutb(scp, PBIO_CFG, 0x80);
189 	printf("pbio val(CFG: 0x%03x)=0x%02x (should be 0x80)\n",
190 		rman_get_start(scp->res), pbinb(scp, PBIO_CFG));
191 	pboutb(scp, PBIO_PORTA, 0xa5);
192 	val = pbinb(scp, PBIO_PORTA);
193 	printf("pbio val=0x%02x (should be 0xa5)\n", val);
194 	if (val != 0xa5) {
195 		bus_release_resource(dev, SYS_RES_IOPORT, rid, sc->res);
196 		return (ENXIO);
197 	}
198 	pboutb(scp, PBIO_PORTA, 0x5a);
199 	val = pbinb(scp, PBIO_PORTA);
200 	printf("pbio val=0x%02x (should be 0x5a)\n", val);
201 	if (val != 0x5a) {
202 		bus_release_resource(dev, SYS_RES_IOPORT, rid, sc->res);
203 		return (ENXIO);
204 	}
205 #endif
206 	device_set_desc(dev, "Intel 8255 PPI (basic mode)");
207 	/* Set all ports to input */
208 	/* pboutb(scp, PBIO_CFG, 0x9b); */
209 	bus_release_resource(dev, SYS_RES_IOPORT, rid, scp->res);
210 	return (0);
211 }
212 
213 /*
214  * Called if the probe succeeded.
215  * We can be destructive here as we know we have the device.
216  * we can also trust the unit number.
217  */
218 static int
219 pbioattach (device_t dev)
220 {
221 	int unit;
222 	int i;
223 	int		rid;
224 	struct pbio_softc *sc;
225 
226 	sc = device_get_softc(dev);
227 	unit = device_get_unit(dev);
228 	rid = 0;
229 	sc->res = bus_alloc_resource_anywhere(dev, SYS_RES_IOPORT, &rid,
230 	    IO_PBIOSIZE, RF_ACTIVE);
231 	if (sc->res == NULL)
232 		return (ENXIO);
233 	sc->bst = rman_get_bustag(sc->res);
234 	sc->bsh = rman_get_bushandle(sc->res);
235 
236 	/*
237 	 * Store whatever seems wise.
238 	 */
239 	sc->iomode = 0x9b;		/* All ports to input */
240 
241 	for (i = 0; i < PBIO_NPORTS; i++)
242 		sc->pd[i].port = make_dev(&pbio_cdevsw, (unit << 2) + i, 0, 0,
243 		    0600, "pbio%d%s", unit, PBIO_PNAME(i));
244 	return (0);
245 }
246 
247 static int
248 pbioioctl (struct cdev *dev, u_long cmd, caddr_t data, int flag,
249     struct thread *td)
250 {
251 	struct pbio_softc *scp;
252 	int port, unit;
253 
254 	unit = UNIT(dev);
255 	port = PORT(dev);
256 	scp = pbio_addr(unit);
257 	if (scp == NULL)
258 		return (ENODEV);
259 	switch (cmd) {
260 	case PBIO_SETDIFF:
261 		scp->pd[port].diff = *(int *)data;
262 		break;
263 	case PBIO_SETIPACE:
264 		scp->pd[port].ipace = *(int *)data;
265 		break;
266 	case PBIO_SETOPACE:
267 		scp->pd[port].opace = *(int *)data;
268 		break;
269 	case PBIO_GETDIFF:
270 		*(int *)data = scp->pd[port].diff;
271 		break;
272 	case PBIO_GETIPACE:
273 		*(int *)data = scp->pd[port].ipace;
274 		break;
275 	case PBIO_GETOPACE:
276 		*(int *)data = scp->pd[port].opace;
277 		break;
278 	default:
279 		return ENXIO;
280 	}
281 	return (0);
282 }
283 
284 static  int
285 pbioopen(struct cdev *dev, int oflags, int devtype, struct thread *td)
286 {
287 	struct pbio_softc *scp;
288 	int ocfg, port, unit;
289 	int portbit;			/* Port configuration bit */
290 
291 	unit = UNIT(dev);
292 	port = PORT(dev);
293 	scp = pbio_addr(unit);
294 	if (scp == NULL)
295 		return (ENODEV);
296 
297 	switch (port) {
298 	case 0: portbit = 0x10; break;	/* Port A */
299 	case 1: portbit = 0x02; break;	/* Port B */
300 	case 2: portbit = 0x08; break;	/* Port CH */
301 	case 3: portbit = 0x01; break;	/* Port CL */
302 	default: return (ENODEV);
303 	}
304 	ocfg = scp->iomode;
305 
306 	if (oflags & FWRITE)
307 		/* Writing == output; zero the bit */
308 		pboutb(scp, PBIO_CFG, scp->iomode = (ocfg & (~portbit)));
309 	else if (oflags & FREAD)
310 		/* Reading == input; set the bit */
311 		pboutb(scp, PBIO_CFG, scp->iomode = (ocfg | portbit));
312 	else
313 		return (EACCES);
314 
315 	return (0);
316 }
317 
318 static  int
319 pbioclose(struct cdev *dev, int fflag, int devtype, struct thread *td)
320 {
321 	struct pbio_softc *scp;
322 	int unit;
323 
324 	unit = UNIT(dev);
325 	scp = pbio_addr(unit);
326 	if (scp == NULL)
327 		return (ENODEV);
328 
329 	return (0);
330 }
331 
332 /*
333  * Return the value of a given port on a given I/O base address
334  * Handles the split C port nibbles and blocking
335  */
336 static int
337 portval(int port, struct pbio_softc *scp, char *val)
338 {
339 	int err;
340 
341 	for (;;) {
342 		switch (port) {
343 		case 0:
344 			*val = pbinb(scp, PBIO_PORTA);
345 			break;
346 		case 1:
347 			*val = pbinb(scp, PBIO_PORTB);
348 			break;
349 		case 2:
350 			*val = (pbinb(scp, PBIO_PORTC) >> 4) & 0xf;
351 			break;
352 		case 3:
353 			*val = pbinb(scp, PBIO_PORTC) & 0xf;
354 			break;
355 		default:
356 			*val = 0;
357 			break;
358 		}
359 		if (scp->pd[port].diff) {
360 			if (*val != scp->pd[port].oldval) {
361 				scp->pd[port].oldval = *val;
362 				return (0);
363 			}
364 			err = tsleep((caddr_t)&(scp->pd[port].diff), PBIOPRI,
365 				     "pbiopl", max(1, scp->pd[port].ipace));
366 			if (err == EINTR)
367 				return (EINTR);
368 		} else
369 			return (0);
370 	}
371 }
372 
373 static  int
374 pbioread(struct cdev *dev, struct uio *uio, int ioflag)
375 {
376 	struct pbio_softc *scp;
377 	int err, i, port, ret, toread, unit;
378 	char val;
379 
380 	unit = UNIT(dev);
381 	port = PORT(dev);
382 	scp = pbio_addr(unit);
383 	if (scp == NULL)
384 		return (ENODEV);
385 
386 	while (uio->uio_resid > 0) {
387 		toread = min(uio->uio_resid, PBIO_BUFSIZ);
388 		if ((ret = uiomove(scp->pd[port].buff, toread, uio)) != 0)
389 			return (ret);
390 		for (i = 0; i < toread; i++) {
391 			if ((err = portval(port, scp, &val)) != 0)
392 				return (err);
393 			scp->pd[port].buff[i] = val;
394 			if (!scp->pd[port].diff && scp->pd[port].ipace)
395 				tsleep((caddr_t)&(scp->pd[port].ipace), PBIOPRI,
396 					"pbioip", scp->pd[port].ipace);
397 		}
398 	}
399 	return 0;
400 }
401 
402 static int
403 pbiowrite(struct cdev *dev, struct uio *uio, int ioflag)
404 {
405 	struct pbio_softc *scp;
406 	int i, port, ret, towrite, unit;
407 	char val, oval;
408 
409 	unit = UNIT(dev);
410 	port = PORT(dev);
411 	scp = pbio_addr(unit);
412 	if (scp == NULL)
413 		return (ENODEV);
414 
415 	while (uio->uio_resid > 0) {
416 		towrite = min(uio->uio_resid, PBIO_BUFSIZ);
417 		if ((ret = uiomove(scp->pd[port].buff, towrite, uio)) != 0)
418 			return (ret);
419 		for (i = 0; i < towrite; i++) {
420 			val = scp->pd[port].buff[i];
421 			switch (port) {
422 			case 0:
423 				pboutb(scp, PBIO_PORTA, val);
424 				break;
425 			case 1:
426 				pboutb(scp, PBIO_PORTB, val);
427 				break;
428 			case 2:
429 				oval = pbinb(scp, PBIO_PORTC);
430 				oval &= 0xf;
431 				val <<= 4;
432 				pboutb(scp, PBIO_PORTC, val | oval);
433 				break;
434 			case 3:
435 				oval = pbinb(scp, PBIO_PORTC);
436 				oval &= 0xf0;
437 				val &= 0xf;
438 				pboutb(scp, PBIO_PORTC, oval | val);
439 				break;
440 			}
441 			if (scp->pd[port].opace)
442 				tsleep((caddr_t)&(scp->pd[port].opace),
443 					PBIOPRI, "pbioop",
444 					scp->pd[port].opace);
445 		}
446 	}
447 	return (0);
448 }
449 
450 static  int
451 pbiopoll(struct cdev *dev, int which, struct thread *td)
452 {
453 	struct pbio_softc *scp;
454 	int unit;
455 
456 	unit = UNIT(dev);
457 	scp = pbio_addr(unit);
458 	if (scp == NULL)
459 		return (ENODEV);
460 
461 	/*
462 	 * Do processing
463 	 */
464 	return (0); /* this is the wrong value I'm sure */
465 }
466