Network overhaul : support for multiple NICs, performance improvement

- Add support for multiple NICs
- Switch from polling to an event loop for the host networking to avoid latency and locking issues
This commit is contained in:
Adrien Moulin
2022-08-21 16:55:47 +02:00
parent 5ae9df6d07
commit 19db1d2c7b
21 changed files with 1557 additions and 1075 deletions

View File

@@ -60,7 +60,6 @@
#include <86box/device.h> #include <86box/device.h>
#include <86box/pit.h> #include <86box/pit.h>
#include <86box/random.h> #include <86box/random.h>
#include <86box/timer.h>
#include <86box/nvr.h> #include <86box/nvr.h>
#include <86box/machine.h> #include <86box/machine.h>
#include <86box/bugger.h> #include <86box/bugger.h>
@@ -85,6 +84,7 @@
#include <86box/mo.h> #include <86box/mo.h>
#include <86box/scsi_disk.h> #include <86box/scsi_disk.h>
#include <86box/cdrom_image.h> #include <86box/cdrom_image.h>
#include <86box/thread.h>
#include <86box/network.h> #include <86box/network.h>
#include <86box/sound.h> #include <86box/sound.h>
#include <86box/midi.h> #include <86box/midi.h>
@@ -93,7 +93,6 @@
#include <86box/ui.h> #include <86box/ui.h>
#include <86box/path.h> #include <86box/path.h>
#include <86box/plat.h> #include <86box/plat.h>
#include <86box/thread.h>
#include <86box/version.h> #include <86box/version.h>
#include <86box/gdbstub.h> #include <86box/gdbstub.h>
#include <86box/machine_status.h> #include <86box/machine_status.h>
@@ -951,8 +950,6 @@ pc_reset_hard_close(void)
/* Close all the memory mappings. */ /* Close all the memory mappings. */
mem_close(); mem_close();
network_timer_stop();
/* Turn off timer processing to avoid potential segmentation faults. */ /* Turn off timer processing to avoid potential segmentation faults. */
timer_close(); timer_close();
@@ -1173,8 +1170,6 @@ pc_close(thread_t *ptr)
/* Close all the memory mappings. */ /* Close all the memory mappings. */
mem_close(); mem_close();
network_timer_stop();
/* Turn off timer processing to avoid potential segmentation faults. */ /* Turn off timer processing to avoid potential segmentation faults. */
timer_close(); timer_close();

View File

@@ -55,6 +55,7 @@
#include <86box/gameport.h> #include <86box/gameport.h>
#include <86box/machine.h> #include <86box/machine.h>
#include <86box/mouse.h> #include <86box/mouse.h>
#include <86box/thread.h>
#include <86box/network.h> #include <86box/network.h>
#include <86box/scsi.h> #include <86box/scsi.h>
#include <86box/scsi_device.h> #include <86box/scsi_device.h>
@@ -1131,45 +1132,89 @@ load_network(void)
{ {
char *cat = "Network"; char *cat = "Network";
char *p; char *p;
char temp[512];
int c = 0, min = 0;
/* Handle legacy configuration which supported only one NIC */
p = config_get_string(cat, "net_card", NULL);
if (p != NULL) {
net_cards_conf[c].device_num = network_card_get_from_internal_name(p);
p = config_get_string(cat, "net_type", NULL); p = config_get_string(cat, "net_type", NULL);
if (p != NULL) { if (p != NULL) {
if (!strcmp(p, "pcap") || !strcmp(p, "1")) if (!strcmp(p, "pcap") || !strcmp(p, "1"))
network_type = NET_TYPE_PCAP; net_cards_conf[c].net_type = NET_TYPE_PCAP;
else if (!strcmp(p, "slirp") || !strcmp(p, "2")) else if (!strcmp(p, "slirp") || !strcmp(p, "2"))
network_type = NET_TYPE_SLIRP; net_cards_conf[c].net_type = NET_TYPE_SLIRP;
else else
network_type = NET_TYPE_NONE; net_cards_conf[c].net_type = NET_TYPE_NONE;
} else } else {
network_type = NET_TYPE_NONE; net_cards_conf[c].net_type = NET_TYPE_NONE;
memset(network_host, '\0', sizeof(network_host));
p = config_get_string(cat, "net_host_device", NULL);
if (p == NULL) {
p = config_get_string(cat, "net_host_device", NULL);
if (p != NULL)
config_delete_var(cat, "net_host_device");
} }
p = config_get_string(cat, "net_host_device", NULL);
if (p != NULL) { if (p != NULL) {
if ((network_dev_to_id(p) == -1) || (network_ndev == 1)) { if ((network_dev_to_id(p) == -1) || (network_ndev == 1)) {
if ((network_ndev == 1) && strcmp(network_host, "none")) { if (network_ndev == 1) {
ui_msgbox_header(MBX_ERROR, (wchar_t *) IDS_2094, (wchar_t *) IDS_2129); ui_msgbox_header(MBX_ERROR, (wchar_t *) IDS_2094, (wchar_t *) IDS_2129);
} else if (network_dev_to_id(p) == -1) { } else if (network_dev_to_id(p) == -1) {
ui_msgbox_header(MBX_ERROR, (wchar_t *) IDS_2095, (wchar_t *) IDS_2129); ui_msgbox_header(MBX_ERROR, (wchar_t *) IDS_2095, (wchar_t *) IDS_2129);
} }
strcpy(net_cards_conf[c].host_dev_name, "none");
strcpy(network_host, "none");
} else { } else {
strncpy(network_host, p, sizeof(network_host) - 1); strncpy(net_cards_conf[c].host_dev_name, p, sizeof(net_cards_conf[c].host_dev_name) - 1);
}
} else {
strcpy(net_cards_conf[c].host_dev_name, "none");
} }
} else
strcpy(network_host, "none");
p = config_get_string(cat, "net_card", NULL); min++;
if (p != NULL) }
network_card = network_card_get_from_internal_name(p);
else config_delete_var(cat, "net_card");
network_card = 0; config_delete_var(cat, "net_type");
config_delete_var(cat, "net_host_device");
for (c = min; c < NET_CARD_MAX; c++) {
sprintf(temp, "net_%02i_card", c + 1);
p = config_get_string(cat, temp, NULL);
if (p != NULL) {
net_cards_conf[c].device_num = network_card_get_from_internal_name(p);
} else {
net_cards_conf[c].device_num = 0;
}
sprintf(temp, "net_%02i_net_type", c + 1);
p = config_get_string(cat, temp, NULL);
if (p != NULL) {
if (!strcmp(p, "pcap") || !strcmp(p, "1")) {
net_cards_conf[c].net_type = NET_TYPE_PCAP;
} else if (!strcmp(p, "slirp") || !strcmp(p, "2")) {
net_cards_conf[c].net_type = NET_TYPE_SLIRP;
} else {
net_cards_conf[c].net_type = NET_TYPE_NONE;
}
} else {
net_cards_conf[c].net_type = NET_TYPE_NONE;
}
sprintf(temp, "net_%02i_host_device", c + 1);
p = config_get_string(cat, temp, NULL);
if (p != NULL) {
if ((network_dev_to_id(p) == -1) || (network_ndev == 1)) {
if (network_ndev == 1) {
ui_msgbox_header(MBX_ERROR, (wchar_t *) IDS_2094, (wchar_t *) IDS_2129);
} else if (network_dev_to_id(p) == -1) {
ui_msgbox_header(MBX_ERROR, (wchar_t *) IDS_2095, (wchar_t *) IDS_2129);
}
strcpy(net_cards_conf[c].host_dev_name, "none");
} else {
strncpy(net_cards_conf[c].host_dev_name, p, sizeof(net_cards_conf[c].host_dev_name) - 1);
}
} else {
strcpy(net_cards_conf[c].host_dev_name, "none");
}
}
} }
/* Load "Ports" section. */ /* Load "Ports" section. */
@@ -2688,29 +2733,41 @@ save_sound(void)
static void static void
save_network(void) save_network(void)
{ {
int c = 0;
char temp[512];
char *cat = "Network"; char *cat = "Network";
if (network_type == NET_TYPE_NONE)
config_delete_var(cat, "net_type"); config_delete_var(cat, "net_type");
else config_delete_var(cat, "net_host_device");
config_set_string(cat, "net_type", config_delete_var(cat, "net_card");
(network_type == NET_TYPE_SLIRP) ? "slirp" : "pcap");
if (network_host[0] != '\0') { for (c = 0; c < NET_CARD_MAX; c++) {
if (!strcmp(network_host, "none")) sprintf(temp, "net_%02i_card", c + 1);
config_delete_var(cat, "net_host_device"); if (net_cards_conf[c].device_num == 0) {
else config_delete_var(cat, temp);
config_set_string(cat, "net_host_device", network_host);
} else { } else {
/* config_set_string(cat, "net_host_device", "none"); */ config_set_string(cat, temp, network_card_get_internal_name(net_cards_conf[c].device_num));
config_delete_var(cat, "net_host_device");
} }
if (network_card == 0) sprintf(temp, "net_%02i_net_type", c + 1);
config_delete_var(cat, "net_card"); if (net_cards_conf[c].net_type == NET_TYPE_NONE) {
config_delete_var(cat, temp);
} else {
config_set_string(cat, temp,
(net_cards_conf[c].net_type == NET_TYPE_SLIRP) ? "slirp" : "pcap");
}
sprintf(temp, "net_%02i_host_device", c + 1);
if (net_cards_conf[c].host_dev_name[0] != '\0') {
if (!strcmp(net_cards_conf[c].host_dev_name, "none"))
config_delete_var(cat, temp);
else else
config_set_string(cat, "net_card", config_set_string(cat, temp, net_cards_conf[c].host_dev_name);
network_card_get_internal_name(network_card)); } else {
/* config_set_string(cat, temp, "none"); */
config_delete_var(cat, temp);
}
}
delete_section_if_empty(cat); delete_section_if_empty(cat);
} }

View File

@@ -184,11 +184,13 @@ typedef struct {
int tx_timer_active; int tx_timer_active;
void *priv; void *priv;
netcard_t *card;
void (*interrupt)(void *priv, int set); void (*interrupt)(void *priv, int set);
} dp8390_t; } dp8390_t;
extern const device_t dp8390_device; extern const device_t dp8390_device;
extern int dp3890_inst;
extern uint32_t dp8390_chipmem_read(dp8390_t *dev, uint32_t addr, unsigned int len); extern uint32_t dp8390_chipmem_read(dp8390_t *dev, uint32_t addr, unsigned int len);

View File

@@ -0,0 +1,22 @@
#ifndef EMU_NET_EVENT_H
#define EMU_NET_EVENT_H
typedef struct {
#ifdef _WIN32
HANDLE handle;
#else
int fds[2];
#endif
} net_evt_t;
extern void net_event_init(net_evt_t *event);
extern void net_event_set(net_evt_t *event);
extern void net_event_clear(net_evt_t *event);
extern void net_event_close(net_evt_t *event);
#ifdef _WIN32
extern HANDLE net_event_get_handle(net_evt_t *event);
#else
extern int net_event_get_fd(net_evt_t *event);
#endif
#endif

View File

@@ -52,8 +52,12 @@
/* Network provider types. */ /* Network provider types. */
#define NET_TYPE_NONE 0 /* networking disabled */ #define NET_TYPE_NONE 0 /* networking disabled */
#define NET_TYPE_PCAP 1 /* use the (Win)Pcap API */ #define NET_TYPE_SLIRP 1 /* use the SLiRP port forwarder */
#define NET_TYPE_SLIRP 2 /* use the SLiRP port forwarder */ #define NET_TYPE_PCAP 2 /* use the (Win)Pcap API */
#define NET_MAX_FRAME 1518
#define NET_QUEUE_LEN 8
#define NET_CARD_MAX 4
/* Supported network cards. */ /* Supported network cards. */
enum { enum {
@@ -64,6 +68,20 @@ enum {
RTL8029AS RTL8029AS
}; };
enum {
NET_QUEUE_RX,
NET_QUEUE_TX_VM,
NET_QUEUE_TX_HOST
};
typedef struct {
int device_num;
int net_type;
char host_dev_name[128];
} netcard_conf_t;
extern netcard_conf_t net_cards_conf[NET_CARD_MAX];
extern int net_card_current;
typedef int (*NETRXCB)(void *, uint8_t *, int); typedef int (*NETRXCB)(void *, uint8_t *, int);
typedef int (*NETWAITCB)(void *); typedef int (*NETWAITCB)(void *);
@@ -71,21 +89,44 @@ typedef int (*NETSETLINKSTATE)(void *);
typedef struct netpkt { typedef struct netpkt {
void *priv; uint8_t *data;
uint8_t data[65536]; /* Maximum length + 1 to round up to the nearest power of 2. */
int len; int len;
uint64_t tsc;
struct netpkt *prev, *next;
} netpkt_t; } netpkt_t;
typedef struct { typedef struct {
const device_t *device; netpkt_t packets[NET_QUEUE_LEN];
int size;
int head;
int tail;
} netqueue_t;
typedef struct _netcard_t netcard_t;
typedef struct netdrv_t {
void (*notify_in)(void *priv);
void *(*init)(const netcard_t *card, const uint8_t *mac_addr, void *priv);
void (*close)(void *priv);
void *priv; void *priv;
} netdrv_t;
extern const netdrv_t net_pcap_drv;
extern const netdrv_t net_slirp_drv;
struct _netcard_t {
const device_t *device;
void *card_drv;
struct netdrv_t host_drv;
int (*poll)(void *); int (*poll)(void *);
NETRXCB rx; NETRXCB rx;
NETWAITCB wait; NETWAITCB wait;
NETSETLINKSTATE set_link_state; NETSETLINKSTATE set_link_state;
} netcard_t; netqueue_t queues[3];
netpkt_t queued_pkt;
mutex_t *tx_mutex;
mutex_t *rx_mutex;
pc_timer_t timer;
};
typedef struct { typedef struct {
char device[128]; char device[128];
@@ -100,31 +141,19 @@ extern "C" {
/* Global variables. */ /* Global variables. */
extern int nic_do_log; /* config */ extern int nic_do_log; /* config */
extern int network_ndev; extern int network_ndev;
extern int network_rx_pause;
extern netdev_t network_devs[32]; extern netdev_t network_devs[32];
/* Function prototypes. */ /* Function prototypes. */
extern void network_wait(uint8_t wait);
extern void network_init(void); extern void network_init(void);
extern void network_attach(void *, uint8_t *, NETRXCB, NETWAITCB, NETSETLINKSTATE); extern netcard_t *network_attach(void *card_drv, uint8_t *mac, NETRXCB rx, NETWAITCB wait, NETSETLINKSTATE set_link_state);
extern void netcard_close(netcard_t *card);
extern void network_close(void); extern void network_close(void);
extern void network_reset(void); extern void network_reset(void);
extern int network_available(void); extern int network_available(void);
extern void network_tx(uint8_t *, int); extern void network_tx(netcard_t *card, uint8_t *, int);
extern int network_tx_queue_check(void);
extern int net_pcap_prepare(netdev_t *); extern int net_pcap_prepare(netdev_t *);
extern int net_pcap_init(void);
extern int net_pcap_reset(const netcard_t *, uint8_t *);
extern void net_pcap_close(void);
extern void net_pcap_in(uint8_t *, int);
extern int net_slirp_init(void);
extern int net_slirp_reset(const netcard_t *, uint8_t *);
extern void net_slirp_close(void);
extern void net_slirp_in(uint8_t *, int);
extern int network_dev_to_id(char *); extern int network_dev_to_id(char *);
extern int network_card_available(int); extern int network_card_available(int);
@@ -133,13 +162,8 @@ extern char *network_card_get_internal_name(int);
extern int network_card_get_from_internal_name(char *); extern int network_card_get_from_internal_name(char *);
extern const device_t *network_card_getdevice(int); extern const device_t *network_card_getdevice(int);
extern void network_set_wait(int wait); extern int network_tx_pop(netcard_t *card, netpkt_t *out_pkt);
extern int network_get_wait(void); extern int network_rx_put(netcard_t *card, uint8_t *bufp, int len);
extern void network_timer_stop(void);
extern void network_queue_put(int tx, void *priv, uint8_t *data, int len);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@@ -14,7 +14,7 @@
# #
add_library(net OBJECT network.c net_pcap.c net_slirp.c net_dp8390.c net_3c503.c add_library(net OBJECT network.c net_pcap.c net_slirp.c net_dp8390.c net_3c503.c
net_ne2000.c net_pcnet.c net_wd8003.c net_plip.c) net_ne2000.c net_pcnet.c net_wd8003.c net_plip.c net_event.c)
option(SLIRP_EXTERNAL "Link against the system-provided libslirp library" OFF) option(SLIRP_EXTERNAL "Link against the system-provided libslirp library" OFF)
mark_as_advanced(SLIRP_EXTERNAL) mark_as_advanced(SLIRP_EXTERNAL)

View File

@@ -55,6 +55,8 @@
#include <86box/mem.h> #include <86box/mem.h>
#include <86box/random.h> #include <86box/random.h>
#include <86box/device.h> #include <86box/device.h>
#include <86box/thread.h>
#include <86box/timer.h>
#include <86box/network.h> #include <86box/network.h>
#include <86box/net_dp8390.h> #include <86box/net_dp8390.h>
#include <86box/net_3c503.h> #include <86box/net_3c503.h>
@@ -592,7 +594,7 @@ threec503_nic_init(const device_t *info)
dev->maclocal[5] = (mac & 0xff); dev->maclocal[5] = (mac & 0xff);
} }
dev->dp8390 = device_add(&dp8390_device); dev->dp8390 = device_add_inst(&dp8390_device, dp3890_inst++);
dev->dp8390->priv = dev; dev->dp8390->priv = dev;
dev->dp8390->interrupt = threec503_interrupt; dev->dp8390->interrupt = threec503_interrupt;
dp8390_set_defaults(dev->dp8390, DP8390_FLAG_CHECK_CR | DP8390_FLAG_CLEAR_IRQ); dp8390_set_defaults(dev->dp8390, DP8390_FLAG_CHECK_CR | DP8390_FLAG_CLEAR_IRQ);
@@ -617,7 +619,7 @@ threec503_nic_init(const device_t *info)
dev->regs.gacfr = 0x09; /* Start with RAM mapping enabled. */ dev->regs.gacfr = 0x09; /* Start with RAM mapping enabled. */
/* Attach ourselves to the network module. */ /* Attach ourselves to the network module. */
network_attach(dev->dp8390, dev->dp8390->physaddr, dp8390_rx, NULL, NULL); dev->dp8390->card = network_attach(dev->dp8390, dev->dp8390->physaddr, dp8390_rx, NULL, NULL);
return(dev); return(dev);
} }

View File

@@ -25,6 +25,8 @@
#define HAVE_STDARG_H #define HAVE_STDARG_H
#include <86box/86box.h> #include <86box/86box.h>
#include <86box/device.h> #include <86box/device.h>
#include <86box/thread.h>
#include <86box/timer.h>
#include <86box/network.h> #include <86box/network.h>
#include <86box/net_dp8390.h> #include <86box/net_dp8390.h>
@@ -33,6 +35,7 @@ static void dp8390_tx(dp8390_t *dev, uint32_t val);
static int dp8390_rx_common(void *priv, uint8_t *buf, int io_len); static int dp8390_rx_common(void *priv, uint8_t *buf, int io_len);
int dp8390_rx(void *priv, uint8_t *buf, int io_len); int dp8390_rx(void *priv, uint8_t *buf, int io_len);
int dp3890_inst = 0;
#ifdef ENABLE_DP8390_LOG #ifdef ENABLE_DP8390_LOG
int dp8390_do_log = ENABLE_DP8390_LOG; int dp8390_do_log = ENABLE_DP8390_LOG;
@@ -225,7 +228,7 @@ dp8390_write_cr(dp8390_t *dev, uint32_t val)
/* Send the packet to the system driver */ /* Send the packet to the system driver */
dev->CR.tx_packet = 1; dev->CR.tx_packet = 1;
network_tx(&dev->mem[(dev->tx_page_start * 256) - dev->mem_start], dev->tx_bytes); network_tx(dev->card, &dev->mem[(dev->tx_page_start * 256) - dev->mem_start], dev->tx_bytes);
/* some more debug */ /* some more debug */
#ifdef ENABLE_DP8390_LOG #ifdef ENABLE_DP8390_LOG
@@ -1099,13 +1102,14 @@ dp8390_close(void *priv)
{ {
dp8390_t *dp8390 = (dp8390_t *) priv; dp8390_t *dp8390 = (dp8390_t *) priv;
/* Make sure the platform layer is shut down. */
network_close();
if (dp8390) { if (dp8390) {
if (dp8390->mem) if (dp8390->mem)
free(dp8390->mem); free(dp8390->mem);
if (dp8390->card) {
netcard_close(dp8390->card);
}
free(dp8390); free(dp8390);
} }
} }

76
src/network/net_event.c Normal file
View File

@@ -0,0 +1,76 @@
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#else
#include <unistd.h>
#include <fcntl.h>
#endif
#include <86box/net_event.h>
#ifndef _WIN32
static void setup_fd(int fd)
{
fcntl(fd, F_SETFD, FD_CLOEXEC);
fcntl(fd, F_SETFL, O_NONBLOCK);
}
#endif
void
net_event_init(net_evt_t *event)
{
#ifdef _WIN32
event->handle = CreateEvent(NULL, FALSE, FALSE, NULL);
#else
(void)pipe(event->fds);
setup_fd(event->fds[0]);
setup_fd(event->fds[1]);
#endif
}
void
net_event_set(net_evt_t *event)
{
#ifdef _WIN32
SetEvent(event->handle);
#else
(void)write(event->fds[1], "a", 1);
#endif
}
void
net_event_clear(net_evt_t *event)
{
#ifdef _WIN32
/* Do nothing on WIN32 since we use an auto-reset event */
#else
char dummy[1];
(void)read(event->fds[0], &dummy, sizeof(dummy));
#endif
}
void
net_event_close(net_evt_t *event)
{
#ifdef _WIN32
CloseHandle(event->handle);
#else
close(event->fds[0]);
close(event->fds[1]);
#endif
}
#ifdef _WIN32
HANDLE
net_event_get_handle(net_evt_t *event)
{
return event->handle;
}
#else
int
net_event_get_fd(net_evt_t *event)
{
return event->fds[0];
}
#endif

View File

@@ -61,6 +61,8 @@
#include <86box/pic.h> #include <86box/pic.h>
#include <86box/random.h> #include <86box/random.h>
#include <86box/device.h> #include <86box/device.h>
#include <86box/thread.h>
#include <86box/timer.h>
#include <86box/network.h> #include <86box/network.h>
#include <86box/net_dp8390.h> #include <86box/net_dp8390.h>
#include <86box/net_ne2000.h> #include <86box/net_ne2000.h>
@@ -973,7 +975,7 @@ nic_init(const device_t *info)
dev->maclocal[5] = (mac & 0xff); dev->maclocal[5] = (mac & 0xff);
} }
dev->dp8390 = device_add(&dp8390_device); dev->dp8390 = device_add_inst(&dp8390_device, dp3890_inst++);
dev->dp8390->priv = dev; dev->dp8390->priv = dev;
dev->dp8390->interrupt = nic_interrupt; dev->dp8390->interrupt = nic_interrupt;
@@ -1120,7 +1122,7 @@ nic_init(const device_t *info)
nic_reset(dev); nic_reset(dev);
/* Attach ourselves to the network module. */ /* Attach ourselves to the network module. */
network_attach(dev->dp8390, dev->dp8390->physaddr, dp8390_rx, NULL, NULL); dev->dp8390->card = network_attach(dev->dp8390, dev->dp8390->physaddr, dp8390_rx, NULL, NULL);
nelog(1, "%s: %s attached IO=0x%X IRQ=%d\n", dev->name, nelog(1, "%s: %s attached IO=0x%X IRQ=%d\n", dev->name,
dev->is_pci?"PCI":"ISA", dev->base_address, dev->base_irq); dev->is_pci?"PCI":"ISA", dev->base_address, dev->base_irq);

View File

@@ -50,15 +50,38 @@
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <wchar.h> #include <wchar.h>
#include <stdbool.h>
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winsock2.h>
#else
#include <poll.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/select.h>
#endif
#define HAVE_STDARG_H #define HAVE_STDARG_H
#include <86box/86box.h> #include <86box/86box.h>
#include <86box/device.h> #include <86box/device.h>
#include <86box/plat.h> #include <86box/plat.h>
#include <86box/plat_dynld.h> #include <86box/plat_dynld.h>
#include <86box/thread.h> #include <86box/thread.h>
#include <86box/timer.h>
#include <86box/network.h> #include <86box/network.h>
#include <86box/net_event.h>
enum {
NET_EVENT_STOP = 0,
NET_EVENT_TX,
NET_EVENT_RX,
NET_EVENT_MAX
};
#ifdef __APPLE__
#include <pcap/pcap.h>
#else
typedef int bpf_int32; typedef int bpf_int32;
typedef unsigned int bpf_u_int32; typedef unsigned int bpf_u_int32;
@@ -82,15 +105,10 @@ struct bpf_program {
typedef struct pcap_if pcap_if_t; typedef struct pcap_if pcap_if_t;
typedef struct net_timeval {
long tv_sec;
long tv_usec;
} net_timeval;
#define PCAP_ERRBUF_SIZE 256 #define PCAP_ERRBUF_SIZE 256
struct pcap_pkthdr { struct pcap_pkthdr {
struct net_timeval ts; struct timeval ts;
bpf_u_int32 caplen; bpf_u_int32 caplen;
bpf_u_int32 len; bpf_u_int32 len;
}; };
@@ -100,30 +118,51 @@ struct pcap_if {
char *name; char *name;
char *description; char *description;
void *addresses; void *addresses;
unsigned int flags; bpf_u_int32 flags;
}; };
#endif
typedef struct {
void *pcap; /* handle to pcap lib instance */
netcard_t *card; /* netcard linked to us */
thread_t *poll_tid;
net_evt_t tx_event;
net_evt_t stop_event;
netpkt_t pkt;
uint8_t mac_addr[6];
} net_pcap_t;
static volatile void *pcap_handle; /* handle to WinPcap DLL */ typedef struct {
static volatile void *pcap; /* handle to WinPcap library */ char *intf_name;
static volatile thread_t *poll_tid; uint8_t *mac_addr;
static const netcard_t *poll_card; /* netcard linked to us */ } net_pcap_params_t;
static event_t *poll_state;
static volatile void *libpcap_handle; /* handle to WinPcap DLL */
/* Pointers to the real functions. */ /* Pointers to the real functions. */
static const char *(*f_pcap_lib_version)(void); static const char *(*f_pcap_lib_version)(void);
static int (*f_pcap_findalldevs)(pcap_if_t **,char *); static int (*f_pcap_findalldevs)(pcap_if_t **,char *);
static void (*f_pcap_freealldevs)(void *); static void (*f_pcap_freealldevs)(void *);
static void *(*f_pcap_open_live)(const char *,int,int,int,char *); static void *(*f_pcap_open_live)(const char *,int,int,int,char *);
static int (*f_pcap_compile)(void *,void *, static int (*f_pcap_compile)(void *,void *, const char *,int,bpf_u_int32);
const char *,int,bpf_u_int32);
static int (*f_pcap_setfilter)(void *,void *); static int (*f_pcap_setfilter)(void *,void *);
static const unsigned char static const unsigned char
*(*f_pcap_next)(void *,void *); *(*f_pcap_next)(void *,void *);
static int (*f_pcap_sendpacket)(void *,const unsigned char *,int); static int (*f_pcap_sendpacket)(void *,const unsigned char *,int);
static void (*f_pcap_close)(void *); static void (*f_pcap_close)(void *);
static int (*f_pcap_setnonblock)(void*, int, char*); static int (*f_pcap_setnonblock)(void*, int, char*);
static int (*f_pcap_set_immediate_mode)(void *, int);
static int (*f_pcap_set_promisc)(void *, int);
static int (*f_pcap_set_snaplen)(void *, int);
static void *(*f_pcap_create)(const char *, char*);
static int (*f_pcap_activate)(void *);
static void *(*f_pcap_geterr)(void *);
#ifdef _WIN32
static HANDLE (*f_pcap_getevent)(void *);
#else
static int (*f_pcap_get_selectable_fd)(void *);
#endif
static dllimp_t pcap_imports[] = { static dllimp_t pcap_imports[] = {
{ "pcap_lib_version", &f_pcap_lib_version }, { "pcap_lib_version", &f_pcap_lib_version },
{ "pcap_findalldevs", &f_pcap_findalldevs }, { "pcap_findalldevs", &f_pcap_findalldevs },
@@ -135,14 +174,23 @@ static dllimp_t pcap_imports[] = {
{ "pcap_sendpacket", &f_pcap_sendpacket }, { "pcap_sendpacket", &f_pcap_sendpacket },
{ "pcap_close", &f_pcap_close }, { "pcap_close", &f_pcap_close },
{ "pcap_setnonblock", &f_pcap_setnonblock }, { "pcap_setnonblock", &f_pcap_setnonblock },
{ "pcap_set_immediate_mode", &f_pcap_set_immediate_mode},
{ "pcap_set_promisc", &f_pcap_set_promisc },
{ "pcap_set_snaplen", &f_pcap_set_snaplen },
{ "pcap_create", &f_pcap_create },
{ "pcap_activate", &f_pcap_activate },
{ "pcap_geterr", &f_pcap_geterr },
#ifdef _WIN32
{ "pcap_getevent", &f_pcap_getevent },
#else
{ "pcap_get_selectable_fd", &f_pcap_get_selectable_fd },
#endif
{ NULL, NULL }, { NULL, NULL },
}; };
#ifdef ENABLE_PCAP_LOG #ifdef ENABLE_PCAP_LOG
int pcap_do_log = ENABLE_PCAP_LOG; int pcap_do_log = ENABLE_PCAP_LOG;
static void static void
pcap_log(const char *fmt, ...) pcap_log(const char *fmt, ...)
{ {
@@ -159,76 +207,118 @@ pcap_log(const char *fmt, ...)
#endif #endif
/* Handle the receiving of frames from the channel. */
static void static void
poll_thread(void *arg) net_pcap_read_packet(net_pcap_t *pcap)
{ {
uint8_t *mac = (uint8_t *)arg;
uint8_t *data = NULL;
struct pcap_pkthdr h; struct pcap_pkthdr h;
uint32_t mac_cmp32[2];
uint16_t mac_cmp16[2]; uint8_t *data = (uint8_t *) f_pcap_next((void *) pcap->pcap, &h);
event_t *evt; if (!data)
int tx; return;
network_rx_put(pcap->card, data, h.caplen);
}
/* Send a packet to the Pcap interface. */
void
net_pcap_in(void *pcap, uint8_t *bufp, int len)
{
if (pcap == NULL)
return;
f_pcap_sendpacket((void *)pcap, bufp, len);
}
void
net_pcap_in_available(void *priv)
{
net_pcap_t *pcap = (net_pcap_t *)priv;
net_event_set(&pcap->tx_event);
}
#ifdef _WIN32
static void
net_pcap_thread(void *priv)
{
net_pcap_t *pcap = (net_pcap_t*)priv;
pcap_log("PCAP: polling started.\n"); pcap_log("PCAP: polling started.\n");
thread_set_event(poll_state);
/* Create a waitable event. */ HANDLE events[NET_EVENT_MAX];
pcap_log("PCAP: Creating event...\n"); events[NET_EVENT_STOP] = net_event_get_handle(&pcap->stop_event);
evt = thread_create_event(); events[NET_EVENT_TX] = net_event_get_handle(&pcap->tx_event);
events[NET_EVENT_RX] = f_pcap_getevent((void *)pcap->pcap);
bool run = true;
while (run) {
int ret = WaitForMultipleObjects(NET_EVENT_MAX, events, FALSE, INFINITE);
switch (ret - WAIT_OBJECT_0) {
case NET_EVENT_STOP:
net_event_clear(&pcap->stop_event);
run = false;
break;
case NET_EVENT_TX:
net_event_clear(&pcap->tx_event);
while (network_tx_pop(pcap->card, &pcap->pkt)) {
net_pcap_in(pcap->pcap, pcap->pkt.data, pcap->pkt.len);
}
break;
case NET_EVENT_RX:
net_pcap_read_packet(pcap);
break;
}
}
pcap_log("PCAP: polling stopped.\n");
}
#else
static void
net_pcap_thread(void *priv)
{
net_pcap_t *pcap = (net_pcap_t*)priv;
pcap_log("PCAP: polling started.\n");
struct pollfd pfd[NET_EVENT_MAX];
pfd[NET_EVENT_STOP].fd = net_event_get_fd(&pcap->stop_event);
pfd[NET_EVENT_STOP].events = POLLIN | POLLPRI;
pfd[NET_EVENT_TX].fd = net_event_get_fd(&pcap->tx_event);
pfd[NET_EVENT_TX].events = POLLIN | POLLPRI;
pfd[NET_EVENT_RX].fd = f_pcap_get_selectable_fd((void *) pcap->pcap);
pfd[NET_EVENT_RX].events = POLLIN | POLLPRI;
/* As long as the channel is open.. */ /* As long as the channel is open.. */
while (pcap != NULL) { while (1) {
/* Request ownership of the device. */ poll(pfd, NET_EVENT_MAX, -1);
network_wait(1);
if (pcap == NULL) { if (pfd[NET_EVENT_STOP].revents & POLLIN) {
network_wait(0); net_event_clear(&pcap->stop_event);
break; break;
} }
if (network_get_wait() || (poll_card->set_link_state && poll_card->set_link_state(poll_card->priv)) || (poll_card->wait && poll_card->wait(poll_card->priv))) if (pfd[NET_EVENT_TX].revents & POLLIN) {
data = NULL; net_event_clear(&pcap->tx_event);
else
data = (uint8_t *)f_pcap_next((void *)pcap, &h);
if (data != NULL) {
/* Received MAC. */
mac_cmp32[0] = *(uint32_t *)(data+6);
mac_cmp16[0] = *(uint16_t *)(data+10);
/* Local MAC. */ if (network_tx_pop(pcap->card, &pcap->pkt)) {
mac_cmp32[1] = *(uint32_t *)mac; net_pcap_in(pcap->pcap, pcap->pkt.data, pcap->pkt.len);
mac_cmp16[1] = *(uint16_t *)(mac+4);
if ((mac_cmp32[0] != mac_cmp32[1]) ||
(mac_cmp16[0] != mac_cmp16[1]))
network_queue_put(0, poll_card->priv, data, h.caplen);
else {
/* Mark as invalid packet. */
data = NULL;
} }
} }
/* Wait for the next packet to arrive - network_do_tx() is called from there. */ if (pfd[NET_EVENT_RX].revents & POLLIN) {
tx = network_tx_queue_check(); net_pcap_read_packet(pcap);
/* Release ownership of the device. */
network_wait(0);
/* If we did not get anything, wait a while. */
if (!tx)
thread_wait_event(evt, 10);
} }
/* No longer needed. */ }
if (evt != NULL)
thread_destroy_event(evt);
pcap_log("PCAP: polling stopped.\n"); pcap_log("PCAP: polling stopped.\n");
if (poll_state != NULL)
thread_set_event(poll_state);
} }
#endif
/* /*
* Prepare the (Win)Pcap module for use. * Prepare the (Win)Pcap module for use.
@@ -244,18 +334,18 @@ net_pcap_prepare(netdev_t *list)
pcap_if_t *devlist, *dev; pcap_if_t *devlist, *dev;
int i = 0; int i = 0;
/* Local variables. */
pcap = NULL;
/* Try loading the DLL. */ /* Try loading the DLL. */
#ifdef _WIN32 #ifdef _WIN32
pcap_handle = dynld_module("wpcap.dll", pcap_imports); libpcap_handle = dynld_module("wpcap.dll", pcap_imports);
#elif defined __APPLE__ #elif defined __APPLE__
pcap_handle = dynld_module("libpcap.dylib", pcap_imports); libpcap_handle = dynld_module("libpcap.dylib", pcap_imports);
#else #else
pcap_handle = dynld_module("libpcap.so", pcap_imports); libpcap_handle = dynld_module("libpcap.so", pcap_imports);
#endif #endif
if (pcap_handle == NULL) return(-1); if (libpcap_handle == NULL) {
pcap_log("PCAP: error loading pcap module\n");
return(-1);
}
/* Retrieve the device list from the local machine */ /* Retrieve the device list from the local machine */
if (f_pcap_findalldevs(&devlist, errbuf) == -1) { if (f_pcap_findalldevs(&devlist, errbuf) == -1) {
@@ -292,141 +382,132 @@ net_pcap_prepare(netdev_t *list)
/* /*
* Initialize (Win)Pcap for use. * Initialize (Win)Pcap for use.
* *
* This is called on every 'cycle' of the emulator,
* if and as long the NetworkType is set to PCAP,
* and also as long as we have a NetCard defined.
*/
int
net_pcap_init(void)
{
char errbuf[PCAP_ERRBUF_SIZE];
char *str;
/* Did we already load the library? */
if (pcap_handle == NULL)
return(-1);
/* Get the PCAP library name and version. */
strcpy(errbuf, f_pcap_lib_version());
str = strchr(errbuf, '(');
if (str != NULL) *(str-1) = '\0';
pcap_log("PCAP: initializing, %s\n", errbuf);
/* Get the value of our capture interface. */
if ((network_host[0] == '\0') || !strcmp(network_host, "none")) {
pcap_log("PCAP: no interface configured!\n");
return(-1);
}
poll_tid = NULL;
poll_state = NULL;
poll_card = NULL;
return(0);
}
/* Close up shop. */
void
net_pcap_close(void)
{
void *pc;
if (pcap == NULL) return;
pcap_log("PCAP: closing.\n");
/* Tell the polling thread to shut down. */
pc = (void *)pcap; pcap = NULL;
/* Tell the thread to terminate. */
if (poll_tid != NULL) {
/* Wait for the thread to finish. */
pcap_log("PCAP: waiting for thread to end...\n");
thread_wait_event(poll_state, -1);
pcap_log("PCAP: thread ended\n");
thread_destroy_event(poll_state);
poll_tid = NULL;
poll_state = NULL;
poll_card = NULL;
}
/* OK, now shut down Pcap itself. */
f_pcap_close(pc);
pcap = NULL;
}
/*
* Reset (Win)Pcap and activate it.
*
* This is called on every 'cycle' of the emulator,
* if and as long the NetworkType is set to PCAP,
* and also as long as we have a NetCard defined.
*
* We already know we have PCAP available, as this * We already know we have PCAP available, as this
* is called when the network activates itself and * is called when the network activates itself and
* tries to attach to the network module. * tries to attach to the network module.
*/ */
int void *
net_pcap_reset(const netcard_t *card, uint8_t *mac) net_pcap_init(const netcard_t *card, const uint8_t *mac_addr, void *priv)
{ {
char errbuf[PCAP_ERRBUF_SIZE]; char errbuf[PCAP_ERRBUF_SIZE];
char *str;
char filter_exp[255]; char filter_exp[255];
struct bpf_program fp; struct bpf_program fp;
/* Open a PCAP live channel. */ char *intf_name = (char*)priv;
if ((pcap = f_pcap_open_live(network_host, /* interface name */
1518, /* max packet size */ /* Did we already load the library? */
1, /* promiscuous mode? */ if (libpcap_handle == NULL) {
10, /* timeout in msec */ pcap_log("PCAP: net_pcap_init without handle.\n");
errbuf)) == NULL) { /* error buffer */ return NULL;
pcap_log(" Unable to open device: %s!\n", network_host);
return(-1);
} }
if (f_pcap_setnonblock((void*)pcap, 1, errbuf) != 0)
/* Get the PCAP library name and version. */
strcpy(errbuf, f_pcap_lib_version());
str = strchr(errbuf, '(');
if (str != NULL)
*(str - 1) = '\0';
pcap_log("PCAP: initializing, %s\n", errbuf);
/* Get the value of our capture interface. */
if ((intf_name[0] == '\0') || !strcmp(intf_name, "none")) {
pcap_log("PCAP: no interface configured!\n");
return NULL;
}
pcap_log("PCAP: interface: %s\n", intf_name);
net_pcap_t *pcap = calloc(1, sizeof(net_pcap_t));
pcap->card = (netcard_t *)card;
memcpy(pcap->mac_addr, mac_addr, sizeof(pcap->mac_addr));
if ((pcap->pcap = f_pcap_create(intf_name, errbuf)) == NULL) {
pcap_log(" Unable to open device: %s!\n", intf_name);
free(pcap);
return NULL;
}
if (f_pcap_setnonblock((void *) pcap->pcap, 1, errbuf) != 0)
pcap_log("PCAP: failed nonblock %s\n", errbuf); pcap_log("PCAP: failed nonblock %s\n", errbuf);
pcap_log("PCAP: interface: %s\n", network_host); if (f_pcap_set_immediate_mode((void *) pcap->pcap, 1) != 0)
pcap_log("PCAP: error setting immediate mode\n");
if (f_pcap_set_promisc((void *) pcap->pcap, 1) != 0)
pcap_log("PCAP: error enabling promiscuous mode\n");
if (f_pcap_set_snaplen((void *) pcap->pcap, NET_MAX_FRAME) != 0)
pcap_log("PCAP: error setting snaplen\n");
if (f_pcap_activate((void *) pcap->pcap) != 0) {
pcap_log("PCAP: failed pcap_activate");
f_pcap_close((void *) pcap->pcap);
free(pcap);
return NULL;
}
/* Create a MAC address based packet filter. */ /* Create a MAC address based packet filter. */
pcap_log("PCAP: installing filter for MAC=%02x:%02x:%02x:%02x:%02x:%02x\n", pcap_log("PCAP: installing filter for MAC=%02x:%02x:%02x:%02x:%02x:%02x\n",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
sprintf(filter_exp, sprintf(filter_exp,
"( ((ether dst ff:ff:ff:ff:ff:ff) or (ether dst %02x:%02x:%02x:%02x:%02x:%02x)) and not (ether src %02x:%02x:%02x:%02x:%02x:%02x) )", "( ((ether dst ff:ff:ff:ff:ff:ff) or (ether dst %02x:%02x:%02x:%02x:%02x:%02x)) and not (ether src %02x:%02x:%02x:%02x:%02x:%02x) )",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5],
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
if (f_pcap_compile((void *)pcap, &fp, filter_exp, 0, 0xffffffff) != -1) { if (f_pcap_compile((void *) pcap->pcap, &fp, filter_exp, 0, 0xffffffff) != -1) {
if (f_pcap_setfilter((void *)pcap, &fp) != 0) { if (f_pcap_setfilter((void *) pcap->pcap, &fp) != 0) {
pcap_log("PCAP: error installing filter (%s) !\n", filter_exp); pcap_log("PCAP: error installing filter (%s) !\n", filter_exp);
f_pcap_close((void *)pcap); f_pcap_close((void *) pcap->pcap);
return(-1); free(pcap);
return NULL;
} }
} else { } else {
pcap_log("PCAP: could not compile filter (%s) !\n", filter_exp); pcap_log("PCAP: could not compile filter (%s) : %s!\n", filter_exp, f_pcap_geterr((void*)pcap->pcap));
f_pcap_close((void *)pcap); f_pcap_close((void *) pcap->pcap);
return(-1); free(pcap);
return NULL;
} }
/* Save the callback info. */ pcap->pkt.data = calloc(1, NET_MAX_FRAME);
poll_card = card; net_event_init(&pcap->tx_event);
net_event_init(&pcap->stop_event);
pcap->poll_tid = thread_create(net_pcap_thread, pcap);
pcap_log("PCAP: starting thread..\n"); return pcap;
poll_state = thread_create_event();
poll_tid = thread_create(poll_thread, mac);
thread_wait_event(poll_state, -1);
return(0);
} }
/* Close up shop. */
/* Send a packet to the Pcap interface. */
void void
net_pcap_in(uint8_t *bufp, int len) net_pcap_close(void *priv)
{ {
if (pcap == NULL) if (!priv)
return; return;
f_pcap_sendpacket((void *)pcap, bufp, len); net_pcap_t *pcap = (net_pcap_t *)priv;
pcap_log("PCAP: closing.\n");
/* Tell the thread to terminate. */
net_event_set(&pcap->stop_event);
/* Wait for the thread to finish. */
pcap_log("PCAP: waiting for thread to end...\n");
thread_wait(pcap->poll_tid);
pcap_log("PCAP: thread ended\n");
free(pcap->pkt.data);
/* OK, now shut down Pcap itself. */
f_pcap_close((void*)pcap->pcap);
net_event_close(&pcap->tx_event);
net_event_close(&pcap->stop_event);
free(pcap);
} }
const netdrv_t net_pcap_drv = {
&net_pcap_in_available,
&net_pcap_init,
&net_pcap_close,
NULL
};

View File

@@ -41,6 +41,8 @@
#include <86box/random.h> #include <86box/random.h>
#include <86box/device.h> #include <86box/device.h>
#include <86box/isapnp.h> #include <86box/isapnp.h>
#include <86box/timer.h>
#include <86box/thread.h>
#include <86box/network.h> #include <86box/network.h>
#include <86box/net_pcnet.h> #include <86box/net_pcnet.h>
#include <86box/bswap.h> #include <86box/bswap.h>
@@ -259,6 +261,7 @@ typedef struct {
int transfer_size; int transfer_size;
uint8_t maclocal[6]; /* configured MAC (local) address */ uint8_t maclocal[6]; /* configured MAC (local) address */
pc_timer_t timer, timer_soft_int, timer_restore; pc_timer_t timer, timer_soft_int, timer_restore;
netcard_t *netcard;
} nic_t; } nic_t;
/** @todo All structs: big endian? */ /** @todo All structs: big endian? */
@@ -1528,7 +1531,7 @@ pcnetAsyncTransmit(nic_t *dev)
pcnetReceiveNoSync(dev, dev->abLoopBuf, dev->xmit_pos); pcnetReceiveNoSync(dev, dev->abLoopBuf, dev->xmit_pos);
} else { } else {
pcnetlog(3, "%s: pcnetAsyncTransmit: transmit loopbuf stp and enp, xmit pos = %d\n", dev->name, dev->xmit_pos); pcnetlog(3, "%s: pcnetAsyncTransmit: transmit loopbuf stp and enp, xmit pos = %d\n", dev->name, dev->xmit_pos);
network_tx(dev->abLoopBuf, dev->xmit_pos); network_tx(dev->netcard, dev->abLoopBuf, dev->xmit_pos);
} }
} else if (cb == 4096) { } else if (cb == 4096) {
/* The Windows NT4 pcnet driver sometimes marks the first /* The Windows NT4 pcnet driver sometimes marks the first
@@ -1639,7 +1642,7 @@ pcnetAsyncTransmit(nic_t *dev)
pcnetReceiveNoSync(dev, dev->abLoopBuf, dev->xmit_pos); pcnetReceiveNoSync(dev, dev->abLoopBuf, dev->xmit_pos);
} else { } else {
pcnetlog(3, "%s: pcnetAsyncTransmit: transmit loopbuf enp\n", dev->name); pcnetlog(3, "%s: pcnetAsyncTransmit: transmit loopbuf enp\n", dev->name);
network_tx(dev->abLoopBuf, dev->xmit_pos); network_tx(dev->netcard, dev->abLoopBuf, dev->xmit_pos);
} }
/* Write back the TMD, pass it to the host */ /* Write back the TMD, pass it to the host */
@@ -3051,7 +3054,7 @@ pcnet_init(const device_t *info)
pcnetHardReset(dev); pcnetHardReset(dev);
/* Attach ourselves to the network module. */ /* Attach ourselves to the network module. */
network_attach(dev, dev->aPROM, pcnetReceiveNoSync, pcnetWaitReceiveAvail, pcnetSetLinkState); dev->netcard = network_attach(dev, dev->aPROM, pcnetReceiveNoSync, pcnetWaitReceiveAvail, pcnetSetLinkState);
timer_add(&dev->timer, pcnetPollTimer, dev, 0); timer_add(&dev->timer, pcnetPollTimer, dev, 0);
@@ -3071,8 +3074,7 @@ pcnet_close(void *priv)
pcnetlog(1, "%s: closed\n", dev->name); pcnetlog(1, "%s: closed\n", dev->name);
/* Make sure the platform layer is shut down. */ netcard_close(dev->netcard);
network_close();
if (dev) { if (dev) {
free(dev); free(dev);

View File

@@ -31,6 +31,8 @@
#include <86box/timer.h> #include <86box/timer.h>
#include <86box/pit.h> #include <86box/pit.h>
#include <86box/device.h> #include <86box/device.h>
#include <86box/thread.h>
#include <86box/timer.h>
#include <86box/network.h> #include <86box/network.h>
#include <86box/net_plip.h> #include <86box/net_plip.h>
@@ -70,6 +72,7 @@ typedef struct
uint8_t *rx_pkt, rx_checksum, rx_return_state; uint8_t *rx_pkt, rx_checksum, rx_return_state;
uint16_t rx_len, rx_ptr; uint16_t rx_len, rx_ptr;
netcard_t *card;
} plip_t; } plip_t;
@@ -117,8 +120,6 @@ timeout_timer(void *priv)
dev->rx_pkt = NULL; dev->rx_pkt = NULL;
} }
network_rx_pause = 0;
timer_disable(&dev->timeout_timer); timer_disable(&dev->timeout_timer);
} }
@@ -229,7 +230,7 @@ plip_write_data(uint8_t val, void *priv)
/* Transmit packet. */ /* Transmit packet. */
plip_log(2, "PLIP: transmitting %d-byte packet\n", dev->tx_len); plip_log(2, "PLIP: transmitting %d-byte packet\n", dev->tx_len);
network_tx(dev->tx_pkt, dev->tx_len); network_tx(dev->card, dev->tx_pkt, dev->tx_len);
} else { } else {
plip_log(1, "PLIP: checksum error: expected %02X, got %02X\n", dev->tx_checksum_calc, dev->tx_checksum); plip_log(1, "PLIP: checksum error: expected %02X, got %02X\n", dev->tx_checksum_calc, dev->tx_checksum);
} }
@@ -381,7 +382,6 @@ plip_receive_packet(plip_t *dev)
} }
if (!dev->rx_pkt || !dev->rx_len) { /* unpause RX queue if there's no packet to receive */ if (!dev->rx_pkt || !dev->rx_len) { /* unpause RX queue if there's no packet to receive */
network_rx_pause = 0;
return; return;
} }
@@ -432,8 +432,6 @@ plip_rx(void *priv, uint8_t *buf, int io_len)
if (!(dev->rx_pkt = malloc(io_len))) /* unlikely */ if (!(dev->rx_pkt = malloc(io_len))) /* unlikely */
fatal("PLIP: unable to allocate rx_pkt\n"); 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. */ /* Copy this packet to our buffer. */
dev->rx_len = io_len; dev->rx_len = io_len;
memcpy(dev->rx_pkt, buf, dev->rx_len); memcpy(dev->rx_pkt, buf, dev->rx_len);
@@ -478,7 +476,7 @@ plip_net_init(const device_t *info)
} }
plip_log(1, " (attached to LPT)\n"); plip_log(1, " (attached to LPT)\n");
network_attach(instance, instance->mac, plip_rx, NULL, NULL); instance->card = network_attach(instance, instance->mac, plip_rx, NULL, NULL);
return instance; return instance;
} }
@@ -487,6 +485,9 @@ plip_net_init(const device_t *info)
static void static void
plip_close(void *priv) plip_close(void *priv)
{ {
if (instance->card) {
netcard_close(instance->card);
}
free(priv); free(priv);
} }

View File

@@ -24,52 +24,51 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <inttypes.h>
#include <wchar.h> #include <wchar.h>
#include <fcntl.h>
#include <unistd.h>
#include <slirp/libslirp.h> #include <slirp/libslirp.h>
#define HAVE_STDARG_H #define HAVE_STDARG_H
#include <86box/86box.h> #include <86box/86box.h>
#include <86box/device.h> #include <86box/device.h>
#include <86box/plat.h> #include <86box/plat.h>
#include <86box/thread.h> #include <86box/thread.h>
#include <86box/timer.h>
#include <86box/network.h> #include <86box/network.h>
#include <86box/machine.h> #include <86box/machine.h>
#include <86box/timer.h>
#include <86box/config.h> #include <86box/config.h>
#include <86box/video.h>
/* 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
#ifdef SLIRP_USE_POLL
#ifdef _WIN32 #ifdef _WIN32
# include <winsock2.h> #define WIN32_LEAN_AND_MEAN
# define poll WSAPoll #include <windows.h>
#else #else
#include <poll.h> #include <poll.h>
#endif #endif
#endif #include <86box/net_event.h>
enum {
NET_EVENT_STOP = 0,
NET_EVENT_TX,
NET_EVENT_RX,
NET_EVENT_MAX
};
typedef struct { typedef struct {
Slirp *slirp; Slirp *slirp;
void *mac; uint8_t mac_addr[6];
const netcard_t *card; /* netcard attached to us */ netcard_t *card; /* netcard attached to us */
volatile thread_t *poll_tid; thread_t *poll_tid;
event_t *poll_state; net_evt_t tx_event;
uint8_t stop; net_evt_t stop_event;
#ifdef SLIRP_USE_POLL netpkt_t pkt;
#ifdef _WIN32
HANDLE sock_event;
#else
uint32_t pfd_len, pfd_size; uint32_t pfd_len, pfd_size;
struct pollfd *pfd; struct pollfd *pfd;
#else
uint32_t nfds;
fd_set rfds, wfds, xfds;
#endif #endif
} slirp_t; } net_slirp_t;
static slirp_t *slirp;
#ifdef ENABLE_SLIRP_LOG #ifdef ENABLE_SLIRP_LOG
int slirp_do_log = ENABLE_SLIRP_LOG; int slirp_do_log = ENABLE_SLIRP_LOG;
@@ -101,7 +100,7 @@ net_slirp_guest_error(const char *msg, void *opaque)
static int64_t static int64_t
net_slirp_clock_get_ns(void *opaque) net_slirp_clock_get_ns(void *opaque)
{ {
return (TIMER_USEC ? (tsc / (TIMER_USEC / 1000)) : 0); return (int64_t)((double)tsc / cpuclock * 1000000000.0);
} }
@@ -118,13 +117,14 @@ static void
net_slirp_timer_free(void *timer, void *opaque) net_slirp_timer_free(void *timer, void *opaque)
{ {
timer_stop(timer); timer_stop(timer);
free(timer);
} }
static void static void
net_slirp_timer_mod(void *timer, int64_t expire_timer, void *opaque) net_slirp_timer_mod(void *timer, int64_t expire_timer, void *opaque)
{ {
timer_set_delay_u64(timer, expire_timer); timer_on_auto(timer, expire_timer * 1000);
} }
@@ -154,12 +154,11 @@ net_slirp_notify(void *opaque)
ssize_t ssize_t
net_slirp_send_packet(const void *qp, size_t pkt_len, void *opaque) net_slirp_send_packet(const void *qp, size_t pkt_len, void *opaque)
{ {
slirp_t *slirp = (slirp_t *) opaque; net_slirp_t *slirp = (net_slirp_t *) opaque;
uint8_t *mac = slirp->mac; uint8_t *mac = slirp->mac_addr;
uint32_t mac_cmp32[2]; 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); slirp_log("SLiRP: received %d-byte packet\n", pkt_len);
/* Received MAC. */ /* Received MAC. */
@@ -169,25 +168,38 @@ net_slirp_send_packet(const void *qp, size_t pkt_len, void *opaque)
/* Local MAC. */ /* Local MAC. */
mac_cmp32[1] = *(uint32_t *) mac; mac_cmp32[1] = *(uint32_t *) mac;
mac_cmp16[1] = *(uint16_t *) (mac + 4); mac_cmp16[1] = *(uint16_t *) (mac + 4);
if ((mac_cmp32[0] != mac_cmp32[1]) || if ((mac_cmp32[0] != mac_cmp32[1]) || (mac_cmp16[0] != mac_cmp16[1])) {
(mac_cmp16[0] != mac_cmp16[1])) { network_rx_put(slirp->card, (uint8_t *) qp, pkt_len);
network_queue_put(0, slirp->card->priv, (uint8_t *) qp, pkt_len);
} }
return pkt_len; return pkt_len;
} else {
slirp_log("SLiRP: ignored %d-byte packet\n", pkt_len);
}
return 0;
} }
#ifdef _WIN32
static int static int
net_slirp_add_poll(int fd, int events, void *opaque) net_slirp_add_poll(int fd, int events, void *opaque)
{ {
slirp_t *slirp = (slirp_t *) opaque; net_slirp_t *slirp = (net_slirp_t *) opaque;
#ifdef SLIRP_USE_POLL long bitmask = 0;
if (events & SLIRP_POLL_IN)
bitmask |= FD_READ | FD_ACCEPT;
if (events & SLIRP_POLL_OUT)
bitmask |= FD_WRITE | FD_CONNECT;
if (events & SLIRP_POLL_HUP)
bitmask |= FD_CLOSE;
if (events & SLIRP_POLL_PRI)
bitmask |= FD_OOB;
WSAEventSelect(fd, slirp->sock_event, bitmask);
return fd;
}
#else
static int
net_slirp_add_poll(int fd, int events, void *opaque)
{
net_slirp_t *slirp = (net_slirp_t *) opaque;
if (slirp->pfd_len >= slirp->pfd_size) { if (slirp->pfd_len >= slirp->pfd_size) {
int newsize = slirp->pfd_size + 16; int newsize = slirp->pfd_size + 16;
struct pollfd *new = realloc(slirp->pfd, newsize * sizeof(struct pollfd)); struct pollfd *new = realloc(slirp->pfd, newsize * sizeof(struct pollfd));
@@ -200,92 +212,73 @@ net_slirp_add_poll(int fd, int events, void *opaque)
int idx = slirp->pfd_len++; int idx = slirp->pfd_len++;
slirp->pfd[idx].fd = fd; slirp->pfd[idx].fd = fd;
int pevents = 0; int pevents = 0;
if (events & SLIRP_POLL_IN) pevents |= POLLIN; if (events & SLIRP_POLL_IN)
if (events & SLIRP_POLL_OUT) pevents |= POLLOUT; pevents |= POLLIN;
# ifndef _WIN32 if (events & SLIRP_POLL_OUT)
/* Windows does not support some events. */ pevents |= POLLOUT;
if (events & SLIRP_POLL_ERR) pevents |= POLLERR; if (events & SLIRP_POLL_ERR)
if (events & SLIRP_POLL_PRI) pevents |= POLLPRI; pevents |= POLLERR;
if (events & SLIRP_POLL_HUP) pevents |= POLLHUP; if (events & SLIRP_POLL_PRI)
# endif pevents |= POLLPRI;
if (events & SLIRP_POLL_HUP)
pevents |= POLLHUP;
slirp->pfd[idx].events = pevents; slirp->pfd[idx].events = pevents;
return idx; return idx;
} else } else
return -1; return -1;
#else
if (events & SLIRP_POLL_IN)
FD_SET(fd, &slirp->rfds);
if (events & SLIRP_POLL_OUT)
FD_SET(fd, &slirp->wfds);
if (events & SLIRP_POLL_PRI)
FD_SET(fd, &slirp->xfds);
if (fd > slirp->nfds)
slirp->nfds = fd;
return fd;
#endif
} }
#endif
#ifdef _WIN32
static int static int
net_slirp_get_revents(int idx, void *opaque) net_slirp_get_revents(int idx, void *opaque)
{ {
slirp_t *slirp = (slirp_t *) opaque; net_slirp_t *slirp = (net_slirp_t *) opaque;
int ret = 0; int ret = 0;
#ifdef SLIRP_USE_POLL WSANETWORKEVENTS ev;
int events = slirp->pfd[idx].revents; if (WSAEnumNetworkEvents(idx, slirp->sock_event, &ev) != 0) {
if (events & POLLIN) ret |= SLIRP_POLL_IN;
if (events & POLLOUT) ret |= SLIRP_POLL_OUT;
if (events & POLLPRI) ret |= SLIRP_POLL_PRI;
if (events & POLLERR) ret |= SLIRP_POLL_ERR;
if (events & POLLHUP) ret |= SLIRP_POLL_HUP;
#else
if (FD_ISSET(idx, &slirp->rfds))
ret |= SLIRP_POLL_IN;
if (FD_ISSET(idx, &slirp->wfds))
ret |= SLIRP_POLL_OUT;
if (FD_ISSET(idx, &slirp->xfds))
ret |= SLIRP_POLL_PRI;
#endif
return ret; return ret;
} }
# define WSA_TO_POLL(_wsaev, _pollev) \
do { \
if (ev.lNetworkEvents & (_wsaev)) { \
ret |= (_pollev); \
if (ev.iErrorCode[_wsaev##_BIT] != 0) { \
ret |= SLIRP_POLL_ERR; \
} \
} \
} while (0)
static void WSA_TO_POLL(FD_READ, SLIRP_POLL_IN);
slirp_tic(slirp_t *slirp) WSA_TO_POLL(FD_ACCEPT, SLIRP_POLL_IN);
{ WSA_TO_POLL(FD_WRITE, SLIRP_POLL_OUT);
int ret; WSA_TO_POLL(FD_CONNECT, SLIRP_POLL_OUT);
uint32_t tmo; WSA_TO_POLL(FD_OOB, SLIRP_POLL_PRI);
WSA_TO_POLL(FD_CLOSE, SLIRP_POLL_HUP);
/* Let SLiRP create a list of all open sockets. */ return ret;
#ifdef SLIRP_USE_POLL
tmo = -1;
slirp->pfd_len = 0;
#else
slirp->nfds = -1;
FD_ZERO(&slirp->rfds);
FD_ZERO(&slirp->wfds);
FD_ZERO(&slirp->xfds);
#endif
slirp_pollfds_fill(slirp->slirp, &tmo, net_slirp_add_poll, slirp);
/* Now wait for something to happen, or at most 'tmo' usec. */
#ifdef SLIRP_USE_POLL
ret = poll(slirp->pfd, slirp->pfd_len, tmo);
#else
if (tmo < 0)
tmo = 500;
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = tmo;
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, (ret <= 0), net_slirp_get_revents, slirp);
} }
#else
static int
net_slirp_get_revents(int idx, void *opaque)
{
net_slirp_t *slirp = (net_slirp_t *) opaque;
int ret = 0;
int events = slirp->pfd[idx].revents;
if (events & POLLIN)
ret |= SLIRP_POLL_IN;
if (events & POLLOUT)
ret |= SLIRP_POLL_OUT;
if (events & POLLPRI)
ret |= SLIRP_POLL_PRI;
if (events & POLLERR)
ret |= SLIRP_POLL_ERR;
if (events & POLLHUP)
ret |= SLIRP_POLL_HUP;
return ret;
}
#endif
static const SlirpCb slirp_cb = { static const SlirpCb slirp_cb = {
.send_packet = net_slirp_send_packet, .send_packet = net_slirp_send_packet,
@@ -299,31 +292,140 @@ static const SlirpCb slirp_cb = {
.notify = net_slirp_notify .notify = net_slirp_notify
}; };
/* Send a packet to the SLiRP interface. */
static void
net_slirp_in(net_slirp_t *slirp, uint8_t *pkt, int pkt_len)
{
if (!slirp)
return;
slirp_log("SLiRP: sending %d-byte packet to host network\n", pkt_len);
slirp_input(slirp->slirp, (const uint8_t *) pkt, pkt_len);
}
void
net_slirp_in_available(void *priv)
{
net_slirp_t *slirp = (net_slirp_t *)priv;
net_event_set(&slirp->tx_event);
}
#ifdef _WIN32
static void
net_slirp_thread(void *priv)
{
net_slirp_t *slirp = (net_slirp_t *) priv;
/* Start polling. */
slirp_log("SLiRP: polling started.\n");
HANDLE events[3];
events[NET_EVENT_STOP] = net_event_get_handle(&slirp->stop_event);
events[NET_EVENT_TX] = net_event_get_handle(&slirp->tx_event);
events[NET_EVENT_RX] = slirp->sock_event;
bool run = true;
while (run) {
uint32_t timeout = -1;
slirp_pollfds_fill(slirp->slirp, &timeout, net_slirp_add_poll, slirp);
if (timeout < 0)
timeout = INFINITE;
int ret = WaitForMultipleObjects(3, events, FALSE, (DWORD)timeout);
switch (ret - WAIT_OBJECT_0) {
case NET_EVENT_STOP:
run = false;
break;
case NET_EVENT_TX:
while (network_tx_pop(slirp->card, &slirp->pkt)) {
net_slirp_in(slirp, slirp->pkt.data, slirp->pkt.len);
}
break;
default:
slirp_pollfds_poll(slirp->slirp, ret == WAIT_FAILED, net_slirp_get_revents, slirp);
break;
}
}
slirp_log("SLiRP: polling stopped.\n");
}
#else
/* Handle the receiving of frames. */ /* Handle the receiving of frames. */
static void static void
poll_thread(void *arg) net_slirp_thread(void *priv)
{ {
slirp_t *slirp = (slirp_t *) arg; net_slirp_t *slirp = (net_slirp_t *) priv;
event_t *evt;
int tx;
/* Start polling. */
slirp_log("SLiRP: polling started.\n");
while (1) {
uint32_t timeout = -1;
slirp->pfd_len = 0;
net_slirp_add_poll(net_event_get_fd(&slirp->stop_event), SLIRP_POLL_IN, slirp);
net_slirp_add_poll(net_event_get_fd(&slirp->tx_event), SLIRP_POLL_IN, slirp);
slirp_pollfds_fill(slirp->slirp, &timeout, net_slirp_add_poll, slirp);
int ret = poll(slirp->pfd, slirp->pfd_len, timeout);
slirp_pollfds_poll(slirp->slirp, (ret < 0), net_slirp_get_revents, slirp);
if (slirp->pfd[NET_EVENT_STOP].revents & POLLIN) {
net_event_clear(&slirp->stop_event);
break;
}
if (slirp->pfd[NET_EVENT_TX].revents & POLLIN) {
net_event_clear(&slirp->tx_event);
if (network_tx_pop(slirp->card, &slirp->pkt)) {
net_slirp_in(slirp, slirp->pkt.data, slirp->pkt.len);
}
}
}
slirp_log("SLiRP: polling stopped.\n");
}
#endif
static int slirp_card_num = 2;
/* Initialize SLiRP for use. */
void *
net_slirp_init(const netcard_t *card, const uint8_t *mac_addr, void *priv)
{
slirp_log("SLiRP: initializing...\n"); slirp_log("SLiRP: initializing...\n");
net_slirp_t *slirp = calloc(1, sizeof(net_slirp_t));
memcpy(slirp->mac_addr, mac_addr, sizeof(slirp->mac_addr));
slirp->card = (netcard_t*)card;
#ifndef _WIN32
slirp->pfd_size = 16 * sizeof(struct pollfd);
slirp->pfd = malloc(slirp->pfd_size);
memset(slirp->pfd, 0, slirp->pfd_size);
#endif
/* Set the IP addresses to use. */ /* Set the IP addresses to use. */
struct in_addr net = { .s_addr = htonl(0x0a000200) }; /* 10.0.2.0 */ struct in_addr net = { .s_addr = htonl(0x0a000000 | (slirp_card_num << 8)) }; /* 10.0.x.0 */
struct in_addr mask = { .s_addr = htonl(0xffffff00) }; /* 255.255.255.0 */ struct in_addr mask = { .s_addr = htonl(0xffffff00) }; /* 255.255.255.0 */
struct in_addr host = { .s_addr = htonl(0x0a000202) }; /* 10.0.2.2 */ struct in_addr host = { .s_addr = htonl(0x0a000002 | (slirp_card_num << 8)) }; /* 10.0.x.2 */
struct in_addr dhcp = { .s_addr = htonl(0x0a00020f) }; /* 10.0.2.15 */ struct in_addr dhcp = { .s_addr = htonl(0x0a00000f | (slirp_card_num << 8)) }; /* 10.0.x.15 */
struct in_addr dns = { .s_addr = htonl(0x0a000203) }; /* 10.0.2.3 */ struct in_addr dns = { .s_addr = htonl(0x0a000003 | (slirp_card_num << 8)) }; /* 10.0.x.3 */
struct in_addr bind = { .s_addr = htonl(0x00000000) }; /* 0.0.0.0 */ struct in_addr bind = { .s_addr = htonl(0x00000000 | (slirp_card_num << 8)) }; /* 0.0.0.0 */
struct in6_addr ipv6_dummy = { 0 }; /* contents don't matter; we're not using IPv6 */ struct in6_addr ipv6_dummy = { 0 }; /* contents don't matter; we're not using IPv6 */
/* Initialize SLiRP. */ /* Initialize SLiRP. */
slirp->slirp = slirp_init(0, 1, net, mask, host, 0, ipv6_dummy, 0, ipv6_dummy, NULL, NULL, NULL, NULL, dhcp, dns, ipv6_dummy, NULL, NULL, &slirp_cb, arg); slirp->slirp = slirp_init(0, 1, net, mask, host, 0, ipv6_dummy, 0, ipv6_dummy, NULL, NULL, NULL, NULL, dhcp, dns, ipv6_dummy, NULL, NULL, &slirp_cb, slirp);
if (!slirp->slirp) { if (!slirp->slirp) {
slirp_log("SLiRP: initialization failed\n"); slirp_log("SLiRP: initialization failed\n");
return; free(slirp);
return NULL;
} }
/* Set up port forwarding. */ /* Set up port forwarding. */
@@ -352,123 +454,48 @@ poll_thread(void *arg)
i++; i++;
} }
/* Start polling. */ slirp->pkt.data = calloc(1, NET_MAX_FRAME);
slirp_log("SLiRP: polling started.\n"); net_event_init(&slirp->tx_event);
thread_set_event(slirp->poll_state); net_event_init(&slirp->stop_event);
#ifdef _WIN32
/* Create a waitable event. */ slirp->sock_event = CreateEvent(NULL, FALSE, FALSE, NULL);
evt = thread_create_event();
while (!slirp->stop) {
/* Request ownership of the queue. */
network_wait(1);
/* Stop processing if asked to. */
if (slirp->stop) {
network_wait(0);
break;
}
/* See if there is any work. */
slirp_tic(slirp);
/* Wait for the next packet to arrive - network_do_tx() is called from there. */
tx = network_tx_queue_check();
/* Release ownership of the queue. */
network_wait(0);
/* If we did not get anything, wait a while. */
if (!tx)
thread_wait_event(evt, 10);
}
/* No longer needed. */
if (evt)
thread_destroy_event(evt);
slirp_log("SLiRP: polling stopped.\n");
thread_set_event(slirp->poll_state);
/* Destroy event here to avoid a crash. */
slirp_log("SLiRP: thread ended\n");
thread_destroy_event(slirp->poll_state);
/* Free here instead of immediately freeing the global slirp on the main
thread to avoid a race condition. */
slirp_cleanup(slirp->slirp);
free(slirp);
}
/* Initialize SLiRP for use. */
int
net_slirp_init(void)
{
return 0;
}
/* Initialize SLiRP for use. */
int
net_slirp_reset(const netcard_t *card, uint8_t *mac)
{
slirp_t *new_slirp = malloc(sizeof(slirp_t));
memset(new_slirp, 0, sizeof(slirp_t));
new_slirp->mac = mac;
new_slirp->card = card;
#ifdef SLIRP_USE_POLL
new_slirp->pfd_size = 16 * sizeof(struct pollfd);
new_slirp->pfd = malloc(new_slirp->pfd_size);
memset(new_slirp->pfd, 0, new_slirp->pfd_size);
#endif #endif
/* Save the callback info. */
slirp = new_slirp;
slirp_log("SLiRP: creating thread...\n"); slirp_log("SLiRP: creating thread...\n");
slirp->poll_state = thread_create_event(); slirp->poll_tid = thread_create(net_slirp_thread, slirp);
slirp->poll_tid = thread_create(poll_thread, new_slirp);
thread_wait_event(slirp->poll_state, -1);
return 0; slirp_card_num++;
return slirp;
} }
void void
net_slirp_close(void) net_slirp_close(void *priv)
{ {
if (!slirp) if (!priv)
return; return;
net_slirp_t *slirp = (net_slirp_t *) priv;
slirp_log("SLiRP: closing\n"); slirp_log("SLiRP: closing\n");
/* Tell the polling thread to shut down. */ /* Tell the polling thread to shut down. */
slirp->stop = 1; net_event_set(&slirp->stop_event);
/* Tell the thread to terminate. */
if (slirp->poll_tid) {
/* Wait for the thread to finish. */ /* Wait for the thread to finish. */
slirp_log("SLiRP: waiting for thread to end...\n"); slirp_log("SLiRP: waiting for thread to end...\n");
thread_wait_event(slirp->poll_state, -1); thread_wait(slirp->poll_tid);
}
net_event_close(&slirp->tx_event);
/* Shutdown work is done by the thread on its local copy of slirp. */ net_event_close(&slirp->stop_event);
slirp = NULL; slirp_cleanup(slirp->slirp);
} free(slirp->pkt.data);
free(slirp);
slirp_card_num--;
/* Send a packet to the SLiRP interface. */
void
net_slirp_in(uint8_t *pkt, int pkt_len)
{
if (!slirp || !slirp->slirp)
return;
slirp_log("SLiRP: sending %d-byte packet\n", pkt_len);
slirp_input(slirp->slirp, (const uint8_t *) pkt, pkt_len);
} }
const netdrv_t net_slirp_drv = {
&net_slirp_in_available,
&net_slirp_init,
&net_slirp_close
};
/* Stubs to stand in for the parts of libslirp we skip compiling. */ /* Stubs to stand in for the parts of libslirp we skip compiling. */
void ncsi_input(void *slirp, const uint8_t *pkt, int pkt_len) {} void ncsi_input(void *slirp, const uint8_t *pkt, int pkt_len) {}

View File

@@ -58,6 +58,8 @@
#include <86box/pic.h> #include <86box/pic.h>
#include <86box/random.h> #include <86box/random.h>
#include <86box/device.h> #include <86box/device.h>
#include <86box/timer.h>
#include <86box/thread.h>
#include <86box/network.h> #include <86box/network.h>
#include <86box/net_dp8390.h> #include <86box/net_dp8390.h>
#include <86box/net_wd8003.h> #include <86box/net_wd8003.h>
@@ -696,7 +698,7 @@ wd_init(const device_t *info)
dev->ram_addr = device_get_config_hex20("ram_addr"); dev->ram_addr = device_get_config_hex20("ram_addr");
} }
dev->dp8390 = device_add(&dp8390_device); dev->dp8390 = device_add_inst(&dp8390_device, dp3890_inst++);
dev->dp8390->priv = dev; dev->dp8390->priv = dev;
dev->dp8390->interrupt = wd_interrupt; dev->dp8390->interrupt = wd_interrupt;
dp8390_set_defaults(dev->dp8390, DP8390_FLAG_CHECK_CR | DP8390_FLAG_CLEAR_IRQ); dp8390_set_defaults(dev->dp8390, DP8390_FLAG_CHECK_CR | DP8390_FLAG_CLEAR_IRQ);
@@ -786,7 +788,7 @@ wd_init(const device_t *info)
mem_mapping_disable(&dev->ram_mapping); mem_mapping_disable(&dev->ram_mapping);
/* Attach ourselves to the network module. */ /* Attach ourselves to the network module. */
network_attach(dev->dp8390, dev->dp8390->physaddr, dp8390_rx, NULL, NULL); dev->dp8390->card = network_attach(dev->dp8390, dev->dp8390->physaddr, dp8390_rx, NULL, NULL);
if (!(dev->board_chip & WE_ID_BUS_MCA)) { if (!(dev->board_chip & WE_ID_BUS_MCA)) {
wdlog("%s: attached IO=0x%X IRQ=%d, RAM addr=0x%06x\n", dev->name, wdlog("%s: attached IO=0x%X IRQ=%d, RAM addr=0x%06x\n", dev->name,

View File

@@ -56,6 +56,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <wchar.h> #include <wchar.h>
#include <time.h> #include <time.h>
#include <stdbool.h>
#define HAVE_STDARG_H #define HAVE_STDARG_H
#include <86box/86box.h> #include <86box/86box.h>
#include <86box/device.h> #include <86box/device.h>
@@ -63,6 +64,7 @@
#include <86box/plat.h> #include <86box/plat.h>
#include <86box/thread.h> #include <86box/thread.h>
#include <86box/ui.h> #include <86box/ui.h>
#include <86box/timer.h>
#include <86box/network.h> #include <86box/network.h>
#include <86box/net_3c503.h> #include <86box/net_3c503.h>
#include <86box/net_ne2000.h> #include <86box/net_ne2000.h>
@@ -70,6 +72,11 @@
#include <86box/net_plip.h> #include <86box/net_plip.h>
#include <86box/net_wd8003.h> #include <86box/net_wd8003.h>
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winsock2.h>
#endif
static const device_t net_none_device = { static const device_t net_none_device = {
.name = "None", .name = "None",
@@ -86,32 +93,32 @@ static const device_t net_none_device = {
}; };
static netcard_t net_cards[] = { static const device_t *net_cards[] = {
// clang-format off &net_none_device,
{ &net_none_device, NULL }, &threec503_device,
{ &threec503_device, NULL }, &pcnet_am79c960_device,
{ &pcnet_am79c960_device, NULL }, &pcnet_am79c961_device,
{ &pcnet_am79c961_device, NULL }, &ne1000_device,
{ &ne1000_device, NULL }, &ne2000_device,
{ &ne2000_device, NULL }, &pcnet_am79c960_eb_device,
{ &pcnet_am79c960_eb_device, NULL }, &rtl8019as_device,
{ &rtl8019as_device, NULL }, &wd8003e_device,
{ &wd8003e_device, NULL }, &wd8003eb_device,
{ &wd8003eb_device, NULL }, &wd8013ebt_device,
{ &wd8013ebt_device, NULL }, &plip_device,
{ &plip_device, NULL }, &ethernext_mc_device,
{ &ethernext_mc_device, NULL }, &wd8003eta_device,
{ &wd8003eta_device, NULL }, &wd8003ea_device,
{ &wd8003ea_device, NULL }, &wd8013epa_device,
{ &wd8013epa_device, NULL }, &pcnet_am79c973_device,
{ &pcnet_am79c973_device, NULL }, &pcnet_am79c970a_device,
{ &pcnet_am79c970a_device, NULL }, &rtl8029as_device,
{ &rtl8029as_device, NULL }, &pcnet_am79c960_vlb_device,
{ &pcnet_am79c960_vlb_device, NULL }, NULL
{ NULL, NULL }
// clang-format off
}; };
netcard_conf_t net_cards_conf[NET_CARD_MAX];
int net_card_current = 0;
/* Global variables. */ /* Global variables. */
int network_type; int network_type;
@@ -119,20 +126,9 @@ int network_ndev;
int network_card; int network_card;
char network_host[522]; char network_host[522];
netdev_t network_devs[32]; netdev_t network_devs[32];
int network_rx_pause = 0,
network_tx_pause = 0;
/* Local variables. */ /* Local variables. */
static volatile atomic_int net_wait = 0;
static mutex_t *network_mutex;
static uint8_t *network_mac;
static uint8_t network_timer_active = 0;
static pc_timer_t network_rx_queue_timer;
static netpkt_t *first_pkt[3] = { NULL, NULL, NULL },
*last_pkt[3] = { NULL, NULL, NULL };
static netpkt_t queued_pkt;
#ifdef ENABLE_NETWORK_LOG #ifdef ENABLE_NETWORK_LOG
int network_do_log = ENABLE_NETWORK_LOG; int network_do_log = ENABLE_NETWORK_LOG;
@@ -191,15 +187,13 @@ network_dump_packet(netpkt_t *pkt)
#endif #endif
void #ifdef _WIN32
network_wait(uint8_t wait) static void
network_winsock_clean(void)
{ {
if (wait) WSACleanup();
thread_wait_mutex(network_mutex);
else
thread_release_mutex(network_mutex);
} }
#endif
/* /*
* Initialize the configured network cards. * Initialize the configured network cards.
@@ -213,9 +207,11 @@ network_init(void)
{ {
int i; int i;
/* Initialize to a known state. */ #ifdef _WIN32
network_type = NET_TYPE_NONE; WSADATA Data;
network_card = 0; WSAStartup(MAKEWORD(2, 0), &Data);
atexit(network_winsock_clean);
#endif
/* Create a first device entry that's always there, as needed by UI. */ /* Create a first device entry that's always there, as needed by UI. */
strcpy(network_devs[0].device, "none"); strcpy(network_devs[0].device, "none");
@@ -247,156 +243,133 @@ network_init(void)
#endif #endif
} }
static void
void network_queue_init(netqueue_t *queue)
network_queue_put(int tx, void *priv, uint8_t *data, int len)
{ {
netpkt_t *temp; queue->size = NET_QUEUE_LEN;
queue->head = queue->tail = 0;
temp = (netpkt_t *) calloc(sizeof(netpkt_t), 1); for (int i=0; i<queue->size; i++) {
temp->priv = priv; queue->packets[i].data = calloc(1, NET_MAX_FRAME);
memcpy(temp->data, data, len); queue->packets[i].len = 0;
temp->len = len;
temp->prev = last_pkt[tx];
temp->next = NULL;
if (last_pkt[tx] != NULL)
last_pkt[tx]->next = temp;
last_pkt[tx] = temp;
if (first_pkt[tx] == NULL)
first_pkt[tx] = temp;
} }
}
static bool
network_queue_full(netqueue_t *queue)
{
return ((queue->head + 1) % queue->size) == queue->tail;
}
static bool
network_queue_empty(netqueue_t *queue)
{
return (queue->head == queue->tail);
}
int
network_queue_put(netqueue_t *queue, uint8_t *data, int len)
{
if (len > NET_MAX_FRAME || network_queue_full(queue)) {
return 0;
}
netpkt_t *pkt = &queue->packets[queue->head];
memcpy(pkt->data, data, len);
pkt->len = len;
queue->head = (queue->head + 1) % queue->size;
return 1;
}
static int
network_queue_get(netqueue_t *queue, netpkt_t *dst_pkt) {
if (network_queue_empty(queue))
return 0;
netpkt_t *pkt = &queue->packets[queue->tail];
memcpy(dst_pkt->data, pkt->data, pkt->len);
dst_pkt->len = pkt->len;
queue->tail = (queue->tail + 1) % queue->size;
return 1;
}
static int
network_queue_move(netqueue_t *dst_q, netqueue_t *src_q)
{
if (network_queue_empty(src_q))
return 0;
if (network_queue_full(dst_q)) {
return 0;
}
netpkt_t *src_pkt = &src_q->packets[src_q->tail];
netpkt_t *dst_pkt = &dst_q->packets[dst_q->head];
uint8_t *tmp_dat = dst_pkt->data;
dst_pkt->data = src_pkt->data;
dst_pkt->len = src_pkt->len;
dst_q->head = (dst_q->head + 1) % dst_q->size;
src_pkt->data = tmp_dat;
src_pkt->len = 0;
src_q->tail = (src_q->tail + 1) % src_q->size;
return 1;
}
static void static void
network_queue_get(int tx, netpkt_t *pkt) network_queue_clear(netqueue_t *queue)
{ {
netpkt_t *temp; for (int i=0; i<queue->size; i++) {
free(queue->packets[i].data);
temp = first_pkt[tx]; queue->packets[i].len = 0;
if (temp == NULL) {
memset(pkt, 0x00, sizeof(netpkt_t));
return;
} }
queue->tail = queue->head = 0;
memcpy(pkt, temp, sizeof(netpkt_t));
first_pkt[tx] = temp->next;
free(temp);
if (first_pkt[tx] == NULL)
last_pkt[tx] = NULL;
}
static void
network_queue_transmit(int tx)
{
netpkt_t *temp;
temp = first_pkt[tx];
if (temp == NULL)
return;
if (temp->len > 0) {
network_dump_packet(temp);
/* Why on earth is this not a function pointer?! */
switch(network_type) {
case NET_TYPE_PCAP:
net_pcap_in(temp->data, temp->len);
break;
case NET_TYPE_SLIRP:
net_slirp_in(temp->data, temp->len);
break;
}
}
first_pkt[tx] = temp->next;
free(temp);
if (first_pkt[tx] == NULL)
last_pkt[tx] = NULL;
}
static void
network_queue_copy(int dest, int src)
{
netpkt_t *temp, *temp2;
temp = first_pkt[src];
if (temp == NULL)
return;
temp2 = (netpkt_t *) calloc(sizeof(netpkt_t), 1);
temp2->priv = temp->priv;
memcpy(temp2->data, temp->data, temp->len);
temp2->len = temp->len;
temp2->prev = last_pkt[dest];
temp2->next = NULL;
if (last_pkt[dest] != NULL)
last_pkt[dest]->next = temp2;
last_pkt[dest] = temp2;
if (first_pkt[dest] == NULL)
first_pkt[dest] = temp2;
first_pkt[src] = temp->next;
free(temp);
if (first_pkt[src] == NULL)
last_pkt[src] = NULL;
}
static void
network_queue_clear(int tx)
{
netpkt_t *temp = first_pkt[tx], *temp2;
if (temp == NULL)
return;
do {
temp2 = temp->next;
free(temp);
temp = temp2;
} while (temp != NULL);
first_pkt[tx] = last_pkt[tx] = NULL;
} }
static void static void
network_rx_queue(void *priv) network_rx_queue(void *priv)
{ {
int ret = 1; netcard_t *card = (netcard_t *)priv;
double timer_period;
int ret = 0;
if (network_rx_pause || !thread_test_mutex(network_mutex)) { bool activity = false;
timer_on_auto(&network_rx_queue_timer, 0.762939453125 * 2.0 * 128.0);
return; if (card->queued_pkt.len == 0) {
thread_wait_mutex(card->rx_mutex);
network_queue_get(&card->queues[NET_QUEUE_RX], &card->queued_pkt);
thread_release_mutex(card->rx_mutex);
} }
if (queued_pkt.len == 0) if (card->queued_pkt.len > 0) {
network_queue_get(0, &queued_pkt); network_dump_packet(&card->queued_pkt);
if (queued_pkt.len > 0) { ret = card->rx(card->card_drv, card->queued_pkt.data, card->queued_pkt.len);
network_dump_packet(&queued_pkt);
ret = net_cards[network_card].rx(queued_pkt.priv, queued_pkt.data, queued_pkt.len);
} }
timer_on_auto(&network_rx_queue_timer, 0.762939453125 * 2.0 * ((queued_pkt.len >= 128) ? ((double) queued_pkt.len) : 128.0));
if (ret) if (ret) {
queued_pkt.len = 0; activity = true;
timer_period = 0.762939453125 * ((card->queued_pkt.len >= 128) ? ((double) card->queued_pkt.len) : 128.0);
card->queued_pkt.len = 0;
} else {
timer_period = 0.762939453125 * 128.0;
}
timer_on_auto(&card->timer, timer_period);
/* Transmission. */ /* Transmission. */
network_queue_copy(1, 2); thread_wait_mutex(card->tx_mutex);
ret = network_queue_move(&card->queues[NET_QUEUE_TX_HOST], &card->queues[NET_QUEUE_TX_VM]);
thread_release_mutex(card->tx_mutex);
if (ret) {
/* Notify host that a packet is available in the TX queue */
card->host_drv.notify_in(card->host_drv.priv);
activity = true;
}
network_wait(0); ui_sb_update_icon(SB_NETWORK, activity);
} }
@@ -407,51 +380,67 @@ network_rx_queue(void *priv)
* finished initializing itself, to link itself to the platform support * finished initializing itself, to link itself to the platform support
* modules. * modules.
*/ */
void netcard_t *
network_attach(void *dev, uint8_t *mac, NETRXCB rx, NETWAITCB wait, NETSETLINKSTATE set_link_state) network_attach(void *card_drv, uint8_t *mac, NETRXCB rx, NETWAITCB wait, NETSETLINKSTATE set_link_state)
{ {
if (network_card == 0) return; netcard_t *card = calloc(1, sizeof(netcard_t));
card->queued_pkt.data = calloc(1, NET_MAX_FRAME);
card->card_drv = card_drv;
card->rx = rx;
card->wait = wait;
card->set_link_state = set_link_state;
card->tx_mutex = thread_create_mutex();
card->rx_mutex = thread_create_mutex();
/* Save the card's info. */ for (int i=0; i<3; i++) {
net_cards[network_card].priv = dev; network_queue_init(&card->queues[i]);
net_cards[network_card].rx = rx; }
net_cards[network_card].wait = wait;
net_cards[network_card].set_link_state = set_link_state;
network_mac = mac;
network_set_wait(0);
/* Activate the platform module. */
switch(network_type) {
case NET_TYPE_PCAP:
(void)net_pcap_reset(&net_cards[network_card], network_mac);
break;
switch (net_cards_conf[net_card_current].net_type) {
case NET_TYPE_SLIRP: case NET_TYPE_SLIRP:
(void)net_slirp_reset(&net_cards[network_card], network_mac); default:
card->host_drv = net_slirp_drv;
card->host_drv.priv = card->host_drv.init(card, mac, NULL);
break;
case NET_TYPE_PCAP:
card->host_drv = net_pcap_drv;
card->host_drv.priv = card->host_drv.init(card, mac, net_cards_conf[net_card_current].host_dev_name);
break; break;
} }
first_pkt[0] = first_pkt[1] = first_pkt[2] = NULL; if (!card->host_drv.priv) {
last_pkt[0] = last_pkt[1] = last_pkt[2] = NULL; thread_close_mutex(card->tx_mutex);
memset(&queued_pkt, 0x00, sizeof(netpkt_t)); thread_close_mutex(card->rx_mutex);
memset(&network_rx_queue_timer, 0x00, sizeof(pc_timer_t)); for (int i=0; i<3; i++) {
timer_add(&network_rx_queue_timer, network_rx_queue, NULL, 0); network_queue_clear(&card->queues[i]);
/* 10 mbps. */
timer_on_auto(&network_rx_queue_timer, 0.762939453125 * 2.0);
network_timer_active = 1;
} }
free(card->queued_pkt.data);
free(card);
return NULL;
}
timer_add(&card->timer, network_rx_queue, card, 0);
timer_on_auto(&card->timer, 0.762939453125 * 2.0);
return card;
}
/* Stop the network timer. */
void void
network_timer_stop(void) netcard_close(netcard_t *card)
{ {
if (network_timer_active) { timer_stop(&card->timer);
timer_stop(&network_rx_queue_timer); card->host_drv.close(card->host_drv.priv);
memset(&network_rx_queue_timer, 0x00, sizeof(pc_timer_t));
network_timer_active = 0; thread_close_mutex(card->tx_mutex);
thread_close_mutex(card->rx_mutex);
for (int i=0; i<3; i++) {
network_queue_clear(&card->queues[i]);
} }
free(card->queued_pkt.data);
free(card);
} }
@@ -459,30 +448,11 @@ network_timer_stop(void)
void void
network_close(void) network_close(void)
{ {
network_timer_stop();
/* If already closed, do nothing. */
if (network_mutex == NULL) return;
/* Force-close the PCAP module. */
net_pcap_close();
/* Force-close the SLIRP module. */
net_slirp_close();
/* Close the network thread mutex. */
thread_close_mutex(network_mutex);
network_mutex = NULL;
network_mac = NULL;
#ifdef ENABLE_NETWORK_LOG #ifdef ENABLE_NETWORK_LOG
thread_close_mutex(network_dump_mutex); thread_close_mutex(network_dump_mutex);
network_dump_mutex = NULL; network_dump_mutex = NULL;
#endif #endif
/* Here is where we clear the queues. */
network_queue_clear(0);
network_queue_clear(1);
network_log("NETWORK: closed.\n"); network_log("NETWORK: closed.\n");
} }
@@ -500,86 +470,52 @@ network_reset(void)
{ {
int i = -1; int i = -1;
network_log("NETWORK: reset (type=%d, card=%d)\n",
network_type, network_card);
ui_sb_update_icon(SB_NETWORK, 0); ui_sb_update_icon(SB_NETWORK, 0);
/* Just in case.. */
network_close();
/* If no active card, we're done. */
if ((network_type==NET_TYPE_NONE) || (network_card==0)) return;
network_mutex = thread_create_mutex();
#ifdef ENABLE_NETWORK_LOG #ifdef ENABLE_NETWORK_LOG
network_dump_mutex = thread_create_mutex(); network_dump_mutex = thread_create_mutex();
#endif #endif
/* Initialize the platform module. */ for (i = 0; i < NET_CARD_MAX; i++) {
switch(network_type) { if (!net_cards_conf[i].device_num || net_cards_conf[i].net_type == NET_TYPE_NONE ||
case NET_TYPE_PCAP: (net_cards_conf[i].net_type == NET_TYPE_PCAP && !strcmp(net_cards_conf[i].host_dev_name, "none"))) {
i = net_pcap_init(); continue;
break;
case NET_TYPE_SLIRP:
i = net_slirp_init();
break;
} }
if (i < 0) { net_card_current = i;
/* Tell user we can't do this (at the moment.) */ device_add_inst(net_cards[net_cards_conf[i].device_num], i + 1);
ui_msgbox_header(MBX_ERROR, (wchar_t *) IDS_2093, (wchar_t *) IDS_2129);
// FIXME: we should ask in the dialog if they want to
// reconfigure or quit, and throw them into the
// Settings dialog if yes.
/* Disable network. */
network_type = NET_TYPE_NONE;
return;
}
network_log("NETWORK: set up for %s, card='%s'\n",
(network_type==NET_TYPE_SLIRP)?"SLiRP":"Pcap",
net_cards[network_card].device->name);
/* Add the (new?) card to the I/O system. */
if (net_cards[network_card].device) {
network_log("NETWORK: adding device '%s'\n",
net_cards[network_card].device->name);
device_add(net_cards[network_card].device);
} }
} }
/* Queue a packet for transmission to one of the network providers. */ /* Queue a packet for transmission to one of the network providers. */
void void
network_tx(uint8_t *bufp, int len) network_tx(netcard_t *card, uint8_t *bufp, int len)
{ {
ui_sb_update_icon(SB_NETWORK, 1); network_queue_put(&card->queues[NET_QUEUE_TX_VM], bufp, len);
network_queue_put(2, NULL, bufp, len);
ui_sb_update_icon(SB_NETWORK, 0);
} }
int network_tx_pop(netcard_t *card, netpkt_t *out_pkt)
/* Actually transmit the packet. */
int
network_tx_queue_check(void)
{ {
if ((first_pkt[1] == NULL) && (last_pkt[1] == NULL)) int ret = 0;
return 0;
if (network_tx_pause) thread_wait_mutex(card->tx_mutex);
return 1; ret = network_queue_get(&card->queues[NET_QUEUE_TX_HOST], out_pkt);
thread_release_mutex(card->tx_mutex);
network_queue_transmit(1); return ret;
return 1;
} }
int network_rx_put(netcard_t *card, uint8_t *bufp, int len)
{
int ret = 0;
thread_wait_mutex(card->rx_mutex);
ret = network_queue_put(&card->queues[NET_QUEUE_RX], bufp, len);
thread_release_mutex(card->rx_mutex);
return ret;
}
int int
network_dev_to_id(char *devname) network_dev_to_id(char *devname)
@@ -601,9 +537,13 @@ network_dev_to_id(char *devname)
int int
network_available(void) network_available(void)
{ {
if ((network_type == NET_TYPE_NONE) || (network_card == 0)) return(0); int available = 0;
return(1); for (int i = 0; i < NET_CARD_MAX; i ++) {
available |= (net_cards_conf[i].device_num > 0) && (net_cards_conf[i].net_type != NET_TYPE_NONE);
}
return available;
} }
@@ -611,8 +551,8 @@ network_available(void)
int int
network_card_available(int card) network_card_available(int card)
{ {
if (net_cards[card].device) if (net_cards[card])
return(device_available(net_cards[card].device)); return(device_available(net_cards[card]));
return(1); return(1);
} }
@@ -622,7 +562,7 @@ network_card_available(int card)
const device_t * const device_t *
network_card_getdevice(int card) network_card_getdevice(int card)
{ {
return(net_cards[card].device); return(net_cards[card]);
} }
@@ -630,9 +570,9 @@ network_card_getdevice(int card)
int int
network_card_has_config(int card) network_card_has_config(int card)
{ {
if (! net_cards[card].device) return(0); if (!net_cards[card]) return(0);
return(device_has_config(net_cards[card].device) ? 1 : 0); return(device_has_config(net_cards[card]) ? 1 : 0);
} }
@@ -640,7 +580,7 @@ network_card_has_config(int card)
char * char *
network_card_get_internal_name(int card) network_card_get_internal_name(int card)
{ {
return device_get_internal_name(net_cards[card].device); return device_get_internal_name(net_cards[card]);
} }
@@ -650,28 +590,11 @@ network_card_get_from_internal_name(char *s)
{ {
int c = 0; int c = 0;
while (net_cards[c].device != NULL) { while (net_cards[c] != NULL) {
if (! strcmp((char *)net_cards[c].device->internal_name, s)) if (! strcmp((char *)net_cards[c]->internal_name, s))
return(c); return(c);
c++; c++;
} }
return 0; return 0;
} }
void
network_set_wait(int wait)
{
net_wait = wait;
}
int
network_get_wait(void)
{
int ret;
ret = net_wait;
return ret;
}

View File

@@ -37,6 +37,7 @@ extern uint64_t tsc;
#include <86box/mo.h> #include <86box/mo.h>
#include <86box/plat.h> #include <86box/plat.h>
#include <86box/machine.h> #include <86box/machine.h>
#include <86box/thread.h>
#include <86box/network.h> #include <86box/network.h>
#include <86box/ui.h> #include <86box/ui.h>
#include <86box/machine_status.h> #include <86box/machine_status.h>

View File

@@ -21,20 +21,29 @@ extern "C" {
#include <86box/86box.h> #include <86box/86box.h>
#include <86box/device.h> #include <86box/device.h>
#include <86box/machine.h> #include <86box/machine.h>
#include <86box/timer.h>
#include <86box/thread.h>
#include <86box/network.h> #include <86box/network.h>
} }
#include "qt_models_common.hpp" #include "qt_models_common.hpp"
#include "qt_deviceconfig.hpp" #include "qt_deviceconfig.hpp"
static void enableElements(Ui::SettingsNetwork *ui) { void SettingsNetwork::enableElements(Ui::SettingsNetwork *ui) {
int netType = ui->comboBoxNetwork->currentData().toInt(); for (int i = 0; i < NET_CARD_MAX; ++i) {
ui->comboBoxPcap->setEnabled(netType == NET_TYPE_PCAP); auto* nic_cbox = findChild<QComboBox*>(QString("comboBoxNIC%1").arg(i+1));
auto* net_type_cbox = findChild<QComboBox*>(QString("comboBoxNet%1").arg(i+1));
auto* intf_cbox = findChild<QComboBox*>(QString("comboBoxIntf%1").arg(i+1));
auto* conf_btn = findChild<QPushButton*>(QString("pushButtonConf%1").arg(i+1));
int netType = net_type_cbox->currentData().toInt();
bool adaptersEnabled = netType == NET_TYPE_SLIRP || bool adaptersEnabled = netType == NET_TYPE_SLIRP ||
(netType == NET_TYPE_PCAP && ui->comboBoxPcap->currentData().toInt() > 0); (netType == NET_TYPE_PCAP && intf_cbox->currentData().toInt() > 0);
ui->comboBoxAdapter->setEnabled(adaptersEnabled);
ui->pushButtonConfigure->setEnabled(adaptersEnabled && ui->comboBoxAdapter->currentIndex() > 0 && network_card_has_config(ui->comboBoxAdapter->currentData().toInt())); intf_cbox->setEnabled(net_type_cbox->currentData().toInt() == NET_TYPE_PCAP);
nic_cbox->setEnabled(adaptersEnabled);
conf_btn->setEnabled(adaptersEnabled && network_card_has_config(nic_cbox->currentData().toInt()));
}
} }
SettingsNetwork::SettingsNetwork(QWidget *parent) : SettingsNetwork::SettingsNetwork(QWidget *parent) :
@@ -43,27 +52,16 @@ SettingsNetwork::SettingsNetwork(QWidget *parent) :
{ {
ui->setupUi(this); ui->setupUi(this);
auto* model = ui->comboBoxNetwork->model();
Models::AddEntry(model, tr("None"), NET_TYPE_NONE);
Models::AddEntry(model, "PCap", NET_TYPE_PCAP);
Models::AddEntry(model, "SLiRP", NET_TYPE_SLIRP);
ui->comboBoxNetwork->setCurrentIndex(network_type);
int selectedRow = 0;
model = ui->comboBoxPcap->model();
QString currentPcapDevice = network_host;
for (int c = 0; c < network_ndev; c++) {
Models::AddEntry(model, tr(network_devs[c].description), c);
if (QString(network_devs[c].device) == currentPcapDevice) {
selectedRow = c;
}
}
ui->comboBoxPcap->setCurrentIndex(-1);
ui->comboBoxPcap->setCurrentIndex(selectedRow);
onCurrentMachineChanged(machine); onCurrentMachineChanged(machine);
enableElements(ui); enableElements(ui);
for (int i = 0; i < NET_CARD_MAX; i++) {
auto* nic_cbox = findChild<QComboBox*>(QString("comboBoxNIC%1").arg(i+1));
auto* net_type_cbox = findChild<QComboBox*>(QString("comboBoxNet%1").arg(i+1));
auto* intf_cbox = findChild<QComboBox*>(QString("comboBoxIntf%1").arg(i+1));
connect(nic_cbox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &SettingsNetwork::on_comboIndexChanged);
connect(net_type_cbox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &SettingsNetwork::on_comboIndexChanged);
connect(intf_cbox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &SettingsNetwork::on_comboIndexChanged);
}
} }
SettingsNetwork::~SettingsNetwork() SettingsNetwork::~SettingsNetwork()
@@ -72,19 +70,30 @@ SettingsNetwork::~SettingsNetwork()
} }
void SettingsNetwork::save() { void SettingsNetwork::save() {
network_type = ui->comboBoxNetwork->currentData().toInt(); for (int i = 0; i < NET_CARD_MAX; ++i) {
memset(network_host, '\0', sizeof(network_host)); auto* cbox = findChild<QComboBox*>(QString("comboBoxNIC%1").arg(i+1));
strcpy(network_host, network_devs[ui->comboBoxPcap->currentData().toInt()].device); net_cards_conf[i].device_num = cbox->currentData().toInt();
network_card = ui->comboBoxAdapter->currentData().toInt(); cbox = findChild<QComboBox*>(QString("comboBoxNet%1").arg(i+1));
net_cards_conf[i].net_type = cbox->currentData().toInt();
cbox = findChild<QComboBox*>(QString("comboBoxIntf%1").arg(i+1));
memset(net_cards_conf[i].host_dev_name, '\0', sizeof(net_cards_conf[i].host_dev_name));
strncpy(net_cards_conf[i].host_dev_name, network_devs[cbox->currentData().toInt()].device, sizeof(net_cards_conf[i].host_dev_name) - 1);
}
} }
void SettingsNetwork::onCurrentMachineChanged(int machineId) { void SettingsNetwork::onCurrentMachineChanged(int machineId) {
this->machineId = machineId; this->machineId = machineId;
auto* model = ui->comboBoxAdapter->model();
auto removeRows = model->rowCount();
int c = 0; int c = 0;
int selectedRow = 0; int selectedRow = 0;
for (int i = 0; i < NET_CARD_MAX; ++i) {
auto* cbox = findChild<QComboBox*>(QString("comboBoxNIC%1").arg(i+1));
auto *model = cbox->model();
auto removeRows = model->rowCount();
c = 0;
selectedRow = 0;
while (true) { while (true) {
auto name = DeviceConfig::DeviceName(network_card_getdevice(c), network_card_get_internal_name(c), 1); auto name = DeviceConfig::DeviceName(network_card_getdevice(c), network_card_get_internal_name(c), 1);
if (name.isEmpty()) { if (name.isEmpty()) {
@@ -93,20 +102,43 @@ void SettingsNetwork::onCurrentMachineChanged(int machineId) {
if (network_card_available(c) && device_is_valid(network_card_getdevice(c), machineId)) { if (network_card_available(c) && device_is_valid(network_card_getdevice(c), machineId)) {
int row = Models::AddEntry(model, name, c); int row = Models::AddEntry(model, name, c);
if (c == network_card) { if (c == net_cards_conf[i].device_num) {
selectedRow = row - removeRows; selectedRow = row - removeRows;
} }
} }
c++; c++;
} }
model->removeRows(0, removeRows); model->removeRows(0, removeRows);
ui->comboBoxAdapter->setEnabled(model->rowCount() > 0); cbox->setEnabled(model->rowCount() > 0);
ui->comboBoxAdapter->setCurrentIndex(-1); cbox->setCurrentIndex(-1);
ui->comboBoxAdapter->setCurrentIndex(selectedRow); cbox->setCurrentIndex(selectedRow);
cbox = findChild<QComboBox*>(QString("comboBoxNet%1").arg(i+1));
model = cbox->model();
Models::AddEntry(model, tr("None"), NET_TYPE_NONE);
Models::AddEntry(model, "SLiRP", NET_TYPE_SLIRP);
if (network_ndev > 1) {
Models::AddEntry(model, "PCap", NET_TYPE_PCAP);
}
cbox->setCurrentIndex(net_cards_conf[i].net_type);
selectedRow = 0;
QString currentPcapDevice = net_cards_conf[i].host_dev_name;
cbox = findChild<QComboBox*>(QString("comboBoxIntf%1").arg(i+1));
model = cbox->model();
for (int c = 0; c < network_ndev; c++) {
Models::AddEntry(model, tr(network_devs[c].description), c);
if (QString(network_devs[c].device) == currentPcapDevice) {
selectedRow = c;
}
}
cbox->setCurrentIndex(selectedRow);
}
} }
void SettingsNetwork::on_comboBoxNetwork_currentIndexChanged(int index) { void SettingsNetwork::on_comboIndexChanged(int index) {
if (index < 0) { if (index < 0) {
return; return;
} }
@@ -114,24 +146,18 @@ void SettingsNetwork::on_comboBoxNetwork_currentIndexChanged(int index) {
enableElements(ui); enableElements(ui);
} }
void SettingsNetwork::on_comboBoxAdapter_currentIndexChanged(int index) { void SettingsNetwork::on_pushButtonConf1_clicked() {
if (index < 0) { DeviceConfig::ConfigureDevice(network_card_getdevice(ui->comboBoxNIC1->currentData().toInt()), 0, qobject_cast<Settings*>(Settings::settings));
return;
} }
enableElements(ui); void SettingsNetwork::on_pushButtonConf2_clicked() {
DeviceConfig::ConfigureDevice(network_card_getdevice(ui->comboBoxNIC2->currentData().toInt()), 0, qobject_cast<Settings*>(Settings::settings));
} }
void SettingsNetwork::on_pushButtonConfigure_clicked() { void SettingsNetwork::on_pushButtonConf3_clicked() {
DeviceConfig::ConfigureDevice(network_card_getdevice(ui->comboBoxAdapter->currentData().toInt()), 0, qobject_cast<Settings*>(Settings::settings)); DeviceConfig::ConfigureDevice(network_card_getdevice(ui->comboBoxNIC3->currentData().toInt()), 0, qobject_cast<Settings*>(Settings::settings));
} }
void SettingsNetwork::on_pushButtonConf4_clicked() {
void SettingsNetwork::on_comboBoxPcap_currentIndexChanged(int index) DeviceConfig::ConfigureDevice(network_card_getdevice(ui->comboBoxNIC4->currentData().toInt()), 0, qobject_cast<Settings*>(Settings::settings));
{
if (index < 0) {
return;
}
enableElements(ui);
} }

View File

@@ -21,11 +21,13 @@ public slots:
void onCurrentMachineChanged(int machineId); void onCurrentMachineChanged(int machineId);
private slots: private slots:
void on_pushButtonConfigure_clicked(); void on_pushButtonConf1_clicked();
void on_comboBoxAdapter_currentIndexChanged(int index); void on_pushButtonConf2_clicked();
void on_comboBoxNetwork_currentIndexChanged(int index); void on_pushButtonConf3_clicked();
void on_pushButtonConf4_clicked();
void on_comboIndexChanged(int index);
void on_comboBoxPcap_currentIndexChanged(int index); void enableElements(Ui::SettingsNetwork *ui);
private: private:
Ui::SettingsNetwork *ui; Ui::SettingsNetwork *ui;

View File

@@ -6,14 +6,14 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>400</width> <width>548</width>
<height>300</height> <height>458</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>Form</string> <string>Form</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout"> <layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin"> <property name="leftMargin">
<number>0</number> <number>0</number>
</property> </property>
@@ -26,7 +26,291 @@
<property name="bottomMargin"> <property name="bottomMargin">
<number>0</number> <number>0</number>
</property> </property>
<item row="6" column="0"> <item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Network Interface Contollers</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="4">
<widget class="QLabel" name="label_7">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Adapter</string>
</property>
</widget>
</item>
<item row="4" column="3">
<widget class="QComboBox" name="comboBoxIntf4">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="3" column="3">
<widget class="QComboBox" name="comboBoxIntf3">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="3" column="5">
<widget class="QPushButton" name="pushButtonConf3">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Configure</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QComboBox" name="comboBoxNet1">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_6">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Card 3:</string>
</property>
</widget>
</item>
<item row="4" column="4">
<widget class="QComboBox" name="comboBoxNIC4">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_3">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Card 1:</string>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QLabel" name="label_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Interface</string>
</property>
</widget>
</item>
<item row="1" column="4">
<widget class="QComboBox" name="comboBoxNIC1">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum>
</property>
</widget>
</item>
<item row="1" column="5">
<widget class="QPushButton" name="pushButtonConf1">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Configure</string>
</property>
</widget>
</item>
<item row="3" column="4">
<widget class="QComboBox" name="comboBoxNIC3">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum>
</property>
</widget>
</item>
<item row="4" column="2">
<widget class="QComboBox" name="comboBoxNet4">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_5">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Card 4:</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Mode</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_4">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Card 2:</string>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QComboBox" name="comboBoxNet3">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QComboBox" name="comboBoxNet2">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="2" column="5">
<widget class="QPushButton" name="pushButtonConf2">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Configure</string>
</property>
</widget>
</item>
<item row="4" column="5">
<widget class="QPushButton" name="pushButtonConf4">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Configure</string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QComboBox" name="comboBoxIntf1">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="2" column="3">
<widget class="QComboBox" name="comboBoxIntf2">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="2" column="4">
<widget class="QComboBox" name="comboBoxNIC2">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer"> <spacer name="verticalSpacer">
<property name="orientation"> <property name="orientation">
<enum>Qt::Vertical</enum> <enum>Qt::Vertical</enum>
@@ -39,57 +323,6 @@
</property> </property>
</spacer> </spacer>
</item> </item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>PCap device:</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Network type:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="comboBoxAdapter">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Network adapter:</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QPushButton" name="pushButtonConfigure">
<property name="text">
<string>Configure</string>
</property>
</widget>
</item>
<item row="0" column="1" colspan="2">
<widget class="QComboBox" name="comboBoxNetwork">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="1" column="1" colspan="2">
<widget class="QComboBox" name="comboBoxPcap"/>
</item>
</layout> </layout>
</widget> </widget>
<resources/> <resources/>

View File

@@ -664,7 +664,7 @@ NETOBJ := network.o \
net_dp8390.o \ net_dp8390.o \
net_3c503.o net_ne2000.o \ net_3c503.o net_ne2000.o \
net_pcnet.o net_wd8003.o \ net_pcnet.o net_wd8003.o \
net_plip.o net_plip.o net_event.o
PRINTOBJ := png.o prt_cpmap.o \ PRINTOBJ := png.o prt_cpmap.o \
prt_escp.o prt_text.o prt_ps.o prt_escp.o prt_text.o prt_ps.o