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