{------------------------------------------------------ MODULE : LPE112 FICHIER : LPE112.PAS ROLE : Acces au lecteur de carte, gestion de la procedure de transmission (PE112 PHILIPS). REMARQUE: Il existe deux protocoles de transfert pour ce lecteur; Le protocole FASTNET étant 30% plus rapide (cf. doc. PHILIPS), c'est celui-ci qui est mis en oeuvre dans ce module. AUTEUR : Pascal Chour DATES : 1986 pour programme initial sous DOS 1998 pour 1ère version PE112 2005 pour portage sous Windows 2012 pour amélioration module+voir ci-dessous. Note : le PE112 n'est pas terrible. Il ne renvoie pas les octets ATR sauf l'historique. Modification 2012 suite à la prise en compte des nouvelles normes 7816 (nouvelles depuis 1986). Dans l'ancienne norme, on pouvait déduire la longueur de L dans CLA,INS,P1,P2,L. Ce n'est plus possible dans certains cas. TPDU qui était un simple buffer d'octets a été transformé en record qui intègre la longueur utile de la trame. Cette longueur doit désormais être initialisée si on appelle un lecteur PCSC. Pour les anciens lecteurs, c'est selon... Normalement ça continue de fonctionner comme avant -------------------------------------------------------} UNIT Lpe112; INTERFACE uses PCvars, IOUtil; (****************************************************************************) (* Spec : Initialisation de l'UART avec les paramètres du lecteur. *) (* Entrée : AD n° de la voie d'E/S à initialiser (0 pour COM1, 1 pour COM2).*) (****************************************************************************) function T7_INIT_UART(CC : integer) : boolean; (****************************************************************************) (* Spec : Permet d'émettre et/ou de recevoir un PDU de la carte *) (* Entrée : CC numéro du contexte courant; *) (* Entrée/Sortie : PDU le PDU émit/reçu de la carte; *) (* STAT status carte suite à l'E/S. *) (****************************************************************************) procedure T7_ENTREE_SORTIE(CC: integer; var PDU:TPDU; var STAT : longint); IMPLEMENTATION const PE_EOT = -1 { $04}; (* fin de transmission ou rien émettre ou "control mode" *) PE_LG_ENTETE_EM = 3; (* Longueur de l'en-tete d'une trame PE112 en émission *) PE_LG_ENTETE_RC = 3; (* Longueur de l'en-tete d'une trame PE112 en reception *) PE_LG_ORDRE = 5; (* Longueur d'un ordre lecteur PE112 *) PE_LG_STATUS = 2; (* Nombre d'octets de status lecteur *) PE_FASTNET = $15; (* Sélection du protocol FASTNET *) {--------------------------- Role : Interprétation des status lecteur; Description : Le lecteur PE112 retourne parfois deux octets de status (par exemple lorsque la carte n'est pas dans le lecteur), cette procédure décode ces status. Remarque : sur les commentaires. Les commentaires en français indiquent le message qui sera affiché à l'écran et en anglais l'inter- prétation selon la documentation PHILIPS. ----------------------------} procedure InterpreterStatusLecteur(OCTET1, OCTET2 : BYTE; var STATUS : longint); begin if (OCTET1 = $60) then begin case OCTET2 of $40 : STATUS := 175; (* erreur non documentée : probable pb init UART *) $C0, (* Command unknown *) $C1, (* Illegal parameters in the command *) $C2 : STATUS := 153; (* Illegal format of the command *) $80 : STATUS := 0; (* Operation correctly terminated *) $81 : STATUS := 145; (* carte absente *) $82 : STATUS := 0; (* Card in correct position *) $84 : STATUS := 0; (* Card in right position *) $85 : STATUS := 146; (* Card pulled out and then replaced in right position *) $87 : STATUS := 0; (* Card still in right position *) $C3 : (* Block length on block header differs from actual block length *); $F0 : STATUS := 230;(* Smart card answer error or erroneous TS byte *) $F1 : STATUS := 148; (* 3 parity errors in TS byte reception *) $F2 : STATUS := 4; (* Smart card cannot be processed *) $F3 : STATUS := 4; (* Card protocol not supported *) $F4 : (* Framing error in reception mode *); $F8 : STATUS := 148;(* 3 parity errors in reception mode *) $FC : STATUS := 148;(* 3 parity errors in transmission mode *) $FD : STATUS := 174;(* Failure in programming voltage generator or possible short circuit *) $FE : STATUS := 147; (* carte muette, Smart Card I/O line held at 0v *) else STATUS := 160;;(* Erreur non répertoriée *) end; (*case*) end else if (OCTET1 = $90) then begin if OCTET2 <> 0 then STATUS := 147; (* erreur rapportée par PE112. Interprétation ?? *) end; end; (******************************************************************************) (* Initialisation UART *) (******************************************************************************) function T7_INIT_UART(CC : integer) : boolean; var i_Status : longint; begin (* Ouverture d'une connexion en mode FASTNET *) CONTEXTE[CC]^.ES.OuvrirCom(CONTEXTE[CC]^.PORT_ES, UART_9600, UART_8bits, UART_None, UART_1Stop, i_Status); T7_INIT_UART := I_STATUS=0; end; procedure T7_ENTREE_SORTIE(CC: integer; var PDU : TPDU; var STAT : longint); var DATA : TPDU; i_Indice, i_LgDonnees : integer; {--------------------------- Role : Emission d'une commande carte ou lecteur; Description : Ajoute à la commande fournie en paramètre sous forme de PDU un en-tête et transmet la trame au lecteur. ----------------------------} procedure EmettreCommande(const DATA : TPDU; i_DataLength : Integer; var i_Status : Integer); var Trame : TPDU; var i_Indice : integer; begin (* Sélection du protocole de transfert de caractères *) Trame.DATA[0] := PE_FASTNET; (* protocole FASTNET *) Trame.DATA[1] := (i_DataLength and $FF00) div 256; (* poids fort de la longueur des données *) Trame.DATA[2] := (i_DataLength and $FF); (* poids faible de la longueur des données *) (* Recopie de la commande dans la trame *) for i_Indice := PE_LG_ENTETE_EM to (i_DataLength + PE_LG_ENTETE_EM) do Trame.DATA[i_Indice] := DATA.DATA[i_Indice+1-PE_LG_ENTETE_EM]; CONTEXTE[CC]^.ES.SEND((i_DataLength + PE_LG_ENTETE_EM), Trame, CONTEXTE[CC]^.PORT_ES,i_Status); end; {--------------------------- Role : Reception des octets émis par le lecteur; Description : Récupère les octets transmis par le lecteur et transmet les données à l'appelant. ----------------------------} procedure RecevoirResultat(var DATA : TPDU; var i_DataLength : Integer; var i_Status : Integer); begin (* Reception du résultat *) I_DataLength := PE_LG_ENTETE_RC; CONTEXTE[CC]^.ES.RECEIVE(i_DataLength, PE_EOT, TCOURT, DATA, CONTEXTE[CC]^.PORT_ES,i_Status); if (i_Status = 0) then begin i_DataLength := DATA.DATA[2]; (* longueur de la trame utile *) CONTEXTE[CC]^.ES.RECEIVE(i_DataLength, PE_EOT, TCOURT, DATA,CONTEXTE[CC]^.PORT_ES,i_Status); if (i_Status = 0) then InterpreterStatusLecteur(Data.DATA[i_DataLength-2], Data.DATA[i_DataLength-1], i_Status); end; end; (******************************************************************************) (* Utilitaires de mise en oeuvre de PE_ENTREE_SORTIE *) (******************************************************************************) {--------------------------- Role : Demande d'acquisition des status lecteur. ----------------------------} procedure RequestReaderStatus(var i_Status : Integer); var DATA : TPDU; i_DataLength : Integer; begin (* Constitution d'une trame de demande de "Status" lecteur *) DATA.DATA[1] := $00; DATA.DATA[2] := $60; DATA.DATA[3] := $10; DATA.DATA[4] := $00; DATA.DATA[5] := $00; EmettreCommande(DATA, PE_LG_ORDRE, i_Status); RecevoirResultat(DATA, i_DataLength, i_Status); end; {--------------------------- Role : Attente d'insertion de carte dans le lecteur; Remarque : Ceci se traduit physiquement par le clignotement deux fois par seconde de la LED verte du lecteur. ----------------------------} procedure Swallow(var i_Status : Integer); var DATA : TPDU; begin (* Constitution d'une trame d'attente d'insertion de carte *) DATA.DATA[1] := $00; DATA.DATA[2] := $60; DATA.DATA[3] := $30; DATA.DATA[4] := $00; DATA.DATA[5] := $00; EmettreCommande(DATA, PE_LG_ORDRE, i_Status); end; {--------------------------- Role : Demande d'éjection de la carte du lecteur; Remarque : cette commande est sans effet si le lecteur n'est pas motorisé. ----------------------------} procedure EjectCard(var i_Status : Integer); var DATA : TPDU; i_DataLength : Integer; begin (* Constitution d'une trame d'éjection de la carte *) DATA.DATA[1] := $00; DATA.DATA[2] := $90; DATA.DATA[3] := $00; DATA.DATA[4] := $00; DATA.DATA[5] := $00; EmettreCommande(DATA, PE_LG_ORDRE, i_Status); RecevoirResultat(DATA, i_DataLength, i_Status); end; {--------------------------- Role : Demande de mise sous tension de la carte. ----------------------------} procedure CardPowerOn(var DATA : TPDU; var i_DataLength : Integer; var i_Status : Integer); begin (* Constitution d'une trame de Mise Sous Tension de la carte *) DATA.DATA[1] := $00; DATA.DATA[2] := $60; DATA.DATA[3] := $00; DATA.DATA[4] := $00; DATA.DATA[5] := $00; EmettreCommande(DATA, PE_LG_ORDRE, i_Status); RecevoirResultat(DATA, i_DataLength, i_Status); end; {--------------------------- Role : Demande de mise hors tension de la carte. ----------------------------} procedure CardPowerOff(var i_Status : Integer); var DATA : TPDU; i_DataLength : Integer; begin (* Constitution d'une trame de Mise Hors Tension de la carte *) DATA.DATA[1] := $00; DATA.DATA[2] := $90; DATA.DATA[3] := $10; DATA.DATA[4] := $00; DATA.DATA[5] := $00; EmettreCommande(DATA, PE_LG_ORDRE, i_Status); RecevoirResultat(DATA, i_DataLength, i_Status); If (i_Status = 0) then InterpreterStatusLecteur(DATA.DATA[i_DataLength-2], DATA.DATA[i_DataLength-1], i_Status); end; begin with CONTEXTE[CC]^ do begin case PDU.DATA[0] of OMHT : begin (* Mise hors tension de la carte *) CardPowerOff(STAT); (* Ejection de la carte si lecteur motorisé sinon commande sans effet *) if (STAT = 0) then EjectCard(STAT); end; OMST, OWARM : begin (* Mise sous tension de la carte. Mise sous tension à chaud non dispo *) (* Vérifie que carte est bien insérée dans le lecteur *) RequestReaderStatus(STAT); (* Si oui on tente de la mettre sous tension *) if (STAT = 0) then begin CardPowerOn(DATA,i_LgDonnees,STAT); if STAT = 0 then begin PDU.DATA[3] := DATA.DATA[i_LgDonnees-2]; (* ME1 *) PDU.DATA[4] := DATA.DATA[i_LgDonnees-1]; (* ME2 *) (* le PE112 ne renvoie pas les octets de MST *) (* on force des valeurs bidons pour le décodage *) PDU.DATA[5] := ASYNCH; PDU.DATA[6] := i_LgDonnees+2; PDU.DATA[7] := $3F; PDU.DATA[8] := i_LgDonnees and $0F; (* TD = 0/Longueur *) (* on recopie les données d'historique *) for I_indice := 0 to I_LgDonnees do PDU.DATA[9+I_indice] := DATA.DATA[I_indice]; end; end; end; OS : begin (* Ordre Sortant *) EmettreCommande(PDU,5,STAT); if (STAT = 0) then RecevoirResultat(DATA,i_LgDonnees,STAT); if STAT = 0 then begin PDU.DATA[3] := DATA.DATA[i_LgDonnees-2]; (* ME1 *) PDU.DATA[4] := DATA.DATA[i_LgDonnees-1]; (* ME2 *) PDU.DATA[5] := i_LgDonnees - PE_LG_STATUS; (* Longueur des données *) for i_Indice := 0 to i_LgDonnees-3 do PDU.DATA[i_Indice+6] := DATA.DATA[i_Indice]; end else fillchar(DATA,sizeof(DATA),0); (* pour faire propre, pas franchement utile *) end; OE : begin (* Ordre Entrant *) for i_Indice := 0 to PDUMAX-1 do Data.DATA[i_Indice] := PDU.DATA[i_Indice+1]; EmettreCommande(PDU,5+PDU.DATA[5],STAT); if (STAT = 0) then RecevoirResultat(DATA, i_LgDonnees,STAT); if STAT = 0 then begin PDU.DATA[3] := DATA.DATA[i_LgDonnees-2]; (* ME1 *) PDU.DATA[4] := DATA.DATA[i_LgDonnees-1]; (* ME2 *) PDU.DATA[5] := 0; (* Longueur des données *) end else fillchar(DATA,sizeof(DATA),0); (* pour faire propre, pas franchement utile *) end; end{case}; end; end; (* T7_ENTREE_SORTIE *) END.