mirror of
https://github.com/switchbrew/libnx.git
synced 2025-06-21 20:42:44 +02:00
Added lp2p.
This commit is contained in:
parent
022887ac2d
commit
8a31abeea7
@ -119,6 +119,7 @@ extern "C" {
|
||||
#include "switch/services/mii.h"
|
||||
#include "switch/services/miiimg.h"
|
||||
#include "switch/services/ldn.h"
|
||||
#include "switch/services/lp2p.h"
|
||||
#include "switch/services/news.h"
|
||||
|
||||
#include "switch/display/binder.h"
|
||||
|
326
nx/include/switch/services/lp2p.h
Normal file
326
nx/include/switch/services/lp2p.h
Normal file
@ -0,0 +1,326 @@
|
||||
/**
|
||||
* @file lp2p.h
|
||||
* @brief lp2p service IPC wrapper, for local-WLAN communications with accessories. See also: https://switchbrew.org/wiki/LDN_services
|
||||
* @note Only available on [9.1.0+].
|
||||
* @author yellows8
|
||||
* @copyright libnx Authors
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "../types.h"
|
||||
#include "../sf/service.h"
|
||||
#include "../kernel/event.h"
|
||||
|
||||
typedef enum {
|
||||
Lp2pServiceType_App = 0, ///< Initializes lp2p:app.
|
||||
Lp2pServiceType_System = 1, ///< Initializes lp2p:sys.
|
||||
} Lp2pServiceType;
|
||||
|
||||
/// MacAddress
|
||||
typedef struct {
|
||||
u8 addr[6]; ///< Address
|
||||
} Lp2pMacAddress;
|
||||
|
||||
/// GroupId
|
||||
typedef struct {
|
||||
u8 id[0x6]; ///< BSSID
|
||||
} Lp2pGroupId;
|
||||
|
||||
/// GroupInfo
|
||||
/// \ref lp2pScan only uses the following fields for the cmd input struct: supported_platform/priority, frequency/channel, and preshared_key_binary_size/preshared_key.
|
||||
typedef struct {
|
||||
u8 unk_x0[0x10]; ///< When zero, this is set to randomly-generated data. Used during key derivation.
|
||||
u64 local_communication_id; ///< LocalCommunicationId. When zero, the value from the user-process control.nacp is loaded. This is later validated by \ref lp2pJoin / \ref lp2pCreateGroup the same way as LdnNetworkConfig::local_communication_id. Used during key derivation.
|
||||
Lp2pGroupId group_id; ///< Should be all-zero for the input struct so that the default is used.
|
||||
char service_name[0x21]; ///< ServiceName. NUL-terminated string for the SSID. If the SSID is invalid, a new SSID is generated, however in this case the original SSID must contain a '-' character.
|
||||
s8 flags_count; ///< Must be <=0x3F.
|
||||
s8 flags[0x40]; ///< Array of s8 with the above count. Each entry value must be <=0x3F. Each entry is an array index used to load a set of flags from a global array with the specified index.
|
||||
u8 supported_platform; ///< SupportedPlatform. Must match value 1. 0 is PlatformIdNX, 1 is PlatformIdFuji.
|
||||
s8 member_count_max; ///< MemberCountMax. Must be <=0x8. If zero during group-creation, a default of value 1 is used for the value passed to a service-cmd.
|
||||
u8 unk_x82; ///< Unknown
|
||||
u8 unk_x83; ///< Unknown
|
||||
u16 frequency; ///< Wifi frequency: 24 = 2.4GHz, 50 = 5GHz.
|
||||
s16 channel; ///< Wifi channel number. 0 = use default, otherwise this must be one of the following depending on the frequency field. 24: 1, 6, 11. 50: 36, 40, 44, 48.
|
||||
u8 network_mode; ///< NetworkMode
|
||||
u8 performance_requirement; ///< PerformanceRequirement
|
||||
u8 security_type; ///< Security type, used during key derivation. 0 = use defaults, 1 = plaintext, 2 = encrypted.
|
||||
s8 static_aes_key_index; ///< StaticAesKeyIndex. Used as the array-index for selecting the KeySource used with GenerateAesKek during key derivation. Should be 1-2, otherwise GenerateAesKek is skipped and zeros are used for the AccessKey instead.
|
||||
u8 unk_x8C; ///< Unknown
|
||||
u8 priority; ///< Priority. Must match one of the following, depending on the used service (doesn't apply to \ref lp2pJoin): 55 = SystemPriority (lp2p:sys), 90 = ApplicationPriority (lp2p:app and lp2p:sys).
|
||||
u8 stealth_enabled; ///< StealthEnabled. Bool flag, controls whether the SSID is hidden.
|
||||
u8 unk_x8F; ///< If zero, a default value of 0x20 is used.
|
||||
u8 unk_x90[0x130]; ///< Unknown
|
||||
u8 preshared_key_binary_size; ///< PresharedKeyBinarySize. Must be 0x20 during key derivation.
|
||||
u8 preshared_key[0x20]; ///< PresharedKey. Used during key derivation.
|
||||
u8 unk_x1E1[0x1F]; ///< Unknown
|
||||
} Lp2pGroupInfo;
|
||||
|
||||
/// ScanResult
|
||||
typedef struct {
|
||||
Lp2pGroupInfo group_info; ///< \ref Lp2pGroupInfo
|
||||
u8 unk_x200; ///< Unknown
|
||||
u8 unk_x201[0x5]; ///< Unknown
|
||||
u16 advertise_data_size; ///< Size of the following AdvertiseData.
|
||||
u8 advertise_data[0x80]; ///< AdvertiseData, with the above size. This originates from \ref lp2pSetAdvertiseData.
|
||||
u8 unk_x288[0x78]; ///< Unknown
|
||||
} Lp2pScanResult;
|
||||
|
||||
/// NodeInfo
|
||||
typedef struct {
|
||||
u8 ip_addr[0x20]; ///< struct sockaddr for the IP address.
|
||||
u8 unk_x20[0x4]; ///< Unknown
|
||||
Lp2pMacAddress mac_addr; ///< \ref Lp2pMacAddress
|
||||
u8 unk_x2A[0x56]; ///< Unknown
|
||||
} Lp2pNodeInfo;
|
||||
|
||||
/// IpConfig. Only contains IPv4 addresses.
|
||||
typedef struct {
|
||||
u8 unk_x0[0x20]; ///< Always zeros.
|
||||
u8 ip_addr[0x20]; ///< struct sockaddr for the IP address.
|
||||
u8 subnet_mask[0x20]; ///< struct sockaddr for the subnet-mask.
|
||||
u8 gateway[0x20]; ///< struct sockaddr for the gateway(?).
|
||||
u8 unk_x80[0x80]; ///< Always zeros.
|
||||
} Lp2pIpConfig;
|
||||
|
||||
/// Initialize lp2p.
|
||||
Result lp2pInitialize(Lp2pServiceType service_type);
|
||||
|
||||
/// Exit lp2p.
|
||||
void lp2pExit(void);
|
||||
|
||||
/// Gets the Service object for INetworkService.
|
||||
Service* lp2pGetServiceSession_INetworkService(void);
|
||||
|
||||
/// Gets the Service object for INetworkServiceMonitor.
|
||||
Service* lp2pGetServiceSession_INetworkServiceMonitor(void);
|
||||
|
||||
/**
|
||||
* @brief Creates a default \ref Lp2pGroupInfo for use with \ref lp2pCreateGroup / \ref lp2pJoin.
|
||||
* @param info \ref Lp2pGroupInfo
|
||||
*/
|
||||
void lp2pCreateGroupInfo(Lp2pGroupInfo *info);
|
||||
|
||||
/**
|
||||
* @brief Creates a default \ref Lp2pGroupInfo for use with \ref lp2pScan.
|
||||
* @param info \ref Lp2pGroupInfo
|
||||
*/
|
||||
void lp2pCreateGroupInfoScan(Lp2pGroupInfo *info);
|
||||
|
||||
/**
|
||||
* @brief Sets Lp2pGroupInfo::service_name.
|
||||
* @param info \ref Lp2pGroupInfo
|
||||
* @param[in] name ServiceName / SSID.
|
||||
*/
|
||||
void lp2pGroupInfoSetServiceName(Lp2pGroupInfo *info, const char *name);
|
||||
|
||||
/**
|
||||
* @brief Sets Lp2pGroupInfo::flags_count and Lp2pGroupInfo::flags.
|
||||
* @param info \ref Lp2pGroupInfo
|
||||
* @param[in] flags Lp2pGroupInfo::flags
|
||||
* @param[in] count Lp2pGroupInfo::flags_count
|
||||
*/
|
||||
void lp2pGroupInfoSetFlags(Lp2pGroupInfo *info, s8 *flags, size_t count);
|
||||
|
||||
/**
|
||||
* @brief Sets Lp2pGroupInfo::member_count_max.
|
||||
* @param info \ref Lp2pGroupInfo
|
||||
* @param[in] count MemberCountMax
|
||||
*/
|
||||
NX_CONSTEXPR void lp2pGroupInfoSetMemberCountMax(Lp2pGroupInfo *info, size_t count) {
|
||||
info->member_count_max = count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets Lp2pGroupInfo::frequency and Lp2pGroupInfo::channel.
|
||||
* @param info \ref Lp2pGroupInfo
|
||||
* @param[in] frequency Lp2pGroupInfo::frequency
|
||||
* @param[in] channel Lp2pGroupInfo::channel
|
||||
*/
|
||||
NX_CONSTEXPR void lp2pGroupInfoSetFrequencyChannel(Lp2pGroupInfo *info, u16 frequency, s16 channel) {
|
||||
info->frequency = frequency;
|
||||
info->channel = channel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets Lp2pGroupInfo::stealth_enabled.
|
||||
* @param info \ref Lp2pGroupInfo
|
||||
* @param[in] flag Lp2pGroupInfo::stealth_enabled
|
||||
*/
|
||||
NX_CONSTEXPR void lp2pGroupInfoSetStealthEnabled(Lp2pGroupInfo *info, bool flag) {
|
||||
info->stealth_enabled = flag!=0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the PresharedKey for the specified \ref Lp2pGroupInfo.
|
||||
* @note Using this is required before using the \ref Lp2pGroupInfo as input for any cmds, so that Lp2pGroupInfo::preshared_key_binary_size gets initialized.
|
||||
* @param info \ref Lp2pGroupInfo
|
||||
* @param[in] key Data for the PresharedKey.
|
||||
* @param[in] size Size to copy into the PresharedKey, max is 0x20.
|
||||
*/
|
||||
void lp2pGroupInfoSetPresharedKey(Lp2pGroupInfo *info, const void* key, size_t size);
|
||||
|
||||
///@name INetworkService
|
||||
///@{
|
||||
|
||||
/**
|
||||
* @brief Scan
|
||||
* @param[in] info \ref Lp2pGroupInfo
|
||||
* @param[out] results Output array of \ref Lp2pScanResult.
|
||||
* @param[in] count Size of the results array in entries.
|
||||
* @param[out] total_out Total output entries.
|
||||
*/
|
||||
Result lp2pScan(const Lp2pGroupInfo *info, Lp2pScanResult *results, s32 count, s32 *total_out);
|
||||
|
||||
/**
|
||||
* @brief CreateGroup
|
||||
* @note The role (\ref lp2pGetRole) must be 0. This eventually sets the role to value 1.
|
||||
* @param[in] info \ref Lp2pGroupInfo
|
||||
*/
|
||||
Result lp2pCreateGroup(const Lp2pGroupInfo *info);
|
||||
|
||||
/**
|
||||
* @brief This destroys the previously created group from \ref lp2pCreateGroup.
|
||||
* @note If no group was previously created (role from \ref lp2pGetRole is not 1), this just returns 0.
|
||||
*/
|
||||
Result lp2pDestroyGroup(void);
|
||||
|
||||
/**
|
||||
* @brief SetAdvertiseData
|
||||
* @note The role (\ref lp2pGetRole) must be <=1.
|
||||
* @note An empty buffer (buffer=NULL/size=0) can be used to reset the AdvertiseData size in state to zero.
|
||||
* @param[out] buffer Input buffer containing arbitrary user data.
|
||||
* @param[in] size Input buffer size, must be <=0x80.
|
||||
*/
|
||||
Result lp2pSetAdvertiseData(const void* buffer, size_t size);
|
||||
|
||||
/**
|
||||
* @brief This sends an Action frame to the specified \ref Lp2pGroupId, with the specified destination \ref Lp2pMacAddress.
|
||||
* @note The role (\ref lp2pGetRole) must be non-zero.
|
||||
* @param[in] buffer Input buffer containing arbitrary user data.
|
||||
* @param[in] size Input buffer size, must be <=0x400.
|
||||
* @param[in] addr \ref Lp2pMacAddress, this can be a broadcast address.
|
||||
* @param[in] group_id \ref Lp2pGroupId
|
||||
* @param[in] frequency Must be >=1. See Lp2pGroupInfo::frequency.
|
||||
* @param[in] channel Must be >=1. See Lp2pGroupInfo::channel.
|
||||
* @param[in] flags This is only used for selecting which func to call internally, via bit0.
|
||||
*/
|
||||
Result lp2pSendToOtherGroup(const void* buffer, size_t size, Lp2pMacAddress addr, Lp2pGroupId group_id, s16 frequency, s16 channel, u32 flags);
|
||||
|
||||
/**
|
||||
* @brief This receives an Action frame. This will block until data is available (?).
|
||||
* @note The role (\ref lp2pGetRole) must be non-zero.
|
||||
* @param[out] buffer Output buffer containing arbitrary user data.
|
||||
* @param[in] size Output buffer size.
|
||||
* @param[in] flags This is only used for selecting which func to call internally, via bit0.
|
||||
* @param[in] addr \ref Lp2pMacAddress
|
||||
* @param[in] unk0 Unknown
|
||||
* @param[in] unk1 Unknown
|
||||
* @param[out] out_size This is the original size used for copying to the output buffer, before it's clamped to the output-buffer size.
|
||||
* @param[out] unk2 Unknown
|
||||
*/
|
||||
Result lp2pRecvFromOtherGroup(void* buffer, size_t size, u32 flags, Lp2pMacAddress *addr, u16 *unk0, s32 *unk1, u64 *out_size, s32 *unk2);
|
||||
|
||||
/**
|
||||
* @brief AddAcceptableGroupId
|
||||
* @param[in] group_id \ref Lp2pGroupId
|
||||
*/
|
||||
Result lp2pAddAcceptableGroupId(Lp2pGroupId group_id);
|
||||
|
||||
/**
|
||||
* @brief RemoveAcceptableGroupId
|
||||
*/
|
||||
Result lp2pRemoveAcceptableGroupId(void);
|
||||
|
||||
///@name INetworkServiceMonitor
|
||||
///@{
|
||||
|
||||
/**
|
||||
* @brief AttachNetworkInterfaceStateChangeEvent
|
||||
* @note The Event must be closed by the user once finished with it.
|
||||
* @param[out] out_event Output Event with autoclear=false.
|
||||
*/
|
||||
Result lp2pAttachNetworkInterfaceStateChangeEvent(Event* out_event);
|
||||
|
||||
/**
|
||||
* @brief GetNetworkInterfaceLastError
|
||||
*/
|
||||
Result lp2pGetNetworkInterfaceLastError(void);
|
||||
|
||||
/**
|
||||
* @brief GetRole
|
||||
* @param[out] out Output Role.
|
||||
*/
|
||||
Result lp2pGetRole(u8 *out);
|
||||
|
||||
/**
|
||||
* @brief GetAdvertiseData
|
||||
* @note The role from \ref lp2pGetRole must be value 2.
|
||||
* @param[out] buffer Output buffer data.
|
||||
* @param[in] size Output buffer size.
|
||||
* @param[out] transfer_size Size of the data copied into the buffer.
|
||||
* @param[out] original_size Original size from state.
|
||||
*/
|
||||
Result lp2pGetAdvertiseData(void* buffer, size_t size, u16 *transfer_size, u16 *original_size);
|
||||
|
||||
/**
|
||||
* @brief GetAdvertiseData2
|
||||
* @note This is identical to \ref lp2pGetAdvertiseData except this doesn't run the role validation.
|
||||
* @param[out] buffer Output buffer data.
|
||||
* @param[in] size Output buffer size.
|
||||
* @param[out] transfer_size Size of the data copied into the buffer.
|
||||
* @param[out] original_size Original size from state.
|
||||
*/
|
||||
Result lp2pGetAdvertiseData2(void* buffer, size_t size, u16 *transfer_size, u16 *original_size);
|
||||
|
||||
/**
|
||||
* @brief GetGroupInfo
|
||||
* @note The role from \ref lp2pGetRole must be non-zero.
|
||||
* @param[out] out \ref Lp2pGroupInfo
|
||||
*/
|
||||
Result lp2pGetGroupInfo(Lp2pGroupInfo *out);
|
||||
|
||||
/**
|
||||
* @brief This runs the same code as \ref lp2pCreateGroup to generate the \ref Lp2pGroupInfo for the input struct.
|
||||
* @param[out] out \ref Lp2pGroupInfo
|
||||
* @param[in] info \ref Lp2pGroupInfo
|
||||
*/
|
||||
Result lp2pJoin(Lp2pGroupInfo *out, const Lp2pGroupInfo *info);
|
||||
|
||||
/**
|
||||
* @brief GetGroupOwner
|
||||
* @note The role from \ref lp2pGetRole must be non-zero.
|
||||
* @param[out] out \ref Lp2pNodeInfo
|
||||
*/
|
||||
Result lp2pGetGroupOwner(Lp2pNodeInfo *out);
|
||||
|
||||
/**
|
||||
* @brief GetIpConfig
|
||||
* @note The role from \ref lp2pGetRole must be non-zero.
|
||||
* @param[out] out \ref Lp2pIpConfig
|
||||
*/
|
||||
Result lp2pGetIpConfig(Lp2pIpConfig *out);
|
||||
|
||||
/**
|
||||
* @brief Leave
|
||||
* @param[out] out Output value.
|
||||
*/
|
||||
Result lp2pLeave(u32 *out);
|
||||
|
||||
/**
|
||||
* @brief AttachJoinEvent
|
||||
* @note The Event must be closed by the user once finished with it.
|
||||
* @param[out] out_event Output Event with autoclear=false.
|
||||
*/
|
||||
Result lp2pAttachJoinEvent(Event* out_event);
|
||||
|
||||
/**
|
||||
* @brief GetMembers
|
||||
* @note The role from \ref lp2pGetRole must be value 1.
|
||||
* @param[out] members Output array of \ref Lp2pNodeInfo.
|
||||
* @param[in] count Size of the members array in entries. A maximum of 8 entries can be returned.
|
||||
* @param[out] total_out Total output entries.
|
||||
*/
|
||||
Result lp2pGetMembers(Lp2pNodeInfo *members, s32 count, s32 *total_out);
|
||||
|
||||
///@}
|
||||
|
360
nx/source/services/lp2p.c
Normal file
360
nx/source/services/lp2p.c
Normal file
@ -0,0 +1,360 @@
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include "service_guard.h"
|
||||
#include "sf/sessionmgr.h"
|
||||
#include "runtime/hosversion.h"
|
||||
#include "services/lp2p.h"
|
||||
|
||||
static Lp2pServiceType g_lp2pServiceType;
|
||||
static Service g_lp2pSrv;
|
||||
static Service g_lp2pINetworkService;
|
||||
static Service g_lp2pINetworkServiceMonitor;
|
||||
static SessionMgr g_lp2pSessionMgr;
|
||||
|
||||
static Result _lp2pCreateNetworkService(Service* srv, Service* srv_out, u32 inval);
|
||||
static Result _lp2pCreateNetworkServiceMonitor(Service* srv, Service* srv_out);
|
||||
|
||||
NX_INLINE bool _lp2pObjectIsChild(Service* s)
|
||||
{
|
||||
return s->session == g_lp2pSrv.session;
|
||||
}
|
||||
|
||||
NX_INLINE Result _lp2pObjectDispatchImpl(
|
||||
Service* s, u32 request_id,
|
||||
const void* in_data, u32 in_data_size,
|
||||
void* out_data, u32 out_data_size,
|
||||
SfDispatchParams disp
|
||||
) {
|
||||
int slot = -1;
|
||||
if (_lp2pObjectIsChild(s)) {
|
||||
slot = sessionmgrAttachClient(&g_lp2pSessionMgr);
|
||||
if (slot < 0) __builtin_unreachable();
|
||||
disp.target_session = sessionmgrGetClientSession(&g_lp2pSessionMgr, slot);
|
||||
serviceAssumeDomain(s);
|
||||
}
|
||||
|
||||
Result rc = serviceDispatchImpl(s, request_id, in_data, in_data_size, out_data, out_data_size, disp);
|
||||
|
||||
if (slot >= 0) {
|
||||
sessionmgrDetachClient(&g_lp2pSessionMgr, slot);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
#define _lp2pObjectDispatch(_s,_rid,...) \
|
||||
_lp2pObjectDispatchImpl((_s),(_rid),NULL,0,NULL,0,(SfDispatchParams){ __VA_ARGS__ })
|
||||
|
||||
#define _lp2pObjectDispatchIn(_s,_rid,_in,...) \
|
||||
_lp2pObjectDispatchImpl((_s),(_rid),&(_in),sizeof(_in),NULL,0,(SfDispatchParams){ __VA_ARGS__ })
|
||||
|
||||
#define _lp2pObjectDispatchOut(_s,_rid,_out,...) \
|
||||
_lp2pObjectDispatchImpl((_s),(_rid),NULL,0,&(_out),sizeof(_out),(SfDispatchParams){ __VA_ARGS__ })
|
||||
|
||||
#define _lp2pObjectDispatchInOut(_s,_rid,_in,_out,...) \
|
||||
_lp2pObjectDispatchImpl((_s),(_rid),&(_in),sizeof(_in),&(_out),sizeof(_out),(SfDispatchParams){ __VA_ARGS__ })
|
||||
|
||||
NX_GENERATE_SERVICE_GUARD_PARAMS(lp2p, (Lp2pServiceType service_type), (service_type));
|
||||
|
||||
Result _lp2pInitialize(Lp2pServiceType service_type) {
|
||||
const char *serv_name = NULL;
|
||||
Result rc = MAKERESULT(Module_Libnx, LibnxError_BadInput);
|
||||
Service srv={0};
|
||||
|
||||
g_lp2pServiceType = service_type;
|
||||
switch (g_lp2pServiceType) {
|
||||
case Lp2pServiceType_App:
|
||||
serv_name = "lp2p:app";
|
||||
rc = 0;
|
||||
break;
|
||||
case Lp2pServiceType_System:
|
||||
serv_name = "lp2p:sys";
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (R_FAILED(rc)) return rc;
|
||||
|
||||
if (hosversionBefore(9,1,0))
|
||||
return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
|
||||
|
||||
rc = smGetService(&g_lp2pSrv, serv_name);
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
rc = serviceConvertToDomain(&g_lp2pSrv);
|
||||
}
|
||||
if (R_SUCCEEDED(rc))
|
||||
rc = sessionmgrCreate(&g_lp2pSessionMgr, g_lp2pSrv.session, 0x4);
|
||||
|
||||
if (R_SUCCEEDED(rc))
|
||||
rc = _lp2pCreateNetworkService(&g_lp2pSrv, &g_lp2pINetworkService, 0x1);
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
rc = smGetService(&srv, serv_name);
|
||||
if (R_SUCCEEDED(rc)) rc = _lp2pCreateNetworkServiceMonitor(&srv, &g_lp2pINetworkServiceMonitor);
|
||||
serviceClose(&srv);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
void _lp2pCleanup(void) {
|
||||
serviceClose(&g_lp2pINetworkServiceMonitor);
|
||||
|
||||
// Close extra sessions
|
||||
sessionmgrClose(&g_lp2pSessionMgr);
|
||||
|
||||
// We can't assume g_lp2pSrv is a domain here because serviceConvertToDomain might have failed
|
||||
serviceClose(&g_lp2pSrv);
|
||||
}
|
||||
|
||||
Service* lp2pGetServiceSession_INetworkService(void) {
|
||||
return &g_lp2pINetworkService;
|
||||
}
|
||||
|
||||
Service* lp2pGetServiceSession_INetworkServiceMonitor(void) {
|
||||
return &g_lp2pINetworkServiceMonitor;
|
||||
}
|
||||
|
||||
static Result _lp2pCreateNetworkService(Service* srv, Service* srv_out, u32 inval) {
|
||||
const struct {
|
||||
u32 inval;
|
||||
u32 pad;
|
||||
u64 pid_placeholder;
|
||||
} in = { inval, 0, 0 };
|
||||
|
||||
return _lp2pObjectDispatchIn(srv, 0, in,
|
||||
.in_send_pid = true,
|
||||
.out_num_objects = 1,
|
||||
.out_objects = srv_out,
|
||||
);
|
||||
}
|
||||
|
||||
static Result _lp2pCreateNetworkServiceMonitor(Service* srv, Service* srv_out) {
|
||||
u64 pid_placeholder=0;
|
||||
return serviceDispatchIn(srv, 8, pid_placeholder,
|
||||
.in_send_pid = true,
|
||||
.out_num_objects = 1,
|
||||
.out_objects = srv_out,
|
||||
);
|
||||
}
|
||||
|
||||
static Result _lp2pObjCmdNoIO(u32 cmd_id) {
|
||||
return _lp2pObjectDispatch(&g_lp2pINetworkService, cmd_id);
|
||||
}
|
||||
|
||||
static Result _lp2pCmdGetEvent(Service* srv, Event* out_event, bool autoclear, u32 cmd_id) {
|
||||
Handle event = INVALID_HANDLE;
|
||||
Result rc = serviceDispatch(srv, cmd_id,
|
||||
.out_handle_attrs = { SfOutHandleAttr_HipcCopy },
|
||||
.out_handles = &event,
|
||||
);
|
||||
|
||||
if (R_SUCCEEDED(rc))
|
||||
eventLoadRemote(out_event, event, autoclear);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
void lp2pCreateGroupInfo(Lp2pGroupInfo *info) {
|
||||
memset(info, 0, sizeof(*info));
|
||||
|
||||
info->flags_count = 1;
|
||||
info->flags[0] = 1;
|
||||
info->supported_platform = 1;
|
||||
info->unk_x82 = 0x2;
|
||||
info->network_mode = 1;
|
||||
info->performance_requirement = 3;
|
||||
info->priority = 90;
|
||||
}
|
||||
|
||||
void lp2pCreateGroupInfoScan(Lp2pGroupInfo *info) {
|
||||
memset(info, 0, sizeof(*info));
|
||||
|
||||
info->supported_platform = 1;
|
||||
info->priority = 90;
|
||||
}
|
||||
|
||||
void lp2pGroupInfoSetServiceName(Lp2pGroupInfo *info, const char *name) {
|
||||
strncpy(info->service_name, name, sizeof(info->service_name)-1);
|
||||
info->service_name[sizeof(info->service_name)-1] = 0;
|
||||
}
|
||||
|
||||
void lp2pGroupInfoSetFlags(Lp2pGroupInfo *info, s8 *flags, size_t count) {
|
||||
info->flags_count = count;
|
||||
if (count < 1) return;
|
||||
for (s8 i=0; i<count && i<sizeof(info->flags); i++)
|
||||
info->flags[i] = flags[i];
|
||||
}
|
||||
|
||||
void lp2pGroupInfoSetPresharedKey(Lp2pGroupInfo *info, const void* key, size_t size) {
|
||||
info->preshared_key_binary_size = sizeof(info->preshared_key);
|
||||
if (size > info->preshared_key_binary_size) size = info->preshared_key_binary_size;
|
||||
memcpy(info->preshared_key, key, size);
|
||||
}
|
||||
|
||||
// INetworkService
|
||||
|
||||
Result lp2pScan(const Lp2pGroupInfo *info, Lp2pScanResult *results, s32 count, s32 *total_out) {
|
||||
return _lp2pObjectDispatchOut(&g_lp2pINetworkService, 512, *total_out,
|
||||
.buffer_attrs = {
|
||||
SfBufferAttr_HipcPointer | SfBufferAttr_In | SfBufferAttr_FixedSize,
|
||||
SfBufferAttr_HipcAutoSelect | SfBufferAttr_Out,
|
||||
},
|
||||
.buffers = {
|
||||
{ info, sizeof(*info) },
|
||||
{ results, count*sizeof(Lp2pScanResult) },
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Result lp2pCreateGroup(const Lp2pGroupInfo *info) {
|
||||
return _lp2pObjectDispatch(&g_lp2pINetworkService, 768,
|
||||
.buffer_attrs = { SfBufferAttr_FixedSize | SfBufferAttr_HipcAutoSelect | SfBufferAttr_In },
|
||||
.buffers = { { info, sizeof(*info) } },
|
||||
);
|
||||
}
|
||||
|
||||
Result lp2pDestroyGroup(void) {
|
||||
return _lp2pObjCmdNoIO(776);
|
||||
}
|
||||
|
||||
Result lp2pSetAdvertiseData(const void* buffer, size_t size) {
|
||||
return _lp2pObjectDispatch(&g_lp2pINetworkService, 784,
|
||||
.buffer_attrs = { SfBufferAttr_HipcAutoSelect | SfBufferAttr_In },
|
||||
.buffers = { { buffer, size } },
|
||||
);
|
||||
}
|
||||
|
||||
Result lp2pSendToOtherGroup(const void* buffer, size_t size, Lp2pMacAddress addr, Lp2pGroupId group_id, s16 frequency, s16 channel, u32 flags) {
|
||||
const struct {
|
||||
Lp2pMacAddress addr;
|
||||
Lp2pGroupId group_id;
|
||||
s16 frequency;
|
||||
s16 channel;
|
||||
u32 flags;
|
||||
} in = { addr, group_id, frequency, channel, flags };
|
||||
|
||||
return _lp2pObjectDispatchIn(&g_lp2pINetworkService, 1536, in,
|
||||
.buffer_attrs = { SfBufferAttr_HipcAutoSelect | SfBufferAttr_In },
|
||||
.buffers = { { buffer, size } },
|
||||
);
|
||||
}
|
||||
|
||||
Result lp2pRecvFromOtherGroup(void* buffer, size_t size, u32 flags, Lp2pMacAddress *addr, u16 *unk0, s32 *unk1, u64 *out_size, s32 *unk2) {
|
||||
struct {
|
||||
Lp2pMacAddress addr;
|
||||
u16 unk0;
|
||||
s16 unk1;
|
||||
u16 pad;
|
||||
u32 out_size;
|
||||
s32 unk2;
|
||||
} out;
|
||||
|
||||
Result rc = _lp2pObjectDispatchInOut(&g_lp2pINetworkService, 1544, flags, out,
|
||||
.buffer_attrs = { SfBufferAttr_HipcAutoSelect | SfBufferAttr_Out },
|
||||
.buffers = { { buffer, size } },
|
||||
);
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
if (addr) *addr = out.addr;
|
||||
if (unk0) *unk0 = out.unk0;
|
||||
if (unk1) *unk1 = out.unk1;
|
||||
if (out_size) *out_size = out.out_size;
|
||||
if (unk2) *unk2 = out.unk2;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
Result lp2pAddAcceptableGroupId(Lp2pGroupId group_id) {
|
||||
return _lp2pObjectDispatchIn(&g_lp2pINetworkService, 1552, group_id);
|
||||
}
|
||||
|
||||
Result lp2pRemoveAcceptableGroupId(void) {
|
||||
return _lp2pObjCmdNoIO(1560);
|
||||
}
|
||||
|
||||
// INetworkServiceMonitor
|
||||
|
||||
Result lp2pAttachNetworkInterfaceStateChangeEvent(Event* out_event) {
|
||||
return _lp2pCmdGetEvent(&g_lp2pINetworkServiceMonitor, out_event, false, 256);
|
||||
}
|
||||
|
||||
Result lp2pGetNetworkInterfaceLastError(void) {
|
||||
return serviceDispatch(&g_lp2pINetworkServiceMonitor, 264);
|
||||
}
|
||||
|
||||
Result lp2pGetRole(u8 *out) {
|
||||
return serviceDispatchOut(&g_lp2pINetworkServiceMonitor, 272, *out);
|
||||
}
|
||||
|
||||
static Result _lp2pGetAdvertiseData(void* buffer, size_t size, u16 *transfer_size, u16 *original_size, u32 cmd_id) {
|
||||
struct {
|
||||
u16 transfer_size;
|
||||
u16 original_size;
|
||||
} out;
|
||||
|
||||
Result rc = serviceDispatchOut(&g_lp2pINetworkServiceMonitor, cmd_id, out,
|
||||
.buffer_attrs = { SfBufferAttr_HipcAutoSelect | SfBufferAttr_Out },
|
||||
.buffers = { { buffer, size } },
|
||||
);
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
if (transfer_size) *transfer_size = out.transfer_size;
|
||||
if (original_size) *original_size = out.original_size;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
Result lp2pGetAdvertiseData(void* buffer, size_t size, u16 *transfer_size, u16 *original_size) {
|
||||
return _lp2pGetAdvertiseData(buffer, size, transfer_size, original_size, 280);
|
||||
}
|
||||
|
||||
Result lp2pGetAdvertiseData2(void* buffer, size_t size, u16 *transfer_size, u16 *original_size) {
|
||||
return _lp2pGetAdvertiseData(buffer, size, transfer_size, original_size, 281);
|
||||
}
|
||||
|
||||
Result lp2pGetGroupInfo(Lp2pGroupInfo *out) {
|
||||
return serviceDispatch(&g_lp2pINetworkServiceMonitor, 288,
|
||||
.buffer_attrs = { SfBufferAttr_FixedSize | SfBufferAttr_HipcAutoSelect | SfBufferAttr_Out },
|
||||
.buffers = { { out, sizeof(*out) } },
|
||||
);
|
||||
}
|
||||
|
||||
Result lp2pJoin(Lp2pGroupInfo *out, const Lp2pGroupInfo *info) {
|
||||
return serviceDispatch(&g_lp2pINetworkServiceMonitor, 296,
|
||||
.buffer_attrs = {
|
||||
SfBufferAttr_HipcAutoSelect | SfBufferAttr_Out | SfBufferAttr_FixedSize,
|
||||
SfBufferAttr_HipcAutoSelect | SfBufferAttr_In | SfBufferAttr_FixedSize,
|
||||
},
|
||||
.buffers = {
|
||||
{ out, sizeof(*out) },
|
||||
{ info, sizeof(*info) },
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Result lp2pGetGroupOwner(Lp2pNodeInfo *out) {
|
||||
return serviceDispatchOut(&g_lp2pINetworkServiceMonitor, 304, *out);
|
||||
}
|
||||
|
||||
Result lp2pGetIpConfig(Lp2pIpConfig *out) {
|
||||
return serviceDispatch(&g_lp2pINetworkServiceMonitor, 312,
|
||||
.buffer_attrs = { SfBufferAttr_FixedSize | SfBufferAttr_HipcPointer | SfBufferAttr_Out },
|
||||
.buffers = { { out, sizeof(*out) } },
|
||||
);
|
||||
}
|
||||
|
||||
Result lp2pLeave(u32 *out) {
|
||||
return serviceDispatchOut(&g_lp2pINetworkServiceMonitor, 320, *out);
|
||||
}
|
||||
|
||||
Result lp2pAttachJoinEvent(Event* out_event) {
|
||||
return _lp2pCmdGetEvent(&g_lp2pINetworkServiceMonitor, out_event, false, 328);
|
||||
}
|
||||
|
||||
Result lp2pGetMembers(Lp2pNodeInfo *members, s32 count, s32 *total_out) {
|
||||
return serviceDispatchOut(&g_lp2pINetworkServiceMonitor, 336, *total_out,
|
||||
.buffer_attrs = { SfBufferAttr_HipcAutoSelect | SfBufferAttr_Out },
|
||||
.buffers = { { members, count*sizeof(Lp2pNodeInfo) } },
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user