xref: /freebsd/sys/dev/pbio/pbio.c (revision 4b9d6057)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
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/param.h>
34 #include <sys/systm.h>
35 #include <sys/kernel.h>		/* SYSINIT stuff */
36 #include <sys/bus.h>
37 #include <sys/resource.h>
38 #include <sys/syslog.h>
39 #include <sys/sysctl.h>
40 #include <sys/conf.h>		/* cdevsw stuff */
41 #include <sys/malloc.h>		/* malloc region definitions */
42 #include <sys/module.h>
43 #include <machine/bus.h>
44 #include <machine/resource.h>
45 #include <sys/rman.h>
46 #include <dev/pbio/pbioio.h>		/* pbio IOCTL definitions */
47 #include <sys/uio.h>
48 #include <sys/fcntl.h>
49 #include <sys/sx.h>
50 #include <isa/isavar.h>
51 
52 /* Function prototypes (these should all be static) */
53 static	d_open_t	pbioopen;
54 static	d_read_t	pbioread;
55 static	d_write_t	pbiowrite;
56 static	d_ioctl_t	pbioioctl;
57 static	d_poll_t	pbiopoll;
58 static	int		pbioprobe(device_t);
59 static	int		pbioattach(device_t);
60 
61 /* Device registers */
62 #define	PBIO_PORTA	0
63 #define	PBIO_PORTB	1
64 #define	PBIO_PORTC	2
65 #define	PBIO_CFG	3
66 #define	PBIO_IOSIZE	4
67 
68 /* Per-port buffer size */
69 #define	PBIO_BUFSIZ 64
70 
71 /* Number of /dev entries */
72 #define	PBIO_NPORTS 4
73 
74 /* I/O port range */
75 #define	IO_PBIOSIZE 4
76 
77 static char *port_names[] = {"a", "b", "ch", "cl"};
78 
79 #define	PBIO_PNAME(n)		(port_names[(n)])
80 
81 #define	PORT(dev)		(dev2unit(dev))
82 
83 #define	pbio_addr(dev)		((dev)->si_drv1)
84 
85 static struct cdevsw pbio_cdevsw = {
86 	.d_version = D_VERSION,
87 	.d_open = pbioopen,
88 	.d_read = pbioread,
89 	.d_write = pbiowrite,
90 	.d_ioctl = pbioioctl,
91 	.d_poll = pbiopoll,
92 	.d_name = "pbio"
93 };
94 
95 /*
96  * Data specific to each I/O port
97  */
98 struct portdata {
99 	struct cdev *port;
100 	int	diff;			/* When true read only differences */
101 	int	ipace;			/* Input pace */
102 	int	opace;			/* Output pace */
103 	char	oldval;			/* Last value read */
104 	char	buff[PBIO_BUFSIZ];	/* Per-port data buffer */
105 };
106 
107 /*
108  * One of these per allocated device
109  */
110 struct pbio_softc {
111 	struct portdata pd[PBIO_NPORTS];/* Per port data */
112 	int	iomode;			/* Virtualized I/O mode port value */
113 					/* The real port is write-only */
114 	struct resource *res;
115 	struct sx lock;
116 };
117 
118 typedef	struct pbio_softc *sc_p;
119 
120 static device_method_t pbio_methods[] = {
121 	/* Device interface */
122 	DEVMETHOD(device_probe,		pbioprobe),
123 	DEVMETHOD(device_attach,	pbioattach),
124 	{ 0, 0 }
125 };
126 
127 static char driver_name[] = "pbio";
128 
129 static driver_t pbio_driver = {
130 	driver_name,
131 	pbio_methods,
132 	sizeof(struct pbio_softc),
133 };
134 
135 DRIVER_MODULE(pbio, isa, pbio_driver, 0, 0);
136 
137 static __inline uint8_t
138 pbinb(struct pbio_softc *scp, int off)
139 {
140 
141 	return (bus_read_1(scp->res, off));
142 }
143 
144 static __inline void
145 pboutb(struct pbio_softc *scp, int off, uint8_t val)
146 {
147 
148 	bus_write_1(scp->res, off, val);
149 }
150 
151 static int
152 pbioprobe(device_t dev)
153 {
154 	int		rid;
155 	struct pbio_softc *scp = device_get_softc(dev);
156 #ifdef GENERIC_PBIO_PROBE
157 	unsigned char val;
158 #endif
159 
160 	if (isa_get_logicalid(dev))		/* skip PnP probes */
161 		return (ENXIO);
162 	rid = 0;
163 	scp->res = bus_alloc_resource_anywhere(dev, SYS_RES_IOPORT, &rid,
164 	    IO_PBIOSIZE, RF_ACTIVE);
165 	if (scp->res == NULL)
166 		return (ENXIO);
167 
168 #ifdef GENERIC_PBIO_PROBE
169 	/*
170 	 * try see if the device is there.
171 	 * This probe works only if the device has no I/O attached to it
172 	 * XXX Better allow flags to abort testing
173 	 */
174 	/* Set all ports to output */
175 	pboutb(scp, PBIO_CFG, 0x80);
176 	printf("pbio val(CFG: 0x%03x)=0x%02x (should be 0x80)\n",
177 		rman_get_start(scp->res), pbinb(scp, PBIO_CFG));
178 	pboutb(scp, PBIO_PORTA, 0xa5);
179 	val = pbinb(scp, PBIO_PORTA);
180 	printf("pbio val=0x%02x (should be 0xa5)\n", val);
181 	if (val != 0xa5) {
182 		bus_release_resource(dev, SYS_RES_IOPORT, rid, sc->res);
183 		return (ENXIO);
184 	}
185 	pboutb(scp, PBIO_PORTA, 0x5a);
186 	val = pbinb(scp, PBIO_PORTA);
187 	printf("pbio val=0x%02x (should be 0x5a)\n", val);
188 	if (val != 0x5a) {
189 		bus_release_resource(dev, SYS_RES_IOPORT, rid, sc->res);
190 		return (ENXIO);
191 	}
192 #endif
193 	device_set_desc(dev, "Intel 8255 PPI (basic mode)");
194 	/* Set all ports to input */
195 	/* pboutb(scp, PBIO_CFG, 0x9b); */
196 	bus_release_resource(dev, SYS_RES_IOPORT, rid, scp->res);
197 	return (BUS_PROBE_DEFAULT);
198 }
199 
200 /*
201  * Called if the probe succeeded.
202  * We can be destructive here as we know we have the device.
203  * we can also trust the unit number.
204  */
205 static int
206 pbioattach (device_t dev)
207 {
208 	struct make_dev_args args;
209 	int unit;
210 	int i;
211 	int		rid;
212 	struct pbio_softc *sc;
213 
214 	sc = device_get_softc(dev);
215 	unit = device_get_unit(dev);
216 	rid = 0;
217 	sc->res = bus_alloc_resource_anywhere(dev, SYS_RES_IOPORT, &rid,
218 	    IO_PBIOSIZE, RF_ACTIVE);
219 	if (sc->res == NULL)
220 		return (ENXIO);
221 
222 	/*
223 	 * Store whatever seems wise.
224 	 */
225 	sc->iomode = 0x9b;		/* All ports to input */
226 
227 	sx_init(&sc->lock, "pbio");
228 	for (i = 0; i < PBIO_NPORTS; i++) {
229 		make_dev_args_init(&args);
230 		args.mda_devsw = &pbio_cdevsw;
231 		args.mda_uid = 0;
232 		args.mda_gid = 0;
233 		args.mda_mode = 0600;
234 		args.mda_unit = i;
235 		args.mda_si_drv1 = sc;
236 		(void)make_dev_s(&args, &sc->pd[i].port, "pbio%d%s", unit,
237 		    PBIO_PNAME(i));
238 	}
239 	return (0);
240 }
241 
242 static int
243 pbioioctl (struct cdev *dev, u_long cmd, caddr_t data, int flag,
244     struct thread *td)
245 {
246 	struct pbio_softc *scp;
247 	int error, port;
248 
249 	error = 0;
250 	port = PORT(dev);
251 	scp = pbio_addr(dev);
252 	sx_xlock(&scp->lock);
253 	switch (cmd) {
254 	case PBIO_SETDIFF:
255 		scp->pd[port].diff = *(int *)data;
256 		break;
257 	case PBIO_SETIPACE:
258 		scp->pd[port].ipace = *(int *)data;
259 		break;
260 	case PBIO_SETOPACE:
261 		scp->pd[port].opace = *(int *)data;
262 		break;
263 	case PBIO_GETDIFF:
264 		*(int *)data = scp->pd[port].diff;
265 		break;
266 	case PBIO_GETIPACE:
267 		*(int *)data = scp->pd[port].ipace;
268 		break;
269 	case PBIO_GETOPACE:
270 		*(int *)data = scp->pd[port].opace;
271 		break;
272 	default:
273 		error = ENXIO;
274 	}
275 	sx_xunlock(&scp->lock);
276 	return (error);
277 }
278 
279 static  int
280 pbioopen(struct cdev *dev, int oflags, int devtype, struct thread *td)
281 {
282 	struct pbio_softc *scp;
283 	int error, ocfg, port;
284 	int portbit;			/* Port configuration bit */
285 
286 	port = PORT(dev);
287 	scp = pbio_addr(dev);
288 
289 	switch (port) {
290 	case 0: portbit = 0x10; break;	/* Port A */
291 	case 1: portbit = 0x02; break;	/* Port B */
292 	case 2: portbit = 0x08; break;	/* Port CH */
293 	case 3: portbit = 0x01; break;	/* Port CL */
294 	default: return (ENODEV);
295 	}
296 	ocfg = scp->iomode;
297 
298 	error = 0;
299 	sx_xlock(&scp->lock);
300 	if (oflags & FWRITE)
301 		/* Writing == output; zero the bit */
302 		pboutb(scp, PBIO_CFG, scp->iomode = (ocfg & (~portbit)));
303 	else if (oflags & FREAD)
304 		/* Reading == input; set the bit */
305 		pboutb(scp, PBIO_CFG, scp->iomode = (ocfg | portbit));
306 	else
307 		error = EACCES;
308 	sx_xunlock(&scp->lock);
309 
310 	return (error);
311 }
312 
313 /*
314  * Return the value of a given port on a given I/O base address
315  * Handles the split C port nibbles and blocking
316  */
317 static int
318 portval(int port, struct pbio_softc *scp, char *val)
319 {
320 	int err;
321 
322 	for (;;) {
323 		switch (port) {
324 		case 0:
325 			*val = pbinb(scp, PBIO_PORTA);
326 			break;
327 		case 1:
328 			*val = pbinb(scp, PBIO_PORTB);
329 			break;
330 		case 2:
331 			*val = (pbinb(scp, PBIO_PORTC) >> 4) & 0xf;
332 			break;
333 		case 3:
334 			*val = pbinb(scp, PBIO_PORTC) & 0xf;
335 			break;
336 		default:
337 			*val = 0;
338 			break;
339 		}
340 		if (scp->pd[port].diff) {
341 			if (*val != scp->pd[port].oldval) {
342 				scp->pd[port].oldval = *val;
343 				return (0);
344 			}
345 			err = pause_sig("pbiopl", max(1, scp->pd[port].ipace));
346 			if (err == EINTR)
347 				return (EINTR);
348 		} else
349 			return (0);
350 	}
351 }
352 
353 static  int
354 pbioread(struct cdev *dev, struct uio *uio, int ioflag)
355 {
356 	struct pbio_softc *scp;
357 	int err, i, port, toread;
358 	char val;
359 
360 	port = PORT(dev);
361 	scp = pbio_addr(dev);
362 
363 	err = 0;
364 	sx_xlock(&scp->lock);
365 	while (uio->uio_resid > 0) {
366 		toread = min(uio->uio_resid, PBIO_BUFSIZ);
367 		if ((err = uiomove(scp->pd[port].buff, toread, uio)) != 0)
368 			break;
369 		for (i = 0; i < toread; i++) {
370 			if ((err = portval(port, scp, &val)) != 0)
371 				break;
372 			scp->pd[port].buff[i] = val;
373 			if (!scp->pd[port].diff && scp->pd[port].ipace)
374 				pause_sig("pbioip", scp->pd[port].ipace);
375 		}
376 	}
377 	sx_xunlock(&scp->lock);
378 	return (err);
379 }
380 
381 static int
382 pbiowrite(struct cdev *dev, struct uio *uio, int ioflag)
383 {
384 	struct pbio_softc *scp;
385 	int i, port, ret, towrite;
386 	char val, oval;
387 
388 	port = PORT(dev);
389 	scp = pbio_addr(dev);
390 
391 	ret = 0;
392 	sx_xlock(&scp->lock);
393 	while (uio->uio_resid > 0) {
394 		towrite = min(uio->uio_resid, PBIO_BUFSIZ);
395 		if ((ret = uiomove(scp->pd[port].buff, towrite, uio)) != 0)
396 			break;
397 		for (i = 0; i < towrite; i++) {
398 			val = scp->pd[port].buff[i];
399 			switch (port) {
400 			case 0:
401 				pboutb(scp, PBIO_PORTA, val);
402 				break;
403 			case 1:
404 				pboutb(scp, PBIO_PORTB, val);
405 				break;
406 			case 2:
407 				oval = pbinb(scp, PBIO_PORTC);
408 				oval &= 0xf;
409 				val <<= 4;
410 				pboutb(scp, PBIO_PORTC, val | oval);
411 				break;
412 			case 3:
413 				oval = pbinb(scp, PBIO_PORTC);
414 				oval &= 0xf0;
415 				val &= 0xf;
416 				pboutb(scp, PBIO_PORTC, oval | val);
417 				break;
418 			}
419 			if (scp->pd[port].opace)
420 				pause_sig("pbioop", scp->pd[port].opace);
421 		}
422 	}
423 	sx_xunlock(&scp->lock);
424 	return (ret);
425 }
426 
427 static  int
428 pbiopoll(struct cdev *dev, int which, struct thread *td)
429 {
430 	/*
431 	 * Do processing
432 	 */
433 	return (0); /* this is the wrong value I'm sure */
434 }
435