void
test_mifare_ultralight_cache (void)
{
    int res;
    MifareUltralightPage page;

    res = mifare_ultralight_read (tag, 0, &page);
    cut_assert_equal_int (0, res, cut_message ("mifare_ultralight_read() failed"));

    /* Check cached pages consistency */
    for (int i = 0; i <= 3; i++) {
	cut_assert_equal_int (1, MIFARE_ULTRALIGHT(tag)->cached_pages[i], cut_message ("Wrong page cache value for tag->cached_pages[%d]", i));
    }
    for (int i = 4; i < MIFARE_ULTRALIGHT_PAGE_COUNT; i++) {
	cut_assert_equal_int (0, MIFARE_ULTRALIGHT(tag)->cached_pages[i], cut_message ("Wrong page cache value for tag->cached_pages[%d]", i));
    }
}
/*
 * Read data to the provided MIFARE tag.
 */
int
mifare_ultralight_write (MifareTag tag, const MifareUltralightPageNumber page, const MifareUltralightPage data)
{
    ASSERT_ACTIVE (tag);
    ASSERT_MIFARE_ULTRALIGHT (tag);
    ASSERT_VALID_PAGE (tag, page, true);

    BUFFER_INIT (cmd, 6);
    BUFFER_INIT (res, 1);

    BUFFER_APPEND (cmd, 0xA2);
    BUFFER_APPEND (cmd, page);
    BUFFER_APPEND_BYTES (cmd, data, sizeof (MifareUltralightPage));

    ULTRALIGHT_TRANSCEIVE (tag, cmd, res);

    /* Invalidate page in cache */
    MIFARE_ULTRALIGHT(tag)->cached_pages[page] = 0;

    return 0;
}
/*
 * Establish connection to the provided tag.
 */
int
mifare_ultralight_connect (MifareTag tag)
{
    ASSERT_INACTIVE (tag);
    ASSERT_MIFARE_ULTRALIGHT (tag);

    nfc_target pnti;
    nfc_modulation modulation = {
	.nmt = NMT_ISO14443A,
	.nbr = NBR_106
    };
    if (nfc_initiator_select_passive_target (tag->device, modulation, tag->info.abtUid, tag->info.szUidLen, &pnti) >= 0) {
	tag->active = 1;
	for (int i = 0; i < MIFARE_ULTRALIGHT_MAX_PAGE_COUNT; i++)
	    MIFARE_ULTRALIGHT(tag)->cached_pages[i] = 0;
    } else {
	errno = EIO;
	return -1;
    }
    return 0;
}
/*
 * Read data from the provided MIFARE tag.
 */
int
mifare_ultralight_read (MifareTag tag, MifareUltralightPageNumber page, MifareUltralightPage *data)
{
    ASSERT_ACTIVE (tag);
    ASSERT_MIFARE_ULTRALIGHT (tag);
    ASSERT_VALID_PAGE (tag, page, false);

    if (!MIFARE_ULTRALIGHT(tag)->cached_pages[page]) {
	BUFFER_INIT (cmd, 2);
	BUFFER_ALIAS (res, MIFARE_ULTRALIGHT(tag)->cache[page], sizeof(MifareUltralightPage) * 4);

	BUFFER_APPEND (cmd, 0x30);
	BUFFER_APPEND (cmd, page);

	ULTRALIGHT_TRANSCEIVE (tag, cmd, res);

	/* Handle wrapped pages */
	int iPageCount;
	if (IS_MIFARE_ULTRALIGHT_C(tag)) {
	    iPageCount = MIFARE_ULTRALIGHT_C_PAGE_COUNT_READ;
	} else {
	    iPageCount = MIFARE_ULTRALIGHT_PAGE_COUNT;
	}
	for (int i = iPageCount; i <= page + 3; i++) {
	    memcpy (MIFARE_ULTRALIGHT(tag)->cache[i % iPageCount], MIFARE_ULTRALIGHT(tag)->cache[i], sizeof (MifareUltralightPage));
	}

	/* Mark pages as cached */
	for (int i = page; i <= page + 3; i++) {
	    MIFARE_ULTRALIGHT(tag)->cached_pages[i % iPageCount] = 1;
	}
    }

    memcpy (data, MIFARE_ULTRALIGHT(tag)->cache[page], sizeof (*data));
    return 0;
}