Files
STM32F4-Base/Drivers/BSP/NET/net_socket.c

1656 lines
62 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.
/*
* 模块名称Network Socket Core
* 模块功能BSD Socket API 核心实现,包含 Socket 管理、状态机、消息队列(线程安全)、事件处理、数据收发等功能,底层调用 CH395F 驱动
* 适用平台STM32F407ZGTx + CH395F 以太网芯片
* 作者:王建锋
* 创建日期2026-07-18
* 修改记录:
* 2026-07-18 王建锋 创建初始版本
* 2026-07-19 王建锋 添加消息队列线程安全机制,替换 xQueueCreate 为 CubeMX osMessageQueue
*/
#include <string.h>
#include <stdio.h>
#include "FreeRTOS.h"
#include "task.h"
#include "cmsis_os.h"
#include "net_socket.h"
#include "ch395f.h"
#include "main.h"
/*
* 调试输出配置
*/
#define DBG_TAG "[NET]"
#include "dbg_log.h"
/*
* 私有宏定义区
*/
#define NET_PHY_CHECK_INTERVAL_MS 5000U /* PHY 状态定期检查间隔ms */
/*
* 全局变量定义区 - 尽量避免使用全局变量
*/
static net_sock_t s_net_socks[NET_MAX_SOCKETS]; /* Socket 控制块数组 */
static int s_net_errno = 0; /* 最后错误码 */
static uint8_t s_net_initialized = 0; /* 初始化标志 */
static uint8_t s_local_ip[4]; /* 本机 IP 缓存网络序net_getsockname 用) */
static uint16_t s_dynamic_port = 49152; /* 动态端口分配起始值 */
/*
* 消息队列 - 线程安全核心
* 外部任务通过 net_send/net_recv/net_close 等 API 将请求放入队列,
* netTask 调用 net_process_messages() 串行处理所有 CH395F 操作
* 由 CubeMX 在 MX_FREERTOS_Init()osKernelInitialize 之后)中创建
*/
extern osMessageQueueId_t netMsgQueueHandle; /* CubeMX 生成的消息队列句柄 */
/*
* 私有函数声明区
*/
static int alloc_socket(void);
static void handle_connect_event(net_sock_t *p_sock);
static void handle_disconnect_event(net_sock_t *p_sock);
static void handle_recv_event(net_sock_t *p_sock);
static void handle_send_event(net_sock_t *p_sock);
static void handle_timeout_event(net_sock_t *p_sock);
static void fire_event(net_sock_t *p_sock, net_event_t event);
static void parse_ip_string(const char *str, uint8_t *ip_arr);
static int net_send_locked(net_sock_t *p_sock, const void *buf, int len);
static int net_recv_locked(net_sock_t *p_sock, void *buf, int len, int flags);
static int net_close_locked(net_sock_t *p_sock);
static int net_accept_locked(int sockfd, struct net_sockaddr *addr, int *addrlen);
/*
* 函数功能:网络子系统初始化(配置 CH395F + 启用多连接模式)
* 入口参数ip - IP 地址字符串 "192.168.1.100"NULL 使用 DHCP 自动获取 const char *
* mask - 子网掩码字符串 "255.255.255.0"NULL 使用默认值 const char *
* gateway - 网关地址字符串 "192.168.1.1"NULL 使用默认值 const char *
* 返回值0 - 初始化成功 int
* -1 - 初始化失败(芯片检测/复位/协议栈初始化失败) int
* 限定条件SPI2 已正确初始化netTask 尚未启动
* 函数说明1. 检查 CH395F 芯片是否存在并复位
* 2. 启用 TCP Server 多连接模式bit1=1设置 TCP MSS、重传参数、KeepAlive 参数
* 3. 配置 IP/网关/子网掩码,或启用 DHCP
* 4. 初始化 CH395F 协议栈并使能 PING 响应
*/
int net_init(const char *ip, const char *mask, const char *gateway) {
ch395f_status_t ret;
DBG_INFO("net_init: start");
/* 清零 Socket 控制块 */
memset(s_net_socks, 0, sizeof(s_net_socks));
s_net_errno = 0;
s_net_initialized = 0;
DBG_INFO("net_init: queue=%p", (void *)netMsgQueueHandle);
DBG_INFO("net_init: check_exist...");
/* 检查 CH395F 芯片 */
if (ch395f_check_exist() != CH395F_STATUS_OK) {
DBG_ERROR("net_init: ch395f_check_exist FAILED");
s_net_errno = NET_ERR;
return -1;
}
DBG_INFO("net_init: reset...");
/* 复位芯片 */
ch395f_reset();
DBG_INFO("net_init: set_fun_para...");
/* 启用单连接模式 + 手动关闭 Socketbit3=1, bit1=0
* bit3=1: DISCONNECT/TIMEOUT 时不自动清空缓冲区,由软件排空数据后手动关闭 */
ch395f_set_fun_para(0x08);
DBG_INFO("net_init: set_tcp_mss...");
/* 设置 TCP MSS */
ch395f_set_tcp_mss(1460);
/* 设置 TCP 重传参数(必须在 INIT_CH395 之前调用*/
ch395f_set_retrans_count(8); /* 重传 8 次*/
ch395f_set_retrans_period(500); /* 每次间隔 500ms总计 4s 重传窗口 */
/* 设置 TCP KeepAlive 参数(必须在 INIT_CH395 之前,单位 ms必须为 500 的倍数IDLE > INTVL<56>?*/
ch395f_set_keepalive_idle(60000); /* 空闲 60 秒后开始探测*/
ch395f_set_keepalive_intvl(5000); /* 5 秒探测一次*/
ch395f_set_keepalive_cnt(3); /* 最多探 3 次*/
/* 设置网络参数(必须在 INIT_CH395 之前调用 */
if (NULL != ip) {
uint8_t ip_arr[4];
uint8_t mask_arr[4];
uint8_t gw_arr[4];
parse_ip_string(ip, ip_arr);
parse_ip_string(mask, mask_arr);
parse_ip_string(gateway, gw_arr);
memcpy(s_local_ip, ip_arr, 4);
ch395f_set_ip_addr(ip_arr);
ch395f_set_gwip_addr(gw_arr);
ch395f_set_mask_addr(mask_arr);
} else {
/* 启用 DHCP */
ch395f_set_dhcp(1);
}
/* 初始化 CH395F 协议栈(先不配置 buffer测试默认值是否工作 */
ret = ch395f_init();
if (ret != CH395F_STATUS_OK) {
s_net_errno = NET_ERR;
return -1;
}
/* 使能 PING 响应 */
ch395f_ping_enable(1);
s_net_initialized = 1;
return 0;
}
/*
* 函数功能:轮询 CH395F 中断状态,更新 Socket 连接状态并处理事件
* 返回值:本次轮询中发生事件的 Socket 数量 int >= 0
* 限定条件s_net_initialized == 1必须在 netTask 上下文中调用
* 函数说明1. GPIO 轮询 CH395F INT# 引脚电平(低电平有效),无需 EXTI 中断
* 2. 读取 GET_GLOB_INT_STATUS_ALL2 字节版)获取所有 Socket 的中断状态
* 3. 处理连接/断开/数据接收/发送完成/超时等事件,更新 Socket 状态机
* 4. RECV 中断为电平触发,每次 net_poll() 只处理一批中断,避免主循环饿死
*/
int net_poll(void) {
/* 本次轮询累计发生的事件数(连接/断开/接收/发送/超时各计 1 */
int event_count = 0;
/* 全局中断状态寄存器值CH395F GET_GLOB_INT_STATUS_ALL 为 16 位) */
uint16_t int_status;
/* 防御性检查:尚未初始化则不处理任何中断,直接返回 */
if (!s_net_initialized) {
return 0;
}
/*
* 采用 do-while(0) 实现“单次批处理”:
* 本函数每被调用一次只读取并处理一批中断,处理完立即返回,不循环等待,
* 避免 RECV 电平中断导致主循环(如 echo饿死。
* 循环体内部用 break 提前退出INT# 无效或无中断时)。
*/
do
{
/*
* GPIO 轮询 INT# 引脚:低电平有效(有中断待处理)。
* 采用 GPIO 轮询而非 EXTI 边沿中断,仅当 INT# 为低电平时才读取状态,减少不必要 SPI 事务。
* 若 INT# 为高(无效),直接退出本次轮询。
*/
if (HAL_GPIO_ReadPin(CH395_INT_GPIO_Port, CH395_INT_Pin) == GPIO_PIN_SET) {
break;
}
/* 读取全局中断状态2 字节):读操作本身会清除 CH395F 中断并使其拉高 INT#
* 若此后仍有新中断CH395F 会再次拉低 INT#,由下一次 net_poll() 处理 */
int_status = ch395f_get_glob_int_status_all();
/* 无有效中断(读到的状态为 0提前退出 */
if (int_status == 0) {
break;
}
/* 遍历所有 Socket处理各自的中断 */
for (int i = 0; i < NET_MAX_SOCKETS; i++) {
/* 取本 Socket 控制块(直接用数组下标,无需 net_get_sock 的越界判断) */
net_sock_t *p_sock = &s_net_socks[i];
/* 全局中断状态 bit4~bit11 分别对应 socket0~socket7 的中断标志,
* 对应位为 0 说明该 Socket 无中断,跳过以节省时间 */
if (!(int_status & (1U << (i + 4)))) {
continue;
}
/* 读取该 Socket 的中断状态寄存器SINT */
uint8_t sock_int = ch395f_get_sock_int_status(i);
/* 连接建立事件:交给状态机处理(监听态/客户端/数据 Socket 分别处理) */
if (sock_int & CH395F_SINT_STAT_CONNECT) {
handle_connect_event(p_sock);
event_count++;
}
/* 连接断开事件:更新状态机并触发断开回调 */
if (sock_int & CH395F_SINT_STAT_DISCONNECT) {
handle_disconnect_event(p_sock);
event_count++;
}
/* 接收数据就绪事件:仅通知上层,实际数据由 net_recv_locked 读出(避免重复读 recv_len */
if (sock_int & CH395F_SINT_STAT_RECV_OK) {
handle_recv_event(p_sock);
event_count++;
}
/* 发送完成事件:通知上层发送缓冲已空,可继续发送 */
if (sock_int & CH395F_SINT_STAT_SEND_OK) {
handle_send_event(p_sock);
event_count++;
}
/* 发送缓冲区空闲:置 send_ready 标志,唤醒等待发送的逻辑 */
if (sock_int & CH395F_SINT_STAT_SENBUF_FREE) {
p_sock->send_ready = 1;
}
/* 超时事件:连接超时或收发超时,更新状态机 */
if (sock_int & CH395F_SINT_STAT_SOCK_TIMEOUT) {
handle_timeout_event(p_sock);
event_count++;
}
}
/*
* 处理完本批中断后退出 do-while(0)
* CH395F 的 RECV 中断为电平触发,只要接收缓冲区仍有数据就保持 INT# 低电平,
* 若此处继续循环会反复处理 RECV 中断,导致主循环中的应用代码(如 echo 回显)得不到执行。
* 因此每次 net_poll() 只处理“一批”,剩余数据由主循环/recv 逻辑在后续轮询中读取。
*/
} while (0);
/* 返回本次累计发生的事件数量0 表示无事件) */
return event_count;
}
/*
* 函数功能:处理消息队列中的请求
* 限定条件:必须在 netTask 上下文中调用(独占 CH395F 和 s_net_socks 访问权)
* 函数说明:每次调用处理一条消息。处理 send/recv 时内部会调用 net_poll()
* 等待操作完成。处理完毕后通知调用方任务。
*/
void net_process_messages(void) {
net_msg_t msg;
if (NULL == netMsgQueueHandle) {
return;
}
while (osMessageQueueGet(netMsgQueueHandle, &msg, NULL, 0) == osOK) {
switch (msg.type) {
case NET_MSG_SEND: {
net_sock_t *p_sock = net_get_sock(msg.sockfd);
if (NULL == p_sock || p_sock->state != NET_SOCK_STATE_ESTABLISHED) {
msg.result = -1;
} else {
msg.result = net_send_locked(p_sock, msg.buf, msg.len);
}
break;
}
case NET_MSG_RECV: {
net_sock_t *p_sock = net_get_sock(msg.sockfd);
if (NULL == p_sock) {
msg.result = -1;
} else {
msg.result = net_recv_locked(p_sock, msg.buf, msg.len, msg.flags);
}
break;
}
case NET_MSG_CLOSE: {
net_sock_t *p_sock = net_get_sock(msg.sockfd);
if (NULL == p_sock) {
msg.result = -1;
} else {
msg.result = net_close_locked(p_sock);
}
break;
}
case NET_MSG_CONNECT: {
msg.result = net_connect(msg.sockfd,
(const struct net_sockaddr *)&msg.addr,
msg.addrlen);
break;
}
case NET_MSG_LISTEN: {
msg.result = net_listen_locked(msg.sockfd);
break;
}
case NET_MSG_ACCEPT: {
struct net_sockaddr *p_addr = (struct net_sockaddr *)msg.buf;
int addrlen = msg.len;
int *p_addrlen = (NULL != p_addr && addrlen > 0) ? &addrlen : NULL;
msg.result = net_accept_locked(msg.sockfd, p_addr, p_addrlen);
break;
}
default:
msg.result = -1;
break;
}
xTaskNotify(msg.caller, (uint32_t)msg.result, eSetValueWithOverwrite);
}
}
/*
* 函数功能:创建 Socket分配空闲 Socket 控制块并初始化状态
* 入口参数domain - 地址族,仅支持 NET_AF_INET int
* type - SOCK_STREAM(TCP) 或 SOCK_DGRAM(UDP) int
* protocol - 通常为 0 int
* 返回值Socket 描述符 (0~7),失败返回 -1 int
* 限定条件net_init() 已调用成功s_net_initialized == 1
* 函数说明:自动分配一个空闲的 Socket 控制块in_use=0初始化为 CREATED 状态
*/
int net_socket(int domain, int type, int protocol) {
int sockfd;
net_sock_t *p_sock;
/* 参数检查*/
if (domain != NET_AF_INET) {
s_net_errno = NET_ERR_INVAL;
return -1;
}
/* 分配空闲 Socket */
sockfd = alloc_socket();
if (sockfd < 0) {
s_net_errno = NET_ERR_NOMEM;
return -1;
}
p_sock = &s_net_socks[sockfd];
p_sock->ch395_sock = (uint8_t)sockfd;
p_sock->type = (uint8_t)type;
p_sock->state = NET_SOCK_STATE_CREATED;
p_sock->in_use = 1;
p_sock->send_ready = 1;
return sockfd;
}
/*
* 函数功能:绑定 Socket 到本地 IP 地址和端口号
* 入口参数sockfd - Socket 描述符 (0~7) int
* addr - 本地地址结构体指针 const struct net_sockaddr *
* addrlen - 地址结构体长度 int >= sizeof(struct net_sockaddr_in)
* 返回值0 - 绑定成功 int
* -1 - 绑定失败参数无效、Socket 未使用等) int
* 限定条件Socket 已通过 net_socket() 创建且未被其他操作占用net_init() 已调用
* 函数说明1. TCP Socket 仅记录本地端口,进入 BOUND 状态等待 listen
* 2. UDP SocketSOCK_DGRAM立即打开 CH395F Socket 并配置为 Server 模式
* - Socket 4~7 需显式分配收发缓冲区
*/
int net_bind(int sockfd, const struct net_sockaddr *addr, int addrlen) {
net_sock_t *p_sock;
const struct net_sockaddr_in *p_addr_in;
/* 参数检<E695B0>?*/
if (sockfd < 0 || sockfd >= NET_MAX_SOCKETS) {
s_net_errno = NET_ERR_BADF;
return -1;
}
p_sock = &s_net_socks[sockfd];
if (!p_sock->in_use) {
s_net_errno = NET_ERR_BADF;
return -1;
}
if (NULL == addr || addrlen < (int)sizeof(struct net_sockaddr_in)) {
s_net_errno = NET_ERR_INVAL;
return -1;
}
p_addr_in = (const struct net_sockaddr_in *)addr;
/* 设置本地端口(转换为主机序) */
p_sock->local_port = net_ntohs(p_addr_in->sin_port);
/* port=0 时自动分配动态端口49152~65535 */
if (p_sock->local_port == 0) {
p_sock->local_port = s_dynamic_port;
s_dynamic_port++;
if (s_dynamic_port > 65535) {
s_dynamic_port = 49152;
}
}
/* UDP (SOCK_DGRAM): 直接在此打开 CH395F Socket */
if (p_sock->type == NET_SOCK_DGRAM) {
uint8_t status;
/* Socket 4-7 默认无收发缓冲,需显式分配 */
if (sockfd >= 4) {
ch395f_set_send_buf((uint8_t)sockfd, 28, 2);
ch395f_set_recv_buf((uint8_t)sockfd, 30, 2);
}
ch395f_set_proto_type((uint8_t)sockfd, CH395F_PROTO_TYPE_UDP);
ch395f_set_sour_port((uint8_t)sockfd, p_sock->local_port);
/* DesIP=0xFFFFFFFF UDP Server 模式(接受任意来源) */
{
uint8_t broadcast[4] = {0xFF, 0xFF, 0xFF, 0xFF};
ch395f_set_des_ip((uint8_t)sockfd, broadcast);
}
ch395f_set_des_port((uint8_t)sockfd, 0);
status = ch395f_open_socket((uint8_t)sockfd);
if (status != CH395F_ERR_SUCCESS) {
DBG_ERROR("sock%d open_socket FAIL: status=0x%02X (expected 0x00)", sockfd, status);
s_net_errno = NET_ERR_BUSY;
return -1;
}
p_sock->state = NET_SOCK_STATE_UDP_OPEN;
} else {
p_sock->state = NET_SOCK_STATE_BOUND;
}
return 0;
}
/*
* 函数功能:启动 TCP Server 监听(内部版本,必须在 netTask 上下文调用)
* 入口参数sockfd - Socket 描述符 (0~7) int
* 返回值0 - 成功,-1 - 失败 int
* 限定条件sockfd 已绑定且为 TCPnetTask 上下文
*/
int net_listen_locked(int sockfd) {
/* 取监听 Socket 控制块直接下标访问sockfd 已在上层 net_listen 校验过范围) */
net_sock_t *p_sock = &s_net_socks[sockfd];
/* CH395F 打开 Socket 的返回状态 */
uint8_t status;
/* 合法性检查Socket 必须已创建且为 TCP 流类型 */
if (!p_sock->in_use || p_sock->type != NET_SOCK_STREAM) {
s_net_errno = NET_ERR_INVAL;
return -1;
}
/* 状态检查:仅允许在 BOUND已 bind或 CREATED未 bind 直接用随机端口)状态下启动监听 */
if (p_sock->state != NET_SOCK_STATE_BOUND &&
p_sock->state != NET_SOCK_STATE_CREATED) {
s_net_errno = NET_ERR_INVAL;
return -1;
}
/*
* 本实现仅支持单连接:监听 Socket 自身即为数据通道,复用 CH395F 默认收发缓冲
* Socket 0~3 默认缓冲互不重叠),不再为数据 Socket 额外分配。
*/
ch395f_set_proto_type(sockfd, CH395F_PROTO_TYPE_TCP);
ch395f_set_sour_port(sockfd, p_sock->local_port);
/* CH395F 关闭 Socket 后需延时才能重新打开Trap 09故失败重试最多 3 次 */
for (int open_try = 0; open_try < 3; open_try++) {
status = ch395f_open_socket(sockfd);
if (status == CH395F_ERR_SUCCESS) {
break; /* 打开成功,退出重试 */
}
ch395f_close_socket(sockfd); /* 打开失败先关闭,再延时后重试 */
HAL_Delay(500);
}
/* 3 次仍失败,返回错误 */
if (status != CH395F_ERR_SUCCESS) {
s_net_errno = NET_ERR_BUSY;
return -1;
}
/* 下发 TCP LISTEN 命令,进入监听状态 */
status = ch395f_tcp_listen(sockfd);
if (status != CH395F_ERR_SUCCESS) {
s_net_errno = NET_ERR_BUSY;
return -1;
}
/* 监听 Socket 进入 LISTENING 态 */
p_sock->state = NET_SOCK_STATE_LISTENING;
/* 单连接CONNECT 中断直接作用于本 Socket 自身 */
/*
* 启动监听前清空各 Socket 及全局中断状态寄存器(读即清除),
* 避免历史残留/伪中断在监听刚开始时误触发连接或断开事件。
*/
for (int j = 0; j < NET_MAX_SOCKETS; j++) {
(void)ch395f_get_sock_int_status((uint8_t)j);
}
(void)ch395f_get_glob_int_status_all();
return 0;
}
/*
* 函数功能:启动 TCP Server 监听(线程安全版本,通过消息队列委托给 netTask
* 入口参数sockfd - Socket 描述符 int
* 返回值0 - 成功,-1 - 失败 int
* 限定条件net_init() + MX_FREERTOS_Init() 已执行;必须在任务上下文中调用
* 函数说明:通过消息队列委托给 netTask确保 SPI 串行化
*/
int net_listen(int sockfd) {
net_msg_t msg;
if (sockfd < 0 || sockfd >= NET_MAX_SOCKETS) {
s_net_errno = NET_ERR_BADF;
return -1;
}
if (NULL == netMsgQueueHandle) {
s_net_errno = NET_ERR;
return -1;
}
msg.type = NET_MSG_LISTEN;
msg.sockfd = sockfd;
msg.caller = xTaskGetCurrentTaskHandle();
msg.result = -1;
if (osMessageQueuePut(netMsgQueueHandle, &msg, 0, osWaitForever) != osOK) {
s_net_errno = NET_ERR_BUSY;
return -1;
}
{
uint32_t s_notified;
xTaskNotifyWait(0, 0, &s_notified, portMAX_DELAY);
msg.result = (int)s_notified;
}
if (msg.result < 0) {
s_net_errno = NET_ERR;
}
return msg.result;
}
/*
* 函数功能TCP Server 接受新连接(内部版本,必须在 netTask 上下文调用)
* 入口参数sockfd - 监听 Socket 描述符 int
* addr - 输出:客户端地址结构体指针 struct net_sockaddr *(可为 NULL
* addrlen - 输入输出:地址结构体长度 int *(可为 NULL
* 返回值:新连接的 Socket 描述符,失败返回 -1 int
* 限定条件Socket 处于 LISTENING 状态
* 函数说明:单连接:监听 Socket 自身在 CONNECT 后已转为 ESTABLISHED
*/
static int net_accept_locked(int sockfd, struct net_sockaddr *addr, int *addrlen) {
net_sock_t *p_listen_sock;
p_listen_sock = &s_net_socks[sockfd];
if (!p_listen_sock->in_use) {
s_net_errno = NET_ERR_INVAL;
return -1;
}
net_poll();
/* 单连接:本 Socket 自身在 CONNECT 后已转为 ESTABLISHED
* 只有处于 ESTABLISHED 才表示有可接受的连接;否则尚无连接 */
if (p_listen_sock->state != NET_SOCK_STATE_ESTABLISHED) {
s_net_errno = NET_ERR_WOULDBLOCK;
return -1;
}
/* 填充对端地址(若调用方需要) */
if (NULL != addr && NULL != addrlen) {
struct net_sockaddr_in *p_addr_in = (struct net_sockaddr_in *)addr;
p_addr_in->sin_family = NET_AF_INET;
p_addr_in->sin_port = net_htons(p_listen_sock->remote_port);
p_addr_in->sin_addr.s_addr = p_listen_sock->remote_ip;
*addrlen = sizeof(struct net_sockaddr_in);
}
/* 单连接:返回的就是监听 Socket 自身(已转为数据通道) */
return sockfd;
}
/*
* 函数功能TCP Server 接受新连接(线程安全版本,通过消息队列委托给 netTask
* 入口参数sockfd - 监听 Socket 描述符 int
* addr - 输出:客户端地址结构体指针 struct net_sockaddr *(可为 NULL
* addrlen - 输入输出:地址结构体长度 int *(可为 NULL
* 返回值:新连接的 Socket 描述符,失败返回 -1 int
* 限定条件Socket 处于 LISTENING 状态net_init() + MX_FREERTOS_Init() 已执行;非 netTask 上下文
* 函数说明:将接受请求放入消息队列,阻塞等待 netTask 处理完毕
*/
int net_accept(int sockfd, struct net_sockaddr *addr, int *addrlen) {
net_msg_t msg;
if (sockfd < 0 || sockfd >= NET_MAX_SOCKETS) {
s_net_errno = NET_ERR_BADF;
return -1;
}
if (NULL == netMsgQueueHandle) {
s_net_errno = NET_ERR;
return -1;
}
msg.type = NET_MSG_ACCEPT;
msg.sockfd = sockfd;
msg.buf = (void *)addr;
msg.len = (NULL != addrlen) ? *addrlen : 0;
msg.caller = xTaskGetCurrentTaskHandle();
msg.result = -1;
if (osMessageQueuePut(netMsgQueueHandle, &msg, 0, osWaitForever) != osOK) {
s_net_errno = NET_ERR_BUSY;
return -1;
}
{
uint32_t s_notified;
xTaskNotifyWait(0, 0, &s_notified, portMAX_DELAY);
msg.result = (int)s_notified;
}
if (NULL != addrlen &&NULL != addr) {
*addrlen = sizeof(struct net_sockaddr_in);
}
if (msg.result < 0) {
s_net_errno = NET_ERR;
}
return msg.result;
}
/*
* 函数功能TCP Client 发起与服务器的连接
* 入口参数sockfd - Socket 描述符 (0~7),必须为已创建的 TCP Socket int
* addr - 服务器地址结构体指针 const struct net_sockaddr *
* addrlen - 地址结构体长度 int >= sizeof(struct net_sockaddr_in)
* 返回值0 - 连接请求发送成功(异步,需等待 ESTABLISHED int
* -1 - 连接失败(参数无效、状态错误等) int
* 限定条件Socket 已通过 net_socket() 创建且 type == NET_SOCK_STREAM尚未连接
* 函数说明:设置目标 IP 和端口后打开 CH395F Socket进入 CONNECTING 状态。连接建立需等待
* net_poll() 检测到 CONNECT 事件并将状态更新为 ESTABLISHED
*/
int net_connect(int sockfd, const struct net_sockaddr *addr, int addrlen) {
net_sock_t *p_sock;
const struct net_sockaddr_in *p_addr_in;
uint8_t status;
/* 参数检<E695B0>?*/
if (sockfd < 0 || sockfd >= NET_MAX_SOCKETS) {
s_net_errno = NET_ERR_BADF;
return -1;
}
p_sock = &s_net_socks[sockfd];
if (!p_sock->in_use || p_sock->type != NET_SOCK_STREAM) {
s_net_errno = NET_ERR_INVAL;
return -1;
}
if (NULL == addr || addrlen < (int)sizeof(struct net_sockaddr_in)) {
s_net_errno = NET_ERR_INVAL;
return -1;
}
p_addr_in = (const struct net_sockaddr_in *)addr;
/* 保存远端地址 */
p_sock->remote_ip = p_addr_in->sin_addr.s_addr;
p_sock->remote_port = net_ntohs(p_addr_in->sin_port);
/* 转换 IP 为数组格式(低字节在前) */
p_sock->remote_ip_arr[0] = (uint8_t)(p_sock->remote_ip);
p_sock->remote_ip_arr[1] = (uint8_t)(p_sock->remote_ip >> 8);
p_sock->remote_ip_arr[2] = (uint8_t)(p_sock->remote_ip >> 16);
p_sock->remote_ip_arr[3] = (uint8_t)(p_sock->remote_ip >> 24);
/* 配置 CH395F Socket */
ch395f_set_proto_type(sockfd, CH395F_PROTO_TYPE_TCP);
ch395f_set_sour_port(sockfd, p_sock->local_port);
ch395f_set_des_ip(sockfd, p_sock->remote_ip_arr);
ch395f_set_des_port(sockfd, p_sock->remote_port);
status = ch395f_open_socket(sockfd);
if (status != CH395F_ERR_SUCCESS) {
s_net_errno = NET_ERR_BUSY;
return -1;
}
/* 发起 TCP 连接 */
ch395f_tcp_connect(sockfd);
p_sock->state = NET_SOCK_STATE_CONNECTING;
return 0;
}
/*
* SPI DMA 单次事务最大有效载荷(取 CH395F 驱动公开常量)
*/
#define NET_DMA_MAX_PAYLOAD ((int)CH395F_SPI_DMA_MAX_PAYLOAD)
/*
* 函数功能:发送数据(内部锁定版本,必须在 netTask 上下文调用)
*/
/*
* 函数功能:发送数据(内部锁定版本,必须在 netTask 上下文调用)
* 入口参数p_sock - Socket 控制块指针 net_sock_t *
* buf - 待发送数据缓冲区指针 const void *
* len - 数据长度 int > 0
* 出口参数:无
* 返回值:实际发送字节数(>= 0-1 表示参数无效或 Socket 未连接 int
* 限定条件p_sock 非空Socket 处于 ESTABLISHED 状态netTask 上下文
* 函数说明1. 按 NET_DMA_MAX_PAYLOAD 分块写入 CH395F 发送缓冲区
* 2. 发送前等待 send_ready 置位,超时返回已发送字节数
* 3. 每写完一块等待 SEND_OK 中断send_ready 清零后重新置位)
*/
static int net_send_locked(net_sock_t *p_sock, const void *buf, int len) {
int sent = 0;
uint32_t tick_start;
int sockfd = (int)(p_sock - s_net_socks);
if (NULL == buf || len <= 0) {
s_net_errno = NET_ERR_INVAL;
return -1;
}
if (p_sock->state != NET_SOCK_STATE_ESTABLISHED) {
s_net_errno = NET_ERR_NOTCONN;
return -1;
}
tick_start = HAL_GetTick();
while (sent < len) {
int chunk = len - sent;
if (chunk > NET_DMA_MAX_PAYLOAD) {
chunk = NET_DMA_MAX_PAYLOAD;
}
if (!p_sock->send_ready) {
if (NET_SEND_TIMEOUT_MS > 0 &&
(HAL_GetTick() - tick_start) >= NET_SEND_TIMEOUT_MS) {
s_net_errno = NET_ERR_TIMEDOUT;
break;
}
net_poll();
continue;
}
ch395f_write_send_buf(sockfd, (uint8_t *)buf + sent, (uint16_t)chunk);
p_sock->send_ready = 0;
sent += chunk;
p_sock->send_bytes += (uint32_t)chunk;
tick_start = HAL_GetTick();
while (!p_sock->send_ready) {
net_poll();
if (NET_SEND_TIMEOUT_MS > 0 &&
(HAL_GetTick() - tick_start) >= NET_SEND_TIMEOUT_MS) {
break;
}
}
}
return sent;
}
/*
* 函数功能:接收数据(内部锁定版本,必须在 netTask 上下文调用)
*/
/*
* 函数功能:接收数据(内部锁定版本,必须在 netTask 上下文调用)
* 入口参数p_sock - Socket 控制块指针 net_sock_t *
* buf - 接收缓冲区指针 void *
* len - 缓冲区大小 int > 0
* flags - 标志位NET_MSG_DONTWAIT 表示非阻塞 int
* 出口参数buf - 填充接收到的数据
* 返回值:实际接收字节数(> 00 表示对端关闭;-1 表示出错/超时/非阻塞无数据 int
* 限定条件p_sock 非空netTask 上下文
* 函数说明1. 循环调用 net_poll() 检查接收长度,有数据即读出
* 2. 非阻塞模式下无数据立即返回 -1NET_ERR_WOULDBLOCK
* 3. CLOSED 状态尝试排空接收缓冲区残余数据Trap 13FIN 可能先于数据到达)
*/
static int net_recv_locked(net_sock_t *p_sock, void *buf, int len, int flags) {
uint16_t recv_len;
uint32_t tick_start;
int sockfd = (int)(p_sock - s_net_socks);
if (NULL == buf || len <= 0) {
s_net_errno = NET_ERR_INVAL;
return -1;
}
tick_start = HAL_GetTick();
while (1) {
net_poll();
recv_len = ch395f_get_recv_len(sockfd);
if (recv_len > 0) {
int read_len = (recv_len > (uint16_t)len) ? len : (int)recv_len;
DBG_INFO("recv sock%d len=%d", sockfd, recv_len);
ch395f_read_recv_buf(sockfd, (uint8_t *)buf, (uint16_t)read_len);
p_sock->recv_bytes += (uint32_t)read_len;
HAL_Delay(1); /* 等待 CH395F 刷新 recv_len 寄存器 */
return read_len;
}
if (flags & NET_MSG_DONTWAIT) {
s_net_errno = NET_ERR_WOULDBLOCK;
return -1;
}
if (NET_RECV_TIMEOUT_MS > 0 &&
(HAL_GetTick() - tick_start) >= NET_RECV_TIMEOUT_MS) {
s_net_errno = NET_ERR_TIMEDOUT;
return -1;
}
if (p_sock->state == NET_SOCK_STATE_CLOSED) {
/* 断开前尝试排空 CH395F 接收缓冲区Trap 13FIN 可能比数据先到) */
uint16_t rem = ch395f_get_recv_len(sockfd);
if (rem > 0) {
int rd = (rem > (uint16_t)len) ? len : (int)rem;
ch395f_read_recv_buf(sockfd, (uint8_t *)buf, (uint16_t)rd);
p_sock->recv_bytes += (uint32_t)rd;
return rd;
}
return 0;
}
}
}
/*
* 函数功能:关闭 socket内部锁定版本必须在 netTask 上下文调用)
*/
/*
* 函数功能:关闭 Socket内部锁定版本必须在 netTask 上下文调用)
* 入口参数p_sock - Socket 控制块指针 net_sock_t *
* 出口参数:无
* 返回值0 - 关闭成功 int
* 限定条件p_sock 非空netTask 上下文
* 函数说明1. 调用 ch395f_close_socket 关闭 CH395F 硬件 Socket
* 2. 清空整个控制块memset 0释放该 Socket 资源
*/
static int net_close_locked(net_sock_t *p_sock) {
int sockfd = (int)(p_sock - s_net_socks);
ch395f_close_socket(sockfd);
memset(p_sock, 0, sizeof(net_sock_t));
return 0;
}
/*
* 函数功能:发送数据(线程安全版本,通过消息队列委托给 netTask
* 入口参数sockfd - Socket 描述符 int >= 0 && < NET_MAX_SOCKETS
* buf - 待发送数据缓冲区指针 const void *
* len - 数据长度 int > 0
* flags - 标志位,通常为 0 int
* 返回值:实际发送字节数 int >= 0 || -1
* -1 - 发送失败(参数无效、队列满等) int
* 限定条件Socket 处于 ESTABLISHED 状态net_init() + MX_FREERTOS_Init() 已执行;非 netTask 上下文
* 函数说明:将发送请求放入消息队列,阻塞等待 netTask 处理完毕并返回结果。netTask 内请使用 net_send_sock()
*/
int net_send(int sockfd, const void *buf, int len, int flags) {
(void)flags;
net_msg_t msg;
if (sockfd < 0 || sockfd >= NET_MAX_SOCKETS) {
s_net_errno = NET_ERR_BADF;
return -1;
}
if (NULL == netMsgQueueHandle) {
s_net_errno = NET_ERR;
return -1;
}
msg.type = NET_MSG_SEND;
msg.sockfd = sockfd;
msg.buf = (void *)buf;
msg.len = len;
msg.flags = 0;
msg.caller = xTaskGetCurrentTaskHandle();
msg.result = -1;
if (osMessageQueuePut(netMsgQueueHandle, &msg, 0, osWaitForever) != osOK) {
s_net_errno = NET_ERR_BUSY;
return -1;
}
{
uint32_t s_notified;
xTaskNotifyWait(0, 0, &s_notified, portMAX_DELAY);
msg.result = (int)s_notified;
}
if (msg.result < 0) {
s_net_errno = NET_ERR;
}
return msg.result;
}
/*
* 函数功能:接收数据(线程安全版本,通过消息队列委托给 netTask
* 入口参数sockfd - Socket 描述符 int >= 0 && < NET_MAX_SOCKETS
* buf - 接收缓冲区指针 void *
* len - 缓冲区大小 int > 0
* flags - 标志位0=阻塞NET_MSG_DONTWAIT=非阻塞 int
* 返回值:实际接收字节数 int >= 0 || 0 || -1
* 0 - 对端关闭连接 int
* -1 - 接收失败(参数无效、队列满等) int
* 限定条件Socket 处于 ESTABLISHED 状态net_init() + MX_FREERTOS_Init() 已执行;非 netTask 上下文
* 函数说明:将接收请求放入消息队列,阻塞等待 netTask 处理完毕并返回结果。netTask 内请使用 net_recv_sock()
*/
int net_recv(int sockfd, void *buf, int len, int flags) {
net_msg_t msg;
if (sockfd < 0 || sockfd >= NET_MAX_SOCKETS) {
s_net_errno = NET_ERR_BADF;
return -1;
}
if (NULL == netMsgQueueHandle) {
s_net_errno = NET_ERR;
return -1;
}
msg.type = NET_MSG_RECV;
msg.sockfd = sockfd;
msg.buf = buf;
msg.len = len;
msg.flags = flags;
msg.caller = xTaskGetCurrentTaskHandle();
msg.result = -1;
if (osMessageQueuePut(netMsgQueueHandle, &msg, 0, osWaitForever) != osOK) {
s_net_errno = NET_ERR_BUSY;
return -1;
}
{
uint32_t s_notified;
xTaskNotifyWait(0, 0, &s_notified, portMAX_DELAY);
msg.result = (int)s_notified;
}
if (msg.result < 0) {
s_net_errno = NET_ERR;
}
return msg.result;
}
/*
* 函数功能:关闭 Socket线程安全版本通过消息队列委托给 netTask
* 入口参数sockfd - Socket 描述符 int >= 0 && < NET_MAX_SOCKETS
* 返回值0 - 关闭成功 int
* -1 - 关闭失败 int
* 限定条件Socket 已创建且处于非 CLOSED 状态net_init() + MX_FREERTOS_Init() 已执行;非 netTask 上下文
* 函数说明:将关闭请求放入消息队列,阻塞等待 netTask 处理完毕。TCP Socket 先断开连接再释放控制块。
* netTask 内请使用 net_close_sock()
*/
int net_close(int sockfd) {
net_msg_t msg;
if (sockfd < 0 || sockfd >= NET_MAX_SOCKETS) {
s_net_errno = NET_ERR_BADF;
return -1;
}
if (NULL == netMsgQueueHandle) {
s_net_errno = NET_ERR;
return -1;
}
msg.type = NET_MSG_CLOSE;
msg.sockfd = sockfd;
msg.caller = xTaskGetCurrentTaskHandle();
msg.result = -1;
if (osMessageQueuePut(netMsgQueueHandle, &msg, 0, osWaitForever) != osOK) {
s_net_errno = NET_ERR_BUSY;
return -1;
}
{
uint32_t s_notified;
xTaskNotifyWait(0, 0, &s_notified, portMAX_DELAY);
msg.result = (int)s_notified;
}
if (msg.result < 0) {
s_net_errno = NET_ERR;
}
return msg.result;
}
/*
* 函数功能:发送 UDP 数据报(含目标地址)
* 入口参数sockfd - Socket 描述符 int >= 0 && < NET_MAX_SOCKETS
* buf - 待发送数据缓冲区指针 const void *
* len - 数据长度 int > 0
* flags - 标志位,通常为 0 int
* dest_addr - 目标地址结构体指针 const struct net_sockaddr *
* addrlen - 目标地址结构体长度 int >= sizeof(struct net_sockaddr_in)
* 返回值:实际发送字节数 int > 0 || -1
* -1 - 发送失败 int
* 限定条件Socket 已创建且为 UDP 类型SOCK_DGRAMnet_init() 已调用
*/
int net_sendto(int sockfd, const void *buf, int len, int flags,
const struct net_sockaddr *dest_addr, int addrlen) {
net_sock_t *p_sock;
const struct net_sockaddr_in *p_addr_in;
int sent = 0;
uint32_t tick_start;
/* 参数检<E695B0>?*/
if (sockfd < 0 || sockfd >= NET_MAX_SOCKETS) {
s_net_errno = NET_ERR_BADF;
return -1;
}
p_sock = &s_net_socks[sockfd];
if (!p_sock->in_use) {
s_net_errno = NET_ERR_BADF;
return -1;
}
if (NULL == buf || len <= 0) {
s_net_errno = NET_ERR_INVAL;
return -1;
}
/* UDP 模式:设置目标地址 */
if (NULL != dest_addr && addrlen >= (int)sizeof(struct net_sockaddr_in)) {
p_addr_in = (const struct net_sockaddr_in *)dest_addr;
/* 转换 IP 为数组格<E7BB84>?*/
uint8_t ip_arr[4];
uint32_t ip = p_addr_in->sin_addr.s_addr;
ip_arr[0] = (uint8_t)(ip);
ip_arr[1] = (uint8_t)(ip >> 8);
ip_arr[2] = (uint8_t)(ip >> 16);
ip_arr[3] = (uint8_t)(ip >> 24);
ch395f_set_des_ip(sockfd, ip_arr);
ch395f_set_des_port(sockfd, net_ntohs(p_addr_in->sin_port));
}
tick_start = HAL_GetTick();
/* 发送数<E98081>?*/
while (sent < len) {
int chunk = len - sent;
if (chunk > NET_SEND_BUF_SIZE) {
chunk = NET_SEND_BUF_SIZE;
}
/* 检查发送缓冲区 */
if (!p_sock->send_ready) {
if (NET_SEND_TIMEOUT_MS > 0 &&
(HAL_GetTick() - tick_start) >= NET_SEND_TIMEOUT_MS) {
s_net_errno = NET_ERR_TIMEDOUT;
break;
}
net_poll();
continue;
}
ch395f_write_send_buf(sockfd, (uint8_t *)buf + sent, (uint16_t)chunk);
p_sock->send_ready = 0;
sent += chunk;
p_sock->send_bytes += (uint32_t)chunk;
/* 等待发送完<E98081>?*/
tick_start = HAL_GetTick();
while (!p_sock->send_ready) {
net_poll();
if (NET_SEND_TIMEOUT_MS > 0 &&
(HAL_GetTick() - tick_start) >= NET_SEND_TIMEOUT_MS) {
break;
}
}
}
return sent;
}
/*
* 函数功能:接收 UDP 数据报(含源地址)
* 入口参数sockfd - Socket 描述符 int >= 0 && < NET_MAX_SOCKETS
* buf - 接收缓冲区指针 void *
* len - 缓冲区大小 int > 0
* flags - 标志位,通常为 0 int
* src_addr - 输出:发送方地址结构体指针 struct net_sockaddr *(可为 NULL
* addrlen - 输入输出:源地址长度 int *(可为 NULL
* 返回值:实际接收字节数 int >= 0 || -1
* -1 - 接收失败 int
* 限定条件Socket 已创建且为 UDP 类型SOCK_DGRAMnet_init() 已调用;有数据可用
*/
int net_recvfrom(int sockfd, void *buf, int len, int flags,
struct net_sockaddr *src_addr, int *addrlen) {
net_sock_t *p_sock;
uint16_t recv_len;
uint32_t tick_start;
/* 参数检<E695B0>?*/
if (sockfd < 0 || sockfd >= NET_MAX_SOCKETS) {
s_net_errno = NET_ERR_BADF;
return -1;
}
p_sock = &s_net_socks[sockfd];
if (!p_sock->in_use) {
s_net_errno = NET_ERR_BADF;
return -1;
}
if (NULL == buf || len <= 0) {
s_net_errno = NET_ERR_INVAL;
return -1;
}
tick_start = HAL_GetTick();
while (1) {
net_poll();
recv_len = ch395f_get_recv_len(sockfd);
if (recv_len > 0) {
/* UDP Server 模式:前 8 字节为固定格式IP + 端口 + 长度<E995BF>?*/
if (p_sock->type == NET_SOCK_DGRAM && recv_len >= 8) {
uint8_t header[8];
ch395f_read_recv_buf(sockfd, header, 8);
recv_len -= 8;
/* 填充来源地址 */
if (NULL != src_addr &&NULL != addrlen &&
*addrlen >= (int)sizeof(struct net_sockaddr_in)) {
struct net_sockaddr_in *p_addr_in = (struct net_sockaddr_in *)src_addr;
p_addr_in->sin_family = NET_AF_INET;
/* CH395F UDP <20>? [0-1] reserved [2-3] src_port(LE) [4-7] src_ip */
p_addr_in->sin_port = net_htons((uint16_t)(header[2] | (header[3] << 8)));
p_addr_in->sin_addr.s_addr = ((uint32_t)header[4]) |
((uint32_t)header[5] << 8) |
((uint32_t)header[6] << 16) |
((uint32_t)header[7] << 24);
*addrlen = sizeof(struct net_sockaddr_in);
}
}
/* 读取数据 */
if (recv_len > 0) {
int read_len = (recv_len > (uint16_t)len) ? len : (int)recv_len;
ch395f_read_recv_buf(sockfd, (uint8_t *)buf, (uint16_t)read_len);
p_sock->recv_bytes += (uint32_t)read_len;
return read_len;
}
}
/* 非阻塞模式检<E5BC8F>?*/
if (flags & NET_MSG_DONTWAIT) {
s_net_errno = NET_ERR_WOULDBLOCK;
return -1;
}
/* 超时检<E697B6>?*/
if (NET_RECV_TIMEOUT_MS > 0 &&
(HAL_GetTick() - tick_start) >= NET_RECV_TIMEOUT_MS) {
s_net_errno = NET_ERR_TIMEDOUT;
return -1;
}
}
}
/*
* 函数功能:发送数据(内部版本,绕过消息队列,供 netTask 直接使用)
*/
/*
* 函数功能:发送数据(内部版本,绕过消息队列,供 netTask 直接使用)
* 入口参数p_sock - Socket 控制块指针 net_sock_t *
* buf - 待发送数据缓冲区指针 const void *
* len - 数据长度 int > 0
* 出口参数:无
* 返回值:实际发送字节数(>= 0-1 表示参数无效 int
* 限定条件p_sock 非空netTask 上下文(避免向自身发送消息造成死锁)
* 函数说明:直接调用 net_send_locked 完成发送
*/
int net_send_sock(net_sock_t *p_sock, const void *buf, int len) {
if (NULL == p_sock) {
s_net_errno = NET_ERR_BADF;
return -1;
}
return net_send_locked(p_sock, buf, len);
}
/*
* 函数功能:接收数据(内部版本,绕过消息队列,供 netTask 直接使用)
* 入口参数p_sock - Socket 控制块指针 net_sock_t *
* buf - 接收缓冲区指针 void *
* len - 缓冲区大小 int > 0
* flags - 标志位NET_MSG_DONTWAIT=非阻塞 int
* 返回值:实际接收字节数 int >= 0 || 0 || -1
* 0 - 对端关闭连接 int
* -1 - 接收失败 int
* 限定条件:必须在 netTask 上下文中调用p_sock->in_use == 1
* 函数说明:直接操作 Socket 控制块,不经过消息队列。避免 netTask 向自身发送消息造成死锁
*/
int net_recv_sock(net_sock_t *p_sock, void *buf, int len, int flags) {
if (NULL == p_sock) {
s_net_errno = NET_ERR_BADF;
return -1;
}
return net_recv_locked(p_sock, buf, len, flags);
}
/*
* 函数功能:关闭 Socket内部版本绕过消息队列供 netTask 直接使用)
* 入口参数p_sock - Socket 控制块指针 net_sock_t *
* 返回值0 - 关闭成功 int
* -1 - 关闭失败 int
* 限定条件:必须在 netTask 上下文中调用p_sock->in_use == 1
* 函数说明:直接操作 Socket 控制块不经过消息队列。TCP Socket 先断开连接再释放控制块
*/
int net_close_sock(net_sock_t *p_sock) {
if (NULL == p_sock) {
s_net_errno = NET_ERR_BADF;
return -1;
}
return net_close_locked(p_sock);
}
/*
* 函数功能:注册 Socket 事件回调函数
* 入口参数sockfd - Socket 描述符 int >= 0 && < NET_MAX_SOCKETS
* cb - 回调函数指针 net_event_cb_t可为 NULL 取消注册)
* arg - 用户自定义参数指针 void *
* 返回值0 - 注册成功 int
* -1 - 注册失败 int
* 限定条件Socket 已创建且 in_use == 1net_init() 已调用
*/
int net_set_event_cb(int sockfd, net_event_cb_t cb, void *arg) {
net_sock_t *p_sock;
/* 参数检<E695B0>?*/
if (sockfd < 0 || sockfd >= NET_MAX_SOCKETS) {
s_net_errno = NET_ERR_BADF;
return -1;
}
p_sock = &s_net_socks[sockfd];
if (!p_sock->in_use) {
s_net_errno = NET_ERR_BADF;
return -1;
}
p_sock->event_cb = cb;
p_sock->event_arg = arg;
return 0;
}
/*
* 函数功能:获取 Socket 最后一次操作返回的错误码
* 返回值当前错误码值NET_OK=0, NET_ERR_xxx=-N int
* 限定条件:无
* 函数说明:每次 net_*() API 调用失败后,错误码被设置。调用成功时不自动清零
*/
int net_get_errno(void) {
return s_net_errno;
}
/*
* 函数功能:将点分十进制 IP 地址字符串转换为网络字节序的 32 位地址
* 入口参数cp - 点分十进制 IP 字符串(如 "192.168.1.100" const char *
* 出口参数:无
* 返回值:网络字节序的 32 位 IP 地址0 表示参数无效) uint32_t
* 限定条件:无
* 函数说明1. 逐段解析十进制数与并小端组装为 32 位值
* 2. 转换结果经 NET_htonl 转为网络字节序
*/
uint32_t net_inet_addr(const char *cp) {
uint32_t addr = 0;
uint32_t val;
int i;
if (NULL == cp) {
return 0;
}
for (i = 0; i < 4; i++) {
val = 0;
while (*cp >= '0' && *cp <= '9') {
val = val * 10 + (*cp - '0');
cp++;
}
addr |= (val << (i * 8));
if (*cp == '.') {
cp++;
}
}
return NET_htonl(addr);
}
/*
* 函数功能:将网络字节序的 32 位地址转换为点分十进制字符串
* 入口参数addr - 网络字节序的 32 位 IP 地址 uint32_t
* buf - 输出缓冲区(至少 16 字节) char *
* 出口参数buf - 填充点分十进制字符串
* 返回值:成功返回 buf 指针buf 为 NULL 时返回 NULL char *
* 限定条件buf 非空且长度足够(>= 16 字节)
* 函数说明1. 先经 NET_ntohl 转为主机字节序再格式化输出
*/
char *net_inet_ntoa(uint32_t addr, char *buf) {
uint32_t naddr = NET_ntohl(addr);
if (NULL == buf) {
return NULL;
}
sprintf(buf, "%lu.%lu.%lu.%lu",
(unsigned long)(naddr & 0xFF),
(unsigned long)((naddr >> 8) & 0xFF),
(unsigned long)((naddr >> 16) & 0xFF),
(unsigned long)((naddr >> 24) & 0xFF));
return buf;
}
/*
* 函数功能:端口字节序转换(主机序 -> 网络序)
* 入口参数hostshort - 主机字节序的 16 位端口 uint16_t
* 出口参数:无
* 返回值:网络字节序的 16 位端口 uint16_t
* 限定条件:无
* 函数说明:直接调用 NET_htons 完成转换
*/
uint16_t net_htons(uint16_t hostshort) {
return NET_htons(hostshort);
}
/*
* 函数功能:端口字节序转换(网络序 -> 主机序)
* 入口参数netshort - 网络字节序的 16 位端口 uint16_t
* 出口参数:无
* 返回值:主机字节序的 16 位端口 uint16_t
* 限定条件:无
* 函数说明:直接调用 NET_ntohs 完成转换
*/
uint16_t net_ntohs(uint16_t netshort) {
return NET_ntohs(netshort);
}
/*
* 函数功能IP 地址字节序转换(主机序 -> 网络序)
* 入口参数hostlong - 主机字节序的 32 位 IP 地址 uint32_t
* 出口参数:无
* 返回值:网络字节序的 32 位 IP 地址 uint32_t
* 限定条件:无
* 函数说明:直接调用 NET_htonl 完成转换
*/
uint32_t net_htonl(uint32_t hostlong) {
return NET_htonl(hostlong);
}
/*
* 函数功能IP 地址字节序转换(网络序 -> 主机序)
* 入口参数netlong - 网络字节序的 32 位 IP 地址 uint32_t
* 出口参数:无
* 返回值:主机字节序的 32 位 IP 地址 uint32_t
* 限定条件:无
* 函数说明:直接调用 NET_ntohl 完成转换
*/
uint32_t net_ntohl(uint32_t netlong) {
return NET_ntohl(netlong);
}
/*
* 函数功能:分配一个空闲的 Socket 控制块
* 入口参数:无
* 出口参数:无
* 返回值:空闲 Socket 索引0~NET_MAX_SOCKETS-1-1 表示无空闲 Socket int
* 限定条件s_net_socks 已定义
* 函数说明1. 线性扫描 s_net_socks返回首个 in_use == 0 的控制块索引
* 2. 调用方负责初始化该控制块
*/
static int alloc_socket(void) {
int i;
for (i = 0; i < NET_MAX_SOCKETS; i++) {
if (!s_net_socks[i].in_use) {
return i;
}
}
return -1;
}
/*
* 入口参数p_sock - 发生连接事件的 Socket 控制块指针 net_sock_t *
* 出口参数:无
* 返回值:无
* 限定条件net_poll() 检测到 CONNECT 中断后调用netTask 上下文
* 函数说明1. 单连接:自身转 ESTABLISHED 并读取对端地址
* 2. TCP ClientCONNECTING连接成功读取对端地址并置 ESTABLISHED
* 3. 连接成功后使能 KeepAlive 并触发 NET_EVENT_CONNECTED 回调
*/
static void handle_connect_event(net_sock_t *p_sock) {
if (p_sock->state == NET_SOCK_STATE_LISTENING) {
/* 单连接:收到 CONNECT 中断 = 自身有连接到达,转为 ESTABLISHED */
DBG_INFO("sock%d standalone accept", p_sock->ch395_sock);
uint8_t remote[6];
ch395f_get_remot_ipp(p_sock->ch395_sock, remote);
memcpy(p_sock->remote_ip_arr, remote, 4);
p_sock->remote_ip = ((uint32_t)remote[0]) |
((uint32_t)remote[1] << 8) |
((uint32_t)remote[2] << 16) |
((uint32_t)remote[3] << 24);
p_sock->remote_port = (uint16_t)(remote[4] | (remote[5] << 8));
p_sock->state = NET_SOCK_STATE_ESTABLISHED;
p_sock->send_ready = 1;
/* 清除残留的 TIMEOUT 中断标志 */
(void)ch395f_get_sock_int_status(p_sock->ch395_sock);
fire_event(p_sock, NET_EVENT_CONNECTED);
} else if (p_sock->state == NET_SOCK_STATE_CONNECTING) {
/* TCP Client 连接成功 */
uint8_t remote[6];
ch395f_get_remot_ipp(p_sock->ch395_sock, remote);
memcpy(p_sock->remote_ip_arr, remote, 4);
p_sock->remote_ip = ((uint32_t)remote[0]) |
((uint32_t)remote[1] << 8) |
((uint32_t)remote[2] << 16) |
((uint32_t)remote[3] << 24);
p_sock->remote_port = (uint16_t)(remote[4] | (remote[5] << 8));
p_sock->state = NET_SOCK_STATE_ESTABLISHED;
ch395f_set_keepalive_enable(p_sock->ch395_sock, 1);
fire_event(p_sock, NET_EVENT_CONNECTED);
}
}
/*
* 函数功能:处理 Socket 断开DISCONNECT事件更新 Socket 状态机
* 入口参数p_sock - 发生断开事件的 Socket 控制块指针 net_sock_t *
* 出口参数:无
* 返回值:无
* 限定条件net_poll() 检测到 DISCONNECT 中断后调用netTask 上下文
* 函数说明1. CH395F 在 DISCONNECT 后自动关闭 SocketFUN_PARA 默认),需重新 OPEN + LISTEN
* 2. 单连接:置 CLOSED交由 net_recv_locked 排空剩余数据后再关
* 3. 触发 NET_EVENT_DISCONNECTED 回调
*/
static void handle_disconnect_event(net_sock_t *p_sock) {
if (p_sock->state == NET_SOCK_STATE_LISTENING) {
DBG_ERROR("WARNING: listen sock%d got disconnect", p_sock->ch395_sock);
} else if (p_sock->state == NET_SOCK_STATE_ESTABLISHED) {
/* 单连接:先不关 CH395F Socket
* 留给 net_recv_locked 把剩余数据读出来再关,置 CLOSED 等待应用重新监听 */
DBG_INFO("sock%d DISCONNECTED, releasing", p_sock->ch395_sock);
p_sock->state = NET_SOCK_STATE_CLOSED;
fire_event(p_sock, NET_EVENT_DISCONNECTED);
} else if (p_sock->state != NET_SOCK_STATE_CLOSED &&
p_sock->state != NET_SOCK_STATE_BOUND &&
p_sock->state != NET_SOCK_STATE_CREATED) {
/* 未预期的断开 */
DBG_ERROR("sock%d disconnect in state %d", p_sock->ch395_sock, p_sock->state);
fire_event(p_sock, NET_EVENT_DISCONNECTED);
}
}
/*
* 函数功能:处理数据接收事件
* 函数功能:处理 Socket 数据接收RECV_OK事件
* 入口参数p_sock - 发生接收事件的 Socket 控制块指针 net_sock_t *
* 出口参数:无
* 返回值:无
* 限定条件net_poll() 检测到 RECV_OK 中断后调用netTask 上下文
* 函数说明1. 仅触发 NET_EVENT_DATA_RECEIVED 回调通知上层
* 2. 不在此读取 recv_len避免与 net_recv_locked 中的读取冲突Trap 13
*/
static void handle_recv_event(net_sock_t *p_sock) {
/* RECV_OK 中断已触发,直接通知上层。不在此读 recv_len
* 避免和 net_recv_locked 里面的 ch395f_get_recv_len 冲突Trap 13 */
fire_event(p_sock, NET_EVENT_DATA_RECEIVED);
}
/*
* 函数功能:处理 Socket 发送完成SEND_OK事件
* 入口参数p_sock - 发生发送完成事件的 Socket 控制块指针 net_sock_t *
* 出口参数:无
* 返回值:无
* 限定条件net_poll() 检测到 SEND_OK 中断后调用netTask 上下文
* 函数说明:触发 NET_EVENT_SEND_COMPLETE 回调通知上层
*/
static void handle_send_event(net_sock_t *p_sock) {
fire_event(p_sock, NET_EVENT_SEND_COMPLETE);
}
/*
* 函数功能:处理 Socket 超时SOCK_TIMEOUT事件更新 Socket 状态机
* 入口参数p_sock - 发生超时事件的 Socket 控制块指针 net_sock_t *
* 出口参数:无
* 返回值:无
* 限定条件net_poll() 检测到 SOCK_TIMEOUT 中断后调用netTask 上下文
* 函数说明1. TCP ClientCONNECTING连接超时回到 CREATED 状态
* 2. 已建立连接:回到 CLOSED单连接
* 3. 监听态:异常超时,记录错误日志
* 4. 触发 NET_EVENT_TIMEOUT 回调
*/
static void handle_timeout_event(net_sock_t *p_sock) {
if (p_sock->state == NET_SOCK_STATE_CONNECTING) {
/* TCP Client 连接超时 */
DBG_ERROR("sock%d CONNECT TIMEOUT", p_sock->ch395_sock);
p_sock->state = NET_SOCK_STATE_CREATED;
} else if (p_sock->state == NET_SOCK_STATE_ESTABLISHED) {
DBG_ERROR("sock%d TIMEOUT", p_sock->ch395_sock);
p_sock->state = NET_SOCK_STATE_CLOSED;
} else if (p_sock->state == NET_SOCK_STATE_LISTENING) {
/* 监听超时(异常情况) */
DBG_ERROR("sock%d LISTEN TIMEOUT", p_sock->ch395_sock);
}
fire_event(p_sock, NET_EVENT_TIMEOUT);
}
/*
* 函数功能:触发 Socket 事件回调(若已注册)
* 入口参数p_sock - 触发事件的 Socket 控制块指针 net_sock_t *
* event - 事件类型 net_event_t
* 出口参数:无
* 返回值:无
* 限定条件net_init() 已调用
* 函数说明1. 仅当 p_sock->event_cb 非空时调用回调
* 2. 回调参数为 ch395_sock、event、event_arg
*/
static void fire_event(net_sock_t *p_sock, net_event_t event) {
if (NULL != p_sock->event_cb) {
p_sock->event_cb(p_sock->ch395_sock, event, p_sock->event_arg);
}
}
/*
* 函数功能:将点分十进制 IP 地址字符串解析为 4 字节数组
* 入口参数str - 点分十进制 IP 字符串(如 "192.168.1.100" const char *
* ip_arr - 输出4 字节 IP 数组(网络序,低字节在前) uint8_t *
* 出口参数ip_arr - 填充解析后的 IP 地址
* 返回值:无
* 限定条件str 与 ip_arr 均非空
* 函数说明1. 逐段解析十进制数写入 ip_arr[0..3]
* 2. 参数非法NULL时直接返回不做解析
*/
static void parse_ip_string(const char *str, uint8_t *ip_arr) {
uint32_t val;
int i;
if (NULL == str ||NULL == ip_arr) {
return;
}
for (i = 0; i < 4; i++) {
val = 0;
while (*str >= '0' && *str <= '9') {
val = val * 10 + (*str - '0');
str++;
}
ip_arr[i] = (uint8_t)val;
if (*str == '.') {
str++;
}
}
}
/*
* 函数功能:根据描述符获取 Socket 控制块指针(内部使用)
* 入口参数sockfd - Socket 描述符 int 0 - NET_MAX_SOCKETS-1
* 出口参数:无
* 返回值Socket 控制块指针;描述符越界或未分配时返回 NULL net_sock_t *
* 限定条件:无
* 函数说明1. 描述符越界(<0 或 >= NET_MAX_SOCKETS返回 NULL
* 2. 未使用in_use == 0的 Socket 返回 NULL
*/
net_sock_t *net_get_sock(int sockfd) {
if (sockfd < 0 || sockfd >= NET_MAX_SOCKETS) {
return NULL;
}
if (!s_net_socks[sockfd].in_use) {
return NULL;
}
return &s_net_socks[sockfd];
}
/*
* 函数功能:获取 Socket 本地地址IP + Port
* 入口参数sockfd - socket 描述符 int
* addr - 输出:本地地址 struct net_sockaddr *
* addrlen - 输入输出:地址结构体长度 int *
* 返回值0 - 成功,-1 - 失败 int
* 限定条件socket 已创建或已绑定
* 函数说明:返回 sin_family + sin_port网络序+ sin_addr网络序从 net_init 缓存的 s_local_ip
*/
int net_getsockname(int sockfd, struct net_sockaddr *addr, int *addrlen) {
if (sockfd < 0 || sockfd >= NET_MAX_SOCKETS) {
s_net_errno = NET_ERR_BADF;
return -1;
}
net_sock_t *p_sock = &s_net_socks[sockfd];
if (!p_sock->in_use && p_sock->state == NET_SOCK_STATE_CLOSED) {
s_net_errno = NET_ERR_BADF;
return -1;
}
if (NULL == addr ||NULL == addrlen || *addrlen < (int)sizeof(struct net_sockaddr_in)) {
s_net_errno = NET_ERR_INVAL;
return -1;
}
struct net_sockaddr_in *p_addr_in = (struct net_sockaddr_in *)addr;
memset(p_addr_in, 0, sizeof(struct net_sockaddr_in));
p_addr_in->sin_family = NET_AF_INET;
p_addr_in->sin_port = net_htons(p_sock->local_port);
p_addr_in->sin_addr.s_addr = ((uint32_t)s_local_ip[0]) |
((uint32_t)s_local_ip[1] << 8) |
((uint32_t)s_local_ip[2] << 16) |
((uint32_t)s_local_ip[3] << 24);
*addrlen = sizeof(struct net_sockaddr_in);
return 0;
}