/* ---------------------------------------------------------------------------- * 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.
/// Different tests can be done with the Windows(c) software Smart Access(c).
/// 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 SAM-BA User Guide, /// the GNU-Based Software Development /// application note or to the IAR EWARM User Guide, /// 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.
/// 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.
/// -# The host use the CCID device driver (usbccid.sys) as the functionnal driver. /// -# Launch Smart Access and connect it to the Card Reader Atmel.
/// Use Smart Access for launch instruction command.
/// 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 #include #include #include #include #include #include #include #include #include #include #include #include //----------------------------------------------------------------------------- // 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; }