Commit e57a8c0eefcf648bd0f357abd94ee2871888f43d
1 parent
2122c51a
low level host parallel port access
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1611 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
4 changed files
with
171 additions
and
65 deletions
Changelog
hw/parallel.c
| 1 | 1 | /* |
| 2 | 2 | * QEMU Parallel PORT emulation |
| 3 | 3 | * |
| 4 | - * Copyright (c) 2003-2004 Fabrice Bellard | |
| 4 | + * Copyright (c) 2003-2005 Fabrice Bellard | |
| 5 | 5 | * |
| 6 | 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
| 7 | 7 | * of this software and associated documentation files (the "Software"), to deal |
| ... | ... | @@ -50,6 +50,7 @@ struct ParallelState { |
| 50 | 50 | int irq; |
| 51 | 51 | int irq_pending; |
| 52 | 52 | CharDriverState *chr; |
| 53 | + int hw_driver; | |
| 53 | 54 | }; |
| 54 | 55 | |
| 55 | 56 | static void parallel_update_irq(ParallelState *s) |
| ... | ... | @@ -70,29 +71,39 @@ static void parallel_ioport_write(void *opaque, uint32_t addr, uint32_t val) |
| 70 | 71 | #endif |
| 71 | 72 | switch(addr) { |
| 72 | 73 | case 0: |
| 73 | - s->data = val; | |
| 74 | - parallel_update_irq(s); | |
| 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 | + } | |
| 75 | 81 | break; |
| 76 | 82 | case 2: |
| 77 | - if ((val & PARA_CTR_INIT) == 0 ) { | |
| 78 | - s->status = PARA_STS_BUSY; | |
| 79 | - s->status |= PARA_STS_ACK; | |
| 80 | - s->status |= PARA_STS_ONLINE; | |
| 81 | - s->status |= PARA_STS_ERROR; | |
| 82 | - } | |
| 83 | - else if (val & PARA_CTR_SELECT) { | |
| 84 | - if (val & PARA_CTR_STROBE) { | |
| 85 | - s->status &= ~PARA_STS_BUSY; | |
| 86 | - if ((s->control & PARA_CTR_STROBE) == 0) | |
| 87 | - qemu_chr_write(s->chr, &s->data, 1); | |
| 88 | - } else { | |
| 89 | - if (s->control & PARA_CTR_INTEN) { | |
| 90 | - s->irq_pending = 1; | |
| 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 | + } | |
| 91 | 102 | } |
| 92 | 103 | } |
| 104 | + parallel_update_irq(s); | |
| 105 | + s->control = val; | |
| 93 | 106 | } |
| 94 | - parallel_update_irq(s); | |
| 95 | - s->control = val; | |
| 96 | 107 | break; |
| 97 | 108 | } |
| 98 | 109 | } |
| ... | ... | @@ -105,24 +116,35 @@ static uint32_t parallel_ioport_read(void *opaque, uint32_t addr) |
| 105 | 116 | addr &= 7; |
| 106 | 117 | switch(addr) { |
| 107 | 118 | case 0: |
| 119 | + if (s->hw_driver) { | |
| 120 | + qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_READ_DATA, &s->data); | |
| 121 | + } | |
| 108 | 122 | ret = s->data; |
| 109 | 123 | break; |
| 110 | 124 | case 1: |
| 111 | - ret = s->status; | |
| 112 | - s->irq_pending = 0; | |
| 113 | - if ((s->status & PARA_STS_BUSY) == 0 && (s->control & PARA_CTR_STROBE) == 0) { | |
| 114 | - /* XXX Fixme: wait 5 microseconds */ | |
| 115 | - if (s->status & PARA_STS_ACK) | |
| 116 | - s->status &= ~PARA_STS_ACK; | |
| 117 | - else { | |
| 118 | - /* XXX Fixme: wait 5 microseconds */ | |
| 119 | - s->status |= PARA_STS_ACK; | |
| 120 | - s->status |= PARA_STS_BUSY; | |
| 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 | + } | |
| 121 | 140 | } |
| 141 | + parallel_update_irq(s); | |
| 122 | 142 | } |
| 123 | - parallel_update_irq(s); | |
| 124 | 143 | break; |
| 125 | 144 | case 2: |
| 145 | + if (s->hw_driver) { | |
| 146 | + qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_READ_CONTROL, &s->control); | |
| 147 | + } | |
| 126 | 148 | ret = s->control; |
| 127 | 149 | break; |
| 128 | 150 | } |
| ... | ... | @@ -153,18 +175,20 @@ static void parallel_receive1(void *opaque, const uint8_t *buf, int size) |
| 153 | 175 | parallel_receive_byte(s, buf[0]); |
| 154 | 176 | } |
| 155 | 177 | |
| 156 | -static void parallel_event(void *opaque, int event) | |
| 157 | -{ | |
| 158 | -} | |
| 159 | - | |
| 160 | 178 | /* If fd is zero, it means that the parallel device uses the console */ |
| 161 | 179 | ParallelState *parallel_init(int base, int irq, CharDriverState *chr) |
| 162 | 180 | { |
| 163 | 181 | ParallelState *s; |
| 182 | + uint8_t dummy; | |
| 164 | 183 | |
| 165 | 184 | s = qemu_mallocz(sizeof(ParallelState)); |
| 166 | 185 | if (!s) |
| 167 | 186 | return NULL; |
| 187 | + s->chr = chr; | |
| 188 | + s->hw_driver = 0; | |
| 189 | + if (qemu_chr_ioctl(chr, CHR_IOCTL_PP_READ_STATUS, &dummy) == 0) | |
| 190 | + s->hw_driver = 1; | |
| 191 | + | |
| 168 | 192 | s->irq = irq; |
| 169 | 193 | s->data = 0; |
| 170 | 194 | s->status = PARA_STS_BUSY; |
| ... | ... | @@ -176,8 +200,6 @@ ParallelState *parallel_init(int base, int irq, CharDriverState *chr) |
| 176 | 200 | |
| 177 | 201 | register_ioport_write(base, 8, 1, parallel_ioport_write, s); |
| 178 | 202 | register_ioport_read(base, 8, 1, parallel_ioport_read, s); |
| 179 | - s->chr = chr; | |
| 180 | 203 | qemu_chr_add_read_handler(chr, parallel_can_receive1, parallel_receive1, s); |
| 181 | - qemu_chr_add_event_handler(chr, parallel_event); | |
| 182 | 204 | return s; |
| 183 | 205 | } | ... | ... |
qemu-doc.texi
| ... | ... | @@ -364,8 +364,11 @@ Virtual console |
| 364 | 364 | @item null |
| 365 | 365 | void device |
| 366 | 366 | @item /dev/XXX |
| 367 | -[Linux only]Use host tty, e.g. @file{/dev/ttyS0}. The host serial port | |
| 367 | +[Linux only] Use host tty, e.g. @file{/dev/ttyS0}. The host serial port | |
| 368 | 368 | parameters are set according to the emulated ones. |
| 369 | +@item /dev/parportN | |
| 370 | +[Linux only, parallel port only] Use host parallel port | |
| 371 | +@var{N}. Currently only SPP parallel port features can be used. | |
| 369 | 372 | @item file:filename |
| 370 | 373 | Write output to filename. No character can be read. |
| 371 | 374 | @item stdio |
| ... | ... | @@ -379,6 +382,15 @@ non graphical mode. |
| 379 | 382 | This option can be used several times to simulate up to 4 serials |
| 380 | 383 | ports. |
| 381 | 384 | |
| 385 | +@item -parallel dev | |
| 386 | +Redirect the virtual parallel port to host device @var{dev} (same | |
| 387 | +devices as the serial port). On Linux hosts, @file{/dev/parportN} can | |
| 388 | +be used to use hardware devices connected on the corresponding host | |
| 389 | +parallel port. | |
| 390 | + | |
| 391 | +This option can be used several times to simulate up to 3 parallel | |
| 392 | +ports. | |
| 393 | + | |
| 382 | 394 | @item -monitor dev |
| 383 | 395 | Redirect the monitor to host device @var{dev} (same devices as the |
| 384 | 396 | serial port). | ... | ... |
vl.c
| ... | ... | @@ -51,6 +51,7 @@ |
| 51 | 51 | #include <pty.h> |
| 52 | 52 | #include <malloc.h> |
| 53 | 53 | #include <linux/rtc.h> |
| 54 | +#include <linux/ppdev.h> | |
| 54 | 55 | #endif |
| 55 | 56 | #endif |
| 56 | 57 | |
| ... | ... | @@ -1013,21 +1014,13 @@ int qemu_chr_write(CharDriverState *s, const uint8_t *buf, int len) |
| 1013 | 1014 | return s->chr_write(s, buf, len); |
| 1014 | 1015 | } |
| 1015 | 1016 | |
| 1016 | -void qemu_chr_set_serial_parameters(CharDriverState *s, | |
| 1017 | - int speed, int parity, | |
| 1018 | - int data_bits, int stop_bits) | |
| 1017 | +int qemu_chr_ioctl(CharDriverState *s, int cmd, void *arg) | |
| 1019 | 1018 | { |
| 1020 | - if (s->chr_set_serial_parameters) | |
| 1021 | - s->chr_set_serial_parameters(s, speed, parity, data_bits, stop_bits); | |
| 1019 | + if (!s->chr_ioctl) | |
| 1020 | + return -ENOTSUP; | |
| 1021 | + return s->chr_ioctl(s, cmd, arg); | |
| 1022 | 1022 | } |
| 1023 | 1023 | |
| 1024 | -void qemu_chr_set_serial_break(CharDriverState *s, int enable) | |
| 1025 | -{ | |
| 1026 | - if (s->chr_set_serial_break) | |
| 1027 | - s->chr_set_serial_break(s, enable); | |
| 1028 | -} | |
| 1029 | - | |
| 1030 | - | |
| 1031 | 1024 | void qemu_chr_printf(CharDriverState *s, const char *fmt, ...) |
| 1032 | 1025 | { |
| 1033 | 1026 | char buf[4096]; |
| ... | ... | @@ -1379,7 +1372,11 @@ static void tty_serial_init(int fd, int speed, |
| 1379 | 1372 | struct termios tty; |
| 1380 | 1373 | speed_t spd; |
| 1381 | 1374 | |
| 1382 | - tcgetattr (0, &tty); | |
| 1375 | +#if 0 | |
| 1376 | + printf("tty_serial_init: speed=%d parity=%c data=%d stop=%d\n", | |
| 1377 | + speed, parity, data_bits, stop_bits); | |
| 1378 | +#endif | |
| 1379 | + tcgetattr (fd, &tty); | |
| 1383 | 1380 | |
| 1384 | 1381 | switch(speed) { |
| 1385 | 1382 | case 50: |
| ... | ... | @@ -1459,20 +1456,29 @@ static void tty_serial_init(int fd, int speed, |
| 1459 | 1456 | tcsetattr (fd, TCSANOW, &tty); |
| 1460 | 1457 | } |
| 1461 | 1458 | |
| 1462 | -static void tty_set_serial_parameters(CharDriverState *chr, | |
| 1463 | - int speed, int parity, | |
| 1464 | - int data_bits, int stop_bits) | |
| 1459 | +static int tty_serial_ioctl(CharDriverState *chr, int cmd, void *arg) | |
| 1465 | 1460 | { |
| 1466 | 1461 | FDCharDriver *s = chr->opaque; |
| 1467 | - tty_serial_init(s->fd_in, speed, parity, data_bits, stop_bits); | |
| 1468 | -} | |
| 1469 | - | |
| 1470 | -static void tty_set_serial_break(CharDriverState *chr, int enable) | |
| 1471 | -{ | |
| 1472 | - FDCharDriver *s = chr->opaque; | |
| 1473 | - /* XXX: find a better solution */ | |
| 1474 | - if (enable) | |
| 1475 | - tcsendbreak(s->fd_in, 1); | |
| 1462 | + | |
| 1463 | + switch(cmd) { | |
| 1464 | + case CHR_IOCTL_SERIAL_SET_PARAMS: | |
| 1465 | + { | |
| 1466 | + QEMUSerialSetParams *ssp = arg; | |
| 1467 | + tty_serial_init(s->fd_in, ssp->speed, ssp->parity, | |
| 1468 | + ssp->data_bits, ssp->stop_bits); | |
| 1469 | + } | |
| 1470 | + break; | |
| 1471 | + case CHR_IOCTL_SERIAL_SET_BREAK: | |
| 1472 | + { | |
| 1473 | + int enable = *(int *)arg; | |
| 1474 | + if (enable) | |
| 1475 | + tcsendbreak(s->fd_in, 1); | |
| 1476 | + } | |
| 1477 | + break; | |
| 1478 | + default: | |
| 1479 | + return -ENOTSUP; | |
| 1480 | + } | |
| 1481 | + return 0; | |
| 1476 | 1482 | } |
| 1477 | 1483 | |
| 1478 | 1484 | CharDriverState *qemu_chr_open_tty(const char *filename) |
| ... | ... | @@ -1480,7 +1486,7 @@ CharDriverState *qemu_chr_open_tty(const char *filename) |
| 1480 | 1486 | CharDriverState *chr; |
| 1481 | 1487 | int fd; |
| 1482 | 1488 | |
| 1483 | - fd = open(filename, O_RDWR); | |
| 1489 | + fd = open(filename, O_RDWR | O_NONBLOCK); | |
| 1484 | 1490 | if (fd < 0) |
| 1485 | 1491 | return NULL; |
| 1486 | 1492 | fcntl(fd, F_SETFL, O_NONBLOCK); |
| ... | ... | @@ -1488,8 +1494,70 @@ CharDriverState *qemu_chr_open_tty(const char *filename) |
| 1488 | 1494 | chr = qemu_chr_open_fd(fd, fd); |
| 1489 | 1495 | if (!chr) |
| 1490 | 1496 | return NULL; |
| 1491 | - chr->chr_set_serial_parameters = tty_set_serial_parameters; | |
| 1492 | - chr->chr_set_serial_break = tty_set_serial_break; | |
| 1497 | + chr->chr_ioctl = tty_serial_ioctl; | |
| 1498 | + return chr; | |
| 1499 | +} | |
| 1500 | + | |
| 1501 | +static int pp_ioctl(CharDriverState *chr, int cmd, void *arg) | |
| 1502 | +{ | |
| 1503 | + int fd = (int)chr->opaque; | |
| 1504 | + uint8_t b; | |
| 1505 | + | |
| 1506 | + switch(cmd) { | |
| 1507 | + case CHR_IOCTL_PP_READ_DATA: | |
| 1508 | + if (ioctl(fd, PPRDATA, &b) < 0) | |
| 1509 | + return -ENOTSUP; | |
| 1510 | + *(uint8_t *)arg = b; | |
| 1511 | + break; | |
| 1512 | + case CHR_IOCTL_PP_WRITE_DATA: | |
| 1513 | + b = *(uint8_t *)arg; | |
| 1514 | + if (ioctl(fd, PPWDATA, &b) < 0) | |
| 1515 | + return -ENOTSUP; | |
| 1516 | + break; | |
| 1517 | + case CHR_IOCTL_PP_READ_CONTROL: | |
| 1518 | + if (ioctl(fd, PPRCONTROL, &b) < 0) | |
| 1519 | + return -ENOTSUP; | |
| 1520 | + *(uint8_t *)arg = b; | |
| 1521 | + break; | |
| 1522 | + case CHR_IOCTL_PP_WRITE_CONTROL: | |
| 1523 | + b = *(uint8_t *)arg; | |
| 1524 | + if (ioctl(fd, PPWCONTROL, &b) < 0) | |
| 1525 | + return -ENOTSUP; | |
| 1526 | + break; | |
| 1527 | + case CHR_IOCTL_PP_READ_STATUS: | |
| 1528 | + if (ioctl(fd, PPRSTATUS, &b) < 0) | |
| 1529 | + return -ENOTSUP; | |
| 1530 | + *(uint8_t *)arg = b; | |
| 1531 | + break; | |
| 1532 | + default: | |
| 1533 | + return -ENOTSUP; | |
| 1534 | + } | |
| 1535 | + return 0; | |
| 1536 | +} | |
| 1537 | + | |
| 1538 | +CharDriverState *qemu_chr_open_pp(const char *filename) | |
| 1539 | +{ | |
| 1540 | + CharDriverState *chr; | |
| 1541 | + int fd; | |
| 1542 | + | |
| 1543 | + fd = open(filename, O_RDWR); | |
| 1544 | + if (fd < 0) | |
| 1545 | + return NULL; | |
| 1546 | + | |
| 1547 | + if (ioctl(fd, PPCLAIM) < 0) { | |
| 1548 | + close(fd); | |
| 1549 | + return NULL; | |
| 1550 | + } | |
| 1551 | + | |
| 1552 | + chr = qemu_mallocz(sizeof(CharDriverState)); | |
| 1553 | + if (!chr) { | |
| 1554 | + close(fd); | |
| 1555 | + return NULL; | |
| 1556 | + } | |
| 1557 | + chr->opaque = (void *)fd; | |
| 1558 | + chr->chr_write = null_chr_write; | |
| 1559 | + chr->chr_add_read_handler = null_chr_add_read_handler; | |
| 1560 | + chr->chr_ioctl = pp_ioctl; | |
| 1493 | 1561 | return chr; |
| 1494 | 1562 | } |
| 1495 | 1563 | |
| ... | ... | @@ -1522,6 +1590,9 @@ CharDriverState *qemu_chr_open(const char *filename) |
| 1522 | 1590 | } else |
| 1523 | 1591 | #endif |
| 1524 | 1592 | #if defined(__linux__) |
| 1593 | + if (strstart(filename, "/dev/parport", NULL)) { | |
| 1594 | + return qemu_chr_open_pp(filename); | |
| 1595 | + } else | |
| 1525 | 1596 | if (strstart(filename, "/dev/", NULL)) { |
| 1526 | 1597 | return qemu_chr_open_tty(filename); |
| 1527 | 1598 | } else | ... | ... |