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