blob: 600b3d7c0b09d772a8b6099f6c1b3e6c752b5c7d [file] [log] [blame] [edit]
/*
*
* neard - Near Field Communication manager
*
* Copyright (C) 2011 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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 General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdint.h>
#include <errno.h>
#include <stdbool.h>
#include <string.h>
#include <sys/socket.h>
#include <linux/socket.h>
#include <near/nfc_copy.h>
#include <near/plugin.h>
#include <near/log.h>
#include <near/types.h>
#include <near/adapter.h>
#include <near/tag.h>
#include <near/ndef.h>
#include <near/tlv.h>
#include <stdio.h>
#define CMD_READ_ALL 0x00 /* Read seg 0 (incl: HR) */
#define CMD_READ_SEGS 0x10 /* Read 16 blocks (128 bytes) */
#define CMD_RID 0x78 /* Read tag UID */
#define CMD_WRITE_E 0x53 /* Write with erase */
#define CMD_WRITE_NE 0x1A /* Write no erase */
#define CMD_WRITE_E8 0x54 /* Write 8 bytes with erase */
#define CMD_WRITE_NE8 0x1B /* Write 8 bytes no erase */
#define OFFSET_STATUS_CMD 0x00
#define OFFSET_HEADER_ROM 0x01
#define HR0_TYPE1_STATIC_96B 0x11
#define HR0_TYPE1_DYNAMIC_512B 0x12
#define HR0_TYPE2_HIGH 0x10
#define HR0_TYPE2_LOW 0x0F
#define BLOCK_SIZE 8
#define LEN_STATUS_BYTE 0x01 /* Status byte */
#define LEN_SPEC_BYTES (LEN_STATUS_BYTE + 0x02) /* HRx */
#define LEN_UID_BYTES (LEN_STATUS_BYTE + 0x07) /* UID bytes */
#define LEN_CC_BYTES 0x04 /* Capab. container */
#define LEN_DYN_BYTES 0x0A /* Bytes CtrlIT and TLV - Dyn. only */
#define TYPE1_MAGIC 0xE1
#define TAG_T1_DATA_UID(data) ((data) + LEN_SPEC_BYTES)
#define TAG_T1_DATA_CC(data) ((data) + LEN_SPEC_BYTES + LEN_UID_BYTES)
#define TAG_T1_DATA_LENGTH(cc) ((cc[2] + 1) * 8 - LEN_CC_BYTES)
#define TAG_T1_DATA_NFC(cc) ((cc)[0] & TYPE1_MAGIC)
#define TYPE1_NOWRITE_ACCESS 0x0F
#define TAG_T1_WRITE_FLAG(cc) ((cc)[3] & TYPE1_NOWRITE_ACCESS)
#define TAG_T1_SEGMENT_SIZE 128
/* Max writable data size */
#define TYPE1_STATIC_MAX_DATA_SIZE_96B 95
#define TYPE1_STATIC_MAX_DATA_SIZE_512B 469
#define UID_LENGTH 4
#define TYPE1_TAG_VER_1_1 0x11
#define TYPE1_TAG_STATIC_SIZE_120 0x0E
#define TYPE1_READ_WRITE_ACCESS 0x00
#define TYPE1_STATIC_TAG_DATA_LENGTH_96B 116
#define TYPE1_STATIC_TAG_DATA_LENGTH_512B 508
#define RESERVED_BLOCK 0x0D
#define FIRST_DYNAMIC_BLOCK 0x02
struct type1_cmd {
uint8_t cmd;
uint8_t addr;
uint8_t data[1];
uint8_t uid[UID_LENGTH];
} __attribute__((packed));
struct type1_cmd8 {
uint8_t cmd;
uint8_t addr;
uint8_t data[8];
uint8_t uid[UID_LENGTH];
} __attribute__((packed));
struct type1_cmd_rseg {
uint8_t cmd;
uint8_t addr;
uint8_t data[8];
uint8_t uid[UID_LENGTH];
} __attribute__((packed));
struct type1_tag {
uint32_t adapter_idx;
uint16_t current_block;
uint16_t current_seg;
uint16_t last_seg;
uint16_t data_read;
uint8_t uid[UID_LENGTH];
near_tag_io_cb cb;
struct near_tag *tag;
};
struct t1_cookie {
uint32_t adapter_idx;
uint32_t target_idx;
uint8_t uid[UID_LENGTH];
uint32_t current_block; /* Static tag */
uint32_t current_byte; /* Static tag */
struct near_ndef_message *ndef;
near_tag_io_cb cb;
uint8_t cc[LEN_CC_BYTES];
};
enum type1_tag_version {
TYPE1_TAG_UNKNOWN,
TYPE1_TAG_96B,
TYPE1_TAG_512B
} t1_tag_version;
static void t1_init_cmd(struct type1_tag *tag, struct type1_cmd *cmd)
{
if (!tag || !cmd)
return;
memcpy(cmd->uid, tag->uid, UID_LENGTH);
}
static int t1_cookie_release(int err, void *data)
{
struct t1_cookie *cookie = data;
DBG("%p", cookie);
if (!cookie)
return err;
if (cookie->cb)
cookie->cb(cookie->adapter_idx, cookie->target_idx, err);
if (cookie->ndef)
g_free(cookie->ndef->data);
g_free(cookie->ndef);
g_free(cookie);
cookie = NULL;
return err;
}
/* Read segments (128 bytes) and store them to the tag data block */
static int data_recv(uint8_t *resp, int length, void *data)
{
struct type1_tag *t1_tag = data;
struct type1_cmd t1_cmd;
uint8_t *tagdata;
size_t data_length;
DBG("%d", length);
if (length < 0)
return length;
length = length - LEN_STATUS_BYTE; /* ignore first byte */
/* Add data to tag mem */
tagdata = near_tag_get_data(t1_tag->tag, &data_length);
/* Check that we have enough free space */
if (data_length - t1_tag->data_read < (uint)length)
return -EINVAL;
memcpy(tagdata + t1_tag->data_read, resp + 2, length - LEN_STATUS_BYTE);
/* Next segment */
t1_tag->data_read = t1_tag->data_read + length - LEN_STATUS_BYTE;
t1_tag->current_seg = t1_tag->current_seg + 1;
if (t1_tag->current_seg <= t1_tag->last_seg) {
struct type1_cmd_rseg t1_cmd_rseg;
/* RSEG cmd */
t1_init_cmd(t1_tag, &t1_cmd);
t1_cmd_rseg.cmd = CMD_READ_SEGS;
memset(t1_cmd_rseg.data, 0x00, 8);
memcpy(t1_cmd_rseg.uid, t1_cmd.uid, sizeof(t1_cmd.uid));
t1_cmd_rseg.addr = (t1_tag->current_seg << 4) & 0xFF;
return near_adapter_send(t1_tag->adapter_idx,
(uint8_t *) &t1_cmd_rseg, sizeof(t1_cmd_rseg),
data_recv, t1_tag, NULL);
} else { /* This is the end */
GList *records;
DBG("READ complete");
records = near_tlv_parse(tagdata, data_length);
near_tag_add_records(t1_tag->tag, records, t1_tag->cb, 0);
/* free memory */
g_free(t1_tag);
return 0;
}
}
/*
* The dynamic read function:
* Bytes [0..3] : CC
* [4..8]: TLV Lock ControlIT (0x01, 0x03, v1, V2, V3)
* [9..13]: TLV Reserved Memory Control (0x02, 0x03, V1, V2, V3)
* [14..]: TLV NDEF (0x03, L0, L1, L2, V1,V2 ...)
*/
static int read_dynamic_tag(uint8_t *cc, int length, void *data,
uint8_t tag_mem_size)
{
struct type1_tag *t1_tag = data;
struct type1_cmd t1_cmd;
struct type1_cmd_rseg t1_cmd_rseg;
uint8_t *tagdata;
uint8_t *pndef;
size_t data_length;
DBG("Dynamic Mode");
tagdata = near_tag_get_data(t1_tag->tag, &data_length);
/* Skip capability container bytes */
pndef = cc + 14; /* right after CC bytes */
/*
* Save first NFC bytes to tag memory
* 10 blocks[0x3..0xC] of 8 bytes + 2 bytes from block 2
*/
memcpy(tagdata, pndef, 10 * BLOCK_SIZE + 2);
t1_tag->data_read = 10 * BLOCK_SIZE + 2;
/* Read the next one, up to the end of the data area */
t1_tag->current_seg = 1;
if (tag_mem_size == HR0_TYPE1_DYNAMIC_512B)
t1_tag->last_seg = 3;
else
return -ENOTSUP;
/* RSEG cmd */
t1_init_cmd(t1_tag, &t1_cmd);
/* T1 read segment */
t1_cmd_rseg.cmd = CMD_READ_SEGS;
memset(t1_cmd_rseg.data, 0x00, 8);
memcpy(t1_cmd_rseg.uid, t1_cmd.uid, sizeof(t1_cmd.uid));
/* 5.3.3 ADDS operand is [b8..b5] */
t1_cmd_rseg.addr = (t1_tag->current_seg << 4) & 0xFF;
return near_adapter_send(t1_tag->adapter_idx,
(uint8_t *)&t1_cmd_rseg, sizeof(t1_cmd_rseg),
data_recv, t1_tag, NULL);
}
static int meta_recv(uint8_t *resp, int length, void *data)
{
struct t1_cookie *cookie = data;
struct near_tag *tag;
struct type1_tag *t1_tag;
uint8_t *cc;
int err = -EOPNOTSUPP;
DBG("%d", length);
if (length < 0) {
err = length;
goto out_err;
}
/* First byte is cmd status */
if (resp[OFFSET_STATUS_CMD] != 0) {
DBG("Command failed: 0x%x", resp[OFFSET_STATUS_CMD]);
err = -EIO;
goto out_err;
}
/* Set tag type */
if (resp[OFFSET_HEADER_ROM] == HR0_TYPE1_STATIC_96B)
t1_tag_version = TYPE1_TAG_96B;
else if (resp[OFFSET_HEADER_ROM] == HR0_TYPE1_DYNAMIC_512B)
t1_tag_version = TYPE1_TAG_512B;
else
t1_tag_version = TYPE1_TAG_UNKNOWN;
/* Check Magic NFC tag */
cc = TAG_T1_DATA_CC(resp);
if (TAG_T1_DATA_NFC(cc) == 0) {
if (resp[OFFSET_HEADER_ROM] == HR0_TYPE1_STATIC_96B) {
err = near_tag_add_data(cookie->adapter_idx,
cookie->target_idx,
NULL,
TYPE1_STATIC_TAG_DATA_LENGTH_96B);
} else if (resp[OFFSET_HEADER_ROM] == HR0_TYPE1_DYNAMIC_512B) {
err = near_tag_add_data(cookie->adapter_idx,
cookie->target_idx,
NULL,
TYPE1_STATIC_TAG_DATA_LENGTH_512B);
} else {
near_error("Not a valid NFC magic tag 0x%x", cc[0]);
err = -EINVAL;
goto out_err;
}
} else {
/* Add data to the tag */
err = near_tag_add_data(cookie->adapter_idx, cookie->target_idx,
NULL, TAG_T1_DATA_LENGTH(cc));
}
if (err < 0)
goto out_err;
tag = near_tag_get_tag(cookie->adapter_idx, cookie->target_idx);
if (!tag) {
err = -ENOMEM;
goto out_err;
}
t1_tag = g_try_malloc0(sizeof(struct type1_tag));
if (!t1_tag) {
err = -ENOMEM;
goto out_err;
}
t1_tag->adapter_idx = cookie->adapter_idx;
t1_tag->cb = cookie->cb;
t1_tag->tag = tag;
memcpy(t1_tag->uid, cookie->uid, UID_LENGTH);
/* Set the ReadWrite flag */
if (TAG_T1_WRITE_FLAG(cc) == TYPE1_NOWRITE_ACCESS)
near_tag_set_ro(tag, TRUE);
else
near_tag_set_ro(tag, FALSE);
/* Check Static or Dynamic memory model */
if (resp[OFFSET_HEADER_ROM] == HR0_TYPE1_STATIC_96B) {
uint8_t *tagdata;
size_t data_length;
GList *records;
if (TAG_T1_DATA_NFC(cc) == 0)
near_tag_set_blank(tag, TRUE);
else
near_tag_set_blank(tag, FALSE);
DBG("READ Static complete");
tagdata = near_tag_get_data(t1_tag->tag, &data_length);
/* Check that we have enough free space */
if (data_length < (size_t)TAG_T1_DATA_LENGTH(cc))
return -EINVAL;
memcpy(tagdata, cc + LEN_CC_BYTES, TAG_T1_DATA_LENGTH(cc));
near_tag_set_memory_layout(tag, NEAR_TAG_MEMORY_STATIC);
records = near_tlv_parse(tagdata, data_length);
near_tag_add_records(t1_tag->tag, records, t1_tag->cb, 0);
g_free(t1_tag);
return 0;
} else if ((resp[OFFSET_HEADER_ROM] & 0xF0) == HR0_TYPE2_HIGH) {
near_tag_set_memory_layout(tag, NEAR_TAG_MEMORY_DYNAMIC);
err = read_dynamic_tag(cc, length, t1_tag,
resp[OFFSET_HEADER_ROM]);
/*
* As reading isn't complete,
* callback shouldn't be called while freeing the cookie
*/
cookie->cb = NULL;
} else {
err = -EOPNOTSUPP;
}
if (err < 0)
g_free(t1_tag);
out_err:
DBG("err %d", err);
return t1_cookie_release(err, cookie);
}
/*
* READALL to read a maximum of 124 bytes.
* This cmd is common to static and dynamic targets
* This should allow to get the HR0 byte.
*/
static int rid_resp(uint8_t *resp, int length, void *data)
{
struct t1_cookie *cookie = data;
struct type1_cmd t1_cmd;
uint8_t *uid;
int err;
DBG("");
/* First byte is cmd status */
if (resp[OFFSET_STATUS_CMD] != 0) {
DBG("Command failed: 0x%x", resp[OFFSET_STATUS_CMD]);
err = -EIO;
goto out_err;
}
uid = TAG_T1_DATA_UID(resp);
DBG("UID 0x%x 0x%x 0x%x 0x%x", uid[0], uid[1], uid[2], uid[3]);
near_tag_set_nfcid(cookie->adapter_idx, cookie->target_idx,
uid, UID_LENGTH);
t1_cmd.cmd = CMD_READ_ALL; /* Read ALL cmd give 124 bytes */
t1_cmd.addr = 0; /* NA */
t1_cmd.data[0] = 0;
memcpy(t1_cmd.uid, uid, UID_LENGTH);
memcpy(cookie->uid, uid, UID_LENGTH);
return near_adapter_send(cookie->adapter_idx,
(uint8_t *)&t1_cmd, sizeof(t1_cmd),
meta_recv, cookie, t1_cookie_release);
out_err:
DBG("err %d", err);
return t1_cookie_release(err, cookie);
}
static int nfctype1_read_meta(uint32_t adapter_idx, uint32_t target_idx,
near_tag_io_cb cb, uint8_t *uid)
{
struct type1_cmd cmd;
struct t1_cookie *cookie;
DBG("");
memset(&cmd, 0, sizeof(cmd));
cookie = g_try_malloc0(sizeof(struct t1_cookie));
if (!cookie)
return -ENOMEM;
cookie->adapter_idx = adapter_idx;
cookie->target_idx = target_idx;
cookie->cb = cb;
if (uid) {
cmd.cmd = CMD_READ_ALL; /* Read ALL cmd give 124 bytes */
memcpy(cmd.uid, uid, UID_LENGTH);
memcpy(cookie->uid, uid, UID_LENGTH);
return near_adapter_send(adapter_idx, (uint8_t *) &cmd,
sizeof(cmd), meta_recv, cookie,
t1_cookie_release);
} else {
cmd.cmd = CMD_RID;
return near_adapter_send(adapter_idx, (uint8_t *) &cmd,
sizeof(cmd), rid_resp, cookie,
t1_cookie_release);
}
}
/* First step: RID to get the tag UID */
static int nfctype1_read(uint32_t adapter_idx,
uint32_t target_idx, near_tag_io_cb cb)
{
uint8_t *uid;
uint8_t uid_length;
int err;
DBG("");
t1_tag_version = TYPE1_TAG_UNKNOWN;
uid = near_tag_get_nfcid(adapter_idx, target_idx, &uid_length);
if (!uid || uid_length != UID_LENGTH) {
if (uid) {
near_error("Invalid UID");
g_free(uid);
return -EINVAL;
}
return nfctype1_read_meta(adapter_idx, target_idx, cb, NULL);
}
err = nfctype1_read_meta(adapter_idx, target_idx, cb, uid);
g_free(uid);
return err;
}
static int write_nmn_e1_resp(uint8_t *resp, int length, void *data)
{
int err = 0;
struct t1_cookie *cookie = data;
DBG("");
if (length < 0)
err = length;
if (resp[OFFSET_STATUS_CMD] != 0)
err = -EIO;
DBG("Done writing");
return t1_cookie_release(err, cookie);
}
static int write_nmn_e1(struct t1_cookie *cookie)
{
struct type1_cmd cmd;
DBG("");
cmd.cmd = CMD_WRITE_E;
cmd.addr = 0x08;
cmd.data[0] = TYPE1_MAGIC;
memcpy(cmd.uid, cookie->uid, UID_LENGTH);
return near_adapter_send(cookie->adapter_idx, (uint8_t *) &cmd,
sizeof(cmd), write_nmn_e1_resp, cookie,
t1_cookie_release);
}
static int data_write_resp(uint8_t *resp, int length, void *data)
{
struct t1_cookie *cookie = data;
struct type1_cmd cmd;
struct type1_cmd8 cmd8;
uint8_t data_block[BLOCK_SIZE];
uint8_t addr = 0;
int err = -EIO;
DBG("");
if (length < 0) {
err = length;
goto out_err;
}
if (resp[OFFSET_STATUS_CMD] != 0) {
err = -EIO;
goto out_err;
}
if (cookie->ndef->offset >= cookie->ndef->length)
return write_nmn_e1(cookie);
if (t1_tag_version == TYPE1_TAG_96B) {
/* Static */
if (cookie->current_byte >= BLOCK_SIZE) {
cookie->current_byte = 0;
cookie->current_block++;
}
cmd.cmd = CMD_WRITE_E;
addr = cookie->current_block << 3;
cmd.addr = addr | (cookie->current_byte & 0x7);
cmd.data[0] = cookie->ndef->data[cookie->ndef->offset];
memcpy(cmd.uid, cookie->uid, UID_LENGTH);
cookie->ndef->offset++;
cookie->current_byte++;
err = near_adapter_send(cookie->adapter_idx, (uint8_t *) &cmd,
sizeof(cmd), data_write_resp, cookie,
NULL);
if (err < 0)
goto out_err;
return err;
} else {
/* Dynamic */
/* Skip reserved and lock/OTP blocks */
if (cookie->current_block == RESERVED_BLOCK)
cookie->current_block += 3;
if ((cookie->ndef->length
- cookie->ndef->offset) >= BLOCK_SIZE) {
if (cookie->current_block == FIRST_DYNAMIC_BLOCK) {
cmd.cmd = CMD_WRITE_E;
cmd.addr = (FIRST_DYNAMIC_BLOCK << 3)
| (cookie->current_byte & 0x7);
cmd.data[0] =
cookie->ndef->data[cookie->ndef->offset];
memcpy(cmd.uid, cookie->uid,
UID_LENGTH);
cookie->ndef->offset++;
cookie->current_byte++;
if (cookie->current_byte >= BLOCK_SIZE)
cookie->current_block++;
goto send_byte; /* Write 2 first bytes */
} else {
cmd8.cmd = CMD_WRITE_E8;
cmd8.addr = cookie->current_block;
memcpy(cmd8.data,
&cookie->ndef->data[cookie->ndef->offset],
BLOCK_SIZE);
cookie->ndef->offset += BLOCK_SIZE;
}
} else {
uint8_t remaining_bytes = 0;
memset(data_block, 0x00, BLOCK_SIZE);
cmd8.cmd = CMD_WRITE_E8;
cmd8.addr = cookie->current_block;
remaining_bytes = cookie->ndef->length
- cookie->ndef->offset;
memcpy(data_block,
&cookie->ndef->data[cookie->ndef->offset],
remaining_bytes);
memcpy(cmd8.data, data_block, BLOCK_SIZE);
cookie->ndef->offset += remaining_bytes;
}
memcpy(cmd8.uid, cookie->uid, UID_LENGTH);
cookie->current_block++;
return near_adapter_send(cookie->adapter_idx,
(uint8_t *)&cmd8,
sizeof(cmd8), data_write_resp, cookie,
t1_cookie_release);
send_byte:
return near_adapter_send(cookie->adapter_idx,
(uint8_t *)&cmd,
sizeof(cmd), data_write_resp,
cookie,
t1_cookie_release);
}
out_err:
return t1_cookie_release(err, cookie);
}
static int data_write(uint32_t adapter_idx, uint32_t target_idx,
struct near_ndef_message *ndef, near_tag_io_cb cb)
{
int err;
struct type1_cmd cmd;
struct t1_cookie *cookie;
uint8_t *uid, uid_length;
DBG("");
uid = near_tag_get_nfcid(adapter_idx, target_idx, &uid_length);
if (!uid || uid_length != UID_LENGTH) {
near_error("Invalid type 1 UID");
err = -EINVAL;
goto out_err;
}
cmd.cmd = CMD_WRITE_E;
cmd.data[0] = 0x00;
memcpy(cmd.uid, uid, UID_LENGTH);
cookie = g_try_malloc0(sizeof(struct t1_cookie));
if (!cookie) {
g_free(uid);
err = -ENOMEM;
goto out_err;
}
cookie->adapter_idx = adapter_idx;
cookie->target_idx = target_idx;
memcpy(cookie->uid, uid, UID_LENGTH);
cookie->ndef = ndef;
cookie->cb = cb;
cmd.addr = 0x08;
if (t1_tag_version == TYPE1_TAG_96B) {
/* Static */
cookie->current_block = 1;
cookie->current_byte = 4;
} else {
/* Dynamic */
cookie->current_block = 2;
cookie->current_byte = 6;
}
g_free(uid);
return near_adapter_send(cookie->adapter_idx, (uint8_t *) &cmd,
sizeof(cmd), data_write_resp, cookie,
t1_cookie_release);
out_err:
if (cb)
cb(adapter_idx, target_idx, err);
return err;
}
/*
* The writing of a new NDEF message SHALL occur as follows:
* Write NMN = 00h to indicate that no valid NDEF message is present
* during writing to allow error detection in the event that the tag
* is removed from the field prior to completion of operation.
* Write VNo and RWA if required
* Write NDEF Message TLV
* Write NDEF Message data
* Write NMN = E1h as the last byte to be written
*/
static int nfctype1_write(uint32_t adapter_idx, uint32_t target_idx,
struct near_ndef_message *ndef,
near_tag_io_cb cb)
{
struct near_tag *tag;
int err;
size_t t1_size = 0;
DBG("");
if (!ndef || !cb) {
err = -EINVAL;
goto out_err;
}
tag = near_tag_get_tag(adapter_idx, target_idx);
if (!tag) {
err = -EINVAL;
goto out_err;
}
/* This check is valid for only static tags.
* Max data length on Type 1 Tag including TLV's
* is TYPE1_STATIC_MAX_DATA_SIZE */
if (near_tag_get_memory_layout(tag) == NEAR_TAG_MEMORY_STATIC) {
if (t1_tag_version == TYPE1_TAG_96B)
t1_size = TYPE1_STATIC_MAX_DATA_SIZE_96B;
else
t1_size = 0;
if ((ndef->length + 3) > t1_size) {
near_error("not enough space on tag");
err = -ENOSPC;
goto out_err;
}
} else if (near_tag_get_memory_layout(tag) ==
NEAR_TAG_MEMORY_DYNAMIC) {
if (t1_tag_version == TYPE1_TAG_512B)
t1_size = TYPE1_STATIC_MAX_DATA_SIZE_512B;
else
t1_size = 0;
if ((ndef->length + 3) > t1_size) {
near_error("not enough space on tag");
err = -ENOSPC;
goto out_err;
}
}
return data_write(adapter_idx, target_idx, ndef, cb);
out_err:
if (cb)
cb(adapter_idx, target_idx, err);
return err;
}
static int check_presence(uint8_t *resp, int length, void *data)
{
struct t1_cookie *cookie = data;
int err = 0;
DBG("%d", length);
if (length < 0)
err = -EIO;
return t1_cookie_release(err, cookie);
}
static int nfctype1_check_presence(uint32_t adapter_idx,
uint32_t target_idx, near_tag_io_cb cb)
{
struct type1_cmd t1_cmd;
struct t1_cookie *cookie;
uint8_t *uid, uid_length;
DBG("");
uid = near_tag_get_nfcid(adapter_idx, target_idx, &uid_length);
if (!uid || uid_length != UID_LENGTH) {
near_error("Invalid type 1 UID");
return -EINVAL;
}
t1_cmd.cmd = CMD_READ_ALL; /* Read ALL cmd give 124 bytes */
t1_cmd.addr = 0; /* NA */
t1_cmd.data[0] = 0;
memcpy(t1_cmd.uid, uid, UID_LENGTH);
g_free(uid);
cookie = g_try_malloc0(sizeof(struct t1_cookie));
if (!cookie)
return -ENOMEM;
cookie->adapter_idx = adapter_idx;
cookie->target_idx = target_idx;
cookie->cb = cb;
return near_adapter_send(adapter_idx, (uint8_t *) &t1_cmd,
sizeof(t1_cmd), check_presence, cookie,
t1_cookie_release);
}
static int format_resp(uint8_t *resp, int length, void *data)
{
int err = 0;
struct t1_cookie *cookie = data;
struct near_tag *tag;
struct type1_cmd cmd;
uint8_t addr;
DBG("");
if (length < 0 || resp[0] != 0) {
err = -EIO;
goto out_err;
}
if (cookie->current_byte < LEN_CC_BYTES) {
cmd.cmd = CMD_WRITE_E;
addr = cookie->current_block << 3;
cmd.addr = addr | (cookie->current_byte & 0x7);
cmd.data[0] = cookie->cc[cookie->current_byte];
cookie->current_byte++;
memcpy(cmd.uid, cookie->uid, UID_LENGTH);
return near_adapter_send(cookie->adapter_idx, (uint8_t *) &cmd,
sizeof(cmd), format_resp, cookie,
t1_cookie_release);
} else {
tag = near_tag_get_tag(cookie->adapter_idx, cookie->target_idx);
if (!tag) {
err = -EINVAL;
goto out_err;
}
DBG("Done formatting");
near_tag_set_blank(tag, FALSE);
}
out_err:
return t1_cookie_release(err, cookie);
}
static int nfctype1_format(uint32_t adapter_idx, uint32_t target_idx,
near_tag_io_cb cb)
{
int err;
struct near_tag *tag;
struct type1_cmd cmd;
struct t1_cookie *cookie;
uint8_t *uid, uid_length, addr;
DBG("");
tag = near_tag_get_tag(adapter_idx, target_idx);
if (!tag)
return -EINVAL;
/* TODO: Dynamic tag format */
if (near_tag_get_memory_layout(tag) != NEAR_TAG_MEMORY_STATIC)
return -EOPNOTSUPP;
uid = near_tag_get_nfcid(adapter_idx, target_idx, &uid_length);
if (!uid || uid_length != UID_LENGTH) {
near_error("Invalid type 1 UID");
return -EINVAL;
}
cookie = g_try_malloc0(sizeof(struct t1_cookie));
if (!cookie) {
err = -EINVAL;
goto out_err;
}
cookie->adapter_idx = adapter_idx;
cookie->target_idx = target_idx;
cookie->cb = cb;
memcpy(cookie->uid, uid, UID_LENGTH);
cookie->cc[0] = TYPE1_MAGIC;
cookie->cc[1] = TYPE1_TAG_VER_1_1;
cookie->cc[2] = TYPE1_TAG_STATIC_SIZE_120;
cookie->cc[3] = TYPE1_READ_WRITE_ACCESS;
cookie->current_block = 1;
cookie->current_byte = 0;
cmd.cmd = CMD_WRITE_E;
addr = cookie->current_block << 3;
cmd.addr = addr | (cookie->current_byte & 0x7);
cmd.data[0] = cookie->cc[cookie->current_byte];
cookie->current_byte++;
memcpy(cmd.uid, cookie->uid, UID_LENGTH);
g_free(uid);
err = near_adapter_send(cookie->adapter_idx, (uint8_t *) &cmd,
sizeof(cmd), format_resp, cookie,
t1_cookie_release);
if (err < 0)
goto out_err;
return 0;
out_err:
g_free(cookie);
g_free(uid);
return err;
}
static struct near_tag_driver type1_driver = {
.type = NFC_PROTO_JEWEL,
.priority = NEAR_TAG_PRIORITY_DEFAULT,
.read = nfctype1_read,
.write = nfctype1_write,
.check_presence = nfctype1_check_presence,
.format = nfctype1_format,
};
static int nfctype1_init(void)
{
DBG("");
return near_tag_driver_register(&type1_driver);
}
static void nfctype1_exit(void)
{
DBG("");
near_tag_driver_unregister(&type1_driver);
}
NEAR_PLUGIN_DEFINE(nfctype1, "NFC Forum Type 1 tags support", VERSION,
NEAR_PLUGIN_PRIORITY_HIGH, nfctype1_init, nfctype1_exit)