From 8e29337c5e0a818f39c36af6cb5c63a904dc4025 Mon Sep 17 00:00:00 2001 From: Mangesh Malusare Date: Tue, 24 Aug 2021 01:25:05 +0530 Subject: [PATCH 01/40] Fix for Tx queue timeout issue [Root cause] -- tx_pending counter was going out of sync and due to which the network queue processing was getting blocked. -- This eventually triggers watchdog in network stack [Fix] -- Make tx_pending variable atomic. This ensures correct updation of the counter. -- Reduce the Max tx pending count as below: SPI interface: 100 packets SDIO interface: 200 packets -- Change the resume threshold count as below. This is to ensure that preference is given to emptying backlog SPI interface: 20 packets SDIO interface: 40 packets [Testing] -- Overnight iperf Tx test on SPI interface -- Overnight iperf Tx test on SDIO interface (pending) Signed-off-by: Mangesh Malusare --- host/linux/host_driver/esp32/sdio/esp_sdio.c | 12 ++++--- host/linux/host_driver/esp32/spi/esp_spi.c | 33 +++++++++++--------- 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/host/linux/host_driver/esp32/sdio/esp_sdio.c b/host/linux/host_driver/esp32/sdio/esp_sdio.c index 8768e6529c..184cfdbd92 100644 --- a/host/linux/host_driver/esp32/sdio/esp_sdio.c +++ b/host/linux/host_driver/esp32/sdio/esp_sdio.c @@ -34,8 +34,8 @@ #include #define MAX_WRITE_RETRIES 2 -#define TX_MAX_PENDING_COUNT 700 -#define TX_RESUME_THRESHOLD (TX_MAX_PENDING_COUNT - (TX_MAX_PENDING_COUNT/5)) +#define TX_MAX_PENDING_COUNT 200 +#define TX_RESUME_THRESHOLD (TX_MAX_PENDING_COUNT/5) #define CHECK_SDIO_RW_ERROR(ret) do { \ if (ret) \ @@ -513,8 +513,12 @@ static int tx_process(void *data) continue; } - atomic_dec(&queue_items); - atomic_dec(&tx_pending); + if (atomic_read(&queue_items)) + atomic_dec(&queue_items); + + if (atomic_read(&tx_pending)) + atomic_dec(&tx_pending); + retry = MAX_WRITE_RETRIES; /* resume network tx queue if bearable load */ diff --git a/host/linux/host_driver/esp32/spi/esp_spi.c b/host/linux/host_driver/esp32/spi/esp_spi.c index 3a9bedf393..be915a5102 100644 --- a/host/linux/host_driver/esp32/spi/esp_spi.c +++ b/host/linux/host_driver/esp32/spi/esp_spi.c @@ -29,8 +29,8 @@ #define SPI_INITIAL_CLK_MHZ 10 #define NUMBER_1M 1000000 -#define TX_MAX_PENDING_COUNT 500 -#define TX_RESUME_THRESHOLD (TX_MAX_PENDING_COUNT - (TX_MAX_PENDING_COUNT/5)) +#define TX_MAX_PENDING_COUNT 100 +#define TX_RESUME_THRESHOLD (TX_MAX_PENDING_COUNT/5) /* ESP in sdkconfig has CONFIG_IDF_FIRMWARE_CHIP_ID entry. * supported values of CONFIG_IDF_FIRMWARE_CHIP_ID are - */ @@ -47,7 +47,7 @@ static void adjust_spi_clock(u8 spi_clk_mhz); volatile u8 data_path = 0; static struct esp_spi_context spi_context; static char hardware_type = 0; -static u32 tx_pending = 0; +static atomic_t tx_pending; static struct esp_if_ops if_ops = { .read = read_packet, @@ -58,7 +58,7 @@ static DEFINE_MUTEX(spi_lock); static void open_data_path(void) { - tx_pending = 0; + atomic_set(&tx_pending, 0); msleep(200); data_path = OPEN_DATAPATH; } @@ -135,16 +135,16 @@ static int write_packet(struct esp_adapter *adapter, struct sk_buff *skb) return -EPERM; } - if (tx_pending >= TX_MAX_PENDING_COUNT) { + if (atomic_read(&tx_pending) >= TX_MAX_PENDING_COUNT) { esp_tx_pause(); dev_kfree_skb(skb); + queue_work(spi_context.spi_workqueue, &spi_context.spi_work); return -EBUSY; } /* Enqueue SKB in tx_q */ skb_queue_tail(&spi_context.tx_q, skb); - - tx_pending++; + atomic_inc(&tx_pending); if (spi_context.spi_workqueue) queue_work(spi_context.spi_workqueue, &spi_context.spi_work); @@ -295,17 +295,25 @@ static void esp_spi_work(struct work_struct *work) struct sk_buff *tx_skb = NULL, *rx_skb = NULL; u8 *rx_buf; int ret = 0; - int trans_ready, rx_pending; + volatile int trans_ready, rx_pending; mutex_lock(&spi_lock); trans_ready = gpio_get_value(HANDSHAKE_PIN); rx_pending = gpio_get_value(SPI_DATA_READY_PIN); - if (trans_ready) { - if (data_path) + if (data_path) { tx_skb = skb_dequeue(&spi_context.tx_q); + if (tx_skb) { + if (atomic_read(&tx_pending)) + atomic_dec(&tx_pending); + + if (atomic_read(&tx_pending) < TX_RESUME_THRESHOLD) { + esp_tx_resume(); + } + } + } if (rx_pending || tx_skb) { memset(&trans, 0, sizeof(trans)); @@ -322,11 +330,6 @@ static void esp_spi_work(struct work_struct *work) /* Configure TX buffer if available */ if (tx_skb) { - tx_pending--; - - if (tx_pending < TX_RESUME_THRESHOLD) - esp_tx_resume(); - trans.tx_buf = tx_skb->data; } else { tx_skb = esp_alloc_skb(SPI_BUF_SIZE); From c4767da6c10dad1c637644dabe6c507c2f4eed6a Mon Sep 17 00:00:00 2001 From: "ajita.chavan" Date: Wed, 25 Aug 2021 07:56:57 +0100 Subject: [PATCH 02/40] Return error code when transport write function fails [Root cause] -- In esp_serial_write function doesn't return error code when transport write function fails -- It returns success even if transport doesnt send data to slave [Fix] -- Error code returned in esp_serial_write function -- Added error handling in transport_pserial.py for write function fail [testing] -- Large data send in control path commands, esp_serial_write returns error code Signed-off-by: ajita.chavan --- .../host_control/python_support/commands.py | 54 +++++++++---------- .../transport/transport_pserial.py | 11 +++- host/linux/host_driver/esp32/esp_serial.c | 4 +- 3 files changed, 39 insertions(+), 30 deletions(-) diff --git a/host/linux/host_control/python_support/commands.py b/host/linux/host_control/python_support/commands.py index 038481022e..9a8770578d 100644 --- a/host/linux/host_control/python_support/commands.py +++ b/host/linux/host_control/python_support/commands.py @@ -82,10 +82,10 @@ def wifi_get_mac(mode): protodata = get_mac.SerializeToString() tp = Transport_pserial(interface) response = tp.send_data(endpoint,protodata) - get_mac.ParseFromString(response[1]) del tp - if response[0] != success: + if response[0] != success and response[0] != None: return failure_str + get_mac.ParseFromString(response[1]) if get_mac.resp_get_mac_address.resp != success: return failure_str else: @@ -105,10 +105,10 @@ def wifi_get_mode(): protodata = get_mode.SerializeToString() tp = Transport_pserial(interface) response = tp.send_data(endpoint,protodata) - get_mode.ParseFromString(response[1]) del tp - if response[0] != success : + if response[0] != success and response[0] != None: return failure_str + get_mode.ParseFromString(response[1]) if get_mode.resp_get_wifi_mode.resp != success: return failure_str return get_mode.resp_get_wifi_mode.mode @@ -133,10 +133,10 @@ def wifi_set_mode(mode): protodata = set_mode.SerializeToString() tp = Transport_pserial(interface) response = tp.send_data(endpoint,protodata) - set_mode.ParseFromString(response[1]) del tp - if response[0] != success : + if response[0] != success and response[0] != None: return failure_str + set_mode.ParseFromString(response[1]) if set_mode.resp_set_wifi_mode.resp != success: return failure_str else: @@ -182,10 +182,10 @@ def wifi_set_ap_config(ssid, pwd, bssid, is_wpa3_supported, listen_interval): protodata = set_ap_config.SerializeToString() tp = Transport_pserial(interface) response = tp.send_data(endpoint,protodata) - set_ap_config.ParseFromString(response[1]) del tp - if response[0] != success : + if response[0] != success and response[0] != None: return failure_str + set_ap_config.ParseFromString(response[1]) if set_ap_config.resp_set_ap_config.resp == EspHostedStatus.TYPE_CONNECTION_FAIL: print("Invalid password entered") return invalid_password_str @@ -222,10 +222,10 @@ def wifi_get_ap_config(): protodata = get_ap_config.SerializeToString() tp = Transport_pserial(interface) response = tp.send_data(endpoint,protodata) - get_ap_config.ParseFromString(response[1]) del tp - if response[0] != success : + if response[0] != success and response[0] != None: return failure_str + get_ap_config.ParseFromString(response[1]) if get_ap_config.resp_get_ap_config.resp == EspHostedStatus.TYPE_NOT_CONNECTED: return not_connected_str elif get_ap_config.resp_get_ap_config.resp != success: @@ -247,10 +247,10 @@ def wifi_disconnect_ap(): protodata = disconnect_ap.SerializeToString() tp = Transport_pserial(interface) response = tp.send_data(endpoint,protodata) - disconnect_ap.ParseFromString(response[1]) del tp - if response[0] != success: + if response[0] != success and response[0] != None: return failure_str + disconnect_ap.ParseFromString(response[1]) if disconnect_ap.resp_disconnect_ap.resp != success: return failure_str else: @@ -312,10 +312,10 @@ def wifi_set_softap_config(ssid, pwd, chnl, ecn, max_conn, ssid_hidden, bw): protodata = set_softap_config.SerializeToString() tp = Transport_pserial(interface) response = tp.send_data(endpoint,protodata) - set_softap_config.ParseFromString(response[1]) del tp - if response[0] != success : + if response[0] != success and response[0] != None: return failure_str + set_softap_config.ParseFromString(response[1]) if set_softap_config.resp_set_softap_config.resp != success: return failure_str else: @@ -348,10 +348,10 @@ def wifi_get_softap_config(): protodata = get_softap_config.SerializeToString() tp = Transport_pserial(interface) response = tp.send_data(endpoint,protodata) - get_softap_config.ParseFromString(response[1]) del tp - if response[0] != success : + if response[0] != success and response[0] != None: return failure_str + get_softap_config.ParseFromString(response[1]) if get_softap_config.resp_get_softap_config.resp != success: return failure_str ssid = get_str(get_softap_config.resp_get_softap_config.ssid) @@ -374,9 +374,9 @@ def wifi_stop_softap(): tp = Transport_pserial(interface) response = tp.send_data(endpoint,protodata) del tp - stop_softap.ParseFromString(response[1]) - if response[0] != success: + if response[0] != success and response[0] != None: return failure_str + stop_softap.ParseFromString(response[1]) if stop_softap.resp_stop_softap.resp != success: return failure_str else: @@ -409,10 +409,10 @@ def wifi_ap_scan_list(): protodata = get_ap_scan_list.SerializeToString() tp = Transport_pserial(interface) response = tp.send_data(endpoint,protodata) - get_ap_scan_list.ParseFromString(response[1]) del tp - if response[0] != success : + if response[0] != success and response[0] != None: return failure_str + get_ap_scan_list.ParseFromString(response[1]) if get_ap_scan_list.resp_scan_ap_list.resp != success: return failure_str count = get_ap_scan_list.resp_scan_ap_list.count @@ -443,10 +443,10 @@ def wifi_connected_stations_list(): protodata = get_connected_stations_list.SerializeToString() tp = Transport_pserial(interface) response = tp.send_data(endpoint,protodata) - get_connected_stations_list.ParseFromString(response[1]) del tp - if response[0] != success : + if response[0] != success and response[0] != None: return failure_str + get_connected_stations_list.ParseFromString(response[1]) if get_connected_stations_list.resp_connected_stas_list.resp != success: return failure_str num = get_connected_stations_list.resp_connected_stas_list.num @@ -488,10 +488,10 @@ def wifi_set_mac(mode, mac): protodata = set_mac.SerializeToString() tp = Transport_pserial(interface) response = tp.send_data(endpoint,protodata) - set_mac.ParseFromString(response[1]) del tp - if response[0] != success: + if response[0] != success and response[0] != None: return failure_str + set_mac.ParseFromString(response[1]) if set_mac.resp_set_mac_address.resp != success: return failure_str else: @@ -516,10 +516,10 @@ def wifi_set_power_save_mode(power_save_mode): protodata = set_power_save_mode.SerializeToString() tp = Transport_pserial(interface) response = tp.send_data(endpoint,protodata) - set_power_save_mode.ParseFromString(response[1]) del tp - if response[0] != success: + if response[0] != success and response[0] != None: return failure_str + set_power_save_mode.ParseFromString(response[1]) if set_power_save_mode.resp_set_power_save_mode.resp != success: return failure_str else: @@ -542,7 +542,7 @@ def wifi_get_power_save_mode(): response = tp.send_data(endpoint,protodata) get_power_save_mode.ParseFromString(response[1]) del tp - if response[0] != success: + if response[0] != success and response[0] != None: return failure_str if get_power_save_mode.resp_get_power_save_mode.resp != success: return failure_str diff --git a/host/linux/host_control/python_support/transport/transport_pserial.py b/host/linux/host_control/python_support/transport/transport_pserial.py index 76c6c5203a..76599d8e6f 100644 --- a/host/linux/host_control/python_support/transport/transport_pserial.py +++ b/host/linux/host_control/python_support/transport/transport_pserial.py @@ -96,7 +96,16 @@ def send_data(self, ep_name, data): buf.extend(PROTO_PSER_TLV_T_DATA) buf.extend(pack('priv, tx_skb); if (ret) { - printk (KERN_ERR "%s: Failed to transmit data\n", __func__); + printk (KERN_ERR "%s: Failed to transmit data, error %d\n", __func__, ret); + return ret; } return size; From 1e6c7de31e3a6f566db23e8cd46101ed3ce67fe9 Mon Sep 17 00:00:00 2001 From: "ajita.chavan" Date: Mon, 9 Aug 2021 13:09:00 +0530 Subject: [PATCH 03/40] serial_write_data function sends data over to_host_queue [root cause] -- `sdio slave transmit error` error occured when control path command's response sends while wlan+BT traffic ongoing. -- which cause SDIO driver to discard the data. [Fix] -- `serial_write_data` function handled over to_host_queue. [Testing] -- control path command send form host while iperf going on, Didn't give `sdio slave transmit error`. Signed-off-by: ajita.chavan --- .../network_adapter/main/app_main.c | 40 ++++++++-------- .../network_adapter/main/protocomm_pserial.c | 25 +++++++--- .../network_adapter/main/sdio_slave_api.c | 11 ++--- .../network_adapter/main/slave_commands.c | 46 +++++++++---------- .../network_adapter/main/spi_slave_api.c | 40 ++++++++-------- 5 files changed, 89 insertions(+), 73 deletions(-) diff --git a/esp/esp_driver/network_adapter/main/app_main.c b/esp/esp_driver/network_adapter/main/app_main.c index 26f46c9c60..c58eb43deb 100644 --- a/esp/esp_driver/network_adapter/main/app_main.c +++ b/esp/esp_driver/network_adapter/main/app_main.c @@ -215,7 +215,7 @@ static void esp_wifi_set_debug_log() esp_err_t wlan_ap_rx_callback(void *buffer, uint16_t len, void *eb) { esp_err_t ret = ESP_OK; - interface_buffer_handle_t buf_handle; + interface_buffer_handle_t buf_handle = {0}; if (!buffer || !eb || !datapath) { if (eb) { @@ -224,9 +224,6 @@ esp_err_t wlan_ap_rx_callback(void *buffer, uint16_t len, void *eb) return ESP_OK; } - /* Prepare buffer descriptor */ - memset(&buf_handle, 0, sizeof(buf_handle)); - buf_handle.if_type = ESP_AP_IF; buf_handle.if_num = 0; buf_handle.payload_len = len; @@ -251,7 +248,7 @@ esp_err_t wlan_ap_rx_callback(void *buffer, uint16_t len, void *eb) esp_err_t wlan_sta_rx_callback(void *buffer, uint16_t len, void *eb) { esp_err_t ret = ESP_OK; - interface_buffer_handle_t buf_handle; + interface_buffer_handle_t buf_handle = {0}; if (!buffer || !eb || !datapath) { if (eb) { @@ -264,9 +261,6 @@ esp_err_t wlan_sta_rx_callback(void *buffer, uint16_t len, void *eb) from_wlan_count++; #endif - /* Prepare buffer descriptor */ - memset(&buf_handle, 0, sizeof(buf_handle)); - buf_handle.if_type = ESP_STA_IF; buf_handle.if_num = 0; buf_handle.payload_len = len; @@ -362,9 +356,9 @@ void process_rx_task(void* pvParameters) { esp_err_t ret = ESP_OK; interface_buffer_handle_t buf_handle = {0}; - struct esp_payload_header *header; - uint8_t *payload; - uint16_t payload_len; + struct esp_payload_header *header = NULL; + uint8_t *payload = NULL; + uint16_t payload_len = 0; while (1) { ret = xQueueReceive(from_host_queue, &buf_handle, portMAX_DELAY); @@ -429,7 +423,7 @@ void process_rx_task(void* pvParameters) void recv_task(void* pvParameters) { interface_buffer_handle_t *buf_handle = NULL; - esp_err_t ret; + esp_err_t ret = ESP_OK; for (;;) { @@ -454,6 +448,7 @@ void recv_task(void* pvParameters) ESP_LOGE(TAG, "Host -> Slave: Failed to send buffer\n"); if (buf_handle->free_buf_handle && buf_handle->priv_buffer_handle) { buf_handle->free_buf_handle(buf_handle->priv_buffer_handle); + buf_handle->priv_buffer_handle = NULL; } } @@ -478,18 +473,29 @@ static int32_t serial_read_data(uint8_t *data, int32_t len) static int32_t serial_write_data(uint8_t* data, int32_t len) { interface_buffer_handle_t buf_handle = {0}; + esp_err_t ret = ESP_OK; buf_handle.if_type = ESP_SERIAL_IF; buf_handle.if_num = 0; buf_handle.payload = data; buf_handle.payload_len = len; + buf_handle.priv_buffer_handle = data; + buf_handle.free_buf_handle = free; + + ret = xQueueSend(to_host_queue, &buf_handle, portMAX_DELAY); - if (datapath && if_context && if_context->if_ops && if_context->if_ops->write) - if_context->if_ops->write(if_handle, &buf_handle); + if (ret != pdTRUE) { + ESP_LOGE(TAG, "Control packet: Failed to send buffer\n"); + if (data) { + free(data); + data = NULL; + } + return ESP_FAIL; + } #if CONFIG_ESP_SERIAL_DEBUG ESP_LOG_BUFFER_HEXDUMP(TAG_TX_S, data, len, ESP_LOG_INFO); #endif - return len; + return ESP_OK; } #ifdef CONFIG_BT_ENABLED @@ -504,7 +510,7 @@ static void controller_rcv_pkt_ready(void) static int host_rcv_pkt(uint8_t *data, uint16_t len) { esp_err_t ret = ESP_OK; - interface_buffer_handle_t buf_handle; + interface_buffer_handle_t buf_handle = {0}; uint8_t *buf = NULL; buf = (uint8_t *) malloc(len); @@ -516,8 +522,6 @@ static int host_rcv_pkt(uint8_t *data, uint16_t len) memcpy(buf, data, len); - memset(&buf_handle, 0, sizeof(buf_handle)); - buf_handle.if_type = ESP_HCI_IF; buf_handle.if_num = 0; buf_handle.payload_len = len; diff --git a/esp/esp_driver/network_adapter/main/protocomm_pserial.c b/esp/esp_driver/network_adapter/main/protocomm_pserial.c index 6ee1894177..ff00bab0cf 100644 --- a/esp/esp_driver/network_adapter/main/protocomm_pserial.c +++ b/esp/esp_driver/network_adapter/main/protocomm_pserial.c @@ -156,10 +156,18 @@ static esp_err_t protocomm_pserial_common_handler(protocomm_t *pc, uint8_t *in, pserial_cfg = pc->priv; ret = compose_tlv(&out, &outlen); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to compose tlv"); + return ESP_FAIL; + } + ret = (pserial_cfg->xmit)(out, (ssize_t) outlen); - free(out); - return ret; + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to transmit data"); + return ESP_FAIL; + } + return ESP_OK; } esp_err_t protocomm_pserial_data_ready(protocomm_t *pc, int len) @@ -196,7 +204,7 @@ static void pserial_task(void *params) { protocomm_t *pc = (protocomm_t *) params; struct pserial_config *pserial_cfg = NULL; - int len = 0; + int len = 0, ret = 0; uint8_t *buf = NULL; pserial_cfg = (struct pserial_config *) pc->priv; @@ -213,9 +221,14 @@ static void pserial_task(void *params) } len = pserial_cfg->recv(buf, len); if (len) { - protocomm_pserial_common_handler(pc, buf, len); - free(buf); - buf = NULL; + ret = protocomm_pserial_common_handler(pc, buf, len); + if (buf) { + free(buf); + buf = NULL; + } + if (ret != ESP_OK) { + return; + } } } diff --git a/esp/esp_driver/network_adapter/main/sdio_slave_api.c b/esp/esp_driver/network_adapter/main/sdio_slave_api.c index 7e7ae0d461..07974a0164 100644 --- a/esp/esp_driver/network_adapter/main/sdio_slave_api.c +++ b/esp/esp_driver/network_adapter/main/sdio_slave_api.c @@ -85,7 +85,7 @@ static void sdio_read_done(void *handle) static interface_handle_t * sdio_init(uint8_t capabilities) { - esp_err_t ret = 0; + esp_err_t ret = ESP_OK; sdio_slave_config_t config = { .sending_mode = SDIO_SLAVE_SEND_STREAM, .send_queue_size = SDIO_SLAVE_QUEUE_SIZE, @@ -145,10 +145,10 @@ static interface_handle_t * sdio_init(uint8_t capabilities) static int32_t sdio_write(interface_handle_t *handle, interface_buffer_handle_t *buf_handle) { esp_err_t ret = ESP_OK; - int32_t total_len; + int32_t total_len = 0; uint8_t* sendbuf = NULL; uint16_t offset = 0; - struct esp_payload_header *header; + struct esp_payload_header *header = NULL; if (!handle || !buf_handle) { ESP_LOGE(TAG , "Invalid arguments"); @@ -159,7 +159,6 @@ static int32_t sdio_write(interface_handle_t *handle, interface_buffer_handle_t return ESP_FAIL; } - if (!buf_handle->payload_len || !buf_handle->payload) { ESP_LOGE(TAG , "Invalid arguments, len:%d", buf_handle->payload_len); return ESP_FAIL; @@ -200,7 +199,7 @@ static int32_t sdio_write(interface_handle_t *handle, interface_buffer_handle_t interface_buffer_handle_t * sdio_read(interface_handle_t *if_handle) { interface_buffer_handle_t *buf_handle = NULL; - struct esp_payload_header *header; + struct esp_payload_header *header = NULL; if (!if_handle) { ESP_LOGE(TAG, "Invalid arguments to sdio_read"); @@ -231,7 +230,7 @@ interface_buffer_handle_t * sdio_read(interface_handle_t *if_handle) static esp_err_t sdio_reset(interface_handle_t *handle) { - esp_err_t ret; + esp_err_t ret = ESP_OK; sdio_slave_stop(); diff --git a/esp/esp_driver/network_adapter/main/slave_commands.c b/esp/esp_driver/network_adapter/main/slave_commands.c index 287e0b71a8..f2d2ef80ed 100644 --- a/esp/esp_driver/network_adapter/main/slave_commands.c +++ b/esp/esp_driver/network_adapter/main/slave_commands.c @@ -232,9 +232,9 @@ typedef struct esp_hosted_config_cmd { static esp_err_t cmd_get_mac_address_handler(EspHostedConfigPayload *req, EspHostedConfigPayload *resp, void *priv_data) { - esp_err_t ret; - uint8_t mac[MAC_LEN]; - char mac_str[BSSID_LENGTH]; + esp_err_t ret = ESP_OK; + uint8_t mac[MAC_LEN] = {0}; + char mac_str[BSSID_LENGTH] = ""; EspHostedRespGetMacAddress *resp_payload = NULL; if (!req || !resp || !req->cmd_get_mac_address) { @@ -298,8 +298,8 @@ static esp_err_t cmd_get_mac_address_handler(EspHostedConfigPayload *req, static esp_err_t cmd_get_wifi_mode_handler (EspHostedConfigPayload *req, EspHostedConfigPayload *resp, void *priv_data) { - esp_err_t ret; - wifi_mode_t mode; + esp_err_t ret = ESP_OK; + wifi_mode_t mode = 0; EspHostedRespGetMode *resp_payload = NULL; if (!req || !resp) { @@ -336,8 +336,8 @@ static esp_err_t cmd_get_wifi_mode_handler (EspHostedConfigPayload *req, static esp_err_t cmd_set_wifi_mode_handler (EspHostedConfigPayload *req, EspHostedConfigPayload *resp, void *priv_data) { - esp_err_t ret; - wifi_mode_t num; + esp_err_t ret = ESP_OK; + wifi_mode_t num = 0; EspHostedRespSetMode *resp_payload = NULL; if (!req || !resp || !req->cmd_set_wifi_mode) { @@ -379,7 +379,7 @@ static esp_err_t cmd_set_wifi_mode_handler (EspHostedConfigPayload *req, static esp_err_t cmd_set_ap_config_handler (EspHostedConfigPayload *req, EspHostedConfigPayload *resp, void *priv_data) { - esp_err_t ret; + esp_err_t ret = ESP_OK; bool event_registered = false; wifi_config_t *wifi_cfg = NULL; EspHostedRespSetAPConfig *resp_payload = NULL; @@ -526,7 +526,7 @@ static esp_err_t cmd_set_ap_config_handler (EspHostedConfigPayload *req, static esp_err_t cmd_get_ap_config_handler (EspHostedConfigPayload *req, EspHostedConfigPayload *resp, void *priv_data) { - esp_err_t ret; + esp_err_t ret = ESP_OK; credentials_t credentials = {0}; wifi_ap_record_t *ap_info = NULL; EspHostedRespGetAPConfig *resp_payload = NULL; @@ -621,7 +621,7 @@ static esp_err_t cmd_get_ap_config_handler (EspHostedConfigPayload *req, static esp_err_t cmd_disconnect_ap_handler (EspHostedConfigPayload *req, EspHostedConfigPayload *resp, void *priv_data) { - esp_err_t ret; + esp_err_t ret = ESP_OK; EspHostedRespGetStatus *resp_payload = NULL; if (!req || !resp) { ESP_LOGE(TAG, "Invalid parameters"); @@ -664,8 +664,8 @@ static esp_err_t cmd_disconnect_ap_handler (EspHostedConfigPayload *req, static esp_err_t cmd_get_softap_config_handler (EspHostedConfigPayload *req, EspHostedConfigPayload *resp, void *priv_data) { - esp_err_t ret; - wifi_bandwidth_t get_bw; + esp_err_t ret = ESP_OK; + wifi_bandwidth_t get_bw = 0; credentials_t credentials = {0}; wifi_config_t get_conf = {0}; EspHostedRespGetSoftAPConfig *resp_payload = NULL; @@ -765,8 +765,8 @@ static esp_err_t cmd_get_softap_config_handler (EspHostedConfigPayload *req, static esp_err_t cmd_set_softap_config_handler (EspHostedConfigPayload *req, EspHostedConfigPayload *resp, void *priv_data) { - esp_err_t ret; - uint8_t mac[MAC_LEN]; + esp_err_t ret = ESP_OK; + uint8_t mac[MAC_LEN] = {0}; wifi_config_t *wifi_config = NULL; EspHostedRespSetSoftAPConfig *resp_payload = NULL; @@ -887,8 +887,8 @@ static esp_err_t cmd_set_softap_config_handler (EspHostedConfigPayload *req, static esp_err_t cmd_get_ap_scan_list_handler (EspHostedConfigPayload *req, EspHostedConfigPayload *resp, void *priv_data) { - esp_err_t ret; - wifi_mode_t mode; + esp_err_t ret = ESP_OK; + wifi_mode_t mode = 0; uint16_t ap_count = 0; credentials_t credentials = {0}; wifi_ap_record_t *ap_info = NULL; @@ -1048,8 +1048,8 @@ static esp_err_t cmd_get_ap_scan_list_handler (EspHostedConfigPayload *req, static esp_err_t cmd_stop_softap_handler (EspHostedConfigPayload *req, EspHostedConfigPayload *resp, void *priv_data) { - esp_err_t ret; - wifi_mode_t mode; + esp_err_t ret = ESP_OK; + wifi_mode_t mode = 0; EspHostedRespGetStatus *resp_payload = NULL; if (!req || !resp) { @@ -1104,8 +1104,8 @@ static esp_err_t cmd_stop_softap_handler (EspHostedConfigPayload *req, static esp_err_t get_connected_sta_list_handler (EspHostedConfigPayload *req, EspHostedConfigPayload *resp, void *priv_data) { - esp_err_t ret; - wifi_mode_t mode; + esp_err_t ret = ESP_OK; + wifi_mode_t mode = 0; credentials_t credentials = {0}; EspHostedRespConnectedSTA *resp_payload = NULL; EspHostedConnectedSTAList **results = NULL; @@ -1329,7 +1329,7 @@ static esp_err_t cmd_get_power_save_mode_handler (EspHostedConfigPayload *req, EspHostedConfigPayload *resp, void *priv_data) { esp_err_t ret = ESP_OK; - wifi_ps_type_t ps_type; + wifi_ps_type_t ps_type = 0; EspHostedRespGetMode *resp_payload = NULL; if (!req || !resp) { @@ -1434,7 +1434,7 @@ static esp_err_t esp_hosted_config_command_dispatcher( EspHostedConfigPayload *req, EspHostedConfigPayload *resp, void *priv_data) { - esp_err_t ret; + esp_err_t ret = ESP_OK; int cmd_index = 0; if (!req || !resp) { @@ -1563,7 +1563,7 @@ static void esp_hosted_config_cleanup(EspHostedConfigPayload *resp) esp_err_t data_transfer_handler(uint32_t session_id,const uint8_t *inbuf, ssize_t inlen, uint8_t **outbuf, ssize_t *outlen, void *priv_data) { - EspHostedConfigPayload *req, resp; + EspHostedConfigPayload *req = NULL, resp = {0}; esp_err_t ret = ESP_OK; if (!inbuf || !outbuf || !outlen) { diff --git a/esp/esp_driver/network_adapter/main/spi_slave_api.c b/esp/esp_driver/network_adapter/main/spi_slave_api.c index cdc91ab7e2..039d7f34b3 100644 --- a/esp/esp_driver/network_adapter/main/spi_slave_api.c +++ b/esp/esp_driver/network_adapter/main/spi_slave_api.c @@ -151,8 +151,8 @@ int interface_remove_driver() static int is_valid_trans_buffer(uint8_t *trans_buf) { - struct esp_payload_header *header; - uint16_t len, offset; + struct esp_payload_header *header = NULL; + uint16_t len = 0, offset = 0; if (!trans_buf) { return pdFALSE; @@ -203,7 +203,7 @@ static uint8_t * get_next_tx_buffer(uint32_t *len) interface_buffer_handle_t buf_handle = {0}; esp_err_t ret = ESP_OK; uint8_t *sendbuf = NULL; - struct esp_payload_header *header; + struct esp_payload_header *header = NULL; /* Get or create new tx_buffer * 1. Check if SPI TX queue has pending buffers. Return if valid buffer is obtained. @@ -254,8 +254,8 @@ static uint8_t * get_next_tx_buffer(uint32_t *len) static int process_spi_rx(interface_buffer_handle_t *buf_handle) { int ret = 0; - struct esp_payload_header *header; - uint16_t len, offset; + struct esp_payload_header *header = NULL; + uint16_t len = 0, offset = 0; /* Validate received buffer. Drop invalid buffer. */ @@ -289,9 +289,9 @@ static int process_spi_rx(interface_buffer_handle_t *buf_handle) static void spi_transaction_tx_task(void* pvParameters) { - spi_slave_transaction_t *spi_trans; - esp_err_t ret = 0; - interface_buffer_handle_t buf_handle; + spi_slave_transaction_t *spi_trans = NULL; + esp_err_t ret = ESP_OK; + interface_buffer_handle_t buf_handle = {0}; for(;;) { ret = xQueueReceive(spi_tx_queue, &buf_handle, portMAX_DELAY); @@ -411,9 +411,9 @@ static void queue_dummy_transaction() static void spi_transaction_post_process_task(void* pvParameters) { spi_slave_transaction_t *spi_trans = NULL; - esp_err_t ret = 0; - interface_buffer_handle_t rx_buf_handle; - struct esp_payload_header *header; + esp_err_t ret = ESP_OK; + interface_buffer_handle_t rx_buf_handle = {0}; + struct esp_payload_header *header = NULL; for (;;) { memset(&rx_buf_handle, 0, sizeof(rx_buf_handle)); @@ -477,10 +477,10 @@ static void spi_transaction_post_process_task(void* pvParameters) static void generate_startup_event(uint8_t cap) { - struct esp_payload_header *header; - interface_buffer_handle_t buf_handle; - struct esp_priv_event *event; - uint8_t *pos; + struct esp_payload_header *header = NULL; + interface_buffer_handle_t buf_handle = {0}; + struct esp_priv_event *event = NULL; + uint8_t *pos = NULL; uint16_t len = 0; memset(&buf_handle, 0, sizeof(buf_handle)); @@ -624,10 +624,10 @@ static interface_handle_t * esp_spi_init(uint8_t capabilities) static int32_t esp_spi_write(interface_handle_t *handle, interface_buffer_handle_t *buf_handle) { esp_err_t ret = ESP_OK; - int32_t total_len; + int32_t total_len = 0; uint16_t offset = 0; - struct esp_payload_header *header; - interface_buffer_handle_t tx_buf_handle; + struct esp_payload_header *header = NULL; + interface_buffer_handle_t tx_buf_handle = {0}; if (!handle || !buf_handle) { ESP_LOGE(TAG , "Invalid arguments\n"); @@ -714,7 +714,7 @@ static interface_buffer_handle_t * esp_spi_read(interface_handle_t *if_handle) static esp_err_t esp_spi_reset(interface_handle_t *handle) { - esp_err_t ret; + esp_err_t ret = ESP_OK; ret = spi_slave_free(ESP_SPI_CONTROLLER); if (ESP_OK != ret) { ESP_LOGE(TAG, "spi slave bus free failed\n"); @@ -724,7 +724,7 @@ static esp_err_t esp_spi_reset(interface_handle_t *handle) static void esp_spi_deinit(interface_handle_t *handle) { - esp_err_t ret; + esp_err_t ret = ESP_OK; if (spi_sema) vSemaphoreDelete(spi_sema); From 52ac50c5739c30f6c266eafee38d13cce3ac1ad2 Mon Sep 17 00:00:00 2001 From: "ajita.chavan" Date: Thu, 26 Aug 2021 14:08:01 +0530 Subject: [PATCH 04/40] 1. Command line arguments added for test.c and stress.c as sta_connect, sta_disconnect, ap_start, ap_stop, scan, sta_list. 2. dhcp commands removed from python scripts. 3. interface up/down, assign mac address to HW interface added in respective examples of station connect, station disconnect, softap start, softap stop in test_api.c and test_api.py. 4. Documentation updated. Signed-off-by: ajita.chavan --- docs/Linux_based_host/Getting_started.md | 23 +- docs/Linux_based_host/directory_structure.md | 4 +- docs/c_api.md | 2 +- docs/c_demo.md | 62 ++++ docs/python_api.md | 4 +- docs/python_demo.md | 39 +++ host/linux/host_control/c_support/stress.c | 173 +++-------- host/linux/host_control/c_support/test.c | 73 +---- host/linux/host_control/c_support/test_api.c | 290 ++++++++++++++++-- host/linux/host_control/c_support/test_api.h | 10 +- .../host_control/c_support/test_config.h | 7 + .../python_support/connected_stations_list.py | 1 + .../python_support/softap_config.py | 5 +- .../python_support/softap_stop.py | 5 +- .../python_support/station_connect.py | 35 +-- .../python_support/station_disconnect.py | 8 +- .../host_control/python_support/stress.py | 3 +- .../linux/host_control/python_support/test.py | 3 + .../host_control/python_support/test_api.py | 154 +++++++++- 19 files changed, 627 insertions(+), 274 deletions(-) create mode 100644 docs/c_demo.md create mode 100644 docs/python_demo.md diff --git a/docs/Linux_based_host/Getting_started.md b/docs/Linux_based_host/Getting_started.md index db2a0ee045..ec99e4172f 100644 --- a/docs/Linux_based_host/Getting_started.md +++ b/docs/Linux_based_host/Getting_started.md @@ -32,7 +32,6 @@ $ cd host/linux/host_control/python_support/ --- * **Connect to external access point** * `station_connect.py` script configures ESP peripheral in WiFi station mode and connects to an external AP with user-provided credentials. - * This script also runs DHCP client that obtains IP address from an external AP. * The script accepts arguments such as SSID, password, optionally BSSID of an external AP, wpa3 support and listen interval (AP beacon intervals). For example: ``` $ python station_connect.py 'xyz' 'xyz123456' --bssid='e5:6c:67:3c:cf:65' --is_wpa3_supported=True --listen_interval=3 @@ -40,8 +39,16 @@ $ cd host/linux/host_control/python_support/ :warning:`Note: WPA3 option is only applicable if target AP supports WPA3.` * As an end result of this script: * Wi-Fi station interface of ESP peripheral will be connected to an external AP - * `ethsta0` interface will be up and it will also have an IP address - * Network data path will be open for higher applications to use this interface for data communication + * `ethsta0` interface will be up and ESP32's MAC address will be assigned to it. + +Note: +* User needs to run DHCP client to obtain IP address from an external AP. After that network data path will be open for higher applications to use this interface for data communication. For an example as below. + +``` +sudo dhclient ethsta0 -r + +sudo dhclient ethsta0 -v +``` --- * **Disconnect from external access point** * `station_disconnect.py` script disconnects ESP peripheral station from an external AP. @@ -49,7 +56,7 @@ $ cd host/linux/host_control/python_support/ $ python station_disconnect.py ``` * As an end result of this script: - * `ethsta0` interface will be in down state and it won't have IP address + * `ethsta0` interface will be in down state * Network data path will be in closed state, hence there won't be any data communication on this interface #### 1.1.2 Wi-Fi softAP Mode Operations @@ -71,7 +78,13 @@ $ cd host/linux/host_control/python_support/ * As an end result of this script: * SoftAP interface will be up and running on ESP peripheral * `ethap0` interface will be in `up` state - * To start data connection, set up a DHCP server on the Raspberry Pi, or configure a static IP address for AP interface (`ethap0`). + * To start data connection, set up a DHCP server on the Raspberry Pi, or configure a static IP address for AP interface (`ethap0`). For an example as below: + +``` +sudo dnsmasq --no-daemon --no-resolv --no-poll --dhcp-script=/system/bin/dhcp_announce --dhcp-range=192.168.4.1,192.168.4.20,1h + +sudo ifconfig ethap0 192.168.4.5 +``` --- * **Stop softAP** * `softap_stop.py` script disables wifi softAP mode on ESP peripheral. diff --git a/docs/Linux_based_host/directory_structure.md b/docs/Linux_based_host/directory_structure.md index 3a3ba91952..daefb31bc7 100644 --- a/docs/Linux_based_host/directory_structure.md +++ b/docs/Linux_based_host/directory_structure.md @@ -3,9 +3,9 @@ ``` ├── linux │   ├── host_control (Contain necessary files for installation of control path) -│   │   ├── c_support (Contain files to test basic control path commands) +│   │   ├── c_support (Contain files to test basic control path commands and stress.c for stress testing) │   │   ├── python_support (Contain python scripts for Wi-Fi functionality, -│   │   │ │ `test.py` to test basic control path commands, also +│   │   │ │ `test.py` to test basic control path commands, stress.py for stress testing, also │   │   │ │ `commands.py` control path commands implementation using │   │   │ │ python protobuf generated files) │   │   │   └── transport (Handles read/write operation of control path commands on diff --git a/docs/c_api.md b/docs/c_api.md index 0e71c8a7f4..e6a58c8c7c 100644 --- a/docs/c_api.md +++ b/docs/c_api.md @@ -1,6 +1,6 @@ # Control Interface: C API's -This document describes C API's provided for control interface. Please refer [test.c](../host/linux/host_control/c_support/test.c) to get an idea how to use these API's. +This document describes C API's provided for control path interface. Please refer [commands.c](../host/host_common/commands.c) for API's defination. [c_demo.md](c_demo.md) gives overview of how to test control path interface in application also how to perform stress testing of control path interface. ## 1. Data Structures diff --git a/docs/c_demo.md b/docs/c_demo.md new file mode 100644 index 0000000000..66bfe9b3db --- /dev/null +++ b/docs/c_demo.md @@ -0,0 +1,62 @@ +# C Demo Application + +[test.c](../host/linux/host_control/c_support/test.c) is a demo application to provide basic command line arguments as follows: + +| Command line argument | Operation | +|:----|:----| +| sta_connect | Connect ESP32 station to external AP, assign MAC address of ESP32 station to `ethsta0` and up `ethsta0` interface | +| sta_disconnect | Disconnect ESP32 station from external AP and down `ethsta0` interface | +| ap_start | Start ESP32 softAP, assign MAC address of ESP32 softAP to `ethap0` and up `ethap0` interface | +| ap_stop | Stop ESP32 softAP stop and down `ethap0` interface | +| scan | Scan external access points | +| sta_list | List external stations connected to softAP | + +It uses APIs present in [test_api.c](../host/linux/host_control/c_support/test_api.c). User should first modify configuration parameters in [test_config.h](../host/linux/host_control/c_support/test_config.h). Then run `make` in [c_support](../host/linux/host_control/c_support) to compile `test.c`. + +Note:- +Please execute `test.out` as below. + +``` +ex. +sudo ./test.out sta_connect sta_disconnect ap_start ap_stop scan sta_list +``` +Note: +* After `sta_connect`, User needs to run DHCP client to obtain IP address from an external AP. Then network data path will be open for higher applications to use `ethsta0` interface for data communication. For an example as below. + +``` +sudo dhclient ethsta0 -r + +sudo dhclient ethsta0 -v +``` + +* After `ap_start` to start data connection, set up a DHCP server on the Raspberry Pi, or configure a static IP address for AP interface (`ethap0`). For an example as below: + +``` +sudo dnsmasq --no-daemon --no-resolv --no-poll --dhcp-script=/system/bin/dhcp_announce --dhcp-range=192.168.4.1,192.168.4.20,1h + +sudo ifconfig ethap0 192.168.4.5 +``` + +# C stress Application + +[stress.c](../host/linux/host_control/c_support/stress.c) use for stress testing of control path APIs. It provides basic command line arguments as follows: + +| Command line argument | Operation | +|:----|:----| +| Any positive interger number | Number of iterations for stress test | +| sta_connect | Connect ESP32 station to external AP, assign MAC address of ESP32 station to `ethsta0` and up `ethsta0` interface | +| sta_disconnect | Disconnect ESP32 station from external AP and down `ethsta0` interface | +| ap_start | Start ESP32 softAP, assign MAC address of ESP32 softAP to `ethap0` and up `ethap0` interface | +| ap_stop | Stop ESP32 softAP stop and down `ethap0` interface | +| scan | Scan external access points | +| sta_list | List external stations connected to softAP | + + Run `make stress` in [c_support](../host/linux/host_control/c_support) directory to compile `stress.c`. + +Note:- +Please execute `stress.out` as below. + +``` +ex. +sudo ./stress.out 1 sta_connect sta_disconnect ap_start ap_stop scan sta_list +``` diff --git a/docs/python_api.md b/docs/python_api.md index 86928f9959..bca671c5b1 100644 --- a/docs/python_api.md +++ b/docs/python_api.md @@ -1,6 +1,8 @@ # Control Interface API's: Python Implementation -This document describes python API's provided for control interface. Please refer [test.py](../host/linux/host_control/python_support/test.py) to get an idea how to use these API's. +This document describes python API's provided for control interface. +[python_demo.md](python_demo.md) gives overview of how to test control path interface in application also how to perform stress testing of control path interface. + ## 1. `wifi_get_mac` This is used to retrieve the MAC address of ESP's station or softAP interface diff --git a/docs/python_demo.md b/docs/python_demo.md new file mode 100644 index 0000000000..7376b8ecd1 --- /dev/null +++ b/docs/python_demo.md @@ -0,0 +1,39 @@ +# Python Demo Application + +[test.py](../host/linux/host_control/python_support/test.py) is a demo application to test control path interface: + +It uses APIs present in [test_api.py](../host/linux/host_control/python_support/test_api.py). User should first modify configuration parameters in [test_config.py](../host/linux/host_control/python_support/test_config.py). + +Note:- +Please execute `test.py` as below. + +``` +sudo python test.py +``` +Note: +* After `test_station_mode_connect` API call, User needs to run DHCP client to obtain IP address from an external AP. Then network data path will be open for higher applications to use `ethsta0` interface for data communication. For an example as below. + +``` +sudo dhclient ethsta0 -r + +sudo dhclient ethsta0 -v +``` + +* After `test_softap_mode_start` API ,to start data connection, set up a DHCP server on the Raspberry Pi, or configure a static IP address for AP interface (`ethap0`). For an example as below: + +``` +sudo dnsmasq --no-daemon --no-resolv --no-poll --dhcp-script=/system/bin/dhcp_announce --dhcp-range=192.168.4.1,192.168.4.20,1h + +sudo ifconfig ethap0 192.168.4.5 +``` + +# Python stress Application + +[stress.py](../host/linux/host_control/python_support/stress.py) use for stress testing of control path APIs. User should first modify configuration parameters in [test_config.py](../host/linux/host_control/python_support/test_config.py). `STRESS_TEST_COUNT` variable is defined in `stress.py` for number of iterations for stress testing. + +Note:- +Please execute `stress.py` as below. +``` +ex. +sudo python stress.py +``` diff --git a/host/linux/host_control/c_support/stress.c b/host/linux/host_control/c_support/stress.c index dcd7a0a2a4..7a2a933c0c 100644 --- a/host/linux/host_control/c_support/stress.c +++ b/host/linux/host_control/c_support/stress.c @@ -20,135 +20,54 @@ #include #include "test_api.h" -#define STRESS_TEST_COUNT 50 - -#define TEST_MODE_NONE (1 << 0) -#define TEST_SCAN_WIFI (1 << 1) -#define TEST_STATION_MAC (1 << 2) -#define TEST_STATION_CONNECT_DISCONNECT (1 << 3) -#define TEST_SOFTAP_MAC (1 << 4) -#define TEST_SOFTAP_START_STOP (1 << 5) -#define TEST_STATION_SOFTAP_MODE (1 << 6) -#define TEST_POWER_SAVE (1 << 7) - -#define STRESS_TEST (TEST_MODE_NONE | TEST_SCAN_WIFI | TEST_STATION_MAC | \ - TEST_STATION_CONNECT_DISCONNECT | TEST_SOFTAP_MAC | \ - TEST_SOFTAP_START_STOP | TEST_STATION_SOFTAP_MODE | TEST_POWER_SAVE) - /***** Please Read *****/ /* Before use stress.c : User must enter user configuration parameter in "test_config.h" file */ -int main() +int main(int argc, char *argv[]) { - /* Below APIs could be used by demo application */ - int ret = control_path_platform_init(); - if (ret != SUCCESS) { - printf("EXIT!!!!\n"); - exit(0); - } - -#if (STRESS_TEST & TEST_MODE_NONE) - for (int i=0; i < STRESS_TEST_COUNT; i++) { - printf("************** %d ****************** \n", i); - test_set_wifi_mode_none(); - - test_get_wifi_mode(); - } -#endif - -#if (STRESS_TEST & TEST_SCAN_WIFI) - for (int i=0; i < STRESS_TEST_COUNT; i++) { - printf("************** %d ****************** \n", i); - test_get_available_wifi(); - } -#endif - - /* station mode */ -#if (STRESS_TEST & TEST_STATION_MAC) - for (int i=0; i < STRESS_TEST_COUNT; i++) { - printf("************** %d ****************** \n", i); - test_set_wifi_mode_station(); - - test_station_mode_set_mac_addr_of_esp(); - - test_station_mode_get_mac_addr(); - } -#endif - -#if (STRESS_TEST & TEST_STATION_CONNECT_DISCONNECT) - for (int i=0; i < STRESS_TEST_COUNT; i++) { - printf("************** %d ****************** \n", i); - test_station_mode_connect(); - - test_station_mode_get_info(); - - test_station_mode_disconnect(); - } -#endif - - /* softap mode */ -#if (STRESS_TEST & TEST_SOFTAP_MAC) - for (int i=0; i < STRESS_TEST_COUNT; i++) { - printf("************** %d ****************** \n", i); - - test_set_wifi_mode_softap(); - - test_softap_mode_set_mac_addr_of_esp(); - - test_softap_mode_get_mac_addr(); - } -#endif - -#if (STRESS_TEST & TEST_SOFTAP_START_STOP) - for (int i=0; i < STRESS_TEST_COUNT; i++) { - printf("************** %d ****************** \n", i); - test_softap_mode_start(); - - test_softap_mode_get_info(); - - printf("Connect station to softAP : %s within 15 seconds\n", SOFTAP_MODE_SSID); - - sleep(15); - - test_softap_mode_connected_clients_info(); - - test_softap_mode_stop(); - - } -#endif - - /* station + softap mode*/ -#if (STRESS_TEST & TEST_STATION_SOFTAP_MODE) - for (int i=0; i < STRESS_TEST_COUNT; i++) { - printf("************** %d ****************** \n", i); - - test_set_wifi_mode_station_softap(); - - test_get_available_wifi(); - - test_station_mode_connect(); - - test_softap_mode_start(); - - test_station_mode_get_info(); - - test_softap_mode_get_info(); - - test_station_mode_disconnect(); - - test_softap_mode_stop(); - } -#endif - - /* power save mode */ -#if (STRESS_TEST & TEST_POWER_SAVE) - for (int i=0; i < STRESS_TEST_COUNT; i++) { - printf("************** %d ****************** \n", i); - - test_set_wifi_power_save_mode(); - - test_get_wifi_power_save_mode(); - } -#endif - return 0; + /* Below APIs could be used by demo application */ + char* num = argv[1]; + int ret = 0, stress_test_count = 0; + ret = control_path_platform_init(); + if (ret != SUCCESS) { + printf("EXIT!!!!\n"); + exit(0); + } + + stress_test_count = atoi(num); + printf("stoi %d\n", stress_test_count); + for (int i=2; i +#include +#include +#include #include "test_api.h" -void test_get_wifi_mode() +#define MAC_LEN 6 +#define MAC_STR_LEN 17 + +#define STA_INTERFACE "ethsta0" +#define AP_INTERFACE "ethap0" +#define MAX_INTERFACE_LEN 10 + +// Function converts mac string to byte stream +static int convert_mac_to_bytes(uint8_t *out, size_t out_len, char *s) +{ + int mac[MAC_LEN] = {0}; + int num_bytes = 0; + if (!s || (strlen(s) < MAC_STR_LEN) || (out_len < MAC_LEN)) { + return FAILURE; + } + num_bytes = sscanf(s, "%2x:%2x:%2x:%2x:%2x:%2x", + &mac[0],&mac[1], &mac[2], &mac[3], &mac[4], &mac[5]); + if ((num_bytes < MAC_LEN) || + (mac[0] > 0xFF) || + (mac[1] > 0xFF) || + (mac[2] > 0xFF) || + (mac[3] > 0xFF) || + (mac[4] > 0xFF) || + (mac[5] > 0xFF)) { + return FAILURE; + } + out[0] = mac[0]&0xff; + out[1] = mac[1]&0xff; + out[2] = mac[2]&0xff; + out[3] = mac[3]&0xff; + out[4] = mac[4]&0xff; + out[5] = mac[5]&0xff; + return SUCCESS; +} + +// Function ups in given interface +static int interface_up(int sockfd, char* iface) +{ + int ret = SUCCESS; + struct ifreq req = {0}; + size_t if_name_len = strnlen(iface, MAX_INTERFACE_LEN); + if (if_name_len < sizeof(req.ifr_name)) { + memcpy(req.ifr_name,iface,if_name_len); + req.ifr_name[if_name_len]='\0'; + } else { + printf("Failed: Max interface length allowed is %ld \n", sizeof(req.ifr_name)); + return FAILURE; + } + req.ifr_flags |= IFF_UP; + ret = ioctl(sockfd, SIOCSIFFLAGS, &req); + if (ret < 0) { + return FAILURE; + } + return SUCCESS; +} + +// Function downs in given interface +static int interface_down(int sockfd, char* iface) +{ + int ret = SUCCESS; + struct ifreq req = {0}; + size_t if_name_len = strnlen(iface, MAX_INTERFACE_LEN); + if (if_name_len < sizeof(req.ifr_name)) { + memcpy(req.ifr_name,iface,if_name_len); + req.ifr_name[if_name_len]='\0'; + } else { + printf("Failed: Max interface length allowed is %ld \n", sizeof(req.ifr_name)); + return FAILURE; + } + req.ifr_flags &= ~IFF_UP; + ret = ioctl(sockfd, SIOCSIFFLAGS, &req); + if (ret < 0) { + return FAILURE; + } + return SUCCESS; +} + +// Function sets mac address to given interface +static int setHWaddr(int sockfd, char* iface, char* mac) +{ + int ret = SUCCESS; + struct ifreq req = {0}; + char mac_bytes[MAC_LEN] = ""; + size_t if_name_len = strnlen(iface, MAX_INTERFACE_LEN); + if (if_name_len < sizeof(req.ifr_name)) { + memcpy(req.ifr_name,iface,if_name_len); + req.ifr_name[if_name_len]='\0'; + } else { + printf("Failed: Max interface length allowed is %ld \n", sizeof(req.ifr_name)); + return FAILURE; + } + ret = convert_mac_to_bytes((uint8_t *)&mac_bytes, sizeof(mac_bytes), mac); + if (ret) { + printf("Failed to convert mac address \n"); + return FAILURE; + } + req.ifr_hwaddr.sa_family = true; + strncpy(req.ifr_hwaddr.sa_data, mac_bytes, MAC_LEN); + req.ifr_hwaddr.sa_data[MAC_LEN-1] = '\0'; + ret = ioctl(sockfd, SIOCSIFHWADDR, &req); + if (ret < 0) { + return FAILURE; + } + return SUCCESS; +} + +int test_get_wifi_mode() { int mode = 0; int ret = wifi_get_mode(&mode); +#ifdef TEST_DEBUG_PRINTS printf("==== %s =>\n",__func__); if (ret == SUCCESS) { printf("wifi mode is %d \n",mode); @@ -30,6 +141,8 @@ void test_get_wifi_mode() printf("Failed to get wifi mode \n"); } printf("====\n\n"); +#endif + return SUCCESS; } int test_set_wifi_mode(int mode) @@ -45,7 +158,7 @@ int test_set_wifi_mode(int mode) } printf("====\n\n"); #endif - return ret; + return SUCCESS; } int test_set_wifi_mode_none() @@ -68,10 +181,11 @@ int test_set_wifi_mode_station_softap() return test_set_wifi_mode(WIFI_MODE_APSTA); } -void test_station_mode_get_mac_addr() +int test_station_mode_get_mac_addr() { char mac[MAC_LENGTH] = ""; int ret = SUCCESS; +#ifdef TEST_DEBUG_PRINTS printf("==== %s =>\n",__func__); ret = wifi_get_mac(WIFI_MODE_STA, mac); if (ret == SUCCESS) { @@ -80,6 +194,8 @@ void test_station_mode_get_mac_addr() printf("Failed to get station mode MAC address \n"); } printf("====\n\n"); +#endif + return SUCCESS; } int test_station_mode_set_mac_addr_of_esp() @@ -97,7 +213,7 @@ int test_station_mode_set_mac_addr_of_esp() printf("====\n\n"); #endif - return ret; + return SUCCESS; } int test_softap_mode_set_mac_addr_of_esp() @@ -114,14 +230,14 @@ int test_softap_mode_set_mac_addr_of_esp() } printf("====\n\n"); #endif - - return ret; + return SUCCESS; } -void test_softap_mode_get_mac_addr() +int test_softap_mode_get_mac_addr() { char mac[MAC_LENGTH] = ""; int ret = wifi_get_mac(WIFI_MODE_AP, mac); +#ifdef TEST_DEBUG_PRINTS printf("==== %s =>\n",__func__); if (ret == SUCCESS) { printf("Softap mode: mac address %s \n", mac); @@ -129,11 +245,15 @@ void test_softap_mode_get_mac_addr() printf("Failed to get softap mode MAC address \n"); } printf("====\n\n"); +#endif + return SUCCESS; } int test_station_mode_connect() { - int ret = SUCCESS; + int ret = SUCCESS, sockfd = 0; + char mac[MAC_LENGTH] = ""; + esp_hosted_control_config_t config = {0}; strcpy((char *)&config.station.ssid , STATION_MODE_SSID); @@ -143,10 +263,9 @@ int test_station_mode_connect() config.station.listen_interval = STATION_MODE_LISTEN_INTERVAL; ret = wifi_set_ap_config(config); -#ifdef TEST_DEBUG_PRINTS printf("==== %s =>\n",__func__); if (ret == SUCCESS) { - printf("Connected to AP \n"); + printf("Connected to AP: '%s'\n",STATION_MODE_SSID); } else if (ret == NO_AP_FOUND){ printf("SSID: %s not found \n",(char *)&config.station.ssid); } else if (ret == INVALID_PASSWORD){ @@ -155,9 +274,47 @@ int test_station_mode_connect() } else { printf("Failed to connect with AP \n"); } + + sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + if (sockfd < 0) { + printf("Failure to open socket\n"); + return FAILURE; + } + + ret = interface_down(sockfd, STA_INTERFACE); + if (ret == SUCCESS) { + printf("%s interface down\n", STA_INTERFACE); + } else { + printf("Unable to down %s interface\n", STA_INTERFACE); + return FAILURE; + } + + ret = wifi_get_mac(WIFI_MODE_STA, mac); + if (ret == SUCCESS) { + printf("Station mode: mac address %s \n", mac); + } else { + printf("Failed to get station mode MAC address \n"); + return FAILURE; + } + + ret = setHWaddr(sockfd, STA_INTERFACE, mac); + if (ret == SUCCESS) { + printf("MAC address %s set to %s interface\n", mac, STA_INTERFACE); + } else { + printf("Unable to set MAC address to %s interface\n", STA_INTERFACE); + return FAILURE; + } + + ret = interface_up(sockfd, STA_INTERFACE); + if (ret == SUCCESS) { + printf("%s interface up\n", STA_INTERFACE); + } else { + printf("Unable to up %s interface\n", STA_INTERFACE); + return FAILURE; + } + printf("====\n\n"); -#endif - return ret; + return SUCCESS; } int test_station_mode_get_info() @@ -178,10 +335,10 @@ int test_station_mode_get_info() } printf("====\n\n"); - return ret; + return SUCCESS; } -void test_get_available_wifi() +int test_get_available_wifi() { int count = 0; esp_hosted_wifi_scanlist_t *list = NULL; @@ -191,9 +348,11 @@ void test_get_available_wifi() list = wifi_ap_scan_list(&count); if (!count) { printf("No AP found \n"); + return FAILURE; } if (!list) { printf("Failed to get scanned AP list \n"); + return FAILURE; } else { printf("Number of available APs is %d \n", count); for (i=0; i\n",__func__); if (ret == SUCCESS) { printf("Disconnected from AP \n"); } else { printf("Failed to disconnect from AP \n"); + return FAILURE; + } + + sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + if (sockfd < 0) { + printf("Failure to open socket\n"); + return sockfd; + } + + ret = interface_down(sockfd, STA_INTERFACE); + if (ret == SUCCESS) { + printf("%s interface down\n", STA_INTERFACE); + } else { + printf("Unable to down %s interface\n", STA_INTERFACE); + return FAILURE; } printf("====\n\n"); -#endif - return ret; + return SUCCESS; } int test_softap_mode_start() { - int ret = SUCCESS; + int ret = SUCCESS, sockfd = 0; + char mac[MAC_LENGTH] = ""; esp_hosted_control_config_t config = {0}; strcpy((char *)&config.softap.ssid, SOFTAP_MODE_SSID); @@ -239,16 +414,54 @@ int test_softap_mode_start() config.softap.bandwidth = SOFTAP_MODE_BANDWIDTH; ret = wifi_set_softap_config(config); -#ifdef TEST_DEBUG_PRINTS printf("==== %s =>\n",__func__); if (ret == SUCCESS) { printf("esp32 softAP started \n"); } else { printf("Failed to set softAP config \n"); + return FAILURE; + } + + sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + if (sockfd < 0) { + printf("Failure to open socket\n"); + return FAILURE; + } + + ret = interface_down(sockfd, AP_INTERFACE); + if (ret == SUCCESS) { + printf("%s interface down\n", AP_INTERFACE); + } else { + printf("Unable to down %s interface\n", AP_INTERFACE); + return FAILURE; + } + + ret = wifi_get_mac(WIFI_MODE_AP, mac); + if (ret == SUCCESS) { + printf("softAP mode: mac address %s \n", mac); + } else { + printf("Failed to get softAP mode MAC address \n"); + return FAILURE; + } + + ret = setHWaddr(sockfd, AP_INTERFACE, mac); + if (ret == SUCCESS) { + printf("MAC address %s set to %s interface\n", mac, AP_INTERFACE); + } else { + printf("Unable to set MAC address to %s interface\n", AP_INTERFACE); + return FAILURE; + } + + ret = interface_up(sockfd, AP_INTERFACE); + if (ret == SUCCESS) { + printf("%s interface up\n", AP_INTERFACE); + } else { + printf("Unable to up %s interface\n", AP_INTERFACE); + return FAILURE; } + printf("====\n\n"); -#endif - return ret; + return SUCCESS; } int test_softap_mode_get_info() @@ -269,10 +482,10 @@ int test_softap_mode_get_info() } printf("====\n\n"); - return ret; + return SUCCESS; } -void test_softap_mode_connected_clients_info() +int test_softap_mode_connected_clients_info() { int count = 0; esp_hosted_wifi_connected_stations_list *stations_list = NULL; @@ -295,22 +508,36 @@ void test_softap_mode_connected_clients_info() stations_list = NULL; } printf("====\n\n"); + return 0; } int test_softap_mode_stop() { - int ret = wifi_stop_softap(); -#ifdef TEST_DEBUG_PRINTS + int ret = SUCCESS, sockfd = 0; + ret = wifi_stop_softap(); printf("==== %s =>\n",__func__); if (ret == SUCCESS) { printf("ESP32 softAP stopped \n"); } else { printf("Failed to stop ESP32 softAP \n"); + return FAILURE; + } + sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + if (sockfd < 0) { + printf("Failure to open socket\n"); + return sockfd; } - printf("====\n\n"); -#endif - return ret; + ret = interface_down(sockfd, AP_INTERFACE); + if (ret == SUCCESS) { + printf("%s interface down\n", AP_INTERFACE); + } else { + printf("Unable to down %s interface\n", AP_INTERFACE); + return FAILURE; + } + + printf("====\n\n"); + return SUCCESS; } @@ -329,13 +556,14 @@ int test_set_wifi_power_save_mode() printf("====\n\n"); #endif - return ret; + return SUCCESS; } int test_get_wifi_power_save_mode() { int power_save_mode = WIFI_PS_MIN_MODEM; int ret = wifi_get_power_save_mode(&power_save_mode); + #ifdef TEST_DEBUG_PRINTS printf("==== %s =>\n",__func__); if (ret == SUCCESS) { @@ -346,5 +574,5 @@ int test_get_wifi_power_save_mode() printf("====\n\n"); #endif - return ret; + return SUCCESS; } diff --git a/host/linux/host_control/c_support/test_api.h b/host/linux/host_control/c_support/test_api.h index dc63b97bf1..d3d4e1894f 100644 --- a/host/linux/host_control/c_support/test_api.h +++ b/host/linux/host_control/c_support/test_api.h @@ -27,7 +27,7 @@ #include "platform_wrapper.h" #include "test_config.h" -void test_get_wifi_mode(); +int test_get_wifi_mode(); int test_set_wifi_mode(int mode); @@ -43,15 +43,15 @@ int test_station_mode_set_mac_addr_of_esp(); int test_softap_mode_set_mac_addr_of_esp(); -void test_station_mode_get_mac_addr(); +int test_station_mode_get_mac_addr(); -void test_softap_mode_get_mac_addr(); +int test_softap_mode_get_mac_addr(); int test_station_mode_connect(); int test_station_mode_get_info(); -void test_get_available_wifi(); +int test_get_available_wifi(); int test_station_mode_disconnect(); @@ -59,7 +59,7 @@ int test_softap_mode_start(); int test_softap_mode_get_info(); -void test_softap_mode_connected_clients_info(); +int test_softap_mode_connected_clients_info(); int test_softap_mode_stop(); diff --git a/host/linux/host_control/c_support/test_config.h b/host/linux/host_control/c_support/test_config.h index 84e5b97b75..75ddb6c256 100644 --- a/host/linux/host_control/c_support/test_config.h +++ b/host/linux/host_control/c_support/test_config.h @@ -21,6 +21,13 @@ #ifndef __TEST_CONFIG_H #define __TEST_CONFIG_H +#define STA_CONNECT "sta_connect" +#define STA_DISCONNECT "sta_disconnect" +#define AP_START "ap_start" +#define AP_STOP "ap_stop" +#define SCAN "scan" +#define STA_LIST "sta_list" + #define MAC_LENGTH 18 #define SSID_LENGTH 32 #define PWD_LENGTH 64 diff --git a/host/linux/host_control/python_support/connected_stations_list.py b/host/linux/host_control/python_support/connected_stations_list.py index e793c1f110..70a33149df 100644 --- a/host/linux/host_control/python_support/connected_stations_list.py +++ b/host/linux/host_control/python_support/connected_stations_list.py @@ -26,6 +26,7 @@ wifi_mode_softap = 2 wifi_mode_station_softap = 3 failure = "failure" +get_mode = 'not_set' stations_list = "No station is connected" parser = argparse.ArgumentParser(description='connected_stations_list.py is a python script which gives list of mac addresses of stations connected to softAP. ex. python connected_stations_list.py') diff --git a/host/linux/host_control/python_support/softap_config.py b/host/linux/host_control/python_support/softap_config.py index 698491908c..1e018e266f 100644 --- a/host/linux/host_control/python_support/softap_config.py +++ b/host/linux/host_control/python_support/softap_config.py @@ -31,7 +31,8 @@ success = 'success' failure = 'failure' flag = success -softap_config = 'Not set' +ap_mac = 'not_set' +softap_config = 'not_set' parser = argparse.ArgumentParser(description='softap_config.py script to configure ESP32 softAP mode. ex. python softap_config.py \'xyz\' \'xyz123456\' 1 3 --max_conn=4 --ssid_hidden=0 --bw=1') @@ -81,6 +82,4 @@ os.system(command) print(command) - time.sleep(1) - print("SoftAP config successfully set") diff --git a/host/linux/host_control/python_support/softap_stop.py b/host/linux/host_control/python_support/softap_stop.py index 112986ad17..4b0032346e 100644 --- a/host/linux/host_control/python_support/softap_stop.py +++ b/host/linux/host_control/python_support/softap_stop.py @@ -28,18 +28,19 @@ wifi_mode_station_softap = 3 failure = "failure" success = "success" -stop = "Not set" +wifi_mode = 'not_set' +stop_softap = 'not_set' flag = success parser = argparse.ArgumentParser(description='softap_stop.py script will stop ESP32 softap ex. python softap_close.py') wifi_mode = wifi_get_mode() -print("WiFi Mode: "+str(wifi_mode)) if (wifi_mode == failure): print("Failed to get wifi mode") flag = failure else: + print("WiFi Mode: "+str(wifi_mode)) stop_softap = wifi_stop_softap() if (stop_softap != success): print("Failed to stop softAP") diff --git a/host/linux/host_control/python_support/station_connect.py b/host/linux/host_control/python_support/station_connect.py index 77d43dcdb1..727b19aaef 100644 --- a/host/linux/host_control/python_support/station_connect.py +++ b/host/linux/host_control/python_support/station_connect.py @@ -32,7 +32,8 @@ failure = "failure" success = "success" flag = success -station_status = 'Nothing set' +sta_mac = 'not_set' +station_status = 'not_set' parser = argparse.ArgumentParser(description='station_connect.py is a python script which connect ESP32 station to AP. ex. python station_connect.py \'xyz\' \'xyz123456\' --bssid=\'e5:6c:67:3c:cf:65\' --is_wpa3_supported=True --listen_interval=3') @@ -63,8 +64,8 @@ print("SSID: "+args.ssid+" not found") elif (station_status == invalid_password_str): print("Incorrect Password: "+args.password) - print("Failed to set AP config") - elif (station_status == success): + print("Failed to connect with AP") + else: print("Connected to "+args.ssid) if (flag != success): @@ -82,30 +83,4 @@ os.system(command) print(command) - time.sleep(1) - - for x in range (5): - command = 'sudo dhclient ethsta0 -r' - os.system(command) - print(command) - - command = 'sudo dhclient ethsta0 -v' - os.system(command) - print(command) - - try: - get_ip = subprocess.check_output('ip addr show | grep "ethsta0" | grep -o "inet [0-9]*\.[0-9]*\.[0-9]*\.[0-9]*" | grep -o "[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*"', shell = True) - if get_ip: - flag = success - break - except subprocess.CalledProcessError: - time.sleep(0.1) - flag = failure - - if (flag == failure): - print("Failed to assign IP address to ethsta0 interface") - else: - print("Success in setting AP config") - - - + print("Interface ethsta0 is up with MAC address "+str(sta_mac)) diff --git a/host/linux/host_control/python_support/station_disconnect.py b/host/linux/host_control/python_support/station_disconnect.py index 2693457c36..90b4e8bd76 100644 --- a/host/linux/host_control/python_support/station_disconnect.py +++ b/host/linux/host_control/python_support/station_disconnect.py @@ -28,8 +28,9 @@ wifi_mode_station_softap = 3 failure = "failure" success = "success" +wifi_mode = 'not_set' +disconnect = "not_set" flag = success -disconnect = "Not set" parser = argparse.ArgumentParser(description='station_disconnect.py script will disconnect ESPStation from AP ex. python station_disconnect.py') @@ -50,11 +51,6 @@ flag = failure if (flag == success): - command = 'sudo dhclient ethsta0 -r' - print('$ '+command) - os.system(command) - command = 'sudo ifconfig ethsta0 down' print('$ '+command) os.system(command) - diff --git a/host/linux/host_control/python_support/stress.py b/host/linux/host_control/python_support/stress.py index 6fa40f2c9e..d964291a9a 100644 --- a/host/linux/host_control/python_support/stress.py +++ b/host/linux/host_control/python_support/stress.py @@ -28,7 +28,8 @@ STRESS_TEST=(TEST_MODE_NONE | TEST_SCAN_WIFI | TEST_STATION_MAC | TEST_STATION_CONNECT_DISCONNECT | TEST_SOFTAP_MAC | TEST_SOFTAP_START_STOP | TEST_STATION_SOFTAP_MODE | TEST_POWER_SAVE) -# Test APIs +#***** Please Read ***** +#* Before use stress.py : User must enter user configuration parameter in "test_config.py" file * # Below APIs could be used by demo application diff --git a/host/linux/host_control/python_support/test.py b/host/linux/host_control/python_support/test.py index 5ed0e571ac..41ebcf5be1 100644 --- a/host/linux/host_control/python_support/test.py +++ b/host/linux/host_control/python_support/test.py @@ -13,6 +13,9 @@ # limitations under the License. from test_api import * +#***** Please Read ***** +#* Before use test.py : User must enter user configuration parameter in "test_config.py" file * + # Test APIs # Below APIs could be used by demo application diff --git a/host/linux/host_control/python_support/test_api.py b/host/linux/host_control/python_support/test_api.py index a0a6918363..778415b07f 100644 --- a/host/linux/host_control/python_support/test_api.py +++ b/host/linux/host_control/python_support/test_api.py @@ -14,6 +14,63 @@ from commands import * from test_config import * +from socket import * +from struct import * +from fcntl import * + +sta_interface = 'ethsta0' +ap_interface = 'ethap0' + +# From linux/socket.h +AF_UNIX = 1 + +# From linux/if.h +IFF_UP = 0x1 + +# From linux/sockios.h +SIOCGIFFLAGS = 0x8913 +SIOCSIFFLAGS = 0x8914 +SIOCSIFHWADDR = 0x8924 + +# Macros for packing data in 16 bytes short character bytes +PACK_FORMAT = '16sh' + +def get_bytes(string): + if sys.version_info >= (3, 0): + return bytes(string, 'utf-8') + else: + return string + +def interface_down(sockfd, iface): + ifreq = pack(PACK_FORMAT, get_bytes(iface), 0) + flags = unpack(PACK_FORMAT, ioctl(sockfd, SIOCGIFFLAGS, ifreq))[1] + flags = flags & ~IFF_UP + ifreq = pack(PACK_FORMAT, get_bytes(iface), flags) + ret = ioctl(sockfd, SIOCSIFFLAGS, ifreq) + if (not ret): + return failure + else: + return success + +def interface_up(sockfd, iface): + ifreq = pack(PACK_FORMAT, get_bytes(iface), 0) + flags = unpack(PACK_FORMAT, ioctl(sockfd, SIOCGIFFLAGS, ifreq))[1] + flags = flags | IFF_UP + ifreq = pack(PACK_FORMAT, get_bytes(iface), flags) + ret = ioctl(sockfd, SIOCSIFFLAGS, ifreq) + if (not ret): + return failure + else: + return success + +def setHWaddr(sockfd, iface, mac): + macbytes = [int(i, 16) for i in mac.split(':')] + ifreq = pack('16sH6B8x', get_bytes(sta_interface), AF_UNIX, *macbytes) + ret = ioctl(sockfd, SIOCSIFHWADDR, ifreq) + if (not ret): + return failure + else: + return success def test_get_wifi_mode(): mode = wifi_get_mode() @@ -79,13 +136,48 @@ def test_softap_mode_set_mac_addr_of_esp(): print("SoftAP MAC address is not set") return + def test_station_mode_connect(): ret = wifi_set_ap_config(STATION_MODE_SSID, STATION_MODE_PWD, STATION_MODE_BSSID,\ STATION_MODE_IS_WPA3_SUPPORTED, STATION_MODE_LISTEN_INTERVAL) - if (ret == failure): + if (ret != failure): + print("Connected to "+STATION_MODE_SSID) + else: print("Failed to connect to AP") + return + + sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP) + if (not sockfd): + print("Failed to open socket") + return + + ret = interface_down(sockfd, sta_interface) + if (ret != failure): + print(sta_interface+" interface down") else: - print(ret) + print("Unable to down "+sta_interface+" interface") + return + + mac = wifi_get_mac(WIFI_MODE_STATION) + if (mac != failure): + print("Station mode: mac address "+str(mac)) + else: + print("Failed to get MAC address") + return + + ret = setHWaddr(sockfd, sta_interface, mac) + if (ret != failure): + print("MAC address "+mac+" set to "+sta_interface+" interface") + else: + print("Unable to set MAC address to "+sta_interface) + return + + ret = interface_up(sockfd, sta_interface) + if (ret != failure): + print(sta_interface+" interface up") + else: + print("Unable to up "+sta_interface+" interface") + return def test_station_mode_get_info(): ret = wifi_get_ap_config() @@ -116,6 +208,18 @@ def test_station_mode_disconnect(): print("Disconnected from AP") else: print("Failed to disconnect from AP") + return + + sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP) + if (not sockfd): + print("Failed to open socket") + return + + ret = interface_down(sockfd, sta_interface) + if (ret != failure): + print(sta_interface+" interface down") + else: + print("Unable to down "+sta_interface+" interface") return def test_softap_mode_start(): @@ -126,6 +230,39 @@ def test_softap_mode_start(): print("ESP32 softAP started") else: print("Failed to set softAP config") + return + + sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP) + if (not sockfd): + print("Failed to open socket") + return + + ret = interface_down(sockfd, ap_interface) + if (ret != failure): + print(ap_interface+" interface down") + else: + print("Unable to down "+ap_interface+" interface") + return + + mac = wifi_get_mac(WIFI_MODE_SOFTAP) + if (mac != failure): + print("SoftAP mode: mac address "+str(mac)) + else: + print("Failed to get MAC address of softAP interface") + return + + ret = setHWaddr(sockfd, ap_interface, mac) + if (ret != failure): + print("MAC address "+mac+" set to "+ap_interface+" interface") + else: + print("Unable to set MAC address to "+ap_interface) + return + + ret = interface_up(sockfd, ap_interface) + if (ret != failure): + print(ap_interface+" interface up") + else: + print("Unable to up "+ap_interface+" interface") return def test_softap_mode_get_info(): @@ -159,6 +296,18 @@ def test_softap_mode_stop(): print("ESP32 softAP stopped") else: print("Failed to stop ESP32 softAP") + return + + sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP) + if (not sockfd): + print("Failed to open socket") + return + + ret = interface_down(sockfd, ap_interface) + if (ret != failure): + print(ap_interface+" interface down") + else: + print("Unable to down "+ap_interface+" interface") return def test_set_wifi_power_save_mode(): @@ -175,4 +324,5 @@ def test_get_wifi_power_save_mode(): print("Power save mode is "+str(power_save_mode)) else: print("Failed to set power save mode") + return From d53a8a4abe6387282c26743af88d486d0cedcdb5 Mon Sep 17 00:00:00 2001 From: Yogesh Mantri Date: Thu, 26 Aug 2021 22:32:42 +0530 Subject: [PATCH 05/40] UART + SPI config added --- docs/Linux_based_host/Linux_based_readme.md | 17 ++++++++++++ docs/Linux_based_host/SPI_setup.md | 14 +++++----- docs/Linux_based_host/UART_setup.md | 9 ++++--- .../network_adapter/main/Kconfig.projbuild | 8 +++++- .../network_adapter/main/app_main.c | 15 +++++++++-- .../network_adapter/main/spi_slave_api.c | 3 +-- .../network_adapter/sdkconfig.defaults | 1 + .../network_adapter/sdkconfig.defaults.esp32 | 27 +++++++++++++++++++ host/linux/host_driver/esp32/esp_bt.c | 1 + host/linux/host_driver/esp32/main.c | 1 + host/linux/host_driver/esp32/spi/esp_spi.h | 2 +- 11 files changed, 81 insertions(+), 17 deletions(-) create mode 100644 esp/esp_driver/network_adapter/sdkconfig.defaults.esp32 diff --git a/docs/Linux_based_host/Linux_based_readme.md b/docs/Linux_based_host/Linux_based_readme.md index da15e44ee3..fac6825e2b 100644 --- a/docs/Linux_based_host/Linux_based_readme.md +++ b/docs/Linux_based_host/Linux_based_readme.md @@ -107,6 +107,23 @@ Prepare connections based on interface requirements and setup host as below. * After loading ESP firmware, execute below command to create `hci0` interface ```sh $ sudo hciattach -s 921600 /dev/serial0 any 921600 flow + +* **Wifi over SPI and Bluetooth over UART** + * Connection Setup + * Prepare connections as per section [1.1 Hardware Setup](SPI_setup.md#11-hardware-setup) of [SPI Setup document](SPI_setup.md) + * Prepare UART connections as per section [1.1 Hardware Setup](UART_setup.md#11-hardware-setup) of [UART setup document](UART_setup.md) + * Host Software + * Prepare Raspberry-Pi as per [1.2 Raspberry-Pi Software Setup](SPI_setup.md#12-raspberry-pi-software-setup) of [SPI Setup document](SPI_setup.md) + * Prepare Raspberry-Pi for UART operations as per section [1.2 Raspberry-Pi Software Setup](UART_setup.md#12-raspberry-pi-software-setup) of [UART setup document](UART_setup.md) + * Compile and load host driver as below: + ```sh + $ cd host/linux/host_control/ + $ ./rpi_init.sh spi btuart + ``` + * After loading ESP firmware, execute below command to create `hci0` interface + ```sh + $ sudo hciattach -s 921600 /dev/serial0 any 921600 flow + ``` ``` #### 1.3.1 ESP Firmware Setup diff --git a/docs/Linux_based_host/SPI_setup.md b/docs/Linux_based_host/SPI_setup.md index b115018814..4902d16f2a 100644 --- a/docs/Linux_based_host/SPI_setup.md +++ b/docs/Linux_based_host/SPI_setup.md @@ -8,12 +8,12 @@ Raspberry-Pi pinout can be found [here!](https://pinout.xyz/pinout/spi) #### 1.1.1 ESP32 setup | Raspberry-Pi Pin | ESP32 Pin | Function | |:-------:|:---------:|:--------:| -| 24 | IO5 | CS0 | -| 23 | IO18 | SCLK | -| 21 | IO19 | MISO | -| 19 | IO23 | MOSI | +| 24 | IO15 | CS0 | +| 23 | IO14 | SCLK | +| 21 | IO12 | MISO | +| 19 | IO13 | MOSI | | 25 | GND | Ground | -| 11 | IO2 | Handshake | +| 15 | IO2 | Handshake | | 13 | IO4 | Data Ready | | 31 | EN | ESP32 Reset | @@ -29,7 +29,7 @@ Setup image is here. | 21 | IO13 | MISO | | 19 | IO11 | MOSI | | 25 | GND | Ground | -| 11 | IO2 | Handshake | +| 15 | IO2 | Handshake | | 13 | IO4 | Data ready | | 31 | RST | ESP32 Reset | @@ -45,7 +45,7 @@ Setup image is here. | 21 | IO02 | MISO | | 19 | IO07 | MOSI | | 25 | GND | Ground | -| 11 | IO03 | Handshake | +| 15 | IO03 | Handshake | | 13 | IO04 | Data ready | | 31 | RST | ESP32 Reset | diff --git a/docs/Linux_based_host/UART_setup.md b/docs/Linux_based_host/UART_setup.md index c5eb80a79b..67fd2e1521 100644 --- a/docs/Linux_based_host/UART_setup.md +++ b/docs/Linux_based_host/UART_setup.md @@ -60,15 +60,16 @@ One can load pre-built release binaries on ESP peripheral or compile those from ```sh $ python esptool.py --chip esp32 --port --baud 960000 --before default_reset \ --after hard_reset write_flash -z --flash_mode dio --flash_freq 40m --flash_size detect \ -0x1000 esp_hosted_bootloader_esp32_sdio_uart_v.bin \ -0x10000 esp_hosted_firmware_esp32_sdio_uart_v.bin \ -0x8000 esp_hosted_partition-table_esp32_sdio_uart_v.bin +0x1000 esp_hosted_bootloader_esp32__uart_v.bin \ +0x10000 esp_hosted_firmware_esp32__uart_v.bin \ +0x8000 esp_hosted_partition-table_esp32__uart_v.bin Where, : serial port of ESP peripheral : 0.1,0.2 etc. Latest from [release page](https://github.com/espressif/esp-hosted/releases) + : sdio or spi ``` -* This command will flash `SDIO+UART` interface binaries on `esp32` chip. +* This command will flash `SDIO+UART` or `SPI+UART` interface binaries on `esp32` chip. * Windows user can use ESP Flash Programming Tool to flash the pre-built binary. diff --git a/esp/esp_driver/network_adapter/main/Kconfig.projbuild b/esp/esp_driver/network_adapter/main/Kconfig.projbuild index 467c50f489..5860428ff6 100644 --- a/esp/esp_driver/network_adapter/main/Kconfig.projbuild +++ b/esp/esp_driver/network_adapter/main/Kconfig.projbuild @@ -25,7 +25,7 @@ menu "Example Configuration" config ESP_SPI_CONTROLLER int "SPI controller to use" depends on IDF_TARGET_ESP32 && ESP_SPI_HOST_INTERFACE - default 3 + default 2 range 2 3 help SPI controller to be used. HSPI->2, VSPI->3 @@ -56,4 +56,10 @@ menu "Example Configuration" help Enable/disable debug prints in wlan driver data path + config ESP_BT_DEBUG + bool "Debug Bluetooth driver data path" + default 0 + help + Enable/disable debug prints in Bluetooth driver data path + endmenu diff --git a/esp/esp_driver/network_adapter/main/app_main.c b/esp/esp_driver/network_adapter/main/app_main.c index c58eb43deb..d3d1136dc8 100644 --- a/esp/esp_driver/network_adapter/main/app_main.c +++ b/esp/esp_driver/network_adapter/main/app_main.c @@ -347,7 +347,7 @@ void send_task(void* pvParameters) } } - sleep(1); + usleep(100*1000); } } } @@ -394,6 +394,9 @@ void process_rx_task(void* pvParameters) } else if (buf_handle.if_type == ESP_HCI_IF) { /* VHCI needs one extra byte at the start of payload */ /* that is accomodated in esp_payload_header */ +#if CONFIG_ESP_BT_DEBUG + ESP_LOG_BUFFER_HEXDUMP("bt_rx", payload, payload_len, ESP_LOG_INFO); +#endif payload--; payload_len++; @@ -429,7 +432,7 @@ void recv_task(void* pvParameters) if (!datapath) { /* Datapath is not enabled by host yet*/ - sleep(1); + usleep(100*1000); continue; } @@ -529,6 +532,10 @@ static int host_rcv_pkt(uint8_t *data, uint16_t len) buf_handle.wlan_buf_handle = buf; buf_handle.free_buf_handle = free; +#if CONFIG_ESP_BT_DEBUG + ESP_LOG_BUFFER_HEXDUMP("bt_tx", data, len, ESP_LOG_INFO); +#endif + ret = xQueueSend(to_host_queue, &buf_handle, portMAX_DELAY); if (ret != pdTRUE) { @@ -551,6 +558,10 @@ static esp_err_t initialise_bluetooth(void) esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); #ifdef CONFIG_BT_HCI_UART_NO +#if CONFIG_ESP_BT_DEBUG + ESP_LOGI(TAG, "UART Pins: TX:%u Rx:%u RTS:%u CTS:%u\n", + BT_TX_PIN, BT_RX_PIN, BT_RTS_PIN, BT_CTS_PIN); +#endif #if CONFIG_BT_HCI_UART_NO == 1 periph_module_enable(PERIPH_UART1_MODULE); #elif CONFIG_BT_HCI_UART_NO == 2 diff --git a/esp/esp_driver/network_adapter/main/spi_slave_api.c b/esp/esp_driver/network_adapter/main/spi_slave_api.c index 039d7f34b3..7ea0f3b058 100644 --- a/esp/esp_driver/network_adapter/main/spi_slave_api.c +++ b/esp/esp_driver/network_adapter/main/spi_slave_api.c @@ -152,7 +152,7 @@ int interface_remove_driver() static int is_valid_trans_buffer(uint8_t *trans_buf) { struct esp_payload_header *header = NULL; - uint16_t len = 0, offset = 0; + uint16_t len = 0; if (!trans_buf) { return pdFALSE; @@ -161,7 +161,6 @@ static int is_valid_trans_buffer(uint8_t *trans_buf) header = (struct esp_payload_header *) trans_buf; len = le16toh(header->len); - offset = le16toh(header->offset); if (!len || (len > SPI_BUFFER_SIZE)) { return pdFALSE; diff --git a/esp/esp_driver/network_adapter/sdkconfig.defaults b/esp/esp_driver/network_adapter/sdkconfig.defaults index 6934dabd97..241fe2f7ac 100644 --- a/esp/esp_driver/network_adapter/sdkconfig.defaults +++ b/esp/esp_driver/network_adapter/sdkconfig.defaults @@ -9,5 +9,6 @@ CONFIG_BT_CONTROLLER_ONLY=y CONFIG_BT_BLUEDROID_ENABLED= CONFIG_BTDM_CONTROLLER_MODE_BTDM=y CONFIG_BTDM_CONTROLLER_HCI_MODE_VHCI=y +CONFIG_BTDM_CTRL_AUTO_LATENCY=y CONFIG_ESP32_WIFI_NVS_ENABLED= diff --git a/esp/esp_driver/network_adapter/sdkconfig.defaults.esp32 b/esp/esp_driver/network_adapter/sdkconfig.defaults.esp32 new file mode 100644 index 0000000000..26fd63bb4d --- /dev/null +++ b/esp/esp_driver/network_adapter/sdkconfig.defaults.esp32 @@ -0,0 +1,27 @@ +CONFIG_ESP32_DEFAULT_CPU_FREQ_160=y +CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=160 +CONFIG_SDIO_DAT2_DISABLED= + +# BT Configuration +CONFIG_BT_ENABLED=y +CONFIG_BT_CONTROLLER_ONLY=y +CONFIG_BT_BLUEDROID_ENABLED= +CONFIG_BTDM_CONTROLLER_MODE_BTDM=y +CONFIG_BTDM_CONTROLLER_HCI_MODE_VHCI=y +CONFIG_BTDM_CTRL_AUTO_LATENCY=y + +CONFIG_ESP32_WIFI_NVS_ENABLED= + +# BT over UART +CONFIG_BTDM_CONTROLLER_HCI_MODE_UART_H4=y +CONFIG_BTDM_CTRL_HCI_MODE_UART_H4=y +CONFIG_BT_HCI_UART_NO=1 +CONFIG_BT_HCI_UART_BAUDRATE=921600 + +#CO-EX config +CONFIG_FREERTOS_UNICORE=n +CONFIG_FREERTOS_HZ=1000 +CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_1=y +CONFIG_BTDM_CONTROLLER_PINNED_TO_CORE=0 +CONFIG_BTDM_CTRL_PINNED_TO_CORE=0 +CONFIG_BTDM_CTRL_PINNED_TO_CORE_0=y diff --git a/host/linux/host_driver/esp32/esp_bt.c b/host/linux/host_driver/esp32/esp_bt.c index b5901a558a..74e9f84b8f 100644 --- a/host/linux/host_driver/esp32/esp_bt.c +++ b/host/linux/host_driver/esp32/esp_bt.c @@ -94,6 +94,7 @@ ESP_BT_SEND_FRAME_PROTOTYPE() printk(KERN_ERR "%s: invalid args", __func__); return -EINVAL; } + //print_hex_dump(KERN_INFO, "bt_tx: ", DUMP_PREFIX_ADDRESS, 16, 1, skb->data, len, 1 ); /* Create space for payload header */ pad_len = sizeof(struct esp_payload_header); diff --git a/host/linux/host_driver/esp32/main.c b/host/linux/host_driver/esp32/main.c index e005180019..12b2590ada 100644 --- a/host/linux/host_driver/esp32/main.c +++ b/host/linux/host_driver/esp32/main.c @@ -379,6 +379,7 @@ static void process_rx_packet(struct sk_buff *skb) skb_pull(skb, offset); type = skb->data; + //print_hex_dump(KERN_INFO, "bt_rx: ", DUMP_PREFIX_ADDRESS, 16, 1, skb->data, len, 1); hci_skb_pkt_type(skb) = *type; skb_pull(skb, 1); diff --git a/host/linux/host_driver/esp32/spi/esp_spi.h b/host/linux/host_driver/esp32/spi/esp_spi.h index e8c0697a2e..ce406a4b9b 100644 --- a/host/linux/host_driver/esp32/spi/esp_spi.h +++ b/host/linux/host_driver/esp32/spi/esp_spi.h @@ -19,7 +19,7 @@ #include "esp.h" -#define HANDSHAKE_PIN 17 +#define HANDSHAKE_PIN 22 #define SPI_IRQ gpio_to_irq(HANDSHAKE_PIN) #define SPI_DATA_READY_PIN 27 #define SPI_DATA_READY_IRQ gpio_to_irq(SPI_DATA_READY_PIN) From 54f5d2e47d0c5a837e26a613cdf590cf94d9ded9 Mon Sep 17 00:00:00 2001 From: Mangesh Malusare Date: Mon, 30 Aug 2021 03:20:34 +0530 Subject: [PATCH 06/40] Add data integrity checks -- Implement simple checksum mechanism -- Include checksum in packet header -- Drop packets if checksum doesn't match Testing: -- Overnight iperf test --- common/include/adapter.h | 34 ++++++++++++++----- .../network_adapter/main/sdio_slave_api.c | 17 ++++++++++ .../network_adapter/main/spi_slave_api.c | 14 ++++++++ host/linux/host_driver/esp32/esp_bt.c | 2 ++ host/linux/host_driver/esp32/esp_serial.c | 1 + host/linux/host_driver/esp32/main.c | 13 +++++++ 6 files changed, 72 insertions(+), 9 deletions(-) diff --git a/common/include/adapter.h b/common/include/adapter.h index 4c66df6829..378e895b01 100644 --- a/common/include/adapter.h +++ b/common/include/adapter.h @@ -5,16 +5,17 @@ #define __ESP_NETWORK_ADAPTER__H struct esp_payload_header { - uint8_t if_type:4; - uint8_t if_num:4; - uint8_t reserved1; - uint16_t len; - uint16_t offset; - uint8_t reserved2; + uint8_t if_type:4; + uint8_t if_num:4; + uint8_t reserved1; + uint16_t len; + uint16_t offset; + uint16_t checksum; + uint8_t reserved2; union { - uint8_t reserved3; - uint8_t hci_pkt_type; /* Packet type for HCI interface */ - uint8_t priv_pkt_type; /* Packet type for priv interface */ + uint8_t reserved3; + uint8_t hci_pkt_type; /* Packet type for HCI interface */ + uint8_t priv_pkt_type; /* Packet type for priv interface */ }; } __attribute__((packed)); @@ -63,4 +64,19 @@ struct esp_priv_event { uint8_t event_len; uint8_t event_data[0]; }__attribute__((packed)); + + +static inline uint16_t compute_checksum(uint8_t *buf, uint16_t len) +{ + uint16_t checksum = 0; + uint16_t i = 0; + + while(i < len) { + checksum += buf[i]; + i++; + } + + return checksum; +} + #endif diff --git a/esp/esp_driver/network_adapter/main/sdio_slave_api.c b/esp/esp_driver/network_adapter/main/sdio_slave_api.c index 07974a0164..c20d9b059b 100644 --- a/esp/esp_driver/network_adapter/main/sdio_slave_api.c +++ b/esp/esp_driver/network_adapter/main/sdio_slave_api.c @@ -184,6 +184,10 @@ static int32_t sdio_write(interface_handle_t *handle, interface_buffer_handle_t header->offset = htole16(offset); memcpy(sendbuf + offset, buf_handle->payload, buf_handle->payload_len); + + header->checksum = htole16(compute_checksum(sendbuf, + offset+buf_handle->payload_len)); + ret = sdio_slave_transmit(sendbuf, total_len); if (ret != ESP_OK) { ESP_LOGE(TAG , "sdio slave transmit error, ret : 0x%x\r\n", ret); @@ -200,6 +204,7 @@ interface_buffer_handle_t * sdio_read(interface_handle_t *if_handle) { interface_buffer_handle_t *buf_handle = NULL; struct esp_payload_header *header = NULL; + uint16_t rx_checksum = 0, checksum = 0, len; if (!if_handle) { ESP_LOGE(TAG, "Invalid arguments to sdio_read"); @@ -221,6 +226,18 @@ interface_buffer_handle_t * sdio_read(interface_handle_t *if_handle) header = (struct esp_payload_header *) buf_handle->payload; + rx_checksum = le16toh(header->checksum); + + header->checksum = 0; + len = le16toh(header->len) + le16toh(header->offset); + + checksum = compute_checksum(buf_handle->payload, len); + + if (checksum != rx_checksum) { + sdio_read_done(buf_handle->sdio_buf_handle); + return NULL; + } + buf_handle->if_type = header->if_type; buf_handle->if_num = header->if_num; buf_handle->free_buf_handle = sdio_read_done; diff --git a/esp/esp_driver/network_adapter/main/spi_slave_api.c b/esp/esp_driver/network_adapter/main/spi_slave_api.c index 7ea0f3b058..0c80c7b627 100644 --- a/esp/esp_driver/network_adapter/main/spi_slave_api.c +++ b/esp/esp_driver/network_adapter/main/spi_slave_api.c @@ -255,6 +255,7 @@ static int process_spi_rx(interface_buffer_handle_t *buf_handle) int ret = 0; struct esp_payload_header *header = NULL; uint16_t len = 0, offset = 0; + uint16_t rx_checksum = 0, checksum = 0; /* Validate received buffer. Drop invalid buffer. */ @@ -267,6 +268,15 @@ static int process_spi_rx(interface_buffer_handle_t *buf_handle) len = le16toh(header->len); offset = le16toh(header->offset); + rx_checksum = le16toh(header->checksum); + header->checksum = 0; + + checksum = compute_checksum(buf_handle->payload, len+offset); + + if (checksum != rx_checksum) { + return -1; + } + if (!len || (len > SPI_BUFFER_SIZE)) { return -1; } @@ -672,6 +682,10 @@ static int32_t esp_spi_write(interface_handle_t *handle, interface_buffer_handle /* copy the data from caller */ memcpy(tx_buf_handle.payload + offset, buf_handle->payload, buf_handle->payload_len); + + header->checksum = htole16(compute_checksum(tx_buf_handle.payload, + offset+buf_handle->payload_len)); + ret = xQueueSend(spi_tx_queue, &tx_buf_handle, portMAX_DELAY); if (ret != pdTRUE) diff --git a/host/linux/host_driver/esp32/esp_bt.c b/host/linux/host_driver/esp32/esp_bt.c index 74e9f84b8f..3cdc89f6e3 100644 --- a/host/linux/host_driver/esp32/esp_bt.c +++ b/host/linux/host_driver/esp32/esp_bt.c @@ -154,6 +154,8 @@ ESP_BT_SEND_FRAME_PROTOTYPE() /* set HCI packet type */ *(pos + pad_len - 1) = pkt_type; + hdr->checksum = cpu_to_le16(compute_checksum(skb->data, (len + pad_len))); + ret = esp_send_packet(adapter, skb); if (ret) { diff --git a/host/linux/host_driver/esp32/esp_serial.c b/host/linux/host_driver/esp32/esp_serial.c index 9b81bdf37c..91ca61e1bd 100644 --- a/host/linux/host_driver/esp32/esp_serial.c +++ b/host/linux/host_driver/esp32/esp_serial.c @@ -92,6 +92,7 @@ static ssize_t esp_serial_write(struct file *file, const char __user *user_buffe printk(KERN_ERR "%s, Error copying buffer to send serial data\n", __func__); return -EFAULT; } + hdr->checksum = cpu_to_le16(compute_checksum(tx_skb->data, (size + sizeof(struct esp_payload_header)))); ret = esp_send_packet(dev->priv, tx_skb); if (ret) { diff --git a/host/linux/host_driver/esp32/main.c b/host/linux/host_driver/esp32/main.c index 12b2590ada..80b8596977 100644 --- a/host/linux/host_driver/esp32/main.c +++ b/host/linux/host_driver/esp32/main.c @@ -299,6 +299,8 @@ static int process_tx_packet (struct sk_buff *skb) payload_header->offset = cpu_to_le16(pad_len); payload_header->reserved1 = c % 255; + payload_header->checksum = cpu_to_le16(compute_checksum(skb->data, (len + pad_len))); + if (!stop_data) { ret = esp_send_packet(priv->adapter, skb); @@ -321,6 +323,7 @@ static void process_rx_packet(struct sk_buff *skb) struct esp_private *priv = NULL; struct esp_payload_header *payload_header = NULL; u16 len = 0, offset = 0; + u16 rx_checksum = 0, checksum = 0; struct hci_dev *hdev = adapter.hcidev; u8 *type = NULL; int ret = 0, ret_len = 0; @@ -333,6 +336,16 @@ static void process_rx_packet(struct sk_buff *skb) len = le16_to_cpu(payload_header->len); offset = le16_to_cpu(payload_header->offset); + rx_checksum = le16_to_cpu(payload_header->checksum); + + payload_header->checksum = 0; + + checksum = compute_checksum(skb->data, (len + offset)); + + if (checksum != rx_checksum) { + dev_kfree_skb_any(skb); + return; + } if (payload_header->if_type == ESP_SERIAL_IF) { #ifdef CONFIG_SUPPORT_ESP_SERIAL From 877e1c534a0fe2b6a58180dffa0351d0f9cce920 Mon Sep 17 00:00:00 2001 From: "ajita.chavan" Date: Thu, 23 Sep 2021 16:12:47 +0530 Subject: [PATCH 07/40] implicit declaration error for function 'periph_module_enable' on release/v4.2 resolved Test: Successfully complied code for release/v4.0, release/v4.2, release/v4.3 Signed-off-by: ajita.chavan --- esp/esp_driver/network_adapter/main/app_main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/esp/esp_driver/network_adapter/main/app_main.c b/esp/esp_driver/network_adapter/main/app_main.c index d3d1136dc8..ffe87554db 100644 --- a/esp/esp_driver/network_adapter/main/app_main.c +++ b/esp/esp_driver/network_adapter/main/app_main.c @@ -44,6 +44,7 @@ #include #include "protocomm_pserial.h" #include "slave_commands.h" +#include "driver/periph_ctrl.h" #define EV_STR(s) "================ "s" ================" static const char TAG[] = "NETWORK_ADAPTER"; From ed60c190c12db8167f64aed1e977be51560c42a9 Mon Sep 17 00:00:00 2001 From: Yogesh Mantri Date: Tue, 5 Oct 2021 18:26:58 +0530 Subject: [PATCH 08/40] CRC check added to the data --- docs/MCU_based_host/MCU_based_readme.md | 8 ++ .../network_adapter/main/spi_slave_api.c | 2 + host/stm32/driver/spi/spi_drv.c | 76 ++++++++++++------- 3 files changed, 59 insertions(+), 27 deletions(-) diff --git a/docs/MCU_based_host/MCU_based_readme.md b/docs/MCU_based_host/MCU_based_readme.md index 48a4ffad28..6b358f43ae 100644 --- a/docs/MCU_based_host/MCU_based_readme.md +++ b/docs/MCU_based_host/MCU_based_readme.md @@ -139,6 +139,10 @@ Run following command and navigate to `Example Configuration -> Transport layer ``` $ make menuconfig ``` +:warning: Skip below step for ESP32-S2 or ESP32-C3. Run for ESP32 only. + +Change SPI controller to VSPI. Please navigate to `Example Configuration → SPI Configuration` and change value of `SPI controller to use` to `3` + To build and flash the app on ESP peripheral, run @@ -165,6 +169,10 @@ Run following command and navigate to `Example Configuration -> Transport layer $ idf.py menuconfig ``` +:warning: Skip below step for ESP32-S2 or ESP32-C3. Run for ESP32 only. + +Change SPI controller to VSPI. Please navigate to `Example Configuration → SPI Configuration` and change value of `SPI controller to use` to `3` + To build and flash the app on ESP peripheral, run ```sh diff --git a/esp/esp_driver/network_adapter/main/spi_slave_api.c b/esp/esp_driver/network_adapter/main/spi_slave_api.c index 0c80c7b627..31a0d6c229 100644 --- a/esp/esp_driver/network_adapter/main/spi_slave_api.c +++ b/esp/esp_driver/network_adapter/main/spi_slave_api.c @@ -496,6 +496,7 @@ static void generate_startup_event(uint8_t cap) buf_handle.payload = heap_caps_malloc(SPI_BUFFER_SIZE, MALLOC_CAP_DMA); assert(buf_handle.payload); + memset(buf_handle.payload, 0, SPI_BUFFER_SIZE); header = (struct esp_payload_header *) buf_handle.payload; @@ -544,6 +545,7 @@ static void generate_startup_event(uint8_t cap) header->len = htole16(len); buf_handle.payload_len = len + sizeof(struct esp_payload_header); + header->checksum = htole16(compute_checksum(buf_handle.payload, buf_handle.payload_len)); xQueueSend(spi_tx_queue, &buf_handle, portMAX_DELAY); } diff --git a/host/stm32/driver/spi/spi_drv.c b/host/stm32/driver/spi/spi_drv.c index b9d27ef88b..90d51b8be4 100644 --- a/host/stm32/driver/spi/spi_drv.c +++ b/host/stm32/driver/spi/spi_drv.c @@ -445,6 +445,7 @@ static stm_ret_t spi_transaction_esp32(uint8_t * txbuff) struct esp_payload_header *payload_header; uint16_t len, offset; HAL_StatusTypeDef retval = HAL_ERROR; + uint16_t rx_checksum = 0, checksum = 0; /* Allocate rx buffer */ rxbuff = (uint8_t *)malloc(MAX_SPI_BUFFER_SIZE); @@ -495,18 +496,27 @@ static stm_ret_t spi_transaction_esp32(uint8_t * txbuff) osDelay(0); } else { - - buf_handle.priv_buffer_handle = rxbuff; - buf_handle.free_buf_handle = free; - buf_handle.payload_len = len; - buf_handle.if_type = payload_header->if_type; - buf_handle.if_num = payload_header->if_num; - buf_handle.payload = rxbuff + offset; - - if (pdTRUE != xQueueSend(from_slave_queue, - &buf_handle, portMAX_DELAY)) { - printf("Failed to send buffer\n\r"); - goto done; + rx_checksum = le16toh(payload_header->checksum); + payload_header->checksum = 0; + checksum = compute_checksum(rxbuff, len+offset); + if (checksum == rx_checksum) { + buf_handle.priv_buffer_handle = rxbuff; + buf_handle.free_buf_handle = free; + buf_handle.payload_len = len; + buf_handle.if_type = payload_header->if_type; + buf_handle.if_num = payload_header->if_num; + buf_handle.payload = rxbuff + offset; + + if (pdTRUE != xQueueSend(from_slave_queue, + &buf_handle, portMAX_DELAY)) { + printf("Failed to send buffer\n\r"); + goto done; + } + } else { + if (rxbuff) { + free(rxbuff); + rxbuff = NULL; + } } } @@ -560,6 +570,7 @@ static stm_ret_t spi_transaction_esp32s2(uint8_t * txbuff) struct esp_payload_header *payload_header; uint16_t len, offset; HAL_StatusTypeDef retval = HAL_ERROR; + uint16_t rx_checksum = 0, checksum = 0; /* Allocate rx buffer */ rxbuff = (uint8_t *)malloc(MAX_SPI_BUFFER_SIZE); @@ -568,7 +579,7 @@ static stm_ret_t spi_transaction_esp32s2(uint8_t * txbuff) if(!txbuff) { /* Even though, there is nothing to send, - * valid resetted txbuff is needed for SPI driver + * valid reseted txbuff is needed for SPI driver */ txbuff = (uint8_t *)malloc(MAX_SPI_BUFFER_SIZE); assert(txbuff); @@ -613,18 +624,29 @@ static stm_ret_t spi_transaction_esp32s2(uint8_t * txbuff) osDelay(0); } else { - - buf_handle.priv_buffer_handle = rxbuff; - buf_handle.free_buf_handle = free; - buf_handle.payload_len = len; - buf_handle.if_type = payload_header->if_type; - buf_handle.if_num = payload_header->if_num; - buf_handle.payload = rxbuff + offset; - - if (pdTRUE != xQueueSend(from_slave_queue, - &buf_handle, portMAX_DELAY)) { - printf("Failed to send buffer\n\r"); - goto done; + rx_checksum = le16toh(payload_header->checksum); + payload_header->checksum = 0; + + checksum = compute_checksum(rxbuff, len+offset); + + if (checksum == rx_checksum) { + buf_handle.priv_buffer_handle = rxbuff; + buf_handle.free_buf_handle = free; + buf_handle.payload_len = len; + buf_handle.if_type = payload_header->if_type; + buf_handle.if_num = payload_header->if_num; + buf_handle.payload = rxbuff + offset; + + if (pdTRUE != xQueueSend(from_slave_queue, + &buf_handle, portMAX_DELAY)) { + printf("Failed to send buffer\n\r"); + goto done; + } + } else { + if (rxbuff) { + free(rxbuff); + rxbuff = NULL; + } } } @@ -820,9 +842,9 @@ static uint8_t * get_tx_buffer(uint8_t *is_valid_tx_buf) payload_header->offset = htole16(sizeof(struct esp_payload_header)); payload_header->if_type = buf_handle.if_type; payload_header->if_num = buf_handle.if_num; - payload_header->reserved1 = 0; - memcpy(payload, buf_handle.payload, min(len, MAX_PAYLOAD_SIZE)); + payload_header->checksum = htole16(compute_checksum(sendbuf, + sizeof(struct esp_payload_header)+len));; } done: From 3ca0a32b7876aa6cf685ed1d69686fa63dd79770 Mon Sep 17 00:00:00 2001 From: t123yh Date: Sun, 10 Oct 2021 11:19:47 +0800 Subject: [PATCH 09/40] Prevent NOHZ tick-stop error in Linux driver Currently, netif_rx is called in non-interrupt context. This leads to error `NOHZ tick-stop error: Non-RCU local softirq work is pending, handler #08!!!` in latest 5.14 kernel. This patch fixes this problem by converting netif_rx to netif_rx_ni. Refer to https://www.mail-archive.com/ath10k@lists.infradead.org/msg13772.html --- host/linux/host_driver/esp32/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/host/linux/host_driver/esp32/main.c b/host/linux/host_driver/esp32/main.c index 80b8596977..9d11c9a678 100644 --- a/host/linux/host_driver/esp32/main.c +++ b/host/linux/host_driver/esp32/main.c @@ -382,7 +382,7 @@ static void process_rx_packet(struct sk_buff *skb) skb->ip_summed = CHECKSUM_NONE; /* Forward skb to kernel */ - netif_rx(skb); + netif_rx_ni(skb); priv->stats.rx_bytes += skb->len; priv->stats.rx_packets++; From 810a415f79a2e47bb6211dc9d2c4007e740d0d97 Mon Sep 17 00:00:00 2001 From: Yogesh Mantri Date: Wed, 6 Oct 2021 21:09:04 +0530 Subject: [PATCH 10/40] Reworked task handling of SPI driver. Changed Ready Pin management. Authored-by: starkeeper82 --- .../network_adapter/main/spi_slave_api.c | 181 +++--------------- 1 file changed, 25 insertions(+), 156 deletions(-) diff --git a/esp/esp_driver/network_adapter/main/spi_slave_api.c b/esp/esp_driver/network_adapter/main/spi_slave_api.c index 31a0d6c229..aa902c3651 100644 --- a/esp/esp_driver/network_adapter/main/spi_slave_api.c +++ b/esp/esp_driver/network_adapter/main/spi_slave_api.c @@ -110,7 +110,6 @@ static uint8_t gpio_handshake = CONFIG_ESP_SPI_GPIO_HANDSHAKE; static uint8_t gpio_data_ready = CONFIG_ESP_SPI_GPIO_DATA_READY; static QueueHandle_t spi_rx_queue = NULL; static QueueHandle_t spi_tx_queue = NULL; -static uint8_t dummy_queued = pdFALSE; static interface_handle_t * esp_spi_init(uint8_t capabilities); static int32_t esp_spi_write(interface_handle_t *handle, @@ -121,7 +120,6 @@ static void esp_spi_deinit(interface_handle_t *handle); static void esp_spi_read_done(void *handle); -static xSemaphoreHandle spi_sema; if_ops_t if_ops = { .init = esp_spi_init, @@ -149,29 +147,6 @@ int interface_remove_driver() return 0; } -static int is_valid_trans_buffer(uint8_t *trans_buf) -{ - struct esp_payload_header *header = NULL; - uint16_t len = 0; - - if (!trans_buf) { - return pdFALSE; - } - - header = (struct esp_payload_header *) trans_buf; - - len = le16toh(header->len); - - if (!len || (len > SPI_BUFFER_SIZE)) { - return pdFALSE; - } - - if ((header->if_type >= ESP_MAX_IF) || (header->if_num)) { - return pdFALSE; - } - - return pdTRUE; -} /* Invoked after transaction is queued and ready for pickup by master */ static void IRAM_ATTR spi_post_setup_cb(spi_slave_transaction_t *trans) @@ -186,15 +161,6 @@ static void IRAM_ATTR spi_post_trans_cb(spi_slave_transaction_t *trans) { /* Clear handshake line */ WRITE_PERI_REG(GPIO_OUT_W1TC_REG, (1 << gpio_handshake)); - - if (trans && is_valid_trans_buffer((uint8_t *)trans->tx_buffer)) { - /* Host has consumed a valid TX buffer - * Clear Data ready line */ - WRITE_PERI_REG(GPIO_OUT_W1TC_REG, (1 << gpio_data_ready)); - } - - if (spi_sema) - xSemaphoreGiveFromISR(spi_sema, NULL); } static uint8_t * get_next_tx_buffer(uint32_t *len) @@ -206,26 +172,21 @@ static uint8_t * get_next_tx_buffer(uint32_t *len) /* Get or create new tx_buffer * 1. Check if SPI TX queue has pending buffers. Return if valid buffer is obtained. - * 2. Prepare dummy tx buffer as below: - * a. Return if dummy tx buffer is already configured - * b. Create a new empty tx buffer and return */ + * 2. Create a new empty tx buffer and return */ /* Get buffer from SPI Tx queue */ ret = xQueueReceive(spi_tx_queue, &buf_handle, 0); if (ret == pdTRUE && buf_handle.payload) { if (len) *len = buf_handle.payload_len; + /* Return real data buffer from queue */ return buf_handle.payload; } - /* Dummy transaction is already queued. Return. */ - if (dummy_queued) { - if (len) - *len = 0; - return NULL; - } + /* No real data pending, clear ready line and indicate host an idle state */ + WRITE_PERI_REG(GPIO_OUT_W1TC_REG, (1 << gpio_data_ready)); - /* Create empty tx buffer */ + /* Create empty dummy buffer */ sendbuf = heap_caps_malloc(SPI_BUFFER_SIZE, MALLOC_CAP_DMA); if (!sendbuf) { ESP_LOGE(TAG, "Failed to allocate memory for dummy transaction"); @@ -296,77 +257,7 @@ static int process_spi_rx(interface_buffer_handle_t *buf_handle) return 0; } -static void spi_transaction_tx_task(void* pvParameters) -{ - spi_slave_transaction_t *spi_trans = NULL; - esp_err_t ret = ESP_OK; - interface_buffer_handle_t buf_handle = {0}; - - for(;;) { - ret = xQueueReceive(spi_tx_queue, &buf_handle, portMAX_DELAY); - - if (ret == pdTRUE && buf_handle.payload) { - spi_trans = malloc(sizeof(spi_slave_transaction_t)); - assert(spi_trans); - - memset(spi_trans, 0, sizeof(spi_slave_transaction_t)); - - /* Attach Rx Buffer */ - spi_trans->rx_buffer = heap_caps_malloc(SPI_BUFFER_SIZE, MALLOC_CAP_DMA); - assert(spi_trans->rx_buffer); - - memset(spi_trans->rx_buffer, 0, SPI_BUFFER_SIZE); - - /* Attach Tx Buffer */ - spi_trans->tx_buffer = buf_handle.payload; - - /* Transaction len */ - spi_trans->length = SPI_BUFFER_SIZE * SPI_BITS_PER_WORD; - - /* Set Data ready high */ - WRITE_PERI_REG(GPIO_OUT_W1TS_REG, (1 << gpio_data_ready)); - - /* Execute transaction */ - ret = xSemaphoreTake(spi_sema, portMAX_DELAY); - - if (ret != pdTRUE) { - ESP_LOGE(TAG, "Failed to obtain semaphore\n"); - - free(spi_trans->rx_buffer); - spi_trans->rx_buffer = NULL; - free((void *)spi_trans->tx_buffer); - spi_trans->tx_buffer = NULL; - free(spi_trans); - spi_trans = NULL; - - WRITE_PERI_REG(GPIO_OUT_W1TS_REG, (0 << gpio_data_ready)); - continue; - } - - ret = spi_slave_queue_trans(ESP_SPI_CONTROLLER, spi_trans, - portMAX_DELAY); - if (ret != ESP_OK) { - ESP_LOGE(TAG , "spi transmit error, ret : 0x%x\r\n", ret); - - free(spi_trans->rx_buffer); - spi_trans->rx_buffer = NULL; - free((void *)spi_trans->tx_buffer); - spi_trans->tx_buffer = NULL; - free(spi_trans); - spi_trans = NULL; - - if (spi_sema) - xSemaphoreGive(spi_sema); - - WRITE_PERI_REG(GPIO_OUT_W1TS_REG, (0 << gpio_data_ready)); - - continue; - } - } - } -} - -static void queue_dummy_transaction() +static void queue_next_transaction(void) { spi_slave_transaction_t *spi_trans = NULL; esp_err_t ret = ESP_OK; @@ -375,7 +266,8 @@ static void queue_dummy_transaction() tx_buffer = get_next_tx_buffer(&len); if (!tx_buffer) { - /* No need to queue dummy transaction */ + /* Queue next transaction failed */ + ESP_LOGE(TAG , "Failed to queue new transaction\r\n"); return; } @@ -404,17 +296,8 @@ static void queue_dummy_transaction() spi_trans->tx_buffer = NULL; free(spi_trans); spi_trans = NULL; - xSemaphoreGive(spi_sema); return; } - - if (!len) { - /* queued dummy transaction, release semaphore */ - dummy_queued = pdTRUE; - } else { - /* Queued transaction with valid TX Buffer. Set Data ready high. */ - WRITE_PERI_REG(GPIO_OUT_W1TS_REG, (1 << gpio_data_ready)); - } } static void spi_transaction_post_process_task(void* pvParameters) @@ -427,9 +310,15 @@ static void spi_transaction_post_process_task(void* pvParameters) for (;;) { memset(&rx_buf_handle, 0, sizeof(rx_buf_handle)); + /* await transmission result, after any kind of transmission a new packet + * (dummy or real) must be placed in SPI slave + */ ret = spi_slave_get_trans_result(ESP_SPI_CONTROLLER, &spi_trans, portMAX_DELAY); + /* Queue new transaction to get ready as soon as possible */ + queue_next_transaction(); + if (ret != ESP_OK) { ESP_LOGE(TAG , "spi transmit error, ret : 0x%x\r\n", ret); continue; @@ -440,32 +329,13 @@ static void spi_transaction_post_process_task(void* pvParameters) continue; } + /* Free any tx buffer, data is not relevant anymore */ if (spi_trans->tx_buffer) { - header = (struct esp_payload_header *) spi_trans->tx_buffer; - - if (header->if_type == 0xF && header->if_num == 0xF && header->offset == 0) { - /* Dummy Tx buffer consumed by host */ - dummy_queued = pdFALSE; - } - free((void *)spi_trans->tx_buffer); spi_trans->tx_buffer = NULL; } - /* Check if dummy transaction is needed - * - * If failed to obtain spi_sema: - * - Transaction is already queued. - * - No need to queue dummy transaction - * - * If spi_sema is obtained: queue dummy transaction - **/ - - ret = xSemaphoreTake(spi_sema, 0); - - if (ret == pdTRUE) - queue_dummy_transaction(); - + /* Process received data */ if (spi_trans->rx_buffer) { rx_buf_handle.payload = spi_trans->rx_buffer; @@ -479,6 +349,7 @@ static void spi_transaction_post_process_task(void* pvParameters) } } + /* free transaction structure */ free(spi_trans); spi_trans = NULL; } @@ -548,6 +419,11 @@ static void generate_startup_event(uint8_t cap) header->checksum = htole16(compute_checksum(buf_handle.payload, buf_handle.payload_len)); xQueueSend(spi_tx_queue, &buf_handle, portMAX_DELAY); + + /* indicate waiting data on ready pin */ + WRITE_PERI_REG(GPIO_OUT_W1TS_REG, (1 << gpio_data_ready)); + /* process first data packet here to start transactions */ + queue_next_transaction(); } static interface_handle_t * esp_spi_init(uint8_t capabilities) @@ -615,13 +491,6 @@ static interface_handle_t * esp_spi_init(uint8_t capabilities) spi_tx_queue = xQueueCreate(SPI_TX_QUEUE_SIZE, sizeof(interface_buffer_handle_t)); assert(spi_tx_queue != NULL); - spi_sema = xSemaphoreCreateBinary(); - assert(spi_sema != NULL); - - xSemaphoreGive(spi_sema); - - assert(xTaskCreate(spi_transaction_tx_task , "spi_tx_task" , 4096 , NULL , - 20 , NULL) == pdTRUE); assert(xTaskCreate(spi_transaction_post_process_task , "spi_post_process_task" , 4096 , NULL , 18 , NULL) == pdTRUE); @@ -693,6 +562,9 @@ static int32_t esp_spi_write(interface_handle_t *handle, interface_buffer_handle if (ret != pdTRUE) return ESP_FAIL; + /* indicate waiting data on ready pin */ + WRITE_PERI_REG(GPIO_OUT_W1TS_REG, (1 << gpio_data_ready)); + return buf_handle->payload_len; } @@ -741,9 +613,6 @@ static void esp_spi_deinit(interface_handle_t *handle) { esp_err_t ret = ESP_OK; - if (spi_sema) - vSemaphoreDelete(spi_sema); - ret = spi_slave_free(ESP_SPI_CONTROLLER); if (ESP_OK != ret) { ESP_LOGE(TAG, "spi slave bus free failed\n"); From 4848e87940567d97e51399056d5834b3e174548d Mon Sep 17 00:00:00 2001 From: Yogesh Mantri Date: Fri, 8 Oct 2021 12:25:10 +0530 Subject: [PATCH 11/40] a. Messages processed with prio queue. \ Priority of msgs: Control path msgs > Bluetooth msgs > Others (Wi-Fi,etc) \ b. Fragmentation of big messages supported at transport \ Co-authored-by: Ajita Chavan ajita.chavan@espressif.com \ Co-authored-by: Mangesh Malusare mangesh.malusare@espressif.com \ Co-authored-by: Yogesh Mantri yogesh.mantri@espressif.com --- common/include/adapter.h | 11 +- .../network_adapter/main/app_main.c | 435 ++++++++++++------ .../network_adapter/main/interface.h | 2 + .../network_adapter/main/protocomm_pserial.c | 25 +- .../network_adapter/main/spi_slave_api.c | 85 +++- .../network_adapter/sdkconfig.defaults.esp32 | 19 +- host/linux/host_driver/esp32/esp_serial.c | 83 +++- host/linux/host_driver/esp32/main.c | 1 - host/linux/host_driver/esp32/sdio/esp_sdio.c | 60 ++- .../host_driver/esp32/sdio/esp_sdio_decl.h | 2 +- host/linux/host_driver/esp32/spi/esp_spi.c | 59 ++- host/linux/host_driver/esp32/spi/esp_spi.h | 4 +- 12 files changed, 538 insertions(+), 248 deletions(-) diff --git a/common/include/adapter.h b/common/include/adapter.h index 378e895b01..b185dabb3b 100644 --- a/common/include/adapter.h +++ b/common/include/adapter.h @@ -4,10 +4,18 @@ #ifndef __ESP_NETWORK_ADAPTER__H #define __ESP_NETWORK_ADAPTER__H +#define PRIO_Q_SERIAL 0 +#define PRIO_Q_BT 1 +#define PRIO_Q_OTHERS 2 +#define MAX_PRIORITY_QUEUES 3 + +/* ESP Payload Header Flags */ +#define MORE_FRAGMENT (1 << 0) + struct esp_payload_header { uint8_t if_type:4; uint8_t if_num:4; - uint8_t reserved1; + uint8_t flags; uint16_t len; uint16_t offset; uint16_t checksum; @@ -17,6 +25,7 @@ struct esp_payload_header { uint8_t hci_pkt_type; /* Packet type for HCI interface */ uint8_t priv_pkt_type; /* Packet type for priv interface */ }; + uint16_t seq_num; } __attribute__((packed)); typedef enum { diff --git a/esp/esp_driver/network_adapter/main/app_main.c b/esp/esp_driver/network_adapter/main/app_main.c index ffe87554db..a7d2730886 100644 --- a/esp/esp_driver/network_adapter/main/app_main.c +++ b/esp/esp_driver/network_adapter/main/app_main.c @@ -66,6 +66,11 @@ static const char TAG_TX_S[] = "CONTROL S -> H"; #define BT_CTS_PIN 23 #endif +#ifdef CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS +#define STATS_TICKS pdMS_TO_TICKS(1000*2) +#define ARRAY_SIZE_OFFSET 5 +#endif + #ifdef CONFIG_BTDM_CONTROLLER_HCI_MODE_VHCI #define VHCI_MAX_TIMEOUT_MS 2000 static SemaphoreHandle_t vhci_send_sem; @@ -87,28 +92,27 @@ uint32_t to_host_sent_count = 0; interface_context_t *if_context = NULL; interface_handle_t *if_handle = NULL; -QueueHandle_t to_host_queue = NULL; -QueueHandle_t from_host_queue = NULL; +QueueHandle_t to_host_queue[MAX_PRIORITY_QUEUES] = {NULL}; #if CONFIG_ESP_SPI_HOST_INTERFACE #ifdef CONFIG_IDF_TARGET_ESP32S2 #define TO_HOST_QUEUE_SIZE 5 -#define FROM_HOST_QUEUE_SIZE 5 #else -#define TO_HOST_QUEUE_SIZE 10 -#define FROM_HOST_QUEUE_SIZE 10 +#define TO_HOST_QUEUE_SIZE 20 #endif #else #define TO_HOST_QUEUE_SIZE 100 -#define FROM_HOST_QUEUE_SIZE 100 #endif +#define ETH_DATA_LEN 1500 + static protocomm_t *pc_pserial; static struct rx_data { uint8_t valid; + uint16_t cur_seq_no; int len; - uint8_t data[1024]; + uint8_t data[4096]; } r; static void print_firmware_version() @@ -232,7 +236,7 @@ esp_err_t wlan_ap_rx_callback(void *buffer, uint16_t len, void *eb) buf_handle.wlan_buf_handle = eb; buf_handle.free_buf_handle = esp_wifi_internal_free_rx_buffer; - ret = xQueueSend(to_host_queue, &buf_handle, portMAX_DELAY); + ret = xQueueSend(to_host_queue[PRIO_Q_OTHERS], &buf_handle, portMAX_DELAY); if (ret != pdTRUE) { ESP_LOGE(TAG, "Slave -> Host: Failed to send buffer\n"); @@ -269,7 +273,7 @@ esp_err_t wlan_sta_rx_callback(void *buffer, uint16_t len, void *eb) buf_handle.wlan_buf_handle = eb; buf_handle.free_buf_handle = esp_wifi_internal_free_rx_buffer; - ret = xQueueSend(to_host_queue, &buf_handle, portMAX_DELAY); + ret = xQueueSend(to_host_queue[PRIO_Q_OTHERS], &buf_handle, portMAX_DELAY); if (ret != pdTRUE) { ESP_LOGE(TAG, "Slave -> Host: Failed to send buffer\n"); @@ -283,143 +287,162 @@ esp_err_t wlan_sta_rx_callback(void *buffer, uint16_t len, void *eb) return ESP_OK; } +void process_tx_pkt(interface_buffer_handle_t *buf_handle) +{ + /* Check if data path is not yet open */ + if (!datapath) { +#if CONFIG_ESP_WLAN_DEBUG + ESP_LOGD (TAG_TX, "Data path stopped"); +#endif + /* Post processing */ + if (buf_handle->free_buf_handle && buf_handle->priv_buffer_handle) { + buf_handle->free_buf_handle(buf_handle->priv_buffer_handle); + buf_handle->priv_buffer_handle = NULL; + } + usleep(100*1000); + return; + } + if (if_context && if_context->if_ops && if_context->if_ops->write) { + if_context->if_ops->write(if_handle, buf_handle); + } + /* Post processing */ + if (buf_handle->free_buf_handle && buf_handle->priv_buffer_handle) { + buf_handle->free_buf_handle(buf_handle->priv_buffer_handle); + buf_handle->priv_buffer_handle = NULL; + } +} + /* Send data to host */ void send_task(void* pvParameters) { - esp_err_t ret = ESP_OK; #ifdef ESP_DEBUG_STATS int t1, t2, t_total = 0; int d_total = 0; #endif interface_buffer_handle_t buf_handle = {0}; - + uint16_t serial_pkts_waiting = 0; + uint16_t bt_pkts_waiting = 0; + uint16_t other_pkts_waiting = 0; while (1) { + serial_pkts_waiting = uxQueueMessagesWaiting(to_host_queue[PRIO_Q_SERIAL]); + bt_pkts_waiting = uxQueueMessagesWaiting(to_host_queue[PRIO_Q_BT]); + other_pkts_waiting = uxQueueMessagesWaiting(to_host_queue[PRIO_Q_OTHERS]); + + if (serial_pkts_waiting) { + while (serial_pkts_waiting) { + if (xQueueReceive(to_host_queue[PRIO_Q_SERIAL], &buf_handle, portMAX_DELAY)) + process_tx_pkt(&buf_handle); + serial_pkts_waiting--; + } + } else if (bt_pkts_waiting) { + if (xQueueReceive(to_host_queue[PRIO_Q_BT], &buf_handle, portMAX_DELAY)) + process_tx_pkt(&buf_handle); + } else if (other_pkts_waiting) { + if (xQueueReceive(to_host_queue[PRIO_Q_OTHERS], &buf_handle, portMAX_DELAY)) + process_tx_pkt(&buf_handle); + } else { + vTaskDelay(1); + } + } +} - ret = xQueueReceive(to_host_queue, &buf_handle, portMAX_DELAY); - - if (datapath) { - if (ret == pdTRUE) { -#ifdef ESP_DEBUG_STATS - to_host_count++; - - /* Send data */ - t1 = XTHAL_GET_CCOUNT(); -#endif - - if (if_context && if_context->if_ops && if_context->if_ops->write) { - if_context->if_ops->write(if_handle, &buf_handle); - } - -#ifdef ESP_DEBUG_STATS - t2 = XTHAL_GET_CCOUNT(); - t_total += t2 - t1; - d_total += buf_handle.payload_len; -#endif - -#if CONFIG_ESP_WLAN_DEBUG - ESP_LOG_BUFFER_HEXDUMP(TAG_TX, buf_handle.payload, buf_handle.payload_len, ESP_LOG_INFO); -#endif - /* Post processing */ - if (buf_handle.free_buf_handle && buf_handle.priv_buffer_handle) { - buf_handle.free_buf_handle(buf_handle.priv_buffer_handle); - buf_handle.priv_buffer_handle = NULL; - } +void process_serial_rx_pkt(uint8_t *buf) +{ + struct esp_payload_header *header = NULL; + uint16_t payload_len = 0; + uint8_t *payload = NULL; + int rem_buff_size; + + header = (struct esp_payload_header *) buf; + payload_len = le16toh(header->len); + payload = buf + le16toh(header->offset); + rem_buff_size = sizeof(r.data) - r.len; + + while (r.valid) + { + ESP_LOGI(TAG,"curr seq: %u header seq: %u\n", + r.cur_seq_no, header->seq_num); + vTaskDelay(10); + } -#ifdef ESP_DEBUG_STATS - to_host_sent_count++; - } - if (t_total) { - ESP_LOGI("TX complete. Total time spent in tx = %d for %d bytes\n", t_total, d_total); - t_total = 0; -#endif - } + if (!r.len) { + /* New Buffer */ + r.cur_seq_no = le16toh(header->seq_num); + } - } else { - if (ret == pdTRUE) { -#if CONFIG_ESP_WLAN_DEBUG - ESP_LOGD (TAG_TX, "Data path stopped"); -#endif + if (header->seq_num != r.cur_seq_no) { + /* Sequence number mismatch */ + r.valid = 1; + protocomm_pserial_data_ready(pc_pserial, r.len); + return; + } - /* Post processing */ - if (buf_handle.free_buf_handle && buf_handle.priv_buffer_handle) { - buf_handle.free_buf_handle(buf_handle.priv_buffer_handle); - buf_handle.priv_buffer_handle = NULL; - } - } + memcpy((r.data + r.len), payload, min(payload_len, rem_buff_size)); + r.len += payload_len; - usleep(100*1000); - } + if (!(header->flags & MORE_FRAGMENT)) { + /* Received complete buffer */ + r.valid = 1; + protocomm_pserial_data_ready(pc_pserial, r.len); } } -void process_rx_task(void* pvParameters) +void process_rx_pkt(interface_buffer_handle_t **buf_handle_p) { - esp_err_t ret = ESP_OK; - interface_buffer_handle_t buf_handle = {0}; struct esp_payload_header *header = NULL; uint8_t *payload = NULL; uint16_t payload_len = 0; + interface_buffer_handle_t *buf_handle = *buf_handle_p; - while (1) { - ret = xQueueReceive(from_host_queue, &buf_handle, portMAX_DELAY); - - if (ret != pdTRUE) { - continue; - } - - header = (struct esp_payload_header *) buf_handle.payload; - payload = buf_handle.payload + le16toh(header->offset); - payload_len = le16toh(header->len); + header = (struct esp_payload_header *) buf_handle->payload; + payload = buf_handle->payload + le16toh(header->offset); + payload_len = le16toh(header->len); #if CONFIG_ESP_WLAN_DEBUG - ESP_LOG_BUFFER_HEXDUMP(TAG_RX, payload, 8, ESP_LOG_INFO); -#endif - - if ((buf_handle.if_type == ESP_STA_IF) && station_connected) { - /* Forward data to wlan driver */ - esp_wifi_internal_tx(ESP_IF_WIFI_STA, payload, payload_len); - } else if (buf_handle.if_type == ESP_AP_IF && softap_started) { - /* Forward data to wlan driver */ - esp_wifi_internal_tx(ESP_IF_WIFI_AP, payload, payload_len); - } else if (buf_handle.if_type == ESP_SERIAL_IF) { - /* Process AT command*/ - memcpy(r.data, payload, min(payload_len, sizeof(r.data))); - r.valid = 1; - r.len = min(payload_len, sizeof(r.data)); + ESP_LOG_BUFFER_HEXDUMP(TAG_RX, payload, 8, ESP_LOG_INFO); +#endif + + if ((buf_handle->if_type == ESP_STA_IF) && station_connected) { + /* Forward data to wlan driver */ + esp_wifi_internal_tx(ESP_IF_WIFI_STA, payload, payload_len); + //ESP_LOG_BUFFER_HEXDUMP("spi_sta_rx", payload, payload_len, ESP_LOG_INFO); + } else if (buf_handle->if_type == ESP_AP_IF && softap_started) { + /* Forward data to wlan driver */ + esp_wifi_internal_tx(ESP_IF_WIFI_AP, payload, payload_len); + } else if (buf_handle->if_type == ESP_SERIAL_IF) { + process_serial_rx_pkt(buf_handle->payload); #if CONFIG_ESP_SERIAL_DEBUG - ESP_LOG_BUFFER_HEXDUMP(TAG_RX_S, r.data, r.len, ESP_LOG_INFO); + ESP_LOG_BUFFER_HEXDUMP(TAG_RX_S, r.data, r.len, ESP_LOG_INFO); #endif - protocomm_pserial_data_ready(pc_pserial, r.len); #ifdef CONFIG_BTDM_CONTROLLER_HCI_MODE_VHCI - } else if (buf_handle.if_type == ESP_HCI_IF) { - /* VHCI needs one extra byte at the start of payload */ - /* that is accomodated in esp_payload_header */ + } else if (buf_handle->if_type == ESP_HCI_IF) { + /* VHCI needs one extra byte at the start of payload */ + /* that is accomodated in esp_payload_header */ #if CONFIG_ESP_BT_DEBUG - ESP_LOG_BUFFER_HEXDUMP("bt_rx", payload, payload_len, ESP_LOG_INFO); + ESP_LOG_BUFFER_HEXDUMP("bt_rx", payload, payload_len, ESP_LOG_INFO); #endif - payload--; - payload_len++; + payload--; + payload_len++; - if (!esp_vhci_host_check_send_available()) { - ESP_LOGD(TAG, "VHCI not available"); - } + if (!esp_vhci_host_check_send_available()) { + ESP_LOGD(TAG, "VHCI not available"); + } - if (vhci_send_sem) { - if (xSemaphoreTake(vhci_send_sem, VHCI_MAX_TIMEOUT_MS) == pdTRUE) { - esp_vhci_host_send_packet(payload, payload_len); - } else { - ESP_LOGI(TAG, "VHCI sem timeout"); - } + if (vhci_send_sem) { + if (xSemaphoreTake(vhci_send_sem, VHCI_MAX_TIMEOUT_MS) == pdTRUE) { + esp_vhci_host_send_packet(payload, payload_len); + } else { + ESP_LOGI(TAG, "VHCI sem timeout"); } -#endif } +#endif + } - /* Free buffer handle */ - if (buf_handle.free_buf_handle && buf_handle.priv_buffer_handle) { - buf_handle.free_buf_handle(buf_handle.priv_buffer_handle); - buf_handle.priv_buffer_handle = NULL; - } + /* Free buffer handle */ + if (buf_handle->free_buf_handle && buf_handle->priv_buffer_handle) { + buf_handle->free_buf_handle(buf_handle->priv_buffer_handle); + buf_handle->priv_buffer_handle = NULL; } } @@ -427,7 +450,6 @@ void process_rx_task(void* pvParameters) void recv_task(void* pvParameters) { interface_buffer_handle_t *buf_handle = NULL; - esp_err_t ret = ESP_OK; for (;;) { @@ -446,15 +468,8 @@ void recv_task(void* pvParameters) } } - ret = xQueueSend(from_host_queue, buf_handle, portMAX_DELAY); + process_rx_pkt(&buf_handle); - if (ret != pdTRUE) { - ESP_LOGE(TAG, "Host -> Slave: Failed to send buffer\n"); - if (buf_handle->free_buf_handle && buf_handle->priv_buffer_handle) { - buf_handle->free_buf_handle(buf_handle->priv_buffer_handle); - buf_handle->priv_buffer_handle = NULL; - } - } free(buf_handle); buf_handle = NULL; @@ -468,6 +483,7 @@ static int32_t serial_read_data(uint8_t *data, int32_t len) memcpy(data, r.data, len); r.valid = 0; r.len = 0; + r.cur_seq_no = 0; } else { printf("No data to be read, len %d \n", len); } @@ -476,29 +492,52 @@ static int32_t serial_read_data(uint8_t *data, int32_t len) static int32_t serial_write_data(uint8_t* data, int32_t len) { - interface_buffer_handle_t buf_handle = {0}; esp_err_t ret = ESP_OK; + uint8_t *pos = data; + int32_t left_len = len; + int32_t frag_len = 0; + static uint16_t seq_num = 0; - buf_handle.if_type = ESP_SERIAL_IF; - buf_handle.if_num = 0; - buf_handle.payload = data; - buf_handle.payload_len = len; - buf_handle.priv_buffer_handle = data; - buf_handle.free_buf_handle = free; + do { + interface_buffer_handle_t buf_handle = {0}; - ret = xQueueSend(to_host_queue, &buf_handle, portMAX_DELAY); + seq_num++; - if (ret != pdTRUE) { - ESP_LOGE(TAG, "Control packet: Failed to send buffer\n"); - if (data) { - free(data); - data = NULL; + buf_handle.if_type = ESP_SERIAL_IF; + buf_handle.if_num = 0; + buf_handle.seq_num = seq_num; + + if (left_len > ETH_DATA_LEN) { + frag_len = ETH_DATA_LEN; + buf_handle.flag = MORE_FRAGMENT; + } else { + frag_len = left_len; + buf_handle.flag = 0; + buf_handle.priv_buffer_handle = data; + buf_handle.free_buf_handle = free; + } + + buf_handle.payload = pos; + buf_handle.payload_len = frag_len; + + ret = xQueueSend(to_host_queue[PRIO_Q_SERIAL], &buf_handle, portMAX_DELAY); + + if (ret != pdTRUE) { + ESP_LOGE(TAG, "Control packet: Failed to send buffer\n"); + if (data) { + free(data); + data = NULL; + } + return ESP_FAIL; } - return ESP_FAIL; - } #if CONFIG_ESP_SERIAL_DEBUG - ESP_LOG_BUFFER_HEXDUMP(TAG_TX_S, data, len, ESP_LOG_INFO); + ESP_LOG_BUFFER_HEXDUMP(TAG_TX_S, data, frag_len, ESP_LOG_INFO); #endif + + left_len -= frag_len; + pos += frag_len; + } while(left_len); + return ESP_OK; } @@ -537,7 +576,7 @@ static int host_rcv_pkt(uint8_t *data, uint16_t len) ESP_LOG_BUFFER_HEXDUMP("bt_tx", data, len, ESP_LOG_INFO); #endif - ret = xQueueSend(to_host_queue, &buf_handle, portMAX_DELAY); + ret = xQueueSend(to_host_queue[PRIO_Q_BT], &buf_handle, portMAX_DELAY); if (ret != pdTRUE) { ESP_LOGE(TAG, "HCI send packet: Failed to send buffer\n"); @@ -670,10 +709,113 @@ int event_handler(uint8_t val) return 0; } +#ifdef CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS +/* These functions are only for debugging purpose + * Please do not enable in production environments + */ +static esp_err_t print_real_time_stats(TickType_t xTicksToWait) +{ + TaskStatus_t *start_array = NULL, *end_array = NULL; + UBaseType_t start_array_size, end_array_size; + uint32_t start_run_time, end_run_time; + esp_err_t ret; + + //Allocate array to store current task states + start_array_size = uxTaskGetNumberOfTasks() + ARRAY_SIZE_OFFSET; + start_array = malloc(sizeof(TaskStatus_t) * start_array_size); + if (start_array == NULL) { + ret = ESP_ERR_NO_MEM; + goto exit; + } + //Get current task states + start_array_size = uxTaskGetSystemState(start_array, start_array_size, &start_run_time); + if (start_array_size == 0) { + ret = ESP_ERR_INVALID_SIZE; + goto exit; + } + + vTaskDelay(xTicksToWait); + + //Allocate array to store tasks states post delay + end_array_size = uxTaskGetNumberOfTasks() + ARRAY_SIZE_OFFSET; + end_array = malloc(sizeof(TaskStatus_t) * end_array_size); + if (end_array == NULL) { + ret = ESP_ERR_NO_MEM; + goto exit; + } + //Get post delay task states + end_array_size = uxTaskGetSystemState(end_array, end_array_size, &end_run_time); + if (end_array_size == 0) { + ret = ESP_ERR_INVALID_SIZE; + goto exit; + } + + //Calculate total_elapsed_time in units of run time stats clock period. + uint32_t total_elapsed_time = (end_run_time - start_run_time); + if (total_elapsed_time == 0) { + ret = ESP_ERR_INVALID_STATE; + goto exit; + } + + printf("| Task | Run Time | Percentage\n"); + //Match each task in start_array to those in the end_array + for (int i = 0; i < start_array_size; i++) { + int k = -1; + for (int j = 0; j < end_array_size; j++) { + if (start_array[i].xHandle == end_array[j].xHandle) { + k = j; + //Mark that task have been matched by overwriting their handles + start_array[i].xHandle = NULL; + end_array[j].xHandle = NULL; + break; + } + } + //Check if matching task found + if (k >= 0) { + uint32_t task_elapsed_time = end_array[k].ulRunTimeCounter - start_array[i].ulRunTimeCounter; + uint32_t percentage_time = (task_elapsed_time * 100UL) / (total_elapsed_time * portNUM_PROCESSORS); + printf("| %s | %d | %d%%\n", start_array[i].pcTaskName, task_elapsed_time, percentage_time); + } + } + + //Print unmatched tasks + for (int i = 0; i < start_array_size; i++) { + if (start_array[i].xHandle != NULL) { + printf("| %s | Deleted\n", start_array[i].pcTaskName); + } + } + for (int i = 0; i < end_array_size; i++) { + if (end_array[i].xHandle != NULL) { + printf("| %s | Created\n", end_array[i].pcTaskName); + } + } + ret = ESP_OK; + +exit: //Common return path + free(start_array); + free(end_array); + return ret; +} + +void task_runtime_stats_task(void* pvParameters) +{ + while (1) { + printf("\n\nGetting real time stats over %d ticks\n", STATS_TICKS); + if (print_real_time_stats(STATS_TICKS) == ESP_OK) { + printf("Real time stats obtained\n"); + } else { + printf("Error getting real time stats\n"); + } + vTaskDelay(pdMS_TO_TICKS(1000*2)); + } +} +#endif + void app_main() { esp_err_t ret; uint8_t capa = 0; + uint8_t prio_q_idx = 0; print_firmware_version(); capa = get_capabilities(); @@ -719,17 +861,18 @@ void app_main() return; } - to_host_queue = xQueueCreate(TO_HOST_QUEUE_SIZE, sizeof(interface_buffer_handle_t)); - assert(to_host_queue != NULL); - - from_host_queue = xQueueCreate(FROM_HOST_QUEUE_SIZE, sizeof(interface_buffer_handle_t)); - assert(from_host_queue != NULL); + for (prio_q_idx=0; prio_q_idxxmit)(out, (ssize_t) outlen); if (ret != ESP_OK) { @@ -172,7 +173,6 @@ static esp_err_t protocomm_pserial_common_handler(protocomm_t *pc, uint8_t *in, esp_err_t protocomm_pserial_data_ready(protocomm_t *pc, int len) { - printf("data ready \n"); struct pserial_config *pserial_cfg = NULL; pserial_cfg = (struct pserial_config *) pc->priv; if (!pserial_cfg) { @@ -216,12 +216,15 @@ static void pserial_task(void *params) while (xQueueReceive(pserial_cfg->req_queue, &len, portMAX_DELAY) == pdTRUE) { buf = (uint8_t *) malloc(len); if (buf == NULL) { - ESP_LOGE(TAG,"Failed to allocate memory"); + ESP_LOGE(TAG,"%s Failed to allocate memory", __func__); return; } len = pserial_cfg->recv(buf, len); if (len) { + //ESP_LOG_BUFFER_HEXDUMP("serial_rx", buf, /*len*/len<16?len:16, ESP_LOG_INFO); ret = protocomm_pserial_common_handler(pc, buf, len); + if (ret) + ESP_LOGI(TAG, "protocomm_pserial_common_handler failed %d\n", ret); if (buf) { free(buf); buf = NULL; @@ -248,7 +251,7 @@ esp_err_t protocomm_pserial_start(protocomm_t *pc, pserial_xmit xmit, pserial_re pserial_cfg = (struct pserial_config *) malloc(sizeof(struct pserial_config)); if (pserial_cfg == NULL) { - ESP_LOGE(TAG,"Failed to allocate memory"); + ESP_LOGE(TAG,"%s Failed to allocate memory", __func__); return ESP_ERR_NO_MEM; } pserial_cfg->xmit = xmit; @@ -256,8 +259,8 @@ esp_err_t protocomm_pserial_start(protocomm_t *pc, pserial_xmit xmit, pserial_re pserial_cfg->req_queue = xQueueCreate(REQ_Q_MAX, sizeof(ssize_t)); pc->priv = pserial_cfg; - - xTaskCreate(pserial_task, "pserial_task", 4096, (void *) pc, TASK_PRIORITY, NULL); + + xTaskCreate(pserial_task, "pserial_task", 4096, (void *) pc, CONTROL_PATH_TASK_PRIORITY, NULL); return ESP_OK; } diff --git a/esp/esp_driver/network_adapter/main/spi_slave_api.c b/esp/esp_driver/network_adapter/main/spi_slave_api.c index aa902c3651..95614dc9c4 100644 --- a/esp/esp_driver/network_adapter/main/spi_slave_api.c +++ b/esp/esp_driver/network_adapter/main/spi_slave_api.c @@ -68,7 +68,7 @@ static const char TAG[] = "SPI_DRIVER"; #else #error "Please choose correct SPI controller" #endif - + #define DMA_CHAN ESP_SPI_CONTROLLER #elif defined CONFIG_IDF_TARGET_ESP32S2 @@ -108,8 +108,8 @@ static interface_context_t context; static interface_handle_t if_handle_g; static uint8_t gpio_handshake = CONFIG_ESP_SPI_GPIO_HANDSHAKE; static uint8_t gpio_data_ready = CONFIG_ESP_SPI_GPIO_DATA_READY; -static QueueHandle_t spi_rx_queue = NULL; -static QueueHandle_t spi_tx_queue = NULL; +static QueueHandle_t spi_rx_queue[MAX_PRIORITY_QUEUES] = {NULL}; +static QueueHandle_t spi_tx_queue[MAX_PRIORITY_QUEUES] = {NULL}; static interface_handle_t * esp_spi_init(uint8_t capabilities); static int32_t esp_spi_write(interface_handle_t *handle, @@ -175,7 +175,15 @@ static uint8_t * get_next_tx_buffer(uint32_t *len) * 2. Create a new empty tx buffer and return */ /* Get buffer from SPI Tx queue */ - ret = xQueueReceive(spi_tx_queue, &buf_handle, 0); + if(uxQueueMessagesWaiting(spi_tx_queue[PRIO_Q_SERIAL])) + ret = xQueueReceive(spi_tx_queue[PRIO_Q_SERIAL], &buf_handle, portMAX_DELAY); + else if(uxQueueMessagesWaiting(spi_tx_queue[PRIO_Q_BT])) + ret = xQueueReceive(spi_tx_queue[PRIO_Q_BT], &buf_handle, portMAX_DELAY); + else if(uxQueueMessagesWaiting(spi_tx_queue[PRIO_Q_OTHERS])) + ret = xQueueReceive(spi_tx_queue[PRIO_Q_OTHERS], &buf_handle, portMAX_DELAY); + else + ret = pdFALSE; + if (ret == pdTRUE && buf_handle.payload) { if (len) *len = buf_handle.payload_len; @@ -229,6 +237,10 @@ static int process_spi_rx(interface_buffer_handle_t *buf_handle) len = le16toh(header->len); offset = le16toh(header->offset); + if (!len || (len > SPI_BUFFER_SIZE)) { + return -1; + } + rx_checksum = le16toh(header->checksum); header->checksum = 0; @@ -238,10 +250,6 @@ static int process_spi_rx(interface_buffer_handle_t *buf_handle) return -1; } - if (!len || (len > SPI_BUFFER_SIZE)) { - return -1; - } - /* Buffer is valid */ buf_handle->if_type = header->if_type; buf_handle->if_num = header->if_num; @@ -249,7 +257,13 @@ static int process_spi_rx(interface_buffer_handle_t *buf_handle) buf_handle->payload_len = le16toh(header->len) + offset; buf_handle->priv_buffer_handle = buf_handle->payload; - ret = xQueueSend(spi_rx_queue, buf_handle, portMAX_DELAY); + if (header->if_type == ESP_SERIAL_IF) { + ret = xQueueSend(spi_rx_queue[PRIO_Q_SERIAL], buf_handle, portMAX_DELAY); + } else if (header->if_type == ESP_HCI_IF) { + ret = xQueueSend(spi_rx_queue[PRIO_Q_BT], buf_handle, portMAX_DELAY); + } else { + ret = xQueueSend(spi_rx_queue[PRIO_Q_OTHERS], buf_handle, portMAX_DELAY); + } if (ret != pdTRUE) return -1; @@ -287,9 +301,10 @@ static void queue_next_transaction(void) /* Transaction len */ spi_trans->length = SPI_BUFFER_SIZE * SPI_BITS_PER_WORD; - ret = spi_slave_queue_trans(ESP_SPI_CONTROLLER, spi_trans, 0); + ret = spi_slave_queue_trans(ESP_SPI_CONTROLLER, spi_trans, portMAX_DELAY); if (ret != ESP_OK) { + ESP_LOGI(TAG, "Failed to queue next SPI transfer\n"); free(spi_trans->rx_buffer); spi_trans->rx_buffer = NULL; free((void *)spi_trans->tx_buffer); @@ -305,12 +320,11 @@ static void spi_transaction_post_process_task(void* pvParameters) spi_slave_transaction_t *spi_trans = NULL; esp_err_t ret = ESP_OK; interface_buffer_handle_t rx_buf_handle = {0}; - struct esp_payload_header *header = NULL; for (;;) { memset(&rx_buf_handle, 0, sizeof(rx_buf_handle)); - /* await transmission result, after any kind of transmission a new packet + /* Await transmission result, after any kind of transmission a new packet * (dummy or real) must be placed in SPI slave */ ret = spi_slave_get_trans_result(ESP_SPI_CONTROLLER, &spi_trans, @@ -349,7 +363,7 @@ static void spi_transaction_post_process_task(void* pvParameters) } } - /* free transaction structure */ + /* Free Transfer structure */ free(spi_trans); spi_trans = NULL; } @@ -418,7 +432,7 @@ static void generate_startup_event(uint8_t cap) buf_handle.payload_len = len + sizeof(struct esp_payload_header); header->checksum = htole16(compute_checksum(buf_handle.payload, buf_handle.payload_len)); - xQueueSend(spi_tx_queue, &buf_handle, portMAX_DELAY); + xQueueSend(spi_tx_queue[PRIO_Q_OTHERS], &buf_handle, portMAX_DELAY); /* indicate waiting data on ready pin */ WRITE_PERI_REG(GPIO_OUT_W1TS_REG, (1 << gpio_data_ready)); @@ -429,6 +443,7 @@ static void generate_startup_event(uint8_t cap) static interface_handle_t * esp_spi_init(uint8_t capabilities) { esp_err_t ret = ESP_OK; + uint8_t prio_q_idx = 0; /* Configuration for the SPI bus */ spi_bus_config_t buscfg={ @@ -438,7 +453,13 @@ static interface_handle_t * esp_spi_init(uint8_t capabilities) .quadwp_io_num = -1, .quadhd_io_num = -1, .max_transfer_sz = SPI_BUFFER_SIZE, +#if 0 + /* + * Moving ESP32 SPI slave interrupts in flash, Keeping it in IRAM gives crash, + * While performing flash erase operation. + */ .intr_flags=ESP_INTR_FLAG_IRAM +#endif }; /* Configuration for the SPI slave interface */ @@ -485,14 +506,16 @@ static interface_handle_t * esp_spi_init(uint8_t capabilities) memset(&if_handle_g, 0, sizeof(if_handle_g)); if_handle_g.state = INIT; - spi_rx_queue = xQueueCreate(SPI_RX_QUEUE_SIZE, sizeof(interface_buffer_handle_t)); - assert(spi_rx_queue != NULL); + for (prio_q_idx=0; prio_q_idxlen = htole16(buf_handle->payload_len); offset = sizeof(struct esp_payload_header); header->offset = htole16(offset); + header->seq_num = htole16(buf_handle->seq_num); + header->flags = buf_handle->flag; /* copy the data from caller */ memcpy(tx_buf_handle.payload + offset, buf_handle->payload, buf_handle->payload_len); @@ -557,7 +582,12 @@ static int32_t esp_spi_write(interface_handle_t *handle, interface_buffer_handle header->checksum = htole16(compute_checksum(tx_buf_handle.payload, offset+buf_handle->payload_len)); - ret = xQueueSend(spi_tx_queue, &tx_buf_handle, portMAX_DELAY); + if (header->if_type == ESP_SERIAL_IF) + ret = xQueueSend(spi_tx_queue[PRIO_Q_SERIAL], &tx_buf_handle, portMAX_DELAY); + else if (header->if_type == ESP_HCI_IF) + ret = xQueueSend(spi_tx_queue[PRIO_Q_BT], &tx_buf_handle, portMAX_DELAY); + else + ret = xQueueSend(spi_tx_queue[PRIO_Q_OTHERS], &tx_buf_handle, portMAX_DELAY); if (ret != pdTRUE) return ESP_FAIL; @@ -589,7 +619,20 @@ static interface_buffer_handle_t * esp_spi_read(interface_handle_t *if_handle) buf_handle = malloc(sizeof(interface_buffer_handle_t)); assert(buf_handle); - ret = xQueueReceive(spi_rx_queue, buf_handle, portMAX_DELAY); + while (1) { + if(uxQueueMessagesWaiting(spi_rx_queue[PRIO_Q_SERIAL])) { + ret = xQueueReceive(spi_rx_queue[PRIO_Q_SERIAL], buf_handle, portMAX_DELAY); + break; + } else if(uxQueueMessagesWaiting(spi_rx_queue[PRIO_Q_BT])) { + ret = xQueueReceive(spi_rx_queue[PRIO_Q_BT], buf_handle, portMAX_DELAY); + break; + } else if(uxQueueMessagesWaiting(spi_rx_queue[PRIO_Q_OTHERS])) { + ret = xQueueReceive(spi_rx_queue[PRIO_Q_OTHERS], buf_handle, portMAX_DELAY); + break; + } else { + vTaskDelay(1); + } + } if (ret != pdTRUE) { free(buf_handle); diff --git a/esp/esp_driver/network_adapter/sdkconfig.defaults.esp32 b/esp/esp_driver/network_adapter/sdkconfig.defaults.esp32 index 26fd63bb4d..c95ba95a92 100644 --- a/esp/esp_driver/network_adapter/sdkconfig.defaults.esp32 +++ b/esp/esp_driver/network_adapter/sdkconfig.defaults.esp32 @@ -1,5 +1,5 @@ -CONFIG_ESP32_DEFAULT_CPU_FREQ_160=y -CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=160 +CONFIG_ESP32_DEFAULT_CPU_FREQ_240=y +CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=240 CONFIG_SDIO_DAT2_DISABLED= # BT Configuration @@ -13,15 +13,20 @@ CONFIG_BTDM_CTRL_AUTO_LATENCY=y CONFIG_ESP32_WIFI_NVS_ENABLED= # BT over UART -CONFIG_BTDM_CONTROLLER_HCI_MODE_UART_H4=y -CONFIG_BTDM_CTRL_HCI_MODE_UART_H4=y -CONFIG_BT_HCI_UART_NO=1 -CONFIG_BT_HCI_UART_BAUDRATE=921600 +#CONFIG_BTDM_CONTROLLER_HCI_MODE_UART_H4=y +#CONFIG_BTDM_CTRL_HCI_MODE_UART_H4=y +#CONFIG_BT_HCI_UART_NO=1 +#CONFIG_BT_HCI_UART_BAUDRATE=921600 #CO-EX config CONFIG_FREERTOS_UNICORE=n -CONFIG_FREERTOS_HZ=1000 +CONFIG_FREERTOS_HZ=400 CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_1=y CONFIG_BTDM_CONTROLLER_PINNED_TO_CORE=0 CONFIG_BTDM_CTRL_PINNED_TO_CORE=0 CONFIG_BTDM_CTRL_PINNED_TO_CORE_0=y + +CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM=16 +CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM=32 +CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER=y +CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER_NUM=64 diff --git a/host/linux/host_driver/esp32/esp_serial.c b/host/linux/host_driver/esp32/esp_serial.c index 91ca61e1bd..b719ecd131 100644 --- a/host/linux/host_driver/esp32/esp_serial.c +++ b/host/linux/host_driver/esp32/esp_serial.c @@ -34,6 +34,7 @@ #define ESP_SERIAL_MAJOR 221 #define ESP_SERIAL_MINOR_MAX 2 #define ESP_RX_RB_SIZE 4096 +#define ESP_SERIAL_MAX_TX 4096 //#define ESP_SERIAL_TEST @@ -65,40 +66,72 @@ static ssize_t esp_serial_write(struct file *file, const char __user *user_buffe struct sk_buff * tx_skb = NULL; int ret = 0; size_t total_len = 0; + size_t frag_len = 0; + u32 left_len = size; + static u16 seq_num = 0; + u8 flag = 0; + u8 *pos; + + if (size > ESP_SERIAL_MAX_TX) { + printk(KERN_ERR "%s: Exceed max tx buffer size [%d]\n", __func__, size); + return 0; + } + seq_num++; dev = (struct esp_serial_devs *) file->private_data; - total_len = size + sizeof(struct esp_payload_header); + pos = (u8 *) user_buffer; + + do { + /* Fragmentation support + * - Fragment large packets into multiple 1500 byte packets + * - MORE_FRAGMENT bit in flag tells if there are more fragments expected + **/ + if (left_len > ETH_DATA_LEN) { + frag_len = ETH_DATA_LEN; + flag = MORE_FRAGMENT; + } else { + frag_len = left_len; + flag = 0; + } - tx_skb = esp_alloc_skb(total_len); - if (!tx_skb) { - printk (KERN_ERR "%s: SKB alloc failed\n", __func__); - return -ENOMEM; - } + total_len = frag_len + sizeof(struct esp_payload_header); - tx_buf = skb_put(tx_skb, total_len); + tx_skb = esp_alloc_skb(total_len); + if (!tx_skb) { + printk (KERN_ERR "%s: SKB alloc failed\n", __func__); + return (size - left_len); + } - hdr = (struct esp_payload_header *) tx_buf; + tx_buf = skb_put(tx_skb, total_len); - memset (hdr, 0, sizeof(struct esp_payload_header)); + hdr = (struct esp_payload_header *) tx_buf; - hdr->if_type = ESP_SERIAL_IF; - hdr->if_num = dev->dev_index; - hdr->len = cpu_to_le16(size); - hdr->offset = cpu_to_le16(sizeof(struct esp_payload_header)); + memset (hdr, 0, sizeof(struct esp_payload_header)); - ret = copy_from_user(tx_buf + hdr->offset, user_buffer, size); - if (ret) { - dev_kfree_skb(tx_skb); - printk(KERN_ERR "%s, Error copying buffer to send serial data\n", __func__); - return -EFAULT; - } - hdr->checksum = cpu_to_le16(compute_checksum(tx_skb->data, (size + sizeof(struct esp_payload_header)))); + hdr->if_type = ESP_SERIAL_IF; + hdr->if_num = dev->dev_index; + hdr->len = cpu_to_le16(frag_len); + hdr->seq_num = cpu_to_le16(seq_num); + hdr->offset = cpu_to_le16(sizeof(struct esp_payload_header)); + hdr->flags |= flag; - ret = esp_send_packet(dev->priv, tx_skb); - if (ret) { - printk (KERN_ERR "%s: Failed to transmit data, error %d\n", __func__, ret); - return ret; - } + ret = copy_from_user(tx_buf + hdr->offset, pos, frag_len); + if (ret) { + dev_kfree_skb(tx_skb); + printk(KERN_ERR "%s, Error copying buffer to send serial data\n", __func__); + return (size - left_len); + } + hdr->checksum = cpu_to_le16(compute_checksum(tx_skb->data, (frag_len + sizeof(struct esp_payload_header)))); + + ret = esp_send_packet(dev->priv, tx_skb); + if (ret) { + printk (KERN_ERR "%s: Failed to transmit data, error %d\n", __func__, ret); + return (size - left_len); + } + + left_len -= frag_len; + pos += frag_len; + } while(left_len); return size; } diff --git a/host/linux/host_driver/esp32/main.c b/host/linux/host_driver/esp32/main.c index 9d11c9a678..e14da6e629 100644 --- a/host/linux/host_driver/esp32/main.c +++ b/host/linux/host_driver/esp32/main.c @@ -297,7 +297,6 @@ static int process_tx_packet (struct sk_buff *skb) payload_header->if_num = priv->if_num; payload_header->len = cpu_to_le16(len); payload_header->offset = cpu_to_le16(pad_len); - payload_header->reserved1 = c % 255; payload_header->checksum = cpu_to_le16(compute_checksum(skb->data, (len + pad_len))); diff --git a/host/linux/host_driver/esp32/sdio/esp_sdio.c b/host/linux/host_driver/esp32/sdio/esp_sdio.c index 184cfdbd92..bc439d3a62 100644 --- a/host/linux/host_driver/esp32/sdio/esp_sdio.c +++ b/host/linux/host_driver/esp32/sdio/esp_sdio.c @@ -44,7 +44,7 @@ struct esp_sdio_context sdio_context; static atomic_t tx_pending; -static atomic_t queue_items; +static atomic_t queue_items[MAX_PRIORITY_QUEUES]; #ifdef CONFIG_ENABLE_MONITOR_PROCESS struct task_struct *monitor_thread; @@ -257,6 +257,7 @@ static void flush_sdio(struct esp_sdio_context *context) static void esp_remove(struct sdio_func *func) { struct esp_sdio_context *context; + uint8_t prio_q_idx = 0; context = sdio_get_drvdata(func); printk(KERN_INFO "%s -> Remove card", __func__); @@ -287,7 +288,9 @@ static void esp_remove(struct sdio_func *func) } } - skb_queue_purge(&(sdio_context.tx_q)); + for (prio_q_idx=0; prio_q_idxadapter)) printk (KERN_ERR "%s: Failed to get adapter\n", __func__); - skb_queue_head_init(&(sdio_context.tx_q)); - atomic_set(&queue_items, 0); + for (prio_q_idx=0; prio_q_idxadapter->if_type = ESP_IF_TYPE_SDIO; @@ -449,6 +455,7 @@ static struct sk_buff * read_packet(struct esp_adapter *adapter) static int write_packet(struct esp_adapter *adapter, struct sk_buff *skb) { u32 max_pkt_size = ESP_RX_BUFFER_SIZE - sizeof(struct esp_payload_header); + struct esp_payload_header *payload_header = (struct esp_payload_header *) skb->data; if (!adapter || !adapter->if_context || !skb || !skb->data || !skb->len) { printk(KERN_ERR "%s: Invalid args\n", __func__); @@ -471,13 +478,20 @@ static int write_packet(struct esp_adapter *adapter, struct sk_buff *skb) return -EBUSY; } - /* Notify to process queue */ - atomic_inc(&queue_items); - /* Enqueue SKB in tx_q */ atomic_inc(&tx_pending); - skb_queue_tail(&(sdio_context.tx_q), skb); + /* Notify to process queue */ + if (payload_header->if_type == ESP_SERIAL_IF) { + atomic_inc(&queue_items[PRIO_Q_SERIAL]); + skb_queue_tail(&(sdio_context.tx_q[PRIO_Q_SERIAL]), skb); + } else if (payload_header->if_type == ESP_HCI_IF) { + atomic_inc(&queue_items[PRIO_Q_BT]); + skb_queue_tail(&(sdio_context.tx_q[PRIO_Q_BT]), skb); + } else { + atomic_inc(&queue_items[PRIO_Q_OTHERS]); + skb_queue_tail(&(sdio_context.tx_q[PRIO_Q_OTHERS]), skb); + } return 0; } @@ -503,19 +517,29 @@ static int tx_process(void *data) continue; } - if (atomic_read(&queue_items) <= 0) { - msleep(10); - continue; - } - - tx_skb = skb_dequeue(&(context->tx_q)); - if (!tx_skb) { + if (atomic_read(&queue_items[PRIO_Q_SERIAL]) > 0) { + tx_skb = skb_dequeue(&(context->tx_q[PRIO_Q_SERIAL])); + if (!tx_skb) { + continue; + } + atomic_dec(&queue_items[PRIO_Q_SERIAL]); + }else if (atomic_read(&queue_items[PRIO_Q_BT]) > 0) { + tx_skb = skb_dequeue(&(context->tx_q[PRIO_Q_BT])); + if (!tx_skb) { + continue; + } + atomic_dec(&queue_items[PRIO_Q_BT]); + } else if (atomic_read(&queue_items[PRIO_Q_OTHERS]) > 0) { + tx_skb = skb_dequeue(&(context->tx_q[PRIO_Q_OTHERS])); + if (!tx_skb) { + continue; + } + atomic_dec(&queue_items[PRIO_Q_OTHERS]); + } else { + msleep(1); continue; } - if (atomic_read(&queue_items)) - atomic_dec(&queue_items); - if (atomic_read(&tx_pending)) atomic_dec(&tx_pending); diff --git a/host/linux/host_driver/esp32/sdio/esp_sdio_decl.h b/host/linux/host_driver/esp32/sdio/esp_sdio_decl.h index 6d3a9ee1bb..1758d5b816 100644 --- a/host/linux/host_driver/esp32/sdio/esp_sdio_decl.h +++ b/host/linux/host_driver/esp32/sdio/esp_sdio_decl.h @@ -92,7 +92,7 @@ struct esp_sdio_context { struct esp_adapter *adapter; struct sdio_func *func; enum context_state state; - struct sk_buff_head tx_q; + struct sk_buff_head tx_q[MAX_PRIORITY_QUEUES]; u32 rx_byte_count; u32 tx_buffer_count; }; diff --git a/host/linux/host_driver/esp32/spi/esp_spi.c b/host/linux/host_driver/esp32/spi/esp_spi.c index be915a5102..67b60da07f 100644 --- a/host/linux/host_driver/esp32/spi/esp_spi.c +++ b/host/linux/host_driver/esp32/spi/esp_spi.c @@ -104,7 +104,11 @@ static struct sk_buff * read_packet(struct esp_adapter *adapter) context = adapter->if_context; if (context->esp_spi_dev) { - skb = skb_dequeue(&(context->rx_q)); + skb = skb_dequeue(&(context->rx_q[PRIO_Q_SERIAL])); + if (!skb) + skb = skb_dequeue(&(context->rx_q[PRIO_Q_BT])); + if (!skb) + skb = skb_dequeue(&(context->rx_q[PRIO_Q_OTHERS])); } else { printk (KERN_ERR "%s: Invalid args\n", __func__); return NULL; @@ -116,6 +120,7 @@ static struct sk_buff * read_packet(struct esp_adapter *adapter) static int write_packet(struct esp_adapter *adapter, struct sk_buff *skb) { u32 max_pkt_size = SPI_BUF_SIZE - sizeof(struct esp_payload_header); + struct esp_payload_header *payload_header = (struct esp_payload_header *) skb->data; if (!adapter || !adapter->if_context || !skb || !skb->data || !skb->len) { printk (KERN_ERR "%s: Invalid args\n", __func__); @@ -135,16 +140,23 @@ static int write_packet(struct esp_adapter *adapter, struct sk_buff *skb) return -EPERM; } - if (atomic_read(&tx_pending) >= TX_MAX_PENDING_COUNT) { - esp_tx_pause(); - dev_kfree_skb(skb); - queue_work(spi_context.spi_workqueue, &spi_context.spi_work); - return -EBUSY; - } /* Enqueue SKB in tx_q */ - skb_queue_tail(&spi_context.tx_q, skb); - atomic_inc(&tx_pending); + if (payload_header->if_type == ESP_SERIAL_IF) { + skb_queue_tail(&spi_context.tx_q[PRIO_Q_SERIAL], skb); + } else if (payload_header->if_type == ESP_HCI_IF) { + skb_queue_tail(&spi_context.tx_q[PRIO_Q_BT], skb); + } else { + if (atomic_read(&tx_pending) >= TX_MAX_PENDING_COUNT) { + esp_tx_pause(); + dev_kfree_skb(skb); + if (spi_context.spi_workqueue) + queue_work(spi_context.spi_workqueue, &spi_context.spi_work); + return -EBUSY; + } + skb_queue_tail(&spi_context.tx_q[PRIO_Q_OTHERS], skb); + atomic_inc(&tx_pending); + } if (spi_context.spi_workqueue) queue_work(spi_context.spi_workqueue, &spi_context.spi_work); @@ -281,7 +293,12 @@ static int process_rx_buf(struct sk_buff *skb) return -EPERM; /* enqueue skb for read_packet to pick it */ - skb_queue_tail(&spi_context.rx_q, skb); + if (header->if_type == ESP_SERIAL_IF) + skb_queue_tail(&spi_context.rx_q[PRIO_Q_SERIAL], skb); + else if (header->if_type == ESP_HCI_IF) + skb_queue_tail(&spi_context.rx_q[PRIO_Q_BT], skb); + else + skb_queue_tail(&spi_context.rx_q[PRIO_Q_OTHERS], skb); /* indicate reception of new packet */ esp_process_new_packet_intr(spi_context.adapter); @@ -304,7 +321,11 @@ static void esp_spi_work(struct work_struct *work) if (trans_ready) { if (data_path) { - tx_skb = skb_dequeue(&spi_context.tx_q); + tx_skb = skb_dequeue(&spi_context.tx_q[PRIO_Q_SERIAL]); + if (!tx_skb) + tx_skb = skb_dequeue(&spi_context.tx_q[PRIO_Q_BT]); + if (!tx_skb) + tx_skb = skb_dequeue(&spi_context.tx_q[PRIO_Q_OTHERS]); if (tx_skb) { if (atomic_read(&tx_pending)) atomic_dec(&tx_pending); @@ -475,6 +496,7 @@ static int spi_reinit_spidev(int spi_clk_mhz) static int spi_init(void) { int status = 0; + uint8_t prio_q_idx = 0; spi_context.spi_workqueue = create_workqueue("ESP_SPI_WORK_QUEUE"); @@ -486,8 +508,11 @@ static int spi_init(void) INIT_WORK(&spi_context.spi_work, esp_spi_work); - skb_queue_head_init(&spi_context.tx_q); - skb_queue_head_init(&spi_context.rx_q); + for (prio_q_idx=0; prio_q_idx Date: Thu, 7 Oct 2021 21:36:53 +0530 Subject: [PATCH 12/40] OTA support added to flash new image at ESP Co-authored-by: Ajita Chavan Co-authored-by: Mangesh Malusare Co-authored-by: Yogesh Mantri --- common/esp_hosted_config.pb-c.c | 552 +++++++++++++++++- common/include/esp_hosted_config.pb-c.h | 226 ++++++- common/proto/esp_hosted_config.proto | 34 ++ .../network_adapter/main/Kconfig.projbuild | 6 + .../network_adapter/main/app_main.c | 11 +- .../network_adapter/main/slave_commands.c | 213 ++++++- .../network_adapter/sdkconfig.defaults | 4 + .../network_adapter/sdkconfig.defaults.esp32 | 4 + host/host_common/commands.c | 199 ++++++- host/host_common/include/commands.h | 28 +- host/linux/host_control/c_support/test.c | 36 +- host/linux/host_control/c_support/test_api.c | 95 ++- host/linux/host_control/c_support/test_api.h | 7 + .../host_control/c_support/test_config.h | 2 + .../host_control/python_support/commands.py | 112 +++- .../python_support/esp_hosted_config_pb2.py | 346 ++++++++++- .../host_control/python_support/ota_update.py | 69 +++ ota_feature.md | 70 +++ 18 files changed, 1950 insertions(+), 64 deletions(-) create mode 100644 host/linux/host_control/python_support/ota_update.py create mode 100644 ota_feature.md diff --git a/common/esp_hosted_config.pb-c.c b/common/esp_hosted_config.pb-c.c index e51f328fed..6215e433da 100644 --- a/common/esp_hosted_config.pb-c.c +++ b/common/esp_hosted_config.pb-c.c @@ -1039,6 +1039,264 @@ void esp_hosted_resp_connected_sta__free_unpacked assert(message->base.descriptor == &esp_hosted_resp_connected_sta__descriptor); protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); } +void esp_hosted_cmd_otabegin__init + (EspHostedCmdOTABegin *message) +{ + static EspHostedCmdOTABegin init_value = ESP_HOSTED_CMD_OTABEGIN__INIT; + *message = init_value; +} +size_t esp_hosted_cmd_otabegin__get_packed_size + (const EspHostedCmdOTABegin *message) +{ + assert(message->base.descriptor == &esp_hosted_cmd_otabegin__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t esp_hosted_cmd_otabegin__pack + (const EspHostedCmdOTABegin *message, + uint8_t *out) +{ + assert(message->base.descriptor == &esp_hosted_cmd_otabegin__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t esp_hosted_cmd_otabegin__pack_to_buffer + (const EspHostedCmdOTABegin *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &esp_hosted_cmd_otabegin__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +EspHostedCmdOTABegin * + esp_hosted_cmd_otabegin__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (EspHostedCmdOTABegin *) + protobuf_c_message_unpack (&esp_hosted_cmd_otabegin__descriptor, + allocator, len, data); +} +void esp_hosted_cmd_otabegin__free_unpacked + (EspHostedCmdOTABegin *message, + ProtobufCAllocator *allocator) +{ + assert(message->base.descriptor == &esp_hosted_cmd_otabegin__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void esp_hosted_resp_otabegin__init + (EspHostedRespOTABegin *message) +{ + static EspHostedRespOTABegin init_value = ESP_HOSTED_RESP_OTABEGIN__INIT; + *message = init_value; +} +size_t esp_hosted_resp_otabegin__get_packed_size + (const EspHostedRespOTABegin *message) +{ + assert(message->base.descriptor == &esp_hosted_resp_otabegin__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t esp_hosted_resp_otabegin__pack + (const EspHostedRespOTABegin *message, + uint8_t *out) +{ + assert(message->base.descriptor == &esp_hosted_resp_otabegin__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t esp_hosted_resp_otabegin__pack_to_buffer + (const EspHostedRespOTABegin *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &esp_hosted_resp_otabegin__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +EspHostedRespOTABegin * + esp_hosted_resp_otabegin__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (EspHostedRespOTABegin *) + protobuf_c_message_unpack (&esp_hosted_resp_otabegin__descriptor, + allocator, len, data); +} +void esp_hosted_resp_otabegin__free_unpacked + (EspHostedRespOTABegin *message, + ProtobufCAllocator *allocator) +{ + assert(message->base.descriptor == &esp_hosted_resp_otabegin__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void esp_hosted_cmd_otawrite__init + (EspHostedCmdOTAWrite *message) +{ + static EspHostedCmdOTAWrite init_value = ESP_HOSTED_CMD_OTAWRITE__INIT; + *message = init_value; +} +size_t esp_hosted_cmd_otawrite__get_packed_size + (const EspHostedCmdOTAWrite *message) +{ + assert(message->base.descriptor == &esp_hosted_cmd_otawrite__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t esp_hosted_cmd_otawrite__pack + (const EspHostedCmdOTAWrite *message, + uint8_t *out) +{ + assert(message->base.descriptor == &esp_hosted_cmd_otawrite__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t esp_hosted_cmd_otawrite__pack_to_buffer + (const EspHostedCmdOTAWrite *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &esp_hosted_cmd_otawrite__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +EspHostedCmdOTAWrite * + esp_hosted_cmd_otawrite__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (EspHostedCmdOTAWrite *) + protobuf_c_message_unpack (&esp_hosted_cmd_otawrite__descriptor, + allocator, len, data); +} +void esp_hosted_cmd_otawrite__free_unpacked + (EspHostedCmdOTAWrite *message, + ProtobufCAllocator *allocator) +{ + assert(message->base.descriptor == &esp_hosted_cmd_otawrite__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void esp_hosted_resp_otawrite__init + (EspHostedRespOTAWrite *message) +{ + static EspHostedRespOTAWrite init_value = ESP_HOSTED_RESP_OTAWRITE__INIT; + *message = init_value; +} +size_t esp_hosted_resp_otawrite__get_packed_size + (const EspHostedRespOTAWrite *message) +{ + assert(message->base.descriptor == &esp_hosted_resp_otawrite__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t esp_hosted_resp_otawrite__pack + (const EspHostedRespOTAWrite *message, + uint8_t *out) +{ + assert(message->base.descriptor == &esp_hosted_resp_otawrite__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t esp_hosted_resp_otawrite__pack_to_buffer + (const EspHostedRespOTAWrite *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &esp_hosted_resp_otawrite__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +EspHostedRespOTAWrite * + esp_hosted_resp_otawrite__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (EspHostedRespOTAWrite *) + protobuf_c_message_unpack (&esp_hosted_resp_otawrite__descriptor, + allocator, len, data); +} +void esp_hosted_resp_otawrite__free_unpacked + (EspHostedRespOTAWrite *message, + ProtobufCAllocator *allocator) +{ + assert(message->base.descriptor == &esp_hosted_resp_otawrite__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void esp_hosted_cmd_otaend__init + (EspHostedCmdOTAEnd *message) +{ + static EspHostedCmdOTAEnd init_value = ESP_HOSTED_CMD_OTAEND__INIT; + *message = init_value; +} +size_t esp_hosted_cmd_otaend__get_packed_size + (const EspHostedCmdOTAEnd *message) +{ + assert(message->base.descriptor == &esp_hosted_cmd_otaend__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t esp_hosted_cmd_otaend__pack + (const EspHostedCmdOTAEnd *message, + uint8_t *out) +{ + assert(message->base.descriptor == &esp_hosted_cmd_otaend__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t esp_hosted_cmd_otaend__pack_to_buffer + (const EspHostedCmdOTAEnd *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &esp_hosted_cmd_otaend__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +EspHostedCmdOTAEnd * + esp_hosted_cmd_otaend__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (EspHostedCmdOTAEnd *) + protobuf_c_message_unpack (&esp_hosted_cmd_otaend__descriptor, + allocator, len, data); +} +void esp_hosted_cmd_otaend__free_unpacked + (EspHostedCmdOTAEnd *message, + ProtobufCAllocator *allocator) +{ + assert(message->base.descriptor == &esp_hosted_cmd_otaend__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void esp_hosted_resp_otaend__init + (EspHostedRespOTAEnd *message) +{ + static EspHostedRespOTAEnd init_value = ESP_HOSTED_RESP_OTAEND__INIT; + *message = init_value; +} +size_t esp_hosted_resp_otaend__get_packed_size + (const EspHostedRespOTAEnd *message) +{ + assert(message->base.descriptor == &esp_hosted_resp_otaend__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t esp_hosted_resp_otaend__pack + (const EspHostedRespOTAEnd *message, + uint8_t *out) +{ + assert(message->base.descriptor == &esp_hosted_resp_otaend__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t esp_hosted_resp_otaend__pack_to_buffer + (const EspHostedRespOTAEnd *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &esp_hosted_resp_otaend__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +EspHostedRespOTAEnd * + esp_hosted_resp_otaend__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (EspHostedRespOTAEnd *) + protobuf_c_message_unpack (&esp_hosted_resp_otaend__descriptor, + allocator, len, data); +} +void esp_hosted_resp_otaend__free_unpacked + (EspHostedRespOTAEnd *message, + ProtobufCAllocator *allocator) +{ + assert(message->base.descriptor == &esp_hosted_resp_otaend__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} void esp_hosted_config_payload__init (EspHostedConfigPayload *message) { @@ -2316,7 +2574,195 @@ const ProtobufCMessageDescriptor esp_hosted_resp_connected_sta__descriptor = (ProtobufCMessageInit) esp_hosted_resp_connected_sta__init, NULL,NULL,NULL /* reserved[123] */ }; -static const ProtobufCFieldDescriptor esp_hosted_config_payload__field_descriptors[29] = +#define esp_hosted_cmd_otabegin__field_descriptors NULL +#define esp_hosted_cmd_otabegin__field_indices_by_name NULL +#define esp_hosted_cmd_otabegin__number_ranges NULL +const ProtobufCMessageDescriptor esp_hosted_cmd_otabegin__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "EspHostedCmdOTABegin", + "EspHostedCmdOTABegin", + "EspHostedCmdOTABegin", + "", + sizeof(EspHostedCmdOTABegin), + 0, + esp_hosted_cmd_otabegin__field_descriptors, + esp_hosted_cmd_otabegin__field_indices_by_name, + 0, esp_hosted_cmd_otabegin__number_ranges, + (ProtobufCMessageInit) esp_hosted_cmd_otabegin__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor esp_hosted_resp_otabegin__field_descriptors[1] = +{ + { + "resp", + 1, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_INT32, + offsetof(EspHostedRespOTABegin, has_resp), + offsetof(EspHostedRespOTABegin, resp), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned esp_hosted_resp_otabegin__field_indices_by_name[] = { + 0, /* field[0] = resp */ +}; +static const ProtobufCIntRange esp_hosted_resp_otabegin__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 1 } +}; +const ProtobufCMessageDescriptor esp_hosted_resp_otabegin__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "EspHostedRespOTABegin", + "EspHostedRespOTABegin", + "EspHostedRespOTABegin", + "", + sizeof(EspHostedRespOTABegin), + 1, + esp_hosted_resp_otabegin__field_descriptors, + esp_hosted_resp_otabegin__field_indices_by_name, + 1, esp_hosted_resp_otabegin__number_ranges, + (ProtobufCMessageInit) esp_hosted_resp_otabegin__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor esp_hosted_cmd_otawrite__field_descriptors[1] = +{ + { + "ota_data", + 1, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BYTES, + offsetof(EspHostedCmdOTAWrite, has_ota_data), + offsetof(EspHostedCmdOTAWrite, ota_data), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned esp_hosted_cmd_otawrite__field_indices_by_name[] = { + 0, /* field[0] = ota_data */ +}; +static const ProtobufCIntRange esp_hosted_cmd_otawrite__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 1 } +}; +const ProtobufCMessageDescriptor esp_hosted_cmd_otawrite__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "EspHostedCmdOTAWrite", + "EspHostedCmdOTAWrite", + "EspHostedCmdOTAWrite", + "", + sizeof(EspHostedCmdOTAWrite), + 1, + esp_hosted_cmd_otawrite__field_descriptors, + esp_hosted_cmd_otawrite__field_indices_by_name, + 1, esp_hosted_cmd_otawrite__number_ranges, + (ProtobufCMessageInit) esp_hosted_cmd_otawrite__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor esp_hosted_resp_otawrite__field_descriptors[1] = +{ + { + "resp", + 1, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_INT32, + offsetof(EspHostedRespOTAWrite, has_resp), + offsetof(EspHostedRespOTAWrite, resp), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned esp_hosted_resp_otawrite__field_indices_by_name[] = { + 0, /* field[0] = resp */ +}; +static const ProtobufCIntRange esp_hosted_resp_otawrite__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 1 } +}; +const ProtobufCMessageDescriptor esp_hosted_resp_otawrite__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "EspHostedRespOTAWrite", + "EspHostedRespOTAWrite", + "EspHostedRespOTAWrite", + "", + sizeof(EspHostedRespOTAWrite), + 1, + esp_hosted_resp_otawrite__field_descriptors, + esp_hosted_resp_otawrite__field_indices_by_name, + 1, esp_hosted_resp_otawrite__number_ranges, + (ProtobufCMessageInit) esp_hosted_resp_otawrite__init, + NULL,NULL,NULL /* reserved[123] */ +}; +#define esp_hosted_cmd_otaend__field_descriptors NULL +#define esp_hosted_cmd_otaend__field_indices_by_name NULL +#define esp_hosted_cmd_otaend__number_ranges NULL +const ProtobufCMessageDescriptor esp_hosted_cmd_otaend__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "EspHostedCmdOTAEnd", + "EspHostedCmdOTAEnd", + "EspHostedCmdOTAEnd", + "", + sizeof(EspHostedCmdOTAEnd), + 0, + esp_hosted_cmd_otaend__field_descriptors, + esp_hosted_cmd_otaend__field_indices_by_name, + 0, esp_hosted_cmd_otaend__number_ranges, + (ProtobufCMessageInit) esp_hosted_cmd_otaend__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor esp_hosted_resp_otaend__field_descriptors[1] = +{ + { + "resp", + 1, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_INT32, + offsetof(EspHostedRespOTAEnd, has_resp), + offsetof(EspHostedRespOTAEnd, resp), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned esp_hosted_resp_otaend__field_indices_by_name[] = { + 0, /* field[0] = resp */ +}; +static const ProtobufCIntRange esp_hosted_resp_otaend__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 1 } +}; +const ProtobufCMessageDescriptor esp_hosted_resp_otaend__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "EspHostedRespOTAEnd", + "EspHostedRespOTAEnd", + "EspHostedRespOTAEnd", + "", + sizeof(EspHostedRespOTAEnd), + 1, + esp_hosted_resp_otaend__field_descriptors, + esp_hosted_resp_otaend__field_indices_by_name, + 1, esp_hosted_resp_otaend__number_ranges, + (ProtobufCMessageInit) esp_hosted_resp_otaend__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor esp_hosted_config_payload__field_descriptors[35] = { { "msg", @@ -2666,6 +3112,78 @@ static const ProtobufCFieldDescriptor esp_hosted_config_payload__field_descripto 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ 0,NULL,NULL /* reserved1,reserved2, etc */ }, + { + "cmd_ota_begin", + 38, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(EspHostedConfigPayload, payload_case), + offsetof(EspHostedConfigPayload, cmd_ota_begin), + &esp_hosted_cmd_otabegin__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "resp_ota_begin", + 39, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(EspHostedConfigPayload, payload_case), + offsetof(EspHostedConfigPayload, resp_ota_begin), + &esp_hosted_resp_otabegin__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "cmd_ota_write", + 40, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(EspHostedConfigPayload, payload_case), + offsetof(EspHostedConfigPayload, cmd_ota_write), + &esp_hosted_cmd_otawrite__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "resp_ota_write", + 41, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(EspHostedConfigPayload, payload_case), + offsetof(EspHostedConfigPayload, resp_ota_write), + &esp_hosted_resp_otawrite__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "cmd_ota_end", + 42, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(EspHostedConfigPayload, payload_case), + offsetof(EspHostedConfigPayload, cmd_ota_end), + &esp_hosted_cmd_otaend__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "resp_ota_end", + 43, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(EspHostedConfigPayload, payload_case), + offsetof(EspHostedConfigPayload, resp_ota_end), + &esp_hosted_resp_otaend__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, }; static const unsigned esp_hosted_config_payload__field_indices_by_name[] = { 21, /* field[21] = cmd_connected_stas_list */ @@ -2675,6 +3193,9 @@ static const unsigned esp_hosted_config_payload__field_indices_by_name[] = { 27, /* field[27] = cmd_get_power_save_mode */ 11, /* field[11] = cmd_get_softap_config */ 3, /* field[3] = cmd_get_wifi_mode */ + 29, /* field[29] = cmd_ota_begin */ + 33, /* field[33] = cmd_ota_end */ + 31, /* field[31] = cmd_ota_write */ 19, /* field[19] = cmd_scan_ap_list */ 9, /* field[9] = cmd_set_ap_config */ 23, /* field[23] = cmd_set_mac_address */ @@ -2690,6 +3211,9 @@ static const unsigned esp_hosted_config_payload__field_indices_by_name[] = { 28, /* field[28] = resp_get_power_save_mode */ 12, /* field[12] = resp_get_softap_config */ 4, /* field[4] = resp_get_wifi_mode */ + 30, /* field[30] = resp_ota_begin */ + 34, /* field[34] = resp_ota_end */ + 32, /* field[32] = resp_ota_write */ 20, /* field[20] = resp_scan_ap_list */ 10, /* field[10] = resp_set_ap_config */ 24, /* field[24] = resp_set_mac_address */ @@ -2702,7 +3226,7 @@ static const ProtobufCIntRange esp_hosted_config_payload__number_ranges[2 + 1] = { { 1, 0 }, { 10, 1 }, - { 0, 29 } + { 0, 35 } }; const ProtobufCMessageDescriptor esp_hosted_config_payload__descriptor = { @@ -2712,7 +3236,7 @@ const ProtobufCMessageDescriptor esp_hosted_config_payload__descriptor = "EspHostedConfigPayload", "", sizeof(EspHostedConfigPayload), - 29, + 35, esp_hosted_config_payload__field_descriptors, esp_hosted_config_payload__field_indices_by_name, 2, esp_hosted_config_payload__number_ranges, @@ -2791,7 +3315,7 @@ const ProtobufCEnumDescriptor esp_hosted_status__descriptor = esp_hosted_status__value_ranges, NULL,NULL,NULL,NULL /* reserved[1234] */ }; -static const ProtobufCEnumValue esp_hosted_config_msg_type__enum_values_by_number[28] = +static const ProtobufCEnumValue esp_hosted_config_msg_type__enum_values_by_number[34] = { { "TypeCmdGetMACAddress", "ESP_HOSTED_CONFIG_MSG_TYPE__TypeCmdGetMACAddress", 0 }, { "TypeRespGetMACAddress", "ESP_HOSTED_CONFIG_MSG_TYPE__TypeRespGetMACAddress", 1 }, @@ -2821,11 +3345,17 @@ static const ProtobufCEnumValue esp_hosted_config_msg_type__enum_values_by_numbe { "TypeRespSetPowerSaveMode", "ESP_HOSTED_CONFIG_MSG_TYPE__TypeRespSetPowerSaveMode", 25 }, { "TypeCmdGetPowerSaveMode", "ESP_HOSTED_CONFIG_MSG_TYPE__TypeCmdGetPowerSaveMode", 26 }, { "TypeRespGetPowerSaveMode", "ESP_HOSTED_CONFIG_MSG_TYPE__TypeRespGetPowerSaveMode", 27 }, + { "TypeCmdOTABegin", "ESP_HOSTED_CONFIG_MSG_TYPE__TypeCmdOTABegin", 28 }, + { "TypeRespOTABegin", "ESP_HOSTED_CONFIG_MSG_TYPE__TypeRespOTABegin", 29 }, + { "TypeCmdOTAWrite", "ESP_HOSTED_CONFIG_MSG_TYPE__TypeCmdOTAWrite", 30 }, + { "TypeRespOTAWrite", "ESP_HOSTED_CONFIG_MSG_TYPE__TypeRespOTAWrite", 31 }, + { "TypeCmdOTAEnd", "ESP_HOSTED_CONFIG_MSG_TYPE__TypeCmdOTAEnd", 32 }, + { "TypeRespOTAEnd", "ESP_HOSTED_CONFIG_MSG_TYPE__TypeRespOTAEnd", 33 }, }; static const ProtobufCIntRange esp_hosted_config_msg_type__value_ranges[] = { -{0, 0},{0, 28} +{0, 0},{0, 34} }; -static const ProtobufCEnumValueIndex esp_hosted_config_msg_type__enum_values_by_name[28] = +static const ProtobufCEnumValueIndex esp_hosted_config_msg_type__enum_values_by_name[34] = { { "TypeCmdDisconnectAP", 14 }, { "TypeCmdGetAPConfig", 6 }, @@ -2835,6 +3365,9 @@ static const ProtobufCEnumValueIndex esp_hosted_config_msg_type__enum_values_by_ { "TypeCmdGetPowerSaveMode", 26 }, { "TypeCmdGetSoftAPConfig", 10 }, { "TypeCmdGetWiFiMode", 2 }, + { "TypeCmdOTABegin", 28 }, + { "TypeCmdOTAEnd", 32 }, + { "TypeCmdOTAWrite", 30 }, { "TypeCmdSetAPConfig", 8 }, { "TypeCmdSetMacAddress", 22 }, { "TypeCmdSetPowerSaveMode", 24 }, @@ -2849,6 +3382,9 @@ static const ProtobufCEnumValueIndex esp_hosted_config_msg_type__enum_values_by_ { "TypeRespGetPowerSaveMode", 27 }, { "TypeRespGetSoftAPConfig", 11 }, { "TypeRespGetWiFiMode", 3 }, + { "TypeRespOTABegin", 29 }, + { "TypeRespOTAEnd", 33 }, + { "TypeRespOTAWrite", 31 }, { "TypeRespSetAPConfig", 9 }, { "TypeRespSetMacAddress", 23 }, { "TypeRespSetPowerSaveMode", 25 }, @@ -2863,9 +3399,9 @@ const ProtobufCEnumDescriptor esp_hosted_config_msg_type__descriptor = "EspHostedConfigMsgType", "EspHostedConfigMsgType", "", - 28, + 34, esp_hosted_config_msg_type__enum_values_by_number, - 28, + 34, esp_hosted_config_msg_type__enum_values_by_name, 1, esp_hosted_config_msg_type__value_ranges, diff --git a/common/include/esp_hosted_config.pb-c.h b/common/include/esp_hosted_config.pb-c.h index 538a6a6ce6..a855a7f645 100644 --- a/common/include/esp_hosted_config.pb-c.h +++ b/common/include/esp_hosted_config.pb-c.h @@ -39,6 +39,12 @@ typedef struct _EspHostedRespScanResult EspHostedRespScanResult; typedef struct _EspHostedConnectedSTAList EspHostedConnectedSTAList; typedef struct _EspHostedCmdConnectedSTA EspHostedCmdConnectedSTA; typedef struct _EspHostedRespConnectedSTA EspHostedRespConnectedSTA; +typedef struct _EspHostedCmdOTABegin EspHostedCmdOTABegin; +typedef struct _EspHostedRespOTABegin EspHostedRespOTABegin; +typedef struct _EspHostedCmdOTAWrite EspHostedCmdOTAWrite; +typedef struct _EspHostedRespOTAWrite EspHostedRespOTAWrite; +typedef struct _EspHostedCmdOTAEnd EspHostedCmdOTAEnd; +typedef struct _EspHostedRespOTAEnd EspHostedRespOTAEnd; typedef struct _EspHostedConfigPayload EspHostedConfigPayload; @@ -90,7 +96,13 @@ typedef enum _EspHostedConfigMsgType { ESP_HOSTED_CONFIG_MSG_TYPE__TypeCmdSetPowerSaveMode = 24, ESP_HOSTED_CONFIG_MSG_TYPE__TypeRespSetPowerSaveMode = 25, ESP_HOSTED_CONFIG_MSG_TYPE__TypeCmdGetPowerSaveMode = 26, - ESP_HOSTED_CONFIG_MSG_TYPE__TypeRespGetPowerSaveMode = 27 + ESP_HOSTED_CONFIG_MSG_TYPE__TypeRespGetPowerSaveMode = 27, + ESP_HOSTED_CONFIG_MSG_TYPE__TypeCmdOTABegin = 28, + ESP_HOSTED_CONFIG_MSG_TYPE__TypeRespOTABegin = 29, + ESP_HOSTED_CONFIG_MSG_TYPE__TypeCmdOTAWrite = 30, + ESP_HOSTED_CONFIG_MSG_TYPE__TypeRespOTAWrite = 31, + ESP_HOSTED_CONFIG_MSG_TYPE__TypeCmdOTAEnd = 32, + ESP_HOSTED_CONFIG_MSG_TYPE__TypeRespOTAEnd = 33 PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(ESP_HOSTED_CONFIG_MSG_TYPE) } EspHostedConfigMsgType; @@ -411,6 +423,68 @@ struct _EspHostedRespConnectedSTA , 0,0, 0,NULL, 0,0 } +struct _EspHostedCmdOTABegin +{ + ProtobufCMessage base; +}; +#define ESP_HOSTED_CMD_OTABEGIN__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&esp_hosted_cmd_otabegin__descriptor) \ + } + + +struct _EspHostedRespOTABegin +{ + ProtobufCMessage base; + protobuf_c_boolean has_resp; + int32_t resp; +}; +#define ESP_HOSTED_RESP_OTABEGIN__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&esp_hosted_resp_otabegin__descriptor) \ + , 0,0 } + + +struct _EspHostedCmdOTAWrite +{ + ProtobufCMessage base; + protobuf_c_boolean has_ota_data; + ProtobufCBinaryData ota_data; +}; +#define ESP_HOSTED_CMD_OTAWRITE__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&esp_hosted_cmd_otawrite__descriptor) \ + , 0,{0,NULL} } + + +struct _EspHostedRespOTAWrite +{ + ProtobufCMessage base; + protobuf_c_boolean has_resp; + int32_t resp; +}; +#define ESP_HOSTED_RESP_OTAWRITE__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&esp_hosted_resp_otawrite__descriptor) \ + , 0,0 } + + +struct _EspHostedCmdOTAEnd +{ + ProtobufCMessage base; +}; +#define ESP_HOSTED_CMD_OTAEND__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&esp_hosted_cmd_otaend__descriptor) \ + } + + +struct _EspHostedRespOTAEnd +{ + ProtobufCMessage base; + protobuf_c_boolean has_resp; + int32_t resp; +}; +#define ESP_HOSTED_RESP_OTAEND__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&esp_hosted_resp_otaend__descriptor) \ + , 0,0 } + + typedef enum { ESP_HOSTED_CONFIG_PAYLOAD__PAYLOAD__NOT_SET = 0, ESP_HOSTED_CONFIG_PAYLOAD__PAYLOAD_CMD_GET_MAC_ADDRESS = 10, @@ -441,6 +515,12 @@ typedef enum { ESP_HOSTED_CONFIG_PAYLOAD__PAYLOAD_RESP_SET_POWER_SAVE_MODE = 35, ESP_HOSTED_CONFIG_PAYLOAD__PAYLOAD_CMD_GET_POWER_SAVE_MODE = 36, ESP_HOSTED_CONFIG_PAYLOAD__PAYLOAD_RESP_GET_POWER_SAVE_MODE = 37, + ESP_HOSTED_CONFIG_PAYLOAD__PAYLOAD_CMD_OTA_BEGIN = 38, + ESP_HOSTED_CONFIG_PAYLOAD__PAYLOAD_RESP_OTA_BEGIN = 39, + ESP_HOSTED_CONFIG_PAYLOAD__PAYLOAD_CMD_OTA_WRITE = 40, + ESP_HOSTED_CONFIG_PAYLOAD__PAYLOAD_RESP_OTA_WRITE = 41, + ESP_HOSTED_CONFIG_PAYLOAD__PAYLOAD_CMD_OTA_END = 42, + ESP_HOSTED_CONFIG_PAYLOAD__PAYLOAD_RESP_OTA_END = 43, } EspHostedConfigPayload__PayloadCase; struct _EspHostedConfigPayload @@ -478,6 +558,12 @@ struct _EspHostedConfigPayload EspHostedRespSetMode *resp_set_power_save_mode; EspHostedCmdGetMode *cmd_get_power_save_mode; EspHostedRespGetMode *resp_get_power_save_mode; + EspHostedCmdOTABegin *cmd_ota_begin; + EspHostedRespOTABegin *resp_ota_begin; + EspHostedCmdOTAWrite *cmd_ota_write; + EspHostedRespOTAWrite *resp_ota_write; + EspHostedCmdOTAEnd *cmd_ota_end; + EspHostedRespOTAEnd *resp_ota_end; }; }; #define ESP_HOSTED_CONFIG_PAYLOAD__INIT \ @@ -941,6 +1027,120 @@ EspHostedRespConnectedSTA * void esp_hosted_resp_connected_sta__free_unpacked (EspHostedRespConnectedSTA *message, ProtobufCAllocator *allocator); +/* EspHostedCmdOTABegin methods */ +void esp_hosted_cmd_otabegin__init + (EspHostedCmdOTABegin *message); +size_t esp_hosted_cmd_otabegin__get_packed_size + (const EspHostedCmdOTABegin *message); +size_t esp_hosted_cmd_otabegin__pack + (const EspHostedCmdOTABegin *message, + uint8_t *out); +size_t esp_hosted_cmd_otabegin__pack_to_buffer + (const EspHostedCmdOTABegin *message, + ProtobufCBuffer *buffer); +EspHostedCmdOTABegin * + esp_hosted_cmd_otabegin__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void esp_hosted_cmd_otabegin__free_unpacked + (EspHostedCmdOTABegin *message, + ProtobufCAllocator *allocator); +/* EspHostedRespOTABegin methods */ +void esp_hosted_resp_otabegin__init + (EspHostedRespOTABegin *message); +size_t esp_hosted_resp_otabegin__get_packed_size + (const EspHostedRespOTABegin *message); +size_t esp_hosted_resp_otabegin__pack + (const EspHostedRespOTABegin *message, + uint8_t *out); +size_t esp_hosted_resp_otabegin__pack_to_buffer + (const EspHostedRespOTABegin *message, + ProtobufCBuffer *buffer); +EspHostedRespOTABegin * + esp_hosted_resp_otabegin__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void esp_hosted_resp_otabegin__free_unpacked + (EspHostedRespOTABegin *message, + ProtobufCAllocator *allocator); +/* EspHostedCmdOTAWrite methods */ +void esp_hosted_cmd_otawrite__init + (EspHostedCmdOTAWrite *message); +size_t esp_hosted_cmd_otawrite__get_packed_size + (const EspHostedCmdOTAWrite *message); +size_t esp_hosted_cmd_otawrite__pack + (const EspHostedCmdOTAWrite *message, + uint8_t *out); +size_t esp_hosted_cmd_otawrite__pack_to_buffer + (const EspHostedCmdOTAWrite *message, + ProtobufCBuffer *buffer); +EspHostedCmdOTAWrite * + esp_hosted_cmd_otawrite__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void esp_hosted_cmd_otawrite__free_unpacked + (EspHostedCmdOTAWrite *message, + ProtobufCAllocator *allocator); +/* EspHostedRespOTAWrite methods */ +void esp_hosted_resp_otawrite__init + (EspHostedRespOTAWrite *message); +size_t esp_hosted_resp_otawrite__get_packed_size + (const EspHostedRespOTAWrite *message); +size_t esp_hosted_resp_otawrite__pack + (const EspHostedRespOTAWrite *message, + uint8_t *out); +size_t esp_hosted_resp_otawrite__pack_to_buffer + (const EspHostedRespOTAWrite *message, + ProtobufCBuffer *buffer); +EspHostedRespOTAWrite * + esp_hosted_resp_otawrite__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void esp_hosted_resp_otawrite__free_unpacked + (EspHostedRespOTAWrite *message, + ProtobufCAllocator *allocator); +/* EspHostedCmdOTAEnd methods */ +void esp_hosted_cmd_otaend__init + (EspHostedCmdOTAEnd *message); +size_t esp_hosted_cmd_otaend__get_packed_size + (const EspHostedCmdOTAEnd *message); +size_t esp_hosted_cmd_otaend__pack + (const EspHostedCmdOTAEnd *message, + uint8_t *out); +size_t esp_hosted_cmd_otaend__pack_to_buffer + (const EspHostedCmdOTAEnd *message, + ProtobufCBuffer *buffer); +EspHostedCmdOTAEnd * + esp_hosted_cmd_otaend__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void esp_hosted_cmd_otaend__free_unpacked + (EspHostedCmdOTAEnd *message, + ProtobufCAllocator *allocator); +/* EspHostedRespOTAEnd methods */ +void esp_hosted_resp_otaend__init + (EspHostedRespOTAEnd *message); +size_t esp_hosted_resp_otaend__get_packed_size + (const EspHostedRespOTAEnd *message); +size_t esp_hosted_resp_otaend__pack + (const EspHostedRespOTAEnd *message, + uint8_t *out); +size_t esp_hosted_resp_otaend__pack_to_buffer + (const EspHostedRespOTAEnd *message, + ProtobufCBuffer *buffer); +EspHostedRespOTAEnd * + esp_hosted_resp_otaend__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void esp_hosted_resp_otaend__free_unpacked + (EspHostedRespOTAEnd *message, + ProtobufCAllocator *allocator); /* EspHostedConfigPayload methods */ void esp_hosted_config_payload__init (EspHostedConfigPayload *message); @@ -1034,6 +1234,24 @@ typedef void (*EspHostedCmdConnectedSTA_Closure) typedef void (*EspHostedRespConnectedSTA_Closure) (const EspHostedRespConnectedSTA *message, void *closure_data); +typedef void (*EspHostedCmdOTABegin_Closure) + (const EspHostedCmdOTABegin *message, + void *closure_data); +typedef void (*EspHostedRespOTABegin_Closure) + (const EspHostedRespOTABegin *message, + void *closure_data); +typedef void (*EspHostedCmdOTAWrite_Closure) + (const EspHostedCmdOTAWrite *message, + void *closure_data); +typedef void (*EspHostedRespOTAWrite_Closure) + (const EspHostedRespOTAWrite *message, + void *closure_data); +typedef void (*EspHostedCmdOTAEnd_Closure) + (const EspHostedCmdOTAEnd *message, + void *closure_data); +typedef void (*EspHostedRespOTAEnd_Closure) + (const EspHostedRespOTAEnd *message, + void *closure_data); typedef void (*EspHostedConfigPayload_Closure) (const EspHostedConfigPayload *message, void *closure_data); @@ -1070,6 +1288,12 @@ extern const ProtobufCMessageDescriptor esp_hosted_resp_scan_result__descriptor; extern const ProtobufCMessageDescriptor esp_hosted_connected_stalist__descriptor; extern const ProtobufCMessageDescriptor esp_hosted_cmd_connected_sta__descriptor; extern const ProtobufCMessageDescriptor esp_hosted_resp_connected_sta__descriptor; +extern const ProtobufCMessageDescriptor esp_hosted_cmd_otabegin__descriptor; +extern const ProtobufCMessageDescriptor esp_hosted_resp_otabegin__descriptor; +extern const ProtobufCMessageDescriptor esp_hosted_cmd_otawrite__descriptor; +extern const ProtobufCMessageDescriptor esp_hosted_resp_otawrite__descriptor; +extern const ProtobufCMessageDescriptor esp_hosted_cmd_otaend__descriptor; +extern const ProtobufCMessageDescriptor esp_hosted_resp_otaend__descriptor; extern const ProtobufCMessageDescriptor esp_hosted_config_payload__descriptor; PROTOBUF_C__END_DECLS diff --git a/common/proto/esp_hosted_config.proto b/common/proto/esp_hosted_config.proto index edf0fbd322..a28b3d32d3 100644 --- a/common/proto/esp_hosted_config.proto +++ b/common/proto/esp_hosted_config.proto @@ -142,6 +142,28 @@ message EspHostedRespConnectedSTA { int32 resp = 3; } +message EspHostedCmdOTABegin { +} + +message EspHostedRespOTABegin { + int32 resp = 1; +} + +message EspHostedCmdOTAWrite { + bytes ota_data = 1; +} + +message EspHostedRespOTAWrite { + int32 resp = 1; +} + +message EspHostedCmdOTAEnd { +} + +message EspHostedRespOTAEnd { + int32 resp = 1; +} + enum EspHostedConfigMsgType { TypeCmdGetMACAddress = 0; TypeRespGetMACAddress = 1; @@ -171,6 +193,12 @@ enum EspHostedConfigMsgType { TypeRespSetPowerSaveMode = 25; TypeCmdGetPowerSaveMode = 26; TypeRespGetPowerSaveMode = 27; + TypeCmdOTABegin = 28; + TypeRespOTABegin = 29; + TypeCmdOTAWrite = 30; + TypeRespOTAWrite = 31; + TypeCmdOTAEnd = 32; + TypeRespOTAEnd = 33; } message EspHostedConfigPayload { @@ -204,5 +232,11 @@ message EspHostedConfigPayload { EspHostedRespSetMode resp_set_power_save_mode = 35; EspHostedCmdGetMode cmd_get_power_save_mode = 36; EspHostedRespGetMode resp_get_power_save_mode = 37; + EspHostedCmdOTABegin cmd_ota_begin = 38; + EspHostedRespOTABegin resp_ota_begin = 39; + EspHostedCmdOTAWrite cmd_ota_write = 40; + EspHostedRespOTAWrite resp_ota_write = 41; + EspHostedCmdOTAEnd cmd_ota_end = 42; + EspHostedRespOTAEnd resp_ota_end = 43; } } diff --git a/esp/esp_driver/network_adapter/main/Kconfig.projbuild b/esp/esp_driver/network_adapter/main/Kconfig.projbuild index 5860428ff6..cf4851ca7b 100644 --- a/esp/esp_driver/network_adapter/main/Kconfig.projbuild +++ b/esp/esp_driver/network_adapter/main/Kconfig.projbuild @@ -44,6 +44,12 @@ menu "Example Configuration" GPIO pin for indicating host that SPI slave has data to be read by host endmenu + config ESP_OTA_WORKAROUND + bool "OTA workaround - Add sleeps while OTA write" + default y + help + Enable/disable sleeps while OTA operations + config ESP_SERIAL_DEBUG bool "Debug Serial driver data path" default 0 diff --git a/esp/esp_driver/network_adapter/main/app_main.c b/esp/esp_driver/network_adapter/main/app_main.c index a7d2730886..2adcb8c81f 100644 --- a/esp/esp_driver/network_adapter/main/app_main.c +++ b/esp/esp_driver/network_adapter/main/app_main.c @@ -82,6 +82,7 @@ volatile uint8_t action = 0; volatile uint8_t datapath = 0; volatile uint8_t station_connected = 0; volatile uint8_t softap_started = 0; +volatile uint8_t ota_ongoing = 0; #ifdef ESP_DEBUG_STATS uint32_t from_wlan_count = 0; @@ -222,7 +223,7 @@ esp_err_t wlan_ap_rx_callback(void *buffer, uint16_t len, void *eb) esp_err_t ret = ESP_OK; interface_buffer_handle_t buf_handle = {0}; - if (!buffer || !eb || !datapath) { + if (!buffer || !eb || !datapath || ota_ongoing) { if (eb) { esp_wifi_internal_free_rx_buffer(eb); } @@ -255,7 +256,7 @@ esp_err_t wlan_sta_rx_callback(void *buffer, uint16_t len, void *eb) esp_err_t ret = ESP_OK; interface_buffer_handle_t buf_handle = {0}; - if (!buffer || !eb || !datapath) { + if (!buffer || !eb || !datapath || ota_ongoing) { if (eb) { esp_wifi_internal_free_rx_buffer(eb); } @@ -792,8 +793,10 @@ static esp_err_t print_real_time_stats(TickType_t xTicksToWait) ret = ESP_OK; exit: //Common return path - free(start_array); - free(end_array); + if (start_array) + free(start_array); + if (end_array) + free(end_array); return ret; } diff --git a/esp/esp_driver/network_adapter/main/slave_commands.c b/esp/esp_driver/network_adapter/main/slave_commands.c index f2d2ef80ed..1e32f91e3c 100644 --- a/esp/esp_driver/network_adapter/main/slave_commands.c +++ b/esp/esp_driver/network_adapter/main/slave_commands.c @@ -19,6 +19,7 @@ #include "esp_private/wifi.h" #include "slave_commands.h" #include "esp_hosted_config.pb-c.h" +#include "esp_ota_ops.h" #define MAC_LEN 6 #define MAC_STR_LEN 17 @@ -39,6 +40,11 @@ #define TIMEOUT_IN_MIN (60*TIMEOUT_IN_SEC) #define TIMEOUT (2*TIMEOUT_IN_MIN) +#define RESTART_TIMEOUT (5*TIMEOUT_IN_SEC) + +#if CONFIG_ESP_OTA_WORKAROUND +#define OTA_SLEEP_TIME_MS (40) +#endif #define mem_free(x) \ { \ @@ -49,12 +55,16 @@ } static const char* TAG = "slave_commands"; +extern volatile uint8_t ota_ongoing; /* FreeRTOS event group to signal when we are connected*/ static EventGroupHandle_t wifi_event_group; static int retry = 0; static bool scan_done = false; +static esp_ota_handle_t handle; +const esp_partition_t* update_partition = NULL; +static int ota_msg = 0; static void station_event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data); @@ -76,6 +86,13 @@ extern esp_err_t wlan_ap_rx_callback(void *buffer, uint16_t len, void *eb); extern volatile uint8_t station_connected; extern volatile uint8_t softap_started; +// OTA end timer callback +void vTimerCallback( TimerHandle_t xTimer ) +{ + xTimerDelete(xTimer, 0); + esp_restart(); +} + // event handler for station connect/disconnect to/from AP static void station_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) @@ -86,7 +103,7 @@ static void station_event_handler(void *arg, esp_event_base_t event_base, retry++; ESP_LOGI(TAG, "Retry to connect to the AP"); } else { - wifi_event_sta_disconnected_t * disconnected_event = \ + wifi_event_sta_disconnected_t * disconnected_event = (wifi_event_sta_disconnected_t *) event_data; if (disconnected_event->reason == WIFI_REASON_NO_AP_FOUND) { xEventGroupSetBits(wifi_event_group, WIFI_NO_AP_FOUND_BIT); @@ -476,7 +493,7 @@ static esp_err_t cmd_set_ap_config_handler (EspHostedConfigPayload *req, } EventBits_t bits = xEventGroupWaitBits(wifi_event_group, - (WIFI_CONNECTED_BIT | WIFI_FAIL_BIT | + (WIFI_CONNECTED_BIT | WIFI_FAIL_BIT | WIFI_NO_AP_FOUND_BIT | WIFI_WRONG_PASSWORD_BIT), pdFALSE, pdFALSE, @@ -1163,7 +1180,7 @@ static esp_err_t get_connected_sta_list_handler (EspHostedConfigPayload *req, resp_payload->num = stas_info->num; if (stas_info->num) { resp_payload->n_stations = stas_info->num; - results = (EspHostedConnectedSTAList **)calloc(stas_info->num, \ + results = (EspHostedConnectedSTAList **)calloc(stas_info->num, sizeof(EspHostedConnectedSTAList)); if (!results) { ESP_LOGE(TAG,"Failed to allocate memory for connected stations"); @@ -1173,7 +1190,7 @@ static esp_err_t get_connected_sta_list_handler (EspHostedConfigPayload *req, for (int i = 0; i < stas_info->num ; i++) { snprintf((char *)credentials.bssid,BSSID_LENGTH, MACSTR,MAC2STR(stas_info->sta[i].mac)); - results[i] = (EspHostedConnectedSTAList *)calloc(1,\ + results[i] = (EspHostedConnectedSTAList *)calloc(1, sizeof(EspHostedConnectedSTAList)); if (!results[i]) { ESP_LOGE(TAG,"Failed to allocated memory"); @@ -1360,6 +1377,170 @@ static esp_err_t cmd_get_power_save_mode_handler (EspHostedConfigPayload *req, return ESP_OK; } +// Function OTA begin +static esp_err_t cmd_ota_begin_handler (EspHostedConfigPayload *req, + EspHostedConfigPayload *resp, void *priv_data) +{ + esp_err_t ret = ESP_OK; + EspHostedRespOTABegin *resp_payload = NULL; + + if (!req || !resp) { + ESP_LOGE(TAG, "Invalid parameters"); + return ESP_FAIL; + } + + ESP_LOGI(TAG, "OTA update started"); + + resp_payload = (EspHostedRespOTABegin *) + calloc(1,sizeof(EspHostedRespOTABegin)); + if (!resp_payload) { + ESP_LOGE(TAG,"Failed to allocate memory"); + return ESP_ERR_NO_MEM; + } + esp_hosted_resp_otabegin__init(resp_payload); + resp->payload_case = ESP_HOSTED_CONFIG_PAYLOAD__PAYLOAD_RESP_OTA_BEGIN; + resp->resp_ota_begin = resp_payload; + resp_payload->has_resp = true; + + // Identify next OTA partition + update_partition = esp_ota_get_next_update_partition(NULL); + if (update_partition == NULL) { + ESP_LOGE(TAG, "Failed to get next update partition"); + goto err; + } + + ESP_LOGI(TAG, "Prepare partition for OTA\n"); + ota_ongoing=1; +#if CONFIG_ESP_OTA_WORKAROUND + vTaskDelay(OTA_SLEEP_TIME_MS/portTICK_PERIOD_MS); +#endif + ret = esp_ota_begin(update_partition, OTA_SIZE_UNKNOWN, &handle); + ota_ongoing=0; + if (ret) { + ESP_LOGE(TAG, "OTA update failed in OTA begin"); + goto err; + } + + ota_msg = 1; + + resp_payload->resp = SUCCESS; + return ESP_OK; +err: + resp_payload->resp = FAILURE; + return ESP_OK; + +} + +// Function OTA write +static esp_err_t cmd_ota_write_handler (EspHostedConfigPayload *req, + EspHostedConfigPayload *resp, void *priv_data) +{ + esp_err_t ret = ESP_OK; + EspHostedRespOTAWrite *resp_payload = NULL; + + if (!req || !resp) { + ESP_LOGE(TAG, "Invalid parameters"); + return ESP_FAIL; + } + + resp_payload = (EspHostedRespOTAWrite *)calloc(1,sizeof(EspHostedRespOTAWrite)); + if (!resp_payload) { + ESP_LOGE(TAG,"Failed to allocate memory"); + return ESP_ERR_NO_MEM; + } + + if (ota_msg) { + ESP_LOGI(TAG, "Flashing image\n"); + ota_msg = 0; + } + esp_hosted_resp_otawrite__init(resp_payload); + resp->payload_case = ESP_HOSTED_CONFIG_PAYLOAD__PAYLOAD_RESP_OTA_WRITE; + resp->resp_ota_write = resp_payload; + resp_payload->has_resp = true; + + ota_ongoing=1; +#if CONFIG_ESP_OTA_WORKAROUND + /* Delay added is to give chance to transfer pending data at transport + * Care to be taken, when OTA ongoing, no other processing should happen + * So big sleep is added before any flash operations start + * */ + vTaskDelay(OTA_SLEEP_TIME_MS/portTICK_PERIOD_MS); +#endif + printf("."); + fflush(stdout); + ret = esp_ota_write( handle, (const void *)req->cmd_ota_write->ota_data.data, req->cmd_ota_write->ota_data.len); + ota_ongoing=0; + if (ret != ESP_OK) { + ESP_LOGE(TAG, "OTA write failed with return code 0x%x",ret); + resp_payload->resp = FAILURE; + return ESP_OK; + } + resp_payload->resp = SUCCESS; + return ESP_OK; +} + +// Function OTA end +static esp_err_t cmd_ota_end_handler (EspHostedConfigPayload *req, + EspHostedConfigPayload *resp, void *priv_data) +{ + esp_err_t ret = ESP_OK; + EspHostedRespOTAEnd *resp_payload = NULL; + TimerHandle_t xTimer = NULL; + + if (!req || !resp) { + ESP_LOGE(TAG, "Invalid parameters"); + return ESP_FAIL; + } + + resp_payload = (EspHostedRespOTAEnd *)calloc(1,sizeof(EspHostedRespOTAEnd)); + if (!resp_payload) { + ESP_LOGE(TAG,"Failed to allocate memory"); + return ESP_ERR_NO_MEM; + } + esp_hosted_resp_otaend__init(resp_payload); + resp->payload_case = ESP_HOSTED_CONFIG_PAYLOAD__PAYLOAD_RESP_OTA_END; + resp->resp_ota_end = resp_payload; + resp_payload->has_resp = true; + + ota_ongoing=1; +#if CONFIG_ESP_OTA_WORKAROUND + vTaskDelay(OTA_SLEEP_TIME_MS/portTICK_PERIOD_MS); +#endif + ret = esp_ota_end(handle); + ota_ongoing=0; + if (ret != ESP_OK) { + if (ret == ESP_ERR_OTA_VALIDATE_FAILED) { + ESP_LOGE(TAG, "Image validation failed, image is corrupted"); + } else { + ESP_LOGE(TAG, "OTA update failed in end (%s)!", esp_err_to_name(ret)); + } + goto err; + } + + // set OTA partition for next boot + ret = esp_ota_set_boot_partition(update_partition); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "esp_ota_set_boot_partition failed (%s)!", esp_err_to_name(ret)); + goto err; + } + xTimer = xTimerCreate("Timer", RESTART_TIMEOUT , pdFALSE, 0, vTimerCallback); + if (xTimer == NULL) { + ESP_LOGE(TAG, "Failed to create timer to restart system"); + goto err; + } + ret = xTimerStart(xTimer, 0); + if (ret != pdPASS) { + ESP_LOGE(TAG, "Failed to start timer to restart system"); + goto err; + } + ESP_LOGE(TAG, "**** OTA updated successfully, ESP32 will be rebooted in 5 sec ****"); + resp_payload->resp = SUCCESS; + return ESP_OK; +err: + resp_payload->resp = FAILURE; + return ESP_OK; +} + static esp_hosted_config_cmd_t cmd_table[] = { { .cmd_num = ESP_HOSTED_CONFIG_MSG_TYPE__TypeCmdGetMACAddress , @@ -1417,6 +1598,18 @@ static esp_hosted_config_cmd_t cmd_table[] = { .cmd_num = ESP_HOSTED_CONFIG_MSG_TYPE__TypeCmdGetPowerSaveMode, .command_handler = cmd_get_power_save_mode_handler }, + { + .cmd_num = ESP_HOSTED_CONFIG_MSG_TYPE__TypeCmdOTABegin, + .command_handler = cmd_ota_begin_handler + }, + { + .cmd_num = ESP_HOSTED_CONFIG_MSG_TYPE__TypeCmdOTAWrite, + .command_handler = cmd_ota_write_handler + }, + { + .cmd_num = ESP_HOSTED_CONFIG_MSG_TYPE__TypeCmdOTAEnd, + .command_handler = cmd_ota_end_handler + }, }; @@ -1554,6 +1747,18 @@ static void esp_hosted_config_cleanup(EspHostedConfigPayload *resp) mem_free(resp->resp_get_power_save_mode); break; } + case (ESP_HOSTED_CONFIG_MSG_TYPE__TypeRespOTABegin) : { + mem_free(resp->resp_ota_begin); + break; + } + case (ESP_HOSTED_CONFIG_MSG_TYPE__TypeRespOTAWrite) : { + mem_free(resp->resp_ota_write); + break; + } + case (ESP_HOSTED_CONFIG_MSG_TYPE__TypeRespOTAEnd) : { + mem_free(resp->resp_ota_end); + break; + } default: ESP_LOGE(TAG, "Unsupported response type"); break; diff --git a/esp/esp_driver/network_adapter/sdkconfig.defaults b/esp/esp_driver/network_adapter/sdkconfig.defaults index 241fe2f7ac..c53e3c0e40 100644 --- a/esp/esp_driver/network_adapter/sdkconfig.defaults +++ b/esp/esp_driver/network_adapter/sdkconfig.defaults @@ -12,3 +12,7 @@ CONFIG_BTDM_CONTROLLER_HCI_MODE_VHCI=y CONFIG_BTDM_CTRL_AUTO_LATENCY=y CONFIG_ESP32_WIFI_NVS_ENABLED= + +# OTA +CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y +CONFIG_PARTITION_TABLE_TWO_OTA=y diff --git a/esp/esp_driver/network_adapter/sdkconfig.defaults.esp32 b/esp/esp_driver/network_adapter/sdkconfig.defaults.esp32 index c95ba95a92..aed5aab135 100644 --- a/esp/esp_driver/network_adapter/sdkconfig.defaults.esp32 +++ b/esp/esp_driver/network_adapter/sdkconfig.defaults.esp32 @@ -30,3 +30,7 @@ CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM=16 CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM=32 CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER=y CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER_NUM=64 + +# OTA +CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y +CONFIG_PARTITION_TABLE_TWO_OTA=y diff --git a/host/host_common/commands.c b/host/host_common/commands.c index 968897892b..173f852163 100644 --- a/host/host_common/commands.c +++ b/host/host_common/commands.c @@ -63,7 +63,7 @@ int wifi_get_mac (int mode, char *mac) req.msg = ESP_HOSTED_CONFIG_MSG_TYPE__TypeCmdGetMACAddress; req.payload_case = ESP_HOSTED_CONFIG_PAYLOAD__PAYLOAD_CMD_GET_MAC_ADDRESS ; - EspHostedCmdGetMacAddress *req_payload = (EspHostedCmdGetMacAddress *) \ + EspHostedCmdGetMacAddress *req_payload = (EspHostedCmdGetMacAddress *) esp_hosted_calloc(1, sizeof(EspHostedCmdGetMacAddress)); if (!req_payload) { command_log("Failed to allocate memory for req_payload\n"); @@ -145,7 +145,7 @@ int wifi_set_mac (int mode, char *mac) req.msg = ESP_HOSTED_CONFIG_MSG_TYPE__TypeCmdSetMacAddress; req.payload_case = ESP_HOSTED_CONFIG_PAYLOAD__PAYLOAD_CMD_SET_MAC_ADDRESS; - EspHostedCmdSetMacAddress *req_payload = (EspHostedCmdSetMacAddress *) \ + EspHostedCmdSetMacAddress *req_payload = (EspHostedCmdSetMacAddress *) esp_hosted_calloc(1, sizeof(EspHostedCmdSetMacAddress)); if (!req_payload) { command_log("Failed to allocate memory for req_payload\n"); @@ -281,7 +281,7 @@ int wifi_set_mode (int mode) req.msg = ESP_HOSTED_CONFIG_MSG_TYPE__TypeCmdSetWiFiMode; req.payload_case = ESP_HOSTED_CONFIG_PAYLOAD__PAYLOAD_CMD_SET_WIFI_MODE; - EspHostedCmdSetMode *req_payload = (EspHostedCmdSetMode *) \ + EspHostedCmdSetMode *req_payload = (EspHostedCmdSetMode *) esp_hosted_calloc( 1, sizeof(EspHostedCmdSetMode)); if (!req_payload) { command_log("Failed to allocate memory for req_payload\n"); @@ -368,7 +368,7 @@ int wifi_set_ap_config (esp_hosted_control_config_t ap_config) req.msg = ESP_HOSTED_CONFIG_MSG_TYPE__TypeCmdSetAPConfig ; req.payload_case = ESP_HOSTED_CONFIG_PAYLOAD__PAYLOAD_CMD_SET_AP_CONFIG; - EspHostedCmdSetAPConfig *req_payload = (EspHostedCmdSetAPConfig *) \ + EspHostedCmdSetAPConfig *req_payload = (EspHostedCmdSetAPConfig *) esp_hosted_calloc(1, sizeof(EspHostedCmdSetAPConfig)); if (!req_payload) { command_log("Failed to allocate memory for req_payload\n"); @@ -413,7 +413,7 @@ int wifi_set_ap_config (esp_hosted_control_config_t ap_config) } if (resp->resp_set_ap_config->resp == INVALID_PASSWORD) { - command_log("Invalid password %s for SSID %s\n",\ + command_log("Invalid password %s for SSID %s\n", (char *)&ap_config.station.pwd, (char *)&ap_config.station.ssid); mem_free(tx_data); mem_free(rx_data); @@ -638,7 +638,7 @@ int wifi_set_softap_config (esp_hosted_control_config_t softap_config) req.msg = ESP_HOSTED_CONFIG_MSG_TYPE__TypeCmdSetSoftAPConfig; req.payload_case = ESP_HOSTED_CONFIG_PAYLOAD__PAYLOAD_CMD_SET_SOFTAP_CONFIG; - EspHostedCmdSetSoftAPConfig *req_payload = (EspHostedCmdSetSoftAPConfig *) \ + EspHostedCmdSetSoftAPConfig *req_payload = (EspHostedCmdSetSoftAPConfig *) esp_hosted_calloc(1, sizeof(EspHostedCmdSetSoftAPConfig)); if (!req_payload) { command_log("Failed to allocate memory for req_payload\n"); @@ -974,7 +974,7 @@ esp_hosted_wifi_connected_stations_list* wifi_connected_stations_list(int *num) *num = resp->resp_connected_stas_list->num; if (resp->resp_connected_stas_list->num) { - list = (esp_hosted_wifi_connected_stations_list *) \ + list = (esp_hosted_wifi_connected_stations_list *) esp_hosted_calloc(resp->resp_connected_stas_list->num, sizeof(esp_hosted_wifi_connected_stations_list)); if (!list) { @@ -1018,7 +1018,7 @@ int wifi_set_power_save_mode (int power_save_mode) req.msg = ESP_HOSTED_CONFIG_MSG_TYPE__TypeCmdSetPowerSaveMode; req.payload_case = ESP_HOSTED_CONFIG_PAYLOAD__PAYLOAD_CMD_SET_POWER_SAVE_MODE; - EspHostedCmdSetMode *req_payload = (EspHostedCmdSetMode *) \ + EspHostedCmdSetMode *req_payload = (EspHostedCmdSetMode *) esp_hosted_calloc(1, sizeof(EspHostedCmdSetMode)); if (!req_payload) { command_log("Failed to allocate memory for req_payload\n"); @@ -1135,3 +1135,186 @@ int wifi_get_power_save_mode (int *power_save_mode) mem_free(tx_data); return FAILURE; } + +// Function performs an OTA begin for ESP32 +int esp_ota_begin() +{ + EspHostedConfigPayload req, *resp = NULL; + uint32_t tx_len = 0, rx_len = 0; + uint8_t *tx_data = NULL, *rx_data = NULL; + + esp_hosted_config_payload__init (&req); + req.has_msg = true; + req.msg = ESP_HOSTED_CONFIG_MSG_TYPE__TypeCmdOTABegin; + req.payload_case = ESP_HOSTED_CONFIG_PAYLOAD__PAYLOAD_CMD_OTA_BEGIN; + tx_len = esp_hosted_config_payload__get_packed_size(&req); + if (!tx_len) { + command_log("Invalid tx length\n"); + return FAILURE; + } + + tx_data = (uint8_t *)esp_hosted_calloc(1, tx_len); + if (!tx_data) { + command_log("Failed to allocate memory for tx_data\n"); + return FAILURE; + } + + esp_hosted_config_payload__pack(&req, tx_data); + + rx_data = transport_pserial_data_handler(tx_data, tx_len, + TIMEOUT_PSERIAL_RESP, &rx_len); + if (!rx_data || !rx_len) { + command_log("Failed to process rx_data\n"); + goto err2; + } + + resp = esp_hosted_config_payload__unpack(NULL, rx_len, rx_data); + if ((!resp) || (!resp->resp_ota_begin)) { + command_log("Failed to unpack rx_data\n"); + goto err1; + } + + if (resp->resp_ota_begin->resp) { + command_log("Failed to start OTA begin\n"); + goto err1; + } + + mem_free(tx_data); + mem_free(rx_data); + return SUCCESS; + +err1: + mem_free(rx_data); +err2: + mem_free(tx_data); + return FAILURE; +} + +// Function performs an OTA write for ESP32 +int esp_ota_write(uint8_t* ota_data, uint32_t ota_data_len) +{ + EspHostedConfigPayload req, *resp = NULL; + uint32_t tx_len = 0, rx_len = 0; + uint8_t *tx_data = NULL, *rx_data = NULL; + + if (!ota_data || (ota_data_len == 0)) { + command_log("Invalid parameter\n"); + return FAILURE; + } + + esp_hosted_config_payload__init (&req); + req.has_msg = true; + req.msg = ESP_HOSTED_CONFIG_MSG_TYPE__TypeCmdOTAWrite; + + req.payload_case = ESP_HOSTED_CONFIG_PAYLOAD__PAYLOAD_CMD_OTA_WRITE; + + EspHostedCmdOTAWrite *req_payload = (EspHostedCmdOTAWrite *) + esp_hosted_calloc(1, sizeof(EspHostedCmdOTAWrite)); + if (!req_payload) { + command_log("Failed to allocate memory for req_payload\n"); + return FAILURE; + } + esp_hosted_cmd_otawrite__init(req_payload); + req_payload->has_ota_data = true; + req_payload->ota_data.data = ota_data; + req_payload->ota_data.len = ota_data_len; + req.cmd_ota_write = req_payload; + tx_len = esp_hosted_config_payload__get_packed_size(&req); + if (!tx_len) { + command_log("Invalid tx length\n"); + goto err3; + } + + tx_data = (uint8_t *)esp_hosted_calloc(1, tx_len); + if (!tx_data) { + command_log("Failed to allocate memory for tx_data\n"); + goto err3; + } + + esp_hosted_config_payload__pack(&req, tx_data); + + rx_data = transport_pserial_data_handler(tx_data, tx_len, + TIMEOUT_PSERIAL_RESP, &rx_len); + if (!rx_data || !rx_len) { + command_log("Failed to process rx_data\n"); + goto err2; + } + + resp = esp_hosted_config_payload__unpack(NULL, rx_len, rx_data); + if ((!resp) || (!resp->resp_ota_write)) { + command_log("Failed to unpack rx_data\n"); + goto err1; + } + + if (resp->resp_ota_write->resp) { + command_log("Failed to give OTA update\n"); + goto err1; + } + + mem_free(tx_data); + mem_free(rx_data); + mem_free(req_payload); + return SUCCESS; + +err1: + mem_free(rx_data); +err2: + mem_free(tx_data); +err3: + mem_free(req_payload); + return FAILURE; +} + +// Function performs an OTA end for ESP32 +int esp_ota_end() +{ + EspHostedConfigPayload req, *resp = NULL; + uint32_t tx_len = 0, rx_len = 0; + uint8_t *tx_data = NULL, *rx_data = NULL; + + esp_hosted_config_payload__init (&req); + req.has_msg = true; + req.msg = ESP_HOSTED_CONFIG_MSG_TYPE__TypeCmdOTAEnd; + req.payload_case = ESP_HOSTED_CONFIG_PAYLOAD__PAYLOAD_CMD_OTA_END; + tx_len = esp_hosted_config_payload__get_packed_size(&req); + if (!tx_len) { + command_log("Invalid tx length\n"); + return FAILURE; + } + + tx_data = (uint8_t *)esp_hosted_calloc(1, tx_len); + if (!tx_data) { + command_log("Failed to allocate memory for tx_data\n"); + return FAILURE; + } + + esp_hosted_config_payload__pack(&req, tx_data); + + rx_data = transport_pserial_data_handler(tx_data, tx_len, + TIMEOUT_PSERIAL_RESP, &rx_len); + if (!rx_data || !rx_len) { + command_log("Failed to process rx_data\n"); + goto err2; + } + + resp = esp_hosted_config_payload__unpack(NULL, rx_len, rx_data); + if ((!resp) || (!resp->resp_ota_end)) { + command_log("Failed to unpack rx_data\n"); + goto err1; + } + + if (resp->resp_ota_end->resp) { + command_log("Failed to OTA end\n"); + goto err1; + } + + mem_free(tx_data); + mem_free(rx_data); + return SUCCESS; + +err1: + mem_free(rx_data); +err2: + mem_free(tx_data); + return FAILURE; +} diff --git a/host/host_common/include/commands.h b/host/host_common/include/commands.h index 4f553daaf1..ccb71ea02a 100644 --- a/host/host_common/include/commands.h +++ b/host/host_common/include/commands.h @@ -152,7 +152,7 @@ int wifi_set_mode(int mode); * - SUCCESS(0) : succeed * - FAILURE(-1) : failed * - NO_AP_FOUND(2) : Given AP not found - * - INVALID_PASSWORD(3) : Invalid password for AP + * - INVALID_PASSWORD(3) : Invalid password for AP */ int wifi_set_ap_config(esp_hosted_control_config_t ap_config); @@ -265,4 +265,30 @@ int wifi_set_power_save_mode(int power_save_mode); * 3 : WIFI_PS_INVALID, Invalid power save mode. In case of failure of command */ int wifi_get_power_save_mode(int *power_save_mode); + +/* + * esp ota begin function performs an OTA begin operation for ESP32 + * which sets partition for OTA write and erase it. + * returns SUCCESS(0) or FAILURE(-1) + */ +int esp_ota_begin(); + +/* + * esp ota write function performs an OTA write operation for ESP32, + * It writes ota_data buffer to OTA partition in flash + * returns SUCCESS(0) or FAILURE(-1) + * Input parameter: + * ota_data : OTA data buffer + * ota_data_len : length of OTA data buffer + */ +int esp_ota_write(uint8_t* ota_data, uint32_t ota_data_len); + +/* + * esp ota end function performs an OTA end operation for ESP32, + * It validates written OTA image, set OTA partition as boot partition for next boot, + * Creates timer which reset ESP32 after 5 sec, + * returns SUCCESS(0) or FAILURE(-1) + */ +int esp_ota_end(); + #endif diff --git a/host/linux/host_control/c_support/test.c b/host/linux/host_control/c_support/test.c index 4cba3bdcc7..72b75ec4bf 100644 --- a/host/linux/host_control/c_support/test.c +++ b/host/linux/host_control/c_support/test.c @@ -25,25 +25,41 @@ int main(int argc, char *argv[]) { /* Below APIs could be used by demo application */ - int ret = control_path_platform_init(); - if (ret != SUCCESS) { - printf("EXIT!!!!\n"); - exit(0); - } + + int ret = control_path_platform_init(); + if (ret != SUCCESS) { + printf("EXIT!!!!\n"); + exit(0); + } + + if (!argc) { + printf("Usage: %s [%s] [%s] [%s] [%s] [%s] [%s]\n", argv[0], + STA_CONNECT, STA_DISCONNECT, AP_START, AP_STOP, + SCAN, STA_LIST); + return -1; + } for (int i=1; i\n",__func__); + if (ret == SUCCESS) { + printf("OTA begin success \n"); + } else { + printf("Failed start OTA begin\n"); + } + printf("====\n\n"); +#endif + + return ret; +} + +int test_ota_write(uint8_t* ota_data, uint32_t ota_data_len) +{ + int ret = esp_ota_write(ota_data, ota_data_len); + +#ifdef TEST_DEBUG_PRINTS + printf("==== %s =>\n",__func__); + if (ret == SUCCESS) { + printf("OTA write success \n"); + + } else { + printf("Failed OTA write\n"); + } + printf("====\n\n"); +#endif + + return ret; +} + +int test_ota_end() +{ + int ret = esp_ota_end(); + +#ifdef TEST_DEBUG_PRINTS + printf("==== %s =>\n",__func__); + if (ret == SUCCESS) { + printf("OTA end success \n"); + } else { + printf("Failed OTA end\n"); + } + printf("====\n\n"); +#endif + + return ret; +} + +int test_ota(char* image_path) +{ + FILE* f = NULL; + char ota_chunk[CHUNK_SIZE] = {0}; + int ret = test_ota_begin(); +#ifdef TEST_DEBUG_PRINTS + printf("==== %s =>\n",__func__); + if (ret == SUCCESS) { + f = fopen(image_path,"rb"); + if (f == NULL) { + printf("Failed to open file %s \n", image_path); + return FAILURE; + } else { + printf("Success in opening %s file \n", image_path); + } + while (!feof(f)) { + fread(&ota_chunk, CHUNK_SIZE, 1, f); + ret = test_ota_write((uint8_t* )&ota_chunk, CHUNK_SIZE); + if (ret) { + break; + } + } + ret = test_ota_end(); + if (ret) { + return FAILURE; + } + } else { + return FAILURE; + } + printf("ESP32 will restart after 5 sec\n"); + printf("====\n\n"); +#endif + return SUCCESS; +} diff --git a/host/linux/host_control/c_support/test_api.h b/host/linux/host_control/c_support/test_api.h index d3d4e1894f..7feb0faec9 100644 --- a/host/linux/host_control/c_support/test_api.h +++ b/host/linux/host_control/c_support/test_api.h @@ -67,4 +67,11 @@ int test_set_wifi_power_save_mode(); int test_get_wifi_power_save_mode(); +int test_ota_begin(); + +int test_ota_write(); + +int test_ota_end(); + +int test_ota(char* image_path); #endif diff --git a/host/linux/host_control/c_support/test_config.h b/host/linux/host_control/c_support/test_config.h index 75ddb6c256..95eed2d529 100644 --- a/host/linux/host_control/c_support/test_config.h +++ b/host/linux/host_control/c_support/test_config.h @@ -27,10 +27,12 @@ #define AP_STOP "ap_stop" #define SCAN "scan" #define STA_LIST "sta_list" +#define OTA "ota" #define MAC_LENGTH 18 #define SSID_LENGTH 32 #define PWD_LENGTH 64 +#define CHUNK_SIZE 4000 /* station mode */ #define STATION_MODE_MAC_ADDRESS "1a:11:11:11:11:11" diff --git a/host/linux/host_control/python_support/commands.py b/host/linux/host_control/python_support/commands.py index 9a8770578d..979831cbfb 100644 --- a/host/linux/host_control/python_support/commands.py +++ b/host/linux/host_control/python_support/commands.py @@ -83,7 +83,9 @@ def wifi_get_mac(mode): tp = Transport_pserial(interface) response = tp.send_data(endpoint,protodata) del tp - if response[0] != success and response[0] != None: + if response[0] != None and response[0] != success: + return failure_str + if response[1] == None or response[1] == "": return failure_str get_mac.ParseFromString(response[1]) if get_mac.resp_get_mac_address.resp != success: @@ -106,7 +108,9 @@ def wifi_get_mode(): tp = Transport_pserial(interface) response = tp.send_data(endpoint,protodata) del tp - if response[0] != success and response[0] != None: + if response[0] != None and response[0] != success: + return failure_str + if response[1] == None or response[1] == "": return failure_str get_mode.ParseFromString(response[1]) if get_mode.resp_get_wifi_mode.resp != success: @@ -134,7 +138,9 @@ def wifi_set_mode(mode): tp = Transport_pserial(interface) response = tp.send_data(endpoint,protodata) del tp - if response[0] != success and response[0] != None: + if response[0] != None and response[0] != success: + return failure_str + if response[1] == None or response[1] == "": return failure_str set_mode.ParseFromString(response[1]) if set_mode.resp_set_wifi_mode.resp != success: @@ -183,7 +189,9 @@ def wifi_set_ap_config(ssid, pwd, bssid, is_wpa3_supported, listen_interval): tp = Transport_pserial(interface) response = tp.send_data(endpoint,protodata) del tp - if response[0] != success and response[0] != None: + if response[0] != None and response[0] != success: + return failure_str + if response[1] == None or response[1] == "": return failure_str set_ap_config.ParseFromString(response[1]) if set_ap_config.resp_set_ap_config.resp == EspHostedStatus.TYPE_CONNECTION_FAIL: @@ -223,7 +231,9 @@ def wifi_get_ap_config(): tp = Transport_pserial(interface) response = tp.send_data(endpoint,protodata) del tp - if response[0] != success and response[0] != None: + if response[0] != None and response[0] != success: + return failure_str + if response[1] == None or response[1] == "": return failure_str get_ap_config.ParseFromString(response[1]) if get_ap_config.resp_get_ap_config.resp == EspHostedStatus.TYPE_NOT_CONNECTED: @@ -248,7 +258,9 @@ def wifi_disconnect_ap(): tp = Transport_pserial(interface) response = tp.send_data(endpoint,protodata) del tp - if response[0] != success and response[0] != None: + if response[0] != None and response[0] != success: + return failure_str + if response[1] == None or response[1] == "": return failure_str disconnect_ap.ParseFromString(response[1]) if disconnect_ap.resp_disconnect_ap.resp != success: @@ -313,7 +325,9 @@ def wifi_set_softap_config(ssid, pwd, chnl, ecn, max_conn, ssid_hidden, bw): tp = Transport_pserial(interface) response = tp.send_data(endpoint,protodata) del tp - if response[0] != success and response[0] != None: + if response[0] != None and response[0] != success: + return failure_str + if response[1] == None or response[1] == "": return failure_str set_softap_config.ParseFromString(response[1]) if set_softap_config.resp_set_softap_config.resp != success: @@ -349,7 +363,9 @@ def wifi_get_softap_config(): tp = Transport_pserial(interface) response = tp.send_data(endpoint,protodata) del tp - if response[0] != success and response[0] != None: + if response[0] != None and response[0] != success: + return failure_str + if response[1] == None or response[1] == "": return failure_str get_softap_config.ParseFromString(response[1]) if get_softap_config.resp_get_softap_config.resp != success: @@ -374,7 +390,9 @@ def wifi_stop_softap(): tp = Transport_pserial(interface) response = tp.send_data(endpoint,protodata) del tp - if response[0] != success and response[0] != None: + if response[0] != None and response[0] != success: + return failure_str + if response[1] == None or response[1] == "": return failure_str stop_softap.ParseFromString(response[1]) if stop_softap.resp_stop_softap.resp != success: @@ -410,7 +428,9 @@ def wifi_ap_scan_list(): tp = Transport_pserial(interface) response = tp.send_data(endpoint,protodata) del tp - if response[0] != success and response[0] != None: + if response[0] != None and response[0] != success: + return failure_str + if response[1] == None or response[1] == "": return failure_str get_ap_scan_list.ParseFromString(response[1]) if get_ap_scan_list.resp_scan_ap_list.resp != success: @@ -444,7 +464,9 @@ def wifi_connected_stations_list(): tp = Transport_pserial(interface) response = tp.send_data(endpoint,protodata) del tp - if response[0] != success and response[0] != None: + if response[0] != None and response[0] != success: + return failure_str + if response[1] == None or response[1] == "": return failure_str get_connected_stations_list.ParseFromString(response[1]) if get_connected_stations_list.resp_connected_stas_list.resp != success: @@ -489,7 +511,9 @@ def wifi_set_mac(mode, mac): tp = Transport_pserial(interface) response = tp.send_data(endpoint,protodata) del tp - if response[0] != success and response[0] != None: + if response[0] != None and response[0] != success: + return failure_str + if response[1] == None or response[1] == "": return failure_str set_mac.ParseFromString(response[1]) if set_mac.resp_set_mac_address.resp != success: @@ -517,7 +541,9 @@ def wifi_set_power_save_mode(power_save_mode): tp = Transport_pserial(interface) response = tp.send_data(endpoint,protodata) del tp - if response[0] != success and response[0] != None: + if response[0] != None and response[0] != success: + return failure_str + if response[1] == None or response[1] == "": return failure_str set_power_save_mode.ParseFromString(response[1]) if set_power_save_mode.resp_set_power_save_mode.resp != success: @@ -540,11 +566,67 @@ def wifi_get_power_save_mode(): protodata = get_power_save_mode.SerializeToString() tp = Transport_pserial(interface) response = tp.send_data(endpoint,protodata) - get_power_save_mode.ParseFromString(response[1]) del tp - if response[0] != success and response[0] != None: + if response[0] != None and response[0] != success: return failure_str + if response[1] == None or response[1] == "": + return failure_str + get_power_save_mode.ParseFromString(response[1]) if get_power_save_mode.resp_get_power_save_mode.resp != success: return failure_str else: return get_power_save_mode.resp_get_power_save_mode.mode + +# wifi ota + +def esp_ota_begin(): + ota_begin = EspHostedConfigPayload() + ota_begin.msg = EspHostedConfigMsgType.TypeCmdOTABegin + protodata = ota_begin.SerializeToString() + tp = Transport_pserial(interface) + response = tp.send_data(endpoint,protodata) + del tp + if response[0] != None and response[0] != success: + return failure_str + if response[1] == None or response[1] == "": + return failure_str + ota_begin.ParseFromString(response[1]) + if ota_begin.resp_ota_begin.resp != success: + return failure_str + else: + return ota_begin.resp_ota_begin.resp + +def esp_ota_write(ota_data): + ota_write = EspHostedConfigPayload() + ota_write.msg = EspHostedConfigMsgType.TypeCmdOTAWrite + ota_write.cmd_ota_write.ota_data = ota_data + protodata = ota_write.SerializeToString() + tp = Transport_pserial(interface) + response = tp.send_data(endpoint,protodata) + del tp + if response[0] != None and response[0] != success: + return failure_str + if response[1] == None or response[1] == "": + return failure_str + ota_write.ParseFromString(response[1]) + if ota_write.resp_ota_write.resp != success: + return failure_str + else: + return ota_write.resp_ota_write.resp + +def esp_ota_end(): + ota_end = EspHostedConfigPayload() + ota_end.msg = EspHostedConfigMsgType.TypeCmdOTAEnd + protodata = ota_end.SerializeToString() + tp = Transport_pserial(interface) + response = tp.send_data(endpoint,protodata) + del tp + if response[0] != None and response[0] != success: + return failure_str + if response[1] == None or response[1] == "": + return failure_str + ota_end.ParseFromString(response[1]) + if ota_end.resp_ota_end.resp != success: + return failure_str + else: + return ota_end.resp_ota_end.resp diff --git a/host/linux/host_control/python_support/esp_hosted_config_pb2.py b/host/linux/host_control/python_support/esp_hosted_config_pb2.py index ebd4eec8bc..1950af6ef0 100644 --- a/host/linux/host_control/python_support/esp_hosted_config_pb2.py +++ b/host/linux/host_control/python_support/esp_hosted_config_pb2.py @@ -20,7 +20,7 @@ syntax='proto3', serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\x17\x65sp_hosted_config.proto\")\n\x19\x45spHostedCmdGetMacAddress\x12\x0c\n\x04mode\x18\x01 \x01(\x05\"7\n\x1a\x45spHostedRespGetMacAddress\x12\x0b\n\x03mac\x18\x01 \x01(\x0c\x12\x0c\n\x04resp\x18\x02 \x01(\x05\"\x15\n\x13\x45spHostedCmdGetMode\"2\n\x14\x45spHostedRespGetMode\x12\x0c\n\x04mode\x18\x01 \x01(\x05\x12\x0c\n\x04resp\x18\x02 \x01(\x05\"#\n\x13\x45spHostedCmdSetMode\x12\x0c\n\x04mode\x18\x01 \x01(\x05\"$\n\x14\x45spHostedRespSetMode\x12\x0c\n\x04resp\x18\x01 \x01(\x05\"\x17\n\x15\x45spHostedCmdGetStatus\"&\n\x16\x45spHostedRespGetStatus\x12\x0c\n\x04resp\x18\x01 \x01(\x05\"6\n\x19\x45spHostedCmdSetMacAddress\x12\x0b\n\x03mac\x18\x01 \x01(\x0c\x12\x0c\n\x04mode\x18\x02 \x01(\x05\"*\n\x1a\x45spHostedRespSetMacAddress\x12\x0c\n\x04resp\x18\x01 \x01(\x05\"\x19\n\x17\x45spHostedCmdGetAPConfig\"\x88\x01\n\x18\x45spHostedRespGetAPConfig\x12\x0c\n\x04ssid\x18\x01 \x01(\x0c\x12\r\n\x05\x62ssid\x18\x02 \x01(\x0c\x12\x0c\n\x04rssi\x18\x03 \x01(\x05\x12\x0c\n\x04\x63hnl\x18\x04 \x01(\x05\x12%\n\x03\x65\x63n\x18\x05 \x01(\x0e\x32\x18.EspHostedEncryptionMode\x12\x0c\n\x04resp\x18\x06 \x01(\x05\"w\n\x17\x45spHostedCmdSetAPConfig\x12\x0c\n\x04ssid\x18\x01 \x01(\t\x12\x0b\n\x03pwd\x18\x02 \x01(\t\x12\r\n\x05\x62ssid\x18\x03 \x01(\t\x12\x19\n\x11is_wpa3_supported\x18\x04 \x01(\x08\x12\x17\n\x0flisten_interval\x18\x05 \x01(\x05\"(\n\x18\x45spHostedRespSetAPConfig\x12\x0c\n\x04resp\x18\x01 \x01(\x05\"\x1d\n\x1b\x45spHostedCmdGetSoftAPConfig\"\xaf\x01\n\x1c\x45spHostedRespGetSoftAPConfig\x12\x0c\n\x04ssid\x18\x01 \x01(\x0c\x12\x0b\n\x03pwd\x18\x02 \x01(\x0c\x12\x0c\n\x04\x63hnl\x18\x03 \x01(\x05\x12%\n\x03\x65\x63n\x18\x04 \x01(\x0e\x32\x18.EspHostedEncryptionMode\x12\x10\n\x08max_conn\x18\x05 \x01(\x05\x12\x13\n\x0bssid_hidden\x18\x06 \x01(\x08\x12\n\n\x02\x62w\x18\x07 \x01(\x05\x12\x0c\n\x04resp\x18\x08 \x01(\x05\"\xa0\x01\n\x1b\x45spHostedCmdSetSoftAPConfig\x12\x0c\n\x04ssid\x18\x01 \x01(\t\x12\x0b\n\x03pwd\x18\x02 \x01(\t\x12\x0c\n\x04\x63hnl\x18\x03 \x01(\x05\x12%\n\x03\x65\x63n\x18\x04 \x01(\x0e\x32\x18.EspHostedEncryptionMode\x12\x10\n\x08max_conn\x18\x05 \x01(\x05\x12\x13\n\x0bssid_hidden\x18\x06 \x01(\x08\x12\n\n\x02\x62w\x18\x07 \x01(\x05\",\n\x1c\x45spHostedRespSetSoftAPConfig\x12\x0c\n\x04resp\x18\x01 \x01(\x05\"u\n\x13\x45spHostedScanResult\x12\x0c\n\x04ssid\x18\x01 \x01(\x0c\x12\x0c\n\x04\x63hnl\x18\x02 \x01(\r\x12\x0c\n\x04rssi\x18\x03 \x01(\x05\x12\r\n\x05\x62ssid\x18\x04 \x01(\x0c\x12%\n\x03\x65\x63n\x18\x05 \x01(\x0e\x32\x18.EspHostedEncryptionMode\"\x18\n\x16\x45spHostedCmdScanResult\"]\n\x17\x45spHostedRespScanResult\x12\r\n\x05\x63ount\x18\x01 \x01(\r\x12%\n\x07\x65ntries\x18\x02 \x03(\x0b\x32\x14.EspHostedScanResult\x12\x0c\n\x04resp\x18\x03 \x01(\x05\"6\n\x19\x45spHostedConnectedSTAList\x12\x0b\n\x03mac\x18\x01 \x01(\x0c\x12\x0c\n\x04rssi\x18\x02 \x01(\x05\"\x1a\n\x18\x45spHostedCmdConnectedSTA\"d\n\x19\x45spHostedRespConnectedSTA\x12\x0b\n\x03num\x18\x01 \x01(\r\x12,\n\x08stations\x18\x02 \x03(\x0b\x32\x1a.EspHostedConnectedSTAList\x12\x0c\n\x04resp\x18\x03 \x01(\x05\"\x8f\r\n\x16\x45spHostedConfigPayload\x12$\n\x03msg\x18\x01 \x01(\x0e\x32\x17.EspHostedConfigMsgType\x12\x39\n\x13\x63md_get_mac_address\x18\n \x01(\x0b\x32\x1a.EspHostedCmdGetMacAddressH\x00\x12;\n\x14resp_get_mac_address\x18\x0b \x01(\x0b\x32\x1b.EspHostedRespGetMacAddressH\x00\x12\x31\n\x11\x63md_get_wifi_mode\x18\x0c \x01(\x0b\x32\x14.EspHostedCmdGetModeH\x00\x12\x33\n\x12resp_get_wifi_mode\x18\r \x01(\x0b\x32\x15.EspHostedRespGetModeH\x00\x12\x31\n\x11\x63md_set_wifi_mode\x18\x0e \x01(\x0b\x32\x14.EspHostedCmdSetModeH\x00\x12\x33\n\x12resp_set_wifi_mode\x18\x0f \x01(\x0b\x32\x15.EspHostedRespSetModeH\x00\x12\x35\n\x11\x63md_get_ap_config\x18\x10 \x01(\x0b\x32\x18.EspHostedCmdGetAPConfigH\x00\x12\x37\n\x12resp_get_ap_config\x18\x11 \x01(\x0b\x32\x19.EspHostedRespGetAPConfigH\x00\x12\x35\n\x11\x63md_set_ap_config\x18\x12 \x01(\x0b\x32\x18.EspHostedCmdSetAPConfigH\x00\x12\x37\n\x12resp_set_ap_config\x18\x13 \x01(\x0b\x32\x19.EspHostedRespSetAPConfigH\x00\x12=\n\x15\x63md_get_softap_config\x18\x14 \x01(\x0b\x32\x1c.EspHostedCmdGetSoftAPConfigH\x00\x12?\n\x16resp_get_softap_config\x18\x15 \x01(\x0b\x32\x1d.EspHostedRespGetSoftAPConfigH\x00\x12=\n\x15\x63md_set_softap_config\x18\x16 \x01(\x0b\x32\x1c.EspHostedCmdSetSoftAPConfigH\x00\x12?\n\x16resp_set_softap_config\x18\x17 \x01(\x0b\x32\x1d.EspHostedRespSetSoftAPConfigH\x00\x12\x33\n\x11\x63md_disconnect_ap\x18\x18 \x01(\x0b\x32\x16.EspHostedCmdGetStatusH\x00\x12\x35\n\x12resp_disconnect_ap\x18\x19 \x01(\x0b\x32\x17.EspHostedRespGetStatusH\x00\x12\x31\n\x0f\x63md_stop_softap\x18\x1a \x01(\x0b\x32\x16.EspHostedCmdGetStatusH\x00\x12\x33\n\x10resp_stop_softap\x18\x1b \x01(\x0b\x32\x17.EspHostedRespGetStatusH\x00\x12\x33\n\x10\x63md_scan_ap_list\x18\x1c \x01(\x0b\x32\x17.EspHostedCmdScanResultH\x00\x12\x35\n\x11resp_scan_ap_list\x18\x1d \x01(\x0b\x32\x18.EspHostedRespScanResultH\x00\x12<\n\x17\x63md_connected_stas_list\x18\x1e \x01(\x0b\x32\x19.EspHostedCmdConnectedSTAH\x00\x12>\n\x18resp_connected_stas_list\x18\x1f \x01(\x0b\x32\x1a.EspHostedRespConnectedSTAH\x00\x12\x39\n\x13\x63md_set_mac_address\x18 \x01(\x0b\x32\x1a.EspHostedCmdSetMacAddressH\x00\x12;\n\x14resp_set_mac_address\x18! \x01(\x0b\x32\x1b.EspHostedRespSetMacAddressH\x00\x12\x37\n\x17\x63md_set_power_save_mode\x18\" \x01(\x0b\x32\x14.EspHostedCmdSetModeH\x00\x12\x39\n\x18resp_set_power_save_mode\x18# \x01(\x0b\x32\x15.EspHostedRespSetModeH\x00\x12\x37\n\x17\x63md_get_power_save_mode\x18$ \x01(\x0b\x32\x14.EspHostedCmdGetModeH\x00\x12\x39\n\x18resp_get_power_save_mode\x18% \x01(\x0b\x32\x15.EspHostedRespGetModeH\x00\x42\t\n\x07payload*\xb7\x01\n\x17\x45spHostedEncryptionMode\x12\r\n\tType_Open\x10\x00\x12\x0c\n\x08Type_WEP\x10\x01\x12\x10\n\x0cType_WPA_PSK\x10\x02\x12\x11\n\rType_WPA2_PSK\x10\x03\x12\x15\n\x11Type_WPA_WPA2_PSK\x10\x04\x12\x18\n\x14Type_WPA2_ENTERPRISE\x10\x05\x12\x11\n\rType_WPA3_PSK\x10\x06\x12\x16\n\x12Type_WPA2_WPA3_PSK\x10\x07*m\n\x0f\x45spHostedStatus\x12\x12\n\x0eTYPE_CONNECTED\x10\x00\x12\x16\n\x12TYPE_NOT_CONNECTED\x10\x01\x12\x14\n\x10TYPE_NO_AP_FOUND\x10\x02\x12\x18\n\x14TYPE_CONNECTION_FAIL\x10\x03*\x86\x06\n\x16\x45spHostedConfigMsgType\x12\x18\n\x14TypeCmdGetMACAddress\x10\x00\x12\x19\n\x15TypeRespGetMACAddress\x10\x01\x12\x16\n\x12TypeCmdGetWiFiMode\x10\x02\x12\x17\n\x13TypeRespGetWiFiMode\x10\x03\x12\x16\n\x12TypeCmdSetWiFiMode\x10\x04\x12\x17\n\x13TypeRespSetWiFiMode\x10\x05\x12\x16\n\x12TypeCmdGetAPConfig\x10\x06\x12\x17\n\x13TypeRespGetAPConfig\x10\x07\x12\x16\n\x12TypeCmdSetAPConfig\x10\x08\x12\x17\n\x13TypeRespSetAPConfig\x10\t\x12\x1a\n\x16TypeCmdGetSoftAPConfig\x10\n\x12\x1b\n\x17TypeRespGetSoftAPConfig\x10\x0b\x12\x1a\n\x16TypeCmdSetSoftAPConfig\x10\x0c\x12\x1b\n\x17TypeRespSetSoftAPConfig\x10\r\x12\x17\n\x13TypeCmdDisconnectAP\x10\x0e\x12\x18\n\x14TypeRespDisconnectAP\x10\x0f\x12\x15\n\x11TypeCmdStopSoftAP\x10\x10\x12\x16\n\x12TypeRespStopSoftAP\x10\x11\x12\x18\n\x14TypeCmdGetAPScanList\x10\x12\x12\x19\n\x15TypeRespGetAPScanList\x10\x13\x12\x1e\n\x1aTypeCmdGetConnectedSTAList\x10\x14\x12\x1f\n\x1bTypeRespGetConnectedSTAList\x10\x15\x12\x18\n\x14TypeCmdSetMacAddress\x10\x16\x12\x19\n\x15TypeRespSetMacAddress\x10\x17\x12\x1b\n\x17TypeCmdSetPowerSaveMode\x10\x18\x12\x1c\n\x18TypeRespSetPowerSaveMode\x10\x19\x12\x1b\n\x17TypeCmdGetPowerSaveMode\x10\x1a\x12\x1c\n\x18TypeRespGetPowerSaveMode\x10\x1b\x62\x06proto3' + serialized_pb=b'\n\x17\x65sp_hosted_config.proto\")\n\x19\x45spHostedCmdGetMacAddress\x12\x0c\n\x04mode\x18\x01 \x01(\x05\"7\n\x1a\x45spHostedRespGetMacAddress\x12\x0b\n\x03mac\x18\x01 \x01(\x0c\x12\x0c\n\x04resp\x18\x02 \x01(\x05\"\x15\n\x13\x45spHostedCmdGetMode\"2\n\x14\x45spHostedRespGetMode\x12\x0c\n\x04mode\x18\x01 \x01(\x05\x12\x0c\n\x04resp\x18\x02 \x01(\x05\"#\n\x13\x45spHostedCmdSetMode\x12\x0c\n\x04mode\x18\x01 \x01(\x05\"$\n\x14\x45spHostedRespSetMode\x12\x0c\n\x04resp\x18\x01 \x01(\x05\"\x17\n\x15\x45spHostedCmdGetStatus\"&\n\x16\x45spHostedRespGetStatus\x12\x0c\n\x04resp\x18\x01 \x01(\x05\"6\n\x19\x45spHostedCmdSetMacAddress\x12\x0b\n\x03mac\x18\x01 \x01(\x0c\x12\x0c\n\x04mode\x18\x02 \x01(\x05\"*\n\x1a\x45spHostedRespSetMacAddress\x12\x0c\n\x04resp\x18\x01 \x01(\x05\"\x19\n\x17\x45spHostedCmdGetAPConfig\"\x88\x01\n\x18\x45spHostedRespGetAPConfig\x12\x0c\n\x04ssid\x18\x01 \x01(\x0c\x12\r\n\x05\x62ssid\x18\x02 \x01(\x0c\x12\x0c\n\x04rssi\x18\x03 \x01(\x05\x12\x0c\n\x04\x63hnl\x18\x04 \x01(\x05\x12%\n\x03\x65\x63n\x18\x05 \x01(\x0e\x32\x18.EspHostedEncryptionMode\x12\x0c\n\x04resp\x18\x06 \x01(\x05\"w\n\x17\x45spHostedCmdSetAPConfig\x12\x0c\n\x04ssid\x18\x01 \x01(\t\x12\x0b\n\x03pwd\x18\x02 \x01(\t\x12\r\n\x05\x62ssid\x18\x03 \x01(\t\x12\x19\n\x11is_wpa3_supported\x18\x04 \x01(\x08\x12\x17\n\x0flisten_interval\x18\x05 \x01(\x05\"(\n\x18\x45spHostedRespSetAPConfig\x12\x0c\n\x04resp\x18\x01 \x01(\x05\"\x1d\n\x1b\x45spHostedCmdGetSoftAPConfig\"\xaf\x01\n\x1c\x45spHostedRespGetSoftAPConfig\x12\x0c\n\x04ssid\x18\x01 \x01(\x0c\x12\x0b\n\x03pwd\x18\x02 \x01(\x0c\x12\x0c\n\x04\x63hnl\x18\x03 \x01(\x05\x12%\n\x03\x65\x63n\x18\x04 \x01(\x0e\x32\x18.EspHostedEncryptionMode\x12\x10\n\x08max_conn\x18\x05 \x01(\x05\x12\x13\n\x0bssid_hidden\x18\x06 \x01(\x08\x12\n\n\x02\x62w\x18\x07 \x01(\x05\x12\x0c\n\x04resp\x18\x08 \x01(\x05\"\xa0\x01\n\x1b\x45spHostedCmdSetSoftAPConfig\x12\x0c\n\x04ssid\x18\x01 \x01(\t\x12\x0b\n\x03pwd\x18\x02 \x01(\t\x12\x0c\n\x04\x63hnl\x18\x03 \x01(\x05\x12%\n\x03\x65\x63n\x18\x04 \x01(\x0e\x32\x18.EspHostedEncryptionMode\x12\x10\n\x08max_conn\x18\x05 \x01(\x05\x12\x13\n\x0bssid_hidden\x18\x06 \x01(\x08\x12\n\n\x02\x62w\x18\x07 \x01(\x05\",\n\x1c\x45spHostedRespSetSoftAPConfig\x12\x0c\n\x04resp\x18\x01 \x01(\x05\"u\n\x13\x45spHostedScanResult\x12\x0c\n\x04ssid\x18\x01 \x01(\x0c\x12\x0c\n\x04\x63hnl\x18\x02 \x01(\r\x12\x0c\n\x04rssi\x18\x03 \x01(\x05\x12\r\n\x05\x62ssid\x18\x04 \x01(\x0c\x12%\n\x03\x65\x63n\x18\x05 \x01(\x0e\x32\x18.EspHostedEncryptionMode\"\x18\n\x16\x45spHostedCmdScanResult\"]\n\x17\x45spHostedRespScanResult\x12\r\n\x05\x63ount\x18\x01 \x01(\r\x12%\n\x07\x65ntries\x18\x02 \x03(\x0b\x32\x14.EspHostedScanResult\x12\x0c\n\x04resp\x18\x03 \x01(\x05\"6\n\x19\x45spHostedConnectedSTAList\x12\x0b\n\x03mac\x18\x01 \x01(\x0c\x12\x0c\n\x04rssi\x18\x02 \x01(\x05\"\x1a\n\x18\x45spHostedCmdConnectedSTA\"d\n\x19\x45spHostedRespConnectedSTA\x12\x0b\n\x03num\x18\x01 \x01(\r\x12,\n\x08stations\x18\x02 \x03(\x0b\x32\x1a.EspHostedConnectedSTAList\x12\x0c\n\x04resp\x18\x03 \x01(\x05\"\x16\n\x14\x45spHostedCmdOTABegin\"%\n\x15\x45spHostedRespOTABegin\x12\x0c\n\x04resp\x18\x01 \x01(\x05\"(\n\x14\x45spHostedCmdOTAWrite\x12\x10\n\x08ota_data\x18\x01 \x01(\x0c\"%\n\x15\x45spHostedRespOTAWrite\x12\x0c\n\x04resp\x18\x01 \x01(\x05\"\x14\n\x12\x45spHostedCmdOTAEnd\"#\n\x13\x45spHostedRespOTAEnd\x12\x0c\n\x04resp\x18\x01 \x01(\x05\"\xad\x0f\n\x16\x45spHostedConfigPayload\x12$\n\x03msg\x18\x01 \x01(\x0e\x32\x17.EspHostedConfigMsgType\x12\x39\n\x13\x63md_get_mac_address\x18\n \x01(\x0b\x32\x1a.EspHostedCmdGetMacAddressH\x00\x12;\n\x14resp_get_mac_address\x18\x0b \x01(\x0b\x32\x1b.EspHostedRespGetMacAddressH\x00\x12\x31\n\x11\x63md_get_wifi_mode\x18\x0c \x01(\x0b\x32\x14.EspHostedCmdGetModeH\x00\x12\x33\n\x12resp_get_wifi_mode\x18\r \x01(\x0b\x32\x15.EspHostedRespGetModeH\x00\x12\x31\n\x11\x63md_set_wifi_mode\x18\x0e \x01(\x0b\x32\x14.EspHostedCmdSetModeH\x00\x12\x33\n\x12resp_set_wifi_mode\x18\x0f \x01(\x0b\x32\x15.EspHostedRespSetModeH\x00\x12\x35\n\x11\x63md_get_ap_config\x18\x10 \x01(\x0b\x32\x18.EspHostedCmdGetAPConfigH\x00\x12\x37\n\x12resp_get_ap_config\x18\x11 \x01(\x0b\x32\x19.EspHostedRespGetAPConfigH\x00\x12\x35\n\x11\x63md_set_ap_config\x18\x12 \x01(\x0b\x32\x18.EspHostedCmdSetAPConfigH\x00\x12\x37\n\x12resp_set_ap_config\x18\x13 \x01(\x0b\x32\x19.EspHostedRespSetAPConfigH\x00\x12=\n\x15\x63md_get_softap_config\x18\x14 \x01(\x0b\x32\x1c.EspHostedCmdGetSoftAPConfigH\x00\x12?\n\x16resp_get_softap_config\x18\x15 \x01(\x0b\x32\x1d.EspHostedRespGetSoftAPConfigH\x00\x12=\n\x15\x63md_set_softap_config\x18\x16 \x01(\x0b\x32\x1c.EspHostedCmdSetSoftAPConfigH\x00\x12?\n\x16resp_set_softap_config\x18\x17 \x01(\x0b\x32\x1d.EspHostedRespSetSoftAPConfigH\x00\x12\x33\n\x11\x63md_disconnect_ap\x18\x18 \x01(\x0b\x32\x16.EspHostedCmdGetStatusH\x00\x12\x35\n\x12resp_disconnect_ap\x18\x19 \x01(\x0b\x32\x17.EspHostedRespGetStatusH\x00\x12\x31\n\x0f\x63md_stop_softap\x18\x1a \x01(\x0b\x32\x16.EspHostedCmdGetStatusH\x00\x12\x33\n\x10resp_stop_softap\x18\x1b \x01(\x0b\x32\x17.EspHostedRespGetStatusH\x00\x12\x33\n\x10\x63md_scan_ap_list\x18\x1c \x01(\x0b\x32\x17.EspHostedCmdScanResultH\x00\x12\x35\n\x11resp_scan_ap_list\x18\x1d \x01(\x0b\x32\x18.EspHostedRespScanResultH\x00\x12<\n\x17\x63md_connected_stas_list\x18\x1e \x01(\x0b\x32\x19.EspHostedCmdConnectedSTAH\x00\x12>\n\x18resp_connected_stas_list\x18\x1f \x01(\x0b\x32\x1a.EspHostedRespConnectedSTAH\x00\x12\x39\n\x13\x63md_set_mac_address\x18 \x01(\x0b\x32\x1a.EspHostedCmdSetMacAddressH\x00\x12;\n\x14resp_set_mac_address\x18! \x01(\x0b\x32\x1b.EspHostedRespSetMacAddressH\x00\x12\x37\n\x17\x63md_set_power_save_mode\x18\" \x01(\x0b\x32\x14.EspHostedCmdSetModeH\x00\x12\x39\n\x18resp_set_power_save_mode\x18# \x01(\x0b\x32\x15.EspHostedRespSetModeH\x00\x12\x37\n\x17\x63md_get_power_save_mode\x18$ \x01(\x0b\x32\x14.EspHostedCmdGetModeH\x00\x12\x39\n\x18resp_get_power_save_mode\x18% \x01(\x0b\x32\x15.EspHostedRespGetModeH\x00\x12.\n\rcmd_ota_begin\x18& \x01(\x0b\x32\x15.EspHostedCmdOTABeginH\x00\x12\x30\n\x0eresp_ota_begin\x18\' \x01(\x0b\x32\x16.EspHostedRespOTABeginH\x00\x12.\n\rcmd_ota_write\x18( \x01(\x0b\x32\x15.EspHostedCmdOTAWriteH\x00\x12\x30\n\x0eresp_ota_write\x18) \x01(\x0b\x32\x16.EspHostedRespOTAWriteH\x00\x12*\n\x0b\x63md_ota_end\x18* \x01(\x0b\x32\x13.EspHostedCmdOTAEndH\x00\x12,\n\x0cresp_ota_end\x18+ \x01(\x0b\x32\x14.EspHostedRespOTAEndH\x00\x42\t\n\x07payload*\xb7\x01\n\x17\x45spHostedEncryptionMode\x12\r\n\tType_Open\x10\x00\x12\x0c\n\x08Type_WEP\x10\x01\x12\x10\n\x0cType_WPA_PSK\x10\x02\x12\x11\n\rType_WPA2_PSK\x10\x03\x12\x15\n\x11Type_WPA_WPA2_PSK\x10\x04\x12\x18\n\x14Type_WPA2_ENTERPRISE\x10\x05\x12\x11\n\rType_WPA3_PSK\x10\x06\x12\x16\n\x12Type_WPA2_WPA3_PSK\x10\x07*m\n\x0f\x45spHostedStatus\x12\x12\n\x0eTYPE_CONNECTED\x10\x00\x12\x16\n\x12TYPE_NOT_CONNECTED\x10\x01\x12\x14\n\x10TYPE_NO_AP_FOUND\x10\x02\x12\x18\n\x14TYPE_CONNECTION_FAIL\x10\x03*\x83\x07\n\x16\x45spHostedConfigMsgType\x12\x18\n\x14TypeCmdGetMACAddress\x10\x00\x12\x19\n\x15TypeRespGetMACAddress\x10\x01\x12\x16\n\x12TypeCmdGetWiFiMode\x10\x02\x12\x17\n\x13TypeRespGetWiFiMode\x10\x03\x12\x16\n\x12TypeCmdSetWiFiMode\x10\x04\x12\x17\n\x13TypeRespSetWiFiMode\x10\x05\x12\x16\n\x12TypeCmdGetAPConfig\x10\x06\x12\x17\n\x13TypeRespGetAPConfig\x10\x07\x12\x16\n\x12TypeCmdSetAPConfig\x10\x08\x12\x17\n\x13TypeRespSetAPConfig\x10\t\x12\x1a\n\x16TypeCmdGetSoftAPConfig\x10\n\x12\x1b\n\x17TypeRespGetSoftAPConfig\x10\x0b\x12\x1a\n\x16TypeCmdSetSoftAPConfig\x10\x0c\x12\x1b\n\x17TypeRespSetSoftAPConfig\x10\r\x12\x17\n\x13TypeCmdDisconnectAP\x10\x0e\x12\x18\n\x14TypeRespDisconnectAP\x10\x0f\x12\x15\n\x11TypeCmdStopSoftAP\x10\x10\x12\x16\n\x12TypeRespStopSoftAP\x10\x11\x12\x18\n\x14TypeCmdGetAPScanList\x10\x12\x12\x19\n\x15TypeRespGetAPScanList\x10\x13\x12\x1e\n\x1aTypeCmdGetConnectedSTAList\x10\x14\x12\x1f\n\x1bTypeRespGetConnectedSTAList\x10\x15\x12\x18\n\x14TypeCmdSetMacAddress\x10\x16\x12\x19\n\x15TypeRespSetMacAddress\x10\x17\x12\x1b\n\x17TypeCmdSetPowerSaveMode\x10\x18\x12\x1c\n\x18TypeRespSetPowerSaveMode\x10\x19\x12\x1b\n\x17TypeCmdGetPowerSaveMode\x10\x1a\x12\x1c\n\x18TypeRespGetPowerSaveMode\x10\x1b\x12\x13\n\x0fTypeCmdOTABegin\x10\x1c\x12\x14\n\x10TypeRespOTABegin\x10\x1d\x12\x13\n\x0fTypeCmdOTAWrite\x10\x1e\x12\x14\n\x10TypeRespOTAWrite\x10\x1f\x12\x11\n\rTypeCmdOTAEnd\x10 \x12\x12\n\x0eTypeRespOTAEnd\x10!b\x06proto3' ) _ESPHOSTEDENCRYPTIONMODE = _descriptor.EnumDescriptor( @@ -73,8 +73,8 @@ ], containing_type=None, serialized_options=None, - serialized_start=3298, - serialized_end=3481, + serialized_start=3787, + serialized_end=3970, ) _sym_db.RegisterEnumDescriptor(_ESPHOSTEDENCRYPTIONMODE) @@ -109,8 +109,8 @@ ], containing_type=None, serialized_options=None, - serialized_start=3483, - serialized_end=3592, + serialized_start=3972, + serialized_end=4081, ) _sym_db.RegisterEnumDescriptor(_ESPHOSTEDSTATUS) @@ -262,11 +262,41 @@ serialized_options=None, type=None, create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TypeCmdOTABegin', index=28, number=28, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TypeRespOTABegin', index=29, number=29, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TypeCmdOTAWrite', index=30, number=30, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TypeRespOTAWrite', index=31, number=31, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TypeCmdOTAEnd', index=32, number=32, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TypeRespOTAEnd', index=33, number=33, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), ], containing_type=None, serialized_options=None, - serialized_start=3595, - serialized_end=4369, + serialized_start=4084, + serialized_end=4983, ) _sym_db.RegisterEnumDescriptor(_ESPHOSTEDCONFIGMSGTYPE) @@ -311,6 +341,12 @@ TypeRespSetPowerSaveMode = 25 TypeCmdGetPowerSaveMode = 26 TypeRespGetPowerSaveMode = 27 +TypeCmdOTABegin = 28 +TypeRespOTABegin = 29 +TypeCmdOTAWrite = 30 +TypeRespOTAWrite = 31 +TypeCmdOTAEnd = 32 +TypeRespOTAEnd = 33 @@ -1278,6 +1314,184 @@ ) +_ESPHOSTEDCMDOTABEGIN = _descriptor.Descriptor( + name='EspHostedCmdOTABegin', + full_name='EspHostedCmdOTABegin', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1615, + serialized_end=1637, +) + + +_ESPHOSTEDRESPOTABEGIN = _descriptor.Descriptor( + name='EspHostedRespOTABegin', + full_name='EspHostedRespOTABegin', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='resp', full_name='EspHostedRespOTABegin.resp', index=0, + number=1, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1639, + serialized_end=1676, +) + + +_ESPHOSTEDCMDOTAWRITE = _descriptor.Descriptor( + name='EspHostedCmdOTAWrite', + full_name='EspHostedCmdOTAWrite', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='ota_data', full_name='EspHostedCmdOTAWrite.ota_data', index=0, + number=1, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=b"", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1678, + serialized_end=1718, +) + + +_ESPHOSTEDRESPOTAWRITE = _descriptor.Descriptor( + name='EspHostedRespOTAWrite', + full_name='EspHostedRespOTAWrite', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='resp', full_name='EspHostedRespOTAWrite.resp', index=0, + number=1, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1720, + serialized_end=1757, +) + + +_ESPHOSTEDCMDOTAEND = _descriptor.Descriptor( + name='EspHostedCmdOTAEnd', + full_name='EspHostedCmdOTAEnd', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1759, + serialized_end=1779, +) + + +_ESPHOSTEDRESPOTAEND = _descriptor.Descriptor( + name='EspHostedRespOTAEnd', + full_name='EspHostedRespOTAEnd', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='resp', full_name='EspHostedRespOTAEnd.resp', index=0, + number=1, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1781, + serialized_end=1816, +) + + _ESPHOSTEDCONFIGPAYLOAD = _descriptor.Descriptor( name='EspHostedConfigPayload', full_name='EspHostedConfigPayload', @@ -1489,6 +1703,48 @@ message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='cmd_ota_begin', full_name='EspHostedConfigPayload.cmd_ota_begin', index=29, + number=38, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='resp_ota_begin', full_name='EspHostedConfigPayload.resp_ota_begin', index=30, + number=39, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='cmd_ota_write', full_name='EspHostedConfigPayload.cmd_ota_write', index=31, + number=40, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='resp_ota_write', full_name='EspHostedConfigPayload.resp_ota_write', index=32, + number=41, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='cmd_ota_end', full_name='EspHostedConfigPayload.cmd_ota_end', index=33, + number=42, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='resp_ota_end', full_name='EspHostedConfigPayload.resp_ota_end', index=34, + number=43, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), ], extensions=[ ], @@ -1506,8 +1762,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=1616, - serialized_end=3295, + serialized_start=1819, + serialized_end=3784, ) _ESPHOSTEDRESPGETAPCONFIG.fields_by_name['ecn'].enum_type = _ESPHOSTEDENCRYPTIONMODE @@ -1545,6 +1801,12 @@ _ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_set_power_save_mode'].message_type = _ESPHOSTEDRESPSETMODE _ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_get_power_save_mode'].message_type = _ESPHOSTEDCMDGETMODE _ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_get_power_save_mode'].message_type = _ESPHOSTEDRESPGETMODE +_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_ota_begin'].message_type = _ESPHOSTEDCMDOTABEGIN +_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_ota_begin'].message_type = _ESPHOSTEDRESPOTABEGIN +_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_ota_write'].message_type = _ESPHOSTEDCMDOTAWRITE +_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_ota_write'].message_type = _ESPHOSTEDRESPOTAWRITE +_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_ota_end'].message_type = _ESPHOSTEDCMDOTAEND +_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_ota_end'].message_type = _ESPHOSTEDRESPOTAEND _ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'].fields.append( _ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_get_mac_address']) _ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_get_mac_address'].containing_oneof = _ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'] @@ -1629,6 +1891,24 @@ _ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'].fields.append( _ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_get_power_save_mode']) _ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_get_power_save_mode'].containing_oneof = _ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'] +_ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'].fields.append( + _ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_ota_begin']) +_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_ota_begin'].containing_oneof = _ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'] +_ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'].fields.append( + _ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_ota_begin']) +_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_ota_begin'].containing_oneof = _ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'] +_ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'].fields.append( + _ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_ota_write']) +_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_ota_write'].containing_oneof = _ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'] +_ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'].fields.append( + _ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_ota_write']) +_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_ota_write'].containing_oneof = _ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'] +_ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'].fields.append( + _ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_ota_end']) +_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_ota_end'].containing_oneof = _ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'] +_ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'].fields.append( + _ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_ota_end']) +_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_ota_end'].containing_oneof = _ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'] DESCRIPTOR.message_types_by_name['EspHostedCmdGetMacAddress'] = _ESPHOSTEDCMDGETMACADDRESS DESCRIPTOR.message_types_by_name['EspHostedRespGetMacAddress'] = _ESPHOSTEDRESPGETMACADDRESS DESCRIPTOR.message_types_by_name['EspHostedCmdGetMode'] = _ESPHOSTEDCMDGETMODE @@ -1653,6 +1933,12 @@ DESCRIPTOR.message_types_by_name['EspHostedConnectedSTAList'] = _ESPHOSTEDCONNECTEDSTALIST DESCRIPTOR.message_types_by_name['EspHostedCmdConnectedSTA'] = _ESPHOSTEDCMDCONNECTEDSTA DESCRIPTOR.message_types_by_name['EspHostedRespConnectedSTA'] = _ESPHOSTEDRESPCONNECTEDSTA +DESCRIPTOR.message_types_by_name['EspHostedCmdOTABegin'] = _ESPHOSTEDCMDOTABEGIN +DESCRIPTOR.message_types_by_name['EspHostedRespOTABegin'] = _ESPHOSTEDRESPOTABEGIN +DESCRIPTOR.message_types_by_name['EspHostedCmdOTAWrite'] = _ESPHOSTEDCMDOTAWRITE +DESCRIPTOR.message_types_by_name['EspHostedRespOTAWrite'] = _ESPHOSTEDRESPOTAWRITE +DESCRIPTOR.message_types_by_name['EspHostedCmdOTAEnd'] = _ESPHOSTEDCMDOTAEND +DESCRIPTOR.message_types_by_name['EspHostedRespOTAEnd'] = _ESPHOSTEDRESPOTAEND DESCRIPTOR.message_types_by_name['EspHostedConfigPayload'] = _ESPHOSTEDCONFIGPAYLOAD DESCRIPTOR.enum_types_by_name['EspHostedEncryptionMode'] = _ESPHOSTEDENCRYPTIONMODE DESCRIPTOR.enum_types_by_name['EspHostedStatus'] = _ESPHOSTEDSTATUS @@ -1827,6 +2113,48 @@ }) _sym_db.RegisterMessage(EspHostedRespConnectedSTA) +EspHostedCmdOTABegin = _reflection.GeneratedProtocolMessageType('EspHostedCmdOTABegin', (_message.Message,), { + 'DESCRIPTOR' : _ESPHOSTEDCMDOTABEGIN, + '__module__' : 'esp_hosted_config_pb2' + # @@protoc_insertion_point(class_scope:EspHostedCmdOTABegin) + }) +_sym_db.RegisterMessage(EspHostedCmdOTABegin) + +EspHostedRespOTABegin = _reflection.GeneratedProtocolMessageType('EspHostedRespOTABegin', (_message.Message,), { + 'DESCRIPTOR' : _ESPHOSTEDRESPOTABEGIN, + '__module__' : 'esp_hosted_config_pb2' + # @@protoc_insertion_point(class_scope:EspHostedRespOTABegin) + }) +_sym_db.RegisterMessage(EspHostedRespOTABegin) + +EspHostedCmdOTAWrite = _reflection.GeneratedProtocolMessageType('EspHostedCmdOTAWrite', (_message.Message,), { + 'DESCRIPTOR' : _ESPHOSTEDCMDOTAWRITE, + '__module__' : 'esp_hosted_config_pb2' + # @@protoc_insertion_point(class_scope:EspHostedCmdOTAWrite) + }) +_sym_db.RegisterMessage(EspHostedCmdOTAWrite) + +EspHostedRespOTAWrite = _reflection.GeneratedProtocolMessageType('EspHostedRespOTAWrite', (_message.Message,), { + 'DESCRIPTOR' : _ESPHOSTEDRESPOTAWRITE, + '__module__' : 'esp_hosted_config_pb2' + # @@protoc_insertion_point(class_scope:EspHostedRespOTAWrite) + }) +_sym_db.RegisterMessage(EspHostedRespOTAWrite) + +EspHostedCmdOTAEnd = _reflection.GeneratedProtocolMessageType('EspHostedCmdOTAEnd', (_message.Message,), { + 'DESCRIPTOR' : _ESPHOSTEDCMDOTAEND, + '__module__' : 'esp_hosted_config_pb2' + # @@protoc_insertion_point(class_scope:EspHostedCmdOTAEnd) + }) +_sym_db.RegisterMessage(EspHostedCmdOTAEnd) + +EspHostedRespOTAEnd = _reflection.GeneratedProtocolMessageType('EspHostedRespOTAEnd', (_message.Message,), { + 'DESCRIPTOR' : _ESPHOSTEDRESPOTAEND, + '__module__' : 'esp_hosted_config_pb2' + # @@protoc_insertion_point(class_scope:EspHostedRespOTAEnd) + }) +_sym_db.RegisterMessage(EspHostedRespOTAEnd) + EspHostedConfigPayload = _reflection.GeneratedProtocolMessageType('EspHostedConfigPayload', (_message.Message,), { 'DESCRIPTOR' : _ESPHOSTEDCONFIGPAYLOAD, '__module__' : 'esp_hosted_config_pb2' diff --git a/host/linux/host_control/python_support/ota_update.py b/host/linux/host_control/python_support/ota_update.py new file mode 100644 index 0000000000..2415562193 --- /dev/null +++ b/host/linux/host_control/python_support/ota_update.py @@ -0,0 +1,69 @@ +# Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from commands import * +import argparse +import requests +import os + +ota_status = "not_completed" + +# Please do not use chunk size bigger than 4000. +chunk_size = 4000 + +parser = argparse.ArgumentParser(description='ota_update.py is a python script which runs OTA update on ESP') + +parser.add_argument("url", type=str, default='', help="URL to OTA image") + +args = parser.parse_args() + +def OTA_END(): + ota_end = esp_ota_end() + if (ota_end == "failure"): + print("Failure in OTA end") + exit() + else: + print("OTA end "+str(ota_end)) + +try: + response = requests.get(args.url, stream = True) +except: + print("Error while fetching URL") + exit() + +ota_status = esp_ota_begin() +if (ota_status == "failure"): + print("Failure in OTA update") + exit() +elif (ota_status == 0): + print("OTA begin: success") +else: + print("OTA begin:"+str(ota_status)) + +if (chunk_size>4000): + chunk_size = 4000 + +for chunk in response.iter_content(chunk_size): + print("|", end="", flush=True) + ota_status = esp_ota_write(chunk) + print(".", end="", flush=True) + if (ota_status == "failure"): + print("Failed to write OTA update") + OTA_END() + exit() + print(".", end="", flush=True) +print("OTA write success") + +OTA_END() +print("ESP32 will restart in 5 sec"); diff --git a/ota_feature.md b/ota_feature.md new file mode 100644 index 0000000000..ac5ce6a5b0 --- /dev/null +++ b/ota_feature.md @@ -0,0 +1,70 @@ +## ESP-Hosted OTA feature + +Currently, new OTA image supported size is upto 1MB. + +There are basic three control path commands for OTA are provided in C and python, as follows: +* esp_ota_begin() -- Sets available OTA partition in flash for OTA write operation and erase it. +* esp_ota_write() -- Write chunk of OTA image data on OTA partition in flash. +* esp_ota_end() -- Validate written OTA image, set OTA partition for next boot and reboot ESP32 after 5 sec. + +Defination of these commands are present in [commands.c](host/host_common/commands.c) and [commands.py](host/linux/host_control/python_support/commands.py) files. + +## How to use + +### On ESP32 side +Build and flash ESP-Hosted application using `idf.py build flash monitor`. + +### On Host side + +#### Note +1. Please stop ongoing BT/BLE operations before starting OTA process. +2. OTA operation should not get interrupted by any other control path command. + +Perform station connect using [station_connect.py](host/linux/host_control/python_support/station_connect.py) script or [test.c](host/linux/host_control/c_support/test.c), which ups `ethsta0` interface. Assign IP using dhclient. + +#### OTA Using Python3 +* ota_update.py + +[ota_update.py](host/linux/host_control/python_support/ota_update.py) python script is used to do OTA update on ESP32. It downloads chunk of OTA image data using HTTP client over `ethsta0` interface and writes on ESP32. After successful completion it restarts ESP32 after 5 sec. + +Usage: +1. start HTTP server on a remote machine which contains OTA image. +Following python command can be used to start HTTP server if it's not running already. + +Syntax: +``` +python3 -m http.server +``` +Example: +``` +python3 -m http.server 9999 +``` + +2. Pass OTA image URL as command line argument to ota_update.py. + +Syntax: +``` +python3 ota_update.py "http://:/ota_image.bin" +``` +Example: +``` +python3 ota_update.py "http://192.168.0.106:9999/network_adapter.bin" +``` + +#### OTA Using C + +[test.c](host/linux/host_control/c_support/test.c) is a test application to test control path command. `./test.out ota "ota_image"` writes chunk of already downloaded OTA image data on ESP32. After successful completion it restarts ESP32 after 5 sec. + +#### Note +The OTA update using C currently assumes the complete binary is available. +User can do OTA update similar to python method using third party HTTP library. + +Usage: +1. Run `make` in `host/linux/host_control/c_support` directory. +2. Download OTA image binary on host machine. +3. Pass OTA image binary path as next comamnd line argument after `ota`. + +Example: +``` +./test.out ota "/path/to/ota_image.bin" +``` From 895295af68eb11b847eb82f71d7ce120dd8f4eb0 Mon Sep 17 00:00:00 2001 From: "ajita.chavan" Date: Thu, 14 Oct 2021 11:13:17 +0530 Subject: [PATCH 13/40] Ringbuffer changes added Signed-off-by: ajita.chavan --- host/host_common/transport_pserial.c | 2 ++ host/linux/host_driver/esp32/esp_rb.c | 32 ++++++++++++++++------- host/linux/host_driver/esp32/esp_rb.h | 6 ++--- host/linux/host_driver/esp32/esp_serial.c | 20 +++++++------- host/linux/host_driver/esp32/main.c | 2 +- 5 files changed, 39 insertions(+), 23 deletions(-) diff --git a/host/host_common/transport_pserial.c b/host/host_common/transport_pserial.c index 7f66bc92a3..434c6cb66e 100644 --- a/host/host_common/transport_pserial.c +++ b/host/host_common/transport_pserial.c @@ -153,6 +153,8 @@ uint8_t * transport_pserial_data_handler(uint8_t* data, uint16_t data_length, if (! read_buf) { command_log("Failed to read RX data\n"); goto err; + } else if (!pro_len) { + command_log("Zero protobuf data length received \n"); } ret = esp_hosted_driver_close(&esp_hosted_driver_handle); diff --git a/host/linux/host_driver/esp32/esp_rb.c b/host/linux/host_driver/esp32/esp_rb.c index b37ec27d69..3b5ed8bee2 100644 --- a/host/linux/host_driver/esp32/esp_rb.c +++ b/host/linux/host_driver/esp32/esp_rb.c @@ -33,9 +33,9 @@ int esp_rb_init(esp_rb_t *rb, size_t sz) return 0; } -ssize_t esp_rb_read_by_user(esp_rb_t *rb, const char __user *buf, size_t sz, int block) +int esp_rb_read_by_user(esp_rb_t *rb, const char __user *buf, size_t sz, int block) { - size_t read_len = 0, temp_len = 0; + int read_len = 0, temp_len = 0; if (down_interruptible(&rb->sem)) { return -ERESTARTSYS; /* Signal interruption */ @@ -62,7 +62,7 @@ ssize_t esp_rb_read_by_user(esp_rb_t *rb, const char __user *buf, size_t sz, int if (copy_to_user((void *)buf, rb->rp, read_len)) { up(&rb->sem); - printk(KERN_WARNING "%s, Incomplete/Failed read\n", __func__); + printk(KERN_WARNING "%s, %d: Incomplete/Failed read\n", __func__, __LINE__); return -EFAULT; } @@ -75,7 +75,7 @@ ssize_t esp_rb_read_by_user(esp_rb_t *rb, const char __user *buf, size_t sz, int temp_len = min(sz-read_len,(size_t)(rb->wp - rb->rp)); if (copy_to_user((void *)buf+read_len, rb->rp, temp_len)) { up(&rb->sem); - printk(KERN_WARNING "%s, Incomplete/Failed read\n", __func__); + printk(KERN_WARNING "%s, %d: Incomplete/Failed read\n", __func__, __LINE__); return -EFAULT; } } @@ -88,8 +88,11 @@ ssize_t esp_rb_read_by_user(esp_rb_t *rb, const char __user *buf, size_t sz, int return read_len; } -size_t get_free_space(esp_rb_t *rb) +int get_free_space(esp_rb_t *rb) { + if (!rb || !rb->rp || !rb->wp) { + return -EFAULT; + } if (rb->rp == rb->wp) { return rb->size - 1; } else { @@ -97,21 +100,26 @@ size_t get_free_space(esp_rb_t *rb) } } -ssize_t esp_rb_write_by_kernel(esp_rb_t *rb, const char *buf, size_t sz) +int esp_rb_write_by_kernel(esp_rb_t *rb, const char *buf, size_t sz) { - size_t write_len = 0, temp_len = 0; + int write_len = 0, temp_len = 0; + + if (!rb || !rb->wp || !rb->rp) { + printk(KERN_INFO "%s:%u rb uninitialized\n", __func__, __LINE__); + return -EFAULT; + } if (down_interruptible(&rb->sem)) { return -ERESTARTSYS; } - if (get_free_space(rb) == 0) { + if (get_free_space(rb) <= 0) { up(&rb->sem); - printk(KERN_ERR "%s, Ringbuffer full, no space to write\n", __func__); + printk(KERN_ERR "%s, %d, Ringbuffer full or inaccessible\n", __func__, __LINE__); return 0; } - sz = min(sz, get_free_space(rb)); + sz = min(sz, (size_t)get_free_space(rb)); if (rb->wp >= rb->rp) { write_len = min(sz, (size_t)(rb->end - rb->wp)); } else { @@ -132,6 +140,10 @@ ssize_t esp_rb_write_by_kernel(esp_rb_t *rb, const char *buf, size_t sz) rb->wp += temp_len; write_len += temp_len; + if (rb->wp == rb->end) { + rb->wp = rb->buf; + } + up(&rb->sem); wake_up_interruptible(&rb->wq); diff --git a/host/linux/host_driver/esp32/esp_rb.h b/host/linux/host_driver/esp32/esp_rb.h index 2a048e340f..72d8c24bc8 100644 --- a/host/linux/host_driver/esp32/esp_rb.h +++ b/host/linux/host_driver/esp32/esp_rb.h @@ -18,8 +18,8 @@ typedef struct esp_rb { int esp_rb_init(esp_rb_t *rb, size_t sz); void esp_rb_cleanup(esp_rb_t *rb); -ssize_t esp_rb_read_by_user(esp_rb_t *rb, const char __user *buf, size_t sz, int block); -ssize_t esp_rb_write_by_kernel(esp_rb_t *rb, const char *buf, size_t sz); -size_t get_free_space(esp_rb_t *rb); +int esp_rb_read_by_user(esp_rb_t *rb, const char __user *buf, size_t sz, int block); +int esp_rb_write_by_kernel(esp_rb_t *rb, const char *buf, size_t sz); +int get_free_space(esp_rb_t *rb); #endif diff --git a/host/linux/host_driver/esp32/esp_serial.c b/host/linux/host_driver/esp32/esp_serial.c index b719ecd131..e8ace8a982 100644 --- a/host/linux/host_driver/esp32/esp_serial.c +++ b/host/linux/host_driver/esp32/esp_serial.c @@ -31,10 +31,10 @@ #include "esp_rb.h" #include "esp_api.h" -#define ESP_SERIAL_MAJOR 221 -#define ESP_SERIAL_MINOR_MAX 2 -#define ESP_RX_RB_SIZE 4096 -#define ESP_SERIAL_MAX_TX 4096 +#define ESP_SERIAL_MAJOR 221 +#define ESP_SERIAL_MINOR_MAX 2 +#define ESP_RX_RB_SIZE 4096 +#define ESP_SERIAL_MAX_TX 4096 //#define ESP_SERIAL_TEST @@ -46,10 +46,10 @@ static struct esp_serial_devs { struct mutex lock; } devs[ESP_SERIAL_MINOR_MAX]; -static ssize_t esp_serial_read(struct file *file, char __user *user_buffer, size_t size, loff_t *offset) +static int esp_serial_read(struct file *file, char __user *user_buffer, size_t size, loff_t *offset) { struct esp_serial_devs *dev = NULL; - ssize_t ret_size = 0; + int ret_size = 0; dev = (struct esp_serial_devs *) file->private_data; ret_size = esp_rb_read_by_user(&dev->rb, user_buffer, size, !(file->f_flags & O_NONBLOCK)); if (ret_size == 0) { @@ -58,7 +58,7 @@ static ssize_t esp_serial_read(struct file *file, char __user *user_buffer, size return ret_size; } -static ssize_t esp_serial_write(struct file *file, const char __user *user_buffer, size_t size, loff_t * offset) +static int esp_serial_write(struct file *file, const char __user *user_buffer, size_t size, loff_t * offset) { struct esp_payload_header *hdr = NULL; u8 *tx_buf = NULL; @@ -182,8 +182,7 @@ const struct file_operations esp_serial_fops = { int esp_serial_data_received(int dev_index, const char *data, size_t len) { - int ret = 0; - size_t ret_len = 0; + int ret = 0, ret_len = 0; if (dev_index >= ESP_SERIAL_MINOR_MAX) { return -EINVAL; } @@ -196,6 +195,9 @@ int esp_serial_data_received(int dev_index, const char *data, size_t len) } ret_len += ret; } + if (ret <= 0) { + return ret; + } if (ret_len != len) { printk(KERN_ERR "%s, RB full, no space to receive. Dropping packet",__func__); } diff --git a/host/linux/host_driver/esp32/main.c b/host/linux/host_driver/esp32/main.c index e14da6e629..421861193f 100644 --- a/host/linux/host_driver/esp32/main.c +++ b/host/linux/host_driver/esp32/main.c @@ -352,7 +352,7 @@ static void process_rx_packet(struct sk_buff *skb) do { ret = esp_serial_data_received(payload_header->if_num, (skb->data + offset + ret_len), (len - ret_len)); - if (ret == -EINVAL) { + if (ret < 0) { printk(KERN_ERR "%s, Failed to process data for iface type %d\n", __func__, payload_header->if_num); break; } From 2fcc401a913aa728020836072689dde3cd0d95ee Mon Sep 17 00:00:00 2001 From: "ajita.chavan" Date: Fri, 13 Aug 2021 15:54:41 +0530 Subject: [PATCH 14/40] BLE 5.0 HCI over SPI Signed-off-by: Yogesh Mantri Documentation for BLE 5.0 Signed-off-by: ajita.chavan --- docs/Linux_based_host/Getting_started.md | 117 ++++++++++ docs/Linux_based_host/SPI_setup.md | 28 ++- .../network_adapter/main/CMakeLists.txt | 2 +- .../network_adapter/main/app_main.c | 185 +--------------- .../network_adapter/main/slave_bt.c | 206 ++++++++++++++++++ .../network_adapter/main/slave_bt.h | 67 ++++++ .../network_adapter/sdkconfig.defaults | 5 +- .../sdkconfig.defaults.esp32c3 | 15 ++ 8 files changed, 441 insertions(+), 184 deletions(-) create mode 100644 esp/esp_driver/network_adapter/main/slave_bt.c create mode 100644 esp/esp_driver/network_adapter/main/slave_bt.h create mode 100644 esp/esp_driver/network_adapter/sdkconfig.defaults.esp32c3 diff --git a/docs/Linux_based_host/Getting_started.md b/docs/Linux_based_host/Getting_started.md index ec99e4172f..9e2494f3f9 100644 --- a/docs/Linux_based_host/Getting_started.md +++ b/docs/Linux_based_host/Getting_started.md @@ -142,6 +142,123 @@ Run `hcitool scan` for BT device scanning. Run `hcitool lescan` for BLE device scanning. +### 2.2 BLE 5.0 testing + +Only ESP32C3 HCI controller supports BLE 5.0. Several new features are introduced in BLE 5.0. The major areas of improvement are: +1. Slot Availability Mask (SAM) +2. 2 Msym/s PHY for LE +3. LE Long Range +4. High Duty Cycle Non-Connectable Advertising +5. LE Advertising Extensions +6. LE Channel Selection Algorithm #2 + +To test BLE 5.0 on RPi minimum `bluez` version `5.56` and above required. Check current `bluez` version by running following command on RPi: + +``` +bluetoothctl -v +``` +:warning: `hcitool lescan` is deprecated. Please dont use it. + +If `bluez` version is less than 5.56 then follow [this](https://scribles.net/updating-bluez-on-raspberry-pi-from-5-43-to-5-50/) link to update bluez to 5.56. Replace `bluez` version `5.50` to `5.56` while following mentioned link. + +### 2.2.1 Basic scan, pair, connect + +Execute following steps on linux host. +Steps: +1. Run `sudo bluetoothctl`. +2. To Turn on power, run `power on`. +3. Make device discoverable, run `discoverable on`. +4. Make device pairable on, run `pairable on`. +5. Set current agent to default, run `default-agent`. +6. Turn on bluetooth support, run `agent on`. +7. Start btmon to collect log in separate terminal window, run `sudo btmon &`. +8. Turn on mobile phone's bluetooth so that linux host can detect it. +9. Start scanning, run `scan on` in bluetoothctl window. +10. Once mobile phone's MAC address is listed in scan list, stop scanning, run `scan off`. +11. To trust, run `trust `. +12. To pair, run `pair `. +13. To connect, run `connect `. +14. Once connected, please run `discoverable off`. + +#### 2.2.2 GATT Server + +BLE 5.0 has backword compability. It can connect with BLE4.2 devices. +Below example demonstrate linux host as GATT server and mobile phone as GATT client. We are using `nRF connect` application for GATT client operartion. + +Execute following steps on linux host. +Steps: +1. Run `sudo bluetoothctl`. +2. Run `list` to get MAC address of ESP32. +3. To set device name, run `menu advertise`. Then `name `. +4. To come back to main menu, run `back`. +5. To start advertising, run `advertise on`. +Perform below steps on Mobile Phone: +6. Turn on mobile phone's bluetooth. Open nRF connect application, ESP32's MAC address will be displayed under `SCANNER` tab as a result of scan. +7. Click on connect. Client tab will be open. Click on `Generic Attribute` option. +8. Perform read/write on listed characteristics fields in `Generic Attribute` service. +To disconnet: +9. Run `disconnect ` on linux host's `bluetoothctrl` OR click on `DISCONNECT` in nRF connect application's `GATT client` screen. + +#### 2.2.3 GATT client + +BLE 5.0 has backword compability. It can connect with BLE4.2 devices. +Below example demonstrate linux host as GATT client and mobile phone as GATT server. We are using `nRF connect` application for GATT server operartion. + +Execute following steps on linux host. +Steps: +1. Run `sudo bluetoothctl`. +2. To Turn on power, run `power on`. +3. Make device discoverable, run `discoverable on`. +4. Make device pairable, run `pairable on`. +5. Set current agent to default, run `default-agent`. +6. Turn on bluetooth support, run `agent on`. +7. Turn on mobile phone's bluetooth so that linux host can detect it. +8. Start scanning, run `scan on`. +9. Once mobile phone's MAC address is listed in scan list, stop scanning, run `scan off`. +10. Start btmon to collect log in separate terminal window, run `sudo btmon &`. +11. To trust, run `trust `. +12. To pair, run `pair `. +13. To connect, run `connect `. +14. Once connected, please run `discoverable off`. +15. Go to gatt menu, run `menu gatt`. +16. list available attributes, run `list-attributes`. +17. select characteristic of service, run `select-attribute `. +18. perform read/write operation on selected characteristic. +19. To disconnect, run `disconnect `. + +#### 2.2.4 1M, 2M, CODED phy for LE + +BLE5.0 supports 1M, 2M and CODED phy. To use 2M and CODED phy for gatt read/write procedure as follow: + +Note: Default selected phy is 1M. To perform gatt read/write with BLE5.0 peripheral, both host and peripheral must have same phy configuration. + +##### Using 1M phy: +1M phy is default phy for BLE5.0. Follow above mentioned steps in section 2.2.1 +for connection. After connection follow gatt read/write from gatt menu in bluetoothctl. + +##### Using 2M phy: +2M phy can not use for connection in BLE5.0 . So configure phy as 1M and 2M both, make connection with other BLE5.0 device and then set phy as 2M. On peripheral side make primary phy as 1M and secondary phy as 2M. + +Steps: +1. To configure phy as 1M and 2M both, run `sudo hcitool cmd 08 31 03 03 03`. +2. To check selected phy, Go to `bluez-5.56` directory. Run `sudo ./tools/btmgmt --index hci0` and run `phy`. +3. Connect to BLE5.0 device using above mentioned steps in section 2.2.1. +4. while executing connect command, there is `LE Enhanced Connection Complete` event in `btmon` log. Note down `handle` value. +5. After connection, exit form bluetoothctl. Run `exit` in bluetoothctl. +6. Now configure phy into 2M. Run `sudo hcitool cmd 08 32 03 02 02 00`. +7. Follow gatt read/write from gatt menu in bluetoothctl. + +##### Using CODED phy: +Configure CODED phy on host and peripheral side. + +Steps: +1. To configure phy as 1M and 2M both, run `sudo hcitool cmd 08 31 03 03 03`. +2. To check selected phy, Go to `bluez-5.56` directory. Run `sudo ./tools/btmgmt --index hci0` and run `phy`. +3. Connect to BLE5.0 device using above mentioned steps in section 2.2.1. +4. while executing connect command, there is `LE Enhanced Connection Complete` event in `btmon` log. Note down `handle` value. +5. After connection, exit form bluetoothctl. Run `exit` in bluetoothctl. +6. Now configure phy into 2M. Run `sudo hcitool cmd 08 32 03 02 02 00`. +7. Follow gatt read/write from gatt menu in bluetoothctl. ## 3. Troubleshoot Instructions diff --git a/docs/Linux_based_host/SPI_setup.md b/docs/Linux_based_host/SPI_setup.md index 4902d16f2a..ae40e33197 100644 --- a/docs/Linux_based_host/SPI_setup.md +++ b/docs/Linux_based_host/SPI_setup.md @@ -105,6 +105,20 @@ Where, ``` * This command will flash `SPI` interface binaries on `esp32s2` chip. +##### ESP32-C3 +```sh +$ python esptool.py --port --baud 960000 --before default_reset --after hard_reset \ +--chip esp32c3 write_flash --flash_mode dio --flash_size detect --flash_freq 80m \ +0x0 esp_hosted_bootloader_esp32c3_spi_v.bin \ +0x8000 esp_hosted_partition-table_esp32c3_spi_v.bin \ +0x10000 esp_hosted_firmware_esp32c3_spi_v.bin + +Where, + : serial port of ESP peripheral + : 0.1,0.2 etc. Latest from [release page](https://github.com/espressif/esp-hosted/releases) +``` +* This command will flash `SPI` interface binaries on `esp32c3` chip. + * Windows user can use ESP Flash Programming Tool to flash the pre-built binary. #### 2.2.2 Source Compilation @@ -115,23 +129,33 @@ Where, ```sh $ cd esp/esp_driver/network_adapter ``` + ##### Using cmake -* Set target if the ESP32S2 is being used. Skip if ESP32 is being used. + +* :warning: `Set target if the ESP32S2 or ESP32C3 is being used. Skip if ESP32 is being used.` ``` $ idf.py set-target esp32s2 ``` +or +``` +$ idf.py set-target esp32c3 +``` + * Execute following command to configure project ```sh $ idf.py menuconfig ``` * This will open project configuration window. To select SPI transport interface, navigate to `Example Configuration -> Transport layer -> SPI interface -> select` and exit from menuconfig. + +* For ESP32C3, select chip revision in addition. Navigate to `Component config → ESP32C3-Specific → Minimum Supported ESP32-C3 Revision` and select chip version of ESP32C3. + * Use below command to compile and flash the project. Replace with ESP peripheral's serial port. ```sh $ idf.py -p build flash ``` ##### Using make -:warning: *make* build system is only supported till ESP32. Please refer cmake section above for ESP32-S2. +:warning: *make* build system is only supported till ESP32. Please refer cmake section above for ESP32-S2 and ESP32-C3. * Execute following command to configure project ```sh $ make menuconfig diff --git a/esp/esp_driver/network_adapter/main/CMakeLists.txt b/esp/esp_driver/network_adapter/main/CMakeLists.txt index e914caa5b7..4c7a6ae0d4 100644 --- a/esp/esp_driver/network_adapter/main/CMakeLists.txt +++ b/esp/esp_driver/network_adapter/main/CMakeLists.txt @@ -1,4 +1,4 @@ -set(COMPONENT_SRCS "slave_commands.c" "../../../../common/esp_hosted_config.pb-c.c" "protocomm_pserial.c" "app_main.c") +set(COMPONENT_SRCS "slave_commands.c" "../../../../common/esp_hosted_config.pb-c.c" "protocomm_pserial.c" "app_main.c" "slave_bt.c") set(COMPONENT_ADD_INCLUDEDIRS "." "../../../../common/include") if(CONFIG_ESP_SDIO_HOST_INTERFACE) diff --git a/esp/esp_driver/network_adapter/main/app_main.c b/esp/esp_driver/network_adapter/main/app_main.c index 2adcb8c81f..ad1d6897a7 100644 --- a/esp/esp_driver/network_adapter/main/app_main.c +++ b/esp/esp_driver/network_adapter/main/app_main.c @@ -45,6 +45,7 @@ #include "protocomm_pserial.h" #include "slave_commands.h" #include "driver/periph_ctrl.h" +#include "slave_bt.c" #define EV_STR(s) "================ "s" ================" static const char TAG[] = "NETWORK_ADAPTER"; @@ -59,25 +60,11 @@ static const char TAG_RX_S[] = "CONTROL H -> S"; static const char TAG_TX_S[] = "CONTROL S -> H"; #endif -#ifdef CONFIG_BT_HCI_UART_NO -#define BT_TX_PIN 5 -#define BT_RX_PIN 18 -#define BT_RTS_PIN 19 -#define BT_CTS_PIN 23 -#endif - #ifdef CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS #define STATS_TICKS pdMS_TO_TICKS(1000*2) #define ARRAY_SIZE_OFFSET 5 #endif -#ifdef CONFIG_BTDM_CONTROLLER_HCI_MODE_VHCI -#define VHCI_MAX_TIMEOUT_MS 2000 -static SemaphoreHandle_t vhci_send_sem; -static void deinitialize_bluetooth(void); -static esp_err_t initialise_bluetooth(void); -#endif - volatile uint8_t action = 0; volatile uint8_t datapath = 0; volatile uint8_t station_connected = 0; @@ -141,31 +128,7 @@ static uint8_t get_capabilities() cap |= ESP_WLAN_SDIO_SUPPORT; #endif #ifdef CONFIG_BT_ENABLED - ESP_LOGI(TAG, "- BT/BLE"); -#if CONFIG_BTDM_CONTROLLER_HCI_MODE_VHCI -#if CONFIG_ESP_SPI_HOST_INTERFACE - ESP_LOGI(TAG, " - HCI Over SPI"); - cap |= ESP_BT_SPI_SUPPORT; -#else - ESP_LOGI(TAG, " - HCI Over SDIO"); - cap |= ESP_BT_SDIO_SUPPORT; -#endif -#else -#ifdef CONFIG_BT_HCI_UART_NO - ESP_LOGI(TAG, " - HCI Over UART"); - cap |= ESP_BT_UART_SUPPORT; -#endif -#endif -#if CONFIG_BTDM_CTRL_MODE_BLE_ONLY - ESP_LOGI(TAG, " - BLE only"); - cap |= ESP_BLE_ONLY_SUPPORT; -#elif CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY - ESP_LOGI(TAG, " - BR_EDR only"); - cap |= ESP_BR_EDR_ONLY_SUPPORT; -#elif CONFIG_BTDM_CTRL_MODE_BTDM - ESP_LOGI(TAG, " - BT/BLE dual mode"); - cap |= ESP_BLE_ONLY_SUPPORT | ESP_BR_EDR_ONLY_SUPPORT; -#endif + cap |= get_bluetooth_capabilities(); #endif return cap; @@ -416,29 +379,12 @@ void process_rx_pkt(interface_buffer_handle_t **buf_handle_p) #if CONFIG_ESP_SERIAL_DEBUG ESP_LOG_BUFFER_HEXDUMP(TAG_RX_S, r.data, r.len, ESP_LOG_INFO); #endif -#ifdef CONFIG_BTDM_CONTROLLER_HCI_MODE_VHCI - } else if (buf_handle->if_type == ESP_HCI_IF) { - /* VHCI needs one extra byte at the start of payload */ - /* that is accomodated in esp_payload_header */ -#if CONFIG_ESP_BT_DEBUG - ESP_LOG_BUFFER_HEXDUMP("bt_rx", payload, payload_len, ESP_LOG_INFO); -#endif - payload--; - payload_len++; - - if (!esp_vhci_host_check_send_available()) { - ESP_LOGD(TAG, "VHCI not available"); - } - - if (vhci_send_sem) { - if (xSemaphoreTake(vhci_send_sem, VHCI_MAX_TIMEOUT_MS) == pdTRUE) { - esp_vhci_host_send_packet(payload, payload_len); - } else { - ESP_LOGI(TAG, "VHCI sem timeout"); - } - } + } +#if defined(CONFIG_BT_ENABLED) && BLUETOOTH_HCI + else if (buf_handle->if_type == ESP_HCI_IF) { + process_hci_rx_pkt(payload, payload_len); + } #endif - } /* Free buffer handle */ if (buf_handle->free_buf_handle && buf_handle->priv_buffer_handle) { @@ -542,123 +488,6 @@ static int32_t serial_write_data(uint8_t* data, int32_t len) return ESP_OK; } -#ifdef CONFIG_BT_ENABLED - -#ifdef CONFIG_BTDM_CONTROLLER_HCI_MODE_VHCI -static void controller_rcv_pkt_ready(void) -{ - if (vhci_send_sem) - xSemaphoreGive(vhci_send_sem); -} - -static int host_rcv_pkt(uint8_t *data, uint16_t len) -{ - esp_err_t ret = ESP_OK; - interface_buffer_handle_t buf_handle = {0}; - uint8_t *buf = NULL; - - buf = (uint8_t *) malloc(len); - - if (!buf) { - ESP_LOGE(TAG, "HCI Send packet: memory allocation failed"); - return ESP_FAIL; - } - - memcpy(buf, data, len); - - buf_handle.if_type = ESP_HCI_IF; - buf_handle.if_num = 0; - buf_handle.payload_len = len; - buf_handle.payload = buf; - buf_handle.wlan_buf_handle = buf; - buf_handle.free_buf_handle = free; - -#if CONFIG_ESP_BT_DEBUG - ESP_LOG_BUFFER_HEXDUMP("bt_tx", data, len, ESP_LOG_INFO); -#endif - - ret = xQueueSend(to_host_queue[PRIO_Q_BT], &buf_handle, portMAX_DELAY); - - if (ret != pdTRUE) { - ESP_LOGE(TAG, "HCI send packet: Failed to send buffer\n"); - free(buf); - return ESP_FAIL; - } - - return 0; -} - -static esp_vhci_host_callback_t vhci_host_cb = { - controller_rcv_pkt_ready, - host_rcv_pkt -}; -#endif - -static esp_err_t initialise_bluetooth(void) -{ - esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); - -#ifdef CONFIG_BT_HCI_UART_NO -#if CONFIG_ESP_BT_DEBUG - ESP_LOGI(TAG, "UART Pins: TX:%u Rx:%u RTS:%u CTS:%u\n", - BT_TX_PIN, BT_RX_PIN, BT_RTS_PIN, BT_CTS_PIN); -#endif -#if CONFIG_BT_HCI_UART_NO == 1 - periph_module_enable(PERIPH_UART1_MODULE); -#elif CONFIG_BT_HCI_UART_NO == 2 - periph_module_enable(PERIPH_UART2_MODULE); -#endif - - periph_module_enable(PERIPH_UHCI0_MODULE); - ESP_ERROR_CHECK( uart_set_pin(CONFIG_BT_HCI_UART_NO, BT_TX_PIN, - BT_RX_PIN, BT_RTS_PIN, BT_CTS_PIN) ); -#endif - ESP_ERROR_CHECK( esp_bt_controller_init(&bt_cfg) ); -#ifdef CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY - ESP_ERROR_CHECK( esp_bt_controller_enable(ESP_BT_MODE_BLE) ); -#elif CONFIG_BTDM_CONTROLLER_MODE_BR_EDR_ONLY - ESP_ERROR_CHECK( esp_bt_controller_enable(ESP_BT_MODE_CLASSIC_BT) ); -#elif CONFIG_BTDM_CONTROLLER_MODE_BTDM - ESP_ERROR_CHECK( esp_bt_controller_enable(ESP_BT_MODE_BTDM) ); -#endif - -#ifdef CONFIG_BTDM_CONTROLLER_HCI_MODE_VHCI - esp_err_t ret = ESP_OK; - ret = esp_vhci_host_register_callback(&vhci_host_cb); - - if (ret != ESP_OK) { - ESP_LOGE(TAG, "Failed to register VHCI callback"); - return ret; - } - - vhci_send_sem = xSemaphoreCreateBinary(); - if (vhci_send_sem == NULL) { - ESP_LOGE(TAG, "Failed to create VHCI send sem"); - return ESP_ERR_NO_MEM; - } - - xSemaphoreGive(vhci_send_sem); -#endif - - return ESP_OK; -} - -static void deinitialize_bluetooth(void) -{ -#ifdef CONFIG_BTDM_CONTROLLER_HCI_MODE_VHCI - if (vhci_send_sem) { - /* Dummy take and give sema before deleting it */ - xSemaphoreTake(vhci_send_sem, portMAX_DELAY); - xSemaphoreGive(vhci_send_sem); - vSemaphoreDelete(vhci_send_sem); - vhci_send_sem = NULL; - } - esp_bt_controller_disable(); - esp_bt_controller_deinit(); -#endif -} -#endif - static esp_err_t initialise_wifi(void) { wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); diff --git a/esp/esp_driver/network_adapter/main/slave_bt.c b/esp/esp_driver/network_adapter/main/slave_bt.c new file mode 100644 index 0000000000..ae59505590 --- /dev/null +++ b/esp/esp_driver/network_adapter/main/slave_bt.c @@ -0,0 +1,206 @@ +// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifdef CONFIG_BT_ENABLED +#include "esp_bt.h" +#include "slave_bt.h" + +static const char BT_TAG[] = "ESP_BT"; +extern QueueHandle_t to_host_queue[MAX_PRIORITY_QUEUES]; + +#if BLUETOOTH_HCI +/* ***** HCI specific part ***** */ + +#define VHCI_MAX_TIMEOUT_MS 2000 +static SemaphoreHandle_t vhci_send_sem; + +static void controller_rcv_pkt_ready(void) +{ + if (vhci_send_sem) + xSemaphoreGive(vhci_send_sem); +} + +static int host_rcv_pkt(uint8_t *data, uint16_t len) +{ + esp_err_t ret = ESP_OK; + interface_buffer_handle_t buf_handle; + uint8_t *buf = NULL; + + buf = (uint8_t *) malloc(len); + + if (!buf) { + ESP_LOGE(BT_TAG, "HCI Send packet: memory allocation failed"); + return ESP_FAIL; + } + + memcpy(buf, data, len); + + memset(&buf_handle, 0, sizeof(buf_handle)); + + buf_handle.if_type = ESP_HCI_IF; + buf_handle.if_num = 0; + buf_handle.payload_len = len; + buf_handle.payload = buf; + buf_handle.wlan_buf_handle = buf; + buf_handle.free_buf_handle = free; + +#if CONFIG_ESP_BT_DEBUG + ESP_LOG_BUFFER_HEXDUMP("bt_tx", data, len, ESP_LOG_INFO); +#endif + ret = xQueueSend(to_host_queue[PRIO_Q_BT], &buf_handle, portMAX_DELAY); + + if (ret != pdTRUE) { + ESP_LOGE(BT_TAG, "HCI send packet: Failed to send buffer\n"); + free(buf); + return ESP_FAIL; + } + + return 0; +} + +static esp_vhci_host_callback_t vhci_host_cb = { + controller_rcv_pkt_ready, + host_rcv_pkt +}; + +void process_hci_rx_pkt(uint8_t *payload, uint16_t payload_len) { + /* VHCI needs one extra byte at the start of payload */ + /* that is accomodated in esp_payload_header */ +#if CONFIG_ESP_BT_DEBUG + ESP_LOG_BUFFER_HEXDUMP("bt_rx", payload, payload_len, ESP_LOG_INFO); +#endif + payload--; + payload_len++; + + if (!esp_vhci_host_check_send_available()) { + ESP_LOGD(BT_TAG, "VHCI not available"); + } + + if (vhci_send_sem) { + if (xSemaphoreTake(vhci_send_sem, VHCI_MAX_TIMEOUT_MS) == pdTRUE) { + esp_vhci_host_send_packet(payload, payload_len); + } else { + ESP_LOGI(BT_TAG, "VHCI sem timeout"); + } + } +} + +#elif BLUETOOTH_UART +/* ***** UART specific part ***** */ + +static void init_uart(void) +{ +#if BLUETOOTH_UART == 1 + periph_module_enable(PERIPH_UART1_MODULE); + periph_module_reset(PERIPH_UART1_MODULE); +#elif BLUETOOTH_UART == 2 + periph_module_enable(PERIPH_UART2_MODULE); + periph_module_reset(PERIPH_UART2_MODULE); +#endif + + periph_module_enable(PERIPH_UHCI0_MODULE); + periph_module_reset(PERIPH_UHCI0_MODULE); + + ESP_ERROR_CHECK( uart_set_pin(BLUETOOTH_UART, BT_TX_PIN, + BT_RX_PIN, BT_RTS_PIN, BT_CTS_PIN) ); + +} + +#endif + +esp_err_t initialise_bluetooth(void) +{ + esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); + +#ifdef BLUETOOTH_UART + init_uart(); +#endif + ESP_ERROR_CHECK( esp_bt_controller_init(&bt_cfg) ); +#if BLUETOOTH_BLE + ESP_ERROR_CHECK( esp_bt_controller_enable(ESP_BT_MODE_BLE) ); +#elif BLUETOOTH_BT + ESP_ERROR_CHECK( esp_bt_controller_enable(ESP_BT_MODE_CLASSIC_BT) ); +#elif BLUETOOTH_BT_BLE + ESP_ERROR_CHECK( esp_bt_controller_enable(ESP_BT_MODE_BTDM) ); +#endif + +#if BLUETOOTH_HCI + esp_err_t ret = ESP_OK; + ret = esp_vhci_host_register_callback(&vhci_host_cb); + + if (ret != ESP_OK) { + ESP_LOGE(BT_TAG, "Failed to register VHCI callback"); + return ret; + } + + vhci_send_sem = xSemaphoreCreateBinary(); + if (vhci_send_sem == NULL) { + ESP_LOGE(BT_TAG, "Failed to create VHCI send sem"); + return ESP_ERR_NO_MEM; + } + + xSemaphoreGive(vhci_send_sem); +#endif + + return ESP_OK; +} + +void deinitialize_bluetooth(void) +{ +#if BLUETOOTH_HCI + if (vhci_send_sem) { + /* Dummy take and give sema before deleting it */ + xSemaphoreTake(vhci_send_sem, portMAX_DELAY); + xSemaphoreGive(vhci_send_sem); + vSemaphoreDelete(vhci_send_sem); + vhci_send_sem = NULL; + } + esp_bt_controller_disable(); + esp_bt_controller_deinit(); +#endif +} + +uint8_t get_bluetooth_capabilities(void) +{ + uint8_t cap = 0; + ESP_LOGI(BT_TAG, "- BT/BLE"); +#if BLUETOOTH_HCI +#if CONFIG_ESP_SPI_HOST_INTERFACE + ESP_LOGI(BT_TAG, " - HCI Over SPI"); + cap |= ESP_BT_SPI_SUPPORT; +#else + ESP_LOGI(BT_TAG, " - HCI Over SDIO"); + cap |= ESP_BT_SDIO_SUPPORT; +#endif +#elif BLUETOOTH_UART + ESP_LOGI(BT_TAG, " - HCI Over UART"); + cap |= ESP_BT_UART_SUPPORT; +#endif + +#if BLUETOOTH_BLE + ESP_LOGI(BT_TAG, " - BLE only"); + cap |= ESP_BLE_ONLY_SUPPORT; +#elif BLUETOOTH_BT + ESP_LOGI(BT_TAG, " - BR_EDR only"); + cap |= ESP_BR_EDR_ONLY_SUPPORT; +#elif BLUETOOTH_BT_BLE + ESP_LOGI(BT_TAG, " - BT/BLE dual mode"); + cap |= ESP_BLE_ONLY_SUPPORT | ESP_BR_EDR_ONLY_SUPPORT; +#endif + return cap; +} + + +#endif diff --git a/esp/esp_driver/network_adapter/main/slave_bt.h b/esp/esp_driver/network_adapter/main/slave_bt.h new file mode 100644 index 0000000000..b10802f783 --- /dev/null +++ b/esp/esp_driver/network_adapter/main/slave_bt.h @@ -0,0 +1,67 @@ +// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// +#ifndef __SLAVE_BT_H__ +#define __SLAVE_BT_H__ + +#ifdef CONFIG_BT_ENABLED + +#ifdef CONFIG_IDF_TARGET_ESP32C3 + #if (CONFIG_BT_CTRL_MODE_EFF == 1) + #define BLUETOOTH_BLE 1 + #elif (CONFIG_BT_CTRL_MODE_EFF == 2) + #define BLUETOOTH_BT 2 + #elif (CONFIG_BT_CTRL_MODE_EFF == 3) + #define BLUETOOTH_BT_BLE 3 + #endif + + #if defined(CONFIG_BT_CTRL_HCI_MODE_VHCI) + #define BLUETOOTH_HCI 4 + #endif + +#else + #if defined(CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY) + #define BLUETOOTH_BLE 1 + #elif defined(CONFIG_BTDM_CONTROLLER_MODE_BR_EDR_ONLY) + #define BLUETOOTH_BT 2 + #elif defined(CONFIG_BTDM_CONTROLLER_MODE_BTDM) + #define BLUETOOTH_BT_BLE 3 + #endif + + #if defined(CONFIG_BTDM_CONTROLLER_HCI_MODE_VHCI) + #define BLUETOOTH_HCI 4 + #elif CONFIG_BT_HCI_UART_NO + #define BLUETOOTH_UART CONFIG_BT_HCI_UART_NO + #endif + +#endif + +#ifdef BLUETOOTH_UART + #include "driver/uart.h" + #define BT_TX_PIN 5 + #define BT_RX_PIN 18 + #define BT_RTS_PIN 19 + #define BT_CTS_PIN 23 +#elif BLUETOOTH_HCI + void process_hci_rx_pkt(uint8_t *payload, uint16_t payload_len); +#endif + +void deinitialize_bluetooth(void); +esp_err_t initialise_bluetooth(void); +uint8_t get_bluetooth_capabilities(void); + +#endif /* CONFIG_BT_ENABLED */ + +#endif /* __SLAVE_BT_H__ */ diff --git a/esp/esp_driver/network_adapter/sdkconfig.defaults b/esp/esp_driver/network_adapter/sdkconfig.defaults index c53e3c0e40..bc3ec5651c 100644 --- a/esp/esp_driver/network_adapter/sdkconfig.defaults +++ b/esp/esp_driver/network_adapter/sdkconfig.defaults @@ -7,9 +7,8 @@ CONFIG_SDIO_DAT2_DISABLED= CONFIG_BT_ENABLED=y CONFIG_BT_CONTROLLER_ONLY=y CONFIG_BT_BLUEDROID_ENABLED= -CONFIG_BTDM_CONTROLLER_MODE_BTDM=y -CONFIG_BTDM_CONTROLLER_HCI_MODE_VHCI=y -CONFIG_BTDM_CTRL_AUTO_LATENCY=y +CONFIG_BTDM_CTRL_MODE_BTDM=y +CONFIG_BTDM_CTRL_HCI_MODE_VHCI=y CONFIG_ESP32_WIFI_NVS_ENABLED= diff --git a/esp/esp_driver/network_adapter/sdkconfig.defaults.esp32c3 b/esp/esp_driver/network_adapter/sdkconfig.defaults.esp32c3 new file mode 100644 index 0000000000..351dc63dee --- /dev/null +++ b/esp/esp_driver/network_adapter/sdkconfig.defaults.esp32c3 @@ -0,0 +1,15 @@ +CONFIG_ESP32_DEFAULT_CPU_FREQ_160=y +CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=160 +CONFIG_FREERTOS_UNICORE=y +CONFIG_SDIO_DAT2_DISABLED= + +# BT Configuration +CONFIG_BT_ENABLED=y +CONFIG_BT_CONTROLLER_ONLY=y +CONFIG_BT_BLUEDROID_ENABLED= +CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y +CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n +CONFIG_BTDM_CTRL_MODE_BTDM=n +CONFIG_BTDM_CTRL_HCI_MODE_VHCI=y + +CONFIG_ESP32_WIFI_NVS_ENABLED= From adb4d177ab6dcbad7c5a93ecf2d9b8441ab813ce Mon Sep 17 00:00:00 2001 From: "ajita.chavan" Date: Wed, 20 Oct 2021 14:28:45 +0530 Subject: [PATCH 15/40] main/app_main.c -- serial_write_data, serial_read_data function signature matched main/slave_commands.c -- get power save mode function error code return Signed-off-by: ajita.chavan --- esp/esp_driver/network_adapter/main/app_main.c | 4 ++-- esp/esp_driver/network_adapter/main/slave_commands.c | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/esp/esp_driver/network_adapter/main/app_main.c b/esp/esp_driver/network_adapter/main/app_main.c index ad1d6897a7..3ec686499e 100644 --- a/esp/esp_driver/network_adapter/main/app_main.c +++ b/esp/esp_driver/network_adapter/main/app_main.c @@ -423,7 +423,7 @@ void recv_task(void* pvParameters) } } -static int32_t serial_read_data(uint8_t *data, int32_t len) +static ssize_t serial_read_data(uint8_t *data, ssize_t len) { len = min(len, r.len); if (r.valid) { @@ -437,7 +437,7 @@ static int32_t serial_read_data(uint8_t *data, int32_t len) return len; } -static int32_t serial_write_data(uint8_t* data, int32_t len) +static esp_err_t serial_write_data(uint8_t* data, ssize_t len) { esp_err_t ret = ESP_OK; uint8_t *pos = data; diff --git a/esp/esp_driver/network_adapter/main/slave_commands.c b/esp/esp_driver/network_adapter/main/slave_commands.c index 1e32f91e3c..1895fb3c5c 100644 --- a/esp/esp_driver/network_adapter/main/slave_commands.c +++ b/esp/esp_driver/network_adapter/main/slave_commands.c @@ -1368,6 +1368,7 @@ static esp_err_t cmd_get_power_save_mode_handler (EspHostedConfigPayload *req, if (ret) { ESP_LOGE(TAG, "Failed to set power save mode"); resp_payload->resp = FAILURE; + return ESP_OK; } else { resp->resp_get_power_save_mode->has_mode = true; resp->resp_get_power_save_mode->mode = ps_type; From 8b43aa9e2f3a8f1a9a0f156a5063a35b43984328 Mon Sep 17 00:00:00 2001 From: "ajita.chavan" Date: Wed, 20 Oct 2021 15:51:57 +0530 Subject: [PATCH 16/40] Fix for Last byte drop of MAC address of ethernet interface Co-authored-by: t123yh Co-authored-by: ajita.chavan Signed-off-by: ajita.chavan --- host/linux/host_control/c_support/test_api.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/host/linux/host_control/c_support/test_api.c b/host/linux/host_control/c_support/test_api.c index 892ce98bd3..4dafc4c831 100644 --- a/host/linux/host_control/c_support/test_api.c +++ b/host/linux/host_control/c_support/test_api.c @@ -21,9 +21,10 @@ #include #include #include +#include #include "test_api.h" -#define MAC_LEN 6 +#define MAC_LEN 7 #define MAC_STR_LEN 17 #define STA_INTERFACE "ethsta0" @@ -40,7 +41,7 @@ static int convert_mac_to_bytes(uint8_t *out, size_t out_len, char *s) } num_bytes = sscanf(s, "%2x:%2x:%2x:%2x:%2x:%2x", &mac[0],&mac[1], &mac[2], &mac[3], &mac[4], &mac[5]); - if ((num_bytes < MAC_LEN) || + if ((num_bytes < (MAC_LEN - 1)) || (mac[0] > 0xFF) || (mac[1] > 0xFF) || (mac[2] > 0xFF) || @@ -114,14 +115,14 @@ static int setHWaddr(int sockfd, char* iface, char* mac) printf("Failed: Max interface length allowed is %ld \n", sizeof(req.ifr_name)); return FAILURE; } + memset(mac_bytes, '\0', MAC_LEN); ret = convert_mac_to_bytes((uint8_t *)&mac_bytes, sizeof(mac_bytes), mac); if (ret) { printf("Failed to convert mac address \n"); return FAILURE; } - req.ifr_hwaddr.sa_family = true; + req.ifr_hwaddr.sa_family = ARPHRD_ETHER; strncpy(req.ifr_hwaddr.sa_data, mac_bytes, MAC_LEN); - req.ifr_hwaddr.sa_data[MAC_LEN-1] = '\0'; ret = ioctl(sockfd, SIOCSIFHWADDR, &req); if (ret < 0) { return FAILURE; From f5838d4a7612f53a7282a27ef07ebdfbfef04ca2 Mon Sep 17 00:00:00 2001 From: "ajita.chavan" Date: Thu, 26 Aug 2021 16:41:43 +0530 Subject: [PATCH 17/40] python bindings for unification of control path commands [testing] -- Tested control path API's for 500 times using stress.c and stress.py respectively. -- Tested interface up/down related API's for 50 times using stress.c and stress.py respectively. Signed-off-by: ajita.chavan --- .../Linux_based_architecture.md | 2 +- docs/Linux_based_host/Linux_based_readme.md | 19 +- docs/Linux_based_host/directory_structure.md | 8 +- docs/c_api.md | 2 + docs/python_api.md | 6 +- host/linux/host_control/c_support/stress.c | 25 +- host/linux/host_control/c_support/test_api.c | 10 +- .../host_control/python_support/Makefile | 23 + .../python_support/ap_scan_list.py | 3 +- .../host_control/python_support/commands.py | 632 ----- .../python_support/commands_lib.py | 443 ++++ .../python_support/commands_map_py_to_c.py | 83 + .../python_support/connected_stations_list.py | 19 +- .../python_support/esp_hosted_config_pb2.py | 2166 ----------------- .../python_support/hosted_config.py | 111 + .../host_control/python_support/ota_update.py | 4 +- .../python_support/softap_config.py | 16 +- .../python_support/softap_stop.py | 14 +- .../python_support/station_connect.py | 16 +- .../python_support/station_disconnect.py | 16 +- .../host_control/python_support/test_api.py | 4 +- .../python_support/transport/__init__.py | 17 - .../python_support/transport/transport.py | 28 - .../transport/transport_pserial.py | 117 - host/linux/host_control/rpi_init.sh | 156 +- 25 files changed, 783 insertions(+), 3157 deletions(-) create mode 100644 host/linux/host_control/python_support/Makefile delete mode 100644 host/linux/host_control/python_support/commands.py create mode 100644 host/linux/host_control/python_support/commands_lib.py create mode 100644 host/linux/host_control/python_support/commands_map_py_to_c.py delete mode 100644 host/linux/host_control/python_support/esp_hosted_config_pb2.py create mode 100644 host/linux/host_control/python_support/hosted_config.py delete mode 100644 host/linux/host_control/python_support/transport/__init__.py delete mode 100644 host/linux/host_control/python_support/transport/transport.py delete mode 100644 host/linux/host_control/python_support/transport/transport_pserial.py diff --git a/docs/Linux_based_host/Linux_based_architecture.md b/docs/Linux_based_host/Linux_based_architecture.md index c8fcb8bc66..805d91464f 100644 --- a/docs/Linux_based_host/Linux_based_architecture.md +++ b/docs/Linux_based_host/Linux_based_architecture.md @@ -41,8 +41,8 @@ This registers HCI interface with Linux kernel. This interface is implemented ov * These commands are used to control and configure Wi-Fi on ESP peripheral. * Control interface makes use of virtual serial interface provided by ESP Host driver. * There are 2 flavors of control interface implementation: - * Python based implementation * C based implementation + * Python based implementation - It uses C based implementation using `ctypes` package. * API's are described in subsequent section --- diff --git a/docs/Linux_based_host/Linux_based_readme.md b/docs/Linux_based_host/Linux_based_readme.md index fac6825e2b..59544bff9a 100644 --- a/docs/Linux_based_host/Linux_based_readme.md +++ b/docs/Linux_based_host/Linux_based_readme.md @@ -44,20 +44,10 @@ Make sure that Raspberry-Pi is equipped with following: * Python 2.x or 3.x ```sh $ sudo apt install python - $ sudo apt install python-pip ``` or ```sh $ sudo apt install python3 - $ sudo apt install python3-pip - ``` - * Protobuf - ```sh - $ pip install protobuf - ``` - or - ```sh - $ pip3 install protobuf ``` * Clone ESP-Hosted code repository ``` @@ -200,14 +190,7 @@ This section identifies Raspberry-Pi specific setup requirements. * Install following tools on Linux Host machine. * Git * Python 2.x or 3.x: We have tested ESP-Hosted solution with python 2.7.13 and 3.5.3 - * Protobuf: - :warning:`Note: We have tested ESP-Hosted solution with Protobuf version >= 3.13.0` - ``` - $ pip install protobuf - or - $ pip3 install protobuf - ``` - * Bluetooth Stack and utilities: + * Bluetooth Stack and utilities: :warning:`Note: We have tested ESP-Hosted solution with bluez 5.43+` * bluetooth * bluez diff --git a/docs/Linux_based_host/directory_structure.md b/docs/Linux_based_host/directory_structure.md index daefb31bc7..59688eefaf 100644 --- a/docs/Linux_based_host/directory_structure.md +++ b/docs/Linux_based_host/directory_structure.md @@ -5,11 +5,9 @@ │   ├── host_control (Contain necessary files for installation of control path) │   │   ├── c_support (Contain files to test basic control path commands and stress.c for stress testing) │   │   ├── python_support (Contain python scripts for Wi-Fi functionality, -│   │   │ │ `test.py` to test basic control path commands, stress.py for stress testing, also -│   │   │ │ `commands.py` control path commands implementation using -│   │   │ │ python protobuf generated files) -│   │   │   └── transport (Handles read/write operation of control path commands on -│   │   │ ESP-Hosted character driver) +│   │   │ `test.py` to test basic control path commands, stress.py for stress testing, also +│   │   │ `commands_lib.py` control path commands implementation using +│   │   │ `ctypes` module converts command requests from python to c and maps command responses from c to python) │   │   ├── rpi_init.sh (Installation sequence for ESP-Hosted driver) │   │   └── spidev_disabler.dts (dts file for SPI transport) │   └── host_driver (Contain ESP-Hosted kernel module files) diff --git a/docs/c_api.md b/docs/c_api.md index e6a58c8c7c..1b695e5b20 100644 --- a/docs/c_api.md +++ b/docs/c_api.md @@ -2,6 +2,8 @@ This document describes C API's provided for control path interface. Please refer [commands.c](../host/host_common/commands.c) for API's defination. [c_demo.md](c_demo.md) gives overview of how to test control path interface in application also how to perform stress testing of control path interface. +A [stress.c](../host/linux/host_control/c_support/stress.c) can use for stress testing of control path commands. In which, `STRESS_TEST_COUNT` variable represents number of iterations and `STRESS_TEST` variable defines which test should get executed. + ## 1. Data Structures ### 1.1 _struct_ `esp_hosted_sta_config_t` diff --git a/docs/python_api.md b/docs/python_api.md index bca671c5b1..3ecc7f34a0 100644 --- a/docs/python_api.md +++ b/docs/python_api.md @@ -4,6 +4,8 @@ This document describes python API's provided for control interface. [python_demo.md](python_demo.md) gives overview of how to test control path interface in application also how to perform stress testing of control path interface. +A [stress.py](../host/linux/host_control/python_support/stress.py) can use for stress testing of control path commands. In which, `STRESS_TEST_COUNT` variable represents number of iterations and `STRESS_TEST` variable defines which test should get executed. + ## 1. `wifi_get_mac` This is used to retrieve the MAC address of ESP's station or softAP interface @@ -261,9 +263,9 @@ Get the list of connected station to the ESP32 softAP. ### Return -*success case* : list of Stationlist tuple(mac,rssi) +*success case* : list of Stationlist tuple(bssid,rssi) Stations credentials:: -- `mac` : +- `bssid` : MAC address of station - `rssi` : RSSI signal strength diff --git a/host/linux/host_control/c_support/stress.c b/host/linux/host_control/c_support/stress.c index 7a2a933c0c..7bd7e92af5 100644 --- a/host/linux/host_control/c_support/stress.c +++ b/host/linux/host_control/c_support/stress.c @@ -35,35 +35,24 @@ int main(int argc, char *argv[]) } stress_test_count = atoi(num); - printf("stoi %d\n", stress_test_count); for (int i=2; i= (3, 0): - return string.decode('utf-8') - else: - return string - -# wifi get mac -# On success, function returns mac address of ESP32's station or softAP mode else "failure" -# mode == 1 for station mac -# mode == 2 for softAP mac - -def wifi_get_mac(mode): - if ((mode <= wifi_mode_none ) or (mode >= wifi_mode_station_softap)): - print("Invalid mode") - return failure_str - get_mac = EspHostedConfigPayload() - get_mac.msg = EspHostedConfigMsgType.TypeCmdGetMACAddress - get_mac.cmd_get_mac_address.mode = mode - protodata = get_mac.SerializeToString() - tp = Transport_pserial(interface) - response = tp.send_data(endpoint,protodata) - del tp - if response[0] != None and response[0] != success: - return failure_str - if response[1] == None or response[1] == "": - return failure_str - get_mac.ParseFromString(response[1]) - if get_mac.resp_get_mac_address.resp != success: - return failure_str - else: - return get_str(get_mac.resp_get_mac_address.mac) - -# wifi get mode -# Function returns ESP32's wifi mode as follows -# 0: null Mode, Wi-Fi mode not set -# 1: station mode -# 2: softAP mode -# 3: station+softAP mode -# or "failure" - -def wifi_get_mode(): - get_mode = EspHostedConfigPayload() - get_mode.msg = EspHostedConfigMsgType.TypeCmdGetWiFiMode - protodata = get_mode.SerializeToString() - tp = Transport_pserial(interface) - response = tp.send_data(endpoint,protodata) - del tp - if response[0] != None and response[0] != success: - return failure_str - if response[1] == None or response[1] == "": - return failure_str - get_mode.ParseFromString(response[1]) - if get_mode.resp_get_wifi_mode.resp != success: - return failure_str - return get_mode.resp_get_wifi_mode.mode - -# wifi set mode -# Function sets ESP32's wifi mode -# Input parameter -# mode : WiFi mode -# (0: null Mode, Wi-Fi mode not set -# 1: station mode -# 2: softAP mode -# 3: station+softAP mode) -# Returns "success" or "failure" - -def wifi_set_mode(mode): - if (mode < wifi_mode_none or mode > wifi_mode_station_softap): - print("Invalid mode") - return failure_str - set_mode = EspHostedConfigPayload() - set_mode.msg = EspHostedConfigMsgType.TypeCmdSetWiFiMode - set_mode.cmd_set_wifi_mode.mode = mode - protodata = set_mode.SerializeToString() - tp = Transport_pserial(interface) - response = tp.send_data(endpoint,protodata) - del tp - if response[0] != None and response[0] != success: - return failure_str - if response[1] == None or response[1] == "": - return failure_str - set_mode.ParseFromString(response[1]) - if set_mode.resp_set_wifi_mode.resp != success: - return failure_str - else: - return success_str - -# wifi set ap config -# Function sets AP config to which ESP32 station should connect -# Input parameter -# ssid : string parameter, ssid of AP, max 32 bytes -# pwd : string parameter, length of password should be 8~64 bytes ASCII -# bssid : MAC address of AP, To differentiate between APs, In case multiple AP has same ssid -# is_wpa3_supported : status of wpa3 supplicant present on AP -# (False : Unsupported -# True : Supported ) -# listen_interval : Listen interval for ESP32 station to receive beacon when WIFI_PS_MAX_MODEM is set. -# Units: AP beacon intervals. Defaults to 3 if set to 0. -# Output: -# 'success' : successfully connected to AP -# 'failure' : Failed to connect to AP -# 'no_ap_found' : AP not found -# 'invalid_password' : Invalid password - -def wifi_set_ap_config(ssid, pwd, bssid, is_wpa3_supported, listen_interval): - if (len(str(ssid)) > max_ssid_len): - print("Invalid SSID length") - return failure_str - if (len(str(pwd)) > max_password_len) : - print("Invalid Password length") - return failure_str - if (len(str(bssid)) > max_bssid_len) : - print("Invalid BSSID length") - return failure_str - if (is_wpa3_supported < 0 or listen_interval < 0) : - print("Invalid Input") - return failure_str - set_ap_config = EspHostedConfigPayload() - set_ap_config.msg = EspHostedConfigMsgType.TypeCmdSetAPConfig - set_ap_config.cmd_set_ap_config.ssid = str(ssid) - set_ap_config.cmd_set_ap_config.pwd = str(pwd) - set_ap_config.cmd_set_ap_config.bssid = str(bssid) - set_ap_config.cmd_set_ap_config.is_wpa3_supported = is_wpa3_supported - set_ap_config.cmd_set_ap_config.listen_interval = listen_interval - protodata = set_ap_config.SerializeToString() - tp = Transport_pserial(interface) - response = tp.send_data(endpoint,protodata) - del tp - if response[0] != None and response[0] != success: - return failure_str - if response[1] == None or response[1] == "": - return failure_str - set_ap_config.ParseFromString(response[1]) - if set_ap_config.resp_set_ap_config.resp == EspHostedStatus.TYPE_CONNECTION_FAIL: - print("Invalid password entered") - return invalid_password_str - elif set_ap_config.resp_set_ap_config.resp == EspHostedStatus.TYPE_NO_AP_FOUND: - print("No AP found") - return no_ap_found_str - elif set_ap_config.resp_set_ap_config.resp != success: - return failure_str - else: - return success_str - -# wifi get ap config -# Function returns AP config to which ESP32 station is connected -# Output parameter -# ssid : ssid of connected AP -# bssid : MAC address of connected AP -# channel : channel ID, 1 ~ 10 -# rssi : rssi signal strength -# encryption_mode : encryption mode -# (encryption modes are -# 0 : OPEN -# 1 : WEP -# 2 : WPA_PSK -# 3 : WPA2_PSK -# 4 : WPA_WPA2_PSK -# 5 : WPA2_ENTERPRISE -# 6 : WPA3_PSK -# 7 : WPA2_WPA3_PSK ) -# In case of not connected to AP, returns "not_connected" - -def wifi_get_ap_config(): - get_ap_config = EspHostedConfigPayload() - get_ap_config.msg = EspHostedConfigMsgType.TypeCmdGetAPConfig - protodata = get_ap_config.SerializeToString() - tp = Transport_pserial(interface) - response = tp.send_data(endpoint,protodata) - del tp - if response[0] != None and response[0] != success: - return failure_str - if response[1] == None or response[1] == "": - return failure_str - get_ap_config.ParseFromString(response[1]) - if get_ap_config.resp_get_ap_config.resp == EspHostedStatus.TYPE_NOT_CONNECTED: - return not_connected_str - elif get_ap_config.resp_get_ap_config.resp != success: - return failure_str - ssid = get_str(get_ap_config.resp_get_ap_config.ssid) - bssid = get_str(get_ap_config.resp_get_ap_config.bssid) - channel = get_ap_config.resp_get_ap_config.chnl - rssi = get_ap_config.resp_get_ap_config.rssi - ecn = get_ap_config.resp_get_ap_config.ecn - return ssid,bssid,channel,rssi,ecn - -# wifi disconnect ap -# Function disconnects ESP32 station from connected AP -# returns "success" or "failure" - -def wifi_disconnect_ap(): - disconnect_ap = EspHostedConfigPayload() - disconnect_ap.msg = EspHostedConfigMsgType.TypeCmdDisconnectAP - protodata = disconnect_ap.SerializeToString() - tp = Transport_pserial(interface) - response = tp.send_data(endpoint,protodata) - del tp - if response[0] != None and response[0] != success: - return failure_str - if response[1] == None or response[1] == "": - return failure_str - disconnect_ap.ParseFromString(response[1]) - if disconnect_ap.resp_disconnect_ap.resp != success: - return failure_str - else: - return success_str - -# wifi set softAP config -# Function sets ESP32 softAP configurations -# returns "success" or "failure" -# Input parameter -# ssid : string parameter, ssid of softAP -# pwd : string parameter, length of password should be 8~64 bytes ASCII -# chnl : channel ID, In range of 1 to 11 -# ecn : Encryption method -# ( 0 : OPEN, -# 2 : WPA_PSK, -# 3 : WPA2_PSK, -# 4 : WPA_WPA2_PSK) -# max_conn : maximum number of stations can connect to ESP32 softAP (should be in range of 1 to 10) -# ssid_hidden : softAP should broadcast its SSID or not -# ( 0 : SSID is broadcast -# 1 : SSID is not broadcast ) -# bw : set bandwidth of ESP32 softAP -# ( 1 : WIFI_BW_HT20 -# 2 : WIFI_BW_HT40 ) - -def wifi_set_softap_config(ssid, pwd, chnl, ecn, max_conn, ssid_hidden, bw): - if (len(ssid) > max_ssid_len) : - print("Invalid SSID length") - return failure_str - if ((len(pwd) > max_password_len) or (ecn == EspHostedEncryptionMode.Type_Open and (len(pwd))) - or (ecn != EspHostedEncryptionMode.Type_Open and (len(pwd) < min_password_len))): - print("Invalid softAP password length") - return failure_str - if ((chnl < min_channel_no) or (chnl > max_channel_no)): - print("Invalid channel number") - return failure_str - if ((ecn < EspHostedEncryptionMode.Type_Open) or (ecn == EspHostedEncryptionMode.Type_WEP) - or (ecn > EspHostedEncryptionMode.Type_WPA_WPA2_PSK)): - print("Asked Encryption method is not supported in softAP mode") - return failure_str - if (max_conn < min_allowed_stations or max_conn > max_allowed_stations): - print("Invalid maximum connection number") - return failure_str - if (ssid_hidden < ssid_broadcast or ssid_hidden > ssid_not_broadcast): - print("Invalid ssid hidden status") - return failure_str - if (bw < wifi_bw_ht20 or bw > wifi_bw_ht40): - print("Invalid BW") - return failure_str - set_softap_config = EspHostedConfigPayload() - set_softap_config.msg = EspHostedConfigMsgType.TypeCmdSetSoftAPConfig - set_softap_config.cmd_set_softap_config.ssid = str(ssid) - set_softap_config.cmd_set_softap_config.pwd = str(pwd) - set_softap_config.cmd_set_softap_config.chnl = chnl - set_softap_config.cmd_set_softap_config.max_conn = max_conn - set_softap_config.cmd_set_softap_config.ssid_hidden = ssid_hidden - set_softap_config.cmd_set_softap_config.bw = bw - set_softap_config.cmd_set_softap_config.ecn = ecn - protodata = set_softap_config.SerializeToString() - tp = Transport_pserial(interface) - response = tp.send_data(endpoint,protodata) - del tp - if response[0] != None and response[0] != success: - return failure_str - if response[1] == None or response[1] == "": - return failure_str - set_softap_config.ParseFromString(response[1]) - if set_softap_config.resp_set_softap_config.resp != success: - return failure_str - else: - return success_str - -# wifi get softAP config -# Funtion gets ESP32 softAP configuration -# Output parameter -# It returns ssid,pwd,chnl,ecn,max_conn,ssid_hidden,bw in case of "success" -# ssid : string parameter, ssid of softAP -# pwd : string parameter, length of password should be 8~64 bytes ASCII -# chnl : channel ID, In range of 1 to 11 -# ecn : Encryption method -# ( 0 : OPEN, -# 2 : WPA_PSK, -# 3 : WPA2_PSK, -# 4 : WPA_WPA2_PSK) -# max_conn : maximum number of stations can connect to ESP32 softAP (will be in range of 1 to 10) -# ssid_hidden : softAP should broadcast its SSID or not -# ( 0 : SSID is broadcast -# 1 : SSID is not broadcast ) -# bw : bandwidth of ESP32 softAP -# ( 1 : WIFI_BW_HT20 -# 2 : WIFI_BW_HT40 ) -# else returns "failure" - -def wifi_get_softap_config(): - get_softap_config = EspHostedConfigPayload() - get_softap_config.msg = EspHostedConfigMsgType.TypeCmdGetSoftAPConfig - protodata = get_softap_config.SerializeToString() - tp = Transport_pserial(interface) - response = tp.send_data(endpoint,protodata) - del tp - if response[0] != None and response[0] != success: - return failure_str - if response[1] == None or response[1] == "": - return failure_str - get_softap_config.ParseFromString(response[1]) - if get_softap_config.resp_get_softap_config.resp != success: - return failure_str - ssid = get_str(get_softap_config.resp_get_softap_config.ssid) - pwd = get_str(get_softap_config.resp_get_softap_config.pwd) - ecn = get_softap_config.resp_get_softap_config.ecn - chnl = get_softap_config.resp_get_softap_config.chnl - max_conn = get_softap_config.resp_get_softap_config.max_conn - ssid_hidden = get_softap_config.resp_get_softap_config.ssid_hidden - bw = get_softap_config.resp_get_softap_config.bw - return ssid,pwd,chnl,ecn,max_conn,ssid_hidden,bw - -# wifi stop softAP -# Function stops ESP32 softAP -# returns "success" or "failure" - -def wifi_stop_softap(): - stop_softap = EspHostedConfigPayload() - stop_softap.msg = EspHostedConfigMsgType.TypeCmdStopSoftAP - protodata = stop_softap.SerializeToString() - tp = Transport_pserial(interface) - response = tp.send_data(endpoint,protodata) - del tp - if response[0] != None and response[0] != success: - return failure_str - if response[1] == None or response[1] == "": - return failure_str - stop_softap.ParseFromString(response[1]) - if stop_softap.resp_stop_softap.resp != success: - return failure_str - else: - return success_str - -# wifi ap scan list -# Function gives scanned list of available APs -# Output parameter -# output is list of Aplist class instances(ssid,chnl,rssi,bssid,ecn) in case of "success" -# AP credentials:: -# ssid : ssid of AP -# channel : channel ID, in range of 1 to 10 -# bssid : MAC address of AP -# rssi : rssi signal strength -# encryption_mode : encryption mode -# (encryption modes are -# 0 : OPEN -# 1 : WEP -# 2 : WPA_PSK -# 3 : WPA2_PSK -# 4 : WPA_WPA2_PSK -# 5 : WPA2_ENTERPRISE -# 6 : WPA3_PSK -# 7 : WPA2_WPA3_PSK ) -# else returns "failure" - -def wifi_ap_scan_list(): - get_ap_scan_list = EspHostedConfigPayload() - get_ap_scan_list.msg = EspHostedConfigMsgType.TypeCmdGetAPScanList - protodata = get_ap_scan_list.SerializeToString() - tp = Transport_pserial(interface) - response = tp.send_data(endpoint,protodata) - del tp - if response[0] != None and response[0] != success: - return failure_str - if response[1] == None or response[1] == "": - return failure_str - get_ap_scan_list.ParseFromString(response[1]) - if get_ap_scan_list.resp_scan_ap_list.resp != success: - return failure_str - count = get_ap_scan_list.resp_scan_ap_list.count - ap_list = [] - for i in range(count) : - ssid = get_str(get_ap_scan_list.resp_scan_ap_list.entries[i].ssid) - chnl = get_ap_scan_list.resp_scan_ap_list.entries[i].chnl - rssi = get_ap_scan_list.resp_scan_ap_list.entries[i].rssi - bssid = get_str(get_ap_scan_list.resp_scan_ap_list.entries[i].bssid) - ecn = get_ap_scan_list.resp_scan_ap_list.entries[i].ecn - ap_list.append(Aplist(ssid,chnl,rssi,bssid,ecn)) - return ap_list - -# wifi connected stations list -# Function gives list of connected stations(maximum 10) to ESP32 softAP -# In case of "success" -# Output parameter -# Stations credentials:: -# mac : MAC address of station -# rssi : rssi signal strength -# If no station is connected, failure return from slave -# output is list of Stationlist class instances -# else returns "failure" - -def wifi_connected_stations_list(): - get_connected_stations_list = EspHostedConfigPayload() - get_connected_stations_list.msg = EspHostedConfigMsgType.TypeCmdGetConnectedSTAList - protodata = get_connected_stations_list.SerializeToString() - tp = Transport_pserial(interface) - response = tp.send_data(endpoint,protodata) - del tp - if response[0] != None and response[0] != success: - return failure_str - if response[1] == None or response[1] == "": - return failure_str - get_connected_stations_list.ParseFromString(response[1]) - if get_connected_stations_list.resp_connected_stas_list.resp != success: - return failure_str - num = get_connected_stations_list.resp_connected_stas_list.num - if (num == 0) : - print("No station is connected") - return failure_str - else : - stas_list = [] - for i in range(num) : - mac = get_str(get_connected_stations_list.resp_connected_stas_list.stations[i].mac) - rssi = get_connected_stations_list.resp_connected_stas_list.stations[i].rssi - stas_list.append(Stationlist(mac,rssi)) - return stas_list - -# wifi set mac -# Function sets MAC address for Station and softAP interface -# mode == 1 for station mac -# mode == 2 for softAP mac -# returns "success" or "failure" -# @attention 1. First set wifi mode before setting MAC address for respective station and softAP Interface -# @attention 2. ESP32 station and softAP have different MAC addresses, do not set them to be the same. -# @attention 3. The bit 0 of the first byte of ESP32 MAC address can not be 1. -# For example, the MAC address can set to be "1a:XX:XX:XX:XX:XX", but can not be "15:XX:XX:XX:XX:XX". -# @attention 4. MAC address will get reset after esp restarts - -def wifi_set_mac(mode, mac): - if (mode <= wifi_mode_none or mode >= wifi_mode_station_softap): - print("Invalid mode") - return failure_str - if (not(len(mac)) or (len(mac) > max_bssid_len)) : - print("Invalid MAC address") - return failure_str - set_mac = EspHostedConfigPayload() - set_mac.msg = EspHostedConfigMsgType.TypeCmdSetMacAddress - set_mac.cmd_set_mac_address.mode = mode - if sys.version_info >= (3, 0): - mac = bytes(mac,'utf-8') - set_mac.cmd_set_mac_address.mac = mac - protodata = set_mac.SerializeToString() - tp = Transport_pserial(interface) - response = tp.send_data(endpoint,protodata) - del tp - if response[0] != None and response[0] != success: - return failure_str - if response[1] == None or response[1] == "": - return failure_str - set_mac.ParseFromString(response[1]) - if set_mac.resp_set_mac_address.resp != success: - return failure_str - else: - return success_str - -# wifi set power save mode -# Function sets ESP32's power save mode, returns "success" or "failure" -# power save mode == 1 WIFI_PS_MIN_MODEM, /**< Minimum modem power saving. -# In this mode, station wakes up to receive beacon every DTIM period */ -# power save mode == 2 WIFI_PS_MAX_MODEM, /**< Maximum modem power saving. -# In this mode, interval to receive beacons is determined by the -# listen_interval parameter in wifi set ap config function*/ -# Default :: power save mode is WIFI_PS_MIN_MODEM - -def wifi_set_power_save_mode(power_save_mode): - if ((power_save_mode < wifi_ps_min_modem) or (power_save_mode > wifi_ps_max_modem)): - print("Invalid power save mode") - return failure_str - set_power_save_mode = EspHostedConfigPayload() - set_power_save_mode.msg = EspHostedConfigMsgType.TypeCmdSetPowerSaveMode - set_power_save_mode.cmd_set_power_save_mode.mode = power_save_mode - protodata = set_power_save_mode.SerializeToString() - tp = Transport_pserial(interface) - response = tp.send_data(endpoint,protodata) - del tp - if response[0] != None and response[0] != success: - return failure_str - if response[1] == None or response[1] == "": - return failure_str - set_power_save_mode.ParseFromString(response[1]) - if set_power_save_mode.resp_set_power_save_mode.resp != success: - return failure_str - else: - return success_str - -# wifi get power save mode -# Function returns power save mode of ESP32 or "failure" -# power save mode == 1 WIFI_PS_MIN_MODEM, /**< Minimum modem power saving. -# In this mode, station wakes up to receive beacon every DTIM period */ -# power save mode == 2 WIFI_PS_MAX_MODEM, /**< Maximum modem power saving. -# In this mode, interval to receive beacons is determined by the -# listen_interval parameter in wifi set ap config function*/ -# Default :: power save mode is WIFI_PS_MIN_MODEM - -def wifi_get_power_save_mode(): - get_power_save_mode = EspHostedConfigPayload() - get_power_save_mode.msg = EspHostedConfigMsgType.TypeCmdGetPowerSaveMode - protodata = get_power_save_mode.SerializeToString() - tp = Transport_pserial(interface) - response = tp.send_data(endpoint,protodata) - del tp - if response[0] != None and response[0] != success: - return failure_str - if response[1] == None or response[1] == "": - return failure_str - get_power_save_mode.ParseFromString(response[1]) - if get_power_save_mode.resp_get_power_save_mode.resp != success: - return failure_str - else: - return get_power_save_mode.resp_get_power_save_mode.mode - -# wifi ota - -def esp_ota_begin(): - ota_begin = EspHostedConfigPayload() - ota_begin.msg = EspHostedConfigMsgType.TypeCmdOTABegin - protodata = ota_begin.SerializeToString() - tp = Transport_pserial(interface) - response = tp.send_data(endpoint,protodata) - del tp - if response[0] != None and response[0] != success: - return failure_str - if response[1] == None or response[1] == "": - return failure_str - ota_begin.ParseFromString(response[1]) - if ota_begin.resp_ota_begin.resp != success: - return failure_str - else: - return ota_begin.resp_ota_begin.resp - -def esp_ota_write(ota_data): - ota_write = EspHostedConfigPayload() - ota_write.msg = EspHostedConfigMsgType.TypeCmdOTAWrite - ota_write.cmd_ota_write.ota_data = ota_data - protodata = ota_write.SerializeToString() - tp = Transport_pserial(interface) - response = tp.send_data(endpoint,protodata) - del tp - if response[0] != None and response[0] != success: - return failure_str - if response[1] == None or response[1] == "": - return failure_str - ota_write.ParseFromString(response[1]) - if ota_write.resp_ota_write.resp != success: - return failure_str - else: - return ota_write.resp_ota_write.resp - -def esp_ota_end(): - ota_end = EspHostedConfigPayload() - ota_end.msg = EspHostedConfigMsgType.TypeCmdOTAEnd - protodata = ota_end.SerializeToString() - tp = Transport_pserial(interface) - response = tp.send_data(endpoint,protodata) - del tp - if response[0] != None and response[0] != success: - return failure_str - if response[1] == None or response[1] == "": - return failure_str - ota_end.ParseFromString(response[1]) - if ota_end.resp_ota_end.resp != success: - return failure_str - else: - return ota_end.resp_ota_end.resp diff --git a/host/linux/host_control/python_support/commands_lib.py b/host/linux/host_control/python_support/commands_lib.py new file mode 100644 index 0000000000..b6e36c3e94 --- /dev/null +++ b/host/linux/host_control/python_support/commands_lib.py @@ -0,0 +1,443 @@ +# Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from hosted_config import * +import commands_map_py_to_c + +# Control path platform init +# On success, It clears stale data from ringbuffer +# On failure, It exits currently running python script +def control_path_platform_init(): + ret = commands_map_py_to_c.control_path_platform_init() + if ret: + print("Control path init failed") + exit() + return + +# wifi get mac +# On success, function returns mac address of ESP32's station or softap mode else "failure" +# mode == 1 for station mac +# mode == 2 for softap mac +def wifi_get_mac(mode): + if ((mode <= WIFI_MODE_NONE ) or (mode >= WIFI_MODE_SOFTAP_STATION)): + print("Invalid mode") + return failure + mac = create_string_buffer(b"",BSSID_LENGTH) + ret = commands_map_py_to_c.wifi_get_mac(mode, mac) + if not ret : + return get_str(mac.value) + else : + return failure + +# wifi set mac +# Function sets MAC address for Station and SoftAP interface +# mode == 1 for station mac +# mode == 2 for softap mac +# returns "success" or "failure" +# @attention 1. First set wifi mode before setting MAC address for respective station and softap Interface +# @attention 2. ESP32 station and softap have different MAC addresses, do not set them to be the same. +# @attention 3. The bit 0 of the first byte of ESP32 MAC address can not be 1. +# For example, the MAC address can set to be "1a:XX:XX:XX:XX:XX", but can not be "15:XX:XX:XX:XX:XX". +# @attention 4. MAC address will get reset after esp restarts +def wifi_set_mac(mode, mac): + if (mode <= WIFI_MODE_NONE or mode >= WIFI_MODE_SOFTAP_STATION): + print("Invalid mode") + return failure + if (not(len(mac)) or (len(mac) > MAX_BSSID_LEN)) : + print("Invalid MAC address") + return failure + ret = commands_map_py_to_c.wifi_set_mac(mode, set_str(mac)) + if not ret: + return success + else: + return failure + +# wifi get mode +# Function returns ESP32's wifi mode as follows +# 0: null Mode, Wi-Fi mode not set +# 1: station mode +# 2: softap mode +# 3: softap+station mode +# or "failure" + +def wifi_get_mode(): + mode = c_uint() + ret = commands_map_py_to_c.wifi_get_mode(byref(mode)) + if not ret : + return int(mode.value) + else : + return failure + +# wifi set mode +# Function sets ESP32's wifi mode +# Input parameter +# mode : WiFi mode +# (0: null Mode, Wi-Fi mode not set +# 1: station mode +# 2: softAP mode +# 3: softAP+station mode) +# Returns "success" or "failure" + +def wifi_set_mode(mode): + if (mode < WIFI_MODE_NONE or mode > WIFI_MODE_SOFTAP_STATION): + print("Invalid mode") + return failure + wifi_mode = c_uint() + wifi_mode.value = mode + ret = commands_map_py_to_c.wifi_set_mode(wifi_mode) + if not ret: + return success + else: + return failure + +# wifi set ap config +# Function sets AP config to which ESP32 station should connect +# Input parameter +# ssid : string parameter, ssid of AP, max 32 bytes +# pwd : string parameter, length of password should be 8~63 bytes ASCII +# bssid : MAC address of AP, To differentiate between APs, In case multiple AP has same ssid +# is_wpa3_supported : status of wpa3 supplicant present on AP +# (False : Unsupported +# True : Supported ) +# listen_interval : Listen interval for ESP32 station to receive beacon when WIFI_PS_MAX_MODEM is set. +# Units: AP beacon intervals. Defaults to 3 if set to 0. + +def wifi_set_ap_config(ssid, pwd, bssid, is_wpa3_supported, listen_interval): + if (len(str(ssid)) > MAX_SSID_LEN): + print("Invalid SSID length") + return failure + if (len(str(pwd)) > (MAX_PASSWORD_LEN - 1)) : + print("Invalid Password length") + return failure + if (len(str(bssid)) > MAX_BSSID_LEN) : + print("Invalid BSSID length") + return failure + if (is_wpa3_supported < 0 or listen_interval < 0) : + print("Invalid Input") + return failure + ap_config = CONTROL_CONFIG() + ap_config.station.ssid = set_str(ssid) + ap_config.station.pwd = set_str(pwd) + ap_config.station.bssid = set_str(bssid) + ap_config.station.is_wpa3_supported = is_wpa3_supported + ap_config.station.listen_interval = listen_interval + ret = commands_map_py_to_c.wifi_set_ap_config(ap_config) + if not ret: + return success + else: + return failure + +# wifi get ap config +# Function returns AP config to which ESP32 station is connected +# Output parameter +# ssid : ssid of connected AP +# bssid : MAC address of connected AP +# channel : channel ID, 1 ~ 10 +# rssi : rssi signal strength +# encryption_mode : encryption mode +# (encryption modes are +# 0 : OPEN +# 1 : WEP +# 2 : WPA_PSK +# 3 : WPA2_PSK +# 4 : WPA_WPA2_PSK +# 5 : WPA2_ENTERPRISE +# 6 : WPA3_PSK +# 7 : WPA2_WPA3_PSK ) +# In case of not connected to AP, returns "not_connected" + +def wifi_get_ap_config(): + ap_config = CONTROL_CONFIG() + ret = commands_map_py_to_c.wifi_get_ap_config(byref(ap_config.station)) + if not ret: + ssid = get_str(ap_config.station.ssid) + bssid = get_str(ap_config.station.bssid) + channel = int(ap_config.station.channel) + rssi = int(ap_config.station.rssi) + ecn = int(ap_config.station.encryption_mode) + return ssid,bssid,channel,rssi,ecn + else: + print("status "+get_str(ap_config.station.status)) + return failure + +# wifi disconnect ap +# Function disconnects ESP32 station from connected AP +# returns "success" or "failure" + +def wifi_disconnect_ap(): + ret = commands_map_py_to_c.wifi_disconnect_ap() + if not ret: + return success + else: + return failure + +# wifi set softap config +# Function sets ESP32 softap configurations +# returns "success" or "failure" +# Input parameter +# ssid : string parameter, ssid of SoftAP +# pwd : string parameter, length of password should be 8~64 bytes ASCII +# chnl : channel ID, In range of 1 to 11 +# ecn : Encryption method +# ( 0 : OPEN, +# 2 : WPA_PSK, +# 3 : WPA2_PSK, +# 4 : WPA_WPA2_PSK) +# max_conn : maximum number of stations can connect to ESP32 SoftAP (should be in range of 1 to 10) +# ssid_hidden : softap should broadcast its SSID or not +# ( 0 : SSID is broadcast +# 1 : SSID is not broadcast ) +# bw : set bandwidth of ESP32 softap +# ( 1 : WIFI_BW_HT20 +# 2 : WIFI_BW_HT40 ) + +def wifi_set_softap_config(ssid, pwd, chnl, ecn, max_conn, ssid_hidden, bw): + if (len(ssid) > MAX_SSID_LEN) : + print("Invalid SSID length softap") + return failure + if ((len(pwd) > MAX_PASSWORD_LEN) or (ecn == WIFI_AUTH_OPEN and (len(pwd))) + or (ecn != WIFI_AUTH_OPEN and (len(pwd) < MIN_PASSWORD_LEN))): + print("Invalid softap password length") + return failure + if ((chnl < MIN_CHANNEL_NO) or (chnl > MAX_CHANNEL_NO)): + print("Invalid channel number") + return failure + if ((ecn < WIFI_AUTH_OPEN) or (ecn == WIFI_AUTH_WEP) + or (ecn > WIFI_AUTH_WPA_WPA2_PSK)): + print("Asked Encryption method is not supported in SoftAP mode") + return failure + if (max_conn < MIN_ALLOWED_STATIONS or max_conn > MAX_ALLOWED_STATIONS): + print("Invalid maximum connection number") + return failure + if (ssid_hidden < SSID_BROADCAST or ssid_hidden > SSID_NOT_BROADCAST): + print("Invalid ssid hidden status") + return failure + if (bw < WIFI_BW_HT20 or bw > WIFI_BW_HT40): + print("Invalid BW") + return failure + + softap_config = CONTROL_CONFIG() + softap_config.softap.ssid = set_str(ssid) + softap_config.softap.pwd = set_str(pwd) + softap_config.softap.channel = chnl + softap_config.softap.encryption_mode = ecn + softap_config.softap.max_connections = max_conn + softap_config.softap.ssid_hidden = ssid_hidden + softap_config.softap.bandwidth = bw + + ret = commands_map_py_to_c.wifi_set_softap_config(softap_config) + if not ret: + return success + else: + return failure + +# wifi get softap config +# Funtion gets ESP32 softAP configuration +# Output parameter +# It returns ssid,pwd,chnl,ecn,max_conn,ssid_hidden,bw in case of "success" +# ssid : string parameter, ssid of SoftAP +# pwd : string parameter, length of password should be 8~64 bytes ASCII +# chnl : channel ID, In range of 1 to 11 +# ecn : Encryption method +# ( 0 : OPEN, +# 2 : WPA_PSK, +# 3 : WPA2_PSK, +# 4 : WPA_WPA2_PSK) +# max_conn : maximum number of stations can connect to ESP32 SoftAP (will be in range of 1 to 10) +# ssid_hidden : softAP should broadcast its SSID or not +# ( 0 : SSID is broadcast +# 1 : SSID is not broadcast ) +# bw : bandwidth of ESP32 softAP +# ( 1 : WIFI_BW_HT20 +# 2 : WIFI_BW_HT40 ) +# else returns "failure" + +def wifi_get_softap_config(): + softap_config = CONTROL_CONFIG() + ret = commands_map_py_to_c.wifi_get_softap_config(byref(softap_config)) + if not ret: + ssid = get_str(softap_config.softap.ssid) + pwd = get_str(softap_config.softap.pwd) + ecn = int(softap_config.softap.encryption_mode) + chnl = int(softap_config.softap.channel) + max_conn = int(softap_config.softap.max_connections) + ssid_hidden = int(softap_config.softap.ssid_hidden) + bw = int(softap_config.softap.bandwidth) + return ssid,pwd,chnl,ecn,max_conn,ssid_hidden,bw + else: + return failure + +# wifi stop softap +# Function stops ESP32 softAP +# returns "success" or "failure" + +def wifi_stop_softap(): + ret = commands_map_py_to_c.wifi_stop_softap() + if not ret: + return success + else: + return failure + +# wifi ap scan list +# Function gives scanned list of available APs +# Output parameter +# output is list of Aplist class instances(ssid,chnl,rssi,bssid,ecn) in case of "success" +# AP credentials:: +# ssid : ssid of AP +# channel : channel ID, in range of 1 to 10 +# bssid : MAC address of AP +# rssi : rssi signal strength +# encryption_mode : encryption mode +# (encryption modes are +# 0 : OPEN +# 1 : WEP +# 2 : WPA_PSK +# 3 : WPA2_PSK +# 4 : WPA_WPA2_PSK +# 5 : WPA2_ENTERPRISE +# 6 : WPA3_PSK +# 7 : WPA2_WPA3_PSK ) +# else returns "failure" + +def wifi_ap_scan_list(): + count = c_uint() + ap_scan_list_ptr = commands_map_py_to_c.wifi_ap_scan_list(byref(count)) + if ap_scan_list_ptr is None: + return failure + list_type = WIFI_SCAN_LIST * count.value + ap_scan_list = cast(ap_scan_list_ptr, POINTER(list_type)) + ap_list = [] + if count.value: + for i in range(count.value): + ssid = get_str(ap_scan_list.contents[i].ssid) + chnl = int(ap_scan_list.contents[i].channel) + rssi = int(ap_scan_list.contents[i].rssi) + bssid = get_str(ap_scan_list.contents[i].bssid) + ecn = int(ap_scan_list.contents[i].encryption_mode) + ap_list.append(Aplist(ssid,chnl,rssi,bssid,ecn)) + commands_map_py_to_c.esp_hosted_free(ap_scan_list_ptr) + ap_scan_list_ptr = None + return ap_list + +# wifi connected stations list +# Function gives list of connected stations(maximum 10) to ESP32 softAP +# In case of "success" +# Output parameter +# Stations credentials:: +# mac : MAC address of station +# rssi : rssi signal strength +# If no station is connected, failure return from slave +# output is list of Stationlist class instances +# else returns "failure" + +def wifi_connected_stations_list(): + count = c_uint() + stations_list_ptr = commands_map_py_to_c.wifi_connected_stations_list(byref(count)) + if stations_list_ptr is None: + return failure + list_type = WIFI_STATIONS_LIST * count.value + stations_list = cast(stations_list_ptr, POINTER(list_type)) + if count.value: + stas_list = [] + for i in range(count.value): + bssid = get_str(stations_list.contents[i].bssid) + rssi = int(stations_list.contents[i].rssi) + stas_list.append(Stationlist(bssid,rssi)) + commands_map_py_to_c.esp_hosted_free(stations_list_ptr) + stations_list_ptr = None + return stas_list + else: + commands_map_py_to_c.esp_hosted_free(stations_list_ptr) + stations_list_ptr = None + print("No station is connected") + return failure + +# wifi set power save mode +# Function sets ESP32's power save mode, returns "success" or "failure" +# power save mode == 1 WIFI_PS_MIN_MODEM, /**< Minimum modem power saving. +# In this mode, station wakes up to receive beacon every DTIM period */ +# power save mode == 2 WIFI_PS_MAX_MODEM, /**< Maximum modem power saving. +# In this mode, interval to receive beacons is determined by the +# listen_interval parameter in wifi set ap config function*/ +# Default :: power save mode is WIFI_PS_MIN_MODEM + +def wifi_set_power_save_mode(power_save_mode): + if ((power_save_mode < WIFI_PS_MIN_MODEM) or (power_save_mode > WIFI_PS_MAX_MODEM)): + print("Unsupported power save mode") + return failure + mode = c_uint() + mode.value = power_save_mode + ret = commands_map_py_to_c.wifi_set_power_save_mode(mode) + if not ret: + return success + else: + return failure + +# wifi get power save mode +# Function returns power save mode of ESP32 or "failure" +# power save mode == 1 WIFI_PS_MIN_MODEM, /**< Minimum modem power saving. +# In this mode, station wakes up to receive beacon every DTIM period */ +# power save mode == 2 WIFI_PS_MAX_MODEM, /**< Maximum modem power saving. +# In this mode, interval to receive beacons is determined by the +# listen_interval parameter in wifi set ap config function*/ +# Default :: power save mode is WIFI_PS_MIN_MODEM + +def wifi_get_power_save_mode(): + mode = c_uint() + ret = commands_map_py_to_c.wifi_get_power_save_mode(byref(mode)) + if not ret: + return mode.value + else: + return failure + +# OTA begin +# function returns "success" or "failure" +# esp ota begin function performs an OTA begin operation for ESP32 +# which sets partition for OTA write and erase it. + +def esp_ota_begin(): + ret = commands_map_py_to_c.esp_ota_begin() + if not ret: + return success + else: + return failure + +# OTA write +# esp ota write function performs an OTA write operation for ESP32, +# function returns "success" or "failure" +# It writes ota_data buffer to OTA partition in flash +# +# Input parameter: +# ota_data : OTA data buffer +# ota_data_len : length of OTA data buffer + +def esp_ota_write(ota_data, ota_image_len): + ret = commands_map_py_to_c.esp_ota_write(ota_data, ota_image_len) + if not ret: + return success + else: + return failure + +# OTA end +# esp ota end function performs an OTA end operation for ESP32, +# function returns "success" or "failure" +# It validates written OTA image, set OTA partition as boot partition for next boot, +# Creates timer which reset ESP32 after 5 sec, + +def esp_ota_end(): + ret = commands_map_py_to_c.esp_ota_end() + if not ret: + return success + else: + return failure diff --git a/host/linux/host_control/python_support/commands_map_py_to_c.py b/host/linux/host_control/python_support/commands_map_py_to_c.py new file mode 100644 index 0000000000..1e469ccf3a --- /dev/null +++ b/host/linux/host_control/python_support/commands_map_py_to_c.py @@ -0,0 +1,83 @@ +# Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from ctypes import * +from hosted_config import * +import os.path +import subprocess +import sys + +lib = os.path.isfile("commands.so") +if not lib: + print("commands.so file is missing, please run ./rpi_init.sh script.") + sys.exit() + +commands_lib = cdll.LoadLibrary(os.path.abspath("commands.so")) + +wifi_get_mac = commands_lib.wifi_get_mac +wifi_get_mac.restype = c_int + +wifi_get_mode = commands_lib.wifi_get_mode +wifi_get_mode.restype = c_int + +wifi_set_mode = commands_lib.wifi_set_mode +wifi_set_mode.restype = c_int + +wifi_set_ap_config = commands_lib.wifi_set_ap_config +wifi_set_ap_config.restype = c_int + +wifi_get_ap_config = commands_lib.wifi_get_ap_config +wifi_get_ap_config.restype = c_int + +wifi_disconnect_ap = commands_lib.wifi_disconnect_ap +wifi_disconnect_ap.restype = c_int + +wifi_set_softap_config = commands_lib.wifi_set_softap_config +wifi_set_softap_config.restype = c_int + +wifi_get_softap_config = commands_lib.wifi_get_softap_config +wifi_get_softap_config.restype = c_int + +wifi_ap_scan_list = commands_lib.wifi_ap_scan_list +wifi_ap_scan_list.restype = c_void_p + +wifi_connected_stations_list = commands_lib.wifi_connected_stations_list +wifi_connected_stations_list.restype = c_void_p + +wifi_set_mac = commands_lib.wifi_set_mac +wifi_set_mac.restype = c_int + +wifi_set_power_save_mode = commands_lib.wifi_set_power_save_mode +wifi_set_power_save_mode.restype = c_int + +wifi_get_power_save_mode = commands_lib.wifi_get_power_save_mode +wifi_get_power_save_mode.restype = c_int + +wifi_stop_softap = commands_lib.wifi_stop_softap +wifi_stop_softap.restype = c_int + +esp_hosted_free = commands_lib.esp_hosted_free +esp_hosted_free.restype = None + +control_path_platform_init = commands_lib.control_path_platform_init +control_path_platform_init.restype = c_int + +esp_ota_begin = commands_lib.esp_ota_begin +esp_ota_begin.restype = c_int + +esp_ota_write = commands_lib.esp_ota_write +esp_ota_write.restype = c_int + +esp_ota_end = commands_lib.esp_ota_end +esp_ota_end.restype = c_int diff --git a/host/linux/host_control/python_support/connected_stations_list.py b/host/linux/host_control/python_support/connected_stations_list.py index 70a33149df..8d3a11ab3a 100644 --- a/host/linux/host_control/python_support/connected_stations_list.py +++ b/host/linux/host_control/python_support/connected_stations_list.py @@ -12,33 +12,22 @@ # See the License for the specific language governing permissions and # limitations under the License. -from commands import * +from commands_lib import * import argparse -# WiFi Mode -# NULL 0 -# Station 1 -# softAP 2 -# Station + softAP 3 - -wifi_mode_none = 0 -wifi_mode_station = 1 -wifi_mode_softap = 2 -wifi_mode_station_softap = 3 -failure = "failure" get_mode = 'not_set' -stations_list = "No station is connected" +stations_list = "No_station_is_connected" parser = argparse.ArgumentParser(description='connected_stations_list.py is a python script which gives list of mac addresses of stations connected to softAP. ex. python connected_stations_list.py') get_mode = wifi_get_mode() -if ((get_mode == wifi_mode_softap) or (get_mode == wifi_mode_station_softap)): +if ((get_mode == WIFI_MODE_SOFTAP) or (get_mode == WIFI_MODE_SOFTAP_STATION)): stations_list = wifi_connected_stations_list() if (stations_list == failure): print("failure in getting connected stations list") else: for obj in stations_list: - print(obj.mac) + print(obj.bssid) elif (get_mode == failure): print("failure in getting wifi mode") else : diff --git a/host/linux/host_control/python_support/esp_hosted_config_pb2.py b/host/linux/host_control/python_support/esp_hosted_config_pb2.py deleted file mode 100644 index 1950af6ef0..0000000000 --- a/host/linux/host_control/python_support/esp_hosted_config_pb2.py +++ /dev/null @@ -1,2166 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: esp_hosted_config.proto -"""Generated protocol buffer code.""" -from google.protobuf.internal import enum_type_wrapper -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection -from google.protobuf import symbol_database as _symbol_database -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - - - -DESCRIPTOR = _descriptor.FileDescriptor( - name='esp_hosted_config.proto', - package='', - syntax='proto3', - serialized_options=None, - create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\x17\x65sp_hosted_config.proto\")\n\x19\x45spHostedCmdGetMacAddress\x12\x0c\n\x04mode\x18\x01 \x01(\x05\"7\n\x1a\x45spHostedRespGetMacAddress\x12\x0b\n\x03mac\x18\x01 \x01(\x0c\x12\x0c\n\x04resp\x18\x02 \x01(\x05\"\x15\n\x13\x45spHostedCmdGetMode\"2\n\x14\x45spHostedRespGetMode\x12\x0c\n\x04mode\x18\x01 \x01(\x05\x12\x0c\n\x04resp\x18\x02 \x01(\x05\"#\n\x13\x45spHostedCmdSetMode\x12\x0c\n\x04mode\x18\x01 \x01(\x05\"$\n\x14\x45spHostedRespSetMode\x12\x0c\n\x04resp\x18\x01 \x01(\x05\"\x17\n\x15\x45spHostedCmdGetStatus\"&\n\x16\x45spHostedRespGetStatus\x12\x0c\n\x04resp\x18\x01 \x01(\x05\"6\n\x19\x45spHostedCmdSetMacAddress\x12\x0b\n\x03mac\x18\x01 \x01(\x0c\x12\x0c\n\x04mode\x18\x02 \x01(\x05\"*\n\x1a\x45spHostedRespSetMacAddress\x12\x0c\n\x04resp\x18\x01 \x01(\x05\"\x19\n\x17\x45spHostedCmdGetAPConfig\"\x88\x01\n\x18\x45spHostedRespGetAPConfig\x12\x0c\n\x04ssid\x18\x01 \x01(\x0c\x12\r\n\x05\x62ssid\x18\x02 \x01(\x0c\x12\x0c\n\x04rssi\x18\x03 \x01(\x05\x12\x0c\n\x04\x63hnl\x18\x04 \x01(\x05\x12%\n\x03\x65\x63n\x18\x05 \x01(\x0e\x32\x18.EspHostedEncryptionMode\x12\x0c\n\x04resp\x18\x06 \x01(\x05\"w\n\x17\x45spHostedCmdSetAPConfig\x12\x0c\n\x04ssid\x18\x01 \x01(\t\x12\x0b\n\x03pwd\x18\x02 \x01(\t\x12\r\n\x05\x62ssid\x18\x03 \x01(\t\x12\x19\n\x11is_wpa3_supported\x18\x04 \x01(\x08\x12\x17\n\x0flisten_interval\x18\x05 \x01(\x05\"(\n\x18\x45spHostedRespSetAPConfig\x12\x0c\n\x04resp\x18\x01 \x01(\x05\"\x1d\n\x1b\x45spHostedCmdGetSoftAPConfig\"\xaf\x01\n\x1c\x45spHostedRespGetSoftAPConfig\x12\x0c\n\x04ssid\x18\x01 \x01(\x0c\x12\x0b\n\x03pwd\x18\x02 \x01(\x0c\x12\x0c\n\x04\x63hnl\x18\x03 \x01(\x05\x12%\n\x03\x65\x63n\x18\x04 \x01(\x0e\x32\x18.EspHostedEncryptionMode\x12\x10\n\x08max_conn\x18\x05 \x01(\x05\x12\x13\n\x0bssid_hidden\x18\x06 \x01(\x08\x12\n\n\x02\x62w\x18\x07 \x01(\x05\x12\x0c\n\x04resp\x18\x08 \x01(\x05\"\xa0\x01\n\x1b\x45spHostedCmdSetSoftAPConfig\x12\x0c\n\x04ssid\x18\x01 \x01(\t\x12\x0b\n\x03pwd\x18\x02 \x01(\t\x12\x0c\n\x04\x63hnl\x18\x03 \x01(\x05\x12%\n\x03\x65\x63n\x18\x04 \x01(\x0e\x32\x18.EspHostedEncryptionMode\x12\x10\n\x08max_conn\x18\x05 \x01(\x05\x12\x13\n\x0bssid_hidden\x18\x06 \x01(\x08\x12\n\n\x02\x62w\x18\x07 \x01(\x05\",\n\x1c\x45spHostedRespSetSoftAPConfig\x12\x0c\n\x04resp\x18\x01 \x01(\x05\"u\n\x13\x45spHostedScanResult\x12\x0c\n\x04ssid\x18\x01 \x01(\x0c\x12\x0c\n\x04\x63hnl\x18\x02 \x01(\r\x12\x0c\n\x04rssi\x18\x03 \x01(\x05\x12\r\n\x05\x62ssid\x18\x04 \x01(\x0c\x12%\n\x03\x65\x63n\x18\x05 \x01(\x0e\x32\x18.EspHostedEncryptionMode\"\x18\n\x16\x45spHostedCmdScanResult\"]\n\x17\x45spHostedRespScanResult\x12\r\n\x05\x63ount\x18\x01 \x01(\r\x12%\n\x07\x65ntries\x18\x02 \x03(\x0b\x32\x14.EspHostedScanResult\x12\x0c\n\x04resp\x18\x03 \x01(\x05\"6\n\x19\x45spHostedConnectedSTAList\x12\x0b\n\x03mac\x18\x01 \x01(\x0c\x12\x0c\n\x04rssi\x18\x02 \x01(\x05\"\x1a\n\x18\x45spHostedCmdConnectedSTA\"d\n\x19\x45spHostedRespConnectedSTA\x12\x0b\n\x03num\x18\x01 \x01(\r\x12,\n\x08stations\x18\x02 \x03(\x0b\x32\x1a.EspHostedConnectedSTAList\x12\x0c\n\x04resp\x18\x03 \x01(\x05\"\x16\n\x14\x45spHostedCmdOTABegin\"%\n\x15\x45spHostedRespOTABegin\x12\x0c\n\x04resp\x18\x01 \x01(\x05\"(\n\x14\x45spHostedCmdOTAWrite\x12\x10\n\x08ota_data\x18\x01 \x01(\x0c\"%\n\x15\x45spHostedRespOTAWrite\x12\x0c\n\x04resp\x18\x01 \x01(\x05\"\x14\n\x12\x45spHostedCmdOTAEnd\"#\n\x13\x45spHostedRespOTAEnd\x12\x0c\n\x04resp\x18\x01 \x01(\x05\"\xad\x0f\n\x16\x45spHostedConfigPayload\x12$\n\x03msg\x18\x01 \x01(\x0e\x32\x17.EspHostedConfigMsgType\x12\x39\n\x13\x63md_get_mac_address\x18\n \x01(\x0b\x32\x1a.EspHostedCmdGetMacAddressH\x00\x12;\n\x14resp_get_mac_address\x18\x0b \x01(\x0b\x32\x1b.EspHostedRespGetMacAddressH\x00\x12\x31\n\x11\x63md_get_wifi_mode\x18\x0c \x01(\x0b\x32\x14.EspHostedCmdGetModeH\x00\x12\x33\n\x12resp_get_wifi_mode\x18\r \x01(\x0b\x32\x15.EspHostedRespGetModeH\x00\x12\x31\n\x11\x63md_set_wifi_mode\x18\x0e \x01(\x0b\x32\x14.EspHostedCmdSetModeH\x00\x12\x33\n\x12resp_set_wifi_mode\x18\x0f \x01(\x0b\x32\x15.EspHostedRespSetModeH\x00\x12\x35\n\x11\x63md_get_ap_config\x18\x10 \x01(\x0b\x32\x18.EspHostedCmdGetAPConfigH\x00\x12\x37\n\x12resp_get_ap_config\x18\x11 \x01(\x0b\x32\x19.EspHostedRespGetAPConfigH\x00\x12\x35\n\x11\x63md_set_ap_config\x18\x12 \x01(\x0b\x32\x18.EspHostedCmdSetAPConfigH\x00\x12\x37\n\x12resp_set_ap_config\x18\x13 \x01(\x0b\x32\x19.EspHostedRespSetAPConfigH\x00\x12=\n\x15\x63md_get_softap_config\x18\x14 \x01(\x0b\x32\x1c.EspHostedCmdGetSoftAPConfigH\x00\x12?\n\x16resp_get_softap_config\x18\x15 \x01(\x0b\x32\x1d.EspHostedRespGetSoftAPConfigH\x00\x12=\n\x15\x63md_set_softap_config\x18\x16 \x01(\x0b\x32\x1c.EspHostedCmdSetSoftAPConfigH\x00\x12?\n\x16resp_set_softap_config\x18\x17 \x01(\x0b\x32\x1d.EspHostedRespSetSoftAPConfigH\x00\x12\x33\n\x11\x63md_disconnect_ap\x18\x18 \x01(\x0b\x32\x16.EspHostedCmdGetStatusH\x00\x12\x35\n\x12resp_disconnect_ap\x18\x19 \x01(\x0b\x32\x17.EspHostedRespGetStatusH\x00\x12\x31\n\x0f\x63md_stop_softap\x18\x1a \x01(\x0b\x32\x16.EspHostedCmdGetStatusH\x00\x12\x33\n\x10resp_stop_softap\x18\x1b \x01(\x0b\x32\x17.EspHostedRespGetStatusH\x00\x12\x33\n\x10\x63md_scan_ap_list\x18\x1c \x01(\x0b\x32\x17.EspHostedCmdScanResultH\x00\x12\x35\n\x11resp_scan_ap_list\x18\x1d \x01(\x0b\x32\x18.EspHostedRespScanResultH\x00\x12<\n\x17\x63md_connected_stas_list\x18\x1e \x01(\x0b\x32\x19.EspHostedCmdConnectedSTAH\x00\x12>\n\x18resp_connected_stas_list\x18\x1f \x01(\x0b\x32\x1a.EspHostedRespConnectedSTAH\x00\x12\x39\n\x13\x63md_set_mac_address\x18 \x01(\x0b\x32\x1a.EspHostedCmdSetMacAddressH\x00\x12;\n\x14resp_set_mac_address\x18! \x01(\x0b\x32\x1b.EspHostedRespSetMacAddressH\x00\x12\x37\n\x17\x63md_set_power_save_mode\x18\" \x01(\x0b\x32\x14.EspHostedCmdSetModeH\x00\x12\x39\n\x18resp_set_power_save_mode\x18# \x01(\x0b\x32\x15.EspHostedRespSetModeH\x00\x12\x37\n\x17\x63md_get_power_save_mode\x18$ \x01(\x0b\x32\x14.EspHostedCmdGetModeH\x00\x12\x39\n\x18resp_get_power_save_mode\x18% \x01(\x0b\x32\x15.EspHostedRespGetModeH\x00\x12.\n\rcmd_ota_begin\x18& \x01(\x0b\x32\x15.EspHostedCmdOTABeginH\x00\x12\x30\n\x0eresp_ota_begin\x18\' \x01(\x0b\x32\x16.EspHostedRespOTABeginH\x00\x12.\n\rcmd_ota_write\x18( \x01(\x0b\x32\x15.EspHostedCmdOTAWriteH\x00\x12\x30\n\x0eresp_ota_write\x18) \x01(\x0b\x32\x16.EspHostedRespOTAWriteH\x00\x12*\n\x0b\x63md_ota_end\x18* \x01(\x0b\x32\x13.EspHostedCmdOTAEndH\x00\x12,\n\x0cresp_ota_end\x18+ \x01(\x0b\x32\x14.EspHostedRespOTAEndH\x00\x42\t\n\x07payload*\xb7\x01\n\x17\x45spHostedEncryptionMode\x12\r\n\tType_Open\x10\x00\x12\x0c\n\x08Type_WEP\x10\x01\x12\x10\n\x0cType_WPA_PSK\x10\x02\x12\x11\n\rType_WPA2_PSK\x10\x03\x12\x15\n\x11Type_WPA_WPA2_PSK\x10\x04\x12\x18\n\x14Type_WPA2_ENTERPRISE\x10\x05\x12\x11\n\rType_WPA3_PSK\x10\x06\x12\x16\n\x12Type_WPA2_WPA3_PSK\x10\x07*m\n\x0f\x45spHostedStatus\x12\x12\n\x0eTYPE_CONNECTED\x10\x00\x12\x16\n\x12TYPE_NOT_CONNECTED\x10\x01\x12\x14\n\x10TYPE_NO_AP_FOUND\x10\x02\x12\x18\n\x14TYPE_CONNECTION_FAIL\x10\x03*\x83\x07\n\x16\x45spHostedConfigMsgType\x12\x18\n\x14TypeCmdGetMACAddress\x10\x00\x12\x19\n\x15TypeRespGetMACAddress\x10\x01\x12\x16\n\x12TypeCmdGetWiFiMode\x10\x02\x12\x17\n\x13TypeRespGetWiFiMode\x10\x03\x12\x16\n\x12TypeCmdSetWiFiMode\x10\x04\x12\x17\n\x13TypeRespSetWiFiMode\x10\x05\x12\x16\n\x12TypeCmdGetAPConfig\x10\x06\x12\x17\n\x13TypeRespGetAPConfig\x10\x07\x12\x16\n\x12TypeCmdSetAPConfig\x10\x08\x12\x17\n\x13TypeRespSetAPConfig\x10\t\x12\x1a\n\x16TypeCmdGetSoftAPConfig\x10\n\x12\x1b\n\x17TypeRespGetSoftAPConfig\x10\x0b\x12\x1a\n\x16TypeCmdSetSoftAPConfig\x10\x0c\x12\x1b\n\x17TypeRespSetSoftAPConfig\x10\r\x12\x17\n\x13TypeCmdDisconnectAP\x10\x0e\x12\x18\n\x14TypeRespDisconnectAP\x10\x0f\x12\x15\n\x11TypeCmdStopSoftAP\x10\x10\x12\x16\n\x12TypeRespStopSoftAP\x10\x11\x12\x18\n\x14TypeCmdGetAPScanList\x10\x12\x12\x19\n\x15TypeRespGetAPScanList\x10\x13\x12\x1e\n\x1aTypeCmdGetConnectedSTAList\x10\x14\x12\x1f\n\x1bTypeRespGetConnectedSTAList\x10\x15\x12\x18\n\x14TypeCmdSetMacAddress\x10\x16\x12\x19\n\x15TypeRespSetMacAddress\x10\x17\x12\x1b\n\x17TypeCmdSetPowerSaveMode\x10\x18\x12\x1c\n\x18TypeRespSetPowerSaveMode\x10\x19\x12\x1b\n\x17TypeCmdGetPowerSaveMode\x10\x1a\x12\x1c\n\x18TypeRespGetPowerSaveMode\x10\x1b\x12\x13\n\x0fTypeCmdOTABegin\x10\x1c\x12\x14\n\x10TypeRespOTABegin\x10\x1d\x12\x13\n\x0fTypeCmdOTAWrite\x10\x1e\x12\x14\n\x10TypeRespOTAWrite\x10\x1f\x12\x11\n\rTypeCmdOTAEnd\x10 \x12\x12\n\x0eTypeRespOTAEnd\x10!b\x06proto3' -) - -_ESPHOSTEDENCRYPTIONMODE = _descriptor.EnumDescriptor( - name='EspHostedEncryptionMode', - full_name='EspHostedEncryptionMode', - filename=None, - file=DESCRIPTOR, - create_key=_descriptor._internal_create_key, - values=[ - _descriptor.EnumValueDescriptor( - name='Type_Open', index=0, number=0, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - _descriptor.EnumValueDescriptor( - name='Type_WEP', index=1, number=1, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - _descriptor.EnumValueDescriptor( - name='Type_WPA_PSK', index=2, number=2, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - _descriptor.EnumValueDescriptor( - name='Type_WPA2_PSK', index=3, number=3, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - _descriptor.EnumValueDescriptor( - name='Type_WPA_WPA2_PSK', index=4, number=4, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - _descriptor.EnumValueDescriptor( - name='Type_WPA2_ENTERPRISE', index=5, number=5, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - _descriptor.EnumValueDescriptor( - name='Type_WPA3_PSK', index=6, number=6, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - _descriptor.EnumValueDescriptor( - name='Type_WPA2_WPA3_PSK', index=7, number=7, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - ], - containing_type=None, - serialized_options=None, - serialized_start=3787, - serialized_end=3970, -) -_sym_db.RegisterEnumDescriptor(_ESPHOSTEDENCRYPTIONMODE) - -EspHostedEncryptionMode = enum_type_wrapper.EnumTypeWrapper(_ESPHOSTEDENCRYPTIONMODE) -_ESPHOSTEDSTATUS = _descriptor.EnumDescriptor( - name='EspHostedStatus', - full_name='EspHostedStatus', - filename=None, - file=DESCRIPTOR, - create_key=_descriptor._internal_create_key, - values=[ - _descriptor.EnumValueDescriptor( - name='TYPE_CONNECTED', index=0, number=0, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - _descriptor.EnumValueDescriptor( - name='TYPE_NOT_CONNECTED', index=1, number=1, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - _descriptor.EnumValueDescriptor( - name='TYPE_NO_AP_FOUND', index=2, number=2, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - _descriptor.EnumValueDescriptor( - name='TYPE_CONNECTION_FAIL', index=3, number=3, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - ], - containing_type=None, - serialized_options=None, - serialized_start=3972, - serialized_end=4081, -) -_sym_db.RegisterEnumDescriptor(_ESPHOSTEDSTATUS) - -EspHostedStatus = enum_type_wrapper.EnumTypeWrapper(_ESPHOSTEDSTATUS) -_ESPHOSTEDCONFIGMSGTYPE = _descriptor.EnumDescriptor( - name='EspHostedConfigMsgType', - full_name='EspHostedConfigMsgType', - filename=None, - file=DESCRIPTOR, - create_key=_descriptor._internal_create_key, - values=[ - _descriptor.EnumValueDescriptor( - name='TypeCmdGetMACAddress', index=0, number=0, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - _descriptor.EnumValueDescriptor( - name='TypeRespGetMACAddress', index=1, number=1, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - _descriptor.EnumValueDescriptor( - name='TypeCmdGetWiFiMode', index=2, number=2, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - _descriptor.EnumValueDescriptor( - name='TypeRespGetWiFiMode', index=3, number=3, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - _descriptor.EnumValueDescriptor( - name='TypeCmdSetWiFiMode', index=4, number=4, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - _descriptor.EnumValueDescriptor( - name='TypeRespSetWiFiMode', index=5, number=5, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - _descriptor.EnumValueDescriptor( - name='TypeCmdGetAPConfig', index=6, number=6, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - _descriptor.EnumValueDescriptor( - name='TypeRespGetAPConfig', index=7, number=7, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - _descriptor.EnumValueDescriptor( - name='TypeCmdSetAPConfig', index=8, number=8, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - _descriptor.EnumValueDescriptor( - name='TypeRespSetAPConfig', index=9, number=9, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - _descriptor.EnumValueDescriptor( - name='TypeCmdGetSoftAPConfig', index=10, number=10, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - _descriptor.EnumValueDescriptor( - name='TypeRespGetSoftAPConfig', index=11, number=11, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - _descriptor.EnumValueDescriptor( - name='TypeCmdSetSoftAPConfig', index=12, number=12, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - _descriptor.EnumValueDescriptor( - name='TypeRespSetSoftAPConfig', index=13, number=13, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - _descriptor.EnumValueDescriptor( - name='TypeCmdDisconnectAP', index=14, number=14, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - _descriptor.EnumValueDescriptor( - name='TypeRespDisconnectAP', index=15, number=15, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - _descriptor.EnumValueDescriptor( - name='TypeCmdStopSoftAP', index=16, number=16, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - _descriptor.EnumValueDescriptor( - name='TypeRespStopSoftAP', index=17, number=17, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - _descriptor.EnumValueDescriptor( - name='TypeCmdGetAPScanList', index=18, number=18, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - _descriptor.EnumValueDescriptor( - name='TypeRespGetAPScanList', index=19, number=19, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - _descriptor.EnumValueDescriptor( - name='TypeCmdGetConnectedSTAList', index=20, number=20, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - _descriptor.EnumValueDescriptor( - name='TypeRespGetConnectedSTAList', index=21, number=21, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - _descriptor.EnumValueDescriptor( - name='TypeCmdSetMacAddress', index=22, number=22, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - _descriptor.EnumValueDescriptor( - name='TypeRespSetMacAddress', index=23, number=23, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - _descriptor.EnumValueDescriptor( - name='TypeCmdSetPowerSaveMode', index=24, number=24, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - _descriptor.EnumValueDescriptor( - name='TypeRespSetPowerSaveMode', index=25, number=25, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - _descriptor.EnumValueDescriptor( - name='TypeCmdGetPowerSaveMode', index=26, number=26, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - _descriptor.EnumValueDescriptor( - name='TypeRespGetPowerSaveMode', index=27, number=27, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - _descriptor.EnumValueDescriptor( - name='TypeCmdOTABegin', index=28, number=28, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - _descriptor.EnumValueDescriptor( - name='TypeRespOTABegin', index=29, number=29, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - _descriptor.EnumValueDescriptor( - name='TypeCmdOTAWrite', index=30, number=30, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - _descriptor.EnumValueDescriptor( - name='TypeRespOTAWrite', index=31, number=31, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - _descriptor.EnumValueDescriptor( - name='TypeCmdOTAEnd', index=32, number=32, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - _descriptor.EnumValueDescriptor( - name='TypeRespOTAEnd', index=33, number=33, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - ], - containing_type=None, - serialized_options=None, - serialized_start=4084, - serialized_end=4983, -) -_sym_db.RegisterEnumDescriptor(_ESPHOSTEDCONFIGMSGTYPE) - -EspHostedConfigMsgType = enum_type_wrapper.EnumTypeWrapper(_ESPHOSTEDCONFIGMSGTYPE) -Type_Open = 0 -Type_WEP = 1 -Type_WPA_PSK = 2 -Type_WPA2_PSK = 3 -Type_WPA_WPA2_PSK = 4 -Type_WPA2_ENTERPRISE = 5 -Type_WPA3_PSK = 6 -Type_WPA2_WPA3_PSK = 7 -TYPE_CONNECTED = 0 -TYPE_NOT_CONNECTED = 1 -TYPE_NO_AP_FOUND = 2 -TYPE_CONNECTION_FAIL = 3 -TypeCmdGetMACAddress = 0 -TypeRespGetMACAddress = 1 -TypeCmdGetWiFiMode = 2 -TypeRespGetWiFiMode = 3 -TypeCmdSetWiFiMode = 4 -TypeRespSetWiFiMode = 5 -TypeCmdGetAPConfig = 6 -TypeRespGetAPConfig = 7 -TypeCmdSetAPConfig = 8 -TypeRespSetAPConfig = 9 -TypeCmdGetSoftAPConfig = 10 -TypeRespGetSoftAPConfig = 11 -TypeCmdSetSoftAPConfig = 12 -TypeRespSetSoftAPConfig = 13 -TypeCmdDisconnectAP = 14 -TypeRespDisconnectAP = 15 -TypeCmdStopSoftAP = 16 -TypeRespStopSoftAP = 17 -TypeCmdGetAPScanList = 18 -TypeRespGetAPScanList = 19 -TypeCmdGetConnectedSTAList = 20 -TypeRespGetConnectedSTAList = 21 -TypeCmdSetMacAddress = 22 -TypeRespSetMacAddress = 23 -TypeCmdSetPowerSaveMode = 24 -TypeRespSetPowerSaveMode = 25 -TypeCmdGetPowerSaveMode = 26 -TypeRespGetPowerSaveMode = 27 -TypeCmdOTABegin = 28 -TypeRespOTABegin = 29 -TypeCmdOTAWrite = 30 -TypeRespOTAWrite = 31 -TypeCmdOTAEnd = 32 -TypeRespOTAEnd = 33 - - - -_ESPHOSTEDCMDGETMACADDRESS = _descriptor.Descriptor( - name='EspHostedCmdGetMacAddress', - full_name='EspHostedCmdGetMacAddress', - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name='mode', full_name='EspHostedCmdGetMacAddress.mode', index=0, - number=1, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=27, - serialized_end=68, -) - - -_ESPHOSTEDRESPGETMACADDRESS = _descriptor.Descriptor( - name='EspHostedRespGetMacAddress', - full_name='EspHostedRespGetMacAddress', - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name='mac', full_name='EspHostedRespGetMacAddress.mac', index=0, - number=1, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=b"", - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='resp', full_name='EspHostedRespGetMacAddress.resp', index=1, - number=2, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=70, - serialized_end=125, -) - - -_ESPHOSTEDCMDGETMODE = _descriptor.Descriptor( - name='EspHostedCmdGetMode', - full_name='EspHostedCmdGetMode', - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=127, - serialized_end=148, -) - - -_ESPHOSTEDRESPGETMODE = _descriptor.Descriptor( - name='EspHostedRespGetMode', - full_name='EspHostedRespGetMode', - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name='mode', full_name='EspHostedRespGetMode.mode', index=0, - number=1, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='resp', full_name='EspHostedRespGetMode.resp', index=1, - number=2, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=150, - serialized_end=200, -) - - -_ESPHOSTEDCMDSETMODE = _descriptor.Descriptor( - name='EspHostedCmdSetMode', - full_name='EspHostedCmdSetMode', - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name='mode', full_name='EspHostedCmdSetMode.mode', index=0, - number=1, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=202, - serialized_end=237, -) - - -_ESPHOSTEDRESPSETMODE = _descriptor.Descriptor( - name='EspHostedRespSetMode', - full_name='EspHostedRespSetMode', - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name='resp', full_name='EspHostedRespSetMode.resp', index=0, - number=1, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=239, - serialized_end=275, -) - - -_ESPHOSTEDCMDGETSTATUS = _descriptor.Descriptor( - name='EspHostedCmdGetStatus', - full_name='EspHostedCmdGetStatus', - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=277, - serialized_end=300, -) - - -_ESPHOSTEDRESPGETSTATUS = _descriptor.Descriptor( - name='EspHostedRespGetStatus', - full_name='EspHostedRespGetStatus', - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name='resp', full_name='EspHostedRespGetStatus.resp', index=0, - number=1, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=302, - serialized_end=340, -) - - -_ESPHOSTEDCMDSETMACADDRESS = _descriptor.Descriptor( - name='EspHostedCmdSetMacAddress', - full_name='EspHostedCmdSetMacAddress', - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name='mac', full_name='EspHostedCmdSetMacAddress.mac', index=0, - number=1, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=b"", - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='mode', full_name='EspHostedCmdSetMacAddress.mode', index=1, - number=2, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=342, - serialized_end=396, -) - - -_ESPHOSTEDRESPSETMACADDRESS = _descriptor.Descriptor( - name='EspHostedRespSetMacAddress', - full_name='EspHostedRespSetMacAddress', - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name='resp', full_name='EspHostedRespSetMacAddress.resp', index=0, - number=1, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=398, - serialized_end=440, -) - - -_ESPHOSTEDCMDGETAPCONFIG = _descriptor.Descriptor( - name='EspHostedCmdGetAPConfig', - full_name='EspHostedCmdGetAPConfig', - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=442, - serialized_end=467, -) - - -_ESPHOSTEDRESPGETAPCONFIG = _descriptor.Descriptor( - name='EspHostedRespGetAPConfig', - full_name='EspHostedRespGetAPConfig', - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name='ssid', full_name='EspHostedRespGetAPConfig.ssid', index=0, - number=1, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=b"", - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='bssid', full_name='EspHostedRespGetAPConfig.bssid', index=1, - number=2, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=b"", - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='rssi', full_name='EspHostedRespGetAPConfig.rssi', index=2, - number=3, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='chnl', full_name='EspHostedRespGetAPConfig.chnl', index=3, - number=4, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='ecn', full_name='EspHostedRespGetAPConfig.ecn', index=4, - number=5, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='resp', full_name='EspHostedRespGetAPConfig.resp', index=5, - number=6, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=470, - serialized_end=606, -) - - -_ESPHOSTEDCMDSETAPCONFIG = _descriptor.Descriptor( - name='EspHostedCmdSetAPConfig', - full_name='EspHostedCmdSetAPConfig', - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name='ssid', full_name='EspHostedCmdSetAPConfig.ssid', index=0, - number=1, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=b"".decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='pwd', full_name='EspHostedCmdSetAPConfig.pwd', index=1, - number=2, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=b"".decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='bssid', full_name='EspHostedCmdSetAPConfig.bssid', index=2, - number=3, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=b"".decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='is_wpa3_supported', full_name='EspHostedCmdSetAPConfig.is_wpa3_supported', index=3, - number=4, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='listen_interval', full_name='EspHostedCmdSetAPConfig.listen_interval', index=4, - number=5, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=608, - serialized_end=727, -) - - -_ESPHOSTEDRESPSETAPCONFIG = _descriptor.Descriptor( - name='EspHostedRespSetAPConfig', - full_name='EspHostedRespSetAPConfig', - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name='resp', full_name='EspHostedRespSetAPConfig.resp', index=0, - number=1, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=729, - serialized_end=769, -) - - -_ESPHOSTEDCMDGETSOFTAPCONFIG = _descriptor.Descriptor( - name='EspHostedCmdGetSoftAPConfig', - full_name='EspHostedCmdGetSoftAPConfig', - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=771, - serialized_end=800, -) - - -_ESPHOSTEDRESPGETSOFTAPCONFIG = _descriptor.Descriptor( - name='EspHostedRespGetSoftAPConfig', - full_name='EspHostedRespGetSoftAPConfig', - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name='ssid', full_name='EspHostedRespGetSoftAPConfig.ssid', index=0, - number=1, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=b"", - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='pwd', full_name='EspHostedRespGetSoftAPConfig.pwd', index=1, - number=2, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=b"", - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='chnl', full_name='EspHostedRespGetSoftAPConfig.chnl', index=2, - number=3, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='ecn', full_name='EspHostedRespGetSoftAPConfig.ecn', index=3, - number=4, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='max_conn', full_name='EspHostedRespGetSoftAPConfig.max_conn', index=4, - number=5, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='ssid_hidden', full_name='EspHostedRespGetSoftAPConfig.ssid_hidden', index=5, - number=6, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='bw', full_name='EspHostedRespGetSoftAPConfig.bw', index=6, - number=7, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='resp', full_name='EspHostedRespGetSoftAPConfig.resp', index=7, - number=8, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=803, - serialized_end=978, -) - - -_ESPHOSTEDCMDSETSOFTAPCONFIG = _descriptor.Descriptor( - name='EspHostedCmdSetSoftAPConfig', - full_name='EspHostedCmdSetSoftAPConfig', - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name='ssid', full_name='EspHostedCmdSetSoftAPConfig.ssid', index=0, - number=1, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=b"".decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='pwd', full_name='EspHostedCmdSetSoftAPConfig.pwd', index=1, - number=2, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=b"".decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='chnl', full_name='EspHostedCmdSetSoftAPConfig.chnl', index=2, - number=3, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='ecn', full_name='EspHostedCmdSetSoftAPConfig.ecn', index=3, - number=4, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='max_conn', full_name='EspHostedCmdSetSoftAPConfig.max_conn', index=4, - number=5, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='ssid_hidden', full_name='EspHostedCmdSetSoftAPConfig.ssid_hidden', index=5, - number=6, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='bw', full_name='EspHostedCmdSetSoftAPConfig.bw', index=6, - number=7, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=981, - serialized_end=1141, -) - - -_ESPHOSTEDRESPSETSOFTAPCONFIG = _descriptor.Descriptor( - name='EspHostedRespSetSoftAPConfig', - full_name='EspHostedRespSetSoftAPConfig', - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name='resp', full_name='EspHostedRespSetSoftAPConfig.resp', index=0, - number=1, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1143, - serialized_end=1187, -) - - -_ESPHOSTEDSCANRESULT = _descriptor.Descriptor( - name='EspHostedScanResult', - full_name='EspHostedScanResult', - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name='ssid', full_name='EspHostedScanResult.ssid', index=0, - number=1, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=b"", - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='chnl', full_name='EspHostedScanResult.chnl', index=1, - number=2, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='rssi', full_name='EspHostedScanResult.rssi', index=2, - number=3, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='bssid', full_name='EspHostedScanResult.bssid', index=3, - number=4, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=b"", - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='ecn', full_name='EspHostedScanResult.ecn', index=4, - number=5, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1189, - serialized_end=1306, -) - - -_ESPHOSTEDCMDSCANRESULT = _descriptor.Descriptor( - name='EspHostedCmdScanResult', - full_name='EspHostedCmdScanResult', - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1308, - serialized_end=1332, -) - - -_ESPHOSTEDRESPSCANRESULT = _descriptor.Descriptor( - name='EspHostedRespScanResult', - full_name='EspHostedRespScanResult', - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name='count', full_name='EspHostedRespScanResult.count', index=0, - number=1, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='entries', full_name='EspHostedRespScanResult.entries', index=1, - number=2, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='resp', full_name='EspHostedRespScanResult.resp', index=2, - number=3, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1334, - serialized_end=1427, -) - - -_ESPHOSTEDCONNECTEDSTALIST = _descriptor.Descriptor( - name='EspHostedConnectedSTAList', - full_name='EspHostedConnectedSTAList', - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name='mac', full_name='EspHostedConnectedSTAList.mac', index=0, - number=1, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=b"", - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='rssi', full_name='EspHostedConnectedSTAList.rssi', index=1, - number=2, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1429, - serialized_end=1483, -) - - -_ESPHOSTEDCMDCONNECTEDSTA = _descriptor.Descriptor( - name='EspHostedCmdConnectedSTA', - full_name='EspHostedCmdConnectedSTA', - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1485, - serialized_end=1511, -) - - -_ESPHOSTEDRESPCONNECTEDSTA = _descriptor.Descriptor( - name='EspHostedRespConnectedSTA', - full_name='EspHostedRespConnectedSTA', - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name='num', full_name='EspHostedRespConnectedSTA.num', index=0, - number=1, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='stations', full_name='EspHostedRespConnectedSTA.stations', index=1, - number=2, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='resp', full_name='EspHostedRespConnectedSTA.resp', index=2, - number=3, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1513, - serialized_end=1613, -) - - -_ESPHOSTEDCMDOTABEGIN = _descriptor.Descriptor( - name='EspHostedCmdOTABegin', - full_name='EspHostedCmdOTABegin', - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1615, - serialized_end=1637, -) - - -_ESPHOSTEDRESPOTABEGIN = _descriptor.Descriptor( - name='EspHostedRespOTABegin', - full_name='EspHostedRespOTABegin', - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name='resp', full_name='EspHostedRespOTABegin.resp', index=0, - number=1, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1639, - serialized_end=1676, -) - - -_ESPHOSTEDCMDOTAWRITE = _descriptor.Descriptor( - name='EspHostedCmdOTAWrite', - full_name='EspHostedCmdOTAWrite', - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name='ota_data', full_name='EspHostedCmdOTAWrite.ota_data', index=0, - number=1, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=b"", - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1678, - serialized_end=1718, -) - - -_ESPHOSTEDRESPOTAWRITE = _descriptor.Descriptor( - name='EspHostedRespOTAWrite', - full_name='EspHostedRespOTAWrite', - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name='resp', full_name='EspHostedRespOTAWrite.resp', index=0, - number=1, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1720, - serialized_end=1757, -) - - -_ESPHOSTEDCMDOTAEND = _descriptor.Descriptor( - name='EspHostedCmdOTAEnd', - full_name='EspHostedCmdOTAEnd', - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1759, - serialized_end=1779, -) - - -_ESPHOSTEDRESPOTAEND = _descriptor.Descriptor( - name='EspHostedRespOTAEnd', - full_name='EspHostedRespOTAEnd', - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name='resp', full_name='EspHostedRespOTAEnd.resp', index=0, - number=1, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1781, - serialized_end=1816, -) - - -_ESPHOSTEDCONFIGPAYLOAD = _descriptor.Descriptor( - name='EspHostedConfigPayload', - full_name='EspHostedConfigPayload', - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name='msg', full_name='EspHostedConfigPayload.msg', index=0, - number=1, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='cmd_get_mac_address', full_name='EspHostedConfigPayload.cmd_get_mac_address', index=1, - number=10, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='resp_get_mac_address', full_name='EspHostedConfigPayload.resp_get_mac_address', index=2, - number=11, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='cmd_get_wifi_mode', full_name='EspHostedConfigPayload.cmd_get_wifi_mode', index=3, - number=12, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='resp_get_wifi_mode', full_name='EspHostedConfigPayload.resp_get_wifi_mode', index=4, - number=13, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='cmd_set_wifi_mode', full_name='EspHostedConfigPayload.cmd_set_wifi_mode', index=5, - number=14, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='resp_set_wifi_mode', full_name='EspHostedConfigPayload.resp_set_wifi_mode', index=6, - number=15, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='cmd_get_ap_config', full_name='EspHostedConfigPayload.cmd_get_ap_config', index=7, - number=16, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='resp_get_ap_config', full_name='EspHostedConfigPayload.resp_get_ap_config', index=8, - number=17, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='cmd_set_ap_config', full_name='EspHostedConfigPayload.cmd_set_ap_config', index=9, - number=18, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='resp_set_ap_config', full_name='EspHostedConfigPayload.resp_set_ap_config', index=10, - number=19, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='cmd_get_softap_config', full_name='EspHostedConfigPayload.cmd_get_softap_config', index=11, - number=20, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='resp_get_softap_config', full_name='EspHostedConfigPayload.resp_get_softap_config', index=12, - number=21, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='cmd_set_softap_config', full_name='EspHostedConfigPayload.cmd_set_softap_config', index=13, - number=22, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='resp_set_softap_config', full_name='EspHostedConfigPayload.resp_set_softap_config', index=14, - number=23, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='cmd_disconnect_ap', full_name='EspHostedConfigPayload.cmd_disconnect_ap', index=15, - number=24, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='resp_disconnect_ap', full_name='EspHostedConfigPayload.resp_disconnect_ap', index=16, - number=25, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='cmd_stop_softap', full_name='EspHostedConfigPayload.cmd_stop_softap', index=17, - number=26, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='resp_stop_softap', full_name='EspHostedConfigPayload.resp_stop_softap', index=18, - number=27, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='cmd_scan_ap_list', full_name='EspHostedConfigPayload.cmd_scan_ap_list', index=19, - number=28, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='resp_scan_ap_list', full_name='EspHostedConfigPayload.resp_scan_ap_list', index=20, - number=29, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='cmd_connected_stas_list', full_name='EspHostedConfigPayload.cmd_connected_stas_list', index=21, - number=30, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='resp_connected_stas_list', full_name='EspHostedConfigPayload.resp_connected_stas_list', index=22, - number=31, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='cmd_set_mac_address', full_name='EspHostedConfigPayload.cmd_set_mac_address', index=23, - number=32, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='resp_set_mac_address', full_name='EspHostedConfigPayload.resp_set_mac_address', index=24, - number=33, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='cmd_set_power_save_mode', full_name='EspHostedConfigPayload.cmd_set_power_save_mode', index=25, - number=34, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='resp_set_power_save_mode', full_name='EspHostedConfigPayload.resp_set_power_save_mode', index=26, - number=35, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='cmd_get_power_save_mode', full_name='EspHostedConfigPayload.cmd_get_power_save_mode', index=27, - number=36, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='resp_get_power_save_mode', full_name='EspHostedConfigPayload.resp_get_power_save_mode', index=28, - number=37, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='cmd_ota_begin', full_name='EspHostedConfigPayload.cmd_ota_begin', index=29, - number=38, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='resp_ota_begin', full_name='EspHostedConfigPayload.resp_ota_begin', index=30, - number=39, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='cmd_ota_write', full_name='EspHostedConfigPayload.cmd_ota_write', index=31, - number=40, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='resp_ota_write', full_name='EspHostedConfigPayload.resp_ota_write', index=32, - number=41, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='cmd_ota_end', full_name='EspHostedConfigPayload.cmd_ota_end', index=33, - number=42, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - _descriptor.FieldDescriptor( - name='resp_ota_end', full_name='EspHostedConfigPayload.resp_ota_end', index=34, - number=43, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - _descriptor.OneofDescriptor( - name='payload', full_name='EspHostedConfigPayload.payload', - index=0, containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[]), - ], - serialized_start=1819, - serialized_end=3784, -) - -_ESPHOSTEDRESPGETAPCONFIG.fields_by_name['ecn'].enum_type = _ESPHOSTEDENCRYPTIONMODE -_ESPHOSTEDRESPGETSOFTAPCONFIG.fields_by_name['ecn'].enum_type = _ESPHOSTEDENCRYPTIONMODE -_ESPHOSTEDCMDSETSOFTAPCONFIG.fields_by_name['ecn'].enum_type = _ESPHOSTEDENCRYPTIONMODE -_ESPHOSTEDSCANRESULT.fields_by_name['ecn'].enum_type = _ESPHOSTEDENCRYPTIONMODE -_ESPHOSTEDRESPSCANRESULT.fields_by_name['entries'].message_type = _ESPHOSTEDSCANRESULT -_ESPHOSTEDRESPCONNECTEDSTA.fields_by_name['stations'].message_type = _ESPHOSTEDCONNECTEDSTALIST -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['msg'].enum_type = _ESPHOSTEDCONFIGMSGTYPE -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_get_mac_address'].message_type = _ESPHOSTEDCMDGETMACADDRESS -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_get_mac_address'].message_type = _ESPHOSTEDRESPGETMACADDRESS -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_get_wifi_mode'].message_type = _ESPHOSTEDCMDGETMODE -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_get_wifi_mode'].message_type = _ESPHOSTEDRESPGETMODE -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_set_wifi_mode'].message_type = _ESPHOSTEDCMDSETMODE -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_set_wifi_mode'].message_type = _ESPHOSTEDRESPSETMODE -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_get_ap_config'].message_type = _ESPHOSTEDCMDGETAPCONFIG -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_get_ap_config'].message_type = _ESPHOSTEDRESPGETAPCONFIG -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_set_ap_config'].message_type = _ESPHOSTEDCMDSETAPCONFIG -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_set_ap_config'].message_type = _ESPHOSTEDRESPSETAPCONFIG -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_get_softap_config'].message_type = _ESPHOSTEDCMDGETSOFTAPCONFIG -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_get_softap_config'].message_type = _ESPHOSTEDRESPGETSOFTAPCONFIG -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_set_softap_config'].message_type = _ESPHOSTEDCMDSETSOFTAPCONFIG -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_set_softap_config'].message_type = _ESPHOSTEDRESPSETSOFTAPCONFIG -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_disconnect_ap'].message_type = _ESPHOSTEDCMDGETSTATUS -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_disconnect_ap'].message_type = _ESPHOSTEDRESPGETSTATUS -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_stop_softap'].message_type = _ESPHOSTEDCMDGETSTATUS -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_stop_softap'].message_type = _ESPHOSTEDRESPGETSTATUS -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_scan_ap_list'].message_type = _ESPHOSTEDCMDSCANRESULT -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_scan_ap_list'].message_type = _ESPHOSTEDRESPSCANRESULT -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_connected_stas_list'].message_type = _ESPHOSTEDCMDCONNECTEDSTA -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_connected_stas_list'].message_type = _ESPHOSTEDRESPCONNECTEDSTA -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_set_mac_address'].message_type = _ESPHOSTEDCMDSETMACADDRESS -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_set_mac_address'].message_type = _ESPHOSTEDRESPSETMACADDRESS -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_set_power_save_mode'].message_type = _ESPHOSTEDCMDSETMODE -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_set_power_save_mode'].message_type = _ESPHOSTEDRESPSETMODE -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_get_power_save_mode'].message_type = _ESPHOSTEDCMDGETMODE -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_get_power_save_mode'].message_type = _ESPHOSTEDRESPGETMODE -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_ota_begin'].message_type = _ESPHOSTEDCMDOTABEGIN -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_ota_begin'].message_type = _ESPHOSTEDRESPOTABEGIN -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_ota_write'].message_type = _ESPHOSTEDCMDOTAWRITE -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_ota_write'].message_type = _ESPHOSTEDRESPOTAWRITE -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_ota_end'].message_type = _ESPHOSTEDCMDOTAEND -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_ota_end'].message_type = _ESPHOSTEDRESPOTAEND -_ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'].fields.append( - _ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_get_mac_address']) -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_get_mac_address'].containing_oneof = _ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'] -_ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'].fields.append( - _ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_get_mac_address']) -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_get_mac_address'].containing_oneof = _ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'] -_ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'].fields.append( - _ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_get_wifi_mode']) -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_get_wifi_mode'].containing_oneof = _ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'] -_ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'].fields.append( - _ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_get_wifi_mode']) -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_get_wifi_mode'].containing_oneof = _ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'] -_ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'].fields.append( - _ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_set_wifi_mode']) -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_set_wifi_mode'].containing_oneof = _ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'] -_ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'].fields.append( - _ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_set_wifi_mode']) -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_set_wifi_mode'].containing_oneof = _ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'] -_ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'].fields.append( - _ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_get_ap_config']) -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_get_ap_config'].containing_oneof = _ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'] -_ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'].fields.append( - _ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_get_ap_config']) -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_get_ap_config'].containing_oneof = _ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'] -_ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'].fields.append( - _ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_set_ap_config']) -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_set_ap_config'].containing_oneof = _ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'] -_ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'].fields.append( - _ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_set_ap_config']) -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_set_ap_config'].containing_oneof = _ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'] -_ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'].fields.append( - _ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_get_softap_config']) -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_get_softap_config'].containing_oneof = _ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'] -_ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'].fields.append( - _ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_get_softap_config']) -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_get_softap_config'].containing_oneof = _ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'] -_ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'].fields.append( - _ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_set_softap_config']) -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_set_softap_config'].containing_oneof = _ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'] -_ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'].fields.append( - _ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_set_softap_config']) -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_set_softap_config'].containing_oneof = _ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'] -_ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'].fields.append( - _ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_disconnect_ap']) -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_disconnect_ap'].containing_oneof = _ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'] -_ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'].fields.append( - _ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_disconnect_ap']) -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_disconnect_ap'].containing_oneof = _ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'] -_ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'].fields.append( - _ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_stop_softap']) -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_stop_softap'].containing_oneof = _ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'] -_ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'].fields.append( - _ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_stop_softap']) -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_stop_softap'].containing_oneof = _ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'] -_ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'].fields.append( - _ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_scan_ap_list']) -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_scan_ap_list'].containing_oneof = _ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'] -_ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'].fields.append( - _ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_scan_ap_list']) -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_scan_ap_list'].containing_oneof = _ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'] -_ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'].fields.append( - _ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_connected_stas_list']) -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_connected_stas_list'].containing_oneof = _ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'] -_ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'].fields.append( - _ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_connected_stas_list']) -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_connected_stas_list'].containing_oneof = _ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'] -_ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'].fields.append( - _ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_set_mac_address']) -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_set_mac_address'].containing_oneof = _ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'] -_ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'].fields.append( - _ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_set_mac_address']) -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_set_mac_address'].containing_oneof = _ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'] -_ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'].fields.append( - _ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_set_power_save_mode']) -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_set_power_save_mode'].containing_oneof = _ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'] -_ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'].fields.append( - _ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_set_power_save_mode']) -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_set_power_save_mode'].containing_oneof = _ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'] -_ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'].fields.append( - _ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_get_power_save_mode']) -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_get_power_save_mode'].containing_oneof = _ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'] -_ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'].fields.append( - _ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_get_power_save_mode']) -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_get_power_save_mode'].containing_oneof = _ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'] -_ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'].fields.append( - _ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_ota_begin']) -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_ota_begin'].containing_oneof = _ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'] -_ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'].fields.append( - _ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_ota_begin']) -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_ota_begin'].containing_oneof = _ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'] -_ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'].fields.append( - _ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_ota_write']) -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_ota_write'].containing_oneof = _ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'] -_ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'].fields.append( - _ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_ota_write']) -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_ota_write'].containing_oneof = _ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'] -_ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'].fields.append( - _ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_ota_end']) -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['cmd_ota_end'].containing_oneof = _ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'] -_ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'].fields.append( - _ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_ota_end']) -_ESPHOSTEDCONFIGPAYLOAD.fields_by_name['resp_ota_end'].containing_oneof = _ESPHOSTEDCONFIGPAYLOAD.oneofs_by_name['payload'] -DESCRIPTOR.message_types_by_name['EspHostedCmdGetMacAddress'] = _ESPHOSTEDCMDGETMACADDRESS -DESCRIPTOR.message_types_by_name['EspHostedRespGetMacAddress'] = _ESPHOSTEDRESPGETMACADDRESS -DESCRIPTOR.message_types_by_name['EspHostedCmdGetMode'] = _ESPHOSTEDCMDGETMODE -DESCRIPTOR.message_types_by_name['EspHostedRespGetMode'] = _ESPHOSTEDRESPGETMODE -DESCRIPTOR.message_types_by_name['EspHostedCmdSetMode'] = _ESPHOSTEDCMDSETMODE -DESCRIPTOR.message_types_by_name['EspHostedRespSetMode'] = _ESPHOSTEDRESPSETMODE -DESCRIPTOR.message_types_by_name['EspHostedCmdGetStatus'] = _ESPHOSTEDCMDGETSTATUS -DESCRIPTOR.message_types_by_name['EspHostedRespGetStatus'] = _ESPHOSTEDRESPGETSTATUS -DESCRIPTOR.message_types_by_name['EspHostedCmdSetMacAddress'] = _ESPHOSTEDCMDSETMACADDRESS -DESCRIPTOR.message_types_by_name['EspHostedRespSetMacAddress'] = _ESPHOSTEDRESPSETMACADDRESS -DESCRIPTOR.message_types_by_name['EspHostedCmdGetAPConfig'] = _ESPHOSTEDCMDGETAPCONFIG -DESCRIPTOR.message_types_by_name['EspHostedRespGetAPConfig'] = _ESPHOSTEDRESPGETAPCONFIG -DESCRIPTOR.message_types_by_name['EspHostedCmdSetAPConfig'] = _ESPHOSTEDCMDSETAPCONFIG -DESCRIPTOR.message_types_by_name['EspHostedRespSetAPConfig'] = _ESPHOSTEDRESPSETAPCONFIG -DESCRIPTOR.message_types_by_name['EspHostedCmdGetSoftAPConfig'] = _ESPHOSTEDCMDGETSOFTAPCONFIG -DESCRIPTOR.message_types_by_name['EspHostedRespGetSoftAPConfig'] = _ESPHOSTEDRESPGETSOFTAPCONFIG -DESCRIPTOR.message_types_by_name['EspHostedCmdSetSoftAPConfig'] = _ESPHOSTEDCMDSETSOFTAPCONFIG -DESCRIPTOR.message_types_by_name['EspHostedRespSetSoftAPConfig'] = _ESPHOSTEDRESPSETSOFTAPCONFIG -DESCRIPTOR.message_types_by_name['EspHostedScanResult'] = _ESPHOSTEDSCANRESULT -DESCRIPTOR.message_types_by_name['EspHostedCmdScanResult'] = _ESPHOSTEDCMDSCANRESULT -DESCRIPTOR.message_types_by_name['EspHostedRespScanResult'] = _ESPHOSTEDRESPSCANRESULT -DESCRIPTOR.message_types_by_name['EspHostedConnectedSTAList'] = _ESPHOSTEDCONNECTEDSTALIST -DESCRIPTOR.message_types_by_name['EspHostedCmdConnectedSTA'] = _ESPHOSTEDCMDCONNECTEDSTA -DESCRIPTOR.message_types_by_name['EspHostedRespConnectedSTA'] = _ESPHOSTEDRESPCONNECTEDSTA -DESCRIPTOR.message_types_by_name['EspHostedCmdOTABegin'] = _ESPHOSTEDCMDOTABEGIN -DESCRIPTOR.message_types_by_name['EspHostedRespOTABegin'] = _ESPHOSTEDRESPOTABEGIN -DESCRIPTOR.message_types_by_name['EspHostedCmdOTAWrite'] = _ESPHOSTEDCMDOTAWRITE -DESCRIPTOR.message_types_by_name['EspHostedRespOTAWrite'] = _ESPHOSTEDRESPOTAWRITE -DESCRIPTOR.message_types_by_name['EspHostedCmdOTAEnd'] = _ESPHOSTEDCMDOTAEND -DESCRIPTOR.message_types_by_name['EspHostedRespOTAEnd'] = _ESPHOSTEDRESPOTAEND -DESCRIPTOR.message_types_by_name['EspHostedConfigPayload'] = _ESPHOSTEDCONFIGPAYLOAD -DESCRIPTOR.enum_types_by_name['EspHostedEncryptionMode'] = _ESPHOSTEDENCRYPTIONMODE -DESCRIPTOR.enum_types_by_name['EspHostedStatus'] = _ESPHOSTEDSTATUS -DESCRIPTOR.enum_types_by_name['EspHostedConfigMsgType'] = _ESPHOSTEDCONFIGMSGTYPE -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - -EspHostedCmdGetMacAddress = _reflection.GeneratedProtocolMessageType('EspHostedCmdGetMacAddress', (_message.Message,), { - 'DESCRIPTOR' : _ESPHOSTEDCMDGETMACADDRESS, - '__module__' : 'esp_hosted_config_pb2' - # @@protoc_insertion_point(class_scope:EspHostedCmdGetMacAddress) - }) -_sym_db.RegisterMessage(EspHostedCmdGetMacAddress) - -EspHostedRespGetMacAddress = _reflection.GeneratedProtocolMessageType('EspHostedRespGetMacAddress', (_message.Message,), { - 'DESCRIPTOR' : _ESPHOSTEDRESPGETMACADDRESS, - '__module__' : 'esp_hosted_config_pb2' - # @@protoc_insertion_point(class_scope:EspHostedRespGetMacAddress) - }) -_sym_db.RegisterMessage(EspHostedRespGetMacAddress) - -EspHostedCmdGetMode = _reflection.GeneratedProtocolMessageType('EspHostedCmdGetMode', (_message.Message,), { - 'DESCRIPTOR' : _ESPHOSTEDCMDGETMODE, - '__module__' : 'esp_hosted_config_pb2' - # @@protoc_insertion_point(class_scope:EspHostedCmdGetMode) - }) -_sym_db.RegisterMessage(EspHostedCmdGetMode) - -EspHostedRespGetMode = _reflection.GeneratedProtocolMessageType('EspHostedRespGetMode', (_message.Message,), { - 'DESCRIPTOR' : _ESPHOSTEDRESPGETMODE, - '__module__' : 'esp_hosted_config_pb2' - # @@protoc_insertion_point(class_scope:EspHostedRespGetMode) - }) -_sym_db.RegisterMessage(EspHostedRespGetMode) - -EspHostedCmdSetMode = _reflection.GeneratedProtocolMessageType('EspHostedCmdSetMode', (_message.Message,), { - 'DESCRIPTOR' : _ESPHOSTEDCMDSETMODE, - '__module__' : 'esp_hosted_config_pb2' - # @@protoc_insertion_point(class_scope:EspHostedCmdSetMode) - }) -_sym_db.RegisterMessage(EspHostedCmdSetMode) - -EspHostedRespSetMode = _reflection.GeneratedProtocolMessageType('EspHostedRespSetMode', (_message.Message,), { - 'DESCRIPTOR' : _ESPHOSTEDRESPSETMODE, - '__module__' : 'esp_hosted_config_pb2' - # @@protoc_insertion_point(class_scope:EspHostedRespSetMode) - }) -_sym_db.RegisterMessage(EspHostedRespSetMode) - -EspHostedCmdGetStatus = _reflection.GeneratedProtocolMessageType('EspHostedCmdGetStatus', (_message.Message,), { - 'DESCRIPTOR' : _ESPHOSTEDCMDGETSTATUS, - '__module__' : 'esp_hosted_config_pb2' - # @@protoc_insertion_point(class_scope:EspHostedCmdGetStatus) - }) -_sym_db.RegisterMessage(EspHostedCmdGetStatus) - -EspHostedRespGetStatus = _reflection.GeneratedProtocolMessageType('EspHostedRespGetStatus', (_message.Message,), { - 'DESCRIPTOR' : _ESPHOSTEDRESPGETSTATUS, - '__module__' : 'esp_hosted_config_pb2' - # @@protoc_insertion_point(class_scope:EspHostedRespGetStatus) - }) -_sym_db.RegisterMessage(EspHostedRespGetStatus) - -EspHostedCmdSetMacAddress = _reflection.GeneratedProtocolMessageType('EspHostedCmdSetMacAddress', (_message.Message,), { - 'DESCRIPTOR' : _ESPHOSTEDCMDSETMACADDRESS, - '__module__' : 'esp_hosted_config_pb2' - # @@protoc_insertion_point(class_scope:EspHostedCmdSetMacAddress) - }) -_sym_db.RegisterMessage(EspHostedCmdSetMacAddress) - -EspHostedRespSetMacAddress = _reflection.GeneratedProtocolMessageType('EspHostedRespSetMacAddress', (_message.Message,), { - 'DESCRIPTOR' : _ESPHOSTEDRESPSETMACADDRESS, - '__module__' : 'esp_hosted_config_pb2' - # @@protoc_insertion_point(class_scope:EspHostedRespSetMacAddress) - }) -_sym_db.RegisterMessage(EspHostedRespSetMacAddress) - -EspHostedCmdGetAPConfig = _reflection.GeneratedProtocolMessageType('EspHostedCmdGetAPConfig', (_message.Message,), { - 'DESCRIPTOR' : _ESPHOSTEDCMDGETAPCONFIG, - '__module__' : 'esp_hosted_config_pb2' - # @@protoc_insertion_point(class_scope:EspHostedCmdGetAPConfig) - }) -_sym_db.RegisterMessage(EspHostedCmdGetAPConfig) - -EspHostedRespGetAPConfig = _reflection.GeneratedProtocolMessageType('EspHostedRespGetAPConfig', (_message.Message,), { - 'DESCRIPTOR' : _ESPHOSTEDRESPGETAPCONFIG, - '__module__' : 'esp_hosted_config_pb2' - # @@protoc_insertion_point(class_scope:EspHostedRespGetAPConfig) - }) -_sym_db.RegisterMessage(EspHostedRespGetAPConfig) - -EspHostedCmdSetAPConfig = _reflection.GeneratedProtocolMessageType('EspHostedCmdSetAPConfig', (_message.Message,), { - 'DESCRIPTOR' : _ESPHOSTEDCMDSETAPCONFIG, - '__module__' : 'esp_hosted_config_pb2' - # @@protoc_insertion_point(class_scope:EspHostedCmdSetAPConfig) - }) -_sym_db.RegisterMessage(EspHostedCmdSetAPConfig) - -EspHostedRespSetAPConfig = _reflection.GeneratedProtocolMessageType('EspHostedRespSetAPConfig', (_message.Message,), { - 'DESCRIPTOR' : _ESPHOSTEDRESPSETAPCONFIG, - '__module__' : 'esp_hosted_config_pb2' - # @@protoc_insertion_point(class_scope:EspHostedRespSetAPConfig) - }) -_sym_db.RegisterMessage(EspHostedRespSetAPConfig) - -EspHostedCmdGetSoftAPConfig = _reflection.GeneratedProtocolMessageType('EspHostedCmdGetSoftAPConfig', (_message.Message,), { - 'DESCRIPTOR' : _ESPHOSTEDCMDGETSOFTAPCONFIG, - '__module__' : 'esp_hosted_config_pb2' - # @@protoc_insertion_point(class_scope:EspHostedCmdGetSoftAPConfig) - }) -_sym_db.RegisterMessage(EspHostedCmdGetSoftAPConfig) - -EspHostedRespGetSoftAPConfig = _reflection.GeneratedProtocolMessageType('EspHostedRespGetSoftAPConfig', (_message.Message,), { - 'DESCRIPTOR' : _ESPHOSTEDRESPGETSOFTAPCONFIG, - '__module__' : 'esp_hosted_config_pb2' - # @@protoc_insertion_point(class_scope:EspHostedRespGetSoftAPConfig) - }) -_sym_db.RegisterMessage(EspHostedRespGetSoftAPConfig) - -EspHostedCmdSetSoftAPConfig = _reflection.GeneratedProtocolMessageType('EspHostedCmdSetSoftAPConfig', (_message.Message,), { - 'DESCRIPTOR' : _ESPHOSTEDCMDSETSOFTAPCONFIG, - '__module__' : 'esp_hosted_config_pb2' - # @@protoc_insertion_point(class_scope:EspHostedCmdSetSoftAPConfig) - }) -_sym_db.RegisterMessage(EspHostedCmdSetSoftAPConfig) - -EspHostedRespSetSoftAPConfig = _reflection.GeneratedProtocolMessageType('EspHostedRespSetSoftAPConfig', (_message.Message,), { - 'DESCRIPTOR' : _ESPHOSTEDRESPSETSOFTAPCONFIG, - '__module__' : 'esp_hosted_config_pb2' - # @@protoc_insertion_point(class_scope:EspHostedRespSetSoftAPConfig) - }) -_sym_db.RegisterMessage(EspHostedRespSetSoftAPConfig) - -EspHostedScanResult = _reflection.GeneratedProtocolMessageType('EspHostedScanResult', (_message.Message,), { - 'DESCRIPTOR' : _ESPHOSTEDSCANRESULT, - '__module__' : 'esp_hosted_config_pb2' - # @@protoc_insertion_point(class_scope:EspHostedScanResult) - }) -_sym_db.RegisterMessage(EspHostedScanResult) - -EspHostedCmdScanResult = _reflection.GeneratedProtocolMessageType('EspHostedCmdScanResult', (_message.Message,), { - 'DESCRIPTOR' : _ESPHOSTEDCMDSCANRESULT, - '__module__' : 'esp_hosted_config_pb2' - # @@protoc_insertion_point(class_scope:EspHostedCmdScanResult) - }) -_sym_db.RegisterMessage(EspHostedCmdScanResult) - -EspHostedRespScanResult = _reflection.GeneratedProtocolMessageType('EspHostedRespScanResult', (_message.Message,), { - 'DESCRIPTOR' : _ESPHOSTEDRESPSCANRESULT, - '__module__' : 'esp_hosted_config_pb2' - # @@protoc_insertion_point(class_scope:EspHostedRespScanResult) - }) -_sym_db.RegisterMessage(EspHostedRespScanResult) - -EspHostedConnectedSTAList = _reflection.GeneratedProtocolMessageType('EspHostedConnectedSTAList', (_message.Message,), { - 'DESCRIPTOR' : _ESPHOSTEDCONNECTEDSTALIST, - '__module__' : 'esp_hosted_config_pb2' - # @@protoc_insertion_point(class_scope:EspHostedConnectedSTAList) - }) -_sym_db.RegisterMessage(EspHostedConnectedSTAList) - -EspHostedCmdConnectedSTA = _reflection.GeneratedProtocolMessageType('EspHostedCmdConnectedSTA', (_message.Message,), { - 'DESCRIPTOR' : _ESPHOSTEDCMDCONNECTEDSTA, - '__module__' : 'esp_hosted_config_pb2' - # @@protoc_insertion_point(class_scope:EspHostedCmdConnectedSTA) - }) -_sym_db.RegisterMessage(EspHostedCmdConnectedSTA) - -EspHostedRespConnectedSTA = _reflection.GeneratedProtocolMessageType('EspHostedRespConnectedSTA', (_message.Message,), { - 'DESCRIPTOR' : _ESPHOSTEDRESPCONNECTEDSTA, - '__module__' : 'esp_hosted_config_pb2' - # @@protoc_insertion_point(class_scope:EspHostedRespConnectedSTA) - }) -_sym_db.RegisterMessage(EspHostedRespConnectedSTA) - -EspHostedCmdOTABegin = _reflection.GeneratedProtocolMessageType('EspHostedCmdOTABegin', (_message.Message,), { - 'DESCRIPTOR' : _ESPHOSTEDCMDOTABEGIN, - '__module__' : 'esp_hosted_config_pb2' - # @@protoc_insertion_point(class_scope:EspHostedCmdOTABegin) - }) -_sym_db.RegisterMessage(EspHostedCmdOTABegin) - -EspHostedRespOTABegin = _reflection.GeneratedProtocolMessageType('EspHostedRespOTABegin', (_message.Message,), { - 'DESCRIPTOR' : _ESPHOSTEDRESPOTABEGIN, - '__module__' : 'esp_hosted_config_pb2' - # @@protoc_insertion_point(class_scope:EspHostedRespOTABegin) - }) -_sym_db.RegisterMessage(EspHostedRespOTABegin) - -EspHostedCmdOTAWrite = _reflection.GeneratedProtocolMessageType('EspHostedCmdOTAWrite', (_message.Message,), { - 'DESCRIPTOR' : _ESPHOSTEDCMDOTAWRITE, - '__module__' : 'esp_hosted_config_pb2' - # @@protoc_insertion_point(class_scope:EspHostedCmdOTAWrite) - }) -_sym_db.RegisterMessage(EspHostedCmdOTAWrite) - -EspHostedRespOTAWrite = _reflection.GeneratedProtocolMessageType('EspHostedRespOTAWrite', (_message.Message,), { - 'DESCRIPTOR' : _ESPHOSTEDRESPOTAWRITE, - '__module__' : 'esp_hosted_config_pb2' - # @@protoc_insertion_point(class_scope:EspHostedRespOTAWrite) - }) -_sym_db.RegisterMessage(EspHostedRespOTAWrite) - -EspHostedCmdOTAEnd = _reflection.GeneratedProtocolMessageType('EspHostedCmdOTAEnd', (_message.Message,), { - 'DESCRIPTOR' : _ESPHOSTEDCMDOTAEND, - '__module__' : 'esp_hosted_config_pb2' - # @@protoc_insertion_point(class_scope:EspHostedCmdOTAEnd) - }) -_sym_db.RegisterMessage(EspHostedCmdOTAEnd) - -EspHostedRespOTAEnd = _reflection.GeneratedProtocolMessageType('EspHostedRespOTAEnd', (_message.Message,), { - 'DESCRIPTOR' : _ESPHOSTEDRESPOTAEND, - '__module__' : 'esp_hosted_config_pb2' - # @@protoc_insertion_point(class_scope:EspHostedRespOTAEnd) - }) -_sym_db.RegisterMessage(EspHostedRespOTAEnd) - -EspHostedConfigPayload = _reflection.GeneratedProtocolMessageType('EspHostedConfigPayload', (_message.Message,), { - 'DESCRIPTOR' : _ESPHOSTEDCONFIGPAYLOAD, - '__module__' : 'esp_hosted_config_pb2' - # @@protoc_insertion_point(class_scope:EspHostedConfigPayload) - }) -_sym_db.RegisterMessage(EspHostedConfigPayload) - - -# @@protoc_insertion_point(module_scope) diff --git a/host/linux/host_control/python_support/hosted_config.py b/host/linux/host_control/python_support/hosted_config.py new file mode 100644 index 0000000000..5203cfbb31 --- /dev/null +++ b/host/linux/host_control/python_support/hosted_config.py @@ -0,0 +1,111 @@ +# Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from ctypes import * +import sys + +success = "success" +failure = "failure" + +SSID_LENGTH = 32 +PASSWORD_LENGTH = 64 +BSSID_LENGTH = 17 +STATUS_LENGTH = 14 + +MAX_SSID_LEN = 32 +MAX_PASSWORD_LEN = 64 +MIN_PASSWORD_LEN = 8 +MAX_BSSID_LEN = 17 +MIN_CHANNEL_NO = 1 +MAX_CHANNEL_NO = 11 +MIN_ALLOWED_STATIONS = 1 +MAX_ALLOWED_STATIONS = 10 +SSID_BROADCAST = 0 +SSID_NOT_BROADCAST = 1 + +(WIFI_MODE_NONE, WIFI_MODE_STATION, + WIFI_MODE_SOFTAP, WIFI_MODE_SOFTAP_STATION, + WIFI_MODE_MAX) = (0, 1, 2, 3, 4) + +(WIFI_AUTH_OPEN, WIFI_AUTH_WEP, + WIFI_AUTH_WPA_PSK, WIFI_AUTH_WPA2_PSK, + WIFI_AUTH_WPA_WPA2_PSK, WIFI_AUTH_WPA2_ENTERPRISE, + WIFI_AUTH_WPA3_PSK, WIFI_AUTH_WPA2_WPA3_PSK, + WIFI_AUTH_MAX) = (0, 1, 2, 3, 4, 5, 6, 7, 8) + +(WIFI_BW_HT20, WIFI_BW_HT40) = (1, 2) + +(WIFI_PS_MIN_MODEM, WIFI_PS_MAX_MODEM, + WIFI_PS_INVALID) = (1, 2, 3) + +class STA_CONFIG(Structure): + _fields_ = [("ssid", c_char * SSID_LENGTH), + ("pwd", c_char * PASSWORD_LENGTH), + ("bssid", c_char * BSSID_LENGTH), + ("is_wpa3_supported", c_bool), + ("rssi", c_int), + ("channel", c_uint), + ("encryption_mode", c_uint), + ("listen_interval", c_ushort), + ("status", c_char * STATUS_LENGTH)] + +class SOFTAP_CONFIG(Structure): + _fields_ = [("ssid", c_char * SSID_LENGTH), + ("pwd", c_char * PASSWORD_LENGTH), + ("channel", c_uint), + ("encryption_mode", c_uint), + ("max_connections", c_uint), + ("ssid_hidden", c_bool), + ("bandwidth", c_uint)] + +class CONTROL_CONFIG(Union): + _fields_ = [("station", STA_CONFIG), + ("softap", SOFTAP_CONFIG)] + + +class WIFI_SCAN_LIST(Structure): + _fields_ = [("ssid", c_char * SSID_LENGTH), + ("bssid", c_char * BSSID_LENGTH), + ("rssi", c_int), + ("channel", c_uint), + ("encryption_mode", c_uint)] + +class WIFI_STATIONS_LIST(Structure): + _fields_ = [("bssid", c_char * BSSID_LENGTH), + ("rssi", c_int)] + +class Aplist: + def __init__(self,ssid,chnl,rssi,bssid,ecn): + self.ssid = ssid + self.chnl = chnl + self.rssi = rssi + self.bssid = bssid + self.ecn = ecn + +class Stationlist: + def __init__(self,bssid,rssi): + self.bssid = bssid + self.rssi = rssi + +def get_str(string): + if sys.version_info >= (3, 0): + return string.decode('utf-8') + else: + return string + +def set_str(string): + if sys.version_info >= (3, 0): + return string.encode('utf-8') + else: + return string diff --git a/host/linux/host_control/python_support/ota_update.py b/host/linux/host_control/python_support/ota_update.py index 2415562193..de97f51dc7 100644 --- a/host/linux/host_control/python_support/ota_update.py +++ b/host/linux/host_control/python_support/ota_update.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from commands import * +from commands_lib import * import argparse import requests import os @@ -56,7 +56,7 @@ def OTA_END(): for chunk in response.iter_content(chunk_size): print("|", end="", flush=True) - ota_status = esp_ota_write(chunk) + ota_status = esp_ota_write(chunk, chunk_size) print(".", end="", flush=True) if (ota_status == "failure"): print("Failed to write OTA update") diff --git a/host/linux/host_control/python_support/softap_config.py b/host/linux/host_control/python_support/softap_config.py index 1e018e266f..f90cdc8ef4 100644 --- a/host/linux/host_control/python_support/softap_config.py +++ b/host/linux/host_control/python_support/softap_config.py @@ -12,24 +12,12 @@ # See the License for the specific language governing permissions and # limitations under the License. -from commands import * +from commands_lib import * import argparse import time import os from distutils.util import strtobool -# WiFi Mode -# NULL 0 -# Station 1 -# SoftAP 2 -# Station+softAP 3 - -wifi_mode_none = 0 -wifi_mode_station = 1 -wifi_mode_softap = 2 -wifi_mode_station_softap = 3 -success = 'success' -failure = 'failure' flag = success ap_mac = 'not_set' softap_config = 'not_set' @@ -52,7 +40,7 @@ args = parser.parse_args() -ap_mac = wifi_get_mac(wifi_mode_softap) +ap_mac = wifi_get_mac(WIFI_MODE_SOFTAP) if (ap_mac == failure): print("Failed to get softAP mac address") flag = failure diff --git a/host/linux/host_control/python_support/softap_stop.py b/host/linux/host_control/python_support/softap_stop.py index 4b0032346e..9988ac0016 100644 --- a/host/linux/host_control/python_support/softap_stop.py +++ b/host/linux/host_control/python_support/softap_stop.py @@ -12,22 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -from commands import * +from commands_lib import * import argparse import os -# WiFi Mode -# NULL 0 -# Station 1 -# softAP 2 -# Station + softAP 3 - -wifi_mode_none = 0 -wifi_mode_station = 1 -wifi_mode_softap = 2 -wifi_mode_station_softap = 3 -failure = "failure" -success = "success" wifi_mode = 'not_set' stop_softap = 'not_set' flag = success diff --git a/host/linux/host_control/python_support/station_connect.py b/host/linux/host_control/python_support/station_connect.py index 727b19aaef..90516f7edf 100644 --- a/host/linux/host_control/python_support/station_connect.py +++ b/host/linux/host_control/python_support/station_connect.py @@ -12,25 +12,13 @@ # See the License for the specific language governing permissions and # limitations under the License. -from commands import * +from commands_lib import * import argparse import time import os import subprocess from distutils.util import strtobool -# WiFi Mode -# NULL 0 -# Station 1 -# SoftAP 2 -# Station + softAP 3 - -wifi_mode_none = 0 -wifi_mode_station = 1 -wifi_mode_softap = 2 -wifi_mode_station_softap = 3 -failure = "failure" -success = "success" flag = success sta_mac = 'not_set' station_status = 'not_set' @@ -49,7 +37,7 @@ args = parser.parse_args() -sta_mac = wifi_get_mac(wifi_mode_station) +sta_mac = wifi_get_mac(WIFI_MODE_STATION) if (sta_mac == failure): flag = failure print("Failed to get station MAC address") diff --git a/host/linux/host_control/python_support/station_disconnect.py b/host/linux/host_control/python_support/station_disconnect.py index 90b4e8bd76..cb6f0c512f 100644 --- a/host/linux/host_control/python_support/station_disconnect.py +++ b/host/linux/host_control/python_support/station_disconnect.py @@ -12,22 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -from commands import * +from commands_lib import * import argparse import os -# WiFi Mode -# NULL 0 -# Station 1 -# SoftAP 2 -# Station + softAP 3 - -wifi_mode_none = 0 -wifi_mode_station = 1 -wifi_mode_softap = 2 -wifi_mode_station_softap = 3 -failure = "failure" -success = "success" wifi_mode = 'not_set' disconnect = "not_set" flag = success @@ -39,7 +27,7 @@ if (wifi_mode == failure): print("Failed to get WiFi Mode") flag = failure -elif ((wifi_mode == wifi_mode_station) or (wifi_mode == wifi_mode_station_softap)): +elif ((wifi_mode == WIFI_MODE_STATION) or (wifi_mode == WIFI_MODE_SOFTAP_STATION)): disconnect = wifi_disconnect_ap() if (disconnect != success): print("Failed to Disconnect from AP") diff --git a/host/linux/host_control/python_support/test_api.py b/host/linux/host_control/python_support/test_api.py index 778415b07f..efda0f2fa8 100644 --- a/host/linux/host_control/python_support/test_api.py +++ b/host/linux/host_control/python_support/test_api.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from commands import * +from commands_lib import * from test_config import * from socket import * from struct import * @@ -285,7 +285,7 @@ def test_softap_mode_connected_clients_info(): stations_list = wifi_connected_stations_list() if (stations_list != failure): for obj in stations_list: - print("station's bssid: "+obj.mac+" rssi: "+str(obj.rssi)) + print("station's bssid: "+obj.bssid+" rssi: "+str(obj.rssi)) else: print("Failed to get connected stations list") return diff --git a/host/linux/host_control/python_support/transport/__init__.py b/host/linux/host_control/python_support/transport/__init__.py deleted file mode 100644 index 96b25b84e0..0000000000 --- a/host/linux/host_control/python_support/transport/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -from .transport import Transport -from .transport_pserial import Transport_pserial # noqa: F403, F401 diff --git a/host/linux/host_control/python_support/transport/transport.py b/host/linux/host_control/python_support/transport/transport.py deleted file mode 100644 index 9eab277f49..0000000000 --- a/host/linux/host_control/python_support/transport/transport.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# Base class for protocomm transport - -import abc - -class Transport(): - - @abc.abstractmethod - def send_session_data(self, data): - pass - - @abc.abstractmethod - def send_config_data(self, data): - pass diff --git a/host/linux/host_control/python_support/transport/transport_pserial.py b/host/linux/host_control/python_support/transport/transport_pserial.py deleted file mode 100644 index 76599d8e6f..0000000000 --- a/host/linux/host_control/python_support/transport/transport_pserial.py +++ /dev/null @@ -1,117 +0,0 @@ -# Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from struct import * -from .transport import Transport -import sys -import os -import select - -failure = -1 -success = 0 - -PROTO_PSER_TLV_T_EPNAME = b'\x01' -PROTO_PSER_TLV_T_DATA = b'\x02' - -# Open `/dev/esps0` in read-write mode -flags = os.O_RDWR - -# All users can read and write -mode = 0o666 - -# Timeout for select -timeout = 20 - -if sys.version_info >= (3, 0): - def get_val(string): - return bytes([string]) -else: - def get_val(string): - return str(string) - -class Transport_pserial(Transport): - def __init__(self, devname): - self.f = os.open(devname, mode, flags) - - def __del__(self): - os.close(self.f) - - def get_len(self, ep_name, in_buf): - if get_val(in_buf[0]) == PROTO_PSER_TLV_T_EPNAME: - if in_buf[1:3] == bytearray(pack(' /dev/null - else - sudo rmmod esp32_spi &> /dev/null - fi - fi + build_python_commands_lib + + cd ../host_driver/esp32/ + if [ `lsmod | grep esp32 | wc -l` != "0" ]; then + sudo rm /dev/esps0 + if [ `lsmod | grep esp32_sdio | wc -l` != "0" ]; then + sudo rmmod esp32_sdio &> /dev/null + else + sudo rmmod esp32_spi &> /dev/null + fi + fi - # For Linux other than Raspberry Pi, Please point + # For Linux other than Raspberry Pi, Please point # CROSS_COMPILE -> /bin/arm-linux-gnueabihf- # KERNEL -> Place where kernel is checked out and built # ARCH -> Architecture @@ -55,93 +65,93 @@ wlan_init() bt_init() { - sudo raspi-gpio set 15 a0 pu - sudo raspi-gpio set 14 a0 pu - sudo raspi-gpio set 16 a3 pu - sudo raspi-gpio set 17 a3 pu + sudo raspi-gpio set 15 a0 pu + sudo raspi-gpio set 14 a0 pu + sudo raspi-gpio set 16 a3 pu + sudo raspi-gpio set 17 a3 pu } usage() { - echo "This script prepares RPI for wlan and bt/ble operation over esp32 device" - echo "\nUsage: ./rpi_init.sh [arguments]" - echo "\nArguments are optional and are as below" - echo " spi: sets ESP32<->RPi communication over SPI" - echo " sdio: sets ESP32<->RPi communication over SDIO" - echo " btuart: Set GPIO pins on RPi for HCI UART operations" - echo " resetpin=6: Set GPIO pins on RPi connected to EN pin of ESP32, used to reset ESP32 (default:6 for BCM6)" - echo "\nExample:" - echo " - Prepare RPi for WLAN operation on SDIO. sdio is default if no interface mentioned" - echo " # ./rpi_init.sh or ./rpi_init.sh sdio" - echo "\n - Use spi for host<->ESP32 communication. sdio is default if no interface mentioned" - echo " # ./rpi_init.sh spi" - echo "\n - Prepare RPi for bt/ble operation over UART and WLAN over SDIO/SPI" - echo " # ./rpi_init.sh sdio btuart or ./rpi_init.sh spi btuart" - echo "\n - use GPIO pin BCM5 (GPIO29) for reset" - echo " # ./rpi_init.sh resetpin=5" - echo "\n - do btuart, use GPIO pin BCM5 (GPIO29) for reset over SDIO/SPI" - echo " # ./rpi_init.sh sdio btuart resetpin=5 or ./rpi_init.sh spi btuart resetpin=5" + echo "This script prepares RPI for wlan and bt/ble operation over esp32 device" + echo "\nUsage: ./rpi_init.sh [arguments]" + echo "\nArguments are optional and are as below" + echo " spi: sets ESP32<->RPi communication over SPI" + echo " sdio: sets ESP32<->RPi communication over SDIO" + echo " btuart: Set GPIO pins on RPi for HCI UART operations" + echo " resetpin=6: Set GPIO pins on RPi connected to EN pin of ESP32, used to reset ESP32 (default:6 for BCM6)" + echo "\nExample:" + echo " - Prepare RPi for WLAN operation on SDIO. sdio is default if no interface mentioned" + echo " # ./rpi_init.sh or ./rpi_init.sh sdio" + echo "\n - Use spi for host<->ESP32 communication. sdio is default if no interface mentioned" + echo " # ./rpi_init.sh spi" + echo "\n - Prepare RPi for bt/ble operation over UART and WLAN over SDIO/SPI" + echo " # ./rpi_init.sh sdio btuart or ./rpi_init.sh spi btuart" + echo "\n - use GPIO pin BCM5 (GPIO29) for reset" + echo " # ./rpi_init.sh resetpin=5" + echo "\n - do btuart, use GPIO pin BCM5 (GPIO29) for reset over SDIO/SPI" + echo " # ./rpi_init.sh sdio btuart resetpin=5 or ./rpi_init.sh spi btuart resetpin=5" } parse_arguments() { - while [ "$1" != "" ] ; do - case $1 in - --help | -h ) - usage - exit 0 - ;; - sdio) - IF_TYPE=$1 - ;; - spi) - IF_TYPE=$1 - ;; - resetpin=*) - echo "Recvd Option: $1" - RESETPIN=$1 - ;; - btuart) - echo "Recvd Option: $1" - BT_INIT_SET="1" - ;; - *) - echo "$1 : unknown option" - usage - exit 1 - ;; - esac - shift - done + while [ "$1" != "" ] ; do + case $1 in + --help | -h ) + usage + exit 0 + ;; + sdio) + IF_TYPE=$1 + ;; + spi) + IF_TYPE=$1 + ;; + resetpin=*) + echo "Recvd Option: $1" + RESETPIN=$1 + ;; + btuart) + echo "Recvd Option: $1" + BT_INIT_SET="1" + ;; + *) + echo "$1 : unknown option" + usage + exit 1 + ;; + esac + shift + done } parse_arguments $* if [ "$IF_TYPE" = "" ] ; then - echo "Error: No protocol selected" - usage - exit 1 + echo "Error: No protocol selected" + usage + exit 1 else - echo "Building for $IF_TYPE protocol" - MODULE_NAME=esp32_${IF_TYPE}.ko + echo "Building for $IF_TYPE protocol" + MODULE_NAME=esp32_${IF_TYPE}.ko fi if [ "$IF_TYPE" = "spi" ] ; then - rm spidev_disabler.dtbo - # Disable default spidev driver - dtc spidev_disabler.dts -O dtb > spidev_disabler.dtbo - sudo dtoverlay -d . spidev_disabler + rm spidev_disabler.dtbo + # Disable default spidev driver + dtc spidev_disabler.dts -O dtb > spidev_disabler.dtbo + sudo dtoverlay -d . spidev_disabler fi if [ `lsmod | grep bluetooth | wc -l` = "0" ]; then - echo "bluetooth module inserted" - sudo modprobe bluetooth + echo "bluetooth module inserted" + sudo modprobe bluetooth fi if [ `lsmod | grep bluetooth | wc -l` != "0" ]; then - wlan_init + wlan_init fi if [ "$BT_INIT_SET" = "1" ] ; then - bt_init + bt_init fi From 7fe2d5088badc7427e4c3cb34625b1b686387f1e Mon Sep 17 00:00:00 2001 From: Yogesh Mantri Date: Wed, 20 Oct 2021 17:58:23 +0530 Subject: [PATCH 18/40] Filter softap traffic - In case of softAP, wifi driver sends all the received frames to upper layer and in turn to the host. Due to this throughput drop is seen when iperf test is performed between two stations connected to same SoftAP. As traffic is already being forwarded at ESP level to expected stations, We can drop traffic destined to stations. Traffic will be dropped for - 1. Destined for stations and not for softap 2. It is non unicast traffic (broadcast/multicast) Signed-off-by: Mangesh Malusare --- .../network_adapter/main/app_main.c | 25 +++++++++++++++++++ .../network_adapter/main/slave_commands.c | 3 ++- .../network_adapter/main/slave_commands.h | 1 + 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/esp/esp_driver/network_adapter/main/app_main.c b/esp/esp_driver/network_adapter/main/app_main.c index 3ec686499e..8510402d72 100644 --- a/esp/esp_driver/network_adapter/main/app_main.c +++ b/esp/esp_driver/network_adapter/main/app_main.c @@ -103,6 +103,8 @@ static struct rx_data { uint8_t data[4096]; } r; +uint8_t ap_mac[MAC_LEN] = {0}; + static void print_firmware_version() { ESP_LOGI(TAG, "*********************************************************************"); @@ -181,10 +183,26 @@ static void esp_wifi_set_debug_log() } +void esp_update_ap_mac(void) +{ + esp_err_t ret = ESP_OK; + char mac_str[BSSID_LENGTH] = ""; + + ret = esp_wifi_get_mac(ESP_IF_WIFI_AP, ap_mac); + ESP_LOGI(TAG,"Get softap mac address"); + if (ret) { + ESP_LOGE(TAG,"Error in getting MAC of ESP softap %d", ret); + } else { + snprintf(mac_str,BSSID_LENGTH,MACSTR,MAC2STR(ap_mac)); + ESP_LOGI(TAG,"AP mac [%s] ", mac_str); + } +} + esp_err_t wlan_ap_rx_callback(void *buffer, uint16_t len, void *eb) { esp_err_t ret = ESP_OK; interface_buffer_handle_t buf_handle = {0}; + uint8_t * ap_buf = buffer; if (!buffer || !eb || !datapath || ota_ongoing) { if (eb) { @@ -193,6 +211,13 @@ esp_err_t wlan_ap_rx_callback(void *buffer, uint16_t len, void *eb) return ESP_OK; } + /* Check destination address against self address */ + if (memcmp(ap_buf, ap_mac, MAC_LEN)) { + /* Check for multicast or broadcast address */ + if (!(ap_buf[0] & 1)) + goto DONE; + } + buf_handle.if_type = ESP_AP_IF; buf_handle.if_num = 0; buf_handle.payload_len = len; diff --git a/esp/esp_driver/network_adapter/main/slave_commands.c b/esp/esp_driver/network_adapter/main/slave_commands.c index 1895fb3c5c..b078f33a86 100644 --- a/esp/esp_driver/network_adapter/main/slave_commands.c +++ b/esp/esp_driver/network_adapter/main/slave_commands.c @@ -21,7 +21,6 @@ #include "esp_hosted_config.pb-c.h" #include "esp_ota_ops.h" -#define MAC_LEN 6 #define MAC_STR_LEN 17 #define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] #define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" @@ -82,6 +81,7 @@ static esp_err_t convert_mac_to_bytes(uint8_t *out, char *s); extern esp_err_t wlan_sta_rx_callback(void *buffer, uint16_t len, void *eb); extern esp_err_t wlan_ap_rx_callback(void *buffer, uint16_t len, void *eb); +void esp_update_ap_mac(void); extern volatile uint8_t station_connected; extern volatile uint8_t softap_started; @@ -138,6 +138,7 @@ static void softap_event_handler(void *arg, esp_event_base_t event_base, MAC2STR(event->mac), event->aid); } else if (event_id == WIFI_EVENT_AP_START) { esp_wifi_internal_reg_rxcb(ESP_IF_WIFI_AP, (wifi_rxcb_t) wlan_ap_rx_callback); + esp_update_ap_mac(); } else if (event_id == WIFI_EVENT_AP_STOP) { ESP_LOGI(TAG,"softap stop handler stop"); esp_wifi_internal_reg_rxcb(ESP_IF_WIFI_AP,NULL); diff --git a/esp/esp_driver/network_adapter/main/slave_commands.h b/esp/esp_driver/network_adapter/main/slave_commands.h index b3f3f59e35..322c11d35a 100644 --- a/esp/esp_driver/network_adapter/main/slave_commands.h +++ b/esp/esp_driver/network_adapter/main/slave_commands.h @@ -19,6 +19,7 @@ #define PASSWORD_LENGTH 64 #define BSSID_LENGTH 19 #define TIMEOUT_IN_SEC (1000 / portTICK_RATE_MS) +#define MAC_LEN 6 typedef struct { uint8_t ssid[SSID_LENGTH]; From 39ff3b90e9edaa650f4317aca7fd7910c9208f89 Mon Sep 17 00:00:00 2001 From: Sonika Rathi Date: Thu, 21 Oct 2021 14:28:18 +0530 Subject: [PATCH 19/40] doc change - SPI frequency bug fix for raspberry pi SPI clock when measured on analyzer, observed lesser than expected. Hardware specific issue and not related to Linux in general. link to original issue: https://github.com/raspberrypi/linux/issues/2286 --- docs/Linux_based_host/SPI_setup.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/Linux_based_host/SPI_setup.md b/docs/Linux_based_host/SPI_setup.md index ae40e33197..af78fbe167 100644 --- a/docs/Linux_based_host/SPI_setup.md +++ b/docs/Linux_based_host/SPI_setup.md @@ -59,9 +59,13 @@ The SPI master driver is disabled by default on Raspberry-Pi OS. To enable it ad dtparam=spi=on dtoverlay=disable-bt ``` +In addition, below options are set as the SPI clock frequency in analyzer is observed to be smaller than expected clock. This is RaspberryPi specific [issue](https://github.com/raspberrypi/linux/issues/2286). +``` +core_freq=250 +core_freq_min=250 +``` Please reboot Raspberry-Pi after changing this file. - ## 2. Load ESP-Hosted Solution ### 2.1 Host Software * Execute following commands in root directory of cloned ESP-Hosted repository on Raspberry-Pi From decd2828352dcee3145cbaf77bc2f88d2788a749 Mon Sep 17 00:00:00 2001 From: Shivani Tipnis Date: Tue, 19 Oct 2021 12:49:47 +0530 Subject: [PATCH 20/40] ci: Add job to sync with github and deploy --- .gitlab-ci.yml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index be34442220..5e4166366d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -31,3 +31,30 @@ deploy_master_github: - git remote remove github &>/dev/null || true - git remote add github git@github.com:espressif/esp-hosted.git - git push github "${CI_COMMIT_SHA}:refs/heads/${CI_COMMIT_REF_NAME}" + +sync_with_github: + stage: deploy + image: $CI_DOCKER_REGISTRY/esp32-ci-env + tags: + - deploy + when: manual + only: + - master + variables: + GITHUB_BRANCH_NAME: "master" + script: + # add ssh key + - mkdir -p ~/.ssh + - chmod 700 ~/.ssh + - echo -n $GH_KEY > ~/.ssh/id_rsa_base64 + - base64 --decode --ignore-garbage ~/.ssh/id_rsa_base64 > ~/.ssh/id_rsa + - chmod 600 ~/.ssh/id_rsa + - echo -e "Host github.com\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config + # git add remote + - git remote remove github &>/dev/null || true + - git remote add github git@github.com:espressif/esp-hosted.git + # sync with github + - git config user.email "test-user@example.com" + - git config user.name "Test User" + - git pull github ${GITHUB_BRANCH_NAME} --rebase + - git push github "${CI_COMMIT_SHA}:refs/heads/${CI_COMMIT_REF_NAME}" From 1ddfee538eb4e2d4b2a50b8f80fc67cc330dcb7c Mon Sep 17 00:00:00 2001 From: Yogesh Mantri Date: Wed, 20 Oct 2021 19:12:15 +0530 Subject: [PATCH 21/40] Doc change - Elaborated how BT/BLE could be implemented in MCU --- README.md | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bb741f0c50..a9824d7f9d 100644 --- a/README.md +++ b/README.md @@ -86,8 +86,8 @@ Below table explains which feature is supported on which transport interface for | ESP device | Transport Interface | WLAN support | Virtual serial interface | BT/BLE support | |:------------:|:-------:|:---------:|:--------:|:--------:| | ESP32 | SDIO | No | No | No | -| ESP32 | SPI | Yes | Yes | HCI interface can be implemented over virtual serial interface | -| ESP32 | UART | No | No | No | +| ESP32 | SPI | Yes | Yes | Yes\* | +| ESP32 | UART | No | No | Yes\*\* | | ESP32-S2 | SDIO | NA | NA | NA | | ESP32-S2 | SPI | Yes | Yes | NA | | ESP32-S2 | UART | No | No | NA | @@ -95,6 +95,23 @@ Below table explains which feature is supported on which transport interface for | ESP32-C3 | SPI | Yes | Yes | No | | ESP32-C3 | UART | No | No | No | +\* BT/BLE over SPI +> BT/BLE support over SPI is not readily available. In order to implement it, one needs to: +> +> Port BT/BLE stack to MCU, \ +> Add a new virtual serial interface using the serial interface API's provided in host driver of ESP-Hosted solution. +> HCI implementation in Linux Driver `host/linux/host_driver/esp32` could be used as reference. Search keyword: `ESP_HCI_IF` +> Register this serial interface with BT/BLE stack as a HCI interface. + +\*\* BT/BLE over UART +> BT/BLE support over UART is not readily available. In order to implement this, one needs to: +> +> Port BT/BLE stack to MCU, \ +> Register the UART serial interface as a HCI interface with BT/BLE stack +> With the help of this UART interface, BT/BLE stack can directly interact with BT controller present on ESP32 bypassing host driver and firmware +> ESP Hosted host driver and a firmware plays no role in this communication + + --- # 2. Get Started From dce7cc96a24f3f3eb20588da5cf9719b427a6493 Mon Sep 17 00:00:00 2001 From: Shivani Tipnis Date: Thu, 21 Oct 2021 23:17:38 +0530 Subject: [PATCH 22/40] ci: Fix sync and deploy to github --- .gitlab-ci.yml | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5e4166366d..3f58627cb5 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -32,7 +32,7 @@ deploy_master_github: - git remote add github git@github.com:espressif/esp-hosted.git - git push github "${CI_COMMIT_SHA}:refs/heads/${CI_COMMIT_REF_NAME}" -sync_with_github: +deploy_force_master_github: stage: deploy image: $CI_DOCKER_REGISTRY/esp32-ci-env tags: @@ -40,21 +40,14 @@ sync_with_github: when: manual only: - master - variables: - GITHUB_BRANCH_NAME: "master" script: - # add ssh key - mkdir -p ~/.ssh - chmod 700 ~/.ssh - echo -n $GH_KEY > ~/.ssh/id_rsa_base64 - base64 --decode --ignore-garbage ~/.ssh/id_rsa_base64 > ~/.ssh/id_rsa - chmod 600 ~/.ssh/id_rsa - echo -e "Host github.com\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config - # git add remote - git remote remove github &>/dev/null || true - git remote add github git@github.com:espressif/esp-hosted.git - # sync with github - - git config user.email "test-user@example.com" - - git config user.name "Test User" - - git pull github ${GITHUB_BRANCH_NAME} --rebase - - git push github "${CI_COMMIT_SHA}:refs/heads/${CI_COMMIT_REF_NAME}" + - git push -f github "${CI_COMMIT_SHA}:refs/heads/${CI_COMMIT_REF_NAME}" + From 5060195958e615902432830dd4336e55fe9c0beb Mon Sep 17 00:00:00 2001 From: Shivani Tipnis Date: Fri, 22 Oct 2021 19:42:57 +0530 Subject: [PATCH 23/40] ci: Disable force push deploy to github --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3f58627cb5..2ba3e5f845 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -32,7 +32,7 @@ deploy_master_github: - git remote add github git@github.com:espressif/esp-hosted.git - git push github "${CI_COMMIT_SHA}:refs/heads/${CI_COMMIT_REF_NAME}" -deploy_force_master_github: +.deploy_force_master_github: stage: deploy image: $CI_DOCKER_REGISTRY/esp32-ci-env tags: From a4892b130dcb7695b84de22bedc7cf76f974f77a Mon Sep 17 00:00:00 2001 From: Yogesh Mantri Date: Fri, 29 Oct 2021 12:42:05 +0530 Subject: [PATCH 24/40] docfix for new fields added in transport header --- README.md | 10 ++++++---- common/include/adapter.h | 5 ++++- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index a9824d7f9d..1858db62e7 100644 --- a/README.md +++ b/README.md @@ -189,18 +189,20 @@ This section describes the data communication protocol used at the transport lay ##### 3.2.1 Payload Format This section explains the payload format used for data transfer on SDIO and SPI interfaces. -* Host and peripheral makes use of 8 byte payload header which preceeds every data packet. +* Host and peripheral makes use of 12 byte payload header which preceeds every data packet. * This payload header provides additional information about the data packet. Based on this header, host/peripheral consumes transmitted data packet. * Payload format is as below | Field | Length | Description | |:-------:|:---------:|:--------| | Interface type | 4 bits | Possible values: STA(0), SoftAP(1), Serial interface(2), HCI (3), Priv interface(4). Rest all values are reserved | -| Reserved | 4 bits | Not in use | -| Reserved | 1 byte | Not in use | +| Interface number | 4 bits | Unused | +| Flags | 1 byte | Additional flags like `MORE_FRAGMENT` in fragmentation | | Packet length | 2 bytes | Actual length of data packet | | Offset to packet | 2 bytes | Offset of actual data packet | -| Reserved | 1 byte | Not in use | +| Checksum | 2 bytes | checksum for complete packet (Includes header and payload) | +| Reserved2 | 1 byte | Not in use | +| seq_num | 2 bytes | Sequence number for serial inerface | | Packet type | 1 byte | reserved when interface type is 0,1 and 2. Applicable only for interface type 3 and 4 | ### 3.3 Integration Guide diff --git a/common/include/adapter.h b/common/include/adapter.h index b185dabb3b..3393d3cc76 100644 --- a/common/include/adapter.h +++ b/common/include/adapter.h @@ -19,13 +19,16 @@ struct esp_payload_header { uint16_t len; uint16_t offset; uint16_t checksum; + uint16_t seq_num; uint8_t reserved2; + /* Position of union field has to always be last, + * this is required for hci_pkt_type */ union { uint8_t reserved3; uint8_t hci_pkt_type; /* Packet type for HCI interface */ uint8_t priv_pkt_type; /* Packet type for priv interface */ }; - uint16_t seq_num; + /* Do no add anything here */ } __attribute__((packed)); typedef enum { From 42f570e12fef2f33cda8650de22673c25a09ad15 Mon Sep 17 00:00:00 2001 From: Yogesh Mantri Date: Tue, 2 Nov 2021 13:48:53 +0530 Subject: [PATCH 25/40] Unify priv interface for SPI and SDIO This 'priv' interface is used for ESP to host notifications (example, ESP32 capabilities) --- .../network_adapter/main/app_main.c | 23 ++- .../network_adapter/main/interface.h | 10 +- .../network_adapter/main/sdio_slave_api.c | 68 +++++++- .../network_adapter/main/spi_slave_api.c | 151 +++++++++--------- host/linux/host_driver/esp32/esp_api.h | 2 + host/linux/host_driver/esp32/main.c | 57 +++++++ host/linux/host_driver/esp32/sdio/esp_sdio.c | 59 +++---- host/linux/host_driver/esp32/spi/esp_spi.c | 55 +------ 8 files changed, 246 insertions(+), 179 deletions(-) diff --git a/esp/esp_driver/network_adapter/main/app_main.c b/esp/esp_driver/network_adapter/main/app_main.c index 8510402d72..b00d13e971 100644 --- a/esp/esp_driver/network_adapter/main/app_main.c +++ b/esp/esp_driver/network_adapter/main/app_main.c @@ -550,15 +550,23 @@ int event_handler(uint8_t val) { switch(val) { case ESP_OPEN_DATA_PATH: - ESP_EARLY_LOGI(TAG, "Start Data Path"); datapath = 1; - if_handle->state = ACTIVE; + if (if_handle) { + if_handle->state = ACTIVE; + ESP_EARLY_LOGI(TAG, "Start Data Path"); + } else { + ESP_EARLY_LOGI(TAG, "Failed to Start Data Path"); + } break; case ESP_CLOSE_DATA_PATH: - ESP_EARLY_LOGI(TAG, "Stop Data Path"); datapath = 0; - if_handle->state = DEACTIVE; + if (if_handle) { + ESP_EARLY_LOGI(TAG, "Stop Data Path"); + if_handle->state = DEACTIVE; + } else { + ESP_EARLY_LOGI(TAG, "Failed to Stop Data Path"); + } break; } return 0; @@ -711,13 +719,18 @@ void app_main() return; } - if_handle = if_context->if_ops->init(capa); + if_handle = if_context->if_ops->init(); if (!if_handle) { ESP_LOGE(TAG, "Failed to initialize driver\n"); return; } + sleep(1); + + /* send capabilities to host */ + generate_startup_event(capa); + for (prio_q_idx=0; prio_q_idx #include #include +#include #include "esp_log.h" #include "interface.h" #include "adapter.h" @@ -32,7 +33,7 @@ interface_context_t context; interface_handle_t if_handle_g; static const char TAG[] = "SDIO_SLAVE"; -static interface_handle_t * sdio_init(uint8_t capabilities); +static interface_handle_t * sdio_init(void); static int32_t sdio_write(interface_handle_t *handle, interface_buffer_handle_t *buf_handle); interface_buffer_handle_t * sdio_read(interface_handle_t *if_handle); static esp_err_t sdio_reset(interface_handle_t *handle); @@ -64,7 +65,7 @@ int interface_remove_driver() return 0; } -static void event_cb(uint8_t val) +IRAM_ATTR static void event_cb(uint8_t val) { if (val == ESP_RESET) { sdio_reset(&if_handle_g); @@ -74,7 +75,64 @@ static void event_cb(uint8_t val) if (context.event_handler) { context.event_handler(val); } +} + +void generate_startup_event(uint8_t cap) +{ + struct esp_payload_header *header = NULL; + interface_buffer_handle_t buf_handle = {0}; + struct esp_priv_event *event = NULL; + uint8_t *pos = NULL; + uint16_t len = 0; + esp_err_t ret = ESP_OK; + + memset(&buf_handle, 0, sizeof(buf_handle)); + + buf_handle.payload = heap_caps_malloc(BUFFER_SIZE, MALLOC_CAP_DMA); + assert(buf_handle.payload); + memset(buf_handle.payload, 0, BUFFER_SIZE); + + header = (struct esp_payload_header *) buf_handle.payload; + + header->if_type = ESP_PRIV_IF; + header->if_num = 0; + header->offset = htole16(sizeof(struct esp_payload_header)); + header->priv_pkt_type = ESP_PACKET_TYPE_EVENT; + + /* Populate event data */ + event = (struct esp_priv_event *) (buf_handle.payload + sizeof(struct esp_payload_header)); + + event->event_type = ESP_PRIV_EVENT_INIT; + + /* Populate TLVs for event */ + pos = event->event_data; + /* TLVs start */ + + /* TLV - Capability */ + *pos = ESP_PRIV_CAPABILITY; pos++;len++; + *pos = LENGTH_1_BYTE; pos++;len++; + *pos = cap; pos++;len++; + + /* TLVs end */ + + event->event_len = len; + + /* payload len = Event len + sizeof(event type) + sizeof(event len) */ + len += 2; + header->len = htole16(len); + + buf_handle.payload_len = len + sizeof(struct esp_payload_header); + header->checksum = htole16(compute_checksum(buf_handle.payload, buf_handle.payload_len)); + + ret = sdio_slave_transmit(buf_handle.payload, buf_handle.payload_len); + if (ret != ESP_OK) { + ESP_LOGE(TAG , "sdio slave tx error, ret : 0x%x\r\n", ret); + free(buf_handle.payload); + return; + } + + free(buf_handle.payload); } static void sdio_read_done(void *handle) @@ -82,8 +140,7 @@ static void sdio_read_done(void *handle) sdio_slave_recv_load_buf((sdio_slave_buf_handle_t) handle); } - -static interface_handle_t * sdio_init(uint8_t capabilities) +static interface_handle_t * sdio_init(void) { esp_err_t ret = ESP_OK; sdio_slave_config_t config = { @@ -133,9 +190,6 @@ static interface_handle_t * sdio_init(uint8_t capabilities) return NULL; } - /* Advertise slave capabilities at 0th offset of reg HOST_SLCHOST_CONF_W0_REG */ - sdio_slave_write_reg(0, capabilities); - memset(&if_handle_g, 0, sizeof(if_handle_g)); if_handle_g.state = INIT; diff --git a/esp/esp_driver/network_adapter/main/spi_slave_api.c b/esp/esp_driver/network_adapter/main/spi_slave_api.c index 95614dc9c4..329d65ef6c 100644 --- a/esp/esp_driver/network_adapter/main/spi_slave_api.c +++ b/esp/esp_driver/network_adapter/main/spi_slave_api.c @@ -102,8 +102,6 @@ static const char TAG[] = "SPI_DRIVER"; #define SPI_TX_QUEUE_SIZE 5 #endif -#define LENGTH_1_BYTE 1 - static interface_context_t context; static interface_handle_t if_handle_g; static uint8_t gpio_handshake = CONFIG_ESP_SPI_GPIO_HANDSHAKE; @@ -111,13 +109,14 @@ static uint8_t gpio_data_ready = CONFIG_ESP_SPI_GPIO_DATA_READY; static QueueHandle_t spi_rx_queue[MAX_PRIORITY_QUEUES] = {NULL}; static QueueHandle_t spi_tx_queue[MAX_PRIORITY_QUEUES] = {NULL}; -static interface_handle_t * esp_spi_init(uint8_t capabilities); +static interface_handle_t * esp_spi_init(void); static int32_t esp_spi_write(interface_handle_t *handle, interface_buffer_handle_t *buf_handle); static interface_buffer_handle_t * esp_spi_read(interface_handle_t *if_handle); static esp_err_t esp_spi_reset(interface_handle_t *handle); static void esp_spi_deinit(interface_handle_t *handle); static void esp_spi_read_done(void *handle); +static void queue_next_transaction(void); @@ -147,6 +146,77 @@ int interface_remove_driver() return 0; } +void generate_startup_event(uint8_t cap) +{ + struct esp_payload_header *header = NULL; + interface_buffer_handle_t buf_handle = {0}; + struct esp_priv_event *event = NULL; + uint8_t *pos = NULL; + uint16_t len = 0; + + memset(&buf_handle, 0, sizeof(buf_handle)); + + buf_handle.payload = heap_caps_malloc(SPI_BUFFER_SIZE, MALLOC_CAP_DMA); + assert(buf_handle.payload); + memset(buf_handle.payload, 0, SPI_BUFFER_SIZE); + + header = (struct esp_payload_header *) buf_handle.payload; + + header->if_type = ESP_PRIV_IF; + header->if_num = 0; + header->offset = htole16(sizeof(struct esp_payload_header)); + header->priv_pkt_type = ESP_PACKET_TYPE_EVENT; + + /* Populate event data */ + event = (struct esp_priv_event *) (buf_handle.payload + sizeof(struct esp_payload_header)); + + event->event_type = ESP_PRIV_EVENT_INIT; + + /* Populate TLVs for event */ + pos = event->event_data; + + /* TLVs start */ + + /* TLV - Board type */ + *pos = ESP_PRIV_FIRMWARE_CHIP_ID; pos++;len++; + *pos = LENGTH_1_BYTE; pos++;len++; + *pos = CONFIG_IDF_FIRMWARE_CHIP_ID; pos++;len++; + + /* TLV - Peripheral clock in MHz */ + *pos = ESP_PRIV_SPI_CLK_MHZ; pos++;len++; + *pos = LENGTH_1_BYTE; pos++;len++; +#ifdef CONFIG_IDF_TARGET_ESP32 + *pos = SPI_CLK_MHZ_ESP32; pos++;len++; +#elif defined CONFIG_IDF_TARGET_ESP32S2 + *pos = SPI_CLK_MHZ_ESP32_S2; pos++;len++; +#elif defined CONFIG_IDF_TARGET_ESP32C3 + *pos = SPI_CLK_MHZ_ESP32_C3; pos++;len++; +#endif + + /* TLV - Capability */ + *pos = ESP_PRIV_CAPABILITY; pos++;len++; + *pos = LENGTH_1_BYTE; pos++;len++; + *pos = cap; pos++;len++; + + /* TLVs end */ + + event->event_len = len; + + /* payload len = Event len + sizeof(event type) + sizeof(event len) */ + len += 2; + header->len = htole16(len); + + buf_handle.payload_len = len + sizeof(struct esp_payload_header); + header->checksum = htole16(compute_checksum(buf_handle.payload, buf_handle.payload_len)); + + xQueueSend(spi_tx_queue[PRIO_Q_OTHERS], &buf_handle, portMAX_DELAY); + + /* indicate waiting data on ready pin */ + WRITE_PERI_REG(GPIO_OUT_W1TS_REG, (1 << gpio_data_ready)); + /* process first data packet here to start transactions */ + queue_next_transaction(); +} + /* Invoked after transaction is queued and ready for pickup by master */ static void IRAM_ATTR spi_post_setup_cb(spi_slave_transaction_t *trans) @@ -369,78 +439,7 @@ static void spi_transaction_post_process_task(void* pvParameters) } } -static void generate_startup_event(uint8_t cap) -{ - struct esp_payload_header *header = NULL; - interface_buffer_handle_t buf_handle = {0}; - struct esp_priv_event *event = NULL; - uint8_t *pos = NULL; - uint16_t len = 0; - - memset(&buf_handle, 0, sizeof(buf_handle)); - - buf_handle.payload = heap_caps_malloc(SPI_BUFFER_SIZE, MALLOC_CAP_DMA); - assert(buf_handle.payload); - memset(buf_handle.payload, 0, SPI_BUFFER_SIZE); - - header = (struct esp_payload_header *) buf_handle.payload; - - header->if_type = ESP_PRIV_IF; - header->if_num = 0; - header->offset = htole16(sizeof(struct esp_payload_header)); - header->priv_pkt_type = ESP_PACKET_TYPE_EVENT; - - /* Populate event data */ - event = (struct esp_priv_event *) (buf_handle.payload + sizeof(struct esp_payload_header)); - - event->event_type = ESP_PRIV_EVENT_INIT; - - /* Populate TLVs for event */ - pos = event->event_data; - - /* TLVs start */ - - /* TLV - Board type */ - *pos = ESP_PRIV_FIRMWARE_CHIP_ID; pos++;len++; - *pos = LENGTH_1_BYTE; pos++;len++; - *pos = CONFIG_IDF_FIRMWARE_CHIP_ID; pos++;len++; - - /* TLV - Peripheral clock in MHz */ - *pos = ESP_PRIV_SPI_CLK_MHZ; pos++;len++; - *pos = LENGTH_1_BYTE; pos++;len++; -#ifdef CONFIG_IDF_TARGET_ESP32 - *pos = SPI_CLK_MHZ_ESP32; pos++;len++; -#elif defined CONFIG_IDF_TARGET_ESP32S2 - *pos = SPI_CLK_MHZ_ESP32_S2; pos++;len++; -#elif defined CONFIG_IDF_TARGET_ESP32C3 - *pos = SPI_CLK_MHZ_ESP32_C3; pos++;len++; -#endif - - /* TLV - Capability */ - *pos = ESP_PRIV_CAPABILITY; pos++;len++; - *pos = LENGTH_1_BYTE; pos++;len++; - *pos = cap; pos++;len++; - - /* TLVs end */ - - event->event_len = len; - - /* payload len = Event len + sizeof(event type) + sizeof(event len) */ - len += 2; - header->len = htole16(len); - - buf_handle.payload_len = len + sizeof(struct esp_payload_header); - header->checksum = htole16(compute_checksum(buf_handle.payload, buf_handle.payload_len)); - - xQueueSend(spi_tx_queue[PRIO_Q_OTHERS], &buf_handle, portMAX_DELAY); - - /* indicate waiting data on ready pin */ - WRITE_PERI_REG(GPIO_OUT_W1TS_REG, (1 << gpio_data_ready)); - /* process first data packet here to start transactions */ - queue_next_transaction(); -} - -static interface_handle_t * esp_spi_init(uint8_t capabilities) +static interface_handle_t * esp_spi_init(void) { esp_err_t ret = ESP_OK; uint8_t prio_q_idx = 0; @@ -519,8 +518,6 @@ static interface_handle_t * esp_spi_init(uint8_t capabilities) usleep(500); - generate_startup_event(capabilities); - return &if_handle_g; } diff --git a/host/linux/host_driver/esp32/esp_api.h b/host/linux/host_driver/esp32/esp_api.h index 590a69cc18..111f142b93 100644 --- a/host/linux/host_driver/esp32/esp_api.h +++ b/host/linux/host_driver/esp32/esp_api.h @@ -31,5 +31,7 @@ int esp_send_packet(struct esp_adapter *adapter, struct sk_buff *skb); u8 esp_is_bt_supported_over_sdio(u32 cap); void esp_tx_pause(void); void esp_tx_resume(void); +void process_init_event(u8 *evt_buf, u8 len); +void process_capabilities(u8 cap); #endif diff --git a/host/linux/host_driver/esp32/main.c b/host/linux/host_driver/esp32/main.c index 421861193f..4ee6a7d225 100644 --- a/host/linux/host_driver/esp32/main.c +++ b/host/linux/host_driver/esp32/main.c @@ -32,6 +32,7 @@ #include "esp_serial.h" #endif #include "esp_bt_api.h" +#include "esp_api.h" MODULE_LICENSE("GPL"); MODULE_AUTHOR("Amey Inamdar "); @@ -317,6 +318,60 @@ static int process_tx_packet (struct sk_buff *skb) return 0; } +void process_capabilities(u8 cap) +{ + struct esp_adapter *adapter = esp_get_adapter(); + printk (KERN_INFO "ESP peripheral capabilities: 0x%x\n", cap); + adapter->capabilities = cap; + + /* Reset BT */ + esp_deinit_bt(esp_get_adapter()); + + if ((cap & ESP_BT_SPI_SUPPORT) || (cap & ESP_BT_SDIO_SUPPORT)) { + msleep(200); + esp_init_bt(esp_get_adapter()); + } +} + + +static void process_event(u8 *evt_buf, u16 len) +{ + struct esp_priv_event *event; + + if (!evt_buf || !len) + return; + + event = (struct esp_priv_event *) evt_buf; + + if (event->event_type == ESP_PRIV_EVENT_INIT) { + printk (KERN_INFO "\nReceived INIT event from ESP32 peripheral"); + process_init_event(event->event_data, event->event_len); + } else { + printk (KERN_WARNING "Drop unknown event"); + } +} + +static void process_priv_communication(struct sk_buff *skb) +{ + struct esp_payload_header *header; + u8 *payload; + u16 len; + + if (!skb || !skb->data) + return; + + header = (struct esp_payload_header *) skb->data; + + payload = skb->data + le16_to_cpu(header->offset); + len = le16_to_cpu(header->len); + + if (header->priv_pkt_type == ESP_PACKET_TYPE_EVENT) { + process_event(payload, len); + } + + dev_kfree_skb(skb); +} + static void process_rx_packet(struct sk_buff *skb) { struct esp_private *priv = NULL; @@ -405,6 +460,8 @@ static void process_rx_packet(struct sk_buff *skb) esp_hci_update_rx_counter(hdev, *type, skb->len); } } + } else if (payload_header->if_type == ESP_PRIV_IF) { + process_priv_communication(skb); } } diff --git a/host/linux/host_driver/esp32/sdio/esp_sdio.c b/host/linux/host_driver/esp32/sdio/esp_sdio.c index bc439d3a62..4dd3f20a62 100644 --- a/host/linux/host_driver/esp32/sdio/esp_sdio.c +++ b/host/linux/host_driver/esp32/sdio/esp_sdio.c @@ -712,7 +712,6 @@ static int esp_probe(struct sdio_func *func, { struct esp_sdio_context *context = NULL; int ret = 0; - uint32_t *cap; if (func->num != 1) { return -EINVAL; @@ -726,10 +725,6 @@ static int esp_probe(struct sdio_func *func, return -ENOMEM; } - generate_slave_intr(context, BIT(ESP_RESET)); - - msleep(200); - atomic_set(&tx_pending, 0); ret = init_context(context); if (ret) { @@ -759,36 +754,7 @@ static int esp_probe(struct sdio_func *func, return ret; } - cap = kmalloc(sizeof(u32), GFP_KERNEL); - - if (!cap) { - esp_remove(func); - printk (KERN_ERR "Failed to allocate memory\n"); - deinit_sdio_func(func); - return -ENOMEM; - } - - /* Read slave capabilities */ - ret = esp_read_reg(context, ESP_SLAVE_SCRATCH_REG_0, - (u8 *) cap, sizeof(* cap), ACQUIRE_LOCK); - - if (ret) { - kfree(cap); - esp_remove(func); - printk (KERN_ERR "Failed to read capability\n"); - deinit_sdio_func(func); - return ret; - } - - context->adapter->capabilities = * cap; - print_capabilities(*cap); - - if (esp_is_bt_supported_over_sdio(*cap)) { - esp_init_bt(context->adapter); - } - - kfree(cap); context->state = ESP_CONTEXT_READY; @@ -799,7 +765,6 @@ static int esp_probe(struct sdio_func *func, printk (KERN_ERR "Failed to create monitor thread\n"); #endif - msleep(200); generate_slave_intr(context, BIT(ESP_OPEN_DATA_PATH)); return ret; } @@ -824,6 +789,30 @@ int esp_init_interface_layer(struct esp_adapter *adapter) return sdio_register_driver(&esp_sdio_driver); } +void process_init_event(u8 *evt_buf, u8 len) +{ + u8 len_left = len, tag_len; + u8 *pos; + + if (!evt_buf) + return; + + pos = evt_buf; + + while (len_left) { + tag_len = *(pos + 1); + printk(KERN_INFO "EVENT: %d\n", *pos); + if (*pos == ESP_PRIV_CAPABILITY) { + process_capabilities(*(pos + 2)); + print_capabilities(*(pos + 2)); + } else { + printk (KERN_WARNING "Unsupported tag in event"); + } + pos += (tag_len+2); + len_left -= (tag_len+2); + } +} + void esp_deinit_interface_layer(void) { sdio_unregister_driver(&esp_sdio_driver); diff --git a/host/linux/host_driver/esp32/spi/esp_spi.c b/host/linux/host_driver/esp32/spi/esp_spi.c index 67b60da07f..42a3ccc324 100644 --- a/host/linux/host_driver/esp32/spi/esp_spi.c +++ b/host/linux/host_driver/esp32/spi/esp_spi.c @@ -164,20 +164,8 @@ static int write_packet(struct esp_adapter *adapter, struct sk_buff *skb) return 0; } -static void process_capabilities(u8 cap) -{ - printk (KERN_INFO "ESP peripheral capabilities: 0x%x\n", cap); - - /* Reset BT */ - esp_deinit_bt(spi_context.adapter); - if ((cap & ESP_BT_SPI_SUPPORT) || (cap & ESP_BT_SDIO_SUPPORT)) { - msleep(200); - esp_init_bt(spi_context.adapter); - } -} - -static void process_init_event(u8 *evt_buf, u8 len) +void process_init_event(u8 *evt_buf, u8 len) { u8 len_left = len, tag_len; u8 *pos; @@ -210,43 +198,6 @@ static void process_init_event(u8 *evt_buf, u8 len) } } -static void process_event(u8 *evt_buf, u16 len) -{ - struct esp_priv_event *event; - - if (!evt_buf || !len) - return; - - event = (struct esp_priv_event *) evt_buf; - - if (event->event_type == ESP_PRIV_EVENT_INIT) { - printk (KERN_INFO "Received INIT event from ESP32 peripheral"); - process_init_event(event->event_data, event->event_len); - } else { - printk (KERN_WARNING "Drop unknown event"); - } -} - -static void process_priv_communication(struct sk_buff *skb) -{ - struct esp_payload_header *header; - u8 *payload; - u16 len; - - if (!skb || !skb->data) - return; - - header = (struct esp_payload_header *) skb->data; - - payload = skb->data + le16_to_cpu(header->offset); - len = le16_to_cpu(header->len); - - if (header->priv_pkt_type == ESP_PACKET_TYPE_EVENT) { - process_event(payload, len); - } - - dev_kfree_skb(skb); -} static int process_rx_buf(struct sk_buff *skb) { @@ -284,10 +235,6 @@ static int process_rx_buf(struct sk_buff *skb) /* Trim SKB to actual size */ skb_trim(skb, len); - if (header->if_type == ESP_PRIV_IF) { - process_priv_communication(skb); - return 0; - } if (!data_path) return -EPERM; From 9625f63d6a8a862d84428df5de8179a9933f090b Mon Sep 17 00:00:00 2001 From: "ajita.chavan" Date: Tue, 2 Nov 2021 06:44:06 +0000 Subject: [PATCH 26/40] vendor specific ie control path command added Signed-off-by: ajita.chavan --- common/esp_hosted_config.pb-c.c | 309 +++++++++++++++++- common/include/esp_hosted_config.pb-c.h | 99 +++++- common/proto/esp_hosted_config.proto | 28 ++ docs/c_api.md | 62 +++- docs/c_demo.md | 11 +- .../network_adapter/main/slave_commands.c | 45 +++ host/host_common/commands.c | 90 +++++ host/host_common/include/commands.h | 34 +- host/linux/host_control/c_support/test.c | 15 +- host/linux/host_control/c_support/test_api.c | 57 +++- host/linux/host_control/c_support/test_api.h | 2 + .../host_control/c_support/test_config.h | 1 + 12 files changed, 729 insertions(+), 24 deletions(-) diff --git a/common/esp_hosted_config.pb-c.c b/common/esp_hosted_config.pb-c.c index 6215e433da..f6e3d8cc22 100644 --- a/common/esp_hosted_config.pb-c.c +++ b/common/esp_hosted_config.pb-c.c @@ -1297,6 +1297,92 @@ void esp_hosted_resp_otaend__free_unpacked assert(message->base.descriptor == &esp_hosted_resp_otaend__descriptor); protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); } +void esp_hosted_cmd_set_vendor_specific_ie__init + (EspHostedCmdSetVendorSpecificIE *message) +{ + static EspHostedCmdSetVendorSpecificIE init_value = ESP_HOSTED_CMD_SET_VENDOR_SPECIFIC_IE__INIT; + *message = init_value; +} +size_t esp_hosted_cmd_set_vendor_specific_ie__get_packed_size + (const EspHostedCmdSetVendorSpecificIE *message) +{ + assert(message->base.descriptor == &esp_hosted_cmd_set_vendor_specific_ie__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t esp_hosted_cmd_set_vendor_specific_ie__pack + (const EspHostedCmdSetVendorSpecificIE *message, + uint8_t *out) +{ + assert(message->base.descriptor == &esp_hosted_cmd_set_vendor_specific_ie__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t esp_hosted_cmd_set_vendor_specific_ie__pack_to_buffer + (const EspHostedCmdSetVendorSpecificIE *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &esp_hosted_cmd_set_vendor_specific_ie__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +EspHostedCmdSetVendorSpecificIE * + esp_hosted_cmd_set_vendor_specific_ie__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (EspHostedCmdSetVendorSpecificIE *) + protobuf_c_message_unpack (&esp_hosted_cmd_set_vendor_specific_ie__descriptor, + allocator, len, data); +} +void esp_hosted_cmd_set_vendor_specific_ie__free_unpacked + (EspHostedCmdSetVendorSpecificIE *message, + ProtobufCAllocator *allocator) +{ + assert(message->base.descriptor == &esp_hosted_cmd_set_vendor_specific_ie__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void esp_hosted_resp_set_vendor_specific_ie__init + (EspHostedRespSetVendorSpecificIE *message) +{ + static EspHostedRespSetVendorSpecificIE init_value = ESP_HOSTED_RESP_SET_VENDOR_SPECIFIC_IE__INIT; + *message = init_value; +} +size_t esp_hosted_resp_set_vendor_specific_ie__get_packed_size + (const EspHostedRespSetVendorSpecificIE *message) +{ + assert(message->base.descriptor == &esp_hosted_resp_set_vendor_specific_ie__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t esp_hosted_resp_set_vendor_specific_ie__pack + (const EspHostedRespSetVendorSpecificIE *message, + uint8_t *out) +{ + assert(message->base.descriptor == &esp_hosted_resp_set_vendor_specific_ie__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t esp_hosted_resp_set_vendor_specific_ie__pack_to_buffer + (const EspHostedRespSetVendorSpecificIE *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &esp_hosted_resp_set_vendor_specific_ie__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +EspHostedRespSetVendorSpecificIE * + esp_hosted_resp_set_vendor_specific_ie__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (EspHostedRespSetVendorSpecificIE *) + protobuf_c_message_unpack (&esp_hosted_resp_set_vendor_specific_ie__descriptor, + allocator, len, data); +} +void esp_hosted_resp_set_vendor_specific_ie__free_unpacked + (EspHostedRespSetVendorSpecificIE *message, + ProtobufCAllocator *allocator) +{ + assert(message->base.descriptor == &esp_hosted_resp_set_vendor_specific_ie__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} void esp_hosted_config_payload__init (EspHostedConfigPayload *message) { @@ -2762,7 +2848,122 @@ const ProtobufCMessageDescriptor esp_hosted_resp_otaend__descriptor = (ProtobufCMessageInit) esp_hosted_resp_otaend__init, NULL,NULL,NULL /* reserved[123] */ }; -static const ProtobufCFieldDescriptor esp_hosted_config_payload__field_descriptors[35] = +static const ProtobufCFieldDescriptor esp_hosted_cmd_set_vendor_specific_ie__field_descriptors[4] = +{ + { + "enable", + 1, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BOOL, + offsetof(EspHostedCmdSetVendorSpecificIE, has_enable), + offsetof(EspHostedCmdSetVendorSpecificIE, enable), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "type", + 2, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_ENUM, + offsetof(EspHostedCmdSetVendorSpecificIE, has_type), + offsetof(EspHostedCmdSetVendorSpecificIE, type), + &esp_hosted_vendor_ietype__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "idx", + 3, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_ENUM, + offsetof(EspHostedCmdSetVendorSpecificIE, has_idx), + offsetof(EspHostedCmdSetVendorSpecificIE, idx), + &esp_hosted_ieid__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "vendor_ie_data", + 4, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BYTES, + offsetof(EspHostedCmdSetVendorSpecificIE, has_vendor_ie_data), + offsetof(EspHostedCmdSetVendorSpecificIE, vendor_ie_data), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned esp_hosted_cmd_set_vendor_specific_ie__field_indices_by_name[] = { + 0, /* field[0] = enable */ + 2, /* field[2] = idx */ + 1, /* field[1] = type */ + 3, /* field[3] = vendor_ie_data */ +}; +static const ProtobufCIntRange esp_hosted_cmd_set_vendor_specific_ie__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 4 } +}; +const ProtobufCMessageDescriptor esp_hosted_cmd_set_vendor_specific_ie__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "EspHostedCmdSetVendorSpecificIE", + "EspHostedCmdSetVendorSpecificIE", + "EspHostedCmdSetVendorSpecificIE", + "", + sizeof(EspHostedCmdSetVendorSpecificIE), + 4, + esp_hosted_cmd_set_vendor_specific_ie__field_descriptors, + esp_hosted_cmd_set_vendor_specific_ie__field_indices_by_name, + 1, esp_hosted_cmd_set_vendor_specific_ie__number_ranges, + (ProtobufCMessageInit) esp_hosted_cmd_set_vendor_specific_ie__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor esp_hosted_resp_set_vendor_specific_ie__field_descriptors[1] = +{ + { + "resp", + 1, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_INT32, + offsetof(EspHostedRespSetVendorSpecificIE, has_resp), + offsetof(EspHostedRespSetVendorSpecificIE, resp), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned esp_hosted_resp_set_vendor_specific_ie__field_indices_by_name[] = { + 0, /* field[0] = resp */ +}; +static const ProtobufCIntRange esp_hosted_resp_set_vendor_specific_ie__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 1 } +}; +const ProtobufCMessageDescriptor esp_hosted_resp_set_vendor_specific_ie__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "EspHostedRespSetVendorSpecificIE", + "EspHostedRespSetVendorSpecificIE", + "EspHostedRespSetVendorSpecificIE", + "", + sizeof(EspHostedRespSetVendorSpecificIE), + 1, + esp_hosted_resp_set_vendor_specific_ie__field_descriptors, + esp_hosted_resp_set_vendor_specific_ie__field_indices_by_name, + 1, esp_hosted_resp_set_vendor_specific_ie__number_ranges, + (ProtobufCMessageInit) esp_hosted_resp_set_vendor_specific_ie__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor esp_hosted_config_payload__field_descriptors[37] = { { "msg", @@ -3184,6 +3385,30 @@ static const ProtobufCFieldDescriptor esp_hosted_config_payload__field_descripto 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ 0,NULL,NULL /* reserved1,reserved2, etc */ }, + { + "cmd_set_vendor_specific_ie", + 44, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(EspHostedConfigPayload, payload_case), + offsetof(EspHostedConfigPayload, cmd_set_vendor_specific_ie), + &esp_hosted_cmd_set_vendor_specific_ie__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "resp_set_vendor_specific_ie", + 45, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(EspHostedConfigPayload, payload_case), + offsetof(EspHostedConfigPayload, resp_set_vendor_specific_ie), + &esp_hosted_resp_set_vendor_specific_ie__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, }; static const unsigned esp_hosted_config_payload__field_indices_by_name[] = { 21, /* field[21] = cmd_connected_stas_list */ @@ -3201,6 +3426,7 @@ static const unsigned esp_hosted_config_payload__field_indices_by_name[] = { 23, /* field[23] = cmd_set_mac_address */ 25, /* field[25] = cmd_set_power_save_mode */ 13, /* field[13] = cmd_set_softap_config */ + 35, /* field[35] = cmd_set_vendor_specific_ie */ 5, /* field[5] = cmd_set_wifi_mode */ 17, /* field[17] = cmd_stop_softap */ 0, /* field[0] = msg */ @@ -3219,6 +3445,7 @@ static const unsigned esp_hosted_config_payload__field_indices_by_name[] = { 24, /* field[24] = resp_set_mac_address */ 26, /* field[26] = resp_set_power_save_mode */ 14, /* field[14] = resp_set_softap_config */ + 36, /* field[36] = resp_set_vendor_specific_ie */ 6, /* field[6] = resp_set_wifi_mode */ 18, /* field[18] = resp_stop_softap */ }; @@ -3226,7 +3453,7 @@ static const ProtobufCIntRange esp_hosted_config_payload__number_ranges[2 + 1] = { { 1, 0 }, { 10, 1 }, - { 0, 35 } + { 0, 37 } }; const ProtobufCMessageDescriptor esp_hosted_config_payload__descriptor = { @@ -3236,7 +3463,7 @@ const ProtobufCMessageDescriptor esp_hosted_config_payload__descriptor = "EspHostedConfigPayload", "", sizeof(EspHostedConfigPayload), - 35, + 37, esp_hosted_config_payload__field_descriptors, esp_hosted_config_payload__field_indices_by_name, 2, esp_hosted_config_payload__number_ranges, @@ -3315,7 +3542,69 @@ const ProtobufCEnumDescriptor esp_hosted_status__descriptor = esp_hosted_status__value_ranges, NULL,NULL,NULL,NULL /* reserved[1234] */ }; -static const ProtobufCEnumValue esp_hosted_config_msg_type__enum_values_by_number[34] = +static const ProtobufCEnumValue esp_hosted_vendor_ietype__enum_values_by_number[5] = +{ + { "VND_IE_TYPE_BEACON", "ESP_HOSTED_VENDOR_IETYPE__VND_IE_TYPE_BEACON", 0 }, + { "VND_IE_TYPE_PROBE_REQ", "ESP_HOSTED_VENDOR_IETYPE__VND_IE_TYPE_PROBE_REQ", 1 }, + { "VND_IE_TYPE_PROBE_RESP", "ESP_HOSTED_VENDOR_IETYPE__VND_IE_TYPE_PROBE_RESP", 2 }, + { "VND_IE_TYPE_ASSOC_REQ", "ESP_HOSTED_VENDOR_IETYPE__VND_IE_TYPE_ASSOC_REQ", 3 }, + { "VND_IE_TYPE_ASSOC_RESP", "ESP_HOSTED_VENDOR_IETYPE__VND_IE_TYPE_ASSOC_RESP", 4 }, +}; +static const ProtobufCIntRange esp_hosted_vendor_ietype__value_ranges[] = { +{0, 0},{0, 5} +}; +static const ProtobufCEnumValueIndex esp_hosted_vendor_ietype__enum_values_by_name[5] = +{ + { "VND_IE_TYPE_ASSOC_REQ", 3 }, + { "VND_IE_TYPE_ASSOC_RESP", 4 }, + { "VND_IE_TYPE_BEACON", 0 }, + { "VND_IE_TYPE_PROBE_REQ", 1 }, + { "VND_IE_TYPE_PROBE_RESP", 2 }, +}; +const ProtobufCEnumDescriptor esp_hosted_vendor_ietype__descriptor = +{ + PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC, + "EspHostedVendorIEType", + "EspHostedVendorIEType", + "EspHostedVendorIEType", + "", + 5, + esp_hosted_vendor_ietype__enum_values_by_number, + 5, + esp_hosted_vendor_ietype__enum_values_by_name, + 1, + esp_hosted_vendor_ietype__value_ranges, + NULL,NULL,NULL,NULL /* reserved[1234] */ +}; +static const ProtobufCEnumValue esp_hosted_ieid__enum_values_by_number[2] = +{ + { "VND_IE_ID_0", "ESP_HOSTED_IEID__VND_IE_ID_0", 0 }, + { "VND_IE_ID_1", "ESP_HOSTED_IEID__VND_IE_ID_1", 1 }, +}; +static const ProtobufCIntRange esp_hosted_ieid__value_ranges[] = { +{0, 0},{0, 2} +}; +static const ProtobufCEnumValueIndex esp_hosted_ieid__enum_values_by_name[2] = +{ + { "VND_IE_ID_0", 0 }, + { "VND_IE_ID_1", 1 }, +}; +const ProtobufCEnumDescriptor esp_hosted_ieid__descriptor = +{ + PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC, + "EspHostedIEID", + "EspHostedIEID", + "EspHostedIEID", + "", + 2, + esp_hosted_ieid__enum_values_by_number, + 2, + esp_hosted_ieid__enum_values_by_name, + 1, + esp_hosted_ieid__value_ranges, + NULL,NULL,NULL,NULL /* reserved[1234] */ +}; +static const ProtobufCEnumValue esp_hosted_config_msg_type__enum_values_by_number[36] = { { "TypeCmdGetMACAddress", "ESP_HOSTED_CONFIG_MSG_TYPE__TypeCmdGetMACAddress", 0 }, { "TypeRespGetMACAddress", "ESP_HOSTED_CONFIG_MSG_TYPE__TypeRespGetMACAddress", 1 }, @@ -3351,11 +3640,13 @@ static const ProtobufCEnumValue esp_hosted_config_msg_type__enum_values_by_numbe { "TypeRespOTAWrite", "ESP_HOSTED_CONFIG_MSG_TYPE__TypeRespOTAWrite", 31 }, { "TypeCmdOTAEnd", "ESP_HOSTED_CONFIG_MSG_TYPE__TypeCmdOTAEnd", 32 }, { "TypeRespOTAEnd", "ESP_HOSTED_CONFIG_MSG_TYPE__TypeRespOTAEnd", 33 }, + { "TypeCmdSetVendorSpecificIE", "ESP_HOSTED_CONFIG_MSG_TYPE__TypeCmdSetVendorSpecificIE", 34 }, + { "TypeRespSetVendorSpecificIE", "ESP_HOSTED_CONFIG_MSG_TYPE__TypeRespSetVendorSpecificIE", 35 }, }; static const ProtobufCIntRange esp_hosted_config_msg_type__value_ranges[] = { -{0, 0},{0, 34} +{0, 0},{0, 36} }; -static const ProtobufCEnumValueIndex esp_hosted_config_msg_type__enum_values_by_name[34] = +static const ProtobufCEnumValueIndex esp_hosted_config_msg_type__enum_values_by_name[36] = { { "TypeCmdDisconnectAP", 14 }, { "TypeCmdGetAPConfig", 6 }, @@ -3372,6 +3663,7 @@ static const ProtobufCEnumValueIndex esp_hosted_config_msg_type__enum_values_by_ { "TypeCmdSetMacAddress", 22 }, { "TypeCmdSetPowerSaveMode", 24 }, { "TypeCmdSetSoftAPConfig", 12 }, + { "TypeCmdSetVendorSpecificIE", 34 }, { "TypeCmdSetWiFiMode", 4 }, { "TypeCmdStopSoftAP", 16 }, { "TypeRespDisconnectAP", 15 }, @@ -3389,6 +3681,7 @@ static const ProtobufCEnumValueIndex esp_hosted_config_msg_type__enum_values_by_ { "TypeRespSetMacAddress", 23 }, { "TypeRespSetPowerSaveMode", 25 }, { "TypeRespSetSoftAPConfig", 13 }, + { "TypeRespSetVendorSpecificIE", 35 }, { "TypeRespSetWiFiMode", 5 }, { "TypeRespStopSoftAP", 17 }, }; @@ -3399,9 +3692,9 @@ const ProtobufCEnumDescriptor esp_hosted_config_msg_type__descriptor = "EspHostedConfigMsgType", "EspHostedConfigMsgType", "", - 34, + 36, esp_hosted_config_msg_type__enum_values_by_number, - 34, + 36, esp_hosted_config_msg_type__enum_values_by_name, 1, esp_hosted_config_msg_type__value_ranges, diff --git a/common/include/esp_hosted_config.pb-c.h b/common/include/esp_hosted_config.pb-c.h index a855a7f645..dd6568fd4e 100644 --- a/common/include/esp_hosted_config.pb-c.h +++ b/common/include/esp_hosted_config.pb-c.h @@ -45,6 +45,8 @@ typedef struct _EspHostedCmdOTAWrite EspHostedCmdOTAWrite; typedef struct _EspHostedRespOTAWrite EspHostedRespOTAWrite; typedef struct _EspHostedCmdOTAEnd EspHostedCmdOTAEnd; typedef struct _EspHostedRespOTAEnd EspHostedRespOTAEnd; +typedef struct _EspHostedCmdSetVendorSpecificIE EspHostedCmdSetVendorSpecificIE; +typedef struct _EspHostedRespSetVendorSpecificIE EspHostedRespSetVendorSpecificIE; typedef struct _EspHostedConfigPayload EspHostedConfigPayload; @@ -68,6 +70,19 @@ typedef enum _EspHostedStatus { ESP_HOSTED_STATUS__TYPE_CONNECTION_FAIL = 3 PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(ESP_HOSTED_STATUS) } EspHostedStatus; +typedef enum _EspHostedVendorIEType { + ESP_HOSTED_VENDOR_IETYPE__VND_IE_TYPE_BEACON = 0, + ESP_HOSTED_VENDOR_IETYPE__VND_IE_TYPE_PROBE_REQ = 1, + ESP_HOSTED_VENDOR_IETYPE__VND_IE_TYPE_PROBE_RESP = 2, + ESP_HOSTED_VENDOR_IETYPE__VND_IE_TYPE_ASSOC_REQ = 3, + ESP_HOSTED_VENDOR_IETYPE__VND_IE_TYPE_ASSOC_RESP = 4 + PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(ESP_HOSTED_VENDOR_IETYPE) +} EspHostedVendorIEType; +typedef enum _EspHostedIEID { + ESP_HOSTED_IEID__VND_IE_ID_0 = 0, + ESP_HOSTED_IEID__VND_IE_ID_1 = 1 + PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(ESP_HOSTED_IEID) +} EspHostedIEID; typedef enum _EspHostedConfigMsgType { ESP_HOSTED_CONFIG_MSG_TYPE__TypeCmdGetMACAddress = 0, ESP_HOSTED_CONFIG_MSG_TYPE__TypeRespGetMACAddress = 1, @@ -102,7 +117,9 @@ typedef enum _EspHostedConfigMsgType { ESP_HOSTED_CONFIG_MSG_TYPE__TypeCmdOTAWrite = 30, ESP_HOSTED_CONFIG_MSG_TYPE__TypeRespOTAWrite = 31, ESP_HOSTED_CONFIG_MSG_TYPE__TypeCmdOTAEnd = 32, - ESP_HOSTED_CONFIG_MSG_TYPE__TypeRespOTAEnd = 33 + ESP_HOSTED_CONFIG_MSG_TYPE__TypeRespOTAEnd = 33, + ESP_HOSTED_CONFIG_MSG_TYPE__TypeCmdSetVendorSpecificIE = 34, + ESP_HOSTED_CONFIG_MSG_TYPE__TypeRespSetVendorSpecificIE = 35 PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(ESP_HOSTED_CONFIG_MSG_TYPE) } EspHostedConfigMsgType; @@ -485,6 +502,34 @@ struct _EspHostedRespOTAEnd , 0,0 } +struct _EspHostedCmdSetVendorSpecificIE +{ + ProtobufCMessage base; + protobuf_c_boolean has_enable; + protobuf_c_boolean enable; + protobuf_c_boolean has_type; + EspHostedVendorIEType type; + protobuf_c_boolean has_idx; + EspHostedIEID idx; + protobuf_c_boolean has_vendor_ie_data; + ProtobufCBinaryData vendor_ie_data; +}; +#define ESP_HOSTED_CMD_SET_VENDOR_SPECIFIC_IE__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&esp_hosted_cmd_set_vendor_specific_ie__descriptor) \ + , 0,0, 0,0, 0,0, 0,{0,NULL} } + + +struct _EspHostedRespSetVendorSpecificIE +{ + ProtobufCMessage base; + protobuf_c_boolean has_resp; + int32_t resp; +}; +#define ESP_HOSTED_RESP_SET_VENDOR_SPECIFIC_IE__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&esp_hosted_resp_set_vendor_specific_ie__descriptor) \ + , 0,0 } + + typedef enum { ESP_HOSTED_CONFIG_PAYLOAD__PAYLOAD__NOT_SET = 0, ESP_HOSTED_CONFIG_PAYLOAD__PAYLOAD_CMD_GET_MAC_ADDRESS = 10, @@ -521,6 +566,8 @@ typedef enum { ESP_HOSTED_CONFIG_PAYLOAD__PAYLOAD_RESP_OTA_WRITE = 41, ESP_HOSTED_CONFIG_PAYLOAD__PAYLOAD_CMD_OTA_END = 42, ESP_HOSTED_CONFIG_PAYLOAD__PAYLOAD_RESP_OTA_END = 43, + ESP_HOSTED_CONFIG_PAYLOAD__PAYLOAD_CMD_SET_VENDOR_SPECIFIC_IE = 44, + ESP_HOSTED_CONFIG_PAYLOAD__PAYLOAD_RESP_SET_VENDOR_SPECIFIC_IE = 45, } EspHostedConfigPayload__PayloadCase; struct _EspHostedConfigPayload @@ -564,6 +611,8 @@ struct _EspHostedConfigPayload EspHostedRespOTAWrite *resp_ota_write; EspHostedCmdOTAEnd *cmd_ota_end; EspHostedRespOTAEnd *resp_ota_end; + EspHostedCmdSetVendorSpecificIE *cmd_set_vendor_specific_ie; + EspHostedRespSetVendorSpecificIE *resp_set_vendor_specific_ie; }; }; #define ESP_HOSTED_CONFIG_PAYLOAD__INIT \ @@ -1141,6 +1190,44 @@ EspHostedRespOTAEnd * void esp_hosted_resp_otaend__free_unpacked (EspHostedRespOTAEnd *message, ProtobufCAllocator *allocator); +/* EspHostedCmdSetVendorSpecificIE methods */ +void esp_hosted_cmd_set_vendor_specific_ie__init + (EspHostedCmdSetVendorSpecificIE *message); +size_t esp_hosted_cmd_set_vendor_specific_ie__get_packed_size + (const EspHostedCmdSetVendorSpecificIE *message); +size_t esp_hosted_cmd_set_vendor_specific_ie__pack + (const EspHostedCmdSetVendorSpecificIE *message, + uint8_t *out); +size_t esp_hosted_cmd_set_vendor_specific_ie__pack_to_buffer + (const EspHostedCmdSetVendorSpecificIE *message, + ProtobufCBuffer *buffer); +EspHostedCmdSetVendorSpecificIE * + esp_hosted_cmd_set_vendor_specific_ie__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void esp_hosted_cmd_set_vendor_specific_ie__free_unpacked + (EspHostedCmdSetVendorSpecificIE *message, + ProtobufCAllocator *allocator); +/* EspHostedRespSetVendorSpecificIE methods */ +void esp_hosted_resp_set_vendor_specific_ie__init + (EspHostedRespSetVendorSpecificIE *message); +size_t esp_hosted_resp_set_vendor_specific_ie__get_packed_size + (const EspHostedRespSetVendorSpecificIE *message); +size_t esp_hosted_resp_set_vendor_specific_ie__pack + (const EspHostedRespSetVendorSpecificIE *message, + uint8_t *out); +size_t esp_hosted_resp_set_vendor_specific_ie__pack_to_buffer + (const EspHostedRespSetVendorSpecificIE *message, + ProtobufCBuffer *buffer); +EspHostedRespSetVendorSpecificIE * + esp_hosted_resp_set_vendor_specific_ie__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void esp_hosted_resp_set_vendor_specific_ie__free_unpacked + (EspHostedRespSetVendorSpecificIE *message, + ProtobufCAllocator *allocator); /* EspHostedConfigPayload methods */ void esp_hosted_config_payload__init (EspHostedConfigPayload *message); @@ -1252,6 +1339,12 @@ typedef void (*EspHostedCmdOTAEnd_Closure) typedef void (*EspHostedRespOTAEnd_Closure) (const EspHostedRespOTAEnd *message, void *closure_data); +typedef void (*EspHostedCmdSetVendorSpecificIE_Closure) + (const EspHostedCmdSetVendorSpecificIE *message, + void *closure_data); +typedef void (*EspHostedRespSetVendorSpecificIE_Closure) + (const EspHostedRespSetVendorSpecificIE *message, + void *closure_data); typedef void (*EspHostedConfigPayload_Closure) (const EspHostedConfigPayload *message, void *closure_data); @@ -1263,6 +1356,8 @@ typedef void (*EspHostedConfigPayload_Closure) extern const ProtobufCEnumDescriptor esp_hosted_encryption_mode__descriptor; extern const ProtobufCEnumDescriptor esp_hosted_status__descriptor; +extern const ProtobufCEnumDescriptor esp_hosted_vendor_ietype__descriptor; +extern const ProtobufCEnumDescriptor esp_hosted_ieid__descriptor; extern const ProtobufCEnumDescriptor esp_hosted_config_msg_type__descriptor; extern const ProtobufCMessageDescriptor esp_hosted_cmd_get_mac_address__descriptor; extern const ProtobufCMessageDescriptor esp_hosted_resp_get_mac_address__descriptor; @@ -1294,6 +1389,8 @@ extern const ProtobufCMessageDescriptor esp_hosted_cmd_otawrite__descriptor; extern const ProtobufCMessageDescriptor esp_hosted_resp_otawrite__descriptor; extern const ProtobufCMessageDescriptor esp_hosted_cmd_otaend__descriptor; extern const ProtobufCMessageDescriptor esp_hosted_resp_otaend__descriptor; +extern const ProtobufCMessageDescriptor esp_hosted_cmd_set_vendor_specific_ie__descriptor; +extern const ProtobufCMessageDescriptor esp_hosted_resp_set_vendor_specific_ie__descriptor; extern const ProtobufCMessageDescriptor esp_hosted_config_payload__descriptor; PROTOBUF_C__END_DECLS diff --git a/common/proto/esp_hosted_config.proto b/common/proto/esp_hosted_config.proto index a28b3d32d3..63dec1e6dc 100644 --- a/common/proto/esp_hosted_config.proto +++ b/common/proto/esp_hosted_config.proto @@ -164,6 +164,30 @@ message EspHostedRespOTAEnd { int32 resp = 1; } +enum EspHostedVendorIEType { + VND_IE_TYPE_BEACON = 0; + VND_IE_TYPE_PROBE_REQ = 1; + VND_IE_TYPE_PROBE_RESP = 2; + VND_IE_TYPE_ASSOC_REQ = 3; + VND_IE_TYPE_ASSOC_RESP = 4; +} + +enum EspHostedIEID { + VND_IE_ID_0 = 0; + VND_IE_ID_1 = 1; +} + +message EspHostedCmdSetVendorSpecificIE { + bool enable = 1; + EspHostedVendorIEType type = 2; + EspHostedIEID idx = 3; + bytes vendor_ie_data = 4; +} + +message EspHostedRespSetVendorSpecificIE { + int32 resp = 1; +} + enum EspHostedConfigMsgType { TypeCmdGetMACAddress = 0; TypeRespGetMACAddress = 1; @@ -199,6 +223,8 @@ enum EspHostedConfigMsgType { TypeRespOTAWrite = 31; TypeCmdOTAEnd = 32; TypeRespOTAEnd = 33; + TypeCmdSetVendorSpecificIE = 34; + TypeRespSetVendorSpecificIE = 35; } message EspHostedConfigPayload { @@ -238,5 +264,7 @@ message EspHostedConfigPayload { EspHostedRespOTAWrite resp_ota_write = 41; EspHostedCmdOTAEnd cmd_ota_end = 42; EspHostedRespOTAEnd resp_ota_end = 43; + EspHostedCmdSetVendorSpecificIE cmd_set_vendor_specific_ie = 44; + EspHostedRespSetVendorSpecificIE resp_set_vendor_specific_ie = 45; } } diff --git a/docs/c_api.md b/docs/c_api.md index 1b695e5b20..b1ed304433 100644 --- a/docs/c_api.md +++ b/docs/c_api.md @@ -112,6 +112,24 @@ BSSID or MAC address of station of length 17. ex. "XX:XX:XX:XX:XX:XX". - `int rssi` : RSSI signal strength of station. +--- + +_struct_ `vendor_ie_data_t`: + +This contains vendor information element to ESP32 softAP. + +*Public Members* +- `uint8_t element_id` : +Should be set to WIFI_VENDOR_IE_ELEMENT_ID (0xDD). +- `uint8_t length` : +Length of all bytes in the element data (payload) following this field. Minimum 4(offset for `vendor_oui` and `vendor_oui_type` field). +- `uint8_t vendor_oui[3]` : +Vendor identifier (OUI). +- `uint8_t vendor_oui_type` : +Vendor-specific OUI type. +- `uint8_t payload[0]` : +Payload. Length is equal to value in 'length' field, minus 4. + --- ## 2. Functions @@ -124,7 +142,7 @@ This is used to get the MAC address of station or softAP interface of ESP32 - `mode` : - `WIFI_MODE_STA` : station - `WIFI_MODE_AP` : softAP -- `mac` : +- `mac` : String in form of "XX:XX:XX:XX:XX:XX" in success case. It should be large enough to store string in form "XX:XX:XX:XX:XX:XX" @@ -420,6 +438,27 @@ User should free `esp_hosted_wifi_connected_stations_list` handler after use. --- +### 2.15 `int wifi_set_vendor_specific_ie(bool enable, wifi_vendor_ie_type_t type, wifi_vendor_ie_id_t idx, void* vnd_ie, uint16_t vnd_ie_size)` +Function set 802.11 Vendor-Specific Information Element. This function needs to get called before starting of ESP32 softAP. + +#### Parameters +- `enable` : +If true, specified IE is enabled. If false, specified IE is removed. +- `type` : +Information Element type. Determines the frame type to associate with the IE. Uses `wifi_vendor_ie_type_t` enum. +- `idx` : +Index to set or clear. Each IE type can be associated with up to two elements (indices 0 & 1). Uses `wifi_vendor_ie_id_t` enum. +- `vnd_ie` : +Pointer to vendor specific element data. First 6 bytes should be a header with fields matching vendor_ie_data_t. If enable is false, this argument is ignored and can be NULL. +- `vnd_ie_size` : +size of vnd_ie data + +#### Return +- 0 : SUCCESS +- -1 : FAILURE + +--- + ## 3. Enumerations ### 3.1 _enum_ `wifi_mode_t` @@ -473,6 +512,27 @@ Invalid power save mode --- +### 3.5 _enum_ `wifi_vendor_ie_type_t` : + +Vendor information element type. Determines the frame type that the IE will be associated with. +_Values_ : +- `WIFI_VND_IE_TYPE_BEACON` = 0 : Type beacon +- `WIFI_VND_IE_TYPE_PROBE_REQ` : Type probe request +- `WIFI_VND_IE_TYPE_PROBE_RESP` : Type probe response +- `WIFI_VND_IE_TYPE_ASSOC_REQ` : Type association request +- `WIFI_VND_IE_TYPE_ASSOC_RESP` : Type association response + +--- + +### 3.6 _enum_ `wifi_vendor_ie_id_t` : + +Vendor Information Element index. Each IE type can have up to two associated vendor ID elements. +_Values_ : +- `WIFI_VND_IE_ID_0` = 0 : ID 0 +- `WIFI_VND_IE_ID_1` : ID 1 + +--- + ## 4. Unions ### 4.1 _union_ `esp_hosted_control_config_t` : diff --git a/docs/c_demo.md b/docs/c_demo.md index 66bfe9b3db..77ef72f65b 100644 --- a/docs/c_demo.md +++ b/docs/c_demo.md @@ -10,6 +10,7 @@ | ap_stop | Stop ESP32 softAP stop and down `ethap0` interface | | scan | Scan external access points | | sta_list | List external stations connected to softAP | +| ap_vendor_ie | Set vendor information element for ESP32 softAP | It uses APIs present in [test_api.c](../host/linux/host_control/c_support/test_api.c). User should first modify configuration parameters in [test_config.h](../host/linux/host_control/c_support/test_config.h). Then run `make` in [c_support](../host/linux/host_control/c_support) to compile `test.c`. @@ -18,7 +19,7 @@ Please execute `test.out` as below. ``` ex. -sudo ./test.out sta_connect sta_disconnect ap_start ap_stop scan sta_list +sudo ./test.out sta_connect sta_disconnect ap_vendor_ie ap_start ap_stop scan sta_list ``` Note: * After `sta_connect`, User needs to run DHCP client to obtain IP address from an external AP. Then network data path will be open for higher applications to use `ethsta0` interface for data communication. For an example as below. @@ -29,6 +30,11 @@ sudo dhclient ethsta0 -r sudo dhclient ethsta0 -v ``` +* `ap_vendor_ie` needs to get called before starting of ESP32 softAP, please edit function `test_set_vendor_specific_ie` in `test_api.c`. +`ap_vendor_ie` should get configured only once till ESP32 reboot. To remove +previous configuration set `enable` flag to `false` in `wifi_set_vendor_specific_ie` API. +After that re-configuration possible of Vendor IE. + * After `ap_start` to start data connection, set up a DHCP server on the Raspberry Pi, or configure a static IP address for AP interface (`ethap0`). For an example as below: ``` @@ -50,6 +56,7 @@ sudo ifconfig ethap0 192.168.4.5 | ap_stop | Stop ESP32 softAP stop and down `ethap0` interface | | scan | Scan external access points | | sta_list | List external stations connected to softAP | +| ap_vendor_ie | Set vendor information element for ESP32 softAP | Run `make stress` in [c_support](../host/linux/host_control/c_support) directory to compile `stress.c`. @@ -58,5 +65,5 @@ Please execute `stress.out` as below. ``` ex. -sudo ./stress.out 1 sta_connect sta_disconnect ap_start ap_stop scan sta_list +sudo ./stress.out 1 sta_connect sta_disconnect ap_vendor_ie ap_start ap_stop scan sta_list ``` diff --git a/esp/esp_driver/network_adapter/main/slave_commands.c b/esp/esp_driver/network_adapter/main/slave_commands.c index b078f33a86..8773db7158 100644 --- a/esp/esp_driver/network_adapter/main/slave_commands.c +++ b/esp/esp_driver/network_adapter/main/slave_commands.c @@ -1543,6 +1543,43 @@ static esp_err_t cmd_ota_end_handler (EspHostedConfigPayload *req, return ESP_OK; } +// Function vendor specific ie +static esp_err_t cmd_set_vender_specific_ie_handler (EspHostedConfigPayload *req, + EspHostedConfigPayload *resp, void *priv_data) +{ + esp_err_t ret = ESP_OK; + EspHostedRespSetVendorSpecificIE *resp_payload = NULL; + + if (!req || !resp || !req->cmd_set_vendor_specific_ie || + !req->cmd_set_vendor_specific_ie->vendor_ie_data.len || + !req->cmd_set_vendor_specific_ie->vendor_ie_data.data) { + ESP_LOGE(TAG, "Invalid parameters"); + return ESP_FAIL; + } + + resp_payload = (EspHostedRespSetVendorSpecificIE *) + calloc(1,sizeof(EspHostedRespSetVendorSpecificIE)); + if (!resp_payload) { + ESP_LOGE(TAG,"Failed to allocate memory"); + return ESP_ERR_NO_MEM; + } + esp_hosted_resp_set_vendor_specific_ie__init(resp_payload); + resp->payload_case = ESP_HOSTED_CONFIG_PAYLOAD__PAYLOAD_RESP_SET_VENDOR_SPECIFIC_IE; + resp->resp_set_vendor_specific_ie = resp_payload; + resp_payload->has_resp = true; + + ret = esp_wifi_set_vendor_ie(req->cmd_set_vendor_specific_ie->enable, + req->cmd_set_vendor_specific_ie->type, req->cmd_set_vendor_specific_ie->idx, + req->cmd_set_vendor_specific_ie->vendor_ie_data.data); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to set vendor information element %d \n", ret); + resp_payload->resp = FAILURE; + return ESP_OK; + } + resp_payload->resp = SUCCESS; + return ESP_OK; +} + static esp_hosted_config_cmd_t cmd_table[] = { { .cmd_num = ESP_HOSTED_CONFIG_MSG_TYPE__TypeCmdGetMACAddress , @@ -1612,6 +1649,10 @@ static esp_hosted_config_cmd_t cmd_table[] = { .cmd_num = ESP_HOSTED_CONFIG_MSG_TYPE__TypeCmdOTAEnd, .command_handler = cmd_ota_end_handler }, + { + .cmd_num = ESP_HOSTED_CONFIG_MSG_TYPE__TypeCmdSetVendorSpecificIE, + .command_handler = cmd_set_vender_specific_ie_handler + }, }; @@ -1761,6 +1802,10 @@ static void esp_hosted_config_cleanup(EspHostedConfigPayload *resp) mem_free(resp->resp_ota_end); break; } + case (ESP_HOSTED_CONFIG_MSG_TYPE__TypeRespSetVendorSpecificIE) : { + mem_free(resp->resp_set_vendor_specific_ie); + break; + } default: ESP_LOGE(TAG, "Unsupported response type"); break; diff --git a/host/host_common/commands.c b/host/host_common/commands.c index 173f852163..a6893e09cd 100644 --- a/host/host_common/commands.c +++ b/host/host_common/commands.c @@ -1136,6 +1136,95 @@ int wifi_get_power_save_mode (int *power_save_mode) return FAILURE; } +// Function set 802.11 Vendor-Specific Information Element +int wifi_set_vendor_specific_ie(bool enable, wifi_vendor_ie_type_t type, + wifi_vendor_ie_id_t idx, void* vnd_ie, uint16_t vnd_ie_size) +{ + EspHostedConfigPayload req, *resp = NULL; + uint32_t tx_len = 0, rx_len = 0; + uint8_t *tx_data = NULL, *rx_data = NULL; + + if ((type > WIFI_VND_IE_TYPE_ASSOC_RESP) || (type < WIFI_VND_IE_TYPE_BEACON)) { + command_log("Invalid vendor ie type \n"); + return FAILURE; + } + + if ((idx > WIFI_VND_IE_ID_1) || (idx < WIFI_VND_IE_ID_0)) { + command_log("Invalid vendor ie ID index \n"); + return FAILURE; + } + if (!vnd_ie) { + command_log("Invalid vendor IE buffer \n"); + return FAILURE; + } + + esp_hosted_config_payload__init (&req); + req.has_msg = true; + req.msg = ESP_HOSTED_CONFIG_MSG_TYPE__TypeCmdSetVendorSpecificIE; + req.payload_case = ESP_HOSTED_CONFIG_PAYLOAD__PAYLOAD_CMD_SET_VENDOR_SPECIFIC_IE; + + EspHostedCmdSetVendorSpecificIE *req_payload = (EspHostedCmdSetVendorSpecificIE *) + esp_hosted_calloc(1, sizeof(EspHostedCmdSetVendorSpecificIE)); + if (!req_payload) { + command_log("Failed to allocate memory for req_payload\n"); + return FAILURE; + } + esp_hosted_cmd_set_vendor_specific_ie__init(req_payload); + req_payload->has_enable = true; + req_payload->enable = enable; + req_payload->has_type = true; + req_payload->type = type; + req_payload->has_idx = true; + req_payload->idx = idx; + req_payload->has_vendor_ie_data = true; + req_payload->vendor_ie_data.len = vnd_ie_size; + req_payload->vendor_ie_data.data = vnd_ie; + req.cmd_set_vendor_specific_ie = req_payload; + tx_len = esp_hosted_config_payload__get_packed_size(&req); + if (!tx_len) { + command_log("Invalid tx length\n"); + goto err3; + } + + tx_data = (uint8_t *)esp_hosted_calloc(1, tx_len); + if (!tx_data) { + command_log("Failed to allocate memory for tx_data\n"); + goto err3; + } + + esp_hosted_config_payload__pack(&req, tx_data); + + rx_data = transport_pserial_data_handler(tx_data, tx_len, + TIMEOUT_PSERIAL_RESP, &rx_len); + if (!rx_data || !rx_len) { + command_log("Failed to process rx_data\n"); + goto err2; + } + + resp = esp_hosted_config_payload__unpack(NULL, rx_len, rx_data); + if ((!resp) || (!resp->resp_set_vendor_specific_ie)) { + command_log("Failed to unpack rx_data\n"); + goto err1; + } + + if (resp->resp_set_vendor_specific_ie->resp) { + command_log("Failed to set vendor specific information element\n"); + goto err1; + } + + mem_free(tx_data); + mem_free(rx_data); + mem_free(req_payload); + return SUCCESS; +err1: + mem_free(rx_data); +err2: + mem_free(tx_data); +err3: + mem_free(req_payload); + return FAILURE; +} + // Function performs an OTA begin for ESP32 int esp_ota_begin() { @@ -1318,3 +1407,4 @@ int esp_ota_end() mem_free(tx_data); return FAILURE; } + diff --git a/host/host_common/include/commands.h b/host/host_common/include/commands.h index ccb71ea02a..c315ad663e 100644 --- a/host/host_common/include/commands.h +++ b/host/host_common/include/commands.h @@ -50,6 +50,19 @@ typedef enum { WIFI_PS_INVALID, } wifi_ps_type_t; +typedef enum { + WIFI_VND_IE_TYPE_BEACON = 0, + WIFI_VND_IE_TYPE_PROBE_REQ, + WIFI_VND_IE_TYPE_PROBE_RESP, + WIFI_VND_IE_TYPE_ASSOC_REQ, + WIFI_VND_IE_TYPE_ASSOC_RESP, +} wifi_vendor_ie_type_t; + +typedef enum { + WIFI_VND_IE_ID_0, + WIFI_VND_IE_ID_1, +} wifi_vendor_ie_id_t; + typedef struct { uint8_t ssid[SSID_LENGTH]; uint8_t pwd[PASSWORD_LENGTH]; @@ -90,6 +103,13 @@ typedef struct { int rssi; } esp_hosted_wifi_connected_stations_list; +typedef struct { + uint8_t element_id; /**< Should be set to WIFI_VENDOR_IE_ELEMENT_ID (0xDD) */ + uint8_t length; /**< Length of all bytes in the element data following this field. Minimum 4. */ + uint8_t vendor_oui[3]; /**< Vendor identifier (OUI). */ + uint8_t vendor_oui_type; /**< Vendor-specific OUI type. */ + uint8_t payload[0]; /**< Payload. Length is equal to value in 'length' field, minus 4. */ +} vendor_ie_data_t; /* wifi get mac function returns status SUCCESS(0) or FAILURE(-1) * Input parameter @@ -266,6 +286,19 @@ int wifi_set_power_save_mode(int power_save_mode); */ int wifi_get_power_save_mode(int *power_save_mode); +/* + * wifi set vendor specific ie function set 802.11 Vendor-Specific Information Element + * returns SUCCESS(0) or FAILURE(-1) + * Input parameter: + * enable : If true, specified IE is enabled. If false, specified IE is removed. + * type : Information Element type. Determines the frame type to associate with the IE. + * idx : Index to set or clear. Each IE type can be associated with up to two elements (indices 0 & 1). + * vnd_ie : Pointer to vendor specific element data. First 6 bytes should be a header with fields matching vendor_ie_data_t. If enable is false, this argument is ignored and can be NULL. + * vnd_ie_size : size of vnd_ie data + */ +int wifi_set_vendor_specific_ie(bool enable, wifi_vendor_ie_type_t type, + wifi_vendor_ie_id_t idx, void* vnd_ie, uint16_t vnd_ie_size); + /* * esp ota begin function performs an OTA begin operation for ESP32 * which sets partition for OTA write and erase it. @@ -290,5 +323,4 @@ int esp_ota_write(uint8_t* ota_data, uint32_t ota_data_len); * returns SUCCESS(0) or FAILURE(-1) */ int esp_ota_end(); - #endif diff --git a/host/linux/host_control/c_support/test.c b/host/linux/host_control/c_support/test.c index 72b75ec4bf..506c1b1c59 100644 --- a/host/linux/host_control/c_support/test.c +++ b/host/linux/host_control/c_support/test.c @@ -32,10 +32,10 @@ int main(int argc, char *argv[]) exit(0); } - if (!argc) { - printf("Usage: %s [%s] [%s] [%s] [%s] [%s] [%s]\n", argv[0], + if (argc == 1) { + printf("Usage: %s [%s] [%s] [%s] [%s] [%s] [%s] [%s] [%s]\n", argv[0], STA_CONNECT, STA_DISCONNECT, AP_START, AP_STOP, - SCAN, STA_LIST); + SCAN, STA_LIST, OTA, AP_VENDOR_IE); return -1; } @@ -54,12 +54,15 @@ int main(int argc, char *argv[]) test_softap_mode_connected_clients_info(); else if (0 == strncasecmp(OTA, argv[i], sizeof(OTA))) test_ota(argv[i+1]); + else if (0 == strncasecmp(AP_VENDOR_IE, argv[i], sizeof(AP_VENDOR_IE))) + test_set_vendor_specific_ie(); else if ((0 == strncasecmp("--help", argv[i], sizeof("--help"))) || (0 == strncasecmp("-h", argv[i], sizeof("-h")))) { - printf("Usage: %s [%s] [%s] [%s] [%s] [%s] [%s]\n", argv[0], - STA_CONNECT, STA_DISCONNECT, AP_START, AP_STOP, SCAN, STA_LIST); + printf("Usage: %s [%s] [%s] [%s] [%s] [%s] [%s] [%s] [%s]\n", argv[0], + STA_CONNECT, STA_DISCONNECT, AP_START, AP_STOP, SCAN, STA_LIST, + OTA, AP_VENDOR_IE); return(0); - } + } } return 0; diff --git a/host/linux/host_control/c_support/test_api.c b/host/linux/host_control/c_support/test_api.c index 199af52e4b..95732375b2 100644 --- a/host/linux/host_control/c_support/test_api.c +++ b/host/linux/host_control/c_support/test_api.c @@ -24,12 +24,19 @@ #include #include "test_api.h" -#define MAC_LEN 7 -#define MAC_STR_LEN 17 +#define MAC_LEN 7 +#define MAC_STR_LEN 17 -#define STA_INTERFACE "ethsta0" -#define AP_INTERFACE "ethap0" -#define MAX_INTERFACE_LEN 10 +#define STA_INTERFACE "ethsta0" +#define AP_INTERFACE "ethap0" +#define MAX_INTERFACE_LEN 10 + +#define WIFI_VENDOR_IE_ELEMENT_ID 0xDD +#define OFFSET 4 +#define VENDOR_OUI_0 1 +#define VENDOR_OUI_1 2 +#define VENDOR_OUI_2 3 +#define VENDOR_OUI_TYPE 22 // Function converts mac string to byte stream static int convert_mac_to_bytes(uint8_t *out, size_t out_len, char *s) @@ -666,3 +673,43 @@ int test_ota(char* image_path) #endif return SUCCESS; } + +int test_set_vendor_specific_ie() +{ + char *data = "Example vendor IE data"; + int vnd_ie_size = sizeof(vendor_ie_data_t)+strlen(data)+1; + vendor_ie_data_t *vnd_ie = (vendor_ie_data_t *)malloc(vnd_ie_size); + if (!vnd_ie) { + printf("Failed to allocate memory for vnd_ie\n"); + return FAILURE; + } + + vnd_ie->element_id = WIFI_VENDOR_IE_ELEMENT_ID; + vnd_ie->length = strlen(data) + OFFSET; + if (vnd_ie->length < OFFSET) { + printf("Length should not be less than %d bytes \n", OFFSET); + free(vnd_ie); + return FAILURE; + } + vnd_ie->vendor_oui[0] = VENDOR_OUI_0; + vnd_ie->vendor_oui[1] = VENDOR_OUI_1; + vnd_ie->vendor_oui[2] = VENDOR_OUI_2; + vnd_ie->vendor_oui_type = VENDOR_OUI_TYPE; + memcpy(vnd_ie->payload, data, strlen(data)); + + int ret = wifi_set_vendor_specific_ie(true, WIFI_VND_IE_TYPE_BEACON, + WIFI_VND_IE_ID_0, vnd_ie, vnd_ie_size); + +#ifdef TEST_DEBUG_PRINTS + printf("==== %s =>\n",__func__); + if (ret == SUCCESS) { + printf("Success in set vendor specific ie\n"); + } else { + printf("Failed to set vendor specific ie\n"); + } + printf("====\n\n"); +#endif + + free(vnd_ie); + return ret; +} diff --git a/host/linux/host_control/c_support/test_api.h b/host/linux/host_control/c_support/test_api.h index 7feb0faec9..8c987fea59 100644 --- a/host/linux/host_control/c_support/test_api.h +++ b/host/linux/host_control/c_support/test_api.h @@ -74,4 +74,6 @@ int test_ota_write(); int test_ota_end(); int test_ota(char* image_path); + +int test_set_vendor_specific_ie(); #endif diff --git a/host/linux/host_control/c_support/test_config.h b/host/linux/host_control/c_support/test_config.h index 95eed2d529..d37d1cd511 100644 --- a/host/linux/host_control/c_support/test_config.h +++ b/host/linux/host_control/c_support/test_config.h @@ -28,6 +28,7 @@ #define SCAN "scan" #define STA_LIST "sta_list" #define OTA "ota" +#define AP_VENDOR_IE "ap_vendor_ie" #define MAC_LENGTH 18 #define SSID_LENGTH 32 From 812cde7d4a22de87cba590d39d942d9b8b5dd7e0 Mon Sep 17 00:00:00 2001 From: Mangesh Malusare Date: Tue, 9 Nov 2021 19:49:22 +0530 Subject: [PATCH 27/40] Fix for throughput drop in TCP Tx case -- In one of the corner case where packet length is 500 bytes, the length of the interface header doesn't get accounted in skb->len -- This leads to checksum failure in firmware and corresponding packet is dropped Fix: -- Added interface header len in skb->len Signed-off-by: Mangesh Malusare --- host/linux/host_driver/esp32/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/host/linux/host_driver/esp32/main.c b/host/linux/host_driver/esp32/main.c index 4ee6a7d225..943c54f44a 100644 --- a/host/linux/host_driver/esp32/main.c +++ b/host/linux/host_driver/esp32/main.c @@ -280,7 +280,7 @@ static int process_tx_packet (struct sk_buff *skb) /* Populate new SKB */ skb_copy_from_linear_data(skb, pos, skb->len); - skb_put(new_skb, skb->len); + skb_put(new_skb, skb->len + pad_len); /* Replace old SKB */ dev_kfree_skb_any(skb); From cf0939730f2e0c55d946cbf951fe87883ce0b350 Mon Sep 17 00:00:00 2001 From: Sonika Rathi Date: Tue, 2 Nov 2021 15:54:05 +0530 Subject: [PATCH 28/40] Fix for softap ioctl in python --- docs/c_api.md | 40 ++++ docs/python_api.md | 29 +++ host/host_common/commands.c | 175 +++++++++++++++ host/host_common/include/commands.h | 36 +++- host/linux/host_control/c_support/test_api.c | 202 +++++++----------- .../python_support/commands_lib.py | 64 +++++- .../python_support/commands_map_py_to_c.py | 15 ++ .../host_control/python_support/test_api.py | 149 ++++++------- 8 files changed, 500 insertions(+), 210 deletions(-) diff --git a/docs/c_api.md b/docs/c_api.md index b1ed304433..f7936cd373 100644 --- a/docs/c_api.md +++ b/docs/c_api.md @@ -454,11 +454,51 @@ Pointer to vendor specific element data. First 6 bytes should be a header with f size of vnd_ie data #### Return + +- 0 : SUCCESS +- -1 : FAILURE + +--- + +### 2.16 `int create_socket(int domain, int type, int protocol, int *sock)` + +This is used to create an endpoint for communication + +#### Parameters + +- `domain` : +The domain argument specifies a communication domain (like AF_INET, AF_INET6), this selects the protocol family which will be used for communication. These families are defined in . +- `type` : +This specifies the communication semantics (like SOCK_DGRAM, SOCK_STREAM). +- `protocol` : +This specifies a particular protocol to be used with the socket. Generally protocol value should be 0, please refer man socket for more details. +- `sock` : +This will return file descriptor (integer number) that refers to that endpoint for the new socket on success or -1 on failure. + +#### Return + - 0 : SUCCESS - -1 : FAILURE --- +### 2.17 `int close_socket(int sock)` + +This is used to close an endpoint of the communication + +#### Parameters + +- `sock` : +This specifies the file descriptor of the endpoint/socket to be closed + +#### Return + +- 0 : SUCCESS +- -1 : FAILURE + +--- + + ## 3. Enumerations ### 3.1 _enum_ `wifi_mode_t` diff --git a/docs/python_api.md b/docs/python_api.md index 3ecc7f34a0..eef7286bdd 100644 --- a/docs/python_api.md +++ b/docs/python_api.md @@ -274,3 +274,32 @@ RSSI signal strength - "failure" string --- + +## 15. `create_socket` +This function creates an endpoint for communication + +### Parameters +- `domain` : +This specifies a communication domain (like AF_INET, AF_INET6). +- `types` : +This specifies the communication semantics (like SOCK_DGRAM, SOCK_STREAM). +- `protocol` : +This specifies a particular protocol to be used with the socket. Generally protocol value should be 0, please refer socket documentation for more details. + +### Return +- File descriptor (integer number) that refers to that endpoint/socket +- "failure" string: if failed. + +--- + +## 16. `close_socket` +This function closes endpoint of the communication + +### Parameters +- `sock` : +This specifies the file descriptor of the endpoint/socket to be closed + +### Return +- "success" or "failure" string + +--- diff --git a/host/host_common/commands.c b/host/host_common/commands.c index a6893e09cd..d2cf015eae 100644 --- a/host/host_common/commands.c +++ b/host/host_common/commands.c @@ -8,6 +8,14 @@ #include "platform_wrapper.h" #include "esp_hosted_config.pb-c.h" +#ifndef STM32F469xx +#include +#include +#include +#include +#include +#endif + #ifdef STM32F469xx #include "common.h" #define command_log(...) printf(__VA_ARGS__); printf("\r"); @@ -16,6 +24,12 @@ #define min(X, Y) (((X) < (Y)) ? (X) : (Y)) #endif +#ifndef STM32F469xx +#define MAX_INTERFACE_LEN IFNAMSIZ +#define MAC_LEN 7 +#define MAC_STR_LEN 17 +#endif + #define SUCCESS 0 #define FAILURE -1 #define MAC_LENGTH 19 @@ -46,6 +60,40 @@ } \ } +#ifndef STM32F469xx +// Function converts mac string to byte stream +static int convert_mac_to_bytes(uint8_t *out, size_t out_len, char *s) +{ + int mac[MAC_LEN] = {0}; + int num_bytes = 0; + if (!s || (strlen(s) < MAC_STR_LEN) || (out_len < MAC_LEN)) { + return FAILURE; + } + + num_bytes = sscanf(s, "%2x:%2x:%2x:%2x:%2x:%2x", + &mac[0],&mac[1], &mac[2], &mac[3], &mac[4], &mac[5]); + + if ((num_bytes < (MAC_LEN - 1)) || + (mac[0] > 0xFF) || + (mac[1] > 0xFF) || + (mac[2] > 0xFF) || + (mac[3] > 0xFF) || + (mac[4] > 0xFF) || + (mac[5] > 0xFF)) { + return FAILURE; + } + + out[0] = mac[0]&0xff; + out[1] = mac[1]&0xff; + out[2] = mac[2]&0xff; + out[3] = mac[3]&0xff; + out[4] = mac[4]&0xff; + out[5] = mac[5]&0xff; + return SUCCESS; +} +#endif + + // Function returns MAC address of ESP32 station/softap int wifi_get_mac (int mode, char *mac) { @@ -1408,3 +1456,130 @@ int esp_ota_end() return FAILURE; } +#ifndef STM32F469xx + +// Function ups in given interface +int interface_up(int sockfd, char* iface) +{ + int ret = SUCCESS; + struct ifreq req = {0}; + size_t if_name_len = strnlen(iface, MAX_INTERFACE_LEN-1); + + if (!iface) { + command_log("Invalid parameter\n"); + return FAILURE; + } + + if (if_name_len < sizeof(req.ifr_name)) { + memcpy(req.ifr_name,iface,if_name_len); + req.ifr_name[if_name_len]='\0'; + } else { + printf("Failed: Max interface length allowed is %u \n", sizeof(req.ifr_name)-1); + return FAILURE; + } + + req.ifr_flags |= IFF_UP; + ret = ioctl(sockfd, SIOCSIFFLAGS, &req); + if (ret < 0) { + return FAILURE; + } + return SUCCESS; +} + +// Function downs in given interface +int interface_down(int sockfd, char* iface) +{ + int ret = SUCCESS; + struct ifreq req = {0}; + size_t if_name_len = strnlen(iface, MAX_INTERFACE_LEN-1); + + if (!iface) { + command_log("Invalid parameter\n"); + return FAILURE; + } + + if (if_name_len < sizeof(req.ifr_name)) { + memcpy(req.ifr_name,iface,if_name_len); + req.ifr_name[if_name_len]='\0'; + } else { + printf("Failed: Max interface length allowed is %u \n", sizeof(req.ifr_name)-1); + return FAILURE; + } + + req.ifr_flags &= ~IFF_UP; + ret = ioctl(sockfd, SIOCSIFFLAGS, &req); + if (ret < 0) { + return FAILURE; + } + return SUCCESS; +} + +// Function sets mac address to given interface +int set_hw_addr(int sockfd, char* iface, char* mac) +{ + int ret = SUCCESS; + struct ifreq req = {0}; + char mac_bytes[MAC_LEN] = ""; + size_t if_name_len = strnlen(iface, MAX_INTERFACE_LEN-1); + + if (!iface || !mac) { + command_log("Invalid parameter\n"); + return FAILURE; + } + + if (if_name_len < sizeof(req.ifr_name)) { + memcpy(req.ifr_name,iface,if_name_len); + req.ifr_name[if_name_len]='\0'; + } else { + printf("Failed: Max interface length allowed is %u \n", sizeof(req.ifr_name)-1); + return FAILURE; + } + + memset(mac_bytes, '\0', MAC_LEN); + ret = convert_mac_to_bytes((uint8_t *)&mac_bytes, sizeof(mac_bytes), mac); + + if (ret) { + printf("Failed to convert mac address \n"); + return FAILURE; + } + + req.ifr_hwaddr.sa_family = ARPHRD_ETHER; + memcpy(req.ifr_hwaddr.sa_data, mac_bytes, MAC_LEN); + ret = ioctl(sockfd, SIOCSIFHWADDR, &req); + + if (ret < 0) { + return FAILURE; + } + return SUCCESS; +} + +// Function creates an endpoint for communication and returns a file descriptor (integer number) that refers to that endpoint +int create_socket(int domain, int type, int protocol, int *sock) +{ + if (!sock) { + command_log("Invalid parameter\n"); + return FAILURE; + } + + *sock = socket(domain, type, protocol); + if (*sock < 0) + { + printf("Failure to open socket\n"); + return FAILURE; + } + return SUCCESS; +} + +// Function closes an endpoint for communication +int close_socket(int sock) +{ + int ret; + ret = close(sock); + if (ret < 0) { + printf("Failure to close socket\n"); + return FAILURE; + } + return SUCCESS; +} + +#endif diff --git a/host/host_common/include/commands.h b/host/host_common/include/commands.h index c315ad663e..2a7f62aa9b 100644 --- a/host/host_common/include/commands.h +++ b/host/host_common/include/commands.h @@ -323,4 +323,38 @@ int esp_ota_write(uint8_t* ota_data, uint32_t ota_data_len); * returns SUCCESS(0) or FAILURE(-1) */ int esp_ota_end(); -#endif + +#ifndef STM32F469xx +/* + * interface_up function ups the given interface + * returns SUCCESS(0) or FAILURE(-1) + */ +int interface_up(int sockfd, char* iface); + +/* + * interface_down function downs the given interface + * returns SUCCESS(0) or FAILURE(-1) + */ +int interface_down(int sockfd, char* iface); + +/* + * Function sets mac address to the given interface + * returns SUCCESS(0) or FAILURE(-1) + */ +int set_hw_addr(int sockfd, char* iface, char* mac); + +/* + * function creates an endpoint for communication and returns a file descriptor (integer number) that refers to that endpoint + * function returns the status SUCCESS(0) or FAILURE(-1) + * int *sock : User should get file descriptor for the new socket on success + */ +int create_socket(int domain, int type, int protocol, int *sock); + +/* + * function closes an endpoint of communication. + * function returns the status SUCCESS(0) or FAILURE(-1) + * int sock : file descrepter of the socket to be closed + */ +int close_socket(int sock); +#endif /* STM32F469xx */ +#endif /* __COMMANDS_H */ diff --git a/host/linux/host_control/c_support/test_api.c b/host/linux/host_control/c_support/test_api.c index 95732375b2..5a6868bd8b 100644 --- a/host/linux/host_control/c_support/test_api.c +++ b/host/linux/host_control/c_support/test_api.c @@ -24,12 +24,8 @@ #include #include "test_api.h" -#define MAC_LEN 7 -#define MAC_STR_LEN 17 - #define STA_INTERFACE "ethsta0" #define AP_INTERFACE "ethap0" -#define MAX_INTERFACE_LEN 10 #define WIFI_VENDOR_IE_ELEMENT_ID 0xDD #define OFFSET 4 @@ -38,105 +34,6 @@ #define VENDOR_OUI_2 3 #define VENDOR_OUI_TYPE 22 -// Function converts mac string to byte stream -static int convert_mac_to_bytes(uint8_t *out, size_t out_len, char *s) -{ - int mac[MAC_LEN] = {0}; - int num_bytes = 0; - if (!s || (strlen(s) < MAC_STR_LEN) || (out_len < MAC_LEN)) { - return FAILURE; - } - num_bytes = sscanf(s, "%2x:%2x:%2x:%2x:%2x:%2x", - &mac[0],&mac[1], &mac[2], &mac[3], &mac[4], &mac[5]); - if ((num_bytes < (MAC_LEN - 1)) || - (mac[0] > 0xFF) || - (mac[1] > 0xFF) || - (mac[2] > 0xFF) || - (mac[3] > 0xFF) || - (mac[4] > 0xFF) || - (mac[5] > 0xFF)) { - return FAILURE; - } - out[0] = mac[0]&0xff; - out[1] = mac[1]&0xff; - out[2] = mac[2]&0xff; - out[3] = mac[3]&0xff; - out[4] = mac[4]&0xff; - out[5] = mac[5]&0xff; - return SUCCESS; -} - -// Function ups in given interface -static int interface_up(int sockfd, char* iface) -{ - int ret = SUCCESS; - struct ifreq req = {0}; - size_t if_name_len = strnlen(iface, MAX_INTERFACE_LEN); - if (if_name_len < sizeof(req.ifr_name)) { - memcpy(req.ifr_name,iface,if_name_len); - req.ifr_name[if_name_len]='\0'; - } else { - printf("Failed: Max interface length allowed is %u \n", sizeof(req.ifr_name)); - return FAILURE; - } - req.ifr_flags |= IFF_UP; - ret = ioctl(sockfd, SIOCSIFFLAGS, &req); - if (ret < 0) { - return FAILURE; - } - return SUCCESS; -} - -// Function downs in given interface -static int interface_down(int sockfd, char* iface) -{ - int ret = SUCCESS; - struct ifreq req = {0}; - size_t if_name_len = strnlen(iface, MAX_INTERFACE_LEN); - if (if_name_len < sizeof(req.ifr_name)) { - memcpy(req.ifr_name,iface,if_name_len); - req.ifr_name[if_name_len]='\0'; - } else { - printf("Failed: Max interface length allowed is %u \n", sizeof(req.ifr_name)); - return FAILURE; - } - req.ifr_flags &= ~IFF_UP; - ret = ioctl(sockfd, SIOCSIFFLAGS, &req); - if (ret < 0) { - return FAILURE; - } - return SUCCESS; -} - -// Function sets mac address to given interface -static int setHWaddr(int sockfd, char* iface, char* mac) -{ - int ret = SUCCESS; - struct ifreq req = {0}; - char mac_bytes[MAC_LEN] = ""; - size_t if_name_len = strnlen(iface, MAX_INTERFACE_LEN); - if (if_name_len < sizeof(req.ifr_name)) { - memcpy(req.ifr_name,iface,if_name_len); - req.ifr_name[if_name_len]='\0'; - } else { - printf("Failed: Max interface length allowed is %u \n", sizeof(req.ifr_name)); - return FAILURE; - } - memset(mac_bytes, '\0', MAC_LEN); - ret = convert_mac_to_bytes((uint8_t *)&mac_bytes, sizeof(mac_bytes), mac); - if (ret) { - printf("Failed to convert mac address \n"); - return FAILURE; - } - req.ifr_hwaddr.sa_family = ARPHRD_ETHER; - strncpy(req.ifr_hwaddr.sa_data, mac_bytes, MAC_LEN); - ret = ioctl(sockfd, SIOCSIFHWADDR, &req); - if (ret < 0) { - return FAILURE; - } - return SUCCESS; -} - int test_get_wifi_mode() { int mode = 0; @@ -283,8 +180,8 @@ int test_station_mode_connect() printf("Failed to connect with AP \n"); } - sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); - if (sockfd < 0) { + ret = create_socket(AF_INET, SOCK_DGRAM, IPPROTO_IP, &sockfd); + if (ret < 0) { printf("Failure to open socket\n"); return FAILURE; } @@ -294,7 +191,7 @@ int test_station_mode_connect() printf("%s interface down\n", STA_INTERFACE); } else { printf("Unable to down %s interface\n", STA_INTERFACE); - return FAILURE; + goto close_sock; } ret = wifi_get_mac(WIFI_MODE_STA, mac); @@ -302,15 +199,15 @@ int test_station_mode_connect() printf("Station mode: mac address %s \n", mac); } else { printf("Failed to get station mode MAC address \n"); - return FAILURE; + goto close_sock; } - ret = setHWaddr(sockfd, STA_INTERFACE, mac); + ret = set_hw_addr(sockfd, STA_INTERFACE, mac); if (ret == SUCCESS) { printf("MAC address %s set to %s interface\n", mac, STA_INTERFACE); } else { printf("Unable to set MAC address to %s interface\n", STA_INTERFACE); - return FAILURE; + goto close_sock; } ret = interface_up(sockfd, STA_INTERFACE); @@ -318,11 +215,23 @@ int test_station_mode_connect() printf("%s interface up\n", STA_INTERFACE); } else { printf("Unable to up %s interface\n", STA_INTERFACE); - return FAILURE; + goto close_sock; } + ret = close_socket(sockfd); + if (ret < 0) { + printf("Failure to close socket\n"); + return FAILURE; + } printf("====\n\n"); return SUCCESS; + +close_sock: + ret = close_socket(sockfd); + if (ret < 0) { + printf("Failure to close socket\n"); + } + return FAILURE; } int test_station_mode_get_info() @@ -389,8 +298,8 @@ int test_station_mode_disconnect() return FAILURE; } - sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); - if (sockfd < 0) { + ret = create_socket(AF_INET, SOCK_DGRAM, IPPROTO_IP, &sockfd); + if (ret < 0) { printf("Failure to open socket\n"); return sockfd; } @@ -400,11 +309,24 @@ int test_station_mode_disconnect() printf("%s interface down\n", STA_INTERFACE); } else { printf("Unable to down %s interface\n", STA_INTERFACE); + goto close_sock; + } + + ret = close_socket(sockfd); + if (ret < 0) { + printf("Failure to close socket\n"); return FAILURE; } - printf("====\n\n"); + printf("====\n\n"); return SUCCESS; + +close_sock: + ret = close_socket(sockfd); + if (ret < 0) { + printf("Failure to close socket\n"); + } + return FAILURE; } int test_softap_mode_start() @@ -430,8 +352,8 @@ int test_softap_mode_start() return FAILURE; } - sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); - if (sockfd < 0) { + ret = create_socket(AF_INET, SOCK_DGRAM, IPPROTO_IP, &sockfd); + if (ret < 0) { printf("Failure to open socket\n"); return FAILURE; } @@ -441,7 +363,7 @@ int test_softap_mode_start() printf("%s interface down\n", AP_INTERFACE); } else { printf("Unable to down %s interface\n", AP_INTERFACE); - return FAILURE; + goto close_sock; } ret = wifi_get_mac(WIFI_MODE_AP, mac); @@ -449,15 +371,15 @@ int test_softap_mode_start() printf("softAP mode: mac address %s \n", mac); } else { printf("Failed to get softAP mode MAC address \n"); - return FAILURE; + goto close_sock; } - ret = setHWaddr(sockfd, AP_INTERFACE, mac); + ret = set_hw_addr(sockfd, AP_INTERFACE, mac); if (ret == SUCCESS) { printf("MAC address %s set to %s interface\n", mac, AP_INTERFACE); } else { printf("Unable to set MAC address to %s interface\n", AP_INTERFACE); - return FAILURE; + goto close_sock; } ret = interface_up(sockfd, AP_INTERFACE); @@ -465,11 +387,24 @@ int test_softap_mode_start() printf("%s interface up\n", AP_INTERFACE); } else { printf("Unable to up %s interface\n", AP_INTERFACE); + goto close_sock; + } + + ret = close_socket(sockfd); + if (ret < 0) { + printf("Failure to close socket\n"); return FAILURE; } printf("====\n\n"); return SUCCESS; + +close_sock: + ret = close_socket(sockfd); + if (ret < 0) { + printf("Failure to close socket\n"); + } + return FAILURE; } int test_softap_mode_get_info() @@ -532,8 +467,8 @@ int test_softap_mode_stop() printf("Failed to stop ESP32 softAP \n"); return FAILURE; } - sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); - if (sockfd < 0) { + ret = create_socket(AF_INET, SOCK_DGRAM, IPPROTO_IP, &sockfd); + if (ret < 0) { printf("Failure to open socket\n"); return sockfd; } @@ -543,13 +478,25 @@ int test_softap_mode_stop() printf("%s interface down\n", AP_INTERFACE); } else { printf("Unable to down %s interface\n", AP_INTERFACE); + goto close_sock; + } + + ret = close_socket(sockfd); + if (ret < 0) { + printf("Failure to close socket\n"); return FAILURE; } printf("====\n\n"); return SUCCESS; -} +close_sock: + ret = close_socket(sockfd); + if (ret < 0) { + printf("Failure to close socket\n"); + } + return FAILURE; +} int test_set_wifi_power_save_mode() { @@ -675,7 +622,7 @@ int test_ota(char* image_path) } int test_set_vendor_specific_ie() -{ +{ char *data = "Example vendor IE data"; int vnd_ie_size = sizeof(vendor_ie_data_t)+strlen(data)+1; vendor_ie_data_t *vnd_ie = (vendor_ie_data_t *)malloc(vnd_ie_size); @@ -686,11 +633,12 @@ int test_set_vendor_specific_ie() vnd_ie->element_id = WIFI_VENDOR_IE_ELEMENT_ID; vnd_ie->length = strlen(data) + OFFSET; - if (vnd_ie->length < OFFSET) { - printf("Length should not be less than %d bytes \n", OFFSET); + if (vnd_ie->length < OFFSET) { + printf("Length should not be less than %d bytes \n", OFFSET); free(vnd_ie); - return FAILURE; - } + return FAILURE; + } + vnd_ie->vendor_oui[0] = VENDOR_OUI_0; vnd_ie->vendor_oui[1] = VENDOR_OUI_1; vnd_ie->vendor_oui[2] = VENDOR_OUI_2; diff --git a/host/linux/host_control/python_support/commands_lib.py b/host/linux/host_control/python_support/commands_lib.py index b6e36c3e94..ac4ec8d90b 100644 --- a/host/linux/host_control/python_support/commands_lib.py +++ b/host/linux/host_control/python_support/commands_lib.py @@ -404,7 +404,7 @@ def wifi_get_power_save_mode(): # OTA begin # function returns "success" or "failure" # esp ota begin function performs an OTA begin operation for ESP32 -# which sets partition for OTA write and erase it. +# which sets partition for OTA write and erase it. def esp_ota_begin(): ret = commands_map_py_to_c.esp_ota_begin() @@ -441,3 +441,65 @@ def esp_ota_end(): return success else: return failure + +# Interface down +# This function downs the given interface iface +# function returns "success" or "failure" + +def interface_down(sockfd, iface): + ret = commands_map_py_to_c.interface_down(sockfd, iface) + if not ret: + return success + else: + return failure + +# Interface up +# This function ups the given interface iface +# function returns "success" or "failure" + +def interface_up(sockfd, iface): + ret = commands_map_py_to_c.interface_up(sockfd, iface) + if not ret: + return success + else: + return failure + +# Set HW address +# This function sets mac address to the given interface iface +# function returns "success" or "failure" + +def set_hw_addr(sockfd, iface, mac): + ret = commands_map_py_to_c.set_hw_addr(sockfd, iface, mac) + if not ret: + return success + else: + return failure + +# Create socket +# This function creates an endpoint for communication +# Function returns "file descriptor (integer number) that refers to that endpoint/socket" or "failure" +# Input parameter: +# domain: specifies a communication domain +# types: specifies the communication semantics +# protocol: specifies a particular protocol to be used with the socket + +def create_socket(domain, types, protocol): + sock = c_int() + ret = commands_map_py_to_c.create_socket(domain, types, protocol, byref(sock)) + if not ret: + return int(sock.value) + else: + return failure + +# Close socket +# This function closes an endpoint for communication +# function returns "success" or "failure" +# Input parameter: +# sock: file descriptor (integer number) that refers to the endpoint/socket + +def close_socket(sock): + ret = commands_map_py_to_c.close_socket(sock) + if not ret: + return success + else: + return failure diff --git a/host/linux/host_control/python_support/commands_map_py_to_c.py b/host/linux/host_control/python_support/commands_map_py_to_c.py index 1e469ccf3a..6de1c2eff3 100644 --- a/host/linux/host_control/python_support/commands_map_py_to_c.py +++ b/host/linux/host_control/python_support/commands_map_py_to_c.py @@ -81,3 +81,18 @@ esp_ota_end = commands_lib.esp_ota_end esp_ota_end.restype = c_int + +interface_up = commands_lib.interface_up +interface_up.restype = c_int + +interface_down = commands_lib.interface_down +interface_down.restype = c_int + +set_hw_addr = commands_lib.set_hw_addr +set_hw_addr.restype = c_int + +create_socket = commands_lib.create_socket +create_socket.restype = c_int + +close_socket = commands_lib.close_socket +close_socket.restype = c_int diff --git a/host/linux/host_control/python_support/test_api.py b/host/linux/host_control/python_support/test_api.py index efda0f2fa8..1bff1f0592 100644 --- a/host/linux/host_control/python_support/test_api.py +++ b/host/linux/host_control/python_support/test_api.py @@ -14,7 +14,7 @@ from commands_lib import * from test_config import * -from socket import * +from socket import * from struct import * from fcntl import * @@ -32,7 +32,7 @@ SIOCSIFFLAGS = 0x8914 SIOCSIFHWADDR = 0x8924 -# Macros for packing data in 16 bytes short character bytes +# Macros for packing data in 16 bytes short character bytes PACK_FORMAT = '16sh' def get_bytes(string): @@ -41,37 +41,6 @@ def get_bytes(string): else: return string -def interface_down(sockfd, iface): - ifreq = pack(PACK_FORMAT, get_bytes(iface), 0) - flags = unpack(PACK_FORMAT, ioctl(sockfd, SIOCGIFFLAGS, ifreq))[1] - flags = flags & ~IFF_UP - ifreq = pack(PACK_FORMAT, get_bytes(iface), flags) - ret = ioctl(sockfd, SIOCSIFFLAGS, ifreq) - if (not ret): - return failure - else: - return success - -def interface_up(sockfd, iface): - ifreq = pack(PACK_FORMAT, get_bytes(iface), 0) - flags = unpack(PACK_FORMAT, ioctl(sockfd, SIOCGIFFLAGS, ifreq))[1] - flags = flags | IFF_UP - ifreq = pack(PACK_FORMAT, get_bytes(iface), flags) - ret = ioctl(sockfd, SIOCSIFFLAGS, ifreq) - if (not ret): - return failure - else: - return success - -def setHWaddr(sockfd, iface, mac): - macbytes = [int(i, 16) for i in mac.split(':')] - ifreq = pack('16sH6B8x', get_bytes(sta_interface), AF_UNIX, *macbytes) - ret = ioctl(sockfd, SIOCSIFHWADDR, ifreq) - if (not ret): - return failure - else: - return success - def test_get_wifi_mode(): mode = wifi_get_mode() if (mode != failure): @@ -146,8 +115,8 @@ def test_station_mode_connect(): print("Failed to connect to AP") return - sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP) - if (not sockfd): + sockfd = create_socket(AF_INET, SOCK_DGRAM, IPPROTO_IP) + if (sockfd == failure): print("Failed to open socket") return @@ -156,27 +125,32 @@ def test_station_mode_connect(): print(sta_interface+" interface down") else: print("Unable to down "+sta_interface+" interface") - return - mac = wifi_get_mac(WIFI_MODE_STATION) - if (mac != failure): - print("Station mode: mac address "+str(mac)) - else: - print("Failed to get MAC address") - return - - ret = setHWaddr(sockfd, sta_interface, mac) - if (ret != failure): - print("MAC address "+mac+" set to "+sta_interface+" interface") - else: - print("Unable to set MAC address to "+sta_interface) - return - - ret = interface_up(sockfd, sta_interface) - if (ret != failure): - print(sta_interface+" interface up") - else: - print("Unable to up "+sta_interface+" interface") + if(ret != failure): + mac = wifi_get_mac(WIFI_MODE_STATION) + if (mac != failure): + print("Station mode: mac address "+str(mac)) + else: + ret = failure + print("Failed to get MAC address") + + if(ret != failure): + ret = set_hw_addr(sockfd, sta_interface, mac) + if (ret != failure): + print("MAC address "+mac+" set to "+sta_interface+" interface") + else: + print("Unable to set MAC address to "+sta_interface) + + if(ret != failure): + ret = interface_up(sockfd, sta_interface) + if (ret != failure): + print(sta_interface+" interface up") + else: + print("Unable to up "+sta_interface+" interface") + + ret = close_socket(sockfd) + if (ret == failure): + print("Unable to close the socket") return def test_station_mode_get_info(): @@ -210,8 +184,8 @@ def test_station_mode_disconnect(): print("Failed to disconnect from AP") return - sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP) - if (not sockfd): + sockfd = create_socket(AF_INET, SOCK_DGRAM, IPPROTO_IP) + if (sockfd == failure): print("Failed to open socket") return @@ -220,6 +194,10 @@ def test_station_mode_disconnect(): print(sta_interface+" interface down") else: print("Unable to down "+sta_interface+" interface") + + ret = close_socket(sockfd) + if (ret == failure): + print("Unable to close the socket") return def test_softap_mode_start(): @@ -232,8 +210,8 @@ def test_softap_mode_start(): print("Failed to set softAP config") return - sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP) - if (not sockfd): + sockfd = create_socket(AF_INET, SOCK_DGRAM, IPPROTO_IP) + if (sockfd == failure): print("Failed to open socket") return @@ -242,27 +220,32 @@ def test_softap_mode_start(): print(ap_interface+" interface down") else: print("Unable to down "+ap_interface+" interface") - return - - mac = wifi_get_mac(WIFI_MODE_SOFTAP) - if (mac != failure): - print("SoftAP mode: mac address "+str(mac)) - else: - print("Failed to get MAC address of softAP interface") - return - ret = setHWaddr(sockfd, ap_interface, mac) - if (ret != failure): - print("MAC address "+mac+" set to "+ap_interface+" interface") - else: - print("Unable to set MAC address to "+ap_interface) - return - - ret = interface_up(sockfd, ap_interface) - if (ret != failure): - print(ap_interface+" interface up") - else: - print("Unable to up "+ap_interface+" interface") + if(ret != failure): + mac = wifi_get_mac(WIFI_MODE_SOFTAP) + if (mac != failure): + print("SoftAP mode: mac address "+str(mac)) + else: + ret = failure + print("Failed to get MAC address of softAP interface") + + if(ret != failure): + ret = set_hw_addr(sockfd, ap_interface, mac) + if (ret != failure): + print("MAC address "+mac+" set to "+ap_interface+" interface") + else: + print("Unable to set MAC address to "+ap_interface) + + if(ret != failure): + ret = interface_up(sockfd, ap_interface) + if (ret != failure): + print(ap_interface+" interface up") + else: + print("Unable to up "+ap_interface+" interface") + + ret = close_socket(sockfd) + if (ret == failure): + print("Unable to close the socket") return def test_softap_mode_get_info(): @@ -298,8 +281,8 @@ def test_softap_mode_stop(): print("Failed to stop ESP32 softAP") return - sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP) - if (not sockfd): + sockfd = create_socket(AF_INET, SOCK_DGRAM, IPPROTO_IP) + if (sockfd == failure): print("Failed to open socket") return @@ -308,6 +291,10 @@ def test_softap_mode_stop(): print(ap_interface+" interface down") else: print("Unable to down "+ap_interface+" interface") + + ret = close_socket(sockfd) + if (ret == failure): + print("Unable to close the socket") return def test_set_wifi_power_save_mode(): From 2bcf7481e09feba4adc37a8847e81c5ddc972e40 Mon Sep 17 00:00:00 2001 From: "ajita.chavan" Date: Wed, 17 Nov 2021 14:58:50 +0530 Subject: [PATCH 29/40] Memory leak fix added in commands.c Signed-off-by: ajita.chavan --- host/host_common/commands.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/host/host_common/commands.c b/host/host_common/commands.c index d2cf015eae..8fa97f24de 100644 --- a/host/host_common/commands.c +++ b/host/host_common/commands.c @@ -160,10 +160,12 @@ int wifi_get_mac (int mode, char *mac) mem_free(tx_data); mem_free(rx_data); mem_free(req_payload); + esp_hosted_config_payload__free_unpacked(resp, NULL); return SUCCESS; err1: mem_free(rx_data); + esp_hosted_config_payload__free_unpacked(resp, NULL); err2: mem_free(tx_data); err3: @@ -242,10 +244,12 @@ int wifi_set_mac (int mode, char *mac) mem_free(tx_data); mem_free(rx_data); mem_free(req_payload); + esp_hosted_config_payload__free_unpacked(resp, NULL); return SUCCESS; err1: mem_free(rx_data); + esp_hosted_config_payload__free_unpacked(resp, NULL); err2: mem_free(tx_data); err3: @@ -303,10 +307,12 @@ int wifi_get_mode (int *mode) *mode = resp->resp_get_wifi_mode->mode; mem_free(tx_data); mem_free(rx_data); + esp_hosted_config_payload__free_unpacked(resp, NULL); return SUCCESS; err1: mem_free(rx_data); + esp_hosted_config_payload__free_unpacked(resp, NULL); err2: mem_free(tx_data); return FAILURE; @@ -376,10 +382,12 @@ int wifi_set_mode (int mode) mem_free(tx_data); mem_free(rx_data); mem_free(req_payload); + esp_hosted_config_payload__free_unpacked(resp, NULL); return SUCCESS; err1: mem_free(rx_data); + esp_hosted_config_payload__free_unpacked(resp, NULL); err2: mem_free(tx_data); err3: @@ -481,10 +489,12 @@ int wifi_set_ap_config (esp_hosted_control_config_t ap_config) mem_free(tx_data); mem_free(rx_data); mem_free(req_payload); + esp_hosted_config_payload__free_unpacked(resp, NULL); return SUCCESS; err1: mem_free(rx_data); + esp_hosted_config_payload__free_unpacked(resp, NULL); err2: mem_free(tx_data); err3: @@ -571,10 +581,12 @@ int wifi_get_ap_config (esp_hosted_control_config_t *ap_config) mem_free(tx_data); mem_free(rx_data); + esp_hosted_config_payload__free_unpacked(resp, NULL); return SUCCESS; err1: mem_free(rx_data); + esp_hosted_config_payload__free_unpacked(resp, NULL); err2: mem_free(tx_data); return FAILURE; @@ -625,10 +637,12 @@ int wifi_disconnect_ap () mem_free(tx_data); mem_free(rx_data); + esp_hosted_config_payload__free_unpacked(resp, NULL); return SUCCESS; err1: mem_free(rx_data); + esp_hosted_config_payload__free_unpacked(resp, NULL); err2: mem_free(tx_data); return FAILURE; @@ -743,10 +757,12 @@ int wifi_set_softap_config (esp_hosted_control_config_t softap_config) mem_free(tx_data); mem_free(rx_data); mem_free(req_payload); + esp_hosted_config_payload__free_unpacked(resp, NULL); return SUCCESS; err1: mem_free(rx_data); + esp_hosted_config_payload__free_unpacked(resp, NULL); err2: mem_free(tx_data); err3: @@ -819,10 +835,12 @@ int wifi_get_softap_config (esp_hosted_control_config_t *softap_config) mem_free(tx_data); mem_free(rx_data); + esp_hosted_config_payload__free_unpacked(resp, NULL); return SUCCESS; err1: mem_free(rx_data); + esp_hosted_config_payload__free_unpacked(resp, NULL); err2: mem_free(tx_data); return FAILURE; @@ -873,10 +891,12 @@ int wifi_stop_softap () mem_free(tx_data); mem_free(rx_data); + esp_hosted_config_payload__free_unpacked(resp, NULL); return SUCCESS; err1: mem_free(rx_data); + esp_hosted_config_payload__free_unpacked(resp, NULL); err2: mem_free(tx_data); return FAILURE; @@ -961,10 +981,12 @@ esp_hosted_wifi_scanlist_t* wifi_ap_scan_list (int *count) mem_free(tx_data); mem_free(rx_data); + esp_hosted_config_payload__free_unpacked(resp, NULL); return list; err1: mem_free(rx_data); + esp_hosted_config_payload__free_unpacked(resp, NULL); err2: mem_free(tx_data); return NULL; @@ -1039,10 +1061,12 @@ esp_hosted_wifi_connected_stations_list* wifi_connected_stations_list(int *num) mem_free(tx_data); mem_free(rx_data); + esp_hosted_config_payload__free_unpacked(resp, NULL); return list; err1: mem_free(rx_data); + esp_hosted_config_payload__free_unpacked(resp, NULL); err2: mem_free(tx_data); return NULL; @@ -1112,10 +1136,12 @@ int wifi_set_power_save_mode (int power_save_mode) mem_free(tx_data); mem_free(rx_data); mem_free(req_payload); + esp_hosted_config_payload__free_unpacked(resp, NULL); return SUCCESS; err1: mem_free(rx_data); + esp_hosted_config_payload__free_unpacked(resp, NULL); err2: mem_free(tx_data); err3: @@ -1175,10 +1201,12 @@ int wifi_get_power_save_mode (int *power_save_mode) *power_save_mode = resp->resp_get_power_save_mode->mode; mem_free(tx_data); mem_free(rx_data); + esp_hosted_config_payload__free_unpacked(resp, NULL); return SUCCESS; err1: mem_free(rx_data); + esp_hosted_config_payload__free_unpacked(resp, NULL); err2: mem_free(tx_data); return FAILURE; @@ -1263,9 +1291,11 @@ int wifi_set_vendor_specific_ie(bool enable, wifi_vendor_ie_type_t type, mem_free(tx_data); mem_free(rx_data); mem_free(req_payload); + esp_hosted_config_payload__free_unpacked(resp, NULL); return SUCCESS; err1: mem_free(rx_data); + esp_hosted_config_payload__free_unpacked(resp, NULL); err2: mem_free(tx_data); err3: @@ -1318,10 +1348,12 @@ int esp_ota_begin() mem_free(tx_data); mem_free(rx_data); + esp_hosted_config_payload__free_unpacked(resp, NULL); return SUCCESS; err1: mem_free(rx_data); + esp_hosted_config_payload__free_unpacked(resp, NULL); err2: mem_free(tx_data); return FAILURE; @@ -1391,10 +1423,12 @@ int esp_ota_write(uint8_t* ota_data, uint32_t ota_data_len) mem_free(tx_data); mem_free(rx_data); mem_free(req_payload); + esp_hosted_config_payload__free_unpacked(resp, NULL); return SUCCESS; err1: mem_free(rx_data); + esp_hosted_config_payload__free_unpacked(resp, NULL); err2: mem_free(tx_data); err3: @@ -1447,10 +1481,12 @@ int esp_ota_end() mem_free(tx_data); mem_free(rx_data); + esp_hosted_config_payload__free_unpacked(resp, NULL); return SUCCESS; err1: mem_free(rx_data); + esp_hosted_config_payload__free_unpacked(resp, NULL); err2: mem_free(tx_data); return FAILURE; From 0c8bae48b814eb870c959a6da3b7a5dde9be7656 Mon Sep 17 00:00:00 2001 From: "ajita.chavan" Date: Wed, 17 Nov 2021 19:11:48 +0530 Subject: [PATCH 30/40] Memory leak corrected commands.c Signed-off-by: ajita.chavan --- host/host_common/commands.c | 2 ++ host/linux/host_control/c_support/test_api.c | 20 +++++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/host/host_common/commands.c b/host/host_common/commands.c index 8fa97f24de..6d483c68d8 100644 --- a/host/host_common/commands.c +++ b/host/host_common/commands.c @@ -474,12 +474,14 @@ int wifi_set_ap_config (esp_hosted_control_config_t ap_config) mem_free(tx_data); mem_free(rx_data); mem_free(req_payload); + esp_hosted_config_payload__free_unpacked(resp, NULL); return INVALID_PASSWORD; } else if (resp->resp_set_ap_config->resp == NO_AP_FOUND) { command_log("SSID: %s not found\n", (char *)&ap_config.station.ssid); mem_free(tx_data); mem_free(rx_data); mem_free(req_payload); + esp_hosted_config_payload__free_unpacked(resp, NULL); return NO_AP_FOUND; } else if (resp->resp_set_ap_config->resp) { command_log("Failed to connect with AP\n"); diff --git a/host/linux/host_control/c_support/test_api.c b/host/linux/host_control/c_support/test_api.c index 5a6868bd8b..1c34f52ff1 100644 --- a/host/linux/host_control/c_support/test_api.c +++ b/host/linux/host_control/c_support/test_api.c @@ -44,6 +44,7 @@ int test_get_wifi_mode() printf("wifi mode is %d \n",mode); } else { printf("Failed to get wifi mode \n"); + return FAILURE; } printf("====\n\n"); #endif @@ -60,6 +61,7 @@ int test_set_wifi_mode(int mode) printf("wifi mode is %d \n", mode); } else { printf("error in setting mode \n"); + return FAILURE; } printf("====\n\n"); #endif @@ -97,6 +99,7 @@ int test_station_mode_get_mac_addr() printf("Station mode: mac address %s \n", mac); } else { printf("Failed to get station mode MAC address \n"); + return FAILURE; } printf("====\n\n"); #endif @@ -114,6 +117,7 @@ int test_station_mode_set_mac_addr_of_esp() printf("MAC address is set \n"); } else { printf("MAC address is not set \n"); + return FAILURE; } printf("====\n\n"); #endif @@ -132,6 +136,7 @@ int test_softap_mode_set_mac_addr_of_esp() printf("MAC address is set \n"); } else { printf("MAC address is not set \n"); + return FAILURE; } printf("====\n\n"); #endif @@ -148,6 +153,7 @@ int test_softap_mode_get_mac_addr() printf("Softap mode: mac address %s \n", mac); } else { printf("Failed to get softap mode MAC address \n"); + return FAILURE; } printf("====\n\n"); #endif @@ -173,11 +179,14 @@ int test_station_mode_connect() printf("Connected to AP: '%s'\n",STATION_MODE_SSID); } else if (ret == NO_AP_FOUND){ printf("SSID: %s not found \n",(char *)&config.station.ssid); + return FAILURE; } else if (ret == INVALID_PASSWORD){ printf("Invalid password %s for SSID %s\n", (char *)&config.station.pwd ,\ (char *)&config.station.ssid); + return FAILURE; } else { printf("Failed to connect with AP \n"); + return FAILURE; } ret = create_socket(AF_INET, SOCK_DGRAM, IPPROTO_IP, &sockfd); @@ -249,6 +258,7 @@ int test_station_mode_get_info() printf("AP's encryption mode %d \n", config.station.encryption_mode); } else { printf("AP's status %s \n", config.station.status); + return FAILURE; } printf("====\n\n"); @@ -422,6 +432,7 @@ int test_softap_mode_get_info() printf("softAP bandwidth mode %d \n", config.softap.bandwidth); } else { printf("Failed to get softAP config \n"); + return FAILURE; } printf("====\n\n"); @@ -438,6 +449,7 @@ int test_softap_mode_connected_clients_info() stations_list = wifi_connected_stations_list(&count); if (!count) { printf("No station found \n"); + return FAILURE; } if (!stations_list) { @@ -453,7 +465,7 @@ int test_softap_mode_connected_clients_info() stations_list = NULL; } printf("====\n\n"); - return 0; + return SUCCESS; } int test_softap_mode_stop() @@ -509,6 +521,7 @@ int test_set_wifi_power_save_mode() printf("Power save mode set \n"); } else { printf("Power save mode is not set \n"); + return FAILURE; } printf("====\n\n"); #endif @@ -527,6 +540,7 @@ int test_get_wifi_power_save_mode() printf("Power save mode is %d \n", power_save_mode); } else { printf("Failed to get power save mode \n"); + return FAILURE; } printf("====\n\n"); #endif @@ -544,6 +558,7 @@ int test_ota_begin() printf("OTA begin success \n"); } else { printf("Failed start OTA begin\n"); + return FAILURE; } printf("====\n\n"); #endif @@ -562,6 +577,7 @@ int test_ota_write(uint8_t* ota_data, uint32_t ota_data_len) } else { printf("Failed OTA write\n"); + return FAILURE; } printf("====\n\n"); #endif @@ -579,6 +595,7 @@ int test_ota_end() printf("OTA end success \n"); } else { printf("Failed OTA end\n"); + return FAILURE; } printf("====\n\n"); #endif @@ -654,6 +671,7 @@ int test_set_vendor_specific_ie() printf("Success in set vendor specific ie\n"); } else { printf("Failed to set vendor specific ie\n"); + return FAILURE; } printf("====\n\n"); #endif From ccae4c7ec98a36d67ac6375a8e18b3a83fe21179 Mon Sep 17 00:00:00 2001 From: Yogesh Mantri Date: Wed, 17 Nov 2021 23:26:02 +0530 Subject: [PATCH 31/40] Fixed build failure for kernel 4.19.66 --- host/host_common/commands.c | 2 ++ host/linux/host_control/c_support/test_api.c | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/host/host_common/commands.c b/host/host_common/commands.c index 6d483c68d8..41bfd8d708 100644 --- a/host/host_common/commands.c +++ b/host/host_common/commands.c @@ -9,6 +9,8 @@ #include "esp_hosted_config.pb-c.h" #ifndef STM32F469xx +#include +#include #include #include #include diff --git a/host/linux/host_control/c_support/test_api.c b/host/linux/host_control/c_support/test_api.c index 1c34f52ff1..6e744e30da 100644 --- a/host/linux/host_control/c_support/test_api.c +++ b/host/linux/host_control/c_support/test_api.c @@ -311,7 +311,7 @@ int test_station_mode_disconnect() ret = create_socket(AF_INET, SOCK_DGRAM, IPPROTO_IP, &sockfd); if (ret < 0) { printf("Failure to open socket\n"); - return sockfd; + return FAILURE; } ret = interface_down(sockfd, STA_INTERFACE); @@ -482,7 +482,7 @@ int test_softap_mode_stop() ret = create_socket(AF_INET, SOCK_DGRAM, IPPROTO_IP, &sockfd); if (ret < 0) { printf("Failure to open socket\n"); - return sockfd; + return FAILURE; } ret = interface_down(sockfd, AP_INTERFACE); From 3a16d79ebacad3a6795f965020fcc2e150b583d0 Mon Sep 17 00:00:00 2001 From: Yogesh Mantri Date: Fri, 19 Nov 2021 08:19:07 +0530 Subject: [PATCH 32/40] Doc fix to reword OTA max binary size limitation --- ota_feature.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ota_feature.md b/ota_feature.md index ac5ce6a5b0..ba28626cbe 100644 --- a/ota_feature.md +++ b/ota_feature.md @@ -1,6 +1,9 @@ ## ESP-Hosted OTA feature -Currently, new OTA image supported size is upto 1MB. +Please note, Maximum size of New OTA image binary depends upon - +1. Total flash size available +2. OTA partition size in partition table +As per current limits, upto 1MB binary size is supported which is configurable from above options. Please refer [partion tables](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/partition-tables.html) for more info. There are basic three control path commands for OTA are provided in C and python, as follows: * esp_ota_begin() -- Sets available OTA partition in flash for OTA write operation and erase it. From 35dad04fae88c13ccbb02d8fc1b4d97805aff566 Mon Sep 17 00:00:00 2001 From: "ajita.chavan" Date: Fri, 12 Nov 2021 18:02:34 +0530 Subject: [PATCH 33/40] wifi get/set tx power control path command added Signed-off-by: ajita.chavan --- common/esp_hosted_config.pb-c.c | 407 +++++++++++++++++- common/include/esp_hosted_config.pb-c.h | 158 ++++++- common/proto/esp_hosted_config.proto | 26 ++ docs/c_api.md | 38 ++ docs/c_demo.md | 2 + docs/python_api.md | 39 ++ .../network_adapter/main/slave_commands.c | 101 +++++ host/host_common/commands.c | 141 ++++++ host/host_common/include/commands.h | 34 +- host/linux/host_control/c_support/stress.c | 4 + host/linux/host_control/c_support/test.c | 12 +- host/linux/host_control/c_support/test_api.c | 38 ++ host/linux/host_control/c_support/test_api.h | 4 + .../host_control/c_support/test_config.h | 3 + .../python_support/commands_lib.py | 43 +- .../python_support/commands_map_py_to_c.py | 6 + .../python_support/hosted_config.py | 12 + .../host_control/python_support/stress.py | 14 +- .../linux/host_control/python_support/test.py | 4 + .../host_control/python_support/test_api.py | 19 +- .../python_support/test_config.py | 3 + 21 files changed, 1083 insertions(+), 25 deletions(-) diff --git a/common/esp_hosted_config.pb-c.c b/common/esp_hosted_config.pb-c.c index f6e3d8cc22..d6ccff1c84 100644 --- a/common/esp_hosted_config.pb-c.c +++ b/common/esp_hosted_config.pb-c.c @@ -1383,6 +1383,178 @@ void esp_hosted_resp_set_vendor_specific_ie__free_unpacked assert(message->base.descriptor == &esp_hosted_resp_set_vendor_specific_ie__descriptor); protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); } +void esp_hosted_cmd_set_wi_fi_maxtxpower__init + (EspHostedCmdSetWiFiMAXTXPower *message) +{ + static EspHostedCmdSetWiFiMAXTXPower init_value = ESP_HOSTED_CMD_SET_WI_FI_MAXTXPOWER__INIT; + *message = init_value; +} +size_t esp_hosted_cmd_set_wi_fi_maxtxpower__get_packed_size + (const EspHostedCmdSetWiFiMAXTXPower *message) +{ + assert(message->base.descriptor == &esp_hosted_cmd_set_wi_fi_maxtxpower__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t esp_hosted_cmd_set_wi_fi_maxtxpower__pack + (const EspHostedCmdSetWiFiMAXTXPower *message, + uint8_t *out) +{ + assert(message->base.descriptor == &esp_hosted_cmd_set_wi_fi_maxtxpower__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t esp_hosted_cmd_set_wi_fi_maxtxpower__pack_to_buffer + (const EspHostedCmdSetWiFiMAXTXPower *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &esp_hosted_cmd_set_wi_fi_maxtxpower__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +EspHostedCmdSetWiFiMAXTXPower * + esp_hosted_cmd_set_wi_fi_maxtxpower__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (EspHostedCmdSetWiFiMAXTXPower *) + protobuf_c_message_unpack (&esp_hosted_cmd_set_wi_fi_maxtxpower__descriptor, + allocator, len, data); +} +void esp_hosted_cmd_set_wi_fi_maxtxpower__free_unpacked + (EspHostedCmdSetWiFiMAXTXPower *message, + ProtobufCAllocator *allocator) +{ + assert(message->base.descriptor == &esp_hosted_cmd_set_wi_fi_maxtxpower__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void esp_hosted_resp_set_wi_fi_maxtxpower__init + (EspHostedRespSetWiFiMAXTXPower *message) +{ + static EspHostedRespSetWiFiMAXTXPower init_value = ESP_HOSTED_RESP_SET_WI_FI_MAXTXPOWER__INIT; + *message = init_value; +} +size_t esp_hosted_resp_set_wi_fi_maxtxpower__get_packed_size + (const EspHostedRespSetWiFiMAXTXPower *message) +{ + assert(message->base.descriptor == &esp_hosted_resp_set_wi_fi_maxtxpower__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t esp_hosted_resp_set_wi_fi_maxtxpower__pack + (const EspHostedRespSetWiFiMAXTXPower *message, + uint8_t *out) +{ + assert(message->base.descriptor == &esp_hosted_resp_set_wi_fi_maxtxpower__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t esp_hosted_resp_set_wi_fi_maxtxpower__pack_to_buffer + (const EspHostedRespSetWiFiMAXTXPower *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &esp_hosted_resp_set_wi_fi_maxtxpower__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +EspHostedRespSetWiFiMAXTXPower * + esp_hosted_resp_set_wi_fi_maxtxpower__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (EspHostedRespSetWiFiMAXTXPower *) + protobuf_c_message_unpack (&esp_hosted_resp_set_wi_fi_maxtxpower__descriptor, + allocator, len, data); +} +void esp_hosted_resp_set_wi_fi_maxtxpower__free_unpacked + (EspHostedRespSetWiFiMAXTXPower *message, + ProtobufCAllocator *allocator) +{ + assert(message->base.descriptor == &esp_hosted_resp_set_wi_fi_maxtxpower__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void esp_hosted_cmd_get_wi_fi_curr_txpower__init + (EspHostedCmdGetWiFiCurrTXPower *message) +{ + static EspHostedCmdGetWiFiCurrTXPower init_value = ESP_HOSTED_CMD_GET_WI_FI_CURR_TXPOWER__INIT; + *message = init_value; +} +size_t esp_hosted_cmd_get_wi_fi_curr_txpower__get_packed_size + (const EspHostedCmdGetWiFiCurrTXPower *message) +{ + assert(message->base.descriptor == &esp_hosted_cmd_get_wi_fi_curr_txpower__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t esp_hosted_cmd_get_wi_fi_curr_txpower__pack + (const EspHostedCmdGetWiFiCurrTXPower *message, + uint8_t *out) +{ + assert(message->base.descriptor == &esp_hosted_cmd_get_wi_fi_curr_txpower__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t esp_hosted_cmd_get_wi_fi_curr_txpower__pack_to_buffer + (const EspHostedCmdGetWiFiCurrTXPower *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &esp_hosted_cmd_get_wi_fi_curr_txpower__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +EspHostedCmdGetWiFiCurrTXPower * + esp_hosted_cmd_get_wi_fi_curr_txpower__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (EspHostedCmdGetWiFiCurrTXPower *) + protobuf_c_message_unpack (&esp_hosted_cmd_get_wi_fi_curr_txpower__descriptor, + allocator, len, data); +} +void esp_hosted_cmd_get_wi_fi_curr_txpower__free_unpacked + (EspHostedCmdGetWiFiCurrTXPower *message, + ProtobufCAllocator *allocator) +{ + assert(message->base.descriptor == &esp_hosted_cmd_get_wi_fi_curr_txpower__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void esp_hosted_resp_get_wi_fi_curr_txpower__init + (EspHostedRespGetWiFiCurrTXPower *message) +{ + static EspHostedRespGetWiFiCurrTXPower init_value = ESP_HOSTED_RESP_GET_WI_FI_CURR_TXPOWER__INIT; + *message = init_value; +} +size_t esp_hosted_resp_get_wi_fi_curr_txpower__get_packed_size + (const EspHostedRespGetWiFiCurrTXPower *message) +{ + assert(message->base.descriptor == &esp_hosted_resp_get_wi_fi_curr_txpower__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t esp_hosted_resp_get_wi_fi_curr_txpower__pack + (const EspHostedRespGetWiFiCurrTXPower *message, + uint8_t *out) +{ + assert(message->base.descriptor == &esp_hosted_resp_get_wi_fi_curr_txpower__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t esp_hosted_resp_get_wi_fi_curr_txpower__pack_to_buffer + (const EspHostedRespGetWiFiCurrTXPower *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &esp_hosted_resp_get_wi_fi_curr_txpower__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +EspHostedRespGetWiFiCurrTXPower * + esp_hosted_resp_get_wi_fi_curr_txpower__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (EspHostedRespGetWiFiCurrTXPower *) + protobuf_c_message_unpack (&esp_hosted_resp_get_wi_fi_curr_txpower__descriptor, + allocator, len, data); +} +void esp_hosted_resp_get_wi_fi_curr_txpower__free_unpacked + (EspHostedRespGetWiFiCurrTXPower *message, + ProtobufCAllocator *allocator) +{ + assert(message->base.descriptor == &esp_hosted_resp_get_wi_fi_curr_txpower__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} void esp_hosted_config_payload__init (EspHostedConfigPayload *message) { @@ -2963,7 +3135,152 @@ const ProtobufCMessageDescriptor esp_hosted_resp_set_vendor_specific_ie__descrip (ProtobufCMessageInit) esp_hosted_resp_set_vendor_specific_ie__init, NULL,NULL,NULL /* reserved[123] */ }; -static const ProtobufCFieldDescriptor esp_hosted_config_payload__field_descriptors[37] = +static const ProtobufCFieldDescriptor esp_hosted_cmd_set_wi_fi_maxtxpower__field_descriptors[1] = +{ + { + "wifi_max_tx_power", + 1, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_INT32, + offsetof(EspHostedCmdSetWiFiMAXTXPower, has_wifi_max_tx_power), + offsetof(EspHostedCmdSetWiFiMAXTXPower, wifi_max_tx_power), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned esp_hosted_cmd_set_wi_fi_maxtxpower__field_indices_by_name[] = { + 0, /* field[0] = wifi_max_tx_power */ +}; +static const ProtobufCIntRange esp_hosted_cmd_set_wi_fi_maxtxpower__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 1 } +}; +const ProtobufCMessageDescriptor esp_hosted_cmd_set_wi_fi_maxtxpower__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "EspHostedCmdSetWiFiMAXTXPower", + "EspHostedCmdSetWiFiMAXTXPower", + "EspHostedCmdSetWiFiMAXTXPower", + "", + sizeof(EspHostedCmdSetWiFiMAXTXPower), + 1, + esp_hosted_cmd_set_wi_fi_maxtxpower__field_descriptors, + esp_hosted_cmd_set_wi_fi_maxtxpower__field_indices_by_name, + 1, esp_hosted_cmd_set_wi_fi_maxtxpower__number_ranges, + (ProtobufCMessageInit) esp_hosted_cmd_set_wi_fi_maxtxpower__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor esp_hosted_resp_set_wi_fi_maxtxpower__field_descriptors[1] = +{ + { + "resp", + 1, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_INT32, + offsetof(EspHostedRespSetWiFiMAXTXPower, has_resp), + offsetof(EspHostedRespSetWiFiMAXTXPower, resp), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned esp_hosted_resp_set_wi_fi_maxtxpower__field_indices_by_name[] = { + 0, /* field[0] = resp */ +}; +static const ProtobufCIntRange esp_hosted_resp_set_wi_fi_maxtxpower__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 1 } +}; +const ProtobufCMessageDescriptor esp_hosted_resp_set_wi_fi_maxtxpower__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "EspHostedRespSetWiFiMAXTXPower", + "EspHostedRespSetWiFiMAXTXPower", + "EspHostedRespSetWiFiMAXTXPower", + "", + sizeof(EspHostedRespSetWiFiMAXTXPower), + 1, + esp_hosted_resp_set_wi_fi_maxtxpower__field_descriptors, + esp_hosted_resp_set_wi_fi_maxtxpower__field_indices_by_name, + 1, esp_hosted_resp_set_wi_fi_maxtxpower__number_ranges, + (ProtobufCMessageInit) esp_hosted_resp_set_wi_fi_maxtxpower__init, + NULL,NULL,NULL /* reserved[123] */ +}; +#define esp_hosted_cmd_get_wi_fi_curr_txpower__field_descriptors NULL +#define esp_hosted_cmd_get_wi_fi_curr_txpower__field_indices_by_name NULL +#define esp_hosted_cmd_get_wi_fi_curr_txpower__number_ranges NULL +const ProtobufCMessageDescriptor esp_hosted_cmd_get_wi_fi_curr_txpower__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "EspHostedCmdGetWiFiCurrTXPower", + "EspHostedCmdGetWiFiCurrTXPower", + "EspHostedCmdGetWiFiCurrTXPower", + "", + sizeof(EspHostedCmdGetWiFiCurrTXPower), + 0, + esp_hosted_cmd_get_wi_fi_curr_txpower__field_descriptors, + esp_hosted_cmd_get_wi_fi_curr_txpower__field_indices_by_name, + 0, esp_hosted_cmd_get_wi_fi_curr_txpower__number_ranges, + (ProtobufCMessageInit) esp_hosted_cmd_get_wi_fi_curr_txpower__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor esp_hosted_resp_get_wi_fi_curr_txpower__field_descriptors[2] = +{ + { + "wifi_curr_tx_power", + 1, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_INT32, + offsetof(EspHostedRespGetWiFiCurrTXPower, has_wifi_curr_tx_power), + offsetof(EspHostedRespGetWiFiCurrTXPower, wifi_curr_tx_power), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "resp", + 2, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_INT32, + offsetof(EspHostedRespGetWiFiCurrTXPower, has_resp), + offsetof(EspHostedRespGetWiFiCurrTXPower, resp), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned esp_hosted_resp_get_wi_fi_curr_txpower__field_indices_by_name[] = { + 1, /* field[1] = resp */ + 0, /* field[0] = wifi_curr_tx_power */ +}; +static const ProtobufCIntRange esp_hosted_resp_get_wi_fi_curr_txpower__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 2 } +}; +const ProtobufCMessageDescriptor esp_hosted_resp_get_wi_fi_curr_txpower__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "EspHostedRespGetWiFiCurrTXPower", + "EspHostedRespGetWiFiCurrTXPower", + "EspHostedRespGetWiFiCurrTXPower", + "", + sizeof(EspHostedRespGetWiFiCurrTXPower), + 2, + esp_hosted_resp_get_wi_fi_curr_txpower__field_descriptors, + esp_hosted_resp_get_wi_fi_curr_txpower__field_indices_by_name, + 1, esp_hosted_resp_get_wi_fi_curr_txpower__number_ranges, + (ProtobufCMessageInit) esp_hosted_resp_get_wi_fi_curr_txpower__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor esp_hosted_config_payload__field_descriptors[41] = { { "msg", @@ -3409,6 +3726,54 @@ static const ProtobufCFieldDescriptor esp_hosted_config_payload__field_descripto 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ 0,NULL,NULL /* reserved1,reserved2, etc */ }, + { + "cmd_set_wifi_max_tx_power", + 46, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(EspHostedConfigPayload, payload_case), + offsetof(EspHostedConfigPayload, cmd_set_wifi_max_tx_power), + &esp_hosted_cmd_set_wi_fi_maxtxpower__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "resp_set_wifi_max_tx_power", + 47, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(EspHostedConfigPayload, payload_case), + offsetof(EspHostedConfigPayload, resp_set_wifi_max_tx_power), + &esp_hosted_resp_set_wi_fi_maxtxpower__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "cmd_get_wifi_curr_tx_power", + 48, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(EspHostedConfigPayload, payload_case), + offsetof(EspHostedConfigPayload, cmd_get_wifi_curr_tx_power), + &esp_hosted_cmd_get_wi_fi_curr_txpower__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "resp_get_wifi_curr_tx_power", + 49, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(EspHostedConfigPayload, payload_case), + offsetof(EspHostedConfigPayload, resp_get_wifi_curr_tx_power), + &esp_hosted_resp_get_wi_fi_curr_txpower__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, }; static const unsigned esp_hosted_config_payload__field_indices_by_name[] = { 21, /* field[21] = cmd_connected_stas_list */ @@ -3417,6 +3782,7 @@ static const unsigned esp_hosted_config_payload__field_indices_by_name[] = { 1, /* field[1] = cmd_get_mac_address */ 27, /* field[27] = cmd_get_power_save_mode */ 11, /* field[11] = cmd_get_softap_config */ + 39, /* field[39] = cmd_get_wifi_curr_tx_power */ 3, /* field[3] = cmd_get_wifi_mode */ 29, /* field[29] = cmd_ota_begin */ 33, /* field[33] = cmd_ota_end */ @@ -3427,6 +3793,7 @@ static const unsigned esp_hosted_config_payload__field_indices_by_name[] = { 25, /* field[25] = cmd_set_power_save_mode */ 13, /* field[13] = cmd_set_softap_config */ 35, /* field[35] = cmd_set_vendor_specific_ie */ + 37, /* field[37] = cmd_set_wifi_max_tx_power */ 5, /* field[5] = cmd_set_wifi_mode */ 17, /* field[17] = cmd_stop_softap */ 0, /* field[0] = msg */ @@ -3436,6 +3803,7 @@ static const unsigned esp_hosted_config_payload__field_indices_by_name[] = { 2, /* field[2] = resp_get_mac_address */ 28, /* field[28] = resp_get_power_save_mode */ 12, /* field[12] = resp_get_softap_config */ + 40, /* field[40] = resp_get_wifi_curr_tx_power */ 4, /* field[4] = resp_get_wifi_mode */ 30, /* field[30] = resp_ota_begin */ 34, /* field[34] = resp_ota_end */ @@ -3446,6 +3814,7 @@ static const unsigned esp_hosted_config_payload__field_indices_by_name[] = { 26, /* field[26] = resp_set_power_save_mode */ 14, /* field[14] = resp_set_softap_config */ 36, /* field[36] = resp_set_vendor_specific_ie */ + 38, /* field[38] = resp_set_wifi_max_tx_power */ 6, /* field[6] = resp_set_wifi_mode */ 18, /* field[18] = resp_stop_softap */ }; @@ -3453,7 +3822,7 @@ static const ProtobufCIntRange esp_hosted_config_payload__number_ranges[2 + 1] = { { 1, 0 }, { 10, 1 }, - { 0, 37 } + { 0, 41 } }; const ProtobufCMessageDescriptor esp_hosted_config_payload__descriptor = { @@ -3463,7 +3832,7 @@ const ProtobufCMessageDescriptor esp_hosted_config_payload__descriptor = "EspHostedConfigPayload", "", sizeof(EspHostedConfigPayload), - 37, + 41, esp_hosted_config_payload__field_descriptors, esp_hosted_config_payload__field_indices_by_name, 2, esp_hosted_config_payload__number_ranges, @@ -3510,22 +3879,26 @@ const ProtobufCEnumDescriptor esp_hosted_encryption_mode__descriptor = esp_hosted_encryption_mode__value_ranges, NULL,NULL,NULL,NULL /* reserved[1234] */ }; -static const ProtobufCEnumValue esp_hosted_status__enum_values_by_number[4] = +static const ProtobufCEnumValue esp_hosted_status__enum_values_by_number[6] = { { "TYPE_CONNECTED", "ESP_HOSTED_STATUS__TYPE_CONNECTED", 0 }, { "TYPE_NOT_CONNECTED", "ESP_HOSTED_STATUS__TYPE_NOT_CONNECTED", 1 }, { "TYPE_NO_AP_FOUND", "ESP_HOSTED_STATUS__TYPE_NO_AP_FOUND", 2 }, { "TYPE_CONNECTION_FAIL", "ESP_HOSTED_STATUS__TYPE_CONNECTION_FAIL", 3 }, + { "TYPE_INVALID_ARGUMET", "ESP_HOSTED_STATUS__TYPE_INVALID_ARGUMET", 4 }, + { "TYPE_OUT_OF_RANGE", "ESP_HOSTED_STATUS__TYPE_OUT_OF_RANGE", 5 }, }; static const ProtobufCIntRange esp_hosted_status__value_ranges[] = { -{0, 0},{0, 4} +{0, 0},{0, 6} }; -static const ProtobufCEnumValueIndex esp_hosted_status__enum_values_by_name[4] = +static const ProtobufCEnumValueIndex esp_hosted_status__enum_values_by_name[6] = { { "TYPE_CONNECTED", 0 }, { "TYPE_CONNECTION_FAIL", 3 }, + { "TYPE_INVALID_ARGUMET", 4 }, { "TYPE_NOT_CONNECTED", 1 }, { "TYPE_NO_AP_FOUND", 2 }, + { "TYPE_OUT_OF_RANGE", 5 }, }; const ProtobufCEnumDescriptor esp_hosted_status__descriptor = { @@ -3534,9 +3907,9 @@ const ProtobufCEnumDescriptor esp_hosted_status__descriptor = "EspHostedStatus", "EspHostedStatus", "", - 4, + 6, esp_hosted_status__enum_values_by_number, - 4, + 6, esp_hosted_status__enum_values_by_name, 1, esp_hosted_status__value_ranges, @@ -3604,7 +3977,7 @@ const ProtobufCEnumDescriptor esp_hosted_ieid__descriptor = esp_hosted_ieid__value_ranges, NULL,NULL,NULL,NULL /* reserved[1234] */ }; -static const ProtobufCEnumValue esp_hosted_config_msg_type__enum_values_by_number[36] = +static const ProtobufCEnumValue esp_hosted_config_msg_type__enum_values_by_number[40] = { { "TypeCmdGetMACAddress", "ESP_HOSTED_CONFIG_MSG_TYPE__TypeCmdGetMACAddress", 0 }, { "TypeRespGetMACAddress", "ESP_HOSTED_CONFIG_MSG_TYPE__TypeRespGetMACAddress", 1 }, @@ -3642,11 +4015,15 @@ static const ProtobufCEnumValue esp_hosted_config_msg_type__enum_values_by_numbe { "TypeRespOTAEnd", "ESP_HOSTED_CONFIG_MSG_TYPE__TypeRespOTAEnd", 33 }, { "TypeCmdSetVendorSpecificIE", "ESP_HOSTED_CONFIG_MSG_TYPE__TypeCmdSetVendorSpecificIE", 34 }, { "TypeRespSetVendorSpecificIE", "ESP_HOSTED_CONFIG_MSG_TYPE__TypeRespSetVendorSpecificIE", 35 }, + { "TypeCmdSetWiFiMAXTXPower", "ESP_HOSTED_CONFIG_MSG_TYPE__TypeCmdSetWiFiMAXTXPower", 36 }, + { "TypeRespSetWiFiMAXTXPower", "ESP_HOSTED_CONFIG_MSG_TYPE__TypeRespSetWiFiMAXTXPower", 37 }, + { "TypeCmdGetWiFiCurrTXPower", "ESP_HOSTED_CONFIG_MSG_TYPE__TypeCmdGetWiFiCurrTXPower", 38 }, + { "TypeRespGetWiFiCurrTXPower", "ESP_HOSTED_CONFIG_MSG_TYPE__TypeRespGetWiFiCurrTXPower", 39 }, }; static const ProtobufCIntRange esp_hosted_config_msg_type__value_ranges[] = { -{0, 0},{0, 36} +{0, 0},{0, 40} }; -static const ProtobufCEnumValueIndex esp_hosted_config_msg_type__enum_values_by_name[36] = +static const ProtobufCEnumValueIndex esp_hosted_config_msg_type__enum_values_by_name[40] = { { "TypeCmdDisconnectAP", 14 }, { "TypeCmdGetAPConfig", 6 }, @@ -3655,6 +4032,7 @@ static const ProtobufCEnumValueIndex esp_hosted_config_msg_type__enum_values_by_ { "TypeCmdGetMACAddress", 0 }, { "TypeCmdGetPowerSaveMode", 26 }, { "TypeCmdGetSoftAPConfig", 10 }, + { "TypeCmdGetWiFiCurrTXPower", 38 }, { "TypeCmdGetWiFiMode", 2 }, { "TypeCmdOTABegin", 28 }, { "TypeCmdOTAEnd", 32 }, @@ -3664,6 +4042,7 @@ static const ProtobufCEnumValueIndex esp_hosted_config_msg_type__enum_values_by_ { "TypeCmdSetPowerSaveMode", 24 }, { "TypeCmdSetSoftAPConfig", 12 }, { "TypeCmdSetVendorSpecificIE", 34 }, + { "TypeCmdSetWiFiMAXTXPower", 36 }, { "TypeCmdSetWiFiMode", 4 }, { "TypeCmdStopSoftAP", 16 }, { "TypeRespDisconnectAP", 15 }, @@ -3673,6 +4052,7 @@ static const ProtobufCEnumValueIndex esp_hosted_config_msg_type__enum_values_by_ { "TypeRespGetMACAddress", 1 }, { "TypeRespGetPowerSaveMode", 27 }, { "TypeRespGetSoftAPConfig", 11 }, + { "TypeRespGetWiFiCurrTXPower", 39 }, { "TypeRespGetWiFiMode", 3 }, { "TypeRespOTABegin", 29 }, { "TypeRespOTAEnd", 33 }, @@ -3682,6 +4062,7 @@ static const ProtobufCEnumValueIndex esp_hosted_config_msg_type__enum_values_by_ { "TypeRespSetPowerSaveMode", 25 }, { "TypeRespSetSoftAPConfig", 13 }, { "TypeRespSetVendorSpecificIE", 35 }, + { "TypeRespSetWiFiMAXTXPower", 37 }, { "TypeRespSetWiFiMode", 5 }, { "TypeRespStopSoftAP", 17 }, }; @@ -3692,9 +4073,9 @@ const ProtobufCEnumDescriptor esp_hosted_config_msg_type__descriptor = "EspHostedConfigMsgType", "EspHostedConfigMsgType", "", - 36, + 40, esp_hosted_config_msg_type__enum_values_by_number, - 36, + 40, esp_hosted_config_msg_type__enum_values_by_name, 1, esp_hosted_config_msg_type__value_ranges, diff --git a/common/include/esp_hosted_config.pb-c.h b/common/include/esp_hosted_config.pb-c.h index dd6568fd4e..c7931ae17c 100644 --- a/common/include/esp_hosted_config.pb-c.h +++ b/common/include/esp_hosted_config.pb-c.h @@ -47,6 +47,10 @@ typedef struct _EspHostedCmdOTAEnd EspHostedCmdOTAEnd; typedef struct _EspHostedRespOTAEnd EspHostedRespOTAEnd; typedef struct _EspHostedCmdSetVendorSpecificIE EspHostedCmdSetVendorSpecificIE; typedef struct _EspHostedRespSetVendorSpecificIE EspHostedRespSetVendorSpecificIE; +typedef struct _EspHostedCmdSetWiFiMAXTXPower EspHostedCmdSetWiFiMAXTXPower; +typedef struct _EspHostedRespSetWiFiMAXTXPower EspHostedRespSetWiFiMAXTXPower; +typedef struct _EspHostedCmdGetWiFiCurrTXPower EspHostedCmdGetWiFiCurrTXPower; +typedef struct _EspHostedRespGetWiFiCurrTXPower EspHostedRespGetWiFiCurrTXPower; typedef struct _EspHostedConfigPayload EspHostedConfigPayload; @@ -67,7 +71,9 @@ typedef enum _EspHostedStatus { ESP_HOSTED_STATUS__TYPE_CONNECTED = 0, ESP_HOSTED_STATUS__TYPE_NOT_CONNECTED = 1, ESP_HOSTED_STATUS__TYPE_NO_AP_FOUND = 2, - ESP_HOSTED_STATUS__TYPE_CONNECTION_FAIL = 3 + ESP_HOSTED_STATUS__TYPE_CONNECTION_FAIL = 3, + ESP_HOSTED_STATUS__TYPE_INVALID_ARGUMET = 4, + ESP_HOSTED_STATUS__TYPE_OUT_OF_RANGE = 5 PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(ESP_HOSTED_STATUS) } EspHostedStatus; typedef enum _EspHostedVendorIEType { @@ -119,7 +125,11 @@ typedef enum _EspHostedConfigMsgType { ESP_HOSTED_CONFIG_MSG_TYPE__TypeCmdOTAEnd = 32, ESP_HOSTED_CONFIG_MSG_TYPE__TypeRespOTAEnd = 33, ESP_HOSTED_CONFIG_MSG_TYPE__TypeCmdSetVendorSpecificIE = 34, - ESP_HOSTED_CONFIG_MSG_TYPE__TypeRespSetVendorSpecificIE = 35 + ESP_HOSTED_CONFIG_MSG_TYPE__TypeRespSetVendorSpecificIE = 35, + ESP_HOSTED_CONFIG_MSG_TYPE__TypeCmdSetWiFiMAXTXPower = 36, + ESP_HOSTED_CONFIG_MSG_TYPE__TypeRespSetWiFiMAXTXPower = 37, + ESP_HOSTED_CONFIG_MSG_TYPE__TypeCmdGetWiFiCurrTXPower = 38, + ESP_HOSTED_CONFIG_MSG_TYPE__TypeRespGetWiFiCurrTXPower = 39 PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(ESP_HOSTED_CONFIG_MSG_TYPE) } EspHostedConfigMsgType; @@ -530,6 +540,50 @@ struct _EspHostedRespSetVendorSpecificIE , 0,0 } +struct _EspHostedCmdSetWiFiMAXTXPower +{ + ProtobufCMessage base; + protobuf_c_boolean has_wifi_max_tx_power; + int32_t wifi_max_tx_power; +}; +#define ESP_HOSTED_CMD_SET_WI_FI_MAXTXPOWER__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&esp_hosted_cmd_set_wi_fi_maxtxpower__descriptor) \ + , 0,0 } + + +struct _EspHostedRespSetWiFiMAXTXPower +{ + ProtobufCMessage base; + protobuf_c_boolean has_resp; + int32_t resp; +}; +#define ESP_HOSTED_RESP_SET_WI_FI_MAXTXPOWER__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&esp_hosted_resp_set_wi_fi_maxtxpower__descriptor) \ + , 0,0 } + + +struct _EspHostedCmdGetWiFiCurrTXPower +{ + ProtobufCMessage base; +}; +#define ESP_HOSTED_CMD_GET_WI_FI_CURR_TXPOWER__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&esp_hosted_cmd_get_wi_fi_curr_txpower__descriptor) \ + } + + +struct _EspHostedRespGetWiFiCurrTXPower +{ + ProtobufCMessage base; + protobuf_c_boolean has_wifi_curr_tx_power; + int32_t wifi_curr_tx_power; + protobuf_c_boolean has_resp; + int32_t resp; +}; +#define ESP_HOSTED_RESP_GET_WI_FI_CURR_TXPOWER__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&esp_hosted_resp_get_wi_fi_curr_txpower__descriptor) \ + , 0,0, 0,0 } + + typedef enum { ESP_HOSTED_CONFIG_PAYLOAD__PAYLOAD__NOT_SET = 0, ESP_HOSTED_CONFIG_PAYLOAD__PAYLOAD_CMD_GET_MAC_ADDRESS = 10, @@ -568,6 +622,10 @@ typedef enum { ESP_HOSTED_CONFIG_PAYLOAD__PAYLOAD_RESP_OTA_END = 43, ESP_HOSTED_CONFIG_PAYLOAD__PAYLOAD_CMD_SET_VENDOR_SPECIFIC_IE = 44, ESP_HOSTED_CONFIG_PAYLOAD__PAYLOAD_RESP_SET_VENDOR_SPECIFIC_IE = 45, + ESP_HOSTED_CONFIG_PAYLOAD__PAYLOAD_CMD_SET_WIFI_MAX_TX_POWER = 46, + ESP_HOSTED_CONFIG_PAYLOAD__PAYLOAD_RESP_SET_WIFI_MAX_TX_POWER = 47, + ESP_HOSTED_CONFIG_PAYLOAD__PAYLOAD_CMD_GET_WIFI_CURR_TX_POWER = 48, + ESP_HOSTED_CONFIG_PAYLOAD__PAYLOAD_RESP_GET_WIFI_CURR_TX_POWER = 49, } EspHostedConfigPayload__PayloadCase; struct _EspHostedConfigPayload @@ -613,6 +671,10 @@ struct _EspHostedConfigPayload EspHostedRespOTAEnd *resp_ota_end; EspHostedCmdSetVendorSpecificIE *cmd_set_vendor_specific_ie; EspHostedRespSetVendorSpecificIE *resp_set_vendor_specific_ie; + EspHostedCmdSetWiFiMAXTXPower *cmd_set_wifi_max_tx_power; + EspHostedRespSetWiFiMAXTXPower *resp_set_wifi_max_tx_power; + EspHostedCmdGetWiFiCurrTXPower *cmd_get_wifi_curr_tx_power; + EspHostedRespGetWiFiCurrTXPower *resp_get_wifi_curr_tx_power; }; }; #define ESP_HOSTED_CONFIG_PAYLOAD__INIT \ @@ -1228,6 +1290,82 @@ EspHostedRespSetVendorSpecificIE * void esp_hosted_resp_set_vendor_specific_ie__free_unpacked (EspHostedRespSetVendorSpecificIE *message, ProtobufCAllocator *allocator); +/* EspHostedCmdSetWiFiMAXTXPower methods */ +void esp_hosted_cmd_set_wi_fi_maxtxpower__init + (EspHostedCmdSetWiFiMAXTXPower *message); +size_t esp_hosted_cmd_set_wi_fi_maxtxpower__get_packed_size + (const EspHostedCmdSetWiFiMAXTXPower *message); +size_t esp_hosted_cmd_set_wi_fi_maxtxpower__pack + (const EspHostedCmdSetWiFiMAXTXPower *message, + uint8_t *out); +size_t esp_hosted_cmd_set_wi_fi_maxtxpower__pack_to_buffer + (const EspHostedCmdSetWiFiMAXTXPower *message, + ProtobufCBuffer *buffer); +EspHostedCmdSetWiFiMAXTXPower * + esp_hosted_cmd_set_wi_fi_maxtxpower__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void esp_hosted_cmd_set_wi_fi_maxtxpower__free_unpacked + (EspHostedCmdSetWiFiMAXTXPower *message, + ProtobufCAllocator *allocator); +/* EspHostedRespSetWiFiMAXTXPower methods */ +void esp_hosted_resp_set_wi_fi_maxtxpower__init + (EspHostedRespSetWiFiMAXTXPower *message); +size_t esp_hosted_resp_set_wi_fi_maxtxpower__get_packed_size + (const EspHostedRespSetWiFiMAXTXPower *message); +size_t esp_hosted_resp_set_wi_fi_maxtxpower__pack + (const EspHostedRespSetWiFiMAXTXPower *message, + uint8_t *out); +size_t esp_hosted_resp_set_wi_fi_maxtxpower__pack_to_buffer + (const EspHostedRespSetWiFiMAXTXPower *message, + ProtobufCBuffer *buffer); +EspHostedRespSetWiFiMAXTXPower * + esp_hosted_resp_set_wi_fi_maxtxpower__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void esp_hosted_resp_set_wi_fi_maxtxpower__free_unpacked + (EspHostedRespSetWiFiMAXTXPower *message, + ProtobufCAllocator *allocator); +/* EspHostedCmdGetWiFiCurrTXPower methods */ +void esp_hosted_cmd_get_wi_fi_curr_txpower__init + (EspHostedCmdGetWiFiCurrTXPower *message); +size_t esp_hosted_cmd_get_wi_fi_curr_txpower__get_packed_size + (const EspHostedCmdGetWiFiCurrTXPower *message); +size_t esp_hosted_cmd_get_wi_fi_curr_txpower__pack + (const EspHostedCmdGetWiFiCurrTXPower *message, + uint8_t *out); +size_t esp_hosted_cmd_get_wi_fi_curr_txpower__pack_to_buffer + (const EspHostedCmdGetWiFiCurrTXPower *message, + ProtobufCBuffer *buffer); +EspHostedCmdGetWiFiCurrTXPower * + esp_hosted_cmd_get_wi_fi_curr_txpower__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void esp_hosted_cmd_get_wi_fi_curr_txpower__free_unpacked + (EspHostedCmdGetWiFiCurrTXPower *message, + ProtobufCAllocator *allocator); +/* EspHostedRespGetWiFiCurrTXPower methods */ +void esp_hosted_resp_get_wi_fi_curr_txpower__init + (EspHostedRespGetWiFiCurrTXPower *message); +size_t esp_hosted_resp_get_wi_fi_curr_txpower__get_packed_size + (const EspHostedRespGetWiFiCurrTXPower *message); +size_t esp_hosted_resp_get_wi_fi_curr_txpower__pack + (const EspHostedRespGetWiFiCurrTXPower *message, + uint8_t *out); +size_t esp_hosted_resp_get_wi_fi_curr_txpower__pack_to_buffer + (const EspHostedRespGetWiFiCurrTXPower *message, + ProtobufCBuffer *buffer); +EspHostedRespGetWiFiCurrTXPower * + esp_hosted_resp_get_wi_fi_curr_txpower__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void esp_hosted_resp_get_wi_fi_curr_txpower__free_unpacked + (EspHostedRespGetWiFiCurrTXPower *message, + ProtobufCAllocator *allocator); /* EspHostedConfigPayload methods */ void esp_hosted_config_payload__init (EspHostedConfigPayload *message); @@ -1345,6 +1483,18 @@ typedef void (*EspHostedCmdSetVendorSpecificIE_Closure) typedef void (*EspHostedRespSetVendorSpecificIE_Closure) (const EspHostedRespSetVendorSpecificIE *message, void *closure_data); +typedef void (*EspHostedCmdSetWiFiMAXTXPower_Closure) + (const EspHostedCmdSetWiFiMAXTXPower *message, + void *closure_data); +typedef void (*EspHostedRespSetWiFiMAXTXPower_Closure) + (const EspHostedRespSetWiFiMAXTXPower *message, + void *closure_data); +typedef void (*EspHostedCmdGetWiFiCurrTXPower_Closure) + (const EspHostedCmdGetWiFiCurrTXPower *message, + void *closure_data); +typedef void (*EspHostedRespGetWiFiCurrTXPower_Closure) + (const EspHostedRespGetWiFiCurrTXPower *message, + void *closure_data); typedef void (*EspHostedConfigPayload_Closure) (const EspHostedConfigPayload *message, void *closure_data); @@ -1391,6 +1541,10 @@ extern const ProtobufCMessageDescriptor esp_hosted_cmd_otaend__descriptor; extern const ProtobufCMessageDescriptor esp_hosted_resp_otaend__descriptor; extern const ProtobufCMessageDescriptor esp_hosted_cmd_set_vendor_specific_ie__descriptor; extern const ProtobufCMessageDescriptor esp_hosted_resp_set_vendor_specific_ie__descriptor; +extern const ProtobufCMessageDescriptor esp_hosted_cmd_set_wi_fi_maxtxpower__descriptor; +extern const ProtobufCMessageDescriptor esp_hosted_resp_set_wi_fi_maxtxpower__descriptor; +extern const ProtobufCMessageDescriptor esp_hosted_cmd_get_wi_fi_curr_txpower__descriptor; +extern const ProtobufCMessageDescriptor esp_hosted_resp_get_wi_fi_curr_txpower__descriptor; extern const ProtobufCMessageDescriptor esp_hosted_config_payload__descriptor; PROTOBUF_C__END_DECLS diff --git a/common/proto/esp_hosted_config.proto b/common/proto/esp_hosted_config.proto index 63dec1e6dc..68757c67ab 100644 --- a/common/proto/esp_hosted_config.proto +++ b/common/proto/esp_hosted_config.proto @@ -69,6 +69,8 @@ enum EspHostedStatus { TYPE_NOT_CONNECTED = 1; TYPE_NO_AP_FOUND = 2; TYPE_CONNECTION_FAIL = 3; + TYPE_INVALID_ARGUMET = 4; + TYPE_OUT_OF_RANGE = 5; } message EspHostedCmdSetAPConfig { @@ -188,6 +190,22 @@ message EspHostedRespSetVendorSpecificIE { int32 resp = 1; } +message EspHostedCmdSetWiFiMAXTXPower { + int32 wifi_max_tx_power = 1; +} + +message EspHostedRespSetWiFiMAXTXPower { + int32 resp = 1; +} + +message EspHostedCmdGetWiFiCurrTXPower { +} + +message EspHostedRespGetWiFiCurrTXPower { + int32 wifi_curr_tx_power = 1; + int32 resp = 2; +} + enum EspHostedConfigMsgType { TypeCmdGetMACAddress = 0; TypeRespGetMACAddress = 1; @@ -225,6 +243,10 @@ enum EspHostedConfigMsgType { TypeRespOTAEnd = 33; TypeCmdSetVendorSpecificIE = 34; TypeRespSetVendorSpecificIE = 35; + TypeCmdSetWiFiMAXTXPower = 36; + TypeRespSetWiFiMAXTXPower = 37; + TypeCmdGetWiFiCurrTXPower = 38; + TypeRespGetWiFiCurrTXPower = 39; } message EspHostedConfigPayload { @@ -266,5 +288,9 @@ message EspHostedConfigPayload { EspHostedRespOTAEnd resp_ota_end = 43; EspHostedCmdSetVendorSpecificIE cmd_set_vendor_specific_ie = 44; EspHostedRespSetVendorSpecificIE resp_set_vendor_specific_ie = 45; + EspHostedCmdSetWiFiMAXTXPower cmd_set_wifi_max_tx_power = 46; + EspHostedRespSetWiFiMAXTXPower resp_set_wifi_max_tx_power = 47; + EspHostedCmdGetWiFiCurrTXPower cmd_get_wifi_curr_tx_power = 48; + EspHostedRespGetWiFiCurrTXPower resp_get_wifi_curr_tx_power = 49; } } diff --git a/docs/c_api.md b/docs/c_api.md index f7936cd373..bdd8f2a998 100644 --- a/docs/c_api.md +++ b/docs/c_api.md @@ -498,6 +498,44 @@ This specifies the file descriptor of the endpoint/socket to be closed --- +### 2.18 `int wifi_set_max_tx_power(int8_t wifi_max_tx_power)` + +Function sets maximum WiFi transmitting power at ESP32. + +#### **Note** +- The value set by this API will be mapped to the max_tx_power of the structure wifi_country_t variable in wifi driver. +- Mapping Table {wifi_max_tx_power, max_tx_power} = {{8, 2}, {20, 5}, {28, 7}, {34, 8}, {44, 11}, {52, 13}, {56, 14}, {60, 15}, {66, 16}, {72, 18}, {80, 20}}. +- Input parameter `wifi_max_tx_power` unit is 0.25dBm, range is [8, 84] corresponding to `2dBm to 20dBm`. +- Relationship between set value and actual value. As follows: {set value range, actual value} = {{[8, 19],8}, {[20, 27],20}, {[28, 33],28}, {[34, 43],34}, {[44, 51],44}, {[52, 55],52}, {[56, 59],56}, {[60, 65],60}, {[66, 71],66}, {[72, 79],72}, {[80, 84],80}}. + +#### Parameters +- `wifi_max_tx_power` : +Maximum WiFi transmitting power. + +#### Return + +- 0 : SUCCESS +- -1 : FAILURE +- 5 : OUT_OF_RANGE. `wifi_max_tx_power` is not in range of [8, 84] corresponding to `2dBm to 20dBm` tx power. + +--- + +### 2.19 `int wifi_get_curr_tx_power(int8_t *wifi_curr_tx_power)` + +Function gets current WiFi transmiting power at ESP32. + +#### Parameters +- `wifi_curr_tx_power` : +Current WiFi transmitting power, unit is 0.25dBm. + +#### **Note** +It is possible that the current wifi transmit power is lesser than that of the requested max transmit power as part of `wifi_set_max_tx_power` API. + +#### Return +- 0 : SUCCESS +- -1 : FAILURE + +--- ## 3. Enumerations diff --git a/docs/c_demo.md b/docs/c_demo.md index 77ef72f65b..71f56798d9 100644 --- a/docs/c_demo.md +++ b/docs/c_demo.md @@ -11,6 +11,7 @@ | scan | Scan external access points | | sta_list | List external stations connected to softAP | | ap_vendor_ie | Set vendor information element for ESP32 softAP | +| wifi_tx_power | sets WiFi maximum transmitting power and get WiFi current transmitting power | It uses APIs present in [test_api.c](../host/linux/host_control/c_support/test_api.c). User should first modify configuration parameters in [test_config.h](../host/linux/host_control/c_support/test_config.h). Then run `make` in [c_support](../host/linux/host_control/c_support) to compile `test.c`. @@ -57,6 +58,7 @@ sudo ifconfig ethap0 192.168.4.5 | scan | Scan external access points | | sta_list | List external stations connected to softAP | | ap_vendor_ie | Set vendor information element for ESP32 softAP | +| wifi_tx_power | sets WiFi maximum transmitting power and get WiFi current transmitting power | Run `make stress` in [c_support](../host/linux/host_control/c_support) directory to compile `stress.c`. diff --git a/docs/python_api.md b/docs/python_api.md index eef7286bdd..68fb2a31fe 100644 --- a/docs/python_api.md +++ b/docs/python_api.md @@ -303,3 +303,42 @@ This specifies the file descriptor of the endpoint/socket to be closed - "success" or "failure" string --- + +## 17. `wifi_set_max_tx_power` + +Function sets maximum WiFi transmitting power at ESP32. + +#### **Note** +- The value set by this API will be mapped to the max_tx_power of the structure wifi_country_t variable in wifi driver. +- Mapping Table {wifi_max_tx_power, max_tx_power} = {{8, 2}, {20, 5}, {28, 7}, {34, 8}, {44, 11}, {52, 13}, {56, 14}, {60, 15}, {66, 16}, {72, 18}, {80, 20}}. +- Input parameter `wifi_max_tx_power` unit is 0.25dBm, range is [8, 84] corresponding to `2dBm to 20dBm`. +- Relationship between set value and actual value. As follows: {set value range, actual value} = {{[8, 19],8}, {[20, 27],20}, {[28, 33],28}, {[34, 43],34}, {[44, 51],44}, {[52, 55],52}, {[56, 59],56}, {[60, 65],60}, {[66, 71],66}, {[72, 79],72}, {[80, 84],80}}. + +### Parameters +- `wifi_max_tx_power` : +Maximum WiFi transmitting power. + +### Return +*Success case* : +"success" string + +*Failure case* : +"failure" string + +*Out of range case*: +"out_of_range" string. `wifi_max_tx_power` is not in range of [8, 84] corresponding to `2dBm to 20dBm` tx power. + +--- + +### 18. `wifi_get_curr_tx_power` + +Function gets current WiFi transmiting power at ESP32. + +### Return +*Success case* : +returns Current WiFi transmitting power, unit is 0.25dBm. It is possible that the current wifi transmit power is lesser than that of the requested max transmit power as part of `wifi_set_max_tx_power` API. + +*Failure case* : +- "failure" string + +--- diff --git a/esp/esp_driver/network_adapter/main/slave_commands.c b/esp/esp_driver/network_adapter/main/slave_commands.c index 8773db7158..883e8c1089 100644 --- a/esp/esp_driver/network_adapter/main/slave_commands.c +++ b/esp/esp_driver/network_adapter/main/slave_commands.c @@ -29,6 +29,8 @@ #define SSID_LENGTH 32 #define PASSWORD_LENGTH 64 #define BSSID_LENGTH 19 +#define MIN_TX_POWER 8 +#define MAX_TX_POWER 84 /* Bits for wifi connect event */ #define WIFI_CONNECTED_BIT BIT0 @@ -1580,6 +1582,89 @@ static esp_err_t cmd_set_vender_specific_ie_handler (EspHostedConfigPayload *req return ESP_OK; } +// Function set wifi maximum TX power +static esp_err_t cmd_set_wifi_max_tx_power_handler (EspHostedConfigPayload *req, + EspHostedConfigPayload *resp, void *priv_data) +{ + esp_err_t ret = ESP_OK; + EspHostedRespSetWiFiMAXTXPower *resp_payload = NULL; + + if (!req || !resp ) { + ESP_LOGE(TAG, "Invalid parameters"); + return ESP_FAIL; + } + + resp_payload = (EspHostedRespSetWiFiMAXTXPower *) + calloc(1,sizeof(EspHostedRespSetWiFiMAXTXPower)); + if (!resp_payload) { + ESP_LOGE(TAG,"Failed to allocate memory"); + return ESP_ERR_NO_MEM; + } + esp_hosted_resp_set_wi_fi_maxtxpower__init(resp_payload); + resp->payload_case = ESP_HOSTED_CONFIG_PAYLOAD__PAYLOAD_RESP_SET_WIFI_MAX_TX_POWER; + resp->resp_set_wifi_max_tx_power = resp_payload; + resp_payload->has_resp = true; + + if ((req->cmd_set_wifi_max_tx_power->wifi_max_tx_power > MAX_TX_POWER) + || (req->cmd_set_wifi_max_tx_power->wifi_max_tx_power < MIN_TX_POWER)) { + ESP_LOGE(TAG, "Invalid maximum transmitting power value"); + ESP_LOGE(TAG, "Value lies between [8,84]"); + ESP_LOGE(TAG, "Please refer `wifi_set_max_tx_power` API documentation \n"); + resp_payload->resp = ESP_HOSTED_STATUS__TYPE_OUT_OF_RANGE; + return ESP_OK; + } + + ret = esp_wifi_set_max_tx_power(req->cmd_set_wifi_max_tx_power->wifi_max_tx_power); + if (ret != SUCCESS) { + ESP_LOGE(TAG, "Failed to set TX power"); + goto err; + } + resp_payload->resp = SUCCESS; + return ESP_OK; +err: + resp_payload->resp = FAILURE; + return ESP_OK; +} + +// Function get wifi TX current power +static esp_err_t cmd_get_wifi_curr_tx_power_handler (EspHostedConfigPayload *req, + EspHostedConfigPayload *resp, void *priv_data) +{ + esp_err_t ret = ESP_OK; + int8_t power = 0; + EspHostedRespGetWiFiCurrTXPower *resp_payload = NULL; + + if (!req || !resp) { + ESP_LOGE(TAG, "Invalid parameters"); + return ESP_FAIL; + } + + resp_payload = (EspHostedRespGetWiFiCurrTXPower *) + calloc(1,sizeof(EspHostedRespGetWiFiCurrTXPower)); + if (!resp_payload) { + ESP_LOGE(TAG,"Failed to allocate memory"); + return ESP_ERR_NO_MEM; + } + + esp_hosted_resp_get_wi_fi_curr_txpower__init(resp_payload); + resp->payload_case = ESP_HOSTED_CONFIG_PAYLOAD__PAYLOAD_RESP_GET_WIFI_CURR_TX_POWER; + resp->resp_get_wifi_curr_tx_power = resp_payload; + resp_payload->has_resp = true; + + ret = esp_wifi_get_max_tx_power(&power); + if (ret != SUCCESS) { + ESP_LOGE(TAG, "Failed to get TX power"); + goto err; + } + resp_payload->has_wifi_curr_tx_power = true; + resp_payload->wifi_curr_tx_power = power; + resp_payload->resp = SUCCESS; + return ESP_OK; +err: + resp_payload->resp = FAILURE; + return ESP_OK; +} + static esp_hosted_config_cmd_t cmd_table[] = { { .cmd_num = ESP_HOSTED_CONFIG_MSG_TYPE__TypeCmdGetMACAddress , @@ -1653,6 +1738,14 @@ static esp_hosted_config_cmd_t cmd_table[] = { .cmd_num = ESP_HOSTED_CONFIG_MSG_TYPE__TypeCmdSetVendorSpecificIE, .command_handler = cmd_set_vender_specific_ie_handler }, + { + .cmd_num = ESP_HOSTED_CONFIG_MSG_TYPE__TypeCmdSetWiFiMAXTXPower, + .command_handler = cmd_set_wifi_max_tx_power_handler + }, + { + .cmd_num = ESP_HOSTED_CONFIG_MSG_TYPE__TypeCmdGetWiFiCurrTXPower, + .command_handler = cmd_get_wifi_curr_tx_power_handler + }, }; @@ -1806,6 +1899,14 @@ static void esp_hosted_config_cleanup(EspHostedConfigPayload *resp) mem_free(resp->resp_set_vendor_specific_ie); break; } + case (ESP_HOSTED_CONFIG_MSG_TYPE__TypeRespSetWiFiMAXTXPower) : { + mem_free(resp->resp_set_wifi_max_tx_power); + break; + } + case (ESP_HOSTED_CONFIG_MSG_TYPE__TypeRespGetWiFiCurrTXPower) : { + mem_free(resp->resp_get_wifi_curr_tx_power); + break; + } default: ESP_LOGE(TAG, "Unsupported response type"); break; diff --git a/host/host_common/commands.c b/host/host_common/commands.c index 41bfd8d708..bc16d4bc9f 100644 --- a/host/host_common/commands.c +++ b/host/host_common/commands.c @@ -1307,6 +1307,147 @@ int wifi_set_vendor_specific_ie(bool enable, wifi_vendor_ie_type_t type, return FAILURE; } +// Function sets maximum transmitting power +int wifi_set_max_tx_power(int8_t wifi_max_tx_power) +{ + EspHostedConfigPayload req, *resp = NULL; + uint32_t tx_len = 0, rx_len = 0; + uint8_t *tx_data = NULL, *rx_data = NULL; + + esp_hosted_config_payload__init (&req); + req.has_msg = true; + req.msg = ESP_HOSTED_CONFIG_MSG_TYPE__TypeCmdSetWiFiMAXTXPower; + req.payload_case = ESP_HOSTED_CONFIG_PAYLOAD__PAYLOAD_CMD_SET_WIFI_MAX_TX_POWER; + + EspHostedCmdSetWiFiMAXTXPower *req_payload = (EspHostedCmdSetWiFiMAXTXPower *) + esp_hosted_calloc( 1, sizeof(EspHostedCmdSetWiFiMAXTXPower)); + if (!req_payload) { + command_log("Failed to allocate memory for req_payload\n"); + return FAILURE; + } + + esp_hosted_cmd_set_wi_fi_maxtxpower__init(req_payload); + req_payload->has_wifi_max_tx_power = true; + req_payload->wifi_max_tx_power = wifi_max_tx_power; + req.cmd_set_wifi_max_tx_power = req_payload; + + tx_len = esp_hosted_config_payload__get_packed_size(&req); + if (!tx_len) { + command_log("Invalid tx length\n"); + goto err3; + } + + tx_data = (uint8_t *)esp_hosted_calloc(1, tx_len); + if (!tx_data) { + command_log("Failed to allocate memory for tx_data\n"); + goto err3; + } + + esp_hosted_config_payload__pack(&req, tx_data); + + rx_data = transport_pserial_data_handler(tx_data, tx_len, + TIMEOUT_PSERIAL_RESP, &rx_len); + if (!rx_data || !rx_len) { + command_log("Failed to process rx_data\n"); + goto err2; + } + + resp = esp_hosted_config_payload__unpack(NULL, rx_len, rx_data); + if ((!resp) || (!resp->resp_set_wifi_max_tx_power)) { + command_log("Failed to unpack rx_data\n"); + goto err1; + } + + if (resp->resp_set_wifi_max_tx_power->resp == ESP_HOSTED_STATUS__TYPE_OUT_OF_RANGE) { + command_log("Out of range maximum transmitting power argument \n"); + command_log("Must lie between [8,84] \n"); + command_log("Please refer `wifi_set_max_tx_power` API documentation \n"); + mem_free(tx_data); + mem_free(rx_data); + mem_free(req_payload); + esp_hosted_config_payload__free_unpacked(resp, NULL); + return OUT_OF_RANGE; + } else if (resp->resp_set_wifi_max_tx_power->resp) { + command_log("Failed to set WiFi maximum TX power\n"); + goto err1; + } + + mem_free(tx_data); + mem_free(rx_data); + mem_free(req_payload); + esp_hosted_config_payload__free_unpacked(resp, NULL); + return SUCCESS; +err1: + mem_free(rx_data); + esp_hosted_config_payload__free_unpacked(resp, NULL); +err2: + mem_free(tx_data); +err3: + mem_free(req_payload); + return FAILURE; +} + +// Function gets wifi current transmiting power +int wifi_get_curr_tx_power(int8_t *wifi_curr_tx_power) +{ + EspHostedConfigPayload req, *resp = NULL; + uint32_t tx_len = 0, rx_len = 0; + uint8_t *tx_data = NULL, *rx_data = NULL; + + if (!wifi_curr_tx_power) { + printf("Invalid argument \n"); + return FAILURE; + } + + esp_hosted_config_payload__init (&req); + req.has_msg = true; + req.msg = ESP_HOSTED_CONFIG_MSG_TYPE__TypeCmdGetWiFiCurrTXPower; + req.payload_case = ESP_HOSTED_CONFIG_PAYLOAD__PAYLOAD_CMD_GET_WIFI_CURR_TX_POWER; + tx_len = esp_hosted_config_payload__get_packed_size(&req); + if (!tx_len) { + command_log("Invalid tx length\n"); + return FAILURE; + } + + tx_data = (uint8_t *)esp_hosted_calloc(1, tx_len); + if (!tx_data) { + command_log("Failed to allocate memory for tx_data\n"); + return FAILURE; + } + + esp_hosted_config_payload__pack(&req, tx_data); + + rx_data = transport_pserial_data_handler(tx_data, tx_len, + TIMEOUT_PSERIAL_RESP, &rx_len); + if (!rx_data || !rx_len) { + command_log("Failed to process rx_data\n"); + goto err2; + } + + resp = esp_hosted_config_payload__unpack(NULL, rx_len, rx_data); + if ((!resp) || (!resp->resp_get_wifi_curr_tx_power)) { + command_log("Failed to unpack rx_data\n"); + goto err1; + } + + if (resp->resp_get_wifi_curr_tx_power->resp) { + command_log("Failed to get WiFi current TX power\n"); + goto err1; + } + *wifi_curr_tx_power = resp->resp_get_wifi_curr_tx_power->wifi_curr_tx_power; + mem_free(tx_data); + mem_free(rx_data); + esp_hosted_config_payload__free_unpacked(resp, NULL); + return SUCCESS; + +err1: + mem_free(rx_data); +err2: + mem_free(tx_data); + esp_hosted_config_payload__free_unpacked(resp, NULL); + return FAILURE; +} + // Function performs an OTA begin for ESP32 int esp_ota_begin() { diff --git a/host/host_common/include/commands.h b/host/host_common/include/commands.h index 2a7f62aa9b..e90bc1da87 100644 --- a/host/host_common/include/commands.h +++ b/host/host_common/include/commands.h @@ -18,6 +18,8 @@ #define NOT_CONNECTED 1 #define NO_AP_FOUND 2 #define INVALID_PASSWORD 3 +#define INVALID_ARGUMENT 4 +#define OUT_OF_RANGE 5 typedef enum { WIFI_MODE_NONE = 0, /**< null mode */ @@ -106,8 +108,8 @@ typedef struct { typedef struct { uint8_t element_id; /**< Should be set to WIFI_VENDOR_IE_ELEMENT_ID (0xDD) */ uint8_t length; /**< Length of all bytes in the element data following this field. Minimum 4. */ - uint8_t vendor_oui[3]; /**< Vendor identifier (OUI). */ - uint8_t vendor_oui_type; /**< Vendor-specific OUI type. */ + uint8_t vendor_oui[3]; /**< Vendor identifier (OUI). */ + uint8_t vendor_oui_type; /**< Vendor-specific OUI type. */ uint8_t payload[0]; /**< Payload. Length is equal to value in 'length' field, minus 4. */ } vendor_ie_data_t; @@ -299,6 +301,32 @@ int wifi_get_power_save_mode(int *power_save_mode); int wifi_set_vendor_specific_ie(bool enable, wifi_vendor_ie_type_t type, wifi_vendor_ie_id_t idx, void* vnd_ie, uint16_t vnd_ie_size); +/* + * wifi_set_max_tx_power function set maximum transmitting power. + * returns SUCCESS(0) or FAILURE(-1) + * + * @attention 1. The value set by this API will be mapped to the max_tx_power of the structure wifi_country_t variable in wifi driver. + * @attention 2. Mapping Table {wifi_max_tx_power, max_tx_power} = {{8, 2}, {20, 5}, {28, 7}, {34, 8}, {44, 11}, + * {52, 13}, {56, 14}, {60, 15}, {66, 16}, {72, 18}, {80, 20}}. + * @attention 4. Param power unit is 0.25dBm, range is [8, 84] corresponding to 2dBm to 20dBm. + * @attention 5. Relationship between set value and actual value. As follows: {set value range, actual value} = {{[8, 19],8}, {[20, 27],20}, {[28, 33],28}, {[34, 43],34}, {[44, 51],44}, {[52, 55],52}, {[56, 59],56}, {[60, 65],60}, {[66, 71],66}, {[72, 79],72}, {[80, 84],80}}. + * + * Input parameter: + * wifi_max_tx_power : Maximum WiFi transmitting power. + * + * returns OUT_OF_RANGE(5), If `wifi_max_tx_power` range is not in [8, 84] corresponding to `2dBm to 20dBm` tx power. + */ +int wifi_set_max_tx_power(int8_t wifi_max_tx_power); + +/* + * wifi_get_curr_tx_power function gets WiFi current transmiting power + * returns SUCCESS(0) or FAILURE(-1) + * + * Output parameter: + * wifi_curr_tx_power : WiFi current transmitting power, unit is 0.25dBm. + */ +int wifi_get_curr_tx_power(int8_t *wifi_curr_tx_power); + /* * esp ota begin function performs an OTA begin operation for ESP32 * which sets partition for OTA write and erase it. @@ -344,7 +372,7 @@ int interface_down(int sockfd, char* iface); int set_hw_addr(int sockfd, char* iface, char* mac); /* - * function creates an endpoint for communication and returns a file descriptor (integer number) that refers to that endpoint + * function creates an endpoint for communication and returns a file descriptor (integer number) that refers to that endpoint * function returns the status SUCCESS(0) or FAILURE(-1) * int *sock : User should get file descriptor for the new socket on success */ diff --git a/host/linux/host_control/c_support/stress.c b/host/linux/host_control/c_support/stress.c index 7bd7e92af5..17978233c5 100644 --- a/host/linux/host_control/c_support/stress.c +++ b/host/linux/host_control/c_support/stress.c @@ -55,6 +55,10 @@ int main(int argc, char *argv[]) if (0 == strncasecmp(STA_LIST, argv[i], sizeof(STA_LIST))) { test_softap_mode_connected_clients_info(); } + if (0 == strncasecmp(WIFI_TX_POWER, argv[i], sizeof(WIFI_TX_POWER))) { + test_wifi_set_max_tx_power(); + test_wifi_get_curr_tx_power(); + } } } diff --git a/host/linux/host_control/c_support/test.c b/host/linux/host_control/c_support/test.c index 506c1b1c59..bcd1929991 100644 --- a/host/linux/host_control/c_support/test.c +++ b/host/linux/host_control/c_support/test.c @@ -33,9 +33,9 @@ int main(int argc, char *argv[]) } if (argc == 1) { - printf("Usage: %s [%s] [%s] [%s] [%s] [%s] [%s] [%s] [%s]\n", argv[0], + printf("Usage: %s [%s] [%s] [%s] [%s] [%s] [%s] [%s] [%s] [%s] \n", argv[0], STA_CONNECT, STA_DISCONNECT, AP_START, AP_STOP, - SCAN, STA_LIST, OTA, AP_VENDOR_IE); + SCAN, STA_LIST, OTA, AP_VENDOR_IE, WIFI_TX_POWER); return -1; } @@ -54,13 +54,17 @@ int main(int argc, char *argv[]) test_softap_mode_connected_clients_info(); else if (0 == strncasecmp(OTA, argv[i], sizeof(OTA))) test_ota(argv[i+1]); + else if (0 == strncasecmp(WIFI_TX_POWER, argv[i], sizeof(WIFI_TX_POWER))) { + test_wifi_set_max_tx_power(); + test_wifi_get_curr_tx_power(); + } else if (0 == strncasecmp(AP_VENDOR_IE, argv[i], sizeof(AP_VENDOR_IE))) test_set_vendor_specific_ie(); else if ((0 == strncasecmp("--help", argv[i], sizeof("--help"))) || (0 == strncasecmp("-h", argv[i], sizeof("-h")))) { - printf("Usage: %s [%s] [%s] [%s] [%s] [%s] [%s] [%s] [%s]\n", argv[0], + printf("Usage: %s [%s] [%s] [%s] [%s] [%s] [%s] [%s] [%s] [%s] \n", argv[0], STA_CONNECT, STA_DISCONNECT, AP_START, AP_STOP, SCAN, STA_LIST, - OTA, AP_VENDOR_IE); + OTA, AP_VENDOR_IE, WIFI_TX_POWER); return(0); } } diff --git a/host/linux/host_control/c_support/test_api.c b/host/linux/host_control/c_support/test_api.c index 6e744e30da..50496fd1f9 100644 --- a/host/linux/host_control/c_support/test_api.c +++ b/host/linux/host_control/c_support/test_api.c @@ -34,6 +34,7 @@ #define VENDOR_OUI_2 3 #define VENDOR_OUI_TYPE 22 + int test_get_wifi_mode() { int mode = 0; @@ -679,3 +680,40 @@ int test_set_vendor_specific_ie() free(vnd_ie); return ret; } + +int test_wifi_set_max_tx_power() +{ + int ret = wifi_set_max_tx_power(INPUT_WIFI_TX_POWER); + +#ifdef TEST_DEBUG_PRINTS + printf("==== %s =>\n",__func__); + if (ret == SUCCESS) { + printf("Success in setting max tx power\n"); + } else if (ret == OUT_OF_RANGE) { + printf("Out of range TX value \n"); + } else { + printf("Failure in setting max tx power\n"); + } + printf("====\n\n"); +#endif + + return ret; +} + +int test_wifi_get_curr_tx_power() +{ + int8_t curr_tx_power = 0; + int ret = wifi_get_curr_tx_power(&curr_tx_power); + +#ifdef TEST_DEBUG_PRINTS + printf("==== %s =>\n",__func__); + if (ret == SUCCESS) { + printf("current TX power is %d \n", curr_tx_power); + } else { + printf("Failure in getting current tx power\n"); + } + printf("====\n\n"); +#endif + + return ret; +} diff --git a/host/linux/host_control/c_support/test_api.h b/host/linux/host_control/c_support/test_api.h index 8c987fea59..733c31274e 100644 --- a/host/linux/host_control/c_support/test_api.h +++ b/host/linux/host_control/c_support/test_api.h @@ -76,4 +76,8 @@ int test_ota_end(); int test_ota(char* image_path); int test_set_vendor_specific_ie(); + +int test_wifi_set_max_tx_power(); + +int test_wifi_get_curr_tx_power(); #endif diff --git a/host/linux/host_control/c_support/test_config.h b/host/linux/host_control/c_support/test_config.h index d37d1cd511..bd963f9576 100644 --- a/host/linux/host_control/c_support/test_config.h +++ b/host/linux/host_control/c_support/test_config.h @@ -29,6 +29,7 @@ #define STA_LIST "sta_list" #define OTA "ota" #define AP_VENDOR_IE "ap_vendor_ie" +#define WIFI_TX_POWER "wifi_tx_power" #define MAC_LENGTH 18 #define SSID_LENGTH 32 @@ -53,6 +54,8 @@ #define SOFTAP_MODE_SSID_HIDDEN false #define SOFTAP_MODE_BANDWIDTH 2 +#define INPUT_WIFI_TX_POWER 8 + #define TEST_DEBUG_PRINTS 1 #endif diff --git a/host/linux/host_control/python_support/commands_lib.py b/host/linux/host_control/python_support/commands_lib.py index ac4ec8d90b..593ce89503 100644 --- a/host/linux/host_control/python_support/commands_lib.py +++ b/host/linux/host_control/python_support/commands_lib.py @@ -133,7 +133,11 @@ def wifi_set_ap_config(ssid, pwd, bssid, is_wpa3_supported, listen_interval): ap_config.station.is_wpa3_supported = is_wpa3_supported ap_config.station.listen_interval = listen_interval ret = commands_map_py_to_c.wifi_set_ap_config(ap_config) - if not ret: + if (ret == NO_AP_FOUND): + return no_ap_found_str + elif (ret == INVALID_PASSWORD): + return invalid_password_str + elif not ret: return success else: return failure @@ -401,6 +405,43 @@ def wifi_get_power_save_mode(): else: return failure +# wifi_set_max_tx_power function set maximum transmitting power, returns "success" or "failure" +# +# @attention 1. The value set by this API will be mapped to the max_tx_power of the structure wifi_country_t variable in wifi driver. +# @attention 2. Mapping Table {wifi_max_tx_power, max_tx_power} = {{8, 2}, {20, 5}, {28, 7}, {34, 8}, {44, 11}, +# {52, 13}, {56, 14}, {60, 15}, {66, 16}, {72, 18}, {80, 20}}. +# @attention 4. Param power unit is 0.25dBm, range is [8, 84] corresponding to 2dBm to 20dBm. +# @attention 5. Relationship between set value and actual value. As follows: {set value range, actual value} = {{[8, 19],8}, {[20, 27],20}, {[28, 33],28}, {[34, 43],34}, {[44, 51],44}, {[52, 55],52}, {[56, 59],56}, {[60, 65],60}, {[66, 71],66}, {[72, 79],72}, {[80, 84],80}}. +# +# Input parameter: +# wifi_max_tx_power : Maximum WiFi transmitting power. +# +# Returns "out_of_range" string. If `wifi_max_tx_power` range is not in [8, 84] corresponding to `2dBm to 20dBm` tx power. + +def wifi_set_max_tx_power(wifi_max_tx_power): + set_power = c_int() + set_power.value = wifi_max_tx_power + ret = commands_map_py_to_c.wifi_set_max_tx_power(set_power) + if (ret == OUT_OF_RANGE): + return out_of_range_str + elif not ret: + return success + else: + return failure + +# wifi_get_curr_tx_power function gets current transmiting power, or returns "failure" +# +# Output parameter: +# wifi_curr_tx_power : Current WiFi transmitting power, unit is 0.25dBm. + +def wifi_get_curr_tx_power(): + wifi_curr_tx_power = c_int() + ret = commands_map_py_to_c.wifi_get_curr_tx_power(byref(wifi_curr_tx_power)) + if not ret: + return wifi_curr_tx_power.value + else: + return failure + # OTA begin # function returns "success" or "failure" # esp ota begin function performs an OTA begin operation for ESP32 diff --git a/host/linux/host_control/python_support/commands_map_py_to_c.py b/host/linux/host_control/python_support/commands_map_py_to_c.py index 6de1c2eff3..61f1220df8 100644 --- a/host/linux/host_control/python_support/commands_map_py_to_c.py +++ b/host/linux/host_control/python_support/commands_map_py_to_c.py @@ -67,6 +67,12 @@ wifi_stop_softap = commands_lib.wifi_stop_softap wifi_stop_softap.restype = c_int +wifi_set_max_tx_power = commands_lib.wifi_set_max_tx_power +wifi_set_max_tx_power.restype = c_int + +wifi_get_curr_tx_power = commands_lib.wifi_get_curr_tx_power +wifi_get_curr_tx_power.restype = c_int + esp_hosted_free = commands_lib.esp_hosted_free esp_hosted_free.restype = None diff --git a/host/linux/host_control/python_support/hosted_config.py b/host/linux/host_control/python_support/hosted_config.py index 5203cfbb31..2f3edee6f0 100644 --- a/host/linux/host_control/python_support/hosted_config.py +++ b/host/linux/host_control/python_support/hosted_config.py @@ -34,6 +34,18 @@ SSID_BROADCAST = 0 SSID_NOT_BROADCAST = 1 +NOT_CONNECTED = 1 +NO_AP_FOUND = 2 +INVALID_PASSWORD = 3 +INVALID_ARGUMENT = 4 +OUT_OF_RANGE = 5 + +not_connected_str = "not_connected" +no_ap_found_str = "no_ap_found" +invalid_password_str = "invalid_password" +invalid_argument_str = "invalid_argument" +out_of_range_str = "out_of_range" + (WIFI_MODE_NONE, WIFI_MODE_STATION, WIFI_MODE_SOFTAP, WIFI_MODE_SOFTAP_STATION, WIFI_MODE_MAX) = (0, 1, 2, 3, 4) diff --git a/host/linux/host_control/python_support/stress.py b/host/linux/host_control/python_support/stress.py index d964291a9a..72d1e21bf7 100644 --- a/host/linux/host_control/python_support/stress.py +++ b/host/linux/host_control/python_support/stress.py @@ -25,8 +25,9 @@ TEST_SOFTAP_START_STOP=(1 << 5) TEST_STATION_SOFTAP_MODE=(1 << 6) TEST_POWER_SAVE=(1 << 7) +TEST_WIFI_TX_POWER=(1 << 8) -STRESS_TEST=(TEST_MODE_NONE | TEST_SCAN_WIFI | TEST_STATION_MAC | TEST_STATION_CONNECT_DISCONNECT | TEST_SOFTAP_MAC | TEST_SOFTAP_START_STOP | TEST_STATION_SOFTAP_MODE | TEST_POWER_SAVE) +STRESS_TEST=(TEST_MODE_NONE | TEST_SCAN_WIFI | TEST_STATION_MAC | TEST_STATION_CONNECT_DISCONNECT | TEST_SOFTAP_MAC | TEST_SOFTAP_START_STOP | TEST_STATION_SOFTAP_MODE | TEST_POWER_SAVE | TEST_WIFI_TX_POWER) #***** Please Read ***** #* Before use stress.py : User must enter user configuration parameter in "test_config.py" file * @@ -130,3 +131,14 @@ test_set_wifi_power_save_mode() test_get_wifi_power_save_mode() + +# maximum transmitting power + +# set and get maximum transmitting power +if (STRESS_TEST & TEST_WIFI_TX_POWER): + for i in (range(STRESS_TEST_COUNT)): + print("*************** "+str(i)+" ****************") + test_wifi_set_max_tx_power() + + test_wifi_get_curr_tx_power() + diff --git a/host/linux/host_control/python_support/test.py b/host/linux/host_control/python_support/test.py index 41ebcf5be1..8e4a7bbfc2 100644 --- a/host/linux/host_control/python_support/test.py +++ b/host/linux/host_control/python_support/test.py @@ -56,3 +56,7 @@ test_set_wifi_power_save_mode() test_get_wifi_power_save_mode() + +# maximum transmitting power +test_wifi_set_max_tx_power() +test_wifi_get_curr_tx_power() diff --git a/host/linux/host_control/python_support/test_api.py b/host/linux/host_control/python_support/test_api.py index 1bff1f0592..989de3d76a 100644 --- a/host/linux/host_control/python_support/test_api.py +++ b/host/linux/host_control/python_support/test_api.py @@ -109,7 +109,7 @@ def test_softap_mode_set_mac_addr_of_esp(): def test_station_mode_connect(): ret = wifi_set_ap_config(STATION_MODE_SSID, STATION_MODE_PWD, STATION_MODE_BSSID,\ STATION_MODE_IS_WPA3_SUPPORTED, STATION_MODE_LISTEN_INTERVAL) - if (ret != failure): + if (ret == success): print("Connected to "+STATION_MODE_SSID) else: print("Failed to connect to AP") @@ -313,3 +313,20 @@ def test_get_wifi_power_save_mode(): print("Failed to set power save mode") return +def test_wifi_set_max_tx_power(): + ret = wifi_set_max_tx_power(INPUT_WIFI_TX_POWER) + if (ret == out_of_range_str): + print("Out of range") + elif (ret == success): + print("Success in setting WiFi maximum TX power") + else: + print("Failure in setting WiFi maximum TX power") + return + +def test_wifi_get_curr_tx_power(): + power = wifi_get_curr_tx_power() + if (power != failure): + print("Maximum TX power is "+str(power)) + else: + print("Failure in getting WiFi current TX power") + return diff --git a/host/linux/host_control/python_support/test_config.py b/host/linux/host_control/python_support/test_config.py index 401dd7e89e..c624511abf 100644 --- a/host/linux/host_control/python_support/test_config.py +++ b/host/linux/host_control/python_support/test_config.py @@ -39,3 +39,6 @@ SOFTAP_MODE_MAX_ALLOWED_CLIENTS=4 SOFTAP_MODE_SSID_HIDDEN=False SOFTAP_MODE_BANDWIDTH=2 + +# maximum transmitting power +INPUT_WIFI_TX_POWER = 8 From d348b2c75cdf41c426b6631934519e724c2d35b1 Mon Sep 17 00:00:00 2001 From: "ajita.chavan" Date: Wed, 24 Nov 2021 10:42:32 +0530 Subject: [PATCH 34/40] Minor documentation changes Signed-off-by: ajita.chavan --- README.md | 4 ++-- docs/Linux_based_host/Linux_based_readme.md | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 1858db62e7..b8b07697bc 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ Below table explains which feature is supported on which transport interface for | ESP32-S2 | SPI | Yes | Yes | NA | | ESP32-S2 | UART | No | No | NA | | ESP32-C3 | SDIO | NA | NA | NA | -| ESP32-C3 | SPI | Yes | Yes | No | +| ESP32-C3 | SPI | Yes | Yes | Yes | | ESP32-C3 | UART | No | No | No | ##### 1.5.2 MCU Host @@ -92,7 +92,7 @@ Below table explains which feature is supported on which transport interface for | ESP32-S2 | SPI | Yes | Yes | NA | | ESP32-S2 | UART | No | No | NA | | ESP32-C3 | SDIO | NA | NA | NA | -| ESP32-C3 | SPI | Yes | Yes | No | +| ESP32-C3 | SPI | Yes | Yes | Yes\* | | ESP32-C3 | UART | No | No | No | \* BT/BLE over SPI diff --git a/docs/Linux_based_host/Linux_based_readme.md b/docs/Linux_based_host/Linux_based_readme.md index 59544bff9a..1d9cad25e5 100644 --- a/docs/Linux_based_host/Linux_based_readme.md +++ b/docs/Linux_based_host/Linux_based_readme.md @@ -114,7 +114,6 @@ Prepare connections based on interface requirements and setup host as below. ```sh $ sudo hciattach -s 921600 /dev/serial0 any 921600 flow ``` - ``` #### 1.3.1 ESP Firmware Setup * Flash pre-built binaries as below. From 47a682acb941c59e96fa30fa18502f1fa3a9873e Mon Sep 17 00:00:00 2001 From: "ajita.chavan" Date: Tue, 30 Nov 2021 13:51:32 +0530 Subject: [PATCH 35/40] hci over UART for C3 Signed-off-by: ajita.chavan --- README.md | 27 +- docs/Linux_based_host/Linux_based_readme.md | 1 + docs/Linux_based_host/UART_setup.md | 76 ++++-- .../Linux_based_host/rpi_esp32_uart_setup.jpg | Bin 0 -> 107621 bytes .../rpi_esp32c3_uart_setup.jpg | Bin 0 -> 81024 bytes .../network_adapter/main/Kconfig.projbuild | 8 + .../network_adapter/main/slave_bt.c | 233 ++++++++++++++++++ .../network_adapter/main/slave_bt.h | 11 +- 8 files changed, 328 insertions(+), 28 deletions(-) create mode 100644 docs/Linux_based_host/rpi_esp32_uart_setup.jpg create mode 100644 docs/Linux_based_host/rpi_esp32c3_uart_setup.jpg diff --git a/README.md b/README.md index b8b07697bc..b7c7063ac2 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,8 @@ ESP-Hosted solution provides following WLAN and BT/BLE features to host: - WLAN Station - WLAN Soft AP - BT/BLE Features: - - v4.2 BR/EDR and BLE + - ESP32 supports v4.2 BR/EDR and BLE + - ESP32-C3 supports v5.0 BLE ### 1.2 Supported ESP boards @@ -68,17 +69,17 @@ ESP-Hosted uses SDIO or SPI bus for interfacing ESP boards and host platform. No ##### 1.5.1 Linux Host Below table explains which feature is supported on which transport interface for Linux based host. -| ESP device | Transport Interface | WLAN support | Virtual serial interface | BT/BLE support | -|:---------:|:-------:|:---------:|:--------:|:--------:| -| ESP32 | SDIO | Yes | Yes | Yes | -| ESP32 | SPI | Yes | Yes | Yes | -| ESP32 | UART | No | No | Yes | -| ESP32-S2 | SDIO | NA | NA | NA | -| ESP32-S2 | SPI | Yes | Yes | NA | -| ESP32-S2 | UART | No | No | NA | -| ESP32-C3 | SDIO | NA | NA | NA | -| ESP32-C3 | SPI | Yes | Yes | Yes | -| ESP32-C3 | UART | No | No | No | +| ESP device | Transport Interface | WLAN support | Virtual serial interface | BT/BLE support | BLE 5.0 support | +|:---------:|:-------:|:---------:|:--------:|:--------:|:--------:| +| ESP32 | SDIO | Yes | Yes | Yes | NA | +| ESP32 | SPI | Yes | Yes | Yes | NA | +| ESP32 | UART | No | No | Yes | NA | +| ESP32-S2 | SDIO | NA | NA | NA | NA | +| ESP32-S2 | SPI | Yes | Yes | NA | NA | +| ESP32-S2 | UART | No | No | NA | NA | +| ESP32-C3 | SDIO | NA | NA | NA | NA | +| ESP32-C3 | SPI | Yes | Yes | Yes | Yes | +| ESP32-C3 | UART | No | No | Yes | Yes | ##### 1.5.2 MCU Host Below table explains which feature is supported on which transport interface for MCU based host. @@ -93,7 +94,7 @@ Below table explains which feature is supported on which transport interface for | ESP32-S2 | UART | No | No | NA | | ESP32-C3 | SDIO | NA | NA | NA | | ESP32-C3 | SPI | Yes | Yes | Yes\* | -| ESP32-C3 | UART | No | No | No | +| ESP32-C3 | UART | No | No | Yes\* | \* BT/BLE over SPI > BT/BLE support over SPI is not readily available. In order to implement it, one needs to: diff --git a/docs/Linux_based_host/Linux_based_readme.md b/docs/Linux_based_host/Linux_based_readme.md index 1d9cad25e5..18e0369186 100644 --- a/docs/Linux_based_host/Linux_based_readme.md +++ b/docs/Linux_based_host/Linux_based_readme.md @@ -207,6 +207,7 @@ ESP-IDF release version to be used for ESP peripherals are | ESP32 | release v4.0 | | ESP32-S2 | release v4.2 | | ESP32-C3 | release v4.3 | +| ESP32-C3 (HCI over UART)| release v4.4 (beta)| Clone appropriate ESP-IDF version as per your ESP peripheral. The control path between Linux host and ESP peripheral is based on `protobuf`. For that, corresponding stack layer, `protocomm` from ESP-IDF is used. Run following command in ESP-IDF directory to make `protocomm_priv.h` available for control path. ``` diff --git a/docs/Linux_based_host/UART_setup.md b/docs/Linux_based_host/UART_setup.md index 67fd2e1521..e904a0d94b 100644 --- a/docs/Linux_based_host/UART_setup.md +++ b/docs/Linux_based_host/UART_setup.md @@ -1,10 +1,13 @@ # Bluetooth/BLE connectivity Setup over UART -This section is only applicable to ESP32 boards. ESP32-S2 does not support Bluetooth/BLE. +This section is only applicable to ESP32 and ESP32C3 boards. ESP32-S2 does not support Bluetooth/BLE. ## 1. Setup ### 1.1 Hardware Setup In this setup, ESP32 board provides Bluetooth/BLE capabilities to host over UART interface. Please connect ESP peripheral to Raspberry-Pi with jumper cables as mentioned below. It may be good to use small length cables to ensure signal integrity. Power ESP32 and Raspberry Pi separately with a power supply that provide sufficient power. ESP32 can be powered through PC using micro-USB cable. +Raspberry-Pi pinout can be found [here!](https://pinout.xyz/pinout/uart) + +#### 1.1.1 ESP32 setup | Raspberry-Pi Pin Function | Raspberry-Pi Pin | ESP32 Pin | ESP32 Pin Function | |:-------:|:--------:|:---------:|:--------:| | RX | 10 | IO5 | TX | @@ -13,7 +16,22 @@ In this setup, ESP32 board provides Bluetooth/BLE capabilities to host over UART | RTS | 11 | IO23 | CTS | | Ground | 39 | GND | Ground | -Raspberry-Pi pinout can be found [here!](https://pinout.xyz/pinout/uart) +Setup image is here. + +![alt text](rpi_esp32_uart_setup.jpg "setup of Raspberry-Pi as host and ESP32 as slave with UART transport") + +#### 1.1.2 ESP32-C3 setup +| Raspberry-Pi Pin Function | Raspberry-Pi Pin | ESP32C3 Pin | ESP32C3 Pin Function | +|:-------:|:--------:|:---------:|:--------:| +| RX | 10 | IO5 | TX | +| TX | 8 | IO18 | RX | +| CTS | 36 | IO19 | RTS | +| RTS | 11 | IO8 | CTS | +| Ground | 39 | GND | Ground | + +Setup image is here. + +![alt text](rpi_esp32c3_uart_setup.jpg "setup of Raspberry-Pi as host and ESP32C3 as slave with UART transport") ### 1.2 Raspberry-Pi Software Setup By default, the UART pins on Raspberry-Pi are in disabled state. In order to enable UART and setup it for bluetooth connection, follow below steps. @@ -55,12 +73,15 @@ One can load pre-built release binaries on ESP peripheral or compile those from #### 2.2.1 Load Pre-built Release Binaries * Download pre-built firmware binaries from [releases](https://github.com/espressif/esp-hosted/releases) -* Please note that this binary is made for UART baudrate of 921600. * Linux users can run below command to flash these binaries. Edit with ESP peripheral's serial port. + +##### ESP32 +* Please note that this binary is made for UART baudrate of 921600. ```sh -$ python esptool.py --chip esp32 --port --baud 960000 --before default_reset \ +$ python esptool.py --chip esp32 --port --baud --before default_reset \ --after hard_reset write_flash -z --flash_mode dio --flash_freq 40m --flash_size detect \ 0x1000 esp_hosted_bootloader_esp32__uart_v.bin \ +0xd000 esp_hosted_ota_data_initial_esp32__v.bin \ 0x10000 esp_hosted_firmware_esp32__uart_v.bin \ 0x8000 esp_hosted_partition-table_esp32__uart_v.bin @@ -71,6 +92,23 @@ Where, ``` * This command will flash `SDIO+UART` or `SPI+UART` interface binaries on `esp32` chip. +##### ESP32-C3 +* Please note that this binary is made for UART baudrate of 921600. + +```sh +$ python esptool.py --port --baud --before default_reset --after hard_reset \ +--chip esp32c3 write_flash --flash_mode dio --flash_size detect --flash_freq 80m \ +0x0 esp_hosted_bootloader_esp32c3_spi_v.bin \ +0xd000 esp_hosted_ota_data_initial_esp32c3_spi_v.bin \ +0x8000 esp_hosted_partition-table_esp32c3_spi_v.bin \ +0x10000 esp_hosted_firmware_esp32c3_spi_v.bin \ + +Where, + : serial port of ESP peripheral + : 0.1,0.2 etc. Latest from [release page](https://github.com/espressif/esp-hosted/releases) +``` +* This command will flash `SPI+UART` interface binaries on `esp32c3` chip. + * Windows user can use ESP Flash Programming Tool to flash the pre-built binary. #### 2.2.2 Source Compilation @@ -82,25 +120,29 @@ $ cd esp/esp_driver/network_adapter ``` ##### Using cmake -* Set target if the ESP32S2 is being used. Skip if ESP32 is being used. + +* :warning: `Set target if the ESP32C3 is being used. Skip if ESP32 is being used.` ``` -$ idf.py set-target esp32s2 +$ idf.py set-target esp32c3 ``` -* Execute following command to configure the project + +* Execute following command to configure project ```sh $ idf.py menuconfig ``` -* This will open project configuration window. - * Navigate to `Component config -> Bluetooth -> Bluetooth controller -> HCI mode -> UART(H4) -> select` - * Also to set baud rate by navigating to, `Component config -> Bluetooth -> Bluetooth controller -> HCI UART(H4) Options -> UART Baudrate for HCI -> -> Ok` - * exit from menuconfig. +* This will open project configuration window. To select SPI transport interface, navigate to `Example Configuration -> Transport layer -> SPI interface -> select` and exit from menuconfig. + +* Change UART Baudrate for HCI as `Component config -> Example Configuration -> UART Baudrate for HCI`. Default is 921600. + +* For ESP32C3, select chip revision in addition. Navigate to `Component config → ESP32C3-Specific → Minimum Supported ESP32-C3 Revision` and select chip version of ESP32C3. + * Use below command to compile and flash the project. Replace with ESP peripheral's serial port. ```sh $ idf.py -p build flash ``` ##### Using make -:warning: *make* build system is only supported till ESP32. Please refer cmake section above for ESP32-S2. +:warning: *make* build system is only supported till ESP32. Please refer cmake section above for ESP32-C3. * Execute following command to configure the project ```sh $ make menuconfig @@ -117,8 +159,14 @@ $ make flash ## 3. Post Setup * After setting up host and loading ESP firmware, execute below command to create `hci0` interface ```sh - $ sudo hciattach -s /dev/serial0 any flow + $ sudo hciattach -s /dev/serial0 any flow ``` * should match UART baud rate of ESP peripheral + +### For ESP32 * Check `CONFIG_BT_HCI_UART_BAUDRATE` parameter in *esp/esp_driver/network_adapter/sdkconfig* -* Alternatively baud rate could be located in menuconfig at, `Component config -> Bluetooth -> Bluetooth controller -> HCI UART(H4) Options -> UART Baudrate for HCI` +* Alternatively baud rate could be located in menuconfig at, * Alternatively baud rate could be located in menuconfig at, `Component config -> Bluetooth -> Bluetooth controller -> HCI UART(H4) Options -> UART Baudrate for HCI` + +### For ESP32-C3 +* Check `CONFIG_EXAMPLE_ESP32C3_HCI_UART_BAUDRATE` parameter in *esp/esp_driver/network_adapter/sdkconfig* +* Alternatively baud rate could be located in menuconfig at, `Component config -> Example Configuration -> UART Baudrate for HCI` \ No newline at end of file diff --git a/docs/Linux_based_host/rpi_esp32_uart_setup.jpg b/docs/Linux_based_host/rpi_esp32_uart_setup.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3b80fc95d402428dade911aae48c902fd32a4396 GIT binary patch literal 107621 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𝔉|I~L^f0gC_gaWay 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 literal 0 HcmV?d00001 diff --git a/docs/Linux_based_host/rpi_esp32c3_uart_setup.jpg b/docs/Linux_based_host/rpi_esp32c3_uart_setup.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f8e48e07d12109e55854509ff859af07619309dc GIT binary patch literal 81024 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

XygcYTbII#~J499)%ci&3hmZ%a=2E=#daE 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 +#include "driver/periph_ctrl.h" +#include "driver/gpio.h" +#include "driver/uart.h" +#include "soc/lldesc.h" #include "esp_bt.h" +#include "esp_log.h" #include "slave_bt.h" +#ifdef CONFIG_IDF_TARGET_ESP32C3 +#include "esp_private/gdma.h" +#include "hal/uhci_ll.h" +#endif + static const char BT_TAG[] = "ESP_BT"; extern QueueHandle_t to_host_queue[MAX_PRIORITY_QUEUES]; @@ -100,6 +111,147 @@ void process_hci_rx_pkt(uint8_t *payload, uint16_t payload_len) { #elif BLUETOOTH_UART /* ***** UART specific part ***** */ +#ifdef CONFIG_IDF_TARGET_ESP32C3 +// Operation functions for HCI UART Transport Layer +static bool hci_uart_tl_init(void); +static void hci_uart_tl_deinit(void); +static void hci_uart_tl_recv_async(uint8_t *buf, uint32_t size, esp_bt_hci_tl_callback_t callback, void *arg); +static void hci_uart_tl_send_async(uint8_t *buf, uint32_t size, esp_bt_hci_tl_callback_t callback, void *arg); +static void hci_uart_tl_flow_on(void); +static bool hci_uart_tl_flow_off(void); +static void hci_uart_tl_finish_transfers(void); + +struct uart_txrxchannel { + esp_bt_hci_tl_callback_t callback; + void *arg; + lldesc_t link; +}; + +struct uart_env_tag { + struct uart_txrxchannel tx; + struct uart_txrxchannel rx; +}; + +struct uart_env_tag uart_env; + +static volatile uhci_dev_t *s_uhci_hw = &UHCI0; +static gdma_channel_handle_t s_rx_channel; +static gdma_channel_handle_t s_tx_channel; + +static esp_bt_hci_tl_t s_hci_uart_tl_funcs = { + ._magic = ESP_BT_HCI_TL_MAGIC_VALUE, + ._version = ESP_BT_HCI_TL_VERSION, + ._reserved = 0, + ._open = (void *)hci_uart_tl_init, + ._close = (void *)hci_uart_tl_deinit, + ._finish_transfers = (void *)hci_uart_tl_finish_transfers, + ._recv = (void *)hci_uart_tl_recv_async, + ._send = (void *)hci_uart_tl_send_async, + ._flow_on = (void *)hci_uart_tl_flow_on, + ._flow_off = (void *)hci_uart_tl_flow_off, +}; + +static bool hci_uart_tl_init(void) +{ + return true; +} + +static void hci_uart_tl_deinit(void) +{ +} + +static IRAM_ATTR void hci_uart_tl_recv_async(uint8_t *buf, uint32_t size, + esp_bt_hci_tl_callback_t callback, void *arg) +{ + assert(buf != NULL); + assert(size != 0); + assert(callback != NULL); + uart_env.rx.callback = callback; + uart_env.rx.arg = arg; + + memset(&uart_env.rx.link, 0, sizeof(lldesc_t)); + uart_env.rx.link.buf = buf; + uart_env.rx.link.size = size; + + s_uhci_hw->pkt_thres.thrs = size; + + gdma_start(s_rx_channel, (intptr_t)(&uart_env.rx.link)); +} + +static IRAM_ATTR void hci_uart_tl_send_async(uint8_t *buf, uint32_t size, + esp_bt_hci_tl_callback_t callback, void *arg) +{ + assert(buf != NULL); + assert(size != 0); + assert(callback != NULL); + + uart_env.tx.callback = callback; + uart_env.tx.arg = arg; + + memset(&uart_env.tx.link, 0, sizeof(lldesc_t)); + uart_env.tx.link.length = size; + uart_env.tx.link.buf = buf; + uart_env.tx.link.eof = 1; + + gdma_start(s_tx_channel, (intptr_t)(&uart_env.tx.link)); +} + +static void hci_uart_tl_flow_on(void) +{ +} + +static bool hci_uart_tl_flow_off(void) +{ + return true; +} + +static void hci_uart_tl_finish_transfers(void) +{ +} + +static IRAM_ATTR bool hci_uart_tl_rx_eof_callback(gdma_channel_handle_t dma_chan, + gdma_event_data_t *event_data, void *user_data) +{ + assert(dma_chan == s_rx_channel); + assert(uart_env.rx.callback != NULL); + esp_bt_hci_tl_callback_t callback = uart_env.rx.callback; + void *arg = uart_env.rx.arg; + + // clear callback pointer + uart_env.rx.callback = NULL; + uart_env.rx.arg = NULL; + + // call handler + callback(arg, ESP_BT_HCI_TL_STATUS_OK); + + // send notification to Bluetooth Controller task + esp_bt_h4tl_eif_io_event_notify(1); + + return true; +} + +static IRAM_ATTR bool hci_uart_tl_tx_eof_callback(gdma_channel_handle_t dma_chan, + gdma_event_data_t *event_data, void *user_data) +{ + assert(dma_chan == s_tx_channel); + assert(uart_env.tx.callback != NULL); + esp_bt_hci_tl_callback_t callback = uart_env.tx.callback; + void *arg = uart_env.tx.arg; + + // clear callback pointer + uart_env.tx.callback = NULL; + uart_env.tx.arg = NULL; + + // call handler + callback(arg, ESP_BT_HCI_TL_STATUS_OK); + + // send notification to Bluetooth Controller task + esp_bt_h4tl_eif_io_event_notify(1); + + return true; +} +#endif + static void init_uart(void) { #if BLUETOOTH_UART == 1 @@ -113,9 +265,85 @@ static void init_uart(void) periph_module_enable(PERIPH_UHCI0_MODULE); periph_module_reset(PERIPH_UHCI0_MODULE); +#ifdef CONFIG_IDF_TARGET_ESP32C3 + gpio_config_t io_output_conf = { + .intr_type = GPIO_PIN_INTR_DISABLE, //disable interrupt + .mode = GPIO_MODE_OUTPUT, // output mode + .pin_bit_mask = GPIO_OUTPUT_PIN_SEL, // bit mask of the output pins + .pull_down_en = 0, // disable pull-down mode + .pull_up_en = 0, // disable pull-up mode + }; + gpio_config(&io_output_conf); + + gpio_config_t io_input_conf = { + .intr_type = GPIO_PIN_INTR_DISABLE, //disable interrupt + .mode = GPIO_MODE_INPUT, // input mode + .pin_bit_mask = GPIO_INPUT_PIN_SEL, // bit mask of the input pins + .pull_down_en = 0, // disable pull-down mode + .pull_up_en = 0, // disable pull-down mode + }; + gpio_config(&io_input_conf); +#endif + ESP_ERROR_CHECK( uart_set_pin(BLUETOOTH_UART, BT_TX_PIN, BT_RX_PIN, BT_RTS_PIN, BT_CTS_PIN) ); +#ifdef CONFIG_IDF_TARGET_ESP32C3 + // configure UART1 + ESP_LOGI(BT_TAG, "baud rate for HCI uart :: %d \n", + CONFIG_EXAMPLE_ESP32C3_HCI_UART_BAUDRATE); + + uart_config_t uart_config = { + .baud_rate = CONFIG_EXAMPLE_ESP32C3_HCI_UART_BAUDRATE, + + .data_bits = UART_DATA_8_BITS, + .parity = UART_PARITY_DISABLE, + .stop_bits = UART_STOP_BITS_1, + .flow_ctrl = UART_HW_FLOWCTRL_CTS_RTS, + .rx_flow_ctrl_thresh = UART_RX_THRS, + .source_clk = UART_SCLK_APB, + }; + ESP_ERROR_CHECK(uart_param_config(BLUETOOTH_UART, &uart_config)); + + // install DMA driver + gdma_channel_alloc_config_t tx_channel_config = { + .flags.reserve_sibling = 1, + .direction = GDMA_CHANNEL_DIRECTION_TX, + }; + ESP_ERROR_CHECK(gdma_new_channel(&tx_channel_config, &s_tx_channel)); + gdma_channel_alloc_config_t rx_channel_config = { + .direction = GDMA_CHANNEL_DIRECTION_RX, + .sibling_chan = s_tx_channel, + }; + ESP_ERROR_CHECK(gdma_new_channel(&rx_channel_config, &s_rx_channel)); + + gdma_connect(s_tx_channel, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_UART, 0)); + gdma_connect(s_rx_channel, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_UART, 0)); + + gdma_strategy_config_t strategy_config = { + .auto_update_desc = false, + .owner_check = false + }; + gdma_apply_strategy(s_tx_channel, &strategy_config); + gdma_apply_strategy(s_rx_channel, &strategy_config); + + gdma_rx_event_callbacks_t rx_cbs = { + .on_recv_eof = hci_uart_tl_rx_eof_callback + }; + gdma_register_rx_event_callbacks(s_rx_channel, &rx_cbs, NULL); + + gdma_tx_event_callbacks_t tx_cbs = { + .on_trans_eof = hci_uart_tl_tx_eof_callback + }; + gdma_register_tx_event_callbacks(s_tx_channel, &tx_cbs, NULL); + + // configure UHCI + uhci_ll_init(s_uhci_hw); + uhci_ll_set_eof_mode(s_uhci_hw, UHCI_RX_LEN_EOF); + // disable software flow control + s_uhci_hw->escape_conf.val = 0; + uhci_ll_attach_uart_port(s_uhci_hw, 1); +#endif } #endif @@ -124,7 +352,12 @@ esp_err_t initialise_bluetooth(void) { esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); + #ifdef BLUETOOTH_UART + #ifdef CONFIG_IDF_TARGET_ESP32C3 + bt_cfg.hci_tl_funcs = &s_hci_uart_tl_funcs; + #endif + init_uart(); #endif ESP_ERROR_CHECK( esp_bt_controller_init(&bt_cfg) ); diff --git a/esp/esp_driver/network_adapter/main/slave_bt.h b/esp/esp_driver/network_adapter/main/slave_bt.h index b10802f783..c3b5d3b41d 100644 --- a/esp/esp_driver/network_adapter/main/slave_bt.h +++ b/esp/esp_driver/network_adapter/main/slave_bt.h @@ -29,6 +29,8 @@ #if defined(CONFIG_BT_CTRL_HCI_MODE_VHCI) #define BLUETOOTH_HCI 4 + #elif CONFIG_BT_CTRL_HCI_MODE_UART_H4 + #define BLUETOOTH_UART 1 #endif #else @@ -53,7 +55,14 @@ #define BT_TX_PIN 5 #define BT_RX_PIN 18 #define BT_RTS_PIN 19 - #define BT_CTS_PIN 23 + #ifdef CONFIG_IDF_TARGET_ESP32C3 + #define BT_CTS_PIN 8 + #define GPIO_OUTPUT_PIN_SEL ((1ULL< Date: Wed, 15 Dec 2021 15:41:11 +0530 Subject: [PATCH 36/40] Compilation check added for esp32c3 HCI over UART Signed-off-by: ajita.chavan --- esp/esp_driver/network_adapter/main/slave_bt.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/esp/esp_driver/network_adapter/main/slave_bt.c b/esp/esp_driver/network_adapter/main/slave_bt.c index 9bf4f11143..e1a48f649b 100644 --- a/esp/esp_driver/network_adapter/main/slave_bt.c +++ b/esp/esp_driver/network_adapter/main/slave_bt.c @@ -25,8 +25,10 @@ #ifdef CONFIG_IDF_TARGET_ESP32C3 #include "esp_private/gdma.h" +#if BLUETOOTH_UART #include "hal/uhci_ll.h" #endif +#endif static const char BT_TAG[] = "ESP_BT"; extern QueueHandle_t to_host_queue[MAX_PRIORITY_QUEUES]; From 6a3d6f30487cc7a671fef0d11cf29265210f75ab Mon Sep 17 00:00:00 2001 From: Yogesh Mantri Date: Fri, 17 Dec 2021 18:23:31 +0530 Subject: [PATCH 37/40] Correction in serial debug log --- esp/esp_driver/network_adapter/main/app_main.c | 7 ++++--- host/linux/host_driver/esp32/esp_serial.c | 2 ++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/esp/esp_driver/network_adapter/main/app_main.c b/esp/esp_driver/network_adapter/main/app_main.c index b00d13e971..106fa85fbd 100644 --- a/esp/esp_driver/network_adapter/main/app_main.c +++ b/esp/esp_driver/network_adapter/main/app_main.c @@ -348,6 +348,10 @@ void process_serial_rx_pkt(uint8_t *buf) payload = buf + le16toh(header->offset); rem_buff_size = sizeof(r.data) - r.len; +#if CONFIG_ESP_SERIAL_DEBUG + ESP_LOG_BUFFER_HEXDUMP(TAG_RX_S, payload, payload_len, ESP_LOG_INFO); +#endif + while (r.valid) { ESP_LOGI(TAG,"curr seq: %u header seq: %u\n", @@ -401,9 +405,6 @@ void process_rx_pkt(interface_buffer_handle_t **buf_handle_p) esp_wifi_internal_tx(ESP_IF_WIFI_AP, payload, payload_len); } else if (buf_handle->if_type == ESP_SERIAL_IF) { process_serial_rx_pkt(buf_handle->payload); -#if CONFIG_ESP_SERIAL_DEBUG - ESP_LOG_BUFFER_HEXDUMP(TAG_RX_S, r.data, r.len, ESP_LOG_INFO); -#endif } #if defined(CONFIG_BT_ENABLED) && BLUETOOTH_HCI else if (buf_handle->if_type == ESP_HCI_IF) { diff --git a/host/linux/host_driver/esp32/esp_serial.c b/host/linux/host_driver/esp32/esp_serial.c index e8ace8a982..b088a1306a 100644 --- a/host/linux/host_driver/esp32/esp_serial.c +++ b/host/linux/host_driver/esp32/esp_serial.c @@ -123,6 +123,8 @@ static int esp_serial_write(struct file *file, const char __user *user_buffer, s } hdr->checksum = cpu_to_le16(compute_checksum(tx_skb->data, (frag_len + sizeof(struct esp_payload_header)))); + /* print_hex_dump(KERN_INFO, "esp_serial_tx: ", DUMP_PREFIX_ADDRESS, 16, 1, pos, frag_len, 1 ); */ + ret = esp_send_packet(dev->priv, tx_skb); if (ret) { printk (KERN_ERR "%s: Failed to transmit data, error %d\n", __func__, ret); From a38a9e2afbe975d68280d8d052076e20d7d5176d Mon Sep 17 00:00:00 2001 From: "ajita.chavan" Date: Wed, 15 Dec 2021 17:30:55 +0530 Subject: [PATCH 38/40] Minor documentation changes in README.md Signed-off-by: ajita.chavan --- README.md | 36 ++++++++++++++++++++---------------- common/proto/README.md | 17 +++++++++-------- 2 files changed, 29 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index b7c7063ac2..36662b1445 100644 --- a/README.md +++ b/README.md @@ -69,32 +69,36 @@ ESP-Hosted uses SDIO or SPI bus for interfacing ESP boards and host platform. No ##### 1.5.1 Linux Host Below table explains which feature is supported on which transport interface for Linux based host. -| ESP device | Transport Interface | WLAN support | Virtual serial interface | BT/BLE support | BLE 5.0 support | -|:---------:|:-------:|:---------:|:--------:|:--------:|:--------:| -| ESP32 | SDIO | Yes | Yes | Yes | NA | -| ESP32 | SPI | Yes | Yes | Yes | NA | -| ESP32 | UART | No | No | Yes | NA | -| ESP32-S2 | SDIO | NA | NA | NA | NA | -| ESP32-S2 | SPI | Yes | Yes | NA | NA | -| ESP32-S2 | UART | No | No | NA | NA | -| ESP32-C3 | SDIO | NA | NA | NA | NA | -| ESP32-C3 | SPI | Yes | Yes | Yes | Yes | -| ESP32-C3 | UART | No | No | Yes | Yes | +| ESP device | Transport Interface | WLAN support | Virtual serial interface | Bluetooth support | +|:---------:|:-------:|:---------:|:--------:|:--------:| +| ESP32 | SDIO | Yes | Yes | BT/BLE 4.2 | +| ESP32 | SPI | Yes | Yes | BT/BLE 4.2 | +| ESP32 | UART | No | No | BT/BLE 4.2 | +| ESP32-S2 | SDIO | NA | NA | NA | +| ESP32-S2 | SPI | Yes | Yes | NA | +| ESP32-S2 | UART | No | No | NA | +| ESP32-C3 | SDIO | NA | NA | NA | +| ESP32-C3 | SPI | Yes | Yes | BLE 5.0 | +| ESP32-C3 | UART | No | No | BLE 5.0 | + +Note: BT stands for Bluetooth BR/EDR and BLE stands for Bluetooth Low Energy specifications. ##### 1.5.2 MCU Host Below table explains which feature is supported on which transport interface for MCU based host. -| ESP device | Transport Interface | WLAN support | Virtual serial interface | BT/BLE support | +| ESP device | Transport Interface | WLAN support | Virtual serial interface | Bluetooth support | |:------------:|:-------:|:---------:|:--------:|:--------:| | ESP32 | SDIO | No | No | No | -| ESP32 | SPI | Yes | Yes | Yes\* | -| ESP32 | UART | No | No | Yes\*\* | +| ESP32 | SPI | Yes | Yes | BT/BLE 4.2\* | +| ESP32 | UART | No | No | BT/BLE 4.2\*\* | | ESP32-S2 | SDIO | NA | NA | NA | | ESP32-S2 | SPI | Yes | Yes | NA | | ESP32-S2 | UART | No | No | NA | | ESP32-C3 | SDIO | NA | NA | NA | -| ESP32-C3 | SPI | Yes | Yes | Yes\* | -| ESP32-C3 | UART | No | No | Yes\* | +| ESP32-C3 | SPI | Yes | Yes | BLE 5.0\* | +| ESP32-C3 | UART | No | No | BLE 5.0\*\* | + +Note: BT stands for Bluetooth BR/EDR and BLE stands for Bluetooth Low Energy specifications. \* BT/BLE over SPI > BT/BLE support over SPI is not readily available. In order to implement it, one needs to: diff --git a/common/proto/README.md b/common/proto/README.md index 6acabdf859..24d35146e4 100644 --- a/common/proto/README.md +++ b/common/proto/README.md @@ -1,17 +1,18 @@ # About Proto Files -The `esp_hosted_config.proto` file is protobuf file which has messages for command and response to communicate between Host and ESP32. Using `esp_hosted_config.proto` file, protobuf generated C and python files are present in `esp/esp_driver/network_adapter/main` and `host/linux/host_control/python_support`. +The `esp_hosted_config.proto` file is protobuf file which has messages for command and response to communicate between Host and ESP32. Using `esp_hosted_config.proto` file, protobuf generated C files are [esp_hosted_config.pb-c.c](../esp_hosted_config.pb-c.c) and (esp_hosted_config.pb-c.h)[../include/esp_hosted_config.pb-c.h]. -User can add his own message field in `.proto` file and generate respective C and python files. +User can add his own message field in `.proto` file and generate respective C files. To generate C based protobuf files, run ``` -protoc-c esp_hosted_config.proto --c_out=../../esp/esp_driver/network_adapter/main/ -``` +cd /common/proto -To generate Python based protobuf files, run -``` -protoc esp_hosted_config.proto --python_out=../../host/linux/host_control/python_support/ +protoc-c esp_hosted_config.proto --c_out=. + +mv esp_hosted_config.pb-c.c ../ + +mv esp_hosted_config.pb-c.h ../include/ ``` -Existing control commands available for use. To send an new command, add python function in `host/linux/host_control/python_support/commands.py`. Similarly add respective C function in `esp/esp_driver/network_adapter/main/slave_commands.c` to handle added message field. User can test added functionality using `host/linux/host_control/python_support/test.py`. +Existing control commands available for use. To send an new command, add C function in `host/host_common/commands.c`, create python binding in `host/linux/host_control/python_support/commands_map_py_to_c.py` and its python function in `host/linux/host_control/python_support/commands_lib.py`. Similarly add respective ESP side C function in `esp/esp_driver/network_adapter/main/slave_commands.c` to handle added message field. User can test added functionality using `host/linux/host_control/python_support/test.py`. From 401809d7aaea4e8a6deee084ea8efb4e4bc66315 Mon Sep 17 00:00:00 2001 From: Yogesh Mantri Date: Wed, 5 Jan 2022 11:33:31 +0530 Subject: [PATCH 39/40] Print ESP side Bluetooth MAC address --- esp/esp_driver/network_adapter/main/app_main.c | 9 +++++++++ esp/esp_driver/network_adapter/main/slave_bt.h | 2 ++ 2 files changed, 11 insertions(+) diff --git a/esp/esp_driver/network_adapter/main/app_main.c b/esp/esp_driver/network_adapter/main/app_main.c index 106fa85fbd..0361b6af8a 100644 --- a/esp/esp_driver/network_adapter/main/app_main.c +++ b/esp/esp_driver/network_adapter/main/app_main.c @@ -682,6 +682,7 @@ void app_main() esp_err_t ret; uint8_t capa = 0; uint8_t prio_q_idx = 0; + uint8_t mac[MAC_LEN] = {0}; print_firmware_version(); capa = get_capabilities(); @@ -697,6 +698,14 @@ void app_main() #ifdef CONFIG_BT_ENABLED initialise_bluetooth(); + + ret = esp_read_mac(mac, ESP_MAC_BT); + if (ret) { + ESP_LOGE(TAG,"Failed to read BT Mac addr\n"); + } else { + ESP_LOGI(TAG, "ESP Bluetooth MAC addr: %2x-%2x-%2x-%2x-%2x-%2x\n", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + } #endif pc_pserial = protocomm_new(); diff --git a/esp/esp_driver/network_adapter/main/slave_bt.h b/esp/esp_driver/network_adapter/main/slave_bt.h index c3b5d3b41d..41a5996558 100644 --- a/esp/esp_driver/network_adapter/main/slave_bt.h +++ b/esp/esp_driver/network_adapter/main/slave_bt.h @@ -46,6 +46,8 @@ #define BLUETOOTH_HCI 4 #elif CONFIG_BT_HCI_UART_NO #define BLUETOOTH_UART CONFIG_BT_HCI_UART_NO + #elif CONFIG_BTDM_CTRL_HCI_UART_NO + #define BLUETOOTH_UART CONFIG_BTDM_CTRL_HCI_UART_NO #endif #endif From 26c9630eca2c97a0b139b36f9d88e6164d3b88d7 Mon Sep 17 00:00:00 2001 From: "ajita.chavan" Date: Fri, 22 Oct 2021 20:19:10 +0530 Subject: [PATCH 40/40] OTA documentation changes, documentation changes for BLE Signed-off-by: ajita.chavan --- README.md | 9 ++- docs/Linux_based_host/Getting_started.md | 41 ++++++++--- .../Linux_based_architecture.md | 9 +++ docs/Linux_based_host/Linux_based_readme.md | 3 +- docs/Linux_based_host/ota_update.md | 38 ++++++++++ docs/c_api.md | 39 +++++++++- docs/c_demo.md | 10 +++ docs/python_api.md | 67 ++++++++++++----- docs/python_demo.md | 46 +++++++++++- .../python_support/commands_lib.py | 4 +- .../python_support/hosted_config.py | 9 +-- ota_feature.md | 73 ------------------- 12 files changed, 233 insertions(+), 115 deletions(-) create mode 100644 docs/Linux_based_host/ota_update.md delete mode 100644 ota_feature.md diff --git a/README.md b/README.md index 36662b1445..525e732834 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,12 @@ Below table explains which feature is supported on which transport interface for | ESP32-C3 | SPI | Yes | Yes | BLE 5.0 | | ESP32-C3 | UART | No | No | BLE 5.0 | -Note: BT stands for Bluetooth BR/EDR and BLE stands for Bluetooth Low Energy specifications. +Note: +* BT stands for Bluetooth BR/EDR and BLE stands for Bluetooth Low Energy specifications. +* ESP-Hosted related BR/EDR 4.2 and BLE 4.2 functionalities are tested with bluez 5.43+. Whereas BLE 5.0 functionalities are tested with bluez 5.45+. +* We suggest latest stable bluez version to be used. Any other bluetooth stack instead of bluez also could be used. +* bluez 5.45 on-wards BLE 5.0 HCI commands are supported. +* BLE 5.0 has backward compability of BLE 4.2. ##### 1.5.2 MCU Host Below table explains which feature is supported on which transport interface for MCU based host. @@ -116,6 +121,8 @@ Note: BT stands for Bluetooth BR/EDR and BLE stands for Bluetooth Low Energy spe > With the help of this UART interface, BT/BLE stack can directly interact with BT controller present on ESP32 bypassing host driver and firmware > ESP Hosted host driver and a firmware plays no role in this communication +* Linux hosts support OTA update (Over The Air ESP32 firmware update) in C and python. MCU hosts can refer the same for their development. For detailed documentation please read +[ota_update.md](docs/Linux_based_host/ota_update.md). --- diff --git a/docs/Linux_based_host/Getting_started.md b/docs/Linux_based_host/Getting_started.md index 9e2494f3f9..42a81a4241 100644 --- a/docs/Linux_based_host/Getting_started.md +++ b/docs/Linux_based_host/Getting_started.md @@ -116,6 +116,12 @@ hci0: Type: Primary Bus: SDIO * This interface supports all standard HCI commands. Use standard hci tools to control and configure this interface. ### 2.1 BT/BLE Test procedure + +* ESP-Hosted related BR/EDR 4.2 and BLE 4.2 functionalities are tested with `bluez` 5.43+. +Whereas BLE 5.0 functionalities are tested with `bluez` 5.45+. +* We suggest latest stable `bluez` version to be used. Any other bluetooth stack instead of `bluez` also could be used. +* To upgrade `bluez` for particular version, follow this [link](https://scribles.net/updating-bluez-on-raspberry-pi-from-5-43-to-5-50/). Replace bluez `older version` to `expected version` while following mentioned link. + #### 2.1.1 GATT server 1. Go to `bluez-5.xx` folder. Run `./test/example-gatt-server`. This will start GATT server on Raspberry-Pi. @@ -152,15 +158,15 @@ Only ESP32C3 HCI controller supports BLE 5.0. Several new features are introduce 5. LE Advertising Extensions 6. LE Channel Selection Algorithm #2 -To test BLE 5.0 on RPi minimum `bluez` version `5.56` and above required. Check current `bluez` version by running following command on RPi: +To test BLE 5.0 on RPi minimum `bluez` version `5.45` and above required. If `bluez` version is less than 5.45 ,then upgrade `bluez` version. + +Check current `bluez` version by running following command on RPi: ``` bluetoothctl -v ``` :warning: `hcitool lescan` is deprecated. Please dont use it. -If `bluez` version is less than 5.56 then follow [this](https://scribles.net/updating-bluez-on-raspberry-pi-from-5-43-to-5-50/) link to update bluez to 5.56. Replace `bluez` version `5.50` to `5.56` while following mentioned link. - ### 2.2.1 Basic scan, pair, connect Execute following steps on linux host. @@ -230,7 +236,12 @@ Steps: BLE5.0 supports 1M, 2M and CODED phy. To use 2M and CODED phy for gatt read/write procedure as follow: -Note: Default selected phy is 1M. To perform gatt read/write with BLE5.0 peripheral, both host and peripheral must have same phy configuration. +Note: +* Default selected phy is 1M. To perform gatt read/write with BLE5.0 peripheral, both host and peripheral must have same phy configuration. + +* 'PHY' feature in BLE 5.0 is verified with btmgmt tool from bluez version 5.56+. + +* If `bluez` version is less than 5.56 ,then upgrade `bluez` version. ##### Using 1M phy: 1M phy is default phy for BLE5.0. Follow above mentioned steps in section 2.2.1 @@ -246,21 +257,29 @@ Steps: 4. while executing connect command, there is `LE Enhanced Connection Complete` event in `btmon` log. Note down `handle` value. 5. After connection, exit form bluetoothctl. Run `exit` in bluetoothctl. 6. Now configure phy into 2M. Run `sudo hcitool cmd 08 32 03 02 02 00`. -7. Follow gatt read/write from gatt menu in bluetoothctl. +7. Follow gatt read/write from `gatt menu` in bluetoothctl. ##### Using CODED phy: Configure CODED phy on host and peripheral side. Steps: -1. To configure phy as 1M and 2M both, run `sudo hcitool cmd 08 31 03 03 03`. +1. To configure phy as CODED phy, run `sudo hcitool cmd 08 31 03 04 04`. 2. To check selected phy, Go to `bluez-5.56` directory. Run `sudo ./tools/btmgmt --index hci0` and run `phy`. 3. Connect to BLE5.0 device using above mentioned steps in section 2.2.1. -4. while executing connect command, there is `LE Enhanced Connection Complete` event in `btmon` log. Note down `handle` value. -5. After connection, exit form bluetoothctl. Run `exit` in bluetoothctl. -6. Now configure phy into 2M. Run `sudo hcitool cmd 08 32 03 02 02 00`. -7. Follow gatt read/write from gatt menu in bluetoothctl. +4. Follow gatt read/write from gatt menu in bluetoothctl. + +## 3. OTA operation + +OTA (Over The Air) update performs following operations. +* Erase ota flash partition of ESP32 +* Download chunk from URL and write that chunk into flash, one by one, till whole binary is written +* Validate the complete written binary in flash +* Sets newly written OTA partition as boot partition +* Reboot the ESP32 after 5 second + +Please follow [OTA update documentation](ota_update.md) for further details. -## 3. Troubleshoot Instructions +## 4. Troubleshoot Instructions Please refer following for troubleshoot instructions if something goes wrong. diff --git a/docs/Linux_based_host/Linux_based_architecture.md b/docs/Linux_based_host/Linux_based_architecture.md index 805d91464f..670223a9ce 100644 --- a/docs/Linux_based_host/Linux_based_architecture.md +++ b/docs/Linux_based_host/Linux_based_architecture.md @@ -127,3 +127,12 @@ Following are few ready to use convenience script provided in the repository. Th python connected_stations_list.py ``` +* **OTA update** + `ota_update.py` script updates ESP32 firmware with provided URL of binary file. This script will perform following operation. + - Erase ota flash partition of ESP32 + - Download chunk from URL and write that chunk into flash, one by one, till whole binary is written + - Validate the complete written binary in flash + - Sets newly written OTA partition as boot partition + - Reboot the ESP32 after 5 second + + Please refer [ota](ota_update.md) for detailed documentation. diff --git a/docs/Linux_based_host/Linux_based_readme.md b/docs/Linux_based_host/Linux_based_readme.md index 18e0369186..82d7c46a03 100644 --- a/docs/Linux_based_host/Linux_based_readme.md +++ b/docs/Linux_based_host/Linux_based_readme.md @@ -190,12 +190,13 @@ This section identifies Raspberry-Pi specific setup requirements. * Git * Python 2.x or 3.x: We have tested ESP-Hosted solution with python 2.7.13 and 3.5.3 * Bluetooth Stack and utilities: - :warning:`Note: We have tested ESP-Hosted solution with bluez 5.43+` + :warning:`Note: ESP-Hosted related BR/EDR 4.2 and BLE 4.2 functionalities are tested with bluez 5.43+. Whereas BLE 5.0 functionalities are tested with bluez 5.45+.` * bluetooth * bluez * bluez-tools * rfkill * bluez-firmware + * We suggest latest stable bluez version to be used. Any other bluetooth stack instead of bluez also could be used. ### 2.2 ESP-IDF Setup :warning:`Note: ESP-IDF is needed to compile ESP-Hosted firmware source. Skip this step if you are planning to use pre-built release binaries.` diff --git a/docs/Linux_based_host/ota_update.md b/docs/Linux_based_host/ota_update.md new file mode 100644 index 0000000000..f00905c2e8 --- /dev/null +++ b/docs/Linux_based_host/ota_update.md @@ -0,0 +1,38 @@ +## **ESP-Hosted OTA update ESP32 image** + +Please note, Maximum size of New OTA image binary depends upon - +1. Total flash size available +2. OTA partition size in partition table +As per current limits, upto 1MB binary size is supported which is configurable using custom partition table CSV. Please refer [partition tables](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/partition-tables.html) for more info. + +There are basic three control path commands for OTA are provided in C and python, as follows: +* esp_ota_begin() -- Sets available OTA partition in flash for OTA write operation and erase it. +* esp_ota_write() -- Write chunk of OTA image data on OTA partition in flash. +* esp_ota_end() -- Validate written OTA image, set OTA partition for next boot and reboot ESP32 after 5 sec. + +Definition of these commands are present in [commands.c](host/host_common/commands.c) and [commands_lib.py](host/linux/host_control/python_support/commands_lib.py) files. + +## **How to use** + +### On ESP32 side +Build and flash ESP-Hosted application using `idf.py build flash monitor`. + +### On Host side + +#### Note +1. Please stop ongoing BT/BLE operations before starting OTA process. +2. OTA operation should not get interrupted by any other control path command. +3. OTA update using HTTP URL is only supported in python. In case HTTP based OTA update is desired, user can do the same using third party HTTP client library. +4. The OTA update using C currently assumes the complete binary is downloaded locally. + +### Python Implementation +User can skip step 1, if station is connected to AP and IP is assigned to `ethsta0` interface. + +1. Perform station connect using [wifi station mode operations](Getting_started.md#111-wi-fi-station-mode-operations) +-> Connect to external access point. + +2. For further details follow this [Link](../python_demo.md#ota-update). + +### C Implementation + +For Further details follow this [Link](../c_demo.md#c-demo-application). diff --git a/docs/c_api.md b/docs/c_api.md index bdd8f2a998..ecf8fe92c7 100644 --- a/docs/c_api.md +++ b/docs/c_api.md @@ -1,6 +1,6 @@ # Control Interface: C API's -This document describes C API's provided for control path interface. Please refer [commands.c](../host/host_common/commands.c) for API's defination. [c_demo.md](c_demo.md) gives overview of how to test control path interface in application also how to perform stress testing of control path interface. +This document describes C API's provided for control path interface. Please refer [commands.c](../host/host_common/commands.c) for API's definition. [c_demo.md](c_demo.md) gives overview of how to test control path interface in application also how to perform stress testing of control path interface. A [stress.c](../host/linux/host_control/c_support/stress.c) can use for stress testing of control path commands. In which, `STRESS_TEST_COUNT` variable represents number of iterations and `STRESS_TEST` variable defines which test should get executed. @@ -531,6 +531,43 @@ Current WiFi transmitting power, unit is 0.25dBm. #### **Note** It is possible that the current wifi transmit power is lesser than that of the requested max transmit power as part of `wifi_set_max_tx_power` API. +#### Return + +- 0 : SUCCESS +- -1 : FAILURE + +--- + +### 2.20 `int esp_ota_begin()` + +esp ota begin function performs an OTA begin operation for ESP32 which erases and prepares existing flash partition for new flash writing. + +#### Return +- 0 : SUCCESS +- -1 : FAILURE + +--- + +### 2.21 `int esp_ota_write(uint8_t* ota_data, uint32_t ota_data_len)` + +esp ota write function performs an OTA write operation for ESP32, It writes bytes from `ota_data` buffer with `ota_data_len` number of bytes to OTA partition in flash. Number of bytes can be small than size of complete binary to be flashed. In that case, this caller is expected to repeatedly call this function till total size written equals size of complete binary. + +#### Parameters + +- `ota_data` : OTA data buffer +- `ota_data_len` : length of OTA data buffer + +#### Return + +- 0 : SUCCESS +- -1 : FAILURE + +--- + +### 2.22 `int esp_ota_end()` + +esp ota end function performs an OTA end operation for ESP32, It validates written OTA image, sets newly written OTA partition as boot partition for next boot, Creates timer which reset ESP32 after 5 sec. + #### Return - 0 : SUCCESS - -1 : FAILURE diff --git a/docs/c_demo.md b/docs/c_demo.md index 71f56798d9..c2b87af301 100644 --- a/docs/c_demo.md +++ b/docs/c_demo.md @@ -12,6 +12,7 @@ | sta_list | List external stations connected to softAP | | ap_vendor_ie | Set vendor information element for ESP32 softAP | | wifi_tx_power | sets WiFi maximum transmitting power and get WiFi current transmitting power | +| ota | performs OTA operation using local OTA binary file | It uses APIs present in [test_api.c](../host/linux/host_control/c_support/test_api.c). User should first modify configuration parameters in [test_config.h](../host/linux/host_control/c_support/test_config.h). Then run `make` in [c_support](../host/linux/host_control/c_support) to compile `test.c`. @@ -44,6 +45,15 @@ sudo dnsmasq --no-daemon --no-resolv --no-poll --dhcp-script=/system/bin/dhcp_an sudo ifconfig ethap0 192.168.4.5 ``` +* OTA - +The OTA update using C currently assumes the complete binary is downloaded locally. +OTA update using HTTP URL is only supported in [python](python_demo.md#ota-update). In case HTTP based OTA update is desired, user can do the same using third party HTTP client library. + +``` +ex. +./test.out ota +``` + # C stress Application [stress.c](../host/linux/host_control/c_support/stress.c) use for stress testing of control path APIs. It provides basic command line arguments as follows: diff --git a/docs/python_api.md b/docs/python_api.md index 68fb2a31fe..ef98f9a9dc 100644 --- a/docs/python_api.md +++ b/docs/python_api.md @@ -6,7 +6,7 @@ This document describes python API's provided for control interface. A [stress.py](../host/linux/host_control/python_support/stress.py) can use for stress testing of control path commands. In which, `STRESS_TEST_COUNT` variable represents number of iterations and `STRESS_TEST` variable defines which test should get executed. -## 1. `wifi_get_mac` +## 1. `wifi_get_mac(mode)` This is used to retrieve the MAC address of ESP's station or softAP interface ### Parameters @@ -20,7 +20,7 @@ String in form of "XX:XX:XX:XX:XX:XX" with MAC address of ESP interface mapping --- -## 2. `wifi_get_mode` +## 2. `wifi_get_mode()` This is used to retrieve the ESP32's Wi-Fi mode ### Return @@ -32,7 +32,7 @@ This is used to retrieve the ESP32's Wi-Fi mode --- -## 3. `wifi_set_mode` +## 3. `wifi_set_mode(mode)` This is used to set the ESP32's Wi-Fi mode ### Parameters @@ -47,7 +47,7 @@ This is used to set the ESP32's Wi-Fi mode --- -## 4. `wifi_set_mac` +## 4. `wifi_set_mac(mode, mac)` This is used to set MAC address for ESP's station or softAP interface ### Parameters @@ -69,7 +69,7 @@ For example, the MAC address can set to be "1a:XX:XX:XX:XX:XX", but can not be " --- -## 5. `wifi_set_power_save_mode` +## 5. `wifi_set_power_save_mode(power_save_mode)` Set ESP32's power save mode ### Parameters @@ -87,7 +87,7 @@ Maximum modem power saving. In this mode, interval to receive beacons is determi --- -## 6. `wifi_get_power_save_mode` +## 6. `wifi_get_power_save_mode()` Get the power save mode of ESP32 ### Return @@ -101,7 +101,7 @@ ESP32 on boot is configured in WIFI_PS_MIN_MODEM --- -## 7. `wifi_set_ap_config` +## 7. `wifi_set_ap_config(ssid, pwd, bssid, is_wpa3_supported, listen_interval)` Set the AP config to which ESP32 station should connect ### Parameters @@ -123,7 +123,7 @@ Listen interval for ESP32 station to receive beacon when WIFI_PS_MAX_MODEM is se --- -## 8. `wifi_get_ap_config` +## 8. `wifi_get_ap_config()` Get the AP config to which ESP32 station is connected ### Return @@ -154,7 +154,7 @@ In case of transport failure --- -## 9. `wifi_disconnect_ap` +## 9. `wifi_disconnect_ap()` Disconnect the AP to which ESP32 station is connected ### Return @@ -162,7 +162,7 @@ Disconnect the AP to which ESP32 station is connected --- -## 10. `wifi_ap_scan_list` +## 10. `wifi_ap_scan_list()` Set the AP config to which ESP32 station should connect ### Return @@ -191,7 +191,7 @@ RSSI signal strength --- -## 11. `wifi_set_softap_config` +## 11. `wifi_set_softap_config(ssid, pwd, chnl, ecn, max_conn, ssid_hidden, bw)` Set the ESP32's softAP config ### Parameters @@ -222,7 +222,7 @@ softAP should broadcast its SSID or not --- -## 12. `wifi_get_softap_config` +## 12. `wifi_get_softap_config()` Get the ESP32's softAP config ### Return @@ -250,7 +250,7 @@ Maximum number of stations can connect to ESP32 softAP (will be in range of 1 to --- -## 13. `wifi_stop_softap` +## 13. `wifi_stop_softap()` Stop the ESP32's softAP ### Return @@ -258,7 +258,7 @@ Stop the ESP32's softAP --- -## 14. `wifi_connected_stations_list` +## 14. `wifi_connected_stations_list()` Get the list of connected station to the ESP32 softAP. ### Return @@ -275,7 +275,7 @@ RSSI signal strength --- -## 15. `create_socket` +## 15. `create_socket(domain, types, protocol)` This function creates an endpoint for communication ### Parameters @@ -292,7 +292,7 @@ This specifies a particular protocol to be used with the socket. Generally proto --- -## 16. `close_socket` +## 16. `close_socket(sock)` This function closes endpoint of the communication ### Parameters @@ -304,7 +304,7 @@ This specifies the file descriptor of the endpoint/socket to be closed --- -## 17. `wifi_set_max_tx_power` +## 17. `wifi_set_max_tx_power(wifi_max_tx_power)` Function sets maximum WiFi transmitting power at ESP32. @@ -330,7 +330,7 @@ Maximum WiFi transmitting power. --- -### 18. `wifi_get_curr_tx_power` +### 18. `wifi_get_curr_tx_power()` Function gets current WiFi transmiting power at ESP32. @@ -342,3 +342,34 @@ returns Current WiFi transmitting power, unit is 0.25dBm. It is possible that th - "failure" string --- + +## 19. `esp_ota_begin()` +esp ota begin function performs an OTA begin operation for ESP32 which erases and prepares existing flash partition for new flash writing. + +### Return + +"success" or "failure" string + +--- + +## 20. `esp_ota_write(ota_data, ota_data_len)` +esp ota write function performs an OTA write operation for ESP32, It writes bytes from `ota_data` buffer with `ota_data_len` number of bytes to OTA partition in flash. Number of bytes can be small than size of complete binary to be flashed. In that case, this caller is expected to repeatedly call this function till total size written equals size of complete binary. +### Parameters + +- `ota_data` : OTA data buffer +- `ota_data_len` : length of OTA data buffer + +### Return + +"success" or "failure" string + +--- + +## 21. `esp_ota_end()` +esp ota end function performs an OTA end operation for ESP32, It validates written OTA image, sets newly written OTA partition as boot partition for next boot, Creates timer which reset ESP32 after 5 sec. + +### Return + +"success" or "failure" string + +--- diff --git a/docs/python_demo.md b/docs/python_demo.md index 7376b8ecd1..5fe28b1e1f 100644 --- a/docs/python_demo.md +++ b/docs/python_demo.md @@ -1,4 +1,4 @@ -# Python Demo Application +# Demo Application [test.py](../host/linux/host_control/python_support/test.py) is a demo application to test control path interface: @@ -27,7 +27,7 @@ sudo dnsmasq --no-daemon --no-resolv --no-poll --dhcp-script=/system/bin/dhcp_an sudo ifconfig ethap0 192.168.4.5 ``` -# Python stress Application +# Stress Application [stress.py](../host/linux/host_control/python_support/stress.py) use for stress testing of control path APIs. User should first modify configuration parameters in [test_config.py](../host/linux/host_control/python_support/test_config.py). `STRESS_TEST_COUNT` variable is defined in `stress.py` for number of iterations for stress testing. @@ -37,3 +37,45 @@ Please execute `stress.py` as below. ex. sudo python stress.py ``` + +# OTA update + +* `ota_update.py` is script which placed in `host/linux/host_control/python_support/` directory. Use below command to navigate to this directory. +```sh +$ cd host/linux/host_control/python_support/ +``` + +* This script assumes station is connected to AP, IP is assigned to `ethsta0` and HTTP URL is accessible. + +* [ota_update.py](host/linux/host_control/python_support/ota_update.py) python script is used to do OTA update on ESP32. It downloads **chunk** of OTA image data using HTTP client over `ethsta0` interface and writes on ESP32. After successful completion it restarts ESP32 after 5 sec. + +Usage: +1. Start HTTP server on a remote machine which contains OTA image. +Following python command can be used to start HTTP server if it's not running already. + +Syntax: +``` +python3 -m http.server +``` +Example: +``` +python3 -m http.server 9999 +``` + +2. Pass OTA image URL as command line argument to ota_update.py. + +Syntax: +``` +python3 ota_update.py "http://:/ota_image.bin" +``` +Example: +``` +python3 ota_update.py "http://192.168.0.106:9999/network_adapter.bin" +``` + +3. It will performs following operations. +* Erase ota flash partition of ESP32 +* Download chunk from URL and write that chunk into flash, one by one, till whole binary is written +* Validate the complete written binary in flash +* Sets newly written OTA partition as boot partition +* Reboot the ESP32 after 5 second diff --git a/host/linux/host_control/python_support/commands_lib.py b/host/linux/host_control/python_support/commands_lib.py index 593ce89503..25557aabad 100644 --- a/host/linux/host_control/python_support/commands_lib.py +++ b/host/linux/host_control/python_support/commands_lib.py @@ -463,8 +463,8 @@ def esp_ota_begin(): # ota_data : OTA data buffer # ota_data_len : length of OTA data buffer -def esp_ota_write(ota_data, ota_image_len): - ret = commands_map_py_to_c.esp_ota_write(ota_data, ota_image_len) +def esp_ota_write(ota_data, ota_data_len): + ret = commands_map_py_to_c.esp_ota_write(ota_data, ota_data_len) if not ret: return success else: diff --git a/host/linux/host_control/python_support/hosted_config.py b/host/linux/host_control/python_support/hosted_config.py index 2f3edee6f0..2bd0127ea4 100644 --- a/host/linux/host_control/python_support/hosted_config.py +++ b/host/linux/host_control/python_support/hosted_config.py @@ -17,6 +17,9 @@ success = "success" failure = "failure" +no_ap_found_str = "no_ap_found" +invalid_password_str = "invalid_password_str" +out_of_range_str = "out_of_range" SSID_LENGTH = 32 PASSWORD_LENGTH = 64 @@ -40,12 +43,6 @@ INVALID_ARGUMENT = 4 OUT_OF_RANGE = 5 -not_connected_str = "not_connected" -no_ap_found_str = "no_ap_found" -invalid_password_str = "invalid_password" -invalid_argument_str = "invalid_argument" -out_of_range_str = "out_of_range" - (WIFI_MODE_NONE, WIFI_MODE_STATION, WIFI_MODE_SOFTAP, WIFI_MODE_SOFTAP_STATION, WIFI_MODE_MAX) = (0, 1, 2, 3, 4) diff --git a/ota_feature.md b/ota_feature.md deleted file mode 100644 index ba28626cbe..0000000000 --- a/ota_feature.md +++ /dev/null @@ -1,73 +0,0 @@ -## ESP-Hosted OTA feature - -Please note, Maximum size of New OTA image binary depends upon - -1. Total flash size available -2. OTA partition size in partition table -As per current limits, upto 1MB binary size is supported which is configurable from above options. Please refer [partion tables](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/partition-tables.html) for more info. - -There are basic three control path commands for OTA are provided in C and python, as follows: -* esp_ota_begin() -- Sets available OTA partition in flash for OTA write operation and erase it. -* esp_ota_write() -- Write chunk of OTA image data on OTA partition in flash. -* esp_ota_end() -- Validate written OTA image, set OTA partition for next boot and reboot ESP32 after 5 sec. - -Defination of these commands are present in [commands.c](host/host_common/commands.c) and [commands.py](host/linux/host_control/python_support/commands.py) files. - -## How to use - -### On ESP32 side -Build and flash ESP-Hosted application using `idf.py build flash monitor`. - -### On Host side - -#### Note -1. Please stop ongoing BT/BLE operations before starting OTA process. -2. OTA operation should not get interrupted by any other control path command. - -Perform station connect using [station_connect.py](host/linux/host_control/python_support/station_connect.py) script or [test.c](host/linux/host_control/c_support/test.c), which ups `ethsta0` interface. Assign IP using dhclient. - -#### OTA Using Python3 -* ota_update.py - -[ota_update.py](host/linux/host_control/python_support/ota_update.py) python script is used to do OTA update on ESP32. It downloads chunk of OTA image data using HTTP client over `ethsta0` interface and writes on ESP32. After successful completion it restarts ESP32 after 5 sec. - -Usage: -1. start HTTP server on a remote machine which contains OTA image. -Following python command can be used to start HTTP server if it's not running already. - -Syntax: -``` -python3 -m http.server -``` -Example: -``` -python3 -m http.server 9999 -``` - -2. Pass OTA image URL as command line argument to ota_update.py. - -Syntax: -``` -python3 ota_update.py "http://:/ota_image.bin" -``` -Example: -``` -python3 ota_update.py "http://192.168.0.106:9999/network_adapter.bin" -``` - -#### OTA Using C - -[test.c](host/linux/host_control/c_support/test.c) is a test application to test control path command. `./test.out ota "ota_image"` writes chunk of already downloaded OTA image data on ESP32. After successful completion it restarts ESP32 after 5 sec. - -#### Note -The OTA update using C currently assumes the complete binary is available. -User can do OTA update similar to python method using third party HTTP library. - -Usage: -1. Run `make` in `host/linux/host_control/c_support` directory. -2. Download OTA image binary on host machine. -3. Pass OTA image binary path as next comamnd line argument after `ota`. - -Example: -``` -./test.out ota "/path/to/ota_image.bin" -```