Add PLIP network device

This commit is contained in:
RichardG867
2020-10-17 01:31:53 -03:00
parent 0189ff8ea6
commit 123462f1d9
6 changed files with 542 additions and 2 deletions

View File

@@ -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*/

View File

@@ -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, <richardg867@gmail.com>
* 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*/

View File

@@ -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];

View File

@@ -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}
};
@@ -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);
else
ret = 0xdf;
break;

507
src/network/net_plip.c Normal file
View File

@@ -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, <richardg867@gmail.com>
* Copyright 2020 RichardG.
*/
#include <inttypes.h>
#include <memory.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#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
};

View File

@@ -721,7 +721,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