]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sys/dev/pbio/pbio.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / sys / dev / pbio / pbio.c
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$");
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) */
53 static  d_open_t        pbioopen;
54 static  d_close_t       pbioclose;
55 static  d_read_t        pbioread;
56 static  d_write_t       pbiowrite;
57 static  d_ioctl_t       pbioioctl;
58 static  d_poll_t        pbiopoll;
59 static  int             pbioprobe(device_t);
60 static  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
78 static 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
87 static 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  */
102 struct 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  */
114 struct 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
123 typedef struct pbio_softc *sc_p;
124
125 static device_method_t pbio_methods[] = {
126         /* Device interface */
127         DEVMETHOD(device_probe,         pbioprobe),
128         DEVMETHOD(device_attach,        pbioattach),
129         { 0, 0 }
130 };
131
132 static  devclass_t      pbio_devclass;
133 #define pbio_addr(unit) \
134             ((struct pbio_softc *) devclass_get_softc(pbio_devclass, unit))
135
136 static char driver_name[] = "pbio";
137
138 static driver_t pbio_driver = {
139         driver_name,
140         pbio_methods,
141         sizeof(struct pbio_softc),
142 };
143
144 DRIVER_MODULE(pbio, isa, pbio_driver, pbio_devclass, 0, 0);
145
146 static __inline uint8_t
147 pbinb(struct pbio_softc *scp, int off)
148 {
149
150         return bus_space_read_1(scp->bst, scp->bsh, off);
151 }
152
153 static __inline void
154 pboutb(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
161 static int
162 pbioprobe(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(dev, SYS_RES_IOPORT, &rid,
174             0, ~0, 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  */
217 static int
218 pbioattach (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(dev, SYS_RES_IOPORT, &rid,
229             0, ~0, 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
246 static int
247 pbioioctl (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
283 static  int
284 pbioopen(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
317 static  int
318 pbioclose(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  */
335 static int
336 portval(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
372 static  int
373 pbioread(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
401 static int
402 pbiowrite(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
449 static  int
450 pbiopoll(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 }