| // Copyright 2018 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef HERMES_MODEM_QRTR_H_ |
| #define HERMES_MODEM_QRTR_H_ |
| |
| #include <deque> |
| #include <iostream> |
| #include <memory> |
| #include <vector> |
| |
| #include <google-lpa/lpa/card/euicc_card.h> |
| #include <libqrtr.h> |
| |
| #include "hermes/executor.h" |
| #include "hermes/logger.h" |
| #include "hermes/qmi_uim.h" |
| #include "hermes/socket_qrtr.h" |
| |
| namespace hermes { |
| |
| // Implementation of EuiccCard using QRTR sockets to send QMI UIM |
| // messages. |
| class ModemQrtr : public lpa::card::EuiccCard { |
| public: |
| // Base class for the tx info specific to a certain type of uim command. |
| // Uim command types that need any additional information should define |
| // a child class. An instance of that class should be set to the data pointer |
| // in its corresponding TxElement. |
| struct TxInfo { |
| virtual ~TxInfo() = default; |
| }; |
| |
| using ResponseCallback = |
| std::function<void(std::vector<std::vector<uint8_t>>& |
| responses, // NOLINT(runtime/references) |
| int err)>; |
| |
| static std::unique_ptr<ModemQrtr> Create( |
| std::unique_ptr<SocketInterface> socket, |
| Logger* logger, |
| Executor* executor); |
| virtual ~ModemQrtr(); |
| |
| // lpa::card::EuiccCard overrides. |
| void SendApdus(std::vector<lpa::card::Apdu> apdus, |
| ResponseCallback cb) override; |
| bool IsSimValidAfterEnable() override; |
| bool IsSimValidAfterDisable() override; |
| lpa::util::EuiccLog* logger() override { return logger_; } |
| |
| private: |
| struct TxElement { |
| std::unique_ptr<TxInfo> info_; |
| uint16_t id_; |
| QmiUimCommand uim_type_; |
| }; |
| |
| ModemQrtr(std::unique_ptr<SocketInterface> socket, |
| Logger* logger, |
| Executor* executor); |
| void Initialize(); |
| void FinalizeInitialization(); |
| void Shutdown(); |
| uint16_t AllocateId(); |
| |
| // Top-level method to transmit an element from the tx queue. Dispatches to |
| // the proper TransmitQmi* method to perform QMI encoding prior to sending |
| // data to the socket. Will remove elements from the tx queue as needed. |
| void TransmitFromQueue(); |
| // Creates and sends RESET QMI request. |
| void TransmitQmiReset(TxElement* tx_element); |
| // Creates and sends OPEN_LOGICAL_CHANNEL QMI request. |
| void TransmitQmiOpenLogicalChannel(TxElement* tx_element); |
| // Creates and sends SEND_APDU QMI request. |
| void TransmitQmiSendApdu(TxElement* tx_element); |
| // Performs QMI encoding and sends data to the QRTR socket. |
| bool SendCommand(QmiUimCommand type, |
| uint16_t id, |
| void* c_struct, |
| qmi_elem_info* ei); |
| |
| // Top-level method when a packet is read from the socket into |buffer_|. Will |
| // perform proper processing based on QRTR packet type. Attempts to transmit |
| // the next element in the tx queue when complete. |
| void ProcessQrtrPacket(uint32_t node, uint32_t port, int size); |
| // Dispatches to proper ReceiveQmi* method based on QMI type. |
| void ProcessQmiPacket(const qrtr_packet& packet); |
| // Performs decoding for OPEN_LOGICAL_CHANNEL QMI response. |
| void ReceiveQmiOpenLogicalChannel(const qrtr_packet& packet); |
| // Performs decoding for SEND_APDU response and calls |on_recv_| with |
| // appropriate parameters. |
| void ReceiveQmiSendApdu(const qrtr_packet& packet); |
| |
| void OnDataAvailable(SocketInterface* socket); |
| |
| // lpa::card::EuiccCard overrides. |
| const lpa::proto::EuiccSpecVersion& GetCardVersion() override; |
| lpa::util::Executor* executor() override { return executor_; } |
| |
| private: |
| friend class ModemQrtrTest; |
| |
| /////////////////// |
| // State Diagram // |
| /////////////////// |
| // |
| // [Start state] |
| // +---------------+ (FinalizeInitialization() called w/failure) |
| // | Uninitialized | <--------------------------------------------+ |
| // +---------------+ | |
| // + | |
| // | (Initialize() called) | |
| // | | |
| // V | |
| // +-------------------+ +------------+ | |
| // | InitializeStarted | +-> | UimStarted | +---+ | |
| // +-------------------+ +------------+ | | |
| // | | |
| // +----------------------------------+ | |
| // | | |
| // V | |
| // +-----------------------+ +----------------------+ | |
| // | LogicalChannelPending | +-> | LogicalChannelOpened | +---------+ |
| // +-----------------------+ +----------------------+ | |
| // | |
| // +------------------------------------------------------+ |
| // | (FinalizeInitialization() called w/success) |
| // V |
| // +-------+ |
| // | Ready | <--------------------+ |
| // +-------+ | |
| // + | |
| // | (Request sent) | (Response received) |
| // | | |
| // V | |
| // +--------------------+ | |
| // | WaitingForResponse | +-------------+ |
| // +--------------------+ |
| // |
| class State { |
| public: |
| enum Value : uint8_t { |
| kUninitialized, |
| kInitializeStarted, |
| kUimStarted, |
| kLogicalChannelPending, |
| kLogicalChannelOpened, |
| kReady, |
| kWaitingForResponse, |
| }; |
| |
| State() : value_(kUninitialized) {} |
| // Transitions to the indicated state. Returns whether or not the transition |
| // was successful. |
| bool Transition(Value value); |
| |
| bool IsInitialized() const { |
| return value_ == kReady || value_ == kWaitingForResponse; |
| } |
| // Returns whether or not some QMI packet can be sent out in the state. Note |
| // that APDUs in particular may only be sent in the kReady state. |
| bool CanSend() const { return value_ == kUimStarted || value_ == kReady; } |
| |
| bool operator==(Value value) const { return value_ == value; } |
| bool operator!=(Value value) const { return value_ != value; } |
| friend std::ostream& operator<<(std::ostream& os, const State state) { |
| os << state.value_; |
| return os; |
| } |
| |
| private: |
| explicit State(Value value) : value_(value) {} |
| |
| Value value_; |
| }; |
| |
| State current_state_; |
| |
| bool extended_apdu_supported_; |
| uint16_t current_transaction_id_; |
| |
| // Logical Channel that will be used to communicate with the chip, returned |
| // from OPEN_LOGICAL_CHANNEL request sent once the QRTR socket has been |
| // opened. |
| uint8_t channel_; |
| // The slot that the logical channel to the eSIM will be made. Initialized in |
| // constructor, hardware specific. |
| uint8_t slot_; |
| |
| std::unique_ptr<SocketInterface> socket_; |
| SocketQrtr::PacketMetadata metadata_; |
| |
| // Buffer for storing data from the QRTR socket |
| std::vector<uint8_t> buffer_; |
| // List of responses for the oldest SendApdus call that hasn't been completely |
| // processed. |
| std::vector<std::vector<uint8_t>> responses_; |
| // Queue of packets to send to the modem |
| std::deque<TxElement> tx_queue_; |
| |
| Logger* logger_; |
| Executor* executor_; |
| lpa::proto::EuiccSpecVersion spec_version_; |
| }; |
| |
| } // namespace hermes |
| |
| #endif // HERMES_MODEM_QRTR_H_ |