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 | ... | ... |