/* * @file MuxDaemon.cpp * @author Mateusz Piesta (mateusz.piesta@mudita.com) * @date 19.06.19 * @brief * @copyright Copyright (C) 2019 mudita.com * @details */ #include "MuxDaemon.hpp" #include #include "log/log.hpp" #include "FreeRTOS.h" #include "task.h" #include "InOutSerialWorker.hpp" #include "NotificationMuxChannel.hpp" #include "ControlMuxChannel.hpp" #include "CommunicationMuxChannel.hpp" constexpr unsigned char MuxDaemon::closeChannelCmd[]; const uint32_t MuxDaemon::virtualPortsCount; namespace QuectelBaudrates { // Default value for 115200 key template struct Baudrates { static const int Value = 5; }; template<> struct Baudrates<115200> { static const int Value = 5; }; template<> struct Baudrates<230400> { static const int Value = 6; }; template<> struct Baudrates<460800> { static const int Value = 7; }; template<> struct Baudrates<921600> { static const int Value = 8; }; template<> struct Baudrates<1500000> { static const int Value = 16; }; template<> struct Baudrates<2000000> { static const int Value = 20; }; template<> struct Baudrates<3000000> { static const int Value = 23; }; template<> struct Baudrates<4000000> { static const int Value = 26; }; } MuxDaemon::MuxDaemon(NotificationMuxChannel::NotificationCallback_t callback) : inOutSerialDataWorker(nullptr), callback(callback) { } MuxDaemon::~MuxDaemon() { CloseMultiplexer(); } std::unique_ptr MuxDaemon::Create(NotificationMuxChannel::NotificationCallback_t callback) { auto inst = std::make_unique(callback); auto ret = inst->Start(); if (ret == false) { return nullptr; } else { return inst; } } std::optional MuxDaemon::GetMuxChannel(MuxChannel::MuxChannelType chan) { if(static_cast(chan) >= channels.size()){ return {}; } else{ return channels[static_cast(chan)].get(); } } void MuxDaemon::RemoveMuxChannel(MuxChannel::MuxChannelType chan) { channels.erase(channels.begin() + static_cast(chan)); } bool MuxDaemon::Start() { if((inOutSerialDataWorker = InOutSerialWorker::Create(this).value_or(nullptr)) == nullptr){ return false; } // At first send AT command to check if modem is turned on auto ret = inOutSerialDataWorker->SendATCommand("AT\r", 2); if(ret.size() == 0 ){ LOG_INFO("Modem does not respond to AT commands, trying close mux mode"); inOutSerialDataWorker->SendFrame(0, closeChannelCmd, sizeof(closeChannelCmd), static_cast(MuxDefines::GSM0710_TYPE_UIH)); // Try sending AT command once again ret = inOutSerialDataWorker->SendATCommand("AT\r",2, 500); if(ret.size() == 1 || ret.size() == 2){ // Modem can send echo response or not, in either case it means that modem is operating in AT mode return StartMultiplexer(); } else{ LOG_INFO("Starting power up procedure..."); // If no response, power up modem and try again //TODO:M.P implement it cellular->PowerUp(); } } else if ((ret.size() == 1 && ret[0] == "OK\r\n") || (ret.size() == 2 && ret[0] == "AT\r\n" && ret[1] == "OK\r\n")){ return StartMultiplexer(); } } bool MuxDaemon::StartMultiplexer() { // Factory reset inOutSerialDataWorker->SendATCommand("AT&F\r", 1); // Turn off local echo inOutSerialDataWorker->SendATCommand("ATE0\r", 2); // Set up modem configuration if (hardwareControlFlowEnable) { inOutSerialDataWorker->SendATCommand("AT+IFC=2,2\r\n", 500); // enable flow control function for module } else { inOutSerialDataWorker->SendATCommand("AT+IFC=0,0\r", 1); // disable flow control function for module } // Set fixed baudrate inOutSerialDataWorker->SendATCommand(("AT+IPR=" + std::to_string(baudRate) + "\r").c_str(), 1); // Route URCs to first MUX channel inOutSerialDataWorker->SendATCommand("AT+QCFG=\"cmux/urcport\",1\r", 1); // Turn off RI pin for incoming calls inOutSerialDataWorker->SendATCommand("AT+QCFG=\"urc/ri/ring\",\"off\"\r", 1); // Turn off RI pin for incoming sms inOutSerialDataWorker->SendATCommand("AT+QCFG=\"urc/ri/smsincoming\",\"off\"\r", 1); // Route URCs to UART1 inOutSerialDataWorker->SendATCommand("AT+QURCCFG=\"urcport\",\"uart1\"\r", 1); // Turn on signal strength change URC inOutSerialDataWorker->SendATCommand("AT+QINDCFG=\"csq\",1\r", 1); // Change incoming call notification from "RING" to "+CRING:type" inOutSerialDataWorker->SendATCommand("AT+CRC=1\r", 1); // Turn on caller's number presentation inOutSerialDataWorker->SendATCommand("AT+CLIP=1\r", 1); /* // Set Message format to Text SendAT("AT+CMGF=1\r", 500); // Set SMS received report format SendAT("AT+CNMI=1,2,0,1,0\r", 500);*/ // This driver supports only Basic mode (max frame length = 127bytes and no frame errors correction) char gsm_command[128] = {}; snprintf(gsm_command, sizeof(gsm_command), "AT+CMUX=%d,%d,%d,%d\r", GSM0710Buffer::cmux_mode, 0, QuectelBaudrates::Baudrates::Value, GSM0710Buffer::cmux_N1 ); // Start CMUX multiplexer inOutSerialDataWorker->SendATCommand(gsm_command, 1); inOutSerialDataWorker->SwitchMode(InOutSerialWorker::Mode::CMUX); state = States::MUX_STATE_MUXING; // Create virtual channels // Control channel is used for controlling Mux. It is not considered as logical channel i.e it can't receive normal data messages channels.push_back(std::make_unique(inOutSerialDataWorker.get())); // Notificiation channel is used mainly for receiving URC messages and handling various async requests channels.push_back(std::make_unique(inOutSerialDataWorker.get(), callback)); // Communication channel is used for sending various requests to GSM modem (SMS/Dial and so on) in blocking manner channels.push_back(std::make_unique(inOutSerialDataWorker.get())); // Mux channels must be initialized in sequence for (auto &w : channels) { w->Open(); vTaskDelay(200); } return true; } int MuxDaemon::CloseMultiplexer() { // Virtual channels need to be deinitialized in reversed order i.e control channel should be closed at the end for (auto w = channels.size(); --w;) { if (channels[w]->GetState() == MuxChannel::State::Opened) { LOG_INFO("Logical channel %d closed", channels[w]->GetChannelNumber()); channels[w]->Close(); vTaskDelay(100); } } // Close control channel LOG_INFO("Control channel %d closed", channels[0]->GetChannelNumber()); channels[0]->Close(); return 0; } std::vector MuxDaemon::SendCommandResponse(MuxChannel::MuxChannelType type, const char *cmd, size_t rxCount, uint32_t timeout) { return channels[static_cast(type)]->SendCommandReponse(cmd, rxCount, timeout); }