diff options
Diffstat (limited to 'src/rfid_proto_tcl.c')
-rw-r--r-- | src/rfid_proto_tcl.c | 694 |
1 files changed, 694 insertions, 0 deletions
diff --git a/src/rfid_proto_tcl.c b/src/rfid_proto_tcl.c new file mode 100644 index 0000000..f070614 --- /dev/null +++ b/src/rfid_proto_tcl.c @@ -0,0 +1,694 @@ +/* ISO 14443-4 (T=CL) implementation, PCD side. + * + * (C) 2005 by Harald Welte <laforge@gnumonks.org> + * + */ + +/* + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> + +#include <rfid/rfid.h> +#include <rfid/rfid_protocol_tcl.h> +#include <rfid/rfid_protocol.h> +#include <rfid/rfid_layer2.h> +#include <rfid/rfid_layer2_iso14443b.h> + +#include <rfid/rfid_asic.h> +#include <rfid/rfid_reader.h> + +#include "rfid_iso14443_common.h" + +#if 1 +#ifdef DEBUGP +#undef DEBUGP +#define DEBUGP(x, ...) +#define DEBUGPC(x, ...) +#endif +#endif + +static enum rfid_frametype l2_to_frame(unsigned int layer2) +{ + switch (layer2) { + case RFID_LAYER2_ISO14443A: + return RFID_14443A_FRAME_REGULAR; + break; + case RFID_LAYER2_ISO14443B: + return RFID_14443B_FRAME_REGULAR; + break; + } + return 0; +} + +static unsigned int sfgi_to_sfgt(struct rfid_protocol_handle *h, + unsigned char sfgi) +{ + unsigned int multiplier; + unsigned int tmp; + + if (sfgi > 14) + sfgi = 14; + + multiplier = 1 << sfgi; /* 2 to the power of sfgi */ + + /* ISO 14443-4:2000(E) Section 5.2.5: + * (256 * 16 / h->l2h->rh->ah->fc) * (2 ^ sfgi) */ + tmp = (unsigned int) 1000000 * 256 * 16; + + return (tmp / h->l2h->rh->ah->fc) * multiplier; +} + +static unsigned int fwi_to_fwt(struct rfid_protocol_handle *h, + unsigned char fwi) +{ + unsigned int multiplier, tmp; + + if (fwi > 14) + fwi = 14; + + multiplier = 1 << fwi; /* 2 to the power of fwi */ + + /* ISO 14443-4:2000(E) Section 7.2.: + * (256*16 / h->l2h->rh->ah->fc) * (2 ^ fwi) */ + + tmp = (unsigned int) 1000000 * 256 * 16; + + return (tmp / h->l2h->rh->ah->fc) * multiplier; +} + +/* 4.9seconds as microseconds (4.9 billion seconds) exceeds 2^32 */ +#define activation_fwt(x) (((u_int64_t)1000000 * 65536 / x->l2h->rh->ah->fc)) +#define deactivation_fwt(x) activation_fwt(x) + +static int +tcl_parse_ats(struct rfid_protocol_handle *h, + unsigned char *ats, unsigned int size) +{ + unsigned char len = ats[0]; + unsigned char t0; + unsigned char *cur; + + if (len == 0 || size == 0) + return -1; + + if (size < len) + len = size; + + h->priv.tcl.ta = 0; + + if (len == 1) { + /* FIXME: assume some default values */ + h->priv.tcl.fsc = 32; + h->priv.tcl.ta = 0x80; /* 0x80 (same d for both dirs) */ + h->priv.tcl.sfgt = sfgi_to_sfgt(h, 0); + if (h->l2h->l2->id == RFID_LAYER2_ISO14443A) { + /* Section 7.2: fwi default for type A is 4 */ + h->priv.tcl.fwt = fwi_to_fwt(h, 4); + } else { + /* Section 7.2: fwi for type B is always in ATQB */ + /* Value is assigned in tcl_connect() */ + /* This function is never called for Type B, since it has no (R)ATS */ + } + return 0; + } + + /* guarateed to be at least 2 bytes in size */ + + t0 = ats[1]; + cur = &ats[2]; + + iso14443_fsdi_to_fsd(&h->priv.tcl.fsc, t0 & 0x0f); + + if (t0 & (1 << 4)) { + /* TA is transmitted */ + h->priv.tcl.ta = *cur++; + } + + if (t0 & (1 << 5)) { + /* TB is transmitted */ + h->priv.tcl.sfgt = sfgi_to_sfgt(h, *cur & 0x0f); + h->priv.tcl.fwt = fwi_to_fwt(h, (*cur & 0xf0) >> 4); + cur++; + } + + if (t0 & (1 << 6)) { + /* TC is transmitted */ + if (*cur & 0x01) + h->priv.tcl.flags |= TCL_HANDLE_F_NAD_SUPPORTED; + if (*cur & 0x02) + h->priv.tcl.flags |= TCL_HANDLE_F_CID_SUPPORTED; + cur++; + } + + h->priv.tcl.historical_len = (ats+len) - cur; + h->priv.tcl.historical_bytes = cur; + + return 0; +} + + +/* request an ATS from the PICC */ +static int +tcl_request_ats(struct rfid_protocol_handle *h) +{ + int ret; + unsigned char rats[2]; + unsigned char fsdi; + + if (h->priv.tcl.state != TCL_STATE_INITIAL) + return -1; + + ret = iso14443_fsd_to_fsdi(&fsdi, h->priv.tcl.fsd); + if (ret < 0) { + DEBUGP("unable to encode FSD of %u as FSDI\n", h->priv.tcl.fsd); + return ret; + } + + rats[0] = 0xe0; + rats[1] = (h->priv.tcl.cid & 0x0f) | ((fsdi << 4) & 0xf0); + + /* transcieve (with CRC) */ + ret = h->l2h->l2->fn.transcieve(h->l2h, RFID_14443A_FRAME_REGULAR, + rats, 2, h->priv.tcl.ats, + &h->priv.tcl.ats_len, activation_fwt(h), + TCL_TRANSP_F_TX_CRC); + if (ret < 0) { + DEBUGP("transcieve of rats failed\n"); + h->priv.tcl.state = TCL_STATE_RATS_SENT; + /* FIXME: retransmit */ + return ret; + } + h->priv.tcl.state = TCL_STATE_ATS_RCVD; + + ret = tcl_parse_ats(h, h->priv.tcl.ats, h->priv.tcl.ats_len); + if (ret < 0) { + DEBUGP("parsing of ats failed\n"); + return ret; + } + + return 0; +} + +#define ATS_TA_DIV_2 1 +#define ATS_TA_DIV_4 2 +#define ATS_TA_DIV_8 4 + +#define PPS_DIV_8 3 +#define PPS_DIV_4 2 +#define PPS_DIV_2 1 +#define PPS_DIV_1 0 +static unsigned char d_to_di(struct rfid_protocol_handle *h, unsigned char D) +{ + static char DI; + unsigned int speed = h->l2h->rh->reader->iso14443a.speed; + + if ((D & ATS_TA_DIV_8) && (speed & RFID_READER_SPEED_848K)) + DI = PPS_DIV_8; + else if ((D & ATS_TA_DIV_4) && (speed & RFID_READER_SPEED_424K)) + DI = PPS_DIV_4; + else if ((D & ATS_TA_DIV_2) && (speed & RFID_READER_SPEED_212K)) + DI = PPS_DIV_2; + else + DI = PPS_DIV_1; + + return DI; +} + + +/* start a PSS run (autimatically configure highest possible speed */ +static int +tcl_do_pps(struct rfid_protocol_handle *h) +{ +#if 0 + int ret; + unsigned char ppss[3]; + unsigned char pps_response[1]; + unsigned int rx_len = 1; + unsigned char Dr, Ds, DrI, DsI; + + if (h->priv.tcl.state != TCL_STATE_ATS_RCVD) + return -1; + + Dr = h->priv.tcl.ta & 0x07; + Ds = h->priv.tcl.ta & 0x70 >> 4; + + if (Dr != Ds && !(h->priv.tcl.ta & 0x80)) { + /* device supports different divisors for rx and tx, but not ?!? */ + DEBUGP("PICC has contradictory TA, aborting PPS\n"); + return -1; + }; + + /* ISO 14443-4:2000(E) Section 5.3. */ + + ppss[0] = 0xd0 & (h->priv.tcl.cid & 0x0f); + ppss[1] = 0x11; + + /* FIXME: deal with different speed for each direction */ + DrI = d_to_di(h, Dr); + DsI = d_to_di(h, Ds); + + ppss[2] = (ppss[2] & 0xf0) | (DrI | DsI << 2); + + ret = h->l2h->l2->fn.transcieve(h->l2h, ppss, 3, pps_response, + &rx_len, h->priv.tcl.fwt, + TCL_TRANSP_F_TX_CRC); + if (ret < 0) + return ret; + + if (pps_response[0] != ppss[0]) { + DEBUGP("PPS Response != PPSS\n"); + return -1; + } + + h->priv.tcl.state = TCL_STATE_ESTABLISHED; +#endif + return 0; +} + + +static int +tcl_build_prologue2(struct tcl_handle *th, + unsigned char *prlg, unsigned int *prlg_len, + unsigned char pcb) +{ + *prlg_len = 1; + + *prlg = pcb; + + if (th->toggle) { + /* we've sent a toggle bit last time */ + th->toggle = 0; + } else { + /* we've not sent a toggle last time: send one */ + th->toggle = 1; + *prlg |= 0x01; + } + + if (th->flags & TCL_HANDLE_F_CID_USED) { + /* ISO 14443-4:2000(E) Section 7.1.1.2 */ + *prlg |= TCL_PCB_CID_FOLLOWING; + (*prlg_len)++; + prlg[*prlg_len] = th->cid & 0x0f; + } + + /* nad only for I-block (0xc0 == 00) */ + if ((th->flags & TCL_HANDLE_F_NAD_USED) && + ((pcb & 0xc0) == 0x00)) { + /* ISO 14443-4:2000(E) Section 7.1.1.3 */ + /* FIXME: in case of chaining only for first frame */ + *prlg |= TCL_PCB_NAD_FOLLOWING; + prlg[*prlg_len] = th->nad; + (*prlg_len)++; + } + + return 0; +} + +static int +tcl_build_prologue_i(struct tcl_handle *th, + unsigned char *prlg, unsigned int *prlg_len) +{ + /* ISO 14443-4:2000(E) Section 7.1.1.1 */ + return tcl_build_prologue2(th, prlg, prlg_len, 0x02); +} + +static int +tcl_build_prologue_r(struct tcl_handle *th, + unsigned char *prlg, unsigned int *prlg_len, + unsigned int nak) +{ + unsigned char pcb = 0xa2; + /* ISO 14443-4:2000(E) Section 7.1.1.1 */ + + if (nak) + pcb |= 0x10; + + return tcl_build_prologue2(th, prlg, prlg_len, pcb); +} + +static int +tcl_build_prologue_s(struct tcl_handle *th, + unsigned char *prlg, unsigned int *prlg_len) +{ + /* ISO 14443-4:2000(E) Section 7.1.1.1 */ + + /* the only S-block from PCD->PICC is DESELECT, + * well, actually there is the S(WTX) response. */ + return tcl_build_prologue2(th, prlg, prlg_len, 0xc2); +} + +/* FIXME: WTXM implementation */ + +static int tcl_prlg_len(struct tcl_handle *th) +{ + int prlg_len = 1; + + if (th->flags & TCL_HANDLE_F_CID_USED) + prlg_len++; + + if (th->flags & TCL_HANDLE_F_NAD_USED) + prlg_len++; + + return prlg_len; +} + +#define max_net_tx_framesize(x) (x->fsc - tcl_prlg_len(x)) + +static int +tcl_connect(struct rfid_protocol_handle *h) +{ + int ret; + + if (h->priv.tcl.state != TCL_STATE_DESELECTED && + h->priv.tcl.state != TCL_STATE_INITIAL) + return -1; + + switch (h->l2h->l2->id) { + case RFID_LAYER2_ISO14443A: + /* Start Type A T=CL Activation Sequence */ + ret = tcl_request_ats(h); + if (ret < 0) + return ret; + + /* Only do PPS if any non-default divisors supported */ + if (h->priv.tcl.ta & 0x77) { + ret = tcl_do_pps(h); + if (ret < 0) + return ret; + } + break; + case RFID_LAYER2_ISO14443B: + /* initialized T=CL state from Type B Activation Data */ + h->priv.tcl.cid = h->l2h->priv.iso14443b.cid; + h->priv.tcl.fsc = h->l2h->priv.iso14443b.fsc; + h->priv.tcl.fsd = h->l2h->priv.iso14443b.fsd; + h->priv.tcl.fwt = h->l2h->priv.iso14443b.fwt; + + /* what about ta? sfgt? */ + + if (h->l2h->priv.iso14443b.flags & ISO14443B_CID_SUPPORTED) + h->priv.tcl.flags |= TCL_HANDLE_F_CID_SUPPORTED; + if (h->l2h->priv.iso14443b.flags & ISO14443B_NAD_SUPPORTED) + h->priv.tcl.flags |= TCL_HANDLE_F_NAD_SUPPORTED; + + switch (h->l2h->priv.iso14443b.state) { + case ISO14443B_STATE_SELECTED: + h->priv.tcl.state = TCL_STATE_ATS_RCVD; + break; + case ISO14443B_STATE_ATTRIB_SENT: + h->priv.tcl.state = TCL_STATE_RATS_SENT; + break; + } + + /* PUPI will be presented as ATS/historical bytes */ + memcpy(h->priv.tcl.ats, h->l2h->uid, 4); + h->priv.tcl.ats_len = 4; + h->priv.tcl.historical_bytes = h->priv.tcl.ats; + + break; + default: + DEBUGP("unsupported l2: %u\n", h->l2h->l2->id); + return -1; + break; + } + + return 0; +} + +static int +tcl_deselect(struct rfid_protocol_handle *h) +{ + /* ISO 14443-4:2000(E) Section 8 */ + int ret; + unsigned char frame[3]; /* 3 bytes prologue, no information */ + unsigned char rx[3]; + unsigned int rx_len = sizeof(rx); + unsigned int prlg_len; + struct tcl_handle *th = &h->priv.tcl; + + if (th->state != TCL_STATE_ESTABLISHED) { + /* FIXME: not sure whether deselect is possible here, + * probably better send a HLTA? */ + } + + /* build DESELECT S-block */ + ret = tcl_build_prologue_s(th, frame, &prlg_len); + if (ret < 0) + return ret; + + ret = h->l2h->l2->fn.transcieve(h->l2h, RFID_14443A_FRAME_REGULAR, + frame, prlg_len, rx, + &rx_len, deactivation_fwt(h), + TCL_TRANSP_F_TX_CRC); + if (ret < 0) { + /* FIXME: retransmit, HLT(A|B) */ + return ret; + } + + th->state = TCL_STATE_DESELECTED; + + return 0; +} + +#define is_s_block(x) ((x & 0xc0) == 0xc0) +#define is_r_block(x) ((x & 0xc0) == 0x80) +#define is_i_block(x) ((x & 0xc0) == 0x00) + +static int +tcl_transcieve(struct rfid_protocol_handle *h, + const unsigned char *tx_data, unsigned int tx_len, + unsigned char *rx_data, unsigned int *rx_len, + unsigned int timeout, unsigned int flags) +{ + int ret; + unsigned char *tx_buf, *rx_buf; + unsigned char *_rx_data = rx_data; + unsigned int _rx_len; + unsigned int max_rx_len = *rx_len; /* maximum number of payoload that + caller has requested */ + unsigned int prlg_len; + struct tcl_handle *th = &h->priv.tcl; + + unsigned char *_tx; + unsigned int _tx_len, _timeout; + unsigned char wtx_resp[3]; + unsigned char ack[10]; + unsigned int ack_len; + + if (tx_len > max_net_tx_framesize(th)) { + /* slow path: we need to use chaining */ + return -1; + } + + tx_buf = malloc(tcl_prlg_len(th) + tx_len); + if (!tx_buf) { + ret = -ENOMEM; + goto out; + } + rx_buf = malloc(tcl_prlg_len(th) + *rx_len); + if (!rx_buf) { + ret = -ENOMEM; + goto out_txb; + } + + if (tcl_build_prologue_i(th, tx_buf, &prlg_len) < 0) { + ret = -1; + goto out_rxb; + } + memcpy(tx_buf + prlg_len, tx_data, tx_len); + + /* intialize to data-to-be-transferred */ + _tx = tx_buf; + _tx_len = tx_len+prlg_len; + _timeout = th->fwt; + _rx_len = *rx_len; + *rx_len = 0; + +do_tx: + ret = h->l2h->l2->fn.transcieve(h->l2h, l2_to_frame(h->l2h->l2->id), + _tx, _tx_len, + rx_buf, &_rx_len, _timeout, 0); + DEBUGP("l2 transcieve finished\n"); + if (ret < 0) + goto out_rxb; + + if ((*rx_buf & 0x01) != h->priv.tcl.toggle) { + DEBUGP("response with wrong toggle bit\n"); + goto out_rxb; + } + + if (is_r_block(*rx_buf)) { + unsigned int txed = _tx - tx_buf; + DEBUGP("R-Block\n"); + /* Handle ACK frame in case of chaining */ + if (*rx_buf & TCL_PCB_CID_FOLLOWING) { + if (*(rx_buf+1) != h->priv.tcl.cid) { + DEBUGP("CID %u is not valid\n", *(rx_buf)+1); + goto out_rxb; + } + } + /* set up parameters for next frame in chain */ + if (txed < tx_len) { + /* move tx pointer by the amount of bytes transferred + * in last frame */ + _tx += _tx_len; + _tx_len = (tx_len - txed); + if (_tx_len > max_net_tx_framesize(th)) { + /* not last frame in chain */ + _tx_len = max_net_tx_framesize(th); + } else { + /* last frame in chain */ + } + goto do_tx; + } else { + DEBUGP("Received ACK in response to last frame in " + "chain?!? Expected I-frame.\n"); + ret = -1; + goto out_rxb; + } + } else if (is_s_block(*rx_buf)) { + unsigned char inf; + unsigned int prlg_len; + + DEBUGP("S-Block\n"); + /* Handle Wait Time Extension */ + if (*rx_buf & TCL_PCB_CID_FOLLOWING) { + if (_rx_len < 3) { + DEBUGP("S-Block with CID but short len\n"); + ret = -1; + goto out_rxb; + } + if (*(rx_buf+1) != h->priv.tcl.cid) { + DEBUGP("CID %u is not valid\n", *(rx_buf)+1); + goto out_rxb; + } + inf = *(rx_buf+2); + } else + inf = *(rx_buf+1); + + if ((*rx_buf & 0x30) != 0x30) { + DEBUGP("S-Block but not WTX?\n"); + ret = -1; + goto out_rxb; + } + inf &= 0x3f; /* only lower 6 bits code WTXM */ + if (inf == 0 || (inf >= 60 && inf <= 63)) { + DEBUGP("WTXM %u is RFU!\n", inf); + ret = -1; + goto out_rxb; + } + + /* Acknowledge WTXM */ + tcl_build_prologue_s(&h->priv.tcl, wtx_resp, &prlg_len); + /* set two bits that make this block a wtx */ + wtx_resp[0] |= 0x30; + wtx_resp[prlg_len] = inf; + _tx = wtx_resp; + _tx_len = prlg_len+1; + _timeout = th->fwt * inf; + + /* start over with next transcieve */ + goto do_tx; /* FIXME: do transcieve locally since we use + totally different buffer */ + + } else if (is_i_block(*rx_buf)) { + unsigned char *inf = rx_buf+1; + unsigned int net_payload_len; + /* we're actually receiving payload data */ + + DEBUGP("I-Block: "); + if (*rx_buf & TCL_PCB_CID_FOLLOWING) { + if (*(rx_buf+1) != h->priv.tcl.cid) { + DEBUGPC("CID %u is not valid\n", *(rx_buf)+1); + goto out_rxb; + } + inf++; + } + if (*rx_buf & TCL_PCB_NAD_FOLLOWING) { + inf++; + } + net_payload_len = _rx_len - (inf - rx_buf); + DEBUGPC("%u bytes\n", net_payload_len); + memcpy(_rx_data, inf, net_payload_len); + /* increment the number of payload bytes that we actually received */ + *rx_len += net_payload_len; + _rx_data += net_payload_len; + + if (*rx_buf & 0x10) { + /* we're not the last frame in the chain, continue rx */ + DEBUGP("we're not the last frame in the chain, continue\n"); + ack_len = sizeof(ack); + tcl_build_prologue_r(&h->priv.tcl, ack, &ack_len, 0); + _tx = ack; + _tx_len = ack_len; + goto do_tx; + } + } + +out_rxb: + free(rx_buf); +out_txb: + free(tx_buf); +out: + return ret; +} + +static struct rfid_protocol_handle * +tcl_init(struct rfid_layer2_handle *l2h) +{ + struct rfid_protocol_handle *th; + unsigned int mru = l2h->rh->ah->mru; + + th = malloc(sizeof(struct rfid_protocol_handle) + mru); + if (!th) + return NULL; + + /* FIXME: mru should be attribute of layer2 (in case it adds/removes + * some overhead */ + memset(th, 0, sizeof(struct rfid_protocol_handle) + mru); + + /* maximum received ats length equals mru of asic/reader */ + th->priv.tcl.state = TCL_STATE_INITIAL; + th->priv.tcl.ats_len = mru; + th->priv.tcl.toggle = 1; + + th->priv.tcl.fsd = iso14443_fsd_approx(mru); + + return th; +} + +static int +tcl_fini(struct rfid_protocol_handle *ph) +{ + free(ph); + return 0; +} + +struct rfid_protocol rfid_protocol_tcl = { + .id = RFID_PROTOCOL_TCL, + .name = "ISO 14443-4 / T=CL", + .fn = { + .init = &tcl_init, + .open = &tcl_connect, + .transcieve = &tcl_transcieve, + .close = &tcl_deselect, + .fini = &tcl_fini, + }, +}; |