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 static int 163 pbioprobe(device_t dev) 164 { 165 int rid; 166 struct pbio_softc *scp = device_get_softc(dev); 167 #ifdef GENERIC_PBIO_PROBE 168 unsigned char val; 169 #endif 170 171 if (isa_get_logicalid(dev)) /* skip PnP probes */ 172 return (ENXIO); 173 rid = 0; 174 scp->res = bus_alloc_resource_anywhere(dev, SYS_RES_IOPORT, &rid, 175 IO_PBIOSIZE, RF_ACTIVE); 176 if (scp->res == NULL) 177 return (ENXIO); 178 179 #ifdef GENERIC_PBIO_PROBE 180 scp->bst = rman_get_bustag(scp->res); 181 scp->bsh = rman_get_bushandle(scp->res); 182 /* 183 * try see if the device is there. 184 * This probe works only if the device has no I/O attached to it 185 * XXX Better allow flags to abort testing 186 */ 187 /* Set all ports to output */ 188 pboutb(scp, PBIO_CFG, 0x80); 189 printf("pbio val(CFG: 0x%03x)=0x%02x (should be 0x80)\n", 190 rman_get_start(scp->res), pbinb(scp, PBIO_CFG)); 191 pboutb(scp, PBIO_PORTA, 0xa5); 192 val = pbinb(scp, PBIO_PORTA); 193 printf("pbio val=0x%02x (should be 0xa5)\n", val); 194 if (val != 0xa5) { 195 bus_release_resource(dev, SYS_RES_IOPORT, rid, sc->res); 196 return (ENXIO); 197 } 198 pboutb(scp, PBIO_PORTA, 0x5a); 199 val = pbinb(scp, PBIO_PORTA); 200 printf("pbio val=0x%02x (should be 0x5a)\n", val); 201 if (val != 0x5a) { 202 bus_release_resource(dev, SYS_RES_IOPORT, rid, sc->res); 203 return (ENXIO); 204 } 205 #endif 206 device_set_desc(dev, "Intel 8255 PPI (basic mode)"); 207 /* Set all ports to input */ 208 /* pboutb(scp, PBIO_CFG, 0x9b); */ 209 bus_release_resource(dev, SYS_RES_IOPORT, rid, scp->res); 210 return (0); 211 } 212 213 /* 214 * Called if the probe succeeded. 215 * We can be destructive here as we know we have the device. 216 * we can also trust the unit number. 217 */ 218 static int 219 pbioattach (device_t dev) 220 { 221 int unit; 222 int i; 223 int rid; 224 struct pbio_softc *sc; 225 226 sc = device_get_softc(dev); 227 unit = device_get_unit(dev); 228 rid = 0; 229 sc->res = bus_alloc_resource_anywhere(dev, SYS_RES_IOPORT, &rid, 230 IO_PBIOSIZE, RF_ACTIVE); 231 if (sc->res == NULL) 232 return (ENXIO); 233 sc->bst = rman_get_bustag(sc->res); 234 sc->bsh = rman_get_bushandle(sc->res); 235 236 /* 237 * Store whatever seems wise. 238 */ 239 sc->iomode = 0x9b; /* All ports to input */ 240 241 for (i = 0; i < PBIO_NPORTS; i++) 242 sc->pd[i].port = make_dev(&pbio_cdevsw, (unit << 2) + i, 0, 0, 243 0600, "pbio%d%s", unit, PBIO_PNAME(i)); 244 return (0); 245 } 246 247 static int 248 pbioioctl (struct cdev *dev, u_long cmd, caddr_t data, int flag, 249 struct thread *td) 250 { 251 struct pbio_softc *scp; 252 int port, unit; 253 254 unit = UNIT(dev); 255 port = PORT(dev); 256 scp = pbio_addr(unit); 257 if (scp == NULL) 258 return (ENODEV); 259 switch (cmd) { 260 case PBIO_SETDIFF: 261 scp->pd[port].diff = *(int *)data; 262 break; 263 case PBIO_SETIPACE: 264 scp->pd[port].ipace = *(int *)data; 265 break; 266 case PBIO_SETOPACE: 267 scp->pd[port].opace = *(int *)data; 268 break; 269 case PBIO_GETDIFF: 270 *(int *)data = scp->pd[port].diff; 271 break; 272 case PBIO_GETIPACE: 273 *(int *)data = scp->pd[port].ipace; 274 break; 275 case PBIO_GETOPACE: 276 *(int *)data = scp->pd[port].opace; 277 break; 278 default: 279 return ENXIO; 280 } 281 return (0); 282 } 283 284 static int 285 pbioopen(struct cdev *dev, int oflags, int devtype, struct thread *td) 286 { 287 struct pbio_softc *scp; 288 int ocfg, port, unit; 289 int portbit; /* Port configuration bit */ 290 291 unit = UNIT(dev); 292 port = PORT(dev); 293 scp = pbio_addr(unit); 294 if (scp == NULL) 295 return (ENODEV); 296 297 switch (port) { 298 case 0: portbit = 0x10; break; /* Port A */ 299 case 1: portbit = 0x02; break; /* Port B */ 300 case 2: portbit = 0x08; break; /* Port CH */ 301 case 3: portbit = 0x01; break; /* Port CL */ 302 default: return (ENODEV); 303 } 304 ocfg = scp->iomode; 305 306 if (oflags & FWRITE) 307 /* Writing == output; zero the bit */ 308 pboutb(scp, PBIO_CFG, scp->iomode = (ocfg & (~portbit))); 309 else if (oflags & FREAD) 310 /* Reading == input; set the bit */ 311 pboutb(scp, PBIO_CFG, scp->iomode = (ocfg | portbit)); 312 else 313 return (EACCES); 314 315 return (0); 316 } 317 318 static int 319 pbioclose(struct cdev *dev, int fflag, int devtype, struct thread *td) 320 { 321 struct pbio_softc *scp; 322 int unit; 323 324 unit = UNIT(dev); 325 scp = pbio_addr(unit); 326 if (scp == NULL) 327 return (ENODEV); 328 329 return (0); 330 } 331 332 /* 333 * Return the value of a given port on a given I/O base address 334 * Handles the split C port nibbles and blocking 335 */ 336 static int 337 portval(int port, struct pbio_softc *scp, char *val) 338 { 339 int err; 340 341 for (;;) { 342 switch (port) { 343 case 0: 344 *val = pbinb(scp, PBIO_PORTA); 345 break; 346 case 1: 347 *val = pbinb(scp, PBIO_PORTB); 348 break; 349 case 2: 350 *val = (pbinb(scp, PBIO_PORTC) >> 4) & 0xf; 351 break; 352 case 3: 353 *val = pbinb(scp, PBIO_PORTC) & 0xf; 354 break; 355 default: 356 *val = 0; 357 break; 358 } 359 if (scp->pd[port].diff) { 360 if (*val != scp->pd[port].oldval) { 361 scp->pd[port].oldval = *val; 362 return (0); 363 } 364 err = tsleep((caddr_t)&(scp->pd[port].diff), PBIOPRI, 365 "pbiopl", max(1, scp->pd[port].ipace)); 366 if (err == EINTR) 367 return (EINTR); 368 } else 369 return (0); 370 } 371 } 372 373 static int 374 pbioread(struct cdev *dev, struct uio *uio, int ioflag) 375 { 376 struct pbio_softc *scp; 377 int err, i, port, ret, toread, unit; 378 char val; 379 380 unit = UNIT(dev); 381 port = PORT(dev); 382 scp = pbio_addr(unit); 383 if (scp == NULL) 384 return (ENODEV); 385 386 while (uio->uio_resid > 0) { 387 toread = min(uio->uio_resid, PBIO_BUFSIZ); 388 if ((ret = uiomove(scp->pd[port].buff, toread, uio)) != 0) 389 return (ret); 390 for (i = 0; i < toread; i++) { 391 if ((err = portval(port, scp, &val)) != 0) 392 return (err); 393 scp->pd[port].buff[i] = val; 394 if (!scp->pd[port].diff && scp->pd[port].ipace) 395 tsleep((caddr_t)&(scp->pd[port].ipace), PBIOPRI, 396 "pbioip", scp->pd[port].ipace); 397 } 398 } 399 return 0; 400 } 401 402 static int 403 pbiowrite(struct cdev *dev, struct uio *uio, int ioflag) 404 { 405 struct pbio_softc *scp; 406 int i, port, ret, towrite, unit; 407 char val, oval; 408 409 unit = UNIT(dev); 410 port = PORT(dev); 411 scp = pbio_addr(unit); 412 if (scp == NULL) 413 return (ENODEV); 414 415 while (uio->uio_resid > 0) { 416 towrite = min(uio->uio_resid, PBIO_BUFSIZ); 417 if ((ret = uiomove(scp->pd[port].buff, towrite, uio)) != 0) 418 return (ret); 419 for (i = 0; i < towrite; i++) { 420 val = scp->pd[port].buff[i]; 421 switch (port) { 422 case 0: 423 pboutb(scp, PBIO_PORTA, val); 424 break; 425 case 1: 426 pboutb(scp, PBIO_PORTB, val); 427 break; 428 case 2: 429 oval = pbinb(scp, PBIO_PORTC); 430 oval &= 0xf; 431 val <<= 4; 432 pboutb(scp, PBIO_PORTC, val | oval); 433 break; 434 case 3: 435 oval = pbinb(scp, PBIO_PORTC); 436 oval &= 0xf0; 437 val &= 0xf; 438 pboutb(scp, PBIO_PORTC, oval | val); 439 break; 440 } 441 if (scp->pd[port].opace) 442 tsleep((caddr_t)&(scp->pd[port].opace), 443 PBIOPRI, "pbioop", 444 scp->pd[port].opace); 445 } 446 } 447 return (0); 448 } 449 450 static int 451 pbiopoll(struct cdev *dev, int which, struct thread *td) 452 { 453 struct pbio_softc *scp; 454 int unit; 455 456 unit = UNIT(dev); 457 scp = pbio_addr(unit); 458 if (scp == NULL) 459 return (ENODEV); 460 461 /* 462 * Do processing 463 */ 464 return (0); /* this is the wrong value I'm sure */ 465 } 466