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 | 2 | * QEMU Parallel PORT emulation |
| 3 | 3 | * |
| 4 | 4 | * Copyright (c) 2003-2005 Fabrice Bellard |
| 5 | + * Copyright (c) 2007 Marko Kohtala | |
| 5 | 6 | * |
| 6 | 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
| 7 | 8 | * of this software and associated documentation files (the "Software"), to deal |
| ... | ... | @@ -25,6 +26,18 @@ |
| 25 | 26 | |
| 26 | 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 | 42 | * These are the definitions for the Printer Status Register |
| 30 | 43 | */ |
| ... | ... | @@ -33,24 +46,31 @@ |
| 33 | 46 | #define PARA_STS_PAPER 0x20 /* Out of paper */ |
| 34 | 47 | #define PARA_STS_ONLINE 0x10 /* Online */ |
| 35 | 48 | #define PARA_STS_ERROR 0x08 /* Error complement */ |
| 49 | +#define PARA_STS_TMOUT 0x01 /* EPP timeout */ | |
| 36 | 50 | |
| 37 | 51 | /* |
| 38 | 52 | * These are the definitions for the Printer Control Register |
| 39 | 53 | */ |
| 54 | +#define PARA_CTR_DIR 0x20 /* Direction (1=read, 0=write) */ | |
| 40 | 55 | #define PARA_CTR_INTEN 0x10 /* IRQ Enable */ |
| 41 | 56 | #define PARA_CTR_SELECT 0x08 /* Select In complement */ |
| 42 | 57 | #define PARA_CTR_INIT 0x04 /* Initialize Printer complement */ |
| 43 | 58 | #define PARA_CTR_AUTOLF 0x02 /* Auto linefeed complement */ |
| 44 | 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 | 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 | 67 | uint8_t control; |
| 50 | 68 | int irq; |
| 51 | 69 | int irq_pending; |
| 52 | 70 | CharDriverState *chr; |
| 53 | 71 | int hw_driver; |
| 72 | + int epp_timeout; | |
| 73 | + uint32_t last_read_offset; /* For debugging */ | |
| 54 | 74 | }; |
| 55 | 75 | |
| 56 | 76 | static void parallel_update_irq(ParallelState *s) |
| ... | ... | @@ -61,96 +81,322 @@ static void parallel_update_irq(ParallelState *s) |
| 61 | 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 | 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 | 131 | addr &= 7; |
| 69 | -#ifdef DEBUG_PARALLEL | |
| 70 | - printf("parallel: write addr=0x%02x val=0x%02x\n", addr, val); | |
| 71 | -#endif | |
| 72 | 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 | 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 | 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 | 232 | ParallelState *s = opaque; |
| 114 | 233 | uint32_t ret = 0xff; |
| 115 | 234 | |
| 116 | 235 | addr &= 7; |
| 117 | 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 | 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 | 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 | 259 | ret = s->control; |
| 149 | 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 | 400 | return ret; |
| 155 | 401 | } |
| 156 | 402 | |
| ... | ... | @@ -163,21 +409,39 @@ ParallelState *parallel_init(int base, int irq, CharDriverState *chr) |
| 163 | 409 | s = qemu_mallocz(sizeof(ParallelState)); |
| 164 | 410 | if (!s) |
| 165 | 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 | 414 | s->status = PARA_STS_BUSY; |
| 174 | 415 | s->status |= PARA_STS_ACK; |
| 175 | 416 | s->status |= PARA_STS_ONLINE; |
| 176 | 417 | s->status |= PARA_STS_ERROR; |
| 177 | 418 | s->control = PARA_CTR_SELECT; |
| 178 | 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 | 446 | return s; |
| 183 | 447 | } | ... | ... |
qemu-doc.texi
| ... | ... | @@ -541,7 +541,7 @@ void device |
| 541 | 541 | parameters are set according to the emulated ones. |
| 542 | 542 | @item /dev/parportN |
| 543 | 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 | 545 | @item file:filename |
| 546 | 546 | Write output to filename. No character can be read. |
| 547 | 547 | @item stdio | ... | ... |
vl.c
| ... | ... | @@ -55,6 +55,7 @@ |
| 55 | 55 | #include <malloc.h> |
| 56 | 56 | #include <linux/rtc.h> |
| 57 | 57 | #include <linux/ppdev.h> |
| 58 | +#include <linux/parport.h> | |
| 58 | 59 | #else |
| 59 | 60 | #include <sys/stat.h> |
| 60 | 61 | #include <sys/ethernet.h> |
| ... | ... | @@ -1813,9 +1814,26 @@ static CharDriverState *qemu_chr_open_tty(const char *filename) |
| 1813 | 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 | 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 | 1837 | uint8_t b; |
| 1820 | 1838 | |
| 1821 | 1839 | switch(cmd) { |
| ... | ... | @@ -1832,7 +1850,10 @@ static int pp_ioctl(CharDriverState *chr, int cmd, void *arg) |
| 1832 | 1850 | case CHR_IOCTL_PP_READ_CONTROL: |
| 1833 | 1851 | if (ioctl(fd, PPRCONTROL, &b) < 0) |
| 1834 | 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 | 1857 | break; |
| 1837 | 1858 | case CHR_IOCTL_PP_WRITE_CONTROL: |
| 1838 | 1859 | b = *(uint8_t *)arg; |
| ... | ... | @@ -1844,15 +1865,63 @@ static int pp_ioctl(CharDriverState *chr, int cmd, void *arg) |
| 1844 | 1865 | return -ENOTSUP; |
| 1845 | 1866 | *(uint8_t *)arg = b; |
| 1846 | 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 | 1904 | default: |
| 1848 | 1905 | return -ENOTSUP; |
| 1849 | 1906 | } |
| 1850 | 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 | 1921 | static CharDriverState *qemu_chr_open_pp(const char *filename) |
| 1854 | 1922 | { |
| 1855 | 1923 | CharDriverState *chr; |
| 1924 | + ParallelCharDriver *drv; | |
| 1856 | 1925 | int fd; |
| 1857 | 1926 | |
| 1858 | 1927 | fd = open(filename, O_RDWR); |
| ... | ... | @@ -1864,14 +1933,24 @@ static CharDriverState *qemu_chr_open_pp(const char *filename) |
| 1864 | 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 | 1944 | chr = qemu_mallocz(sizeof(CharDriverState)); |
| 1868 | 1945 | if (!chr) { |
| 1946 | + qemu_free(drv); | |
| 1869 | 1947 | close(fd); |
| 1870 | 1948 | return NULL; |
| 1871 | 1949 | } |
| 1872 | - chr->opaque = (void *)fd; | |
| 1873 | 1950 | chr->chr_write = null_chr_write; |
| 1874 | 1951 | chr->chr_ioctl = pp_ioctl; |
| 1952 | + chr->chr_close = pp_close; | |
| 1953 | + chr->opaque = drv; | |
| 1875 | 1954 | |
| 1876 | 1955 | qemu_chr_reset(chr); |
| 1877 | 1956 | ... | ... |
vl.h
| ... | ... | @@ -285,6 +285,10 @@ typedef struct { |
| 285 | 285 | #define CHR_IOCTL_PP_READ_CONTROL 5 |
| 286 | 286 | #define CHR_IOCTL_PP_WRITE_CONTROL 6 |
| 287 | 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 | 293 | typedef void IOEventHandler(void *opaque, int event); |
| 290 | 294 | |
| ... | ... | @@ -349,6 +353,11 @@ extern CharDriverState *serial_hds[MAX_SERIAL_PORTS]; |
| 349 | 353 | |
| 350 | 354 | extern CharDriverState *parallel_hds[MAX_PARALLEL_PORTS]; |
| 351 | 355 | |
| 356 | +struct ParallelIOArg { | |
| 357 | + void *buffer; | |
| 358 | + int count; | |
| 359 | +}; | |
| 360 | + | |
| 352 | 361 | /* VLANs support */ |
| 353 | 362 | |
| 354 | 363 | typedef struct VLANClientState VLANClientState; | ... | ... |