Add extracted tools: CitrineOS, OpenOCPP, ShapeShifter
- CitrineOS core extracted (CSMS OCPP 2.0.1) - OpenOCPP extracted (firmware OCPP 1.6J/2.0.1) - ShapeShifter library installed (pip install -e) - ShapeShifter specification extracted - EVerest extracted TODO updated with progress
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
#
|
||||
# AUTO GENERATED - MARKED REGIONS WILL BE KEPT
|
||||
# template version 3
|
||||
#
|
||||
|
||||
# module setup:
|
||||
# - ${MODULE_NAME}: module name
|
||||
ev_setup_cpp_module()
|
||||
|
||||
# ev@bcc62523-e22b-41d7-ba2f-825b493a3c97:v1
|
||||
add_subdirectory(pn532_serial)
|
||||
|
||||
target_include_directories(${MODULE_NAME}
|
||||
PRIVATE
|
||||
"common"
|
||||
"pn532_serial"
|
||||
)
|
||||
|
||||
target_link_libraries(${MODULE_NAME}
|
||||
PRIVATE
|
||||
everest::helpers
|
||||
pn532_serial
|
||||
)
|
||||
# ev@bcc62523-e22b-41d7-ba2f-825b493a3c97:v1
|
||||
|
||||
target_sources(${MODULE_NAME}
|
||||
PRIVATE
|
||||
"main/auth_token_providerImpl.cpp"
|
||||
)
|
||||
|
||||
# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1
|
||||
# insert other things like install cmds etc here
|
||||
# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1
|
||||
@@ -0,0 +1,15 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2022 - 2022 Pionix GmbH and Contributors to EVerest
|
||||
#include "PN532TokenProvider.hpp"
|
||||
|
||||
namespace module {
|
||||
|
||||
void PN532TokenProvider::init() {
|
||||
invoke_init(*p_main);
|
||||
}
|
||||
|
||||
void PN532TokenProvider::ready() {
|
||||
invoke_ready(*p_main);
|
||||
}
|
||||
|
||||
} // namespace module
|
||||
@@ -0,0 +1,58 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
#ifndef PN532TOKEN_PROVIDER_HPP
|
||||
#define PN532TOKEN_PROVIDER_HPP
|
||||
|
||||
//
|
||||
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
|
||||
// template version 2
|
||||
//
|
||||
|
||||
#include "ld-ev.hpp"
|
||||
|
||||
// headers for provided interface implementations
|
||||
#include <generated/interfaces/auth_token_provider/Implementation.hpp>
|
||||
|
||||
// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1
|
||||
// insert your custom include headers here
|
||||
// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1
|
||||
|
||||
namespace module {
|
||||
|
||||
struct Conf {};
|
||||
|
||||
class PN532TokenProvider : public Everest::ModuleBase {
|
||||
public:
|
||||
PN532TokenProvider() = delete;
|
||||
PN532TokenProvider(const ModuleInfo& info, std::unique_ptr<auth_token_providerImplBase> p_main, Conf& config) :
|
||||
ModuleBase(info), p_main(std::move(p_main)), config(config){};
|
||||
|
||||
const std::unique_ptr<auth_token_providerImplBase> p_main;
|
||||
const Conf& config;
|
||||
|
||||
// ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1
|
||||
// insert your public definitions here
|
||||
// ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1
|
||||
|
||||
protected:
|
||||
// ev@4714b2ab-a24f-4b95-ab81-36439e1478de:v1
|
||||
// insert your protected definitions here
|
||||
// ev@4714b2ab-a24f-4b95-ab81-36439e1478de:v1
|
||||
|
||||
private:
|
||||
friend class LdEverest;
|
||||
void init();
|
||||
void ready();
|
||||
|
||||
// ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1
|
||||
// insert your private definitions here
|
||||
// ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1
|
||||
};
|
||||
|
||||
// ev@087e516b-124c-48df-94fb-109508c7cda9:v1
|
||||
// insert other definitions here
|
||||
// ev@087e516b-124c-48df-94fb-109508c7cda9:v1
|
||||
|
||||
} // namespace module
|
||||
|
||||
#endif // PN532TOKEN_PROVIDER_HPP
|
||||
@@ -0,0 +1,84 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#include "auth_token_providerImpl.hpp"
|
||||
|
||||
#include <everest/helpers/helpers.hpp>
|
||||
|
||||
#include <fmt/core.h>
|
||||
|
||||
namespace module {
|
||||
namespace main {
|
||||
|
||||
void auth_token_providerImpl::init() {
|
||||
// initialize serial driver
|
||||
if (config.debug) {
|
||||
serial.enableDebug();
|
||||
EVLOG_info << "Serial port: " << config.serial_port << " baud rate: " << config.baud_rate;
|
||||
}
|
||||
if (!serial.openDevice(config.serial_port.c_str(), config.baud_rate)) {
|
||||
if (!this->error_state_monitor->is_error_active("generic/CommunicationFault", "Communication timed out")) {
|
||||
auto error_message =
|
||||
fmt::format("Could not open serial port {} with baud rate {}", config.serial_port, config.baud_rate);
|
||||
auto error = this->error_factory->create_error("generic/CommunicationFault", "Communication timed out",
|
||||
error_message);
|
||||
raise_error(error);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void auth_token_providerImpl::ready() {
|
||||
serial.run();
|
||||
|
||||
serial.reset();
|
||||
// configure Secure Access Module (SAM)
|
||||
auto configure_sam_future = serial.configureSAM();
|
||||
while (configure_sam_future.wait_for(std::chrono::milliseconds(100)) != std::future_status::ready) {
|
||||
EVLOG_verbose << "Retrying to configure SAM";
|
||||
configure_sam_future = serial.configureSAM();
|
||||
}
|
||||
auto configure_sam = configure_sam_future.get();
|
||||
if (configure_sam) {
|
||||
EVLOG_debug << "Configured SAM";
|
||||
}
|
||||
|
||||
auto firmware_version_future = serial.getFirmwareVersion();
|
||||
while (firmware_version_future.wait_for(std::chrono::milliseconds(100)) != std::future_status::ready) {
|
||||
EVLOG_verbose << "Retrying to get firmware version";
|
||||
firmware_version_future = serial.getFirmwareVersion();
|
||||
}
|
||||
auto firmware_version = firmware_version_future.get();
|
||||
if (firmware_version.valid) {
|
||||
std::shared_ptr<FirmwareVersion> fv = std::dynamic_pointer_cast<FirmwareVersion>(firmware_version.message);
|
||||
EVLOG_info << "PN532 firmware version: " << std::to_string(fv->ver) << "." << std::to_string(fv->rev);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
auto in_list_passive_target_response_future = serial.inListPassiveTarget();
|
||||
while (in_list_passive_target_response_future.wait_for(std::chrono::seconds(5)) != std::future_status::ready) {
|
||||
EVLOG_verbose << "Retrying to get target";
|
||||
in_list_passive_target_response_future = serial.inListPassiveTarget();
|
||||
}
|
||||
auto in_list_passive_target_response = in_list_passive_target_response_future.get();
|
||||
if (in_list_passive_target_response.valid) {
|
||||
std::shared_ptr<InListPassiveTargetResponse> in_list_passive_target_response_message =
|
||||
std::dynamic_pointer_cast<InListPassiveTargetResponse>(in_list_passive_target_response.message);
|
||||
auto target_data = in_list_passive_target_response_message->target_data;
|
||||
for (auto entry : target_data) {
|
||||
types::authorization::ProvidedIdToken provided_token;
|
||||
provided_token.id_token = {entry.getNFCID(), types::authorization::IdTokenType::ISO14443};
|
||||
provided_token.authorization_type = types::authorization::AuthorizationType::RFID;
|
||||
if (config.debug) {
|
||||
EVLOG_info << "Publishing new rfid/nfc token: " << everest::helpers::redact(provided_token);
|
||||
}
|
||||
this->publish_provided_token(provided_token);
|
||||
}
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::seconds(config.read_timeout));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace main
|
||||
} // namespace module
|
||||
@@ -0,0 +1,68 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
#ifndef MAIN_AUTH_TOKEN_PROVIDER_IMPL_HPP
|
||||
#define MAIN_AUTH_TOKEN_PROVIDER_IMPL_HPP
|
||||
|
||||
//
|
||||
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
|
||||
// template version 3
|
||||
//
|
||||
|
||||
#include <generated/interfaces/auth_token_provider/Implementation.hpp>
|
||||
|
||||
#include "../PN532TokenProvider.hpp"
|
||||
|
||||
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
|
||||
// insert your custom include headers here
|
||||
#include "pn532_serial/PN532Serial.h"
|
||||
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
|
||||
|
||||
namespace module {
|
||||
namespace main {
|
||||
|
||||
struct Conf {
|
||||
std::string serial_port;
|
||||
int baud_rate;
|
||||
int read_timeout;
|
||||
bool debug;
|
||||
};
|
||||
|
||||
class auth_token_providerImpl : public auth_token_providerImplBase {
|
||||
public:
|
||||
auth_token_providerImpl() = delete;
|
||||
auth_token_providerImpl(Everest::ModuleAdapter* ev, const Everest::PtrContainer<PN532TokenProvider>& mod,
|
||||
Conf& config) :
|
||||
auth_token_providerImplBase(ev, "main"), mod(mod), config(config){};
|
||||
|
||||
// ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1
|
||||
// insert your public definitions here
|
||||
// ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1
|
||||
|
||||
protected:
|
||||
// no commands defined for this interface
|
||||
|
||||
// ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1
|
||||
// insert your protected definitions here
|
||||
// ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1
|
||||
|
||||
private:
|
||||
const Everest::PtrContainer<PN532TokenProvider>& mod;
|
||||
const Conf& config;
|
||||
|
||||
virtual void init() override;
|
||||
virtual void ready() override;
|
||||
|
||||
// ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1
|
||||
// insert your private definitions here
|
||||
PN532Serial serial;
|
||||
// ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1
|
||||
};
|
||||
|
||||
// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1
|
||||
// insert other definitions here
|
||||
// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1
|
||||
|
||||
} // namespace main
|
||||
} // namespace module
|
||||
|
||||
#endif // MAIN_AUTH_TOKEN_PROVIDER_IMPL_HPP
|
||||
@@ -0,0 +1,33 @@
|
||||
description: PN532 RFID/NFC token provider returning the token as soon as the tag can be read by the reader
|
||||
provides:
|
||||
main:
|
||||
description: Implementation of PN532 RFID/NFC token provider
|
||||
interface: auth_token_provider
|
||||
config:
|
||||
serial_port:
|
||||
description: Serial port the PN532 hardware is connected to
|
||||
type: string
|
||||
default: /dev/ttyS0
|
||||
baud_rate:
|
||||
description: Serial baud rate to use when communicating with PN532 hardware
|
||||
type: integer
|
||||
minimum: 9600
|
||||
maximum: 230400
|
||||
default: 115200
|
||||
read_timeout:
|
||||
description: Time between subsequent card reads (in s)
|
||||
type: integer
|
||||
minimum: 0
|
||||
maximum: 120
|
||||
default: 5
|
||||
debug:
|
||||
description: Show debug output on command line.
|
||||
type: boolean
|
||||
default: false
|
||||
metadata:
|
||||
license: https://opensource.org/licenses/Apache-2.0
|
||||
authors:
|
||||
- Cornelius Claussen
|
||||
- Kai-Uwe Hermann
|
||||
- Thilo Molitor
|
||||
- Anton Wöllert
|
||||
@@ -0,0 +1,18 @@
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
# set the project name
|
||||
project(pn532_serial VERSION 0.1)
|
||||
# specify the C++ standard
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
||||
|
||||
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
# add the executable
|
||||
add_library(pn532_serial STATIC PN532Serial.cpp)
|
||||
ev_register_library_target(pn532_serial)
|
||||
|
||||
target_include_directories(pn532_serial PUBLIC "${PROJECT_BINARY_DIR}")
|
||||
target_link_libraries(pn532_serial PRIVATE Threads::Threads everest::framework)
|
||||
@@ -0,0 +1,331 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2022 Pionix GmbH and Contributors to EVerest
|
||||
#include "PN532Serial.h"
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <everest/logging.hpp>
|
||||
|
||||
constexpr size_t FIRMWARE_VERSION_IC_POS = 0;
|
||||
constexpr size_t FIRMWARE_VERSION_VER_POS = 1;
|
||||
constexpr size_t FIRMWARE_VERSION_REV_POS = 2;
|
||||
constexpr size_t FIRMWARE_VERSION_SUPPORT_POS = 3;
|
||||
|
||||
constexpr size_t IN_LIST_PASSIVE_TARGET_NBTG = 0;
|
||||
constexpr size_t IN_LIST_PASSIVE_TARGET_TG = 1;
|
||||
constexpr size_t IN_LIST_PASSIVE_TARGET_SENS_RES_MSB = 2;
|
||||
constexpr size_t IN_LIST_PASSIVE_TARGET_SENS_RES_LSB = 3;
|
||||
constexpr size_t IN_LIST_PASSIVE_TARGET_SEL_RES = 4;
|
||||
constexpr size_t IN_LIST_PASSIVE_TARGET_NFCID_LENGTH = 5;
|
||||
|
||||
// command codes
|
||||
constexpr uint8_t COMMAND_GET_FIRMWARE_VERSION = 0x02;
|
||||
constexpr uint8_t COMMAND_GET_FIRMWARE_VERSION_RESPONSE = 0x03;
|
||||
constexpr uint8_t COMMAND_SAM_CONFIGURATION = 0x14;
|
||||
constexpr uint8_t COMMAND_SAM_CONFIGURATION_RESPONSE = 0x15;
|
||||
constexpr uint8_t COMMAND_IN_LIST_PASSIVE_TARGET = 0x4a;
|
||||
constexpr uint8_t COMMAND_IN_LIST_PASSIVE_TARGET_RESPONSE = 0x4b;
|
||||
|
||||
void PN532Serial::run() {
|
||||
readThreadHandle = std::thread(&PN532Serial::readThread, this);
|
||||
}
|
||||
|
||||
void PN532Serial::readThread() {
|
||||
uint8_t buf[2048];
|
||||
size_t n = 0;
|
||||
ssize_t first_start_code_offset;
|
||||
|
||||
while (true) {
|
||||
if (readThreadHandle.shouldExit()) {
|
||||
break;
|
||||
}
|
||||
auto c = read(fd, &buf[n], sizeof(buf) - n);
|
||||
if (c == -1) {
|
||||
EVLOG_error << "Read failed: " << strerror(errno) << " (" << errno << ")";
|
||||
break;
|
||||
}
|
||||
if (c == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (this->debug) {
|
||||
EVLOG_info << "Received: " << hexdump(buf + n, c);
|
||||
}
|
||||
|
||||
n += c;
|
||||
|
||||
restart_buffer_analysis:
|
||||
EVLOG_verbose << "buffer content (size: " << n << "): " << hexdump(buf, n);
|
||||
|
||||
// packet minimum size is 6 byte for ACK or NACK, other frames are larger
|
||||
if (n < 6) {
|
||||
continue;
|
||||
}
|
||||
|
||||
first_start_code_offset = -1;
|
||||
|
||||
// we cannot assume that we receive the whole response in one read call
|
||||
// because of different driver/interfaces, so we have to scan for well-known
|
||||
// patterns or check whether it looks like a valid frame after a start code
|
||||
// pattern; if we find one, we can delete it from the buffer and move the
|
||||
// left-over buffer content to the front, then we must restart analyzing
|
||||
// to be sure to handle any following frame immediately and don't wait
|
||||
// until the next read call runs/returns
|
||||
for (ssize_t i = 0; i <= n - 6; ++i) {
|
||||
// check for ACK or NACKs
|
||||
if (memcmp(&buf[i], ACK_FRAME.data(), ACK_FRAME.size()) == 0) {
|
||||
EVLOG_debug << "Received ACK";
|
||||
n = n - i - ACK_FRAME.size();
|
||||
memmove(buf, buf + i + ACK_FRAME.size(), n);
|
||||
goto restart_buffer_analysis;
|
||||
}
|
||||
if (memcmp(&buf[i], NACK_FRAME.data(), NACK_FRAME.size()) == 0) {
|
||||
EVLOG_debug << "Received NACK";
|
||||
n = n - i - NACK_FRAME.size();
|
||||
memmove(buf, buf + i + NACK_FRAME.size(), n);
|
||||
goto restart_buffer_analysis;
|
||||
}
|
||||
|
||||
// check for start_code
|
||||
if (memcmp(&buf[i], START_CODE.data(), START_CODE.size()) == 0) {
|
||||
if (first_start_code_offset == -1) {
|
||||
first_start_code_offset = i;
|
||||
}
|
||||
|
||||
auto len = buf[i + START_CODE.size()];
|
||||
auto lcs = buf[i + START_CODE.size() + 1];
|
||||
|
||||
if (((len + lcs) & 0xff) != 0) {
|
||||
EVLOG_verbose << "package length checksum mismatch";
|
||||
continue;
|
||||
}
|
||||
|
||||
// offset + start code size + len and lcs byte + data length + dcs and postamble
|
||||
if (i + START_CODE.size() + 2 + len + 2 > n) {
|
||||
EVLOG_verbose << "frame still too short or length bogus";
|
||||
continue;
|
||||
}
|
||||
|
||||
// accesses beyond i are safe below since we ensure that above
|
||||
auto tfi = buf[i + START_CODE.size() + 2];
|
||||
if (tfi != PN532_TO_HOST) {
|
||||
EVLOG_verbose << "frame is not a response";
|
||||
continue;
|
||||
}
|
||||
|
||||
if (buf[i + START_CODE.size() + 2 + len + 1] != 0x00) {
|
||||
EVLOG_verbose << "postamble check failed";
|
||||
continue;
|
||||
}
|
||||
|
||||
auto dcs_compl = 0;
|
||||
for (size_t j = 0; j < len; ++j) {
|
||||
dcs_compl += buf[i + START_CODE.size() + 2 + j];
|
||||
}
|
||||
auto dcs = buf[i + START_CODE.size() + 2 + len];
|
||||
if (((dcs_compl + dcs) & 0xff) != 0) {
|
||||
EVLOG_verbose << "package data checksum does not match";
|
||||
continue;
|
||||
}
|
||||
|
||||
// all checks so far indicate that frame/packet looks good
|
||||
command_code = buf[i + START_CODE.size() + 2 + 1];
|
||||
data = std::vector<uint8_t>(&buf[i + START_CODE.size() + 2 + 1 + 1],
|
||||
&buf[i + START_CODE.size() + 2 + len]);
|
||||
parseData();
|
||||
|
||||
// discard processed data, the processed size is:
|
||||
// offset (incl. preamble) + start code size + len and lcs byte + data length + dcs and postamble
|
||||
auto processed_size = i + START_CODE.size() + 1 + 1 + data.size() + 1 + 1;
|
||||
n = n - processed_size;
|
||||
memmove(buf, buf + processed_size, n);
|
||||
goto restart_buffer_analysis;
|
||||
}
|
||||
}
|
||||
|
||||
if (first_start_code_offset == -1) {
|
||||
// no start code found so far: continue with outer loop and receive more data
|
||||
} else {
|
||||
// use first start code offset found and assume garbage before -> discard this
|
||||
n -= first_start_code_offset;
|
||||
memmove(buf, buf + first_start_code_offset, n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PN532Serial::parseData() {
|
||||
if (command_code == COMMAND_GET_FIRMWARE_VERSION_RESPONSE && this->get_firmware_version_promise) {
|
||||
PN532Response response;
|
||||
if (data.size() == 4) {
|
||||
EVLOG_debug << "Sending ACK";
|
||||
serialWrite(ACK_FRAME);
|
||||
|
||||
response.valid = true;
|
||||
response.message = std::make_shared<FirmwareVersion>(
|
||||
data.at(FIRMWARE_VERSION_IC_POS), data.at(FIRMWARE_VERSION_VER_POS), data.at(FIRMWARE_VERSION_REV_POS),
|
||||
data.at(FIRMWARE_VERSION_SUPPORT_POS));
|
||||
|
||||
this->get_firmware_version_promise->set_value(response);
|
||||
} else {
|
||||
EVLOG_debug << "Sending NACK";
|
||||
serialWrite(NACK_FRAME);
|
||||
}
|
||||
} else if (command_code == COMMAND_SAM_CONFIGURATION_RESPONSE && this->configure_sam_promise) {
|
||||
EVLOG_debug << "Sending ACK";
|
||||
serialWrite(ACK_FRAME);
|
||||
this->configure_sam_promise->set_value(true);
|
||||
} else if (command_code == COMMAND_IN_LIST_PASSIVE_TARGET_RESPONSE && this->in_list_passive_target_promise) {
|
||||
// always send ACK for now
|
||||
EVLOG_debug << "Sending ACK";
|
||||
serialWrite(ACK_FRAME);
|
||||
parseInListPassiveTargetResponse();
|
||||
}
|
||||
}
|
||||
|
||||
void PN532Serial::parseInListPassiveTargetResponse() {
|
||||
PN532Response response;
|
||||
auto in_list_passive_target_response = std::make_shared<InListPassiveTargetResponse>();
|
||||
TargetData target_data;
|
||||
if (data.size() >= 6) {
|
||||
in_list_passive_target_response->nbtg = data.at(IN_LIST_PASSIVE_TARGET_NBTG);
|
||||
if (in_list_passive_target_response->nbtg == 1) {
|
||||
target_data.tg = data.at(IN_LIST_PASSIVE_TARGET_TG);
|
||||
target_data.sens_res_msb = data.at(IN_LIST_PASSIVE_TARGET_SENS_RES_MSB);
|
||||
target_data.sens_res_lsb = data.at(IN_LIST_PASSIVE_TARGET_SENS_RES_LSB);
|
||||
target_data.sens_res = (target_data.sens_res_msb << 8) + target_data.sens_res_lsb;
|
||||
target_data.sel_res = data.at(IN_LIST_PASSIVE_TARGET_SEL_RES);
|
||||
target_data.nfcid_length = data.at(IN_LIST_PASSIVE_TARGET_NFCID_LENGTH);
|
||||
|
||||
if (this->debug) {
|
||||
EVLOG_info << "Target data: " << std::right << std::hex << "\ntg: 0x" << std::setfill('0')
|
||||
<< std::setw(2) << (int)target_data.tg << "\nsens_res_msb: 0x" << std::setfill('0')
|
||||
<< std::setw(2) << (int)target_data.sens_res_msb << "\nsens_res_lsb: 0x" << std::setfill('0')
|
||||
<< std::setw(2) << (int)target_data.sens_res_lsb << "\nsens_res: 0x" << std::setfill('0')
|
||||
<< std::setw(4) << (int)target_data.sens_res << "\nsel_res: 0x" << std::setfill('0')
|
||||
<< std::setw(2) << (int)target_data.sel_res << "\nnfcid_length: 0x" << std::setfill('0')
|
||||
<< std::setw(2) << (int)target_data.nfcid_length;
|
||||
}
|
||||
|
||||
if (data.size() >= 6 + target_data.nfcid_length) {
|
||||
response.valid = true;
|
||||
for (ssize_t i = 6; i < 6 + target_data.nfcid_length; i++) {
|
||||
target_data.nfcid.push_back(data.at(i));
|
||||
}
|
||||
|
||||
if (this->debug) {
|
||||
std::stringstream nfcid_stream;
|
||||
nfcid_stream << hexdump(target_data.nfcid);
|
||||
nfcid_stream << " (length: " << std::dec << target_data.nfcid.size() << ")";
|
||||
EVLOG_info << "NFCID: " << nfcid_stream.str();
|
||||
}
|
||||
|
||||
if (data.size() >= 6 + target_data.nfcid_length + 1) {
|
||||
auto ats_length = data.at(6 + target_data.nfcid_length);
|
||||
|
||||
if (data.size() >= 6 + target_data.nfcid_length + ats_length) {
|
||||
for (size_t i = 6 + target_data.nfcid_length; i < 6 + target_data.nfcid_length + ats_length;
|
||||
i++) {
|
||||
target_data.ats.push_back(data.at(i));
|
||||
}
|
||||
}
|
||||
|
||||
if (this->debug) {
|
||||
std::stringstream ats_stream;
|
||||
ats_stream << hexdump(target_data.ats);
|
||||
ats_stream << " (length: " << std::dec << target_data.ats.size() << ")";
|
||||
EVLOG_info << "ATS: " << ats_stream.str();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
in_list_passive_target_response->target_data.push_back(target_data);
|
||||
}
|
||||
}
|
||||
|
||||
response.message = in_list_passive_target_response;
|
||||
this->in_list_passive_target_promise->set_value(response);
|
||||
}
|
||||
|
||||
std::future<bool> PN532Serial::configureSAM() {
|
||||
this->configure_sam_promise = std::make_unique<std::promise<bool>>();
|
||||
this->serialWriteCommand({
|
||||
COMMAND_SAM_CONFIGURATION, // command code
|
||||
0x01, // normal mode
|
||||
0x00, // no timeout
|
||||
0x01 // P70_IRQ pin driven by PN532
|
||||
});
|
||||
return this->configure_sam_promise->get_future();
|
||||
}
|
||||
|
||||
std::future<PN532Response> PN532Serial::getFirmwareVersion() {
|
||||
this->get_firmware_version_promise = std::make_unique<std::promise<PN532Response>>();
|
||||
this->serialWriteCommand({COMMAND_GET_FIRMWARE_VERSION});
|
||||
return this->get_firmware_version_promise->get_future();
|
||||
}
|
||||
|
||||
std::future<PN532Response> PN532Serial::inListPassiveTarget() {
|
||||
this->in_list_passive_target_promise = std::make_unique<std::promise<PN532Response>>();
|
||||
this->serialWriteCommand({
|
||||
COMMAND_IN_LIST_PASSIVE_TARGET, // command code
|
||||
0x01, // one target
|
||||
0x00 // 105 kbps type A (ISO/IEC1443 Type A)
|
||||
});
|
||||
return this->in_list_passive_target_promise->get_future();
|
||||
}
|
||||
|
||||
bool PN532Serial::serialWrite(const std::vector<uint8_t>& data) {
|
||||
auto rv = write(fd, data.data(), data.size());
|
||||
|
||||
if (rv != data.size()) {
|
||||
EVLOG_error << "Write failed: " << strerror(errno) << " (" << errno << ")";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PN532Serial::serialWriteCommand(const std::vector<uint8_t>& data) {
|
||||
// calculate packet length checksum
|
||||
uint8_t lcs = data.size() + 1;
|
||||
lcs = (uint8_t)-lcs;
|
||||
|
||||
// calculate data check sum
|
||||
uint8_t dcs = HOST_TO_PN532;
|
||||
for (auto element : data) {
|
||||
dcs += element;
|
||||
}
|
||||
dcs = (uint8_t)-dcs;
|
||||
|
||||
std::vector<uint8_t> serial_data;
|
||||
// size: preamble + start code + LEN + LCS + TFI + data + DCS + postamble
|
||||
serial_data.reserve(1 + START_CODE.size() + 1 + 1 + 1 + data.size() + 1 + 1);
|
||||
serial_data.push_back(PREAMBLE);
|
||||
serial_data.insert(serial_data.end(), START_CODE.begin(), START_CODE.end());
|
||||
serial_data.push_back(data.size() + 1);
|
||||
serial_data.push_back(lcs);
|
||||
serial_data.push_back(HOST_TO_PN532);
|
||||
serial_data.insert(serial_data.end(), data.begin(), data.end());
|
||||
serial_data.push_back(dcs);
|
||||
serial_data.push_back(POSTAMBLE);
|
||||
|
||||
if (this->debug) {
|
||||
EVLOG_info << "Sending bytes: " << hexdump(serial_data);
|
||||
}
|
||||
|
||||
return this->serialWrite(serial_data);
|
||||
}
|
||||
|
||||
bool PN532Serial::reset() {
|
||||
return this->serialWrite(
|
||||
{0x55, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
|
||||
}
|
||||
|
||||
void PN532Serial::enableDebug() {
|
||||
this->debug = true;
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2022 Pionix GmbH and Contributors to EVerest
|
||||
#ifndef PN532_SERIAL
|
||||
#define PN532_SERIAL
|
||||
|
||||
#include <future>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <stdint.h>
|
||||
#include <termios.h>
|
||||
#include <utils/serial.hpp>
|
||||
#include <utils/thread.hpp>
|
||||
#include <vector>
|
||||
|
||||
enum class PN532Command {
|
||||
SAMConfiguration,
|
||||
GetFirmwareVersion
|
||||
};
|
||||
|
||||
struct PN532Message {
|
||||
virtual ~PN532Message() = default;
|
||||
};
|
||||
|
||||
struct PN532Response {
|
||||
bool valid = false;
|
||||
std::shared_ptr<PN532Message> message;
|
||||
};
|
||||
|
||||
struct FirmwareVersion : public PN532Message {
|
||||
uint8_t ic;
|
||||
uint8_t ver; // TODO: also pre-parsed version string of this
|
||||
uint8_t rev;
|
||||
uint8_t support; // TODO: bool flags
|
||||
FirmwareVersion(uint8_t ic, uint8_t ver, uint8_t rev, uint8_t support) :
|
||||
ic(ic), ver(ver), rev(rev), support(support) {
|
||||
}
|
||||
};
|
||||
|
||||
struct TargetData {
|
||||
uint8_t tg;
|
||||
uint8_t sens_res_msb;
|
||||
uint8_t sens_res_lsb;
|
||||
uint16_t sens_res;
|
||||
uint8_t sel_res;
|
||||
uint8_t nfcid_length;
|
||||
std::vector<uint8_t> nfcid;
|
||||
std::vector<uint8_t> ats;
|
||||
|
||||
std::string getNFCID() {
|
||||
std::stringstream ss;
|
||||
for (auto it = nfcid.begin(); it != nfcid.end(); it++) {
|
||||
int id = *it;
|
||||
ss << std::setfill('0') << std::setw(2) << std::hex << id;
|
||||
}
|
||||
return ss.str();
|
||||
};
|
||||
};
|
||||
|
||||
struct InListPassiveTargetResponse : public PN532Message {
|
||||
uint8_t nbtg = 0;
|
||||
std::vector<TargetData> target_data;
|
||||
};
|
||||
|
||||
class PN532Serial : public Everest::Serial {
|
||||
|
||||
public:
|
||||
PN532Serial() = default;
|
||||
virtual ~PN532Serial() = default;
|
||||
|
||||
void readThread();
|
||||
void run() override;
|
||||
bool reset();
|
||||
bool serialWrite(const std::vector<uint8_t>& data);
|
||||
bool serialWriteCommand(const std::vector<uint8_t>& data);
|
||||
std::future<bool> configureSAM();
|
||||
std::future<PN532Response> getFirmwareVersion();
|
||||
std::future<PN532Response> inListPassiveTarget();
|
||||
void enableDebug();
|
||||
|
||||
private:
|
||||
static constexpr uint8_t PREAMBLE = 0x00;
|
||||
static constexpr uint8_t POSTAMBLE = 0x00;
|
||||
static constexpr uint8_t HOST_TO_PN532 = 0xd4;
|
||||
static constexpr uint8_t PN532_TO_HOST = 0xd5;
|
||||
const std::vector<uint8_t> START_CODE{0x00, 0xff};
|
||||
const std::vector<uint8_t> ACK_FRAME{0x00, 0x00, 0xff, 0x00, 0xff, 0x00};
|
||||
const std::vector<uint8_t> NACK_FRAME{0x00, 0x00, 0xff, 0xff, 0x00, 0x00};
|
||||
std::unique_ptr<std::promise<bool>> configure_sam_promise;
|
||||
std::unique_ptr<std::promise<PN532Response>> get_firmware_version_promise;
|
||||
std::unique_ptr<std::promise<PN532Response>> in_list_passive_target_promise;
|
||||
uint8_t command_code = 0;
|
||||
std::vector<uint8_t> data;
|
||||
bool debug = false;
|
||||
|
||||
void parseData();
|
||||
void parseInListPassiveTargetResponse();
|
||||
|
||||
// Read thread for serial port
|
||||
Everest::Thread readThreadHandle;
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user