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) (minor(dev) >> 2) 83 #define PORT(dev) (minor(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 } 465