507 lines
20 KiB
C
507 lines
20 KiB
C
#include "includes.h"
|
||
#include "w5500_socket.h"
|
||
|
||
static u8 socket_num = 0;
|
||
static void *socket_priv[SOCKET_MAX_NUM] = {0};
|
||
/********************************************************************
|
||
*函数名:socket_malloc
|
||
*功 能:申请socket端口号:0-7
|
||
*参 数:无
|
||
*返回值:端口号
|
||
*********************************************************************/
|
||
static int socket_malloc(void *priv)
|
||
{
|
||
u8 i;
|
||
int ret = -1;
|
||
for (i = 0; i < SOCKET_MAX_NUM ; i++) {
|
||
if (!(socket_num & BIT(i)) && socket_priv[i] == NULL) {
|
||
socket_priv[i] = priv;
|
||
socket_num |= BIT(i);
|
||
ret = i;
|
||
break;
|
||
}
|
||
}
|
||
return ret;
|
||
}
|
||
/********************************************************************
|
||
*函数名:socket_free
|
||
*功 能:释放socket端口号:0-7
|
||
*参 数:port 端口号
|
||
*返回值:无
|
||
*********************************************************************/
|
||
static void socket_free(u8 port)
|
||
{
|
||
if (port >= SOCKET_MAX_NUM) {
|
||
return;
|
||
}
|
||
if (port & BIT(port) && socket_priv[port] != NULL) {
|
||
socket_priv[port] = NULL;
|
||
socket_num &= ~BIT(port);
|
||
}
|
||
}
|
||
/********************************************************************
|
||
*函数名:socket_err
|
||
*功 能:socket出现异常,需要确定socket或IP问题
|
||
*参 数:err出错参数:UNREACH CONFLICT
|
||
*返回值:无
|
||
*********************************************************************/
|
||
static void socket_err(u8 err)
|
||
{
|
||
for (u8 num = 0; num < SOCKET_MAX_NUM; num ++) {
|
||
SOCKET_INFO *socket = (SOCKET_INFO *)socket_priv[num];
|
||
if (socket && (err == UNREACH || err == CONFLICT)) { //Socket事件处理
|
||
socket->status |= IR_DISCON;//要求关闭
|
||
}
|
||
#if OS_SUPPORT
|
||
if (socket) {
|
||
os_sem_post(&socket->irq_sem);
|
||
}
|
||
#endif
|
||
}
|
||
}
|
||
/********************************************************************
|
||
*函数名:socket_port_rank
|
||
*功 能:socket本地端口号生成
|
||
*参 数:无
|
||
*返回值:无
|
||
*********************************************************************/
|
||
static unsigned short socket_port_rank(void)
|
||
{
|
||
#if 1
|
||
int i = 0, p = 0;
|
||
while (p < 1024 || p >= 65535) {
|
||
/*p = rand() % 65535;*/
|
||
p = CPU_RAND() % 65535;
|
||
i++;
|
||
if (i > 1000) {
|
||
p = LOCAL_DEFAULT_PORT;
|
||
break;
|
||
}
|
||
}
|
||
return (unsigned short)p;
|
||
#else
|
||
return LOCAL_DEFAULT_PORT;
|
||
#endif
|
||
}
|
||
/********************************************************************
|
||
*函数名:detect_gateway
|
||
*功 能:检查网关及获取网关的物理地址,检测网卡是否可用
|
||
*参 数:socket 网络socket结构体
|
||
*返回值:真 可用,假 不可用
|
||
*********************************************************************/
|
||
static int detect_gateway(SOCKET_INFO *socket)//检查网关服务器
|
||
{
|
||
u8 ip_adde[4];
|
||
u8 j = 0;
|
||
|
||
ip_adde[0] = ((socket->local_ip >> 24) & 0xff) + 1;
|
||
ip_adde[1] = ((socket->local_ip >> 16) & 0xff) + 1;
|
||
ip_adde[2] = ((socket->local_ip >> 8) & 0xff) + 1;
|
||
ip_adde[3] = ((socket->local_ip >> 0) & 0xff) + 1;
|
||
|
||
Write_W5500_SOCK_4Byte(socket->socket_port, Sn_DIPR, ip_adde); //向目的地址寄存器写入与本机IP不同的IP值,检查网关及获取网关的物理地址
|
||
|
||
if (socket->prot == TCP_PROTOCOL) { //TCP
|
||
Write_W5500_SOCK_1Byte(socket->socket_port, Sn_MR, MR_TCP); //设置socket为TCP模式
|
||
Write_W5500_SOCK_1Byte(socket->socket_port, Sn_CR, OPEN); //打开Socket
|
||
socket_delay_ms(10);//延时10ms
|
||
|
||
if (Read_W5500_SOCK_1Byte(socket->socket_port, Sn_SR) != SOCK_INIT) { //如果socket打开失败
|
||
Write_W5500_SOCK_1Byte(socket->socket_port, Sn_CR, CLOSE); //打开不成功,关闭Socket
|
||
socket_log("%s %d err \n", __FUNCTION__, __LINE__);
|
||
return FALSE; //返回FALSE(0x00)
|
||
}
|
||
|
||
Write_W5500_SOCK_1Byte(socket->socket_port, Sn_CR, CONNECT); //设置Socket为Connect模式
|
||
|
||
do {
|
||
j = Read_W5500_SOCK_1Byte(socket->socket_port, Sn_IR); //读取Socket中断标志寄存器
|
||
if (j != 0) {
|
||
Write_W5500_SOCK_1Byte(socket->socket_port, Sn_IR, j); //清空中断标志
|
||
}
|
||
socket_delay_ms(10);//延时10ms
|
||
if ((j & IR_TIMEOUT) == IR_TIMEOUT) {
|
||
socket_log("%s %d err \n", __FUNCTION__, __LINE__);
|
||
return FALSE;
|
||
} else if (Read_W5500_SOCK_1Byte(socket->socket_port, Sn_DHAR) != 0xff) {
|
||
Write_W5500_SOCK_1Byte(socket->socket_port, Sn_CR, CLOSE); //关闭Socket
|
||
return TRUE;
|
||
}
|
||
} while (1);
|
||
} else if (socket->prot == UDP_PROTOCOL) { //UDP
|
||
|
||
}
|
||
return TRUE;
|
||
}
|
||
/********************************************************************
|
||
*函数名:socket_set_timer_out
|
||
*功 能:设置socket超时时间
|
||
*参 数:socket 网络socket结构体
|
||
*返回值:无
|
||
*********************************************************************/
|
||
static void socket_set_timer_out(SOCKET_INFO *socket, u32 time)
|
||
{
|
||
socket->time_out = (u32)time;
|
||
}
|
||
/********************************************************************
|
||
*函数名:socket_interrupt
|
||
*功 能:硬件中断回调函数
|
||
*参 数:无
|
||
*返回值:无
|
||
*********************************************************************/
|
||
void socket_interrupt(void)
|
||
{
|
||
SOCKET_INFO *socket;
|
||
unsigned char i, j, num;
|
||
unsigned int cnt = 0;
|
||
|
||
i = Read_W5500_1Byte(IR);//读取中断标志寄存器
|
||
Write_W5500_1Byte(IR, (i & 0xf0)); //回写清除中断标志
|
||
if ((i & CONFLICT) == CONFLICT) { //IP地址冲突异常处理
|
||
socket_log("warning ip address conflict !!!!!!!!!!!!!!!!!!\r\n\r\n");
|
||
/*socket_err(CONFLICT);*/
|
||
}
|
||
if ((i & UNREACH) == UNREACH) { //地址无法到达异常处理
|
||
socket_log("warning invalid ip address !!!!!!!!!!!!!!!!!!\r\n\r\n");//可能是数据发太猛
|
||
/*socket_err(UNREACH);*/
|
||
}
|
||
if ((i = Read_W5500_1Byte(SIR)) != 0) { //读取端口中断标志寄存器
|
||
for (num = 0; num < SOCKET_MAX_NUM; num ++) {
|
||
socket = (SOCKET_INFO *)socket_priv[num];
|
||
if (socket && (i & BIT(socket->socket_port))) { //Socket事件处理
|
||
j = Read_W5500_SOCK_1Byte(socket->socket_port, Sn_IR); //读取Socket中断标志寄存器
|
||
if (!j) { //无标记则跳过
|
||
continue;
|
||
}
|
||
Write_W5500_SOCK_1Byte(socket->socket_port, Sn_IR, j); //清空标志
|
||
socket_lock(socket->lock);
|
||
if (j & IR_CON) { //在TCP模式下,Socket成功连接
|
||
socket->status |= IR_CON; //网络连接状态0x02,端口完成连接,可以正常传输数据
|
||
}
|
||
if (j & IR_DISCON) { //在TCP模式下Socket断开连接处理
|
||
Write_W5500_SOCK_1Byte(socket->socket_port, Sn_CR, CLOSE); //关闭端口,等待重新打开连接
|
||
socket->status |= IR_DISCON; //网络连接状态0x00,端口连接失败
|
||
}
|
||
if (j & IR_SEND_OK) { //Socket数据发送完成
|
||
socket->status |= IR_SEND_OK; //端口发送一个数据包完成
|
||
}
|
||
if (j & IR_RECV) { //Socket接收到数据,可以启动S_rx_process()函数
|
||
socket->status |= IR_RECV; //端口接收到一个数据包
|
||
}
|
||
if (j & IR_TIMEOUT) { //Socket连接或数据传输超时处理
|
||
Write_W5500_SOCK_1Byte(socket->socket_port, Sn_CR, CLOSE); // 关闭端口,等待重新打开连接
|
||
socket->status |= IR_TIMEOUT;
|
||
}
|
||
socket_unlock(socket->lock);
|
||
#if OS_SUPPORT
|
||
os_sem_post(&socket->irq_sem);
|
||
#endif
|
||
}
|
||
}
|
||
}
|
||
}
|
||
/********************************************************************
|
||
*函数名:socket_create
|
||
*功 能:创建一个socket
|
||
*参 数:socket 网络socket结构体
|
||
*返回值:0 成功,小于0 失败
|
||
*********************************************************************/
|
||
int socket_create(SOCKET_INFO *socket)
|
||
{
|
||
char cnt = 0;
|
||
int ret = socket_malloc(socket);//申请socket端口号 0-7
|
||
if (ret < 0) {
|
||
socket_log("err no enough port\n");
|
||
return -EFAULT;
|
||
}
|
||
socket->socket_port = (u8)ret;
|
||
if (!socket->local_ip) {
|
||
socket->local_ip = W5500_ip_addr_get();
|
||
}
|
||
if (!socket->local_port && socket->mode == TCP_SERVER_MODE) {
|
||
socket_log("err unknown port = %d\n", socket->local_port);
|
||
return -EFAULT;
|
||
}
|
||
if (!socket->local_port) {
|
||
socket->local_port = htons(socket_port_rank());
|
||
}
|
||
redo:
|
||
if (!detect_gateway(socket)) { //检查网关、获取网关的物理地址,检测网卡是否可用
|
||
cnt++;
|
||
if (cnt < 5) {
|
||
socket_delay_ms(100);
|
||
goto redo;
|
||
}
|
||
socket_free(socket->socket_port);
|
||
return -EFAULT;
|
||
}
|
||
#if OS_SUPPORT
|
||
os_sem_create(&socket->irq_sem, 0);
|
||
#endif
|
||
if (socket->prot == TCP_PROTOCOL) { //TCP
|
||
Write_W5500_SOCK_2Byte(socket->socket_port, Sn_MSSR, SOCKET_MAX_SIZE); //最大分片字节数=1460,设置分片长度,参考W5500数据手册,该值可以不修改
|
||
Write_W5500_SOCK_2Byte(socket->socket_port, Sn_PORT, socket->local_port); //设置端口的端口号,在服务器侦听模式不需要设置目的IP和目的端口号
|
||
if (socket->mode == TCP_CLIENT_MODE) {
|
||
Write_W5500_SOCK_2Byte(socket->socket_port, Sn_DPORTR, socket->remote_port);//设置端口目的(远程)端口号
|
||
Write_W5500_SOCK_4Byte(socket->socket_port, Sn_DIPR, (u8 *)&socket->remote_ip); //设置端口目的(远程)IP地址
|
||
}
|
||
} else if (socket->prot == UDP_PROTOCOL) { //UDP
|
||
Socket_UDP(socket->socket_port);
|
||
Write_W5500_SOCK_2Byte(socket->socket_port, Sn_DPORTR, socket->remote_port); //设置目的主机端口号
|
||
Write_W5500_SOCK_4Byte(socket->socket_port, Sn_DIPR, (u8 *)&socket->remote_ip); //设置目的主机IP
|
||
}
|
||
socket->status = 0;
|
||
socket->lock = 0;
|
||
|
||
//启动中断,参考W5500数据手册确定自己需要的中断类型
|
||
//IMR_CONFLICT是IP地址冲突异常中断,IMR_UNREACH是UDP通信时,地址无法到达的异常中断
|
||
//其它是Socket事件中断,根据需要添加
|
||
Write_W5500_1Byte(IMR, IM_IR7 | IM_IR6);
|
||
Write_W5500_1Byte(SIMR, BIT(socket->socket_port));
|
||
Write_W5500_SOCK_1Byte(socket->socket_port, Sn_IMR, IMR_SENDOK | IMR_TIMEOUT | IMR_RECV | IMR_DISCON | IMR_CON);
|
||
return ENONE;
|
||
}
|
||
/********************************************************************
|
||
*函数名:socket_accept
|
||
*功 能:等待客户端连接(TCP服务器模式,注意:只能一个客户端连接)
|
||
*参 数:socket 网络socket结构体,accpet_timeout 等待超时时间(0永不超时,单位ms)
|
||
*返回值:0 成功, 小于0 失败
|
||
*********************************************************************/
|
||
int socket_accept(SOCKET_INFO *socket, u32 accpet_timeout)//TCP 服务器模式下,注意:只能一个客户端连接
|
||
{
|
||
int ret = -EFAULT;
|
||
u32 timeout = accpet_timeout;
|
||
u32 cnt = 0;
|
||
u32 retry = 0;
|
||
|
||
if (socket->prot == TCP_PROTOCOL) { //TCP
|
||
if (socket->mode == TCP_SERVER_MODE) { //服务器
|
||
redo:
|
||
if (!Socket_Listen(socket->socket_port)) {
|
||
retry++;
|
||
if (retry < 50) {
|
||
socket_log("listen retry against ...\n");
|
||
socket_delay_ms(500);
|
||
goto redo;
|
||
} else {
|
||
socket_log("err in function %s \n", __FUNCTION__);
|
||
socket_log("retry against err , please check the net line \n");
|
||
}
|
||
} else {
|
||
socket_log("local_ip : %d:%d:%d:%d listen port : %d , wait connet ...\n",
|
||
(u8)(socket->local_ip >> 24),
|
||
(u8)(socket->local_ip >> 16),
|
||
(u8)(socket->local_ip >> 8),
|
||
(u8)(socket->local_ip & 0xFF),
|
||
socket->local_port);
|
||
while (1) { //阻塞等待客户端连接
|
||
cnt++;
|
||
#if OS_SUPPORT
|
||
if (cnt > 1 || os_sem_pend(&socket->irq_sem, timeout ? timeout / 10 : 0)) {
|
||
os_sem_set(&socket->irq_sem, 0);
|
||
return -ETIMOUT;
|
||
}
|
||
#else
|
||
socket_delay_ms(10);
|
||
if (timeout && ((cnt * 10) > timeout)) { //accpet超时
|
||
return -ETIMOUT;
|
||
}
|
||
#endif
|
||
socket_lock(socket->lock);
|
||
if (socket->status & IR_CON) { //网络连接状态0x01,端口完成连接,可以正常传输数据
|
||
socket->status &= ~IR_CON; //清空标记
|
||
ret = ENONE;
|
||
socket_unlock(socket->lock);
|
||
break;
|
||
}
|
||
socket_unlock(socket->lock);
|
||
}
|
||
socket_set_timer_out(socket, socket->time_out);
|
||
socket_log("client conneted \n");
|
||
}
|
||
}
|
||
}
|
||
return ret;
|
||
}
|
||
/********************************************************************
|
||
*函数名:socket_connect
|
||
*功 能:连接服务器(TCP服务器模式)
|
||
*参 数:socket 网络socket结构体
|
||
*返回值:0 成功, 小于0 失败
|
||
*********************************************************************/
|
||
int socket_connect(SOCKET_INFO *socket)//TCP 客户端模式下
|
||
{
|
||
u32 retry = 0;
|
||
u32 cnt = 0;
|
||
u32 timeout = socket->time_out;
|
||
int ret = -EFAULT;
|
||
if (socket->prot == TCP_PROTOCOL) { //TCP
|
||
if (socket->mode == TCP_CLIENT_MODE) {
|
||
redo:
|
||
if (!Socket_Connect(socket->socket_port)) {
|
||
retry++;
|
||
if (retry < 50) {
|
||
socket_log("connect retry against ...\n");
|
||
socket_delay_ms(50);
|
||
goto redo;
|
||
} else {
|
||
socket_log("err in function %s \n", __FUNCTION__);
|
||
socket_log("retry against err , please check the net line \n");
|
||
}
|
||
} else {
|
||
while (1) {
|
||
cnt++;
|
||
#if OS_SUPPORT
|
||
if (cnt > 1 || os_sem_pend(&socket->irq_sem, timeout ? timeout / 10 : 0)) {
|
||
os_sem_set(&socket->irq_sem, 0);
|
||
return -ETIMOUT;
|
||
}
|
||
#else
|
||
socket_delay_ms(10);
|
||
if (timeout && cnt > timeout) {
|
||
return -ETIMOUT;//接收超时
|
||
}
|
||
#endif
|
||
socket_lock(socket->lock);
|
||
if (socket->status & IR_DISCON) { //在TCP模式下Socket断开连接处理
|
||
socket->status &= ~IR_CON; //清空标记
|
||
socket_unlock(socket->lock);
|
||
return -EDISCON;
|
||
}
|
||
if (socket->status & IR_TIMEOUT) { //Socket连接或数据传输超时处理
|
||
socket->status &= ~IR_TIMEOUT; //清空标记
|
||
socket_unlock(socket->lock);
|
||
return -ETIMOUT;
|
||
}
|
||
if (socket->status & IR_CON) { //Socket连接成功
|
||
socket->status &= ~IR_CON; //清空标记
|
||
socket_unlock(socket->lock);
|
||
break;
|
||
}
|
||
socket_unlock(socket->lock);
|
||
}
|
||
ret = ENONE;
|
||
}
|
||
}
|
||
}
|
||
return ret;
|
||
}
|
||
/********************************************************************
|
||
*函数名:socket_recv
|
||
*功 能:连接服务器(TCP服务器模式)
|
||
*参 数:socket 网络socket结构体,buf接收数据地址,size 数据地址长度
|
||
*返回值:大于0 成功接收字节数, 小于等于0 失败
|
||
*********************************************************************/
|
||
int socket_recv(SOCKET_INFO *socket, u8 *buf, u32 size)
|
||
{
|
||
u32 cnt = 0;
|
||
u32 timeout = socket->time_out;
|
||
u16 recv;
|
||
if (size > SOCKET_MAX_SIZE) {
|
||
socket_log("err recv size %d > %d \n", size, SOCKET_MAX_SIZE);
|
||
return -EFAULT;
|
||
}
|
||
|
||
wait:
|
||
while (1) {
|
||
cnt++;
|
||
#if OS_SUPPORT
|
||
if (cnt > 1 || os_sem_pend(&socket->irq_sem, timeout ? timeout / 10 : 0)) {
|
||
os_sem_set(&socket->irq_sem, 0);
|
||
return -ETIMOUT;
|
||
}
|
||
#else
|
||
socket_delay_ms(10);
|
||
if (timeout && cnt > timeout) {
|
||
return -ETIMOUT;//接收超时
|
||
}
|
||
#endif
|
||
socket_lock(socket->lock);
|
||
if (socket->status & IR_DISCON) { //在TCP模式下Socket断开连接处理
|
||
socket->status &= ~IR_CON; //清空标记
|
||
socket_unlock(socket->lock);
|
||
return -EDISCON;
|
||
}
|
||
if (socket->status & IR_TIMEOUT) { //Socket连接或数据传输超时处理
|
||
socket->status &= ~IR_TIMEOUT; //清空标记
|
||
socket_unlock(socket->lock);
|
||
return -ETIMOUT;
|
||
}
|
||
if (socket->status & IR_RECV) { //Socket接收到数据
|
||
socket->status &= ~IR_RECV; //清空标记
|
||
socket_unlock(socket->lock);
|
||
break;
|
||
}
|
||
socket_unlock(socket->lock);
|
||
}
|
||
recv = Read_SOCK_Data_Buffer(socket->socket_port, buf);//读取一包数据,一次最大读取1460个字节
|
||
if (recv > size) {
|
||
socket_log("err in recv buff , recv size %d , buff size : %d \n", recv, size);
|
||
return -ENOMEM;
|
||
} else if (recv == 0) {
|
||
goto wait;
|
||
}
|
||
if (socket->prot == UDP_PROTOCOL) { //注意:UDP接收带有8字节头部
|
||
recv -= 8;
|
||
memcpy(buf, buf + 8, recv);
|
||
}
|
||
return recv;
|
||
}
|
||
/********************************************************************
|
||
*函数名:socket_send
|
||
*功 能:连接服务器(TCP服务器模式)
|
||
*参 数:socket 网络socket结构体,buf接收数据地址,size 数据地址长度
|
||
*返回值:大于0 成功发送字节数, 小于等于0 失败
|
||
*********************************************************************/
|
||
int socket_send(SOCKET_INFO *socket, u8 *buf, u32 size)
|
||
{
|
||
u32 cnt = 0;
|
||
u32 timeout = socket->time_out;
|
||
|
||
//如果是UDP模式,可以在此设置目的主机的IP和端口号
|
||
if ((Read_W5500_SOCK_1Byte(socket->socket_port, Sn_MR) & 0x0f) != SOCK_UDP) { //如果Socket打开失败
|
||
Write_W5500_SOCK_2Byte(socket->socket_port, Sn_DPORTR, socket->remote_port);//设置端口目的(远程)端口号
|
||
Write_W5500_SOCK_4Byte(socket->socket_port, Sn_DIPR, (u8 *)&socket->remote_ip); //设置端口目的(远程)IP地址
|
||
}
|
||
Write_SOCK_Data_Buffer(socket->socket_port, buf, size);
|
||
while (1) {
|
||
cnt++;
|
||
#if OS_SUPPORT
|
||
if (cnt > 1 || os_sem_pend(&socket->irq_sem, timeout ? timeout / 10 : 0)) {
|
||
os_sem_set(&socket->irq_sem, 0);
|
||
return -ETIMOUT;
|
||
}
|
||
#else
|
||
socket_delay_ms(10);
|
||
if (timeout && cnt > timeout) {
|
||
return -ETIMOUT;//发送超时
|
||
}
|
||
#endif
|
||
socket_lock(socket->lock);
|
||
if (socket->status & IR_SEND_OK) {
|
||
socket->status &= ~IR_SEND_OK; //发送成功,清空标志
|
||
socket_unlock(socket->lock);
|
||
break;
|
||
}
|
||
socket_unlock(socket->lock);
|
||
}
|
||
return size;
|
||
}
|
||
/********************************************************************
|
||
*函数名:socket_close
|
||
*功 能:关闭socket
|
||
*参 数:socket 网络socket结构体
|
||
*返回值:无
|
||
*********************************************************************/
|
||
void socket_close(SOCKET_INFO *socket)
|
||
{
|
||
Write_W5500_SOCK_1Byte(socket->socket_port, Sn_CR, DISCON);
|
||
Write_W5500_SOCK_1Byte(socket->socket_port, Sn_CR, CLOSE);
|
||
socket_free(socket->socket_port);//释放socket端口号0-7
|
||
#if OS_SUPPORT
|
||
os_sem_del(&socket->irq_sem, 0);
|
||
#endif
|
||
memset(socket, 0, sizeof(SOCKET_INFO));
|
||
}
|
||
|