diff --git a/src/chipset/via_pipc.c b/src/chipset/via_pipc.c index 670e7d54f..b175b7ff7 100644 --- a/src/chipset/via_pipc.c +++ b/src/chipset/via_pipc.c @@ -170,8 +170,8 @@ pipc_reset_hard(void *priv) dev->ide_regs[0x34] = 0xc0; dev->ide_regs[0x3c] = 0x0e; - if (dev->local < VIA_PIPC_686A) - dev->ide_regs[0x40] = 0x08; + if (dev->local <= VIA_PIPC_586B) + dev->ide_regs[0x40] = 0x04; dev->ide_regs[0x41] = 0x02; dev->ide_regs[0x42] = 0x09; dev->ide_regs[0x43] = (dev->local >= VIA_PIPC_686A) ? 0x0a : 0x3a; @@ -513,17 +513,21 @@ pipc_write(int func, int addr, uint8_t val, void *priv) pci_set_irq_level(PCI_INTD, !(val & 1)); break; case 0x55: + pipc_log("PIPC: PCI INT%c %d\n", (dev->local >= VIA_PIPC_596A) ? 'A' : 'D', val >> 4); pci_set_irq_routing((dev->local >= VIA_PIPC_596A) ? PCI_INTA : PCI_INTD, (val & 0xf0) ? (val >> 4) : PCI_IRQ_DISABLED); if (dev->local <= VIA_PIPC_586B) pci_set_mirq_routing(PCI_MIRQ0, (val & 0x0f) ? (val & 0x0f) : PCI_IRQ_DISABLED); dev->pci_isa_regs[0x55] = val; break; case 0x56: + pipc_log("PIPC: PCI INT%c %d\n", (dev->local >= VIA_PIPC_596A) ? 'C' : 'A', val >> 4); + pipc_log("PIPC: PCI INTB %d\n", val & 0x0f); pci_set_irq_routing((dev->local >= VIA_PIPC_596A) ? PCI_INTC : PCI_INTA, (val & 0xf0) ? (val >> 4) : PCI_IRQ_DISABLED); pci_set_irq_routing(PCI_INTB, (val & 0x0f) ? (val & 0x0f) : PCI_IRQ_DISABLED); dev->pci_isa_regs[0x56] = val; break; case 0x57: + pipc_log("PIPC: PCI INT%c %d\n", (dev->local >= VIA_PIPC_596A) ? 'D' : 'C', val >> 4); pci_set_irq_routing((dev->local >= VIA_PIPC_596A) ? PCI_INTD : PCI_INTC, (val & 0xf0) ? (val >> 4) : PCI_IRQ_DISABLED); if (dev->local <= VIA_PIPC_586B) pci_set_mirq_routing(PCI_MIRQ1, (val & 0x0f) ? (val & 0x0f) : PCI_IRQ_DISABLED); @@ -656,10 +660,37 @@ pipc_write(int func, int addr, uint8_t val, void *priv) break; case 0x40: - dev->ide_regs[0x40] = val; + if (dev->local <= VIA_PIPC_586B) { + dev->ide_regs[0x40] = val & 0x03; + dev->ide_regs[0x40] |= 0x04; + } else + dev->ide_regs[0x40] = val & 0x0f; pipc_ide_handlers(dev); break; + case 0x50: case 0x52: + if (dev->local <= VIA_PIPC_586B) + dev->ide_regs[addr] = val & 0xe3; + else if (dev->local <= VIA_PIPC_596B) + dev->ide_regs[addr] = val & 0xeb; + else if (dev->local <= VIA_PIPC_686A) + dev->ide_regs[addr] = val & 0xef; + else + dev->ide_regs[addr] = val & 0xf7; + break; + case 0x51: case 0x53: + if (dev->local <= VIA_PIPC_596B) + dev->ide_regs[addr] = val & 0xe3; + else if (dev->local <= VIA_PIPC_686A) + dev->ide_regs[addr] = val & 0xe7; + else + dev->ide_regs[addr] = val & 0xf7; + break; + + case 0x61: case 0x69: + dev->ide_regs[addr] = val & 0x0f; + break; + default: dev->ide_regs[addr] = val; break; diff --git a/src/include/86box/lpt.h b/src/include/86box/lpt.h index 43a91a09a..3a2ac2959 100644 --- a/src/include/86box/lpt.h +++ b/src/include/86box/lpt.h @@ -1,3 +1,6 @@ +#ifndef EMU_LPT_H +# define EMU_LPT_H + typedef struct { const char *name; @@ -58,3 +61,5 @@ extern const lpt_device_t lpt_dac_device; extern const lpt_device_t lpt_dac_stereo_device; extern const lpt_device_t dss_device; + +#endif /*EMU_LPT_H*/ diff --git a/src/include/86box/net_plip.h b/src/include/86box/net_plip.h new file mode 100644 index 000000000..d45b80091 --- /dev/null +++ b/src/include/86box/net_plip.h @@ -0,0 +1,24 @@ +/* + * 86Box A hypervisor and IBM PC system emulator that specializes in + * running old operating systems and software designed for IBM + * PC systems and compatibles from 1981 through fairly recent + * system designs based on the PCI bus. + * + * This file is part of the 86Box distribution. + * + * Definitions for the PLIP parallel port network device. + * + * + * + * Author: RichardG, + * Copyright 2020 RichardG. + */ +#ifndef NET_PLIP_H +# define NET_PLIP_H +# include <86box/device.h> +# include <86box/lpt.h> + +extern const lpt_device_t lpt_plip_device; +extern const device_t plip_device; + +#endif /*NET_PLIP_H*/ diff --git a/src/include/86box/network.h b/src/include/86box/network.h index 37c66013e..c6bb348df 100644 --- a/src/include/86box/network.h +++ b/src/include/86box/network.h @@ -101,6 +101,7 @@ extern "C" { /* Global variables. */ extern int nic_do_log; /* config */ extern int network_ndev; +extern int network_rx_pause; extern netdev_t network_devs[32]; diff --git a/src/lpt.c b/src/lpt.c index 9d1228082..bf0cc339e 100644 --- a/src/lpt.c +++ b/src/lpt.c @@ -11,6 +11,7 @@ #include <86box/pic.h> #include <86box/sound.h> #include <86box/prt_devs.h> +#include <86box/net_plip.h> lpt_port_t lpt_ports[3]; @@ -30,6 +31,7 @@ static const struct {"Generic Text Printer", "text_prt", &lpt_prt_text_device}, {"Generic ESC/P Dot-Matrix", "dot_matrix", &lpt_prt_escp_device}, {"Generic PostScript Printer", "postscript", &lpt_prt_ps_device}, + {"PLIP Network", "plip", &lpt_plip_device}, {"", "", NULL} }; @@ -91,8 +93,8 @@ lpt_devices_close(void) for (i = 0; i < 3; i++) { dev = &lpt_ports[i]; - if (dev->dt) - dev->dt->close(dev->priv); + if (dev->dt) + dev->dt->close(dev->priv); dev->dt = NULL; } @@ -140,7 +142,7 @@ lpt_read(uint16_t port, void *priv) case 1: if (dev->dt && dev->dt->read_status) - ret = dev->dt->read_status(dev->priv) | 0x0f; + ret = dev->dt->read_status(dev->priv) | 0x07; else ret = 0xdf; break; diff --git a/src/machine/m_at_socket370.c b/src/machine/m_at_socket370.c index fc3a241dd..0848e95f8 100644 --- a/src/machine/m_at_socket370.c +++ b/src/machine/m_at_socket370.c @@ -301,7 +301,7 @@ machine_at_apas3_init(const machine_t *model) device_add(&via_apro_device); device_add(&via_vt82c586b_device); device_add(&fdc37c669_device); - device_add(&keyboard_ps2_pci_device); + device_add(&keyboard_ps2_ami_pci_device); device_add(&sst_flash_39sf020_device); spd_register(SPD_TYPE_SDRAM, 0x7, 256); diff --git a/src/machine/m_at_sockets7.c b/src/machine/m_at_sockets7.c index cd9880a7b..6bcab176a 100644 --- a/src/machine/m_at_sockets7.c +++ b/src/machine/m_at_sockets7.c @@ -122,7 +122,7 @@ machine_at_ficva503a_init(const machine_t *model) pci_init(PCI_CONFIG_TYPE_1); pci_register_slot(0x00, PCI_CARD_NORTHBRIDGE, 0, 0, 0, 0); - pci_register_slot(0x07, PCI_CARD_SOUTHBRIDGE, 0, 0, 3, 4); + pci_register_slot(0x07, PCI_CARD_SOUTHBRIDGE, 1, 2, 3, 4); pci_register_slot(0x08, PCI_CARD_NORMAL, 1, 2, 3, 4); pci_register_slot(0x09, PCI_CARD_NORMAL, 2, 3, 4, 1); pci_register_slot(0x0A, PCI_CARD_NORMAL, 3, 4, 1, 2); @@ -131,7 +131,7 @@ machine_at_ficva503a_init(const machine_t *model) device_add(&via_mvp3_device); device_add(&via_vt82c686a_device); - device_add(&keyboard_ps2_pci_device); + device_add(&keyboard_ps2_ami_pci_device); device_add(&via_vt82c686_sio_device); device_add(&sst_flash_39sf020_device); spd_register(SPD_TYPE_SDRAM, 0x7, 256); diff --git a/src/network/net_plip.c b/src/network/net_plip.c new file mode 100644 index 000000000..ca58b5824 --- /dev/null +++ b/src/network/net_plip.c @@ -0,0 +1,507 @@ +/* + * 86Box A hypervisor and IBM PC system emulator that specializes in + * running old operating systems and software designed for IBM + * PC systems and compatibles from 1981 through fairly recent + * system designs based on the PCI bus. + * + * This file is part of the 86Box distribution. + * + * Emulation of a PLIP parallel port network device. + * + * Tested against the Linux plip.c driver and the DOS plip.com + * packet driver. PLIP is not particularly fast, as it's a 4-bit + * half-duplex protocol operating over SPP. + * + * + * + * Author: RichardG, + * Copyright 2020 RichardG. + */ +#include +#include +#include +#include +#include +#include +#include +#define HAVE_STDARG_H +#include <86box/86box.h> +#include <86box/language.h> +#include <86box/lpt.h> +#include <86box/timer.h> +#include <86box/pit.h> +#include <86box/device.h> +#include <86box/network.h> +#include <86box/net_plip.h> + + +enum { + PLIP_START = 0x00, + PLIP_TX_LEN_LSB_LOW = 0x10, + PLIP_TX_LEN_LSB_HIGH, + PLIP_TX_LEN_MSB_LOW, + PLIP_TX_LEN_MSB_HIGH, + PLIP_TX_DATA_LOW, + PLIP_TX_DATA_HIGH, + PLIP_TX_CHECKSUM_LOW, + PLIP_TX_CHECKSUM_HIGH, + PLIP_RX_LEN_LSB_LOW = 0x20, + PLIP_RX_LEN_LSB_HIGH, + PLIP_RX_LEN_MSB_LOW, + PLIP_RX_LEN_MSB_HIGH, + PLIP_RX_DATA_LOW, + PLIP_RX_DATA_HIGH, + PLIP_RX_CHECKSUM_LOW, + PLIP_RX_CHECKSUM_HIGH, + PLIP_END = 0x40 +}; + +typedef struct +{ + uint8_t mac[6]; + + void *lpt; + pc_timer_t rx_timer; + pc_timer_t timeout_timer; + uint8_t status, ctrl; + + uint8_t state, ack, tx_checksum, tx_checksum_calc, *tx_pkt; + uint16_t tx_len, tx_ptr; + + uint8_t *rx_pkt, rx_checksum, rx_return_state; + uint16_t rx_len, rx_ptr; +} plip_t; + + +static void plip_receive_packet(plip_t *dev); + +plip_t *instance; + + +#ifdef ENABLE_PLIP_LOG +int plip_do_log = ENABLE_PLIP_LOG; + +static void +plip_log(uint8_t lvl, const char *fmt, ...) +{ + va_list ap; + + if (plip_do_log >= lvl) { + va_start(ap, fmt); + pclog_ex(fmt, ap); + va_end(ap); + } +} +#else +#define plip_log(lvl, fmt, ...) +#endif + + +static void +timeout_timer(void *priv) +{ + plip_t *dev = (plip_t *) priv; + + plip_log(1, "PLIP: timeout at state %d status %02X\n", dev->state, dev->status); + + /* Throw everything out the window. */ + dev->state = PLIP_START; + dev->status = 0x80; + + if (dev->tx_pkt) { + free(dev->tx_pkt); + dev->tx_pkt = NULL; + } + if (dev->rx_pkt) { + free(dev->rx_pkt); + dev->rx_pkt = NULL; + } + + timer_disable(&dev->timeout_timer); +} + + +static void +plip_write_data(uint8_t val, void *priv) +{ + plip_t *dev = (plip_t *) priv; + + plip_log(3, "PLIP: write_data(%02X)\n", val); + + switch (dev->state) { + case PLIP_START: + if (val == 0x08) { /* D3/ACK wakes us up */ + plip_log(2, "PLIP: ACK wakeup\n"); + dev->state = PLIP_TX_LEN_LSB_LOW; + dev->status = 0x08; + break; + } + return; + + case PLIP_TX_LEN_LSB_LOW: + if (!(val & 0x10)) + return; /* D4/BUSY not high yet */ + dev->tx_len = val & 0xf; + plip_log(2, "PLIP: tx_len = %04X (1/4)\n", dev->tx_len); + dev->state = PLIP_TX_LEN_LSB_HIGH; + dev->status &= ~0x88; + break; + + case PLIP_TX_LEN_LSB_HIGH: + if (val & 0x10) + return; /* D4/BUSY not low yet */ + dev->tx_len |= (val & 0xf) << 4; + plip_log(2, "PLIP: tx_len = %04X (2/4)\n", dev->tx_len); + dev->state = PLIP_TX_LEN_MSB_LOW; + dev->status |= 0x80; + break; + + case PLIP_TX_LEN_MSB_LOW: + if (!(val & 0x10)) + return; /* D4/BUSY not high yet */ + dev->tx_len |= (val & 0xf) << 8; + plip_log(2, "PLIP: tx_len = %04X (3/4)\n", dev->tx_len); + dev->state = PLIP_TX_LEN_MSB_HIGH; + dev->status &= ~0x80; + break; + + case PLIP_TX_LEN_MSB_HIGH: + if (val & 0x10) + return; /* D4/BUSY not low yet */ + dev->tx_len |= (val & 0xf) << 12; + plip_log(2, "PLIP: tx_len = %04X (4/4)\n", dev->tx_len); + + /* We have the length, allocate a packet. */ + if (!(dev->tx_pkt = malloc(dev->tx_len))) /* unlikely */ + fatal("PLIP: unable to allocate tx_pkt\n"); + dev->tx_ptr = 0; + dev->tx_checksum_calc = 0; + + dev->state = PLIP_TX_DATA_LOW; + dev->status |= 0x80; + break; + + case PLIP_TX_DATA_LOW: + if (!(val & 0x10)) + return; /* D4/BUSY not high yet */ + dev->tx_pkt[dev->tx_ptr] = val & 0x0f; + plip_log(2, "PLIP: tx_pkt[%d] = %02X (1/2)\n", dev->tx_ptr, dev->tx_pkt[dev->tx_ptr]); + dev->state = PLIP_TX_DATA_HIGH; + dev->status &= ~0x80; + break; + + case PLIP_TX_DATA_HIGH: + if (val & 0x10) + return; /* D4/BUSY not low yet */ + dev->tx_pkt[dev->tx_ptr] |= (val & 0x0f) << 4; + plip_log(2, "PLIP: tx_pkt[%d] = %02X (2/2)\n", dev->tx_ptr, dev->tx_pkt[dev->tx_ptr]); + dev->tx_checksum_calc += dev->tx_pkt[dev->tx_ptr++]; + + /* Are we done yet? */ + if (dev->tx_ptr < dev->tx_len) /* no, receive another byte */ + dev->state = PLIP_TX_DATA_LOW; + else /* yes, move on to checksum */ + dev->state = PLIP_TX_CHECKSUM_LOW; + dev->status |= 0x80; + break; + + case PLIP_TX_CHECKSUM_LOW: + if (!(val & 0x10)) + return; /* D4/BUSY not high yet */ + dev->tx_checksum = val & 0x0f; + plip_log(2, "PLIP: tx_checksum = %02X (1/2)\n", dev->tx_checksum); + dev->state = PLIP_TX_CHECKSUM_HIGH; + dev->status &= ~0x80; + break; + + case PLIP_TX_CHECKSUM_HIGH: + if (val & 0x10) + return; /* D4/BUSY not low yet */ + dev->tx_checksum |= (val & 0x0f) << 4; + plip_log(2, "PLIP: tx_checksum = %02X (2/2)\n", dev->tx_checksum); + + /* Verify checksum. */ + if (dev->tx_checksum_calc == dev->tx_checksum) { + /* Make sure we know the other end's MAC address. */ + memcpy(dev->mac, dev->tx_pkt + 6, 6); + + /* Transmit packet. */ + plip_log(2, "PLIP: transmitting %d-byte packet\n", dev->tx_len); + network_tx(dev->tx_pkt, dev->tx_len); + } else { + plip_log(1, "PLIP: checksum error: expected %02X, got %02X\n", dev->tx_checksum_calc, dev->tx_checksum); + } + + /* We're done with this packet. */ + free(dev->tx_pkt); + dev->tx_pkt = NULL; + dev->tx_len = 0; + + dev->state = PLIP_END; + dev->status |= 0x80; + break; + + case PLIP_RX_LEN_LSB_LOW: + if (!(val & 0x01)) + return; /* D3/ACK not high yet */ + plip_log(2, "PLIP: rx_len = %04X (1/4)\n", dev->rx_len); + dev->status = (dev->rx_len & 0x0f) << 3; + dev->state = PLIP_RX_LEN_LSB_HIGH; + break; + + case PLIP_RX_LEN_LSB_HIGH: + if (!(val & 0x10)) + return; /* D4/BUSY not high yet */ + plip_log(2, "PLIP: rx_len = %04X (2/4)\n", dev->rx_len); + dev->status = ((dev->rx_len >> 4) & 0x0f) << 3; + dev->status |= 0x80; + dev->state = PLIP_RX_LEN_MSB_LOW; + break; + + case PLIP_RX_LEN_MSB_LOW: + if (val & 0x10) + return; /* D4/BUSY not low yet */ + plip_log(2, "PLIP: rx_len = %04X (3/4)\n", dev->rx_len); + dev->status = ((dev->rx_len >> 8) & 0x0f) << 3; + dev->state = PLIP_RX_LEN_MSB_HIGH; + break; + + case PLIP_RX_LEN_MSB_HIGH: + if (!(val & 0x10)) + return; /* D4/BUSY not high yet */ + plip_log(2, "PLIP: rx_len = %04X (4/4)\n", dev->rx_len); + dev->status = ((dev->rx_len >> 12) & 0x0f) << 3; + dev->status |= 0x80; + + dev->rx_ptr = 0; + dev->rx_checksum = 0; + dev->state = PLIP_RX_DATA_LOW; + break; + + case PLIP_RX_DATA_LOW: + if (val & 0x10) + return; /* D4/BUSY not low yet */ + plip_log(2, "PLIP: rx_pkt[%d] = %02X (1/2)\n", dev->rx_ptr, dev->rx_pkt[dev->rx_ptr]); + dev->status = (dev->rx_pkt[dev->rx_ptr] & 0x0f) << 3; + dev->state = PLIP_RX_DATA_HIGH; + break; + + case PLIP_RX_DATA_HIGH: + if (!(val & 0x10)) + return; /* D4/BUSY not high yet */ + plip_log(2, "PLIP: rx_pkt[%d] = %02X (2/2)\n", dev->rx_ptr, dev->rx_pkt[dev->rx_ptr]); + dev->status = ((dev->rx_pkt[dev->rx_ptr] >> 4) & 0x0f) << 3; + dev->status |= 0x80; + dev->rx_checksum += dev->rx_pkt[dev->rx_ptr++]; + + /* Are we done yet? */ + if (dev->rx_ptr < dev->rx_len) /* no, send another byte */ + dev->state = PLIP_RX_DATA_LOW; + else /* yes, move on to checksum */ + dev->state = PLIP_RX_CHECKSUM_LOW; + break; + + case PLIP_RX_CHECKSUM_LOW: + if (val & 0x10) + return; /* D4/BUSY not low yet */ + plip_log(2, "PLIP: rx_checksum = %02X (1/2)\n", dev->rx_checksum); + dev->status = (dev->rx_checksum & 0x0f) << 3; + dev->state = PLIP_RX_CHECKSUM_HIGH; + break; + + case PLIP_RX_CHECKSUM_HIGH: + if (!(val & 0x10)) + return; /* D4/BUSY not high yet */ + plip_log(2, "PLIP: rx_checksum = %02X (2/2)\n", dev->rx_checksum); + dev->status = ((dev->rx_checksum >> 4) & 0x0f) << 3; + dev->status |= 0x80; + + /* We're done with this packet. */ + free(dev->rx_pkt); + dev->rx_pkt = NULL; + dev->rx_len = 0; + + dev->state = PLIP_END; + break; + + case PLIP_END: + if (val == 0x00) { /* written after TX or RX is done */ + plip_log(2, "PLIP: end\n"); + dev->status = 0x80; + dev->state = PLIP_START; + + timer_set_delay_u64(&dev->rx_timer, ISACONST); /* for DOS */ + } + + /* Disengage timeout timer. */ + timer_disable(&dev->timeout_timer); + return; + } + + /* Engage timeout timer unless otherwise specified. */ + timer_set_delay_u64(&dev->timeout_timer, 1000000 * TIMER_USEC); +} + + +static void +plip_write_ctrl(uint8_t val, void *priv) +{ + plip_t *dev = (plip_t *) priv; + + plip_log(3, "PLIP: write_ctrl(%02X)\n", val); + + dev->ctrl = val; + + if (val & 0x10) /* for Linux */ + timer_set_delay_u64(&dev->rx_timer, ISACONST); +} + + +static uint8_t +plip_read_status(void *priv) +{ + plip_t *dev = (plip_t *) priv; + + plip_log(3, "PLIP: read_status() = %02X\n", dev->status); + + return dev->status; +} + + +static void +plip_receive_packet(plip_t *dev) +{ + /* At least the Linux driver supports being interrupted + in the PLIP_TX_LEN_LSB_LOW state, but let's be safe. */ + if (dev->state > PLIP_START) { + plip_log(3, "PLIP: cannot receive, operation already in progress\n"); + return; + } + + if (!dev->rx_pkt || !dev->rx_len) { /* unpause RX queue if there's no packet to receive */ + network_rx_pause = 0; + return; + } + + if (!(dev->ctrl & 0x10)) { /* checking this is essential to avoid collisions */ + plip_log(3, "PLIP: cannot receive, interrupts are off\n"); + return; + } + + plip_log(2, "PLIP: receiving %d-byte packet\n", dev->rx_len); + + /* Set up to receive a packet. */ + dev->status = 0xc7; /* DOS expects exactly 0xc7, while Linux masks the 7 off */ + dev->state = PLIP_RX_LEN_LSB_LOW; + + /* Engage timeout timer. */ + timer_set_delay_u64(&dev->timeout_timer, 1000000 * TIMER_USEC); + + /* Wake the other end up. */ + lpt_irq(dev->lpt, 1); +} + + +/* This timer defers a call to plip_receive_packet to + the next ISA clock, in order to avoid IRQ weirdness. */ +static void +rx_timer(void *priv) +{ + plip_t *dev = (plip_t *) priv; + + plip_receive_packet(dev); + + timer_disable(&dev->rx_timer); +} + + +static void +plip_rx(void *priv, uint8_t *buf, int io_len) +{ + plip_t *dev = (plip_t *) priv; + + plip_log(2, "PLIP: incoming %d-byte packet\n", io_len); + + if (dev->rx_pkt) { /* shouldn't really happen with the RX queue paused */ + plip_log(3, "PLIP: already have a packet to receive"); + return; + } + + if (!(dev->rx_pkt = malloc(io_len))) /* unlikely */ + fatal("PLIP: unable to allocate rx_pkt\n"); + + network_rx_pause = 1; /* make sure we don't get any more packets while processing this one */ + + /* Copy this packet to our buffer. */ + dev->rx_len = io_len; + memcpy(dev->rx_pkt, buf, dev->rx_len); + + /* Dispatch this packet immediately if we're doing nothing. */ + plip_receive_packet(dev); +} + + +static void * +plip_lpt_init(void *lpt) +{ + plip_t *dev = (plip_t *) malloc(sizeof(plip_t)); + memset(dev, 0, sizeof(plip_t)); + + plip_log(1, "PLIP: lpt_init()\n"); + + dev->lpt = lpt; + memset(dev->mac, 0xfc, 6); /* static MAC used by Linux; just a placeholder */ + + dev->status = 0x80; + + timer_add(&dev->rx_timer, rx_timer, dev, 0); + timer_add(&dev->timeout_timer, timeout_timer, dev, 0); + + instance = dev; + + return dev; +} + + +static void * +plip_net_init(const device_t *info) +{ + plip_log(1, "PLIP: net_init()"); + + if (!instance) { + plip_log(1, " (not attached to LPT)\n"); + return NULL; + } + + plip_log(1, " (attached to LPT)\n"); + network_attach(instance, instance->mac, plip_rx, NULL, NULL); + + return instance; +} + + +static void +plip_close(void *priv) +{ + free(priv); +} + + +const lpt_device_t lpt_plip_device = { + .name = "Parallel Line Internet Protocol (LPT)", + .init = plip_lpt_init, + .close = plip_close, + .write_data = plip_write_data, + .write_ctrl = plip_write_ctrl, + .read_data = NULL, + .read_status = plip_read_status, + .read_ctrl = NULL +}; + +const device_t plip_device = +{ + "Parallel Line Internet Protocol (Network)", + 0, 0, + plip_net_init, NULL, + NULL, NULL, NULL, NULL +}; diff --git a/src/network/net_slirp.c b/src/network/net_slirp.c index e3899accb..1c743d674 100644 --- a/src/network/net_slirp.c +++ b/src/network/net_slirp.c @@ -36,8 +36,8 @@ #include <86box/config.h> -/* SLiRP can use poll() or select() for socket polling. While poll() is - better, it's slow and limited on Windows, so use select() there. */ +/* SLiRP can use poll() or select() for socket polling. + poll() is best on *nix but slow and limited on Windows. */ #ifndef _WIN32 # define SLIRP_USE_POLL 1 #endif @@ -93,7 +93,7 @@ slirp_log(const char *fmt, ...) static void net_slirp_guest_error(const char *msg, void *opaque) { - slirp_log("SLiRP: guest_error: %s\n", msg); + slirp_log("SLiRP: guest_error(): %s\n", msg); } @@ -156,7 +156,7 @@ net_slirp_send_packet(const void *qp, size_t pkt_len, void *opaque) slirp_t *slirp = (slirp_t *) opaque; uint8_t *mac = slirp->mac; uint32_t mac_cmp32[2]; - uint16_t mac_cmp16[2]; + uint16_t mac_cmp16[2]; if (!(slirp->card->set_link_state && slirp->card->set_link_state(slirp->card->priv)) && !(slirp->card->wait && slirp->card->wait(slirp->card->priv))) { slirp_log("SLiRP: received %d-byte packet\n", pkt_len); @@ -252,7 +252,7 @@ net_slirp_get_revents(int idx, void *opaque) static void slirp_tic(slirp_t *slirp) { - int ret2; + int ret; uint32_t tmo; /* Let SLiRP create a list of all open sockets. */ @@ -269,7 +269,7 @@ slirp_tic(slirp_t *slirp) /* Now wait for something to happen, or at most 'tmo' usec. */ #ifdef SLIRP_USE_POLL - ret2 = poll(slirp->pfd, slirp->pfd_len, tmo); + ret = poll(slirp->pfd, slirp->pfd_len, tmo); #else if (tmo < 0) tmo = 500; @@ -278,11 +278,11 @@ slirp_tic(slirp_t *slirp) tv.tv_sec = 0; tv.tv_usec = tmo; - ret2 = select(slirp->nfds + 1, &slirp->rfds, &slirp->wfds, &slirp->xfds, &tv); + ret = select(slirp->nfds + 1, &slirp->rfds, &slirp->wfds, &slirp->xfds, &tv); #endif /* If something happened, let SLiRP handle it. */ - slirp_pollfds_poll(slirp->slirp, (ret2 <= 0), net_slirp_get_revents, slirp); + slirp_pollfds_poll(slirp->slirp, (ret <= 0), net_slirp_get_revents, slirp); } @@ -316,7 +316,7 @@ poll_thread(void *arg) struct in_addr dhcp = { .s_addr = htonl(0x0a00020f) }; /* 10.0.2.15 */ struct in_addr dns = { .s_addr = htonl(0x0a000203) }; /* 10.0.2.3 */ struct in_addr bind = { .s_addr = htonl(0x00000000) }; /* 0.0.0.0 */ - struct in6_addr ipv6dummy; /* contents don't matter; we're not doing IPv6 */ + struct in6_addr ipv6dummy; /* contents don't matter; we're not using IPv6 */ /* Initialize SLiRP. */ slirp->slirp = slirp_init(0, 1, net, mask, host, 0, ipv6dummy, 0, ipv6dummy, NULL, NULL, NULL, NULL, dhcp, dns, ipv6dummy, NULL, NULL, &slirp_cb, arg); @@ -326,23 +326,27 @@ poll_thread(void *arg) } /* Set up port forwarding. */ - int udp, from, to, i = 0; + int udp, external, internal, i = 0; char *category = "SLiRP Port Forwarding"; - char key[16]; + char key[20]; while (1) { - sprintf(key, "%d_udp", i); - udp = config_get_int(category, key, 0); - sprintf(key, "%d_from", i); - from = config_get_int(category, key, 0); - if (from < 1) + sprintf(key, "%d_protocol", i); + udp = strncmp(config_get_string(category, key, "tcp"), "udp", -1) == 0; + sprintf(key, "%d_external", i); + external = config_get_int(category, key, 0); + sprintf(key, "%d_internal", i); + internal = config_get_int(category, key, 0); + if ((external <= 0) && (internal <= 0)) break; - sprintf(key, "%d_to", i); - to = config_get_int(category, key, from); + else if (internal <= 0) + internal = external; + else if (external <= 0) + external = internal; - if (slirp_add_hostfwd(slirp->slirp, udp, bind, from, dhcp, to) == 0) - pclog("SLiRP: Forwarded %s port host:%d to guest:%d\n", udp ? "UDP" : "TCP", from, to); + if (slirp_add_hostfwd(slirp->slirp, udp, bind, external, dhcp, internal) == 0) + pclog("SLiRP: Forwarded %s port external:%d to internal:%d\n", udp ? "UDP" : "TCP", external, internal); else - pclog("SLiRP: Failed to forward %s port host:%d to guest:%d\n", udp ? "UDP" : "TCP", from, to); + pclog("SLiRP: Failed to forward %s port external:%d to internal:%d\n", udp ? "UDP" : "TCP", external, internal); i++; } diff --git a/src/network/network.c b/src/network/network.c index d232c3650..1e7dfa308 100644 --- a/src/network/network.c +++ b/src/network/network.c @@ -54,6 +54,7 @@ #include #include #include +#include #define HAVE_STDARG_H #include <86box/86box.h> #include <86box/device.h> @@ -64,6 +65,7 @@ #include <86box/net_3c503.h> #include <86box/net_ne2000.h> #include <86box/net_pcnet.h> +#include <86box/net_plip.h> #include <86box/net_wd8003.h> @@ -88,6 +90,8 @@ static netcard_t net_cards[] = { NULL }, { "[ISA] Western Digital WD8013EBT","wd8013ebt", &wd8013ebt_device, NULL }, + { "[LPT] PLIP", "plip", &plip_device, + NULL }, { "[MCA] NetWorth Ethernet/MC", "ethernextmc", ðernext_mc_device, NULL }, { "[MCA] Western Digital WD8003ET/A","wd8003eta", &wd8003eta_device, @@ -113,6 +117,7 @@ int network_ndev; int network_card; char network_host[522]; netdev_t network_devs[32]; +int network_rx_pause = 0; #ifdef ENABLE_NIC_LOG int nic_do_log = ENABLE_NIC_LOG; #endif @@ -140,6 +145,7 @@ static struct { #ifdef ENABLE_NETWORK_LOG int network_do_log = ENABLE_NETWORK_LOG; +FILE *network_dump = NULL; static void @@ -153,8 +159,29 @@ network_log(const char *fmt, ...) va_end(ap); } } + + +static void +network_dump_packet(netpkt_t *pkt) +{ + if (!network_dump) + return; + + struct timeval tv; + gettimeofday(&tv, NULL); + struct { + uint32_t ts_sec, ts_usec, incl_len, orig_len; + } pcap_packet_hdr = { + tv.tv_sec, tv.tv_usec, pkt->len, pkt->len + }; + + fwrite(&pcap_packet_hdr, sizeof(pcap_packet_hdr), 1, network_dump); + fwrite(pkt->data, pkt->len, 1, network_dump); + fflush(network_dump); +} #else #define network_log(fmt, ...) +#define network_dump_packet(pkt) #endif @@ -220,6 +247,25 @@ network_init(void) i = net_pcap_prepare(&network_devs[network_ndev]); if (i > 0) network_ndev += i; + +#ifdef ENABLE_NETWORK_LOG + /* Start packet dump. */ + network_dump = fopen("network.pcap", "wb"); + + struct { + uint32_t magic_number; + uint16_t version_major, version_minor; + int32_t thiszone; + uint32_t sigfigs, snaplen, network; + } pcap_hdr = { + 0xa1b2c3d4, + 2, 4, + 0, + 0, 65535, 1 + }; + fwrite(&pcap_hdr, sizeof(pcap_hdr), 1, network_dump); + fflush(network_dump); +#endif } @@ -294,12 +340,18 @@ network_queue_clear(int tx) static void network_rx_queue(void *priv) { + if (network_rx_pause) { + timer_on_auto(&network_rx_queue_timer, 0.762939453125 * 2.0 * 128.0); + return; + } + netpkt_t *pkt = NULL; network_busy(1); network_queue_get(0, &pkt); if ((pkt != NULL) && (pkt->len > 0)) { + network_dump_packet(pkt); net_cards[network_card].rx(pkt->priv, pkt->data, pkt->len); if (pkt->len >= 128) timer_on_auto(&network_rx_queue_timer, 0.762939453125 * 2.0 * ((double) pkt->len)); @@ -404,6 +456,7 @@ network_close(void) /* Here is where we clear the queues. */ network_queue_clear(0); network_queue_clear(1); + network_rx_pause = 0; network_log("NETWORK: closed.\n"); } @@ -501,6 +554,7 @@ network_do_tx(void) network_queue_get(1, &pkt); if ((pkt != NULL) && (pkt->len > 0)) { + network_dump_packet(pkt); switch(network_type) { case NET_TYPE_PCAP: net_pcap_in(pkt->data, pkt->len); diff --git a/src/win/Makefile.mingw b/src/win/Makefile.mingw index 67b2a38aa..74b8c42ad 100644 --- a/src/win/Makefile.mingw +++ b/src/win/Makefile.mingw @@ -744,7 +744,8 @@ NETOBJ := network.o \ tcp_output.o tcp_subr.o tcp_timer.o udp.o util.o version.o \ net_dp8390.o \ net_3c503.o net_ne2000.o \ - net_pcnet.o net_wd8003.o + net_pcnet.o net_wd8003.o \ + net_plip.o PRINTOBJ := png.o prt_cpmap.o \ prt_escp.o prt_text.o prt_ps.o