void aptEventHandler(void *arg) { bool runThread = true; while(runThread) { s32 syncedID = 0; svcWaitSynchronizationN(&syncedID, aptEvents, 3, 0, U64_MAX); svcClearEvent(aptEvents[syncedID]); switch(syncedID) { // Event 0 means we got a signal from NS (home button, power button etc). case 0x0: __handle_notification(); break; // Event 1 means we got an incoming parameter. case 0x1: runThread = __handle_incoming_parameter(); break; // Event 2 means we should exit the thread. case 0x2: runThread = false; break; } } }
int main(void) { Handle handles[10] = {0}; //notification handle + service handles MyThread receiverThread = {0}, senderThread = {0}, PXISRV11HandlerThread = {0}; for(u32 i = 0; i < 9; i++) assertSuccess(srvRegisterService(handles + 1 + i, serviceNames[i], 1)); assertSuccess(MyThread_Create(&receiverThread, receiver, receiverStack, THREAD_STACK_SIZE, 0x2D, -2)); assertSuccess(MyThread_Create(&senderThread, sender, senderStack, THREAD_STACK_SIZE, 0x2D, -2)); assertSuccess(MyThread_Create(&PXISRV11HandlerThread, PXISRV11Handler, PXISRV11HandlerStack, THREAD_STACK_SIZE, 0x2D, -2)); assertSuccess(srvEnableNotification(&handles[0])); while(!shouldTerminate) { s32 index = 0; assertSuccess(svcWaitSynchronizationN(&index, handles, 10, false, -1LL)); if(index == 0) { u32 notificationId; assertSuccess(srvReceiveNotification(¬ificationId)); if(notificationId == 0x100) shouldTerminate = true; } else { Handle session = 0; SessionData *data = &sessionManager.sessionData[index - 1]; assertSuccess(svcAcceptSession(&session, handles[index])); RecursiveLock_Lock(&sessionManager.senderLock); if(data->handle != 0) svcBreak(USERBREAK_PANIC); data->handle = session; assertSuccess(svcSignalEvent(sessionManager.sendAllBuffersToArm9Event)); RecursiveLock_Unlock(&sessionManager.senderLock); } } u32 PXIMC_OnPXITerminate = 0x10000; //TODO: see if this is correct sessionManager.sessionData[0].state = STATE_SENT_TO_ARM9; sendPXICmdbuf(NULL, 0, &PXIMC_OnPXITerminate); assertSuccess(MyThread_Join(&receiverThread, -1LL)); assertSuccess(MyThread_Join(&senderThread, -1LL)); assertSuccess(MyThread_Join(&PXISRV11HandlerThread, -1LL)); for(u32 i = 0; i < 10; i++) svcCloseHandle(handles[i]); return 0; }
static inline void initPXI(void) { Result res; Handle handles[2] = {0}; PXIReset(); if(PXISyncInterrupt != 0) svcBreak(USERBREAK_PANIC); //0xE0A0183B assertSuccess(svcCreateEvent(&PXISyncInterrupt, RESET_ONESHOT)); if(PXITransferMutex != 0) svcBreak(USERBREAK_PANIC); //0xE0A0183B assertSuccess(svcCreateMutex(&PXITransferMutex, false)); assertSuccess(svcCreateEvent(&handles[0], RESET_ONESHOT)); //receive FIFO not empty assertSuccess(svcCreateEvent(&handles[1], RESET_ONESHOT)); //send FIFO empty assertSuccess(bindPXIInterrupts(&PXISyncInterrupt, &handles[0], &handles[1])); s32 handleIndex; do { while(!PXIIsSendFIFOFull()) PXISendWord(0); res = assertSuccess(svcWaitSynchronization(handles[0], 0LL)); if(R_DESCRIPTION(res) == RD_TIMEOUT) assertSuccess(svcWaitSynchronizationN(&handleIndex, handles, 2, false, -1LL)); else handleIndex = 0; } while(handleIndex != 0); unbindPXIInterrupts(NULL, &handles[0], &handles[1]); PXISendByte(1); while(PXIReceiveByte() < 1); while (!PXIIsReceiveFIFOEmpty()) PXIReceiveWord(); PXISendByte(2); while(PXIReceiveByte() < 2); svcCloseHandle(handles[0]); svcCloseHandle(handles[1]); }
static void task_populate_titledb_thread(void* arg) { populate_titledb_data* data = (populate_titledb_data*) arg; Result res = 0; linked_list titles; linked_list_init(&titles); json_t* root = NULL; if(R_SUCCEEDED(res = http_download_json("https://api.titledb.com/v1/entry?nested=true" "&only=id&only=name&only=author&only=headline&only=category" "&only=cia.id&only=cia.mtime&only=cia.version&only=cia.size&only=cia.titleid" "&only=tdsx.id&only=tdsx.mtime&only=tdsx.version&only=tdsx.size&only=tdsx.smdh.id", &root, 1024 * 1024))) { if(json_is_array(root)) { for(u32 i = 0; i < json_array_size(root) && R_SUCCEEDED(res); i++) { svcWaitSynchronization(task_get_pause_event(), U64_MAX); if(task_is_quit_all() || svcWaitSynchronization(data->cancelEvent, 0) == 0) { break; } json_t* entry = json_array_get(root, i); if(json_is_object(entry)) { list_item* item = (list_item*) calloc(1, sizeof(list_item)); if(item != NULL) { titledb_info* titledbInfo = (titledb_info*) calloc(1, sizeof(titledb_info)); if(titledbInfo != NULL) { titledbInfo->id = (u32) json_object_get_integer(entry, "id", 0); string_copy(titledbInfo->category, json_object_get_string(entry, "category", "Unknown"), sizeof(titledbInfo->category)); string_copy(titledbInfo->meta.shortDescription, json_object_get_string(entry, "name", ""), sizeof(titledbInfo->meta.shortDescription)); string_copy(titledbInfo->meta.publisher, json_object_get_string(entry, "author", ""), sizeof(titledbInfo->meta.publisher)); json_t* headline = json_object_get(entry, "headline"); if(json_is_string(headline)) { const char* val = json_string_value(headline); if(json_string_length(headline) > sizeof(titledbInfo->headline) - 1) { snprintf(titledbInfo->headline, sizeof(titledbInfo->headline), "%.508s...", val); } else { string_copy(titledbInfo->headline, val, sizeof(titledbInfo->headline)); } } else { titledbInfo->headline[0] = '\0'; } json_t* cias = json_object_get(entry, "cia"); if(json_is_array(cias)) { for(u32 j = 0; j < json_array_size(cias); j++) { json_t* cia = json_array_get(cias, j); if(json_is_object(cia)) { const char* mtime = json_object_get_string(cia, "mtime", "Unknown"); if(!titledbInfo->cia.exists || task_populate_titledb_compare_dates(mtime, titledbInfo->cia.mtime, sizeof(titledbInfo->cia.mtime)) >= 0) { titledbInfo->cia.exists = true; titledbInfo->cia.id = (u32) json_object_get_integer(cia, "id", 0); string_copy(titledbInfo->cia.mtime, mtime, sizeof(titledbInfo->cia.mtime)); string_copy(titledbInfo->cia.version, json_object_get_string(cia, "version", "Unknown"), sizeof(titledbInfo->cia.version)); titledbInfo->cia.size = (u32) json_object_get_integer(cia, "size", 0); titledbInfo->cia.titleId = strtoull(json_object_get_string(cia, "titleid", "0"), NULL, 16); } } } } json_t* tdsxs = json_object_get(entry, "tdsx"); if(json_is_array(tdsxs)) { for(u32 j = 0; j < json_array_size(tdsxs); j++) { json_t* tdsx = json_array_get(tdsxs, j); if(json_is_object(tdsx)) { const char* mtime = json_object_get_string(tdsx, "mtime", "Unknown"); if(!titledbInfo->tdsx.exists || task_populate_titledb_compare_dates(mtime, titledbInfo->tdsx.mtime, sizeof(titledbInfo->tdsx.mtime)) >= 0) { titledbInfo->tdsx.exists = true; titledbInfo->tdsx.id = (u32) json_object_get_integer(tdsx, "id", 0); string_copy(titledbInfo->tdsx.mtime, mtime, sizeof(titledbInfo->tdsx.mtime)); string_copy(titledbInfo->tdsx.version, json_object_get_string(tdsx, "version", "Unknown"), sizeof(titledbInfo->tdsx.version)); titledbInfo->tdsx.size = (u32) json_object_get_integer(tdsx, "size", 0); json_t* smdh = json_object_get(tdsx, "smdh"); if(json_is_object(smdh)) { titledbInfo->tdsx.smdh.exists = true; titledbInfo->tdsx.smdh.id = (u32) json_object_get_integer(smdh, "id", 0); } } } } } char* latestTime = "Unknown"; if(titledbInfo->cia.exists && titledbInfo->tdsx.exists) { if(task_populate_titledb_compare_dates(titledbInfo->cia.mtime, titledbInfo->tdsx.mtime, sizeof(titledbInfo->cia.mtime)) >= 0) { latestTime = titledbInfo->cia.mtime; } else { latestTime = titledbInfo->tdsx.mtime; } } else if(titledbInfo->cia.exists) { latestTime = titledbInfo->cia.mtime; } else if(titledbInfo->tdsx.exists) { latestTime = titledbInfo->tdsx.mtime; } string_copy(titledbInfo->mtime, latestTime, sizeof(titledbInfo->mtime)); if((titledbInfo->cia.exists || titledbInfo->tdsx.exists) && (data->filter == NULL || data->filter(data->userData, titledbInfo))) { string_copy(item->name, titledbInfo->meta.shortDescription, LIST_ITEM_NAME_MAX); item->data = titledbInfo; task_populate_titledb_update_status(item); linked_list_add_sorted(&titles, item, data->userData, data->compare); } else { free(titledbInfo); free(item); } } else { free(item); res = R_APP_OUT_OF_MEMORY; } } else { res = R_APP_OUT_OF_MEMORY; } } } linked_list_iter iter; linked_list_iterate(&titles, &iter); while(linked_list_iter_has_next(&iter)) { list_item* item = linked_list_iter_next(&iter); if(R_SUCCEEDED(res)) { linked_list_add(data->items, item); } else { task_free_titledb(item); linked_list_iter_remove(&iter); } } } else { res = R_APP_BAD_DATA; } json_decref(root); } data->itemsListed = true; if(R_SUCCEEDED(res)) { linked_list_iter iter; linked_list_iterate(&titles, &iter); while(linked_list_iter_has_next(&iter)) { svcWaitSynchronization(task_get_pause_event(), U64_MAX); Handle events[2] = {data->resumeEvent, data->cancelEvent}; s32 index = 0; svcWaitSynchronizationN(&index, events, 2, false, U64_MAX); if(task_is_quit_all() || svcWaitSynchronization(data->cancelEvent, 0) == 0) { break; } list_item* item = (list_item*) linked_list_iter_next(&iter); titledb_info* titledbInfo = (titledb_info*) item->data; char url[128]; if(titledbInfo->cia.exists) { snprintf(url, sizeof(url), "https://3ds.titledb.com/v1/cia/%lu/icon_l.bin", titledbInfo->cia.id); } else if(titledbInfo->tdsx.exists && titledbInfo->tdsx.smdh.exists) { snprintf(url, sizeof(url), "https://3ds.titledb.com/v1/smdh/%lu/icon_l.bin", titledbInfo->tdsx.smdh.id); } else { continue; } u8 icon[0x1200]; u32 iconSize = 0; if(R_SUCCEEDED(http_download(url, &iconSize, &icon, sizeof(icon))) && iconSize == sizeof(icon)) { titledbInfo->meta.texture = screen_allocate_free_texture(); screen_load_texture_tiled(titledbInfo->meta.texture, icon, sizeof(icon), 48, 48, GPU_RGB565, false); } } } linked_list_destroy(&titles); svcCloseHandle(data->resumeEvent); svcCloseHandle(data->cancelEvent); data->result = res; data->finished = true; }
static void task_capture_cam_thread(void* arg) { capture_cam_data* data = (capture_cam_data*) arg; Handle events[EVENT_COUNT] = {0}; events[EVENT_CANCEL] = data->cancelEvent; Result res = 0; u32 bufferSize = data->width * data->height * sizeof(u16); u16* buffer = (u16*) calloc(1, bufferSize); if(buffer != NULL) { if(R_SUCCEEDED(res = camInit())) { u32 cam = data->camera == CAMERA_OUTER ? SELECT_OUT1 : SELECT_IN1; if(R_SUCCEEDED(res = CAMU_SetSize(cam, SIZE_CTR_TOP_LCD, CONTEXT_A)) && R_SUCCEEDED(res = CAMU_SetOutputFormat(cam, OUTPUT_RGB_565, CONTEXT_A)) && R_SUCCEEDED(res = CAMU_SetFrameRate(cam, FRAME_RATE_30)) && R_SUCCEEDED(res = CAMU_SetNoiseFilter(cam, true)) && R_SUCCEEDED(res = CAMU_SetAutoExposure(cam, true)) && R_SUCCEEDED(res = CAMU_SetAutoWhiteBalance(cam, true)) && R_SUCCEEDED(res = CAMU_Activate(cam))) { u32 transferUnit = 0; if(R_SUCCEEDED(res = CAMU_GetBufferErrorInterruptEvent(&events[EVENT_BUFFER_ERROR], PORT_CAM1)) && R_SUCCEEDED(res = CAMU_SetTrimming(PORT_CAM1, true)) && R_SUCCEEDED(res = CAMU_SetTrimmingParamsCenter(PORT_CAM1, data->width, data->height, 400, 240)) && R_SUCCEEDED(res = CAMU_GetMaxBytes(&transferUnit, data->width, data->height)) && R_SUCCEEDED(res = CAMU_SetTransferBytes(PORT_CAM1, transferUnit, data->width, data->height)) && R_SUCCEEDED(res = CAMU_ClearBuffer(PORT_CAM1)) && R_SUCCEEDED(res = CAMU_SetReceiving(&events[EVENT_RECV], buffer, PORT_CAM1, bufferSize, (s16) transferUnit)) && R_SUCCEEDED(res = CAMU_StartCapture(PORT_CAM1))) { bool cancelRequested = false; while(!task_is_quit_all() && !cancelRequested && R_SUCCEEDED(res)) { svcWaitSynchronization(task_get_pause_event(), U64_MAX); s32 index = 0; if(R_SUCCEEDED(res = svcWaitSynchronizationN(&index, events, EVENT_COUNT, false, U64_MAX))) { switch(index) { case EVENT_CANCEL: cancelRequested = true; break; case EVENT_RECV: svcCloseHandle(events[EVENT_RECV]); events[EVENT_RECV] = 0; svcWaitSynchronization(data->mutex, U64_MAX); memcpy(data->buffer, buffer, bufferSize); GSPGPU_FlushDataCache(data->buffer, bufferSize); svcReleaseMutex(data->mutex); res = CAMU_SetReceiving(&events[EVENT_RECV], buffer, PORT_CAM1, bufferSize, (s16) transferUnit); break; case EVENT_BUFFER_ERROR: svcCloseHandle(events[EVENT_RECV]); events[EVENT_RECV] = 0; if(R_SUCCEEDED(res = CAMU_ClearBuffer(PORT_CAM1)) && R_SUCCEEDED(res = CAMU_SetReceiving(&events[EVENT_RECV], buffer, PORT_CAM1, bufferSize, (s16) transferUnit))) { res = CAMU_StartCapture(PORT_CAM1); } break; default: break; } } } CAMU_StopCapture(PORT_CAM1); bool busy = false; while(R_SUCCEEDED(CAMU_IsBusy(&busy, PORT_CAM1)) && busy) { svcSleepThread(1000000); } CAMU_ClearBuffer(PORT_CAM1); } CAMU_Activate(SELECT_NONE); } camExit(); } free(buffer); } else { res = R_APP_OUT_OF_MEMORY; } for(int i = 0; i < EVENT_COUNT; i++) { if(events[i] != 0) { svcCloseHandle(events[i]); events[i] = 0; } } svcCloseHandle(data->mutex); data->result = res; data->finished = true; }