226 lines
5.6 KiB
C
226 lines
5.6 KiB
C
/*
|
|
* 86Box A hypervisor and IBM PC system emulator that specializes in
|
|
* running old operating systems and software designed for IBM
|
|
* PC systems and compatibles from 1981 through fairly recent
|
|
* system designs based on the PCI bus.
|
|
*
|
|
* This file is part of the 86Box distribution.
|
|
*
|
|
* Null network driver
|
|
*
|
|
*
|
|
*
|
|
* Authors: cold-brewed
|
|
*
|
|
* Copyright 2023 The 86Box development team
|
|
*/
|
|
|
|
#include <stdarg.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <wchar.h>
|
|
#include <stdbool.h>
|
|
#ifdef _WIN32
|
|
# define WIN32_LEAN_AND_MEAN
|
|
# include <windows.h>
|
|
# include <winsock2.h>
|
|
#else
|
|
# include <poll.h>
|
|
#endif
|
|
|
|
#define HAVE_STDARG_H
|
|
#include <86box/86box.h>
|
|
#include <86box/device.h>
|
|
#include <86box/thread.h>
|
|
#include <86box/timer.h>
|
|
#include <86box/network.h>
|
|
#include <86box/net_event.h>
|
|
#include <86box/plat_unused.h>
|
|
|
|
enum {
|
|
NET_EVENT_STOP = 0,
|
|
NET_EVENT_TX,
|
|
NET_EVENT_RX,
|
|
NET_EVENT_MAX
|
|
};
|
|
|
|
/* Special define for the windows portion. Because we are not interested
|
|
* in NET_EVENT_RX for the null driver, we only need to poll up to
|
|
* NET_EVENT_TX. NET_EVENT_RX gives us a different NET_EVENT_MAX
|
|
* excluding NET_EVENT_RX. */
|
|
#define NET_EVENT_TX_MAX NET_EVENT_RX
|
|
|
|
#define NULL_PKT_BATCH NET_QUEUE_LEN
|
|
|
|
typedef struct net_null_t {
|
|
uint8_t mac_addr[6];
|
|
netcard_t *card;
|
|
thread_t *poll_tid;
|
|
net_evt_t tx_event;
|
|
net_evt_t stop_event;
|
|
netpkt_t pkt;
|
|
netpkt_t pktv[NULL_PKT_BATCH];
|
|
} net_null_t;
|
|
|
|
#ifdef ENABLE_NET_NULL_LOG
|
|
int net_null_do_log = ENABLE_NET_NULL_LOG;
|
|
|
|
static void
|
|
net_null_log(const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
if (net_null_do_log) {
|
|
va_start(ap, fmt);
|
|
pclog_ex(fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
}
|
|
#else
|
|
# define net_null_log(fmt, ...)
|
|
#endif
|
|
|
|
#ifdef _WIN32
|
|
static void
|
|
net_null_thread(void *priv)
|
|
{
|
|
net_null_t *net_null = (net_null_t *) priv;
|
|
|
|
net_null_log("Null Network: polling started.\n");
|
|
|
|
HANDLE events[NET_EVENT_TX_MAX];
|
|
events[NET_EVENT_STOP] = net_event_get_handle(&net_null->stop_event);
|
|
events[NET_EVENT_TX] = net_event_get_handle(&net_null->tx_event);
|
|
|
|
bool run = true;
|
|
|
|
while (run) {
|
|
int ret = WaitForMultipleObjects(NET_EVENT_TX_MAX, events, FALSE, INFINITE);
|
|
|
|
switch (ret - WAIT_OBJECT_0) {
|
|
case NET_EVENT_STOP:
|
|
net_event_clear(&net_null->stop_event);
|
|
run = false;
|
|
break;
|
|
|
|
case NET_EVENT_TX:
|
|
net_event_clear(&net_null->tx_event);
|
|
int packets = network_tx_popv(net_null->card, net_null->pktv, NULL_PKT_BATCH);
|
|
for (int i = 0; i < packets; i++) {
|
|
net_null_log("Null Network: Ignoring TX packet (%d bytes)\n", net_null->pktv[i].len);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
net_null_log("Null Network: Unknown event.\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
net_null_log("Null Network: polling stopped.\n");
|
|
}
|
|
#else
|
|
static void
|
|
net_null_thread(void *priv)
|
|
{
|
|
net_null_t *net_null = (net_null_t *) priv;
|
|
|
|
net_null_log("Null Network: polling started.\n");
|
|
|
|
struct pollfd pfd[NET_EVENT_MAX];
|
|
pfd[NET_EVENT_STOP].fd = net_event_get_fd(&net_null->stop_event);
|
|
pfd[NET_EVENT_STOP].events = POLLIN | POLLPRI;
|
|
|
|
pfd[NET_EVENT_TX].fd = net_event_get_fd(&net_null->tx_event);
|
|
pfd[NET_EVENT_TX].events = POLLIN | POLLPRI;
|
|
|
|
while (1) {
|
|
poll(pfd, NET_EVENT_MAX, -1);
|
|
|
|
if (pfd[NET_EVENT_STOP].revents & POLLIN) {
|
|
net_event_clear(&net_null->stop_event);
|
|
break;
|
|
}
|
|
|
|
if (pfd[NET_EVENT_TX].revents & POLLIN) {
|
|
net_event_clear(&net_null->tx_event);
|
|
|
|
int packets = network_tx_popv(net_null->card, net_null->pktv, NULL_PKT_BATCH);
|
|
for (int i = 0; i < packets; i++) {
|
|
net_null_log("Null Network: Ignoring TX packet (%d bytes)\n", net_null->pktv[i].len);
|
|
}
|
|
}
|
|
}
|
|
|
|
net_null_log("Null Network: polling stopped.\n");
|
|
}
|
|
#endif
|
|
|
|
void *
|
|
net_null_init(const netcard_t *card, const uint8_t *mac_addr, UNUSED(void *priv), UNUSED(char *netdrv_errbuf))
|
|
{
|
|
net_null_log("Null Network: Init\n");
|
|
|
|
net_null_t *net_null = calloc(1, sizeof(net_null_t));
|
|
net_null->card = (netcard_t *) card;
|
|
memcpy(net_null->mac_addr, mac_addr, sizeof(net_null->mac_addr));
|
|
|
|
for (int i = 0; i < NULL_PKT_BATCH; i++) {
|
|
net_null->pktv[i].data = calloc(1, NET_MAX_FRAME);
|
|
}
|
|
net_null->pkt.data = calloc(1, NET_MAX_FRAME);
|
|
|
|
net_event_init(&net_null->tx_event);
|
|
net_event_init(&net_null->stop_event);
|
|
net_null->poll_tid = thread_create(net_null_thread, net_null);
|
|
|
|
return net_null;
|
|
}
|
|
|
|
|
|
|
|
void
|
|
net_null_in_available(void *priv)
|
|
{
|
|
net_null_t *net_null = (net_null_t *) priv;
|
|
net_event_set(&net_null->tx_event);
|
|
}
|
|
|
|
void
|
|
net_null_close(void *priv)
|
|
{
|
|
if (!priv)
|
|
return;
|
|
|
|
net_null_t *net_null = (net_null_t *) priv;
|
|
|
|
net_null_log("Null Network: closing.\n");
|
|
|
|
/* Tell the thread to terminate. */
|
|
net_event_set(&net_null->stop_event);
|
|
|
|
/* Wait for the thread to finish. */
|
|
net_null_log("Null Network: waiting for thread to end...\n");
|
|
thread_wait(net_null->poll_tid);
|
|
net_null_log("Null Network: thread ended\n");
|
|
|
|
for (int i = 0; i < NULL_PKT_BATCH; i++) {
|
|
free(net_null->pktv[i].data);
|
|
}
|
|
free(net_null->pkt.data);
|
|
|
|
net_event_close(&net_null->tx_event);
|
|
net_event_close(&net_null->stop_event);
|
|
|
|
free(net_null);
|
|
}
|
|
|
|
const netdrv_t net_null_drv = {
|
|
&net_null_in_available,
|
|
&net_null_init,
|
|
&net_null_close,
|
|
NULL
|
|
};
|