1139749Simp/*- 2136230Simp * Copyright (c) 2000-2004 3136230Simp * Diomidis D. Spinellis, Athens, Greece 4136230Simp * All rights reserved. 5136230Simp * 6136230Simp * Redistribution and use in source and binary forms, with or without 7136230Simp * modification, are permitted provided that the following conditions 8136230Simp * are met: 9136230Simp * 1. Redistributions of source code must retain the above copyright 10136230Simp * notice, this list of conditions and the following disclaimer as 11136230Simp * the first lines of this file unmodified. 12136230Simp * 2. Redistributions in binary form must reproduce the above copyright 13136230Simp * notice, this list of conditions and the following disclaimer in the 14136230Simp * documentation and/or other materials provided with the distribution. 15136230Simp * 16136230Simp * THIS SOFTWARE IS PROVIDED BY Diomidis D. Spinellis ``AS IS'' AND ANY 17136230Simp * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18136230Simp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19136230Simp * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Diomidis D. Spinellis BE 20136230Simp * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21136230Simp * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22136230Simp * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 23136230Simp * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 24136230Simp * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 25136230Simp * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 26136230Simp * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27136230Simp * 28136230Simp * $Id: pbio.c,v 1.12 2003/10/11 13:05:08 dds Exp dds $ 29136230Simp */ 30136230Simp 31136230Simp#include <sys/cdefs.h> 32136305Sobrien__FBSDID("$FreeBSD$"); 33136305Sobrien 34136230Simp#include <sys/param.h> 35136230Simp#include <sys/systm.h> 36136230Simp#include <sys/kernel.h> /* SYSINIT stuff */ 37136230Simp#include <sys/bus.h> 38136230Simp#include <sys/resource.h> 39136230Simp#include <sys/syslog.h> 40136230Simp#include <sys/sysctl.h> 41136230Simp#include <sys/conf.h> /* cdevsw stuff */ 42136230Simp#include <sys/malloc.h> /* malloc region definitions */ 43136230Simp#include <sys/module.h> 44136230Simp#include <machine/bus.h> 45136230Simp#include <machine/resource.h> 46136230Simp#include <sys/rman.h> 47137585Sobrien#include <dev/pbio/pbioio.h> /* pbio IOCTL definitions */ 48136230Simp#include <sys/uio.h> 49136230Simp#include <sys/fcntl.h> 50141680Simp#include <isa/isavar.h> 51136230Simp 52136230Simp/* Function prototypes (these should all be static) */ 53136305Sobrienstatic d_open_t pbioopen; 54136305Sobrienstatic d_close_t pbioclose; 55136305Sobrienstatic d_read_t pbioread; 56136305Sobrienstatic d_write_t pbiowrite; 57136305Sobrienstatic d_ioctl_t pbioioctl; 58136305Sobrienstatic d_poll_t pbiopoll; 59136230Simpstatic int pbioprobe(device_t); 60136230Simpstatic int pbioattach(device_t); 61136230Simp 62136230Simp/* Device registers */ 63136230Simp#define PBIO_PORTA 0 64136230Simp#define PBIO_PORTB 1 65136230Simp#define PBIO_PORTC 2 66136230Simp#define PBIO_CFG 3 67136305Sobrien#define PBIO_IOSIZE 4 68136230Simp 69136230Simp/* Per-port buffer size */ 70136305Sobrien#define PBIO_BUFSIZ 64 71136230Simp 72136230Simp/* Number of /dev entries */ 73136305Sobrien#define PBIO_NPORTS 4 74136230Simp 75136230Simp/* I/O port range */ 76136305Sobrien#define IO_PBIOSIZE 4 77136230Simp 78136230Simpstatic char *port_names[] = {"a", "b", "ch", "cl"}; 79136230Simp 80136230Simp#define PBIO_PNAME(n) (port_names[(n)]) 81136230Simp 82183397Sed#define UNIT(dev) (dev2unit(dev) >> 2) 83183397Sed#define PORT(dev) (dev2unit(dev) & 0x3) 84136230Simp 85136230Simp#define PBIOPRI ((PZERO + 5) | PCATCH) 86136230Simp 87136230Simpstatic struct cdevsw pbio_cdevsw = { 88136230Simp .d_version = D_VERSION, 89136230Simp .d_flags = D_NEEDGIANT, 90136230Simp .d_open = pbioopen, 91136230Simp .d_close = pbioclose, 92136230Simp .d_read = pbioread, 93136230Simp .d_write = pbiowrite, 94136230Simp .d_ioctl = pbioioctl, 95136230Simp .d_poll = pbiopoll, 96136230Simp .d_name = "pbio" 97136230Simp}; 98136230Simp 99136230Simp/* 100136230Simp * Data specific to each I/O port 101136230Simp */ 102136230Simpstruct portdata { 103136230Simp struct cdev *port; 104136230Simp int diff; /* When true read only differences */ 105136230Simp int ipace; /* Input pace */ 106136230Simp int opace; /* Output pace */ 107136230Simp char oldval; /* Last value read */ 108136230Simp char buff[PBIO_BUFSIZ]; /* Per-port data buffer */ 109136230Simp}; 110136230Simp 111136230Simp/* 112136230Simp * One of these per allocated device 113136230Simp */ 114136230Simpstruct pbio_softc { 115136305Sobrien struct portdata pd[PBIO_NPORTS];/* Per port data */ 116136230Simp int iomode; /* Virtualized I/O mode port value */ 117136230Simp /* The real port is write-only */ 118136341Simp struct resource *res; 119136341Simp bus_space_tag_t bst; 120136341Simp bus_space_handle_t bsh; 121136341Simp}; 122136230Simp 123136230Simptypedef struct pbio_softc *sc_p; 124136230Simp 125136230Simpstatic device_method_t pbio_methods[] = { 126136230Simp /* Device interface */ 127136230Simp DEVMETHOD(device_probe, pbioprobe), 128136230Simp DEVMETHOD(device_attach, pbioattach), 129136230Simp { 0, 0 } 130136230Simp}; 131136230Simp 132136230Simpstatic devclass_t pbio_devclass; 133136305Sobrien#define pbio_addr(unit) \ 134136305Sobrien ((struct pbio_softc *) devclass_get_softc(pbio_devclass, unit)) 135136230Simp 136136230Simpstatic char driver_name[] = "pbio"; 137136230Simp 138136230Simpstatic driver_t pbio_driver = { 139136230Simp driver_name, 140136230Simp pbio_methods, 141136230Simp sizeof(struct pbio_softc), 142136230Simp}; 143136230Simp 144136230SimpDRIVER_MODULE(pbio, isa, pbio_driver, pbio_devclass, 0, 0); 145136230Simp 146136341Simpstatic __inline uint8_t 147136341Simppbinb(struct pbio_softc *scp, int off) 148136341Simp{ 149136341Simp 150136341Simp return bus_space_read_1(scp->bst, scp->bsh, off); 151136341Simp} 152136341Simp 153136341Simpstatic __inline void 154136341Simppboutb(struct pbio_softc *scp, int off, uint8_t val) 155136341Simp{ 156136341Simp 157136341Simp bus_space_write_1(scp->bst, scp->bsh, off, val); 158136341Simp} 159136341Simp 160136341Simp 161136230Simpstatic int 162136230Simppbioprobe(device_t dev) 163136230Simp{ 164136341Simp int rid; 165136341Simp struct pbio_softc *scp = device_get_softc(dev); 166136230Simp#ifdef GENERIC_PBIO_PROBE 167136230Simp unsigned char val; 168136230Simp#endif 169136230Simp 170141680Simp if (isa_get_logicalid(dev)) /* skip PnP probes */ 171141680Simp return (ENXIO); 172136230Simp rid = 0; 173136341Simp scp->res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 174136341Simp 0, ~0, IO_PBIOSIZE, RF_ACTIVE); 175136341Simp if (scp->res == NULL) 176136230Simp return (ENXIO); 177136230Simp 178136230Simp#ifdef GENERIC_PBIO_PROBE 179136341Simp scp->bst = rman_get_bustag(scp->res); 180136341Simp scp->bsh = rman_get_bushandle(scp->res); 181136230Simp /* 182136230Simp * try see if the device is there. 183136230Simp * This probe works only if the device has no I/O attached to it 184136230Simp * XXX Better allow flags to abort testing 185136230Simp */ 186136230Simp /* Set all ports to output */ 187136341Simp pboutb(scp, PBIO_CFG, 0x80); 188136230Simp printf("pbio val(CFG: 0x%03x)=0x%02x (should be 0x80)\n", 189136362Simp rman_get_start(scp->res), pbinb(scp, PBIO_CFG)); 190136341Simp pboutb(scp, PBIO_PORTA, 0xa5); 191136341Simp val = pbinb(scp, PBIO_PORTA); 192136230Simp printf("pbio val=0x%02x (should be 0xa5)\n", val); 193136341Simp if (val != 0xa5) { 194136341Simp bus_release_resource(dev, SYS_RES_IOPORT, rid, sc->res); 195136230Simp return (ENXIO); 196136341Simp } 197136341Simp pboutb(scp, PBIO_PORTA, 0x5a); 198136341Simp val = pbinb(scp, PBIO_PORTA); 199136230Simp printf("pbio val=0x%02x (should be 0x5a)\n", val); 200136341Simp if (val != 0x5a) { 201136341Simp bus_release_resource(dev, SYS_RES_IOPORT, rid, sc->res); 202136230Simp return (ENXIO); 203136341Simp } 204136230Simp#endif 205136230Simp device_set_desc(dev, "Intel 8255 PPI (basic mode)"); 206136230Simp /* Set all ports to input */ 207136341Simp /* pboutb(scp, PBIO_CFG, 0x9b); */ 208136341Simp bus_release_resource(dev, SYS_RES_IOPORT, rid, scp->res); 209136230Simp return (0); 210136230Simp} 211136230Simp 212136230Simp/* 213136230Simp * Called if the probe succeeded. 214136230Simp * We can be destructive here as we know we have the device. 215136230Simp * we can also trust the unit number. 216136230Simp */ 217136230Simpstatic int 218136230Simppbioattach (device_t dev) 219136230Simp{ 220136341Simp int unit; 221136341Simp int i; 222136341Simp int rid; 223136230Simp struct pbio_softc *sc; 224136230Simp 225136341Simp sc = device_get_softc(dev); 226136305Sobrien unit = device_get_unit(dev); 227136230Simp rid = 0; 228136341Simp sc->res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 229136341Simp 0, ~0, IO_PBIOSIZE, RF_ACTIVE); 230136341Simp if (sc->res == NULL) 231136230Simp return (ENXIO); 232136341Simp sc->bst = rman_get_bustag(sc->res); 233136341Simp sc->bsh = rman_get_bushandle(sc->res); 234136230Simp 235136230Simp /* 236136230Simp * Store whatever seems wise. 237136230Simp */ 238136230Simp sc->iomode = 0x9b; /* All ports to input */ 239136230Simp 240136230Simp for (i = 0; i < PBIO_NPORTS; i++) 241136305Sobrien sc->pd[i].port = make_dev(&pbio_cdevsw, (unit << 2) + i, 0, 0, 242136305Sobrien 0600, "pbio%d%s", unit, PBIO_PNAME(i)); 243136305Sobrien return (0); 244136230Simp} 245136230Simp 246136230Simpstatic int 247136230Simppbioioctl (struct cdev *dev, u_long cmd, caddr_t data, int flag, 248136230Simp struct thread *td) 249136230Simp{ 250136230Simp struct pbio_softc *scp; 251136305Sobrien int port, unit; 252136230Simp 253136305Sobrien unit = UNIT(dev); 254136305Sobrien port = PORT(dev); 255136230Simp scp = pbio_addr(unit); 256136230Simp if (scp == NULL) 257136230Simp return (ENODEV); 258136230Simp switch (cmd) { 259136305Sobrien case PBIO_SETDIFF: 260136230Simp scp->pd[port].diff = *(int *)data; 261136230Simp break; 262136305Sobrien case PBIO_SETIPACE: 263136230Simp scp->pd[port].ipace = *(int *)data; 264136230Simp break; 265136305Sobrien case PBIO_SETOPACE: 266136230Simp scp->pd[port].opace = *(int *)data; 267136230Simp break; 268136305Sobrien case PBIO_GETDIFF: 269136230Simp *(int *)data = scp->pd[port].diff; 270136230Simp break; 271136305Sobrien case PBIO_GETIPACE: 272136230Simp *(int *)data = scp->pd[port].ipace; 273136230Simp break; 274136305Sobrien case PBIO_GETOPACE: 275136230Simp *(int *)data = scp->pd[port].opace; 276136230Simp break; 277136305Sobrien default: 278136230Simp return ENXIO; 279136230Simp } 280136230Simp return (0); 281136230Simp} 282136230Simp 283136230Simpstatic int 284136230Simppbioopen(struct cdev *dev, int oflags, int devtype, struct thread *td) 285136230Simp{ 286136230Simp struct pbio_softc *scp; 287136305Sobrien int ocfg, port, unit; 288136230Simp int portbit; /* Port configuration bit */ 289136230Simp 290136305Sobrien unit = UNIT(dev); 291136305Sobrien port = PORT(dev); 292136230Simp scp = pbio_addr(unit); 293136230Simp if (scp == NULL) 294136230Simp return (ENODEV); 295136230Simp 296136230Simp switch (port) { 297136230Simp case 0: portbit = 0x10; break; /* Port A */ 298136230Simp case 1: portbit = 0x02; break; /* Port B */ 299136230Simp case 2: portbit = 0x08; break; /* Port CH */ 300136230Simp case 3: portbit = 0x01; break; /* Port CL */ 301136230Simp default: return (ENODEV); 302136230Simp } 303136230Simp ocfg = scp->iomode; 304136230Simp 305136230Simp if (oflags & FWRITE) 306136230Simp /* Writing == output; zero the bit */ 307136341Simp pboutb(scp, PBIO_CFG, scp->iomode = (ocfg & (~portbit))); 308136230Simp else if (oflags & FREAD) 309136230Simp /* Reading == input; set the bit */ 310136341Simp pboutb(scp, PBIO_CFG, scp->iomode = (ocfg | portbit)); 311136230Simp else 312136230Simp return (EACCES); 313136230Simp 314136230Simp return (0); 315136230Simp} 316136230Simp 317136230Simpstatic int 318136230Simppbioclose(struct cdev *dev, int fflag, int devtype, struct thread *td) 319136230Simp{ 320136230Simp struct pbio_softc *scp; 321136305Sobrien int unit; 322136230Simp 323136305Sobrien unit = UNIT(dev); 324136230Simp scp = pbio_addr(unit); 325136230Simp if (scp == NULL) 326136230Simp return (ENODEV); 327136230Simp 328136230Simp return (0); 329136230Simp} 330136230Simp 331136230Simp/* 332136230Simp * Return the value of a given port on a given I/O base address 333136230Simp * Handles the split C port nibbles and blocking 334136230Simp */ 335136230Simpstatic int 336136230Simpportval(int port, struct pbio_softc *scp, char *val) 337136230Simp{ 338136230Simp int err; 339136230Simp 340136230Simp for (;;) { 341136230Simp switch (port) { 342136230Simp case 0: 343136341Simp *val = pbinb(scp, PBIO_PORTA); 344136230Simp break; 345136230Simp case 1: 346136341Simp *val = pbinb(scp, PBIO_PORTB); 347136230Simp break; 348136230Simp case 2: 349136341Simp *val = (pbinb(scp, PBIO_PORTC) >> 4) & 0xf; 350136230Simp break; 351136230Simp case 3: 352136341Simp *val = pbinb(scp, PBIO_PORTC) & 0xf; 353136230Simp break; 354136230Simp default: 355136230Simp *val = 0; 356136230Simp break; 357136230Simp } 358136230Simp if (scp->pd[port].diff) { 359136230Simp if (*val != scp->pd[port].oldval) { 360136230Simp scp->pd[port].oldval = *val; 361136230Simp return (0); 362136230Simp } 363136230Simp err = tsleep((caddr_t)&(scp->pd[port].diff), PBIOPRI, 364136230Simp "pbiopl", max(1, scp->pd[port].ipace)); 365136230Simp if (err == EINTR) 366136230Simp return (EINTR); 367136230Simp } else 368136230Simp return (0); 369136230Simp } 370136230Simp} 371136230Simp 372136230Simpstatic int 373136230Simppbioread(struct cdev *dev, struct uio *uio, int ioflag) 374136230Simp{ 375136230Simp struct pbio_softc *scp; 376136305Sobrien int err, i, port, ret, toread, unit; 377136230Simp char val; 378136230Simp 379136305Sobrien unit = UNIT(dev); 380136305Sobrien port = PORT(dev); 381136230Simp scp = pbio_addr(unit); 382136230Simp if (scp == NULL) 383136230Simp return (ENODEV); 384136230Simp 385136230Simp while (uio->uio_resid > 0) { 386136230Simp toread = min(uio->uio_resid, PBIO_BUFSIZ); 387136230Simp if ((ret = uiomove(scp->pd[port].buff, toread, uio)) != 0) 388136230Simp return (ret); 389136230Simp for (i = 0; i < toread; i++) { 390136230Simp if ((err = portval(port, scp, &val)) != 0) 391136230Simp return (err); 392136230Simp scp->pd[port].buff[i] = val; 393136230Simp if (!scp->pd[port].diff && scp->pd[port].ipace) 394136230Simp tsleep((caddr_t)&(scp->pd[port].ipace), PBIOPRI, 395136230Simp "pbioip", scp->pd[port].ipace); 396136230Simp } 397136230Simp } 398136230Simp return 0; 399136230Simp} 400136230Simp 401136305Sobrienstatic int 402136230Simppbiowrite(struct cdev *dev, struct uio *uio, int ioflag) 403136230Simp{ 404136230Simp struct pbio_softc *scp; 405136362Simp int i, port, ret, towrite, unit; 406136230Simp char val, oval; 407136230Simp 408136305Sobrien unit = UNIT(dev); 409136305Sobrien port = PORT(dev); 410136230Simp scp = pbio_addr(unit); 411136230Simp if (scp == NULL) 412136230Simp return (ENODEV); 413136230Simp 414136230Simp while (uio->uio_resid > 0) { 415136230Simp towrite = min(uio->uio_resid, PBIO_BUFSIZ); 416136230Simp if ((ret = uiomove(scp->pd[port].buff, towrite, uio)) != 0) 417136230Simp return (ret); 418136230Simp for (i = 0; i < towrite; i++) { 419136230Simp val = scp->pd[port].buff[i]; 420136230Simp switch (port) { 421136230Simp case 0: 422136341Simp pboutb(scp, PBIO_PORTA, val); 423136230Simp break; 424136230Simp case 1: 425136341Simp pboutb(scp, PBIO_PORTB, val); 426136230Simp break; 427136230Simp case 2: 428136341Simp oval = pbinb(scp, PBIO_PORTC); 429136230Simp oval &= 0xf; 430136230Simp val <<= 4; 431136341Simp pboutb(scp, PBIO_PORTC, val | oval); 432136230Simp break; 433136230Simp case 3: 434136341Simp oval = pbinb(scp, PBIO_PORTC); 435136230Simp oval &= 0xf0; 436136230Simp val &= 0xf; 437136341Simp pboutb(scp, PBIO_PORTC, oval | val); 438136230Simp break; 439136230Simp } 440136230Simp if (scp->pd[port].opace) 441136230Simp tsleep((caddr_t)&(scp->pd[port].opace), 442136230Simp PBIOPRI, "pbioop", 443136230Simp scp->pd[port].opace); 444136230Simp } 445136230Simp } 446136230Simp return (0); 447136230Simp} 448136230Simp 449136230Simpstatic int 450136230Simppbiopoll(struct cdev *dev, int which, struct thread *td) 451136230Simp{ 452136230Simp struct pbio_softc *scp; 453136305Sobrien int unit; 454136230Simp 455136305Sobrien unit = UNIT(dev); 456136230Simp scp = pbio_addr(unit); 457136230Simp if (scp == NULL) 458136230Simp return (ENODEV); 459136230Simp 460136230Simp /* 461136230Simp * Do processing 462136230Simp */ 463136230Simp return (0); /* this is the wrong value I'm sure */ 464136230Simp} 465