#pragma once
/* CCID Protocol related Definitions
 *
 * (C) 2019-2020 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 as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * 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 <stdint.h>
#include <osmocom/core/utils.h>

#define USB_CLASS_CCID 11

/* Identifies the length of type of subordinate descriptors of a CCID device
 * Table 5.1-1 Smart Card Device Class descriptors */
struct usb_ccid_class_descriptor {
	uint8_t  bLength;
	uint8_t  bDescriptorType;
	uint16_t bcdCCID;
	uint8_t  bMaxSlotIndex;
	uint8_t  bVoltageSupport;
	uint32_t dwProtocols;
	uint32_t dwDefaultClock;
	uint32_t dwMaximumClock;
	uint8_t  bNumClockSupported;
	uint32_t dwDataRate;
	uint32_t dwMaxDataRate;
	uint8_t  bNumDataRatesSupported;
	uint32_t dwMaxIFSD;
	uint32_t dwSynchProtocols;
	uint32_t dwMechanical;
	uint32_t dwFeatures;
	uint32_t dwMaxCCIDMessageLength;
	uint8_t  bClassGetResponse;
	uint8_t  bClassEnvelope;
	uint16_t wLcdLayout;
	uint8_t  bPINSupport;
	uint8_t  bMaxCCIDBusySlots;
} __attribute__((packed));
/* handling of bulk out from host */

enum ccid_msg_type {
	/* Section 6.3 / Table 6.3-1: Interrupt IN */
	RDR_to_PC_NotifySlotChange		= 0x50,
	RDR_to_PC_HardwareError			= 0x51,

	/* Section 6.1 / Table 6.1-1: Bulk OUT */
	PC_to_RDR_IccPowerOn			= 0x62,
	PC_to_RDR_IccPowerOff			= 0x63,
	PC_to_RDR_GetSlotStatus			= 0x65,
	PC_to_RDR_XfrBlock			= 0x6f,
	PC_to_RDR_GetParameters			= 0x6c,
	PC_to_RDR_ResetParameters		= 0x6d,
	PC_to_RDR_SetParameters			= 0x61,
	PC_to_RDR_Escape			= 0x6b,
	PC_to_RDR_IccClock			= 0x6e,
	PC_to_RDR_T0APDU			= 0x6a,
	PC_to_RDR_Secure			= 0x69,
	PC_to_RDR_Mechanical			= 0x71,
	PC_to_RDR_Abort				= 0x72,
	PC_to_RDR_SetDataRateAndClockFrequency	= 0x73,

	/* Section 6.2 / Table 6.2-1: Bulk IN */
	RDR_to_PC_DataBlock			= 0x80,
	RDR_to_PC_SlotStatus			= 0x81,
	RDR_to_PC_Parameters			= 0x82,
	RDR_to_PC_Escape			= 0x83,
	RDR_to_PC_DataRateAndClockFrequency	= 0x84,
};

/* CCID message header on BULK-OUT endpoint */
struct ccid_header {
	uint8_t		bMessageType;
	uint32_t	dwLength;
	uint8_t		bSlot;
	uint8_t		bSeq;
} __attribute__ ((packed));

/* CCID Class-Specific Control Request (Section 5.3 / Table 5.3-1) */
enum ccid_class_spec_req {
	CLASS_SPEC_CCID_ABORT		= 0x01,
	CLASS_SPEC_CCID_GET_CLOCK_FREQ	= 0x02,
	CLASS_SPEC_CCID_GET_DATA_RATES	= 0x03
};

/***********************************************************************
 * Bulk OUT
 ***********************************************************************/

/* Section 6.1.1 */
enum ccid_power_select {
	CCID_PWRSEL_AUTO	= 0x00,
	CCID_PWRSEL_5V0		= 0x01,
	CCID_PWRSEL_3V0		= 0x02,
	CCID_PWRSEL_1V8		= 0x03,
};

/* Section 6.1.1 */
struct ccid_pc_to_rdr_icc_power_on {
	struct ccid_header hdr;
	uint8_t bPowerSelect;
	uint8_t abRFU[2];
} __attribute__ ((packed));
/* Response: RDR_to_PC_DataBlock */

/* Section 6.1.2 */
struct ccid_pc_to_rdr_icc_power_off {
	struct ccid_header hdr;
	uint8_t abRFU[3];
} __attribute__ ((packed));
/* Response: RDR_to_PC_SlotStatus */

/* Section 6.1.3 */
struct ccid_pc_to_rdr_get_slot_status {
	struct ccid_header hdr;
	uint8_t abRFU[3];
} __attribute__ ((packed));
/* Response: RDR_to_PC_SlotStatus */

/* Section 6.1.4 */
struct ccid_pc_to_rdr_xfr_block {
	struct ccid_header hdr;
	uint8_t bBWI;
	uint16_t wLevelParameter;
	uint8_t abData[0];
} __attribute__ ((packed));
/* Response: RDR_to_PC_DataBlock */

/* Section 6.1.5 */
struct ccid_pc_to_rdr_get_parameters {
	struct ccid_header hdr;
	uint8_t abRFU[3];
} __attribute__ ((packed));
/* Response: RDR_to_PC_Parameters */

/* Section 6.1.6 */
struct ccid_pc_to_rdr_reset_parameters {
	struct ccid_header hdr;
	uint8_t abRFU[3];
} __attribute__ ((packed));
/* Response: RDR_to_PC_Parameters */

/* Section 6.1.7 */
enum ccid_protocol_num {
	CCID_PROTOCOL_NUM_T0	= 0x00,
	CCID_PROTOCOL_NUM_T1	= 0x01,
	CCID_PROTOCOL_NUM_2WIRE	= 0x80,
	CCID_PROTOCOL_NUM_3WIRE	= 0x81,
	CCID_PROTOCOL_NUM_I2C	= 0x82,
};
enum ccid_clock_stop {
	CCID_CLOCK_STOP_NOTALLOWED	= 0x00,
	CCID_CLOCK_STOP_LOW		= 0x01,
	CCID_CLOCK_STOP_HIGH		= 0x02,
	CCID_CLOCK_STOP_EITHER		= 0x03,
};
enum ccid_t1_csum_type {
	CCID_CSUM_TYPE_LRC	= 0,
	CCID_CSUM_TYPE_CRC	= 1,
};
struct ccid_proto_data_t0 {
	uint8_t bmFindexDindex;
	uint8_t bmTCCKST0;
	uint8_t bGuardTimeT0;
	uint8_t bWaitingIntegerT0;
	uint8_t bClockStop;
} __attribute__ ((packed));
struct ccid_proto_data_t1 {
	uint8_t bmFindexDindex;
	uint8_t bmTCCKST1;
	uint8_t bGuardTimeT1;
	uint8_t bWaitingIntegersT1;
	uint8_t bClockStop;
	uint8_t bIFSC;
	uint8_t bNadValue;
} __attribute__ ((packed));
struct ccid_pc_to_rdr_set_parameters {
	struct ccid_header hdr;
	uint8_t bProtocolNum;
	uint8_t abRFU[2];
	union {
		struct ccid_proto_data_t0 t0;
		struct ccid_proto_data_t1 t1;
	} abProtocolData;
} __attribute__ ((packed));
/* Response: RDR_to_PC_Parameters */

/* Section 6.1.8 */
struct ccid_pc_to_rdr_escape {
	struct ccid_header hdr;
	uint8_t abRFU[3];
	uint8_t abData[0];
} __attribute__ ((packed));
/* Response: RDR_to_PC_Escape */

/* Section 6.1.9 */
enum ccid_clock_command {
	CCID_CLOCK_CMD_RESTART		= 0x00,
	CCID_CLOCK_CMD_STOP		= 0x01,
};
struct ccid_pc_to_rdr_icc_clock {
	struct ccid_header hdr;
	uint8_t bClockCommand;
	uint8_t abRFU[2];
} __attribute__ ((packed));
/* response: RDR_to_PC_SlotStatus */

/* Section 6.1.10 */
struct ccid_pc_to_rdr_t0apdu {
	struct ccid_header hdr;
	uint8_t bmChanges;
	uint8_t bClassGetResponse;
	uint8_t bClassEnvelope;
} __attribute__ ((packed));
/* Response: RDR_to_PC_SlotStatus */

/* Section 6.1.11 */
struct ccid_pc_to_rdr_secure {
	struct ccid_header hdr;
	uint8_t bBWI;
	uint16_t wLevelParameter;
	uint8_t abData[0];
} __attribute__ ((packed));
struct ccid_pin_operation_data {
	uint8_t bPINOperation;
	uint8_t abPNDataStructure[0];
} __attribute__ ((packed));
struct ccid_pin_verification_data {
	uint8_t bTimeOut;
	uint8_t bmFormatString;
	uint8_t bmPINBlockString;
	uint8_t bmPINLengthFormat;
	uint16_t wPINMaxExtraDigit;
	uint8_t bEntryValidationCondition;
	uint8_t bNumberMessage;
	uint16_t wLangId;
	uint8_t bMsgIndex;
	uint8_t bTecPrologue;
	uint8_t abPINApdu[0];
} __attribute__ ((packed));
/* Response: RDR_to_PC_DataBlock */

/* Section 6.1.12 */
struct ccid_pc_to_rdr_mechanical {
	struct ccid_header hdr;
	uint8_t bFunction; /* ccid_mech_function */
	uint8_t abRFU[2];
} __attribute__ ((packed));
enum ccid_mech_function {
	CCID_MECH_FN_ACCEPT_CARD	= 0x01,
	CCID_MECH_FN_EJECT_CARD		= 0x02,
	CCID_MECH_FN_CAPTURE_CARD	= 0x03,
	CCID_MECH_FN_LOCK_CARD		= 0x04,
	CCID_MECH_FN_UNLOCK_CARD	= 0x05,
};
/* Response: RDR_to_PC_SlotStatus */

/* Section 6.1.13 */
struct ccid_pc_to_rdr_abort {
	struct ccid_header hdr;
	uint8_t abRFU[3];
} __attribute__ ((packed));
/* Response: RDR_to_PC_SlotStatus */

/* Section 6.1.14 */
struct ccid_pc_to_rdr_set_rate_and_clock {
	struct ccid_header hdr;
	uint8_t abRFU[3];
	uint32_t dwClockFrequency;
	uint32_t dwDataRate;
} __attribute__ ((packed));
/* Response: RDR_to_PC_DataRateAndClockFrequency */

union ccid_pc_to_rdr {
	struct ccid_pc_to_rdr_icc_power_on		icc_power_on;
	struct ccid_pc_to_rdr_icc_power_off		icc_power_off;
	struct ccid_pc_to_rdr_get_slot_status		get_slot_status;
	struct ccid_pc_to_rdr_xfr_block			xfr_block;
	struct ccid_pc_to_rdr_get_parameters		get_parameters;
	struct ccid_pc_to_rdr_reset_parameters		reset_parameters;
	struct ccid_pc_to_rdr_set_parameters		set_parameters;
	struct ccid_pc_to_rdr_escape			escape;
	struct ccid_pc_to_rdr_icc_clock			icc_clock;
	struct ccid_pc_to_rdr_t0apdu			t0apdu;
	struct ccid_pc_to_rdr_secure			secure;
	struct ccid_pc_to_rdr_mechanical		mechanical;
	struct ccid_pc_to_rdr_abort			abort;
	struct ccid_pc_to_rdr_set_rate_and_clock	set_rate_and_clock;
};

/***********************************************************************
 * Bulk IN
 ***********************************************************************/

/* CCID message header on BULK-IN endpoint */
struct ccid_header_in {
	struct ccid_header hdr;
	uint8_t		bStatus;
	uint8_t		bError;
} __attribute__ ((packed));

/* Section 6.2.1 RDR_to_PC_DataBlock */
struct ccid_rdr_to_pc_data_block {
	struct ccid_header_in hdr;
	uint8_t bChainParameter;
	uint8_t abData[0];
} __attribute__ ((packed));

/* Section 6.2.2 RDR_to_PC_SlotStatus */
struct ccid_rdr_to_pc_slot_status {
	struct ccid_header_in hdr;
	uint8_t bClockStatus;
} __attribute__ ((packed));
enum ccid_clock_status {
	CCID_CLOCK_STATUS_RUNNING	= 0x00,
	CCID_CLOCK_STATUS_STOPPED_L	= 0x01,
	CCID_CLOCK_STATUS_STOPPED_H	= 0x02,
	CCID_CLOCK_STATUS_STOPPED_UNKN	= 0x03,
};

/* Section 6.2.3 RDR_to_PC_Parameters */
struct ccid_rdr_to_pc_parameters {
	struct ccid_header_in hdr;
	uint8_t bProtocolNum;
	union {
		struct ccid_proto_data_t0 t0;
		struct ccid_proto_data_t1 t1;
	} abProtocolData;
} __attribute__ ((packed));

/* Section 6.2.4 RDR_to_PC_Escape */
struct ccid_rdr_to_pc_escape {
	struct ccid_header_in hdr;
	uint8_t bRFU;
	uint8_t abData[0];
} __attribute__ ((packed));

/* Section 6.2.5 RDR_to_PC_DataRateAndClockFrequency */
struct ccid_rdr_to_pc_data_rate_and_clock {
	struct ccid_header_in hdr;
	uint8_t bRFU;
	uint32_t dwClockFrequency;
	uint32_t dwDataRate;
} __attribute__ ((packed));

/* Section 6.2.6 */
#define CCID_ICC_STATUS_MASK		0x03
#define CCID_ICC_STATUS_PRES_ACT	0x00
#define CCID_ICC_STATUS_PRES_INACT	0x01
#define CCID_ICC_STATUS_NO_ICC		0x02
#define CCID_CMD_STATUS_MASK		0xC0
#define CCID_CMD_STATUS_OK		0x00
#define CCID_CMD_STATUS_FAILED		0x40
#define CCID_CMD_STATUS_TIME_EXT	0x80
/* Table 6.2-2: Slot Error value when bmCommandStatus == 1 */
enum ccid_error_code {
	CCID_ERR_CMD_ABORTED			= 0xff,
	CCID_ERR_ICC_MUTE			= 0xfe,
	CCID_ERR_XFR_PARITY_ERROR		= 0xfd,
	CCID_ERR_XFR_OVERRUN			= 0xfc,
	CCID_ERR_HW_ERROR			= 0xfb,
	CCID_ERR_BAD_ATR_TS			= 0xf8,
	CCID_ERR_BAD_ATR_TCK			= 0xf7,
	CCID_ERR_ICC_PROTOCOL_NOT_SUPPORTED	= 0xf6,
	CCID_ERR_ICC_CLASS_NOT_SUPPORTED	= 0xf5,
	CCID_ERR_PROCEDURE_BYTE_CONFLICT	= 0xf4,
	CCID_ERR_DEACTIVATED_PROTOCOL		= 0xf3,
	CCID_ERR_BUSY_WITH_AUTO_SEQUENCE	= 0xf2,
	CCID_ERR_PIN_TIMEOUT			= 0xf0,
	CCID_ERR_PIN_CANCELLED			= 0xef,
	CCID_ERR_CMD_SLOT_BUSY			= 0xe0,
	CCID_ERR_CMD_NOT_SUPPORTED		= 0x00
};

union ccid_rdr_to_pc {
	struct ccid_rdr_to_pc_data_block		data_block;
	struct ccid_rdr_to_pc_slot_status		slot_status;
	struct ccid_rdr_to_pc_parameters		parameters;
	struct ccid_rdr_to_pc_escape			escape;
	struct ccid_rdr_to_pc_data_rate_and_clock	rate_and_clock;
};

/***********************************************************************
 * Interupt IN
 ***********************************************************************/

/* Section 6.3.1 */
struct ccid_rdr_to_pc_notify_slot_change {
	uint8_t bMessageType;
	uint8_t bmSlotCCState[0];	/* as long as bNumSlots/4 padded to next byte */
} __attribute__ ((packed));

/* Section 6.3.2 - Interrupt IN message (4 bytes, NOT bulk header format) */
struct ccid_rdr_to_pc_hardware_error {
	uint8_t bMessageType; /* 0x51 */
	uint8_t bSlot;
	uint8_t bSeq;
	uint8_t bHardwareErrorCode;
} __attribute__ ((packed));

union ccid_rdr_to_pc_irq {
	struct ccid_rdr_to_pc_notify_slot_change	slot_change;
	struct ccid_rdr_to_pc_hardware_error		hw_error;
};


extern const struct value_string ccid_msg_type_vals[];
extern const struct value_string ccid_class_spec_req_vals[];
extern const struct value_string ccid_power_select_vals[];
extern const struct value_string ccid_clock_command_vals[];
extern const struct value_string ccid_error_code_vals[];

