1
1
/*
2
- * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
2
+ * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
3
3
*
4
4
* SPDX-License-Identifier: Apache-2.0
5
5
*/
@@ -103,6 +103,49 @@ bool DCE_Mode::set_unsafe(DTE *dte, ModuleIf *device, Netif &netif, modem_mode m
103
103
return true ;
104
104
case modem_mode::DUAL_MODE: // Only DTE can be in Dual mode
105
105
break ;
106
+ case modem_mode::AUTODETECT: {
107
+ auto guessed = guess_unsafe (dte, true );
108
+ if (guessed == modem_mode::UNDEF) {
109
+ return false ;
110
+ }
111
+ // prepare the undefined mode before to allow all possible transitions
112
+ if (!dte->set_mode (modem_mode::UNDEF)) {
113
+ return false ;
114
+ }
115
+ mode = modem_mode::UNDEF;
116
+ ESP_LOGI (" DCE mode" , " Detected mode: %d" , static_cast <int >(guessed));
117
+ if (guessed == modem_mode::DATA_MODE) {
118
+ return set_unsafe (dte, device, netif, esp_modem::modem_mode::RESUME_DATA_MODE);
119
+ } else if (guessed == esp_modem::modem_mode::COMMAND_MODE) {
120
+ return set_unsafe (dte, device, netif, esp_modem::modem_mode::RESUME_COMMAND_MODE);
121
+ } else if (guessed == esp_modem::modem_mode::CMUX_MODE) {
122
+ if (!set_unsafe (dte, device, netif, esp_modem::modem_mode::RESUME_CMUX_MANUAL_MODE)) {
123
+ return false ;
124
+ }
125
+ // now we guess the mode for each terminal
126
+ guessed = guess_unsafe (dte, false );
127
+ ESP_LOGI (" DCE mode" , " Detected mode on primary term: %d" , static_cast <int >(guessed));
128
+ if (guessed == modem_mode::DATA_MODE) {
129
+ if (!set_unsafe (dte, device, netif, esp_modem::modem_mode::RESUME_CMUX_MANUAL_DATA)) {
130
+ return false ;
131
+ }
132
+ // vTaskDelay(pdMS_TO_TICKS(1000));
133
+ } else {
134
+ // swap terminals, and do the same for the secondary terminal
135
+ if (!set_unsafe (dte, device, netif, esp_modem::modem_mode::CMUX_MANUAL_SWAP)) {
136
+ return false ;
137
+ }
138
+ }
139
+ guessed = guess_unsafe (dte, false );
140
+ ESP_LOGI (" DCE mode" , " Detected mode on secondary term: %d" , static_cast <int >(guessed));
141
+ if (guessed == modem_mode::DATA_MODE) {
142
+ if (!set_unsafe (dte, device, netif, esp_modem::modem_mode::RESUME_CMUX_MANUAL_DATA)) {
143
+ return false ;
144
+ }
145
+ }
146
+ }
147
+ return true ;
148
+ }
106
149
case modem_mode::COMMAND_MODE:
107
150
if (mode == modem_mode::COMMAND_MODE || mode >= modem_mode::CMUX_MANUAL_MODE) {
108
151
return false ;
@@ -122,6 +165,35 @@ bool DCE_Mode::set_unsafe(DTE *dte, ModuleIf *device, Netif &netif, modem_mode m
122
165
}
123
166
mode = m;
124
167
return true ;
168
+ case modem_mode::RESUME_DATA_MODE:
169
+ if (!dte->set_mode (modem_mode::DATA_MODE)) {
170
+ return false ;
171
+ }
172
+ netif.start ();
173
+ mode = modem_mode::DATA_MODE;
174
+ return true ;
175
+ case modem_mode::RESUME_COMMAND_MODE:
176
+ if (!dte->set_mode (modem_mode::COMMAND_MODE)) {
177
+ return false ;
178
+ }
179
+ mode = modem_mode::COMMAND_MODE;
180
+ return true ;
181
+ case modem_mode::RESUME_CMUX_MANUAL_MODE:
182
+ if (!dte->set_mode (modem_mode::CMUX_MANUAL_MODE)) {
183
+ return false ;
184
+ }
185
+ mode = modem_mode::CMUX_MANUAL_MODE;
186
+ return true ;
187
+ case modem_mode::RESUME_CMUX_MANUAL_DATA:
188
+ if (!dte->set_mode (modem_mode::CMUX_MANUAL_SWAP)) {
189
+ return false ;
190
+ }
191
+ netif.start ();
192
+ // if (!dte->set_mode(modem_mode::CMUX_MANUAL_SWAP)) {
193
+ // return false;
194
+ // }
195
+ mode = modem_mode::CMUX_MANUAL_MODE;
196
+ return true ;
125
197
case modem_mode::DATA_MODE:
126
198
if (mode == modem_mode::DATA_MODE || mode == modem_mode::CMUX_MODE || mode >= modem_mode::CMUX_MANUAL_MODE) {
127
199
return false ;
@@ -191,4 +263,107 @@ modem_mode DCE_Mode::get()
191
263
return mode;
192
264
}
193
265
266
+ modem_mode DCE_Mode::guess (DTE *dte, bool with_cmux)
267
+ {
268
+ Scoped<DTE> lock (*dte);
269
+ return guess_unsafe (dte, with_cmux);
270
+ }
271
+
272
+ namespace probe {
273
+
274
+ namespace ppp {
275
+ const uint8_t lcp_echo_request[] = {0x7e , 0xff , 0x03 , 0xc0 , 0x21 , 0x09 , 0x01 , 0x00 , 0x08 , 0x99 , 0xd1 , 0x35 , 0xc1 , 0x8e , 0x2c , 0x7e };
276
+ const uint8_t lcp_echo_reply_head[] = {0x7e , 0xff , 0x7d , 0x23 , 0xc0 };
277
+ const size_t mode = 1 << 0 ;
278
+ const int timeout = 200 ;
279
+ }
280
+
281
+ namespace cmd {
282
+ const char at[] = " \r\n AT\r\n " ;
283
+ size_t max_at_reply = 16 ; // account for some whitespaces and/or CMUX encapsulation
284
+ const char reply[] = { ' O' , ' K' };
285
+ const int mode = 1 << 1 ;
286
+ const int timeout = 500 ;
287
+ }
288
+
289
+ namespace cmux {
290
+ const uint8_t sabm0_reqest[] = {0xf9 , 0x03 , 0x3f , 0x01 , 0x1c , 0xf9 };
291
+ const uint8_t sabm0_reply[] = {0xf9 , 0x03 , 0x73 , 0x01 };
292
+ const int mode = 1 << 0 ;
293
+ const int timeout = 200 ;
294
+ }
295
+ };
296
+
297
+ modem_mode DCE_Mode::guess_unsafe (DTE *dte, bool with_cmux)
298
+ {
299
+ // placeholder for reply and its size, since it could come in pieces, and we have to cache
300
+ // this is passed to the lambda as references.
301
+ // must make sure the lambda is cleared before exiting this function (done by dte->on_read(nullptr))
302
+ uint8_t reply[std::max (probe::cmd::max_at_reply, std::max (sizeof (probe::ppp::lcp_echo_request), sizeof (probe::cmux::sabm0_reply)))];
303
+ size_t reply_pos = 0 ;
304
+ auto signal = std::make_shared<SignalGroup>();
305
+ std::weak_ptr<SignalGroup> weak_signal = signal ;
306
+ dte->on_read ([weak_signal, with_cmux, &reply, &reply_pos](uint8_t *data, size_t len) {
307
+ // storing the response in the `reply` array and de-fragmenting
308
+ if (reply_pos >= sizeof (reply)) {
309
+ return command_result::TIMEOUT;
310
+ }
311
+ auto reply_size = std::min ((size_t )sizeof (reply) - reply_pos, len);
312
+ ::memcpy (reply + reply_pos, data, reply_size);
313
+ reply_pos += reply_size;
314
+ ESP_LOG_BUFFER_HEXDUMP (" esp-modem: guess mode data:" , reply, reply_pos, ESP_LOG_ERROR);
315
+
316
+ // Check whether the response resembles the "golden" reply (for these 3 protocols)
317
+ if (reply_pos >= sizeof (probe::ppp::lcp_echo_reply_head)) {
318
+ // check for initial 2 bytes
319
+ uint8_t *ptr = static_cast <uint8_t *>(memmem (reply, reply_pos, probe::ppp::lcp_echo_reply_head, 2 ));
320
+ // and check the other two bytes for protocol ID: LCP
321
+ if (ptr && ptr[3 ] == probe::ppp::lcp_echo_reply_head[3 ] && ptr[4 ] == probe::ppp::lcp_echo_reply_head[4 ]) {
322
+ if (auto signal = weak_signal.lock ()) {
323
+ signal ->set (probe::ppp::mode);
324
+ }
325
+ }
326
+ }
327
+ if (reply_pos >= 4 && memmem (reply, reply_pos, probe::cmd::reply, sizeof (probe::cmd::reply))) {
328
+ if (reply[0 ] != 0xf9 ) { // double check that the reply is not wrapped in CMUX headers
329
+ if (auto signal = weak_signal.lock ()) {
330
+ signal ->set (probe::cmd::mode);
331
+ }
332
+ }
333
+ }
334
+ if (with_cmux && reply_pos >= sizeof (probe::cmux::sabm0_reply)) {
335
+ // checking the initial 3 bytes
336
+ uint8_t *ptr = static_cast <uint8_t *>(memmem (reply, reply_pos, probe::cmux::sabm0_reply, 3 ));
337
+ // and checking that DLCI is 0 (control frame)
338
+ if (ptr && (ptr[3 ] >> 2 ) == 0 ) {
339
+ if (auto signal = weak_signal.lock ()) {
340
+ signal ->set (probe::cmux::mode);
341
+ }
342
+ }
343
+ }
344
+ return command_result::TIMEOUT;
345
+ });
346
+ auto guessed = modem_mode::UNDEF;
347
+ // Check the PPP mode fist by sending LCP echo request
348
+ dte->send ((uint8_t *)probe::ppp::lcp_echo_request, sizeof (probe::ppp::lcp_echo_request), 0 );
349
+ if (signal ->wait (probe::ppp::mode, probe::ppp::timeout)) {
350
+ guessed = modem_mode::DATA_MODE;
351
+ } else { // LCP echo timeout
352
+ // now check for AT mode
353
+ reply_pos = 0 ;
354
+ dte->send ((uint8_t *)probe::cmd::at, sizeof (probe::cmd::at), 0 );
355
+ if (signal ->wait (probe::cmd::mode, probe::cmd::timeout)) {
356
+ guessed = modem_mode::COMMAND_MODE;
357
+ } else if (with_cmux) { // no AT reply, check for CMUX mode (if requested)
358
+ reply_pos = 0 ;
359
+ dte->send ((uint8_t *) probe::cmux::sabm0_reqest, sizeof (probe::cmux::sabm0_reqest), 0 );
360
+ if (signal ->wait (probe::cmux::mode, probe::cmux::timeout)) {
361
+ guessed = modem_mode::CMUX_MODE;
362
+ }
363
+ }
364
+ }
365
+ dte->on_read (nullptr );
366
+ return guessed;
367
+ }
368
+
194
369
} // esp_modem
0 commit comments