1/*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2000-2004 5 * Diomidis D. Spinellis, Athens, Greece 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer as 13 * the first lines of this file unmodified. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY Diomidis D. Spinellis ``AS IS'' AND ANY 19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 21 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Diomidis D. Spinellis BE 22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 25 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 27 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 28 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 * 30 * $Id: pbio.c,v 1.12 2003/10/11 13:05:08 dds Exp dds $ 31 */ 32 33#include <sys/cdefs.h> 34__FBSDID("$FreeBSD$"); 35 36#include <sys/param.h> 37#include <sys/systm.h> 38#include <sys/kernel.h> /* SYSINIT stuff */ 39#include <sys/bus.h> 40#include <sys/resource.h> 41#include <sys/syslog.h> 42#include <sys/sysctl.h> 43#include <sys/conf.h> /* cdevsw stuff */ 44#include <sys/malloc.h> /* malloc region definitions */ 45#include <sys/module.h> 46#include <machine/bus.h> 47#include <machine/resource.h> 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#include <isa/isavar.h> 53 54/* Function prototypes (these should all be static) */ 55static d_open_t pbioopen; 56static d_close_t pbioclose; 57static d_read_t pbioread; 58static d_write_t pbiowrite; 59static d_ioctl_t pbioioctl; 60static d_poll_t pbiopoll; 61static int pbioprobe(device_t); 62static int pbioattach(device_t); 63 64/* Device registers */ 65#define PBIO_PORTA 0 66#define PBIO_PORTB 1 67#define PBIO_PORTC 2 68#define PBIO_CFG 3 69#define PBIO_IOSIZE 4 70 71/* Per-port buffer size */ 72#define PBIO_BUFSIZ 64 73 74/* Number of /dev entries */ 75#define PBIO_NPORTS 4 76 77/* I/O port range */ 78#define IO_PBIOSIZE 4 79 80static char *port_names[] = {"a", "b", "ch", "cl"}; 81 82#define PBIO_PNAME(n) (port_names[(n)]) 83 84#define UNIT(dev) (dev2unit(dev) >> 2) 85#define PORT(dev) (dev2unit(dev) & 0x3) 86 87#define PBIOPRI ((PZERO + 5) | PCATCH) 88 89static struct cdevsw pbio_cdevsw = { 90 .d_version = D_VERSION, 91 .d_flags = D_NEEDGIANT, 92 .d_open = pbioopen, 93 .d_close = pbioclose, 94 .d_read = pbioread, 95 .d_write = pbiowrite, 96 .d_ioctl = pbioioctl, 97 .d_poll = pbiopoll, 98 .d_name = "pbio" 99}; 100 101/* 102 * Data specific to each I/O port 103 */ 104struct portdata { 105 struct cdev *port; 106 int diff; /* When true read only differences */ 107 int ipace; /* Input pace */ 108 int opace; /* Output pace */ 109 char oldval; /* Last value read */ 110 char buff[PBIO_BUFSIZ]; /* Per-port data buffer */ 111}; 112 113/* 114 * One of these per allocated device 115 */ 116struct pbio_softc { 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 struct resource *res; 121 bus_space_tag_t bst; 122 bus_space_handle_t bsh; 123}; 124 125typedef struct pbio_softc *sc_p; 126 127static device_method_t pbio_methods[] = { 128 /* Device interface */ 129 DEVMETHOD(device_probe, pbioprobe), 130 DEVMETHOD(device_attach, pbioattach), 131 { 0, 0 } 132}; 133 134static devclass_t pbio_devclass; 135#define pbio_addr(unit) \ 136 ((struct pbio_softc *) devclass_get_softc(pbio_devclass, unit)) 137 138static char driver_name[] = "pbio"; 139 140static driver_t pbio_driver = { 141 driver_name, 142 pbio_methods, 143 sizeof(struct pbio_softc), 144}; 145 146DRIVER_MODULE(pbio, isa, pbio_driver, pbio_devclass, 0, 0); 147 148static __inline uint8_t 149pbinb(struct pbio_softc *scp, int off) 150{ 151 152 return bus_space_read_1(scp->bst, scp->bsh, off); 153} 154 155static __inline void 156pboutb(struct pbio_softc *scp, int off, uint8_t val) 157{ 158 159 bus_space_write_1(scp->bst, scp->bsh, off, val); 160} 161 162 163static int 164pbioprobe(device_t dev) 165{ 166 int rid; 167 struct pbio_softc *scp = device_get_softc(dev); 168#ifdef GENERIC_PBIO_PROBE 169 unsigned char val; 170#endif 171 172 if (isa_get_logicalid(dev)) /* skip PnP probes */ 173 return (ENXIO); 174 rid = 0; 175 scp->res = bus_alloc_resource_anywhere(dev, SYS_RES_IOPORT, &rid, 176 IO_PBIOSIZE, RF_ACTIVE); 177 if (scp->res == NULL) 178 return (ENXIO); 179 180#ifdef GENERIC_PBIO_PROBE 181 scp->bst = rman_get_bustag(scp->res); 182 scp->bsh = rman_get_bushandle(scp->res); 183 /* 184 * try see if the device is there. 185 * This probe works only if the device has no I/O attached to it 186 * XXX Better allow flags to abort testing 187 */ 188 /* Set all ports to output */ 189 pboutb(scp, PBIO_CFG, 0x80); 190 printf("pbio val(CFG: 0x%03x)=0x%02x (should be 0x80)\n", 191 rman_get_start(scp->res), pbinb(scp, PBIO_CFG)); 192 pboutb(scp, PBIO_PORTA, 0xa5); 193 val = pbinb(scp, PBIO_PORTA); 194 printf("pbio val=0x%02x (should be 0xa5)\n", val); 195 if (val != 0xa5) { 196 bus_release_resource(dev, SYS_RES_IOPORT, rid, sc->res); 197 return (ENXIO); 198 } 199 pboutb(scp, PBIO_PORTA, 0x5a); 200 val = pbinb(scp, PBIO_PORTA); 201 printf("pbio val=0x%02x (should be 0x5a)\n", val); 202 if (val != 0x5a) { 203 bus_release_resource(dev, SYS_RES_IOPORT, rid, sc->res); 204 return (ENXIO); 205 } 206#endif 207 device_set_desc(dev, "Intel 8255 PPI (basic mode)"); 208 /* Set all ports to input */ 209 /* pboutb(scp, PBIO_CFG, 0x9b); */ 210 bus_release_resource(dev, SYS_RES_IOPORT, rid, scp->res); 211 return (0); 212} 213 214/* 215 * Called if the probe succeeded. 216 * We can be destructive here as we know we have the device. 217 * we can also trust the unit number. 218 */ 219static int 220pbioattach (device_t dev) 221{ 222 int unit; 223 int i; 224 int rid; 225 struct pbio_softc *sc; 226 227 sc = device_get_softc(dev); 228 unit = device_get_unit(dev); 229 rid = 0; 230 sc->res = bus_alloc_resource_anywhere(dev, SYS_RES_IOPORT, &rid, 231 IO_PBIOSIZE, RF_ACTIVE); 232 if (sc->res == NULL) 233 return (ENXIO); 234 sc->bst = rman_get_bustag(sc->res); 235 sc->bsh = rman_get_bushandle(sc->res); 236 237 /* 238 * Store whatever seems wise. 239 */ 240 sc->iomode = 0x9b; /* All ports to input */ 241 242 for (i = 0; i < PBIO_NPORTS; i++) 243 sc->pd[i].port = make_dev(&pbio_cdevsw, (unit << 2) + i, 0, 0, 244 0600, "pbio%d%s", unit, PBIO_PNAME(i)); 245 return (0); 246} 247 248static int 249pbioioctl (struct cdev *dev, u_long cmd, caddr_t data, int flag, 250 struct thread *td) 251{ 252 struct pbio_softc *scp; 253 int port, unit; 254 255 unit = UNIT(dev); 256 port = PORT(dev); 257 scp = pbio_addr(unit); 258 if (scp == NULL) 259 return (ENODEV); 260 switch (cmd) { 261 case PBIO_SETDIFF: 262 scp->pd[port].diff = *(int *)data; 263 break; 264 case PBIO_SETIPACE: 265 scp->pd[port].ipace = *(int *)data; 266 break; 267 case PBIO_SETOPACE: 268 scp->pd[port].opace = *(int *)data; 269 break; 270 case PBIO_GETDIFF: 271 *(int *)data = scp->pd[port].diff; 272 break; 273 case PBIO_GETIPACE: 274 *(int *)data = scp->pd[port].ipace; 275 break; 276 case PBIO_GETOPACE: 277 *(int *)data = scp->pd[port].opace; 278 break; 279 default: 280 return ENXIO; 281 } 282 return (0); 283} 284 285static int 286pbioopen(struct cdev *dev, int oflags, int devtype, struct thread *td) 287{ 288 struct pbio_softc *scp; 289 int ocfg, port, unit; 290 int portbit; /* Port configuration bit */ 291 292 unit = UNIT(dev); 293 port = PORT(dev); 294 scp = pbio_addr(unit); 295 if (scp == NULL) 296 return (ENODEV); 297 298 switch (port) { 299 case 0: portbit = 0x10; break; /* Port A */ 300 case 1: portbit = 0x02; break; /* Port B */ 301 case 2: portbit = 0x08; break; /* Port CH */ 302 case 3: portbit = 0x01; break; /* Port CL */ 303 default: return (ENODEV); 304 } 305 ocfg = scp->iomode; 306 307 if (oflags & FWRITE) 308 /* Writing == output; zero the bit */ 309 pboutb(scp, PBIO_CFG, scp->iomode = (ocfg & (~portbit))); 310 else if (oflags & FREAD) 311 /* Reading == input; set the bit */ 312 pboutb(scp, PBIO_CFG, scp->iomode = (ocfg | portbit)); 313 else 314 return (EACCES); 315 316 return (0); 317} 318 319static int 320pbioclose(struct cdev *dev, int fflag, int devtype, struct thread *td) 321{ 322 struct pbio_softc *scp; 323 int unit; 324 325 unit = UNIT(dev); 326 scp = pbio_addr(unit); 327 if (scp == NULL) 328 return (ENODEV); 329 330 return (0); 331} 332 333/* 334 * Return the value of a given port on a given I/O base address 335 * Handles the split C port nibbles and blocking 336 */ 337static int 338portval(int port, struct pbio_softc *scp, char *val) 339{ 340 int err; 341 342 for (;;) { 343 switch (port) { 344 case 0: 345 *val = pbinb(scp, PBIO_PORTA); 346 break; 347 case 1: 348 *val = pbinb(scp, PBIO_PORTB); 349 break; 350 case 2: 351 *val = (pbinb(scp, PBIO_PORTC) >> 4) & 0xf; 352 break; 353 case 3: 354 *val = pbinb(scp, PBIO_PORTC) & 0xf; 355 break; 356 default: 357 *val = 0; 358 break; 359 } 360 if (scp->pd[port].diff) { 361 if (*val != scp->pd[port].oldval) { 362 scp->pd[port].oldval = *val; 363 return (0); 364 } 365 err = tsleep((caddr_t)&(scp->pd[port].diff), PBIOPRI, 366 "pbiopl", max(1, scp->pd[port].ipace)); 367 if (err == EINTR) 368 return (EINTR); 369 } else 370 return (0); 371 } 372} 373 374static int 375pbioread(struct cdev *dev, struct uio *uio, int ioflag) 376{ 377 struct pbio_softc *scp; 378 int err, i, port, ret, toread, unit; 379 char val; 380 381 unit = UNIT(dev); 382 port = PORT(dev); 383 scp = pbio_addr(unit); 384 if (scp == NULL) 385 return (ENODEV); 386 387 while (uio->uio_resid > 0) { 388 toread = min(uio->uio_resid, PBIO_BUFSIZ); 389 if ((ret = uiomove(scp->pd[port].buff, toread, uio)) != 0) 390 return (ret); 391 for (i = 0; i < toread; i++) { 392 if ((err = portval(port, scp, &val)) != 0) 393 return (err); 394 scp->pd[port].buff[i] = val; 395 if (!scp->pd[port].diff && scp->pd[port].ipace) 396 tsleep((caddr_t)&(scp->pd[port].ipace), PBIOPRI, 397 "pbioip", scp->pd[port].ipace); 398 } 399 } 400 return 0; 401} 402 403static int 404pbiowrite(struct cdev *dev, struct uio *uio, int ioflag) 405{ 406 struct pbio_softc *scp; 407 int i, port, ret, towrite, unit; 408 char val, oval; 409 410 unit = UNIT(dev); 411 port = PORT(dev); 412 scp = pbio_addr(unit); 413 if (scp == NULL) 414 return (ENODEV); 415 416 while (uio->uio_resid > 0) { 417 towrite = min(uio->uio_resid, PBIO_BUFSIZ); 418 if ((ret = uiomove(scp->pd[port].buff, towrite, uio)) != 0) 419 return (ret); 420 for (i = 0; i < towrite; i++) { 421 val = scp->pd[port].buff[i]; 422 switch (port) { 423 case 0: 424 pboutb(scp, PBIO_PORTA, val); 425 break; 426 case 1: 427 pboutb(scp, PBIO_PORTB, val); 428 break; 429 case 2: 430 oval = pbinb(scp, PBIO_PORTC); 431 oval &= 0xf; 432 val <<= 4; 433 pboutb(scp, PBIO_PORTC, val | oval); 434 break; 435 case 3: 436 oval = pbinb(scp, PBIO_PORTC); 437 oval &= 0xf0; 438 val &= 0xf; 439 pboutb(scp, PBIO_PORTC, oval | val); 440 break; 441 } 442 if (scp->pd[port].opace) 443 tsleep((caddr_t)&(scp->pd[port].opace), 444 PBIOPRI, "pbioop", 445 scp->pd[port].opace); 446 } 447 } 448 return (0); 449} 450 451static int 452pbiopoll(struct cdev *dev, int which, struct thread *td) 453{ 454 struct pbio_softc *scp; 455 int unit; 456 457 unit = UNIT(dev); 458 scp = pbio_addr(unit); 459 if (scp == NULL) 460 return (ENODEV); 461 462 /* 463 * Do processing 464 */ 465 return (0); /* this is the wrong value I'm sure */ 466} 467