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