summaryrefslogtreecommitdiff
path: root/fw/esp32
diff options
context:
space:
mode:
Diffstat (limited to 'fw/esp32')
-rw-r--r--fw/esp32/LICENSE339
-rw-r--r--fw/esp32/Makefile9
-rw-r--r--fw/esp32/components/eos/app.c225
-rw-r--r--fw/esp32/components/eos/at_cmd.c174
-rw-r--r--fw/esp32/components/eos/cell.c122
-rw-r--r--fw/esp32/components/eos/cell_modem.c825
-rw-r--r--fw/esp32/components/eos/cell_pcm.c275
-rw-r--r--fw/esp32/components/eos/cell_pdp.c31
-rw-r--r--fw/esp32/components/eos/cell_sms.c276
-rw-r--r--fw/esp32/components/eos/cell_ussd.c103
-rw-r--r--fw/esp32/components/eos/cell_voice.c129
-rw-r--r--fw/esp32/components/eos/component.mk0
-rw-r--r--fw/esp32/components/eos/gsm.c472
-rw-r--r--fw/esp32/components/eos/gsm_cp.c110
-rw-r--r--fw/esp32/components/eos/i2c.c82
-rw-r--r--fw/esp32/components/eos/include/_net.h1
-rw-r--r--fw/esp32/components/eos/include/app.h23
-rw-r--r--fw/esp32/components/eos/include/at_cmd.h18
-rw-r--r--fw/esp32/components/eos/include/cell.h91
-rw-r--r--fw/esp32/components/eos/include/eos.h27
-rw-r--r--fw/esp32/components/eos/include/gsm.h139
-rw-r--r--fw/esp32/components/eos/include/i2c.h9
-rw-r--r--fw/esp32/components/eos/include/msgq.h31
-rw-r--r--fw/esp32/components/eos/include/net.h34
-rw-r--r--fw/esp32/components/eos/include/power.h20
-rw-r--r--fw/esp32/components/eos/include/sock.h18
-rw-r--r--fw/esp32/components/eos/include/tun.h1
l---------fw/esp32/components/eos/include/unicode.h1
-rw-r--r--fw/esp32/components/eos/include/wifi.h13
-rw-r--r--fw/esp32/components/eos/msgq.c66
-rw-r--r--fw/esp32/components/eos/net.c302
-rw-r--r--fw/esp32/components/eos/power.c324
-rw-r--r--fw/esp32/components/eos/sock.c159
-rw-r--r--fw/esp32/components/eos/tun.c65
l---------fw/esp32/components/eos/unicode.c1
-rwxr-xr-xfw/esp32/components/eos/wifi.c343
-rw-r--r--fw/esp32/main/app_main.c46
-rw-r--r--fw/esp32/main/component.mk8
38 files changed, 4912 insertions, 0 deletions
diff --git a/fw/esp32/LICENSE b/fw/esp32/LICENSE
new file mode 100644
index 0000000..d159169
--- /dev/null
+++ b/fw/esp32/LICENSE
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/fw/esp32/Makefile b/fw/esp32/Makefile
new file mode 100644
index 0000000..eb26385
--- /dev/null
+++ b/fw/esp32/Makefile
@@ -0,0 +1,9 @@
+#
+# This is a project Makefile. It is assumed the directory this Makefile resides in is a
+# project subdirectory.
+#
+
+PROJECT_NAME := eos-app
+
+include $(IDF_PATH)/make/project.mk
+
diff --git a/fw/esp32/components/eos/app.c b/fw/esp32/components/eos/app.c
new file mode 100644
index 0000000..5f8cc43
--- /dev/null
+++ b/fw/esp32/components/eos/app.c
@@ -0,0 +1,225 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <freertos/FreeRTOS.h>
+#include <freertos/semphr.h>
+#include <freertos/task.h>
+
+#include <esp_system.h>
+#include <esp_log.h>
+#include <esp_err.h>
+#include <esp_heap_caps.h>
+#include <driver/gpio.h>
+#include <driver/spi_slave.h>
+
+#include "eos.h"
+#include "msgq.h"
+#include "app.h"
+
+#define SPI_GPIO_CTS 26
+#define SPI_GPIO_RTS 27
+#define SPI_GPIO_MOSI 13
+#define SPI_GPIO_MISO 12
+#define SPI_GPIO_SCLK 14
+#define SPI_GPIO_CS 15
+
+#define SPI_SIZE_BUF (EOS_APP_SIZE_BUF + 8)
+#define SPI_SIZE_HDR 3
+
+static EOSBufQ app_buf_q;
+static unsigned char *app_bufq_array[EOS_APP_SIZE_BUFQ];
+
+static EOSMsgQ app_send_q;
+static EOSMsgItem app_sndq_array[EOS_APP_SIZE_SNDQ];
+
+static SemaphoreHandle_t mutex;
+static SemaphoreHandle_t semaph;
+static TaskHandle_t app_xchg_task_handle;
+static const char *TAG = "EOS APP";
+
+static eos_app_fptr_t app_handler[EOS_APP_MAX_MTYPE];
+
+static void bad_handler(unsigned char mtype, unsigned char *buffer, uint16_t len) {
+ ESP_LOGE(TAG, "bad handler: %d len: %d", mtype, len);
+}
+
+// Called after a transaction is queued and ready for pickup by master. We use this to set the handshake line high.
+static void _post_setup_cb(spi_slave_transaction_t *trans) {
+ gpio_set_level(SPI_GPIO_CTS, 1);
+}
+
+// Called after transaction is sent/received. We use this to set the handshake line low.
+static void _post_trans_cb(spi_slave_transaction_t *trans) {
+ gpio_set_level(SPI_GPIO_CTS, 0);
+}
+
+static void app_xchg_task(void *pvParameters) {
+ unsigned char mtype = 0;
+ unsigned char mtype_flags = 0;
+ unsigned char *buffer;
+ uint16_t len;
+ unsigned char *buf_send = heap_caps_malloc(SPI_SIZE_BUF, MALLOC_CAP_DMA);
+ unsigned char *buf_recv = heap_caps_malloc(SPI_SIZE_BUF, MALLOC_CAP_DMA);
+ esp_err_t ret;
+ size_t trans_len;
+
+ static spi_slave_transaction_t spi_tr;
+
+ //Configuration for the SPI bus
+ static spi_bus_config_t spi_bus_cfg = {
+ .mosi_io_num = SPI_GPIO_MOSI,
+ .miso_io_num = SPI_GPIO_MISO,
+ .sclk_io_num = SPI_GPIO_SCLK
+ };
+
+ //Configuration for the SPI slave interface
+ static spi_slave_interface_config_t spi_slave_cfg = {
+ .mode = 0,
+ .spics_io_num = SPI_GPIO_CS,
+ .queue_size = 2,
+ .flags = 0,
+ .post_setup_cb = _post_setup_cb,
+ .post_trans_cb = _post_trans_cb
+ };
+
+ //Initialize SPI slave interface
+ ret = spi_slave_initialize(HSPI_HOST, &spi_bus_cfg, &spi_slave_cfg, 1);
+ assert(ret == ESP_OK);
+
+ memset(&spi_tr, 0, sizeof(spi_tr));
+ spi_tr.tx_buffer = buf_send;
+ spi_tr.rx_buffer = buf_recv;
+ spi_tr.length = SPI_SIZE_BUF * 8;
+
+ while (1) {
+ xSemaphoreTake(mutex, portMAX_DELAY);
+
+ eos_msgq_pop(&app_send_q, &mtype, &buffer, &len);
+ if (mtype) {
+ buf_send[0] = mtype;
+ buf_send[1] = len >> 8;
+ buf_send[2] = len & 0xFF;
+ if (buffer) {
+ memcpy(buf_send + SPI_SIZE_HDR, buffer, len);
+ eos_bufq_push(&app_buf_q, buffer);
+ xSemaphoreGive(semaph);
+ }
+ } else {
+ gpio_set_level(SPI_GPIO_RTS, 0);
+ buf_send[0] = 0;
+ buf_send[1] = 0;
+ buf_send[2] = 0;
+ len = 0;
+ }
+
+ xSemaphoreGive(mutex);
+
+ buf_recv[0] = 0;
+ buf_recv[1] = 0;
+ buf_recv[2] = 0;
+ spi_slave_transmit(HSPI_HOST, &spi_tr, portMAX_DELAY);
+
+ trans_len = spi_tr.trans_len / 8;
+ if (trans_len < SPI_SIZE_HDR) continue;
+
+ if (len + SPI_SIZE_HDR > trans_len) {
+ spi_tr.tx_buffer = buf_send + trans_len;
+ spi_tr.rx_buffer = buf_recv + trans_len;
+ spi_tr.length = (SPI_SIZE_BUF - trans_len) * 8;
+ spi_slave_transmit(HSPI_HOST, &spi_tr, portMAX_DELAY);
+ spi_tr.tx_buffer = buf_send;
+ spi_tr.rx_buffer = buf_recv;
+ spi_tr.length = SPI_SIZE_BUF * 8;
+ }
+ mtype = buf_recv[0] & ~EOS_APP_MTYPE_FLAG_MASK;
+ mtype_flags = buf_recv[0] & EOS_APP_MTYPE_FLAG_MASK;
+ len = (uint16_t)buf_recv[1] << 8;
+ len |= (uint16_t)buf_recv[2] & 0xFF;
+ buffer = buf_recv + 3;
+
+ if (mtype == 0x00) continue;
+
+ if ((mtype <= EOS_APP_MAX_MTYPE) && (len <= EOS_APP_MTU)) {
+ app_handler[mtype - 1](mtype, buffer, len);
+ } else {
+ bad_handler(mtype, buffer, len);
+ }
+ }
+ vTaskDelete(NULL);
+}
+
+void eos_app_init(void) {
+ int i;
+
+ // Configuration for the handshake lines
+ gpio_config_t io_conf;
+
+ io_conf.intr_type = GPIO_INTR_DISABLE;
+ io_conf.mode = GPIO_MODE_OUTPUT;
+ io_conf.pull_up_en = 0;
+ io_conf.pull_down_en = 0;
+ io_conf.pin_bit_mask = ((uint64_t)1 << SPI_GPIO_CTS);
+ gpio_config(&io_conf);
+ gpio_set_level(SPI_GPIO_CTS, 0);
+
+ io_conf.intr_type = GPIO_INTR_DISABLE;
+ io_conf.mode = GPIO_MODE_OUTPUT;
+ io_conf.pull_up_en = 0;
+ io_conf.pull_down_en = 0;
+ io_conf.pin_bit_mask = ((uint64_t)1 << SPI_GPIO_RTS);
+ gpio_config(&io_conf);
+ gpio_set_level(SPI_GPIO_RTS, 0);
+
+ eos_msgq_init(&app_send_q, app_sndq_array, EOS_APP_SIZE_SNDQ);
+ eos_bufq_init(&app_buf_q, app_bufq_array, EOS_APP_SIZE_BUFQ);
+ for (i=0; i<EOS_APP_SIZE_BUFQ; i++) {
+ eos_bufq_push(&app_buf_q, malloc(EOS_APP_SIZE_BUF));
+ }
+
+ for (i=0; i<EOS_APP_MAX_MTYPE; i++) {
+ app_handler[i] = bad_handler;
+ }
+
+ semaph = xSemaphoreCreateCounting(EOS_APP_SIZE_BUFQ, EOS_APP_SIZE_BUFQ);
+ mutex = xSemaphoreCreateBinary();
+ xSemaphoreGive(mutex);
+ xTaskCreate(&app_xchg_task, "app_xchg", EOS_TASK_SSIZE_APP_XCHG, NULL, EOS_TASK_PRIORITY_APP_XCHG, &app_xchg_task_handle);
+ ESP_LOGI(TAG, "INIT");
+}
+
+unsigned char *eos_app_alloc(void) {
+ unsigned char *ret;
+
+ xSemaphoreTake(semaph, portMAX_DELAY);
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ ret = eos_bufq_pop(&app_buf_q);
+ xSemaphoreGive(mutex);
+
+ return ret;
+}
+
+void eos_app_free(unsigned char *buf) {
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ eos_bufq_push(&app_buf_q, buf);
+ xSemaphoreGive(semaph);
+ xSemaphoreGive(mutex);
+}
+
+int eos_app_send(unsigned char mtype, unsigned char *buffer, uint16_t len) {
+ int rv = EOS_OK;
+
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ gpio_set_level(SPI_GPIO_RTS, 1);
+ rv = eos_msgq_push(&app_send_q, mtype, buffer, len);
+ xSemaphoreGive(mutex);
+
+ if (rv) eos_app_free(buffer);
+
+ return rv;
+}
+
+void eos_app_set_handler(unsigned char mtype, eos_app_fptr_t handler) {
+ if (handler == NULL) handler = bad_handler;
+ if (mtype && (mtype <= EOS_APP_MAX_MTYPE)) app_handler[mtype - 1] = handler;
+}
diff --git a/fw/esp32/components/eos/at_cmd.c b/fw/esp32/components/eos/at_cmd.c
new file mode 100644
index 0000000..82baa92
--- /dev/null
+++ b/fw/esp32/components/eos/at_cmd.c
@@ -0,0 +1,174 @@
+#include <stdlib.h>
+#include <string.h>
+
+#include <freertos/FreeRTOS.h>
+#include <freertos/semphr.h>
+#include <esp_timer.h>
+#include <esp_log.h>
+
+#include "eos.h"
+
+#include "cell.h"
+#include "at_cmd.h"
+
+static const char *TAG = "EOS ATCMD";
+
+typedef struct ATURCItem {
+ regex_t re;
+ at_urc_cb_t cb;
+ char pattern[AT_SIZE_PATTERN];
+} ATURCItem;
+
+typedef struct ATURCList {
+ ATURCItem item[AT_SIZE_URC_LIST];
+ int len;
+} ATURCList;
+
+static ATURCList urc_list;
+static SemaphoreHandle_t mutex;
+
+static char at_buf[EOS_CELL_UART_SIZE_BUF];
+
+void at_init(void) {
+ memset(&urc_list, 0, sizeof(ATURCList));
+
+ mutex = xSemaphoreCreateBinary();
+ xSemaphoreGive(mutex);
+}
+
+int at_urc_process(char *urc) {
+ regmatch_t match[AT_SIZE_NMATCH];
+ at_urc_cb_t cb = NULL;
+ int i;
+
+ if (urc[0] == '\0') return 0;
+
+ xSemaphoreTake(mutex, portMAX_DELAY);
+
+ for (i=0; i<urc_list.len; i++) {
+ if (regexec(&urc_list.item[i].re, urc, AT_SIZE_NMATCH, match, 0) == 0) {
+ cb = urc_list.item[i].cb;
+ break;
+ }
+ }
+
+ xSemaphoreGive(mutex);
+
+ if (cb) {
+ cb(urc, match);
+ ESP_LOGI(TAG, "URC Processed: %s", urc);
+ return 1;
+ }
+
+ ESP_LOGI(TAG, "URC NOT Processed: %s", urc);
+ return 0;
+}
+
+int at_urc_insert(char *pattern, at_urc_cb_t cb, int flags) {
+ int r;
+ int rv = EOS_OK;
+
+ if (strlen(pattern) >= AT_SIZE_PATTERN) return EOS_ERR;
+
+ xSemaphoreTake(mutex, portMAX_DELAY);
+
+ r = regcomp(&urc_list.item[urc_list.len].re, pattern, flags);
+ if (r) rv = EOS_ERR;
+
+ if (!rv && (urc_list.len == AT_SIZE_URC_LIST)) rv = EOS_ERR_FULL;
+ if (!rv) {
+ strcpy(urc_list.item[urc_list.len].pattern, pattern);
+ urc_list.item[urc_list.len].cb = cb;
+ urc_list.len++;
+ }
+
+ xSemaphoreGive(mutex);
+
+ return rv;
+}
+
+int at_urc_delete(char *pattern) {
+ int i;
+ int rv = EOS_ERR_NOTFOUND;
+
+ xSemaphoreTake(mutex, portMAX_DELAY);
+
+ for (i=0; i<urc_list.len; i++) {
+ if ((strcmp(pattern, urc_list.item[i].pattern) == 0)) {
+ regfree(&urc_list.item[i].re);
+ if (i != urc_list.len - 1) memmove(&urc_list.item[i], &urc_list.item[i + 1], (urc_list.len - i - 1) * sizeof(ATURCItem));
+ urc_list.len--;
+ memset(&urc_list.item[urc_list.len], 0, sizeof(ATURCItem));
+ rv = EOS_OK;
+ break;
+ }
+ }
+
+ xSemaphoreGive(mutex);
+
+ return rv;
+}
+
+void at_cmd(char *cmd) {
+ eos_modem_write(cmd, strlen(cmd));
+ ESP_LOGI(TAG, "Cmd: %s", cmd);
+}
+
+int at_expect(char *str_ok, char *str_err, uint32_t timeout) {
+ return at_expect_match(str_ok, str_err, NULL, NULL, 0, REG_EXTENDED, timeout);
+}
+
+int at_expect_match(char *str_ok, char *str_err, char **buf, regmatch_t match[], size_t match_size, int flags, uint32_t timeout) {
+ int rv;
+ regex_t re_ok;
+ regex_t re_err;
+ uint32_t e = 0;
+ uint64_t t_start = esp_timer_get_time();
+
+ if (str_ok) {
+ rv = regcomp(&re_ok, str_ok, flags);
+ if (rv) {
+ return EOS_ERR;
+ }
+ }
+
+ if (str_err) {
+ rv = regcomp(&re_err, str_err, flags);
+ if (rv) {
+ if (str_ok) regfree(&re_ok);
+ return EOS_ERR;
+ }
+ }
+
+ if (buf) *buf = at_buf;
+ do {
+ rv = eos_modem_readln(at_buf, sizeof(at_buf), timeout ? timeout - e : 0);
+ if (rv) {
+ if (str_ok) regfree(&re_ok);
+ if (str_err) regfree(&re_err);
+ return rv;
+ }
+
+ ESP_LOGI(TAG, "Expect: %s", at_buf);
+
+ if (str_ok && (regexec(&re_ok, at_buf, match_size, match, 0) == 0)) {
+ regfree(&re_ok);
+ if (str_err) regfree(&re_err);
+ return 1;
+ }
+ if (str_err && (regexec(&re_err, at_buf, match_size, match, 0) == 0)) {
+ if (str_ok) regfree(&re_ok);
+ regfree(&re_err);
+ return 0;
+ }
+
+ at_urc_process(at_buf);
+
+ e = (uint32_t)(esp_timer_get_time() - t_start) / 1000;
+ if (timeout && (e > timeout)) {
+ if (str_ok) regfree(&re_ok);
+ if (str_err) regfree(&re_err);
+ return EOS_ERR_TIMEOUT;
+ }
+ } while (1);
+}
diff --git a/fw/esp32/components/eos/cell.c b/fw/esp32/components/eos/cell.c
new file mode 100644
index 0000000..b31e973
--- /dev/null
+++ b/fw/esp32/components/eos/cell.c
@@ -0,0 +1,122 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <freertos/FreeRTOS.h>
+#include <freertos/semphr.h>
+#include <freertos/task.h>
+#include <freertos/queue.h>
+#include <esp_log.h>
+
+#include "eos.h"
+#include "msgq.h"
+#include "net.h"
+#include "cell.h"
+
+#define CELL_SIZE_QUEUE 2
+
+static const char *TAG = "EOS CELL";
+
+static uint8_t cell_mode;
+
+static EOSBufQ cell_buf_q;
+static unsigned char *cell_bufq_array[CELL_SIZE_QUEUE];
+
+static SemaphoreHandle_t mutex;
+static QueueHandle_t cell_queue;
+
+static void _cell_handler(unsigned char _mtype, unsigned char *buffer, uint16_t size) {
+ uint8_t mtype;
+
+ if (size < 1) return;
+ mtype = buffer[0];
+ switch (mtype & EOS_CELL_MTYPE_MASK) {
+ case EOS_CELL_MTYPE_DEV:
+ switch (mtype & ~EOS_CELL_MTYPE_MASK) {
+ case EOS_CELL_MTYPE_RESET:
+ eos_modem_reset();
+ break;
+
+ case EOS_CELL_MTYPE_UART_DATA:
+ if (eos_modem_get_mode() == EOS_CELL_UART_MODE_RELAY) eos_modem_write(buffer+1, size-1);
+ break;
+
+ case EOS_CELL_MTYPE_UART_TAKE:
+ cell_mode = eos_modem_get_mode();
+ eos_modem_set_mode(EOS_CELL_UART_MODE_RELAY);
+ break;
+
+ case EOS_CELL_MTYPE_UART_GIVE:
+ eos_modem_set_mode(cell_mode);
+ break;
+ }
+ break;
+
+ case EOS_CELL_MTYPE_VOICE:
+ eos_cell_voice_handler(mtype & ~EOS_CELL_MTYPE_MASK, buffer, size);
+ break;
+
+ case EOS_CELL_MTYPE_SMS:
+ eos_cell_sms_handler(mtype & ~EOS_CELL_MTYPE_MASK, buffer, size);
+ break;
+
+ case EOS_CELL_MTYPE_USSD:
+ eos_cell_ussd_handler(mtype & ~EOS_CELL_MTYPE_MASK, buffer, size);
+ break;
+
+ case EOS_CELL_MTYPE_PDP:
+ eos_cell_pdp_handler(mtype & ~EOS_CELL_MTYPE_MASK, buffer, size);
+ break;
+ }
+}
+
+static void cell_handler_task(void *pvParameters) {
+ EOSMsgItem mi;
+
+ while (1) {
+ if (xQueueReceive(cell_queue, &mi, portMAX_DELAY)) {
+ _cell_handler(mi.type, mi.buffer, mi.len);
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ eos_bufq_push(&cell_buf_q, mi.buffer);
+ xSemaphoreGive(mutex);
+ }
+ }
+ vTaskDelete(NULL);
+}
+
+static void cell_handler(unsigned char type, unsigned char *buffer, uint16_t len) {
+ EOSMsgItem mi;
+ unsigned char *buf;
+
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ buf = eos_bufq_pop(&cell_buf_q);
+ xSemaphoreGive(mutex);
+
+ if (buf == NULL) {
+ ESP_LOGE(TAG, "Cell message NOT handled: %2x", type);
+ return;
+ }
+
+ memcpy(buf, buffer, len);
+ mi.type = type;
+ mi.buffer = buf;
+ mi.len = len;
+ xQueueSend(cell_queue, &mi, portMAX_DELAY);
+}
+
+void eos_cell_init(void) {
+ int i;
+
+ eos_bufq_init(&cell_buf_q, cell_bufq_array, CELL_SIZE_QUEUE);
+ for (i=0; i<CELL_SIZE_QUEUE; i++) {
+ eos_bufq_push(&cell_buf_q, malloc(EOS_NET_SIZE_BUF));
+ }
+
+ mutex = xSemaphoreCreateBinary();
+ xSemaphoreGive(mutex);
+ cell_queue = xQueueCreate(CELL_SIZE_QUEUE, sizeof(EOSMsgItem));
+ xTaskCreate(cell_handler_task, "cell_handler", EOS_TASK_SSIZE_CELL, NULL, EOS_TASK_PRIORITY_CELL, NULL);
+
+ eos_net_set_handler(EOS_NET_MTYPE_CELL, cell_handler);
+}
+
diff --git a/fw/esp32/components/eos/cell_modem.c b/fw/esp32/components/eos/cell_modem.c
new file mode 100644
index 0000000..f5ee0a9
--- /dev/null
+++ b/fw/esp32/components/eos/cell_modem.c
@@ -0,0 +1,825 @@
+#include <stdlib.h>
+#include <string.h>
+
+#include <freertos/FreeRTOS.h>
+#include <freertos/semphr.h>
+#include <freertos/task.h>
+#include <freertos/queue.h>
+#include <netif/ppp/pppos.h>
+#include <netif/ppp/pppapi.h>
+#include <driver/uart.h>
+#include <driver/gpio.h>
+#include <esp_timer.h>
+#include <esp_sleep.h>
+#include <esp_log.h>
+
+#include "eos.h"
+#include "net.h"
+#include "power.h"
+
+#include "at_cmd.h"
+#include "cell.h"
+
+// XXX: PPP reconnect on failure
+
+#define UART_SIZE_IO_BUF 8192
+
+#define UART_GPIO_TXD 16
+#define UART_GPIO_RXD 17
+#define UART_GPIO_DTR 32
+#define UART_GPIO_RI 35
+
+#define MODEM_ETYPE_INIT 1
+#define MODEM_ETYPE_RI 2
+
+#define AT_CMD_INIT_SIZE 5
+
+#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y))
+#define MAX(X, Y) (((X) > (Y)) ? (X) : (Y))
+
+static const char *TAG = "EOS MODEM";
+
+static char *at_cmd_init[AT_CMD_INIT_SIZE] = {
+ "AT+CFGRI=1\r",
+ "AT+CSCLK=1\r",
+ "AT+CLIP=1\r",
+ "AT+CMGF=0\r",
+ "AT+CPMS=\"ME\",\"ME\",\"ME\"\r"
+};
+
+static char modem_initialized = 0;
+static SemaphoreHandle_t mutex;
+
+static QueueHandle_t modem_queue;
+static QueueHandle_t uart_queue;
+
+static char urc_buf[EOS_CELL_UART_SIZE_BUF];
+static char uart_buf[EOS_CELL_UART_SIZE_BUF];
+static size_t uart_buf_len;
+static char uart_buf_dirty = 0;
+
+static uint8_t uart_mode = EOS_CELL_UART_MODE_ATCMD;
+static uint8_t _uart_mode = EOS_CELL_UART_MODE_UNDEF;
+static SemaphoreHandle_t uart_mutex;
+
+static char ppp_apn[64];
+static char ppp_user[64];
+static char ppp_pass[64];
+static SemaphoreHandle_t ppp_mutex;
+
+static ppp_pcb *ppp_handle;
+static struct netif ppp_netif;
+
+typedef enum {
+ UART_EEVT_MODE = UART_EVENT_MAX
+} uart_eevt_type_t;
+
+typedef struct {
+ uint8_t type;
+} modem_event_t;
+
+static void modem_atcmd_read(size_t bsize);
+
+static void uart_data_read(uint8_t mode) {
+ unsigned char *buf;
+ int rd;
+ size_t bsize;
+
+ uart_get_buffered_data_len(UART_NUM_2, &bsize);
+ switch (mode) {
+ case EOS_CELL_UART_MODE_ATCMD:
+ modem_atcmd_read(bsize);
+ break;
+
+ case EOS_CELL_UART_MODE_PPP:
+ rd = 0;
+
+ do {
+ int _rd = eos_modem_read(uart_buf, MIN(bsize - rd, sizeof(uart_buf)), 100);
+ if (ppp_handle) pppos_input_tcpip(ppp_handle, (uint8_t *)uart_buf, _rd);
+ rd += _rd;
+ } while (rd != bsize);
+ break;
+
+ case EOS_CELL_UART_MODE_RELAY:
+ rd = 0;
+
+ do {
+ int _rd;
+
+ buf = eos_net_alloc();
+ buf[0] = EOS_CELL_MTYPE_DEV | EOS_CELL_MTYPE_UART_DATA;
+ _rd = eos_modem_read(buf + 1, MIN(bsize - rd, EOS_NET_MTU - 1), 100);
+ eos_net_send(EOS_NET_MTYPE_CELL, buf, _rd + 1);
+ rd += _rd;
+ } while (rd != bsize);
+ break;
+
+ default:
+ break;
+
+ }
+}
+
+static void uart_event_task(void *pvParameters) {
+ char mode = EOS_CELL_UART_MODE_ATCMD;
+ char _mode = EOS_CELL_UART_MODE_ATCMD;
+ uart_event_t event;
+
+ xSemaphoreTake(uart_mutex, portMAX_DELAY);
+ while (1) {
+ /* Waiting for UART event.
+ */
+ if (xQueueReceive(uart_queue, &event, portMAX_DELAY)) {
+ switch (event.type) {
+ case UART_DATA:
+ /* Event of UART receiving data
+ */
+ if (mode != EOS_CELL_UART_MODE_NONE) uart_data_read(mode);
+ if ((mode != _mode) && (uart_buf_len == 0)) {
+ if (_mode == EOS_CELL_UART_MODE_NONE) xSemaphoreGive(uart_mutex);
+ mode = _mode;
+ }
+ break;
+
+ case UART_EEVT_MODE:
+ /* Mode change
+ */
+ _mode = (char)event.size;
+ if ((_mode != mode) && ((uart_buf_len == 0) || (mode == EOS_CELL_UART_MODE_NONE))) {
+ if (mode == EOS_CELL_UART_MODE_NONE) {
+ xSemaphoreTake(uart_mutex, portMAX_DELAY);
+ uart_data_read(_mode);
+ }
+ if (_mode == EOS_CELL_UART_MODE_NONE) xSemaphoreGive(uart_mutex);
+ mode = _mode;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ vTaskDelete(NULL);
+}
+
+static void IRAM_ATTR uart_ri_isr_handler(void *arg) {
+ modem_event_t evt;
+
+ evt.type = MODEM_ETYPE_RI;
+ xQueueSendFromISR(modem_queue, &evt, NULL);
+}
+
+static void modem_set_mode(uint8_t mode) {
+ uart_event_t evt;
+
+ evt.type = UART_EEVT_MODE;
+ evt.size = mode;
+ xQueueSend(uart_queue, &evt, portMAX_DELAY);
+}
+
+static int modem_atcmd_init(void) {
+ unsigned char *buf;
+ int echo_on = 0;
+ int tries = 3;
+ int i, r;
+
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ modem_set_mode(EOS_CELL_UART_MODE_NONE);
+ r = xSemaphoreTake(uart_mutex, 1000);
+ if (r == pdFALSE) {
+ modem_set_mode(uart_mode);
+ xSemaphoreGive(mutex);
+ return EOS_ERR_TIMEOUT;
+ }
+
+ do {
+ at_cmd("AT\r");
+ r = at_expect("^AT", "^OK", 1000);
+ if (r >= 0) {
+ echo_on = r;
+ if (echo_on) {
+ r = at_expect("^OK", NULL, 1000);
+ }
+ break;
+ }
+ tries--;
+ } while (tries);
+
+ if (tries == 0) {
+ modem_set_mode(uart_mode);
+ xSemaphoreGive(uart_mutex);
+ xSemaphoreGive(mutex);
+
+ return EOS_ERR_TIMEOUT;
+ }
+
+ if (echo_on) {
+ at_cmd("AT&F\r");
+ r = at_expect("^AT&F", NULL, 1000);
+ r = at_expect("^OK", NULL, 1000);
+ } else {
+ at_cmd("AT&F\r");
+ r = at_expect("^OK", NULL, 1000);
+ }
+ at_cmd("ATE0\r");
+ r = at_expect("^ATE0", NULL, 1000);
+ r = at_expect("^OK", "^ERROR", 1000);
+
+ for (i=0; i<AT_CMD_INIT_SIZE; i++) {
+ at_cmd(at_cmd_init[i]);
+ r = at_expect("^OK", "^ERROR", 1000);
+ }
+
+ buf = eos_net_alloc();
+ buf[0] = EOS_CELL_MTYPE_DEV | EOS_CELL_MTYPE_READY;
+ eos_net_send(EOS_NET_MTYPE_CELL, buf, 1);
+
+ modem_initialized = 1;
+ modem_set_mode(uart_mode);
+ xSemaphoreGive(uart_mutex);
+ xSemaphoreGive(mutex);
+
+ return EOS_OK;
+}
+
+
+static void modem_atcmd_read(size_t bsize) {
+ char *ln_end;
+ int rd = 0;
+
+ do {
+ char *uart_curr = uart_buf + uart_buf_len;
+ int _rd = eos_modem_read(uart_curr, MIN(bsize - rd, sizeof(uart_buf) - uart_buf_len - 1), 100);
+
+ rd += _rd;
+ uart_buf_len += _rd;
+ uart_buf[uart_buf_len] = '\0';
+ while ((ln_end = strchr(uart_curr, '\n'))) {
+ char *_ln_end = ln_end;
+
+ while ((_ln_end > uart_buf) && (*(_ln_end - 1) == '\r')) _ln_end--;
+ memcpy(urc_buf, uart_buf, _ln_end - uart_buf);
+ urc_buf[_ln_end - uart_buf] = '\0';
+
+ uart_buf_len -= ln_end - uart_buf + 1;
+ if (uart_buf_len) memmove(uart_buf, ln_end + 1, uart_buf_len);
+ if (!uart_buf_dirty) at_urc_process(urc_buf);
+
+ uart_curr = uart_buf;
+ uart_buf[uart_buf_len] = '\0';
+ uart_buf_dirty = 0;
+ }
+ if (uart_buf_len == sizeof(uart_buf) - 1) {
+ uart_buf_len = 0;
+ uart_buf_dirty = 1;
+ }
+ } while (rd != bsize);
+}
+
+static void modem_urc_init_handler(char *urc, regmatch_t m[]) {
+ modem_event_t evt;
+
+ evt.type = MODEM_ETYPE_INIT;
+ xQueueSend(modem_queue, &evt, portMAX_DELAY);
+}
+
+static void modem_event_task(void *pvParameters) {
+ modem_event_t evt;
+
+ while (1) {
+ if (xQueueReceive(modem_queue, &evt, portMAX_DELAY)) {
+ switch (evt.type) {
+ case MODEM_ETYPE_INIT:
+ modem_atcmd_init();
+ break;
+
+ case MODEM_ETYPE_RI:
+ ESP_LOGI(TAG, "URC from RI");
+ break;
+
+ default:
+ break;
+ }
+
+ /* Obsolete!!!
+ uint64_t t_start = esp_timer_get_time();
+ if (xQueueReceive(modem_queue, &level, 200 / portTICK_RATE_MS) && (level == 1)) {
+ uint64_t t_end = esp_timer_get_time();
+ ESP_LOGI(TAG, "URC:%u", (uint32_t)(t_end - t_start));
+ } else {
+ ESP_LOGI(TAG, "RING");
+ }
+ */
+
+ }
+ }
+ vTaskDelete(NULL);
+}
+
+static char *memstr(char *mem, size_t size, char *str) {
+ size_t i = 0;
+ char *max_mem;
+
+ if (str[0] == '\0') return NULL;
+
+ max_mem = mem + size;
+
+ while (mem < max_mem) {
+ if (*mem != str[i]) {
+ mem -= i;
+ i = 0;
+ } else {
+ if (str[i+1] == '\0') return mem - i;
+ i++;
+ }
+ mem++;
+ }
+
+ return NULL;
+}
+
+static uint32_t ppp_output_cb(ppp_pcb *pcb, uint8_t *data, uint32_t len, void *ctx) {
+ size_t rv;
+
+ xSemaphoreTake(ppp_mutex, portMAX_DELAY);
+ rv = eos_modem_write(data, len);
+ xSemaphoreGive(ppp_mutex);
+
+ return rv;
+}
+
+/* PPP status callback */
+static void ppp_status_cb(ppp_pcb *pcb, int err_code, void *ctx) {
+ unsigned char *rbuf;
+ struct netif *pppif = ppp_netif(pcb);
+
+ LWIP_UNUSED_ARG(ctx);
+
+ switch(err_code) {
+ case PPPERR_NONE: {
+ ESP_LOGI(TAG, "status_cb: Connect");
+ ESP_LOGI(TAG," our_ipaddr = %s\n", ipaddr_ntoa(&pppif->ip_addr));
+ ESP_LOGI(TAG," his_ipaddr = %s\n", ipaddr_ntoa(&pppif->gw));
+ ESP_LOGI(TAG," netmask = %s\n", ipaddr_ntoa(&pppif->netmask));
+ rbuf = eos_net_alloc();
+ rbuf[0] = EOS_CELL_MTYPE_PDP | EOS_CELL_MTYPE_PDP_CONNECT;
+ rbuf[1] = EOS_OK;
+ eos_net_send(EOS_NET_MTYPE_CELL, rbuf, 2);
+ return;
+ }
+ case PPPERR_PARAM: {
+ ESP_LOGE(TAG, "status_cb: Invalid parameter");
+ break;
+ }
+ case PPPERR_OPEN: {
+ ESP_LOGE(TAG, "status_cb: Unable to open PPP session");
+ break;
+ }
+ case PPPERR_DEVICE: {
+ ESP_LOGE(TAG, "status_cb: Invalid I/O device for PPP");
+ break;
+ }
+ case PPPERR_ALLOC: {
+ ESP_LOGE(TAG, "status_cb: Unable to allocate resources");
+ break;
+ }
+ case PPPERR_USER: {
+ ESP_LOGI(TAG, "status_cb: Disconnect");
+ break;
+ }
+ case PPPERR_CONNECT: {
+ ESP_LOGE(TAG, "status_cb: Connection lost");
+ break;
+ }
+ case PPPERR_AUTHFAIL: {
+ ESP_LOGE(TAG, "status_cb: Failed authentication challenge");
+ break;
+ }
+ case PPPERR_PROTOCOL: {
+ ESP_LOGE(TAG, "status_cb: Failed to meet protocol");
+ break;
+ }
+ case PPPERR_PEERDEAD: {
+ ESP_LOGE(TAG, "status_cb: Connection timeout");
+ break;
+ }
+ case PPPERR_IDLETIMEOUT: {
+ ESP_LOGE(TAG, "status_cb: Idle Timeout");
+ break;
+ }
+ case PPPERR_CONNECTTIME: {
+ ESP_LOGE(TAG, "status_cb: Max connect time reached");
+ break;
+ }
+ case PPPERR_LOOPBACK: {
+ ESP_LOGE(TAG, "status_cb: Loopback detected");
+ break;
+ }
+ default: {
+ ESP_LOGE(TAG, "status_cb: Unknown error code %d", err_code);
+ break;
+ }
+ }
+
+ xSemaphoreTake(mutex, portMAX_DELAY);
+
+ if (_uart_mode == EOS_CELL_UART_MODE_UNDEF) _uart_mode = EOS_CELL_UART_MODE_ATCMD;
+ uart_mode = _uart_mode;
+ _uart_mode = EOS_CELL_UART_MODE_UNDEF;
+
+ modem_set_mode(EOS_CELL_UART_MODE_NONE);
+ xSemaphoreTake(uart_mutex, portMAX_DELAY);
+
+ ppp_handle = NULL;
+ modem_set_mode(uart_mode);
+
+ xSemaphoreGive(uart_mutex);
+ xSemaphoreGive(mutex);
+
+ ppp_free(pcb);
+}
+
+static int ppp_pause(uint32_t timeout) {
+ int done = 0;
+ int len = 0;
+ int rv = EOS_OK;
+ int start = 0;
+ int r;
+
+ char *ok_str = NULL;
+ uint64_t t_start;
+ uint32_t dt, _dt;
+
+ modem_set_mode(EOS_CELL_UART_MODE_NONE);
+
+ t_start = esp_timer_get_time();
+ r = xSemaphoreTake(uart_mutex, timeout ? timeout / portTICK_PERIOD_MS : portMAX_DELAY);
+ if (r == pdFALSE) return EOS_ERR_TIMEOUT;
+ if (timeout) {
+ dt = ((esp_timer_get_time() - t_start) / 1000);
+ if (dt >= timeout) {
+ modem_set_mode(EOS_CELL_UART_MODE_PPP);
+ xSemaphoreGive(uart_mutex);
+ return EOS_ERR_TIMEOUT;
+ }
+ }
+ r = xSemaphoreTake(ppp_mutex, timeout ? (timeout - dt) / portTICK_PERIOD_MS : portMAX_DELAY);
+ if (r == pdFALSE) {
+ modem_set_mode(EOS_CELL_UART_MODE_PPP);
+ xSemaphoreGive(uart_mutex);
+ return EOS_ERR_TIMEOUT;
+ }
+ eos_modem_flush();
+
+ _dt = ((esp_timer_get_time() - t_start) / 1000);
+ do {
+ len = eos_modem_read(uart_buf + uart_buf_len, sizeof(uart_buf) - uart_buf_len, 10);
+ dt = ((esp_timer_get_time() - t_start) / 1000);
+ if ((dt - _dt) >= 1000) {
+ _dt =dt;
+ at_cmd("+++");
+ start = 1;
+ }
+ if (start && (len > 0)) {
+ if (uart_buf_len > 5) {
+ ok_str = memstr(uart_buf + uart_buf_len - 5, len + 5, "\r\nOK\r\n");
+ } else {
+ ok_str = memstr(uart_buf, uart_buf_len + len, "\r\nOK\r\n");
+ }
+ uart_buf_len += len;
+ }
+ if (ok_str) {
+ if (ppp_handle) pppos_input_tcpip(ppp_handle, (uint8_t *)uart_buf, ok_str - uart_buf);
+ ok_str += 6;
+ uart_buf_len -= ok_str - uart_buf;
+ if (uart_buf_len) memmove(uart_buf, ok_str, uart_buf_len);
+ done = 1;
+ } else if (uart_buf_len == sizeof(uart_buf)) {
+ if (ppp_handle) pppos_input_tcpip(ppp_handle, (uint8_t *)uart_buf, sizeof(uart_buf) / 2);
+ memcpy(uart_buf, uart_buf + sizeof(uart_buf) / 2, sizeof(uart_buf) / 2);
+ uart_buf_len = sizeof(uart_buf) / 2;
+ }
+ if (!done && timeout && (dt >= timeout)) {
+ modem_set_mode(EOS_CELL_UART_MODE_PPP);
+ xSemaphoreGive(uart_mutex);
+ xSemaphoreGive(ppp_mutex);
+ return EOS_ERR_TIMEOUT;
+ }
+ } while (!done);
+
+ return rv;
+}
+
+static int ppp_resume(void) {
+ int r;
+ int rv = EOS_OK;
+
+ at_cmd("ATO\r");
+ r = at_expect("^CONNECT", "^(ERROR|NO CARRIER)", 1000);
+ if (r <= 0) rv = EOS_ERR;
+
+ modem_set_mode(EOS_CELL_UART_MODE_PPP);
+ xSemaphoreGive(uart_mutex);
+ xSemaphoreGive(ppp_mutex);
+
+ return rv;
+}
+
+static int ppp_setup(void) {
+ int r;
+ char cmd[64];
+ int cmd_len = snprintf(cmd, sizeof(cmd), "AT+CGDCONT=1,\"IP\",\"%s\"\r", ppp_apn);
+
+ if ((cmd_len < 0) || (cmd_len >= sizeof(cmd))) return EOS_ERR;
+
+ modem_set_mode(EOS_CELL_UART_MODE_NONE);
+ r = xSemaphoreTake(uart_mutex, 1000 / portTICK_PERIOD_MS);
+ if (r == pdFALSE) {
+ modem_set_mode(uart_mode);
+ return EOS_ERR_TIMEOUT;
+ }
+
+ at_cmd(cmd);
+ r = at_expect("^OK", "^ERROR", 1000);
+ if (r <= 0) {
+ modem_set_mode(uart_mode);
+ xSemaphoreGive(uart_mutex);
+ return EOS_ERR;
+ }
+
+ at_cmd("AT+CGDATA=\"PPP\",1\r");
+ r = at_expect("^CONNECT", "^NO CARRIER", 1000);
+ if (r <= 0) {
+ modem_set_mode(uart_mode);
+ xSemaphoreGive(uart_mutex);
+ return EOS_ERR;
+ }
+
+ ppp_handle = pppapi_pppos_create(&ppp_netif, ppp_output_cb, ppp_status_cb, NULL);
+ ppp_set_usepeerdns(ppp_handle, 1);
+ ppp_set_default(ppp_handle);
+ ppp_set_auth(ppp_handle, PPPAUTHTYPE_ANY, ppp_user, ppp_pass);
+ ppp_connect(ppp_handle, 0);
+
+ modem_set_mode(EOS_CELL_UART_MODE_PPP);
+ xSemaphoreGive(uart_mutex);
+
+ return EOS_OK;
+}
+
+void eos_modem_init(void) {
+ /* Configure parameters of an UART driver,
+ * communication pins and install the driver */
+ uart_config_t uart_config = {
+ .baud_rate = 115200,
+ .data_bits = UART_DATA_8_BITS,
+ .parity = UART_PARITY_DISABLE,
+ .stop_bits = UART_STOP_BITS_1,
+ .flow_ctrl = UART_HW_FLOWCTRL_DISABLE
+ };
+ uart_param_config(UART_NUM_2, &uart_config);
+ uart_set_pin(UART_NUM_2, UART_GPIO_TXD, UART_GPIO_RXD, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
+ uart_driver_install(UART_NUM_2, UART_SIZE_IO_BUF, UART_SIZE_IO_BUF, 10, &uart_queue, 0);
+
+ // Configuration for the DTR/RI lines
+ gpio_config_t io_conf;
+
+ io_conf.intr_type = GPIO_INTR_DISABLE;
+ io_conf.mode = GPIO_MODE_OUTPUT;
+ io_conf.pin_bit_mask = ((uint64_t)1 << UART_GPIO_DTR);
+ io_conf.pull_up_en = 0;
+ io_conf.pull_down_en = 0;
+ gpio_config(&io_conf);
+ gpio_set_level(UART_GPIO_DTR, 0);
+
+ io_conf.intr_type = GPIO_INTR_NEGEDGE;
+ io_conf.mode = GPIO_MODE_INPUT;
+ io_conf.pin_bit_mask = ((uint64_t)1 << UART_GPIO_RI);
+ io_conf.pull_up_en = 0;
+ io_conf.pull_down_en = 0;
+ gpio_config(&io_conf);
+
+ mutex = xSemaphoreCreateBinary();
+ xSemaphoreGive(mutex);
+
+ uart_mutex = xSemaphoreCreateBinary();
+ xSemaphoreGive(uart_mutex);
+
+ ppp_mutex = xSemaphoreCreateBinary();
+ xSemaphoreGive(ppp_mutex);
+
+ modem_queue = xQueueCreate(4, sizeof(modem_event_t));
+ xTaskCreate(uart_event_task, "uart_event", EOS_TASK_SSIZE_UART, NULL, EOS_TASK_PRIORITY_UART, NULL);
+ xTaskCreate(modem_event_task, "modem_event", EOS_TASK_SSIZE_MODEM, NULL, EOS_TASK_PRIORITY_MODEM, NULL);
+
+ gpio_isr_handler_add(UART_GPIO_RI, uart_ri_isr_handler, NULL);
+
+ at_init();
+ at_urc_insert("^PB DONE", modem_urc_init_handler, REG_EXTENDED);
+ eos_modem_set_mode(EOS_CELL_UART_MODE_ATCMD);
+
+ eos_cell_voice_init();
+ eos_cell_sms_init();
+ eos_cell_ussd_init();
+ ESP_LOGI(TAG, "INIT");
+}
+
+void eos_modem_flush(void){
+ uart_wait_tx_done(UART_NUM_2, portMAX_DELAY);
+}
+
+size_t eos_modem_write(void *data, size_t size) {
+ return uart_write_bytes(UART_NUM_2, (const char *)data, size);
+}
+
+size_t eos_modem_read(void *data, size_t size, uint32_t timeout) {
+ return uart_read_bytes(UART_NUM_2, (uint8_t *)data, size, timeout / portTICK_RATE_MS);
+}
+
+int eos_modem_readln(char *buf, size_t buf_size, uint32_t timeout) {
+ char *ln_end = NULL;
+ size_t buf_len = 0;
+ uint64_t t_start = esp_timer_get_time();
+
+ buf[0] = '\0';
+ if (uart_buf_len) {
+ buf_len = MIN(buf_size -1, uart_buf_len);
+ memcpy(buf, uart_buf, buf_len);
+ buf[buf_len] = '\0';
+ ln_end = strchr(buf, '\n');
+
+ uart_buf_len -= buf_len;
+ if (uart_buf_len) memmove(uart_buf, uart_buf + buf_len, uart_buf_len);
+ }
+
+ while (ln_end == NULL) {
+ int rv = EOS_OK;
+ int len;
+
+ if (buf_len == buf_size - 1) rv = EOS_ERR_FULL;
+ if (!rv && timeout && ((uint32_t)((esp_timer_get_time() - t_start) / 1000) > timeout)) rv = EOS_ERR_TIMEOUT;
+ if (rv) {
+ uart_buf_dirty = 1;
+ return rv;
+ }
+
+ len = eos_modem_read(buf + buf_len, MIN(buf_size - buf_len - 1, sizeof(uart_buf) - uart_buf_len), 10);
+ if (len > 0) {
+ buf[buf_len + len] = '\0';
+ ln_end = strchr(buf + buf_len, '\n');
+ buf_len += len;
+ }
+ }
+ buf_len -= ln_end - buf + 1;
+ if (buf_len) {
+ if (uart_buf_len) memmove(uart_buf + buf_len, uart_buf, uart_buf_len);
+ memcpy(uart_buf, ln_end + 1, buf_len);
+ uart_buf_len += buf_len;
+ }
+
+ while ((ln_end > buf) && (*(ln_end - 1) == '\r')) ln_end--;
+ *ln_end = '\0';
+
+ return EOS_OK;
+}
+
+uint8_t eos_modem_get_mode(void) {
+ uint8_t ret;
+
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ ret = uart_mode;
+ xSemaphoreGive(mutex);
+
+ return ret;
+}
+
+int eos_modem_set_mode(uint8_t mode) {
+ int rv = EOS_OK;
+
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ if (mode != uart_mode) {
+ if ((uart_mode == EOS_CELL_UART_MODE_PPP) && ppp_handle) {
+ _uart_mode = mode;
+ pppapi_close(ppp_handle, 0);
+ } else {
+ if (mode == EOS_CELL_UART_MODE_PPP) {
+ rv = ppp_setup();
+ } else {
+ modem_set_mode(mode);
+ }
+ if (!rv) uart_mode = mode;
+ }
+ }
+ xSemaphoreGive(mutex);
+
+ return rv;
+}
+
+int eos_modem_take(uint32_t timeout) {
+ int rv = EOS_OK;
+
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ if (!modem_initialized) rv = EOS_ERR_BUSY;
+ if (!rv) {
+ if (uart_mode == EOS_CELL_UART_MODE_PPP) {
+ rv = ppp_pause(timeout);
+ } else {
+ int r;
+
+ modem_set_mode(EOS_CELL_UART_MODE_NONE);
+ r = xSemaphoreTake(uart_mutex, timeout ? timeout / portTICK_PERIOD_MS : portMAX_DELAY);
+ if (r == pdFALSE) {
+ modem_set_mode(uart_mode);
+ rv = EOS_ERR_TIMEOUT;
+ }
+ }
+ }
+ if (rv) xSemaphoreGive(mutex);
+
+ return rv;
+}
+
+void eos_modem_give(void) {
+ if (uart_mode == EOS_CELL_UART_MODE_PPP) {
+ int rv = ppp_resume();
+ if (rv) ESP_LOGW(TAG, "PPP resume failed");
+ } else {
+ modem_set_mode(uart_mode);
+ xSemaphoreGive(uart_mutex);
+ }
+ xSemaphoreGive(mutex);
+}
+
+void eos_modem_sleep(uint8_t mode) {
+ int r;
+
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ modem_set_mode(EOS_CELL_UART_MODE_NONE);
+ r = xSemaphoreTake(uart_mutex, 1000 / portTICK_PERIOD_MS);
+ if (r == pdFALSE) {
+ ESP_LOGE(TAG, "Obtaining mutex before sleep failed");
+ }
+ gpio_set_level(UART_GPIO_DTR, 1);
+ if (mode == EOS_PWR_SMODE_DEEP) {
+ gpio_hold_en(UART_GPIO_DTR);
+ }
+}
+
+void eos_modem_wake(uint8_t source, uint8_t mode) {
+ if (source == EOS_PWR_WAKE_UART) {
+ modem_event_t evt;
+
+ evt.type = MODEM_ETYPE_RI;
+ xQueueSend(modem_queue, &evt, portMAX_DELAY);
+ }
+
+ if (mode != EOS_PWR_SMODE_DEEP) {
+ gpio_set_intr_type(UART_GPIO_RI, GPIO_INTR_NEGEDGE);
+ gpio_isr_handler_add(UART_GPIO_RI, uart_ri_isr_handler, NULL);
+ gpio_set_level(UART_GPIO_DTR, 0);
+
+ modem_set_mode(uart_mode);
+ xSemaphoreGive(uart_mutex);
+ xSemaphoreGive(mutex);
+ } else {
+ gpio_hold_dis(UART_GPIO_DTR);
+ }
+}
+
+int eos_modem_reset(void) {
+ int rv;
+
+ rv = eos_modem_take(1000);
+ if (rv) return rv;
+
+ at_cmd("AT+CRESET\r");
+ at_expect("^OK", NULL, 1000);
+ uart_mode = EOS_CELL_UART_MODE_ATCMD;
+ modem_initialized = 0;
+ eos_modem_give();
+
+ return EOS_OK;
+}
+
+void eos_ppp_set_apn(char *apn) {
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ strncpy(ppp_apn, apn, sizeof(ppp_apn) - 1);
+ xSemaphoreGive(mutex);
+}
+
+void eos_ppp_set_auth(char *user, char *pass) {
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ strncpy(ppp_user, user, sizeof(ppp_user) - 1);
+ strncpy(ppp_pass, pass, sizeof(ppp_pass) - 1);
+ xSemaphoreGive(mutex);
+}
+
+int eos_ppp_connect(void) {
+ return eos_modem_set_mode(EOS_CELL_UART_MODE_PPP);
+}
+
+void eos_ppp_disconnect(void) {
+ eos_modem_set_mode(EOS_CELL_UART_MODE_ATCMD);
+}
diff --git a/fw/esp32/components/eos/cell_pcm.c b/fw/esp32/components/eos/cell_pcm.c
new file mode 100644
index 0000000..a022aab
--- /dev/null
+++ b/fw/esp32/components/eos/cell_pcm.c
@@ -0,0 +1,275 @@
+#include <stdlib.h>
+#include <string.h>
+
+#include <freertos/FreeRTOS.h>
+#include <freertos/semphr.h>
+#include <freertos/task.h>
+#include <freertos/queue.h>
+#include <driver/i2s.h>
+#include <driver/gpio.h>
+#include <esp_log.h>
+
+#include "eos.h"
+#include "net.h"
+#include "msgq.h"
+#include "cell.h"
+
+#define PCM_MIC_WM 128
+#define PCM_HOLD_CNT_TX 3
+#define PCM_HOLD_CNT_RX 3
+#define PCM_SIZE_BUFQ 4
+#define PCM_SIZE_BUF (PCM_MIC_WM * 4)
+
+#define PCM_GPIO_BCK 33
+#define PCM_GPIO_WS 4
+#define PCM_GPIO_DIN 34
+#define PCM_GPIO_DOUT 2
+
+#define PCM_ETYPE_WRITE 1
+
+static EOSBufQ pcm_buf_q;
+static unsigned char *pcm_bufq_array[PCM_SIZE_BUFQ];
+
+static EOSMsgQ pcm_evt_q;
+static EOSMsgItem pcm_evtq_array[PCM_SIZE_BUFQ];
+static char pcm_hold_tx;
+static char pcm_active = 0;
+
+static i2s_dev_t* I2S[I2S_NUM_MAX] = {&I2S0, &I2S1};
+
+static QueueHandle_t i2s_queue;
+static SemaphoreHandle_t mutex;
+
+static const char *TAG = "EOS PCM";
+
+static void i2s_event_task(void *pvParameters) {
+ i2s_event_t event;
+ unsigned char *buf;
+ unsigned char _type;
+ size_t bytes_w;
+ ssize_t bytes_r;
+ uint16_t bytes_e;
+ ssize_t hold_bytes_r = 0;
+ unsigned char *hold_buf = NULL;
+ char hold_cnt = 0;
+
+ while (1) {
+ // Waiting for I2S event.
+ if (xQueueReceive(i2s_queue, &event, portMAX_DELAY)) {
+ switch (event.type) {
+ case I2S_EVENT_RX_DONE:
+ // Event of I2S receiving data
+ if (!hold_cnt) {
+ buf = eos_net_alloc();
+ buf[0] = EOS_CELL_MTYPE_VOICE | EOS_CELL_MTYPE_VOICE_PCM;
+ bytes_r = eos_cell_pcm_read(buf + 1, PCM_MIC_WM);
+ eos_net_send(EOS_NET_MTYPE_CELL, buf, bytes_r + 1);
+ } else {
+ hold_cnt--;
+ if (hold_buf == NULL) {
+ hold_buf = eos_net_alloc();
+ hold_buf[0] = EOS_CELL_MTYPE_VOICE | EOS_CELL_MTYPE_VOICE_PCM;
+ }
+ if (1 + hold_bytes_r + PCM_MIC_WM <= EOS_NET_MTU) hold_bytes_r += eos_cell_pcm_read(hold_buf + 1 + hold_bytes_r, PCM_MIC_WM);
+ if (hold_cnt == 0) {
+ eos_net_send(EOS_NET_MTYPE_CELL, hold_buf, hold_bytes_r + 1);
+ hold_bytes_r = 0;
+ hold_buf = NULL;
+ }
+ }
+
+ buf = NULL;
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ if (pcm_hold_tx && (eos_msgq_len(&pcm_evt_q) == PCM_HOLD_CNT_TX)) pcm_hold_tx = 0;
+ if (!pcm_hold_tx) eos_msgq_pop(&pcm_evt_q, &_type, &buf, &bytes_e);
+ xSemaphoreGive(mutex);
+
+ if (buf) {
+ i2s_write(I2S_NUM_0, (const void *)buf, bytes_e, &bytes_w, portMAX_DELAY);
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ eos_bufq_push(&pcm_buf_q, buf);
+ xSemaphoreGive(mutex);
+ }
+ break;
+ case I2S_EVENT_DMA_ERROR:
+ ESP_LOGE(TAG, "*** I2S DMA ERROR ***");
+ break;
+ case I2S_EVENT_MAX:
+ hold_cnt = PCM_HOLD_CNT_RX;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ vTaskDelete(NULL);
+}
+
+void eos_cell_pcm_init(void) {
+ int i;
+
+ i2s_config_t i2s_config = {
+ .mode = I2S_MODE_SLAVE | I2S_MODE_TX | I2S_MODE_RX,
+ .sample_rate = 32000,
+ .bits_per_sample = 32,
+ .channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
+ .communication_format = I2S_COMM_FORMAT_STAND_I2S,
+ .dma_buf_count = 4,
+ .dma_buf_len = PCM_MIC_WM,
+ .use_apll = true,
+ .fixed_mclk = 2048000 * 8,
+ .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1
+ };
+
+ i2s_pin_config_t pin_config = {
+ .bck_io_num = PCM_GPIO_BCK,
+ .ws_io_num = PCM_GPIO_WS,
+ .data_in_num = PCM_GPIO_DIN,
+ .data_out_num = PCM_GPIO_DOUT
+ };
+ i2s_driver_install(I2S_NUM_0, &i2s_config, 10, &i2s_queue); //install and start i2s driver
+ i2s_stop(I2S_NUM_0);
+ i2s_set_pin(I2S_NUM_0, &pin_config);
+ gpio_matrix_in(pin_config.ws_io_num, I2S0I_WS_IN_IDX, 1);
+ gpio_matrix_in(pin_config.bck_io_num, I2S0I_BCK_IN_IDX, 1);
+ ESP_LOGI(TAG, "TX FIFO:%d TX CHAN:%d RX FIFO:%d RX CHAN:%d", I2S[I2S_NUM_0]->fifo_conf.tx_fifo_mod, I2S[I2S_NUM_0]->conf_chan.tx_chan_mod, I2S[I2S_NUM_0]->fifo_conf.rx_fifo_mod, I2S[I2S_NUM_0]->conf_chan.rx_chan_mod);
+
+ I2S[I2S_NUM_0]->fifo_conf.tx_fifo_mod = 2;
+ I2S[I2S_NUM_0]->conf_chan.tx_chan_mod = 0;
+
+ I2S[I2S_NUM_0]->fifo_conf.rx_fifo_mod = 3;
+ I2S[I2S_NUM_0]->conf_chan.rx_chan_mod = 1;
+ // I2S[I2S_NUM_0]->conf.tx_mono = 1;
+ I2S[I2S_NUM_0]->conf.rx_mono = 1;
+ // I2S[I2S_NUM_0]->timing.tx_dsync_sw = 1
+ // I2S[I2S_NUM_0]->timing.rx_dsync_sw = 1
+ // I2S[I2S_NUM_0]->conf.sig_loopback = 0;
+
+ // I2S[I2S_NUM_0]->timing.tx_bck_in_inv = 1;
+
+ eos_msgq_init(&pcm_evt_q, pcm_evtq_array, PCM_SIZE_BUFQ);
+ eos_bufq_init(&pcm_buf_q, pcm_bufq_array, PCM_SIZE_BUFQ);
+ for (i=0; i<PCM_SIZE_BUFQ; i++) {
+ eos_bufq_push(&pcm_buf_q, malloc(PCM_SIZE_BUF));
+ }
+
+ mutex = xSemaphoreCreateBinary();
+ xSemaphoreGive(mutex);
+
+ // Create a task to handle i2s event from ISR
+ xTaskCreate(i2s_event_task, "i2s_event", EOS_TASK_SSIZE_I2S, NULL, EOS_TASK_PRIORITY_I2S, NULL);
+ ESP_LOGI(TAG, "INIT");
+}
+
+ssize_t eos_cell_pcm_read(unsigned char *data, size_t size) {
+ static unsigned char buf[PCM_SIZE_BUF];
+ size_t bytes_r;
+ int i;
+
+ if (size > PCM_MIC_WM) return EOS_ERR;
+
+ esp_err_t ret = i2s_read(I2S_NUM_0, (void *)buf, size * 4, &bytes_r, portMAX_DELAY);
+ if (ret != ESP_OK) return EOS_ERR;
+
+ for (i=0; i<size/2; i++) {
+ data[i * 2] = buf[i * 8 + 3];
+ data[i * 2 + 1] = buf[i * 8 + 2];
+ }
+
+ return bytes_r / 4;
+}
+
+static ssize_t pcm_expand(unsigned char *buf, unsigned char *data, size_t size) {
+ int i;
+
+ if (size > PCM_MIC_WM) return EOS_ERR;
+
+ memset(buf, 0, PCM_SIZE_BUF);
+ for (i=0; i<size/2; i++) {
+ buf[i * 8 + 3] = data[i * 2];
+ buf[i * 8 + 2] = data[i * 2 + 1];
+ }
+
+ return size * 4;
+}
+
+int eos_cell_pcm_push(unsigned char *data, size_t size) {
+ unsigned char *buf = NULL;
+ ssize_t esize;
+ int rv;
+
+ if (size > PCM_MIC_WM) return EOS_ERR;
+
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ if (!pcm_active) {
+ xSemaphoreGive(mutex);
+ return EOS_ERR;
+ }
+ if (pcm_hold_tx && (eos_msgq_len(&pcm_evt_q) == PCM_HOLD_CNT_TX)) {
+ unsigned char _type;
+ uint16_t _len;
+
+ eos_msgq_pop(&pcm_evt_q, &_type, &buf, &_len);
+ } else {
+ buf = eos_bufq_pop(&pcm_buf_q);
+ }
+ xSemaphoreGive(mutex);
+
+ if (buf == NULL) return EOS_ERR_EMPTY;
+
+ esize = pcm_expand(buf, data, size);
+ if (esize < 0) {
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ eos_bufq_push(&pcm_buf_q, buf);
+ xSemaphoreGive(mutex);
+ return esize;
+ }
+
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ rv = eos_msgq_push(&pcm_evt_q, PCM_ETYPE_WRITE, buf, esize);
+ if (rv) eos_bufq_push(&pcm_buf_q, buf);
+ xSemaphoreGive(mutex);
+
+ return rv;
+}
+
+void eos_cell_pcm_start(void) {
+ i2s_event_t evt;
+
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ if (pcm_active) {
+ xSemaphoreGive(mutex);
+ return;
+ }
+ while (1) {
+ unsigned char _type;
+ unsigned char *buf;
+ uint16_t len;
+
+ eos_msgq_pop(&pcm_evt_q, &_type, &buf, &len);
+ if (buf) {
+ eos_bufq_push(&pcm_buf_q, buf);
+ } else {
+ break;
+ }
+ }
+ pcm_active = 1;
+ pcm_hold_tx = 1;
+ xSemaphoreGive(mutex);
+
+ evt.type = I2S_EVENT_MAX; /* my type */
+ xQueueSend(i2s_queue, &evt, portMAX_DELAY);
+ i2s_zero_dma_buffer(I2S_NUM_0);
+ i2s_start(I2S_NUM_0);
+}
+
+void eos_cell_pcm_stop(void) {
+ char active;
+
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ active = pcm_active;
+ pcm_active = 0;
+ xSemaphoreGive(mutex);
+
+ if (active) i2s_stop(I2S_NUM_0);
+}
diff --git a/fw/esp32/components/eos/cell_pdp.c b/fw/esp32/components/eos/cell_pdp.c
new file mode 100644
index 0000000..1aa0575
--- /dev/null
+++ b/fw/esp32/components/eos/cell_pdp.c
@@ -0,0 +1,31 @@
+#include <stdlib.h>
+#include <string.h>
+
+#include <esp_log.h>
+
+#include "eos.h"
+#include "cell.h"
+
+void eos_cell_pdp_handler(unsigned char mtype, unsigned char *buffer, uint16_t size) {
+ char *apn, *user, *pass;
+
+ buffer += 1;
+ size -= 1;
+ switch (mtype) {
+ case EOS_CELL_MTYPE_PDP_CONFIG:
+ apn = (char *)buffer;
+ user = apn + strlen(apn) + 1;
+ pass = user + strlen(user) + 1;
+ eos_ppp_set_apn(apn);
+ eos_ppp_set_auth(user, pass);
+ break;
+
+ case EOS_CELL_MTYPE_PDP_CONNECT:
+ eos_ppp_connect();
+ break;
+
+ case EOS_CELL_MTYPE_PDP_DISCONNECT:
+ eos_ppp_disconnect();
+ break;
+ }
+}
diff --git a/fw/esp32/components/eos/cell_sms.c b/fw/esp32/components/eos/cell_sms.c
new file mode 100644
index 0000000..92c016e
--- /dev/null
+++ b/fw/esp32/components/eos/cell_sms.c
@@ -0,0 +1,276 @@
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <esp_log.h>
+
+#include "eos.h"
+#include "net.h"
+#include "unicode.h"
+#include "gsm.h"
+#include "at_cmd.h"
+#include "cell.h"
+
+#define CTRL_Z 0x1a
+
+static const char *TAG = "EOS SMS";
+
+static char cmd[256];
+
+static char pdu[4096];
+static int pdu_len;
+
+static uint8_t udh[GSM_UDH_SIZE];
+static int udh_len;
+
+static uint8_t msg[GSM_MSG_SIZE];
+static int msg_len;
+static uint8_t msg_enc;
+
+static char orig_addr[GSM_ADDR_SIZE];
+static int orig_addr_len;
+static uint8_t orig_addr_type;
+
+static char smsc_addr[GSM_ADDR_SIZE];
+static int smsc_addr_len;
+static uint8_t smsc_addr_type;
+
+static char smsc_timestamp[GSM_TS_SIZE];
+
+uint16_t flags;
+
+static int sms_decode(unsigned char *buf, uint16_t *_len) {
+ int i, j, rv;
+ uint16_t len = 0;
+ uint8_t smsc_info, smsc_info_len;
+
+ if (pdu_len < 2) return GSM_ERR_SIZE;
+ smsc_info = pdu_getc(pdu);
+ smsc_info_len = 2 * (smsc_info + 1);
+ if (pdu_len < smsc_info_len) return GSM_ERR_SIZE;
+
+ if (smsc_info > 1) {
+ pdu_putc((smsc_info - 1) * 2, pdu);
+ rv = gsm_addr_dec(pdu, pdu_len, smsc_addr, &smsc_addr_len, &smsc_addr_type);
+ if (rv < 0) smsc_addr_len = 0;
+ }
+
+ rv = gsm_sms_dec(pdu + smsc_info_len, pdu_len - smsc_info_len, orig_addr, &orig_addr_len, &orig_addr_type, udh, &udh_len, msg, &msg_len, smsc_timestamp, &msg_enc, &flags);
+ if (rv == GSM_ERR_NOT_SUPPORTED) ESP_LOGE(TAG, "Message not supported: %s", pdu);
+ if (rv < 0) return rv;
+ if (msg_enc == GSM_ENC_8BIT) return EOS_ERR;
+
+ buf[0] = flags >> 8;
+ buf[1] = flags;
+ len += 2;
+
+ memcpy(buf + len, smsc_timestamp, GSM_TS_SIZE);
+ len += GSM_TS_SIZE;
+
+ if ((orig_addr_type & GSM_TON) == GSM_TON_ALPHANUMERIC) {
+ buf[len] = EOS_CELL_SMS_ADDRTYPE_ALPHA;
+ buf[len + 1] = 0;
+ len += 2;
+
+ i = 0;
+ j = 0;
+ while (i < orig_addr_len) {
+ uint16_t ch;
+
+ rv = gsm_7bit_to_ucs2((char *)orig_addr + i, orig_addr_len - i, &ch);
+ if (rv < 0) return EOS_ERR;
+ i += rv;
+ rv = utf8_enc(ch, buf + len + j);
+ if (rv < 0) return EOS_ERR;
+ j += rv;
+ }
+ buf[len - 1] = j;
+ len += j;
+ } else {
+ buf[len] = ((orig_addr_type & GSM_TON) == GSM_TON_INTERNATIONAL) ? EOS_CELL_SMS_ADDRTYPE_INTL : EOS_CELL_SMS_ADDRTYPE_OTHER;
+ buf[len + 1] = orig_addr_len;
+ len += 2;
+ memcpy(buf + len, orig_addr, orig_addr_len);
+ len += orig_addr_len;
+ }
+
+ i = 0;
+ j = 0;
+ while (i < msg_len) {
+ utf32_t ch;
+
+ if (msg_enc == GSM_ENC_7BIT) {
+ uint16_t _ch;
+
+ rv = gsm_7bit_to_ucs2((char *)msg + i, msg_len - i, &_ch);
+ ch = _ch;
+ } else {
+ if (((msg_len - i) < 4) && (utf16_len(msg + i) > 2)) {
+ rv = EOS_ERR;
+ } else {
+ rv = utf16_dec(msg + i, &ch);
+ }
+ }
+ if (rv < 0) return EOS_ERR;
+ i += rv;
+
+ rv = utf8_enc(ch, buf + len + j);
+ if (rv < 0) return EOS_ERR;
+ j += rv;
+ }
+ buf[len + j] = '\0';
+ len += j + 1;
+ *_len = len;
+
+ return EOS_OK;
+}
+
+static int sms_encode(unsigned char *buffer, uint16_t size) {
+ utf32_t ch;
+ int i, rv;
+ char *addr;
+ uint8_t addr_type;
+ int addr_len;
+
+ if (size == 0) return EOS_ERR;
+
+ flags = buffer[0] << 8;
+ flags |= buffer[1];
+ buffer += 2;
+ size -= 2;
+
+ if (size < 2) return EOS_ERR;
+ switch(buffer[0]) {
+ case EOS_CELL_SMS_ADDRTYPE_INTL:
+ addr_type = GSM_EXT | GSM_TON_INTERNATIONAL | GSM_NPI_TELEPHONE;
+ break;
+
+ case EOS_CELL_SMS_ADDRTYPE_OTHER:
+ addr_type = GSM_EXT | GSM_TON_UNKNOWN | GSM_NPI_TELEPHONE;
+ break;
+
+ default: return EOS_ERR;
+ }
+ addr_len = buffer[1];
+ addr = (char *)buffer + 2;
+
+ if (size < 2 + addr_len) return EOS_ERR;
+ buffer += 2 + addr_len;
+ size -= 2 + addr_len;
+
+ i = 0;
+ msg_len = 0;
+ while (i < size) {
+ rv = utf8_dec(buffer + i, &ch);
+ if (rv < 0) return EOS_ERR;
+ if (ch >= 0xffff) return EOS_ERR;
+ i += rv;
+
+ rv = gsm_ucs2_to_7bit(ch, (char *)msg + msg_len, sizeof(msg) - msg_len);
+ if (rv < 0) return EOS_ERR;
+ msg_len += rv;
+ }
+
+ pdu_putc(0, pdu);
+ rv = gsm_sms_enc(addr, addr_len, addr_type, NULL, 0, msg, msg_len, GSM_ENC_7BIT, flags, pdu + 2, sizeof(pdu) - 4);
+ if (rv < 0) return EOS_ERR;
+ pdu_len = rv;
+ pdu[pdu_len + 2] = CTRL_Z;
+ pdu[pdu_len + 3] = '\0';
+
+ return EOS_OK;
+}
+
+void eos_cell_sms_handler(unsigned char mtype, unsigned char *buffer, uint16_t size) {
+ int rv;
+ char b[4];
+
+ buffer += 1;
+ size -= 1;
+ switch (mtype) {
+ case EOS_CELL_MTYPE_SMS_LIST:
+ if (size == 0) return;
+ snprintf(cmd, sizeof(cmd), "AT+CMGL=%d\r", buffer[0]);
+
+ rv = eos_modem_take(1000);
+ if (rv) return;
+ at_cmd(cmd);
+ do {
+ unsigned char *buf;
+ uint16_t len;
+
+ rv = at_expect("^\\+CMGL: [0-9]+,[0-9],.*,[0-9]+$", "^OK", 1000);
+ if (rv != 1) break;
+
+ rv = eos_modem_readln(pdu, sizeof(pdu), 1000);
+ if (rv) break;
+
+ pdu_len = strlen(pdu);
+
+ buf = eos_net_alloc();
+ buf[0] = EOS_CELL_MTYPE_SMS | EOS_CELL_MTYPE_SMS_MSG_ITEM;
+ rv = sms_decode(buf + 1, &len);
+ if (rv) {
+ eos_net_free(buf);
+ } else {
+ eos_net_send(EOS_NET_MTYPE_CELL, buf, len + 1);
+ }
+ } while (1);
+ eos_modem_give();
+
+ break;
+
+ case EOS_CELL_MTYPE_SMS_SEND:
+ rv = sms_encode(buffer, size);
+ if (rv) return;
+
+ snprintf(cmd, sizeof(cmd), "AT+CMGS=%d\r", pdu_len / 2);
+
+ rv = eos_modem_take(1000);
+ if (rv) return;
+ at_cmd(cmd);
+ // wait for: '> ' (0d 0a 3e 20)
+ eos_modem_read(b, 4, 1000);
+ at_cmd(pdu);
+ rv = at_expect("^\\+CMGS: [0-9]+", "^ERROR", 5000);
+ if (rv == 1) rv = at_expect("^OK", "^ERROR", 1000);
+ eos_modem_give();
+
+ break;
+ }
+}
+
+static void sms_received_handler(char *urc, regmatch_t m[]) {
+ int ref, rv;
+
+ sscanf(urc + m[1].rm_so, "%d", &ref);
+
+ snprintf(cmd, sizeof(cmd), "AT+CMGR=%d\r", ref);
+ at_cmd(cmd);
+
+ rv = at_expect("^\\+CMGR: [0-9],.*,[0-9]+$", "^ERROR", 1000);
+ if (rv == 1) {
+ unsigned char *buf;
+ uint16_t len;
+
+ rv = eos_modem_readln(pdu, sizeof(pdu), 1000);
+ if (rv) return;
+ pdu_len = strlen(pdu);
+
+ rv = at_expect("^OK", NULL, 1000);
+
+ buf = eos_net_alloc();
+ buf[0] = EOS_CELL_MTYPE_SMS | EOS_CELL_MTYPE_SMS_MSG_NEW;
+ rv = sms_decode(buf + 1, &len);
+ if (rv) {
+ eos_net_free(buf);
+ } else {
+ len++;
+ eos_net_send(EOS_NET_MTYPE_CELL, buf, len);
+ }
+ }
+}
+
+void eos_cell_sms_init(void) {
+ at_urc_insert("^\\+CMTI: .*,([0-9]+)$", sms_received_handler, REG_EXTENDED);
+}
diff --git a/fw/esp32/components/eos/cell_ussd.c b/fw/esp32/components/eos/cell_ussd.c
new file mode 100644
index 0000000..195feaf
--- /dev/null
+++ b/fw/esp32/components/eos/cell_ussd.c
@@ -0,0 +1,103 @@
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <esp_log.h>
+
+#include "eos.h"
+#include "net.h"
+#include "at_cmd.h"
+#include "cell.h"
+
+static const char *TAG = "EOS USSD";
+
+static char cmd[256];
+static int cmd_len;
+
+void eos_cell_ussd_handler(unsigned char mtype, unsigned char *buffer, uint16_t size) {
+ int rv;
+
+ buffer += 1;
+ size -= 1;
+ switch (mtype) {
+ case EOS_CELL_MTYPE_USSD_REQUEST:
+ if (size == 0) return;
+
+ buffer[size] = '\0';
+ cmd_len = snprintf(cmd, sizeof(cmd), "AT+CUSD=1,\"%s\",15\r", buffer);
+ if ((cmd_len < 0) || (cmd_len >= sizeof(cmd))) return;
+
+ rv = eos_modem_take(1000);
+ if (rv) return;
+
+ at_cmd(cmd);
+ rv = at_expect("^OK", "^ERROR", 1000);
+
+ eos_modem_give();
+ break;
+
+ case EOS_CELL_MTYPE_USSD_CANCEL:
+ rv = eos_modem_take(1000);
+ if (rv) return;
+
+ at_cmd("AT+CUSD=2\r");
+ rv = at_expect("^OK", "^ERROR", 1000);
+
+ eos_modem_give();
+ break;
+ }
+
+}
+
+static void ussd_reply_handler(char *urc, regmatch_t m[]) {
+ int rep, rv;
+ unsigned char *buf;
+ uint16_t len;
+ char *_buf;
+ size_t _len;
+ regex_t re;
+ regmatch_t match[2];
+
+ rv = regcomp(&re, ".*(\",[0-9]+)$", REG_EXTENDED);
+ if (rv) return;
+
+ sscanf(urc + m[1].rm_so, "%1d", &rep);
+
+ buf = eos_net_alloc();
+ buf[0] = EOS_CELL_MTYPE_USSD | EOS_CELL_MTYPE_USSD_REPLY;
+ buf[1] = rep;
+ len = 2;
+
+ rv = EOS_OK;
+ _buf = (char *)buf + len;
+ strcpy(_buf, urc + m[2].rm_so);
+ do {
+ if (regexec(&re, _buf, 2, match, 0) == 0) {
+ ESP_LOGI(TAG, "MATCH:%ld %s", match[1].rm_so, _buf);
+ _buf[match[1].rm_so] = '\0';
+ _len = strlen(_buf);
+ len += _len + 1;
+ break;
+ } else {
+ _len = strlen(_buf);
+ _buf[_len] = '\n';
+ _buf += _len + 1;
+ len += _len + 1;
+ }
+ rv = eos_modem_readln(_buf, EOS_NET_MTU - len, 1000);
+ if (rv) break;
+ } while (1);
+
+ if (rv) {
+ ESP_LOGE(TAG, "USSD error");
+ eos_net_free(buf);
+ } else {
+ eos_net_send(EOS_NET_MTYPE_CELL, buf, len);
+ }
+ regfree(&re);
+}
+
+void eos_cell_ussd_init(void) {
+ at_urc_insert("\\+CUSD: ([0-9]),\"(.*)", ussd_reply_handler, REG_EXTENDED);
+
+} \ No newline at end of file
diff --git a/fw/esp32/components/eos/cell_voice.c b/fw/esp32/components/eos/cell_voice.c
new file mode 100644
index 0000000..f0655bd
--- /dev/null
+++ b/fw/esp32/components/eos/cell_voice.c
@@ -0,0 +1,129 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <esp_timer.h>
+#include <esp_log.h>
+
+#include "eos.h"
+#include "net.h"
+#include "at_cmd.h"
+#include "cell.h"
+
+static const char *TAG = "EOS VOICE";
+
+static char cmd[256];
+static int cmd_len;
+
+void eos_cell_voice_handler(unsigned char mtype, unsigned char *buffer, uint16_t size) {
+ int rv;
+
+ buffer += 1;
+ size -= 1;
+ switch (mtype) {
+ case EOS_CELL_MTYPE_VOICE_DIAL:
+ if (size == 0) return;
+
+ buffer[size] = '\0';
+ cmd_len = snprintf(cmd, sizeof(cmd), "ATD%s;\r", buffer);
+ if ((cmd_len < 0) || (cmd_len >= sizeof(cmd))) return;
+
+ rv = eos_modem_take(1000);
+ if (rv) return;
+ at_cmd(cmd);
+ rv = at_expect("^OK", "^ERROR", 1000);
+ eos_modem_give();
+
+ eos_cell_pcm_start();
+ break;
+
+ case EOS_CELL_MTYPE_VOICE_ANSWER:
+ rv = eos_modem_take(1000);
+ if (rv) return;
+ at_cmd("ATA\r");
+ rv = at_expect("^OK", "^ERROR", 1000);
+ eos_modem_give();
+
+ eos_cell_pcm_start();
+ break;
+
+ case EOS_CELL_MTYPE_VOICE_HANGUP:
+ eos_cell_pcm_stop();
+
+ rv = eos_modem_take(1000);
+ if (rv) return;
+ at_cmd("AT+CHUP\r");
+ rv = at_expect("^OK", "^ERROR", 1000);
+ eos_modem_give();
+
+ break;
+
+ case EOS_CELL_MTYPE_VOICE_PCM:
+ eos_cell_pcm_push(buffer+1, size-1);
+ break;
+ }
+}
+
+static void ring_handler(char *urc, regmatch_t m[]) {
+ unsigned char *buf;
+ char *ring_buf;
+ uint16_t len;
+ regmatch_t match[2];
+ int rv = EOS_OK;
+
+ buf = eos_net_alloc();
+ buf[0] = EOS_CELL_MTYPE_VOICE | EOS_CELL_MTYPE_VOICE_RING;
+ len = 1;
+ rv = at_expect_match("^\\+CLIP: \"(\\+?[0-9]+)\"", NULL, &ring_buf, match, 2, REG_EXTENDED, 1000);
+ if (rv == 1) {
+ ring_buf[match[1].rm_eo] = '\0';
+ strcpy((char *)buf + 1, ring_buf + match[1].rm_so);
+ len += 1 + match[1].rm_eo - match[1].rm_so;
+ }
+ eos_net_send(EOS_NET_MTYPE_CELL, buf, len);
+}
+
+static void call_begin_handler(char *urc, regmatch_t m[]) {
+ unsigned char *buf;
+
+ buf = eos_net_alloc();
+ buf[0] = EOS_CELL_MTYPE_VOICE | EOS_CELL_MTYPE_VOICE_BEGIN;
+ eos_net_send(EOS_NET_MTYPE_CELL, buf, 1);
+}
+
+static void call_end_handler(char *urc, regmatch_t m[]) {
+ unsigned char *buf;
+ int duration = 0;
+
+ eos_cell_pcm_stop();
+
+ sscanf(urc + m[1].rm_so, "%6d", &duration);
+ buf = eos_net_alloc();
+ buf[0] = EOS_CELL_MTYPE_VOICE | EOS_CELL_MTYPE_VOICE_END;
+ buf[1] = duration >> 24;
+ buf[2] = duration >> 16;
+ buf[3] = duration >> 8;
+ buf[4] = duration;
+ eos_net_send(EOS_NET_MTYPE_CELL, buf, 5);
+}
+
+static void call_missed_handler(char *urc, regmatch_t m[]) {
+ unsigned char *buf;
+ uint16_t len;
+
+ eos_cell_pcm_stop();
+
+ buf = eos_net_alloc();
+ buf[0] = EOS_CELL_MTYPE_VOICE | EOS_CELL_MTYPE_VOICE_MISS;
+ urc[m[1].rm_eo] = '\0';
+ strcpy((char *)buf + 1, urc + m[1].rm_so);
+ len = 2 + m[1].rm_eo - m[1].rm_so;
+ eos_net_send(EOS_NET_MTYPE_CELL, buf, len);
+}
+
+void eos_cell_voice_init(void) {
+ at_urc_insert("^RING", ring_handler, REG_EXTENDED);
+ at_urc_insert("^VOICE CALL: BEGIN", call_begin_handler, REG_EXTENDED);
+ at_urc_insert("^VOICE CALL: END: ([0-9]{6}$)$", call_end_handler, REG_EXTENDED);
+ at_urc_insert("^MISSED.CALL: [^ ]+ (\\+?[0-9]+)$", call_missed_handler, REG_EXTENDED);
+} \ No newline at end of file
diff --git a/fw/esp32/components/eos/component.mk b/fw/esp32/components/eos/component.mk
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/fw/esp32/components/eos/component.mk
diff --git a/fw/esp32/components/eos/gsm.c b/fw/esp32/components/eos/gsm.c
new file mode 100644
index 0000000..6ae73ee
--- /dev/null
+++ b/fw/esp32/components/eos/gsm.c
@@ -0,0 +1,472 @@
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "gsm.h"
+
+#define DIVC(x,y) ((x) / (y) + ((x) % (y) != 0))
+
+uint8_t pdu_getc(char *pdu) {
+
+ int ch;
+ sscanf(pdu, "%2X", &ch);
+ return ch;
+}
+
+void pdu_putc(uint8_t ch, char *pdu) {
+ char b[3];
+
+ sprintf(b, "%.2X", ch);
+ *pdu = b[0];
+ *(pdu + 1) = b[1];
+}
+
+void pdu_gets(char *pdu, uint8_t *s, int s_len) {
+ int i, ch;
+
+ for (i=0; i<s_len; i++) {
+ sscanf(pdu + 2 * i, "%2X", &ch);
+ s[i] = ch;
+ }
+}
+
+void pdu_puts(uint8_t *s, int s_len, char *pdu) {
+ int i;
+ char b[3];
+
+ for (i=0; i<s_len; i++) {
+ sprintf(b, "%.2X", s[i]);
+ *(pdu + 2 * i) = b[0];
+ *(pdu + 2 * i + 1) = b[1];
+ }
+}
+
+void gsm_dcs_dec(uint8_t dcs, uint8_t *enc, uint16_t *flags) {
+ if ((dcs & GSM_DCS_GENERAL_IND) == 0) {
+ *enc = dcs & GSM_DCS_ENC;
+ if (dcs & GSM_DCS_CLASS_IND) {
+ *flags |= GSM_FLAG_CLASS;
+ *flags |= (uint16_t)(dcs & GSM_DCS_CLASS) << 8;
+ }
+ if (dcs & GSM_DCS_COMPRESS_IND) *flags |= GSM_FLAG_COMPRESS;
+ if (dcs & GSM_DCS_DELETE_IND) *flags |= GSM_FLAG_DELETE;
+ } else {
+ uint8_t group = dcs & GSM_DCS_GROUP;
+
+ switch (group) {
+ case GSM_DCS_MWI_DISCARD:
+ case GSM_DCS_MWI_STORE_GSM7:
+ case GSM_DCS_MWI_STORE_UCS2:
+ if (group == GSM_DCS_MWI_STORE_UCS2) {
+ *enc = GSM_ENC_UCS2;
+ } else {
+ *enc = GSM_ENC_7BIT;
+ }
+ if (GSM_DCS_MWI_DISCARD) *flags |= GSM_FLAG_DISCARD;
+ *flags |= GSM_FLAG_MWI;
+ *flags |= (uint16_t)(dcs & (GSM_DCS_MWI_SENSE | GSM_DCS_MWI_TYPE)) << 12;
+ break;
+
+ case GSM_DCS_ENCLASS:
+ *flags |= GSM_FLAG_CLASS;
+ *flags |= (uint16_t)(dcs & GSM_DCS_CLASS) << 8;
+ *enc = dcs & GSM_DCS_ENCLASS_ENC ? GSM_ENC_8BIT : GSM_ENC_7BIT;
+ break;
+ }
+ }
+}
+
+void gsm_dcs_enc(uint8_t enc, uint16_t flags, uint8_t *dcs) {
+ *dcs = enc;
+ if (flags & GSM_FLAG_CLASS) {
+ *dcs |= GSM_DCS_CLASS_IND;
+ *dcs |= (flags >> 8) & GSM_DCS_CLASS;
+ }
+ if (flags & GSM_FLAG_COMPRESS) *dcs |= GSM_DCS_COMPRESS_IND;
+ if (flags & GSM_FLAG_DELETE) *dcs |= GSM_DCS_DELETE_IND;
+}
+
+int gsm_ts_enc(char *ts, char *pdu, int pdu_size) {
+ uint8_t tz;
+ int tz_hh, tz_mm;
+
+ if (pdu_size < 14) return GSM_ERR_SIZE;
+
+ pdu[1] = ts[2]; // YY
+ pdu[0] = ts[3];
+
+ pdu[3] = ts[5]; // MM
+ pdu[2] = ts[6];
+
+ pdu[5] = ts[8]; // DD
+ pdu[4] = ts[9];
+
+ pdu[7] = ts[11]; // hh
+ pdu[6] = ts[12];
+
+ pdu[9] = ts[14]; // mm
+ pdu[8] = ts[15];
+
+ pdu[11] = ts[17]; // ss
+ pdu[10] = ts[18];
+
+ sscanf(ts + 20, "%2d:%2d", &tz_hh, &tz_mm);
+ tz = tz_hh * 4 + tz_mm / 15;
+ tz = (tz / 10) | ((tz % 10) << 4);
+ if (ts[19] == '-') tz |= 0x08;
+
+ pdu_putc(tz, pdu + 12);
+
+ return 14;
+}
+
+int gsm_ts_dec(char *pdu, int pdu_len, char *ts) {
+ uint8_t tz;
+
+ if (pdu_len < 14) return GSM_ERR_SIZE;
+
+ ts[0] = '2';
+ ts[1] = '0';
+ ts[2] = pdu[1]; // YY
+ ts[3] = pdu[0];
+ ts[4] = '-';
+ ts[5] = pdu[3]; // MM
+ ts[6] = pdu[2];
+ ts[7] = '-';
+ ts[8] = pdu[5]; // DD
+ ts[9] = pdu[4];
+ ts[10] = 'T';
+ ts[11] = pdu[7]; // hh
+ ts[12] = pdu[6];
+ ts[13] = ':';
+ ts[14] = pdu[9]; // mm
+ ts[15] = pdu[8];
+ ts[16] = ':';
+ ts[17] = pdu[11]; // ss
+ ts[18] = pdu[10];
+
+ tz = pdu_getc(pdu + 12);
+ if (tz & 0x08) {
+ ts[19] = '-';
+ tz = tz & ~0x08;
+ } else {
+ ts[19] = '+';
+ }
+ tz = (tz & 0x0f) * 10 + (tz >> 4);
+ sprintf(ts + 20, "%.2d:%.2d", tz / 4, (tz % 4) * 15);
+
+ return 14;
+}
+
+int gsm_7bit_enc(char *text, int text_len, char *pdu, int padb) {
+ uint8_t carry = 0;
+ int i = 0, pdu_len = 0, shc = 0;
+
+ if (!text_len) return 0;
+
+ if (padb) {
+ shc = 7 - padb;
+ } else {
+ carry = *text;
+ i++;
+ }
+
+ while (i < text_len) {
+ pdu_putc(carry | (*(text + i) << (7 - shc)), pdu + pdu_len);
+ pdu_len += 2;
+
+ shc++;
+ shc = shc % 7;
+ if (!shc) {
+ i++;
+ if (i == text_len) return pdu_len;
+ }
+
+ carry = *(text + i) >> shc;
+ i++;
+ }
+ pdu_putc(carry, pdu + pdu_len);
+ pdu_len += 2;
+
+ return pdu_len;
+}
+
+int gsm_7bit_dec(char *pdu, char *text, int text_len, int padb) {
+ uint8_t ch;
+ uint8_t carry = 0;
+ int i = 0, pdu_len = 0, shc = 0;
+
+ if (!text_len) return 0;
+
+ if (padb) {
+ ch = pdu_getc(pdu);
+ pdu_len += 2;
+ if (padb == 1) {
+ *text = ch >> 1;
+ i++;
+ } else {
+ carry = ch >> padb;
+ shc = 8 - padb;
+ }
+ }
+
+ while (i < text_len) {
+ ch = pdu_getc(pdu + pdu_len);
+ pdu_len += 2;
+
+ *(text + i) = ((ch << shc) | carry) & 0x7f;
+ carry = ch >> (7 - shc);
+ i++;
+
+ shc++;
+ shc = shc % 7;
+ if (!shc && (i < text_len)) {
+ *(text + i) = carry;
+ carry = 0;
+ i++;
+ }
+ }
+
+ return pdu_len;
+}
+
+int gsm_addr_enc(char *addr, int addr_len, uint8_t addr_type, char *pdu, int pdu_size) {
+ int _pdu_len;
+
+ addr_type |= GSM_EXT;
+
+ if ((addr_type & GSM_TON) == GSM_TON_ALPHANUMERIC) {
+ int _addr_len = DIVC(addr_len * 7, 4);
+
+ _pdu_len = 4 + DIVC(_addr_len, 2) * 2;
+ if (pdu_size < _pdu_len) return GSM_ERR_SIZE;
+
+ pdu_putc(_addr_len, pdu);
+ pdu_putc(addr_type, pdu + 2);
+ gsm_7bit_enc(addr, addr_len, pdu, 0);
+ } else {
+ int i;
+
+ _pdu_len = 4 + DIVC(addr_len, 2) * 2;
+ if (pdu_size < _pdu_len) return GSM_ERR_SIZE;
+
+ pdu_putc(addr_len, pdu);
+ pdu_putc(addr_type, pdu + 2);
+ for (i=0; i<addr_len / 2; i++) {
+ pdu[4 + 2 * i] = addr[2 * i + 1];
+ pdu[4 + 2 * i + 1] = addr[2 * i];
+ }
+ if (addr_len % 2 != 0) {
+ pdu[4 + 2 * i] = 'F';
+ pdu[4 + 2 * i + 1] = addr[2 * i];
+ }
+ }
+
+ return _pdu_len;
+}
+
+int gsm_addr_dec(char *pdu, int pdu_len, char *addr, int *addr_len, uint8_t *addr_type) {
+ int _pdu_len;
+
+ if (pdu_len < 4) return GSM_ERR_SIZE;
+
+ *addr_len = pdu_getc(pdu);
+ *addr_type = pdu_getc(pdu + 2);
+ if (*addr_len > GSM_ADDR_SIZE) return GSM_ERR_SIZE;
+
+ if (!(*addr_type & GSM_EXT)) return GSM_ERR;
+
+ _pdu_len = 4 + DIVC(*addr_len, 2) * 2;
+ if (pdu_len < _pdu_len) return GSM_ERR_SIZE;
+
+ if ((*addr_type & GSM_TON) == GSM_TON_ALPHANUMERIC) {
+ *addr_len = (*addr_len * 4) / 7;
+
+ gsm_7bit_dec(pdu + 4, addr, *addr_len, 0);
+ } else {
+ int i;
+
+ for (i=0; i<*addr_len / 2; i++) {
+ addr[2 * i] = pdu[4 + 2 * i + 1];
+ addr[2 * i + 1] = pdu[4 + 2 * i];
+ }
+ if (*addr_len % 2 != 0) {
+ addr[2 * i] = pdu[4 + 2 * i + 1];
+ }
+ }
+
+ return _pdu_len;
+}
+
+int gsm_sms_enc(char *addr, int addr_len, uint8_t addr_type, uint8_t *udh, int udh_len, uint8_t *msg, int msg_len, uint8_t enc, uint16_t flags, char *pdu, int pdu_size) {
+ int rv, _pdu_len = 0;
+ uint8_t mti;
+ uint8_t mr;
+ uint8_t pid;
+ uint8_t dcs;
+ uint8_t udl;
+
+ mti = GSM_MTI_SUBMIT;
+ if (udh_len) mti |= GSM_UDHI;
+ if (flags & GSM_FLAG_STATUS_REPORT) mti |= GSM_SRR;
+ if (flags & GSM_FLAG_REPLY_PATH) mti |= GSM_RP;
+ mr = 0;
+
+ if (pdu_size < 4) return GSM_ERR_SIZE;
+ pdu_putc(mti, pdu);
+ pdu_putc(mr, pdu + 2);
+ _pdu_len += 4;
+
+ rv = gsm_addr_enc(addr, addr_len, addr_type, pdu + _pdu_len, pdu_size - _pdu_len);
+ if (rv < 0) return rv;
+ _pdu_len += rv;
+
+ if (pdu_size < _pdu_len + 4) return GSM_ERR_SIZE;
+ if (flags & GSM_FLAG_TYPE0) {
+ pid = GSM_PID_TYPE0;
+ } else {
+ pid = GSM_PID_DEFAULT;
+ }
+ pid = 37;
+ pdu_putc(pid, pdu + _pdu_len);
+ gsm_dcs_enc(enc, flags, &dcs);
+ pdu_putc(dcs, pdu + _pdu_len + 2);
+ _pdu_len += 4;
+
+ if (enc == GSM_ENC_7BIT) {
+ int udh_blen = 0;
+ int padb = 0;
+
+ if (udh_len) {
+ udh_blen = 8 * (udh_len + 1);
+ padb = DIVC(udh_blen, 7) * 7 - udh_blen;
+ }
+ udl = DIVC(udh_blen, 7) + msg_len;
+
+ if (pdu_size < _pdu_len + (DIVC(udl * 7, 8) + 1) * 2) return GSM_ERR_SIZE;
+ pdu_putc(udl, pdu + _pdu_len);
+ _pdu_len += 2;
+
+ if (udh_len) {
+ pdu_putc(udh_len, pdu + _pdu_len);
+ pdu_puts(udh, udh_len, pdu + _pdu_len + 2);
+ _pdu_len += (udh_len + 1) * 2;
+ }
+
+ rv = gsm_7bit_enc((char *)msg, msg_len, pdu + _pdu_len, padb);
+ if (rv < 0) return rv;
+ _pdu_len += rv;
+ } else {
+ udl = msg_len + (udh_len ? udh_len + 1 : 0);
+
+ if (pdu_size < _pdu_len + (udl + 1) * 2) return GSM_ERR_SIZE;
+ pdu_putc(udl, pdu + _pdu_len);
+ _pdu_len += 2;
+
+ if (udh_len) {
+ pdu_putc(udh_len, pdu + _pdu_len);
+ pdu_puts(udh, udh_len, pdu + _pdu_len + 2);
+ _pdu_len += (udh_len + 1) * 2;
+ }
+
+ pdu_puts(msg, msg_len, pdu + _pdu_len);
+ _pdu_len += msg_len * 2;
+ }
+
+ return _pdu_len;
+}
+
+int gsm_sms_dec(char *pdu, int pdu_len, char *addr, int *addr_len, uint8_t *addr_type, uint8_t *udh, int *udh_len, uint8_t *msg, int *msg_len, char *ts, uint8_t *enc, uint16_t *flags) {
+ int rv, _pdu_len = 0;
+ uint8_t mti;
+ uint8_t pid;
+ uint8_t dcs;
+ uint8_t udl;
+
+ *enc = 0xff;
+ *flags = 0;
+ if (pdu_len < 2) return GSM_ERR_SIZE;
+ mti = pdu_getc(pdu);
+ _pdu_len += 2;
+
+ if ((mti & GSM_MTI) != GSM_MTI_DELIVER) return GSM_ERR_NOT_SUPPORTED;
+ if (mti & GSM_SRI) *flags |= GSM_FLAG_STATUS_REPORT;
+ if (mti & GSM_RP) *flags |= GSM_FLAG_REPLY_PATH;
+
+ rv = gsm_addr_dec(pdu + _pdu_len, pdu_len - _pdu_len, addr, addr_len, addr_type);
+ if (rv < 0) return rv;
+ _pdu_len += rv;
+
+ if (pdu_len < _pdu_len + 4) return GSM_ERR_SIZE;
+ pid = pdu_getc(pdu + _pdu_len);
+ if (pid == GSM_PID_TYPE0) {
+ *flags |= GSM_FLAG_TYPE0;
+ } else if (pid != GSM_PID_DEFAULT) {
+ return GSM_ERR_NOT_SUPPORTED;
+ }
+ dcs = pdu_getc(pdu + _pdu_len + 2);
+ gsm_dcs_dec(dcs, enc, flags);
+ _pdu_len += 4;
+
+ rv = gsm_ts_dec(pdu + _pdu_len, pdu_len - _pdu_len, ts);
+ if (rv < 0) return rv;
+ _pdu_len += rv;
+
+ if (pdu_len < _pdu_len + 2) return GSM_ERR_SIZE;
+ udl = pdu_getc(pdu + _pdu_len);
+ _pdu_len += 2;
+
+ if ((mti & GSM_UDHI) && (udl == 0)) return GSM_ERR;
+ *udh_len = 0;
+
+ if (*enc == GSM_ENC_7BIT) {
+ int udh_blen = 0;
+ int padb = 0;
+
+ if (pdu_len < _pdu_len + DIVC(udl * 7, 8) * 2) return GSM_ERR_SIZE;
+
+ if (mti & GSM_UDHI) {
+ *udh_len = pdu_getc(pdu + _pdu_len);
+ udh_blen = 8 * (*udh_len + 1);
+ padb = DIVC(udh_blen, 7) * 7 - udh_blen;
+
+ if (udl * 7 < udh_blen) return GSM_ERR;
+ if (*udh_len > GSM_UDH_SIZE) return GSM_ERR_SIZE;
+
+ pdu_gets(pdu + _pdu_len + 2, udh, *udh_len);
+ _pdu_len += (*udh_len + 1) * 2;
+ } else {
+ *udh_len = 0;
+ }
+
+ *msg_len = udl - DIVC(udh_blen, 7);
+ if (*msg_len > GSM_UD_SIZE) return GSM_ERR_SIZE;
+
+ rv = gsm_7bit_dec(pdu + _pdu_len, (char *)msg, *msg_len, padb);
+ if (rv < 0) return rv;
+ _pdu_len += rv;
+ } else {
+ if (pdu_len < _pdu_len + udl * 2) return GSM_ERR_SIZE;
+
+ if (mti & GSM_UDHI) {
+ *udh_len = pdu_getc(pdu + _pdu_len);
+
+ if (udl < *udh_len + 1) return GSM_ERR;
+ if (*udh_len > GSM_UDH_SIZE) return GSM_ERR_SIZE;
+
+ pdu_gets(pdu + _pdu_len + 2, udh, *udh_len);
+ _pdu_len += (*udh_len + 1) * 2;
+ } else {
+ *udh_len = 0;
+ }
+
+ *msg_len = udl - (*udh_len ? *udh_len + 1 : 0);
+ if (*msg_len > GSM_UD_SIZE) return GSM_ERR_SIZE;
+ if ((*enc == GSM_ENC_UCS2) && ((*msg_len % 2) != 0)) return GSM_ERR;
+
+ pdu_gets(pdu + _pdu_len, msg, *msg_len);
+ _pdu_len += *msg_len * 2;
+ }
+
+ return _pdu_len;
+}
diff --git a/fw/esp32/components/eos/gsm_cp.c b/fw/esp32/components/eos/gsm_cp.c
new file mode 100644
index 0000000..508aa54
--- /dev/null
+++ b/fw/esp32/components/eos/gsm_cp.c
@@ -0,0 +1,110 @@
+#include <stdint.h>
+
+#include "gsm.h"
+
+#define UCS2_SUPL_SIZE 11
+
+static const uint16_t gsm7_to_ucs2[128] = {
+ 0x0040, 0x00a3, 0x0024, 0x00a5, 0x00e8, 0x00e9, 0x00f9, 0x00ec, 0x00f2, 0x00c7, 0x000a, 0x00d8, 0x00f8, 0x000d, 0x00c5, 0x00e5,
+ 0x0394, 0x005f, 0x03a6, 0x0393, 0x039b, 0x03a9, 0x03a0, 0x03a8, 0x03a3, 0x0398, 0x039e, 0x001b, 0x00c6, 0x00e6, 0x00df, 0x00c9,
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x00a4, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
+ 0x00a1, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x00c4, 0x00d6, 0x00d1, 0x00dc, 0x00a7,
+ 0x00bf, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x00e4, 0x00f6, 0x00f1, 0x00fc, 0x00e0
+};
+
+// Ext table ss2 (0x1b 0x1b) is mapped to space
+static const uint16_t gsm7e_to_ucs2[128] = {
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x000c, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0x005e, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x0020, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x007b, 0x007d, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x005c,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x005b, 0x007e, 0x005d, 0xffff,
+ 0x007c, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x20ac, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff
+};
+
+static const uint16_t ucs2_to_gsm7[256] = {
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x000a, 0xffff, 0x1b0a, 0x000d, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x001b, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0002, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
+ 0x0000, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x1b3c, 0x1b2f, 0x1b3e, 0x1b14, 0x0011,
+ 0xffff, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x1b28, 0x1b40, 0x1b29, 0x1b3d, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0x0040, 0xffff, 0x0001, 0x0024, 0x0003, 0xffff, 0x005f, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x0060,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0x005b, 0x000e, 0x001c, 0x0009, 0xffff, 0x001f, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0x005d, 0xffff, 0xffff, 0xffff, 0xffff, 0x005c, 0xffff, 0x000b, 0xffff, 0xffff, 0xffff, 0x005e, 0xffff, 0xffff, 0x001e,
+ 0x007f, 0xffff, 0xffff, 0xffff, 0x007b, 0x000f, 0x001d, 0xffff, 0x0004, 0x0005, 0xffff, 0xffff, 0x0007, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0x007d, 0x0008, 0xffff, 0xffff, 0xffff, 0x007c, 0xffff, 0x000c, 0x0006, 0xffff, 0xffff, 0x007e, 0xffff, 0xffff, 0xffff
+};
+
+static const uint16_t ucs2_to_gsm7_supl[UCS2_SUPL_SIZE][2] = {
+ {0x0394, 0x10},
+ {0x03a6, 0x12},
+ {0x0393, 0x13},
+ {0x039b, 0x14},
+ {0x03a9, 0x15},
+ {0x03a0, 0x16},
+ {0x03a8, 0x17},
+ {0x03a3, 0x18},
+ {0x0398, 0x19},
+ {0x039e, 0x1a},
+ {0x20ac, 0x1b65}
+};
+
+int gsm_ucs2_to_7bit(uint16_t ucs2, char *gsm7, int gsm7_size) {
+ uint16_t ch = 0xffff;
+ int ret = 0;
+
+ if (gsm7_size < 1) return GSM_ERR_SIZE;
+
+ if (ucs2 < 256) {
+ ch = ucs2_to_gsm7[ucs2];
+ } else {
+ int i;
+
+ for (i=0; i<UCS2_SUPL_SIZE; i++) {
+ if (ucs2_to_gsm7_supl[i][0] == ucs2) {
+ ch = ucs2_to_gsm7_supl[i][1];
+ break;
+ }
+ }
+ }
+ if (ch == 0xffff) return GSM_ERR;
+ if (ch & 0xff00) {
+ if (gsm7_size < 2) return GSM_ERR_SIZE;
+ *gsm7 = 0x1b;
+ gsm7++;
+ ret++;
+ }
+ *gsm7 = ch & 0x7f;
+ ret++;
+
+ return ret;
+}
+
+int gsm_7bit_to_ucs2(char *gsm7, int gsm7_len, uint16_t *ucs2) {
+ int ret;
+
+ if (gsm7_len < 1) return GSM_ERR_SIZE;
+ if (*gsm7 != 0x1b) {
+ *ucs2 = gsm7_to_ucs2[*gsm7 & 0x7f];
+ ret = 1;
+ } else {
+ if (gsm7_len < 2) return GSM_ERR_SIZE;
+ gsm7++;
+ ret = 2;
+ *ucs2 = gsm7e_to_ucs2[*gsm7 & 0x7f];
+ }
+ if (*ucs2 == 0xffff) return GSM_ERR;
+
+ return ret;
+}
diff --git a/fw/esp32/components/eos/i2c.c b/fw/esp32/components/eos/i2c.c
new file mode 100644
index 0000000..828e4cd
--- /dev/null
+++ b/fw/esp32/components/eos/i2c.c
@@ -0,0 +1,82 @@
+#include <stdlib.h>
+
+#include <esp_log.h>
+#include <driver/i2c.h>
+
+#include "eos.h"
+
+static const char *TAG = "EOS I2C";
+
+#define I2C_MASTER_NUM I2C_NUM_0
+#define I2C_MASTER_FREQ_HZ 100000
+#define I2C_MASTER_GPIO_SCL 25
+#define I2C_MASTER_GPIO_SDA 26
+
+#define I2C_MASTER_TX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */
+#define I2C_MASTER_RX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */
+#define ACK_CHECK_EN 0x1 /*!< I2C master will check ack from slave*/
+#define ACK_CHECK_DIS 0x0 /*!< I2C master will not check ack from slave */
+#define ACK_VAL 0x0 /*!< I2C ack value */
+#define NCK_VAL 0x1 /*!< I2C nack value */
+
+void eos_i2c_init(void) {
+ i2c_config_t conf;
+ conf.mode = I2C_MODE_MASTER;
+ conf.sda_io_num = I2C_MASTER_GPIO_SDA;
+ conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
+ conf.scl_io_num = I2C_MASTER_GPIO_SCL;
+ conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
+ conf.master.clk_speed = I2C_MASTER_FREQ_HZ;
+ i2c_param_config(I2C_MASTER_NUM, &conf);
+ i2c_driver_install(I2C_MASTER_NUM, conf.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0);
+ ESP_LOGI(TAG, "INIT");
+}
+
+int eos_i2c_read(uint8_t addr, uint8_t reg, uint8_t *data, size_t len) {
+ int i, ret;
+ i2c_cmd_handle_t cmd = i2c_cmd_link_create();
+ i2c_master_start(cmd);
+ i2c_master_write_byte(cmd, addr << 1 | I2C_MASTER_WRITE, ACK_CHECK_EN);
+ i2c_master_write_byte(cmd, reg, ACK_CHECK_EN);
+ i2c_master_start(cmd);
+ i2c_master_write_byte(cmd, addr << 1 | I2C_MASTER_READ, ACK_CHECK_EN);
+ for (i=0; i < len - 1; i++) {
+ i2c_master_read_byte(cmd, data+i, ACK_VAL);
+ }
+ i2c_master_read_byte(cmd, data+i, NCK_VAL);
+ i2c_master_stop(cmd);
+
+ ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, 1000 / portTICK_RATE_MS);
+ i2c_cmd_link_delete(cmd);
+ if (ret != ESP_OK) {
+ return EOS_ERR;
+ }
+ return EOS_OK;
+}
+
+int eos_i2c_write(uint8_t addr, uint8_t reg, uint8_t *data, size_t len) {
+ int i, ret;
+ i2c_cmd_handle_t cmd = i2c_cmd_link_create();
+ i2c_master_start(cmd);
+ i2c_master_write_byte(cmd, addr << 1 | I2C_MASTER_WRITE, ACK_CHECK_EN);
+ i2c_master_write_byte(cmd, reg, ACK_CHECK_EN);
+ for (i=0; i < len; i++) {
+ i2c_master_write_byte(cmd, *(data+i), ACK_CHECK_EN);
+ }
+ i2c_master_stop(cmd);
+
+ ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, 1000 / portTICK_RATE_MS);
+ i2c_cmd_link_delete(cmd);
+ if (ret != ESP_OK) {
+ return EOS_ERR;
+ }
+ return EOS_OK;
+}
+
+int eos_i2c_read8(uint8_t addr, uint8_t reg, uint8_t *data) {
+ return eos_i2c_read(addr, reg, data, 1);
+}
+
+int eos_i2c_write8(uint8_t addr, uint8_t reg, uint8_t data) {
+ return eos_i2c_write(addr, reg, &data, 1);
+}
diff --git a/fw/esp32/components/eos/include/_net.h b/fw/esp32/components/eos/include/_net.h
new file mode 100644
index 0000000..35b5308
--- /dev/null
+++ b/fw/esp32/components/eos/include/_net.h
@@ -0,0 +1 @@
+#include "net.h" \ No newline at end of file
diff --git a/fw/esp32/components/eos/include/app.h b/fw/esp32/components/eos/include/app.h
new file mode 100644
index 0000000..2033b2b
--- /dev/null
+++ b/fw/esp32/components/eos/include/app.h
@@ -0,0 +1,23 @@
+#include <stdint.h>
+
+/* common */
+#define EOS_APP_MTU 1500
+#define EOS_APP_SIZE_BUF EOS_APP_MTU
+
+#define EOS_APP_MTYPE_TUN 1
+#define EOS_APP_MAX_MTYPE 8
+
+#define EOS_APP_MTYPE_FLAG_MASK 0xc0
+
+/* esp32 specific */
+#define EOS_APP_SIZE_BUFQ 4
+#define EOS_APP_SIZE_SNDQ 4
+
+typedef void (*eos_app_fptr_t) (unsigned char, unsigned char *, uint16_t);
+
+void eos_app_init(void);
+
+unsigned char *eos_app_alloc(void);
+void eos_app_free(unsigned char *buf);
+int eos_app_send(unsigned char mtype, unsigned char *buffer, uint16_t len);
+void eos_app_set_handler(unsigned char mtype, eos_app_fptr_t handler);
diff --git a/fw/esp32/components/eos/include/at_cmd.h b/fw/esp32/components/eos/include/at_cmd.h
new file mode 100644
index 0000000..2d0813e
--- /dev/null
+++ b/fw/esp32/components/eos/include/at_cmd.h
@@ -0,0 +1,18 @@
+#include <sys/types.h>
+#include <stdint.h>
+#include <regex.h>
+
+#define AT_SIZE_NMATCH 4
+#define AT_SIZE_PATTERN 64
+
+#define AT_SIZE_URC_LIST 32
+
+typedef void (*at_urc_cb_t) (char *, regmatch_t[]);
+
+void at_init(void);
+int at_urc_process(char *urc);
+int at_urc_insert(char *pattern, at_urc_cb_t cb, int flags);
+int at_urc_delete(char *pattern);
+void at_cmd(char *cmd);
+int at_expect(char *str_ok, char *str_err, uint32_t timeout);
+int at_expect_match(char *str_ok, char *str_err, char **buf, regmatch_t match[], size_t match_size, int flags, uint32_t timeout);
diff --git a/fw/esp32/components/eos/include/cell.h b/fw/esp32/components/eos/include/cell.h
new file mode 100644
index 0000000..fa9a0e2
--- /dev/null
+++ b/fw/esp32/components/eos/include/cell.h
@@ -0,0 +1,91 @@
+#include <sys/types.h>
+#include <stdint.h>
+
+#define EOS_CELL_MTYPE_DEV 0x10
+#define EOS_CELL_MTYPE_VOICE 0x20
+#define EOS_CELL_MTYPE_SMS 0x30
+#define EOS_CELL_MTYPE_CBS 0x40
+#define EOS_CELL_MTYPE_USSD 0x50
+#define EOS_CELL_MTYPE_PDP 0x60
+
+#define EOS_CELL_MTYPE_MASK 0xf0
+#define EOS_CELL_MAX_MTYPE 8
+
+/* EOS_CELL_MTYPE_DEV subtypes */
+#define EOS_CELL_MTYPE_READY 1
+#define EOS_CELL_MTYPE_UART_DATA 2
+#define EOS_CELL_MTYPE_UART_TAKE 3
+#define EOS_CELL_MTYPE_UART_GIVE 4
+#define EOS_CELL_MTYPE_RESET 5
+
+#define EOS_CELL_MTYPE_VOICE_PCM 1
+#define EOS_CELL_MTYPE_VOICE_DIAL 2
+#define EOS_CELL_MTYPE_VOICE_RING 3
+#define EOS_CELL_MTYPE_VOICE_ANSWER 4
+#define EOS_CELL_MTYPE_VOICE_HANGUP 5
+#define EOS_CELL_MTYPE_VOICE_BEGIN 6
+#define EOS_CELL_MTYPE_VOICE_END 7
+#define EOS_CELL_MTYPE_VOICE_MISS 8
+#define EOS_CELL_MTYPE_VOICE_BUSY 9
+#define EOS_CELL_MTYPE_VOICE_ERR 10
+
+#define EOS_CELL_MTYPE_SMS_LIST 1
+#define EOS_CELL_MTYPE_SMS_SEND 2
+#define EOS_CELL_MTYPE_SMS_MSG_NEW 3
+#define EOS_CELL_MTYPE_SMS_MSG_ITEM 4
+
+#define EOS_CELL_MTYPE_USSD_REQUEST 1
+#define EOS_CELL_MTYPE_USSD_REPLY 2
+#define EOS_CELL_MTYPE_USSD_CANCEL 3
+
+#define EOS_CELL_MTYPE_PDP_CONFIG 1
+#define EOS_CELL_MTYPE_PDP_CONNECT 2
+#define EOS_CELL_MTYPE_PDP_DISCONNECT 3
+
+#define EOS_CELL_SMS_ADDRTYPE_INTL 1
+#define EOS_CELL_SMS_ADDRTYPE_ALPHA 2
+#define EOS_CELL_SMS_ADDRTYPE_OTHER 3
+
+#define EOS_CELL_UART_MODE_NONE 0
+#define EOS_CELL_UART_MODE_ATCMD 1
+#define EOS_CELL_UART_MODE_PPP 2
+#define EOS_CELL_UART_MODE_RELAY 3
+#define EOS_CELL_UART_MODE_UNDEF 0xff
+
+#define EOS_CELL_UART_SIZE_BUF 1024
+
+void eos_cell_init(void);
+
+void eos_modem_init(void);
+void eos_modem_flush(void);
+size_t eos_modem_write(void *data, size_t size);
+size_t eos_modem_read(void *data, size_t size, uint32_t timeout);
+int eos_modem_readln(char *buf, size_t buf_size, uint32_t timeout);
+int eos_modem_resp(char *ok_str, char *err_str, uint32_t timeout);
+uint8_t eos_modem_get_mode(void);
+int eos_modem_set_mode(uint8_t mode);
+int eos_modem_take(uint32_t timeout);
+void eos_modem_give(void);
+void eos_modem_sleep(uint8_t mode);
+void eos_modem_wake(uint8_t source, uint8_t mode);
+int eos_modem_reset(void);
+
+void eos_ppp_set_apn(char *apn);
+void eos_ppp_set_auth(char *user, char *pass);
+int eos_ppp_connect(void);
+void eos_ppp_disconnect(void);
+
+void eos_cell_pcm_init(void);
+ssize_t eos_cell_pcm_read(unsigned char *data, size_t size);
+int eos_cell_pcm_push(unsigned char *data, size_t size);
+void eos_cell_pcm_start(void);
+void eos_cell_pcm_stop(void);
+
+void eos_cell_voice_handler(unsigned char mtype, unsigned char *buffer, uint16_t size);
+void eos_cell_sms_handler(unsigned char mtype, unsigned char *buffer, uint16_t size);
+void eos_cell_ussd_handler(unsigned char mtype, unsigned char *buffer, uint16_t size);
+void eos_cell_pdp_handler(unsigned char mtype, unsigned char *buffer, uint16_t size);
+
+void eos_cell_voice_init(void);
+void eos_cell_sms_init(void);
+void eos_cell_ussd_init(void);
diff --git a/fw/esp32/components/eos/include/eos.h b/fw/esp32/components/eos/include/eos.h
new file mode 100644
index 0000000..6f420ea
--- /dev/null
+++ b/fw/esp32/components/eos/include/eos.h
@@ -0,0 +1,27 @@
+#define EOS_OK 0
+#define EOS_ERR -1
+#define EOS_ERR_TIMEOUT -2
+#define EOS_ERR_BUSY -3
+
+#define EOS_ERR_FULL -10
+#define EOS_ERR_EMPTY -11
+#define EOS_ERR_NOTFOUND -12
+
+#define EOS_TASK_PRIORITY_NET_XCHG 1
+#define EOS_TASK_PRIORITY_APP_XCHG 1
+#define EOS_TASK_PRIORITY_UDP_RCVR 1
+#define EOS_TASK_PRIORITY_UART 1
+#define EOS_TASK_PRIORITY_MODEM 1
+#define EOS_TASK_PRIORITY_I2S 1
+#define EOS_TASK_PRIORITY_CELL 1
+#define EOS_TASK_PRIORITY_PWR 1
+
+#define EOS_TASK_SSIZE_NET_XCHG 8192
+#define EOS_TASK_SSIZE_APP_XCHG 8192
+#define EOS_TASK_SSIZE_UDP_RCVR 4096
+#define EOS_TASK_SSIZE_UART 4096
+#define EOS_TASK_SSIZE_MODEM 4096
+#define EOS_TASK_SSIZE_I2S 4096
+#define EOS_TASK_SSIZE_CELL 4096
+#define EOS_TASK_SSIZE_PWR 4096
+
diff --git a/fw/esp32/components/eos/include/gsm.h b/fw/esp32/components/eos/include/gsm.h
new file mode 100644
index 0000000..7864b09
--- /dev/null
+++ b/fw/esp32/components/eos/include/gsm.h
@@ -0,0 +1,139 @@
+#define GSM_OK 0
+#define GSM_ERR -1
+#define GSM_ERR_SIZE -10
+#define GSM_ERR_NOT_SUPPORTED -11
+
+#define GSM_TS_SIZE 25
+#define GSM_UD_SIZE 160
+#define GSM_UDH_SIZE 140
+#define GSM_MSG_SIZE GSM_UD_SIZE
+#define GSM_ADDR_SIZE 20
+
+/* Message-Type-Indicator */
+#define GSM_MTI 0x03
+#define GSM_MTI_DELIVER 0x00
+#define GSM_MTI_DELIVER_REPORT 0x00
+#define GSM_MTI_SUBMIT 0x01
+#define GSM_MTI_SUBMIT_REPORT 0x01
+#define GSM_MTI_COMMAND 0x02
+#define GSM_MTI_COMMAND_REPORT 0x02
+
+#define GSM_MMS 0x04 /* More-Messages-to-Send */
+#define GSM_RD 0x04 /* Reject-Duplicates */
+#define GSM_LP 0x08 /* Loop-Prevention */
+
+/* Validity-Period-Format */
+#define GSM_VPF 0x18
+#define GSM_VPF_NONE 0x00
+#define GSM_VPF_ENHANCED 0x08
+#define GSM_VPF_RELATIVE 0x10
+#define GSM_VPF_ABSOLUTE 0x18
+
+#define GSM_SRI 0x20 /* Status-Report-Indication */
+#define GSM_SRR 0x20 /* Status-Report-Request */
+#define GSM_SRQ 0x20 /* Status-Report-Qualifier */
+#define GSM_UDHI 0x40 /* User-Data-Header-Indicator */
+#define GSM_RP 0x80 /* Reply-Path */
+
+/* Type-of-Number */
+#define GSM_TON 0x70
+#define GSM_TON_UNKNOWN 0x00
+#define GSM_TON_INTERNATIONAL 0x10
+#define GSM_TON_NATIONAL 0x20
+#define GSM_TON_NETWORK 0x30
+#define GSM_TON_SUBSCRIBER 0x40
+#define GSM_TON_ALPHANUMERIC 0x50
+#define GSM_TON_ABBRREVIATED 0x60
+
+/* Numbering-Plan-Identification */
+#define GSM_NPI 0x0f
+#define GSM_NPI_UNKNOWN 0x00
+#define GSM_NPI_TELEPHONE 0x01
+#define GSM_NPI_DATA 0x03
+#define GSM_NPI_TELEX 0x04
+#define GSM_NPI_SCS1 0x05
+#define GSM_NPI_SCS2 0x06
+#define GSM_NPI_NATIONAL 0x08
+#define GSM_NPI_PRIVATE 0x09
+#define GSM_NPI_ERMES 0x0a
+
+#define GSM_EXT 0x80
+
+/* Protocol-Identifier */
+#define GSM_PID_DEFAULT 0
+#define GSM_PID_TYPE0 64
+
+/* Data-Coding-Scheme */
+#define GSM_DCS_CLASS 0x03
+#define GSM_DCS_ENC 0x0c
+
+#define GSM_DCS_CLASS_IND 0x10
+#define GSM_DCS_COMPRESS_IND 0x20
+#define GSM_DCS_DELETE_IND 0x40
+#define GSM_DCS_GENERAL_IND 0x80
+#define GSM_DCS_GROUP 0xf0
+
+#define GSM_DCS_MWI_DISCARD 0xc0
+#define GSM_DCS_MWI_STORE_GSM7 0xd0
+#define GSM_DCS_MWI_STORE_UCS2 0xe0
+#define GSM_DCS_MWI_SENSE 0x08
+#define GSM_DCS_MWI_TYPE 0x03
+
+#define GSM_DCS_ENCLASS 0xf0
+#define GSM_DCS_ENCLASS_ENC 0x04
+
+/* Parameter-Indicator */
+#define GSM_PI_PID 0x01
+#define GSM_PI_DCS 0x02
+#define GSM_PI_UD 0x04
+#define GSM_PI_EXT 0x08
+
+/* character set */
+#define GSM_ENC_7BIT 0x00
+#define GSM_ENC_8BIT 0x04
+#define GSM_ENC_UCS2 0x08
+
+/* message waiting indication */
+#define GSM_MWI_TYPE_VOICEMAIL 0x00
+#define GSM_MWI_TYPE_FAX 0x01
+#define GSM_MWI_TYPE_EMAIL 0x02
+#define GSM_MWI_TYPE_OTHER 0x03
+
+/* flags */
+#define GSM_FLAG_COMPRESS 0x0001
+#define GSM_FLAG_DELETE 0x0002
+#define GSM_FLAG_DISCARD 0x0004
+#define GSM_FLAG_TYPE0 0x0008
+#define GSM_FLAG_STATUS_REPORT 0x0010
+#define GSM_FLAG_REPLY_PATH 0x0020
+
+/* message class */
+#define GSM_FLAG_CLASS 0x0400
+#define GSM_FLAG_CLASS0 0x0000 /* Flash */
+#define GSM_FLAG_CLASS1 0x0100 /* ME-specific */
+#define GSM_FLAG_CLASS2 0x0200 /* (U)SIM-specific */
+#define GSM_FLAG_CLASS4 0x0300 /* TE-specific */
+#define GSM_FLAG_CLASS_MASK 0x0f00
+
+/* message waiting indication */
+#define GSM_FLAG_MWI 0x4000
+#define GSM_FLAG_MWI_SENSE 0x8000
+#define GSM_FLAG_MWI_VOICEMAIL 0x0000
+#define GSM_FLAG_MWI_FAX 0x1000
+#define GSM_FLAG_MWI_EMAIL 0x2000
+#define GSM_FLAG_MWI_OTHER 0x3000
+#define GSM_FLAG_MWI_MASK 0xf000
+
+uint8_t pdu_getc(char *pdu);
+void pdu_putc(uint8_t ch, char *pdu);
+void pdu_gets(char *pdu, uint8_t *s, int s_len);
+void pdu_puts(uint8_t *s, int s_len, char *pdu);
+int gsm_ucs2_to_7bit(uint16_t ucs2, char *gsm7, int gsm7_size);
+int gsm_7bit_to_ucs2(char *gsm7, int gsm7_len, uint16_t *ucs2);
+
+int gsm_7bit_enc(char *text, int text_len, char *pdu, int padb);
+int gsm_7bit_dec(char *pdu, char *text, int text_len, int padb);
+int gsm_addr_enc(char *addr, int addr_len, uint8_t addr_type, char *pdu, int pdu_size);
+int gsm_addr_dec(char *pdu, int pdu_len, char *addr, int *addr_len, uint8_t *addr_type);
+int gsm_sms_enc(char *addr, int addr_len, uint8_t addr_type, uint8_t *udh, int udh_len, uint8_t *msg, int msg_len, uint8_t enc, uint16_t flags, char *pdu, int pdu_size);
+int gsm_sms_dec(char *pdu, int pdu_len, char *addr, int *addr_len, uint8_t *addr_type, uint8_t *udh, int *udh_len, uint8_t *msg, int *msg_len, char *ts, uint8_t *enc, uint16_t *flags);
diff --git a/fw/esp32/components/eos/include/i2c.h b/fw/esp32/components/eos/include/i2c.h
new file mode 100644
index 0000000..f014141
--- /dev/null
+++ b/fw/esp32/components/eos/include/i2c.h
@@ -0,0 +1,9 @@
+#include <sys/types.h>
+#include <stdint.h>
+
+void eos_i2c_init(void);
+
+int eos_i2c_read(uint8_t addr, uint8_t reg, uint8_t *data, size_t len);
+int eos_i2c_write(uint8_t addr, uint8_t reg, uint8_t *data, size_t len);
+int eos_i2c_read8(uint8_t addr, uint8_t reg, uint8_t *data);
+int eos_i2c_write8(uint8_t addr, uint8_t reg, uint8_t data);
diff --git a/fw/esp32/components/eos/include/msgq.h b/fw/esp32/components/eos/include/msgq.h
new file mode 100644
index 0000000..bbfe041
--- /dev/null
+++ b/fw/esp32/components/eos/include/msgq.h
@@ -0,0 +1,31 @@
+#include <stdint.h>
+
+typedef struct EOSMsgItem {
+ unsigned char type;
+ unsigned char *buffer;
+ uint16_t len;
+} EOSMsgItem;
+
+typedef struct EOSMsgQ {
+ uint8_t idx_r;
+ uint8_t idx_w;
+ uint8_t size;
+ EOSMsgItem *array;
+} EOSMsgQ;
+
+void eos_msgq_init(EOSMsgQ *msgq, EOSMsgItem *array, uint8_t size);
+int eos_msgq_push(EOSMsgQ *msgq, unsigned char type, unsigned char *buffer, uint16_t len);
+void eos_msgq_pop(EOSMsgQ *msgq, unsigned char *type, unsigned char **buffer, uint16_t *len);
+uint8_t eos_msgq_len(EOSMsgQ *msgq);
+
+typedef struct EOSBufQ {
+ uint8_t idx_r;
+ uint8_t idx_w;
+ uint8_t size;
+ unsigned char **array;
+} EOSBufQ;
+
+void eos_bufq_init(EOSBufQ *bufq, unsigned char **array, uint8_t size);
+int eos_bufq_push(EOSBufQ *bufq, unsigned char *buffer);
+unsigned char *eos_bufq_pop(EOSBufQ *bufq);
+uint8_t eos_bufq_len(EOSBufQ *bufq);
diff --git a/fw/esp32/components/eos/include/net.h b/fw/esp32/components/eos/include/net.h
new file mode 100644
index 0000000..cf0cb6b
--- /dev/null
+++ b/fw/esp32/components/eos/include/net.h
@@ -0,0 +1,34 @@
+#include <stdint.h>
+
+/* common */
+#define EOS_NET_MTU 1500
+#define EOS_NET_SIZE_BUF EOS_NET_MTU
+
+#define EOS_NET_MTYPE_SOCK 1
+#define EOS_NET_MTYPE_POWER 4
+
+#define EOS_NET_MTYPE_WIFI 5
+#define EOS_NET_MTYPE_CELL 6
+#define EOS_NET_MTYPE_SIP 7
+#define EOS_NET_MTYPE_APP 8
+
+#define EOS_NET_MAX_MTYPE 8
+
+#define EOS_NET_MTYPE_FLAG_ONEW 0x40
+#define EOS_NET_MTYPE_FLAG_REPW 0x80
+#define EOS_NET_MTYPE_FLAG_MASK 0xc0
+
+/* esp32 specific */
+#define EOS_NET_SIZE_BUFQ 4
+#define EOS_NET_SIZE_SNDQ 4
+
+typedef void (*eos_net_fptr_t) (unsigned char, unsigned char *, uint16_t);
+
+void eos_net_init(void);
+
+unsigned char *eos_net_alloc(void);
+void eos_net_free(unsigned char *buf);
+int eos_net_send(unsigned char mtype, unsigned char *buffer, uint16_t len);
+void eos_net_set_handler(unsigned char mtype, eos_net_fptr_t handler);
+void eos_net_sleep_done(uint8_t mode);
+void eos_net_wake(uint8_t source, uint8_t mode);
diff --git a/fw/esp32/components/eos/include/power.h b/fw/esp32/components/eos/include/power.h
new file mode 100644
index 0000000..3d2e0bc
--- /dev/null
+++ b/fw/esp32/components/eos/include/power.h
@@ -0,0 +1,20 @@
+#include <stdint.h>
+
+#define EOS_PWR_MTYPE_BUTTON 1
+
+#define EOS_PWR_WAKE_RST 0
+#define EOS_PWR_WAKE_BTN 1
+#define EOS_PWR_WAKE_NET 2
+#define EOS_PWR_WAKE_MSG 3
+#define EOS_PWR_WAKE_UART 4
+
+#define EOS_PWR_SMODE_LIGHT 1
+#define EOS_PWR_SMODE_DEEP 2
+
+void eos_power_init(void);
+
+void eos_power_wait4init(void);
+uint8_t eos_power_wakeup_cause(void);
+void eos_power_sleep(void);
+void eos_power_wake(uint8_t source);
+void eos_power_net_ready(void); \ No newline at end of file
diff --git a/fw/esp32/components/eos/include/sock.h b/fw/esp32/components/eos/include/sock.h
new file mode 100644
index 0000000..7e937cb
--- /dev/null
+++ b/fw/esp32/components/eos/include/sock.h
@@ -0,0 +1,18 @@
+#include <stdint.h>
+
+#define EOS_SOCK_MTYPE_PKT 0
+#define EOS_SOCK_MTYPE_OPEN_DGRAM 1
+#define EOS_SOCK_MTYPE_CLOSE 127
+
+#define EOS_SOCK_MAX_SOCK 2
+
+#define EOS_SOCK_SIZE_UDP_HDR 8
+
+#define EOS_IPv4_ADDR_SIZE 4
+
+typedef struct EOSNetAddr {
+ unsigned char host[EOS_IPv4_ADDR_SIZE];
+ uint16_t port;
+} EOSNetAddr;
+
+void eos_sock_init(void); \ No newline at end of file
diff --git a/fw/esp32/components/eos/include/tun.h b/fw/esp32/components/eos/include/tun.h
new file mode 100644
index 0000000..3acb2a6
--- /dev/null
+++ b/fw/esp32/components/eos/include/tun.h
@@ -0,0 +1 @@
+void eos_tun_init(void); \ No newline at end of file
diff --git a/fw/esp32/components/eos/include/unicode.h b/fw/esp32/components/eos/include/unicode.h
new file mode 120000
index 0000000..e859a65
--- /dev/null
+++ b/fw/esp32/components/eos/include/unicode.h
@@ -0,0 +1 @@
+../../../../fe310/eos/unicode.h \ No newline at end of file
diff --git a/fw/esp32/components/eos/include/wifi.h b/fw/esp32/components/eos/include/wifi.h
new file mode 100644
index 0000000..11bccec
--- /dev/null
+++ b/fw/esp32/components/eos/include/wifi.h
@@ -0,0 +1,13 @@
+#define EOS_WIFI_MTYPE_SCAN 1
+#define EOS_WIFI_MTYPE_CONFIG 2
+#define EOS_WIFI_MTYPE_CONNECT 3
+#define EOS_WIFI_MTYPE_DISCONNECT 4
+
+#define EOS_WIFI_MAX_MTYPE 5
+
+void eos_wifi_init(void);
+
+int eos_wifi_scan(void);
+int eos_wifi_set_config(char *ssid, char *pass);
+int eos_wifi_connect(void);
+int eos_wifi_disconnect(void);
diff --git a/fw/esp32/components/eos/msgq.c b/fw/esp32/components/eos/msgq.c
new file mode 100644
index 0000000..c200f7c
--- /dev/null
+++ b/fw/esp32/components/eos/msgq.c
@@ -0,0 +1,66 @@
+#include <stdlib.h>
+
+#include "eos.h"
+#include "msgq.h"
+
+#define IDX_MASK(IDX, SIZE) ((IDX) & ((SIZE) - 1))
+
+void eos_msgq_init(EOSMsgQ *msgq, EOSMsgItem *array, uint8_t size) {
+ msgq->idx_r = 0;
+ msgq->idx_w = 0;
+ msgq->size = size;
+ msgq->array = array;
+}
+
+int eos_msgq_push(EOSMsgQ *msgq, unsigned char type, unsigned char *buffer, uint16_t len) {
+ if ((uint8_t)(msgq->idx_w - msgq->idx_r) == msgq->size) return EOS_ERR_FULL;
+
+ uint8_t idx = IDX_MASK(msgq->idx_w, msgq->size);
+ msgq->array[idx].type = type;
+ msgq->array[idx].buffer = buffer;
+ msgq->array[idx].len = len;
+ msgq->idx_w++;
+ return EOS_OK;
+}
+
+void eos_msgq_pop(EOSMsgQ *msgq, unsigned char *type, unsigned char **buffer, uint16_t *len) {
+ if (msgq->idx_r == msgq->idx_w) {
+ *type = 0;
+ *buffer = NULL;
+ *len = 0;
+ } else {
+ uint8_t idx = IDX_MASK(msgq->idx_r, msgq->size);
+ *type = msgq->array[idx].type;
+ *buffer = msgq->array[idx].buffer;
+ *len = msgq->array[idx].len;
+ msgq->idx_r++;
+ }
+}
+
+uint8_t eos_msgq_len(EOSMsgQ *msgq) {
+ return (uint8_t)(msgq->idx_w - msgq->idx_r);
+}
+
+void eos_bufq_init(EOSBufQ *bufq, unsigned char **array, uint8_t size) {
+ bufq->idx_r = 0;
+ bufq->idx_w = 0;
+ bufq->size = size;
+ bufq->array = array;
+}
+
+int eos_bufq_push(EOSBufQ *bufq, unsigned char *buffer) {
+ if ((uint8_t)(bufq->idx_w - bufq->idx_r) == bufq->size) return EOS_ERR_FULL;
+
+ bufq->array[IDX_MASK(bufq->idx_w++, bufq->size)] = buffer;
+ return EOS_OK;
+}
+
+unsigned char *eos_bufq_pop(EOSBufQ *bufq) {
+ if (bufq->idx_r == bufq->idx_w) return NULL;
+
+ return bufq->array[IDX_MASK(bufq->idx_r++, bufq->size)];
+}
+
+uint8_t eos_bufq_len(EOSBufQ *bufq) {
+ return (uint8_t)(bufq->idx_w - bufq->idx_r);
+}
diff --git a/fw/esp32/components/eos/net.c b/fw/esp32/components/eos/net.c
new file mode 100644
index 0000000..0491b41
--- /dev/null
+++ b/fw/esp32/components/eos/net.c
@@ -0,0 +1,302 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <freertos/FreeRTOS.h>
+#include <freertos/semphr.h>
+#include <freertos/task.h>
+
+#include <esp_system.h>
+#include <esp_log.h>
+#include <esp_err.h>
+#include <esp_heap_caps.h>
+#include <driver/gpio.h>
+#include <driver/spi_slave.h>
+
+#include "eos.h"
+#include "msgq.h"
+#include "power.h"
+#include "net.h"
+
+#define SPI_GPIO_RTS 22
+#define SPI_GPIO_CTS 21
+#define SPI_GPIO_MOSI 23
+#define SPI_GPIO_MISO 19
+#define SPI_GPIO_SCLK 18
+#define SPI_GPIO_CS 5
+
+#define SPI_SIZE_BUF (EOS_NET_SIZE_BUF + 4)
+#define SPI_SIZE_HDR 3
+
+static volatile char net_sleep = 0;
+
+static EOSBufQ net_buf_q;
+static unsigned char *net_bufq_array[EOS_NET_SIZE_BUFQ];
+
+static EOSMsgQ net_send_q;
+static EOSMsgItem net_sndq_array[EOS_NET_SIZE_SNDQ];
+
+static SemaphoreHandle_t mutex;
+static SemaphoreHandle_t semaph;
+static TaskHandle_t net_xchg_task_handle;
+static const char *TAG = "EOS NET";
+
+static eos_net_fptr_t net_handler[EOS_NET_MAX_MTYPE];
+
+static void bad_handler(unsigned char mtype, unsigned char *buffer, uint16_t len) {
+ ESP_LOGE(TAG, "bad handler: %d len: %d", mtype, len);
+}
+
+// Called after a transaction is queued and ready for pickup by master. We use this to set the handshake line high.
+static void _post_setup_cb(spi_slave_transaction_t *trans) {
+ gpio_set_level(SPI_GPIO_CTS, 1);
+}
+
+// Called after transaction is sent/received. We use this to set the handshake line low.
+static void _post_trans_cb(spi_slave_transaction_t *trans) {
+ gpio_set_level(SPI_GPIO_CTS, 0);
+}
+
+static void net_xchg_task(void *pvParameters) {
+ int wake = 0;
+ int reply = 0;
+ int skip_next_msg = 0;
+ unsigned char mtype = 0;
+ unsigned char mtype_flags = 0;
+ unsigned char *buffer;
+ uint16_t len;
+ unsigned char *buf_send = heap_caps_malloc(SPI_SIZE_BUF, MALLOC_CAP_DMA);
+ unsigned char *buf_recv = heap_caps_malloc(SPI_SIZE_BUF, MALLOC_CAP_DMA);
+ esp_err_t ret;
+
+ static spi_slave_transaction_t spi_tr;
+
+ //Configuration for the SPI bus
+ static spi_bus_config_t spi_bus_cfg = {
+ .mosi_io_num = SPI_GPIO_MOSI,
+ .miso_io_num = SPI_GPIO_MISO,
+ .sclk_io_num = SPI_GPIO_SCLK
+ };
+
+ //Configuration for the SPI slave interface
+ static spi_slave_interface_config_t spi_slave_cfg = {
+ .mode = 0,
+ .spics_io_num = SPI_GPIO_CS,
+ .queue_size = 2,
+ .flags = 0,
+ .post_setup_cb = _post_setup_cb,
+ .post_trans_cb = _post_trans_cb
+ };
+
+ //Initialize SPI slave interface
+ ret = spi_slave_initialize(VSPI_HOST, &spi_bus_cfg, &spi_slave_cfg, 2);
+ assert(ret == ESP_OK);
+
+ memset(&spi_tr, 0, sizeof(spi_tr));
+ spi_tr.tx_buffer = buf_send;
+ spi_tr.rx_buffer = buf_recv;
+ spi_tr.length = SPI_SIZE_BUF * 8;
+
+ if (eos_power_wakeup_cause()) {
+ wake = 1;
+ skip_next_msg = 1;
+ }
+
+ eos_power_wait4init();
+ while (1) {
+ if (!skip_next_msg) {
+ xSemaphoreTake(mutex, portMAX_DELAY);
+
+ eos_msgq_pop(&net_send_q, &mtype, &buffer, &len);
+ if (mtype) {
+ buf_send[0] = mtype;
+ buf_send[1] = len >> 8;
+ buf_send[2] = len & 0xFF;
+ if (buffer) {
+ memcpy(buf_send + SPI_SIZE_HDR, buffer, len);
+ eos_bufq_push(&net_buf_q, buffer);
+ xSemaphoreGive(semaph);
+ }
+ } else {
+ gpio_set_level(SPI_GPIO_RTS, 0);
+ buf_send[0] = 0;
+ buf_send[1] = 0;
+ buf_send[2] = 0;
+ }
+
+ xSemaphoreGive(mutex);
+ }
+ skip_next_msg = 0;
+
+ if (reply) {
+ spi_tr.tx_buffer = buf_recv;
+ spi_tr.rx_buffer = NULL;
+ } else {
+ buf_recv[0] = 0;
+ buf_recv[1] = 0;
+ buf_recv[2] = 0;
+ }
+ spi_slave_transmit(VSPI_HOST, &spi_tr, portMAX_DELAY);
+ // ESP_LOGI(TAG, "RECV:%d", buf_recv[0]);
+ if (reply) {
+ reply = 0;
+ spi_tr.tx_buffer = buf_send;
+ spi_tr.rx_buffer = buf_recv;
+ if (buf_send[0]) skip_next_msg = 1;
+ continue;
+ }
+
+ if (wake) {
+ eos_power_net_ready();
+ wake = 0;
+ }
+
+ if ((spi_tr.trans_len / 8) < SPI_SIZE_HDR) continue;
+ if (buf_recv[0] == 0x00) continue;
+
+ if (buf_recv[0] == 0xFF) { // Sleep req
+ if (buf_send[0] == 0) {
+ int abort = 0;
+
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ net_sleep = 1;
+ if (eos_msgq_len(&net_send_q)) abort = 1;
+ xSemaphoreGive(mutex);
+
+ spi_slave_free(VSPI_HOST);
+
+ eos_power_sleep();
+ if (abort) eos_power_wake(EOS_PWR_WAKE_MSG);
+
+ vTaskSuspend(NULL);
+
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ net_sleep = 0;
+ xSemaphoreGive(mutex);
+
+ spi_slave_initialize(VSPI_HOST, &spi_bus_cfg, &spi_slave_cfg, 1);
+ wake = 1;
+ skip_next_msg = 1;
+ }
+ continue;
+ }
+
+ mtype = buf_recv[0] & ~EOS_NET_MTYPE_FLAG_MASK;
+ mtype_flags = buf_recv[0] & EOS_NET_MTYPE_FLAG_MASK;
+ len = (uint16_t)buf_recv[1] << 8;
+ len |= (uint16_t)buf_recv[2] & 0xFF;
+ buffer = buf_recv + SPI_SIZE_HDR;
+ if ((mtype <= EOS_NET_MAX_MTYPE) && (len <= EOS_NET_MTU)) {
+ net_handler[mtype - 1](mtype, buffer, len);
+ } else {
+ bad_handler(mtype, buffer, len);
+ }
+ if ((mtype_flags & EOS_NET_MTYPE_FLAG_ONEW) && buf_send[0]) {
+ skip_next_msg = 1;
+ }
+ if (mtype_flags & EOS_NET_MTYPE_FLAG_REPW) {
+ skip_next_msg = 1;
+ reply = 1;
+ }
+ }
+ vTaskDelete(NULL);
+}
+
+void eos_net_init(void) {
+ int i;
+
+ // Configuration for the handshake lines
+ gpio_config_t io_conf;
+
+ io_conf.intr_type = GPIO_INTR_DISABLE;
+ io_conf.mode = GPIO_MODE_OUTPUT;
+ io_conf.pull_up_en = 0;
+ io_conf.pull_down_en = 0;
+ io_conf.pin_bit_mask = ((uint64_t)1 << SPI_GPIO_CTS);
+ gpio_config(&io_conf);
+ gpio_set_level(SPI_GPIO_CTS, 0);
+
+ io_conf.intr_type = GPIO_INTR_DISABLE;
+ io_conf.mode = GPIO_MODE_OUTPUT;
+ io_conf.pull_up_en = 0;
+ io_conf.pull_down_en = 0;
+ io_conf.pin_bit_mask = ((uint64_t)1 << SPI_GPIO_RTS);
+ gpio_config(&io_conf);
+ gpio_set_level(SPI_GPIO_RTS, 0);
+
+ eos_msgq_init(&net_send_q, net_sndq_array, EOS_NET_SIZE_SNDQ);
+ eos_bufq_init(&net_buf_q, net_bufq_array, EOS_NET_SIZE_BUFQ);
+ for (i=0; i<EOS_NET_SIZE_BUFQ; i++) {
+ eos_bufq_push(&net_buf_q, malloc(EOS_NET_SIZE_BUF));
+ }
+
+ for (i=0; i<EOS_NET_MAX_MTYPE; i++) {
+ net_handler[i] = bad_handler;
+ }
+
+ semaph = xSemaphoreCreateCounting(EOS_NET_SIZE_BUFQ, EOS_NET_SIZE_BUFQ);
+ mutex = xSemaphoreCreateBinary();
+ xSemaphoreGive(mutex);
+ xTaskCreate(&net_xchg_task, "net_xchg", EOS_TASK_SSIZE_NET_XCHG, NULL, EOS_TASK_PRIORITY_NET_XCHG, &net_xchg_task_handle);
+ ESP_LOGI(TAG, "INIT");
+}
+
+unsigned char *eos_net_alloc(void) {
+ unsigned char *ret;
+
+ xSemaphoreTake(semaph, portMAX_DELAY);
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ ret = eos_bufq_pop(&net_buf_q);
+ xSemaphoreGive(mutex);
+
+ return ret;
+}
+
+void eos_net_free(unsigned char *buf) {
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ eos_bufq_push(&net_buf_q, buf);
+ xSemaphoreGive(semaph);
+ xSemaphoreGive(mutex);
+}
+
+int eos_net_send(unsigned char mtype, unsigned char *buffer, uint16_t len) {
+ int rv = EOS_OK;
+ int sleep;
+
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ sleep = net_sleep;
+ gpio_set_level(SPI_GPIO_RTS, 1);
+ rv = eos_msgq_push(&net_send_q, mtype, buffer, len);
+ xSemaphoreGive(mutex);
+
+ if (rv) eos_net_free(buffer);
+
+ if (sleep) eos_power_wake(EOS_PWR_WAKE_MSG);
+
+ return rv;
+}
+
+void eos_net_set_handler(unsigned char mtype, eos_net_fptr_t handler) {
+ if (handler == NULL) handler = bad_handler;
+ if (mtype && (mtype <= EOS_NET_MAX_MTYPE)) net_handler[mtype - 1] = handler;
+}
+
+void eos_net_sleep_done(uint8_t mode) {
+ gpio_set_level(SPI_GPIO_CTS, 1);
+ vTaskDelay(200 / portTICK_PERIOD_MS);
+ gpio_set_level(SPI_GPIO_CTS, 0);
+}
+
+void eos_net_wake(uint8_t source, uint8_t mode) {
+ int sleep;
+
+ if (mode == EOS_PWR_SMODE_DEEP) return;
+ do {
+ vTaskResume(net_xchg_task_handle);
+ vTaskDelay(10 / portTICK_PERIOD_MS);
+
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ sleep = net_sleep;
+ xSemaphoreGive(mutex);
+ } while (sleep);
+}
diff --git a/fw/esp32/components/eos/power.c b/fw/esp32/components/eos/power.c
new file mode 100644
index 0000000..0cbb4e0
--- /dev/null
+++ b/fw/esp32/components/eos/power.c
@@ -0,0 +1,324 @@
+#include <stdlib.h>
+
+#include <freertos/FreeRTOS.h>
+#include <freertos/task.h>
+#include <freertos/queue.h>
+#include <driver/gpio.h>
+#include <esp_sleep.h>
+#include <esp_pm.h>
+#include <esp_log.h>
+
+#include "eos.h"
+#include "net.h"
+#include "cell.h"
+#include "power.h"
+
+#define POWER_GPIO_BTN 0
+#define POWER_GPIO_NET 5
+#define POWER_GPIO_UART 35
+
+#define POWER_ETYPE_BTN 1
+#define POWER_ETYPE_SLEEP 2
+#define POWER_ETYPE_WAKE 3
+#define POWER_ETYPE_NETRDY 4
+
+typedef struct {
+ uint8_t type;
+ union {
+ uint8_t source;
+ uint8_t level;
+ };
+} power_event_t;
+
+static esp_pm_lock_handle_t power_lock_cpu_freq;
+static esp_pm_lock_handle_t power_lock_apb_freq;
+static esp_pm_lock_handle_t power_lock_no_sleep;
+
+static const char *TAG = "EOS POWER";
+
+static QueueHandle_t power_queue;
+
+static volatile int init_done = 0;
+
+static void IRAM_ATTR btn_handler(void *arg) {
+ power_event_t evt;
+
+ evt.type = POWER_ETYPE_BTN;
+ evt.level = gpio_get_level(POWER_GPIO_BTN);
+ xQueueSendFromISR(power_queue, &evt, NULL);
+}
+
+static void IRAM_ATTR btn_wake_handler(void *arg) {
+ power_event_t evt;
+
+ gpio_intr_disable(POWER_GPIO_BTN);
+
+ evt.type = POWER_ETYPE_WAKE;
+ evt.source = EOS_PWR_WAKE_BTN;
+ xQueueSendFromISR(power_queue, &evt, NULL);
+
+}
+
+static void IRAM_ATTR net_wake_handler(void *arg) {
+ power_event_t evt;
+
+ gpio_intr_disable(POWER_GPIO_NET);
+
+ evt.type = POWER_ETYPE_WAKE;
+ evt.source = EOS_PWR_WAKE_NET;
+ xQueueSendFromISR(power_queue, &evt, NULL);
+}
+
+static void IRAM_ATTR uart_wake_handler(void *arg) {
+ power_event_t evt;
+
+ gpio_intr_disable(POWER_GPIO_UART);
+
+ evt.type = POWER_ETYPE_WAKE;
+ evt.source = EOS_PWR_WAKE_UART;
+ xQueueSendFromISR(power_queue, &evt, NULL);
+}
+
+void power_sleep(uint8_t mode) {
+ gpio_config_t io_conf;
+
+ eos_modem_sleep(mode);
+ eos_net_sleep_done(mode);
+
+ switch (mode) {
+ case EOS_PWR_SMODE_LIGHT:
+ io_conf.intr_type = GPIO_INTR_DISABLE;
+ io_conf.mode = GPIO_MODE_INPUT;
+ io_conf.pin_bit_mask = ((uint64_t)1 << POWER_GPIO_NET);
+ io_conf.pull_up_en = 0;
+ io_conf.pull_down_en = 0;
+ gpio_config(&io_conf);
+
+ gpio_isr_handler_add(POWER_GPIO_BTN, btn_wake_handler, NULL);
+ gpio_isr_handler_add(POWER_GPIO_NET, net_wake_handler, NULL);
+ gpio_isr_handler_add(POWER_GPIO_UART, uart_wake_handler, NULL);
+
+ esp_sleep_enable_gpio_wakeup();
+ gpio_wakeup_enable(POWER_GPIO_BTN, GPIO_INTR_LOW_LEVEL);
+ gpio_wakeup_enable(POWER_GPIO_NET, GPIO_INTR_LOW_LEVEL);
+ gpio_wakeup_enable(POWER_GPIO_UART, GPIO_INTR_LOW_LEVEL);
+
+ ESP_LOGI(TAG, "SLEEP");
+
+ esp_pm_lock_release(power_lock_apb_freq);
+ esp_pm_lock_release(power_lock_no_sleep);
+
+ break;
+
+ case EOS_PWR_SMODE_DEEP:
+ gpio_deep_sleep_hold_en();
+ esp_sleep_enable_ext0_wakeup(POWER_GPIO_BTN, 0);
+ esp_sleep_enable_ext1_wakeup((uint64_t)1 << POWER_GPIO_UART, ESP_EXT1_WAKEUP_ALL_LOW);
+
+ ESP_LOGI(TAG, "SLEEP");
+
+ esp_deep_sleep_start();
+ break;
+
+ default:
+ break;
+ }
+}
+
+void power_wake_stage1(uint8_t source, uint8_t mode) {
+ gpio_config_t io_conf;
+
+ if (mode == EOS_PWR_SMODE_LIGHT) {
+ esp_pm_lock_acquire(power_lock_apb_freq);
+ esp_pm_lock_acquire(power_lock_no_sleep);
+
+ gpio_wakeup_disable(POWER_GPIO_BTN);
+ gpio_wakeup_disable(POWER_GPIO_NET);
+ gpio_wakeup_disable(POWER_GPIO_UART);
+
+ gpio_isr_handler_remove(POWER_GPIO_NET);
+ io_conf.intr_type = GPIO_INTR_DISABLE;
+ io_conf.mode = GPIO_MODE_DISABLE;
+ io_conf.pin_bit_mask = ((uint64_t)1 << POWER_GPIO_NET);
+ io_conf.pull_up_en = 0;
+ io_conf.pull_down_en = 0;
+ gpio_config(&io_conf);
+ }
+
+ gpio_intr_disable(POWER_GPIO_BTN);
+ if ((source != EOS_PWR_WAKE_BTN) && (source != EOS_PWR_WAKE_NET)) {
+ gpio_set_direction(POWER_GPIO_BTN, GPIO_MODE_OUTPUT);
+ gpio_set_level(POWER_GPIO_BTN, 0);
+ vTaskDelay(200 / portTICK_PERIOD_MS);
+ gpio_set_direction(POWER_GPIO_BTN, GPIO_MODE_INPUT);
+ }
+
+ eos_net_wake(source, mode);
+}
+
+void power_wake_stage2(uint8_t source, uint8_t mode) {
+ eos_modem_wake(source, mode);
+
+ gpio_set_intr_type(POWER_GPIO_BTN, GPIO_INTR_ANYEDGE);
+ gpio_isr_handler_add(POWER_GPIO_BTN, btn_handler, NULL);
+
+ ESP_LOGI(TAG, "WAKE");
+}
+
+static void power_event_task(void *pvParameters) {
+ unsigned char *buf;
+ power_event_t evt;
+ uint8_t source;
+ uint8_t wakeup_cause;
+ uint8_t mode;
+ int sleep;
+
+ source = 0;
+ wakeup_cause = eos_power_wakeup_cause();
+ if (wakeup_cause) {
+ mode = EOS_PWR_SMODE_DEEP;
+ sleep = 1;
+ } else {
+ mode = EOS_PWR_SMODE_LIGHT;
+ sleep = 0;
+ }
+
+ while (1) {
+ if (xQueueReceive(power_queue, &evt, portMAX_DELAY)) {
+ switch (evt.type) {
+ case POWER_ETYPE_SLEEP:
+ if (!sleep) {
+ mode = EOS_PWR_SMODE_DEEP;
+ power_sleep(mode);
+ sleep = 1;
+ }
+ break;
+
+ case POWER_ETYPE_WAKE:
+ if (sleep) {
+ source = evt.source;
+ power_wake_stage1(source, mode);
+ }
+ break;
+
+ case POWER_ETYPE_NETRDY:
+ if (sleep && source) {
+ power_wake_stage2(source, mode);
+ sleep = 0;
+ source = 0;
+ }
+ break;
+
+ case POWER_ETYPE_BTN:
+ buf = eos_net_alloc();
+ buf[0] = EOS_PWR_MTYPE_BUTTON;
+ buf[1] = evt.level;
+ eos_net_send(EOS_NET_MTYPE_POWER, buf, 2);
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ vTaskDelete(NULL);
+}
+
+void eos_power_init(void) {
+ esp_err_t ret;
+ gpio_config_t io_conf;
+ esp_pm_config_esp32_t pwr_conf;
+ uint8_t wakeup_cause;
+
+ io_conf.intr_type = GPIO_INTR_ANYEDGE;
+ io_conf.mode = GPIO_MODE_INPUT;
+ io_conf.pin_bit_mask = ((uint64_t)1 << POWER_GPIO_BTN);
+ io_conf.pull_up_en = 1;
+ io_conf.pull_down_en = 0;
+ gpio_config(&io_conf);
+ gpio_isr_handler_add(POWER_GPIO_BTN, btn_handler, NULL);
+
+ /*
+ ret = esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
+ assert(ret == ESP_OK);
+ ret = esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_FAST_MEM, ESP_PD_OPTION_ON);
+ assert(ret == ESP_OK);
+ ret = esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_SLOW_MEM, ESP_PD_OPTION_ON);
+ assert(ret == ESP_OK);
+ */
+
+ ret = esp_pm_lock_create(ESP_PM_CPU_FREQ_MAX, 0, NULL, &power_lock_cpu_freq);
+ assert(ret == ESP_OK);
+ ret = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, NULL, &power_lock_apb_freq);
+ assert(ret == ESP_OK);
+ ret = esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, NULL, &power_lock_no_sleep);
+ assert(ret == ESP_OK);
+
+ ret = esp_pm_lock_acquire(power_lock_cpu_freq);
+ assert(ret == ESP_OK);
+ ret = esp_pm_lock_acquire(power_lock_apb_freq);
+ assert(ret == ESP_OK);
+ ret = esp_pm_lock_acquire(power_lock_no_sleep);
+ assert(ret == ESP_OK);
+
+ pwr_conf.max_freq_mhz = 160;
+ pwr_conf.min_freq_mhz = 80;
+ pwr_conf.light_sleep_enable = 1;
+
+ ret = esp_pm_configure(&pwr_conf);
+ assert(ret == ESP_OK);
+
+ power_queue = xQueueCreate(4, sizeof(power_event_t));
+ xTaskCreate(power_event_task, "power_event", EOS_TASK_SSIZE_PWR, NULL, EOS_TASK_PRIORITY_PWR, NULL);
+
+ wakeup_cause = eos_power_wakeup_cause();
+ if (wakeup_cause) eos_power_wake(wakeup_cause);
+
+ init_done = 1;
+ ESP_LOGI(TAG, "INIT");
+}
+
+void eos_power_wait4init(void) {
+ while (!init_done);
+}
+
+uint8_t eos_power_wakeup_cause(void) {
+ esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
+
+ switch (cause) {
+ case ESP_SLEEP_WAKEUP_EXT0:
+ return EOS_PWR_WAKE_BTN;
+
+ case ESP_SLEEP_WAKEUP_EXT1:
+ return EOS_PWR_WAKE_UART;
+
+ default:
+ return EOS_PWR_WAKE_RST;
+ }
+}
+
+void eos_power_sleep(void) {
+ power_event_t evt;
+
+ evt.type = POWER_ETYPE_SLEEP;
+ evt.source = 0;
+ xQueueSend(power_queue, &evt, portMAX_DELAY);
+}
+
+void eos_power_wake(uint8_t source) {
+ power_event_t evt;
+
+ evt.type = POWER_ETYPE_WAKE;
+ evt.source = source;
+
+ xQueueSend(power_queue, &evt, portMAX_DELAY);
+}
+
+void eos_power_net_ready(void) {
+ power_event_t evt;
+
+ evt.type = POWER_ETYPE_NETRDY;
+ evt.source = 0;
+
+ xQueueSend(power_queue, &evt, portMAX_DELAY);
+}
diff --git a/fw/esp32/components/eos/sock.c b/fw/esp32/components/eos/sock.c
new file mode 100644
index 0000000..355d7c7
--- /dev/null
+++ b/fw/esp32/components/eos/sock.c
@@ -0,0 +1,159 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <freertos/FreeRTOS.h>
+#include <freertos/semphr.h>
+#include <freertos/task.h>
+
+#include <esp_system.h>
+#include <esp_log.h>
+#include <esp_err.h>
+
+#include <lwip/sockets.h>
+#include <lwip/err.h>
+#include <lwip/sockets.h>
+#include <lwip/sys.h>
+#include <lwip/netdb.h>
+#include <lwip/dns.h>
+
+#include "eos.h"
+#include "net.h"
+#include "sock.h"
+
+static const char *TAG = "EOS SOCK";
+static SemaphoreHandle_t mutex;
+static int _socks[EOS_SOCK_MAX_SOCK];
+
+static int t_open_dgram(void) {
+ struct sockaddr_in _myaddr;
+ int sock;
+
+ sock = socket(PF_INET, SOCK_DGRAM, 0);
+ if (sock < 0) return sock;
+
+ memset((char *)&_myaddr, 0, sizeof(_myaddr));
+ _myaddr.sin_family = AF_INET;
+ _myaddr.sin_addr.s_addr = htonl(INADDR_ANY);
+ _myaddr.sin_port = htons(3000);
+
+ int rv = bind(sock, (struct sockaddr *)&_myaddr, sizeof(_myaddr));
+ if (rv < 0) {
+ close(sock);
+ return rv;
+ }
+ return sock;
+}
+
+static void t_close(int sock) {
+ close(sock);
+}
+
+static ssize_t t_sendto(int sock, void *msg, size_t msg_size, EOSNetAddr *addr) {
+ struct sockaddr_in servaddr;
+
+ memset((void *)&servaddr, 0, sizeof(servaddr));
+ servaddr.sin_family = AF_INET;
+ servaddr.sin_port = addr->port;
+ memcpy((void *)&servaddr.sin_addr, addr->host, sizeof(addr->host));
+ return sendto(sock, msg, msg_size, 0, (struct sockaddr *)&servaddr, sizeof(servaddr));
+}
+
+static ssize_t t_recvfrom(int sock, void *msg, size_t msg_size, EOSNetAddr *addr) {
+ struct sockaddr_in servaddr;
+ socklen_t addrlen = sizeof(servaddr);
+ memset((void *)&servaddr, 0, sizeof(servaddr));
+
+ ssize_t recvlen = recvfrom(sock, msg, msg_size, 0, (struct sockaddr *)&servaddr, &addrlen);
+ if (recvlen < 0) return recvlen;
+
+ if (addr) {
+ addr->port = servaddr.sin_port;
+ memcpy(addr->host, (void *)&servaddr.sin_addr, sizeof(addr->host));
+ }
+ return recvlen;
+}
+
+static void udp_rcvr_task(void *pvParameters) {
+ EOSNetAddr addr;
+ uint8_t esock = (uint8_t)pvParameters;
+ int sock = _socks[esock-1];
+ unsigned char *buf;
+
+ do {
+ ssize_t rv;
+
+ buf = eos_net_alloc();
+ rv = t_recvfrom(sock, buf + EOS_SOCK_SIZE_UDP_HDR, EOS_NET_MTU - EOS_SOCK_SIZE_UDP_HDR, &addr);
+ if (rv < 0) {
+ sock = 0;
+ eos_net_free(buf);
+ ESP_LOGE(TAG, "UDP RECV ERR:%d", rv);
+ continue;
+ }
+ buf[0] = EOS_SOCK_MTYPE_PKT;
+ buf[1] = esock;
+ memcpy(buf+2, addr.host, sizeof(addr.host));
+ memcpy(buf+2+sizeof(addr.host), &addr.port, sizeof(addr.port));
+ eos_net_send(EOS_NET_MTYPE_SOCK, buf, rv + EOS_SOCK_SIZE_UDP_HDR);
+ } while(sock);
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ _socks[esock-1] = 0;
+ xSemaphoreGive(mutex);
+ vTaskDelete(NULL);
+}
+
+static void sock_handler(unsigned char type, unsigned char *buffer, uint16_t size) {
+ EOSNetAddr addr;
+ uint8_t esock;
+ int sock, i;
+ unsigned char *rbuf;
+
+ if (size < 1) return;
+
+ switch(buffer[0]) {
+ case EOS_SOCK_MTYPE_PKT:
+ if (size < EOS_SOCK_SIZE_UDP_HDR) return;
+ sock = _socks[buffer[1] - 1];
+ memcpy(addr.host, buffer + 2, sizeof(addr.host));
+ memcpy(&addr.port, buffer + 2 + sizeof(addr.host), sizeof(addr.port));
+ t_sendto(sock, buffer + EOS_SOCK_SIZE_UDP_HDR, size - EOS_SOCK_SIZE_UDP_HDR, &addr);
+ break;
+
+ case EOS_SOCK_MTYPE_OPEN_DGRAM:
+ sock = t_open_dgram();
+ esock = 0;
+ if (sock > 0) {
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ for (i=0; i<EOS_SOCK_MAX_SOCK; i++) {
+ if (_socks[i] == 0) {
+ esock = i+1;
+ _socks[i] = sock;
+ }
+ }
+ xSemaphoreGive(mutex);
+ }
+ xTaskCreate(&udp_rcvr_task, "udp_rcvr", EOS_TASK_SSIZE_UDP_RCVR, (void *)esock, EOS_TASK_PRIORITY_UDP_RCVR, NULL);
+ rbuf = eos_net_alloc();
+ rbuf[0] = EOS_SOCK_MTYPE_OPEN_DGRAM;
+ rbuf[1] = esock;
+ eos_net_send(EOS_NET_MTYPE_SOCK, rbuf, 2);
+ break;
+
+ case EOS_SOCK_MTYPE_CLOSE:
+ if (size < 2) return;
+ sock = _socks[buffer[1]-1];
+ t_close(sock);
+ break;
+
+ default:
+ break;
+ }
+}
+
+void eos_sock_init(void) {
+ mutex = xSemaphoreCreateBinary();
+ xSemaphoreGive(mutex);
+ eos_net_set_handler(EOS_NET_MTYPE_SOCK, sock_handler);
+ ESP_LOGI(TAG, "INIT");
+} \ No newline at end of file
diff --git a/fw/esp32/components/eos/tun.c b/fw/esp32/components/eos/tun.c
new file mode 100644
index 0000000..a7181ee
--- /dev/null
+++ b/fw/esp32/components/eos/tun.c
@@ -0,0 +1,65 @@
+#include <string.h>
+#include <stdio.h>
+
+#include <lwip/pbuf.h>
+#include <lwip/netif.h>
+#include <lwip/tcpip.h>
+#include <lwip/etharp.h>
+
+#include "app.h"
+#include "tun.h"
+
+static ip4_addr_t ipaddr, netmask, gw;
+static struct netif netif_tun;
+
+static err_t ESP_IRAM_ATTR tun_output(struct netif *netif, struct pbuf *p, const struct ip4_addr *ipaddr) {
+ unsigned char *buf;
+ struct pbuf *q;
+
+ for (q = p; q != NULL; q = q->next) {
+ if (q->len > EOS_APP_MTU) continue;
+
+ buf = eos_app_alloc();
+ memcpy(buf, q->payload, q->len);
+ eos_app_send(EOS_APP_MTYPE_TUN, buf, q->len);
+ }
+
+ return ERR_OK;
+}
+
+static void ESP_IRAM_ATTR tun_input(unsigned char mtype, unsigned char *buffer, uint16_t len) {
+ struct netif *netif = &netif_tun;
+ struct pbuf *p;
+ int rv;
+
+ if (!netif_is_up(netif)) return;
+
+ p = pbuf_alloc(PBUF_RAW, len, PBUF_RAM);
+ if (p == NULL) return;
+ memcpy(p->payload, buffer, len);
+ rv = netif->input(p, netif);
+ if (rv != ERR_OK) {
+ pbuf_free(p);
+ }
+}
+
+static err_t tun_init(struct netif *netif) {
+ netif->name[0] = 't';
+ netif->name[1] = 'n';
+ netif->hostname = NULL;
+ netif->output = tun_output;
+ netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_LINK_UP;
+ netif->mtu = 1500;
+
+ return ERR_OK;
+}
+
+void eos_tun_init(void) {
+ IP4_ADDR(&gw, 0,0,0,0);
+ IP4_ADDR(&ipaddr, 192,168,10,2);
+ IP4_ADDR(&netmask, 255,255,255,0);
+
+ netif_add(&netif_tun, &ipaddr, &netmask, &gw, NULL, tun_init, tcpip_input);
+ netif_set_up(&netif_tun);
+ eos_app_set_handler(EOS_APP_MTYPE_TUN, tun_input);
+} \ No newline at end of file
diff --git a/fw/esp32/components/eos/unicode.c b/fw/esp32/components/eos/unicode.c
new file mode 120000
index 0000000..4cdffa7
--- /dev/null
+++ b/fw/esp32/components/eos/unicode.c
@@ -0,0 +1 @@
+../../../fe310/eos/unicode.c \ No newline at end of file
diff --git a/fw/esp32/components/eos/wifi.c b/fw/esp32/components/eos/wifi.c
new file mode 100755
index 0000000..d47ae3a
--- /dev/null
+++ b/fw/esp32/components/eos/wifi.c
@@ -0,0 +1,343 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <freertos/FreeRTOS.h>
+#include <freertos/task.h>
+
+#include <esp_system.h>
+#include <esp_event.h>
+#include <esp_log.h>
+#include <esp_err.h>
+#include <esp_wifi.h>
+#include <nvs_flash.h>
+
+#include "eos.h"
+#include "net.h"
+#include "wifi.h"
+
+// XXX: WiFi fail due to no DHCP server
+
+#define WIFI_MAX_SCAN_RECORDS 20
+#define WIFI_MAX_CONNECT_ATTEMPTS 3
+
+#define WIFI_STATE_STOPPED 0
+#define WIFI_STATE_SCANNING 1
+#define WIFI_STATE_CONNECTING 2
+#define WIFI_STATE_CONNECTED 3
+#define WIFI_STATE_DISCONNECTING 4
+#define WIFI_STATE_DISCONNECTED 5
+
+#define WIFI_ACTION_NONE 0
+#define WIFI_ACTION_SCAN 1
+#define WIFI_ACTION_CONNECT 2
+#define WIFI_ACTION_DISCONNECT 3
+
+static const char *TAG = "EOS WIFI";
+
+static SemaphoreHandle_t mutex;
+
+static wifi_config_t wifi_sta_config;
+static wifi_scan_config_t wifi_scan_config;
+
+static int wifi_connect_cnt = 0;
+static uint8_t wifi_action;
+static uint8_t wifi_state;
+static wifi_ap_record_t scan_r[WIFI_MAX_SCAN_RECORDS];
+static uint16_t scan_n;
+
+static void wifi_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) {
+ esp_err_t ret = ESP_OK;
+ char _disconnect;
+ uint8_t _action, _state;
+ unsigned char *rbuf, *p;
+ int i, len;
+ ip_event_got_ip_t *got_ip;
+
+ if (event_base == WIFI_EVENT) {
+ switch(event_id) {
+ case WIFI_EVENT_SCAN_DONE:
+ scan_n = WIFI_MAX_SCAN_RECORDS;
+ memset(scan_r, 0, sizeof(scan_r));
+ esp_wifi_scan_get_ap_records(&scan_n, scan_r);
+
+ ESP_LOGI(TAG, "Scan done: %d", scan_n);
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ _state = wifi_state;
+ if (wifi_state == WIFI_STATE_CONNECTED) wifi_action = WIFI_ACTION_NONE;
+ xSemaphoreGive(mutex);
+
+ if (_state != WIFI_STATE_CONNECTED) ret = esp_wifi_stop();
+
+ rbuf = eos_net_alloc();
+ rbuf[0] = EOS_WIFI_MTYPE_SCAN;
+ p = rbuf + 1;
+ for (i=0; i<scan_n; i++) {
+ len = strnlen((char *)scan_r[i].ssid, 33);
+ if (len > 32) continue;
+ if (p - rbuf + len + 1 > EOS_NET_MTU) break;
+ strcpy((char *)p, (char *)scan_r[i].ssid);
+ p += len + 1;
+ }
+ eos_net_send(EOS_NET_MTYPE_WIFI, rbuf, p - rbuf);
+ break;
+
+ case WIFI_EVENT_STA_START:
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ _action = wifi_action;
+ xSemaphoreGive(mutex);
+
+ switch (_action) {
+ case WIFI_ACTION_SCAN:
+ ret = esp_wifi_scan_start(&wifi_scan_config, 0);
+ break;
+
+ case WIFI_ACTION_CONNECT:
+ ret = esp_wifi_connect();
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case WIFI_EVENT_STA_STOP:
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ wifi_state = WIFI_STATE_STOPPED;
+ wifi_action = WIFI_ACTION_NONE;
+ xSemaphoreGive(mutex);
+ break;
+
+ case WIFI_EVENT_STA_CONNECTED:
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ wifi_state = WIFI_STATE_CONNECTED;
+ wifi_action = WIFI_ACTION_NONE;
+ wifi_connect_cnt = WIFI_MAX_CONNECT_ATTEMPTS;
+ xSemaphoreGive(mutex);
+ break;
+
+ case WIFI_EVENT_STA_DISCONNECTED:
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ if (wifi_connect_cnt) wifi_connect_cnt--;
+ _action = wifi_action;
+ _disconnect = (wifi_connect_cnt == 0);
+ if (_disconnect) wifi_state = WIFI_STATE_DISCONNECTED;
+ xSemaphoreGive(mutex);
+
+ if (_disconnect) {
+ rbuf = eos_net_alloc();
+ rbuf[0] = EOS_WIFI_MTYPE_DISCONNECT;
+ eos_net_send(EOS_NET_MTYPE_WIFI, rbuf, 1);
+ if (!_action) ret = esp_wifi_stop();
+ } else {
+ ret = esp_wifi_connect();
+ }
+ break;
+
+ default: // Ignore the other event types
+ break;
+ }
+ } else if (event_base == IP_EVENT) {
+ switch(event_id) {
+ case IP_EVENT_STA_GOT_IP:
+ got_ip = (ip_event_got_ip_t *)event_data;
+ ESP_LOGI(TAG, "IP address: " IPSTR, IP2STR(&got_ip->ip_info.ip));
+ rbuf = eos_net_alloc();
+ rbuf[0] = EOS_WIFI_MTYPE_CONNECT;
+ rbuf[1] = EOS_OK;
+ eos_net_send(EOS_NET_MTYPE_WIFI, rbuf, 2);
+
+ /* ip_changed is set even at normal connect!
+ if (got_ip->ip_changed) {
+ ESP_LOGI(TAG, "IP changed");
+ // send wifi reconnect
+ } else {
+ rbuf = eos_net_alloc();
+ rbuf[0] = EOS_WIFI_MTYPE_CONNECT;
+ rbuf[1] = EOS_OK;
+ eos_net_send(EOS_NET_MTYPE_WIFI, rbuf, 2);
+ }
+ */
+ break;
+
+ default: // Ignore the other event types
+ break;
+ }
+ }
+
+ if (ret != ESP_OK) ESP_LOGE(TAG, "EVT HANDLER ERR:%d EVT:%d", ret, event_id);
+}
+
+static void wifi_handler(unsigned char _mtype, unsigned char *buffer, uint16_t size) {
+ uint8_t mtype;
+ int rv;
+ char *ssid, *pass;
+
+ if (size < 1) return;
+
+ mtype = buffer[0];
+ rv = EOS_OK;
+ buffer += 1;
+ size -= 1;
+
+ switch (mtype) {
+ case EOS_WIFI_MTYPE_SCAN:
+ rv = eos_wifi_scan();
+ break;
+
+ case EOS_WIFI_MTYPE_CONFIG:
+ ssid = (char *)buffer;
+ pass = ssid + strlen(ssid) + 1;
+ rv = eos_wifi_set_config(ssid, pass);
+ break;
+
+ case EOS_WIFI_MTYPE_CONNECT:
+ rv = eos_wifi_connect();
+ break;
+
+ case EOS_WIFI_MTYPE_DISCONNECT:
+ rv = eos_wifi_disconnect();
+ break;
+ }
+
+ if (rv) ESP_LOGE(TAG, "MSG HANDLER ERR:%d MSG:%d", rv, mtype);
+}
+
+void eos_wifi_init(void) {
+ esp_err_t ret;
+ wifi_init_config_t wifi_config = WIFI_INIT_CONFIG_DEFAULT();
+
+ memset(&wifi_sta_config, 0, sizeof(wifi_sta_config));
+
+ esp_netif_create_default_wifi_sta();
+
+ ret = esp_wifi_init(&wifi_config);
+ assert(ret == ESP_OK);
+
+ ret = esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL, NULL);
+ assert(ret == ESP_OK);
+
+ ret = esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &wifi_event_handler, NULL, NULL);
+ assert(ret == ESP_OK);
+
+ ret = esp_wifi_set_storage(WIFI_STORAGE_RAM);
+ assert(ret == ESP_OK);
+
+ ret = esp_wifi_set_mode(WIFI_MODE_STA);
+ assert(ret == ESP_OK);
+
+ ret = esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_sta_config);
+ assert(ret == ESP_OK);
+
+ ret = esp_wifi_stop();
+ assert(ret == ESP_OK);
+
+ mutex = xSemaphoreCreateBinary();
+ xSemaphoreGive(mutex);
+
+ eos_net_set_handler(EOS_NET_MTYPE_WIFI, wifi_handler);
+ ESP_LOGI(TAG, "INIT");
+}
+
+int eos_wifi_scan(void) {
+ int rv = EOS_OK;
+ esp_err_t ret = ESP_OK;
+ uint8_t _wifi_state = 0;
+
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ if (!wifi_action) {
+ _wifi_state = wifi_state;
+
+ wifi_action = WIFI_ACTION_SCAN;
+ if (wifi_state == WIFI_STATE_STOPPED) wifi_state = WIFI_STATE_SCANNING;
+
+ memset(&wifi_scan_config, 0, sizeof(wifi_scan_config));
+ } else {
+ rv = EOS_ERR_BUSY;
+ }
+ xSemaphoreGive(mutex);
+
+ if (rv) return rv;
+
+ if (_wifi_state == WIFI_STATE_STOPPED) {
+ ret = esp_wifi_start();
+ } else {
+ ret = esp_wifi_scan_start(&wifi_scan_config, 0);
+ }
+ if (ret != ESP_OK) rv = EOS_ERR;
+
+ return rv;
+}
+
+int eos_wifi_set_config(char *ssid, char *pass) {
+ int rv = EOS_OK;
+
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ if (!wifi_action) {
+ if (ssid) strncpy((char *)wifi_sta_config.sta.ssid, ssid, sizeof(wifi_sta_config.sta.ssid) - 1);
+ if (pass) strncpy((char *)wifi_sta_config.sta.password, pass, sizeof(wifi_sta_config.sta.password) - 1);
+ } else {
+ rv = EOS_ERR_BUSY;
+
+ }
+ xSemaphoreGive(mutex);
+
+ return rv;
+}
+
+int eos_wifi_connect(void) {
+ int rv = EOS_OK;
+ esp_err_t ret = ESP_OK;
+ uint8_t _wifi_state = 0;
+
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ if (!wifi_action) {
+ _wifi_state = wifi_state;
+
+ wifi_action = WIFI_ACTION_CONNECT;
+ wifi_state = WIFI_STATE_CONNECTING;
+ wifi_connect_cnt = WIFI_MAX_CONNECT_ATTEMPTS;
+ } else {
+ rv = EOS_ERR_BUSY;
+ }
+ xSemaphoreGive(mutex);
+
+ if (rv) return rv;
+
+ esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_sta_config);
+
+ if (_wifi_state == WIFI_STATE_STOPPED) {
+ ret = esp_wifi_start();
+ } else {
+ ret = esp_wifi_connect();
+ }
+ if (ret != ESP_OK) rv = EOS_ERR;
+
+ return rv;
+}
+
+int eos_wifi_disconnect(void) {
+ int rv = EOS_OK;
+ esp_err_t ret = ESP_OK;
+
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ if (!wifi_action) {
+ wifi_action = WIFI_ACTION_DISCONNECT;
+ wifi_state = WIFI_STATE_DISCONNECTING;
+ wifi_connect_cnt = 0;
+
+ memset(wifi_sta_config.sta.ssid, 0, sizeof(wifi_sta_config.sta.ssid));
+ memset(wifi_sta_config.sta.password, 0, sizeof(wifi_sta_config.sta.password));
+ } else {
+ rv = EOS_ERR_BUSY;
+ }
+ xSemaphoreGive(mutex);
+
+ if (rv) return rv;
+
+ ret = esp_wifi_stop();
+ if (ret != ESP_OK) rv = EOS_ERR;
+
+ return rv;
+}
+
diff --git a/fw/esp32/main/app_main.c b/fw/esp32/main/app_main.c
new file mode 100644
index 0000000..1ae7f7c
--- /dev/null
+++ b/fw/esp32/main/app_main.c
@@ -0,0 +1,46 @@
+#include <freertos/FreeRTOS.h>
+#include <freertos/task.h>
+
+#include <driver/gpio.h>
+#include <esp_event.h>
+#include <esp_netif.h>
+#include <esp_err.h>
+#include <esp_log.h>
+
+#include "i2c.h"
+#include "cell.h"
+#include "_net.h"
+#include "wifi.h"
+#include "sock.h"
+#include "app.h"
+#include "tun.h"
+#include "power.h"
+
+#define ESP_INTR_FLAG_DEFAULT 0
+
+// Main application
+void app_main() {
+ esp_err_t ret;
+
+ ret = esp_netif_init();
+ assert(ret == ESP_OK);
+
+ ret = esp_event_loop_create_default();
+ assert(ret == ESP_OK);
+
+ eos_net_init();
+
+ eos_cell_pcm_init();
+ gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);
+ eos_modem_init();
+
+ eos_cell_init();
+ eos_wifi_init();
+ eos_sock_init();
+
+#ifdef EOS_WITH_APP
+ eos_app_init();
+ eos_tun_init();
+#endif
+ eos_power_init();
+}
diff --git a/fw/esp32/main/component.mk b/fw/esp32/main/component.mk
new file mode 100644
index 0000000..61f8990
--- /dev/null
+++ b/fw/esp32/main/component.mk
@@ -0,0 +1,8 @@
+#
+# Main component makefile.
+#
+# This Makefile can be left empty. By default, it will take the sources in the
+# src/ directory, compile them and link them into lib(subdirectory_name).a
+# in the build directory. This behaviour is entirely configurable,
+# please read the ESP-IDF documents if you need to do this.
+#