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