/* ----------------------------------------------------------------------------
 *         ATMEL Microcontroller Software Support
 * ----------------------------------------------------------------------------
 * Copyright (c) 2009, Atmel Corporation
 * Copyright (c) 2018-2019, sysmocom -s.f.m.c. GmbH, Author: Kevin Redon <kredon@sysmocom.de>
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the disclaimer below.
 *
 * Atmel's name may not be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
 * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * ----------------------------------------------------------------------------
 */

/*----------------------------------------------------------------------------
 *         Headers
 *----------------------------------------------------------------------------*/

#include "board.h"
#include "simtrace.h"
#include "simtrace_usb.h"
#include "utils.h"
#include "USBD_HAL.h"

#include <cciddriverdescriptors.h>
#include <usb/common/dfu/usb_dfu.h>
#include <usb/device/dfu/dfu.h>

/*------------------------------------------------------------------------------
 *       USB String descriptors 
 *------------------------------------------------------------------------------*/
#include "usb_strings_generated.h"

// the index of the strings (must match the order in usb_strings.txt)
enum strDescNum {
	// static strings from usb_strings
	MANUF_STR = 1,
	PRODUCT_STRING,
	SNIFFER_CONF_STR,
	CCID_CONF_STR,
	PHONE_CONF_STR,
	MITM_CONF_STR,
	CARDEM_USIM1_INTF_STR,
	CARDEM_USIM2_INTF_STR,
	CARDEM_USIM3_INTF_STR,
	CARDEM_USIM4_INTF_STR,
	// runtime strings
	SERIAL_STR,
	VERSION_CONF_STR,
	VERSION_STR,
	// count
	STRING_DESC_CNT
};

/** array of static (from usb_strings) and runtime (serial, version) USB strings
 */
static const unsigned char *usb_strings_extended[ARRAY_SIZE(usb_strings) + 3];

/* USB string for the serial (using 128-bit device ID) */
static unsigned char usb_string_serial[] = {
	USBStringDescriptor_LENGTH(32),
	USBGenericDescriptor_STRING,
	USBStringDescriptor_UNICODE('0'),
	USBStringDescriptor_UNICODE('0'),
	USBStringDescriptor_UNICODE('1'),
	USBStringDescriptor_UNICODE('1'),
	USBStringDescriptor_UNICODE('2'),
	USBStringDescriptor_UNICODE('2'),
	USBStringDescriptor_UNICODE('3'),
	USBStringDescriptor_UNICODE('3'),
	USBStringDescriptor_UNICODE('4'),
	USBStringDescriptor_UNICODE('4'),
	USBStringDescriptor_UNICODE('5'),
	USBStringDescriptor_UNICODE('5'),
	USBStringDescriptor_UNICODE('6'),
	USBStringDescriptor_UNICODE('6'),
	USBStringDescriptor_UNICODE('7'),
	USBStringDescriptor_UNICODE('7'),
	USBStringDescriptor_UNICODE('8'),
	USBStringDescriptor_UNICODE('8'),
	USBStringDescriptor_UNICODE('9'),
	USBStringDescriptor_UNICODE('9'),
	USBStringDescriptor_UNICODE('a'),
	USBStringDescriptor_UNICODE('a'),
	USBStringDescriptor_UNICODE('b'),
	USBStringDescriptor_UNICODE('b'),
	USBStringDescriptor_UNICODE('c'),
	USBStringDescriptor_UNICODE('c'),
	USBStringDescriptor_UNICODE('d'),
	USBStringDescriptor_UNICODE('d'),
	USBStringDescriptor_UNICODE('e'),
	USBStringDescriptor_UNICODE('e'),
	USBStringDescriptor_UNICODE('f'),
	USBStringDescriptor_UNICODE('f'),
};

/* USB string for the version */
static const unsigned char usb_string_version_conf[] = {
	USBStringDescriptor_LENGTH(16),
	USBGenericDescriptor_STRING,
	USBStringDescriptor_UNICODE('f'),
	USBStringDescriptor_UNICODE('i'),
	USBStringDescriptor_UNICODE('r'),
	USBStringDescriptor_UNICODE('m'),
	USBStringDescriptor_UNICODE('w'),
	USBStringDescriptor_UNICODE('a'),
	USBStringDescriptor_UNICODE('r'),
	USBStringDescriptor_UNICODE('e'),
	USBStringDescriptor_UNICODE(' '),
	USBStringDescriptor_UNICODE('v'),
	USBStringDescriptor_UNICODE('e'),
	USBStringDescriptor_UNICODE('r'),
	USBStringDescriptor_UNICODE('s'),
	USBStringDescriptor_UNICODE('i'),
	USBStringDescriptor_UNICODE('o'),
	USBStringDescriptor_UNICODE('n'),
};
static const char git_version[] = GIT_VERSION;
static unsigned char usb_string_version[2 + ARRAY_SIZE(git_version) * 2 - 2];

/*------------------------------------------------------------------------------
 *       USB Device descriptors 
 *------------------------------------------------------------------------------*/

#ifdef HAVE_SNIFFER
typedef struct _SIMTraceDriverConfigurationDescriptorSniffer {

	/** Standard configuration descriptor. */
	USBConfigurationDescriptor configuration;
	USBInterfaceDescriptor sniffer;
	USBEndpointDescriptor sniffer_dataOut;
	USBEndpointDescriptor sniffer_dataIn;
	USBEndpointDescriptor sniffer_interruptIn;
	DFURT_IF_DESCRIPTOR_STRUCT;
} __attribute__ ((packed)) SIMTraceDriverConfigurationDescriptorSniffer;

static const SIMTraceDriverConfigurationDescriptorSniffer
				configurationDescriptorSniffer = {
	/* Standard configuration descriptor */
	.configuration = {
		.bLength 		= sizeof(USBConfigurationDescriptor),
		.bDescriptorType	= USBGenericDescriptor_CONFIGURATION,
		.wTotalLength		= sizeof(SIMTraceDriverConfigurationDescriptorSniffer),
		.bNumInterfaces		= 1+DFURT_NUM_IF,
		.bConfigurationValue	= CFG_NUM_SNIFF,
		.iConfiguration		= SNIFFER_CONF_STR,
		.bmAttributes		= USBD_BMATTRIBUTES,
		.bMaxPower		= USBConfigurationDescriptor_POWER(100),
	},
	/* Communication class interface standard descriptor */
	.sniffer = {
		.bLength = sizeof(USBInterfaceDescriptor),
		.bDescriptorType 	= USBGenericDescriptor_INTERFACE,
		.bInterfaceNumber	= 0,
		.bAlternateSetting	= 0,
		.bNumEndpoints		= 3,
		.bInterfaceClass	= USB_CLASS_PROPRIETARY,
		.bInterfaceSubClass	= SIMTRACE_SNIFFER_USB_SUBCLASS,
		.bInterfaceProtocol	= 0,
		.iInterface		= SNIFFER_CONF_STR,
	},
	/* Bulk-OUT endpoint standard descriptor */
	.sniffer_dataOut = {
		.bLength 		= sizeof(USBEndpointDescriptor),
		.bDescriptorType	= USBGenericDescriptor_ENDPOINT,
		.bEndpointAddress	= USBEndpointDescriptor_ADDRESS(
						USBEndpointDescriptor_OUT,
						SIMTRACE_USB_EP_CARD_DATAOUT),
		.bmAttributes		= USBEndpointDescriptor_BULK,
		.wMaxPacketSize	= USBEndpointDescriptor_MAXBULKSIZE_FS,
		.bInterval		= 0,
	},
	/* Bulk-IN endpoint descriptor */
	.sniffer_dataIn = {
		.bLength		= sizeof(USBEndpointDescriptor),
		.bDescriptorType	= USBGenericDescriptor_ENDPOINT,
		.bEndpointAddress	= USBEndpointDescriptor_ADDRESS(
						USBEndpointDescriptor_IN,
						SIMTRACE_USB_EP_CARD_DATAIN),
		.bmAttributes		= USBEndpointDescriptor_BULK,
		.wMaxPacketSize = USBEndpointDescriptor_MAXBULKSIZE_FS,
		.bInterval		= 0,
	},
	// Notification endpoint descriptor
	.sniffer_interruptIn = {
		.bLength		= sizeof(USBEndpointDescriptor),
		.bDescriptorType	= USBGenericDescriptor_ENDPOINT,
		.bEndpointAddress	= USBEndpointDescriptor_ADDRESS(
						USBEndpointDescriptor_IN,
						SIMTRACE_USB_EP_CARD_INT),
		.bmAttributes		= USBEndpointDescriptor_INTERRUPT,
		.wMaxPacketSize	= USBEndpointDescriptor_MAXINTERRUPTSIZE_FS,
		.bInterval = 1,
	},
	DFURT_IF_DESCRIPTOR(1, 0),
};
#endif /* HAVE_SNIFFER */

#ifdef HAVE_CCID
static const CCIDDriverConfigurationDescriptors configurationDescriptorCCID = {
	// Standard USB configuration descriptor
	{
		.bLength		= sizeof(USBConfigurationDescriptor),
		.bDescriptorType	= USBGenericDescriptor_CONFIGURATION,
		.wTotalLength		= sizeof(CCIDDriverConfigurationDescriptors),
		.bNumInterfaces		= 1+DFURT_NUM_IF,
		.bConfigurationValue	= CFG_NUM_CCID,
		.iConfiguration		= CCID_CONF_STR,
		.bmAttributes		= BOARD_USB_BMATTRIBUTES,
		.bMaxPower		= USBConfigurationDescriptor_POWER(100),
	},
	// CCID interface descriptor
	// Table 4.3-1 Interface Descriptor
	// Interface descriptor
	{
		.bLength		= sizeof(USBInterfaceDescriptor),
		.bDescriptorType	= USBGenericDescriptor_INTERFACE,
		.bInterfaceNumber	= 0,
		.bAlternateSetting	= 0,
		.bNumEndpoints		= 3,
		.bInterfaceClass	= SMART_CARD_DEVICE_CLASS,
		.bInterfaceSubClass	= 0,
		.bInterfaceProtocol	= 0,
		.iInterface		= CCID_CONF_STR,
	},
	{
		.bLength		= sizeof(CCIDDescriptor),
		.bDescriptorType	= CCID_DECRIPTOR_TYPE,
		.bcdCCID		= CCID1_10,	// CCID version
		.bMaxSlotIndex		= 0,	// 1 slot 
		.bVoltageSupport	= VOLTS_3_0,
		.dwProtocols		= (1 << PROTOCOL_TO),
		.dwDefaultClock		= 3580,
		.dwMaximumClock		= 3580,
		.bNumClockSupported	= 0,
		.dwDataRate		= 9600,
		.dwMaxDataRate		= 9600,
		.bNumDataRatesSupported = 0,
		.dwMaxIFSD		= 0xfe,
		.dwSynchProtocols	= 0,
		.dwMechanical		= 0,
		.dwFeatures = CCID_FEATURES_AUTO_CLOCK | CCID_FEATURES_AUTO_BAUD |
			      CCID_FEATURES_AUTO_PCONF | CCID_FEATURES_AUTO_PNEGO |
			      CCID_FEATURES_EXC_TPDU,
		.dwMaxCCIDMessageLength	= 271,	/* For extended APDU
						   level the value shall
						   be between 261 + 10 */
		.bClassGetResponse	= 0xFF,	// Echoes the class of the APDU
		.bClassEnvelope		= 0xFF,	// Echoes the class of the APDU
		.wLcdLayout		= 0,	// wLcdLayout: no LCD
		.bPINSupport		= 0,	// bPINSupport: No PIN
		.bMaxCCIDBusySlots	= 1,
	},
	// Bulk-OUT endpoint descriptor
	{
		.bLength		= sizeof(USBEndpointDescriptor),
		.bDescriptorType	= USBGenericDescriptor_ENDPOINT,
		.bEndpointAddress	=
			USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_OUT,
				      		      CCID_EPT_DATA_OUT),
		.bmAttributes		= USBEndpointDescriptor_BULK,
		.wMaxPacketSize	= USBEndpointDescriptor_MAXBULKSIZE_FS,
		.bInterval		= 0x00,
	},
	// Bulk-IN endpoint descriptor
	{
		.bLength		= sizeof(USBEndpointDescriptor),
		.bDescriptorType	= USBGenericDescriptor_ENDPOINT,
		.bEndpointAddress	=
			USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_IN,
						      CCID_EPT_DATA_IN),
		.bmAttributes		= USBEndpointDescriptor_BULK,
		.wMaxPacketSize	= USBEndpointDescriptor_MAXBULKSIZE_FS),
		.bInterval		= 0x00,
	},
	// Notification endpoint descriptor
	{
		.bLength		= sizeof(USBEndpointDescriptor),
		.bDescriptorType	= USBGenericDescriptor_ENDPOINT,
		.bEndpointAddress	=
			USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_IN,
						      CCID_EPT_NOTIFICATION),
		.bmAttributes		= USBEndpointDescriptor_INTERRUPT,
		.wMaxPacketSize = USBEndpointDescriptor_MAXINTERRUPTSIZE_FS,
		.bInterval = 0x10,
	},
	DFURT_IF_DESCRIPTOR(1, 0),
};
#endif /* HAVE_CCID */

#ifdef HAVE_CARDEM
/* SIM card emulator */
typedef struct _SIMTraceDriverConfigurationDescriptorPhone {
	/* Standard configuration descriptor. */
	USBConfigurationDescriptor configuration;
	USBInterfaceDescriptor phone;
	USBEndpointDescriptor phone_dataOut;
	USBEndpointDescriptor phone_dataIn;
	USBEndpointDescriptor phone_interruptIn;
#ifdef CARDEMU_SECOND_UART
	USBInterfaceDescriptor usim2;
	USBEndpointDescriptor usim2_dataOut;
	USBEndpointDescriptor usim2_dataIn;
	USBEndpointDescriptor usim2_interruptIn;
#endif
	DFURT_IF_DESCRIPTOR_STRUCT;
} __attribute__ ((packed)) SIMTraceDriverConfigurationDescriptorPhone;

static const SIMTraceDriverConfigurationDescriptorPhone
				configurationDescriptorPhone = {
	/* Standard configuration descriptor */
	.configuration = {
		.bLength		= sizeof(USBConfigurationDescriptor),
		.bDescriptorType	= USBGenericDescriptor_CONFIGURATION,
		.wTotalLength		= sizeof(SIMTraceDriverConfigurationDescriptorPhone),
#ifdef CARDEMU_SECOND_UART
		.bNumInterfaces		= 2+DFURT_NUM_IF,
#else
		.bNumInterfaces		= 1+DFURT_NUM_IF,
#endif
		.bConfigurationValue	= CFG_NUM_PHONE,
		.iConfiguration		= PHONE_CONF_STR,
		.bmAttributes		= USBD_BMATTRIBUTES,
		.bMaxPower		= USBConfigurationDescriptor_POWER(100)
	},
	/* Communication class interface standard descriptor */
	.phone = {
		.bLength		= sizeof(USBInterfaceDescriptor),
		.bDescriptorType	= USBGenericDescriptor_INTERFACE,
		.bInterfaceNumber	= 0,
		.bAlternateSetting	= 0,
		.bNumEndpoints		= 3,
		.bInterfaceClass	= USB_CLASS_PROPRIETARY,
		.bInterfaceSubClass	= SIMTRACE_CARDEM_USB_SUBCLASS,
		.bInterfaceProtocol	= 0,
		.iInterface		= CARDEM_USIM1_INTF_STR,
	},
	/* Bulk-OUT endpoint standard descriptor */
	.phone_dataOut = {
		.bLength		= sizeof(USBEndpointDescriptor),
		.bDescriptorType	= USBGenericDescriptor_ENDPOINT,
		.bEndpointAddress	= USBEndpointDescriptor_ADDRESS(
						USBEndpointDescriptor_OUT,
						SIMTRACE_CARDEM_USB_EP_USIM1_DATAOUT),
		.bmAttributes		= USBEndpointDescriptor_BULK,
		.wMaxPacketSize		= USBEndpointDescriptor_MAXBULKSIZE_FS,
		.bInterval		= 0	/* Must be 0 for full-speed bulk endpoints */
	},
	/* Bulk-IN endpoint descriptor */
	.phone_dataIn = {
		.bLength 		= sizeof(USBEndpointDescriptor),
		.bDescriptorType	= USBGenericDescriptor_ENDPOINT,
		.bEndpointAddress	= USBEndpointDescriptor_ADDRESS(
						USBEndpointDescriptor_IN,
						SIMTRACE_CARDEM_USB_EP_USIM1_DATAIN),
		.bmAttributes		= USBEndpointDescriptor_BULK,
		.wMaxPacketSize		= USBEndpointDescriptor_MAXBULKSIZE_FS,
		.bInterval		= 0	/* Must be 0 for full-speed bulk endpoints */
	},
	/* Notification endpoint descriptor */
	.phone_interruptIn = {
		.bLength		= sizeof(USBEndpointDescriptor),
		.bDescriptorType	= USBGenericDescriptor_ENDPOINT,
		.bEndpointAddress	= USBEndpointDescriptor_ADDRESS(
						USBEndpointDescriptor_IN,
						SIMTRACE_CARDEM_USB_EP_USIM1_INT),
		.bmAttributes		= USBEndpointDescriptor_INTERRUPT,
		.wMaxPacketSize		= USBEndpointDescriptor_MAXINTERRUPTSIZE_FS,
		.bInterval		= 1
	},
#ifdef CARDEMU_SECOND_UART
	/* Communication class interface standard descriptor */
	.usim2 = {
		.bLength		= sizeof(USBInterfaceDescriptor),
		.bDescriptorType	= USBGenericDescriptor_INTERFACE,
		.bInterfaceNumber	= 1,
		.bAlternateSetting	= 0,
		.bNumEndpoints		= 3,
		.bInterfaceClass	= USB_CLASS_PROPRIETARY,
		.bInterfaceSubClass	= SIMTRACE_CARDEM_USB_SUBCLASS,
		.bInterfaceProtocol	= 0,
		.iInterface		= CARDEM_USIM2_INTF_STR,
	},
	/* Bulk-OUT endpoint standard descriptor */
	.usim2_dataOut = {
		.bLength		= sizeof(USBEndpointDescriptor),
		.bDescriptorType	= USBGenericDescriptor_ENDPOINT,
		.bEndpointAddress	= USBEndpointDescriptor_ADDRESS(
						USBEndpointDescriptor_OUT,
						SIMTRACE_CARDEM_USB_EP_USIM2_DATAOUT),
		.bmAttributes		= USBEndpointDescriptor_BULK,
		.wMaxPacketSize		= USBEndpointDescriptor_MAXBULKSIZE_FS,
		.bInterval		= 0	/* Must be 0 for full-speed bulk endpoints */
	}
	,
	/* Bulk-IN endpoint descriptor */
	.usim2_dataIn = {
		.bLength		= sizeof(USBEndpointDescriptor),
		.bDescriptorType	= USBGenericDescriptor_ENDPOINT,
		.bEndpointAddress	= USBEndpointDescriptor_ADDRESS(
						USBEndpointDescriptor_IN,
						SIMTRACE_CARDEM_USB_EP_USIM2_DATAIN),
		.bmAttributes		= USBEndpointDescriptor_BULK,
		.wMaxPacketSize		= USBEndpointDescriptor_MAXBULKSIZE_FS,
		.bInterval		= 0	/* Must be 0 for full-speed bulk endpoints */
	},
	/* Notification endpoint descriptor */
	.usim2_interruptIn = {
		.bLength		= sizeof(USBEndpointDescriptor),
		.bDescriptorType	= USBGenericDescriptor_ENDPOINT,
		.bEndpointAddress	= USBEndpointDescriptor_ADDRESS(
						USBEndpointDescriptor_IN,
						SIMTRACE_CARDEM_USB_EP_USIM2_INT),
		.bmAttributes		= USBEndpointDescriptor_INTERRUPT,
		.wMaxPacketSize		= USBEndpointDescriptor_MAXINTERRUPTSIZE_FS,
		.bInterval		= 1,
	},
	DFURT_IF_DESCRIPTOR(2, 0),
#else
	DFURT_IF_DESCRIPTOR(1, 0),
#endif
};
#endif /* HAVE_CARDEM */

#ifdef HAVE_MITM
typedef struct _SIMTraceDriverConfigurationDescriptorMITM {
	/* Standard configuration descriptor. */
	USBConfigurationDescriptor configuration;
	USBInterfaceDescriptor simcard;
	/// CCID descriptor
	CCIDDescriptor ccid;
	/// Bulk OUT endpoint descriptor
	USBEndpointDescriptor simcard_dataOut;
	/// Bulk IN endpoint descriptor
	USBEndpointDescriptor simcard_dataIn;
	/// Interrupt OUT endpoint descriptor
	USBEndpointDescriptor simcard_interruptIn;

	USBInterfaceDescriptor phone;
	USBEndpointDescriptor phone_dataOut;
	USBEndpointDescriptor phone_dataIn;
	USBEndpointDescriptor phone_interruptIn;

	DFURT_IF_DESCRIPTOR_STRUCT;

} __attribute__ ((packed)) SIMTraceDriverConfigurationDescriptorMITM;

static const SIMTraceDriverConfigurationDescriptorMITM
				configurationDescriptorMITM = {
	/* Standard configuration descriptor */
	.configuration = {
		.bLength		= sizeof(USBConfigurationDescriptor),
		.bDescriptorType	= USBGenericDescriptor_CONFIGURATION,
		.wTotalLength		= sizeof(SIMTraceDriverConfigurationDescriptorMITM),
		.bNumInterfaces		= 2+DFURT_NUM_IF,
		.bConfigurationValue	= CFG_NUM_MITM,
		.iConfiguration		= MITM_CONF_STR,
		.bmAttributes		= USBD_BMATTRIBUTES,
		.bMaxPower		= USBConfigurationDescriptor_POWER(100),
	},
	// CCID interface descriptor
	// Table 4.3-1 Interface Descriptor
	// Interface descriptor
	.simcard = {
		.bLength		= sizeof(USBInterfaceDescriptor),
		.bDescriptorType	= USBGenericDescriptor_INTERFACE,
		.bInterfaceNumber	= 0,
		.bAlternateSetting	= 0,
		.bNumEndpoints		= 3,
		.bInterfaceClass	= SMART_CARD_DEVICE_CLASS,
		.bInterfaceSubClass	= 0,
		.bInterfaceProtocol	= 0,
		.iInterface		= CCID_CONF_STR,
	},
	.ccid = {
		.bLength		= sizeof(CCIDDescriptor),
		.bDescriptorType	= CCID_DECRIPTOR_TYPE,
		.bcdCCID		= CCID1_10,	// CCID version
		.bMaxSlotIndex		= 0,	// 1 slot 
		.bVoltageSupport	= VOLTS_3_0,
		.dwProtocols		= (1 << PROTOCOL_TO),
		.dwDefaultClock		= 3580,
		.dwMaximumClock		= 3580,
		.bNumClockSupported	= 0,
		.dwDataRate		= 9600,
		.dwMaxDataRate		= 9600,
		.bNumDataRatesSupported = 0,
		.dwMaxIFSD		= 0xfe,
		.dwSynchProtocols	= 0,
		.dwMechanical		= 0,
		.dwFeatures = CCID_FEATURES_AUTO_CLOCK | CCID_FEATURES_AUTO_BAUD |
			      CCID_FEATURES_AUTO_PCONF | CCID_FEATURES_AUTO_PNEGO |
			      CCID_FEATURES_EXC_TPDU,
		.dwMaxCCIDMessageLength	= 271,	/* For extended APDU
						   level the value shall
						   be between 261 + 10 */
		.bClassGetResponse	= 0xFF,	// Echoes the class of the APDU
		.bClassEnvelope		= 0xFF,	// Echoes the class of the APDU
		.wLcdLayout		= 0,	// wLcdLayout: no LCD
		.bPINSupport		= 0,	// bPINSupport: No PIN
		.bMaxCCIDBusySlots	= 1,
	},
	// Bulk-OUT endpoint descriptor
	.simcard_dataOut = {
		.bLength		= sizeof(USBEndpointDescriptor),
		.bDescriptorType	= USBGenericDescriptor_ENDPOINT,
		.bEndpointAddress	=
			USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_OUT,
				      		      CCID_EPT_DATA_OUT),
		.bmAttributes		= USBEndpointDescriptor_BULK,
		.wMaxPacketSize	= USBEndpointDescriptor_MAXBULKSIZE_FS,
		.bInterval		= 0x00,
	},
	// Bulk-IN endpoint descriptor
	.simcard_dataIn = {
		.bLength		= sizeof(USBEndpointDescriptor),
		.bDescriptorType	= USBGenericDescriptor_ENDPOINT,
		.bEndpointAddress	=
			USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_IN,
						      CCID_EPT_DATA_IN),
		.bmAttributes		= USBEndpointDescriptor_BULK,
		.wMaxPacketSize	= USBEndpointDescriptor_MAXBULKSIZE_FS,
		.bInterval		= 0x00,
	},
	// Notification endpoint descriptor
	.simcard_interruptIn = {
		.bLength		= sizeof(USBEndpointDescriptor),
		.bDescriptorType	= USBGenericDescriptor_ENDPOINT,
		.bEndpointAddress	=
			USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_IN,
						      CCID_EPT_NOTIFICATION),
		.bmAttributes		= USBEndpointDescriptor_INTERRUPT,
		.wMaxPacketSize = USBEndpointDescriptor_MAXINTERRUPTSIZE_FS,
		.bInterval = 1,
	},

	/* Communication class interface standard descriptor */
	.phone = {
		.bLength		= sizeof(USBInterfaceDescriptor),
		.bDescriptorType	= USBGenericDescriptor_INTERFACE,
		.bInterfaceNumber	= 1,
		.bAlternateSetting	= 0,
		.bNumEndpoints		= 3,
		.bInterfaceClass	= 0xff,
		.bInterfaceSubClass	= SIMTRACE_SUBCLASS_CARDEM,
		.bInterfaceProtocol	= 0,
		.iInterface		= PHONE_CONF_STR,
	},
	/* Bulk-OUT endpoint standard descriptor */
	.phone_dataOut = {
		.bLength		= sizeof(USBEndpointDescriptor),
		.bDescriptorType	= USBGenericDescriptor_ENDPOINT,
		.bEndpointAddress	= USBEndpointDescriptor_ADDRESS(
						USBEndpointDescriptor_OUT,
						SIMTRACE_USB_EP_PHONE_DATAOUT),
		.bmAttributes		= USBEndpointDescriptor_BULK,
		.wMaxPacketSize		= USBEndpointDescriptor_MAXBULKSIZE_FS,
		.bInterval		= 0, /* Must be 0 for full-speed bulk endpoints */
	},
	/* Bulk-IN endpoint descriptor */
	.phone_dataIn = {
		.bLength		= sizeof(USBEndpointDescriptor),
		.bDescriptorType	= USBGenericDescriptor_ENDPOINT,
		.bEndpointAddress	= USBEndpointDescriptor_ADDRESS(
						USBEndpointDescriptor_IN,
						SIMTRACE_USB_EP_PHONE_DATAIN),
		.bmAttributes		= USBEndpointDescriptor_BULK,
		.wMaxPacketSize		= USBEndpointDescriptor_MAXBULKSIZE_FS,
		.bInterval		= 0, /* Must be 0 for full-speed bulk endpoints */
	},
	/* Notification endpoint descriptor */
	{
		.bLength		= sizeof(USBEndpointDescriptor),
		.bDescriptorType	= USBGenericDescriptor_ENDPOINT,
		.bEndpointAddress	= USBEndpointDescriptor_ADDRESS(
						USBEndpointDescriptor_IN,
						SIMTRACE_USB_EP_PHONE_INT),
		.bmAttributes		= USBEndpointDescriptor_INTERRUPT,
		.wMaxPacketSize		= USBEndpointDescriptor_MAXINTERRUPTSIZE_FS,
		.bInterval		= 1
	},
	DFURT_IF_DESCRIPTOR(2, 0),
};
#endif /* HAVE_CARDEM */

/* USB descriptor just to show the version */
typedef struct _SIMTraceDriverConfigurationDescriptorVersion {
	/** Standard configuration descriptor. */
	USBConfigurationDescriptor configuration;
	USBInterfaceDescriptor version;
} __attribute__ ((packed)) SIMTraceDriverConfigurationDescriptorVersion;

static const SIMTraceDriverConfigurationDescriptorVersion
				configurationDescriptorVersion = {
	/* Standard configuration descriptor for the interface descriptor*/
	.configuration = {
		.bLength 		= sizeof(USBConfigurationDescriptor),
		.bDescriptorType	= USBGenericDescriptor_CONFIGURATION,
		.wTotalLength		= sizeof(SIMTraceDriverConfigurationDescriptorVersion),
		.bNumInterfaces		= 1,
		.bConfigurationValue	= CFG_NUM_VERSION,
		.iConfiguration		= VERSION_CONF_STR,
		.bmAttributes		= USBD_BMATTRIBUTES,
		.bMaxPower		= USBConfigurationDescriptor_POWER(100),
	},
	/* Interface standard descriptor just holding the version information */
	.version = {
		.bLength = sizeof(USBInterfaceDescriptor),
		.bDescriptorType 	= USBGenericDescriptor_INTERFACE,
		.bInterfaceNumber	= 0,
		.bAlternateSetting	= 0,
		.bNumEndpoints		= 0,
		.bInterfaceClass	= USB_CLASS_PROPRIETARY,
		.bInterfaceSubClass	= 0xff,
		.bInterfaceProtocol	= 0,
		.iInterface		= VERSION_STR,
	},
};

const USBConfigurationDescriptor *configurationDescriptorsArr[] = {
#ifdef HAVE_SNIFFER
	&configurationDescriptorSniffer.configuration,
#endif
#ifdef HAVE_CCID
	&configurationDescriptorCCID.configuration,
#endif
#ifdef HAVE_CARDEM
	&configurationDescriptorPhone.configuration,
#endif
#ifdef HAVE_MITM
	&configurationDescriptorMITM.configuration,
#endif
	&configurationDescriptorVersion.configuration,
};

/** Standard USB device descriptor for the CDC serial driver */
const USBDeviceDescriptor deviceDescriptor = {
	.bLength		= sizeof(USBDeviceDescriptor),
	.bDescriptorType	= USBGenericDescriptor_DEVICE,
	.bcdUSB			= USBDeviceDescriptor_USB2_00,
	.bDeviceClass		= 0,
	.bDeviceSubClass	= 0,
	.bDeviceProtocol	= 0,
	.bMaxPacketSize0	= 64,
	.idVendor		= BOARD_USB_VENDOR_ID,
	.idProduct		= BOARD_USB_PRODUCT_ID,
	.bcdDevice		= 2,	/* Release number */
	.iManufacturer		= MANUF_STR,
	.iProduct		= PRODUCT_STRING,
	.iSerialNumber		= SERIAL_STR,
	.bNumConfigurations	= ARRAY_SIZE(configurationDescriptorsArr),
};

/* AT91SAM3S only supports full speed, but not high speed USB */
static const USBDDriverDescriptors driverDescriptors = {
	&deviceDescriptor,
	(const USBConfigurationDescriptor **)&(configurationDescriptorsArr),	/* first full-speed configuration descriptor */
	0,			/* No full-speed device qualifier descriptor */
	0,			/* No full-speed other speed configuration */
	0,			/* No high-speed device descriptor */
	0,			/* No high-speed configuration descriptor */
	0,			/* No high-speed device qualifier descriptor */
	0,			/* No high-speed other speed configuration descriptor */
	usb_strings_extended,
	ARRAY_SIZE(usb_strings_extended),/* cnt string descriptors in list */
};

/*----------------------------------------------------------------------------
 *        Functions
 *----------------------------------------------------------------------------*/

void SIMtrace_USB_Initialize(void)
{
	unsigned int i;
	/* Signal USB reset by disabling the pull-up on USB D+ for at least 10 ms */
	USBD_HAL_Disconnect();
	USBD_HAL_Suspend();
	mdelay(500);
	USBD_HAL_Activate();

	// Get std USB driver
	USBDDriver *pUsbd = USBD_GetDriver();

	// put device ID into USB serial number description
	unsigned int device_id[4];
	EEFC_ReadUniqueID(device_id);
	char device_id_string[32 + 1];
	snprintf(device_id_string, ARRAY_SIZE(device_id_string), "%08x%08x%08x%08x",
		device_id[0], device_id[1], device_id[2], device_id[3]);
	for (i = 0; i < ARRAY_SIZE(device_id_string) - 1; i++) {
		usb_string_serial[2 + 2 * i] = device_id_string[i];
	}

	// put version into USB string
	usb_string_version[0] = USBStringDescriptor_LENGTH(ARRAY_SIZE(git_version) - 1);
	usb_string_version[1] = USBGenericDescriptor_STRING;
	for (i = 0; i < ARRAY_SIZE(git_version) - 1; i++) {
		usb_string_version[2 + i * 2 + 0] = git_version[i];
		usb_string_version[2 + i * 2 + 1] = 0;
	}

	// fill extended USB strings
	for (i = 0; i < ARRAY_SIZE(usb_strings) && i < ARRAY_SIZE(usb_strings_extended); i++) {
		usb_strings_extended[i] = usb_strings[i];
	}
	usb_strings_extended[SERIAL_STR] = usb_string_serial;
	usb_strings_extended[VERSION_CONF_STR] = usb_string_version_conf;
	usb_strings_extended[VERSION_STR] = usb_string_version;

	// Initialize standard USB driver
	USBDDriver_Initialize(pUsbd, &driverDescriptors, 0);	// Multiple interface settings not supported
	USBD_Init();
	USBD_Connect();

	NVIC_EnableIRQ(UDP_IRQn);
}
