pbio.c revision 136230
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 * $FreeBSD: head/sys/dev/pbio/pbio.c 136230 2004-10-07 16:21:03Z imp $ 30 * 31 */ 32 33#include <sys/cdefs.h> 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 <sys/pbioio.h> /* pbio IOCTL definitions */ 50#include <sys/uio.h> 51#include <sys/fcntl.h> 52 53/* Function prototypes (these should all be static) */ 54static d_open_t pbioopen; 55static d_close_t pbioclose; 56static d_read_t pbioread; 57static d_write_t pbiowrite; 58static d_ioctl_t pbioioctl; 59static d_poll_t pbiopoll; 60static int pbioprobe(device_t); 61static 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 79static 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 88static 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 */ 103struct 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 */ 115struct pbio_softc { 116 int iobase; /* I/O base */ 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} ; 121 122typedef struct pbio_softc *sc_p; 123 124static device_method_t pbio_methods[] = { 125 /* Device interface */ 126 DEVMETHOD(device_probe, pbioprobe), 127 DEVMETHOD(device_attach, pbioattach), 128 129 { 0, 0 } 130}; 131 132static devclass_t pbio_devclass; 133#define pbio_addr(unit) ((struct pbio_softc *) \ 134 devclass_get_softc(pbio_devclass, unit)) 135 136static char driver_name[] = "pbio"; 137 138static driver_t pbio_driver = { 139 driver_name, 140 pbio_methods, 141 sizeof(struct pbio_softc), 142}; 143 144DRIVER_MODULE(pbio, isa, pbio_driver, pbio_devclass, 0, 0); 145 146static int 147pbioprobe(device_t dev) 148{ 149 int iobase; 150 int rid; 151 struct resource *port; 152#ifdef GENERIC_PBIO_PROBE 153 unsigned char val; 154#endif 155 156 rid = 0; 157 port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 158 0, ~0, IO_PBIOSIZE, RF_ACTIVE); 159 if (!port) 160 return (ENXIO); 161 162 iobase = rman_get_start(port); 163 164#ifdef GENERIC_PBIO_PROBE 165 /* 166 * try see if the device is there. 167 * This probe works only if the device has no I/O attached to it 168 * XXX Better allow flags to abort testing 169 */ 170 /* Set all ports to output */ 171 outb(iobase + PBIO_CFG, 0x80); 172 printf("pbio val(CFG: 0x%03x)=0x%02x (should be 0x80)\n", 173 iobase, inb(iobase + PBIO_CFG)); 174 outb(iobase + PBIO_PORTA, 0xa5); 175 val = inb(iobase + PBIO_PORTA); 176 printf("pbio val=0x%02x (should be 0xa5)\n", val); 177 if (val != 0xa5) 178 return (ENXIO); 179 outb(iobase + PBIO_PORTA, 0x5a); 180 val = inb(iobase + PBIO_PORTA); 181 printf("pbio val=0x%02x (should be 0x5a)\n", val); 182 if (val != 0x5a) 183 return (ENXIO); 184#endif 185 device_set_desc(dev, "Intel 8255 PPI (basic mode)"); 186 /* Set all ports to input */ 187 /* outb(iobase + PBIO_CFG, 0x9b); */ 188 bus_release_resource(dev, SYS_RES_IOPORT, rid, port); 189 return (0); 190} 191 192/* 193 * Called if the probe succeeded. 194 * We can be destructive here as we know we have the device. 195 * we can also trust the unit number. 196 */ 197static int 198pbioattach (device_t dev) 199{ 200 int unit = device_get_unit(dev); 201 int flags; 202 int i; 203 int iobase; 204 int rid; 205 struct resource *port; 206 struct pbio_softc *sc; 207 208 rid = 0; 209 port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 210 0, ~0, IO_PBIOSIZE, RF_ACTIVE); 211 if (!port) 212 return (ENXIO); 213 214 iobase = rman_get_start(port); 215 sc = device_get_softc(dev); 216 217 /* 218 * Allocate storage for this instance . 219 */ 220 bzero(sc, sizeof(*sc)); 221 flags = device_get_flags(dev); 222 223 /* 224 * Store whatever seems wise. 225 */ 226 sc->iobase = iobase; 227 sc->iomode = 0x9b; /* All ports to input */ 228 229 for (i = 0; i < PBIO_NPORTS; i++) 230 sc->pd[i].port = make_dev(&pbio_cdevsw, (unit << 2) + i, 0, 0, 0600, 231 "pbio%d%s", unit, PBIO_PNAME(i)); 232 return 0; 233} 234 235static int 236pbioioctl (struct cdev *dev, u_long cmd, caddr_t data, int flag, 237 struct thread *td) 238{ 239 int unit = UNIT(dev); 240 int port = PORT(dev); 241 struct pbio_softc *scp; 242 243 scp = pbio_addr(unit); 244 if (scp == NULL) 245 return (ENODEV); 246 switch (cmd) { 247 case PBIO_SETDIFF: 248 scp->pd[port].diff = *(int *)data; 249 break; 250 case PBIO_SETIPACE: 251 scp->pd[port].ipace = *(int *)data; 252 break; 253 case PBIO_SETOPACE: 254 scp->pd[port].opace = *(int *)data; 255 break; 256 case PBIO_GETDIFF: 257 *(int *)data = scp->pd[port].diff; 258 break; 259 case PBIO_GETIPACE: 260 *(int *)data = scp->pd[port].ipace; 261 break; 262 case PBIO_GETOPACE: 263 *(int *)data = scp->pd[port].opace; 264 break; 265 default: 266 return ENXIO; 267 } 268 return (0); 269} 270 271static int 272pbioopen(struct cdev *dev, int oflags, int devtype, struct thread *td) 273{ 274 int unit = UNIT(dev); 275 int port = PORT(dev); 276 struct pbio_softc *scp; 277 int ocfg; 278 int portbit; /* Port configuration bit */ 279 280 scp = pbio_addr(unit); 281 if (scp == NULL) 282 return (ENODEV); 283 284 switch (port) { 285 case 0: portbit = 0x10; break; /* Port A */ 286 case 1: portbit = 0x02; break; /* Port B */ 287 case 2: portbit = 0x08; break; /* Port CH */ 288 case 3: portbit = 0x01; break; /* Port CL */ 289 default: return (ENODEV); 290 } 291 ocfg = scp->iomode; 292 293 if (oflags & FWRITE) 294 /* Writing == output; zero the bit */ 295 outb(scp->iobase + PBIO_CFG, scp->iomode = (ocfg & (~portbit))); 296 else if (oflags & FREAD) 297 /* Reading == input; set the bit */ 298 outb(scp->iobase + PBIO_CFG, scp->iomode = (ocfg | portbit)); 299 else 300 return (EACCES); 301 302 return (0); 303} 304 305static int 306pbioclose(struct cdev *dev, int fflag, int devtype, struct thread *td) 307{ 308 int unit = UNIT(dev); 309 struct pbio_softc *scp; 310 311 scp = pbio_addr(unit); 312 if (scp == NULL) 313 return (ENODEV); 314 315 return (0); 316} 317 318/* 319 * Return the value of a given port on a given I/O base address 320 * Handles the split C port nibbles and blocking 321 */ 322static int 323portval(int port, struct pbio_softc *scp, char *val) 324{ 325 int err; 326 327 for (;;) { 328 switch (port) { 329 case 0: 330 *val = inb(scp->iobase + PBIO_PORTA); 331 break; 332 case 1: 333 *val = inb(scp->iobase + PBIO_PORTB); 334 break; 335 case 2: 336 *val = (inb(scp->iobase + PBIO_PORTC) >> 4) & 0xf; 337 break; 338 case 3: 339 *val = inb(scp->iobase + PBIO_PORTC) & 0xf; 340 break; 341 default: 342 *val = 0; 343 break; 344 } 345 if (scp->pd[port].diff) { 346 if (*val != scp->pd[port].oldval) { 347 scp->pd[port].oldval = *val; 348 return (0); 349 } 350 err = tsleep((caddr_t)&(scp->pd[port].diff), PBIOPRI, 351 "pbiopl", max(1, scp->pd[port].ipace)); 352 if (err == EINTR) 353 return (EINTR); 354 } else 355 return (0); 356 } 357} 358 359static int 360pbioread(struct cdev *dev, struct uio *uio, int ioflag) 361{ 362 int unit = UNIT(dev); 363 int port = PORT(dev); 364 struct pbio_softc *scp; 365 int toread; 366 int err; 367 int ret, i; 368 char val; 369 370 scp = pbio_addr(unit); 371 if (scp == NULL) 372 return (ENODEV); 373 374 while (uio->uio_resid > 0) { 375 toread = min(uio->uio_resid, PBIO_BUFSIZ); 376 if ((ret = uiomove(scp->pd[port].buff, toread, uio)) != 0) 377 return (ret); 378 for (i = 0; i < toread; i++) { 379 if ((err = portval(port, scp, &val)) != 0) 380 return (err); 381 scp->pd[port].buff[i] = val; 382 if (!scp->pd[port].diff && scp->pd[port].ipace) 383 tsleep((caddr_t)&(scp->pd[port].ipace), PBIOPRI, 384 "pbioip", scp->pd[port].ipace); 385 } 386 } 387 return 0; 388} 389 390static int 391pbiowrite(struct cdev *dev, struct uio *uio, int ioflag) 392{ 393 int unit = UNIT(dev); 394 int port = PORT(dev); 395 struct pbio_softc *scp; 396 char val, oval; 397 int towrite; 398 int ret; 399 int iobase; 400 int i; 401 402 scp = pbio_addr(unit); 403 if (scp == NULL) 404 return (ENODEV); 405 406 iobase = scp->iobase; 407 while (uio->uio_resid > 0) { 408 towrite = min(uio->uio_resid, PBIO_BUFSIZ); 409 if ((ret = uiomove(scp->pd[port].buff, towrite, uio)) != 0) 410 return (ret); 411 for (i = 0; i < towrite; i++) { 412 val = scp->pd[port].buff[i]; 413 switch (port) { 414 case 0: 415 outb(iobase + PBIO_PORTA, val); 416 break; 417 case 1: 418 outb(iobase + PBIO_PORTB, val); 419 break; 420 case 2: 421 oval = inb(iobase + PBIO_PORTC); 422 oval &= 0xf; 423 val <<= 4; 424 outb(iobase + PBIO_PORTC, val | oval); 425 break; 426 case 3: 427 oval = inb(iobase + PBIO_PORTC); 428 oval &= 0xf0; 429 val &= 0xf; 430 outb(iobase + PBIO_PORTC, oval | val); 431 break; 432 } 433 if (scp->pd[port].opace) 434 tsleep((caddr_t)&(scp->pd[port].opace), 435 PBIOPRI, "pbioop", 436 scp->pd[port].opace); 437 } 438 } 439 return (0); 440} 441 442static int 443pbiopoll(struct cdev *dev, int which, struct thread *td) 444{ 445 int unit = UNIT(dev); 446 struct pbio_softc *scp; 447 448 scp = pbio_addr(unit); 449 if (scp == NULL) 450 return (ENODEV); 451 452 /* 453 * Do processing 454 */ 455 return (0); /* this is the wrong value I'm sure */ 456} 457