- add mifare classic support
[librfid] / rfid_proto_tcl.c
index 299360e..248eea5 100644 (file)
@@ -4,6 +4,21 @@
  *
  */
 
+/*
+ *  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>
 static unsigned int sfgi_to_sfgt(struct rfid_protocol_handle *h, 
                                 unsigned char sfgi)
 {
-       /* ISO 14443-4:2000(E) Section 5.2.5. */
-       return (256 * 16 / h->l2h->rh->ah->fc) * (2 ^ 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)
 {
-       /* ISO 14443-4:2000(E) Section 7.2. */
-       return (256*16 / h->l2h->rh->ah->fc) * (2 ^ 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;
 }
 
-#define activation_fwt(x) (65536 / x->l2h->rh->ah->fc)
+/* 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
@@ -53,17 +91,20 @@ tcl_parse_ats(struct rfid_protocol_handle *h,
        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 = 0;
+               h->priv.tcl.ta = 0x80;  /* 0x80 (same d for both dirs) */
                h->priv.tcl.sfgt = sfgi_to_sfgt(h, 0);
-               if (1 /* FIXME: is_iso14443a */) {
+               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 */
-                       /* FIXME */
+                       /* Value is assigned in tcl_connect() */
+                       /* This function is never called for Type B, since it has no (R)ATS */
                }
                return 0;
        }
@@ -143,28 +184,81 @@ tcl_request_ats(struct rfid_protocol_handle *h)
 
        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_pss(struct rfid_protocol_handle *h)
+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;
 
-       //ppss[2] = 0x00 & foo;
+       /* FIXME: deal with different speed for each direction */
+       DrI = d_to_di(h, Dr);
+       DsI = d_to_di(h, Ds);
 
-       // FIXME: finish
-       
-       return -1;
+       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;
 }
 
 
@@ -177,6 +271,15 @@ tcl_build_prologue2(struct tcl_handle *th,
 
        *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;
@@ -225,7 +328,8 @@ tcl_build_prologue_s(struct tcl_handle *th,
 {
        /* ISO 14443-4:2000(E) Section 7.1.1.1 */
 
-       /* the only S-block from PCD->PICC is DESELECT: */
+       /* 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);
 }
 
@@ -262,10 +366,11 @@ tcl_connect(struct rfid_protocol_handle *h)
                if (ret < 0)
                        return ret;
 
-               if (0 /* FIXME */) {
-                       ret = tcl_do_pss(h);
+               /* Only do PPS if any non-default divisors supported */
+               if (h->priv.tcl.ta & 0x77) {
+                       ret = tcl_do_pps(h);
                        if (ret < 0)
-                               return -1;
+                               return ret;
                }
                break;
        case RFID_LAYER2_ISO14443B:
@@ -292,7 +397,7 @@ tcl_connect(struct rfid_protocol_handle *h)
                }
 
                /* PUPI will be presented as ATS/historical bytes */
-               memcpy(h->priv.tcl.ats, h->l2h->priv.iso14443b.pupi, 4);
+               memcpy(h->priv.tcl.ats, h->l2h->uid, 4);
                h->priv.tcl.ats_len = 4;
                h->priv.tcl.historical_bytes = h->priv.tcl.ats;
 
@@ -340,6 +445,10 @@ tcl_deselect(struct rfid_protocol_handle *h)
        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,
@@ -351,6 +460,12 @@ tcl_transcieve(struct rfid_protocol_handle *h,
        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;
@@ -373,13 +488,124 @@ tcl_transcieve(struct rfid_protocol_handle *h,
        }
        memcpy(tx_buf + prlg_len, tx_data, tx_len);
 
-       ret = h->l2h->l2->fn.transcieve(h->l2h, tx_buf, tx_len+prlg_len, 
-                                    rx_buf, rx_len, th->fwt, 0);
+       /* intialize to data-to-be-transferred */
+       _tx = tx_buf;
+       _tx_len = tx_len+prlg_len;
+       _timeout = th->fwt;
+
+do_tx:
+       ret = h->l2h->l2->fn.transcieve(h->l2h, _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;
+       }
 
-       memcpy(rx_data, rx_buf, *rx_len);
+       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;
+               /* we're actually receiving payload data */
+
+               DEBUGP("I-Block\n");
+               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;
+                       }
+                       inf++;
+               }
+               if (*rx_buf & TCL_PCB_NAD_FOLLOWING) {
+                       inf++;
+               }
+               memcpy(rx_data, inf, *rx_len - (inf - rx_buf));
+
+               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);
@@ -420,8 +646,7 @@ tcl_init(struct rfid_layer2_handle *l2h)
        /* maximum received ats length equals mru of asic/reader */
        th->priv.tcl.state = TCL_STATE_INITIAL;
        th->priv.tcl.ats_len = mru;
-       th->l2h = l2h;
-       th->proto = &rfid_protocol_tcl;
+       th->priv.tcl.toggle = 1;
 
        th->priv.tcl.fsd = iso14443_fsd_approx(mru);