at91work/usb-device-ccid-project/main.c

569 lines
22 KiB
C

/* ----------------------------------------------------------------------------
* ATMEL Microcontroller Software Support
* ----------------------------------------------------------------------------
* Copyright (c) 2008, Atmel Corporation
*
* 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.
* ----------------------------------------------------------------------------
*/
//------------------------------------------------------------------------------
/// \dir "USB device CCID project with AT91SAM Microcontrollers"
///
/// !!!Purpose
///
/// This example demonstrates the CCID (USB Chip/Smart Card Interface Devices) on AT91
/// microcontrollers.
///
/// !!!Requirements
///
/// This example can be used on AT91SAM9260-EK, AT91SAM9263-EK, AT91SAM9G20-EK,
/// AT91SAM9XE-EK.
///
/// !!!Description
///
/// This application implement a CCID driver.<BR>
/// Different tests can be done with the Windows(c) software Smart Access(c).<BR>
/// Smart Access: a powerful scripting tool to write sequences of commands.
///
/// !!!Usage
///
/// -# Install Smart Access(c) software.
/// -# Build the program and download it inside the evaluation board. Please
/// refer to the <a href="http://www.atmel.com/dyn/resources/prod_documents/doc6224.pdf">SAM-BA User Guide</a>,
/// the <a href="http://www.atmel.com/dyn/resources/prod_documents/doc6310.pdf">GNU-Based Software Development</a>
/// application note or to the <a href="ftp://ftp.iar.se/WWWfiles/arm/Guides/EWARM_UserGuide.ENU.pdf">IAR EWARM User Guide</a>,
/// depending on your chosen solution.
/// -# On the computer, open and configure a terminal application (e.g.
/// HyperTerminal on Microsoft Windows) with these settings:
/// - 115200 bauds
/// - 8 data bits
/// - No parity
/// - 1 stop bit
/// - Hardware flow control (RTS/CTS)
/// -# Start the application. The following traces shall appear on the terminal:
/// \code
/// -- USB Device CCID Project xxx --
/// -- AT91xxxxxx-xx
/// -- Compiled: xxx xx xxxx xx:xx:xx --
/// \endcode
/// -# Connect the USB cable to the board. Traces should start to appear on the terminal.<BR>
/// If this is the first time the device is connected to the PC,
/// the operating system may take some time to install it.
/// -# The device is enumerated as a Smart Card Device implementing CCID class.<BR>
/// -# The host use the CCID device driver (usbccid.sys) as the functionnal driver.
/// -# Launch Smart Access and connect it to the Card Reader Atmel.<BR>
/// Use Smart Access for launch instruction command.<BR>
/// Note that instruction command case one, two and three are implemanted.
///
/// !!!Contents
///
/// The definitions and functions provided are dispatched inside a number of
/// files:
/// - main.c main software
//------------------------------------------------------------------------------
/// \unit
///
/// !Purpose
/// Implementation of the USB CCID.
///
/// !Contents
/// The code can be roughly broken down as follows:
/// - Configuration functions
/// - Interrupt handlers
/// - Utility functions
/// - The main() function, which implements the program behavior
/// - Use ICC insertion event() and CCID_SmartCardRequest() CCID functions
///
/// Please refer to the list of functions in the #Overview# tab of this unit
/// for more detailed information.
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// Headers
//------------------------------------------------------------------------------
#include <board.h>
#include <aic/aic.h>
#include <pio/pio.h>
#include <pio/pio_it.h>
#include <utility/trace.h>
#include <utility/assert.h>
#include <utility/led.h>
#include <usb/device/core/USBD.h>
#include <dbgu/dbgu.h>
#include <usb/device/ccid/cciddriver.h>
#include <iso7816/iso7816_4.h>
#include <pmc/pmc.h>
#include <string.h>
//-----------------------------------------------------------------------------
// Local Define
//-----------------------------------------------------------------------------
/// Use for power management
#define STATE_IDLE 0
/// The USB device is in suspend state
#define STATE_SUSPEND 4
/// The USB device is in resume state
#define STATE_RESUME 5
//-----------------------------------------------------------------------------
// Local variables
//-----------------------------------------------------------------------------
/// State of USB, for suspend and resume
unsigned char USBState = STATE_IDLE;
/// List of pins that must be configured for use by the application.
static const Pin pinsISO7816[] = {PINS_ISO7816};
static const Pin pinIso7816RstMC = PIN_ISO7816_RSTMC;
static const Pin pinsPower[] = {
/* Swlf Power (LDO): low = off */
{1 << 5, AT91C_BASE_PIOA, AT91C_ID_PIOA, PIO_OUTPUT_0, PIO_DEFAULT},
/* Phone pass-through power: high = off */
{1 << 26, AT91C_BASE_PIOA, AT91C_ID_PIOA, PIO_OUTPUT_1, PIO_DEFAULT},
};
//------------------------------------------------------------------------------
// Optional smartcard detection
//------------------------------------------------------------------------------
#ifdef PIN_SMARTCARD_CONNECT
/// Smartcard detection pin.
static const Pin pinSmartCard = PIN_SMARTCARD_CONNECT;
//------------------------------------------------------------------------------
/// PIO interrupt service routine. Checks if the smartcard has been connected
/// or disconnected.
//------------------------------------------------------------------------------
static void ISR_PioSmartCard(const Pin *pPin)
{
// Check current level on pin
if (PIO_Get(&pinSmartCard) == 0) {
printf("-H- Insert\n\r");
CCID_Insertion();
} else {
printf("-H- Removal\n\r");
CCID_Removal();
}
}
//------------------------------------------------------------------------------
/// Configures the smartcard detection pin to trigger an interrupt.
//------------------------------------------------------------------------------
static void ConfigureCardDetection(void)
{
PIO_Configure(&pinSmartCard, 1);
PIO_ConfigureIt(&pinSmartCard, ISR_PioSmartCard);
PIO_EnableIt(&pinSmartCard);
}
#else
//------------------------------------------------------------------------------
/// Dummy implementation.
//------------------------------------------------------------------------------
static void ConfigureCardDetection(void)
{
printf("-I- Smartcard detection not supported.\n\r");
}
#endif
//------------------------------------------------------------------------------
/// VBus monitoring (optional)
//------------------------------------------------------------------------------
#if defined(PIN_USB_VBUS)
#define VBUS_CONFIGURE() VBus_Configure()
/// VBus pin instance.
static const Pin pinVbus = PIN_USB_VBUS;
//------------------------------------------------------------------------------
/// Handles interrupts coming from PIO controllers.
//------------------------------------------------------------------------------
static void ISR_Vbus(const Pin *pPin)
{
// Check current level on VBus
if (PIO_Get(&pinVbus)) {
TRACE_INFO("VBUS conn\n\r");
USBD_Connect();
}
else {
TRACE_INFO("VBUS discon\n\r");
USBD_Disconnect();
}
}
//------------------------------------------------------------------------------
/// Configures the VBus pin to trigger an interrupt when the level on that pin
/// changes.
//------------------------------------------------------------------------------
static void VBus_Configure( void )
{
TRACE_INFO("VBus configuration\n\r");
// Configure PIO
PIO_Configure(&pinVbus, 1);
PIO_ConfigureIt(&pinVbus, ISR_Vbus);
PIO_EnableIt(&pinVbus);
// Check current level on VBus
if (PIO_Get(&pinVbus)) {
// if VBUS present, force the connect
TRACE_INFO("VBUS conn\n\r");
USBD_Connect();
}
else {
USBD_Disconnect();
}
}
#else
#define VBUS_CONFIGURE() USBD_Connect()
#endif //#if defined(PIN_USB_VBUS)
#if defined (CP15_PRESENT)
//------------------------------------------------------------------------------
/// Put the CPU in 32kHz, disable PLL, main oscillator
/// Put voltage regulator in standby mode
//------------------------------------------------------------------------------
void LowPowerMode(void)
{
PMC_CPUInIdleMode();
}
//------------------------------------------------------------------------------
/// Put voltage regulator in normal mode
/// Return the CPU to normal speed 48MHz, enable PLL, main oscillator
//------------------------------------------------------------------------------
void NormalPowerMode(void)
{
}
#elif defined(at91sam7a3)
//------------------------------------------------------------------------------
/// Put the CPU in 32kHz, disable PLL, main oscillator
//------------------------------------------------------------------------------
void LowPowerMode(void)
{
// MCK=48MHz to MCK=32kHz
// MCK = SLCK/2 : change source first from 48 000 000 to 18. / 2 = 9M
AT91C_BASE_PMC->PMC_MCKR = AT91C_PMC_PRES_CLK_2;
while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) );
// MCK=SLCK : then change prescaler
AT91C_BASE_PMC->PMC_MCKR = AT91C_PMC_CSS_SLOW_CLK;
while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) );
// disable PLL
AT91C_BASE_PMC->PMC_PLLR = 0;
// Disable Main Oscillator
AT91C_BASE_PMC->PMC_MOR = 0;
PMC_DisableProcessorClock();
}
//------------------------------------------------------------------------------
/// Return the CPU to normal speed 48MHz, enable PLL, main oscillator
//------------------------------------------------------------------------------
void NormalPowerMode(void)
{
// MCK=32kHz to MCK=48MHz
// enable Main Oscillator
AT91C_BASE_PMC->PMC_MOR = (( (AT91C_CKGR_OSCOUNT & (0x06 <<8)) | AT91C_CKGR_MOSCEN ));
while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MOSCS ) );
// enable PLL@96MHz
AT91C_BASE_PMC->PMC_PLLR = ((AT91C_CKGR_DIV & 0x0E) |
(AT91C_CKGR_PLLCOUNT & (28<<8)) |
(AT91C_CKGR_MUL & (0x48<<16)));
while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_LOCK ) );
while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) );
AT91C_BASE_CKGR->CKGR_PLLR |= AT91C_CKGR_USBDIV_1 ;
// MCK=SLCK/2 : change prescaler first
AT91C_BASE_PMC->PMC_MCKR = AT91C_PMC_PRES_CLK_2;
while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) );
// MCK=PLLCK/2 : then change source
AT91C_BASE_PMC->PMC_MCKR |= AT91C_PMC_CSS_PLL_CLK ;
while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) );
}
#elif defined (at91sam7se)
//------------------------------------------------------------------------------
/// Put the CPU in 32kHz, disable PLL, main oscillator
/// Put voltage regulator in standby mode
//------------------------------------------------------------------------------
void LowPowerMode(void)
{
// MCK=48MHz to MCK=32kHz
// MCK = SLCK/2 : change source first from 48 000 000 to 18. / 2 = 9M
AT91C_BASE_PMC->PMC_MCKR = AT91C_PMC_PRES_CLK_2;
while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) );
// MCK=SLCK : then change prescaler
AT91C_BASE_PMC->PMC_MCKR = AT91C_PMC_CSS_SLOW_CLK;
while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) );
// disable PLL
AT91C_BASE_PMC->PMC_PLLR = 0;
// Disable Main Oscillator
AT91C_BASE_PMC->PMC_MOR = 0;
// Voltage regulator in standby mode : Enable VREG Low Power Mode
AT91C_BASE_VREG->VREG_MR |= AT91C_VREG_PSTDBY;
PMC_DisableProcessorClock();
}
//------------------------------------------------------------------------------
/// Put voltage regulator in normal mode
/// Return the CPU to normal speed 48MHz, enable PLL, main oscillator
//------------------------------------------------------------------------------
void NormalPowerMode(void)
{
// Voltage regulator in normal mode : Disable VREG Low Power Mode
AT91C_BASE_VREG->VREG_MR &= ~AT91C_VREG_PSTDBY;
// MCK=32kHz to MCK=48MHz
// enable Main Oscillator
AT91C_BASE_PMC->PMC_MOR = (( (AT91C_CKGR_OSCOUNT & (0x06 <<8)) | AT91C_CKGR_MOSCEN ));
while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MOSCS ) );
// enable PLL@96MHz
AT91C_BASE_PMC->PMC_PLLR = ((AT91C_CKGR_DIV & 0x0E) |
(AT91C_CKGR_PLLCOUNT & (28<<8)) |
(AT91C_CKGR_MUL & (0x48<<16)));
while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_LOCK ) );
while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) );
AT91C_BASE_CKGR->CKGR_PLLR |= AT91C_CKGR_USBDIV_1 ;
// MCK=SLCK/2 : change prescaler first
AT91C_BASE_PMC->PMC_MCKR = AT91C_PMC_PRES_CLK_2;
while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) );
// MCK=PLLCK/2 : then change source
AT91C_BASE_PMC->PMC_MCKR |= AT91C_PMC_CSS_PLL_CLK ;
while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) );
}
#elif defined (at91sam7s)
//------------------------------------------------------------------------------
/// Put the CPU in 32kHz, disable PLL, main oscillator
/// Put voltage regulator in standby mode
//------------------------------------------------------------------------------
void LowPowerMode(void)
{
// MCK=48MHz to MCK=32kHz
// MCK = SLCK/2 : change source first from 48 000 000 to 18. / 2 = 9M
AT91C_BASE_PMC->PMC_MCKR = AT91C_PMC_PRES_CLK_2;
while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) );
// MCK=SLCK : then change prescaler
AT91C_BASE_PMC->PMC_MCKR = AT91C_PMC_CSS_SLOW_CLK;
while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) );
// disable PLL
AT91C_BASE_PMC->PMC_PLLR = 0;
// Disable Main Oscillator
AT91C_BASE_PMC->PMC_MOR = 0;
// Voltage regulator in standby mode : Enable VREG Low Power Mode
AT91C_BASE_VREG->VREG_MR |= AT91C_VREG_PSTDBY;
PMC_DisableProcessorClock();
}
//------------------------------------------------------------------------------
/// Put voltage regulator in normal mode
/// Return the CPU to normal speed 48MHz, enable PLL, main oscillator
//------------------------------------------------------------------------------
void NormalPowerMode(void)
{
// Voltage regulator in normal mode : Disable VREG Low Power Mode
AT91C_BASE_VREG->VREG_MR &= ~AT91C_VREG_PSTDBY;
// MCK=32kHz to MCK=48MHz
// enable Main Oscillator
AT91C_BASE_PMC->PMC_MOR = (( (AT91C_CKGR_OSCOUNT & (0x06 <<8)) | AT91C_CKGR_MOSCEN ));
while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MOSCS ) );
// enable PLL@96MHz
AT91C_BASE_PMC->PMC_PLLR = ((AT91C_CKGR_DIV & 0x0E) |
(AT91C_CKGR_PLLCOUNT & (28<<8)) |
(AT91C_CKGR_MUL & (0x48<<16)));
while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_LOCK ) );
while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) );
AT91C_BASE_CKGR->CKGR_PLLR |= AT91C_CKGR_USBDIV_1 ;
// MCK=SLCK/2 : change prescaler first
AT91C_BASE_PMC->PMC_MCKR = AT91C_PMC_PRES_CLK_2;
while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) );
// MCK=PLLCK/2 : then change source
AT91C_BASE_PMC->PMC_MCKR |= AT91C_PMC_CSS_PLL_CLK ;
while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) );
}
#elif defined (at91sam7x) || defined (at91sam7xc)
//------------------------------------------------------------------------------
/// Put the CPU in 32kHz, disable PLL, main oscillator
/// Put voltage regulator in standby mode
//------------------------------------------------------------------------------
void LowPowerMode(void)
{
// MCK=48MHz to MCK=32kHz
// MCK = SLCK/2 : change source first from 48 000 000 to 18. / 2 = 9M
AT91C_BASE_PMC->PMC_MCKR = AT91C_PMC_PRES_CLK_2;
while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) );
// MCK=SLCK : then change prescaler
AT91C_BASE_PMC->PMC_MCKR = AT91C_PMC_CSS_SLOW_CLK;
while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) );
// disable PLL
AT91C_BASE_PMC->PMC_PLLR = 0;
// Disable Main Oscillator
AT91C_BASE_PMC->PMC_MOR = 0;
// Voltage regulator in standby mode : Enable VREG Low Power Mode
AT91C_BASE_VREG->VREG_MR |= AT91C_VREG_PSTDBY;
PMC_DisableProcessorClock();
}
//------------------------------------------------------------------------------
/// Put voltage regulator in normal mode
/// Return the CPU to normal speed 48MHz, enable PLL, main oscillator
//------------------------------------------------------------------------------
void NormalPowerMode(void)
{
// Voltage regulator in normal mode : Disable VREG Low Power Mode
AT91C_BASE_VREG->VREG_MR &= ~AT91C_VREG_PSTDBY;
// MCK=32kHz to MCK=48MHz
// enable Main Oscillator
AT91C_BASE_PMC->PMC_MOR = (( (AT91C_CKGR_OSCOUNT & (0x06 <<8)) | AT91C_CKGR_MOSCEN ));
while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MOSCS ) );
// enable PLL@96MHz
AT91C_BASE_PMC->PMC_PLLR = ((AT91C_CKGR_DIV & 0x0E) |
(AT91C_CKGR_PLLCOUNT & (28<<8)) |
(AT91C_CKGR_MUL & (0x48<<16)));
while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_LOCK ) );
while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) );
AT91C_BASE_CKGR->CKGR_PLLR |= AT91C_CKGR_USBDIV_1 ;
// MCK=SLCK/2 : change prescaler first
AT91C_BASE_PMC->PMC_MCKR = AT91C_PMC_PRES_CLK_2;
while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) );
// MCK=PLLCK/2 : then change source
AT91C_BASE_PMC->PMC_MCKR |= AT91C_PMC_CSS_PLL_CLK ;
while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) );
}
#endif
//------------------------------------------------------------------------------
// Callbacks re-implementation
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
/// Invoked when the USB device leaves the Suspended state. By default,
/// configures the LEDs.
//------------------------------------------------------------------------------
void USBDCallbacks_Resumed(void)
{
// Initialize LEDs
LED_Configure(USBD_LEDPOWER);
LED_Set(USBD_LEDPOWER);
LED_Configure(USBD_LEDUSB);
LED_Clear(USBD_LEDUSB);
USBState = STATE_RESUME;
}
//------------------------------------------------------------------------------
/// Invoked when the USB device gets suspended. By default, turns off all LEDs.
//------------------------------------------------------------------------------
void USBDCallbacks_Suspended(void)
{
// Turn off LEDs
LED_Clear(USBD_LEDPOWER);
LED_Clear(USBD_LEDUSB);
USBState = STATE_SUSPEND;
}
//------------------------------------------------------------------------------
// Exported functions
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
/// Initializes the CCID driver and runs it.
/// \return Unused (ANSI-C compatibility)
//------------------------------------------------------------------------------
int main( void )
{
// Initialize traces
TRACE_CONFIGURE(DBGU_STANDARD, 115200, BOARD_MCK);
printf("-- USB Device CCID Project %s --\n\r", SOFTPACK_VERSION);
printf("-- %s\n\r", BOARD_NAME);
printf("-- Compiled: %s %s --\n\r", __DATE__, __TIME__);
// If they are present, configure Vbus & Wake-up pins
PIO_InitializeInterrupts(0);
// Configure IT on Smart Card
ConfigureCardDetection();
// Configure ISO7816 driver
PIO_Configure(pinsISO7816, PIO_LISTSIZE(pinsISO7816));
PIO_Configure(pinsPower, PIO_LISTSIZE(pinsPower));
/* power up the card */
PIO_Set(&pinsPower[0]);
ISO7816_Init( pinIso7816RstMC );
// USB audio driver initialization
CCIDDriver_Initialize();
// connect if needed
VBUS_CONFIGURE();
while (USBD_GetState() < USBD_STATE_CONFIGURED);
CCID_Insertion();
// Infinite loop
while (1) {
if( USBState == STATE_SUSPEND ) {
TRACE_DEBUG("suspend !\n\r");
LowPowerMode();
USBState = STATE_IDLE;
}
if( USBState == STATE_RESUME ) {
// Return in normal MODE
TRACE_DEBUG("resume !\n\r");
NormalPowerMode();
USBState = STATE_IDLE;
}
CCID_SmartCardRequest();
}
return 0;
}