diff options
Diffstat (limited to 'fw/esp32')
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. +# |