diff --git a/libraries/libmesosphere/source/board/nintendo/nx/kern_atomics_registers.hpp b/libraries/libmesosphere/source/board/nintendo/nx/kern_atomics_registers.hpp
new file mode 100644
index 000000000..ee6564f59
--- /dev/null
+++ b/libraries/libmesosphere/source/board/nintendo/nx/kern_atomics_registers.hpp
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+
+#define ATOMICS_AP0_TRIGGER 0x000
+#define ATOMICS_AP0_RESULT(id) (0xc00 + id * 4)
+
+#define TRIGGER_CMD_GET 4
diff --git a/libraries/libmesosphere/source/board/nintendo/nx/kern_bpmp_api.hpp b/libraries/libmesosphere/source/board/nintendo/nx/kern_bpmp_api.hpp
new file mode 100644
index 000000000..38c5b0796
--- /dev/null
+++ b/libraries/libmesosphere/source/board/nintendo/nx/kern_bpmp_api.hpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+
+/* Message Flags */
+#define BPMP_MSG_DO_ACK (1 << 0)
+#define BPMP_MSG_RING_DOORBELL (1 << 1)
+
+/* Messages */
+#define MRQ_PING 0
+#define MRQ_ENABLE_SUSPEND 17
+#define MRQ_CPU_PMIC_SELECT 28
+
+/* BPMP Power states. */
+#define TEGRA_BPMP_PM_CC1 9
+#define TEGRA_BPMP_PM_CC4 12
+#define TEGRA_BPMP_PM_CC6 14
+#define TEGRA_BPMP_PM_CC7 15
+#define TEGRA_BPMP_PM_SC1 17
+#define TEGRA_BPMP_PM_SC2 18
+#define TEGRA_BPMP_PM_SC3 19
+#define TEGRA_BPMP_PM_SC4 20
+#define TEGRA_BPMP_PM_SC7 23
+
+/* Channel states. */
+#define CH_MASK(ch) (0x3u << ((ch) * 2))
+#define SL_SIGL(ch) (0x0u << ((ch) * 2))
+#define SL_QUED(ch) (0x1u << ((ch) * 2))
+#define MA_FREE(ch) (0x2u << ((ch) * 2))
+#define MA_ACKD(ch) (0x3u << ((ch) * 2))
+
+constexpr inline int MessageSize = 0x80;
+constexpr inline int MessageDataSizeMax = 0x78;
+
+struct MailboxData {
+ s32 code;
+ s32 flags;
+ u8 data[MessageDataSizeMax];
+};
+
+static_assert(ams::util::is_pod::value);
+static_assert(sizeof(MailboxData) == MessageSize);
+
+struct ChannelData {
+ MailboxData *ib;
+ MailboxData *ob;
+};
+
+
diff --git a/libraries/libmesosphere/source/board/nintendo/nx/kern_flow_registers.hpp b/libraries/libmesosphere/source/board/nintendo/nx/kern_flow_registers.hpp
new file mode 100644
index 000000000..373454499
--- /dev/null
+++ b/libraries/libmesosphere/source/board/nintendo/nx/kern_flow_registers.hpp
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+
+#define FLOW_CTLR_CC4_HVC_CONTROL 0x060
+#define FLOW_CTLR_CC4_RETENTION_CONTROL 0x064
+#define FLOW_CTLR_CC4_HVC_RETRY 0x08C
diff --git a/libraries/libmesosphere/source/board/nintendo/nx/kern_ictlr_registers.hpp b/libraries/libmesosphere/source/board/nintendo/nx/kern_ictlr_registers.hpp
new file mode 100644
index 000000000..35623ea45
--- /dev/null
+++ b/libraries/libmesosphere/source/board/nintendo/nx/kern_ictlr_registers.hpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+
+#define ICTLR_REG_BASE(irq) ((((irq) - 32) >> 5) * 0x100)
+#define ICTLR_FIR_SET(irq) (ICTLR_REG_BASE(irq) + 0x18)
+#define ICTLR_FIR_CLR(irq) (ICTLR_REG_BASE(irq) + 0x1c)
+#define FIR_BIT(irq) (1 << ((irq) & 0x1f))
+
+#define INT_GIC_BASE (0)
+#define INT_PRI_BASE (INT_GIC_BASE + 32)
+#define INT_SHR_SEM_OUTBOX_IBF (INT_PRI_BASE + 6)
diff --git a/libraries/libmesosphere/source/board/nintendo/nx/kern_lps_driver.cpp b/libraries/libmesosphere/source/board/nintendo/nx/kern_lps_driver.cpp
index 74c3b8ab7..9981771ae 100644
--- a/libraries/libmesosphere/source/board/nintendo/nx/kern_lps_driver.cpp
+++ b/libraries/libmesosphere/source/board/nintendo/nx/kern_lps_driver.cpp
@@ -17,24 +17,379 @@
#include "kern_lps_driver.hpp"
#include "kern_k_sleep_manager.hpp"
+#include "kern_bpmp_api.hpp"
+#include "kern_atomics_registers.hpp"
+#include "kern_ictlr_registers.hpp"
+#include "kern_flow_registers.hpp"
+#include "kern_sema_registers.hpp"
+
namespace ams::kern::board::nintendo::nx::lps {
namespace {
- constinit bool g_lps_init_done = false;
+ constexpr inline int ChannelCount = 12;
+
+ constexpr inline TimeSpan ChannelTimeout = TimeSpan::FromMicroSeconds(1);
+
+ constinit bool g_lps_init_done = false;
+ constinit bool g_bpmp_connected = false;
+ constinit bool g_bpmp_mail_initialized = false;
+
+ constinit KSpinLock g_bpmp_mrq_lock;
+
+ constinit KVirtualAddress g_evp_address = Null;
+ constinit KVirtualAddress g_flow_address = Null;
+ constinit KVirtualAddress g_prictlr_address = Null;
+ constinit KVirtualAddress g_sema_address = Null;
+ constinit KVirtualAddress g_atomics_address = Null;
+ constinit KVirtualAddress g_clkrst_address = Null;
+
+ constinit ChannelData g_channel_area[ChannelCount] = {};
+
+ ALWAYS_INLINE u32 Read(KVirtualAddress address) {
+ return *GetPointer(address);
+ }
+
+ ALWAYS_INLINE void Write(KVirtualAddress address, u32 value) {
+ *GetPointer(address) = value;
+ }
+
+ void InitializeDeviceVirtualAddresses() {
+ /* Retrieve randomized mappings. */
+ g_evp_address = KMemoryLayout::GetDeviceVirtualAddress(KMemoryRegionType_LegacyLpsExceptionVectors);
+ g_flow_address = KMemoryLayout::GetDeviceVirtualAddress(KMemoryRegionType_LegacyLpsFlowController);
+ g_prictlr_address = KMemoryLayout::GetDeviceVirtualAddress(KMemoryRegionType_LegacyLpsPrimaryICtlr);
+ g_sema_address = KMemoryLayout::GetDeviceVirtualAddress(KMemoryRegionType_LegacyLpsSemaphore);
+ g_atomics_address = KMemoryLayout::GetDeviceVirtualAddress(KMemoryRegionType_LegacyLpsAtomics);
+ g_clkrst_address = KMemoryLayout::GetDeviceVirtualAddress(KMemoryRegionType_LegacyLpsClkRst);
+ }
+
+ /* NOTE: linux "do_cc4_init" */
+ void ConfigureCc3AndCc4() {
+ /* Configure CC4/CC3 as enabled with time threshold as 2 microseconds. */
+ Write(g_flow_address + FLOW_CTLR_CC4_HVC_CONTROL, (0x2 << 3) | 0x1);
+
+ /* Configure Retention with threshold 2 microseconds. */
+ Write(g_flow_address + FLOW_CTLR_CC4_RETENTION_CONTROL, (0x2 << 3));
+
+ /* Configure CC3/CC3 retry threshold as 2 microseconds. */
+ Write(g_flow_address + FLOW_CTLR_CC4_HVC_RETRY, (0x2 << 3));
+
+ /* Read the retry register to ensure writes take. */
+ Read(g_flow_address + FLOW_CTLR_CC4_HVC_RETRY);
+ }
+
+ constexpr bool IsValidMessageDataSize(int size) {
+ return 0 <= size && size < MessageDataSizeMax;
+ }
+
+ /* NOTE: linux "bpmp_valid_txfer" */
+ constexpr bool IsTransferValid(const void *ob, int ob_size, void *ib, int ib_size) {
+ return IsValidMessageDataSize(ob_size) && IsValidMessageDataSize(ib_size) && (ob_size == 0 || ob != nullptr) && (ib_size == 0 || ib != nullptr);
+ }
+
+ /* NOTE: linux "bpmp_ob_channel" */
+ int BpmpGetOutboundChannel() {
+ return GetCurrentCoreId();
+ }
+
+ /* NOTE: linux "bpmp_ch_sta" */
+ u32 BpmpGetChannelState(int channel) {
+ cpu::DataSynchronizationBarrier();
+ return Read(g_sema_address + RES_SEMA_SHRD_SMP_STA) & CH_MASK(channel);
+ }
+
+ /* NOTE: linux "bpmp_master_free" */
+ bool BpmpIsMasterFree(int channel) {
+ return BpmpGetChannelState(channel) == MA_FREE(channel);
+ }
+
+ /* NOTE: linux "bpmp_master_acked" */
+ bool BpmpIsMasterAcked(int channel) {
+ return BpmpGetChannelState(channel) == MA_ACKD(channel);
+ }
+
+ /* NOTE: linux "bpmp_signal_slave" */
+ void BpmpSignalSlave(int channel) {
+ Write(g_sema_address + RES_SEMA_SHRD_SMP_CLR, CH_MASK(channel));
+ cpu::DataSynchronizationBarrier();
+ }
+
+ /* NOTE: linux "bpmp_free_master" */
+ void BpmpFreeMaster(int channel) {
+ /* Transition state from ack'd to free. */
+ Write(g_sema_address + RES_SEMA_SHRD_SMP_CLR, ((MA_ACKD(channel)) ^ (MA_FREE(channel))));
+ cpu::DataSynchronizationBarrier();
+ }
+
+ /* NOTE: linux "bpmp_ring_doorbell" */
+ void BpmpRingDoorbell() {
+ Write(g_prictlr_address + ICTLR_FIR_SET(INT_SHR_SEM_OUTBOX_IBF), FIR_BIT(INT_SHR_SEM_OUTBOX_IBF));
+ cpu::DataSynchronizationBarrier();
+ }
+
+ /* NOTE: linux "bpmp_wait_master_free" */
+ int BpmpWaitMasterFree(int channel) {
+ /* Check if the master is already freed. */
+ if (BpmpIsMasterFree(channel)) {
+ return 0;
+ }
+
+ /* Spin-poll for the master to be freed until timeout occurs. */
+ const auto start_tick = KHardwareTimer::GetTick();
+ const auto timeout = ams::svc::Tick(ChannelTimeout);
+ do {
+ if (BpmpIsMasterFree(channel)) {
+ return 0;
+ }
+ } while ((KHardwareTimer::GetTick() - start_tick) < timeout);
+
+ /* The master didn't become free. */
+ return -1;
+ }
+
+ /* NOTE: linux "bpmp_wait_ack" */
+ int BpmpWaitAck(int channel) {
+ /* Check if the master is already ACK'd. */
+ if (BpmpIsMasterAcked(channel)) {
+ return 0;
+ }
+
+ /* Spin-poll for the master to be ACK'd until timeout occurs. */
+ const auto start_tick = KHardwareTimer::GetTick();
+ const auto timeout = ams::svc::Tick(ChannelTimeout);
+ do {
+ if (BpmpIsMasterAcked(channel)) {
+ return 0;
+ }
+ } while ((KHardwareTimer::GetTick() - start_tick) < timeout);
+
+ /* The master didn't get ACK'd. */
+ return -1;
+ }
+
+ /* NOTE: linux "bpmp_write_ch" */
+ int BpmpWriteChannel(int channel, int mrq, int flags, const void *data, size_t data_size) {
+ /* Wait to be able to master the mailbox. */
+ if (int res = BpmpWaitMasterFree(channel); res != 0) {
+ return res;
+ }
+
+ /* Prepare the message. */
+ MailboxData *mb = g_channel_area[channel].ob;
+ mb->code = mrq;
+ mb->flags = flags;
+ if (data != nullptr) {
+ std::memcpy(mb->data, data, data_size);
+ }
+
+ /* Signal to slave that message is available. */
+ BpmpSignalSlave(channel);
+
+ return 0;
+ }
+
+ /* NOTE: linux "__bpmp_read_ch" */
+ int BpmpReadChannel(int channel, void *data, size_t data_size) {
+ /* Get the message. */
+ MailboxData *mb = g_channel_area[channel].ib;
+
+ /* Copy any return data. */
+ if (data != nullptr) {
+ std::memcpy(data, mb->data, data_size);
+ }
+
+ /* Free the channel. */
+ BpmpFreeMaster(channel);
+
+ /* Return result. */
+ return mb->code;
+ }
+
+ /* NOTE: linux "tegra_bpmp_send_receive_atomic" or "tegra_bpmp_send_receive". */
+ int BpmpSendAndReceive(int mrq, const void *ob, int ob_size, void *ib, int ib_size) {
+ /* Validate that the data transfer is valid. */
+ if (!IsTransferValid(ob, ob_size, ib, ib_size)) {
+ return -1;
+ }
+
+ /* Validate that the bpmp is connected. */
+ if (!g_bpmp_connected) {
+ return -1;
+ }
+
+ /* Disable interrupts. */
+ KScopedInterruptDisable di;
+
+ /* Acquire exclusive access to send mrqs. */
+ KScopedSpinLock lk(g_bpmp_mrq_lock);
+
+ /* Send the message. */
+ int channel = BpmpGetOutboundChannel();
+ if (int res = BpmpWriteChannel(channel, mrq, BPMP_MSG_DO_ACK, ob, ob_size); res != 0) {
+ return res;
+ }
+
+ /* Send "doorbell" irq to the bpmp firmware. */
+ BpmpRingDoorbell();
+
+ /* Wait for the bpmp firmware to acknowledge our request. */
+ if (int res = BpmpWaitAck(channel); res != 0) {
+ return res;
+ }
+
+ /* Read the data the bpmp sent back. */
+ return BpmpReadChannel(channel, ib, ib_size);
+ }
+
+ /* NOTE: linux "tegra_bpmp_send" */
+ int BpmpSend(int mrq, const void *ob, int ob_size) {
+ /* Validate that the data transfer is valid. */
+ if (!IsTransferValid(ob, ob_size, nullptr, 0)) {
+ return -1;
+ }
+
+ /* Validate that the bpmp is connected. */
+ if (!g_bpmp_connected) {
+ return -1;
+ }
+
+ /* Disable interrupts. */
+ KScopedInterruptDisable di;
+
+ /* Acquire exclusive access to send mrqs. */
+ KScopedSpinLock lk(g_bpmp_mrq_lock);
+
+ /* Send the message. */
+ int channel = BpmpGetOutboundChannel();
+ if (int res = BpmpWriteChannel(channel, mrq, 0, ob, ob_size); res != 0) {
+ return res;
+ }
+
+ /* Send "doorbell" irq to the bpmp firmware. */
+ BpmpRingDoorbell();
+
+ return 0;
+ }
+
+ /* NOTE: modified linux "tegra_bpmp_enable_suspend" */
+ int BpmpEnableSuspend(int mode, int flags) {
+ /* Prepare data for bpmp. */
+ const s32 data[] = { mode, flags };
+
+ /* Send the data. */
+ return BpmpSend(MRQ_ENABLE_SUSPEND, data, sizeof(data));
+ }
+
+ /* NOTE: linux "__bpmp_connect" */
+ int ConnectToBpmp() {
+ /* Check if we've already connected. */
+ if (g_bpmp_connected) {
+ return 0;
+ }
+
+ /* Verify that the resource semaphore state is set. */
+ if (Read(g_sema_address + RES_SEMA_SHRD_SMP_STA) == 0) {
+ return -1;
+ }
+
+ /* Get the channels, which the bpmp firmware has configured in advance. */
+ {
+ const KVirtualAddress iram_virt_addr = KMemoryLayout::GetDeviceVirtualAddress (KMemoryRegionType_LegacyLpsIram);
+ const KPhysicalAddress iram_phys_addr = KMemoryLayout::GetDevicePhysicalAddress(KMemoryRegionType_LegacyLpsIram);
+
+ for (auto i = 0; i < ChannelCount; ++i) {
+ /* Trigger a get command for the desired channel. */
+ Write(g_atomics_address + ATOMICS_AP0_TRIGGER, TRIGGER_CMD_GET | (i << 16));
+
+ /* Retrieve the channel phys-addr-in-iram, and convert it to a kernel address. */
+ auto *ch = GetPointer(iram_virt_addr + (Read(g_atomics_address + ATOMICS_AP0_RESULT(i)) - GetInteger(iram_phys_addr)));
+
+ /* Verify the channel isn't null. */
+ /* NOTE: This is an utterly nonsense check, as this would require the bpmp firmware to specify */
+ /* a phys-to-virt diff as an address. On 1.0.0, which had no ASLR, this was 0x8028C000. */
+ /* However, Nintendo has the check, and we'll preserve it to be faithful. */
+ if (ch == nullptr) {
+ return -1;
+ }
+
+ /* Set the channel in the channel area. */
+ g_channel_area[i].ib = ch;
+ g_channel_area[i].ob = ch;
+ }
+ }
+
+ /* Mark driver as connected to bpmp. */
+ g_bpmp_connected = true;
+
+ return 0;
+ }
+
+ /* NOTE: Modified linux "bpmp_mail_init" */
+ int InitializeBpmpMail() {
+ /* Check if we've already initialized. */
+ if (g_bpmp_mail_initialized) {
+ return 0;
+ }
+
+ /* Mark function as having been called. */
+ g_bpmp_mail_initialized = true;
+
+ /* Forward declare result/reply variables. */
+ int res, request = 0, reply = 0;
+
+ /* Try to connect to the bpmp. */
+ if (res = ConnectToBpmp(); res != 0) {
+ MESOSPHERE_LOG("bpmp: connect error returns %d\n", res);
+ return res;
+ }
+
+ /* Ensure that we can successfully ping the bpmp. */
+ request = 1;
+ if (res = BpmpSendAndReceive(MRQ_PING, std::addressof(request), sizeof(request), std::addressof(reply), sizeof(reply)); res != 0) {
+ MESOSPHERE_LOG("bpmp: MRQ_PING error returns %d with reply %d\n", res, reply);
+ return res;
+ }
+
+ /* Configure the PMIC. */
+ request = 1;
+ if (res = BpmpSendAndReceive(MRQ_CPU_PMIC_SELECT, std::addressof(request), sizeof(request), std::addressof(reply), sizeof(reply)); res != 0) {
+ MESOSPHERE_LOG("bpmp: MRQ_CPU_PMIC_SELECT for MAX77621 error returns %d with reply %d\n", res, reply);
+ return res;
+ }
+
+ return 0;
+ }
}
void Initialize() {
if (!g_lps_init_done) {
- MESOSPHERE_UNIMPLEMENTED();
+ /* Get the addresses of the devices the driver needs. */
+ InitializeDeviceVirtualAddresses();
+
+ /* Configure CC3/CC4. */
+ ConfigureCc3AndCc4();
+
+ /* Initialize ccplex <-> bpmp mail. */
+ /* NOTE: Nintendo does not check that this call succeeds. */
+ InitializeBpmpMail();
g_lps_init_done = true;
}
}
- Result EnableSuspend() {
- MESOSPHERE_UNIMPLEMENTED();
+ Result EnableSuspend(bool enable) {
+ /* If we're not on core 0, there's nothing to do. */
+ R_SUCCEED_IF(GetCurrentCoreId() != 0);
+
+ /* If we're not enabling suspend, there's nothing to do. */
+ R_SUCCEED_IF(!enable);
+
+ /* Instruct BPMP to enable suspend-to-sc7. */
+ R_UNLESS(BpmpEnableSuspend(TEGRA_BPMP_PM_SC7, 0) == 0, svc::ResultInvalidState());
+
+ return ResultSuccess();
}
void InvokeCpuSleepHandler(uintptr_t arg, uintptr_t entry) {
@@ -45,6 +400,11 @@ namespace ams::kern::board::nintendo::nx::lps {
/* Invoke the sleep hander. */
KSleepManager::CpuSleepHandler(arg, entry);
+
+ /* TODO: restore saved clkrst reg */
+
+ /* Configure CC3/CC4. */
+ ConfigureCc3AndCc4();
}
}
\ No newline at end of file
diff --git a/libraries/libmesosphere/source/board/nintendo/nx/kern_lps_driver.hpp b/libraries/libmesosphere/source/board/nintendo/nx/kern_lps_driver.hpp
index e63e409b7..49ff4b3d3 100644
--- a/libraries/libmesosphere/source/board/nintendo/nx/kern_lps_driver.hpp
+++ b/libraries/libmesosphere/source/board/nintendo/nx/kern_lps_driver.hpp
@@ -21,7 +21,7 @@ namespace ams::kern::board::nintendo::nx {
namespace lps {
void Initialize();
- Result EnableSuspend();
+ Result EnableSuspend(bool enable);
void InvokeCpuSleepHandler(uintptr_t arg, uintptr_t entry);
}
diff --git a/libraries/libmesosphere/source/board/nintendo/nx/kern_sema_registers.hpp b/libraries/libmesosphere/source/board/nintendo/nx/kern_sema_registers.hpp
new file mode 100644
index 000000000..b623df176
--- /dev/null
+++ b/libraries/libmesosphere/source/board/nintendo/nx/kern_sema_registers.hpp
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+
+#define RES_SEMA_SHRD_SMP_STA 0x000
+#define RES_SEMA_SHRD_SMP_SET 0x004
+#define RES_SEMA_SHRD_SMP_CLR 0x008