M module-services/service-cellular/CMakeLists.txt => module-services/service-cellular/CMakeLists.txt +7 -0
@@ 2,6 2,10 @@ project(service-cellular)
message( "${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR}" )
set(SOURCES
+ src/ServiceCellularPriv.cpp
+ src/state.cpp
+ src/SimCard.cpp
+
CellularCall.cpp
CellularServiceAPI.cpp
CellularUrcHandler.cpp
@@ 37,6 41,9 @@ add_library(${PROJECT_NAME} STATIC ${SOURCES})
target_include_directories(${PROJECT_NAME}
PUBLIC
${CMAKE_CURRENT_LIST_DIR}
+ ${CMAKE_CURRENT_LIST_DIR}/include
+ PRIVATE
+ ${CMAKE_CURRENT_LIST_DIR}/src
)
target_link_libraries(${PROJECT_NAME}
A module-services/service-cellular/include/service-cellular-api => module-services/service-cellular/include/service-cellular-api +7 -0
@@ 0,0 1,7 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#pragma once
+
+#include "service-cellular/api/request/sim.hpp"
+#include "service-cellular/api/notification/notification.hpp"
A module-services/service-cellular/include/service-cellular/api/common.hpp => module-services/service-cellular/include/service-cellular/api/common.hpp +37 -0
@@ 0,0 1,37 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#pragma once
+
+#include <vector>
+
+namespace cellular
+{
+ namespace service
+ {
+ constexpr const char *name = "ServiceCellular";
+ }
+
+ namespace api
+ {
+ enum class CallMode
+ {
+ Regular,
+ Emergency
+ };
+
+ enum class SimLockState
+ {
+ Locked,
+ Unlocked
+ };
+
+ enum class SimSlot
+ {
+ SIM1 = 0,
+ SIM2 = 1
+ };
+
+ using SimCode = std::vector<unsigned int>;
+ } // namespace api
+} // namespace cellular
A module-services/service-cellular/include/service-cellular/api/notification/notification.hpp => module-services/service-cellular/include/service-cellular/api/notification/notification.hpp +51 -0
@@ 0,0 1,51 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#pragma once
+
+#include <Service/Message.hpp>
+
+#include <service-cellular/api/common.hpp>
+
+namespace cellular::msg
+{
+ struct Notification : public sys::Message
+ {
+ Notification() : Message(Type::Data)
+ {}
+ };
+
+ namespace notification
+ {
+ struct SimReady : public msg::Notification
+ {
+ explicit SimReady(bool ready) : ready(ready)
+ {}
+ const bool ready;
+ };
+
+ struct SimNeedPin : public msg::Notification
+ {
+ explicit SimNeedPin(unsigned int attempts) : attempts(attempts)
+ {}
+ const unsigned int attempts;
+ };
+
+ struct SimNeedPuk : public msg::Notification
+ {
+ explicit SimNeedPuk(unsigned int attempts) : attempts(attempts)
+ {}
+ const unsigned int attempts;
+ };
+
+ struct SimBlocked : public msg::Notification
+ {};
+
+ struct UnhandledCME : public msg::Notification
+ {
+ explicit UnhandledCME(unsigned int code) : code(code)
+ {}
+ const unsigned int code;
+ };
+ } // namespace notification
+} // namespace cellular::msg
A module-services/service-cellular/include/service-cellular/api/request/request.hpp => module-services/service-cellular/include/service-cellular/api/request/request.hpp +25 -0
@@ 0,0 1,25 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#pragma once
+
+#include <Service/Message.hpp>
+
+#include <service-cellular/api/common.hpp>
+
+namespace cellular::msg
+{
+ struct Request : public sys::Message
+ {
+ Request() : Message(Type::Data)
+ {}
+ static constexpr const char *target = cellular::service::name;
+ };
+ struct Response : public sys::Message
+ {
+ explicit Response(bool retCode = true) : Message(Type::Data), retCode(retCode)
+ {}
+ const bool retCode;
+ };
+
+} // namespace cellular::msg
A module-services/service-cellular/include/service-cellular/api/request/sim.hpp => module-services/service-cellular/include/service-cellular/api/request/sim.hpp +84 -0
@@ 0,0 1,84 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#pragma once
+
+#include "request.hpp"
+
+namespace cellular::msg::request::sim
+{
+ /** Set active SIM card
+ */
+ struct SetActiveSim : public msg::Request
+ {
+ explicit SetActiveSim(api::SimSlot sim) : sim(sim)
+ {}
+ const api::SimSlot sim;
+
+ using Response = msg::Response;
+ };
+
+ /** Get current state of PIN lock
+ */
+ struct GetLockState : public msg::Request
+ {
+ struct Response : public msg::Response
+ {
+ explicit Response(bool locked) : locked(locked)
+ {}
+ const bool locked;
+ };
+ };
+
+ /** Unlock SIM using PIN
+ */
+ struct PinUnlock : msg::Request
+ {
+ explicit PinUnlock(const api::SimCode &pin) : pin(pin)
+ {}
+ const api::SimCode pin;
+
+ using Response = msg::Response;
+ };
+
+ /** Change current PIN
+ */
+ struct ChangePin : msg::Request
+ {
+ explicit ChangePin(const api::SimCode &oldPin, const api::SimCode &pin) : oldPin(oldPin), pin(pin)
+ {}
+ const api::SimCode oldPin;
+ const api::SimCode pin;
+
+ using Response = msg::Response;
+ };
+
+ /** Reset PIN using PUK
+ */
+ struct UnblockWithPuk : msg::Request
+ {
+ explicit UnblockWithPuk(const api::SimCode &puk, const api::SimCode &pin) : puk(puk), pin(pin)
+ {}
+ const api::SimCode puk;
+ const api::SimCode pin;
+
+ using Response = msg::Response;
+ };
+
+ /** Enable or disable PIN lock on SIM
+ */
+ struct SetPinLock : msg::Request
+ {
+ explicit SetPinLock(api::SimLockState lock, const api::SimCode &pin) : lock(lock), pin(pin)
+ {}
+ const api::SimLockState lock;
+ const api::SimCode pin;
+
+ struct Response : msg::Response
+ {
+ explicit Response(bool retCode, api::SimLockState lock) : msg::Response(retCode), lock(lock)
+ {}
+ const api::SimLockState lock;
+ };
+ };
+} // namespace cellular::msg::request::sim
A module-services/service-cellular/include/service-cellular/state.hpp => module-services/service-cellular/include/service-cellular/state.hpp +65 -0
@@ 0,0 1,65 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#pragma once
+
+namespace sys
+{
+ class Service;
+}
+
+namespace cellular::service
+{
+ class State
+ {
+ public:
+ enum class ST
+ {
+ Idle, /// does nothing
+ WaitForStartPermission, /// waiting for permission to start the module
+ PowerUpRequest, /// process request of power up
+ StatusCheck, /// set on service start - check for modem status - skipped on T3 board
+ PowerUpProcedure, /// due to lack of Status pin on T3, we don't know whether is on or off
+ PowerUpInProgress, /// waiting for modem powered up by polling various bauds
+ BaudDetect, /// baud detection procedure
+ CellularConfProcedure, /// configuration procedure
+ AudioConfigurationProcedure, /// audio configuration for modem (could be in ModemConfiguration)
+ APNConfProcedure, /// Configure APN set by user, check if modem have similar
+ SanityCheck, /// prior to ModemOn last sanity checks for one time configurations etc
+ ModemOn, /// modem ready - indicates that modem is fully configured, ( **SIM is not yet configured** )
+ URCReady, /// State indicates that URC handling is enabled
+ SimInit, /// initialize sim card
+ SimSelect, /// triggers hw SIM selection (! state now will be **changed on URC** )
+ ModemFatalFailure, /// modem full shutdown need
+ Failed,
+ Ready, /// Service is fully initialized
+ PowerDownStarted, /// modem is disconnecting from network. It might take a while if poor coverage
+ PowerDownWaiting, /// modem has deregistered and is now approaching low power state
+ PowerDown, /// modem is known to be turned off
+ };
+
+ enum class PowerState
+ {
+ Off,
+ On
+ };
+
+ private:
+ enum ST state = ST::Idle;
+ sys::Service *owner;
+
+ public:
+ explicit State(sys::Service *owner);
+
+ [[nodiscard]] static const char *c_str(ST state);
+ [[nodiscard]] const char *c_str() const;
+
+ /// 1. sets state of ServiceCellular
+ /// 2. sends Multicast notification of ServiceCellular state
+ ///
+ /// \note This is for service cellular only it could be private and friend
+ void set(ST state);
+
+ ST get() const;
+ };
+} // namespace cellular::service
A module-services/service-cellular/src/ServiceCellularPriv.cpp => module-services/service-cellular/src/ServiceCellularPriv.cpp +87 -0
@@ 0,0 1,87 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#include "ServiceCellularPriv.hpp"
+#include "messages.hpp"
+
+#include <service-cellular-api>
+
+#include <service-evtmgr/EVMessages.hpp>
+#include <service-evtmgr/Constants.hpp>
+
+namespace cellular::internal
+{
+ ServiceCellularPriv::ServiceCellularPriv(ServiceCellular *owner)
+ : owner{owner}, simCard{std::make_unique<SimCard>()}, state{std::make_unique<State>(owner)}
+ {
+ initSimCard();
+ }
+
+ void ServiceCellularPriv::initSimCard()
+ {
+ using namespace cellular::msg;
+ simCard->onSimReady = [this](bool ready) {
+ // SIM causes SIM INIT, only on ready
+ if (ready) {
+ state->set(State::ST::SimInit);
+ }
+ owner->bus.sendMulticast(std::make_shared<notification::SimReady>(ready),
+ sys::BusChannel::ServiceCellularNotifications);
+ };
+ simCard->onNeedPin = [this](unsigned int attempts) {
+ owner->bus.sendMulticast(std::make_shared<notification::SimNeedPin>(attempts),
+ sys::BusChannel::ServiceCellularNotifications);
+ };
+ simCard->onNeedPuk = [this](unsigned int attempts) {
+ owner->bus.sendMulticast(std::make_shared<notification::SimNeedPuk>(attempts),
+ sys::BusChannel::ServiceCellularNotifications);
+ };
+ simCard->onSimBlocked = [this]() {
+ owner->bus.sendMulticast(std::make_shared<notification::SimBlocked>(),
+ sys::BusChannel::ServiceCellularNotifications);
+ };
+ simCard->onSimEvent = [this]() {
+ owner->bus.sendUnicast(std::make_shared<sevm::SIMMessage>(), ::service::name::evt_manager);
+ };
+ simCard->onUnhandledCME = [this](unsigned int code) {
+ owner->bus.sendMulticast(std::make_shared<notification::UnhandledCME>(code),
+ sys::BusChannel::ServiceCellularNotifications);
+ };
+ }
+
+ void ServiceCellularPriv::connectSimCard()
+ {
+ using namespace cellular::msg;
+ owner->connect(typeid(request::sim::SetActiveSim), [&](sys::Message *request) -> sys::MessagePointer {
+ auto msg = static_cast<request::sim::SetActiveSim *>(request);
+ return std::make_shared<request::sim::SetActiveSim::Response>(simCard->handleSetActiveSim(msg->sim));
+ });
+ owner->connect(typeid(request::sim::GetLockState), [&](sys::Message *) -> sys::MessagePointer {
+ return std::make_shared<request::sim::GetLockState::Response>(simCard->handleIsPinLocked());
+ });
+ owner->connect(typeid(request::sim::ChangePin), [&](sys::Message *request) -> sys::MessagePointer {
+ auto msg = static_cast<request::sim::ChangePin *>(request);
+ return std::make_shared<request::sim::ChangePin::Response>(simCard->handleChangePin(msg->oldPin, msg->pin));
+ });
+ owner->connect(typeid(request::sim::UnblockWithPuk), [&](sys::Message *request) -> sys::MessagePointer {
+ auto msg = static_cast<request::sim::UnblockWithPuk *>(request);
+ return std::make_shared<request::sim::UnblockWithPuk::Response>(
+ simCard->handleUnblockWithPuk(msg->puk, msg->pin));
+ });
+ owner->connect(typeid(request::sim::SetPinLock), [&](sys::Message *request) -> sys::MessagePointer {
+ auto msg = static_cast<request::sim::SetPinLock *>(request);
+ return std::make_shared<request::sim::SetPinLock::Response>(simCard->handleSetPinLock(msg->pin, msg->lock),
+ msg->lock);
+ });
+ owner->connect(typeid(request::sim::PinUnlock), [&](sys::Message *request) -> sys::MessagePointer {
+ auto msg = static_cast<request::sim::PinUnlock *>(request);
+ return std::make_shared<request::sim::PinUnlock::Response>(simCard->handlePinUnlock(msg->pin));
+ });
+ owner->connect(typeid(internal::msg::SimStateChanged), [&](sys::Message *request) -> sys::MessagePointer {
+ auto msg = static_cast<internal::msg::SimStateChanged *>(request);
+ simCard->handleSimStateChanged(msg->state);
+ return sys::MessageNone{};
+ });
+ }
+
+} // namespace cellular::internal
A module-services/service-cellular/src/ServiceCellularPriv.hpp => module-services/service-cellular/src/ServiceCellularPriv.hpp +33 -0
@@ 0,0 1,33 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#pragma once
+
+#include <service-cellular/ServiceCellular.hpp>
+#include <service-cellular/state.hpp>
+
+#include "SimCard.hpp"
+
+namespace cellular::internal
+{
+ using service::SimCard;
+ using service::State;
+
+ class ServiceCellularPriv
+ {
+ ServiceCellular *owner;
+
+ std::unique_ptr<SimCard> simCard;
+ std::unique_ptr<State> state;
+
+ State::PowerState nextPowerState = State::PowerState::Off;
+
+ public:
+ ServiceCellularPriv(ServiceCellular *owner);
+
+ void connectSimCard();
+
+ private:
+ void initSimCard();
+ };
+} // namespace cellular::internal
A module-services/service-cellular/src/SimCard.cpp => module-services/service-cellular/src/SimCard.cpp +273 -0
@@ 0,0 1,273 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#include "SimCard.hpp"
+
+#include <common_data/EventStore.hpp>
+#include <bsp/cellular/bsp_cellular.hpp>
+#include <modem/BaseChannel.hpp>
+#include <at/ATFactory.hpp>
+#include <at/UrcFactory.hpp>
+#include <at/UrcCpin.hpp>
+
+namespace cellular
+{
+ namespace internal
+ {
+ constexpr const char *pinString[] = {"SC", "P2"};
+
+ service::sim::Result convertErrorFromATResult(const at::Result atres)
+ {
+ if (std::holds_alternative<at::EquipmentErrorCode>(atres.errorCode)) {
+
+ auto err = static_cast<int>(std::get<at::EquipmentErrorCode>(atres.errorCode));
+ if ((err > static_cast<int>(service::sim::Result::AT_ERROR_Begin)) &&
+ (err < static_cast<int>(service::sim::Result::AT_ERROR_End))) {
+ return static_cast<service::sim::Result>(err);
+ }
+ }
+ return service::sim::Result::Unknown;
+ }
+
+ std::string simCodeToString(const cellular::api::SimCode &v)
+ {
+ std::string buf;
+ std::transform(v.begin(), v.end(), std::back_inserter(buf), [](auto &&c) { return '0' + c; });
+ return buf;
+ }
+
+ } // namespace internal
+
+ namespace service
+ {
+ bool SimCard::ready() const
+ {
+ return channel;
+ }
+
+ void SimCard::setChannel(at::BaseChannel *channel)
+ {
+ this->channel = channel;
+ }
+
+ bool SimCard::handleSetActiveSim(api::SimSlot sim)
+ {
+ Store::GSM::get()->selected = static_cast<Store::GSM::SIM>(sim);
+ bsp::cellular::sim::simSelect();
+ bsp::cellular::sim::hotSwapTrigger();
+ return true;
+ }
+
+ bool SimCard::handleIsPinLocked() const
+ {
+ return isPinLocked();
+ }
+
+ bool SimCard::handleChangePin(const api::SimCode &oldPin, const api::SimCode &pin)
+ {
+ const auto _oldPin = internal::simCodeToString(oldPin);
+ const auto _pin = internal::simCodeToString(pin);
+ return processPinResult(changePin(_oldPin, _pin));
+ }
+
+ bool SimCard::handleUnblockWithPuk(const api::SimCode &puk, const api::SimCode &pin)
+ {
+ const auto _puk = internal::simCodeToString(puk);
+ const auto _pin = internal::simCodeToString(pin);
+ return processPinResult(supplyPuk(_puk, _pin));
+ }
+
+ bool SimCard::handleSetPinLock(const api::SimCode &pin, api::SimLockState lock)
+ {
+ const auto _pin = internal::simCodeToString(pin);
+ return processPinResult(setPinLock(_pin, lock == cellular::api::SimLockState::Locked));
+ }
+
+ bool SimCard::handlePinUnlock(const api::SimCode &pin)
+ {
+ const auto _pin = internal::simCodeToString(pin);
+ return processPinResult(supplyPin(_pin));
+ }
+
+ void SimCard::handleSimStateChanged(at::SimState state)
+ {
+ handleSimState(state);
+ }
+
+ void SimCard::handleSimState(at::SimState state)
+ {
+ switch (state) {
+ case at::SimState::Ready:
+ Store::GSM::get()->sim = Store::GSM::get()->selected;
+ if (onSimReady)
+ onSimReady(true);
+ break;
+ case at::SimState::NotReady:
+ Store::GSM::get()->sim = Store::GSM::SIM::SIM_FAIL;
+ if (onSimReady)
+ onSimReady(false);
+ break;
+ case at::SimState::SimPin:
+ [[fallthrough]];
+ case at::SimState::SimPin2: {
+ if (auto pc = getAttemptsCounters(state == at::SimState::SimPin ? sim::Pin::PIN1 : sim::Pin::PIN2);
+ pc) {
+ if (pc.value().PukCounter != 0) {
+ if (onNeedPin)
+ onNeedPin(pc.value().PinCounter);
+ break;
+ }
+ }
+ if (onSimBlocked)
+ onSimBlocked();
+ break;
+ }
+ case at::SimState::SimPuk:
+ [[fallthrough]];
+ case at::SimState::SimPuk2: {
+ if (auto pc = getAttemptsCounters(state == at::SimState::SimPuk ? sim::Pin::PIN1 : sim::Pin::PIN2);
+ pc) {
+ if (pc.value().PukCounter != 0) {
+ if (onNeedPuk)
+ onNeedPuk(pc.value().PukCounter);
+ break;
+ }
+ }
+ if (onSimBlocked)
+ onSimBlocked();
+ break;
+ }
+ case at::SimState::Locked:
+ Store::GSM::get()->sim = Store::GSM::SIM::SIM_FAIL;
+ if (onSimBlocked)
+ onSimBlocked();
+ break;
+ 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:
+ [[fallthrough]];
+ case at::SimState::Unknown:
+ LOG_ERROR("SimState not supported");
+ Store::GSM::get()->sim = Store::GSM::SIM::SIM_UNKNOWN;
+ break;
+ }
+ if (onSimEvent)
+ onSimEvent();
+ }
+
+ std::optional<at::response::qpinc::AttemptsCounters> SimCard::getAttemptsCounters(sim::Pin pin) const
+ {
+ if (!ready()) {
+ return std::nullopt;
+ }
+
+ auto resp =
+ channel->cmd(at::factory(at::AT::QPINC) + "\"" + internal::pinString[static_cast<int>(pin)] + "\"");
+ at::response::qpinc::AttemptsCounters ret;
+ if (at::response::parseQPINC(resp, ret)) {
+ return ret;
+ }
+
+ return std::nullopt;
+ }
+
+ bool SimCard::processPinResult(sim::Result result)
+ {
+ if (result == sim::Result::IncorrectPassword) {
+ if (auto state = simState(); state) {
+ handleSimState(*state);
+ }
+ }
+ else if (result != sim::Result::OK) {
+ if (onUnhandledCME)
+ onUnhandledCME(static_cast<unsigned int>(result));
+ }
+ return result == sim::Result::OK;
+ }
+
+ sim::Result SimCard::supplyPin(const std::string &pin) const
+ {
+ return sendCommand(sim::LockType::PIN, at::factory(at::AT::CPIN) + "\"" + pin + "\"");
+ }
+
+ sim::Result SimCard::changePin(const std::string &oldPin, const std::string &newPin) const
+ {
+ return sendCommand(sim::LockType::PIN,
+ at::factory(at::AT::CPWD) + "\"SC\", \"" + oldPin + "\",\"" + newPin + "\"");
+ }
+
+ sim::Result SimCard::supplyPuk(const std::string &puk, const std::string &pin) const
+ {
+ return sendCommand(sim::LockType::PUK, at::factory(at::AT::CPIN) + "\"" + puk + "\"" + ",\"" + pin + "\"");
+ }
+
+ sim::Result SimCard::setPinLock(const std::string &pin, bool lock) const
+ {
+ return sendCommand(sim::LockType::PIN,
+ at::factory(at::AT::CLCK) + "\"SC\"," + (lock ? "1" : "0") + ",\"" + pin + "\"");
+ }
+
+ bool SimCard::isPinLocked() const
+ {
+ 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;
+ }
+
+ std::optional<at::SimState> SimCard::simState() const
+ {
+ 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);
+ if (auto cpin = dynamic_cast<at::urc::Cpin *>(urc.get())) {
+ return cpin->getState();
+ }
+ }
+ }
+ }
+ return at::SimState::Unknown;
+ }
+
+ sim::Result SimCard::sendCommand(sim::LockType check, const at::Cmd &cmd) const
+ {
+ if (auto pc = getAttemptsCounters(); pc) {
+ switch (check) {
+ case sim::LockType::PIN:
+ if (pc.value().PinCounter == 0)
+ return sim::Result::Locked;
+ break;
+ case sim::LockType::PUK:
+ if (pc.value().PukCounter == 0)
+ return sim::Result::Locked;
+ break;
+ }
+ }
+ else {
+ return sim::Result::Unknown;
+ }
+
+ if (auto resp = channel->cmd(cmd); resp.code != at::Result::Code::OK) {
+ return internal::convertErrorFromATResult(resp);
+ }
+
+ return sim::Result::OK;
+ }
+ } // namespace service
+} // namespace cellular
A module-services/service-cellular/src/SimCard.hpp => module-services/service-cellular/src/SimCard.hpp +160 -0
@@ 0,0 1,160 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#pragma once
+
+#include <at/response.hpp>
+#include <at/SimState.hpp>
+#include <service-cellular/api/common.hpp>
+
+namespace at
+{
+ class Cmd;
+ class BaseChannel;
+} // namespace at
+
+namespace cellular::service::sim
+{
+ enum class Pin
+ {
+ PIN1 = 0,
+ PIN2 = 1
+ };
+
+ enum class Result
+ {
+ 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 */
+
+ };
+
+ enum class LockType
+ {
+ PIN,
+ PUK
+ };
+} // namespace cellular::service::sim
+
+namespace cellular::internal
+{
+ service::sim::Result convertErrorFromATResult(const at::Result atres);
+ std::string simCodeToString(const cellular::api::SimCode &v);
+} // namespace cellular::internal
+
+namespace cellular::service
+{
+ class SimCard
+ {
+ public:
+ /** Check if sim card slot has been selected and cmd channel is set
+ * @return true if ready to communicate
+ */
+ bool ready() const;
+
+ /** Set cmd channel
+ * \param channel channel (or nullptr to block communication)
+ */
+ void setChannel(at::BaseChannel *channel = nullptr);
+
+ /**
+ * Request message handlers
+ */
+ bool handleSetActiveSim(api::SimSlot sim);
+ bool handleIsPinLocked() const;
+ bool handleChangePin(const api::SimCode &old_pin, const api::SimCode &pin);
+ bool handleUnblockWithPuk(const api::SimCode &puk, const api::SimCode &pin);
+ bool handleSetPinLock(const api::SimCode &pin, api::SimLockState lock);
+ bool handlePinUnlock(const api::SimCode &pin);
+
+ /**
+ * Internal message handlers
+ */
+ void handleSimStateChanged(at::SimState state);
+
+ /**
+ * Notification events
+ */
+ std::function<void(bool ready)> onSimReady;
+ std::function<void(unsigned int attempts)> onNeedPin;
+ std::function<void(unsigned int attempts)> onNeedPuk;
+ std::function<void()> onSimBlocked;
+ std::function<void()> onSimEvent;
+ std::function<void(unsigned int code)> onUnhandledCME;
+
+ private:
+ /** Get information about attempts of PIN and PUK for standard sim card (optionally PIN2/PUK2)
+ * @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(sim::Pin pin = sim::Pin::PIN1) 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
+ */
+ sim::Result 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
+ */
+ sim::Result supplyPuk(const std::string &puk, const std::string &pin) 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
+ */
+ sim::Result setPinLock(const std::string &pin, bool lock) 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
+ */
+ sim::Result changePin(const std::string &oldPin, const std::string &newPin) const;
+
+ /** Check whether the pin needs to be provided, only for standard pin.
+ * \return true if need pin to unlock SIM card functionality
+ */
+ bool isPinLocked() const;
+
+ /** Read internal SIM state using CPIN AT commands
+ */
+ std::optional<at::SimState> simState() const;
+
+ /** Process sim::Result from PIN lock/unlock operations
+ * \param result result from operation (`sendCommand()`)
+ * \result return true on success
+ */
+ bool processPinResult(sim::Result result);
+
+ sim::Result sendCommand(sim::LockType check, const at::Cmd &cmd) const;
+
+ void handleSimState(at::SimState state);
+
+ at::BaseChannel *channel = nullptr;
+ std::optional<api::SimSlot> sim = std::nullopt;
+ };
+
+} // namespace cellular::service
A module-services/service-cellular/src/messages.hpp => module-services/service-cellular/src/messages.hpp +17 -0
@@ 0,0 1,17 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#pragma once
+
+#include <service-cellular/api/request/request.hpp>
+#include <at/SimState.hpp>
+
+namespace cellular::internal::msg
+{
+ struct SimStateChanged : public cellular::msg::Request
+ {
+ SimStateChanged(at::SimState state) : state(state)
+ {}
+ at::SimState state;
+ };
+} // namespace cellular::internal::msg
A module-services/service-cellular/src/state.cpp => module-services/service-cellular/src/state.cpp +79 -0
@@ 0,0 1,79 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#include <service-cellular/state.hpp>
+#include <Service/Service.hpp>
+#include <service-cellular/CellularMessage.hpp>
+
+namespace cellular::service
+{
+ const char *State::c_str(State::ST state)
+ {
+ switch (state) {
+ case ST::Idle:
+ return "Idle";
+ case ST::WaitForStartPermission:
+ return "WaitForStartPermission";
+ case ST::PowerUpRequest:
+ return "PowerUpRequest";
+ case ST::StatusCheck:
+ return "StatusCheck";
+ case ST::PowerUpInProgress:
+ return "PowerUpInProgress";
+ case ST::PowerUpProcedure:
+ return "PowerUpProcedure";
+ case ST::BaudDetect:
+ return "BaudDetect";
+ case ST::AudioConfigurationProcedure:
+ return "AudioConfigurationProcedure";
+ case ST::APNConfProcedure:
+ return "APNConfProcedure";
+ case ST::ModemOn:
+ return "ModemOn";
+ case ST::URCReady:
+ return "URCReady";
+ case ST::SimSelect:
+ return "SimSelect";
+ case ST::Failed:
+ return "Failed";
+ case ST::SanityCheck:
+ return "SanityCheck";
+ case ST::SimInit:
+ return "SimInit";
+ case ST::ModemFatalFailure:
+ return "ModemFatalFailure";
+ case ST::CellularConfProcedure:
+ return "CellularStartConfProcedure";
+ case ST::Ready:
+ return "Ready";
+ case ST::PowerDownStarted:
+ return "PowerDownStarted";
+ case ST::PowerDownWaiting:
+ return "PowerDownWaiting";
+ case ST::PowerDown:
+ return "PowerDown";
+ }
+ return "";
+ }
+
+ State::State(sys::Service *owner) : owner(owner)
+ {}
+
+ const char *State::c_str() const
+ {
+ return State::c_str(state);
+ }
+
+ void State::set(ST state)
+ {
+ LOG_DEBUG("GSM state: (%s) -> (%s)", c_str(this->state), c_str(state));
+ this->state = state;
+ auto msg = std::make_shared<StateChange>(static_cast<cellular::State::ST>(state));
+ owner->bus.sendMulticast(msg, sys::BusChannel::ServiceCellularNotifications);
+ }
+
+ State::ST State::get() const
+ {
+ return this->state;
+ }
+} // namespace cellular::service
M module-services/service-cellular/tests/unittest_simcard.cpp => module-services/service-cellular/tests/unittest_simcard.cpp +100 -5
@@ 4,15 4,110 @@
#define CATCH_CONFIG_MAIN
#include <catch2/catch.hpp>
-#include <service-cellular/SimCard.hpp>
+#include <service-cellular/src/SimCard.hpp>
+
+#include <at/Cmd.hpp>
+#include <at/Result.hpp>
+#include <at/ATFactory.hpp>
+
+#include <module-cellular/test/mock/AtCommon_channel.hpp>
+
+namespace at
+{
+ class QPINC_Channel : public ChannelMock
+ {
+ public:
+ Result::Code code;
+ at::EquipmentErrorCode err;
+ const std::string pinAttempts;
+ const std::string pukAttempts;
+
+ QPINC_Channel(Result::Code code = Result::Code::OK,
+ at::EquipmentErrorCode err = at::EquipmentErrorCode::NoInformation,
+ int pinAttempts = 2,
+ int pukAttempts = 1)
+ : code(code), err(err), pinAttempts(std::to_string(pinAttempts)), pukAttempts(std::to_string(pukAttempts))
+ {}
+
+ auto cmd(const at::Cmd &at) -> Result override
+ {
+ const auto &cmd = at.getCmd();
+ if (cmd.rfind("AT+QPINC=", 0) == 0)
+ return Result(Result::Code::OK, {"+QPINC:0," + pinAttempts + "," + pukAttempts, "OK"});
+ Result result(code, {});
+ result.errorCode = err;
+ return result;
+ }
+ };
+} // namespace at
TEST_CASE("SimCard functionality test")
{
SECTION("pinToString from vector")
{
- std::vector<unsigned int> v{1, 2, 3, 4};
- std::vector<unsigned int> empty;
- REQUIRE(SimCard::pinToString(v) == "1234");
- REQUIRE(SimCard::pinToString(empty).empty());
+ cellular::api::SimCode v{1, 2, 3, 4};
+ cellular::api::SimCode empty;
+ REQUIRE(cellular::internal::simCodeToString(v) == "1234");
+ REQUIRE(cellular::internal::simCodeToString(empty).empty());
+ }
+
+ SECTION("Check Channel mockup sanity")
+ {
+ auto channel = at::QPINC_Channel();
+
+ auto resp = channel.cmd(at::factory(at::AT::QPINC));
+ REQUIRE(resp.code == at::Result::Code::OK);
+
+ at::response::qpinc::AttemptsCounters ret;
+ REQUIRE(at::response::parseQPINC(resp, ret));
+
+ REQUIRE(resp.code == at::Result::Code::OK);
+ REQUIRE(ret.PinCounter == 2);
+ REQUIRE(ret.PukCounter == 1);
+ }
+
+ SECTION("Unlock with PIN - sucessful")
+ {
+ cellular::service::SimCard simCard;
+
+ auto mockChannel = at::QPINC_Channel();
+ simCard.setChannel(&mockChannel);
+
+ bool event = false;
+ simCard.onUnhandledCME = [&event](unsigned int code) { event = true; };
+
+ auto result = simCard.handlePinUnlock({});
+ REQUIRE(result);
+ REQUIRE(!event);
+ }
+
+ SECTION("Unlock with PIN - incorrect password")
+ {
+ cellular::service::SimCard simCard;
+
+ auto mockChannel = at::QPINC_Channel(at::Result::Code::ERROR, at::EquipmentErrorCode::IncorrectPassword);
+ simCard.setChannel(&mockChannel);
+
+ bool event = false;
+ simCard.onUnhandledCME = [&event](unsigned int code) { event = true; };
+
+ auto result = simCard.handlePinUnlock({});
+ REQUIRE(!result);
+ REQUIRE(!event);
+ }
+
+ SECTION("Unlock with PIN - unhandled CME")
+ {
+ cellular::service::SimCard simCard;
+
+ auto mockChannel = at::QPINC_Channel(at::Result::Code::ERROR);
+ simCard.setChannel(&mockChannel);
+
+ bool event = false;
+ simCard.onUnhandledCME = [&event](unsigned int code) { event = true; };
+
+ auto result = simCard.handlePinUnlock({});
+ REQUIRE(!result);
+ REQUIRE(event);
}
}
M module-sys/Service/BusProxy.hpp => module-sys/Service/BusProxy.hpp +6 -0
@@ 30,6 30,12 @@ namespace sys
void sendMulticast(std::shared_ptr<Message> message, BusChannel channel);
void sendBroadcast(std::shared_ptr<Message> message);
+ template <typename Msg, typename... Params> bool sendUnicast(Params &&... params)
+ {
+ auto msg = std::make_shared<Msg>(std::forward<Params>(params)...);
+ return sendUnicast(msg, Msg::target);
+ }
+
std::vector<BusChannel> channels;
private: