NFC: extract frontend-facing tag state
Added a new state amiibo_in_range. This state is akin to the real world physical relationship between a 3DS machine and an amiibo, which is independent from the service state (or even the machine is powered on or not). The service state nfc_tag_state is then synchronized with this physical state on every potential point when the state changes. This solves the issue where user might load an amiibo before NFC service initializes, or remove an amiibo after NFC service shutdown, which previously causes inconsistent state change. Also removed std::atomic on nfc_tag_state, because 1. It is already protected by g_hle_lock 2. It wasn't properly used in the code anyway. For example, there are many double loading on this variable, which effectively make it non-atomic.
This commit is contained in:
parent
32b88d4719
commit
8b8111b425
|
@ -53,7 +53,7 @@ void Module::Interface::Initialize(Kernel::HLERequestContext& ctx) {
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
if (nfc->nfc_tag_state != TagState::NotInitialized) {
|
if (nfc->nfc_tag_state != TagState::NotInitialized) {
|
||||||
LOG_ERROR(Service_NFC, "Invalid TagState {}", static_cast<int>(nfc->nfc_tag_state.load()));
|
LOG_ERROR(Service_NFC, "Invalid TagState {}", static_cast<int>(nfc->nfc_tag_state));
|
||||||
rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC,
|
rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC,
|
||||||
ErrorSummary::InvalidState, ErrorLevel::Status));
|
ErrorSummary::InvalidState, ErrorLevel::Status));
|
||||||
return;
|
return;
|
||||||
|
@ -99,13 +99,14 @@ void Module::Interface::StartTagScanning(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
if (nfc->nfc_tag_state != TagState::NotScanning &&
|
if (nfc->nfc_tag_state != TagState::NotScanning &&
|
||||||
nfc->nfc_tag_state != TagState::TagOutOfRange) {
|
nfc->nfc_tag_state != TagState::TagOutOfRange) {
|
||||||
LOG_ERROR(Service_NFC, "Invalid TagState {}", static_cast<int>(nfc->nfc_tag_state.load()));
|
LOG_ERROR(Service_NFC, "Invalid TagState {}", static_cast<int>(nfc->nfc_tag_state));
|
||||||
rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC,
|
rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC,
|
||||||
ErrorSummary::InvalidState, ErrorLevel::Status));
|
ErrorSummary::InvalidState, ErrorLevel::Status));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
nfc->nfc_tag_state = TagState::Scanning;
|
nfc->nfc_tag_state = TagState::Scanning;
|
||||||
|
nfc->SyncTagState();
|
||||||
|
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
LOG_WARNING(Service_NFC, "(STUBBED) called, in_val={:04x}", in_val);
|
LOG_WARNING(Service_NFC, "(STUBBED) called, in_val={:04x}", in_val);
|
||||||
|
@ -116,7 +117,7 @@ void Module::Interface::GetTagInfo(Kernel::HLERequestContext& ctx) {
|
||||||
|
|
||||||
if (nfc->nfc_tag_state != TagState::TagInRange &&
|
if (nfc->nfc_tag_state != TagState::TagInRange &&
|
||||||
nfc->nfc_tag_state != TagState::TagDataLoaded && nfc->nfc_tag_state != TagState::Unknown6) {
|
nfc->nfc_tag_state != TagState::TagDataLoaded && nfc->nfc_tag_state != TagState::Unknown6) {
|
||||||
LOG_ERROR(Service_NFC, "Invalid TagState {}", static_cast<int>(nfc->nfc_tag_state.load()));
|
LOG_ERROR(Service_NFC, "Invalid TagState {}", static_cast<int>(nfc->nfc_tag_state));
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC,
|
rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC,
|
||||||
ErrorSummary::InvalidState, ErrorLevel::Status));
|
ErrorSummary::InvalidState, ErrorLevel::Status));
|
||||||
|
@ -163,7 +164,7 @@ void Module::Interface::StopTagScanning(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
if (nfc->nfc_tag_state == TagState::NotInitialized ||
|
if (nfc->nfc_tag_state == TagState::NotInitialized ||
|
||||||
nfc->nfc_tag_state == TagState::NotScanning) {
|
nfc->nfc_tag_state == TagState::NotScanning) {
|
||||||
LOG_ERROR(Service_NFC, "Invalid TagState {}", static_cast<int>(nfc->nfc_tag_state.load()));
|
LOG_ERROR(Service_NFC, "Invalid TagState {}", static_cast<int>(nfc->nfc_tag_state));
|
||||||
rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC,
|
rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC,
|
||||||
ErrorSummary::InvalidState, ErrorLevel::Status));
|
ErrorSummary::InvalidState, ErrorLevel::Status));
|
||||||
return;
|
return;
|
||||||
|
@ -192,13 +193,14 @@ void Module::Interface::ResetTagScanState(Kernel::HLERequestContext& ctx) {
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
if (nfc->nfc_tag_state != TagState::TagDataLoaded && nfc->nfc_tag_state != TagState::Unknown6) {
|
if (nfc->nfc_tag_state != TagState::TagDataLoaded && nfc->nfc_tag_state != TagState::Unknown6) {
|
||||||
LOG_ERROR(Service_NFC, "Invalid TagState {}", static_cast<int>(nfc->nfc_tag_state.load()));
|
LOG_ERROR(Service_NFC, "Invalid TagState {}", static_cast<int>(nfc->nfc_tag_state));
|
||||||
rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC,
|
rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC,
|
||||||
ErrorSummary::InvalidState, ErrorLevel::Status));
|
ErrorSummary::InvalidState, ErrorLevel::Status));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
nfc->nfc_tag_state = TagState::TagInRange;
|
nfc->nfc_tag_state = TagState::TagInRange;
|
||||||
|
nfc->SyncTagState();
|
||||||
|
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
LOG_DEBUG(Service_NFC, "called");
|
LOG_DEBUG(Service_NFC, "called");
|
||||||
|
@ -208,7 +210,7 @@ void Module::Interface::GetTagInRangeEvent(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp(ctx, 0x0B, 0, 0);
|
IPC::RequestParser rp(ctx, 0x0B, 0, 0);
|
||||||
|
|
||||||
if (nfc->nfc_tag_state != TagState::NotScanning) {
|
if (nfc->nfc_tag_state != TagState::NotScanning) {
|
||||||
LOG_ERROR(Service_NFC, "Invalid TagState {}", static_cast<int>(nfc->nfc_tag_state.load()));
|
LOG_ERROR(Service_NFC, "Invalid TagState {}", static_cast<int>(nfc->nfc_tag_state));
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC,
|
rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC,
|
||||||
ErrorSummary::InvalidState, ErrorLevel::Status));
|
ErrorSummary::InvalidState, ErrorLevel::Status));
|
||||||
|
@ -225,7 +227,7 @@ void Module::Interface::GetTagOutOfRangeEvent(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp(ctx, 0x0C, 0, 0);
|
IPC::RequestParser rp(ctx, 0x0C, 0, 0);
|
||||||
|
|
||||||
if (nfc->nfc_tag_state != TagState::NotScanning) {
|
if (nfc->nfc_tag_state != TagState::NotScanning) {
|
||||||
LOG_ERROR(Service_NFC, "Invalid TagState {}", static_cast<int>(nfc->nfc_tag_state.load()));
|
LOG_ERROR(Service_NFC, "Invalid TagState {}", static_cast<int>(nfc->nfc_tag_state));
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC,
|
rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC,
|
||||||
ErrorSummary::InvalidState, ErrorLevel::Status));
|
ErrorSummary::InvalidState, ErrorLevel::Status));
|
||||||
|
@ -243,7 +245,7 @@ void Module::Interface::GetTagState(Kernel::HLERequestContext& ctx) {
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
rb.PushEnum(nfc->nfc_tag_state.load());
|
rb.PushEnum(nfc->nfc_tag_state);
|
||||||
LOG_DEBUG(Service_NFC, "called");
|
LOG_DEBUG(Service_NFC, "called");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -261,7 +263,7 @@ void Module::Interface::Unknown0x1A(Kernel::HLERequestContext& ctx) {
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
if (nfc->nfc_tag_state != TagState::TagInRange) {
|
if (nfc->nfc_tag_state != TagState::TagInRange) {
|
||||||
LOG_ERROR(Service_NFC, "Invalid TagState {}", static_cast<int>(nfc->nfc_tag_state.load()));
|
LOG_ERROR(Service_NFC, "Invalid TagState {}", static_cast<int>(nfc->nfc_tag_state));
|
||||||
rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC,
|
rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC,
|
||||||
ErrorSummary::InvalidState, ErrorLevel::Status));
|
ErrorSummary::InvalidState, ErrorLevel::Status));
|
||||||
return;
|
return;
|
||||||
|
@ -277,7 +279,7 @@ void Module::Interface::GetIdentificationBlock(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp(ctx, 0x1B, 0, 0);
|
IPC::RequestParser rp(ctx, 0x1B, 0, 0);
|
||||||
|
|
||||||
if (nfc->nfc_tag_state != TagState::TagDataLoaded && nfc->nfc_tag_state != TagState::Unknown6) {
|
if (nfc->nfc_tag_state != TagState::TagDataLoaded && nfc->nfc_tag_state != TagState::Unknown6) {
|
||||||
LOG_ERROR(Service_NFC, "Invalid TagState {}", static_cast<int>(nfc->nfc_tag_state.load()));
|
LOG_ERROR(Service_NFC, "Invalid TagState {}", static_cast<int>(nfc->nfc_tag_state));
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC,
|
rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC,
|
||||||
ErrorSummary::InvalidState, ErrorLevel::Status));
|
ErrorSummary::InvalidState, ErrorLevel::Status));
|
||||||
|
@ -304,15 +306,29 @@ std::shared_ptr<Module> Module::Interface::GetModule() const {
|
||||||
void Module::Interface::LoadAmiibo(const AmiiboData& amiibo_data) {
|
void Module::Interface::LoadAmiibo(const AmiiboData& amiibo_data) {
|
||||||
std::lock_guard lock(HLE::g_hle_lock);
|
std::lock_guard lock(HLE::g_hle_lock);
|
||||||
nfc->amiibo_data = amiibo_data;
|
nfc->amiibo_data = amiibo_data;
|
||||||
nfc->nfc_tag_state = Service::NFC::TagState::TagInRange;
|
nfc->amiibo_in_range = true;
|
||||||
nfc->tag_in_range_event->Signal();
|
nfc->SyncTagState();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Module::Interface::RemoveAmiibo() {
|
void Module::Interface::RemoveAmiibo() {
|
||||||
std::lock_guard lock(HLE::g_hle_lock);
|
std::lock_guard lock(HLE::g_hle_lock);
|
||||||
nfc->nfc_tag_state = Service::NFC::TagState::TagOutOfRange;
|
nfc->amiibo_in_range = false;
|
||||||
nfc->tag_out_of_range_event->Signal();
|
nfc->SyncTagState();
|
||||||
nfc->amiibo_data = {};
|
}
|
||||||
|
|
||||||
|
void Module::SyncTagState() {
|
||||||
|
if (amiibo_in_range &&
|
||||||
|
(nfc_tag_state == TagState::TagOutOfRange || nfc_tag_state == TagState::Scanning)) {
|
||||||
|
// TODO (wwylele): Should TagOutOfRange->TagInRange transition only happen on the same tag
|
||||||
|
// detected on Scanning->TagInRange?
|
||||||
|
nfc_tag_state = TagState::TagInRange;
|
||||||
|
tag_in_range_event->Signal();
|
||||||
|
} else if (!amiibo_in_range && nfc_tag_state == TagState::TagInRange) {
|
||||||
|
nfc_tag_state = TagState::TagOutOfRange;
|
||||||
|
// TODO (wwylele): If a tag is removed during TagDataLoaded/Unknown6, should this event
|
||||||
|
// signals early?
|
||||||
|
tag_out_of_range_event->Signal();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Module::Interface::Interface(std::shared_ptr<Module> nfc, const char* name, u32 max_session)
|
Module::Interface::Interface(std::shared_ptr<Module> nfc, const char* name, u32 max_session)
|
||||||
|
|
|
@ -231,12 +231,16 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// Sync nfc_tag_state with amiibo_in_range and signal events on state change.
|
||||||
|
void SyncTagState();
|
||||||
|
|
||||||
std::shared_ptr<Kernel::Event> tag_in_range_event;
|
std::shared_ptr<Kernel::Event> tag_in_range_event;
|
||||||
std::shared_ptr<Kernel::Event> tag_out_of_range_event;
|
std::shared_ptr<Kernel::Event> tag_out_of_range_event;
|
||||||
std::atomic<TagState> nfc_tag_state = TagState::NotInitialized;
|
TagState nfc_tag_state = TagState::NotInitialized;
|
||||||
CommunicationStatus nfc_status = CommunicationStatus::NfcInitialized;
|
CommunicationStatus nfc_status = CommunicationStatus::NfcInitialized;
|
||||||
|
|
||||||
AmiiboData amiibo_data{};
|
AmiiboData amiibo_data{};
|
||||||
|
bool amiibo_in_range = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
void InstallInterfaces(Core::System& system);
|
void InstallInterfaces(Core::System& system);
|
||||||
|
|
Loading…
Reference in New Issue