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