M CMakeLists.txt => CMakeLists.txt +1 -1
@@ 21,7 21,7 @@ message("TARGET_COMPILE_DEFINITIONS: ${TARGET_COMPILE_OPTIONS}")
message("TARGET_LIBRARIES: ${TARGET_LIBRARIES}")
message("TARGET_LINKER_FLAGS: ${TARGET_LINKER_FLAGS}")
-add_executable(${PROJECT_NAME} "")
+add_executable(${PROJECT_NAME} "" )
if (NOT ${PROJECT_TARGET} STREQUAL "TARGET_RT1051")
set(ENABLE_TESTS ON)
M changelog.md => changelog.md +8 -0
@@ 1,5 1,13 @@
# MuditaOS changelog
+
+## Added
+
+* `[cellular]` Added SIM PIN/PUK handling
+* `[cellular]` Added change PIN functionality
+* `[cellular]` Added possibility of unlock SIM card (no PIN on start) functionality
+
+
## [0.46.1 2020-11-13]
## Added
M module-cellular/CMakeLists.txt => module-cellular/CMakeLists.txt +1 -0
@@ 30,6 30,7 @@ set(SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/at/src/UrcCreg.cpp
${CMAKE_CURRENT_SOURCE_DIR}/at/src/UrcCmti.cpp
${CMAKE_CURRENT_SOURCE_DIR}/at/src/UrcClip.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/at/src/UrcCpin.cpp
${CMAKE_CURRENT_SOURCE_DIR}/at/src/UrcPoweredDown.cpp
${CMAKE_CURRENT_SOURCE_DIR}/at/src/UrcResponse.cpp
${CMAKE_CURRENT_SOURCE_DIR}/at/src/UrcFactory.cpp
M module-cellular/at/Commands.hpp => module-cellular/at/Commands.hpp +9 -1
@@ 119,6 119,10 @@ namespace at
CUSD_SEND,
SET_SMS_STORAGE,
CPIN,
+ GET_CPIN,
+ QPINC, /// Get Pin/Puk attempts. For standard SIM facility (parameter) is "SC"
+ CLCK,
+ CPWD,
ENABLE_TIME_ZONE_UPDATE,
SET_TIME_ZONE_REPORTING,
DISABLE_TIME_ZONE_UPDATE,
@@ 199,7 203,11 @@ namespace at
{AT::CUSD_CLOSE_SESSION, {"AT+CUSD=2"}},
{AT::CUSD_SEND, {"AT+CUSD=1,"}},
{AT::SET_SMS_STORAGE, {"AT+CPMS=\"SM\",\"SM\",\"SM\"", 300}},
- {AT::CPIN, {"AT+CPIN="}},
+ {AT::CPIN, {"AT+CPIN=", default_timeout}},
+ {AT::GET_CPIN, {"AT+CPIN?", default_timeout}},
+ {AT::QPINC, {"AT+QPINC=", default_timeout}},
+ {AT::CLCK, {"AT+CLCK=", default_timeout}},
+ {AT::CPWD, {"AT+CPWD=", default_timeout}},
{AT::ENABLE_TIME_ZONE_UPDATE, {"AT+CTZU=3"}},
{AT::SET_TIME_ZONE_REPORTING, {"AT+CTZR=2"}},
{AT::DISABLE_TIME_ZONE_UPDATE, {"AT+CTZU=0"}},
M module-cellular/at/ErrorCode.hpp => module-cellular/at/ErrorCode.hpp +1 -1
@@ 30,7 30,7 @@ namespace at
SIM_PIN_required = 11,
SIM_PUKRequired = 12,
SIMFailure = 13,
- SIMBusy = 14,
+ SIMBusy = 14, /*!< could be returned on removed card and QPINC, PINC ret 10 */
SIMWrong = 15,
IncorrectPassword = 16,
SIM_PIN2Required = 17,
A module-cellular/at/SimState.hpp => module-cellular/at/SimState.hpp +30 -0
@@ 0,0 1,30 @@
+// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#pragma once
+
+namespace at
+{
+ /// SimState extends AT states based on CPIN (URC and AT Command)
+ enum class SimState
+ {
+ Ready,
+ NotReady,
+
+ SimPin,
+ SimPuk,
+ SimPin2,
+ SimPuk2,
+ PhNetPin,
+ PhNetPuk,
+ PhNetSPin,
+ PhNetSPuk,
+ PhSpPin,
+ PhSpPuk,
+ PhCorpPin,
+ PhCorpPuk,
+
+ Locked, ///< In case of attempt counters equal zero
+ Unknown
+ };
+} // namespace at
A module-cellular/at/UrcCpin.hpp => module-cellular/at/UrcCpin.hpp +65 -0
@@ 0,0 1,65 @@
+// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#pragma once
+
+#include "Urc.hpp"
+#include "SimState.hpp"
+
+namespace at::urc
+{
+
+ /// +CPIN: <state> - Indicate SIM card pin state
+ class Cpin : public Urc
+ {
+ enum Tokens
+ {
+ State = 0
+ };
+
+ public:
+ static constexpr auto cpin_ready = "READY"; ///< MT is not pending for any password
+ static constexpr auto cpin_not_ready = "NOT READY"; ///< +CPIN urc, SIM not ready
+ static constexpr auto cpin_sim_pin = "SIM PIN"; ///< MT is waiting for SIM PIN to be given
+ static constexpr auto cpin_sim_puk = "SIM PUK"; ///< MT is waiting for SIM PUK to be given
+ static constexpr auto cpin_sim_pin2 = "SIM PIN2"; ///< MT is waiting for SIM PIN2 to be given
+ static constexpr auto cpin_sim_puk2 = "SIM PUK2"; ///< MT is waiting for SIM PUK2 to be given
+ static constexpr auto cpin_phnet_pin =
+ "PH-NET PIN"; ///< MT is waiting for network personalization password to be given
+ static constexpr auto cpin_phnet_puk =
+ "PH-NET PUK"; ///< MT is waiting for network personalization unblocking password
+ static constexpr auto cpin_phnestsub_pin =
+ "PH-NETSUB PIN"; ///< MT is waiting for network subset personalization password to be given
+ static constexpr auto cpin_phnestsub_puk =
+ "PH-NETSUB PUK"; ///< MT is waiting for network subset personalization unblocking password to be given
+ static constexpr auto cpin_phsp_pin =
+ "PH-SP PIN"; ///< MT is waiting for service provider personalization password to be given
+ static constexpr auto cpin_phsp_puk =
+ "PH-SP PUK"; ///< MT is waiting for service provider personalization unblocking password to be given
+ static constexpr auto cpin_phcorp_pin =
+ "PH-CORP PIN"; ///< MT is waiting for corporate personalization password to be given
+ static constexpr auto cpin_phcorp_puk =
+ "PH-CORP PUK"; ///< MT is waiting for corporate personalization unblocking password to be given
+
+ static constexpr auto head = "+CPIN";
+
+ static bool isURC(const std::string uHead)
+ {
+ return uHead.find(Cpin::head) != std::string::npos;
+ }
+
+ using Urc::Urc;
+
+ [[nodiscard]] auto isValid() const noexcept -> bool override;
+ [[nodiscard]] auto getMessage() const noexcept -> std::optional<std::string>;
+ [[nodiscard]] auto getState() const noexcept -> std::optional<at::SimState>;
+
+ void Handle(UrcHandler &h) final
+ {
+ h.Handle(*this);
+ }
+
+ static at::SimState parseState(std::string_view state);
+ };
+
+} // namespace at::urc
M module-cellular/at/UrcHandler.hpp => module-cellular/at/UrcHandler.hpp +2 -0
@@ 11,6 11,7 @@ namespace at::urc
class Cusd;
class Ctze;
class Qind;
+ class Cpin;
class PoweredDown;
class UrcResponse;
@@ 23,6 24,7 @@ namespace at::urc
virtual void Handle(Cusd &urc) = 0;
virtual void Handle(Ctze &urc) = 0;
virtual void Handle(Qind &urc) = 0;
+ virtual void Handle(Cpin &urc) = 0;
virtual void Handle(PoweredDown &urc) = 0;
virtual void Handle(UrcResponse &urc) = 0;
};
M module-cellular/at/response.cpp => module-cellular/at/response.cpp +40 -0
@@ 10,6 10,46 @@ namespace at
{
namespace response
{
+ std::optional<std::vector<std::string>> getTokensForATCommand(const at::Result &resp, std::string_view head)
+ {
+ if (resp.code == at::Result::Code::OK) {
+ if (resp.response.size()) {
+ for (auto el : resp.response) {
+ if (el.compare(0, head.length(), head) == 0) {
+ auto body = el.substr(head.length());
+ return utils::split(body, ",");
+ }
+ }
+ }
+ }
+ return std::nullopt;
+ }
+
+ bool parseQPINC(const at::Result &resp, qpinc::AttemptsCounters &ret)
+ {
+ /// parse only first result from QPINC
+ const std::string_view AT_QPINC_SC = "+QPINC:";
+ if (auto tokens = getTokensForATCommand(resp, AT_QPINC_SC); tokens) {
+ constexpr int QPINC_TokensCount = 3;
+ if ((*tokens).size() == QPINC_TokensCount) {
+ utils::toNumeric((*tokens)[1], ret.PinCounter);
+ utils::toNumeric((*tokens)[2], ret.PukCounter);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool parseCLCK(const at::Result &resp, int &ret)
+ {
+ const std::string_view AT_CLCK = "+CLCK:";
+ if (auto tokens = getTokensForATCommand(resp, AT_CLCK); tokens) {
+ if ((*tokens).size() != 0) {
+ return utils::toNumeric((*tokens)[0], ret);
+ }
+ }
+ return false;
+ }
bool parseCSQ(std::string response, std::string &result)
{
M module-cellular/at/response.hpp => module-cellular/at/response.hpp +13 -0
@@ 13,12 13,25 @@ namespace at
{
namespace response
{
+ namespace qpinc
+ {
+ /// Structure that holds parsed information from AT+QPINC command
+ struct AttemptsCounters
+ {
+ int PinCounter; /*!< PIN attempts counter */
+ int PukCounter; /*!< PUK attempts counter */
+ };
+ } // namespace qpinc
+
std::vector<std::string> tokenize(std::string &response, std::string separator = ",");
+ std::optional<std::vector<std::string>> getTokensForATCommand(const at::Result &resp, std::string_view head);
bool parseCSQ(std::string response, std::string &result);
bool parseCSQ(std::string cellularResponse, uint32_t &result);
bool parseCREG(std::string &response, uint32_t &result);
bool parseCREG(std::string &response, std::string &result);
bool parseQNWINFO(std::string &response, std::string &result);
+ bool parseQPINC(const at::Result &resp, qpinc::AttemptsCounters &ret);
+ bool parseCLCK(const at::Result &resp, int &ret);
namespace creg
{
A module-cellular/at/src/UrcCpin.cpp => module-cellular/at/src/UrcCpin.cpp +57 -0
@@ 0,0 1,57 @@
+// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#include "UrcCpin.hpp"
+
+using namespace at::urc;
+
+at::SimState Cpin::parseState(std::string_view state)
+{
+ const std::map<std::string_view, SimState> cpin_convert_map = {
+ {Cpin::cpin_ready, SimState::Ready},
+ {Cpin::cpin_not_ready, SimState::NotReady},
+ {Cpin::cpin_sim_pin, SimState::SimPin},
+ {Cpin::cpin_sim_puk, SimState::SimPuk},
+ {Cpin::cpin_sim_pin2, SimState::SimPin2},
+ {Cpin::cpin_sim_puk2, SimState::SimPuk2},
+ {Cpin::cpin_phnet_pin, SimState::PhNetPin},
+ {Cpin::cpin_phnet_puk, SimState::PhNetPuk},
+ {Cpin::cpin_phnestsub_pin, SimState::PhNetSPin},
+ {Cpin::cpin_phnestsub_puk, SimState::PhNetSPuk},
+ {Cpin::cpin_phsp_pin, SimState::PhSpPin},
+ {Cpin::cpin_phsp_puk, SimState::PhSpPuk},
+ {Cpin::cpin_phcorp_pin, SimState::PhCorpPin},
+ {Cpin::cpin_phcorp_puk, SimState::PhCorpPuk},
+
+ };
+
+ auto it = cpin_convert_map.find(state);
+
+ if (it != cpin_convert_map.end()) {
+ return it->second;
+ }
+ return SimState::Unknown;
+}
+
+auto Cpin::isValid() const noexcept -> bool
+{
+ return tokens.size() == magic_enum::enum_count<Tokens>();
+}
+
+auto Cpin::getMessage() const noexcept -> std::optional<std::string>
+{
+ if (!isValid()) {
+ return std::nullopt;
+ }
+ return tokens[Tokens::State];
+}
+
+auto Cpin::getState() const noexcept -> std::optional<at::SimState>
+{
+ if (!isValid()) {
+ return std::nullopt;
+ }
+ auto msg = getMessage();
+ LOG_DEBUG("CPIN State: %s", (*msg).c_str());
+ return parseState(*msg);
+}
M module-cellular/at/src/UrcFactory.cpp => module-cellular/at/src/UrcFactory.cpp +4 -1
@@ 11,7 11,7 @@
#include <UrcClip.hpp>
#include <UrcPoweredDown.hpp>
#include <UrcResponse.hpp>
-
+#include <UrcCpin.hpp>
using namespace at::urc;
std::unique_ptr<Urc> UrcFactory::Create(const std::string &urcMessage)
@@ 43,6 43,9 @@ std::unique_ptr<Urc> UrcFactory::Create(const std::string &urcMessage)
else if (Clip::isURC(head)) {
return std::make_unique<Clip>(body);
}
+ else if (Cpin::isURC(head)) {
+ return std::make_unique<Cpin>(body);
+ }
else if (PoweredDown::isURC(head)) {
return std::make_unique<PoweredDown>(body);
}
M module-cellular/test/unittest_URC.cpp => module-cellular/test/unittest_URC.cpp +22 -0
@@ 16,9 16,11 @@
#include "UrcCreg.hpp"
#include "UrcCmti.hpp"
#include "UrcClip.hpp"
+#include "UrcCpin.hpp"
#include "UrcPoweredDown.hpp"
#include "UrcResponse.hpp"
#include "UrcFactory.hpp"
+#include "SimState.hpp"
template <typename urcType> static auto getURC(std::unique_ptr<at::urc::Urc> &urc) -> std::shared_ptr<urcType>
{
@@ 673,6 675,26 @@ TEST_CASE("+CLIP")
}
}
+TEST_CASE("+CPIN")
+{
+ SECTION("CPIN supported")
+ {
+ auto urc = at::urc::UrcFactory::Create("+CPIN: SIM PIN");
+ auto cpin = getURC<at::urc::Cpin>(urc);
+ REQUIRE(cpin);
+ REQUIRE(cpin->isValid());
+ REQUIRE(cpin->getState() == at::SimState::SimPin);
+ REQUIRE(cpin->getMessage() == "SIM PIN");
+ }
+ SECTION("CPIN not supported")
+ {
+ auto urc = at::urc::UrcFactory::Create("+CPIN: \"Unknown\"");
+ auto cpin = getURC<at::urc::Cpin>(urc);
+ REQUIRE(cpin);
+ REQUIRE(cpin->getState() == at::SimState::Unknown);
+ }
+}
+
TEST_CASE("POWERED DOWN")
{
SECTION("POWERED DOWN valid")
M module-services/service-cellular/CMakeLists.txt => module-services/service-cellular/CMakeLists.txt +2 -1
@@ 1,4 1,4 @@
-project(service-cellular)
+project(service-cellular)
message( "${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR}" )
set(SOURCES
@@ 8,6 8,7 @@ set(SOURCES
ServiceCellular.cpp
SignalStrength.cpp
CallRequest.cpp
+ SimCard.cpp
CallRequestFactory.cpp
CellularCallRequestHandler.cpp
)
M module-services/service-cellular/CellularUrcHandler.cpp => module-services/service-cellular/CellularUrcHandler.cpp +14 -0
@@ 138,6 138,20 @@ void CellularUrcHandler::Handle(Qind &urc)
}
}
+void CellularUrcHandler::Handle(Cpin &urc)
+{
+ if (urc.isValid()) {
+ auto state = urc.getState();
+ if (!state) {
+ LOG_INFO("Invalid cpin - ignore");
+ }
+ else {
+ response = std::make_unique<CellularSimStateMessage>(*state, *urc.getMessage());
+ urc.setHandled(true);
+ }
+ }
+}
+
void CellularUrcHandler::Handle(PoweredDown &urc)
{
if (urc.isValid()) {
M module-services/service-cellular/CellularUrcHandler.hpp => module-services/service-cellular/CellularUrcHandler.hpp +3 -2
@@ 1,4 1,4 @@
-// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
+// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#pragma once
@@ 12,7 12,7 @@
#include <module-cellular/at/UrcCreg.hpp>
#include <module-cellular/at/UrcCtze.hpp>
#include <module-cellular/at/UrcCusd.hpp>
-#include <module-cellular/at/UrcHandler.hpp>
+#include <module-cellular/at/UrcCpin.hpp>
#include <module-cellular/at/UrcPoweredDown.hpp>
#include <module-cellular/at/UrcQind.hpp>
#include <module-cellular/at/UrcResponse.hpp>
@@ 34,6 34,7 @@ class CellularUrcHandler : public UrcHandler
void Handle(Cusd &urc) final;
void Handle(Ctze &urc) final;
void Handle(Qind &urc) final;
+ void Handle(Cpin &urc) final;
void Handle(PoweredDown &urc) final;
void Handle(UrcResponse &urc) final;
M module-services/service-cellular/ServiceCellular.cpp => module-services/service-cellular/ServiceCellular.cpp +191 -69
@@ 1,4 1,4 @@
-// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
+// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#include "CellularUrcHandler.hpp"
@@ 9,6 9,7 @@
#include "service-cellular/SignalStrength.hpp"
#include "service-cellular/State.hpp"
#include "service-cellular/USSD.hpp"
+#include "SimCard.hpp"
#include "service-cellular/CallRequestFactory.hpp"
#include "service-cellular/CellularCallRequestHandler.hpp"
@@ 40,6 41,7 @@
#include <at/UrcCtze.hpp>
#include <at/UrcCusd.hpp>
#include <at/UrcQind.hpp>
+#include <at/UrcCpin.hpp> // for Cpin
#include <at/response.hpp>
#include <bsp/cellular/bsp_cellular.hpp>
#include <common_data/EventStore.hpp>
@@ 646,47 648,6 @@ sys::MessagePointer ServiceCellular::DataReceivedHandler(sys::DataMessage *msgl,
state.set(this, State::ST::SimSelect);
break;
}
- case MessageType::CellularSimVerifyPinRequest: {
- auto msg = dynamic_cast<CellularSimVerifyPinRequestMessage *>(msgl);
- if (msg != nullptr) {
- auto channel = cmux->get(TS0710::Channel::Commands);
- if (channel) {
-
- auto vpin = msg->gePinValue();
-
- std::stringstream ss;
- for (unsigned int i : vpin) {
- ss << i;
- }
- std::string pin;
- ss >> pin;
- LOG_DEBUG("PIN SET %s", ss.str().c_str());
-
- sys::MessagePointer rmsg;
-
- if (Store::GSM::get()->selected == msg->getSimCard()) {
-
- auto resp = channel->cmd(at::factory(at::AT::CPIN) + pin + ";\r");
-
- if (resp.code == at::Result::Code::OK) {
- responseMsg = std::make_shared<CellularResponseMessage>(true);
- }
- else {
- responseMsg = std::make_shared<CellularResponseMessage>(false);
- // TODO: Sim unlock error.
- }
- }
- else {
- LOG_ERROR("Selected SIM card differ from currently supported");
- responseMsg = std::make_shared<CellularResponseMessage>(false);
- at::Result fatalError;
- fatalError.code = at::Result::Code::ERROR;
- // TODO: Sim unlock error.
- }
- }
- }
- break;
- }
case MessageType::CellularListCurrentCalls: {
auto ret = cmux->get(TS0710::Channel::Commands)->cmd(at::AT::CLCC);
auto size = ret.response.size();
@@ 959,6 920,10 @@ sys::MessagePointer ServiceCellular::DataReceivedHandler(sys::DataMessage *msgl,
auto resp = transmitDtmfTone(msg->getDigit());
responseMsg = std::make_shared<CellularResponseMessage>(resp);
} break;
+ case MessageType::CellularSimState: {
+ auto msg = static_cast<CellularSimStateMessage *>(msgl);
+ responseMsg = std::make_shared<CellularResponseMessage>(handleSimState(msg->getState(), msg->getMessage()));
+ } break;
case MessageType::CellularUSSDRequest: {
auto msg = dynamic_cast<CellularUSSDMessage *>(msgl);
if (msg != nullptr) {
@@ 1005,8 970,14 @@ sys::MessagePointer ServiceCellular::DataReceivedHandler(sys::DataMessage *msgl,
}
}
+/**
+ * NOTICE: URC handling function identifyNotification works on different thread, so sending
+ * any AT commands is not allowed here (also in URC handlers and other functions called from here)
+ * @return
+ */
std::optional<std::shared_ptr<CellularMessage>> ServiceCellular::identifyNotification(const std::string &data)
{
+
CellularUrcHandler urcHandler(*this);
std::string str(data.begin(), data.end());
@@ 1017,40 988,191 @@ std::optional<std::shared_ptr<CellularMessage>> ServiceCellular::identifyNotific
auto urc = at::urc::UrcFactory::Create(str);
urc->Handle(urcHandler);
- if (auto ret = str.find("+CPIN: ") != std::string::npos) {
- /// TODO handle different sim statuses - i.e. no sim, sim error, sim puk, sim pin etc.
- if (str.find("NOT READY", ret) == std::string::npos) {
+ if (!urc->isHandled()) {
+ LOG_WARN("Unhandled notification: %s", logStr.c_str());
+ }
- Store::GSM::get()->sim = Store::GSM::get()->selected;
- if (str.find("SIM PIN", ret) != std::string::npos) {
- // TODO: Request pin.
- }
- else if (str.find("READY", ret) != std::string::npos) {
- // TODO: Sim unlocked.
- }
- else {
- LOG_WARN("Not supported: %s", logStr.c_str());
- Store::GSM::get()->sim = Store::GSM::SIM::SIM_FAIL;
+ return urcHandler.getResponse();
+}
+
+bool ServiceCellular::requestPin(unsigned int attempts, const std::string msg)
+{
+ LOG_DEBUG("REQUEST PIN");
+ return true;
+}
+
+bool ServiceCellular::requestPuk(unsigned int attempts, const std::string msg)
+{
+ LOG_ERROR("REQUEST PUK");
+ return true;
+}
+
+bool ServiceCellular::sendSimUnlocked()
+{
+ LOG_DEBUG("SIM UNLOCKED");
+ return true;
+}
+
+bool ServiceCellular::sendSimBlocked()
+{
+ LOG_ERROR("SIM BLOCKED");
+ return true;
+}
+
+bool ServiceCellular::sendUnhandledCME(unsigned int cme_error)
+{
+ LOG_ERROR("UNHANDLED CME %d", cme_error);
+ return true;
+}
+
+bool ServiceCellular::sendBadPin()
+{
+ LOG_DEBUG("SEND BAD PIN");
+ SimCard simCard(*this);
+ std::string msg;
+ if (auto state = simCard.simStateWithMessage(msg); state) {
+ return handleSimState(*state, msg);
+ }
+ return false;
+}
+
+bool ServiceCellular::sendBadPuk()
+{
+ LOG_DEBUG("SEND BAD PUK");
+ SimCard simCard(*this);
+ std::string msg;
+ if (auto state = simCard.simStateWithMessage(msg); state) {
+ return handleSimState(*state, msg);
+ }
+ return false;
+}
+
+bool ServiceCellular::sendChangePinResult(SimCardResult res)
+{
+ LOG_DEBUG("SEND CHANGE PIN RESULT");
+ return true;
+}
+
+bool ServiceCellular::changePin(const std::string oldPin, const std::string newPin)
+{
+ SimCard simCard(*this);
+ sendChangePinResult(simCard.changePin(oldPin, newPin));
+ return true;
+}
+
+bool ServiceCellular::unlockSimPin(std::string pin)
+{
+ SimCard simCard(*this);
+ SimCardResult sime;
+ LOG_DEBUG("PIN: %s", pin.c_str());
+ sime = simCard.supplyPin(pin);
+
+ if (sime == SimCardResult::IncorrectPassword) {
+ sendBadPin();
+ return false;
+ }
+
+ if (sime == SimCardResult::OK) {
+ return true;
+ }
+ else {
+ sendUnhandledCME(static_cast<unsigned int>(sime));
+ return false;
+ }
+}
+
+bool ServiceCellular::unlockSimPuk(std::string puk, std::string pin)
+{
+ SimCard simCard(*this);
+ SimCardResult sime;
+ LOG_DEBUG("PUK: %s %s", puk.c_str(), pin.c_str());
+ sime = simCard.supplyPuk(puk, pin);
+
+ if (sime == SimCardResult::IncorrectPassword) {
+ sendBadPuk();
+ return false;
+ }
+
+ if (sime == SimCardResult::OK) {
+ return true;
+ }
+ sendUnhandledCME(static_cast<unsigned int>(sime));
+ return false;
+}
+
+bool ServiceCellular::handleSimState(at::SimState state, const std::string message)
+{
+
+ std::optional<std::unique_ptr<CellularMessage>> response;
+ switch (state) {
+ case at::SimState::Ready:
+ Store::GSM::get()->sim = Store::GSM::get()->selected;
+ // SIM causes SIM INIT, only on ready
+ response = std::make_unique<CellularNotificationMessage>(CellularNotificationMessage::Type::SIM);
+ sendSimUnlocked();
+ break;
+ case at::SimState::NotReady:
+ LOG_DEBUG("Not ready");
+ Store::GSM::get()->sim = Store::GSM::SIM::SIM_FAIL;
+ break;
+ case at::SimState::SimPin: {
+ SimCard simCard(*this);
+ if (auto pc = simCard.getAttemptsCounters(); pc) {
+ if (pc.value().PukCounter != 0) {
+ requestPin(pc.value().PinCounter, message);
+ break;
}
- LOG_DEBUG("SIM OK!");
- }
- else {
- LOG_ERROR("SIM ERROR");
- Store::GSM::get()->sim = Store::GSM::SIM::SIM_FAIL;
}
- if ((str.find("NOT", ret) == std::string::npos) && (str.find("READY", ret) != std::string::npos)) {
- return std::make_shared<CellularNotificationMessage>(CellularNotificationMessage::Type::SIM);
+ sendSimBlocked();
+ break;
+ }
+ case at::SimState::SimPuk: {
+ SimCard simCard(*this);
+ if (auto pc = simCard.getAttemptsCounters(); pc) {
+ if (pc.value().PukCounter != 0) {
+ requestPuk(pc.value().PukCounter, message);
+ break;
+ }
}
- auto message = std::make_shared<sevm::SIMMessage>();
- sys::Bus::SendUnicast(message, service::name::evt_manager, this);
- return std::nullopt;
+ sendSimBlocked();
+ break;
}
-
- if (!urc->isHandled()) {
- LOG_WARN("Unhandled notification: %s", logStr.c_str());
+ case at::SimState::SimPin2:
+ [[fallthrough]];
+ case at::SimState::SimPuk2:
+ [[fallthrough]];
+ case at::SimState::PhNetPin:
+ [[fallthrough]];
+ case at::SimState::PhNetPuk:
+ [[fallthrough]];
+ case at::SimState::PhNetSPin:
+ [[fallthrough]];
+ case at::SimState::PhNetSPuk:
+ [[fallthrough]];
+ case at::SimState::PhSpPin:
+ [[fallthrough]];
+ case at::SimState::PhSpPuk:
+ [[fallthrough]];
+ case at::SimState::PhCorpPin:
+ [[fallthrough]];
+ case at::SimState::PhCorpPuk:
+ Store::GSM::get()->sim = Store::GSM::SIM::SIM_UNKNOWN;
+ LOG_ERROR("SimState not supported");
+ break;
+ case at::SimState::Locked:
+ Store::GSM::get()->sim = Store::GSM::SIM::SIM_FAIL;
+ sendSimBlocked();
+ break;
+ case at::SimState::Unknown:
+ LOG_ERROR("SimState not supported");
+ Store::GSM::get()->sim = Store::GSM::SIM::SIM_UNKNOWN;
+ break;
}
- return urcHandler.getResponse();
+ auto simMessage = std::make_shared<sevm::SIMMessage>();
+ sys::Bus::SendUnicast(simMessage, service::name::evt_manager, this);
+
+ return true;
}
bool ServiceCellular::sendSMS(SMSRecord record)
A module-services/service-cellular/SimCard.cpp => module-services/service-cellular/SimCard.cpp +169 -0
@@ 0,0 1,169 @@
+// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#include "SimCard.hpp"
+#include <log/log.hpp>
+#include <variant>
+#include "Result.hpp"
+#include "UrcCpin.hpp" //for Cpin parseState
+#include "UrcFactory.hpp"
+
+SimCardResult SimCard::convertErrorFromATResult(const at::Result atres) const
+{
+ if (std::holds_alternative<at::EquipmentErrorCode>(atres.errorCode)) {
+
+ auto cerr = static_cast<int>(std::get<at::EquipmentErrorCode>(atres.errorCode));
+ if ((cerr > static_cast<int>(SimCardResult::AT_ERROR_Begin)) &&
+ (cerr < static_cast<int>(SimCardResult::AT_ERROR_End))) {
+ return static_cast<SimCardResult>(cerr);
+ }
+ }
+ return SimCardResult::Unknown;
+}
+
+std::optional<at::response::qpinc::AttemptsCounters> SimCard::getAttemptsCounters() const
+{
+ auto channel = cellularService.cmux->get(TS0710::Channel::Commands);
+ if (channel) {
+ auto resp = channel->cmd(at::factory(at::AT::QPINC) + "\"SC\"");
+ at::response::qpinc::AttemptsCounters ret;
+ if (at::response::parseQPINC(resp, ret)) {
+ return ret;
+ }
+ }
+
+ return std::nullopt;
+}
+
+SimCardResult SimCard::supplyPin(const std::string pin) const
+{
+ if (auto pc = getAttemptsCounters(); pc) {
+ if (pc.value().PinCounter > 0) {
+ if (auto channel = cellularService.cmux->get(TS0710::Channel::Commands); channel) {
+ auto resp = channel->cmd(at::factory(at::AT::CPIN) + "\"" + pin + "\"");
+
+ if (resp.code == at::Result::Code::OK) {
+ return SimCardResult::OK;
+ }
+ else {
+ return convertErrorFromATResult(resp);
+ }
+ }
+ }
+ else {
+ if (pc.value().PukCounter > 0) {
+ return SimCardResult::SIM_PUKRequired;
+ }
+ else {
+ return SimCardResult::Locked;
+ }
+ }
+ }
+ return SimCardResult::Unknown;
+}
+
+SimCardResult SimCard::supplyPuk(const std::string puk, const std::string pin) const
+{
+ if (auto pc = getAttemptsCounters(); pc) {
+ if (pc.value().PukCounter != 0) {
+ if (auto channel = cellularService.cmux->get(TS0710::Channel::Commands); channel) {
+ auto resp = channel->cmd(at::factory(at::AT::CPIN) + "\"" + puk + "\"" + ",\"" + pin + "\"");
+ if (resp.code == at::Result::Code::OK) {
+ return SimCardResult::OK;
+ }
+ else {
+ return convertErrorFromATResult(resp);
+ }
+ }
+ }
+ else {
+ return SimCardResult::Locked;
+ }
+ }
+
+ return SimCardResult::Unknown;
+}
+
+bool SimCard::isPinLocked() const
+{
+ if (auto channel = cellularService.cmux->get(TS0710::Channel::Commands); channel) {
+ auto resp = channel->cmd(at::factory(at::AT::CLCK) + "\"SC\",2\r");
+ int val = 0;
+ if (at::response::parseCLCK(resp, val)) {
+ return val != 0;
+ }
+ }
+ return true;
+}
+
+SimCardResult SimCard::setPinLock(bool lock, const std::string pin) const
+{
+ if (auto pc = getAttemptsCounters(); pc) {
+ if (pc.value().PukCounter != 0) {
+ auto channel = cellularService.cmux->get(TS0710::Channel::Commands);
+ if (channel) {
+ auto resp =
+ channel->cmd(at::factory(at::AT::CLCK) + "\"SC\"," + (lock ? "1" : "0") + ",\"" + pin + "\"");
+ if (resp.code == at::Result::Code::OK) {
+ return SimCardResult::OK;
+ }
+ else {
+ return convertErrorFromATResult(resp);
+ }
+ }
+ }
+ else {
+ return SimCardResult::Locked;
+ }
+ }
+
+ return SimCardResult::Unknown;
+}
+
+SimCardResult SimCard::changePin(const std::string oldPin, const std::string newPin) const
+{
+ if (auto pc = getAttemptsCounters(); pc) {
+ if (pc.value().PukCounter != 0) {
+ auto channel = cellularService.cmux->get(TS0710::Channel::Commands);
+ if (channel) {
+ auto resp = channel->cmd(at::factory(at::AT::CPWD) + "\"SC\", \"" + oldPin + "\",\"" + newPin + "\"");
+ if (resp.code == at::Result::Code::OK) {
+ return SimCardResult::OK;
+ }
+ else {
+ return convertErrorFromATResult(resp);
+ }
+ }
+ }
+ else {
+ return SimCardResult::Locked;
+ }
+ }
+
+ return SimCardResult::Unknown;
+}
+
+std::optional<at::SimState> SimCard::simState() const
+{
+ std::string buf;
+ return simStateWithMessage(buf);
+}
+
+std::optional<at::SimState> SimCard::simStateWithMessage(std::string &message) const
+{
+ if (auto channel = cellularService.cmux->get(TS0710::Channel::Commands); channel) {
+ auto resp = channel->cmd(at::factory(at::AT::GET_CPIN));
+ if (resp.code == at::Result::Code::OK) {
+ if (resp.response.size()) {
+ for (auto el : resp.response) {
+ auto urc = at::urc::UrcFactory::Create(el);
+ auto cpin = std::unique_ptr<at::urc::Cpin>{static_cast<at::urc::Cpin *>(urc.release())};
+ if (cpin) {
+ return cpin->getState();
+ }
+ }
+ }
+ }
+ }
+ return at::SimState::Unknown;
+}
A module-services/service-cellular/SimCard.hpp => module-services/service-cellular/SimCard.hpp +76 -0
@@ 0,0 1,76 @@
+// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#pragma once
+
+#include <string>
+
+#include <SimState.hpp>
+
+#include "service-cellular/SimCardResult.hpp"
+#include "service-cellular/CellularMessage.hpp"
+#include "service-cellular/CellularServiceAPI.hpp"
+#include "service-cellular/ServiceCellular.hpp"
+#include "response.hpp"
+
+class SimCard
+{
+ public:
+ explicit SimCard(ServiceCellular &cellularService) : cellularService(cellularService)
+ {}
+
+ /** Get information about attempts of PIN and PUK for standard sim card (eg. not PIN2)
+ * @return As optional SimCard::AttemptsCounters, in case of error nullopt. Should be noted that in some case could
+ * return SIMFailure which could mean 0 attempts (happen if lock during session, on modem/sim reboot again return
+ * 0,0);
+ */
+ std::optional<at::response::qpinc::AttemptsCounters> getAttemptsCounters() const;
+
+ /** Supply pin for modem
+ * \param pin digits as a string from 4-8 digits
+ * \return return OK on success in other case see details in SimCardResult
+ */
+ SimCardResult supplyPin(const std::string pin) const;
+
+ /** Supply pin for modem
+ * \param puk puk as standard 8 digits
+ * \param pin, new pin digits as a string from 4-8 digits
+ * \return return OK on success in other case see details in SimCardResult
+ */
+ SimCardResult supplyPuk(const std::string puk, const std::string pin) const;
+
+ /** return whether the pin needs to be provided, only for standard pin.
+ * \return true if need pin to unlock SIM card functionality
+ */
+ bool isPinLocked() const;
+
+ /** Set whether to provide pin. Always need to provide actual pin for sim card, only for standard PIN
+ * \param lock true for lock SIM card
+ * \param pin actual pin for SIM card
+ * \return
+ */
+ SimCardResult setPinLock(bool lock, const std::string pin) const;
+
+ /** Change pin, only for standard pin. To get effect of change pin, SIM cart or modem should be restarted
+ * simplest solution is to call AT+CFUN=0/1
+ * \param oldPin
+ * \param newPin
+ * \return return OK on success, else see SimCardResult
+ */
+ SimCardResult changePin(const std::string oldPin, const std::string newPin) const;
+
+ /** Return SIM state based on CPIN AT commands
+ */
+ std::optional<at::SimState> simState() const;
+ std::optional<at::SimState> simStateWithMessage(std::string &message) const;
+
+ private:
+ ServiceCellular &cellularService;
+
+ /** Helper function to convert from one enum to another (only with part of them). Base on continuous range of sim
+ * errors in AT.
+ */
+ SimCardResult convertErrorFromATResult(const at::Result) const;
+
+ std::optional<std::vector<std::string>> getTokensForATCommand(const at::Result &resp, std::string_view head);
+};
M module-services/service-cellular/service-cellular/CellularMessage.hpp => module-services/service-cellular/service-cellular/CellularMessage.hpp +37 -1
@@ 12,6 12,7 @@
#include <Service/Message.hpp>
#include <module-bsp/bsp/cellular/bsp_cellular.hpp>
#include <utf8/UTF8.hpp>
+#include <SimState.hpp>
#include <memory>
#include <string>
@@ 79,6 80,29 @@ class CellularNotificationMessage : public CellularMessage
Type type;
std::string data;
};
+
+class CellularSimStateMessage : public CellularMessage
+{
+ private:
+ at::SimState state;
+ std::string message;
+
+ public:
+ explicit CellularSimStateMessage(at::SimState state, std::string message)
+ : CellularMessage(MessageType::CellularSimState), state(state), message(std::move(message))
+ {}
+
+ at::SimState getState() const noexcept
+ {
+ return state;
+ }
+
+ std::string getMessage() const
+ {
+ return message;
+ }
+};
+
class CellularTimeNotificationMessage : public CellularMessage
{
private:
@@ 241,20 265,32 @@ class CellularSimResponseMessage : public CellularSimMessage
static const SimState defaultSimState = SimState::SIMUnlocked;
};
+/// Message use only for mockup GUI purposes
class CellularSimVerifyPinRequestMessage : public CellularSimMessage
{
public:
CellularSimVerifyPinRequestMessage(Store::GSM::SIM sim, std::vector<unsigned int> pinValue)
: CellularSimMessage(MessageType::CellularSimVerifyPinRequest, sim), pinValue(std::move(pinValue))
{}
+ CellularSimVerifyPinRequestMessage(Store::GSM::SIM sim,
+ std::vector<unsigned int> pinValue,
+ std::vector<unsigned int> pukValue)
+ : CellularSimMessage(MessageType::CellularSimVerifyPinRequest, sim), pinValue(std::move(pinValue)),
+ pukValue(std::move(pukValue))
+ {}
- std::vector<unsigned int> gePinValue() const noexcept
+ std::vector<unsigned int> getPinValue() const
{
return pinValue;
}
+ std::vector<unsigned int> getPukValue() const
+ {
+ return pukValue;
+ }
private:
std::vector<unsigned int> pinValue;
+ std::vector<unsigned int> pukValue;
};
class CellularGetChannelMessage : public sys::DataMessage
M module-services/service-cellular/service-cellular/ServiceCellular.hpp => module-services/service-cellular/service-cellular/ServiceCellular.hpp +72 -1
@@ 1,8 1,10 @@
-// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
+// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#pragma once
+#include "SimCardResult.hpp"
+
#include "CellularCall.hpp"
#include "CellularMessage.hpp"
#include "State.hpp"
@@ 29,6 31,7 @@
#include <vector>
class MuxDaemon;
+
namespace db
{
namespace query
@@ 81,8 84,72 @@ class ServiceCellular : public sys::Service
std::vector<std::string> getNetworkInfo();
std::vector<std::string> scanOperators();
+ /** group of action/messages send "outside" eg. GUI
+ * requestPin is call anytime modem need pin, here should be called any action
+ * which allow user input (or mockup) pin. Then send appropriate action to notify the modem
+ * \param attempts Attempts counter for current action
+ * \param msg Literal name of action eg. SIM PIN
+ * \return
+ */
+ bool requestPin(unsigned int attempts, const std::string msg);
+
+ /** requestPuk is call anytime modem need puk, here should be called any action
+ * which allow user input (or mockup) puk and new pin. Then send appropriate action to notify the modem
+ * \param attempts Attempts counter for current action
+ * \param msg Literal name of action eg. SIM PUK
+ * \return
+ */
+ bool requestPuk(unsigned int attempts, const std::string msg);
+
+ /** Call in case of SIM card unlocked, MT ready. Place for sending message/action inform rest
+ * \return
+ */
+ bool sendSimUnlocked();
+
+ /** Call in case of SIM card locked (card fail, eg. to many bad PUK). Place for sending message/action inform rest
+ * \return
+ */
+ bool sendSimBlocked();
+
+ /** From this point should be send message/action call interaction in other layers eg. GUI
+ * \param cme_error
+ * \return
+ */
+ bool sendUnhandledCME(unsigned int cme_error);
+
+ /** Similar to sendBadPin
+ * \return
+ */
+ bool sendBadPin();
+
+ /** Message send, when modem return incorrect password for PIN message.
+ * Probably modem firmware depend. On current version last bad message (attempts=1) return PUK request
+ * and generate PUK URC, so finally action on puk request will be call. This implementation allow to
+ * rethrow URC (so achive similar behavior in all cases).
+ * \return
+ */
+ bool sendBadPuk();
+
+ /** Place to send action notifying eg. GUI
+ * \param res
+ * \return
+ */
+ bool sendChangePinResult(SimCardResult res);
+
+ /// sim functionality
+
+ /** Function ready for change pin action send to Service Cellular form eg. GUI
+ * \param oldPin
+ * \param newPin
+ * \return
+ */
+ bool changePin(const std::string oldPin, const std::string newPin);
+ bool unlockSimPin(std::string pin);
+ bool unlockSimPuk(std::string puk, std::string pin);
+
private:
std::unique_ptr<TS0710> cmux = std::make_unique<TS0710>(PortSpeed_e::PS460800, this);
+
// used for polling for call state
std::unique_ptr<sys::Timer> callStateTimer;
std::unique_ptr<sys::Timer> stateTimer;
@@ 151,6 218,7 @@ class ServiceCellular : public sys::Service
/// \note some run state should be added to ignore non system messages now...
bool handle_fatal_failure();
bool handle_ready();
+
bool handleAllMessagesFromMessageStorage();
[[nodiscard]] SMSRecord createSMSRecord(const UTF8 &decodedMessage,
const UTF8 &receivedNumber,
@@ 182,6 250,9 @@ class ServiceCellular : public sys::Service
bool handleUSSDURC();
void handleUSSDTimer();
+ bool handleSimState(at::SimState state, const std::string message);
+
friend class CellularUrcHandler;
friend class CellularCallRequestHandler;
+ friend class SimCard;
};
A module-services/service-cellular/service-cellular/SimCardResult.hpp => module-services/service-cellular/service-cellular/SimCardResult.hpp +28 -0
@@ 0,0 1,28 @@
+// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#pragma once
+
+enum class SimCardResult
+{
+ OK = 0,
+ Ready = 1,
+ Locked = 2, /*!< In case of attempt counters set to 0 */
+
+ AT_ERROR_Begin =
+ 9, /*!< this is only for separate AT SIM errors from new one added, AT errors list end with AT_ERROR_End */
+
+ SIMNotInserted = 10,
+ SIM_PIN_required = 11,
+ SIM_PUKRequired = 12,
+ Failure = 13,
+ Busy = 14,
+ Wrong = 15,
+ IncorrectPassword = 16,
+
+ AT_ERROR_End = 17,
+
+ Unknown = 0xFF /*!< Unknown, any reason (not only AT), in some case AT commends return just error, eg. twice
+ supply good pin, second AT commend return ERROR */
+
+};
M module-services/service-fota/FotaUrcHandler.hpp => module-services/service-fota/FotaUrcHandler.hpp +1 -0
@@ 25,6 25,7 @@ class FotaUrcHandler : public UrcHandler
virtual void Handle(Cmti &urc){};
virtual void Handle(Cusd &urc){};
virtual void Handle(Ctze &urc){};
+ virtual void Handle(Cpin &urc){};
virtual void Handle(PoweredDown &urc){};
virtual void Handle(UrcResponse &urc){};
M source/MessageType.hpp => source/MessageType.hpp +1 -0
@@ 101,6 101,7 @@ enum class MessageType
CellularTransmitDtmfTones,
CellularUSSDRequest,
CellularTimeUpdated,
+ CellularSimState,
DBNotesAdd, ///< Add new note's record
DBNotesRemove, ///< Remove selected note's record