
sample: examples\aliyun_iotkit_csdk_mqtt project: board\TencentOS_tiny_EVB_MX_Plus\KEIL\aliyun_iotkit_csdk_mqtt
655 lines
15 KiB
C
655 lines
15 KiB
C
/*
|
|
* Copyright (C) 2015-2019 Alibaba Group Holding Limited
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "infra_types.h"
|
|
#include "mqtt_api.h"
|
|
#include "at_wrapper.h"
|
|
|
|
#include "at_conn_mbox.h"
|
|
#include "at_conn_mgmt.h"
|
|
|
|
#define AT_DEFAULT_INPUTMBOX_SIZE 3
|
|
#define AT_DEFAULT_PAYLOAD_SIZE (CONFIG_MQTT_MESSAGE_MAXLEN + CONFIG_MQTT_TOPIC_MAXLEN + 20)
|
|
|
|
#define AT_DEFAULT_SEND_TIMEOUT_MS 1000
|
|
#define AT_DEFAULT_RECV_TIMEOUT_MS 1000
|
|
|
|
#define DNS_MAX_NAME_LENGTH 256
|
|
#define AT_IP4_ANY_ADDR "0.0.0.0"
|
|
#define IPV4_STR_MAX_LEN 16
|
|
|
|
#define AT_MAX_PAYLOAD_SIZE 1512
|
|
|
|
#define UNUSED_ATCONN -1
|
|
|
|
#ifdef AT_DEBUG_MODE
|
|
#define AT_DEBUG(...) do{HAL_Printf(__VA_ARGS__);HAL_Printf("\r\n");}while(0)
|
|
#define AT_ERROR(...) do{HAL_Printf(__VA_ARGS__);HAL_Printf("\r\n");}while(0)
|
|
#else
|
|
#define AT_DEBUG(...)
|
|
#define AT_ERROR(...)
|
|
#endif
|
|
|
|
enum netconn_state {
|
|
NETCONN_NONE = 0,
|
|
NETCONN_WRITE,
|
|
NETCONN_LISTEN,
|
|
NETCONN_CONNECT,
|
|
NETCONN_CLOSE
|
|
};
|
|
|
|
/** Contains all internal pointers and states used for a socket */
|
|
struct at_conn {
|
|
/** connnection ID */
|
|
int connid;
|
|
/** type of the netconn (TCP) */
|
|
enum netconn_type type;
|
|
/** current state of the netconn */
|
|
enum netconn_state state;
|
|
/** remote port number */
|
|
uint16_t remote_port;
|
|
/** remote ip address */
|
|
char remote_ip[IPV4_STR_MAX_LEN];
|
|
/** data that was left from the previous read */
|
|
void *lastdata;
|
|
/** offset in the data that was left from the previous read */
|
|
uint16_t lastoffset;
|
|
/** mbox where received packets are stored until they are fetched
|
|
by the neconn application thread. */
|
|
at_mbox_t recvmbox;
|
|
/** pointer buffer for mbox which is used by ringbuf module. */
|
|
void *recvbuf[AT_DEFAULT_INPUTMBOX_SIZE];
|
|
/** timeout to wait for sending data (which means enqueueing data for sending
|
|
in internal buffers) in milliseconds */
|
|
int send_timeout_ms;
|
|
/** timeout in milliseconds to wait for new data to be received
|
|
(or connections to arrive for listening netconns) */
|
|
int recv_timeout_ms;
|
|
};
|
|
|
|
typedef struct at_netbuf {
|
|
void *payload;
|
|
uint16_t len;
|
|
uint16_t remote_port;
|
|
char remote_ip[IPV4_STR_MAX_LEN];
|
|
} at_netbuf_t;
|
|
|
|
/** The global array of available at */
|
|
static struct at_conn atconnects[AT_CONN_NUM];
|
|
static void *g_atconnmutex = NULL;
|
|
|
|
#ifndef PLATFORM_HAS_DYNMEM
|
|
static at_netbuf_t atnetbuf[AT_DEFAULT_INPUTMBOX_SIZE] =
|
|
{{NULL, 0, 0, {'\0'}}};
|
|
|
|
typedef struct at_payload {
|
|
uint8_t buf[AT_DEFAULT_PAYLOAD_SIZE];
|
|
uint8_t used;
|
|
} at_payload_t;
|
|
|
|
static at_payload_t atpayload[AT_DEFAULT_INPUTMBOX_SIZE] =
|
|
{{{0}, 0}};
|
|
#endif
|
|
|
|
static void *alloc_payload(int size)
|
|
{
|
|
#ifdef PLATFORM_HAS_DYNMEM
|
|
return HAL_Malloc(size);
|
|
#else
|
|
int i;
|
|
|
|
if (size <= 0 || size > AT_DEFAULT_PAYLOAD_SIZE) {
|
|
return NULL;
|
|
}
|
|
|
|
for (i = 0; i < AT_DEFAULT_INPUTMBOX_SIZE; i++) {
|
|
if (0 == atpayload[i].used) {
|
|
atpayload[i].used = 1;
|
|
return atpayload[i].buf;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
#endif
|
|
}
|
|
|
|
static void free_payload(void *payload)
|
|
{
|
|
if (payload) {
|
|
#ifdef PLATFORM_HAS_DYNMEM
|
|
HAL_Free(payload);
|
|
#else
|
|
memset(payload, 0, sizeof(at_payload_t));
|
|
#endif
|
|
}
|
|
}
|
|
|
|
static at_netbuf_t *alloc_atnetbuf(void)
|
|
{
|
|
#ifdef PLATFORM_HAS_DYNMEM
|
|
return HAL_Malloc(sizeof(at_netbuf_t));
|
|
#else
|
|
int i;
|
|
|
|
for (i = 0; i < AT_DEFAULT_INPUTMBOX_SIZE; i++) {
|
|
if (NULL == atnetbuf[i].payload) {
|
|
return &atnetbuf[i];
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
#endif
|
|
}
|
|
|
|
static void free_atnetbuf(at_netbuf_t *netbuf)
|
|
{
|
|
if (netbuf) {
|
|
#ifdef PLATFORM_HAS_DYNMEM
|
|
HAL_Free(netbuf);
|
|
#else
|
|
memset(netbuf, 0, sizeof(at_netbuf_t));
|
|
#endif
|
|
}
|
|
}
|
|
|
|
static struct at_conn *get_conn(int c)
|
|
{
|
|
struct at_conn *conn = NULL;
|
|
|
|
if ((c < 0) || (c >= AT_CONN_NUM)) {
|
|
AT_DEBUG("get_conn(%d): invalid", c);
|
|
return NULL;
|
|
}
|
|
|
|
conn = &atconnects[c];
|
|
|
|
if (UNUSED_ATCONN == conn->connid) {
|
|
AT_DEBUG("get_conn(%d): not active", c);
|
|
return NULL;
|
|
}
|
|
|
|
return conn;
|
|
}
|
|
|
|
static int at_newconn(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < AT_CONN_NUM; i++) {
|
|
if (atconnects[i].connid == UNUSED_ATCONN) {
|
|
if (at_mbox_new(&atconnects[i].recvmbox,
|
|
AT_DEFAULT_INPUTMBOX_SIZE,
|
|
atconnects[i].recvbuf) != 0) {
|
|
AT_ERROR("fai to new input mail box size %d \n", AT_DEFAULT_INPUTMBOX_SIZE);
|
|
return -1;
|
|
}
|
|
|
|
atconnects[i].type = NETCONN_INVALID;
|
|
atconnects[i].state = NETCONN_NONE;
|
|
atconnects[i].lastdata = NULL;
|
|
atconnects[i].lastoffset = 0;
|
|
atconnects[i].connid = i;
|
|
atconnects[i].send_timeout_ms = AT_DEFAULT_SEND_TIMEOUT_MS;
|
|
atconnects[i].recv_timeout_ms = AT_DEFAULT_RECV_TIMEOUT_MS;
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static void at_drainconn(struct at_conn *conn)
|
|
{
|
|
at_netbuf_t *mem;
|
|
|
|
if (NULL == conn)
|
|
return;
|
|
|
|
if (at_mbox_valid(&conn->recvmbox)) {
|
|
while (at_mbox_tryfetch(&conn->recvmbox, (void **)(&mem)) != AT_MBOX_EMPTY) {
|
|
if (mem != NULL) {
|
|
if (mem->payload) {
|
|
free_payload(mem->payload);
|
|
mem->payload = NULL;
|
|
}
|
|
free_atnetbuf(mem);
|
|
}
|
|
}
|
|
at_mbox_free(&conn->recvmbox);
|
|
at_mbox_set_invalid(&conn->recvmbox);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static int at_freeconn(struct at_conn *conn)
|
|
{
|
|
at_netbuf_t *buf = NULL;
|
|
|
|
if (NULL == conn)
|
|
return -1;
|
|
|
|
if (NULL != conn->lastdata) {
|
|
buf = (at_netbuf_t *) conn->lastdata;
|
|
|
|
if (buf->payload) {
|
|
free_payload(buf->payload);
|
|
buf->payload = NULL;
|
|
}
|
|
|
|
free_atnetbuf(buf);
|
|
}
|
|
|
|
conn->lastdata = NULL;
|
|
conn->lastoffset = 0;
|
|
|
|
at_drainconn(conn);
|
|
|
|
conn->type = NETCONN_INVALID;
|
|
conn->state = NETCONN_NONE;
|
|
conn->connid = UNUSED_ATCONN;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int at_conn_fetch(struct at_conn *conn, at_netbuf_t **new_buf)
|
|
{
|
|
uint32_t ret = 0;
|
|
void *buf = NULL;
|
|
|
|
if (NULL == conn || NULL == new_buf) {
|
|
return -1;
|
|
}
|
|
|
|
if (!at_mbox_valid(&conn->recvmbox)) {
|
|
AT_ERROR("conn %d invalid recvmbox\n", conn->connid);
|
|
return -1;
|
|
}
|
|
|
|
ret = at_mbox_fetch(&conn->recvmbox, &buf, conn->recv_timeout_ms);
|
|
if (ret == AT_MBOX_TIMEOUT) {
|
|
AT_ERROR("at conn %d fetch data time out %d\n", conn->connid, conn->recv_timeout_ms);
|
|
return -1;
|
|
}
|
|
|
|
*new_buf = buf;
|
|
return 0;
|
|
}
|
|
|
|
/****************************public interface*********************/
|
|
int at_conn_input(struct at_conn_input *param)
|
|
{
|
|
int s = -1;
|
|
void *data = NULL;
|
|
int len = 0;
|
|
char *remote_ip = NULL;
|
|
uint16_t remote_port = 0;
|
|
struct at_conn *conn = NULL;
|
|
at_netbuf_t *buf = NULL;
|
|
|
|
if (NULL == param) {
|
|
AT_ERROR("at conn input param NULL\n");
|
|
return -1;
|
|
}
|
|
|
|
s = param->fd;
|
|
data = param->data;
|
|
len = param->datalen;
|
|
remote_ip = param->remote_ip;
|
|
remote_port = param->remote_port;
|
|
|
|
if (NULL == data || 0 == len) {
|
|
AT_ERROR("low level invalid input data\n");
|
|
return -1;
|
|
}
|
|
|
|
if (remote_ip != NULL &&
|
|
strlen(remote_ip) > IPV4_STR_MAX_LEN) {
|
|
AT_ERROR("invalid ip string");
|
|
return -1;
|
|
}
|
|
|
|
conn = get_conn(s);
|
|
if (NULL == conn) {
|
|
AT_ERROR("conn %d doesn't exist\n", s);
|
|
return -1;
|
|
}
|
|
|
|
if (conn->connid < 0) {
|
|
AT_ERROR("conn %d invalid connid\n", s);
|
|
return -1;
|
|
}
|
|
|
|
if (!at_mbox_valid(&conn->recvmbox)) {
|
|
AT_ERROR("invalid conn to input packet\n");
|
|
return -1;
|
|
}
|
|
|
|
buf = alloc_atnetbuf();
|
|
if (NULL == buf) {
|
|
AT_ERROR("alloc at net buf size %d fail\n", sizeof(at_netbuf_t));
|
|
return -1;
|
|
}
|
|
memset(buf, 0, sizeof(*buf));
|
|
|
|
buf->payload = alloc_payload(len);
|
|
if (NULL == buf->payload) {
|
|
free_atnetbuf(buf);
|
|
AT_ERROR("alloc payload size %d fail\n", len);
|
|
return -1;
|
|
}
|
|
|
|
memcpy(buf->payload, data, len);
|
|
buf->len = len;
|
|
buf->remote_port = remote_port;
|
|
if (remote_ip)
|
|
memcpy(buf->remote_ip, remote_ip, IPV4_STR_MAX_LEN);
|
|
|
|
if (at_mbox_trypost(&conn->recvmbox, buf) != 0) {
|
|
free_payload(buf->payload);
|
|
buf->payload = NULL;
|
|
free_atnetbuf(buf);
|
|
AT_ERROR("try post recv packet fail\n");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int at_conn_init(void)
|
|
{
|
|
static int at_conn_init_done = 0;
|
|
int i;
|
|
|
|
if (at_conn_init_done) {
|
|
AT_ERROR("at conn have already init done\n");
|
|
return 0;
|
|
}
|
|
|
|
for (i = 0; i < AT_CONN_NUM; i++) {
|
|
atconnects[i].connid = UNUSED_ATCONN;
|
|
}
|
|
|
|
g_atconnmutex = HAL_MutexCreate();
|
|
if (g_atconnmutex == NULL) {
|
|
AT_ERROR("failed to creat g_atconnmutex \n");
|
|
return -1;
|
|
}
|
|
|
|
if (HAL_AT_CONN_Init() != 0) {
|
|
AT_ERROR("at conn low level init fail\n");
|
|
HAL_MutexDestroy(g_atconnmutex);
|
|
return -1;
|
|
}
|
|
|
|
at_conn_init_done = 1;
|
|
|
|
return 0 ;
|
|
}
|
|
|
|
int at_conn_getaddrinfo(const char *nodename, char resultip[16])
|
|
{
|
|
int namelen;
|
|
|
|
if (NULL == nodename || NULL == resultip) {
|
|
return -1;
|
|
}
|
|
|
|
namelen = strlen(nodename);
|
|
if (namelen > DNS_MAX_NAME_LENGTH)
|
|
return -1;
|
|
|
|
if (HAL_AT_CONN_DomainToIp((char *)nodename, resultip) != 0) {
|
|
AT_ERROR("domain to ip failed.");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int at_conn_setup(netconn_type_t type)
|
|
{
|
|
struct at_conn *conn = NULL;
|
|
int connid = -1;
|
|
|
|
if (type >= NETCONN_TYPE_NUM || type <= NETCONN_INVALID) {
|
|
return -1;
|
|
}
|
|
|
|
HAL_MutexLock(g_atconnmutex);
|
|
if ((connid = at_newconn()) == -1) {
|
|
AT_ERROR("fai to new at conn\n");
|
|
HAL_MutexUnlock(g_atconnmutex);
|
|
return -1;
|
|
}
|
|
|
|
if ((conn = get_conn(connid)) == NULL) {
|
|
AT_ERROR("fai to get at conn\n");
|
|
HAL_MutexUnlock(g_atconnmutex);
|
|
return -1;
|
|
}
|
|
|
|
conn->type = type;
|
|
conn->state = NETCONN_NONE;
|
|
HAL_MutexUnlock(g_atconnmutex);
|
|
|
|
return connid;
|
|
}
|
|
|
|
int at_conn_start(int connid, char* remoteipaddr, uint16_t remoteport)
|
|
{
|
|
char *ipv4anyadrr = AT_IP4_ANY_ADDR;
|
|
at_conn_t statconn = {0};
|
|
struct at_conn *conn = NULL;
|
|
|
|
HAL_MutexLock(g_atconnmutex);
|
|
conn = get_conn(connid);
|
|
if (NULL == conn) {
|
|
AT_ERROR("at_startconn: invalid conn\n");
|
|
HAL_MutexUnlock(g_atconnmutex);
|
|
return -1;
|
|
}
|
|
|
|
if (conn->state != NETCONN_NONE) {
|
|
AT_ERROR("at_startconn: conn %d state is %d \n", connid, conn->state);
|
|
HAL_MutexUnlock(g_atconnmutex);
|
|
return -1;
|
|
}
|
|
|
|
statconn.fd = connid;
|
|
statconn.r_port = remoteport;
|
|
statconn.l_port = -1;
|
|
statconn.addr = (char *)remoteipaddr;
|
|
if (NULL == statconn.addr) {
|
|
statconn.addr = ipv4anyadrr;
|
|
}
|
|
|
|
switch (conn->type) {
|
|
case NETCONN_TCP:
|
|
statconn.type = TCP_CLIENT;
|
|
if (HAL_AT_CONN_Start(&statconn) != 0) {
|
|
AT_ERROR("fail to setup tcp connect, remote is %s port is %d.\n", statconn.addr, remoteport);
|
|
HAL_MutexUnlock(g_atconnmutex);
|
|
return -1;
|
|
}
|
|
memcpy(conn->remote_ip, statconn.addr, IPV4_STR_MAX_LEN);
|
|
conn->remote_port = remoteport;
|
|
break;
|
|
default:
|
|
AT_ERROR("Unsupported at connection type.\n");
|
|
HAL_MutexUnlock(g_atconnmutex);
|
|
return -1;
|
|
}
|
|
|
|
/* Update at conn state */
|
|
conn->state = NETCONN_CONNECT;
|
|
HAL_MutexUnlock(g_atconnmutex);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int at_conn_close(int c)
|
|
{
|
|
struct at_conn *conn = NULL;
|
|
int err;
|
|
|
|
AT_DEBUG("at_close(%d)\r\n", c);
|
|
|
|
conn = get_conn(c);
|
|
if (NULL == conn) {
|
|
return -1;
|
|
}
|
|
|
|
if (conn->state == NETCONN_CONNECT) {
|
|
if (HAL_AT_CONN_Close(c, -1) != 0) {
|
|
AT_DEBUG("HAL_AT_close failed.");
|
|
}
|
|
}
|
|
|
|
HAL_MutexLock(g_atconnmutex);
|
|
err = at_freeconn(conn);
|
|
HAL_MutexUnlock(g_atconnmutex);
|
|
if (err != 0) {
|
|
AT_ERROR("at_freeconn failed in %s.", __func__);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int at_conn_recvbufempty(int c)
|
|
{
|
|
struct at_conn *conn = NULL;
|
|
|
|
conn = get_conn(c);
|
|
if (NULL == conn) {
|
|
AT_ERROR("at_recvbufempty cannot get socket %d\n", c);
|
|
return -1;
|
|
}
|
|
|
|
/* remain data */
|
|
if (conn->lastdata)
|
|
return 0;
|
|
|
|
if (!at_mbox_valid(&conn->recvmbox)) {
|
|
AT_ERROR("conn %d invalid recvmbox\n", c);
|
|
return -1;
|
|
}
|
|
|
|
return at_mbox_empty(&conn->recvmbox);
|
|
}
|
|
|
|
int at_conn_send(int c, const void *data, uint32_t size)
|
|
{
|
|
struct at_conn *conn = NULL;
|
|
|
|
if (NULL == data || size == 0 || size > AT_MAX_PAYLOAD_SIZE) {
|
|
AT_ERROR("at_conn_send fail to send, size %d\n", size);
|
|
return -1;
|
|
}
|
|
|
|
conn = get_conn(c);
|
|
if (NULL == conn) {
|
|
AT_ERROR("at_conn_send fail to get conn %d\n", c);
|
|
return -1;
|
|
}
|
|
|
|
if (conn->type == NETCONN_TCP) {
|
|
if (conn->state == NETCONN_NONE) {
|
|
AT_ERROR("at_conn_send connect %d state %d\n", c, conn->state);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (HAL_AT_CONN_Send(c, (uint8_t *)data, size, NULL, -1, conn->send_timeout_ms)) {
|
|
AT_ERROR("c %d fail to send do nothing for now\n", c);
|
|
return -1;
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
int at_conn_recv(int c, void *mem, uint32_t len)
|
|
{
|
|
struct at_conn *conn = NULL;
|
|
at_netbuf_t *buf = NULL;
|
|
int off = 0;
|
|
uint16_t buflen = 0;
|
|
uint16_t copylen = 0;
|
|
int err = 0;
|
|
uint8_t done = 0;
|
|
|
|
if (NULL == mem || 0 == len) {
|
|
return -1;
|
|
}
|
|
|
|
conn = get_conn(c);
|
|
if (NULL == conn) {
|
|
AT_ERROR("at_conn_recv fail to get conn %d\n", c);
|
|
return -1;
|
|
}
|
|
|
|
do {
|
|
if (conn->lastdata) {
|
|
buf = conn->lastdata;
|
|
} else {
|
|
err = at_conn_fetch(conn, &buf);
|
|
if (err != 0 || buf == NULL || buf->payload == NULL) {
|
|
if (off > 0) {
|
|
return off;
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
conn->lastdata = buf;
|
|
}
|
|
|
|
buflen = buf->len;
|
|
AT_DEBUG("at_conn_recv: buflen=%u, len=%u, off=%d, lastoffset=%u\n",
|
|
buflen, len, off, conn->lastoffset);
|
|
|
|
buflen -= conn->lastoffset;
|
|
if (len > buflen) {
|
|
copylen = buflen;
|
|
} else {
|
|
copylen = len;
|
|
}
|
|
|
|
memcpy(&((uint8_t *)mem)[off], &((uint8_t *)buf->payload)[conn->lastoffset], copylen);
|
|
off += copylen;
|
|
|
|
if (NETCONN_TCP == conn->type) {
|
|
if (len < copylen) {
|
|
AT_ERROR("invalid copylen %d, len = %d\n", copylen, len);
|
|
return -1;
|
|
}
|
|
|
|
len -= copylen;
|
|
if (len <= 0) {
|
|
done = 1;
|
|
}
|
|
} else {
|
|
done = 1;
|
|
}
|
|
|
|
if ((NETCONN_TCP == conn->type) && (buflen > copylen)) {
|
|
conn->lastdata = buf;
|
|
conn->lastoffset += copylen;
|
|
} else {
|
|
conn->lastdata = NULL;
|
|
conn->lastoffset = 0;
|
|
free_payload(buf->payload);
|
|
buf->payload = NULL;
|
|
free_atnetbuf(buf);
|
|
buf = NULL;
|
|
}
|
|
} while (!done);
|
|
|
|
return off;
|
|
}
|