/**************************************************************************** * apps/netutils/esp8266/esp8266.c * * Derives from an application to demo an Arduino connected to the ESPRESSIF * ESP8266 with AT command firmware. * * Copyright (C) 2015 Pierre-Noel Bouteville. All rights reserved. * Author: Pierre-Noel Bouteville * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * 3. Neither the name NuttX nor the names of its contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "netutils/esp8266.h" #ifdef CONFIG_NETUTILS_ESP8266 /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ #define NETAPP_IPCONFIG_MAC_OFFSET (20) #ifndef CONFIG_NETUTILS_ESP8266_MAXTXLEN # define CONFIG_NETUTILS_ESP8266_MAXTXLEN 256 #endif #ifndef CONFIG_NETUTILS_ESP8266_MAXRXLEN # define CONFIG_NETUTILS_ESP8266_MAXRXLEN 256 #endif #if (CONFIG_NETUTILS_ESP8266_MAXRXLEN < CONFIG_NETUTILS_ESP8266_WORKER_BUF_LEN) # error "CONFIG_NETUTILS_ESP8266_WORKER_BUF_LEN whould be bigger than CONFIG_NETUTILS_ESP8266_MAXRXLEN" #endif #define BUF_CMD_LEN CONFIG_NETUTILS_ESP8266_MAXTXLEN #define BUF_ANS_LEN CONFIG_NETUTILS_ESP8266_MAXRXLEN #define BUF_WORKER_LEN CONFIG_NETUTILS_ESP8266_WORKER_BUF_LEN #define CON_NBR 4 #define ESP8266_ACCESS_POINT_NBR_MAX 32 #define lespWAITING_OK_POLLING_MS 250 #define lespTIMEOUT_FLUSH_MS 100 #define lespTIMEOUT_MS 1000 #define lespTIMEOUT_MS_SEND 1000 #define lespTIMEOUT_MS_CONNECTION 30000 #define lespTIMEOUT_MS_LISP_AP 5000 #define lespTIMEOUT_FLOODING_OFFSET_S 3 #define lespTIMEOUT_MS_RECV_S 60 #define lespCON_USED_MASK(idx) (1<<(idx)) #define lespPOLLING_TIME_MS 1000 /* Must be a power of 2 */ #define SOCKET_FIFO_SIZE 2048 #define SOCKET_NBR 4 #define FLAGS_SOCK_USED (1 << 0) #define FLAGS_SOCK_CONNECTED (1 << 1) #define FLAGS_SOCK_TYPE_MASK (3 << 2) #define FLAGS_SOCK_TYPE_TCP (0 << 2) #define FLAGS_SOCK_TYPE_UDP (1 << 2) #define FLAGS_SOCK_TYPE_SSL (2 << 2) /* non standard but useful */ /**************************************************************************** * Private Types ****************************************************************************/ typedef enum { lesp_eERR = -1, lesp_eNONE = 0, lesp_eOK = 1 }lesp_ans_t; typedef struct { sem_t *sem; uint8_t flags; uint16_t inndx; uint16_t outndx; struct timespec rcv_timeo; uint8_t rxbuf[SOCKET_FIFO_SIZE]; } lesp_socket_t; typedef struct { bool running; pthread_t thread; char rxbuf[BUF_WORKER_LEN]; sem_t sem; /* Inform that something is received */ char buf[BUF_ANS_LEN]; /* Last complete line received */ lesp_ans_t ans; /* Last ans received (OK,FAIL or ERROR) */ pthread_mutex_t mutex; } lesp_worker_t; typedef struct { pthread_mutex_t mutex; bool is_initialized; int fd; lesp_worker_t worker; lesp_socket_t sockets[SOCKET_NBR]; lesp_ans_t ans; char bufans[BUF_ANS_LEN]; char bufcmd[BUF_CMD_LEN]; struct hostent hostent; /* ESP Got only One ip + 1 for NULL that indicate end of list */ in_addr_t * h_addr_list_buf[2]; in_addr_t in_addr; } lesp_state_t; /**************************************************************************** * Private Functions prototypes ****************************************************************************/ static int lesp_low_level_read(uint8_t *buf, int size); static inline int lesp_read_ipd(int sockfd, int len); static lesp_socket_t *get_sock(int sockfd); /**************************************************************************** * Private Data ****************************************************************************/ lesp_state_t g_lesp_state = { .mutex = PTHREAD_MUTEX_INITIALIZER, .is_initialized = false, .fd = -1, .worker.running = false, .worker.ans = lesp_eNONE, .worker.mutex = PTHREAD_MUTEX_INITIALIZER, .ans = lesp_eNONE, }; /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Name: lesp_clear_read_buffer * * Description: * * Input Parmeters: * * Returned Value: * None * ****************************************************************************/ static inline void lesp_clear_read_buffer(void) { g_lesp_state.bufans[0] = '\0'; } /**************************************************************************** * Name: lesp_clear_read_ans * * Description: * * Input Parmeters: * * Returned Value: * None * ****************************************************************************/ static inline void lesp_clear_read_ans(void) { g_lesp_state.ans = lesp_eNONE; } /**************************************************************************** * Name: lesp_str_to_unsigned * * Description: * * Input Parmeters: * * Returned Value: * unsigned value * ****************************************************************************/ static inline int lesp_str_to_unsigned(char **p_ptr, char end) { int nbr = 0; char *ptr = *p_ptr; while (*ptr != end) { char c = *ptr++ - '0'; if ((c < 0) || (c >= 10)) return -1; nbr *= 10; nbr += c; } *p_ptr = ptr+1; /* Pass the end char */ return nbr; } /**************************************************************************** * Name: lesp_set_baudrate * * Description: * Set com port to baudrate. * * Input Parmeters: * sockfd : int baudrate * * Returned Value: * 0 on success, -1 incase of error. * ****************************************************************************/ #ifdef CONFIG_SERIAL_TERMIOS static int lesp_set_baudrate(int baudrate) { struct termios term; if (ioctl(g_lesp_state.fd, TCGETS, (unsigned long)&term) < 0) { nerr("ERROR: TCGETS failed.\n"); return -1; } if ((cfsetispeed(&term, baudrate) < 0) || (cfsetospeed(&term, baudrate) < 0)) { nerr("ERROR: Connot set baudrate %0x08X\n", baudrate); return -1; } if (ioctl(g_lesp_state.fd, TCSETS, (unsigned long)&term) < 0) { nerr("ERROR: TCSETS failed.\n"); return -1; } return 0; } #endif /**************************************************************************** * Name: get_sock * * Description: * Get socket structure pointer of sockfd. * * Input Parmeters: * sockfd : socket id * * Returned Value: * socket id pointer, NULL on error. * ****************************************************************************/ static lesp_socket_t *get_sock(int sockfd) { DEBUGASSERT(sockfd >= 0); if (!g_lesp_state.is_initialized) { errno = ENETDOWN; ninfo("Esp8266 not initialized; can't list access points\n"); return NULL; } if (((unsigned int)sockfd) >= SOCKET_NBR) { errno = EINVAL; ninfo("Esp8266 invalid sockfd\n", sockfd); return NULL; } if ((g_lesp_state.sockets[sockfd].flags & FLAGS_SOCK_USED) == 0) { errno = EPERM; nerr("ERROR: Connection id %d not Created!\n", sockfd); return NULL; } return &g_lesp_state.sockets[sockfd]; } /**************************************************************************** * Name: get_sock_protected * * Description: * Get socket structure pointer of sockfd with worker.mutex portection. * * Input Parmeters: * sockfd : socket id * * Returned Value: * socket id pointer, NULL on error. * ****************************************************************************/ static lesp_socket_t *get_sock_protected(int sockfd) { lesp_socket_t *ret; pthread_mutex_lock(&g_lesp_state.worker.mutex); ret = get_sock(sockfd); pthread_mutex_unlock(&g_lesp_state.worker.mutex); return ret; } /**************************************************************************** * Name: set_sock_closed * * Description: * Set socket sockfd mark as closed without check and don't send message to * esp8266. * * Input Parmeters: * sockfd : socket id * * Returned Value: * None * ****************************************************************************/ static void set_sock_closed(int sockfd) { sem_t *sem; lesp_socket_t *sock; DEBUGASSERT(((unsigned int)sockfd) < SOCKET_NBR); sock = &g_lesp_state.sockets[sockfd]; sem = sock->sem; sock->sem = NULL; sock->flags = 0; sock->inndx = 0; sock->outndx = 0; ninfo("Socket %d closed\n", sockfd); if (sem != NULL) { sem_post(sem); } } /**************************************************************************** * Name: lesp_low_level_read * * Description: * put size data from esp8266 into buf. * * Input Parmeters: * buf : buffer to store read data * size : size of data to read * * Returned Value: * number of byte read, -1 in case of error. * ****************************************************************************/ static int lesp_low_level_read(uint8_t *buf, int size) { int ret = 0; struct pollfd fds[1]; memset(fds, 0, sizeof(struct pollfd)); fds[0].fd = g_lesp_state.fd; fds[0].events = POLLIN; /* poll return 1=>even occur 0=>timeout or -1=>error */ ret = poll(fds, 1, lespPOLLING_TIME_MS); if (ret < 0) { int errcode = errno; nerr("ERROR: worker read Error %d (errno %d)\n", ret, errcode); UNUSED(errcode); } else if ((fds[0].revents & POLLERR) && (fds[0].revents & POLLHUP)) { nerr("ERROR: worker poll read Error %d\n", ret); ret = -1; } else if (fds[0].revents & POLLIN) { ret = read(g_lesp_state.fd, buf, size); } if (ret < 0) { return -1; } return ret; } /**************************************************************************** * Name: lesp_read_ipd * * Description: * Try to treat an '+IPD' command in worker buffer. Worker buffer should * already contain '+IPD,,:' * * Note: * g_lesp_state.worker.mutex should be locked. * * Input Parmeters: * sockfd : socker number to put received data. * len : data size to read on serial port. * * Returned Value: * 1 was an IPD, 0 is not an IPD, -1 on error. * ****************************************************************************/ static inline int lesp_read_ipd(int sockfd, int len) { lesp_socket_t *sock; sock = get_sock(sockfd); ninfo("Read %d bytes for socket %d \n", len, sockfd); if (sock == NULL) { nwarn("socket not opened: drop all data.\n"); } while (len) { int size; uint8_t *buf = (uint8_t *)g_lesp_state.worker.rxbuf; size = len; if (size >= BUF_WORKER_LEN) { size = BUF_WORKER_LEN; } size = lesp_low_level_read(buf, size); if (size <= 0) { return -1; } len -= size; if (sock != NULL) { while (size--) { int next; uint8_t b; /* Read the next byte from the buffer */ b = *buf++; /* Pre-calculate the next 'inndx'. We do this so that we can * check if the FIFO is full. */ next = sock->inndx + 1; if (next >= SOCKET_FIFO_SIZE) { next -= SOCKET_FIFO_SIZE; } /* Is there space in the circular buffer for another byte? If * the next 'inndx' would be equal to the 'outndx', then the * circular buffer is full. */ if (next == sock->outndx) { pthread_mutex_unlock(&g_lesp_state.worker.mutex); usleep(100); /* leave time of aplicative to read buffer */ pthread_mutex_lock(&g_lesp_state.worker.mutex); } if (next != sock->outndx) { /* Yes.. add the byte to the circular buffer */ sock->rxbuf[sock->inndx] = b; sock->inndx = next; } else { /* No.. the we have lost data */ nwarn("overflow socket 0x%02X\n", b); } } if (sock->sem) { ninfo("post %p \n", sock->sem); sem_post(sock->sem); } } } return 1; } /**************************************************************************** * Name: lesp_vsend_cmd * * Description: * Send cmd with format and argument like sprintf. * * Input Parmeters: * format : null terminated format string to send. * ap : format values. * * Returned Value: * len of cmd on success, -1 on error. * ****************************************************************************/ int lesp_vsend_cmd(FAR const IPTR char *format, va_list ap) { int ret = 0; ret = vsnprintf(g_lesp_state.bufcmd, BUF_CMD_LEN, format, ap); if (ret >= BUF_CMD_LEN) { g_lesp_state.bufcmd[BUF_CMD_LEN-1] = '\0'; ninfo("Buffer too small for '%s'...\n", g_lesp_state.bufcmd); ret = -1; } ninfo("Write:%s\n", g_lesp_state.bufcmd); ret = write(g_lesp_state.fd, g_lesp_state.bufcmd, ret); if (ret < 0) { ret = -1; } return ret; } /**************************************************************************** * Name: lesp_send_cmd * * Description: * Send cmd with format and argument like sprintf. * * Input Parmeters: * format : null terminated format string to send. * ... : format values. * * Note it clear AT Rx buffer (not socket buffer) * * Returned Value: * len of cmd on success, -1 on error. * ****************************************************************************/ static int lesp_send_cmd(FAR const IPTR char *format, ...) { int ret = 0; va_list ap; lesp_clear_read_buffer(); lesp_clear_read_ans(); /* Let lesp_vsend_cmd do the real work */ va_start(ap, format); ret = lesp_vsend_cmd(format, ap); va_end(ap); return ret; } /**************************************************************************** * Name: lesp_read * * Description: * Read a answer line with timeout. * * Input Parmeters: * timeout_ms : timeout in millisecond. * * Returned Value: * len of line without line return on success, -1 on error. * ****************************************************************************/ static int lesp_read(int timeout_ms) { int ret = 0; struct timespec ts; if (! g_lesp_state.is_initialized) { ninfo("Esp8266 not initialized; can't list access points\n"); return -1; } if (clock_gettime(CLOCK_REALTIME, &ts) < 0) { return -1; } ts.tv_nsec += (timeout_ms%1000) * 1000000; if (ts.tv_nsec >= 1000000000) { ts.tv_nsec -= 1000000000; ts.tv_sec += 1; } ts.tv_sec += (timeout_ms/1000); do { if (sem_timedwait(&g_lesp_state.worker.sem, &ts) < 0) { return -1; } pthread_mutex_lock(&g_lesp_state.worker.mutex); if (g_lesp_state.worker.ans != lesp_eNONE) { g_lesp_state.ans = g_lesp_state.worker.ans; g_lesp_state.worker.ans = lesp_eNONE; } ret = strlen(g_lesp_state.worker.buf); if (ret > 0) { /* +1 to copy null */ memcpy(g_lesp_state.bufans, g_lesp_state.worker.buf, ret+1); } g_lesp_state.worker.buf[0] = '\0'; /* buffer is read */ pthread_mutex_unlock(&g_lesp_state.worker.mutex); } while ((ret <= 0) && (g_lesp_state.ans == lesp_eNONE)); ninfo("lesp_read %d=>%s and ans = %d \n", ret, g_lesp_state.bufans, g_lesp_state.ans); return ret; } /**************************************************************************** * Name: lesp_flush * * Description: * Read and discard all waiting data in rx buffer. * * Input Parmeters: * None * * Returned Value: * None * ****************************************************************************/ static void lesp_flush(void) { do { lesp_clear_read_buffer(); lesp_clear_read_ans(); } while (lesp_read(lespTIMEOUT_FLUSH_MS) >= 0); } /**************************************************************************** * Name: lesp_read_ans_ok * * Description: * Read up to read OK, ERROR, or FAIL. * * Input Parmeters: * timeout_ms : timeout in millisecond. * * Returned Value: * 0 on OK, -1 on error. * ****************************************************************************/ int lesp_read_ans_ok(int timeout_ms) { int ret = 0; time_t end; end = time(NULL) + (timeout_ms/1000) + lespTIMEOUT_FLOODING_OFFSET_S; while (g_lesp_state.ans != lesp_eOK) { ret = lesp_read(timeout_ms); if ((ret < 0) || (g_lesp_state.ans == lesp_eERR) || \ (time(NULL) > end)) { ret = -1; break; } ninfo("Got:%s\n", g_lesp_state.bufans); } lesp_clear_read_ans(); lesp_clear_read_buffer(); return ret; } /**************************************************************************** * Name: lesp_ask_ans_ok * * Description: * Ask and ignore line start with '+' and except a "OK" answer. * * Input Parmeters: * cmd : command sent * timeout_ms : timeout in millisecond * * Returned Value: * len of line without line return on success, -1 on error. * ****************************************************************************/ static int lesp_ask_ans_ok(int timeout_ms, FAR const IPTR char *format, ...) { int ret = 0; va_list ap; va_start(ap, format); ret = lesp_vsend_cmd(format, ap); va_end(ap); if (ret >= 0) { ret = lesp_read_ans_ok(timeout_ms); } return ret; } /**************************************************************************** * Name: lesp_check * * Description: * check if esp is ready (initialized and AT return OK) * * Input Parmeters: * None * * Returned Value: * 0 : all is ok, -1 in case of error. * ****************************************************************************/ static int lesp_check(void) { if (! g_lesp_state.is_initialized) { nerr("ERROR: ESP8266 not initialized\n"); return -1; } lesp_flush(); if (lesp_ask_ans_ok(lespTIMEOUT_MS, "AT\r\n") < 0) { nerr("ERROR: ESP8266 not answer at AT command\n"); return -1; } return 0; } /**************************************************************************** * Name: lesp_parse_cipdomain_ans_line * * Description: * Try to decode line start with: * +CIPDOMAIN: * see in: * https://room-15.github.io/blog/2015/03/26/esp8266-at-command-reference/ * or * http://www.espressif.com/sites/default/files/documentation/4a-esp8266_at_instruction_set_en.pdf * * net ip * +CIPDOMAIN:"192.168.1.1" * * Note: * - Content of ptr is modified and string in ap point into ptr string. * - in current version, only one ip is returned by ESP8266. * * Input Parmeters: * ptr : +CIPDOMAIN line null terminated string pointer. * ip : ip of hostname * * Returned Value: * 0 on success, -1 in case of error. * ****************************************************************************/ static int lesp_parse_cwdomain_ans_line(const char *ptr, in_addr_t *ip) { int field_idx; char *ptr_next; for (field_idx = 0; field_idx <= 1; field_idx++) { if (field_idx == 0) { ptr_next = strchr(ptr, ':'); } else if (field_idx == 1) { ptr_next = strchr(ptr, '\0'); } if (ptr_next == NULL) { return -1; } *ptr_next = '\0'; switch (field_idx) { case 0: if (strncmp(ptr, "+CIP", 4) != 0) { return -1; } break; case 1: /* No '"' for this command ! */ if (inet_pton(AF_INET, ptr, ip) < 0) { return -1; } break; } ptr = ptr_next + 1; } return 0; } /**************************************************************************** * Name: lesp_parse_cipxxx_ans_line * * Description: * Try to decode line start with: * +CIPAP? * +CIPAP_XXX? * +CIPSTA? * +CIPSTA_XXX? * see in: * https://room-15.github.io/blog/2015/03/26/esp8266-at-command-reference/ * or * http://www.espressif.com/sites/default/files/documentation/4a-esp8266_at_instruction_set_en.pdf * * net ip net mask * +CIPxxxx:ip:"192.168.1.1","255.255.255.0","192.168.1.1", * * Note: * - Content of ptr is modified and string in ap point into ptr string. * - in current version, only net ip is returned by ESP8266. * * Input Parmeters: * ptr : +CWLAP line null terminated string pointer. * ap : ap result of parsing. * * Returned Value: * 0 on success, -1 in case of error. * ****************************************************************************/ static int lesp_parse_cipxxx_ans_line(const char *ptr, in_addr_t *ip) { int field_idx; char *ptr_next; for (field_idx = 0; field_idx <= 2; field_idx++) { if (field_idx <= 1) { ptr_next = strchr(ptr, ':'); } else if (field_idx == 2) { ptr_next = strchr(ptr, '\0'); } else { ptr_next = strchr(ptr, ','); } if (ptr_next == NULL) { return -1; } *ptr_next = '\0'; switch (field_idx) { case 0: if (strncmp(ptr, "+CIP", 4) != 0) { return -1; } break; case 1: /* ip label */ break; case 2: ptr++; /* Remove first '"' */ *(ptr_next - 1) = '\0'; if (inet_pton(AF_INET, ptr, ip) < 0) { return -1; } break; } ptr = ptr_next + 1; } return 0; } /**************************************************************************** * Name: lesp_parse_cwjap_ans_line * * Description: * Try to decode @b +CWJAP? or +CWJAP_XXX? line. * see in: * https://room-15.github.io/blog/2015/03/26/esp8266-at-command-reference/ * or * http://www.espressif.com/sites/default/files/documentation/4a-esp8266_at_instruction_set_en.pdf * * SSID BSSID ch RSSI * +CWLAP:"FreeWifi","00:07:cb:07:b6:00", 1, -90 * or * +CWJAP_CUR:"WLACTAP","f4:f2:6d:e8:5e:74",6,-59 * * Note: Content of ptr is modified and string in ap point into ptr string. * * Input Parmeters: * ptr : +CWLAP line null terminated string pointer. * ap : ap result of parsing. * * Returned Value: * 0 on success, -1 in case of error. * ****************************************************************************/ static int lesp_parse_cwjap_ans_line(char *ptr, lesp_ap_t *ap) { int field_idx; char *ptr_next; for (field_idx = 0; field_idx <= 4; field_idx++) { if (field_idx == 0) { ptr_next = strchr(ptr, ':'); } else if (field_idx == 4) { ptr_next = strchr(ptr, '\0'); } else { ptr_next = strchr(ptr, ','); } if (ptr_next == NULL) { return -1; } *ptr_next = '\0'; switch (field_idx) { case 0: if (strncmp(ptr, "+CWJAP", 6) != 0) { return -1; } break; case 1: ptr++; /* Remove first '"' */ *(ptr_next - 1) = '\0'; strncpy(ap->ssid, ptr, lespSSID_SIZE); ap->ssid[lespSSID_SIZE] = '\0'; break; case 2: { int i; ptr++; /* Remove first '"' */ *(ptr_next - 1) = '\0'; for (i = 0; i < lespBSSID_SIZE ; i++) { ap->bssid[i] = strtol(ptr, &ptr, 16); if (*ptr == ':') { ptr++; } } } break; case 3: { int i = atoi(ptr); ap->channel = i; } break; case 4: { int i = atoi(ptr); if (i > 0) { i = -i; } ap->rssi = i; } break; } ptr = ptr_next + 1; } return 0; } /**************************************************************************** * Name: lesp_parse_cwlap_ans_line * * Description: * Try to decode @b +CWLAP line. * see in: * https://room-15.github.io/blog/2015/03/26/esp8266-at-command-reference/ * * +CWLAP:(0,"FreeWifi",-90,"00:07:cb:07:b6:00",1) * 0 => security * "FreeWifi" => ssid * -90 => rssi * "00:07:cb:07:b6:00" => mac * * Note: Content of ptr is modified and string in ap point into ptr string. * * Input Parmeters: * ptr : +CWLAP line null terminated string pointer. * ap : ap result of parsing. * * Returned Value: * 0 on success, -1 in case of error. * ****************************************************************************/ static int lesp_parse_cwlap_ans_line(char *ptr, lesp_ap_t *ap) { int field_idx; char *ptr_next; for (field_idx = 0; field_idx <= 5; field_idx++) { if (field_idx == 0) { ptr_next = strchr(ptr, '('); } else if (field_idx == 5) { ptr_next = strchr(ptr, ')'); } else { ptr_next = strchr(ptr, ','); } if (ptr_next == NULL) { return -1; } *ptr_next = '\0'; switch (field_idx) { case 0: if (strcmp(ptr, "+CWLAP:") != 0) { return -1; } break; case 1: { int i = *ptr - '0'; if ((i < 0) || (i >= lesp_eSECURITY_NBR)) { return -1; } ap->security = i; } break; case 2: ptr++; /* Remove first '"' */ *(ptr_next - 1) = '\0'; strncpy(ap->ssid, ptr, lespSSID_SIZE); ap->ssid[lespSSID_SIZE] = '\0'; break; case 3: { int i = atoi(ptr); if (i > 0) { i = -i; } ap->rssi = i; } break; case 4: { int i; ptr++; /* Remove first '"' */ *(ptr_next - 1) = '\0'; for (i = 0; i < lespBSSID_SIZE ; i++) { ap->bssid[i] = strtol(ptr, &ptr, 16); if (*ptr == ':') { ptr++; } } } break; } ptr = ptr_next + 1; } return 0; } /**************************************************************************** * Name: lesp_worker * * Description: * Esp8266 worker thread. * * Input Parmeters: * args : unused * * Returned Value: * NULL * ****************************************************************************/ static void *lesp_worker(void *args) { int ret = 0; int rxlen = 0; lesp_worker_t *worker = &g_lesp_state.worker; UNUSED(args); ninfo("worker Started \n"); while (worker->running) { uint8_t c; ret = lesp_low_level_read(&c, 1); if (ret < 0) { nerr("ERROR: worker read data Error %d\n", ret); } else if (ret > 0) { /* ninfo("c:0x%02X (%c)\n", c); */ pthread_mutex_lock(&(worker->mutex)); if (c == '\n') { if (worker->rxbuf[rxlen-1] == '\r') { rxlen--; } DEBUGASSERT(rxlen >= 0); DEBUGASSERT(rxlen < BUF_WORKER_LEN); worker->rxbuf[rxlen] = '\0'; if (rxlen != 0) { if (strcmp(worker->rxbuf, "OK") == 0) { worker->ans = lesp_eOK; } else if ((strcmp(worker->rxbuf, "FAIL") == 0) || (strcmp(worker->rxbuf, "ERROR") == 0) ) { worker->ans = lesp_eERR; } else if ((rxlen == 8) && (memcmp(worker->rxbuf+1, ",CLOSED", 7) == 0)) { unsigned int sockid = worker->rxbuf[0] - '0'; if (sockid < SOCKET_NBR) { set_sock_closed(sockid); } } else { if (worker->buf[0] != '\0') { pthread_mutex_unlock(&(worker->mutex)); usleep(100); /* leave time of aplicative to read buffer */ pthread_mutex_lock(&(worker->mutex)); } /* ninfo("Worker Read data:%s\n", worker->rxbuf); */ if (rxlen+1 <= BUF_ANS_LEN) { memcpy(worker->buf, worker->rxbuf, rxlen+1); } else { nerr("Worker ans line is too long:%s\n", worker->rxbuf); } } sem_post(&worker->sem); worker->rxbuf[0] = '\0'; rxlen = 0; } } else if (rxlen < BUF_WORKER_LEN - 1) { worker->rxbuf[rxlen++] = c; if ((c == ':') && (memcmp(worker->rxbuf, "+IPD,", 5) == 0)) { int sockfd; int len; char *ptr = worker->rxbuf+5; sockfd = lesp_str_to_unsigned(&ptr, ','); if (sockfd >= 0) { len = lesp_str_to_unsigned(&ptr, ':'); if (len >= 0) { lesp_read_ipd(sockfd, len); } } rxlen = 0; } } else { nerr("Read char overflow:%c\n", c); } pthread_mutex_unlock(&(worker->mutex)); } } return NULL; } /**************************************************************************** * Name: lesp_create_worker * * Description: * start Esp8266 worker thread. * * Input Parmeters: * priority : POSIX priority of worker thread. * * Returned Value: * 0 on success, -1 in case of error. * ****************************************************************************/ static inline int lesp_create_worker(int priority) { int ret = 0; /* Thread */ pthread_attr_t thread_attr; /* Ihm priority lower than normal */ struct sched_param param; ret = pthread_attr_init(&thread_attr); if (ret < 0) { nerr("ERROR: Cannot Set scheduler parameter thread (%d)\n", ret); } else { ret = pthread_attr_getschedparam(&thread_attr, ¶m); if (ret >= 0) { param.sched_priority += priority; ret = pthread_attr_setschedparam(&thread_attr, ¶m); } else { nerr("ERROR: Cannot Get/Set scheduler parameter thread (%d)\n", ret); } g_lesp_state.worker.running = true; ret = pthread_create(&g_lesp_state.worker.thread, (ret < 0)?NULL:&thread_attr, lesp_worker, NULL); if (ret < 0) { nerr("ERROR: Cannot Create thread return (%d)\n", ret); g_lesp_state.worker.running = false; } #if CONFIG_TASK_NAME_SIZE > 0 if (ret >= 0) { pthread_setname_np(g_lesp_state.worker.thread, "ESP8266"); } #endif if (pthread_attr_destroy(&thread_attr) < 0) { nerr("ERROR: Cannot destroy thread attribute (%d)\n", ret); } } return 0; } /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: lesp_initialize * * Description: * initialise Esp8266 class. * - open port * - configure port * - etc... * * Input Parmeters: * None * * Returned Value: * 0 on success, -1 in case of error. * ****************************************************************************/ int lesp_initialize(void) { int ret = 0; pthread_mutex_lock(&g_lesp_state.mutex); if (g_lesp_state.is_initialized) { pthread_mutex_unlock(&g_lesp_state.mutex); ninfo("Esp8266 already initialized\n"); return 0; } pthread_mutex_lock(&g_lesp_state.worker.mutex); ninfo("Initializing Esp8266...\n"); memset(g_lesp_state.sockets, 0, SOCKET_NBR * sizeof(lesp_socket_t)); if (sem_init(&g_lesp_state.worker.sem, 0, 0) < 0) { ninfo("Cannot create semaphore\n"); ret = -1; } if (ret >= 0 && g_lesp_state.fd < 0) { g_lesp_state.fd = open(CONFIG_NETUTILS_ESP8266_DEV_PATH, O_RDWR); } if (ret >= 0 && g_lesp_state.fd < 0) { nerr("ERROR: Cannot open %s\n", CONFIG_NETUTILS_ESP8266_DEV_PATH); ret = -1; } #ifdef CONFIG_SERIAL_TERMIOS if (ret >= 0 && lesp_set_baudrate(CONFIG_NETUTILS_ESP8266_BAUDRATE) < 0) { nerr("ERROR: Cannot set baud rate %d\n", CONFIG_NETUTILS_ESP8266_BAUDRATE); ret = -1; } #endif if ((ret >= 0) && (g_lesp_state.worker.running == false)) { ret = lesp_create_worker(CONFIG_NETUTILS_ESP8266_THREADPRIO); } if (ret < 0) { ninfo("Esp8266 initialisation failed!\n"); ret = -1; } else { g_lesp_state.is_initialized = true; ninfo("Esp8266 initialized\n"); } pthread_mutex_unlock(&g_lesp_state.worker.mutex); pthread_mutex_unlock(&g_lesp_state.mutex); return 0; } /**************************************************************************** * Name: lesp_soft_reset * * Description: * reset esp8266 (command "AT+RST"); * * Input Parmeters: * None * * Returned Value: * 0 on success, -1 in case of error. * ****************************************************************************/ int lesp_soft_reset(void) { int ret = 0; int i; pthread_mutex_lock(&g_lesp_state.mutex); /* Rry to close opened reset */ pthread_mutex_lock(&g_lesp_state.worker.mutex); for (i = 0; i < SOCKET_NBR; i++) { if ((g_lesp_state.sockets[i].flags & FLAGS_SOCK_USED) != 0) { set_sock_closed(i); } } pthread_mutex_unlock(&g_lesp_state.worker.mutex); /* Leave time to close socket */ sleep(1); /* Send reset */ lesp_send_cmd("AT+RST\r\n"); /* Leave time to reset */ sleep(1); lesp_flush(); while (lesp_ask_ans_ok(lespTIMEOUT_MS, "ATE0\r\n") < 0) { sleep(1); lesp_flush(); } if (ret >= 0) { ret = lesp_ask_ans_ok(lespTIMEOUT_MS, "AT+GMR\r\n"); } /* Enable the module to act as a “Station” */ if (ret >= 0) { ret = lesp_ask_ans_ok(lespTIMEOUT_MS, "AT+CWMODE_CUR=1\r\n"); } /* Enable the multi connection */ if (ret >= 0) { ret = lesp_ask_ans_ok(lespTIMEOUT_MS, "AT+CIPMUX=1\r\n"); } if (ret < 0) { ret = -1; } pthread_mutex_unlock(&g_lesp_state.mutex); return 0; } /**************************************************************************** * Name: lesp_soft_reset * * Description: * reset esp8266 (command "AT+RST"); * * Input Parmeters: * None * * Returned Value: * 0 on success, -1 in case of error. * ****************************************************************************/ int lesp_ap_connect(const char *ssid_name, const char *ap_key, int timeout_s) { int ret = 0; ninfo("Starting manual connect...\n"); pthread_mutex_lock(&g_lesp_state.mutex); ret = lesp_check(); if (ret >= 0) { ret = lesp_ask_ans_ok(timeout_s*1000, "AT+CWJAP=\"%s\",\"%s\"\r\n", ssid_name, ap_key); } pthread_mutex_unlock(&g_lesp_state.mutex); if (ret < 0) { return -1; } ninfo("Wifi connected\n"); return 0; } /**************************************************************************** * Name: lesp_ap_get * * Description: * Read the current connected Access Point. * * Input Parmeters: * None * * Output Parmeters: * ap : details of connected acces point * * Returned Value: * 0 on success, -1 in case of error. * ****************************************************************************/ int lesp_ap_get(lesp_ap_t *ap) { int ret = 0; ninfo("Get Access Point info...\n"); pthread_mutex_lock(&g_lesp_state.mutex); ret = lesp_check(); if (ret >= 0) { ret = lesp_send_cmd("AT+CWJAP_CUR?\r\n"); } if (ret >= 0) { ret = lesp_read(lespTIMEOUT_MS); } if (ret >= 0) { ninfo("Read:%s\n", g_lesp_state.bufans); ret = lesp_parse_cwjap_ans_line(g_lesp_state.bufans, ap); if (ret < 0) { nerr("ERROR: Line badly formed.\n"); } } if (ret >= 0) { ret = lesp_read_ans_ok(lespTIMEOUT_MS); } pthread_mutex_unlock(&g_lesp_state.mutex); if (ret < 0) { nerr("ERROR: Get access point information.\n"); return -1; } ninfo("Connected to %s\n", ap->ssid); return 0; } /**************************************************************************** * Name: lesp_get_net * * Description: * Read the current network details. * * Input Parmeters: * mode : lesp_eMODE_AP or lesp_eMODE_STATION * * Output Parmeters: * ip : ip of interface * mask : mask of interface (not implemented by esp8266) * gw : gateway of interface (not implemented by esp8266) * * Returned Value: * 0 on success, -1 in case of error. * ****************************************************************************/ int lesp_get_net(lesp_mode_t mode, in_addr_t *ip, in_addr_t *mask, in_addr_t *gw) { int ret = 0; ninfo("Get IP info...\n"); pthread_mutex_lock(&g_lesp_state.mutex); ret = lesp_check(); if (ret >= 0) { ret = lesp_send_cmd("AT+CIP%s_CUR?\r\n", (mode == lesp_eMODE_STATION)?"STA":"AP"); } if (ret >= 0) { ret = lesp_read(lespTIMEOUT_MS); } if (ret >= 0) { ninfo("Read:%s\n", g_lesp_state.bufans); ret = lesp_parse_cipxxx_ans_line(g_lesp_state.bufans, ip); if (ret < 0) { nerr("ERROR: Line badly formed.\n"); } } if (ret >= 0) { ret = lesp_read(lespTIMEOUT_MS); } if (ret >= 0) { ninfo("Read:%s\n", g_lesp_state.bufans); ret = lesp_parse_cipxxx_ans_line(g_lesp_state.bufans, mask); if (ret < 0) { nerr("ERROR: Line badly formed.\n"); } } if (ret >= 0) { ret = lesp_read(lespTIMEOUT_MS); } if (ret >= 0) { ninfo("Read:%s\n", g_lesp_state.bufans); ret = lesp_parse_cipxxx_ans_line(g_lesp_state.bufans, mask); if (ret < 0) { nerr("ERROR: Line badly formed.\n"); } } if (ret >= 0) { ret = lesp_read_ans_ok(lespTIMEOUT_MS); } pthread_mutex_unlock(&g_lesp_state.mutex); if (ret < 0) { nerr("ERROR: Get network information.\n"); return -1; } return 0; } /**************************************************************************** * Name: lesp_set_net * * Description: * It will set network ip of mode. * Warning: use lesp_eMODE_STATION or lesp_eMODE_AP. * * Input Parmeters: * mode : mode to configure. * ip : ip of interface. * mask : network mask of interface. * gateway : gateway ip of network. * * Returned Value: * 0 on success, -1 on error. * ****************************************************************************/ int lesp_set_net(lesp_mode_t mode, in_addr_t ip, in_addr_t mask, in_addr_t gateway) { int ret = 0; pthread_mutex_lock(&g_lesp_state.mutex); ret = lesp_check(); if (ret >= 0) { ret = lesp_ask_ans_ok(lespTIMEOUT_MS, "AT+CIP%s_CUR=\"%d.%d.%d.%d\"," "\"%d.%d.%d.%d\",\"%d.%d.%d.%d\"\r\n", (mode == lesp_eMODE_STATION)?"STA":"AP", *((uint8_t *)&(ip)+0), *((uint8_t *)&(ip)+1), *((uint8_t *)&(ip)+2), *((uint8_t *)&(ip)+3), *((uint8_t *)&(gateway)+0), *((uint8_t *)&(gateway)+1), *((uint8_t *)&(gateway)+2), *((uint8_t *)&(gateway)+3), *((uint8_t *)&(mask)+0), *((uint8_t *)&(mask)+1), *((uint8_t *)&(mask)+2), *((uint8_t *)&(mask)+3)); } pthread_mutex_unlock(&g_lesp_state.mutex); if (ret < 0) { return -1; } return 0; } /**************************************************************************** * Name: lesp_set_dhcp * * Description: * It will Enable or disable DHCP of mode. * * Input Parmeters: * mode : mode to configure. * enable : true for enable, false for disable. * * Returned Value: * 0 on success, -1 on error. * ****************************************************************************/ int lesp_set_dhcp(lesp_mode_t mode, bool enable) { int ret = 0; pthread_mutex_lock(&g_lesp_state.mutex); ret = lesp_check(); if (ret >= 0) { ret = lesp_ask_ans_ok(lespTIMEOUT_MS, "AT+CWDHCP_CUR=%d,%c\r\n", mode, (enable)?'1':'0'); } pthread_mutex_unlock(&g_lesp_state.mutex); if (ret < 0) { return -1; } return 0; } /**************************************************************************** * Name: lesp_get_dhcp * * Description: * It will get if DHCP is Enable or disable for each mode. * * Input Parmeters: * ap_enable : true DHCP is enable in Access Point mode, false otherwise. * sta_enable : true DHCP is enable in Station mode, false otherwise. * * Returned Value: * 0 on success, -1 on error. * ****************************************************************************/ int lesp_get_dhcp(bool *ap_enable, bool *sta_enable) { int ret = 0; pthread_mutex_lock(&g_lesp_state.mutex); ninfo("Get DHCP State...\n"); ret = lesp_check(); if (ret >= 0) { ret = lesp_send_cmd("AT+CWDHCP_CUR?\r\n"); } if (ret >= 0) { ret = lesp_read(lespTIMEOUT_MS); } if (ret >= 0) { char *ptr = g_lesp_state.bufans; ninfo("Read:%s\n", ptr); ptr = strchr(ptr, ':'); ptr++; switch (*ptr - '0') { case 0: *ap_enable = 0; *sta_enable = 0; break; case 1: *ap_enable = 1; *sta_enable = 0; break; case 2: *ap_enable = 0; *sta_enable = 1; break; case 3: *ap_enable = 1; *sta_enable = 1; break; default: nerr("ERROR: Line badly formed.\n"); break; } } if (ret >= 0) { ret = lesp_read_ans_ok(lespTIMEOUT_MS); } pthread_mutex_unlock(&g_lesp_state.mutex); if (ret < 0) { nerr("ERROR: Get DHCP.\n"); return -1; } return 0; } /**************************************************************************** * Name: lesp_list_access_points * * Description: * Search all access points. * * Input Parmeters: * cb : call back call for each access point found. * * Returned Value: * Number access point(s) found on success, -1 on error. * ****************************************************************************/ int lesp_list_access_points(lesp_cb_t cb) { lesp_ap_t ap; int ret = 0; int number = 0; pthread_mutex_lock(&g_lesp_state.mutex); ninfo("List access point(s)...\n"); ret = lesp_check(); if (ret >= 0) { ret = lesp_ask_ans_ok(lespTIMEOUT_MS, "AT\r\n"); } if (ret >= 0) { ret = lesp_send_cmd("AT+CWLAP\r\n"); } while (ret >= 0) { ret = lesp_read(lespTIMEOUT_MS_LISP_AP); if (ret < 0) { continue; } ninfo("Read:%s\n", g_lesp_state.bufans); if (strcmp(g_lesp_state.bufans, "OK") == 0) { break; } ret = lesp_parse_cwlap_ans_line(g_lesp_state.bufans, &ap); if (ret < 0) { nerr("ERROR: Line badly formed."); } cb(&ap); number++; } pthread_mutex_unlock(&g_lesp_state.mutex); if (ret < 0) { nerr("ERROR: list access points."); return -1; } ninfo("Access Point list finished with %d ap founds\n", number); return number; } /**************************************************************************** * Name: lesp_security_to_str * * Description: * return corresponding string of security enum. * * Input Parmeters: * security : enum value of string * * Returned Value: * String corresponding to security value. * ****************************************************************************/ const char *lesp_security_to_str(lesp_security_t security) { switch (security) { case lesp_eSECURITY_NONE: return "NONE"; case lesp_eSECURITY_WEP: return "WEP"; case lesp_eSECURITY_WPA_PSK: return "WPA_PSK"; case lesp_eSECURITY_WPA2_PSK: return "WPA2_PSK"; case lesp_eSECURITY_WPA_WPA2_PSK: return "WPA_WPA2_PSK"; default: return "Unknown"; } } /**************************************************************************** * Name: lesp_socket * * Description: * Equivalent of POSIX socket for esp8266. * socket() creates an endpoint for communication and returns a descriptor. * * Input Parameters: * domain : only PF_INET is supported * type : only SOCK_STREAM is supported * protocol : only IPPROTO_TCP is supported * * Returned Value: * A non-negative socket descriptor on success; -1 on error. * ****************************************************************************/ int lesp_socket(int domain, int type, int protocol) { int ret = 0; int i = CON_NBR; int flags = 0; if (domain != PF_INET) { nerr("ERROR: only PF_INET Implemented!\n"); return -1; } switch (type) { case SOCK_STREAM: flags |= FLAGS_SOCK_TYPE_TCP; break; case SOCK_DGRAM: flags |= FLAGS_SOCK_TYPE_UDP; break; case -1: flags |= FLAGS_SOCK_TYPE_SSL; break; default: nerr("ERROR: Only SOCK_DGRAM and SOCK_STREAM Implemented!\n"); errno = ESOCKTNOSUPPORT; return -1; } flags |= FLAGS_SOCK_USED; pthread_mutex_lock(&g_lesp_state.worker.mutex); ret = -1; if (!g_lesp_state.is_initialized) { ninfo("Esp8266 not initialized; can't list access points\n"); errno = ENETDOWN; } else { for (i = 0; i < CON_NBR; i++) { if ((g_lesp_state.sockets[i].flags & FLAGS_SOCK_USED) == 0) { g_lesp_state.sockets[i].flags = flags; g_lesp_state.sockets[i].rcv_timeo.tv_sec = lespTIMEOUT_MS_RECV_S; g_lesp_state.sockets[i].rcv_timeo.tv_nsec = 0; ret = i; break; } } if (ret < 0) { errno = EAGAIN; } } pthread_mutex_unlock(&g_lesp_state.worker.mutex); return ret; } /**************************************************************************** * Name: lesp_closesocket * * Description: * Equivalent of POSIX close for esp8266. * close socket creates with lesp_socket. * * Input Parameters: * sockfd : socket indentifer. * * Returned Value: * A 0 on success; -1 on error. * ****************************************************************************/ int lesp_closesocket(int sockfd) { int ret = 0; lesp_socket_t *sock = NULL; pthread_mutex_lock(&g_lesp_state.mutex); ninfo("List access point(s)...\n"); ret = lesp_check(); if (ret >= 0) { sock = get_sock_protected(sockfd); if (sock == NULL) { ret = -1; } } if (ret >= 0) { ret = lesp_ask_ans_ok(lespTIMEOUT_MS, "AT+CIPCLOSE=%d\r\n", sockfd); pthread_mutex_lock(&g_lesp_state.worker.mutex); set_sock_closed(sockfd); pthread_mutex_unlock(&g_lesp_state.worker.mutex); } pthread_mutex_unlock(&g_lesp_state.mutex); if (ret < 0) { nerr("ERROR: Close socket %d.\n", sockfd); return -1; } return 0; } /**************************************************************************** * Name: lesp_bind * * Description: * Equivalent of POSIX bind for esp8266. * * Input Parameters: * sockfd Socket descriptor of the socket to bind * addr Socket local address * addrlen Length of 'addr' * * Returned Value: * A 0 on success; -1 on error. * ****************************************************************************/ int lesp_bind(int sockfd, FAR const struct sockaddr *addr, socklen_t addrlen) { int ret = 0; pthread_mutex_lock(&g_lesp_state.mutex); ninfo("Bind socket %d...\n", sockfd); ret = lesp_check(); if (ret >= 0) { nerr("ERROR: Not implemented %s\n", __func__); errno = EIO; ret = -1; } pthread_mutex_unlock(&g_lesp_state.mutex); return ret; } /**************************************************************************** * Name: lesp_connect * * Description: * Equivalent of POSIX connect for esp8266. * * Input Parameters: * sockfd Socket descriptor returned by socket() * addr Server address (form depends on type of socket) * addrlen Length of actual 'addr' * * Returned Value: * A 0 on success; -1 on error. * ****************************************************************************/ int lesp_connect(int sockfd, FAR const struct sockaddr *addr, socklen_t addrlen) { int ret = 0; const char *proto_str; lesp_socket_t *sock; struct sockaddr_in *in; unsigned short port; in_addr_t ip; in = (struct sockaddr_in *)addr; port = ntohs(in->sin_port); /* e.g. htons(3490) */ ip = in->sin_addr.s_addr; DEBUGASSERT(in->sin_family == AF_INET); DEBUGASSERT(addrlen == sizeof(struct sockaddr_in)); pthread_mutex_lock(&g_lesp_state.mutex); ninfo("Connect %d...\n", sockfd); ret = lesp_check(); if (ret >= 0) { pthread_mutex_lock(&g_lesp_state.worker.mutex); sock = get_sock(sockfd); if (sock == NULL) { ret = -1; } } if (ret >= 0) { switch (FLAGS_SOCK_TYPE_MASK & sock->flags) { case FLAGS_SOCK_TYPE_TCP: proto_str = "TCP"; break; case FLAGS_SOCK_TYPE_UDP: proto_str = "UDP"; break; case FLAGS_SOCK_TYPE_SSL: proto_str = "SSL"; break; default: errno = ESOCKTNOSUPPORT; ret = -1; } } pthread_mutex_unlock(&g_lesp_state.worker.mutex); if (ret >= 0) { ret = lesp_ask_ans_ok(lespTIMEOUT_MS, "AT+CIPSTART=%d,\"%s\"," "\"%d.%d.%d.%d\",%d\r\n", sockfd, proto_str, *((uint8_t *)&(ip)+0), *((uint8_t *)&(ip)+1), *((uint8_t *)&(ip)+2), *((uint8_t *)&(ip)+3), port); if (ret < 0) { errno = EIO; } } pthread_mutex_unlock(&g_lesp_state.mutex); if (ret < 0) { nerr("ERROR: Connect socket %d.\n", sockfd); return -1; } return 0; } /**************************************************************************** * Name: lesp_listen * * Description: * Equivalent of POSIX listen for esp8266. * * Input Parameters: * sockfd Socket descriptor returned by socket() * backlog The maximum length the queue of pending connections may grow. * If a connection request arrives with the queue full, the client * may receive an error with an indication of ECONNREFUSED or, * if the underlying protocol supports retransmission, the request * may be ignored so that retries succeed. * * Returned Value: * A 0 on success; -1 on error. * ****************************************************************************/ int lesp_listen(int sockfd, int backlog) { int ret = 0; pthread_mutex_lock(&g_lesp_state.mutex); ninfo("Connect %d...\n", sockfd); ret = lesp_check(); if (ret >= 0) { nerr("ERROR: Not implemented %s\n", __func__); errno = EIO; ret = -1; } pthread_mutex_unlock(&g_lesp_state.mutex); return ret; } /**************************************************************************** * Name: lesp_accept * * Description: * Equivalent of POSIX accept for esp8266. * * Input Parameters: * sockfd Socket descriptor returned by socket() * addr Receives the address of the connecting client * addrlen Input: allocated size of 'addr', Return: returned size of 'addr' * * Returned Value: * A 0 on success; -1 on error. * ****************************************************************************/ int lesp_accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) { int ret = 0; pthread_mutex_lock(&g_lesp_state.mutex); ninfo("Connect %d...\n", sockfd); ret = lesp_check(); if (ret >= 0) { nerr("ERROR: Not implemented %s\n", __func__); errno = EIO; ret = -1; } pthread_mutex_unlock(&g_lesp_state.mutex); return ret; } /**************************************************************************** * Name: lesp_send * * Description: * Equivalent of POSIX send for esp8266. * * Input Parameters: * sockfd Socket descriptor returned by socket() * buf Data to send * len Length of data to send * flags Send flags * * Returned Value: * A 0 on success; -1 on error. * ****************************************************************************/ ssize_t lesp_send(int sockfd, FAR const uint8_t *buf, size_t len, int flags) { int ret = 0; lesp_socket_t *sock = NULL; UNUSED(flags); pthread_mutex_lock(&g_lesp_state.mutex); ninfo("Send %d bytes in %d socket...\n", len, sockfd); ret = lesp_check(); if (ret >= 0) { sock = get_sock_protected(sockfd); if (sock == NULL) { ret = -1; } } if (ret >= 0) { ret = lesp_ask_ans_ok(lespTIMEOUT_MS, "AT+CIPSEND=%d,%d\r\n", sockfd, len); } if (ret >= 0) { ninfo("Sending in socket %d, %d bytes\n", sockfd, len); ret = write(g_lesp_state.fd, buf, len); } while (ret >= 0) { char * ptr = g_lesp_state.bufans; ret = lesp_read(lespTIMEOUT_MS); if (ret < 0) { break; } while ((*ptr != 0) && (*ptr != 'S')) { ptr++; } if (*ptr == 'S') { if (strcmp(ptr, "SEND OK") == 0) break; } } pthread_mutex_unlock(&g_lesp_state.mutex); if (ret < 0) { nerr("ERROR: Cannot send in socket %d, %d bytes\n", sockfd, len); return -1; } ninfo("Sent\n"); return len; } /**************************************************************************** * Name: lesp_recv * * Description: * Equivalent of POSIX recv for esp8266. * * Input Parameters: * sockfd Socket descriptor returned by socket() * buf Data to receive * len Length of data to receive * flags Send flags * * Returned Value: * A 0 on success; -1 on error. * ****************************************************************************/ ssize_t lesp_recv(int sockfd, FAR uint8_t *buf, size_t len, int flags) { int ret = 0; lesp_socket_t *sock; sem_t sem; if (sem_init(&sem, 0, 0) < 0) { ninfo("Cannot create semaphore\n"); return -1; } pthread_mutex_lock(&g_lesp_state.worker.mutex); UNUSED(flags); sock = get_sock(sockfd); if (sock == NULL) { ret = -1; } if (ret >= 0 && sock->inndx == sock->outndx) { struct timespec ts; if (clock_gettime(CLOCK_REALTIME, &ts) < 0) { ret = -1; } else { ts.tv_sec += sock->rcv_timeo.tv_sec; ts.tv_nsec += sock->rcv_timeo.tv_nsec; if (ts.tv_nsec >= 1000000000) { ts.tv_sec++; ts.tv_nsec -= 1000000000; } sock->sem = &sem; while (ret >= 0 && sock->inndx == sock->outndx) { pthread_mutex_unlock(&g_lesp_state.worker.mutex); ret = sem_timedwait(&sem, &ts); pthread_mutex_lock(&g_lesp_state.worker.mutex); } sock->sem = NULL; } } if (ret >= 0) { ret = 0; while (ret < len && sock->outndx != sock->inndx) { /* Remove one byte from the circular buffer */ int ndx = sock->outndx; *buf++ = sock->rxbuf[ndx]; /* Increment the circular buffer 'outndx' */ if (++ndx >= SOCKET_FIFO_SIZE) { ndx -= SOCKET_FIFO_SIZE; } sock->outndx = ndx; /* Increment the count of bytes returned */ ret++; } } pthread_mutex_unlock(&g_lesp_state.worker.mutex); if (ret < 0) { return -1; } return ret; } /**************************************************************************** * Name: lesp_setsockopt * * Description: * Equivalent of POSIX setsockopt for esp8266. * * Input Parameters: * sockfd Socket descriptor returned by socket() * level Protocol level to set the option * option identifies the option to set * value Points to the argument value * value_len The length of the argument value * * Returned Value: * A 0 on success; -1 on error. * ****************************************************************************/ int lesp_setsockopt(int sockfd, int level, int option, FAR const void *value, socklen_t value_len) { int ret = 0; lesp_socket_t *sock; if (level != SOL_SOCKET) { nerr("ERROR: %s:Not implemented level:%d\n", __func__, level); errno = EINVAL; return -1; } pthread_mutex_lock(&g_lesp_state.worker.mutex); sock = get_sock(sockfd); if (sock == NULL) { ret = -1; } else { switch (option) { case SO_RCVTIMEO: if (value_len == sizeof(struct timeval)) { sock->rcv_timeo.tv_sec = ((struct timeval *)(value))->tv_sec; sock->rcv_timeo.tv_nsec = ((struct timeval *)(value))->tv_usec; sock->rcv_timeo.tv_nsec *= 1000; /* tv_usec to tv_nsec */ } else { nerr("ERROR: Wrong size:%d\n", level); errno = EINVAL; ret = -1; } break; default: nerr("ERROR: Not implemented %s\n", __func__); errno = EINVAL; ret = -1; break; } } pthread_mutex_unlock(&g_lesp_state.worker.mutex); return ret; } /**************************************************************************** * Name: lesp_getsockopt * * Description: * Equivalent of POSIX getsockopt for esp8266. * * Input Parameters: * sockfd Socket descriptor returned by socket() * level Protocol level to set the option * option identifies the option to set * value Points to the argument value * value_len The length of the argument value * * Returned Value: * A 0 on success; -1 on error. * ****************************************************************************/ int lesp_getsockopt(int sockfd, int level, int option, FAR void *value, FAR socklen_t *value_len) { int ret = 0; pthread_mutex_lock(&g_lesp_state.mutex); ninfo("getsockopt on %d socket...\n", sockfd); ret = lesp_check(); if (ret >= 0) { nerr("ERROR: Not implemented %s\n", __func__); errno = EIO; ret = -1; } pthread_mutex_unlock(&g_lesp_state.mutex); return ret; } /**************************************************************************** * Name: lesp_gethostbyname * * Description: * Equivalent of POSIX gethostbyname for esp8266. * * Input Parameters: * name - The name of the host to find. * * Returned Value: * Upon successful completion, this function will return a pointer to a * hostent structure if the requested entry was found, and a null pointer * if the end of the database was reached or the requested entry was not * found. * * Upon unsuccessful completion, gethostbyname() return NULL. * ****************************************************************************/ FAR struct hostent *lesp_gethostbyname(FAR const char *hostname) { int ret = 0; memset(&g_lesp_state.hostent, 0, sizeof(g_lesp_state.hostent)); g_lesp_state.hostent.h_addr_list = (char**)&g_lesp_state.h_addr_list_buf; g_lesp_state.hostent.h_addrtype = AF_INET; g_lesp_state.hostent.h_length = sizeof(struct sockaddr_in); g_lesp_state.h_addr_list_buf[0] = &g_lesp_state.in_addr; g_lesp_state.h_addr_list_buf[1] = NULL; pthread_mutex_lock(&g_lesp_state.mutex); ninfo("Get host by name '%s' ...\n", hostname); ret = lesp_check(); if (ret >= 0) { ret = lesp_send_cmd("AT+CIPDOMAIN=\"%s\"\r\n", hostname); } if (ret >= 0) { ret = lesp_read(lespTIMEOUT_MS); } if (ret >= 0) { ninfo("Read:%s\n", g_lesp_state.bufans); ret = lesp_parse_cwdomain_ans_line(g_lesp_state.bufans, &g_lesp_state.in_addr); if (ret < 0) { nerr("ERROR: Line badly formed.\n"); errno = EIO; } } if (ret >= 0) { ret = lesp_read_ans_ok(lespTIMEOUT_MS); } pthread_mutex_unlock(&g_lesp_state.mutex); if (ret < 0) { nerr("ERROR: Get host by name.\n"); return NULL; } return &g_lesp_state.hostent; } #endif /* CONFIG_NETUTILS_ESP8266 */