xref: /openbmc/qemu/hw/char/parallel.c (revision becdfa00)
149ab747fSPaolo Bonzini /*
249ab747fSPaolo Bonzini  * QEMU Parallel PORT emulation
349ab747fSPaolo Bonzini  *
449ab747fSPaolo Bonzini  * Copyright (c) 2003-2005 Fabrice Bellard
549ab747fSPaolo Bonzini  * Copyright (c) 2007 Marko Kohtala
649ab747fSPaolo Bonzini  *
749ab747fSPaolo Bonzini  * Permission is hereby granted, free of charge, to any person obtaining a copy
849ab747fSPaolo Bonzini  * of this software and associated documentation files (the "Software"), to deal
949ab747fSPaolo Bonzini  * in the Software without restriction, including without limitation the rights
1049ab747fSPaolo Bonzini  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1149ab747fSPaolo Bonzini  * copies of the Software, and to permit persons to whom the Software is
1249ab747fSPaolo Bonzini  * furnished to do so, subject to the following conditions:
1349ab747fSPaolo Bonzini  *
1449ab747fSPaolo Bonzini  * The above copyright notice and this permission notice shall be included in
1549ab747fSPaolo Bonzini  * all copies or substantial portions of the Software.
1649ab747fSPaolo Bonzini  *
1749ab747fSPaolo Bonzini  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1849ab747fSPaolo Bonzini  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1949ab747fSPaolo Bonzini  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
2049ab747fSPaolo Bonzini  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2149ab747fSPaolo Bonzini  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2249ab747fSPaolo Bonzini  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2349ab747fSPaolo Bonzini  * THE SOFTWARE.
2449ab747fSPaolo Bonzini  */
25b6a0aa05SPeter Maydell #include "qemu/osdep.h"
26da34e65cSMarkus Armbruster #include "qapi/error.h"
2749ab747fSPaolo Bonzini #include "hw/hw.h"
28dccfcd0eSPaolo Bonzini #include "sysemu/char.h"
2949ab747fSPaolo Bonzini #include "hw/isa/isa.h"
3049ab747fSPaolo Bonzini #include "hw/i386/pc.h"
3149ab747fSPaolo Bonzini #include "sysemu/sysemu.h"
3249ab747fSPaolo Bonzini 
3349ab747fSPaolo Bonzini //#define DEBUG_PARALLEL
3449ab747fSPaolo Bonzini 
3549ab747fSPaolo Bonzini #ifdef DEBUG_PARALLEL
3649ab747fSPaolo Bonzini #define pdebug(fmt, ...) printf("pp: " fmt, ## __VA_ARGS__)
3749ab747fSPaolo Bonzini #else
3849ab747fSPaolo Bonzini #define pdebug(fmt, ...) ((void)0)
3949ab747fSPaolo Bonzini #endif
4049ab747fSPaolo Bonzini 
4149ab747fSPaolo Bonzini #define PARA_REG_DATA 0
4249ab747fSPaolo Bonzini #define PARA_REG_STS 1
4349ab747fSPaolo Bonzini #define PARA_REG_CTR 2
4449ab747fSPaolo Bonzini #define PARA_REG_EPP_ADDR 3
4549ab747fSPaolo Bonzini #define PARA_REG_EPP_DATA 4
4649ab747fSPaolo Bonzini 
4749ab747fSPaolo Bonzini /*
4849ab747fSPaolo Bonzini  * These are the definitions for the Printer Status Register
4949ab747fSPaolo Bonzini  */
5049ab747fSPaolo Bonzini #define PARA_STS_BUSY	0x80	/* Busy complement */
5149ab747fSPaolo Bonzini #define PARA_STS_ACK	0x40	/* Acknowledge */
5249ab747fSPaolo Bonzini #define PARA_STS_PAPER	0x20	/* Out of paper */
5349ab747fSPaolo Bonzini #define PARA_STS_ONLINE	0x10	/* Online */
5449ab747fSPaolo Bonzini #define PARA_STS_ERROR	0x08	/* Error complement */
5549ab747fSPaolo Bonzini #define PARA_STS_TMOUT	0x01	/* EPP timeout */
5649ab747fSPaolo Bonzini 
5749ab747fSPaolo Bonzini /*
5849ab747fSPaolo Bonzini  * These are the definitions for the Printer Control Register
5949ab747fSPaolo Bonzini  */
6049ab747fSPaolo Bonzini #define PARA_CTR_DIR	0x20	/* Direction (1=read, 0=write) */
6149ab747fSPaolo Bonzini #define PARA_CTR_INTEN	0x10	/* IRQ Enable */
6249ab747fSPaolo Bonzini #define PARA_CTR_SELECT	0x08	/* Select In complement */
6349ab747fSPaolo Bonzini #define PARA_CTR_INIT	0x04	/* Initialize Printer complement */
6449ab747fSPaolo Bonzini #define PARA_CTR_AUTOLF	0x02	/* Auto linefeed complement */
6549ab747fSPaolo Bonzini #define PARA_CTR_STROBE	0x01	/* Strobe complement */
6649ab747fSPaolo Bonzini 
6749ab747fSPaolo Bonzini #define PARA_CTR_SIGNAL (PARA_CTR_SELECT|PARA_CTR_INIT|PARA_CTR_AUTOLF|PARA_CTR_STROBE)
6849ab747fSPaolo Bonzini 
6949ab747fSPaolo Bonzini typedef struct ParallelState {
7049ab747fSPaolo Bonzini     MemoryRegion iomem;
7149ab747fSPaolo Bonzini     uint8_t dataw;
7249ab747fSPaolo Bonzini     uint8_t datar;
7349ab747fSPaolo Bonzini     uint8_t status;
7449ab747fSPaolo Bonzini     uint8_t control;
7549ab747fSPaolo Bonzini     qemu_irq irq;
7649ab747fSPaolo Bonzini     int irq_pending;
77*becdfa00SMarc-André Lureau     CharBackend chr;
7849ab747fSPaolo Bonzini     int hw_driver;
7949ab747fSPaolo Bonzini     int epp_timeout;
8049ab747fSPaolo Bonzini     uint32_t last_read_offset; /* For debugging */
8149ab747fSPaolo Bonzini     /* Memory-mapped interface */
8249ab747fSPaolo Bonzini     int it_shift;
83e305a165SMarc-André Lureau     PortioList portio_list;
8449ab747fSPaolo Bonzini } ParallelState;
8549ab747fSPaolo Bonzini 
86b0dc5ee6SAndreas Färber #define TYPE_ISA_PARALLEL "isa-parallel"
87b0dc5ee6SAndreas Färber #define ISA_PARALLEL(obj) \
88b0dc5ee6SAndreas Färber     OBJECT_CHECK(ISAParallelState, (obj), TYPE_ISA_PARALLEL)
89b0dc5ee6SAndreas Färber 
9049ab747fSPaolo Bonzini typedef struct ISAParallelState {
91b0dc5ee6SAndreas Färber     ISADevice parent_obj;
92b0dc5ee6SAndreas Färber 
9349ab747fSPaolo Bonzini     uint32_t index;
9449ab747fSPaolo Bonzini     uint32_t iobase;
9549ab747fSPaolo Bonzini     uint32_t isairq;
9649ab747fSPaolo Bonzini     ParallelState state;
9749ab747fSPaolo Bonzini } ISAParallelState;
9849ab747fSPaolo Bonzini 
9949ab747fSPaolo Bonzini static void parallel_update_irq(ParallelState *s)
10049ab747fSPaolo Bonzini {
10149ab747fSPaolo Bonzini     if (s->irq_pending)
10249ab747fSPaolo Bonzini         qemu_irq_raise(s->irq);
10349ab747fSPaolo Bonzini     else
10449ab747fSPaolo Bonzini         qemu_irq_lower(s->irq);
10549ab747fSPaolo Bonzini }
10649ab747fSPaolo Bonzini 
10749ab747fSPaolo Bonzini static void
10849ab747fSPaolo Bonzini parallel_ioport_write_sw(void *opaque, uint32_t addr, uint32_t val)
10949ab747fSPaolo Bonzini {
11049ab747fSPaolo Bonzini     ParallelState *s = opaque;
11149ab747fSPaolo Bonzini 
11249ab747fSPaolo Bonzini     pdebug("write addr=0x%02x val=0x%02x\n", addr, val);
11349ab747fSPaolo Bonzini 
11449ab747fSPaolo Bonzini     addr &= 7;
11549ab747fSPaolo Bonzini     switch(addr) {
11649ab747fSPaolo Bonzini     case PARA_REG_DATA:
11749ab747fSPaolo Bonzini         s->dataw = val;
11849ab747fSPaolo Bonzini         parallel_update_irq(s);
11949ab747fSPaolo Bonzini         break;
12049ab747fSPaolo Bonzini     case PARA_REG_CTR:
12149ab747fSPaolo Bonzini         val |= 0xc0;
12249ab747fSPaolo Bonzini         if ((val & PARA_CTR_INIT) == 0 ) {
12349ab747fSPaolo Bonzini             s->status = PARA_STS_BUSY;
12449ab747fSPaolo Bonzini             s->status |= PARA_STS_ACK;
12549ab747fSPaolo Bonzini             s->status |= PARA_STS_ONLINE;
12649ab747fSPaolo Bonzini             s->status |= PARA_STS_ERROR;
12749ab747fSPaolo Bonzini         }
12849ab747fSPaolo Bonzini         else if (val & PARA_CTR_SELECT) {
12949ab747fSPaolo Bonzini             if (val & PARA_CTR_STROBE) {
13049ab747fSPaolo Bonzini                 s->status &= ~PARA_STS_BUSY;
13149ab747fSPaolo Bonzini                 if ((s->control & PARA_CTR_STROBE) == 0)
1326ab3fc32SDaniel P. Berrange                     /* XXX this blocks entire thread. Rewrite to use
1336ab3fc32SDaniel P. Berrange                      * qemu_chr_fe_write and background I/O callbacks */
134*becdfa00SMarc-André Lureau                     qemu_chr_fe_write_all(s->chr.chr, &s->dataw, 1);
13549ab747fSPaolo Bonzini             } else {
13649ab747fSPaolo Bonzini                 if (s->control & PARA_CTR_INTEN) {
13749ab747fSPaolo Bonzini                     s->irq_pending = 1;
13849ab747fSPaolo Bonzini                 }
13949ab747fSPaolo Bonzini             }
14049ab747fSPaolo Bonzini         }
14149ab747fSPaolo Bonzini         parallel_update_irq(s);
14249ab747fSPaolo Bonzini         s->control = val;
14349ab747fSPaolo Bonzini         break;
14449ab747fSPaolo Bonzini     }
14549ab747fSPaolo Bonzini }
14649ab747fSPaolo Bonzini 
14749ab747fSPaolo Bonzini static void parallel_ioport_write_hw(void *opaque, uint32_t addr, uint32_t val)
14849ab747fSPaolo Bonzini {
14949ab747fSPaolo Bonzini     ParallelState *s = opaque;
15049ab747fSPaolo Bonzini     uint8_t parm = val;
15149ab747fSPaolo Bonzini     int dir;
15249ab747fSPaolo Bonzini 
15349ab747fSPaolo Bonzini     /* Sometimes programs do several writes for timing purposes on old
15449ab747fSPaolo Bonzini        HW. Take care not to waste time on writes that do nothing. */
15549ab747fSPaolo Bonzini 
15649ab747fSPaolo Bonzini     s->last_read_offset = ~0U;
15749ab747fSPaolo Bonzini 
15849ab747fSPaolo Bonzini     addr &= 7;
15949ab747fSPaolo Bonzini     switch(addr) {
16049ab747fSPaolo Bonzini     case PARA_REG_DATA:
16149ab747fSPaolo Bonzini         if (s->dataw == val)
16249ab747fSPaolo Bonzini             return;
16349ab747fSPaolo Bonzini         pdebug("wd%02x\n", val);
164*becdfa00SMarc-André Lureau         qemu_chr_fe_ioctl(s->chr.chr, CHR_IOCTL_PP_WRITE_DATA, &parm);
16549ab747fSPaolo Bonzini         s->dataw = val;
16649ab747fSPaolo Bonzini         break;
16749ab747fSPaolo Bonzini     case PARA_REG_STS:
16849ab747fSPaolo Bonzini         pdebug("ws%02x\n", val);
16949ab747fSPaolo Bonzini         if (val & PARA_STS_TMOUT)
17049ab747fSPaolo Bonzini             s->epp_timeout = 0;
17149ab747fSPaolo Bonzini         break;
17249ab747fSPaolo Bonzini     case PARA_REG_CTR:
17349ab747fSPaolo Bonzini         val |= 0xc0;
17449ab747fSPaolo Bonzini         if (s->control == val)
17549ab747fSPaolo Bonzini             return;
17649ab747fSPaolo Bonzini         pdebug("wc%02x\n", val);
17749ab747fSPaolo Bonzini 
17849ab747fSPaolo Bonzini         if ((val & PARA_CTR_DIR) != (s->control & PARA_CTR_DIR)) {
17949ab747fSPaolo Bonzini             if (val & PARA_CTR_DIR) {
18049ab747fSPaolo Bonzini                 dir = 1;
18149ab747fSPaolo Bonzini             } else {
18249ab747fSPaolo Bonzini                 dir = 0;
18349ab747fSPaolo Bonzini             }
184*becdfa00SMarc-André Lureau             qemu_chr_fe_ioctl(s->chr.chr, CHR_IOCTL_PP_DATA_DIR, &dir);
18549ab747fSPaolo Bonzini             parm &= ~PARA_CTR_DIR;
18649ab747fSPaolo Bonzini         }
18749ab747fSPaolo Bonzini 
188*becdfa00SMarc-André Lureau         qemu_chr_fe_ioctl(s->chr.chr, CHR_IOCTL_PP_WRITE_CONTROL, &parm);
18949ab747fSPaolo Bonzini         s->control = val;
19049ab747fSPaolo Bonzini         break;
19149ab747fSPaolo Bonzini     case PARA_REG_EPP_ADDR:
19249ab747fSPaolo Bonzini         if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != PARA_CTR_INIT)
19349ab747fSPaolo Bonzini             /* Controls not correct for EPP address cycle, so do nothing */
19449ab747fSPaolo Bonzini             pdebug("wa%02x s\n", val);
19549ab747fSPaolo Bonzini         else {
19649ab747fSPaolo Bonzini             struct ParallelIOArg ioarg = { .buffer = &parm, .count = 1 };
197*becdfa00SMarc-André Lureau             if (qemu_chr_fe_ioctl(s->chr.chr,
198*becdfa00SMarc-André Lureau                                   CHR_IOCTL_PP_EPP_WRITE_ADDR, &ioarg)) {
19949ab747fSPaolo Bonzini                 s->epp_timeout = 1;
20049ab747fSPaolo Bonzini                 pdebug("wa%02x t\n", val);
20149ab747fSPaolo Bonzini             }
20249ab747fSPaolo Bonzini             else
20349ab747fSPaolo Bonzini                 pdebug("wa%02x\n", val);
20449ab747fSPaolo Bonzini         }
20549ab747fSPaolo Bonzini         break;
20649ab747fSPaolo Bonzini     case PARA_REG_EPP_DATA:
20749ab747fSPaolo Bonzini         if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != PARA_CTR_INIT)
20849ab747fSPaolo Bonzini             /* Controls not correct for EPP data cycle, so do nothing */
20949ab747fSPaolo Bonzini             pdebug("we%02x s\n", val);
21049ab747fSPaolo Bonzini         else {
21149ab747fSPaolo Bonzini             struct ParallelIOArg ioarg = { .buffer = &parm, .count = 1 };
212*becdfa00SMarc-André Lureau             if (qemu_chr_fe_ioctl(s->chr.chr, CHR_IOCTL_PP_EPP_WRITE, &ioarg)) {
21349ab747fSPaolo Bonzini                 s->epp_timeout = 1;
21449ab747fSPaolo Bonzini                 pdebug("we%02x t\n", val);
21549ab747fSPaolo Bonzini             }
21649ab747fSPaolo Bonzini             else
21749ab747fSPaolo Bonzini                 pdebug("we%02x\n", val);
21849ab747fSPaolo Bonzini         }
21949ab747fSPaolo Bonzini         break;
22049ab747fSPaolo Bonzini     }
22149ab747fSPaolo Bonzini }
22249ab747fSPaolo Bonzini 
22349ab747fSPaolo Bonzini static void
22449ab747fSPaolo Bonzini parallel_ioport_eppdata_write_hw2(void *opaque, uint32_t addr, uint32_t val)
22549ab747fSPaolo Bonzini {
22649ab747fSPaolo Bonzini     ParallelState *s = opaque;
22749ab747fSPaolo Bonzini     uint16_t eppdata = cpu_to_le16(val);
22849ab747fSPaolo Bonzini     int err;
22949ab747fSPaolo Bonzini     struct ParallelIOArg ioarg = {
23049ab747fSPaolo Bonzini         .buffer = &eppdata, .count = sizeof(eppdata)
23149ab747fSPaolo Bonzini     };
23249ab747fSPaolo Bonzini     if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != PARA_CTR_INIT) {
23349ab747fSPaolo Bonzini         /* Controls not correct for EPP data cycle, so do nothing */
23449ab747fSPaolo Bonzini         pdebug("we%04x s\n", val);
23549ab747fSPaolo Bonzini         return;
23649ab747fSPaolo Bonzini     }
237*becdfa00SMarc-André Lureau     err = qemu_chr_fe_ioctl(s->chr.chr, CHR_IOCTL_PP_EPP_WRITE, &ioarg);
23849ab747fSPaolo Bonzini     if (err) {
23949ab747fSPaolo Bonzini         s->epp_timeout = 1;
24049ab747fSPaolo Bonzini         pdebug("we%04x t\n", val);
24149ab747fSPaolo Bonzini     }
24249ab747fSPaolo Bonzini     else
24349ab747fSPaolo Bonzini         pdebug("we%04x\n", val);
24449ab747fSPaolo Bonzini }
24549ab747fSPaolo Bonzini 
24649ab747fSPaolo Bonzini static void
24749ab747fSPaolo Bonzini parallel_ioport_eppdata_write_hw4(void *opaque, uint32_t addr, uint32_t val)
24849ab747fSPaolo Bonzini {
24949ab747fSPaolo Bonzini     ParallelState *s = opaque;
25049ab747fSPaolo Bonzini     uint32_t eppdata = cpu_to_le32(val);
25149ab747fSPaolo Bonzini     int err;
25249ab747fSPaolo Bonzini     struct ParallelIOArg ioarg = {
25349ab747fSPaolo Bonzini         .buffer = &eppdata, .count = sizeof(eppdata)
25449ab747fSPaolo Bonzini     };
25549ab747fSPaolo Bonzini     if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != PARA_CTR_INIT) {
25649ab747fSPaolo Bonzini         /* Controls not correct for EPP data cycle, so do nothing */
25749ab747fSPaolo Bonzini         pdebug("we%08x s\n", val);
25849ab747fSPaolo Bonzini         return;
25949ab747fSPaolo Bonzini     }
260*becdfa00SMarc-André Lureau     err = qemu_chr_fe_ioctl(s->chr.chr, CHR_IOCTL_PP_EPP_WRITE, &ioarg);
26149ab747fSPaolo Bonzini     if (err) {
26249ab747fSPaolo Bonzini         s->epp_timeout = 1;
26349ab747fSPaolo Bonzini         pdebug("we%08x t\n", val);
26449ab747fSPaolo Bonzini     }
26549ab747fSPaolo Bonzini     else
26649ab747fSPaolo Bonzini         pdebug("we%08x\n", val);
26749ab747fSPaolo Bonzini }
26849ab747fSPaolo Bonzini 
26949ab747fSPaolo Bonzini static uint32_t parallel_ioport_read_sw(void *opaque, uint32_t addr)
27049ab747fSPaolo Bonzini {
27149ab747fSPaolo Bonzini     ParallelState *s = opaque;
27249ab747fSPaolo Bonzini     uint32_t ret = 0xff;
27349ab747fSPaolo Bonzini 
27449ab747fSPaolo Bonzini     addr &= 7;
27549ab747fSPaolo Bonzini     switch(addr) {
27649ab747fSPaolo Bonzini     case PARA_REG_DATA:
27749ab747fSPaolo Bonzini         if (s->control & PARA_CTR_DIR)
27849ab747fSPaolo Bonzini             ret = s->datar;
27949ab747fSPaolo Bonzini         else
28049ab747fSPaolo Bonzini             ret = s->dataw;
28149ab747fSPaolo Bonzini         break;
28249ab747fSPaolo Bonzini     case PARA_REG_STS:
28349ab747fSPaolo Bonzini         ret = s->status;
28449ab747fSPaolo Bonzini         s->irq_pending = 0;
28549ab747fSPaolo Bonzini         if ((s->status & PARA_STS_BUSY) == 0 && (s->control & PARA_CTR_STROBE) == 0) {
28649ab747fSPaolo Bonzini             /* XXX Fixme: wait 5 microseconds */
28749ab747fSPaolo Bonzini             if (s->status & PARA_STS_ACK)
28849ab747fSPaolo Bonzini                 s->status &= ~PARA_STS_ACK;
28949ab747fSPaolo Bonzini             else {
29049ab747fSPaolo Bonzini                 /* XXX Fixme: wait 5 microseconds */
29149ab747fSPaolo Bonzini                 s->status |= PARA_STS_ACK;
29249ab747fSPaolo Bonzini                 s->status |= PARA_STS_BUSY;
29349ab747fSPaolo Bonzini             }
29449ab747fSPaolo Bonzini         }
29549ab747fSPaolo Bonzini         parallel_update_irq(s);
29649ab747fSPaolo Bonzini         break;
29749ab747fSPaolo Bonzini     case PARA_REG_CTR:
29849ab747fSPaolo Bonzini         ret = s->control;
29949ab747fSPaolo Bonzini         break;
30049ab747fSPaolo Bonzini     }
30149ab747fSPaolo Bonzini     pdebug("read addr=0x%02x val=0x%02x\n", addr, ret);
30249ab747fSPaolo Bonzini     return ret;
30349ab747fSPaolo Bonzini }
30449ab747fSPaolo Bonzini 
30549ab747fSPaolo Bonzini static uint32_t parallel_ioport_read_hw(void *opaque, uint32_t addr)
30649ab747fSPaolo Bonzini {
30749ab747fSPaolo Bonzini     ParallelState *s = opaque;
30849ab747fSPaolo Bonzini     uint8_t ret = 0xff;
30949ab747fSPaolo Bonzini     addr &= 7;
31049ab747fSPaolo Bonzini     switch(addr) {
31149ab747fSPaolo Bonzini     case PARA_REG_DATA:
312*becdfa00SMarc-André Lureau         qemu_chr_fe_ioctl(s->chr.chr, CHR_IOCTL_PP_READ_DATA, &ret);
31349ab747fSPaolo Bonzini         if (s->last_read_offset != addr || s->datar != ret)
31449ab747fSPaolo Bonzini             pdebug("rd%02x\n", ret);
31549ab747fSPaolo Bonzini         s->datar = ret;
31649ab747fSPaolo Bonzini         break;
31749ab747fSPaolo Bonzini     case PARA_REG_STS:
318*becdfa00SMarc-André Lureau         qemu_chr_fe_ioctl(s->chr.chr, CHR_IOCTL_PP_READ_STATUS, &ret);
31949ab747fSPaolo Bonzini         ret &= ~PARA_STS_TMOUT;
32049ab747fSPaolo Bonzini         if (s->epp_timeout)
32149ab747fSPaolo Bonzini             ret |= PARA_STS_TMOUT;
32249ab747fSPaolo Bonzini         if (s->last_read_offset != addr || s->status != ret)
32349ab747fSPaolo Bonzini             pdebug("rs%02x\n", ret);
32449ab747fSPaolo Bonzini         s->status = ret;
32549ab747fSPaolo Bonzini         break;
32649ab747fSPaolo Bonzini     case PARA_REG_CTR:
32749ab747fSPaolo Bonzini         /* s->control has some bits fixed to 1. It is zero only when
32849ab747fSPaolo Bonzini            it has not been yet written to.  */
32949ab747fSPaolo Bonzini         if (s->control == 0) {
330*becdfa00SMarc-André Lureau             qemu_chr_fe_ioctl(s->chr.chr, CHR_IOCTL_PP_READ_CONTROL, &ret);
33149ab747fSPaolo Bonzini             if (s->last_read_offset != addr)
33249ab747fSPaolo Bonzini                 pdebug("rc%02x\n", ret);
33349ab747fSPaolo Bonzini             s->control = ret;
33449ab747fSPaolo Bonzini         }
33549ab747fSPaolo Bonzini         else {
33649ab747fSPaolo Bonzini             ret = s->control;
33749ab747fSPaolo Bonzini             if (s->last_read_offset != addr)
33849ab747fSPaolo Bonzini                 pdebug("rc%02x\n", ret);
33949ab747fSPaolo Bonzini         }
34049ab747fSPaolo Bonzini         break;
34149ab747fSPaolo Bonzini     case PARA_REG_EPP_ADDR:
342*becdfa00SMarc-André Lureau         if ((s->control & (PARA_CTR_DIR | PARA_CTR_SIGNAL)) !=
343*becdfa00SMarc-André Lureau             (PARA_CTR_DIR | PARA_CTR_INIT))
34449ab747fSPaolo Bonzini             /* Controls not correct for EPP addr cycle, so do nothing */
34549ab747fSPaolo Bonzini             pdebug("ra%02x s\n", ret);
34649ab747fSPaolo Bonzini         else {
34749ab747fSPaolo Bonzini             struct ParallelIOArg ioarg = { .buffer = &ret, .count = 1 };
348*becdfa00SMarc-André Lureau             if (qemu_chr_fe_ioctl(s->chr.chr,
349*becdfa00SMarc-André Lureau                                   CHR_IOCTL_PP_EPP_READ_ADDR, &ioarg)) {
35049ab747fSPaolo Bonzini                 s->epp_timeout = 1;
35149ab747fSPaolo Bonzini                 pdebug("ra%02x t\n", ret);
35249ab747fSPaolo Bonzini             }
35349ab747fSPaolo Bonzini             else
35449ab747fSPaolo Bonzini                 pdebug("ra%02x\n", ret);
35549ab747fSPaolo Bonzini         }
35649ab747fSPaolo Bonzini         break;
35749ab747fSPaolo Bonzini     case PARA_REG_EPP_DATA:
358*becdfa00SMarc-André Lureau         if ((s->control & (PARA_CTR_DIR | PARA_CTR_SIGNAL)) !=
359*becdfa00SMarc-André Lureau             (PARA_CTR_DIR | PARA_CTR_INIT))
36049ab747fSPaolo Bonzini             /* Controls not correct for EPP data cycle, so do nothing */
36149ab747fSPaolo Bonzini             pdebug("re%02x s\n", ret);
36249ab747fSPaolo Bonzini         else {
36349ab747fSPaolo Bonzini             struct ParallelIOArg ioarg = { .buffer = &ret, .count = 1 };
364*becdfa00SMarc-André Lureau             if (qemu_chr_fe_ioctl(s->chr.chr, CHR_IOCTL_PP_EPP_READ, &ioarg)) {
36549ab747fSPaolo Bonzini                 s->epp_timeout = 1;
36649ab747fSPaolo Bonzini                 pdebug("re%02x t\n", ret);
36749ab747fSPaolo Bonzini             }
36849ab747fSPaolo Bonzini             else
36949ab747fSPaolo Bonzini                 pdebug("re%02x\n", ret);
37049ab747fSPaolo Bonzini         }
37149ab747fSPaolo Bonzini         break;
37249ab747fSPaolo Bonzini     }
37349ab747fSPaolo Bonzini     s->last_read_offset = addr;
37449ab747fSPaolo Bonzini     return ret;
37549ab747fSPaolo Bonzini }
37649ab747fSPaolo Bonzini 
37749ab747fSPaolo Bonzini static uint32_t
37849ab747fSPaolo Bonzini parallel_ioport_eppdata_read_hw2(void *opaque, uint32_t addr)
37949ab747fSPaolo Bonzini {
38049ab747fSPaolo Bonzini     ParallelState *s = opaque;
38149ab747fSPaolo Bonzini     uint32_t ret;
38249ab747fSPaolo Bonzini     uint16_t eppdata = ~0;
38349ab747fSPaolo Bonzini     int err;
38449ab747fSPaolo Bonzini     struct ParallelIOArg ioarg = {
38549ab747fSPaolo Bonzini         .buffer = &eppdata, .count = sizeof(eppdata)
38649ab747fSPaolo Bonzini     };
38749ab747fSPaolo Bonzini     if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != (PARA_CTR_DIR|PARA_CTR_INIT)) {
38849ab747fSPaolo Bonzini         /* Controls not correct for EPP data cycle, so do nothing */
38949ab747fSPaolo Bonzini         pdebug("re%04x s\n", eppdata);
39049ab747fSPaolo Bonzini         return eppdata;
39149ab747fSPaolo Bonzini     }
392*becdfa00SMarc-André Lureau     err = qemu_chr_fe_ioctl(s->chr.chr, CHR_IOCTL_PP_EPP_READ, &ioarg);
39349ab747fSPaolo Bonzini     ret = le16_to_cpu(eppdata);
39449ab747fSPaolo Bonzini 
39549ab747fSPaolo Bonzini     if (err) {
39649ab747fSPaolo Bonzini         s->epp_timeout = 1;
39749ab747fSPaolo Bonzini         pdebug("re%04x t\n", ret);
39849ab747fSPaolo Bonzini     }
39949ab747fSPaolo Bonzini     else
40049ab747fSPaolo Bonzini         pdebug("re%04x\n", ret);
40149ab747fSPaolo Bonzini     return ret;
40249ab747fSPaolo Bonzini }
40349ab747fSPaolo Bonzini 
40449ab747fSPaolo Bonzini static uint32_t
40549ab747fSPaolo Bonzini parallel_ioport_eppdata_read_hw4(void *opaque, uint32_t addr)
40649ab747fSPaolo Bonzini {
40749ab747fSPaolo Bonzini     ParallelState *s = opaque;
40849ab747fSPaolo Bonzini     uint32_t ret;
40949ab747fSPaolo Bonzini     uint32_t eppdata = ~0U;
41049ab747fSPaolo Bonzini     int err;
41149ab747fSPaolo Bonzini     struct ParallelIOArg ioarg = {
41249ab747fSPaolo Bonzini         .buffer = &eppdata, .count = sizeof(eppdata)
41349ab747fSPaolo Bonzini     };
41449ab747fSPaolo Bonzini     if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != (PARA_CTR_DIR|PARA_CTR_INIT)) {
41549ab747fSPaolo Bonzini         /* Controls not correct for EPP data cycle, so do nothing */
41649ab747fSPaolo Bonzini         pdebug("re%08x s\n", eppdata);
41749ab747fSPaolo Bonzini         return eppdata;
41849ab747fSPaolo Bonzini     }
419*becdfa00SMarc-André Lureau     err = qemu_chr_fe_ioctl(s->chr.chr, CHR_IOCTL_PP_EPP_READ, &ioarg);
42049ab747fSPaolo Bonzini     ret = le32_to_cpu(eppdata);
42149ab747fSPaolo Bonzini 
42249ab747fSPaolo Bonzini     if (err) {
42349ab747fSPaolo Bonzini         s->epp_timeout = 1;
42449ab747fSPaolo Bonzini         pdebug("re%08x t\n", ret);
42549ab747fSPaolo Bonzini     }
42649ab747fSPaolo Bonzini     else
42749ab747fSPaolo Bonzini         pdebug("re%08x\n", ret);
42849ab747fSPaolo Bonzini     return ret;
42949ab747fSPaolo Bonzini }
43049ab747fSPaolo Bonzini 
43149ab747fSPaolo Bonzini static void parallel_ioport_ecp_write(void *opaque, uint32_t addr, uint32_t val)
43249ab747fSPaolo Bonzini {
43349ab747fSPaolo Bonzini     pdebug("wecp%d=%02x\n", addr & 7, val);
43449ab747fSPaolo Bonzini }
43549ab747fSPaolo Bonzini 
43649ab747fSPaolo Bonzini static uint32_t parallel_ioport_ecp_read(void *opaque, uint32_t addr)
43749ab747fSPaolo Bonzini {
43849ab747fSPaolo Bonzini     uint8_t ret = 0xff;
43949ab747fSPaolo Bonzini 
44049ab747fSPaolo Bonzini     pdebug("recp%d:%02x\n", addr & 7, ret);
44149ab747fSPaolo Bonzini     return ret;
44249ab747fSPaolo Bonzini }
44349ab747fSPaolo Bonzini 
44449ab747fSPaolo Bonzini static void parallel_reset(void *opaque)
44549ab747fSPaolo Bonzini {
44649ab747fSPaolo Bonzini     ParallelState *s = opaque;
44749ab747fSPaolo Bonzini 
44849ab747fSPaolo Bonzini     s->datar = ~0;
44949ab747fSPaolo Bonzini     s->dataw = ~0;
45049ab747fSPaolo Bonzini     s->status = PARA_STS_BUSY;
45149ab747fSPaolo Bonzini     s->status |= PARA_STS_ACK;
45249ab747fSPaolo Bonzini     s->status |= PARA_STS_ONLINE;
45349ab747fSPaolo Bonzini     s->status |= PARA_STS_ERROR;
45449ab747fSPaolo Bonzini     s->status |= PARA_STS_TMOUT;
45549ab747fSPaolo Bonzini     s->control = PARA_CTR_SELECT;
45649ab747fSPaolo Bonzini     s->control |= PARA_CTR_INIT;
45749ab747fSPaolo Bonzini     s->control |= 0xc0;
45849ab747fSPaolo Bonzini     s->irq_pending = 0;
45949ab747fSPaolo Bonzini     s->hw_driver = 0;
46049ab747fSPaolo Bonzini     s->epp_timeout = 0;
46149ab747fSPaolo Bonzini     s->last_read_offset = ~0U;
46249ab747fSPaolo Bonzini }
46349ab747fSPaolo Bonzini 
46449ab747fSPaolo Bonzini static const int isa_parallel_io[MAX_PARALLEL_PORTS] = { 0x378, 0x278, 0x3bc };
46549ab747fSPaolo Bonzini 
46649ab747fSPaolo Bonzini static const MemoryRegionPortio isa_parallel_portio_hw_list[] = {
46749ab747fSPaolo Bonzini     { 0, 8, 1,
46849ab747fSPaolo Bonzini       .read = parallel_ioport_read_hw,
46949ab747fSPaolo Bonzini       .write = parallel_ioport_write_hw },
47049ab747fSPaolo Bonzini     { 4, 1, 2,
47149ab747fSPaolo Bonzini       .read = parallel_ioport_eppdata_read_hw2,
47249ab747fSPaolo Bonzini       .write = parallel_ioport_eppdata_write_hw2 },
47349ab747fSPaolo Bonzini     { 4, 1, 4,
47449ab747fSPaolo Bonzini       .read = parallel_ioport_eppdata_read_hw4,
47549ab747fSPaolo Bonzini       .write = parallel_ioport_eppdata_write_hw4 },
47649ab747fSPaolo Bonzini     { 0x400, 8, 1,
47749ab747fSPaolo Bonzini       .read = parallel_ioport_ecp_read,
47849ab747fSPaolo Bonzini       .write = parallel_ioport_ecp_write },
47949ab747fSPaolo Bonzini     PORTIO_END_OF_LIST(),
48049ab747fSPaolo Bonzini };
48149ab747fSPaolo Bonzini 
48249ab747fSPaolo Bonzini static const MemoryRegionPortio isa_parallel_portio_sw_list[] = {
48349ab747fSPaolo Bonzini     { 0, 8, 1,
48449ab747fSPaolo Bonzini       .read = parallel_ioport_read_sw,
48549ab747fSPaolo Bonzini       .write = parallel_ioport_write_sw },
48649ab747fSPaolo Bonzini     PORTIO_END_OF_LIST(),
48749ab747fSPaolo Bonzini };
48849ab747fSPaolo Bonzini 
489461a2753SPavel Dovgalyuk 
490461a2753SPavel Dovgalyuk static const VMStateDescription vmstate_parallel_isa = {
491461a2753SPavel Dovgalyuk     .name = "parallel_isa",
492461a2753SPavel Dovgalyuk     .version_id = 1,
493461a2753SPavel Dovgalyuk     .minimum_version_id = 1,
494461a2753SPavel Dovgalyuk     .fields      = (VMStateField[]) {
495461a2753SPavel Dovgalyuk         VMSTATE_UINT8(state.dataw, ISAParallelState),
496461a2753SPavel Dovgalyuk         VMSTATE_UINT8(state.datar, ISAParallelState),
497461a2753SPavel Dovgalyuk         VMSTATE_UINT8(state.status, ISAParallelState),
498461a2753SPavel Dovgalyuk         VMSTATE_UINT8(state.control, ISAParallelState),
499461a2753SPavel Dovgalyuk         VMSTATE_INT32(state.irq_pending, ISAParallelState),
500461a2753SPavel Dovgalyuk         VMSTATE_INT32(state.epp_timeout, ISAParallelState),
501461a2753SPavel Dovgalyuk         VMSTATE_END_OF_LIST()
502461a2753SPavel Dovgalyuk     }
503461a2753SPavel Dovgalyuk };
504461a2753SPavel Dovgalyuk 
505461a2753SPavel Dovgalyuk 
506db895a1eSAndreas Färber static void parallel_isa_realizefn(DeviceState *dev, Error **errp)
50749ab747fSPaolo Bonzini {
50849ab747fSPaolo Bonzini     static int index;
509db895a1eSAndreas Färber     ISADevice *isadev = ISA_DEVICE(dev);
510b0dc5ee6SAndreas Färber     ISAParallelState *isa = ISA_PARALLEL(dev);
51149ab747fSPaolo Bonzini     ParallelState *s = &isa->state;
51249ab747fSPaolo Bonzini     int base;
51349ab747fSPaolo Bonzini     uint8_t dummy;
51449ab747fSPaolo Bonzini 
515*becdfa00SMarc-André Lureau     if (!s->chr.chr) {
516db895a1eSAndreas Färber         error_setg(errp, "Can't create parallel device, empty char device");
517db895a1eSAndreas Färber         return;
51849ab747fSPaolo Bonzini     }
51949ab747fSPaolo Bonzini 
520db895a1eSAndreas Färber     if (isa->index == -1) {
52149ab747fSPaolo Bonzini         isa->index = index;
522db895a1eSAndreas Färber     }
523db895a1eSAndreas Färber     if (isa->index >= MAX_PARALLEL_PORTS) {
524db895a1eSAndreas Färber         error_setg(errp, "Max. supported number of parallel ports is %d.",
525db895a1eSAndreas Färber                    MAX_PARALLEL_PORTS);
526db895a1eSAndreas Färber         return;
527db895a1eSAndreas Färber     }
528db895a1eSAndreas Färber     if (isa->iobase == -1) {
52949ab747fSPaolo Bonzini         isa->iobase = isa_parallel_io[isa->index];
530db895a1eSAndreas Färber     }
53149ab747fSPaolo Bonzini     index++;
53249ab747fSPaolo Bonzini 
53349ab747fSPaolo Bonzini     base = isa->iobase;
534db895a1eSAndreas Färber     isa_init_irq(isadev, &s->irq, isa->isairq);
53549ab747fSPaolo Bonzini     qemu_register_reset(parallel_reset, s);
53649ab747fSPaolo Bonzini 
537*becdfa00SMarc-André Lureau     if (qemu_chr_fe_ioctl(s->chr.chr, CHR_IOCTL_PP_READ_STATUS, &dummy) == 0) {
53849ab747fSPaolo Bonzini         s->hw_driver = 1;
53949ab747fSPaolo Bonzini         s->status = dummy;
54049ab747fSPaolo Bonzini     }
54149ab747fSPaolo Bonzini 
542e305a165SMarc-André Lureau     isa_register_portio_list(isadev, &s->portio_list, base,
54349ab747fSPaolo Bonzini                              (s->hw_driver
54449ab747fSPaolo Bonzini                               ? &isa_parallel_portio_hw_list[0]
54549ab747fSPaolo Bonzini                               : &isa_parallel_portio_sw_list[0]),
54649ab747fSPaolo Bonzini                              s, "parallel");
54749ab747fSPaolo Bonzini }
54849ab747fSPaolo Bonzini 
54949ab747fSPaolo Bonzini /* Memory mapped interface */
55049ab747fSPaolo Bonzini static uint32_t parallel_mm_readb (void *opaque, hwaddr addr)
55149ab747fSPaolo Bonzini {
55249ab747fSPaolo Bonzini     ParallelState *s = opaque;
55349ab747fSPaolo Bonzini 
55449ab747fSPaolo Bonzini     return parallel_ioport_read_sw(s, addr >> s->it_shift) & 0xFF;
55549ab747fSPaolo Bonzini }
55649ab747fSPaolo Bonzini 
55749ab747fSPaolo Bonzini static void parallel_mm_writeb (void *opaque,
55849ab747fSPaolo Bonzini                                 hwaddr addr, uint32_t value)
55949ab747fSPaolo Bonzini {
56049ab747fSPaolo Bonzini     ParallelState *s = opaque;
56149ab747fSPaolo Bonzini 
56249ab747fSPaolo Bonzini     parallel_ioport_write_sw(s, addr >> s->it_shift, value & 0xFF);
56349ab747fSPaolo Bonzini }
56449ab747fSPaolo Bonzini 
56549ab747fSPaolo Bonzini static uint32_t parallel_mm_readw (void *opaque, hwaddr addr)
56649ab747fSPaolo Bonzini {
56749ab747fSPaolo Bonzini     ParallelState *s = opaque;
56849ab747fSPaolo Bonzini 
56949ab747fSPaolo Bonzini     return parallel_ioport_read_sw(s, addr >> s->it_shift) & 0xFFFF;
57049ab747fSPaolo Bonzini }
57149ab747fSPaolo Bonzini 
57249ab747fSPaolo Bonzini static void parallel_mm_writew (void *opaque,
57349ab747fSPaolo Bonzini                                 hwaddr addr, uint32_t value)
57449ab747fSPaolo Bonzini {
57549ab747fSPaolo Bonzini     ParallelState *s = opaque;
57649ab747fSPaolo Bonzini 
57749ab747fSPaolo Bonzini     parallel_ioport_write_sw(s, addr >> s->it_shift, value & 0xFFFF);
57849ab747fSPaolo Bonzini }
57949ab747fSPaolo Bonzini 
58049ab747fSPaolo Bonzini static uint32_t parallel_mm_readl (void *opaque, hwaddr addr)
58149ab747fSPaolo Bonzini {
58249ab747fSPaolo Bonzini     ParallelState *s = opaque;
58349ab747fSPaolo Bonzini 
58449ab747fSPaolo Bonzini     return parallel_ioport_read_sw(s, addr >> s->it_shift);
58549ab747fSPaolo Bonzini }
58649ab747fSPaolo Bonzini 
58749ab747fSPaolo Bonzini static void parallel_mm_writel (void *opaque,
58849ab747fSPaolo Bonzini                                 hwaddr addr, uint32_t value)
58949ab747fSPaolo Bonzini {
59049ab747fSPaolo Bonzini     ParallelState *s = opaque;
59149ab747fSPaolo Bonzini 
59249ab747fSPaolo Bonzini     parallel_ioport_write_sw(s, addr >> s->it_shift, value);
59349ab747fSPaolo Bonzini }
59449ab747fSPaolo Bonzini 
59549ab747fSPaolo Bonzini static const MemoryRegionOps parallel_mm_ops = {
59649ab747fSPaolo Bonzini     .old_mmio = {
59749ab747fSPaolo Bonzini         .read = { parallel_mm_readb, parallel_mm_readw, parallel_mm_readl },
59849ab747fSPaolo Bonzini         .write = { parallel_mm_writeb, parallel_mm_writew, parallel_mm_writel },
59949ab747fSPaolo Bonzini     },
60049ab747fSPaolo Bonzini     .endianness = DEVICE_NATIVE_ENDIAN,
60149ab747fSPaolo Bonzini };
60249ab747fSPaolo Bonzini 
60349ab747fSPaolo Bonzini /* If fd is zero, it means that the parallel device uses the console */
60449ab747fSPaolo Bonzini bool parallel_mm_init(MemoryRegion *address_space,
60549ab747fSPaolo Bonzini                       hwaddr base, int it_shift, qemu_irq irq,
60649ab747fSPaolo Bonzini                       CharDriverState *chr)
60749ab747fSPaolo Bonzini {
60849ab747fSPaolo Bonzini     ParallelState *s;
60949ab747fSPaolo Bonzini 
61049ab747fSPaolo Bonzini     s = g_malloc0(sizeof(ParallelState));
61149ab747fSPaolo Bonzini     s->irq = irq;
612*becdfa00SMarc-André Lureau     qemu_chr_fe_init(&s->chr, chr, &error_abort);
61349ab747fSPaolo Bonzini     s->it_shift = it_shift;
61449ab747fSPaolo Bonzini     qemu_register_reset(parallel_reset, s);
61549ab747fSPaolo Bonzini 
6162c9b15caSPaolo Bonzini     memory_region_init_io(&s->iomem, NULL, &parallel_mm_ops, s,
61749ab747fSPaolo Bonzini                           "parallel", 8 << it_shift);
61849ab747fSPaolo Bonzini     memory_region_add_subregion(address_space, base, &s->iomem);
61949ab747fSPaolo Bonzini     return true;
62049ab747fSPaolo Bonzini }
62149ab747fSPaolo Bonzini 
62249ab747fSPaolo Bonzini static Property parallel_isa_properties[] = {
62349ab747fSPaolo Bonzini     DEFINE_PROP_UINT32("index", ISAParallelState, index,   -1),
624c7bcc85dSPaolo Bonzini     DEFINE_PROP_UINT32("iobase", ISAParallelState, iobase,  -1),
62549ab747fSPaolo Bonzini     DEFINE_PROP_UINT32("irq",   ISAParallelState, isairq,  7),
62649ab747fSPaolo Bonzini     DEFINE_PROP_CHR("chardev",  ISAParallelState, state.chr),
62749ab747fSPaolo Bonzini     DEFINE_PROP_END_OF_LIST(),
62849ab747fSPaolo Bonzini };
62949ab747fSPaolo Bonzini 
63049ab747fSPaolo Bonzini static void parallel_isa_class_initfn(ObjectClass *klass, void *data)
63149ab747fSPaolo Bonzini {
63249ab747fSPaolo Bonzini     DeviceClass *dc = DEVICE_CLASS(klass);
633db895a1eSAndreas Färber 
634db895a1eSAndreas Färber     dc->realize = parallel_isa_realizefn;
635461a2753SPavel Dovgalyuk     dc->vmsd = &vmstate_parallel_isa;
63649ab747fSPaolo Bonzini     dc->props = parallel_isa_properties;
637125ee0edSMarcel Apfelbaum     set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
63849ab747fSPaolo Bonzini }
63949ab747fSPaolo Bonzini 
64049ab747fSPaolo Bonzini static const TypeInfo parallel_isa_info = {
641b0dc5ee6SAndreas Färber     .name          = TYPE_ISA_PARALLEL,
64249ab747fSPaolo Bonzini     .parent        = TYPE_ISA_DEVICE,
64349ab747fSPaolo Bonzini     .instance_size = sizeof(ISAParallelState),
64449ab747fSPaolo Bonzini     .class_init    = parallel_isa_class_initfn,
64549ab747fSPaolo Bonzini };
64649ab747fSPaolo Bonzini 
64749ab747fSPaolo Bonzini static void parallel_register_types(void)
64849ab747fSPaolo Bonzini {
64949ab747fSPaolo Bonzini     type_register_static(&parallel_isa_info);
65049ab747fSPaolo Bonzini }
65149ab747fSPaolo Bonzini 
65249ab747fSPaolo Bonzini type_init(parallel_register_types)
653