SmartOS/Drivers/W5500.cpp

1612 lines
42 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "stddef.h"
#include "Kernel\Sys.h"
#include "Device\Spi.h"
#include "Device\Power.h"
#include "Net\ITransport.h"
#include "Net\Socket.h"
#include "Message\DataStore.h"
#include "Net\Dhcp.h"
#include "Net\DNS.h"
#include "W5500.h"
#include "Kernel\TTime.h"
#include "Kernel\Task.h"
#include "App\FlushPort.h"
#define NET_DEBUG DEBUG
//#define NET_DEBUG 0
#if NET_DEBUG
#define net_printf debug_printf
#else
#define net_printf(format, ...)
#endif
/*
硬件设置部分
硬件:TCP,UDP,ICMP,IPv4,ARP,IGMP,PPPoE
引脚 PMODE[2:0]---- 也可以通过 PHYCFGR 寄存器进行配置
000 10BI半双工关闭自动协商
001 10BI全双工关闭自动协商
010 100BI半双工关闭自动协商
011 100BI全双工关闭自动协商
100 100BI半双工启用自动协商
101 未启用
110 未启用
111 所有功能,启用自动协商
RSTn 重置 低电平有效低电平时间至少500us才能生效
InTn 中断输出 (低电平有效)--- 程序中 IRQ 引脚
支持SPI 模式0和模式3 自动识别
*/
/*
内部结构
一个通用寄存器区
0x0000-0x0039 独立寻址BSB选区
IP MAC 等整体信息
八个Socket寄存器区
0x0000-0x0030 独立寻址BSB选区
8个Socket收发缓存 总缓存32K
发缓存一共16K(0x0000-0x3fff) 初始化分配为 每个Socket 2K
收缓存一共16K(0x0000-0x3fff) 初始化分配为 每个Socket 2K
再分配时 收发都不可越16K大边界 地址自动按Socket编号调配
*/
class ByteStruct // 位域基类
{
public:
void Init(byte data = 0) { *(byte*)this = data; }
byte ToByte() { return *(byte*)this; }
};
// 数据帧格式
// 2byte Address + 1byte TConfig + nbyte Data Phase
// 可变数据长度模式下SCSn 拉低代表W5500的SPI 帧开始(地址段),拉高表示帧结束 可能跟SPI NSS 不一样)
// 必须设定为1字节对齐否则offsetof会得到错误的位置
#pragma pack(push) // 保存对齐状态
// 强制结构体紧凑分配空间
#pragma pack(1)
// 通用寄存器结构
typedef struct
{
byte MR; // 模式 0x0000
byte GAR[4]; // 网关地址 0x0001
byte SUBR[4]; // 子网掩码 0x0005
byte SHAR[6]; // 源MAC地址 0x0009
byte SIPR[4]; // 源IP地址 0x000f
ushort INTLEVEL; // 低电平中断定时器寄存器 0x0013
byte IR; // 中断寄存器 0x0015
byte IMR; // 中断屏蔽寄存器 0x0016
byte SIR; // Socket中断寄存器 0x0017
byte SIMR; // Socket中断屏蔽寄存器 0x0018
ushort RTR; // 重试时间 0x0019
byte RCR; // 重试计数 0x001b
byte PTIMER; // PPP 连接控制协议请求定时寄存器 0x001c
byte PMAGIC; // PPP 连接控制协议幻数寄存器 0x001d
byte PHAR[6]; // PPPoE 模式下目标 MAC 寄存器 0x001e
byte PSID[2]; // PPPoE 模式下会话 ID 寄存器 0x0024
byte PMRU[2]; // PPPoE 模式下最大接收单元 0x0026
byte UIPR[4]; // 无法抵达 IP 地址寄存器【只读】 0x0028
byte UPORTR[2]; // 无法抵达端口寄存器【只读】 0x002c
byte PHYCFGR; // PHY 配置寄存器 0x002e
//byte VERSIONR // 芯片版本寄存器【只读】 0x0039 // 地址不连续
}TGeneral; // 只有一份 所以直接定义就好
typedef struct
{
byte MR; //0x0000 // Socket 模式寄存器
byte CR; //0x0001 // 配置寄存器 【较为特殊】【只写读为0x00】
byte IR; //0x0002 // 中断寄存器 写1清0
byte SR; //0x0003 // 状态寄存器 【只读】
byte PORT[2]; //0x0004 // TCP UDP 模式下端口号 OPEN之前配置好
byte DHAR[6]; //0x0006 // 目的MAC,SEND_MAC使用;CONNECT/SEND 命令时ARP获取到的MAC
byte DIPR[4]; //0x000c // 目标IP地址
byte DPORT[2]; //0x0010 // 目标端口
byte MSSR[2]; //0x0012 // TCP UDP 模式下 MTU 最大传输单元大小 默认最大值
// TCP:1460; UDP:1472; MACRAW:1514;
// MACRAW 模式时 由于MTU 不在内部处理默认MTU将会生效
// PPPoE 模式下 略
// TCP UDP 模式下,传输数据比 MTU大时数据将会自动划分成默认MTU 单元大小
byte Reserved; //0x0014
byte TOS; //0x0015 // IP包头 服务类型 OPEN之前配置
byte TTL; //0x0016 // 生存时间 TTL OPEN之前配置
byte Reserved2[7]; //0x0017 - 0x001d
byte RXBUF_SIZE;//0x001e // 接收缓存大小 1 2 4 8 16 单位KByte
byte TXBUF_SIZE;//0x001f // 发送缓存大小 1 2 4 8 16 单位KByte
byte TX_FSR[2]; //0x0020 // 空闲发送寄存器大小
byte TX_RD[2]; //0x0022 // 发送读缓存指针
byte TX_WR[2]; //0x0024 // 发送写缓存指针
byte RX_RSR[2]; //0x0026 // 空闲接收寄存器大小
byte RX_RD[2]; //0x0028 // 接收读缓存指针
byte RX_WR[2]; //0x002a // 接收写缓存指针
byte IMR; //0x002c // 中断屏蔽寄存器 结构跟IR一样 0屏蔽 1不屏蔽
byte FRAG[2]; //0x002d // IP包头 分段部分 分段寄存器
byte KPALVTR; //0x002f // 只在TCP模式下使用 在线时间寄存器 单位5s
// 为0 时 手动SEND_KEEP
// > 0 时 忽略SEND_KEEP操作
}TSocket;
#pragma pack(pop) // 恢复对齐状态
// 位域 低位在前
// 配置寄存器 TConfig 结构
typedef struct : ByteStruct
{
byte OpMode:2; // SPI 工作模式
// 00 可变数据长度模式N字节数据段1<=N
// 01 固定数据长度模式1字节数据长度N=1
// 10 固定数据长度模式1字节数据长度N=2
// 11 固定数据长度模式1字节数据长度N=4
// 固定模式不需要NSS 芯片NSS 接地) 可变模式SPI NSS 受控
byte ReadWrite:1; // 0读 1
byte BlockSelect:2; // 区域选择位域 // 1个通用寄存器 8个Socket寄存器组选择写缓存收缓存
// 00000 通用
// XXX01 SocketXXX 寄存器
// XXX10 SocketXXX 发缓存
// XXX11 SocketXXX 收缓存
// 其余保留, 如果选择了保留的将会导致W5500故障
byte Socket:3; // Socket组0表示通用寄存器
}TConfig;
// 通用寄存器结构
// 模式寄存器
typedef struct : ByteStruct
{
byte Reserved:1;
byte FARP:1; // 强迫ARP 0 关闭1开启 强迫ARP模式下无论是否发送数据都会强迫ARP请求
byte Reserved2:1;
byte PPPoE:1; // PPPoE 0 关闭1开启
byte PB:1; // Ping 屏蔽位 1 ping不响应0 ping 有响应
byte WOL:1; // 网络唤醒 0关闭1开启 (收到唤醒包发生中断)
byte Reserved3:1;
byte RST:1; // 软件复位 1 复位
}T_Mode;
// IR 不为零中断
// IMR 为零屏蔽中断
typedef struct : ByteStruct // IMR 结构一样
{
byte Reserved:4;
byte MP:1; // 收到网络唤醒包
byte PPPoE:1; // PPPoE 连接不可达
byte UNREACH:1; // 目标不可达
byte CONFLICT:1; // IP冲突
}T_Interrupt;
// PHY 配置
typedef struct : ByteStruct
{
byte LNK:1; // 连接状态 1已连接 0连接断开
byte SPD:1; // 速度状态 1:100Mpbs 0:10Mbps 【只读】
byte DPX:1; // 双工状态 1全双工 0半双工 【只读】
byte OPMDC:3; // 如下OPMD
byte OPMD:1; // 1通过OPMDC配置0通过硬件引脚
// 配置引脚 PMODE[2:0]---- 也可以通过 PHYCFGR 寄存器进行配置
// 000 10BI半双工关闭自动协商
// 001 10BI全双工关闭自动协商
// 010 100BI半双工关闭自动协商
// 011 100BI全双工关闭自动协商
// 100 100BI半双工启用自动协商
// 101 未启用
// 110 未启用
// 111 所有功能,启用自动协商
byte RST:1; // 重启 为0时内部PHY 重启
// 重启后为1
}T_PHYCFGR;
/****************************** 基础类 ************************************/
// 硬件Socket控制器
class HardSocket : public ITransport, public Socket
{
private:
W5500& _Host; // W5500公共部分控制器
protected:
byte ReadConfig();
void WriteConfig(byte dat);
byte ReadStatus();
byte ReadInterrupt();
void WriteInterrupt(byte dat);
public:
bool Enable; // 启用
byte Index; // 使用的硬Socket编号 也是BSB选项的一部分
HardSocket(W5500& host, NetType protocol);
virtual ~HardSocket();
// 网卡状态输出
void StateShow();
// 打开Socket
virtual bool OnOpen();
virtual void OnClose();
// 应用配置,修改远程地址和端口
void Change(const IPEndPoint& remote);
//// 应用配置,修改远程地址和端口
//virtual bool Change(const String& remote, ushort port);
virtual bool OnWrite(const Buffer& bs);
virtual uint OnRead(Buffer& bs);
// 发送数据
virtual bool Send(const Buffer& bs);
// 接收数据
virtual uint Receive(Buffer& bs);
// 恢复配置
virtual void Recovery();
// 处理一些不需要异步处理的中断 减少异步次数
void Process();
virtual void OnProcess(byte reg) = 0;
// 用户注册的中断事件处理 异步调用
virtual void RaiseReceive() = 0;
// 清空所有接收缓冲区
void ClearRX();
};
class TcpClient : public HardSocket
{
public:
TcpClient(W5500& host): HardSocket(host, NetType::Tcp){ Init(); };
void Init();
virtual ~TcpClient();
virtual bool OnOpen();
virtual void OnClose();
bool Listen();
// 恢复配置,还要维护连接问题
virtual void Recovery();
// 中断分发 维护状态
virtual void OnProcess(byte reg);
// 用户注册的中断事件处理 异步调用
virtual void RaiseReceive();
virtual String& ToStr(String& str) const { return str + "Tcp_" + Local.Port; }
private:
bool Linked;
uint _tidRodyguard; // 维护 Link 状态的任务
static void RodyguardTask(void* param);
};
// UDP接收到的数据结构 RemoteIP(4 byte) + RemotePort(2 byte) + Length(2 byte) + Data(Length byte)
class UdpClient : public HardSocket
{
public:
UdpClient(W5500& host) : HardSocket(host, NetType::Udp) { }
virtual bool SendTo(const Buffer& bs, const IPEndPoint& remote);
// 中断分发 维护状态
virtual void OnProcess(byte reg);
// 用户注册的中断事件处理 异步调用
virtual void RaiseReceive();
virtual String& ToStr(String& str) const { return str + "Udp_" + Local.Port; }
private:
virtual bool OnWriteEx(const Buffer& bs, const void* opt);
};
/****************************** W5500 ************************************/
W5500::W5500() { Init(); }
W5500::W5500(Spi* spi, Pin irq, Pin rst)
{
Init();
Init(spi, irq, rst);
}
W5500::W5500(SPI spi, Pin irq, Pin rst)
{
auto spi_ = new Spi(spi, 36000000);
Init();
Init(spi_, irq, rst);
}
W5500::~W5500()
{
Close();
delete _spi;
}
// 初始化
void W5500::Init()
{
Name = "W5500";
Speed = 100;
_spi = nullptr;
Led = nullptr;
_Dns = nullptr;
_Dhcp = nullptr;
PhaseOM = 0x00;
RetryTime = 200;
RetryCount = 8;
LowLevelTime= 0;
PingACK = true;
TaskID = 0;
}
// 初始化
void W5500::Init(Spi* spi, Pin irq, Pin rst)
{
assert(spi, "spi");
//debug_printf("\r\n");
if(rst != P0) Rst.Init(rst, true);
if(irq != P0)
{
// 中断引脚初始化
debug_printf("W5500::Init IRQ = P%c%d RST = P%c%d\r\n", _PIN_NAME(irq), _PIN_NAME(rst));
//Irq.ShakeTime = 0;
Irq.Floating = false;
Irq.Pull = InputPort::UP;
//Irq.Mode = InputPort::Rising;
Irq.HardEvent = true;
Irq.Init(irq, true);
Irq.Press.Bind(&W5500::OnIRQ, this);
Irq.UsePress();
}
_spi = spi;
InitConfig();
LoadConfig();
}
void W5500::SetLed(Pin led)
{
if(led != P0)
{
auto port = new OutputPort(led);
SetLed(*port);
}
}
void W5500::SetLed(OutputPort& led)
{
auto fp = new FlushPort();
fp->Port = &led;
fp->Start();
Led = fp;
}
bool W5500::OnOpen()
{
net_printf("W5500::Open\r\n");
//ShowInfo();
net_printf("\tRst: ");
if(!Rst.Open()) return false;
//net_printf("硬件复位 \r\n");
Rst = true; // 低电平有效
Sys.Delay(600); // 最少500us
Rst = false;
net_printf("\tIrq: ");
Irq.Open();
net_printf("Reset=%d Irq=%d \r\n", Rst.Read(), Irq.Read());
// 先开SPI再复位否则可能有问题
_spi->Open();
// 复位
Reset();
// 读硬件版本
byte ver = 0;
TimeWheel tw(5000);
while(!tw.Expired() && !ver) ver = ReadByte(0x0039);
if(!ver)
{
OnClose();
debug_printf("W5500::Open 打开失败!无法识别硬件版本!\r\n");
return false;
}
net_printf("硬件版本: %02X\r\n", ver);
#if NET_DEBUG
//StateShow();
//PhyStateShow();
#endif
return true;
}
void W5500::OnClose()
{
net_printf("W5500::Close \r\n");
Sys.RemoveTask(TaskID);
_spi->Close();
Irq.Close();
Rst.Close();
}
bool W5500::OnLink(uint retry)
{
if(retry > 1 && !CheckLink()) return false;
debug_printf("等待PHY连接 ");
T_PHYCFGR phy;
phy.Init(0x00); // 先清空
TimeWheel tw(3000);
tw.Sleep = 50;
int temp = 0;
while(!tw.Expired() && !phy.LNK)
{
phy.Init(ReadByte(offsetof(TGeneral, PHYCFGR)));
//Sys.Sleep(50);
if(++temp == 10)
{
temp = 0;
debug_printf(".");
}
}
if(phy.LNK)
{
debug_printf("PHY已连接 ");
if(phy.SPD) { net_printf("100Mpbs ");}
else { net_printf("10Mpbs ");}
if(phy.DPX) { net_printf("全双工");}
else { net_printf("半双工");}
net_printf("网络");
debug_printf("\r\n");
}
else
{
net_printf("PHY连接异常\r\n");
//OnClose();
debug_printf("W5500::Link 连接失败!请检查网线!\r\n");
return false;
}
Config();
//设置发送缓冲区和接收缓冲区的大小参考W5500数据手册
for(int i=0; i<8; i++)
{
WriteByte(offsetof(TSocket, RXBUF_SIZE), 0x02, i+1); //Socket Rx memory size=2k
WriteByte(offsetof(TSocket, RXBUF_SIZE), 0x02, i+1); //Socket Tx mempry size=2k
}
// 为解决芯片有时候无法接收数据的问题,需要守护任务辅助
if(!TaskID)
{
int time = (Irq.Empty() || !Irq.HardEvent) ? 10 : -1;
TaskID = Sys.AddTask([](void* p){ ((W5500*)p)->OnIRQ(); }, this, time, time, "W5500中断");
auto task = Task::Get(TaskID);
task->MaxDeepth = 2; // 以太网允许重入,因为有时候在接收里面等待下一次接收
}
return true;
}
bool W5500::Config()
{
ShowConfig();
// 读所有寄存器
TGeneral gen;
Buffer bs(&gen, sizeof(gen));
ReadFrame(0, bs);
// 设置地址、网关、掩码、MAC等
Gateway .CopyTo(gen.GAR);
Mask .CopyTo(gen.SUBR);
Mac .CopyTo(gen.SHAR);
IP .CopyTo(gen.SIPR);
gen.RTR = RetryTime / 10;
gen.RCR = RetryCount;
// 启用所有Socket中断
gen.SIMR = 0xFF;
if(LowLevelTime > 0)
gen.INTLEVEL = LowLevelTime;
else
LowLevelTime = gen.INTLEVEL;
// 工作模式
T_Mode mr;
mr.Init(gen.MR);
mr.PB = PingACK ? 0 : 1; // 0 是有响应
mr.FARP = 1; // 强迫ARP模式下无论是否发送数据都会强迫ARP请求
gen.MR = mr.ToByte();
// 打开中断
T_Interrupt ir;
ir.Init(gen.IMR); // 中断屏蔽 0屏蔽
ir.UNREACH = 1; // 目标不可达
ir.CONFLICT = 1; // IP冲突
gen.IMR = ir.ToByte();
// 一次性全部写入
WriteFrame(0, bs);
return true;
}
void W5500::ChangePower(int level)
{
// 进入低功耗时,直接关闭
if(level) Close();
}
// 复位
void W5500::Reset()
{
T_Mode mr;
mr.Init();
mr.RST = 1;
WriteByte(offsetof(TGeneral, MR), mr.ToByte());
// 必须要等一会,否则初始化会失败
Sys.Delay(600); // 最少500us
TimeWheel tw(10);
while(!ReadByte(0x0039) && !tw.Expired());
}
// 网卡状态输出
void W5500::StateShow()
{
// 一次性全部读出
TGeneral gen;
ByteArray bs(&gen, sizeof(gen));
ReadFrame(0, bs);
net_printf("\r\nW5500::State\r\n");
net_printf("MR (模式): 0x%02X ", gen.MR);
T_Mode mr;
mr.Init(gen.MR);
net_printf("WOL: %d ",mr.WOL);
net_printf("PB: %d ",mr.PB);
net_printf("PPPoE: %d ",mr.PPPoE);
net_printf("FARP: %d ",mr.FARP);
net_printf("\r\n");
net_printf("GAR (网关地址): "); Gateway.Show(true);
net_printf("SUBR (子网掩码): "); Mask.Show(true);
net_printf("SHAR (源MAC地址): "); Mac.Show(true);
net_printf("SIPR (源IP地址): "); IP.Show(true);
net_printf("INTLEVEL(中断低电平时间): %d\r\n", LowLevelTime); // 回头计算一下
net_printf("IMR (中断屏蔽): 0x%02X ", gen.IMR);
T_Interrupt imr;
imr.Init(gen.IMR);
net_printf("CONFLICT: %d ",imr.CONFLICT);
net_printf("UNREACH: %d ",imr.UNREACH);
net_printf("PPPoE: %d ",imr.PPPoE);
net_printf("MP: %d ",imr.MP);
net_printf("\r\n");
net_printf("SIMR (Socket中断屏蔽): 0x%02X\r\n", gen.SIMR);
net_printf("RTR (重试时间): %d\r\n", RetryTime); // 回头计算一下
net_printf("RCR (重试计数): %d 次\r\n", RetryCount);
}
// 输出物理链路层状态
void W5500::PhyStateShow()
{
T_PHYCFGR phy;
phy.Init(ReadByte(offsetof(TGeneral, PHYCFGR)));
if(phy.OPMD)
{
net_printf("PHY 模式由 OPMDC 配置\r\n");
switch(phy.OPMDC)
{
case 0 : net_printf(" 10BI半双工关闭自动协商\r\n");break;
case 1 : net_printf(" 10BI全双工关闭自动协商\r\n");break;
case 2 : net_printf(" 100BI半双工关闭自动协商\r\n");break;
case 3 : net_printf(" 100BI全双工关闭自动协商\r\n");break;
case 4 : net_printf(" 100BI半双工启用自动协商\r\n");break;
case 7 : net_printf(" 所有功能,启用自动协商\r\n");break;
}
}
else
{
net_printf("PHY 模式由引脚配置\r\n");
}
if(phy.LNK)
{
net_printf("已连接 ");
if(phy.SPD) { net_printf("100Mpbs ");}
else { net_printf("10Mpbs ");}
if(phy.DPX) { net_printf("全双工");}
else { net_printf("半双工");}
net_printf("网络\r\n");
}
else
{
net_printf("连接已断开\r\n");
}
}
bool W5500::WriteByte(ushort addr, byte dat, byte socket, byte block)
{
SpiScope sc(_spi);
SetAddress(addr, 1, socket, block);
_spi->Write(dat);
return true;
}
byte W5500::ReadByte(ushort addr, byte socket, byte block)
{
SpiScope sc(_spi);
SetAddress(addr, 0, socket, block);
return _spi->Write(0x00);
}
bool W5500::WriteByte2(ushort addr, ushort dat, byte socket, byte block)
{
SpiScope sc(_spi);
SetAddress(addr, 1, socket, block);
_spi->Write((byte)dat);
_spi->Write(dat >> 8);
return true;
}
ushort W5500::ReadByte2(ushort addr, byte socket, byte block)
{
SpiScope sc(_spi);
SetAddress(addr, 0, socket, block);
ushort temp = _spi->Write(0x00);
temp += _spi->Write(0x00) << 8;
return temp;
}
void W5500::SetAddress(ushort addr, byte rw, byte socket, byte block)
{
// 地址高位在前
_spi->Write(addr >> 8);
_spi->Write(addr & 0xFF);
TConfig cfg;
cfg.Socket = socket;
cfg.BlockSelect = block; // 寄存器区域选择
cfg.OpMode = PhaseOM; // 类内整体
cfg.ReadWrite = rw; // 读写
_spi->Write(cfg.ToByte());
}
bool W5500::WriteFrame(ushort addr, const Buffer& bs, byte socket, byte block)
{
SpiScope sc(_spi);
SetAddress(addr, 1, socket, block);
_spi->Write(bs);
return true;
}
bool W5500::ReadFrame(ushort addr, Buffer& bs, byte socket, byte block)
{
SpiScope sc(_spi);
SetAddress(addr, 0, socket, block);
_spi->Read(bs);
return true;
}
// 测试PHY状态 返回是否LNK
bool W5500::CheckLink()
{
if(!Open()) return false;
T_PHYCFGR phy;
phy.Init(ReadByte(offsetof(TGeneral, PHYCFGR)));
return phy.LNK;
}
Socket* W5500::CreateSocket(NetType type)
{
if(Sockets.Count() >= 8)
{
debug_printf("没有空余的Socket可用了 !\r\n");
return nullptr;
}
HardSocket* socket = nullptr;
switch(type)
{
case NetType::Tcp:
socket = new TcpClient(*this);
case NetType::Udp:
socket = new UdpClient(*this);
default:
break;
}
if(socket)
{
socket->Index = Sockets.Count();
Sockets.Add(socket);
}
return socket;
}
// irq 中断处理部分
void W5500::OnIRQ(InputPort& port, bool down)
{
if(!down) return; // 低电平中断
Sys.SetTask(TaskID, true, 0);
}
void W5500::OnIRQ()
{
// 读出中断状态
byte dat = ReadByte(offsetof(TGeneral, IR));
if(dat != 0x00)
{
net_printf("W5500::OnIRQ 0x%02X \r\n", dat);
// 分析IR
T_Interrupt ir;
ir.Init(dat);
if(ir.CONFLICT)
{
// IP 冲突
net_printf("IP地址冲突 \r\n");
if (_Dhcp)((Dhcp*)_Dhcp)->Start();
}
if(ir.MP)
{
// 收到网络唤醒包
net_printf("收到网络唤醒包 \r\n");
}
if(ir.UNREACH)
{
#if NET_DEBUG
// 读出不可达 IP 的信息
ByteArray bs(6);
ReadFrame(offsetof(TGeneral, UIPR), bs); // UIPR + UPORTR
IPEndPoint ep(bs);
ep.Port = _REV16(ep.Port);
net_printf("IP端口 目标不可达:%s \r\n", ep.ToString().GetBuffer());
// 处理..
#endif
}
// PPPoE 连接不可达
if(ir.PPPoE)
{
}
// 清空 IR 中断
WriteByte(offsetof(TGeneral, IR), ir.ToByte());
}
dat = ReadByte(offsetof(TGeneral, SIR));
if(dat != 0x00)
{
// 设定小灯快闪时间,单位毫秒
if(Led) Led->Write(500);
byte dat2 = dat;
for(int i = 0; i < 8 && i < Sockets.Count(); i++)
{
if(dat2 & 0x01)
{
//net_printf("W5500::Socket[%d] 中断\r\n", i);
if(Sockets[i]) ((HardSocket*)Sockets[i])->Process();
}
dat2 >>= 1;
if(dat2 == 0x00) break;
}
// 清空 SIR 中断
WriteByte(offsetof(TGeneral, SIR), dat);
}
else
{
#if NET_DEBUG
//StateShow();
//PhyStateShow();
#endif
}
// 中断位清零 说明书说的不够清晰 有待完善
}
// DNS解析。默认仅支持字符串IP地址解析
IPAddress W5500::QueryDNS(const String& domain)
{
auto ip = IPAddress::Parse(domain);
if(ip != IPAddress::Any()) return ip;
if(_Dns) return _Dns(this, domain);
return ip;
}
static IPAddress FullQueryDNS(NetworkInterface* host, const String& domain)
{
return DNS::Query(*host, domain);
}
// 是否使用完整DNS
bool W5500::EnableDNS()
{
_Dns = FullQueryDNS;
return true;
}
/*static void OnDhcpStop(W5500& net, Dhcp& dhcp)
{
// DHCP成功或者失败且超过最大错误次数都要启动网关让它以上一次配置工作
if(dhcp.Result || dhcp.Times >= dhcp.MaxTimes)
{
net.NetReady(net);
}
}*/
// 启用DHCP
bool W5500::EnableDHCP()
{
if(_Dhcp) return true;
// 打开DHCP
auto dhcp = new Dhcp(*this);
//dhcp->OnStop.Bind(OnDhcpStop, this);
dhcp->Start();
_Dhcp = dhcp;
return true;
}
/****************************** 硬件Socket控制器 ************************************/
typedef struct : ByteStruct
{
byte Protocol:4; // Socket n 协议模式
// 0000 Closed
// 0001 TCP
// 0010 UDP
// 0100 MACRAW 【限定 Socket0 使用】
/* MACRAW 格式 *
* |***PACET_INFO**|********DATA_PACKET************** | *
* | PACKET_SIZE |DEC MAC|SOU MAC| TYPE | PAYLOAD | *
* | 2BYTE | 6BYTE | 6BYTE | 2BYTE|46-1500BYTE| */
byte UCASTB_MIP6B:1; // 【UCASTB】【MIP6B】
// [UCASTB] 单播阻塞 条件【UDP 模式】且MULTI=1
// 0关闭单播阻塞1开启单播阻塞
// [MIP6B] IPv6包阻塞 条件【MACRAW 模式】
// 0关闭IPv6包阻塞1开启IPv6包阻塞
byte ND_MC_MMB:1; // 【ND】【MC】【MMB】
// [ND] 使用无延时ACK 条件【TCP 模式】
// 0关闭无延时ACK选项需要RTR设定的超时时间做延时
// 1开启无延时ACK选项, 尽量没有延时的回复ACK
// [MC] IGMP版本 条件【UDP 模式】且MULTI=1
// 0IGMP 版本2
// 1IGMP 版本1
// [MMB] 多播阻塞 条件【MACRAW 模式】
// 0关闭多播阻塞
// 1开启多播阻塞
byte BCASTB:1; // 广播阻塞 条件【MACRAW 模式】或者【UDP 模式】
// 0关闭广播阻塞
// 1开启广播阻塞
byte MULTI_MFEN:1; // 【MULTI】【MFEN】
// [MULTI] UDP多播模式 条件【UDP 模式】
// 0关闭多播
// 1开启多播
// 注意使用多播模式需要DIPR DPORT 在通过CR 打开配置命令打开之前分别配置组播IP地址及端口号
// [MFEN] MAC 过滤 条件【MACRAW 模式】
// 0: 关闭 MAC 过滤,收到网络的所有包
// 1开启 MAC 过滤,只接收广播包和发给自己的包
}S_Mode;
// CR 寄存器比较特殊
// 写入后会自动清零但命令任然在处理中为验证命令是否完成需检查IR或者SnSR寄存器
enum S_Command
{
OPEN = 0x01, // open 之后,检测 SR 的值
// MR.Protocol SR
// 【CLOSE】 -
// 【TCP】 【SOCK_INT】0X13
// 【UDP】 【SOCK_UDP】0X22
// 【MACRAW】 【SOCK_MACRAW】0X02
LISTEN = 0x02, // 只在【TCP 模式】下生效 LISTEN 会把 Socket 变成服务端等待TCP连接
// LISTEN 把 SOCK_INIT 变为 SOCK_LISTEN
// 当 SOCK 被连接成功 SOCK_LISTEN 变为 SOCK_ESTABLISHE,同时 IR.CON =1
// 当连接非法断开 IR.TIMEOUT =1 SR = SOCK_CLOSED
CONNECT = 0x04, // 只在【TCP 模式】下生效 Socket 作为客户端 对 DIPR:DPORT 发起连接
// 链接成功时 SR = SOCK_ESTABLISHE IR.CON =1
// 三种情况意味着请求失败 SR = SOCK_CLOSED
// 1.ARP 发生超时(IR.TIMEOUT =1)
// 2.没有收到 SYN/ACK 数据包(IR.TIMEOUT = 1)
// 3.收到RST数据包
DISCON = 0x08, // 只在【TCP 模式】下生效 FIN/ACK 断开机制断开连接 (不分 客户端,服务端)
// SR = SOCK_CLOSED
// 另外 当断开请求没有收到ACK时 IR.TIMEOUT =1
// 使用 CLOSE 命令来代替 DISCON 的话SR = SOCK_CLOSED 不再执行 FIN/ACK 断开机制
// 收到RST数据包 将无条件 SR = SOCK_CLOSED
CLOSE = 0x10, // 关闭Socket SR = SOCK_CLOSED
SEND = 0x20, // 发送 Socket TX内存中的所有缓冲数据
// 一般来说先通常自动ARP获取到MAC才进行传输
SEND_MAC = 0x21, // 只在【UDP 模式】下生效
// 使用DHAR 中的MAC进行发送不进行ARP
SEND_KEEP = 0x22, // 只在【TCP 模式】下生效 通过发送1字节在线心跳包检查连接状态
// 如果对方不能在超时计数期内反馈在线心跳包,这个连接将被关闭并触发中断
RECV = 0x40, // 通过使用接收读指针寄存器 RX_RD 来判定 接收缓存是否完成接收处理
};
//中断寄存器
typedef struct : ByteStruct
{
byte CON:1; // 连接成功 SR = SOCK_ESTABLISHE 此位生效
byte DISCON:1; // 连接中断 接收到对方 FIN/ACK 断开机制数据包时 此位生效
byte RECV:1; // 无论何时,接收到对方数据包,此位生效
byte TIMEOUT:1; // ARP TCP 超时被触发
byte SEND_OK:1; // 发送完成
byte Reserved:3;
}S_Interrupt;
// 状态
enum S_Status
{
// 常规状态
SOCK_CLOSED = 0x00, // 关闭
SOCK_INIT = 0x13, // TCP 工作模式 OPEN 后状态
SOCK_LISTEN = 0x14, // TCP 服务端模式 等待 TCP 连接请求
SOCK_ESTABLISHE = 0x17, // TCP 连接OK 此状态下可以 Send 或者 Recv
SOCK_CLOSE_WAIT = 0x1C, // TCP 连接成功基础上 收到FIN 包可以进行数据传输若要全部关闭需要使用DISCON命令
SOCK_UDP = 0x22, // UDP 工作模式
SOCK_MACRAW = 0x02, // MACRAW 工作模式
// 中间状态
SOCK_SYNSENT = 0x15, // CONNECT 指令后(SOCK_INIT 状态后)SOCK_ESTABLISHE 状态前
SOCK_SYNRECV = 0x16, // 收到客户端连接请求包,发送了连接应答后,连接建立成功前
// SOCK_LISTEN 状态后SOCK_ESTABLISHE 状态前
// 如果超时 Sn_SR = SOCK_CLOSED Sn_IR.TIMEOUT =1
SOCK_FIN_WAIT = 0x18, // Socket 正在关闭
SOCK_CLOSING = 0x1A, // Socket 正在关闭
SOCK_TIME_WAIT = 0x1B, // Socket 正在关闭
SOCK_LAST_ACK = 0x1D, // Socket 被动关闭状态下,等待对方断开连接请求做出回应
// 超时或者成功收到断开请求都将 SOCK_CLOSED
};
#define SocRegWrite(P, D) _Host.WriteByte(offsetof(TSocket, P), D, Index, 0x01)
#define SocRegRead(P) _Host.ReadByte(offsetof(TSocket, P), Index, 0x01)
#define SocRegWrite2(P, D) _Host.WriteByte2(offsetof(TSocket, P), D, Index, 0x01)
#define SocRegRead2(P) _Host.ReadByte2(offsetof(TSocket, P), Index, 0x01)
#define SocRegWrites(P, D) _Host.WriteFrame(offsetof(TSocket, P), D, Index, 0x01)
#define SocRegReads(P, bs) _Host.ReadFrame(offsetof(TSocket, P), bs, Index, 0x01)
HardSocket::HardSocket(W5500& host, NetType protocol) : _Host(host)
{
MaxSize = 1500;
Host = &host;
Protocol = protocol;
}
HardSocket::~HardSocket()
{
_Host.Sockets.Remove(this);
}
byte HardSocket::ReadConfig() { return SocRegRead(CR); }
void HardSocket::WriteConfig(byte dat) { SocRegWrite(CR, dat); }
byte HardSocket::ReadStatus() { return SocRegRead(SR); }
byte HardSocket::ReadInterrupt() { return SocRegRead(IR); }
void HardSocket::WriteInterrupt(byte dat) { SocRegWrite(IR, dat); }
void HardSocket::StateShow()
{
TSocket soc;
ByteArray bs(&soc, sizeof(soc));
// 一次性全部读出
_Host.ReadFrame(0, bs, Index, 0x01);
//bs.CopyTo((byte*)&soc);
net_printf("\r\nW5500::Socket %d::State\r\n",Index);
net_printf("MR (模式): 0x%02X\r\n", soc.MR);
S_Mode mr;
mr.Init(soc.MR);
net_printf(" Protocol:");
switch(mr.Protocol)
{
case 0x00:net_printf(" Closed\r\n");break;
case 0x01:net_printf(" TCP\r\n");break;
case 0x02:net_printf(" UDP\r\n");break;
case 0x03:
{
if(Index == 0x00)net_printf(" MACRAW");
else net_printf(" ERROR\r\n");
break;
}
default:break;
}
net_printf(" UCASTB_MIP6B: %d\r\n",mr.UCASTB_MIP6B);
net_printf(" ND_MC_MMB: %d\r\n",mr.ND_MC_MMB);
net_printf(" BCASTB: %d\r\n",mr.BCASTB);
net_printf(" MULTI_MFEN: %d\r\n",mr.MULTI_MFEN);
//switch(mr.Protocol) // 不输出这么详细
//{
// case 0x00:break;
// case 0x01:break;
// case 0x02:
// if(mr.MULTI_MFEN)
// {
// net_printf("UDP ");
// if(mr.MULTI_MFEN)
// {
// net_printf("开启多播 ");
// if(mr.UCASTB_MIP6B)
// net_printf("开启单播阻塞 ");
// if(mr.ND_MC_MMB)
// net_printf("IGMP 版本1 ");
// else
// net_printf("IGMP 版本0 ");
// }
// net_printf("\r\n");
// }
// break;
// case 0x03:break;
//}
enum S_Status stat= *(enum S_Status*) &soc.SR;
net_printf("SR (状态): ");
switch(stat)
{
// 公共
case SOCK_CLOSED: net_printf("SOCK_CLOSED\r\n");break;
case SOCK_CLOSING: net_printf("SOCK_CLOSING\r\n");break;
case SOCK_SYNRECV: net_printf("SOCK_SYNRECV\r\n");break;
// TCP
case SOCK_INIT: net_printf("SOCK_INIT\r\n");break;
case SOCK_TIME_WAIT: net_printf("SOCK_TIME_WAIT\r\n");break;
case SOCK_LISTEN: net_printf("SOCK_LISTEN\r\n");break;
case SOCK_CLOSE_WAIT: net_printf("SOCK_CLOSE_WAIT\r\n");break;
case SOCK_FIN_WAIT: net_printf("SOCK_FIN_WAIT\r\n");break;
case SOCK_SYNSENT: net_printf("SOCK_SYNSENT\r\n");break;
case SOCK_LAST_ACK: net_printf("SOCK_LAST_ACK\r\n");break;
case SOCK_ESTABLISHE: net_printf("SOCK_ESTABLISHE\r\n");break;
// UDP
case SOCK_UDP: net_printf("SOCK_UDP\r\n");break;
case SOCK_MACRAW: net_printf("SOCK_MACRAW\r\n");break;
default:break;
}
S_Interrupt irqStat;
irqStat.Init(soc.IR);
net_printf("IR (中断状态): 0x%02X\r\n",soc.IR);
net_printf(" CON: %d\r\n",irqStat.CON);
net_printf(" DISCON: %d\r\n",irqStat.DISCON);
net_printf(" RECV: %d\r\n",irqStat.RECV);
net_printf(" TIMEOUT: %d\r\n",irqStat.TIMEOUT);
net_printf(" SEND_OK: %d\r\n",irqStat.SEND_OK);
net_printf("DPORT = 0x%02X%02X\r\n", soc.DPORT[1], soc.DPORT[0]);
}
bool HardSocket::OnOpen()
{
if(Index >= 8)
{
net_printf("Socket 0x%02X 编号不正确,打开失败\r\n", Index);
return false;
}
// 确保宿主打开
if(!_Host.Open()) return false;
// 如果有域名地址,尝试解析,失败后采用数字地址
if(Server)
{
auto ip = _Host.QueryDNS(Server);
if(ip != IPAddress::Any()) Remote.Address = ip;
}
if(Remote.Address == IPAddress::Any()) return false;
// 如果没有指定本地端口,则使用累加端口
if(!Local.Port)
{
// 累加端口
static ushort g_port = 1024;
// 随机拿到1024 至 1024+255 的端口号
if (g_port <= 1024)g_port += Sys.Ms() & 0xff;
Local.Port = g_port++;
}
Local.Address = _Host.IP;
#if DEBUG
debug_printf("%s::Open ", Protocol == NetType::Tcp ? "Tcp" : "Udp");
Local.Show(false);
debug_printf(" => ");
Server.Show(false);
debug_printf(" ");
Remote.Show(true);
#endif
// 设置分片长度参考W5500数据手册该值可以不修改
// 默认值udp 1472 tcp 1460 其他类型不管他 有默认不设置也没啥
if(Protocol == NetType::Udp)
SocRegWrite2(MSSR, 1472);
else if(Protocol == NetType::Tcp)
SocRegWrite2(MSSR, 1460);
// 设置自己的端口号
SocRegWrite2(PORT, _REV16(Local.Port));
// 设置端口目的(远程)IP地址
SocRegWrites(DIPR, Remote.Address.ToArray());
// 设置端口目的(远程)端口号
SocRegWrite2(DPORT, _REV16(Remote.Port));
// 设置Socket为UDP模式
S_Mode mode;
mode.Init();
if(Protocol == NetType::Tcp)
mode.Protocol = 0x01;
if(Protocol == NetType::Udp)
mode.Protocol = 0x02;
//if(Protocol == 0x02) mode.MULTI_MFEN = 1;
SocRegWrite(MR, mode.ToByte());
// 清空所有中断位
WriteInterrupt(0xFF);
// 打开所有中断形式
SocRegWrite(IMR, 0xFF);
WriteConfig(RECV);
// 打开Socket
WriteConfig(OPEN);
//Sys.Sleep(5); //延时5ms
//如果Socket打开失败
bool rs = false;
byte sr = 0;
TimeWheel tw(50);
while(!tw.Expired())
{
sr = ReadStatus();
if((Protocol == NetType::Tcp && sr == SOCK_INIT)
|| (Protocol == NetType::Udp && sr == SOCK_UDP))
{
rs = true;
break;
}
}
if(!rs)
{
debug_printf("protocol %d, Socket %d 打开失败 SR : 0x%02X \r\n", (byte)Protocol,Index, sr);
OnClose();
return false;
}
return true;
}
void HardSocket::OnClose()
{
WriteConfig(CLOSE);
WriteInterrupt(0xFF);
debug_printf("%s::Close ", Protocol == NetType::Tcp ? "Tcp" : "Udp");
Local.Show(false);
debug_printf(" => ");
Remote.Show(true);
}
// 应用配置,修改远程地址和端口
void HardSocket::Change(const IPEndPoint& remote)
{
#if DEBUG
/*debug_printf("%s::Open ", Protocol == 0x01 ? "Tcp" : "Udp");
Local.Show(false);
debug_printf(" => ");
remote.Show(true);*/
#endif
// 设置端口目的(远程)IP地址
SocRegWrites(DIPR, remote.Address.ToArray());
// 设置端口目的(远程)端口号
SocRegWrite2(DPORT, _REV16(remote.Port));
}
/*// 应用配置,修改远程地址和端口
bool HardSocket::Change(const String& remote, ushort port)
{
// 可能这个字符串是IP地址尝试解析
auto ip = IPAddress::Parse(remote);
if(ip == IPAddress::Any())
{
if(_Host.OnResolve) ip = _Host.OnResolve(&_Host, remote);
if(ip == IPAddress::Any()) return false;
}
Remote.Address = ip;
Remote.Port = port;
return true;
}*/
// 接收数据
uint HardSocket::Receive(Buffer& bs)
{
if(!Open()) return 0;
// 读取收到数据容量
ushort size = _REV16(SocRegRead2(RX_RSR));
if(size == 0)
{
// 没有收到数据时,需要给缓冲区置零,否则系统逻辑会混乱
bs.SetLength(0);
return 0;
}
// 读取收到数据的首地址
ushort offset = _REV16(SocRegRead2(RX_RD));
// 长度受 bs 限制时 最大读取bs.Lenth
if(size > bs.Length()) size = bs.Length();
// 设置 实际要读的长度
bs.SetLength(size);
_Host.ReadFrame(offset, bs, Index, 0x03);
// 更新实际物理地址,
SocRegWrite2(RX_RD, _REV16(offset + size));
// 生效 RX_RD
WriteConfig(RECV);
// 等待操作完成
// while(ReadConfig());
//返回接收到数据的长度
return size;
}
// 发送数据
bool HardSocket::Send(const Buffer& bs)
{
if(!Open()) return false;
/*debug_printf("%s::Send [%d]=", Protocol == 0x01 ? "Tcp" : "Udp", bs.Length());
bs.Show(true);*/
// 读取状态
byte st = ReadStatus();
// 不在UDP 不在TCP连接OK 状态下返回
if(!(st == SOCK_UDP || st == SOCK_ESTABLISHE))return false;
// 读取缓冲区空闲大小 硬件内部自动计算好空闲大小
ushort remain = _REV16(SocRegRead2(TX_FSR));
if( remain < bs.Length())return false;
// 读取发送缓冲区写指针
ushort addr = _REV16(SocRegRead2(TX_WR));
_Host.WriteFrame(addr, bs, Index, 0x02);
// 更新发送缓存写指针位置
addr += bs.Length();
SocRegWrite2(TX_WR,_REV16(addr));
// 启动发送 异步中断处理发送异常等
WriteConfig(SEND);
// 控制轮询任务,加快处理
Sys.SetTask(_Host.TaskID, true, 20);
return true;
}
bool HardSocket::OnWrite(const Buffer& bs) { return Send(bs); }
uint HardSocket::OnRead(Buffer& bs) { return Receive(bs); }
void HardSocket::ClearRX()
{
// 读取收到数据容量
ushort size = _REV16(SocRegRead2(RX_RSR));
// 读取收到数据的首地址
ushort offset = _REV16(SocRegRead2(RX_RD));
// 读指针直接 = 接收指针,即让数据区无效
SocRegWrite2(RX_RD, _REV16(offset + size));
// 生效 RX_RD
WriteConfig(RECV);
}
void HardSocket::Recovery()
{
Close();
// 直接重新打开好了
Open();
}
void HardSocket::Process()
{
byte reg = ReadInterrupt();
//debug_printf("Interrupt 0x%02X \r\n", reg);
OnProcess(reg);
// 清空中断位
WriteInterrupt(reg);
}
/****************************** TcpClient ************************************/
void TcpClient::Init()
{
Linked = 0;
// 关闭任务,等打开以后再开
if(!Opened) Sys.SetTask(_tidRodyguard, false);
}
TcpClient::~TcpClient() //: ~HardSockets()
{
//HardSocket::~HardSocket();
Sys.RemoveTask(_tidRodyguard);
}
void TcpClient::RodyguardTask(void* param)
{
TcpClient * tcp = (TcpClient*)param;
// 做一下判断,防止多线程状态不统一
if(!tcp->Linked)
{
}
}
bool TcpClient::OnOpen()
{
//SocketWrites(DIPR, Remote.Address.ToArray());
if(!HardSocket::OnOpen()) return false;
WriteConfig(CONNECT);
// 等待操作完成
while(ReadConfig());
// 等待3秒
TimeWheel tw(3000);
while(ReadStatus() != SOCK_SYNSENT)
{
if(ReadStatus() == SOCK_ESTABLISHE) return true;
S_Interrupt ir;
ir.Init(ReadInterrupt());
if(ir.TIMEOUT)
{
// 清除超时中断
WriteInterrupt(ir.ToByte());
return false;
}
if(tw.Expired())
{
debug_printf("TcpClient::OnOpen 连接超时 Status=0x%02X Interrupt=0x%02X \r\n", ReadStatus(), ReadInterrupt());
return false;
}
}
return true;
}
void TcpClient::OnClose()
{
WriteConfig(DISCON);
HardSocket::Close();
}
bool TcpClient::Listen()
{
if(!HardSocket::Open()) return false;
WriteConfig(LISTEN); //设置Socket为侦听模式
Sys.Sleep(5); //延时5ms
//如果socket设置失败
if(ReadStatus() != SOCK_LISTEN)
{
WriteConfig(CLOSE); //关闭Socket
return false;
}
return true;
}
// 恢复配置,还要维护连接问题
void TcpClient::Recovery(){}
//
void TcpClient::OnProcess(byte reg)
{
S_Interrupt ir;
ir.Init(reg);
if(ir.RECV)
{
RaiseReceive();
}
if(ir.CON)
{
Linked = true;
net_printf("W5500::OnProcess 连接成功\r\n");
}
if(ir.DISCON)
{
Opened = false;
Linked = false;
net_printf("W5500::OnProcess 断开\r\n");
}
/*if(ir.SEND_OK)
{
net_printf("W5500::OnProcess 发送完成\r\n");
}*/
// 超时直接判定掉线
if(ir.TIMEOUT)
{
Linked = false;
net_printf("W5500::OnProcess 超时\r\n");
}
if(Opened && !Linked)
{
/*
//net_printf("激活异步维护线程\r\n");
if(_tidRodyguard)
Sys.SetTask(_tidRodyguard, true, 0);
*/
}
// ir.SEND_OK 忽略不管
}
// 异步中断
void TcpClient::RaiseReceive()
{
byte buf[1500];
Buffer bs(buf, ArrayLength(buf));
int size = Receive(bs);
if(size > 1500)return;
// 回调中断
OnReceive(bs, nullptr);
}
/****************************** UdpClient ************************************/
bool UdpClient::SendTo(const Buffer& bs, const IPEndPoint& remote)
{
if(remote == Remote) return Send(bs);
Change(remote);
/*IPEndPoint ep = Remote;
Close();
Remote = remote;
Open();*/
bool rs = Send(bs);
/*Close();
Remote = ep;
Open();*/
Change(Remote);
return rs;
}
bool UdpClient::OnWriteEx(const Buffer& bs, const void* opt)
{
auto ep = (IPEndPoint*)opt;
if(!ep) return OnWrite(bs);
return SendTo(bs, *ep);
}
void UdpClient::OnProcess(byte reg)
{
S_Interrupt ir;
ir.Init(reg);
// UDP 模式下只处理 SendOK Recv 两种情况
/*net_printf("IR(中断状态): 0x%02X\r\n",ir.ToByte());
net_printf(" RECV: %d\r\n",ir.RECV);
net_printf(" SEND_OK: %d\r\n",ir.SEND_OK);*/
if(ir.RECV)
{
RaiseReceive();
}
#if NET_DEBUG
else if(ir.TIMEOUT)
{
net_printf("SEND_OK TIMEOUT\r\n");
}
#endif
// SEND OK 不需要处理 但要清空中断位
}
// UDP 异步只有一种情况 收到数据 可能有多个数据包
// UDP接收到的数据结构 RemoteIP(4 byte) + RemotePort(2 byte) + Length(2 byte) + Data(Length byte)
void UdpClient::RaiseReceive()
{
byte buf[1500];
Buffer bs(buf, ArrayLength(buf));
ushort size = Receive(bs);
Stream ms(bs.GetBuffer(), size);
// 拆包
while(ms.Remain())
{
IPEndPoint ep = ms.ReadArray(6);
ep.Port = _REV16(ep.Port);
ushort len = ms.ReadUInt16();
len = _REV16(len);
// 数据长度不对可能是数据错位引起的,直接丢弃数据包
if(len > 1500)
{
net_printf("W5500 UDP数据接收有误, ep=%s Length=%d \r\n", ep.ToString().GetBuffer(), len);
return;
}
// 回调中断
Buffer bs3(ms.ReadBytes(len), len);
OnReceive(bs3, &ep);
};
}