mirror of
https://github.com/espruino/Espruino.git
synced 2025-12-08 19:06:15 +00:00
1222 lines
39 KiB
C
1222 lines
39 KiB
C
/*
|
|
5 * network_esp8266_board.c
|
|
|
|
*
|
|
* Created on: Aug 29, 2015
|
|
* Author: kolban
|
|
*/
|
|
|
|
/**
|
|
* This file contains the implementation of the ESP8266_BOARD network interfaces at the TCP/IP
|
|
* level.
|
|
*
|
|
* Design notes
|
|
* ------------
|
|
* We maintain an array of socketData structures. The number of such structures is defined in the
|
|
* MAX_SOCKETS define. The private variable that contains the array is called "socketArray".
|
|
* Each one of these array instances represents a possible socket that we can use.
|
|
*
|
|
* Within the code, this allows us to reference a socket instance by an integer. For example,
|
|
* socket 0 is the 1st instance in the array.
|
|
*
|
|
* Associated with the array are accessor functions:
|
|
*
|
|
* o getNextFreeSocket - Return the next free socket or -1 if there are no free sockets.
|
|
* o getSocketData - Get the socketData structure corresponding to the socket integer.
|
|
* o resetSocket - Reset the state of the socket and clean it up if needed.
|
|
* o releaseSocket - Release the socket and return it to the pool of unused sockets.
|
|
*
|
|
*/
|
|
// ESP8266 specific includes
|
|
#define ESPSDK_1_3_0
|
|
#include <c_types.h>
|
|
#include <user_interface.h>
|
|
#include <mem.h>
|
|
#include <osapi.h>
|
|
#include <espconn.h>
|
|
#include <espmissingincludes.h>
|
|
|
|
#define _GCC_WRAP_STDINT_H
|
|
typedef long long int64_t;
|
|
|
|
#include "network_esp8266.h"
|
|
#include "esp8266_board_utils.h"
|
|
|
|
/**
|
|
* \brief The maximum number of concurrently open sockets we support.
|
|
* We should probably pair this with the ESP8266 concept of the maximum number of sockets
|
|
* that an ESP8266 instance can also support.
|
|
*/
|
|
#define MAX_SOCKETS (10)
|
|
|
|
#define LOG os_printf
|
|
|
|
static struct socketData *getSocketData(int s);
|
|
|
|
static int getServerSocketByLocalPort(unsigned short port);
|
|
static void setSocketInError(int socketId, char *msg, int code);
|
|
static void dumpEspConn(struct espconn *pEspConn);
|
|
static int getNextFreeSocket();
|
|
static void doClose(int socketId);
|
|
static void releaseSocket(int socketId);
|
|
static void resetSocket(int sckt);
|
|
|
|
static void esp8266_callback_connectCB_inbound(void *arg);
|
|
static void esp8266_callback_connectCB_outbound(void *arg);
|
|
static void esp8266_callback_disconnectCB(void *arg);
|
|
static void esp8266_callback_sentCB(void *arg);
|
|
static void esp8266_callback_writeFinishedCB(void *arg);
|
|
static void esp8266_callback_recvCB(void *arg, char *pData, unsigned short len);
|
|
static void esp8266_callback_reconnectCB(void *arg, sint8 err);
|
|
|
|
/**
|
|
* \brief A data structure that represents a memory buffer.
|
|
* A memory buffer is an object that represents a sized piece of memory. Given a
|
|
* memory buffer object, we know how big it is and can set or get data from it.
|
|
*/
|
|
struct memoryBuffer {
|
|
/**
|
|
* \brief The size of data associated with this buffer.
|
|
*/
|
|
size_t length;
|
|
|
|
/**
|
|
* \brief A pointer to the memory associated with this buffer. This should be
|
|
* NULL if `length` is 0.
|
|
*/
|
|
uint8 *buf;
|
|
};
|
|
|
|
static uint8 *memoryBuffer_read(struct memoryBuffer *pMemoryBuffer, size_t readSize);
|
|
static uint8 *memoryBuffer_append(struct memoryBuffer *pMemoryBuffer, uint8 *pNewData, size_t length);
|
|
static void memoryBuffer_delete(struct memoryBuffer *pMemoryBuffer);
|
|
static int memoryBuffer_getSize(struct memoryBuffer *pMemoryBuffer);
|
|
|
|
/**
|
|
* \brief The potential states for a socket.
|
|
* See the socket state diagram.
|
|
*/
|
|
enum SOCKET_STATE {
|
|
SOCKET_STATE_UNUSED, //!< The socket is unused
|
|
SOCKET_STATE_IDLE, //!< The socket is idle
|
|
SOCKET_STATE_CONNECTING, //!< The socket is connecting
|
|
SOCKET_STATE_TRANSMITTING, //!< The socket is transmitting
|
|
SOCKET_STATE_CLOSING, //!< The socket is closing
|
|
SOCKET_STATE_HOST_RESOLVING, //!< Resolving a a hostname
|
|
SOCKET_STATE_ERROR //!< The socket is in error
|
|
};
|
|
|
|
/**
|
|
* void initSocketData()
|
|
* int getNextFreeSocket()
|
|
* void releaseSocket(int s)
|
|
* struct socketData *getSocketData(int s)
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
* Here are some notes on the accepted list algorithms.
|
|
*
|
|
* tail - Where old entries are removed
|
|
* head - Where new entries are added
|
|
*
|
|
* When tail = head - the list is empty
|
|
*
|
|
* When a new entry is added:
|
|
* error: mod(head+1) == tail // List full
|
|
* *head = new entry
|
|
* head = mod(head+1)
|
|
*
|
|
* When an old entry is removed
|
|
* error: tail == head // List empty
|
|
* retrieved entry = *tail
|
|
* tail = mod(tail+1)
|
|
*
|
|
*
|
|
* 0 1 2 3 4 5 6 7 8
|
|
* [ ][ ][ ][ ][ ][ ][ ][ ][ ]
|
|
*/
|
|
|
|
/**
|
|
* \brief The maximum number of accepted sockets that can be remembered.
|
|
*/
|
|
#define MAX_ACCEPTED_SOCKETS (10)
|
|
|
|
/**
|
|
* \brief The core socket structure.
|
|
* The structure is initialized by resetSocket.
|
|
*/
|
|
struct socketData {
|
|
int socketId; //!< The id of THIS socket.
|
|
enum SOCKET_STATE state; //!< What is the socket state?
|
|
bool isConnected; //!< Is this socket connected?
|
|
bool isServer; //!< Are we a server?
|
|
bool shouldClose; //!< Should this socket close when it can?
|
|
|
|
struct espconn *pEspconn; //!< The ESPConn structure.
|
|
|
|
struct memoryBuffer txMemoryBuffer; //!< Data to be transmitted.
|
|
uint8 *currentTx; //!< Data currently being transmitted.
|
|
uint8 *rxBuf; //!< Data received (inbound).
|
|
size_t rxBufLen; //!< The length of data in the buffer ready for consumption.
|
|
|
|
char *errorMsg; //!< Error message.
|
|
int errorCode; //!< Error code.
|
|
|
|
/**
|
|
* \brief A list of accepted sockets.
|
|
* This array contains the storage of a list of sockets that have been accepted by this
|
|
* server socket but have not yet been delivered to Espruino. A `head` and `tail`
|
|
* pair of indices are also associated.
|
|
*/
|
|
int acceptedSockets[MAX_ACCEPTED_SOCKETS];
|
|
|
|
/**
|
|
* \brief The head of the list of accepted sockets.
|
|
* The index into `acceptedSockets` where the next accepted socket will be placed.
|
|
*/
|
|
uint8 acceptedSocketsHead;
|
|
|
|
/**
|
|
* \brief The tail of the list of accepted sockets.
|
|
* The index into `acceptedSockets` where the next accepted socket will be retrieved.
|
|
*/
|
|
uint8 acceptedSocketsTail;
|
|
|
|
};
|
|
|
|
|
|
/**
|
|
* \brief An array of socket data structures.
|
|
*/
|
|
static struct socketData socketArray[MAX_SOCKETS];
|
|
|
|
|
|
/**
|
|
* \brief An array of `esp_tcp` data structures.
|
|
*/
|
|
static esp_tcp tcpArray[MAX_SOCKETS];
|
|
|
|
|
|
/**
|
|
* \brief An array of `struct espconn` data structures.
|
|
*/
|
|
static struct espconn espconnArray[MAX_SOCKETS];
|
|
|
|
|
|
/**
|
|
* \brief Write the details of a socket to the debug log.
|
|
* The data associated with the socket is dumped to the debug log.
|
|
*/
|
|
void esp8266_dumpSocket(
|
|
int socketId //!< The ID of the socket data structure to be logged.
|
|
) {
|
|
struct socketData *pSocketData = getSocketData(socketId);
|
|
LOG("Dump of socket=%d\n", socketId);
|
|
LOG(" - isConnected=%d", pSocketData->isConnected);
|
|
LOG(", isServer=%d", pSocketData->isServer);
|
|
LOG(", acceptedSockets=[");
|
|
int s=pSocketData->acceptedSocketsTail;
|
|
while(s != pSocketData->acceptedSocketsHead) {
|
|
LOG(" %d", pSocketData->acceptedSockets[s]);
|
|
s = (s+1)%MAX_ACCEPTED_SOCKETS;
|
|
}
|
|
LOG("]");
|
|
LOG(", rxBufLen=%d", pSocketData->rxBufLen);
|
|
LOG(", tx length=%d", memoryBuffer_getSize(&(pSocketData->txMemoryBuffer)));
|
|
LOG(", currentTx=0x%d", (int)pSocketData->currentTx);
|
|
char *stateMsg;
|
|
switch(pSocketData->state) {
|
|
case SOCKET_STATE_CLOSING:
|
|
stateMsg = "SOCKET_STATE_CLOSING";
|
|
break;
|
|
case SOCKET_STATE_CONNECTING:
|
|
stateMsg = "SOCKET_STATE_CONNECTING";
|
|
break;
|
|
case SOCKET_STATE_ERROR:
|
|
stateMsg = "SOCKET_STATE_ERROR";
|
|
break;
|
|
case SOCKET_STATE_IDLE:
|
|
stateMsg = "SOCKET_STATE_IDLE";
|
|
break;
|
|
case SOCKET_STATE_TRANSMITTING:
|
|
stateMsg = "SOCKET_STATE_TRANSMITTING";
|
|
break;
|
|
case SOCKET_STATE_HOST_RESOLVING:
|
|
stateMsg = "SOCKET_STATE_HOST_RESOLVING";
|
|
break;
|
|
case SOCKET_STATE_UNUSED:
|
|
stateMsg = "SOCKET_STATE_UNUSED";
|
|
break;
|
|
default:
|
|
stateMsg = "Unexpected state!!";
|
|
break;
|
|
}
|
|
LOG(", state=%s", stateMsg);
|
|
LOG(", errorCode=%d", pSocketData->errorCode);
|
|
|
|
// Print the errorMsg if it has anything to say
|
|
if (pSocketData->errorMsg != NULL && strlen(pSocketData->errorMsg) > 0) {
|
|
LOG(", errorMsg=\"%s\"", pSocketData->errorMsg);
|
|
}
|
|
|
|
LOG("\n");
|
|
} // End of dumpSocket
|
|
|
|
|
|
/**
|
|
* \brief Dump a struct espconn (for debugging purposes).
|
|
*/
|
|
static void dumpEspConn(
|
|
struct espconn *pEspConn //!<
|
|
) {
|
|
char ipString[20];
|
|
LOG("Dump of espconn: 0x%x\n", (int)pEspConn);
|
|
if (pEspConn == NULL) {
|
|
return;
|
|
}
|
|
switch(pEspConn->type) {
|
|
case ESPCONN_TCP:
|
|
LOG(" - type = TCP\n");
|
|
LOG(" - local address = %d.%d.%d.%d [%d]\n",
|
|
pEspConn->proto.tcp->local_ip[0],
|
|
pEspConn->proto.tcp->local_ip[1],
|
|
pEspConn->proto.tcp->local_ip[2],
|
|
pEspConn->proto.tcp->local_ip[3],
|
|
pEspConn->proto.tcp->local_port);
|
|
LOG(" - remote address = %d.%d.%d.%d [%d]\n",
|
|
pEspConn->proto.tcp->remote_ip[0],
|
|
pEspConn->proto.tcp->remote_ip[1],
|
|
pEspConn->proto.tcp->remote_ip[2],
|
|
pEspConn->proto.tcp->remote_ip[3],
|
|
pEspConn->proto.tcp->remote_port);
|
|
break;
|
|
case ESPCONN_UDP:
|
|
LOG(" - type = UDP\n");
|
|
LOG(" - local_port = %d\n", pEspConn->proto.udp->local_port);
|
|
LOG(" - local_ip = %d.%d.%d.%d\n",
|
|
pEspConn->proto.tcp->local_ip[0],
|
|
pEspConn->proto.tcp->local_ip[1],
|
|
pEspConn->proto.tcp->local_ip[2],
|
|
pEspConn->proto.tcp->local_ip[3]);
|
|
LOG(" - remote_port = %d\n", pEspConn->proto.udp->remote_port);
|
|
LOG(" - remote_ip = %d.%d.%d.%d\n",
|
|
pEspConn->proto.tcp->remote_ip[0],
|
|
pEspConn->proto.tcp->remote_ip[1],
|
|
pEspConn->proto.tcp->remote_ip[2],
|
|
pEspConn->proto.tcp->remote_ip[3]);
|
|
break;
|
|
default:
|
|
LOG(" - type = Unknown!! 0x%x\n", pEspConn->type);
|
|
}
|
|
switch(pEspConn->state) {
|
|
case ESPCONN_NONE:
|
|
LOG(" - state=NONE");
|
|
break;
|
|
case ESPCONN_WAIT:
|
|
LOG(" - state=WAIT");
|
|
break;
|
|
case ESPCONN_LISTEN:
|
|
LOG(" - state=LISTEN");
|
|
break;
|
|
case ESPCONN_CONNECT:
|
|
LOG(" - state=CONNECT");
|
|
break;
|
|
case ESPCONN_WRITE:
|
|
LOG(" - state=WRITE");
|
|
break;
|
|
case ESPCONN_READ:
|
|
LOG(" - state=READ");
|
|
break;
|
|
case ESPCONN_CLOSE:
|
|
LOG(" - state=CLOSE");
|
|
break;
|
|
default:
|
|
LOG(" - state=unknown!!");
|
|
break;
|
|
}
|
|
LOG(", link_cnt=%d", pEspConn->link_cnt);
|
|
LOG(", reverse=0x%x\n", (unsigned int)pEspConn->reverse);
|
|
} // End of dumpEspConn
|
|
|
|
|
|
/**
|
|
* \brief Get the next free socket.
|
|
* Look for the first free socket in the array of sockets and return the first one
|
|
* that is available after first flagging it as now in use. If no available
|
|
* socket can be found, return -1.
|
|
*/
|
|
static int getNextFreeSocket() {
|
|
int i;
|
|
for (i=0; i<MAX_SOCKETS; i++) {
|
|
if (socketArray[i].state == SOCKET_STATE_UNUSED) {
|
|
return i;
|
|
}
|
|
} // End of for each socket.
|
|
return(-1);
|
|
} // End of getNextFreeSocket
|
|
|
|
|
|
/**
|
|
* \brief Retrieve the socketData for the given socket index.
|
|
*/
|
|
static struct socketData *getSocketData(int s) {
|
|
assert(s >=0 && s<MAX_SOCKETS);
|
|
return &socketArray[s];
|
|
} // End of getSocketData
|
|
|
|
|
|
/**
|
|
* \brief Find the server socket that is bound to the given local port.
|
|
* \return The socket id of the socket listening on the given port or -1 if there is no
|
|
* server socket that matches.
|
|
*/
|
|
static int getServerSocketByLocalPort(
|
|
unsigned short port //!< The port number on which a server socket is listening.
|
|
) {
|
|
// Loop through each of the sockets in the socket array looking for a socket
|
|
// that is inuse, a server and has a local_port of the passed in port number.
|
|
int socketArrayIndex;
|
|
for (socketArrayIndex=0; socketArrayIndex<MAX_SOCKETS; socketArrayIndex++) {
|
|
struct socketData *pSocketData = socketArray;
|
|
if (pSocketData->state != SOCKET_STATE_UNUSED &&
|
|
pSocketData->isServer == true &&
|
|
pSocketData->pEspconn->proto.tcp->local_port == port)
|
|
{
|
|
return socketArrayIndex;
|
|
}
|
|
pSocketData++;
|
|
} // End of for each socket
|
|
return -1;
|
|
} // End of getServerSocketByLocalPort
|
|
|
|
|
|
/**
|
|
* \brief Reset the socket to its clean and unused state.
|
|
*/
|
|
static void resetSocket(
|
|
int sckt //!<
|
|
) {
|
|
struct socketData *pSocketData = getSocketData(sckt);
|
|
|
|
memoryBuffer_delete(&pSocketData->txMemoryBuffer);
|
|
|
|
pSocketData->pEspconn = &espconnArray[sckt];
|
|
pSocketData->state = SOCKET_STATE_UNUSED;
|
|
pSocketData->rxBuf = NULL;
|
|
pSocketData->rxBufLen = 0;
|
|
pSocketData->isServer = false;
|
|
pSocketData->isConnected = false;
|
|
pSocketData->shouldClose = false;
|
|
pSocketData->socketId = sckt;
|
|
pSocketData->errorMsg = "";
|
|
pSocketData->errorCode = 0;
|
|
|
|
pSocketData->acceptedSocketsHead = 0; // Set the head to 0
|
|
pSocketData->acceptedSocketsTail = 0; // Set the tail to 9.
|
|
|
|
struct espconn *pEspconn = pSocketData->pEspconn;
|
|
pEspconn->type = ESPCONN_TCP;
|
|
pEspconn->state = ESPCONN_NONE;
|
|
pEspconn->proto.tcp = &tcpArray[sckt];
|
|
pEspconn->reverse = NULL;
|
|
|
|
|
|
espconn_regist_connectcb(pEspconn, esp8266_callback_connectCB_outbound);
|
|
espconn_regist_disconcb(pEspconn, esp8266_callback_disconnectCB);
|
|
espconn_regist_reconcb(pEspconn, esp8266_callback_reconnectCB);
|
|
espconn_regist_sentcb(pEspconn, esp8266_callback_sentCB);
|
|
espconn_regist_recvcb(pEspconn, esp8266_callback_recvCB);
|
|
espconn_regist_write_finish(pEspconn, esp8266_callback_writeFinishedCB);
|
|
} // End of resetSocket
|
|
|
|
|
|
/**
|
|
* \brief Release the socket and return it to the free pool.
|
|
*/
|
|
static void releaseSocket(
|
|
int socketId //!< The socket id of the socket to be released.
|
|
) {
|
|
os_printf("> releaseSocket: %d\n", socketId);
|
|
assert(socketId >=0 && socketId<MAX_SOCKETS);
|
|
esp8266_dumpSocket(socketId);
|
|
|
|
struct socketData *pSocketData = getSocketData(socketId);
|
|
assert(pSocketData->state != SOCKET_STATE_UNUSED);
|
|
|
|
if (memoryBuffer_getSize(&pSocketData->txMemoryBuffer) > 0) {
|
|
os_printf(" - Oh oh ... attempt to close socket while the TX memoryBuffer is not empty!\n");
|
|
}
|
|
if (pSocketData->rxBuf != NULL || pSocketData->rxBufLen != 0) {
|
|
os_printf(" - Oh oh ... attempt to close socket while the rxBuffer is not empty!\n");
|
|
}
|
|
resetSocket(socketId);
|
|
os_printf("< releaseSocket\n");
|
|
} // End of releaseSocket
|
|
|
|
|
|
/**
|
|
* \brief Initialize the ESP8266_BOARD environment.
|
|
* Walk through each of the sockets and initialize each one.
|
|
*/
|
|
void netInit_esp8266_board() {
|
|
int socketArrayIndex;
|
|
for (socketArrayIndex=0; socketArrayIndex<MAX_SOCKETS; socketArrayIndex++) {
|
|
resetSocket(socketArrayIndex);
|
|
} // End of for each socket
|
|
} // netInit_esp8266_board
|
|
|
|
|
|
/**
|
|
* \brief Perform an actual closure of the socket by calling the ESP8266 disconnect API.
|
|
* This is broken out into its own function because this can happen in
|
|
* a number of possible places.
|
|
*/
|
|
static void doClose(
|
|
int socketId //!< The socket id to be closed.
|
|
) {
|
|
struct socketData *pSocketData = getSocketData(socketId);
|
|
|
|
if (pSocketData->state != SOCKET_STATE_CLOSING) {
|
|
int rc = espconn_disconnect(pSocketData->pEspconn);
|
|
pSocketData->state = SOCKET_STATE_CLOSING;
|
|
|
|
if (rc != 0) {
|
|
os_printf("espconn_disconnect: rc=%d\n", rc);
|
|
setSocketInError(socketId, "espconn_disconnect", rc);
|
|
}
|
|
} else {
|
|
releaseSocket(socketId);
|
|
}
|
|
} // End of doClose
|
|
|
|
|
|
/**
|
|
* \brief Set the given socket as being in error supplying a message and a code.
|
|
* The socket state is placed in `SOCKET_STATE_ERROR`.
|
|
*/
|
|
static void setSocketInError(
|
|
int socketId, //!< The socket id that is being flagged as in error.
|
|
char *msg, //!< A message to associate with the error.
|
|
int code //!< A low level error code.
|
|
) {
|
|
struct socketData *pSocketData = getSocketData(socketId);
|
|
pSocketData->state = SOCKET_STATE_ERROR;
|
|
pSocketData->errorMsg = msg;
|
|
pSocketData->errorCode = code;
|
|
} // End of setSocketInError
|
|
|
|
/**
|
|
* \brief Callback function registered to the ESP8266 environment that is
|
|
* invoked when a new inbound connection has been formed.
|
|
* A new connection
|
|
* can occur when the ESP8266 makes a call out to a partner (in that
|
|
* case the ESP8266 is acting as a client) or a new connection can
|
|
* occur when a partner calls into a listening ESP8266. In that case
|
|
* the ESP8266 is acting as a server.
|
|
*/
|
|
static void esp8266_callback_connectCB_inbound(
|
|
void *arg //!<
|
|
) {
|
|
os_printf(">> connectCB_inbound\n");
|
|
struct espconn *pEspconn = (struct espconn *)arg;
|
|
assert(pEspconn != NULL);
|
|
|
|
dumpEspConn(pEspconn);
|
|
|
|
int s = getServerSocketByLocalPort(pEspconn->proto.tcp->local_port);
|
|
assert(s != -1);
|
|
struct socketData *pSocketData = getSocketData(s);
|
|
assert(pSocketData != NULL);
|
|
|
|
esp8266_dumpSocket(pSocketData->socketId);
|
|
|
|
os_printf("** new client has connected to us **\n");
|
|
|
|
if ((pSocketData->acceptedSocketsHead + 1) % MAX_ACCEPTED_SOCKETS == pSocketData->acceptedSocketsTail) {
|
|
os_printf("WARNING!! - Discarding inbound client because we have too many accepted clients.\n");
|
|
os_printf("<< connectCB_inbound\n");
|
|
return;
|
|
}
|
|
|
|
int clientSocket = getNextFreeSocket();
|
|
if (clientSocket < 0) {
|
|
os_printf("!!! Ran out of sockets !!!\n");
|
|
return;
|
|
}
|
|
struct socketData *pClientSocketData = getSocketData(clientSocket);
|
|
assert(pClientSocketData != NULL);
|
|
pClientSocketData->pEspconn = pEspconn;
|
|
pClientSocketData->pEspconn->reverse = pClientSocketData;
|
|
pClientSocketData->isServer = false;
|
|
pClientSocketData->isConnected = true;
|
|
pClientSocketData->socketId = clientSocket;
|
|
pClientSocketData->state = SOCKET_STATE_IDLE;
|
|
|
|
pSocketData->acceptedSockets[pSocketData->acceptedSocketsHead] = clientSocket;
|
|
pSocketData->acceptedSocketsHead = (pSocketData->acceptedSocketsHead + 1) % MAX_ACCEPTED_SOCKETS;
|
|
|
|
os_printf("<< connectCB_inbound\n");
|
|
} // End of esp8266_callback_connectCB_inbound
|
|
|
|
/**
|
|
* \brief Callback function registered to the ESP8266 environment that is
|
|
* invoked when a new outbound connection has been formed.
|
|
*/
|
|
static void esp8266_callback_connectCB_outbound(
|
|
void *arg //!< A pointer to a `struct espconn`.
|
|
) {
|
|
os_printf(">> connectCB_outbound\n");
|
|
struct espconn *pEspconn = (struct espconn *)arg;
|
|
assert(pEspconn != NULL);
|
|
|
|
dumpEspConn(pEspconn);
|
|
|
|
struct socketData *pSocketData = (struct socketData *)pEspconn->reverse;
|
|
assert(pSocketData != NULL);
|
|
|
|
esp8266_dumpSocket(pSocketData->socketId);
|
|
|
|
// Flag the socket as connected to a partner.
|
|
pSocketData->isConnected = true;
|
|
|
|
assert(pSocketData->state == SOCKET_STATE_CONNECTING);
|
|
if (pSocketData->shouldClose) {
|
|
doClose(pSocketData->socketId);
|
|
} else {
|
|
pSocketData->state = SOCKET_STATE_IDLE;
|
|
}
|
|
os_printf("<< connectCB_outbound\n");
|
|
} // End of esp8266_callback_connectCB_outbound
|
|
|
|
|
|
/**
|
|
* \brief Callback function registered to the ESP8266 environment that is
|
|
* Invoked when a previous connection has been disconnected.
|
|
*/
|
|
static void esp8266_callback_disconnectCB(
|
|
void *arg //!< A pointer to a `struct espconn`.
|
|
) {
|
|
struct espconn *pEspconn = (struct espconn *)arg;
|
|
struct socketData *pSocketData = (struct socketData *)pEspconn->reverse;
|
|
assert(pSocketData != NULL);
|
|
assert(pSocketData->state != SOCKET_STATE_UNUSED);
|
|
|
|
os_printf(">> disconnectCB\n");
|
|
dumpEspConn(pEspconn);
|
|
esp8266_dumpSocket(pSocketData->socketId);
|
|
|
|
// If the socket state is SOCKET_STATE_CLOSING then that means we can release the socket. The reason
|
|
// for this is that the last thing the user did was request an explicit socket close.
|
|
if (pSocketData->state == SOCKET_STATE_CLOSING) {
|
|
releaseSocket(pSocketData->socketId);
|
|
} else {
|
|
pSocketData->state = SOCKET_STATE_CLOSING;
|
|
pSocketData->isConnected = false;
|
|
}
|
|
os_printf("<< disconnectCB\n");
|
|
} // End of disconnectCB
|
|
|
|
|
|
/**
|
|
*
|
|
*/
|
|
static void esp8266_callback_writeFinishedCB(
|
|
void *arg //!< A pointer to a `struct espconn`.
|
|
) {
|
|
os_printf(">> writeFinishedCB\n");
|
|
struct espconn *pEspconn = (struct espconn *)arg;
|
|
struct socketData *pSocketData = (struct socketData *)pEspconn->reverse;
|
|
if (pSocketData->currentTx != NULL) {
|
|
os_free(pSocketData->currentTx);
|
|
pSocketData->currentTx = NULL;
|
|
}
|
|
os_printf("<< writeFinishedCB\n");
|
|
} // End of writeFinishedCB
|
|
|
|
|
|
/**
|
|
* \brief Error handler callback.
|
|
* Although this is called reconnect by Espressif, this is really an error handler
|
|
* routine. It will be called when an error is detected.
|
|
*/
|
|
static void esp8266_callback_reconnectCB(
|
|
void *arg, //!< A pointer to a `struct espconn`.
|
|
sint8 err //!< The error code.
|
|
) {
|
|
os_printf(">> reconnectCB: Error code is: %d - %s\n", err, esp8266_errorToString(err));
|
|
os_printf("<< reconnectCB");
|
|
} // End of reconnectCB
|
|
|
|
|
|
/**
|
|
* \brief Callback function registered to the ESP8266 environment that is
|
|
* invoked when a send operation has been completed.
|
|
*/
|
|
static void esp8266_callback_sentCB(
|
|
void *arg //!< A pointer to a `struct espconn`.
|
|
) {
|
|
os_printf(">> sendCB\n");
|
|
struct espconn *pEspconn = (struct espconn *)arg;
|
|
struct socketData *pSocketData = (struct socketData *)pEspconn->reverse;
|
|
|
|
assert(pSocketData != NULL);
|
|
assert(pSocketData->state == SOCKET_STATE_TRANSMITTING);
|
|
|
|
// We have transmitted the data ... which means that the data that was in the transmission
|
|
// buffer can be released.
|
|
if (pSocketData->currentTx != NULL) {
|
|
os_free(pSocketData->currentTx);
|
|
pSocketData->currentTx = NULL;
|
|
}
|
|
|
|
if (pSocketData->shouldClose) {
|
|
doClose(pSocketData->socketId);
|
|
} else {
|
|
pSocketData->state = SOCKET_STATE_IDLE;
|
|
}
|
|
os_printf("<< sendCB\n");
|
|
} // End of sentCB
|
|
|
|
|
|
/**
|
|
* \brief ESP8266 callback function that is invoked when new data has arrived over
|
|
* the TCP/IP connection.
|
|
*/
|
|
static void esp8266_callback_recvCB(
|
|
void *arg, //!< A pointer to a `struct espconn`.
|
|
char *pData, //!< A pointer to data received over the socket.
|
|
unsigned short len //!< The length of the data.
|
|
) {
|
|
struct espconn *pEspconn = (struct espconn *)arg;
|
|
struct socketData *pSocketData = (struct socketData *)pEspconn->reverse;
|
|
|
|
assert(pSocketData != NULL);
|
|
assert(pSocketData->state != SOCKET_STATE_UNUSED);
|
|
|
|
os_printf(">> recvCB for socket=%d, length=%d\n", pSocketData->socketId, len);
|
|
|
|
// If we don't have any existing unconsumed data then malloc some storage and
|
|
// copy the received data into that storage.
|
|
if (pSocketData->rxBufLen == 0) {
|
|
pSocketData->rxBuf = (void *)os_malloc(len);
|
|
memcpy(pSocketData->rxBuf, pData, len);
|
|
pSocketData->rxBufLen = len;
|
|
} else {
|
|
// Allocate a new buffer big enough for the original data and the new data
|
|
// Copy the original data to the start of the new buffer ...
|
|
// Copy the new new data to the offset into the new buffer just after
|
|
// the original data.
|
|
// Release the original data.
|
|
// Update the socket data.
|
|
uint8 *pNewBuf = (uint8 *)os_malloc(len + pSocketData->rxBufLen);
|
|
memcpy(pNewBuf, pSocketData->rxBuf, pSocketData->rxBufLen);
|
|
memcpy(pNewBuf + pSocketData->rxBufLen, pData, len);
|
|
os_free(pSocketData->rxBuf);
|
|
pSocketData->rxBuf = pNewBuf;
|
|
pSocketData->rxBufLen += len;
|
|
} // End of new data allocated.
|
|
dumpEspConn(pEspconn);
|
|
os_printf("<< recvCB\n");
|
|
|
|
} // End of recvCB
|
|
|
|
|
|
// -------------------------------------------------
|
|
|
|
/**
|
|
* \brief Define the implementation functions for the logical network functions.
|
|
*/
|
|
void netSetCallbacks_esp8266_board(
|
|
JsNetwork *net //!< The Network we are going to use.
|
|
) {
|
|
net->idle = net_ESP8266_BOARD_idle;
|
|
net->checkError = net_ESP8266_BOARD_checkError;
|
|
net->createsocket = net_ESP8266_BOARD_createSocket;
|
|
net->closesocket = net_ESP8266_BOARD_closeSocket;
|
|
net->accept = net_ESP8266_BOARD_accept;
|
|
net->gethostbyname = net_ESP8266_BOARD_gethostbyname;
|
|
net->recv = net_ESP8266_BOARD_recv;
|
|
net->send = net_ESP8266_BOARD_send;
|
|
} // End of netSetCallbacks_esp8266_board
|
|
|
|
|
|
/**
|
|
* \brief Determine if there is a new client connection on the server socket.
|
|
* This function is called to poll to see if the serverSckt has a new
|
|
* accepted connection (socket) and, if it does, return it else return -1 to indicate
|
|
* that there was no new accepted socket.
|
|
*/
|
|
int net_ESP8266_BOARD_accept(
|
|
JsNetwork *net, //!< The Network we are going to use to create the socket.
|
|
int serverSckt //!< The socket that we are checking to see if there is a new client connection.
|
|
) {
|
|
//os_printf("> net_ESP8266_BOARD_accept\n");
|
|
struct socketData *pSocketData = getSocketData(serverSckt);
|
|
assert(pSocketData->state != SOCKET_STATE_UNUSED);
|
|
assert(pSocketData->isServer == true);
|
|
|
|
// If the list is empty, return.
|
|
if (pSocketData->acceptedSocketsHead == pSocketData->acceptedSocketsTail) {
|
|
// Return -1 if there is no new client socket for this server.
|
|
return -1;
|
|
}
|
|
|
|
// Return the 1st socket id that is in the list of accepted sockets. We also update the
|
|
// list to indicate that it has been read.
|
|
int acceptedSocketId = pSocketData->acceptedSockets[pSocketData->acceptedSocketsTail];
|
|
pSocketData->acceptedSocketsTail = (pSocketData->acceptedSocketsTail + 1) % MAX_ACCEPTED_SOCKETS;
|
|
|
|
os_printf("> net_ESP8266_BOARD_accept: Accepted a new socket, socketId=%d\n", acceptedSocketId);
|
|
return acceptedSocketId;
|
|
} // End of net_ESP8266_BOARD_accept
|
|
|
|
|
|
/**
|
|
* \brief Receive data from the network device.
|
|
* Returns the number of bytes received which may be 0 and -1 if there was an error.
|
|
*/
|
|
int net_ESP8266_BOARD_recv(
|
|
JsNetwork *net, //!< The Network we are going to use to create the socket.
|
|
int sckt, //!< The socket from which we are to receive data.
|
|
void *buf, //!< The storage buffer into which we will receive data.
|
|
size_t len //!< The length of the buffer.
|
|
) {
|
|
assert(sckt >=0 && sckt<MAX_SOCKETS);
|
|
struct socketData *pSocketData = getSocketData(sckt);
|
|
assert(pSocketData->state != SOCKET_STATE_UNUSED);
|
|
|
|
// If there is no data in the receive buffer, then all we need do is return
|
|
// 0 bytes as the length of data moved.
|
|
if (pSocketData->rxBufLen == 0) {
|
|
if (pSocketData->state == SOCKET_STATE_CLOSING) {
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// If the receive buffer contains data and is it is able to fit in the buffer
|
|
// passed into us then we can copy all the data and the receive buffer will be clear.
|
|
if (pSocketData->rxBufLen <= len) {
|
|
memcpy(buf, pSocketData->rxBuf, pSocketData->rxBufLen);
|
|
int retLen = pSocketData->rxBufLen;
|
|
os_free(pSocketData->rxBuf);
|
|
pSocketData->rxBufLen = 0;
|
|
pSocketData->rxBuf = NULL;
|
|
return retLen;
|
|
}
|
|
|
|
// If we are here, then we have more data in the receive buffer than is available
|
|
// to be returned in this request for data. So we have to copy the amount of data
|
|
// that is allowed to be returned and then strip that from the beginning of the
|
|
// receive buffer.
|
|
|
|
// First we copy the data we are going to return.
|
|
memcpy(buf, pSocketData->rxBuf, len);
|
|
|
|
// Next we allocate a new buffer and copy in the data we are not returning.
|
|
uint8 *pNewBuf = (uint8 *)os_malloc(pSocketData->rxBufLen-len);
|
|
memcpy(pNewBuf, pSocketData->rxBuf + len, pSocketData->rxBufLen-len);
|
|
|
|
// Now we juggle pointers and release the original RX buffer now that we have a new
|
|
// one. It is likely that this algorithm can be significantly improved since there
|
|
// is a period of time where we might actuall have TWO copies of the data.
|
|
uint8 *pTemp = pSocketData->rxBuf;
|
|
pSocketData->rxBuf = pNewBuf;
|
|
pSocketData->rxBufLen = pSocketData->rxBufLen-len;
|
|
os_free(pTemp);
|
|
|
|
return len;
|
|
} // End of net_ESP8266_BOARD_recv.
|
|
|
|
|
|
/**
|
|
* \brief Send data to the partner.
|
|
* The return is the number of bytes actually transmitted which may also be
|
|
* 0 to indicate no bytes sent or -1 to indicate an error. For the ESP8266 implementation we
|
|
* will return 0 if the socket is not connected or we are in the `SOCKET_STATE_TRANSMITTING`
|
|
* state.
|
|
*/
|
|
int net_ESP8266_BOARD_send(
|
|
JsNetwork *net, //!< The Network we are going to use to create the socket.
|
|
int sckt, //!< The socket over which we will send data.
|
|
const void *buf, //!< The buffer containing the data to be sent.
|
|
size_t len //!< The length of data in the buffer to send.
|
|
) {
|
|
assert(sckt >=0 && sckt<MAX_SOCKETS);
|
|
os_printf("> net_ESP8266_BOARD_send: Request to send data to socket %d of size %d: ", sckt, len);
|
|
|
|
struct socketData *pSocketData = getSocketData(sckt);
|
|
assert(pSocketData->state != SOCKET_STATE_UNUSED);
|
|
|
|
// If we are not connected, then we can't currently send data.
|
|
if (pSocketData->isConnected == false) {
|
|
os_printf(" - Not connected\n");
|
|
return 0;
|
|
}
|
|
|
|
// If we are currently sending data, we can't send more.
|
|
if (pSocketData->state == SOCKET_STATE_TRANSMITTING) {
|
|
os_printf(" - Currently transmitting\n");
|
|
return 0;
|
|
}
|
|
|
|
// Log the content of the data we are sening.
|
|
esp8266_board_writeString(buf, len);
|
|
|
|
os_printf("\n");
|
|
|
|
assert(pSocketData->state == SOCKET_STATE_IDLE);
|
|
|
|
pSocketData->state = SOCKET_STATE_TRANSMITTING;
|
|
|
|
// Copy the data that was passed to us to a private area. We do this because we must not
|
|
// assume that the data passed in will be available after this function returns. It may have
|
|
// been passed in on the stack.
|
|
assert(pSocketData->currentTx == NULL);
|
|
pSocketData->currentTx = (uint8_t *)os_malloc(len);
|
|
memcpy(pSocketData->currentTx, buf, len);
|
|
|
|
// Send the data over the ESP8266 SDK.
|
|
int rc = espconn_send(pSocketData->pEspconn, pSocketData->currentTx, len);
|
|
if (rc < 0) {
|
|
setSocketInError(sckt, "espconn_send", rc);
|
|
os_free(pSocketData->currentTx);
|
|
pSocketData->currentTx = NULL;
|
|
return -1;
|
|
}
|
|
|
|
esp8266_dumpSocket(sckt);
|
|
os_printf("< net_ESP8266_BOARD_send\n");
|
|
return len;
|
|
} // End of net_ESP8266_BOARD_send
|
|
|
|
|
|
/**
|
|
* \brief Perform idle processing.
|
|
* There is the possibility that we may wish to perform logic when we are idle. For the
|
|
* ESP8266 there is no specific idle network processing needed.
|
|
*/
|
|
void net_ESP8266_BOARD_idle(
|
|
JsNetwork *net //!< The Network we are part of.
|
|
) {
|
|
// Don't echo here because it is called continuously
|
|
//os_printf("> net_ESP8266_BOARD_idle\n");
|
|
} // End of net_ESP8266_BOARD_idle
|
|
|
|
|
|
/**
|
|
* \brief Check for errors.
|
|
* Returns true if there are NO errors.
|
|
*/
|
|
bool net_ESP8266_BOARD_checkError(
|
|
JsNetwork *net //!< The Network we are going to use to create the socket.
|
|
) {
|
|
//os_printf("> net_ESP8266_BOARD_checkError\n");
|
|
return true;
|
|
} // End of net_ESP8266_BOARD_checkError
|
|
|
|
|
|
/**
|
|
* \brief Create a new socket.
|
|
* if `ipAddress == 0`, creates a server otherwise creates a client (and automatically connects). Returns >=0 on success.
|
|
*/
|
|
int net_ESP8266_BOARD_createSocket(
|
|
JsNetwork *net, //!< The Network we are going to use to create the socket.
|
|
uint32_t ipAddress, //!< The address of the partner of the socket or 0 if we are to be a server.
|
|
unsigned short port //!< The port number that the partner is listening upon.
|
|
) {
|
|
os_printf("> net_ESP8266_BOARD_createSocket: host: %d.%d.%d.%d, port:%d \n", ((char *)(&ipAddress))[0], ((char *)(&ipAddress))[1], ((char *)(&ipAddress))[2], ((char *)(&ipAddress))[3], port);
|
|
|
|
bool isServer = (ipAddress == 0);
|
|
|
|
int sckt = getNextFreeSocket();
|
|
if (sckt < 0) { // No free socket
|
|
os_printf("< net_ESP8266_BOARD_createSocket: No free sockets\n");
|
|
return -1;
|
|
}
|
|
|
|
struct socketData *pSocketData = getSocketData(sckt);
|
|
struct espconn *pEspconn = pSocketData->pEspconn;
|
|
|
|
espconn_regist_disconcb(pEspconn, esp8266_callback_disconnectCB);
|
|
espconn_regist_reconcb(pEspconn, esp8266_callback_reconnectCB);
|
|
espconn_regist_sentcb(pEspconn, esp8266_callback_sentCB);
|
|
espconn_regist_recvcb(pEspconn, esp8266_callback_recvCB);
|
|
espconn_regist_write_finish(pEspconn, esp8266_callback_writeFinishedCB);
|
|
|
|
struct ip_info ipconfig;
|
|
wifi_get_ip_info(STATION_IF, &ipconfig); // Get the local IP address
|
|
os_memcpy(pEspconn->proto.tcp->local_ip, &ipconfig.ip, 4);
|
|
pEspconn->type = ESPCONN_TCP;
|
|
pEspconn->state = ESPCONN_NONE;
|
|
pEspconn->proto.tcp = &tcpArray[sckt];
|
|
pEspconn->reverse = pSocketData;
|
|
|
|
// If ipAddress != 0 then make a client connection
|
|
if (isServer == false) {
|
|
pSocketData->state = SOCKET_STATE_CONNECTING;
|
|
pSocketData->isServer = false;
|
|
pEspconn->proto.tcp->remote_port = port;
|
|
pEspconn->proto.tcp->local_port = espconn_port();
|
|
|
|
*(uint32 *)(pEspconn->proto.tcp->remote_ip) = ipAddress;
|
|
|
|
// Ensure that we have flagged this socket as NOT connected
|
|
pSocketData->isConnected = false;
|
|
|
|
espconn_regist_connectcb(pEspconn, esp8266_callback_connectCB_outbound);
|
|
|
|
// Make a call to espconn_connect.
|
|
int rc = espconn_connect(pEspconn);
|
|
if (rc != 0) {
|
|
os_printf("Err: net_ESP8266_BOARD_createSocket -> espconn_connect returned: %d. Using local port: %d\n", rc, pEspconn->proto.tcp->local_port);
|
|
setSocketInError(sckt, "espconn_connect", rc);
|
|
}
|
|
/*
|
|
os_printf("Checking to see if we are connected ...\n");
|
|
uint32 clock= system_get_time();
|
|
while(pSocketData->isConnected == false) {
|
|
system_soft_wdt_feed();
|
|
if ((system_get_time() - clock) > 10000000) {
|
|
os_printf("clock %d\n", (unit32)clock);
|
|
clock = system_get_time();
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
// If the ipAddress IS 0 ... then we are a server.
|
|
else
|
|
{
|
|
pSocketData->state = SOCKET_STATE_IDLE;
|
|
pSocketData->isServer = true;
|
|
// We are going to set ourselves up as a server
|
|
pEspconn->proto.tcp->local_port = port;
|
|
|
|
espconn_regist_connectcb(pEspconn, esp8266_callback_connectCB_inbound);
|
|
|
|
// Make a call to espconn_accept
|
|
int rc = espconn_accept(pEspconn);
|
|
if (rc != 0) {
|
|
os_printf("Err: net_ESP8266_BOARD_createSocket -> espconn_accept returned: %d. Using local port: %d\n", rc, pEspconn->proto.tcp->local_port);
|
|
setSocketInError(sckt, "espconn_accept", rc);
|
|
}
|
|
} // End of
|
|
|
|
dumpEspConn(pEspconn);
|
|
os_printf("< net_ESP8266_BOARD_createSocket, socket=%d\n", sckt);
|
|
return sckt;
|
|
} // End of net_ESP8266_BOARD_createSocket
|
|
|
|
|
|
/**
|
|
* \brief Close a socket.
|
|
*/
|
|
void net_ESP8266_BOARD_closeSocket(
|
|
JsNetwork *net, //!< The Network we are going to use to create the socket.
|
|
int socketId //!< The socket to be closed.
|
|
) {
|
|
os_printf("> net_ESP8266_BOARD_closeSocket, socket=%d\n", socketId);
|
|
|
|
assert(socketId >=0 && socketId<MAX_SOCKETS);
|
|
|
|
struct socketData *pSocketData = getSocketData(socketId);
|
|
|
|
assert(pSocketData != NULL);
|
|
assert(pSocketData->state != SOCKET_STATE_UNUSED); // Shouldn't be closing an unused socket.
|
|
|
|
dumpEspConn(pSocketData->pEspconn);
|
|
esp8266_dumpSocket(socketId);
|
|
|
|
// How we close the socket is a function of what kind of socket it is.
|
|
if (pSocketData->isServer == true) {
|
|
int rc = espconn_delete(pSocketData->pEspconn);
|
|
if (rc != 0) {
|
|
os_printf("espconn_delete: rc=%d\n", rc);
|
|
}
|
|
} // End this is a server socket
|
|
else
|
|
{
|
|
if (pSocketData->state == SOCKET_STATE_IDLE || pSocketData->state == SOCKET_STATE_CLOSING) {
|
|
doClose(socketId);
|
|
} else {
|
|
pSocketData->shouldClose = true;
|
|
}
|
|
} // End this is a client socket
|
|
} // End of net_ESP8266_BOARD_closeSocket
|
|
|
|
|
|
/**
|
|
* \brief Callback handler for espconn_gethostbyname.
|
|
* This is a function that will be called back by the ESP8266 when the resolution of
|
|
* a hostname has been completed.
|
|
*/
|
|
static void dnsFoundCallback(const char *hostName, ip_addr_t *ipAddr, void *arg) {
|
|
assert(arg != NULL);
|
|
|
|
uint32_t *returnIp = (uint32_t *)arg;
|
|
|
|
// ipAddr will be NULL if the IP address can not be resolved.
|
|
if (ipAddr == NULL) {
|
|
* returnIp = 0;
|
|
} else {
|
|
*returnIp = ipAddr->addr;
|
|
}
|
|
} // End of dnsFoundCallback
|
|
|
|
|
|
/**
|
|
* \brief Get an IP address from a name.
|
|
* Sets 'outIp' to 0 on failure and 0xFFFFFFFF on unknown. At some time later, the
|
|
* IP address will be properly updated.
|
|
*/
|
|
void net_ESP8266_BOARD_gethostbyname(
|
|
JsNetwork *net, //!< The Network we are going to use to create the socket.
|
|
char *hostName, //!< The string representing the hostname we wish to lookup.
|
|
uint32_t *outIp //!< The address into which the resolved IP address will be stored.
|
|
) {
|
|
assert(hostName != NULL);
|
|
assert(outIp != NULL);
|
|
os_printf("> net_ESP8266_BOARD_gethostbyname: Resolving: %s\n", hostName);
|
|
int rc = espconn_gethostbyname((struct espconn *)outIp, hostName, (ip_addr_t *)outIp, dnsFoundCallback);
|
|
// A rc of ESPCONN_OK means that we have an IP and it was stored in outIp.
|
|
// A rc of ESPCONN_INPROGRESS means that we will get the IP on a callback.
|
|
if (rc == ESPCONN_INPROGRESS) {
|
|
*outIp = 0xFFFFFFFF;
|
|
}
|
|
} // End of net_ESP8266_BOARD_gethostbyname
|
|
|
|
|
|
// ----------------------------------------------------------------
|
|
|
|
/**
|
|
* The following section is all about a logical concept called a memoryBuffer. This is an
|
|
* abstract data type that contains data in memory. The operations we can perform upon it
|
|
* are:
|
|
|
|
* o memoryBuffer_append - Append data to the end of the memory buffer.
|
|
* o memoryBuffer_read - Read a fixed number of bytes from the memory buffer.
|
|
* o memoryBuffer_delete - Delete the memory buffer. No further operations should be performed
|
|
* against it.
|
|
* o memoryBuffer_getSize - Get the size of data contained in the memory buffer.
|
|
*/
|
|
|
|
/**
|
|
* \brief Delete all content of the memory buffer.
|
|
*/
|
|
static void memoryBuffer_delete(
|
|
struct memoryBuffer *pMemoryBuffer //!<
|
|
) {
|
|
if (pMemoryBuffer->length > 0) {
|
|
os_free(pMemoryBuffer->buf);
|
|
pMemoryBuffer->buf = NULL;
|
|
pMemoryBuffer->length = 0;
|
|
}
|
|
} // End of memoryBuffer_delete
|
|
|
|
|
|
/**
|
|
* \brief Append new data to the end of the existing memory buffer.
|
|
*/
|
|
static uint8 *memoryBuffer_append(
|
|
struct memoryBuffer *pMemoryBuffer, //!<
|
|
uint8 *pNewData, //!<
|
|
size_t length //!<
|
|
) {
|
|
assert(pMemoryBuffer != NULL);
|
|
|
|
if (length == 0) {
|
|
return pMemoryBuffer->buf;
|
|
}
|
|
|
|
assert(pNewData != NULL);
|
|
|
|
// Handle the memory buffer being empty.
|
|
if (pMemoryBuffer->length == 0) {
|
|
pMemoryBuffer->buf = (uint8 *)os_malloc(length);
|
|
if (pMemoryBuffer->buf == NULL) { // Out of memory
|
|
jsError("malloc failed at memoryBuffer_append trying to allocate %d", length);
|
|
} else {
|
|
memcpy(pMemoryBuffer->buf, pNewData, length);
|
|
pMemoryBuffer->length = length;
|
|
}
|
|
} else {
|
|
// The memory buffer was not empty, so we append data.
|
|
int newSize = pMemoryBuffer->length + length;
|
|
uint8 *resizedStorage = (uint8 *)os_realloc(pMemoryBuffer->buf, newSize);
|
|
if (resizedStorage != NULL) {
|
|
pMemoryBuffer->buf = resizedStorage;
|
|
memcpy(pMemoryBuffer->buf + length, pNewData, length);
|
|
pMemoryBuffer->length = newSize;
|
|
}
|
|
}
|
|
return pMemoryBuffer->buf;
|
|
} // End of memoryBuffer_append
|
|
|
|
|
|
/**
|
|
* \brief Return how much data is stored in the memory buffer.
|
|
*/
|
|
static int memoryBuffer_getSize(
|
|
struct memoryBuffer *pMemoryBuffer //!<
|
|
) {
|
|
assert(pMemoryBuffer != NULL);
|
|
return pMemoryBuffer->length;
|
|
} // End of memoryBuffer_getSize
|
|
|
|
|
|
/**
|
|
* \brief Read data from the memory buffer of an exact size.
|
|
* The data that is returned
|
|
* should be released with an os_free() call.
|
|
*/
|
|
static uint8 *memoryBuffer_read(
|
|
struct memoryBuffer *pMemoryBuffer, //!<
|
|
size_t readSize //!<
|
|
) {
|
|
assert(pMemoryBuffer != NULL);
|
|
assert((pMemoryBuffer->length > 0 && pMemoryBuffer->buf != NULL) || pMemoryBuffer->length == 0);
|
|
|
|
// Check that we are NOT trying to read more data than we actually have available to us.
|
|
assert(readSize > pMemoryBuffer->length);
|
|
|
|
// Handle the case where we are trying to read 0 bytes.
|
|
if (readSize == 0) {
|
|
return NULL;
|
|
}
|
|
|
|
// If the size of data we are willing to read is EXACTLY the size of the buffer we
|
|
// have, then simply return a pointer to the buffer and we are done.
|
|
if (readSize == pMemoryBuffer->length) {
|
|
uint8 *pTemp = pMemoryBuffer->buf;
|
|
pMemoryBuffer->buf = NULL;
|
|
pMemoryBuffer->length = 0;
|
|
return pTemp;
|
|
}
|
|
|
|
// We can assert that size < memory buffer length.
|
|
//
|
|
// Here we have determined that we wish to consume LESS data that we have available.
|
|
// That means we have to split our data into parts.
|
|
|
|
// First we build the data that we will return and copy in the memory buffer data.
|
|
uint8 *pTemp = (uint8 *)os_malloc(readSize);
|
|
if (pTemp == NULL) { // Out of memory
|
|
jsError("malloc failed at memoryBuffer_append trying to allocate %d", readSize);
|
|
return NULL;
|
|
}
|
|
os_memcpy(pTemp, pMemoryBuffer->buf, readSize);
|
|
|
|
// Now we create a memory buffer to hold the remaining data that was not
|
|
// returned.
|
|
int newSize = pMemoryBuffer->length - readSize;
|
|
uint8 *pTemp2 = (uint8 *)os_malloc(newSize);
|
|
os_memcpy(pTemp2, pMemoryBuffer->buf + readSize, newSize);
|
|
os_free(pMemoryBuffer->buf);
|
|
pMemoryBuffer->buf = pTemp2;
|
|
pMemoryBuffer->length = newSize;
|
|
return pTemp;
|
|
} // End of memoryBuffer_read
|
|
// End of file
|