977 lines
26 KiB
C
Executable File
977 lines
26 KiB
C
Executable File
/****************************************************************************
|
|
|
|
Copyright(c) 2019 by Aerospace C.Power (Chongqing) Microelectronics. ALL RIGHTS RESERVED.
|
|
|
|
This Information is proprietary to Aerospace C.Power (Chongqing) Microelectronics and MAY NOT
|
|
be copied by any method or incorporated into another program without
|
|
the express written consent of Aerospace C.Power. This Information or any portion
|
|
thereof remains the property of Aerospace C.Power. The Information contained herein
|
|
is believed to be accurate and Aerospace C.Power assumes no responsibility or
|
|
liability for its use in any way and conveys no license or title under
|
|
any patent or copyright and makes no representation or warranty that this
|
|
Information is free from patent or copyright infringement.
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
/* os_shim header files */
|
|
#include "os_types_api.h"
|
|
#include "os_event_api.h"
|
|
|
|
/* lwip header files */
|
|
#include "posix/sys/socket.h"
|
|
|
|
/* iot header files */
|
|
#include "iot_config.h"
|
|
#include "iot_task_api.h"
|
|
#include "iot_utils_api.h"
|
|
#include "iot_errno_api.h"
|
|
#include "iot_socket_api.h"
|
|
#include "iot_mem_pool_api.h"
|
|
|
|
|
|
#if IOT_LWIP_SUPPORT
|
|
|
|
#define IOT_SOCKET_MSG_POOL_SIZE 16
|
|
#define IOT_SOCKET_RECV_BUF_LEN 1024
|
|
#define IOT_SOCKET_SYNC_IP "127.0.0.1"
|
|
#define IOT_SOCKET_SYNC_UDP_PORT 44444
|
|
|
|
#define IOT_SOCKET_MAX_CNT 12
|
|
|
|
/* event for socket task to handle */
|
|
#define IOT_SOCKET_MSG_TYPE_CREATE_SOCK 1
|
|
#define IOT_SOCKET_MSG_TYPE_DELETE_SOCK 2
|
|
|
|
/* data send to sync_socket. */
|
|
const static uint8_t sync_data[] = {0xFA, 0xFB, 0xFC, 0xFD};
|
|
|
|
typedef struct _iot_socket_op_arg {
|
|
/* address to be bind with the new created socket.
|
|
* as address for ipv4 and ipv6 share the same buffer,
|
|
* we can always use ipv4 address to refer to this buffer.
|
|
*/
|
|
union {
|
|
struct sockaddr_in ipv4_addr;
|
|
#if LWIP_IPV6
|
|
struct sockaddr_in6 ipv6_addr;
|
|
#endif
|
|
} addr;
|
|
/* type of the socket to be create, see IOT_SOCKET_TYPE_XXX */
|
|
uint8_t sock_type;
|
|
/* callback function for the socket. */
|
|
iot_socket_cb_func_t func;
|
|
/* required headroom for received data of iot_socket_cb_func_t. */
|
|
uint8_t headroom;
|
|
/* socket to operate on. */
|
|
int32_t socket;
|
|
/* result of the operation. see ERR_XXX */
|
|
uint32_t result;
|
|
} iot_socket_op_arg_t;
|
|
|
|
/* socket task message */
|
|
typedef struct _iot_socket_msg {
|
|
/* iot task message */
|
|
iot_task_msg_t task_msg;
|
|
void *arg_ptr;
|
|
} iot_socket_msg_t;
|
|
|
|
typedef struct _iot_socket_entry {
|
|
/* socket handle. */
|
|
int32_t socket;
|
|
/* callback function for the socket, shall not be NULL for valid entry. */
|
|
iot_socket_cb_func_t func;
|
|
/* socket of the socket, see IOT_SOCKET_TYPE_XXX for detail. */
|
|
uint8_t sock_type;
|
|
/* domain of the socket, AF_NET for ipv4, AF_NET6 for ipv6. */
|
|
uint8_t domain;
|
|
/* required headroom for received data of iot_socket_cb_func_t. */
|
|
uint8_t headroom;
|
|
} iot_socket_entry_t;
|
|
|
|
typedef struct _iot_socket_global {
|
|
/* address for sync socket. */
|
|
struct sockaddr_in sync_addr;
|
|
/* pointer to socket task message queues */
|
|
iot_msg_queue_t msg_q;
|
|
/* socket task messages pool */
|
|
iot_mem_pool_t *msg_p;
|
|
/* socket and its callback method */
|
|
iot_socket_entry_t sock_entry[IOT_SOCKET_MAX_CNT];
|
|
/* socket used for sync */
|
|
int32_t sync_socket;
|
|
/* event for sync. only one task can create/delete socket at any time. */
|
|
os_event_h sock_evt;
|
|
/* event for sync. make async operation looks like sync operation. */
|
|
os_event_h sock_op_done;
|
|
/* socket task handle. */
|
|
iot_task_h task_h;
|
|
} iot_socket_global_t;
|
|
|
|
iot_socket_global_t *p_socket_glb = NULL;
|
|
|
|
static inline uint32_t iot_socket_is_ipv6_enabled()
|
|
{
|
|
uint32_t ipv6_enabled = 0;
|
|
#if LWIP_IPV6
|
|
ipv6_enabled = 1;
|
|
#endif
|
|
return ipv6_enabled;
|
|
}
|
|
|
|
static inline uint32_t iot_socket_is_valid_domain(int32_t domain)
|
|
{
|
|
if (domain == AF_INET) {
|
|
return 1;
|
|
}
|
|
|
|
if (domain == AF_INET6 && iot_socket_is_ipv6_enabled()) {
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline uint32_t iot_socket_set_addr(struct sockaddr_in *addr,
|
|
uint8_t domain, const char* ip, uint16_t port)
|
|
{
|
|
struct sockaddr_in6 *addr_v6;
|
|
if (domain == AF_INET) {
|
|
if (ip == NULL) {
|
|
ip4_addr_set_any((ip4_addr_t*)&addr->sin_addr.s_addr);
|
|
} else {
|
|
addr->sin_addr.s_addr = inet_addr(ip);
|
|
if (IPADDR_NONE == addr->sin_addr.s_addr) {
|
|
/* invalid ip address. */
|
|
return ERR_INVAL;
|
|
}
|
|
}
|
|
|
|
addr->sin_family = AF_INET;
|
|
addr->sin_port = htons(port);
|
|
addr->sin_len = sizeof(struct sockaddr_in);
|
|
return ERR_OK;
|
|
} else if (domain == AF_INET6) {
|
|
#if LWIP_IPV6
|
|
addr_v6 = (struct sockaddr_in6 *)addr;
|
|
if (ip == NULL) {
|
|
ip6_addr_set_any((ip6_addr_t*)&addr_v6->sin6_addr);
|
|
} else {
|
|
if (!ip6addr_aton(ip, (ip6_addr_t*)&addr_v6->sin6_addr)) {
|
|
/* invalid ip address. */
|
|
return ERR_INVAL;
|
|
}
|
|
}
|
|
|
|
addr_v6->sin6_family = AF_INET6;
|
|
addr_v6->sin6_port = htons(port);
|
|
addr_v6->sin6_len = sizeof(struct sockaddr_in6);
|
|
return ERR_OK;
|
|
#else
|
|
(void)addr_v6;
|
|
(void)addr;
|
|
(void)domain;
|
|
(void)ip;
|
|
(void)port;
|
|
return ERR_NOSUPP;
|
|
#endif
|
|
} else {
|
|
return ERR_INVAL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* @brief: add a socket to local socket list
|
|
* @param socket: the socket to be added.
|
|
* @param sock_type: type of the socket. see IOT_SOCKET_TYPE_XXX.
|
|
* @param domain: domain of the socket, AF_NET or AF_INET6.
|
|
* @param func: callback function to be called when data/event
|
|
* arrival on this socket.
|
|
* @param pkt_headroom: required headroom for received data of
|
|
* iot_socket_cb_func_t.
|
|
* @return: ERR_OK for succeed case, other ERR_XXX for failed case.
|
|
*/
|
|
static uint32_t iot_socket_add_sock_entry(int32_t socket, uint8_t sock_type,
|
|
uint8_t domain, iot_socket_cb_func_t func, uint8_t pkt_headroom)
|
|
{
|
|
uint32_t i;
|
|
iot_socket_entry_t *free_entry = NULL;
|
|
if (func == NULL) {
|
|
return ERR_INVAL;
|
|
}
|
|
|
|
if (!iot_socket_is_valid_domain(domain)) {
|
|
return ERR_INVAL;
|
|
}
|
|
|
|
for (i = 0; i < IOT_SOCKET_MAX_CNT; ++i) {
|
|
if (free_entry == NULL && p_socket_glb->sock_entry[i].func == NULL) {
|
|
/* find a free entry. */
|
|
free_entry = &p_socket_glb->sock_entry[i];
|
|
}
|
|
|
|
/* if find the socket entry, update it's callback function. */
|
|
if (p_socket_glb->sock_entry[i].func &&
|
|
p_socket_glb->sock_entry[i].socket == socket) {
|
|
p_socket_glb->sock_entry[i].func = func;
|
|
p_socket_glb->sock_entry[i].sock_type = sock_type;
|
|
p_socket_glb->sock_entry[i].domain = domain;
|
|
p_socket_glb->sock_entry[i].headroom = pkt_headroom;
|
|
return ERR_OK;
|
|
}
|
|
}
|
|
|
|
if (free_entry) {
|
|
free_entry->func = func;
|
|
free_entry->socket = socket;
|
|
free_entry->sock_type = sock_type;
|
|
free_entry->domain = domain;
|
|
free_entry->headroom = pkt_headroom;
|
|
return ERR_OK;
|
|
}
|
|
|
|
/* failed, no free entry. */
|
|
return ERR_FAIL;
|
|
}
|
|
|
|
static iot_socket_entry_t* iot_socket_get_sock_entry(int32_t socket)
|
|
{
|
|
uint32_t i;
|
|
for (i = 0; i < IOT_SOCKET_MAX_CNT; ++i) {
|
|
/* if find the socket entry, update it's callback function */
|
|
if (p_socket_glb->sock_entry[i].func &&
|
|
p_socket_glb->sock_entry[i].socket == socket) {
|
|
return &p_socket_glb->sock_entry[i];
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void iot_socket_rm_all_sock_entry()
|
|
{
|
|
uint32_t i;
|
|
for (i = 0; i < IOT_SOCKET_MAX_CNT; ++i) {
|
|
/* if find the socket entry, update it's callback function */
|
|
if (p_socket_glb->sock_entry[i].func) {
|
|
p_socket_glb->sock_entry[i].func = NULL;
|
|
p_socket_glb->sock_entry[i].socket = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void iot_socket_rm_sock_entry(int32_t socket)
|
|
{
|
|
iot_socket_entry_t* sock_entry;
|
|
sock_entry = iot_socket_get_sock_entry(socket);
|
|
if (sock_entry) {
|
|
/* if find the socket entry, update it's callback function */
|
|
sock_entry->func = NULL;
|
|
sock_entry->socket = 0;
|
|
sock_entry->sock_type = 0;
|
|
sock_entry->domain = 0;
|
|
}
|
|
}
|
|
|
|
static iot_socket_cb_func_t iot_socket_get_cb(int32_t socket)
|
|
{
|
|
iot_socket_entry_t *sock_entry = iot_socket_get_sock_entry(socket);
|
|
if (sock_entry) {
|
|
return sock_entry->func;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static uint8_t iot_socket_get_msg_hdr_len(int32_t socket)
|
|
{
|
|
iot_socket_entry_t *sock_entry = iot_socket_get_sock_entry(socket);
|
|
if (sock_entry) {
|
|
return sock_entry->headroom;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void iot_socket_invoke_cb(int32_t socket, uint32_t cb_type,
|
|
iot_pkt_t *pkt)
|
|
{
|
|
iot_socket_cb_func_t cb = iot_socket_get_cb(socket);
|
|
if (cb) {
|
|
cb(cb_type, socket, pkt);
|
|
} else {
|
|
iot_pkt_free(pkt);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* @brief: alloc a message from socket task's msg pool
|
|
* @return: socket message. NULL if failed to allocate a message.
|
|
*/
|
|
iot_socket_msg_t *iot_socket_task_alloc_msg()
|
|
{
|
|
iot_task_msg_t *msg;
|
|
msg = iot_mem_pool_alloc_with_reserve(p_socket_glb->msg_p, 0);
|
|
if (msg) {
|
|
iot_msg_entry_init(&msg->link);
|
|
}
|
|
|
|
return (iot_socket_msg_t*)msg;
|
|
}
|
|
|
|
/*
|
|
* @brief: free a msg to socket task's msg pool
|
|
* @param socket_msg: socket message to be freed.
|
|
*/
|
|
void iot_socket_task_free_msg(iot_socket_msg_t *socket_msg)
|
|
{
|
|
iot_task_msg_t *msg = (iot_task_msg_t *)&socket_msg->task_msg;
|
|
iot_mem_pool_free(p_socket_glb->msg_p, msg);
|
|
}
|
|
|
|
/*
|
|
* @brief: send data to sync socket to notify socket task
|
|
* that there is msg in queue and shall be processed.
|
|
*/
|
|
static void iot_socket_task_notify()
|
|
{
|
|
/* send a udp packet to signal socket. */
|
|
sendto(p_socket_glb->sync_socket, sync_data, sizeof(sync_data), 0,
|
|
(const struct sockaddr *)&p_socket_glb->sync_addr,
|
|
sizeof(p_socket_glb->sync_addr));
|
|
}
|
|
|
|
/*
|
|
* @brief: put a msg into msg queue and notify socket task.
|
|
* it returns when socket task finish operation.
|
|
* @param sock_msg: msg to be put into queue of socket task.
|
|
*/
|
|
static void iot_socket_task_put_msg_sync(iot_socket_msg_t *sock_msg)
|
|
{
|
|
iot_task_msg_t *msg = &sock_msg->task_msg;
|
|
os_wait_event(p_socket_glb->sock_evt, MAX_TIME);
|
|
iot_msg_queue_put(&p_socket_glb->msg_q, &msg->link);
|
|
iot_socket_task_notify();
|
|
/* wait until the operation finished.
|
|
* make async operation looks like sync operation.
|
|
*/
|
|
os_wait_event(p_socket_glb->sock_op_done, MAX_TIME);
|
|
os_set_event(p_socket_glb->sock_evt);
|
|
}
|
|
|
|
/*
|
|
* @brief: get a msg from msg pool of socket task.
|
|
* @return: msg from msg queue of socket task, NULL if failed..
|
|
*/
|
|
static iot_socket_msg_t* iot_socket_task_get_msg()
|
|
{
|
|
iot_task_msg_t *msg = NULL;
|
|
iot_msg_entry_t *entry = iot_msg_queue_get(&p_socket_glb->msg_q);
|
|
if (entry) {
|
|
msg = container_of(entry, iot_task_msg_t, link);
|
|
}
|
|
|
|
return (iot_socket_msg_t*)msg;
|
|
}
|
|
|
|
static void iot_socket_create_internal(iot_socket_msg_t *msg)
|
|
{
|
|
int32_t ret;
|
|
int32_t sock;
|
|
uint8_t domain;
|
|
uint32_t proto_type;
|
|
uint8_t socket_type;
|
|
iot_socket_op_arg_t *p_arg = (iot_socket_op_arg_t *)msg->arg_ptr;
|
|
if (p_arg == NULL) {
|
|
return;
|
|
}
|
|
|
|
socket_type = p_arg->sock_type;
|
|
if (socket_type == IOT_SOCKET_TYPE_UDP) {
|
|
proto_type = SOCK_DGRAM;
|
|
} else {
|
|
/* unsupported socket type. */
|
|
p_arg->socket = -1;
|
|
p_arg->result = ERR_INVAL;
|
|
return;
|
|
}
|
|
|
|
domain = p_arg->addr.ipv4_addr.sin_family;
|
|
sock = socket(domain, proto_type, 0);
|
|
ret = bind(sock, (struct sockaddr*)&p_arg->addr.ipv4_addr,
|
|
p_arg->addr.ipv4_addr.sin_len);
|
|
if (ret) {
|
|
/* failed to bind the socket */
|
|
closesocket(sock);
|
|
p_arg->socket = -1;
|
|
p_arg->result = ERR_FAIL;
|
|
return;
|
|
}
|
|
|
|
switch (proto_type) {
|
|
case SOCK_DGRAM:
|
|
{
|
|
p_arg->socket = sock;
|
|
p_arg->result = ERR_OK;
|
|
iot_socket_add_sock_entry(sock, socket_type, domain, p_arg->func,
|
|
p_arg->headroom);
|
|
break;
|
|
}
|
|
case SOCK_STREAM:
|
|
default:
|
|
p_arg->socket = -1;
|
|
p_arg->result = ERR_NOSUPP;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void iot_socket_delete_internal(iot_socket_msg_t *msg)
|
|
{
|
|
int32_t sock;
|
|
iot_socket_entry_t *sock_entry;
|
|
iot_socket_op_arg_t *p_arg = (iot_socket_op_arg_t *)msg->arg_ptr;
|
|
if (p_arg == NULL) {
|
|
return;
|
|
}
|
|
|
|
sock = p_arg->socket;
|
|
if (sock <= 0) {
|
|
/* socket 0 is for internal use. it cannot be deleted.
|
|
* socket less than 0 is invalid.
|
|
*/
|
|
return;
|
|
}
|
|
|
|
sock_entry = iot_socket_get_sock_entry(sock);
|
|
if (sock_entry == NULL) {
|
|
return;
|
|
}
|
|
|
|
switch (sock_entry->sock_type) {
|
|
case IOT_SOCKET_TYPE_UDP:
|
|
{
|
|
closesocket(sock);
|
|
iot_socket_rm_sock_entry(sock);
|
|
break;
|
|
}
|
|
default:
|
|
/* api shall reject unsupported operation.
|
|
* codes shall not reach here.
|
|
*/
|
|
IOT_ASSERT(0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* @brief: message handler method. It always frees the msg.
|
|
* @param msg: message to be handled.
|
|
*/
|
|
static void iot_socket_msg_handler(iot_socket_msg_t *msg)
|
|
{
|
|
if (msg == NULL) {
|
|
return;
|
|
}
|
|
|
|
switch (msg->task_msg.type) {
|
|
case IOT_SOCKET_MSG_TYPE_CREATE_SOCK:
|
|
{
|
|
iot_socket_create_internal(msg);
|
|
os_set_event(p_socket_glb->sock_op_done);
|
|
break;
|
|
}
|
|
case IOT_SOCKET_MSG_TYPE_DELETE_SOCK:
|
|
{
|
|
iot_socket_delete_internal(msg);
|
|
os_set_event(p_socket_glb->sock_op_done);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
iot_socket_task_free_msg(msg);
|
|
}
|
|
|
|
/*
|
|
* @brief: get fd object from current socket list.
|
|
* @param set: output parameter for fd_set object
|
|
* @return: max socket handle in fd_set, -1 for failed case.
|
|
*/
|
|
static int32_t iot_socket_get_fd(fd_set *set)
|
|
{
|
|
uint32_t i;
|
|
int32_t max_fd = -1;
|
|
/* clear the fd_set object. */
|
|
os_mem_set(set, 0, sizeof(*set));
|
|
for (i = 0; i < IOT_SOCKET_MAX_CNT; ++i) {
|
|
if (p_socket_glb->sock_entry[i].func) {
|
|
if (p_socket_glb->sock_entry[i].socket > max_fd) {
|
|
max_fd = p_socket_glb->sock_entry[i].socket;
|
|
}
|
|
FD_SET(p_socket_glb->sock_entry[i].socket, set);
|
|
}
|
|
}
|
|
|
|
return max_fd;
|
|
}
|
|
|
|
/*
|
|
* @brief: task function for socket task.
|
|
* it use a socket as a event for synchronization.
|
|
* when recv data from sync socket, it takes a msg from task
|
|
* msg queue and handle it. when recv data from other socket,
|
|
* it call corresponding callback function.
|
|
* @arg: argument for the task func
|
|
*/
|
|
static void iot_socket_task_func(void* arg)
|
|
{
|
|
int32_t i;
|
|
int32_t result;
|
|
int32_t max_fd;
|
|
int32_t pending_data_len = 0;
|
|
uint8_t pkt_headroom = 0;
|
|
fd_set fd;
|
|
iot_pkt_t *pkt;
|
|
|
|
iot_printf("%s - %p\n", __FUNCTION__, arg);
|
|
while (1) {
|
|
max_fd = iot_socket_get_fd(&fd);
|
|
result = select(max_fd + 1, &fd, NULL, NULL, NULL);
|
|
if (result < 0) {
|
|
/* error happened */
|
|
continue;
|
|
}
|
|
|
|
for (i = 0; i <= max_fd; i++) {
|
|
if (!FD_ISSET(i, &fd)) {
|
|
continue;
|
|
}
|
|
|
|
if (ioctlsocket(i, FIONREAD, &pending_data_len)) {
|
|
/* for ioctlsocket to work on lwip, LWIP_FIONREAD_LINUXMODE
|
|
* shall be set to 1 to enable Linux style ioctl/FIONREAD.
|
|
* Linux style ioctl/FIONREAD return length next pending data.
|
|
* Windows style ioctl/FIONREAD return length of all pending
|
|
* data, which shall NOT be used here.
|
|
*/
|
|
IOT_ASSERT(0);
|
|
}
|
|
|
|
if (pending_data_len == 0) {
|
|
pending_data_len = 1;
|
|
}
|
|
|
|
pkt_headroom = iot_socket_get_msg_hdr_len(i);
|
|
pkt = iot_pkt_alloc(pending_data_len + pkt_headroom,
|
|
IOT_SOCKET_MID);
|
|
IOT_ASSERT(pkt);
|
|
iot_pkt_reserve(pkt, pkt_headroom);
|
|
result = recv(i, iot_pkt_data(pkt),
|
|
iot_pkt_tail_len(pkt) - pkt_headroom, 0);
|
|
iot_pkt_put(pkt, result);
|
|
iot_socket_invoke_cb(i, IOT_SOCKET_CB_DATA_RECV, pkt);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* @brief: callback method for sync socket
|
|
* @param cb_type: type of event that trigger the callback
|
|
* @param socket: the socket on which event occur
|
|
* @param data_pkt: pkt contains data for the callback.
|
|
* this method shall free data_pkt.
|
|
*/
|
|
static void iot_sg_sync_socket_cb(uint32_t cb_type, int32_t socket,
|
|
iot_pkt_t* data_pkt)
|
|
{
|
|
uint8_t *ptr;
|
|
iot_socket_msg_t *msg;
|
|
if (cb_type != IOT_SOCKET_CB_DATA_RECV) {
|
|
return;
|
|
}
|
|
|
|
if (socket != p_socket_glb->sync_socket) {
|
|
return;
|
|
}
|
|
|
|
if (data_pkt == NULL) {
|
|
return;
|
|
}
|
|
|
|
ptr = iot_pkt_data(data_pkt);
|
|
if (iot_pkt_data_len(data_pkt) == sizeof(sync_data) &&
|
|
os_mem_cmp(ptr, sync_data, sizeof(sync_data)) == 0) {
|
|
/* receives data from sync socket and pattern matches. */
|
|
msg = iot_socket_task_get_msg();
|
|
iot_socket_msg_handler(msg);
|
|
}
|
|
|
|
iot_pkt_free(data_pkt);
|
|
}
|
|
|
|
uint32_t iot_socket_task_init()
|
|
{
|
|
int32_t ret;
|
|
uint8_t socket_created = 0;
|
|
uint8_t msg_q_created = 0;
|
|
|
|
if (p_socket_glb == NULL) {
|
|
p_socket_glb = os_mem_malloc(IOT_SOCKET_MID, sizeof(*p_socket_glb));
|
|
if (p_socket_glb == NULL) {
|
|
goto err_label;
|
|
}
|
|
}
|
|
|
|
if (p_socket_glb->task_h) {
|
|
/* don't create again. */
|
|
return ERR_OK;
|
|
}
|
|
|
|
/* event for synchronization, it's set by default. */
|
|
p_socket_glb->sock_evt = os_create_event(IOT_SMART_GRID_MID, 1);
|
|
if (p_socket_glb->sock_evt == 0) {
|
|
goto err_label;
|
|
}
|
|
|
|
p_socket_glb->sock_op_done = os_create_event(IOT_SMART_GRID_MID, 0);
|
|
if (p_socket_glb->sock_op_done == 0) {
|
|
goto err_label;
|
|
}
|
|
|
|
p_socket_glb->sync_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
|
if (p_socket_glb->sync_socket >= 0) {
|
|
socket_created = 1;
|
|
} else {
|
|
/* create socket failed. close the socket. */
|
|
goto err_label;
|
|
}
|
|
|
|
/* init local address for sync. */
|
|
iot_socket_set_addr(&p_socket_glb->sync_addr, AF_INET,
|
|
IOT_SOCKET_SYNC_IP, IOT_SOCKET_SYNC_UDP_PORT);
|
|
|
|
/* bind socket with local ipv4 address. */
|
|
ret = bind(p_socket_glb->sync_socket,
|
|
(const struct sockaddr*)&p_socket_glb->sync_addr,
|
|
sizeof(p_socket_glb->sync_addr));
|
|
iot_printf("bind socket, ret = %d\n", ret);
|
|
if (ret) {
|
|
/* bind failed. close the socket. */
|
|
goto err_label;
|
|
}
|
|
|
|
ret = iot_mem_pool_new(IOT_SOCKET_MID, IOT_SOCKET_MSG_POOL_SIZE,
|
|
sizeof(iot_socket_msg_t), &p_socket_glb->msg_p, 1);
|
|
if (ret != ERR_OK) {
|
|
goto err_label;
|
|
}
|
|
|
|
msg_q_created = 1;
|
|
ret = iot_msg_queue_init(&p_socket_glb->msg_q);
|
|
if (ret != ERR_OK) {
|
|
goto err_label;
|
|
}
|
|
|
|
iot_socket_add_sock_entry(p_socket_glb->sync_socket,
|
|
IOT_SOCKET_TYPE_UDP, AF_INET, iot_sg_sync_socket_cb, 0);
|
|
|
|
p_socket_glb->task_h = os_create_task(iot_socket_task_func,
|
|
p_socket_glb, IOT_SOCKET_TASK_PRIO);
|
|
if (p_socket_glb->task_h == 0) {
|
|
goto err_label;
|
|
}
|
|
|
|
iot_printf("%s: socket_task = 0x%08X\n",
|
|
__FUNCTION__, p_socket_glb->task_h);
|
|
return ERR_OK;
|
|
|
|
err_label:
|
|
if (p_socket_glb->sock_evt) {
|
|
os_delete_event(p_socket_glb->sock_evt);
|
|
p_socket_glb->sock_evt = 0;
|
|
}
|
|
|
|
if (p_socket_glb->sock_op_done) {
|
|
os_delete_event(p_socket_glb->sock_op_done);
|
|
p_socket_glb->sock_op_done = 0;
|
|
}
|
|
|
|
if (p_socket_glb->task_h) {
|
|
os_delete_task(p_socket_glb->task_h);
|
|
p_socket_glb->task_h = 0;
|
|
}
|
|
|
|
if (socket_created) {
|
|
closesocket(p_socket_glb->sync_socket);
|
|
}
|
|
|
|
if (p_socket_glb->msg_p) {
|
|
iot_mem_pool_destroy(p_socket_glb->msg_p);
|
|
}
|
|
|
|
if (msg_q_created) {
|
|
iot_msg_queue_deinit(&p_socket_glb->msg_q);
|
|
}
|
|
|
|
iot_socket_rm_all_sock_entry();
|
|
if (p_socket_glb) {
|
|
os_mem_free(p_socket_glb);
|
|
p_socket_glb = NULL;
|
|
}
|
|
|
|
/* failed to create socket task. */
|
|
return ERR_FAIL;
|
|
}
|
|
|
|
void iot_socket_task_deinit()
|
|
{
|
|
if (p_socket_glb == NULL) {
|
|
/* task was not initialized yet. */
|
|
return;
|
|
}
|
|
|
|
if (p_socket_glb->task_h) {
|
|
os_delete_task(p_socket_glb->task_h);
|
|
p_socket_glb->task_h = 0;
|
|
}
|
|
|
|
if (p_socket_glb->sock_evt) {
|
|
os_delete_event(p_socket_glb->sock_evt);
|
|
p_socket_glb->sock_evt = 0;
|
|
}
|
|
|
|
if (p_socket_glb->sock_op_done) {
|
|
os_delete_event(p_socket_glb->sock_op_done);
|
|
p_socket_glb->sock_op_done = 0;
|
|
}
|
|
|
|
closesocket(p_socket_glb->sync_socket);
|
|
p_socket_glb->sync_socket = 0;
|
|
iot_msg_queue_deinit(&p_socket_glb->msg_q);
|
|
|
|
if (p_socket_glb->msg_p) {
|
|
iot_mem_pool_destroy(p_socket_glb->msg_p);
|
|
p_socket_glb->msg_p = NULL;
|
|
}
|
|
|
|
iot_socket_rm_all_sock_entry();
|
|
os_mem_free(p_socket_glb);
|
|
p_socket_glb = NULL;
|
|
}
|
|
|
|
static inline uint32_t iot_socket_create_arg_validate(uint32_t socket_type,
|
|
uint32_t is_ipv6, iot_socket_cb_func_t recv_cb, int32_t *ret_sock)
|
|
{
|
|
int32_t result = ERR_OK;
|
|
uint32_t valid_sock_type;
|
|
if (recv_cb == NULL || ret_sock == NULL) {
|
|
/* invalid arguments. */
|
|
result = ERR_INVAL;
|
|
goto exit_label;
|
|
}
|
|
|
|
if (iot_socket_is_ipv6_enabled() == 0 && is_ipv6) {
|
|
/* ipv6 is not supported */
|
|
result = ERR_NOSUPP;
|
|
goto exit_label;
|
|
}
|
|
|
|
switch (socket_type) {
|
|
case IOT_SOCKET_TYPE_UDP:
|
|
{
|
|
valid_sock_type = 1;
|
|
break;
|
|
}
|
|
default:
|
|
valid_sock_type = 0;
|
|
break;
|
|
}
|
|
|
|
if (valid_sock_type == 0) {
|
|
result = ERR_INVAL;
|
|
goto exit_label;
|
|
}
|
|
|
|
exit_label:
|
|
return result;
|
|
}
|
|
|
|
uint32_t iot_socket_create(uint8_t socket_type, uint8_t is_ipv6,
|
|
const char *ip, uint16_t port, iot_socket_cb_func_t recv_cb,
|
|
uint8_t pkt_headroom, int32_t *ret_sock)
|
|
{
|
|
iot_socket_msg_t *msg;
|
|
int32_t result = ERR_OK;
|
|
uint8_t domain = AF_INET;
|
|
iot_socket_op_arg_t create_arg = {0};
|
|
|
|
if (p_socket_glb == NULL) {
|
|
iot_printf("%s - error. socket not start yet.\n", __FUNCTION__);
|
|
result = ERR_NOT_READY;
|
|
goto exit_label;
|
|
}
|
|
|
|
if (iot_socket_create_arg_validate(socket_type, is_ipv6,
|
|
recv_cb, ret_sock)) {
|
|
/* invalid arguments. */
|
|
result = ERR_INVAL;
|
|
goto exit_label;
|
|
}
|
|
|
|
msg = iot_socket_task_alloc_msg();
|
|
if (msg == NULL) {
|
|
result = ERR_NOMEM;
|
|
goto exit_label;
|
|
}
|
|
|
|
if (is_ipv6) {
|
|
domain = AF_INET6;
|
|
}
|
|
|
|
msg->task_msg.type = IOT_SOCKET_MSG_TYPE_CREATE_SOCK;
|
|
msg->task_msg.id = 0;
|
|
msg->arg_ptr = (void*)&create_arg;
|
|
create_arg.func = recv_cb;
|
|
create_arg.sock_type = socket_type;
|
|
create_arg.headroom = pkt_headroom;
|
|
if (ERR_OK != iot_socket_set_addr(&create_arg.addr.ipv4_addr,
|
|
domain, ip, port)) {
|
|
iot_socket_task_free_msg(msg);
|
|
result = ERR_INVAL;
|
|
goto exit_label;
|
|
}
|
|
|
|
|
|
iot_socket_task_put_msg_sync(msg);
|
|
*ret_sock = create_arg.socket;
|
|
result = create_arg.result;
|
|
if (*ret_sock < 0) {
|
|
result = ERR_FAIL;
|
|
}
|
|
|
|
exit_label:
|
|
iot_printf("%s - %d, reason = %lu\n", __FUNCTION__, *ret_sock, result);
|
|
return result;
|
|
}
|
|
|
|
void iot_socket_delete(int32_t socket)
|
|
{
|
|
int32_t result = ERR_OK;
|
|
iot_socket_op_arg_t delete_arg = {0};
|
|
iot_socket_msg_t *msg;
|
|
|
|
if (p_socket_glb == NULL) {
|
|
iot_printf("%s - error. socket not start yet.\n", __FUNCTION__);
|
|
result = ERR_NOT_READY;
|
|
goto exit_label;
|
|
}
|
|
|
|
if (socket <= 0) {
|
|
/* socket 0 is for internal use. it shall not be deleted.
|
|
* socket less than 0 is invalid.
|
|
*/
|
|
result = ERR_INVAL;
|
|
goto exit_label;
|
|
}
|
|
|
|
msg = iot_socket_task_alloc_msg();
|
|
if (msg == NULL) {
|
|
result = ERR_NOMEM;
|
|
goto exit_label;
|
|
}
|
|
|
|
msg->task_msg.type = IOT_SOCKET_MSG_TYPE_DELETE_SOCK;
|
|
msg->task_msg.id = 0;
|
|
msg->arg_ptr = (void*)&delete_arg;
|
|
delete_arg.socket = socket;
|
|
|
|
iot_socket_task_put_msg_sync(msg);
|
|
exit_label:
|
|
iot_printf("%s - result = %lu.\n", __FUNCTION__, result);
|
|
return;
|
|
}
|
|
|
|
uint32_t iot_socket_udp_send(int32_t socket, uint8_t *data,
|
|
uint32_t data_len, uint32_t is_ipv6, const char *ip, uint16_t port)
|
|
{
|
|
uint32_t result = ERR_OK;
|
|
struct sockaddr_storage sock_addr_obj = {0};
|
|
struct sockaddr_in *sock_addr = (struct sockaddr_in*)&sock_addr_obj;
|
|
int32_t send_ret;
|
|
uint8_t domain;
|
|
|
|
if (p_socket_glb == NULL) {
|
|
iot_printf("%s - error. socket not start yet.\n", __FUNCTION__);
|
|
result = ERR_NOT_READY;
|
|
goto exit_label;
|
|
}
|
|
|
|
if (data == NULL || data_len == 0) {
|
|
result = ERR_INVAL;
|
|
goto exit_label;
|
|
}
|
|
|
|
domain = AF_INET;
|
|
if (is_ipv6) {
|
|
domain = AF_INET6;
|
|
}
|
|
|
|
if (iot_socket_set_addr(sock_addr, domain, ip, port)) {
|
|
result = ERR_INVAL;
|
|
goto exit_label;
|
|
}
|
|
|
|
send_ret = sendto(socket, data, data_len, 0,
|
|
(const struct sockaddr*)sock_addr, sock_addr->sin_len);
|
|
iot_printf("%s: len = %d, send ret = %d\n",
|
|
__FUNCTION__, data_len, send_ret);
|
|
|
|
exit_label:
|
|
return result;
|
|
}
|
|
|
|
#else /* IOT_LWIP_SUPPORT */
|
|
|
|
uint32_t iot_socket_task_init()
|
|
{
|
|
return ERR_NOSUPP;
|
|
}
|
|
|
|
void iot_socket_task_deinit()
|
|
{
|
|
}
|
|
|
|
uint32_t iot_socket_create(uint8_t socket_type, uint8_t is_ipv6,
|
|
const char *ip, uint16_t port, iot_socket_cb_func_t recv_cb,
|
|
uint8_t pkt_headroom, int32_t *ret_sock)
|
|
{
|
|
(void)socket_type;
|
|
(void)is_ipv6;
|
|
(void)ip;
|
|
(void)port;
|
|
(void)recv_cb;
|
|
(void)pkt_headroom;
|
|
(void)ret_sock;
|
|
return ERR_NOSUPP;
|
|
}
|
|
|
|
void iot_socket_delete(int32_t socket)
|
|
{
|
|
(void)socket;
|
|
}
|
|
|
|
uint32_t iot_socket_udp_send(int32_t socket, uint8_t *data, uint32_t len,
|
|
uint32_t is_ipv6, const char *ip, uint16_t port)
|
|
{
|
|
(void)socket;
|
|
(void)data;
|
|
(void)len;
|
|
(void)is_ipv6;
|
|
(void)ip;
|
|
(void)port;
|
|
return ERR_NOSUPP;
|
|
}
|
|
|
|
#endif /* IOT_LWIP_SUPPORT */
|