/* * 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 #include #include #include #include #include #include #ifdef _WIN32 # define WIN32_LEAN_AND_MEAN # include # include #else # include #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> 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 { 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, void *priv, 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 };