doubao_chat/apps/common/eth/w5500/w5500_socket.c

507 lines
20 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 "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));
}