Kenji Arai / mbed-os_TYBLE16

Dependents:   TYBLE16_simple_data_logger TYBLE16_MP3_Air

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers QUECTEL_BG96_CellularStack.cpp Source File

QUECTEL_BG96_CellularStack.cpp

00001 /*
00002  * Copyright (c) 2017, Arm Limited and affiliates.
00003  * SPDX-License-Identifier: Apache-2.0
00004  *
00005  * Licensed under the Apache License, Version 2.0 (the "License");
00006  * you may not use this file except in compliance with the License.
00007  * You may obtain a copy of the License at
00008  *
00009  *     http://www.apache.org/licenses/LICENSE-2.0
00010  *
00011  * Unless required by applicable law or agreed to in writing, software
00012  * distributed under the License is distributed on an "AS IS" BASIS,
00013  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00014  * See the License for the specific language governing permissions and
00015  * limitations under the License.
00016  */
00017 
00018 #include <stdio.h>
00019 #include <string.h>
00020 #include "QUECTEL/BG96/QUECTEL_BG96_CellularStack.h"
00021 #include "CellularLog.h"
00022 #include "netsocket/TLSSocket.h"
00023 
00024 // Ref: Quectel_BG96_SSL_AT_Commands_Manual, ch 2.1.1 AT+QSSLCFG
00025 static const int BG96_SUPPORTED_SSL_VERSION     = 4; // All
00026 static const char BG96_SUPPORTED_CIPHER_SUITE[] = "0xFFFF"; // Support all
00027 
00028 // TODO: At the moment we support only one active SSL context
00029 //       Later can be expanded to support multiple contexts. Modem supports IDs 0-5.
00030 static const int sslctxID = 0;
00031 
00032 using namespace mbed;
00033 
00034 QUECTEL_BG96_CellularStack::QUECTEL_BG96_CellularStack(ATHandler &atHandler, int cid, nsapi_ip_stack_t stack_type, AT_CellularDevice &device) :
00035     AT_CellularStack(atHandler, cid, stack_type, device)
00036 #ifdef MBED_CONF_CELLULAR_OFFLOAD_DNS_QUERIES
00037 #if (MBED_CONF_CELLULAR_OFFLOAD_DNS_QUERIES != 1)
00038 #error Define cellular.offload-dns-queries to null or 1.
00039 #endif
00040     , _dns_callback(NULL), _dns_version(NSAPI_UNSPEC )
00041 #endif
00042     , _tls_sec_level(0)
00043 {
00044     _at.set_urc_handler("+QIURC: \"recv", mbed::Callback<void()>(this, &QUECTEL_BG96_CellularStack::urc_qiurc_recv));
00045     _at.set_urc_handler("+QIURC: \"close", mbed::Callback<void()>(this, &QUECTEL_BG96_CellularStack::urc_qiurc_closed));
00046 #ifdef MBED_CONF_CELLULAR_OFFLOAD_DNS_QUERIES
00047     _at.set_urc_handler("+QIURC: \"dnsgip\",", mbed::Callback<void()>(this, &QUECTEL_BG96_CellularStack::urc_qiurc_dnsgip));
00048 #endif
00049 
00050     _at.set_urc_handler("+QSSLURC: \"recv", mbed::Callback<void()>(this, &QUECTEL_BG96_CellularStack::urc_qiurc_recv));
00051     _at.set_urc_handler("+QSSLURC: \"close", mbed::Callback<void()>(this, &QUECTEL_BG96_CellularStack::urc_qiurc_closed));
00052 
00053     // TODO: this needs to be handled properly, but now making just a quick hack
00054     // Close all SSL sockets if open. This can happen for example if application processor
00055     // was reset but modem not. Old sockets are still up and running and it prevents
00056     // new SSL configurations and creating new sockets.
00057     for (int i = 0; i < 12; i++) {
00058         _at.clear_error();
00059         tr_debug("Closing SSL socket %d...", i);
00060         _at.at_cmd_discard("+QSSLCLOSE", "=", "%d", i);
00061     }
00062     _at.clear_error();
00063 }
00064 
00065 QUECTEL_BG96_CellularStack::~QUECTEL_BG96_CellularStack()
00066 {
00067 }
00068 
00069 nsapi_error_t QUECTEL_BG96_CellularStack::socket_listen(nsapi_socket_t handle, int backlog)
00070 {
00071     return NSAPI_ERROR_UNSUPPORTED ;
00072 }
00073 
00074 nsapi_error_t QUECTEL_BG96_CellularStack::socket_accept(void *server, void **socket, SocketAddress *addr)
00075 {
00076     return NSAPI_ERROR_UNSUPPORTED ;
00077 }
00078 
00079 nsapi_error_t QUECTEL_BG96_CellularStack::socket_connect(nsapi_socket_t handle, const SocketAddress &address)
00080 {
00081     CellularSocket *socket = (CellularSocket *)handle;
00082 
00083     int modem_connect_id = -1;
00084     int err = NSAPI_ERROR_NO_CONNECTION ;
00085 
00086     int request_connect_id = find_socket_index(socket);
00087     // assert here as its a programming error if the socket container doesn't contain
00088     // specified handle
00089     MBED_ASSERT(request_connect_id != -1);
00090 
00091     _at.lock();
00092     if (socket->proto == NSAPI_TCP ) {
00093         if (socket->tls_socket) {
00094             if (_tls_sec_level == 0) {
00095                 _at.unlock();
00096                 return NSAPI_ERROR_AUTH_FAILURE ;
00097             }
00098 
00099             _at.at_cmd_discard("+QSSLOPEN", "=", "%d%d%d%s%d%d", _cid, sslctxID, request_connect_id,
00100                                address.get_ip_address(), address.get_port(), 0);
00101             handle_open_socket_response(modem_connect_id, err, true);
00102 
00103             if ((_at.get_last_error() == NSAPI_ERROR_OK ) && err) {
00104                 if (err == BG96_SOCKET_BIND_FAIL) {
00105                     socket->id = -1;
00106                     _at.unlock();
00107                     return NSAPI_ERROR_PARAMETER ;
00108                 }
00109                 socket_close_impl(modem_connect_id);
00110 
00111                 _at.at_cmd_discard("+QSSLOPEN", "=", "%d%d%d%s%d%d", _cid, sslctxID, request_connect_id,
00112                                    address.get_ip_address(), address.get_port(), 0);
00113                 handle_open_socket_response(modem_connect_id, err, true);
00114             }
00115         } else {
00116             char ipdot[NSAPI_IP_SIZE];
00117             ip2dot(address, ipdot);
00118             _at.at_cmd_discard("+QIOPEN", "=", "%d%d%s%s%d%d%d", _cid, request_connect_id, "TCP",
00119                                ipdot, address.get_port(), socket->localAddress.get_port(), 0);
00120 
00121             handle_open_socket_response(modem_connect_id, err, false);
00122 
00123             if ((_at.get_last_error() == NSAPI_ERROR_OK ) && err) {
00124                 if (err == BG96_SOCKET_BIND_FAIL) {
00125                     socket->id = -1;
00126                     _at.unlock();
00127                     return NSAPI_ERROR_PARAMETER ;
00128                 }
00129                 socket_close_impl(modem_connect_id);
00130 
00131                 _at.at_cmd_discard("+QIOPEN", "=", "%d%d%s%s%d%d%d", _cid, request_connect_id, "TCP",
00132                                    ipdot, address.get_port(), socket->localAddress.get_port(), 0);
00133 
00134                 handle_open_socket_response(modem_connect_id, err, false);
00135             }
00136         }
00137     }
00138 
00139     // If opened successfully BUT not requested one, close it
00140     if (!err && (modem_connect_id != request_connect_id)) {
00141         _at.at_cmd_discard("+QICLOSE", "=", "%d", modem_connect_id);
00142     }
00143 
00144     nsapi_error_t ret_val = _at.get_last_error();
00145     _at.unlock();
00146 
00147     if ((!err) && (ret_val == NSAPI_ERROR_OK ) && (modem_connect_id == request_connect_id)) {
00148         socket->id = request_connect_id;
00149         socket->remoteAddress = address;
00150         socket->connected = true;
00151         return NSAPI_ERROR_OK ;
00152     }
00153 
00154     return err;
00155 }
00156 
00157 void QUECTEL_BG96_CellularStack::urc_qiurc_recv()
00158 {
00159     urc_qiurc(URC_RECV);
00160 }
00161 
00162 void QUECTEL_BG96_CellularStack::urc_qiurc_closed()
00163 {
00164     urc_qiurc(URC_CLOSED);
00165 }
00166 
00167 #ifdef MBED_CONF_CELLULAR_OFFLOAD_DNS_QUERIES
00168 bool QUECTEL_BG96_CellularStack::read_dnsgip(SocketAddress &address, nsapi_version_t _dns_version)
00169 {
00170     if (_at.read_int() == 0) {
00171         int count = _at.read_int();
00172         _at.skip_param();
00173         for (; count > 0; count--) {
00174             _at.resp_start("+QIURC: \"dnsgip\",");
00175             char ipAddress[NSAPI_IP_SIZE];
00176             _at.read_string(ipAddress, sizeof(ipAddress));
00177             if (address.set_ip_address(ipAddress)) {
00178                 if (_dns_version == NSAPI_UNSPEC  || _dns_version == address.get_ip_version()) {
00179                     return true;
00180                 }
00181             }
00182         }
00183     }
00184     return false;
00185 }
00186 
00187 void QUECTEL_BG96_CellularStack::urc_qiurc_dnsgip()
00188 {
00189     if (!_dns_callback) {
00190         return;
00191     }
00192     SocketAddress address;
00193     if (read_dnsgip(address, _dns_version)) {
00194         _dns_callback(NSAPI_ERROR_OK , &address);
00195     } else {
00196         _dns_callback(NSAPI_ERROR_DNS_FAILURE , NULL);
00197     }
00198     _dns_callback = NULL;
00199 }
00200 #endif
00201 
00202 void QUECTEL_BG96_CellularStack::urc_qiurc(urc_type_t urc_type)
00203 {
00204     _at.lock();
00205     _at.skip_param();
00206     const int sock_id = _at.read_int();
00207     const nsapi_error_t err = _at.unlock_return_error();
00208 
00209     if (err != NSAPI_ERROR_OK ) {
00210         return;
00211     }
00212 
00213     CellularSocket *sock = find_socket(sock_id);
00214     if (sock) {
00215         if (urc_type == URC_CLOSED) {
00216             tr_info("Socket closed %d", sock_id);
00217             sock->closed = true;
00218         }
00219         if (sock->_cb) {
00220             sock->_cb(sock->_data);
00221         }
00222     }
00223 }
00224 
00225 int QUECTEL_BG96_CellularStack::get_max_socket_count()
00226 {
00227     return BG96_SOCKET_MAX;
00228 }
00229 
00230 bool QUECTEL_BG96_CellularStack::is_protocol_supported(nsapi_protocol_t protocol)
00231 {
00232     return (protocol == NSAPI_UDP  || protocol == NSAPI_TCP );
00233 }
00234 
00235 nsapi_error_t QUECTEL_BG96_CellularStack::socket_close_impl(int sock_id)
00236 {
00237     _at.set_at_timeout(BG96_CLOSE_SOCKET_TIMEOUT);
00238     nsapi_error_t err;
00239     CellularSocket *socket = find_socket(sock_id);
00240     if (socket && socket->tls_socket) {
00241         err = _at.at_cmd_discard("+QSSLCLOSE", "=", "%d", sock_id);
00242         if (err == NSAPI_ERROR_OK ) {
00243             // Disable TLSSocket settings to prevent reuse on next socket without setting the values
00244             _tls_sec_level = 0;
00245             err = _at.at_cmd_discard("+QSSLCFG", "=\"seclevel\",", "%d%d", sslctxID, _tls_sec_level);
00246         }
00247     } else {
00248         err = _at.at_cmd_discard("+QICLOSE", "=", "%d", sock_id);
00249     }
00250     _at.restore_at_timeout();
00251 
00252     return err;
00253 }
00254 
00255 void QUECTEL_BG96_CellularStack::handle_open_socket_response(int &modem_connect_id, int &err, bool tlssocket)
00256 {
00257     // OK
00258     // QIOPEN -> should be handled as URC?
00259     _at.set_at_timeout(BG96_CREATE_SOCKET_TIMEOUT);
00260 
00261     if (tlssocket) {
00262         _at.resp_start("+QSSLOPEN:");
00263     } else {
00264         _at.resp_start("+QIOPEN:");
00265     }
00266 
00267     _at.restore_at_timeout();
00268     modem_connect_id = _at.read_int();
00269     err = _at.read_int();
00270     if (tlssocket && err != 0) {
00271         err = NSAPI_ERROR_AUTH_FAILURE ;
00272     }
00273 }
00274 
00275 nsapi_error_t QUECTEL_BG96_CellularStack::create_socket_impl(CellularSocket *socket)
00276 {
00277     int modem_connect_id = -1;
00278     int remote_port = 0;
00279     int err = -1;
00280 
00281     int request_connect_id = find_socket_index(socket);
00282     // assert here as its a programming error if the socket container doesn't contain
00283     // specified handle
00284     MBED_ASSERT(request_connect_id != -1);
00285 
00286     if (socket->proto == NSAPI_UDP  && !socket->connected) {
00287         _at.at_cmd_discard("+QIOPEN", "=", "%d%d%s%s%d%d%d", _cid, request_connect_id, "UDP SERVICE",
00288                            (_ip_ver_sendto == NSAPI_IPv4 ) ? "127.0.0.1" : "0:0:0:0:0:0:0:1",
00289                            remote_port, socket->localAddress.get_port(), 0);
00290 
00291         handle_open_socket_response(modem_connect_id, err, false);
00292 
00293         if ((_at.get_last_error() == NSAPI_ERROR_OK ) && err) {
00294             if (err == BG96_SOCKET_BIND_FAIL) {
00295                 socket->id = -1;
00296                 return NSAPI_ERROR_PARAMETER ;
00297             }
00298             socket_close_impl(modem_connect_id);
00299 
00300             _at.at_cmd_discard("+QIOPEN", "=", "%d%d%s%s%d%d%d", _cid, request_connect_id, "UDP SERVICE",
00301                                (_ip_ver_sendto == NSAPI_IPv4 ) ? "127.0.0.1" : "0:0:0:0:0:0:0:1",
00302                                remote_port, socket->localAddress.get_port(), 0);
00303 
00304             handle_open_socket_response(modem_connect_id, err, false);
00305         }
00306     } else if (socket->proto == NSAPI_UDP  && socket->connected) {
00307         char ipdot[NSAPI_IP_SIZE];
00308         ip2dot(socket->remoteAddress, ipdot);
00309         _at.at_cmd_discard("+QIOPEN", "=", "%d%d%s%s%d", _cid, request_connect_id, "UDP",
00310                            ipdot, socket->remoteAddress.get_port());
00311 
00312         handle_open_socket_response(modem_connect_id, err, false);
00313 
00314         if ((_at.get_last_error() == NSAPI_ERROR_OK ) && err) {
00315             if (err == BG96_SOCKET_BIND_FAIL) {
00316                 socket->id = -1;
00317                 return NSAPI_ERROR_PARAMETER ;
00318             }
00319             socket_close_impl(modem_connect_id);
00320 
00321             _at.at_cmd_discard("+QIOPEN", "=", "%d%d%s%s%d", _cid, request_connect_id, "UDP",
00322                                ipdot, socket->remoteAddress.get_port());
00323 
00324             handle_open_socket_response(modem_connect_id, err, false);
00325         }
00326     }
00327 
00328     // If opened successfully BUT not requested one, close it
00329     if (!err && (modem_connect_id != request_connect_id)) {
00330         socket_close_impl(modem_connect_id);
00331     }
00332 
00333     nsapi_error_t ret_val = _at.get_last_error();
00334 
00335     if (ret_val == NSAPI_ERROR_OK  && (modem_connect_id == request_connect_id)) {
00336         socket->id = request_connect_id;
00337     }
00338 
00339     return ret_val;
00340 }
00341 
00342 nsapi_size_or_error_t QUECTEL_BG96_CellularStack::socket_sendto_impl(CellularSocket *socket, const SocketAddress &address,
00343                                                                      const void *data, nsapi_size_t size)
00344 {
00345     if (size > BG96_MAX_SEND_SIZE) {
00346         return NSAPI_ERROR_PARAMETER ;
00347     }
00348 
00349     if (_ip_ver_sendto != address.get_ip_version()) {
00350         _ip_ver_sendto =  address.get_ip_version();
00351         socket_close_impl(socket->id);
00352         create_socket_impl(socket);
00353     }
00354 
00355     int sent_len = 0;
00356     int sent_len_before = 0;
00357     int sent_len_after = 0;
00358 
00359     if (socket->tls_socket) {
00360         sent_len_after = size;
00361     } else {
00362         // Get the sent count before sending
00363         _at.at_cmd_int("+QISEND", "=", sent_len_before, "%d%d", socket->id, 0);
00364     }
00365 
00366     // Send
00367     if (socket->proto == NSAPI_UDP ) {
00368         char ipdot[NSAPI_IP_SIZE];
00369         ip2dot(address, ipdot);
00370         _at.cmd_start_stop("+QISEND", "=", "%d%d%s%d", socket->id, size,
00371                            ipdot, address.get_port());
00372     } else {
00373         if (socket->tls_socket) {
00374             _at.cmd_start_stop("+QSSLSEND", "=", "%d%d", socket->id, size);
00375         } else {
00376             _at.cmd_start_stop("+QISEND", "=", "%d%d", socket->id, size);
00377         }
00378     }
00379 
00380     _at.resp_start(">");
00381     _at.write_bytes((uint8_t *)data, size);
00382     _at.resp_start();
00383     _at.set_stop_tag("\r\n");
00384     // Possible responses are SEND OK, SEND FAIL or ERROR.
00385     char response[16];
00386     response[0] = '\0';
00387     _at.read_string(response, sizeof(response));
00388     _at.resp_stop();
00389     if (strcmp(response, "SEND OK") != 0) {
00390         return NSAPI_ERROR_DEVICE_ERROR ;
00391     }
00392 
00393     // Get the sent count after sending
00394     nsapi_size_or_error_t err = NSAPI_ERROR_OK ;
00395 
00396     if (!socket->tls_socket) {
00397         err = _at.at_cmd_int("+QISEND", "=", sent_len_after, "%d%d", socket->id, 0);
00398     }
00399 
00400     if (err == NSAPI_ERROR_OK ) {
00401         sent_len = sent_len_after - sent_len_before;
00402         return sent_len;
00403     }
00404 
00405     return err;
00406 }
00407 
00408 nsapi_size_or_error_t QUECTEL_BG96_CellularStack::socket_recvfrom_impl(CellularSocket *socket, SocketAddress *address,
00409                                                                        void *buffer, nsapi_size_t size)
00410 {
00411     nsapi_size_or_error_t recv_len = 0;
00412     int port = -1;
00413     char ip_address[NSAPI_IP_SIZE + 1];
00414 
00415     if (socket->proto == NSAPI_TCP ) {
00416         // do not read more than max size
00417         size = size > BG96_MAX_RECV_SIZE ? BG96_MAX_RECV_SIZE : size;
00418         if (socket->tls_socket) {
00419             _at.cmd_start_stop("+QSSLRECV", "=", "%d%d", socket->id, size);
00420         } else {
00421             _at.cmd_start_stop("+QIRD", "=", "%d%d", socket->id, size);
00422         }
00423     } else {
00424         _at.cmd_start_stop("+QIRD", "=", "%d", socket->id);
00425     }
00426 
00427     if (socket->tls_socket) {
00428         _at.resp_start("+QSSLRECV:");
00429     } else {
00430         _at.resp_start("+QIRD:");
00431     }
00432 
00433     recv_len = _at.read_int();
00434     if (recv_len > 0) {
00435         // UDP has remote_IP and remote_port parameters
00436         if (socket->proto == NSAPI_UDP ) {
00437             _at.read_string(ip_address, sizeof(ip_address));
00438             port = _at.read_int();
00439         }
00440         // do not read more than buffer size
00441         recv_len = recv_len > (nsapi_size_or_error_t)size ? size : recv_len;
00442         _at.read_bytes((uint8_t *)buffer, recv_len);
00443     }
00444     _at.resp_stop();
00445 
00446     // We block only if 0 recv length really means no data.
00447     // If 0 is followed by ip address and port can be an UDP 0 length packet
00448     if (!recv_len && port < 0) {
00449         return NSAPI_ERROR_WOULD_BLOCK ;
00450     }
00451 
00452     if (address) {
00453         address->set_ip_address(ip_address);
00454         address->set_port(port);
00455     }
00456 
00457     return recv_len;
00458 }
00459 
00460 #ifdef MBED_CONF_CELLULAR_OFFLOAD_DNS_QUERIES
00461 nsapi_error_t QUECTEL_BG96_CellularStack::gethostbyname(const char *host, SocketAddress *address,
00462                                                         nsapi_version_t version, const char *interface_name)
00463 {
00464     (void) interface_name;
00465     MBED_ASSERT(host);
00466     MBED_ASSERT(address);
00467 
00468     _at.lock();
00469 
00470     if (_dns_callback) {
00471         _at.unlock();
00472         return NSAPI_ERROR_BUSY ;
00473     }
00474 
00475     if (!address->set_ip_address(host)) {
00476         _at.set_at_timeout(60 * 1000); // from BG96_TCP/IP_AT_Commands_Manual_V1.0
00477         _at.at_cmd_discard("+QIDNSGIP", "=", "%d%s", _cid, host);
00478         _at.resp_start("+QIURC: \"dnsgip\",");
00479         _at.restore_at_timeout();
00480         if (!read_dnsgip(*address, version)) {
00481             _at.unlock();
00482             return NSAPI_ERROR_DNS_FAILURE ;
00483         }
00484     }
00485 
00486     return _at.unlock_return_error();
00487 }
00488 
00489 nsapi_value_or_error_t QUECTEL_BG96_CellularStack::gethostbyname_async(const char *host, hostbyname_cb_t callback,
00490                                                                        nsapi_version_t version, const char *interface_name)
00491 {
00492     (void) interface_name;
00493     MBED_ASSERT(host);
00494     MBED_ASSERT(callback);
00495 
00496     _at.lock();
00497 
00498     if (_dns_callback) {
00499         _at.unlock();
00500         return NSAPI_ERROR_BUSY ;
00501     }
00502 
00503     _at.at_cmd_discard("+QIDNSGIP", "=", "%d%s", _cid, host);
00504     if (!_at.get_last_error()) {
00505         _dns_callback = callback;
00506         _dns_version = version;
00507     }
00508 
00509     return _at.unlock_return_error() ? NSAPI_ERROR_DNS_FAILURE  : NSAPI_ERROR_OK ;
00510 }
00511 
00512 nsapi_error_t QUECTEL_BG96_CellularStack::gethostbyname_async_cancel(int id)
00513 {
00514     _at.lock();
00515     _dns_callback = NULL;
00516     _at.unlock();
00517     return NSAPI_ERROR_OK ;
00518 }
00519 #endif
00520 
00521 void QUECTEL_BG96_CellularStack::ip2dot(const SocketAddress &ip, char *dot)
00522 {
00523     if (ip.get_ip_version() == NSAPI_IPv6 ) {
00524         const uint8_t *bytes = (uint8_t *)ip.get_ip_bytes();
00525         for (int i = 0; i < NSAPI_IPv6_BYTES; i += 2) {
00526             if (i != 0) {
00527                 *dot++ = ':';
00528             }
00529             dot += sprintf(dot, "%x", (*(bytes + i) << 8 | *(bytes + i + 1)));
00530         }
00531     } else if (ip.get_ip_version() == NSAPI_IPv4 ) {
00532         strcpy(dot, ip.get_ip_address());
00533     } else {
00534         *dot = '\0';
00535     }
00536 }
00537 
00538 nsapi_error_t QUECTEL_BG96_CellularStack::set_to_modem_impl(const char *filename, const char *config, const char *data, size_t size)
00539 {
00540     // Delete old file from the modem.
00541     _at.at_cmd_discard("+QFDEL", "=", "%s", filename);
00542     _at.clear_error(); // Ignore error if file didn't exist
00543 
00544     // Upload new file to modem
00545     _at.cmd_start_stop("+QFUPL", "=", "%s%d", filename, size);
00546     _at.resp_start("CONNECT");
00547     _at.write_bytes((uint8_t *)data, size);
00548     _at.resp_start("+QFUPL:");
00549     size_t upload_size = _at.read_int();
00550     _at.resp_stop();
00551     if (upload_size != size) {
00552         tr_error("Upload error! orig = %d, uploaded = %d", size, upload_size);
00553         return NSAPI_ERROR_DEVICE_ERROR ;
00554     }
00555 
00556     // Configure into use
00557     _at.at_cmd_discard("+QSSLCFG", "=", "%s%d%s", config, sslctxID, filename);
00558 
00559     return _at.get_last_error();
00560 }
00561 
00562 
00563 nsapi_error_t QUECTEL_BG96_CellularStack::setsockopt(nsapi_socket_t handle, int level,
00564                                                      int optname, const void *optval, unsigned optlen)
00565 {
00566     CellularSocket *socket = (CellularSocket *)handle;
00567     nsapi_error_t ret = NSAPI_ERROR_OK ;
00568 
00569     if (level == NSAPI_TLSSOCKET_LEVEL ) {
00570         if (optval) {
00571             _at.lock();
00572             switch (optname) {
00573                 case NSAPI_TLSSOCKET_ENABLE : {
00574                     MBED_ASSERT(optlen == sizeof(bool));
00575                     bool *enabled = (bool *)optval;
00576                     if (socket->proto == NSAPI_TCP ) {
00577                         socket->tls_socket = enabled;
00578 
00579                         if (enabled) {
00580                             _at.at_cmd_discard("+QSSLCFG", "=\"seclevel\",", "%d%d", sslctxID, _tls_sec_level);
00581 
00582                             _at.at_cmd_discard("+QSSLCFG", "=\"sslversion\",", "%d%d", sslctxID, BG96_SUPPORTED_SSL_VERSION);
00583 
00584                             _at.cmd_start("AT+QSSLCFG=\"ciphersuite\",");
00585                             _at.write_int(sslctxID);
00586                             _at.write_string(BG96_SUPPORTED_CIPHER_SUITE, false);
00587                             _at.cmd_stop_read_resp();
00588 
00589                             ret = _at.get_last_error();
00590                         }
00591                     } else {
00592                         tr_error("Trying to set non-TCPSocket as TLSSocket");
00593                         ret = NSAPI_ERROR_PARAMETER ;
00594                     }
00595                 }
00596                 break;
00597 
00598                 case NSAPI_TLSSOCKET_SET_HOSTNAME : {
00599                     const char *hostname = (const char *)optval;
00600                     _at.at_cmd_discard("+QSSLCFG", "=\"checkhost\",", "%d%s", sslctxID, hostname);
00601                     ret = _at.get_last_error();
00602                 }
00603                 break;
00604 
00605                 case NSAPI_TLSSOCKET_SET_CACERT : {
00606                     const char *cacert = (const char *)optval;
00607                     ret = set_to_modem_impl("cacert.pem", "cacert", cacert, optlen);
00608 
00609                     // Set sec level to "Manage server authentication" if only cacert is in use
00610                     if (ret == NSAPI_ERROR_OK  && _tls_sec_level == 0) {
00611                         _tls_sec_level = 1;
00612                     }
00613                 }
00614                 break;
00615 
00616                 case NSAPI_TLSSOCKET_SET_CLCERT : {
00617                     const char *clcert = (const char *)optval;
00618                     ret = set_to_modem_impl("clcert.pem", "clientcert", clcert, optlen);
00619 
00620                     // Set sec level to "Manage server and client authentication if requested by the remote server"
00621                     if (ret == NSAPI_ERROR_OK ) {
00622                         _tls_sec_level = 2;
00623                     }
00624                 }
00625                 break;
00626 
00627                 case NSAPI_TLSSOCKET_SET_CLKEY : {
00628                     const char *clkey = (const char *)optval;
00629                     ret = set_to_modem_impl("client.key", "clientkey", clkey, optlen);
00630 
00631                     // Set sec level to "Manage server and client authentication if requested by the remote server"
00632                     if (ret == NSAPI_ERROR_OK ) {
00633                         _tls_sec_level = 2;
00634                     }
00635                 }
00636                 break;
00637 
00638                 default:
00639                     tr_error("Unsupported sockopt (%d)", optname);
00640                     ret = NSAPI_ERROR_UNSUPPORTED ;
00641             }
00642             _at.unlock();
00643         } else {
00644             tr_error("No optval!");
00645             ret = NSAPI_ERROR_PARAMETER ;
00646         }
00647     } else {
00648         tr_warning("Unsupported level (%d)", level);
00649         ret = NSAPI_ERROR_UNSUPPORTED ;
00650     }
00651 
00652     return ret;
00653 }