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