1 // Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 #include <stdlib.h>
15 #include <string.h>
16 #include "esp_log.h"
17 #include "esp_modem_dce_service.h"
18 #include "bg96.h"
19 
20 #define MODEM_RESULT_CODE_POWERDOWN "POWERED DOWN"
21 
22 static const char *DCE_TAG = "bg96";
23 
24 /**
25  * @brief Macro defined for error checking
26  *
27  */
28 #define DCE_CHECK(a, str, goto_tag, ...)                                              \
29     do                                                                                \
30     {                                                                                 \
31         if (!(a))                                                                     \
32         {                                                                             \
33             ESP_LOGE(DCE_TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
34             goto goto_tag;                                                            \
35         }                                                                             \
36     } while (0)
37 
38 /**
39  * @brief Handle response from AT+QPOWD=1
40  */
bg96_handle_power_down(modem_dce_t * dce,const char * line)41 static esp_err_t bg96_handle_power_down(modem_dce_t *dce, const char *line)
42 {
43     esp_err_t err = ESP_FAIL;
44     if (strstr(line, MODEM_RESULT_CODE_SUCCESS)) {
45         err = ESP_OK;
46     } else if (strstr(line, MODEM_RESULT_CODE_POWERDOWN)) {
47         err = esp_modem_process_command_done(dce, MODEM_STATE_SUCCESS);
48     }
49     return err;
50 }
51 
52 /**
53  * @brief Set Working Mode
54  *
55  * @param dce Modem DCE object
56  * @param mode woking mode
57  * @return esp_err_t
58  *      - ESP_OK on success
59  *      - ESP_FAIL on error
60  */
bg96_set_working_mode(modem_dce_t * dce,modem_mode_t mode)61 static esp_err_t bg96_set_working_mode(modem_dce_t *dce, modem_mode_t mode)
62 {
63     modem_dte_t *dte = dce->dte;
64     switch (mode) {
65     case MODEM_COMMAND_MODE:
66         vTaskDelay(pdMS_TO_TICKS(1000)); // spec: 1s delay for the modem to recognize the escape sequence
67         dce->handle_line = esp_modem_dce_handle_exit_data_mode;
68         if (dte->send_cmd(dte, "+++", MODEM_COMMAND_TIMEOUT_MODE_CHANGE) != ESP_OK) {
69             // "+++" Could fail if we are already in the command mode.
70             // in that case we ignore the timeout and re-sync the modem
71             ESP_LOGI(DCE_TAG, "Sending \"+++\" command failed");
72             dce->handle_line = esp_modem_dce_handle_response_default;
73             DCE_CHECK(dte->send_cmd(dte, "AT\r", MODEM_COMMAND_TIMEOUT_DEFAULT) == ESP_OK, "send command failed", err);
74             DCE_CHECK(dce->state == MODEM_STATE_SUCCESS, "sync failed", err);
75         } else {
76             DCE_CHECK(dce->state == MODEM_STATE_SUCCESS, "enter command mode failed", err);
77         }
78         ESP_LOGD(DCE_TAG, "enter command mode ok");
79         dce->mode = MODEM_COMMAND_MODE;
80         break;
81     case MODEM_PPP_MODE:
82         dce->handle_line = esp_modem_dce_handle_atd_ppp;
83         DCE_CHECK(dte->send_cmd(dte, "ATD*99***1#\r", MODEM_COMMAND_TIMEOUT_MODE_CHANGE) == ESP_OK, "send command failed", err);
84         if (dce->state != MODEM_STATE_SUCCESS) {
85             // Initiate PPP mode could fail, if we've already "dialed" the data call before.
86             // in that case we retry with "ATO" to just resume the data mode
87             ESP_LOGD(DCE_TAG, "enter ppp mode failed, retry with ATO");
88             dce->handle_line = esp_modem_dce_handle_atd_ppp;
89             DCE_CHECK(dte->send_cmd(dte, "ATO\r", MODEM_COMMAND_TIMEOUT_MODE_CHANGE) == ESP_OK, "send command failed", err);
90             DCE_CHECK(dce->state == MODEM_STATE_SUCCESS, "enter ppp mode failed", err);
91         }
92         ESP_LOGD(DCE_TAG, "enter ppp mode ok");
93         dce->mode = MODEM_PPP_MODE;
94         break;
95     default:
96         ESP_LOGW(DCE_TAG, "unsupported working mode: %d", mode);
97         goto err;
98         break;
99     }
100     return ESP_OK;
101 err:
102     return ESP_FAIL;
103 }
104 
105 /**
106  * @brief Power down
107  *
108  * @param bg96_dce bg96 object
109  * @return esp_err_t
110  *      - ESP_OK on success
111  *      - ESP_FAIL on error
112  */
bg96_power_down(modem_dce_t * dce)113 static esp_err_t bg96_power_down(modem_dce_t *dce)
114 {
115     modem_dte_t *dte = dce->dte;
116     dce->handle_line = bg96_handle_power_down;
117     DCE_CHECK(dte->send_cmd(dte, "AT+QPOWD=1\r", MODEM_COMMAND_TIMEOUT_POWEROFF) == ESP_OK, "send command failed", err);
118     DCE_CHECK(dce->state == MODEM_STATE_SUCCESS, "power down failed", err);
119     ESP_LOGD(DCE_TAG, "power down ok");
120     return ESP_OK;
121 err:
122     return ESP_FAIL;
123 }
124 
125 /**
126  * @brief Deinitialize BG96 object
127  *
128  * @param dce Modem DCE object
129  * @return esp_err_t
130  *      - ESP_OK on success
131  *      - ESP_FAIL on fail
132  */
bg96_deinit(modem_dce_t * dce)133 static esp_err_t bg96_deinit(modem_dce_t *dce)
134 {
135     esp_modem_dce_t *esp_modem_dce = __containerof(dce, esp_modem_dce_t, parent);
136     if (dce->dte) {
137         dce->dte->dce = NULL;
138     }
139     free(esp_modem_dce);
140     return ESP_OK;
141 }
142 
bg96_init(modem_dte_t * dte)143 modem_dce_t *bg96_init(modem_dte_t *dte)
144 {
145     DCE_CHECK(dte, "DCE should bind with a DTE", err);
146     /* malloc memory for esp_modem_dce object */
147     esp_modem_dce_t *esp_modem_dce = calloc(1, sizeof(esp_modem_dce_t));
148     DCE_CHECK(esp_modem_dce, "calloc bg96_dce failed", err);
149     /* Bind DTE with DCE */
150     esp_modem_dce->parent.dte = dte;
151     dte->dce = &(esp_modem_dce->parent);
152     /* Bind methods */
153     esp_modem_dce->parent.handle_line = NULL;
154     esp_modem_dce->parent.sync = esp_modem_dce_sync;
155     esp_modem_dce->parent.echo_mode = esp_modem_dce_echo;
156     esp_modem_dce->parent.store_profile = esp_modem_dce_store_profile;
157     esp_modem_dce->parent.set_flow_ctrl = esp_modem_dce_set_flow_ctrl;
158     esp_modem_dce->parent.define_pdp_context = esp_modem_dce_define_pdp_context;
159     esp_modem_dce->parent.hang_up = esp_modem_dce_hang_up;
160     esp_modem_dce->parent.get_signal_quality = esp_modem_dce_get_signal_quality;
161     esp_modem_dce->parent.get_battery_status = esp_modem_dce_get_battery_status;
162     esp_modem_dce->parent.get_operator_name = esp_modem_dce_get_operator_name;
163     esp_modem_dce->parent.set_working_mode = bg96_set_working_mode;
164     esp_modem_dce->parent.power_down = bg96_power_down;
165     esp_modem_dce->parent.deinit = bg96_deinit;
166     /* Sync between DTE and DCE */
167     DCE_CHECK(esp_modem_dce_sync(&(esp_modem_dce->parent)) == ESP_OK, "sync failed", err_io);
168     /* Close echo */
169     DCE_CHECK(esp_modem_dce_echo(&(esp_modem_dce->parent), false) == ESP_OK, "close echo mode failed", err_io);
170     /* Get Module name */
171     DCE_CHECK(esp_modem_dce_get_module_name(&(esp_modem_dce->parent)) == ESP_OK, "get module name failed", err_io);
172     /* Get IMEI number */
173     DCE_CHECK(esp_modem_dce_get_imei_number(&(esp_modem_dce->parent)) == ESP_OK, "get imei failed", err_io);
174     /* Get IMSI number */
175     DCE_CHECK(esp_modem_dce_get_imsi_number(&(esp_modem_dce->parent)) == ESP_OK, "get imsi failed", err_io);
176     /* Get operator name */
177     DCE_CHECK(esp_modem_dce_get_operator_name(&(esp_modem_dce->parent)) == ESP_OK, "get operator name failed", err_io);
178     return &(esp_modem_dce->parent);
179 err_io:
180     free(esp_modem_dce);
181     dte->dce = NULL;
182 err:
183     return NULL;
184 }
185