diff --git a/Changelog b/Changelog index 32c417b6f836673c55796085a802f199e1d2c7dd..6b8420fd9385293616b505556cf176dd2f492411 100644 --- a/Changelog +++ b/Changelog @@ -8,6 +8,7 @@ version 0.7.3: - ES1370 PCI audio device (malc) - Initial USB support - Linux host serial port access + - Linux host low level parallel port access version 0.7.2: diff --git a/hw/parallel.c b/hw/parallel.c index a304d7fdf7ae24b4fcea7a5319f5e2460de45e04..95ac7fd688a10383451b0c37dfee18c0918377a9 100644 --- a/hw/parallel.c +++ b/hw/parallel.c @@ -1,7 +1,7 @@ /* * QEMU Parallel PORT emulation * - * Copyright (c) 2003-2004 Fabrice Bellard + * Copyright (c) 2003-2005 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 @@ -50,6 +50,7 @@ struct ParallelState { int irq; int irq_pending; CharDriverState *chr; + int hw_driver; }; static void parallel_update_irq(ParallelState *s) @@ -70,29 +71,39 @@ static void parallel_ioport_write(void *opaque, uint32_t addr, uint32_t val) #endif switch(addr) { case 0: - s->data = val; - parallel_update_irq(s); + if (s->hw_driver) { + s->data = val; + qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_WRITE_DATA, &s->data); + } else { + 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; + if (s->hw_driver) { + s->control = val; + qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_WRITE_CONTROL, &s->control); + } else { + 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; } - parallel_update_irq(s); - s->control = val; break; } } @@ -105,24 +116,35 @@ static uint32_t parallel_ioport_read(void *opaque, uint32_t addr) addr &= 7; switch(addr) { case 0: + if (s->hw_driver) { + qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_READ_DATA, &s->data); + } 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; + if (s->hw_driver) { + qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_READ_STATUS, &s->status); + ret = s->status; + } else { + 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); } - parallel_update_irq(s); break; case 2: + if (s->hw_driver) { + qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_READ_CONTROL, &s->control); + } ret = s->control; break; } @@ -153,18 +175,20 @@ static void parallel_receive1(void *opaque, const uint8_t *buf, int size) parallel_receive_byte(s, buf[0]); } -static void parallel_event(void *opaque, int event) -{ -} - /* If fd is zero, it means that the parallel device uses the console */ ParallelState *parallel_init(int base, int irq, CharDriverState *chr) { ParallelState *s; + uint8_t dummy; s = qemu_mallocz(sizeof(ParallelState)); if (!s) return NULL; + s->chr = chr; + s->hw_driver = 0; + if (qemu_chr_ioctl(chr, CHR_IOCTL_PP_READ_STATUS, &dummy) == 0) + s->hw_driver = 1; + s->irq = irq; s->data = 0; s->status = PARA_STS_BUSY; @@ -176,8 +200,6 @@ ParallelState *parallel_init(int base, int irq, CharDriverState *chr) 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/qemu-doc.texi b/qemu-doc.texi index 253484fcdad5dc9862d825e98f35865369dccc11..41b7422789b362bf693d254d3dc7563213ccdfa3 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -364,8 +364,11 @@ Virtual console @item null void device @item /dev/XXX -[Linux only]Use host tty, e.g. @file{/dev/ttyS0}. The host serial port +[Linux only] Use host tty, e.g. @file{/dev/ttyS0}. The host serial port parameters are set according to the emulated ones. +@item /dev/parportN +[Linux only, parallel port only] Use host parallel port +@var{N}. Currently only SPP parallel port features can be used. @item file:filename Write output to filename. No character can be read. @item stdio @@ -379,6 +382,15 @@ non graphical mode. This option can be used several times to simulate up to 4 serials ports. +@item -parallel dev +Redirect the virtual parallel port to host device @var{dev} (same +devices as the serial port). On Linux hosts, @file{/dev/parportN} can +be used to use hardware devices connected on the corresponding host +parallel port. + +This option can be used several times to simulate up to 3 parallel +ports. + @item -monitor dev Redirect the monitor to host device @var{dev} (same devices as the serial port). diff --git a/vl.c b/vl.c index 75ec23bb37d2baed1aac867357cb1326f7e112ed..2ced8886843daceb92a04351e82108863d65e0c2 100644 --- a/vl.c +++ b/vl.c @@ -51,6 +51,7 @@ #include #include #include +#include #endif #endif @@ -1013,21 +1014,13 @@ int qemu_chr_write(CharDriverState *s, const uint8_t *buf, int len) return s->chr_write(s, buf, len); } -void qemu_chr_set_serial_parameters(CharDriverState *s, - int speed, int parity, - int data_bits, int stop_bits) +int qemu_chr_ioctl(CharDriverState *s, int cmd, void *arg) { - if (s->chr_set_serial_parameters) - s->chr_set_serial_parameters(s, speed, parity, data_bits, stop_bits); + if (!s->chr_ioctl) + return -ENOTSUP; + return s->chr_ioctl(s, cmd, arg); } -void qemu_chr_set_serial_break(CharDriverState *s, int enable) -{ - if (s->chr_set_serial_break) - s->chr_set_serial_break(s, enable); -} - - void qemu_chr_printf(CharDriverState *s, const char *fmt, ...) { char buf[4096]; @@ -1379,7 +1372,11 @@ static void tty_serial_init(int fd, int speed, struct termios tty; speed_t spd; - tcgetattr (0, &tty); +#if 0 + printf("tty_serial_init: speed=%d parity=%c data=%d stop=%d\n", + speed, parity, data_bits, stop_bits); +#endif + tcgetattr (fd, &tty); switch(speed) { case 50: @@ -1459,20 +1456,29 @@ static void tty_serial_init(int fd, int speed, tcsetattr (fd, TCSANOW, &tty); } -static void tty_set_serial_parameters(CharDriverState *chr, - int speed, int parity, - int data_bits, int stop_bits) +static int tty_serial_ioctl(CharDriverState *chr, int cmd, void *arg) { FDCharDriver *s = chr->opaque; - tty_serial_init(s->fd_in, speed, parity, data_bits, stop_bits); -} - -static void tty_set_serial_break(CharDriverState *chr, int enable) -{ - FDCharDriver *s = chr->opaque; - /* XXX: find a better solution */ - if (enable) - tcsendbreak(s->fd_in, 1); + + switch(cmd) { + case CHR_IOCTL_SERIAL_SET_PARAMS: + { + QEMUSerialSetParams *ssp = arg; + tty_serial_init(s->fd_in, ssp->speed, ssp->parity, + ssp->data_bits, ssp->stop_bits); + } + break; + case CHR_IOCTL_SERIAL_SET_BREAK: + { + int enable = *(int *)arg; + if (enable) + tcsendbreak(s->fd_in, 1); + } + break; + default: + return -ENOTSUP; + } + return 0; } CharDriverState *qemu_chr_open_tty(const char *filename) @@ -1480,7 +1486,7 @@ CharDriverState *qemu_chr_open_tty(const char *filename) CharDriverState *chr; int fd; - fd = open(filename, O_RDWR); + fd = open(filename, O_RDWR | O_NONBLOCK); if (fd < 0) return NULL; fcntl(fd, F_SETFL, O_NONBLOCK); @@ -1488,8 +1494,70 @@ CharDriverState *qemu_chr_open_tty(const char *filename) chr = qemu_chr_open_fd(fd, fd); if (!chr) return NULL; - chr->chr_set_serial_parameters = tty_set_serial_parameters; - chr->chr_set_serial_break = tty_set_serial_break; + chr->chr_ioctl = tty_serial_ioctl; + return chr; +} + +static int pp_ioctl(CharDriverState *chr, int cmd, void *arg) +{ + int fd = (int)chr->opaque; + uint8_t b; + + switch(cmd) { + case CHR_IOCTL_PP_READ_DATA: + if (ioctl(fd, PPRDATA, &b) < 0) + return -ENOTSUP; + *(uint8_t *)arg = b; + break; + case CHR_IOCTL_PP_WRITE_DATA: + b = *(uint8_t *)arg; + if (ioctl(fd, PPWDATA, &b) < 0) + return -ENOTSUP; + break; + case CHR_IOCTL_PP_READ_CONTROL: + if (ioctl(fd, PPRCONTROL, &b) < 0) + return -ENOTSUP; + *(uint8_t *)arg = b; + break; + case CHR_IOCTL_PP_WRITE_CONTROL: + b = *(uint8_t *)arg; + if (ioctl(fd, PPWCONTROL, &b) < 0) + return -ENOTSUP; + break; + case CHR_IOCTL_PP_READ_STATUS: + if (ioctl(fd, PPRSTATUS, &b) < 0) + return -ENOTSUP; + *(uint8_t *)arg = b; + break; + default: + return -ENOTSUP; + } + return 0; +} + +CharDriverState *qemu_chr_open_pp(const char *filename) +{ + CharDriverState *chr; + int fd; + + fd = open(filename, O_RDWR); + if (fd < 0) + return NULL; + + if (ioctl(fd, PPCLAIM) < 0) { + close(fd); + return NULL; + } + + chr = qemu_mallocz(sizeof(CharDriverState)); + if (!chr) { + close(fd); + return NULL; + } + chr->opaque = (void *)fd; + chr->chr_write = null_chr_write; + chr->chr_add_read_handler = null_chr_add_read_handler; + chr->chr_ioctl = pp_ioctl; return chr; } @@ -1522,6 +1590,9 @@ CharDriverState *qemu_chr_open(const char *filename) } else #endif #if defined(__linux__) + if (strstart(filename, "/dev/parport", NULL)) { + return qemu_chr_open_pp(filename); + } else if (strstart(filename, "/dev/", NULL)) { return qemu_chr_open_tty(filename); } else