#include #include "types.h" #include "result.h" #include "gfx/parcel.h" #include "gfx/buffer_producer.h" // This implements the version of Android IGraphicBufferProducer used by Switch. // Hence names/params etc here are based on Android IGraphicBufferProducer.cpp. // Based on an old version of the enum from the above .cpp. // Unknown whether all of these are correct for Switch. enum { /* 0x1 */ REQUEST_BUFFER = BINDER_FIRST_CALL_TRANSACTION, /* 0x2 */ SET_BUFFER_COUNT, /* 0x3 */ DEQUEUE_BUFFER, /* 0x4 */ DETACH_BUFFER, /* 0x5 */ DETACH_NEXT_BUFFER, /* 0x6 */ ATTACH_BUFFER, /* 0x7 */ QUEUE_BUFFER, /* 0x8 */ CANCEL_BUFFER, /* 0x9 */ QUERY, /* 0xA */ CONNECT, /* 0xB */ DISCONNECT, /* 0xC */ SET_SIDEBAND_STREAM, /* 0xD */ ALLOCATE_BUFFERS, /* 0xE */ GRAPHIC_BUFFER_INIT, // Custom Switch-specific command - unofficial name. }; static char g_bufferProducer_InterfaceDescriptor[] = "android.gui.IGraphicBufferProducer"; static Binder *g_bufferProducerBinderSession; Result bufferProducerInitialize(Binder *session) { g_bufferProducerBinderSession = session; return 0; } void bufferProducerExit(void) { g_bufferProducerBinderSession = NULL; } Result bufferProducerRequestBuffer(s32 bufferIdx, bufferProducerGraphicBuffer *buf) { Result rc; Parcel parcel, parcel_reply; if (g_bufferProducerBinderSession == NULL) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); parcelInitialize(&parcel); parcelInitialize(&parcel_reply); parcelWriteInterfaceToken(&parcel, g_bufferProducer_InterfaceDescriptor); parcelWriteInt32(&parcel, bufferIdx); rc = parcelTransact(g_bufferProducerBinderSession, REQUEST_BUFFER, &parcel, &parcel_reply); if (R_SUCCEEDED(rc)) { int nonNull = parcelReadInt32(&parcel_reply); if (nonNull != 0) { size_t tmpsize=0; void* tmp_ptr; tmp_ptr = parcelReadFlattenedObject(&parcel_reply, &tmpsize); if (tmp_ptr==NULL || tmpsize!=sizeof(bufferProducerGraphicBuffer)) return MAKERESULT(Module_Libnx, LibnxError_BadInput); if (buf) memcpy(buf, tmp_ptr, sizeof(bufferProducerGraphicBuffer)); } int status = parcelReadInt32(&parcel_reply); if (status != 0) { rc = MAKERESULT(Module_Libnx, LibnxError_BufferProducerError); } } return rc; } Result bufferProducerDequeueBuffer(bool async, u32 width, u32 height, s32 format, u32 usage, s32 *buf, bufferProducerFence *fence) { Result rc; Parcel parcel, parcel_reply; if (g_bufferProducerBinderSession == NULL) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); parcelInitialize(&parcel); parcelInitialize(&parcel_reply); parcelWriteInterfaceToken(&parcel, g_bufferProducer_InterfaceDescriptor); parcelWriteInt32(&parcel, async); parcelWriteUInt32(&parcel, width); parcelWriteUInt32(&parcel, height); parcelWriteInt32(&parcel, format); parcelWriteUInt32(&parcel, usage); rc = parcelTransact(g_bufferProducerBinderSession, DEQUEUE_BUFFER, &parcel, &parcel_reply); if (R_SUCCEEDED(rc)) { *buf = parcelReadInt32(&parcel_reply); if(parcelReadInt32(&parcel_reply)) { size_t tmpsize=0; void* tmp_ptr; tmp_ptr = parcelReadFlattenedObject(&parcel_reply, &tmpsize); if (tmp_ptr==NULL || tmpsize!=sizeof(bufferProducerFence)) return MAKERESULT(Module_Libnx, LibnxError_BadInput); if (fence) memcpy(fence, tmp_ptr, sizeof(bufferProducerFence)); } int result = parcelReadInt32(&parcel_reply); if (result != 0) rc = MAKERESULT(Module_Libnx, LibnxError_BufferProducerError); } return rc; } Result bufferProducerDetachBuffer(s32 slot) { Result rc; Parcel parcel, parcel_reply; if (g_bufferProducerBinderSession == NULL) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); parcelInitialize(&parcel); parcelInitialize(&parcel_reply); parcelWriteInterfaceToken(&parcel, g_bufferProducer_InterfaceDescriptor); parcelWriteInt32(&parcel, slot); rc = parcelTransact(g_bufferProducerBinderSession, DETACH_BUFFER, &parcel, &parcel_reply); if (R_SUCCEEDED(rc)) { //TODO: parse reply } return rc; } Result bufferProducerQueueBuffer(s32 buf, bufferProducerQueueBufferInput *input, bufferProducerQueueBufferOutput *output) { Result rc; Parcel parcel, parcel_reply; if (g_bufferProducerBinderSession == NULL) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); parcelInitialize(&parcel); parcelInitialize(&parcel_reply); parcelWriteInterfaceToken(&parcel, g_bufferProducer_InterfaceDescriptor); parcelWriteInt32(&parcel, buf); parcelWriteFlattenedObject(&parcel, input, sizeof(bufferProducerQueueBufferInput)); rc = parcelTransact(g_bufferProducerBinderSession, QUEUE_BUFFER, &parcel, &parcel_reply); if (R_SUCCEEDED(rc)) { if (parcelReadData(&parcel_reply, output, sizeof(bufferProducerQueueBufferOutput))==NULL) return MAKERESULT(Module_Libnx, LibnxError_BadInput); int result = parcelReadInt32(&parcel_reply); if (result != 0) rc = MAKERESULT(Module_Libnx, LibnxError_BufferProducerError); } return rc; } Result bufferProducerQuery(s32 what, s32* value) { Result rc; Parcel parcel, parcel_reply; if (g_bufferProducerBinderSession == NULL) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); parcelInitialize(&parcel); parcelInitialize(&parcel_reply); parcelWriteInterfaceToken(&parcel, g_bufferProducer_InterfaceDescriptor); parcelWriteInt32(&parcel, what); rc = parcelTransact(g_bufferProducerBinderSession, QUERY, &parcel, &parcel_reply); if (R_SUCCEEDED(rc)) { *value = parcelReadInt32(&parcel_reply); int result = parcelReadInt32(&parcel_reply); if (result != 0) rc = MAKERESULT(Module_Libnx, LibnxError_BufferProducerError); } return rc; } Result bufferProducerConnect(s32 api, bool producerControlledByApp, bufferProducerQueueBufferOutput *output) { Result rc; Parcel parcel, parcel_reply; if (g_bufferProducerBinderSession == NULL) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); parcelInitialize(&parcel); parcelInitialize(&parcel_reply); parcelWriteInterfaceToken(&parcel, g_bufferProducer_InterfaceDescriptor); // Hard-code this as if listener==NULL, since that's not known to be used officially. parcelWriteInt32(&parcel, 0); parcelWriteInt32(&parcel, api); parcelWriteInt32(&parcel, producerControlledByApp); rc = parcelTransact(g_bufferProducerBinderSession, CONNECT, &parcel, &parcel_reply); if (R_SUCCEEDED(rc)) { if (parcelReadData(&parcel_reply, output, sizeof(bufferProducerQueueBufferOutput))==NULL) return MAKERESULT(Module_Libnx, LibnxError_BadInput); int result = parcelReadInt32(&parcel_reply); if (result != 0) rc = MAKERESULT(Module_Libnx, LibnxError_BufferProducerError); } return rc; } Result bufferProducerDisconnect(s32 api) { Result rc; Parcel parcel, parcel_reply; if (g_bufferProducerBinderSession == NULL) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); parcelInitialize(&parcel); parcelInitialize(&parcel_reply); parcelWriteInterfaceToken(&parcel, g_bufferProducer_InterfaceDescriptor); parcelWriteInt32(&parcel, api); rc = parcelTransact(g_bufferProducerBinderSession, DISCONNECT, &parcel, &parcel_reply); if (R_SUCCEEDED(rc)) { //TODO: parse reply } return rc; } Result bufferProducerGraphicBufferInit(s32 buf, bufferProducerGraphicBuffer *input) { Result rc; Parcel parcel, parcel_reply; bool flag = 0; if (g_bufferProducerBinderSession==NULL) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); parcelInitialize(&parcel); parcelInitialize(&parcel_reply); parcelWriteInterfaceToken(&parcel, g_bufferProducer_InterfaceDescriptor); parcelWriteInt32(&parcel, buf); if (input!=NULL) flag = 1; parcelWriteInt32(&parcel, flag); if (flag) parcelWriteFlattenedObject(&parcel, input, sizeof(bufferProducerGraphicBuffer)); rc = parcelTransact(g_bufferProducerBinderSession, GRAPHIC_BUFFER_INIT, &parcel, &parcel_reply); if (R_SUCCEEDED(rc)) { int result = parcelReadInt32(&parcel_reply); if (result != 0) rc = MAKERESULT(Module_Libnx, LibnxError_BufferProducerError); } return rc; }