Added svcGetSystemTick. Added LIBNX_PARCEL_ERRBASE to result.h. Added BINDER_FIRST_CALL_TRANSACTION. Added gfxproducer and parcel. Use gfxproducer in gfx and added gfxSwapBuffers().

This commit is contained in:
yellows8 2017-11-07 13:56:57 -05:00
parent b5abd52b8b
commit 1d8c51f6fa
11 changed files with 459 additions and 1 deletions

View File

@ -32,6 +32,8 @@ extern "C" {
#include <switch/services/vi.h>
#include <switch/gfx/gfx.h>
#include <switch/gfx/parcel.h>
#include <switch/gfx/gfxproducer.h>
#ifdef __cplusplus
}

View File

@ -2,3 +2,4 @@
void gfxInitDefault(void);
void gfxExit(void);
void gfxWaitForVsync();
void gfxSwapBuffers();

View File

@ -0,0 +1,9 @@
Result gfxproducerInitialize(binderSession *session);
void gfxproducerExit();
Result gfxproducerRequestBuffer(s32 bufferIdx);
Result gfxproducerDequeueBuffer(bool async, u32 width, u32 height, s32 format, u32 usage);
Result gfxproducerQueueBuffer(s32 buf, u8 input[0x5c]);
Result gfxproducerQuery(s32 what, s32* value);
Result gfxproducerConnect(s32 api, bool producerControlledByApp);
Result gfxproducerBufferInit(s32 buf, u8 input[0x178]);

View File

@ -0,0 +1,28 @@
#include <string.h>
#include <switch.h>
typedef struct {
u8 ParcelData[0x400];
u32 ParcelData_maxsize;
u32 ParcelData_size;
u32 ParcelData_pos;
u8 *ParcelObjects;
u32 ParcelObjectsSize;
} parcelContext;
void parcelInitializeContext(parcelContext *ctx);
Result parcelTransact(binderSession *session, u32 code, parcelContext *in_parcel, parcelContext *out_parcel);
void* parcelWriteData(parcelContext *ctx, void* data, size_t data_size);
void* parcelReadData(parcelContext *ctx, void* data, size_t data_size);
void parcelWriteInt32(parcelContext *ctx, s32 val);
void parcelWriteUInt32(parcelContext *ctx, u32 val);
void parcelWriteString16_fromchar(parcelContext *ctx, const char *str);
void parcelWriteInterfaceToken(parcelContext *ctx, const char *interface);
s32 parcelReadInt32(parcelContext *ctx);
u32 parcelReadUInt32(parcelContext *ctx);

View File

@ -29,3 +29,4 @@
#define LIBNX_NOTFOUND 8
#define LIBNX_IOERROR 9
#define LIBNX_BADINPUT 10
#define LIBNX_PARCEL_ERRBASE 100

View File

@ -1,3 +1,5 @@
#define BINDER_FIRST_CALL_TRANSACTION 0x1
typedef struct {
bool initialized;
Handle sessionhandle;

View File

@ -45,6 +45,7 @@ Result svcWaitSynchronization(s32* index, const Handle* handles, s32 handleCount
Result svcArbitrateLock(u32 wait_tag, u32* tag_location, u32 self_tag);
Result svcArbitrateUnlock(u32* tag_location);
Result svcConnectToNamedPort(Handle* session, const char* name);
u64 svcGetSystemTick(void);
Result svcSendSyncRequest(Handle session);
Result svcBreak(u32 breakReason, u64 inval1, u64 inval2);
Result svcGetInfo(u64* out, u64 id0, Handle handle, u64 id1);

View File

@ -9,6 +9,50 @@ static u8 g_gfxNativeWindow[0x100];
static u64 g_gfxNativeWindow_Size;
static s32 g_gfxNativeWindow_ID;
static binderSession g_gfxBinderSession;
static s32 g_gfxCurrentBuffer = 0;
static u32 g_gfxQueueBufferData[0x5c>>2] = {
0x54, 0x0,
0x0, 0x0, //u64 timestamp
0x1, 0x0, 0x0,
0x0, 0x0, 0x0, 0x2,
0x0, 0x0, 0x1, 0x1,
0x42,
0x13f4, //Increased by 6/7 each time.
0xffffffff, 0x0,
0xffffffff, 0x0, 0xffffffff, 0x0};
static u32 g_gfxBufferInitData[0x178>>2] = {
0x1, 0x16c, 0x0,
0x47424652,
1280, 720,
1280,
0x1, 0xb00, 0x2a, 0x0,
0x0, 0x51, 0xffffffff, 0xcb8,
0x0, 0xdaffcaff, 0x2a, 0x0,
0xb00, 0x1, 0x1, 1280,
0x3c0000, 0x1, 0x0, 1280,
720, 0x532120, 0x1, 0x3,
0x1400, 0xcb8,
0x0,
0xfe,
0x4, 0x0, 0x0, 0x0,
0x0, 0x3c0000, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
0x0,
0x0, 0x0 //Unknown, some timestamp perhaps?
};
static Result _gfxGetNativeWindowID(u8 *buf, u64 size, s32 *out_ID) {
u32 *bufptr = (u32*)buf;
@ -25,13 +69,38 @@ static Result _gfxGetNativeWindowID(u8 *buf, u64 size, s32 *out_ID) {
return 0;
}
static Result _gfxDequeueBuffer() {
return gfxproducerDequeueBuffer(1, 1280, 720, 0, 0x300);
}
static Result _gfxQueueBuffer(s32 buf) {
Result rc=0;
u64 *ptr64 = (u64*)&g_gfxQueueBufferData;
ptr64[1] = svcGetSystemTick();//Unknown what is actually used for timestamp, but shouldn't(?) matter.
rc = gfxproducerQueueBuffer(buf, (u8*)g_gfxQueueBufferData);
if (R_FAILED(rc)) return rc;
if(buf==0) {
g_gfxQueueBufferData[0x10]+= 0x6;
} else {
g_gfxQueueBufferData[0x10]+= 0x7;
}
return rc;
}
static Result _gfxInit(viServiceType servicetype, const char *DisplayName, u32 LayerFlags, u64 LayerId) {
Result rc=0;
s32 tmp=0;
u32 i=0;
u64 *ptr64 = (u64*)g_gfxQueueBufferData;
if(g_gfxInitialized)return 0;
g_gfxNativeWindow_ID = 0;
g_gfxDisplayVsyncEvent = INVALID_HANDLE;
g_gfxCurrentBuffer = 0;
rc = viInitialize(servicetype);
if (R_FAILED(rc)) return rc;
@ -51,7 +120,36 @@ static Result _gfxInit(viServiceType servicetype, const char *DisplayName, u32 L
rc = binderInitSession(&g_gfxBinderSession, 0x0f);
}
//TODO: Send binder parcels.
if (R_SUCCEEDED(rc)) rc = gfxproducerInitialize(&g_gfxBinderSession);
if (R_SUCCEEDED(rc)) rc = gfxproducerConnect(2, 0);
if (R_SUCCEEDED(rc)) rc = gfxproducerQuery(2, &tmp);//"NATIVE_WINDOW_FORMAT"
if (R_SUCCEEDED(rc)) {
for(i=0; i<2; i++) {
g_gfxBufferInitData[0xa] = i;
g_gfxBufferInitData[0x20] = 0x3c0000*i;
ptr64[0x170>>3] = svcGetSystemTick();
rc = gfxproducerBufferInit(0, (u8*)g_gfxBufferInitData);
if (R_FAILED(rc)) break;
}
}
if (R_SUCCEEDED(rc)) {
for(i=0; i<2; i++) {
rc = _gfxDequeueBuffer();
if (R_FAILED(rc)) break;
rc = gfxproducerRequestBuffer(i);
if (R_FAILED(rc)) break;
rc = _gfxQueueBuffer(i);
if (R_FAILED(rc)) break;
}
}
if (R_SUCCEEDED(rc)) rc = _gfxDequeueBuffer();
if (R_FAILED(rc)) {
binderExitSession(&g_gfxBinderSession);
@ -73,6 +171,8 @@ void gfxInitDefault(void) {
void gfxExit(void) {
if(!g_gfxInitialized)return;
gfxproducerExit();
binderExitSession(&g_gfxBinderSession);
viCloseLayer(&g_gfxLayer);
@ -96,3 +196,14 @@ void gfxWaitForVsync() {
svcWaitSynchronization(&tmpindex, &g_gfxDisplayVsyncEvent, 1, U64_MAX);
}
void gfxSwapBuffers() {
Result rc=0;
rc = _gfxQueueBuffer(g_gfxCurrentBuffer);
g_gfxCurrentBuffer ^= 1;
if (R_SUCCEEDED(rc)) rc = _gfxDequeueBuffer();
if (R_FAILED(rc)) fatalSimple(rc);
}

175
nx/source/gfx/gfxproducer.c Normal file
View File

@ -0,0 +1,175 @@
#include <string.h>
#include <switch.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 {
REQUEST_BUFFER = BINDER_FIRST_CALL_TRANSACTION, //0x1
SET_BUFFER_COUNT, //0x2
DEQUEUE_BUFFER, //0x3
DETACH_BUFFER, //0x4
DETACH_NEXT_BUFFER, //0x5
ATTACH_BUFFER, //0x6
QUEUE_BUFFER, //0x7
CANCEL_BUFFER, //0x8
QUERY, //0x9
CONNECT, //0xA
DISCONNECT, //0xB
SET_SIDEBAND_STREAM, //0xC
ALLOCATE_BUFFERS, //0xD
};
static char _gfxproducer_InterfaceDescriptor[] = "android.gui.IGraphicBufferProducer";
static binderSession *g_gfxproducerBinderSession;
Result gfxproducerInitialize(binderSession *session) {
g_gfxproducerBinderSession = session;
return 0;
}
void gfxproducerExit() {
g_gfxproducerBinderSession = NULL;
}
Result gfxproducerRequestBuffer(s32 bufferIdx) {
Result rc;
parcelContext parcel, parcel_reply;
if (g_gfxproducerBinderSession==NULL) return MAKERESULT(MODULE_LIBNX, LIBNX_NOTINITIALIZED);
parcelInitializeContext(&parcel);
parcelInitializeContext(&parcel_reply);
parcelWriteInterfaceToken(&parcel, _gfxproducer_InterfaceDescriptor);
parcelWriteInt32(&parcel, bufferIdx);
rc = parcelTransact(g_gfxproducerBinderSession, REQUEST_BUFFER, &parcel, &parcel_reply);
if (R_FAILED(rc)) return rc;
//TODO: parse reply
return 0;
}
Result gfxproducerDequeueBuffer(bool async, u32 width, u32 height, s32 format, u32 usage) {
Result rc;
parcelContext parcel, parcel_reply;
if (g_gfxproducerBinderSession==NULL) return MAKERESULT(MODULE_LIBNX, LIBNX_NOTINITIALIZED);
parcelInitializeContext(&parcel);
parcelInitializeContext(&parcel_reply);
parcelWriteInterfaceToken(&parcel, _gfxproducer_InterfaceDescriptor);
parcelWriteInt32(&parcel, async);
parcelWriteUInt32(&parcel, width);
parcelWriteUInt32(&parcel, height);
parcelWriteInt32(&parcel, format);
parcelWriteUInt32(&parcel, usage);
rc = parcelTransact(g_gfxproducerBinderSession, DEQUEUE_BUFFER, &parcel, &parcel_reply);
if (R_FAILED(rc)) return rc;
//TODO: parse reply
return 0;
}
Result gfxproducerQueueBuffer(s32 buf, u8 input[0x5c]) {
Result rc;
parcelContext parcel, parcel_reply;
if (g_gfxproducerBinderSession==NULL) return MAKERESULT(MODULE_LIBNX, LIBNX_NOTINITIALIZED);
parcelInitializeContext(&parcel);
parcelInitializeContext(&parcel_reply);
parcelWriteInterfaceToken(&parcel, _gfxproducer_InterfaceDescriptor);
parcelWriteInt32(&parcel, buf);
parcelWriteData(&parcel, input, 0x5c);
rc = parcelTransact(g_gfxproducerBinderSession, DEQUEUE_BUFFER, &parcel, &parcel_reply);
if (R_FAILED(rc)) return rc;
//TODO: parse reply
return 0;
}
Result gfxproducerQuery(s32 what, s32* value) {
Result rc;
parcelContext parcel, parcel_reply;
if (g_gfxproducerBinderSession==NULL) return MAKERESULT(MODULE_LIBNX, LIBNX_NOTINITIALIZED);
parcelInitializeContext(&parcel);
parcelInitializeContext(&parcel_reply);
parcelWriteInterfaceToken(&parcel, _gfxproducer_InterfaceDescriptor);
parcelWriteInt32(&parcel, what);
rc = parcelTransact(g_gfxproducerBinderSession, QUERY, &parcel, &parcel_reply);
if (R_FAILED(rc)) return rc;
*value = parcelReadInt32(&parcel_reply);
rc = parcelReadInt32(&parcel_reply);
if (rc) rc = MAKERESULT(MODULE_LIBNX, LIBNX_PARCEL_ERRBASE);
return rc;
}
Result gfxproducerConnect(s32 api, bool producerControlledByApp) {
Result rc;
parcelContext parcel, parcel_reply;
if (g_gfxproducerBinderSession==NULL) return MAKERESULT(MODULE_LIBNX, LIBNX_NOTINITIALIZED);
parcelInitializeContext(&parcel);
parcelInitializeContext(&parcel_reply);
parcelWriteInterfaceToken(&parcel, _gfxproducer_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_gfxproducerBinderSession, CONNECT, &parcel, &parcel_reply);
if (R_FAILED(rc)) return rc;
//TODO: parse reply
return 0;
}
//Unknown what this is.
Result gfxproducerBufferInit(s32 buf, u8 input[0x178]) {
Result rc;
parcelContext parcel, parcel_reply;
if (g_gfxproducerBinderSession==NULL) return MAKERESULT(MODULE_LIBNX, LIBNX_NOTINITIALIZED);
parcelInitializeContext(&parcel);
parcelInitializeContext(&parcel_reply);
parcelWriteInterfaceToken(&parcel, _gfxproducer_InterfaceDescriptor);
parcelWriteInt32(&parcel, buf);
parcelWriteData(&parcel, input, 0x178);
rc = parcelTransact(g_gfxproducerBinderSession, 0xE, &parcel, &parcel_reply);
if (R_FAILED(rc)) return rc;
//TODO: parse reply
return 0;
}

123
nx/source/gfx/parcel.c Normal file
View File

@ -0,0 +1,123 @@
#include <string.h>
#include <switch.h>
//This implements Android Parcel, hence names etc here are based on Android Parcel.cpp.
/*u8 parcel_reply_log[0x10000] = {0};
size_t parcel_reply_log_size = 0;*/
void parcelInitializeContext(parcelContext *ctx) {
memset(ctx, 0, sizeof(parcelContext));
ctx->ParcelData_maxsize = sizeof(ctx->ParcelData);
}
//outparcel is allzero with this. This is presumably invalid?
Result parcelTransact(binderSession *session, u32 code, parcelContext *in_parcel, parcelContext *out_parcel) {
Result rc=0;
u8 inparcel[0x400];
static u8 outparcel[0x1000];
size_t outparcel_size = 0x1000;
u32 *inparcel32 = (u32*)inparcel;
u32 *outparcel32 = (u32*)outparcel;
u32 ParcelDataSize = in_parcel->ParcelData_size;
u32 ParcelObjectsSize = in_parcel->ParcelObjectsSize;
memset(inparcel, 0, sizeof(inparcel));
memset(outparcel, 0, outparcel_size);
if((size_t)ParcelDataSize >= sizeof(inparcel) || (size_t)ParcelObjectsSize >= sizeof(inparcel) || ((size_t)ParcelDataSize)+((size_t)ParcelObjectsSize)+0x10 >= sizeof(inparcel)) return MAKERESULT(MODULE_LIBNX, LIBNX_BADINPUT);
inparcel32[0] = ParcelDataSize;//ParcelDataSize
inparcel32[1] = 0x10;//ParcelDataOffset
inparcel32[2] = ParcelObjectsSize;//ParcelObjectsSize
inparcel32[3] = 0x10+ParcelDataSize;//ParcelObjectsOffset
if(in_parcel->ParcelData && ParcelDataSize)memcpy(&inparcel[inparcel32[1]], in_parcel->ParcelData, ParcelDataSize);
if(in_parcel->ParcelObjects && ParcelObjectsSize)memcpy(&inparcel[inparcel32[3]], in_parcel->ParcelObjects, ParcelObjectsSize);
rc = binderTransactParcel(session, code, inparcel, ParcelDataSize+ParcelObjectsSize+0x10, outparcel, outparcel_size, 0);
if (R_FAILED(rc)) return rc;
if((size_t)outparcel32[1] >= outparcel_size || ((size_t)outparcel32[0])+((size_t)outparcel32[1]) >= outparcel_size) return MAKERESULT(MODULE_LIBNX, LIBNX_BADINPUT);
if((size_t)outparcel32[2] >= outparcel_size || ((size_t)outparcel32[2])+((size_t)outparcel32[3]) >= outparcel_size) return MAKERESULT(MODULE_LIBNX, LIBNX_BADINPUT);
if((size_t)outparcel32[0] >= outparcel_size || (size_t)outparcel32[3] >= outparcel_size) return MAKERESULT(MODULE_LIBNX, LIBNX_BADINPUT);
memcpy(out_parcel->ParcelData, &outparcel[outparcel32[1]], outparcel32[0]);
out_parcel->ParcelData_size = outparcel32[0];
/*memcpy(&parcel_reply_log[parcel_reply_log_size], out_parcel, outparcel_size);
parcel_reply_log_size+= outparcel_size;*/
return 0;
}
void* parcelWriteData(parcelContext *ctx, void* data, size_t data_size) {
void* ptr = ctx->ParcelData;
if(data_size & BIT(31)) return NULL;
data_size = (data_size+3) & ~3;
if(ctx->ParcelData_size + data_size >= ctx->ParcelData_maxsize) return NULL;
if(data)memcpy(&ctx->ParcelData[ctx->ParcelData_size], data, data_size);
ctx->ParcelData_size+= data_size;
return ptr;
}
void* parcelReadData(parcelContext *ctx, void* data, size_t data_size) {
void* ptr = ctx->ParcelData;
size_t aligned_data_size;
if(data_size & BIT(31)) return NULL;
aligned_data_size = (data_size+3) & ~3;
if(ctx->ParcelData_pos + aligned_data_size >= ctx->ParcelData_size) return NULL;
if(data)memcpy(data, &ctx->ParcelData[ctx->ParcelData_pos], data_size);
ctx->ParcelData_pos+= aligned_data_size;
return ptr;
}
void parcelWriteInt32(parcelContext *ctx, s32 val) {
parcelWriteData(ctx, &val, sizeof(val));
}
void parcelWriteUInt32(parcelContext *ctx, u32 val) {
parcelWriteData(ctx, &val, sizeof(val));
}
void parcelWriteString16_fromchar(parcelContext *ctx, const char *str) {
u32 pos, len;
u16 *ptr;
len = strlen(str);
parcelWriteInt32(ctx, len);
len++;
ptr = parcelWriteData(ctx, NULL, len*2);
if(ptr==NULL)return;
for(pos=0; pos<len; pos++) {
ptr[pos] = (u16)str[pos];
}
}
void parcelWriteInterfaceToken(parcelContext *ctx, const char *interface) {
parcelWriteInt32(ctx, 0x100);
parcelWriteString16_fromchar(ctx, interface);
}
s32 parcelReadInt32(parcelContext *ctx) {
s32 val = 0;
parcelReadData(ctx, &val, sizeof(val));
return val;
}
u32 parcelReadUInt32(parcelContext *ctx) {
u32 val = 0;
parcelReadData(ctx, &val, sizeof(val));
return val;
}

View File

@ -118,6 +118,11 @@ SVC_BEGIN svcConnectToNamedPort
ret
SVC_END
SVC_BEGIN svcGetSystemTick
svc 0x1E
ret
SVC_END
SVC_BEGIN svcSendSyncRequest
svc 0x21
ret