Darn.

I managed to send an older version of the patch.

Here is the latest. Sorry for the junk.

Marko

On 2/4/07, Marko Kohtala <[EMAIL PROTECTED]> 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.

Marko



diff --git a/hw/parallel.c b/hw/parallel.c
index cba9561..bd61e5e 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,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 +83,351 @@ 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)
+#ifdef __linux__
+#include <linux/parport.h>
+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;
+}
+#endif
+
+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);
+#ifdef __linux__
+	/* 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);
+#endif
+	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(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);
+    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 +446,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);
 
_______________________________________________
Qemu-devel mailing list
Qemu-devel@nongnu.org
http://lists.nongnu.org/mailman/listinfo/qemu-devel

Reply via email to