Commit 5867c88a8256c6501c382ba421c91464dd871e3b
1 parent
d5d10bc3
Parport EPP support for Linux, by Marko Kohtala.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2430 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
4 changed files
with
436 additions
and
84 deletions
hw/parallel.c
| @@ -2,6 +2,7 @@ | @@ -2,6 +2,7 @@ | ||
| 2 | * QEMU Parallel PORT emulation | 2 | * QEMU Parallel PORT emulation |
| 3 | * | 3 | * |
| 4 | * Copyright (c) 2003-2005 Fabrice Bellard | 4 | * Copyright (c) 2003-2005 Fabrice Bellard |
| 5 | + * Copyright (c) 2007 Marko Kohtala | ||
| 5 | * | 6 | * |
| 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy | 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
| 7 | * of this software and associated documentation files (the "Software"), to deal | 8 | * of this software and associated documentation files (the "Software"), to deal |
| @@ -25,6 +26,18 @@ | @@ -25,6 +26,18 @@ | ||
| 25 | 26 | ||
| 26 | //#define DEBUG_PARALLEL | 27 | //#define DEBUG_PARALLEL |
| 27 | 28 | ||
| 29 | +#ifdef DEBUG_PARALLEL | ||
| 30 | +#define pdebug(fmt, arg...) printf("pp: " fmt, ##arg) | ||
| 31 | +#else | ||
| 32 | +#define pdebug(fmt, arg...) ((void)0) | ||
| 33 | +#endif | ||
| 34 | + | ||
| 35 | +#define PARA_REG_DATA 0 | ||
| 36 | +#define PARA_REG_STS 1 | ||
| 37 | +#define PARA_REG_CTR 2 | ||
| 38 | +#define PARA_REG_EPP_ADDR 3 | ||
| 39 | +#define PARA_REG_EPP_DATA 4 | ||
| 40 | + | ||
| 28 | /* | 41 | /* |
| 29 | * These are the definitions for the Printer Status Register | 42 | * These are the definitions for the Printer Status Register |
| 30 | */ | 43 | */ |
| @@ -33,24 +46,31 @@ | @@ -33,24 +46,31 @@ | ||
| 33 | #define PARA_STS_PAPER 0x20 /* Out of paper */ | 46 | #define PARA_STS_PAPER 0x20 /* Out of paper */ |
| 34 | #define PARA_STS_ONLINE 0x10 /* Online */ | 47 | #define PARA_STS_ONLINE 0x10 /* Online */ |
| 35 | #define PARA_STS_ERROR 0x08 /* Error complement */ | 48 | #define PARA_STS_ERROR 0x08 /* Error complement */ |
| 49 | +#define PARA_STS_TMOUT 0x01 /* EPP timeout */ | ||
| 36 | 50 | ||
| 37 | /* | 51 | /* |
| 38 | * These are the definitions for the Printer Control Register | 52 | * These are the definitions for the Printer Control Register |
| 39 | */ | 53 | */ |
| 54 | +#define PARA_CTR_DIR 0x20 /* Direction (1=read, 0=write) */ | ||
| 40 | #define PARA_CTR_INTEN 0x10 /* IRQ Enable */ | 55 | #define PARA_CTR_INTEN 0x10 /* IRQ Enable */ |
| 41 | #define PARA_CTR_SELECT 0x08 /* Select In complement */ | 56 | #define PARA_CTR_SELECT 0x08 /* Select In complement */ |
| 42 | #define PARA_CTR_INIT 0x04 /* Initialize Printer complement */ | 57 | #define PARA_CTR_INIT 0x04 /* Initialize Printer complement */ |
| 43 | #define PARA_CTR_AUTOLF 0x02 /* Auto linefeed complement */ | 58 | #define PARA_CTR_AUTOLF 0x02 /* Auto linefeed complement */ |
| 44 | #define PARA_CTR_STROBE 0x01 /* Strobe complement */ | 59 | #define PARA_CTR_STROBE 0x01 /* Strobe complement */ |
| 45 | 60 | ||
| 61 | +#define PARA_CTR_SIGNAL (PARA_CTR_SELECT|PARA_CTR_INIT|PARA_CTR_AUTOLF|PARA_CTR_STROBE) | ||
| 62 | + | ||
| 46 | struct ParallelState { | 63 | struct ParallelState { |
| 47 | - uint8_t data; | ||
| 48 | - uint8_t status; /* read only register */ | 64 | + uint8_t dataw; |
| 65 | + uint8_t datar; | ||
| 66 | + uint8_t status; | ||
| 49 | uint8_t control; | 67 | uint8_t control; |
| 50 | int irq; | 68 | int irq; |
| 51 | int irq_pending; | 69 | int irq_pending; |
| 52 | CharDriverState *chr; | 70 | CharDriverState *chr; |
| 53 | int hw_driver; | 71 | int hw_driver; |
| 72 | + int epp_timeout; | ||
| 73 | + uint32_t last_read_offset; /* For debugging */ | ||
| 54 | }; | 74 | }; |
| 55 | 75 | ||
| 56 | static void parallel_update_irq(ParallelState *s) | 76 | static void parallel_update_irq(ParallelState *s) |
| @@ -61,96 +81,322 @@ static void parallel_update_irq(ParallelState *s) | @@ -61,96 +81,322 @@ static void parallel_update_irq(ParallelState *s) | ||
| 61 | pic_set_irq(s->irq, 0); | 81 | pic_set_irq(s->irq, 0); |
| 62 | } | 82 | } |
| 63 | 83 | ||
| 64 | -static void parallel_ioport_write(void *opaque, uint32_t addr, uint32_t val) | 84 | +static void |
| 85 | +parallel_ioport_write_sw(void *opaque, uint32_t addr, uint32_t val) | ||
| 65 | { | 86 | { |
| 66 | ParallelState *s = opaque; | 87 | ParallelState *s = opaque; |
| 67 | 88 | ||
| 89 | + pdebug("write addr=0x%02x val=0x%02x\n", addr, val); | ||
| 90 | + | ||
| 91 | + addr &= 7; | ||
| 92 | + switch(addr) { | ||
| 93 | + case PARA_REG_DATA: | ||
| 94 | + s->dataw = val; | ||
| 95 | + parallel_update_irq(s); | ||
| 96 | + break; | ||
| 97 | + case PARA_REG_CTR: | ||
| 98 | + if ((val & PARA_CTR_INIT) == 0 ) { | ||
| 99 | + s->status = PARA_STS_BUSY; | ||
| 100 | + s->status |= PARA_STS_ACK; | ||
| 101 | + s->status |= PARA_STS_ONLINE; | ||
| 102 | + s->status |= PARA_STS_ERROR; | ||
| 103 | + } | ||
| 104 | + else if (val & PARA_CTR_SELECT) { | ||
| 105 | + if (val & PARA_CTR_STROBE) { | ||
| 106 | + s->status &= ~PARA_STS_BUSY; | ||
| 107 | + if ((s->control & PARA_CTR_STROBE) == 0) | ||
| 108 | + qemu_chr_write(s->chr, &s->dataw, 1); | ||
| 109 | + } else { | ||
| 110 | + if (s->control & PARA_CTR_INTEN) { | ||
| 111 | + s->irq_pending = 1; | ||
| 112 | + } | ||
| 113 | + } | ||
| 114 | + } | ||
| 115 | + parallel_update_irq(s); | ||
| 116 | + s->control = val; | ||
| 117 | + break; | ||
| 118 | + } | ||
| 119 | +} | ||
| 120 | + | ||
| 121 | +static void parallel_ioport_write_hw(void *opaque, uint32_t addr, uint32_t val) | ||
| 122 | +{ | ||
| 123 | + ParallelState *s = opaque; | ||
| 124 | + uint8_t parm = val; | ||
| 125 | + | ||
| 126 | + /* Sometimes programs do several writes for timing purposes on old | ||
| 127 | + HW. Take care not to waste time on writes that do nothing. */ | ||
| 128 | + | ||
| 129 | + s->last_read_offset = ~0U; | ||
| 130 | + | ||
| 68 | addr &= 7; | 131 | addr &= 7; |
| 69 | -#ifdef DEBUG_PARALLEL | ||
| 70 | - printf("parallel: write addr=0x%02x val=0x%02x\n", addr, val); | ||
| 71 | -#endif | ||
| 72 | switch(addr) { | 132 | switch(addr) { |
| 73 | - case 0: | ||
| 74 | - if (s->hw_driver) { | ||
| 75 | - s->data = val; | ||
| 76 | - qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_WRITE_DATA, &s->data); | ||
| 77 | - } else { | ||
| 78 | - s->data = val; | ||
| 79 | - parallel_update_irq(s); | ||
| 80 | - } | 133 | + case PARA_REG_DATA: |
| 134 | + if (s->dataw == val) | ||
| 135 | + return; | ||
| 136 | + pdebug("wd%02x\n", val); | ||
| 137 | + qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_WRITE_DATA, &parm); | ||
| 138 | + s->dataw = val; | ||
| 81 | break; | 139 | break; |
| 82 | - case 2: | ||
| 83 | - if (s->hw_driver) { | ||
| 84 | - s->control = val; | ||
| 85 | - qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_WRITE_CONTROL, &s->control); | ||
| 86 | - } else { | ||
| 87 | - if ((val & PARA_CTR_INIT) == 0 ) { | ||
| 88 | - s->status = PARA_STS_BUSY; | ||
| 89 | - s->status |= PARA_STS_ACK; | ||
| 90 | - s->status |= PARA_STS_ONLINE; | ||
| 91 | - s->status |= PARA_STS_ERROR; | ||
| 92 | - } | ||
| 93 | - else if (val & PARA_CTR_SELECT) { | ||
| 94 | - if (val & PARA_CTR_STROBE) { | ||
| 95 | - s->status &= ~PARA_STS_BUSY; | ||
| 96 | - if ((s->control & PARA_CTR_STROBE) == 0) | ||
| 97 | - qemu_chr_write(s->chr, &s->data, 1); | ||
| 98 | - } else { | ||
| 99 | - if (s->control & PARA_CTR_INTEN) { | ||
| 100 | - s->irq_pending = 1; | ||
| 101 | - } | ||
| 102 | - } | ||
| 103 | - } | ||
| 104 | - parallel_update_irq(s); | ||
| 105 | - s->control = val; | ||
| 106 | - } | 140 | + case PARA_REG_STS: |
| 141 | + pdebug("ws%02x\n", val); | ||
| 142 | + if (val & PARA_STS_TMOUT) | ||
| 143 | + s->epp_timeout = 0; | ||
| 144 | + break; | ||
| 145 | + case PARA_REG_CTR: | ||
| 146 | + val |= 0xc0; | ||
| 147 | + if (s->control == val) | ||
| 148 | + return; | ||
| 149 | + pdebug("wc%02x\n", val); | ||
| 150 | + qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_WRITE_CONTROL, &parm); | ||
| 151 | + s->control = val; | ||
| 107 | break; | 152 | break; |
| 153 | + case PARA_REG_EPP_ADDR: | ||
| 154 | + if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != PARA_CTR_INIT) | ||
| 155 | + /* Controls not correct for EPP address cycle, so do nothing */ | ||
| 156 | + pdebug("wa%02x s\n", val); | ||
| 157 | + else { | ||
| 158 | + struct ParallelIOArg ioarg = { .buffer = &parm, .count = 1 }; | ||
| 159 | + if (qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_EPP_WRITE_ADDR, &ioarg)) { | ||
| 160 | + s->epp_timeout = 1; | ||
| 161 | + pdebug("wa%02x t\n", val); | ||
| 162 | + } | ||
| 163 | + else | ||
| 164 | + pdebug("wa%02x\n", val); | ||
| 165 | + } | ||
| 166 | + break; | ||
| 167 | + case PARA_REG_EPP_DATA: | ||
| 168 | + if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != PARA_CTR_INIT) | ||
| 169 | + /* Controls not correct for EPP data cycle, so do nothing */ | ||
| 170 | + pdebug("we%02x s\n", val); | ||
| 171 | + else { | ||
| 172 | + struct ParallelIOArg ioarg = { .buffer = &parm, .count = 1 }; | ||
| 173 | + if (qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_EPP_WRITE, &ioarg)) { | ||
| 174 | + s->epp_timeout = 1; | ||
| 175 | + pdebug("we%02x t\n", val); | ||
| 176 | + } | ||
| 177 | + else | ||
| 178 | + pdebug("we%02x\n", val); | ||
| 179 | + } | ||
| 180 | + break; | ||
| 181 | + } | ||
| 182 | +} | ||
| 183 | + | ||
| 184 | +static void | ||
| 185 | +parallel_ioport_eppdata_write_hw2(void *opaque, uint32_t addr, uint32_t val) | ||
| 186 | +{ | ||
| 187 | + ParallelState *s = opaque; | ||
| 188 | + uint16_t eppdata = cpu_to_le16(val); | ||
| 189 | + int err; | ||
| 190 | + struct ParallelIOArg ioarg = { | ||
| 191 | + .buffer = &eppdata, .count = sizeof(eppdata) | ||
| 192 | + }; | ||
| 193 | + if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != PARA_CTR_INIT) { | ||
| 194 | + /* Controls not correct for EPP data cycle, so do nothing */ | ||
| 195 | + pdebug("we%04x s\n", val); | ||
| 196 | + return; | ||
| 197 | + } | ||
| 198 | + err = qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_EPP_WRITE, &ioarg); | ||
| 199 | + if (err) { | ||
| 200 | + s->epp_timeout = 1; | ||
| 201 | + pdebug("we%04x t\n", val); | ||
| 202 | + } | ||
| 203 | + else | ||
| 204 | + pdebug("we%04x\n", val); | ||
| 205 | +} | ||
| 206 | + | ||
| 207 | +static void | ||
| 208 | +parallel_ioport_eppdata_write_hw4(void *opaque, uint32_t addr, uint32_t val) | ||
| 209 | +{ | ||
| 210 | + ParallelState *s = opaque; | ||
| 211 | + uint32_t eppdata = cpu_to_le32(val); | ||
| 212 | + int err; | ||
| 213 | + struct ParallelIOArg ioarg = { | ||
| 214 | + .buffer = &eppdata, .count = sizeof(eppdata) | ||
| 215 | + }; | ||
| 216 | + if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != PARA_CTR_INIT) { | ||
| 217 | + /* Controls not correct for EPP data cycle, so do nothing */ | ||
| 218 | + pdebug("we%08x s\n", val); | ||
| 219 | + return; | ||
| 220 | + } | ||
| 221 | + err = qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_EPP_WRITE, &ioarg); | ||
| 222 | + if (err) { | ||
| 223 | + s->epp_timeout = 1; | ||
| 224 | + pdebug("we%08x t\n", val); | ||
| 108 | } | 225 | } |
| 226 | + else | ||
| 227 | + pdebug("we%08x\n", val); | ||
| 109 | } | 228 | } |
| 110 | 229 | ||
| 111 | -static uint32_t parallel_ioport_read(void *opaque, uint32_t addr) | 230 | +static uint32_t parallel_ioport_read_sw(void *opaque, uint32_t addr) |
| 112 | { | 231 | { |
| 113 | ParallelState *s = opaque; | 232 | ParallelState *s = opaque; |
| 114 | uint32_t ret = 0xff; | 233 | uint32_t ret = 0xff; |
| 115 | 234 | ||
| 116 | addr &= 7; | 235 | addr &= 7; |
| 117 | switch(addr) { | 236 | switch(addr) { |
| 118 | - case 0: | ||
| 119 | - if (s->hw_driver) { | ||
| 120 | - qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_READ_DATA, &s->data); | ||
| 121 | - } | ||
| 122 | - ret = s->data; | 237 | + case PARA_REG_DATA: |
| 238 | + if (s->control & PARA_CTR_DIR) | ||
| 239 | + ret = s->datar; | ||
| 240 | + else | ||
| 241 | + ret = s->dataw; | ||
| 123 | break; | 242 | break; |
| 124 | - case 1: | ||
| 125 | - if (s->hw_driver) { | ||
| 126 | - qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_READ_STATUS, &s->status); | ||
| 127 | - ret = s->status; | ||
| 128 | - } else { | ||
| 129 | - ret = s->status; | ||
| 130 | - s->irq_pending = 0; | ||
| 131 | - if ((s->status & PARA_STS_BUSY) == 0 && (s->control & PARA_CTR_STROBE) == 0) { | ||
| 132 | - /* XXX Fixme: wait 5 microseconds */ | ||
| 133 | - if (s->status & PARA_STS_ACK) | ||
| 134 | - s->status &= ~PARA_STS_ACK; | ||
| 135 | - else { | ||
| 136 | - /* XXX Fixme: wait 5 microseconds */ | ||
| 137 | - s->status |= PARA_STS_ACK; | ||
| 138 | - s->status |= PARA_STS_BUSY; | ||
| 139 | - } | ||
| 140 | - } | ||
| 141 | - parallel_update_irq(s); | ||
| 142 | - } | 243 | + case PARA_REG_STS: |
| 244 | + ret = s->status; | ||
| 245 | + s->irq_pending = 0; | ||
| 246 | + if ((s->status & PARA_STS_BUSY) == 0 && (s->control & PARA_CTR_STROBE) == 0) { | ||
| 247 | + /* XXX Fixme: wait 5 microseconds */ | ||
| 248 | + if (s->status & PARA_STS_ACK) | ||
| 249 | + s->status &= ~PARA_STS_ACK; | ||
| 250 | + else { | ||
| 251 | + /* XXX Fixme: wait 5 microseconds */ | ||
| 252 | + s->status |= PARA_STS_ACK; | ||
| 253 | + s->status |= PARA_STS_BUSY; | ||
| 254 | + } | ||
| 255 | + } | ||
| 256 | + parallel_update_irq(s); | ||
| 143 | break; | 257 | break; |
| 144 | - case 2: | ||
| 145 | - if (s->hw_driver) { | ||
| 146 | - qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_READ_CONTROL, &s->control); | ||
| 147 | - } | 258 | + case PARA_REG_CTR: |
| 148 | ret = s->control; | 259 | ret = s->control; |
| 149 | break; | 260 | break; |
| 150 | } | 261 | } |
| 151 | -#ifdef DEBUG_PARALLEL | ||
| 152 | - printf("parallel: read addr=0x%02x val=0x%02x\n", addr, ret); | ||
| 153 | -#endif | 262 | + pdebug("read addr=0x%02x val=0x%02x\n", addr, ret); |
| 263 | + return ret; | ||
| 264 | +} | ||
| 265 | + | ||
| 266 | +static uint32_t parallel_ioport_read_hw(void *opaque, uint32_t addr) | ||
| 267 | +{ | ||
| 268 | + ParallelState *s = opaque; | ||
| 269 | + uint8_t ret = 0xff; | ||
| 270 | + addr &= 7; | ||
| 271 | + switch(addr) { | ||
| 272 | + case PARA_REG_DATA: | ||
| 273 | + qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_READ_DATA, &ret); | ||
| 274 | + if (s->last_read_offset != addr || s->datar != ret) | ||
| 275 | + pdebug("rd%02x\n", ret); | ||
| 276 | + s->datar = ret; | ||
| 277 | + break; | ||
| 278 | + case PARA_REG_STS: | ||
| 279 | + qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_READ_STATUS, &ret); | ||
| 280 | + ret &= ~PARA_STS_TMOUT; | ||
| 281 | + if (s->epp_timeout) | ||
| 282 | + ret |= PARA_STS_TMOUT; | ||
| 283 | + if (s->last_read_offset != addr || s->status != ret) | ||
| 284 | + pdebug("rs%02x\n", ret); | ||
| 285 | + s->status = ret; | ||
| 286 | + break; | ||
| 287 | + case PARA_REG_CTR: | ||
| 288 | + /* s->control has some bits fixed to 1. It is zero only when | ||
| 289 | + it has not been yet written to. */ | ||
| 290 | + if (s->control == 0) { | ||
| 291 | + qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_READ_CONTROL, &ret); | ||
| 292 | + if (s->last_read_offset != addr) | ||
| 293 | + pdebug("rc%02x\n", ret); | ||
| 294 | + s->control = ret; | ||
| 295 | + } | ||
| 296 | + else { | ||
| 297 | + ret = s->control; | ||
| 298 | + if (s->last_read_offset != addr) | ||
| 299 | + pdebug("rc%02x\n", ret); | ||
| 300 | + } | ||
| 301 | + break; | ||
| 302 | + case PARA_REG_EPP_ADDR: | ||
| 303 | + if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != (PARA_CTR_DIR|PARA_CTR_INIT)) | ||
| 304 | + /* Controls not correct for EPP addr cycle, so do nothing */ | ||
| 305 | + pdebug("ra%02x s\n", ret); | ||
| 306 | + else { | ||
| 307 | + struct ParallelIOArg ioarg = { .buffer = &ret, .count = 1 }; | ||
| 308 | + if (qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_EPP_READ_ADDR, &ioarg)) { | ||
| 309 | + s->epp_timeout = 1; | ||
| 310 | + pdebug("ra%02x t\n", ret); | ||
| 311 | + } | ||
| 312 | + else | ||
| 313 | + pdebug("ra%02x\n", ret); | ||
| 314 | + } | ||
| 315 | + break; | ||
| 316 | + case PARA_REG_EPP_DATA: | ||
| 317 | + if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != (PARA_CTR_DIR|PARA_CTR_INIT)) | ||
| 318 | + /* Controls not correct for EPP data cycle, so do nothing */ | ||
| 319 | + pdebug("re%02x s\n", ret); | ||
| 320 | + else { | ||
| 321 | + struct ParallelIOArg ioarg = { .buffer = &ret, .count = 1 }; | ||
| 322 | + if (qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_EPP_READ, &ioarg)) { | ||
| 323 | + s->epp_timeout = 1; | ||
| 324 | + pdebug("re%02x t\n", ret); | ||
| 325 | + } | ||
| 326 | + else | ||
| 327 | + pdebug("re%02x\n", ret); | ||
| 328 | + } | ||
| 329 | + break; | ||
| 330 | + } | ||
| 331 | + s->last_read_offset = addr; | ||
| 332 | + return ret; | ||
| 333 | +} | ||
| 334 | + | ||
| 335 | +static uint32_t | ||
| 336 | +parallel_ioport_eppdata_read_hw2(void *opaque, uint32_t addr) | ||
| 337 | +{ | ||
| 338 | + ParallelState *s = opaque; | ||
| 339 | + uint32_t ret; | ||
| 340 | + uint16_t eppdata = ~0; | ||
| 341 | + int err; | ||
| 342 | + struct ParallelIOArg ioarg = { | ||
| 343 | + .buffer = &eppdata, .count = sizeof(eppdata) | ||
| 344 | + }; | ||
| 345 | + if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != (PARA_CTR_DIR|PARA_CTR_INIT)) { | ||
| 346 | + /* Controls not correct for EPP data cycle, so do nothing */ | ||
| 347 | + pdebug("re%04x s\n", eppdata); | ||
| 348 | + return eppdata; | ||
| 349 | + } | ||
| 350 | + err = qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_EPP_READ, &ioarg); | ||
| 351 | + ret = le16_to_cpu(eppdata); | ||
| 352 | + | ||
| 353 | + if (err) { | ||
| 354 | + s->epp_timeout = 1; | ||
| 355 | + pdebug("re%04x t\n", ret); | ||
| 356 | + } | ||
| 357 | + else | ||
| 358 | + pdebug("re%04x\n", ret); | ||
| 359 | + return ret; | ||
| 360 | +} | ||
| 361 | + | ||
| 362 | +static uint32_t | ||
| 363 | +parallel_ioport_eppdata_read_hw4(void *opaque, uint32_t addr) | ||
| 364 | +{ | ||
| 365 | + ParallelState *s = opaque; | ||
| 366 | + uint32_t ret; | ||
| 367 | + uint32_t eppdata = ~0U; | ||
| 368 | + int err; | ||
| 369 | + struct ParallelIOArg ioarg = { | ||
| 370 | + .buffer = &eppdata, .count = sizeof(eppdata) | ||
| 371 | + }; | ||
| 372 | + if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != (PARA_CTR_DIR|PARA_CTR_INIT)) { | ||
| 373 | + /* Controls not correct for EPP data cycle, so do nothing */ | ||
| 374 | + pdebug("re%08x s\n", eppdata); | ||
| 375 | + return eppdata; | ||
| 376 | + } | ||
| 377 | + err = qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_EPP_READ, &ioarg); | ||
| 378 | + ret = le32_to_cpu(eppdata); | ||
| 379 | + | ||
| 380 | + if (err) { | ||
| 381 | + s->epp_timeout = 1; | ||
| 382 | + pdebug("re%08x t\n", ret); | ||
| 383 | + } | ||
| 384 | + else | ||
| 385 | + pdebug("re%08x\n", ret); | ||
| 386 | + return ret; | ||
| 387 | +} | ||
| 388 | + | ||
| 389 | +static void parallel_ioport_ecp_write(void *opaque, uint32_t addr, uint32_t val) | ||
| 390 | +{ | ||
| 391 | + addr &= 7; | ||
| 392 | + pdebug("wecp%d=%02x\n", addr, val); | ||
| 393 | +} | ||
| 394 | + | ||
| 395 | +static uint32_t parallel_ioport_ecp_read(void *opaque, uint32_t addr) | ||
| 396 | +{ | ||
| 397 | + uint8_t ret = 0xff; | ||
| 398 | + addr &= 7; | ||
| 399 | + pdebug("recp%d:%02x\n", addr, ret); | ||
| 154 | return ret; | 400 | return ret; |
| 155 | } | 401 | } |
| 156 | 402 | ||
| @@ -163,21 +409,39 @@ ParallelState *parallel_init(int base, int irq, CharDriverState *chr) | @@ -163,21 +409,39 @@ ParallelState *parallel_init(int base, int irq, CharDriverState *chr) | ||
| 163 | s = qemu_mallocz(sizeof(ParallelState)); | 409 | s = qemu_mallocz(sizeof(ParallelState)); |
| 164 | if (!s) | 410 | if (!s) |
| 165 | return NULL; | 411 | return NULL; |
| 166 | - s->chr = chr; | ||
| 167 | - s->hw_driver = 0; | ||
| 168 | - if (qemu_chr_ioctl(chr, CHR_IOCTL_PP_READ_STATUS, &dummy) == 0) | ||
| 169 | - s->hw_driver = 1; | ||
| 170 | - | ||
| 171 | - s->irq = irq; | ||
| 172 | - s->data = 0; | 412 | + s->datar = ~0; |
| 413 | + s->dataw = ~0; | ||
| 173 | s->status = PARA_STS_BUSY; | 414 | s->status = PARA_STS_BUSY; |
| 174 | s->status |= PARA_STS_ACK; | 415 | s->status |= PARA_STS_ACK; |
| 175 | s->status |= PARA_STS_ONLINE; | 416 | s->status |= PARA_STS_ONLINE; |
| 176 | s->status |= PARA_STS_ERROR; | 417 | s->status |= PARA_STS_ERROR; |
| 177 | s->control = PARA_CTR_SELECT; | 418 | s->control = PARA_CTR_SELECT; |
| 178 | s->control |= PARA_CTR_INIT; | 419 | s->control |= PARA_CTR_INIT; |
| 420 | + s->irq = irq; | ||
| 421 | + s->irq_pending = 0; | ||
| 422 | + s->chr = chr; | ||
| 423 | + s->hw_driver = 0; | ||
| 424 | + s->epp_timeout = 0; | ||
| 425 | + s->last_read_offset = ~0U; | ||
| 179 | 426 | ||
| 180 | - register_ioport_write(base, 8, 1, parallel_ioport_write, s); | ||
| 181 | - register_ioport_read(base, 8, 1, parallel_ioport_read, s); | 427 | + if (qemu_chr_ioctl(chr, CHR_IOCTL_PP_READ_STATUS, &dummy) == 0) { |
| 428 | + s->hw_driver = 1; | ||
| 429 | + s->status = dummy; | ||
| 430 | + } | ||
| 431 | + | ||
| 432 | + if (s->hw_driver) { | ||
| 433 | + register_ioport_write(base, 8, 1, parallel_ioport_write_hw, s); | ||
| 434 | + register_ioport_read(base, 8, 1, parallel_ioport_read_hw, s); | ||
| 435 | + register_ioport_write(base+4, 1, 2, parallel_ioport_eppdata_write_hw2, s); | ||
| 436 | + register_ioport_read(base+4, 1, 2, parallel_ioport_eppdata_read_hw2, s); | ||
| 437 | + register_ioport_write(base+4, 1, 4, parallel_ioport_eppdata_write_hw4, s); | ||
| 438 | + register_ioport_read(base+4, 1, 4, parallel_ioport_eppdata_read_hw4, s); | ||
| 439 | + register_ioport_write(base+0x400, 8, 1, parallel_ioport_ecp_write, s); | ||
| 440 | + register_ioport_read(base+0x400, 8, 1, parallel_ioport_ecp_read, s); | ||
| 441 | + } | ||
| 442 | + else { | ||
| 443 | + register_ioport_write(base, 8, 1, parallel_ioport_write_sw, s); | ||
| 444 | + register_ioport_read(base, 8, 1, parallel_ioport_read_sw, s); | ||
| 445 | + } | ||
| 182 | return s; | 446 | return s; |
| 183 | } | 447 | } |
qemu-doc.texi
| @@ -541,7 +541,7 @@ void device | @@ -541,7 +541,7 @@ void device | ||
| 541 | parameters are set according to the emulated ones. | 541 | parameters are set according to the emulated ones. |
| 542 | @item /dev/parportN | 542 | @item /dev/parportN |
| 543 | [Linux only, parallel port only] Use host parallel port | 543 | [Linux only, parallel port only] Use host parallel port |
| 544 | -@var{N}. Currently only SPP parallel port features can be used. | 544 | +@var{N}. Currently SPP and EPP parallel port features can be used. |
| 545 | @item file:filename | 545 | @item file:filename |
| 546 | Write output to filename. No character can be read. | 546 | Write output to filename. No character can be read. |
| 547 | @item stdio | 547 | @item stdio |
vl.c
| @@ -55,6 +55,7 @@ | @@ -55,6 +55,7 @@ | ||
| 55 | #include <malloc.h> | 55 | #include <malloc.h> |
| 56 | #include <linux/rtc.h> | 56 | #include <linux/rtc.h> |
| 57 | #include <linux/ppdev.h> | 57 | #include <linux/ppdev.h> |
| 58 | +#include <linux/parport.h> | ||
| 58 | #else | 59 | #else |
| 59 | #include <sys/stat.h> | 60 | #include <sys/stat.h> |
| 60 | #include <sys/ethernet.h> | 61 | #include <sys/ethernet.h> |
| @@ -1813,9 +1814,26 @@ static CharDriverState *qemu_chr_open_tty(const char *filename) | @@ -1813,9 +1814,26 @@ static CharDriverState *qemu_chr_open_tty(const char *filename) | ||
| 1813 | return chr; | 1814 | return chr; |
| 1814 | } | 1815 | } |
| 1815 | 1816 | ||
| 1817 | +typedef struct { | ||
| 1818 | + int fd; | ||
| 1819 | + int mode; | ||
| 1820 | +} ParallelCharDriver; | ||
| 1821 | + | ||
| 1822 | +static int pp_hw_mode(ParallelCharDriver *s, uint16_t mode) | ||
| 1823 | +{ | ||
| 1824 | + if (s->mode != mode) { | ||
| 1825 | + int m = mode; | ||
| 1826 | + if (ioctl(s->fd, PPSETMODE, &m) < 0) | ||
| 1827 | + return 0; | ||
| 1828 | + s->mode = mode; | ||
| 1829 | + } | ||
| 1830 | + return 1; | ||
| 1831 | +} | ||
| 1832 | + | ||
| 1816 | static int pp_ioctl(CharDriverState *chr, int cmd, void *arg) | 1833 | static int pp_ioctl(CharDriverState *chr, int cmd, void *arg) |
| 1817 | { | 1834 | { |
| 1818 | - int fd = (int)chr->opaque; | 1835 | + ParallelCharDriver *drv = chr->opaque; |
| 1836 | + int fd = drv->fd; | ||
| 1819 | uint8_t b; | 1837 | uint8_t b; |
| 1820 | 1838 | ||
| 1821 | switch(cmd) { | 1839 | switch(cmd) { |
| @@ -1832,7 +1850,10 @@ static int pp_ioctl(CharDriverState *chr, int cmd, void *arg) | @@ -1832,7 +1850,10 @@ static int pp_ioctl(CharDriverState *chr, int cmd, void *arg) | ||
| 1832 | case CHR_IOCTL_PP_READ_CONTROL: | 1850 | case CHR_IOCTL_PP_READ_CONTROL: |
| 1833 | if (ioctl(fd, PPRCONTROL, &b) < 0) | 1851 | if (ioctl(fd, PPRCONTROL, &b) < 0) |
| 1834 | return -ENOTSUP; | 1852 | return -ENOTSUP; |
| 1835 | - *(uint8_t *)arg = b; | 1853 | + /* Linux gives only the lowest bits, and no way to know data |
| 1854 | + direction! For better compatibility set the fixed upper | ||
| 1855 | + bits. */ | ||
| 1856 | + *(uint8_t *)arg = b | 0xc0; | ||
| 1836 | break; | 1857 | break; |
| 1837 | case CHR_IOCTL_PP_WRITE_CONTROL: | 1858 | case CHR_IOCTL_PP_WRITE_CONTROL: |
| 1838 | b = *(uint8_t *)arg; | 1859 | b = *(uint8_t *)arg; |
| @@ -1844,15 +1865,63 @@ static int pp_ioctl(CharDriverState *chr, int cmd, void *arg) | @@ -1844,15 +1865,63 @@ static int pp_ioctl(CharDriverState *chr, int cmd, void *arg) | ||
| 1844 | return -ENOTSUP; | 1865 | return -ENOTSUP; |
| 1845 | *(uint8_t *)arg = b; | 1866 | *(uint8_t *)arg = b; |
| 1846 | break; | 1867 | break; |
| 1868 | + case CHR_IOCTL_PP_EPP_READ_ADDR: | ||
| 1869 | + if (pp_hw_mode(drv, IEEE1284_MODE_EPP|IEEE1284_ADDR)) { | ||
| 1870 | + struct ParallelIOArg *parg = arg; | ||
| 1871 | + int n = read(fd, parg->buffer, parg->count); | ||
| 1872 | + if (n != parg->count) { | ||
| 1873 | + return -EIO; | ||
| 1874 | + } | ||
| 1875 | + } | ||
| 1876 | + break; | ||
| 1877 | + case CHR_IOCTL_PP_EPP_READ: | ||
| 1878 | + if (pp_hw_mode(drv, IEEE1284_MODE_EPP)) { | ||
| 1879 | + struct ParallelIOArg *parg = arg; | ||
| 1880 | + int n = read(fd, parg->buffer, parg->count); | ||
| 1881 | + if (n != parg->count) { | ||
| 1882 | + return -EIO; | ||
| 1883 | + } | ||
| 1884 | + } | ||
| 1885 | + break; | ||
| 1886 | + case CHR_IOCTL_PP_EPP_WRITE_ADDR: | ||
| 1887 | + if (pp_hw_mode(drv, IEEE1284_MODE_EPP|IEEE1284_ADDR)) { | ||
| 1888 | + struct ParallelIOArg *parg = arg; | ||
| 1889 | + int n = write(fd, parg->buffer, parg->count); | ||
| 1890 | + if (n != parg->count) { | ||
| 1891 | + return -EIO; | ||
| 1892 | + } | ||
| 1893 | + } | ||
| 1894 | + break; | ||
| 1895 | + case CHR_IOCTL_PP_EPP_WRITE: | ||
| 1896 | + if (pp_hw_mode(drv, IEEE1284_MODE_EPP)) { | ||
| 1897 | + struct ParallelIOArg *parg = arg; | ||
| 1898 | + int n = write(fd, parg->buffer, parg->count); | ||
| 1899 | + if (n != parg->count) { | ||
| 1900 | + return -EIO; | ||
| 1901 | + } | ||
| 1902 | + } | ||
| 1903 | + break; | ||
| 1847 | default: | 1904 | default: |
| 1848 | return -ENOTSUP; | 1905 | return -ENOTSUP; |
| 1849 | } | 1906 | } |
| 1850 | return 0; | 1907 | return 0; |
| 1851 | } | 1908 | } |
| 1852 | 1909 | ||
| 1910 | +static void pp_close(CharDriverState *chr) | ||
| 1911 | +{ | ||
| 1912 | + ParallelCharDriver *drv = chr->opaque; | ||
| 1913 | + int fd = drv->fd; | ||
| 1914 | + | ||
| 1915 | + pp_hw_mode(drv, IEEE1284_MODE_COMPAT); | ||
| 1916 | + ioctl(fd, PPRELEASE); | ||
| 1917 | + close(fd); | ||
| 1918 | + qemu_free(drv); | ||
| 1919 | +} | ||
| 1920 | + | ||
| 1853 | static CharDriverState *qemu_chr_open_pp(const char *filename) | 1921 | static CharDriverState *qemu_chr_open_pp(const char *filename) |
| 1854 | { | 1922 | { |
| 1855 | CharDriverState *chr; | 1923 | CharDriverState *chr; |
| 1924 | + ParallelCharDriver *drv; | ||
| 1856 | int fd; | 1925 | int fd; |
| 1857 | 1926 | ||
| 1858 | fd = open(filename, O_RDWR); | 1927 | fd = open(filename, O_RDWR); |
| @@ -1864,14 +1933,24 @@ static CharDriverState *qemu_chr_open_pp(const char *filename) | @@ -1864,14 +1933,24 @@ static CharDriverState *qemu_chr_open_pp(const char *filename) | ||
| 1864 | return NULL; | 1933 | return NULL; |
| 1865 | } | 1934 | } |
| 1866 | 1935 | ||
| 1936 | + drv = qemu_mallocz(sizeof(ParallelCharDriver)); | ||
| 1937 | + if (!drv) { | ||
| 1938 | + close(fd); | ||
| 1939 | + return NULL; | ||
| 1940 | + } | ||
| 1941 | + drv->fd = fd; | ||
| 1942 | + drv->mode = IEEE1284_MODE_COMPAT; | ||
| 1943 | + | ||
| 1867 | chr = qemu_mallocz(sizeof(CharDriverState)); | 1944 | chr = qemu_mallocz(sizeof(CharDriverState)); |
| 1868 | if (!chr) { | 1945 | if (!chr) { |
| 1946 | + qemu_free(drv); | ||
| 1869 | close(fd); | 1947 | close(fd); |
| 1870 | return NULL; | 1948 | return NULL; |
| 1871 | } | 1949 | } |
| 1872 | - chr->opaque = (void *)fd; | ||
| 1873 | chr->chr_write = null_chr_write; | 1950 | chr->chr_write = null_chr_write; |
| 1874 | chr->chr_ioctl = pp_ioctl; | 1951 | chr->chr_ioctl = pp_ioctl; |
| 1952 | + chr->chr_close = pp_close; | ||
| 1953 | + chr->opaque = drv; | ||
| 1875 | 1954 | ||
| 1876 | qemu_chr_reset(chr); | 1955 | qemu_chr_reset(chr); |
| 1877 | 1956 |
vl.h
| @@ -285,6 +285,10 @@ typedef struct { | @@ -285,6 +285,10 @@ typedef struct { | ||
| 285 | #define CHR_IOCTL_PP_READ_CONTROL 5 | 285 | #define CHR_IOCTL_PP_READ_CONTROL 5 |
| 286 | #define CHR_IOCTL_PP_WRITE_CONTROL 6 | 286 | #define CHR_IOCTL_PP_WRITE_CONTROL 6 |
| 287 | #define CHR_IOCTL_PP_READ_STATUS 7 | 287 | #define CHR_IOCTL_PP_READ_STATUS 7 |
| 288 | +#define CHR_IOCTL_PP_EPP_READ_ADDR 8 | ||
| 289 | +#define CHR_IOCTL_PP_EPP_READ 9 | ||
| 290 | +#define CHR_IOCTL_PP_EPP_WRITE_ADDR 10 | ||
| 291 | +#define CHR_IOCTL_PP_EPP_WRITE 11 | ||
| 288 | 292 | ||
| 289 | typedef void IOEventHandler(void *opaque, int event); | 293 | typedef void IOEventHandler(void *opaque, int event); |
| 290 | 294 | ||
| @@ -349,6 +353,11 @@ extern CharDriverState *serial_hds[MAX_SERIAL_PORTS]; | @@ -349,6 +353,11 @@ extern CharDriverState *serial_hds[MAX_SERIAL_PORTS]; | ||
| 349 | 353 | ||
| 350 | extern CharDriverState *parallel_hds[MAX_PARALLEL_PORTS]; | 354 | extern CharDriverState *parallel_hds[MAX_PARALLEL_PORTS]; |
| 351 | 355 | ||
| 356 | +struct ParallelIOArg { | ||
| 357 | + void *buffer; | ||
| 358 | + int count; | ||
| 359 | +}; | ||
| 360 | + | ||
| 352 | /* VLANs support */ | 361 | /* VLANs support */ |
| 353 | 362 | ||
| 354 | typedef struct VLANClientState VLANClientState; | 363 | typedef struct VLANClientState VLANClientState; |