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