diff --git a/Changelog b/Changelog index 539e3ec962..c1e5fff7de 100644 --- a/Changelog +++ b/Changelog @@ -12,6 +12,7 @@ version 0.6.2: - initial x86_64 target support - initial APIC support - MMX/SSE/SSE2/PNI support + - PC parallel port support (Mark Jonckheere) version 0.6.1: diff --git a/Makefile.target b/Makefile.target index 942a105ea6..6c49acfb24 100644 --- a/Makefile.target +++ b/Makefile.target @@ -316,7 +316,7 @@ ifeq ($(TARGET_BASE_ARCH), i386) # Hardware support VL_OBJS+= ide.o ne2000.o pckbd.o vga.o $(SOUND_HW) dma.o $(AUDIODRV) VL_OBJS+= fdc.o mc146818rtc.o serial.o i8259.o i8254.o pc.o -VL_OBJS+= cirrus_vga.o mixeng.o apic.o +VL_OBJS+= cirrus_vga.o mixeng.o apic.o parallel.o endif ifeq ($(TARGET_ARCH), ppc) VL_OBJS+= ppc.o ide.o ne2000.o pckbd.o vga.o $(SOUND_HW) dma.o $(AUDIODRV) diff --git a/hw/parallel.c b/hw/parallel.c new file mode 100644 index 0000000000..1b9fe7cef5 --- /dev/null +++ b/hw/parallel.c @@ -0,0 +1,188 @@ +/* + * QEMU Parallel PORT emulation + * + * Copyright (c) 2003-2004 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" + +//#define DEBUG_PARALLEL + +/* + * These are the definitions for the Printer Status Register + */ +#define PARA_STS_BUSY 0x80 /* Busy complement */ +#define PARA_STS_ACK 0x40 /* Acknowledge */ +#define PARA_STS_PAPER 0x20 /* Out of paper */ +#define PARA_STS_ONLINE 0x10 /* Online */ +#define PARA_STS_ERROR 0x08 /* Error complement */ + +/* + * These are the definitions for the Printer Control Register + */ +#define PARA_CTR_INTEN 0x10 /* IRQ Enable */ +#define PARA_CTR_SELECT 0x08 /* Select In complement */ +#define PARA_CTR_INIT 0x04 /* Initialize Printer complement */ +#define PARA_CTR_AUTOLF 0x02 /* Auto linefeed complement */ +#define PARA_CTR_STROBE 0x01 /* Strobe complement */ + +struct ParallelState { + uint8_t data; + uint8_t status; /* read only register */ + uint8_t control; + int irq; + int irq_pending; + CharDriverState *chr; +}; + +static void parallel_update_irq(ParallelState *s) +{ + if (s->irq_pending) + pic_set_irq(s->irq, 1); + else + pic_set_irq(s->irq, 0); +} + +static void parallel_ioport_write(void *opaque, uint32_t addr, uint32_t val) +{ + ParallelState *s = opaque; + + addr &= 7; +#ifdef DEBUG_PARALLEL + printf("parallel: write addr=0x%02x val=0x%02x\n", addr, val); +#endif + switch(addr) { + case 0: + s->data = val; + parallel_update_irq(s); + break; + case 2: + if ((val & PARA_CTR_INIT) == 0 ) { + s->status = PARA_STS_BUSY; + s->status |= PARA_STS_ACK; + s->status |= PARA_STS_ONLINE; + s->status |= PARA_STS_ERROR; + } + else if (val & PARA_CTR_SELECT) { + if (val & PARA_CTR_STROBE) { + s->status &= ~PARA_STS_BUSY; + if ((s->control & PARA_CTR_STROBE) == 0) + qemu_chr_write(s->chr, &s->data, 1); + } else { + if (s->control & PARA_CTR_INTEN) { + s->irq_pending = 1; + } + } + } + parallel_update_irq(s); + s->control = val; + break; + } +} + +static uint32_t parallel_ioport_read(void *opaque, uint32_t addr) +{ + ParallelState *s = opaque; + uint32_t ret = 0xff; + + addr &= 7; + switch(addr) { + case 0: + ret = s->data; + break; + case 1: + ret = s->status; + s->irq_pending = 0; + if ((s->status & PARA_STS_BUSY) == 0 && (s->control & PARA_CTR_STROBE) == 0) { + /* XXX Fixme: wait 5 microseconds */ + if (s->status & PARA_STS_ACK) + s->status &= ~PARA_STS_ACK; + else { + /* XXX Fixme: wait 5 microseconds */ + s->status |= PARA_STS_ACK; + s->status |= PARA_STS_BUSY; + } + } + parallel_update_irq(s); + break; + case 2: + ret = s->control; + break; + } +#ifdef DEBUG_PARALLEL + printf("parallel: read addr=0x%02x val=0x%02x\n", addr, ret); +#endif + return ret; +} + +static int parallel_can_receive(ParallelState *s) +{ + return 0; +} + +static void parallel_receive_byte(ParallelState *s, int ch) +{ +} + +static void parallel_receive_break(ParallelState *s) +{ +} + +static int parallel_can_receive1(void *opaque) +{ + ParallelState *s = opaque; + return parallel_can_receive(s); +} + +static void parallel_receive1(void *opaque, const uint8_t *buf, int size) +{ + ParallelState *s = opaque; + parallel_receive_byte(s, buf[0]); +} + +static void parallel_event(void *opaque, int event) +{ + ParallelState *s = opaque; +} + +/* If fd is zero, it means that the parallel device uses the console */ +ParallelState *parallel_init(int base, int irq, CharDriverState *chr) +{ + ParallelState *s; + + s = qemu_mallocz(sizeof(ParallelState)); + if (!s) + return NULL; + s->irq = irq; + s->data = 0; + s->status = PARA_STS_BUSY; + s->status |= PARA_STS_ACK; + s->status |= PARA_STS_ONLINE; + s->status |= PARA_STS_ERROR; + s->control = PARA_CTR_SELECT; + s->control |= PARA_CTR_INIT; + + register_ioport_write(base, 8, 1, parallel_ioport_write, s); + register_ioport_read(base, 8, 1, parallel_ioport_read, s); + s->chr = chr; + qemu_chr_add_read_handler(chr, parallel_can_receive1, parallel_receive1, s); + qemu_chr_add_event_handler(chr, parallel_event); + return s; +} diff --git a/hw/pc.c b/hw/pc.c index 7b6c8946fa..5deb445a3c 100644 --- a/hw/pc.c +++ b/hw/pc.c @@ -376,6 +376,9 @@ static int ne2000_irq[NE2000_NB_MAX] = { 9, 10, 11, 3, 4, 5 }; static int serial_io[MAX_SERIAL_PORTS] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 }; static int serial_irq[MAX_SERIAL_PORTS] = { 4, 3, 4, 3 }; +static int parallel_io[MAX_PARALLEL_PORTS] = { 0x378, 0x278, 0x3bc }; +static int parallel_irq[MAX_PARALLEL_PORTS] = { 7, 7, 7 }; + /* PC hardware initialisation */ void pc_init(int ram_size, int vga_ram_size, int boot_device, DisplayState *ds, const char **fd_filename, int snapshot, @@ -538,6 +541,12 @@ void pc_init(int ram_size, int vga_ram_size, int boot_device, } } + for(i = 0; i < MAX_PARALLEL_PORTS; i++) { + if (parallel_hds[i]) { + parallel_init(parallel_io[i], parallel_irq[i], parallel_hds[i]); + } + } + if (pci_enabled) { for(i = 0; i < nb_nics; i++) { pci_ne2000_init(pci_bus, &nd_table[i]); diff --git a/vl.c b/vl.c index f478f9b23b..468032fb5f 100644 --- a/vl.c +++ b/vl.c @@ -136,6 +136,7 @@ int graphic_depth = 15; int full_screen = 0; TextConsole *vga_console; CharDriverState *serial_hds[MAX_SERIAL_PORTS]; +CharDriverState *parallel_hds[MAX_PARALLEL_PORTS]; /***********************************************************/ /* x86 ISA bus support */ @@ -2750,6 +2751,7 @@ void help(void) "Debug/Expert options:\n" "-monitor dev redirect the monitor to char device 'dev'\n" "-serial dev redirect the serial port to char device 'dev'\n" + "-parallel dev redirect the parallel port to char device 'dev'\n" "-pidfile file Write PID to 'file'\n" "-S freeze CPU at startup (use 'c' to start execution)\n" "-s wait gdb connection to port %d\n" @@ -2842,6 +2844,7 @@ enum { QEMU_OPTION_std_vga, QEMU_OPTION_monitor, QEMU_OPTION_serial, + QEMU_OPTION_parallel, QEMU_OPTION_loadvm, QEMU_OPTION_full_screen, QEMU_OPTION_pidfile, @@ -2904,6 +2907,7 @@ const QEMUOption qemu_options[] = { { "std-vga", 0, QEMU_OPTION_std_vga }, { "monitor", 1, QEMU_OPTION_monitor }, { "serial", 1, QEMU_OPTION_serial }, + { "parallel", 1, QEMU_OPTION_parallel }, { "loadvm", HAS_ARG, QEMU_OPTION_loadvm }, { "full-screen", 0, QEMU_OPTION_full_screen }, { "pidfile", HAS_ARG, QEMU_OPTION_pidfile }, @@ -2986,6 +2990,8 @@ int main(int argc, char **argv) char monitor_device[128]; char serial_devices[MAX_SERIAL_PORTS][128]; int serial_device_index; + char parallel_devices[MAX_PARALLEL_PORTS][128]; + int parallel_device_index; const char *loadvm = NULL; #if !defined(CONFIG_SOFTMMU) @@ -3019,6 +3025,11 @@ int main(int argc, char **argv) serial_devices[i][0] = '\0'; serial_device_index = 0; + pstrcpy(parallel_devices[0], sizeof(parallel_devices[0]), "vc"); + for(i = 1; i < MAX_PARALLEL_PORTS; i++) + parallel_devices[i][0] = '\0'; + parallel_device_index = 0; + nb_tun_fds = 0; net_if_type = -1; nb_nics = 1; @@ -3115,6 +3126,7 @@ int main(int argc, char **argv) case QEMU_OPTION_nographic: pstrcpy(monitor_device, sizeof(monitor_device), "stdio"); pstrcpy(serial_devices[0], sizeof(serial_devices[0]), "stdio"); + pstrcpy(parallel_devices[0], sizeof(parallel_devices[0]), "stdio"); nographic = 1; break; case QEMU_OPTION_kernel: @@ -3329,6 +3341,15 @@ int main(int argc, char **argv) sizeof(serial_devices[0]), optarg); serial_device_index++; break; + case QEMU_OPTION_parallel: + if (parallel_device_index >= MAX_PARALLEL_PORTS) { + fprintf(stderr, "qemu: too many parallel ports\n"); + exit(1); + } + pstrcpy(parallel_devices[parallel_device_index], + sizeof(parallel_devices[0]), optarg); + parallel_device_index++; + break; case QEMU_OPTION_loadvm: loadvm = optarg; break; @@ -3552,6 +3573,19 @@ int main(int argc, char **argv) } } + for(i = 0; i < MAX_PARALLEL_PORTS; i++) { + if (parallel_devices[i][0] != '\0') { + parallel_hds[i] = qemu_chr_open(parallel_devices[i]); + if (!parallel_hds[i]) { + fprintf(stderr, "qemu: could not open parallel device '%s'\n", + parallel_devices[i]); + exit(1); + } + if (!strcmp(parallel_devices[i], "vc")) + qemu_chr_printf(parallel_hds[i], "parallel%d console\n", i); + } + } + /* setup cpu signal handlers for MMU / self modifying code handling */ #if !defined(CONFIG_SOFTMMU) diff --git a/vl.h b/vl.h index a80d9057b3..39ea6550b0 100644 --- a/vl.h +++ b/vl.h @@ -224,6 +224,12 @@ void console_select(unsigned int index); extern CharDriverState *serial_hds[MAX_SERIAL_PORTS]; +/* parallel ports */ + +#define MAX_PARALLEL_PORTS 3 + +extern CharDriverState *parallel_hds[MAX_PARALLEL_PORTS]; + /* network redirectors support */ #define MAX_NICS 8 @@ -632,6 +638,11 @@ void rtc_set_date(RTCState *s, const struct tm *tm); typedef struct SerialState SerialState; SerialState *serial_init(int base, int irq, CharDriverState *chr); +/* parallel.c */ + +typedef struct ParallelState ParallelState; +ParallelState *parallel_init(int base, int irq, CharDriverState *chr); + /* i8259.c */ void pic_set_irq(int irq, int level);