diff --git a/.gitignore b/.gitignore
index 5d4bb95..9394e33 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,6 +21,8 @@ ltmain.sh
missing
stamp-h1
test-driver
+examples/felica-lite-dump
+examples/felica-read-ndef
examples/mifare-classic-format
examples/mifare-classic-read-ndef
examples/mifare-classic-write-ndef
diff --git a/README.md b/README.md
index 06e55c4..2898b4e 100644
--- a/README.md
+++ b/README.md
@@ -8,21 +8,22 @@ If you are new to _libfreefare_ or the _nfc-tools_, you should collect useful in
# Feature matrix
## Tags
-| Tag | Status |
-|:-------------------|:--------------|
-| MIFARE Classic 1k | Supported |
-| MIFARE Classic 4k | Supported |
-| MIFARE DESFire 2k | Supported |
-| MIFARE DESFire 4k | Supported |
-| MIFARE DESFire 8k | Supported |
-| MIFARE DESFire EV1 | Supported |
-| MIFARE Mini | Not supported |
-| MIFARE Plus S 2k | Not supported |
-| MIFARE Plus S 4k | Not supported |
-| MIFARE Plus X 2k | Not supported |
-| MIFARE Plus X 4k | Not supported |
-| MIFARE Ultralight | Supported |
-| MIFARE UltralightC | Supported |
+| Tag | Status |
+|:--------------------|:--------------|
+| FeliCa Lite | Supported |
+| MIFARE Classic 1k | Supported |
+| MIFARE Classic 4k | Supported |
+| MIFARE DESFire 2k | Supported |
+| MIFARE DESFire 4k | Supported |
+| MIFARE DESFire 8k | Supported |
+| MIFARE DESFire EV1 | Supported |
+| MIFARE Mini | Not supported |
+| MIFARE Plus S 2k | Not supported |
+| MIFARE Plus S 4k | Not supported |
+| MIFARE Plus X 2k | Not supported |
+| MIFARE Plus X 4k | Not supported |
+| MIFARE Ultralight | Supported |
+| MIFARE Ultralight C | Supported |
## Specifications
| Specification | Status |
diff --git a/examples/Makefile.am b/examples/Makefile.am
index 1d63de9..eece1dd 100644
--- a/examples/Makefile.am
+++ b/examples/Makefile.am
@@ -1,7 +1,9 @@
AM_CFLAGS = -I. -I$(top_srcdir)/libfreefare @LIBNFC_CFLAGS@
AM_LDFLAGS = @LIBNFC_LIBS@
-bin_PROGRAMS = mifare-classic-format \
+bin_PROGRAMS = felica-lite-dump \
+ felica-read-ndef \
+ mifare-classic-format \
mifare-classic-write-ndef \
mifare-classic-read-ndef \
mifare-desfire-access \
@@ -15,6 +17,12 @@ bin_PROGRAMS = mifare-classic-format \
mifare-desfire-write-ndef \
mifare-ultralight-info
+felica_lite_dump_SOURCES = felica-lite-dump.c
+felica_lite_dump_LDADD = $(top_builddir)/libfreefare/libfreefare.la
+
+felica_read_ndef_SOURCES = felica-read-ndef.c
+felica_read_ndef_LDADD = $(top_builddir)/libfreefare/libfreefare.la
+
mifare_classic_format_SOURCES = mifare-classic-format.c
mifare_classic_format_LDADD = $(top_builddir)/libfreefare/libfreefare.la
diff --git a/examples/felica-lite-dump.c b/examples/felica-lite-dump.c
new file mode 100644
index 0000000..df77be9
--- /dev/null
+++ b/examples/felica-lite-dump.c
@@ -0,0 +1,104 @@
+/*-
+ * Copyright (C) 2015, Romain Tartiere.
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see
+ */
+
+#include
+#include
+
+#include
+
+#include
+
+int
+main (void)
+{
+ nfc_device *device = NULL;
+ FreefareTag *tags = NULL;
+ nfc_connstring devices[8];
+
+ nfc_context *context;
+ nfc_init (&context);
+ if (context == NULL)
+ errx (EXIT_FAILURE, "Unable to init libnfc (malloc)");
+
+ size_t device_count = nfc_list_devices (context, devices, 8);
+ if (device_count <= 0)
+ errx (EXIT_FAILURE, "No NFC device found.");
+
+ for (size_t d = 0; d < device_count; d++) {
+ device = nfc_open (context, devices[d]);
+ if (!device) {
+ errx (EXIT_FAILURE, "nfc_open() failed.");
+ }
+
+ tags = freefare_get_tags (device);
+ if (!tags) {
+ nfc_close (device);
+ errx (EXIT_FAILURE, "Error listing FeliCa tag.");
+ }
+
+ for (int i = 0; tags[i]; i++) {
+ int r = felica_connect (tags[i]);
+ if (r < 0)
+ errx (EXIT_FAILURE, "Cannot connect to FeliCa target");
+
+ printf ("Dumping %s tag %s\n", freefare_get_tag_friendly_name (tags[i]), freefare_get_tag_uid (tags[i]));
+ printf ("Number\tName\tData\n");
+
+ for (int block = 0x00; block < 0x0f; block++) {
+ uint8_t buffer[16];
+
+ if (felica_read (tags[i], FELICA_SC_RO, block, buffer, sizeof (buffer)) < 0)
+ errx (EXIT_FAILURE, "Error reading block %d", block);
+
+ if (block < 0x0e)
+ printf ("0x%02x\tS_PAD%d\t", block, block);
+ else
+ printf ("0x%02x\tREG\t", block);
+ for (int j = 0; j < 16; j++) {
+ printf ("%02x ", buffer[j]);
+ }
+ printf ("\n");
+ }
+
+ char *block_names[] = {
+ "RC", "MAC", "ID", "D_ID", "SER_C", "SYS_C", "CKV", "CK", "MC",
+ };
+ int valid_bytes[] = {
+ 16, 8, 16, 16, 2, 2, 2, 16, 5
+ };
+ for (int block = 0x80; block < 0x89; block++) {
+ uint8_t buffer[16];
+
+ if (felica_read (tags[i], FELICA_SC_RO, block, buffer, sizeof (buffer)) < 0)
+ errx (EXIT_FAILURE, "Error reading block %d", block);
+
+ printf ("0x%02x\t%s\t", block, block_names[block - 0x80]);
+ for (int j = 0; j < valid_bytes[block - 0x80]; j++) {
+ printf ("%02x ", buffer[j]);
+ }
+ printf ("\n");
+ }
+
+ felica_disconnect (tags[i]);
+ }
+
+ freefare_free_tags (tags);
+ nfc_close (device);
+ }
+
+ exit(EXIT_SUCCESS);
+}
diff --git a/examples/felica-read-ndef.c b/examples/felica-read-ndef.c
new file mode 100644
index 0000000..0c5bc77
--- /dev/null
+++ b/examples/felica-read-ndef.c
@@ -0,0 +1,164 @@
+/*-
+ * Copyright (C) 2015, Romain Tartiere.
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see
+ */
+
+#include "config.h"
+
+#include
+#include
+#include
+#include
+
+#if defined(HAVE_SYS_ENDIAN_H)
+# include
+#endif
+
+#if defined(HAVE_ENDIAN_H)
+# include
+#endif
+
+#include
+
+#include
+
+#define NDEF_BUFFER_SIZE 512
+
+void
+usage (char *progname)
+{
+ fprintf (stderr, "usage: %s [options]\n", progname);
+ fprintf (stderr, "\nAvailable options:\n");
+ fprintf (stderr, " -o FILE Write NDEF message to FILE\n");
+}
+
+int
+main (int argc, char *argv[])
+{
+ int error = EXIT_SUCCESS;
+ nfc_device *device = NULL;
+ FreefareTag *tags = NULL;
+
+ int ch;
+ char *ndef_file = NULL;
+ while ((ch = getopt (argc, argv, "o:")) != -1) {
+ switch (ch) {
+ case 'o':
+ ndef_file = optarg;
+ case '?':
+ usage (argv[0]);
+ exit (EXIT_FAILURE);
+ }
+ }
+
+ nfc_connstring devices[8];
+
+ size_t device_count;
+
+ nfc_context *context;
+ nfc_init (&context);
+ if (context == NULL)
+ errx (EXIT_FAILURE, "Unable to init libnfc (malloc)");
+
+ device_count = nfc_list_devices (context, devices, 8);
+ if (device_count <= 0)
+ errx (EXIT_FAILURE, "No NFC device found.");
+
+ for (size_t d = 0; d < device_count; d++) {
+ device = nfc_open (context, devices[d]);
+ if (!device) {
+ warnx ("nfc_open() failed.");
+ error = EXIT_FAILURE;
+ continue;
+ }
+
+ tags = freefare_get_tags (device);
+ if (!tags) {
+ nfc_close (device);
+ errx (EXIT_FAILURE, "Error listing FeliCa tag.");
+ }
+
+ for (int i = 0; (!error) && tags[i]; i++) {
+ int r = felica_connect (tags[i]);
+ if (r < 0)
+ errx (EXIT_FAILURE, "Cannot connect to FeliCa target");
+
+ int block = 1;
+ uint8_t ndef_message[NDEF_BUFFER_SIZE];
+ int ndef_space_left = NDEF_BUFFER_SIZE;
+ uint8_t *p = ndef_message;
+ ssize_t s;
+
+ // FIXME Instead of reading as much as we can, we should read until end of NDEF record (if any is found).
+ while ((ndef_space_left >= 16) && (s = felica_read (tags[i], FELICA_SC_RO, block++, p, 16)) > 0) {
+ p += s;
+ ndef_space_left -= s;
+ }
+
+ size_t ndef_message_length = 0;
+
+ if ((ndef_message[0] & 0x80) == 0x80) {
+ uint8_t *ndef_record;
+ do {
+ ndef_record = ndef_message + ndef_message_length;
+
+ size_t ndef_record_length = 1 + 1 + ndef_record[1];
+ size_t payload_length;
+ size_t payload_length_length;
+
+ if (ndef_record[0] & 0x10) {
+ /* Short record */
+ payload_length = ndef_record[2];
+ payload_length_length = 1;
+ } else {
+ payload_length = be32toh ((uint32_t) ndef_record + 2);
+ payload_length_length = 4;
+ }
+ ndef_record_length += payload_length_length;
+ ndef_record_length += payload_length;
+
+ if (ndef_record[0] & 0x08) {
+ ndef_record_length += 2;
+ }
+
+ ndef_message_length += ndef_record_length;
+ if (ndef_message_length > NDEF_BUFFER_SIZE)
+ errx (EXIT_FAILURE, "NDEF message truncated");
+
+ } while ((ndef_record[0] & 0x40) != 0x40);
+ }
+
+ if (ndef_message_length == 0)
+ errx (EXIT_FAILURE, "No NDEF message found");
+
+ FILE *f;
+ if (ndef_file)
+ f = fopen (ndef_file, "w");
+ else
+ f = stdout;
+
+ if (fwrite (ndef_message, ndef_message_length, 1, f) != 1)
+ err (EXIT_FAILURE, "Can't write NDEF message");
+
+ fclose (f);
+
+ felica_disconnect (tags[i]);
+ }
+
+ freefare_free_tags (tags);
+ nfc_close (device);
+ }
+ exit(EXIT_SUCCESS);
+}
diff --git a/libfreefare/Makefile.am b/libfreefare/Makefile.am
index 326e4c4..da2d652 100644
--- a/libfreefare/Makefile.am
+++ b/libfreefare/Makefile.am
@@ -3,7 +3,8 @@ AM_LDFLAGS = @LIBNFC_LIBS@
lib_LTLIBRARIES = libfreefare.la
-libfreefare_la_SOURCES = freefare.c \
+libfreefare_la_SOURCES = felica.c \
+ freefare.c \
mifare_classic.c \
mifare_ultralight.c \
mifare_desfire.c \
diff --git a/libfreefare/felica.c b/libfreefare/felica.c
new file mode 100644
index 0000000..ef06130
--- /dev/null
+++ b/libfreefare/felica.c
@@ -0,0 +1,211 @@
+/*-
+ * Copyright (C) 2015, Romain Tartiere.
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see
+ */
+
+/*
+ * This implementation was written based on information provided by the
+ * following documents:
+ *
+ * FeliCa Lite User's Manual
+ * Version 1.2
+ * No. M624-E01-20
+ */
+
+#include "config.h"
+
+#include
+#include
+#include
+
+#include
+
+#ifdef WITH_DEBUG
+# include
+#endif
+
+#include
+#include "freefare_internal.h"
+
+#define MAX_BLOCK_COUNT 8
+
+inline static
+ssize_t felica_transceive (FreefareTag tag, uint8_t *data_in, uint8_t *data_out, size_t data_out_length)
+{
+ DEBUG_XFER (data_in, data_in_length, "===> ");
+ ssize_t res = nfc_initiator_transceive_bytes (tag->device, data_in, data_in[0], data_out, data_out_length, 0);
+ DEBUG_XFER (data_out, res, "<=== ");
+ return res;
+}
+
+FreefareTag
+felica_tag_new (void)
+{
+ return malloc (sizeof (struct felica_tag));
+}
+
+void
+felica_tag_free (FreefareTag tag)
+{
+ free (tag);
+}
+
+
+
+int
+felica_connect (FreefareTag tag)
+{
+ ASSERT_INACTIVE (tag);
+ ASSERT_FELICA (tag);
+
+ nfc_target pnti;
+ nfc_modulation modulation = {
+ .nmt = NMT_FELICA,
+ .nbr = NBR_212,
+ };
+ if (nfc_initiator_select_passive_target (tag->device, modulation, tag->info.nti.nfi.abtId, 0, &pnti) >= 0) {
+ tag->active = 1;
+ } else {
+ errno = EIO;
+ return -1;
+ }
+ return 0;
+}
+
+int
+felica_disconnect (FreefareTag tag)
+{
+ ASSERT_ACTIVE (tag);
+ ASSERT_FELICA (tag);
+
+ if (nfc_initiator_deselect_target (tag->device) >= 0) {
+ tag->active = 0;
+ } else {
+ errno = EIO;
+ return -1;
+ }
+ return 0;
+}
+
+
+
+ssize_t
+felica_read_ex (FreefareTag tag, uint16_t service, uint8_t block_count, uint8_t blocks[], uint8_t *data, size_t length)
+{
+ ASSERT_ACTIVE (tag);
+ ASSERT_FELICA (tag);
+
+ assert (block_count <= MAX_BLOCK_COUNT);
+ assert (length == 16 * block_count);
+
+ DEBUG_FUNCTION();
+
+ uint8_t cmd[1 + 1 + 8 + 1 + 2 + 1 + 2 * MAX_BLOCK_COUNT] = {
+ 0x00, /* Length */
+ 0x06,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01,
+ 0x00, 0x00, /* Service */
+ 0x00,
+ /* Block ... */
+ };
+
+ uint8_t res[100];
+
+ cmd[0] = 14 + 2 * block_count;
+ memcpy (cmd + 2, tag->info.nti.nfi.abtId, 8);
+ cmd[11] = service;
+ cmd[12] = service >> 8;
+ cmd[13] = block_count;
+
+ for (int i = 0; i < block_count; i++) {
+ cmd[14 + 2*i] = 0x80;
+ cmd[14 + 2*i + 1] = blocks[i];
+ }
+
+ int cnt = felica_transceive (tag, cmd, res, sizeof (res));
+ if (cnt != 1 + 1 + 8 + 1 + 1 + 1 + 16 * block_count) {
+ return -1;
+ }
+ size_t len = MIN(res[12] * 16, length);
+ memcpy (data, res + 13, len);
+
+ return len;
+}
+
+ssize_t
+felica_read (FreefareTag tag, uint16_t service, uint8_t block, uint8_t *data, size_t length)
+{
+ uint8_t blocks[] = {
+ block
+ };
+
+ return felica_read_ex (tag, service, 1, blocks, data, length);
+}
+
+ssize_t
+felica_write_ex (FreefareTag tag, uint16_t service, uint8_t block_count, uint8_t blocks[], uint8_t *data, size_t length)
+{
+ ASSERT_ACTIVE (tag);
+ ASSERT_FELICA (tag);
+
+ DEBUG_FUNCTION();
+
+ assert (block_count <= MAX_BLOCK_COUNT);
+ assert (length == 16 * block_count);
+
+ uint8_t cmd[1 + 1 + 8 + 1 + 2 + 1 + 2 + 16 * MAX_BLOCK_COUNT] = {
+ 0x00, /* Length */
+ 0x08,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01,
+ 0x00, 0x00, /* Service */
+ 0x00,
+ /* Block ... */
+ /* n * 16 */
+ };
+
+ uint8_t res[12];
+
+ cmd[0] = 1 + 1 + 8 + 1 + 2 * 1 + 1 + 2 * 1 + 16 * block_count;
+ memcpy (cmd + 2, tag->info.nti.nfi.abtId, 8);
+ cmd[11] = service;
+ cmd[12] = service >> 8;
+ cmd[13] = block_count;
+
+ for (int i = 0; i < block_count; i++) {
+ cmd[14 + 2*i] = 0x80;
+ cmd[14 + 2*i + 1] = blocks[i];
+ }
+
+ memcpy (cmd + 14 + 2 * block_count, data, length);
+
+ ssize_t cnt = felica_transceive (tag, cmd, res, sizeof (res));
+
+ if (cnt != sizeof (res))
+ return -1;
+
+ return res[10] == 0 ? 0 : -1;
+}
+
+ssize_t
+felica_write (FreefareTag tag, uint16_t service, uint8_t block, uint8_t *data, size_t length)
+{
+ uint8_t blocks[] = {
+ block
+ };
+
+ return felica_write_ex (tag, service, 1, blocks, data, length);
+}
diff --git a/libfreefare/freefare.3 b/libfreefare/freefare.3
index bea39a9..a504a81 100644
--- a/libfreefare/freefare.3
+++ b/libfreefare/freefare.3
@@ -50,6 +50,7 @@ Mifare card manipulation library (libfreefare, \-lfreefare)
.Fn freefare_get_tags "nfc_device_t *device"
.Bd -literal
enum freefare_tag_type {
+ FELICA,
MIFARE_CLASSIC_1K,
MIFARE_CLASSIC_4K,
MIFARE_DESFIRE,
diff --git a/libfreefare/freefare.c b/libfreefare/freefare.c
index ed947f9..320440f 100644
--- a/libfreefare/freefare.c
+++ b/libfreefare/freefare.c
@@ -27,6 +27,7 @@
#define NXP_MANUFACTURER_CODE 0x04
struct supported_tag supported_tags[] = {
+ { FELICA, "FeliCA", NMT_FELICA, 0x00, 0, 0, { 0x00 }, NULL },
{ MIFARE_CLASSIC_1K, "Mifare Classic 1k", NMT_ISO14443A, 0x08, 0, 0, { 0x00 }, NULL },
{ MIFARE_CLASSIC_1K, "Mifare Classic 1k (Emulated)", NMT_ISO14443A, 0x28, 0, 0, { 0x00 }, NULL },
{ MIFARE_CLASSIC_1K, "Mifare Classic 1k (Emulated)", NMT_ISO14443A, 0x68, 0, 0, { 0x00 }, NULL },
@@ -55,6 +56,11 @@ freefare_tag_new (nfc_device *device, nfc_target target)
if (target.nm.nmt != supported_tags[i].modulation_type)
continue;
+ if (target.nm.nmt == NMT_FELICA) {
+ tag_info = &(supported_tags[i]);
+ found = true;
+ break;
+ }
if ((target.nm.nmt == NMT_ISO14443A) && ((target.nti.nai.szUidLen == 4) || (target.nti.nai.abtUid[0] == NXP_MANUFACTURER_CODE)) &&
(target.nti.nai.btSak == supported_tags[i].SAK) &&
(!supported_tags[i].ATS_min_length || ((target.nti.nai.szAtsLen >= supported_tags[i].ATS_min_length) &&
@@ -73,6 +79,9 @@ freefare_tag_new (nfc_device *device, nfc_target target)
/* Allocate memory for the found MIFARE target */
switch (tag_info->type) {
+ case FELICA:
+ tag = felica_tag_new ();
+ break;
case MIFARE_CLASSIC_1K:
case MIFARE_CLASSIC_4K:
tag = mifare_classic_tag_new ();
@@ -162,6 +171,26 @@ freefare_get_tags (nfc_device *device)
}
}
+ // Poll for a FELICA tag
+ modulation.nmt = NMT_FELICA;
+ modulation.nbr = NBR_424; // FIXME NBR_212 should also be supported
+ if ((candidates_count = nfc_initiator_list_passive_targets(device, modulation, candidates, MAX_CANDIDATES)) < 0)
+ return NULL;
+
+ for (int c = 0; c < candidates_count; c++) {
+ FreefareTag t;
+ if ((t = freefare_tag_new(device, candidates[c]))) {
+ /* (Re)Allocate memory for the found FELICA targets array */
+ FreefareTag *p = realloc (tags, (tag_count + 2) * sizeof (FreefareTag));
+ if (p)
+ tags = p;
+ else
+ return tags; // FAIL! Return what has been found so far.
+ tags[tag_count++] = t;
+ tags[tag_count] = NULL;
+ }
+ }
+
return tags;
}
@@ -189,10 +218,27 @@ freefare_get_tag_friendly_name (FreefareTag tag)
char *
freefare_get_tag_uid (FreefareTag tag)
{
- char *res;
- if ((res = malloc (2 * tag->info.nti.nai.szUidLen + 1))) {
- for (size_t i =0; i < tag->info.nti.nai.szUidLen; i++)
- snprintf (res + 2*i, 3, "%02x", tag->info.nti.nai.abtUid[i]);
+ char *res = NULL;
+ switch (tag->info.nm.nmt) {
+ case NMT_FELICA:
+ if ((res = malloc (16))) {
+ for (size_t i = 0; i < 8; i++)
+ snprintf (res + 2*i, 3, "%02x", tag->info.nti.nfi.abtId[i]);
+ }
+ break;
+ case NMT_ISO14443A:
+ if ((res = malloc (2 * tag->info.nti.nai.szUidLen + 1))) {
+ for (size_t i = 0; i < tag->info.nti.nai.szUidLen; i++)
+ snprintf (res + 2*i, 3, "%02x", tag->info.nti.nai.abtUid[i]);
+ }
+ break;
+ case NMT_DEP:
+ case NMT_ISO14443B2CT:
+ case NMT_ISO14443B2SR:
+ case NMT_ISO14443B:
+ case NMT_ISO14443BI:
+ case NMT_JEWEL:
+ res = strdup ("UNKNOWN");
}
return res;
}
@@ -213,6 +259,9 @@ freefare_free_tag (FreefareTag tag)
{
if (tag) {
switch (tag->tag_info->type) {
+ case FELICA:
+ felica_tag_free (tag);
+ break;
case MIFARE_CLASSIC_1K:
case MIFARE_CLASSIC_4K:
mifare_classic_tag_free (tag);
diff --git a/libfreefare/freefare.h b/libfreefare/freefare.h
index 6a6183c..ca89a40 100644
--- a/libfreefare/freefare.h
+++ b/libfreefare/freefare.h
@@ -29,6 +29,7 @@
#endif // __cplusplus
enum freefare_tag_type {
+ FELICA,
// MIFARE_MINI,
MIFARE_CLASSIC_1K,
MIFARE_CLASSIC_4K,
@@ -66,6 +67,17 @@ const char *freefare_strerror (FreefareTag tag);
int freefare_strerror_r (FreefareTag tag, char *buffer, size_t len);
void freefare_perror (FreefareTag tag, const char *string);
+#define FELICA_SC_RW 0x0009
+#define FELICA_SC_RO 0x000b
+
+int felica_connect (FreefareTag tag);
+int felica_disconnect (FreefareTag tag);
+
+ssize_t felica_read (FreefareTag tag, uint16_t service, uint8_t block, uint8_t *data, size_t length);
+ssize_t felica_read_ex (FreefareTag tag, uint16_t service, uint8_t block_count, uint8_t blocks[], uint8_t *data, size_t length);
+ssize_t felica_write (FreefareTag tag, uint16_t service, uint8_t block, uint8_t *data, size_t length);
+ssize_t felica_write_ex (FreefareTag tag, uint16_t service, uint8_t block_count, uint8_t blocks[], uint8_t *data, size_t length);
+
int mifare_ultralight_connect (FreefareTag tag);
int mifare_ultralight_disconnect (FreefareTag tag);
diff --git a/libfreefare/freefare_internal.h b/libfreefare/freefare_internal.h
index 6df3f63..b7779a2 100644
--- a/libfreefare/freefare_internal.h
+++ b/libfreefare/freefare_internal.h
@@ -102,6 +102,8 @@ struct mad_sector_0x00;
struct mad_sector_0x10;
void nxp_crc (uint8_t *crc, const uint8_t value);
+FreefareTag felica_tag_new (void);
+void felica_tag_free (FreefareTag tag);
FreefareTag mifare_classic_tag_new (void);
void mifare_classic_tag_free (FreefareTag tag);
FreefareTag mifare_desfire_tag_new (void);
@@ -189,6 +191,10 @@ struct freefare_tag {
int active;
};
+struct felica_tag {
+ struct freefare_tag __tag;
+};
+
struct mifare_classic_tag {
struct freefare_tag __tag;
@@ -262,6 +268,7 @@ struct mifare_ultralight_tag {
#define ASSERT_ACTIVE(tag) do { if (!tag->active) return errno = ENXIO, -1; } while (0)
#define ASSERT_INACTIVE(tag) do { if (tag->active) return errno = ENXIO, -1; } while (0)
+#define ASSERT_FELICA(tag) do { if (tag->tag_info->type != FELICA) return errno = ENODEV, -1; } while (0)
#define ASSERT_MIFARE_CLASSIC(tag) do { if ((tag->tag_info->type != MIFARE_CLASSIC_1K) && (tag->tag_info->type != MIFARE_CLASSIC_4K)) return errno = ENODEV, -1; } while (0)
#define ASSERT_MIFARE_DESFIRE(tag) do { if (tag->tag_info->type != MIFARE_DESFIRE) return errno = ENODEV, -1; } while (0)
#define IS_MIFARE_ULTRALIGHT_C(tag) (tag->tag_info->type == MIFARE_ULTRALIGHT_C)
@@ -274,6 +281,7 @@ struct mifare_ultralight_tag {
* This macros are intended to provide a convenient way to cast abstract
* FreefareTag structures to concrete Tags (e.g. MIFARE Classic tag).
*/
+#define FELICA(tag) ((struct felica_tag *) tag)
#define MIFARE_CLASSIC(tag) ((struct mifare_classic_tag *) tag)
#define MIFARE_DESFIRE(tag) ((struct mifare_desfire_tag *) tag)
#define MIFARE_ULTRALIGHT(tag) ((struct mifare_ultralight_tag *) tag)
diff --git a/test/Makefile.am b/test/Makefile.am
index e4e3151..56a2d04 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -11,6 +11,7 @@ TESTS = run-test.sh
TESTS_ENVIRONMENT = NO_MAKE=yes CUTTER="$(CUTTER)"
cutter_unit_test_libs = \
+ test_felica.la \
test_mad.la \
test_mifare_application.la \
test_mifare_classic.la \
@@ -33,6 +34,11 @@ endif
AM_LDFLAGS = -module -rpath $(libdir) -avoid-version -no-undefined
+test_felica_la_SOURCES = test_felica.c \
+ felica_fixture.c \
+ felica_fixture.h
+test_felica_la_LIBADD = $(top_builddir)/libfreefare/libfreefare.la
+
test_mad_la_SOURCES = test_mad.c
test_mad_la_LIBADD = $(top_builddir)/libfreefare/libfreefare.la
diff --git a/test/felica_fixture.c b/test/felica_fixture.c
new file mode 100644
index 0000000..3beb9fd
--- /dev/null
+++ b/test/felica_fixture.c
@@ -0,0 +1,82 @@
+/*-
+ * Copyright (C) 2015, Romain Tartiere
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see
+ */
+
+#include
+#include
+
+#include "felica_fixture.h"
+
+static nfc_context *context;
+static nfc_device *device = NULL;
+static FreefareTag *tags = NULL;
+FreefareTag tag = NULL;
+
+void
+cut_setup (void)
+{
+ int res;
+ nfc_connstring devices[8];
+ size_t device_count;
+
+ nfc_init (&context);
+ cut_assert_not_null (context, cut_message ("Unable to init libnfc (malloc)"));
+
+ device_count = nfc_list_devices (context, devices, 8);
+ if (device_count <= 0)
+ cut_omit ("No device found");
+
+ for (size_t i = 0; i < device_count; i++) {
+ device = nfc_open (context, devices[i]);
+ if (!device)
+ cut_omit ("nfc_open() failed.");
+
+ tags = freefare_get_tags (device);
+ cut_assert_not_null (tags, cut_message ("freefare_get_tags() failed"));
+
+ tag = NULL;
+ for (int i=0; tags[i]; i++) {
+ if (freefare_get_tag_type(tags[i]) == FELICA) {
+ tag = tags[i];
+ res = felica_connect (tag);
+ cut_assert_equal_int (0, res, cut_message ("felica_connect() failed"));
+ return;
+ }
+ }
+ nfc_close (device);
+ device = NULL;
+ freefare_free_tags (tags);
+ tags = NULL;
+ }
+ cut_omit ("No FeliCa tag on NFC device");
+}
+
+void
+cut_teardown (void)
+{
+ if (tag)
+ mifare_classic_disconnect (tag);
+
+ if (tags) {
+ freefare_free_tags (tags);
+ tags = NULL;
+ }
+
+ if (device)
+ nfc_close (device);
+
+ nfc_exit (context);
+}
diff --git a/test/felica_fixture.h b/test/felica_fixture.h
new file mode 100644
index 0000000..3548d26
--- /dev/null
+++ b/test/felica_fixture.h
@@ -0,0 +1,18 @@
+/*-
+ * Copyright (C) 2015, Romain Tartiere.
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see
+ */
+
+extern FreefareTag tag;
diff --git a/test/test_felica.c b/test/test_felica.c
new file mode 100644
index 0000000..d6c21cc
--- /dev/null
+++ b/test/test_felica.c
@@ -0,0 +1,38 @@
+#include
+
+#include
+
+#include "felica_fixture.h"
+
+void
+test_felica_read_without_encryption (void)
+{
+ uint8_t buffer[64];
+
+ int res = felica_read (tag, FELICA_SC_RO, 0x00, buffer, 16);
+ cut_assert_equal_int (16, res);
+
+ uint8_t blocks[] = {
+ 0x02,
+ 0x03,
+ 0x04,
+ };
+
+ res = felica_read_ex (tag, FELICA_SC_RO, 3, blocks, buffer, 3 * 16);
+ cut_assert_equal_int (3 * 16, res);
+}
+
+void
+test_felica_write_without_encryption (void)
+{
+ uint8_t buffer[16] = {
+ 0x00, 0x01, 0x02, 0x03,
+ 0x03, 0x04, 0x05, 0x06,
+ 0x07, 0x08, 0x09, 0x0a,
+ 0x0b, 0x0c, 0x0d, 0x0e,
+ };
+
+ int res = felica_write (tag, FELICA_SC_RW, 0x0a, buffer, sizeof (buffer));
+
+ cut_assert_equal_int (0, res);
+}