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 char driver_name[] = "pbio"; 131 132 static driver_t pbio_driver = { 133 driver_name, 134 pbio_methods, 135 sizeof(struct pbio_softc), 136 }; 137 138 DRIVER_MODULE(pbio, isa, pbio_driver, 0, 0); 139 140 static __inline uint8_t 141 pbinb(struct pbio_softc *scp, int off) 142 { 143 144 return (bus_read_1(scp->res, off)); 145 } 146 147 static __inline void 148 pboutb(struct pbio_softc *scp, int off, uint8_t val) 149 { 150 151 bus_write_1(scp->res, off, val); 152 } 153 154 static int 155 pbioprobe(device_t dev) 156 { 157 int rid; 158 struct pbio_softc *scp = device_get_softc(dev); 159 #ifdef GENERIC_PBIO_PROBE 160 unsigned char val; 161 #endif 162 163 if (isa_get_logicalid(dev)) /* skip PnP probes */ 164 return (ENXIO); 165 rid = 0; 166 scp->res = bus_alloc_resource_anywhere(dev, SYS_RES_IOPORT, &rid, 167 IO_PBIOSIZE, RF_ACTIVE); 168 if (scp->res == NULL) 169 return (ENXIO); 170 171 #ifdef GENERIC_PBIO_PROBE 172 /* 173 * try see if the device is there. 174 * This probe works only if the device has no I/O attached to it 175 * XXX Better allow flags to abort testing 176 */ 177 /* Set all ports to output */ 178 pboutb(scp, PBIO_CFG, 0x80); 179 printf("pbio val(CFG: 0x%03x)=0x%02x (should be 0x80)\n", 180 rman_get_start(scp->res), pbinb(scp, PBIO_CFG)); 181 pboutb(scp, PBIO_PORTA, 0xa5); 182 val = pbinb(scp, PBIO_PORTA); 183 printf("pbio val=0x%02x (should be 0xa5)\n", val); 184 if (val != 0xa5) { 185 bus_release_resource(dev, SYS_RES_IOPORT, rid, sc->res); 186 return (ENXIO); 187 } 188 pboutb(scp, PBIO_PORTA, 0x5a); 189 val = pbinb(scp, PBIO_PORTA); 190 printf("pbio val=0x%02x (should be 0x5a)\n", val); 191 if (val != 0x5a) { 192 bus_release_resource(dev, SYS_RES_IOPORT, rid, sc->res); 193 return (ENXIO); 194 } 195 #endif 196 device_set_desc(dev, "Intel 8255 PPI (basic mode)"); 197 /* Set all ports to input */ 198 /* pboutb(scp, PBIO_CFG, 0x9b); */ 199 bus_release_resource(dev, SYS_RES_IOPORT, rid, scp->res); 200 return (BUS_PROBE_DEFAULT); 201 } 202 203 /* 204 * Called if the probe succeeded. 205 * We can be destructive here as we know we have the device. 206 * we can also trust the unit number. 207 */ 208 static int 209 pbioattach (device_t dev) 210 { 211 struct make_dev_args args; 212 int unit; 213 int i; 214 int rid; 215 struct pbio_softc *sc; 216 217 sc = device_get_softc(dev); 218 unit = device_get_unit(dev); 219 rid = 0; 220 sc->res = bus_alloc_resource_anywhere(dev, SYS_RES_IOPORT, &rid, 221 IO_PBIOSIZE, RF_ACTIVE); 222 if (sc->res == NULL) 223 return (ENXIO); 224 225 /* 226 * Store whatever seems wise. 227 */ 228 sc->iomode = 0x9b; /* All ports to input */ 229 230 sx_init(&sc->lock, "pbio"); 231 for (i = 0; i < PBIO_NPORTS; i++) { 232 make_dev_args_init(&args); 233 args.mda_devsw = &pbio_cdevsw; 234 args.mda_uid = 0; 235 args.mda_gid = 0; 236 args.mda_mode = 0600; 237 args.mda_unit = i; 238 args.mda_si_drv1 = sc; 239 (void)make_dev_s(&args, &sc->pd[i].port, "pbio%d%s", unit, 240 PBIO_PNAME(i)); 241 } 242 return (0); 243 } 244 245 static int 246 pbioioctl (struct cdev *dev, u_long cmd, caddr_t data, int flag, 247 struct thread *td) 248 { 249 struct pbio_softc *scp; 250 int error, port; 251 252 error = 0; 253 port = PORT(dev); 254 scp = pbio_addr(dev); 255 sx_xlock(&scp->lock); 256 switch (cmd) { 257 case PBIO_SETDIFF: 258 scp->pd[port].diff = *(int *)data; 259 break; 260 case PBIO_SETIPACE: 261 scp->pd[port].ipace = *(int *)data; 262 break; 263 case PBIO_SETOPACE: 264 scp->pd[port].opace = *(int *)data; 265 break; 266 case PBIO_GETDIFF: 267 *(int *)data = scp->pd[port].diff; 268 break; 269 case PBIO_GETIPACE: 270 *(int *)data = scp->pd[port].ipace; 271 break; 272 case PBIO_GETOPACE: 273 *(int *)data = scp->pd[port].opace; 274 break; 275 default: 276 error = ENXIO; 277 } 278 sx_xunlock(&scp->lock); 279 return (error); 280 } 281 282 static int 283 pbioopen(struct cdev *dev, int oflags, int devtype, struct thread *td) 284 { 285 struct pbio_softc *scp; 286 int error, ocfg, port; 287 int portbit; /* Port configuration bit */ 288 289 port = PORT(dev); 290 scp = pbio_addr(dev); 291 292 switch (port) { 293 case 0: portbit = 0x10; break; /* Port A */ 294 case 1: portbit = 0x02; break; /* Port B */ 295 case 2: portbit = 0x08; break; /* Port CH */ 296 case 3: portbit = 0x01; break; /* Port CL */ 297 default: return (ENODEV); 298 } 299 ocfg = scp->iomode; 300 301 error = 0; 302 sx_xlock(&scp->lock); 303 if (oflags & FWRITE) 304 /* Writing == output; zero the bit */ 305 pboutb(scp, PBIO_CFG, scp->iomode = (ocfg & (~portbit))); 306 else if (oflags & FREAD) 307 /* Reading == input; set the bit */ 308 pboutb(scp, PBIO_CFG, scp->iomode = (ocfg | portbit)); 309 else 310 error = EACCES; 311 sx_xunlock(&scp->lock); 312 313 return (error); 314 } 315 316 /* 317 * Return the value of a given port on a given I/O base address 318 * Handles the split C port nibbles and blocking 319 */ 320 static int 321 portval(int port, struct pbio_softc *scp, char *val) 322 { 323 int err; 324 325 for (;;) { 326 switch (port) { 327 case 0: 328 *val = pbinb(scp, PBIO_PORTA); 329 break; 330 case 1: 331 *val = pbinb(scp, PBIO_PORTB); 332 break; 333 case 2: 334 *val = (pbinb(scp, PBIO_PORTC) >> 4) & 0xf; 335 break; 336 case 3: 337 *val = pbinb(scp, PBIO_PORTC) & 0xf; 338 break; 339 default: 340 *val = 0; 341 break; 342 } 343 if (scp->pd[port].diff) { 344 if (*val != scp->pd[port].oldval) { 345 scp->pd[port].oldval = *val; 346 return (0); 347 } 348 err = pause_sig("pbiopl", max(1, scp->pd[port].ipace)); 349 if (err == EINTR) 350 return (EINTR); 351 } else 352 return (0); 353 } 354 } 355 356 static int 357 pbioread(struct cdev *dev, struct uio *uio, int ioflag) 358 { 359 struct pbio_softc *scp; 360 int err, i, port, toread; 361 char val; 362 363 port = PORT(dev); 364 scp = pbio_addr(dev); 365 366 err = 0; 367 sx_xlock(&scp->lock); 368 while (uio->uio_resid > 0) { 369 toread = min(uio->uio_resid, PBIO_BUFSIZ); 370 if ((err = uiomove(scp->pd[port].buff, toread, uio)) != 0) 371 break; 372 for (i = 0; i < toread; i++) { 373 if ((err = portval(port, scp, &val)) != 0) 374 break; 375 scp->pd[port].buff[i] = val; 376 if (!scp->pd[port].diff && scp->pd[port].ipace) 377 pause_sig("pbioip", scp->pd[port].ipace); 378 } 379 } 380 sx_xunlock(&scp->lock); 381 return (err); 382 } 383 384 static int 385 pbiowrite(struct cdev *dev, struct uio *uio, int ioflag) 386 { 387 struct pbio_softc *scp; 388 int i, port, ret, towrite; 389 char val, oval; 390 391 port = PORT(dev); 392 scp = pbio_addr(dev); 393 394 ret = 0; 395 sx_xlock(&scp->lock); 396 while (uio->uio_resid > 0) { 397 towrite = min(uio->uio_resid, PBIO_BUFSIZ); 398 if ((ret = uiomove(scp->pd[port].buff, towrite, uio)) != 0) 399 break; 400 for (i = 0; i < towrite; i++) { 401 val = scp->pd[port].buff[i]; 402 switch (port) { 403 case 0: 404 pboutb(scp, PBIO_PORTA, val); 405 break; 406 case 1: 407 pboutb(scp, PBIO_PORTB, val); 408 break; 409 case 2: 410 oval = pbinb(scp, PBIO_PORTC); 411 oval &= 0xf; 412 val <<= 4; 413 pboutb(scp, PBIO_PORTC, val | oval); 414 break; 415 case 3: 416 oval = pbinb(scp, PBIO_PORTC); 417 oval &= 0xf0; 418 val &= 0xf; 419 pboutb(scp, PBIO_PORTC, oval | val); 420 break; 421 } 422 if (scp->pd[port].opace) 423 pause_sig("pbioop", scp->pd[port].opace); 424 } 425 } 426 sx_xunlock(&scp->lock); 427 return (ret); 428 } 429 430 static int 431 pbiopoll(struct cdev *dev, int which, struct thread *td) 432 { 433 /* 434 * Do processing 435 */ 436 return (0); /* this is the wrong value I'm sure */ 437 } 438