1 // Copyright 2008 Dolphin Emulator Project 2 // Licensed under GPLv2+ 3 // Refer to the license.txt file included. 4 5 #pragma once 6 7 #include <atomic> 8 #include <thread> 9 #include <vector> 10 11 #ifdef _WIN32 12 #include <Windows.h> 13 #endif 14 15 #include <SFML/Network.hpp> 16 17 #include "Common/Flag.h" 18 #include "Core/HW/EXI/EXI_Device.h" 19 20 class PointerWrap; 21 22 namespace ExpansionInterface 23 { 24 // Network Control Register A 25 enum NCRA 26 { 27 NCRA_RESET = 0x01, // RESET 28 NCRA_ST0 = 0x02, // Start transmit command/status 29 NCRA_ST1 = 0x04, // " 30 NCRA_SR = 0x08 // Start Receive 31 }; 32 33 // Network Control Register B 34 enum NCRB 35 { 36 NCRB_PR = 0x01, // Promiscuous Mode 37 NCRB_CA = 0x02, // Capture Effect Mode 38 NCRB_PM = 0x04, // Pass Multicast 39 NCRB_PB = 0x08, // Pass Bad Frame 40 NCRB_AB = 0x10, // Accept Broadcast 41 NCRB_HBD = 0x20, // reserved 42 NCRB_RXINTC = 0xC0 // Receive Interrupt Counter (mask) 43 }; 44 45 // Interrupt Mask Register 46 // Interrupt Register 47 enum Interrupts 48 { 49 INT_FRAG = 0x01, // Fragment Counter 50 INT_R = 0x02, // Receive 51 INT_T = 0x04, // Transmit 52 INT_R_ERR = 0x08, // Receive Error 53 INT_T_ERR = 0x10, // Transmit Error 54 INT_FIFO_ERR = 0x20, // FIFO Error 55 INT_BUS_ERR = 0x40, // BUS Error 56 INT_RBF = 0x80 // RX Buffer Full 57 }; 58 59 // NWAY Configuration Register 60 enum NWAYC 61 { 62 NWAYC_FD = 0x01, // Full Duplex Mode 63 NWAYC_PS100_10 = 0x02, // Port Select 100/10 64 NWAYC_ANE = 0x04, // Autonegotiate enable 65 66 // Autonegotiation status bits... 67 68 NWAYC_NTTEST = 0x40, // Reserved 69 NWAYC_LTE = 0x80 // Link Test Enable 70 }; 71 72 enum NWAYS 73 { 74 NWAYS_LS10 = 0x01, 75 NWAYS_LS100 = 0x02, 76 NWAYS_LPNWAY = 0x04, 77 NWAYS_ANCLPT = 0x08, 78 NWAYS_100TXF = 0x10, 79 NWAYS_100TXH = 0x20, 80 NWAYS_10TXF = 0x40, 81 NWAYS_10TXH = 0x80 82 }; 83 84 enum MISC1 85 { 86 MISC1_BURSTDMA = 0x01, 87 MISC1_DISLDMA = 0x02, 88 MISC1_TPF = 0x04, 89 MISC1_TPH = 0x08, 90 MISC1_TXF = 0x10, 91 MISC1_TXH = 0x20, 92 MISC1_TXFIFORST = 0x40, 93 MISC1_RXFIFORST = 0x80 94 }; 95 96 enum MISC2 97 { 98 MISC2_HBRLEN0 = 0x01, 99 MISC2_HBRLEN1 = 0x02, 100 MISC2_RUNTSIZE = 0x04, 101 MISC2_DREQBCTRL = 0x08, 102 MISC2_RINTSEL = 0x10, 103 MISC2_ITPSEL = 0x20, 104 MISC2_A11A8EN = 0x40, 105 MISC2_AUTORCVR = 0x80 106 }; 107 108 enum 109 { 110 BBA_NCRA = 0x00, 111 BBA_NCRB = 0x01, 112 113 BBA_LTPS = 0x04, 114 BBA_LRPS = 0x05, 115 116 BBA_IMR = 0x08, 117 BBA_IR = 0x09, 118 119 BBA_BP = 0x0a, 120 BBA_TLBP = 0x0c, 121 BBA_TWP = 0x0e, 122 BBA_IOB = 0x10, 123 BBA_TRP = 0x12, 124 BBA_RWP = 0x16, 125 BBA_RRP = 0x18, 126 BBA_RHBP = 0x1a, 127 128 BBA_RXINTT = 0x14, 129 130 BBA_NAFR_PAR0 = 0x20, 131 BBA_NAFR_PAR1 = 0x21, 132 BBA_NAFR_PAR2 = 0x22, 133 BBA_NAFR_PAR3 = 0x23, 134 BBA_NAFR_PAR4 = 0x24, 135 BBA_NAFR_PAR5 = 0x25, 136 BBA_NAFR_MAR0 = 0x26, 137 BBA_NAFR_MAR1 = 0x27, 138 BBA_NAFR_MAR2 = 0x28, 139 BBA_NAFR_MAR3 = 0x29, 140 BBA_NAFR_MAR4 = 0x2a, 141 BBA_NAFR_MAR5 = 0x2b, 142 BBA_NAFR_MAR6 = 0x2c, 143 BBA_NAFR_MAR7 = 0x2d, 144 145 BBA_NWAYC = 0x30, 146 BBA_NWAYS = 0x31, 147 148 BBA_GCA = 0x32, 149 150 BBA_MISC = 0x3d, 151 152 BBA_TXFIFOCNT = 0x3e, 153 BBA_WRTXFIFOD = 0x48, 154 155 BBA_MISC2 = 0x50, 156 157 BBA_SI_ACTRL = 0x5c, 158 BBA_SI_STATUS = 0x5d, 159 BBA_SI_ACTRL2 = 0x60 160 }; 161 162 enum 163 { 164 BBA_NUM_PAGES = 0x10, 165 BBA_PAGE_SIZE = 0x100, 166 BBA_MEM_SIZE = BBA_NUM_PAGES * BBA_PAGE_SIZE, 167 BBA_TXFIFO_SIZE = 1518 168 }; 169 170 enum 171 { 172 EXI_DEVTYPE_ETHER = 0x04020200 173 }; 174 175 enum SendStatus 176 { 177 DESC_CC0 = 0x01, 178 DESC_CC1 = 0x02, 179 DESC_CC2 = 0x04, 180 DESC_CC3 = 0x08, 181 DESC_CRSLOST = 0x10, 182 DESC_UF = 0x20, 183 DESC_OWC = 0x40, 184 DESC_OWN = 0x80 185 }; 186 187 enum RecvStatus 188 { 189 DESC_BF = 0x01, 190 DESC_CRC = 0x02, 191 DESC_FAE = 0x04, 192 DESC_FO = 0x08, 193 DESC_RW = 0x10, 194 DESC_MF = 0x20, 195 DESC_RF = 0x40, 196 DESC_RERR = 0x80 197 }; 198 199 #define BBA_RECV_SIZE 0x800 200 201 enum class BBADeviceType 202 { 203 TAP, 204 XLINK, 205 }; 206 207 class CEXIETHERNET : public IEXIDevice 208 { 209 public: 210 explicit CEXIETHERNET(BBADeviceType type); 211 virtual ~CEXIETHERNET(); 212 void SetCS(int cs) override; 213 bool IsPresent() const override; 214 bool IsInterruptSet() override; 215 void ImmWrite(u32 data, u32 size) override; 216 u32 ImmRead(u32 size) override; 217 void DMAWrite(u32 addr, u32 size) override; 218 void DMARead(u32 addr, u32 size) override; 219 void DoState(PointerWrap& p) override; 220 221 private: 222 struct 223 { 224 enum 225 { 226 READ, 227 WRITE 228 } direction; 229 230 enum 231 { 232 EXI, 233 MX 234 } region; 235 236 u16 address; 237 bool valid; 238 } transfer = {}; 239 240 enum 241 { 242 EXI_ID, 243 REVISION_ID, 244 INTERRUPT_MASK, 245 INTERRUPT, 246 DEVICE_ID, 247 ACSTART, 248 HASH_READ = 8, 249 HASH_WRITE, 250 HASH_STATUS = 0xb, 251 RESET = 0xf 252 }; 253 254 // exi regs 255 struct EXIStatus 256 { 257 enum 258 { 259 TRANSFER = 0x80 260 }; 261 262 u8 revision_id = 0; // 0xf0 263 u8 interrupt_mask = 0; 264 u8 interrupt = 0; 265 u16 device_id = 0xD107; 266 u8 acstart = 0x4E; 267 u32 hash_challenge = 0; 268 u32 hash_response = 0; 269 u8 hash_status = 0; 270 } exi_status; 271 272 struct Descriptor 273 { 274 u32 word; 275 setDescriptor276 inline void set(u32 const next_page, u32 const packet_length, u32 const status) 277 { 278 word = 0; 279 word |= (status & 0xff) << 24; 280 word |= (packet_length & 0xfff) << 12; 281 word |= next_page & 0xfff; 282 } 283 }; 284 page_ptr(int const index)285 inline u16 page_ptr(int const index) const 286 { 287 return ((u16)mBbaMem[index + 1] << 8) | mBbaMem[index]; 288 } 289 ptr_from_page_ptr(int const index)290 inline u8* ptr_from_page_ptr(int const index) const { return &mBbaMem[page_ptr(index) << 8]; } 291 bool IsMXCommand(u32 const data); 292 bool IsWriteCommand(u32 const data); 293 const char* GetRegisterName() const; 294 void MXHardReset(); 295 void MXCommandHandler(u32 data, u32 size); 296 void DirectFIFOWrite(const u8* data, u32 size); 297 void SendFromDirectFIFO(); 298 void SendFromPacketBuffer(); 299 void SendComplete(); 300 u8 HashIndex(const u8* dest_eth_addr); 301 bool RecvMACFilter(); 302 void inc_rwp(); 303 bool RecvHandlePacket(); 304 305 std::unique_ptr<u8[]> mBbaMem; 306 std::unique_ptr<u8[]> tx_fifo; 307 308 class NetworkInterface 309 { 310 protected: 311 CEXIETHERNET* m_eth_ref = nullptr; NetworkInterface(CEXIETHERNET * eth_ref)312 explicit NetworkInterface(CEXIETHERNET* eth_ref) : m_eth_ref{eth_ref} {} 313 314 public: Activate()315 virtual bool Activate() { return false; } Deactivate()316 virtual void Deactivate() {} IsActivated()317 virtual bool IsActivated() { return false; } SendFrame(const u8 * frame,u32 size)318 virtual bool SendFrame(const u8* frame, u32 size) { return false; } RecvInit()319 virtual bool RecvInit() { return false; } RecvStart()320 virtual void RecvStart() {} RecvStop()321 virtual void RecvStop() {} 322 323 virtual ~NetworkInterface() = default; 324 }; 325 326 class TAPNetworkInterface : public NetworkInterface 327 { 328 public: TAPNetworkInterface(CEXIETHERNET * eth_ref)329 explicit TAPNetworkInterface(CEXIETHERNET* eth_ref) : NetworkInterface(eth_ref) {} 330 331 public: 332 bool Activate() override; 333 void Deactivate() override; 334 bool IsActivated() override; 335 bool SendFrame(const u8* frame, u32 size) override; 336 bool RecvInit() override; 337 void RecvStart() override; 338 void RecvStop() override; 339 340 private: 341 #if defined(WIN32) || defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \ 342 defined(__OpenBSD__) 343 std::thread readThread; 344 Common::Flag readEnabled; 345 Common::Flag readThreadShutdown; 346 static void ReadThreadHandler(TAPNetworkInterface* self); 347 #endif 348 #if defined(_WIN32) 349 HANDLE mHAdapter = INVALID_HANDLE_VALUE; 350 OVERLAPPED mReadOverlapped = {}; 351 OVERLAPPED mWriteOverlapped = {}; 352 std::vector<u8> mWriteBuffer; 353 bool mWritePending = false; 354 #elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) 355 int fd = -1; 356 #endif 357 }; 358 359 class XLinkNetworkInterface : public NetworkInterface 360 { 361 public: XLinkNetworkInterface(CEXIETHERNET * eth_ref,std::string dest_ip,int dest_port,std::string identifier,bool chat_osd_enabled)362 XLinkNetworkInterface(CEXIETHERNET* eth_ref, std::string dest_ip, int dest_port, 363 std::string identifier, bool chat_osd_enabled) 364 : NetworkInterface(eth_ref), m_dest_ip(std::move(dest_ip)), m_dest_port(dest_port), 365 m_client_identifier(identifier), m_chat_osd_enabled(chat_osd_enabled) 366 { 367 } 368 369 public: 370 bool Activate() override; 371 void Deactivate() override; 372 bool IsActivated() override; 373 bool SendFrame(const u8* frame, u32 size) override; 374 bool RecvInit() override; 375 void RecvStart() override; 376 void RecvStop() override; 377 378 private: 379 std::string m_dest_ip; 380 int m_dest_port; 381 std::string m_client_identifier; 382 bool m_chat_osd_enabled; 383 bool m_bba_link_up = false; 384 bool m_bba_failure_notified = false; 385 #if defined(WIN32) || defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \ 386 defined(__OpenBSD__) 387 sf::UdpSocket m_sf_socket; 388 sf::IpAddress m_sf_recipient_ip; 389 char m_in_frame[9004]; 390 char m_out_frame[9004]; 391 std::thread m_read_thread; 392 Common::Flag m_read_enabled; 393 Common::Flag m_read_thread_shutdown; 394 static void ReadThreadHandler(XLinkNetworkInterface* self); 395 #endif 396 }; 397 398 std::unique_ptr<NetworkInterface> m_network_interface; 399 400 std::unique_ptr<u8[]> mRecvBuffer; 401 u32 mRecvBufferLength = 0; 402 }; 403 } // namespace ExpansionInterface 404