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
| @@ -8,6 +8,7 @@ version 0.7.3: | @@ -8,6 +8,7 @@ version 0.7.3: | ||
| 8 | - ES1370 PCI audio device (malc) | 8 | - ES1370 PCI audio device (malc) |
| 9 | - Initial USB support | 9 | - Initial USB support |
| 10 | - Linux host serial port access | 10 | - Linux host serial port access |
| 11 | + - Linux host low level parallel port access | ||
| 11 | 12 | ||
| 12 | version 0.7.2: | 13 | version 0.7.2: |
| 13 | 14 |
hw/parallel.c
| 1 | /* | 1 | /* |
| 2 | * QEMU Parallel PORT emulation | 2 | * QEMU Parallel PORT emulation |
| 3 | * | 3 | * |
| 4 | - * Copyright (c) 2003-2004 Fabrice Bellard | 4 | + * Copyright (c) 2003-2005 Fabrice Bellard |
| 5 | * | 5 | * |
| 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy | 6 | * 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 | 7 | * of this software and associated documentation files (the "Software"), to deal |
| @@ -50,6 +50,7 @@ struct ParallelState { | @@ -50,6 +50,7 @@ struct ParallelState { | ||
| 50 | int irq; | 50 | int irq; |
| 51 | int irq_pending; | 51 | int irq_pending; |
| 52 | CharDriverState *chr; | 52 | CharDriverState *chr; |
| 53 | + int hw_driver; | ||
| 53 | }; | 54 | }; |
| 54 | 55 | ||
| 55 | static void parallel_update_irq(ParallelState *s) | 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,29 +71,39 @@ static void parallel_ioport_write(void *opaque, uint32_t addr, uint32_t val) | ||
| 70 | #endif | 71 | #endif |
| 71 | switch(addr) { | 72 | switch(addr) { |
| 72 | case 0: | 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 | break; | 81 | break; |
| 76 | case 2: | 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 | break; | 107 | break; |
| 97 | } | 108 | } |
| 98 | } | 109 | } |
| @@ -105,24 +116,35 @@ static uint32_t parallel_ioport_read(void *opaque, uint32_t addr) | @@ -105,24 +116,35 @@ static uint32_t parallel_ioport_read(void *opaque, uint32_t addr) | ||
| 105 | addr &= 7; | 116 | addr &= 7; |
| 106 | switch(addr) { | 117 | switch(addr) { |
| 107 | case 0: | 118 | case 0: |
| 119 | + if (s->hw_driver) { | ||
| 120 | + qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_READ_DATA, &s->data); | ||
| 121 | + } | ||
| 108 | ret = s->data; | 122 | ret = s->data; |
| 109 | break; | 123 | break; |
| 110 | case 1: | 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 | break; | 143 | break; |
| 125 | case 2: | 144 | case 2: |
| 145 | + if (s->hw_driver) { | ||
| 146 | + qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_READ_CONTROL, &s->control); | ||
| 147 | + } | ||
| 126 | ret = s->control; | 148 | ret = s->control; |
| 127 | break; | 149 | break; |
| 128 | } | 150 | } |
| @@ -153,18 +175,20 @@ static void parallel_receive1(void *opaque, const uint8_t *buf, int size) | @@ -153,18 +175,20 @@ static void parallel_receive1(void *opaque, const uint8_t *buf, int size) | ||
| 153 | parallel_receive_byte(s, buf[0]); | 175 | parallel_receive_byte(s, buf[0]); |
| 154 | } | 176 | } |
| 155 | 177 | ||
| 156 | -static void parallel_event(void *opaque, int event) | ||
| 157 | -{ | ||
| 158 | -} | ||
| 159 | - | ||
| 160 | /* If fd is zero, it means that the parallel device uses the console */ | 178 | /* If fd is zero, it means that the parallel device uses the console */ |
| 161 | ParallelState *parallel_init(int base, int irq, CharDriverState *chr) | 179 | ParallelState *parallel_init(int base, int irq, CharDriverState *chr) |
| 162 | { | 180 | { |
| 163 | ParallelState *s; | 181 | ParallelState *s; |
| 182 | + uint8_t dummy; | ||
| 164 | 183 | ||
| 165 | s = qemu_mallocz(sizeof(ParallelState)); | 184 | s = qemu_mallocz(sizeof(ParallelState)); |
| 166 | if (!s) | 185 | if (!s) |
| 167 | return NULL; | 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 | s->irq = irq; | 192 | s->irq = irq; |
| 169 | s->data = 0; | 193 | s->data = 0; |
| 170 | s->status = PARA_STS_BUSY; | 194 | s->status = PARA_STS_BUSY; |
| @@ -176,8 +200,6 @@ ParallelState *parallel_init(int base, int irq, CharDriverState *chr) | @@ -176,8 +200,6 @@ ParallelState *parallel_init(int base, int irq, CharDriverState *chr) | ||
| 176 | 200 | ||
| 177 | register_ioport_write(base, 8, 1, parallel_ioport_write, s); | 201 | register_ioport_write(base, 8, 1, parallel_ioport_write, s); |
| 178 | register_ioport_read(base, 8, 1, parallel_ioport_read, s); | 202 | register_ioport_read(base, 8, 1, parallel_ioport_read, s); |
| 179 | - s->chr = chr; | ||
| 180 | qemu_chr_add_read_handler(chr, parallel_can_receive1, parallel_receive1, s); | 203 | qemu_chr_add_read_handler(chr, parallel_can_receive1, parallel_receive1, s); |
| 181 | - qemu_chr_add_event_handler(chr, parallel_event); | ||
| 182 | return s; | 204 | return s; |
| 183 | } | 205 | } |
qemu-doc.texi
| @@ -364,8 +364,11 @@ Virtual console | @@ -364,8 +364,11 @@ Virtual console | ||
| 364 | @item null | 364 | @item null |
| 365 | void device | 365 | void device |
| 366 | @item /dev/XXX | 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 | parameters are set according to the emulated ones. | 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 | @item file:filename | 372 | @item file:filename |
| 370 | Write output to filename. No character can be read. | 373 | Write output to filename. No character can be read. |
| 371 | @item stdio | 374 | @item stdio |
| @@ -379,6 +382,15 @@ non graphical mode. | @@ -379,6 +382,15 @@ non graphical mode. | ||
| 379 | This option can be used several times to simulate up to 4 serials | 382 | This option can be used several times to simulate up to 4 serials |
| 380 | ports. | 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 | @item -monitor dev | 394 | @item -monitor dev |
| 383 | Redirect the monitor to host device @var{dev} (same devices as the | 395 | Redirect the monitor to host device @var{dev} (same devices as the |
| 384 | serial port). | 396 | serial port). |
vl.c
| @@ -51,6 +51,7 @@ | @@ -51,6 +51,7 @@ | ||
| 51 | #include <pty.h> | 51 | #include <pty.h> |
| 52 | #include <malloc.h> | 52 | #include <malloc.h> |
| 53 | #include <linux/rtc.h> | 53 | #include <linux/rtc.h> |
| 54 | +#include <linux/ppdev.h> | ||
| 54 | #endif | 55 | #endif |
| 55 | #endif | 56 | #endif |
| 56 | 57 | ||
| @@ -1013,21 +1014,13 @@ int qemu_chr_write(CharDriverState *s, const uint8_t *buf, int len) | @@ -1013,21 +1014,13 @@ int qemu_chr_write(CharDriverState *s, const uint8_t *buf, int len) | ||
| 1013 | return s->chr_write(s, buf, len); | 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 | void qemu_chr_printf(CharDriverState *s, const char *fmt, ...) | 1024 | void qemu_chr_printf(CharDriverState *s, const char *fmt, ...) |
| 1032 | { | 1025 | { |
| 1033 | char buf[4096]; | 1026 | char buf[4096]; |
| @@ -1379,7 +1372,11 @@ static void tty_serial_init(int fd, int speed, | @@ -1379,7 +1372,11 @@ static void tty_serial_init(int fd, int speed, | ||
| 1379 | struct termios tty; | 1372 | struct termios tty; |
| 1380 | speed_t spd; | 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 | switch(speed) { | 1381 | switch(speed) { |
| 1385 | case 50: | 1382 | case 50: |
| @@ -1459,20 +1456,29 @@ static void tty_serial_init(int fd, int speed, | @@ -1459,20 +1456,29 @@ static void tty_serial_init(int fd, int speed, | ||
| 1459 | tcsetattr (fd, TCSANOW, &tty); | 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 | FDCharDriver *s = chr->opaque; | 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 | CharDriverState *qemu_chr_open_tty(const char *filename) | 1484 | CharDriverState *qemu_chr_open_tty(const char *filename) |
| @@ -1480,7 +1486,7 @@ CharDriverState *qemu_chr_open_tty(const char *filename) | @@ -1480,7 +1486,7 @@ CharDriverState *qemu_chr_open_tty(const char *filename) | ||
| 1480 | CharDriverState *chr; | 1486 | CharDriverState *chr; |
| 1481 | int fd; | 1487 | int fd; |
| 1482 | 1488 | ||
| 1483 | - fd = open(filename, O_RDWR); | 1489 | + fd = open(filename, O_RDWR | O_NONBLOCK); |
| 1484 | if (fd < 0) | 1490 | if (fd < 0) |
| 1485 | return NULL; | 1491 | return NULL; |
| 1486 | fcntl(fd, F_SETFL, O_NONBLOCK); | 1492 | fcntl(fd, F_SETFL, O_NONBLOCK); |
| @@ -1488,8 +1494,70 @@ CharDriverState *qemu_chr_open_tty(const char *filename) | @@ -1488,8 +1494,70 @@ CharDriverState *qemu_chr_open_tty(const char *filename) | ||
| 1488 | chr = qemu_chr_open_fd(fd, fd); | 1494 | chr = qemu_chr_open_fd(fd, fd); |
| 1489 | if (!chr) | 1495 | if (!chr) |
| 1490 | return NULL; | 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 | return chr; | 1561 | return chr; |
| 1494 | } | 1562 | } |
| 1495 | 1563 | ||
| @@ -1522,6 +1590,9 @@ CharDriverState *qemu_chr_open(const char *filename) | @@ -1522,6 +1590,9 @@ CharDriverState *qemu_chr_open(const char *filename) | ||
| 1522 | } else | 1590 | } else |
| 1523 | #endif | 1591 | #endif |
| 1524 | #if defined(__linux__) | 1592 | #if defined(__linux__) |
| 1593 | + if (strstart(filename, "/dev/parport", NULL)) { | ||
| 1594 | + return qemu_chr_open_pp(filename); | ||
| 1595 | + } else | ||
| 1525 | if (strstart(filename, "/dev/", NULL)) { | 1596 | if (strstart(filename, "/dev/", NULL)) { |
| 1526 | return qemu_chr_open_tty(filename); | 1597 | return qemu_chr_open_tty(filename); |
| 1527 | } else | 1598 | } else |