diff --git a/libctru/include/3ds/services/uds.h b/libctru/include/3ds/services/uds.h index 97666d9..cb1c5bb 100644 --- a/libctru/include/3ds/services/uds.h +++ b/libctru/include/3ds/services/uds.h @@ -1,20 +1,34 @@ /** * @file uds.h - * @brief UDS(NWMUDS) local-WLAN service. https://3dbrew.org/wiki/NWM_Services + * @brief UDS(NWMUDS) local-WLAN service. WARNING: This code is not ready to be used by applications yet. https://3dbrew.org/wiki/NWM_Services */ #pragma once +/// Maximum number of nodes(devices) that can be connected to the network. +#define UDS_MAXNODES 16 + +/// Broadcast value for NetworkNodeID / alias for all NetworkNodeIDs. +#define UDS_BROADCAST_NETWORKNODEID 0xFFFF + +/// Default value that can be used for udsSendTo() input8. +#define UDS_SEND_INPUT8_DEFAULT 0x2 + /// Node info struct. typedef struct { u64 uds_friendcodeseed;//UDS version of the FriendCodeSeed. u8 usercfg[0x18];//This is the first 0x18-bytes from this config block: https://www.3dbrew.org/wiki/Config_Savegame#0x000A0000_Block - u32 words_x20[2];//Not initialized by DLP-sysmodule. + + //The rest of this is initialized by NWM-module. + u16 NetworkNodeID; + u16 pad_x22; + u32 word_x24; } udsNodeInfo; /// Network struct stored as big-endian. typedef struct { u8 host_macaddress[6]; - u8 unk_x6[2]; + u8 hostmacaddr_flag;//"This flag being set to non-zero presumably indicates that the MAC address is set." + u8 unk_x7; u8 initialized_flag;//Must be non-zero otherwise NWM-module will use zeros internally instead of the actual field data, for most/all(?) of the fields in this struct. @@ -44,6 +58,47 @@ typedef struct { Handle event; } udsBindContext; +/// General NWM input structure used for AP scanning. +typedef struct { + u16 unk_x0; + u16 unk_x2; + u16 unk_x4; + u16 unk_x6; + + u8 mac_address[6]; + + u8 unk_xe[0x26];//Not initialized by dlp. +} nwmScanInputStruct; + +/// General NWM output structure from AP scanning. +typedef struct { + u32 maxsize;//"Max output size, from the command request." + u32 size;//"Total amount of output data written relative to struct+0. 0xC when there's no entries." + u32 total_entries;//"Total entries, 0 for none. " + + //The entries start here. +} nwmBeaconDataReplyHeader; + +/// General NWM output structure from AP scanning, for each entry. +typedef struct { + u32 size;//"Size of this entire entry. The next entry starts at curentry_startoffset+curentry_size." + u32 unk_x4; + u8 mac_address[6];//"AP MAC address." + u8 unk_xe[6]; + u32 unk_x14; + u32 val_x1c;//"Value 0x1C(size of this header and/or offset to the actual beacon data)." + + //The actual beacon data starts here. +} nwmBeaconDataReplyEntry; + +/// Output structure generated from host scanning output. +typedef struct { + nwmBeaconDataReplyEntry datareply_entry; + udsNetworkStruct network; + u32 total_nodes;//Total number of nodes actually connected to the network, including the host. + udsNodeInfo nodes[UDS_MAXNODES]; +} udsNetworkScanInfo; + enum { UDSNETATTR_DisableConnectClients = BIT(1), //When set new Clients are not allowed to connect. UDSNETATTR_DisableConnectSpectators = BIT(2), //When set new Spectators are (probably) not allowed to connect. @@ -60,15 +115,6 @@ typedef enum { UDSCONTYPE_Spectator = 0x2 } udsConnectionType; -/// Maximum number of nodes(devices) that can be connected to the network. -#define UDS_MAXNODES 16 - -/// Broadcast value for NetworkNodeID / alias for all NetworkNodeIDs. -#define UDS_BROADCAST_NETWORKNODEID 0xFFFF - -/// Default value that can be used for udsSendTo() input8. -#define UDS_SEND_INPUT8_DEFAULT 0x2 - /** * @brief Initializes UDS. * @param sharedmem_size This must be 0x1000-byte aligned. @@ -81,16 +127,24 @@ void udsExit(void); /** * @brief Generates a NodeInfo struct with data loaded from system-config. + * @param nodeinfo Output NodeInfo struct. * @param username If set, this is the UTF-8 string to convert for use in the struct. Max len is 10 characters without NUL-terminator. */ Result udsGenerateNodeInfo(udsNodeInfo *nodeinfo, const uint8_t *username); /** * @brief Loads the UTF-16 username stored in the input NodeInfo struct, converted to UTF-8. + * @param nodeinfo Input NodeInfo struct. * @param username This is the output UTF-8 string. Max len is 10 characters without NUL-terminator. */ Result udsGetNodeInfoUsername(udsNodeInfo *nodeinfo, uint8_t *username); +/** + * @brief Checks whether a NodeInfo struct was initialized by NWM-module(not any output from udsGenerateNodeInfo()). + * @param nodeinfo Input NodeInfo struct. + */ +bool udsCheckNodeInfoInitialized(udsNodeInfo *nodeinfo); + /** * @brief Generates a default NetworkStruct for creating networks. * @param network The output struct. @@ -100,6 +154,17 @@ Result udsGetNodeInfoUsername(udsNodeInfo *nodeinfo, uint8_t *username); */ void udsGenerateDefaultNetworkStruct(udsNetworkStruct *network, u32 wlancommID, u8 id8, u8 max_nodes); +/** + * @brief Scans for networks via beacon-scanning. + * @param outbuf Buffer which will be used by the beacon-scanning command and for the data parsing afterwards. Normally there's no need to use the contents of this buffer once this function returns. + * @param maxsize Max size of the buffer. + * @Param networks Ptr where the allocated udsNetworkScanInfo array buffer is written. The allocsize is sizeof(udsNetworkScanInfo)*total_networks. + * @Param total_networks Total number of networks stored under the networks buffer. + * @param wlancommID Unique local-WLAN communications ID for each application. + * @param id8 Additional ID that can be used by the application for different types of networks. + */ +Result udsScanBeacons(u8 *outbuf, u32 maxsize, udsNetworkScanInfo **networks, u32 *total_networks, u32 wlancommID, u8 id8); + /** * @brief Create a bind. * @param bindcontext The output bind context. @@ -113,6 +178,16 @@ Result udsBind(udsBindContext *bindcontext, u16 NetworkNodeID); */ Result udsUnbind(udsBindContext *bindcontext); +/** + * @brief Receives data over the network. + * @param bindcontext Bind context. + * @param buf Output receive buffer. + * @param size Size of the buffer. + * @param actual_size If set, the actual size written into the output buffer is stored here. This is zero when no data was received. + * @param src_NetworkNodeID If set, the source NetworkNodeID is written here. This is zero when no data was received. + */ +Result udsPullPacket(udsBindContext *bindcontext, void* buf, size_t size, size_t *actual_size, u16 *src_NetworkNodeID); + /** * @brief Sends data over the network. * @param dst_NetworkNodeID Destination NetworkNodeID. @@ -123,6 +198,12 @@ Result udsUnbind(udsBindContext *bindcontext); */ Result udsSendTo(u16 dst_NetworkNodeID, u8 input8, u8 flags, void* buf, size_t size); +/** + * @brief Gets the wifi channel currently being used. + * @param channel Output channel. + */ +Result udsGetChannel(u32 *channel); + /** * @brief Starts hosting a new network. * @param network The NetworkStruct, you can use udsGenerateDefaultNetworkStruct() for generating this. @@ -132,8 +213,24 @@ Result udsSendTo(u16 dst_NetworkNodeID, u8 input8, u8 flags, void* buf, size_t s */ Result udsCreateNetwork(udsNetworkStruct *network, void* passphrase, size_t passphrase_size, udsBindContext *bindcontext); +/** + * @brief Connect to a network. + * @param network The NetworkStruct, you can use udsScanBeacons() for this. + * @param passphrase Raw input passphrase buffer. + * @param passphrase_size Size of the passphrase buffer. + * @param bindcontext Output bind context which will be created for this host. + * @param recv_NetworkNodeID This is the NetworkNodeID passed to udsBind() internally. + * @param connection_type Type of connection, see the udsConnectionType enum values. + */ +Result udsConnectNetwork(udsNetworkStruct *network, void* passphrase, size_t passphrase_size, udsBindContext *context, u16 recv_NetworkNodeID, udsConnectionType connection_type); + /** * @brief Stop hosting the network. */ Result udsDestroyNetwork(void); +/** + * @brief Disconnect this client device from the network. + */ +Result udsDisconnectNetwork(void); + diff --git a/libctru/source/services/uds.c b/libctru/source/services/uds.c index a76df52..032be6a 100644 --- a/libctru/source/services/uds.c +++ b/libctru/source/services/uds.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include <3ds/types.h> #include <3ds/result.h> @@ -30,11 +31,18 @@ static Result udsipc_InitializeWithVersion(udsNodeInfo *nodeinfo, Handle sharedm static Result udsipc_Shutdown(void); static Result udsipc_BeginHostingNetwork(udsNetworkStruct *network, void* passphrase, size_t passphrase_size); +static Result udsipc_ConnectToNetwork(udsNetworkStruct *network, void* passphrase, size_t passphrase_size, udsConnectionType connection_type); static Result udsipc_SetProbeResponseParam(u32 oui, s8 data); +static Result udsipc_RecvBeaconBroadcastData(u8 *outbuf, u32 maxsize, nwmScanInputStruct *scaninput, u32 wlancommID, u8 id8, Handle event); + static Result udsipc_Bind(udsBindContext *bindcontext, u32 input0, u8 input1, u16 NetworkNodeID); static Result udsipc_Unbind(udsBindContext *bindcontext); +static Result udsipc_DecryptBeaconData(udsNetworkStruct *network, u8 *tag0, u8 *tag1, udsNodeInfo *out); + +static Result usd_parsebeacon(u8 *buf, u32 size, udsNetworkScanInfo *networkscan); + Result udsInit(u32 sharedmem_size, const uint8_t *username) { Result ret=0; @@ -162,6 +170,12 @@ Result udsGetNodeInfoUsername(udsNodeInfo *nodeinfo, uint8_t *username) return 0; } +bool udsCheckNodeInfoInitialized(udsNodeInfo *nodeinfo) +{ + if(nodeinfo->NetworkNodeID)return true; + return false; +} + void udsGenerateDefaultNetworkStruct(udsNetworkStruct *network, u32 wlancommID, u8 id8, u8 max_nodes) { u8 oui_value[3] = {0x00, 0x1f, 0x32}; @@ -176,7 +190,7 @@ void udsGenerateDefaultNetworkStruct(udsNetworkStruct *network, u32 wlancommID, network->wlancommID = htonl(wlancommID); network->id8 = id8; - network->attributes = UDSNETATTR_Default; + network->attributes = htons(UDSNETATTR_Default); if(max_nodes > UDS_MAXNODES)max_nodes = UDS_MAXNODES; network->max_nodes = max_nodes; @@ -245,6 +259,20 @@ Result udsCreateNetwork(udsNetworkStruct *network, void* passphrase, size_t pass return ret; } +Result udsConnectNetwork(udsNetworkStruct *network, void* passphrase, size_t passphrase_size, udsBindContext *context, u16 recv_NetworkNodeID, udsConnectionType connection_type) +{ + Result ret=0; + printf("connecting...\n"); + ret = udsipc_ConnectToNetwork(network, passphrase, passphrase_size, connection_type); + if(R_FAILED(ret))return ret; + printf("bind...\n"); + ret = udsBind(context, recv_NetworkNodeID); + + if(R_FAILED(ret))udsDisconnectNetwork(); + + return ret; +} + static Result udsipc_InitializeWithVersion(udsNodeInfo *nodeinfo, Handle sharedmem_handle, u32 sharedmem_size, Handle *eventhandle) { u32* cmdbuf=getThreadCommandBuffer(); @@ -292,6 +320,106 @@ Result udsDestroyNetwork(void) return cmdbuf[1]; } +Result udsDisconnectNetwork(void) +{ + u32* cmdbuf=getThreadCommandBuffer(); + + cmdbuf[0]=IPC_MakeHeader(0xA,0,0); // 0xA0000 + + Result ret=0; + if(R_FAILED(ret=svcSendSyncRequest(__uds_servhandle)))return ret; + + return cmdbuf[1]; +} + +Result udsScanBeacons(u8 *outbuf, u32 maxsize, udsNetworkScanInfo **networks, u32 *total_networks, u32 wlancommID, u8 id8) +{ + Result ret=0; + Handle event=0; + u32 entpos, curpos; + nwmScanInputStruct scaninput; + nwmBeaconDataReplyHeader *hdr; + nwmBeaconDataReplyEntry *entry; + udsNetworkScanInfo *networks_ptr; + + if(total_networks)*total_networks = 0; + if(networks)*networks = NULL; + + memset(&scaninput, 0, sizeof(nwmScanInputStruct)); + + scaninput.unk_x0 = 0x1; + scaninput.unk_x2 = 0x2; + scaninput.unk_x4 = 0x0421; + scaninput.unk_x6 = 0x6e; + + memset(scaninput.mac_address, 0xff, sizeof(scaninput.mac_address)); + + if(maxsize < sizeof(nwmBeaconDataReplyHeader))return -2; + + ret = svcCreateEvent(&event, 0); + if(R_FAILED(ret))return ret; + + ret = udsipc_RecvBeaconBroadcastData(outbuf, maxsize, &scaninput, wlancommID, id8, event); + svcCloseHandle(event); + if(R_FAILED(ret))return ret; + + hdr = (nwmBeaconDataReplyHeader*)outbuf; + curpos = sizeof(nwmBeaconDataReplyHeader); + + if(hdr->maxsize != maxsize)return -2; + if(hdr->size > maxsize)return -2; + + if(hdr->total_entries) + { + if(networks) + { + networks_ptr = malloc(sizeof(udsNetworkScanInfo) * hdr->total_entries); + if(networks_ptr == NULL)return -1; + memset(networks_ptr, 0, sizeof(udsNetworkScanInfo) * hdr->total_entries); + *networks = networks_ptr; + } + if(total_networks)*total_networks = hdr->total_entries; + + if(networks) + { + for(entpos=0; entpostotal_entries; entpos++) + { + if(curpos >= hdr->size) + { + ret = -2; + break; + } + + entry = (nwmBeaconDataReplyEntry*)&outbuf[curpos]; + if(entry->size > hdr->size || curpos + entry->size > hdr->size || entry->size <= sizeof(nwmBeaconDataReplyEntry)) + { + ret = -2; + break; + } + + memcpy(&networks_ptr[entpos].datareply_entry, entry, sizeof(nwmBeaconDataReplyEntry)); + + ret = usd_parsebeacon(&outbuf[curpos + sizeof(nwmBeaconDataReplyEntry)], entry->size - sizeof(nwmBeaconDataReplyEntry), &networks_ptr[entpos]); + if(R_FAILED(ret))break; + + curpos+= entry->size; + } + } + + if(R_FAILED(ret)) + { + if(networks) + { + free(*networks); + *networks = NULL; + } + if(total_networks)*total_networks = 0; + } + } + + return ret; +} + Result udsBind(udsBindContext *bindcontext, u16 NetworkNodeID) { u32 pos; @@ -329,6 +457,146 @@ Result udsUnbind(udsBindContext *bindcontext) return ret; } +static Result usd_parsebeacon(u8 *buf, u32 size, udsNetworkScanInfo *networkscan) +{ + Result ret=0; + + u8 tagid, tag_datalen; + u8 *tagptr; + u8 oui[3] = {0x00, 0x1f, 0x32}; + u8 oui_type; + u8 appdata_size; + + //Index0 = 21(0x15), index1=24(0x18), index2=25(0x19). + u8 *tags_data[3] = {0}; + u32 tags_sizes[3] = {0}; + int tagindex; + u32 pos; + + u8 tmp_tagdata[0xfe*2]; + + if(size < 0xc)return -3; + + buf+=0xc;//Skip down to the tagged parameters in the beacon. + size-=0xc; + + while(size)//Locate each of the Nintendo vendor tags which this code uses. + { + if(size < 2)return -3; + + tagid = buf[0]; + tag_datalen = buf[1]; + + buf+= 0x2; + size-= 0x2; + + if(tag_datalen > size)return -3; + + if(tagid==0xdd)//Vendor tag + { + if(tag_datalen < 4)return -3; + + if(memcmp(buf, oui, sizeof(oui))==0) + { + oui_type = buf[3]; + + tagindex = -1; + + if(oui_type==21) + { + tagindex = 0; + } + else if(oui_type==24) + { + tagindex = 1; + } + else if(oui_type==25) + { + tagindex = 2; + } + + if(tagindex>=0) + { + tags_data[tagindex] = buf; + tags_sizes[tagindex] = tag_datalen; + } + } + } + + buf+= tag_datalen; + size-= tag_datalen; + } + + for(tagindex=0; tagindex<3; tagindex++)//Verify that the required tags exist and have valid sizes. + { + if(tagindex!=2 && (tags_data[tagindex]==NULL || tags_sizes[tagindex]==0))return -3; + + if(tagindex && tags_sizes[tagindex] > 0xFE)return -3; + if(tagindex==1 && tags_sizes[tagindex] < 0x12)return -3; + + if(tagindex==0 && ((tags_sizes[tagindex]<0x34) || (tags_sizes[tagindex]>0x34+0xC8)))return -3; + } + + //Tag type21 + tagindex = 0; + { + tagptr = tags_data[tagindex]; + tag_datalen = tags_sizes[tagindex]; + + appdata_size = tagptr[0x33]; + if((appdata_size > 0xC8) || (appdata_size > tag_datalen-0x34))return -3;//Verify the appdata size. + + memset(&networkscan->network, 0, sizeof(udsNetworkStruct)); + + memcpy(&networkscan->network.oui_value, tagptr, 0x1F); + + networkscan->network.appdata_size = appdata_size; + if(appdata_size)memcpy(networkscan->network.appdata, &tagptr[0x34], appdata_size); + + networkscan->network.initialized_flag = 1; + networkscan->network.hostmacaddr_flag = 1; + memcpy(networkscan->network.host_macaddress, networkscan->datareply_entry.mac_address, sizeof(networkscan->network.host_macaddress)); + } + + memset(tmp_tagdata, 0, sizeof(tmp_tagdata)); + for(tagindex=1; tagindex<3; tagindex++) + { + if(tags_data[tagindex])memcpy(&tmp_tagdata[0xfe * (tagindex-1)], tags_data[tagindex], tags_sizes[tagindex]); + } + + ret = udsipc_DecryptBeaconData(&networkscan->network, tmp_tagdata, &tmp_tagdata[0xfe], networkscan->nodes); + if(R_FAILED(ret))return ret; + + for(pos=0; posnodes[pos]))break; + + networkscan->total_nodes++; + } + + return 0; +} + +static Result udsipc_RecvBeaconBroadcastData(u8 *outbuf, u32 maxsize, nwmScanInputStruct *scaninput, u32 wlancommID, u8 id8, Handle event) +{ + u32* cmdbuf=getThreadCommandBuffer(); + + cmdbuf[0]=IPC_MakeHeader(0xF,16,4); // 0xF0404 + cmdbuf[1]=maxsize; + memcpy(&cmdbuf[2], scaninput, sizeof(nwmScanInputStruct)); + cmdbuf[15]=wlancommID; + cmdbuf[16]=id8; + cmdbuf[17]=IPC_Desc_SharedHandles(1); + cmdbuf[18]=event; + cmdbuf[19]=IPC_Desc_Buffer(maxsize, IPC_BUFFER_W); + cmdbuf[20]=(u32)outbuf; + + Result ret=0; + if(R_FAILED(ret=svcSendSyncRequest(__uds_servhandle)))return ret; + + return cmdbuf[1]; +} + static Result udsipc_Bind(udsBindContext *bindcontext, u32 input0, u8 input1, u16 NetworkNodeID)//input0 and input1 are unknown. { u32* cmdbuf=getThreadCommandBuffer(); @@ -364,6 +632,44 @@ static Result udsipc_Unbind(udsBindContext *bindcontext) return cmdbuf[1]; } +Result udsPullPacket(udsBindContext *bindcontext, void* buf, size_t size, size_t *actual_size, u16 *src_NetworkNodeID) +{ + u32* cmdbuf=getThreadCommandBuffer(); + u32 saved_threadstorage[2]; + + u32 aligned_size = (size+0x3) & ~0x3; + + cmdbuf[0]=IPC_MakeHeader(0x14,3,0); // 0x1400C0 + cmdbuf[1]=bindcontext->BindNodeID; + cmdbuf[2]=aligned_size>>2; + cmdbuf[3]=size; + + u32 * staticbufs = getThreadStaticBuffers(); + saved_threadstorage[0] = staticbufs[0]; + saved_threadstorage[1] = staticbufs[1]; + + staticbufs[0] = IPC_Desc_StaticBuffer(aligned_size,0); + staticbufs[1] = (u32)buf; + + Result ret=0; + ret=svcSendSyncRequest(__uds_servhandle); + + staticbufs[0] = saved_threadstorage[0]; + staticbufs[1] = saved_threadstorage[1]; + + if(R_FAILED(ret))return ret; + + ret = cmdbuf[1]; + + if(R_SUCCEEDED(ret)) + { + if(actual_size)*actual_size = cmdbuf[2]; + if(src_NetworkNodeID)*src_NetworkNodeID = cmdbuf[3]; + } + + return ret; +} + Result udsSendTo(u16 dst_NetworkNodeID, u8 input8, u8 flags, void* buf, size_t size) { u32* cmdbuf=getThreadCommandBuffer(); @@ -386,6 +692,24 @@ Result udsSendTo(u16 dst_NetworkNodeID, u8 input8, u8 flags, void* buf, size_t s return cmdbuf[1]; } +Result udsGetChannel(u32 *channel) +{ + u32* cmdbuf=getThreadCommandBuffer(); + + cmdbuf[0]=IPC_MakeHeader(0x1A,0,0); // 0x1A0000 + + Result ret=0; + if(R_FAILED(ret=svcSendSyncRequest(__uds_servhandle)))return ret; + ret = cmdbuf[1]; + + if(R_SUCCEEDED(ret)) + { + *channel = cmdbuf[2]; + } + + return ret; +} + static Result udsipc_BeginHostingNetwork(udsNetworkStruct *network, void* passphrase, size_t passphrase_size) { u32* cmdbuf=getThreadCommandBuffer(); @@ -395,7 +719,7 @@ static Result udsipc_BeginHostingNetwork(udsNetworkStruct *network, void* passph cmdbuf[2]=IPC_Desc_StaticBuffer(sizeof(udsNetworkStruct), 1); cmdbuf[3]=(u32)network; cmdbuf[4]=IPC_Desc_StaticBuffer(passphrase_size, 0); - cmdbuf[5]=(u32)network; + cmdbuf[5]=(u32)passphrase; Result ret=0; if(R_FAILED(ret=svcSendSyncRequest(__uds_servhandle)))return ret; @@ -403,6 +727,57 @@ static Result udsipc_BeginHostingNetwork(udsNetworkStruct *network, void* passph return cmdbuf[1]; } +static Result udsipc_ConnectToNetwork(udsNetworkStruct *network, void* passphrase, size_t passphrase_size, udsConnectionType connection_type) +{ + u32* cmdbuf=getThreadCommandBuffer(); + + cmdbuf[0]=IPC_MakeHeader(0x1E,2,4); // 0x1E0084 + cmdbuf[1]=connection_type; + cmdbuf[2]=passphrase_size; + cmdbuf[3]=IPC_Desc_StaticBuffer(sizeof(udsNetworkStruct), 1); + cmdbuf[4]=(u32)network; + cmdbuf[5]=IPC_Desc_StaticBuffer(passphrase_size, 0); + cmdbuf[6]=(u32)passphrase; + + Result ret=0; + if(R_FAILED(ret=svcSendSyncRequest(__uds_servhandle)))return ret; + + return cmdbuf[1]; +} + +static Result udsipc_DecryptBeaconData(udsNetworkStruct *network, u8 *tag0, u8 *tag1, udsNodeInfo *out) +{ + u32* cmdbuf=getThreadCommandBuffer(); + u32 tagsize = 0xfe; + + u32 saved_threadstorage[2]; + + cmdbuf[0]=IPC_MakeHeader(0x1F,0,6); // 0x1F0006 + cmdbuf[1]=IPC_Desc_StaticBuffer(sizeof(udsNetworkStruct), 1); + cmdbuf[2]=(u32)network; + cmdbuf[3]=IPC_Desc_StaticBuffer(tagsize, 2); + cmdbuf[4]=(u32)tag0; + cmdbuf[5]=IPC_Desc_StaticBuffer(tagsize, 3); + cmdbuf[6]=(u32)tag1; + + u32 * staticbufs = getThreadStaticBuffers(); + saved_threadstorage[0] = staticbufs[0]; + saved_threadstorage[1] = staticbufs[1]; + + staticbufs[0] = IPC_Desc_StaticBuffer(0x280,0); + staticbufs[1] = (u32)out; + + Result ret=0; + ret=svcSendSyncRequest(__uds_servhandle); + + staticbufs[0] = saved_threadstorage[0]; + staticbufs[1] = saved_threadstorage[1]; + + if(R_FAILED(ret))return ret; + + return cmdbuf[1]; +} + static Result udsipc_SetProbeResponseParam(u32 oui, s8 data) { u32* cmdbuf=getThreadCommandBuffer();