/* * (C) Copyright 1992, ..., 2007 the "DOSEMU-Development-Team". * * for details see file COPYING.DOSEMU in the DOSEMU distribution */ /* for the Linux dos emulator versions 0.49 and newer * */ #define LPT_C 1 #include #include #include #include #include #include #include #include #include #include "emu.h" #include "bios.h" #include "port.h" #include "timers.h" #include "lpt.h" #include "utilities.h" #include "dos2linux.h" static int stub_printer_write(int, int); /* status bits, Centronics */ #define CTS_STAT_NOIOERR LPT_STAT_NOIOERR #define CTS_STAT_ONLINE LPT_STAT_ONLINE #define CTS_STAT_NOPAPER LPT_STAT_NOPAPER #define CTS_STAT_NOT_ACKing LPT_STAT_NOT_ACK #define CTS_STAT_BUSY LPT_STAT_NOT_BUSY /* control bits, Centronics */ #define CTS_CTRL_NOT_SELECT LPT_CTRL_SELECT #define CTS_CTRL_NOT_INIT LPT_CTRL_NOT_INIT #define CTS_CTRL_NOT_AUTOLF LPT_CTRL_AUTOLF #define CTS_CTRL_NOT_STROBE LPT_CTRL_STROBE /* inversion masks to convert LPT<-->Centronics */ #define LPT_STAT_INV_MASK (CTS_STAT_BUSY) #define LPT_CTRL_INV_MASK (CTS_CTRL_NOT_STROBE | CTS_CTRL_NOT_AUTOLF | \ CTS_CTRL_NOT_SELECT) #define DEFAULT_STAT (CTS_STAT_ONLINE | CTS_STAT_NOIOERR | \ CTS_STAT_NOT_ACKing | LPT_STAT_NOT_IRQ) #define DEFAULT_CTRL (CTS_CTRL_NOT_INIT | CTS_CTRL_NOT_AUTOLF | \ CTS_CTRL_NOT_STROBE) struct printer lpt[NUM_PRINTERS] = { {NULL, NULL, 5, 0x378, .control = DEFAULT_CTRL, .status = DEFAULT_STAT}, {NULL, NULL, 5, 0x278, .control = DEFAULT_CTRL, .status = DEFAULT_STAT}, {NULL, NULL, 10, 0x3bc, .control = DEFAULT_CTRL, .status = DEFAULT_STAT} }; static int get_printer(ioport_t port) { int i; for (i = 0; i < 3; i++) if (lpt[i].base_port <= port && port <= lpt[i].base_port + 2) return i; return -1; } static Bit8u printer_io_read(ioport_t port) { int i = get_printer(port); Bit8u val; if (i == -1) return 0xff; switch (port - lpt[i].base_port) { case 0: val = lpt[i].data; /* simple unidirectional port */ if (debug_level('p') >= 5) p_printf("LPT%d: Reading data byte %#x\n", i, val); break; case 1: /* status port, r/o */ val = lpt[i].status ^ LPT_STAT_INV_MASK; /* we should really set ACK after 5 us but here we just use the fact that the BIOS only checks this once */ lpt[i].status |= CTS_STAT_NOT_ACKing | LPT_STAT_NOT_IRQ; lpt[i].status &= ~CTS_STAT_BUSY; if (debug_level('p') >= 5) p_printf("LPT%d: Reading status byte %#x\n", i, val); break; case 2: val = lpt[i].control ^ LPT_CTRL_INV_MASK; if (debug_level('p') >= 5) p_printf("LPT%d: Reading control byte %#x\n", i, val); break; default: val = 0xff; break; } return val; } static void printer_io_write(ioport_t port, Bit8u value) { int i = get_printer(port); if (i == -1) return; switch (port - lpt[i].base_port) { case 0: if (debug_level('p') >= 5) p_printf("LPT%d: Writing data byte %#x\n", i, value); lpt[i].data = value; break; case 1: /* status port, r/o */ break; case 2: if (debug_level('p') >= 5) p_printf("LPT%d: Writing control byte %#x\n", i, value); value ^= LPT_CTRL_INV_MASK; // convert to Centronics if (((lpt[i].control & (CTS_CTRL_NOT_STROBE | CTS_CTRL_NOT_SELECT)) == 0) && (value & CTS_CTRL_NOT_STROBE)) { /* STROBE rising */ printer_write(i, lpt[i].data); lpt[i].status &= ~CTS_STAT_NOT_ACKing; lpt[i].status |= CTS_STAT_BUSY; } lpt[i].control = value; break; } } static int dev_printer_open(int prnum) { int um = umask(026); lpt[prnum].file = fopen(lpt[prnum].dev, "a"); umask(um); return 0; } static int pipe_printer_open(int prnum) { p_printf("LPT: doing printer command ..%s..\n", lpt[prnum].prtcmd); lpt[prnum].file = popen(lpt[prnum].prtcmd, "w"); if (lpt[prnum].file == NULL) error("system(\"%s\") in lpt.c failed, cannot print!\ Command returned error %s\n", lpt[prnum].prtcmd, strerror(errno)); return 0; } int printer_open(int prnum) { int rc; if (lpt[prnum].file != NULL) return 0; if (lpt[prnum].fops.open == NULL) return -1; rc = lpt[prnum].fops.open(prnum); /* use line buffering so we don't need to have a long wait for output */ setvbuf(lpt[prnum].file, NULL, _IOLBF, 0); p_printf("LPT: opened printer %d to %s, file %p\n", prnum, lpt[prnum].dev ? lpt[prnum].dev : "<>", (void *) lpt[prnum].file); return rc; } static int dev_printer_close(int prnum) { if (lpt[prnum].file != NULL) fclose(lpt[prnum].file); return 0; } static int pipe_printer_close(int prnum) { if (lpt[prnum].file != NULL) pclose(lpt[prnum].file); lpt[prnum].file = NULL; return 0; } int printer_close(int prnum) { if (lpt[prnum].fops.close) { p_printf("LPT: closing printer %d, %s\n", prnum, lpt[prnum].dev ? lpt[prnum].dev : "<>"); lpt[prnum].fops.close(prnum); lpt[prnum].file = NULL; lpt[prnum].remaining = -1; } lpt[prnum].fops.write = stub_printer_write; return 0; } static int stub_printer_write(int prnum, int outchar) { printer_open(prnum); /* from now on, use real write */ lpt[prnum].fops.write = lpt[prnum].fops.realwrite; return printer_write(prnum, outchar); } static int file_printer_write(int prnum, int outchar) { lpt[prnum].remaining = lpt[prnum].delay; fputc(outchar, lpt[prnum].file); return 0; } int printer_write(int prnum, int outchar) { return lpt[prnum].fops.write(prnum, outchar); } /* DANG_BEGIN_FUNCTION printer_init * * description: * Initialize printer control structures * * DANG_END_FUNCTIONS */ static struct p_fops dev_pfops = { dev_printer_open, stub_printer_write, dev_printer_close, file_printer_write }; static struct p_fops pipe_pfops = { pipe_printer_open, stub_printer_write, pipe_printer_close, file_printer_write }; void printer_init(void) { int i; emu_iodev_t io_device; io_device.read_portb = printer_io_read; io_device.write_portb = printer_io_write; io_device.read_portw = NULL; io_device.write_portw = NULL; io_device.read_portd = NULL; io_device.write_portd = NULL; io_device.handler_name = "Parallel printer"; io_device.irq = EMU_NO_IRQ; io_device.fd = -1; for (i = 0; i < 3; i++) { p_printf("LPT: initializing printer %s\n", lpt[i].dev ? lpt[i].dev : "<>"); lpt[i].file = NULL; lpt[i].remaining = -1; /* mark not accessed yet */ if (lpt[i].dev) lpt[i].fops = dev_pfops; else if (lpt[i].prtcmd) lpt[i].fops = pipe_pfops; if (i >= config.num_lpt) lpt[i].base_port = 0; if (lpt[i].base_port != 0 && lpt[i].fops.open) { io_device.start_addr = lpt[i].base_port; io_device.end_addr = lpt[i].base_port + 2; port_register_handler(io_device, 0); } } } void close_all_printers(void) { int loop; for (loop = 0; loop < NUM_PRINTERS; loop++) { p_printf("LPT: closing printer %d (%s)\n", loop, lpt[loop].dev ? lpt[loop].dev : "<>"); printer_close(loop); } } int printer_tick(u_long secno) { int i; for (i = 0; i < NUM_PRINTERS; i++) { if (lpt[i].remaining >= 0) { p_printf("LPT: doing real tick for %d\n", i); if (lpt[i].remaining) { reset_idle(2); lpt[i].remaining--; if (!lpt[i].remaining) printer_close(i); else if (lpt[i].file != NULL) fflush(lpt[i].file); } } } return 0; } void printer_config(int prnum, struct printer *pptr) { struct printer *destptr; if (prnum < NUM_PRINTERS) { destptr = &lpt[prnum]; destptr->prtcmd = pptr->prtcmd; destptr->dev = pptr->dev; destptr->file = pptr->file; destptr->remaining = pptr->remaining; destptr->delay = pptr->delay; } } void printer_print_config(int prnum, void (*print)(const char *, ...)) { struct printer *pptr = &lpt[prnum]; (*print)("LPT%d command \"%s\" timeout %d device \"%s\" baseport 0x%03x\n", prnum+1, (pptr->prtcmd ? pptr->prtcmd : ""), pptr->delay, (pptr->dev ? pptr->dev : ""), pptr->base_port); } #undef LPT_C