1 1.82 riastrad /* $NetBSD: lpt.c,v 1.82 2018/09/03 16:29:31 riastradh Exp $ */ 2 1.23 cgd 3 1.1 cgd /* 4 1.51 mycroft * Copyright (c) 1993, 1994 Charles M. Hannum. 5 1.1 cgd * Copyright (c) 1990 William F. Jolitz, TeleMuse 6 1.1 cgd * All rights reserved. 7 1.1 cgd * 8 1.1 cgd * Redistribution and use in source and binary forms, with or without 9 1.1 cgd * modification, are permitted provided that the following conditions 10 1.1 cgd * are met: 11 1.1 cgd * 1. Redistributions of source code must retain the above copyright 12 1.1 cgd * notice, this list of conditions and the following disclaimer. 13 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright 14 1.1 cgd * notice, this list of conditions and the following disclaimer in the 15 1.1 cgd * documentation and/or other materials provided with the distribution. 16 1.1 cgd * 3. All advertising materials mentioning features or use of this software 17 1.1 cgd * must display the following acknowledgement: 18 1.64 perry * This software is a component of "386BSD" developed by 19 1.1 cgd * William F. Jolitz, TeleMuse. 20 1.1 cgd * 4. Neither the name of the developer nor the name "386BSD" 21 1.1 cgd * may be used to endorse or promote products derived from this software 22 1.1 cgd * without specific prior written permission. 23 1.1 cgd * 24 1.64 perry * THIS SOFTWARE IS A COMPONENT OF 386BSD DEVELOPED BY WILLIAM F. JOLITZ 25 1.64 perry * AND IS INTENDED FOR RESEARCH AND EDUCATIONAL PURPOSES ONLY. THIS 26 1.64 perry * SOFTWARE SHOULD NOT BE CONSIDERED TO BE A COMMERCIAL PRODUCT. 27 1.64 perry * THE DEVELOPER URGES THAT USERS WHO REQUIRE A COMMERCIAL PRODUCT 28 1.1 cgd * NOT MAKE USE OF THIS WORK. 29 1.1 cgd * 30 1.1 cgd * FOR USERS WHO WISH TO UNDERSTAND THE 386BSD SYSTEM DEVELOPED 31 1.64 perry * BY WILLIAM F. JOLITZ, WE RECOMMEND THE USER STUDY WRITTEN 32 1.64 perry * REFERENCES SUCH AS THE "PORTING UNIX TO THE 386" SERIES 33 1.64 perry * (BEGINNING JANUARY 1991 "DR. DOBBS JOURNAL", USA AND BEGINNING 34 1.64 perry * JUNE 1991 "UNIX MAGAZIN", GERMANY) BY WILLIAM F. JOLITZ AND 35 1.64 perry * LYNNE GREER JOLITZ, AS WELL AS OTHER BOOKS ON UNIX AND THE 36 1.64 perry * ON-LINE 386BSD USER MANUAL BEFORE USE. A BOOK DISCUSSING THE INTERNALS 37 1.1 cgd * OF 386BSD ENTITLED "386BSD FROM THE INSIDE OUT" WILL BE AVAILABLE LATE 1992. 38 1.1 cgd * 39 1.1 cgd * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER ``AS IS'' AND 40 1.1 cgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 41 1.1 cgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 42 1.1 cgd * ARE DISCLAIMED. IN NO EVENT SHALL THE DEVELOPER BE LIABLE 43 1.1 cgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 44 1.1 cgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 45 1.1 cgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 46 1.1 cgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 47 1.1 cgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 48 1.1 cgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 49 1.1 cgd * SUCH DAMAGE. 50 1.1 cgd */ 51 1.1 cgd 52 1.1 cgd /* 53 1.45 is * Device Driver for AT style parallel printer port 54 1.1 cgd */ 55 1.58 lukem 56 1.58 lukem #include <sys/cdefs.h> 57 1.82 riastrad __KERNEL_RCSID(0, "$NetBSD: lpt.c,v 1.82 2018/09/03 16:29:31 riastradh Exp $"); 58 1.1 cgd 59 1.9 mycroft #include <sys/param.h> 60 1.9 mycroft #include <sys/systm.h> 61 1.9 mycroft #include <sys/proc.h> 62 1.47 thorpej #include <sys/malloc.h> 63 1.9 mycroft #include <sys/kernel.h> 64 1.9 mycroft #include <sys/ioctl.h> 65 1.9 mycroft #include <sys/uio.h> 66 1.11 mycroft #include <sys/device.h> 67 1.38 christos #include <sys/conf.h> 68 1.11 mycroft #include <sys/syslog.h> 69 1.71 ad #include <sys/intr.h> 70 1.1 cgd 71 1.72 ad #include <sys/bus.h> 72 1.9 mycroft 73 1.45 is #include <dev/ic/lptreg.h> 74 1.45 is #include <dev/ic/lptvar.h> 75 1.1 cgd 76 1.81 riastrad #include "ioconf.h" 77 1.81 riastrad 78 1.11 mycroft #define TIMEOUT hz*16 /* wait up to 16 seconds for a ready */ 79 1.11 mycroft #define STEP hz/4 80 1.11 mycroft 81 1.11 mycroft #define LPTPRI (PZERO+8) 82 1.11 mycroft #define LPT_BSIZE 1024 83 1.1 cgd 84 1.45 is #define LPTDEBUG 85 1.45 is 86 1.44 mikel #ifndef LPTDEBUG 87 1.38 christos #define LPRINTF(a) 88 1.1 cgd #else 89 1.44 mikel #define LPRINTF(a) if (lptdebug) printf a 90 1.44 mikel int lptdebug = 0; 91 1.1 cgd #endif 92 1.1 cgd 93 1.59 gehenna dev_type_open(lptopen); 94 1.59 gehenna dev_type_close(lptclose); 95 1.59 gehenna dev_type_write(lptwrite); 96 1.59 gehenna dev_type_ioctl(lptioctl); 97 1.59 gehenna 98 1.59 gehenna const struct cdevsw lpt_cdevsw = { 99 1.79 dholland .d_open = lptopen, 100 1.79 dholland .d_close = lptclose, 101 1.79 dholland .d_read = noread, 102 1.79 dholland .d_write = lptwrite, 103 1.79 dholland .d_ioctl = lptioctl, 104 1.79 dholland .d_stop = nostop, 105 1.79 dholland .d_tty = notty, 106 1.79 dholland .d_poll = nopoll, 107 1.79 dholland .d_mmap = nommap, 108 1.79 dholland .d_kqfilter = nokqfilter, 109 1.80 dholland .d_discard = nodiscard, 110 1.79 dholland .d_flag = D_OTHER 111 1.59 gehenna }; 112 1.1 cgd 113 1.11 mycroft #define LPTUNIT(s) (minor(s) & 0x1f) 114 1.11 mycroft #define LPTFLAGS(s) (minor(s) & 0xe0) 115 1.1 cgd 116 1.71 ad static void lptsoftintr(void *); 117 1.71 ad 118 1.15 mycroft void 119 1.76 dsl lpt_attach_subr(struct lpt_softc *sc) 120 1.1 cgd { 121 1.42 thorpej bus_space_tag_t iot; 122 1.42 thorpej bus_space_handle_t ioh; 123 1.15 mycroft 124 1.6 mycroft sc->sc_state = 0; 125 1.32 cgd 126 1.45 is iot = sc->sc_iot; 127 1.45 is ioh = sc->sc_ioh; 128 1.32 cgd 129 1.42 thorpej bus_space_write_1(iot, ioh, lpt_control, LPC_NINIT); 130 1.49 cgd 131 1.70 ad callout_init(&sc->sc_wakeup_ch, 0); 132 1.71 ad sc->sc_sih = softint_establish(SOFTINT_SERIAL, lptsoftintr, sc); 133 1.56 thorpej 134 1.49 cgd sc->sc_dev_ok = 1; 135 1.1 cgd } 136 1.1 cgd 137 1.73 dyoung int 138 1.73 dyoung lpt_detach_subr(device_t self, int flags) 139 1.73 dyoung { 140 1.73 dyoung struct lpt_softc *sc = device_private(self); 141 1.73 dyoung 142 1.73 dyoung sc->sc_dev_ok = 0; 143 1.73 dyoung softint_disestablish(sc->sc_sih); 144 1.73 dyoung callout_destroy(&sc->sc_wakeup_ch); 145 1.73 dyoung return 0; 146 1.73 dyoung } 147 1.73 dyoung 148 1.1 cgd /* 149 1.11 mycroft * Reset the printer, then wait until it's selected and not busy. 150 1.1 cgd */ 151 1.11 mycroft int 152 1.68 christos lptopen(dev_t dev, int flag, int mode, struct lwp *l) 153 1.1 cgd { 154 1.11 mycroft u_char flags = LPTFLAGS(dev); 155 1.11 mycroft struct lpt_softc *sc; 156 1.42 thorpej bus_space_tag_t iot; 157 1.42 thorpej bus_space_handle_t ioh; 158 1.11 mycroft u_char control; 159 1.11 mycroft int error; 160 1.11 mycroft int spin; 161 1.11 mycroft 162 1.75 cegger sc = device_lookup_private(&lpt_cd, LPTUNIT(dev)); 163 1.49 cgd if (!sc || !sc->sc_dev_ok) 164 1.11 mycroft return ENXIO; 165 1.11 mycroft 166 1.45 is #if 0 /* XXX what to do? */ 167 1.25 mycroft if (sc->sc_irq == IRQUNK && (flags & LPT_NOINTR) == 0) 168 1.11 mycroft return ENXIO; 169 1.45 is #endif 170 1.11 mycroft 171 1.11 mycroft #ifdef DIAGNOSTIC 172 1.11 mycroft if (sc->sc_state) 173 1.74 cube aprint_verbose_dev(sc->sc_dev, "stat=0x%x not zero\n", 174 1.11 mycroft sc->sc_state); 175 1.11 mycroft #endif 176 1.11 mycroft 177 1.11 mycroft if (sc->sc_state) 178 1.11 mycroft return EBUSY; 179 1.1 cgd 180 1.11 mycroft sc->sc_state = LPT_INIT; 181 1.11 mycroft sc->sc_flags = flags; 182 1.74 cube LPRINTF(("%s: open: flags=0x%x\n", device_xname(sc->sc_dev), 183 1.44 mikel (unsigned)flags)); 184 1.42 thorpej iot = sc->sc_iot; 185 1.32 cgd ioh = sc->sc_ioh; 186 1.11 mycroft 187 1.11 mycroft if ((flags & LPT_NOPRIME) == 0) { 188 1.11 mycroft /* assert INIT for 100 usec to start up printer */ 189 1.42 thorpej bus_space_write_1(iot, ioh, lpt_control, LPC_SELECT); 190 1.14 mycroft delay(100); 191 1.1 cgd } 192 1.11 mycroft 193 1.11 mycroft control = LPC_SELECT | LPC_NINIT; 194 1.42 thorpej bus_space_write_1(iot, ioh, lpt_control, control); 195 1.1 cgd 196 1.1 cgd /* wait till ready (printer running diagnostics) */ 197 1.25 mycroft for (spin = 0; NOT_READY_ERR(); spin += STEP) { 198 1.11 mycroft if (spin >= TIMEOUT) { 199 1.1 cgd sc->sc_state = 0; 200 1.11 mycroft return EBUSY; 201 1.1 cgd } 202 1.1 cgd 203 1.1 cgd /* wait 1/4 second, give up if we get a signal */ 204 1.69 christos error = tsleep((void *)sc, LPTPRI | PCATCH, "lptopen", STEP); 205 1.38 christos if (error != EWOULDBLOCK) { 206 1.1 cgd sc->sc_state = 0; 207 1.11 mycroft return error; 208 1.1 cgd } 209 1.1 cgd } 210 1.1 cgd 211 1.11 mycroft if ((flags & LPT_NOINTR) == 0) 212 1.11 mycroft control |= LPC_IENABLE; 213 1.11 mycroft if (flags & LPT_AUTOLF) 214 1.11 mycroft control |= LPC_AUTOLF; 215 1.11 mycroft sc->sc_control = control; 216 1.42 thorpej bus_space_write_1(iot, ioh, lpt_control, control); 217 1.11 mycroft 218 1.47 thorpej sc->sc_inbuf = malloc(LPT_BSIZE, M_DEVBUF, M_WAITOK); 219 1.11 mycroft sc->sc_count = 0; 220 1.11 mycroft sc->sc_state = LPT_OPEN; 221 1.25 mycroft 222 1.25 mycroft if ((sc->sc_flags & LPT_NOINTR) == 0) 223 1.25 mycroft lptwakeup(sc); 224 1.11 mycroft 225 1.74 cube LPRINTF(("%s: opened\n", device_xname(sc->sc_dev))); 226 1.11 mycroft return 0; 227 1.1 cgd } 228 1.1 cgd 229 1.11 mycroft int 230 1.76 dsl lptnotready(u_char status, struct lpt_softc *sc) 231 1.11 mycroft { 232 1.21 mycroft u_char new; 233 1.21 mycroft 234 1.25 mycroft status = (status ^ LPS_INVERT) & LPS_MASK; 235 1.21 mycroft new = status & ~sc->sc_laststatus; 236 1.21 mycroft sc->sc_laststatus = status; 237 1.1 cgd 238 1.52 perry if (sc->sc_state & LPT_OPEN) { 239 1.52 perry if (new & LPS_SELECT) 240 1.52 perry log(LOG_NOTICE, 241 1.74 cube "%s: offline\n", device_xname(sc->sc_dev)); 242 1.52 perry else if (new & LPS_NOPAPER) 243 1.52 perry log(LOG_NOTICE, 244 1.74 cube "%s: out of paper\n", device_xname(sc->sc_dev)); 245 1.52 perry else if (new & LPS_NERR) 246 1.52 perry log(LOG_NOTICE, 247 1.74 cube "%s: output error\n", device_xname(sc->sc_dev)); 248 1.52 perry } 249 1.64 perry 250 1.25 mycroft return status; 251 1.11 mycroft } 252 1.11 mycroft 253 1.11 mycroft void 254 1.76 dsl lptwakeup(void *arg) 255 1.18 cgd { 256 1.25 mycroft struct lpt_softc *sc = arg; 257 1.11 mycroft int s; 258 1.11 mycroft 259 1.78 rmind s = splvm(); 260 1.16 mycroft lptintr(sc); 261 1.13 mycroft splx(s); 262 1.11 mycroft 263 1.56 thorpej callout_reset(&sc->sc_wakeup_ch, STEP, lptwakeup, sc); 264 1.1 cgd } 265 1.1 cgd 266 1.1 cgd /* 267 1.11 mycroft * Close the device, and free the local line buffer. 268 1.1 cgd */ 269 1.36 mycroft int 270 1.68 christos lptclose(dev_t dev, int flag, int mode, 271 1.68 christos struct lwp *l) 272 1.1 cgd { 273 1.74 cube struct lpt_softc *sc = 274 1.75 cegger device_lookup_private(&lpt_cd, LPTUNIT(dev)); 275 1.42 thorpej bus_space_tag_t iot = sc->sc_iot; 276 1.42 thorpej bus_space_handle_t ioh = sc->sc_ioh; 277 1.1 cgd 278 1.11 mycroft if (sc->sc_count) 279 1.46 is (void) lptpushbytes(sc); 280 1.1 cgd 281 1.25 mycroft if ((sc->sc_flags & LPT_NOINTR) == 0) 282 1.56 thorpej callout_stop(&sc->sc_wakeup_ch); 283 1.25 mycroft 284 1.42 thorpej bus_space_write_1(iot, ioh, lpt_control, LPC_NINIT); 285 1.1 cgd sc->sc_state = 0; 286 1.42 thorpej bus_space_write_1(iot, ioh, lpt_control, LPC_NINIT); 287 1.47 thorpej free(sc->sc_inbuf, M_DEVBUF); 288 1.11 mycroft 289 1.74 cube LPRINTF(("%s: closed\n", device_xname(sc->sc_dev))); 290 1.11 mycroft return 0; 291 1.11 mycroft } 292 1.11 mycroft 293 1.11 mycroft int 294 1.76 dsl lptpushbytes(struct lpt_softc *sc) 295 1.11 mycroft { 296 1.42 thorpej bus_space_tag_t iot = sc->sc_iot; 297 1.42 thorpej bus_space_handle_t ioh = sc->sc_ioh; 298 1.11 mycroft int error; 299 1.11 mycroft 300 1.11 mycroft if (sc->sc_flags & LPT_NOINTR) { 301 1.11 mycroft int spin, tic; 302 1.11 mycroft u_char control = sc->sc_control; 303 1.11 mycroft 304 1.11 mycroft while (sc->sc_count > 0) { 305 1.11 mycroft spin = 0; 306 1.25 mycroft while (NOT_READY()) { 307 1.25 mycroft if (++spin < sc->sc_spinmax) 308 1.25 mycroft continue; 309 1.11 mycroft tic = 0; 310 1.12 mycroft /* adapt busy-wait algorithm */ 311 1.12 mycroft sc->sc_spinmax++; 312 1.25 mycroft while (NOT_READY_ERR()) { 313 1.11 mycroft /* exponential backoff */ 314 1.11 mycroft tic = tic + tic + 1; 315 1.11 mycroft if (tic > TIMEOUT) 316 1.11 mycroft tic = TIMEOUT; 317 1.69 christos error = tsleep((void *)sc, 318 1.11 mycroft LPTPRI | PCATCH, "lptpsh", tic); 319 1.11 mycroft if (error != EWOULDBLOCK) 320 1.11 mycroft return error; 321 1.11 mycroft } 322 1.25 mycroft break; 323 1.11 mycroft } 324 1.11 mycroft 325 1.42 thorpej bus_space_write_1(iot, ioh, lpt_data, *sc->sc_cp++); 326 1.61 rafal DELAY(1); 327 1.44 mikel bus_space_write_1(iot, ioh, lpt_control, 328 1.44 mikel control | LPC_STROBE); 329 1.61 rafal DELAY(1); 330 1.11 mycroft sc->sc_count--; 331 1.42 thorpej bus_space_write_1(iot, ioh, lpt_control, control); 332 1.61 rafal DELAY(1); 333 1.11 mycroft 334 1.11 mycroft /* adapt busy-wait algorithm */ 335 1.25 mycroft if (spin*2 + 16 < sc->sc_spinmax) 336 1.12 mycroft sc->sc_spinmax--; 337 1.11 mycroft } 338 1.11 mycroft } else { 339 1.11 mycroft int s; 340 1.11 mycroft 341 1.11 mycroft while (sc->sc_count > 0) { 342 1.11 mycroft /* if the printer is ready for a char, give it one */ 343 1.11 mycroft if ((sc->sc_state & LPT_OBUSY) == 0) { 344 1.74 cube LPRINTF(("%s: write %lu\n", 345 1.74 cube device_xname(sc->sc_dev), 346 1.53 thorpej (u_long)sc->sc_count)); 347 1.78 rmind s = splvm(); 348 1.16 mycroft (void) lptintr(sc); 349 1.11 mycroft splx(s); 350 1.11 mycroft } 351 1.69 christos error = tsleep((void *)sc, LPTPRI | PCATCH, 352 1.38 christos "lptwrite2", 0); 353 1.38 christos if (error) 354 1.11 mycroft return error; 355 1.11 mycroft } 356 1.11 mycroft } 357 1.11 mycroft return 0; 358 1.1 cgd } 359 1.1 cgd 360 1.64 perry /* 361 1.11 mycroft * Copy a line from user space to a local buffer, then call putc to get the 362 1.11 mycroft * chars moved to the output queue. 363 1.1 cgd */ 364 1.36 mycroft int 365 1.68 christos lptwrite(dev_t dev, struct uio *uio, int flags) 366 1.1 cgd { 367 1.74 cube struct lpt_softc *sc = 368 1.75 cegger device_lookup_private(&lpt_cd, LPTUNIT(dev)); 369 1.11 mycroft size_t n; 370 1.11 mycroft int error = 0; 371 1.11 mycroft 372 1.82 riastrad while ((n = uimin(LPT_BSIZE, uio->uio_resid)) != 0) { 373 1.47 thorpej uiomove(sc->sc_cp = sc->sc_inbuf, n, uio); 374 1.11 mycroft sc->sc_count = n; 375 1.46 is error = lptpushbytes(sc); 376 1.11 mycroft if (error) { 377 1.11 mycroft /* 378 1.11 mycroft * Return accurate residual if interrupted or timed 379 1.11 mycroft * out. 380 1.11 mycroft */ 381 1.11 mycroft uio->uio_resid += sc->sc_count; 382 1.11 mycroft sc->sc_count = 0; 383 1.11 mycroft return error; 384 1.1 cgd } 385 1.1 cgd } 386 1.11 mycroft return 0; 387 1.1 cgd } 388 1.1 cgd 389 1.1 cgd /* 390 1.11 mycroft * Handle printer interrupts which occur when the printer is ready to accept 391 1.11 mycroft * another char. 392 1.1 cgd */ 393 1.11 mycroft int 394 1.76 dsl lptintr(void *arg) 395 1.1 cgd { 396 1.30 cgd struct lpt_softc *sc = arg; 397 1.42 thorpej bus_space_tag_t iot = sc->sc_iot; 398 1.42 thorpej bus_space_handle_t ioh = sc->sc_ioh; 399 1.1 cgd 400 1.13 mycroft #if 0 401 1.11 mycroft if ((sc->sc_state & LPT_OPEN) == 0) 402 1.11 mycroft return 0; 403 1.13 mycroft #endif 404 1.6 mycroft 405 1.11 mycroft /* is printer online and ready for output */ 406 1.25 mycroft if (NOT_READY() && NOT_READY_ERR()) 407 1.11 mycroft return 0; 408 1.6 mycroft 409 1.11 mycroft if (sc->sc_count) { 410 1.13 mycroft u_char control = sc->sc_control; 411 1.11 mycroft /* send char */ 412 1.42 thorpej bus_space_write_1(iot, ioh, lpt_data, *sc->sc_cp++); 413 1.55 perry DELAY(1); 414 1.42 thorpej bus_space_write_1(iot, ioh, lpt_control, control | LPC_STROBE); 415 1.55 perry DELAY(1); 416 1.13 mycroft sc->sc_count--; 417 1.42 thorpej bus_space_write_1(iot, ioh, lpt_control, control); 418 1.61 rafal DELAY(1); 419 1.11 mycroft sc->sc_state |= LPT_OBUSY; 420 1.11 mycroft } else 421 1.11 mycroft sc->sc_state &= ~LPT_OBUSY; 422 1.1 cgd 423 1.11 mycroft if (sc->sc_count == 0) { 424 1.1 cgd /* none, wake up the top half to get more */ 425 1.71 ad softint_schedule(sc->sc_sih); 426 1.11 mycroft } 427 1.11 mycroft 428 1.11 mycroft return 1; 429 1.1 cgd } 430 1.1 cgd 431 1.71 ad static void 432 1.71 ad lptsoftintr(void *cookie) 433 1.71 ad { 434 1.71 ad 435 1.71 ad wakeup(cookie); 436 1.71 ad } 437 1.71 ad 438 1.2 cgd int 439 1.69 christos lptioctl(dev_t dev, u_long cmd, void *data, 440 1.68 christos int flag, struct lwp *l) 441 1.2 cgd { 442 1.67 christos return ENODEV; 443 1.2 cgd } 444