saltynx integration
This commit is contained in:
231
Source/sys-clk/common/include/SaltyNX.h
Normal file
231
Source/sys-clk/common/include/SaltyNX.h
Normal file
@@ -0,0 +1,231 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "ipc.h"
|
||||||
|
|
||||||
|
Handle saltysd_orig;
|
||||||
|
|
||||||
|
Result SaltySD_Connect() {
|
||||||
|
for (int i = 0; i < 200; i++) {
|
||||||
|
if (!svcConnectToNamedPort(&saltysd_orig, "SaltySD"))
|
||||||
|
return 0;
|
||||||
|
svcSleepThread(1000*1000);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result SaltySD_Term()
|
||||||
|
{
|
||||||
|
Result ret;
|
||||||
|
IpcCommand c;
|
||||||
|
|
||||||
|
ipcInitialize(&c);
|
||||||
|
ipcSendPid(&c);
|
||||||
|
|
||||||
|
struct input
|
||||||
|
{
|
||||||
|
u64 magic;
|
||||||
|
u64 cmd_id;
|
||||||
|
u64 zero;
|
||||||
|
u64 reserved[2];
|
||||||
|
} *raw;
|
||||||
|
|
||||||
|
raw = (input*)ipcPrepareHeader(&c, sizeof(*raw));
|
||||||
|
|
||||||
|
raw->magic = SFCI_MAGIC;
|
||||||
|
raw->cmd_id = 0;
|
||||||
|
raw->zero = 0;
|
||||||
|
|
||||||
|
ret = ipcDispatch(saltysd_orig);
|
||||||
|
|
||||||
|
if (R_SUCCEEDED(ret))
|
||||||
|
{
|
||||||
|
IpcParsedCommand r;
|
||||||
|
ipcParse(&r);
|
||||||
|
|
||||||
|
struct output {
|
||||||
|
u64 magic;
|
||||||
|
u64 result;
|
||||||
|
} *resp = (output*)r.Raw;
|
||||||
|
|
||||||
|
ret = resp->result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Session terminated works too.
|
||||||
|
svcCloseHandle(saltysd_orig);
|
||||||
|
if (ret == 0xf601) return 0;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result SaltySD_CheckIfSharedMemoryAvailable(ptrdiff_t *offset, u64 size)
|
||||||
|
{
|
||||||
|
Result ret = 0;
|
||||||
|
|
||||||
|
// Send a command
|
||||||
|
IpcCommand c;
|
||||||
|
ipcInitialize(&c);
|
||||||
|
ipcSendPid(&c);
|
||||||
|
|
||||||
|
struct input {
|
||||||
|
u64 magic;
|
||||||
|
u64 cmd_id;
|
||||||
|
u64 size;
|
||||||
|
u32 reserved[2];
|
||||||
|
} *raw;
|
||||||
|
|
||||||
|
raw = (input*)ipcPrepareHeader(&c, sizeof(*raw));
|
||||||
|
|
||||||
|
raw->magic = SFCI_MAGIC;
|
||||||
|
raw->cmd_id = 6;
|
||||||
|
raw->size = size;
|
||||||
|
|
||||||
|
ret = ipcDispatch(saltysd_orig);
|
||||||
|
|
||||||
|
if (R_SUCCEEDED(ret)) {
|
||||||
|
IpcParsedCommand r;
|
||||||
|
ipcParse(&r);
|
||||||
|
|
||||||
|
struct output {
|
||||||
|
u64 magic;
|
||||||
|
u64 result;
|
||||||
|
u64 offset;
|
||||||
|
} *resp = (output*)r.Raw;
|
||||||
|
|
||||||
|
ret = resp->result;
|
||||||
|
|
||||||
|
if (!ret)
|
||||||
|
{
|
||||||
|
*offset = resp->offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result SaltySD_GetSharedMemoryHandle(Handle *retrieve)
|
||||||
|
{
|
||||||
|
Result ret = 0;
|
||||||
|
|
||||||
|
// Send a command
|
||||||
|
IpcCommand c;
|
||||||
|
ipcInitialize(&c);
|
||||||
|
ipcSendPid(&c);
|
||||||
|
|
||||||
|
struct input {
|
||||||
|
u64 magic;
|
||||||
|
u64 cmd_id;
|
||||||
|
u32 reserved[4];
|
||||||
|
} *raw;
|
||||||
|
|
||||||
|
raw = (input*)ipcPrepareHeader(&c, sizeof(*raw));
|
||||||
|
|
||||||
|
raw->magic = SFCI_MAGIC;
|
||||||
|
raw->cmd_id = 7;
|
||||||
|
|
||||||
|
ret = ipcDispatch(saltysd_orig);
|
||||||
|
|
||||||
|
if (R_SUCCEEDED(ret)) {
|
||||||
|
IpcParsedCommand r;
|
||||||
|
ipcParse(&r);
|
||||||
|
|
||||||
|
struct output {
|
||||||
|
u64 magic;
|
||||||
|
u64 result;
|
||||||
|
u64 reserved[2];
|
||||||
|
} *resp = (output*)r.Raw;
|
||||||
|
|
||||||
|
ret = resp->result;
|
||||||
|
|
||||||
|
if (!ret)
|
||||||
|
{
|
||||||
|
*retrieve = r.Handles[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result SaltySD_GetDisplayRefreshRate(uint8_t* refreshRate)
|
||||||
|
{
|
||||||
|
Result ret = 0;
|
||||||
|
|
||||||
|
// Send a command
|
||||||
|
IpcCommand c;
|
||||||
|
ipcInitialize(&c);
|
||||||
|
ipcSendPid(&c);
|
||||||
|
|
||||||
|
struct input {
|
||||||
|
u64 magic;
|
||||||
|
u64 cmd_id;
|
||||||
|
u64 zero;
|
||||||
|
u64 reserved;
|
||||||
|
} *raw;
|
||||||
|
|
||||||
|
raw = (input*)ipcPrepareHeader(&c, sizeof(*raw));
|
||||||
|
|
||||||
|
raw->magic = SFCI_MAGIC;
|
||||||
|
raw->cmd_id = 10;
|
||||||
|
raw->zero = 0;
|
||||||
|
|
||||||
|
ret = ipcDispatch(saltysd_orig);
|
||||||
|
|
||||||
|
if (R_SUCCEEDED(ret)) {
|
||||||
|
IpcParsedCommand r;
|
||||||
|
ipcParse(&r);
|
||||||
|
|
||||||
|
struct output {
|
||||||
|
u64 magic;
|
||||||
|
u64 result;
|
||||||
|
u64 refreshRate;
|
||||||
|
u64 reserved;
|
||||||
|
} *resp = (output*)r.Raw;
|
||||||
|
|
||||||
|
ret = resp->result;
|
||||||
|
|
||||||
|
if (!ret)
|
||||||
|
{
|
||||||
|
*refreshRate = (uint8_t)(resp->refreshRate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result SaltySD_SetDisplayRefreshRate(uint8_t refreshRate)
|
||||||
|
{
|
||||||
|
Result ret = 0;
|
||||||
|
|
||||||
|
// Send a command
|
||||||
|
IpcCommand c;
|
||||||
|
ipcInitialize(&c);
|
||||||
|
ipcSendPid(&c);
|
||||||
|
|
||||||
|
struct input {
|
||||||
|
u64 magic;
|
||||||
|
u64 cmd_id;
|
||||||
|
u64 refreshRate;
|
||||||
|
u64 reserved;
|
||||||
|
} *raw;
|
||||||
|
|
||||||
|
raw = (input*)ipcPrepareHeader(&c, sizeof(*raw));
|
||||||
|
|
||||||
|
raw->magic = SFCI_MAGIC;
|
||||||
|
raw->cmd_id = 11;
|
||||||
|
raw->refreshRate = refreshRate;
|
||||||
|
|
||||||
|
ret = ipcDispatch(saltysd_orig);
|
||||||
|
|
||||||
|
if (R_SUCCEEDED(ret)) {
|
||||||
|
IpcParsedCommand r;
|
||||||
|
ipcParse(&r);
|
||||||
|
|
||||||
|
struct output {
|
||||||
|
u64 magic;
|
||||||
|
u64 result;
|
||||||
|
u64 reserved[2];
|
||||||
|
} *resp = (output*)r.Raw;
|
||||||
|
|
||||||
|
ret = resp->result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
@@ -19,7 +19,6 @@
|
|||||||
#include <switch.h>
|
#include <switch.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
// Battery charging flags
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
BatteryFlag_NoHub = BIT(0), // Hub is disconnected
|
BatteryFlag_NoHub = BIT(0), // Hub is disconnected
|
||||||
BatteryFlag_Rail = BIT(8), // At least one Joy-con is charging from rail
|
BatteryFlag_Rail = BIT(8), // At least one Joy-con is charging from rail
|
||||||
@@ -27,7 +26,6 @@ typedef enum {
|
|||||||
BatteryFlag_ACC = BIT(16) // Accessory
|
BatteryFlag_ACC = BIT(16) // Accessory
|
||||||
} BatteryChargeFlags;
|
} BatteryChargeFlags;
|
||||||
|
|
||||||
// Power Delivery Controller State (BM92T series)
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
PDState_NewPDO = 1, // Received new Power Data Object
|
PDState_NewPDO = 1, // Received new Power Data Object
|
||||||
PDState_NoPD = 2, // No Power Delivery source is detected
|
PDState_NoPD = 2, // No Power Delivery source is detected
|
||||||
@@ -47,14 +45,11 @@ typedef enum {
|
|||||||
ChargerType_Apple_1000mA = 8,
|
ChargerType_Apple_1000mA = 8,
|
||||||
ChargerType_Apple_2000mA = 9
|
ChargerType_Apple_2000mA = 9
|
||||||
} BatteryChargerType;
|
} BatteryChargerType;
|
||||||
|
|
||||||
// Power role (USB Power Delivery)
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
PowerRole_Sink = 1, // Device is receiving power
|
PowerRole_Sink = 1, // Device is receiving power
|
||||||
PowerRole_Source = 2 // Device is providing power
|
PowerRole_Source = 2 // Device is providing power
|
||||||
} BatteryPowerRole;
|
} BatteryPowerRole;
|
||||||
|
|
||||||
// Complete battery charge information structure
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int32_t InputCurrentLimit; // Input (Sink) current limit in mA
|
int32_t InputCurrentLimit; // Input (Sink) current limit in mA
|
||||||
int32_t VBUSCurrentLimit; // Output (Source/VBUS/OTG) current limit in mA
|
int32_t VBUSCurrentLimit; // Output (Source/VBUS/OTG) current limit in mA
|
||||||
@@ -64,7 +59,7 @@ typedef struct {
|
|||||||
int32_t unk_x14; // Unknown field (possibly flags)
|
int32_t unk_x14; // Unknown field (possibly flags)
|
||||||
BatteryPDControllerState PDControllerState; // PD Controller State
|
BatteryPDControllerState PDControllerState; // PD Controller State
|
||||||
int32_t BatteryTemperature; // Battery temperature in milli-Celsius
|
int32_t BatteryTemperature; // Battery temperature in milli-Celsius
|
||||||
int32_t RawBatteryCharge; // Battery charge in per cent-mille (100% = 100000)
|
int32_t RawBatteryCharge; // Battery charge in percentmille
|
||||||
int32_t VoltageAvg; // Average voltage in mV
|
int32_t VoltageAvg; // Average voltage in mV
|
||||||
int32_t BatteryAge; // Battery health (capacity full/design) in pcm
|
int32_t BatteryAge; // Battery health (capacity full/design) in pcm
|
||||||
BatteryPowerRole PowerRole; // Current power role
|
BatteryPowerRole PowerRole; // Current power role
|
||||||
@@ -74,36 +69,27 @@ typedef struct {
|
|||||||
BatteryChargeFlags Flags; // Various status flags
|
BatteryChargeFlags Flags; // Various status flags
|
||||||
} BatteryChargeInfo;
|
} BatteryChargeInfo;
|
||||||
|
|
||||||
// Helper macro to check if battery charging is enabled
|
|
||||||
#define IS_BATTERY_CHARGING_ENABLED(info) (((info)->unk_x14 >> 8) & 1)
|
#define IS_BATTERY_CHARGING_ENABLED(info) (((info)->unk_x14 >> 8) & 1)
|
||||||
|
|
||||||
// Initialize the battery info driver
|
|
||||||
Result batteryInfoInitialize(void);
|
Result batteryInfoInitialize(void);
|
||||||
|
|
||||||
// Cleanup the battery info driver
|
|
||||||
void batteryInfoExit(void);
|
void batteryInfoExit(void);
|
||||||
|
|
||||||
// Get complete battery charge information
|
|
||||||
Result batteryInfoGetChargeInfo(BatteryChargeInfo *out);
|
Result batteryInfoGetChargeInfo(BatteryChargeInfo *out);
|
||||||
|
|
||||||
// Get battery charge percentage (0-100)
|
|
||||||
Result batteryInfoGetChargePercentage(u32 *out);
|
Result batteryInfoGetChargePercentage(u32 *out);
|
||||||
|
|
||||||
// Check if enough power is being supplied
|
|
||||||
Result batteryInfoIsEnoughPowerSupplied(bool *out);
|
Result batteryInfoIsEnoughPowerSupplied(bool *out);
|
||||||
|
|
||||||
// Battery charge control functions
|
|
||||||
Result batteryInfoEnableCharging(void);
|
Result batteryInfoEnableCharging(void);
|
||||||
Result batteryInfoDisableCharging(void);
|
Result batteryInfoDisableCharging(void);
|
||||||
Result batteryInfoEnableFastCharging(void);
|
Result batteryInfoEnableFastCharging(void);
|
||||||
Result batteryInfoDisableFastCharging(void);
|
Result batteryInfoDisableFastCharging(void);
|
||||||
|
|
||||||
// Helper functions to get human-readable strings
|
|
||||||
const char* batteryInfoGetChargerTypeString(BatteryChargerType type);
|
const char* batteryInfoGetChargerTypeString(BatteryChargerType type);
|
||||||
const char* batteryInfoGetPowerRoleString(BatteryPowerRole role);
|
const char* batteryInfoGetPowerRoleString(BatteryPowerRole role);
|
||||||
const char* batteryInfoGetPDStateString(BatteryPDControllerState state);
|
const char* batteryInfoGetPDStateString(BatteryPDControllerState state);
|
||||||
|
|
||||||
// Convenience functions for common values
|
|
||||||
static inline int batteryInfoGetTemperatureMiliCelsius(BatteryChargeInfo *info) {
|
static inline int batteryInfoGetTemperatureMiliCelsius(BatteryChargeInfo *info) {
|
||||||
return info->BatteryTemperature;
|
return info->BatteryTemperature;
|
||||||
}
|
}
|
||||||
@@ -120,7 +106,6 @@ static inline bool batteryInfoIsCharging(BatteryChargeInfo *info) {
|
|||||||
return IS_BATTERY_CHARGING_ENABLED(info);
|
return IS_BATTERY_CHARGING_ENABLED(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
// String lookup tables
|
|
||||||
static const char* s_chargerTypeStrings[] = {
|
static const char* s_chargerTypeStrings[] = {
|
||||||
"None",
|
"None",
|
||||||
"Power Delivery",
|
"Power Delivery",
|
||||||
@@ -187,7 +172,6 @@ static Result psmDisableFastBatteryCharging_internal(void) {
|
|||||||
return serviceDispatch(&g_psmService, 11);
|
return serviceDispatch(&g_psmService, 11);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Public API implementations
|
|
||||||
Result batteryInfoInitialize(void) {
|
Result batteryInfoInitialize(void) {
|
||||||
if (g_batteryInfoInitialized)
|
if (g_batteryInfoInitialized)
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
756
Source/sys-clk/common/include/ipc.h
Normal file
756
Source/sys-clk/common/include/ipc.h
Normal file
@@ -0,0 +1,756 @@
|
|||||||
|
/**
|
||||||
|
* @file ipc.h
|
||||||
|
* @brief Inter-process communication handling
|
||||||
|
* @author plutoo
|
||||||
|
* @copyright libnx Authors
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <switch.h>
|
||||||
|
|
||||||
|
/// IPC input header magic
|
||||||
|
#define SFCI_MAGIC 0x49434653
|
||||||
|
/// IPC output header magic
|
||||||
|
#define SFCO_MAGIC 0x4f434653
|
||||||
|
|
||||||
|
/// IPC invalid object ID
|
||||||
|
#define IPC_INVALID_OBJECT_ID UINT32_MAX
|
||||||
|
|
||||||
|
///@name IPC request building
|
||||||
|
///@{
|
||||||
|
|
||||||
|
/// IPC command (request) structure.
|
||||||
|
#define IPC_MAX_BUFFERS 8
|
||||||
|
#define IPC_MAX_OBJECTS 8
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
BufferType_Normal=0, ///< Regular buffer.
|
||||||
|
BufferType_Type1=1, ///< Allows ProcessMemory and shared TransferMemory.
|
||||||
|
BufferType_Invalid=2,
|
||||||
|
BufferType_Type3=3 ///< Same as Type1 except remote process is not allowed to use device-mapping.
|
||||||
|
} BufferType;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
BufferDirection_Send=0,
|
||||||
|
BufferDirection_Recv=1,
|
||||||
|
BufferDirection_Exch=2,
|
||||||
|
} BufferDirection;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
IpcCommandType_Invalid = 0,
|
||||||
|
IpcCommandType_LegacyRequest = 1,
|
||||||
|
IpcCommandType_Close = 2,
|
||||||
|
IpcCommandType_LegacyControl = 3,
|
||||||
|
IpcCommandType_Request = 4,
|
||||||
|
IpcCommandType_Control = 5,
|
||||||
|
IpcCommandType_RequestWithContext = 6,
|
||||||
|
IpcCommandType_ControlWithContext = 7,
|
||||||
|
} IpcCommandType;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
DomainMessageType_Invalid = 0,
|
||||||
|
DomainMessageType_SendMessage = 1,
|
||||||
|
DomainMessageType_Close = 2,
|
||||||
|
} DomainMessageType;
|
||||||
|
|
||||||
|
/// IPC domain message header.
|
||||||
|
typedef struct {
|
||||||
|
u8 Type;
|
||||||
|
u8 NumObjectIds;
|
||||||
|
u16 Length;
|
||||||
|
u32 ThisObjectId;
|
||||||
|
u32 Pad[2];
|
||||||
|
} DomainMessageHeader;
|
||||||
|
|
||||||
|
/// IPC domain response header.
|
||||||
|
typedef struct {
|
||||||
|
u32 NumObjectIds;
|
||||||
|
u32 Pad[3];
|
||||||
|
} DomainResponseHeader;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
size_t NumSend; // A
|
||||||
|
size_t NumRecv; // B
|
||||||
|
size_t NumExch; // W
|
||||||
|
const void* Buffers[IPC_MAX_BUFFERS];
|
||||||
|
size_t BufferSizes[IPC_MAX_BUFFERS];
|
||||||
|
BufferType BufferTypes[IPC_MAX_BUFFERS];
|
||||||
|
|
||||||
|
size_t NumStaticIn; // X
|
||||||
|
size_t NumStaticOut; // C
|
||||||
|
const void* Statics[IPC_MAX_BUFFERS];
|
||||||
|
size_t StaticSizes[IPC_MAX_BUFFERS];
|
||||||
|
u8 StaticIndices[IPC_MAX_BUFFERS];
|
||||||
|
|
||||||
|
bool SendPid;
|
||||||
|
size_t NumHandlesCopy;
|
||||||
|
size_t NumHandlesMove;
|
||||||
|
Handle Handles[IPC_MAX_OBJECTS];
|
||||||
|
|
||||||
|
size_t NumObjectIds;
|
||||||
|
u32 ObjectIds[IPC_MAX_OBJECTS];
|
||||||
|
} IpcCommand;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initializes an IPC command structure.
|
||||||
|
* @param cmd IPC command structure.
|
||||||
|
*/
|
||||||
|
static inline void ipcInitialize(IpcCommand* cmd) {
|
||||||
|
*cmd = (IpcCommand){};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// IPC buffer descriptor.
|
||||||
|
typedef struct {
|
||||||
|
u32 Size; ///< Size of the buffer.
|
||||||
|
u32 Addr; ///< Lower 32-bits of the address of the buffer
|
||||||
|
u32 Packed; ///< Packed data (including higher bits of the address)
|
||||||
|
} IpcBufferDescriptor;
|
||||||
|
|
||||||
|
/// IPC static send-buffer descriptor.
|
||||||
|
typedef struct {
|
||||||
|
u32 Packed; ///< Packed data (including higher bits of the address)
|
||||||
|
u32 Addr; ///< Lower 32-bits of the address
|
||||||
|
} IpcStaticSendDescriptor;
|
||||||
|
|
||||||
|
/// IPC static receive-buffer descriptor.
|
||||||
|
typedef struct {
|
||||||
|
u32 Addr; ///< Lower 32-bits of the address of the buffer
|
||||||
|
u32 Packed; ///< Packed data (including higher bits of the address)
|
||||||
|
} IpcStaticRecvDescriptor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Adds a buffer to an IPC command structure.
|
||||||
|
* @param cmd IPC command structure.
|
||||||
|
* @param buffer Address of the buffer.
|
||||||
|
* @param size Size of the buffer.
|
||||||
|
* @param type Buffer type.
|
||||||
|
*/
|
||||||
|
static inline void ipcAddSendBuffer(IpcCommand* cmd, const void* buffer, size_t size, BufferType type) {
|
||||||
|
size_t off = cmd->NumSend;
|
||||||
|
cmd->Buffers[off] = buffer;
|
||||||
|
cmd->BufferSizes[off] = size;
|
||||||
|
cmd->BufferTypes[off] = type;
|
||||||
|
cmd->NumSend++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Adds a receive-buffer to an IPC command structure.
|
||||||
|
* @param cmd IPC command structure.
|
||||||
|
* @param buffer Address of the buffer.
|
||||||
|
* @param size Size of the buffer.
|
||||||
|
* @param type Buffer type.
|
||||||
|
*/
|
||||||
|
static inline void ipcAddRecvBuffer(IpcCommand* cmd, void* buffer, size_t size, BufferType type) {
|
||||||
|
size_t off = cmd->NumSend + cmd->NumRecv;
|
||||||
|
cmd->Buffers[off] = buffer;
|
||||||
|
cmd->BufferSizes[off] = size;
|
||||||
|
cmd->BufferTypes[off] = type;
|
||||||
|
cmd->NumRecv++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Adds an exchange-buffer to an IPC command structure.
|
||||||
|
* @param cmd IPC command structure.
|
||||||
|
* @param buffer Address of the buffer.
|
||||||
|
* @param size Size of the buffer.
|
||||||
|
* @param type Buffer type.
|
||||||
|
*/
|
||||||
|
static inline void ipcAddExchBuffer(IpcCommand* cmd, void* buffer, size_t size, BufferType type) {
|
||||||
|
size_t off = cmd->NumSend + cmd->NumRecv + cmd->NumExch;
|
||||||
|
cmd->Buffers[off] = buffer;
|
||||||
|
cmd->BufferSizes[off] = size;
|
||||||
|
cmd->BufferTypes[off] = type;
|
||||||
|
cmd->NumExch++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Adds a static-buffer to an IPC command structure.
|
||||||
|
* @param cmd IPC command structure.
|
||||||
|
* @param buffer Address of the buffer.
|
||||||
|
* @param size Size of the buffer.
|
||||||
|
* @param index Index of buffer.
|
||||||
|
*/
|
||||||
|
static inline void ipcAddSendStatic(IpcCommand* cmd, const void* buffer, size_t size, u8 index) {
|
||||||
|
size_t off = cmd->NumStaticIn;
|
||||||
|
cmd->Statics[off] = buffer;
|
||||||
|
cmd->StaticSizes[off] = size;
|
||||||
|
cmd->StaticIndices[off] = index;
|
||||||
|
cmd->NumStaticIn++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Adds a static-receive-buffer to an IPC command structure.
|
||||||
|
* @param cmd IPC command structure.
|
||||||
|
* @param buffer Address of the buffer.
|
||||||
|
* @param size Size of the buffer.
|
||||||
|
* @param index Index of buffer.
|
||||||
|
*/
|
||||||
|
static inline void ipcAddRecvStatic(IpcCommand* cmd, void* buffer, size_t size, u8 index) {
|
||||||
|
size_t off = cmd->NumStaticIn + cmd->NumStaticOut;
|
||||||
|
cmd->Statics[off] = buffer;
|
||||||
|
cmd->StaticSizes[off] = size;
|
||||||
|
cmd->StaticIndices[off] = index;
|
||||||
|
cmd->NumStaticOut++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Adds a smart-buffer (buffer + static-buffer pair) to an IPC command structure.
|
||||||
|
* @param cmd IPC command structure.
|
||||||
|
* @param pointer_buffer_size Pointer buffer size.
|
||||||
|
* @param buffer Address of the buffer.
|
||||||
|
* @param size Size of the buffer.
|
||||||
|
* @param index Index of buffer.
|
||||||
|
*/
|
||||||
|
static inline void ipcAddSendSmart(IpcCommand* cmd, size_t pointer_buffer_size, const void* buffer, size_t size, u8 index) {
|
||||||
|
if (pointer_buffer_size != 0 && size <= pointer_buffer_size) {
|
||||||
|
ipcAddSendBuffer(cmd, NULL, 0, BufferType_Normal);
|
||||||
|
ipcAddSendStatic(cmd, buffer, size, index);
|
||||||
|
} else {
|
||||||
|
ipcAddSendBuffer(cmd, buffer, size, BufferType_Normal);
|
||||||
|
ipcAddSendStatic(cmd, NULL, 0, index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Adds a smart-receive-buffer (buffer + static-receive-buffer pair) to an IPC command structure.
|
||||||
|
* @param cmd IPC command structure.
|
||||||
|
* @param pointer_buffer_size Pointer buffer size.
|
||||||
|
* @param buffer Address of the buffer.
|
||||||
|
* @param size Size of the buffer.
|
||||||
|
* @param index Index of buffer.
|
||||||
|
*/
|
||||||
|
static inline void ipcAddRecvSmart(IpcCommand* cmd, size_t pointer_buffer_size, void* buffer, size_t size, u8 index) {
|
||||||
|
if (pointer_buffer_size != 0 && size <= pointer_buffer_size) {
|
||||||
|
ipcAddRecvBuffer(cmd, NULL, 0, BufferType_Normal);
|
||||||
|
ipcAddRecvStatic(cmd, buffer, size, index);
|
||||||
|
} else {
|
||||||
|
ipcAddRecvBuffer(cmd, buffer, size, BufferType_Normal);
|
||||||
|
ipcAddRecvStatic(cmd, NULL, 0, index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Tags an IPC command structure to send the PID.
|
||||||
|
* @param cmd IPC command structure.
|
||||||
|
*/
|
||||||
|
static inline void ipcSendPid(IpcCommand* cmd) {
|
||||||
|
cmd->SendPid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Adds a copy-handle to be sent through an IPC command structure.
|
||||||
|
* @param cmd IPC command structure.
|
||||||
|
* @param h Handle to send.
|
||||||
|
* @remark The receiving process gets a copy of the handle.
|
||||||
|
*/
|
||||||
|
static inline void ipcSendHandleCopy(IpcCommand* cmd, Handle h) {
|
||||||
|
cmd->Handles[cmd->NumHandlesCopy++] = h;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Adds a move-handle to be sent through an IPC command structure.
|
||||||
|
* @param cmd IPC command structure.
|
||||||
|
* @param h Handle to send.
|
||||||
|
* @remark The sending process loses ownership of the handle, which is transferred to the receiving process.
|
||||||
|
*/
|
||||||
|
static inline void ipcSendHandleMove(IpcCommand* cmd, Handle h) {
|
||||||
|
cmd->Handles[cmd->NumHandlesCopy + cmd->NumHandlesMove++] = h;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Prepares the header of an IPC command structure.
|
||||||
|
* @param cmd IPC command structure.
|
||||||
|
* @param sizeof_raw Size in bytes of the raw data structure to embed inside the IPC request
|
||||||
|
* @return Pointer to the raw embedded data structure in the request, ready to be filled out.
|
||||||
|
*/
|
||||||
|
static inline void* ipcPrepareHeader(IpcCommand* cmd, size_t sizeof_raw) {
|
||||||
|
u32* buf = (u32*)armGetTls();
|
||||||
|
size_t i;
|
||||||
|
*buf++ = IpcCommandType_Request | (cmd->NumStaticIn << 16) | (cmd->NumSend << 20) | (cmd->NumRecv << 24) | (cmd->NumExch << 28);
|
||||||
|
|
||||||
|
u32* fill_in_size_later = buf;
|
||||||
|
|
||||||
|
if (cmd->NumStaticOut > 0) {
|
||||||
|
*buf = (cmd->NumStaticOut + 2) << 10;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
*buf = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd->SendPid || cmd->NumHandlesCopy > 0 || cmd->NumHandlesMove > 0) {
|
||||||
|
*buf++ |= 0x80000000;
|
||||||
|
*buf++ = (!!cmd->SendPid) | (cmd->NumHandlesCopy << 1) | (cmd->NumHandlesMove << 5);
|
||||||
|
|
||||||
|
if (cmd->SendPid)
|
||||||
|
buf += 2;
|
||||||
|
|
||||||
|
for (i=0; i<(cmd->NumHandlesCopy + cmd->NumHandlesMove); i++)
|
||||||
|
*buf++ = cmd->Handles[i];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
buf++;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i=0; i<cmd->NumStaticIn; i++, buf+=2) {
|
||||||
|
IpcStaticSendDescriptor* desc = (IpcStaticSendDescriptor*) buf;
|
||||||
|
|
||||||
|
uintptr_t ptr = (uintptr_t) cmd->Statics[i];
|
||||||
|
desc->Addr = ptr;
|
||||||
|
desc->Packed = cmd->StaticIndices[i] | (cmd->StaticSizes[i] << 16) |
|
||||||
|
(((ptr >> 32) & 15) << 12) | (((ptr >> 36) & 15) << 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i=0; i<(cmd->NumSend + cmd->NumRecv + cmd->NumExch); i++, buf+=3) {
|
||||||
|
IpcBufferDescriptor* desc = (IpcBufferDescriptor*) buf;
|
||||||
|
desc->Size = cmd->BufferSizes[i];
|
||||||
|
|
||||||
|
uintptr_t ptr = (uintptr_t) cmd->Buffers[i];
|
||||||
|
desc->Addr = ptr;
|
||||||
|
desc->Packed = cmd->BufferTypes[i] |
|
||||||
|
(((ptr >> 32) & 15) << 28) | ((ptr >> 36) << 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 padding = ((16 - (((uintptr_t) buf) & 15)) & 15) / 4;
|
||||||
|
u32* raw = (u32*) (buf + padding);
|
||||||
|
|
||||||
|
size_t raw_size = (sizeof_raw/4) + 4;
|
||||||
|
buf += raw_size;
|
||||||
|
|
||||||
|
u16* buf_u16 = (u16*) buf;
|
||||||
|
|
||||||
|
for (i=0; i<cmd->NumStaticOut; i++) {
|
||||||
|
size_t off = cmd->NumStaticIn + i;
|
||||||
|
size_t sz = (uintptr_t) cmd->StaticSizes[off];
|
||||||
|
|
||||||
|
buf_u16[i] = (sz > 0xFFFF) ? 0 : sz;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t u16s_size = ((2*cmd->NumStaticOut) + 3)/4;
|
||||||
|
buf += u16s_size;
|
||||||
|
raw_size += u16s_size;
|
||||||
|
|
||||||
|
*fill_in_size_later |= raw_size;
|
||||||
|
|
||||||
|
for (i=0; i<cmd->NumStaticOut; i++, buf+=2) {
|
||||||
|
IpcStaticRecvDescriptor* desc = (IpcStaticRecvDescriptor*) buf;
|
||||||
|
size_t off = cmd->NumStaticIn + i;
|
||||||
|
|
||||||
|
uintptr_t ptr = (uintptr_t) cmd->Statics[off];
|
||||||
|
desc->Addr = ptr;
|
||||||
|
desc->Packed = (ptr >> 32) | (cmd->StaticSizes[off] << 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (void*) raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Dispatches an IPC request.
|
||||||
|
* @param session IPC session handle.
|
||||||
|
* @return Result code.
|
||||||
|
*/
|
||||||
|
static inline Result ipcDispatch(Handle session) {
|
||||||
|
return svcSendSyncRequest(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
///@}
|
||||||
|
|
||||||
|
///@name IPC response parsing
|
||||||
|
///@{
|
||||||
|
|
||||||
|
/// IPC parsed command (response) structure.
|
||||||
|
typedef struct {
|
||||||
|
IpcCommandType CommandType; ///< Type of the command
|
||||||
|
|
||||||
|
bool HasPid; ///< true if the 'Pid' field is filled out.
|
||||||
|
u64 Pid; ///< PID included in the response (only if HasPid is true)
|
||||||
|
|
||||||
|
size_t NumHandles; ///< Number of handles copied.
|
||||||
|
Handle Handles[IPC_MAX_OBJECTS]; ///< Handles.
|
||||||
|
bool WasHandleCopied[IPC_MAX_OBJECTS]; ///< true if the handle was moved, false if it was copied.
|
||||||
|
|
||||||
|
bool IsDomainRequest; ///< true if the the message is a Domain message.
|
||||||
|
DomainMessageType InMessageType; ///< Type of the domain message.
|
||||||
|
u32 InMessageLength; ///< Size of rawdata (for domain messages).
|
||||||
|
u32 InThisObjectId; ///< Object ID to call the command on (for domain messages).
|
||||||
|
size_t InNumObjectIds; ///< Number of object IDs (for domain messages).
|
||||||
|
u32 InObjectIds[IPC_MAX_OBJECTS]; ///< Object IDs (for domain messages).
|
||||||
|
|
||||||
|
bool IsDomainResponse; ///< true if the the message is a Domain response.
|
||||||
|
size_t OutNumObjectIds; ///< Number of object IDs (for domain responses).
|
||||||
|
u32 OutObjectIds[IPC_MAX_OBJECTS]; ///< Object IDs (for domain responses).
|
||||||
|
|
||||||
|
size_t NumBuffers; ///< Number of buffers in the response.
|
||||||
|
void* Buffers[IPC_MAX_BUFFERS]; ///< Pointers to the buffers.
|
||||||
|
size_t BufferSizes[IPC_MAX_BUFFERS]; ///< Sizes of the buffers.
|
||||||
|
BufferType BufferTypes[IPC_MAX_BUFFERS]; ///< Types of the buffers.
|
||||||
|
BufferDirection BufferDirections[IPC_MAX_BUFFERS]; ///< Direction of each buffer.
|
||||||
|
|
||||||
|
size_t NumStatics; ///< Number of statics in the response.
|
||||||
|
void* Statics[IPC_MAX_BUFFERS]; ///< Pointers to the statics.
|
||||||
|
size_t StaticSizes[IPC_MAX_BUFFERS]; ///< Sizes of the statics.
|
||||||
|
u8 StaticIndices[IPC_MAX_BUFFERS]; ///< Indices of the statics.
|
||||||
|
|
||||||
|
size_t NumStaticsOut; ///< Number of output statics available in the response.
|
||||||
|
|
||||||
|
void* Raw; ///< Pointer to the raw embedded data structure in the response.
|
||||||
|
void* RawWithoutPadding; ///< Pointer to the raw embedded data structure, without padding.
|
||||||
|
size_t RawSize; ///< Size of the raw embedded data.
|
||||||
|
} IpcParsedCommand;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Parse an IPC command response into an IPC parsed command structure.
|
||||||
|
* @param r IPC parsed command structure to fill in.
|
||||||
|
* @return Result code.
|
||||||
|
*/
|
||||||
|
static inline Result ipcParse(IpcParsedCommand* r) {
|
||||||
|
u32* buf = (u32*)armGetTls();
|
||||||
|
u32 ctrl0 = *buf++;
|
||||||
|
u32 ctrl1 = *buf++;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
r->IsDomainRequest = false;
|
||||||
|
r->IsDomainResponse = false;
|
||||||
|
|
||||||
|
r->CommandType = (IpcCommandType) (ctrl0 & 0xffff);
|
||||||
|
r->HasPid = false;
|
||||||
|
r->RawSize = (ctrl1 & 0x1ff) * 4;
|
||||||
|
r->NumHandles = 0;
|
||||||
|
|
||||||
|
r->NumStaticsOut = (ctrl1 >> 10) & 15;
|
||||||
|
if (r->NumStaticsOut >> 1) r->NumStaticsOut--; // Value 2 -> Single descriptor
|
||||||
|
if (r->NumStaticsOut >> 1) r->NumStaticsOut--; // Value 3+ -> (Value - 2) descriptors
|
||||||
|
|
||||||
|
if (ctrl1 & 0x80000000) {
|
||||||
|
u32 ctrl2 = *buf++;
|
||||||
|
|
||||||
|
if (ctrl2 & 1) {
|
||||||
|
r->HasPid = true;
|
||||||
|
r->Pid = *buf++;
|
||||||
|
r->Pid |= ((u64)(*buf++)) << 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t num_handles_copy = ((ctrl2 >> 1) & 15);
|
||||||
|
size_t num_handles_move = ((ctrl2 >> 5) & 15);
|
||||||
|
|
||||||
|
size_t num_handles = num_handles_copy + num_handles_move;
|
||||||
|
u32* buf_after_handles = buf + num_handles;
|
||||||
|
|
||||||
|
if (num_handles > IPC_MAX_OBJECTS)
|
||||||
|
num_handles = IPC_MAX_OBJECTS;
|
||||||
|
|
||||||
|
for (i=0; i<num_handles; i++)
|
||||||
|
{
|
||||||
|
r->Handles[i] = *(buf+i);
|
||||||
|
r->WasHandleCopied[i] = (i < num_handles_copy);
|
||||||
|
}
|
||||||
|
|
||||||
|
r->NumHandles = num_handles;
|
||||||
|
buf = buf_after_handles;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t num_statics = (ctrl0 >> 16) & 15;
|
||||||
|
u32* buf_after_statics = buf + num_statics*2;
|
||||||
|
|
||||||
|
if (num_statics > IPC_MAX_BUFFERS)
|
||||||
|
num_statics = IPC_MAX_BUFFERS;
|
||||||
|
|
||||||
|
for (i=0; i<num_statics; i++, buf+=2) {
|
||||||
|
IpcStaticSendDescriptor* desc = (IpcStaticSendDescriptor*) buf;
|
||||||
|
u64 packed = (u64) desc->Packed;
|
||||||
|
|
||||||
|
r->Statics[i] = (void*) (desc->Addr | (((packed >> 12) & 15) << 32) | (((packed >> 6) & 15) << 36));
|
||||||
|
r->StaticSizes[i] = packed >> 16;
|
||||||
|
r->StaticIndices[i] = packed & 63;
|
||||||
|
}
|
||||||
|
|
||||||
|
r->NumStatics = num_statics;
|
||||||
|
buf = buf_after_statics;
|
||||||
|
|
||||||
|
size_t num_bufs_send = (ctrl0 >> 20) & 15;
|
||||||
|
size_t num_bufs_recv = (ctrl0 >> 24) & 15;
|
||||||
|
size_t num_bufs_exch = (ctrl0 >> 28) & 15;
|
||||||
|
|
||||||
|
size_t num_bufs = num_bufs_send + num_bufs_recv + num_bufs_exch;
|
||||||
|
r->Raw = (void*)(((uintptr_t)(buf + num_bufs*3) + 15) &~ 15);
|
||||||
|
r->RawWithoutPadding = (void*)((uintptr_t)(buf + num_bufs*3));
|
||||||
|
|
||||||
|
if (num_bufs > IPC_MAX_BUFFERS)
|
||||||
|
num_bufs = IPC_MAX_BUFFERS;
|
||||||
|
|
||||||
|
for (i=0; i<num_bufs; i++, buf+=3) {
|
||||||
|
IpcBufferDescriptor* desc = (IpcBufferDescriptor*) buf;
|
||||||
|
u64 packed = (u64) desc->Packed;
|
||||||
|
|
||||||
|
r->Buffers[i] = (void*) (desc->Addr | ((packed >> 28) << 32) | (((packed >> 2) & 15) << 36));
|
||||||
|
r->BufferSizes[i] = desc->Size;
|
||||||
|
r->BufferTypes[i] = (BufferType) (packed & 3);
|
||||||
|
|
||||||
|
if (i < num_bufs_send)
|
||||||
|
r->BufferDirections[i] = BufferDirection_Send;
|
||||||
|
else if (i < (num_bufs_send + num_bufs_recv))
|
||||||
|
r->BufferDirections[i] = BufferDirection_Recv;
|
||||||
|
else
|
||||||
|
r->BufferDirections[i] = BufferDirection_Exch;
|
||||||
|
}
|
||||||
|
|
||||||
|
r->NumBuffers = num_bufs;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Queries the size of an IPC pointer buffer.
|
||||||
|
* @param session IPC session handle.
|
||||||
|
* @param size Output variable in which to store the size.
|
||||||
|
* @return Result code.
|
||||||
|
*/
|
||||||
|
static inline Result ipcQueryPointerBufferSize(Handle session, size_t *size) {
|
||||||
|
u32* buf = (u32*)armGetTls();
|
||||||
|
|
||||||
|
buf[0] = IpcCommandType_Control;
|
||||||
|
buf[1] = 8;
|
||||||
|
buf[2] = 0;
|
||||||
|
buf[3] = 0;
|
||||||
|
buf[4] = SFCI_MAGIC;
|
||||||
|
buf[5] = 0;
|
||||||
|
buf[6] = 3;
|
||||||
|
buf[7] = 0;
|
||||||
|
|
||||||
|
Result rc = ipcDispatch(session);
|
||||||
|
|
||||||
|
if (R_SUCCEEDED(rc)) {
|
||||||
|
IpcParsedCommand r;
|
||||||
|
ipcParse(&r);
|
||||||
|
|
||||||
|
struct ipcQueryPointerBufferSizeResponse {
|
||||||
|
u64 magic;
|
||||||
|
u64 result;
|
||||||
|
u32 size;
|
||||||
|
} *raw = (struct ipcQueryPointerBufferSizeResponse*)r.Raw;
|
||||||
|
|
||||||
|
rc = raw->result;
|
||||||
|
|
||||||
|
if (R_SUCCEEDED(rc)) {
|
||||||
|
*size = raw->size & 0xffff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Closes the IPC session with proper clean up.
|
||||||
|
* @param session IPC session handle.
|
||||||
|
* @return Result code.
|
||||||
|
*/
|
||||||
|
static inline Result ipcCloseSession(Handle session) {
|
||||||
|
u32* buf = (u32*)armGetTls();
|
||||||
|
buf[0] = IpcCommandType_Close;
|
||||||
|
buf[1] = 0;
|
||||||
|
return ipcDispatch(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Clones an IPC session.
|
||||||
|
* @param session IPC session handle.
|
||||||
|
* @param unk Unknown.
|
||||||
|
* @param new_session_out Output cloned IPC session handle.
|
||||||
|
* @return Result code.
|
||||||
|
*/
|
||||||
|
static inline Result ipcCloneSession(Handle session, u32 unk, Handle* new_session_out) {
|
||||||
|
u32* buf = (u32*)armGetTls();
|
||||||
|
|
||||||
|
buf[0] = IpcCommandType_Control;
|
||||||
|
buf[1] = 9;
|
||||||
|
buf[2] = 0;
|
||||||
|
buf[3] = 0;
|
||||||
|
buf[4] = SFCI_MAGIC;
|
||||||
|
buf[5] = 0;
|
||||||
|
buf[6] = 4;
|
||||||
|
buf[7] = 0;
|
||||||
|
buf[8] = unk;
|
||||||
|
|
||||||
|
Result rc = ipcDispatch(session);
|
||||||
|
|
||||||
|
if (R_SUCCEEDED(rc)) {
|
||||||
|
IpcParsedCommand r;
|
||||||
|
ipcParse(&r);
|
||||||
|
|
||||||
|
struct ipcCloneSessionResponse {
|
||||||
|
u64 magic;
|
||||||
|
u64 result;
|
||||||
|
} *raw = (struct ipcCloneSessionResponse*)r.Raw;
|
||||||
|
|
||||||
|
rc = raw->result;
|
||||||
|
|
||||||
|
if (R_SUCCEEDED(rc) && new_session_out) {
|
||||||
|
*new_session_out = r.Handles[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
///@}
|
||||||
|
|
||||||
|
///@name IPC domain handling
|
||||||
|
///@{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Converts an IPC session handle into a domain.
|
||||||
|
* @param session IPC session handle.
|
||||||
|
* @param object_id_out Output variable in which to store the object ID.
|
||||||
|
* @return Result code.
|
||||||
|
*/
|
||||||
|
static inline Result ipcConvertSessionToDomain(Handle session, u32* object_id_out) {
|
||||||
|
u32* buf = (u32*)armGetTls();
|
||||||
|
|
||||||
|
buf[0] = IpcCommandType_Control;
|
||||||
|
buf[1] = 8;
|
||||||
|
buf[4] = SFCI_MAGIC;
|
||||||
|
buf[5] = 0;
|
||||||
|
buf[6] = 0;
|
||||||
|
buf[7] = 0;
|
||||||
|
|
||||||
|
Result rc = ipcDispatch(session);
|
||||||
|
|
||||||
|
if (R_SUCCEEDED(rc)) {
|
||||||
|
IpcParsedCommand r;
|
||||||
|
ipcParse(&r);
|
||||||
|
|
||||||
|
struct ipcConvertSessionToDomainResponse {
|
||||||
|
u64 magic;
|
||||||
|
u64 result;
|
||||||
|
u32 object_id;
|
||||||
|
} *raw = (struct ipcConvertSessionToDomainResponse*)r.Raw;
|
||||||
|
|
||||||
|
rc = raw->result;
|
||||||
|
|
||||||
|
if (R_SUCCEEDED(rc)) {
|
||||||
|
*object_id_out = raw->object_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Adds an object ID to be sent through an IPC domain command structure.
|
||||||
|
* @param cmd IPC domain command structure.
|
||||||
|
* @param object_id Object ID to send.
|
||||||
|
*/
|
||||||
|
static inline void ipcSendObjectId(IpcCommand* cmd, u32 object_id) {
|
||||||
|
cmd->ObjectIds[cmd->NumObjectIds++] = object_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Prepares the header of an IPC command structure (domain version).
|
||||||
|
* @param cmd IPC command structure.
|
||||||
|
* @param sizeof_raw Size in bytes of the raw data structure to embed inside the IPC request
|
||||||
|
* @param object_id Domain object ID.
|
||||||
|
* @return Pointer to the raw embedded data structure in the request, ready to be filled out.
|
||||||
|
*/
|
||||||
|
static inline void* ipcPrepareHeaderForDomain(IpcCommand* cmd, size_t sizeof_raw, u32 object_id) {
|
||||||
|
void* raw = ipcPrepareHeader(cmd, sizeof_raw + sizeof(DomainMessageHeader) + cmd->NumObjectIds*sizeof(u32));
|
||||||
|
DomainMessageHeader* hdr = (DomainMessageHeader*) raw;
|
||||||
|
u32 *object_ids = (u32*)(((uintptr_t) raw) + sizeof(DomainMessageHeader) + sizeof_raw);
|
||||||
|
|
||||||
|
hdr->Type = DomainMessageType_SendMessage;
|
||||||
|
hdr->NumObjectIds = (u8)cmd->NumObjectIds;
|
||||||
|
hdr->Length = sizeof_raw;
|
||||||
|
hdr->ThisObjectId = object_id;
|
||||||
|
hdr->Pad[0] = hdr->Pad[1] = 0;
|
||||||
|
|
||||||
|
for(size_t i = 0; i < cmd->NumObjectIds; i++)
|
||||||
|
object_ids[i] = cmd->ObjectIds[i];
|
||||||
|
return (void*)(((uintptr_t) raw) + sizeof(DomainMessageHeader));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Parse an IPC command request into an IPC parsed command structure (domain version).
|
||||||
|
* @param r IPC parsed command structure to fill in.
|
||||||
|
* @return Result code.
|
||||||
|
*/
|
||||||
|
static inline Result ipcParseDomainRequest(IpcParsedCommand* r) {
|
||||||
|
Result rc = ipcParse(r);
|
||||||
|
DomainMessageHeader *hdr;
|
||||||
|
u32 *object_ids;
|
||||||
|
if(R_FAILED(rc))
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
hdr = (DomainMessageHeader*) r->Raw;
|
||||||
|
object_ids = (u32*)(((uintptr_t) hdr) + sizeof(DomainMessageHeader) + hdr->Length);
|
||||||
|
r->Raw = (void*)(((uintptr_t) r->Raw) + sizeof(DomainMessageHeader));
|
||||||
|
|
||||||
|
r->IsDomainRequest = true;
|
||||||
|
r->InMessageType = (DomainMessageType)(hdr->Type);
|
||||||
|
switch (r->InMessageType) {
|
||||||
|
case DomainMessageType_SendMessage:
|
||||||
|
case DomainMessageType_Close:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return MAKERESULT(Module_Libnx, LibnxError_DomainMessageUnknownType);
|
||||||
|
}
|
||||||
|
|
||||||
|
r->InThisObjectId = hdr->ThisObjectId;
|
||||||
|
r->InNumObjectIds = hdr->NumObjectIds > 8 ? 8 : hdr->NumObjectIds;
|
||||||
|
if ((uintptr_t)object_ids + sizeof(u32) * r->InNumObjectIds - (uintptr_t)armGetTls() >= 0x100) {
|
||||||
|
return MAKERESULT(Module_Libnx, LibnxError_DomainMessageTooManyObjectIds);
|
||||||
|
}
|
||||||
|
for(size_t i = 0; i < r->InNumObjectIds; i++)
|
||||||
|
r->InObjectIds[i] = object_ids[i];
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Parse an IPC command response into an IPC parsed command structure (domain version).
|
||||||
|
* @param r IPC parsed command structure to fill in.
|
||||||
|
* @param sizeof_raw Size in bytes of the raw data structure.
|
||||||
|
* @return Result code.
|
||||||
|
*/
|
||||||
|
static inline Result ipcParseDomainResponse(IpcParsedCommand* r, size_t sizeof_raw) {
|
||||||
|
Result rc = ipcParse(r);
|
||||||
|
DomainResponseHeader *hdr;
|
||||||
|
u32 *object_ids;
|
||||||
|
if(R_FAILED(rc))
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
hdr = (DomainResponseHeader*) r->Raw;
|
||||||
|
r->Raw = (void*)(((uintptr_t) r->Raw) + sizeof(DomainResponseHeader));
|
||||||
|
object_ids = (u32*)(((uintptr_t) r->Raw) + sizeof_raw);//Official sw doesn't align this.
|
||||||
|
|
||||||
|
r->IsDomainResponse = true;
|
||||||
|
|
||||||
|
r->OutNumObjectIds = hdr->NumObjectIds > 8 ? 8 : hdr->NumObjectIds;
|
||||||
|
if ((uintptr_t)object_ids + sizeof(u32) * r->OutNumObjectIds - (uintptr_t)armGetTls() >= 0x100) {
|
||||||
|
return MAKERESULT(Module_Libnx, LibnxError_DomainMessageTooManyObjectIds);
|
||||||
|
}
|
||||||
|
for(size_t i = 0; i < r->OutNumObjectIds; i++)
|
||||||
|
r->OutObjectIds[i] = object_ids[i];
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Closes a domain object by ID.
|
||||||
|
* @param session IPC session handle.
|
||||||
|
* @param object_id ID of the object to close.
|
||||||
|
* @return Result code.
|
||||||
|
*/
|
||||||
|
static inline Result ipcCloseObjectById(Handle session, u32 object_id) {
|
||||||
|
IpcCommand c;
|
||||||
|
DomainMessageHeader* hdr;
|
||||||
|
|
||||||
|
ipcInitialize(&c);
|
||||||
|
hdr = (DomainMessageHeader*)ipcPrepareHeader(&c, sizeof(DomainMessageHeader));
|
||||||
|
|
||||||
|
hdr->Type = DomainMessageType_Close;
|
||||||
|
hdr->NumObjectIds = 0;
|
||||||
|
hdr->Length = 0;
|
||||||
|
hdr->ThisObjectId = object_id;
|
||||||
|
hdr->Pad[0] = hdr->Pad[1] = 0;
|
||||||
|
|
||||||
|
return ipcDispatch(session); // this command has no associated response
|
||||||
|
}
|
||||||
|
|
||||||
|
///@}
|
||||||
@@ -88,6 +88,7 @@ static const DockedTimings g_dockedTimings1080p[] = {
|
|||||||
{8, 32, 40, 108, 8, 6, 0, 528880}, //220Hz CVT-RBv2
|
{8, 32, 40, 108, 8, 6, 0, 528880}, //220Hz CVT-RBv2
|
||||||
{8, 32, 40, 114, 8, 6, 0, 555680}, //230Hz CVT-RBv2
|
{8, 32, 40, 114, 8, 6, 0, 555680}, //230Hz CVT-RBv2
|
||||||
{8, 32, 40, 121, 8, 6, 0, 583200}, //240Hz CVT-RBv2
|
{8, 32, 40, 121, 8, 6, 0, 583200}, //240Hz CVT-RBv2
|
||||||
|
// technically you can go to 476hz, but in practice, why would you?
|
||||||
};
|
};
|
||||||
|
|
||||||
static const HandheldTimings g_handheldTimingsRETRO[] = {
|
static const HandheldTimings g_handheldTimingsRETRO[] = {
|
||||||
|
|||||||
@@ -6,11 +6,11 @@
|
|||||||
"main_thread_stack_size": "0x0000C000",
|
"main_thread_stack_size": "0x0000C000",
|
||||||
"main_thread_priority": 16,
|
"main_thread_priority": 16,
|
||||||
"default_cpu_id": 3,
|
"default_cpu_id": 3,
|
||||||
"process_category": 0,
|
"process_category": 1,
|
||||||
"is_retail": true,
|
"is_retail": true,
|
||||||
"pool_partition": 2,
|
"pool_partition": 2,
|
||||||
"is_64_bit": true,
|
"is_64_bit": true,
|
||||||
"address_space_type": 1,
|
"address_space_type": 3,
|
||||||
"filesystem_access": {
|
"filesystem_access": {
|
||||||
"permissions": "0xFFFFFFFFFFFFFFFF"
|
"permissions": "0xFFFFFFFFFFFFFFFF"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -43,9 +43,9 @@
|
|||||||
#define HOSPPC_HAS_BOOST (hosversionAtLeast(7,0,0))
|
#define HOSPPC_HAS_BOOST (hosversionAtLeast(7,0,0))
|
||||||
|
|
||||||
// governor constants
|
// governor constants
|
||||||
#define POLL_NS = 5'000'000; // 5 ms – governor poll rate
|
#define POLL_NS 5'000'000 // 5 ms – governor poll rate
|
||||||
#define DOWN_HOLD_TICKS = 10; // 50 ms – how long to in POLL_NS to hold while ramping down
|
#define DOWN_HOLD_TICKS 10 // 50 ms – how long to in POLL_NS to hold while ramping down
|
||||||
#define STEP_UTIL = 900; // multiplier for step calculations
|
#define STEP_UTIL 900 // multiplier for step calculations
|
||||||
|
|
||||||
bool isGpuGovernorEnabled = false;
|
bool isGpuGovernorEnabled = false;
|
||||||
bool isCpuGovernorEnabled = false;
|
bool isCpuGovernorEnabled = false;
|
||||||
@@ -100,6 +100,8 @@ ClockManager::ClockManager()
|
|||||||
this->lastCsvWriteNs = 0;
|
this->lastCsvWriteNs = 0;
|
||||||
|
|
||||||
this->sysDockIntegration = new SysDockIntegration;
|
this->sysDockIntegration = new SysDockIntegration;
|
||||||
|
this->saltyNXIntegration = new SaltyNXIntegration;
|
||||||
|
|
||||||
memset(&initialConfigValues, 0, sizeof(initialConfigValues));
|
memset(&initialConfigValues, 0, sizeof(initialConfigValues));
|
||||||
this->GetKipData();
|
this->GetKipData();
|
||||||
|
|
||||||
@@ -322,7 +324,7 @@ u32 ClockManager::SchedutilTargetHz(u32 util, u32 tableMaxHz) {
|
|||||||
|
|
||||||
u32 ClockManager::TableIndexForHz(const FreqTable& table, u32 targetHz) { // must pass in a freqTable as tables are different for cpu/gpu
|
u32 ClockManager::TableIndexForHz(const FreqTable& table, u32 targetHz) { // must pass in a freqTable as tables are different for cpu/gpu
|
||||||
for (u32 i = 0; i < table.count; i++)
|
for (u32 i = 0; i < table.count; i++)
|
||||||
if (this->freqTable.list[i] >= targetHz)
|
if (table.list[i] >= targetHz)
|
||||||
return i;
|
return i;
|
||||||
return table.count - 1;
|
return table.count - 1;
|
||||||
}
|
}
|
||||||
@@ -630,6 +632,7 @@ void ClockManager::HandleFreqReset(SysClkModule module, bool isBoost) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ClockManager::SetClocks(bool isBoost) {
|
void ClockManager::SetClocks(bool isBoost) {
|
||||||
|
FileUtils::LogLine("FPS: %d", this->saltyNXIntegration->GetFPS());
|
||||||
std::uint32_t targetHz = 0;
|
std::uint32_t targetHz = 0;
|
||||||
std::uint32_t maxHz = 0;
|
std::uint32_t maxHz = 0;
|
||||||
std::uint32_t nearestHz = 0;
|
std::uint32_t nearestHz = 0;
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
#include "integrations.h"
|
#include "integrations.h"
|
||||||
|
|
||||||
class SysDockIntegration;
|
class SysDockIntegration;
|
||||||
|
class SaltyNXIntegration;
|
||||||
class ClockManager
|
class ClockManager
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -257,4 +258,5 @@ class ClockManager
|
|||||||
std::uint64_t lastPowerLogNs;
|
std::uint64_t lastPowerLogNs;
|
||||||
std::uint64_t lastCsvWriteNs;
|
std::uint64_t lastCsvWriteNs;
|
||||||
SysDockIntegration *sysDockIntegration;
|
SysDockIntegration *sysDockIntegration;
|
||||||
|
SaltyNXIntegration *saltyNXIntegration;
|
||||||
};
|
};
|
||||||
@@ -18,6 +18,8 @@
|
|||||||
|
|
||||||
#include "integrations.h"
|
#include "integrations.h"
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#include <SaltyNX.h>
|
||||||
|
#include "process_management.h"
|
||||||
|
|
||||||
SysDockIntegration::SysDockIntegration() {
|
SysDockIntegration::SysDockIntegration() {
|
||||||
}
|
}
|
||||||
@@ -29,4 +31,74 @@ bool SysDockIntegration::getCurrentSysDockState() {
|
|||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SaltyNXIntegration::SaltyNXIntegration() {
|
||||||
|
if(!CheckPort()) return;
|
||||||
|
LoadSharedMemory();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//Check if SaltyNX is working
|
||||||
|
bool SaltyNXIntegration::CheckPort () {
|
||||||
|
Handle saltysd;
|
||||||
|
for (int i = 0; i < 67; i++) {
|
||||||
|
if (R_SUCCEEDED(svcConnectToNamedPort(&saltysd, "InjectServ"))) {
|
||||||
|
svcCloseHandle(saltysd);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (i == 66) return false;
|
||||||
|
svcSleepThread(1'000'000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = 0; i < 67; i++) {
|
||||||
|
if (R_SUCCEEDED(svcConnectToNamedPort(&saltysd, "InjectServ"))) {
|
||||||
|
svcCloseHandle(saltysd);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else svcSleepThread(1'000'000);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SaltyNXIntegration::LoadSharedMemory() {
|
||||||
|
if (SaltySD_Connect())
|
||||||
|
return;
|
||||||
|
|
||||||
|
SaltySD_GetSharedMemoryHandle(&remoteSharedMemory);
|
||||||
|
SaltySD_Term();
|
||||||
|
|
||||||
|
shmemLoadRemote(&_sharedmemory, remoteSharedMemory, 0x1000, Perm_Rw);
|
||||||
|
if (!shmemMap(&_sharedmemory))
|
||||||
|
SharedMemoryUsed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SaltyNXIntegration::searchSharedMemoryBlock(uintptr_t base) {
|
||||||
|
ptrdiff_t search_offset = 0;
|
||||||
|
while(search_offset < 0x1000) {
|
||||||
|
NxFps = (NxFpsSharedBlock*)(base + search_offset);
|
||||||
|
if (NxFps -> MAGIC == 0x465053) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else search_offset += 4;
|
||||||
|
}
|
||||||
|
NxFps = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 prevTid = 0;
|
||||||
|
|
||||||
|
u8 SaltyNXIntegration::GetFPS() {
|
||||||
|
if(ProcessManagement::GetCurrentApplicationId() <= 0x010000000000FFFFULL) return 254; // only try to read fps for games, not system apps
|
||||||
|
if(prevTid != ProcessManagement::GetCurrentApplicationId()) {
|
||||||
|
uintptr_t base = (uintptr_t)shmemGetAddr(&_sharedmemory);
|
||||||
|
searchSharedMemoryBlock(base);
|
||||||
|
prevTid = ProcessManagement::GetCurrentApplicationId();
|
||||||
|
}
|
||||||
|
if (NxFps) {
|
||||||
|
return NxFps->FPS;
|
||||||
|
} else {
|
||||||
|
return 254;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -34,4 +34,58 @@ public:
|
|||||||
SysDockIntegration();
|
SysDockIntegration();
|
||||||
|
|
||||||
bool getCurrentSysDockState();
|
bool getCurrentSysDockState();
|
||||||
|
};
|
||||||
|
|
||||||
|
class SaltyNXIntegration {
|
||||||
|
public:
|
||||||
|
struct resolutionCalls {
|
||||||
|
uint16_t width;
|
||||||
|
uint16_t height;
|
||||||
|
uint16_t calls;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NxFpsSharedBlock {
|
||||||
|
uint32_t MAGIC;
|
||||||
|
uint8_t FPS;
|
||||||
|
float FPSavg;
|
||||||
|
bool pluginActive;
|
||||||
|
uint8_t FPSlocked;
|
||||||
|
uint8_t FPSmode;
|
||||||
|
uint8_t ZeroSync;
|
||||||
|
uint8_t patchApplied;
|
||||||
|
uint8_t API;
|
||||||
|
uint32_t FPSticks[10];
|
||||||
|
uint8_t Buffers;
|
||||||
|
uint8_t SetBuffers;
|
||||||
|
uint8_t ActiveBuffers;
|
||||||
|
uint8_t SetActiveBuffers;
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
bool handheld: 1;
|
||||||
|
bool docked: 1;
|
||||||
|
unsigned int reserved: 6;
|
||||||
|
} NX_PACKED ds;
|
||||||
|
uint8_t general;
|
||||||
|
} displaySync;
|
||||||
|
resolutionCalls renderCalls[8];
|
||||||
|
resolutionCalls viewportCalls[8];
|
||||||
|
bool forceOriginalRefreshRate;
|
||||||
|
bool dontForce60InDocked;
|
||||||
|
bool forceSuspend;
|
||||||
|
uint8_t currentRefreshRate;
|
||||||
|
float readSpeedPerSecond;
|
||||||
|
uint8_t FPSlockedDocked;
|
||||||
|
uint64_t frameNumber;
|
||||||
|
} NX_PACKED;
|
||||||
|
|
||||||
|
NxFpsSharedBlock* NxFps = 0;
|
||||||
|
SharedMemory _sharedmemory = {};
|
||||||
|
bool SharedMemoryUsed = false;
|
||||||
|
Handle remoteSharedMemory = 1;
|
||||||
|
SaltyNXIntegration();
|
||||||
|
|
||||||
|
bool CheckPort();
|
||||||
|
void LoadSharedMemory();
|
||||||
|
void searchSharedMemoryBlock(uintptr_t base);
|
||||||
|
u8 GetFPS();
|
||||||
};
|
};
|
||||||
@@ -38,8 +38,11 @@
|
|||||||
#include "ipc_service.h"
|
#include "ipc_service.h"
|
||||||
#define INNER_HEAP_SIZE 0x40000
|
#define INNER_HEAP_SIZE 0x40000
|
||||||
|
|
||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
{
|
{
|
||||||
|
void virtmemSetup(void);
|
||||||
|
|
||||||
extern std::uint32_t __start__;
|
extern std::uint32_t __start__;
|
||||||
|
|
||||||
std::uint32_t __nx_applet_type = AppletType_None;
|
std::uint32_t __nx_applet_type = AppletType_None;
|
||||||
@@ -88,6 +91,8 @@ extern "C"
|
|||||||
rc = i2cInitialize();
|
rc = i2cInitialize();
|
||||||
if (R_FAILED(rc))
|
if (R_FAILED(rc))
|
||||||
diagAbortWithResult(MAKERESULT(Module_Libnx, LibnxError_ShouldNotHappen));
|
diagAbortWithResult(MAKERESULT(Module_Libnx, LibnxError_ShouldNotHappen));
|
||||||
|
|
||||||
|
virtmemSetup();
|
||||||
}
|
}
|
||||||
|
|
||||||
void __appExit(void)
|
void __appExit(void)
|
||||||
|
|||||||
Reference in New Issue
Block a user