Пример #1
0
PRSEV_HANDLER_DEF(E_STATE_APP_SLEEP, tsEvent *pEv, teEvent eEvent, uint32 u32evarg) {
	if (eEvent == E_EVENT_NEW_STATE) {
		// Sleep は必ず E_EVENT_NEW_STATE 内など1回のみ呼び出される場所で呼び出す。
		V_PRINTF(LB"! Sleeping...");
		V_FLUSH();

		pEv->bKeepStateOnSetAll = FALSE; // スリープ復帰の状態を維持

		// Mininode の場合、特別な処理は無いのだが、ポーズ処理を行う
		ToCoNet_Nwk_bPause(sAppData.pContextNwk);

		// センサー用の電源制御回路を Hi に戻す
		vPortSetSns(FALSE);

#ifdef LITE2525A
		vPortSetLo(LED);
#else
		vPortSetHi(LED);
#endif
		// 周期スリープに入る
		if(sAppData.sFlash.sData.i16param == NORMAL || sAppData.sFlash.sData.i16param == NEKOTTER ){
			vSleep(sAppData.sFlash.sData.u32Slp, sAppData.u16frame_count == 1 ? FALSE : TRUE, FALSE);
		}else{
			//	割り込みの設定
			vAHI_DioSetDirection(PORT_INPUT_MASK_ADXL345, 0); // set as input
			(void)u32AHI_DioInterruptStatus(); // clear interrupt register
			vAHI_DioWakeEnable(PORT_INPUT_MASK_ADXL345, 0); // also use as DIO WAKE SOURCE
			vAHI_DioWakeEdge(PORT_INPUT_MASK_ADXL345, 0); // 割り込みエッジ(立上がりに設定)

			vSleep(0, FALSE, FALSE);
		}
	}
}
/**
 * アプリケーション再送の条件判定と短いスリープ処理
 *
 * @param E_SATET_APP_RETRY
 * @param pEv
 * @param eEvent
 * @param u32evarg
 */
PRSEV_HANDLER_DEF(E_STATE_APP_RETRY, tsEvent *pEv, teEvent eEvent, uint32 u32evarg) {
	V_PRINTF(LB"!E_STATE_APP_RETRY (%d)", u8RetryCount);
	if (!bRetry || u8RetryCount == 0) {
		ToCoNet_Event_SetState(pEv, E_STATE_APP_SLEEP); // スリープ状態へ遷移
		return;
	}

	// 遷移してきたとき
	if (eEvent == E_EVENT_NEW_STATE) {
		if (u8RetryCount == 0xff) {
			u8RetryCount = sAppData.sFlash.sData.u8wait % 10;
		}

		// 短いスリープを行う (u8RetryCount > 0)
		pEv->bKeepStateOnSetAll = TRUE;
		V_FLUSH();

		uint16 u16dur = u16RetryDur / 2 + (ToCoNet_u32GetRand() % u16RetryDur); // +/-50% のランダム化
		ToCoNet_vSleep(E_AHI_WAKE_TIMER_1, u16dur, FALSE, FALSE);
		return;
	}

	// スリープ復帰
	if (eEvent == E_EVENT_START_UP) {
		V_PRINTF(LB"!woke from mini sleep(%d)", u8RetryCount);
		if (IS_APPCONF_OPT_SECURE()) {
			bRegAesKey(sAppData.sFlash.sData.u32EncKey);
		}
		ToCoNet_Nwk_bResume(sAppData.pContextNwk);
		ToCoNet_Event_SetState(pEv, E_STATE_APP_RETRY_TX);
		u8RetryCount--; // 再送回数を減らす

		return;
	}
}
/*	スリープをする状態	*/
PRSEV_HANDLER_DEF(E_STATE_APP_SLEEP, tsEvent *pEv, teEvent eEvent, uint32 u32evarg) {
	if (eEvent == E_EVENT_NEW_STATE) {
		// Sleep は必ず E_EVENT_NEW_STATE 内など1回のみ呼び出される場所で呼び出す。
		V_PRINTF(LB"Sleeping...");
		V_FLUSH();

		// Mininode の場合、特別な処理は無いのだが、ポーズ処理を行う
		ToCoNet_Nwk_bPause(sAppData.pContextNwk);

		// print message.
		vAHI_UartDisable(UART_PORT); // UART を解除してから(このコードは入っていなくても動作は同じ)

		// set UART Rx port as interrupt source
		vAHI_DioSetDirection(PORT_INPUT_MASK, 0); // set as input

		(void)u32AHI_DioInterruptStatus(); // clear interrupt register

		vAHI_DioWakeEnable(PORT_INPUT_MASK, 0); // also use as DIO WAKE SOURCE

		// i16paramに関わらず状態の変化で起床
		if(sAppData.bDI1_Now_Opened) {
			vAHI_DioWakeEdge(0, PORT_INPUT_MASK); // 割り込みエッジ(立下りに設定)
		} else {
			vAHI_DioWakeEdge(PORT_INPUT_MASK, 0); // 割り込みエッジ(立上がりに設定)
		}

		// wake up using wakeup timer as well.
		ToCoNet_vSleep(E_AHI_WAKE_TIMER_0, sAppData.sFlash.sData.u32Slp, FALSE, FALSE ); // PERIODIC RAM OFF SLEEP USING WK0

	}
}
/*
 * ADC 計測をしてデータ送信するアプリケーション制御
 */
PRSEV_HANDLER_DEF(E_STATE_IDLE, tsEvent *pEv, teEvent eEvent, uint32 u32evarg) {
	if (eEvent == E_EVENT_START_UP) {
		if (u32evarg & EVARG_START_UP_WAKEUP_RAMHOLD_MASK) {
			// Warm start message
			V_PRINTF(LB "*** Warm starting woke by %s. ***", sAppData.bWakeupByButton ? "DIO" : "WakeTimer");
		} else {
			// 開始する
			// start up message
			vSerInitMessage();

			V_PRINTF(LB "*** Cold starting");
			V_PRINTF(LB "* start end device[%d]", u32TickCount_ms & 0xFFFF);
			V_FLUSH();
		}

		// RC クロックのキャリブレーションを行う
		ToCoNet_u16RcCalib(sAppData.sFlash.sData.u16RcClock);

		// センサーがらみの変数の初期化
		u8sns_cmplt = 0;

		// TSL2561 の初期化
		vTSL2561_Init(&sObjTSL2561, &sSnsObj);
		vSnsObj_Process(&sSnsObj, E_ORDER_KICK);
		if (bSnsObj_isComplete(&sSnsObj)) {
			// 即座に完了した時はセンサーが接続されていない、通信エラー等
			u8sns_cmplt |= E_SNS_TSL2561_CMP;
			V_PRINTF(LB "*** TSL2561 comm err?");
			ToCoNet_Event_SetState(pEv, E_STATE_APP_SLEEP); // スリープ状態へ遷移
			return;
		}

		// ADC の取得
		vADC_WaitInit();
		vSnsObj_Process(&sAppData.sADC, E_ORDER_KICK);

		V_PRINTF( LB"Start I2C" );
		// RUNNING 状態
		ToCoNet_Event_SetState(pEv, E_STATE_RUNNING);
	} else {
		V_PRINTF(LB "*** unexpected state.");
		ToCoNet_Event_SetState(pEv, E_STATE_APP_SLEEP); // スリープ状態へ遷移
	}
}
Пример #5
0
PRSEV_HANDLER_DEF(E_STATE_APP_SLEEP, tsEvent *pEv, teEvent eEvent, uint32 u32evarg) {
	if (eEvent == E_EVENT_NEW_STATE) {
		// Sleep は必ず E_EVENT_NEW_STATE 内など1回のみ呼び出される場所で呼び出す。
		V_PRINTF(LB"! Sleeping...");
		V_FLUSH();

		pEv->bKeepStateOnSetAll = FALSE; // スリープ復帰の状態を維持

		// Mininode の場合、特別な処理は無いのだが、ポーズ処理を行う
		ToCoNet_Nwk_bPause(sAppData.pContextNwk);

		// センサー用の電源制御回路を Hi に戻す
		vPortSetSns(FALSE);

		// 周期スリープに入る
		//  - 初回は5秒あけて、次回以降はスリープ復帰を基点に5秒
		vSleep(sAppData.sFlash.sData.u32Slp, sAppData.u16frame_count == 1 ? FALSE : TRUE, FALSE);
	}
}
static int
slackspace_test(int verbose) {
    unsigned ksize, vsize;
    int ksizes[] = { sizeof( ((small_title_chunk_t*) 0)->data ) - 1,
                     sizeof( ((small_title_chunk_t*) 0)->data ),
                     sizeof( ((small_title_chunk_t*) 0)->data ) + 1,
                     ( sizeof( ((small_title_chunk_t*) 0)->data ) ) +
                     ( (SMALL_CHUNKS_PER_LARGE_CHUNK - 2) * sizeof( ((small_body_chunk_t*) 0)->data ) ) - 1,
                     ( sizeof( ((small_title_chunk_t*) 0)->data ) ) +
                     ( (SMALL_CHUNKS_PER_LARGE_CHUNK - 2) * sizeof( ((small_body_chunk_t*) 0)->data ) ),
                     ( sizeof( ((small_title_chunk_t*) 0)->data ) ) +
                     ( (SMALL_CHUNKS_PER_LARGE_CHUNK - 2) * sizeof( ((small_body_chunk_t*) 0)->data ) ),
    };

    V_LPRINTF(1, "%s\n", __FUNCTION__);

    for (vsize = 0;
         vsize < 16 * 1024;
         vsize ++) {
        for (ksize = 0;
             ksize < (sizeof(ksizes) / sizeof(*ksizes));
             ksize ++) {
            size_t slack = slackspace(ksizes[ksize], vsize);

            if (vsize % 1024 == 0) {
                V_PRINTF(2, "\r  *  allocating object value size=%u", vsize);
                V_FLUSH(2);
            }

            if (vsize == 0 &&
                ksizes[ksize] == 0) {
                continue;
            }

            TASSERT( is_large_chunk(ksizes[ksize], vsize) == is_large_chunk(ksizes[ksize], vsize + slack) );
            TASSERT( chunks_needed(ksizes[ksize], vsize) == chunks_needed(ksizes[ksize], vsize + slack) );
        }
    }
    V_PRINTF(2, "\n");

    return 0;
}
PRSEV_HANDLER_DEF(E_STATE_APP_SLEEP, tsEvent *pEv, teEvent eEvent, uint32 u32evarg) {
	if (eEvent == E_EVENT_NEW_STATE) {
		// Sleep は必ず E_EVENT_NEW_STATE 内など1回のみ呼び出される場所で呼び出す。
		V_PRINTF(LB"! Sleeping...");
		V_FLUSH();

		// Mininode の場合、特別な処理は無いのだが、ポーズ処理を行う
		ToCoNet_Nwk_bPause(sAppData.pContextNwk);

		// センサー用の電源制御回路を Hi に戻す
		vPortSetSns(FALSE);

#ifdef LITE2525A
		vPortSetLo(LED);
#else
		vPortSetHi(LED);
#endif
		vAHI_DioWakeEnable(0, PORT_INPUT_MASK); // DISABLE DIO WAKE SOURCE
		ToCoNet_vSleep( E_AHI_WAKE_TIMER_0, sAppData.sFlash.sData.u32Slp, FALSE, FALSE);
	}
}
/*
 * 送信後のチャタリング対策を行う状態
 */
PRSEV_HANDLER_DEF(E_STATE_APP_CHAT_SLEEP, tsEvent *pEv, teEvent eEvent, uint32 u32evarg) {

		/*	遷移してきたとき一旦眠る	*/
		if(eEvent == E_EVENT_NEW_STATE){
			V_PRINTF(LB"Safe Chattering...");
			V_FLUSH();
			// Mininode の場合、特別な処理は無いのだが、ポーズ処理を行う
			ToCoNet_Nwk_bPause(sAppData.pContextNwk);

			//	割り込み禁止でスリープ
			pEv->bKeepStateOnSetAll = TRUE;		//	この状態から起床
			vAHI_DioWakeEnable(0, PORT_INPUT_MASK); // DISABLE DIO WAKE SOURCE

			ToCoNet_vSleep(E_AHI_WAKE_TIMER_1, 200UL, FALSE, FALSE);

		/*	起床後すぐこの状態になったときずっと眠る状態へ	*/
		}else if(eEvent == E_EVENT_START_UP){
			pEv->bKeepStateOnSetAll = FALSE;
			ToCoNet_Event_SetState(pEv, E_STATE_APP_SLEEP); // スリープ状態へ遷移
		}
}
/**
 * スリープへの遷移
 *
 * @param E_STATE_APP_SLEEP
 * @param pEv
 * @param eEvent
 * @param u32evarg
 */
PRSEV_HANDLER_DEF(E_STATE_APP_SLEEP, tsEvent *pEv, teEvent eEvent, uint32 u32evarg) {
	if (eEvent == E_EVENT_NEW_STATE) {
		pEv->bKeepStateOnSetAll = FALSE;
		u8RetryCount = 0xff;

		// センサー用の電源制御回路を Hi に戻す
		vPortSetSns(FALSE);

		// RTS0 を通信禁止にする
		vPortSetHi(PORT_RTS0);

		// Sleep は必ず E_EVENT_NEW_STATE 内など1回のみ呼び出される場所で呼び出す。
		V_PRINTF(LB"Sleeping...");
		V_FLUSH();

		// Mininode の場合、特別な処理は無いのだが、ポーズ処理を行う
		ToCoNet_Nwk_bPause(sAppData.pContextNwk);

		// スリープ状態に遷移
		vSleep(0, FALSE, FALSE);
	}
}
/*
 * allocate all memory with small and large chunks.  link them such the
 * allocation of a large object will start evicting small chunks but then stop
 * because the large chunk LRU has an older item.  this covers part of case 3
 * and part of case 4 for the small item alloc in flat_storage_lru_evict(..).
 */
static int
mixed_items_release_small_and_large_items_scan_stop_test(int verbose) {
    typedef struct {
        item* it;
        char key[KEY_MAX_LENGTH];
        uint8_t klen;
    } mixed_items_release_one_small_item_t;

    size_t num_small_objects = (fsi.large_free_list_sz / 2) * SMALL_CHUNKS_PER_LARGE_CHUNK;
    /* this is not the same as fsi.large_free_list_sz / 2 due to rounding. */
    size_t num_large_objects = fsi.large_free_list_sz - (fsi.large_free_list_sz / 2);
    mixed_items_release_one_small_item_t* large_items = malloc(sizeof(mixed_items_release_one_small_item_t) *
                                                               num_large_objects);
    mixed_items_release_one_small_item_t* small_items = malloc(sizeof(mixed_items_release_one_small_item_t) *
                                                               num_small_objects);
    item* lru_trigger;
    size_t max_small_key_size = SMALL_TITLE_CHUNK_DATA_SZ;
    size_t min_size_for_large_chunk = ( sizeof( ((small_title_chunk_t*) 0)->data ) ) +
        ( (SMALL_CHUNKS_PER_LARGE_CHUNK - 1) * sizeof( ((small_body_chunk_t*) 0)->data ) ) +
        1;
    size_t i;
    char key[KEY_MAX_LENGTH];
    size_t klen;
    size_t large_free_list_sz = fsi.large_free_list_sz, small_free_list_sz = fsi.small_free_list_sz;

    V_PRINTF(1, "  * %s\n", __FUNCTION__);

    TASSERT(fsi.large_free_list_sz != 0);
    TASSERT(fsi.small_free_list_sz == 0);

    for (i = 0; i < num_small_objects; i ++) {
        V_PRINTF(2, "\r  *  allocating small object %lu", i);
        V_FLUSH(2);
        do {
            small_items[i].klen = make_random_key(small_items[i].key, max_small_key_size, true);
        } while (assoc_find(small_items[i].key, small_items[i].klen));

        small_items[i].it = do_item_alloc(small_items[i].key, small_items[i].klen,
                                          FLAGS, 0,
                                          0, addr);
        TASSERT(small_items[i].it);
        TASSERT(is_item_large_chunk(small_items[i].it) == 0);

        do_item_link(small_items[i].it, small_items[i].key);
    }
    V_PRINTF(2, "\n");

    for (i = 0; i < num_large_objects; i ++) {
        V_PRINTF(2, "\r  *  allocating large object %lu", i);
        V_FLUSH(2);
        do {
            large_items[i].klen = make_random_key(large_items[i].key, KEY_MAX_LENGTH, true);
        } while (assoc_find(large_items[i].key, large_items[i].klen));

        large_items[i].it = do_item_alloc(large_items[i].key, large_items[i].klen,
                                          FLAGS, 0,
                                          min_size_for_large_chunk - large_items[i].klen, addr);
        TASSERT(large_items[i].it);
        TASSERT(is_item_large_chunk(large_items[i].it));

        do_item_link(large_items[i].it, large_items[i].key);
    }
    V_PRINTF(2, "\n");

    TASSERT(fsi.large_free_list_sz == 0 &&
            fsi.small_free_list_sz == 0);

    V_LPRINTF(2, "update items\n");
    /* update the objects we want to clobber *first*.  but since ties go to the
     * large item, we need to bump the time stamp to ensure the small item is
     * released first. */
    current_time += ITEM_UPDATE_INTERVAL + 1; /* initial bump to ensure that
                                               * LRU reordering takes place. */

    do_item_update(small_items[0].it);
    current_time += 1;
    do_item_update(large_items[0].it);

    /* bump the timestamp and add the remaining items. */
    current_time += 1;
    for (i = 1; i < num_small_objects; i ++) {
        do_item_update(small_items[i].it);
    }
    for (i = 1; i < num_large_objects; i ++) {
        do_item_update(large_items[i].it);
    }

    V_LPRINTF(2, "dereferencing objects\n");
    for (i = 0; i < num_small_objects; i ++) {
        do_item_deref(small_items[i].it);
    }
    for (i = 0; i < num_large_objects; i ++) {
        do_item_deref(large_items[i].it);
    }

    V_LPRINTF(2, "alloc after deref\n");
    do {
        klen = make_random_key(key, max_small_key_size, true);
    } while (assoc_find(key, klen));
    lru_trigger = do_item_alloc(key, klen, FLAGS, 0, LARGE_TITLE_CHUNK_DATA_SZ - klen, addr);
    TASSERT(lru_trigger != NULL);
    TASSERT(is_item_large_chunk(lru_trigger));

    V_LPRINTF(2, "search for evicted objects\n");
    TASSERT(assoc_find(small_items[0].key, small_items[0].klen) == NULL);
    TASSERT(assoc_find(large_items[0].key, large_items[0].klen) == NULL);

    V_LPRINTF(2, "ensuring that objects that shouldn't be evicted are still present\n");
    for (i = 1; i < num_small_objects; i ++) {
        TASSERT(assoc_find(small_items[i].key, small_items[i].klen));
    }
    for (i = 1; i < num_large_objects; i ++) {
        TASSERT(assoc_find(large_items[i].key, large_items[i].klen));
    }

    V_LPRINTF(2, "cleanup objects\n");
    for (i = 1; i < num_small_objects; i ++) {
        do_item_unlink(small_items[i].it, UNLINK_NORMAL, small_items[i].key);
    }
    for (i = 1; i < num_large_objects; i ++) {
        do_item_unlink(large_items[i].it, UNLINK_NORMAL, large_items[i].key);
    }
    do_item_deref(lru_trigger);

    TASSERT(fsi.large_free_list_sz == large_free_list_sz &&
            fsi.small_free_list_sz == small_free_list_sz);

    return 0;
}
/*
 * allocate all memory with small and large chunks.  link them such that the
 * small items are the oldest.  allocate one large object that can be covered by
 * the release of one large item.  this covers part of case 4 for the large item
 * alloc in flat_storage_lru_evict(..).
 */
static int
mixed_items_release_one_large_item_test(int verbose) {
    typedef struct {
        item* it;
        char key[KEY_MAX_LENGTH];
        uint8_t klen;
    } mixed_items_release_one_small_item_t;

    size_t num_small_objects = (fsi.large_free_list_sz / 2) * SMALL_CHUNKS_PER_LARGE_CHUNK;
    /* this is not the same as fsi.large_free_list_sz / 2 due to rounding. */
    size_t num_large_objects = fsi.large_free_list_sz - (fsi.large_free_list_sz / 2);
    mixed_items_release_one_small_item_t* large_items = malloc(sizeof(mixed_items_release_one_small_item_t) *
                                                               num_large_objects);
    mixed_items_release_one_small_item_t* small_items = malloc(sizeof(mixed_items_release_one_small_item_t) *
                                                               num_small_objects);
    item* lru_trigger;
    size_t max_small_key_size = SMALL_TITLE_CHUNK_DATA_SZ;
    size_t min_size_for_large_chunk = ( sizeof( ((small_title_chunk_t*) 0)->data ) ) +
        ( (SMALL_CHUNKS_PER_LARGE_CHUNK - 1) * sizeof( ((small_body_chunk_t*) 0)->data ) ) +
        1;
    size_t i;
    char key[KEY_MAX_LENGTH];
    size_t klen;
    size_t large_free_list_sz = fsi.large_free_list_sz, small_free_list_sz = fsi.small_free_list_sz;

    V_PRINTF(1, "  * %s\n", __FUNCTION__);

    TASSERT(fsi.large_free_list_sz != 0);
    TASSERT(fsi.small_free_list_sz == 0);

    for (i = 0; i < num_large_objects; i ++) {
        V_PRINTF(2, "\r  *  allocating large object %lu", i);
        V_FLUSH(2);
        do {
            large_items[i].klen = make_random_key(large_items[i].key, KEY_MAX_LENGTH, true);
        } while (assoc_find(large_items[i].key, large_items[i].klen));

        large_items[i].it = do_item_alloc(large_items[i].key, large_items[i].klen,
                                          FLAGS, 0,
                                          min_size_for_large_chunk - large_items[i].klen,
                                          addr);
        TASSERT(large_items[i].it);
        TASSERT(is_item_large_chunk(large_items[i].it));

        do_item_link(large_items[i].it, large_items[i].key);
    }
    V_PRINTF(2, "\n");

    for (i = 0; i < num_small_objects; i ++) {
        V_PRINTF(2, "\r  *  allocating small object %lu", i);
        V_FLUSH(2);
        do {
            small_items[i].klen = make_random_key(small_items[i].key, max_small_key_size, true);
        } while (assoc_find(small_items[i].key, small_items[i].klen));

        small_items[i].it = do_item_alloc(small_items[i].key, small_items[i].klen,
                                          FLAGS, 0,
                                          0, addr);
        TASSERT(small_items[i].it);
        TASSERT(is_item_large_chunk(small_items[i].it) == 0);

        do_item_link(small_items[i].it, small_items[i].key);
    }
    V_PRINTF(2, "\n");

    TASSERT(fsi.large_free_list_sz == 0 &&
            fsi.small_free_list_sz == 0);

    V_LPRINTF(2, "alloc before deref\n");
    do {
        klen = make_random_key(key, max_small_key_size, true);
    } while (assoc_find(key, klen));

    lru_trigger = do_item_alloc(key, klen, FLAGS, 0, 0, addr);
    TASSERT(lru_trigger == NULL);

    V_LPRINTF(2, "dereferencing objects\n");
    for (i = 0; i < num_small_objects; i ++) {
        do_item_deref(small_items[i].it);
    }
    for (i = 0; i < num_large_objects; i ++) {
        do_item_deref(large_items[i].it);
    }

    V_LPRINTF(2, "alloc after deref\n");
    lru_trigger = do_item_alloc(key, klen, FLAGS, 0, min_size_for_large_chunk - klen, addr);
    TASSERT(lru_trigger != NULL);

    V_LPRINTF(2, "search for evicted object\n");
    TASSERT(assoc_find(large_items[0].key, large_items[0].klen) == NULL);

    V_LPRINTF(2, "ensuring that objects that shouldn't be evicted are still present\n");
    for (i = 0; i < num_small_objects; i ++) {
        TASSERT(assoc_find(small_items[i].key, small_items[i].klen));
    }
    for (i = 1; i < num_large_objects; i ++) {
        TASSERT(assoc_find(large_items[i].key, large_items[i].klen));
    }

    V_LPRINTF(2, "cleanup objects\n");
    for (i = 0; i < num_small_objects; i ++) {
        do_item_unlink(small_items[i].it, UNLINK_NORMAL, small_items[i].key);
    }
    for (i = 1; i < num_large_objects; i ++) {
        do_item_unlink(large_items[i].it, UNLINK_NORMAL, large_items[i].key);
    }
    do_item_deref(lru_trigger);

    TASSERT(fsi.large_free_list_sz == large_free_list_sz &&
            fsi.small_free_list_sz == small_free_list_sz);

    return 0;
}
/*
 * this is a negative test to ensure the proper behavior when we don't have
 * sufficient resources.  in this case, we have sufficient small items on the
 * LRU, and enough of them have refcount == 0, but all the parent broken chunks
 * have refcount > 0.
 */
static int
insufficient_available_large_broken_chunks(int verbose) {
    typedef struct {
        item* it;
        char key[KEY_MAX_LENGTH];
        uint8_t klen;
    } all_small_chunks_key_t;

    size_t num_objects = fsi.large_free_list_sz * SMALL_CHUNKS_PER_LARGE_CHUNK;
    all_small_chunks_key_t* small_items = malloc(sizeof(all_small_chunks_key_t) * num_objects);
    item* lru_trigger;
    size_t max_key_size = SMALL_TITLE_CHUNK_DATA_SZ;
    size_t min_size_for_large_chunk = ( sizeof( ((small_title_chunk_t*) 0)->data ) ) +
        ( (SMALL_CHUNKS_PER_LARGE_CHUNK - 1) * sizeof( ((small_body_chunk_t*) 0)->data ) ) +
        1;
    size_t i;
    char key[KEY_MAX_LENGTH];
    size_t klen;
    size_t large_free_list_sz = fsi.large_free_list_sz, small_free_list_sz = fsi.small_free_list_sz;

    V_PRINTF(1, "  * %s\n", __FUNCTION__);

    TASSERT(fsi.large_free_list_sz != 0);
    TASSERT(fsi.small_free_list_sz == 0);

    for (i = 0; i < num_objects; i ++) {
        V_PRINTF(2, "\r  *  allocating object %lu", i);
        V_FLUSH(2);
        do {
            small_items[i].klen = make_random_key(small_items[i].key, max_key_size, true);
        } while (assoc_find(small_items[i].key, small_items[i].klen));

        small_items[i].it = do_item_alloc(small_items[i].key, small_items[i].klen, FLAGS, 0, 0, addr);
        TASSERT(small_items[i].it);
        TASSERT(is_item_large_chunk(small_items[i].it) == false);

        do_item_link(small_items[i].it, small_items[i].key);
    }
    V_PRINTF(2, "\n");

    TASSERT(fsi.large_free_list_sz == 0 &&
            fsi.small_free_list_sz == 0);

    V_LPRINTF(2, "alloc before deref\n");
    do {
        klen = make_random_key(key, max_key_size, true);
    } while (assoc_find(key, klen));

    lru_trigger = do_item_alloc(key, klen, FLAGS, 0, min_size_for_large_chunk - klen, addr);
    TASSERT(lru_trigger == NULL);

    V_LPRINTF(2, "dereferencing objects\n");
    for (i = 0; i < num_objects; i += 2) {
        do_item_deref(small_items[i].it);
    }

    V_LPRINTF(2, "alloc after deref\n");
    lru_trigger = do_item_alloc(key, klen, FLAGS, 0, min_size_for_large_chunk - klen, addr);
    TASSERT(lru_trigger == NULL);

    V_LPRINTF(2, "ensuring that objects that shouldn't be evicted are still present\n");
    for (i = 0; i < num_objects; i ++) {
        bool should_be_found;
        /* we free everything we encounter that has no refcount until we hit the
         * LRU_SEARCH_DEPTH, at which time we cease searching. */
        if (i % 2 == 0 && i < (LRU_SEARCH_DEPTH * 2)) {
            should_be_found = false;
        } else {
            should_be_found = true;
        }
        TASSERT((assoc_find(small_items[i].key, small_items[i].klen) ? (true) : (false)) ==
                should_be_found);
    }

    V_LPRINTF(2, "cleanup objects\n");
    for (i = 0; i < num_objects; i ++) {
        /* we dereference all the odd numbered items */
        if ((i % 2) != 0) {
            do_item_deref(small_items[i].it);
        }

        /* we unlink everything that's still in the LRU. */
        if (i % 2 == 0 && i < (LRU_SEARCH_DEPTH * 2)) {
            ;
        } else {
            do_item_unlink(small_items[i].it, UNLINK_NORMAL, small_items[i].key);
        }
    }

    TASSERT(fsi.large_free_list_sz == large_free_list_sz &&
            fsi.small_free_list_sz == small_free_list_sz);

    return 0;
}
/*
 * allocate nearly all memory with small items (all memory -
 * SMALL_CHUNKS_PER_LARGE_CHUNK - 1).  then set it up such that there is only
 * one item eligible to be freed (i.e., by removing the remaining items from the
 * LRU.  allocate one large object.  this will require the migration of one
 * single chunk item at the LRU head.  this covers part of case 1 for the small
 * item alloc in flat_storage_lru_evict(..).
 */
static int
all_small_items_migrate_small_single_chunk_item_at_lru_head_test(int verbose) {
    typedef struct {
        item* it;
        char key[KEY_MAX_LENGTH];
        uint8_t klen;
    } test_keys_t;

    size_t num_objects = fsi.large_free_list_sz * SMALL_CHUNKS_PER_LARGE_CHUNK;
    test_keys_t* items = malloc(sizeof(test_keys_t) * num_objects);
    item* lru_trigger;
    size_t max_small_key_size = SMALL_TITLE_CHUNK_DATA_SZ;
    size_t min_size_for_large_chunk = ( sizeof( ((small_title_chunk_t*) 0)->data ) ) +
        ( (SMALL_CHUNKS_PER_LARGE_CHUNK - 1) * sizeof( ((small_body_chunk_t*) 0)->data ) ) +
        1;
    size_t i, count;
    char key[KEY_MAX_LENGTH];
    size_t klen;
    size_t large_free_list_sz = fsi.large_free_list_sz, small_free_list_sz = fsi.small_free_list_sz;

    V_PRINTF(1, "  * %s\n", __FUNCTION__);

    TASSERT(fsi.large_free_list_sz != 0);
    TASSERT(fsi.small_free_list_sz == 0);

    for (i = 0, count = 0;
         fsi.large_free_list_sz ||
             fsi.small_free_list_sz > SMALL_CHUNKS_PER_LARGE_CHUNK - 1;
         i ++, count ++) {
        V_PRINTF(2, "\r  *  allocating small object %lu", i);
        V_FLUSH(2);

        assert(i < num_objects);

        do {
            items[i].klen = make_random_key(items[i].key, max_small_key_size, true);
        } while (assoc_find(items[i].key, items[i].klen));

        items[i].it = do_item_alloc(items[i].key, items[i].klen,
                                    FLAGS, 0, 0, addr);
        TASSERT(items[i].it);
        TASSERT(is_item_large_chunk(items[i].it) == 0);

        do_item_link(items[i].it, items[i].key);
    }
    V_PRINTF(2, "\n");

    TASSERT(fsi.large_free_list_sz == 0);
    TASSERT(fsi.small_free_list_sz == SMALL_CHUNKS_PER_LARGE_CHUNK - 1);

    // remove all but one item from the LRU.  and release our reference to the
    // item we don't remove from the LRU.
    for (i = 0; i < count - 1; i ++) {
        do_item_unlink(items[i].it, UNLINK_NORMAL, items[i].key);
    }
    do_item_deref(items[count - 1].it);

    TASSERT(fsi.lru_head == items[count - 1].it);

    TASSERT(fsi.large_free_list_sz == 0);
    TASSERT(fsi.small_free_list_sz == SMALL_CHUNKS_PER_LARGE_CHUNK - 1);

    V_LPRINTF(2, "alloc\n");
    do {
        klen = make_random_key(key, max_small_key_size, true);
    } while (assoc_find(key, klen));

    lru_trigger = do_item_alloc(key, klen, FLAGS, 0, min_size_for_large_chunk - klen, addr);
    TASSERT(lru_trigger != NULL);

    V_LPRINTF(2, "search for evicted object\n");
    TASSERT(assoc_find(items[count - 1].key, items[count - 1].klen) == NULL);

    V_LPRINTF(2, "cleanup objects\n");
    for (i = 0; i < count - 1; i ++) {
        do_item_deref(items[i].it);
    }
    do_item_deref(lru_trigger);

    TASSERT(fsi.large_free_list_sz == large_free_list_sz &&
            fsi.small_free_list_sz == small_free_list_sz);

    return 0;
}
/*
 * allocate all memory with small items.  allocate one large object that can be
 * covered by the release of small items, but also requires the migration of
 * single chunk items.  this covers part of case 1 for the large item alloc in
 * flat_storage_lru_evict(..).
 */
static int
all_small_items_migrate_small_single_chunk_items_test(int verbose) {
    typedef struct {
        item* it;
        char key[KEY_MAX_LENGTH];
        uint8_t klen;
    } test_keys_t;

    size_t num_objects = fsi.large_free_list_sz * SMALL_CHUNKS_PER_LARGE_CHUNK;
    test_keys_t* items = malloc(sizeof(test_keys_t) * num_objects);
    item* lru_trigger;
    size_t max_small_key_size = SMALL_TITLE_CHUNK_DATA_SZ;
    size_t min_size_for_large_chunk = ( sizeof( ((small_title_chunk_t*) 0)->data ) ) +
        ( (SMALL_CHUNKS_PER_LARGE_CHUNK - 1) * sizeof( ((small_body_chunk_t*) 0)->data ) ) +
        1;
    size_t i;
    char key[KEY_MAX_LENGTH];
    size_t klen;
    size_t large_free_list_sz = fsi.large_free_list_sz, small_free_list_sz = fsi.small_free_list_sz;

    V_PRINTF(1, "  * %s\n", __FUNCTION__);

    TASSERT(fsi.large_free_list_sz != 0);
    TASSERT(fsi.small_free_list_sz == 0);

    for (i = 0; i < num_objects; i ++) {
        V_PRINTF(2, "\r  *  allocating small object %lu", i);
        V_FLUSH(2);
        do {
            items[i].klen = make_random_key(items[i].key, max_small_key_size, true);
        } while (assoc_find(items[i].key, items[i].klen));

        items[i].it = do_item_alloc(items[i].key, items[i].klen,
                                    FLAGS, 0, 0, addr);
        TASSERT(items[i].it);
        TASSERT(is_item_large_chunk(items[i].it) == 0);

        do_item_link(items[i].it, items[i].key);
    }
    V_PRINTF(2, "\n");

    TASSERT(fsi.large_free_list_sz == 0 &&
            fsi.small_free_list_sz == 0);

    /* access items we don't want to move. */
    current_time += ITEM_UPDATE_INTERVAL + 1;
    /* touch every other item.  the ones that are not touched in (0,
     * SMALL_CHUNKS_PER_LARGE_CHUNK * 2) will be evicted. */
    for (i = 0; i < SMALL_CHUNKS_PER_LARGE_CHUNK * 2; i += 2) {
        do_item_update(items[i].it);
    }
    /* touch remaining items */
    for (i = SMALL_CHUNKS_PER_LARGE_CHUNK * 2; i < num_objects; i ++) {
        do_item_update(items[i].it);
    }

    V_LPRINTF(2, "dereferencing objects\n");
    for (i = 0; i < num_objects; i ++) {
        do_item_deref(items[i].it);
    }

    V_LPRINTF(2, "alloc after deref\n");
    do {
        klen = make_random_key(key, max_small_key_size, true);
    } while (assoc_find(key, klen));

    lru_trigger = do_item_alloc(key, klen, FLAGS, 0, min_size_for_large_chunk - klen, addr);
    TASSERT(lru_trigger != NULL);

    V_LPRINTF(2, "search for evicted object\n");
    for (i = 1; i < SMALL_CHUNKS_PER_LARGE_CHUNK * 2; i += 2) {
        TASSERT(assoc_find(items[i].key, items[i].klen) == NULL);
    }

    V_LPRINTF(2, "ensuring that objects that shouldn't be evicted are still present\n");
    for (i = 0; i < SMALL_CHUNKS_PER_LARGE_CHUNK * 2; i += 2) {
        /* these may have been moved. */
        TASSERT((items[i].it = assoc_find(items[i].key, items[i].klen)));
    }
    for (i = SMALL_CHUNKS_PER_LARGE_CHUNK * 2; i < num_objects; i ++) {
        TASSERT(assoc_find(items[i].key, items[i].klen));
    }

    V_LPRINTF(2, "cleanup objects\n");
    for (i = 0; i < SMALL_CHUNKS_PER_LARGE_CHUNK * 2; i += 2) {
        do_item_unlink(items[i].it, UNLINK_NORMAL, items[i].key);
    }
    for (i = SMALL_CHUNKS_PER_LARGE_CHUNK * 2; i < num_objects; i ++) {
        do_item_unlink(items[i].it, UNLINK_NORMAL, items[i].key);
    }
    do_item_deref(lru_trigger);

    TASSERT(fsi.large_free_list_sz == large_free_list_sz &&
            fsi.small_free_list_sz == small_free_list_sz);

    return 0;
}
/**
 * UART コマンド待ち
 * @param E_STATE_RUNNING
 * @param pEv
 * @param eEvent
 * @param u32evarg
 */
PRSEV_HANDLER_DEF(E_STATE_RUNNING, tsEvent *pEv, teEvent eEvent, uint32 u32evarg) {
	if (eEvent == E_EVENT_NEW_STATE) {
		uint8 au8msg[16], *q = au8msg;

		V_PRINTF(LB "[RUNNING]");
		V_FLUSH();

		// 起動メッセージとしてシリアル番号を出力する
		S_BE_DWORD(ToCoNet_u32GetSerial());
		sSerCmdOut.au8data = au8msg;
		sSerCmdOut.u16len = q - au8msg;

		sSerCmdOut.vOutput(&sSerCmdOut, &sSerStream);
	} else
#if 0
	if (!bPortRead(DIO_BUTTON)) {
		// DIO_BUTTON が Hi に戻ったらスリープに戻す
		ToCoNet_Event_SetState(pEv, E_STATE_APP_SLEEP);
	} else
#endif
	if (eEvent == E_ORDER_KICK) {
		// UARTのコマンドが入力されるまで待って、送信
		tsSerCmd_Context *pCmd = NULL;
		if (u32evarg) {
			pCmd = (void*)u32evarg;
		}

		if (pCmd && pCmd->u16len <= 80) {
			; // この場合は処理する
		} else {
			ToCoNet_Event_SetState(pEv, E_STATE_APP_SLEEP);
			return;
		}

		V_PRINTF(LB"[RUNNING/UARTRECV ln=%d]", pCmd->u16len);
		sAppData.u16frame_count++; // シリアル番号を更新する

		tsTxDataApp sTx;
		memset(&sTx, 0, sizeof(sTx)); // 必ず0クリアしてから使う!
		uint8 *q =  sTx.auData;

		sTx.u32SrcAddr = ToCoNet_u32GetSerial();

		if (IS_APPCONF_OPT_TO_ROUTER()) {
			// ルータがアプリ中で一度受信して、ルータから親機に再配送
			sTx.u32DstAddr = TOCONET_NWK_ADDR_NEIGHBOUR_ABOVE;
		} else {
			// ルータがアプリ中では受信せず、単純に中継する
			sTx.u32DstAddr = TOCONET_NWK_ADDR_PARENT;
		}

		// ペイロードの準備
		S_OCTET('T');
		S_OCTET(sAppData.sFlash.sData.u8id);
		S_BE_WORD(sAppData.u16frame_count);

		S_OCTET(PKT_ID_UART); // パケット識別子
		S_OCTET(pCmd->u16len); // ペイロードサイズ
		memcpy(q, pCmd->au8data, pCmd->u16len); // データ部
		q += pCmd->u16len;

		sTx.u8Len = q - sTx.auData; // パケットのサイズ
		sTx.u8CbId = sAppData.u16frame_count & 0xFF; // TxEvent で通知される番号、送信先には通知されない
		sTx.u8Seq = sAppData.u16frame_count & 0xFF; // シーケンス番号(送信先に通知される)
		sTx.u8Cmd = 0; // 0..7 の値を取る。パケットの種別を分けたい時に使用する
		//sTx.u8Retry = 0x81; // 強制2回送信

		if (IS_APPCONF_OPT_SECURE()) {
			sTx.bSecurePacket = TRUE;
		}

		if (ToCoNet_Nwk_bTx(sAppData.pContextNwk, &sTx)) {
			V_PRINTF(LB"TxOk");
			ToCoNet_Tx_vProcessQueue(); // 送信処理をタイマーを待たずに実行する
			ToCoNet_Event_SetState(pEv, E_STATE_APP_WAIT_TX);
		} else {
			V_PRINTF(LB"TxFl");
			ToCoNet_Event_SetState(pEv, E_STATE_APP_RETRY);
		}

		if (bRetry) {
			sTx_Retry = sTx;
		}

		V_PRINTF(" FR=%04X", sAppData.u16frame_count);
	}

	// タイムアウト(期待の時間までにデータが来なかった)
	if (ToCoNet_Event_u32TickFrNewState(pEv) > sAppData.sFlash.sData.u32Slp) {
		V_PRINTF(LB"! TIME OUT (E_STATE_RUNNING)");
		ToCoNet_Event_SetState(pEv, E_STATE_APP_SLEEP); // スリープ状態へ遷移
	}
}
/* allocate all memory with small chunks.  allocate one more object.  it should
 * free up the oldest object.  release all objects.  this covers case 1 for the
 * small item alloc in flat_storage_lru_evict(..). */
static int
all_small_chunks_test(int verbose) {
    typedef struct {
        item* it;
        char key[KEY_MAX_LENGTH];
        uint8_t klen;
    } all_small_chunks_key_t;

    size_t num_objects = fsi.large_free_list_sz * SMALL_CHUNKS_PER_LARGE_CHUNK;
    all_small_chunks_key_t* small_items = malloc(sizeof(all_small_chunks_key_t) * num_objects);
    item* lru_trigger;
    size_t max_key_size = SMALL_TITLE_CHUNK_DATA_SZ;
    size_t i;
    char key[KEY_MAX_LENGTH];
    size_t klen;
    size_t large_free_list_sz = fsi.large_free_list_sz, small_free_list_sz = fsi.small_free_list_sz;

    V_PRINTF(1, "  * %s\n", __FUNCTION__);

    TASSERT(fsi.large_free_list_sz != 0);
    TASSERT(fsi.small_free_list_sz == 0);

    for (i = 0; i < num_objects; i ++) {
        V_PRINTF(2, "\r  *  allocating object %lu", i);
        V_FLUSH(2);
        do {
            small_items[i].klen = make_random_key(small_items[i].key, max_key_size, true);
        } while (assoc_find(small_items[i].key, small_items[i].klen));

        small_items[i].it = do_item_alloc(small_items[i].key, small_items[i].klen, FLAGS, 0, 0,
                                          addr);
        TASSERT(small_items[i].it);
        TASSERT(is_item_large_chunk(small_items[i].it) == false);

        do_item_link(small_items[i].it, small_items[i].key);
    }
    V_PRINTF(2, "\n");

    TASSERT(fsi.large_free_list_sz == 0 &&
            fsi.small_free_list_sz == 0);

    V_LPRINTF(2, "alloc before deref\n");
    do {
        klen = make_random_key(key, max_key_size, true);
    } while (assoc_find(key, klen));

    lru_trigger = do_item_alloc(key, klen, FLAGS, 0, 0, addr);
    TASSERT(lru_trigger == NULL);

    V_LPRINTF(2, "dereferencing objects\n");
    for (i = 0; i < num_objects; i ++) {
        do_item_deref(small_items[i].it);
    }

    V_LPRINTF(2, "alloc after deref\n");
    lru_trigger = do_item_alloc(key, klen, FLAGS, 0, 0, addr);
    TASSERT(lru_trigger != NULL);

    V_LPRINTF(2, "search for evicted object\n");
    TASSERT(assoc_find(small_items[0].key, small_items[0].klen) == NULL);

    V_LPRINTF(2, "ensuring that objects that shouldn't be evicted are still present\n");
    for (i = 1; i < num_objects; i ++) {
        TASSERT(assoc_find(small_items[i].key, small_items[i].klen));
    }

    V_LPRINTF(2, "cleanup objects\n");
    for (i = 1; i < num_objects; i ++) {
        do_item_unlink(small_items[i].it, UNLINK_NORMAL, small_items[i].key);
    }
    do_item_deref(lru_trigger);

    TASSERT(fsi.large_free_list_sz == large_free_list_sz &&
            fsi.small_free_list_sz == small_free_list_sz);

    return 0;
}