diff --git a/nx/include/switch/services/btdrv.h b/nx/include/switch/services/btdrv.h index 49e38b57..694ecf4f 100644 --- a/nx/include/switch/services/btdrv.h +++ b/nx/include/switch/services/btdrv.h @@ -225,6 +225,15 @@ typedef struct { } data; } BtdrvHidReportEventInfoBufferData; +/// Data for \ref btdrvGetAudioEventInfo. The data stored here depends on the \ref BtdrvAudioEventType. +typedef union { + struct { + u32 status; ///< Status: 0 = AV connection closed, 1 = AV connection opened, 2 = failed to open AV connection. + BtdrvAddress addr; ///< Device address. + u8 pad[2]; ///< Padding + } connection; ///< ::BtdrvAudioEventType_Connection +} BtdrvAudioEventInfo; + /// CircularBuffer typedef struct { Mutex mutex; @@ -1083,6 +1092,163 @@ Result btdrvSetBleScanParameter(u16 unk0, u16 unk1); */ Result btdrvMoveToSecondaryPiconet(BtdrvAddress addr); +/** + * @brief IsBluetoothEnabled + * @note Only available on [12.0.0+]. + * @param[out] out Output flag. + */ +Result btdrvIsBluetoothEnabled(bool *out); + +/** + * @brief AcquireAudioEvent + * @note Only available on [12.0.0+]. + * @param[out] out_event Output Event. + * @param[in] autoclear Event autoclear. + */ +Result btdrvAcquireAudioEvent(Event* out_event, bool autoclear); + +/** + * @brief GetAudioEventInfo + * @note Only available on [12.0.0+]. + * @param[out] buffer Output buffer, see \ref BtdrvAudioEventInfo. + * @param[in] size Output buffer size. + * @param[out] type \ref BtdrvAudioEventType. + */ +Result btdrvGetAudioEventInfo(void* buffer, size_t size, BtdrvAudioEventType *type); + +/** + * @brief OpenAudioConnection + * @note Only available on [12.0.0+]. + * @param[in] addr \ref BtdrvAddress + */ +Result btdrvOpenAudioConnection(BtdrvAddress addr); + +/** + * @brief CloseAudioConnection + * @note Only available on [12.0.0+]. + * @param[in] addr \ref BtdrvAddress + */ +Result btdrvCloseAudioConnection(BtdrvAddress addr); + +/** + * @brief OpenAudioOut + * @note Only available on [12.0.0+]. + * @param[in] addr \ref BtdrvAddress + * @param[out] audio_handle Audio handle. + */ +Result btdrvOpenAudioOut(BtdrvAddress addr, u32 *audio_handle); + +/** + * @brief CloseAudioOut + * @note Only available on [12.0.0+]. + * @param[in] audio_handle Audio handle from \ref btdrvOpenAudioOut. + */ +Result btdrvCloseAudioOut(u32 audio_handle); + +/** + * @brief StartAudioOut + * @note Only available on [12.0.0+]. + * @param[in] audio_handle Audio handle from \ref btdrvOpenAudioOut. + * @param[in] pcm_param \ref BtdrvPcmParameter + * @param[in] in_latency Input latency in nanoseconds. + * @param[out] out_latency Output latency in nanoseconds. + * @param[out] out1 Unknown output. + */ +Result btdrvStartAudioOut(u32 audio_handle, const BtdrvPcmParameter *pcm_param, s64 in_latency, s64 *out_latency, u64 *out1); + +/** + * @brief StopAudioOut + * @note Only available on [12.0.0+]. + * @param[in] audio_handle Audio handle from \ref btdrvOpenAudioOut. + */ +Result btdrvStopAudioOut(u32 audio_handle); + +/** + * @brief GetAudioOutState + * @note Only available on [12.0.0+]. + * @param[in] audio_handle Audio handle from \ref btdrvOpenAudioOut. + * @param[out] out \ref BtdrvAudioOutState + */ +Result btdrvGetAudioOutState(u32 audio_handle, BtdrvAudioOutState *out); + +/** + * @brief GetAudioOutFeedingCodec + * @note Only available on [12.0.0+]. + * @param[in] audio_handle Audio handle from \ref btdrvOpenAudioOut. + * @param[out] out \ref BtdrvAudioCodec + */ +Result btdrvGetAudioOutFeedingCodec(u32 audio_handle, BtdrvAudioCodec *out); + +/** + * @brief GetAudioOutFeedingParameter + * @note Only available on [12.0.0+]. + * @param[in] audio_handle Audio handle from \ref btdrvOpenAudioOut. + * @param[out] out \ref BtdrvPcmParameter + */ +Result btdrvGetAudioOutFeedingParameter(u32 audio_handle, BtdrvPcmParameter *out); + +/** + * @brief AcquireAudioOutStateChangedEvent + * @note Only available on [12.0.0+]. + * @param[in] audio_handle Audio handle from \ref btdrvOpenAudioOut. + * @param[out] out_event Output Event. + * @param[in] autoclear Event autoclear. + */ +Result btdrvAcquireAudioOutStateChangedEvent(u32 audio_handle, Event* out_event, bool autoclear); + +/** + * @brief AcquireAudioOutBufferAvailableEvent + * @note Only available on [12.0.0+]. + * @param[in] audio_handle Audio handle from \ref btdrvOpenAudioOut. + * @param[out] out_event Output Event. + * @param[in] autoclear Event autoclear. + */ +Result btdrvAcquireAudioOutBufferAvailableEvent(u32 audio_handle, Event* out_event, bool autoclear); + +/** + * @brief SendAudioData + * @note Only available on [12.0.0+]. + * @param[in] audio_handle Audio handle from \ref btdrvOpenAudioOut. + * @param[in] buffer Input buffer. + * @param[in] size Input buffer size. + * @param[out] Output transferred size. This is always either 0 (error occured) or the buffer size. + */ +Result btdrvSendAudioData(u32 audio_handle, const void* buffer, size_t size, u64 *transferred_size); + +/** + * @brief AcquireAudioControlInputStateChangedEvent + * @note Only available on [12.0.0+]. + * @param[out] out_event Output Event. + * @param[in] autoclear Event autoclear. + */ +Result btdrvAcquireAudioControlInputStateChangedEvent(Event* out_event, bool autoclear); + +/** + * @brief GetAudioControlInputState + * @note Only available on [12.0.0+]. + * @param[out] states Output array of \ref BtdrvAudioControlButtonState. + * @param[in] count Size of the states array in entries, the maximum is 0xF. + * @param[out] total_out Total output entries. + */ +Result btdrvGetAudioControlInputState(BtdrvAudioControlButtonState *states, s32 count, s32 *total_out); + +/** + * @brief AcquireAudioConnectionStateChangedEvent + * @note Only available on [12.0.0+]. + * @param[out] out_event Output Event. + * @param[in] autoclear Event autoclear. + */ +Result btdrvAcquireAudioConnectionStateChangedEvent(Event* out_event, bool autoclear); + +/** + * @brief GetConnectedAudioDevice + * @note Only available on [12.0.0+]. + * @param[out] addrs Output array of \ref BtdrvAddress. + * @param[in] count Size of the addrs array in entries, the maximum is 0x8. + * @param[out] total_out Total output entries. + */ +Result btdrvGetConnectedAudioDevice(BtdrvAddress *addrs, s32 count, s32 *total_out); + /** * @brief IsManufacturingMode * @note Only available on [5.0.0+]. diff --git a/nx/include/switch/services/btdrv_types.h b/nx/include/switch/services/btdrv_types.h index cb68b2fe..2feed997 100644 --- a/nx/include/switch/services/btdrv_types.h +++ b/nx/include/switch/services/btdrv_types.h @@ -73,6 +73,23 @@ typedef enum { BtdrvFatalReason_Enable = 7, ///< Only for \ref BtdrvEventInfo: triggered after enabling bluetooth, depending on the value of a global state field. } BtdrvFatalReason; +/// AudioEventType +typedef enum { + BtdrvAudioEventType_None = 0, ///< None + BtdrvAudioEventType_Connection = 1, ///< Connection +} BtdrvAudioEventType; + +/// AudioOutState +typedef enum { + BtdrvAudioOutState_Stopped = 0, ///< Stopped + BtdrvAudioOutState_Started = 1, ///< Started +} BtdrvAudioOutState; + +/// AudioCodec +typedef enum { + BtdrvAudioCodec_Pcm = 0, ///< Raw PCM +} BtdrvAudioCodec; + /// Address typedef struct { u8 address[0x6]; ///< Address @@ -220,3 +237,15 @@ typedef struct { u8 data[0x200]; ///< BtdrvLeEventInfo::data } BtdrvBleClientGattOperationInfo; +/// PcmParameter +typedef struct { + u32 unk_x0; ///< Must be 0-3. Controls number of channels: 0 = mono, non-zero = stereo. + s32 sample_rate; ///< Sample rate. Must be one of the following: 16000, 32000, 44100, 48000. + u32 bits_per_sample; ///< Bits per sample. Must be 8 or 16. +} BtdrvPcmParameter; + +/// AudioControlButtonState +typedef struct { + u8 unk_x0[0x10]; ///< Unknown +} BtdrvAudioControlButtonState; + diff --git a/nx/source/services/btdrv.c b/nx/source/services/btdrv.c index 56118dd3..191d8b95 100644 --- a/nx/source/services/btdrv.c +++ b/nx/source/services/btdrv.c @@ -99,6 +99,10 @@ static Result _btdrvCmdNoInOutBool(bool *out, u32 cmd_id) { return rc; } +static Result _btdrvCmdInU32OutU32(u32 inval, u32 *out, u32 cmd_id) { + return serviceDispatchInOut(&g_btdrvSrv, cmd_id, inval, *out); +} + static Result _btdrvCmdGetEvent(Event* out_event, bool autoclear, u32 cmd_id) { Handle tmp_handle = INVALID_HANDLE; Result rc = 0; @@ -108,6 +112,18 @@ static Result _btdrvCmdGetEvent(Event* out_event, bool autoclear, u32 cmd_id) { return rc; } +static Result _btdrvCmdInU32OutEvent(u32 inval, Event* out_event, bool autoclear, u32 cmd_id) { + Handle tmp_handle = INVALID_HANDLE; + + Result rc = serviceDispatchIn(&g_btdrvSrv, cmd_id, inval, + .out_handle_attrs = { SfOutHandleAttr_HipcCopy }, + .out_handles = &tmp_handle, + ); + + if (R_SUCCEEDED(rc)) eventLoadRemote(out_event, tmp_handle, autoclear); + return rc; +} + static Result _btdrvCmdInBufPtrFixed(const void* buffer, size_t size, u32 cmd_id) { return serviceDispatch(&g_btdrvSrv, cmd_id, .buffer_attrs = { SfBufferAttr_HipcPointer | SfBufferAttr_In | SfBufferAttr_FixedSize }, @@ -1125,6 +1141,167 @@ Result btdrvMoveToSecondaryPiconet(BtdrvAddress addr) { return _btdrvCmdInAddrNoOut(addr, 99); } +Result btdrvIsBluetoothEnabled(bool *out) { + if (hosversionBefore(12,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + + return _btdrvCmdNoInOutBool(out, 100); +} + +Result btdrvAcquireAudioEvent(Event* out_event, bool autoclear) { + if (hosversionBefore(12,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + + return _btdrvCmdGetEvent(out_event, autoclear, 128); +} + +Result btdrvGetAudioEventInfo(void* buffer, size_t size, BtdrvAudioEventType *type) { + if (hosversionBefore(12,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + + u32 tmp=0; + Result rc = _btdrvCmdOutU32OutBuf(buffer, size, &tmp, 129); + if (R_SUCCEEDED(rc) && type) *type = tmp; + return rc; +} + +Result btdrvOpenAudioConnection(BtdrvAddress addr) { + if (hosversionBefore(12,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + + return _btdrvCmdInAddrNoOut(addr, 130); +} + +Result btdrvCloseAudioConnection(BtdrvAddress addr) { + if (hosversionBefore(12,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + + return _btdrvCmdInAddrNoOut(addr, 131); +} + +Result btdrvOpenAudioOut(BtdrvAddress addr, u32 *audio_handle) { + if (hosversionBefore(12,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + + return serviceDispatchInOut(&g_btdrvSrv, 132, addr, *audio_handle); +} + +Result btdrvCloseAudioOut(u32 audio_handle) { + if (hosversionBefore(12,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + + return _btdrvCmdInU32NoOut(audio_handle, 133); +} + +Result btdrvAcquireAudioOutStateChangedEvent(u32 audio_handle, Event* out_event, bool autoclear) { + if (hosversionBefore(12,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + + return _btdrvCmdInU32OutEvent(audio_handle, out_event, autoclear, 134); +} + +Result btdrvStartAudioOut(u32 audio_handle, const BtdrvPcmParameter *pcm_param, s64 in_latency, s64 *out_latency, u64 *out1) { + if (hosversionBefore(12,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + + const struct { + u32 audio_handle; + BtdrvPcmParameter pcm_param; + s64 latency; + } in = { audio_handle, *pcm_param, in_latency }; + + struct { + s64 latency; + u64 out1; + } out; + + Result rc = serviceDispatchInOut(&g_btdrvSrv, 135, in, out); + if (R_SUCCEEDED(rc)) { + if (out_latency) *out_latency = out.latency; + if (out1) *out1 = out.out1; + } + return rc; +} + +Result btdrvStopAudioOut(u32 audio_handle) { + if (hosversionBefore(12,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + + return _btdrvCmdInU32NoOut(audio_handle, 136); +} + +Result btdrvGetAudioOutState(u32 audio_handle, BtdrvAudioOutState *out) { + if (hosversionBefore(12,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + + u32 tmp=0; + Result rc = _btdrvCmdInU32OutU32(audio_handle, &tmp, 137); + if (R_SUCCEEDED(rc) && out) *out = tmp; + return rc; +} + +Result btdrvGetAudioOutFeedingCodec(u32 audio_handle, BtdrvAudioCodec *out) { + if (hosversionBefore(12,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + + u32 tmp=0; + Result rc = _btdrvCmdInU32OutU32(audio_handle, &tmp, 138); + if (R_SUCCEEDED(rc) && out) *out = tmp; + return rc; +} + +Result btdrvGetAudioOutFeedingParameter(u32 audio_handle, BtdrvPcmParameter *out) { + if (hosversionBefore(12,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + + return serviceDispatchInOut(&g_btdrvSrv, 139, audio_handle, *out); +} + +Result btdrvAcquireAudioOutBufferAvailableEvent(u32 audio_handle, Event* out_event, bool autoclear) { + if (hosversionBefore(12,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + + return _btdrvCmdInU32OutEvent(audio_handle, out_event, autoclear, 140); +} + +Result btdrvSendAudioData(u32 audio_handle, const void* buffer, size_t size, u64 *transferred_size) { + if (hosversionBefore(12,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + + return serviceDispatchInOut(&g_btdrvSrv, 141, audio_handle, *transferred_size, + .buffer_attrs = { SfBufferAttr_HipcPointer | SfBufferAttr_In }, + .buffers = { { buffer, size } }, + ); +} + +Result btdrvAcquireAudioControlInputStateChangedEvent(Event* out_event, bool autoclear) { + if (hosversionBefore(12,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + + return _btdrvCmdGetEvent(out_event, autoclear, 142); +} + +Result btdrvGetAudioControlInputState(BtdrvAudioControlButtonState *states, s32 count, s32 *total_out) { + if (hosversionBefore(12,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + + return _btdrvCmdOutU32OutBuf(states, count*sizeof(BtdrvAudioControlButtonState), (u32*)total_out, 143); +} + +Result btdrvAcquireAudioConnectionStateChangedEvent(Event* out_event, bool autoclear) { + if (hosversionBefore(12,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + + return _btdrvCmdGetEvent(out_event, autoclear, 144); +} + +Result btdrvGetConnectedAudioDevice(BtdrvAddress *addrs, s32 count, s32 *total_out) { + if (hosversionBefore(12,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + + return _btdrvCmdOutU32OutBuf(addrs, count*sizeof(BtdrvAddress), (u32*)total_out, 145); +} + Result btdrvIsManufacturingMode(bool *out) { if (hosversionBefore(5,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);