On 2/5/07, Marko Kohtala <[EMAIL PROTECTED]> wrote:
Fabrice Bellard:
>Marko Kohtala wrote:
>
>    Hi.
>
>    With the attached patch I am able to use Kodak Advantix FD 300 APS
>    scanner from Win98 when hosted under Linux ix86. It adds EPP support
>    and fixes some register bits to match real hw so port identification
>    works better.
>
>    I tried to separate the linux dependencies with #ifdef __linux__, but
>    can not test this does not break compilation for other ports of qemu.
>
>I don't understand why you need a #ifdef __linux__ in parallel.c.
Normally, the qemu >character device should suffice to do the
appropriate abstraction.
...
I am too new with qemu to say how to develop the qemu character device
abstraction for these requirements.

Thanks for the offline tip, Fabrice.

Here is a new patch for EPP support. This time all linux code is in
ioctls. I hope you find it good enough.

Marko
diff --git a/hw/parallel.c b/hw/parallel.c
index cba9561..8f3495a 100644
--- a/hw/parallel.c
+++ b/hw/parallel.c
@@ -2,6 +2,7 @@
  * QEMU Parallel PORT emulation
  * 
  * Copyright (c) 2003-2005 Fabrice Bellard
+ * Copyright (c) 2007 Marko Kohtala
  * 
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
@@ -25,6 +26,18 @@
 
 //#define DEBUG_PARALLEL
 
+#ifdef DEBUG_PARALLEL
+#define pdebug(fmt, arg...) printf("pp: " fmt, ##arg)
+#else
+#define pdebug(fmt, arg...) ((void)0)
+#endif
+
+#define PARA_REG_DATA 0
+#define PARA_REG_STS 1
+#define PARA_REG_CTR 2
+#define PARA_REG_EPP_ADDR 3
+#define PARA_REG_EPP_DATA 4
+
 /*
  * These are the definitions for the Printer Status Register
  */
@@ -33,24 +46,31 @@
 #define PARA_STS_PAPER	0x20	/* Out of paper */
 #define PARA_STS_ONLINE	0x10	/* Online */
 #define PARA_STS_ERROR	0x08	/* Error complement */
+#define PARA_STS_TMOUT	0x01	/* EPP timeout */
 
 /*
  * These are the definitions for the Printer Control Register
  */
+#define PARA_CTR_DIR	0x20	/* Direction (1=read, 0=write) */
 #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 */
 
+#define PARA_CTR_SIGNAL (PARA_CTR_SELECT|PARA_CTR_INIT|PARA_CTR_AUTOLF|PARA_CTR_STROBE)
+
 struct ParallelState {
-    uint8_t data;
-    uint8_t status; /* read only register */
+    uint8_t dataw;
+    uint8_t datar;
+    uint8_t status;
     uint8_t control;
     int irq;
     int irq_pending;
     CharDriverState *chr;
     int hw_driver;
+    int epp_timeout;
+    uint32_t last_read_offset; /* For debugging */
 };
 
 static void parallel_update_irq(ParallelState *s)
@@ -61,96 +81,322 @@ static void parallel_update_irq(ParallelState *s)
         pic_set_irq(s->irq, 0);
 }
 
-static void parallel_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+static void
+parallel_ioport_write_sw(void *opaque, uint32_t addr, uint32_t val)
 {
     ParallelState *s = opaque;
     
+    pdebug("write addr=0x%02x val=0x%02x\n", addr, val);
+
+    addr &= 7;
+    switch(addr) {
+    case PARA_REG_DATA:
+	s->dataw = val;
+	parallel_update_irq(s);
+        break;
+    case PARA_REG_CTR:
+	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->dataw, 1);
+	    } else {
+		if (s->control & PARA_CTR_INTEN) {
+		    s->irq_pending = 1;
+		}
+	    }
+	}
+	parallel_update_irq(s);
+	s->control = val;
+        break;
+    }
+}
+
+static void parallel_ioport_write_hw(void *opaque, uint32_t addr, uint32_t val)
+{
+    ParallelState *s = opaque;
+    uint8_t parm = val;
+
+    /* Sometimes programs do several writes for timing purposes on old
+       HW. Take care not to waste time on writes that do nothing. */
+
+    s->last_read_offset = ~0U;
+
     addr &= 7;
-#ifdef DEBUG_PARALLEL
-    printf("parallel: write addr=0x%02x val=0x%02x\n", addr, val);
-#endif
     switch(addr) {
-    case 0:
-        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);
-        }
+    case PARA_REG_DATA:
+        if (s->dataw == val)
+	    return;
+	pdebug("wd%02x\n", val);
+	qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_WRITE_DATA, &parm);
+	s->dataw = val;
         break;
-    case 2:
-        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;
-        }
+    case PARA_REG_STS:
+	pdebug("ws%02x\n", val);
+	if (val & PARA_STS_TMOUT)
+	    s->epp_timeout = 0;
+	break;
+    case PARA_REG_CTR:
+        val |= 0xc0;
+        if (s->control == val)
+	    return;
+	pdebug("wc%02x\n", val);
+	qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_WRITE_CONTROL, &parm);
+	s->control = val;
         break;
+    case PARA_REG_EPP_ADDR:
+	if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != PARA_CTR_INIT)
+	    /* Controls not correct for EPP address cycle, so do nothing */
+	    pdebug("wa%02x s\n", val);
+	else {
+	    struct ParallelIOArg ioarg = { .buffer = &parm, .count = 1 };
+	    if (qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_EPP_WRITE_ADDR, &ioarg)) {
+		s->epp_timeout = 1;
+		pdebug("wa%02x t\n", val);
+	    }
+	    else
+		pdebug("wa%02x\n", val);
+	}
+	break;
+    case PARA_REG_EPP_DATA:
+	if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != PARA_CTR_INIT)
+	    /* Controls not correct for EPP data cycle, so do nothing */
+	    pdebug("we%02x s\n", val);
+	else {
+	    struct ParallelIOArg ioarg = { .buffer = &parm, .count = 1 };
+	    if (qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_EPP_WRITE, &ioarg)) {
+		s->epp_timeout = 1;
+		pdebug("we%02x t\n", val);
+	    }
+	    else
+		pdebug("we%02x\n", val);
+	}
+	break;
+    }
+}
+
+static void
+parallel_ioport_eppdata_write_hw2(void *opaque, uint32_t addr, uint32_t val)
+{
+    ParallelState *s = opaque;
+    uint16_t eppdata = cpu_to_le16(val);
+    int err;
+    struct ParallelIOArg ioarg = {
+	.buffer = &eppdata, .count = sizeof(eppdata)
+    };
+    if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != PARA_CTR_INIT) {
+	/* Controls not correct for EPP data cycle, so do nothing */
+	pdebug("we%04x s\n", val);
+	return;
+    }
+    err = qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_EPP_WRITE, &ioarg);
+    if (err) {
+	s->epp_timeout = 1;
+	pdebug("we%04x t\n", val);
+    }
+    else
+	pdebug("we%04x\n", val);
+}
+
+static void
+parallel_ioport_eppdata_write_hw4(void *opaque, uint32_t addr, uint32_t val)
+{
+    ParallelState *s = opaque;
+    uint32_t eppdata = cpu_to_le32(val);
+    int err;
+    struct ParallelIOArg ioarg = {
+	.buffer = &eppdata, .count = sizeof(eppdata)
+    };
+    if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != PARA_CTR_INIT) {
+	/* Controls not correct for EPP data cycle, so do nothing */
+	pdebug("we%08x s\n", val);
+	return;
+    }
+    err = qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_EPP_WRITE, &ioarg);
+    if (err) {
+	s->epp_timeout = 1;
+	pdebug("we%08x t\n", val);
     }
+    else
+	pdebug("we%08x\n", val);
 }
 
-static uint32_t parallel_ioport_read(void *opaque, uint32_t addr)
+static uint32_t parallel_ioport_read_sw(void *opaque, uint32_t addr)
 {
     ParallelState *s = opaque;
     uint32_t ret = 0xff;
 
     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; 
+    case PARA_REG_DATA:
+	if (s->control & PARA_CTR_DIR)
+	    ret = s->datar;
+	else
+	    ret = s->dataw;
         break;
-    case 1:
-        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);
-        }
+    case PARA_REG_STS:
+	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:
-        if (s->hw_driver) {
-            qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_READ_CONTROL, &s->control);
-        }
+    case PARA_REG_CTR:
         ret = s->control;
         break;
     }
-#ifdef DEBUG_PARALLEL
-    printf("parallel: read addr=0x%02x val=0x%02x\n", addr, ret);
-#endif
+    pdebug("read addr=0x%02x val=0x%02x\n", addr, ret);
+    return ret;
+}
+
+static uint32_t parallel_ioport_read_hw(void *opaque, uint32_t addr)
+{
+    ParallelState *s = opaque;
+    uint8_t ret = 0xff;
+    addr &= 7;
+    switch(addr) {
+    case PARA_REG_DATA:
+	qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_READ_DATA, &ret);
+	if (s->last_read_offset != addr || s->datar != ret)
+	    pdebug("rd%02x\n", ret);
+        s->datar = ret; 
+        break;
+    case PARA_REG_STS:
+	qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_READ_STATUS, &ret);
+	ret &= ~PARA_STS_TMOUT;
+	if (s->epp_timeout)
+	    ret |= PARA_STS_TMOUT;
+	if (s->last_read_offset != addr || s->status != ret)
+	    pdebug("rs%02x\n", ret);
+	s->status = ret;
+        break;
+    case PARA_REG_CTR:
+        /* s->control has some bits fixed to 1. It is zero only when
+	   it has not been yet written to.  */
+	if (s->control == 0) {
+	    qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_READ_CONTROL, &ret);
+	    if (s->last_read_offset != addr)
+		pdebug("rc%02x\n", ret);
+	    s->control = ret;
+	}
+	else {
+	    ret = s->control;
+	    if (s->last_read_offset != addr)
+		pdebug("rc%02x\n", ret);
+	}
+        break;
+    case PARA_REG_EPP_ADDR:
+	if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != (PARA_CTR_DIR|PARA_CTR_INIT))
+	    /* Controls not correct for EPP addr cycle, so do nothing */
+	    pdebug("ra%02x s\n", ret);
+	else {
+	    struct ParallelIOArg ioarg = { .buffer = &ret, .count = 1 };
+	    if (qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_EPP_READ_ADDR, &ioarg)) {
+		s->epp_timeout = 1;
+		pdebug("ra%02x t\n", ret);
+	    }
+	    else
+		pdebug("ra%02x\n", ret);
+	}
+	break;
+    case PARA_REG_EPP_DATA:
+	if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != (PARA_CTR_DIR|PARA_CTR_INIT))
+	    /* Controls not correct for EPP data cycle, so do nothing */
+	    pdebug("re%02x s\n", ret);
+	else {
+	    struct ParallelIOArg ioarg = { .buffer = &ret, .count = 1 };
+	    if (qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_EPP_READ, &ioarg)) {
+		s->epp_timeout = 1;
+		pdebug("re%02x t\n", ret);
+	    }
+	    else
+		pdebug("re%02x\n", ret);
+	}
+	break;
+    }
+    s->last_read_offset = addr;
+    return ret;
+}
+
+static uint32_t
+parallel_ioport_eppdata_read_hw2(void *opaque, uint32_t addr)
+{
+    ParallelState *s = opaque;
+    uint32_t ret;
+    uint16_t eppdata = ~0;
+    int err;
+    struct ParallelIOArg ioarg = {
+	.buffer = &eppdata, .count = sizeof(eppdata)
+    };
+    if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != (PARA_CTR_DIR|PARA_CTR_INIT)) {
+	/* Controls not correct for EPP data cycle, so do nothing */
+	pdebug("re%04x s\n", eppdata);
+	return eppdata;
+    }
+    err = qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_EPP_READ, &ioarg);
+    ret = le16_to_cpu(eppdata);
+
+    if (err) {
+	s->epp_timeout = 1;
+	pdebug("re%04x t\n", ret);
+    }
+    else
+	pdebug("re%04x\n", ret);
+    return ret;
+}
+
+static uint32_t
+parallel_ioport_eppdata_read_hw4(void *opaque, uint32_t addr)
+{
+    ParallelState *s = opaque;
+    uint32_t ret;
+    uint32_t eppdata = ~0U;
+    int err;
+    struct ParallelIOArg ioarg = {
+	.buffer = &eppdata, .count = sizeof(eppdata)
+    };
+    if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != (PARA_CTR_DIR|PARA_CTR_INIT)) {
+	/* Controls not correct for EPP data cycle, so do nothing */
+	pdebug("re%08x s\n", eppdata);
+	return eppdata;
+    }
+    err = qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_EPP_READ, &ioarg);
+    ret = le32_to_cpu(eppdata);
+
+    if (err) {
+	s->epp_timeout = 1;
+	pdebug("re%08x t\n", ret);
+    }
+    else
+	pdebug("re%08x\n", ret);
+    return ret;
+}
+
+static void parallel_ioport_ecp_write(void *opaque, uint32_t addr, uint32_t val)
+{
+    addr &= 7;
+    pdebug("wecp%d=%02x\n", addr, val);
+}
+
+static uint32_t parallel_ioport_ecp_read(void *opaque, uint32_t addr)
+{
+    uint8_t ret = 0xff;
+    addr &= 7;
+    pdebug("recp%d:%02x\n", addr, ret);
     return ret;
 }
 
@@ -163,21 +409,39 @@ ParallelState *parallel_init(int base, int irq, CharDriverState *chr)
     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->datar = ~0;
+    s->dataw = ~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;
+    s->irq = irq;
+    s->irq_pending = 0;
+    s->chr = chr;
+    s->hw_driver = 0;
+    s->epp_timeout = 0;
+    s->last_read_offset = ~0U;
 
-    register_ioport_write(base, 8, 1, parallel_ioport_write, s);
-    register_ioport_read(base, 8, 1, parallel_ioport_read, s);
+    if (qemu_chr_ioctl(chr, CHR_IOCTL_PP_READ_STATUS, &dummy) == 0) {
+        s->hw_driver = 1;
+	s->status = dummy;
+    }
+
+    if (s->hw_driver) {
+	register_ioport_write(base, 8, 1, parallel_ioport_write_hw, s);
+	register_ioport_read(base, 8, 1, parallel_ioport_read_hw, s);
+	register_ioport_write(base+4, 1, 2, parallel_ioport_eppdata_write_hw2, s);
+	register_ioport_read(base+4, 1, 2, parallel_ioport_eppdata_read_hw2, s);
+	register_ioport_write(base+4, 1, 4, parallel_ioport_eppdata_write_hw4, s);
+	register_ioport_read(base+4, 1, 4, parallel_ioport_eppdata_read_hw4, s);
+	register_ioport_write(base+0x400, 8, 1, parallel_ioport_ecp_write, s);
+	register_ioport_read(base+0x400, 8, 1, parallel_ioport_ecp_read, s);
+    }
+    else {
+	register_ioport_write(base, 8, 1, parallel_ioport_write_sw, s);
+	register_ioport_read(base, 8, 1, parallel_ioport_read_sw, s);
+    }
     return s;
 }
diff --git a/qemu-doc.texi b/qemu-doc.texi
index 9bc6b5a..a4c9970 100644
--- a/qemu-doc.texi
+++ b/qemu-doc.texi
@@ -541,7 +541,7 @@ void device
 parameters are set according to the emulated ones.
 @item /dev/parportN
 [Linux only, parallel port only] Use host parallel port
[EMAIL PROTECTED] Currently only SPP parallel port features can be used.
[EMAIL PROTECTED] Currently SPP and EPP parallel port features can be used.
 @item file:filename
 Write output to filename. No character can be read.
 @item stdio
diff --git a/vl.c b/vl.c
index 71e21be..112f3a7 100644
--- a/vl.c
+++ b/vl.c
@@ -55,6 +55,7 @@
 #include <malloc.h>
 #include <linux/rtc.h>
 #include <linux/ppdev.h>
+#include <linux/parport.h>
 #endif
 #endif
 #endif
@@ -1798,9 +1799,26 @@ static CharDriverState *qemu_chr_open_tty(const char *filename)
     return chr;
 }
 
+typedef struct {
+    int fd;
+    int mode;
+} ParallelCharDriver;
+
+static int pp_hw_mode(ParallelCharDriver *s, uint16_t mode)
+{
+    if (s->mode != mode) {
+	int m = mode;
+        if (ioctl(s->fd, PPSETMODE, &m) < 0)
+            return 0;
+	s->mode = mode;
+    }
+    return 1;
+}
+
 static int pp_ioctl(CharDriverState *chr, int cmd, void *arg)
 {
-    int fd = (int)chr->opaque;
+    ParallelCharDriver *drv = chr->opaque;
+    int fd = drv->fd;
     uint8_t b;
 
     switch(cmd) {
@@ -1817,7 +1835,10 @@ static int pp_ioctl(CharDriverState *chr, int cmd, void *arg)
     case CHR_IOCTL_PP_READ_CONTROL:
         if (ioctl(fd, PPRCONTROL, &b) < 0)
             return -ENOTSUP;
-        *(uint8_t *)arg = b;
+	/* Linux gives only the lowest bits, and no way to know data
+	   direction! For better compatibility set the fixed upper
+	   bits. */
+        *(uint8_t *)arg = b | 0xc0;
         break;
     case CHR_IOCTL_PP_WRITE_CONTROL:
         b = *(uint8_t *)arg;
@@ -1829,15 +1850,63 @@ static int pp_ioctl(CharDriverState *chr, int cmd, void *arg)
             return -ENOTSUP;
         *(uint8_t *)arg = b;
         break;
+    case CHR_IOCTL_PP_EPP_READ_ADDR:
+	if (pp_hw_mode(drv, IEEE1284_MODE_EPP|IEEE1284_ADDR)) {
+	    struct ParallelIOArg *parg = arg;
+	    int n = read(fd, parg->buffer, parg->count);
+	    if (n != parg->count) {
+		return -EIO;
+	    }
+	}
+        break;
+    case CHR_IOCTL_PP_EPP_READ:
+	if (pp_hw_mode(drv, IEEE1284_MODE_EPP)) {
+	    struct ParallelIOArg *parg = arg;
+	    int n = read(fd, parg->buffer, parg->count);
+	    if (n != parg->count) {
+		return -EIO;
+	    }
+	}
+        break;
+    case CHR_IOCTL_PP_EPP_WRITE_ADDR:
+	if (pp_hw_mode(drv, IEEE1284_MODE_EPP|IEEE1284_ADDR)) {
+	    struct ParallelIOArg *parg = arg;
+	    int n = write(fd, parg->buffer, parg->count);
+	    if (n != parg->count) {
+		return -EIO;
+	    }
+	}
+        break;
+    case CHR_IOCTL_PP_EPP_WRITE:
+	if (pp_hw_mode(drv, IEEE1284_MODE_EPP)) {
+	    struct ParallelIOArg *parg = arg;
+	    int n = write(fd, parg->buffer, parg->count);
+	    if (n != parg->count) {
+		return -EIO;
+	    }
+	}
+        break;
     default:
         return -ENOTSUP;
     }
     return 0;
 }
 
+static void pp_close(CharDriverState *chr)
+{
+    ParallelCharDriver *drv = chr->opaque;
+    int fd = drv->fd;
+
+    pp_hw_mode(drv, IEEE1284_MODE_COMPAT);
+    ioctl(fd, PPRELEASE);
+    close(fd);
+    qemu_free(drv);
+}
+
 static CharDriverState *qemu_chr_open_pp(const char *filename)
 {
     CharDriverState *chr;
+    ParallelCharDriver *drv;
     int fd;
 
     fd = open(filename, O_RDWR);
@@ -1849,14 +1918,24 @@ static CharDriverState *qemu_chr_open_pp(const char *filename)
         return NULL;
     }
 
+    drv = qemu_mallocz(sizeof(ParallelCharDriver));
+    if (!drv) {
+        close(fd);
+        return NULL;
+    }
+    drv->fd = fd;
+    drv->mode = IEEE1284_MODE_COMPAT;
+
     chr = qemu_mallocz(sizeof(CharDriverState));
     if (!chr) {
+	qemu_free(drv);
         close(fd);
         return NULL;
     }
-    chr->opaque = (void *)fd;
     chr->chr_write = null_chr_write;
     chr->chr_ioctl = pp_ioctl;
+    chr->chr_close = pp_close;
+    chr->opaque = drv;
 
     qemu_chr_reset(chr);
 
diff --git a/vl.h b/vl.h
index a06ce18..79de55f 100644
--- a/vl.h
+++ b/vl.h
@@ -285,6 +285,10 @@ typedef struct {
 #define CHR_IOCTL_PP_READ_CONTROL     5
 #define CHR_IOCTL_PP_WRITE_CONTROL    6
 #define CHR_IOCTL_PP_READ_STATUS      7
+#define CHR_IOCTL_PP_EPP_READ_ADDR    8
+#define CHR_IOCTL_PP_EPP_READ         9
+#define CHR_IOCTL_PP_EPP_WRITE_ADDR  10
+#define CHR_IOCTL_PP_EPP_WRITE       11
 
 typedef void IOEventHandler(void *opaque, int event);
 
@@ -349,6 +353,11 @@ extern CharDriverState *serial_hds[MAX_SERIAL_PORTS];
 
 extern CharDriverState *parallel_hds[MAX_PARALLEL_PORTS];
 
+struct ParallelIOArg {
+    void *buffer;
+    int count;
+};
+
 /* VLANs support */
 
 typedef struct VLANClientState VLANClientState;
_______________________________________________
Qemu-devel mailing list
Qemu-devel@nongnu.org
http://lists.nongnu.org/mailman/listinfo/qemu-devel

Reply via email to