From 698678f508ad29b85a2619e63c94ec0b003aebd9 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Thu, 13 Dec 2018 23:20:04 -0500 Subject: [PATCH 001/186] rawnet vice cleaned up TFE and called it RAWNET. Slightly better code separation. I removed libnet stuff in the unix version and added a darwin version that uses the vmnet framework. --- src/CMakeLists.txt | 1 + src/rawnet/CMakeLists.txt | 13 + src/rawnet/cs8900.c | 1675 ++++++++++++++++++++++++++++++++ src/rawnet/cs8900.h | 64 ++ src/rawnet/rawnet.c | 79 ++ src/rawnet/rawnet.h | 75 ++ src/rawnet/rawnetarch.h | 68 ++ src/rawnet/rawnetarch_darwin.c | 255 +++++ src/rawnet/rawnetarch_unix.c | 538 ++++++++++ src/rawnet/rawnetarch_win32.c | 542 +++++++++++ src/rawnet/rawnetsupp.c | 151 +++ src/rawnet/rawnetsupp.h | 56 ++ 12 files changed, 3517 insertions(+) create mode 100644 src/rawnet/CMakeLists.txt create mode 100644 src/rawnet/cs8900.c create mode 100644 src/rawnet/cs8900.h create mode 100644 src/rawnet/rawnet.c create mode 100644 src/rawnet/rawnet.h create mode 100644 src/rawnet/rawnetarch.h create mode 100644 src/rawnet/rawnetarch_darwin.c create mode 100644 src/rawnet/rawnetarch_unix.c create mode 100644 src/rawnet/rawnetarch_win32.c create mode 100644 src/rawnet/rawnetsupp.c create mode 100644 src/rawnet/rawnetsupp.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5d448fe..f2fd86c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -95,6 +95,7 @@ add_executable(partls partls.c) add_subdirectory(atbridge) add_subdirectory(tfe) +add_subdirectory(rawnet) if (DRIVER MATCHES "SDL") set(driver_code sdl2_driver.c sdl2snd_driver.c) diff --git a/src/rawnet/CMakeLists.txt b/src/rawnet/CMakeLists.txt new file mode 100644 index 0000000..d91e72d --- /dev/null +++ b/src/rawnet/CMakeLists.txt @@ -0,0 +1,13 @@ + +if (WIN32) + set(rawnetarch rawnetarch_win32.c) +elseif(APPLE) + set(rawnetarch rawnetarch_darwin.c) +elseif(UNIX) + set(rawnetarch rawnetarch_unix.c) +endif() + +add_library(rawnet cs8900.c rawnet.c rawnetsupp.c ${rawnetarch}) + +target_compile_definitions(rawnet PUBLIC HAVE_RAWNET) + diff --git a/src/rawnet/cs8900.c b/src/rawnet/cs8900.c new file mode 100644 index 0000000..616f9a4 --- /dev/null +++ b/src/rawnet/cs8900.c @@ -0,0 +1,1675 @@ +/* + * cs8900.c - CS8900 Ethernet Core + * + * Written by + * Spiro Trikaliotis + * Christian Vogelgsang + * + * This file is part of VICE, the Versatile Commodore Emulator. + * See README for copyright notice. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA. + * + */ + +//#include "vice.h" + +#ifdef HAVE_RAWNET + +#include +#include +#include +#include +//#ifdef DOS_TFE +//#include /* FIXME: remove? */ +//#endif + +//#include "archdep.h" +#include "cs8900.h" +// #include "crc32.h" +// #include "lib.h" +// #include "log.h" +// #include "monitor.h" +#include "rawnetarch.h" +#include "rawnetsupp.h" +// #include "resources.h" +// #include "snapshot.h" +// #include "types.h" +// #include "util.h" + +/* FIXME: + - add register dump +*/ + +/* warn illegal behaviour */ +/* #define CS8900_DEBUG_WARN_REG 1 */ /* warn about invalid register accesses */ +/* #define CS8900_DEBUG_WARN_RXTX 1 */ /* warn about invalid rx or tx conditions */ + +/** #define CS8900_DEBUG_INIT 1 **/ +/** #define CS8900_DEBUG_LOAD 1 **/ /* enable to see tfe port reads */ +/** #define CS8900_DEBUG_STORE 1 **/ /* enable to see tfe port writes */ +/** #define CS8900_DEBUG_REGISTERS 1 **/ /* enable to see CS8900a register I/O */ +/** #define CS8900_DEBUG_IGNORE_RXEVENT 1 **/ /* enable to ignore RXEVENT in DEBUG_REGISTERS */ +/** #define CS8900_DEBUG_RXTX_STATE 1 **/ /* enable to see tranceiver state changes */ +/** #define CS8900_DEBUG_RXTX_DATA 1 **/ /* enable to see data in/out flow */ +/** #define RAWNET_DEBUG_FRAMES 1 **/ /* enable to see arch frame send/recv - might be defined in rawnetarch.h ! */ + +/* ------------------------------------------------------------------------- */ +/* variables needed */ + +//static log_t cs8900_log = LOG_ERR; + +/* status which received packages to accept + This is used in cs8900_should_accept(). +*/ +static uint8_t cs8900_ia_mac[6] = { 0, 0, 0, 0, 0, 0 }; + +/* remember the value of the hash mask */ +static uint32_t cs8900_hash_mask[2]; + +/* reveiver setup */ +static uint16_t cs8900_recv_control = 0; /* copy of CC_RXCTL (contains all bits below) */ +static int cs8900_recv_broadcast = 0; /* broadcast */ +static int cs8900_recv_mac = 0; /* individual address (IA) */ +static int cs8900_recv_multicast = 0; /* multicast if address passes the hash filter */ +static int cs8900_recv_correct = 0; /* accept correct frames */ +static int cs8900_recv_promiscuous = 0; /* promiscuous mode */ +static int cs8900_recv_hashfilter = 0; /* accept if IA passes the hash filter */ + +/* TFE registers */ +/* these are the 8 16-bit-ports for "I/O space configuration" + (see 4.10 on page 75 of cs8900a-4.pdf, the cs8900a data sheet) + + REMARK: The TFE operates the cs8900a in IO space configuration, as + it generates I/OW and I/OR signals. +*/ +#define CS8900_COUNT_IO_REGISTER 0x10 /* we have 16 I/O register */ + +static uint8_t *cs8900 = NULL; +/* + RW: RXTXDATA = DE00/DE01 + RW: RXTXDATA2 = DE02/DE03 (for 32-bit-operation) + -W: TXCMD = DE04/DE05 (TxCMD, Transmit Command) mapped to PP + 0144 (Reg. 9, Sec. 4.4, page 46) + -W: TXLENGTH = DE06/DE07 (TxLenght, Transmit Length) mapped to PP + 0146 + R-: INTSTQUEUE = DE08/DE09 (Interrupt Status Queue) mapped to PP + 0120 (ISQ, Sec. 5.1, page 78) + RW: PP_PTR = DE0A/DE0B (PacketPage Pointer) (see. page 75p: Read -011.---- ----.----) + RW: PP_DATA0 = DE0C/DE0D (PacketPage Data (Port 0)) + RW: PP_DATA1 = DE0E/DE0F (PacketPage Data (Port 1)) (for 32 bit only) +*/ + +#define CS8900_ADDR_RXTXDATA 0x00 /* RW */ +#define CS8900_ADDR_RXTXDATA2 0x02 /* RW 32 bit only! */ +#define CS8900_ADDR_TXCMD 0x04 /* -W Maps to PP+0144 */ +#define CS8900_ADDR_TXLENGTH 0x06 /* -W Maps to PP+0146 */ +#define CS8900_ADDR_INTSTQUEUE 0x08 /* R- Interrupt status queue, maps to PP + 0120 */ +#define CS8900_ADDR_PP_PTR 0x0a /* RW PacketPage Pointer */ +#define CS8900_ADDR_PP_DATA 0x0c /* RW PacketPage Data, Port 0 */ +#define CS8900_ADDR_PP_DATA2 0x0e /* RW PacketPage Data, Port 1 - 32 bit only */ + +/* Makros for reading and writing the visible TFE register: */ +#define GET_CS8900_8(_xxx_) (assert(_xxx_ < CS8900_COUNT_IO_REGISTER), cs8900[_xxx_]) + +#define SET_CS8900_8(_xxx_, _val_) \ + do { \ + assert(_xxx_ < CS8900_COUNT_IO_REGISTER); \ + cs8900[_xxx_] = (_val_) & 0xff; \ + } while (0) + +#define GET_CS8900_16(_xxx_) (assert(_xxx_ < CS8900_COUNT_IO_REGISTER), cs8900[_xxx_] | (cs8900[_xxx_ + 1] << 8)) + +#define SET_CS8900_16(_xxx_, _val_) \ + do { \ + assert(_xxx_ < CS8900_COUNT_IO_REGISTER); \ + cs8900[_xxx_] = (_val_) & 0xff; \ + cs8900[_xxx_ + 1] = (_val_ >> 8) & 0xff; \ + } while (0) + +/* The PacketPage register */ +/* note: The locations 0 to MAX_PACKETPAGE_ARRAY-1 are handled in this array. */ + +#define MAX_PACKETPAGE_ARRAY 0x1000 /* 4 KB */ + +static uint8_t *cs8900_packetpage = NULL; + +static uint16_t cs8900_packetpage_ptr = 0; + +/* Makros for reading and writing the PacketPage register: */ + +#define GET_PP_8(_xxx_) (assert(_xxx_ < MAX_PACKETPAGE_ARRAY), cs8900_packetpage[_xxx_]) + +#define GET_PP_16(_xxx_) (assert(_xxx_ < MAX_PACKETPAGE_ARRAY), assert((_xxx_ & 1) == 0), ((uint16_t)cs8900_packetpage[_xxx_]) | ((uint16_t)cs8900_packetpage[_xxx_ + 1] << 8)) + +#define GET_PP_32(_xxx_) \ + (assert(_xxx_ < MAX_PACKETPAGE_ARRAY), assert((_xxx_ & 3) == 0), \ + (((long)cs8900_packetpage[_xxx_])) | (((long)cs8900_packetpage[_xxx_ + 1]) << 8) | (((long)cs8900_packetpage[_xxx_ + 2]) << 16) | (((long)cs8900_packetpage[_xxx_ + 3]) << 24)) + +#define SET_PP_8(_xxx_, _val_) \ + do { \ + assert(_xxx_ < MAX_PACKETPAGE_ARRAY); \ + cs8900_packetpage[_xxx_] = (_val_) & 0xFF; \ + } while (0) + +#define SET_PP_16(_xxx_, _val_) \ + do { \ + assert(_xxx_ < MAX_PACKETPAGE_ARRAY); \ + assert((_xxx_ & 1) == 0), \ + cs8900_packetpage[_xxx_] = (_val_) & 0xFF; \ + cs8900_packetpage[_xxx_ + 1] = (_val_ >> 8) & 0xFF; \ + } while (0) + +#define SET_PP_32(_xxx_, _val_) \ + do { \ + assert(_xxx_ < MAX_PACKETPAGE_ARRAY); \ + assert((_xxx_ & 3) == 0), \ + cs8900_packetpage[_xxx_] = (_val_) & 0xFF; \ + cs8900_packetpage[_xxx_ + 1] = (_val_ >> 8) & 0xFF; \ + cs8900_packetpage[_xxx_ + 2] = (_val_ >> 16) & 0xFF; \ + cs8900_packetpage[_xxx_ + 3] = (_val_ >> 24) & 0xFF; \ + } while (0) + +/* The packetpage register: see p. 39f */ +#define CS8900_PP_ADDR_PRODUCTID 0x0000 /* R- - 4.3., p. 41 */ +#define CS8900_PP_ADDR_IOBASE 0x0020 /* i RW - 4.3., p. 41 - 4.7., p. 72 */ +#define CS8900_PP_ADDR_INTNO 0x0022 /* i RW - 3.2., p. 17 - 4.3., p. 41 */ +#define CS8900_PP_ADDR_DMA_CHAN 0x0024 /* i RW - 3.2., p. 17 - 4.3., p. 41 */ +#define CS8900_PP_ADDR_DMA_SOF 0x0026 /* ? R- - 4.3., p. 41 - 5.4., p. 89 */ +#define CS8900_PP_ADDR_DMA_FC 0x0028 /* ? R- - 4.3., p. 41, "Receive DMA" */ +#define CS8900_PP_ADDR_RXDMA_BC 0x002a /* ? R- - 4.3., p. 41 - 5.4., p. 89 */ +#define CS8900_PP_ADDR_MEMBASE 0x002c /* i RW - 4.3., p. 41 - 4.9., p. 73 */ +#define CS8900_PP_ADDR_BPROM_BASE 0x0030 /* i RW - 3.6., p. 24 - 4.3., p. 41 */ +#define CS8900_PP_ADDR_BPROM_MASK 0x0034 /* i RW - 3.6., p. 24 - 4.3., p. 41 */ + +/* 0x0038 - 0x003F: reserved */ +#define CS8900_PP_ADDR_EEPROM_CMD 0x0040 /* i RW - 3.5., p. 23 - 4.3., p. 41 */ +#define CS8900_PP_ADDR_EEPROM_DATA 0x0042 /* i RW - 3.5., p. 23 - 4.3., p. 41 */ + +/* 0x0044 - 0x004F: reserved */ +#define CS8900_PP_ADDR_REC_FRAME_BC 0x0050 /* RW - 4.3., p. 41 - 5.2.9., p. 86 */ + +/* 0x0052 - 0x00FF: reserved */ +#define CS8900_PP_ADDR_CONF_CTRL 0x0100 /* - RW - 4.4., p. 46; see below */ +#define CS8900_PP_ADDR_STATUS_EVENT 0x0120 /* - R- - 4.4., p. 46; see below */ + +/* 0x0140 - 0x0143: reserved */ +#define CS8900_PP_ADDR_TXCMD 0x0144 /* # -W - 4.5., p. 70 - 5.7., p. 98 */ +#define CS8900_PP_ADDR_TXLENGTH 0x0146 /* # -W - 4.5., p. 70 - 5.7., p. 98 */ + +/* 0x0148 - 0x014F: reserved */ +#define CS8900_PP_ADDR_LOG_ADDR_FILTER 0x0150 /* RW - 4.6., p. 71 - 5.3., p. 86 */ +#define CS8900_PP_ADDR_MAC_ADDR 0x0158 /* # RW - 4.6., p. 71 - 5.3., p. 86 */ + +/* 0x015E - 0x03FF: reserved */ +#define CS8900_PP_ADDR_RXSTATUS 0x0400 /* R- - 4.7., p. 72 - 5.2., p. 78 */ +#define CS8900_PP_ADDR_RXLENGTH 0x0402 /* R- - 4.7., p. 72 - 5.2., p. 78 */ +#define CS8900_PP_ADDR_RX_FRAMELOC 0x0404 /* R- - 4.7., p. 72 - 5.2., p. 78 */ + +/* here, the received frame is stored */ +#define CS8900_PP_ADDR_TX_FRAMELOC 0x0A00 /* -W - 4.7., p. 72 - 5.7., p. 98 */ + +/* here, the frame to transmit is stored */ +#define CS8900_PP_ADDR_END 0x1000 /* memory to TFE_PP_ADDR_END-1 is used */ + +/* TFE_PP_ADDR_CONF_CTRL is subdivided: */ +#define CS8900_PP_ADDR_CC_RXCFG 0x0102 /* # RW - 4.4.6., p. 52 - 0003 */ +#define CS8900_PP_ADDR_CC_RXCTL 0x0104 /* # RW - 4.4.8., p. 54 - 0005 */ +#define CS8900_PP_ADDR_CC_TXCFG 0x0106 /* RW - 4.4.9., p. 55 - 0007 */ +#define CS8900_PP_ADDR_CC_TXCMD 0x0108 /* R- - 4.4.11., p. 57 - 0009 */ +#define CS8900_PP_ADDR_CC_BUFCFG 0x010A /* RW - 4.4.12., p. 58 - 000B */ +#define CS8900_PP_ADDR_CC_LINECTL 0x0112 /* # RW - 4.4.16., p. 62 - 0013 */ +#define CS8900_PP_ADDR_CC_SELFCTL 0x0114 /* RW - 4.4.18., p. 64 - 0015 */ +#define CS8900_PP_ADDR_CC_BUSCTL 0x0116 /* RW - 4.4.20., p. 66 - 0017 */ +#define CS8900_PP_ADDR_CC_TESTCTL 0x0118 /* RW - 4.4.22., p. 68 - 0019 */ + +/* CS8900_PP_ADDR_STATUS_EVENT is subdivided: */ +#define CS8900_PP_ADDR_SE_ISQ 0x0120 /* R- - 4.4.5., p. 51 - 0000 */ +#define CS8900_PP_ADDR_SE_RXEVENT 0x0124 /* # R- - 4.4.7., p. 53 - 0004 */ +#define CS8900_PP_ADDR_SE_TXEVENT 0x0128 /* R- - 4.4.10., p. 56 - 0008 */ +#define CS8900_PP_ADDR_SE_BUFEVENT 0x012C /* R- - 4.4.13., p. 59 - 000C */ +#define CS8900_PP_ADDR_SE_RXMISS 0x0130 /* R- - 4.4.14., p. 60 - 0010 */ +#define CS8900_PP_ADDR_SE_TXCOL 0x0132 /* R- - 4.4.15., p. 61 - 0012 */ +#define CS8900_PP_ADDR_SE_LINEST 0x0134 /* R- - 4.4.17., p. 63 - 0014 */ +#define CS8900_PP_ADDR_SE_SELFST 0x0136 /* R- - 4.4.19., p. 65 - 0016 */ +#define CS8900_PP_ADDR_SE_BUSST 0x0138 /* # R- - 4.4.21., p. 67 - 0018 */ +#define CS8900_PP_ADDR_SE_TDR 0x013C /* R- - 4.4.23., p. 69 - 001C */ + +/* ------------------------------------------------------------------------- */ +/* more variables needed */ + +static uint16_t tx_buffer = CS8900_PP_ADDR_TX_FRAMELOC; +static uint16_t rx_buffer = CS8900_PP_ADDR_RXSTATUS; + +static uint16_t tx_count = 0; +static uint16_t rx_count = 0; +static uint16_t tx_length = 0; +static uint16_t rx_length = 0; + +#define CS8900_TX_IDLE 0 +#define CS8900_TX_GOT_CMD 1 +#define CS8900_TX_GOT_LEN 2 +#define CS8900_TX_READ_BUSST 3 + +#define CS8900_RX_IDLE 0 +#define CS8900_RX_GOT_FRAME 1 + +/* tranceiver state */ +static int tx_state = CS8900_TX_IDLE; +static int rx_state = CS8900_RX_IDLE; +static int tx_enabled = 0; +static int rx_enabled = 0; + +static int rxevent_read_mask = 3; /* set if L and/or H byte was read in RXEVENT? */ + +/* ------------------------------------------------------------------------- */ +/* some parameter definitions */ + +#define MAX_TXLENGTH 1518 +#define MIN_TXLENGTH 4 + +#define MAX_RXLENGTH 1518 +#define MIN_RXLENGTH 64 + +/* ------------------------------------------------------------------------- */ +/* debugging functions */ + +#ifdef RAWNET_DEBUG_FRAMES + +#define MAXLEN_DEBUG 1600 + +static int cs8900DebugMaxFrameLengthToDump = 150; + +static char *debug_outbuffer(const int length, const unsigned char * const buffer) +{ + int i; + static char outbuffer[MAXLEN_DEBUG * 4 + 1]; + char *p = outbuffer; + + assert(cs8900DebugMaxFrameLengthToDump <= MAXLEN_DEBUG); + + *p = 0; + + for (i = 0; i < cs8900DebugMaxFrameLengthToDump; i++) { + if (i >= length) { + break; + } + sprintf( p, "%02X%c", buffer[i], ((i + 1) % 16 == 0) ? '*' : (((i + 1) % 8 == 0) ? '-' : ' ')); + p += 3; + } + + return outbuffer; +} +#endif + +/* ------------------------------------------------------------------------- */ +/* initialization and deinitialization functions */ + +static void cs8900_set_tx_status(int ready, int error) +{ + uint16_t old_status = GET_PP_16(CS8900_PP_ADDR_SE_BUSST); + + /* mask out TxBidErr and Rdy4TxNOW */ + uint16_t new_status = old_status & ~0x180; + if (ready) { + new_status |= 0x100; /* set Rdy4TxNOW */ + } + if (error) { + new_status |= 0x080; /* set TxBidErr */ + } + + if (new_status != old_status) { + SET_PP_16(CS8900_PP_ADDR_SE_BUSST, new_status); +#ifdef CS8900_DEBUG_RXTX_STATE + log_message(cs8900_log, "TX: set status Rdy4TxNOW=%d TxBidErr=%d", ready, error); +#endif + } +} + +static void cs8900_set_receiver(int enabled) +{ + rx_enabled = enabled; + rx_state = CS8900_RX_IDLE; + + rxevent_read_mask = 3; /* was L or H byte read in RXEVENT? */ +} + +static void cs8900_set_transmitter(int enabled) +{ + tx_enabled = enabled; + tx_state = CS8900_TX_IDLE; + + cs8900_set_tx_status(0, 0); +} + +void cs8900_reset(void) +{ + int i; + + assert(cs8900); + assert(cs8900_packetpage); + + rawnet_arch_pre_reset(); + + /* initialize visible IO register and PacketPage registers */ + memset(cs8900, 0, CS8900_COUNT_IO_REGISTER); + memset(cs8900_packetpage, 0, MAX_PACKETPAGE_ARRAY); + + /* according to page 19 unless stated otherwise */ + SET_PP_32(CS8900_PP_ADDR_PRODUCTID, 0x0900630E ); /* p.41: 0E630009 for Rev. D; reversed order! */ + SET_PP_16(CS8900_PP_ADDR_IOBASE, 0x0300); + SET_PP_16(CS8900_PP_ADDR_INTNO, 0x0004); /* xxxx xxxx xxxx x100b */ + SET_PP_16(CS8900_PP_ADDR_DMA_CHAN, 0x0003); /* xxxx xxxx xxxx xx11b */ + + /* according to descriptions of the registers, see definitions of + CS8900_PP_ADDR_CC_... and CS8900_PP_ADDR_SE_... above! */ + + SET_PP_16(CS8900_PP_ADDR_CC_RXCFG, 0x0003); + SET_PP_16(CS8900_PP_ADDR_CC_RXCTL, 0x0005); + SET_PP_16(CS8900_PP_ADDR_CC_TXCFG, 0x0007); + SET_PP_16(CS8900_PP_ADDR_CC_TXCMD, 0x0009); + SET_PP_16(CS8900_PP_ADDR_CC_BUFCFG, 0x000B); + SET_PP_16(CS8900_PP_ADDR_CC_LINECTL, 0x0013); + SET_PP_16(CS8900_PP_ADDR_CC_SELFCTL, 0x0015); + SET_PP_16(CS8900_PP_ADDR_CC_BUSCTL, 0x0017); + SET_PP_16(CS8900_PP_ADDR_CC_TESTCTL, 0x0019); + + SET_PP_16(CS8900_PP_ADDR_SE_ISQ, 0x0000); + SET_PP_16(CS8900_PP_ADDR_SE_RXEVENT, 0x0004); + SET_PP_16(CS8900_PP_ADDR_SE_TXEVENT, 0x0008); + SET_PP_16(CS8900_PP_ADDR_SE_BUFEVENT, 0x000C); + SET_PP_16(CS8900_PP_ADDR_SE_RXMISS, 0x0010); + SET_PP_16(CS8900_PP_ADDR_SE_TXCOL, 0x0012); + /* according to specs the reset value is 0x0014, however we also set + bit 7 - Link OK + bit 9 - 10Base-T + bit 12 - Polarity OK + which makes 0x1294 ... because some software might check these and + expects them to be "Up". + FIXME: we should perhaps maintain these bits elsewhere + */ + SET_PP_16(CS8900_PP_ADDR_SE_LINEST, 0x1294); + SET_PP_16(CS8900_PP_ADDR_SE_SELFST, 0x0016); + SET_PP_16(CS8900_PP_ADDR_SE_BUSST, 0x0018); + SET_PP_16(CS8900_PP_ADDR_SE_TDR, 0x001C); + + SET_PP_16(CS8900_PP_ADDR_TXCMD, 0x0009); + + /* 4.4.19 Self Status Register, p. 65 + Important: set INITD (Bit 7) to signal device is ready */ + SET_PP_16(CS8900_PP_ADDR_SE_SELFST, 0x0896); + + cs8900_recv_control = GET_PP_16(CS8900_PP_ADDR_CC_RXCTL); + + /* spec: mac address is undefined after reset. + real HW: keeps the last set address. */ + for (i = 0; i < 6; i++) { + SET_PP_8(CS8900_PP_ADDR_MAC_ADDR + i, cs8900_ia_mac[i]); + } + + /* reset state */ + cs8900_set_transmitter(0); + cs8900_set_receiver(0); + + rawnet_arch_post_reset(); + + log_message(cs8900_log, "CS8900a rev.D reset"); +} + +int cs8900_activate(const char *net_interface) +{ + assert(cs8900 == NULL); + assert(cs8900_packetpage == NULL); + +#ifdef CS8900_DEBUG + log_message(cs8900_log, "cs8900_activate()."); +#endif + + /* allocate memory for visible IO register */ + cs8900 = lib_malloc(CS8900_COUNT_IO_REGISTER); + if (cs8900 == NULL) { +#ifdef CS8900_DEBUG_INIT + log_message(cs8900_log, "cs8900_activate: Allocating cs8900 failed."); +#endif + return -1; + } + + /* allocate memory for PacketPage register */ + cs8900_packetpage = lib_malloc(MAX_PACKETPAGE_ARRAY); + if (cs8900_packetpage == NULL) { +#ifdef CS8900_DEBUG_INIT + log_message(cs8900_log, "cs8900_activate: Allocating cs8900_packetpage failed."); +#endif + lib_free(cs8900); + cs8900 = NULL; + return -1; + } + +#ifdef CS8900_DEBUG_INIT + log_message(cs8900_log, "cs8900_activate: Allocated memory successfully."); + log_message(cs8900_log, "\tcs8900 at $%08X, cs8900_packetpage at $%08X", cs8900, cs8900_packetpage); +#endif + + if (!rawnet_arch_activate(net_interface)) { + lib_free(cs8900_packetpage); + lib_free(cs8900); + cs8900 = NULL; + cs8900_packetpage = NULL; + return -2; + } + + /* virtually reset the LAN chip */ + cs8900_reset(); + return 0; +} + +int cs8900_deactivate(void) +{ +#ifdef CS8900_DEBUG + log_message(cs8900_log, "cs8900_deactivate()."); +#endif + + assert(cs8900 && cs8900_packetpage); + + rawnet_arch_deactivate(); + + lib_free(cs8900); + cs8900 = NULL; + lib_free(cs8900_packetpage); + cs8900_packetpage = NULL; + return 0; +} + +void cs8900_shutdown(void) +{ +#ifdef CS8900_DEBUG + log_message(cs8900_log, "cs8900_shutdown()."); +#endif + + assert((cs8900 && cs8900_packetpage) || (!cs8900 && !cs8900_packetpage)); + + if (cs8900) { +#ifdef CS8900_DEBUG + log_message(cs8900_log, "...1"); +#endif + cs8900_deactivate(); + } + +#ifdef CS8900_DEBUG + log_message(cs8900_log, "cs8900_shutdown() done."); +#endif +} + +int cs8900_init(void) +{ + //cs8900_log = log_open("CS8900"); + if (!rawnet_arch_init()) { + return -1; + } + return 0; +} + +/* ------------------------------------------------------------------------- */ +/* reading and writing CS8900 register functions */ + +/* +These registers are currently fully or partially supported: + +CS8900_PP_ADDR_CC_RXCFG 0x0102 * # RW - 4.4.6., p. 52 - 0003 * +CS8900_PP_ADDR_CC_RXCTL 0x0104 * # RW - 4.4.8., p. 54 - 0005 * +CS8900_PP_ADDR_CC_LINECTL 0x0112 * # RW - 4.4.16., p. 62 - 0013 * +CS8900_PP_ADDR_SE_RXEVENT 0x0124 * # R- - 4.4.7., p. 53 - 0004 * +CS8900_PP_ADDR_SE_BUSST 0x0138 * # R- - 4.4.21., p. 67 - 0018 * +CS8900_PP_ADDR_TXCMD 0x0144 * # -W - 4.5., p. 70 - 5.7., p. 98 * +CS8900_PP_ADDR_TXLENGTH 0x0146 * # -W - 4.5., p. 70 - 5.7., p. 98 * +CS8900_PP_ADDR_MAC_ADDR 0x0158 * # RW - 4.6., p. 71 - 5.3., p. 86 * + 0x015a + 0x015c +*/ + +#ifdef RAWNET_DEBUG_FRAMES +#define return( _x_ ) \ + { \ + int retval = _x_; \ + \ + log_message(cs8900_log, "%s correct_mac=%u, broadcast=%u, multicast=%u, hashed=%u, hash_index=%u", \ + (retval ? "+++ ACCEPTED" : "--- rejected"), *pcorrect_mac, *pbroadcast, *pmulticast, *phashed, *phash_index); \ + return retval; \ + } +#endif + +/* + This is a helper for cs8900_receive() to determine if the received frame should be accepted + according to the settings. + + This function is even allowed to be called in rawnetarch.c from rawnet_arch_receive() + (via rawnet_should_accept) if necessary, and must be registered using rawnet_set_should_accept_func, + which is the reason why its prototype is included in cs8900.h. +*/ +int cs8900_should_accept(unsigned char *buffer, int length, int *phashed, int *phash_index, int *pcorrect_mac, int *pbroadcast, int *pmulticast) +{ + int hashreg; /* Hash Register (for hash computation) */ + + assert(length >= 6); /* we need at least 6 octets since the DA has this length */ + + /* first of all, delete any status */ + *phashed = 0; + *phash_index = 0; + *pcorrect_mac = 0; + *pbroadcast = 0; + *pmulticast = 0; + +#ifdef RAWNET_DEBUG_FRAMES + log_message(cs8900_log, "cs8900_should_accept called with %02X:%02X:%02X:%02X:%02X:%02X, length=%4u and buffer %s", + cs8900_ia_mac[0], cs8900_ia_mac[1], cs8900_ia_mac[2], cs8900_ia_mac[3], cs8900_ia_mac[4], cs8900_ia_mac[5], length, debug_outbuffer(length, buffer)); +#endif + + if (buffer[0] == cs8900_ia_mac[0] && buffer[1] == cs8900_ia_mac[1] && buffer[2] == cs8900_ia_mac[2] && buffer[3] == cs8900_ia_mac[3] && buffer[4] == cs8900_ia_mac[4] && buffer[5] == cs8900_ia_mac[5]) { + /* this is our individual address (IA) */ + + *pcorrect_mac = 1; + + /* if we don't want "correct MAC", we might have the chance + * that this address fits the hash index + */ + if (cs8900_recv_mac || cs8900_recv_promiscuous) { + return(1); + } + } + + if (buffer[0] == 0xFF && buffer[1] == 0xFF && buffer[2] == 0xFF && buffer[3] == 0xFF && buffer[4] == 0xFF && buffer[5] == 0xFF) { + /* this is a broadcast address */ + *pbroadcast = 1; + + /* broadcasts cannot be accepted by the hash filter */ + return ((cs8900_recv_broadcast || cs8900_recv_promiscuous) ? 1 : 0); + } + + /* now check if DA passes the hash filter */ + hashreg = (~crc32_buf((char *)buffer, 6) >> 26) & 0x3F; + + *phashed = (cs8900_hash_mask[(hashreg >= 32) ? 1 : 0] & (1 << (hashreg & 0x1F))) ? 1 : 0; + if (*phashed) { + *phash_index = hashreg; + + if (buffer[0] & 0x80) { + /* we have a multicast address */ + *pmulticast = 1; + + /* if the multicast address fits into the hash filter, + * the hashed bit has to be clear + */ + *phashed = 0; + + return ((cs8900_recv_multicast || cs8900_recv_promiscuous) ? 1 : 0); + } + return ((cs8900_recv_hashfilter || cs8900_recv_promiscuous) ? 1 : 0); + } + + return(cs8900_recv_promiscuous ? 1 : 0); +} + +#ifdef RAWNET_DEBUG_FRAMES + #undef return +#endif + +/* buffer - where to store a frame */ +/* &len - length of received frame */ +/* &hashed - set if the dest. address is accepted by the hash filter */ +/* &hash_index - hash table index if hashed == TRUE */ +/* &rx_ok - set if good CRC and valid length */ +/* &correct_mac - set if dest. address is exactly our IA */ +/* &broadcast - set if dest. address is a broadcast address */ +/* &crc_error - set if received frame had a CRC error */ + +static uint16_t cs8900_receive(void) +{ + uint16_t ret_val = 0x0004; + + uint8_t buffer[MAX_RXLENGTH]; + + int len; + int hashed; + int hash_index; + int rx_ok; + int correct_mac; + int broadcast; + int multicast = 0; /* avoid warning */ + int crc_error; + + int newframe; + + int ready; + + do { + len = MAX_RXLENGTH; + + ready = 1; /* assume we will find a good frame */ + + newframe = rawnet_arch_receive(buffer, &len, &hashed, &hash_index, &rx_ok, &correct_mac, &broadcast, &crc_error); + + assert((len & 1) == 0); /* length has to be even! */ + + if (newframe) { + if (hashed || correct_mac || broadcast) { + /* we already know the type of frame: Trust it! */ +#ifdef RAWNET_DEBUG_FRAMES + log_message( cs8900_log, "+++ cs8900_receive(): *** hashed=%u, correct_mac=%u, broadcast=%u", hashed, correct_mac, broadcast); +#endif + } else { + /* determine ourself the type of frame */ + if (!cs8900_should_accept(buffer, len, &hashed, &hash_index, &correct_mac, &broadcast, &multicast)) { + /* if we should not accept this frame, just do nothing + * now, look for another one */ + ready = 0; /* try another frame */ + continue; + } + } + + /* we did receive a frame, return that status */ + ret_val |= rx_ok ? 0x0100 : 0; + ret_val |= multicast ? 0x0200 : 0; + + if (!multicast) { + ret_val |= hashed ? 0x0040 : 0; + } + + if (hashed && rx_ok) { + /* we have the 2nd, special format with hash index: */ + assert(hash_index < 64); + ret_val |= hash_index << 9; + } else { + /* we have the regular format */ + ret_val |= correct_mac ? 0x0400 : 0; + ret_val |= broadcast ? 0x0800 : 0; + ret_val |= crc_error ? 0x1000 : 0; + ret_val |= (len < MIN_RXLENGTH) ? 0x2000 : 0; + ret_val |= (len > MAX_RXLENGTH) ? 0x4000 : 0; + } + + /* discard any octets that are beyond the MAX_RXLEN */ + if (len > MAX_RXLENGTH) { + len = MAX_RXLENGTH; + } + + if (rx_ok) { + int i; + + /* set relevant parts of the PP area to correct values */ + SET_PP_16(CS8900_PP_ADDR_RXLENGTH, len); + + for (i = 0; i < len; i++) { + SET_PP_8(CS8900_PP_ADDR_RX_FRAMELOC + i, buffer[i]); + } + + /* set rx_buffer to where start reading * + * According to 4.10.9 (pp. 76-77), we start with RxStatus and RxLength! + */ + rx_buffer = CS8900_PP_ADDR_RXSTATUS; + rx_length = len; + rx_count = 0; +#ifdef CS8900_DEBUG_WARN_RXTX + if (rx_state != CS8900_RX_IDLE) { + log_message(cs8900_log, "WARNING! New frame overwrites pending one!"); + } +#endif + rx_state = CS8900_RX_GOT_FRAME; +#ifdef CS8900_DEBUG_RXTX_STATE + log_message(cs8900_log, "RX: recvd frame (length=%04x,status=%04x)", rx_length, ret_val); +#endif + } + } + } while (!ready); + +#ifdef RAWNET_DEBUG_FRAMES + if (ret_val != 0x0004) { + log_message( cs8900_log, "+++ cs8900_receive(): ret_val=%04X", ret_val); + } +#endif + + return ret_val; +} + +/* ------------------------------------------------------------------------- */ +/* TX/RX buffer handling */ + +static void cs8900_write_tx_buffer(uint8_t value, int odd_address) +{ + /* write tx data only if valid buffer is ready */ + if (tx_state != CS8900_TX_READ_BUSST) { +#ifdef CS8900_DEBUG_WARN_RXTX + log_message(cs8900_log, "WARNING! Ignoring TX Write without correct Transmit Condition! (odd=%d,value=%02x)", odd_address, value); +#endif + /* ensure correct tx state (needed if transmit < 4 was started) */ + cs8900_set_tx_status(0, 0); + } else { +#ifdef CS8900_DEBUG_RXTX_STATE + if (tx_count == 0) { + log_message(cs8900_log, "TX: write frame (length=%04x)", tx_length); + } +#endif + + /* always write LH, LH... to tx buffer */ + uint16_t addr = tx_buffer; + if (odd_address) { + addr++; + tx_buffer += 2; + } + tx_count++; + SET_PP_8(addr, value); + +#ifdef CS8900_DEBUG_RXTX_DATA + log_message(cs8900_log, "TX: %04x/%04x: %02x (buffer=%04x,odd=%d)", tx_count, tx_length, value, addr, odd_address); +#endif + + /* full frame transmitted? */ + if (tx_count == tx_length) { +#ifdef RAWNET_DEBUG_FRAMES + log_message(cs8900_log, "rawnet_arch_transmit() called with: length=%4u and buffer %s", tx_length, debug_outbuffer(tx_length, &cs8900_packetpage[CS8900_PP_ADDR_TX_FRAMELOC])); +#endif + + if (!tx_enabled) { +#ifdef CS8900_DEBUG_WARN_RXTX + log_message(cs8900_log, "WARNING! Can't transmit frame (Transmitter is not enabled)!"); +#endif + } else { + /* send frame */ + uint16_t txcmd = GET_PP_16(CS8900_PP_ADDR_CC_TXCMD); + rawnet_arch_transmit( + txcmd & 0x0100 ? 1 : 0, /* FORCE: Delete waiting frames in transmit buffer */ + txcmd & 0x0200 ? 1 : 0, /* ONECOLL: Terminate after just one collision */ + txcmd & 0x1000 ? 1 : 0, /* INHIBITCRC: Do not append CRC to the transmission */ + txcmd & 0x2000 ? 1 : 0, /* TXPADDIS: Disable padding to 60/64 octets */ + tx_length, &cs8900_packetpage[CS8900_PP_ADDR_TX_FRAMELOC]); + } + + /* reset transmitter state */ + tx_state = CS8900_TX_IDLE; + +#ifdef CS8900_DEBUG_RXTX_STATE + log_message(cs8900_log, "TX: sent frame (length=%04x)", tx_length); +#endif + + /* reset tx status */ + cs8900_set_tx_status(0, 0); + } + } +} + +static uint8_t cs8900_read_rx_buffer(int odd_address) +{ + if (rx_state != CS8900_RX_GOT_FRAME) { +#ifdef CS8900_DEBUG_WARN_RXTX + log_message(cs8900_log, "WARNING! RX Read without frame available! (odd=%d)", odd_address); +#endif + /* always reads zero on HW */ + return 0; + } else { + /* + According to the CS8900 spec, the handling is the following: + first read H, then L (RX_STATUS), then H, then L (RX_LENGTH). + Inside the RX frame data, we always get L then H, until the end is reached. + + even odd + CS8900_PP_ADDR_RXSTATUS: - proceed + CS8900_PP_ADDR_RXLENGTH: - proceed + CS8900_PP_ADDR_RX_FRAMELOC: - - + CS8900_PP_ADDR_RX_FRAMELOC+2: proceed - + CS8900_PP_ADDR_RX_FRAMELOC+4: proceed - + + */ + uint16_t addr = odd_address ? 1 : 0; + uint8_t value; + + /* read RXSTATUS or RX_LENGTH */ + if (rx_count < 4) { + addr += rx_buffer; + value = GET_PP_8(addr); + rx_count++; + + /* incr after RXSTATUS or RX_LENGTH even (L) read */ + if (!odd_address) { + rx_buffer += 2; + } + } else { + /* read frame data */ + + /* incr before frame read (but not in first word) */ + if ((rx_count >= 6) && (!odd_address)) { + rx_buffer += 2; + } + + addr += rx_buffer; + value = GET_PP_8(addr); + rx_count++; + } + +#ifdef CS8900_DEBUG_RXTX_DATA + log_message(cs8900_log, "RX: %04x/%04x: %02x (buffer=%04x,odd=%d)", rx_count, rx_length + 4, value, addr, odd_address); +#endif + + /* check frame end */ + if (rx_count >= rx_length + 4) { + /* reset receiver state to idle */ + rx_state = CS8900_RX_IDLE; +#ifdef CS8900_DEBUG_RXTX_STATE + log_message(cs8900_log, "RX: read frame (length=%04x)", rx_length); +#endif + } + return value; + } +} + +/* ------------------------------------------------------------------------- */ +/* handle side-effects of read and write operations */ + +#define on_off_str(x) ((x) ? on_off[0] : on_off[1]) + +/* + This is called *after* the relevant octets are written +*/ +static void cs8900_sideeffects_write_pp(uint16_t ppaddress, int odd_address) +{ + const char *on_off[2] = { "on", "off" }; + uint16_t content = GET_PP_16( ppaddress ); + + assert((ppaddress & 1) == 0); + + switch (ppaddress) { + case CS8900_PP_ADDR_CC_RXCFG: + /* Skip_1 Flag: remove current (partial) tx frame and restore state */ + if (content & 0x40) { + /* restore tx state */ + if (tx_state != CS8900_TX_IDLE) { + tx_state = CS8900_TX_IDLE; +#ifdef CS8900_DEBUG_RXTX_STATE + log_message(cs8900_log, "TX: skipping current frame"); +#endif + } + + /* reset transmitter */ + cs8900_set_transmitter(tx_enabled); + + /* this is an "act once" bit, thus restore it to zero. */ + content &= ~0x40; + SET_PP_16(ppaddress, content); + } + break; + case CS8900_PP_ADDR_CC_RXCTL: + if (cs8900_recv_control != content) { + cs8900_recv_broadcast = content & 0x0800; /* broadcast */ + cs8900_recv_mac = content & 0x0400; /* individual address (IA) */ + cs8900_recv_multicast = content & 0x0200; /* multicast if address passes the hash filter */ + cs8900_recv_correct = content & 0x0100; /* accept correct frames */ + cs8900_recv_promiscuous = content & 0x0080; /* promiscuous mode */ + cs8900_recv_hashfilter = content & 0x0040; /* accept if IA passes the hash filter */ + cs8900_recv_control = content; + + log_message(cs8900_log, "setup receiver: broadcast=%s mac=%s multicast=%s correct=%s promiscuous=%s hashfilter=%s", + on_off_str(cs8900_recv_broadcast), on_off_str(cs8900_recv_mac), on_off_str(cs8900_recv_multicast), on_off_str(cs8900_recv_correct), on_off_str(cs8900_recv_promiscuous), on_off_str(cs8900_recv_hashfilter)); + + rawnet_arch_recv_ctl(cs8900_recv_broadcast, cs8900_recv_mac, cs8900_recv_multicast, cs8900_recv_correct, cs8900_recv_promiscuous, cs8900_recv_hashfilter); + } + break; + case CS8900_PP_ADDR_CC_LINECTL: + { + int enable_tx = (content & 0x0080) == 0x0080; + int enable_rx = (content & 0x0040) == 0x0040; + + if ((enable_tx != tx_enabled) || (enable_rx != rx_enabled)) { + rawnet_arch_line_ctl(enable_tx, enable_rx); + cs8900_set_transmitter(enable_tx); + cs8900_set_receiver(enable_rx); + + log_message(cs8900_log, "line control: transmitter=%s receiver=%s", on_off_str(enable_tx), on_off_str(enable_rx)); + } + } + break; + case CS8900_PP_ADDR_CC_SELFCTL: + { + /* reset chip? */ + if ((content & 0x40) == 0x40) { + cs8900_reset(); + } + } + break; + case CS8900_PP_ADDR_TXCMD: + { + if (odd_address) { + uint16_t txcommand = GET_PP_16(CS8900_PP_ADDR_TXCMD); + + /* already transmitting? */ + if (tx_state == CS8900_TX_READ_BUSST) { +#ifdef CS8900_DEBUG_WARN_RXTX + log_message(cs8900_log, "WARNING! Early abort of transmitted frame"); +#endif + } + + /* The transmit status command gets the last transmit command */ + SET_PP_16(CS8900_PP_ADDR_CC_TXCMD, txcommand); + + /* set transmit state */ + tx_state = CS8900_TX_GOT_CMD; + cs8900_set_tx_status(0, 0); + +#ifdef CS8900_DEBUG_RXTX_STATE + log_message(cs8900_log, "TX: COMMAND accepted (%04x)", txcommand); +#endif + } + } + break; + case CS8900_PP_ADDR_TXLENGTH: + { + if (odd_address && (tx_state == CS8900_TX_GOT_CMD)) { + uint16_t txlength = GET_PP_16(CS8900_PP_ADDR_TXLENGTH); + uint16_t txcommand = GET_PP_16(CS8900_PP_ADDR_CC_TXCMD); + + if (txlength < 4) { + /* frame to short */ +#ifdef CS8900_DEBUG_RXTX_STATE + log_message(cs8900_log, "TX: LENGTH rejected - too short! (%04x)", txlength); +#endif + /* mask space available but do not commit */ + tx_state = CS8900_TX_IDLE; + cs8900_set_tx_status(1, 0); + } else if ((txlength > MAX_TXLENGTH) || ((txlength > MAX_TXLENGTH - 4) && (!(txcommand & 0x1000)))) { + tx_state = CS8900_TX_IDLE; +#ifdef CS8900_DEBUG_RXTX_STATE + log_message(cs8900_log, "TX: LENGTH rejected - too long! (%04x)", txlength); +#endif + /* txlength too big, mark an error */ + cs8900_set_tx_status(0, 1); + } else { + /* make sure we put the octets to transmit at the right place */ + tx_buffer = CS8900_PP_ADDR_TX_FRAMELOC; + tx_count = 0; + tx_length = txlength; + tx_state = CS8900_TX_GOT_LEN; + +#ifdef CS8900_DEBUG_RXTX_STATE + log_message(cs8900_log, "TX: LENGTH accepted (%04x)", txlength); +#endif + /* all right, signal that we're ready for the next frame */ + cs8900_set_tx_status(1, 0); + } + } + } + break; + case CS8900_PP_ADDR_LOG_ADDR_FILTER: + case CS8900_PP_ADDR_LOG_ADDR_FILTER + 2: + case CS8900_PP_ADDR_LOG_ADDR_FILTER + 4: + case CS8900_PP_ADDR_LOG_ADDR_FILTER + 6: + { + unsigned int pos = 8 * (ppaddress - CS8900_PP_ADDR_LOG_ADDR_FILTER + odd_address); + uint32_t *p = (pos < 32) ? &cs8900_hash_mask[0] : &cs8900_hash_mask[1]; + + *p &= ~(0xFF << pos); /* clear out relevant bits */ + *p |= GET_PP_8(ppaddress + odd_address) << pos; + + rawnet_arch_set_hashfilter(cs8900_hash_mask); + +#if 0 + if (odd_address && (ppaddress == CS8900_PP_ADDR_LOG_ADDR_FILTER + 6)) { + log_message(cs8900_log, "set hash filter: %02x:%02x:%02x:%02x:%02x:%02x", + cs8900_hash_mask[0], cs8900_hash_mask[1], cs8900_hash_mask[2], cs8900_hash_mask[3], cs8900_hash_mask[4], cs8900_hash_mask[5]); + } +#endif + } + break; + case CS8900_PP_ADDR_MAC_ADDR: + case CS8900_PP_ADDR_MAC_ADDR + 2: + case CS8900_PP_ADDR_MAC_ADDR + 4: + /* the MAC address has been changed */ + cs8900_ia_mac[ppaddress - CS8900_PP_ADDR_MAC_ADDR + odd_address] = GET_PP_8(ppaddress + odd_address); + rawnet_arch_set_mac(cs8900_ia_mac); + if (odd_address && (ppaddress == CS8900_PP_ADDR_MAC_ADDR + 4)) { + log_message(cs8900_log, "set MAC address: %02x:%02x:%02x:%02x:%02x:%02x", + cs8900_ia_mac[0], cs8900_ia_mac[1], cs8900_ia_mac[2], cs8900_ia_mac[3], cs8900_ia_mac[4], cs8900_ia_mac[5]); + } + break; + } +} +#undef on_off_str + +/* + This is called *before* the relevant octets are read +*/ +static void cs8900_sideeffects_read_pp(uint16_t ppaddress, int odd_address) +{ + switch (ppaddress) { + case CS8900_PP_ADDR_SE_RXEVENT: + /* reading this before all octets of the frame are read + performs an "implied skip" */ + { + int access_mask = (odd_address) ? 1 : 2; + /* update the status register only if the full word of the last + status was read! unfortunately different access patterns are + possible: either the status is read LH, LH, LH... + or HL, HL, HL, or even L, L, L or H, H, H */ + if ((access_mask & rxevent_read_mask) != 0) { + /* receiver is not enabled */ + if (!rx_enabled) { +#ifdef CS8900_DEBUG_WARN_RXTX + log_message(cs8900_log, "WARNING! Can't receive any frame (Receiver is not enabled)!"); +#endif + } else { + /* perform frame reception */ + uint16_t ret_val = cs8900_receive(); + + /* RXSTATUS and RXEVENT are the same, except that RXSTATUS buffers + the old value while RXEVENT sets a new value whenever it is called + */ + SET_PP_16(CS8900_PP_ADDR_RXSTATUS, ret_val); + SET_PP_16(CS8900_PP_ADDR_SE_RXEVENT, ret_val); + } + /* reset read mask of (possible) other access */ + rxevent_read_mask = access_mask; + } else { + /* add access bit to mask */ + rxevent_read_mask |= access_mask; + } + } + break; + case CS8900_PP_ADDR_SE_BUSST: + if (odd_address) { + /* read busst before transmit condition is fullfilled */ + if (tx_state == CS8900_TX_GOT_LEN) { + uint16_t bus_status = GET_PP_16(CS8900_PP_ADDR_SE_BUSST); + + /* check Rdy4TXNow flag */ + if ((bus_status & 0x100) == 0x100) { + tx_state = CS8900_TX_READ_BUSST; +#ifdef CS8900_DEBUG_RXTX_STATE + log_message(cs8900_log, "TX: Ready4TXNow set! (%04x)", bus_status); +#endif + } else { +#ifdef CS8900_DEBUG_RXTX_STATE + log_message(cs8900_log, "TX: waiting for Ready4TXNow! (%04x)", bus_status); +#endif + } + } + } + break; + } +} + +/* ------------------------------------------------------------------------- */ +/* read/write from packet page register */ + +/* read a register from packet page */ +static uint16_t cs8900_read_register(uint16_t ppaddress) +{ + uint16_t value = GET_PP_16(ppaddress); + + /* --- check the register address --- */ + if (ppaddress < 0x100) { + /* reserved range reads 0x0300 on real HW */ + if ((ppaddress >= 0x0004) && (ppaddress < 0x0020)) { + return 0x0300; + } + } else if (ppaddress < 0x120) { + /* --- read control register range --- */ + uint16_t regNum = ppaddress - 0x100; + + regNum &= ~1; + regNum++; +#ifdef CS8900_DEBUG_REGISTERS + log_message(cs8900_log, "Read Control Register %04x: %04x (reg=%02x)", ppaddress, value, regNum); +#endif + + /* reserved register? */ + if ((regNum == 0x01) || (regNum == 0x11) || (regNum > 0x19)) { +#ifdef CS8900_DEBUG_WARN_REG + log_message(cs8900_log, "WARNING! Read reserved Control Register %04x (reg=%02x)", ppaddress, regNum); +#endif + /* real HW returns 0x0300 in reserved register range */ + return 0x0300; + } + + /* make sure internal address is always valid */ + assert((value & 0x3f) == regNum); + } else if (ppaddress < 0x140) { + /* --- read status register range --- */ + uint16_t regNum = ppaddress - 0x120; + + regNum &= ~1; +#ifdef CS8900_DEBUG_REGISTERS +#ifdef CS8900_DEBUG_IGNORE_RXEVENT + if (regNum != 4) /* do not show RXEVENT */ +#endif + log_message(cs8900_log, "Read Status Register %04x: %04x (reg=%02x)", ppaddress, value, regNum); +#endif + + /* reserved register? */ + if ((regNum == 0x02) || (regNum == 0x06) || (regNum == 0x0a) || (regNum == 0x0e) || (regNum == 0x1a) || (regNum == 0x1e)) { +#ifdef CS8900_DEBUG_WARN_REG + log_message(cs8900_log, "WARNING! Read reserved Status Register %04x (reg=%02x)", ppaddress, regNum); +#endif + /* real HW returns 0x0300 in reserved register range */ + return 0x0300; + } + + /* make sure internal address is always valid */ + assert((value & 0x3f) == regNum); + } else if (ppaddress < 0x150) { + /* --- read transmit register range --- */ + if (ppaddress == 0x144) { + /* make sure internal address is always valid */ + assert((value & 0x3f) == 0x09); +#ifdef CS8900_DEBUG_REGISTERS + log_message(cs8900_log, "Read TX Cmd Register %04x: %04x", ppaddress, value); +#endif + } else if (ppaddress == 0x146) { +#ifdef CS8900_DEBUG_REGISTERS + log_message(cs8900_log, "Read TX Len Register %04x: %04x", ppaddress, value); +#endif + } else { + /* reserved range */ +#ifdef CS8900_DEBUG_WARN_REG + log_message(cs8900_log, "WARNING! Read reserved Initiate Transmit Register %04x", ppaddress); +#endif + /* real HW returns 0x0300 in reserved register range */ + return 0x0300; + } + } else if (ppaddress < 0x160) { + /* --- read address filter register range --- */ + /* reserved range */ + if (ppaddress >= 0x15e) { +#ifdef CS8900_DEBUG_WARN_REG + log_message(cs8900_log, "WARNING! Read reserved Address Filter Register %04x", ppaddress); +#endif + /* real HW returns 0x0300 in reserved register range */ + return 0x0300; + } + } else if (ppaddress < 0x400) { + /* --- reserved range below 0x400 --- + returns 0x300 on real HW + */ +#ifdef CS8900_DEBUG_WARN_REG + log_message(cs8900_log, "WARNING! Read reserved Register %04x", ppaddress); +#endif + return 0x0300; + } else if (ppaddress < 0xa00) { + /* --- range from 0x400 .. 0x9ff --- RX Frame */ +#ifdef CS8900_DEBUG_WARN_REG + log_message(cs8900_log, "WARNING! Read from RX Buffer Range %04x", ppaddress); +#endif + return 0x0000; + } else { + /* --- range from 0xa00 .. 0xfff --- TX Frame */ +#ifdef CS8900_DEBUG_WARN_REG + log_message(cs8900_log, "WARNING! Read from TX Buffer Range %04x", ppaddress); +#endif + return 0x0000; + } + + /* actually read from pp memory */ + return value; +} + +static void cs8900_write_register(uint16_t ppaddress, uint16_t value) +{ + /* --- write bus interface register range --- */ + if (ppaddress < 0x100) { + int ignore = 0; + + if (ppaddress < 0x20) { + ignore = 1; + } else if ((ppaddress >= 0x26) && (ppaddress < 0x2c)) { + ignore = 1; + } else if (ppaddress == 0x38) { + ignore = 1; + } else if (ppaddress >= 0x44) { + ignore = 1; + } + if (ignore) { +#ifdef CS8900_DEBUG_WARN_REG + log_message(cs8900_log, "WARNING! Ignoring write to read only/reserved Bus Interface Register %04x", ppaddress); +#endif + return; + } + } else if (ppaddress < 0x120) { + /* --- write to control register range --- */ + uint16_t regNum = ppaddress - 0x100; + + regNum &= ~1; + regNum += 1; + /* validate internal address */ + if ((value & 0x3f) != regNum) { + /* fix internal address */ + value &= ~0x3f; + value |= regNum; + } +#ifdef CS8900_DEBUG_REGISTERS + log_message(cs8900_log, "Write Control Register %04x: %04x (reg=%02x)", ppaddress, value, regNum); +#endif + + /* invalid register? -> ignore! */ + if ((regNum == 0x01) || (regNum == 0x11) || (regNum > 0x19)) { +#ifdef CS8900_DEBUG_WARN_REG + log_message(cs8900_log, "WARNING! Ignoring write to reserved Control Register %04x (reg=%02x)", ppaddress, regNum); +#endif + return; + } + } else if (ppaddress < 0x140) { + /* --- write to status register range --- */ +#ifdef CS8900_DEBUG_WARN_REG + log_message(cs8900_log, "WARNING! Ignoring write to read-only Status Register %04x", ppaddress); +#endif + return; + } else if (ppaddress < 0x150) { + /* --- write to initiate transmit register range --- */ + /* check tx_cmd register */ + if (ppaddress == 0x144) { + /* validate internal address */ + if ((value & 0x3f) != 0x09) { + /* fix internal address */ + value &= ~0x3f; + value |= 0x09; + } + /* mask out reserved bits */ + value &= 0x33ff; +#ifdef CS8900_DEBUG_REGISTERS + log_message(cs8900_log, "Write TX Cmd Register %04x: %04x", ppaddress, value); +#endif + } else if (ppaddress == 0x146) { + /* check tx_length register */ + /* HW always masks 0x0fff */ + value &= 0x0fff; +#ifdef CS8900_DEBUG_REGISTERS + log_message(cs8900_log, "Write TX Len Register %04x: %04x", ppaddress, value); +#endif + } else if ((ppaddress < 0x144) || (ppaddress > 0x147)) { + /* reserved range */ +#ifdef CS8900_DEBUG_WARN_REG + log_message(cs8900_log, "WARNING! Ignoring write to reserved Initiate Transmit Register %04x", ppaddress); +#endif + return; + } + } else if (ppaddress < 0x160) { + /* --- write to address filter register range --- */ + /* reserved range */ + if (ppaddress >= 0x15e) { +#ifdef CS8900_DEBUG_WARN_REG + log_message(cs8900_log, "WARNING! Ingoring write to reserved Address Filter Register %04x", ppaddress); +#endif + return; + } + } else if (ppaddress < 0x400) { + /* --- ignore write outside --- */ +#ifdef CS8900_DEBUG_WARN_REG + log_message(cs8900_log, "WARNING! Ingoring write to reserved Register %04x", ppaddress); +#endif + return; + } else if (ppaddress < 0xa00) { +#ifdef CS8900_DEBUG_WARN_REG + log_message(cs8900_log, "WARNING! Ignoring write to RX Buffer Range %04x", ppaddress); +#endif + return; + } else { +#ifdef CS8900_DEBUG_WARN_REG + log_message(cs8900_log, "WARNING! Ignoring write to TX Buffer Range %04x", ppaddress); +#endif + return; + } + + /* actually set value */ + SET_PP_16(ppaddress, value); +} + +#define PP_PTR_AUTO_INCR_FLAG 0x8000 /* auto increment flag in package pointer */ +#define PP_PTR_FLAG_MASK 0xf000 /* is always : x y 1 1 (with x=auto incr) */ +#define PP_PTR_ADDR_MASK 0x0fff /* address portion of packet page pointer */ + +static void cs8900_auto_incr_pp_ptr(void) +{ + /* perform auto increment of packet page pointer */ + if ((cs8900_packetpage_ptr & PP_PTR_AUTO_INCR_FLAG) == PP_PTR_AUTO_INCR_FLAG) { + /* pointer is always increment by one on real HW */ + uint16_t ptr = cs8900_packetpage_ptr & PP_PTR_ADDR_MASK; + uint16_t flags = cs8900_packetpage_ptr & PP_PTR_FLAG_MASK; + + ptr++; + cs8900_packetpage_ptr = ptr | flags; + } +} + +/* ------------------------------------------------------------------------- */ +/* read/write CS8900 registers from VICE */ + +#define LO_uint8_t(x) (uint8_t)((x) & 0xff) +#define HI_uint8_t(x) (uint8_t)(((x) >> 8) & 0xff) +#define LOHI_uint16_t(x, y) ((uint16_t)(x) | (((uint16_t)(y)) << 8 )) + +/* ----- read byte from I/O range in VICE ----- */ +uint8_t cs8900_read(uint16_t io_address) +{ + uint8_t retval, lo, hi; + uint16_t word_value; + uint16_t reg_base; + + assert(cs8900); + assert(cs8900_packetpage); + assert(io_address < 0x10); + + /* register base addr */ + reg_base = io_address & ~1; + + /* RX register is special as it reads from RX buffer directly */ + if ((reg_base == CS8900_ADDR_RXTXDATA) || (reg_base == CS8900_ADDR_RXTXDATA2)) { + return cs8900_read_rx_buffer(io_address & 0x01); + } + + /* read packet page pointer */ + if (reg_base == CS8900_ADDR_PP_PTR) { + word_value = cs8900_packetpage_ptr; + } else { + /* read a register from packet page */ + uint16_t ppaddress = 0; + + /* determine read addr in packet page */ + switch (reg_base) { + /* PP_DATA2 behaves like PP_DATA on real HW + both show the contents at the page pointer */ + case CS8900_ADDR_PP_DATA: + case CS8900_ADDR_PP_DATA2: + /* mask and align address of packet pointer */ + ppaddress = cs8900_packetpage_ptr & PP_PTR_ADDR_MASK; + ppaddress &= ~1; + /* if flags match then auto incr pointer */ + cs8900_auto_incr_pp_ptr(); + break; + case CS8900_ADDR_INTSTQUEUE: + ppaddress = CS8900_PP_ADDR_SE_ISQ; + break; + case CS8900_ADDR_TXCMD: + ppaddress = CS8900_PP_ADDR_TXCMD; + break; + case CS8900_ADDR_TXLENGTH: + ppaddress = CS8900_PP_ADDR_TXLENGTH; + break; + default: + /* invalid! */ + assert(0); + break; + } + + /* do side effects before access */ + cs8900_sideeffects_read_pp(ppaddress, io_address & 1); + + /* read register value */ + word_value = cs8900_read_register(ppaddress); + +#ifdef CS8900_DEBUG_LOAD + log_message(cs8900_log, "reading PP Ptr: $%04X => $%04X.", ppaddress, word_value); +#endif + } + + /* extract return value from word_value */ + lo = LO_uint8_t(word_value); + hi = HI_uint8_t(word_value); + if ((io_address & 1) == 0) { + /* low byte on even address */ + retval = lo; + } else { + /* high byte on odd address */ + retval = hi; + } + +#ifdef CS8900_DEBUG_LOAD + log_message(cs8900_log, "read [$%02X] => $%02X.", io_address, retval); +#endif + + /* update _word_ value in register bank */ + cs8900[reg_base] = lo; + cs8900[reg_base + 1] = hi; + + return retval; +} + +/* ----- peek byte without side effects from I/O range in VICE ----- */ +uint8_t cs8900_peek(uint16_t io_address) +{ + uint8_t retval, lo, hi; + uint16_t word_value; + uint16_t reg_base; + + assert(cs8900); + assert(cs8900_packetpage); + assert(io_address < 0x10); + + /* register base addr */ + reg_base = io_address & ~1; + + /* RX register is special as it reads from RX buffer directly */ + if ((reg_base == CS8900_ADDR_RXTXDATA) || (reg_base == CS8900_ADDR_RXTXDATA2)) { + return 0; /* FIXME: peek rx buffer */ + } + + /* read packet page pointer */ + if (reg_base == CS8900_ADDR_PP_PTR) { + word_value = cs8900_packetpage_ptr; + } else { + /* read a register from packet page */ + uint16_t ppaddress = 0; + + /* determine read addr in packet page */ + switch (reg_base) { + /* PP_DATA2 behaves like PP_DATA on real HW + both show the contents at the page pointer */ + case CS8900_ADDR_PP_DATA: + case CS8900_ADDR_PP_DATA2: + /* mask and align address of packet pointer */ + ppaddress = cs8900_packetpage_ptr & PP_PTR_ADDR_MASK; + ppaddress &= ~1; + /* if flags match then auto incr pointer */ + cs8900_auto_incr_pp_ptr(); + break; + case CS8900_ADDR_INTSTQUEUE: + ppaddress = CS8900_PP_ADDR_SE_ISQ; + break; + case CS8900_ADDR_TXCMD: + ppaddress = CS8900_PP_ADDR_TXCMD; + break; + case CS8900_ADDR_TXLENGTH: + ppaddress = CS8900_PP_ADDR_TXLENGTH; + break; + default: + /* invalid! */ + assert(0); + break; + } + + /* read register value */ + word_value = cs8900_read_register(ppaddress); + } + + /* extract return value from word_value */ + lo = LO_uint8_t(word_value); + hi = HI_uint8_t(word_value); + if ((io_address & 1) == 0) { + /* low byte on even address */ + retval = lo; + } else { + /* high byte on odd address */ + retval = hi; + } + return retval; +} + +/* ----- write byte to I/O range of VICE ----- */ +void cs8900_store(uint16_t io_address, uint8_t byte) +{ + uint16_t reg_base; + uint16_t word_value; + + assert(cs8900); + assert(cs8900_packetpage); + assert(io_address < 0x10); + +#ifdef CS8900_DEBUG_STORE + log_message(cs8900_log, "store [$%02X] <= $%02X.", io_address, (int)byte); +#endif + + /* register base addr */ + reg_base = io_address & ~1; + + /* TX Register is special as it writes to TX buffer directly */ + if ((reg_base == CS8900_ADDR_RXTXDATA) || (reg_base == CS8900_ADDR_RXTXDATA2)) { + cs8900_write_tx_buffer(byte, io_address & 1); + return; + } + + /* combine stored value with new written byte */ + if ((io_address & 1) == 0) { + /* overwrite low byte */ + word_value = LOHI_uint16_t(byte, cs8900[reg_base + 1]); + } else { + /* overwrite high byte */ + word_value = LOHI_uint16_t(cs8900[reg_base], byte); + } + + if (reg_base == CS8900_ADDR_PP_PTR) { + /* we store the full package pointer in cs8900_packetpage_ptr variable. + this includes the mask area (0xf000) and the addr range (0x0fff). + we ensure that the bits 0x3000 are always set (as in real HW). + odd values of the pointer are valid and supported. + only register read and write have to be mapped to word boundary. */ + word_value |= 0x3000; + cs8900_packetpage_ptr = word_value; + +#ifdef CS8900_DEBUG_STORE + log_message(cs8900_log, "set PP Ptr to $%04X.", cs8900_packetpage_ptr); +#endif + } else { + /* write a register */ + + /*! \TODO: Find a reasonable default */ + uint16_t ppaddress = CS8900_PP_ADDR_PRODUCTID; + + /* now determine address of write in packet page */ + switch (reg_base) { + case CS8900_ADDR_PP_DATA: + case CS8900_ADDR_PP_DATA2: + /* mask and align ppaddress from page pointer */ + ppaddress = cs8900_packetpage_ptr & (MAX_PACKETPAGE_ARRAY - 1); + ppaddress &= ~1; + /* auto increment pp ptr */ + cs8900_auto_incr_pp_ptr(); + break; + case CS8900_ADDR_TXCMD: + ppaddress = CS8900_PP_ADDR_TXCMD; + break; + case CS8900_ADDR_TXLENGTH: + ppaddress = CS8900_PP_ADDR_TXLENGTH; + break; + case CS8900_ADDR_INTSTQUEUE: + ppaddress = CS8900_PP_ADDR_SE_ISQ; + break; + case CS8900_ADDR_PP_PTR: + break; + default: + /* invalid */ + assert(0); + break; + } + +#ifdef CS8900_DEBUG_STORE + log_message(cs8900_log, "before writing to PP Ptr: $%04X <= $%04X.", ppaddress, word_value); +#endif + + /* perform the write */ + cs8900_write_register(ppaddress, word_value); + + /* handle sideeffects */ + cs8900_sideeffects_write_pp(ppaddress, io_address & 1); + + /* update word value if it was changed in write register or by side effect */ + word_value = GET_PP_16(ppaddress); + +#ifdef CS8900_DEBUG_STORE + log_message(cs8900_log, "after writing to PP Ptr: $%04X <= $%04X.", ppaddress, word_value); +#endif + } + + /* update cs8900 registers */ + cs8900[reg_base] = LO_uint8_t(word_value); + cs8900[reg_base + 1] = HI_uint8_t(word_value); +} + +int cs8900_dump(void) +{ + /* FIXME: this is incomplete */ + // mon_out("Link status: %s\n", (GET_PP_16(CS8900_PP_ADDR_SE_LINEST) & 0x80) ? "up" : "no link"); + // mon_out("Package Page Ptr: $%04X (autoincrement %s)\n", cs8900_packetpage_ptr & PP_PTR_ADDR_MASK, (cs8900_packetpage_ptr & PP_PTR_AUTO_INCR_FLAG) != 0 ? "enabled" : "disabled"); + return 0; +} + +/* ---------------------------------------------------------------------*/ +/* snapshot support functions */ + +#define CART_DUMP_VER_MAJOR 0 +#define CART_DUMP_VER_MINOR 0 +#define SNAP_MODULE_NAME "CS8900" + +/* FIXME: implement snapshot support */ +int cs8900_snapshot_write_module(snapshot_t *s) +{ + return -1; +#if 0 + snapshot_module_t *m; + + m = snapshot_module_create(s, SNAP_MODULE_NAME, + CART_DUMP_VER_MAJOR, CART_DUMP_VER_MINOR); + if (m == NULL) { + return -1; + } + + if (0) { + snapshot_module_close(m); + return -1; + } + + snapshot_module_close(m); + return 0; +#endif +} + +int cs8900_snapshot_read_module(snapshot_t *s) +{ + return -1; +#if 0 + uint8_t vmajor, vminor; + snapshot_module_t *m; + + m = snapshot_module_open(s, SNAP_MODULE_NAME, &vmajor, &vminor); + if (m == NULL) { + return -1; + } + + if ((vmajor != CART_DUMP_VER_MAJOR) || (vminor != CART_DUMP_VER_MINOR)) { + snapshot_module_close(m); + return -1; + } + + if (0) { + snapshot_module_close(m); + return -1; + } + + snapshot_module_close(m); + return 0; +#endif +} + +#endif /* #ifdef HAVE_RAWNET */ diff --git a/src/rawnet/cs8900.h b/src/rawnet/cs8900.h new file mode 100644 index 0000000..97b23fb --- /dev/null +++ b/src/rawnet/cs8900.h @@ -0,0 +1,64 @@ +/* + * cs8900.h - CS8900 Ethernet Core + * + * Written by + * Spiro Trikaliotis + * + * This file is part of VICE, the Versatile Commodore Emulator. + * See README for copyright notice. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA. + * + */ + +#include + +#ifndef HAVE_RAWNET + #error CS8900.H should not be included if HAVE_RAWNET is not defined! +#endif /* #ifdef HAVE_RAWNET */ + +#ifndef VICE_CS8900_H +#define VICE_CS8900_H + +//#include <"types.h"> + +typedef struct snapshot_s snapshot_t; +extern int cs8900_snapshot_read_module(snapshot_t *s); +extern int cs8900_snapshot_write_module(snapshot_t *s); + +extern int cs8900_init(void); +extern void cs8900_reset(void); + +extern int cs8900_activate(const char *net_interface); +extern int cs8900_deactivate(void); +extern void cs8900_shutdown(void); + +extern uint8_t cs8900_read(uint16_t io_address); +extern uint8_t cs8900_peek(uint16_t io_address); +extern void cs8900_store(uint16_t io_address, uint8_t byte); +extern int cs8900_dump(void); + +/* + This is a helper for cs8900_receive() to determine if the received frame should be accepted + according to the settings. + + This function is even allowed to be called (indirectly via rawnet_should_accept) in rawnetarch.c + from rawnet_arch_receive() if necessary, and must be registered using rawnet_set_should_accept_func + at init time. +*/ +extern int cs8900_should_accept(unsigned char *buffer, int length, int *phashed, int *phash_index, int *pcorrect_mac, int *pbroadcast, int *pmulticast); + +#endif diff --git a/src/rawnet/rawnet.c b/src/rawnet/rawnet.c new file mode 100644 index 0000000..24c7b87 --- /dev/null +++ b/src/rawnet/rawnet.c @@ -0,0 +1,79 @@ +/* + * rawnet.c - raw ethernet interface + * + * Written by + * Spiro Trikaliotis + * Christian Vogelgsang + * + * This file is part of VICE, the Versatile Commodore Emulator. + * See README for copyright notice. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA. + * + */ + +// #include "vice.h" + +#ifdef HAVE_RAWNET + +#include +#include +#include + +#include "rawnet.h" +#include "rawnetsupp.h" +#include "rawnetarch.h" + +static int (*should_accept)(unsigned char *, int, int *, int *, int *, int *, int *) = NULL; + +int rawnet_should_accept(unsigned char *buffer, int length, int *phashed, int *phash_index, int *pcorrect_mac, int *pbroadcast, int *pmulticast) +{ + assert(should_accept); + return should_accept(buffer, length, phashed, phash_index, pcorrect_mac, pbroadcast, pmulticast); +} + +void rawnet_set_should_accept_func(int (*func)(unsigned char *, int, int *, int *, int *, int *, int *)) +{ + should_accept = func; +} + +/* ------------------------------------------------------------------------- */ +/* functions for selecting and querying available NICs */ + +int rawnet_enumadapter_open(void) +{ + if (!rawnet_arch_enumadapter_open()) { + /* tfe_cannot_use = 1; */ + return 0; + } + return 1; +} + +int rawnet_enumadapter(char **ppname, char **ppdescription) +{ + return rawnet_arch_enumadapter(ppname, ppdescription); +} + +int rawnet_enumadapter_close(void) +{ + return rawnet_arch_enumadapter_close(); +} + +char *rawnet_get_standard_interface(void) +{ + return rawnet_arch_get_standard_interface(); +} +#endif /* #ifdef HAVE_RAWNET */ diff --git a/src/rawnet/rawnet.h b/src/rawnet/rawnet.h new file mode 100644 index 0000000..33a0c92 --- /dev/null +++ b/src/rawnet/rawnet.h @@ -0,0 +1,75 @@ +/* + * rawnet.h - raw ethernet interface + * + * Written by + * Spiro Trikaliotis + * + * This file is part of VICE, the Versatile Commodore Emulator. + * See README for copyright notice. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA. + * + */ + +#ifdef HAVE_RAWNET +#else + #error RAWNET.H should not be included if HAVE_RAWNET is not defined! +#endif /* #ifdef HAVE_RAWNET */ + +#ifndef VICE_RAWNET_H +#define VICE_RAWNET_H + +/* + This is a helper for the _receive() function of the emulated ethernet chip to determine + if the received frame should be accepted according to the settings. + + This function is even allowed to be called in rawnetarch.c from rawnet_arch_receive() if + necessary. the respective helper function of the emulated ethernet chip must be registered + using rawnet_set_should_accept_func at init time. +*/ + +extern int rawnet_should_accept(unsigned char *buffer, int length, int *phashed, int *phash_index, int *pcorrect_mac, int *pbroadcast, int *pmulticast); +extern void rawnet_set_should_accept_func(int (*func)(unsigned char *, int, int *, int *, int *, int *, int *)); + +/* + + These functions let the UI enumerate the available interfaces. + + First, rawnet_enumadapter_open() is used to start enumeration. + + rawnet_enumadapter() is then used to gather information for each adapter present + on the system, where: + + ppname points to a pointer which will hold the name of the interface + ppdescription points to a pointer which will hold the description of the interface + + For each of these parameters, new memory is allocated, so it has to be + freed with lib_free(). + Note: The description can be NULL, since pcap_if_t.desc can be NULL, so + check the description before calling lib_free() on it. + + rawnet_enumadapter_close() must be used to stop processing. + + Each function returns 1 on success, and 0 on failure. + rawnet_enumadapter() only fails if there is no more adpater; in this case, + *ppname and *ppdescription are not altered. +*/ +extern int rawnet_enumadapter_open(void); +extern int rawnet_enumadapter(char **ppname, char **ppdescription); +extern int rawnet_enumadapter_close(void); +extern char *rawnet_get_standard_interface(void); + +#endif diff --git a/src/rawnet/rawnetarch.h b/src/rawnet/rawnetarch.h new file mode 100644 index 0000000..262dd29 --- /dev/null +++ b/src/rawnet/rawnetarch.h @@ -0,0 +1,68 @@ +/* + * rawnetarch.h - raw ethernet interface + * architecture-dependant stuff + * + * Written by + * Spiro Trikaliotis + * + * This file is part of VICE, the Versatile Commodore Emulator. + * See README for copyright notice. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA. + * + */ + +#include + +#ifdef HAVE_RAWNET +#else + #error RAWNETARCH.H should not be included if HAVE_RAWNET is not defined! +#endif /* #ifdef HAVE_RAWNET */ + +#ifndef VICE_RAWNETARCH_H +#define VICE_RAWNETARCH_H + +/* define this only if VICE should write each and every frame received + and send into the VICE log + WARNING: The log grows very fast! +*/ +/* #define RAWNET_DEBUG_FRAMES */ + +// #include "types.h" + +extern int rawnet_arch_init(void); +extern void rawnet_arch_pre_reset(void); +extern void rawnet_arch_post_reset(void); +extern int rawnet_arch_activate(const char *interface_name); +extern void rawnet_arch_deactivate(void); +extern void rawnet_arch_set_mac(const uint8_t mac[6]); +extern void rawnet_arch_set_hashfilter(const uint32_t hash_mask[2]); + +extern void rawnet_arch_recv_ctl(int bBroadcast, int bIA, int bMulticast, int bCorrect, int bPromiscuous, int bIAHash); + +extern void rawnet_arch_line_ctl(int bEnableTransmitter, int bEnableReceiver); + +extern void rawnet_arch_transmit(int force, int onecoll, int inhibit_crc, int tx_pad_dis, int txlength, uint8_t *txframe); + +extern int rawnet_arch_receive(uint8_t *pbuffer, int *plen, int *phashed, int *phash_index, int *prx_ok, int *pcorrect_mac, int *pbroadcast, int *pcrc_error); + +extern int rawnet_arch_enumadapter_open(void); +extern int rawnet_arch_enumadapter(char **ppname, char **ppdescription); +extern int rawnet_arch_enumadapter_close(void); + +extern char *rawnet_arch_get_standard_interface(void); + +#endif diff --git a/src/rawnet/rawnetarch_darwin.c b/src/rawnet/rawnetarch_darwin.c new file mode 100644 index 0000000..b8ed502 --- /dev/null +++ b/src/rawnet/rawnetarch_darwin.c @@ -0,0 +1,255 @@ +/* + * OS X 10.10+ + * vmnet support (clang -framework vmnet -framework Foundation) + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rawnetarch.h" +#include "rawnetsupp.h" + + +static const uint8_t broadcast_mac[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + +static interface_ref interface; +static uint8_t interface_mac[6]; +static uint64_t interface_mtu; +static uint64_t interface_packet_size; +static vmnet_return_t interface_status; + +int rawnet_arch_init(void) { + interface = NULL; + return 1; +} +void rawnet_arch_pre_reset(void) { + /* NOP */ +} + +void rawnet_arch_post_reset(void) { + /* NOP */ +} + +int rawnet_arch_activate(const char *interface_name) { + + xpc_object_t dict; + dispatch_queue_t q; + dispatch_semaphore_t sem; + + /* + * there's no way to set the MAC address directly. + * using vmnet_interface_id_key w/ the previous interface id + * *MIGHT* re-use the previous MAC address. + */ + + memset(interface_mac, 0, sizeof(interface_mac)); + interface_status = 0; + interface_mtu = 0; + interface_packet_size = 0; + + dict = xpc_dictionary_create(NULL, NULL, 0); + xpc_dictionary_set_uint64(dict, vmnet_operation_mode_key, VMNET_SHARED_MODE); + sem = dispatch_semaphore_create(0); + q = dispatch_get_global_queue(QOS_CLASS_UTILITY, 0); + + interface = vmnet_start_interface(dict, q, ^(vmnet_return_t status, xpc_object_t params){ + interface_status = status; + if (status == VMNET_SUCCESS) { + const char *cp; + cp = xpc_dictionary_get_string(params, vmnet_mac_address_key); + sscanf(cp, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", + &interface_mac[0], + &interface_mac[1], + &interface_mac[2], + &interface_mac[3], + &interface_mac[4], + &interface_mac[5] + ); + + interface_mtu = xpc_dictionary_get_uint64(params, vmnet_mtu_key); + interface_packet_size = xpc_dictionary_get_uint64(params, vmnet_max_packet_size_key); + } + dispatch_semaphore_signal(sem); + }); + dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); + + + if (interface_status != VMNET_SUCCESS) { + log_message(rawnet_arch_log, "vmnet_start_interface failed\n"); + if (interface) { + vmnet_stop_interface(interface, q, ^(vmnet_return_t status){ + dispatch_semaphore_signal(sem); + }); + dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); + interface = NULL; + } + } + + + dispatch_release(sem); + xpc_release(dict); + return interface_status == VMNET_SUCCESS; +} + +void rawnet_arch_deactivate(void) { + + dispatch_queue_t q; + dispatch_semaphore_t sem; + + + if (interface) { + sem = dispatch_semaphore_create(0); + q = dispatch_get_global_queue(QOS_CLASS_UTILITY, 0); + + vmnet_stop_interface(interface, q, ^(vmnet_return_t status){ + dispatch_semaphore_signal(sem); + }); + dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); + dispatch_release(sem); + + interface = NULL; + interface_status = 0; + } + +} + +void rawnet_arch_set_mac(const uint8_t mac[6]) { + /* NOP */ +} +void rawnet_arch_set_hashfilter(const uint32_t hash_mask[2]) { + /* NOP */ +} + +void rawnet_arch_recv_ctl(int bBroadcast, int bIA, int bMulticast, int bCorrect, int bPromiscuous, int bIAHash) { + /* NOP */ +} + +void rawnet_arch_line_ctl(int bEnableTransmitter, int bEnableReceiver) { + /* NOP */ +} + +void rawnet_arch_transmit(int force, int onecoll, int inhibit_crc, int tx_pad_dis, int txlength, uint8_t *txframe) { + + int count = 1; + vmnet_return_t st; + struct vmpktdesc v; + struct iovec iov; + + if (txlength == 0) return; + if (txlength > interface_packet_size) { + log_message(rawnet_arch_log, "packet is too big: %d\n", txlength); + return; + } + + iov.iov_base = txframe; + iov.iov_len = txlength; + + v.vm_pkt_size = txlength; + v.vm_pkt_iov = &iov; + v.vm_pkt_iovcnt = 1; + v.vm_flags = 0; + + st = vmnet_write(interface, &v, &count); + if (st != VMNET_SUCCESS) { + log_message(rawnet_arch_log, "vmnet_write failed!\n"); + } + return; +} + +int rawnet_arch_receive(uint8_t *pbuffer, int *plen, int *phashed, int *phash_index, int *prx_ok, int *pcorrect_mac, int *pbroadcast, int *pcrc_error) { + + char *buffer; + //unsigned hashreg; + + int count = 1; + int xfer; + vmnet_return_t st; + struct vmpktdesc v; + struct iovec iov; + + + buffer = malloc(interface_packet_size); + + iov.iov_base = buffer; + iov.iov_len = interface_packet_size; + + v.vm_pkt_size = interface_packet_size; + v.vm_pkt_iov = &iov; + v.vm_pkt_iovcnt = 1; + v.vm_flags = 0; + + st = vmnet_read(interface, &v, &count); + if (st != VMNET_SUCCESS) { + log_message(rawnet_arch_log, "vmnet_write failed!\n"); + free(buffer); + return 0; + } + + if (count == 0) { + free(buffer); + return 0; + } + xfer = iov.iov_len; + if (xfer > *plen) xfer = *plen; + memcpy(pbuffer, buffer, xfer); + + xfer = iov.iov_len; + if (xfer & 0x01) ++xfer; /* ??? */ + *plen = xfer; /* actual frame size */ + + *phashed = + *phash_index = + *pbroadcast = + *pcorrect_mac = + *pcrc_error = 0; + + *prx_ok = 1; + *pcorrect_mac = memcmp(buffer, interface_mac, 6) == 0; + *pbroadcast = memcmp(buffer, broadcast_mac, 6) == 0; + + /* vmnet won't send promiscuous packets */ + #if 0 + hashreg = (~crc32_buf(buffer, 6) >> 26) & 0x3f; + if (hash_mask[hashreg >= 32] & (1 << (hashreg & 0x1F))) { + *phashed = 1; + *phash_index = hashreg; + } else { + *phashed = 0; + *phash_index = 0; + } + #endif + + free(buffer); + return 1; +} + +static unsigned adapter_index = 0; +int rawnet_arch_enumadapter_open(void) { + adapter_index = 0; + return 1; +} + +int rawnet_arch_enumadapter(char **ppname, char **ppdescription) { + + if (adapter_index == 0) { + ++adapter_index; + if (ppname) *ppname = lib_stralloc("vmnet"); + if (ppdescription) *ppdescription = lib_stralloc("vmnet"); + return 1; + } + return 0; +} + +int rawnet_arch_enumadapter_close(void) { + return 1; +} + +char *rawnet_arch_get_standard_interface(void) { + return lib_stralloc("vmnet"); +} diff --git a/src/rawnet/rawnetarch_unix.c b/src/rawnet/rawnetarch_unix.c new file mode 100644 index 0000000..7bf8916 --- /dev/null +++ b/src/rawnet/rawnetarch_unix.c @@ -0,0 +1,538 @@ +/** \file rawnetarch_unix.c + * \brief Raw ethernet interface, architecture-dependent stuff + * + * \author Spiro Trikaliotis + * \author Bas Wassink + * + * These functions let the UI enumerate the available interfaces. + * + * First, rawnet_arch_enumadapter_open() is used to start enumeration. + * + * rawnet_arch_enumadapter() is then used to gather information for each adapter + * present on the system, where: + * + * ppname points to a pointer which will hold the name of the interface + * ppdescription points to a pointer which will hold the description of the + * interface + * + * For each of these parameters, new memory is allocated, so it has to be + * freed with lib_free(), except ppdescription, which can be `NULL`, though + * calling lib_free() on `NULL` is safe. + * + * rawnet_arch_enumadapter_close() must be used to stop processing. + * + * Each function returns 1 on success, and 0 on failure. + * rawnet_arch_enumadapter() only fails if there is no more adpater; in this + * case, *ppname and *ppdescription are not altered. + */ + +/* + * This file is part of VICE, the Versatile Commodore Emulator. + * See README for copyright notice. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA. + * + */ + +#include + +// #include "vice.h" + +#ifdef HAVE_RAWNET + +#include + +#include +#include +#include +#include + +// #include "lib.h" +// #include "log.h" +#include "rawnetarch.h" +#include "rawnetsupp.h" + +/* + * FIXME: rename all remaining tfe_ stuff to rawnet_ + */ + +#define RAWNET_DEBUG_WARN 1 /* this should not be deactivated + * If this should not be deactived, why is this + * here at all? --compyx + */ + + +/** \brief Only select devices that are PCAP_IF_UP + * + * Since on Linux pcap_findalldevs() returns all interfaces, including special + * kernal devices such as nfqueue, filtering the list returned by pcap makes + * sense. Should this filtering cause trouble on other Unices, this define can + * be guarded with #ifdef SOME_UNIX_VERSION to disable the filtering. + */ +#ifdef PCAP_IF_UP +#define RAWNET_ONLY_IF_UP +#endif + + +/** #define RAWNET_DEBUG_ARCH 1 **/ +/** #define RAWNET_DEBUG_PKTDUMP 1 **/ + +/* ------------------------------------------------------------------------- */ +/* variables needed */ + +// static log_t rawnet_arch_log = LOG_ERR; + + +/** \brief Iterator for the list returned by pcap_findalldevs() + */ +static pcap_if_t *rawnet_pcap_dev_iter = NULL; + + +/** \brief Device list returned by pcap_findalldevs() + * + * Can be `NULL` since pcap_findalldevs() considers not finding any devices a + * succesful outcome. + */ +static pcap_if_t *rawnet_pcap_dev_list = NULL; + + +static pcap_t *rawnet_pcap_fp = NULL; + + +/** \brief Buffer for pcap error messages + */ +static char rawnet_pcap_errbuf[PCAP_ERRBUF_SIZE]; + + +#ifdef RAWNET_DEBUG_PKTDUMP + +static void debug_output( const char *text, uint8_t *what, int count ) +{ + char buffer[256]; + char *p = buffer; + char *pbuffer1 = what; + int len1 = count; + int i; + + sprintf(buffer, "\n%s: length = %u\n", text, len1); + fprintf(stderr, "%s", buffer); + do { + p = buffer; + for (i=0; (i<8) && len1>0; len1--, i++) { + sprintf(p, "%02x ", (unsigned int)(unsigned char)*pbuffer1++); + p += 3; + } + *(p-1) = '\n'; *p = 0; + fprintf(stderr, "%s", buffer); + } while (len1>0); +} +#endif /* #ifdef RAWNET_DEBUG_PKTDUMP */ + + +int rawnet_arch_enumadapter_open(void) +{ + if (pcap_findalldevs(&rawnet_pcap_dev_list, rawnet_pcap_errbuf) == -1) { + log_message(rawnet_arch_log, + "ERROR in rawnet_arch_enumadapter_open: pcap_findalldevs: '%s'", + rawnet_pcap_errbuf); + return 0; + } + + if (!rawnet_pcap_dev_list) { + log_message(rawnet_arch_log, + "ERROR in rawnet_arch_enumadapter_open, finding all pcap " + "devices - Do we have the necessary privilege rights?"); + return 0; + } + + rawnet_pcap_dev_iter = rawnet_pcap_dev_list; + return 1; +} + + +/** \brief Get current pcap device iterator values + * + * The \a ppname and \a ppdescription are heap-allocated via lib_stralloc() + * and should thus be freed after use with lib_free(). Please not that + * \a ppdescription can be `NULL` due to pcap_if_t->description being `NULL`, + * so check against `NULL` before using it. Calling lib_free() on it is safe + * though, free(`NULL`) is guaranteed to just do nothing. + * + * \param[out] ppname device name + * \param[out] ppdescription device description + * + * \return bool (1 on success, 0 on failure) + */ +int rawnet_arch_enumadapter(char **ppname, char **ppdescription) +{ +#ifdef RAWNET_ONLY_IF_UP + /* only select devices that are up */ + while (rawnet_pcap_dev_iter != NULL + && !(rawnet_pcap_dev_iter->flags & PCAP_IF_UP)) { + rawnet_pcap_dev_iter = rawnet_pcap_dev_iter->next; + } +#endif + + if (rawnet_pcap_dev_iter == NULL) { + return 0; + } + + *ppname = lib_stralloc(rawnet_pcap_dev_iter->name); + /* carefull: pcap_if_t->description can be NULL and lib_stralloc() fails on + * passing `NULL` */ + if (rawnet_pcap_dev_iter->description != NULL) { + *ppdescription = lib_stralloc(rawnet_pcap_dev_iter->description); + } else { + *ppdescription = NULL; + } + + rawnet_pcap_dev_iter = rawnet_pcap_dev_iter->next; + + return 1; +} + +int rawnet_arch_enumadapter_close(void) +{ + if (rawnet_pcap_dev_list) { + pcap_freealldevs(rawnet_pcap_dev_list); + rawnet_pcap_dev_list = NULL; + } + return 1; +} + +static int rawnet_pcap_open_adapter(const char *interface_name) +{ + rawnet_pcap_fp = pcap_open_live((char*)interface_name, 1700, 1, 20, rawnet_pcap_errbuf); + if ( rawnet_pcap_fp == NULL) { + log_message(rawnet_arch_log, "ERROR opening adapter: '%s'", rawnet_pcap_errbuf); + return 0; + } + + if (pcap_setnonblock(rawnet_pcap_fp, 1, rawnet_pcap_errbuf) < 0) { + log_message(rawnet_arch_log, "WARNING: Setting PCAP to non-blocking failed: '%s'", rawnet_pcap_errbuf); + } + + /* Check the link layer. We support only Ethernet for simplicity. */ + if (pcap_datalink(rawnet_pcap_fp) != DLT_EN10MB) { + log_message(rawnet_arch_log, "ERROR: TFE works only on Ethernet networks."); + return 0; + } + + return 1; +} + +/* ------------------------------------------------------------------------- */ +/* the architecture-dependend functions */ + +int rawnet_arch_init(void) +{ + //rawnet_arch_log = log_open("TFEARCH"); + + return 1; +} + +void rawnet_arch_pre_reset(void) +{ +#ifdef RAWNET_DEBUG_ARCH + log_message( rawnet_arch_log, "rawnet_arch_pre_reset()." ); +#endif +} + +void rawnet_arch_post_reset(void) +{ +#ifdef RAWNET_DEBUG_ARCH + log_message( rawnet_arch_log, "rawnet_arch_post_reset()." ); +#endif +} + +int rawnet_arch_activate(const char *interface_name) +{ +#ifdef RAWNET_DEBUG_ARCH + log_message( rawnet_arch_log, "rawnet_arch_activate()." ); +#endif + if (!rawnet_pcap_open_adapter(interface_name)) { + return 0; + } + return 1; +} + +void rawnet_arch_deactivate( void ) +{ +#ifdef RAWNET_DEBUG_ARCH + log_message( rawnet_arch_log, "rawnet_arch_deactivate()." ); +#endif +} + +void rawnet_arch_set_mac( const uint8_t mac[6] ) +{ +#ifdef RAWNET_DEBUG_ARCH + log_message( rawnet_arch_log, "New MAC address set: %02X:%02X:%02X:%02X:%02X:%02X.", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5] ); +#endif +} + +void rawnet_arch_set_hashfilter(const uint32_t hash_mask[2]) +{ +#ifdef RAWNET_DEBUG_ARCH + log_message( rawnet_arch_log, "New hash filter set: %08X:%08X.", hash_mask[1], hash_mask[0]); +#endif +} + +/* int bBroadcast - broadcast */ +/* int bIA - individual address (IA) */ +/* int bMulticast - multicast if address passes the hash filter */ +/* int bCorrect - accept correct frames */ +/* int bPromiscuous - promiscuous mode */ +/* int bIAHash - accept if IA passes the hash filter */ + +void rawnet_arch_recv_ctl(int bBroadcast, int bIA, int bMulticast, int bCorrect, int bPromiscuous, int bIAHash) +{ +#ifdef RAWNET_DEBUG_ARCH + log_message(rawnet_arch_log, "rawnet_arch_recv_ctl() called with the following parameters:" ); + log_message(rawnet_arch_log, "\tbBroadcast = %s", bBroadcast ? "TRUE" : "FALSE"); + log_message(rawnet_arch_log, "\tbIA = %s", bIA ? "TRUE" : "FALSE"); + log_message(rawnet_arch_log, "\tbMulticast = %s", bMulticast ? "TRUE" : "FALSE"); + log_message(rawnet_arch_log, "\tbCorrect = %s", bCorrect ? "TRUE" : "FALSE"); + log_message(rawnet_arch_log, "\tbPromiscuous = %s", bPromiscuous ? "TRUE" : "FALSE"); + log_message(rawnet_arch_log, "\tbIAHash = %s", bIAHash ? "TRUE" : "FALSE"); +#endif +} + +void rawnet_arch_line_ctl(int bEnableTransmitter, int bEnableReceiver ) +{ +#ifdef RAWNET_DEBUG_ARCH + log_message(rawnet_arch_log, + "rawnet_arch_line_ctl() called with the following parameters:"); + log_message(rawnet_arch_log, + "\tbEnableTransmitter = %s", bEnableTransmitter ? "TRUE" : "FALSE"); + log_message(rawnet_arch_log, + "\tbEnableReceiver = %s", bEnableReceiver ? "TRUE" : "FALSE"); +#endif +} + + +/** \brief Raw pcap packet + */ +typedef struct rawnet_pcap_internal_s { + unsigned int len; /**< length of packet data */ + uint8_t *buffer; /**< packet data */ +} rawnet_pcap_internal_t; + + +/** \brief Callback function invoked by libpcap for every incoming packet + * + * \param[in,out] param reference to internal VICE packet struct + * \param[in] header pcap header + * \param[in] pkt_data packet data + */ +static void rawnet_pcap_packet_handler(u_char *param, + const struct pcap_pkthdr *header, const u_char *pkt_data) +{ + rawnet_pcap_internal_t *pinternal = (void*)param; + + /* determine the count of bytes which has been returned, + * but make sure not to overrun the buffer + */ + if (header->caplen < pinternal->len) { + pinternal->len = header->caplen; + } + + memcpy(pinternal->buffer, pkt_data, pinternal->len); +} + + +/** \brief Receives a frame + * + * If there's none, it returns a -1 in \a pinternal->len, if there is one, + * it returns the length of the frame in bytes in \a pinternal->len. + * + * It copies the frame to \a buffer and returns the number of copied bytes as + * the return value. + * + * \param[in,out] pinternal internal VICE packet struct + * + * \note At most 'len' bytes are copied. + * + * \return number of bytes copied or -1 on failure + */ +static int rawnet_arch_receive_frame(rawnet_pcap_internal_t *pinternal) +{ + int ret = -1; + + /* check if there is something to receive */ + if (pcap_dispatch(rawnet_pcap_fp, 1, rawnet_pcap_packet_handler, + (void*)pinternal) != 0) { + /* Something has been received */ + ret = pinternal->len; + } + +#ifdef RAWNET_DEBUG_ARCH + log_message(rawnet_arch_log, + "rawnet_arch_receive_frame() called, returns %d.", ret); +#endif + + return ret; +} + + +/** \brief Transmit a frame + * + * \param[in] force Delete waiting frames in transmit buffer + * \param[in] onecoll Terminate after just one collision + * \param[in] inhibit_crc Do not append CRC to the transmission + * \param[in] tx_pad_dis Disable padding to 60 Bytes + * \param[in] txlength Frame length + * \param[in] txframe Pointer to the frame to be transmitted + */ +void rawnet_arch_transmit(int force, int onecoll, int inhibit_crc, + int tx_pad_dis, int txlength, uint8_t *txframe) +{ + +#ifdef RAWNET_DEBUG_ARCH + log_message(rawnet_arch_log, + "rawnet_arch_transmit() called, with: force = %s, onecoll = %s, " + "inhibit_crc=%s, tx_pad_dis=%s, txlength=%u", + force ? "TRUE" : "FALSE", + onecoll ? "TRUE" : "FALSE", + inhibit_crc ? "TRUE" : "FALSE", + tx_pad_dis ? "TRUE" : "FALSE", + txlength); +#endif + +#ifdef RAWNET_DEBUG_PKTDUMP + debug_output("Transmit frame: ", txframe, txlength); +#endif /* #ifdef RAWNET_DEBUG_PKTDUMP */ + + if (pcap_sendpacket(rawnet_pcap_fp, txframe, txlength) < 0) { + log_message(rawnet_arch_log, "WARNING! Could not send packet!"); + } +} + + +/** + * \brief Check if a frame was received + * + * This function checks if there was a frame received. If so, it returns 1, + * else 0. + * + * If there was no frame, none of the parameters is changed! + * + * If there was a frame, the following actions are done: + * + * - at maximum \a plen byte are transferred into the buffer given by \a pbuffer + * - \a plen gets the length of the received frame, EVEN if this is more + * than has been copied to \a pbuffer! + * - if the dest. address was accepted by the hash filter, \a phashed is set, + * else cleared. + * - if the dest. address was accepted by the hash filter, \a phash_index is + * set to the number of the rule leading to the acceptance + * - if the receive was ok (good CRC and valid length), \a *prx_ok is set, else + * cleared. + * - if the dest. address was accepted because it's exactly our MAC address + * (set by rawnet_arch_set_mac()), \a pcorrect_mac is set, else cleared. + * - if the dest. address was accepted since it was a broadcast address, + * \a pbroadcast is set, else cleared. + * - if the received frame had a crc error, \a pcrc_error is set, else cleared + * + * \param[out] buffer where to store a frame + * \param[in,out] plen IN: maximum length of frame to copy; + * OUT: length of received frame OUT + * can be bigger than IN if received frame was + * longer than supplied buffer + * \param[out] phashed set if the dest. address is accepted by the + * hash filter + * \param[out] phash_index hash table index if hashed == TRUE + * \param[out] prx_ok set if good CRC and valid length + * \param[out] pcorrect_mac set if dest. address is exactly our IA + * \param[out[ pbroadcast set if dest. address is a broadcast address + * \param[out] pcrc_error set if received frame had a CRC error +*/ +int rawnet_arch_receive(uint8_t *pbuffer, int *plen, int *phashed, + int *phash_index, int *prx_ok, int *pcorrect_mac, int *pbroadcast, + int *pcrc_error) +{ + int len; + + rawnet_pcap_internal_t internal = { *plen, pbuffer }; + +#ifdef RAWNET_DEBUG_ARCH + log_message(rawnet_arch_log, + "rawnet_arch_receive() called, with *plen=%u.", + *plen); +#endif + + assert((*plen & 1) == 0); + + len = rawnet_arch_receive_frame(&internal); + + if (len != -1) { + +#ifdef RAWNET_DEBUG_PKTDUMP + debug_output("Received frame: ", internal.buffer, internal.len); +#endif /* #ifdef RAWNET_DEBUG_PKTDUMP */ + + if (len & 1) { + ++len; + } + + *plen = len; + + /* we don't decide if this frame fits the needs; + * by setting all zero, we let tfe.c do the work + * for us + */ + *phashed = + *phash_index = + *pbroadcast = + *pcorrect_mac = + *pcrc_error = 0; + + /* this frame has been received correctly */ + *prx_ok = 1; + + return 1; + } + + return 0; +} + + +/** \brief Find default device on which to capture + * + * \return name of standard interface + * + * \note pcap_lookupdev() has been deprecated, so the correct way to get + * the default device is to use the first entry returned by + * pcap_findalldevs(). + * See http://www.tcpdump.org/manpages/pcap_lookupdev.3pcap.html + * + * \return default interface name or `NULL` when not found + * + * \note free the returned value with lib_free() if not `NULL` + */ +char *rawnet_arch_get_standard_interface(void) +{ + char *dev = NULL; + char errbuf[PCAP_ERRBUF_SIZE]; + pcap_if_t *list; + + if (pcap_findalldevs(&list, errbuf) == 0 && list != NULL) { + dev = lib_stralloc(list[0].name); + pcap_freealldevs(list); + } + return dev; +} + +#endif /* #ifdef HAVE_RAWNET */ diff --git a/src/rawnet/rawnetarch_win32.c b/src/rawnet/rawnetarch_win32.c new file mode 100644 index 0000000..9f2ed1e --- /dev/null +++ b/src/rawnet/rawnetarch_win32.c @@ -0,0 +1,542 @@ +/** \file rawnetarch_win32.c + * \brief Raw ethernet interface, Win32 stuff + * + * \author Spiro Trikaliotis + */ + +/* + * This file is part of VICE, the Versatile Commodore Emulator. + * See README for copyright notice. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA. + * + */ + +// #include "vice.h" + +#ifdef HAVE_RAWNET + +/* #define WPCAP */ + +#include + +#include +#include +#include +#include + +// #include "lib.h" +// #include "log.h" +#include "rawnet.h" +#include "rawnetarch.h" +#include "rawnetsupp.h" + +typedef pcap_t *(*pcap_open_live_t)(const char *, int, int, int, char *); +typedef int (*pcap_dispatch_t)(pcap_t *, int, pcap_handler, u_char *); +typedef int (*pcap_setnonblock_t)(pcap_t *, int, char *); +typedef int (*pcap_datalink_t)(pcap_t *); +typedef int (*pcap_findalldevs_t)(pcap_if_t **, char *); +typedef void (*pcap_freealldevs_t)(pcap_if_t *); +typedef int (*pcap_sendpacket_t)(pcap_t *p, u_char *buf, int size); +typedef char *(*pcap_lookupdev_t)(char *); + +/** #define RAWNET_DEBUG_ARCH 1 **/ +/** #define RAWNET_DEBUG_PKTDUMP 1 **/ + +/* #define RAWNET_DEBUG_FRAMES - might be defined in rawnetarch.h ! */ + +#define RAWNET_DEBUG_WARN 1 /* this should not be deactivated */ + +static pcap_open_live_t p_pcap_open_live; +static pcap_dispatch_t p_pcap_dispatch; +static pcap_setnonblock_t p_pcap_setnonblock; +static pcap_findalldevs_t p_pcap_findalldevs; +static pcap_freealldevs_t p_pcap_freealldevs; +static pcap_sendpacket_t p_pcap_sendpacket; +static pcap_datalink_t p_pcap_datalink; +static pcap_lookupdev_t p_pcap_lookupdev; + +static HINSTANCE pcap_library = NULL; + +/* ------------------------------------------------------------------------- */ +/* variables needed */ + +//static log_t rawnet_arch_log = LOG_ERR; + +static pcap_if_t *EthernetPcapNextDev = NULL; +static pcap_if_t *EthernetPcapAlldevs = NULL; +static pcap_t *EthernetPcapFP = NULL; + +static char EthernetPcapErrbuf[PCAP_ERRBUF_SIZE]; + +#ifdef RAWNET_DEBUG_PKTDUMP + +static void debug_output(const char *text, uint8_t *what, int count) +{ + char buffer[256]; + char *p = buffer; + char *pbuffer1 = what; + int len1 = count; + int i; + + sprintf(buffer, "\n%s: length = %u\n", text, len1); + OutputDebugString(buffer); + do { + p = buffer; + for (i = 0; (i < 8) && len1 > 0; len1--, i++) { + sprintf( p, "%02x ", (unsigned int)(unsigned char)*pbuffer1++); + p += 3; + } + *(p - 1) = '\n'; + *p = 0; + OutputDebugString(buffer); + } while (len1 > 0); +} +#endif /* #ifdef RAWNET_DEBUG_PKTDUMP */ + + +static void EthernetPcapFreeLibrary(void) +{ + if (pcap_library) { + if (!FreeLibrary(pcap_library)) { + log_message(rawnet_arch_log, "FreeLibrary WPCAP.DLL failed!"); + } + pcap_library = NULL; + + p_pcap_open_live = NULL; + p_pcap_dispatch = NULL; + p_pcap_setnonblock = NULL; + p_pcap_findalldevs = NULL; + p_pcap_freealldevs = NULL; + p_pcap_sendpacket = NULL; + p_pcap_datalink = NULL; + p_pcap_lookupdev = NULL; + } +} + +/* since I don't like typing too much... */ +#define GET_PROC_ADDRESS_AND_TEST( _name_ ) \ + p_##_name_ = (_name_##_t) GetProcAddress(pcap_library, #_name_); \ + if (!p_##_name_ ) { \ + log_message(rawnet_arch_log, "GetProcAddress " #_name_ " failed!"); \ + EthernetPcapFreeLibrary(); \ + return FALSE; \ + } + +static BOOL EthernetPcapLoadLibrary(void) +{ + if (!pcap_library) { + pcap_library = LoadLibrary(TEXT("wpcap.dll")); + + if (!pcap_library) { + log_message(rawnet_arch_log, "LoadLibrary WPCAP.DLL failed!"); + return FALSE; + } + + GET_PROC_ADDRESS_AND_TEST(pcap_open_live); + GET_PROC_ADDRESS_AND_TEST(pcap_dispatch); + GET_PROC_ADDRESS_AND_TEST(pcap_setnonblock); + GET_PROC_ADDRESS_AND_TEST(pcap_findalldevs); + GET_PROC_ADDRESS_AND_TEST(pcap_freealldevs); + GET_PROC_ADDRESS_AND_TEST(pcap_sendpacket); + GET_PROC_ADDRESS_AND_TEST(pcap_datalink); + GET_PROC_ADDRESS_AND_TEST(pcap_lookupdev); + } + + return TRUE; +} + +#undef GET_PROC_ADDRESS_AND_TEST + + +/* + These functions let the UI enumerate the available interfaces. + + First, rawnet_arch_enumadapter_open() is used to start enumeration. + + rawnet_arch_enumadapter is then used to gather information for each adapter present + on the system, where: + + ppname points to a pointer which will hold the name of the interface + ppdescription points to a pointer which will hold the description of the interface + + For each of these parameters, new memory is allocated, so it has to be + freed with lib_free(). + + rawnet_arch_enumadapter_close() must be used to stop processing. + + Each function returns 1 on success, and 0 on failure. + rawnet_arch_enumadapter() only fails if there is no more adpater; in this case, + *ppname and *ppdescription are not altered. +*/ +int rawnet_arch_enumadapter_open(void) +{ + if (!EthernetPcapLoadLibrary()) { + return 0; + } + + if ((*p_pcap_findalldevs)(&EthernetPcapAlldevs, EthernetPcapErrbuf) == -1) { + log_message(rawnet_arch_log, "ERROR in rawnet_arch_enumadapter_open: pcap_findalldevs: '%s'", EthernetPcapErrbuf); + return 0; + } + + if (!EthernetPcapAlldevs) { + log_message(rawnet_arch_log, "ERROR in rawnet_arch_enumadapter_open, finding all pcap devices - Do we have the necessary privilege rights?"); + return 0; + } + + EthernetPcapNextDev = EthernetPcapAlldevs; + + return 1; +} + +int rawnet_arch_enumadapter(char **ppname, char **ppdescription) +{ + if (!EthernetPcapNextDev) { + return 0; + } + + *ppname = lib_stralloc(EthernetPcapNextDev->name); + *ppdescription = lib_stralloc(EthernetPcapNextDev->description); + + EthernetPcapNextDev = EthernetPcapNextDev->next; + + return 1; +} + +int rawnet_arch_enumadapter_close(void) +{ + if (EthernetPcapAlldevs) { + (*p_pcap_freealldevs)(EthernetPcapAlldevs); + EthernetPcapAlldevs = NULL; + } + return 1; +} + +static BOOL EthernetPcapOpenAdapter(const char *interface_name) +{ + pcap_if_t *EthernetPcapDevice = NULL; + + if (!rawnet_enumadapter_open()) { + return FALSE; + } else { + /* look if we can find the specified adapter */ + char *pname; + char *pdescription; + BOOL found = FALSE; + + if (interface_name) { + /* we have an interface name, try it */ + EthernetPcapDevice = EthernetPcapAlldevs; + + while (rawnet_enumadapter(&pname, &pdescription)) { + if (strcmp(pname, interface_name) == 0) { + found = TRUE; + } + lib_free(pname); + lib_free(pdescription); + if (found) break; + EthernetPcapDevice = EthernetPcapNextDev; + } + } + + if (!found) { + /* just take the first adapter */ + EthernetPcapDevice = EthernetPcapAlldevs; + } + } + + EthernetPcapFP = (*p_pcap_open_live)(EthernetPcapDevice->name, 1700, 1, 20, EthernetPcapErrbuf); + if (EthernetPcapFP == NULL) { + log_message(rawnet_arch_log, "ERROR opening adapter: '%s'", EthernetPcapErrbuf); + rawnet_enumadapter_close(); + return FALSE; + } + + if ((*p_pcap_setnonblock)(EthernetPcapFP, 1, EthernetPcapErrbuf) < 0) { + log_message(rawnet_arch_log, "WARNING: Setting PCAP to non-blocking failed: '%s'", EthernetPcapErrbuf); + } + + /* Check the link layer. We support only Ethernet for simplicity. */ + if ((*p_pcap_datalink)(EthernetPcapFP) != DLT_EN10MB) { + log_message(rawnet_arch_log, "ERROR: Ethernet works only on Ethernet networks."); + rawnet_enumadapter_close(); + return FALSE; + } + + rawnet_enumadapter_close(); + return TRUE; +} + +/* ------------------------------------------------------------------------- */ +/* the architecture-dependend functions */ + +int rawnet_arch_init(void) +{ + //rawnet_arch_log = log_open("EthernetARCH"); + + if (!EthernetPcapLoadLibrary()) { + return 0; + } + + return 1; +} + +void rawnet_arch_pre_reset( void ) +{ +#ifdef RAWNET_DEBUG_ARCH + log_message(rawnet_arch_log, "rawnet_arch_pre_reset()."); +#endif +} + +void rawnet_arch_post_reset( void ) +{ +#ifdef RAWNET_DEBUG_ARCH + log_message(rawnet_arch_log, "rawnet_arch_post_reset()."); +#endif +} + +int rawnet_arch_activate(const char *interface_name) +{ +#ifdef RAWNET_DEBUG_ARCH + log_message(rawnet_arch_log, "rawnet_arch_activate()."); +#endif + if (!EthernetPcapOpenAdapter(interface_name)) { + return 0; + } + return 1; +} + +void rawnet_arch_deactivate( void ) +{ +#ifdef RAWNET_DEBUG_ARCH + log_message(rawnet_arch_log, "rawnet_arch_deactivate()."); +#endif +} + +void rawnet_arch_set_mac( const uint8_t mac[6] ) +{ +#if defined(RAWNET_DEBUG_ARCH) || defined(RAWNET_DEBUG_FRAMES) + log_message(rawnet_arch_log, "New MAC address set: %02X:%02X:%02X:%02X:%02X:%02X.", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); +#endif +} + +void rawnet_arch_set_hashfilter(const uint32_t hash_mask[2]) +{ +#if defined(RAWNET_DEBUG_ARCH) || defined(RAWNET_DEBUG_FRAMES) + log_message(rawnet_arch_log, "New hash filter set: %08X:%08X.", hash_mask[1], hash_mask[0]); +#endif +} + +/* int bBroadcast - broadcast */ +/* int bIA - individual address (IA) */ +/* int bMulticast - multicast if address passes the hash filter */ +/* int bCorrect - accept correct frames */ +/* int bPromiscuous - promiscuous mode */ +/* int bIAHash - accept if IA passes the hash filter */ + + +void rawnet_arch_recv_ctl(int bBroadcast, int bIA, int bMulticast, int bCorrect, int bPromiscuous, int bIAHash) +{ +#if defined(RAWNET_DEBUG_ARCH) || defined(RAWNET_DEBUG_FRAMES) + log_message(rawnet_arch_log, "rawnet_arch_recv_ctl() called with the following parameters:" ); + log_message(rawnet_arch_log, "\tbBroadcast = %s", bBroadcast ? "TRUE" : "FALSE" ); + log_message(rawnet_arch_log, "\tbIA = %s", bIA ? "TRUE" : "FALSE" ); + log_message(rawnet_arch_log, "\tbMulticast = %s", bMulticast ? "TRUE" : "FALSE" ); + log_message(rawnet_arch_log, "\tbCorrect = %s", bCorrect ? "TRUE" : "FALSE" ); + log_message(rawnet_arch_log, "\tbPromiscuous = %s", bPromiscuous ? "TRUE" : "FALSE" ); + log_message(rawnet_arch_log, "\tbIAHash = %s", bIAHash ? "TRUE" : "FALSE" ); +#endif +} + +void rawnet_arch_line_ctl(int bEnableTransmitter, int bEnableReceiver) +{ +#if defined(RAWNET_DEBUG_ARCH) || defined(RAWNET_DEBUG_FRAMES) + log_message(rawnet_arch_log, "rawnet_arch_line_ctl() called with the following parameters:" ); + log_message(rawnet_arch_log, "\tbEnableTransmitter = %s", bEnableTransmitter ? "TRUE" : "FALSE" ); + log_message(rawnet_arch_log, "\tbEnableReceiver = %s", bEnableReceiver ? "TRUE" : "FALSE" ); +#endif +} + +typedef struct Ethernet_PCAP_internal_s { + unsigned int len; + uint8_t *buffer; +} Ethernet_PCAP_internal_t; + +/* Callback function invoked by libpcap for every incoming packet */ +static void EthernetPcapPacketHandler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data) +{ + Ethernet_PCAP_internal_t *pinternal = (void*)param; + + /* determine the count of bytes which has been returned, + * but make sure not to overrun the buffer + */ + if (header->caplen < pinternal->len) { + pinternal->len = header->caplen; + } + + memcpy(pinternal->buffer, pkt_data, pinternal->len); +} + +/* the following function receives a frame. + + If there's none, it returns a -1. + If there is one, it returns the length of the frame in bytes. + + It copies the frame to *buffer and returns the number of copied + bytes as return value. + + At most 'len' bytes are copied. +*/ +static int rawnet_arch_receive_frame(Ethernet_PCAP_internal_t *pinternal) +{ + int ret = -1; + + /* check if there is something to receive */ + if ((*p_pcap_dispatch)(EthernetPcapFP, 1, EthernetPcapPacketHandler, (void*)pinternal) != 0) { + /* Something has been received */ + ret = pinternal->len; + } + +#ifdef RAWNET_DEBUG_ARCH + log_message(rawnet_arch_log, "rawnet_arch_receive_frame() called, returns %d.", ret); +#endif + + return ret; +} + +/* int force - FORCE: Delete waiting frames in transmit buffer */ +/* int onecoll - ONECOLL: Terminate after just one collision */ +/* int inhibit_crc - INHIBITCRC: Do not append CRC to the transmission */ +/* int tx_pad_dis - TXPADDIS: Disable padding to 60 Bytes */ +/* int txlength - Frame length */ +/* uint8_t *txframe - Pointer to the frame to be transmitted */ + +void rawnet_arch_transmit(int force, int onecoll, int inhibit_crc, int tx_pad_dis, int txlength, uint8_t *txframe) +{ +#ifdef RAWNET_DEBUG_ARCH + log_message(rawnet_arch_log, "rawnet_arch_transmit() called, with: force = %s, onecoll = %s, inhibit_crc=%s, tx_pad_dis=%s, txlength=%u", + force ? "TRUE" : "FALSE", + onecoll ? "TRUE" : "FALSE", + inhibit_crc ? "TRUE" : "FALSE", + tx_pad_dis ? "TRUE" : "FALSE", + txlength); +#endif + +#ifdef RAWNET_DEBUG_PKTDUMP + debug_output("Transmit frame: ", txframe, txlength); +#endif /* #ifdef RAWNET_DEBUG_PKTDUMP */ + + if ((*p_pcap_sendpacket)(EthernetPcapFP, txframe, txlength) == -1) { + log_message(rawnet_arch_log, "WARNING! Could not send packet!"); + } +} + +/* + rawnet_arch_receive() + + This function checks if there was a frame received. + If so, it returns 1, else 0. + + If there was no frame, none of the parameters is changed! + + If there was a frame, the following actions are done: + + - at maximum *plen byte are transferred into the buffer given by pbuffer + - *plen gets the length of the received frame, EVEN if this is more + than has been copied to pbuffer! + - if the dest. address was accepted by the hash filter, *phashed is set, else + cleared. + - if the dest. address was accepted by the hash filter, *phash_index is + set to the number of the rule leading to the acceptance + - if the receive was ok (good CRC and valid length), *prx_ok is set, + else cleared. + - if the dest. address was accepted because it's exactly our MAC address + (set by rawnet_arch_set_mac()), *pcorrect_mac is set, else cleared. + - if the dest. address was accepted since it was a broadcast address, + *pbroadcast is set, else cleared. + - if the received frame had a crc error, *pcrc_error is set, else cleared +*/ + +/* uint8_t *pbuffer - where to store a frame */ +/* int *plen - IN: maximum length of frame to copy; + OUT: length of received frame + OUT can be bigger than IN if received frame was + longer than supplied buffer */ +/* int *phashed - set if the dest. address is accepted by the hash filter */ +/* int *phash_index - hash table index if hashed == TRUE */ +/* int *prx_ok - set if good CRC and valid length */ +/* int *pcorrect_mac - set if dest. address is exactly our IA */ +/* int *pbroadcast - set if dest. address is a broadcast address */ +/* int *pcrc_error - set if received frame had a CRC error */ + +int rawnet_arch_receive(uint8_t *pbuffer, int *plen, int *phashed, int *phash_index, int *prx_ok, int *pcorrect_mac, int *pbroadcast, int *pcrc_error) +{ + int len; + + Ethernet_PCAP_internal_t internal; + + internal.len = *plen; + internal.buffer = pbuffer; + +#ifdef RAWNET_DEBUG_ARCH + log_message(rawnet_arch_log, "rawnet_arch_receive() called, with *plen=%u.", *plen); +#endif + + assert((*plen & 1) == 0); + + len = rawnet_arch_receive_frame(&internal); + + if (len != -1) { + +#ifdef RAWNET_DEBUG_PKTDUMP + debug_output("Received frame: ", internal.buffer, internal.len); +#endif /* #ifdef RAWNET_DEBUG_PKTDUMP */ + + if (len & 1) { + ++len; + } + + *plen = len; + + /* we don't decide if this frame fits the needs; + * by setting all zero, we let ethernet.c do the work + * for us + */ + *phashed = *phash_index = *pbroadcast = *pcorrect_mac = *pcrc_error = 0; + + /* this frame has been received correctly */ + *prx_ok = 1; + + return 1; + } + + return 0; +} + +char *rawnet_arch_get_standard_interface(void) +{ + char *dev, errbuf[PCAP_ERRBUF_SIZE]; + + if (!EthernetPcapLoadLibrary()) { + return NULL; + } + + dev = lib_stralloc((*p_pcap_lookupdev)(errbuf)); + + return dev; +} +#endif /* #ifdef HAVE_RAWNET */ diff --git a/src/rawnet/rawnetsupp.c b/src/rawnet/rawnetsupp.c new file mode 100644 index 0000000..94c8541 --- /dev/null +++ b/src/rawnet/rawnetsupp.c @@ -0,0 +1,151 @@ +/* + * This file is a consolidation of functions required for tfe + * emulation taken from the following files + * + * lib.c - Library functions. + * util.c - Miscellaneous utility functions. + * crc32.c + * + * Written by + * Andreas Boose + * Ettore Perazzoli + * Andreas Matthies + * Tibor Biczo + * Spiro Trikaliotis * + * + * This file is part of VICE, the Versatile Commodore Emulator. + * See README for copyright notice. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA. + * + */ + + +#include +#include +#include +#include +#include + +#include "rawnetsupp.h" + + + +#define CRC32_POLY 0xedb88320 +static unsigned long crc32_table[256]; +static int crc32_is_initialized = 0; + +void lib_free(void *ptr) { + free(ptr); +} + +void *lib_malloc(size_t size) { + + void *ptr = malloc(size); + + if (ptr == NULL && size > 0) { + perror("lib_malloc"); + exit(-1); + } + + return ptr; +} + +/*-----------------------------------------------------------------------*/ + +/* Malloc enough space for `str', copy `str' into it and return its + address. */ +char *lib_stralloc(const char *str) { + char *ptr; + + if (str == NULL) { + fprintf(stderr, "lib_stralloc\n"); + exit(-1); + } + + ptr = strdup(str); + if (!ptr) { + perror("lib_stralloc"); + exit(-1); + } + + return ptr; +} + + + +/* Like realloc, but abort if not enough memory is available. */ +void *lib_realloc(void *ptr, size_t size) { + + + void *new_ptr = realloc(ptr, size); + + if (new_ptr == NULL) { + perror("lib_realloc"); + exit(-1); + } + + return new_ptr; +} + +// Util Stuff + +/* Set a new value to the dynamically allocated string *str. + Returns `-1' if nothing has to be done. */ +int util_string_set(char **str, const char *new_value) { + if (*str == NULL) { + if (new_value != NULL) + *str = lib_stralloc(new_value); + } else { + if (new_value == NULL) { + lib_free(*str); + *str = NULL; + } else { + /* Skip copy if src and dest are already the same. */ + if (strcmp(*str, new_value) == 0) + return -1; + + *str = (char *)lib_realloc(*str, strlen(new_value) + 1); + strcpy(*str, new_value); + } + } + return 0; +} + + +// crc32 Stuff + +unsigned long crc32_buf(const char *buffer, unsigned int len) { + int i, j; + unsigned long crc, c; + const char *p; + + if (!crc32_is_initialized) { + for (i = 0; i < 256; i++) { + c = (unsigned long) i; + for (j = 0; j < 8; j++) + c = c & 1 ? CRC32_POLY ^ (c >> 1) : c >> 1; + crc32_table[i] = c; + } + crc32_is_initialized = 1; + } + + crc = 0xffffffff; + for (p = buffer; len > 0; ++p, --len) + crc = (crc >> 8) ^ crc32_table[(crc ^ *p) & 0xff]; + + return ~crc; +} diff --git a/src/rawnet/rawnetsupp.h b/src/rawnet/rawnetsupp.h new file mode 100644 index 0000000..34dd97d --- /dev/null +++ b/src/rawnet/rawnetsupp.h @@ -0,0 +1,56 @@ +/* + * This file is a consolidation of functions required for tfe + * emulation taken from the following files + * + * lib.h - Library functions. + * util.h - Miscellaneous utility functions. + * crc32.h + * + * Written by + * Andreas Boose + * Ettore Perazzoli + * Manfred Spraul + * Andreas Matthies + * Tibor Biczo + * Spiro Trikaliotis * + * + * This file is part of VICE, the Versatile Commodore Emulator. + * See README for copyright notice. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA. + * + */ + + + +#ifndef _TFESUPP_H +#define _TFESUPP_H + +#include + +extern FILE* g_fh; // Filehandle for log file + +extern void *lib_malloc(size_t size); +extern void *lib_realloc(void *p, size_t size); +extern void lib_free(void *ptr); +extern char *lib_stralloc(const char *str); + +extern int util_string_set(char **str, const char *new_value); + +extern unsigned long crc32_buf(const char *buffer, unsigned int len); + +#define log_message(level,...) fprintf(stderr,__VA_ARGS__) +#endif From 1b1983b64a0ed53e26d7ef237b00f973c59802b4 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Fri, 14 Dec 2018 20:35:04 -0500 Subject: [PATCH 002/186] rawnet - add link libraries, debugging defines. --- src/rawnet/CMakeLists.txt | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/rawnet/CMakeLists.txt b/src/rawnet/CMakeLists.txt index d91e72d..c43e371 100644 --- a/src/rawnet/CMakeLists.txt +++ b/src/rawnet/CMakeLists.txt @@ -1,5 +1,5 @@ -if (WIN32) +if(WIN32) set(rawnetarch rawnetarch_win32.c) elseif(APPLE) set(rawnetarch rawnetarch_darwin.c) @@ -10,4 +10,11 @@ endif() add_library(rawnet cs8900.c rawnet.c rawnetsupp.c ${rawnetarch}) target_compile_definitions(rawnet PUBLIC HAVE_RAWNET) +target_compile_definitions(rawnet PRIVATE CS8900_DEBUG CS8900_DEBUG_STORE CS8900_DEBUG_LOAD) +if(WIN32) +elseif(APPLE) + target_link_libraries(rawnet PRIVATE "-framework vmnet") +elseif(UNIX) + target_link_libraries(rawnet PRIVATE pcap) +endif() \ No newline at end of file From a8c23a4c2a086db0d88e6dc6f320bb3c8735b169 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Fri, 14 Dec 2018 20:35:23 -0500 Subject: [PATCH 003/186] rawnet - fix logging, add hexdump. --- src/rawnet/rawnetsupp.c | 29 +++++++++++++++++++++++++++++ src/rawnet/rawnetsupp.h | 4 +++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/rawnet/rawnetsupp.c b/src/rawnet/rawnetsupp.c index 94c8541..4f6e33e 100644 --- a/src/rawnet/rawnetsupp.c +++ b/src/rawnet/rawnetsupp.c @@ -149,3 +149,32 @@ unsigned long crc32_buf(const char *buffer, unsigned int len) { return ~crc; } + +void rawnet_hexdump(unsigned char *what, int count ) { + static const char hex[] = "0123456789abcdef"; + char buffer1[16 * 3 + 1]; + char buffer2[16 + 1]; + unsigned offset; + + + offset = 0; + while (count) { + unsigned char x = *what++; + + buffer1[offset * 3] = hex[x >> 4]; + buffer1[offset * 3 + 1] = hex[x & 0x0f]; + buffer1[offset * 3 + 2] = ' '; + + buffer2[offset] = isprint(x) ? x : '.'; + + + --count; + ++offset; + if (offset == 16 || count == 0) { + buffer1[offset * 3] = 0; + buffer2[offset] = 0; + fprintf(stderr, "%-50s %s\n", buffer1, buffer2); + offset = 0; + } + } +} diff --git a/src/rawnet/rawnetsupp.h b/src/rawnet/rawnetsupp.h index 34dd97d..3e1f667 100644 --- a/src/rawnet/rawnetsupp.h +++ b/src/rawnet/rawnetsupp.h @@ -52,5 +52,7 @@ extern int util_string_set(char **str, const char *new_value); extern unsigned long crc32_buf(const char *buffer, unsigned int len); -#define log_message(level,...) fprintf(stderr,__VA_ARGS__) +void rawnet_hexdump(unsigned char *what, int count ); + +#define log_message(level,...) do { fprintf(stderr,__VA_ARGS__); fputs("\n", stderr); } while (0) #endif From 12c8f215ba4d4a7d5e2c872a8288a103196bd129 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Fri, 14 Dec 2018 20:35:50 -0500 Subject: [PATCH 004/186] darwin vmnet tweaks. --- src/rawnet/rawnetarch_darwin.c | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/src/rawnet/rawnetarch_darwin.c b/src/rawnet/rawnetarch_darwin.c index b8ed502..69af2aa 100644 --- a/src/rawnet/rawnetarch_darwin.c +++ b/src/rawnet/rawnetarch_darwin.c @@ -63,6 +63,7 @@ int rawnet_arch_activate(const char *interface_name) { if (status == VMNET_SUCCESS) { const char *cp; cp = xpc_dictionary_get_string(params, vmnet_mac_address_key); + fprintf(stderr, "vmnet mac: %s\n", cp); sscanf(cp, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &interface_mac[0], &interface_mac[1], @@ -74,6 +75,9 @@ int rawnet_arch_activate(const char *interface_name) { interface_mtu = xpc_dictionary_get_uint64(params, vmnet_mtu_key); interface_packet_size = xpc_dictionary_get_uint64(params, vmnet_max_packet_size_key); + + fprintf(stderr, "vmnet mtu: %u\n", (unsigned)interface_mtu); + } dispatch_semaphore_signal(sem); }); @@ -81,7 +85,7 @@ int rawnet_arch_activate(const char *interface_name) { if (interface_status != VMNET_SUCCESS) { - log_message(rawnet_arch_log, "vmnet_start_interface failed\n"); + log_message(rawnet_arch_log, "vmnet_start_interface failed"); if (interface) { vmnet_stop_interface(interface, q, ^(vmnet_return_t status){ dispatch_semaphore_signal(sem); @@ -143,10 +147,11 @@ void rawnet_arch_transmit(int force, int onecoll, int inhibit_crc, int tx_pad_di if (txlength == 0) return; if (txlength > interface_packet_size) { - log_message(rawnet_arch_log, "packet is too big: %d\n", txlength); + log_message(rawnet_arch_log, "packet is too big: %d", txlength); return; } + iov.iov_base = txframe; iov.iov_len = txlength; @@ -154,10 +159,15 @@ void rawnet_arch_transmit(int force, int onecoll, int inhibit_crc, int tx_pad_di v.vm_pkt_iov = &iov; v.vm_pkt_iovcnt = 1; v.vm_flags = 0; + + + fprintf(stderr, "\rawnet_arch_transmit: %u\n", (unsigned)iov.iov_len); + rawnet_hexdump(iov.iov_base, iov.iov_len); + st = vmnet_write(interface, &v, &count); if (st != VMNET_SUCCESS) { - log_message(rawnet_arch_log, "vmnet_write failed!\n"); + log_message(rawnet_arch_log, "vmnet_write failed!"); } return; } @@ -174,7 +184,7 @@ int rawnet_arch_receive(uint8_t *pbuffer, int *plen, int *phashed, int *phash_in struct iovec iov; - buffer = malloc(interface_packet_size); + buffer = calloc(interface_packet_size, 1); iov.iov_base = buffer; iov.iov_len = interface_packet_size; @@ -186,20 +196,25 @@ int rawnet_arch_receive(uint8_t *pbuffer, int *plen, int *phashed, int *phash_in st = vmnet_read(interface, &v, &count); if (st != VMNET_SUCCESS) { - log_message(rawnet_arch_log, "vmnet_write failed!\n"); + log_message(rawnet_arch_log, "vmnet_write failed!"); free(buffer); return 0; } - if (count == 0) { + if (count < 1) { free(buffer); return 0; } - xfer = iov.iov_len; + + // iov.iov_len is not updated with the read count, apparently. + fprintf(stderr, "\nrawnet_arch_receive: %u\n", (unsigned)v.vm_pkt_size); + rawnet_hexdump(iov.iov_base, v.vm_pkt_size); + + xfer = v.vm_pkt_size; if (xfer > *plen) xfer = *plen; memcpy(pbuffer, buffer, xfer); - xfer = iov.iov_len; + xfer = v.vm_pkt_size; if (xfer & 0x01) ++xfer; /* ??? */ *plen = xfer; /* actual frame size */ From ba5080791006714d79e7d6b97a1b89c25fa3a962 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Fri, 14 Dec 2018 20:37:39 -0500 Subject: [PATCH 005/186] tfe -> rawnet. --- src/Makefile | 1 + src/config.c | 20 +++++++++--------- src/moremem.c | 18 +++++++++-------- src/sim65816.c | 55 ++++++++++++++++++++++++++++++++++---------------- 4 files changed, 59 insertions(+), 35 deletions(-) diff --git a/src/Makefile b/src/Makefile index 7da45aa..7db8e09 100644 --- a/src/Makefile +++ b/src/Makefile @@ -9,6 +9,7 @@ ATOBJ = atbridge/aarp.o atbridge/atbridge.o atbridge/elap.o atbridge/llap.o atbr PCAPOBJ = atbridge/pcap_delay.o TFEOBJ = tfe/tfe.o tfe/tfearch.o tfe/tfesupp.o FSTOBJ = unix_host_common.o host_common.o host_fst.o host_mli.o +RAWNETOBJ = rawnet/cs8900.o rawnet/rawnet.o rawnet/rawnetsupp.o include vars diff --git a/src/config.c b/src/config.c index fd0f9e5..bc10363 100644 --- a/src/config.c +++ b/src/config.c @@ -16,9 +16,8 @@ #include #endif -#ifdef HAVE_TFE -#include "tfe/tfesupp.h" -#include "tfe/protos_tfe.h" +#ifdef HAVE_RAWNET +#include "rawnet/rawnet.h" #endif #if defined _MSC_VER @@ -76,6 +75,7 @@ extern int g_joystick_button_1; extern int g_joystick_button_2; extern int g_joystick_button_3; extern int g_ethernet; +extern int g_ethernet_enabled; extern int g_halt_on_bad_read; extern int g_ignore_bad_acc; extern int g_ignore_halts; @@ -695,24 +695,24 @@ void cfg_iwreset() { imagewriter_init(g_imagewriter_dpi,g_imagewriter_paper,g_imagewriter_banner, g_imagewriter_output,g_imagewriter_multipage); return; } -#ifdef HAVE_TFE +#ifdef HAVE_RAWNET void cfg_get_tfe_name() { int i = 0; char *ppname = NULL; char *ppdes = NULL; cfg_htab_vtab(0,11); - if (tfe_enumadapter_open()) + if (rawnet_enumadapter_open()) { cfg_printf("Interface List:\n---------------"); - while(tfe_enumadapter(&ppname,&ppdes)) + while(rawnet_enumadapter(&ppname,&ppdes)) { cfg_htab_vtab(0, 13+i); cfg_printf("%2d: %s",i,ppdes); i++; - lib_free(ppname); - lib_free(ppdes); + free(ppname); + free(ppdes); } - tfe_enumadapter_close(); + rawnet_enumadapter_close(); } else { @@ -3247,7 +3247,7 @@ void config_control_panel() { if(g_cfg_slotdrive >= 0) { cfg_file_draw(); } -#ifdef HAVE_TFE +#ifdef HAVE_RAWNET /*HACK eh, at least I think it is. Display the available ethernet interfaces when in the ethernet control panel. This is the only way one can customize a menu pane. Kent did it with the directory browser, so why not.*/ diff --git a/src/moremem.c b/src/moremem.c index 47a7cbc..41496d7 100644 --- a/src/moremem.c +++ b/src/moremem.c @@ -7,8 +7,8 @@ #include "defc.h" -#ifdef HAVE_TFE -#include "tfe/protos_tfe.h" +#ifdef HAVE_RAWNET +#include "rawnet/cs8900.h" #endif extern char const g_gsplus_version_str[]; @@ -34,6 +34,8 @@ extern int g_rom_version; extern int g_user_page2_shadow; extern int g_parallel; +extern int g_ethernet_enabled; + char c; /* from iwm.c */ int g_num_shadow_all_banks = 0; @@ -1591,7 +1593,7 @@ int io_read(word32 loc, double *cyc_ptr) { //case 0xb8: // return 0; // break; -#ifdef HAVE_TFE +#ifdef HAVE_RAWNET /*Uthernet read access on slot 3*/ case 0xb0: case 0xb1: @@ -1609,8 +1611,8 @@ int io_read(word32 loc, double *cyc_ptr) { case 0xbd: case 0xbe: case 0xbf: - if (tfe_enabled) { - return tfe_read((word16)loc & 0xf); + if (g_ethernet_enabled) { + return cs8900_read((word16)loc & 0xf); } else {return 0;} @@ -2326,7 +2328,7 @@ void io_write(word32 loc, int val, double *cyc_ptr) { //case 0xb8: case 0xb9: case 0xba: case 0xbb: //case 0xbc: case 0xbd: case 0xbe: case 0xbf: // UNIMPL_WRITE; -#ifdef HAVE_TFE +#ifdef HAVE_RAWNET /*Uthernet write access on slot 3*/ case 0xb0: case 0xb1: @@ -2344,9 +2346,9 @@ void io_write(word32 loc, int val, double *cyc_ptr) { case 0xbd: case 0xbe: case 0xbf: - if (tfe_enabled) + if (g_ethernet_enabled) { - tfe_store((word16)loc & 0xf, (byte)val); + cs8900_store((word16)loc & 0xf, (byte)val); return; } else diff --git a/src/sim65816.c b/src/sim65816.c index 99f846e..ed6150b 100644 --- a/src/sim65816.c +++ b/src/sim65816.c @@ -21,9 +21,9 @@ extern char g_config_gsplus_screenshot_dir[]; #define vsnprintf _vsnprintf #endif -#ifdef HAVE_TFE - #include "tfe/tfesupp.h" - #include "tfe/protos_tfe.h" +#ifdef HAVE_RAWNET + #include "rawnet/rawnet.h" + #include "rawnet/cs8900.h" #endif #if defined (_WIN32) && !defined(WIN_SDL)|| defined(__CYGWIN__) && !defined(WIN_SDL) @@ -143,6 +143,7 @@ int g_serial_out_masking = 0; int g_serial_modem[2] = { 0, 1 }; int g_ethernet = 0; int g_ethernet_interface = 0; +int g_ethernet_enabled = 0; int g_parallel = 0; int g_parallel_out_masking = 0; int g_printer = 0; @@ -945,31 +946,44 @@ int gsplusmain(int argc, char **argv) { } printer_init(g_printer_dpi,85,110,g_printer_output,g_printer_multipage != 0); //If ethernet is enabled in config.gsport, let's initialize it -#ifdef HAVE_TFE +#ifdef HAVE_RAWNET + g_ethernet_enabled = 0; if (g_ethernet == 1) { - int i = 0; + int i = -1; + int ok = 0; char *ppname = NULL; char *ppdes = NULL; - if (tfe_enumadapter_open()) + if (rawnet_enumadapter_open()) { //Loop through the available adapters until we reach the interface number specified in config.gsport - while(tfe_enumadapter(&ppname,&ppdes)) + while(rawnet_enumadapter(&ppname,&ppdes)) { - if (i == g_ethernet_interface) break; - i++; + ++i; + if (i == g_ethernet_interface) { + + if (cs8900_init() >= 0 && cs8900_activate(ppname) >= 0) { + g_ethernet_enabled = 1; + } else { + fprintf(stderr, "Unable to start ethernet.\n"); + } + + free(ppname); + free(ppdes); + + break; + } + + free(ppname); + free(ppdes); } - tfe_enumadapter_close(); - printf("Using host ethernet interface: %s\nUthernet support is ON.\n",ppdes); + rawnet_enumadapter_close(); } - else + + if (i < 0) { - printf("No ethernet host adapters found. Do you have PCap installed/enabled?\nUthernet support is OFF.\n"); + fprintf(stderr, "No ethernet host adapters found. Do you have PCap installed/enabled?\nUthernet support is OFF.\n"); } - set_tfe_interface(ppname); //Connect the emulated ethernet device with the selected host adapter - lib_free(ppname); - lib_free(ppdes); - tfe_init(); } #endif @@ -1021,6 +1035,13 @@ int gsplusmain(int argc, char **argv) { load_roms_shut_memory(); clear_fatal_logs(); +#if HAVE_RAWNET + if (g_ethernet_enabled) { + cs8900_deactivate(); + cs8900_shutdown(); + } +#endif + // OG Not needed anymore : the emulator will quit gently //my_exit(0); end_screen(); From f55024938212249569fe69f51836bed7d648fb6e Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Tue, 18 Dec 2018 19:20:11 -0500 Subject: [PATCH 006/186] tweak rawnet_hexdump --- src/rawnet/rawnetsupp.c | 9 +++++---- src/rawnet/rawnetsupp.h | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/rawnet/rawnetsupp.c b/src/rawnet/rawnetsupp.c index 4f6e33e..49d18b2 100644 --- a/src/rawnet/rawnetsupp.c +++ b/src/rawnet/rawnetsupp.c @@ -150,22 +150,23 @@ unsigned long crc32_buf(const char *buffer, unsigned int len) { return ~crc; } -void rawnet_hexdump(unsigned char *what, int count ) { +void rawnet_hexdump(const void *what, int count) { static const char hex[] = "0123456789abcdef"; char buffer1[16 * 3 + 1]; char buffer2[16 + 1]; unsigned offset; + unsigned char *cp = (unsigned char *)what; offset = 0; - while (count) { - unsigned char x = *what++; + while (count > 0) { + unsigned char x = *cp++; buffer1[offset * 3] = hex[x >> 4]; buffer1[offset * 3 + 1] = hex[x & 0x0f]; buffer1[offset * 3 + 2] = ' '; - buffer2[offset] = isprint(x) ? x : '.'; + buffer2[offset] = (x < 0x80) && isprint(x) ? x : '.'; --count; diff --git a/src/rawnet/rawnetsupp.h b/src/rawnet/rawnetsupp.h index 3e1f667..a3d4f34 100644 --- a/src/rawnet/rawnetsupp.h +++ b/src/rawnet/rawnetsupp.h @@ -52,7 +52,7 @@ extern int util_string_set(char **str, const char *new_value); extern unsigned long crc32_buf(const char *buffer, unsigned int len); -void rawnet_hexdump(unsigned char *what, int count ); +extern void rawnet_hexdump(const void *what, int count); #define log_message(level,...) do { fprintf(stderr,__VA_ARGS__); fputs("\n", stderr); } while (0) #endif From 9370ed86046648eb2c28d54c3cc491418920a16a Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Tue, 18 Dec 2018 19:21:20 -0500 Subject: [PATCH 007/186] rawnet - add functions to get mac and mtu, if known. --- src/rawnet/rawnetarch.h | 4 ++++ src/rawnet/rawnetarch_darwin.c | 13 +++++++++++++ src/rawnet/rawnetarch_unix.c | 8 ++++++++ src/rawnet/rawnetarch_win32.c | 9 +++++++++ 4 files changed, 34 insertions(+) diff --git a/src/rawnet/rawnetarch.h b/src/rawnet/rawnetarch.h index 262dd29..f5749f3 100644 --- a/src/rawnet/rawnetarch.h +++ b/src/rawnet/rawnetarch.h @@ -65,4 +65,8 @@ extern int rawnet_arch_enumadapter_close(void); extern char *rawnet_arch_get_standard_interface(void); + +extern int rawnet_arch_get_mtu(void); +extern int rawnet_arch_get_mac(uint8_t mac[6]); + #endif diff --git a/src/rawnet/rawnetarch_darwin.c b/src/rawnet/rawnetarch_darwin.c index 69af2aa..8855d46 100644 --- a/src/rawnet/rawnetarch_darwin.c +++ b/src/rawnet/rawnetarch_darwin.c @@ -268,3 +268,16 @@ int rawnet_arch_enumadapter_close(void) { char *rawnet_arch_get_standard_interface(void) { return lib_stralloc("vmnet"); } + +int rawnet_arch_get_mtu(void) { + return interface ? interface_mtu : -1; +} + +int rawnet_arch_get_mac(uint8_t mac[6]) { + if (interface) { + memcpy(mac, interface_mac, 6); + return 1; + } + return -1; +} + diff --git a/src/rawnet/rawnetarch_unix.c b/src/rawnet/rawnetarch_unix.c index 7bf8916..4b737d9 100644 --- a/src/rawnet/rawnetarch_unix.c +++ b/src/rawnet/rawnetarch_unix.c @@ -535,4 +535,12 @@ char *rawnet_arch_get_standard_interface(void) return dev; } +extern int rawnet_arch_get_mtu(void) { + return -1; +} + +extern int rawnet_arch_get_mac(uint8_t mac[6]) { + return -1; +} + #endif /* #ifdef HAVE_RAWNET */ diff --git a/src/rawnet/rawnetarch_win32.c b/src/rawnet/rawnetarch_win32.c index 9f2ed1e..3a2655c 100644 --- a/src/rawnet/rawnetarch_win32.c +++ b/src/rawnet/rawnetarch_win32.c @@ -539,4 +539,13 @@ char *rawnet_arch_get_standard_interface(void) return dev; } + +extern int rawnet_arch_get_mtu(void) { + return -1; +} + +extern int rawnet_arch_get_mac(uint8_t mac[6]) { + return -1; +} + #endif /* #ifdef HAVE_RAWNET */ From b98635aaf1b56d0dce31827b30dcd042545ede94 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Tue, 18 Dec 2018 19:23:57 -0500 Subject: [PATCH 008/186] darwin rawnet - since the MAC address can't change, switch it with Uthernet's mac address. Unfortunately, dhcp requests include a mac address, so it also has to be swapped in the DHCP message. --- src/rawnet/rawnetarch_darwin.c | 241 +++++++++++++++++++++++++++++++-- 1 file changed, 232 insertions(+), 9 deletions(-) diff --git a/src/rawnet/rawnetarch_darwin.c b/src/rawnet/rawnetarch_darwin.c index 8855d46..dc362ab 100644 --- a/src/rawnet/rawnetarch_darwin.c +++ b/src/rawnet/rawnetarch_darwin.c @@ -20,6 +20,7 @@ static const uint8_t broadcast_mac[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; static interface_ref interface; static uint8_t interface_mac[6]; +static uint8_t interface_fake_mac[6]; static uint64_t interface_mtu; static uint64_t interface_packet_size; static vmnet_return_t interface_status; @@ -49,6 +50,7 @@ int rawnet_arch_activate(const char *interface_name) { */ memset(interface_mac, 0, sizeof(interface_mac)); + memset(interface_fake_mac, 0, sizeof(interface_fake_mac)); interface_status = 0; interface_mtu = 0; interface_packet_size = 0; @@ -124,7 +126,11 @@ void rawnet_arch_deactivate(void) { } void rawnet_arch_set_mac(const uint8_t mac[6]) { - /* NOP */ + +#ifdef RAWNET_DEBUG_ARCH + log_message( rawnet_arch_log, "New MAC address set: %02X:%02X:%02X:%02X:%02X:%02X.", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5] ); +#endif + memcpy(interface_fake_mac, mac, 6); } void rawnet_arch_set_hashfilter(const uint32_t hash_mask[2]) { /* NOP */ @@ -138,12 +144,213 @@ void rawnet_arch_line_ctl(int bEnableTransmitter, int bEnableReceiver) { /* NOP */ } + + + +enum { + eth_dest = 0, // destination address + eth_src = 6, // source address + eth_type = 12, // packet type + eth_data = 14, // packet data +}; + +enum { + ip_ver_ihl = 0, + ip_tos = 1, + ip_len = 2, + ip_id = 4, + ip_frag = 6, + ip_ttl = 8, + ip_proto = 9, + ip_header_cksum = 10, + ip_src = 12, + ip_dest = 16, + ip_data = 20, +}; + +enum { + udp_source = 0, // source port + udp_dest = 2, // destination port + udp_len = 4, // length + udp_cksum = 6, // checksum + udp_data = 8, // total length udp header +}; + +enum { + bootp_op = 0, // operation + bootp_hw = 1, // hardware type + bootp_hlen = 2, // hardware len + bootp_hp = 3, // hops + bootp_transid = 4, // transaction id + bootp_secs = 8, // seconds since start + bootp_flags = 10, // flags + bootp_ipaddr = 12, // ip address knwon by client + bootp_ipclient = 16, // client ip from server + bootp_ipserver = 20, // server ip + bootp_ipgateway = 24, // gateway ip + bootp_client_hrd = 28, // client mac address + bootp_spare = 34, + bootp_host = 44, + bootp_fname = 108, + bootp_data = 236, // total length bootp packet +}; + +enum { + arp_hw = 14, // hw type (eth = 0001) + arp_proto = 16, // protocol (ip = 0800) + arp_hwlen = 18, // hw addr len (eth = 06) + arp_protolen = 19, // proto addr len (ip = 04) + arp_op = 20, // request = 0001, reply = 0002 + arp_shw = 22, // sender hw addr + arp_sp = 28, // sender proto addr + arp_thw = 32, // target hw addr + arp_tp = 38, // target protoaddr + arp_data = 42, // total length of packet +}; + +enum { + dhcp_discover = 1, + dhcp_offer = 2, + dhcp_request = 3, + dhcp_decline = 4, + dhcp_pack = 5, + dhcp_nack = 6, + dhcp_release = 7, + dhcp_inform = 8, +}; + +static uint8_t oo[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static uint8_t ff[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + +static int is_arp(const uint8_t *packet, unsigned size) { + return size == arp_data + && packet[12] == 0x08 && packet[13] == 0x06 /* ARP */ + && packet[14] == 0x00 && packet[15] == 0x01 /* ethernet */ + && packet[16] == 0x08 && packet[17] == 0x00 /* ipv4 */ + && packet[18] == 0x06 /* hardware size */ + && packet[19] == 0x04 /* protocol size */ + ; +} + +static int is_broadcast(const uint8_t *packet, unsigned size) { + return !memcmp(packet + 0, ff, 6); +} + +static int is_unicast(const uint8_t *packet, unsigned size) { + return (*packet & 0x01) == 0; +} + +static int is_multicast(const uint8_t *packet, unsigned size) { + return (*packet & 0x01) == 0x01 && !is_broadcast(packet, size); +} + +static int is_dhcp_out(const uint8_t *packet, unsigned size) { + static uint8_t cookie[] = { 0x63, 0x82, 0x53, 0x63 }; + return size >= 282 + //&& !memcmp(&packet[0], ff, 6) /* broadcast */ + && packet[12] == 0x08 && packet[13] == 0x00 + && packet[14] == 0x45 /* version 4 */ + && packet[23] == 0x11 /* UDP */ + //&& !memcmp(&packet[26], oo, 4) /* source ip */ + //&& !memcmp(&packet[30], ff, 4) /* dest ip */ + && packet[34] == 0x00 && packet[35] == 0x44 /* source port */ + && packet[36] == 0x00 && packet[37] == 0x43 /* dest port */ + //&& packet[44] == 0x01 /* dhcp boot req */ + && packet[43] == 0x01 /* ethernet */ + && packet[44] == 0x06 /* 6 byte mac */ + && !memcmp(&packet[278], cookie, 4) + ; +} + + +static int is_dhcp_in(const uint8_t *packet, unsigned size) { + static uint8_t cookie[] = { 0x63, 0x82, 0x53, 0x63 }; + return size >= 282 + //&& !memcmp(&packet[0], ff, 6) /* broadcast */ + && packet[12] == 0x08 && packet[13] == 0x00 + && packet[14] == 0x45 /* version 4 */ + && packet[23] == 0x11 /* UDP */ + //&& !memcmp(&packet[26], oo, 4) /* source ip */ + //&& !memcmp(&packet[30], ff, 4) /* dest ip */ + && packet[34] == 0x00 && packet[35] == 0x43 /* source port */ + && packet[36] == 0x00 && packet[37] == 0x44 /* dest port */ + //&& packet[44] == 0x01 /* dhcp boot req */ + && packet[43] == 0x01 /* ethernet */ + && packet[44] == 0x06 /* 6 byte mac */ + && !memcmp(&packet[278], cookie, 4) + ; +} + +static unsigned ip_checksum(const uint8_t *packet) { + unsigned x = 0; + unsigned i; + for (i = 0; i < ip_data; i += 2) { + if (i == ip_header_cksum) continue; + x += packet[eth_data + i + 0 ] << 8; + x += packet[eth_data + i + 1]; + } + + /* add the carry */ + x += x >> 16; + x &= 0xffff; + return ~x & 0xffff; +} + +static void fix_incoming_packet(uint8_t *packet, unsigned size, const uint8_t real_mac[6], const uint8_t fake_mac[6]) { + + if (memcmp(packet + 0, real_mac, 6) == 0) + memcpy(packet + 0, fake_mac, 6); + + /* dhcp request - fix the hardware address */ + if (is_unicast(packet, size) && is_dhcp_in(packet, size)) { + if (!memcmp(packet + 70, real_mac, 6)) + memcpy(packet + 70, fake_mac, 6); + return; + } + +} + +static void fix_outgoing_packet(uint8_t *packet, unsigned size, const uint8_t real_mac[6], const uint8_t fake_mac[6]) { + + + + if (memcmp(packet + 6, fake_mac, 6) == 0) + memcpy(packet + 6, real_mac, 6); + + if (is_arp(packet, size)) { + /* sender mac address */ + if (!memcmp(packet + 22, fake_mac, 6)) + memcpy(packet + 22, real_mac, 6); + return; + } + + /* dhcp request - fix the hardware address */ + if (is_broadcast(packet, size) && is_dhcp_out(packet, size)) { + /* set source ip to 0 - LL bug? */ + /* fixed in link layer as of 2018-12-17 */ +#if 0 + unsigned ck; + memset(packet + 26, 0, 4); + ck = ip_checksum(packet); + packet[eth_data + ip_header_cksum + 0] = ck >> 8; + packet[eth_data + ip_header_cksum + 1] = ck; +#endif + + if (!memcmp(packet + 70, fake_mac, 6)) + memcpy(packet + 70, real_mac, 6); + return; + } + +} + + void rawnet_arch_transmit(int force, int onecoll, int inhibit_crc, int tx_pad_dis, int txlength, uint8_t *txframe) { int count = 1; vmnet_return_t st; struct vmpktdesc v; struct iovec iov; + uint8_t *buffer; if (txlength == 0) return; if (txlength > interface_packet_size) { @@ -151,8 +358,17 @@ void rawnet_arch_transmit(int force, int onecoll, int inhibit_crc, int tx_pad_di return; } + if (txlength < 12) { + log_message(rawnet_arch_log, "packet is too small: %d", txlength); + return; + } - iov.iov_base = txframe; + /* copy the buffer and fix the source mac address. */ + buffer = (uint8_t *)calloc(txlength, 1); + memcpy(buffer, txframe, txlength); + fix_outgoing_packet(buffer, txlength, interface_mac, interface_fake_mac); + + iov.iov_base = buffer; iov.iov_len = txlength; v.vm_pkt_size = txlength; @@ -161,11 +377,12 @@ void rawnet_arch_transmit(int force, int onecoll, int inhibit_crc, int tx_pad_di v.vm_flags = 0; - fprintf(stderr, "\rawnet_arch_transmit: %u\n", (unsigned)iov.iov_len); - rawnet_hexdump(iov.iov_base, iov.iov_len); + fprintf(stderr, "\nrawnet_arch_transmit: %u\n", (unsigned)iov.iov_len); + rawnet_hexdump(buffer, v.vm_pkt_size); st = vmnet_write(interface, &v, &count); + free(buffer); if (st != VMNET_SUCCESS) { log_message(rawnet_arch_log, "vmnet_write failed!"); } @@ -174,7 +391,7 @@ void rawnet_arch_transmit(int force, int onecoll, int inhibit_crc, int tx_pad_di int rawnet_arch_receive(uint8_t *pbuffer, int *plen, int *phashed, int *phash_index, int *prx_ok, int *pcorrect_mac, int *pbroadcast, int *pcrc_error) { - char *buffer; + uint8_t *buffer; //unsigned hashreg; int count = 1; @@ -184,7 +401,7 @@ int rawnet_arch_receive(uint8_t *pbuffer, int *plen, int *phashed, int *phash_in struct iovec iov; - buffer = calloc(interface_packet_size, 1); + buffer = (uint8_t *)calloc(interface_packet_size, 1); iov.iov_base = buffer; iov.iov_len = interface_packet_size; @@ -206,9 +423,13 @@ int rawnet_arch_receive(uint8_t *pbuffer, int *plen, int *phashed, int *phash_in return 0; } + fix_incoming_packet(buffer, v.vm_pkt_size, interface_mac, interface_fake_mac); + // iov.iov_len is not updated with the read count, apparently. - fprintf(stderr, "\nrawnet_arch_receive: %u\n", (unsigned)v.vm_pkt_size); - rawnet_hexdump(iov.iov_base, v.vm_pkt_size); + if (!is_multicast(buffer, v.vm_pkt_size)) { /* multicast crap */ + fprintf(stderr, "\nrawnet_arch_receive: %u\n", (unsigned)v.vm_pkt_size); + rawnet_hexdump(buffer, v.vm_pkt_size); + } xfer = v.vm_pkt_size; if (xfer > *plen) xfer = *plen; @@ -225,9 +446,11 @@ int rawnet_arch_receive(uint8_t *pbuffer, int *plen, int *phashed, int *phash_in *pcrc_error = 0; *prx_ok = 1; + #if 0 *pcorrect_mac = memcmp(buffer, interface_mac, 6) == 0; *pbroadcast = memcmp(buffer, broadcast_mac, 6) == 0; - + #endif + /* vmnet won't send promiscuous packets */ #if 0 hashreg = (~crc32_buf(buffer, 6) >> 26) & 0x3f; From 7a6ea5ef9905e49b9d27bbfc648a71cbd98773ba Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Wed, 19 Dec 2018 21:07:25 -0500 Subject: [PATCH 009/186] remove unused variables. --- src/sim65816.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/sim65816.c b/src/sim65816.c index ed6150b..9e26a7c 100644 --- a/src/sim65816.c +++ b/src/sim65816.c @@ -885,9 +885,7 @@ void banner() { int gsplusmain(int argc, char **argv) { int diff; - int skip_amt; int tmp1; - int i; char *final_arg = 0; @@ -951,7 +949,6 @@ int gsplusmain(int argc, char **argv) { if (g_ethernet == 1) { int i = -1; - int ok = 0; char *ppname = NULL; char *ppdes = NULL; if (rawnet_enumadapter_open()) From 18d988319e2f02858c3bfba8f78f5ba1ff6a9a57 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Wed, 19 Dec 2018 21:07:57 -0500 Subject: [PATCH 010/186] rawnet cmake --- src/CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f2fd86c..1818f56 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -151,8 +151,8 @@ SET_SOURCE_FILES_PROPERTIES( MACOSX_PACKAGE_LOCATION Resources ) -target_link_libraries(GSplus atbridge) -target_link_libraries(GSplus tfe) +# target_link_libraries(GSplus atbridge) +target_link_libraries(GSplus rawnet) if (DRIVER MATCHES "WIN32") target_link_libraries(GSplus comdlg32 Shlwapi IPHlpApi @@ -166,7 +166,7 @@ if (DRIVER MATCHES "SDL") endif() if (APPLE) - target_link_libraries(GSplus "-framework Cocoa -framework vmnet") + target_link_libraries(GSplus "-framework Cocoa") endif() if (TOGGLE_STATUS) From 1790096088af16585681c288e15a43a6cc04487e Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Tue, 25 Dec 2018 20:00:15 -0500 Subject: [PATCH 011/186] clean up arrow key code in the config window. --- src/config.c | 106 ++++++++++++++++++++++++++++----------------------- 1 file changed, 58 insertions(+), 48 deletions(-) diff --git a/src/config.c b/src/config.c index bc10363..73ad9bd 100644 --- a/src/config.c +++ b/src/config.c @@ -3136,6 +3136,23 @@ void cfg_file_handle_key(int key) { } } + +static int config_read_key(void) { + int key = -1; + while(g_config_control_panel & !(halt_sim&HALT_WANTTOQUIT)) { + video_update(); + key = adb_read_c000(); + if(key & 0x80) { + key = key & 0x7f; + (void)adb_access_c010(); + return key; + } + micro_sleep(1.0/60.0); + g_cfg_vbl_count++; + } + return -1; +} + void config_control_panel() { void (*fn_ptr)(); const char *str; @@ -3144,10 +3161,10 @@ void config_control_panel() { int print_eject_help; int line; int type; - int match_found; int menu_line; int menu_inc; int max_line; + int min_line; int key; int i, j; // First, save important text screen state @@ -3188,10 +3205,39 @@ void config_control_panel() { } cfg_home(); line = 1; - max_line = 1; - match_found = 0; print_eject_help = 0; cfg_printf("%s\n\n", menuptr[0].str); + + /* calc max/min items */ + max_line = 0; + min_line = 0; + for (i = 0;;++i) { + const char *cp = menuptr[i].str; + if (!cp) break; + if (!*cp) continue; /* place holder */ + if (!min_line) min_line = i; + max_line = i; + } + + /* menu advancement */ + if (menu_inc > 0) { + if (menu_line > max_line) menu_line = max_line; + for( ; menu_line < max_line; ++menu_line) { + const char *cp = menuptr[menu_line].str; + if (*cp) break; + } + menu_inc = 0; + } + + if (menu_inc < 0) { + if (menu_line < min_line) menu_line = min_line; + for( ; menu_line > min_line; --menu_line) { + const char *cp = menuptr[menu_line].str; + if (*cp) break; + } + menu_inc = 0; + } + while(line < 24) { str = menuptr[line].str; type = menuptr[line].cfgtype; @@ -3203,27 +3249,11 @@ void config_control_panel() { print_eject_help = 1; } cfg_parse_menu(menuptr, line, menu_line, 0); - if(line == menu_line) { - if(type != 0) { - match_found = 1; - } else if(menu_inc) { - menu_line++; - } else { - menu_line--; - } - } - if(line > max_line) { - max_line = line; - } + cfg_printf("%s\n", g_cfg_opt_buf); line++; } - if((menu_line < 1) && !match_found) { - menu_line = 1; - } - if((menu_line >= max_line) && !match_found) { - menu_line = max_line; - } + if(g_rom_version < 0) { cfg_htab_vtab(0, 21); cfg_printf("\bYOU MUST SELECT A VALID ROM FILE\b\n"); @@ -3264,36 +3294,19 @@ void config_control_panel() { g_cfg_triggeriwreset = 1; } #endif - key = -1; - while(g_config_control_panel & !(halt_sim&HALT_WANTTOQUIT)) { - video_update(); - key = adb_read_c000(); - if(key & 0x80) { - key = key & 0x7f; - (void)adb_access_c010(); - break; - } else { - key = -1; - } - micro_sleep(1.0/60.0); - g_cfg_vbl_count++; - if(!match_found) { - break; - } - } + key = config_read_key(); + if (key < 0) break; + if((key >= 0) && (g_cfg_slotdrive < 0)) { // Normal menu system switch(key) { case 0x0a: /* down arrow */ - menu_line++; + if (menu_line < max_line) menu_line++; menu_inc = 1; break; case 0x0b: /* up arrow */ - menu_line--; - menu_inc = 0; - if(menu_line < 1) { - menu_line = 1; - } + if (menu_line > 1) --menu_line; + menu_inc = -1; break; case 0x33: /* pg dn */ menu_line += CFG_PG_SCROLL_AMT; @@ -3301,10 +3314,7 @@ void config_control_panel() { break; case 0x39: /* pg up */ menu_line -= CFG_PG_SCROLL_AMT; - menu_inc = 0; - if(menu_line < 1) { - menu_line = 1; - } + menu_inc = -1; break; case 0x15: /* right arrow */ cfg_parse_menu(menuptr, menu_line,menu_line,1); From 8dcbb38a62a1f07f21e6cb106dc05e19bf1ec5a1 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Tue, 25 Dec 2018 21:52:41 -0500 Subject: [PATCH 012/186] clean up config menus --- src/config.c | 252 ++++++++++++++++++++++++++++----------------------- 1 file changed, 140 insertions(+), 112 deletions(-) diff --git a/src/config.c b/src/config.c index 73ad9bd..f654f43 100644 --- a/src/config.c +++ b/src/config.c @@ -26,6 +26,20 @@ typedef unsigned int mode_t; #endif + +#define KEY_DOWN_ARROW 0x0a +#define KEY_UP_ARROW 0x0b +#define KEY_RIGHT_ARROW 0x15 +#define KEY_LEFT_ARROW 0x08 +#define KEY_TAB 0x09 +#define KEY_RETURN 0x0d +#define KEY_PAGE_DOWN 0x1079 +#define KEY_PAGE_UP 0x1074 +#define KEY_ESC 0x1b +#define KEY_DELETE 0x7f + + + static const char parse_log_prefix_file[] = "Option set [file]:"; @@ -3077,7 +3091,7 @@ void cfg_file_handle_key(int key) { g_cfg_dirlist.invalid = 1; /* re-read directory */ } else { switch(key) { - case 0x1b: + case KEY_ESC: if(g_cfg_slotdrive < 0xfff) { eject_disk_by_num(g_cfg_slotdrive >> 8, g_cfg_slotdrive & 0xff); } @@ -3085,41 +3099,41 @@ void cfg_file_handle_key(int key) { g_cfg_select_partition = -1; g_cfg_dirlist.invalid = 1; break; - case 0x0a: /* down arrow */ + case KEY_DOWN_ARROW: /* down arrow */ if(g_cfg_file_pathfield == 0) { listhdrptr->curent++; cfg_fix_topent(listhdrptr); } break; - case 0x0b: /* up arrow */ + case KEY_UP_ARROW: /* up arrow */ if(g_cfg_file_pathfield == 0) { listhdrptr->curent--; cfg_fix_topent(listhdrptr); } break; - case 0x33: /* pg dn */ + case KEY_PAGE_DOWN: /* pg dn */ if(g_cfg_file_pathfield == 0) { listhdrptr->curent += CFG_PG_SCROLL_AMT; cfg_fix_topent(listhdrptr); } break; - case 0x39: /* pg up */ + case KEY_PAGE_UP: /* pg up */ if(g_cfg_file_pathfield == 0) { listhdrptr->curent -= CFG_PG_SCROLL_AMT; cfg_fix_topent(listhdrptr); } break; - case 0x0d: /* return */ + case KEY_RETURN: /* return */ cfg_file_selected(0); break; - case 0x09: /* tab */ + case KEY_TAB: /* tab */ g_cfg_file_pathfield = !g_cfg_file_pathfield; break; - case 0x15: + case KEY_RIGHT_ARROW: glogf("You can't go right!"); /* eggs - DB */ break; - case 0x08: /* left arrow */ - case 0x7f: /* delete key */ + case KEY_LEFT_ARROW: /* left arrow */ + case KEY_DELETE: /* delete key */ if(g_cfg_file_pathfield) { len = strlen(&g_cfg_file_curpath[0]) - 1; if(len >= 0) { @@ -3127,7 +3141,7 @@ void cfg_file_handle_key(int key) { } } break; - case 0x20: /* space -- selects file/directory */ + case ' ': /* space -- selects file/directory */ cfg_file_selected(g_cfg_file_dir_only); break; default: @@ -3139,12 +3153,17 @@ void cfg_file_handle_key(int key) { static int config_read_key(void) { int key = -1; + int mods; while(g_config_control_panel & !(halt_sim&HALT_WANTTOQUIT)) { video_update(); key = adb_read_c000(); if(key & 0x80) { key = key & 0x7f; + mods = adb_read_c025(); (void)adb_access_c010(); + //printf("key: %02x modifiers: %02x\n", key, mods); + // Fkeys have the keypad bit set (but so do numbers) */ + if ((mods & 0x10) && key > 0x3f) key |= 0x1000; return key; } micro_sleep(1.0/60.0); @@ -3153,12 +3172,29 @@ static int config_read_key(void) { return -1; } +void config_display_file_menu(void) { + + int key; + cfg_file_init(); + while (g_cfg_slotdrive >= 0) { + cfg_file_draw(); + + cfg_htab_vtab(0, 23); + cfg_printf("Move: \tJ\t \tK\t Change: \tH\t \tU\t \tM"); + if (g_cfg_slotdrive < 0xfff) cfg_printf("\t Eject: \bESC\b"); + + key = config_read_key(); + if (key < 0) break; + cfg_file_handle_key(key); + } + +} + void config_control_panel() { void (*fn_ptr)(); const char *str; Cfg_menu *menuptr; void *ptr; - int print_eject_help; int line; int type; int menu_line; @@ -3205,7 +3241,6 @@ void config_control_panel() { } cfg_home(); line = 1; - print_eject_help = 0; cfg_printf("%s\n\n", menuptr[0].str); /* calc max/min items */ @@ -3245,9 +3280,7 @@ void config_control_panel() { if(str == 0) { break; } - if((type & 0xf) == CFGTYPE_DISK) { - print_eject_help = 1; - } + cfg_parse_menu(menuptr, line, menu_line, 0); cfg_printf("%s\n", g_cfg_opt_buf); @@ -3259,14 +3292,10 @@ void config_control_panel() { cfg_printf("\bYOU MUST SELECT A VALID ROM FILE\b\n"); } cfg_htab_vtab(0, 23); - cfg_printf("Move: \tJ\t \tK\t Change: \tH\t \tU\t \tM\t"); - if(print_eject_help) { - cfg_printf(" Eject: "); - if(g_cfg_slotdrive >= 0) { - cfg_printf("\bESC\b"); - } else { - cfg_printf("E"); - } + cfg_printf("Move: \tJ\t \tK\t Change: \tH\t \tU\t \tM"); + type = menuptr[menu_line].cfgtype; + if ((type & 0x0f) == CFGTYPE_DISK) { + cfg_printf("\t Eject: E"); } #if 0 cfg_htab_vtab(0, 22); @@ -3274,9 +3303,7 @@ void config_control_panel() { menu_line, line, g_cfg_vbl_count, g_adb_repeat_vbl, g_key_down); #endif - if(g_cfg_slotdrive >= 0) { - cfg_file_draw(); - } + #ifdef HAVE_RAWNET /*HACK eh, at least I think it is. Display the available ethernet interfaces when in the ethernet control panel. This is the only way one can customize a menu pane. @@ -3286,94 +3313,95 @@ void config_control_panel() { cfg_get_tfe_name(); } #endif -#ifdef HAVE_SDL - /*If user enters the Virtual Imagewriter control panel, flag it so we can - automatically apply changes on exit.*/ - if(menuptr == g_cfg_imagewriter_menu) - { - g_cfg_triggeriwreset = 1; - } -#endif + + + key = config_read_key(); if (key < 0) break; - if((key >= 0) && (g_cfg_slotdrive < 0)) { - // Normal menu system - switch(key) { - case 0x0a: /* down arrow */ - if (menu_line < max_line) menu_line++; - menu_inc = 1; - break; - case 0x0b: /* up arrow */ - if (menu_line > 1) --menu_line; - menu_inc = -1; - break; - case 0x33: /* pg dn */ - menu_line += CFG_PG_SCROLL_AMT; - menu_inc = 1; - break; - case 0x39: /* pg up */ - menu_line -= CFG_PG_SCROLL_AMT; - menu_inc = -1; - break; - case 0x15: /* right arrow */ - cfg_parse_menu(menuptr, menu_line,menu_line,1); - break; - case 0x08: /* left arrow */ - cfg_parse_menu(menuptr,menu_line,menu_line,-1); - break; - case 0x0d: - type = menuptr[menu_line].cfgtype; - ptr = menuptr[menu_line].ptr; - str = menuptr[menu_line].str; - switch(type & 0xf) { - case CFGTYPE_MENU: - menuptr = (Cfg_menu *)ptr; - menu_line = 1; - break; - case CFGTYPE_DISK: - g_cfg_slotdrive = type >> 4; - g_cfg_file_dir_only = 0; - cfg_file_init(); - break; - case CFGTYPE_FUNC: - fn_ptr = (void (*)())ptr; - (*fn_ptr)(); - adb_all_keys_up(); //Needed otherwise menu function will continue to repeat until we move selection up or down - break; - case CFGTYPE_FILE: - g_cfg_slotdrive = 0xfff; - g_cfg_file_def_name = str /* *((char **)ptr) */; // was ptr - g_cfg_file_strptr = (char **)ptr; - g_cfg_file_dir_only = 0; - cfg_file_init(); - break; - case CFGTYPE_DIR: - g_cfg_slotdrive = 0xfff; - g_cfg_file_def_name = str /* *((char **)ptr) */; // was ptr - g_cfg_file_strptr = (char **)ptr; - g_cfg_file_dir_only = 1; - cfg_file_init(); - break; - } - break; - case 0x1b: - // Jump to last menu entry - menu_line = max_line; - break; - case 'e': - case 'E': - type = menuptr[menu_line].cfgtype; - if((type & 0xf) == CFGTYPE_DISK) { - eject_disk_by_num(type >> 12, - (type >> 4) & 0xff); - } - break; - default: - glogf("Unhandled config key: 0x%02x", key); - } - } else if(key >= 0) { - cfg_file_handle_key(key); + // Normal menu system + switch(key) { + case KEY_DOWN_ARROW: /* down arrow */ + if (menu_line < max_line) menu_line++; + menu_inc = 1; + break; + case KEY_UP_ARROW: /* up arrow */ + if (menu_line > 1) --menu_line; + menu_inc = -1; + break; + case KEY_PAGE_DOWN: /* pg dn */ + menu_line += CFG_PG_SCROLL_AMT; + menu_inc = 1; + break; + case KEY_PAGE_UP: /* pg up */ + menu_line -= CFG_PG_SCROLL_AMT; + menu_inc = -1; + break; + case KEY_RIGHT_ARROW: /* right arrow */ + cfg_parse_menu(menuptr, menu_line,menu_line,1); + break; + case KEY_LEFT_ARROW: /* left arrow */ + cfg_parse_menu(menuptr,menu_line,menu_line,-1); + break; + case KEY_RETURN: + type = menuptr[menu_line].cfgtype; + ptr = menuptr[menu_line].ptr; + str = menuptr[menu_line].str; + switch(type & 0xf) { + case CFGTYPE_MENU: + menuptr = (Cfg_menu *)ptr; + menu_line = 1; + +#ifdef HAVE_SDL + /*If user enters the Virtual Imagewriter control panel, flag it so we can + automatically apply changes on exit.*/ + if(menuptr == g_cfg_imagewriter_menu) { + g_cfg_triggeriwreset = 1; + } +#endif + break; + + case CFGTYPE_FUNC: + fn_ptr = (void (*)())ptr; + (*fn_ptr)(); + adb_all_keys_up(); //Needed otherwise menu function will continue to repeat until we move selection up or down + break; + + case CFGTYPE_DISK: + g_cfg_slotdrive = type >> 4; + g_cfg_file_dir_only = 0; + config_display_file_menu(); + break; + case CFGTYPE_FILE: + g_cfg_slotdrive = 0xfff; + g_cfg_file_def_name = str /* *((char **)ptr) */; // was ptr + g_cfg_file_strptr = (char **)ptr; + g_cfg_file_dir_only = 0; + config_display_file_menu(); + break; + case CFGTYPE_DIR: + g_cfg_slotdrive = 0xfff; + g_cfg_file_def_name = str /* *((char **)ptr) */; // was ptr + g_cfg_file_strptr = (char **)ptr; + g_cfg_file_dir_only = 1; + config_display_file_menu(); + break; + } + break; + case KEY_ESC: + // Jump to last menu entry + menu_line = max_line; + break; + case 'e': + case 'E': + type = menuptr[menu_line].cfgtype; + if((type & 0xf) == CFGTYPE_DISK) { + eject_disk_by_num(type >> 12, + (type >> 4) & 0xff); + } + break; + default: + glogf("Unhandled config key: 0x%02x", key); } } for(i = 0; i < 0x400; i++) { From fc100dd6b6e49fde19d89cae8c2cb07d6c42c1e7 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Tue, 25 Dec 2018 21:53:56 -0500 Subject: [PATCH 013/186] sdl key mapping - remove alternate value (eg, page up) for keypad numbers. Hitting real page-up is converted to the 3 key, for example, which is bad. --- src/sdl2_driver.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sdl2_driver.c b/src/sdl2_driver.c index c5697a9..3e45367 100644 --- a/src/sdl2_driver.c +++ b/src/sdl2_driver.c @@ -150,9 +150,9 @@ int a2_key_to_sdlkeycode[][3] = { { 0x75, SDLK_DELETE, 0 }, { 0x77, SDLK_END, 0 }, { 0x79, SDLK_PAGEDOWN, 0 }, - { 0x59, SDLK_KP_7, SDLK_HOME }, - { 0x5b, SDLK_KP_8, SDLK_UP }, - { 0x5c, SDLK_KP_9, SDLK_PAGEUP }, + { 0x59, SDLK_KP_7, 0 }, + { 0x5b, SDLK_KP_8, 0 }, + { 0x5c, SDLK_KP_9, 0 }, { 0x4e, SDLK_KP_MINUS, 0 }, { 0x39, SDLK_CAPSLOCK, 0 }, { 0x00, SDLK_a, 'A' }, @@ -167,9 +167,9 @@ int a2_key_to_sdlkeycode[][3] = { { 0x29, SDLK_SEMICOLON, SDLK_COLON }, { 0x27, SDLK_QUOTE, SDLK_QUOTEDBL }, { 0x24, SDLK_RETURN, 0 }, - { 0x56, SDLK_KP_4, SDLK_LEFT}, + { 0x56, SDLK_KP_4, 0}, { 0x57, SDLK_KP_5, 0 }, - { 0x58, SDLK_KP_6, SDLK_RIGHT }, + { 0x58, SDLK_KP_6, 0 }, { 0x45, SDLK_KP_PLUS, 0 }, { 0x38, SDLK_LSHIFT, SDLK_RSHIFT }, { 0x06, SDLK_z, 'Z' }, @@ -184,8 +184,8 @@ int a2_key_to_sdlkeycode[][3] = { { 0x2c, SDLK_SLASH, SDLK_QUESTION }, { 0x3e, SDLK_UP, 0 }, { 0x53, SDLK_KP_1, 0 }, - { 0x54, SDLK_KP_2, SDLK_DOWN }, - { 0x55, SDLK_KP_3, SDLK_PAGEDOWN }, + { 0x54, SDLK_KP_2, 0 }, + { 0x55, SDLK_KP_3, 0 }, { 0x36, SDLK_RCTRL, SDLK_LCTRL }, #if defined(__APPLE__) { 0x3a, SDLK_LALT, SDLK_RALT }, /* Option */ From ce3bb3d642b651f38044ab0975c7ed2d6a6fc5da Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Wed, 26 Dec 2018 00:11:42 -0500 Subject: [PATCH 014/186] add new menu lookup for ethernet interface. --- src/config.c | 205 ++++++++++++++++++++++++++++++++++++++------------- src/config.h | 3 +- src/defc.h | 1 + 3 files changed, 155 insertions(+), 54 deletions(-) diff --git a/src/config.c b/src/config.c index f654f43..bc52f3c 100644 --- a/src/config.c +++ b/src/config.c @@ -88,8 +88,8 @@ extern int g_joystick_button_0; extern int g_joystick_button_1; extern int g_joystick_button_2; extern int g_joystick_button_3; -extern int g_ethernet; -extern int g_ethernet_enabled; + + extern int g_halt_on_bad_read; extern int g_ignore_bad_acc; extern int g_ignore_halts; @@ -114,7 +114,8 @@ extern int g_joystick_trim_amount_y; extern int g_swap_paddles; extern int g_invert_paddles; extern int g_ethernet; -extern int g_ethernet_interface; +extern int g_ethernet_enabled; +extern char * g_ethernet_interface; extern int g_appletalk_bridging; extern int g_appletalk_turbo; extern int g_appletalk_diagnostics; @@ -213,6 +214,8 @@ int g_cfg_file_dir_only = 0; #define MAX_PARTITION_BLK_SIZE 65536 +void display_rawnet_menu(const char *name, const char **value); + extern Cfg_menu g_cfg_main_menu[]; #define KNMP(a) &a, #a, 0 @@ -247,7 +250,6 @@ Cfg_menu g_cfg_uiless_menu[] = { { "", KNMP(g_joystick_button_1), CFGTYPE_INT }, { "", KNMP(g_joystick_button_2), CFGTYPE_INT }, { "", KNMP(g_joystick_button_3), CFGTYPE_INT }, - { "", KNMP(g_ethernet), CFGTYPE_INT }, { "", KNMP(g_halt_on_bad_read), CFGTYPE_INT }, { "", KNMP(g_ignore_bad_acc), CFGTYPE_INT }, { "", KNMP(g_ignore_halts), CFGTYPE_INT }, @@ -366,8 +368,8 @@ Cfg_menu g_cfg_parallel_menu[] = { Cfg_menu g_cfg_ethernet_menu[] = { { "Ethernet Card Configuration", g_cfg_ethernet_menu, 0, 0, CFGTYPE_MENU }, - { "Use Interface Number,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10", - KNMP(g_ethernet_interface), CFGTYPE_INT }, + { "Interface", + KNMP(g_ethernet_interface), CFGTYPE_STR_FUNC, display_rawnet_menu }, { "", 0, 0, 0, 0 }, { "Uthernet Card in Slot 3,0,Off,1,On", KNMP(g_ethernet), CFGTYPE_INT }, @@ -483,15 +485,14 @@ Cfg_menu g_cfg_main_menu[] = { { "Expansion Mem Size,0,0MB,0x100000,1MB,0x200000,2MB,0x300000,3MB," "0x400000,4MB,0x600000,6MB,0x800000,8MB,0xa00000,10MB,0xc00000,12MB," "0xe00000,14MB", KNMP(g_mem_size_exp), CFGTYPE_INT }, - { "Dump text screen to file", (void *)cfg_text_screen_dump, 0, 0, CFGTYPE_FUNC}, + { "Dump text screen to file", 0, 0, 0, CFGTYPE_FUNC, cfg_text_screen_dump}, #ifdef HAVE_SDL - { "Reset Virtual ImageWriter", (void *)cfg_iwreset, 0, 0, CFGTYPE_FUNC }, + { "Reset Virtual ImageWriter", 0, 0, 0, CFGTYPE_FUNC, cfg_iwreset }, #endif { "", 0, 0, 0, 0 }, - { "Save changes to configuration file", (void *)config_write_config_gsplus_file, 0, 0, - CFGTYPE_FUNC }, + { "Save changes to configuration file", 0, 0, 0, CFGTYPE_FUNC, config_write_config_gsplus_file }, { "", 0, 0, 0, 0 }, - { "Exit Config (or press F4)", (void *)cfg_exit, 0, 0, CFGTYPE_FUNC }, + { "Exit Config (or press F4)", 0, 0, 0, CFGTYPE_FUNC, cfg_exit }, { 0, 0, 0, 0, 0 }, }; @@ -609,6 +610,7 @@ void config_init_menus(Cfg_menu *menuptr) { case CFGTYPE_STR: case CFGTYPE_FILE: case CFGTYPE_DIR: + case CFGTYPE_STR_FUNC: str_ptr = (char **)menuptr->ptr; str = *str_ptr; // We need to malloc this string since all @@ -709,36 +711,7 @@ void cfg_iwreset() { imagewriter_init(g_imagewriter_dpi,g_imagewriter_paper,g_imagewriter_banner, g_imagewriter_output,g_imagewriter_multipage); return; } -#ifdef HAVE_RAWNET -void cfg_get_tfe_name() { - int i = 0; - char *ppname = NULL; - char *ppdes = NULL; - cfg_htab_vtab(0,11); - if (rawnet_enumadapter_open()) - { - cfg_printf("Interface List:\n---------------"); - while(rawnet_enumadapter(&ppname,&ppdes)) - { - cfg_htab_vtab(0, 13+i); - cfg_printf("%2d: %s",i,ppdes); - i++; - free(ppname); - free(ppdes); - } - rawnet_enumadapter_close(); - } - else - { - #if defined(_WIN32) || defined(__CYGWIN__) - cfg_printf("ERROR: Install/Enable WinPcap for Ethernet Support!!"); - #else - cfg_printf("ERROR: Install/Enable LibPcap for Ethernet Support!!"); - #endif - } - return; -} -#endif + void config_vbl_update(int doit_3_persec) { if(doit_3_persec) { @@ -824,6 +797,7 @@ void config_parse_option(char *buf, int pos, int len, int line) { case CFGTYPE_STR: case CFGTYPE_FILE: case CFGTYPE_DIR: + case CFGTYPE_STR_FUNC: strptr = (char **)menuptr->ptr; if(strptr && *strptr) { free(*strptr); @@ -1347,6 +1321,7 @@ void config_write_config_gsplus_file() { case CFGTYPE_STR: case CFGTYPE_FILE: case CFGTYPE_DIR: + case CFGTYPE_STR_FUNC: curstr = *((char **)menuptr->ptr); defstr = *((char **)menuptr->defptr); if(strcmp(curstr, defstr) != 0) { @@ -2073,6 +2048,11 @@ void cfg_putchar(int c) { g_cfg_curs_x = x; } +void cfg_puts(const char *str, int nl) { + for(;*str; ++str) cfg_putchar(*str); + if (nl) cfg_putchar('\n'); +} + void cfg_printf(const char *fmt, ...) { va_list ap; int c; @@ -2274,6 +2254,7 @@ void cfg_parse_menu(Cfg_menu *menuptr, int menu_pos, int highlight_pos, int chan case CFGTYPE_STR: case CFGTYPE_FILE: case CFGTYPE_DIR: + case CFGTYPE_STR_FUNC: str_ptr = (char **)menuptr->ptr; curstr = *str_ptr; str_ptr = (char **)menuptr->defptr; @@ -2306,6 +2287,7 @@ void cfg_parse_menu(Cfg_menu *menuptr, int menu_pos, int highlight_pos, int chan case CFGTYPE_INT: case CFGTYPE_FILE: case CFGTYPE_DIR: + case CFGTYPE_STR_FUNC: g_cfg_opt_buf[bufpos++] = ' '; g_cfg_opt_buf[bufpos++] = '='; g_cfg_opt_buf[bufpos++] = ' '; @@ -2385,6 +2367,7 @@ void cfg_parse_menu(Cfg_menu *menuptr, int menu_pos, int highlight_pos, int chan snprintf(str, CFG_OPT_MAXSTR, "%d", curval); break; case CFGTYPE_STR: + case CFGTYPE_STR_FUNC: str = &(g_cfg_opts_strs[0][0]); //printf("curstr is: %s str is: %s\n", curstr,str); snprintf(str, CFG_OPT_MAXSTR, "%s", curstr); @@ -3190,11 +3173,11 @@ void config_display_file_menu(void) { } -void config_control_panel() { - void (*fn_ptr)(); +void config_control_panel() { const char *str; Cfg_menu *menuptr; void *ptr; + void *cookie; int line; int type; int menu_line; @@ -3305,13 +3288,7 @@ void config_control_panel() { #endif #ifdef HAVE_RAWNET - /*HACK eh, at least I think it is. Display the available ethernet interfaces - when in the ethernet control panel. This is the only way one can customize a menu pane. - Kent did it with the directory browser, so why not.*/ - if(menuptr == g_cfg_ethernet_menu) - { - cfg_get_tfe_name(); - } + #endif @@ -3347,6 +3324,7 @@ void config_control_panel() { type = menuptr[menu_line].cfgtype; ptr = menuptr[menu_line].ptr; str = menuptr[menu_line].str; + cookie = menuptr[menu_line].cookie; switch(type & 0xf) { case CFGTYPE_MENU: menuptr = (Cfg_menu *)ptr; @@ -3361,11 +3339,13 @@ void config_control_panel() { #endif break; - case CFGTYPE_FUNC: - fn_ptr = (void (*)())ptr; - (*fn_ptr)(); + case CFGTYPE_FUNC: { + void (*fn)(void); + fn = (void (*)(void))cookie; + if (fn) fn(); adb_all_keys_up(); //Needed otherwise menu function will continue to repeat until we move selection up or down break; + } case CFGTYPE_DISK: g_cfg_slotdrive = type >> 4; @@ -3386,6 +3366,15 @@ void config_control_panel() { g_cfg_file_dir_only = 1; config_display_file_menu(); break; + + case CFGTYPE_STR_FUNC: { + void (*fn)(const char *, char **); + fn = (void (*)(const char *, char **))cookie; + if (fn) fn(str, (char **)ptr); + adb_all_keys_up(); + break; + } + } break; case KEY_ESC: @@ -3429,3 +3418,113 @@ void x_clk_setup_bram_version() { g_bram_ptr = (&g_bram[1][0]); // ROM 03 } } + + +#ifdef HAVE_RAWNET + +void display_rawnet_menu(const char *name, const char **value) { + + char *entries[20]; + int i; + int index = -1; + int count = 0; + char *ppname = NULL; + char *ppdes = NULL; + + memset(entries, 0, sizeof(entries)); + + if (rawnet_enumadapter_open()) { + count = 0; + while(rawnet_enumadapter(&ppname,&ppdes)) { + entries[count] = ppname; + free(ppdes); + + if (index < 0 && !strcmp(*value, ppname)) index = count; + ++count; + if (count == 20) break; + } + rawnet_enumadapter_close(); + } + + if (index < 0) index = 0; + + for(;;) { + int key; + + cfg_home(); + cfg_puts(name, 1); + for (i = 0; i < 20; ++i) { + char *cp = entries[i]; + if (!cp) break; + + cfg_htab_vtab(4, i + 2); + if (i == index) cfg_putchar('\b'); /* inverse */ + cfg_puts(cp, 1); + if (i == index) cfg_putchar('\b'); + } + + cfg_htab_vtab(0, 23); + cfg_puts("Move: \tJ\t \tK\t Change: \tM",1); + + key = config_read_key(); + switch(key) { + case KEY_UP_ARROW: + if (index) --index; + break; + case KEY_DOWN_ARROW: + if (index < count - 1) ++index; + break; + case KEY_PAGE_UP: + index -= CFG_PG_SCROLL_AMT; + if (index < 0) index = 0; + break; + case KEY_PAGE_DOWN: + index += CFG_PG_SCROLL_AMT; + if (index >= count) index = count - 1; + break; + case KEY_RETURN: + if (index < count) { + *value = strdup(entries[index]); + } + key = -1; + break; + case KEY_ESC: + key = -1; + break; + } + if (key < 0) break; + } + + for (i = 0; i < 20; ++i) free(entries[i]); +} + + +void cfg_get_tfe_name() { + int i = 0; + char *ppname = NULL; + char *ppdes = NULL; + cfg_htab_vtab(0,11); + if (rawnet_enumadapter_open()) + { + cfg_printf("Interface List:\n---------------"); + while(rawnet_enumadapter(&ppname,&ppdes)) + { + cfg_htab_vtab(0, 13+i); + cfg_printf("%2d: %s",i,ppdes); + i++; + free(ppname); + free(ppdes); + } + rawnet_enumadapter_close(); + } + else + { + #if defined(_WIN32) || defined(__CYGWIN__) + cfg_printf("ERROR: Install/Enable WinPcap for Ethernet Support!!"); + #else + cfg_printf("ERROR: Install/Enable LibPcap for Ethernet Support!!"); + #endif + } + return; +} +#endif diff --git a/src/config.h b/src/config.h index 779fff7..f03c5ef 100644 --- a/src/config.h +++ b/src/config.h @@ -12,12 +12,13 @@ #define CFG_NUM_SHOWENTS 16 #define CFGTYPE_MENU 1 -#define CFGTYPE_INT 2 +#define CFGTYPE_INT 2 #define CFGTYPE_DISK 3 #define CFGTYPE_FUNC 4 #define CFGTYPE_FILE 5 #define CFGTYPE_STR 6 #define CFGTYPE_DIR 7 +#define CFGTYPE_STR_FUNC 8 /* CFGTYPE limited to just 4 bits: 0-15 */ /* Cfg_menu, Cfg_dirent and Cfg_listhdr are defined in defc.h */ diff --git a/src/defc.h b/src/defc.h index d6317d7..bb0416f 100644 --- a/src/defc.h +++ b/src/defc.h @@ -191,6 +191,7 @@ STRUCT(Cfg_menu) { const char *name_str; void *defptr; int cfgtype; + void *cookie; }; STRUCT(Cfg_dirent) { From 44ee12488249a85006efbba0d2bf31f56e858232 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Wed, 26 Dec 2018 20:01:22 -0500 Subject: [PATCH 015/186] can use numbers to navigate file picker. --- src/config.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/config.c b/src/config.c index bc52f3c..cf406b7 100644 --- a/src/config.c +++ b/src/config.c @@ -7,6 +7,7 @@ #include "defc.h" #include +#include #include "config.h" #include "glog.h" #include "imagewriter.h" @@ -3065,9 +3066,7 @@ void cfg_file_handle_key(int key) { listhdrptr = &g_cfg_partitionlist; } - // can't hotkey numbers because it falsely matches PGUP/PGDN - if( (g_cfg_file_pathfield == 0) && - ((key >= 'a' && key <= 'z') || (key >= 'A' && key <= 'Z')) ) { + if( (g_cfg_file_pathfield == 0) && isalnum(key)) { /* jump to file starting with this letter */ g_cfg_file_match[0] = key; g_cfg_file_match[1] = 0; From c34b0a4c02ada730413d03199960ae33255c4071 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Wed, 26 Dec 2018 22:51:04 -0500 Subject: [PATCH 016/186] vmnet - allocate one buffer and use it. --- src/rawnet/rawnetarch_darwin.c | 44 +++++++++++++++------------------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/src/rawnet/rawnetarch_darwin.c b/src/rawnet/rawnetarch_darwin.c index dc362ab..63b4d10 100644 --- a/src/rawnet/rawnetarch_darwin.c +++ b/src/rawnet/rawnetarch_darwin.c @@ -23,6 +23,7 @@ static uint8_t interface_mac[6]; static uint8_t interface_fake_mac[6]; static uint64_t interface_mtu; static uint64_t interface_packet_size; +static uint8_t *interface_buffer = NULL; static vmnet_return_t interface_status; int rawnet_arch_init(void) { @@ -85,8 +86,9 @@ int rawnet_arch_activate(const char *interface_name) { }); dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); - - if (interface_status != VMNET_SUCCESS) { + if (interface_status == VMNET_SUCCESS) { + interface_buffer = (uint8_t *)malloc(interface_packet_size); + } else { log_message(rawnet_arch_log, "vmnet_start_interface failed"); if (interface) { vmnet_stop_interface(interface, q, ^(vmnet_return_t status){ @@ -122,6 +124,8 @@ void rawnet_arch_deactivate(void) { interface = NULL; interface_status = 0; } + free(interface_buffer); + interface_buffer = NULL; } @@ -350,7 +354,6 @@ void rawnet_arch_transmit(int force, int onecoll, int inhibit_crc, int tx_pad_di vmnet_return_t st; struct vmpktdesc v; struct iovec iov; - uint8_t *buffer; if (txlength == 0) return; if (txlength > interface_packet_size) { @@ -364,11 +367,10 @@ void rawnet_arch_transmit(int force, int onecoll, int inhibit_crc, int tx_pad_di } /* copy the buffer and fix the source mac address. */ - buffer = (uint8_t *)calloc(txlength, 1); - memcpy(buffer, txframe, txlength); - fix_outgoing_packet(buffer, txlength, interface_mac, interface_fake_mac); + memcpy(interface_buffer, txframe, txlength); + fix_outgoing_packet(interface_buffer, txlength, interface_mac, interface_fake_mac); - iov.iov_base = buffer; + iov.iov_base = interface_buffer; iov.iov_len = txlength; v.vm_pkt_size = txlength; @@ -378,11 +380,11 @@ void rawnet_arch_transmit(int force, int onecoll, int inhibit_crc, int tx_pad_di fprintf(stderr, "\nrawnet_arch_transmit: %u\n", (unsigned)iov.iov_len); - rawnet_hexdump(buffer, v.vm_pkt_size); + rawnet_hexdump(interface_buffer, v.vm_pkt_size); st = vmnet_write(interface, &v, &count); - free(buffer); + if (st != VMNET_SUCCESS) { log_message(rawnet_arch_log, "vmnet_write failed!"); } @@ -391,8 +393,6 @@ void rawnet_arch_transmit(int force, int onecoll, int inhibit_crc, int tx_pad_di int rawnet_arch_receive(uint8_t *pbuffer, int *plen, int *phashed, int *phash_index, int *prx_ok, int *pcorrect_mac, int *pbroadcast, int *pcrc_error) { - uint8_t *buffer; - //unsigned hashreg; int count = 1; int xfer; @@ -400,10 +400,7 @@ int rawnet_arch_receive(uint8_t *pbuffer, int *plen, int *phashed, int *phash_in struct vmpktdesc v; struct iovec iov; - - buffer = (uint8_t *)calloc(interface_packet_size, 1); - - iov.iov_base = buffer; + iov.iov_base = interface_buffer; iov.iov_len = interface_packet_size; v.vm_pkt_size = interface_packet_size; @@ -414,26 +411,24 @@ int rawnet_arch_receive(uint8_t *pbuffer, int *plen, int *phashed, int *phash_in st = vmnet_read(interface, &v, &count); if (st != VMNET_SUCCESS) { log_message(rawnet_arch_log, "vmnet_write failed!"); - free(buffer); return 0; } if (count < 1) { - free(buffer); return 0; } - fix_incoming_packet(buffer, v.vm_pkt_size, interface_mac, interface_fake_mac); + fix_incoming_packet(interface_buffer, v.vm_pkt_size, interface_mac, interface_fake_mac); // iov.iov_len is not updated with the read count, apparently. - if (!is_multicast(buffer, v.vm_pkt_size)) { /* multicast crap */ + if (!is_multicast(interface_buffer, v.vm_pkt_size)) { /* multicast crap */ fprintf(stderr, "\nrawnet_arch_receive: %u\n", (unsigned)v.vm_pkt_size); - rawnet_hexdump(buffer, v.vm_pkt_size); + rawnet_hexdump(interface_buffer, v.vm_pkt_size); } xfer = v.vm_pkt_size; if (xfer > *plen) xfer = *plen; - memcpy(pbuffer, buffer, xfer); + memcpy(pbuffer, interface_buffer, xfer); xfer = v.vm_pkt_size; if (xfer & 0x01) ++xfer; /* ??? */ @@ -447,13 +442,13 @@ int rawnet_arch_receive(uint8_t *pbuffer, int *plen, int *phashed, int *phash_in *prx_ok = 1; #if 0 - *pcorrect_mac = memcmp(buffer, interface_mac, 6) == 0; - *pbroadcast = memcmp(buffer, broadcast_mac, 6) == 0; + *pcorrect_mac = memcmp(interface_buffer, interface_mac, 6) == 0; + *pbroadcast = memcmp(interface_buffer, broadcast_mac, 6) == 0; #endif /* vmnet won't send promiscuous packets */ #if 0 - hashreg = (~crc32_buf(buffer, 6) >> 26) & 0x3f; + hashreg = (~crc32_buf(interface_buffer, 6) >> 26) & 0x3f; if (hash_mask[hashreg >= 32] & (1 << (hashreg & 0x1F))) { *phashed = 1; *phash_index = hashreg; @@ -463,7 +458,6 @@ int rawnet_arch_receive(uint8_t *pbuffer, int *plen, int *phashed, int *phash_in } #endif - free(buffer); return 1; } From 0b03aad15d11f3280368038d01025339bfee6be8 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Sat, 29 Dec 2018 15:04:14 -0500 Subject: [PATCH 017/186] tap/tun interface (alpha!) --- src/rawnet/rawnetarch_tap.c | 309 ++++++++++++++++++++++++++++++++++++ 1 file changed, 309 insertions(+) create mode 100644 src/rawnet/rawnetarch_tap.c diff --git a/src/rawnet/rawnetarch_tap.c b/src/rawnet/rawnetarch_tap.c new file mode 100644 index 0000000..8e59d78 --- /dev/null +++ b/src/rawnet/rawnetarch_tap.c @@ -0,0 +1,309 @@ +/* tun/tap support */ +/* for Linux, *BSD */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#if defined(__linux__) +#include +#include +#endif + +#include "rawnetarch.h" +#include "rawnetsupp.h" + +#if defined(__linux__) +const char *default_interface_name = "/dev/net/tun"; +#endif + +#if defined(__FreeBSD__) +const char *default_interface_name = "/dev/tap"; +#endif + +static int interface_fd = -1; + + +int rawnet_arch_init(void) { + interface_fd = -1; + return 1; +} +void rawnet_arch_pre_reset(void) { + /* NOP */ +} + +void rawnet_arch_post_reset(void) { + /* NOP */ +} + + +/* interface name may be a full path (/dev/tap0) or relative (tap0) */ +int rawnet_arch_activate(const char *interface_name) { + + struct ifreq ifr; + + int ok; + char *tmp = NULL; + int one = 1; + int fd; + + if (!interface_name || !*interface_name) { + interface_name = default_interface_name; + } + if (interface_name[0] != '/') { + ok = asprintf(&tmp, "/dev/%s", interface_name); + if (ok < 0) { + perror("rawnet_arch_activate: asprintf"); + return 0; + } + interface_name = tmp; + } + + fd = open(interface_name, O_RDWR); + if (fd < 0) { + fprintf(stderr, "rawnet_arch_activate: open(%s): %s\n", interface_name, strerror(errno)); + free(tmp); + return 0; + } + free(tmp); + + ok = ioctl(fd, FIONBIO, &one); + if (ok < 0) { + perror("ioctl(FIONBIO"); + close(fd); + return 0; + } + + #if defined(__linux__) + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_flags = IFF_TAP | IFF_NO_PI; + ok = ioctl(fd, TUNSETIFF, (void *) &ifr); + if (ok < 0) { + perror("ioctl(TUNSETIFF)"); + close(fd); + return 0; + } + #endif + + interface_fd = fd; + return 1; +} + +void rawnet_arch_deactivate(void) { + if (interface_fd >= 0) + close(interface_fd); + interface_fd = -1; +} + + +void rawnet_arch_set_mac(const uint8_t mac[6]) { + +#ifdef RAWNET_DEBUG_ARCH + log_message( rawnet_arch_log, "New MAC address set: %02X:%02X:%02X:%02X:%02X:%02X.", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5] ); +#endif + if (interface_fd < 0) return; + + + #if defined(__linux__) + struct ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + memcpy(ifr.ifr_hwaddr.sa_data, mac, 6); + if (ioctl(interface_fd, SIOCSIFHWADDR, (void *)&ifr) < 0) + perror("ioctl(SIOCSIFHWADDR)"); + #endif + + #if defined(__FreeBSD__) + if (ioctl(interface_fd, SIOCSIFADDR, mac) < 0) + perror("ioctl(SIOCSIFADDR)"); + #endif + + +} +void rawnet_arch_set_hashfilter(const uint32_t hash_mask[2]) { + /* NOP */ +} + +void rawnet_arch_recv_ctl(int bBroadcast, int bIA, int bMulticast, int bCorrect, int bPromiscuous, int bIAHash) { + /* NOP */ +} + +void rawnet_arch_line_ctl(int bEnableTransmitter, int bEnableReceiver) { + /* NOP */ +} + +void rawnet_arch_transmit(int force, int onecoll, int inhibit_crc, int tx_pad_dis, int txlength, uint8_t *txframe) { + + ssize_t ok = write(interface_fd, txframe, txlength); +} + + +int rawnet_arch_receive(uint8_t *pbuffer, int *plen, int *phashed, int *phash_index, int *prx_ok, int *pcorrect_mac, int *pbroadcast, int *pcrc_error) { + + int count = *plen; + ssize_t ok = read(interface_fd, pbuffer, count); + if (ok < 0) return 0; + if (ok & 0x01) ++ok; /* ??? */ + *plen = ok; + *prx_ok = 1; + *phashed = *pcorrect_mac = *pbroadcast = *pcrc_error = 0; + + return 1; +} + + +static unsigned adapter_index = 0; +static unsigned adapter_count = 0; +static char **devices = NULL; + +static int cmp(const void *a, const void *b) { + return strcasecmp(*(const char **)a, *(const char **)b); +} + +int rawnet_arch_enumadapter_open(void) { + adapter_index = 0; + adapter_count = 0; + devices = NULL; + + char buffer[MAXPATHLEN]; + + unsigned capacity = 0; + unsigned count = 0; + DIR *dp = NULL; + + int i; + + + capacity = 20; + devices = (char **)malloc(sizeof(char *) * capacity); + if (!devices) return 0; + + dp = opendir("/sys/class/net/"); + if (!dp) goto fail; + + for(;;) { + char *cp; + FILE *fp; + struct dirent *d = readdir(dp); + if (!d) break; + + if (d->d_name[0] == '.') continue; + + sprintf(buffer, "/sys/class/net/%s/tun_flags", d->d_name); + fp = fopen(buffer, "r"); + if (!fp) continue; + cp = fgets(buffer, sizeof(buffer), fp); + fclose(fp); + if (cp) { + /* expect 0x1002 */ + int flags = strtol(cp, (char **)NULL, 16); + if (flags == IFF_TAP | IFF_NO_PI) { + + devices[count++] = strdup(d->d_name); + if (count == capacity) { + char **tmp; + capacity *= 2; + tmp = (char **)realloc(devices, sizeof(char *) * capacity); + if (tmp) devices = tmp; + else break; /* no mem? */ + } + + } + } + + } + closedir(dp); + + /* sort them ... */ + qsort(devices, count, sizeof(char *), cmp); + + adapter_count = count; + return 1; + +fail: + if (dp) closedir(dp); + if (devices) for(i = 0; i = adapter_count) return 0; + + if (ppdescription) *ppdescription = NULL; + if (ppname) *ppname = strdup(devices[adapter_index]); + ++adapter_index; + return 1; +} + + +char *rawnet_arch_get_standard_interface(void) { + return lib_stralloc(default_interface_name); +} + +int rawnet_arch_get_mtu(void) { + if (interface_fd < 0) return -1; + + #if defined(__linux__) + + struct ifreq ifr; + if (ioctl(interface_fd, TUNGETIFF, (void *) &ifr) < 0) return -1; /* ? */ + return ifr.ifr_mtu; + #endif + + #if defined(__FreeBSD__) + struct tapinfo ti; + if (ioctl(interface_fd, TAPSIFINFO, &ti) < 0) return -1; + return ti.mtu; + #endif + + return -1; +} + +int rawnet_arch_get_mac(uint8_t mac[6]) { + + + if (interface_fd < 0) return -1; + + #if defined(__linux__) + struct ifreq ifr; + if (ioctl(interface_fd, SIOCGIFHWADDR, (void *)&ifr) < 0) return -1; + memcpy(mac, ifr.ifr_hwaddr.sa_data, 6); + return 0; + #endif + + #if defined(__FreeBSD__) + if (ioctl(interface_fd, SIOCSIFADDR, mac) < 0) return -1; + return 0; + #endif + + return -1; + +} + From 324c9a191f8bcc0874c35749b763c472a5ee65d3 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Sun, 30 Dec 2018 15:49:29 -0500 Subject: [PATCH 018/186] win32 - support for npcap which is a successor to winpcap. winpcap is still supported. --- src/rawnet/rawnetarch_win32.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/rawnet/rawnetarch_win32.c b/src/rawnet/rawnetarch_win32.c index 3a2655c..81d5b4b 100644 --- a/src/rawnet/rawnetarch_win32.c +++ b/src/rawnet/rawnetarch_win32.c @@ -138,7 +138,21 @@ static void EthernetPcapFreeLibrary(void) static BOOL EthernetPcapLoadLibrary(void) { + /* + * npcap is c:\System32\Npcap\wpcap.dll + * winpcap is c:\System32\wpcap.dll + * + */ if (!pcap_library) { + /* This inserts c:\System32\Npcap\ into the search path. */ + char buffer[512]; + unsigned length; + length = GetSystemDirectory(buffer, sizeof(buffer) - sizeof("\\Npcap")); + if (length) { + strcat(buffer + length, "\\Npcap"); + SetDllDirectory(buffer); + } + pcap_library = LoadLibrary(TEXT("wpcap.dll")); if (!pcap_library) { From c4796f77ecd133e1c38e7813c603c32a3603a445 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Sun, 30 Dec 2018 15:50:41 -0500 Subject: [PATCH 019/186] rawnet - add rawnet_status() call to check if interface is open. --- src/rawnet/rawnet.c | 7 +++++++ src/rawnet/rawnet.h | 2 ++ src/rawnet/rawnetarch.h | 2 ++ src/rawnet/rawnetarch_darwin.c | 5 +++++ src/rawnet/rawnetarch_tap.c | 4 ++++ src/rawnet/rawnetarch_unix.c | 5 +++++ src/rawnet/rawnetarch_win32.c | 5 +++++ 7 files changed, 30 insertions(+) diff --git a/src/rawnet/rawnet.c b/src/rawnet/rawnet.c index 24c7b87..7ccedfb 100644 --- a/src/rawnet/rawnet.c +++ b/src/rawnet/rawnet.c @@ -76,4 +76,11 @@ char *rawnet_get_standard_interface(void) { return rawnet_arch_get_standard_interface(); } + +extern int rawnet_status(void) +{ + return rawnet_arch_status(); +} + + #endif /* #ifdef HAVE_RAWNET */ diff --git a/src/rawnet/rawnet.h b/src/rawnet/rawnet.h index 33a0c92..bc61944 100644 --- a/src/rawnet/rawnet.h +++ b/src/rawnet/rawnet.h @@ -72,4 +72,6 @@ extern int rawnet_enumadapter(char **ppname, char **ppdescription); extern int rawnet_enumadapter_close(void); extern char *rawnet_get_standard_interface(void); +extern int rawnet_status(void); + #endif diff --git a/src/rawnet/rawnetarch.h b/src/rawnet/rawnetarch.h index f5749f3..4b76204 100644 --- a/src/rawnet/rawnetarch.h +++ b/src/rawnet/rawnetarch.h @@ -69,4 +69,6 @@ extern char *rawnet_arch_get_standard_interface(void); extern int rawnet_arch_get_mtu(void); extern int rawnet_arch_get_mac(uint8_t mac[6]); +extern int rawnet_arch_status(void); + #endif diff --git a/src/rawnet/rawnetarch_darwin.c b/src/rawnet/rawnetarch_darwin.c index 63b4d10..582f832 100644 --- a/src/rawnet/rawnetarch_darwin.c +++ b/src/rawnet/rawnetarch_darwin.c @@ -498,3 +498,8 @@ int rawnet_arch_get_mac(uint8_t mac[6]) { return -1; } + +int rawnet_arch_status(void) { + return interface ? 1 : 0; +} + diff --git a/src/rawnet/rawnetarch_tap.c b/src/rawnet/rawnetarch_tap.c index 8e59d78..7b886cb 100644 --- a/src/rawnet/rawnetarch_tap.c +++ b/src/rawnet/rawnetarch_tap.c @@ -307,3 +307,7 @@ int rawnet_arch_get_mac(uint8_t mac[6]) { } +int rawnet_arch_status(void) { + return interface_fd >= 0 ? 1 : 0; +} + diff --git a/src/rawnet/rawnetarch_unix.c b/src/rawnet/rawnetarch_unix.c index 4b737d9..a31c6d9 100644 --- a/src/rawnet/rawnetarch_unix.c +++ b/src/rawnet/rawnetarch_unix.c @@ -543,4 +543,9 @@ extern int rawnet_arch_get_mac(uint8_t mac[6]) { return -1; } +int rawnet_arch_status(void) { + return rawnet_pcap_fp ? 1 : 0; +} + + #endif /* #ifdef HAVE_RAWNET */ diff --git a/src/rawnet/rawnetarch_win32.c b/src/rawnet/rawnetarch_win32.c index 81d5b4b..42426c0 100644 --- a/src/rawnet/rawnetarch_win32.c +++ b/src/rawnet/rawnetarch_win32.c @@ -562,4 +562,9 @@ extern int rawnet_arch_get_mac(uint8_t mac[6]) { return -1; } +int rawnet_arch_status(void) { + return EthernetPcapFP ? 1 : 0; +} + + #endif /* #ifdef HAVE_RAWNET */ From 5884e751bce0d63da531f5adb36216377d33a83c Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Sun, 30 Dec 2018 15:51:53 -0500 Subject: [PATCH 020/186] rawnet - close pcap if not an ethernet device. --- src/rawnet/rawnetarch_unix.c | 2 ++ src/rawnet/rawnetarch_win32.c | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/src/rawnet/rawnetarch_unix.c b/src/rawnet/rawnetarch_unix.c index a31c6d9..fb1541c 100644 --- a/src/rawnet/rawnetarch_unix.c +++ b/src/rawnet/rawnetarch_unix.c @@ -228,6 +228,8 @@ static int rawnet_pcap_open_adapter(const char *interface_name) /* Check the link layer. We support only Ethernet for simplicity. */ if (pcap_datalink(rawnet_pcap_fp) != DLT_EN10MB) { log_message(rawnet_arch_log, "ERROR: TFE works only on Ethernet networks."); + pcap_close(rawnet_pcap_fp); + rawnet_pcap_fp = NULL; return 0; } diff --git a/src/rawnet/rawnetarch_win32.c b/src/rawnet/rawnetarch_win32.c index 42426c0..741dbce 100644 --- a/src/rawnet/rawnetarch_win32.c +++ b/src/rawnet/rawnetarch_win32.c @@ -45,6 +45,7 @@ #include "rawnetsupp.h" typedef pcap_t *(*pcap_open_live_t)(const char *, int, int, int, char *); +typedef void *(*pcap_close_t)(pcap_if_t *); typedef int (*pcap_dispatch_t)(pcap_t *, int, pcap_handler, u_char *); typedef int (*pcap_setnonblock_t)(pcap_t *, int, char *); typedef int (*pcap_datalink_t)(pcap_t *); @@ -61,6 +62,7 @@ typedef char *(*pcap_lookupdev_t)(char *); #define RAWNET_DEBUG_WARN 1 /* this should not be deactivated */ static pcap_open_live_t p_pcap_open_live; +static pcap_close_t p_pcap_close; static pcap_dispatch_t p_pcap_dispatch; static pcap_setnonblock_t p_pcap_setnonblock; static pcap_findalldevs_t p_pcap_findalldevs; @@ -117,6 +119,7 @@ static void EthernetPcapFreeLibrary(void) pcap_library = NULL; p_pcap_open_live = NULL; + p_pcap_close = NULL; p_pcap_dispatch = NULL; p_pcap_setnonblock = NULL; p_pcap_findalldevs = NULL; @@ -161,6 +164,7 @@ static BOOL EthernetPcapLoadLibrary(void) } GET_PROC_ADDRESS_AND_TEST(pcap_open_live); + GET_PROC_ADDRESS_AND_TEST(pcap_close); GET_PROC_ADDRESS_AND_TEST(pcap_dispatch); GET_PROC_ADDRESS_AND_TEST(pcap_setnonblock); GET_PROC_ADDRESS_AND_TEST(pcap_findalldevs); @@ -288,6 +292,8 @@ static BOOL EthernetPcapOpenAdapter(const char *interface_name) if ((*p_pcap_datalink)(EthernetPcapFP) != DLT_EN10MB) { log_message(rawnet_arch_log, "ERROR: Ethernet works only on Ethernet networks."); rawnet_enumadapter_close(); + *(p_pcap_close(EthernetPcapFP)); + EthernetPcapFP = NULL; return FALSE; } From 41b01509adc1f9110f61af58555f32ee6697cde6 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Sun, 30 Dec 2018 15:53:54 -0500 Subject: [PATCH 021/186] rawnet - replace ugly transmit/receive functions with read/write. --- src/rawnet/CMakeLists.txt | 6 +- src/rawnet/rawnetarch.c | 144 +++++++++++++++++++++++++++++++++ src/rawnet/rawnetarch.h | 3 + src/rawnet/rawnetarch_darwin.c | 127 +++++++++++------------------ src/rawnet/rawnetarch_tap.c | 19 +---- src/rawnet/rawnetarch_unix.c | 128 +++++------------------------ src/rawnet/rawnetarch_win32.c | 41 ++++++---- 7 files changed, 247 insertions(+), 221 deletions(-) create mode 100644 src/rawnet/rawnetarch.c diff --git a/src/rawnet/CMakeLists.txt b/src/rawnet/CMakeLists.txt index c43e371..1ff2337 100644 --- a/src/rawnet/CMakeLists.txt +++ b/src/rawnet/CMakeLists.txt @@ -3,17 +3,19 @@ if(WIN32) set(rawnetarch rawnetarch_win32.c) elseif(APPLE) set(rawnetarch rawnetarch_darwin.c) + #set(rawnetarch rawnetarch_unix.c) elseif(UNIX) set(rawnetarch rawnetarch_unix.c) endif() -add_library(rawnet cs8900.c rawnet.c rawnetsupp.c ${rawnetarch}) +add_library(rawnet cs8900.c rawnet.c rawnetsupp.c rawnetarch.c ${rawnetarch}) target_compile_definitions(rawnet PUBLIC HAVE_RAWNET) -target_compile_definitions(rawnet PRIVATE CS8900_DEBUG CS8900_DEBUG_STORE CS8900_DEBUG_LOAD) +target_compile_definitions(rawnet PRIVATE CS8900_DEBUG RAWNET_DEBUG_FRAMES) if(WIN32) elseif(APPLE) + #target_link_libraries(rawnet PRIVATE pcap) target_link_libraries(rawnet PRIVATE "-framework vmnet") elseif(UNIX) target_link_libraries(rawnet PRIVATE pcap) diff --git a/src/rawnet/rawnetarch.c b/src/rawnet/rawnetarch.c new file mode 100644 index 0000000..560b918 --- /dev/null +++ b/src/rawnet/rawnetarch.c @@ -0,0 +1,144 @@ +/** \file rawnetarch.c + * \brief raw ethernet interface, architecture-dependant stuff + * + * \author Bas Wassink + +#ifdef HAVE_RAWNET + +/* backward compatibility junk */ + + +/** \brief Transmit a frame + * + * \param[in] force Delete waiting frames in transmit buffer + * \param[in] onecoll Terminate after just one collision + * \param[in] inhibit_crc Do not append CRC to the transmission + * \param[in] tx_pad_dis Disable padding to 60 Bytes + * \param[in] txlength Frame length + * \param[in] txframe Pointer to the frame to be transmitted + */ +void rawnet_arch_transmit(int force, int onecoll, int inhibit_crc, + int tx_pad_dis, int txlength, uint8_t *txframe) +{ + + int ok; + +#ifdef RAWNET_DEBUG_ARCH + log_message(rawnet_arch_log, + "rawnet_arch_transmit() called, with: force = %s, onecoll = %s, " + "inhibit_crc=%s, tx_pad_dis=%s, txlength=%u", + force ? "TRUE" : "FALSE", + onecoll ? "TRUE" : "FALSE", + inhibit_crc ? "TRUE" : "FALSE", + tx_pad_dis ? "TRUE" : "FALSE", + txlength); +#endif + + ok = rawnet_arch_write(txframe, txlength); + if (ok < 0) { + log_message(rawnet_arch_log, "WARNING! Could not send packet!"); + } +} + +/** + * \brief Check if a frame was received + * + * This function checks if there was a frame received. If so, it returns 1, + * else 0. + * + * If there was no frame, none of the parameters is changed! + * + * If there was a frame, the following actions are done: + * + * - at maximum \a plen byte are transferred into the buffer given by \a pbuffer + * - \a plen gets the length of the received frame, EVEN if this is more + * than has been copied to \a pbuffer! + * - if the dest. address was accepted by the hash filter, \a phashed is set, + * else cleared. + * - if the dest. address was accepted by the hash filter, \a phash_index is + * set to the number of the rule leading to the acceptance + * - if the receive was ok (good CRC and valid length), \a *prx_ok is set, else + * cleared. + * - if the dest. address was accepted because it's exactly our MAC address + * (set by rawnet_arch_set_mac()), \a pcorrect_mac is set, else cleared. + * - if the dest. address was accepted since it was a broadcast address, + * \a pbroadcast is set, else cleared. + * - if the received frame had a crc error, \a pcrc_error is set, else cleared + * + * \param[out] buffer where to store a frame + * \param[in,out] plen IN: maximum length of frame to copy; + * OUT: length of received frame OUT + * can be bigger than IN if received frame was + * longer than supplied buffer + * \param[out] phashed set if the dest. address is accepted by the + * hash filter + * \param[out] phash_index hash table index if hashed == TRUE + * \param[out] prx_ok set if good CRC and valid length + * \param[out] pcorrect_mac set if dest. address is exactly our IA + * \param[out[ pbroadcast set if dest. address is a broadcast address + * \param[out] pcrc_error set if received frame had a CRC error +*/ +int rawnet_arch_receive(uint8_t *pbuffer, int *plen, int *phashed, + int *phash_index, int *prx_ok, int *pcorrect_mac, int *pbroadcast, + int *pcrc_error) +{ + int ok; + +#ifdef RAWNET_DEBUG_ARCH + log_message(rawnet_arch_log, + "rawnet_arch_receive() called, with *plen=%u.", + *plen); +#endif + + + + assert((*plen & 1) == 0); + + ok = rawnet_arch_read(pbuffer, *plen); + if (ok < 0) return 0; + + if (ok & 1) ++ok; + *plen = ok; + + *phashed = + *phash_index = + *pbroadcast = + *pcorrect_mac = + *pcrc_error = 0; + + /* this frame has been received correctly */ + *prx_ok = 1; + return 1; + +} + + + + +#endif /* ifdef HAVE_RAWNET */ diff --git a/src/rawnet/rawnetarch.h b/src/rawnet/rawnetarch.h index 4b76204..68ddc68 100644 --- a/src/rawnet/rawnetarch.h +++ b/src/rawnet/rawnetarch.h @@ -66,6 +66,9 @@ extern int rawnet_arch_enumadapter_close(void); extern char *rawnet_arch_get_standard_interface(void); +extern int rawnet_arch_read(void *buffer, int length); +extern int rawnet_arch_write(const void *buffer, int length); + extern int rawnet_arch_get_mtu(void); extern int rawnet_arch_get_mac(uint8_t mac[6]); diff --git a/src/rawnet/rawnetarch_darwin.c b/src/rawnet/rawnetarch_darwin.c index 582f832..e52a173 100644 --- a/src/rawnet/rawnetarch_darwin.c +++ b/src/rawnet/rawnetarch_darwin.c @@ -347,52 +347,7 @@ static void fix_outgoing_packet(uint8_t *packet, unsigned size, const uint8_t re } - -void rawnet_arch_transmit(int force, int onecoll, int inhibit_crc, int tx_pad_dis, int txlength, uint8_t *txframe) { - - int count = 1; - vmnet_return_t st; - struct vmpktdesc v; - struct iovec iov; - - if (txlength == 0) return; - if (txlength > interface_packet_size) { - log_message(rawnet_arch_log, "packet is too big: %d", txlength); - return; - } - - if (txlength < 12) { - log_message(rawnet_arch_log, "packet is too small: %d", txlength); - return; - } - - /* copy the buffer and fix the source mac address. */ - memcpy(interface_buffer, txframe, txlength); - fix_outgoing_packet(interface_buffer, txlength, interface_mac, interface_fake_mac); - - iov.iov_base = interface_buffer; - iov.iov_len = txlength; - - v.vm_pkt_size = txlength; - v.vm_pkt_iov = &iov; - v.vm_pkt_iovcnt = 1; - v.vm_flags = 0; - - - fprintf(stderr, "\nrawnet_arch_transmit: %u\n", (unsigned)iov.iov_len); - rawnet_hexdump(interface_buffer, v.vm_pkt_size); - - - st = vmnet_write(interface, &v, &count); - - if (st != VMNET_SUCCESS) { - log_message(rawnet_arch_log, "vmnet_write failed!"); - } - return; -} - -int rawnet_arch_receive(uint8_t *pbuffer, int *plen, int *phashed, int *phash_index, int *prx_ok, int *pcorrect_mac, int *pbroadcast, int *pcrc_error) { - +int rawnet_arch_read(void *buffer, int nbyte) { int count = 1; int xfer; @@ -410,8 +365,8 @@ int rawnet_arch_receive(uint8_t *pbuffer, int *plen, int *phashed, int *phash_in st = vmnet_read(interface, &v, &count); if (st != VMNET_SUCCESS) { - log_message(rawnet_arch_log, "vmnet_write failed!"); - return 0; + log_message(rawnet_arch_log, "vmnet_read failed!"); + return -1; } if (count < 1) { @@ -427,40 +382,54 @@ int rawnet_arch_receive(uint8_t *pbuffer, int *plen, int *phashed, int *phash_in } xfer = v.vm_pkt_size; - if (xfer > *plen) xfer = *plen; - memcpy(pbuffer, interface_buffer, xfer); + memcpy(buffer, interface_buffer, xfer); - xfer = v.vm_pkt_size; - if (xfer & 0x01) ++xfer; /* ??? */ - *plen = xfer; /* actual frame size */ - - *phashed = - *phash_index = - *pbroadcast = - *pcorrect_mac = - *pcrc_error = 0; - - *prx_ok = 1; - #if 0 - *pcorrect_mac = memcmp(interface_buffer, interface_mac, 6) == 0; - *pbroadcast = memcmp(interface_buffer, broadcast_mac, 6) == 0; - #endif - - /* vmnet won't send promiscuous packets */ - #if 0 - hashreg = (~crc32_buf(interface_buffer, 6) >> 26) & 0x3f; - if (hash_mask[hashreg >= 32] & (1 << (hashreg & 0x1F))) { - *phashed = 1; - *phash_index = hashreg; - } else { - *phashed = 0; - *phash_index = 0; - } - #endif - - return 1; + return xfer; } +int rawnet_arch_write(const void *buffer, int nbyte) { + + int count = 1; + vmnet_return_t st; + struct vmpktdesc v; + struct iovec iov; + + if (nbyte <= 0) return 0; + + if (nbyte > interface_packet_size) { + log_message(rawnet_arch_log, "packet is too big: %d", nbyte); + return -1; + } + + /* copy the buffer and fix the source mac address. */ + memcpy(interface_buffer, buffer, nbyte); + fix_outgoing_packet(interface_buffer, nbyte, interface_mac, interface_fake_mac); + + iov.iov_base = interface_buffer; + iov.iov_len = nbyte; + + v.vm_pkt_size = nbyte; + v.vm_pkt_iov = &iov; + v.vm_pkt_iovcnt = 1; + v.vm_flags = 0; + + + fprintf(stderr, "\nrawnet_arch_transmit: %u\n", (unsigned)iov.iov_len); + rawnet_hexdump(interface_buffer, v.vm_pkt_size); + + + st = vmnet_write(interface, &v, &count); + + if (st != VMNET_SUCCESS) { + log_message(rawnet_arch_log, "vmnet_write failed!"); + return -1; + } + return nbyte; +} + + + + static unsigned adapter_index = 0; int rawnet_arch_enumadapter_open(void) { adapter_index = 0; diff --git a/src/rawnet/rawnetarch_tap.c b/src/rawnet/rawnetarch_tap.c index 7b886cb..cc596b7 100644 --- a/src/rawnet/rawnetarch_tap.c +++ b/src/rawnet/rawnetarch_tap.c @@ -142,23 +142,12 @@ void rawnet_arch_line_ctl(int bEnableTransmitter, int bEnableReceiver) { /* NOP */ } -void rawnet_arch_transmit(int force, int onecoll, int inhibit_crc, int tx_pad_dis, int txlength, uint8_t *txframe) { - - ssize_t ok = write(interface_fd, txframe, txlength); +int rawnet_arch_read(void *buffer, int nbyte) { + return read(interface_fd, buffer, nbyte); } - -int rawnet_arch_receive(uint8_t *pbuffer, int *plen, int *phashed, int *phash_index, int *prx_ok, int *pcorrect_mac, int *pbroadcast, int *pcrc_error) { - - int count = *plen; - ssize_t ok = read(interface_fd, pbuffer, count); - if (ok < 0) return 0; - if (ok & 0x01) ++ok; /* ??? */ - *plen = ok; - *prx_ok = 1; - *phashed = *pcorrect_mac = *pbroadcast = *pcrc_error = 0; - - return 1; +int rawnet_arch_write(const void *buffer, int nbyte) { + return write(interface_fd, buffer, nbyte); } diff --git a/src/rawnet/rawnetarch_unix.c b/src/rawnet/rawnetarch_unix.c index fb1541c..4177b76 100644 --- a/src/rawnet/rawnetarch_unix.c +++ b/src/rawnet/rawnetarch_unix.c @@ -388,30 +388,25 @@ static int rawnet_arch_receive_frame(rawnet_pcap_internal_t *pinternal) return ret; } +int rawnet_arch_read(void *buffer, int nbyte) { -/** \brief Transmit a frame - * - * \param[in] force Delete waiting frames in transmit buffer - * \param[in] onecoll Terminate after just one collision - * \param[in] inhibit_crc Do not append CRC to the transmission - * \param[in] tx_pad_dis Disable padding to 60 Bytes - * \param[in] txlength Frame length - * \param[in] txframe Pointer to the frame to be transmitted - */ -void rawnet_arch_transmit(int force, int onecoll, int inhibit_crc, - int tx_pad_dis, int txlength, uint8_t *txframe) -{ + int len; -#ifdef RAWNET_DEBUG_ARCH - log_message(rawnet_arch_log, - "rawnet_arch_transmit() called, with: force = %s, onecoll = %s, " - "inhibit_crc=%s, tx_pad_dis=%s, txlength=%u", - force ? "TRUE" : "FALSE", - onecoll ? "TRUE" : "FALSE", - inhibit_crc ? "TRUE" : "FALSE", - tx_pad_dis ? "TRUE" : "FALSE", - txlength); -#endif + rawnet_pcap_internal_t internal = { nbyte, (uint8_t *)buffer }; + + len = rawnet_arch_receive_frame(&internal); + + if (len <= 0) return len; + +#ifdef RAWNET_DEBUG_PKTDUMP + debug_output("Received frame: ", internal.buffer, internal.len); +#endif /* #ifdef RAWNET_DEBUG_PKTDUMP */ + return len; + +} + + +int rawnet_arch_write(const void *buffer, int nbyte) { #ifdef RAWNET_DEBUG_PKTDUMP debug_output("Transmit frame: ", txframe, txlength); @@ -419,97 +414,12 @@ void rawnet_arch_transmit(int force, int onecoll, int inhibit_crc, if (pcap_sendpacket(rawnet_pcap_fp, txframe, txlength) < 0) { log_message(rawnet_arch_log, "WARNING! Could not send packet!"); + return -1; } + return nbyte; } -/** - * \brief Check if a frame was received - * - * This function checks if there was a frame received. If so, it returns 1, - * else 0. - * - * If there was no frame, none of the parameters is changed! - * - * If there was a frame, the following actions are done: - * - * - at maximum \a plen byte are transferred into the buffer given by \a pbuffer - * - \a plen gets the length of the received frame, EVEN if this is more - * than has been copied to \a pbuffer! - * - if the dest. address was accepted by the hash filter, \a phashed is set, - * else cleared. - * - if the dest. address was accepted by the hash filter, \a phash_index is - * set to the number of the rule leading to the acceptance - * - if the receive was ok (good CRC and valid length), \a *prx_ok is set, else - * cleared. - * - if the dest. address was accepted because it's exactly our MAC address - * (set by rawnet_arch_set_mac()), \a pcorrect_mac is set, else cleared. - * - if the dest. address was accepted since it was a broadcast address, - * \a pbroadcast is set, else cleared. - * - if the received frame had a crc error, \a pcrc_error is set, else cleared - * - * \param[out] buffer where to store a frame - * \param[in,out] plen IN: maximum length of frame to copy; - * OUT: length of received frame OUT - * can be bigger than IN if received frame was - * longer than supplied buffer - * \param[out] phashed set if the dest. address is accepted by the - * hash filter - * \param[out] phash_index hash table index if hashed == TRUE - * \param[out] prx_ok set if good CRC and valid length - * \param[out] pcorrect_mac set if dest. address is exactly our IA - * \param[out[ pbroadcast set if dest. address is a broadcast address - * \param[out] pcrc_error set if received frame had a CRC error -*/ -int rawnet_arch_receive(uint8_t *pbuffer, int *plen, int *phashed, - int *phash_index, int *prx_ok, int *pcorrect_mac, int *pbroadcast, - int *pcrc_error) -{ - int len; - - rawnet_pcap_internal_t internal = { *plen, pbuffer }; - -#ifdef RAWNET_DEBUG_ARCH - log_message(rawnet_arch_log, - "rawnet_arch_receive() called, with *plen=%u.", - *plen); -#endif - - assert((*plen & 1) == 0); - - len = rawnet_arch_receive_frame(&internal); - - if (len != -1) { - -#ifdef RAWNET_DEBUG_PKTDUMP - debug_output("Received frame: ", internal.buffer, internal.len); -#endif /* #ifdef RAWNET_DEBUG_PKTDUMP */ - - if (len & 1) { - ++len; - } - - *plen = len; - - /* we don't decide if this frame fits the needs; - * by setting all zero, we let tfe.c do the work - * for us - */ - *phashed = - *phash_index = - *pbroadcast = - *pcorrect_mac = - *pcrc_error = 0; - - /* this frame has been received correctly */ - *prx_ok = 1; - - return 1; - } - - return 0; -} - /** \brief Find default device on which to capture * diff --git a/src/rawnet/rawnetarch_win32.c b/src/rawnet/rawnetarch_win32.c index 741dbce..e6527d1 100644 --- a/src/rawnet/rawnetarch_win32.c +++ b/src/rawnet/rawnetarch_win32.c @@ -438,23 +438,29 @@ static int rawnet_arch_receive_frame(Ethernet_PCAP_internal_t *pinternal) return ret; } -/* int force - FORCE: Delete waiting frames in transmit buffer */ -/* int onecoll - ONECOLL: Terminate after just one collision */ -/* int inhibit_crc - INHIBITCRC: Do not append CRC to the transmission */ -/* int tx_pad_dis - TXPADDIS: Disable padding to 60 Bytes */ -/* int txlength - Frame length */ -/* uint8_t *txframe - Pointer to the frame to be transmitted */ -void rawnet_arch_transmit(int force, int onecoll, int inhibit_crc, int tx_pad_dis, int txlength, uint8_t *txframe) -{ -#ifdef RAWNET_DEBUG_ARCH - log_message(rawnet_arch_log, "rawnet_arch_transmit() called, with: force = %s, onecoll = %s, inhibit_crc=%s, tx_pad_dis=%s, txlength=%u", - force ? "TRUE" : "FALSE", - onecoll ? "TRUE" : "FALSE", - inhibit_crc ? "TRUE" : "FALSE", - tx_pad_dis ? "TRUE" : "FALSE", - txlength); -#endif +int rawnet_arch_read(void *buffer, int nbyte) { + + int len; + + Ethernet_PCAP_internal_t internal; + + internal.len = nbyte; + internal.buffer = (uint8_t *)buffer; + + len = rawnet_arch_receive_frame(&internal); + + if (len <= 0) return len; + +#ifdef RAWNET_DEBUG_PKTDUMP + debug_output("Received frame: ", internal.buffer, internal.len); +#endif /* #ifdef RAWNET_DEBUG_PKTDUMP */ + return len; + +} + + +int rawnet_arch_write(const void *buffer, int nbyte) { #ifdef RAWNET_DEBUG_PKTDUMP debug_output("Transmit frame: ", txframe, txlength); @@ -462,9 +468,12 @@ void rawnet_arch_transmit(int force, int onecoll, int inhibit_crc, int tx_pad_di if ((*p_pcap_sendpacket)(EthernetPcapFP, txframe, txlength) == -1) { log_message(rawnet_arch_log, "WARNING! Could not send packet!"); + return -1; } + return nbyte; } + /* rawnet_arch_receive() From 04314d7bb0632a7fdb35ced3549378d5107e557e Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Mon, 31 Dec 2018 20:48:07 -0500 Subject: [PATCH 022/186] dead code, no longer needed as of Uthernet Link Layer 1.0.2 --- src/rawnet/rawnetarch_darwin.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/rawnet/rawnetarch_darwin.c b/src/rawnet/rawnetarch_darwin.c index e52a173..8aa8cc8 100644 --- a/src/rawnet/rawnetarch_darwin.c +++ b/src/rawnet/rawnetarch_darwin.c @@ -330,15 +330,6 @@ static void fix_outgoing_packet(uint8_t *packet, unsigned size, const uint8_t re /* dhcp request - fix the hardware address */ if (is_broadcast(packet, size) && is_dhcp_out(packet, size)) { - /* set source ip to 0 - LL bug? */ - /* fixed in link layer as of 2018-12-17 */ -#if 0 - unsigned ck; - memset(packet + 26, 0, 4); - ck = ip_checksum(packet); - packet[eth_data + ip_header_cksum + 0] = ck >> 8; - packet[eth_data + ip_header_cksum + 1] = ck; -#endif if (!memcmp(packet + 70, fake_mac, 6)) memcpy(packet + 70, real_mac, 6); From 4af3cf07617ca8eb06a18290c1a03b7a82171a41 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Mon, 31 Dec 2018 20:48:34 -0500 Subject: [PATCH 023/186] untested win32 mac code. --- src/rawnet/rawnetarch_win32.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/rawnet/rawnetarch_win32.c b/src/rawnet/rawnetarch_win32.c index e6527d1..b4b1883 100644 --- a/src/rawnet/rawnetarch_win32.c +++ b/src/rawnet/rawnetarch_win32.c @@ -574,6 +574,19 @@ extern int rawnet_arch_get_mtu(void) { } extern int rawnet_arch_get_mac(uint8_t mac[6]) { + + char buffer[sizeof(PACKET_OID_DATA) + 6]; + PPACKET_OID_DATA data = (PPACKET_OID_DATA)data; + + /* 802.5 = token ring, 802.3 = wired ethernet */ + data->Oid = OID_802_3_CURRENT_ADDRESS; // OID_802_3_CURRENT_ADDRESS ? OID_802_3_PERMANENT_ADDRESS ? + data->Length = 6; + + if (PacketRequest(EthernetPcapFP, FALSE, data)) { + memcpy(mac, data->Data, 6); + return 0; + } + return -1; } From aaeda41baf02e7de385dc46684681b2c3a3bf326 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Mon, 31 Dec 2018 20:49:07 -0500 Subject: [PATCH 024/186] unix mtu/mac code for pcap. --- src/rawnet/rawnetarch_unix.c | 42 ++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/rawnet/rawnetarch_unix.c b/src/rawnet/rawnetarch_unix.c index 4177b76..115c1a5 100644 --- a/src/rawnet/rawnetarch_unix.c +++ b/src/rawnet/rawnetarch_unix.c @@ -110,6 +110,7 @@ static pcap_if_t *rawnet_pcap_dev_list = NULL; static pcap_t *rawnet_pcap_fp = NULL; +static char *rawnet_device_name = NULL; /** \brief Buffer for pcap error messages @@ -232,6 +233,7 @@ static int rawnet_pcap_open_adapter(const char *interface_name) rawnet_pcap_fp = NULL; return 0; } + rawnet_device_name = strdup(interface_name); return 1; } @@ -448,10 +450,50 @@ char *rawnet_arch_get_standard_interface(void) } extern int rawnet_arch_get_mtu(void) { + + #if defined(__linux__) + int fd; + int ok; + struct ifreq ifr; + + if (!rawnet_device_name) return -1; + + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) return -1; + + memset(&ifr, 0, sizeof(ifr)); + strcpy(ifr.ifr_name, rawnet_device_name); + ok = ioctl(fd, SIOCGIFMTU, (void *)&ifr); + close(fd); + if (ok < 0) return -1; + return ifr.ifr_mtu; + #endif + return -1; } extern int rawnet_arch_get_mac(uint8_t mac[6]) { + + #if defined(__linux__) + + int fd; + struct ifreq ifr; + int ok; + + if (!rawnet_device_name) return -1; + + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) return -1; + + memset(&ifr, 0, sizeof(ifr)); + strcpy(ifr.ifr_name, rawnet_device_name); + ok = ioctl(fd, SIOCGIFHWADDR, &ifr); + close(fd); + if (ok < 0) return -1; + memcpy(mac, &ifr.ifr_hwaddr.sa_data, 6); + return 0; + #endif + return -1; } From fae38450e9fca63fb067f6404649a5d1dd44eadb Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Mon, 31 Dec 2018 20:49:39 -0500 Subject: [PATCH 025/186] tap cleanup, more *BSD support. --- src/rawnet/rawnetarch_tap.c | 147 ++++++++++++++++++++++++++++++------ 1 file changed, 123 insertions(+), 24 deletions(-) diff --git a/src/rawnet/rawnetarch_tap.c b/src/rawnet/rawnetarch_tap.c index cc596b7..b7d099f 100644 --- a/src/rawnet/rawnetarch_tap.c +++ b/src/rawnet/rawnetarch_tap.c @@ -1,6 +1,39 @@ /* tun/tap support */ /* for Linux, *BSD */ +/* + * tap is a virtual ethernet devices. + * open the device, configure, and read/write ethernet frames. + * + * Linux setup: (from Network Programmability and Automation, Appendix A) + * + * Notes: + * - this assumes eth0 is your main interface device + * - may need to install the iproute/iproute2 package (ip command) + * - do this stuff as root. + * + * 1. create a tap interface + * $ ip tuntap add tap65816 mode tap user YOUR_USER_NAME + * $ ip link set tap65816 up + * + * 2. create a network bridge + * $ ip link add name br0 type bridge + * $ ip link set br0 up + * + * 3. bridge the physical network and virtual network. + * $ ip link set eth0 master br0 + * $ ip link set tap65816 master br0 + * + * 4. remove ip address from physical device (This will kill networking) + * ip address flush dev eth0 + * + * 5. and add the ip address to the bridge + * dhclient br0 # if using dhcp + * ip address add 192.168.1.1/24 dev eth0 # if using static ip address. + */ + + + #define _GNU_SOURCE #include @@ -25,15 +58,15 @@ #include "rawnetsupp.h" #if defined(__linux__) -const char *default_interface_name = "/dev/net/tun"; +#define TAP_DEVICE "/dev/net/tun" #endif #if defined(__FreeBSD__) -const char *default_interface_name = "/dev/tap"; +#define TAP_DEVICE "/dev/tap" #endif static int interface_fd = -1; - +static char *interface_dev = NULL; int rawnet_arch_init(void) { interface_fd = -1; @@ -48,35 +81,25 @@ void rawnet_arch_post_reset(void) { } -/* interface name may be a full path (/dev/tap0) or relative (tap0) */ +#if defined(__linux__) +/* interface name. default is tap65816. */ int rawnet_arch_activate(const char *interface_name) { struct ifreq ifr; int ok; - char *tmp = NULL; int one = 1; int fd; if (!interface_name || !*interface_name) { - interface_name = default_interface_name; - } - if (interface_name[0] != '/') { - ok = asprintf(&tmp, "/dev/%s", interface_name); - if (ok < 0) { - perror("rawnet_arch_activate: asprintf"); - return 0; - } - interface_name = tmp; + interface_name = "tap65816"; } - fd = open(interface_name, O_RDWR); + fd = open(TAP_DEVICE, O_RDWR); if (fd < 0) { - fprintf(stderr, "rawnet_arch_activate: open(%s): %s\n", interface_name, strerror(errno)); - free(tmp); + fprintf(stderr, "rawnet_arch_activate: open(%s): %s\n", TAP_DEVICE, strerror(errno)); return 0; } - free(tmp); ok = ioctl(fd, FIONBIO, &one); if (ok < 0) { @@ -85,8 +108,8 @@ int rawnet_arch_activate(const char *interface_name) { return 0; } - #if defined(__linux__) memset(&ifr, 0, sizeof(ifr)); + strcpy(&if.ifr_name, interface_name); ifr.ifr_flags = IFF_TAP | IFF_NO_PI; ok = ioctl(fd, TUNSETIFF, (void *) &ifr); if (ok < 0) { @@ -94,15 +117,60 @@ int rawnet_arch_activate(const char *interface_name) { close(fd); return 0; } - #endif + interface_dev = strdup(interface_name); interface_fd = fd; return 1; } +#endif + +#if defined(__FreeBSD__) +/* man tap(4) */ +/* interface name. default is tap65816. */ +int rawnet_arch_activate(const char *interface_name) { + + struct ifreq ifr; + + int ok; + int one = 1; + int fd; + char *path[64]; + + if (!interface_name || !*interface_name) { + interface_name = "tap65816"; + } + + ok = snprintf(path, sizeof(path), "/dev/%s", interface_name); + if (ok >= sizeof(path)) return 0; + + fd = open(path, O_RDWR); + if (fd < 0) { + fprintf(stderr, "rawnet_arch_activate: open(%s): %s\n", path, strerror(errno)); + return 0; + } + + ok = ioctl(fd, FIONBIO, &one); + if (ok < 0) { + perror("ioctl(FIONBIO"); + close(fd); + return 0; + } + + interface_dev = strdup(interface_name); + interface_fd = fd; + return 1; +} +#endif + + + void rawnet_arch_deactivate(void) { if (interface_fd >= 0) close(interface_fd); + free(interface_dev); + + interface_dev = NULL; interface_fd = -1; } @@ -177,6 +245,9 @@ int rawnet_arch_enumadapter_open(void) { devices = (char **)malloc(sizeof(char *) * capacity); if (!devices) return 0; + +#if defined(__linux__) + dp = opendir("/sys/class/net/"); if (!dp) goto fail; @@ -196,7 +267,7 @@ int rawnet_arch_enumadapter_open(void) { if (cp) { /* expect 0x1002 */ int flags = strtol(cp, (char **)NULL, 16); - if (flags == IFF_TAP | IFF_NO_PI) { + if ((flags & TUN_TYPE_MASK) == IFF_TAP) { devices[count++] = strdup(d->d_name); if (count == capacity) { @@ -212,6 +283,32 @@ int rawnet_arch_enumadapter_open(void) { } closedir(dp); +#endif + +#if defined(__FreeBSD__) + /* dev/tapxxxx */ + dp = opendir("/dev/"); + if (!dp) goto fail; + for(;;) { + struct dirent *d = readdir(dp); + if (!d) break; + + if (d->d_name[0] == '.') continue; + if (strncmp(d->d_name, "tap", 3) == 0 && isdigit(d->d_name[3])) { + + devices[count++] = strdup(d->d_name); + if (count == capacity) { + char **tmp; + capacity *= 2; + tmp = (char **)realloc(devices, sizeof(char *) * capacity); + if (tmp) devices = tmp; + else break; /* no mem? */ + } + } + + } + closedir(dp); +#endif /* sort them ... */ qsort(devices, count, sizeof(char *), cmp); @@ -253,16 +350,17 @@ int rawnet_arch_enumadapter(char **ppname, char **ppdescription) { char *rawnet_arch_get_standard_interface(void) { - return lib_stralloc(default_interface_name); + return lib_stralloc("tap65816"); } int rawnet_arch_get_mtu(void) { if (interface_fd < 0) return -1; #if defined(__linux__) - + /* n.b. linux tap driver doesn't actually support SIOCGIFMTU */ struct ifreq ifr; - if (ioctl(interface_fd, TUNGETIFF, (void *) &ifr) < 0) return -1; /* ? */ + memset(&ifr, 0, sizeof(ifr)); + if (ioctl(interface_fd, SIOCGIFMTU, (void *) &ifr) < 0) return -1; /* ? */ return ifr.ifr_mtu; #endif @@ -282,6 +380,7 @@ int rawnet_arch_get_mac(uint8_t mac[6]) { #if defined(__linux__) struct ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); if (ioctl(interface_fd, SIOCGIFHWADDR, (void *)&ifr) < 0) return -1; memcpy(mac, ifr.ifr_hwaddr.sa_data, 6); return 0; From 4da1a246c78ff71947a11544be1d6e63a47765a5 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Mon, 31 Dec 2018 20:50:07 -0500 Subject: [PATCH 026/186] get rid of the -enet flag, for now. --- src/options.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/options.c b/src/options.c index 0040f81..86c03f4 100644 --- a/src/options.c +++ b/src/options.c @@ -315,15 +315,6 @@ int parse_cli_options(int argc, char **argv) { glogf("%s Setting scanline simulator darkness to %d%%", parse_log_prefix, tmp1); g_scanline_simulator = tmp1; i++; - } else if(!strcmp("-enet", argv[i])) { - if((i+1) >= argc) { - glogf("%s Error, option '-enet' missing argument", parse_log_prefix); - exit(1); - } - tmp1 = strtol(argv[i+1], 0, 0); - glogf("%s Using %d as ethernet enable val", parse_log_prefix, tmp1); - g_ethernet = tmp1; - i++; } else if(!strcmp("-x", argv[i])) { if((i+1) >= argc) { glogf("%s Error, option '-x' missing argument", parse_log_prefix); @@ -422,7 +413,6 @@ void help_exit() { printf(" -skip value Set skip_amt to value\n"); printf(" -audio value Set audio enable to value\n"); printf(" -arate value Set preferred audio rate to value\n"); - printf(" -enet value Set ethernet to value\n"); printf(" -config value Set config file to value\n"); printf(" -debugport value Set debugport to value\n"); printf(" -ssdir value Set screenshot save directory to value\n"); From ecf6c58661ce0f9ee2e51d4df703dc914c0c0a34 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Mon, 31 Dec 2018 20:51:14 -0500 Subject: [PATCH 027/186] improved ethernet startup. use device name instead of index into a list that could change. --- src/sim65816.c | 42 +++++++++++------------------------------- 1 file changed, 11 insertions(+), 31 deletions(-) diff --git a/src/sim65816.c b/src/sim65816.c index 9e26a7c..bad7ac2 100644 --- a/src/sim65816.c +++ b/src/sim65816.c @@ -142,7 +142,7 @@ int g_iw2_emul = 0; int g_serial_out_masking = 0; int g_serial_modem[2] = { 0, 1 }; int g_ethernet = 0; -int g_ethernet_interface = 0; +char* g_ethernet_interface = ""; int g_ethernet_enabled = 0; int g_parallel = 0; int g_parallel_out_masking = 0; @@ -946,41 +946,21 @@ int gsplusmain(int argc, char **argv) { //If ethernet is enabled in config.gsport, let's initialize it #ifdef HAVE_RAWNET g_ethernet_enabled = 0; - if (g_ethernet == 1) + /* todo - pass device name via cmd line? */ + if (g_ethernet) { - int i = -1; - char *ppname = NULL; - char *ppdes = NULL; - if (rawnet_enumadapter_open()) - { - //Loop through the available adapters until we reach the interface number specified in config.gsport - while(rawnet_enumadapter(&ppname,&ppdes)) - { - ++i; - if (i == g_ethernet_interface) { + if (!g_ethernet_interface || !*g_ethernet_interface) { + g_ethernet_interface = rawnet_get_standard_interface(); + } - if (cs8900_init() >= 0 && cs8900_activate(ppname) >= 0) { - g_ethernet_enabled = 1; - } else { - fprintf(stderr, "Unable to start ethernet.\n"); - } - - free(ppname); - free(ppdes); - - break; + if (g_ethernet_interface && *g_ethernet_interface) { + if (cs8900_init() >= 0 && cs8900_activate(g_ethernet_interface) >= 0) { + g_ethernet_enabled = 1; + } else { + glogf("Unable to start ethernet (%s).\n", g_ethernet_interface); } - - free(ppname); - free(ppdes); - } - rawnet_enumadapter_close(); } - if (i < 0) - { - fprintf(stderr, "No ethernet host adapters found. Do you have PCap installed/enabled?\nUthernet support is OFF.\n"); - } } #endif From 4923efb5fc7c6f6cde5a903dc96bd25cfa410e26 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Wed, 2 Jan 2019 18:45:14 -0500 Subject: [PATCH 028/186] cleanup to build under msys2/mingw64 --- src/CMakeLists.txt | 23 ++++- src/atbridge/CMakeLists.txt | 5 + src/atbridge/aarp.c | 2 +- src/atbridge/atbridge.c | 2 +- src/atbridge/elap.c | 10 +- src/atbridge/pcap_delay.h | 2 +- src/rawnet/CMakeLists.txt | 2 + src/rawnet/rawnetarch_unix.c | 4 +- src/rawnet/rawnetarch_win32.c | 175 +++++++++++++++------------------- src/scc_socket_driver.c | 2 +- src/sim65816.c | 14 +++ src/tfe/CMakeLists.txt | 4 + 12 files changed, 133 insertions(+), 112 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1818f56..03c987f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -52,6 +52,8 @@ set(DRIVER "SDL" CACHE STRING "Driver (SDL, X11, WIN32, FB, or HEADLESS") option(DEBUGGER "Enable the debugger" ON) option(HOST_FST "Enable host fst support" ON) option(TOGGLE_STATUS "Enable F10 Toggle Status support (win32/x11)" OFF) +option(WITH_RAWNET "Enable Uthernet emulation" OFF) +option(WITH_ATBRIDGE "Enable AT Bridge" OFF) set(generated_headers 8inst_c.h 16inst_c.h 8inst_s.h 16inst_s.h size_c.h size_s.h 8size_s.h 16size_s.h) @@ -82,7 +84,8 @@ if(CYGWIN OR MSYS) endif() if(WIN32) - add_definitions(-D__USE_W32_SOCKETS -D_WINSOCK2API_) + # WTF? + #add_definitions(-D__USE_W32_SOCKETS -D_WINSOCK2API_) endif() if(MSVC) @@ -151,13 +154,19 @@ SET_SOURCE_FILES_PROPERTIES( MACOSX_PACKAGE_LOCATION Resources ) -# target_link_libraries(GSplus atbridge) -target_link_libraries(GSplus rawnet) +if (WITH_RAWNET) + target_link_libraries(GSplus rawnet) +endif() + +if (WITH_ATBRIDGE) + target_link_libraries(GSplus atbridge) +endif() if (DRIVER MATCHES "WIN32") target_link_libraries(GSplus comdlg32 Shlwapi IPHlpApi winmm gdi32 dsound comctl32 ws2_32 shell32 ) + target_compile_definitions(GSplus PRIVATE WIN_SOUND) endif() if (DRIVER MATCHES "SDL") @@ -170,9 +179,15 @@ if (APPLE) endif() if (TOGGLE_STATUS) - target_compile_options(GSplus PUBLIC -DTOGGLE_STATUS) + target_compile_definitions(GSplus PUBLIC TOGGLE_STATUS) endif() +if (DEBUGGER) + target_compile_definitions(GSplus PRIVATE GSPLUS_DEBUGGER) +endif() + + + #if (APPLE AND DRIVER MATCHES "SDL") # target_compile_options(GSplus PRIVATE -F${CMAKE_CURRENT_SOURCE_DIR} ) # target_link_libraries(GSplus -F${CMAKE_CURRENT_SOURCE_DIR} "-framework SDL2" -Wl,-rpath,@executable_path/../Frameworks) diff --git a/src/atbridge/CMakeLists.txt b/src/atbridge/CMakeLists.txt index 050752a..e161e6b 100644 --- a/src/atbridge/CMakeLists.txt +++ b/src/atbridge/CMakeLists.txt @@ -3,3 +3,8 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON) add_library(atbridge aarp.c atbridge.c elap.c llap.c pcap_delay.c port.c) target_compile_definitions(atbridge PUBLIC HAVE_ATBRIDGE) + +if(WIN32) + target_include_directories(atbridge PRIVATE ../rawnet/include) + target_link_libraries(atbridge ws2_32) # winsock2 +endif() diff --git a/src/atbridge/aarp.c b/src/atbridge/aarp.c index edbb925..dcb066d 100644 --- a/src/atbridge/aarp.c +++ b/src/atbridge/aarp.c @@ -17,7 +17,7 @@ #include "aarp.h" #ifdef WIN32 -#include +#include #elif __linux__ #include #endif diff --git a/src/atbridge/atbridge.c b/src/atbridge/atbridge.c index f908f2c..e2204b9 100644 --- a/src/atbridge/atbridge.c +++ b/src/atbridge/atbridge.c @@ -18,7 +18,7 @@ #include "aarp.h" #ifdef WIN32 -#include +#include #elif __linux__ #include #endif diff --git a/src/atbridge/elap.c b/src/atbridge/elap.c index 3774df4..c55c4cc 100644 --- a/src/atbridge/elap.c +++ b/src/atbridge/elap.c @@ -16,12 +16,12 @@ #include "elap_defs.h" #include "pcap_delay.h" -#ifdef __CYGWIN__ -#include -#include -#endif +// #ifdef __CYGWIN__ +// #include +// #include +// #endif #ifdef WIN32 -#include +#include #include #endif #ifdef __linux__ diff --git a/src/atbridge/pcap_delay.h b/src/atbridge/pcap_delay.h index 03e6920..64b1bf3 100644 --- a/src/atbridge/pcap_delay.h +++ b/src/atbridge/pcap_delay.h @@ -15,7 +15,7 @@ Feel free to extend the wrapper. */ #ifdef WIN32 -#include "../arch/win32/pcap.h" +#include #elif __linux__ #include #elif __APPLE__ diff --git a/src/rawnet/CMakeLists.txt b/src/rawnet/CMakeLists.txt index 1ff2337..fceacbc 100644 --- a/src/rawnet/CMakeLists.txt +++ b/src/rawnet/CMakeLists.txt @@ -14,6 +14,8 @@ target_compile_definitions(rawnet PUBLIC HAVE_RAWNET) target_compile_definitions(rawnet PRIVATE CS8900_DEBUG RAWNET_DEBUG_FRAMES) if(WIN32) + target_include_directories(rawnet PRIVATE include) + target_link_libraries(rawnet ws2_32) # winsock2 elseif(APPLE) #target_link_libraries(rawnet PRIVATE pcap) target_link_libraries(rawnet PRIVATE "-framework vmnet") diff --git a/src/rawnet/rawnetarch_unix.c b/src/rawnet/rawnetarch_unix.c index 115c1a5..0d30d0f 100644 --- a/src/rawnet/rawnetarch_unix.c +++ b/src/rawnet/rawnetarch_unix.c @@ -411,10 +411,10 @@ int rawnet_arch_read(void *buffer, int nbyte) { int rawnet_arch_write(const void *buffer, int nbyte) { #ifdef RAWNET_DEBUG_PKTDUMP - debug_output("Transmit frame: ", txframe, txlength); + debug_output("Transmit frame: ", buffer, nbyte); #endif /* #ifdef RAWNET_DEBUG_PKTDUMP */ - if (pcap_sendpacket(rawnet_pcap_fp, txframe, txlength) < 0) { + if (pcap_sendpacket(rawnet_pcap_fp, buffer, nbyte) < 0) { log_message(rawnet_arch_log, "WARNING! Could not send packet!"); return -1; } diff --git a/src/rawnet/rawnetarch_win32.c b/src/rawnet/rawnetarch_win32.c index b4b1883..5da1a46 100644 --- a/src/rawnet/rawnetarch_win32.c +++ b/src/rawnet/rawnetarch_win32.c @@ -31,7 +31,17 @@ /* #define WPCAP */ + + #include +/* prevent bpf redeclaration in packet32 */ +#ifndef BPF_MAJOR_VERSION +#define BPF_MAJOR_VERSION +#endif +#include +#include +#include + #include #include @@ -45,7 +55,7 @@ #include "rawnetsupp.h" typedef pcap_t *(*pcap_open_live_t)(const char *, int, int, int, char *); -typedef void *(*pcap_close_t)(pcap_if_t *); +typedef void *(*pcap_close_t)(pcap_t *); typedef int (*pcap_dispatch_t)(pcap_t *, int, pcap_handler, u_char *); typedef int (*pcap_setnonblock_t)(pcap_t *, int, char *); typedef int (*pcap_datalink_t)(pcap_t *); @@ -54,6 +64,13 @@ typedef void (*pcap_freealldevs_t)(pcap_if_t *); typedef int (*pcap_sendpacket_t)(pcap_t *p, u_char *buf, int size); typedef char *(*pcap_lookupdev_t)(char *); + +typedef VOID (*PacketCloseAdapter_t)(LPADAPTER lpAdapter); +typedef LPADAPTER (*PacketOpenAdapter_t)(PCHAR AdapterName); +typedef BOOLEAN (*PacketSendPacket_t)(LPADAPTER AdapterObject, LPPACKET pPacket, BOOLEAN Sync); +typedef BOOLEAN (*PacketRequest_t)(LPADAPTER AdapterObject, BOOLEAN Set, PPACKET_OID_DATA OidData); + + /** #define RAWNET_DEBUG_ARCH 1 **/ /** #define RAWNET_DEBUG_PKTDUMP 1 **/ @@ -71,7 +88,13 @@ static pcap_sendpacket_t p_pcap_sendpacket; static pcap_datalink_t p_pcap_datalink; static pcap_lookupdev_t p_pcap_lookupdev; +static PacketCloseAdapter_t p_PacketCloseAdapter; +static PacketOpenAdapter_t p_PacketOpenAdapter; +static PacketSendPacket_t p_PacketSendPacket; +static PacketRequest_t p_PacketRequest; + static HINSTANCE pcap_library = NULL; +static HINSTANCE packet_library = NULL; /* ------------------------------------------------------------------------- */ /* variables needed */ @@ -81,6 +104,7 @@ static HINSTANCE pcap_library = NULL; static pcap_if_t *EthernetPcapNextDev = NULL; static pcap_if_t *EthernetPcapAlldevs = NULL; static pcap_t *EthernetPcapFP = NULL; +static char *rawnet_device_name = NULL; static char EthernetPcapErrbuf[PCAP_ERRBUF_SIZE]; @@ -118,6 +142,9 @@ static void EthernetPcapFreeLibrary(void) } pcap_library = NULL; + if (packet_library) FreeLibrary(packet_library); + packet_library = NULL; + p_pcap_open_live = NULL; p_pcap_close = NULL; p_pcap_dispatch = NULL; @@ -127,12 +154,17 @@ static void EthernetPcapFreeLibrary(void) p_pcap_sendpacket = NULL; p_pcap_datalink = NULL; p_pcap_lookupdev = NULL; + + p_PacketOpenAdapter = NULL; + p_PacketCloseAdapter = NULL; + p_PacketSendPacket = NULL; + p_PacketRequest = NULL; } } /* since I don't like typing too much... */ #define GET_PROC_ADDRESS_AND_TEST( _name_ ) \ - p_##_name_ = (_name_##_t) GetProcAddress(pcap_library, #_name_); \ + p_##_name_ = (_name_##_t) GetProcAddress(x, #_name_); \ if (!p_##_name_ ) { \ log_message(rawnet_arch_log, "GetProcAddress " #_name_ " failed!"); \ EthernetPcapFreeLibrary(); \ @@ -146,6 +178,7 @@ static BOOL EthernetPcapLoadLibrary(void) * winpcap is c:\System32\wpcap.dll * */ + HINSTANCE x = NULL; if (!pcap_library) { /* This inserts c:\System32\Npcap\ into the search path. */ char buffer[512]; @@ -163,6 +196,7 @@ static BOOL EthernetPcapLoadLibrary(void) return FALSE; } + x = pcap_library; GET_PROC_ADDRESS_AND_TEST(pcap_open_live); GET_PROC_ADDRESS_AND_TEST(pcap_close); GET_PROC_ADDRESS_AND_TEST(pcap_dispatch); @@ -174,6 +208,21 @@ static BOOL EthernetPcapLoadLibrary(void) GET_PROC_ADDRESS_AND_TEST(pcap_lookupdev); } + if (!packet_library) { + packet_library = LoadLibrary(TEXT("Packet.dll")); + + if (!packet_library) { + log_message(rawnet_arch_log, "LoadLibrary Packet.dll failed!"); + return FALSE; + } + + x = packet_library; + GET_PROC_ADDRESS_AND_TEST(PacketOpenAdapter); + GET_PROC_ADDRESS_AND_TEST(PacketCloseAdapter); + GET_PROC_ADDRESS_AND_TEST(PacketSendPacket); + GET_PROC_ADDRESS_AND_TEST(PacketRequest); + } + return TRUE; } @@ -230,6 +279,10 @@ int rawnet_arch_enumadapter(char **ppname, char **ppdescription) *ppname = lib_stralloc(EthernetPcapNextDev->name); *ppdescription = lib_stralloc(EthernetPcapNextDev->description); + printf("%s: %s\n", + EthernetPcapNextDev->name ? EthernetPcapNextDev->name : "", + EthernetPcapNextDev->description ? EthernetPcapNextDev->description : "" + ); EthernetPcapNextDev = EthernetPcapNextDev->next; return 1; @@ -284,19 +337,20 @@ static BOOL EthernetPcapOpenAdapter(const char *interface_name) return FALSE; } - if ((*p_pcap_setnonblock)(EthernetPcapFP, 1, EthernetPcapErrbuf) < 0) { - log_message(rawnet_arch_log, "WARNING: Setting PCAP to non-blocking failed: '%s'", EthernetPcapErrbuf); - } - /* Check the link layer. We support only Ethernet for simplicity. */ if ((*p_pcap_datalink)(EthernetPcapFP) != DLT_EN10MB) { log_message(rawnet_arch_log, "ERROR: Ethernet works only on Ethernet networks."); rawnet_enumadapter_close(); - *(p_pcap_close(EthernetPcapFP)); + (*p_pcap_close)(EthernetPcapFP); EthernetPcapFP = NULL; return FALSE; } + if ((*p_pcap_setnonblock)(EthernetPcapFP, 1, EthernetPcapErrbuf) < 0) { + log_message(rawnet_arch_log, "WARNING: Setting PCAP to non-blocking failed: '%s'", EthernetPcapErrbuf); + } + + rawnet_device_name = strdup(EthernetPcapDevice->name); rawnet_enumadapter_close(); return TRUE; } @@ -463,10 +517,10 @@ int rawnet_arch_read(void *buffer, int nbyte) { int rawnet_arch_write(const void *buffer, int nbyte) { #ifdef RAWNET_DEBUG_PKTDUMP - debug_output("Transmit frame: ", txframe, txlength); + debug_output("Transmit frame: ", buffer, nbyte); #endif /* #ifdef RAWNET_DEBUG_PKTDUMP */ - if ((*p_pcap_sendpacket)(EthernetPcapFP, txframe, txlength) == -1) { + if ((*p_pcap_sendpacket)(EthernetPcapFP, (u_char *)buffer, nbyte) == -1) { log_message(rawnet_arch_log, "WARNING! Could not send packet!"); return -1; } @@ -474,88 +528,6 @@ int rawnet_arch_write(const void *buffer, int nbyte) { } -/* - rawnet_arch_receive() - - This function checks if there was a frame received. - If so, it returns 1, else 0. - - If there was no frame, none of the parameters is changed! - - If there was a frame, the following actions are done: - - - at maximum *plen byte are transferred into the buffer given by pbuffer - - *plen gets the length of the received frame, EVEN if this is more - than has been copied to pbuffer! - - if the dest. address was accepted by the hash filter, *phashed is set, else - cleared. - - if the dest. address was accepted by the hash filter, *phash_index is - set to the number of the rule leading to the acceptance - - if the receive was ok (good CRC and valid length), *prx_ok is set, - else cleared. - - if the dest. address was accepted because it's exactly our MAC address - (set by rawnet_arch_set_mac()), *pcorrect_mac is set, else cleared. - - if the dest. address was accepted since it was a broadcast address, - *pbroadcast is set, else cleared. - - if the received frame had a crc error, *pcrc_error is set, else cleared -*/ - -/* uint8_t *pbuffer - where to store a frame */ -/* int *plen - IN: maximum length of frame to copy; - OUT: length of received frame - OUT can be bigger than IN if received frame was - longer than supplied buffer */ -/* int *phashed - set if the dest. address is accepted by the hash filter */ -/* int *phash_index - hash table index if hashed == TRUE */ -/* int *prx_ok - set if good CRC and valid length */ -/* int *pcorrect_mac - set if dest. address is exactly our IA */ -/* int *pbroadcast - set if dest. address is a broadcast address */ -/* int *pcrc_error - set if received frame had a CRC error */ - -int rawnet_arch_receive(uint8_t *pbuffer, int *plen, int *phashed, int *phash_index, int *prx_ok, int *pcorrect_mac, int *pbroadcast, int *pcrc_error) -{ - int len; - - Ethernet_PCAP_internal_t internal; - - internal.len = *plen; - internal.buffer = pbuffer; - -#ifdef RAWNET_DEBUG_ARCH - log_message(rawnet_arch_log, "rawnet_arch_receive() called, with *plen=%u.", *plen); -#endif - - assert((*plen & 1) == 0); - - len = rawnet_arch_receive_frame(&internal); - - if (len != -1) { - -#ifdef RAWNET_DEBUG_PKTDUMP - debug_output("Received frame: ", internal.buffer, internal.len); -#endif /* #ifdef RAWNET_DEBUG_PKTDUMP */ - - if (len & 1) { - ++len; - } - - *plen = len; - - /* we don't decide if this frame fits the needs; - * by setting all zero, we let ethernet.c do the work - * for us - */ - *phashed = *phash_index = *pbroadcast = *pcorrect_mac = *pcrc_error = 0; - - /* this frame has been received correctly */ - *prx_ok = 1; - - return 1; - } - - return 0; -} - char *rawnet_arch_get_standard_interface(void) { char *dev, errbuf[PCAP_ERRBUF_SIZE]; @@ -575,19 +547,28 @@ extern int rawnet_arch_get_mtu(void) { extern int rawnet_arch_get_mac(uint8_t mac[6]) { + int rv = -1; + LPADAPTER outp = NULL; char buffer[sizeof(PACKET_OID_DATA) + 6]; - PPACKET_OID_DATA data = (PPACKET_OID_DATA)data; + PPACKET_OID_DATA data = (PPACKET_OID_DATA)buffer; + + if (!packet_library) return -1; + /* 802.5 = token ring, 802.3 = wired ethernet */ data->Oid = OID_802_3_CURRENT_ADDRESS; // OID_802_3_CURRENT_ADDRESS ? OID_802_3_PERMANENT_ADDRESS ? data->Length = 6; - if (PacketRequest(EthernetPcapFP, FALSE, data)) { - memcpy(mac, data->Data, 6); - return 0; - } - return -1; + outp = p_PacketOpenAdapter(rawnet_device_name); + if (!outp || outp->hFile == INVALID_HANDLE_VALUE) return -1; + + if (p_PacketRequest(outp, FALSE, data)) { + memcpy(mac, data->Data, 6); + rv = 0; + } + p_PacketCloseAdapter(outp); + return rv; } int rawnet_arch_status(void) { diff --git a/src/scc_socket_driver.c b/src/scc_socket_driver.c index bf43bfd..af038ce 100644 --- a/src/scc_socket_driver.c +++ b/src/scc_socket_driver.c @@ -20,7 +20,7 @@ extern Scc scc_stat[2]; extern int g_serial_modem[]; -#if !(defined _MSC_VER || defined __CYGWIN__) +#if !(defined _WIN32) extern int h_errno; #else #define socklen_t int diff --git a/src/sim65816.c b/src/sim65816.c index bad7ac2..db78638 100644 --- a/src/sim65816.c +++ b/src/sim65816.c @@ -33,6 +33,20 @@ extern char g_config_gsplus_screenshot_dir[]; extern void get_cwd(LPTSTR buffer, int size); #endif +#ifndef ENABLE_DEBUGGER +int g_dbg_step = 0; +int g_dbg_enable_port = 0; + +void debug_server_poll(void) { } +int debug_events_waiting(void) { return 0; } +void debug_handle_event(void) { } +void debug_init(void) { } +void do_go_debug(void) { } + +#endif + + + #define PC_LOG_LEN (8*1024) int g_speed_fast; // OG Expose fast parameter diff --git a/src/tfe/CMakeLists.txt b/src/tfe/CMakeLists.txt index 80df2c3..ea0c249 100644 --- a/src/tfe/CMakeLists.txt +++ b/src/tfe/CMakeLists.txt @@ -4,3 +4,7 @@ add_library(tfe tfe.c tfearch.c tfesupp.c) target_compile_definitions(tfe PUBLIC HAVE_TFE) +if(WIN32) + target_include_directories(tfe PRIVATE ../rawnet/include) + target_link_libraries(tfe ws2_32) # winsock2 +endif() \ No newline at end of file From b353a7ff22c4ddd8d2813e438254f3cc2da5b0bc Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Wed, 2 Jan 2019 19:53:34 -0500 Subject: [PATCH 029/186] cleanup headers a bit --- CMakeLists.txt | 22 + .../dirent-win32.h => include/msvc/dirent.h | 0 include/npcap/Packet32.h | 421 ++++++ include/npcap/pcap-bpf.h | 45 + include/npcap/pcap-namedb.h | 40 + include/npcap/pcap-stdinc.h | 126 ++ include/npcap/pcap.h | 43 + include/npcap/pcap/bluetooth.h | 55 + include/npcap/pcap/bpf.h | 280 ++++ include/npcap/pcap/can_socketcan.h | 54 + include/npcap/pcap/dlt.h | 1340 +++++++++++++++++ include/npcap/pcap/export-defs.h | 108 ++ include/npcap/pcap/ipnet.h | 43 + include/npcap/pcap/namedb.h | 85 ++ include/npcap/pcap/nflog.h | 92 ++ include/npcap/pcap/pcap.h | 538 +++++++ include/npcap/pcap/sll.h | 129 ++ include/npcap/pcap/usb.h | 141 ++ include/npcap/pcap/vlan.h | 44 + include/npcap/remote-ext.h | 467 ++++++ src/CMakeLists.txt | 30 +- src/arch/win32/bittypes.h | 86 -- src/arch/win32/bpf.h | 523 ------- src/arch/win32/ip6_misc.h | 159 -- src/arch/win32/pcap-stdinc.h | 44 - src/arch/win32/pcap.h | 273 ---- src/atbridge/CMakeLists.txt | 1 - src/atbridge/pcap_delay.h | 6 - src/config.c | 10 +- src/rawnet/CMakeLists.txt | 1 - 30 files changed, 4084 insertions(+), 1122 deletions(-) create mode 100644 CMakeLists.txt rename src/arch/win32/dirent-win32.h => include/msvc/dirent.h (100%) create mode 100644 include/npcap/Packet32.h create mode 100644 include/npcap/pcap-bpf.h create mode 100644 include/npcap/pcap-namedb.h create mode 100644 include/npcap/pcap-stdinc.h create mode 100644 include/npcap/pcap.h create mode 100644 include/npcap/pcap/bluetooth.h create mode 100644 include/npcap/pcap/bpf.h create mode 100644 include/npcap/pcap/can_socketcan.h create mode 100644 include/npcap/pcap/dlt.h create mode 100644 include/npcap/pcap/export-defs.h create mode 100644 include/npcap/pcap/ipnet.h create mode 100644 include/npcap/pcap/namedb.h create mode 100644 include/npcap/pcap/nflog.h create mode 100644 include/npcap/pcap/pcap.h create mode 100644 include/npcap/pcap/sll.h create mode 100644 include/npcap/pcap/usb.h create mode 100644 include/npcap/pcap/vlan.h create mode 100644 include/npcap/remote-ext.h delete mode 100644 src/arch/win32/bittypes.h delete mode 100644 src/arch/win32/bpf.h delete mode 100644 src/arch/win32/ip6_misc.h delete mode 100644 src/arch/win32/pcap-stdinc.h delete mode 100644 src/arch/win32/pcap.h diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..0823646 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,22 @@ +cmake_minimum_required(VERSION 3.1) +project(gsplus VERSION 0.14) + + +if(CYGWIN OR MSYS) + set(WIN32 1) + add_definitions(-DWIN32 -D_WIN32) +endif() + +# msys/cygwin/mingw32 add dirent.h, etc. +# Visual C++ does not. +if(MSVC) + include_directories(include/msvc) + add_definitions(-D_CRT_SECURE_NO_WARNINGS) +endif() + +# add pcap headers for win32. assume os/x, linux, etc, already have them. +if(WIN32) + include_directories(include/npcap) +endif() + +add_subdirectory(src bin) \ No newline at end of file diff --git a/src/arch/win32/dirent-win32.h b/include/msvc/dirent.h similarity index 100% rename from src/arch/win32/dirent-win32.h rename to include/msvc/dirent.h diff --git a/include/npcap/Packet32.h b/include/npcap/Packet32.h new file mode 100644 index 0000000..1e2edc7 --- /dev/null +++ b/include/npcap/Packet32.h @@ -0,0 +1,421 @@ +/***********************IMPORTANT NPCAP LICENSE TERMS*********************** + * * + * Npcap is a Windows packet sniffing driver and library and is copyright * + * (c) 2013-2016 by Insecure.Com LLC ("The Nmap Project"). All rights * + * reserved. * + * * + * Even though Npcap source code is publicly available for review, it is * + * not open source software and my not be redistributed or incorporated * + * into other software without special permission from the Nmap Project. * + * We fund the Npcap project by selling a commercial license which allows * + * companies to redistribute Npcap with their products and also provides * + * for support, warranty, and indemnification rights. For details on * + * obtaining such a license, please contact: * + * * + * sales@nmap.com * + * * + * Free and open source software producers are also welcome to contact us * + * for redistribution requests. However, we normally recommend that such * + * authors instead ask your users to download and install Npcap * + * themselves. * + * * + * Since the Npcap source code is available for download and review, * + * users sometimes contribute code patches to fix bugs or add new * + * features. By sending these changes to the Nmap Project (including * + * through direct email or our mailing lists or submitting pull requests * + * through our source code repository), it is understood unless you * + * specify otherwise that you are offering the Nmap Project the * + * unlimited, non-exclusive right to reuse, modify, and relicence your * + * code contribution so that we may (but are not obligated to) * + * incorporate it into Npcap. If you wish to specify special license * + * conditions or restrictions on your contributions, just say so when you * + * send them. * + * * + * This software is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * + * * + * Other copyright notices and attribution may appear below this license * + * header. We have kept those for attribution purposes, but any license * + * terms granted by those notices apply only to their original work, and * + * not to any changes made by the Nmap Project or to this entire file. * + * * + * This header summarizes a few important aspects of the Npcap license, * + * but is not a substitute for the full Npcap license agreement, which is * + * in the LICENSE file included with Npcap and also available at * + * https://github.com/nmap/npcap/blob/master/LICENSE. * + * * + ***************************************************************************/ +/* + * Copyright (c) 1999 - 2005 NetGroup, Politecnico di Torino (Italy) + * Copyright (c) 2005 - 2007 CACE Technologies, Davis (California) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Politecnico di Torino, CACE Technologies + * nor the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/** @ingroup packetapi + * @{ + */ + +/** @defgroup packet32h Packet.dll definitions and data structures + * Packet32.h contains the data structures and the definitions used by packet.dll. + * The file is used both by the Win9x and the WinNTx versions of packet.dll, and can be included + * by the applications that use the functions of this library + * @{ + */ + +#ifndef __PACKET32 +#define __PACKET32 + +#include + +#ifdef HAVE_AIRPCAP_API +#include +#else +#if !defined(AIRPCAP_HANDLE__EAE405F5_0171_9592_B3C2_C19EC426AD34__DEFINED_) +#define AIRPCAP_HANDLE__EAE405F5_0171_9592_B3C2_C19EC426AD34__DEFINED_ +typedef struct _AirpcapHandle* PAirpcapHandle; +#endif /* AIRPCAP_HANDLE__EAE405F5_0171_9592_B3C2_C19EC426AD34__DEFINED_ */ +#endif /* HAVE_AIRPCAP_API */ + +#ifdef HAVE_DAG_API +#include +#endif /* HAVE_DAG_API */ + +// Libpcap/wpcap recognizes this macro and knows Npcap Packet API is provided for compilation. +#define HAVE_NPCAP_PACKET_API + +// Working modes +#define PACKET_MODE_CAPT 0x0 ///< Capture mode +#define PACKET_MODE_STAT 0x1 ///< Statistical mode +#define PACKET_MODE_MON 0x2 ///< Monitoring mode +#define PACKET_MODE_DUMP 0x10 ///< Dump mode +#define PACKET_MODE_STAT_DUMP MODE_DUMP | MODE_STAT ///< Statistical dump Mode + + +/// Alignment macro. Defines the alignment size. +#define Packet_ALIGNMENT sizeof(int) +/// Alignment macro. Rounds up to the next even multiple of Packet_ALIGNMENT. +#define Packet_WORDALIGN(x) (((x)+(Packet_ALIGNMENT-1))&~(Packet_ALIGNMENT-1)) + +#define NdisMediumNull -1 ///< Custom linktype: NDIS doesn't provide an equivalent +#define NdisMediumCHDLC -2 ///< Custom linktype: NDIS doesn't provide an equivalent +#define NdisMediumPPPSerial -3 ///< Custom linktype: NDIS doesn't provide an equivalent +#define NdisMediumBare80211 -4 ///< Custom linktype: NDIS doesn't provide an equivalent +#define NdisMediumRadio80211 -5 ///< Custom linktype: NDIS doesn't provide an equivalent +#define NdisMediumPpi -6 ///< Custom linktype: NDIS doesn't provide an equivalent + +// Loopback behaviour definitions +#define NPF_DISABLE_LOOPBACK 1 ///< Drop the packets sent by the NPF driver +#define NPF_ENABLE_LOOPBACK 2 ///< Capture the packets sent by the NPF driver + +/*! + \brief Network type structure. + + This structure is used by the PacketGetNetType() function to return information on the current adapter's type and speed. +*/ +typedef struct NetType +{ + UINT LinkType; ///< The MAC of the current network adapter (see function PacketGetNetType() for more information) + ULONGLONG LinkSpeed; ///< The speed of the network in bits per second +}NetType; + + +//some definitions stolen from libpcap + +#ifndef BPF_MAJOR_VERSION + +/*! + \brief A BPF pseudo-assembly program. + + The program will be injected in the kernel by the PacketSetBPF() function and applied to every incoming packet. +*/ +struct bpf_program +{ + UINT bf_len; ///< Indicates the number of instructions of the program, i.e. the number of struct bpf_insn that will follow. + struct bpf_insn* bf_insns; ///< A pointer to the first instruction of the program. +}; + +/*! + \brief A single BPF pseudo-instruction. + + bpf_insn contains a single instruction for the BPF register-machine. It is used to send a filter program to the driver. +*/ +struct bpf_insn +{ + USHORT code; ///< Instruction type and addressing mode. + UCHAR jt; ///< Jump if true + UCHAR jf; ///< Jump if false + int k; ///< Generic field used for various purposes. +}; + +/*! + \brief Structure that contains a couple of statistics values on the current capture. + + It is used by packet.dll to return statistics about a capture session. +*/ +struct bpf_stat +{ + UINT bs_recv; ///< Number of packets that the driver received from the network adapter + ///< from the beginning of the current capture. This value includes the packets + ///< lost by the driver. + UINT bs_drop; ///< number of packets that the driver lost from the beginning of a capture. + ///< Basically, a packet is lost when the the buffer of the driver is full. + ///< In this situation the packet cannot be stored and the driver rejects it. + UINT ps_ifdrop; ///< drops by interface. XXX not yet supported + UINT bs_capt; ///< number of packets that pass the filter, find place in the kernel buffer and + ///< thus reach the application. +}; + +/*! + \brief Packet header. + + This structure defines the header associated with every packet delivered to the application. +*/ +struct bpf_hdr +{ + struct timeval bh_tstamp; ///< The timestamp associated with the captured packet. + ///< It is stored in a TimeVal structure. + UINT bh_caplen; ///< Length of captured portion. The captured portion can be different + ///< from the original packet, because it is possible (with a proper filter) + ///< to instruct the driver to capture only a portion of the packets. + UINT bh_datalen; ///< Original length of packet + USHORT bh_hdrlen; ///< Length of bpf header (this struct plus alignment padding). In some cases, + ///< a padding could be added between the end of this structure and the packet + ///< data for performance reasons. This filed can be used to retrieve the actual data + ///< of the packet. +}; + +/*! + \brief Dump packet header. + + This structure defines the header associated with the packets in a buffer to be used with PacketSendPackets(). + It is simpler than the bpf_hdr, because it corresponds to the header associated by WinPcap and libpcap to a + packet in a dump file. This makes straightforward sending WinPcap dump files to the network. +*/ +struct dump_bpf_hdr +{ + struct timeval ts; ///< Time stamp of the packet + UINT caplen; ///< Length of captured portion. The captured portion can smaller than the + ///< the original packet, because it is possible (with a proper filter) to + ///< instruct the driver to capture only a portion of the packets. + UINT len; ///< Length of the original packet (off wire). +}; + + +#endif + +struct bpf_stat; + +#define DOSNAMEPREFIX TEXT("Packet_") ///< Prefix added to the adapters device names to create the WinPcap devices +#define MAX_LINK_NAME_LENGTH 64 //< Maximum length of the devices symbolic links +#define NMAX_PACKET 65535 + +/*! + \brief Addresses of a network adapter. + + This structure is used by the PacketGetNetInfoEx() function to return the IP addresses associated with + an adapter. +*/ +typedef struct npf_if_addr +{ + struct sockaddr_storage IPAddress; ///< IP address. + struct sockaddr_storage SubnetMask; ///< Netmask for that address. + struct sockaddr_storage Broadcast; ///< Broadcast address. +}npf_if_addr; + + +#define ADAPTER_NAME_LENGTH 256 + 12 ///< Maximum length for the name of an adapter. The value is the same used by the IP Helper API. +#define ADAPTER_DESC_LENGTH 128 ///< Maximum length for the description of an adapter. The value is the same used by the IP Helper API. +#define MAX_MAC_ADDR_LENGTH 8 ///< Maximum length for the link layer address of an adapter. The value is the same used by the IP Helper API. +#define MAX_NETWORK_ADDRESSES 16 ///< Maximum length for the link layer address of an adapter. The value is the same used by the IP Helper API. + + +typedef struct WAN_ADAPTER_INT WAN_ADAPTER; ///< Describes an opened wan (dialup, VPN...) network adapter using the NetMon API +typedef WAN_ADAPTER* PWAN_ADAPTER; ///< Describes an opened wan (dialup, VPN...) network adapter using the NetMon API + +#define INFO_FLAG_NDIS_ADAPTER 0 ///< Flag for ADAPTER_INFO: this is a traditional ndis adapter +#define INFO_FLAG_NDISWAN_ADAPTER 1 ///< Flag for ADAPTER_INFO: this is a NdisWan adapter, and it's managed by WANPACKET +#define INFO_FLAG_DAG_CARD 2 ///< Flag for ADAPTER_INFO: this is a DAG card +#define INFO_FLAG_DAG_FILE 6 ///< Flag for ADAPTER_INFO: this is a DAG file +#define INFO_FLAG_DONT_EXPORT 8 ///< Flag for ADAPTER_INFO: when this flag is set, the adapter will not be listed or openend by winpcap. This allows to prevent exporting broken network adapters, like for example FireWire ones. +#define INFO_FLAG_AIRPCAP_CARD 16 ///< Flag for ADAPTER_INFO: this is an airpcap card +#define INFO_FLAG_NPFIM_DEVICE 32 + +/*! + \brief Describes an opened network adapter. + + This structure is the most important for the functioning of packet.dll, but the great part of its fields + should be ignored by the user, since the library offers functions that avoid to cope with low-level parameters +*/ +typedef struct _ADAPTER +{ + HANDLE hFile; ///< \internal Handle to an open instance of the NPF driver. + CHAR SymbolicLink[MAX_LINK_NAME_LENGTH]; ///< \internal A string containing the name of the network adapter currently opened. + int NumWrites; ///< \internal Number of times a packets written on this adapter will be repeated + ///< on the wire. + HANDLE ReadEvent; ///< A notification event associated with the read calls on the adapter. + ///< It can be passed to standard Win32 functions (like WaitForSingleObject + ///< or WaitForMultipleObjects) to wait until the driver's buffer contains some + ///< data. It is particularly useful in GUI applications that need to wait + ///< concurrently on several events. In Windows NT/2000 the PacketSetMinToCopy() + ///< function can be used to define the minimum amount of data in the kernel buffer + ///< that will cause the event to be signalled. + + UINT ReadTimeOut; ///< \internal The amount of time after which a read on the driver will be released and + ///< ReadEvent will be signaled, also if no packets were captured + CHAR Name[ADAPTER_NAME_LENGTH]; + PWAN_ADAPTER pWanAdapter; + UINT Flags; ///< Adapter's flags. Tell if this adapter must be treated in a different way, using the Netmon API or the dagc API. + +#ifdef HAVE_AIRPCAP_API + PAirpcapHandle AirpcapAd; +#endif // HAVE_AIRPCAP_API + +#ifdef HAVE_NPFIM_API + void* NpfImHandle; +#endif // HAVE_NPFIM_API + +#ifdef HAVE_DAG_API + dagc_t* pDagCard; ///< Pointer to the dagc API adapter descriptor for this adapter + PCHAR DagBuffer; ///< Pointer to the buffer with the packets that is received from the DAG card + struct timeval DagReadTimeout; ///< Read timeout. The dagc API requires a timeval structure + unsigned DagFcsLen; ///< Length of the frame check sequence attached to any packet by the card. Obtained from the registry + DWORD DagFastProcess; ///< True if the user requests fast capture processing on this card. Higher level applications can use this value to provide a faster but possibly unprecise capture (for example, libpcap doesn't convert the timestamps). +#endif // HAVE_DAG_API +} ADAPTER, * LPADAPTER; + +/*! + \brief Structure that contains a group of packets coming from the driver. + + This structure defines the header associated with every packet delivered to the application. +*/ +typedef struct _PACKET +{ + HANDLE hEvent; ///< \deprecated Still present for compatibility with old applications. + OVERLAPPED OverLapped; ///< \deprecated Still present for compatibility with old applications. + PVOID Buffer; ///< Buffer with containing the packets. See the PacketReceivePacket() for + ///< details about the organization of the data in this buffer + UINT Length; ///< Length of the buffer + DWORD ulBytesReceived; ///< Number of valid bytes present in the buffer, i.e. amount of data + ///< received by the last call to PacketReceivePacket() + BOOLEAN bIoComplete; ///< \deprecated Still present for compatibility with old applications. +} PACKET, * LPPACKET; + +/*! + \brief Structure containing an OID request. + + It is used by the PacketRequest() function to send an OID to the interface card driver. + It can be used, for example, to retrieve the status of the error counters on the adapter, its MAC address, + the list of the multicast groups defined on it, and so on. +*/ +struct _PACKET_OID_DATA +{ + ULONG Oid; ///< OID code. See the Microsoft DDK documentation or the file ntddndis.h + ///< for a complete list of valid codes. + ULONG Length; ///< Length of the data field + UCHAR Data[1]; ///< variable-lenght field that contains the information passed to or received + ///< from the adapter. +}; +typedef struct _PACKET_OID_DATA PACKET_OID_DATA, * PPACKET_OID_DATA; + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * @} + */ + + /* + BOOLEAN QueryWinPcapRegistryStringA(CHAR *SubKeyName, + CHAR *Value, + UINT *pValueLen, + CHAR *DefaultVal); + BOOLEAN QueryWinPcapRegistryStringW(WCHAR *SubKeyName, + WCHAR *Value, + UINT *pValueLen, + WCHAR *DefaultVal); + */ + + //--------------------------------------------------------------------------- + // EXPORTED FUNCTIONS + //--------------------------------------------------------------------------- + + PCHAR PacketGetVersion(); + PCHAR PacketGetDriverVersion(); + PCHAR PacketGetDriverName(); + BOOLEAN PacketSetMinToCopy(LPADAPTER AdapterObject, int nbytes); + BOOLEAN PacketSetNumWrites(LPADAPTER AdapterObject, int nwrites); + BOOLEAN PacketSetMode(LPADAPTER AdapterObject, int mode); + BOOLEAN PacketSetReadTimeout(LPADAPTER AdapterObject, int timeout); + BOOLEAN PacketSetBpf(LPADAPTER AdapterObject, struct bpf_program* fp); + BOOLEAN PacketSetLoopbackBehavior(LPADAPTER AdapterObject, UINT LoopbackBehavior); + INT PacketSetSnapLen(LPADAPTER AdapterObject, int snaplen); + BOOLEAN PacketGetStats(LPADAPTER AdapterObject, struct bpf_stat* s); + BOOLEAN PacketGetStatsEx(LPADAPTER AdapterObject, struct bpf_stat* s); + BOOLEAN PacketSetBuff(LPADAPTER AdapterObject, int dim); + BOOLEAN PacketGetNetType(LPADAPTER AdapterObject, NetType* type); + BOOLEAN PacketIsLoopbackAdapter(PCHAR AdapterName); + int PacketIsMonitorModeSupported(PCHAR AdapterName); + int PacketSetMonitorMode(PCHAR AdapterName, int mode); + int PacketGetMonitorMode(PCHAR AdapterName); + LPADAPTER PacketOpenAdapter(PCHAR AdapterName); + BOOLEAN PacketSendPacket(LPADAPTER AdapterObject, LPPACKET pPacket, BOOLEAN Sync); + INT PacketSendPackets(LPADAPTER AdapterObject, PVOID PacketBuff, ULONG Size, BOOLEAN Sync); + LPPACKET PacketAllocatePacket(void); + VOID PacketInitPacket(LPPACKET lpPacket, PVOID Buffer, UINT Length); + VOID PacketFreePacket(LPPACKET lpPacket); + BOOLEAN PacketReceivePacket(LPADAPTER AdapterObject, LPPACKET lpPacket, BOOLEAN Sync); + BOOLEAN PacketSetHwFilter(LPADAPTER AdapterObject, ULONG Filter); + BOOLEAN PacketGetAdapterNames(PCHAR pStr, PULONG BufferSize); + BOOLEAN PacketGetNetInfoEx(PCHAR AdapterName, npf_if_addr* buffer, PLONG NEntries); + BOOLEAN PacketRequest(LPADAPTER AdapterObject, BOOLEAN Set, PPACKET_OID_DATA OidData); + HANDLE PacketGetReadEvent(LPADAPTER AdapterObject); + BOOLEAN PacketSetDumpName(LPADAPTER AdapterObject, void* name, int len); + BOOLEAN PacketSetDumpLimits(LPADAPTER AdapterObject, UINT maxfilesize, UINT maxnpacks); + BOOLEAN PacketIsDumpEnded(LPADAPTER AdapterObject, BOOLEAN sync); + BOOL PacketStopDriver(); + BOOL PacketStopDriver60(); + VOID PacketCloseAdapter(LPADAPTER lpAdapter); + BOOLEAN PacketStartOem(PCHAR errorString, UINT errorStringLength); + BOOLEAN PacketStartOemEx(PCHAR errorString, UINT errorStringLength, ULONG flags); + PAirpcapHandle PacketGetAirPcapHandle(LPADAPTER AdapterObject); + + // + // Used by PacketStartOemEx + // +#define PACKET_START_OEM_NO_NETMON 0x00000001 + +#ifdef __cplusplus +} +#endif + +#endif //__PACKET32 diff --git a/include/npcap/pcap-bpf.h b/include/npcap/pcap-bpf.h new file mode 100644 index 0000000..ebb64c3 --- /dev/null +++ b/include/npcap/pcap-bpf.h @@ -0,0 +1,45 @@ +/*- + * Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from the Stanford/CMU enet packet filter, + * (net/enet.c) distributed as part of 4.3BSD, and code contributed + * to Berkeley by Steven McCanne and Van Jacobson both of Lawrence + * Berkeley Laboratory. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * For backwards compatibility. + * + * Note to OS vendors: do NOT get rid of this file! Some applications + * might expect to be able to include . + */ +#include diff --git a/include/npcap/pcap-namedb.h b/include/npcap/pcap-namedb.h new file mode 100644 index 0000000..d5908c9 --- /dev/null +++ b/include/npcap/pcap-namedb.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 1994, 1996 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the Computer Systems + * Engineering Group at Lawrence Berkeley Laboratory. + * 4. Neither the name of the University nor of the Laboratory may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * For backwards compatibility. + * + * Note to OS vendors: do NOT get rid of this file! Some applications + * might expect to be able to include . + */ +#include diff --git a/include/npcap/pcap-stdinc.h b/include/npcap/pcap-stdinc.h new file mode 100644 index 0000000..a1be680 --- /dev/null +++ b/include/npcap/pcap-stdinc.h @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2002 - 2005 NetGroup, Politecnico di Torino (Italy) + * Copyright (c) 2005 - 2009 CACE Technologies, Inc. Davis (California) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Politecnico di Torino nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Copyright (C) 1999 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef pcap_stdinc_h +#define pcap_stdinc_h + +/* + * Avoids a compiler warning in case this was already defined + * (someone defined _WINSOCKAPI_ when including 'windows.h', in order + * to prevent it from including 'winsock.h') + */ +#ifdef _WINSOCKAPI_ +#undef _WINSOCKAPI_ +#endif + +#include +#include +#include +#include + +#include + +#if defined(_MSC_VER) + /* + * MSVC. + */ + #if _MSC_VER >= 1800 + /* + * VS 2013 or newer; we have . + */ + #include + + #define u_int8_t uint8_t + #define u_int16_t uint16_t + #define u_int32_t uint32_t + #define u_int64_t uint64_t + #else + /* + * Earlier VS; we have to define this stuff ourselves. + */ + #ifndef HAVE_U_INT8_T + typedef unsigned char u_int8_t; + typedef signed char int8_t; + #endif + + #ifndef HAVE_U_INT16_T + typedef unsigned short u_int16_t; + typedef signed short int16_t; + #endif + + #ifndef HAVE_U_INT32_T + typedef unsigned int u_int32_t; + typedef signed int int32_t; + #endif + + #ifndef HAVE_U_INT64_T + #ifdef _MSC_EXTENSIONS + typedef unsigned _int64 u_int64_t; + typedef _int64 int64_t; + #else /* _MSC_EXTENSIONS */ + typedef unsigned long long u_int64_t; + typedef long long int64_t; + #endif + #endif + #endif +#elif defined(__MINGW32__) + #include +#endif + +#endif /* pcap_stdinc_h */ diff --git a/include/npcap/pcap.h b/include/npcap/pcap.h new file mode 100644 index 0000000..174e32d --- /dev/null +++ b/include/npcap/pcap.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 1993, 1994, 1995, 1996, 1997 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the Computer Systems + * Engineering Group at Lawrence Berkeley Laboratory. + * 4. Neither the name of the University nor of the Laboratory may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * For backwards compatibility. + * + * Note to OS vendors: do NOT get rid of this file! Many applications + * expect to be able to include , and at least some of them + * go through contortions in their configure scripts to try to detect + * OSes that have "helpfully" moved pcap.h to without + * leaving behind a file. + */ +#include diff --git a/include/npcap/pcap/bluetooth.h b/include/npcap/pcap/bluetooth.h new file mode 100644 index 0000000..c5f378a --- /dev/null +++ b/include/npcap/pcap/bluetooth.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2006 Paolo Abeni (Italy) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * bluetooth data struct + * By Paolo Abeni + */ + +#ifndef lib_pcap_bluetooth_h +#define lib_pcap_bluetooth_h + +/* + * Header prepended libpcap to each bluetooth h4 frame, + * fields are in network byte order + */ +typedef struct _pcap_bluetooth_h4_header { + u_int32_t direction; /* if first bit is set direction is incoming */ +} pcap_bluetooth_h4_header; + +/* + * Header prepended libpcap to each bluetooth linux monitor frame, + * fields are in network byte order + */ +typedef struct _pcap_bluetooth_linux_monitor_header { + u_int16_t adapter_id; + u_int16_t opcode; +} pcap_bluetooth_linux_monitor_header; + + +#endif diff --git a/include/npcap/pcap/bpf.h b/include/npcap/pcap/bpf.h new file mode 100644 index 0000000..78ad890 --- /dev/null +++ b/include/npcap/pcap/bpf.h @@ -0,0 +1,280 @@ +/*- + * Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from the Stanford/CMU enet packet filter, + * (net/enet.c) distributed as part of 4.3BSD, and code contributed + * to Berkeley by Steven McCanne and Van Jacobson both of Lawrence + * Berkeley Laboratory. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)bpf.h 7.1 (Berkeley) 5/7/91 + */ + +/* + * This is libpcap's cut-down version of bpf.h; it includes only + * the stuff needed for the code generator and the userland BPF + * interpreter, and the libpcap APIs for setting filters, etc.. + * + * "pcap-bpf.c" will include the native OS version, as it deals with + * the OS's BPF implementation. + * + * At least two programs found by Google Code Search explicitly includes + * (even though / includes it for you), + * so moving that stuff to would break the build for some + * programs. + */ + +/* + * If we've already included , don't re-define this stuff. + * We assume BSD-style multiple-include protection in , + * which is true of all but the oldest versions of FreeBSD and NetBSD, + * or Tru64 UNIX-style multiple-include protection (or, at least, + * Tru64 UNIX 5.x-style; I don't have earlier versions available to check), + * or AIX-style multiple-include protection (or, at least, AIX 5.x-style; + * I don't have earlier versions available to check), or QNX-style + * multiple-include protection (as per GitHub pull request #394). + * + * We do not check for BPF_MAJOR_VERSION, as that's defined by + * , which is directly or indirectly included in some + * programs that also include pcap.h, and doesn't + * define stuff we need. + * + * This also provides our own multiple-include protection. + */ +#if !defined(_NET_BPF_H_) && !defined(_NET_BPF_H_INCLUDED) && !defined(_BPF_H_) && !defined(_H_BPF) && !defined(lib_pcap_bpf_h) +#define lib_pcap_bpf_h + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* BSD style release date */ +#define BPF_RELEASE 199606 + +#ifdef MSDOS /* must be 32-bit */ +typedef long bpf_int32; +typedef unsigned long bpf_u_int32; +#else +typedef int bpf_int32; +typedef u_int bpf_u_int32; +#endif + +/* + * Alignment macros. BPF_WORDALIGN rounds up to the next + * even multiple of BPF_ALIGNMENT. + * + * Tcpdump's print-pflog.c uses this, so we define it here. + */ +#ifndef __NetBSD__ +#define BPF_ALIGNMENT sizeof(bpf_int32) +#else +#define BPF_ALIGNMENT sizeof(long) +#endif +#define BPF_WORDALIGN(x) (((x)+(BPF_ALIGNMENT-1))&~(BPF_ALIGNMENT-1)) + +/* + * Structure for "pcap_compile()", "pcap_setfilter()", etc.. + */ +struct bpf_program { + u_int bf_len; + struct bpf_insn *bf_insns; +}; + +#include + +/* + * The instruction encodings. + * + * Please inform tcpdump-workers@lists.tcpdump.org if you use any + * of the reserved values, so that we can note that they're used + * (and perhaps implement it in the reference BPF implementation + * and encourage its implementation elsewhere). + */ + +/* + * The upper 8 bits of the opcode aren't used. BSD/OS used 0x8000. + */ + +/* instruction classes */ +#define BPF_CLASS(code) ((code) & 0x07) +#define BPF_LD 0x00 +#define BPF_LDX 0x01 +#define BPF_ST 0x02 +#define BPF_STX 0x03 +#define BPF_ALU 0x04 +#define BPF_JMP 0x05 +#define BPF_RET 0x06 +#define BPF_MISC 0x07 + +/* ld/ldx fields */ +#define BPF_SIZE(code) ((code) & 0x18) +#define BPF_W 0x00 +#define BPF_H 0x08 +#define BPF_B 0x10 +/* 0x18 reserved; used by BSD/OS */ +#define BPF_MODE(code) ((code) & 0xe0) +#define BPF_IMM 0x00 +#define BPF_ABS 0x20 +#define BPF_IND 0x40 +#define BPF_MEM 0x60 +#define BPF_LEN 0x80 +#define BPF_MSH 0xa0 +/* 0xc0 reserved; used by BSD/OS */ +/* 0xe0 reserved; used by BSD/OS */ + +/* alu/jmp fields */ +#define BPF_OP(code) ((code) & 0xf0) +#define BPF_ADD 0x00 +#define BPF_SUB 0x10 +#define BPF_MUL 0x20 +#define BPF_DIV 0x30 +#define BPF_OR 0x40 +#define BPF_AND 0x50 +#define BPF_LSH 0x60 +#define BPF_RSH 0x70 +#define BPF_NEG 0x80 +#define BPF_MOD 0x90 +#define BPF_XOR 0xa0 +/* 0xb0 reserved */ +/* 0xc0 reserved */ +/* 0xd0 reserved */ +/* 0xe0 reserved */ +/* 0xf0 reserved */ + +#define BPF_JA 0x00 +#define BPF_JEQ 0x10 +#define BPF_JGT 0x20 +#define BPF_JGE 0x30 +#define BPF_JSET 0x40 +/* 0x50 reserved; used on BSD/OS */ +/* 0x60 reserved */ +/* 0x70 reserved */ +/* 0x80 reserved */ +/* 0x90 reserved */ +/* 0xa0 reserved */ +/* 0xb0 reserved */ +/* 0xc0 reserved */ +/* 0xd0 reserved */ +/* 0xe0 reserved */ +/* 0xf0 reserved */ +#define BPF_SRC(code) ((code) & 0x08) +#define BPF_K 0x00 +#define BPF_X 0x08 + +/* ret - BPF_K and BPF_X also apply */ +#define BPF_RVAL(code) ((code) & 0x18) +#define BPF_A 0x10 +/* 0x18 reserved */ + +/* misc */ +#define BPF_MISCOP(code) ((code) & 0xf8) +#define BPF_TAX 0x00 +/* 0x08 reserved */ +/* 0x10 reserved */ +/* 0x18 reserved */ +/* #define BPF_COP 0x20 NetBSD "coprocessor" extensions */ +/* 0x28 reserved */ +/* 0x30 reserved */ +/* 0x38 reserved */ +/* #define BPF_COPX 0x40 NetBSD "coprocessor" extensions */ +/* also used on BSD/OS */ +/* 0x48 reserved */ +/* 0x50 reserved */ +/* 0x58 reserved */ +/* 0x60 reserved */ +/* 0x68 reserved */ +/* 0x70 reserved */ +/* 0x78 reserved */ +#define BPF_TXA 0x80 +/* 0x88 reserved */ +/* 0x90 reserved */ +/* 0x98 reserved */ +/* 0xa0 reserved */ +/* 0xa8 reserved */ +/* 0xb0 reserved */ +/* 0xb8 reserved */ +/* 0xc0 reserved; used on BSD/OS */ +/* 0xc8 reserved */ +/* 0xd0 reserved */ +/* 0xd8 reserved */ +/* 0xe0 reserved */ +/* 0xe8 reserved */ +/* 0xf0 reserved */ +/* 0xf8 reserved */ + +/* + * The instruction data structure. + */ +struct bpf_insn { + u_short code; + u_char jt; + u_char jf; + bpf_u_int32 k; +}; + +/* + * Auxiliary data, for use when interpreting a filter intended for the + * Linux kernel when the kernel rejects the filter (requiring us to + * run it in userland). It contains VLAN tag information. + */ +struct bpf_aux_data { + u_short vlan_tag_present; + u_short vlan_tag; +}; + +/* + * Macros for insn array initializers. + */ +#define BPF_STMT(code, k) { (u_short)(code), 0, 0, k } +#define BPF_JUMP(code, k, jt, jf) { (u_short)(code), jt, jf, k } + +#if __STDC__ || defined(__cplusplus) +PCAP_API int bpf_validate(const struct bpf_insn *, int); +PCAP_API u_int bpf_filter(const struct bpf_insn *, const u_char *, u_int, u_int); +extern u_int bpf_filter_with_aux_data(const struct bpf_insn *, const u_char *, u_int, u_int, const struct bpf_aux_data *); +#else +PCAP_API int bpf_validate(); +PCAP_API u_int bpf_filter(); +extern u_int bpf_filter_with_aux_data(); +#endif + +/* + * Number of scratch memory words (for BPF_LD|BPF_MEM and BPF_ST). + */ +#define BPF_MEMWORDS 16 + +#ifdef __cplusplus +} +#endif + +#endif /* !defined(_NET_BPF_H_) && !defined(_BPF_H_) && !defined(_H_BPF) && !defined(lib_pcap_bpf_h) */ diff --git a/include/npcap/pcap/can_socketcan.h b/include/npcap/pcap/can_socketcan.h new file mode 100644 index 0000000..68d2a13 --- /dev/null +++ b/include/npcap/pcap/can_socketcan.h @@ -0,0 +1,54 @@ +/*- + * Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from the Stanford/CMU enet packet filter, + * (net/enet.c) distributed as part of 4.3BSD, and code contributed + * to Berkeley by Steven McCanne and Van Jacobson both of Lawrence + * Berkeley Laboratory. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lib_pcap_can_socketcan_h +#define lib_pcap_can_socketcan_h + +/* + * SocketCAN header, as per Documentation/networking/can.txt in the + * Linux source. + */ +typedef struct { + u_int32_t can_id; + u_int8_t payload_length; + u_int8_t pad; + u_int8_t reserved1; + u_int8_t reserved2; +} pcap_can_socketcan_hdr; + +#endif diff --git a/include/npcap/pcap/dlt.h b/include/npcap/pcap/dlt.h new file mode 100644 index 0000000..2d74713 --- /dev/null +++ b/include/npcap/pcap/dlt.h @@ -0,0 +1,1340 @@ +/*- + * Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from the Stanford/CMU enet packet filter, + * (net/enet.c) distributed as part of 4.3BSD, and code contributed + * to Berkeley by Steven McCanne and Van Jacobson both of Lawrence + * Berkeley Laboratory. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)bpf.h 7.1 (Berkeley) 5/7/91 + */ + +#ifndef lib_pcap_dlt_h +#define lib_pcap_dlt_h + +/* + * Link-layer header type codes. + * + * Do *NOT* add new values to this list without asking + * "tcpdump-workers@lists.tcpdump.org" for a value. Otherwise, you run + * the risk of using a value that's already being used for some other + * purpose, and of having tools that read libpcap-format captures not + * being able to handle captures with your new DLT_ value, with no hope + * that they will ever be changed to do so (as that would destroy their + * ability to read captures using that value for that other purpose). + * + * See + * + * http://www.tcpdump.org/linktypes.html + * + * for detailed descriptions of some of these link-layer header types. + */ + +/* + * These are the types that are the same on all platforms, and that + * have been defined by for ages. + */ +#define DLT_NULL 0 /* BSD loopback encapsulation */ +#define DLT_EN10MB 1 /* Ethernet (10Mb) */ +#define DLT_EN3MB 2 /* Experimental Ethernet (3Mb) */ +#define DLT_AX25 3 /* Amateur Radio AX.25 */ +#define DLT_PRONET 4 /* Proteon ProNET Token Ring */ +#define DLT_CHAOS 5 /* Chaos */ +#define DLT_IEEE802 6 /* 802.5 Token Ring */ +#define DLT_ARCNET 7 /* ARCNET, with BSD-style header */ +#define DLT_SLIP 8 /* Serial Line IP */ +#define DLT_PPP 9 /* Point-to-point Protocol */ +#define DLT_FDDI 10 /* FDDI */ + +/* + * These are types that are different on some platforms, and that + * have been defined by for ages. We use #ifdefs to + * detect the BSDs that define them differently from the traditional + * libpcap + * + * XXX - DLT_ATM_RFC1483 is 13 in BSD/OS, and DLT_RAW is 14 in BSD/OS, + * but I don't know what the right #define is for BSD/OS. + */ +#define DLT_ATM_RFC1483 11 /* LLC-encapsulated ATM */ + +#ifdef __OpenBSD__ +#define DLT_RAW 14 /* raw IP */ +#else +#define DLT_RAW 12 /* raw IP */ +#endif + +/* + * Given that the only OS that currently generates BSD/OS SLIP or PPP + * is, well, BSD/OS, arguably everybody should have chosen its values + * for DLT_SLIP_BSDOS and DLT_PPP_BSDOS, which are 15 and 16, but they + * didn't. So it goes. + */ +#if defined(__NetBSD__) || defined(__FreeBSD__) +#ifndef DLT_SLIP_BSDOS +#define DLT_SLIP_BSDOS 13 /* BSD/OS Serial Line IP */ +#define DLT_PPP_BSDOS 14 /* BSD/OS Point-to-point Protocol */ +#endif +#else +#define DLT_SLIP_BSDOS 15 /* BSD/OS Serial Line IP */ +#define DLT_PPP_BSDOS 16 /* BSD/OS Point-to-point Protocol */ +#endif + +/* + * 17 was used for DLT_PFLOG in OpenBSD; it no longer is. + * + * It was DLT_LANE8023 in SuSE 6.3, so we defined LINKTYPE_PFLOG + * as 117 so that pflog captures would use a link-layer header type + * value that didn't collide with any other values. On all + * platforms other than OpenBSD, we defined DLT_PFLOG as 117, + * and we mapped between LINKTYPE_PFLOG and DLT_PFLOG. + * + * OpenBSD eventually switched to using 117 for DLT_PFLOG as well. + * + * Don't use 17 for anything else. + */ + +/* + * 18 is used for DLT_PFSYNC in OpenBSD, NetBSD, DragonFly BSD and + * Mac OS X; don't use it for anything else. (FreeBSD uses 121, + * which collides with DLT_HHDLC, even though it doesn't use 18 + * for anything and doesn't appear to have ever used it for anything.) + * + * We define it as 18 on those platforms; it is, unfortunately, used + * for DLT_CIP in Suse 6.3, so we don't define it as DLT_PFSYNC + * in general. As the packet format for it, like that for + * DLT_PFLOG, is not only OS-dependent but OS-version-dependent, + * we don't support printing it in tcpdump except on OSes that + * have the relevant header files, so it's not that useful on + * other platforms. + */ +#if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__APPLE__) +#define DLT_PFSYNC 18 +#endif + +#define DLT_ATM_CLIP 19 /* Linux Classical-IP over ATM */ + +/* + * Apparently Redback uses this for its SmartEdge 400/800. I hope + * nobody else decided to use it, too. + */ +#define DLT_REDBACK_SMARTEDGE 32 + +/* + * These values are defined by NetBSD; other platforms should refrain from + * using them for other purposes, so that NetBSD savefiles with link + * types of 50 or 51 can be read as this type on all platforms. + */ +#define DLT_PPP_SERIAL 50 /* PPP over serial with HDLC encapsulation */ +#define DLT_PPP_ETHER 51 /* PPP over Ethernet */ + +/* + * The Axent Raptor firewall - now the Symantec Enterprise Firewall - uses + * a link-layer type of 99 for the tcpdump it supplies. The link-layer + * header has 6 bytes of unknown data, something that appears to be an + * Ethernet type, and 36 bytes that appear to be 0 in at least one capture + * I've seen. + */ +#define DLT_SYMANTEC_FIREWALL 99 + +/* + * Values between 100 and 103 are used in capture file headers as + * link-layer header type LINKTYPE_ values corresponding to DLT_ types + * that differ between platforms; don't use those values for new DLT_ + * new types. + */ + +/* + * Values starting with 104 are used for newly-assigned link-layer + * header type values; for those link-layer header types, the DLT_ + * value returned by pcap_datalink() and passed to pcap_open_dead(), + * and the LINKTYPE_ value that appears in capture files, are the + * same. + * + * DLT_MATCHING_MIN is the lowest such value; DLT_MATCHING_MAX is + * the highest such value. + */ +#define DLT_MATCHING_MIN 104 + +/* + * This value was defined by libpcap 0.5; platforms that have defined + * it with a different value should define it here with that value - + * a link type of 104 in a save file will be mapped to DLT_C_HDLC, + * whatever value that happens to be, so programs will correctly + * handle files with that link type regardless of the value of + * DLT_C_HDLC. + * + * The name DLT_C_HDLC was used by BSD/OS; we use that name for source + * compatibility with programs written for BSD/OS. + * + * libpcap 0.5 defined it as DLT_CHDLC; we define DLT_CHDLC as well, + * for source compatibility with programs written for libpcap 0.5. + */ +#define DLT_C_HDLC 104 /* Cisco HDLC */ +#define DLT_CHDLC DLT_C_HDLC + +#define DLT_IEEE802_11 105 /* IEEE 802.11 wireless */ + +/* + * 106 is reserved for Linux Classical IP over ATM; it's like DLT_RAW, + * except when it isn't. (I.e., sometimes it's just raw IP, and + * sometimes it isn't.) We currently handle it as DLT_LINUX_SLL, + * so that we don't have to worry about the link-layer header.) + */ + +/* + * Frame Relay; BSD/OS has a DLT_FR with a value of 11, but that collides + * with other values. + * DLT_FR and DLT_FRELAY packets start with the Q.922 Frame Relay header + * (DLCI, etc.). + */ +#define DLT_FRELAY 107 + +/* + * OpenBSD DLT_LOOP, for loopback devices; it's like DLT_NULL, except + * that the AF_ type in the link-layer header is in network byte order. + * + * DLT_LOOP is 12 in OpenBSD, but that's DLT_RAW in other OSes, so + * we don't use 12 for it in OSes other than OpenBSD. + */ +#ifdef __OpenBSD__ +#define DLT_LOOP 12 +#else +#define DLT_LOOP 108 +#endif + +/* + * Encapsulated packets for IPsec; DLT_ENC is 13 in OpenBSD, but that's + * DLT_SLIP_BSDOS in NetBSD, so we don't use 13 for it in OSes other + * than OpenBSD. + */ +#ifdef __OpenBSD__ +#define DLT_ENC 13 +#else +#define DLT_ENC 109 +#endif + +/* + * Values between 110 and 112 are reserved for use in capture file headers + * as link-layer types corresponding to DLT_ types that might differ + * between platforms; don't use those values for new DLT_ types + * other than the corresponding DLT_ types. + */ + +/* + * This is for Linux cooked sockets. + */ +#define DLT_LINUX_SLL 113 + +/* + * Apple LocalTalk hardware. + */ +#define DLT_LTALK 114 + +/* + * Acorn Econet. + */ +#define DLT_ECONET 115 + +/* + * Reserved for use with OpenBSD ipfilter. + */ +#define DLT_IPFILTER 116 + +/* + * OpenBSD DLT_PFLOG. + */ +#define DLT_PFLOG 117 + +/* + * Registered for Cisco-internal use. + */ +#define DLT_CISCO_IOS 118 + +/* + * For 802.11 cards using the Prism II chips, with a link-layer + * header including Prism monitor mode information plus an 802.11 + * header. + */ +#define DLT_PRISM_HEADER 119 + +/* + * Reserved for Aironet 802.11 cards, with an Aironet link-layer header + * (see Doug Ambrisko's FreeBSD patches). + */ +#define DLT_AIRONET_HEADER 120 + +/* + * Sigh. + * + * 121 was reserved for Siemens HiPath HDLC on 2002-01-25, as + * requested by Tomas Kukosa. + * + * On 2004-02-25, a FreeBSD checkin to sys/net/bpf.h was made that + * assigned 121 as DLT_PFSYNC. In current versions, its libpcap + * does DLT_ <-> LINKTYPE_ mapping, mapping DLT_PFSYNC to a + * LINKTYPE_PFSYNC value of 246, so it should write out DLT_PFSYNC + * dump files with 246 as the link-layer header type. (Earlier + * versions might not have done mapping, in which case they would + * have written them out with a link-layer header type of 121.) + * + * OpenBSD, from which pf came, however, uses 18 for DLT_PFSYNC; + * its libpcap does no DLT_ <-> LINKTYPE_ mapping, so it would + * write out DLT_PFSYNC dump files with use 18 as the link-layer + * header type. + * + * NetBSD, DragonFly BSD, and Darwin also use 18 for DLT_PFSYNC; in + * current versions, their libpcaps do DLT_ <-> LINKTYPE_ mapping, + * mapping DLT_PFSYNC to a LINKTYPE_PFSYNC value of 246, so they + * should write out DLT_PFSYNC dump files with 246 as the link-layer + * header type. (Earlier versions might not have done mapping, + * in which case they'd work the same way OpenBSD does, writing + * them out with a link-layer header type of 18.) + * + * We'll define DLT_PFSYNC as: + * + * 18 on NetBSD, OpenBSD, DragonFly BSD, and Darwin; + * + * 121 on FreeBSD; + * + * 246 everywhere else. + * + * We'll define DLT_HHDLC as 121 on everything except for FreeBSD; + * anybody who wants to compile, on FreeBSD, code that uses DLT_HHDLC + * is out of luck. + * + * We'll define LINKTYPE_PFSYNC as 246 on *all* platforms, so that + * savefiles written using *this* code won't use 18 or 121 for PFSYNC, + * they'll all use 246. + * + * Code that uses pcap_datalink() to determine the link-layer header + * type of a savefile won't, when built and run on FreeBSD, be able + * to distinguish between LINKTYPE_PFSYNC and LINKTYPE_HHDLC capture + * files, as pcap_datalink() will give 121 for both of them. Code + * that doesn't, such as the code in Wireshark, will be able to + * distinguish between them. + * + * FreeBSD's libpcap won't map a link-layer header type of 18 - i.e., + * DLT_PFSYNC files from OpenBSD and possibly older versions of NetBSD, + * DragonFly BSD, and OS X - to DLT_PFSYNC, so code built with FreeBSD's + * libpcap won't treat those files as DLT_PFSYNC files. + * + * Other libpcaps won't map a link-layer header type of 121 to DLT_PFSYNC; + * this means they can read DLT_HHDLC files, if any exist, but won't + * treat pcap files written by any older versions of FreeBSD libpcap that + * didn't map to 246 as DLT_PFSYNC files. + */ +#ifdef __FreeBSD__ +#define DLT_PFSYNC 121 +#else +#define DLT_HHDLC 121 +#endif + +/* + * This is for RFC 2625 IP-over-Fibre Channel. + * + * This is not for use with raw Fibre Channel, where the link-layer + * header starts with a Fibre Channel frame header; it's for IP-over-FC, + * where the link-layer header starts with an RFC 2625 Network_Header + * field. + */ +#define DLT_IP_OVER_FC 122 + +/* + * This is for Full Frontal ATM on Solaris with SunATM, with a + * pseudo-header followed by an AALn PDU. + * + * There may be other forms of Full Frontal ATM on other OSes, + * with different pseudo-headers. + * + * If ATM software returns a pseudo-header with VPI/VCI information + * (and, ideally, packet type information, e.g. signalling, ILMI, + * LANE, LLC-multiplexed traffic, etc.), it should not use + * DLT_ATM_RFC1483, but should get a new DLT_ value, so tcpdump + * and the like don't have to infer the presence or absence of a + * pseudo-header and the form of the pseudo-header. + */ +#define DLT_SUNATM 123 /* Solaris+SunATM */ + +/* + * Reserved as per request from Kent Dahlgren + * for private use. + */ +#define DLT_RIO 124 /* RapidIO */ +#define DLT_PCI_EXP 125 /* PCI Express */ +#define DLT_AURORA 126 /* Xilinx Aurora link layer */ + +/* + * Header for 802.11 plus a number of bits of link-layer information + * including radio information, used by some recent BSD drivers as + * well as the madwifi Atheros driver for Linux. + */ +#define DLT_IEEE802_11_RADIO 127 /* 802.11 plus radiotap radio header */ + +/* + * Reserved for the TZSP encapsulation, as per request from + * Chris Waters + * TZSP is a generic encapsulation for any other link type, + * which includes a means to include meta-information + * with the packet, e.g. signal strength and channel + * for 802.11 packets. + */ +#define DLT_TZSP 128 /* Tazmen Sniffer Protocol */ + +/* + * BSD's ARCNET headers have the source host, destination host, + * and type at the beginning of the packet; that's what's handed + * up to userland via BPF. + * + * Linux's ARCNET headers, however, have a 2-byte offset field + * between the host IDs and the type; that's what's handed up + * to userland via PF_PACKET sockets. + * + * We therefore have to have separate DLT_ values for them. + */ +#define DLT_ARCNET_LINUX 129 /* ARCNET */ + +/* + * Juniper-private data link types, as per request from + * Hannes Gredler . The DLT_s are used + * for passing on chassis-internal metainformation such as + * QOS profiles, etc.. + */ +#define DLT_JUNIPER_MLPPP 130 +#define DLT_JUNIPER_MLFR 131 +#define DLT_JUNIPER_ES 132 +#define DLT_JUNIPER_GGSN 133 +#define DLT_JUNIPER_MFR 134 +#define DLT_JUNIPER_ATM2 135 +#define DLT_JUNIPER_SERVICES 136 +#define DLT_JUNIPER_ATM1 137 + +/* + * Apple IP-over-IEEE 1394, as per a request from Dieter Siegmund + * . The header that's presented is an Ethernet-like + * header: + * + * #define FIREWIRE_EUI64_LEN 8 + * struct firewire_header { + * u_char firewire_dhost[FIREWIRE_EUI64_LEN]; + * u_char firewire_shost[FIREWIRE_EUI64_LEN]; + * u_short firewire_type; + * }; + * + * with "firewire_type" being an Ethernet type value, rather than, + * for example, raw GASP frames being handed up. + */ +#define DLT_APPLE_IP_OVER_IEEE1394 138 + +/* + * Various SS7 encapsulations, as per a request from Jeff Morriss + * and subsequent discussions. + */ +#define DLT_MTP2_WITH_PHDR 139 /* pseudo-header with various info, followed by MTP2 */ +#define DLT_MTP2 140 /* MTP2, without pseudo-header */ +#define DLT_MTP3 141 /* MTP3, without pseudo-header or MTP2 */ +#define DLT_SCCP 142 /* SCCP, without pseudo-header or MTP2 or MTP3 */ + +/* + * DOCSIS MAC frames. + */ +#define DLT_DOCSIS 143 + +/* + * Linux-IrDA packets. Protocol defined at http://www.irda.org. + * Those packets include IrLAP headers and above (IrLMP...), but + * don't include Phy framing (SOF/EOF/CRC & byte stuffing), because Phy + * framing can be handled by the hardware and depend on the bitrate. + * This is exactly the format you would get capturing on a Linux-IrDA + * interface (irdaX), but not on a raw serial port. + * Note the capture is done in "Linux-cooked" mode, so each packet include + * a fake packet header (struct sll_header). This is because IrDA packet + * decoding is dependant on the direction of the packet (incomming or + * outgoing). + * When/if other platform implement IrDA capture, we may revisit the + * issue and define a real DLT_IRDA... + * Jean II + */ +#define DLT_LINUX_IRDA 144 + +/* + * Reserved for IBM SP switch and IBM Next Federation switch. + */ +#define DLT_IBM_SP 145 +#define DLT_IBM_SN 146 + +/* + * Reserved for private use. If you have some link-layer header type + * that you want to use within your organization, with the capture files + * using that link-layer header type not ever be sent outside your + * organization, you can use these values. + * + * No libpcap release will use these for any purpose, nor will any + * tcpdump release use them, either. + * + * Do *NOT* use these in capture files that you expect anybody not using + * your private versions of capture-file-reading tools to read; in + * particular, do *NOT* use them in products, otherwise you may find that + * people won't be able to use tcpdump, or snort, or Ethereal, or... to + * read capture files from your firewall/intrusion detection/traffic + * monitoring/etc. appliance, or whatever product uses that DLT_ value, + * and you may also find that the developers of those applications will + * not accept patches to let them read those files. + * + * Also, do not use them if somebody might send you a capture using them + * for *their* private type and tools using them for *your* private type + * would have to read them. + * + * Instead, ask "tcpdump-workers@lists.tcpdump.org" for a new DLT_ value, + * as per the comment above, and use the type you're given. + */ +#define DLT_USER0 147 +#define DLT_USER1 148 +#define DLT_USER2 149 +#define DLT_USER3 150 +#define DLT_USER4 151 +#define DLT_USER5 152 +#define DLT_USER6 153 +#define DLT_USER7 154 +#define DLT_USER8 155 +#define DLT_USER9 156 +#define DLT_USER10 157 +#define DLT_USER11 158 +#define DLT_USER12 159 +#define DLT_USER13 160 +#define DLT_USER14 161 +#define DLT_USER15 162 + +/* + * For future use with 802.11 captures - defined by AbsoluteValue + * Systems to store a number of bits of link-layer information + * including radio information: + * + * http://www.shaftnet.org/~pizza/software/capturefrm.txt + * + * but it might be used by some non-AVS drivers now or in the + * future. + */ +#define DLT_IEEE802_11_RADIO_AVS 163 /* 802.11 plus AVS radio header */ + +/* + * Juniper-private data link type, as per request from + * Hannes Gredler . The DLT_s are used + * for passing on chassis-internal metainformation such as + * QOS profiles, etc.. + */ +#define DLT_JUNIPER_MONITOR 164 + +/* + * BACnet MS/TP frames. + */ +#define DLT_BACNET_MS_TP 165 + +/* + * Another PPP variant as per request from Karsten Keil . + * + * This is used in some OSes to allow a kernel socket filter to distinguish + * between incoming and outgoing packets, on a socket intended to + * supply pppd with outgoing packets so it can do dial-on-demand and + * hangup-on-lack-of-demand; incoming packets are filtered out so they + * don't cause pppd to hold the connection up (you don't want random + * input packets such as port scans, packets from old lost connections, + * etc. to force the connection to stay up). + * + * The first byte of the PPP header (0xff03) is modified to accomodate + * the direction - 0x00 = IN, 0x01 = OUT. + */ +#define DLT_PPP_PPPD 166 + +/* + * Names for backwards compatibility with older versions of some PPP + * software; new software should use DLT_PPP_PPPD. + */ +#define DLT_PPP_WITH_DIRECTION DLT_PPP_PPPD +#define DLT_LINUX_PPP_WITHDIRECTION DLT_PPP_PPPD + +/* + * Juniper-private data link type, as per request from + * Hannes Gredler . The DLT_s are used + * for passing on chassis-internal metainformation such as + * QOS profiles, cookies, etc.. + */ +#define DLT_JUNIPER_PPPOE 167 +#define DLT_JUNIPER_PPPOE_ATM 168 + +#define DLT_GPRS_LLC 169 /* GPRS LLC */ +#define DLT_GPF_T 170 /* GPF-T (ITU-T G.7041/Y.1303) */ +#define DLT_GPF_F 171 /* GPF-F (ITU-T G.7041/Y.1303) */ + +/* + * Requested by Oolan Zimmer for use in Gcom's T1/E1 line + * monitoring equipment. + */ +#define DLT_GCOM_T1E1 172 +#define DLT_GCOM_SERIAL 173 + +/* + * Juniper-private data link type, as per request from + * Hannes Gredler . The DLT_ is used + * for internal communication to Physical Interface Cards (PIC) + */ +#define DLT_JUNIPER_PIC_PEER 174 + +/* + * Link types requested by Gregor Maier of Endace + * Measurement Systems. They add an ERF header (see + * http://www.endace.com/support/EndaceRecordFormat.pdf) in front of + * the link-layer header. + */ +#define DLT_ERF_ETH 175 /* Ethernet */ +#define DLT_ERF_POS 176 /* Packet-over-SONET */ + +/* + * Requested by Daniele Orlandi for raw LAPD + * for vISDN (http://www.orlandi.com/visdn/). Its link-layer header + * includes additional information before the LAPD header, so it's + * not necessarily a generic LAPD header. + */ +#define DLT_LINUX_LAPD 177 + +/* + * Juniper-private data link type, as per request from + * Hannes Gredler . + * The DLT_ are used for prepending meta-information + * like interface index, interface name + * before standard Ethernet, PPP, Frelay & C-HDLC Frames + */ +#define DLT_JUNIPER_ETHER 178 +#define DLT_JUNIPER_PPP 179 +#define DLT_JUNIPER_FRELAY 180 +#define DLT_JUNIPER_CHDLC 181 + +/* + * Multi Link Frame Relay (FRF.16) + */ +#define DLT_MFR 182 + +/* + * Juniper-private data link type, as per request from + * Hannes Gredler . + * The DLT_ is used for internal communication with a + * voice Adapter Card (PIC) + */ +#define DLT_JUNIPER_VP 183 + +/* + * Arinc 429 frames. + * DLT_ requested by Gianluca Varenni . + * Every frame contains a 32bit A429 label. + * More documentation on Arinc 429 can be found at + * http://www.condoreng.com/support/downloads/tutorials/ARINCTutorial.pdf + */ +#define DLT_A429 184 + +/* + * Arinc 653 Interpartition Communication messages. + * DLT_ requested by Gianluca Varenni . + * Please refer to the A653-1 standard for more information. + */ +#define DLT_A653_ICM 185 + +/* + * This used to be "USB packets, beginning with a USB setup header; + * requested by Paolo Abeni ." + * + * However, that header didn't work all that well - it left out some + * useful information - and was abandoned in favor of the DLT_USB_LINUX + * header. + * + * This is now used by FreeBSD for its BPF taps for USB; that has its + * own headers. So it is written, so it is done. + * + * For source-code compatibility, we also define DLT_USB to have this + * value. We do it numerically so that, if code that includes this + * file (directly or indirectly) also includes an OS header that also + * defines DLT_USB as 186, we don't get a redefinition warning. + * (NetBSD 7 does that.) + */ +#define DLT_USB_FREEBSD 186 +#define DLT_USB 186 + +/* + * Bluetooth HCI UART transport layer (part H:4); requested by + * Paolo Abeni. + */ +#define DLT_BLUETOOTH_HCI_H4 187 + +/* + * IEEE 802.16 MAC Common Part Sublayer; requested by Maria Cruz + * . + */ +#define DLT_IEEE802_16_MAC_CPS 188 + +/* + * USB packets, beginning with a Linux USB header; requested by + * Paolo Abeni . + */ +#define DLT_USB_LINUX 189 + +/* + * Controller Area Network (CAN) v. 2.0B packets. + * DLT_ requested by Gianluca Varenni . + * Used to dump CAN packets coming from a CAN Vector board. + * More documentation on the CAN v2.0B frames can be found at + * http://www.can-cia.org/downloads/?269 + */ +#define DLT_CAN20B 190 + +/* + * IEEE 802.15.4, with address fields padded, as is done by Linux + * drivers; requested by Juergen Schimmer. + */ +#define DLT_IEEE802_15_4_LINUX 191 + +/* + * Per Packet Information encapsulated packets. + * DLT_ requested by Gianluca Varenni . + */ +#define DLT_PPI 192 + +/* + * Header for 802.16 MAC Common Part Sublayer plus a radiotap radio header; + * requested by Charles Clancy. + */ +#define DLT_IEEE802_16_MAC_CPS_RADIO 193 + +/* + * Juniper-private data link type, as per request from + * Hannes Gredler . + * The DLT_ is used for internal communication with a + * integrated service module (ISM). + */ +#define DLT_JUNIPER_ISM 194 + +/* + * IEEE 802.15.4, exactly as it appears in the spec (no padding, no + * nothing); requested by Mikko Saarnivala . + * For this one, we expect the FCS to be present at the end of the frame; + * if the frame has no FCS, DLT_IEEE802_15_4_NOFCS should be used. + */ +#define DLT_IEEE802_15_4 195 + +/* + * Various link-layer types, with a pseudo-header, for SITA + * (http://www.sita.aero/); requested by Fulko Hew (fulko.hew@gmail.com). + */ +#define DLT_SITA 196 + +/* + * Various link-layer types, with a pseudo-header, for Endace DAG cards; + * encapsulates Endace ERF records. Requested by Stephen Donnelly + * . + */ +#define DLT_ERF 197 + +/* + * Special header prepended to Ethernet packets when capturing from a + * u10 Networks board. Requested by Phil Mulholland + * . + */ +#define DLT_RAIF1 198 + +/* + * IPMB packet for IPMI, beginning with the I2C slave address, followed + * by the netFn and LUN, etc.. Requested by Chanthy Toeung + * . + */ +#define DLT_IPMB 199 + +/* + * Juniper-private data link type, as per request from + * Hannes Gredler . + * The DLT_ is used for capturing data on a secure tunnel interface. + */ +#define DLT_JUNIPER_ST 200 + +/* + * Bluetooth HCI UART transport layer (part H:4), with pseudo-header + * that includes direction information; requested by Paolo Abeni. + */ +#define DLT_BLUETOOTH_HCI_H4_WITH_PHDR 201 + +/* + * AX.25 packet with a 1-byte KISS header; see + * + * http://www.ax25.net/kiss.htm + * + * as per Richard Stearn . + */ +#define DLT_AX25_KISS 202 + +/* + * LAPD packets from an ISDN channel, starting with the address field, + * with no pseudo-header. + * Requested by Varuna De Silva . + */ +#define DLT_LAPD 203 + +/* + * Variants of various link-layer headers, with a one-byte direction + * pseudo-header prepended - zero means "received by this host", + * non-zero (any non-zero value) means "sent by this host" - as per + * Will Barker . + */ +#define DLT_PPP_WITH_DIR 204 /* PPP - don't confuse with DLT_PPP_WITH_DIRECTION */ +#define DLT_C_HDLC_WITH_DIR 205 /* Cisco HDLC */ +#define DLT_FRELAY_WITH_DIR 206 /* Frame Relay */ +#define DLT_LAPB_WITH_DIR 207 /* LAPB */ + +/* + * 208 is reserved for an as-yet-unspecified proprietary link-layer + * type, as requested by Will Barker. + */ + +/* + * IPMB with a Linux-specific pseudo-header; as requested by Alexey Neyman + * . + */ +#define DLT_IPMB_LINUX 209 + +/* + * FlexRay automotive bus - http://www.flexray.com/ - as requested + * by Hannes Kaelber . + */ +#define DLT_FLEXRAY 210 + +/* + * Media Oriented Systems Transport (MOST) bus for multimedia + * transport - http://www.mostcooperation.com/ - as requested + * by Hannes Kaelber . + */ +#define DLT_MOST 211 + +/* + * Local Interconnect Network (LIN) bus for vehicle networks - + * http://www.lin-subbus.org/ - as requested by Hannes Kaelber + * . + */ +#define DLT_LIN 212 + +/* + * X2E-private data link type used for serial line capture, + * as requested by Hannes Kaelber . + */ +#define DLT_X2E_SERIAL 213 + +/* + * X2E-private data link type used for the Xoraya data logger + * family, as requested by Hannes Kaelber . + */ +#define DLT_X2E_XORAYA 214 + +/* + * IEEE 802.15.4, exactly as it appears in the spec (no padding, no + * nothing), but with the PHY-level data for non-ASK PHYs (4 octets + * of 0 as preamble, one octet of SFD, one octet of frame length+ + * reserved bit, and then the MAC-layer data, starting with the + * frame control field). + * + * Requested by Max Filippov . + */ +#define DLT_IEEE802_15_4_NONASK_PHY 215 + +/* + * David Gibson requested this for + * captures from the Linux kernel /dev/input/eventN devices. This + * is used to communicate keystrokes and mouse movements from the + * Linux kernel to display systems, such as Xorg. + */ +#define DLT_LINUX_EVDEV 216 + +/* + * GSM Um and Abis interfaces, preceded by a "gsmtap" header. + * + * Requested by Harald Welte . + */ +#define DLT_GSMTAP_UM 217 +#define DLT_GSMTAP_ABIS 218 + +/* + * MPLS, with an MPLS label as the link-layer header. + * Requested by Michele Marchetto on behalf + * of OpenBSD. + */ +#define DLT_MPLS 219 + +/* + * USB packets, beginning with a Linux USB header, with the USB header + * padded to 64 bytes; required for memory-mapped access. + */ +#define DLT_USB_LINUX_MMAPPED 220 + +/* + * DECT packets, with a pseudo-header; requested by + * Matthias Wenzel . + */ +#define DLT_DECT 221 + +/* + * From: "Lidwa, Eric (GSFC-582.0)[SGT INC]" + * Date: Mon, 11 May 2009 11:18:30 -0500 + * + * DLT_AOS. We need it for AOS Space Data Link Protocol. + * I have already written dissectors for but need an OK from + * legal before I can submit a patch. + * + */ +#define DLT_AOS 222 + +/* + * Wireless HART (Highway Addressable Remote Transducer) + * From the HART Communication Foundation + * IES/PAS 62591 + * + * Requested by Sam Roberts . + */ +#define DLT_WIHART 223 + +/* + * Fibre Channel FC-2 frames, beginning with a Frame_Header. + * Requested by Kahou Lei . + */ +#define DLT_FC_2 224 + +/* + * Fibre Channel FC-2 frames, beginning with an encoding of the + * SOF, and ending with an encoding of the EOF. + * + * The encodings represent the frame delimiters as 4-byte sequences + * representing the corresponding ordered sets, with K28.5 + * represented as 0xBC, and the D symbols as the corresponding + * byte values; for example, SOFi2, which is K28.5 - D21.5 - D1.2 - D21.2, + * is represented as 0xBC 0xB5 0x55 0x55. + * + * Requested by Kahou Lei . + */ +#define DLT_FC_2_WITH_FRAME_DELIMS 225 + +/* + * Solaris ipnet pseudo-header; requested by Darren Reed . + * + * The pseudo-header starts with a one-byte version number; for version 2, + * the pseudo-header is: + * + * struct dl_ipnetinfo { + * u_int8_t dli_version; + * u_int8_t dli_family; + * u_int16_t dli_htype; + * u_int32_t dli_pktlen; + * u_int32_t dli_ifindex; + * u_int32_t dli_grifindex; + * u_int32_t dli_zsrc; + * u_int32_t dli_zdst; + * }; + * + * dli_version is 2 for the current version of the pseudo-header. + * + * dli_family is a Solaris address family value, so it's 2 for IPv4 + * and 26 for IPv6. + * + * dli_htype is a "hook type" - 0 for incoming packets, 1 for outgoing + * packets, and 2 for packets arriving from another zone on the same + * machine. + * + * dli_pktlen is the length of the packet data following the pseudo-header + * (so the captured length minus dli_pktlen is the length of the + * pseudo-header, assuming the entire pseudo-header was captured). + * + * dli_ifindex is the interface index of the interface on which the + * packet arrived. + * + * dli_grifindex is the group interface index number (for IPMP interfaces). + * + * dli_zsrc is the zone identifier for the source of the packet. + * + * dli_zdst is the zone identifier for the destination of the packet. + * + * A zone number of 0 is the global zone; a zone number of 0xffffffff + * means that the packet arrived from another host on the network, not + * from another zone on the same machine. + * + * An IPv4 or IPv6 datagram follows the pseudo-header; dli_family indicates + * which of those it is. + */ +#define DLT_IPNET 226 + +/* + * CAN (Controller Area Network) frames, with a pseudo-header as supplied + * by Linux SocketCAN, and with multi-byte numerical fields in that header + * in big-endian byte order. + * + * See Documentation/networking/can.txt in the Linux source. + * + * Requested by Felix Obenhuber . + */ +#define DLT_CAN_SOCKETCAN 227 + +/* + * Raw IPv4/IPv6; different from DLT_RAW in that the DLT_ value specifies + * whether it's v4 or v6. Requested by Darren Reed . + */ +#define DLT_IPV4 228 +#define DLT_IPV6 229 + +/* + * IEEE 802.15.4, exactly as it appears in the spec (no padding, no + * nothing), and with no FCS at the end of the frame; requested by + * Jon Smirl . + */ +#define DLT_IEEE802_15_4_NOFCS 230 + +/* + * Raw D-Bus: + * + * http://www.freedesktop.org/wiki/Software/dbus + * + * messages: + * + * http://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-messages + * + * starting with the endianness flag, followed by the message type, etc., + * but without the authentication handshake before the message sequence: + * + * http://dbus.freedesktop.org/doc/dbus-specification.html#auth-protocol + * + * Requested by Martin Vidner . + */ +#define DLT_DBUS 231 + +/* + * Juniper-private data link type, as per request from + * Hannes Gredler . + */ +#define DLT_JUNIPER_VS 232 +#define DLT_JUNIPER_SRX_E2E 233 +#define DLT_JUNIPER_FIBRECHANNEL 234 + +/* + * DVB-CI (DVB Common Interface for communication between a PC Card + * module and a DVB receiver). See + * + * http://www.kaiser.cx/pcap-dvbci.html + * + * for the specification. + * + * Requested by Martin Kaiser . + */ +#define DLT_DVB_CI 235 + +/* + * Variant of 3GPP TS 27.010 multiplexing protocol (similar to, but + * *not* the same as, 27.010). Requested by Hans-Christoph Schemmel + * . + */ +#define DLT_MUX27010 236 + +/* + * STANAG 5066 D_PDUs. Requested by M. Baris Demiray + * . + */ +#define DLT_STANAG_5066_D_PDU 237 + +/* + * Juniper-private data link type, as per request from + * Hannes Gredler . + */ +#define DLT_JUNIPER_ATM_CEMIC 238 + +/* + * NetFilter LOG messages + * (payload of netlink NFNL_SUBSYS_ULOG/NFULNL_MSG_PACKET packets) + * + * Requested by Jakub Zawadzki + */ +#define DLT_NFLOG 239 + +/* + * Hilscher Gesellschaft fuer Systemautomation mbH link-layer type + * for Ethernet packets with a 4-byte pseudo-header and always + * with the payload including the FCS, as supplied by their + * netANALYZER hardware and software. + * + * Requested by Holger P. Frommer + */ +#define DLT_NETANALYZER 240 + +/* + * Hilscher Gesellschaft fuer Systemautomation mbH link-layer type + * for Ethernet packets with a 4-byte pseudo-header and FCS and + * with the Ethernet header preceded by 7 bytes of preamble and + * 1 byte of SFD, as supplied by their netANALYZER hardware and + * software. + * + * Requested by Holger P. Frommer + */ +#define DLT_NETANALYZER_TRANSPARENT 241 + +/* + * IP-over-InfiniBand, as specified by RFC 4391. + * + * Requested by Petr Sumbera . + */ +#define DLT_IPOIB 242 + +/* + * MPEG-2 transport stream (ISO 13818-1/ITU-T H.222.0). + * + * Requested by Guy Martin . + */ +#define DLT_MPEG_2_TS 243 + +/* + * ng4T GmbH's UMTS Iub/Iur-over-ATM and Iub/Iur-over-IP format as + * used by their ng40 protocol tester. + * + * Requested by Jens Grimmer . + */ +#define DLT_NG40 244 + +/* + * Pseudo-header giving adapter number and flags, followed by an NFC + * (Near-Field Communications) Logical Link Control Protocol (LLCP) PDU, + * as specified by NFC Forum Logical Link Control Protocol Technical + * Specification LLCP 1.1. + * + * Requested by Mike Wakerly . + */ +#define DLT_NFC_LLCP 245 + +/* + * 246 is used as LINKTYPE_PFSYNC; do not use it for any other purpose. + * + * DLT_PFSYNC has different values on different platforms, and all of + * them collide with something used elsewhere. On platforms that + * don't already define it, define it as 246. + */ +#if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(__DragonFly__) && !defined(__APPLE__) +#define DLT_PFSYNC 246 +#endif + +/* + * Raw InfiniBand packets, starting with the Local Routing Header. + * + * Requested by Oren Kladnitsky . + */ +#define DLT_INFINIBAND 247 + +/* + * SCTP, with no lower-level protocols (i.e., no IPv4 or IPv6). + * + * Requested by Michael Tuexen . + */ +#define DLT_SCTP 248 + +/* + * USB packets, beginning with a USBPcap header. + * + * Requested by Tomasz Mon + */ +#define DLT_USBPCAP 249 + +/* + * Schweitzer Engineering Laboratories "RTAC" product serial-line + * packets. + * + * Requested by Chris Bontje . + */ +#define DLT_RTAC_SERIAL 250 + +/* + * Bluetooth Low Energy air interface link-layer packets. + * + * Requested by Mike Kershaw . + */ +#define DLT_BLUETOOTH_LE_LL 251 + +/* + * DLT type for upper-protocol layer PDU saves from wireshark. + * + * the actual contents are determined by two TAGs stored with each + * packet: + * EXP_PDU_TAG_LINKTYPE the link type (LINKTYPE_ value) of the + * original packet. + * + * EXP_PDU_TAG_PROTO_NAME the name of the wireshark dissector + * that can make sense of the data stored. + */ +#define DLT_WIRESHARK_UPPER_PDU 252 + +/* + * DLT type for the netlink protocol (nlmon devices). + */ +#define DLT_NETLINK 253 + +/* + * Bluetooth Linux Monitor headers for the BlueZ stack. + */ +#define DLT_BLUETOOTH_LINUX_MONITOR 254 + +/* + * Bluetooth Basic Rate/Enhanced Data Rate baseband packets, as + * captured by Ubertooth. + */ +#define DLT_BLUETOOTH_BREDR_BB 255 + +/* + * Bluetooth Low Energy link layer packets, as captured by Ubertooth. + */ +#define DLT_BLUETOOTH_LE_LL_WITH_PHDR 256 + +/* + * PROFIBUS data link layer. + */ +#define DLT_PROFIBUS_DL 257 + +/* + * Apple's DLT_PKTAP headers. + * + * Sadly, the folks at Apple either had no clue that the DLT_USERn values + * are for internal use within an organization and partners only, and + * didn't know that the right way to get a link-layer header type is to + * ask tcpdump.org for one, or knew and didn't care, so they just + * used DLT_USER2, which causes problems for everything except for + * their version of tcpdump. + * + * So I'll just give them one; hopefully this will show up in a + * libpcap release in time for them to get this into 10.10 Big Sur + * or whatever Mavericks' successor is called. LINKTYPE_PKTAP + * will be 258 *even on OS X*; that is *intentional*, so that + * PKTAP files look the same on *all* OSes (different OSes can have + * different numerical values for a given DLT_, but *MUST NOT* have + * different values for what goes in a file, as files can be moved + * between OSes!). + * + * When capturing, on a system with a Darwin-based OS, on a device + * that returns 149 (DLT_USER2 and Apple's DLT_PKTAP) with this + * version of libpcap, the DLT_ value for the pcap_t will be DLT_PKTAP, + * and that will continue to be DLT_USER2 on Darwin-based OSes. That way, + * binary compatibility with Mavericks is preserved for programs using + * this version of libpcap. This does mean that if you were using + * DLT_USER2 for some capture device on OS X, you can't do so with + * this version of libpcap, just as you can't with Apple's libpcap - + * on OS X, they define DLT_PKTAP to be DLT_USER2, so programs won't + * be able to distinguish between PKTAP and whatever you were using + * DLT_USER2 for. + * + * If the program saves the capture to a file using this version of + * libpcap's pcap_dump code, the LINKTYPE_ value in the file will be + * LINKTYPE_PKTAP, which will be 258, even on Darwin-based OSes. + * That way, the file will *not* be a DLT_USER2 file. That means + * that the latest version of tcpdump, when built with this version + * of libpcap, and sufficiently recent versions of Wireshark will + * be able to read those files and interpret them correctly; however, + * Apple's version of tcpdump in OS X 10.9 won't be able to handle + * them. (Hopefully, Apple will pick up this version of libpcap, + * and the corresponding version of tcpdump, so that tcpdump will + * be able to handle the old LINKTYPE_USER2 captures *and* the new + * LINKTYPE_PKTAP captures.) + */ +#ifdef __APPLE__ +#define DLT_PKTAP DLT_USER2 +#else +#define DLT_PKTAP 258 +#endif + +/* + * Ethernet packets preceded by a header giving the last 6 octets + * of the preamble specified by 802.3-2012 Clause 65, section + * 65.1.3.2 "Transmit". + */ +#define DLT_EPON 259 + +/* + * IPMI trace packets, as specified by Table 3-20 "Trace Data Block Format" + * in the PICMG HPM.2 specification. + */ +#define DLT_IPMI_HPM_2 260 + +/* + * per Joshua Wright , formats for Zwave captures. + */ +#define DLT_ZWAVE_R1_R2 261 +#define DLT_ZWAVE_R3 262 + +/* + * per Steve Karg , formats for Wattstopper + * Digital Lighting Management room bus serial protocol captures. + */ +#define DLT_WATTSTOPPER_DLM 263 + +/* + * ISO 14443 contactless smart card messages. + */ +#define DLT_ISO_14443 264 + +/* + * Radio data system (RDS) groups. IEC 62106. + * Per Jonathan Brucker . + */ +#define DLT_RDS 265 + +/* + * In case the code that includes this file (directly or indirectly) + * has also included OS files that happen to define DLT_MATCHING_MAX, + * with a different value (perhaps because that OS hasn't picked up + * the latest version of our DLT definitions), we undefine the + * previous value of DLT_MATCHING_MAX. + */ +#ifdef DLT_MATCHING_MAX +#undef DLT_MATCHING_MAX +#endif +#define DLT_MATCHING_MAX 265 /* highest value in the "matching" range */ + +/* + * DLT and savefile link type values are split into a class and + * a member of that class. A class value of 0 indicates a regular + * DLT_/LINKTYPE_ value. + */ +#define DLT_CLASS(x) ((x) & 0x03ff0000) + +/* + * NetBSD-specific generic "raw" link type. The class value indicates + * that this is the generic raw type, and the lower 16 bits are the + * address family we're dealing with. Those values are NetBSD-specific; + * do not assume that they correspond to AF_ values for your operating + * system. + */ +#define DLT_CLASS_NETBSD_RAWAF 0x02240000 +#define DLT_NETBSD_RAWAF(af) (DLT_CLASS_NETBSD_RAWAF | (af)) +#define DLT_NETBSD_RAWAF_AF(x) ((x) & 0x0000ffff) +#define DLT_IS_NETBSD_RAWAF(x) (DLT_CLASS(x) == DLT_CLASS_NETBSD_RAWAF) + +#endif /* !defined(lib_pcap_dlt_h) */ diff --git a/include/npcap/pcap/export-defs.h b/include/npcap/pcap/export-defs.h new file mode 100644 index 0000000..a235057 --- /dev/null +++ b/include/npcap/pcap/export-defs.h @@ -0,0 +1,108 @@ +/* -*- Mode: c; tab-width: 8; indent-tabs-mode: 1; c-basic-offset: 8; -*- */ +/* + * Copyright (c) 1993, 1994, 1995, 1996, 1997 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the Computer Systems + * Engineering Group at Lawrence Berkeley Laboratory. + * 4. Neither the name of the University nor of the Laboratory may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lib_pcap_export_defs_h +#define lib_pcap_export_defs_h + +/* + * PCAP_API_DEF must be used when defining *data* exported from + * libpcap. It can be used when defining *functions* exported + * from libpcap, but it doesn't have to be used there. It + * should not be used in declarations in headers. + * + * PCAP_API must be used when *declaring* data or functions + * exported from libpcap; PCAP_API_DEF won't work on all platforms. + */ + +/* + * Check whether this is GCC major.minor or a later release, or some + * compiler that claims to be "just like GCC" of that version or a + * later release. + */ +#define IS_AT_LEAST_GNUC_VERSION(major, minor) \ + (defined(__GNUC__) && \ + (__GNUC__ > (major) || \ + (__GNUC__ == (major) && __GNUC_MINOR__ >= (minor)))) + +#if defined(_WIN32) + #ifdef BUILDING_PCAP + /* + * We're compiling libpcap, so we should export functions in our + * API. + */ + #define PCAP_API_DEF __declspec(dllexport) + #else + #define PCAP_API_DEF __declspec(dllimport) + #endif +#elif defined(MSDOS) + /* XXX - does this need special treatment? */ + #define PCAP_API_DEF +#else /* UN*X */ + #ifdef BUILDING_PCAP + /* + * We're compiling libpcap, so we should export functions in our API. + * The compiler might be configured not to export functions from a + * shared library by default, so we might have to explicitly mark + * functions as exported. + */ + #if IS_AT_LEAST_GNUC_VERSION(3, 4) + /* + * GCC 3.4 or later, or some compiler asserting compatibility with + * GCC 3.4 or later, so we have __attribute__((visibility()). + */ + #define PCAP_API_DEF __attribute__((visibility("default"))) + #elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x550) + /* + * Sun C 5.5 or later, so we have __global. + * (Sun C 5.9 and later also have __attribute__((visibility()), + * but there's no reason to prefer it with Sun C.) + */ + #define PCAP_API_DEF __global + #else + /* + * We don't have anything to say. + */ + #define PCAP_API_DEF + #endif + #else + /* + * We're not building libpcap. + */ + #define PCAP_API_DEF + #endif +#endif /* _WIN32/MSDOS/UN*X */ + +#define PCAP_API PCAP_API_DEF extern + +#endif /* lib_pcap_export_defs_h */ diff --git a/include/npcap/pcap/ipnet.h b/include/npcap/pcap/ipnet.h new file mode 100644 index 0000000..5330847 --- /dev/null +++ b/include/npcap/pcap/ipnet.h @@ -0,0 +1,43 @@ +/*- + * Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from the Stanford/CMU enet packet filter, + * (net/enet.c) distributed as part of 4.3BSD, and code contributed + * to Berkeley by Steven McCanne and Van Jacobson both of Lawrence + * Berkeley Laboratory. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#define IPH_AF_INET 2 /* Matches Solaris's AF_INET */ +#define IPH_AF_INET6 26 /* Matches Solaris's AF_INET6 */ + +#define IPNET_OUTBOUND 1 +#define IPNET_INBOUND 2 diff --git a/include/npcap/pcap/namedb.h b/include/npcap/pcap/namedb.h new file mode 100644 index 0000000..73fb40a --- /dev/null +++ b/include/npcap/pcap/namedb.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 1994, 1996 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the Computer Systems + * Engineering Group at Lawrence Berkeley Laboratory. + * 4. Neither the name of the University nor of the Laboratory may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lib_pcap_namedb_h +#define lib_pcap_namedb_h + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * As returned by the pcap_next_etherent() + * XXX this stuff doesn't belong in this interface, but this + * library already must do name to address translation, so + * on systems that don't have support for /etc/ethers, we + * export these hooks since they're already being used by + * some applications (such as tcpdump) and already being + * marked as exported in some OSes offering libpcap (such + * as Debian). + */ +struct pcap_etherent { + u_char addr[6]; + char name[122]; +}; +#ifndef PCAP_ETHERS_FILE +#define PCAP_ETHERS_FILE "/etc/ethers" +#endif +PCAP_API struct pcap_etherent *pcap_next_etherent(FILE *); +PCAP_API u_char *pcap_ether_hostton(const char*); +PCAP_API u_char *pcap_ether_aton(const char *); + +PCAP_API bpf_u_int32 **pcap_nametoaddr(const char *); +#ifdef INET6 +PCAP_API struct addrinfo *pcap_nametoaddrinfo(const char *); +#endif +PCAP_API bpf_u_int32 pcap_nametonetaddr(const char *); + +PCAP_API int pcap_nametoport(const char *, int *, int *); +PCAP_API int pcap_nametoportrange(const char *, int *, int *, int *); +PCAP_API int pcap_nametoproto(const char *); +PCAP_API int pcap_nametoeproto(const char *); +PCAP_API int pcap_nametollc(const char *); +/* + * If a protocol is unknown, PROTO_UNDEF is returned. + * Also, pcap_nametoport() returns the protocol along with the port number. + * If there are ambiguous entried in /etc/services (i.e. domain + * can be either tcp or udp) PROTO_UNDEF is returned. + */ +#define PROTO_UNDEF -1 + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/npcap/pcap/nflog.h b/include/npcap/pcap/nflog.h new file mode 100644 index 0000000..a3867cd --- /dev/null +++ b/include/npcap/pcap/nflog.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2013, Petar Alilovic, + * Faculty of Electrical Engineering and Computing, University of Zagreb + * All rights reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + */ + +#ifndef lib_pcap_nflog_h +#define lib_pcap_nflog_h + +/* + * Structure of an NFLOG header and TLV parts, as described at + * http://www.tcpdump.org/linktypes/LINKTYPE_NFLOG.html + * + * The NFLOG header is big-endian. + * + * The TLV length and type are in host byte order. The value is either + * big-endian or is an array of bytes in some externally-specified byte + * order (text string, link-layer address, link-layer header, packet + * data, etc.). + */ +typedef struct nflog_hdr { + u_int8_t nflog_family; /* address family */ + u_int8_t nflog_version; /* version */ + u_int16_t nflog_rid; /* resource ID */ +} nflog_hdr_t; + +typedef struct nflog_tlv { + u_int16_t tlv_length; /* tlv length */ + u_int16_t tlv_type; /* tlv type */ + /* value follows this */ +} nflog_tlv_t; + +typedef struct nflog_packet_hdr { + u_int16_t hw_protocol; /* hw protocol */ + u_int8_t hook; /* netfilter hook */ + u_int8_t pad; /* padding to 32 bits */ +} nflog_packet_hdr_t; + +typedef struct nflog_hwaddr { + u_int16_t hw_addrlen; /* address length */ + u_int16_t pad; /* padding to 32-bit boundary */ + u_int8_t hw_addr[8]; /* address, up to 8 bytes */ +} nflog_hwaddr_t; + +typedef struct nflog_timestamp { + u_int64_t sec; + u_int64_t usec; +} nflog_timestamp_t; + +/* + * TLV types. + */ +#define NFULA_PACKET_HDR 1 /* nflog_packet_hdr_t */ +#define NFULA_MARK 2 /* packet mark from skbuff */ +#define NFULA_TIMESTAMP 3 /* nflog_timestamp_t for skbuff's time stamp */ +#define NFULA_IFINDEX_INDEV 4 /* ifindex of device on which packet received (possibly bridge group) */ +#define NFULA_IFINDEX_OUTDEV 5 /* ifindex of device on which packet transmitted (possibly bridge group) */ +#define NFULA_IFINDEX_PHYSINDEV 6 /* ifindex of physical device on which packet received (not bridge group) */ +#define NFULA_IFINDEX_PHYSOUTDEV 7 /* ifindex of physical device on which packet transmitted (not bridge group) */ +#define NFULA_HWADDR 8 /* nflog_hwaddr_t for hardware address */ +#define NFULA_PAYLOAD 9 /* packet payload */ +#define NFULA_PREFIX 10 /* text string - null-terminated, count includes NUL */ +#define NFULA_UID 11 /* UID owning socket on which packet was sent/received */ +#define NFULA_SEQ 12 /* sequence number of packets on this NFLOG socket */ +#define NFULA_SEQ_GLOBAL 13 /* sequence number of pakets on all NFLOG sockets */ +#define NFULA_GID 14 /* GID owning socket on which packet was sent/received */ +#define NFULA_HWTYPE 15 /* ARPHRD_ type of skbuff's device */ +#define NFULA_HWHEADER 16 /* skbuff's MAC-layer header */ +#define NFULA_HWLEN 17 /* length of skbuff's MAC-layer header */ + +#endif diff --git a/include/npcap/pcap/pcap.h b/include/npcap/pcap/pcap.h new file mode 100644 index 0000000..7f92a37 --- /dev/null +++ b/include/npcap/pcap/pcap.h @@ -0,0 +1,538 @@ +/* -*- Mode: c; tab-width: 8; indent-tabs-mode: 1; c-basic-offset: 8; -*- */ +/* + * Copyright (c) 1993, 1994, 1995, 1996, 1997 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the Computer Systems + * Engineering Group at Lawrence Berkeley Laboratory. + * 4. Neither the name of the University nor of the Laboratory may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lib_pcap_pcap_h +#define lib_pcap_pcap_h + +#include + +#if defined(_WIN32) + #include +#elif defined(MSDOS) + #include + #include /* u_int, u_char etc. */ +#else /* UN*X */ + #include + #include +#endif /* _WIN32/MSDOS/UN*X */ + +#ifndef PCAP_DONT_INCLUDE_PCAP_BPF_H +#include +#endif + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Version number of the current version of the pcap file format. + * + * NOTE: this is *NOT* the version number of the libpcap library. + * To fetch the version information for the version of libpcap + * you're using, use pcap_lib_version(). + */ +#define PCAP_VERSION_MAJOR 2 +#define PCAP_VERSION_MINOR 4 + +#define PCAP_ERRBUF_SIZE 256 + +/* + * Compatibility for systems that have a bpf.h that + * predates the bpf typedefs for 64-bit support. + */ +#if BPF_RELEASE - 0 < 199406 +typedef int bpf_int32; +typedef u_int bpf_u_int32; +#endif + +typedef struct pcap pcap_t; +typedef struct pcap_dumper pcap_dumper_t; +typedef struct pcap_if pcap_if_t; +typedef struct pcap_addr pcap_addr_t; + +/* + * The first record in the file contains saved values for some + * of the flags used in the printout phases of tcpdump. + * Many fields here are 32 bit ints so compilers won't insert unwanted + * padding; these files need to be interchangeable across architectures. + * + * Do not change the layout of this structure, in any way (this includes + * changes that only affect the length of fields in this structure). + * + * Also, do not change the interpretation of any of the members of this + * structure, in any way (this includes using values other than + * LINKTYPE_ values, as defined in "savefile.c", in the "linktype" + * field). + * + * Instead: + * + * introduce a new structure for the new format, if the layout + * of the structure changed; + * + * send mail to "tcpdump-workers@lists.tcpdump.org", requesting + * a new magic number for your new capture file format, and, when + * you get the new magic number, put it in "savefile.c"; + * + * use that magic number for save files with the changed file + * header; + * + * make the code in "savefile.c" capable of reading files with + * the old file header as well as files with the new file header + * (using the magic number to determine the header format). + * + * Then supply the changes by forking the branch at + * + * https://github.com/the-tcpdump-group/libpcap/issues + * + * and issuing a pull request, so that future versions of libpcap and + * programs that use it (such as tcpdump) will be able to read your new + * capture file format. + */ +struct pcap_file_header { + bpf_u_int32 magic; + u_short version_major; + u_short version_minor; + bpf_int32 thiszone; /* gmt to local correction */ + bpf_u_int32 sigfigs; /* accuracy of timestamps */ + bpf_u_int32 snaplen; /* max length saved portion of each pkt */ + bpf_u_int32 linktype; /* data link type (LINKTYPE_*) */ +}; + +/* + * Macros for the value returned by pcap_datalink_ext(). + * + * If LT_FCS_LENGTH_PRESENT(x) is true, the LT_FCS_LENGTH(x) macro + * gives the FCS length of packets in the capture. + */ +#define LT_FCS_LENGTH_PRESENT(x) ((x) & 0x04000000) +#define LT_FCS_LENGTH(x) (((x) & 0xF0000000) >> 28) +#define LT_FCS_DATALINK_EXT(x) ((((x) & 0xF) << 28) | 0x04000000) + +typedef enum { + PCAP_D_INOUT = 0, + PCAP_D_IN, + PCAP_D_OUT +} pcap_direction_t; + +/* + * Generic per-packet information, as supplied by libpcap. + * + * The time stamp can and should be a "struct timeval", regardless of + * whether your system supports 32-bit tv_sec in "struct timeval", + * 64-bit tv_sec in "struct timeval", or both if it supports both 32-bit + * and 64-bit applications. The on-disk format of savefiles uses 32-bit + * tv_sec (and tv_usec); this structure is irrelevant to that. 32-bit + * and 64-bit versions of libpcap, even if they're on the same platform, + * should supply the appropriate version of "struct timeval", even if + * that's not what the underlying packet capture mechanism supplies. + */ +struct pcap_pkthdr { + struct timeval ts; /* time stamp */ + bpf_u_int32 caplen; /* length of portion present */ + bpf_u_int32 len; /* length this packet (off wire) */ +}; + +/* + * As returned by the pcap_stats() + */ +struct pcap_stat { + u_int ps_recv; /* number of packets received */ + u_int ps_drop; /* number of packets dropped */ + u_int ps_ifdrop; /* drops by interface -- only supported on some platforms */ +#if defined(_WIN32) && defined(HAVE_REMOTE) + u_int ps_capt; /* number of packets that reach the application */ + u_int ps_sent; /* number of packets sent by the server on the network */ + u_int ps_netdrop; /* number of packets lost on the network */ +#endif /* _WIN32 && HAVE_REMOTE */ +}; + +#ifdef MSDOS +/* + * As returned by the pcap_stats_ex() + */ +struct pcap_stat_ex { + u_long rx_packets; /* total packets received */ + u_long tx_packets; /* total packets transmitted */ + u_long rx_bytes; /* total bytes received */ + u_long tx_bytes; /* total bytes transmitted */ + u_long rx_errors; /* bad packets received */ + u_long tx_errors; /* packet transmit problems */ + u_long rx_dropped; /* no space in Rx buffers */ + u_long tx_dropped; /* no space available for Tx */ + u_long multicast; /* multicast packets received */ + u_long collisions; + + /* detailed rx_errors: */ + u_long rx_length_errors; + u_long rx_over_errors; /* receiver ring buff overflow */ + u_long rx_crc_errors; /* recv'd pkt with crc error */ + u_long rx_frame_errors; /* recv'd frame alignment error */ + u_long rx_fifo_errors; /* recv'r fifo overrun */ + u_long rx_missed_errors; /* recv'r missed packet */ + + /* detailed tx_errors */ + u_long tx_aborted_errors; + u_long tx_carrier_errors; + u_long tx_fifo_errors; + u_long tx_heartbeat_errors; + u_long tx_window_errors; + }; +#endif + +/* + * Item in a list of interfaces. + */ +struct pcap_if { + struct pcap_if *next; + char *name; /* name to hand to "pcap_open_live()" */ + char *description; /* textual description of interface, or NULL */ + struct pcap_addr *addresses; + bpf_u_int32 flags; /* PCAP_IF_ interface flags */ +}; + +#define PCAP_IF_LOOPBACK 0x00000001 /* interface is loopback */ +#define PCAP_IF_UP 0x00000002 /* interface is up */ +#define PCAP_IF_RUNNING 0x00000004 /* interface is running */ + +/* + * Representation of an interface address. + */ +struct pcap_addr { + struct pcap_addr *next; + struct sockaddr *addr; /* address */ + struct sockaddr *netmask; /* netmask for that address */ + struct sockaddr *broadaddr; /* broadcast address for that address */ + struct sockaddr *dstaddr; /* P2P destination address for that address */ +}; + +typedef void (*pcap_handler)(u_char *, const struct pcap_pkthdr *, + const u_char *); + +/* + * Error codes for the pcap API. + * These will all be negative, so you can check for the success or + * failure of a call that returns these codes by checking for a + * negative value. + */ +#define PCAP_ERROR -1 /* generic error code */ +#define PCAP_ERROR_BREAK -2 /* loop terminated by pcap_breakloop */ +#define PCAP_ERROR_NOT_ACTIVATED -3 /* the capture needs to be activated */ +#define PCAP_ERROR_ACTIVATED -4 /* the operation can't be performed on already activated captures */ +#define PCAP_ERROR_NO_SUCH_DEVICE -5 /* no such device exists */ +#define PCAP_ERROR_RFMON_NOTSUP -6 /* this device doesn't support rfmon (monitor) mode */ +#define PCAP_ERROR_NOT_RFMON -7 /* operation supported only in monitor mode */ +#define PCAP_ERROR_PERM_DENIED -8 /* no permission to open the device */ +#define PCAP_ERROR_IFACE_NOT_UP -9 /* interface isn't up */ +#define PCAP_ERROR_CANTSET_TSTAMP_TYPE -10 /* this device doesn't support setting the time stamp type */ +#define PCAP_ERROR_PROMISC_PERM_DENIED -11 /* you don't have permission to capture in promiscuous mode */ +#define PCAP_ERROR_TSTAMP_PRECISION_NOTSUP -12 /* the requested time stamp precision is not supported */ + +/* + * Warning codes for the pcap API. + * These will all be positive and non-zero, so they won't look like + * errors. + */ +#define PCAP_WARNING 1 /* generic warning code */ +#define PCAP_WARNING_PROMISC_NOTSUP 2 /* this device doesn't support promiscuous mode */ +#define PCAP_WARNING_TSTAMP_TYPE_NOTSUP 3 /* the requested time stamp type is not supported */ + +/* + * Value to pass to pcap_compile() as the netmask if you don't know what + * the netmask is. + */ +#define PCAP_NETMASK_UNKNOWN 0xffffffff + +PCAP_API char *pcap_lookupdev(char *); +PCAP_API int pcap_lookupnet(const char *, bpf_u_int32 *, bpf_u_int32 *, char *); + +PCAP_API pcap_t *pcap_create(const char *, char *); +PCAP_API int pcap_set_snaplen(pcap_t *, int); +PCAP_API int pcap_set_promisc(pcap_t *, int); +PCAP_API int pcap_can_set_rfmon(pcap_t *); +PCAP_API int pcap_set_rfmon(pcap_t *, int); +PCAP_API int pcap_set_timeout(pcap_t *, int); +PCAP_API int pcap_set_tstamp_type(pcap_t *, int); +PCAP_API int pcap_set_immediate_mode(pcap_t *, int); +PCAP_API int pcap_set_buffer_size(pcap_t *, int); +PCAP_API int pcap_set_tstamp_precision(pcap_t *, int); +PCAP_API int pcap_get_tstamp_precision(pcap_t *); +PCAP_API int pcap_activate(pcap_t *); + +PCAP_API int pcap_list_tstamp_types(pcap_t *, int **); +PCAP_API void pcap_free_tstamp_types(int *); +PCAP_API int pcap_tstamp_type_name_to_val(const char *); +PCAP_API const char *pcap_tstamp_type_val_to_name(int); +PCAP_API const char *pcap_tstamp_type_val_to_description(int); + +/* + * Time stamp types. + * Not all systems and interfaces will necessarily support all of these. + * + * A system that supports PCAP_TSTAMP_HOST is offering time stamps + * provided by the host machine, rather than by the capture device, + * but not committing to any characteristics of the time stamp; + * it will not offer any of the PCAP_TSTAMP_HOST_ subtypes. + * + * PCAP_TSTAMP_HOST_LOWPREC is a time stamp, provided by the host machine, + * that's low-precision but relatively cheap to fetch; it's normally done + * using the system clock, so it's normally synchronized with times you'd + * fetch from system calls. + * + * PCAP_TSTAMP_HOST_HIPREC is a time stamp, provided by the host machine, + * that's high-precision; it might be more expensive to fetch. It might + * or might not be synchronized with the system clock, and might have + * problems with time stamps for packets received on different CPUs, + * depending on the platform. + * + * PCAP_TSTAMP_ADAPTER is a high-precision time stamp supplied by the + * capture device; it's synchronized with the system clock. + * + * PCAP_TSTAMP_ADAPTER_UNSYNCED is a high-precision time stamp supplied by + * the capture device; it's not synchronized with the system clock. + * + * Note that time stamps synchronized with the system clock can go + * backwards, as the system clock can go backwards. If a clock is + * not in sync with the system clock, that could be because the + * system clock isn't keeping accurate time, because the other + * clock isn't keeping accurate time, or both. + * + * Note that host-provided time stamps generally correspond to the + * time when the time-stamping code sees the packet; this could + * be some unknown amount of time after the first or last bit of + * the packet is received by the network adapter, due to batching + * of interrupts for packet arrival, queueing delays, etc.. + */ +#define PCAP_TSTAMP_HOST 0 /* host-provided, unknown characteristics */ +#define PCAP_TSTAMP_HOST_LOWPREC 1 /* host-provided, low precision */ +#define PCAP_TSTAMP_HOST_HIPREC 2 /* host-provided, high precision */ +#define PCAP_TSTAMP_ADAPTER 3 /* device-provided, synced with the system clock */ +#define PCAP_TSTAMP_ADAPTER_UNSYNCED 4 /* device-provided, not synced with the system clock */ + +/* + * Time stamp resolution types. + * Not all systems and interfaces will necessarily support all of these + * resolutions when doing live captures; all of them can be requested + * when reading a savefile. + */ +#define PCAP_TSTAMP_PRECISION_MICRO 0 /* use timestamps with microsecond precision, default */ +#define PCAP_TSTAMP_PRECISION_NANO 1 /* use timestamps with nanosecond precision */ + +PCAP_API pcap_t *pcap_open_live(const char *, int, int, int, char *); +PCAP_API pcap_t *pcap_open_dead(int, int); +PCAP_API pcap_t *pcap_open_dead_with_tstamp_precision(int, int, u_int); +PCAP_API pcap_t *pcap_open_offline_with_tstamp_precision(const char *, u_int, char *); +PCAP_API pcap_t *pcap_open_offline(const char *, char *); +#ifdef _WIN32 + PCAP_API pcap_t *pcap_hopen_offline_with_tstamp_precision(intptr_t, u_int, char *); + PCAP_API pcap_t *pcap_hopen_offline(intptr_t, char *); + /* + * If we're building libpcap, these are internal routines in savefile.c, + * so we mustn't define them as macros. + */ + #ifndef BUILDING_PCAP + #define pcap_fopen_offline_with_tstamp_precision(f,p,b) \ + pcap_hopen_offline_with_tstamp_precision(_get_osfhandle(_fileno(f)), p, b) + #define pcap_fopen_offline(f,b) \ + pcap_hopen_offline(_get_osfhandle(_fileno(f)), b) + #endif +#else /*_WIN32*/ + PCAP_API pcap_t *pcap_fopen_offline_with_tstamp_precision(FILE *, u_int, char *); + PCAP_API pcap_t *pcap_fopen_offline(FILE *, char *); +#endif /*_WIN32*/ + +PCAP_API void pcap_close(pcap_t *); +PCAP_API int pcap_loop(pcap_t *, int, pcap_handler, u_char *); +PCAP_API int pcap_dispatch(pcap_t *, int, pcap_handler, u_char *); +PCAP_API const u_char *pcap_next(pcap_t *, struct pcap_pkthdr *); +PCAP_API int pcap_next_ex(pcap_t *, struct pcap_pkthdr **, const u_char **); +PCAP_API void pcap_breakloop(pcap_t *); +PCAP_API int pcap_stats(pcap_t *, struct pcap_stat *); +PCAP_API int pcap_setfilter(pcap_t *, struct bpf_program *); +PCAP_API int pcap_setdirection(pcap_t *, pcap_direction_t); +PCAP_API int pcap_getnonblock(pcap_t *, char *); +PCAP_API int pcap_setnonblock(pcap_t *, int, char *); +PCAP_API int pcap_inject(pcap_t *, const void *, size_t); +PCAP_API int pcap_sendpacket(pcap_t *, const u_char *, int); +PCAP_API const char *pcap_statustostr(int); +PCAP_API const char *pcap_strerror(int); +PCAP_API char *pcap_geterr(pcap_t *); +PCAP_API void pcap_perror(pcap_t *, const char *); +PCAP_API int pcap_compile(pcap_t *, struct bpf_program *, const char *, int, + bpf_u_int32); +PCAP_API int pcap_compile_nopcap(int, int, struct bpf_program *, + const char *, int, bpf_u_int32); +PCAP_API void pcap_freecode(struct bpf_program *); +PCAP_API int pcap_offline_filter(const struct bpf_program *, + const struct pcap_pkthdr *, const u_char *); +PCAP_API int pcap_datalink(pcap_t *); +PCAP_API int pcap_datalink_ext(pcap_t *); +PCAP_API int pcap_list_datalinks(pcap_t *, int **); +PCAP_API int pcap_set_datalink(pcap_t *, int); +PCAP_API void pcap_free_datalinks(int *); +PCAP_API int pcap_datalink_name_to_val(const char *); +PCAP_API const char *pcap_datalink_val_to_name(int); +PCAP_API const char *pcap_datalink_val_to_description(int); +PCAP_API int pcap_snapshot(pcap_t *); +PCAP_API int pcap_is_swapped(pcap_t *); +PCAP_API int pcap_major_version(pcap_t *); +PCAP_API int pcap_minor_version(pcap_t *); + +/* XXX */ +PCAP_API FILE *pcap_file(pcap_t *); +PCAP_API int pcap_fileno(pcap_t *); + +#ifdef _WIN32 + PCAP_API int pcap_wsockinit(void); +#endif + +PCAP_API pcap_dumper_t *pcap_dump_open(pcap_t *, const char *); +PCAP_API pcap_dumper_t *pcap_dump_fopen(pcap_t *, FILE *fp); +PCAP_API pcap_dumper_t *pcap_dump_open_append(pcap_t *, const char *); +PCAP_API FILE *pcap_dump_file(pcap_dumper_t *); +PCAP_API long pcap_dump_ftell(pcap_dumper_t *); +PCAP_API int pcap_dump_flush(pcap_dumper_t *); +PCAP_API void pcap_dump_close(pcap_dumper_t *); +PCAP_API void pcap_dump(u_char *, const struct pcap_pkthdr *, const u_char *); + +PCAP_API int pcap_findalldevs(pcap_if_t **, char *); +PCAP_API void pcap_freealldevs(pcap_if_t *); + +PCAP_API const char *pcap_lib_version(void); + +/* + * On at least some versions of NetBSD and QNX, we don't want to declare + * bpf_filter() here, as it's also be declared in , with a + * different signature, but, on other BSD-flavored UN*Xes, it's not + * declared in , so we *do* want to declare it here, so it's + * declared when we build pcap-bpf.c. + */ +#if !defined(__NetBSD__) && !defined(__QNX__) + PCAP_API u_int bpf_filter(const struct bpf_insn *, const u_char *, u_int, u_int); +#endif +PCAP_API int bpf_validate(const struct bpf_insn *f, int len); +PCAP_API char *bpf_image(const struct bpf_insn *, int); +PCAP_API void bpf_dump(const struct bpf_program *, int); + +#if defined(_WIN32) + + /* + * Win32 definitions + */ + + /*! + \brief A queue of raw packets that will be sent to the network with pcap_sendqueue_transmit(). + */ + struct pcap_send_queue + { + u_int maxlen; /* Maximum size of the the queue, in bytes. This + variable contains the size of the buffer field. */ + u_int len; /* Current size of the queue, in bytes. */ + char *buffer; /* Buffer containing the packets to be sent. */ + }; + + typedef struct pcap_send_queue pcap_send_queue; + + /*! + \brief This typedef is a support for the pcap_get_airpcap_handle() function + */ + #if !defined(AIRPCAP_HANDLE__EAE405F5_0171_9592_B3C2_C19EC426AD34__DEFINED_) + #define AIRPCAP_HANDLE__EAE405F5_0171_9592_B3C2_C19EC426AD34__DEFINED_ + typedef struct _AirpcapHandle *PAirpcapHandle; + #endif + + PCAP_API int pcap_setbuff(pcap_t *p, int dim); + PCAP_API int pcap_setmode(pcap_t *p, int mode); + PCAP_API int pcap_setmintocopy(pcap_t *p, int size); + + PCAP_API HANDLE pcap_getevent(pcap_t *p); + + PCAP_API int pcap_oid_get_request(pcap_t *, bpf_u_int32, void *, size_t *); + PCAP_API int pcap_oid_set_request(pcap_t *, bpf_u_int32, const void *, size_t *); + + PCAP_API pcap_send_queue* pcap_sendqueue_alloc(u_int memsize); + + PCAP_API void pcap_sendqueue_destroy(pcap_send_queue* queue); + + PCAP_API int pcap_sendqueue_queue(pcap_send_queue* queue, const struct pcap_pkthdr *pkt_header, const u_char *pkt_data); + + PCAP_API u_int pcap_sendqueue_transmit(pcap_t *p, pcap_send_queue* queue, int sync); + + PCAP_API struct pcap_stat *pcap_stats_ex(pcap_t *p, int *pcap_stat_size); + + PCAP_API int pcap_setuserbuffer(pcap_t *p, int size); + + PCAP_API int pcap_live_dump(pcap_t *p, char *filename, int maxsize, int maxpacks); + + PCAP_API int pcap_live_dump_ended(pcap_t *p, int sync); + + PCAP_API int pcap_start_oem(char* err_str, int flags); + + PCAP_API PAirpcapHandle pcap_get_airpcap_handle(pcap_t *p); + + #define MODE_CAPT 0 + #define MODE_STAT 1 + #define MODE_MON 2 + +#elif defined(MSDOS) + + /* + * MS-DOS definitions + */ + + PCAP_API int pcap_stats_ex (pcap_t *, struct pcap_stat_ex *); + PCAP_API void pcap_set_wait (pcap_t *p, void (*yield)(void), int wait); + PCAP_API u_long pcap_mac_packets (void); + +#else /* UN*X */ + + /* + * UN*X definitions + */ + + PCAP_API int pcap_get_selectable_fd(pcap_t *); + +#endif /* _WIN32/MSDOS/UN*X */ + +#ifdef HAVE_REMOTE + /* Includes most of the public stuff that is needed for the remote capture */ + #include +#endif /* HAVE_REMOTE */ + +#ifdef __cplusplus +} +#endif + +#endif /* lib_pcap_pcap_h */ diff --git a/include/npcap/pcap/sll.h b/include/npcap/pcap/sll.h new file mode 100644 index 0000000..b46d15f --- /dev/null +++ b/include/npcap/pcap/sll.h @@ -0,0 +1,129 @@ +/*- + * Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from the Stanford/CMU enet packet filter, + * (net/enet.c) distributed as part of 4.3BSD, and code contributed + * to Berkeley by Steven McCanne and Van Jacobson both of Lawrence + * Berkeley Laboratory. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * For captures on Linux cooked sockets, we construct a fake header + * that includes: + * + * a 2-byte "packet type" which is one of: + * + * LINUX_SLL_HOST packet was sent to us + * LINUX_SLL_BROADCAST packet was broadcast + * LINUX_SLL_MULTICAST packet was multicast + * LINUX_SLL_OTHERHOST packet was sent to somebody else + * LINUX_SLL_OUTGOING packet was sent *by* us; + * + * a 2-byte Ethernet protocol field; + * + * a 2-byte link-layer type; + * + * a 2-byte link-layer address length; + * + * an 8-byte source link-layer address, whose actual length is + * specified by the previous value. + * + * All fields except for the link-layer address are in network byte order. + * + * DO NOT change the layout of this structure, or change any of the + * LINUX_SLL_ values below. If you must change the link-layer header + * for a "cooked" Linux capture, introduce a new DLT_ type (ask + * "tcpdump-workers@lists.tcpdump.org" for one, so that you don't give it + * a value that collides with a value already being used), and use the + * new header in captures of that type, so that programs that can + * handle DLT_LINUX_SLL captures will continue to handle them correctly + * without any change, and so that capture files with different headers + * can be told apart and programs that read them can dissect the + * packets in them. + */ + +#ifndef lib_pcap_sll_h +#define lib_pcap_sll_h + +/* + * A DLT_LINUX_SLL fake link-layer header. + */ +#define SLL_HDR_LEN 16 /* total header length */ +#define SLL_ADDRLEN 8 /* length of address field */ + +struct sll_header { + u_int16_t sll_pkttype; /* packet type */ + u_int16_t sll_hatype; /* link-layer address type */ + u_int16_t sll_halen; /* link-layer address length */ + u_int8_t sll_addr[SLL_ADDRLEN]; /* link-layer address */ + u_int16_t sll_protocol; /* protocol */ +}; + +/* + * The LINUX_SLL_ values for "sll_pkttype"; these correspond to the + * PACKET_ values on Linux, but are defined here so that they're + * available even on systems other than Linux, and so that they + * don't change even if the PACKET_ values change. + */ +#define LINUX_SLL_HOST 0 +#define LINUX_SLL_BROADCAST 1 +#define LINUX_SLL_MULTICAST 2 +#define LINUX_SLL_OTHERHOST 3 +#define LINUX_SLL_OUTGOING 4 + +/* + * The LINUX_SLL_ values for "sll_protocol"; these correspond to the + * ETH_P_ values on Linux, but are defined here so that they're + * available even on systems other than Linux. We assume, for now, + * that the ETH_P_ values won't change in Linux; if they do, then: + * + * if we don't translate them in "pcap-linux.c", capture files + * won't necessarily be readable if captured on a system that + * defines ETH_P_ values that don't match these values; + * + * if we do translate them in "pcap-linux.c", that makes life + * unpleasant for the BPF code generator, as the values you test + * for in the kernel aren't the values that you test for when + * reading a capture file, so the fixup code run on BPF programs + * handed to the kernel ends up having to do more work. + * + * Add other values here as necessary, for handling packet types that + * might show up on non-Ethernet, non-802.x networks. (Not all the ones + * in the Linux "if_ether.h" will, I suspect, actually show up in + * captures.) + */ +#define LINUX_SLL_P_802_3 0x0001 /* Novell 802.3 frames without 802.2 LLC header */ +#define LINUX_SLL_P_802_2 0x0004 /* 802.2 frames (not D/I/X Ethernet) */ +#define LINUX_SLL_P_CAN 0x000C /* CAN frames, with SocketCAN pseudo-headers */ +#define LINUX_SLL_P_CANFD 0x000D /* CAN FD frames, with SocketCAN pseudo-headers */ + +#endif diff --git a/include/npcap/pcap/usb.h b/include/npcap/pcap/usb.h new file mode 100644 index 0000000..26a9046 --- /dev/null +++ b/include/npcap/pcap/usb.h @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2006 Paolo Abeni (Italy) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Basic USB data struct + * By Paolo Abeni + */ + +#ifndef lib_pcap_usb_h +#define lib_pcap_usb_h + +/* + * possible transfer mode + */ +#define URB_TRANSFER_IN 0x80 +#define URB_ISOCHRONOUS 0x0 +#define URB_INTERRUPT 0x1 +#define URB_CONTROL 0x2 +#define URB_BULK 0x3 + +/* + * possible event type + */ +#define URB_SUBMIT 'S' +#define URB_COMPLETE 'C' +#define URB_ERROR 'E' + +/* + * USB setup header as defined in USB specification. + * Appears at the front of each Control S-type packet in DLT_USB captures. + */ +typedef struct _usb_setup { + u_int8_t bmRequestType; + u_int8_t bRequest; + u_int16_t wValue; + u_int16_t wIndex; + u_int16_t wLength; +} pcap_usb_setup; + +/* + * Information from the URB for Isochronous transfers. + */ +typedef struct _iso_rec { + int32_t error_count; + int32_t numdesc; +} iso_rec; + +/* + * Header prepended by linux kernel to each event. + * Appears at the front of each packet in DLT_USB_LINUX captures. + */ +typedef struct _usb_header { + u_int64_t id; + u_int8_t event_type; + u_int8_t transfer_type; + u_int8_t endpoint_number; + u_int8_t device_address; + u_int16_t bus_id; + char setup_flag;/*if !=0 the urb setup header is not present*/ + char data_flag; /*if !=0 no urb data is present*/ + int64_t ts_sec; + int32_t ts_usec; + int32_t status; + u_int32_t urb_len; + u_int32_t data_len; /* amount of urb data really present in this event*/ + pcap_usb_setup setup; +} pcap_usb_header; + +/* + * Header prepended by linux kernel to each event for the 2.6.31 + * and later kernels; for the 2.6.21 through 2.6.30 kernels, the + * "iso_rec" information, and the fields starting with "interval" + * are zeroed-out padding fields. + * + * Appears at the front of each packet in DLT_USB_LINUX_MMAPPED captures. + */ +typedef struct _usb_header_mmapped { + u_int64_t id; + u_int8_t event_type; + u_int8_t transfer_type; + u_int8_t endpoint_number; + u_int8_t device_address; + u_int16_t bus_id; + char setup_flag;/*if !=0 the urb setup header is not present*/ + char data_flag; /*if !=0 no urb data is present*/ + int64_t ts_sec; + int32_t ts_usec; + int32_t status; + u_int32_t urb_len; + u_int32_t data_len; /* amount of urb data really present in this event*/ + union { + pcap_usb_setup setup; + iso_rec iso; + } s; + int32_t interval; /* for Interrupt and Isochronous events */ + int32_t start_frame; /* for Isochronous events */ + u_int32_t xfer_flags; /* copy of URB's transfer flags */ + u_int32_t ndesc; /* number of isochronous descriptors */ +} pcap_usb_header_mmapped; + +/* + * Isochronous descriptors; for isochronous transfers there might be + * one or more of these at the beginning of the packet data. The + * number of descriptors is given by the "ndesc" field in the header; + * as indicated, in older kernels that don't put the descriptors at + * the beginning of the packet, that field is zeroed out, so that field + * can be trusted even in captures from older kernels. + */ +typedef struct _usb_isodesc { + int32_t status; + u_int32_t offset; + u_int32_t len; + u_int8_t pad[4]; +} usb_isodesc; + +#endif diff --git a/include/npcap/pcap/vlan.h b/include/npcap/pcap/vlan.h new file mode 100644 index 0000000..021f612 --- /dev/null +++ b/include/npcap/pcap/vlan.h @@ -0,0 +1,44 @@ +/*- + * Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lib_pcap_vlan_h +#define lib_pcap_vlan_h + +struct vlan_tag { + u_int16_t vlan_tpid; /* ETH_P_8021Q */ + u_int16_t vlan_tci; /* VLAN TCI */ +}; + +#define VLAN_TAG_LEN 4 + +#endif diff --git a/include/npcap/remote-ext.h b/include/npcap/remote-ext.h new file mode 100644 index 0000000..ed2f9bb --- /dev/null +++ b/include/npcap/remote-ext.h @@ -0,0 +1,467 @@ +/* + * Copyright (c) 2002 - 2003 + * NetGroup, Politecnico di Torino (Italy) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Politecnico di Torino nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#ifndef __REMOTE_EXT_H__ +#define __REMOTE_EXT_H__ + + +#ifndef HAVE_REMOTE +#error Please do not include this file directly. Just define HAVE_REMOTE and then include pcap.h +#endif + +/*// Definition for Microsoft Visual Studio */ +#if _MSC_VER > 1000 +#pragma once +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * \file remote-ext.h + * + * The goal of this file it to include most of the new definitions that should be + * placed into the pcap.h file. + * + * It includes all new definitions (structures and functions like pcap_open(). + * Some of the functions are not really a remote feature, but, right now, + * they are placed here. + */ + + + +/*// All this stuff is public */ +/* + * \addtogroup remote_struct + * \{ + */ + + + + +/* + * \brief Defines the maximum buffer size in which address, port, interface names are kept. + * + * In case the adapter name or such is larger than this value, it is truncated. + * This is not used by the user; however it must be aware that an hostname / interface + * name longer than this value will be truncated. + */ +#define PCAP_BUF_SIZE 1024 + + +/* + * \addtogroup remote_source_ID + * \{ + */ + + +/* + * \brief Internal representation of the type of source in use (file, + * remote/local interface). + * + * This indicates a file, i.e. the user want to open a capture from a local file. + */ +#define PCAP_SRC_FILE 2 +/* + * \brief Internal representation of the type of source in use (file, + * remote/local interface). + * + * This indicates a local interface, i.e. the user want to open a capture from + * a local interface. This does not involve the RPCAP protocol. + */ +#define PCAP_SRC_IFLOCAL 3 +/* + * \brief Internal representation of the type of source in use (file, + * remote/local interface). + * + * This indicates a remote interface, i.e. the user want to open a capture from + * an interface on a remote host. This does involve the RPCAP protocol. + */ +#define PCAP_SRC_IFREMOTE 4 + +/* + * \} + */ + + + +/* \addtogroup remote_source_string + * + * The formats allowed by the pcap_open() are the following: + * - file://path_and_filename [opens a local file] + * - rpcap://devicename [opens the selected device devices available on the local host, without using the RPCAP protocol] + * - rpcap://host/devicename [opens the selected device available on a remote host] + * - rpcap://host:port/devicename [opens the selected device available on a remote host, using a non-standard port for RPCAP] + * - adaptername [to open a local adapter; kept for compability, but it is strongly discouraged] + * - (NULL) [to open the first local adapter; kept for compability, but it is strongly discouraged] + * + * The formats allowed by the pcap_findalldevs_ex() are the following: + * - file://folder/ [lists all the files in the given folder] + * - rpcap:// [lists all local adapters] + * - rpcap://host:port/ [lists the devices available on a remote host] + * + * Referring to the 'host' and 'port' parameters, they can be either numeric or literal. Since + * IPv6 is fully supported, these are the allowed formats: + * + * - host (literal): e.g. host.foo.bar + * - host (numeric IPv4): e.g. 10.11.12.13 + * - host (numeric IPv4, IPv6 style): e.g. [10.11.12.13] + * - host (numeric IPv6): e.g. [1:2:3::4] + * - port: can be either numeric (e.g. '80') or literal (e.g. 'http') + * + * Here you find some allowed examples: + * - rpcap://host.foo.bar/devicename [everything literal, no port number] + * - rpcap://host.foo.bar:1234/devicename [everything literal, with port number] + * - rpcap://10.11.12.13/devicename [IPv4 numeric, no port number] + * - rpcap://10.11.12.13:1234/devicename [IPv4 numeric, with port number] + * - rpcap://[10.11.12.13]:1234/devicename [IPv4 numeric with IPv6 format, with port number] + * - rpcap://[1:2:3::4]/devicename [IPv6 numeric, no port number] + * - rpcap://[1:2:3::4]:1234/devicename [IPv6 numeric, with port number] + * - rpcap://[1:2:3::4]:http/devicename [IPv6 numeric, with literal port number] + * + * \{ + */ + + +/* + * \brief String that will be used to determine the type of source in use (file, + * remote/local interface). + * + * This string will be prepended to the interface name in order to create a string + * that contains all the information required to open the source. + * + * This string indicates that the user wants to open a capture from a local file. + */ +#define PCAP_SRC_FILE_STRING "file://" +/* + * \brief String that will be used to determine the type of source in use (file, + * remote/local interface). + * + * This string will be prepended to the interface name in order to create a string + * that contains all the information required to open the source. + * + * This string indicates that the user wants to open a capture from a network interface. + * This string does not necessarily involve the use of the RPCAP protocol. If the + * interface required resides on the local host, the RPCAP protocol is not involved + * and the local functions are used. + */ +#define PCAP_SRC_IF_STRING "rpcap://" + +/* + * \} + */ + + + + + +/* + * \addtogroup remote_open_flags + * \{ + */ + +/* + * \brief Defines if the adapter has to go in promiscuous mode. + * + * It is '1' if you have to open the adapter in promiscuous mode, '0' otherwise. + * Note that even if this parameter is false, the interface could well be in promiscuous + * mode for some other reason (for example because another capture process with + * promiscuous mode enabled is currently using that interface). + * On on Linux systems with 2.2 or later kernels (that have the "any" device), this + * flag does not work on the "any" device; if an argument of "any" is supplied, + * the 'promisc' flag is ignored. + */ +#define PCAP_OPENFLAG_PROMISCUOUS 1 + +/* + * \brief Defines if the data transfer (in case of a remote + * capture) has to be done with UDP protocol. + * + * If it is '1' if you want a UDP data connection, '0' if you want + * a TCP data connection; control connection is always TCP-based. + * A UDP connection is much lighter, but it does not guarantee that all + * the captured packets arrive to the client workstation. Moreover, + * it could be harmful in case of network congestion. + * This flag is meaningless if the source is not a remote interface. + * In that case, it is simply ignored. + */ +#define PCAP_OPENFLAG_DATATX_UDP 2 + + +/* + * \brief Defines if the remote probe will capture its own generated traffic. + * + * In case the remote probe uses the same interface to capture traffic and to send + * data back to the caller, the captured traffic includes the RPCAP traffic as well. + * If this flag is turned on, the RPCAP traffic is excluded from the capture, so that + * the trace returned back to the collector is does not include this traffic. + */ +#define PCAP_OPENFLAG_NOCAPTURE_RPCAP 4 + +/* + * \brief Defines if the local adapter will capture its own generated traffic. + * + * This flag tells the underlying capture driver to drop the packets that were sent by itself. + * This is useful when building applications like bridges, that should ignore the traffic + * they just sent. + */ +#define PCAP_OPENFLAG_NOCAPTURE_LOCAL 8 + +/* + * \brief This flag configures the adapter for maximum responsiveness. + * + * In presence of a large value for nbytes, WinPcap waits for the arrival of several packets before + * copying the data to the user. This guarantees a low number of system calls, i.e. lower processor usage, + * i.e. better performance, which is good for applications like sniffers. If the user sets the + * PCAP_OPENFLAG_MAX_RESPONSIVENESS flag, the capture driver will copy the packets as soon as the application + * is ready to receive them. This is suggested for real time applications (like, for example, a bridge) + * that need the best responsiveness. + */ +#define PCAP_OPENFLAG_MAX_RESPONSIVENESS 16 + +/* + * \} + */ + + +/* + * \addtogroup remote_samp_methods + * \{ + */ + +/* + *\brief No sampling has to be done on the current capture. + * + * In this case, no sampling algorithms are applied to the current capture. + */ +#define PCAP_SAMP_NOSAMP 0 + +/* + * \brief It defines that only 1 out of N packets must be returned to the user. + * + * In this case, the 'value' field of the 'pcap_samp' structure indicates the + * number of packets (minus 1) that must be discarded before one packet got accepted. + * In other words, if 'value = 10', the first packet is returned to the caller, while + * the following 9 are discarded. + */ +#define PCAP_SAMP_1_EVERY_N 1 + +/* + * \brief It defines that we have to return 1 packet every N milliseconds. + * + * In this case, the 'value' field of the 'pcap_samp' structure indicates the 'waiting + * time' in milliseconds before one packet got accepted. + * In other words, if 'value = 10', the first packet is returned to the caller; the next + * returned one will be the first packet that arrives when 10ms have elapsed. + */ +#define PCAP_SAMP_FIRST_AFTER_N_MS 2 + +/* + * \} + */ + + +/* + * \addtogroup remote_auth_methods + * \{ + */ + +/* + * \brief It defines the NULL authentication. + * + * This value has to be used within the 'type' member of the pcap_rmtauth structure. + * The 'NULL' authentication has to be equal to 'zero', so that old applications + * can just put every field of struct pcap_rmtauth to zero, and it does work. + */ +#define RPCAP_RMTAUTH_NULL 0 +/* + * \brief It defines the username/password authentication. + * + * With this type of authentication, the RPCAP protocol will use the username/ + * password provided to authenticate the user on the remote machine. If the + * authentication is successful (and the user has the right to open network devices) + * the RPCAP connection will continue; otherwise it will be dropped. + * + * This value has to be used within the 'type' member of the pcap_rmtauth structure. + */ +#define RPCAP_RMTAUTH_PWD 1 + +/* + * \} + */ + + + + +/* + * \brief This structure keeps the information needed to autheticate + * the user on a remote machine. + * + * The remote machine can either grant or refuse the access according + * to the information provided. + * In case the NULL authentication is required, both 'username' and + * 'password' can be NULL pointers. + * + * This structure is meaningless if the source is not a remote interface; + * in that case, the functions which requires such a structure can accept + * a NULL pointer as well. + */ +struct pcap_rmtauth +{ + /* + * \brief Type of the authentication required. + * + * In order to provide maximum flexibility, we can support different types + * of authentication based on the value of this 'type' variable. The currently + * supported authentication methods are defined into the + * \link remote_auth_methods Remote Authentication Methods Section\endlink. + */ + int type; + /* + * \brief Zero-terminated string containing the username that has to be + * used on the remote machine for authentication. + * + * This field is meaningless in case of the RPCAP_RMTAUTH_NULL authentication + * and it can be NULL. + */ + char *username; + /* + * \brief Zero-terminated string containing the password that has to be + * used on the remote machine for authentication. + * + * This field is meaningless in case of the RPCAP_RMTAUTH_NULL authentication + * and it can be NULL. + */ + char *password; +}; + + +/* + * \brief This structure defines the information related to sampling. + * + * In case the sampling is requested, the capturing device should read + * only a subset of the packets coming from the source. The returned packets depend + * on the sampling parameters. + * + * \warning The sampling process is applied after the filtering process. + * In other words, packets are filtered first, then the sampling process selects a + * subset of the 'filtered' packets and it returns them to the caller. + */ +struct pcap_samp +{ + /* + * Method used for sampling. Currently, the supported methods are listed in the + * \link remote_samp_methods Sampling Methods Section\endlink. + */ + int method; + + /* + * This value depends on the sampling method defined. For its meaning, please check + * at the \link remote_samp_methods Sampling Methods Section\endlink. + */ + int value; +}; + + + + +// Maximum length of an host name (needed for the RPCAP active mode) +#define RPCAP_HOSTLIST_SIZE 1024 + + +/* + * \} + */ // end of public documentation + + +// Exported functions + + + +/* + * \name New WinPcap functions + * + * This section lists the new functions that are able to help considerably in writing + * WinPcap programs because of their easiness of use. + */ +// \{ +PCAP_API pcap_t *pcap_open(const char *source, int snaplen, int flags, int read_timeout, struct pcap_rmtauth *auth, char *errbuf); +PCAP_API int pcap_createsrcstr(char *source, int type, const char *host, const char *port, const char *name, char *errbuf); +PCAP_API int pcap_parsesrcstr(const char *source, int *type, char *host, char *port, char *name, char *errbuf); +PCAP_API int pcap_findalldevs_ex(char *source, struct pcap_rmtauth *auth, pcap_if_t **alldevs, char *errbuf); +PCAP_API struct pcap_samp *pcap_setsampling(pcap_t *p); + +// \} +// End of new WinPcap functions + +/* + * \name Remote Capture functions + */ + +/* + * Some minor differences between UN*X sockets and and Winsock sockets. + */ +#ifndef _WIN32 + /*! + * \brief In Winsock, a socket handle is of type SOCKET; in UN*X, it's + * a file descriptor, and therefore a signed integer. + * We define SOCKET to be a signed integer on UN*X, so that it can + * be used on both platforms. + */ + #define SOCKET int + + /*! + * \brief In Winsock, the error return if socket() fails is INVALID_SOCKET; + * in UN*X, it's -1. + * We define INVALID_SOCKET to be -1 on UN*X, so that it can be used on + * both platforms. + */ + #define INVALID_SOCKET -1 +#endif + +// \{ +PCAP_API SOCKET pcap_remoteact_accept(const char *address, const char *port, const char *hostlist, char *connectinghost, struct pcap_rmtauth *auth, char *errbuf); +PCAP_API int pcap_remoteact_list(char *hostlist, char sep, int size, char *errbuf); +PCAP_API int pcap_remoteact_close(const char *host, char *errbuf); +PCAP_API void pcap_remoteact_cleanup(); +// \} +// End of remote capture functions + +#ifdef __cplusplus +} +#endif + + +#endif + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 03c987f..b6ec3bc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,9 +1,3 @@ -cmake_minimum_required(VERSION 3.1) -project(gsplus VERSION 0.14) - -# -# cmake -DCMAKE_BUILD_TYPE=Release -# INCLUDE (CheckFunctionExists) INCLUDE (CheckLibraryExists) @@ -49,8 +43,8 @@ pkg_check_modules(FREETYPE2 freetype2) # run ccmake, cmake -LH, or cmake -D... # set(DRIVER "SDL" CACHE STRING "Driver (SDL, X11, WIN32, FB, or HEADLESS") -option(DEBUGGER "Enable the debugger" ON) -option(HOST_FST "Enable host fst support" ON) +option(WITH_DEBUGGER "Enable the debugger" ON) +option(WITH_HOST_FST "Enable host fst support" ON) option(TOGGLE_STATUS "Enable F10 Toggle Status support (win32/x11)" OFF) option(WITH_RAWNET "Enable Uthernet emulation" OFF) option(WITH_ATBRIDGE "Enable AT Bridge" OFF) @@ -78,20 +72,6 @@ add_custom_command( WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) -if(CYGWIN OR MSYS) - set(WIN32 1) - add_definitions(-DWIN32 -D_WIN32) -endif() - -if(WIN32) - # WTF? - #add_definitions(-D__USE_W32_SOCKETS -D_WINSOCK2API_) -endif() - -if(MSVC) - add_definitions(-D_CRT_SECURE_NO_WARNINGS) -endif() - add_executable(to_pro to_pro.c) add_executable(partls partls.c) @@ -137,8 +117,8 @@ add_executable(GSplus WIN32 MACOSX_BUNDLE smartport.c sound.c sound_driver.c video.c scc_socket_driver.c glog.c imagewriter.cpp scc_imagewriter.c scc_llap.c options.c - $<$:debug.c> - $<$:${host_fst_code}> + $<$:debug.c> + $<$:${host_fst_code}> ${driver_code} ${generated_headers} @@ -182,7 +162,7 @@ if (TOGGLE_STATUS) target_compile_definitions(GSplus PUBLIC TOGGLE_STATUS) endif() -if (DEBUGGER) +if (WITH_DEBUGGER) target_compile_definitions(GSplus PRIVATE GSPLUS_DEBUGGER) endif() diff --git a/src/arch/win32/bittypes.h b/src/arch/win32/bittypes.h deleted file mode 100644 index 654f37b..0000000 --- a/src/arch/win32/bittypes.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 1999 WIDE Project. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the project nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ -#ifndef _BITTYPES_H -#define _BITTYPES_H - -#ifndef HAVE_U_INT8_T - -#if SIZEOF_CHAR == 1 -typedef unsigned char u_int8_t; -typedef signed char int8_t; -#elif SIZEOF_INT == 1 -typedef unsigned int u_int8_t; -typedef signed int int8_t; -#else /* XXX */ -#error "there's no appropriate type for u_int8_t" -#endif -#define HAVE_U_INT8_T 1 -#define HAVE_INT8_T 1 - -#endif /* HAVE_U_INT8_T */ - -#ifndef HAVE_U_INT16_T - -#if SIZEOF_SHORT == 2 -typedef unsigned short u_int16_t; -typedef signed short int16_t; -#elif SIZEOF_INT == 2 -typedef unsigned int u_int16_t; -typedef signed int int16_t; -#elif SIZEOF_CHAR == 2 -typedef unsigned char u_int16_t; -typedef signed char int16_t; -#else /* XXX */ -#error "there's no appropriate type for u_int16_t" -#endif -#define HAVE_U_INT16_T 1 -#define HAVE_INT16_T 1 - -#endif /* HAVE_U_INT16_T */ - -#ifndef HAVE_U_INT32_T - -#if SIZEOF_INT == 4 -typedef unsigned int u_int32_t; -typedef signed int int32_t; -#elif SIZEOF_LONG == 4 -typedef unsigned long u_int32_t; -typedef signed long int32_t; -#elif SIZEOF_SHORT == 4 -typedef unsigned short u_int32_t; -typedef signed short int32_t; -#else /* XXX */ -#error "there's no appropriate type for u_int32_t" -#endif -#define HAVE_U_INT32_T 1 -#define HAVE_INT32_T 1 - -#endif /* HAVE_U_INT32_T */ - -#endif /* _BITTYPES_H */ diff --git a/src/arch/win32/bpf.h b/src/arch/win32/bpf.h deleted file mode 100644 index 51f9ecb..0000000 --- a/src/arch/win32/bpf.h +++ /dev/null @@ -1,523 +0,0 @@ -/*- - * Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from the Stanford/CMU enet packet filter, - * (net/enet.c) distributed as part of 4.3BSD, and code contributed - * to Berkeley by Steven McCanne and Van Jacobson both of Lawrence - * Berkeley Laboratory. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)bpf.h 7.1 (Berkeley) 5/7/91 - * - * @(#) $Header$ (LBL) - */ - -#ifndef BPF_MAJOR_VERSION - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef _MSC_VER -#include -typedef unsigned int u_int; -typedef unsigned char u_char; -typedef unsigned short u_short; -#endif - -/* BSD style release date */ -#define BPF_RELEASE 199606 - -typedef int bpf_int32; -typedef u_int bpf_u_int32; - -/* - * Alignment macros. BPF_WORDALIGN rounds up to the next - * even multiple of BPF_ALIGNMENT. - */ -#ifndef __NetBSD__ -#define BPF_ALIGNMENT sizeof(bpf_int32) -#else -#define BPF_ALIGNMENT sizeof(long) -#endif -#define BPF_WORDALIGN(x) (((x)+(BPF_ALIGNMENT-1))&~(BPF_ALIGNMENT-1)) - -#define BPF_MAXINSNS 512 -#define BPF_MAXBUFSIZE 0x8000 -#define BPF_MINBUFSIZE 32 - -/* - * Structure for BIOCSETF. - */ -struct bpf_program { - u_int bf_len; - struct bpf_insn *bf_insns; -}; - -/* - * Struct returned by BIOCGSTATS. - */ -struct bpf_stat { - u_int bs_recv; /* number of packets received */ - u_int bs_drop; /* number of packets dropped */ -}; - -/* - * Struct return by BIOCVERSION. This represents the version number of - * the filter language described by the instruction encodings below. - * bpf understands a program iff kernel_major == filter_major && - * kernel_minor >= filter_minor, that is, if the value returned by the - * running kernel has the same major number and a minor number equal - * equal to or less than the filter being downloaded. Otherwise, the - * results are undefined, meaning an error may be returned or packets - * may be accepted haphazardly. - * It has nothing to do with the source code version. - */ -struct bpf_version { - u_short bv_major; - u_short bv_minor; -}; -/* Current version number of filter architecture. */ -#define BPF_MAJOR_VERSION 1 -#define BPF_MINOR_VERSION 1 - -/* - * BPF ioctls - * - * The first set is for compatibility with Sun's pcc style - * header files. If your using gcc, we assume that you - * have run fixincludes so the latter set should work. - */ -#if (defined(sun) || defined(ibm032)) && !defined(__GNUC__) -#define BIOCGBLEN _IOR(B,102, u_int) -#define BIOCSBLEN _IOWR(B,102, u_int) -#define BIOCSETF _IOW(B,103, struct bpf_program) -#define BIOCFLUSH _IO(B,104) -#define BIOCPROMISC _IO(B,105) -#define BIOCGDLT _IOR(B,106, u_int) -#define BIOCGETIF _IOR(B,107, struct ifreq) -#define BIOCSETIF _IOW(B,108, struct ifreq) -#define BIOCSRTIMEOUT _IOW(B,109, struct timeval) -#define BIOCGRTIMEOUT _IOR(B,110, struct timeval) -#define BIOCGSTATS _IOR(B,111, struct bpf_stat) -#define BIOCIMMEDIATE _IOW(B,112, u_int) -#define BIOCVERSION _IOR(B,113, struct bpf_version) -#define BIOCSTCPF _IOW(B,114, struct bpf_program) -#define BIOCSUDPF _IOW(B,115, struct bpf_program) -#else -#define BIOCGBLEN _IOR('B',102, u_int) -#define BIOCSBLEN _IOWR('B',102, u_int) -#define BIOCSETF _IOW('B',103, struct bpf_program) -#define BIOCFLUSH _IO('B',104) -#define BIOCPROMISC _IO('B',105) -#define BIOCGDLT _IOR('B',106, u_int) -#define BIOCGETIF _IOR('B',107, struct ifreq) -#define BIOCSETIF _IOW('B',108, struct ifreq) -#define BIOCSRTIMEOUT _IOW('B',109, struct timeval) -#define BIOCGRTIMEOUT _IOR('B',110, struct timeval) -#define BIOCGSTATS _IOR('B',111, struct bpf_stat) -#define BIOCIMMEDIATE _IOW('B',112, u_int) -#define BIOCVERSION _IOR('B',113, struct bpf_version) -#define BIOCSTCPF _IOW('B',114, struct bpf_program) -#define BIOCSUDPF _IOW('B',115, struct bpf_program) -#endif - -/* - * Structure prepended to each packet. - */ -struct bpf_hdr { - struct timeval bh_tstamp; /* time stamp */ - bpf_u_int32 bh_caplen; /* length of captured portion */ - bpf_u_int32 bh_datalen; /* original length of packet */ - u_short bh_hdrlen; /* length of bpf header (this struct - plus alignment padding) */ -}; -/* - * Because the structure above is not a multiple of 4 bytes, some compilers - * will insist on inserting padding; hence, sizeof(struct bpf_hdr) won't work. - * Only the kernel needs to know about it; applications use bh_hdrlen. - */ -#if defined(KERNEL) || defined(_KERNEL) -#define SIZEOF_BPF_HDR 18 -#endif - -/* - * Data-link level type codes. - */ - -/* - * These are the types that are the same on all platforms; on other - * platforms, a should be supplied that defines the additional - * DLT_* codes appropriately for that platform (the BSDs, for example, - * should not just pick up this version of "bpf.h"; they should also define - * the additional DLT_* codes used by their kernels, as well as the values - * defined here - and, if the values they use for particular DLT_ types - * differ from those here, they should use their values, not the ones - * here). - */ -#define DLT_NULL 0 /* no link-layer encapsulation */ -#define DLT_EN10MB 1 /* Ethernet (10Mb) */ -#define DLT_EN3MB 2 /* Experimental Ethernet (3Mb) */ -#define DLT_AX25 3 /* Amateur Radio AX.25 */ -#define DLT_PRONET 4 /* Proteon ProNET Token Ring */ -#define DLT_CHAOS 5 /* Chaos */ -#define DLT_IEEE802 6 /* IEEE 802 Networks */ -#define DLT_ARCNET 7 /* ARCNET, with BSD-style header */ -#define DLT_SLIP 8 /* Serial Line IP */ -#define DLT_PPP 9 /* Point-to-point Protocol */ -#define DLT_FDDI 10 /* FDDI */ - -/* - * These are values from the traditional libpcap "bpf.h". - * Ports of this to particular platforms should replace these definitions - * with the ones appropriate to that platform, if the values are - * different on that platform. - */ -#define DLT_ATM_RFC1483 11 /* LLC/SNAP encapsulated atm */ -#define DLT_RAW 12 /* raw IP */ - -/* - * These are values from BSD/OS's "bpf.h". - * These are not the same as the values from the traditional libpcap - * "bpf.h"; however, these values shouldn't be generated by any - * OS other than BSD/OS, so the correct values to use here are the - * BSD/OS values. - * - * Platforms that have already assigned these values to other - * DLT_ codes, however, should give these codes the values - * from that platform, so that programs that use these codes will - * continue to compile - even though they won't correctly read - * files of these types. - */ -#ifdef __NetBSD__ -#ifndef DLT_SLIP_BSDOS -#define DLT_SLIP_BSDOS 13 /* BSD/OS Serial Line IP */ -#define DLT_PPP_BSDOS 14 /* BSD/OS Point-to-point Protocol */ -#endif -#else -#define DLT_SLIP_BSDOS 15 /* BSD/OS Serial Line IP */ -#define DLT_PPP_BSDOS 16 /* BSD/OS Point-to-point Protocol */ -#endif - -#define DLT_ATM_CLIP 19 /* Linux Classical-IP over ATM */ - -/* - * These values are defined by NetBSD; other platforms should refrain from - * using them for other purposes, so that NetBSD savefiles with link - * types of 50 or 51 can be read as this type on all platforms. - */ -#define DLT_PPP_SERIAL 50 /* PPP over serial with HDLC encapsulation */ -#define DLT_PPP_ETHER 51 /* PPP over Ethernet */ - -/* - * Values between 100 and 103 are used in capture file headers as - * link-layer types corresponding to DLT_ types that differ - * between platforms; don't use those values for new DLT_ new types. - */ - -/* - * This value was defined by libpcap 0.5; platforms that have defined - * it with a different value should define it here with that value - - * a link type of 104 in a save file will be mapped to DLT_C_HDLC, - * whatever value that happens to be, so programs will correctly - * handle files with that link type regardless of the value of - * DLT_C_HDLC. - * - * The name DLT_C_HDLC was used by BSD/OS; we use that name for source - * compatibility with programs written for BSD/OS. - * - * libpcap 0.5 defined it as DLT_CHDLC; we define DLT_CHDLC as well, - * for source compatibility with programs written for libpcap 0.5. - */ -#define DLT_C_HDLC 104 /* Cisco HDLC */ -#define DLT_CHDLC DLT_C_HDLC - -#define DLT_IEEE802_11 105 /* IEEE 802.11 wireless */ - -/* - * 106 is reserved for Linux Classical IP over ATM; it's like DLT_RAW, - * except when it isn't. (I.e., sometimes it's just raw IP, and - * sometimes it isn't.) We currently handle it as DLT_LINUX_SLL, - * so that we don't have to worry about the link-layer header.) - */ - -/* - * Frame Relay; BSD/OS has a DLT_FR with a value of 11, but that collides - * with other values. - * DLT_FR and DLT_FRELAY packets start with the Q.922 Frame Relay header - * (DLCI, etc.). - */ -#define DLT_FRELAY 107 - -/* - * OpenBSD DLT_LOOP, for loopback devices; it's like DLT_NULL, except - * that the AF_ type in the link-layer header is in network byte order. - * - * OpenBSD defines it as 12, but that collides with DLT_RAW, so we - * define it as 108 here. If OpenBSD picks up this file, it should - * define DLT_LOOP as 12 in its version, as per the comment above - - * and should not use 108 as a DLT_ value. - */ -#define DLT_LOOP 108 - -/* - * Values between 109 and 112 are used in capture file headers as - * link-layer types corresponding to DLT_ types that might differ - * between platforms; don't use those values for new DLT_ types - * other than the corresponding DLT_ types. - */ - -/* - * This is for Linux cooked sockets. - */ -#define DLT_LINUX_SLL 113 - -/* - * Apple LocalTalk hardware. - */ -#define DLT_LTALK 114 - -/* - * Acorn Econet. - */ -#define DLT_ECONET 115 - -/* - * Reserved for use with OpenBSD ipfilter. - */ -#define DLT_IPFILTER 116 - -/* - * Reserved for use in capture-file headers as a link-layer type - * corresponding to OpenBSD DLT_PFLOG; DLT_PFLOG is 17 in OpenBSD, - * but that's DLT_LANE8023 in SuSE 6.3, so we can't use 17 for it - * in capture-file headers. - */ -#define DLT_PFLOG 117 - -/* - * Registered for Cisco-internal use. - */ -#define DLT_CISCO_IOS 118 - -/* - * Reserved for 802.11 cards using the Prism II chips, with a link-layer - * header including Prism monitor mode information plus an 802.11 - * header. - */ -#define DLT_PRISM_HEADER 119 - -/* - * Reserved for Aironet 802.11 cards, with an Aironet link-layer header - * (see Doug Ambrisko's FreeBSD patches). - */ -#define DLT_AIRONET_HEADER 120 - -/* - * Reserved for Siemens HiPath HDLC. - */ -#define DLT_HHDLC 121 - -/* - * This is for RFC 2625 IP-over-Fibre Channel. - * - * This is not for use with raw Fibre Channel, where the link-layer - * header starts with a Fibre Channel frame header; it's for IP-over-FC, - * where the link-layer header starts with an RFC 2625 Network_Header - * field. - */ -#define DLT_IP_OVER_FC 122 - -/* - * This is for Full Frontal ATM on Solaris with SunATM, with a - * pseudo-header followed by an AALn PDU. - * - * There may be other forms of Full Frontal ATM on other OSes, - * with different pseudo-headers. - * - * If ATM software returns a pseudo-header with VPI/VCI information - * (and, ideally, packet type information, e.g. signalling, ILMI, - * LANE, LLC-multiplexed traffic, etc.), it should not use - * DLT_ATM_RFC1483, but should get a new DLT_ value, so tcpdump - * and the like don't have to infer the presence or absence of a - * pseudo-header and the form of the pseudo-header. - */ -#define DLT_SUNATM 123 /* Solaris+SunATM */ - -/* - * Reserved as per request from Kent Dahlgren - * for private use. - */ -#define DLT_RIO 124 /* RapidIO */ -#define DLT_PCI_EXP 125 /* PCI Express */ -#define DLT_AURORA 126 /* Xilinx Aurora link layer */ - -/* - * For future use with 802.11 captures - defined by AbsoluteValue - * Systems to store a number of bits of link-layer information: - * - * http://www.shaftnet.org/~pizza/software/capturefrm.txt - * - * but could and arguably should also be used by non-AVS Linux - * 802.11 drivers and BSD drivers; that may happen in the future. - */ -#define DLT_IEEE802_11_RADIO 127 /* 802.11 plus WLAN header */ - -/* - * Reserved for the TZSP encapsulation, as per request from - * Chris Waters - * TZSP is a generic encapsulation for any other link type, - * which includes a means to include meta-information - * with the packet, e.g. signal strength and channel - * for 802.11 packets. - */ -#define DLT_TZSP 128 /* Tazmen Sniffer Protocol */ - -/* - * BSD's ARCNET headers have the source host, destination host, - * and type at the beginning of the packet; that's what's handed - * up to userland via BPF. - * - * Linux's ARCNET headers, however, have a 2-byte offset field - * between the host IDs and the type; that's what's handed up - * to userland via PF_PACKET sockets. - * - * We therefore have to have separate DLT_ values for them. - */ -#define DLT_ARCNET_LINUX 129 /* ARCNET */ - -/* - * The instruction encodings. - */ -/* instruction classes */ -#define BPF_CLASS(code) ((code) & 0x07) -#define BPF_LD 0x00 -#define BPF_LDX 0x01 -#define BPF_ST 0x02 -#define BPF_STX 0x03 -#define BPF_ALU 0x04 -#define BPF_JMP 0x05 -#define BPF_RET 0x06 -#define BPF_MISC 0x07 - -/* ld/ldx fields */ -#define BPF_SIZE(code) ((code) & 0x18) -#define BPF_W 0x00 -#define BPF_H 0x08 -#define BPF_B 0x10 -#define BPF_MODE(code) ((code) & 0xe0) -#define BPF_IMM 0x00 -#define BPF_ABS 0x20 -#define BPF_IND 0x40 -#define BPF_MEM 0x60 -#define BPF_LEN 0x80 -#define BPF_MSH 0xa0 - -/* alu/jmp fields */ -#define BPF_OP(code) ((code) & 0xf0) -#define BPF_ADD 0x00 -#define BPF_SUB 0x10 -#define BPF_MUL 0x20 -#define BPF_DIV 0x30 -#define BPF_OR 0x40 -#define BPF_AND 0x50 -#define BPF_LSH 0x60 -#define BPF_RSH 0x70 -#define BPF_NEG 0x80 -#define BPF_JA 0x00 -#define BPF_JEQ 0x10 -#define BPF_JGT 0x20 -#define BPF_JGE 0x30 -#define BPF_JSET 0x40 -#define BPF_SRC(code) ((code) & 0x08) -#define BPF_K 0x00 -#define BPF_X 0x08 - -/* ret - BPF_K and BPF_X also apply */ -#define BPF_RVAL(code) ((code) & 0x18) -#define BPF_A 0x10 - -/* misc */ -#define BPF_MISCOP(code) ((code) & 0xf8) -#define BPF_TAX 0x00 -#define BPF_TXA 0x80 - -/* - * The instruction data structure. - */ -struct bpf_insn { - u_short code; - u_char jt; - u_char jf; - bpf_int32 k; -}; - -/* - * Macros for insn array initializers. - */ -#define BPF_STMT(code, k) { (u_short)(code), 0, 0, k } -#define BPF_JUMP(code, k, jt, jf) { (u_short)(code), jt, jf, k } - -#if defined(BSD) && (defined(KERNEL) || defined(_KERNEL)) -/* - * Systems based on non-BSD kernels don't have ifnet's (or they don't mean - * anything if it is in ) and won't work like this. - */ -# if __STDC__ -extern void bpf_tap(struct ifnet *, u_char *, u_int); -extern void bpf_mtap(struct ifnet *, struct mbuf *); -extern void bpfattach(struct ifnet *, u_int, u_int); -extern void bpfilterattach(int); -# else -extern void bpf_tap(); -extern void bpf_mtap(); -extern void bpfattach(); -extern void bpfilterattach(); -# endif /* __STDC__ */ -#endif /* BSD && (_KERNEL || KERNEL) */ -#if __STDC__ || defined(__cplusplus) -extern int bpf_validate(struct bpf_insn *, int); -extern u_int bpf_filter(struct bpf_insn *, u_char *, u_int, u_int); -#else -extern int bpf_validate(); -extern u_int bpf_filter(); -#endif - -/* - * Number of scratch memory words (for BPF_LD|BPF_MEM and BPF_ST). - */ -#define BPF_MEMWORDS 16 - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/arch/win32/ip6_misc.h b/src/arch/win32/ip6_misc.h deleted file mode 100644 index 6714f0e..0000000 --- a/src/arch/win32/ip6_misc.h +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright (c) 1993, 1994, 1997 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that: (1) source code distributions - * retain the above copyright notice and this paragraph in its entirety, (2) - * distributions including binary code include the above copyright notice and - * this paragraph in its entirety in the documentation or other materials - * provided with the distribution, and (3) all advertising materials mentioning - * features or use of this software display the following acknowledgement: - * ``This product includes software developed by the University of California, - * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of - * the University nor the names of its contributors may be used to endorse - * or promote products derived from this software without specific prior - * written permission. - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. - * - * @(#) $Header$ (LBL) - */ - -/* - * This file contains a collage of declarations for IPv6 from FreeBSD not present in Windows - */ - -#include - -#ifndef __MINGW32__ -#include -#endif /* __MINGW32__ */ - -#define IN_MULTICAST(a) IN_CLASSD(a) - -#define IN_EXPERIMENTAL(a) ((((u_int32_t) (a)) & 0xe0000000) == 0xe0000000) - -#define IN_LOOPBACKNET 127 - -#ifdef __MINGW32__ -/* IPv6 address */ -struct in6_addr - { - union - { - u_int8_t u6_addr8[16]; - u_int16_t u6_addr16[8]; - u_int32_t u6_addr32[4]; - } in6_u; -#define s6_addr in6_u.u6_addr8 -#define s6_addr16 in6_u.u6_addr16 -#define s6_addr32 in6_u.u6_addr32 -#define s6_addr64 in6_u.u6_addr64 - }; - -#define IN6ADDR_ANY_INIT { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } -#define IN6ADDR_LOOPBACK_INIT { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } -#endif /* __MINGW32__ */ - - -#ifdef __MINGW32__ -typedef unsigned short sa_family_t; - -#define __SOCKADDR_COMMON(sa_prefix) \ - sa_family_t sa_prefix##family - -/* Ditto, for IPv6. */ -struct sockaddr_in6 - { - __SOCKADDR_COMMON (sin6_); - u_int16_t sin6_port; /* Transport layer port # */ - u_int32_t sin6_flowinfo; /* IPv6 flow information */ - struct in6_addr sin6_addr; /* IPv6 address */ - }; - -#define IN6_IS_ADDR_V4MAPPED(a) \ - ((((u_int32_t *) (a))[0] == 0) && (((u_int32_t *) (a))[1] == 0) && \ - (((u_int32_t *) (a))[2] == htonl (0xffff))) - -#define IN6_IS_ADDR_MULTICAST(a) (((u_int8_t *) (a))[0] == 0xff) - -#define IN6_IS_ADDR_LINKLOCAL(a) \ - ((((u_int32_t *) (a))[0] & htonl (0xffc00000)) == htonl (0xfe800000)) - -#define IN6_IS_ADDR_LOOPBACK(a) \ - (((u_int32_t *) (a))[0] == 0 && ((u_int32_t *) (a))[1] == 0 && \ - ((u_int32_t *) (a))[2] == 0 && ((u_int32_t *) (a))[3] == htonl (1)) -#endif /* __MINGW32__ */ - -#define ip6_vfc ip6_ctlun.ip6_un2_vfc -#define ip6_flow ip6_ctlun.ip6_un1.ip6_un1_flow -#define ip6_plen ip6_ctlun.ip6_un1.ip6_un1_plen -#define ip6_nxt ip6_ctlun.ip6_un1.ip6_un1_nxt -#define ip6_hlim ip6_ctlun.ip6_un1.ip6_un1_hlim -#define ip6_hops ip6_ctlun.ip6_un1.ip6_un1_hlim - -#define nd_rd_type nd_rd_hdr.icmp6_type -#define nd_rd_code nd_rd_hdr.icmp6_code -#define nd_rd_cksum nd_rd_hdr.icmp6_cksum -#define nd_rd_reserved nd_rd_hdr.icmp6_data32[0] - -/* - * IPV6 extension headers - */ -#define IPPROTO_HOPOPTS 0 /* IPv6 hop-by-hop options */ -#define IPPROTO_IPV6 41 /* IPv6 header. */ -#define IPPROTO_ROUTING 43 /* IPv6 routing header */ -#define IPPROTO_FRAGMENT 44 /* IPv6 fragmentation header */ -#define IPPROTO_ESP 50 /* encapsulating security payload */ -#define IPPROTO_AH 51 /* authentication header */ -#define IPPROTO_ICMPV6 58 /* ICMPv6 */ -#define IPPROTO_NONE 59 /* IPv6 no next header */ -#define IPPROTO_DSTOPTS 60 /* IPv6 destination options */ -#define IPPROTO_PIM 103 /* Protocol Independent Multicast. */ - -#define IPV6_RTHDR_TYPE_0 0 - -/* Option types and related macros */ -#define IP6OPT_PAD1 0x00 /* 00 0 00000 */ -#define IP6OPT_PADN 0x01 /* 00 0 00001 */ -#define IP6OPT_JUMBO 0xC2 /* 11 0 00010 = 194 */ -#define IP6OPT_JUMBO_LEN 6 -#define IP6OPT_ROUTER_ALERT 0x05 /* 00 0 00101 */ - -#define IP6OPT_RTALERT_LEN 4 -#define IP6OPT_RTALERT_MLD 0 /* Datagram contains an MLD message */ -#define IP6OPT_RTALERT_RSVP 1 /* Datagram contains an RSVP message */ -#define IP6OPT_RTALERT_ACTNET 2 /* contains an Active Networks msg */ -#define IP6OPT_MINLEN 2 - -#define IP6OPT_BINDING_UPDATE 0xc6 /* 11 0 00110 */ -#define IP6OPT_BINDING_ACK 0x07 /* 00 0 00111 */ -#define IP6OPT_BINDING_REQ 0x08 /* 00 0 01000 */ -#define IP6OPT_HOME_ADDRESS 0xc9 /* 11 0 01001 */ -#define IP6OPT_EID 0x8a /* 10 0 01010 */ - -#define IP6OPT_TYPE(o) ((o) & 0xC0) -#define IP6OPT_TYPE_SKIP 0x00 -#define IP6OPT_TYPE_DISCARD 0x40 -#define IP6OPT_TYPE_FORCEICMP 0x80 -#define IP6OPT_TYPE_ICMP 0xC0 - -#define IP6OPT_MUTABLE 0x20 - - -#ifdef __MINGW32__ -#ifndef EAI_ADDRFAMILY -struct addrinfo { - int ai_flags; /* AI_PASSIVE, AI_CANONNAME */ - int ai_family; /* PF_xxx */ - int ai_socktype; /* SOCK_xxx */ - int ai_protocol; /* 0 or IPPROTO_xxx for IPv4 and IPv6 */ - size_t ai_addrlen; /* length of ai_addr */ - char *ai_canonname; /* canonical name for hostname */ - struct sockaddr *ai_addr; /* binary address */ - struct addrinfo *ai_next; /* next structure in linked list */ -}; -#endif -#endif /* __MINGW32__ */ diff --git a/src/arch/win32/pcap-stdinc.h b/src/arch/win32/pcap-stdinc.h deleted file mode 100644 index 221003d..0000000 --- a/src/arch/win32/pcap-stdinc.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2002 - * Politecnico di Torino. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that: (1) source code distributions - * retain the above copyright notice and this paragraph in its entirety, (2) - * distributions including binary code include the above copyright notice and - * this paragraph in its entirety in the documentation or other materials - * provided with the distribution, and (3) all advertising materials mentioning - * features or use of this software display the following acknowledgement: - * ``This product includes software developed by the Politecnico - * di Torino, and its contributors.'' Neither the name of - * the University nor the names of its contributors may be used to endorse - * or promote products derived from this software without specific prior - * written permission. - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. - * - * @(#) $Header$ (LBL) - */ - -#define SIZEOF_CHAR 1 -#define SIZEOF_SHORT 2 -#define SIZEOF_INT 4 - -#define _WINSOCKAPI_ -#include - -#include -#include "bittypes.h" -#include -#include - -#ifndef __MINGW32__ -#include "IP6_misc.h" -#endif - -#define caddr_t char* - -#define snprintf _snprintf -//#define vsnprintf _vsnprintf - diff --git a/src/arch/win32/pcap.h b/src/arch/win32/pcap.h deleted file mode 100644 index 3b8dcda..0000000 --- a/src/arch/win32/pcap.h +++ /dev/null @@ -1,273 +0,0 @@ -/* -*- Mode: c; tab-width: 8; indent-tabs-mode: 1; c-basic-offset: 8; -*- */ -/* - * Copyright (c) 1993, 1994, 1995, 1996, 1997 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the Computer Systems - * Engineering Group at Lawrence Berkeley Laboratory. - * 4. Neither the name of the University nor of the Laboratory may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#) $Header$ (LBL) - */ - -#ifndef lib_pcap_h -#define lib_pcap_h - -#include - -#if defined _MSC_VER -# include -#else -#include -#endif - -/* RGJ Changed it to "bpf.h" for AppleWin */ -#include "bpf.h" - -#include - -#ifdef REMOTE - // We have to define the SOCKET here, although it has been defined in sockutils.h - // This is to avoid the distribution of the 'sockutils.h' file around - // (for example in the WinPcap developer's pack) - #ifndef SOCKET - #ifdef WIN32 - #define SOCKET unsigned int - #else - #define SOCKET int - #endif - #endif -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -#define PCAP_VERSION_MAJOR 2 -#define PCAP_VERSION_MINOR 4 - -#define PCAP_ERRBUF_SIZE 256 - -/* - * Compatibility for systems that have a bpf.h that - * predates the bpf typedefs for 64-bit support. - */ -#if BPF_RELEASE - 0 < 199406 -typedef int bpf_int32; -typedef u_int bpf_u_int32; -#endif - -typedef struct pcap pcap_t; -typedef struct pcap_dumper pcap_dumper_t; -typedef struct pcap_if pcap_if_t; -typedef struct pcap_addr pcap_addr_t; - -/* - * The first record in the file contains saved values for some - * of the flags used in the printout phases of tcpdump. - * Many fields here are 32 bit ints so compilers won't insert unwanted - * padding; these files need to be interchangeable across architectures. - * - * Do not change the layout of this structure, in any way (this includes - * changes that only affect the length of fields in this structure). - * - * Also, do not change the interpretation of any of the members of this - * structure, in any way (this includes using values other than - * LINKTYPE_ values, as defined in "savefile.c", in the "linktype" - * field). - * - * Instead: - * - * introduce a new structure for the new format, if the layout - * of the structure changed; - * - * send mail to "tcpdump-workers@tcpdump.org", requesting a new - * magic number for your new capture file format, and, when - * you get the new magic number, put it in "savefile.c"; - * - * use that magic number for save files with the changed file - * header; - * - * make the code in "savefile.c" capable of reading files with - * the old file header as well as files with the new file header - * (using the magic number to determine the header format). - * - * Then supply the changes to "patches@tcpdump.org", so that future - * versions of libpcap and programs that use it (such as tcpdump) will - * be able to read your new capture file format. - */ -struct pcap_file_header { - bpf_u_int32 magic; - u_short version_major; - u_short version_minor; - bpf_int32 thiszone; /* gmt to local correction */ - bpf_u_int32 sigfigs; /* accuracy of timestamps */ - bpf_u_int32 snaplen; /* max length saved portion of each pkt */ - bpf_u_int32 linktype; /* data link type (LINKTYPE_*) */ -}; - -/* - * Each packet in the dump file is prepended with this generic header. - * This gets around the problem of different headers for different - * packet interfaces. - */ -struct pcap_pkthdr { - struct timeval ts; /* time stamp */ - bpf_u_int32 caplen; /* length of portion present */ - bpf_u_int32 len; /* length this packet (off wire) */ -}; - -/* - * As returned by the pcap_stats() - */ -struct pcap_stat { - u_int ps_recv; /* number of packets received */ - u_int ps_drop; /* number of packets dropped */ - u_int ps_ifdrop; /* drops by interface XXX not yet supported */ -#ifdef REMOTE -#ifdef WIN32 -// u_int bs_capt; /* number of packets that reach the application */ -#endif /* WIN32 */ - u_int ps_capt; /* number of packets that reach the application; please get rid off the Win32 ifdef */ - u_int ps_sent; /* number of packets sent by the server on the network */ - u_int ps_netdrop; /* number of packets lost on the network */ -#endif -}; - -/* - * Item in a list of interfaces. - */ -struct pcap_if { - struct pcap_if *next; - char *name; /* name to hand to "pcap_open_live()" */ - char *description; /* textual description of interface, or NULL */ - struct pcap_addr *addresses; - bpf_u_int32 flags; /* PCAP_IF_ interface flags */ -}; - -#define PCAP_IF_LOOPBACK 0x00000001 /* interface is loopback */ - -/* - * Representation of an interface address. - */ -struct pcap_addr { - struct pcap_addr *next; - struct sockaddr *addr; /* address */ - struct sockaddr *netmask; /* netmask for that address */ - struct sockaddr *broadaddr; /* broadcast address for that address */ - struct sockaddr *dstaddr; /* P2P destination address for that address */ -}; - -typedef void (*pcap_handler)(u_char *, const struct pcap_pkthdr *, - const u_char *); - -char *pcap_lookupdev(char *); -int pcap_lookupnet(const char *, bpf_u_int32 *, bpf_u_int32 *, char *); -pcap_t *pcap_open_live(const char *, int, int, int, char *); -pcap_t *pcap_open_dead(int, int); -pcap_t *pcap_open_offline(const char *, char *); -void pcap_close(pcap_t *); -int pcap_loop(pcap_t *, int, pcap_handler, u_char *); -int pcap_dispatch(pcap_t *, int, pcap_handler, u_char *); -const u_char* - pcap_next(pcap_t *, struct pcap_pkthdr *); -int pcap_stats(pcap_t *, struct pcap_stat *); -int pcap_setfilter(pcap_t *, struct bpf_program *); -int pcap_getnonblock(pcap_t *, char *); -int pcap_setnonblock(pcap_t *, int, char *); -void pcap_perror(pcap_t *, char *); -char *pcap_strerror(int); -char *pcap_geterr(pcap_t *); -int pcap_compile(pcap_t *, struct bpf_program *, char *, int, - bpf_u_int32); -int pcap_compile_nopcap(int, int, struct bpf_program *, - char *, int, bpf_u_int32); -void pcap_freecode(struct bpf_program *); -int pcap_datalink(pcap_t *); -int pcap_list_datalinks(pcap_t *, int **); -int pcap_set_datalink(pcap_t *, int); -int pcap_datalink_name_to_val(const char *); -const char *pcap_datalink_val_to_name(int); -int pcap_snapshot(pcap_t *); -int pcap_is_swapped(pcap_t *); -int pcap_major_version(pcap_t *); -int pcap_minor_version(pcap_t *); - -/* XXX */ -FILE *pcap_file(pcap_t *); -int pcap_fileno(pcap_t *); - -pcap_dumper_t *pcap_dump_open(pcap_t *, const char *); -int pcap_dump_flush(pcap_dumper_t *); -void pcap_dump_close(pcap_dumper_t *); -void pcap_dump(u_char *, const struct pcap_pkthdr *, const u_char *); - -int pcap_findalldevs(pcap_if_t **, char *); -void pcap_freealldevs(pcap_if_t *); - -/* To avoid callback, this returns one packet at a time */ -int pcap_next_ex(pcap_t *p, struct pcap_pkthdr **pkt_header, u_char **pkt_data); - -/* XXX this guy lives in the bpf tree */ -u_int bpf_filter(struct bpf_insn *, u_char *, u_int, u_int); -int bpf_validate(struct bpf_insn *f, int len); -char *bpf_image(struct bpf_insn *, int); -void bpf_dump(struct bpf_program *, int); - -#ifdef WIN32 -/* - * Win32 definitions - */ - -int pcap_setbuff(pcap_t *p, int dim); -int pcap_setmode(pcap_t *p, int mode); -int pcap_sendpacket(pcap_t *p, u_char *buf, int size); -int pcap_setmintocopy(pcap_t *p, int size); - -#ifdef WPCAP -/* Include file with the wpcap-specific extensions */ -#include -#endif - -#define MODE_CAPT 0 -#define MODE_STAT 1 -#define MODE_MON 2 - -#endif /* WIN32 */ - -#ifdef REMOTE -/* Includes most of the public stuff that is needed for the remote capture */ -#include "remote-ext.h" -#endif - - - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/atbridge/CMakeLists.txt b/src/atbridge/CMakeLists.txt index e161e6b..7bdcac3 100644 --- a/src/atbridge/CMakeLists.txt +++ b/src/atbridge/CMakeLists.txt @@ -5,6 +5,5 @@ add_library(atbridge aarp.c atbridge.c elap.c llap.c pcap_delay.c port.c) target_compile_definitions(atbridge PUBLIC HAVE_ATBRIDGE) if(WIN32) - target_include_directories(atbridge PRIVATE ../rawnet/include) target_link_libraries(atbridge ws2_32) # winsock2 endif() diff --git a/src/atbridge/pcap_delay.h b/src/atbridge/pcap_delay.h index 64b1bf3..329d234 100644 --- a/src/atbridge/pcap_delay.h +++ b/src/atbridge/pcap_delay.h @@ -14,13 +14,7 @@ This wrapper provides a subset of the available PCAP APIs necessary for ATBridge Feel free to extend the wrapper. */ -#ifdef WIN32 #include -#elif __linux__ -#include -#elif __APPLE__ -#include -#endif bool pcapdelay_load(); bool pcapdelay_is_loaded(); diff --git a/src/config.c b/src/config.c index cf406b7..c3972b1 100644 --- a/src/config.c +++ b/src/config.c @@ -11,11 +11,8 @@ #include "config.h" #include "glog.h" #include "imagewriter.h" -#if defined(_MSC_VER) -#include "arch\win32\dirent-win32.h" -#else + #include -#endif #ifdef HAVE_RAWNET #include "rawnet/rawnet.h" @@ -367,6 +364,7 @@ Cfg_menu g_cfg_parallel_menu[] = { { 0, 0, 0, 0, 0 }, }; +#ifdef HAVE_RAWNET Cfg_menu g_cfg_ethernet_menu[] = { { "Ethernet Card Configuration", g_cfg_ethernet_menu, 0, 0, CFGTYPE_MENU }, { "Interface", @@ -385,6 +383,8 @@ Cfg_menu g_cfg_ethernet_menu[] = { { "Back to Main Config", g_cfg_main_menu, 0, 0, CFGTYPE_MENU }, { 0, 0, 0, 0, 0 }, }; +#endif + #ifdef HAVE_SDL Cfg_menu g_cfg_printer_menu[] = { { "Virtual Epson Configuration", g_cfg_printer_menu, 0, 0, CFGTYPE_MENU }, @@ -474,7 +474,9 @@ Cfg_menu g_cfg_main_menu[] = { { "ROM File Selection", g_cfg_rom_menu, 0, 0, CFGTYPE_MENU }, { "HOST FST Configuration", g_cfg_host_menu, 0, 0, CFGTYPE_MENU }, { "Serial Port Configuration", g_cfg_serial_menu, 0, 0, CFGTYPE_MENU }, +#ifdef HAVE_RAWNET { "Ethernet Card Configuration", g_cfg_ethernet_menu, 0, 0, CFGTYPE_MENU }, +#endif { "Parallel Card Configuration", g_cfg_parallel_menu, 0, 0, CFGTYPE_MENU }, #ifdef HAVE_SDL { "Virtual Epson Configuration", g_cfg_printer_menu, 0, 0, CFGTYPE_MENU }, diff --git a/src/rawnet/CMakeLists.txt b/src/rawnet/CMakeLists.txt index fceacbc..2e70bc4 100644 --- a/src/rawnet/CMakeLists.txt +++ b/src/rawnet/CMakeLists.txt @@ -14,7 +14,6 @@ target_compile_definitions(rawnet PUBLIC HAVE_RAWNET) target_compile_definitions(rawnet PRIVATE CS8900_DEBUG RAWNET_DEBUG_FRAMES) if(WIN32) - target_include_directories(rawnet PRIVATE include) target_link_libraries(rawnet ws2_32) # winsock2 elseif(APPLE) #target_link_libraries(rawnet PRIVATE pcap) From 7422409f53d535a555299d4f60ed095d3e987da2 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Wed, 2 Jan 2019 21:48:55 -0500 Subject: [PATCH 030/186] remove stale old msvc projects --- src/atbridge/atbridge.vcxproj | 108 ------------- src/atbridge/atbridge.vcxproj.filters | 66 -------- src/gsport.sln | 36 ----- src/gsport.vcxproj | 203 ------------------------ src/gsport.vcxproj.filters | 217 -------------------------- src/tfe/tfe.vcxproj | 108 ------------- src/tfe/tfe.vcxproj.filters | 54 ------- 7 files changed, 792 deletions(-) delete mode 100644 src/atbridge/atbridge.vcxproj delete mode 100644 src/atbridge/atbridge.vcxproj.filters delete mode 100644 src/gsport.sln delete mode 100644 src/gsport.vcxproj delete mode 100644 src/gsport.vcxproj.filters delete mode 100644 src/tfe/tfe.vcxproj delete mode 100644 src/tfe/tfe.vcxproj.filters diff --git a/src/atbridge/atbridge.vcxproj b/src/atbridge/atbridge.vcxproj deleted file mode 100644 index 8654124..0000000 --- a/src/atbridge/atbridge.vcxproj +++ /dev/null @@ -1,108 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - - - - - - - - - - - - - - - - - - - - - {2C88133A-7CB8-4C03-AF4D-4ECFC6F8500B} - Win32Proj - atbridge - - - - StaticLibrary - true - v120 - MultiByte - - - StaticLibrary - false - v120 - true - MultiByte - - - - - - - - - - - - - - - - - Level3 - Disabled - WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) - true - Default - false - - - Windows - true - - - %(AdditionalDependencies) - - - - - Level3 - - - MaxSpeed - true - true - WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) - true - Speed - StreamingSIMDExtensions2 - Default - - - Windows - true - true - true - - - %(AdditionalDependencies) - - - - - - diff --git a/src/atbridge/atbridge.vcxproj.filters b/src/atbridge/atbridge.vcxproj.filters deleted file mode 100644 index 3cbdd44..0000000 --- a/src/atbridge/atbridge.vcxproj.filters +++ /dev/null @@ -1,66 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hpp;hxx;hm;inl;inc;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - diff --git a/src/gsport.sln b/src/gsport.sln deleted file mode 100644 index 9a17a1d..0000000 --- a/src/gsport.sln +++ /dev/null @@ -1,36 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Express 2012 for Windows Desktop -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gsport", "gsport.vcxproj", "{0B4E527A-DB50-4B5F-9B08-303ABAF7356A}" - ProjectSection(ProjectDependencies) = postProject - {2C88133A-7CB8-4C03-AF4D-4ECFC6F8500B} = {2C88133A-7CB8-4C03-AF4D-4ECFC6F8500B} - {E810477A-E004-4308-A58A-21393213EF89} = {E810477A-E004-4308-A58A-21393213EF89} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tfe", "tfe\tfe.vcxproj", "{E810477A-E004-4308-A58A-21393213EF89}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "atbridge", "atbridge\atbridge.vcxproj", "{2C88133A-7CB8-4C03-AF4D-4ECFC6F8500B}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Release|Win32 = Release|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {0B4E527A-DB50-4B5F-9B08-303ABAF7356A}.Debug|Win32.ActiveCfg = Debug|Win32 - {0B4E527A-DB50-4B5F-9B08-303ABAF7356A}.Debug|Win32.Build.0 = Debug|Win32 - {0B4E527A-DB50-4B5F-9B08-303ABAF7356A}.Release|Win32.ActiveCfg = Release|Win32 - {0B4E527A-DB50-4B5F-9B08-303ABAF7356A}.Release|Win32.Build.0 = Release|Win32 - {E810477A-E004-4308-A58A-21393213EF89}.Debug|Win32.ActiveCfg = Debug|Win32 - {E810477A-E004-4308-A58A-21393213EF89}.Debug|Win32.Build.0 = Debug|Win32 - {E810477A-E004-4308-A58A-21393213EF89}.Release|Win32.ActiveCfg = Release|Win32 - {E810477A-E004-4308-A58A-21393213EF89}.Release|Win32.Build.0 = Release|Win32 - {2C88133A-7CB8-4C03-AF4D-4ECFC6F8500B}.Debug|Win32.ActiveCfg = Debug|Win32 - {2C88133A-7CB8-4C03-AF4D-4ECFC6F8500B}.Debug|Win32.Build.0 = Debug|Win32 - {2C88133A-7CB8-4C03-AF4D-4ECFC6F8500B}.Release|Win32.ActiveCfg = Release|Win32 - {2C88133A-7CB8-4C03-AF4D-4ECFC6F8500B}.Release|Win32.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/src/gsport.vcxproj b/src/gsport.vcxproj deleted file mode 100644 index 647685c..0000000 --- a/src/gsport.vcxproj +++ /dev/null @@ -1,203 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - - {0B4E527A-DB50-4B5F-9B08-303ABAF7356A} - Win32Proj - gsport - - - - Application - true - MultiByte - v120 - - - Application - false - true - MultiByte - v120 - - - - - - - - - - - - - true - - - false - - - - NotUsing - Level3 - Disabled - WIN32;_DEBUG;_CONSOLE;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;WIN_SOUND;GSPLUS_LITTLE_ENDIAN;HAVE_TFE;HAVE_ATBRIDGE;TOGGLE_STATUS;%(PreprocessorDefinitions) - CompileAsC - Speed - true - ProgramDatabase - EnableFastChecks - Prompt - true - true - false - - - Console - true - IPHLPAPI.lib;Winmm.lib;Ws2_32.lib;Shlwapi.lib;$(SolutionDir)$(Configuration)\tfe.lib;;$(SolutionDir)$(Configuration)\atbridge.lib;%(AdditionalDependencies) - MachineX86 - - - - - Level3 - NotUsing - Full - true - true - WIN32;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;WIN_SOUND;GSPLUS_LITTLE_ENDIAN;HAVE_ATBRIDGE;HAVE_TFE;TOGGLE_STATUS;%(PreprocessorDefinitions) - Speed - false - CompileAsC - Prompt - StreamingSIMDExtensions2 - AnySuitable - true - true - - - Console - true - true - true - IPHLPAPI.lib;Winmm.lib;Ws2_32.lib;Shlwapi.lib;$(SolutionDir)$(Configuration)\tfe.lib;;$(SolutionDir)$(Configuration)\atbridge.lib;%(AdditionalDependencies) - - - - - - - - - - - - - - - perl make_inst c 8 instable.h > 8inst_c.h -perl make_inst c 16 instable.h > 16inst_c.h -perl make_inst s 8 instable.h > 8inst_s.h -perl make_inst s 16 instable.h > 16inst_s.h - 8inst_c.h 16inst_c.h 8inst_s.h 16inst_s.h - false - perl make_inst c 8 instable.h > 8inst_c.h -perl make_inst c 16 instable.h > 16inst_c.h -perl make_inst s 8 instable.h > 8inst_s.h -perl make_inst s 16 instable.h > 16inst_s.h - 8inst_c.h 16inst_c.h 8inst_s.h 16inst_s.h - false - - - - - - - - - - - - - - - - - - - - - - - perl make_size c size_tab.h > size_c.h -perl make_size s size_tab.h > size_s.h -perl make_size 8 size_tab.h > 8size_s.h -perl make_size 16 size_tab.h > 16size_s.h -perl make_size c size_tab.h > size_c.h - size_c.h size_s.h 8size_s.h 16size_s.h - false - perl make_size c size_tab.h > size_c.h -perl make_size s size_tab.h > size_s.h -perl make_size 8 size_tab.h > 8size_s.h -perl make_size 16 size_tab.h > 16size_s.h -perl make_size c size_tab.h > size_c.h - size_c.h size_s.h 8size_s.h 16size_s.h - false - - - - - - - - - - - - - - - - - - - - - CompileAsCpp - CompileAsCpp - - - - CompileAsCpp - CompileAsCpp - - - - - - - - - - - - - - - - - - - - - diff --git a/src/gsport.vcxproj.filters b/src/gsport.vcxproj.filters deleted file mode 100644 index 59b4026..0000000 --- a/src/gsport.vcxproj.filters +++ /dev/null @@ -1,217 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hpp;hxx;hm;inl;inc;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - - - Header Files - - - Header Files - - - - - Resource Files - - - diff --git a/src/tfe/tfe.vcxproj b/src/tfe/tfe.vcxproj deleted file mode 100644 index 55aa273..0000000 --- a/src/tfe/tfe.vcxproj +++ /dev/null @@ -1,108 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - - {E810477A-E004-4308-A58A-21393213EF89} - Win32Proj - tfe - - - - StaticLibrary - true - MultiByte - v120 - - - StaticLibrary - false - true - MultiByte - v120 - - - - - - - - - - - - - true - - - false - - - - NotUsing - Level3 - Disabled - WIN32;_DEBUG;_WINDOWS;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;TFE_EXPORTS;%(PreprocessorDefinitions) - Speed - false - ProgramDatabase - Default - CompileAsC - true - true - false - - - Windows - true - - - - - Level3 - NotUsing - Full - true - true - WIN32;NDEBUG;_WINDOWS;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;TFE_EXPORTS;%(PreprocessorDefinitions) - Speed - CompileAsC - false - StreamingSIMDExtensions - AnySuitable - true - - - Windows - true - true - true - - - - - - - - - - - - - - - - - - - - - diff --git a/src/tfe/tfe.vcxproj.filters b/src/tfe/tfe.vcxproj.filters deleted file mode 100644 index 78f75f4..0000000 --- a/src/tfe/tfe.vcxproj.filters +++ /dev/null @@ -1,54 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hpp;hxx;hm;inl;inc;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - - - Source Files - - - Source Files - - - Source Files - - - From f0bd88195bcb210b60a248c746b7d19031e8a4bc Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Wed, 2 Jan 2019 21:49:38 -0500 Subject: [PATCH 031/186] bsd tuntap documentation. --- src/rawnet/Networking.txt | 35 +++++++++++++++++++++++++++++++++++ src/rawnet/rawnetarch_tap.c | 14 ++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 src/rawnet/Networking.txt diff --git a/src/rawnet/Networking.txt b/src/rawnet/Networking.txt new file mode 100644 index 0000000..084797c --- /dev/null +++ b/src/rawnet/Networking.txt @@ -0,0 +1,35 @@ +Networking +---------- + +GS+ can emulate an Uthernet (the original) card in slot 3. Marinetti is supported with the Uthernet Link Layer. Version 1.0.2 or newer is recommended. + +Configuration: + +In the settings menu, select Ethernet Card Configuration. + +Make sure Uthernet Card in Slot 3 is set to On. + +Select the Interface menu to choose the selected interface from a menu. + +Win32: + +Ethernet support uses Winpcap or its modern successor, npcap. You need to install them. + +Winpcap/npcap require a hardwired ethernet connection in promiscuous mode -- they work by tapping into the ethernet stream. + +Interface names are not particularly meaningful. Sorry. Run `getmac /v` from cmd.exe to get a human friendly name for the interface device. + +In marinetti, hardcode the ip address, gateway, and dns servers. + +OS X: + +Ethernet support uses the vmnet framework. This provides a virtual ethernet device, dhcp server, and dns server, all bridged to the Macintosh's network. + +Unfortunately, vmnet requires root permissions or a codesigning entitlment which may only valid for applications through the Mac App Store. + +In marinetti, use DHCP. + +Linux: + +Ethernet support uses the tap ethernet device. This require setting up the device and bridging it to your local network. + diff --git a/src/rawnet/rawnetarch_tap.c b/src/rawnet/rawnetarch_tap.c index b7d099f..940a74f 100644 --- a/src/rawnet/rawnetarch_tap.c +++ b/src/rawnet/rawnetarch_tap.c @@ -30,6 +30,20 @@ * 5. and add the ip address to the bridge * dhclient br0 # if using dhcp * ip address add 192.168.1.1/24 dev eth0 # if using static ip address. + * + * *BSD: + * - assumes eth0 is your main interface device. + * $ ifconfig bridge0 create + * $ ifconfig tap65816 create + * $ ifconfig bridge0 addm eth0 addm tap65816 up + * + * allow normal users to open tap devices? + * $ sysctl net.link.tap.user_open=1 + * $ sysctl net.link.tap.up_on_open=1 + * + * set permissions + * $ chown YOUR_USER_NAME /dev/tap65816 + * $ chmod 660 /dev/tap65816 */ From b0764fb93cb7c94a4a10b6ac1dcc4fac7bae9d91 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Wed, 2 Jan 2019 21:49:48 -0500 Subject: [PATCH 032/186] build fix. --- src/sim65816.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sim65816.c b/src/sim65816.c index db78638..536e666 100644 --- a/src/sim65816.c +++ b/src/sim65816.c @@ -33,7 +33,7 @@ extern char g_config_gsplus_screenshot_dir[]; extern void get_cwd(LPTSTR buffer, int size); #endif -#ifndef ENABLE_DEBUGGER +#ifndef GSPLUS_DEBUGGER int g_dbg_step = 0; int g_dbg_enable_port = 0; From 1700285fd2c4bb4f863fd4fb96927c59aa977315 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Fri, 4 Jan 2019 17:53:16 -0500 Subject: [PATCH 033/186] cleanup to build with MSVC. --- CMakeLists.txt | 1 + src/config.c | 7 +++-- src/host_common.c | 64 ++++------------------------------------- src/host_mli.c | 5 ++++ src/options.c | 5 ++++ src/unix_host_common.c | 57 ++++++++++++++++++++++++++++++++++++ src/win32_host_common.c | 16 +++++++---- src/win32_host_fst.c | 10 +++++-- 8 files changed, 97 insertions(+), 68 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0823646..d7006c6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,6 +12,7 @@ endif() if(MSVC) include_directories(include/msvc) add_definitions(-D_CRT_SECURE_NO_WARNINGS) + add_compile_options(/wd4996) endif() # add pcap headers for win32. assume os/x, linux, etc, already have them. diff --git a/src/config.c b/src/config.c index c3972b1..669b8e2 100644 --- a/src/config.c +++ b/src/config.c @@ -19,9 +19,12 @@ #endif #if defined _MSC_VER -#include -#define snprintf _snprintf + typedef unsigned int mode_t; + +#define strcasecmp stricmp +#define strncasecmp strnicmp + #endif diff --git a/src/host_common.c b/src/host_common.c index 6cab5ab..6b594e6 100644 --- a/src/host_common.c +++ b/src/host_common.c @@ -1,14 +1,9 @@ #define _BSD_SOURCE -#include -#include -#include #include -#include #include #include #include -#include #include "defc.h" #include "gsos.h" @@ -18,6 +13,12 @@ #include #endif +#ifdef _MSC_VER +#define strcasecmp stricmp +#define strncasecmp strnicmp +#endif + + #include "host_common.h" @@ -149,59 +150,6 @@ void host_text_to_merlin(byte *buffer, size_t size) { } -/* - * error remapping. - * NOTE - GS/OS errors are a superset of P8 errors - */ - -static word32 enoent(const char *path) { - /* - some op on path return ENOENT. check if it's - fileNotFound or pathNotFound - */ - char *p = (char *)path; - for(;;) { - struct stat st; - p = dirname(p); - if (p == NULL) break; - if (p[0] == '.' && p[1] == 0) break; - if (p[0] == '/' && p[1] == 0) break; - if (stat(p, &st) < 0) return pathNotFound; - } - return fileNotFound; -} - -word32 host_map_errno(int xerrno) { - switch(xerrno) { - case 0: return 0; - case EBADF: - return invalidAccess; -#ifdef EDQUOT - case EDQUOT: -#endif - case EFBIG: - return volumeFull; - case ENOENT: - return fileNotFound; - case ENOTDIR: - return pathNotFound; - case ENOMEM: - return outOfMem; - case EEXIST: - return dupPathname; - case ENOTEMPTY: - return invalidAccess; - - default: - return drvrIOError; - } -} - -word32 host_map_errno_path(int xerrno, const char *path) { - if (xerrno == ENOENT) return enoent(path); - return host_map_errno(xerrno); -} - const char *host_error_name(word16 error) { static char *errors[] = { "", diff --git a/src/host_mli.c b/src/host_mli.c index 5356e94..9550e5f 100644 --- a/src/host_mli.c +++ b/src/host_mli.c @@ -26,6 +26,11 @@ #include "host_common.h" +#ifdef _MSC_VER +#define strcasecmp stricmp +#define strncasecmp strnicmp +#endif + #define LEVEL 0xBFD8 // current file level #define DEVNUM 0xBF30 // last slot / drive diff --git a/src/options.c b/src/options.c index 86c03f4..f58d199 100644 --- a/src/options.c +++ b/src/options.c @@ -8,10 +8,15 @@ #include #include #include +#include #include "options.h" #include "glog.h" #include "defc.h" +#ifdef _MSC_VER +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#endif + // config is parsed in config.c :: config_parse_config_gsplus_file() // cli is parsed here. would be nice to reuse some code diff --git a/src/unix_host_common.c b/src/unix_host_common.c index 0df5b91..adc5110 100644 --- a/src/unix_host_common.c +++ b/src/unix_host_common.c @@ -7,6 +7,8 @@ #include #include #include +#include +#include #if defined(__APPLE__) #include @@ -510,3 +512,58 @@ unsigned host_storage_type(const char *path, word16 *error) { *error = badStoreType; return 0; } + + +/* + * error remapping. + * NOTE - GS/OS errors are a superset of P8 errors + */ + +static word32 enoent(const char *path) { + /* + some op on path return ENOENT. check if it's + fileNotFound or pathNotFound + */ + char *p = (char *)path; + for(;;) { + struct stat st; + p = dirname(p); + if (p == NULL) break; + if (p[0] == '.' && p[1] == 0) break; + if (p[0] == '/' && p[1] == 0) break; + if (stat(p, &st) < 0) return pathNotFound; + } + return fileNotFound; +} + +word32 host_map_errno(int xerrno) { + switch(xerrno) { + case 0: return 0; + case EBADF: + return invalidAccess; +#ifdef EDQUOT + case EDQUOT: +#endif + case EFBIG: + return volumeFull; + case ENOENT: + return fileNotFound; + case ENOTDIR: + return pathNotFound; + case ENOMEM: + return outOfMem; + case EEXIST: + return dupPathname; + case ENOTEMPTY: + return invalidAccess; + + default: + return drvrIOError; + } +} + +word32 host_map_errno_path(int xerrno, const char *path) { + if (xerrno == ENOENT) return enoent(path); + return host_map_errno(xerrno); +} + diff --git a/src/win32_host_common.c b/src/win32_host_common.c index 1b3e016..7e22cc5 100644 --- a/src/win32_host_common.c +++ b/src/win32_host_common.c @@ -11,6 +11,11 @@ #include "host_common.h" +#ifdef _MSC_VER +#define strcasecmp stricmp +#define strncasecmp strnicmp +#endif + void afp_init(struct AFP_Info *info, word16 file_type, word32 aux_type) { @@ -66,7 +71,7 @@ void afp_synchronize(struct AFP_Info *info, int preference) { -static DWORD root_file_id[3] = {}; +static DWORD root_file_id[3] = { 0 }; unsigned host_startup(void) { @@ -97,10 +102,8 @@ unsigned host_startup(void) { if (!(fbi.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { fprintf(stderr, "%s is not a directory\n", host_root); - CloseHandle(h); return invalidFSTop; } - CloseHandle(h); return 0; } @@ -231,8 +234,8 @@ FILETIME host_get_date_time(word32 ptr) { TzSpecificLocalTimeToSystemTime(NULL, &tmLocal, &tmUTC); - if (!SystemTimeToFileTime(&tmUTC, &utc)) utc =(FILETIME){0, 0}; - + if (!SystemTimeToFileTime(&tmUTC, &utc)) memset(&utc, 0, sizeof(utc)); + return utc; } @@ -249,6 +252,7 @@ FILETIME host_get_date_time_rec(word32 ptr) { SYSTEMTIME tmUTC; memset(&tmLocal, 0, sizeof(tmLocal)); memset(&tmUTC, 0, sizeof(tmUTC)); + memset(&utc, 0, sizeof(utc)); tmLocal.wSecond = buffer[0]; tmLocal.wMinute = buffer[1]; @@ -258,7 +262,7 @@ FILETIME host_get_date_time_rec(word32 ptr) { tmLocal.wMonth = buffer[5] + 1; TzSpecificLocalTimeToSystemTime(NULL, &tmLocal, &tmUTC); - if (!SystemTimeToFileTime(&tmUTC, &utc)) utc =(FILETIME){0, 0}; + if (!SystemTimeToFileTime(&tmUTC, &utc)) memset(&utc, 0, sizeof(utc)); return utc; } diff --git a/src/win32_host_fst.c b/src/win32_host_fst.c index 05f3360..824edbc 100644 --- a/src/win32_host_fst.c +++ b/src/win32_host_fst.c @@ -20,6 +20,12 @@ #include "host_common.h" +#ifdef _MSC_VER +#define strcasecmp stricmp +#define strncasecmp strnicmp +#endif + + extern Engine_reg engine; @@ -62,7 +68,7 @@ static struct directory *read_directory(const char *path, word16 *error); #define COOKIE_BASE 0x8000 -static word32 cookies[32] = {}; +static word32 cookies[32] = { 0 }; static int alloc_cookie() { for (int i = 0; i < 32; ++i) { @@ -746,7 +752,7 @@ static struct directory *read_directory(const char *path, word16 *error) { size = sizeof(struct directory) + capacity * sizeof(char *); struct directory * tmp = realloc(dd, size); if (!tmp) { - *error = host_map_errno(errno); + *error = outOfMem; free_directory(dd); FindClose(h); return NULL; From 55de5068125bde4763e3a3548cd9fd9b867cce73 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Fri, 4 Jan 2019 22:58:56 -0500 Subject: [PATCH 034/186] msvc warnings --- src/to_pro.c | 5 +++++ src/win32_host_fst.c | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/to_pro.c b/src/to_pro.c index c74f70b..3beb9e3 100755 --- a/src/to_pro.c +++ b/src/to_pro.c @@ -16,6 +16,11 @@ #include #endif +#ifdef _MSC_VER +#define strcasecmp stricmp +#define strncasecmp strnicmp +#endif + #define DEF_DISK_SIZE (800*1024) #define MAX_FILE_NAMES 51 diff --git a/src/win32_host_fst.c b/src/win32_host_fst.c index 824edbc..8922a65 100644 --- a/src/win32_host_fst.c +++ b/src/win32_host_fst.c @@ -1213,11 +1213,11 @@ static word16 win_seek(HANDLE h, word16 base, word32 displacement, LARGE_INTEGER break; case markMinus: m = FILE_CURRENT; - d.QuadPart = -displacement; + d.QuadPart = -(LONGLONG)displacement; break; case eofMinus: m = FILE_END; - d.QuadPart = -displacement; + d.QuadPart = -(LONGLONG)displacement; break; default: From 4a5d6effd51c084c1569fdec81680222bcff4eb5 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Sun, 6 Jan 2019 22:58:41 -0500 Subject: [PATCH 035/186] to work around vmnet root/entitlement restrictions, add a stand-alone helper utility than can be setuid root. vmnet_helper creates a vmnet connection then communicates back to GS+ via pipes to stdin/stdout and a simple binary messaging protocol. --- src/CMakeLists.txt | 12 + src/rawnet/CMakeLists.txt | 9 +- src/rawnet/rawnet.c | 3 +- src/rawnet/rawnetarch.c | 2 +- src/rawnet/rawnetarch_darwin.c | 2 + src/rawnet/rawnetarch_vmnet_helper.c | 673 +++++++++++++++++++++++++++ src/rawnet/vmnet_helper.c | 331 +++++++++++++ 7 files changed, 1027 insertions(+), 5 deletions(-) create mode 100644 src/rawnet/rawnetarch_vmnet_helper.c create mode 100644 src/rawnet/vmnet_helper.c diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b6ec3bc..2702d7e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -134,6 +134,18 @@ SET_SOURCE_FILES_PROPERTIES( MACOSX_PACKAGE_LOCATION Resources ) +if(APPLE) + add_custom_command(TARGET GSplus POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${CMAKE_CURRENT_BINARY_DIR}/rawnet/vmnet_helper" + "${CMAKE_CURRENT_BINARY_DIR}/GSplus.app/Contents/MacOS/vmnet_helper" + # COMMAND sudo chown root "${CMAKE_CURRENT_BINARY_DIR}/GSplus.app/Contents/MacOS/vmnet_helper" + # COMMAND sudo chmod +s "${CMAKE_CURRENT_BINARY_DIR}/GSplus.app/Contents/MacOS/vmnet_helper" + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/rawnet/vmnet_helper) +endif() +# SET_SOURCE_FILES_PROPERTIES(vmnet_helper PROPERTIES MACOSX_PACKAGE_LOCATION MacOS) + + if (WITH_RAWNET) target_link_libraries(GSplus rawnet) endif() diff --git a/src/rawnet/CMakeLists.txt b/src/rawnet/CMakeLists.txt index 2e70bc4..a38b88b 100644 --- a/src/rawnet/CMakeLists.txt +++ b/src/rawnet/CMakeLists.txt @@ -2,7 +2,7 @@ if(WIN32) set(rawnetarch rawnetarch_win32.c) elseif(APPLE) - set(rawnetarch rawnetarch_darwin.c) + set(rawnetarch rawnetarch_vmnet_helper.c) #set(rawnetarch rawnetarch_unix.c) elseif(UNIX) set(rawnetarch rawnetarch_unix.c) @@ -13,11 +13,16 @@ add_library(rawnet cs8900.c rawnet.c rawnetsupp.c rawnetarch.c ${rawnetarch}) target_compile_definitions(rawnet PUBLIC HAVE_RAWNET) target_compile_definitions(rawnet PRIVATE CS8900_DEBUG RAWNET_DEBUG_FRAMES) +target_compile_options(rawnet PRIVATE -g) + if(WIN32) target_link_libraries(rawnet ws2_32) # winsock2 elseif(APPLE) #target_link_libraries(rawnet PRIVATE pcap) - target_link_libraries(rawnet PRIVATE "-framework vmnet") + #target_link_libraries(rawnet PRIVATE "-framework vmnet") + add_executable(vmnet_helper vmnet_helper.c) + target_link_libraries(vmnet_helper PRIVATE "-framework vmnet") + elseif(UNIX) target_link_libraries(rawnet PRIVATE pcap) endif() \ No newline at end of file diff --git a/src/rawnet/rawnet.c b/src/rawnet/rawnet.c index 7ccedfb..3146130 100644 --- a/src/rawnet/rawnet.c +++ b/src/rawnet/rawnet.c @@ -77,10 +77,9 @@ char *rawnet_get_standard_interface(void) return rawnet_arch_get_standard_interface(); } -extern int rawnet_status(void) +int rawnet_status(void) { return rawnet_arch_status(); } - #endif /* #ifdef HAVE_RAWNET */ diff --git a/src/rawnet/rawnetarch.c b/src/rawnet/rawnetarch.c index 560b918..7149de8 100644 --- a/src/rawnet/rawnetarch.c +++ b/src/rawnet/rawnetarch.c @@ -121,7 +121,7 @@ int rawnet_arch_receive(uint8_t *pbuffer, int *plen, int *phashed, assert((*plen & 1) == 0); ok = rawnet_arch_read(pbuffer, *plen); - if (ok < 0) return 0; + if (ok <= 0) return 0; if (ok & 1) ++ok; *plen = ok; diff --git a/src/rawnet/rawnetarch_darwin.c b/src/rawnet/rawnetarch_darwin.c index 8aa8cc8..5a0c0a1 100644 --- a/src/rawnet/rawnetarch_darwin.c +++ b/src/rawnet/rawnetarch_darwin.c @@ -50,6 +50,8 @@ int rawnet_arch_activate(const char *interface_name) { * *MIGHT* re-use the previous MAC address. */ + if (interface) return 1; + memset(interface_mac, 0, sizeof(interface_mac)); memset(interface_fake_mac, 0, sizeof(interface_fake_mac)); interface_status = 0; diff --git a/src/rawnet/rawnetarch_vmnet_helper.c b/src/rawnet/rawnetarch_vmnet_helper.c new file mode 100644 index 0000000..2e8a9cc --- /dev/null +++ b/src/rawnet/rawnetarch_vmnet_helper.c @@ -0,0 +1,673 @@ +/* + * OS X 10.10+ + * vmnet support (clang -framework vmnet -framework Foundation) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rawnetarch.h" +#include "rawnetsupp.h" + + +enum { + MSG_QUIT, + MSG_STATUS, + MSG_READ, + MSG_WRITE +}; +#define MAKE_MSG(msg, extra) (msg | ((extra) << 8)) + +static const uint8_t broadcast_mac[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + +static uint8_t interface_mac[6]; +static uint8_t interface_fake_mac[6]; +static uint32_t interface_mtu; +static uint32_t interface_packet_size; +static uint8_t *interface_buffer = NULL; + +static pid_t interface_pid = 0; +static int interface_pipe[2]; + + +static pid_t safe_waitpid(pid_t pid, int *stat_loc, int options) { + for(;;) { + pid_t rv = waitpid(pid, stat_loc,options); + if (rv < 0 && errno == EINTR) continue; + return rv; + } +} + +static ssize_t safe_read(void *data, size_t nbytes) { + for (;;) { + ssize_t rv = read(interface_pipe[0], data, nbytes); + if (rv < 0 && errno == EINTR) continue; + return rv; + } +} + +static ssize_t safe_readv(const struct iovec *iov, int iovcnt) { + + for(;;) { + ssize_t rv = readv(interface_pipe[0], iov, iovcnt); + if (rv < 0 && errno == EINTR) continue; + return rv; + } +} + + +static ssize_t safe_write(const void *data, size_t nbytes) { + for (;;) { + ssize_t rv = write(interface_pipe[1], data, nbytes); + if (rv < 0 && errno == EINTR) continue; + return rv; + } +} + +static ssize_t safe_writev(const struct iovec *iov, int iovcnt) { + + for(;;) { + ssize_t rv = writev(interface_pipe[1], iov, iovcnt); + if (rv < 0 && errno == EINTR) continue; + return rv; + } +} + +/* block the sigpipe signal */ +static int block_pipe(struct sigaction *oact) { + struct sigaction act; + memset(&act, 0, sizeof(act)); + act.sa_handler = SIG_IGN; + act.sa_flags = SA_RESTART; + + return sigaction(SIGPIPE, &act, oact); +} +static int restore_pipe(const struct sigaction *oact) { + return sigaction(SIGPIPE, oact, NULL); +} + +#if 0 +static int block_pipe(sigset_t *oldset) { + sigset_t set; + + sigemptyset(&set); + sigaddset(&set, SIGPIPE); + sigaddset(&set, SIGCHLD); + + return sigprocmask(SIG_BLOCK, &set, oldset); +} +#endif + +static int check_child_status(void) { + pid_t pid; + int stat; + pid = safe_waitpid(interface_pid, &stat, WNOHANG); + + if (pid < 0 && errno == ECHILD) { + fprintf(stderr, "child process does not exist.\n"); + close(interface_pipe[0]); + close(interface_pipe[1]); + interface_pid = 0; + return 0; + } + if (pid == interface_pid) { + if (WIFEXITED(stat)) fprintf(stderr, "child process exited.\n"); + if (WIFSIGNALED(stat)) fprintf(stderr, "child process signalled.\n"); + + close(interface_pipe[0]); + close(interface_pipe[1]); + interface_pid = 0; + return 0; + } + return 1; +} + +static char *get_relative_path(const char *leaf) { + + uint32_t size = 0; + char *buffer = 0; + int ok; + char *cp; + int l; + + l = strlen(leaf); + ok = _NSGetExecutablePath(NULL, &size); + size += l + 1; + buffer = malloc(size); + if (buffer) { + ok = _NSGetExecutablePath(buffer, &size); + if (ok < 0) { + free(buffer); + return NULL; + } + cp = strrchr(buffer, '/'); + if (cp) + strcpy(cp + 1 , leaf); + else { + free(buffer); + buffer = NULL; + } + } + return buffer; +} + + +int rawnet_arch_init(void) { + //interface = NULL; + return 1; +} +void rawnet_arch_pre_reset(void) { + /* NOP */ +} + +void rawnet_arch_post_reset(void) { + /* NOP */ +} + + + + +int rawnet_arch_activate(const char *interface_name) { + + + int ok; + posix_spawn_file_actions_t actions; + posix_spawnattr_t attr; + char *argv[] = { "vmnet_helper", NULL }; + char *path = NULL; + + int pipe_stdin[2]; + int pipe_stdout[2]; + + struct sigaction oldaction; + + + if (interface_pid > 0) return 1; + + /* fd[0] = read, fd[1] = write */ + ok = pipe(pipe_stdin); + if (ok < 0) { warn("pipe"); return 0; } + + ok = pipe(pipe_stdout); + if (ok < 0) { warn("pipe"); return 0; } + + block_pipe(&oldaction); + + posix_spawn_file_actions_init(&actions); + posix_spawnattr_init(&attr); + + + posix_spawn_file_actions_adddup2(&actions, pipe_stdin[0], STDIN_FILENO); + posix_spawn_file_actions_adddup2(&actions, pipe_stdout[1], STDOUT_FILENO); + + posix_spawn_file_actions_addclose(&actions, pipe_stdin[0]); + posix_spawn_file_actions_addclose(&actions, pipe_stdin[1]); + + posix_spawn_file_actions_addclose(&actions, pipe_stdout[0]); + posix_spawn_file_actions_addclose(&actions, pipe_stdout[1]); + + + path = get_relative_path("vmnet_helper"); + ok = posix_spawn(&interface_pid, path, &actions, &attr, argv, NULL); + free(path); + posix_spawn_file_actions_destroy(&actions); + posix_spawnattr_destroy(&attr); + + close(pipe_stdin[0]); + close(pipe_stdout[1]); + /* posix spawn returns 0 on success, error code on failure. */ + + if (ok != 0) { + fprintf(stderr, "posix_spawn vmnet_helper failed: %d\n", ok); + close(pipe_stdin[1]); + close(pipe_stdout[0]); + interface_pid = -1; + return 0; + } + + interface_pipe[0] = pipe_stdout[0]; + interface_pipe[1] = pipe_stdin[1]; + + /* now get the mac/mtu/etc */ + + uint32_t msg = MAKE_MSG(MSG_STATUS, 0); + ok = safe_write(&msg, 4); + if (ok != 4) goto fail; + + ok = safe_read(&msg, 4); + if (ok != 4) goto fail; + + if (msg != MAKE_MSG(MSG_STATUS, 6 + 4 + 4)) goto fail; + + struct iovec iov[3]; + iov[0].iov_len = 6; + iov[0].iov_base = interface_mac; + iov[1].iov_len = 4; + iov[1].iov_base = &interface_mtu; + iov[2].iov_len = 4; + iov[2].iov_base = &interface_packet_size; + + ok = safe_readv(iov, 3); + if (ok != 6 + 4 + 4) goto fail; + + /* sanity check */ + /* expect MTU = 1500, packet_size = 1518 */ + if (interface_packet_size < 256) { + interface_packet_size = 1518; + } + interface_buffer = malloc(interface_packet_size); + if (!interface_buffer) goto fail; + + restore_pipe(&oldaction); + return 1; + +fail: + close(interface_pipe[0]); + close(interface_pipe[1]); + safe_waitpid(interface_pid, NULL, 0); + interface_pid = 0; + + restore_pipe(&oldaction); + return 0; +} + +void rawnet_arch_deactivate(void) { + + if (interface_pid) { + close(interface_pipe[0]); + close(interface_pipe[1]); + for(;;) { + int ok = waitpid(interface_pid, NULL, 0); + if (ok < 0 && errno == EINTR) continue; + break; + } + interface_pid = 0; + } + free(interface_buffer); + interface_buffer = NULL; +} + +void rawnet_arch_set_mac(const uint8_t mac[6]) { + +#ifdef RAWNET_DEBUG_ARCH + log_message( rawnet_arch_log, "New MAC address set: %02X:%02X:%02X:%02X:%02X:%02X.", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5] ); +#endif + memcpy(interface_fake_mac, mac, 6); +} +void rawnet_arch_set_hashfilter(const uint32_t hash_mask[2]) { + /* NOP */ +} + +void rawnet_arch_recv_ctl(int bBroadcast, int bIA, int bMulticast, int bCorrect, int bPromiscuous, int bIAHash) { + /* NOP */ +} + +void rawnet_arch_line_ctl(int bEnableTransmitter, int bEnableReceiver) { + /* NOP */ +} + + + + +enum { + eth_dest = 0, // destination address + eth_src = 6, // source address + eth_type = 12, // packet type + eth_data = 14, // packet data +}; + +enum { + ip_ver_ihl = 0, + ip_tos = 1, + ip_len = 2, + ip_id = 4, + ip_frag = 6, + ip_ttl = 8, + ip_proto = 9, + ip_header_cksum = 10, + ip_src = 12, + ip_dest = 16, + ip_data = 20, +}; + +enum { + udp_source = 0, // source port + udp_dest = 2, // destination port + udp_len = 4, // length + udp_cksum = 6, // checksum + udp_data = 8, // total length udp header +}; + +enum { + bootp_op = 0, // operation + bootp_hw = 1, // hardware type + bootp_hlen = 2, // hardware len + bootp_hp = 3, // hops + bootp_transid = 4, // transaction id + bootp_secs = 8, // seconds since start + bootp_flags = 10, // flags + bootp_ipaddr = 12, // ip address knwon by client + bootp_ipclient = 16, // client ip from server + bootp_ipserver = 20, // server ip + bootp_ipgateway = 24, // gateway ip + bootp_client_hrd = 28, // client mac address + bootp_spare = 34, + bootp_host = 44, + bootp_fname = 108, + bootp_data = 236, // total length bootp packet +}; + +enum { + arp_hw = 14, // hw type (eth = 0001) + arp_proto = 16, // protocol (ip = 0800) + arp_hwlen = 18, // hw addr len (eth = 06) + arp_protolen = 19, // proto addr len (ip = 04) + arp_op = 20, // request = 0001, reply = 0002 + arp_shw = 22, // sender hw addr + arp_sp = 28, // sender proto addr + arp_thw = 32, // target hw addr + arp_tp = 38, // target protoaddr + arp_data = 42, // total length of packet +}; + +enum { + dhcp_discover = 1, + dhcp_offer = 2, + dhcp_request = 3, + dhcp_decline = 4, + dhcp_pack = 5, + dhcp_nack = 6, + dhcp_release = 7, + dhcp_inform = 8, +}; + +static uint8_t oo[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static uint8_t ff[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + +static int is_arp(const uint8_t *packet, unsigned size) { + return size == arp_data + && packet[12] == 0x08 && packet[13] == 0x06 /* ARP */ + && packet[14] == 0x00 && packet[15] == 0x01 /* ethernet */ + && packet[16] == 0x08 && packet[17] == 0x00 /* ipv4 */ + && packet[18] == 0x06 /* hardware size */ + && packet[19] == 0x04 /* protocol size */ + ; +} + +static int is_broadcast(const uint8_t *packet, unsigned size) { + return !memcmp(packet + 0, ff, 6); +} + +static int is_unicast(const uint8_t *packet, unsigned size) { + return (*packet & 0x01) == 0; +} + +static int is_multicast(const uint8_t *packet, unsigned size) { + return (*packet & 0x01) == 0x01 && !is_broadcast(packet, size); +} + +static int is_dhcp_out(const uint8_t *packet, unsigned size) { + static uint8_t cookie[] = { 0x63, 0x82, 0x53, 0x63 }; + return size >= 282 + //&& !memcmp(&packet[0], ff, 6) /* broadcast */ + && packet[12] == 0x08 && packet[13] == 0x00 + && packet[14] == 0x45 /* version 4 */ + && packet[23] == 0x11 /* UDP */ + //&& !memcmp(&packet[26], oo, 4) /* source ip */ + //&& !memcmp(&packet[30], ff, 4) /* dest ip */ + && packet[34] == 0x00 && packet[35] == 0x44 /* source port */ + && packet[36] == 0x00 && packet[37] == 0x43 /* dest port */ + //&& packet[44] == 0x01 /* dhcp boot req */ + && packet[43] == 0x01 /* ethernet */ + && packet[44] == 0x06 /* 6 byte mac */ + && !memcmp(&packet[278], cookie, 4) + ; +} + + +static int is_dhcp_in(const uint8_t *packet, unsigned size) { + static uint8_t cookie[] = { 0x63, 0x82, 0x53, 0x63 }; + return size >= 282 + //&& !memcmp(&packet[0], ff, 6) /* broadcast */ + && packet[12] == 0x08 && packet[13] == 0x00 + && packet[14] == 0x45 /* version 4 */ + && packet[23] == 0x11 /* UDP */ + //&& !memcmp(&packet[26], oo, 4) /* source ip */ + //&& !memcmp(&packet[30], ff, 4) /* dest ip */ + && packet[34] == 0x00 && packet[35] == 0x43 /* source port */ + && packet[36] == 0x00 && packet[37] == 0x44 /* dest port */ + //&& packet[44] == 0x01 /* dhcp boot req */ + && packet[43] == 0x01 /* ethernet */ + && packet[44] == 0x06 /* 6 byte mac */ + && !memcmp(&packet[278], cookie, 4) + ; +} + +static unsigned ip_checksum(const uint8_t *packet) { + unsigned x = 0; + unsigned i; + for (i = 0; i < ip_data; i += 2) { + if (i == ip_header_cksum) continue; + x += packet[eth_data + i + 0 ] << 8; + x += packet[eth_data + i + 1]; + } + + /* add the carry */ + x += x >> 16; + x &= 0xffff; + return ~x & 0xffff; +} + +static void fix_incoming_packet(uint8_t *packet, unsigned size, const uint8_t real_mac[6], const uint8_t fake_mac[6]) { + + if (memcmp(packet + 0, real_mac, 6) == 0) + memcpy(packet + 0, fake_mac, 6); + + /* dhcp request - fix the hardware address */ + if (is_unicast(packet, size) && is_dhcp_in(packet, size)) { + if (!memcmp(packet + 70, real_mac, 6)) + memcpy(packet + 70, fake_mac, 6); + return; + } + +} + +static void fix_outgoing_packet(uint8_t *packet, unsigned size, const uint8_t real_mac[6], const uint8_t fake_mac[6]) { + + + + if (memcmp(packet + 6, fake_mac, 6) == 0) + memcpy(packet + 6, real_mac, 6); + + if (is_arp(packet, size)) { + /* sender mac address */ + if (!memcmp(packet + 22, fake_mac, 6)) + memcpy(packet + 22, real_mac, 6); + return; + } + + /* dhcp request - fix the hardware address */ + if (is_broadcast(packet, size) && is_dhcp_out(packet, size)) { + + if (!memcmp(packet + 70, fake_mac, 6)) + memcpy(packet + 70, real_mac, 6); + return; + } + +} + + +int rawnet_arch_read(void *buffer, int nbyte) { + + + uint32_t msg; + int ok; + int xfer; + struct sigaction oldaction; + + if (interface_pid <= 0) return -1; + + block_pipe(&oldaction); + + msg = MAKE_MSG(MSG_READ, 0); + + ok = safe_write(&msg, 4); + if (ok != 4) goto fail; + + ok = safe_read(&msg, 4); + if (ok != 4) goto fail; + + if ((msg & 0xff) != MSG_READ) goto fail; + + xfer = msg >> 8; + if (xfer > interface_packet_size) { + + fprintf(stderr, "packet size too big: %d\n", xfer); + + /* drain the message ... */ + while (xfer) { + int count = interface_packet_size; + if (count > xfer) count = xfer; + ok = safe_read(interface_buffer, count); + if (ok < 0) goto fail; + xfer -= ok; + } + return -1; + } + + if (xfer == 0) return -1; + + ok = safe_read(interface_buffer, xfer); + if (ok != xfer) goto fail; + + + fix_incoming_packet(interface_buffer, xfer, interface_mac, interface_fake_mac); + + if (!is_multicast(interface_buffer, xfer)) { /* multicast crap */ + fprintf(stderr, "\nrawnet_arch_receive: %u\n", (unsigned)xfer); + rawnet_hexdump(interface_buffer, xfer); + } + + if (xfer > nbyte) xfer = nbyte; + memcpy(buffer, interface_buffer, xfer); + + restore_pipe(&oldaction); + return xfer; + +fail: + /* check if process still ok? */ + check_child_status(); + restore_pipe(&oldaction); + return -1; +} + +int rawnet_arch_write(const void *buffer, int nbyte) { + + int ok; + uint32_t msg; + struct iovec iov[2]; + struct sigaction oldaction; + + if (interface_pid <= 0) return -1; + + + + if (nbyte <= 0) return 0; + + if (nbyte > interface_packet_size) { + log_message(rawnet_arch_log, "packet is too big: %d", nbyte); + return -1; + } + + + /* copy the buffer and fix the source mac address. */ + memcpy(interface_buffer, buffer, nbyte); + fix_outgoing_packet(interface_buffer, nbyte, interface_mac, interface_fake_mac); + + + fprintf(stderr, "\nrawnet_arch_transmit: %u\n", (unsigned)nbyte); + rawnet_hexdump(interface_buffer, nbyte); + + + block_pipe(&oldaction); + + msg = MAKE_MSG(MSG_WRITE, nbyte); + + iov[0].iov_base = &msg; + iov[0].iov_len = 4; + iov[1].iov_base = interface_buffer; + iov[1].iov_len = nbyte; + + + ok = safe_writev(iov, 2); + if (ok != 4 + nbyte) goto fail; + + ok = safe_read(&msg, 4); + if (ok != 4) goto fail; + + if (msg != MAKE_MSG(MSG_WRITE, nbyte)) goto fail; + + restore_pipe(&oldaction); + return nbyte; + +fail: + check_child_status(); + restore_pipe(&oldaction); + return -1; +} + + + + +static unsigned adapter_index = 0; +int rawnet_arch_enumadapter_open(void) { + adapter_index = 0; + return 1; +} + +int rawnet_arch_enumadapter(char **ppname, char **ppdescription) { + + if (adapter_index == 0) { + ++adapter_index; + if (ppname) *ppname = lib_stralloc("vmnet"); + if (ppdescription) *ppdescription = lib_stralloc("vmnet"); + return 1; + } + return 0; +} + +int rawnet_arch_enumadapter_close(void) { + return 1; +} + +char *rawnet_arch_get_standard_interface(void) { + return lib_stralloc("vmnet"); +} + +int rawnet_arch_get_mtu(void) { + return interface_pid > 0 ? interface_mtu : -1; +} + +int rawnet_arch_get_mac(uint8_t mac[6]) { + if (interface_pid > 0) { + memcpy(mac, interface_mac, 6); + return 1; + } + return -1; +} + + +int rawnet_arch_status(void) { + return interface_pid > 0 ? 1 : 0; +} + diff --git a/src/rawnet/vmnet_helper.c b/src/rawnet/vmnet_helper.c new file mode 100644 index 0000000..6d108d0 --- /dev/null +++ b/src/rawnet/vmnet_helper.c @@ -0,0 +1,331 @@ +/* vmnet helper */ +/* because it needs root permissions ... sigh */ + +/* + * basicly... run as root, read messages from stdin, write to stdout. + */ + + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +static interface_ref interface; +static uint8_t interface_mac[6]; +static long interface_mtu; +static long interface_packet_size; +static vmnet_return_t interface_status; + +static size_t buffer_size = 0; +static uint8_t *buffer = NULL; + +enum { + MSG_QUIT, + MSG_STATUS, + MSG_READ, + MSG_WRITE +}; +#define MAKE_MSG(msg, extra) (msg | ((extra) << 8)) + +ssize_t safe_read(void *buffer, size_t nbyte) { + + ssize_t rv; + for(;;) { + rv = read(STDIN_FILENO, buffer, nbyte); + if (rv < 0) { + if (errno == EINTR) continue; + err(1, "read"); + } + break; + } + return rv; +} + + +ssize_t safe_readv(const struct iovec *iov, int iovcnt) { + + ssize_t rv; + for(;;) { + rv = readv(STDIN_FILENO, iov, iovcnt); + if (rv < 0) { + if (errno == EINTR) continue; + err(1, "readv"); + } + break; + } + return rv; +} + +ssize_t safe_write(const void *buffer, size_t nbyte) { + + ssize_t rv; + for(;;) { + rv = write(STDOUT_FILENO, buffer, nbyte); + if (rv < 0) { + if (errno == EINTR) continue; + err(1, "write"); + } + break; + } + return rv; +} + +ssize_t safe_writev(const struct iovec *iov, int iovcnt) { + + ssize_t rv; + for(;;) { + rv = writev(STDOUT_FILENO, iov, iovcnt); + if (rv < 0) { + if (errno == EINTR) continue; + err(1, "writev"); + } + break; + } + return rv; +} + + +void msg_status(uint32_t size) { + struct iovec iov[4]; + + uint32_t msg = MAKE_MSG(MSG_STATUS, 6 + 4 + 4); + + iov[0].iov_len = 4; + iov[0].iov_base = &msg; + + iov[1].iov_len = 6; + iov[1].iov_base = interface_mac; + + iov[2].iov_len = 4; + iov[2].iov_base = &interface_mtu; + + iov[3].iov_len = 4; + iov[3].iov_base = &interface_packet_size; + + + safe_writev(iov, 4); +} + +int classify_mac(uint8_t *mac) { + if ((mac[0] & 0x01) == 0) return 1; /* unicast */ + if (memcmp(mac, "\xff\xff\xff\xff\xff\xff", 6) == 0) return 0xff; /* broadcast */ + return 2; /* multicast */ +} + +void msg_read(uint32_t flags) { + /* flag to block broadcast, multicast, etc? */ + + int count = 1; + int xfer; + vmnet_return_t st; + struct vmpktdesc v; + struct iovec iov[2]; + + uint32_t msg; + + + for(;;) { + int type; + + iov[0].iov_base = buffer; + iov[0].iov_len = interface_packet_size; + + v.vm_pkt_size = interface_packet_size; + v.vm_pkt_iov = iov; + v.vm_pkt_iovcnt = 1; + v.vm_flags = 0; + + count = 1; + st = vmnet_read(interface, &v, &count); + if (st != VMNET_SUCCESS) errx(1, "vmnet_read"); + + if (count < 1) break; + /* todo -- skip multicast messages based on flag? */ + type = classify_mac(buffer); + if (type == 2) continue; /* multicast */ + break; + } + + xfer = count == 1 ? v.vm_pkt_size : 0; + msg = MAKE_MSG(MSG_READ, xfer); + iov[0].iov_len = 4; + iov[0].iov_base = &msg; + iov[1].iov_len = xfer; + iov[1].iov_base = buffer; + + safe_writev(iov, count == 1 ? 2 : 1); +} + + +void msg_write(uint32_t size) { + + ssize_t ok; + + int count = 1; + vmnet_return_t st; + struct vmpktdesc v; + struct iovec iov; + uint32_t msg; + + if (size > interface_packet_size) errx(1, "packet too big"); + for(;;) { + ok = safe_read(buffer, size); + if (ok < 0) err(1,"read"); + if (ok != size) errx(1,"message truncated"); + break; + } + + iov.iov_base = buffer; + iov.iov_len = size; + + v.vm_pkt_size = size; + v.vm_pkt_iov = &iov; + v.vm_pkt_iovcnt = 1; + v.vm_flags = 0; + + st = vmnet_write(interface, &v, &count); + + if (st != VMNET_SUCCESS) errx(1, "vmnet_write"); + + + msg = MAKE_MSG(MSG_WRITE, size); + iov.iov_len = 4; + iov.iov_base = &msg; + + safe_writev(&iov, 1); +} + +void startup(void) { + + xpc_object_t dict; + dispatch_queue_t q; + dispatch_semaphore_t sem; + + + memset(interface_mac, 0, sizeof(interface_mac)); + interface_status = 0; + interface_mtu = 0; + interface_packet_size = 0; + + dict = xpc_dictionary_create(NULL, NULL, 0); + xpc_dictionary_set_uint64(dict, vmnet_operation_mode_key, VMNET_SHARED_MODE); + sem = dispatch_semaphore_create(0); + q = dispatch_get_global_queue(QOS_CLASS_UTILITY, 0); + + interface = vmnet_start_interface(dict, q, ^(vmnet_return_t status, xpc_object_t params){ + interface_status = status; + if (status == VMNET_SUCCESS) { + const char *cp; + cp = xpc_dictionary_get_string(params, vmnet_mac_address_key); + fprintf(stderr, "vmnet mac: %s\n", cp); + sscanf(cp, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", + &interface_mac[0], + &interface_mac[1], + &interface_mac[2], + &interface_mac[3], + &interface_mac[4], + &interface_mac[5] + ); + + interface_mtu = xpc_dictionary_get_uint64(params, vmnet_mtu_key); + interface_packet_size = xpc_dictionary_get_uint64(params, vmnet_max_packet_size_key); + + fprintf(stderr, "vmnet mtu: %u\n", (unsigned)interface_mtu); + fprintf(stderr, "vmnet packet size: %u\n", (unsigned)interface_packet_size); + + } + dispatch_semaphore_signal(sem); + }); + dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); + + if (interface_status == VMNET_SUCCESS) { + buffer_size = (interface_packet_size * 2 + 1023) & ~1023; + buffer = (uint8_t *)malloc(buffer_size); + } else { + if (interface) { + vmnet_stop_interface(interface, q, ^(vmnet_return_t status){ + dispatch_semaphore_signal(sem); + }); + dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); + interface = NULL; + } + errx(1,"vmnet_start_interface failed"); + } + + + dispatch_release(sem); + xpc_release(dict); + +} + +void shutdown(void) { + + dispatch_queue_t q; + dispatch_semaphore_t sem; + + + if (interface) { + sem = dispatch_semaphore_create(0); + q = dispatch_get_global_queue(QOS_CLASS_UTILITY, 0); + + vmnet_stop_interface(interface, q, ^(vmnet_return_t status){ + dispatch_semaphore_signal(sem); + }); + dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); + dispatch_release(sem); + + interface = NULL; + interface_status = 0; + } + free(buffer); + buffer = NULL; + buffer_size = 0; + +} + +int main(int argc, char **argv) { + + + uint32_t msg; + uint32_t extra; + ssize_t ok; + + + startup(); + + for(;;) { + ok = safe_read(&msg, 4); + if (ok == 0) break; + if (ok != 4) errx(1,"read msg"); + + extra = msg >> 8; + msg = msg & 0xff; + + switch(msg) { + case MSG_STATUS: + msg_status(extra); + break; + case MSG_QUIT: + shutdown(); + exit(0); + case MSG_READ: + msg_read(extra); + break; + case MSG_WRITE: + msg_write(extra); + break; + } + } + + shutdown(); + exit(0); +} From 5e852773cac9d065d497ebb6b3aad3687e035f0f Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Tue, 8 Jan 2019 18:41:55 -0500 Subject: [PATCH 036/186] os x - setuid helper utility to connect to the bridge interface. --- src/rawnet/rawnetarch_tap.c | 74 ++++++- ...rawnetarch_darwin.c => rawnetarch_vmnet.c} | 202 +----------------- src/rawnet/rawnetarch_vmnet_helper.c | 201 +---------------- src/rawnet/rawnetsupp.c | 191 +++++++++++++++++ src/rawnet/rawnetsupp.h | 3 + 5 files changed, 279 insertions(+), 392 deletions(-) rename src/rawnet/{rawnetarch_darwin.c => rawnetarch_vmnet.c} (52%) diff --git a/src/rawnet/rawnetarch_tap.c b/src/rawnet/rawnetarch_tap.c index 940a74f..a7c7e88 100644 --- a/src/rawnet/rawnetarch_tap.c +++ b/src/rawnet/rawnetarch_tap.c @@ -82,6 +82,12 @@ static int interface_fd = -1; static char *interface_dev = NULL; +static uint8_t interface_mac[6]; +static uint8_t interface_fake_mac[6]; + +static uint8_t *interface_buffer = NULL; +static int interface_buffer_size = 0; + int rawnet_arch_init(void) { interface_fd = -1; return 1; @@ -94,6 +100,18 @@ void rawnet_arch_post_reset(void) { /* NOP */ } +/* memoized buffer for ethernet packets, etc */ +static int make_buffer(int size) { + if (size <= interface_buffer_size) return 0; + if (interface_buffer) free(interface_buffer); + if (size < 1500) size = 1500; /* good mtu size */ + interface_buffer_size = 0; + size *= 2; + interface_buffer = malloc(size); + if (!interface_buffer) return -1; + interface_buffer_size = size; + return 0; +} #if defined(__linux__) /* interface name. default is tap65816. */ @@ -132,6 +150,15 @@ int rawnet_arch_activate(const char *interface_name) { return 0; } + if (rawnet_arch_get_mac(interface_mac) < 0) { + perror("rawnet_arch_get_mac"); + close(fd); + return 0; + } + /* copy mac to fake mac */ + memcpy(interface_fake_mac, interface_mac, 6); + + interface_dev = strdup(interface_name); interface_fd = fd; return 1; @@ -170,6 +197,16 @@ int rawnet_arch_activate(const char *interface_name) { return 0; } + if (rawnet_arch_get_mac(interface_mac) < 0) { + perror("rawnet_arch_get_mac"); + close(fd); + return 0; + } + /* copy mac to fake mac */ + memcpy(interface_fake_mac, interface_mac, 6); + + + interface_dev = strdup(interface_name); interface_fd = fd; return 1; @@ -184,6 +221,10 @@ void rawnet_arch_deactivate(void) { close(interface_fd); free(interface_dev); + free(interface_buffer); + interface_buffer = 0; + interface_buffer_size = 0; + interface_dev = NULL; interface_fd = -1; } @@ -194,6 +235,14 @@ void rawnet_arch_set_mac(const uint8_t mac[6]) { #ifdef RAWNET_DEBUG_ARCH log_message( rawnet_arch_log, "New MAC address set: %02X:%02X:%02X:%02X:%02X:%02X.", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5] ); #endif + + memcpy(interface_fake_mac, mac, 6); + + /* in theory, SIOCSIFHWADDR (linux) or SIOCSIFADDR (bsd) will set the mac address. */ + /* in testing, (linux) I get EBUSY or EOPNOTSUPP */ + + +#if 0 if (interface_fd < 0) return; @@ -209,8 +258,7 @@ void rawnet_arch_set_mac(const uint8_t mac[6]) { if (ioctl(interface_fd, SIOCSIFADDR, mac) < 0) perror("ioctl(SIOCSIFADDR)"); #endif - - +#endif } void rawnet_arch_set_hashfilter(const uint32_t hash_mask[2]) { /* NOP */ @@ -225,11 +273,29 @@ void rawnet_arch_line_ctl(int bEnableTransmitter, int bEnableReceiver) { } int rawnet_arch_read(void *buffer, int nbyte) { - return read(interface_fd, buffer, nbyte); + int ok; + + if (make_buffer(nbyte) < 0) return -1; + + ok = read(interface_fd, interface_buffer, nbyte); + if (ok <= 0) return -1; + + rawnet_fix_incoming_packet(interface_buffer, ok, interface_mac, interface_fake_mac); + memcpy(buffer, interface_buffer, ok); + return ok; } int rawnet_arch_write(const void *buffer, int nbyte) { - return write(interface_fd, buffer, nbyte); + int ok; + + if (make_buffer(nbyte) < 0) return -1; + + + memcpy(interface_buffer, buffer, nbyte); + rawnet_fix_outgoing_packet(interface_buffer, nbyte, interface_mac, interface_fake_mac); + + ok = write(interface_fd, interface_buffer, nbyte); + return ok; } diff --git a/src/rawnet/rawnetarch_darwin.c b/src/rawnet/rawnetarch_vmnet.c similarity index 52% rename from src/rawnet/rawnetarch_darwin.c rename to src/rawnet/rawnetarch_vmnet.c index 5a0c0a1..8a1cbb1 100644 --- a/src/rawnet/rawnetarch_darwin.c +++ b/src/rawnet/rawnetarch_vmnet.c @@ -16,8 +16,6 @@ #include "rawnetsupp.h" -static const uint8_t broadcast_mac[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - static interface_ref interface; static uint8_t interface_mac[6]; static uint8_t interface_fake_mac[6]; @@ -90,6 +88,10 @@ int rawnet_arch_activate(const char *interface_name) { if (interface_status == VMNET_SUCCESS) { interface_buffer = (uint8_t *)malloc(interface_packet_size); + + /* copy mac to fake mac */ + memcpy(interface_fake_mac, interface_mac, 6); + } else { log_message(rawnet_arch_log, "vmnet_start_interface failed"); if (interface) { @@ -151,195 +153,6 @@ void rawnet_arch_line_ctl(int bEnableTransmitter, int bEnableReceiver) { } - - -enum { - eth_dest = 0, // destination address - eth_src = 6, // source address - eth_type = 12, // packet type - eth_data = 14, // packet data -}; - -enum { - ip_ver_ihl = 0, - ip_tos = 1, - ip_len = 2, - ip_id = 4, - ip_frag = 6, - ip_ttl = 8, - ip_proto = 9, - ip_header_cksum = 10, - ip_src = 12, - ip_dest = 16, - ip_data = 20, -}; - -enum { - udp_source = 0, // source port - udp_dest = 2, // destination port - udp_len = 4, // length - udp_cksum = 6, // checksum - udp_data = 8, // total length udp header -}; - -enum { - bootp_op = 0, // operation - bootp_hw = 1, // hardware type - bootp_hlen = 2, // hardware len - bootp_hp = 3, // hops - bootp_transid = 4, // transaction id - bootp_secs = 8, // seconds since start - bootp_flags = 10, // flags - bootp_ipaddr = 12, // ip address knwon by client - bootp_ipclient = 16, // client ip from server - bootp_ipserver = 20, // server ip - bootp_ipgateway = 24, // gateway ip - bootp_client_hrd = 28, // client mac address - bootp_spare = 34, - bootp_host = 44, - bootp_fname = 108, - bootp_data = 236, // total length bootp packet -}; - -enum { - arp_hw = 14, // hw type (eth = 0001) - arp_proto = 16, // protocol (ip = 0800) - arp_hwlen = 18, // hw addr len (eth = 06) - arp_protolen = 19, // proto addr len (ip = 04) - arp_op = 20, // request = 0001, reply = 0002 - arp_shw = 22, // sender hw addr - arp_sp = 28, // sender proto addr - arp_thw = 32, // target hw addr - arp_tp = 38, // target protoaddr - arp_data = 42, // total length of packet -}; - -enum { - dhcp_discover = 1, - dhcp_offer = 2, - dhcp_request = 3, - dhcp_decline = 4, - dhcp_pack = 5, - dhcp_nack = 6, - dhcp_release = 7, - dhcp_inform = 8, -}; - -static uint8_t oo[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; -static uint8_t ff[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - -static int is_arp(const uint8_t *packet, unsigned size) { - return size == arp_data - && packet[12] == 0x08 && packet[13] == 0x06 /* ARP */ - && packet[14] == 0x00 && packet[15] == 0x01 /* ethernet */ - && packet[16] == 0x08 && packet[17] == 0x00 /* ipv4 */ - && packet[18] == 0x06 /* hardware size */ - && packet[19] == 0x04 /* protocol size */ - ; -} - -static int is_broadcast(const uint8_t *packet, unsigned size) { - return !memcmp(packet + 0, ff, 6); -} - -static int is_unicast(const uint8_t *packet, unsigned size) { - return (*packet & 0x01) == 0; -} - -static int is_multicast(const uint8_t *packet, unsigned size) { - return (*packet & 0x01) == 0x01 && !is_broadcast(packet, size); -} - -static int is_dhcp_out(const uint8_t *packet, unsigned size) { - static uint8_t cookie[] = { 0x63, 0x82, 0x53, 0x63 }; - return size >= 282 - //&& !memcmp(&packet[0], ff, 6) /* broadcast */ - && packet[12] == 0x08 && packet[13] == 0x00 - && packet[14] == 0x45 /* version 4 */ - && packet[23] == 0x11 /* UDP */ - //&& !memcmp(&packet[26], oo, 4) /* source ip */ - //&& !memcmp(&packet[30], ff, 4) /* dest ip */ - && packet[34] == 0x00 && packet[35] == 0x44 /* source port */ - && packet[36] == 0x00 && packet[37] == 0x43 /* dest port */ - //&& packet[44] == 0x01 /* dhcp boot req */ - && packet[43] == 0x01 /* ethernet */ - && packet[44] == 0x06 /* 6 byte mac */ - && !memcmp(&packet[278], cookie, 4) - ; -} - - -static int is_dhcp_in(const uint8_t *packet, unsigned size) { - static uint8_t cookie[] = { 0x63, 0x82, 0x53, 0x63 }; - return size >= 282 - //&& !memcmp(&packet[0], ff, 6) /* broadcast */ - && packet[12] == 0x08 && packet[13] == 0x00 - && packet[14] == 0x45 /* version 4 */ - && packet[23] == 0x11 /* UDP */ - //&& !memcmp(&packet[26], oo, 4) /* source ip */ - //&& !memcmp(&packet[30], ff, 4) /* dest ip */ - && packet[34] == 0x00 && packet[35] == 0x43 /* source port */ - && packet[36] == 0x00 && packet[37] == 0x44 /* dest port */ - //&& packet[44] == 0x01 /* dhcp boot req */ - && packet[43] == 0x01 /* ethernet */ - && packet[44] == 0x06 /* 6 byte mac */ - && !memcmp(&packet[278], cookie, 4) - ; -} - -static unsigned ip_checksum(const uint8_t *packet) { - unsigned x = 0; - unsigned i; - for (i = 0; i < ip_data; i += 2) { - if (i == ip_header_cksum) continue; - x += packet[eth_data + i + 0 ] << 8; - x += packet[eth_data + i + 1]; - } - - /* add the carry */ - x += x >> 16; - x &= 0xffff; - return ~x & 0xffff; -} - -static void fix_incoming_packet(uint8_t *packet, unsigned size, const uint8_t real_mac[6], const uint8_t fake_mac[6]) { - - if (memcmp(packet + 0, real_mac, 6) == 0) - memcpy(packet + 0, fake_mac, 6); - - /* dhcp request - fix the hardware address */ - if (is_unicast(packet, size) && is_dhcp_in(packet, size)) { - if (!memcmp(packet + 70, real_mac, 6)) - memcpy(packet + 70, fake_mac, 6); - return; - } - -} - -static void fix_outgoing_packet(uint8_t *packet, unsigned size, const uint8_t real_mac[6], const uint8_t fake_mac[6]) { - - - - if (memcmp(packet + 6, fake_mac, 6) == 0) - memcpy(packet + 6, real_mac, 6); - - if (is_arp(packet, size)) { - /* sender mac address */ - if (!memcmp(packet + 22, fake_mac, 6)) - memcpy(packet + 22, real_mac, 6); - return; - } - - /* dhcp request - fix the hardware address */ - if (is_broadcast(packet, size) && is_dhcp_out(packet, size)) { - - if (!memcmp(packet + 70, fake_mac, 6)) - memcpy(packet + 70, real_mac, 6); - return; - } - -} - int rawnet_arch_read(void *buffer, int nbyte) { int count = 1; @@ -366,10 +179,11 @@ int rawnet_arch_read(void *buffer, int nbyte) { return 0; } - fix_incoming_packet(interface_buffer, v.vm_pkt_size, interface_mac, interface_fake_mac); + rawnet_fix_incoming_packet(interface_buffer, v.vm_pkt_size, interface_mac, interface_fake_mac); // iov.iov_len is not updated with the read count, apparently. - if (!is_multicast(interface_buffer, v.vm_pkt_size)) { /* multicast crap */ + /* don't sebug multicast packets */ + if (interface_buffer[0] == 0xff || (interface_buffer[0] & 0x01) == 0 ) { fprintf(stderr, "\nrawnet_arch_receive: %u\n", (unsigned)v.vm_pkt_size); rawnet_hexdump(interface_buffer, v.vm_pkt_size); } @@ -396,7 +210,7 @@ int rawnet_arch_write(const void *buffer, int nbyte) { /* copy the buffer and fix the source mac address. */ memcpy(interface_buffer, buffer, nbyte); - fix_outgoing_packet(interface_buffer, nbyte, interface_mac, interface_fake_mac); + rawnet_fix_outgoing_packet(interface_buffer, nbyte, interface_mac, interface_fake_mac); iov.iov_base = interface_buffer; iov.iov_len = nbyte; diff --git a/src/rawnet/rawnetarch_vmnet_helper.c b/src/rawnet/rawnetarch_vmnet_helper.c index 2e8a9cc..7b1ec93 100644 --- a/src/rawnet/rawnetarch_vmnet_helper.c +++ b/src/rawnet/rawnetarch_vmnet_helper.c @@ -28,8 +28,6 @@ enum { }; #define MAKE_MSG(msg, extra) (msg | ((extra) << 8)) -static const uint8_t broadcast_mac[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - static uint8_t interface_mac[6]; static uint8_t interface_fake_mac[6]; static uint32_t interface_mtu; @@ -260,6 +258,9 @@ int rawnet_arch_activate(const char *interface_name) { ok = safe_readv(iov, 3); if (ok != 6 + 4 + 4) goto fail; + /* copy mac to fake mac */ + memcpy(interface_fake_mac, interface_mac, 6); + /* sanity check */ /* expect MTU = 1500, packet_size = 1518 */ if (interface_packet_size < 256) { @@ -318,195 +319,6 @@ void rawnet_arch_line_ctl(int bEnableTransmitter, int bEnableReceiver) { - -enum { - eth_dest = 0, // destination address - eth_src = 6, // source address - eth_type = 12, // packet type - eth_data = 14, // packet data -}; - -enum { - ip_ver_ihl = 0, - ip_tos = 1, - ip_len = 2, - ip_id = 4, - ip_frag = 6, - ip_ttl = 8, - ip_proto = 9, - ip_header_cksum = 10, - ip_src = 12, - ip_dest = 16, - ip_data = 20, -}; - -enum { - udp_source = 0, // source port - udp_dest = 2, // destination port - udp_len = 4, // length - udp_cksum = 6, // checksum - udp_data = 8, // total length udp header -}; - -enum { - bootp_op = 0, // operation - bootp_hw = 1, // hardware type - bootp_hlen = 2, // hardware len - bootp_hp = 3, // hops - bootp_transid = 4, // transaction id - bootp_secs = 8, // seconds since start - bootp_flags = 10, // flags - bootp_ipaddr = 12, // ip address knwon by client - bootp_ipclient = 16, // client ip from server - bootp_ipserver = 20, // server ip - bootp_ipgateway = 24, // gateway ip - bootp_client_hrd = 28, // client mac address - bootp_spare = 34, - bootp_host = 44, - bootp_fname = 108, - bootp_data = 236, // total length bootp packet -}; - -enum { - arp_hw = 14, // hw type (eth = 0001) - arp_proto = 16, // protocol (ip = 0800) - arp_hwlen = 18, // hw addr len (eth = 06) - arp_protolen = 19, // proto addr len (ip = 04) - arp_op = 20, // request = 0001, reply = 0002 - arp_shw = 22, // sender hw addr - arp_sp = 28, // sender proto addr - arp_thw = 32, // target hw addr - arp_tp = 38, // target protoaddr - arp_data = 42, // total length of packet -}; - -enum { - dhcp_discover = 1, - dhcp_offer = 2, - dhcp_request = 3, - dhcp_decline = 4, - dhcp_pack = 5, - dhcp_nack = 6, - dhcp_release = 7, - dhcp_inform = 8, -}; - -static uint8_t oo[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; -static uint8_t ff[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - -static int is_arp(const uint8_t *packet, unsigned size) { - return size == arp_data - && packet[12] == 0x08 && packet[13] == 0x06 /* ARP */ - && packet[14] == 0x00 && packet[15] == 0x01 /* ethernet */ - && packet[16] == 0x08 && packet[17] == 0x00 /* ipv4 */ - && packet[18] == 0x06 /* hardware size */ - && packet[19] == 0x04 /* protocol size */ - ; -} - -static int is_broadcast(const uint8_t *packet, unsigned size) { - return !memcmp(packet + 0, ff, 6); -} - -static int is_unicast(const uint8_t *packet, unsigned size) { - return (*packet & 0x01) == 0; -} - -static int is_multicast(const uint8_t *packet, unsigned size) { - return (*packet & 0x01) == 0x01 && !is_broadcast(packet, size); -} - -static int is_dhcp_out(const uint8_t *packet, unsigned size) { - static uint8_t cookie[] = { 0x63, 0x82, 0x53, 0x63 }; - return size >= 282 - //&& !memcmp(&packet[0], ff, 6) /* broadcast */ - && packet[12] == 0x08 && packet[13] == 0x00 - && packet[14] == 0x45 /* version 4 */ - && packet[23] == 0x11 /* UDP */ - //&& !memcmp(&packet[26], oo, 4) /* source ip */ - //&& !memcmp(&packet[30], ff, 4) /* dest ip */ - && packet[34] == 0x00 && packet[35] == 0x44 /* source port */ - && packet[36] == 0x00 && packet[37] == 0x43 /* dest port */ - //&& packet[44] == 0x01 /* dhcp boot req */ - && packet[43] == 0x01 /* ethernet */ - && packet[44] == 0x06 /* 6 byte mac */ - && !memcmp(&packet[278], cookie, 4) - ; -} - - -static int is_dhcp_in(const uint8_t *packet, unsigned size) { - static uint8_t cookie[] = { 0x63, 0x82, 0x53, 0x63 }; - return size >= 282 - //&& !memcmp(&packet[0], ff, 6) /* broadcast */ - && packet[12] == 0x08 && packet[13] == 0x00 - && packet[14] == 0x45 /* version 4 */ - && packet[23] == 0x11 /* UDP */ - //&& !memcmp(&packet[26], oo, 4) /* source ip */ - //&& !memcmp(&packet[30], ff, 4) /* dest ip */ - && packet[34] == 0x00 && packet[35] == 0x43 /* source port */ - && packet[36] == 0x00 && packet[37] == 0x44 /* dest port */ - //&& packet[44] == 0x01 /* dhcp boot req */ - && packet[43] == 0x01 /* ethernet */ - && packet[44] == 0x06 /* 6 byte mac */ - && !memcmp(&packet[278], cookie, 4) - ; -} - -static unsigned ip_checksum(const uint8_t *packet) { - unsigned x = 0; - unsigned i; - for (i = 0; i < ip_data; i += 2) { - if (i == ip_header_cksum) continue; - x += packet[eth_data + i + 0 ] << 8; - x += packet[eth_data + i + 1]; - } - - /* add the carry */ - x += x >> 16; - x &= 0xffff; - return ~x & 0xffff; -} - -static void fix_incoming_packet(uint8_t *packet, unsigned size, const uint8_t real_mac[6], const uint8_t fake_mac[6]) { - - if (memcmp(packet + 0, real_mac, 6) == 0) - memcpy(packet + 0, fake_mac, 6); - - /* dhcp request - fix the hardware address */ - if (is_unicast(packet, size) && is_dhcp_in(packet, size)) { - if (!memcmp(packet + 70, real_mac, 6)) - memcpy(packet + 70, fake_mac, 6); - return; - } - -} - -static void fix_outgoing_packet(uint8_t *packet, unsigned size, const uint8_t real_mac[6], const uint8_t fake_mac[6]) { - - - - if (memcmp(packet + 6, fake_mac, 6) == 0) - memcpy(packet + 6, real_mac, 6); - - if (is_arp(packet, size)) { - /* sender mac address */ - if (!memcmp(packet + 22, fake_mac, 6)) - memcpy(packet + 22, real_mac, 6); - return; - } - - /* dhcp request - fix the hardware address */ - if (is_broadcast(packet, size) && is_dhcp_out(packet, size)) { - - if (!memcmp(packet + 70, fake_mac, 6)) - memcpy(packet + 70, real_mac, 6); - return; - } - -} - - int rawnet_arch_read(void *buffer, int nbyte) { @@ -551,9 +363,10 @@ int rawnet_arch_read(void *buffer, int nbyte) { if (ok != xfer) goto fail; - fix_incoming_packet(interface_buffer, xfer, interface_mac, interface_fake_mac); + rawnet_fix_incoming_packet(interface_buffer, xfer, interface_mac, interface_fake_mac); - if (!is_multicast(interface_buffer, xfer)) { /* multicast crap */ + /* don't sebug multicast packets */ + if (interface_buffer[0] == 0xff || (interface_buffer[0] & 0x01) == 0 ) { fprintf(stderr, "\nrawnet_arch_receive: %u\n", (unsigned)xfer); rawnet_hexdump(interface_buffer, xfer); } @@ -592,7 +405,7 @@ int rawnet_arch_write(const void *buffer, int nbyte) { /* copy the buffer and fix the source mac address. */ memcpy(interface_buffer, buffer, nbyte); - fix_outgoing_packet(interface_buffer, nbyte, interface_mac, interface_fake_mac); + rawnet_fix_outgoing_packet(interface_buffer, nbyte, interface_mac, interface_fake_mac); fprintf(stderr, "\nrawnet_arch_transmit: %u\n", (unsigned)nbyte); diff --git a/src/rawnet/rawnetsupp.c b/src/rawnet/rawnetsupp.c index 49d18b2..5fb6db8 100644 --- a/src/rawnet/rawnetsupp.c +++ b/src/rawnet/rawnetsupp.c @@ -179,3 +179,194 @@ void rawnet_hexdump(const void *what, int count) { } } } + + + + + +enum { + eth_dest = 0, // destination address + eth_src = 6, // source address + eth_type = 12, // packet type + eth_data = 14, // packet data +}; + +enum { + ip_ver_ihl = 0, + ip_tos = 1, + ip_len = 2, + ip_id = 4, + ip_frag = 6, + ip_ttl = 8, + ip_proto = 9, + ip_header_cksum = 10, + ip_src = 12, + ip_dest = 16, + ip_data = 20, +}; + +enum { + udp_source = 0, // source port + udp_dest = 2, // destination port + udp_len = 4, // length + udp_cksum = 6, // checksum + udp_data = 8, // total length udp header +}; + +enum { + bootp_op = 0, // operation + bootp_hw = 1, // hardware type + bootp_hlen = 2, // hardware len + bootp_hp = 3, // hops + bootp_transid = 4, // transaction id + bootp_secs = 8, // seconds since start + bootp_flags = 10, // flags + bootp_ipaddr = 12, // ip address knwon by client + bootp_ipclient = 16, // client ip from server + bootp_ipserver = 20, // server ip + bootp_ipgateway = 24, // gateway ip + bootp_client_hrd = 28, // client mac address + bootp_spare = 34, + bootp_host = 44, + bootp_fname = 108, + bootp_data = 236, // total length bootp packet +}; + +enum { + arp_hw = 14, // hw type (eth = 0001) + arp_proto = 16, // protocol (ip = 0800) + arp_hwlen = 18, // hw addr len (eth = 06) + arp_protolen = 19, // proto addr len (ip = 04) + arp_op = 20, // request = 0001, reply = 0002 + arp_shw = 22, // sender hw addr + arp_sp = 28, // sender proto addr + arp_thw = 32, // target hw addr + arp_tp = 38, // target protoaddr + arp_data = 42, // total length of packet +}; + +enum { + dhcp_discover = 1, + dhcp_offer = 2, + dhcp_request = 3, + dhcp_decline = 4, + dhcp_pack = 5, + dhcp_nack = 6, + dhcp_release = 7, + dhcp_inform = 8, +}; + +static uint8_t oo[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static uint8_t ff[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + +static int is_arp(const uint8_t *packet, unsigned size) { + return size == arp_data + && packet[12] == 0x08 && packet[13] == 0x06 /* ARP */ + && packet[14] == 0x00 && packet[15] == 0x01 /* ethernet */ + && packet[16] == 0x08 && packet[17] == 0x00 /* ipv4 */ + && packet[18] == 0x06 /* hardware size */ + && packet[19] == 0x04 /* protocol size */ + ; +} + +static int is_broadcast(const uint8_t *packet, unsigned size) { + return !memcmp(packet + 0, ff, 6); +} + +static int is_unicast(const uint8_t *packet, unsigned size) { + return (*packet & 0x01) == 0; +} + +static int is_multicast(const uint8_t *packet, unsigned size) { + return (*packet & 0x01) == 0x01 && !is_broadcast(packet, size); +} + +static int is_dhcp_out(const uint8_t *packet, unsigned size) { + static uint8_t cookie[] = { 0x63, 0x82, 0x53, 0x63 }; + return size >= 282 + //&& !memcmp(&packet[0], ff, 6) /* broadcast */ + && packet[12] == 0x08 && packet[13] == 0x00 + && packet[14] == 0x45 /* version 4 */ + && packet[23] == 0x11 /* UDP */ + //&& !memcmp(&packet[26], oo, 4) /* source ip */ + //&& !memcmp(&packet[30], ff, 4) /* dest ip */ + && packet[34] == 0x00 && packet[35] == 0x44 /* source port */ + && packet[36] == 0x00 && packet[37] == 0x43 /* dest port */ + //&& packet[44] == 0x01 /* dhcp boot req */ + && packet[43] == 0x01 /* ethernet */ + && packet[44] == 0x06 /* 6 byte mac */ + && !memcmp(&packet[278], cookie, 4) + ; +} + + +static int is_dhcp_in(const uint8_t *packet, unsigned size) { + static uint8_t cookie[] = { 0x63, 0x82, 0x53, 0x63 }; + return size >= 282 + //&& !memcmp(&packet[0], ff, 6) /* broadcast */ + && packet[12] == 0x08 && packet[13] == 0x00 + && packet[14] == 0x45 /* version 4 */ + && packet[23] == 0x11 /* UDP */ + //&& !memcmp(&packet[26], oo, 4) /* source ip */ + //&& !memcmp(&packet[30], ff, 4) /* dest ip */ + && packet[34] == 0x00 && packet[35] == 0x43 /* source port */ + && packet[36] == 0x00 && packet[37] == 0x44 /* dest port */ + //&& packet[44] == 0x01 /* dhcp boot req */ + && packet[43] == 0x01 /* ethernet */ + && packet[44] == 0x06 /* 6 byte mac */ + && !memcmp(&packet[278], cookie, 4) + ; +} + +static unsigned ip_checksum(const uint8_t *packet) { + unsigned x = 0; + unsigned i; + for (i = 0; i < ip_data; i += 2) { + if (i == ip_header_cksum) continue; + x += packet[eth_data + i + 0 ] << 8; + x += packet[eth_data + i + 1]; + } + + /* add the carry */ + x += x >> 16; + x &= 0xffff; + return ~x & 0xffff; +} + +void rawnet_fix_incoming_packet(uint8_t *packet, unsigned size, const uint8_t real_mac[6], const uint8_t fake_mac[6]) { + + if (memcmp(packet + 0, real_mac, 6) == 0) + memcpy(packet + 0, fake_mac, 6); + + /* dhcp request - fix the hardware address */ + if (is_unicast(packet, size) && is_dhcp_in(packet, size)) { + if (!memcmp(packet + 70, real_mac, 6)) + memcpy(packet + 70, fake_mac, 6); + return; + } + +} + +void rawnet_fix_outgoing_packet(uint8_t *packet, unsigned size, const uint8_t real_mac[6], const uint8_t fake_mac[6]) { + + + + if (memcmp(packet + 6, fake_mac, 6) == 0) + memcpy(packet + 6, real_mac, 6); + + if (is_arp(packet, size)) { + /* sender mac address */ + if (!memcmp(packet + 22, fake_mac, 6)) + memcpy(packet + 22, real_mac, 6); + return; + } + + /* dhcp request - fix the hardware address */ + if (is_broadcast(packet, size) && is_dhcp_out(packet, size)) { + + if (!memcmp(packet + 70, fake_mac, 6)) + memcpy(packet + 70, real_mac, 6); + return; + } + +} diff --git a/src/rawnet/rawnetsupp.h b/src/rawnet/rawnetsupp.h index a3d4f34..412e14f 100644 --- a/src/rawnet/rawnetsupp.h +++ b/src/rawnet/rawnetsupp.h @@ -53,6 +53,9 @@ extern int util_string_set(char **str, const char *new_value); extern unsigned long crc32_buf(const char *buffer, unsigned int len); extern void rawnet_hexdump(const void *what, int count); +extern void rawnet_fix_incoming_packet(uint8_t *packet, unsigned size, const uint8_t real_mac[6], const uint8_t fake_mac[6]); +extern void rawnet_fix_outgoing_packet(uint8_t *packet, unsigned size, const uint8_t real_mac[6], const uint8_t fake_mac[6]); + #define log_message(level,...) do { fprintf(stderr,__VA_ARGS__); fputs("\n", stderr); } while (0) #endif From 43b7330256a293e46f57b0026de2774db62f7157 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Thu, 10 Jan 2019 20:36:17 -0500 Subject: [PATCH 037/186] clean up CMake dependencies a little bit. --- src/CMakeLists.txt | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2702d7e..0355e94 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -139,9 +139,8 @@ if(APPLE) COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_BINARY_DIR}/rawnet/vmnet_helper" "${CMAKE_CURRENT_BINARY_DIR}/GSplus.app/Contents/MacOS/vmnet_helper" - # COMMAND sudo chown root "${CMAKE_CURRENT_BINARY_DIR}/GSplus.app/Contents/MacOS/vmnet_helper" - # COMMAND sudo chmod +s "${CMAKE_CURRENT_BINARY_DIR}/GSplus.app/Contents/MacOS/vmnet_helper" - DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/rawnet/vmnet_helper) + ) + add_dependencies(GSplus vmnet_helper) endif() # SET_SOURCE_FILES_PROPERTIES(vmnet_helper PROPERTIES MACOSX_PACKAGE_LOCATION MacOS) @@ -185,14 +184,6 @@ endif() # target_link_libraries(GSplus -F${CMAKE_CURRENT_SOURCE_DIR} "-framework SDL2" -Wl,-rpath,@executable_path/../Frameworks) #endif() -# if (APPLE AND CMAKE_BUILD_TYPE MATCHES "Release") -# add_custom_command(TARGET GSplus -# POST_BUILD -# COMMAND dylibbundler -od -b -x GSplus.app/Contents/MacOS/GSplus -d GSplus.app/Contents/libs -# #COMMAND cp to_pro partls GSplus.app/Contents/Resources/ -# COMMENT bundling libraries... -# ) -# endif() if (APPLE) add_custom_target(bundle @@ -200,4 +191,9 @@ if (APPLE) COMMAND dylibbundler -od -b -x GSplus.app/Contents/MacOS/GSplus -d GSplus.app/Contents/libs COMMENT bundling libraries... ) + add_custom_target(setuid + COMMAND sudo chown root GSplus.app/Contents/MacOS/vmnet_helper + COMMAND sudo chmod +s GSplus.app/Contents/MacOS/vmnet_helper + USES_TERMINAL) + add_dependencies(setuid vmnet_helper) endif() From ff1022db8f57a392a26539d6134fc0bf1d1a9124 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Sat, 12 Jan 2019 21:10:51 -0500 Subject: [PATCH 038/186] sdl driver keymap mistakes. --- src/sdl2_driver.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sdl2_driver.c b/src/sdl2_driver.c index 3e45367..4f30041 100644 --- a/src/sdl2_driver.c +++ b/src/sdl2_driver.c @@ -144,8 +144,8 @@ int a2_key_to_sdlkeycode[][3] = { { 0x22, SDLK_i, 'I' }, { 0x1f, SDLK_o, 'O' }, { 0x23, SDLK_p, 'P' }, - { 0x21, SDLK_RIGHTBRACKET, '{' }, - { 0x1e, SDLK_LEFTBRACKET, '}' }, + { 0x21, SDLK_LEFTBRACKET, '{' }, + { 0x1e, SDLK_RIGHTBRACKET, '}' }, { 0x2a, SDLK_BACKSLASH, '|' }, /* backslash, bar */ { 0x75, SDLK_DELETE, 0 }, { 0x77, SDLK_END, 0 }, From ad3ec85a1d5f196b8ea3dbce0858c99a0480e6d6 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Wed, 16 Jan 2019 18:22:36 -0500 Subject: [PATCH 039/186] shift-keypad . was generating a keypad comma. very strange. --- src/adb.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/adb.h b/src/adb.h index 2c2e7c7..d518157 100644 --- a/src/adb.h +++ b/src/adb.h @@ -82,7 +82,7 @@ const int a2_key_to_ascii[][4] = { { 0x3f, -1, -1, -1 }, { 0x40, -1, -1, -1 }, - { 0x41, 0x102e, 0x102c, -1 }, /* keypad . */ + { 0x41, 0x102e, 0x102e, -1 }, /* keypad . */ { 0x42, -1, -1, -1 }, { 0x43, 0x102a, 0x102a, -1 }, /* keypad * */ { 0x44, -1, -1, -1 }, From 9f5a6566f5533367ce7ffd189885be15ccf8e8f6 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Wed, 16 Jan 2019 18:23:17 -0500 Subject: [PATCH 040/186] sdl driver - support for the Home key. --- src/sdl2_driver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sdl2_driver.c b/src/sdl2_driver.c index 4f30041..63a2614 100644 --- a/src/sdl2_driver.c +++ b/src/sdl2_driver.c @@ -127,7 +127,7 @@ int a2_key_to_sdlkeycode[][3] = { { 0x18, SDLK_EQUALS, SDLK_PLUS }, { 0x33, SDLK_BACKSPACE, 0 }, { 0x72, SDLK_INSERT, 0 }, /* Help? XK_Help */ - /* { 0x73, XK_Home, 0 }, alias XK_Home to be XK_KP_Equal! */ + { 0x73, SDLK_HOME, 0 }, { 0x74, SDLK_PAGEUP, 0 }, { 0x47, SDLK_NUMLOCKCLEAR, 0 }, /* Clear, XK_Clear */ { 0x51, SDLK_KP_EQUALS, 0 }, /* Note XK_Home alias! XK_Home */ From 77eb25fc716c6ab3f188ef4c83eac44c7f0c99da Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Tue, 22 Jan 2019 19:28:59 -0500 Subject: [PATCH 041/186] use defines instead of bit shifts --- src/engine_c.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/engine_c.c b/src/engine_c.c index 9372be2..88fc614 100644 --- a/src/engine_c.c +++ b/src/engine_c.c @@ -156,7 +156,7 @@ extern word32 slow_mem_changed[]; stat = GET_PAGE_INFO_RD(((addr) >> 8) & 0xffff); \ wstat = PTR2WORD(stat) & 0xff; \ ptr = stat - wstat + ((addr) & 0xff); \ - if(wstat & (1 << (31 - BANK_IO_BIT)) || iostrobe == 1) { \ + if(wstat & BANK_IO_TMP || iostrobe == 1) { \ fcycles_tmp1 = fcycles; \ dest = get_memory8_io_stub((addr), stat, \ &fcycles_tmp1, fplus_x_m1); \ @@ -172,7 +172,7 @@ extern word32 slow_mem_changed[]; stat = GET_PAGE_INFO_RD(((addr) >> 8) & 0xffff); \ wstat = PTR2WORD(stat) & 0xff; \ ptr = stat - wstat + ((addr) & 0xff); \ - if((wstat & (1 << (31 - BANK_IO_BIT))) || (((addr) & 0xff) == 0xff)) { \ + if((wstat & BANK_IO_TMP) || (((addr) & 0xff) == 0xff)) { \ fcycles_tmp1 = fcycles; \ dest = get_memory16_pieces_stub((addr), stat, \ &fcycles_tmp1, fplus_ptr, in_bank); \ @@ -188,7 +188,7 @@ extern word32 slow_mem_changed[]; stat = GET_PAGE_INFO_RD(((addr) >> 8) & 0xffff); \ wstat = PTR2WORD(stat) & 0xff; \ ptr = stat - wstat + ((addr) & 0xff); \ - if((wstat & (1 << (31 - BANK_IO_BIT))) || (((addr) & 0xfe) == 0xfe)) { \ + if((wstat & BANK_IO_TMP) || (((addr) & 0xfe) == 0xfe)) { \ fcycles_tmp1 = fcycles; \ dest = get_memory24_pieces_stub((addr), stat, \ &fcycles_tmp1, fplus_ptr, in_bank); \ @@ -454,11 +454,11 @@ void set_memory8_io_stub(word32 addr, word32 val, byte *stat, double *fcycs_ptr, } ptr = stat - wstat + ((addr) & 0xff); \ fcycles = *fcycs_ptr; - if(wstat & (1 << (31 - BANK_IO2_BIT))) { + if(wstat & BANK_IO2_TMP) { FCYCLES_ROUND; *fcycs_ptr = fcycles; set_memory_io((addr), val, fcycs_ptr); - } else if(wstat & (1 << (31 - BANK_SHADOW_BIT))) { + } else if(wstat & BANK_SHADOW) { FCYCS_PTR_FCYCLES_ROUND_SLOW; tmp1 = (addr & 0xffff); setmem_tmp1 = g_slow_memory_ptr[tmp1]; @@ -468,7 +468,7 @@ void set_memory8_io_stub(word32 addr, word32 val, byte *stat, double *fcycs_ptr, slow_mem_changed[tmp1 >> CHANGE_SHIFT] |= (1 << (31-((tmp1 >> SHIFT_PER_CHANGE) & 0x1f))); } - } else if(wstat & (1 << (31 - BANK_SHADOW2_BIT))) { + } else if(wstat & BANK_SHADOW2) { FCYCS_PTR_FCYCLES_ROUND_SLOW; tmp2 = (addr & 0xffff); tmp1 = 0x10000 + tmp2; @@ -886,7 +886,7 @@ word32 get_remaining_operands(word32 addr, word32 opcode, word32 psr, Fplus *fpl FINISH(RET_C70D, 0); \ } \ } \ - if(wstat & (1 << (31 - BANK_IO2_BIT)) || iostrobe == 1) { \ + if(wstat & BANK_IO2_TMP || iostrobe == 1) { \ FCYCLES_ROUND; \ fcycles_tmp1 = fcycles; \ opcode = get_memory_io((addr), &fcycles_tmp1); \ From ee078aeb191ff5854296634d0568af923c13b79c Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Tue, 22 Jan 2019 21:52:00 -0500 Subject: [PATCH 042/186] remove breakpoint checks (to be added back later) --- src/engine_c.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/engine_c.c b/src/engine_c.c index 88fc614..409ac77 100644 --- a/src/engine_c.c +++ b/src/engine_c.c @@ -372,9 +372,6 @@ word32 get_memory8_io_stub(word32 addr, byte *stat, double *fcycs_ptr, byte *ptr; wstat = PTR2WORD(stat) & 0xff; - if(wstat & BANK_BREAK) { - check_breakpoints(addr); - } fcycles = *fcycs_ptr; if(wstat & BANK_IO2_TMP || iostrobe == 1) { FCYCLES_ROUND; @@ -449,10 +446,8 @@ void set_memory8_io_stub(word32 addr, word32 val, byte *stat, double *fcycs_ptr, word32 wstat; wstat = PTR2WORD(stat) & 0xff; - if(wstat & (1 << (31 - BANK_BREAK_BIT))) { - check_breakpoints(addr); - } - ptr = stat - wstat + ((addr) & 0xff); \ + + ptr = stat - wstat + ((addr) & 0xff); fcycles = *fcycs_ptr; if(wstat & BANK_IO2_TMP) { FCYCLES_ROUND; From 38e691562c1635d0858d2d61f2af94972a05a553 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Tue, 22 Jan 2019 22:37:20 -0500 Subject: [PATCH 043/186] use c99 stdint types. --- src/defc.h | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/defc.h b/src/defc.h index bb0416f..cf619ca 100644 --- a/src/defc.h +++ b/src/defc.h @@ -7,6 +7,8 @@ #include "defcomm.h" +#include + // OG redirect printf to console #ifdef ACTIVEGS #include @@ -18,14 +20,11 @@ extern "C" int fOutputInfo(FILE*,const char* format,...); #define STRUCT(a) typedef struct _ ## a a; struct _ ## a -typedef unsigned char byte; -typedef unsigned short word16; -typedef unsigned int word32; -#if _MSC_VER -typedef unsigned __int64 word64; -#else -typedef unsigned long long word64; -#endif +typedef uint8_t byte; +typedef uint16_t word16; +typedef uint32_t word32; +typedef uint64_t word64; + void U_STACK_TRACE(); From 7e87fc696819cc49836c1b7466f02015819d1afa Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Sat, 26 Jan 2019 16:48:06 -0500 Subject: [PATCH 044/186] replace inline asm for rdtsc with compiler intrinsics. --- src/engine_c.c | 44 +++++++++++++++++++------------------------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/src/engine_c.c b/src/engine_c.c index 409ac77..c9c9c8b 100644 --- a/src/engine_c.c +++ b/src/engine_c.c @@ -755,38 +755,32 @@ void fixed_memory_ptrs_shut() { g_rom_cards_ptr = NULL; } +#if defined(_MSC_VER) + #include +#elif defined(__GNUC__) + + #if defined(__i386__) || defined(__x86_64__) + #include + #elif defined(__powerpc__) || defined(__ppc__) + #define __rdtsc() __builtin_ppc_mftb() + #else + #warning __rdtsc unavailable. + #define __rdtsc() 0 + #endif +#else + #warning __rdtsc unavailable. + #define __rdtsc() 0 +#endif word32 get_itimer() { #if defined(_WIN32) LARGE_INTEGER count; if (QueryPerformanceCounter(&count)) return count.LowPart; - else - return 0; -#elif defined(__i386) && defined(__GNUC__) - /* Here's my bad ia32 asm code to do rdtsc */ - /* Linux source uses: */ - /* asm volatile("rdtsc" : "=a"(ret) : : "edx"); */ - /* asm volatile("rdtsc" : "=%eax"(ret) : : "%edx"); */ - - /* GCC bug report 2001-03/msg00786.html used: */ - /*register word64 dtmp; */ - /*asm volatile ("rdtsc" : "=A" (dtmp)); */ - /*return (word32)dtmp; */ - - register word32 ret; - - asm volatile ("rdtsc;movl %%eax,%0" : "=r" (ret) : : "%eax","%edx"); - - return ret; -#elif defined(__POWERPC__) && defined(__GNUC__) - register word32 ret; - - asm volatile ("mftb %0" : "=r" (ret)); - return ret; -#else - return 0; #endif + + return __rdtsc(); + } void set_halt_act(int val) { From f376e2a442fe344db01b7655a2fdff2f977d2feb Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Sat, 26 Jan 2019 17:06:48 -0500 Subject: [PATCH 045/186] new memory checking [part 1] _ macros bypass the memory checking code. --- src/engine_c.c | 164 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 135 insertions(+), 29 deletions(-) diff --git a/src/engine_c.c b/src/engine_c.c index c9c9c8b..29b0ee5 100644 --- a/src/engine_c.c +++ b/src/engine_c.c @@ -218,43 +218,75 @@ extern word32 slow_mem_changed[]; GET_MEMORY16(save_addr, dest, 1); \ } +/* + _ macros do not do any MMU checking +*/ -#define PUSH8(arg) \ - SET_MEMORY8(stack, arg); \ +#if 0 +#define MMU_CHECK(addr, val, bytes, in_page, in_bank) \ + if (abort_support && check_abort_breakpoints(addr, bytes, in_page, in_bank)) { \ + g_abort_value = val; \ + g_abort_address = addr; \ + g_ret1 = RET_ABORT; \ + g_ret2 = saved_pc; \ + kpc = saved_pc; + goto abort; \ + } +#else +#define MMU_CHECK(addr, val, bytes, in_page, in_bank) +#endif +#define PUSH8(arg) \ + MMU_CHECK(stack, arg, 1, 0, 0) \ + _PUSH8(arg) \ + +#define _PUSH8(arg) \ + _SET_MEMORY8(stack, arg); \ stack--; \ if(psr & 0x100) { \ - stack = 0x100 | (stack & 0xff); \ + stack = 0x100 | (stack & 0xff); \ } \ stack = stack & 0xffff; -#define PUSH16(arg) \ +#define PUSH16(arg) \ + MMU_CHECK(stack, arg, 2, 1, 1) \ + _PUSH16(arg) + +#define _PUSH16(arg) \ if((stack & 0xfe) == 0) { \ /* stack will cross page! */ \ - PUSH8((arg) >> 8); \ - PUSH8(arg); \ + _PUSH8((arg) >> 8); \ + _PUSH8(arg); \ } else { \ stack -= 2; \ stack = stack & 0xffff; \ - SET_MEMORY16(stack + 1, arg, 1); \ + _SET_MEMORY16(stack + 1, arg, 1); \ } -#define PUSH16_UNSAFE(arg) \ +#define PUSH16_UNSAFE(arg) \ + MMU_CHECK((stack - 1) & 0xffff, arg, 2, 0, 1) \ + _PUSH16_UNSAFE(arg) + +#define _PUSH16_UNSAFE(arg) \ save_addr = (stack - 1) & 0xffff; \ stack -= 2; \ if(psr & 0x100) { \ - stack = 0x100 | (stack & 0xff); \ + stack = 0x100 | (stack & 0xff); \ } \ stack = stack & 0xffff; \ - SET_MEMORY16(save_addr, arg, 1); + _SET_MEMORY16(save_addr, arg, 1); -#define PUSH24_UNSAFE(arg) \ +#define PUSH24_UNSAFE(arg) \ + MMU_CHECK((stack - 2) & 0xffff, arg, 2, 0, 1) \ + _PUSH24_UNSAFE(arg) + +#define _PUSH24_UNSAFE(arg) \ save_addr = (stack - 2) & 0xffff; \ stack -= 3; \ if(psr & 0x100) { \ - stack = 0x100 | (stack & 0xff); \ + stack = 0x100 | (stack & 0xff); \ } \ stack = stack & 0xffff; \ - SET_MEMORY24(save_addr, arg, 1); + _SET_MEMORY24(save_addr, arg, 1); #define PULL8(dest) \ stack++; \ @@ -303,7 +335,11 @@ extern word32 slow_mem_changed[]; } \ } -#define SET_MEMORY8(addr, val) \ +#define SET_MEMORY8(addr, val) \ + MMU_CHECK(addr, val, 1, 0, 0) \ + _SET_MEMORY8(addr, val) + +#define _SET_MEMORY8(addr, val) \ LOG_DATA_MACRO(addr, val, 8); \ CYCLES_PLUS_1; \ stat = GET_PAGE_INFO_WR(((addr) >> 8) & 0xffff); \ @@ -318,8 +354,12 @@ extern word32 slow_mem_changed[]; *ptr = val; \ } +#define SET_MEMORY16(addr, val, in_bank) \ + MMU_CHECK(addr, val, 2, 0, in_bank) \ + _SET_MEMORY16(addr, val, in_bank) -#define SET_MEMORY16(addr, val, in_bank) \ + +#define _SET_MEMORY16(addr, val, in_bank) \ LOG_DATA_MACRO(addr, val, 16); \ stat = GET_PAGE_INFO_WR(((addr) >> 8) & 0xffff); \ wstat = PTR2WORD(stat) & 0xff; \ @@ -335,7 +375,12 @@ extern word32 slow_mem_changed[]; ptr[1] = (val) >> 8; \ } -#define SET_MEMORY24(addr, val, in_bank) \ +#define SET_MEMORY24(addr, val, in_bank) \ + MMU_CHECK(addr, val, 3, 0, in_bank) \ + _SET_MEMORY24(addr, val, in_bank) + + +#define _SET_MEMORY24(addr, val, in_bank) \ LOG_DATA_MACRO(addr, val, 24); \ stat = GET_PAGE_INFO_WR(((addr) >> 8) & 0xffff); \ wstat = PTR2WORD(stat) & 0xff; \ @@ -352,6 +397,8 @@ extern word32 slow_mem_changed[]; ptr[2] = (val) >> 16; \ } + + void check_breakpoints(word32 addr) { int count; int i; @@ -365,6 +412,51 @@ void check_breakpoints(word32 addr) { } } +word32 g_num_kpc_breakpoints = 0; +word32 g_kpc_breakpoints[20]; +int check_kpc_breakpoints(word32 addr) { + int i; + for (i = 0; i < g_num_kpc_breakpoints; ++i) { + if (g_kpc_breakpoints[i] == addr) { + return 1; + } + } + return 0; +} + + +/* 65816 abort just pushes the orignal pc (-1?) on the stack. + MMU hardware could, of course, store the address [and value?] + +*/ +word32 g_abort_address; +word32 g_abort_value; + +int check_abort_breakpoints(word32 addr, unsigned bytes, int in_page, int in_bank) { + byte *stat; + word32 wstat; + word32 mask = 0xffffff; + int i; + + if (in_bank) mask = 0xffff; + if (in_page) mask = 0xff; + + while (bytes--) { + stat = GET_PAGE_INFO_WR(((addr) >> 8) & 0xffff); + wstat = PTR2WORD(stat) & 0xff; + if ((wstat & BANK_BREAK)) { + for (i = 0; i < g_num_breakpoints; ++i) { + if (addr == g_breakpts[i]) return 1; + } + } + + addr = ((addr + 1) & mask) | (addr & ~mask); + } + return 0; +} + + + word32 get_memory8_io_stub(word32 addr, byte *stat, double *fcycs_ptr, double fplus_x_m1) { double fcycles; @@ -488,12 +580,12 @@ void set_memory16_pieces_stub(word32 addr, word32 val, double *fcycs_ptr, word32 addrp1; word32 wstat; fcycles = *fcycs_ptr; - SET_MEMORY8(addr, val); + _SET_MEMORY8(addr, val); addrp1 = addr + 1; if(in_bank) { addrp1 = (addr & 0xff0000) + (addrp1 & 0xffff); } - SET_MEMORY8(addrp1, val >> 8); + _SET_MEMORY8(addrp1, val >> 8); *fcycs_ptr = fcycles; } @@ -511,17 +603,17 @@ void set_memory24_pieces_stub(word32 addr, word32 val, double *fcycs_ptr, fcycles = *fcycs_ptr; fplus_1 = fplus_ptr->plus_1; fplus_x_m1 = fplus_ptr->plus_x_minus_1; - SET_MEMORY8(addr, val); + _SET_MEMORY8(addr, val); addrp1 = addr + 1; if(in_bank) { addrp1 = (addr & 0xff0000) + (addrp1 & 0xffff); } - SET_MEMORY8(addrp1, val >> 8); + _SET_MEMORY8(addrp1, val >> 8); addrp2 = addr + 2; if(in_bank) { addrp2 = (addr & 0xff0000) + (addrp2 & 0xffff); } - SET_MEMORY8(addrp2, val >> 16); + _SET_MEMORY8(addrp2, val >> 16); *fcycs_ptr = fcycles; } @@ -586,7 +678,7 @@ void set_memory_c(word32 addr, word32 val, int cycs) { fcycles = g_cur_dcycs - g_last_vbl_dcycs; fplus_1 = 0; fplus_x_m1 = 0; - SET_MEMORY8(addr, val); + _SET_MEMORY8(addr, val); } void set_memory16_c(word32 addr, word32 val, int cycs) { @@ -601,7 +693,7 @@ void set_memory16_c(word32 addr, word32 val, int cycs) { fplus_1 = 0; fplus_2 = 0; fplus_x_m1 = 0; - SET_MEMORY16(addr, val, 0); + _SET_MEMORY16(addr, val, 0); } void set_memory24_c(word32 addr, word32 val, int cycs) { @@ -854,18 +946,23 @@ word32 get_remaining_operands(word32 addr, word32 opcode, word32 psr, Fplus *fpl return arg; } -#define FETCH_OPCODE \ - addr = kpc; \ +#define FLAG_IGNORE_BREAKPOINTS 0x0001 + +#define FETCH_OPCODE \ + addr = saved_pc = kpc; \ CYCLES_PLUS_2; \ stat = GET_PAGE_INFO_RD(((addr) >> 8) & 0xffff); \ wstat = PTR2WORD(stat) & 0xff; \ ptr = stat - wstat + ((addr) & 0xff); \ arg_ptr = ptr; \ opcode = *ptr; \ - if((wstat & (1 << (31-BANK_IO_BIT))) || ((addr & 0xff) > 0xfc)) { \ - if(wstat & BANK_BREAK) { \ - check_breakpoints(addr); \ - } \ + if (wstat & BANK_BREAK) { \ + wstat &= ~BANK_BREAK; \ + if (kpc_support && check_kpc_breakpoints(addr)) { \ + FINISH(RET_KPC, addr); \ + } \ + } \ + if((wstat & BANK_IO_TMP) || ((addr & 0xff) > 0xfc)) { \ if((addr & 0xfffff0) == 0x00c700) { \ if(addr == 0xc700) { \ FINISH(RET_C700, 0); \ @@ -924,6 +1021,15 @@ int enter_engine(Engine_reg *engine_ptr) { word32 addr_latch; word32 tmp1, tmp2; + word32 saved_pc = 0; + + word32 abort_support = g_num_breakpoints ? 1 : 0; + word32 kpc_support = g_num_kpc_breakpoints ? 1 : 0; + + + if (engine_ptr->flags & 0x01) abort_support = 0; + if (engine_ptr->flags & 0x01) kpc_support = 0; + tmp_pc_ptr = 0; From 66f3971027e55bfc76fbe823a9f5d2e51e8da1fb Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Sat, 26 Jan 2019 17:07:40 -0500 Subject: [PATCH 046/186] add debug flag register to engine. --- src/defc.h | 3 ++- src/defcomm.h | 26 +++++++++++++++----------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/defc.h b/src/defc.h index cf619ca..d817191 100644 --- a/src/defc.h +++ b/src/defc.h @@ -152,6 +152,7 @@ STRUCT(Fplus) { }; STRUCT(Engine_reg) { + Fplus *fplus_ptr; double fcycles; word32 kpc; word32 acc; @@ -164,7 +165,7 @@ STRUCT(Engine_reg) { word32 direct; word32 psr; - Fplus *fplus_ptr; + word32 flags; }; STRUCT(Kimage) { diff --git a/src/defcomm.h b/src/defcomm.h index 25b44cb..643ae08 100644 --- a/src/defcomm.h +++ b/src/defcomm.h @@ -49,17 +49,21 @@ #define BANK_BAD_MEM (&g_dummy_memory1_ptr[0xff]) - -#define ENGINE_FCYCLES 0x00 -#define ENGINE_REG_KPC 0x08 -#define ENGINE_REG_ACC 0x0c -#define ENGINE_REG_XREG 0x10 -#define ENGINE_REG_YREG 0x14 -#define ENGINE_REG_STACK 0x18 -#define ENGINE_REG_DBANK 0x1c -#define ENGINE_REG_DIRECT 0x20 -#define ENGINE_REG_PSR 0x24 -#define ENGINE_FPLUS_PTR 0x28 +/* + * this is only relevant for the PA RISC asm. + * + */ +#define ENGINE_FPLUS_PTR 0x00 +#define ENGINE_FCYCLES 0x08 +#define ENGINE_REG_KPC 0x10 +#define ENGINE_REG_ACC 0x14 +#define ENGINE_REG_XREG 0x18 +#define ENGINE_REG_YREG 0x1c +#define ENGINE_REG_STACK 0x20 +#define ENGINE_REG_DBANK 0x24 +#define ENGINE_REG_DIRECT 0x28 +#define ENGINE_REG_PSR 0x2c +#define ENGINE_FLAGS 0x30 #define LOG_PC_DCYCS 0x00 #define LOG_PC_DBANK_KPC 0x08 From 0c0c886be9ae040330d6aa02c42101881f073611 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Sat, 26 Jan 2019 17:14:39 -0500 Subject: [PATCH 047/186] drop the g_dbg_step variable for now... --- src/dis.c | 1 - src/engine_c.c | 14 -------------- src/sim65816.c | 10 +++------- 3 files changed, 3 insertions(+), 22 deletions(-) diff --git a/src/dis.c b/src/dis.c index a84feb2..9dd95df 100644 --- a/src/dis.c +++ b/src/dis.c @@ -17,7 +17,6 @@ extern byte *g_memory_ptr; extern byte *g_slow_memory_ptr; extern int halt_sim; extern int enter_debug; -extern int g_dbg_step; //debug.c extern int timeout; //debug.c extern int g_c068_statereg; extern word32 stop_run_at; diff --git a/src/engine_c.c b/src/engine_c.c index 29b0ee5..c69177d 100644 --- a/src/engine_c.c +++ b/src/engine_c.c @@ -24,7 +24,6 @@ # define FCYCS_PTR_FCYCLES_ROUND_SLOW #endif -extern int g_dbg_step; extern int halt_sim; extern int g_code_red; extern int g_ignore_halts; @@ -399,19 +398,6 @@ extern word32 slow_mem_changed[]; -void check_breakpoints(word32 addr) { - int count; - int i; - - count = g_num_breakpoints; - for(i = 0; i < count; i++) { - if((g_breakpts[i] & 0xffffff) == addr) { - g_dbg_step = -2; - halt2_printf("Hit breakpoint at %06x\n", addr); - } - } -} - word32 g_num_kpc_breakpoints = 0; word32 g_kpc_breakpoints[20]; int check_kpc_breakpoints(word32 addr) { diff --git a/src/sim65816.c b/src/sim65816.c index 536e666..16d2476 100644 --- a/src/sim65816.c +++ b/src/sim65816.c @@ -34,7 +34,6 @@ extern void get_cwd(LPTSTR buffer, int size); #endif #ifndef GSPLUS_DEBUGGER -int g_dbg_step = 0; int g_dbg_enable_port = 0; void debug_server_poll(void) { } @@ -83,7 +82,6 @@ const char *g_gsplus_default_paths[] = { // probably overkill on the paths #define EV_VID_UPD 7 extern int g_stepping; -extern int g_dbg_step; extern int g_c068_statereg; @@ -775,7 +773,6 @@ void do_reset() { engine.kpc = get_memory16_c(0x00fffc, 0); g_stepping = 0; - g_dbg_step = 0; if (g_irq_pending) halt_printf("*** irq remainings...\n"); @@ -998,7 +995,6 @@ int gsplusmain(int argc, char **argv) { do_reset(); g_stepping = 0; - g_dbg_step = 0; // OG Notify emulator has been initialized and ready to accept external events g_initialized = 1; @@ -1587,7 +1583,7 @@ void run_prog() { engine.fcycles = prerun_fcycles; fcycles_stop = (g_event_start.next->dcycs - g_last_vbl_dcycs) + 0.001; - if(g_stepping || g_dbg_step < 0) { + if(g_stepping) { fcycles_stop = prerun_fcycles; } g_fcycles_stop = fcycles_stop; @@ -1649,7 +1645,7 @@ void run_prog() { if(halt_sim != 0 && halt_sim != HALT_EVENT) { break; } - if(g_stepping || g_dbg_step != 0) { + if(g_stepping) { printf("HIT STEPPING BREAK!\n"); break; } @@ -1719,7 +1715,7 @@ void run_prog() { if(halt_sim != 0 && halt_sim != HALT_EVENT) { break; } - if(g_stepping || g_dbg_step != 0) { + if(g_stepping) { break; } } From 8d60a2d437e91c42d5ac19224e47536e5506ae51 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Mon, 28 Jan 2019 18:10:27 -0500 Subject: [PATCH 048/186] clean up the disassembler a little bit. --- src/debug.c | 30 +++++----------- src/dis.c | 27 ++++++-------- src/{disas.h => disasm.c} | 75 +++++++++++---------------------------- src/disasm.h | 41 +++++++++++++++++++++ 4 files changed, 80 insertions(+), 93 deletions(-) rename src/{disas.h => disasm.c} (85%) create mode 100644 src/disasm.h diff --git a/src/debug.c b/src/debug.c index 49c3021..5df885e 100644 --- a/src/debug.c +++ b/src/debug.c @@ -20,13 +20,7 @@ #include "glog.h" // DISASSEMBLER STUFF -enum { - ABS = 1, ABSX, ABSY, ABSLONG, ABSIND, ABSXIND, IMPLY, ACCUM, IMMED, JUST8, - DLOC, DLOCX, DLOCY, LONG, LONGX, DLOCIND, DLOCINDY, DLOCXIND, DLOCBRAK, - DLOCBRAKY, DISP8, DISP8S, DISP8SINDY, DISP16, MVPMVN, REPVAL, SEPVAL -}; -extern const char * const disas_opcodes[256]; -extern const word32 disas_types[256]; +#include "disasm.h" // STEPPING/ENGINE STUFF extern Engine_reg engine; @@ -1217,8 +1211,8 @@ int do_dis_json(char *buf, word32 kpc, int accsize, int xsize, int op_provided, kpc++; - dtype = disas_types[opcode]; - out = disas_opcodes[opcode]; + dtype = disasm_types[opcode]; + out = disasm_opcodes[opcode]; type = dtype & 0xff; args = dtype >> 8; @@ -1292,6 +1286,12 @@ int do_dis_json(char *buf, word32 kpc, int accsize, int xsize, int op_provided, } sprintf(buf_disasm,"%s $%06x",out,val); break; + case ABSLONGX: + if(args != 3) { + printf("arg # mismatch for opcode %x\n", opcode); + } + sprintf(buf_disasm,"%s $%06x,X",out,val); + break; case ABSIND: if(args != 2) { printf("arg # mismatch for opcode %x\n", opcode); @@ -1349,18 +1349,6 @@ int do_dis_json(char *buf, word32 kpc, int accsize, int xsize, int op_provided, } sprintf(buf_disasm,"%s $%02x,Y",out,val); break; - case LONG: - if(args != 3) { - printf("arg # mismatch for opcode %x\n", opcode); - } - sprintf(buf_disasm,"%s $%06x",out,val); - break; - case LONGX: - if(args != 3) { - printf("arg # mismatch for opcode %x\n", opcode); - } - sprintf(buf_disasm,"%s $%06x,X",out,val); - break; case DLOCIND: if(args != 1) { printf("arg # mismatch for opcode %x\n", opcode); diff --git a/src/dis.c b/src/dis.c index 9dd95df..6bc6e87 100644 --- a/src/dis.c +++ b/src/dis.c @@ -9,7 +9,7 @@ #include "defc.h" #include -#include "disas.h" +#include "disasm.h" #define LINE_SIZE 160 @@ -836,8 +836,8 @@ int do_dis(FILE *outfile, word32 kpc, int accsize, int xsize, kpc++; - dtype = disas_types[opcode]; - out = disas_opcodes[opcode]; + dtype = disasm_types[opcode]; + out = disasm_opcodes[opcode]; type = dtype & 0xff; args = dtype >> 8; @@ -915,6 +915,13 @@ int do_dis(FILE *outfile, word32 kpc, int accsize, int xsize, sprintf(buffer,"%s\t$%06x",out,val); show_line(outfile, oldkpc,instr,args+1,buffer); break; + case ABSLONGX: + if(args != 3) { + printf("arg # mismatch for opcode %x\n", opcode); + } + sprintf(buffer,"%s\t$%06x,X",out,val); + show_line(outfile, oldkpc,instr,args+1,buffer); + break; case ABSIND: if(args != 2) { printf("arg # mismatch for opcode %x\n", opcode); @@ -981,20 +988,6 @@ int do_dis(FILE *outfile, word32 kpc, int accsize, int xsize, sprintf(buffer,"%s\t$%02x,Y",out,val); show_line(outfile, oldkpc,instr,args+1,buffer); break; - case LONG: - if(args != 3) { - printf("arg # mismatch for opcode %x\n", opcode); - } - sprintf(buffer,"%s\t$%06x",out,val); - show_line(outfile, oldkpc,instr,args+1,buffer); - break; - case LONGX: - if(args != 3) { - printf("arg # mismatch for opcode %x\n", opcode); - } - sprintf(buffer,"%s\t$%06x,X",out,val); - show_line(outfile, oldkpc,instr,args+1,buffer); - break; case DLOCIND: if(args != 1) { printf("arg # mismatch for opcode %x\n", opcode); diff --git a/src/disas.h b/src/disasm.c similarity index 85% rename from src/disas.h rename to src/disasm.c index 112d816..91c0082 100644 --- a/src/disas.h +++ b/src/disasm.c @@ -1,42 +1,7 @@ -/* - GSPLUS - Advanced Apple IIGS Emulator Environment - Based on the KEGS emulator written by Kent Dickey - See COPYRIGHT.txt for Copyright information - See LICENSE.txt for license (GPL v2) -*/ -enum { - ABS = 1, - ABSX, - ABSY, - ABSLONG, - ABSIND, - ABSXIND, - IMPLY, - ACCUM, - IMMED, - JUST8, - DLOC, - DLOCX, - DLOCY, - LONG, - LONGX, - DLOCIND, - DLOCINDY, - DLOCXIND, - DLOCBRAK, - DLOCBRAKY, - DISP8, - DISP8S, - DISP8SINDY, - DISP16, - MVPMVN, - REPVAL, - SEPVAL -}; +#include "disasm.h" - -const char * const disas_opcodes[256] = { +const char * const disasm_opcodes[256] = { "BRK", "ORA", "COP", "ORA", "TSB", "ORA", "ASL", "ORA", /* 00-07 */ "PHP", "ORA", "ASL", "PHD", "TSB", "ORA", "ASL", "ORA", /* 08-0f */ "BPL", "ORA", "ORA", "ORA", "TRB", "ORA", "ASL", "ORA", /* 10-17 */ @@ -72,7 +37,7 @@ const char * const disas_opcodes[256] = { }; -const word32 disas_types[256] = { +const unsigned disasm_types[256] = { JUST8+0x100, DLOCXIND+0x100, /* 00-01 */ JUST8+0x100, DISP8S+0x100, /* 02-03 */ DLOC+0x100, DLOC+0x100, /* 04-05 */ @@ -80,7 +45,7 @@ const word32 disas_types[256] = { IMPLY+0x000, IMMED+0x400, /* 08-9 */ ACCUM+0x000, IMPLY+0x000, /* 0a-b */ ABS+0x200, ABS+0x200, /* c-d */ - ABS+0x200, LONG+0x300, /* e-f */ + ABS+0x200, ABSLONG+0x300, /* e-f */ DISP8+0x100, DLOCINDY+0x100, /* 10-11 */ DLOCIND+0x100, DISP8SINDY+0x100, /* 12-13 */ DLOC+0x100, DLOCX+0x100, /* 14-15 */ @@ -88,7 +53,7 @@ const word32 disas_types[256] = { IMPLY+0x000, ABSY+0x200, /* 18-19 */ ACCUM+0x000, IMPLY+0x000, /* 1a-1b */ ABS+0x200, ABSX+0x200, /* 1c-1d */ - ABSX+0x200, LONGX+0x300, /* 1e-1f */ + ABSX+0x200, ABSLONGX+0x300, /* 1e-1f */ ABS+0x200, DLOCXIND+0x100, /* 20-21 */ ABSLONG+0x300, DISP8S+0x100, /* 22-23 */ DLOC+0x100, DLOC+0x100, /* 24-25 */ @@ -96,7 +61,7 @@ const word32 disas_types[256] = { IMPLY+0x000, IMMED+0x400, /* 28-29 */ ACCUM+0x000, IMPLY+0x000, /* 2a-2b */ ABS+0x200, ABS+0x200, /* 2c-2d */ - ABS+0x200, LONG+0x300, /* 2e-2f */ + ABS+0x200, ABSLONG+0x300, /* 2e-2f */ DISP8+0x100, DLOCINDY+0x100, /* 30-31 */ DLOCIND+0x100, DISP8SINDY+0x100, /* 32-33 */ DLOCX+0x100, DLOCX+0x100, /* 34-35 */ @@ -104,7 +69,7 @@ const word32 disas_types[256] = { IMPLY+0x000, ABSY+0x200, /* 38-39 */ ACCUM+0x000, IMPLY+0x000, /* 3a-3b */ ABSX+0x200, ABSX+0x200, /* 3c-3d */ - ABSX+0x200, LONGX+0x300, /* 3e-3f */ + ABSX+0x200, ABSLONGX+0x300, /* 3e-3f */ IMPLY+0x000, DLOCXIND+0x100, /* 40-41 */ JUST8+0x100, DISP8S+0x100, /* 42-43 */ MVPMVN+0x200, DLOC+0x100, /* 44-45 */ @@ -112,15 +77,15 @@ const word32 disas_types[256] = { IMPLY+0x000, IMMED+0x400, /* 48-49 */ ACCUM+0x000, IMPLY+0x000, /* 4a-4b */ ABS+0x200, ABS+0x200, /* 4c-4d */ - ABS+0x200, LONG+0x300, /* 4e-4f */ + ABS+0x200, ABSLONG+0x300, /* 4e-4f */ DISP8+0x100, DLOCINDY+0x100, /* 50-51 */ DLOCIND+0x100, DISP8SINDY+0x100, /* 52-53 */ MVPMVN+0x200, DLOCX+0x100, /* 54-55 */ DLOCX+0x100, DLOCBRAKY+0x100, /* 56-57 */ IMPLY+0x000, ABSY+0x200, /* 58-59 */ IMPLY+0x000, IMPLY+0x000, /* 5a-5b */ - LONG+0x300, ABSX+0x200, /* 5c-5d */ - ABSX+0x200, LONGX+0x300, /* 5e-5f */ + ABSLONG+0x300, ABSX+0x200, /* 5c-5d */ + ABSX+0x200, ABSLONGX+0x300, /* 5e-5f */ IMPLY+0x000, DLOCXIND+0x100, /* 60-61 */ DISP16+0x200, DISP8S+0x100, /* 62-63 */ DLOC+0x100, DLOC+0x100, /* 64-65 */ @@ -128,7 +93,7 @@ const word32 disas_types[256] = { IMPLY+0x000, IMMED+0x400, /* 68-69 */ ACCUM+0x000, IMPLY+0x000, /* 6a-6b */ ABSIND+0x200, ABS+0x200, /* 6c-6d */ - ABS+0x200, LONG+0x300, /* 6e-6f */ + ABS+0x200, ABSLONG+0x300, /* 6e-6f */ DISP8+0x100, DLOCINDY+0x100, /* 70-71 */ DLOCIND+0x100, DISP8SINDY+0x100, /* 72-73 */ DLOCX+0x100, DLOCX+0x100, /* 74-75 */ @@ -136,7 +101,7 @@ const word32 disas_types[256] = { IMPLY+0x000, ABSY+0x200, /* 78-79 */ IMPLY+0x000, IMPLY+0x000, /* 7a-7b */ ABSXIND+0x200, ABSX+0x200, /* 7c-7d */ - ABSX+0x200, LONGX+0x300, /* 7e-7f */ + ABSX+0x200, ABSLONGX+0x300, /* 7e-7f */ DISP8+0x100, DLOCXIND+0x100, /* 80-81 */ DISP16+0x200, DISP8S+0x100, /* 82-83 */ DLOC+0x100, DLOC+0x100, /* 84-85 */ @@ -144,7 +109,7 @@ const word32 disas_types[256] = { IMPLY+0x000, IMMED+0x400, /* 88-89 */ IMPLY+0x000, IMPLY+0x000, /* 8a-8b */ ABS+0x200, ABS+0x200, /* 8c-8d */ - ABS+0x200, LONG+0x300, /* 8e-8f */ + ABS+0x200, ABSLONG+0x300, /* 8e-8f */ DISP8+0x100, DLOCINDY+0x100, /* 90-91 */ DLOCIND+0x100, DISP8SINDY+0x100, /* 92-93 */ DLOCX+0x100, DLOCX+0x100, /* 94-95 */ @@ -152,7 +117,7 @@ const word32 disas_types[256] = { IMPLY+0x000, ABSY+0x200, /* 98-99 */ IMPLY+0x000, IMPLY+0x000, /* 9a-9b */ ABS+0x200, ABSX+0x200, /* 9c-9d */ - ABSX+0x200, LONGX+0x300, /* 9e-9f */ + ABSX+0x200, ABSLONGX+0x300, /* 9e-9f */ IMMED+0x500, DLOCXIND+0x100, /* a0-a1 */ IMMED+0x500, DISP8S+0x100, /* a2-a3 */ DLOC+0x100, DLOC+0x100, /* a4-a5 */ @@ -160,7 +125,7 @@ const word32 disas_types[256] = { IMPLY+0x000, IMMED+0x400, /* a8-a9 */ IMPLY+0x000, IMPLY+0x000, /* aa-ab */ ABS+0x200, ABS+0x200, /* ac-ad */ - ABS+0x200, LONG+0x300, /* ae-af */ + ABS+0x200, ABSLONG+0x300, /* ae-af */ DISP8+0x100, DLOCINDY+0x100, /* b0-b1 */ DLOCIND+0x100, DISP8SINDY+0x100, /* b2-b3 */ DLOCX+0x100, DLOCX+0x100, /* b4-b5 */ @@ -168,7 +133,7 @@ const word32 disas_types[256] = { IMPLY+0x000, ABSY+0x200, /* b8-b9 */ IMPLY+0x000, IMPLY+0x000, /* ba-bb */ ABSX+0x200, ABSX+0x200, /* bc-bd */ - ABSY+0x200, LONGX+0x300, /* be-bf */ + ABSY+0x200, ABSLONGX+0x300, /* be-bf */ IMMED+0x500, DLOCXIND+0x100, /* c0-c1 */ REPVAL+0x100, DISP8S+0x100, /* c2-c3 */ DLOC+0x100, DLOC+0x100, /* c4-c5 */ @@ -176,7 +141,7 @@ const word32 disas_types[256] = { IMPLY+0x000, IMMED+0x400, /* c8-c9 */ IMPLY+0x000, IMPLY+0x000, /* ca-cb */ ABS+0x200, ABS+0x200, /* cc-cd */ - ABS+0x200, LONG+0x300, /* ce-cf */ + ABS+0x200, ABSLONG+0x300, /* ce-cf */ DISP8+0x100, DLOCINDY+0x100, /* d0-d1 */ DLOCIND+0x100, DISP8SINDY+0x100, /* d2-d3 */ DLOC+0x100, DLOCX+0x100, /* d4-d5 */ @@ -184,7 +149,7 @@ const word32 disas_types[256] = { IMPLY+0x000, ABSY+0x200, /* d8-d9 */ IMPLY+0x000, IMPLY+0x000, /* da-db */ ABSIND+0x200, ABSX+0x200, /* dc-dd */ - ABSX+0x200, LONGX+0x300, /* de-df */ + ABSX+0x200, ABSLONGX+0x300, /* de-df */ IMMED+0x500, DLOCXIND+0x100, /* e0-e1 */ SEPVAL+0x100, DISP8S+0x100, /* e2-e3 */ DLOC+0x100, DLOC+0x100, /* e4-e5 */ @@ -192,7 +157,7 @@ const word32 disas_types[256] = { IMPLY+0x000, IMMED+0x400, /* e8-e9 */ IMPLY+0x000, IMPLY+0x000, /* ea-eb */ ABS+0x200, ABS+0x200, /* ec-ed */ - ABS+0x200, LONG+0x300, /* ee-ef */ + ABS+0x200, ABSLONG+0x300, /* ee-ef */ DISP8+0x100, DLOCINDY+0x100, /* f0-f1 */ DLOCIND+0x100, DISP8SINDY+0x100, /* f2-f3 */ IMMED+0x200, DLOCX+0x100, /* f4-f5 */ @@ -200,5 +165,5 @@ const word32 disas_types[256] = { IMPLY+0x000, ABSY+0x200, /* f8-f9 */ IMPLY+0x000, IMPLY+0x000, /* fa-fb */ ABSXIND+0x200, ABSX+0x200, /* fc-fd */ - ABSX+0x200, LONGX+0x300, /* fe-ff */ + ABSX+0x200, ABSLONGX+0x300, /* fe-ff */ }; diff --git a/src/disasm.h b/src/disasm.h new file mode 100644 index 0000000..3c437fd --- /dev/null +++ b/src/disasm.h @@ -0,0 +1,41 @@ +/* + GSPLUS - Advanced Apple IIGS Emulator Environment + Based on the KEGS emulator written by Kent Dickey + See COPYRIGHT.txt for Copyright information + See LICENSE.txt for license (GPL v2) +*/ + +enum { + ABS = 1, + ABSX, + ABSY, + ABSIND, + ABSXIND, + IMPLY, + ACCUM, + IMMED, + JUST8, + DLOC, + DLOCX, + DLOCY, + ABSLONG, + ABSLONGX, + DLOCIND, + DLOCINDY, + DLOCXIND, + DLOCBRAK, + DLOCBRAKY, + DISP8, + DISP8S, + DISP8SINDY, + DISP16, + MVPMVN, + REPVAL, + SEPVAL +}; + + +extern const char * const disasm_opcodes[256]; + + +extern const unsigned disasm_types[256]; \ No newline at end of file From 0e2f35e7fe1c003b34a562883eed2345ef446d10 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Mon, 28 Jan 2019 22:01:29 -0500 Subject: [PATCH 049/186] re-order instructions so zero/negative flags are set AFTER memory write (which may abort) --- src/defs_instr.h | 24 ++++++++++++------------ src/engine_c.c | 5 +++++ 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/defs_instr.h b/src/defs_instr.h index 79257bc..b8a66e7 100644 --- a/src/defs_instr.h +++ b/src/defs_instr.h @@ -1055,14 +1055,14 @@ defs_instr_start_16 .word 0 # define TSB_INST(in_bank) \ tmp1 = arg | acc; \ CYCLES_PLUS_1; \ - zero = arg & acc; \ - SET_MEMORY8(addr_latch, tmp1); + SET_MEMORY8(addr_latch, tmp1); \ + zero = arg & acc; # else # define TSB_INST(in_bank) \ tmp1 = arg | acc; \ CYCLES_PLUS_1; \ - zero = arg & acc; \ - SET_MEMORY16(addr_latch, tmp1, in_bank); + SET_MEMORY16(addr_latch, tmp1, in_bank); \ + zero = arg & acc; # endif #endif @@ -1107,15 +1107,15 @@ defs_instr_start_16 .word 0 psr = (psr & 0x1fe) + ((arg >> 7) & 1); \ tmp1 = (arg << 1) & 0xff; \ CYCLES_PLUS_1; \ - SET_NEG_ZERO8(tmp1); \ - SET_MEMORY8(addr_latch, tmp1); + SET_MEMORY8(addr_latch, tmp1); \ + SET_NEG_ZERO8(tmp1); # else # define ASL_INST(in_bank) \ psr = (psr & 0x1fe) + ((arg >> 15) & 1);\ tmp1 = (arg << 1) & 0xffff; \ CYCLES_PLUS_1; \ - SET_NEG_ZERO16(tmp1); \ - SET_MEMORY16(addr_latch, tmp1, in_bank); + SET_MEMORY16(addr_latch, tmp1, in_bank);\ + SET_NEG_ZERO16(tmp1); # endif #endif @@ -1302,14 +1302,14 @@ defs_instr_start_16 .word 0 arg = arg & 0xff; \ tmp1 = arg & ~acc; \ CYCLES_PLUS_1; \ - zero = arg & acc; \ - SET_MEMORY8(addr_latch, tmp1); + SET_MEMORY8(addr_latch, tmp1); \ + zero = arg & acc; # else # define TRB_INST(in_bank) \ tmp1 = arg & ~acc; \ CYCLES_PLUS_1; \ - zero = arg & acc; \ - SET_MEMORY16(addr_latch, tmp1, in_bank); + SET_MEMORY16(addr_latch, tmp1, in_bank);\ + zero = arg & acc; # endif #endif diff --git a/src/engine_c.c b/src/engine_c.c index c69177d..4365f8f 100644 --- a/src/engine_c.c +++ b/src/engine_c.c @@ -230,6 +230,9 @@ extern word32 slow_mem_changed[]; g_ret2 = saved_pc; \ kpc = saved_pc; goto abort; \ + kpc = saved_pc; \ + psr = saved_psr; \ + goto finish; \ } #else #define MMU_CHECK(addr, val, bytes, in_page, in_bank) @@ -936,6 +939,7 @@ word32 get_remaining_operands(word32 addr, word32 opcode, word32 psr, Fplus *fpl #define FETCH_OPCODE \ addr = saved_pc = kpc; \ + saved_psr = psr; \ CYCLES_PLUS_2; \ stat = GET_PAGE_INFO_RD(((addr) >> 8) & 0xffff); \ wstat = PTR2WORD(stat) & 0xff; \ @@ -1008,6 +1012,7 @@ int enter_engine(Engine_reg *engine_ptr) { word32 tmp1, tmp2; word32 saved_pc = 0; + word32 saved_psr = 0; word32 abort_support = g_num_breakpoints ? 1 : 0; word32 kpc_support = g_num_kpc_breakpoints ? 1 : 0; From 81c74470efce205c20f6f3fe04b934f14c41d5a0 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Mon, 28 Jan 2019 22:59:00 -0500 Subject: [PATCH 050/186] PUSH16 MMU check was incorrect. --- src/engine_c.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/engine_c.c b/src/engine_c.c index 4365f8f..2f58384 100644 --- a/src/engine_c.c +++ b/src/engine_c.c @@ -250,7 +250,10 @@ extern word32 slow_mem_changed[]; stack = stack & 0xffff; #define PUSH16(arg) \ - MMU_CHECK(stack, arg, 2, 1, 1) \ + tmp2 = stack - 1; \ + if (psr >> 8) tmp2 = (tmp2 & 0xff) | 0x0100; \ + tmp2 &= 0xffff; \ + MMU_CHECK(tmp2, arg, 2, psr >> 8, 1) \ _PUSH16(arg) #define _PUSH16(arg) \ From 0cf532c00d07ba9dbfb4dc01325d200c80e74353 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Mon, 28 Jan 2019 23:03:04 -0500 Subject: [PATCH 051/186] BRK / COP weren't pushing the current z/neg flags. --- src/instable.h | 53 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 41 insertions(+), 12 deletions(-) diff --git a/src/instable.h b/src/instable.h index 5ff6a5e..95ab03a 100644 --- a/src/instable.h +++ b/src/instable.h @@ -81,17 +81,30 @@ brk_testing_SYM } g_num_brk++; INC_KPC_2; + + psr = psr & (~0x82); + psr |= (neg << 7); + psr |= ((!zero) << 1); + tmp1 = psr & 0xff; if(psr & 0x100) { - PUSH16(kpc & 0xffff); - PUSH8(psr & 0xff); + MMU_CHECK( + ((stack - 2) & 0xff) | 0x0100, + (kpc << 8) | tmp1, 3, 1, 1 + ) + _PUSH16(kpc & 0xffff); + _PUSH8(tmp1); GET_MEMORY16(0xfffe, kpc, 0); dbank = 0; } else { - PUSH8(kpc >> 16); - PUSH16(kpc); - PUSH8(psr & 0xff); + MMU_CHECK( + (stack - 3) & 0xffff, + (kpc << 8) | tmp1, 4, 0, 1 + ) + _PUSH8(kpc >> 16); + _PUSH16(kpc); + _PUSH8(tmp1); GET_MEMORY16(0xffe6, kpc, 0); - halt_printf("Halting for native break!\n"); + //halt_printf("Halting for native break!\n"); } kpc = kpc & 0xffff; psr |= 0x4; @@ -155,16 +168,32 @@ cop_native_SYM #else g_num_cop++; INC_KPC_2; + + psr = psr & (~0x82); + psr |= (neg << 7); + psr |= ((!zero) << 1); + tmp1 = psr & 0xff; + if(psr & 0x100) { - halt_printf("Halting for emul COP at %04x\n", kpc); - PUSH16(kpc & 0xffff); - PUSH8(psr & 0xff); + //halt_printf("Halting for emul COP at %04x\n", kpc); + MMU_CHECK( + ((stack - 2) & 0xff) | 0x0100, + (kpc << 8) | tmp1, 3, 1, 1 + ) + + _PUSH16(kpc & 0xffff); + _PUSH8(tmp1); GET_MEMORY16(0xfff4, kpc, 0); dbank = 0; } else { - PUSH8(kpc >> 16); - PUSH16(kpc & 0xffff); - PUSH8(psr & 0xff); + MMU_CHECK( + (stack - 3) & 0xffff, + (kpc << 8) | tmp1, 4, 0, 1 + ) + + _PUSH8(kpc >> 16); + _PUSH16(kpc & 0xffff); + _PUSH8(tmp1); GET_MEMORY16(0xffe4, kpc, 0); } kpc = kpc & 0xffff; From c4aa64de482c1b9e1bd38a17aad4db9b70e3e725 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Mon, 28 Jan 2019 23:04:20 -0500 Subject: [PATCH 052/186] new breakpoint code. --- src/debug_shell.re2c | 696 +++++++++++++++++++++++++++++++++++++++++++ src/defcomm.h | 5 + src/engine_c.c | 58 ++-- src/moremem.c | 47 ++- src/options.c | 5 + 5 files changed, 783 insertions(+), 28 deletions(-) create mode 100644 src/debug_shell.re2c diff --git a/src/debug_shell.re2c b/src/debug_shell.re2c new file mode 100644 index 0000000..1e353f4 --- /dev/null +++ b/src/debug_shell.re2c @@ -0,0 +1,696 @@ + +/* + * requires re2c (http://re2c.org) + * + */ + +#include +#include "defc.h" +#include +#include +#include + +#include "disasm.h" + + +extern int g_fullscreen; +extern int g_config_control_panel; +extern Engine_reg engine; + + +int g_num_breakpoints = 0; +word32 g_breakpts[MAX_BREAK_POINTS]; + +int g_num_mp_breakpoints = 0; +word32 g_mp_breakpoints[MAX_BREAK_POINTS]; + +int g_num_bp_breakpoints = 0; +word32 g_bp_breakpoints[MAX_BREAK_POINTS]; + +int g_dbg_shell = 0; +int g_stepping = 0; + +word32 g_abort_address = 0; +word32 g_abort_value = 0; +word32 g_abort_bytes = 0; + + +word32 do_hexdump(word32 address, int lines) { + static char hex[] = "0123456789abcdef"; + + char buffer1[64]; + char buffer2[20]; + int i; + + while (--lines) { + char *cp = buffer1; + for (i = 0; i < 16; ++i) { + uint8_t c = get_memory_c(address + i, 0); + *cp++ = hex[c >> 4]; + *cp++ = hex[c & 0x0f]; + *cp++ = ' '; + if (i == 7) *cp++ = ' '; + + buffer2[i] = (c < 0x80) && isprint(c) ? c : '.'; + } + *cp = 0; + buffer2[16] = 0; + printf("%02x/%04x: %s %s\n", + address >> 16, address & 0xffff, + buffer1, buffer2 + ); + address += 16; + address &= 0xffffff; + } + + return address; +} + +word32 do_list(word32 address, int *psr_ptr, int lines) { + + char buffer[32]; + int psr = *psr_ptr; + unsigned opcode; + unsigned dtype; + unsigned operand; + word32 pc; + int i; + int args; + + while (lines--) { + pc = address; + + opcode = get_memory_c(address++, 0) & 0xff; + dtype = disasm_types[opcode]; + args = dtype >> 8; + + switch (args) { + case 4: + args = psr & 0x20 ? 1 : 2; + break; + case 5: + args = psr & 0x10 ? 1 : 2; + break; + } + operand = 0; + switch(args) { + case 1: + operand = get_memory_c(address, 0); + break; + case 2: + operand = get_memory16_c(address, 0); + break; + case 3: + operand = get_memory24_c(address, 0); + break; + } + address += args; + buffer[0] = 0; + + switch(dtype & 0xff) { + case ABS: + sprintf(buffer, "$%04x", operand); + break; + case ABSX: + sprintf(buffer, "$%04x,x", operand); + break; + case ABSY: + sprintf(buffer, "$%04x,y", operand); + break; + + case ABSIND: + sprintf(buffer,"($%04x)",operand ); + break; + case ABSXIND: + sprintf(buffer,"($%04x,x)",operand ); + break; + case IMPLY: + break; + case ACCUM: + break; + case IMMED: + sprintf(buffer,"#$%0*x",args * 2, operand); + break; + case JUST8: + sprintf(buffer,"$%02x",operand); + break; + case DLOC: + sprintf(buffer,"$%02x",operand); + break; + case DLOCX: + sprintf(buffer,"$%02x,x",operand); + break; + case DLOCY: + sprintf(buffer,"$%02x,y",operand); + break; + case ABSLONG: + sprintf(buffer,"$%06x",operand); + break; + case ABSLONGX: + sprintf(buffer,"$%06x,x",operand); + break; + case DLOCIND: + sprintf(buffer,"($%02x)",operand); + break; + case DLOCINDY: + sprintf(buffer,"($%02x),y",operand); + break; + case DLOCXIND: + sprintf(buffer,"($%02x,x)",operand); + break; + case DLOCBRAK: + sprintf(buffer,"[$%02x]",operand); + break; + case DLOCBRAKY: + sprintf(buffer,"[$%02x],y",operand); + break; + case DISP8: + sprintf(buffer,"$%04x", + (address+(int8_t)operand) & 0xffff); + break; + case DISP8S: + sprintf(buffer,"$%02x,s",operand); + break; + case DISP8SINDY: + sprintf(buffer,"($%02x,s),y",operand); + break; + case DISP16: + sprintf(buffer,"$%04x", + (address+(int16_t)(operand)) & 0xffff); + break; + case MVPMVN: + sprintf(buffer,"$%02x,$%02x",operand & 0xff,operand >> 8); + break; + case SEPVAL: + psr |= operand; + sprintf(buffer,"#$%02x",operand); + break; + case REPVAL: + psr &= ~operand; + sprintf(buffer,"#$%02x",operand); + break; + } + + printf("%02x/%04x: %s %-10s", + pc >> 16, pc & 0xffff, + disasm_opcodes[opcode], buffer); + + printf("%02x", opcode); + for(i = 0 ; i < args; ++i) { + printf(" %02x", operand & 0xff); + operand >>= 8; + } + fputc('\n', stdout); + } + *psr_ptr = psr; + return address; +} + + +enum { + REG_A, + REG_B, + REG_D, + REG_E, + REG_K, + REG_MX, + REG_P, + REG_PC, + REG_S, + REG_X, + REG_Y, +}; + +static word32 to_hex(const char *iter, const char *end) { + word32 rv = 0; + while(iter != end) { + char c = *iter++; + rv <<= 4; + if (isdigit(c)) rv |= c - '0'; + else rv |= (c | 0x20) - 'a' + 10; + } + return rv; +} + + +/*!re2c + re2c:define:YYCTYPE = char; + re2c:yyfill:enable = 0; + end = "\x00"; + x = [A-Fa-f0-9]; +*/ + +static int do_assign(const char *cp, int reg) { + + word32 addr = 0; + int has_bank = 0; + const char *YYCURSOR = cp; + const char *YYMARKER = NULL; + + /*!re2c + + * { return -1; } + x{6} end { + addr = to_hex(cp, cp + 6); + has_bank = 1; + goto next; + } + x{2} "/" x{4} end { + addr = to_hex(cp, cp + 2) << 16; + addr |= to_hex(cp + 3, cp + 7); + has_bank = 1; + goto next; + } + x{4} end { + addr = to_hex(cp, cp + 4); + has_bank = 0; + goto next; + } + "%"[01]{2} end { + if (reg != REG_MX) return -1; + if (cp[1] == '1') addr |= 0x20; + if (cp[2] == '1') addr |= 0x10; + goto next; + } + */ + +next: + + if (has_bank && reg != REG_PC) return -1; + + switch(reg) { + case REG_A: engine.acc = addr; break; + case REG_X: engine.xreg = addr; break; + case REG_Y: engine.yreg = addr; break; + case REG_D: engine.direct = addr; break; + case REG_S: engine.stack = addr; break; + case REG_B: engine.dbank = addr & 0xff; break; + + case REG_E: + engine.psr &= 0xff; + if (addr) engine.psr |= 0x0100; + break; + + case REG_K: + addr = (addr & 0xff) << 16; + engine.kpc = (engine.kpc & 0xffff) | addr; + break; + + case REG_PC: + if (!has_bank) addr |= (engine.kpc & 0xff0000); + engine.kpc = addr; + break; + + case REG_P: + engine.psr &= ~0xff; + engine.psr |= (addr & 0xff); + break; + + case REG_MX: + /* only 00, 10, 20, 30 are legal values */ + if ((addr | 0x30) != 0x30) return -1; + engine.psr &= ~0x30; + engine.psr |= addr; + break; + + + } + + /* fixup registers */ + if (engine.psr & 0x0100) { + engine.psr |= 0x30; + engine.stack = (engine.stack & 0xff) | 0x0100; + } + if (engine.psr & 0x10) { + engine.xreg &= 0xff; + engine.yreg &= 0xff; + } + return 0; +} + + +static int addr_cmp(const void *a, const void *b) { + word32 aa = *(const word32 *)a; + word32 bb = *(const word32 *)b; + + return (int)aa - (int)bb; +} + + + + + +void show_bp() { + int i; + word32 addr; + for (i = 0; i < g_num_bp_breakpoints; ++i) { + addr = g_bp_breakpoints[i]; + printf("%02x/%04x\n", addr >> 16, addr & 0xffff); + } +} + +void set_bp(word32 addr) { + int i; + + for (i = 0; i < g_num_bp_breakpoints; ++i) { + if (g_bp_breakpoints[i] == addr) break; + } + + if (i < g_num_bp_breakpoints) return; /* already set */ + if (g_num_bp_breakpoints == MAX_BREAK_POINTS) { + printf("Too many breakpoints.\n"); + return; + } + g_bp_breakpoints[g_num_bp_breakpoints++] = addr; + + + qsort(g_bp_breakpoints, g_num_bp_breakpoints, sizeof(word32), addr_cmp); + fixup_brks(); +} + +void delete_bp(word32 addr) { + int i; + + for (i = 0; i < g_num_bp_breakpoints; ++i) { + if (g_bp_breakpoints[i] == addr) break; + } + + + if (i == g_num_bp_breakpoints) return; /* not set */ + g_bp_breakpoints[i] = 0; + g_bp_breakpoints[i] = g_bp_breakpoints[--g_num_bp_breakpoints]; + + qsort(g_bp_breakpoints, g_num_bp_breakpoints, sizeof(word32), addr_cmp); + fixup_brks(); + +} +static int do_bp_mp_common(const char *cp, word32 default_bank, word32 *breakpoints, int *num_breakpoints) { + /* bp + * bp [+-] address + */ + + int i = 0; + int plus = 1; + word32 addr = 0; + const char *YYCURSOR = NULL; + const char *YYMARKER = NULL; + + while (*cp == ' ') ++cp; + switch(*cp) { + case '-': plus = 0; + case '+': + ++cp; + break; + } + + if (*cp == 0) { + /* just print them... */ + for (i = 0; i < *num_breakpoints; ++i) { + addr = breakpoints[i]; + printf("%02x/%04x\n", addr >> 16, addr & 0xffff); + } + return 0; + } + + YYCURSOR = cp; + /*!re2c + + * { return -1; } + x{6} end { + addr = to_hex(cp, cp + 6); + goto next; + } + x{2} "/" x{4} end { + addr = to_hex(cp, cp + 2) << 16; + addr |= to_hex(cp + 3, cp + 7); + goto next; + } + x{4} end { + addr = to_hex(cp, cp + 4); + addr |= default_bank; + goto next; + } + */ + +next: + + for (i = 0; i < *num_breakpoints; ++i) { + if (breakpoints[i] == addr) break; + } + + if (plus) { + if (i < *num_breakpoints) return 0; /* already set */ + if (*num_breakpoints == MAX_BREAK_POINTS) { + printf("Too many breakpoints.\n"); + return 0; + } + breakpoints[(*num_breakpoints)++] = addr; + } else { + if (i == *num_breakpoints) return 0; /* not set */ + breakpoints[i] = 0; + breakpoints[i] = breakpoints[--(*num_breakpoints)]; + } + + /* sort */ + qsort(breakpoints, *num_breakpoints, sizeof(word32), addr_cmp); + fixup_brks(); + + return 0; +} + +static int do_bp(const char *cp) { + return do_bp_mp_common(cp, engine.kpc & 0xff0000, + g_bp_breakpoints, &g_num_bp_breakpoints); +} + +static int do_mp(const char *cp) { + return do_bp_mp_common(cp, engine.dbank << 16, + g_mp_breakpoints, &g_num_mp_breakpoints); +} + + +static word32 g_prev_address = 0; + +/* return -1 on error, 0 on success, 1 if debug shell should exit. */ +static int parse_command(const char *cp) { + + const char *YYCURSOR = cp; + const char *YYMARKER = NULL; + + int addr = 0; + int has_bank = 0; + int has_addr = 0; + + /*!re2c + + _ = (" " | "\x00"); + + * { --YYCURSOR; if (cp == YYCURSOR) goto next; return -1; } + + "a=" { return do_assign(YYCURSOR, REG_A); } + "x=" { return do_assign(YYCURSOR, REG_X); } + "y=" { return do_assign(YYCURSOR, REG_Y); } + "d=" { return do_assign(YYCURSOR, REG_D); } + "e=" { return do_assign(YYCURSOR, REG_E); } + "s=" { return do_assign(YYCURSOR, REG_S); } + "k=" { return do_assign(YYCURSOR, REG_K); } + "b=" { return do_assign(YYCURSOR, REG_B); } + "p=" { return do_assign(YYCURSOR, REG_P); } + "pc=" { return do_assign(YYCURSOR, REG_PC); } + "mx=" { return do_assign(YYCURSOR, REG_MX); } + + "bp" / _ { return do_bp(YYCURSOR); } + "mp" / _ { return do_mp(YYCURSOR); } + + "*" end { show_regs(); return 0; } + + ("q" | "quit" | "exit") end { return 'q'; } + "reset" end { do_reset(); return 0; } + + + x{6} { + addr = to_hex(cp, cp + 6); + has_bank = 1; + has_addr = 1; + goto next; + } + x{2} "/" x{4} { + addr = to_hex(cp, cp + 2) << 16; + addr |= to_hex(cp + 3, cp + 7); + has_bank = 1; + has_addr = 1; + goto next; + } + x{1,4} { + addr = to_hex(cp, YYCURSOR); + has_bank = 0; + has_addr = 1; + goto next; + } + */ +next: + + /*!re2c + * { return -1; } + ";l" end { + int psr = engine.psr; + if (!has_addr) { + addr = g_prev_address; + if (!addr) { + addr = engine.kpc; + has_bank = 1; + } + } + if (!has_bank) { + addr |= (engine.kpc & 0xff0000); + } + g_prev_address = do_list(addr, &psr, 20); + return 0; + } + ";h" end { + if (!has_addr) { + addr = g_prev_address; + has_bank = 1; + } + if (!has_bank) { + addr |= (engine.dbank << 16); + } + g_prev_address = do_hexdump(addr, 20); + return 0; + } + "g" end { + /* GO! */ + if (has_addr) { + if (!has_bank) addr |= (engine.kpc & 0xff0000); + engine.kpc = addr; + } + g_stepping = 0; + return 1; + } + "s" end { + /* step */ + if (has_addr) { + if (!has_bank) addr |= (engine.kpc & 0xff0000); + engine.kpc = addr; + } + g_stepping = 1; + return 1; + } + + */ +} + +char *readline(const char *prompt) { + static char buffer[1024]; + fputs(prompt, stdout); + fflush(stdout); + + for(;;) { + int ok = read(STDIN_FILENO, buffer, sizeof(buffer)-1); + if (ok < 0) { + if (ok == EINTR) continue; + return NULL; + } + if (ok == 0) return NULL; + while (ok) { + char c = buffer[ok-1]; + if (c == ' ' || c == '\r' || c == '\n') { + --ok; + continue; + } + break; + } + buffer[ok] = 0; + + return buffer; + } +} + +int debug_shell(int code) { + int c; + char *cp; + + int psr = engine.psr; + + + /* todo -- only clear IF exit pc == entry pc ? */ + if (code == RET_BP) { + engine.flags |= FLAG_IGNORE_BP; + printf("Breakpoint hit:\n"); + } + if (code == RET_MP) { + engine.flags |= FLAG_IGNORE_MP; + printf("Memory breakpoint hit\n"); + switch (g_abort_bytes) { + case 1: g_abort_value &= 0xff; break; + case 2: g_abort_value &= 0xffff; break; + case 3: g_abort_value &= 0xffffff; break; + } + printf("%06x: %0*x\n", g_abort_address, g_abort_bytes * 2, g_abort_value); + } + + do_list(engine.kpc, &psr, 1); + + for(;;) { + cp = readline("> "); + if (!cp) return 0; + if (!*cp) continue; + c = parse_command(cp); + if (c < 0) printf("error.\n"); + if (c == 'q') return 0; + if (c == 1) return 1; + } +} + +/* also called by do_step */ +void do_go() { + int ret; + int ok; + + /* if -g flag, start with debug shell ... */ + + + g_config_control_panel = 1; + if (g_dbg_shell) { + ok = debug_shell(0); + if (!ok) return; + } + + g_config_control_panel = 0; + clear_halt(); + + for(;;) { + + ret = run_prog(); + if (ret || g_stepping) { + g_config_control_panel = 1; + ok = debug_shell(ret); + if (!ok) return; + g_config_control_panel = 0; + } + } +} + +/* legacy */ + + + +void halt_printf(const char *fmt, ...) { + va_list args; + + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); + + set_halt(1); +} + +void halt2_printf(const char *fmt, ...) { + va_list args; + + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); + + set_halt(2); +} diff --git a/src/defcomm.h b/src/defcomm.h index 643ae08..332c3be 100644 --- a/src/defcomm.h +++ b/src/defcomm.h @@ -94,7 +94,12 @@ #define RET_C70A 0xb #define RET_C70D 0xc #define RET_IRQ 0xd +#define RET_BP 0xe +#define RET_MP 0xf +#define FLAG_IGNORE_MP 0x01 +#define FLAG_IGNORE_BP 0x02 +#define FLAG_STEP 0x04 #define MODE_BORDER 0 #define MODE_TEXT 1 diff --git a/src/engine_c.c b/src/engine_c.c index 2f58384..4b32b28 100644 --- a/src/engine_c.c +++ b/src/engine_c.c @@ -221,15 +221,11 @@ extern word32 slow_mem_changed[]; _ macros do not do any MMU checking */ -#if 0 +#if 1 #define MMU_CHECK(addr, val, bytes, in_page, in_bank) \ - if (abort_support && check_abort_breakpoints(addr, bytes, in_page, in_bank)) { \ - g_abort_value = val; \ - g_abort_address = addr; \ - g_ret1 = RET_ABORT; \ + if (abort_support && check_mp_breakpoints(addr, val, bytes, in_page, in_bank)) { \ + g_ret1 = RET_MP; \ g_ret2 = saved_pc; \ - kpc = saved_pc; - goto abort; \ kpc = saved_pc; \ psr = saved_psr; \ goto finish; \ @@ -404,12 +400,20 @@ extern word32 slow_mem_changed[]; -word32 g_num_kpc_breakpoints = 0; -word32 g_kpc_breakpoints[20]; -int check_kpc_breakpoints(word32 addr) { +extern int g_num_bp_breakpoints; +extern word32 g_bp_breakpoints[]; + +extern int g_num_mp_breakpoints; +extern word32 g_mp_breakpoints[]; + +extern word32 g_abort_address; +extern word32 g_abort_value; +extern word32 g_abort_bytes; + +int check_bp_breakpoints(word32 addr) { int i; - for (i = 0; i < g_num_kpc_breakpoints; ++i) { - if (g_kpc_breakpoints[i] == addr) { + for (i = 0; i < g_num_bp_breakpoints; ++i) { + if (g_bp_breakpoints[i] == addr) { return 1; } } @@ -421,15 +425,17 @@ int check_kpc_breakpoints(word32 addr) { MMU hardware could, of course, store the address [and value?] */ -word32 g_abort_address; -word32 g_abort_value; -int check_abort_breakpoints(word32 addr, unsigned bytes, int in_page, int in_bank) { + +int check_mp_breakpoints(word32 addr, word32 value, unsigned bytes, int in_page, int in_bank) { byte *stat; word32 wstat; word32 mask = 0xffffff; int i; + unsigned xbytes = bytes; + word32 xaddr = addr; + if (in_bank) mask = 0xffff; if (in_page) mask = 0xff; @@ -437,8 +443,14 @@ int check_abort_breakpoints(word32 addr, unsigned bytes, int in_page, int in_ban stat = GET_PAGE_INFO_WR(((addr) >> 8) & 0xffff); wstat = PTR2WORD(stat) & 0xff; if ((wstat & BANK_BREAK)) { - for (i = 0; i < g_num_breakpoints; ++i) { - if (addr == g_breakpts[i]) return 1; + for (i = 0; i < g_num_mp_breakpoints; ++i) { + if (addr == g_mp_breakpoints[i]) { + g_abort_address = xaddr; + g_abort_bytes = xbytes; + mask = (1 << (xbytes << 3))-1; + g_abort_value = value & mask; + return 1; + } } } @@ -951,8 +963,8 @@ word32 get_remaining_operands(word32 addr, word32 opcode, word32 psr, Fplus *fpl opcode = *ptr; \ if (wstat & BANK_BREAK) { \ wstat &= ~BANK_BREAK; \ - if (kpc_support && check_kpc_breakpoints(addr)) { \ - FINISH(RET_KPC, addr); \ + if (kpc_support && check_bp_breakpoints(addr)) { \ + FINISH(RET_BP, addr); \ } \ } \ if((wstat & BANK_IO_TMP) || ((addr & 0xff) > 0xfc)) { \ @@ -1017,12 +1029,12 @@ int enter_engine(Engine_reg *engine_ptr) { word32 saved_pc = 0; word32 saved_psr = 0; - word32 abort_support = g_num_breakpoints ? 1 : 0; - word32 kpc_support = g_num_kpc_breakpoints ? 1 : 0; + word32 abort_support = g_num_mp_breakpoints ? 1 : 0; + word32 kpc_support = g_num_bp_breakpoints ? 1 : 0; - if (engine_ptr->flags & 0x01) abort_support = 0; - if (engine_ptr->flags & 0x01) kpc_support = 0; + if (engine_ptr->flags & FLAG_IGNORE_MP) abort_support = 0; + if (engine_ptr->flags & FLAG_IGNORE_BP) kpc_support = 0; tmp_pc_ptr = 0; diff --git a/src/moremem.c b/src/moremem.c index 41496d7..42b8c26 100644 --- a/src/moremem.c +++ b/src/moremem.c @@ -6,6 +6,7 @@ */ #include "defc.h" +#include #ifdef HAVE_RAWNET #include "rawnet/cs8900.h" @@ -24,8 +25,13 @@ extern unsigned char iostrobe; extern word32 slow_mem_changed[]; -extern int g_num_breakpoints; -extern word32 g_breakpts[]; + +extern int g_num_mp_breakpoints; +extern word32 g_mp_breakpoints[]; + +extern int g_num_bp_breakpoints; +extern word32 g_bp_breakpoints[]; + extern Page_info page_info_rd_wr[]; @@ -242,11 +248,40 @@ void moremem_init() { void fixup_brks() { word32 page; - word32 tmp, tmp2; Pg_info val; - int is_wr_only; - int i, num; + int i; + /* need to clear break bit from all pages first? */ + for (page = 0; page < 0xffff; ++page) { + val = GET_PAGE_INFO_RD(page); + val = (Pg_info)((ptrdiff_t)val &~ BANK_BREAK); + SET_PAGE_INFO_RD(page, val); + } + for (page = 0; page < 0xffff; ++page) { + val = GET_PAGE_INFO_WR(page); + val = (Pg_info)((ptrdiff_t)val &~ BANK_BREAK); + SET_PAGE_INFO_WR(page, val); + } + + + /* bp are read-only. mp are read/write */ + for (i = 0; i < g_num_bp_breakpoints; ++i) { + page = (g_bp_breakpoints[i] >> 8) & 0xffff; + val = GET_PAGE_INFO_RD(page); + val = (Pg_info)((ptrdiff_t)val | BANK_BREAK); + SET_PAGE_INFO_RD(page, val); + /* why IO_TMP? */ + } + + for (i = 0; i < g_num_mp_breakpoints; ++i) { + page = (g_mp_breakpoints[i] >> 8) & 0xffff; + val = GET_PAGE_INFO_WR(page); + val = (Pg_info)((ptrdiff_t)val | BANK_BREAK); + SET_PAGE_INFO_WR(page, val); + /* why IO_TMP? */ + } + +#if 0 num = g_num_breakpoints; for(i = 0; i < num; i++) { page = (g_breakpts[i] >> 8) & 0xffff; @@ -262,6 +297,8 @@ void fixup_brks() { tmp2 = tmp | BANK_IO_TMP | BANK_BREAK; SET_PAGE_INFO_WR(page, val - tmp + tmp2); } +#endif + } void fixup_hires_on() { diff --git a/src/options.c b/src/options.c index f58d199..ed737d1 100644 --- a/src/options.c +++ b/src/options.c @@ -67,6 +67,8 @@ extern int g_audio_enable; // defined in sound.c // Start in fullscreen mode extern int g_fullscreen; // defined in adb.c, because weird driver writing for x +extern int g_dbg_shell; + // Specify the joystick - SDL2 extern int g_joystick_number; // defined in joystick_driver.c extern int g_joystick_x_axis; // defined in joystick_driver.c @@ -383,6 +385,8 @@ int parse_cli_options(int argc, char **argv) { g_dbg_enable_port = strtol(argv[i+1], 0, 0); glogf("%s Using %d for debug port", parse_log_prefix, g_dbg_enable_port); i++; + } else if (!strcmp("-g", argv[i])) { + g_dbg_shell = 1; } else { if ((i == (argc - 1)) && (strncmp("-", argv[i], 1) != 0)) { final_arg = argv[i]; @@ -392,6 +396,7 @@ int parse_cli_options(int argc, char **argv) { } } } + return 0; } void help_exit() { From 6034573693754b9080c2365f82a390dd7c6180e3 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Mon, 28 Jan 2019 23:04:55 -0500 Subject: [PATCH 053/186] udpated cmakefile for new debug code. --- src/CMakeLists.txt | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0355e94..c54ef23 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -72,6 +72,11 @@ add_custom_command( WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) +add_custom_command( + OUTPUT debug_shell.c + COMMAND re2c -o ${CMAKE_CURRENT_BINARY_DIR}/debug_shell.c "${CMAKE_CURRENT_SOURCE_DIR}/debug_shell.re2c" + MAIN_DEPENDENCY debug_shell.re2c +) add_executable(to_pro to_pro.c) add_executable(partls partls.c) @@ -112,11 +117,14 @@ set(MACOSX_BUNDLE_GUI_IDENTIFIER com.dagenbrock.gsplus) # https://cmake.org/Wiki/CMake:Bundles_And_Frameworks # OS X properties. add_executable(GSplus WIN32 MACOSX_BUNDLE - adb.c clock.c config.c dis.c engine_c.c scc.c iwm.c + adb.c clock.c config.c engine_c.c scc.c iwm.c joystick_driver.c moremem.c paddles.c parallel.c printer.cpp sim65816.c smartport.c sound.c sound_driver.c video.c scc_socket_driver.c glog.c imagewriter.cpp scc_imagewriter.c scc_llap.c options.c + disasm.c + debug_shell.c + $<$:debug.c> $<$:${host_fst_code}> ${driver_code} From 619ea2109d0d49e62722bbe416600d648f4411fd Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Tue, 29 Jan 2019 00:21:24 -0500 Subject: [PATCH 054/186] remove dead code, update for new debugger code --- src/protos.h | 2 +- src/sim65816.c | 132 +++++++------------------------------------------ 2 files changed, 20 insertions(+), 114 deletions(-) diff --git a/src/protos.h b/src/protos.h index 23932ff..a4350f5 100644 --- a/src/protos.h +++ b/src/protos.h @@ -385,7 +385,7 @@ double remove_event_scc(int type); void show_all_events(void); void show_pmhz(void); void setup_zip_speeds(void); -void run_prog(void); +int run_prog(void); void add_irq(word32 irq_mask); void remove_irq(word32 irq_mask); void take_irq(int is_it_brk); diff --git a/src/sim65816.c b/src/sim65816.c index 16d2476..52a3304 100644 --- a/src/sim65816.c +++ b/src/sim65816.c @@ -319,6 +319,7 @@ void sim65816_initglobals() { g_mem_size_total = 256*1024; /* Total contiguous RAM from 0 */ } +#if 0 void show_pc_log() { FILE *pcfile; Pc_log *log_pc_ptr; @@ -412,7 +413,7 @@ void show_pc_log() { fclose(pcfile); } - +#endif #define TOOLBOX_LOG_LEN 64 @@ -482,30 +483,6 @@ void show_toolbox_log() { } } -#if 0 -/* get_memory_c is not used, get_memory_asm is, but this does what the */ -/* assembly language would do */ -word32 get_memory_c(word32 loc, int diff_cycles) { - byte *addr; - word32 result; - int index; - -#ifdef CHECK_BREAKPOINTS - check_breakpoints_c(loc); -#endif - - index = loc >> 8; - result = page_info[index].rd; - if(result & BANK_IO_BIT) { - return get_memory_io(loc, diff_cycles); - } - - addr = (byte *)((result & 0xffffff00) + (loc & 0xff)); - - return *addr; -} -#endif - word32 get_memory_io(word32 loc, double *cyc_ptr) { int tmp; @@ -554,71 +531,6 @@ word32 get_memory_io(word32 loc, double *cyc_ptr) { return 0; } -#if 0 -word32 get_memory16_pieces(word32 loc, int diff_cycles) { - return(get_memory_c(loc, diff_cycles) + - (get_memory_c(loc+1, diff_cycles) << 8)); -} - -word32 get_memory24(word32 loc, int diff_cycles) { - return(get_memory_c(loc, diff_cycles) + - (get_memory_c(loc+1, diff_cycles) << 8) + - (get_memory_c(loc+2, diff_cycles) << 16)); -} -#endif - -#if 0 -void set_memory(word32 loc, int val, int diff_cycles) { - byte *ptr; - word32 new_addr; - word32 tmp; - word32 or_val; - int or_pos; - int old_slow_val; - -#ifdef CHECK_BREAKPOINTS - check_breakpoints_c(loc); -#endif - - tmp = GET_PAGE_INFO_WR((loc>>8) & 0xffff); - if(tmp & BANK_IO) { - set_memory_io(loc, val, diff_cycles); - return; - } - - if((loc & 0xfef000) == 0xe0c000) { - printf("set_memory_special: non-io for addr %08x, %02x, %d\n", - loc, val, diff_cycles); - halt_printf("tmp: %08x\n", tmp); - } - - ptr = (byte *)(tmp & (~0xff)); - - new_addr = loc & 0xffff; - old_slow_val = val; - - if(tmp & BANK_SHADOW) { - old_slow_val = g_slow_memory_ptr[new_addr]; - } else if(tmp & BANK_SHADOW2) { - new_addr += 0x10000; - old_slow_val = g_slow_memory_ptr[new_addr]; - } - - if(old_slow_val != val) { - g_slow_memory_ptr[new_addr] = val; - or_pos = (new_addr >> SHIFT_PER_CHANGE) & 0x1f; - or_val = DEP1(1, or_pos, 0); - if((new_addr >> CHANGE_SHIFT) >= SLOW_MEM_CH_SIZE) { - printf("new_addr: %08x\n", new_addr); - exit(12); - } - slow_mem_changed[(new_addr & 0xffff) >> CHANGE_SHIFT] |= or_val; - } - - ptr[loc & 0xff] = val; - -} -#endif void set_memory_io(word32 loc, int val, double *cyc_ptr) { word32 tmp; @@ -666,25 +578,6 @@ void set_memory_io(word32 loc, int val, double *cyc_ptr) { } -#if 0 -void check_breakpoints_c(word32 loc) { - int index; - int count; - int i; - - index = (loc & (MAX_BP_INDEX-1)); - count = breakpoints[index].count; - if(count) { - for(i = 0; i < count; i++) { - if(loc == breakpoints[index].addrs[i]) { - halt_printf("Write hit breakpoint %d!\n", i); - } - } - } -} -#endif - - void show_regs_act(Engine_reg *eptr) { int tmp_acc, tmp_x, tmp_y, tmp_psw; int kpc; @@ -809,6 +702,7 @@ void check_engine_asm_defines() { CHECK(eptr, eptr->direct, ENGINE_REG_DIRECT, val1, val2); CHECK(eptr, eptr->psr, ENGINE_REG_PSR, val1, val2); CHECK(eptr, eptr->kpc, ENGINE_REG_KPC, val1, val2); + CHECK(eptr, eptr->flags, ENGINE_FLAGS, val1, val2); pcptr = &pclog; CHECK(pcptr, pcptr->dbank_kpc, LOG_PC_DBANK_KPC, val1, val2); @@ -1005,8 +899,6 @@ int gsplusmain(int argc, char **argv) { do_go_debug(); } else { do_go(); - /* If we get here, we hit a breakpoint, call debug intfc */ - do_debug_intfc(); } // OG Notify emulator is being closed, and cannot accept events anymore @@ -1473,7 +1365,7 @@ void setup_zip_speeds() { } } -void run_prog() { +int run_prog() { Fplus *fplus_ptr; Event *this_event; Event *db1; @@ -1583,7 +1475,7 @@ void run_prog() { engine.fcycles = prerun_fcycles; fcycles_stop = (g_event_start.next->dcycs - g_last_vbl_dcycs) + 0.001; - if(g_stepping) { + if(g_stepping || engine.flags) { fcycles_stop = prerun_fcycles; } g_fcycles_stop = fcycles_stop; @@ -1619,6 +1511,7 @@ void run_prog() { if(ret != 0) { g_engine_action++; handle_action(ret); + ret >>= 28; } if(halt_sim == HALT_EVENT) { @@ -1715,16 +1608,24 @@ void run_prog() { if(halt_sim != 0 && halt_sim != HALT_EVENT) { break; } + if (ret == RET_MP) break; + if (ret == RET_BP) break; + engine.flags &= ~(FLAG_IGNORE_BP | FLAG_IGNORE_MP); if(g_stepping) { + ret = 0; break; } } +#if 0 if(!g_testing) { printf("leaving run_prog, halt_sim:%d\n", halt_sim); } +#endif x_auto_repeat_on(0); + + return ret; } void add_irq(word32 irq_mask) { @@ -2328,6 +2229,7 @@ void init_reg() { engine.direct = 0; engine.psr = 0x134; engine.fplus_ptr = 0; + engine.flags = 0; } @@ -2375,6 +2277,10 @@ void handle_action(word32 ret) { case RET_STP: do_stp(); break; + case RET_BP: + case RET_MP: + /* handled elsewhere */ + break; default: halt_printf("Unknown special action: %08x!\n", ret); } From d47589374c6afdcc660ff49f7596ae6207f656cc Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Tue, 29 Jan 2019 19:18:08 -0500 Subject: [PATCH 055/186] bp/mp are now post-fix ops. --- src/CMakeLists.txt | 2 +- src/debug_shell.re2c | 183 +++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 178 insertions(+), 7 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c54ef23..b26ac50 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -74,7 +74,7 @@ add_custom_command( add_custom_command( OUTPUT debug_shell.c - COMMAND re2c -o ${CMAKE_CURRENT_BINARY_DIR}/debug_shell.c "${CMAKE_CURRENT_SOURCE_DIR}/debug_shell.re2c" + COMMAND re2c -T -W -o ${CMAKE_CURRENT_BINARY_DIR}/debug_shell.c "${CMAKE_CURRENT_SOURCE_DIR}/debug_shell.re2c" MAIN_DEPENDENCY debug_shell.re2c ) diff --git a/src/debug_shell.re2c b/src/debug_shell.re2c index 1e353f4..1b75d71 100644 --- a/src/debug_shell.re2c +++ b/src/debug_shell.re2c @@ -18,9 +18,6 @@ extern int g_config_control_panel; extern Engine_reg engine; -int g_num_breakpoints = 0; -word32 g_breakpts[MAX_BREAK_POINTS]; - int g_num_mp_breakpoints = 0; word32 g_mp_breakpoints[MAX_BREAK_POINTS]; @@ -35,6 +32,20 @@ word32 g_abort_value = 0; word32 g_abort_bytes = 0; +/* + * todo + * - tool break support + * - gs/os break support + * - p8 break support + * - nifty list data files + * - gsbug template files + * - r -> run to next rts/rtl + * - n -> step over jsl/jsr [ temporary breakpoints? ] + * - ! -> mini assembler + * - option to drop into debug shell on BRK command + */ + + word32 do_hexdump(word32 address, int lines) { static char hex[] = "0123456789abcdef"; @@ -339,10 +350,11 @@ static int addr_cmp(const void *a, const void *b) { - +#if 0 void show_bp() { int i; word32 addr; + printf("Breakpoints:\n"); for (i = 0; i < g_num_bp_breakpoints; ++i) { addr = g_bp_breakpoints[i]; printf("%02x/%04x\n", addr >> 16, addr & 0xffff); @@ -382,8 +394,118 @@ void delete_bp(word32 addr) { qsort(g_bp_breakpoints, g_num_bp_breakpoints, sizeof(word32), addr_cmp); fixup_brks(); +} +#endif + + +void show_bp(int type) { + int i; + word32 addr; + word32 *breakpoints; + int num_breakpoints; + + switch(type) { + case 'B': + breakpoints = g_bp_breakpoints; + num_breakpoints = g_num_bp_breakpoints; + break; + case 'M': + breakpoints = g_mp_breakpoints; + num_breakpoints = g_num_mp_breakpoints; + break; + default: + fputs("Invalid breakpoint type\n", stderr); + return; + } + + fputs("Breakpoints:\n", stdout); + for (i = 0; i < num_breakpoints; ++i) { + addr = breakpoints[i]; + printf("%02x/%04x\n", addr >> 16, addr & 0xffff); + } +} + +void set_bp(int type, word32 addr) { + int i; + + word32 *breakpoints; + int num_breakpoints; + + switch(type) { + case 'B': + breakpoints = g_bp_breakpoints; + num_breakpoints = g_num_bp_breakpoints; + break; + case 'M': + breakpoints = g_mp_breakpoints; + num_breakpoints = g_num_mp_breakpoints; + break; + default: + fputs("Invalid breakpoint type\n", stderr); + return; + } + + for (i = 0; i < num_breakpoints; ++i) { + if (breakpoints[i] == addr) break; + } + + if (i < num_breakpoints) return; /* already set */ + if (num_breakpoints == MAX_BREAK_POINTS) { + printf("Too many breakpoints.\n"); + return; + } + breakpoints[num_breakpoints++] = addr; + + switch(type) { + case 'B': g_num_bp_breakpoints = num_breakpoints; break; + case 'M': g_num_mp_breakpoints = num_breakpoints; break; + } + + qsort(breakpoints, num_breakpoints, sizeof(word32), addr_cmp); + fixup_brks(); +} + +void delete_bp(int type, word32 addr) { + int i; + + word32 *breakpoints; + int num_breakpoints; + + switch(type) { + case 'B': + breakpoints = g_bp_breakpoints; + num_breakpoints = g_num_bp_breakpoints; + break; + case 'M': + breakpoints = g_mp_breakpoints; + num_breakpoints = g_num_mp_breakpoints; + break; + default: + fputs("Invalid breakpoint type\n", stderr); + return; + } + + for (i = 0; i < num_breakpoints; ++i) { + if (breakpoints[i] == addr) break; + } + + switch(type) { + case 'B': g_num_bp_breakpoints = num_breakpoints; break; + case 'M': g_num_mp_breakpoints = num_breakpoints; break; + } + + if (i == num_breakpoints) return; /* not set */ + breakpoints[i] = 0; + breakpoints[i] = breakpoints[--num_breakpoints]; + + qsort(breakpoints, num_breakpoints, sizeof(word32), addr_cmp); + fixup_brks(); + } + + + static int do_bp_mp_common(const char *cp, word32 default_bank, word32 *breakpoints, int *num_breakpoints) { /* bp * bp [+-] address @@ -468,6 +590,29 @@ static int do_mp(const char *cp) { g_mp_breakpoints, &g_num_mp_breakpoints); } +static void do_help(void) { + + fputs( + " * Print registers\n" + " reg=value Assign register (a,x,y,etc)\n" + " reset Reset computer\n" + " quit exit debugger\n" + " s single step\n" + " g go\n" + " bp List PC breakpoints\n" + " mp List memory breakpoints\n" + "\n" + " [address];l List\n" + " [address];h Hexdump\n" + " [address];bp Set PC breakpoint\n" + " [address];bp- Remove PC breakpoint\n" + " [address];mp Set memory breakpoint\n" + " [address];mp- Remove memory breakpoint\n" + "\n" + "\n" + "Address = 12/3456, 123456, or 1234\n", + stdout); +} static word32 g_prev_address = 0; @@ -476,11 +621,16 @@ static int parse_command(const char *cp) { const char *YYCURSOR = cp; const char *YYMARKER = NULL; + const char *ptr = NULL; int addr = 0; int has_bank = 0; int has_addr = 0; + /*!stags:re2c format = "const char *@@;\n"; */ + + while (*cp == ' ') ++cp; + /*!re2c _ = (" " | "\x00"); @@ -499,13 +649,14 @@ static int parse_command(const char *cp) { "pc=" { return do_assign(YYCURSOR, REG_PC); } "mx=" { return do_assign(YYCURSOR, REG_MX); } - "bp" / _ { return do_bp(YYCURSOR); } - "mp" / _ { return do_mp(YYCURSOR); } + "bp" end { show_bp('B'); return 0; } + "mp" end { show_bp('M'); return 0; } "*" end { show_regs(); return 0; } ("q" | "quit" | "exit") end { return 'q'; } "reset" end { do_reset(); return 0; } + ("help" | "?") end { do_help(); return 0; } x{6} { @@ -577,6 +728,26 @@ next: return 1; } + ";bp" @ptr [+-]? end { + char plus = *ptr; + if (!has_addr && plus == 0) { show_bp('B'); return 0; } + if (!has_addr) return -1; + if (!has_bank) addr |= (engine.kpc & 0xff0000); + if (plus == '-') delete_bp('B', addr); + else set_bp('B', addr); + return 0; + } + + ";mp" @ptr [+-]? end { + char plus = *ptr; + if (!has_addr && plus == 0) { show_bp('M'); return 0; } + if (!has_addr) return -1; + if (!has_bank) addr |= (engine.kpc & 0xff0000); + if (plus == '-') delete_bp('M', addr); + else set_bp('M', addr); + return 0; + } + */ } From e62889901c567b9328b281b54d3226046f8b79af Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Tue, 29 Jan 2019 19:20:01 -0500 Subject: [PATCH 056/186] fix up tcp debug to work with new debugger --- src/debug.c | 21 +++++++++++++-------- src/protos.h | 6 +++--- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/debug.c b/src/debug.c index 5df885e..bb79a1c 100644 --- a/src/debug.c +++ b/src/debug.c @@ -25,13 +25,13 @@ // STEPPING/ENGINE STUFF extern Engine_reg engine; extern int g_config_control_panel; -extern int g_num_breakpoints; -extern word32 g_breakpts[MAX_BREAK_POINTS]; int g_dbg_enable_port = 0; int debug_pause = 0; int g_dbg_step = 0; int step_count = 0; +extern int g_stepping; + // emulator command stuff extern int g_limit_speed; extern int g_screenshot_requested; @@ -217,9 +217,11 @@ void debug_handle_event() { } else { g_dbg_step = -1; // first one just halts } + g_stepping = 1; break; case G_DBG_COMMAND_CONTINUE: //4 g_dbg_step = 0; + g_stepping = 0; step_count = 0; break; case G_DBG_COMMAND_GET_MEM: //6 @@ -232,7 +234,7 @@ void debug_handle_event() { exit(0); // HALT! break; case G_DBG_COMMAND_DEBUGGER: - do_debug_intfc(); + //do_debug_intfc(); break; case G_DBG_COMMAND_SET_CPU: event_set_cpu(&dbg_cmd_queue[0].cdata); @@ -303,13 +305,16 @@ void api_push_cpu() { void api_push_brk() { int i; + extern int g_num_bp_breakpoints; + extern word32 g_bp_breakpoints[]; + // build our json array of breakpoints tmp_buffer2_4k[0] = '\0'; // start with empty string char *str_ptr = tmp_buffer2_4k; - for(i = 0; i < g_num_breakpoints; i++) { + for(i = 0; i < g_num_bp_breakpoints; i++) { //printf("{\"bp:%02x: %06x\n", i, g_breakpts[i]); - str_ptr += sprintf(str_ptr, "{\"trig\":\"addr\",\"match\":\"%06X\"}", g_breakpts[i]); - if (i < g_num_breakpoints-1) { + str_ptr += sprintf(str_ptr, "{\"trig\":\"addr\",\"match\":\"%06X\"}", g_bp_breakpoints[i]); + if (i < g_num_bp_breakpoints-1) { str_ptr += sprintf(str_ptr, ","); } } @@ -603,7 +608,7 @@ void event_add_brk(char *str) { int addr = 0; sscanf(str, "%06X", &addr); addr = addr & 0xFFFFFF; // 24 bit KPC address for 65816 - set_bp(addr); + set_bp('B', addr); api_push_brk(); api_write_socket(); @@ -615,7 +620,7 @@ void event_del_brk(char *str) { int addr = 0; sscanf(str, "%06X", &addr); addr = addr & 0xFFFFFF; // 24 bit KPC address for 65816 - delete_bp(addr); + delete_bp('B', addr); api_push_brk(); api_write_socket(); diff --git a/src/protos.h b/src/protos.h index a4350f5..7d2a82e 100644 --- a/src/protos.h +++ b/src/protos.h @@ -172,9 +172,9 @@ void do_debug_intfc(void); word32 dis_get_memory_ptr(word32 addr); void show_one_toolset(FILE *toolfile, int toolnum, word32 addr); void show_toolset_tables(word32 a2bank, word32 addr); -void set_bp(word32 addr); -void show_bp(void); -void delete_bp(word32 addr); +void set_bp(int type, word32 addr); +void show_bp(int type); +void delete_bp(int type, word32 addr); void do_blank(void); void do_go(void); void do_go_debug(void); // socket debug ver From 82229498b4cc9db61f807edfa745025f3d044e6b Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Mon, 4 Feb 2019 20:41:11 -0500 Subject: [PATCH 057/186] adb keycodes. --- src/adb_keycodes.h | 189 ++++++++++++++++++++++++++++++++++++++ src/sdl2_driver.c | 224 ++++++++++++++++++++++++--------------------- 2 files changed, 310 insertions(+), 103 deletions(-) create mode 100644 src/adb_keycodes.h diff --git a/src/adb_keycodes.h b/src/adb_keycodes.h new file mode 100644 index 0000000..00dabd5 --- /dev/null +++ b/src/adb_keycodes.h @@ -0,0 +1,189 @@ +#ifndef adb_keycodes_h +#define adb_keycodes_h + +/* Taken from */ + +/* + * Summary: + * Virtual keycodes + * + * Discussion: + * These constants are the virtual keycodes defined originally in + * Inside Mac Volume V, pg. V-191. They identify physical keys on a + * keyboard. Those constants with "ANSI" in the name are labeled + * according to the key position on an ANSI-standard US keyboard. + * For example, kVK_ANSI_A indicates the virtual keycode for the key + * with the letter 'A' in the US keyboard layout. Other keyboard + * layouts may have the 'A' key label on a different physical key; + * in this case, pressing 'A' will generate a different virtual + * keycode. + */ +enum { + kVK_ANSI_A = 0x00, + kVK_ANSI_S = 0x01, + kVK_ANSI_D = 0x02, + kVK_ANSI_F = 0x03, + kVK_ANSI_H = 0x04, + kVK_ANSI_G = 0x05, + kVK_ANSI_Z = 0x06, + kVK_ANSI_X = 0x07, + kVK_ANSI_C = 0x08, + kVK_ANSI_V = 0x09, + kVK_ANSI_B = 0x0B, + kVK_ANSI_Q = 0x0C, + kVK_ANSI_W = 0x0D, + kVK_ANSI_E = 0x0E, + kVK_ANSI_R = 0x0F, + kVK_ANSI_Y = 0x10, + kVK_ANSI_T = 0x11, + kVK_ANSI_1 = 0x12, + kVK_ANSI_2 = 0x13, + kVK_ANSI_3 = 0x14, + kVK_ANSI_4 = 0x15, + kVK_ANSI_6 = 0x16, + kVK_ANSI_5 = 0x17, + kVK_ANSI_Equal = 0x18, + kVK_ANSI_9 = 0x19, + kVK_ANSI_7 = 0x1A, + kVK_ANSI_Minus = 0x1B, + kVK_ANSI_8 = 0x1C, + kVK_ANSI_0 = 0x1D, + kVK_ANSI_RightBracket = 0x1E, + kVK_ANSI_O = 0x1F, + kVK_ANSI_U = 0x20, + kVK_ANSI_LeftBracket = 0x21, + kVK_ANSI_I = 0x22, + kVK_ANSI_P = 0x23, + kVK_ANSI_L = 0x25, + kVK_ANSI_J = 0x26, + kVK_ANSI_Quote = 0x27, + kVK_ANSI_K = 0x28, + kVK_ANSI_Semicolon = 0x29, + kVK_ANSI_Backslash = 0x2A, + kVK_ANSI_Comma = 0x2B, + kVK_ANSI_Slash = 0x2C, + kVK_ANSI_N = 0x2D, + kVK_ANSI_M = 0x2E, + kVK_ANSI_Period = 0x2F, + kVK_ANSI_Grave = 0x32, + kVK_ANSI_KeypadDecimal = 0x41, + kVK_ANSI_KeypadMultiply = 0x43, + kVK_ANSI_KeypadPlus = 0x45, + kVK_ANSI_KeypadClear = 0x47, + kVK_ANSI_KeypadDivide = 0x4B, + kVK_ANSI_KeypadEnter = 0x4C, + kVK_ANSI_KeypadMinus = 0x4E, + kVK_ANSI_KeypadEquals = 0x51, + kVK_ANSI_Keypad0 = 0x52, + kVK_ANSI_Keypad1 = 0x53, + kVK_ANSI_Keypad2 = 0x54, + kVK_ANSI_Keypad3 = 0x55, + kVK_ANSI_Keypad4 = 0x56, + kVK_ANSI_Keypad5 = 0x57, + kVK_ANSI_Keypad6 = 0x58, + kVK_ANSI_Keypad7 = 0x59, + kVK_ANSI_Keypad8 = 0x5B, + kVK_ANSI_Keypad9 = 0x5C +}; + +/* keycodes for keys that are independent of keyboard layout*/ +enum { + kVK_Return = 0x24, + kVK_Tab = 0x30, + kVK_Space = 0x31, + kVK_Delete = 0x33, + kVK_Escape = 0x35, + kVK_Control = 0x36, + kVK_Command = 0x37, + kVK_Shift = 0x38, + kVK_CapsLock = 0x39, + kVK_Option = 0x3A, + kVK_LeftArrow = 0x3B, + kVK_RightArrow = 0x3C, + kVK_DownArrow = 0x3D, + kVK_UpArrow = 0x3E, + kVK_Function = 0x3F, + kVK_F17 = 0x40, + kVK_VolumeUp = 0x48, + kVK_VolumeDown = 0x49, + kVK_Mute = 0x4A, + kVK_F18 = 0x4F, + kVK_F19 = 0x50, + kVK_F20 = 0x5A, + kVK_F5 = 0x60, + kVK_F6 = 0x61, + kVK_F7 = 0x62, + kVK_F3 = 0x63, + kVK_F8 = 0x64, + kVK_F9 = 0x65, + kVK_F11 = 0x67, + kVK_F13 = 0x69, + kVK_F16 = 0x6A, + kVK_F14 = 0x6B, + kVK_F10 = 0x6D, + kVK_F12 = 0x6F, + kVK_F15 = 0x71, + kVK_Help = 0x72, + kVK_Home = 0x73, + kVK_PageUp = 0x74, + kVK_ForwardDelete = 0x75, + kVK_F4 = 0x76, + kVK_End = 0x77, + kVK_F2 = 0x78, + kVK_PageDown = 0x79, + kVK_F1 = 0x7A, + + + + kVK_Reset = 0x7F, /* actually 7F7F */ + + kVK_Insert = kVK_Help, + +#if 0 + kVK_RightCommand = 0x37, + kVK_RightShift = 0x7B, + kVK_RightOption = 0x7C, + kVK_RightControl = 0x7D, +#else + kVK_RightCommand = kVK_Command, + kVK_RightShift = kVK_Shift, + kVK_RightOption = kVK_Option, + kVK_RightControl = kVK_Control, +#endif + +#if 0 +/* Mac OS used a KMAP resource to remap ADB codes: + * Control 36 -> 3B + * Left Arrow 3B -> 7B + * Right Arrow 3C -> 7C + * Down Arrow 3D -> 7D + * Up Arrow 3E -> 7E + * Right Shift 7B* -> 3C + * Right Option 7C* -> 3D + * Right Control 7D* -> 3E + * + * Right shift/option/control only generate unique codes + * when device handler ID in keyboard register 3 has a + * value of $3 (as opposed to $2). + * + * See Guide to the Macintosh Family Hardware, ch 8, page 306-310 + * + * KMAP also maps $7e to $36 ... ? + * + */ + + kVK_RightCommand = 0x36, + kVK_RightShift = 0x3C, + kVK_RightOption = 0x3D, + kVK_RightControl = 0x3E, + + kVK_LeftArrow = 0x7B, + kVK_RightArrow = 0x7C, + kVK_DownArrow = 0x7D, + kVK_UpArrow = 0x7E, + +#endif + +}; + +#endif diff --git a/src/sdl2_driver.c b/src/sdl2_driver.c index 63a2614..e56f6f0 100644 --- a/src/sdl2_driver.c +++ b/src/sdl2_driver.c @@ -31,6 +31,8 @@ unsigned int lastTime = 0, currentTime, frames; #define ControlMask 4 #define LockMask 2 +#include "adb_keycodes.h" + int g_use_shmem = 0; int g_num_check_input_calls = 0; @@ -95,112 +97,128 @@ void handle_sdl_mouse_event(SDL_Event event); int g_num_a2_keycodes = 0; int a2_key_to_sdlkeycode[][3] = { - { 0x35, SDLK_ESCAPE, 0 }, - { 0x7a, SDLK_F1, 0 }, - { 0x78, SDLK_F2, 0 }, - { 0x63, SDLK_F3, 0 }, - { 0x76, SDLK_F4, 0 }, - { 0x60, SDLK_F5, 0 }, - { 0x61, SDLK_F6, 0 }, - { 0x62, SDLK_F7, 0 }, - { 0x64, SDLK_F8, 0 }, - { 0x65, SDLK_F9, 0 }, - { 0x6d, SDLK_F10, 0 }, - { 0x67, SDLK_F11, 0 }, - { 0x6f, SDLK_F12, 0 }, - { 0x69, SDLK_F13, 0 }, - { 0x6b, SDLK_F14, 0 }, - { 0x71, SDLK_F15, 0 }, - { 0x7f, SDLK_PAUSE, 0 }, - { 0x32, SDLK_BACKQUOTE, '~' }, /* Key number 18? */ - { 0x12, SDLK_1, '!' }, - { 0x13, SDLK_2, '@' }, - { 0x14, SDLK_3, '#' }, - { 0x15, SDLK_4, '$' }, - { 0x17, SDLK_5, '%' }, - { 0x16, SDLK_6, '^' }, - { 0x1a, SDLK_7, '&' }, - { 0x1c, SDLK_8, '*' }, - { 0x19, SDLK_9, '(' }, - { 0x1d, SDLK_0, ')' }, - { 0x1b, SDLK_MINUS, SDLK_UNDERSCORE }, - { 0x18, SDLK_EQUALS, SDLK_PLUS }, - { 0x33, SDLK_BACKSPACE, 0 }, - { 0x72, SDLK_INSERT, 0 }, /* Help? XK_Help */ - { 0x73, SDLK_HOME, 0 }, - { 0x74, SDLK_PAGEUP, 0 }, - { 0x47, SDLK_NUMLOCKCLEAR, 0 }, /* Clear, XK_Clear */ - { 0x51, SDLK_KP_EQUALS, 0 }, /* Note XK_Home alias! XK_Home */ - { 0x4b, SDLK_KP_DIVIDE, 0 }, - { 0x43, SDLK_KP_MULTIPLY, 0 }, - { 0x30, SDLK_TAB, 0 }, - { 0x0c, SDLK_q, 'Q' }, - { 0x0d, SDLK_w, 'W' }, - { 0x0e, SDLK_e, 'E' }, - { 0x0f, SDLK_r, 'R' }, - { 0x11, SDLK_t, 'T' }, - { 0x10, SDLK_y, 'Y' }, - { 0x20, SDLK_u, 'U' }, - { 0x22, SDLK_i, 'I' }, - { 0x1f, SDLK_o, 'O' }, - { 0x23, SDLK_p, 'P' }, - { 0x21, SDLK_LEFTBRACKET, '{' }, - { 0x1e, SDLK_RIGHTBRACKET, '}' }, - { 0x2a, SDLK_BACKSLASH, '|' }, /* backslash, bar */ - { 0x75, SDLK_DELETE, 0 }, - { 0x77, SDLK_END, 0 }, - { 0x79, SDLK_PAGEDOWN, 0 }, - { 0x59, SDLK_KP_7, 0 }, - { 0x5b, SDLK_KP_8, 0 }, - { 0x5c, SDLK_KP_9, 0 }, - { 0x4e, SDLK_KP_MINUS, 0 }, - { 0x39, SDLK_CAPSLOCK, 0 }, - { 0x00, SDLK_a, 'A' }, - { 0x01, SDLK_s, 'S' }, - { 0x02, SDLK_d, 'D' }, - { 0x03, SDLK_f, 'F' }, - { 0x05, SDLK_g, 'G' }, - { 0x04, SDLK_h, 'H' }, - { 0x26, SDLK_j, 'J' }, - { 0x28, SDLK_k, 'K' }, - { 0x25, SDLK_l, 'L' }, - { 0x29, SDLK_SEMICOLON, SDLK_COLON }, - { 0x27, SDLK_QUOTE, SDLK_QUOTEDBL }, - { 0x24, SDLK_RETURN, 0 }, - { 0x56, SDLK_KP_4, 0}, - { 0x57, SDLK_KP_5, 0 }, - { 0x58, SDLK_KP_6, 0 }, - { 0x45, SDLK_KP_PLUS, 0 }, - { 0x38, SDLK_LSHIFT, SDLK_RSHIFT }, - { 0x06, SDLK_z, 'Z' }, - { 0x07, SDLK_x, 'X' }, - { 0x08, SDLK_c, 'C' }, - { 0x09, SDLK_v, 'V' }, - { 0x0b, SDLK_b, 'B' }, - { 0x2d, SDLK_n, 'N' }, - { 0x2e, SDLK_m, 'M' }, - { 0x2b, SDLK_COMMA, SDLK_LESS }, - { 0x2f, SDLK_PERIOD, SDLK_GREATER }, - { 0x2c, SDLK_SLASH, SDLK_QUESTION }, - { 0x3e, SDLK_UP, 0 }, - { 0x53, SDLK_KP_1, 0 }, - { 0x54, SDLK_KP_2, 0 }, - { 0x55, SDLK_KP_3, 0 }, - { 0x36, SDLK_RCTRL, SDLK_LCTRL }, + { kVK_Escape, SDLK_ESCAPE, 0 }, + { kVK_F1, SDLK_F1, 0 }, + { kVK_F2, SDLK_F2, 0 }, + { kVK_F3, SDLK_F3, 0 }, + { kVK_F4, SDLK_F4, 0 }, + { kVK_F5, SDLK_F5, 0 }, + { kVK_F6, SDLK_F6, 0 }, + { kVK_F7, SDLK_F7, 0 }, + { kVK_F8, SDLK_F8, 0 }, + { kVK_F9, SDLK_F9, 0 }, + { kVK_F10, SDLK_F10, 0 }, + { kVK_F11, SDLK_F11, 0 }, + { kVK_F12, SDLK_F12, 0 }, + { kVK_F13, SDLK_F13, 0 }, + { kVK_F14, SDLK_F14, 0 }, + { kVK_F15, SDLK_F15, 0 }, + { kVK_F16, SDLK_F16, 0 }, + { kVK_F17, SDLK_F17, 0 }, + { kVK_F18, SDLK_F18, 0 }, + { kVK_F19, SDLK_F19, 0 }, + { kVK_F20, SDLK_F20, 0 }, + { kVK_Reset, SDLK_PAUSE, 0 }, + { kVK_ANSI_Grave, SDLK_BACKQUOTE, '~' }, + { kVK_ANSI_1, SDLK_1, '!' }, + { kVK_ANSI_2, SDLK_2, '@' }, + { kVK_ANSI_3, SDLK_3, '#' }, + { kVK_ANSI_4, SDLK_4, '$' }, + { kVK_ANSI_5, SDLK_5, '%' }, + { kVK_ANSI_6, SDLK_6, '^' }, + { kVK_ANSI_7, SDLK_7, '&' }, + { kVK_ANSI_8, SDLK_8, '*' }, + { kVK_ANSI_9, SDLK_9, '(' }, + { kVK_ANSI_0, SDLK_0, ')' }, + { kVK_ANSI_Minus, SDLK_MINUS, SDLK_UNDERSCORE }, + { kVK_ANSI_Equal, SDLK_EQUALS, SDLK_PLUS }, + { kVK_Delete, SDLK_BACKSPACE, 0 }, + { kVK_Help, SDLK_INSERT, 0 }, /* Help? XK_Help */ + { kVK_Home, SDLK_HOME, 0 }, + { kVK_PageUp, SDLK_PAGEUP, 0 }, + { kVK_Tab, SDLK_TAB, 0 }, + { kVK_ANSI_Q, SDLK_q, 'Q' }, + { kVK_ANSI_W, SDLK_w, 'W' }, + { kVK_ANSI_E, SDLK_e, 'E' }, + { kVK_ANSI_R, SDLK_r, 'R' }, + { kVK_ANSI_T, SDLK_t, 'T' }, + { kVK_ANSI_Y, SDLK_y, 'Y' }, + { kVK_ANSI_U, SDLK_u, 'U' }, + { kVK_ANSI_I, SDLK_i, 'I' }, + { kVK_ANSI_O, SDLK_o, 'O' }, + { kVK_ANSI_P, SDLK_p, 'P' }, + { kVK_ANSI_LeftBracket, SDLK_LEFTBRACKET, '{' }, + { kVK_ANSI_RightBracket, SDLK_RIGHTBRACKET, '}' }, + { kVK_ANSI_Backslash, SDLK_BACKSLASH, '|' }, /* backslash, bar */ + { kVK_ForwardDelete, SDLK_DELETE, 0 }, + { kVK_End, SDLK_END, 0 }, + { kVK_PageDown, SDLK_PAGEDOWN, 0 }, + { kVK_ANSI_A, SDLK_a, 'A' }, + { kVK_ANSI_S, SDLK_s, 'S' }, + { kVK_ANSI_D, SDLK_d, 'D' }, + { kVK_ANSI_F, SDLK_f, 'F' }, + { kVK_ANSI_G, SDLK_g, 'G' }, + { kVK_ANSI_H, SDLK_h, 'H' }, + { kVK_ANSI_J, SDLK_j, 'J' }, + { kVK_ANSI_K, SDLK_k, 'K' }, + { kVK_ANSI_L, SDLK_l, 'L' }, + { kVK_ANSI_Semicolon, SDLK_SEMICOLON, SDLK_COLON }, + { kVK_ANSI_Quote, SDLK_QUOTE, SDLK_QUOTEDBL }, + { kVK_Return, SDLK_RETURN, 0 }, + { kVK_Shift, SDLK_LSHIFT, 0 }, + { kVK_RightShift, SDLK_RSHIFT, 0 }, + { kVK_ANSI_Z, SDLK_z, 'Z' }, + { kVK_ANSI_X, SDLK_x, 'X' }, + { kVK_ANSI_C, SDLK_c, 'C' }, + { kVK_ANSI_V, SDLK_v, 'V' }, + { kVK_ANSI_B, SDLK_b, 'B' }, + { kVK_ANSI_N, SDLK_n, 'N' }, + { kVK_ANSI_M, SDLK_m, 'M' }, + { kVK_ANSI_Comma, SDLK_COMMA, SDLK_LESS }, + { kVK_ANSI_Period, SDLK_PERIOD, SDLK_GREATER }, + { kVK_ANSI_Slash, SDLK_SLASH, SDLK_QUESTION }, + + { kVK_CapsLock, SDLK_CAPSLOCK, 0 }, + + { kVK_Control, SDLK_LCTRL, 0 }, + { kVK_RightControl, SDLK_RCTRL, 0 }, #if defined(__APPLE__) - { 0x3a, SDLK_LALT, SDLK_RALT }, /* Option */ - { 0x37, SDLK_LGUI, SDLK_RGUI }, /* Command */ + { kVK_Option, SDLK_LALT, 0 }, /* Option */ + { kVK_RightOption, SDLK_RALT, 0 }, /* Option */ + { kVK_Command, SDLK_LGUI, 0 }, /* Command */ + { kVK_RightCommand, SDLK_RGUI, 0 }, /* Command */ #else - { 0x3a, SDLK_LGUI, SDLK_RGUI }, /* Command */ - { 0x37, SDLK_LALT, SDLK_RALT }, /* Option */ + { kVK_Option, SDLK_LGUI, 0 }, /* Command */ + { kVK_RightOption, SDLK_RGUI, 0 }, /* Command */ + { kVK_Command, SDLK_LALT, 0 }, /* Option */ + { kVK_RightCommand, SDLK_RALT, 0 }, /* Option */ #endif - { 0x31, SDLK_SPACE, 0 }, - { 0x3b, SDLK_LEFT, 0 }, - { 0x3d, SDLK_DOWN, 0 }, - { 0x3c, SDLK_RIGHT, 0 }, - { 0x52, SDLK_KP_0, 0 }, - { 0x41, SDLK_KP_PERIOD, 0 }, - { 0x4c, SDLK_KP_ENTER, 0 }, + { kVK_Space, SDLK_SPACE, 0 }, + { kVK_LeftArrow, SDLK_LEFT, 0 }, + { kVK_DownArrow, SDLK_DOWN, 0 }, + { kVK_RightArrow, SDLK_RIGHT, 0 }, + { kVK_UpArrow, SDLK_UP, 0 }, + + { kVK_ANSI_Keypad0, SDLK_KP_0, 0 }, + { kVK_ANSI_Keypad1, SDLK_KP_1, 0 }, + { kVK_ANSI_Keypad2, SDLK_KP_2, 0 }, + { kVK_ANSI_Keypad3, SDLK_KP_3, 0 }, + { kVK_ANSI_Keypad4, SDLK_KP_4, 0 }, + { kVK_ANSI_Keypad5, SDLK_KP_5, 0 }, + { kVK_ANSI_Keypad6, SDLK_KP_6, 0 }, + { kVK_ANSI_Keypad7, SDLK_KP_7, 0 }, + { kVK_ANSI_Keypad8, SDLK_KP_8, 0 }, + { kVK_ANSI_Keypad9, SDLK_KP_9, 0 }, + + { kVK_ANSI_KeypadMinus, SDLK_KP_MINUS, 0 }, + { kVK_ANSI_KeypadPlus, SDLK_KP_PLUS, 0 }, + { kVK_ANSI_KeypadEquals, SDLK_KP_EQUALS, 0 }, /* Note XK_Home alias! XK_Home */ + { kVK_ANSI_KeypadDivide, SDLK_KP_DIVIDE, 0 }, + { kVK_ANSI_KeypadMultiply,SDLK_KP_MULTIPLY, 0 }, + { kVK_ANSI_KeypadDecimal, SDLK_KP_PERIOD, 0 }, + { kVK_ANSI_KeypadEnter, SDLK_KP_ENTER, 0 }, + { kVK_ANSI_KeypadClear, SDLK_NUMLOCKCLEAR, 0 }, /* Clear, XK_Clear */ + { -1, -1, -1 } }; From 35993914f9024692529374dd37ac78c8744b234b Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Mon, 4 Feb 2019 20:43:12 -0500 Subject: [PATCH 058/186] update adb keycodes with F15-F20, use proper value for F keys, eliminate Mac OS arrow key mapping. --- src/adb.c | 63 +++++++++++++++++++++++++++++++++++-------------------- src/adb.h | 60 +++++++++++++++++++++++++--------------------------- 2 files changed, 69 insertions(+), 54 deletions(-) diff --git a/src/adb.c b/src/adb.c index 0bce5f2..bd1c28c 100644 --- a/src/adb.c +++ b/src/adb.c @@ -10,6 +10,9 @@ #include "adb.h" #include "glog.h" +#include "adb_keycodes.h" + + int g_fullscreen = 0; int g_grabmouse = 0; @@ -1655,34 +1658,48 @@ void adb_physical_key_update(int a2code, int is_up) { /* Now check for special keys (function keys, etc) */ ascii_and_type = a2_key_to_ascii[a2code][1]; special = 0; - if((ascii_and_type & 0xf000) == 0x8000) { - /* special function key */ + + /* special FKeys? */ + if (ascii_and_type & 0x1000) { special = ascii_and_type & 0xff; - switch(special) { - case 0x01: /* F1 - remap to cmd */ - a2code = 0x37; + switch (special) { + case kVK_F1: + /* F1 - remap to cmd */ + a2code = kVK_Command; special = 0; break; - case 0x02: /* F2 - remap to option */ - a2code = 0x3a; + case kVK_F2: + /* F2 - remap to option */ + a2code = kVK_Option; special = 0; break; - case 0x0c: /* F12 - remap to reset */ - a2code = 0x7f; + case kVK_F12: + /* F12 - remap to reset */ + a2code = kVK_Reset; special = 0; break; + case kVK_F3: + case kVK_F4: + case kVK_F5: + case kVK_F6: + case kVK_F7: + case kVK_F8: + case kVK_F9: + case kVK_F10: + case kVK_F11: + break; default: - break; + special = 0; } } /* CUA clipboard paste - for those that remember ctrl-insert/shift-insert */ - if(is_up == 0 && a2code == 0x72 && SHIFT_DOWN) { + if(is_up == 0 && a2code == kVK_Insert && SHIFT_DOWN) { clipboard_paste(); } /* Only process reset requests here */ - if(is_up == 0 && a2code == 0x7f && CTRL_DOWN) { + if(is_up == 0 && a2code == kVK_Reset && CTRL_DOWN) { /* Reset pressed! */ glogf("Reset pressed since CTRL_DOWN: %d", CTRL_DOWN); do_reset(); @@ -1691,10 +1708,10 @@ void adb_physical_key_update(int a2code, int is_up) { if(special && !is_up) { switch(special) { - case 0x03: /* F3 - screenshot */ + case kVK_F3: /* F3 - screenshot */ g_screenshot_requested = 1; break; - case 0x04: /* F4 - emulator config panel */ + case kVK_F4: /* F4 - emulator config panel */ if (CMD_DOWN) { glog("Alt-F4 Quit!"); iwm_shut(); @@ -1705,7 +1722,7 @@ void adb_physical_key_update(int a2code, int is_up) { cfg_toggle_config_panel(); } break; - case 0x05: /* F5 - emulator clipboard paste */ + case kVK_F5: /* F5 - emulator clipboard paste */ if (SHIFT_DOWN) { g_grabmouse = !g_grabmouse; #ifdef HAVE_SDL @@ -1717,18 +1734,18 @@ void adb_physical_key_update(int a2code, int is_up) { clipboard_paste(); } break; - case 0x06: /* F6 - emulator speed */ + case kVK_F6: /* F6 - emulator speed */ if(SHIFT_DOWN) { halt2_printf("Shift-F6 pressed\n"); } else { adb_increment_speed(); } break; - case 0x07: /* F7 - fast disk emul */ + case kVK_F7: /* F7 - fast disk emul */ g_fast_disk_emul = !g_fast_disk_emul; glogf("g_fast_disk_emul is now %d", g_fast_disk_emul); break; - case 0x08: /* F8 - warp pointer */ + case kVK_F8: /* F8 - warp pointer */ g_warp_pointer = !g_warp_pointer; if(g_hide_pointer != g_warp_pointer) { g_hide_pointer = g_warp_pointer; @@ -1736,7 +1753,7 @@ void adb_physical_key_update(int a2code, int is_up) { } glogf("g_warp_pointer is now %d", g_warp_pointer); break; - case 0x09: /* F9 - swap paddles */ + case kVK_F9: /* F9 - swap paddles */ if(SHIFT_DOWN) { g_swap_paddles = !g_swap_paddles; glogf("Swap paddles is now: %d", g_swap_paddles); @@ -1745,7 +1762,7 @@ void adb_physical_key_update(int a2code, int is_up) { glogf("Invert paddles is now: %d", g_invert_paddles); } break; - case 0x0a: /* F10 - change a2vid paletter */ + case kVK_F10: /* F10 - change a2vid paletter */ if (SHIFT_DOWN) { #ifdef TOGGLE_STATUS extern void x_toggle_status_lines(); @@ -1758,7 +1775,7 @@ void adb_physical_key_update(int a2code, int is_up) { change_a2vid_palette((g_a2vid_palette + 1) & 0xf); } break; - case 0x0b: /* F11 - full screen */ + case kVK_F11: /* F11 - full screen */ g_fullscreen = !g_fullscreen; x_full_screen(g_fullscreen); break; @@ -1780,10 +1797,10 @@ void adb_physical_key_update(int a2code, int is_up) { /* keypress pass on further, except for cmd/opt */ if(ascii == 0x30) { /* remap '0' to cmd */ - a2code = 0x37; + a2code = kVK_Command; } else if(ascii == 0x2e || ascii == 0x2c) { /* remap '.' and ',' to option */ - a2code = 0x3a; + a2code = kVK_Option; } else { /* Just ignore it in this mode */ return; diff --git a/src/adb.h b/src/adb.h index d518157..41005ea 100644 --- a/src/adb.h +++ b/src/adb.h @@ -79,9 +79,9 @@ const int a2_key_to_ascii[][4] = { { 0x3c, 0x15, 0x15, -1 }, /* right */ { 0x3d, 0x0a, 0x0a, -1 }, /* down */ { 0x3e, 0x0b, 0x0b, -1 }, /* up arrow */ - { 0x3f, -1, -1, -1 }, + { 0x3f, 0x103f, 0x103f, -1 }, /* function key */ - { 0x40, -1, -1, -1 }, + { 0x40, 0x1040, 0x1040, -1 }, /* F17 */ { 0x41, 0x102e, 0x102e, -1 }, /* keypad . */ { 0x42, -1, -1, -1 }, { 0x43, 0x102a, 0x102a, -1 }, /* keypad * */ @@ -90,16 +90,16 @@ const int a2_key_to_ascii[][4] = { { 0x46, -1, -1, -1 }, { 0x47, 0x1018, 0x1018, -1 }, /* keypad Clear */ - { 0x48, -1, -1, -1 }, - { 0x49, -1, -1, -1 }, - { 0x4a, -1, -1, -1 }, + { 0x48, 0x1048, 0x1048, -1 }, /* volume up */ + { 0x49, 0x1049, 0x1049, -1 }, /* volume down */ + { 0x4a, 0x104a, 0x104a, -1 }, /* mute */ { 0x4b, 0x102f, 0x102f, -1 }, /* keypad / */ { 0x4c, 0x100d, 0x100d, -1 }, /* keypad enter */ { 0x4d, -1, -1, -1 }, { 0x4e, 0x102d, 0x102d, -1 }, /* keypad - */ - { 0x4f, -1, -1, -1 }, + { 0x4f, 0x104f, 0x104f, -1 }, /* F18 */ - { 0x50, -1, -1, -1 }, + { 0x50, 0x1050, 0x1050, -1 }, /* F19 */ { 0x51, 0x103d, 0x103d, -1 }, /* keypad = */ { 0x52, 0x1030, 0x1030, -1 }, /* keypad 0 */ { 0x53, 0x1031, 0x1031, -1 }, /* keypad 1 */ @@ -110,48 +110,46 @@ const int a2_key_to_ascii[][4] = { { 0x58, 0x1036, 0x1036, -1 }, /* keypad 6 */ { 0x59, 0x1037, 0x1037, -1 }, /* keypad 7 */ - { 0x5a, 'a', 'A', 0x01 }, /* probably not necessary */ + { 0x5a, 0x105a, 0x105a, -1 }, /* F20 */ { 0x5b, 0x1038, 0x1038, -1 }, /* keypad 8 */ { 0x5c, 0x1039, 0x1039, -1 }, /* keypad 9 */ { 0x5d, -1, -1, -1 }, { 0x5e, -1, -1, -1 }, { 0x5f, -1, -1, -1 }, - { 0x60, 0x8005, 0x1060, -1 }, /* F5 */ - { 0x61, 0x8006, 0x1061, -1 }, /* F6 */ - { 0x62, 0x8007, 0x1062, -1 }, /* F7 */ - { 0x63, 0x8003, 0x1063, -1 }, /* F3 */ - { 0x64, 0x8008, 0x1064, -1 }, /* F8 */ - { 0x65, 0x8009, 0x1065, -1 }, /* F9 */ + { 0x60, 0x1060, 0x1060, -1 }, /* F5 */ + { 0x61, 0x1061, 0x1061, -1 }, /* F6 */ + { 0x62, 0x1062, 0x1062, -1 }, /* F7 */ + { 0x63, 0x1063, 0x1063, -1 }, /* F3 */ + { 0x64, 0x1064, 0x1064, -1 }, /* F8 */ + { 0x65, 0x1065, 0x1065, -1 }, /* F9 */ { 0x66, -1, -1, -1 }, - { 0x67, 0x800b, 0x1067, -1 }, /* F11 */ + { 0x67, 0x1067, 0x1067, -1 }, /* F11 */ { 0x68, -1, -1, -1 }, -// { 0x69, 0x800d, 0x1069, -1 }, /* F13 */ -// OG remap F13 to reset - { 0x69, 0x800c, 0x1069, -1 }, /* F13 */ - { 0x6a, -1, -1, -1 }, - { 0x6b, 0x800e, 0x106b, -1 }, /* F14 */ + { 0x69, 0x1069, 0x1069, -1 }, /* F13 */ + { 0x6a, 0x106a, 0x106a, -1 }, /* F16 */ + { 0x6b, 0x106b, 0x106b, -1 }, /* F14 */ { 0x6c, -1, -1, -1 }, - { 0x6d, 0x800a, 0x106d, -1 }, /* F10 */ - { 0x6e, 0x4000, 0x4000, -1 }, /* windows key alias to option */ - { 0x6f, 0x800c, 0x106f, -1 }, /* F12 */ + { 0x6d, 0x106d, 0x106d, -1 }, /* F10 */ + { 0x6e, -1, -1, -1 }, + { 0x6f, 0x106f, 0x106f, -1 }, /* F12 */ { 0x70, -1, -1, -1 }, - { 0x71, 0x800f, 0x1071, -1 }, /* F15 */ + { 0x71, 0x1071, 0x1071, -1 }, /* F15 */ { 0x72, 0x1072, 0x1072, -1 }, /* Help, insert */ { 0x73, 0x1073, 0x1073, -1 }, /* Home */ { 0x74, 0x1074, 0x1074, -1 }, /* Page up */ { 0x75, 0x1075, 0x1075, -1 }, /* keypad delete */ - { 0x76, 0x8004, 0x1076, -1 }, /* F4 */ + { 0x76, 0x1076, 0x1076, -1 }, /* F4 */ { 0x77, 0x1077, 0x1077, -1 }, /* keypad end */ - { 0x78, 0x8002, 0x1078, -1 }, /* F2 */ + { 0x78, 0x1078, 0x1078, -1 }, /* F2 */ { 0x79, 0x1079, 0x1079, -1 }, /* keypad page down */ - { 0x7a, 0x8001, 0x107a, -1 }, /* F1 */ - { 0x7b, 0x08, 0x08, -1 }, /* left */ /* remapped to 0x3b */ - { 0x7c, 0x15, 0x15, -1 }, /* right */ /* remapped to 0x3c */ - { 0x7d, 0x0a, 0x0a, -1 }, /* down */ /* remapped to 0x3d */ - { 0x7e, 0x0b, 0x0b, -1 }, /* up arrow */ /* remapped to 0x3e */ + { 0x7a, 0x107a, 0x107a, -1 }, /* F1 */ + { 0x7b, 0x0100, 0x0100, -1 }, /* right shift */ + { 0x7c, 0x4000, 0x4000, -1 }, /* right option */ + { 0x7d, 0x0200, 0x0200, -1 }, /* right control */ + { 0x7e, -1, -1, -1 }, { 0x7f, -1, -1, -1 }, /* Reset */ }; From 2fb1249c961730bd1af8d9605cc986fa782234be Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Mon, 4 Feb 2019 23:45:39 -0500 Subject: [PATCH 059/186] clean up windows key mapping to use adb names codes. also fixes a few mistakes with keypad values. --- src/win_generic.c | 4 +- src/win_keymap.h | 221 ++++++++++++++++++++++++---------------------- 2 files changed, 117 insertions(+), 108 deletions(-) diff --git a/src/win_generic.c b/src/win_generic.c index 7c8ef3d..ef991d3 100644 --- a/src/win_generic.c +++ b/src/win_generic.c @@ -15,6 +15,8 @@ #include "defc.h" #include "protos.h" #include "protos_windriver.h" +#include "adb_keycodes.h" + #include "win_keymap.h" extern int Verbose; @@ -239,7 +241,7 @@ void win_event_key(HWND hwnd, UINT raw_vk, BOOL down, int repeat, UINT flags) capslock_down = GetKeyState(VK_CAPITAL) & 0x01; if(capslock_down != g_win_capslock_down) { g_win_capslock_down = capslock_down; - adb_physical_key_update(0x39, !capslock_down); + adb_physical_key_update(kVK_CapsLock, !capslock_down); } return; // Do no more processing! diff --git a/src/win_keymap.h b/src/win_keymap.h index 00b8d59..7534894 100644 --- a/src/win_keymap.h +++ b/src/win_keymap.h @@ -1,124 +1,131 @@ /* this table is used to search for the Windows VK_* in col 1 or 2 */ /* flags bit 8 is or'ed into the VK, so we can distinguish keypad keys */ /* regardless of numlock */ + int g_a2_key_to_wsym[][3] = { - { 0x35, VK_ESCAPE, 0 }, - { 0x7a, VK_F1, 0 }, - { 0x78, VK_F2, 0 }, // OG Was 7B but F2 is defined has 0x78 in a2_key_to_ascii - { 0x63, VK_F3, 0 }, - { 0x76, VK_F4, 0 }, - { 0x60, VK_F5, 0 }, - { 0x61, VK_F6, 0 }, - { 0x62, VK_F7, 0 }, - { 0x64, VK_F8, 0 }, - { 0x65, VK_F9, 0 }, - { 0x6d, VK_F10, 0 }, - { 0x67, VK_F11, 0 }, - { 0x6f, VK_F12, 0 }, - { 0x69, VK_F13, 0 }, - { 0x6b, VK_F14, 0 }, - { 0x71, VK_F15, 0 }, - { 0x7f, VK_PAUSE, VK_CANCEL+0x100 }, + { kVK_Escape, VK_ESCAPE, 0 }, + { kVK_F1, VK_F1, 0 }, + { kVK_F2, VK_F2, 0 }, + { kVK_F3, VK_F3, 0 }, + { kVK_F4, VK_F4, 0 }, + { kVK_F5, VK_F5, 0 }, + { kVK_F6, VK_F6, 0 }, + { kVK_F7, VK_F7, 0 }, + { kVK_F8, VK_F8, 0 }, + { kVK_F9, VK_F9, 0 }, + { kVK_F10, VK_F10, 0 }, + { kVK_F11, VK_F11, 0 }, + { kVK_F12, VK_F12, 0 }, + { kVK_F13, VK_F13, 0 }, + { kVK_F14, VK_F14, 0 }, + { kVK_F15, VK_F15, 0 }, + { kVK_Reset, VK_PAUSE, VK_CANCEL+0x100 }, - { 0x32, 0xc0, 0 }, /* '`' */ - { 0x12, '1', 0 }, - { 0x13, '2', 0 }, - { 0x14, '3', 0 }, - { 0x15, '4', 0 }, - { 0x17, '5', 0 }, - { 0x16, '6', 0 }, - { 0x1a, '7', 0 }, - { 0x1c, '8', 0 }, - { 0x19, '9', 0 }, - { 0x1d, '0', 0 }, - { 0x1b, 0xbd, 0 }, /* '-' */ - { 0x18, 0xbb, 0 }, /* '=' */ - { 0x33, VK_BACK, 0 }, /* backspace */ - { 0x72, VK_INSERT+0x100, 0 }, /* Insert key */ -/* { 0x73, XK_Home, 0 }, alias VK_HOME to be KP_Equal! */ - { 0x74, VK_PRIOR+0x100, 0 }, /* pageup */ - { 0x47, VK_NUMLOCK, VK_NUMLOCK+0x100 }, /* clear */ - { 0x51, VK_HOME+0x100, 0 }, /* KP_equal is HOME key */ - { 0x4b, VK_DIVIDE, VK_DIVIDE+0x100 }, - { 0x43, VK_MULTIPLY, VK_MULTIPLY+0x100 }, + { kVK_ANSI_Grave, VK_OEM_3, 0 }, /* '`' */ + { kVK_ANSI_1, '1', 0 }, + { kVK_ANSI_2, '2', 0 }, + { kVK_ANSI_3, '3', 0 }, + { kVK_ANSI_4, '4', 0 }, + { kVK_ANSI_5, '5', 0 }, + { kVK_ANSI_6, '6', 0 }, + { kVK_ANSI_7, '7', 0 }, + { kVK_ANSI_8, '8', 0 }, + { kVK_ANSI_9, '9', 0 }, + { kVK_ANSI_0, '0', 0 }, + { kVK_ANSI_Minus, VK_OEM_MINUS, 0 }, /* '-' */ + { kVK_ANSI_Equal, VK_OEM_PLUS, 0 }, /* '=' */ + { kVK_Delete, VK_BACK, 0 }, /* backspace */ + { kVK_Insert, VK_INSERT+0x100, 0 }, /* Insert key */ + { kVK_PageUp, VK_PRIOR+0x100, 0 }, /* pageup */ + { kVK_Home, VK_HOME+0x100, 0 }, /* KP_equal is HOME key */ - { 0x30, VK_TAB, 0 }, - { 0x0c, 'Q', 0 }, - { 0x0d, 'W', 0 }, - { 0x0e, 'E', 0 }, - { 0x0f, 'R', 0 }, - { 0x11, 'T', 0 }, - { 0x10, 'Y', 0 }, - { 0x20, 'U', 0 }, - { 0x22, 'I', 0 }, - { 0x1f, 'O', 0 }, - { 0x23, 'P', 0 }, - { 0x21, 0xdb, 0 }, /* [ */ - { 0x1e, 0xdd, 0 }, /* ] */ - { 0x2a, 0xdc, 0 }, /* backslash, bar */ - { 0x75, VK_DELETE+0x100, 0 }, - { 0x77, VK_END+0x100, VK_END }, - { 0x79, VK_NEXT+0x100, 0 }, - { 0x59, VK_NUMPAD7, VK_HOME }, - { 0x5b, VK_NUMPAD8, VK_UP }, - { 0x5c, VK_NUMPAD9, VK_PRIOR }, - { 0x4e, VK_SUBTRACT, VK_SUBTRACT+0x100 }, + { kVK_Tab, VK_TAB, 0 }, + { kVK_ANSI_Q, 'Q', 0 }, + { kVK_ANSI_W, 'W', 0 }, + { kVK_ANSI_E, 'E', 0 }, + { kVK_ANSI_R, 'R', 0 }, + { kVK_ANSI_T, 'T', 0 }, + { kVK_ANSI_Y, 'Y', 0 }, + { kVK_ANSI_U, 'U', 0 }, + { kVK_ANSI_I, 'I', 0 }, + { kVK_ANSI_O, 'O', 0 }, + { kVK_ANSI_P, 'P', 0 }, + { kVK_ANSI_LeftBracket, VK_OEM_4, 0 }, /* [ */ + { kVK_ANSI_RightBracket, VK_OEM_6, 0 }, /* ] */ + { kVK_ANSI_Backslash, VK_OEM_5, 0 }, /* backslash, bar */ + { kVK_ForwardDelete, VK_DELETE+0x100, 0 }, + { kVK_End, VK_END+0x100, 0 }, + { kVK_PageDown, VK_NEXT+0x100, 0 }, - // { 0x39, VK_CAPITAL, 0 }, // Handled specially! - { 0x00, 'A', 0 }, - { 0x01, 'S', 0 }, - { 0x02, 'D', 0 }, - { 0x03, 'F', 0 }, - { 0x05, 'G', 0 }, - { 0x04, 'H', 0 }, - { 0x26, 'J', 0 }, - { 0x28, 'K', 0 }, - { 0x25, 'L', 0 }, - { 0x29, 0xba, 0 }, /* ; */ - { 0x27, 0xde, 0 }, /* single quote */ - { 0x24, VK_RETURN, 0 }, - { 0x56, VK_NUMPAD4, VK_LEFT }, - { 0x57, VK_NUMPAD5, VK_CLEAR }, - { 0x58, VK_NUMPAD6, VK_RIGHT }, - { 0x45, VK_ADD, 0 }, - { 0x38, VK_SHIFT, 0 }, - { 0x06, 'Z', 0 }, - { 0x07, 'X', 0 }, - { 0x08, 'C', 0 }, - { 0x09, 'V', 0 }, - { 0x0b, 'B', 0 }, - { 0x2d, 'N', 0 }, - { 0x2e, 'M', 0 }, - { 0x2b, 0xbc, 0 }, /* , */ - { 0x2f, 0xbe, 0 }, /* . */ - { 0x2c, 0xbf, 0 }, /* / */ - { 0x3e, VK_UP+0x100, 0 }, - { 0x53, VK_NUMPAD1, VK_END }, - { 0x54, VK_NUMPAD2, VK_DOWN }, - { 0x55, VK_NUMPAD3, VK_NEXT }, - { 0x36, VK_CONTROL, VK_CONTROL+0x100 }, - { 0x3a, VK_SNAPSHOT+0x100, VK_MENU+0x100 },/* Opt=prntscrn or alt-r */ + // { kVK_CapsLock, VK_CAPITAL, 0 }, // Handled specially! + { kVK_ANSI_A, 'A', 0 }, + { kVK_ANSI_S, 'S', 0 }, + { kVK_ANSI_D, 'D', 0 }, + { kVK_ANSI_F, 'F', 0 }, + { kVK_ANSI_G, 'G', 0 }, + { kVK_ANSI_H, 'H', 0 }, + { kVK_ANSI_J, 'J', 0 }, + { kVK_ANSI_K, 'K', 0 }, + { kVK_ANSI_L, 'L', 0 }, + { kVK_ANSI_Semicolon, VK_OEM_1, 0 }, /* ; */ + { kVK_ANSI_Quote, VK_OEM_7, 0 }, /* single quote */ + { kVK_Return, VK_RETURN, 0 }, + + + { kVK_Shift, VK_SHIFT, 0 }, + { kVK_ANSI_Z, 'Z', 0 }, + { kVK_ANSI_X, 'X', 0 }, + { kVK_ANSI_C, 'C', 0 }, + { kVK_ANSI_V, 'V', 0 }, + { kVK_ANSI_B, 'B', 0 }, + { kVK_ANSI_N, 'N', 0 }, + { kVK_ANSI_M, 'M', 0 }, + { kVK_ANSI_Comma, VK_OEM_COMMA, 0 }, /* , */ + { kVK_ANSI_Period, VK_OEM_PERIOD, 0 }, /* . */ + { kVK_ANSI_Slash, VK_OEM_2, 0 }, /* / */ + + + { kVK_Control, VK_CONTROL, VK_CONTROL+0x100 }, + { kVK_Option, VK_SNAPSHOT+0x100, VK_MENU+0x100 },/* Opt=prntscrn or alt-r */ // OG ActiveGS map OA-CA to Win & AltKey #ifndef ACTIVEGS - { 0x37, VK_SCROLL, VK_MENU }, /* Command=scr_lock or alt-l */ + { kVK_Command, VK_SCROLL, VK_MENU }, /* Command=scr_lock or alt-l */ #else - { 0x7f, VK_CANCEL, 0 }, - { 0x3A, VK_LWIN+0x100, VK_LWIN }, - { 0x37, VK_MENU, 0 }, /* Command=alt-l */ - { 0x37, VK_LMENU, 0 }, /* Command=alt-l */ - { 0x7F, VK_SCROLL,0 }, /* RESET */ - { 0x36, VK_LCONTROL, 0 }, // CTRL + { kVK_Reset, VK_CANCEL, 0 }, + { kVK_Option, VK_LWIN+0x100, VK_LWIN }, + { kVK_Command, VK_MENU, 0 }, /* Command=alt-l */ + { kVK_Command, VK_LMENU, 0 }, /* Command=alt-l */ + { kVK_Reset, VK_SCROLL,0 }, /* RESET */ + { kVK_Control, VK_LCONTROL, 0 }, // CTRL #endif - { 0x31, ' ', 0 }, - { 0x3b, VK_LEFT+0x100, 0 }, - { 0x3d, VK_DOWN+0x100, 0 }, - { 0x3c, VK_RIGHT+0x100, 0 }, - { 0x52, VK_NUMPAD0, VK_INSERT }, - { 0x41, VK_DECIMAL, VK_DECIMAL }, - { 0x4c, VK_RETURN+0x100, 0 }, + { kVK_Space, ' ', 0 }, + { kVK_LeftArrow, VK_LEFT+0x100, 0 }, + { kVK_DownArrow, VK_DOWN+0x100, 0 }, + { kVK_RightArrow, VK_RIGHT+0x100, 0 }, + { kVK_UpArrow, VK_UP+0x100, 0 }, + + + { kVK_ANSI_Keypad1, VK_NUMPAD1, VK_END }, + { kVK_ANSI_Keypad2, VK_NUMPAD2, VK_DOWN }, + { kVK_ANSI_Keypad3, VK_NUMPAD3, VK_NEXT }, + { kVK_ANSI_Keypad4, VK_NUMPAD4, VK_LEFT }, + { kVK_ANSI_Keypad5, VK_NUMPAD5, VK_CLEAR }, + { kVK_ANSI_Keypad6, VK_NUMPAD6, VK_RIGHT }, + { kVK_ANSI_Keypad7, VK_NUMPAD7, VK_HOME }, + { kVK_ANSI_Keypad8, VK_NUMPAD8, VK_UP }, + { kVK_ANSI_Keypad9, VK_NUMPAD9, VK_PRIOR }, + { kVK_ANSI_Keypad0, VK_NUMPAD0, VK_INSERT }, + { kVK_ANSI_KeypadDecimal, VK_DECIMAL, VK_DECIMAL }, + { kVK_ANSI_KeypadEnter, VK_RETURN+0x100, 0 }, + { kVK_ANSI_KeypadClear, VK_NUMLOCK, VK_NUMLOCK+0x100 }, /* clear */ + { kVK_ANSI_KeypadMinus, VK_SUBTRACT, VK_SUBTRACT+0x100 }, + { kVK_ANSI_KeypadPlus, VK_ADD, VK_ADD+0x100 }, + { kVK_ANSI_KeypadDivide, VK_DIVIDE, VK_DIVIDE+0x100 }, + { kVK_ANSI_KeypadMultiply, VK_MULTIPLY, VK_MULTIPLY+0x100 }, + { -1, -1, -1 } }; From b7ff6d56e81773ac3b1dec7898a3e427e782d819 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Wed, 6 Feb 2019 22:46:08 -0500 Subject: [PATCH 060/186] debug shell - @ and ^ indirection. --- src/debug_shell.re2c | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/src/debug_shell.re2c b/src/debug_shell.re2c index 1b75d71..b0e6362 100644 --- a/src/debug_shell.re2c +++ b/src/debug_shell.re2c @@ -635,7 +635,7 @@ static int parse_command(const char *cp) { _ = (" " | "\x00"); - * { --YYCURSOR; if (cp == YYCURSOR) goto next; return -1; } + * { --YYCURSOR; if (cp == YYCURSOR) goto command; return -1; } "a=" { return do_assign(YYCURSOR, REG_A); } "x=" { return do_assign(YYCURSOR, REG_X); } @@ -663,24 +663,47 @@ static int parse_command(const char *cp) { addr = to_hex(cp, cp + 6); has_bank = 1; has_addr = 1; - goto next; + goto indir; } x{2} "/" x{4} { addr = to_hex(cp, cp + 2) << 16; addr |= to_hex(cp + 3, cp + 7); has_bank = 1; has_addr = 1; - goto next; + goto indir; } x{1,4} { addr = to_hex(cp, YYCURSOR); has_bank = 0; has_addr = 1; - goto next; + goto indir; } */ -next: +indir: + /* only gets here if address specified */ + for(;;) { + /*!re2c + "" { break; } + "^" { + /* 3-byte indirection */ + if (!has_bank) addr = (g_prev_address & 0xff0000) | addr; + addr = get_memory24_c(addr, 0); + has_bank = 1; + continue; + } + "@" { + /* 2-byte indirection */ + /* 3-byte indirection */ + if (!has_bank) addr = (g_prev_address & 0xff0000) | addr; + word32 b = addr & 0xff0000; + addr = get_memory16_c(addr, 0) | b; + has_bank = 1; + continue; + } + */ + } +command: /*!re2c * { return -1; } ";l" end { From b08806c12a7cc1bcd0de928eed0798ea4a5905d7 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Wed, 6 Feb 2019 22:46:46 -0500 Subject: [PATCH 061/186] debug shell - : memory assignment --- src/debug_shell.re2c | 48 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/debug_shell.re2c b/src/debug_shell.re2c index b0e6362..d7537fd 100644 --- a/src/debug_shell.re2c +++ b/src/debug_shell.re2c @@ -339,6 +339,44 @@ next: return 0; } +static word32 do_mem_assign(word32 addr, const char *cp) { + /* "string" -> pokes ASCII chars */ + /* 'string' -> pokes ASCII chars | 0x80 */ + /* xx -> pokes hex byte */ + + const char *YYCURSOR = cp; + const char *YYMARKER = NULL; + + for(;;) { + const char *start = YYCURSOR; + /*!re2c + end { return addr; } + " " { continue; } + * { + fputs("Invalid data\n", stderr); + return addr; + } + x{2} { + set_memory_c(addr++, to_hex(start, YYCURSOR), 0); + continue; + } + ["] [^"\x00]* ["] { + for(++start; start < YYCURSOR -1; ++start) { + set_memory_c(addr++, *start, 0); + } + continue; + } + ['] [^'\x00]* ['] { + for(++start; start < YYCURSOR -1; ++start) { + set_memory_c(addr++, *start | 0x80, 0); + } + continue; + } + + */ + } +} + static int addr_cmp(const void *a, const void *b) { word32 aa = *(const word32 *)a; @@ -707,6 +745,16 @@ command: /*!re2c * { return -1; } ";l" end { + + ":" { + if (!has_addr) { + addr = g_prev_address; + has_bank = 1; + } + if (!has_bank) addr |= (g_prev_address & 0xff0000); + g_prev_address = do_mem_assign(addr, YYCURSOR); + return 0; + } int psr = engine.psr; if (!has_addr) { addr = g_prev_address; From d0ed507ef40c03f18b470030bd187e8958bc8611 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Wed, 6 Feb 2019 22:47:35 -0500 Subject: [PATCH 062/186] debug shell - control-C sets asynchronous halt request. --- src/debug_shell.re2c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/debug_shell.re2c b/src/debug_shell.re2c index d7537fd..5ecf337 100644 --- a/src/debug_shell.re2c +++ b/src/debug_shell.re2c @@ -5,17 +5,23 @@ */ #include -#include "defc.h" #include #include #include +#include +#include + +#include "defc.h" #include "disasm.h" + + extern int g_fullscreen; extern int g_config_control_panel; extern Engine_reg engine; +extern int halt_sim; int g_num_mp_breakpoints = 0; @@ -884,13 +890,27 @@ int debug_shell(int code) { } } +static void do_sig_intr(int sig, siginfo_t *info, void *context) { + set_halt(4); +} + /* also called by do_step */ void do_go() { int ret; int ok; /* if -g flag, start with debug shell ... */ + if (isatty(STDIN_FILENO)) { + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_SIGINFO; + sa.sa_sigaction = do_sig_intr; + sigaction(SIGINT, &sa, NULL); + } else { + g_dbg_shell = 0; + } g_config_control_panel = 1; if (g_dbg_shell) { @@ -909,6 +929,7 @@ void do_go() { ok = debug_shell(ret); if (!ok) return; g_config_control_panel = 0; + halt_sim &= ~0x07; /* clear any pending control-Cs, etc. */ } } } From dccb965631655ccbe181665979eba4759f347360 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Wed, 6 Feb 2019 22:48:04 -0500 Subject: [PATCH 063/186] eliminate overflow warnings when byteswapping. --- src/defc.h | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/defc.h b/src/defc.h index d817191..2b0a322 100644 --- a/src/defc.h +++ b/src/defc.h @@ -44,11 +44,13 @@ void U_STACK_TRACE(); #ifdef GSPLUS_LITTLE_ENDIAN // @todo: look at using for fastest platform implementations -# define BIGEND(a) ((((a) >> 24) & 0xff) + \ - (((a) >> 8) & 0xff00) + \ - (((a) << 8) & 0xff0000) + \ - (((a) << 24) & 0xff000000)) -# define GET_BE_WORD16(a) ((((a) >> 8) & 0xff) + (((a) << 8) & 0xff00)) +# define BIGEND(a) (\ + (((a) >> 24) & 0xff) + \ + (((a) >> 8) & 0xff00) + \ + (((a) & 0xff00) << 8) + \ + (((a) & 0xff ) << 24) \ + ) +# define GET_BE_WORD16(a) ((((a) >> 8) & 0xff) + ((((a) & 0xff) << 8))) # define GET_BE_WORD32(a) (BIGEND(a)) #else # define BIGEND(a) (a) From 68260dcf6a482d992658efaf505b4605609461f0 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Wed, 6 Feb 2019 22:50:38 -0500 Subject: [PATCH 064/186] debugger - new WANTS_BRK bit. setting it will cause BRKS to invoke the debugger instead of being handled within the 65816. --- src/defcomm.h | 3 ++- src/engine_c.c | 7 ++++--- src/instable.h | 9 +++++---- src/sim65816.c | 4 +--- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/defcomm.h b/src/defcomm.h index 332c3be..ae86aef 100644 --- a/src/defcomm.h +++ b/src/defcomm.h @@ -81,7 +81,7 @@ #define FPLUS_PLUS_3 0x10 #define FPLUS_PLUS_X_M1 0x18 -#define RET_BREAK 0x1 +#define RET_BRK 0x1 #define RET_COP 0x2 #define RET_WDM 0x3 #define RET_MVP 0x4 @@ -100,6 +100,7 @@ #define FLAG_IGNORE_MP 0x01 #define FLAG_IGNORE_BP 0x02 #define FLAG_STEP 0x04 +#define FLAG_WANT_BRK 0x08 #define MODE_BORDER 0 #define MODE_TEXT 1 diff --git a/src/engine_c.c b/src/engine_c.c index 4b32b28..511e7f1 100644 --- a/src/engine_c.c +++ b/src/engine_c.c @@ -1026,15 +1026,16 @@ int enter_engine(Engine_reg *engine_ptr) { word32 addr_latch; word32 tmp1, tmp2; + word32 flags = 0; word32 saved_pc = 0; word32 saved_psr = 0; word32 abort_support = g_num_mp_breakpoints ? 1 : 0; word32 kpc_support = g_num_bp_breakpoints ? 1 : 0; - - if (engine_ptr->flags & FLAG_IGNORE_MP) abort_support = 0; - if (engine_ptr->flags & FLAG_IGNORE_BP) kpc_support = 0; + flags = engine_ptr->flags; + if (flags & FLAG_IGNORE_MP) abort_support = 0; + if (flags & FLAG_IGNORE_BP) kpc_support = 0; tmp_pc_ptr = 0; diff --git a/src/instable.h b/src/instable.h index 95ab03a..bb71684 100644 --- a/src/instable.h +++ b/src/instable.h @@ -71,16 +71,17 @@ brk_testing_SYM DEC_KPC2; CYCLES_PLUS_2 b dispatch_done - depi RET_BREAK,3,4,ret0 + depi RET_BRK,3,4,ret0 #else GET_1BYTE_ARG; - if(g_testing) { + + if(flags & FLAG_WANT_BRK) { CYCLES_PLUS_2; - FINISH(RET_BREAK, arg); + FINISH(RET_BRK, arg); } - g_num_brk++; INC_KPC_2; + g_num_brk++; psr = psr & (~0x82); psr |= (neg << 7); diff --git a/src/sim65816.c b/src/sim65816.c index 52a3304..1e83171 100644 --- a/src/sim65816.c +++ b/src/sim65816.c @@ -2239,9 +2239,6 @@ void handle_action(word32 ret) { type = EXTRU(ret,3,4); switch(type) { - case RET_BREAK: - do_break(ret & 0xff); - break; case RET_COP: do_cop(ret & 0xff); break; @@ -2279,6 +2276,7 @@ void handle_action(word32 ret) { break; case RET_BP: case RET_MP: + case RET_BRK: /* handled elsewhere */ break; default: From 524d4a27e4e5a27ea10c9486e775789a6aa65e4d Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Wed, 6 Feb 2019 22:53:44 -0500 Subject: [PATCH 065/186] halt_printf, control-c, etc set a halt bit. This now invokes the debugger, asynchronously (ie, after the current instruction finishes execution). --- src/debug_shell.re2c | 6 ++++++ src/defcomm.h | 3 +-- src/engine_c.c | 3 ++- src/sim65816.c | 25 +++++++++++-------------- 4 files changed, 20 insertions(+), 17 deletions(-) diff --git a/src/debug_shell.re2c b/src/debug_shell.re2c index 5ecf337..472607a 100644 --- a/src/debug_shell.re2c +++ b/src/debug_shell.re2c @@ -876,6 +876,12 @@ int debug_shell(int code) { } printf("%06x: %0*x\n", g_abort_address, g_abort_bytes * 2, g_abort_value); } + if (code == RET_BRK) { + /* hit a BRK op */ + } + if (code == RET_HALT) { + /* halt_printf */ + } do_list(engine.kpc, &psr, 1); diff --git a/src/defcomm.h b/src/defcomm.h index ae86aef..aaca6c0 100644 --- a/src/defcomm.h +++ b/src/defcomm.h @@ -88,8 +88,7 @@ #define RET_MVN 0x5 #define RET_WAI 0x6 #define RET_STP 0x7 -#define RET_ADD_DEC_8 0x8 -#define RET_ADD_DEC_16 0x9 +#define RET_HALT 0x8 #define RET_C700 0xa #define RET_C70A 0xb #define RET_C70D 0xc diff --git a/src/engine_c.c b/src/engine_c.c index 511e7f1..a4cf77c 100644 --- a/src/engine_c.c +++ b/src/engine_c.c @@ -880,7 +880,8 @@ word32 get_itimer() { } void set_halt_act(int val) { - if(val == 1 && g_ignore_halts && !g_user_halt_bad) { + extern int g_dbg_shell; + if(val == 1 && g_ignore_halts && !g_user_halt_bad && !g_dbg_shell) { g_code_red++; } else { halt_sim |= val; diff --git a/src/sim65816.c b/src/sim65816.c index 1e83171..7cc7ddf 100644 --- a/src/sim65816.c +++ b/src/sim65816.c @@ -1514,11 +1514,11 @@ int run_prog() { ret >>= 28; } - if(halt_sim == HALT_EVENT) { + if(halt_sim & HALT_EVENT) { g_engine_halt_event++; /* if we needed to stop to check for interrupts, */ /* clear halt */ - halt_sim = 0; + halt_sim &= ~HALT_EVENT; } #if 0 @@ -1535,7 +1535,7 @@ int run_prog() { this_event = g_event_start.next; while(dcycs >= this_event->dcycs) { - if(halt_sim != 0 && halt_sim != HALT_EVENT) { + if(halt_sim & ~HALT_EVENT) { break; } if(g_stepping) { @@ -1605,9 +1605,7 @@ int run_prog() { } #endif - if(halt_sim != 0 && halt_sim != HALT_EVENT) { - break; - } + if (ret == RET_MP) break; if (ret == RET_BP) break; engine.flags &= ~(FLAG_IGNORE_BP | FLAG_IGNORE_MP); @@ -1615,6 +1613,12 @@ int run_prog() { ret = 0; break; } + if(halt_sim & 0x07) { + halt_sim &= ~0x07; + ret = RET_HALT; + break; + } + } #if 0 @@ -2256,14 +2260,6 @@ void handle_action(word32 ret) { case RET_C70D: do_c70d(ret); break; -#if 0 - case RET_ADD_DEC_8: - do_add_dec_8(ret); - break; - case RET_ADD_DEC_16: - do_add_dec_16(ret); - break; -#endif case RET_IRQ: irq_printf("Special fast IRQ response. irq_pending: %x\n", g_irq_pending); @@ -2277,6 +2273,7 @@ void handle_action(word32 ret) { case RET_BP: case RET_MP: case RET_BRK: + case RET_HALT: /* handled elsewhere */ break; default: From 2b1e04c3ac7a631092624481cb7835978b857fab Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Wed, 6 Feb 2019 22:54:18 -0500 Subject: [PATCH 066/186] debug shell - allow registers as addresses. --- src/debug_shell.re2c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/debug_shell.re2c b/src/debug_shell.re2c index 472607a..c2c1f32 100644 --- a/src/debug_shell.re2c +++ b/src/debug_shell.re2c @@ -702,6 +702,42 @@ static int parse_command(const char *cp) { "reset" end { do_reset(); return 0; } ("help" | "?") end { do_help(); return 0; } + "pc" { + addr = engine.kpc; + has_addr = 1; + has_bank = 1; + goto indir; + } + "a" { + addr = engine.acc; + has_addr = 1; + has_bank = 0; + goto indir; + } + "x" { + addr = engine.xreg; + has_addr = 1; + has_bank = 0; + goto indir; + } + "y" { + addr = engine.yreg; + has_addr = 1; + has_bank = 0; + goto indir; + } + "s" { + addr = engine.stack; + has_addr = 1; + has_bank = 1; + goto indir; + } + "d" { + addr = engine.dbank; + has_addr = 1; + has_bank = 1; + goto indir; + } x{6} { addr = to_hex(cp, cp + 6); From ea9a16fc5f0d83e560e50e94bab57dc881d03622 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Wed, 6 Feb 2019 22:54:41 -0500 Subject: [PATCH 067/186] debug shell - ;l or l for list. --- src/debug_shell.re2c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/debug_shell.re2c b/src/debug_shell.re2c index c2c1f32..1f89f2a 100644 --- a/src/debug_shell.re2c +++ b/src/debug_shell.re2c @@ -786,7 +786,6 @@ indir: command: /*!re2c * { return -1; } - ";l" end { ":" { if (!has_addr) { @@ -797,6 +796,8 @@ command: g_prev_address = do_mem_assign(addr, YYCURSOR); return 0; } + + ";" ? "l" end { int psr = engine.psr; if (!has_addr) { addr = g_prev_address; From 964293a3b8a98a9697327faef5a1ccab785e78f3 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Sat, 9 Feb 2019 00:17:00 -0500 Subject: [PATCH 068/186] travis.yml --- .travis.yml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..c4e9837 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,27 @@ +language: c + +matrix: + include: + - os: linux + dist: xenial + - os: osx + +addons: + apt: + packages: + - re2c + - libsdl2-dev + - libsdl2-image-dev + + homebrew: + packages: + - re2c + - sdl2 + - sdl2_image + +before_script: + - mkdir build + - cd build + - cmake .. + +script: make \ No newline at end of file From 66fbd7f6778302d807c7780a03951d275b8bb005 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Sun, 10 Feb 2019 21:40:22 -0500 Subject: [PATCH 069/186] move readline code to separate file. --- src/CMakeLists.txt | 14 +++- src/debug_shell.re2c | 32 ++------- src/readline.c | 164 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 184 insertions(+), 26 deletions(-) create mode 100644 src/readline.c diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b26ac50..ccab47e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -48,7 +48,7 @@ option(WITH_HOST_FST "Enable host fst support" ON) option(TOGGLE_STATUS "Enable F10 Toggle Status support (win32/x11)" OFF) option(WITH_RAWNET "Enable Uthernet emulation" OFF) option(WITH_ATBRIDGE "Enable AT Bridge" OFF) - +set(READLINE "NONE" CACHE STRING "Readline library (NONE, READLINE, LIBEDIT") set(generated_headers 8inst_c.h 16inst_c.h 8inst_s.h 16inst_s.h size_c.h size_s.h 8size_s.h 16size_s.h) add_custom_command( @@ -142,6 +142,8 @@ SET_SOURCE_FILES_PROPERTIES( MACOSX_PACKAGE_LOCATION Resources ) + + if(APPLE) add_custom_command(TARGET GSplus POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different @@ -152,6 +154,16 @@ if(APPLE) endif() # SET_SOURCE_FILES_PROPERTIES(vmnet_helper PROPERTIES MACOSX_PACKAGE_LOCATION MacOS) +add_library(x_readline readline.c) +if(READLINE MATCHES "READLINE") + target_compile_definitions(x_readline PRIVATE USE_READLINE) + target_link_libraries(x_readline PUBLIC history readline) +elseif(READLINE MATCHES "LIBEDIT") + target_compile_definitions(x_readline PRIVATE USE_LIBEDIT) + target_link_libraries(x_readline PUBLIC edit) +endif() + +target_link_libraries(GSplus x_readline) if (WITH_RAWNET) target_link_libraries(GSplus rawnet) diff --git a/src/debug_shell.re2c b/src/debug_shell.re2c index 1f89f2a..f516ff2 100644 --- a/src/debug_shell.re2c +++ b/src/debug_shell.re2c @@ -16,7 +16,7 @@ #include "disasm.h" - +extern char *x_readline(const char *prompt); extern int g_fullscreen; extern int g_config_control_panel; @@ -865,31 +865,13 @@ command: */ } -char *readline(const char *prompt) { - static char buffer[1024]; - fputs(prompt, stdout); - fflush(stdout); +enum { + MODE_NORMAL, + MODE_ASSEMBER +}; - for(;;) { - int ok = read(STDIN_FILENO, buffer, sizeof(buffer)-1); - if (ok < 0) { - if (ok == EINTR) continue; - return NULL; - } - if (ok == 0) return NULL; - while (ok) { - char c = buffer[ok-1]; - if (c == ' ' || c == '\r' || c == '\n') { - --ok; - continue; - } - break; - } - buffer[ok] = 0; +static int g_debugger_mode = MODE_NORMAL; - return buffer; - } -} int debug_shell(int code) { int c; @@ -923,7 +905,7 @@ int debug_shell(int code) { do_list(engine.kpc, &psr, 1); for(;;) { - cp = readline("> "); + cp = x_readline("> "); if (!cp) return 0; if (!*cp) continue; c = parse_command(cp); diff --git a/src/readline.c b/src/readline.c new file mode 100644 index 0000000..041d0d7 --- /dev/null +++ b/src/readline.c @@ -0,0 +1,164 @@ +#include +#include +#include +#include +#include + +#if defined(USE_LIBEDIT) + +#include + + + /* BSD libedit support */ +EditLine *el = NULL; +History *hist = NULL; +HistEvent ev; + +static const char *el_prompt = NULL; +static char *prompt_fn(EditLine *el) { + return el_prompt ? (char *)el_prompt : ""; +} + +char *x_readline(const char *prompt) { + static char buffer[1024]; + int ok = 0; + const char *cp; + + if (!el) { + hist = history_init(); + history(hist, &ev, H_SETSIZE, 50); + history(hist, &ev, H_SETUNIQUE, 1); + + el = el_init("GS+", stdin, stdout, stderr); + el_set(el, EL_EDITOR, "emacs"); + el_set(el, EL_BIND, "-e", NULL, NULL, NULL); + el_set(el, EL_HIST, history, hist); + el_set(el, EL_PROMPT, prompt_fn); + el_set(el, EL_SIGNAL, 1); + el_source(el, NULL); + } + + el_prompt = prompt; + cp = el_gets(el, &ok); + el_prompt = NULL; + if (ok <= 0) return NULL; + if (ok > sizeof(buffer) - 1) return ""; + + + memcpy(buffer, cp, ok); + while (ok) { + unsigned c = buffer[ok-1]; + if (c == ' ' || c == '\r' || c == '\n') { + --ok; + continue; + } + break; + } + buffer[ok] = 0; + + if (*buffer) + history(hist, &ev, H_ENTER, buffer); + + return buffer; +} + +void x_readline_end(void) { + if (el) { + el_end(el); + el = NULL; + } + if (hist) { + history_end(hist); + hist = NULL; + } +} + +#elif defined(USE_READLINE) + /* GNU Readline support */ + +#include +#include + +static int readline_init = 0; +/* suppress tab completion, which defaults to filenames */ +static char **rl_acf(const char* text, int start, int end) { + return NULL; +} +char *x_readline(const char *prompt) { + static char buffer[1024]; + + char *cp; + int ok; + + if (!readline_init) { + rl_readline_name = "GS+"; + rl_attempted_completion_function = rl_acf; + + using_history(); + stifle_history(50); + + readline_init = 1; + } + + char *cp = readline(prompt); + if (!cp) return NULL; + ok = strlen(cp); + if (ok > sizeof(buffer1) - 1) { + free(cp); + return ""; + } + memcpy(buffer, cp, ok); + while (ok) { + unsigned c = buffer[ok-1]; + if (c == ' ' || c == '\r' || c == '\n') { + --ok; + continue; + } + break; + } + buffer[ok] = 0; + free(cp); + + /* append to history, but only if unique from prev. entry */ + if (*buffer) { + HIST_ENTRY *h = history_get(history_length-1); + if (h == NULL || strcmp(buffer, h->line)) + add_history(buffer); + } + return buffer; +} + +void x_readline_end(void) { +} +#else + +char *x_readline(const char *prompt) { + static char buffer[1024]; + fputs(prompt, stdout); + fflush(stdout); + + for(;;) { + int ok = read(STDIN_FILENO, buffer, sizeof(buffer)-1); + if (ok < 0) { + if (ok == EINTR) continue; + return NULL; + } + if (ok == 0) return NULL; + while (ok) { + unsigned c = buffer[ok-1]; + if (c == ' ' || c == '\r' || c == '\n') { + --ok; + continue; + } + break; + } + buffer[ok] = 0; + + return buffer; + } +} + +void x_readline_end(void) { + +} +#endif From fdcb504d9c84634d77911d0103771b3d89f9b379 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Sun, 10 Feb 2019 21:40:47 -0500 Subject: [PATCH 070/186] use simpler signal interface. --- src/debug_shell.re2c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/debug_shell.re2c b/src/debug_shell.re2c index f516ff2..190f33c 100644 --- a/src/debug_shell.re2c +++ b/src/debug_shell.re2c @@ -915,7 +915,7 @@ int debug_shell(int code) { } } -static void do_sig_intr(int sig, siginfo_t *info, void *context) { +static void do_sig_intr(int sig) { set_halt(4); } @@ -929,8 +929,7 @@ void do_go() { struct sigaction sa; memset(&sa, 0, sizeof(sa)); sigemptyset(&sa.sa_mask); - sa.sa_flags = SA_SIGINFO; - sa.sa_sigaction = do_sig_intr; + sa.sa_handler = do_sig_intr; sigaction(SIGINT, &sa, NULL); } else { From 25caf2dbad38c3fc56a59d4d3ae4dcbc7918b870 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Sun, 10 Feb 2019 21:42:15 -0500 Subject: [PATCH 071/186] add ;s stack command. --- src/debug_shell.re2c | 290 +++++++++++++++++++++++-------------------- 1 file changed, 154 insertions(+), 136 deletions(-) diff --git a/src/debug_shell.re2c b/src/debug_shell.re2c index 190f33c..e99d0fd 100644 --- a/src/debug_shell.re2c +++ b/src/debug_shell.re2c @@ -38,6 +38,10 @@ word32 g_abort_value = 0; word32 g_abort_bytes = 0; +static word32 g_prev_address = 0; +static word32 g_prev_stack_address = 0; +static word32 g_prev_stack_bank = 0; + /* * todo * - tool break support @@ -83,6 +87,129 @@ word32 do_hexdump(word32 address, int lines) { return address; } +static int check_stack_rts(word32 address, unsigned bank, char *buffer) { + word32 rt; + unsigned op; + + rt = get_memory24_c(address, 0); + if (rt == 0) return 0; + + rt = (rt & 0xff0000) | ((rt - 3) & 0xffff); + op = get_memory_c(rt, 0); + if (op == 0x22) { + word32 tmp = get_memory24_c(rt + 1, 0); /* crosses bank :/ */ + /* todo -- check if tmp is known address, include below */ + /* todo -- if jsl e10000, check for prev. ldx # */ + sprintf(buffer, "%02x/%04x: JSL %06x", + rt >> 16, rt & 0xffff, tmp); + return 3; + } + /* sigh... this might re-check 0000 a lot */ + rt = get_memory16_c(address, 0); + rt = bank | ((rt - 2) & 0xffff); + + op = get_memory_c(rt, 0); + if (op == 0x20 || op == 0xfc) { + word32 tmp = get_memory16_c(rt + 1, 0); + if (op == 0x20) { + sprintf(buffer, "%02x/%04x: JSR %04x", + rt >> 16, rt & 0xffff, tmp); + } + if (op == 0xfc) { + sprintf(buffer, "%02x/%04x: JSR (%04x,x)", + rt >> 16, rt & 0xffff, tmp); + } + + return 2; + } + + + return 0; +} + +word32 do_stack(word32 address, unsigned bank) { + + /* scan through stack. + * if address looks like jsr return address + * or jsl return address, print it. + * otherwise, hexdump it. + * + * jsr absolute = 0x20 + * jsr (absolute,x) = 0xfc + * jsl abslong = 0x22 + * + * + * TODO - in Niftylist, JSL updates the bank. + * TODO - don't display c000, etc. + */ + + static char hex[] = "0123456789abcdef"; + + char buffer1[64]; + char buffer2[64]; + + unsigned offset = 0; + unsigned line = 0; + char *cp = buffer1; + + ++address; + + while(line < 20) { + unsigned c; + int tmp = check_stack_rts(address + offset, bank, buffer2); + if (tmp) { + /* flush any pending data */ + + if (offset) { + *cp = 0; + printf("%02x/%04x: %s\n", + address >> 16, address & 0xffff, buffer1); + ++line; + address += offset; + offset = 0; + cp = buffer1; + } + + for (offset = 0; offset < tmp; ++offset) { + c = get_memory_c(address + offset, 0); + *cp++ = hex[c >> 4]; + *cp++ = hex[c & 0x0f]; + *cp++ = ' '; + } + + *cp = 0; + printf("%02x/%04x: %-12s%s\n", + address >> 16, address & 0xffff, buffer1, buffer2); + ++line; + address += offset; + offset = 0; + cp = buffer1; + continue; + } + + c = get_memory_c(address + offset, 0); + *cp++ = hex[c >> 4]; + *cp++ = hex[c & 0x0f]; + *cp++ = ' '; + + if (offset == 7) *cp++ = ' '; + ++offset; + + if (offset == 16) { + *cp = 0; + printf("%02x/%04x: %s\n", + address >> 16, address & 0xffff, buffer1); + ++line; + address += offset; + offset = 0; + cp = buffer1; + } + } + + return address; + +} + word32 do_list(word32 address, int *psr_ptr, int lines) { char buffer[32]; @@ -393,55 +520,6 @@ static int addr_cmp(const void *a, const void *b) { - -#if 0 -void show_bp() { - int i; - word32 addr; - printf("Breakpoints:\n"); - for (i = 0; i < g_num_bp_breakpoints; ++i) { - addr = g_bp_breakpoints[i]; - printf("%02x/%04x\n", addr >> 16, addr & 0xffff); - } -} - -void set_bp(word32 addr) { - int i; - - for (i = 0; i < g_num_bp_breakpoints; ++i) { - if (g_bp_breakpoints[i] == addr) break; - } - - if (i < g_num_bp_breakpoints) return; /* already set */ - if (g_num_bp_breakpoints == MAX_BREAK_POINTS) { - printf("Too many breakpoints.\n"); - return; - } - g_bp_breakpoints[g_num_bp_breakpoints++] = addr; - - - qsort(g_bp_breakpoints, g_num_bp_breakpoints, sizeof(word32), addr_cmp); - fixup_brks(); -} - -void delete_bp(word32 addr) { - int i; - - for (i = 0; i < g_num_bp_breakpoints; ++i) { - if (g_bp_breakpoints[i] == addr) break; - } - - - if (i == g_num_bp_breakpoints) return; /* not set */ - g_bp_breakpoints[i] = 0; - g_bp_breakpoints[i] = g_bp_breakpoints[--g_num_bp_breakpoints]; - - qsort(g_bp_breakpoints, g_num_bp_breakpoints, sizeof(word32), addr_cmp); - fixup_brks(); -} -#endif - - void show_bp(int type) { int i; word32 addr; @@ -549,91 +627,6 @@ void delete_bp(int type, word32 addr) { } - -static int do_bp_mp_common(const char *cp, word32 default_bank, word32 *breakpoints, int *num_breakpoints) { - /* bp - * bp [+-] address - */ - - int i = 0; - int plus = 1; - word32 addr = 0; - const char *YYCURSOR = NULL; - const char *YYMARKER = NULL; - - while (*cp == ' ') ++cp; - switch(*cp) { - case '-': plus = 0; - case '+': - ++cp; - break; - } - - if (*cp == 0) { - /* just print them... */ - for (i = 0; i < *num_breakpoints; ++i) { - addr = breakpoints[i]; - printf("%02x/%04x\n", addr >> 16, addr & 0xffff); - } - return 0; - } - - YYCURSOR = cp; - /*!re2c - - * { return -1; } - x{6} end { - addr = to_hex(cp, cp + 6); - goto next; - } - x{2} "/" x{4} end { - addr = to_hex(cp, cp + 2) << 16; - addr |= to_hex(cp + 3, cp + 7); - goto next; - } - x{4} end { - addr = to_hex(cp, cp + 4); - addr |= default_bank; - goto next; - } - */ - -next: - - for (i = 0; i < *num_breakpoints; ++i) { - if (breakpoints[i] == addr) break; - } - - if (plus) { - if (i < *num_breakpoints) return 0; /* already set */ - if (*num_breakpoints == MAX_BREAK_POINTS) { - printf("Too many breakpoints.\n"); - return 0; - } - breakpoints[(*num_breakpoints)++] = addr; - } else { - if (i == *num_breakpoints) return 0; /* not set */ - breakpoints[i] = 0; - breakpoints[i] = breakpoints[--(*num_breakpoints)]; - } - - /* sort */ - qsort(breakpoints, *num_breakpoints, sizeof(word32), addr_cmp); - fixup_brks(); - - return 0; -} - -static int do_bp(const char *cp) { - return do_bp_mp_common(cp, engine.kpc & 0xff0000, - g_bp_breakpoints, &g_num_bp_breakpoints); -} - -static int do_mp(const char *cp) { - return do_bp_mp_common(cp, engine.dbank << 16, - g_mp_breakpoints, &g_num_mp_breakpoints); -} - static void do_help(void) { fputs( @@ -658,11 +651,17 @@ static void do_help(void) { stdout); } -static word32 g_prev_address = 0; + /* return -1 on error, 0 on success, 1 if debug shell should exit. */ static int parse_command(const char *cp) { + + /* TODO: + pc++ EOL -> skip current instruction + ! -> mini assembler mode. + */ + const char *YYCURSOR = cp; const char *YYMARKER = NULL; const char *ptr = NULL; @@ -797,7 +796,7 @@ command: return 0; } - ";" ? "l" end { + (";l" | "l") end { int psr = engine.psr; if (!has_addr) { addr = g_prev_address; @@ -823,6 +822,22 @@ command: g_prev_address = do_hexdump(addr, 20); return 0; } + + ";s" end { + /* print the stack */ + word32 bank; + if (!has_addr) { + addr = g_prev_stack_address; + bank = g_prev_stack_bank; + } + if (has_bank) { + bank = addr & 0xff0000; + addr &= 0xffff; + } + g_prev_stack_address = do_stack(addr,bank); + return 0; + } + "g" end { /* GO! */ if (has_addr) { @@ -904,6 +919,9 @@ int debug_shell(int code) { do_list(engine.kpc, &psr, 1); + g_prev_stack_address = engine.stack; + g_prev_stack_bank = engine.kpc & 0xff0000; + for(;;) { cp = x_readline("> "); if (!cp) return 0; From 926e9b7e474fb7df4ae07230db65e63725134edc Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Sun, 10 Feb 2019 21:43:15 -0500 Subject: [PATCH 072/186] travis - need freetype. --- .travis.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index c4e9837..a58c12b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,16 +12,17 @@ addons: - re2c - libsdl2-dev - libsdl2-image-dev - + - libfreetype6-dev homebrew: packages: - re2c - sdl2 - sdl2_image + - freetype before_script: - mkdir build - cd build - cmake .. -script: make \ No newline at end of file +script: make GSplus From 817792f94dae866d5fc1d9c60742763a0e2da6c8 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Sun, 10 Feb 2019 22:21:06 -0500 Subject: [PATCH 073/186] ubuntu, debian, etc, have outdated version of re2c. imagine that. --- src/CMakeLists.txt | 2 +- src/debug_shell.re2c | 24 +++++++++++------------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ccab47e..d2a054a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -74,7 +74,7 @@ add_custom_command( add_custom_command( OUTPUT debug_shell.c - COMMAND re2c -T -W -o ${CMAKE_CURRENT_BINARY_DIR}/debug_shell.c "${CMAKE_CURRENT_SOURCE_DIR}/debug_shell.re2c" + COMMAND re2c -W -o ${CMAKE_CURRENT_BINARY_DIR}/debug_shell.c "${CMAKE_CURRENT_SOURCE_DIR}/debug_shell.re2c" MAIN_DEPENDENCY debug_shell.re2c ) diff --git a/src/debug_shell.re2c b/src/debug_shell.re2c index e99d0fd..e28615b 100644 --- a/src/debug_shell.re2c +++ b/src/debug_shell.re2c @@ -611,19 +611,17 @@ void delete_bp(int type, word32 addr) { if (breakpoints[i] == addr) break; } + if (i == num_breakpoints) return; /* not set */ + breakpoints[i] = 0; + breakpoints[i] = breakpoints[--num_breakpoints]; + switch(type) { case 'B': g_num_bp_breakpoints = num_breakpoints; break; case 'M': g_num_mp_breakpoints = num_breakpoints; break; } - if (i == num_breakpoints) return; /* not set */ - breakpoints[i] = 0; - breakpoints[i] = breakpoints[--num_breakpoints]; - qsort(breakpoints, num_breakpoints, sizeof(word32), addr_cmp); fixup_brks(); - - } @@ -664,14 +662,12 @@ static int parse_command(const char *cp) { const char *YYCURSOR = cp; const char *YYMARKER = NULL; - const char *ptr = NULL; + //const char *ptr = NULL; int addr = 0; int has_bank = 0; int has_addr = 0; - /*!stags:re2c format = "const char *@@;\n"; */ - while (*cp == ' ') ++cp; /*!re2c @@ -759,6 +755,7 @@ static int parse_command(const char *cp) { } */ indir: + cp = YYCURSOR; /* only gets here if address specified */ for(;;) { /*!re2c @@ -783,6 +780,7 @@ indir: } command: + cp = YYCURSOR; /*!re2c * { return -1; } @@ -857,8 +855,8 @@ command: return 1; } - ";bp" @ptr [+-]? end { - char plus = *ptr; + ";bp" [+-]? end { + char plus = cp[3]; if (!has_addr && plus == 0) { show_bp('B'); return 0; } if (!has_addr) return -1; if (!has_bank) addr |= (engine.kpc & 0xff0000); @@ -867,8 +865,8 @@ command: return 0; } - ";mp" @ptr [+-]? end { - char plus = *ptr; + ";mp" [+-]? end { + char plus = cp[3]; if (!has_addr && plus == 0) { show_bp('M'); return 0; } if (!has_addr) return -1; if (!has_bank) addr |= (engine.kpc & 0xff0000); From f9a86eed8a5e1245abf37298838cfae8131b9431 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Sun, 10 Feb 2019 22:39:23 -0500 Subject: [PATCH 074/186] auto-select readline library by default. --- src/CMakeLists.txt | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d2a054a..77ef77d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -48,7 +48,7 @@ option(WITH_HOST_FST "Enable host fst support" ON) option(TOGGLE_STATUS "Enable F10 Toggle Status support (win32/x11)" OFF) option(WITH_RAWNET "Enable Uthernet emulation" OFF) option(WITH_ATBRIDGE "Enable AT Bridge" OFF) -set(READLINE "NONE" CACHE STRING "Readline library (NONE, READLINE, LIBEDIT") +set(READLINE "AUTO" CACHE STRING "Readline library (AUTO, NONE, READLINE, LIBEDIT)") set(generated_headers 8inst_c.h 16inst_c.h 8inst_s.h 16inst_s.h size_c.h size_s.h 8size_s.h 16size_s.h) add_custom_command( @@ -155,6 +155,14 @@ endif() # SET_SOURCE_FILES_PROPERTIES(vmnet_helper PROPERTIES MACOSX_PACKAGE_LOCATION MacOS) add_library(x_readline readline.c) +if (READLINE MATCHES "AUTO") + if (CMAKE_SYSTEM_NAME MATCHES "(Darwin|FreeBSD|OpenBSD|NetBSD)") + set(READLINE "LIBEDIT") + endif() + if (CMAKE_SYSTEM_NAME MATCHES "Linux") + set(READLINE "READLINE") + endif() +endif() if(READLINE MATCHES "READLINE") target_compile_definitions(x_readline PRIVATE USE_READLINE) target_link_libraries(x_readline PUBLIC history readline) From 0021956044633401fb59a9ce71f1a97cef7d4f78 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Sun, 10 Feb 2019 22:45:42 -0500 Subject: [PATCH 075/186] bug fixes. --- src/readline.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/readline.c b/src/readline.c index 041d0d7..9b26071 100644 --- a/src/readline.c +++ b/src/readline.c @@ -100,10 +100,10 @@ char *x_readline(const char *prompt) { readline_init = 1; } - char *cp = readline(prompt); + cp = readline(prompt); if (!cp) return NULL; ok = strlen(cp); - if (ok > sizeof(buffer1) - 1) { + if (ok > sizeof(buffer) - 1) { free(cp); return ""; } From 4a0c190ce3eefd81e1f988e14bd5eaf9cc385660 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Mon, 11 Feb 2019 18:11:45 -0500 Subject: [PATCH 076/186] win32 readline support, maybe. --- src/readline.c | 108 +++++++++++++++++++++++++++++++------------------ 1 file changed, 69 insertions(+), 39 deletions(-) diff --git a/src/readline.c b/src/readline.c index 9b26071..c855942 100644 --- a/src/readline.c +++ b/src/readline.c @@ -1,9 +1,25 @@ #include #include #include -#include #include + +#define HISTORY_SIZE 64 + +/* remove trailing whitespace and terminators */ +static void cleanup_buffer(char *buffer, int size) { + + while (size > 0) { + unsigned c = buffer[size-1]; + if (c == ' ' || c == '\r' || c == '\n') { + --size; + continue; + } + break; + } + buffer[size] = 0; +} + #if defined(USE_LIBEDIT) #include @@ -21,12 +37,12 @@ static char *prompt_fn(EditLine *el) { char *x_readline(const char *prompt) { static char buffer[1024]; - int ok = 0; + int count = 0; const char *cp; if (!el) { hist = history_init(); - history(hist, &ev, H_SETSIZE, 50); + history(hist, &ev, H_SETSIZE, HISTORY_SIZE); history(hist, &ev, H_SETUNIQUE, 1); el = el_init("GS+", stdin, stdout, stderr); @@ -39,22 +55,14 @@ char *x_readline(const char *prompt) { } el_prompt = prompt; - cp = el_gets(el, &ok); + cp = el_gets(el, &count); el_prompt = NULL; - if (ok <= 0) return NULL; - if (ok > sizeof(buffer) - 1) return ""; + if (count <= 0) return NULL; + if (count > sizeof(buffer) - 1) return ""; - memcpy(buffer, cp, ok); - while (ok) { - unsigned c = buffer[ok-1]; - if (c == ' ' || c == '\r' || c == '\n') { - --ok; - continue; - } - break; - } - buffer[ok] = 0; + memcpy(buffer, cp, count); + cleanup_buffer(buffer, count); if (*buffer) history(hist, &ev, H_ENTER, buffer); @@ -88,35 +96,27 @@ char *x_readline(const char *prompt) { static char buffer[1024]; char *cp; - int ok; + int count; if (!readline_init) { rl_readline_name = "GS+"; rl_attempted_completion_function = rl_acf; using_history(); - stifle_history(50); + stifle_history(HISTORY_SIZE); readline_init = 1; } cp = readline(prompt); if (!cp) return NULL; - ok = strlen(cp); - if (ok > sizeof(buffer) - 1) { + count = strlen(cp); + if (count > sizeof(buffer) - 1) { free(cp); return ""; } - memcpy(buffer, cp, ok); - while (ok) { - unsigned c = buffer[ok-1]; - if (c == ' ' || c == '\r' || c == '\n') { - --ok; - continue; - } - break; - } - buffer[ok] = 0; + memcpy(buffer, cp, count); + cleanup_buffer(buffer, count); free(cp); /* append to history, but only if unique from prev. entry */ @@ -130,8 +130,46 @@ char *x_readline(const char *prompt) { void x_readline_end(void) { } +#elif defined(WIN32) + +#include + +static int readline_init = 0; + +char *x_readline(const char *prompt) { + static char buffer[1024]; + DWORD count = 0; + BOOL ok; + HANDLE h = GetStdHandle(STD_INPUT_HANDLE), + if (!readline_init) { + CONSOLE_HISTORY_INFO chi; + DWORD mode; + + memset(&chi, 0, sizeof(chi)); + chi.cbSize = sizeof(CONSOLE_HISTORY_INFO); + chi.HistoryBufferSize = HISTORY_SIZE; + chi.NumberOfHistoryBuffers = 1; /* ???? */ + chi.dwFlags = HISTORY_NO_DUP_FLAG; + SetConsoleHistoryInfo(&chi); + + mode = ENABLE_ECHO_INPUT | ENABLE_EXTENDED_FLAGS | ENABLE_INSERT_MODE | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_QUICK_EDIT_MODE; + SetConsoleMode(h, mode); + + readline_init = 1; + } + + ok = ReadConsole(h, buffer, sizeof(buffer), &count, NULL); + if (!ok) return NULL; + + cleanup_buffer(buffer, count); + return buffer; +} + + #else +#include + char *x_readline(const char *prompt) { static char buffer[1024]; fputs(prompt, stdout); @@ -144,15 +182,7 @@ char *x_readline(const char *prompt) { return NULL; } if (ok == 0) return NULL; - while (ok) { - unsigned c = buffer[ok-1]; - if (c == ' ' || c == '\r' || c == '\n') { - --ok; - continue; - } - break; - } - buffer[ok] = 0; + cleanup_buffer(buffer, ok); return buffer; } From 14e1fe18f88f5c4f09b21dc4c8e8c51f633749f4 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Mon, 11 Feb 2019 20:15:30 -0500 Subject: [PATCH 077/186] move icon files around. --- src/CMakeLists.txt | 4 ++-- src/assets/gsp_icon.icns | Bin 0 -> 990353 bytes src/{ => assets}/gsp_icon.ico | Bin src/{ => assets}/gsport32.ico | Bin src/{ => assets}/win32.ico | Bin src/win32.rc | 4 ++-- 6 files changed, 4 insertions(+), 4 deletions(-) create mode 100644 src/assets/gsp_icon.icns rename src/{ => assets}/gsp_icon.ico (100%) rename src/{ => assets}/gsport32.ico (100%) rename src/{ => assets}/win32.ico (100%) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 77ef77d..2c5ac4d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -131,13 +131,13 @@ add_executable(GSplus WIN32 MACOSX_BUNDLE ${generated_headers} $<$:win32.rc> - $<$:gsp_icon.icns> + $<$:assets/gsp_icon.icns> $<$:fix_mac_menu.m> ) SET_SOURCE_FILES_PROPERTIES( - gsp_icon.icns + assets/gsp_icon.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources ) diff --git a/src/assets/gsp_icon.icns b/src/assets/gsp_icon.icns new file mode 100644 index 0000000000000000000000000000000000000000..c191909d32e88f88b1bd9c6ac07f7ac7873b46e0 GIT binary patch literal 990353 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 z%0A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%0Dr zzV>O1f6&zMnl_gUZ&h4$h*YaX49QWSE$+H}H35(k)v`CbH$lb+xSrhrtIJ2MP9-2O zhc?e8ac=_h=`cQC8iP6Cnro{s_*`^+*SE#n-UP@VfD-JnL+5S}9a}x;u<%#FgJY&C z?jBcW?D?q%mLk8Tpru^Dxjf-+QcvLMS2{?p8|f{bBoDpW97PA`WV~;9&Q}hv`nCUJ zlY!Huz`ZW!&j0OidBbqV8ISZy*B!iL&BaY`jEok`N5o5ERoR@T)RB0%I5oe5FTA=u zpt+uf*TUNTM*W|DVLplX(Aghwi@i2F?h~+OX%2h(f$#bD;mp5%{qSQy`u!4pw~o&_ z@1o&#uYa@e0pP6{F0R@Hn|(X4AL=5;Up0q}zpegHlVq~OfmpmU?MGbO1ZLYGQBx%{ zHXLos|A*OQLbsPT;rmAVeIolrJQULg^G`l5v3EyDpSRcGQs7Y^K!d(5kNooAmVWsf zkohn3o0nAE+hIbWv9SlB>;nKEWx{bp|FZ-y7;Q>CilbT#yh$-@V~lbfwG9*i+78U zq!OY|OTMInE*>$72Ruo0nwg)6&)y7s9qb$I$j`6)+OP3rfzPvdj``&3m9}B-%B!xj z<3}$a&OP`1;r(Zxh2jBzo$oxzp6TU+8D`yrOdM?I@l7b=q&2(GCTpEm611i>8k2K3bRcbKkI+fwVf!)hj#DuR|oeSPPEmd|M21F>NzkYsbGT&~%bJ1@?-TZ4+=zV1u3$0`kUBJUXpMN0G{*$vA>5 z&gI;l=XQWBbCn15kpF^q>D`9_}y3kNoA*G+*R?HJpM7mKY#u|8J_W#PqmBu{cVC7 zuPnKIcOI$HS@vxEc9yqqvyEW4+-MU3TP3nhaN&Z{#s$T5T`r(FIO8de8l+XnI`I(# z{Oc}GOdU5>Yg?XBJt;Y4a$0z8{fqxPzCq7w6>p!=DlKjP+RuJ`dN>r{Z}^hOpE3OL zD_=hR@t?kN_?Z{I)W6Fh*>|`2SAXk|hL^qMC*wm=2iu3D28L_eOuG;33)}y7P*;2l zvC?Z7e0M;&#fPoCK)6Z=ElP4UOuW;D=Vd)rjMwp|8Y^i}coE^MH- zek6l(1=_Rd3asC+h1b(TebV=42Pv{$#~- zTIK6v_^+aqgT$wYD_B%c;kTqapOuYC0H2oK?gW6>emuz6GNcpBj=IR+j&2?sWW1af zUF$Q!mtygV_iR&3HUi4}{6;)Q&A^4Ms1G-MNpxNJ#v_i6wN)w(i7_e$`8)FjV60S5 zJ-h#@Cl2>N<*SEp`i5r@FMPqj9A5Lf*AFlKg4yiJ!SZBFMY}IP0#&0 ze@!ViDa9%q7j^RHiSa+Nt-k48bzXa8x4TVd9AN|9R9CA|`}GywJLtLCudWyIUx$|Y z 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�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 z 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