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 */
 |