⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tda8007.c

📁 单片机读写TDA8007
💻 C
📖 第 1 页 / 共 3 页
字号:
/*---------------------------------------------------------------------------
 *  Copyright (C) 2004 Dallas Semiconductor Corporation, All Rights Reserved.
 * 
 *  Permission is hereby granted, free of charge, to any person obtaining a
 *  copy of this software and associated documentation files (the "Software"),
 *  to deal in the Software without restriction, including without limitation
 *  the rights to use, copy, modify, merge, publish, distribute, sublicense,
 *  and/or sell copies of the Software, and to permit persons to whom the
 *  Software is furnished to do so, subject to the following conditions:
 * 
 *  The above copyright notice and this permission notice shall be included
 *  in all copies or substantial portions of the Software.
 * 
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 *  OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 *  MERCHANTABILITY,  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 *  IN NO EVENT SHALL DALLAS SEMICONDUCTOR BE LIABLE FOR ANY CLAIM, DAMAGES
 *  OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 *  ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 *  OTHER DEALINGS IN THE SOFTWARE.
 * 
 *  Except as contained in this notice, the name of Dallas Semiconductor
 *  shall not be used except as stated in the Dallas Semiconductor
 *  Branding Policy.
 * ---------------------------------------------------------------------------
 */
/** 
 * \file lib8007.c
 * \brief Interface library for 8007 smartcard interface chips
 *
 * This library contains routines required to communicate with smartcards
 * via an 8007 while conforming to:
 * ISO 7816
 * EMV 4.1
 * GSM ????
 */

// TODO:
// Test CRC16 CCITT against ISO 3309/CCITT vectors
// Test T=0 error modes
// Test T=1 error modes
// Implement BWT, CWT, etc in T=1
// Implement WWT in T=0
// Implement/test T=1 receive chaining
// Add error handling to detect power, serial, etc. problems and report to user.
// Implement baud rate detection/negotiation (this is called PPS?)
// Add Tx(i) parameter checking (e.g. EMV requires failure for lack of TA3 parameter)
// Automatically power down on certain errors (too many parity, etc.)
// Find out why MSR.CRED bit sometimes sticks at zero, and screws up dssc_writeregister() and readregister().
// Consider moving all ISO, EMV, etc special ATR checks into a single function.

#include <stdio.h>
#include <string.h>

#include <reg5000.h>
//#include <REG390.H>
#include <absacc.h>
#include "tda8007.h"

// FIXME: Remove after we find out why MSR.CRED sometimes never gets back to 1.
//#define DEBUG_CRED

// 0 - no debug, 1 - errors, 2 - basic, 3 - very verbose
#define DEBUG 1

void clearATRstruct(struct ATR*);
int16_t dssc_ATRsequence(uint8_t);

uint8_t workingBuffer[512];
// FIXME: Do I want to keep the ATR raw buffer around for both slots?  If not, go to singular ATRLength.
uint8_t ATRLength[CARD_SLOTS];


struct ATR lastATR[CARD_SLOTS];
uint8_t TMode[CARD_SLOTS]; // T=0 or T=1
uint32_t WWT[CARD_SLOTS];  // Work wait time
uint32_t CWT[CARD_SLOTS];  // Character wait time
uint32_t BWT[CARD_SLOTS];  // Block wait time
uint8_t EDCtype[CARD_SLOTS];
uint8_t NAD[CARD_SLOTS];  // Node address byte to use when communicating in T=1
uint8_t IFSC[CARD_SLOTS];  // Maximum segment length when sending to card in T=1

uint8_t currentSlot = 0;

/*
Dump formatted contents of ATR struct
*/

#if DEBUG > 0
void dumpATRStruct(struct ATR myatr)
{
	  int i;
	  printf("ATR\n");
	  printf("TS: %02bx\n",(uint8_t)myatr.TS);
	  printf("T0: %02bx\n",(uint8_t)myatr.T0);
	  for (i = 1;i < 8;i++)
	  {
	    if (myatr.TA[i] != -1)
	      printf("TA%d: %02bx\n",i,(uint8_t)myatr.TA[i]);
	    if (myatr.TB[i] != -1)
	      printf("TB%d: %02bx\n",i,(uint8_t)myatr.TB[i]);
	    if (myatr.TC[i] != -1)
	      printf("TC%d: %02bx\n",i,(uint8_t)myatr.TC[i]);
	    if (myatr.TD[i] != -1)
	      printf("TD%d: %02bx\n",i,(uint8_t)myatr.TD[i]);
	  }
	  for (i = 0;i < myatr.HistoricalLength;i++)
	  {
	    printf("%02bx ",myatr.Historical[i]);
	  }
	  printf("\n");
	  if (myatr.TCK != -1)
	    printf("TCK: %02bx\n",(uint8_t)myatr.TCK);
}
#endif

/*
Used to clear the ATR struct before card power up.
*/
void clearATRStruct(struct ATR *myatr)
{
  memset(myatr,0xFF,sizeof(struct ATR));
  myatr->HistoricalLength = 0;
}

/* 
CCITT CRC 16
X^16+X^12+X^5+1
Be sure to use 0xFFFF as initial crc value.
FIXME: Find test vectors and test.


NOTE: There is another CRC16 that is not used for smart cards.  Here it is:
CRC16
X^16+X^15+X^2+1
0x8005
0xA001
*/
uint16_t update_crc(uint8_t value, uint16_t crc)
{
  int i;

  uint16_t newval = value << 8;

  for (i = 0;i < 8;i++)
  {
    if ((crc ^ newval) & 0x8000)
    {
      crc <<= 1;
      crc ^= 0x1021;
    }
    else
    {
      crc <<= 1;
    }
    newval <<= 1;
  }

/*
  // Fast implementation from www.eagleairaust.com.au
  crc = (crc >> 8) | (crc << 4);
  crc ^= value;
  crc ^= (crc & 0xFF) >> 4;
  crc ^= (crc << 8) << 4;
  crc ^= ((crc & 0xFF) << 4) << 1;
*/
  return crc;
}

/*
Read a byte from the UART or timeout after BWT (T=1), CWT (T=1) or WWT (T=0).
Not tested.
*/
int16_t readByte()
{
	  uint8_t val;
	  uint32_t timeout;
	
	  while(!(dssc_readregister(MSR) & MSR_TBE_RBF_MASK))
	  {
	    val = dssc_readregister(USR);
	    if (val & USR_PE_MASK)
	    {
#if DEBUG > 0
	      printf("parity error\n");
#endif
	      return ERR_RECEIVE_PARITY;
	    }
	    if (val & (USR_TOL3_MASK|USR_TOL2_MASK|USR_TOL1_MASK))
	    {
#if DEBUG > 0
	      printf("timeout error %02bx\n",val);
#endif
	      return ERR_RECEIVE_TIMEOUT;
	    }
	  }
	
	  // Read and store byte
	  val = dssc_readregister(URR);
	
	  if (TMode[currentSlot] == 0)
	    timeout = WWT[currentSlot];  // Use WWT for T=0
	  else
	    timeout = CWT[currentSlot];  // Use CWT for T=1
	
	  // Set up timer for 24 bit timer, start immediately
	  dssc_writeregister(TOC,0x00);
	  dssc_writeregister(TOR3,timeout >> 16);
	  dssc_writeregister(TOR2,timeout >> 8);
	  dssc_writeregister(TOR1,timeout);
	  dssc_writeregister(TOC,0x68);
	
	
	  return val;
}

/*
FIXME: This is not the proper way to timeout.  Use the 8007!!!!
Read a byte from the UART or timeout after some arbitrary loop timeout.
*/
/*int16_t readByteOld()
{
  uint8_t val;
  int count = 0;

  while(!(dssc_readregister(MSR) & MSR_TBE_RBF_MASK))
  {
    count++;
    if (count > 5000) 
    {
      printf("timeout\n");
      return ERR_RECEIVE_TIMEOUT;
    }
    val = dssc_readregister(USR);
    if (val & USR_PE_MASK)
    {
      printf("parity error\n");
      return ERR_RECEIVE_PARITY;
    }
  }
  // Read and store byte
  val = dssc_readregister(URR);

  return val;
}
*/

/*
Write a byte and leave the 8007 in transmit mode
*/
void writeByte(uint8_t onebyte)
{
	  uint8_t val;
	  val = dssc_readregister(UCR1);
	  // set T bit
	  dssc_writeregister(UCR1,val | UCR1_T_R_MASK);
	  dssc_writeregister(UTR,onebyte);
	  // wait for byte to go out
	  while (!(dssc_readregister(USR) & USR_TBE_RBF_MASK));
}

/*
Write a byte and put the 8007 in receive mode
*/
void writeLastByte(uint8_t onebyte)
{
	  uint8_t val;
	  uint32_t timeout;
	
	  if (TMode[currentSlot] == 0)
	    timeout = WWT[currentSlot];  // Use WWT for T=0
	  else
	    timeout = BWT[currentSlot];  // Use BWT for T=1
	
	  // Set up timer for 24 bit timer, edge trigger start
	  dssc_writeregister(TOC,0x00);
	  dssc_writeregister(TOR3,timeout >> 16);
	  dssc_writeregister(TOR2,timeout >> 8);
	  dssc_writeregister(TOR1,timeout);
	  dssc_writeregister(TOC,0x7C);
	
	  val = dssc_readregister(UCR1);
	  // set LCT and T bit
	  dssc_writeregister(UCR1,val | (UCR1_T_R_MASK|UCR1_LCT_MASK));
	  dssc_writeregister(UTR,onebyte);
	  // wait for byte to go out
	  while (!(dssc_readregister(USR) & USR_TBE_RBF_MASK));
}

/*
Write a byte and put the 8007 in receive mode
*/
/*void writeLastByteOld(uint8_t onebyte)
{
  uint8_t val;

writeLastByteTimeout(onebyte);
return;
  val = dssc_readregister(UCR1);
  // set LCT and T bit
  dssc_writeregister(UCR1,val | (UCR1_T_R_MASK|UCR1_LCT_MASK));
  dssc_writeregister(UTR,onebyte);
  // wait for byte to go out
  while (!(dssc_readregister(USR) & USR_TBE_RBF_MASK));
}
*/

/*
Generate Error Data Check value depending on EDC type
*/
uint16_t generateEDC(uint8_t type,uint8_t onebyte,uint16_t value)
{
  if (type == EDC_TYPE_LRC)
  {
    return (value ^ onebyte);
  }
  else // type = CRC
  {
    return update_crc(onebyte,value);
  }
}

/*
Send a T=1 formatted block.
Formats T=1 packet from raw input data.  Length must already be
within the requirements of the destination smart card.
*/
void sendBlock(uint8_t NAD,uint8_t PCB,int16_t length,uint8_t *buffer,uint8_t type)
{
  int i;
  uint16_t EDC;

  if (type == EDC_TYPE_LRC)
  {
    EDC = 0;
  }
  else // type = CRC
  {
    EDC = 0xFFFF;
  }

  writeByte(NAD);
  EDC = generateEDC(type,NAD,EDC);
  writeByte(PCB);
  EDC = generateEDC(type,PCB,EDC);
  writeByte(length);
  EDC = generateEDC(type,length,EDC);
  for (i=0;i<length;i++)
  {
    writeByte(buffer[i]);
    EDC = generateEDC(type,buffer[i],EDC);
  }

  if (type == EDC_TYPE_LRC)
  {
    writeLastByte(EDC);
  }
  else // type = CRC
  {
    writeByte(EDC);
    writeLastByte(EDC>>8);
  }
}

/*
Receive a T=1 formatted block.
This does a raw receive, it does not check sequence numbers.
FIXME: The receive block function should do more to handle checking sequencing and
only return the raw data and some sort of pass/fail code to the caller.
*/
int16_t receiveBlock(uint8_t *rNAD,uint8_t *rPCB,uint8_t *rLEN,uint8_t *buffer,uint8_t type)
{
  int16_t retval;
  int16_t index = 0;
  uint16_t EDC;
  int16_t i;
  uint8_t expectedLength;

  // Get NAD, PCB, and Length
  retval = readByte();
  if (retval < 0) return retval;
  *rNAD = retval;
  retval = readByte();
  if (retval < 0) return retval;
  *rPCB = retval;
  retval = readByte();
  if (retval < 0) return retval;
  *rLEN = retval;


  // Add one to length for LRC
  expectedLength = *rLEN+1;
  // Add additional byte if using CRC
  if (type == EDC_TYPE_CRC)
    expectedLength++;

  // Get all data bytes plus EDC (1 or 2 bytes at end)
  for (i = 0;i < expectedLength;i++)
  {
    retval = readByte();
    if (retval < 0)
    {
      return retval;
    }
    buffer[index++] = retval;
  }

  // Check the LRC or CRC
  if (type == EDC_TYPE_LRC)
  {
    EDC = 0;
    EDC = generateEDC(EDC_TYPE_LRC,*rNAD,EDC);
    EDC = generateEDC(EDC_TYPE_LRC,*rPCB,EDC);
    EDC = generateEDC(EDC_TYPE_LRC,*rLEN,EDC);
    for (i = 0;i < index;i++)
    {
      EDC = generateEDC(EDC_TYPE_LRC,buffer[i],EDC);
    }
    if (EDC != 0)
    {
#if DEBUG > 0
      printf("Bad receive LRC\n");
#endif
      return ERR_RECEIVE_LRC;
    }
  }
  else // EDC is CRC
  {
    EDC = 0xFFFF;
    EDC = generateEDC(EDC_TYPE_LRC,*rNAD,EDC);
    EDC = generateEDC(EDC_TYPE_LRC,*rPCB,EDC);
    EDC = generateEDC(EDC_TYPE_LRC,*rLEN,EDC);
    for (i = 0;i < (index-2);i++)
    {
      EDC = generateEDC(EDC_TYPE_CRC,buffer[i],EDC);
    }
    
    if (((EDC >> 8) != buffer[index-2]) ||
        ((EDC & 0xFF) != buffer[index-1]))
    {
#if DEBUG > 0
      printf("Bad receive CRC\n");
#endif
      return ERR_RECEIVE_CRC;
    }
  }

#if DEBUG > 1
  printf("NAD: %02bx ",*rNAD);
  printf("PCB: %02bx ",*rPCB);
  printf("LEN: %02bx ",*rLEN);
  printf("D: ");
  for (i = 0;i < index;i++)
    printf("%02bx ",buffer[i]);
  printf("\n");
#endif

  return *rLEN;
}

/*
Send an S block for setting IFSD (card terminal buffer size)
*/
int16_t dssc_sendsblockIFSD(uint8_t IFSD)
{
  uint8_t buffer[1];
  uint8_t rNAD,rPCB,rLEN;
  uint8_t rbuffer[100];

#if DEBUG > 1
  printf("T=1 Sending IFSD\n");
#endif
  buffer[0] = IFSD;
  sendBlock(0,0xC1,1,buffer,EDCtype[currentSlot]);
  receiveBlock(&rNAD,&rPCB,&rLEN,rbuffer,EDCtype[currentSlot]);

  // If we do not get a confirmation response, fail.
  if ((rPCB == 0xE1) && (rLEN == 1) && (rbuffer[0] == IFSD))
    return 0;
  else
    return ERR_SETIFSD_FAILURE;
}

/*
Set Node address info for this card
*/
void dssc_setNAD(uint8_t value)
{
  NAD[currentSlot] = value;
}

/*
Send a message using T=1 protocol
*/
int16_t dssc_sendAPDUT1(uint8_t *buffer,int16_t length,uint8_t *rbuffer)
{
#if DEBUG > 1
  int index;
#endif
  int16_t maxSegLength = IFSC[currentSlot];  // Set by ATR (TA3), or default to 0x20
  int16_t retval;
  uint8_t sequenceNumber = 0;
  uint8_t rNAD,rPCB,rLEN;
  uint8_t tempbuffer[512];  // FIXME: What size should the temp buffer be??

#if DEBUG > 1
  printf("T=1 Sending APDU: ");
  for (index = 0;index < length;index++)
    printf("%02bx ",buffer[index]);
  printf("\n");
#endif

  while (length > maxSegLength)
  {
    // Send block with chaining mode, current sequence number, and maximum length.
    sendBlock(NAD[currentSlot],sequenceNumber|0x20,maxSegLength,buffer,EDCtype[currentSlot]);
    retval = receiveBlock(&rNAD,&rPCB,&rLEN,tempbuffer,EDCtype[currentSlot]);
    if (retval < 0) return retval;
    // Check for bad sequence number return
    if ((rPCB ^ ((sequenceNumber>>2) ^ 0x10)) != 0x80)
    {
#if DEBUG > 0
      printf("Bad return: %02bx\n",rPCB);
      printf("%02bx, %02bx, %02bx\n",sequenceNumber,sequenceNumber ^ 0x10,rPCB^sequenceNumber^0x10);
#endif
      return ERR_RECEIVE_SEQUENCENUM;
    }
    sequenceNumber ^= 0x40;
    buffer += maxSegLength;
    length -= maxSegLength;
  }

  // Send last (or only) block.  Then, we can wait for the receive side.
  sendBlock(NAD[currentSlot],sequenceNumber,length,buffer,EDCtype[currentSlot]);

  // FIXME: Implement receive chaining here.  Loop while chaining bit set and info packets coming.
  // FIXME: How do we handle non-I-block PCBs?
  retval = receiveBlock(&rNAD,&rPCB,&rLEN,tempbuffer,EDCtype[currentSlot]);
  if (retval < 0) return retval;

  memcpy(rbuffer,tempbuffer,rLEN);

  return retval;
}

/*
Send a message using T=0 protocol
*/
int16_t dssc_sendAPDUT0(uint8_t *buffer,int16_t length,uint8_t *rbuffer)
{
  int index;
  int16_t rindex = 0;
  uint8_t val;
  uint8_t INS = buffer[1];
  int retval;

#if DEBUG > 1
  printf("T=0 Sending APDU: ");
  for (index = 0;index < length;index++)
    printf("%02bx ",buffer[index]);
  printf("\n");
#endif

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -