/*---------------------------------------------------------------------------*
  Project:  TwlSDK - FS - demos - async
  File:     main.c

  Copyright 2003-2008 Nintendo.  All rights reserved.

  These coded instructions, statements, and computer programs contain
  proprietary information of Nintendo of America Inc. and/or Nintendo
  Company Ltd., and are protected by Federal copyright law.  They may
  not be disclosed to third parties or copied or duplicated in any form,
  in whole or in part, without the prior written consent of Nintendo.

  $Date:: 2008-10-02 #$
  $Rev: 8827 $
  $Author: yosizaki $
 *---------------------------------------------------------------------------*/

#include <nitro.h>


// 非同期FS操作の動作テスト。

enum
{
    data_00,
    data_ff,
    data_inc,
    data_dec,
    datamode_max
};

#define	TASK_THREAD_MAX	4
#define	TASK_REPEAT_TIMES	1

typedef struct
{
    OSThread thread;
    u8      stack[4096] ATTRIBUTE_ALIGN(32);
    u8      buf[CARD_ROM_PAGE_SIZE] ATTRIBUTE_ALIGN(32);
    int     mode;
    u32     priority;
    u8      padding[24];
}
TaskThread;


static TaskThread task_thread[TASK_THREAD_MAX] ATTRIBUTE_ALIGN(32);
static u32 total_read_size = 0;

// ファイルを開いて読み込み続けるタスク。
static void AsyncAccessTask(void *p)
{
    TaskThread *const pt = (TaskThread *) p;
    int     i;
    int     repeat;
    int     count;
    int     total_size;
    BOOL    ret;
    FSFile  file;
    count = 0;
    repeat = 0;
    total_size = 0;
    for (;;)
    {
        switch (pt->mode)
        {
        case data_00:
            ret = FS_OpenFileEx(&file, "rom:/00.bin", FS_FILEMODE_R);
            break;
        case data_ff:
            ret = FS_OpenFileEx(&file, "rom:/ff.bin", FS_FILEMODE_R);
            break;
        case data_inc:
            ret = FS_OpenFileEx(&file, "rom:/inc.bin", FS_FILEMODE_R);
            break;
        case data_dec:
            ret = FS_OpenFileEx(&file, "rom:/dec.bin", FS_FILEMODE_R);
            break;
        default:
            ret = FALSE;
            break;
        }
        SDK_ASSERT(ret);
        for (;;)
        {
            int     n = FS_ReadFile(&file, pt->buf, sizeof(pt->buf));
            SDK_ASSERT(n >= 0);
            if (n == 0)
                break;
            switch (pt->mode)
            {
            case data_00:
                for (i = 0; i < n; ++i)
                {
                    SDK_ASSERT(pt->buf[i] == 0x00);
                }
                break;
            case data_ff:
                for (i = 0; i < n; ++i)
                {
                    SDK_ASSERT(pt->buf[i] == 0xFF);
                }
                break;
            case data_inc:
                for (i = 0; i < n; ++i)
                {
                    SDK_ASSERT(pt->buf[i] == (u8)i);
                }
                break;
            case data_dec:
                for (i = 0; i < n; ++i)
                {
                    SDK_ASSERT(pt->buf[i] == (u8)~i);
                }
                break;
            }
            total_size += n;
        }
        ret = FS_CloseFile(&file);
        SDK_ASSERT(ret);
        ++count;
        if (++repeat >= TASK_REPEAT_TIMES)
        {
            u32     current_total;
            OSIntrMode bak_psr = OS_DisableInterrupts();
            total_read_size += total_size;
            current_total = total_read_size;
            total_size = 0;
            OS_TPrintf("thread[%d](priority=%2d) : %8d times done. (mode=%d) ... total %8d byte\n",
                       pt - task_thread, pt->priority, count, pt->mode, current_total);
            (void)OS_RestoreInterrupts(bak_psr);
            repeat = 0;
            if (++pt->mode >= datamode_max)
            {
                pt->mode = data_00;
            }
        }
    }
}

void NitroMain(void)
{
    // OSとメモリアロケータを初期化。
    OS_Init();
    {
        OSHeapHandle hh;
        void   *tmp;
        tmp = OS_InitAlloc(OS_ARENA_MAIN, OS_GetMainArenaLo(), OS_GetMainArenaHi(), 1);
        OS_SetArenaLo(OS_ARENA_MAIN, tmp);
        hh = OS_CreateHeap(OS_ARENA_MAIN, OS_GetMainArenaLo(), OS_GetMainArenaHi());
        if (hh < 0)
        {
            OS_TPanic("ARM9: Fail to create heap...\n");
        }
        (void)OS_SetCurrentHeap(OS_ARENA_MAIN, hh);
    }
    (void)OS_EnableIrq();
    // FSを初期化。
    FS_Init(3);
    {
        u32     need_size = FS_GetTableSize();
        void   *p_table = OS_Alloc(need_size);
        SDK_ASSERT(p_table != NULL);
        (void)FS_LoadTable(p_table, need_size);
    }

    OS_TPrintf("\n"
               "++++++++++++++++++++++++++++++++++++++++\n"
               "test : create %d threads to check asynchronous access ... \n\n", TASK_THREAD_MAX);
    {
        int     i;
        // ファイルを読み込み続けるスレッドを複数生成。
        for (i = 0; i < TASK_THREAD_MAX; ++i)
        {
            TaskThread * pt = &task_thread[i];
            pt->mode = data_00 + i;
            pt->priority = (u32)(OS_THREAD_LAUNCHER_PRIORITY - (pt - task_thread));
            OS_CreateThread(&pt->thread,
                            AsyncAccessTask, pt,
                            pt->stack + sizeof(pt->stack), sizeof(pt->stack), pt->priority);
            OS_WakeupThreadDirect(&pt->thread);
        }
    }

    // あとは各スレッドのログが出力され続ける。
    for (;;)
    {
        OS_Halt();
    }

}
