wim/Scard/src/ScardSession.cpp
changeset 0 164170e6151a
equal deleted inserted replaced
-1:000000000000 0:164170e6151a
       
     1 /*
       
     2 * Copyright (c) 2003 Nokia Corporation and/or its subsidiary(-ies). 
       
     3 * All rights reserved.
       
     4 * This component and the accompanying materials are made available
       
     5 * under the terms of "Eclipse Public License v1.0"
       
     6 * which accompanies this distribution, and is available
       
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 *
       
     9 * Initial Contributors:
       
    10 * Nokia Corporation - initial contribution.
       
    11 *
       
    12 * Contributors:
       
    13 *
       
    14 * Description:  Session for the Scard server, to a single client-side session
       
    15 *
       
    16 */
       
    17 
       
    18 
       
    19 
       
    20 // INCLUDE FILES
       
    21 #include    "ScardBase.h"
       
    22 #include    "ScardReader.h"
       
    23 #include    "ScardClsv.h"
       
    24 #include    "ScardNotifyRegistry.h"
       
    25 #include    "ScardSession.h"
       
    26 #include    "ScardServer.h"
       
    27 #include    "ScardAccessControl.h"
       
    28 #include    "WimTrace.h"
       
    29 
       
    30 #ifdef _DEBUG // for logging
       
    31 #include    "ScardLogs.h"
       
    32 #include    <flogger.h> 
       
    33 #endif
       
    34 
       
    35 // LOCAL CONSTANTS AND MACROS
       
    36 const TInt KResponseLengthCloseChannel = 0;
       
    37 const TInt KResponseLengthOpenChannel  = 1;
       
    38 const TInt KCommandLengthCloseChannel  = 4;
       
    39 const TInt KCommandLengthOpenChannel   = 6;
       
    40 
       
    41 // Assert that the channel has been opened for this session, and if not, leave
       
    42 #define __ASSERT_CHANNEL_OPEN( a ) TRAPD( channelClosed,\
       
    43                     iAccessCtrl->ValidateChannelL( ( a ), iSessionID ) );\
       
    44                     if ( channelClosed )\
       
    45                         {\
       
    46                         aMessage.Complete( KScServerErrIllegalChannel );\
       
    47                         return;\
       
    48                         }
       
    49 
       
    50 // ============================ MEMBER FUNCTIONS ===============================
       
    51 
       
    52 // -----------------------------------------------------------------------------
       
    53 // CScardSession::CScardSession
       
    54 // C++ default constructor can NOT contain any code, that
       
    55 // might leave.
       
    56 // -----------------------------------------------------------------------------
       
    57 //
       
    58 CScardSession::CScardSession()
       
    59     : CSession2()
       
    60     {
       
    61     _WIMTRACE(_L("WIM|Scard|CScardSession::CScardSession|Begin"));
       
    62     }
       
    63 
       
    64 // -----------------------------------------------------------------------------
       
    65 // CScardSession::ConstructL
       
    66 // Symbian 2nd phase constructor can leave.
       
    67 // -----------------------------------------------------------------------------
       
    68 //
       
    69 void CScardSession::ConstructL( CScardServer* aServer )
       
    70     {
       
    71     _WIMTRACE(_L("WIM|Scard|CScardSession::ConstructL|Begin"));
       
    72     iServer         = aServer;
       
    73     iNotifyRegistry = iServer->NotifyRegistry();
       
    74     }
       
    75 
       
    76 // -----------------------------------------------------------------------------
       
    77 // CScardSession::NewL
       
    78 // Two-phased constructor.
       
    79 // -----------------------------------------------------------------------------
       
    80 //
       
    81 CScardSession* CScardSession::NewL( CScardServer* aServer )
       
    82     {
       
    83     _WIMTRACE(_L("WIM|Scard|CScardSession::NewL|Begin"));
       
    84     CScardSession* self = new( ELeave ) CScardSession;
       
    85     
       
    86     CleanupStack::PushL( self );
       
    87     self->ConstructL( aServer );
       
    88     CleanupStack::Pop( self );
       
    89 
       
    90     return self;
       
    91     }
       
    92 
       
    93 // Destructor
       
    94 CScardSession::~CScardSession()
       
    95     {
       
    96     DeleteBuffers();
       
    97     }
       
    98 
       
    99 
       
   100 // -----------------------------------------------------------------------------
       
   101 // CScardSession::ServiceL
       
   102 // Handle the client message
       
   103 // -----------------------------------------------------------------------------
       
   104 //
       
   105 void CScardSession::ServiceL( const TMessageHandle& aMessageHandle )
       
   106     {
       
   107     _WIMTRACE(_L("WIM|Scard|CScardSession::ServiceL|Begin"));
       
   108     ServiceL( aMessageHandle.iMessage );
       
   109     }
       
   110 
       
   111 // -----------------------------------------------------------------------------
       
   112 // CScardSession::CloseSession
       
   113 // Clean up the reader event listeners
       
   114 // -----------------------------------------------------------------------------
       
   115 //
       
   116 void CScardSession::CloseSession( const RMessage2& aMessage )
       
   117     {
       
   118     _WIMTRACE(_L("WIM|Scard|CScardSession::CloseSession|Begin"));
       
   119     if ( iAccessCtrl && iAccessCtrl->IsAttached( this ) )
       
   120         {
       
   121         iAccessCtrl->DetachSessionFromReader( iSessionID );
       
   122         }
       
   123 
       
   124     RThread client;
       
   125     aMessage.Client( client );
       
   126     iNotifyRegistry->RemoveReaderEventListeners( client );
       
   127     }
       
   128 
       
   129 // -----------------------------------------------------------------------------
       
   130 // CScardSession::AsynchronousServiceComplete
       
   131 // An asynchronous service has been completed by the reader.
       
   132 // -----------------------------------------------------------------------------
       
   133 //
       
   134 void CScardSession::AsynchronousServiceComplete(
       
   135     const TMessageHandle& aMessageHandle, 
       
   136     const TInt aErrorCode )
       
   137     {
       
   138     _WIMTRACE(_L("WIM|Scard|CScardSession::AsynchronousServiceComplete|Begin"));
       
   139     const RMessage2& aMessage = aMessageHandle.iMessage;
       
   140     //  In all cases, if the operation has encountered a problem, ignore 
       
   141     //  results and just complain to the client that something went wrong.
       
   142     if ( aErrorCode != KErrNone )
       
   143         {
       
   144 #ifdef _DEBUG
       
   145          RFileLogger::WriteFormat( KScardLogDir, KScardLogFileName, 
       
   146             EFileLoggingModeAppend, 
       
   147             _L( "CScardSession::AsynchronousServiceComplete ERROR: %d.\n" ),
       
   148             aErrorCode );
       
   149 #endif
       
   150 
       
   151         DeleteBuffers();
       
   152         aMessage.Complete( aErrorCode );
       
   153         return;
       
   154         }
       
   155 
       
   156     //  Now, see what got serviced
       
   157     _WIMTRACE2(_L("WIM|Scard|CScardSession::AsynchronousServiceComplete|Event=%d"), aMessage.Function());
       
   158     switch ( aMessage.Function() )
       
   159         {
       
   160         case EScardServerTransmitToCard:
       
   161             {
       
   162 #ifdef _DEBUG
       
   163              RFileLogger::Write( KScardLogDir, KScardLogFileName, 
       
   164                 EFileLoggingModeAppend, 
       
   165                 _L( "Transmit to card completed.\n" ) );
       
   166 #endif
       
   167             //  Write response to client and cleanup
       
   168             WriteToClient( aMessage, *iResponse, 1 );
       
   169             DeleteBuffers();
       
   170             aMessage.Complete( KErrNone );
       
   171             break;
       
   172             }
       
   173 
       
   174         case EScardServerGetCapabilities:
       
   175             {
       
   176 #ifdef _DEBUG
       
   177              RFileLogger::Write( KScardLogDir, KScardLogFileName, 
       
   178                 EFileLoggingModeAppend, 
       
   179                 _L( "Get capabilities completed.\n" ) );
       
   180 #endif
       
   181             //  Write response to client and cleanup
       
   182             WriteToClient( aMessage, *iResponse, 2 );
       
   183             DeleteBuffers();
       
   184             aMessage.Complete( KErrNone );
       
   185             break;
       
   186             }
       
   187 
       
   188         case EScardServerManageChannel:
       
   189             {
       
   190 #ifdef _DEBUG
       
   191              RFileLogger::Write( KScardLogDir, KScardLogFileName, 
       
   192                 EFileLoggingModeAppend, 
       
   193                 _L( "Manage channel completed.\n" ) );
       
   194 #endif
       
   195 
       
   196             //  Which type of channel management got serviced?
       
   197             switch ( aMessage.Int0() )
       
   198                 {
       
   199                 case EOpenAnyChannel:
       
   200                     {
       
   201                     //  Was there a proper response 
       
   202                     //  (aka. the command was successful)
       
   203                     if ( iResponse->Length() == KOpenChannelResponseLength )
       
   204                         {
       
   205                         //  Add this session to the newly opened channel
       
   206                         TBool ok( EFalse );
       
   207                         TRAPD( mem, ok = iAccessCtrl->AddSessionToChannelL( 
       
   208                             (*iResponse)[0], iSessionID ) ); 
       
   209                         __ASSERT_MEMORY( mem );
       
   210                         if ( !ok )
       
   211                             {
       
   212                             aMessage.Complete( KScErrNotSupported );
       
   213                             return;
       
   214                             }
       
   215                         }
       
   216                     //  Then write the card's response back to the client 
       
   217                     //  and cleanup
       
   218                     WriteToClient( aMessage, *iResponse, 1 );
       
   219                     DeleteBuffers();                    
       
   220                     aMessage.Complete( KErrNone );
       
   221                     break;
       
   222                     }
       
   223                 case ECloseChannel:
       
   224                     {
       
   225                     //  Channel closed, OK
       
   226                     aMessage.Complete( KErrNone );
       
   227                     DeleteBuffers();
       
   228                     break;
       
   229                     }
       
   230                 default:
       
   231                     {
       
   232                     break;
       
   233                     }
       
   234                 }
       
   235             return;
       
   236             }
       
   237 
       
   238         default:
       
   239             {
       
   240             User::Panic( _L( "Message Stack Corrupted" ), 
       
   241                 KScServerPanicInternalError );
       
   242             break;
       
   243             }
       
   244         }
       
   245     }
       
   246 
       
   247 // -----------------------------------------------------------------------------
       
   248 // CScardSession::ServiceL
       
   249 // Message handler for the session.
       
   250 // Dispatches requests to the appropriate handler
       
   251 // Trap harness for dispatcher
       
   252 // -----------------------------------------------------------------------------
       
   253 //
       
   254 void CScardSession::ServiceL( const RMessage2& aMessage )
       
   255     {
       
   256     _WIMTRACE2(_L("WIM|Scard|CScardSession::ServiceL|Begin|Event=%d"), aMessage.Function());
       
   257     //  Divide the operation to the handler functions
       
   258     switch ( aMessage.Function() )
       
   259         {
       
   260         //  These functions are synchronous, and don't use message stack,
       
   261         //  so we can complete the messages here.
       
   262         case EScardServerNotifyChange:
       
   263             {
       
   264             iNotifyRegistry->AddReaderEventListenerL( aMessage );
       
   265             // Does not complete message because it is completed when card
       
   266             // event occurs
       
   267             break;
       
   268             }
       
   269         case EScardServerCancelNotifyChange:
       
   270             {
       
   271             iNotifyRegistry->RemoveReaderEventListener( aMessage );
       
   272             aMessage.Complete( KErrNone );
       
   273             break;
       
   274             }
       
   275         case EScardServerCloseSession:
       
   276             {
       
   277             CloseSession( aMessage );
       
   278             aMessage.Complete( KErrNone );
       
   279             break;
       
   280             }
       
   281         case EScardServerDisconnectFromReader:
       
   282             {
       
   283             DisconnectFromReader();
       
   284             aMessage.Complete( KErrNone );
       
   285             break;
       
   286             }
       
   287         case EScardServerCancelTransmit:
       
   288             {
       
   289             CancelTransmitL();
       
   290             aMessage.Complete( KErrNone );
       
   291             break;
       
   292             }
       
   293         //  These functions are asynchronous, and (may) put their messages
       
   294         //  in the message stack, so they do completion internally.
       
   295         case EScardServerConnectToReader:
       
   296             {
       
   297             TInt desLen = aMessage.GetDesMaxLength( 1 );
       
   298             _WIMTRACE2(_L( "WIM|Scard|EScardServerConnectToReader desLen %d" ), desLen );
       
   299             if ( desLen <= 0 )
       
   300                {
       
   301                _WIMTRACE(_L(" WIM|Scard|EScardServerConnectToReader Descriptor is bad"));
       
   302                aMessage.Panic( _L( "ScardServer" ), KErrBadDescriptor );
       
   303                break;
       
   304                }
       
   305             ConnectToReaderL( aMessage );
       
   306             break;
       
   307             }
       
   308          case EScardServerGetATR:
       
   309             {
       
   310             aMessage.Complete( KErrNotSupported );
       
   311             break;
       
   312             }
       
   313         case EScardServerTransmitToCard:
       
   314             {
       
   315             TransmitToCard( aMessage );
       
   316             break;
       
   317             }
       
   318         case EScardServerGetCapabilities:
       
   319             {
       
   320             GetCapabilities( aMessage );
       
   321             break;
       
   322             }
       
   323         case EScardServerManageChannel:
       
   324             {
       
   325             ManageChannel( aMessage );
       
   326             break;
       
   327             }
       
   328         
       
   329         //  A corrupt request
       
   330         default:
       
   331             aMessage.Panic( _L( "ScardServer" ), KScServerPanicBadRequest );
       
   332             break;
       
   333         }
       
   334     }
       
   335 
       
   336 // -----------------------------------------------------------------------------
       
   337 // CScardSession::ConnectToReaderL
       
   338 // Ask Access Control to add this session to registry, and update session ID
       
   339 // -----------------------------------------------------------------------------
       
   340 //
       
   341 void CScardSession::ConnectToReaderL( const RMessage2& aMessage )
       
   342     {
       
   343     _WIMTRACE(_L("WIM|Scard|CScardSession::ConnectToReaderL|Begin"));
       
   344 #ifdef _DEBUG
       
   345     RFileLogger::WriteFormat( KScardLogDir, KScardLogFileName, 
       
   346         EFileLoggingModeAppend,
       
   347         _L( "CScardSession::ConnectToReaderL entered.\n" ) );
       
   348 #endif
       
   349     //  If already connected, what more could the client want??
       
   350     if ( iConnectedToReader )
       
   351         {
       
   352 #ifdef _DEBUG
       
   353         RFileLogger::WriteFormat( KScardLogDir, KScardLogFileName, 
       
   354          EFileLoggingModeAppend, 
       
   355          _L( "CScardSession::ConnectToReaderL FAILED: already connected.\n" ) );
       
   356 #endif
       
   357         aMessage.Complete( KScErrAlreadyExists );
       
   358         return;
       
   359         }
       
   360     // Store client thread from message to member to be used in PanicClient().
       
   361     aMessage.Client( iClient );
       
   362     // If not, get on with it...            
       
   363     iServer->ConnectToReaderL( this, aMessage );
       
   364     }
       
   365 
       
   366 // -----------------------------------------------------------------------------
       
   367 // CScardSession::DisconnectFromReader
       
   368 // Ask Access Control to detach this session from the reader.
       
   369 // -----------------------------------------------------------------------------
       
   370 //
       
   371 void CScardSession::DisconnectFromReader()
       
   372     {
       
   373     _WIMTRACE(_L("WIM|Scard|CScardSession::DisconnectFromReader|Begin"));
       
   374     //  If connected at all, disconnect
       
   375     if ( iConnectedToReader )
       
   376         {
       
   377         iAccessCtrl->DetachSessionFromReader( iSessionID );
       
   378         iConnectedToReader = EFalse;
       
   379         iAccessCtrl = NULL;
       
   380         }
       
   381     }
       
   382 
       
   383 // -----------------------------------------------------------------------------
       
   384 // CScardSession::GetCapabilities
       
   385 // Get capabilities of the card
       
   386 // -----------------------------------------------------------------------------
       
   387 //
       
   388 void CScardSession::GetCapabilities( const RMessage2& aMessage )
       
   389     {
       
   390     _WIMTRACE(_L("WIM|Scard|CScardSession::GetCapabilities|Begin"));
       
   391     if ( !iConnectedToReader )
       
   392         {
       
   393         aMessage.Panic( _L( "Reader not contacted" ), 
       
   394             KScPanicNoResourceConnection );
       
   395         return;
       
   396         }
       
   397     const TInt8 channel( 0 );
       
   398 
       
   399     if ( iAccessCtrl->ReaderIsReady( iSessionID, channel ) )
       
   400         {
       
   401         TRequestStatus& status =
       
   402             iAccessCtrl->InitiateCommunication( iSessionID, aMessage,
       
   403                 reinterpret_cast< TInt32>( aMessage.Ptr3() ), channel );
       
   404         __ASSERT_MEMORY( status.Int() );
       
   405         TInt32 commandCode( 0 );
       
   406         commandCode = aMessage.Int1();
       
   407         commandCode |= ( aMessage.Int0() << 8 ) & 0xff00;
       
   408 
       
   409         //  Get the response buffer
       
   410         TInt error = AllocateResponse( aMessage, 2 );
       
   411         if ( error )
       
   412             {
       
   413             aMessage.Complete( error );
       
   414             return;
       
   415             }
       
   416 
       
   417         TInt32 timeout = reinterpret_cast< TInt32>( aMessage.Ptr3() );
       
   418         //  ...do the control command
       
   419         iReader->GetCapabilities( status, commandCode, *iResponse, timeout );
       
   420         }
       
   421     //  The reader was busy, queue
       
   422     else
       
   423         {
       
   424 #ifdef _DEBUG
       
   425         RFileLogger::Write( KScardLogDir, KScardLogFileName, 
       
   426         EFileLoggingModeAppend, 
       
   427         _L("CScardSession::GetCapabilities()-reader busy, message queued.\n") );
       
   428 #endif
       
   429 
       
   430         iAccessCtrl->QueueExecution( aMessage, iSessionID,
       
   431             reinterpret_cast< TInt32 >( aMessage.Ptr3() ), channel );
       
   432         }
       
   433     }
       
   434 
       
   435 // -----------------------------------------------------------------------------
       
   436 // CScardSession::TransmitToCard
       
   437 // Transmit data to the card
       
   438 // -----------------------------------------------------------------------------
       
   439 //
       
   440 void CScardSession::TransmitToCard( const RMessage2& aMessage )
       
   441     {
       
   442     _WIMTRACE(_L("WIM|Scard|CScardSession::TransmitToCard|Begin"));
       
   443     if ( !iConnectedToReader )
       
   444         {
       
   445         aMessage.Panic( _L( "Reader not contacted" ), 
       
   446             KScPanicNoResourceConnection );
       
   447         return;
       
   448         }
       
   449 
       
   450     // check if this client has the access rights for the reader
       
   451     // and reader is ready
       
   452     const TInt8 channel( static_cast< TInt8>( aMessage.Int3() ) );
       
   453     __ASSERT_CHANNEL_OPEN( channel );
       
   454 
       
   455     if ( iAccessCtrl->ReaderIsReady( iSessionID, channel ) )
       
   456         {
       
   457         //  Read the command APDU from the client
       
   458         TInt error = ReadFromClient( aMessage, iCommandBuffer, iCommand, 0 );
       
   459         if ( error )
       
   460             {
       
   461 #ifdef _DEBUG
       
   462     RFileLogger::WriteFormat( KScardLogDir, KScardLogFileName, 
       
   463         EFileLoggingModeAppend, 
       
   464         _L("CScardSession::TransmitToCard()-Cmd APDU couldn't be read from\
       
   465         client. Error: %d.\n"), error );
       
   466 #endif
       
   467             return;
       
   468             }
       
   469 
       
   470         //  If the channel number is not the same one as asked for
       
   471         //  in the parameter 3 of the message, adjust the command...
       
   472         if ( ( (*iCommand)[KCLA] & 0x0f) != channel )
       
   473             {
       
   474 #ifdef _DEBUG
       
   475             RFileLogger::WriteFormat( KScardLogDir, KScardLogFileName, 
       
   476             EFileLoggingModeAppend, 
       
   477             _L("CScardSession::TransmitToCard() -- Channel changed from %d\
       
   478              to: %d.\n"), (*iCommand)[KCLA] & 0x0f, channel & 0x0f );
       
   479 #endif
       
   480             (*iCommand)[KCLA] &= 0xf0;
       
   481             (*iCommand)[KCLA] |= channel;
       
   482             }
       
   483 
       
   484         //  No manage channel apdu's are allowed. 
       
   485         //  Use CScardComm::ManageChannel() instead.
       
   486         if ( (*iCommand)[KINS] == KManageChannel )
       
   487             {
       
   488 #ifdef _DEBUG
       
   489             RFileLogger::Write( KScardLogDir, KScardLogFileName, 
       
   490                 EFileLoggingModeAppend, 
       
   491                 _L( "CScardSession::TransmitToCard() -- Error! Manage channel\
       
   492                 APDU.\n" ) );
       
   493 #endif
       
   494             delete iCommand;
       
   495             iCommand = NULL;
       
   496             aMessage.Complete( KScServerErrIllegalOperation );
       
   497             return;
       
   498             }
       
   499 
       
   500         //  Alloc response 
       
   501         error = AllocateResponse( aMessage, 1 );
       
   502         if ( error )
       
   503             {
       
   504 #ifdef _DEBUG
       
   505             RFileLogger::Write( KScardLogDir, KScardLogFileName, 
       
   506                 EFileLoggingModeAppend, 
       
   507                 _L( "CScardSession::TransmitToCard() -- Could not allocate\
       
   508                 response.\n" ) );
       
   509 #endif
       
   510             return;
       
   511             }
       
   512 
       
   513         //  Transfer APDU
       
   514         TRequestStatus& sr =
       
   515             iAccessCtrl->InitiateCommunication( iSessionID, aMessage,
       
   516                 reinterpret_cast< TInt32>( aMessage.Ptr2() ), channel );
       
   517         __ASSERT_MEMORY( sr.Int() );
       
   518         const TInt32 timeout = static_cast< TInt32>( aMessage.Int2() );
       
   519 #ifdef _DEBUG
       
   520         RFileLogger::HexDump( KScardLogDir, KScardLogFileName, 
       
   521             EFileLoggingModeAppend, 
       
   522             _S( "CScardSession::TransmitToCard() command APDU:\n" ), 0,
       
   523             iCommand->Ptr(), iCommand->Length() );
       
   524 #endif
       
   525         iReader->TransmitToCard( sr, *iCommand, *iResponse, timeout );
       
   526         }
       
   527     //  The reader was busy, queue
       
   528     else
       
   529         {
       
   530 #ifdef _DEBUG
       
   531         RFileLogger::Write( KScardLogDir, KScardLogFileName, 
       
   532             EFileLoggingModeAppend, 
       
   533             _L( "CScardSession::TransmitToCard() -- reader busy. message\
       
   534             queued.\n" ) );
       
   535 #endif
       
   536         iAccessCtrl->QueueExecution( aMessage, iSessionID, 
       
   537             static_cast< TInt32>( aMessage.Int2() ), channel );
       
   538         }
       
   539     }
       
   540 
       
   541 // -----------------------------------------------------------------------------
       
   542 // CScardSession::CancelTransmit
       
   543 // Cancel all transmissions belonging to this session
       
   544 // -----------------------------------------------------------------------------
       
   545 //
       
   546 void CScardSession::CancelTransmitL()
       
   547     {
       
   548     _WIMTRACE(_L("WIM|Scard|CScardSession::CancelTransmit|Begin"));
       
   549     // It is not clear if this session is attempting to connect, so cancel any
       
   550     // connection attempts as well... If this session is not connecting at the
       
   551     // moment, this does nothing.
       
   552     iServer->ConnectionRegistry()->CancelConnection( this );
       
   553 
       
   554     if ( iAccessCtrl )
       
   555         {
       
   556         //  Cancel from the reader
       
   557         iAccessCtrl->CancelTransmissionsL( iSessionID );
       
   558         }
       
   559     }
       
   560 
       
   561 // -----------------------------------------------------------------------------
       
   562 // CScardSession::ManageChannel
       
   563 // Manage logical channel
       
   564 // -----------------------------------------------------------------------------
       
   565 //
       
   566 void CScardSession::ManageChannel( const RMessage2& aMessage )
       
   567     {
       
   568     _WIMTRACE(_L("WIM|Scard|CScardSession::ManageChannel|Begin"));
       
   569     if ( !iConnectedToReader )
       
   570         {
       
   571         aMessage.Panic( _L( "Reader not contacted" ), 
       
   572             KScPanicNoResourceConnection );
       
   573         return;
       
   574         }
       
   575     
       
   576     const TUint8 maxChannels( 4 );
       
   577 
       
   578     //  What exactly does the client want to do?
       
   579     const TInt operationCode = aMessage.Int0();
       
   580     _WIMTRACE2(_L("WIM|Scard|CScardSession::ManageChannel|operationCode=%d"), operationCode);
       
   581     switch ( operationCode )
       
   582         {
       
   583         //  Open a channel and the card will assign the channel number
       
   584         case EOpenAnyChannel:
       
   585             {
       
   586             //  If we're already on all the channels, return error indicating 
       
   587             //  that no more channels are available
       
   588             TBool allOpen( ETrue );
       
   589             TInt8 i( 0 );
       
   590             for ( i = 1; i < maxChannels; i++ )
       
   591                 {
       
   592                 TRAPD( closed, iAccessCtrl->ValidateChannelL( i, iSessionID ) );
       
   593                 if ( closed )
       
   594                     {
       
   595                     allOpen = EFalse;
       
   596                     break;
       
   597                     }
       
   598                 }
       
   599             if ( allOpen )
       
   600                 {
       
   601 #ifdef _DEBUG
       
   602                 RFileLogger::Write( KScardLogDir, KScardLogFileName, 
       
   603                     EFileLoggingModeAppend, 
       
   604                     _L( "CScardSession::ManageChannel() -- Open any channel:\
       
   605                     all channels open.\n" ) );
       
   606 #endif
       
   607                 _WIMTRACE(_L("WIM|Scard|CScardSession::ManageChannel|no more channels available"));
       
   608                 aMessage.Complete( KScErrFull );
       
   609                 return;
       
   610                 }
       
   611 
       
   612             //  Check if all channels have already been opened.
       
   613             //  (Of course this session is not yet on all of them)
       
   614             allOpen = ETrue;
       
   615             for ( i = 1 ; i < maxChannels ; i++ ) 
       
   616                 {
       
   617                 if ( !( iAccessCtrl->ChannelOpenedYet( i ) ) )  
       
   618                     {
       
   619                     allOpen = EFalse;
       
   620                     break;
       
   621                     }
       
   622                 }
       
   623 
       
   624             //  If all channels have been opened, get the first unreserved
       
   625             //  one and assign the session to it. If no unreserved channels
       
   626             //  are found, put the message on hold for the next available channel.
       
   627             if ( allOpen )
       
   628                 {
       
   629                 _WIMTRACE(_L("WIM|Scard|CScardSession::ManageChannel|all channels open"));
       
   630                 const TInt8 channel = iAccessCtrl->UnreservedLogicalChannel();
       
   631                 if ( channel > 0 )
       
   632                     {
       
   633                     _WIMTRACE(_L("WIM|Scard|CScardSession::ManageChannel|unreserved channels found"));
       
   634                     TRAPD( err, iAccessCtrl->AddSessionToChannelL( 
       
   635                         channel, iSessionID ) ); 
       
   636                     __ASSERT_MEMORY( err );
       
   637                     _WIMTRACE(_L("WIM|Scard|CScardSession::ManageChannel|session added to channel"));
       
   638                     aMessage.Complete( KErrNone );
       
   639                     return;
       
   640                     }
       
   641                 else
       
   642                     {
       
   643                     _WIMTRACE(_L("WIM|Scard|CScardSession::ManageChannel|no unreserved channels found"));
       
   644                     iAccessCtrl->QueueChannelOperation( aMessage, iSessionID,
       
   645                         reinterpret_cast< TInt32>( aMessage.Ptr2() ),
       
   646                         KAllChannels );
       
   647                     return;
       
   648                     }
       
   649                 }
       
   650             _WIMTRACE(_L("WIM|Scard|CScardSession::ManageChannel|some channels not open"));
       
   651             //  The card has logical channels available that have not been 
       
   652             //  yet opened. Make the command and send it to the SC
       
   653             if ( iAccessCtrl->ReaderIsReady( iSessionID, KChannel0 ) )
       
   654                 {
       
   655                 _WIMTRACE(_L("WIM|Scard|CScardSession::ManageChannel|reader ready"));
       
   656                 DoCommandAndTransmit( 
       
   657                     aMessage,
       
   658                     KOpenChannel,
       
   659                     0,
       
   660                     KCommandLengthOpenChannel,
       
   661                     KResponseLengthOpenChannel,
       
   662                     reinterpret_cast< TInt32 >( aMessage.Ptr2() ),
       
   663                     KChannel0 );
       
   664                 return;
       
   665                 }
       
   666             //  The reader was busy, queue
       
   667             else
       
   668                 {
       
   669 #ifdef _DEBUG
       
   670                 RFileLogger::Write( KScardLogDir, KScardLogFileName, 
       
   671                     EFileLoggingModeAppend, 
       
   672                     _L( "CScardSession::ManageChannel() -- reader busy.\
       
   673                     message queued.\n" ) );
       
   674 #endif
       
   675                 _WIMTRACE(_L("WIM|Scard|CScardSession::ManageChannel|reader busy"));
       
   676                 iAccessCtrl->QueueExecution( aMessage, iSessionID,
       
   677                     reinterpret_cast< TInt32>( aMessage.Ptr2() ), KChannel0 );
       
   678                 return;
       
   679                 }
       
   680             //break; //unreachable
       
   681             }
       
   682 
       
   683         //  Close a channel
       
   684         case ECloseChannel:
       
   685             {
       
   686             const TUint8 channel( static_cast< TUint8>( aMessage.Int1() ) );
       
   687             TBool stillOpen( EFalse );
       
   688             //  Remove this session from the channel
       
   689             TRAPD( err, stillOpen = iAccessCtrl->RemoveSessionFromChannelL( 
       
   690                 channel, iSessionID ) );
       
   691             if ( err != KErrNone )
       
   692                 {
       
   693 #ifdef _DEBUG
       
   694                 RFileLogger::WriteFormat( KScardLogDir, KScardLogFileName, 
       
   695                     EFileLoggingModeAppend, 
       
   696                     _L( "CScardSession::ManageChannel() -- Close channel:\
       
   697                     Error %d in removing session.\n" ), err );
       
   698 #endif
       
   699                 aMessage.Complete( err );
       
   700                 return;
       
   701                 }
       
   702 
       
   703             //  If no one else is using the channel send CloseChannel to card
       
   704             if ( !stillOpen )
       
   705                 {
       
   706                 if ( iAccessCtrl->ReaderIsReady( iSessionID, KChannel0 ) )
       
   707                     {
       
   708                     //  If possible send the close channel APDU
       
   709                     DoCommandAndTransmit( 
       
   710                         aMessage,
       
   711                         KCloseChannel,
       
   712                         channel, 
       
   713                         KCommandLengthCloseChannel,
       
   714                         KResponseLengthCloseChannel,
       
   715                         reinterpret_cast< TInt32>( aMessage.Ptr2() ),
       
   716                         KChannel0 );
       
   717                     return;
       
   718                     }
       
   719 
       
   720                 //  The reader is busy, queue
       
   721                 else
       
   722                     {
       
   723 #ifdef _DEBUG
       
   724                     RFileLogger::Write( KScardLogDir, KScardLogFileName, 
       
   725                         EFileLoggingModeAppend, 
       
   726                         _L( "CScardSession::ManageChannel() -- reader busy.\
       
   727                         message queued.\n" ) );
       
   728 #endif
       
   729                     iAccessCtrl->QueueExecution( aMessage, iSessionID, 0, 
       
   730                         KChannel0 );
       
   731                     return;
       
   732                     }
       
   733                 }
       
   734             //  Some one else is still on the channel, so we can't physically 
       
   735             //  close it from the SC
       
   736             else
       
   737                 {
       
   738                 aMessage.Complete( KErrNone );
       
   739                 return;
       
   740                 }
       
   741             //break; //unreachable
       
   742             }
       
   743 
       
   744         //  Check channel status
       
   745         case EChannelStatus:
       
   746             {
       
   747             //  Get status
       
   748             TUint16 status = iAccessCtrl->ChannelStatus();
       
   749             TBuf8<2> buf(2);
       
   750             // Split the 16 bit integer to two 8-bit bytes
       
   751             buf[0] = static_cast< TInt8 >( status & 0x00ff );
       
   752             buf[1] = static_cast< TInt8 >( ( status & 0xff00 ) >> 8 );
       
   753 
       
   754             //  Inform client
       
   755             WriteToClient( aMessage, buf, 1 );
       
   756             aMessage.Complete( KErrNone );
       
   757             break;
       
   758             }
       
   759         //  Something's gotten warped
       
   760         default:
       
   761             aMessage.Complete( KScErrBadArgument );
       
   762             break;
       
   763         }
       
   764     }
       
   765 
       
   766 // -----------------------------------------------------------------------------
       
   767 // CScardSession::DoCommandAndTransmit
       
   768 // Create a command APDU for ManageChannel operations, and send it
       
   769 // -----------------------------------------------------------------------------
       
   770 //
       
   771 void CScardSession::DoCommandAndTransmit(
       
   772     const RMessage2& aMessage, 
       
   773     const TUint8 aCommand, 
       
   774     const TUint8 aP2,
       
   775     const TInt8 aCommandLength,
       
   776     const TUint8 aResponseLength, 
       
   777     const TInt32 aTimeOut, 
       
   778     const TUint8 aChannel )
       
   779     {
       
   780     _WIMTRACE(_L("WIM|Scard|CScardSession::DoCommandAndTransmit|Begin"));
       
   781 
       
   782     TRAPD( memFail, DoCommandAndTransmitL( aMessage,
       
   783                                            aCommand,
       
   784                                            aP2,
       
   785                                            aCommandLength,
       
   786                                            aResponseLength,
       
   787                                            aTimeOut,
       
   788                                            aChannel ) );
       
   789     __ASSERT_MEMORY( memFail );
       
   790     }
       
   791 
       
   792 // -----------------------------------------------------------------------------
       
   793 // CScardSession::DoCommandAndTransmitL
       
   794 // Create a command APDU for ManageChannel operations, and send it
       
   795 // -----------------------------------------------------------------------------
       
   796 //
       
   797 void CScardSession::DoCommandAndTransmitL(
       
   798     const RMessage2& aMessage,
       
   799     const TUint8 aCommand,
       
   800     const TUint8 aP2,
       
   801     const TInt8 aCommandLength,
       
   802     const TUint8 aResponseLength,
       
   803     const TInt32 aTimeOut,
       
   804     const TUint8 aChannel )
       
   805     {
       
   806     _WIMTRACE(_L("WIM|Scard|CScardSession::DoCommandAndTransmitL|Begin"));
       
   807     TRequestStatus& status = iAccessCtrl->InitiateCommunication( iSessionID,
       
   808         aMessage, aTimeOut, static_cast< TInt8 >( aChannel ) );
       
   809     __ASSERT_MEMORY( status.Int() );
       
   810 
       
   811     // allocate and assert the command
       
   812     iCommandBuffer = HBufC8::NewL( aCommandLength );
       
   813     iCommand = new( ELeave ) TPtr8( iCommandBuffer->Des() );
       
   814 
       
   815     // Allocate and assert the response
       
   816 
       
   817     // response + 2 status bytes
       
   818     iResponseBuffer = HBufC8::NewL( aResponseLength + 2 );
       
   819     iResponse = new( ELeave ) TPtr8( iResponseBuffer->Des() );
       
   820 
       
   821     //  Make the command APDU
       
   822     iCommand->SetLength( 4 );
       
   823     (*iCommand)[KCLA] = aChannel;
       
   824     (*iCommand)[KINS] = KManageChannel;
       
   825     (*iCommand)[KP1] = aCommand;
       
   826     (*iCommand)[KP2] = aP2;
       
   827 
       
   828     if ( aResponseLength )
       
   829         {
       
   830         //  one byte length
       
   831         iCommand->SetLength( 5 );
       
   832         (*iCommand)[KLcHigh] = aResponseLength;
       
   833         }
       
   834 
       
   835     //  Transmit the APDU to the SC
       
   836 #ifdef _DEBUG
       
   837     RFileLogger::HexDump( KScardLogDir, KScardLogFileName,
       
   838         EFileLoggingModeAppend,
       
   839         _S( "CScardSession::DoCommandAndTransmit() command APDU:\n" ), 0,
       
   840         iCommand->Ptr(), iCommand->Length() );
       
   841 #endif
       
   842 
       
   843     iReader->TransmitToCard( status, *iCommand, *iResponse, aTimeOut );
       
   844     _WIMTRACE(_L("WIM|Scard|CScardSession::DoCommandAndTransmitL|End"));
       
   845     return;
       
   846     }
       
   847 
       
   848 // -----------------------------------------------------------------------------
       
   849 // CScardSession::WriteToClient
       
   850 // Write the provided descriptor to the client thread, 8 bit version.
       
   851 // -----------------------------------------------------------------------------
       
   852 //
       
   853 void CScardSession::WriteToClient(
       
   854     const RMessage2& aMessage, 
       
   855     const TDesC8& aResponseBuffer, 
       
   856     const TInt aPtrIndex )
       
   857     {
       
   858     _WIMTRACE(_L("WIM|Scard|CScardSession::WriteToClient|Begin"));
       
   859 
       
   860     TInt desLen = DesLength( aMessage, aPtrIndex, ETrue ); //Len of descriptor;
       
   861 
       
   862     //  Make sure there is always enough space for the response
       
   863     __ASSERT_ALWAYS( desLen >= aResponseBuffer.Length(), 
       
   864         aMessage.Panic( _L( "Response space too short" ), 
       
   865             KScServerPanicBadDescriptor ) );
       
   866 
       
   867     //  Write
       
   868     TRAPD( desError, aMessage.WriteL( aPtrIndex, aResponseBuffer ) );
       
   869 
       
   870     if ( desError )
       
   871         {
       
   872         aMessage.Panic( _L( "ScardServer" ), KScServerPanicBadDescriptor );
       
   873         }
       
   874     }
       
   875 
       
   876 // -----------------------------------------------------------------------------
       
   877 // CScardSession::WriteToClient
       
   878 // Write the provided descriptor to the client thread, 16 bit version.
       
   879 // -----------------------------------------------------------------------------
       
   880 //
       
   881 void CScardSession::WriteToClient(
       
   882     const RMessage2& aMessage, 
       
   883     const TDesC16& aResponseBuffer, 
       
   884     const TInt aPtrIndex )
       
   885     {
       
   886     _WIMTRACE(_L("WIM|Scard|CScardSession::|Begin"));
       
   887     TInt desLen = DesLength( aMessage, aPtrIndex, ETrue ); //Len of descriptor
       
   888     _WIMTRACE2(_L("WIM|Scard|CScardSession:: %d "), desLen );
       
   889     _WIMTRACE2(_L("WIM|Scard|CScardSession:: %d "), aResponseBuffer.Length() );
       
   890 
       
   891     //  Make sure there is always enough space for the response
       
   892     __ASSERT_ALWAYS( desLen >= aResponseBuffer.Length(), 
       
   893         aMessage.Panic( _L( "Response space too short" ), 
       
   894             KScServerPanicBadDescriptor ) );
       
   895 
       
   896     //  Write
       
   897     TRAPD( desError, aMessage.WriteL( aPtrIndex, aResponseBuffer ) );
       
   898 
       
   899     if ( desError )
       
   900         {
       
   901         aMessage.Panic( _L( "ScardServer" ), KScServerPanicBadDescriptor );
       
   902         }
       
   903     }
       
   904 
       
   905 // -----------------------------------------------------------------------------
       
   906 // CScardSession::ReadFromClient
       
   907 // 8 bit version
       
   908 // -----------------------------------------------------------------------------
       
   909 //
       
   910 TInt CScardSession::ReadFromClient(
       
   911     const RMessage2& aMessage, 
       
   912     HBufC8*& aBuffer, 
       
   913     TPtr8*& aPointer, 
       
   914     const TInt aPtrIndex )
       
   915     {
       
   916     _WIMTRACE(_L("WIM|Scard|CScardSession::ReadFromClient|Begin"));
       
   917     TInt desLen = DesLength( aMessage, aPtrIndex, EFalse );
       
   918 
       
   919     //  Allocate the buffer and pointer to it
       
   920     TRAPD( memFail, aBuffer = HBufC8::NewL( desLen ) );
       
   921     __ASSERT_MEMORY( memFail );
       
   922 
       
   923     aPointer = new TPtr8( aBuffer->Des() );
       
   924     __ASSERT_MEMORY( !aPointer );
       
   925 
       
   926     //  Read the descriptor and that's it
       
   927     TRAPD( desError, aMessage.ReadL( aPtrIndex, *aPointer ) );
       
   928     if ( desError )
       
   929         {
       
   930         aMessage.Panic( _L( "ScardServer" ), KScServerPanicBadDescriptor );
       
   931         return KScErrGeneral;
       
   932         }
       
   933     return KErrNone;
       
   934     }
       
   935 
       
   936 // -----------------------------------------------------------------------------
       
   937 // CScardSession::AllocateResponse
       
   938 // Allocate space for the internal response buffer. The lenght of the 
       
   939 // necessary space is deduced from the message.
       
   940 // -----------------------------------------------------------------------------
       
   941 //
       
   942 TInt CScardSession::AllocateResponse( const RMessage2& aMessage,
       
   943                                       const TInt aPtrIndex )
       
   944     {
       
   945     _WIMTRACE(_L("WIM|Scard|CScardSession::AllocateResponse|Begin"));
       
   946     TInt desLen = DesLength( aMessage, aPtrIndex, ETrue ); //Len of descriptor;
       
   947 
       
   948     //  If no space is needed, why bother
       
   949     if ( desLen <= 0 )
       
   950         {
       
   951         return -1;
       
   952         }
       
   953 
       
   954     //  Allocate and assert the response buffer
       
   955     TRAPD( memFail, AllocateResponseL( desLen ) );
       
   956     __ASSERT_MEMORY( memFail );
       
   957 
       
   958     return KErrNone;
       
   959     }
       
   960 
       
   961 // -----------------------------------------------------------------------------
       
   962 // CScardSession::AllocateResponse
       
   963 // Allocate space for the internal response buffer. The lenght of the
       
   964 // necessary space is deduced from the message.
       
   965 // -----------------------------------------------------------------------------
       
   966 //
       
   967 void CScardSession::AllocateResponseL( TInt aDesLen )
       
   968     {
       
   969     _WIMTRACE(_L("WIM|Scard|CScardSession::AllocateResponseL|Begin"));
       
   970 
       
   971     //  Allocate the response buffer
       
   972     iResponseBuffer = HBufC8::NewL( aDesLen );
       
   973     iResponse = new( ELeave ) TPtr8( iResponseBuffer->Des() );
       
   974     }
       
   975 
       
   976 // -----------------------------------------------------------------------------
       
   977 // CScardSession::DeleteBuffers
       
   978 // Delete all reserved buffers
       
   979 // -----------------------------------------------------------------------------
       
   980 //
       
   981 void CScardSession::DeleteBuffers()
       
   982     {
       
   983     _WIMTRACE(_L("WIM|Scard|CScardSession::DeleteBuffers|Begin"));
       
   984     delete iCommand; 
       
   985     delete iResponse;
       
   986     iCommand = NULL; 
       
   987     iResponse = NULL;
       
   988     delete iCommandBuffer; 
       
   989     delete iResponseBuffer;
       
   990     iCommandBuffer = NULL; 
       
   991     iResponseBuffer = NULL;
       
   992     _WIMTRACE(_L("WIM|Scard|CScardSession::DeleteBuffers|End"));
       
   993     }
       
   994 
       
   995 // -----------------------------------------------------------------------------
       
   996 // CScardSession::ConnectionDone
       
   997 // Connection has established (maybe with error)
       
   998 // -----------------------------------------------------------------------------
       
   999 //
       
  1000 void CScardSession::ConnectionDone(
       
  1001     const TReaderID& aReaderID, 
       
  1002     const RMessage2& aMessage, 
       
  1003     const TInt aErrorCode )
       
  1004     {
       
  1005     _WIMTRACE(_L("WIM|Scard|CScardSession::ConnectionDone|Begin"));
       
  1006     //  Connection successfully completed, attach to the reader and write the
       
  1007     //  reader's name to the client thread
       
  1008     if ( aErrorCode == KErrNone )
       
  1009         {
       
  1010         iAccessCtrl = iServer->FindAccessControl( aReaderID );
       
  1011         __ASSERT_MEMORY( !iAccessCtrl );
       
  1012         TRAPD( readerError, iReader = iAccessCtrl->AttachSessionToReaderL(
       
  1013             this, iSessionID ) );
       
  1014         if ( readerError == KErrNone )
       
  1015             {
       
  1016             iConnectedToReader = ETrue;
       
  1017             TScardReaderName name( iServer->FriendlyName( aReaderID ) );
       
  1018             WriteToClient( aMessage, name, 1 );
       
  1019             }
       
  1020         else 
       
  1021             {
       
  1022 #ifdef _DEBUG
       
  1023             RFileLogger::WriteFormat( KScardLogDir, KScardLogFileName, 
       
  1024                 EFileLoggingModeAppend, 
       
  1025                 _L( "CScardSession::ConnectionDone() -- Error %d when attaching\
       
  1026                 session to reader: \n" ), readerError );
       
  1027 #endif
       
  1028             iConnectedToReader = EFalse;
       
  1029             }
       
  1030         }
       
  1031     //  No connection could be established
       
  1032     else
       
  1033         {
       
  1034 #ifdef _DEBUG
       
  1035         RFileLogger::WriteFormat( KScardLogDir, KScardLogFileName, 
       
  1036             EFileLoggingModeAppend, 
       
  1037             _L( "CScardSession::ConnectionDone() -- ERROR: %d.\n" ), 
       
  1038             aErrorCode );
       
  1039 #endif
       
  1040         iConnectedToReader = EFalse;
       
  1041         }
       
  1042     }
       
  1043 
       
  1044 // -----------------------------------------------------------------------------
       
  1045 // CScardSession::DesLength
       
  1046 // Return length of descriptor in client message
       
  1047 // -----------------------------------------------------------------------------
       
  1048 //
       
  1049 TInt CScardSession::DesLength(
       
  1050     const RMessage2& aMessage,
       
  1051     const TInt aPtrIndex,
       
  1052     const TBool aMaxLength )
       
  1053     {
       
  1054     _WIMTRACE(_L("WIM|Scard|CScardSession::DesLength|Begin"));
       
  1055     TInt desLen( 0 );
       
  1056 
       
  1057     if ( aMaxLength ) //Get max length
       
  1058         {
       
  1059         desLen = aMessage.GetDesMaxLength( aPtrIndex );
       
  1060         }
       
  1061     else
       
  1062         {
       
  1063         desLen = aMessage.GetDesLength( aPtrIndex );
       
  1064         }
       
  1065 
       
  1066     return desLen;
       
  1067     }
       
  1068 
       
  1069 // -----------------------------------------------------------------------------
       
  1070 // CScardSession::MessagePointer
       
  1071 // Return pointer to wanted block of client message 
       
  1072 // -----------------------------------------------------------------------------
       
  1073 //
       
  1074 TAny* CScardSession::MessagePointer(
       
  1075     const RMessage2& aMessage,
       
  1076     const TInt aPtrIndex )
       
  1077     {
       
  1078     _WIMTRACE(_L("WIM|Scard|CScardSession::MessagePointer|Begin"));
       
  1079     TAny* messagePointer = NULL;
       
  1080     
       
  1081     //  See from which pointer we are reading and set parameters accordingly
       
  1082     switch ( aPtrIndex )
       
  1083         {
       
  1084         case 0:
       
  1085             {
       
  1086             messagePointer = ( TAny* )aMessage.Ptr0();
       
  1087             break;
       
  1088             }
       
  1089         case 1:
       
  1090             {
       
  1091             messagePointer = ( TAny* )aMessage.Ptr1();
       
  1092             break;
       
  1093             }
       
  1094         case 2:
       
  1095             {
       
  1096             messagePointer = ( TAny* )aMessage.Ptr2();
       
  1097             break;
       
  1098             }
       
  1099         case 3:
       
  1100             {
       
  1101             messagePointer = ( TAny* )aMessage.Ptr3();
       
  1102             break;
       
  1103             }
       
  1104         default:
       
  1105             {
       
  1106             User::Panic( _L( "Bad index" ), KScServerPanicInternalError );
       
  1107             }
       
  1108         }
       
  1109     return messagePointer;
       
  1110     }
       
  1111 
       
  1112 // -----------------------------------------------------------------------------
       
  1113 // CScardSession::CardEvent
       
  1114 // This is here only for derivative class CScardConnector
       
  1115 // It implements and needs this function
       
  1116 // -----------------------------------------------------------------------------
       
  1117 //
       
  1118 void CScardSession::CardEvent(
       
  1119     const TScardServiceStatus, 
       
  1120     const TScardATR&, 
       
  1121     const TReaderID& )
       
  1122     {
       
  1123     _WIMTRACE(_L("WIM|Scard|CScardSession::CardEvent|Begin"));
       
  1124     }
       
  1125 
       
  1126 //  End of File