pbio.c revision 136230
150397Sobrien/* 290075Sobrien * Copyright (c) 2000-2004 390075Sobrien * Diomidis D. Spinellis, Athens, Greece 418334Speter * All rights reserved. 518334Speter * 618334Speter * Redistribution and use in source and binary forms, with or without 718334Speter * modification, are permitted provided that the following conditions 818334Speter * are met: 918334Speter * 1. Redistributions of source code must retain the above copyright 1018334Speter * notice, this list of conditions and the following disclaimer as 1118334Speter * the first lines of this file unmodified. 1218334Speter * 2. Redistributions in binary form must reproduce the above copyright 1318334Speter * notice, this list of conditions and the following disclaimer in the 1418334Speter * documentation and/or other materials provided with the distribution. 1518334Speter * 1618334Speter * THIS SOFTWARE IS PROVIDED BY Diomidis D. Spinellis ``AS IS'' AND ANY 1718334Speter * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1818334Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 1918334Speter * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Diomidis D. Spinellis BE 2018334Speter * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2118334Speter * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2218334Speter * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 2318334Speter * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 2418334Speter * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 2518334Speter * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 2690075Sobrien * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2790075Sobrien * 2890075Sobrien * $Id: pbio.c,v 1.12 2003/10/11 13:05:08 dds Exp dds $ 2990075Sobrien * $FreeBSD: head/sys/dev/pbio/pbio.c 136230 2004-10-07 16:21:03Z imp $ 3090075Sobrien * 3190075Sobrien */ 3290075Sobrien 3390075Sobrien#include <sys/cdefs.h> 3490075Sobrien#include <sys/param.h> 3518334Speter#include <sys/systm.h> 3618334Speter#include <sys/kernel.h> /* SYSINIT stuff */ 3718334Speter#include <sys/bus.h> 3818334Speter#include <sys/resource.h> 3918334Speter#include <sys/syslog.h> 4018334Speter#include <sys/sysctl.h> 4118334Speter#include <sys/conf.h> /* cdevsw stuff */ 4218334Speter#include <sys/malloc.h> /* malloc region definitions */ 4318334Speter#include <sys/module.h> 4450397Sobrien#include <machine/bus_pio.h> 4550397Sobrien#include <machine/bus.h> 4650397Sobrien#include <machine/resource.h> 4718334Speter#include <machine/clock.h> /* DELAY() */ 4818334Speter#include <sys/rman.h> 4990075Sobrien#include <sys/pbioio.h> /* pbio IOCTL definitions */ 5018334Speter#include <sys/uio.h> 5190075Sobrien#include <sys/fcntl.h> 5290075Sobrien 5390075Sobrien/* Function prototypes (these should all be static) */ 5418334Speterstatic d_open_t pbioopen; 5590075Sobrienstatic d_close_t pbioclose; 5690075Sobrienstatic d_read_t pbioread; 5718334Speterstatic d_write_t pbiowrite; 5818334Speterstatic d_ioctl_t pbioioctl; 5918334Speterstatic d_poll_t pbiopoll; 6018334Speterstatic int pbioprobe(device_t); 6190075Sobrienstatic int pbioattach(device_t); 6218334Speter 6390075Sobrien/* Device registers */ 6418334Speter#define PBIO_PORTA 0 6518334Speter#define PBIO_PORTB 1 6690075Sobrien#define PBIO_PORTC 2 6790075Sobrien#define PBIO_CFG 3 6890075Sobrien#define PBIO_IOSIZE 4 6990075Sobrien 7090075Sobrien/* Per-port buffer size */ 7190075Sobrien#define PBIO_BUFSIZ 64 7290075Sobrien 7390075Sobrien/* Number of /dev entries */ 7490075Sobrien#define PBIO_NPORTS 4 7518334Speter 7618334Speter/* I/O port range */ 7718334Speter#define IO_PBIOSIZE 4 7818334Speter 7918334Speterstatic char *port_names[] = {"a", "b", "ch", "cl"}; 8018334Speter 8118334Speter#define PBIO_PNAME(n) (port_names[(n)]) 8218334Speter 8318334Speter#define UNIT(dev) (minor(dev) >> 2) 8418334Speter#define PORT(dev) (minor(dev) & 0x3) 8518334Speter 8618334Speter#define PBIOPRI ((PZERO + 5) | PCATCH) 8718334Speter 8890075Sobrienstatic struct cdevsw pbio_cdevsw = { 8918334Speter .d_version = D_VERSION, 9018334Speter .d_flags = D_NEEDGIANT, 9148743Sobrien .d_open = pbioopen, 9250397Sobrien .d_close = pbioclose, 9318334Speter .d_read = pbioread, 9450397Sobrien .d_write = pbiowrite, 9518334Speter .d_ioctl = pbioioctl, 9618334Speter .d_poll = pbiopoll, 9750397Sobrien .d_name = "pbio" 9850397Sobrien}; 9948743Sobrien 10018334Speter/* 10118334Speter * Data specific to each I/O port 10218334Speter */ 10318334Speterstruct portdata { 10418334Speter struct cdev *port; 10518334Speter int diff; /* When true read only differences */ 10618334Speter int ipace; /* Input pace */ 10718334Speter int opace; /* Output pace */ 10818334Speter char oldval; /* Last value read */ 10918334Speter char buff[PBIO_BUFSIZ]; /* Per-port data buffer */ 11018334Speter}; 11118334Speter 11218334Speter/* 11318334Speter * One of these per allocated device 11490075Sobrien */ 11518334Speterstruct pbio_softc { 11618334Speter int iobase; /* I/O base */ 11748743Sobrien struct portdata pd[PBIO_NPORTS]; /* Per port data */ 11818334Speter int iomode; /* Virtualized I/O mode port value */ 11918334Speter /* The real port is write-only */ 12018334Speter} ; 12118334Speter 12218334Spetertypedef struct pbio_softc *sc_p; 12318334Speter 12418334Speterstatic device_method_t pbio_methods[] = { 12518334Speter /* Device interface */ 12618334Speter DEVMETHOD(device_probe, pbioprobe), 12718334Speter DEVMETHOD(device_attach, pbioattach), 12818334Speter 12918334Speter { 0, 0 } 13018334Speter}; 13118334Speter 13218334Speterstatic devclass_t pbio_devclass; 13318334Speter#define pbio_addr(unit) ((struct pbio_softc *) \ 13418334Speter devclass_get_softc(pbio_devclass, unit)) 13550397Sobrien 13648743Sobrienstatic char driver_name[] = "pbio"; 13748743Sobrien 13848743Sobrienstatic driver_t pbio_driver = { 13948743Sobrien driver_name, 14048743Sobrien pbio_methods, 14148743Sobrien sizeof(struct pbio_softc), 14248743Sobrien}; 14350397Sobrien 14418334SpeterDRIVER_MODULE(pbio, isa, pbio_driver, pbio_devclass, 0, 0); 14550397Sobrien 14650397Sobrienstatic int 14750397Sobrienpbioprobe(device_t dev) 14850397Sobrien{ 14950397Sobrien int iobase; 15050397Sobrien int rid; 15150397Sobrien struct resource *port; 15250397Sobrien#ifdef GENERIC_PBIO_PROBE 15350397Sobrien unsigned char val; 15450397Sobrien#endif 15550397Sobrien 15650397Sobrien rid = 0; 15750397Sobrien port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 15850397Sobrien 0, ~0, IO_PBIOSIZE, RF_ACTIVE); 15950397Sobrien if (!port) 16052284Sobrien return (ENXIO); 16152284Sobrien 16252284Sobrien iobase = rman_get_start(port); 16352284Sobrien 16452284Sobrien#ifdef GENERIC_PBIO_PROBE 16552284Sobrien /* 16652284Sobrien * try see if the device is there. 16750397Sobrien * This probe works only if the device has no I/O attached to it 16890075Sobrien * XXX Better allow flags to abort testing 16990075Sobrien */ 17090075Sobrien /* Set all ports to output */ 17190075Sobrien outb(iobase + PBIO_CFG, 0x80); 17290075Sobrien printf("pbio val(CFG: 0x%03x)=0x%02x (should be 0x80)\n", 17390075Sobrien iobase, inb(iobase + PBIO_CFG)); 17490075Sobrien outb(iobase + PBIO_PORTA, 0xa5); 17590075Sobrien val = inb(iobase + PBIO_PORTA); 17690075Sobrien printf("pbio val=0x%02x (should be 0xa5)\n", val); 17790075Sobrien if (val != 0xa5) 17890075Sobrien return (ENXIO); 17990075Sobrien outb(iobase + PBIO_PORTA, 0x5a); 18090075Sobrien val = inb(iobase + PBIO_PORTA); 18190075Sobrien printf("pbio val=0x%02x (should be 0x5a)\n", val); 18290075Sobrien if (val != 0x5a) 18390075Sobrien return (ENXIO); 18490075Sobrien#endif 18590075Sobrien device_set_desc(dev, "Intel 8255 PPI (basic mode)"); 18690075Sobrien /* Set all ports to input */ 18790075Sobrien /* outb(iobase + PBIO_CFG, 0x9b); */ 18890075Sobrien bus_release_resource(dev, SYS_RES_IOPORT, rid, port); 18990075Sobrien return (0); 19090075Sobrien} 19190075Sobrien 19290075Sobrien/* 19390075Sobrien * Called if the probe succeeded. 19490075Sobrien * We can be destructive here as we know we have the device. 19590075Sobrien * we can also trust the unit number. 19690075Sobrien */ 19790075Sobrienstatic int 19890075Sobrienpbioattach (device_t dev) 19990075Sobrien{ 20090075Sobrien int unit = device_get_unit(dev); 20190075Sobrien int flags; 20290075Sobrien int i; 20390075Sobrien int iobase; 20490075Sobrien int rid; 20590075Sobrien struct resource *port; 20690075Sobrien struct pbio_softc *sc; 20790075Sobrien 20890075Sobrien rid = 0; 20990075Sobrien port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 21090075Sobrien 0, ~0, IO_PBIOSIZE, RF_ACTIVE); 21190075Sobrien if (!port) 21290075Sobrien return (ENXIO); 21390075Sobrien 21490075Sobrien iobase = rman_get_start(port); 21590075Sobrien sc = device_get_softc(dev); 21690075Sobrien 21790075Sobrien /* 21890075Sobrien * Allocate storage for this instance . 21990075Sobrien */ 22090075Sobrien bzero(sc, sizeof(*sc)); 22190075Sobrien flags = device_get_flags(dev); 22290075Sobrien 22390075Sobrien /* 22490075Sobrien * Store whatever seems wise. 22590075Sobrien */ 22690075Sobrien sc->iobase = iobase; 22790075Sobrien sc->iomode = 0x9b; /* All ports to input */ 22890075Sobrien 22990075Sobrien for (i = 0; i < PBIO_NPORTS; i++) 23090075Sobrien sc->pd[i].port = make_dev(&pbio_cdevsw, (unit << 2) + i, 0, 0, 0600, 23190075Sobrien "pbio%d%s", unit, PBIO_PNAME(i)); 23290075Sobrien return 0; 23390075Sobrien} 23490075Sobrien 23590075Sobrienstatic int 23690075Sobrienpbioioctl (struct cdev *dev, u_long cmd, caddr_t data, int flag, 23790075Sobrien struct thread *td) 23890075Sobrien{ 23990075Sobrien int unit = UNIT(dev); 24090075Sobrien int port = PORT(dev); 24190075Sobrien struct pbio_softc *scp; 24290075Sobrien 24390075Sobrien scp = pbio_addr(unit); 24490075Sobrien if (scp == NULL) 24590075Sobrien return (ENODEV); 24690075Sobrien switch (cmd) { 24790075Sobrien case PBIO_SETDIFF: 24890075Sobrien scp->pd[port].diff = *(int *)data; 24990075Sobrien break; 25090075Sobrien case PBIO_SETIPACE: 25190075Sobrien scp->pd[port].ipace = *(int *)data; 25290075Sobrien break; 25390075Sobrien case PBIO_SETOPACE: 25490075Sobrien scp->pd[port].opace = *(int *)data; 25590075Sobrien break; 25690075Sobrien case PBIO_GETDIFF: 25790075Sobrien *(int *)data = scp->pd[port].diff; 25890075Sobrien break; 25990075Sobrien case PBIO_GETIPACE: 26090075Sobrien *(int *)data = scp->pd[port].ipace; 26190075Sobrien break; 26290075Sobrien case PBIO_GETOPACE: 26390075Sobrien *(int *)data = scp->pd[port].opace; 26490075Sobrien break; 26590075Sobrien default: 26690075Sobrien return ENXIO; 26790075Sobrien } 26890075Sobrien return (0); 26990075Sobrien} 27090075Sobrien 27190075Sobrienstatic int 27290075Sobrienpbioopen(struct cdev *dev, int oflags, int devtype, struct thread *td) 27390075Sobrien{ 27490075Sobrien int unit = UNIT(dev); 27590075Sobrien int port = PORT(dev); 27690075Sobrien struct pbio_softc *scp; 27790075Sobrien int ocfg; 27890075Sobrien int portbit; /* Port configuration bit */ 27990075Sobrien 28090075Sobrien 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