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