/*
 * This file and its contents are supplied under the terms of the
 * Common Development and Distribution License ("CDDL"), version 1.0.
 * You may only use this file in accordance with the terms of version
 * 1.0 of the CDDL.
 *
 * A full copy of the text of the CDDL should have accompanied this
 * source.  A copy of the CDDL is also available via the Internet at
 * http://www.illumos.org/license/CDDL.
 */

/*
 * Copyright 2019, Joyent, Inc.
 */

/*
 * ATR parsing routines shared between userland (ccidadm) and the kernel (CCID
 * driver)
 */

#include "atr.h"
#include <sys/debug.h>
#include <sys/sysmacros.h>

#ifdef	_KERNEL
#include <sys/inttypes.h>
#include <sys/sunddi.h>
#include <sys/kmem.h>
#else
#include <inttypes.h>
#include <strings.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#endif

/*
 * The ATR must have at least 2 bytes and then may have up to 33 bytes. The
 * first byte is always TS and the second required byte is T0.
 */
#define	ATR_TS_IDX	0
#define	ATR_T0_IDX	1

/*
 * There are two valid values for TS. It must either be 0x3F or 0x3B. This is
 * required per ISO/IEC 7816-3:2006 section 8.1.
 */
#define	ATR_TS_INVERSE	0x3F
#define	ATR_TS_DIRECT	0x3B

/*
 * After TS, each word is used to indicate a combination of protocol and the
 * number of bits defined for that protocol. The lower nibble is treated as the
 * protocol. The upper nibble is treated to indicate which of four defined words
 * are present. These are usually referred to as TA, TB, TC, and TD. TD is
 * always used to indicate the next protocol and the number of bytes present for
 * that. T0 works in a similar way, except that it defines the number of
 * historical bytes present in its protocol section and then it refers to a set
 * of pre-defined global bytes that may be present.
 */
#define	ATR_TD_PROT(x)	((x) & 0x0f)
#define	ATR_TD_NBITS(x)	(((x) & 0xf0) >> 4)
#define	ATR_TA_MASK	0x1
#define	ATR_TB_MASK	0x2
#define	ATR_TC_MASK	0x4
#define	ATR_TD_MASK	0x8

#define	ATR_TA1_FTABLE(x)	(((x) & 0xf0) >> 4)
#define	ATR_TA1_DITABLE(x)	((x) & 0x0f)

#define	ATR_TA2_CANCHANGE(x)	(((x) & 0x80) == 0)
#define	ATR_TA2_HONORTA1(x)	(((x) & 0x10) == 0)
#define	ATR_TA2_PROTOCOL(x)	((x) & 0x0f)

/*
 * When the checksum is required in the ATR, each byte must XOR to zero.
 */
#define	ATR_CKSUM_TARGET	0

/*
 * Maximum number of historic ATR bytes. This is limited by the fact that it's a
 * 4-bit nibble.
 */
#define	ATR_HISTORICAL_MAX	15

/*
 * The maximum number of TA, TB, TC, and TD levels that can be encountered in a
 * given structure. In the best case, there are 30 bytes available (TS, T0, and
 * TCK use the others). Given that each one of these needs 4 bytes to be
 * represented, the maximum number of layers that can fit is seven.
 */
#define	ATR_TI_MAX	7

/*
 * Defined protocol values. See ISO/IEC 7816-3:2006 8.2.3 for this list.
 * Reserved values are noted but not defined.
 */
#define	ATR_PROTOCOL_T0		0
#define	ATR_PROTOCOL_T1		1

#define	ATR_T1_TB0_CWI(x)	((x) & 0x0f)
#define	ATR_T1_TB0_BWI(x)	(((x) & 0xf0) >> 4)
#define	ATR_T1_TC0_CRC(x)	(((x) & 0x01) != 0)

/*
 * T=2 and T=3 are reserved for future full-duplex operation.
 * T=4 is reserved for enhanced half-duplex character transmission.
 * T=5-13 are reserved for future use by ISO/IEC JTC 1/SC 17.
 * T=14 is for protocols not standardized by ISO/IEC JTC 1/SC 17.
 */
#define	ATR_PROTOCOL_T15	15

#define	ATR_T15_TA0_CLOCK(x)	(((x) & 0xc0) >> 6)
#define	ATR_T15_TA0_VOLTAGE(x)	((x) & 0x3f)

#define	ATR_T15_TB0_SPU_STANDARD(x)	(((x & 0x80)) != 0)

/*
 * Various definitions for the configuration of historical data. This comes from
 * ISO/IEC 7816-4:2013 Section 12.1.1.
 */

/*
 * The first historical byte is used to indicate the encoding of the data. Only
 * values 0x00, 0x80-0x8f are defined. All others are proprietary. 0x81-0x8f are
 * reserved for future use.
 */
#define	ATR_HIST_CAT_MAND_STATUS	0x00
#define	ATR_HIST_CAT_TLV_STATUS		0x80
#define	ATR_HIST_CAT_RFU_MIN		0x81
#define	ATR_HIST_CAT_RFU_MAX		0x8f

/*
 * From ISO/IEC 7816-3:2006 Section 8.3.
 *
 * The default value for Fi is 372 which is table entry 1. The default value for
 * Di is 1, which is table entry 1.
 */
#define	ATR_FI_DEFAULT_INDEX	1
#define	ATR_DI_DEFAULT_INDEX	1
#define	ATR_EXTRA_GUARDTIME_DEFAULT	0

/*
 * From ISO/IEC 7816-3:2006 Section 10.2.
 */
#define	ATR_T0_WI_DEFAULT	10

/*
 * From ISO/IEC 7816-3:2006 Section 11.4.3.
 */
#define	ATR_T1_CWI_DEFAULT	13

/*
 * From ISO/IEC 7816-3:2006 Section 11.4.3.
 */
#define	ATR_T1_BWI_DEFAULT	4

/*
 * From ISO/IEC 7816-3:2006 Section 11.4.2.
 */
#define	ATR_T1_IFSC_DEFAULT	32

/*
 * From ISO/IEC 7816-3:2006 Section 11.4.4
 */
#define	ATR_T1_CHECKSUM_DEFAULT	ATR_T1_CHECKSUM_LRC

/*
 * Definitions for PPS construction. These are derived from ISO/IEC 7816-3:2006
 * section 9, Protocol and parameters selection.
 */
#define	PPS_LEN_MIN	3	/* PPSS, PPS0, PCK */
#define	PPS_LEN_MAX	PPS_BUFFER_MAX
#define	PPS_PPSS_INDEX	0
#define	PPS_PPSS_VAL	0xff
#define	PPS_PPS0_INDEX	0x01
#define	PPS_PPS0_PROT(x)	((x) & 0x0f)
#define	PPS_PPS0_PPS1		(1 << 4)
#define	PPS_PPS0_PPS2		(1 << 5)
#define	PPS_PPS0_PPS3		(1 << 6)
#define	PPS_PPS1_SETVAL(f, d)	((((f) & 0x0f) << 4) | ((d) & 0x0f))

/*
 * This enum and subsequent structure is used to represent a single level of
 * 'T'. This includes the possibility for all three values to be set and records
 * the protocol.
 */
typedef enum atr_ti_flags {
	ATR_TI_HAVE_TA	= 1 << 0,
	ATR_TI_HAVE_TB	= 1 << 1,
	ATR_TI_HAVE_TC	= 1 << 2,
	ATR_TI_HAVE_TD	= 1 << 3
} atr_ti_flags_t;

typedef struct atr_ti {
	uint8_t		atrti_protocol;
	uint8_t		atrti_ti_val;
	uint8_t		atrti_td_idx;
	atr_ti_flags_t	atrti_flags;
	uint8_t		atrti_ta;
	uint8_t		atrti_tb;
	uint8_t		atrti_tc;
	uint8_t		atrti_td;
} atr_ti_t;

typedef enum atr_flags {
	ATR_F_USES_DIRECT	= 1 << 0,
	ATR_F_USES_INVERSE	= 1 << 1,
	ATR_F_HAS_CHECKSUM	= 1 << 2,
	ATR_F_VALID		= 1 << 3
} atr_flags_t;


struct atr_data {
	atr_flags_t	atr_flags;
	uint8_t		atr_nti;
	atr_ti_t	atr_ti[ATR_TI_MAX];
	uint8_t		atr_nhistoric;
	uint8_t		atr_historic[ATR_HISTORICAL_MAX];
	uint8_t		atr_cksum;
	uint8_t		atr_raw[ATR_LEN_MAX];
	uint8_t		atr_nraw;
};

/*
 * These tables maps the bit values for Fi from 7816-3:2006 section 8.3 Table 7.
 */
static uint_t atr_fi_valtable[16] = {
	372,		/* 0000 */
	372,		/* 0001 */
	558,		/* 0010 */
	744,		/* 0011 */
	1116,		/* 0100 */
	1488,		/* 0101 */
	1860,		/* 0110 */
	0,		/* 0111 */
	0,		/* 1000 */
	512,		/* 1001 */
	768,		/* 1010 */
	1024,		/* 1011 */
	1536,		/* 1100 */
	2048,		/* 1101 */
	0,		/* 1110 */
	0		/* 1111 */
};

static const char *atr_fi_table[16] = {
	"372",		/* 0000 */
	"372",		/* 0001 */
	"558",		/* 0010 */
	"744",		/* 0011 */
	"1116",		/* 0100 */
	"1488",		/* 0101 */
	"1860",		/* 0110 */
	"RFU",		/* 0111 */
	"RFU",		/* 1000 */
	"512",		/* 1001 */
	"768",		/* 1010 */
	"1024",		/* 1011 */
	"1536",		/* 1100 */
	"2048",		/* 1101 */
	"RFU",		/* 1110 */
	"RFU",		/* 1111 */
};

/*
 * This table maps the bit values for f(max) from 7816-3:2006 section 8.3
 * Table 7.
 */
static const char *atr_fmax_table[16] = {
	"4",		/* 0000 */
	"5",		/* 0001 */
	"6",		/* 0010 */
	"8",		/* 0011 */
	"12",		/* 0100 */
	"16",		/* 0101 */
	"20",		/* 0110 */
	"-",		/* 0111 */
	"-",		/* 1000 */
	"5",		/* 1001 */
	"7.5",		/* 1010 */
	"10",		/* 1011 */
	"15",		/* 1100 */
	"20",		/* 1101 */
	"-",		/* 1110 */
	"-",		/* 1111 */
};

/*
 * This table maps the bit values for Di from 7816-3:2006 section 8.3 Table 8.
 */
static uint_t atr_di_valtable[16] = {
	0,		/* 0000 */
	1,		/* 0001 */
	2,		/* 0010 */
	4,		/* 0011 */
	8,		/* 0100 */
	16,		/* 0101 */
	32,		/* 0110 */
	64,		/* 0111 */
	12,		/* 1000 */
	20,		/* 1001 */
	0,		/* 1010 */
	0,		/* 1011 */
	0,		/* 1100 */
	0,		/* 1101 */
	0,		/* 1110 */
	0		/* 1111 */
};

static const char *atr_di_table[16] = {
	"RFU",		/* 0000 */
	"1",		/* 0001 */
	"2",		/* 0010 */
	"4",		/* 0011 */
	"8",		/* 0100 */
	"16",		/* 0101 */
	"32",		/* 0110 */
	"64",		/* 0111 */
	"12",		/* 1000 */
	"20",		/* 1001 */
	"RFU",		/* 1010 */
	"RFU",		/* 1011 */
	"RFU",		/* 1100 */
	"RFU",		/* 1101 */
	"RFU",		/* 1110 */
	"RFU",		/* 1111 */
};

/*
 * This table maps the bit values for the clock stop indicator from 7816-3:2006
 * section 8.3 Table 9.
 */
static const char *atr_clock_table[4] = {
	"disallowed",		/* 00 */
	"signal low",		/* 01 */
	"signal high",		/* 10 */
	"signal low or high"	/* 11 */
};

uint_t
atr_fi_index_to_value(uint8_t val)
{
	if (val >= ARRAY_SIZE(atr_fi_valtable)) {
		return (0);
	}

	return (atr_fi_valtable[val]);
}

const char *
atr_fi_index_to_string(uint8_t val)
{
	if (val >= ARRAY_SIZE(atr_fi_table)) {
		return ("<invalid>");
	}

	return (atr_fi_table[val]);
}

const char *
atr_fmax_index_to_string(uint8_t val)
{
	if (val >= ARRAY_SIZE(atr_fmax_table)) {
		return ("<invalid>");
	}

	return (atr_fmax_table[val]);
}

uint_t
atr_di_index_to_value(uint8_t val)
{
	if (val >= ARRAY_SIZE(atr_di_valtable)) {
		return (0);
	}

	return (atr_di_valtable[val]);
}
const char *
atr_di_index_to_string(uint8_t val)
{
	if (val >= ARRAY_SIZE(atr_di_table)) {
		return ("<invalid>");
	}

	return (atr_di_table[val]);
}

const char *
atr_clock_stop_to_string(atr_clock_stop_t val)
{
	if (val >= ARRAY_SIZE(atr_clock_table)) {
		return ("<invalid>");
	}

	return (atr_clock_table[val]);
}

const char *
atr_protocol_to_string(atr_protocol_t prot)
{
	if (prot == ATR_P_NONE) {
		return ("none");
	}

	if ((prot & ATR_P_T0) == ATR_P_T0) {
		return ("T=0");
	} else if ((prot & ATR_P_T1) == ATR_P_T1) {
		return ("T=1");
	} else {
		return ("T=0, T=1");
	}
}

const char *
atr_convention_to_string(atr_convention_t conv)
{
	if (conv == ATR_CONVENTION_DIRECT) {
		return ("direct");
	} else if (conv == ATR_CONVENTION_INVERSE) {
		return ("inverse");
	} else {
		return ("<invalid convention>");
	}
}

const char *
atr_strerror(atr_parsecode_t code)
{
	switch (code) {
	case ATR_CODE_OK:
		return ("ATR parsed successfully");
	case ATR_CODE_TOO_SHORT:
		return ("Specified buffer too short");
	case ATR_CODE_TOO_LONG:
		return ("Specified buffer too long");
	case ATR_CODE_INVALID_TS:
		return ("ATR has invalid TS byte value");
	case ATR_CODE_OVERRUN:
		return ("ATR data requires more bytes than provided");
	case ATR_CODE_UNDERRUN:
		return ("ATR data did not use all provided bytes");
	case ATR_CODE_CHECKSUM_ERROR:
		return ("ATR data did not checksum correctly");
	case ATR_CODE_INVALID_TD1:
		return ("ATR data has invalid protocol in TD1");
	default:
		return ("Unknown Parse Code");
	}
}

static uint_t
atr_count_cbits(uint8_t x)
{
	uint_t ret = 0;

	if (x & ATR_TA_MASK)
		ret++;
	if (x & ATR_TB_MASK)
		ret++;
	if (x & ATR_TC_MASK)
		ret++;
	if (x & ATR_TD_MASK)
		ret++;
	return (ret);
}

/*
 * Parse out ATR values. Focus on only parsing it and not interpreting it.
 * Interpretation should be done in other functions that can walk over the data
 * and be more protocol-aware.
 */
atr_parsecode_t
atr_parse(const uint8_t *buf, size_t len, atr_data_t *data)
{
	uint_t nhist, cbits, ncbits, idx, Ti, prot;
	uint_t ncksum = 0;
	atr_ti_t *atp;

	/*
	 * Zero out data in case someone's come back around for another loop on
	 * the same data.
	 */
	bzero(data, sizeof (atr_data_t));

	if (len < ATR_LEN_MIN) {
		return (ATR_CODE_TOO_SHORT);
	}

	if (len > ATR_LEN_MAX) {
		return (ATR_CODE_TOO_LONG);
	}

	if (buf[ATR_TS_IDX] != ATR_TS_INVERSE &&
	    buf[ATR_TS_IDX] != ATR_TS_DIRECT) {
		return (ATR_CODE_INVALID_TS);
	}

	bcopy(buf, data->atr_raw, len);
	data->atr_nraw = len;

	if (buf[ATR_TS_IDX] == ATR_TS_DIRECT) {
		data->atr_flags |= ATR_F_USES_DIRECT;
	} else {
		data->atr_flags |= ATR_F_USES_INVERSE;
	}

	/*
	 * The protocol of T0 is the number of historical bits present.
	 */
	nhist = ATR_TD_PROT(buf[ATR_T0_IDX]);
	cbits = ATR_TD_NBITS(buf[ATR_T0_IDX]);
	idx = ATR_T0_IDX + 1;
	ncbits = atr_count_cbits(cbits);

	/*
	 * Ti is used to track the current iteration of T[A,B,C,D] that we are
	 * on, as the ISO/IEC standard suggests. The way that values are
	 * interpreted depends on the value of Ti.
	 *
	 * When Ti is one, TA, TB, and TC represent global properties. TD's
	 * protocol represents the preferred protocol.
	 *
	 * When Ti is two, TA, TB, and TC also represent global properties.
	 * However, TC only has meaning if the protocol is T=0.
	 *
	 * When Ti is 15, it indicates more global properties.
	 *
	 * For all other values of Ti, the meaning depends on the protocol in
	 * question and they are all properties specific to that protocol.
	 */
	Ti = 1;
	/*
	 * Initialize prot to an invalid protocol to help us deal with the
	 * normal workflow and make sure that we don't mistakenly do anything.
	 */
	prot = UINT32_MAX;
	for (;;) {
		atp = &data->atr_ti[data->atr_nti];
		data->atr_nti++;
		ASSERT3U(data->atr_nti, <=, ATR_TI_MAX);

		/*
		 * Make sure that we have enough space to read all the cbits.
		 * idx points to the first cbit, which could also potentially be
		 * over the length of the buffer. This is why we subtract one
		 * from idx when doing the calculation.
		 */
		if (idx - 1 + ncbits >= len) {
			return (ATR_CODE_OVERRUN);
		}

		ASSERT3U(Ti, !=, 0);

		/*
		 * At the moment we opt to ignore reserved protocols.
		 */
		atp->atrti_protocol = prot;
		atp->atrti_ti_val = Ti;
		atp->atrti_td_idx = idx - 1;

		if (cbits & ATR_TA_MASK) {
			atp->atrti_flags |= ATR_TI_HAVE_TA;
			atp->atrti_ta = buf[idx];
			idx++;
		}

		if (cbits & ATR_TB_MASK) {
			atp->atrti_flags |= ATR_TI_HAVE_TB;
			atp->atrti_tb = buf[idx];
			idx++;
		}

		if (cbits & ATR_TC_MASK) {
			atp->atrti_flags |= ATR_TI_HAVE_TC;
			atp->atrti_tc = buf[idx];
			idx++;
		}

		if (cbits & ATR_TD_MASK) {
			atp->atrti_flags |= ATR_TI_HAVE_TD;
			atp->atrti_td = buf[idx];
			cbits = ATR_TD_NBITS(buf[idx]);
			prot = ATR_TD_PROT(buf[idx]);
			ncbits = atr_count_cbits(cbits);
			if (prot != 0)
				ncksum = 1;

			/*
			 * T=15 is not allowed in TD1 per 8.2.3.
			 */
			if (Ti == 1 && prot == 0xf)
				return (ATR_CODE_INVALID_TD1);

			idx++;
			/*
			 * Encountering TD means that once we take the next loop
			 * and we need to increment Ti.
			 */
			Ti++;
		} else {
			break;
		}
	}

	/*
	 * We've parsed all of the cbits. At this point, we should take into
	 * account all of the historical bits and potentially the checksum.
	 */
	if (idx - 1 + nhist + ncksum >= len) {
		return (ATR_CODE_OVERRUN);
	}

	if (idx + nhist + ncksum != len) {
		return (ATR_CODE_UNDERRUN);
	}

	if (nhist > 0) {
		data->atr_nhistoric = nhist;
		bcopy(&buf[idx], data->atr_historic, nhist);
	}

	if (ncksum > 0) {
		size_t i;
		uint8_t val;

		/*
		 * Per ISO/IEC 7816-3:2006 Section 8.2.5 the checksum is all
		 * bytes excluding TS. Therefore, we must start at byte 1.
		 */
		for (val = 0, i = 1; i < len; i++) {
			val ^= buf[i];
		}

		if (val != ATR_CKSUM_TARGET) {
			return (ATR_CODE_CHECKSUM_ERROR);
		}
		data->atr_flags |= ATR_F_HAS_CHECKSUM;
		data->atr_cksum = buf[len - 1];
	}

	data->atr_flags |= ATR_F_VALID;
	return (ATR_CODE_OK);
}

uint8_t
atr_fi_default_index(void)
{
	return (ATR_FI_DEFAULT_INDEX);
}

uint8_t
atr_di_default_index(void)
{
	return (ATR_DI_DEFAULT_INDEX);
}

/*
 * Parse the data to determine which protocols are supported in this atr data.
 * Based on this, users can come and ask us to fill in protocol information.
 */
atr_protocol_t
atr_supported_protocols(atr_data_t *data)
{
	uint_t i;
	atr_protocol_t prot;

	if ((data->atr_flags & ATR_F_VALID) == 0)
		return (ATR_P_NONE);

	/*
	 * Based on 8.2.3 of ISO/IEC 7816-3:2006, if TD1 is present, then that
	 * indicates the first protocol. However, if it is not present, then
	 * that implies that T=0 is the only supported protocol. Otherwise, all
	 * protocols are referenced in ascending order. The first entry in
	 * atr_ti refers to data from T0, so the protocol in the second entry
	 * would have the TD1 data.
	 */
	if (data->atr_nti < 2) {
		return (ATR_P_T0);
	}

	prot = ATR_P_NONE;
	for (i = 0; i < data->atr_nti; i++) {
		switch (data->atr_ti[i].atrti_protocol) {
		case ATR_PROTOCOL_T0:
			prot |= ATR_P_T0;
			break;
		case ATR_PROTOCOL_T1:
			prot |= ATR_P_T1;
			break;
		default:
			/*
			 * T=15 is not a protocol, and all other protocol values
			 * are currently reserved for future use.
			 */
			continue;
		}
	}

	/*
	 * It's possible we've found nothing specific in the above loop (for
	 * example, only T=15 global bits were found). In that case, the card
	 * defaults to T=0.
	 */
	if (prot == ATR_P_NONE)
		prot = ATR_P_T0;

	return (prot);
}

boolean_t
atr_params_negotiable(atr_data_t *data)
{
	/* If for some reason we're called with invalid data, assume it's not */
	if ((data->atr_flags & ATR_F_VALID) == 0)
		return (B_FALSE);


	/*
	 * Whether or not we're negotiable is in the second global page, so atr
	 * index 1. If TA2 is missing, then the card always is negotiable.
	 */
	if (data->atr_nti < 2 ||
	    (data->atr_ti[1].atrti_flags & ATR_TI_HAVE_TA) == 0) {
		return (B_TRUE);
	}

	if (ATR_TA2_CANCHANGE(data->atr_ti[1].atrti_ta)) {
		return (B_TRUE);
	}

	return (B_FALSE);
}

atr_protocol_t
atr_default_protocol(atr_data_t *data)
{
	uint8_t prot;

	if ((data->atr_flags & ATR_F_VALID) == 0)
		return (ATR_P_NONE);
	/*
	 * If we don't have an TA2 byte, then the system defaults to T=0.
	 */
	if (data->atr_nti < 2) {
		return (ATR_P_T0);
	}

	/*
	 * If TA2 is present, then it encodes the default protocol. Otherwise,
	 * we have to grab the protocol value from TD1, which is called the
	 * 'first offered protocol'.
	 */
	if ((data->atr_ti[1].atrti_flags & ATR_TI_HAVE_TA) != 0) {
		prot = ATR_TA2_PROTOCOL(data->atr_ti[1].atrti_ta);
	} else {
		prot = data->atr_ti[1].atrti_protocol;
	}

	switch (prot) {
	case ATR_PROTOCOL_T0:
		return (ATR_P_T0);
	case ATR_PROTOCOL_T1:
		return (ATR_P_T1);
	default:
		return (ATR_P_NONE);
	}
}

uint8_t
atr_fi_index(atr_data_t *data)
{
	if (data->atr_nti < 1) {
		return (ATR_FI_DEFAULT_INDEX);
	}

	/*
	 * If TA is specified, it is present in TA1. TA2 may override its
	 * presence, so if it is here, check that first to determine whether or
	 * not we should check TA1.
	 */
	if (data->atr_nti >= 2 &&
	    (data->atr_ti[1].atrti_flags & ATR_TI_HAVE_TA) != 0) {
		if (!ATR_TA2_HONORTA1(data->atr_ti[1].atrti_ta)) {
			return (ATR_FI_DEFAULT_INDEX);
		}
	}

	if ((data->atr_ti[0].atrti_flags & ATR_TI_HAVE_TA) != 0) {
		return (ATR_TA1_FTABLE(data->atr_ti[0].atrti_ta));
	}

	return (ATR_FI_DEFAULT_INDEX);
}

uint8_t
atr_di_index(atr_data_t *data)
{
	if (data->atr_nti < 1) {
		return (ATR_DI_DEFAULT_INDEX);
	}

	/*
	 * If TA is specified, it is present in TA1. TA2 may override its
	 * presence, so if it is here, check that first to determine whether or
	 * not we should check TA1.
	 */
	if (data->atr_nti >= 2 &&
	    (data->atr_ti[1].atrti_flags & ATR_TI_HAVE_TA) != 0) {
		if (!ATR_TA2_HONORTA1(data->atr_ti[1].atrti_ta)) {
			return (ATR_DI_DEFAULT_INDEX);
		}
	}

	if ((data->atr_ti[0].atrti_flags & ATR_TI_HAVE_TA) != 0) {
		return (ATR_TA1_DITABLE(data->atr_ti[0].atrti_ta));
	}

	return (ATR_DI_DEFAULT_INDEX);
}

atr_convention_t
atr_convention(atr_data_t *data)
{
	if ((data->atr_flags & ATR_F_USES_DIRECT) != 0) {
		return (ATR_CONVENTION_DIRECT);
	}
	return (ATR_CONVENTION_INVERSE);
}

uint8_t
atr_extra_guardtime(atr_data_t *data)
{
	if ((data->atr_flags & ATR_F_VALID) == 0)
		return (ATR_EXTRA_GUARDTIME_DEFAULT);

	if (data->atr_nti >= 1 &&
	    (data->atr_ti[0].atrti_flags & ATR_TI_HAVE_TC) != 0) {
		return (data->atr_ti[0].atrti_tc);
	}

	return (ATR_EXTRA_GUARDTIME_DEFAULT);
}

uint8_t
atr_t0_wi(atr_data_t *data)
{
	if ((data->atr_flags & ATR_F_VALID) == 0)
		return (ATR_T0_WI_DEFAULT);

	/*
	 * This is stored in the optional global byte in TC2; however, it only
	 * applies to T=0.
	 */
	if (data->atr_nti >= 2 &&
	    data->atr_ti[1].atrti_protocol == ATR_PROTOCOL_T0 &&
	    (data->atr_ti[1].atrti_flags & ATR_TI_HAVE_TC) != 0) {
		return (data->atr_ti[1].atrti_tc);
	}

	return (ATR_T0_WI_DEFAULT);
}

uint8_t
atr_t1_cwi(atr_data_t *data)
{
	uint8_t i;

	if (data->atr_nti <= 2) {
		return (ATR_T1_CWI_DEFAULT);
	}

	for (i = 2; i < data->atr_nti; i++) {
		if (data->atr_ti[i].atrti_protocol == ATR_PROTOCOL_T1) {
			if ((data->atr_ti[i].atrti_flags & ATR_TI_HAVE_TB) !=
			    0) {
				uint8_t tb = data->atr_ti[i].atrti_tb;
				return (ATR_T1_TB0_CWI(tb));
			}

			return (ATR_T1_CWI_DEFAULT);
		}
	}

	return (ATR_T1_CWI_DEFAULT);
}

atr_clock_stop_t
atr_clock_stop(atr_data_t *data)
{
	uint8_t i;

	for (i = 0; i < data->atr_nti; i++) {
		if (data->atr_ti[i].atrti_protocol == ATR_PROTOCOL_T15) {
			if ((data->atr_ti[i].atrti_flags & ATR_TI_HAVE_TA) !=
			    0) {
				uint8_t ta = data->atr_ti[i].atrti_ta;
				return (ATR_T15_TA0_CLOCK(ta));
			}

			return (ATR_CLOCK_STOP_NONE);
		}
	}

	return (ATR_CLOCK_STOP_NONE);
}

atr_t1_checksum_t
atr_t1_checksum(atr_data_t *data)
{
	uint8_t i;

	if (data->atr_nti <= 2) {
		return (ATR_T1_CHECKSUM_DEFAULT);
	}

	for (i = 2; i < data->atr_nti; i++) {
		if (data->atr_ti[i].atrti_protocol == ATR_PROTOCOL_T1) {
			if ((data->atr_ti[i].atrti_flags & ATR_TI_HAVE_TC) !=
			    0) {
				if (ATR_T1_TC0_CRC(data->atr_ti[i].atrti_tc)) {
					return (ATR_T1_CHECKSUM_CRC);
				} else {
					return (ATR_T1_CHECKSUM_LRC);
				}
			}

			return (ATR_T1_CHECKSUM_DEFAULT);
		}
	}

	return (ATR_T1_CHECKSUM_DEFAULT);

}

uint8_t
atr_t1_bwi(atr_data_t *data)
{
	uint8_t i;

	if (data->atr_nti <= 2) {
		return (ATR_T1_BWI_DEFAULT);
	}

	for (i = 2; i < data->atr_nti; i++) {
		if (data->atr_ti[i].atrti_protocol == ATR_PROTOCOL_T1) {
			if ((data->atr_ti[i].atrti_flags & ATR_TI_HAVE_TB) !=
			    0) {
				uint8_t tb = data->atr_ti[i].atrti_tb;
				return (ATR_T1_TB0_BWI(tb));
			}

			return (ATR_T1_BWI_DEFAULT);
		}
	}

	return (ATR_T1_BWI_DEFAULT);
}

uint8_t
atr_t1_ifsc(atr_data_t *data)
{
	uint8_t i;

	if (data->atr_nti <= 2) {
		return (ATR_T1_IFSC_DEFAULT);
	}

	for (i = 2; i < data->atr_nti; i++) {
		if (data->atr_ti[i].atrti_protocol == ATR_PROTOCOL_T1) {
			if ((data->atr_ti[i].atrti_flags & ATR_TI_HAVE_TA) !=
			    0) {
				return (data->atr_ti[i].atrti_ta);
			}

			return (ATR_T1_IFSC_DEFAULT);
		}
	}

	return (ATR_T1_IFSC_DEFAULT);
}

/*
 * Attempt to determine which set of data rates we should be able to use for a
 * given class of protocol. Here we want to do the calculation based on the CCID
 * specification, section 9.4.x. To use these higher rates we need:
 *
 * + Reader's data rate > frequency * Di / Fi.
 *
 * To determine which rate and frequency we use, we look at the reader's
 * features. If the reader supports both the Automatic baud rate and automatic
 * ICC clock frequency change, then we use the _maximum_ rate. Otherwise we will
 * indicate that we can use the ATR's properties, but will require changing the
 * default data rate.
 *
 * Now, some ICC devices are not negotiable. In those cases, we'll see if we can
 * fit it in with either the default or maximum data rates. If not, then we'll
 * not be able to support this card.
 *
 * There are two wrinkles that exist in this. The first is supported frequencies
 * and data rates. If there are no additional data rates supported, then all of
 * the data rates between the default and max are supported. If not, then only
 * those specified in the data rates array are supported.
 *
 * The second hurdle is that we need to do this division and try and avoid the
 * pitfalls of floating point arithmetic, as floating point is not allowed in
 * the kernel (and this is shared). Importantly that means only integers are
 * allowed here.
 */
atr_data_rate_choice_t
atr_data_rate(atr_data_t *data, ccid_class_descr_t *class, uint32_t *rates,
    uint_t nrates, uint32_t *dataratep)
{
	uint_t nfeats = CCID_CLASS_F_AUTO_ICC_CLOCK | CCID_CLASS_F_AUTO_BAUD;
	uint8_t di, fi;
	uint_t dival, fival;
	boolean_t autospeed, negotiable, exprates;
	uint64_t maxval, defval;

	if ((data->atr_flags & ATR_F_VALID) == 0)
		return (ATR_RATE_UNSUPPORTED);

	di = atr_di_index(data);
	fi = atr_fi_index(data);
	dival = atr_di_index_to_value(di);
	fival = atr_fi_index_to_value(fi);
	autospeed = (class->ccd_dwFeatures & nfeats) == nfeats;
	exprates = class->ccd_bNumDataRatesSupported != 0;
	negotiable = atr_params_negotiable(data);

	/*
	 * We don't support cards with fixed rates at this time as it's not
	 * clear what that rate should be. If it's negotiable, we'll let them
	 * run at the default. Otherwise, we have to fail the request until
	 * we implement the logic to search their data rates.
	 */
	if (exprates) {
		if (negotiable) {
			return (ATR_RATE_USEDEFAULT);
		}
		return (ATR_RATE_UNSUPPORTED);
	}

	/*
	 * This indicates that the card gave us values that were reserved for
	 * future use. If we could negotiate it, then just stick with the
	 * default paramters. Otherwise, return that we can't support this ICC.
	 */
	if (dival == 0 || fival == 0) {
		if (negotiable)
			return (ATR_RATE_USEDEFAULT);
		return (ATR_RATE_UNSUPPORTED);
	}

	/*
	 * Calculate the maximum and default values.
	 */
	maxval = class->ccd_dwMaximumClock * 1000;
	maxval *= dival;
	maxval /= fival;

	defval = class->ccd_dwDefaultClock * 1000;
	defval *= dival;
	defval /= fival;

	/*
	 * We're allowed any set of data rates between the default and the
	 * maximum. Check if the maximum data rate will work for either the
	 * default or maximum clock. If so, then we can use the cards rates.
	 *
	 * To account for the fact that we may have had a fractional value,
	 * we require a strict greater than comparison.
	 */
	if ((uint64_t)class->ccd_dwMaxDataRate > maxval ||
	    (uint64_t)class->ccd_dwMaxDataRate > defval) {
		if (autospeed) {
			return (ATR_RATE_USEATR);
		}
	}

	/*
	 * If the CCID reader can't handle the ICC's proposed rates, then fall
	 * back to the defaults if we're allowed to negotiate. Otherwise, we're
	 * not able to use this ICC.
	 */
	if (negotiable) {
		return (ATR_RATE_USEDEFAULT);
	}

	return (ATR_RATE_UNSUPPORTED);
}

void
atr_data_reset(atr_data_t *data)
{
	bzero(data, sizeof (*data));
}

#ifdef	_KERNEL
atr_data_t *
atr_data_alloc(void)
{
	return (kmem_zalloc(sizeof (atr_data_t), KM_SLEEP));
}

void
atr_data_free(atr_data_t *data)
{
	kmem_free(data, sizeof (atr_data_t));
}

/*
 * Make sure that the response we got from the ICC is valid. It must pass
 * checksum and have the PPSS value set correctly. The protocol must match
 * what we requested; however, the PPS1-3 bits are a bit different. They may
 * only be set in the response if we set them in the request. However, they
 * do not have to be set in the response.
 */
boolean_t
atr_pps_valid(void *reqbuf, size_t reqlen, void *respbuf, size_t resplen)
{
	uint8_t val, i, reqidx, respidx;
	uint8_t *req = reqbuf, *resp = respbuf;

	if (resplen > PPS_LEN_MAX || resplen < PPS_LEN_MIN)
		return (B_FALSE);

	/*
	 * Before we validate the data, make sure the checksum is valid.
	 */
	for (i = 0, val = 0; i < resplen; i++) {
		val ^= resp[i];
	}

	/* Checksum failure */
	if (val != 0) {
		return (B_FALSE);
	}

	/*
	 * We should always have PPSS echoed back as we set it.
	 */
	if (resp[PPS_PPSS_INDEX] != PPS_PPSS_VAL) {
		return (B_FALSE);
	}

	/*
	 * Go through and make sure the number of bytes present makes sense for
	 * the number of bits set in PPS1.
	 */
	val = PPS_LEN_MIN;
	if (resp[PPS_PPS0_INDEX] & PPS_PPS0_PPS1)
		val++;
	if (resp[PPS_PPS0_INDEX] & PPS_PPS0_PPS2)
		val++;
	if (resp[PPS_PPS0_INDEX] & PPS_PPS0_PPS3)
		val++;
	if (val != resplen)
		return (B_FALSE);

	/*
	 * Now we've finally verified that the response is syntactically valid.
	 * We must go through and make sure that it is semantically valid.
	 */
	if (PPS_PPS0_PROT(req[PPS_PPS0_INDEX]) !=
	    PPS_PPS0_PROT(resp[PPS_PPS0_INDEX])) {
		return (B_FALSE);
	}

	/*
	 * When checking the PPS bit and extensions, we first check in the
	 * response as a bit in the request is allowed to not be in the
	 * response. But not the opposite way around. We also have to keep track
	 * of the fact that the index for values will vary.
	 */
	reqidx = respidx = PPS_PPS0_INDEX + 1;
	if ((resp[PPS_PPS0_INDEX] & PPS_PPS0_PPS1) != 0) {
		if ((req[PPS_PPS0_INDEX] & PPS_PPS0_PPS1) == 0) {
			return (B_FALSE);
		}

		if (req[reqidx] != resp[respidx]) {
			return (B_FALSE);
		}

		reqidx++;
		respidx++;
	} else if ((req[PPS_PPS0_INDEX] & PPS_PPS0_PPS1) != 0) {
		reqidx++;
	}

	if ((resp[PPS_PPS0_INDEX] & PPS_PPS0_PPS2) != 0) {
		if ((req[PPS_PPS0_INDEX] & PPS_PPS0_PPS2) == 0) {
			return (B_FALSE);
		}

		if (req[reqidx] != resp[respidx]) {
			return (B_FALSE);
		}

		reqidx++;
		respidx++;
	} else if ((req[PPS_PPS0_INDEX] & PPS_PPS0_PPS2) != 0) {
		reqidx++;
	}

	if ((resp[PPS_PPS0_INDEX] & PPS_PPS0_PPS3) != 0) {
		/*
		 * At this time, we never specify PPS3 in a request. Therefore
		 * if it is present in the response, treat this as an invalid
		 * request.
		 */
		return (B_FALSE);
	}

	return (B_TRUE);
}

uint_t
atr_pps_generate(uint8_t *buf, size_t buflen, atr_protocol_t prot,
    boolean_t pps1, uint8_t fi, uint8_t di, boolean_t pps2, uint8_t spu)
{
	uint8_t protval, cksum, i;
	uint_t len = 0;

	if (buflen < PPS_BUFFER_MAX)
		return (0);

	buf[PPS_PPSS_INDEX] = PPS_PPSS_VAL;
	switch (prot) {
	case ATR_P_T0:
		protval = 0;
		break;
	case ATR_P_T1:
		protval = 1;
		break;
	default:
		return (0);
	}

	buf[PPS_PPS0_INDEX] = PPS_PPS0_PROT(protval);
	len = 2;
	if (pps1) {
		buf[PPS_PPS0_INDEX] |= PPS_PPS0_PPS1;
		buf[len++] = PPS_PPS1_SETVAL(fi, di);
	}

	if (pps2) {
		buf[PPS_PPS0_INDEX] |= PPS_PPS0_PPS2;
		buf[len++] = spu;
	}

	/*
	 * The checksum must xor to zero.
	 */
	for (i = 0, cksum = 0; i < len; i++) {
		cksum ^= buf[i];
	}
	buf[len++] = cksum;
	return (len);
}

/*
 * The caller of this wants to know if the Fi/Di values that they proposed were
 * accepted. The caller must have already called atr_pps_valid(). At this point,
 * we can say that the value was accepted if the PPS1 bit is set.
 */
boolean_t
atr_pps_fidi_accepted(void *respbuf, size_t len)
{
	uint8_t *resp = respbuf;
	return ((resp[PPS_PPS0_INDEX] & PPS_PPS0_PPS1) != 0);
}

#else	/* !_KERNEL */
atr_data_t *
atr_data_alloc(void)
{
	return (calloc(1, sizeof (atr_data_t)));
}

void
atr_data_free(atr_data_t *data)
{
	if (data == NULL)
		return;
	free(data);
}

/*
 * This table maps the bit values for Fi from 7816-3:2006 section 8.3 Table 9.
 * The table is up to 6 bits wide. Entries not present are RFU. We use NULL as a
 * sentinel to indicate that.
 */
static const char *atr_voltage_table[64] = {
	NULL,			/* 00 0000 */
	"5V",			/* 00 0001 */
	"3V",			/* 00 0010 */
	"5V, 3V",		/* 00 0011 */
	"1.5V",			/* 00 0100 */
	NULL,			/* 00 0101 */
	"3V, 1.5V",		/* 00 0110 */
	"5V, 3V, 1.5V"		/* 00 0111 */
};

static void
atr_data_dump_ta(atr_ti_t *atp, FILE *out, uint_t level)
{
	uint8_t ta;

	if (!(atp->atrti_flags & ATR_TI_HAVE_TA)) {
		return;
	}

	ta = atp->atrti_ta;
	(void) fprintf(out, "   %c%c%c+-> TA%u 0x%02x",
	    atp->atrti_flags & ATR_TI_HAVE_TD ? '|' : ' ',
	    atp->atrti_flags & ATR_TI_HAVE_TC ? '|' : ' ',
	    atp->atrti_flags & ATR_TI_HAVE_TB ? '|' : ' ',
	    atp->atrti_ti_val, ta);
	switch (atp->atrti_ti_val) {
	case 1:
		(void) fprintf(out, "; Fi: %s, F(max): %s MHz, Di: %s",
		    atr_fi_table[ATR_TA1_FTABLE(ta)],
		    atr_fmax_table[ATR_TA1_FTABLE(ta)],
		    atr_di_table[ATR_TA1_DITABLE(ta)]);
		break;
	case 2:
		(void) fprintf(out, "; ICC in %s mode; %shonoring TA1; default "
		    "T=%u",
		    ATR_TA2_CANCHANGE(ta) ? "negotiable" : "specific",
		    ATR_TA2_HONORTA1(ta) ? "" : "not ",
		    ATR_TA2_PROTOCOL(ta));
		break;
	default:
		switch (atp->atrti_protocol) {
		case ATR_PROTOCOL_T1:
			if (level != 0)
				break;
			if (ta == 0 || ta == 0xff) {
				(void) fprintf(out, "; IFSC: RFU");
			} else {
				(void) fprintf(out, "; IFSC: %u", ta);
			}
			break;
		case ATR_PROTOCOL_T15:
			if (level != 0)
				break;
			(void) fprintf(out, "; Clock stop: %s, Supported "
			    "Voltage: %s",
			    atr_clock_table[ATR_T15_TA0_CLOCK(ta)],
			    atr_voltage_table[ATR_T15_TA0_VOLTAGE(ta)] != NULL ?
			    atr_voltage_table[ATR_T15_TA0_VOLTAGE(ta)] : "RFU");
			break;
		default:
			break;
		}
	}
	(void) fprintf(out, "\n");
}

static void
atr_data_dump_tb(atr_ti_t *atp, FILE *out, uint_t level)
{
	uint8_t tb;

	if (!(atp->atrti_flags & ATR_TI_HAVE_TB)) {
		return;
	}

	tb = atp->atrti_tb;
	(void) fprintf(out, "   %c%c+--> TB%u 0x%02x",
	    atp->atrti_flags & ATR_TI_HAVE_TD ? '|' : ' ',
	    atp->atrti_flags & ATR_TI_HAVE_TC ? '|' : ' ',
	    atp->atrti_ti_val, tb);
	switch (atp->atrti_ti_val) {
	case 1:
	case 2:
		(void) fprintf(out, "; deprecated");
		break;
	default:
		switch (atp->atrti_protocol) {
		case ATR_PROTOCOL_T1:
			if (level != 0)
				break;
			(void) fprintf(out, "; CWI: %u, BWI: %u\n",
			    ATR_T1_TB0_CWI(tb),
			    ATR_T1_TB0_BWI(tb));
			break;
		case ATR_PROTOCOL_T15:
			if (level != 0)
				break;
			(void) fprintf(out, "; SPU: %s", tb == 0 ? "not used" :
			    ATR_T15_TB0_SPU_STANDARD(tb) ? "standard" :
			    "proprietary");
			break;
		default:
			break;
		}
	}
	(void) fprintf(out, "\n");
}

static void
atr_data_dump_tc(atr_ti_t *atp, FILE *out, uint_t level)
{
	uint8_t tc;

	if (!(atp->atrti_flags & ATR_TI_HAVE_TC)) {
		return;
	}

	tc = atp->atrti_tc;
	(void) fprintf(out, "   %c+---> TC%u 0x%02x",
	    atp->atrti_flags & ATR_TI_HAVE_TD ? '|' : ' ',
	    atp->atrti_ti_val, tc);

	switch (atp->atrti_ti_val) {
	case 1:
		(void) fprintf(out, "; Extra Guard Time Integer: %u", tc);
		break;
	case 2:
		if (atp->atrti_protocol != ATR_PROTOCOL_T0) {
			(void) fprintf(out, "; illegal value -- only valid for "
			    "T=0");
		} else {
			(void) fprintf(out, "; Waiting Time Integer: %u", tc);
		}
		break;
	default:
		switch (atp->atrti_protocol) {
		case ATR_PROTOCOL_T1:
			if (level != 0)
				break;
			(void) fprintf(out, "; Error Detection Code: %s",
			    ATR_T1_TC0_CRC(tc) ? "CRC" : "LRC");
			break;
		default:
			break;
		}
	}
	(void) fprintf(out, "\n");
}

void
atr_data_hexdump(const uint8_t *buf, size_t nbytes, FILE *out)
{
	size_t i, j;

	/* Print out the header */
	(void) fprintf(out, "%*s    0", 4, "");
	for (i = 1; i < 16; i++) {
		if (i % 4 == 0 && i % 16 != 0) {
			(void) fprintf(out, " ");
		}

		(void) fprintf(out, "%2x", i);
	}
	(void) fprintf(out, "  0123456789abcdef\n");

	/* Print out data */
	for (i = 0; i < nbytes; i++) {

		if (i % 16 == 0) {
			(void) fprintf(out, "%04x:  ", i);
		}

		if (i % 4 == 0 && i % 16 != 0) {
			(void) fprintf(out, " ");
		}

		(void) fprintf(out, "%02x", buf[i]);

		if (i % 16 == 15 || i + 1 == nbytes) {
			for (j = (i % 16) + 1; j < 16; j++) {
				if (j % 4 == 0 && j % 16 != 0) {
					(void) fprintf(out, " ");
				}

				(void) fprintf(out, "  ");
			}

			(void) fprintf(out, "  ");
			for (j = i - (i % 16); j <= i; j++) {
				(void) fprintf(out, "%c",
				    isprint(buf[j]) ? buf[j] : '.');
			}
			(void) printf("\n");
		}
	}
}

static void
atr_data_hexdump_historical(atr_data_t *data, FILE *out)
{
	(void) fprintf(out, "Dumping raw historical bytes\n");

	atr_data_hexdump(data->atr_historic, data->atr_nhistoric, out);
}

static void
atr_data_dump_historical(atr_data_t *data, FILE *out)
{
	uint8_t cat;

	(void) fprintf(out, "Historic Data: %u bytes", data->atr_nhistoric);
	if (data->atr_nhistoric == 0) {
		(void) fprintf(out, "\n");
		return;
	}

	cat = data->atr_historic[0];
	(void) fprintf(out, "; format (0x%02x) ", cat);
	if (cat == ATR_HIST_CAT_MAND_STATUS) {
		(void) fprintf(out, "card status, not shown");
	} else if (cat == ATR_HIST_CAT_TLV_STATUS) {
		(void) fprintf(out, "COMPACT-TLV, not shown");
	} else if (cat >= ATR_HIST_CAT_RFU_MIN && cat <= ATR_HIST_CAT_RFU_MAX) {
		(void) fprintf(out, "reserved\n");
		atr_data_hexdump_historical(data, out);
		return;
	} else {
		(void) fprintf(out, "proprietary\n");
		atr_data_hexdump_historical(data, out);
		return;
	}
}

void
atr_data_dump(atr_data_t *data, FILE *out)
{
	uint8_t i, level;
	if ((data->atr_flags & ATR_F_VALID) == 0)
		return;

	(void) fprintf(out, "TS  0x%02u - ", data->atr_raw[0]);
	if (data->atr_flags & ATR_F_USES_DIRECT) {
		(void) fprintf(out, "direct convention\n");
	} else {
		(void) fprintf(out, "inverse convention\n");
	}

	level = 0;
	for (i = 0; i < data->atr_nti; i++) {
		atr_ti_t *atp = &data->atr_ti[i];

		/*
		 * Various protocols may appear multiple times, indicating
		 * different sets of bits each time. When dealing with T0 and
		 * TD1, the protocol doesn't matter. Otherwise if we have the
		 * same value, we should increment this.
		 */
		if (i <= 2) {
			level = 0;
		} else if (atp->atrti_protocol ==
		    data->atr_ti[i - 1].atrti_protocol) {
			level++;
		} else {
			level = 0;
		}

		if (i == 0) {
			(void) fprintf(out, "T0  ");
		} else {
			(void) fprintf(out, "TD%u ", i);
		}
		(void) fprintf(out, "0x%02x\n",
		    data->atr_raw[atp->atrti_td_idx]);
		(void) fprintf(out, "      |+-> ");
		if (i == 0) {
			(void) fprintf(out, "%u historical bytes\n",
			    data->atr_nhistoric);
		} else {
			(void) fprintf(out, "protocol T=%u\n",
			    atp->atrti_protocol);
		}
		(void) fprintf(out, "      v\n");
		(void) fprintf(out, " 0r%u%u%u%u\n",
		    atp->atrti_flags & ATR_TI_HAVE_TD ? 1 : 0,
		    atp->atrti_flags & ATR_TI_HAVE_TC ? 1 : 0,
		    atp->atrti_flags & ATR_TI_HAVE_TB ? 1 : 0,
		    atp->atrti_flags & ATR_TI_HAVE_TA ? 1 : 0);

		atr_data_dump_ta(atp, out, level);
		atr_data_dump_tb(atp, out, level);
		atr_data_dump_tc(atp, out, level);
		if (atp->atrti_flags & ATR_TI_HAVE_TD) {
			(void) fprintf(out, "   v\n");
		}
	}

	atr_data_dump_historical(data, out);

	if (data->atr_flags & ATR_F_HAS_CHECKSUM) {
		(void) fprintf(out, "TCK  0x%02x\n", data->atr_cksum);
	} else {
		(void) fprintf(out, "TCK  ----; Checksum not present\n");
	}

}
#endif	/* _KERNEL */