qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH] parport EPP support for Linux
@ 2007-02-04 20:36 Marko Kohtala
  2007-02-04 23:25 ` Fabrice Bellard
  0 siblings, 1 reply; 4+ messages in thread
From: Marko Kohtala @ 2007-02-04 20:36 UTC (permalink / raw)
  To: qemu-devel

[-- Attachment #1: Type: text/plain, Size: 580 bytes --]

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.

Suggestions are welcome, but I also welcome you to make the
enhancements yourself. This is good enough for me.

I hope it is good enough for inclusion in Qemu. It applies and was
tested against current CVS.

Marko

[-- Attachment #2: parallel-epp.patch --]
[-- Type: text/x-patch, Size: 16772 bytes --]

diff --git a/hw/parallel.c b/hw/parallel.c
index cba9561..44c1a28 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
@@ -22,9 +23,22 @@
  * THE SOFTWARE.
  */
 #include "vl.h"
+#include <linux/parport.h>
 
 //#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 +47,33 @@
 #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;
+    uint8_t addr;
+    int mode;
     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 +84,346 @@ 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 int hw_mode(ParallelState *s, uint16_t mode)
+{
+    if (s->mode != mode) {
+	int m = mode;
+	if (qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_SETMODE, &m) < 0)
+	    return 0;
+	s->mode = mode;
+    }
+    return 1;
+}
+
+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;
-#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:
+	s->dataw = val;
+	parallel_update_irq(s);
         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_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 uint32_t parallel_ioport_read(void *opaque, uint32_t addr)
+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;
+    switch(addr) {
+    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 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);
+	/* Let's be nice and return the parport device to compatible
+	   mode when we seem to be negotiating out of EPP. */
+	if ((val & PARA_CTR_SIGNAL) != PARA_CTR_INIT)
+	    hw_mode(s, IEEE1284_MODE_COMPAT);
+	s->control = val;
+        break;
+#ifdef __linux__
+    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 if (hw_mode(s, IEEE1284_MODE_EPP|IEEE1284_ADDR)) {
+	    int n = write((int)s->chr->opaque, &parm, 1);
+	    if (n != 1) {
+		s->epp_timeout = 1;
+		pdebug("wa%02x t\n", val);
+	    }
+	    else
+		pdebug("wa%02x\n", val);
+	}
+	else
+	    pdebug("wa%02x m\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 if (hw_mode(s, IEEE1284_MODE_EPP)) {
+	    int n = write((int)s->chr->opaque, &parm, 1);
+	    if (n != 1) {
+		s->epp_timeout = 1;
+		pdebug("we%02x t\n", val);
+	    }
+	    else
+		pdebug("we%02x\n", val);
+	}
+	else
+	    pdebug("we%02x m\n", val);
+	break;
+#endif
+    }
+}
+
+#ifdef __linux__
+static void
+parallel_ioport_eppdata_write_hw2(void *opaque, uint32_t addr, uint32_t val)
+{
+    ParallelState *s = opaque;
+    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;
+    }
+    pdebug("we%04x\n", val);
+    if (hw_mode(s, IEEE1284_MODE_EPP)) {
+	uint16_t eppdata = cpu_to_le16(val);
+	int n = write((int)s->chr->opaque, (uint8_t*)&eppdata, sizeof(eppdata));
+	if (n != sizeof(eppdata))
+	    s->epp_timeout = 1;
+    }
+}
+
+static void
+parallel_ioport_eppdata_write_hw4(void *opaque, uint32_t addr, uint32_t val)
+{
+    ParallelState *s = opaque;
+    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;
+    }
+    pdebug("we%08x\n", val);
+    if (hw_mode(s, IEEE1284_MODE_EPP)) {
+	uint32_t eppdata = cpu_to_le32(val);
+	/* XXX Fixme: don't break abstractions, use more generic
+	   interface */
+	int n = write((int)s->chr->opaque, (uint8_t*)&eppdata, sizeof(eppdata));
+	if (n != sizeof(eppdata))
+	    s->epp_timeout = 1;
+    }
+}
+#endif
+
+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);
+    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;
+#ifdef __linux__
+    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 if (hw_mode(s, IEEE1284_MODE_EPP|IEEE1284_ADDR)) {
+	    /* XXX Fixme: don't break abstractions, use more generic
+	       interface. We can not poll, we have to read. The device
+	       waits us to read, it'll not send us anything so poll is
+	       no good. We have to read. Read had better be completed
+	       before we return. Need an API for that. */
+	    int n = read((int)s->chr->opaque, &ret, 1);
+	    if (n != 1) {
+		s->epp_timeout = 1;
+		pdebug("ra%02x t\n", ret);
+	    }
+	    else
+		pdebug("ra%02x\n", ret);
+	}
+	else
+	    pdebug("ra m\n");
+	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 if (hw_mode(s, IEEE1284_MODE_EPP)) {
+	    /* XXX Fixme: don't break abstractions, use more generic
+	       interface */
+	    int n = read((int)s->chr->opaque, &ret, 1);
+	    if (n != 1) {
+		s->epp_timeout = 1;
+		pdebug("re%02x t\n", ret);
+	    }
+	    else
+		pdebug("re%02x\n", ret);
+	}
+	else
+	    pdebug("re m\n");
+	break;
+    }
+#endif
+    s->last_read_offset = addr;
+    return ret;
+}
+
+#ifdef __linux__
+static uint32_t
+parallel_ioport_eppdata_read_hw2(void *opaque, uint32_t addr)
+{
+    ParallelState *s = opaque;
+    uint32_t ret = ~0U;
+    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", ret);
+	return ret;
+    }
+    if (hw_mode(s, IEEE1284_MODE_EPP)) {
+	uint16_t eppdata;
+	/* XXX Fixme: don't break abstractions, use more generic
+	   interface */
+	int n = read((int)s->chr->opaque, &eppdata, sizeof(eppdata));
+	if (n != sizeof(eppdata))
+	    s->epp_timeout = 1;
+	else
+	    ret = le16_to_cpu(eppdata);
+    }
+    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 = ~0U;
+    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", ret);
+	return ret;
+    }
+    if (hw_mode(s, IEEE1284_MODE_EPP)) {
+	uint32_t eppdata;
+	/* XXX Fixme: don't break abstractions, use more generic
+	   interface */
+	int n = read((int)s->chr->opaque, &eppdata, sizeof(eppdata));
+	if (n != sizeof(eppdata))
+	    s->epp_timeout = 1;
+	else
+	    ret = le32_to_cpu(eppdata);
+    }
+    pdebug("re%08x\n", ret);
+    return ret;
+}
 #endif
+
+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;
 }
 
@@ -169,15 +442,31 @@ ParallelState *parallel_init(int base, int irq, CharDriverState *chr)
         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->addr = ~0;
 
-    register_ioport_write(base, 8, 1, parallel_ioport_write, s);
-    register_ioport_read(base, 8, 1, parallel_ioport_read, s);
+    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);
+#ifdef __linux__
+	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);
+#endif
+    }
+    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/vl.c b/vl.c
index 574452d..bc3b4c1 100644
--- a/vl.c
+++ b/vl.c
@@ -1818,7 +1818,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;
@@ -1830,6 +1833,10 @@ static int pp_ioctl(CharDriverState *chr, int cmd, void *arg)
             return -ENOTSUP;
         *(uint8_t *)arg = b;
         break;
+    case CHR_IOCTL_PP_SETMODE:
+        if (ioctl(fd, PPSETMODE, arg) < 0)
+            return -ENOTSUP;
+        break;
     default:
         return -ENOTSUP;
     }
diff --git a/vl.h b/vl.h
index d492f3e..0bdc296 100644
--- a/vl.h
+++ b/vl.h
@@ -286,6 +286,7 @@ 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_SETMODE          8
 
 typedef void IOEventHandler(void *opaque, int event);
 

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [Qemu-devel] [PATCH] parport EPP support for Linux
  2007-02-04 20:36 Marko Kohtala
@ 2007-02-04 23:25 ` Fabrice Bellard
  0 siblings, 0 replies; 4+ messages in thread
From: Fabrice Bellard @ 2007-02-04 23:25 UTC (permalink / raw)
  To: qemu-devel

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.
> 
> Suggestions are welcome, but I also welcome you to make the
> enhancements yourself. This is good enough for me.
> 
> I hope it is good enough for inclusion in Qemu. It applies and was
> tested against current CVS.

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.

Regards,

Fabrice.

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [Qemu-devel] [PATCH] parport EPP support for Linux
@ 2007-02-05  6:06 Marko Kohtala
  2007-02-12 19:02 ` Marko Kohtala
  0 siblings, 1 reply; 4+ messages in thread
From: Marko Kohtala @ 2007-02-05  6:06 UTC (permalink / raw)
  To: fabrice; +Cc: qemu-devel

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 did not find appropriate abstraction. Linux parallel device needs to
do a read() on the device. A read() on the parallel port device
executes EPP read cycle on the parallel port to read from the device
attached to it. Poll() does not execute such cycles, but it only
signals read if the port makes an interrupt, and therefore the current
poll() based character device abstraction does not work.

So there are few read() and write() calls in the code.

Also the parallel port device IEEE mode settings use some constants
from linux/parport.h. These are needed to tell the device execute EPP
data or address cycles on reads and writes.

I am too new with qemu to say how to develop the qemu character device
abstraction for these requirements.

Marko

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [Qemu-devel] [PATCH] parport EPP support for Linux
  2007-02-05  6:06 [Qemu-devel] [PATCH] parport EPP support for Linux Marko Kohtala
@ 2007-02-12 19:02 ` Marko Kohtala
  0 siblings, 0 replies; 4+ messages in thread
From: Marko Kohtala @ 2007-02-12 19:02 UTC (permalink / raw)
  To: fabrice, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 958 bytes --]

On 2/5/07, Marko Kohtala <marko.kohtala@gmail.com> 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

[-- Attachment #2: parallel-epp.patch --]
[-- Type: text/x-patch, Size: 19798 bytes --]

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
-@var{N}. Currently only SPP parallel port features can be used.
+@var{N}. 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;

^ permalink raw reply related	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2007-02-12 19:02 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-02-05  6:06 [Qemu-devel] [PATCH] parport EPP support for Linux Marko Kohtala
2007-02-12 19:02 ` Marko Kohtala
  -- strict thread matches above, loose matches on Subject: below --
2007-02-04 20:36 Marko Kohtala
2007-02-04 23:25 ` Fabrice Bellard

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).