API Application Example API Header File #ifndef __MMD1100_H__ #define __MMD1100_H__ #include /* types */ #define DEVICE_MMD1100 "/dev/ttyS5" #define MMD1100_DEFAULT_BAUDRATE 9600 //#define MMD1100_DEFAULT_BAUDRATE 19200 //#define MMD1100_DEFAULT_BAUDRATE 38400 //#define MMD1100_DEFAULT_BAUDRATE 57600 //#define MMD1100_DEFAULT_BAUDRATE 115200 #define MMD1100_TIMEOUT_BETWEEN_SERIAL_INPUT_DATA 20 /*20 msec*/ typedef enum { MMD1100_COMMAND_GET_VERSION = 0, MMD1100_COMMAND_LOAD_USER_PARAMETERS, MMD1100_COMMAND_UART_CALIBRATION, MMD1100_COMMAND_OTP_WRITE, MMD1100_COMMAND_GET_STATUS, MMD1100_COMMAND_READ_DATA_RETRY, MMD1100_COMMAND_SOFTWARE_RESET } MMD1100_Command_t; typedef struct { /* */ uint8_t ErrorCodeForTrack1; uint8_t ErrorCodeForTrack2; uint8_t ErrorCodeForTrack3; /* */ uint8_t LengthOfTrack1; uint8_t LengthOfTrack2; uint8_t LengthOfTrack3; /* ISO7813 */ uint8_t pTrack1[79 * 2]; uint8_t pTrack2[40 * 2]; uint8_t pTrack3[107 * 2]; /* */ uint8_t LengthOfRaw1; uint8_t LengthOfRaw2; uint8_t LengthOfRaw3; uint8_t pRaw1[79 * 2]; uint8_t pRaw2[40 * 2]; uint8_t pRaw3[107 * 2]; } MSRTrack_t; typedef struct { MMD1100_Command_t Command; uint8_t ErrorCode; uint8_t pData[100]; } MMD1100Response_t; /* open serial device for cummunication with MMD1100 return: -1: Fail to operation -2: Input Paramters Error 0: OK */ extern int mmd1100_open(int *pDeviceFileDescriptor); /* close serial device for end of cummunication with MMD1100 return: -1: Fail to operation 0: OK */ extern int mmd1100_close(void); /* serial buffer flush for cummunication with MMD1100 */ extern void mmd1100_serial_flush(void); /* get serial device file descriptor for cummunication with MMD1100 return: -1: Fail to operation -2: Input Paramters Error 0: OK */ extern int mmd1100_get_device_file_descriptor(int *pDeviceFileDescriptor); /* receive return: -1: Fail to operation -2: Input Parameter Error -4: Timeout or Not Ready 0: OK */ extern int mmd1100_receive(MSRTrack_t *pMSRTrack, MMD1100Response_t *pResponse, uint32_t *pIsNotTrack, uint32_t ResponseTimeout); /* -1: invalid parameter 0 : OK */ extern int mmd1100_make_packet(uint8_t *pPacket, uint32_t *pMakedPacketByteLength, MMD1100_Command_t Command, uint8_t pArguments[4]); /* -1: invalid parameter -2: send error 0 : Not open + : Send length */ extern int mmd1100_send(uint8_t *pPacket, uint8_t PacketByteLength); #endif /* __MMD1100_H__ */ API Source File #include /* types */ #include /* printf */ #include /* memcpy, strlen... */ #include /* struct termios */ #include /* for open/close .. */ #include /* for O_RDWR */ #include /* for ioctl */ #include /* struct pollfd */ #include /* struct timespec */ #include "CRC_LRC.h" /* lrc_xor() */ #include "MemoryControl.h" /* memory_pick_up_bit() */ #include "MMD1100.h" /* */ /* ** ANSI/ISO BCD Data format ** This is a 5-bit Binary Coded Decimal format. It uses a 16-character set, which uses 4 of the 5 available bits. The 5th bit is an ODD parity bit, which means there must be an odd number of 1's in the 5-bit character..the parity bit will 'force' the total to be odd. Also, the Least Significant Bits are read FIRST on the strip. The sum of the 1's in each case is odd, thanks to the parity bit. If the read system adds up the 5 bits and gets an EVEN number, it flags the read as ERROR, and you gotta scan the card again. (yeah, I *know* a lot of you out there *already* understand parity, but I gotta cover all the bases...not everyone sleeps with their modem and can recite the entire AT command set at will, you know ;). See following Figure for details of ANSI/ISO BCD. ANSI/ISO BCD Data Format * Remember that b1 (bit #1) is the LSB (least significant bit)! * The LSB is read FIRST! * Hexadecimal conversions of the Data Bits are given in parenthesis (xH). -Data Bits- Parity b1 b2 b3 b4 b5 Character Function 0 0 0 0 1 0 (0H) Data 1 0 0 0 0 1 (1H) " 0 1 0 0 0 2 (2H) " 1 1 0 0 1 3 (3H) " 0 0 1 0 0 4 (4H) " 1 0 1 0 1 5 (5H) " 0 1 1 0 1 6 (6H) " 1 1 1 0 0 7 (7H) " 0 0 0 1 0 8 (8H) " 1 0 0 1 1 9 (9H) " 0 1 0 1 1 : (AH) Control 1 1 0 1 0 ; (BH) Start Sentinel 0 0 1 1 1 < (CH) Control 1 0 1 1 0 = (DH) Field Separator 0 1 1 1 0 > (EH) Control 1 1 1 1 1 ? (FH) End Sentinel ***** 16 Character 5-bit Set ***** 10 Numeric Data Characters 3 Framing/Field Characters 3 Control Characters The magstripe begins with a string of Zero bit-cells to permit the self-clocking feature of biphase to "sync" and begin decoding. A "Start Sentinel" character then tells the reformatting process where to start grouping the decoded bitstream into groups of 5 bits each. At the end of the data, an "End Sentinel" is encountered, which is followed by an "Longitudinal Redundancy Check (LRC) character. The LRC is a parity check for the sums of all b1, b2, b3, and b4 data bits of all preceding characters. The LRC character will catch the remote error that could occur if an individual character had two compensating errors in its bit pattern (which would fool the 5th-bit parity check). The START SENTINEL, END SENTINEL, and LRC are collectively called "Framing Characters", and are discarded at the end of the reformatting process. */ /* ** ANSI/ISO ALPHA Data Format ** Alphanumeric data can also be encoded on magstripes. The second ANSI/ISO data format is ALPHA (alphanumeric) and involves a 7-bit character set with 64 characters. As before, an odd parity bit is added to the required 6 data bits for each of the 64 characters. ANSI/ISO ALPHA Data Format * Remember that b1 (bit #1) is the LSB (least significant bit)! * The LSB is read FIRST! * Hexadecimal conversions of the Data Bits are given in parenthesis (xH). ----Data Bits---- Parity b1 b2 b3 b4 b5 b6 b7 Character Function 40 0 0 0 0 0 0 1 space (0H) Special 01 1 0 0 0 0 0 0 ! (1H) " 02 0 1 0 0 0 0 0 " (2H) " 43 1 1 0 0 0 0 1 # (3H) " 04 0 0 1 0 0 0 0 $ (4H) " 1 0 1 0 0 0 1 % (5H) Start Sentinel 0 1 1 0 0 0 1 & (6H) Special 1 1 1 0 0 0 0 ' (7H) " 0 0 0 1 0 0 0 ( (8H) " 1 0 0 1 0 0 1 ) (9H) " 0 1 0 1 0 0 1 * (AH) " 1 1 0 1 0 0 0 + (BH) " 0 0 1 1 0 0 1 , (CH) " 1 0 1 1 0 0 0 - (DH) " 0 1 1 1 0 0 0 . (EH) " 1 1 1 1 0 0 1 / (FH) " 0 0 0 0 1 0 0 0 (10H) Data (numeric) 1 0 0 0 1 0 1 1 (11H) " 0 1 0 0 1 0 1 2 (12H) " 1 1 0 0 1 0 0 3 (13H) " 0 0 1 0 1 0 1 4 (14H) " 1 0 1 0 1 0 0 5 (15H) " 0 1 1 0 1 0 0 6 (16H) " 1 1 1 0 1 0 1 7 (17H) " 0 0 0 1 1 0 1 8 (18H) " 1 0 0 1 1 0 0 9 (19H) " 0 1 0 1 1 0 0 : (1AH) Special 1 1 0 1 1 0 1 ; (1BH) " 0 0 1 1 1 0 0 < (1CH) " 1 0 1 1 1 0 1 = (1DH) " 0 1 1 1 1 0 1 > (1EH) " 1 1 1 1 1 0 0 ? (1FH) End Sentinel 0 0 0 0 0 1 0 @ (20H) Special 1 0 0 0 0 1 1 A (21H) Data (alpha) 0 1 0 0 0 1 1 B (22H) " 1 1 0 0 0 1 0 C (23H) " 0 0 1 0 0 1 1 D (24H) " 1 0 1 0 0 1 0 E (25H) " 0 1 1 0 0 1 0 F (26H) " 1 1 1 0 0 1 1 G (27H) " 0 0 0 1 0 1 1 H (28H) " 1 0 0 1 0 1 0 I (29H) " 0 1 0 1 0 1 0 J (2AH) " 1 1 0 1 0 1 1 K (2BH) " 0 0 1 1 0 1 0 L (2CH) " 1 0 1 1 0 1 1 M (2DH) " 0 1 1 1 0 1 1 N (2EH) " 1 1 1 1 0 1 0 O (2FH) " 0 0 0 0 1 1 1 P (30H) " 1 0 0 0 1 1 0 Q (31H) " 0 1 0 0 1 1 0 R (32H) " 1 1 0 0 1 1 1 S (33H) " 0 0 1 0 1 1 0 T (34H) " 1 0 1 0 1 1 1 U (35H) " 0 1 1 0 1 1 1 V (36H) " 1 1 1 0 1 1 0 W (37H) " 0 0 0 1 1 1 0 X (38H) " 1 0 0 1 1 1 1 Y (39H) " 0 1 0 1 1 1 1 Z (3AH) " 1 1 0 1 1 1 0 [ (3BH) Special 0 0 1 1 1 1 1 \ (3DH) Special 1 0 1 1 1 1 0 ] (3EH) Special 0 1 1 1 1 1 0 ^ (3FH) Field Separator 1 1 1 1 1 1 1 _ (40H) Special ***** 64 Character 7-bit Set ***** * 43 Alphanumeric Data Characters * 3 Framing/Field Characters * 18 Control/Special Characters The two ANSI/ISO formats, ALPHA and BCD, allow a great variety of data to be stored on magstripes. Most cards with magstripes use these formats, but occasionally some do not. More about those lateron. */ /* ANSI/ISO Track 1,2,3 Standards Track Name Density Format Characters Function ----------------------------------------------------------------------------------- 1 IATA 210 bpi ALPHA 79 Read Name & Account 2 ABA 75 bpi BCD 40 Read Account 3 THRIFT 210 bpi BCD 107 Read Account & *Encode* Transaction This contains an explanation about the format of the three magnetic tracks in standard identification cards, particularly those used in financial transactions, i.e., credit and debit cards. It is a summary of the international standards ISO-7813(tracks 1 and 2) and ISO-4909(track 3). Track 1 (IATA) Up to 79 ALPHA 7-bit (including parity) characters (alphanumeric) including SS, ES and LRC. Read only. It comprises the following fields (in this order): SS: Start Sentinel. 1 character: %. FC: Format Code. 1 character (alphabetic only): A: Reserved for proprietary use of card issuer. B: Bank/financial. This is the format described here. C-M: Reserved for use by ANSI Subcommittee X3B10. N-Z: Available for use by individual card issuers. PAN: Primary Account Number. Up to 19 digits: In accordance with the account numbering scheme in ISO 7812. It consists of the following parts: IIN: Issuer Identification Number. Up to 6 digits: It consists of the following parts: MII: Major Industry Identifier. Up to 2 digits: 0: Reserved for future use by ISO/TC 68. 00: Institutions other than card issuers. 1: Airlines. 2: Airlines and other future assignments. 3: Travel and entertainment. 4: Banking/financial. 5: Banking/financial. 59: Financial institutions not registered by ISO. 6: Merchandising and banking. 7: Petroleum. 8: Telecommunications and other future assignments. 89: Telecommunications administrations and private operating agencies. 9: Reserved for national use. II: Issuer Identifier. Up to 5 digits: Identifies the issuer. In some cases the first digit indicates the length of the IIN or the responsibility of its assignment outside of ISO. If MII = 9 then the first 3 digits should be the country code (CC). IAI: Individual Account Identification. Up to 12 digits: Assigned by the card issuing institution. CD: Check Digit. 1 digit: Calculated using the Luhn formula for computing modulus 10 "double-add-double" check digit: Double the value of alternate (odd) digits starting by the least significant (first right hand) digit. Then add the individual digits of doubled (odd) numbers and even digits of the original number. If the value ends in 0 then the check digit is 0. Otherwise subtract the value from the next higher number ending in 0 (tens complement of the unit digit). The result is the check digit. Example: If the account number without check digit is 1234 5678 9012 344 then (8) + 4 + (6) + 2 + (2) + 0 + (1 + 8) + 8 + (1 + 4) + 6 + (1 + 0) + 4 + (6) + 2 + (2) = 65, therefore the check digit is 70 - 65 = 5 and so the complete account number is 1234 5678 9012 3445. MasterCard PAN is variable up to 16 digits and VISA is 13 or 16 digits, including check digit. FS: Field Separator. 1 character: ^. CC: Country Code. 3 digits: Only if PAN starts with 59 (MasterCard). Country codes are defined in ISO 3166: 724 for Spain, 840 for USA, etc. NM: Name. 2-26 characters: Surname(s) (separated by space if necessary). Surname separator: /. First name(s) or initial(s) (separated by space if necessary). Period (when followed by title). Title (when used). If this field is not used the content will be an space followed by a surname separator (/). FS: Field Separator. 1 character: ^. ED: Expiry Date. 4 digits: YYMM. If this field is not used a FS will be in place. Required by MasterCard and VISA. SC: Service Code. 3 digits: Digit 1 (most significant): Interchange and technology: 0: Reserved for future use by ISO. 1: Available for international interchange. 2: Available for international interchange and with integrated circuit, which should be used for the financial transaction when feasible. 3: Reserved for future use by ISO. 4: Reserved for future use by ISO. 5: Available for national interchange only, except under bilateral agreement. 6: Available for national interchange only, except under bilateral agreement, and with integrated circuit, which should be used for the financial transaction when feasible. 7: Not available for general interchange, except under bilateral agreement. 8: Reserved for future use by ISO. 9: Test. Digit 2: Authorization processing: 0: Transactions are authorized following the normal rules. 1: Reserved for future use by ISO. 2: Transactions are authorized by issuer and should be online. 3: Reserved for future use by ISO. 4: Transactions are authorized by issuer and should be online, except under bilateral agreement. 5: Reserved for future use by ISO. 6: Reserved for future use by ISO. 7: Reserved for future use by ISO. 8: Reserved for future use by ISO. 9: Reserved for future use by ISO. Digit 3 (least significant): Range of services and PIN requirements: 0: No restrictions and PIN required. 1: No restrictions. 2: Goods and services only (no cash). 3: ATM only and PIN required. 4: Cash only. 5: Goods and services only (no cash) and PIN required. 6: No restrictions and require PIN when feasible. 7: Goods and services only (no cash) and require PIN when feasible. 8: Reserved for future use by ISO. 9: Reserved for future use by ISO. If this field in not used a FS will be in place. Required by MasterCard and VISA. PVV: Offset or PVV (PIN Verification Value). 5 digits: Required by MasterCard and VISA. DD: Discretionary Data. Rest of characters: Reserved for proprietary use of card issuer. ES: End Sentinel. 1 character: ?. LRC: Longitude Redundancy Check. 1 character. Track 2 (ABA) Up to 40 BCD 5-bit (including parity) characters (numeric) including SS, ES and LRC. Read only. It comprises the following fields (in this order): SS: Start Sentinel. 1 character: ;. PAN: Primary Account Number. Up to 19 digits: In accordance with the account numbering scheme in ISO 7812. It consists of the following parts: IIN: Issuer Identification Number. Up to 6 digits: It consists of the following parts: MII: Major Industry Identifier. Up to 2 digits: 0: Reserved for future use by ISO/TC 68. 00: Institutions other than card issuers. 1: Airlines. 2: Airlines and other future assignments. 3: Travel and entertainment. 4: Banking/financial. 5: Banking/financial. 59: Financial institutions not registered by ISO. 6: Merchandising and banking. 7: Petroleum. 8: Telecommunications and other future assignments. 89: Telecommunications administrations and private operating agencies. 9: Reserved for national use. II: Issuer Identifier. Up to 5 digits: Identifies the issuer. In some cases the first digit indicates the length of the IIN or the responsibility of its assignment outside of ISO. If MII = 9 then the first 3 digits should be the country code (CC). IAI: Individual Account Identification. Up to 12 digits: Assigned by the card issuing institution. CD: Check Digit. 1 digit: Calculated using the Luhn formula for computing modulus 10 "double-add-double" check digit: Double the value of alternate (odd) digits starting by the least significant (first right hand) digit. Then add the individual digits of doubled (odd) numbers and even digits of the original number. If the value ends in 0 then the check digit is 0. Otherwise subtract the value from the next higher number ending in 0 (tens complement of the unit digit). The result is the check digit. Example: If the account number without check digit is 1234 5678 9012 344 then (8) + 4 + (6) + 2 + (2) + 0 + (1 + 8) + 8 + (1 + 4) + 6 + (1 + 0) + 4 + (6) + 2 + (2) = 65, therefore the check digit is 70 - 65 = 5 and so the complete account number is 1234 5678 9012 3445. MasterCard PAN is variable up to 16 digits and VISA is 13 or 16 digits including check digit. FS: Field Separator. 1 character: =. CC: Country Code. 3 digits: Only if PAN starts with 59 (MasterCard). Country codes are defined in ISO 3166: 724 for Spain, 840 for USA, etc. ED: Expiry Date. 4 digits: YYMM. If this field is not used a FS will be in place. Required by MasterCard and VISA. SC: Service Code. 3 digits: Digit 1 (most significant): Interchange and technology: 0: Reserved for future use by ISO. 1: Available for international interchange. 2: Available for international interchange and with integrated circuit, which should be used for the financial transaction when feasible. 3: Reserved for future use by ISO. 4: Reserved for future use by ISO. 5: Available for national interchange only, except under bilateral agreement. 6: Available for national interchange only, except under bilateral agreement, and with integrated circuit, which should be used for the financial transaction when feasible. 7: Not available for general interchange, except under bilateral agreement. 8: Reserved for future use by ISO. 9: Test. Digit 2: Authorization processing: 0: Transactions are authorized following the normal rules. 1: Reserved for future use by ISO. 2: Transactions are authorized by issuer and should be online. 3: Reserved for future use by ISO. 4: Transactions are authorized by issuer and should be online, except under bilateral agreement. 5: Reserved for future use by ISO. 6: Reserved for future use by ISO. 7: Reserved for future use by ISO. 8: Reserved for future use by ISO. 9: Reserved for future use by ISO. Digit 3 (least significant): Range of services and PIN requirements: 0: No restrictions and PIN required. 1: No restrictions. 2: Goods and services only (no cash). 3: ATM only and PIN required. 4: Cash only. 5: Goods and services only (no cash) and PIN required. 6: No restrictions and require PIN when feasible. 7: Goods and services only (no cash) and require PIN when feasible. 8: Reserved for future use by ISO. 9: Reserved for future use by ISO. If this field in not used a FS will be in place. Required by MasterCard and VISA. PVV: Offset or PVV (PIN Verification Value). 5 digits: Required by MasterCard and VISA. DD: Discretionary Data. Rest of characters: Reserved for proprietary use of card issuer. ES: End Sentinel. 1 character: ?. LRC: Longitude Redundancy Check. 1 character. Track 3 (THRIFT-TTS) Up to 107 BCD 5-bit (including parity) characters (numeric) including SS, ES and LRC. Read and write. It comprises the following fields (in this order): SS: Start Sentinel. 1 character: ;. FC: Format Code. 2 digits: 00: Not valid for international interchange. 01-02: Bank/financial. These are the formats described here. 03-19: Reserved for future use by ISO/TC 68. 20-89: Reserved for future use by ISO/TC 95 SC 17. 90-99: Reserved for proprietary use of card issuer, but not for international interchange. PAN: Primary Account Number. Up to 19 digits: In accordance with the account numbering scheme in ISO 7812. It consists of the following parts: IIN: Issuer Identification Number. Up to 6 digits: It consists of the following parts: MII: Major Industry Identifier. Up to 2 digits: 0: Reserved for future use by ISO/TC 68. 00: Institutions other than card issuers. 1: Airlines. 2: Airlines and other future assignments. 3: Travel and entertainment. 4: Banking/financial. 5: Banking/financial. 59: Financial institutions not registered by ISO. 6: Merchandising and banking. 7: Petroleum. 8: Telecommunications and other future assignments. 89: Telecommunications administrations and private operating agencies. 9: Reserved for national use. II: Issuer Identifier. Up to 5 digits: Identifies the issuer. In some cases the first digit indicates the length of the IIN or the responsibility of its assignment outside of ISO. If MII = 9 then the first 3 digits should be the country code (CC). IAI: Individual Account Identification. Up to 12 digits: Assigned by the card issuing institution. CD: Check Digit. 1 digit: Calculated using the Luhn formula for computing modulus 10 "double-add-double" check digit: Double the value of alternate (odd) digits starting by the least significant (first right hand) digit. Then add the individual digits of doubled (odd) numbers and even digits of the original number. If the value ends in 0 then the check digit is 0. Otherwise subtract the value from the next higher number ending in 0 (tens complement of the unit digit). The result is the check digit. Example: If the account number without check digit is 1234 5678 9012 344 then (8) + 4 + (6) + 2 + (2) + 0 + (1 + 8) + 8 + (1 + 4) + 6 + (1 + 0) + 4 + (6) + 2 + (2) = 65, therefore the check digit is 70 - 65 = 5 and so the complete account number is 1234 5678 9012 3445. MasterCard PAN is variable up to 16 digits and VISA is 13 or 16 digits, including check digit. If track 3 is to be used together with track 2 then PAN is an optional field. FS: Field Separator. 1 character: =. CC: Country Code. 3 digits: Country codes are defined in ISO 3166: 724 for Spain, 840 for USA, etc. If this field in not used a FS will be in place. CuC: Currency Code. 3 digits: Currency codes are defined in ISO-4217:724 for Spanish peseta, 840 for US dollar, etc. If 3 zeros (000) are written in this field it means card not valid for international interchange. CE: Currency Exponent. 1 digit: 0-5: Power of ten by which multiply the currency amount fields (AA and AR) to get their actual values in the currency of the CuC field. AA: Amount Authorized per cycle. 4 digits: Maximum amount of money permitted in one cycle. If 4 zeros (0000) are written in this field it means card not valid for charge operations (no debit). AR: Amount Remaining this cycle. 4 digits: Maximum amount of money permitted in this cycle. This field is dynamic, it is initialized with the value of the AA field the first time the card is used in a new cycle. Then it is modified accordingly. CB: Cycle Begin (Validity Date). 4 digits: Date in which actual cycle began. The format is YDDD where Y stands for the least significant digit of the year and DDD is the day of the year (001 to 366). The field must be updated each time a new cycle begin. Alternatively this field may indicate the date from which the card is valid. CL: Cycle Length. 2 digits: 00: Infinite, AR should be decremented but never reset. 01-79: Number of days. 80: Cycle begin each 7 days. 81: Cycle begin each 14 days. 82: Cycle begins each 1st and 15th days of every month. 83: Cycle begins the day of the month specified in CB of every month. 84: Cycle begins the day of the month specified in CB of every third month. 85: Cycle begins the day of the month specified in CB of every sixth month. 86: Cycle begins the day of the year specified in CB of every year. 87-89: Reserved for future use by ISO/TC 68. 90-99: Reserved for proprietary use of card issuer, but not for international interchange. This field represents the duration of the cycle for which the AA limit holds. RC: Retry Count. 1 digit: Number of remaining PIN trials. It is initialized to 3 and reduced by one unit after every wrong PIN entered. It is reset to 3 after a successful PIN introduction. When this field reaches 0 the card is invalid for any interchange purpose. PINCP: PIN Control Parameters (PINPARM). 6 digits: If FC = 01 the two first digits represent the algorithm used to calculate PIN, where 00-09 mean private algorithm, 10-19 mean DEA and values 20 to 99 are reserved for future use by ISO/TC 68. Next 4 digits are PIN offset, a complementary value of PIN so customers can change their PIN, or PVV. If FC = 02 the first digit represents the algorithm used to calculate PIN, where 0 means private algorithm, 1 means DEA and values 2 to 9 are reserved for future use by ISO/TC 68. The second digit represents a key for the algorithm. Next 4 digits are PIN offset, a complementary value of PIN so customers can change their PIN, or PVV. If this field is not used a FS will be in place. IC: Interchange Control. 1 digit: 0: No restriction. 1: Not available for international interchange. 2-8: Limited interchange, only local use and under agreement. 9: Limited interchange, recommended for test cards. PANSR: PAN Service Restriction. 2 digits: The first digit defines the type of account: 0: Associated account number not encoded on track. 1: Savings account. 2: Current or checking account. 3: Credit card account. 4: Generic or universal account. 5: Interest-bearing current or checking account. 6-8: Reserved for future use by ISO/TC 68. 9: Reserved for card issuer's internal use, not for interchange. The second digit defines the service restrictions: 0: No restrictions. 1: No cash dispense. 2: No point of sale (POS) transaction. 3: No cash dispense and no POS transaction. 4: Authorization required. 5-7: Reserved for future use by ISO/TC 68. 8-9: Reserved for card issuer's internal use, only local use and under agreement. FSANSR: FSAN Service Restriction. 2 digits: Same values and meaning as for PANSR field. SSANSR: SSAN Service Restriction. 2 digits: Same values and meaning as for PANSR field. ED: Expiry Date. 4 digits: YYMM. If this field is not used a FS will be in place. CSN: Card Sequence Number. 1 digit: Allows to distinguish among different cards with the same PAN. It is incremented by one unit in any additional card. If FC = 02 and a FS is in place it means that field ACSN is present. CScN: Card Security Number. 9 digits: The first digit represents the algorithm used to calculate a verification value to validate the information on the magnetic track against the embossed characters, where 0-4 are available for national use, 5-8 are available for international security methods given by ISO/TC 68, and 9 is reserved for private use. Next 8 digits are the verification value. If this field in not used a FS will be in place. FSAN: First Subsidiary Account Number. Variable number of digits: This is an optional field. FS: Field Separator. 1 character: =. SSAN: Second Subsidiary Account Number. Variable number of digits: This is an optional field. FS: Field Separator. 1 character: =. RM: Relay Marker. 1 digit: 0: Include AD and DD fields in transactions messages. 1: Do not include AD field in transactions messages. 2: Do not include DD field in transactions messages. 3-9: Invalid. CCD: Crypto Check Digits. 6 digits: A validation value which permits integrity verification of the magnetic stripe content. If this field in not used a FS will be in place. AD: Additional Data. Rest of characters: Optional field reserved for proprietary use of card issuer if FC = 01. If FC = 02 the following subfields are required: TD: Transaction Date. 4 digits: Date of last cash dispense. The format is YDDD where Y stands for the least significant digit of the year and DDD is the day of the year (001 to 366). If this field in not used a FS will be in place. AVV: Additional Verification Value(s). 8 digits: Validation of the PIN or two additional PINs corresponding to different keys of the same algorithm. It can be an eight digit value, two four digit numbers or, in connection with the last four digits of the PINCP field, two six digit numbers. If this field in not used a FS will be in place. ACSN: Alternative Card Sequence Number. 3 digits: Same purpose as CSN field but it allows for a maximum of 1000 different cards instead of 10. If this field in not used a FS will be in place. INIC: International Network Identification Code. 3 digits: Code for identification of an international group of issuers, when the IIN can not be used. It is equivalent to the term Network International Identifier used in ISO 8583. If this field in not used a FS will be in place. DD: Discretionary Data. Rest of characters: Optional field reserved for proprietary use of card issuer. ES: End Sentinel. 1 character: ?. LRC: Longitude Redundancy Check. 1 character. */ /* Caja de Madrid ATM card (VISA logo): Embossing: Card holder : L. PADILLA Card number*: 1234 5678 9012 3445 Expiration : 01/99 *The card number (PAN) uses, as most of the credit and debit cards, the Luhn Check Digit Algorithm. Valid numbers must comply with the following rule. If the card number has an even number of digits, then the adding up of the even numbered digits plus the odd numbered digits doubled (minus 9 if the doubled digit is greater than 9) has to be multiple of 10. If the card number has an odd number of digits, then the same applies but doubling even (instead of odd) numbered digits. Using the example above: 2 x 1 + 2 + 2 x 3 + 4 + 2 x 5 - 9 + 6 + 2 x 7 - 9 + 8 + 2 x 9 - 9 + 0 + 2 x 1 + 2 + 2 x 3 + 4 + 2 x 4 + 5 = 70 and therefore the number in the example is a valid number for a credit or debit card. Actually is the other way round, the last digit of the card number is calculated so that the whole number complies with Luhn Check, therefore the last digit is called check digit. Track 1: %B1234567890123445^PADILLA/L. ^99011200000000000000**XXX******?* ^^^ ^^ ^^ ^ ^ ^ ^^ |||_ Card number ||_ Card holder || | | |_ CVV** ||_ LRC ||_ Format code |_ Field separator || | | |_ End sentinel |_ Start sentinel Field separator _|| | |_ Discretionary data Expiration _| |_ Service code Track 2: ;1234567890123445=99011200XXXX00000000?* ^^ ^^ ^ ^ ^^ ||_ Card number || | |_ Encrypted||_ LRC |_ Start sentinel|| | PIN*** |_ End sentinel || |_ Service code Field separator _||_ Expiration Track 3: ;011234567890123445=724724100000000000030300XXXX040400099010=************************==1=0000000000000000?* ^^ ^ ^^ ^ ^ ^ ^ ^ ^^ ^ ^ ^^ ^^^^^ ^^ || | || | |_ Currency | | | || | | ||_ First subsidiary |||||_ Additional || || | || | exponent | | | || | | | account number (FSAN)|||| data || || |_ Card number || |_ Currency | | | || | | |_ Field separator ||||_ Field ||_ LRC ||_ Format code || (Peseta) | | | || | |_ Expiration ||| separator |_ End sentinel |_ Start sentinel ||_ Country (Spain) | | | || |_ FSAN service restriction |||_ Relay marker |_ Field separator | | | ||_ PAN service restriction ||_ Field separator Cycle length _| | | |_ Interchange control |_ Field separator Retry count _| |_ Encrypted PIN*** **The CVV (Card Verification Value) is a cryptographic signature of the card number, the expiration date and the service code to verify the integrity of these data in the track. It is calculated with an algorithm similar to that of PIN encryption. ***The PIN (Personal Identification Number) is basically encrypted as follows. The card number is taken as an hexadecimal number and is encrypted with the DES algorithm using a secret key, which is called the "PIN key". The first four digits are decimalized (i.e., A = 0, B = 1, ...) and are called the "natural PIN". An offset is added (without carry) to the natural PIN in order to obtain the customer PIN. The customer PIN may be changed but the natural PIN cannot. The offset is what is written in track 3 and I called the "encrypted PIN". Here you have an example: Card number: 1234567890123445hex input for DES. PIN key: 0123456789ABCDEFhex key for DES. Encrypted card number: 9A466AD30DFE0381hex output from DES. Natural PIN: 9046. Offset: 2298 (this number is written on track 3). Customer PIN: 1234. See also the original VISA scheme and a discussion on other systems. See the two much more detailed and complete documents linked in my main page for magnetic stripe cards. */ /* Caja Madrid VISA or VISA Electron card: Embossing or printing: Card holder: L. PADILLA Card number: 1234 5678 9012 3445 Expiration : 01/99 Track 1: %B1234567890123445^PADILLA/L. ^99011X100000*000000000XXX000000?* ^^^ ^^ ^^ ^ ^ ^ ^^ |||_ Card number ||_ Card holder || | | |_ CVV ||_ LRC ||_ Format code |_ Field separator || | | |_ End sentinel |_ Start sentinel Field separator _|| | |_ Discretionary data Expiration _| |_ Service code: 101 for VISA and 121 for VISA Electron Track 2: ;1234567890123445=99011X10XXXXXXX00000?* ^^ ^^ ^ ^ ^ ^^ ||_ Card number || | | |_ CVV ||_ LRC |_ Start sentinel|| | | |_ End sentinel Field separator _|| | |_ Encrypted PIN (except if duplicate card) Expiration _| |_ Service code: 101 for VISA and 121 for VISA Electron Track 3: ;011234567890123445=724724000000000****00300XXXX020200099010=********************==1=100000000000000000**?* ^^ ^ ^^ ^ ^ ^ ^ ^^ ^ ^ ^^ ^^^^^ ^^ || | || |_ Currency | | | || | | ||_ FSAN ||||| || || |_ Card number || (Peseta) | | | || | | |_ Field separator |||||_ Additional data ||_ LRC ||_ Format code ||_ Country | | | || | |_ Expiration ||||_ Field separator |_ End sentinel |_ Start sentinel | (Spain) | | | || |_ FSAN service restriction |||_ Relay marker |_ Field | | | ||_ PAN service restriction ||_ Field separator separator | | | |_ Interchange control |_ Field separator Validity date _| | |_ Encrypted PIN (except if duplicate card) (except if duplicate card) |_ Retry count */ /* Uno-e VISA Electron card: Printing: Card holder: L. PADILLA Card number: 1234 5678 9012 3445 Expiration : 01/99 Track 1: %B1234567890123445^PADILLA/L. ^99011211XXXX*000000**0XXX0**000?* ^^^ ^^ ^^ ^ ^^ ^ ^ ^^ |||_ Card number ||_ Card holder || | || |_ Discr. |_ CVV ||_ LRC ||_ Format code |_ Field separator || | || data |_ End sentinel |_ Start sentinel Field separator _|| | ||_ PVV* Expiration _| | |_ PVV key indicator |_ Service code Track 2: ;1234567890123445=99011211XXXXXXX00***?* ^^ ^^ ^ ^^ ^ ^^ ||_ Card number || | || |_ CVV ||_ LRC |_ Start sentinel|| | ||_ PVV* |_ End sentinel Field separator _|| | |_ PVV key indicator Expiration _| |_ Service code Track 3: ;011234567890123445=000978100000000****8330*0000920000099010=************************==1=0000000*00000000?* ^^ ^ ^ ^ ^ ^ ^ ^ ^^ ^ ^^ ^^^^^ ^^ || | | | |_ Curr. | | |_Retry|| | ||_ FSAN |||||_ Additional ||_ LRC || |_ Card number | Curr. expon.| | count|| | |_ Field separator |||| data |_ End sentinel ||_ Format code | (Euro) | | || |_ Expiration ||||_ Field separator |_ Start sentinel |_ Field | |_ Cycle ||_ PAN service restriction |||_ Relay marker separator | length|_ Interchange control ||_ Field separator Validity date _| |_ Field separator *The PVV (PIN Verification Value) is another way of PIN encryption. It is calculated with an algorithm similar to that described above. */ /* Caja Bilbao Vizcaya ATM card (MasterCard logo): Embossing: Card holder: L. PADILLA Card number: 1234 5678 9012 3445 Expiration : 01/99 Track 1: %B1234567890123445^PADILLA/L. ^990110100000000XXX****XXX******?* ^^^ ^^ ^^ ^ ^ ^ ^ ^^ |||_ Card number ||_ Card holder || | | | |_ CVV ||_ LRC ||_ Format code |_ Field separator || | | |_ Number |_ End sentinel |_ Start sentinel Field separator _|| | |_ Discretionary data Expiration _| |_ Service code Track 2: ;1234567890123445=990110100000XXXXXX00?* ^^ ^^ ^ ^ ^ ^^ ||_ Card number || | CVV _| | ||_ LRC |_ Start sentinel|| | Number _| |_ End sentinel || |_ Service code Field separator _||_ Expiration Track 3: ;011234567890123445=7247241000000000000001000000040400099010=************************==0=0002000000******?* ^^ ^ ^^ ^ ^ ^ ^^ ^ ^ ^^ ^^^^^ ^^ || |_ Card number || | |_ Currency | || | | ||_ First subsidiary |||||_ Additional || ||_ Format code || | exponent | || | | | account number (FSAN)|||| data || |_ Start sentinel || |_ Currency | || | | |_ Field separator ||||_ Field ||_ LRC Field separator _|| (Peseta) | || | |_ Expiration ||| separator |_ End sentinel |_ Country (Spain) | || |_ FSAN service restriction |||_ Relay marker Retry count _| ||_ PAN service restriction ||_ Field separator |_ Interchange control |_ Field separator */ /* Sanitas (private sanitary coverage) card: Embossing: Card holder: L. PADILLA Card number: 999999 8888 777 666 5 Validity : 01/98 - 02/99 Track 1: %BXXXXXX99999900000888877=L. PADILLA =666599029801?* ^^^ ^ ^ ^^ ^^ ^ ^^ ||| | | ||_ Card holder || | ||_ LRC ||| | | |_ Field separator || | |_ End sentinel ||| | |_ Card number(2) || |_ Validity ||| |_ Card number(1) Field separator _||_ Card number(3) |||_ Number ||_ Format code |_ Start sentinel Track 2: ;XXXXXX999999000008888777=666990259801?* ^^ ^ ^ ^^ ^ ^^ ^^ || | | || | || ||_ LRC || | | || | || |_ End sentinel || | | || | ||_ Validity(2) || | | || | |_ Card number(4) || | | || |_ Validity(1) || | | ||_ Card number(3) || | | |_ Field separator || | |_ Card number(2) || |_ Card number(1) ||_ Number |_ Start sentinel Track 3: Empty (all zeros) */ /* Social security card: Embossing: Card holder: L. PADILLA Card number: 999999 PDVS123456789012 Expiration : 03/99 Track 1: %B999999^PDVS123456789012^PADILLA L. ^0X0000399 ?* ^^^ ^^^^^^ ^^ ^ ^ ^ ^^ ||| ||||||_ Card ||_ Card holder | | |_ Expiration ||_ LRC ||| ||||| number(2)|_ Field separator (formerly =)| | |_ End sentinel ||| |||||_ 3rd letter from 2nd surname | |_ = 1 -> Non pensioner ||| ||||_ 1st letter from 2nd surname | = 2 -> Pensioner ||| |||_ 3rd letter from 1st surname |_ Field separator (formerly =) ||| ||_ 1st letter from 1st surname ||| |_ Field separator (formerly =) |||_ Card number(1) ||_ Format code |_ Start sentinel Track 2: ;999999554749123456789012=00X990300000?* ^^ ^ ^ ^ ^ ^^ ^^ || | | |_ Card | || ||_ LRC || | | number(2)| || |_ End sentinel || | | | ||_ Expiration || | | | |_ See above || | | |_ Field separator || | |_ = 30 x (#V - 30) + #S - 22, where #X means decimal value of character X in the ALPHA format (#A = 33, #B = 34, ...) || |_ = 30 x (#P - 30) + #D - 22, where P, D, V and S come from the card number (see above) ||_ Card number(1) |_ Start sentinel Track 3: Empty (all zeros) or not present */ /* Vitalicio-Salud (private sanitary coverage) card: Embossing: Card holder: L. PADILLA Card number: 0123456789 Validity : 04/99 - 12/99 Track 1: %0123456789L. PADILLA ?* ^^ ^ ^^ || |_ Card holder ||_ LRC ||_ Card number |_ End sentinel |_ Start sentinel Track 2: ;0123456789?* ^^ ^^ ||_ Card ||_ LRC | number |_ End sentinel |_ Start sentinel Track 3: Empty (all zeros) */ /* Ciudad de las Artes y las Ciencias pass ticket: Printing: Date: 16/12/00 20:00 Number: 010100381781 Track 1: Not present Track 2: ;012001120100381781?* ^ ^ ^ ^^ | |_ Date?| ||_ LRC | | |_ End sentinel | |_ Partial number |_ Start sentinel Track 3: Not present */ /* Graphispag93 exhibitor ID card: Printing: Card holder: RUEDA, J Track 1: %RUEDA, J?* ^^ ^^ || ||_ LRC || |_ End sentinel ||_ Card holder |_ Start sentinel Track 2: Not present Track 3: Not present */ /* Carrefour gas station discount ticket: Printing: None Track 1: Not present Track 2: ;9999989999999999=01015990000000000000?* ^ ^ ^^ |_ Start sentinel|_ Field separator ||_ LRC |_ End sentinel Track 3: Not present */ /* Madrid public parking ticket: Printing: Date: 14:31 06-02-98 Track 1: Not present Track 2: ;11431060298?* ^ ^ ^^ | |_ Date ||_ LRC |_ Start |_ End sentinel sentinel Track 3: Not present */ /* Parque Ferial Juan Carlos I parking ticket: Printing: Date: 27/02/2003 13:32:05 String: 01044 9 35397 P01 Track 1: Not present Track 2: ;304601=0534177205044000000000000100?* ^ ^ ********^ ^^ | | |_ Digits 3 to 5 || | | of string || | |_ Field separator ||_ LRC |_ Start sentinel |_ End sentinel Track 3: Not present */ /* Val Thorens parking card: Printing: Number : 11069 Parking : P0 Validity: 02/01/96 - 06/01/96 Track 1: Not present Track 2: ;>4155594>4713000110=11106909912310000?* ^ ^ ^ ^^ |_ Start sentinel | |_ Number ||_ LRC |_ Field separator|_ End sentinel Track 3: Not present */ /* Read more at: http://www.epanorama.net/documents/smartcard/magcard.html */ #define MMD1100_DEBUG(fmt, args ...) printf("DEBUG:%s(%d): " fmt, __FUNCTION__,__LINE__, ## args) #define MMD1100_ERROR(fmt, args ...) printf("ERROR:%s(%d): " fmt, __FUNCTION__,__LINE__, ## args) #define MMD1100_INFO(fmt, args ...) printf("INFOs:%s(%d): " fmt, __FUNCTION__,__LINE__, ## args) #define MMD1100_BYTE_SIZE_OF_BUFFER_FOR_SERIAL 512 #define MMD1100_BYTE_SIZE_OF_BUFFER_FOR_PACKET 1024 #define MMD1100_PACKET_POSITION_STX 0 #define MMD1100_PACKET_POSITION_CLASS 1 #define MMD1100_PACKET_POSITION_FUNCTION 2 #define MMD1100_PACKET_POSITION_LENGTH 3 #define MMD1100_PACKET_POSITION_STATUS 4 #define MMD1100_PACKET_POSITION_DATA_START 5 #define MMD1100_PACKET_POSITION_TRACK_ID 2 #define MMD1100_DEFINE_STX 0x02 #define MMD1100_DEFINE_ETX 0x03 #define MMD1100_DEFINE_TRACK_DATA_MODE_2 0x33 //#define MMD1100_DEFINE_TRACK_DATA_MODE 0x35 //#define MMD1100_DEFINE_TRACK_DATA_MODE 0x37 #define MMD1100_DEFINE_TRACK_ID_1 0x25 //#define MMD1100_DEFINE_TRACK_ID_2 0x3F //#define MMD1100_DEFINE_TRACK_ID_3 0x5E #define MMD1100_DEFINE_TRACK_ID_2 0x5E /*Bug Fix*/ #define MMD1100_DEFINE_TRACK_ID_3 0x3F /*Bug Fix*/ #define MMD1100_DEFINE_ACK 0x06 #define MMD1100_DEFINE_NAK 0x15 #define MMD1100_COMMAND_CLASS_GET_VERSION 0x10 #define MMD1100_COMMAND_CLASS_LOAD_USER_PARAMETERS 0x12 #define MMD1100_COMMAND_CLASS_UART_CALIBRATION 0x13 #define MMD1100_COMMAND_CLASS_OTP_WRITE 0x15 #define MMD1100_COMMAND_CLASS_GET_STATUS 0x16 #define MMD1100_COMMAND_CLASS_READ_DATA_RETRY 0x17 #define MMD1100_COMMAND_CLASS_SOFTWARE_RESET 0x18 #define MMD1100_COMMAND_FUNCTION_GET_VERSION 0x31 #define MMD1100_COMMAND_FUNCTION_LOAD_USER_PARAMETERS 0x31 #define MMD1100_COMMAND_FUNCTION_UART_CALIBRATION 0x31 #define MMD1100_COMMAND_FUNCTION_OTP_WRITE 0x33 #define MMD1100_COMMAND_FUNCTION_GET_STATUS 0x31 #define MMD1100_COMMAND_FUNCTION_READ_DATA_RETRY 0x31 #define MMD1100_COMMAND_FUNCTION_SOFTWARE_RESET 0x31 #define MMD1100_COMMAND_LENGTH_GET_VERSION 0x00 #define MMD1100_COMMAND_LENGTH_LOAD_USER_PARAMETERS 0x04 #define MMD1100_COMMAND_LENGTH_UART_CALIBRATION 0x05 #define MMD1100_COMMAND_LENGTH_OTP_WRITE 0x00 #define MMD1100_COMMAND_LENGTH_GET_STATUS 0x00 #define MMD1100_COMMAND_LENGTH_READ_DATA_RETRY 0x00 #define MMD1100_COMMAND_LENGTH_SOFTWARE_RESET 0x00 #define MMD1100_RESPONSE_ACK_LENGTH_GET_VERSION 0x10 #define MMD1100_RESPONSE_ACK_LENGTH_LOAD_USER_PARAMETERS 0x04 #define MMD1100_RESPONSE_ACK_LENGTH_UART_CALIBRATION 0x01 #define MMD1100_RESPONSE_ACK_LENGTH_OTP_WRITE 0x01 #define MMD1100_RESPONSE_ACK_LENGTH_GET_STATUS 0x06 #define MMD1100_RESPONSE_NAK_LENGTH 0x01 typedef enum { MMD1100_PACKET_CHECK_STATE_STX = 0, MMD1100_PACKET_CHECK_STATE_MODE_CLASS, MMD1100_PACKET_CHECK_STATE_TRACK_ID, MMD1100_PACKET_CHECK_STATE_TRACK_LENGTH, MMD1100_PACKET_CHECK_STATE_TRACK_STATE, MMD1100_PACKET_CHECK_STATE_TRACK_DATA, MMD1100_PACKET_CHECK_STATE_FUNCTION, MMD1100_PACKET_CHECK_STATE_LENGTH, MMD1100_PACKET_CHECK_STATE_STATUS, MMD1100_PACKET_CHECK_STATE_DATA, MMD1100_PACKET_CHECK_STATE_ETX, MMD1100_PACKET_CHECK_STATE_BCC } MMD1100PacketCheckState_t; typedef struct { /* Device File Descriptor */ int Device; struct termios BackupTerminalInfo; /* Serial Receive Buffer */ uint8_t pBufferForSerial[MMD1100_BYTE_SIZE_OF_BUFFER_FOR_SERIAL]; uint32_t HeadPointerForSerial; uint32_t TailPointerForSerial; /* Received Packet Buffer */ uint8_t pBufferForPacket[MMD1100_BYTE_SIZE_OF_BUFFER_FOR_PACKET]; uint32_t PacketOffset; MMD1100PacketCheckState_t PacketCheckState; uint32_t DataLengthInPacket; uint32_t PacketLength; } MMD1100context_t; static MMD1100context_t MMD1100context = { /* Device File Descriptor */ .Device = -1, /* struct termios BackupTerminalInfo*/ /* Serial Receive Buffer */ .pBufferForSerial = {0}, .HeadPointerForSerial = 0, .TailPointerForSerial = 0, /* Received Packet Buffer */ .pBufferForPacket = {0}, .PacketOffset = 0, .PacketCheckState = MMD1100_PACKET_CHECK_STATE_STX, .DataLengthInPacket = 0, .PacketLength = 0 }; /* 5 bit (Data + Parity) ANSI/ISO BCD Data Format convert to 8 bit ASCII */ static const uint8_t pDataFormatTableForISO_BCD[] = { /* 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 */ ' ', '1', '2', ' ', '4', ' ', ' ', '7', /* 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F */ '8', ' ', ' ', ';', ' ', '=', '>', ' ', /* 0x10 0x11 0x12 0x13 0x14 0x15 0x16 0x17 */ '0', ' ', ' ', '3', ' ', '5', '6', ' ', /* 0x18 0x19 0x1A 0x1B 0x1C 0x1D 0x1E 0x1F */ ' ', '9', ':', ' ', '<', ' ', ' ', '?' }; /* 7 bit (Data + Parity) ANSI/ISO ALPHA Data Format convert to 8 bit ASCII */ static const uint8_t pDataFormatTableForISO_ALPHA[] = { /* 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 */ 0x00, '!', '"', 0x00, '$', 0x00, 0x00, '\'', /* 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F */ '(', 0x00, 0x00, '+', 0x00, '-', '.', 0x00, /* 0x10 0x11 0x12 0x13 0x14 0x15 0x16 0x17 */ '0', 0x00, 0x00, '3', 0x00, '5', '6', 0x00, /* 0x18 0x19 0x1A 0x1B 0x1C 0x1D 0x1E 0x1F */ 0x00, '9', ':', 0x00, '<', 0x00, 0x00, '?', /* 0x20 0x21 0x22 0x23 0x24 0x25 0x26 0x27 */ '@', 0x00, 0x00, 'C', 0x00, 'E', 'F', 0x00, /* 0x28 0x29 0x2A 0x2B 0x2C 0x2D 0x2E 0x2F */ 0x00, 'I', 'J', 0x00, 'L', 0x00, 0x00, 'O', /* 0x30 0x31 0x32 0x33 0x34 0x35 0x36 0x37 */ 0x00, 'Q', 'R', 0x00, 'T', 0x00, 0x00, 'W', /* 0x38 0x39 0x3A 0x3B 0x3C 0x3D 0x3E 0x3F */ 'X', 0x00, 0x00, '[', 0x00, 'D', '^', 0x00, /* 0x40 0x41 0x42 0x43 0x44 0x45 0x46 0x47 */ ' ', 0x00, 0x00, '#', 0x00, '%', '&', 0x00, /* 0x48 0x49 0x4A 0x4B 0x4C 0x4D 0x4E 0x4F */ 0x00, ')', '*', 0x00, ',', 0x00, 0x00, '/', /* 0x50 0x51 0x52 0x53 0x54 0x55 0x56 0x57 */ 0x00, '1', '2', 0x00, '4', 0x00, 0x00, '7', /* 0x58 0x59 0x5A 0x5B 0x5C 0x5D 0x5E 0x5F */ '8', 0x00, 0x00, 0x00, 0x00, '=', '>', 0x00, /* 0x60 0x61 0x62 0x63 0x64 0x65 0x66 0x67 */ 0x00, 'A', 'B', 0x00, 'D', 0x00, 0x00, 'G', /* 0x68 0x69 0x6A 0x6B 0x6C 0x6D 0x6E 0x6F */ 'H', 0x00, 0x00, 'K', 0x00, 'M', 'N', 0x00, /* 0x70 0x71 0x72 0x73 0x74 0x75 0x76 0x77 */ 'P', 0x00, 0x00, 'S', 0x00, 'U', 'V', 0x00, /* 0x78 0x79 0x7A 0x7B 0x7C 0x7D 0x7E 0x7F */ 0x00, 'Y', 'Z', 0x00, '\\', 0x00, 0x00, '_' }; static int mmd100_convert_rawdata_to_bcd( uint8_t *pTarget, uint8_t *pSource, uint32_t ByteLengthOfSource, int StartFromLast) { uint32_t TotalBitInMemory; uint32_t BitPositionInMemory; uint32_t BitPositionInBCD; int ByteLengthOfConvertedBCD; int FirstHighBitPosition; int IsERROR; uint8_t BCD; if ( ( pTarget == (uint8_t *)0) || ( pSource == (uint8_t *)0 ) ) { return -1; } FirstHighBitPosition = -1; TotalBitInMemory = ByteLengthOfSource * 8; BCD = 0; IsERROR = 0; for ( BitPositionInMemory = 0; BitPositionInMemory < TotalBitInMemory; BitPositionInMemory++) { BCD >>= 1; if ( memory_pick_up_bit( (void *)pSource, TotalBitInMemory, BitPositionInMemory, StartFromLast) == 1 ) { if ( FirstHighBitPosition == -1 ) { FirstHighBitPosition = BitPositionInMemory; } BCD |= 0x10; } else { BCD &= 0xEF; } BCD &= 0x1F; if ( BCD == 0x0B/* ';' = SS */ ) { if (FirstHighBitPosition + 4 != BitPositionInMemory) { IsERROR = 1; /* SS error */ } /* get sync */ break; } } BitPositionInMemory++; /* skip parity bit for sync char */ if(BitPositionInMemory >= TotalBitInMemory) { return -2; /* Not find SS */ } ByteLengthOfConvertedBCD = 0; pTarget[ByteLengthOfConvertedBCD++] = pDataFormatTableForISO_BCD[0x0B]; /* ';' = SS */ /* gather data */ BitPositionInBCD=0; BCD = 0; for ( ; BitPositionInMemory < TotalBitInMemory; BitPositionInMemory++) { BCD |= (memory_pick_up_bit( (void *)pSource, TotalBitInMemory, BitPositionInMemory, StartFromLast) << BitPositionInBCD); BitPositionInBCD ++; if ( BitPositionInBCD == 5 ) { pTarget[ByteLengthOfConvertedBCD++] = pDataFormatTableForISO_BCD[BCD]; BitPositionInBCD=0; BCD = 0; } } return ByteLengthOfConvertedBCD; } static void mmd1100_packet_flush_buffer(void); /* open serial device for cummunication with MMD1100 return: -1: Fail to operation -2: Input Paramters Error 0: OK */ int mmd1100_open(int *pDeviceFileDescriptor) { struct termios TerminalInfo; if ( pDeviceFileDescriptor == (int *)0 ) { return -2; } if ( MMD1100context.Device < 0 ) { /* Initial Serial Receive Buffer */ MMD1100context.HeadPointerForSerial = 0; MMD1100context.TailPointerForSerial = 0; /* Initial Received Packet Buffer */ mmd1100_packet_flush_buffer(); /* 화일을 연다. */ MMD1100context.Device = open( DEVICE_MMD1100, (O_RDWR | O_NOCTTY) ); if ( MMD1100context.Device < 0 ) { /* 화일 열기 실패 */ MMD1100_ERROR( "failed to open %s\n", DEVICE_MMD1100 ); MMD1100context.Device = -1; *pDeviceFileDescriptor = MMD1100context.Device; return -1; } /* 현재 설정을 BackupTerminalInfo에 저장 */ tcgetattr( MMD1100context.Device, &MMD1100context.BackupTerminalInfo ); memset( &TerminalInfo, 0, sizeof(TerminalInfo) ); switch( MMD1100_DEFAULT_BAUDRATE ) { case 19200: TerminalInfo.c_cflag = B19200 | CS8 | CSTOPB | CLOCAL | CREAD ; break; case 38400: TerminalInfo.c_cflag = B38400 | CS8 | CSTOPB | CLOCAL | CREAD ; break; case 57600: TerminalInfo.c_cflag = B57600 | CS8 | CSTOPB | CLOCAL | CREAD ; break; case 115200: TerminalInfo.c_cflag = B115200 | CS8 | CSTOPB | CLOCAL | CREAD ; break; case 9600: default: TerminalInfo.c_cflag = B9600 | CS8 | CSTOPB | CLOCAL | CREAD ; break; } TerminalInfo.c_iflag = 0; /* IGNPAR? */ TerminalInfo.c_oflag = 0; //set input mode (non-canonical, no echo,.....) TerminalInfo.c_lflag = 0; TerminalInfo.c_cc[VTIME] = 30; /* time-out 값으로 사용된다. time-out 값은 TIME*0.1초 이다. */ TerminalInfo.c_cc[VMIN] = 0; /* MIN은 read가 리턴되기 위한 최소한의 문자 개수 */ tcflush(MMD1100context.Device, TCIFLUSH); tcsetattr(MMD1100context.Device, TCSANOW, &TerminalInfo); // raw mode fcntl(MMD1100context.Device, F_SETFL, O_NONBLOCK); } else { MMD1100_DEBUG("%s already opened\n", DEVICE_MMD1100); } *pDeviceFileDescriptor = MMD1100context.Device; return 0; } /* close serial device for end of cummunication with MMD1100 return: -1: Fail to operation 0: OK */ int mmd1100_close(void) { if ( MMD1100context.Device < 0 ) { MMD1100_ERROR("%s already closed\n", DEVICE_MMD1100); return -1; } /* 이전 상태로 되돌린다. */ tcsetattr( MMD1100context.Device, TCSANOW, &MMD1100context.BackupTerminalInfo ); close(MMD1100context.Device); MMD1100context.Device = -1; return 0; } /* get serial device file descriptor for cummunication with MMD1100 return: -1: Fail to operation -2: Input Paramters Error 0: OK */ int mmd1100_get_device_file_descriptor(int *pDeviceFileDescriptor) { if ( pDeviceFileDescriptor == (int *)0 ) { return -2; } if ( MMD1100context.Device != -1 ) { *pDeviceFileDescriptor = MMD1100context.Device; return 0; } MMD1100_ERROR("%s is closed\n", DEVICE_MMD1100); return -1; } /* serial buffer flush for cummunication with MMD1100 */ void mmd1100_serial_flush(void) { struct pollfd PollFileDescriptor; int Result; uint8_t ReceivedByte; if ( MMD1100context.Device != -1 ) { memset( &PollFileDescriptor, 0, sizeof(struct pollfd) ); while(1) { PollFileDescriptor.fd = MMD1100context.Device; PollFileDescriptor.events = POLLIN; PollFileDescriptor.revents = 0; Result = poll( &PollFileDescriptor, 1, 2 ); // 2 msec if ( (Result == 1) && ((PollFileDescriptor.revents & POLLIN) != 0) ) { read(MMD1100context.Device, &ReceivedByte, 1); } else { break; } } } else { MMD1100_ERROR("%s is closed\n", DEVICE_MMD1100); } MMD1100context.HeadPointerForSerial = 0; MMD1100context.TailPointerForSerial = 0; } /* send data(n byte) to MMD1100 through serial device */ static void mmd1100_serial_transmit_data(uint8_t *pData, uint32_t ByteLengthOfData) { int Result; uint8_t Byte; while(ByteLengthOfData--) { Byte = *pData++; do { Result = write(MMD1100context.Device, &Byte, 1); } while( Result != 1 ); } } /* polling serial device to save input data from MMD1100 */ static void mmd1100_serial_polling(void) { struct pollfd PollFileDescriptor; uint32_t Temp; int Result; uint8_t ReceivedByte; memset( &PollFileDescriptor, 0, sizeof(struct pollfd) ); PollFileDescriptor.fd = MMD1100context.Device; PollFileDescriptor.events = POLLIN; PollFileDescriptor.revents = 0; Result = poll( (struct pollfd *)&PollFileDescriptor, 1, 20); // 20 msec if ( (Result == 1) && ((PollFileDescriptor.revents & POLLIN) != 0) ) { if ( read(MMD1100context.Device, &ReceivedByte, 1) == 1 ) { MMD1100context.pBufferForSerial[MMD1100context.HeadPointerForSerial] = ReceivedByte; Temp = (MMD1100context.HeadPointerForSerial + 1) % MMD1100_BYTE_SIZE_OF_BUFFER_FOR_SERIAL; if ( Temp != MMD1100context.TailPointerForSerial ) { MMD1100context.HeadPointerForSerial = Temp; } } } } /* check serial buffer to find received data from MMD1100 return: 0: Data is not received 1: Data received */ static int mmd1100_serial_check_buffer(void) { // check lowlevel serial data, if we have no data if ( MMD1100context.HeadPointerForSerial == MMD1100context.TailPointerForSerial ) { mmd1100_serial_polling(); } if ( MMD1100context.HeadPointerForSerial != MMD1100context.TailPointerForSerial ) { return 1; } return 0; } /* get data from serial buffer return: -1: Fail to operation -2: Input Paramters Error 0: OK */ static int mmd1100_serial_get_data_from_buffer(uint8_t *pData) { if ( pData == (uint8_t *)0 ) { return -2; } if ( MMD1100context.TailPointerForSerial == MMD1100context.HeadPointerForSerial) { return -1; } *pData = MMD1100context.pBufferForSerial[MMD1100context.TailPointerForSerial]; MMD1100context.TailPointerForSerial++; MMD1100context.TailPointerForSerial %= MMD1100_BYTE_SIZE_OF_BUFFER_FOR_SERIAL; return 0; } /* flush received packet buffer */ static void mmd1100_packet_flush_buffer(void) { MMD1100context.PacketOffset = 0; MMD1100context.PacketCheckState = MMD1100_PACKET_CHECK_STATE_STX; } /* -1: invalid 0 : OK */ static int mmd1100_check_response(uint8_t Class, uint8_t Function, uint8_t Length, uint8_t Status) { switch (Class) { case MMD1100_COMMAND_CLASS_GET_VERSION: if ( Function == MMD1100_COMMAND_FUNCTION_GET_VERSION ) { if ( ( ( Status == MMD1100_DEFINE_ACK ) && ( Length == MMD1100_RESPONSE_ACK_LENGTH_GET_VERSION ) ) || ( ( Status == MMD1100_DEFINE_NAK ) && ( Length == MMD1100_RESPONSE_NAK_LENGTH ) ) ) { return 0; } } break; case MMD1100_COMMAND_CLASS_LOAD_USER_PARAMETERS: if ( Function == MMD1100_COMMAND_FUNCTION_LOAD_USER_PARAMETERS ) { if ( ( ( Status == MMD1100_DEFINE_ACK ) && ( Length == MMD1100_RESPONSE_ACK_LENGTH_LOAD_USER_PARAMETERS ) ) || ( ( Status == MMD1100_DEFINE_NAK ) && ( Length == MMD1100_RESPONSE_NAK_LENGTH ) ) ) { return 0; } } break; case MMD1100_COMMAND_CLASS_UART_CALIBRATION: if ( Function == MMD1100_COMMAND_FUNCTION_UART_CALIBRATION ) { if ( ( ( Status == MMD1100_DEFINE_ACK ) && ( Length == MMD1100_RESPONSE_ACK_LENGTH_UART_CALIBRATION ) ) || ( ( Status == MMD1100_DEFINE_NAK ) && ( Length == MMD1100_RESPONSE_NAK_LENGTH ) ) ) { return 0; } } break; case MMD1100_COMMAND_CLASS_OTP_WRITE: if ( Function == MMD1100_COMMAND_FUNCTION_OTP_WRITE ) { if ( ( ( Status == MMD1100_DEFINE_ACK ) && ( Length == MMD1100_RESPONSE_ACK_LENGTH_OTP_WRITE ) ) || ( ( Status == MMD1100_DEFINE_NAK ) && ( Length == MMD1100_RESPONSE_NAK_LENGTH ) ) ) { return 0; } } break; case MMD1100_COMMAND_CLASS_GET_STATUS: if ( Function == MMD1100_COMMAND_FUNCTION_GET_STATUS ) { if ( ( ( Status == MMD1100_DEFINE_ACK ) && ( Length == MMD1100_RESPONSE_ACK_LENGTH_GET_STATUS ) ) || ( ( Status == MMD1100_DEFINE_NAK ) && ( Length == MMD1100_RESPONSE_NAK_LENGTH ) ) ) { return 0; } } break; case MMD1100_COMMAND_CLASS_READ_DATA_RETRY: if ( Function == MMD1100_COMMAND_FUNCTION_READ_DATA_RETRY ) { if ( ( Status == MMD1100_DEFINE_NAK ) && ( Length == MMD1100_RESPONSE_NAK_LENGTH ) ) { return 0; } } break; case MMD1100_COMMAND_CLASS_SOFTWARE_RESET: if ( Function == MMD1100_COMMAND_FUNCTION_SOFTWARE_RESET ) { if ( ( Status == MMD1100_DEFINE_NAK ) && ( Length == MMD1100_RESPONSE_NAK_LENGTH ) ) { return 0; } } break; default: break; } return -1; } /* make response packet form serial buffer return: 2: Notify Packet 1: Need Time Wait -1: Fail to operation -2: Packet LRC Error -3: Packet Data is too Big -4: Timeout or Not Ready 0: OK */ static int mmd1100_packet_make_response_from_serial_buffer(void) { struct timespec CurrentTime; struct timespec GetTimeToLastData; uint8_t Data; while( mmd1100_serial_check_buffer() == 1 ) { /* Serial buffer has data */ mmd1100_serial_get_data_from_buffer( &Data ); clock_gettime( CLOCK_REALTIME, &GetTimeToLastData ); /* Push Data to Packet Buffer */ MMD1100context.pBufferForPacket[MMD1100context.PacketOffset] = Data; /* printf("0x%02X ", Data); */ if ( MMD1100context.PacketOffset < MMD1100_BYTE_SIZE_OF_BUFFER_FOR_PACKET ) { MMD1100context.PacketOffset++; } switch( MMD1100context.PacketCheckState ) { case MMD1100_PACKET_CHECK_STATE_STX: if ( Data == MMD1100_DEFINE_STX ) { MMD1100context.PacketCheckState = MMD1100_PACKET_CHECK_STATE_MODE_CLASS; } else { mmd1100_packet_flush_buffer(); // invaild command sequence MMD1100_DEBUG( "invalid received Data : 0x%02X\n", Data ); return -1; } break; case MMD1100_PACKET_CHECK_STATE_MODE_CLASS: MMD1100context.PacketLength = 0; if ( Data == MMD1100_DEFINE_TRACK_DATA_MODE_2 ) { MMD1100context.PacketCheckState = MMD1100_PACKET_CHECK_STATE_TRACK_ID; } else { switch ( Data ) { case MMD1100_COMMAND_CLASS_GET_VERSION: case MMD1100_COMMAND_CLASS_LOAD_USER_PARAMETERS: case MMD1100_COMMAND_CLASS_UART_CALIBRATION: case MMD1100_COMMAND_CLASS_OTP_WRITE: case MMD1100_COMMAND_CLASS_GET_STATUS: case MMD1100_COMMAND_CLASS_READ_DATA_RETRY: case MMD1100_COMMAND_CLASS_SOFTWARE_RESET: MMD1100context.PacketCheckState = MMD1100_PACKET_CHECK_STATE_FUNCTION; break; default: mmd1100_packet_flush_buffer(); // invaild command sequence MMD1100_DEBUG( "invalid received Data : 0x%02X\n", Data ); return -1; break; } } break; case MMD1100_PACKET_CHECK_STATE_TRACK_ID: if ( ( Data == MMD1100_DEFINE_TRACK_ID_1 ) || ( Data == MMD1100_DEFINE_TRACK_ID_2 ) || ( Data == MMD1100_DEFINE_TRACK_ID_3 ) ) { MMD1100context.PacketCheckState = MMD1100_PACKET_CHECK_STATE_TRACK_LENGTH; } else if ( Data == MMD1100_DEFINE_ETX ) { MMD1100context.PacketLength += 3; MMD1100context.PacketCheckState = MMD1100_PACKET_CHECK_STATE_BCC; } else { mmd1100_packet_flush_buffer(); // invaild command sequence MMD1100_DEBUG("invalid received Data : 0x%02X\n", Data); return -1; } break; case MMD1100_PACKET_CHECK_STATE_TRACK_LENGTH: MMD1100context.PacketCheckState = MMD1100_PACKET_CHECK_STATE_TRACK_STATE; MMD1100context.DataLengthInPacket = Data; break; case MMD1100_PACKET_CHECK_STATE_TRACK_STATE: if ( ( Data == MMD1100_DEFINE_ACK ) || ( Data == MMD1100_DEFINE_NAK ) ) { MMD1100context.PacketCheckState = MMD1100_PACKET_CHECK_STATE_TRACK_DATA; MMD1100context.PacketLength += (MMD1100context.DataLengthInPacket + 3); } else { mmd1100_packet_flush_buffer(); // invaild command sequence MMD1100_DEBUG("invalid received Data : 0x%02X\n", Data); return -1; } break; case MMD1100_PACKET_CHECK_STATE_TRACK_DATA: MMD1100context.DataLengthInPacket--; if ( MMD1100context.DataLengthInPacket == 0 ) { MMD1100context.PacketCheckState = MMD1100_PACKET_CHECK_STATE_TRACK_ID; } break; case MMD1100_PACKET_CHECK_STATE_FUNCTION: switch ( MMD1100context.pBufferForPacket[MMD1100_PACKET_POSITION_CLASS] ) { case MMD1100_COMMAND_CLASS_GET_VERSION: if ( Data == MMD1100_COMMAND_FUNCTION_GET_VERSION ) { MMD1100context.PacketCheckState = MMD1100_PACKET_CHECK_STATE_LENGTH; } break; case MMD1100_COMMAND_CLASS_LOAD_USER_PARAMETERS: if ( Data == MMD1100_COMMAND_FUNCTION_LOAD_USER_PARAMETERS ) { MMD1100context.PacketCheckState = MMD1100_PACKET_CHECK_STATE_LENGTH; } break; case MMD1100_COMMAND_CLASS_UART_CALIBRATION: if ( Data == MMD1100_COMMAND_FUNCTION_UART_CALIBRATION ) { MMD1100context.PacketCheckState = MMD1100_PACKET_CHECK_STATE_LENGTH; } break; case MMD1100_COMMAND_CLASS_OTP_WRITE: if ( Data == MMD1100_COMMAND_FUNCTION_OTP_WRITE ) { MMD1100context.PacketCheckState = MMD1100_PACKET_CHECK_STATE_LENGTH; } break; case MMD1100_COMMAND_CLASS_GET_STATUS: if ( Data == MMD1100_COMMAND_FUNCTION_GET_STATUS ) { MMD1100context.PacketCheckState = MMD1100_PACKET_CHECK_STATE_LENGTH; } break; case MMD1100_COMMAND_CLASS_READ_DATA_RETRY: if ( Data == MMD1100_COMMAND_FUNCTION_READ_DATA_RETRY ) { MMD1100context.PacketCheckState = MMD1100_PACKET_CHECK_STATE_LENGTH; } break; case MMD1100_COMMAND_CLASS_SOFTWARE_RESET: if ( Data == MMD1100_COMMAND_FUNCTION_SOFTWARE_RESET ) { MMD1100context.PacketCheckState = MMD1100_PACKET_CHECK_STATE_LENGTH; } break; default: break; } if ( MMD1100context.PacketCheckState != MMD1100_PACKET_CHECK_STATE_LENGTH ) { mmd1100_packet_flush_buffer(); // invaild command sequence MMD1100_DEBUG("invalid received Data : 0x%02X\n", Data); return -1; } break; case MMD1100_PACKET_CHECK_STATE_LENGTH: switch ( MMD1100context.pBufferForPacket[MMD1100_PACKET_POSITION_CLASS] ) { case MMD1100_COMMAND_CLASS_GET_VERSION: if ( ( Data == MMD1100_RESPONSE_ACK_LENGTH_GET_VERSION ) || ( Data == MMD1100_RESPONSE_NAK_LENGTH ) ) { MMD1100context.PacketCheckState = MMD1100_PACKET_CHECK_STATE_STATUS; } break; case MMD1100_COMMAND_CLASS_LOAD_USER_PARAMETERS: if ( ( Data == MMD1100_RESPONSE_ACK_LENGTH_LOAD_USER_PARAMETERS ) || ( Data == MMD1100_RESPONSE_NAK_LENGTH ) ) { MMD1100context.PacketCheckState = MMD1100_PACKET_CHECK_STATE_STATUS; } break; case MMD1100_COMMAND_CLASS_UART_CALIBRATION: if ( ( Data == MMD1100_RESPONSE_ACK_LENGTH_UART_CALIBRATION ) || ( Data == MMD1100_RESPONSE_NAK_LENGTH ) ) { MMD1100context.PacketCheckState = MMD1100_PACKET_CHECK_STATE_STATUS; } break; case MMD1100_COMMAND_CLASS_OTP_WRITE: if ( ( Data == MMD1100_RESPONSE_ACK_LENGTH_OTP_WRITE ) || ( Data == MMD1100_RESPONSE_NAK_LENGTH ) ) { MMD1100context.PacketCheckState = MMD1100_PACKET_CHECK_STATE_STATUS; } break; case MMD1100_COMMAND_CLASS_GET_STATUS: if ( ( Data == MMD1100_RESPONSE_ACK_LENGTH_GET_STATUS ) || ( Data == MMD1100_RESPONSE_NAK_LENGTH ) ) { MMD1100context.PacketCheckState = MMD1100_PACKET_CHECK_STATE_STATUS; } break; case MMD1100_COMMAND_CLASS_READ_DATA_RETRY: if ( Data == MMD1100_RESPONSE_NAK_LENGTH ) { MMD1100context.PacketCheckState = MMD1100_PACKET_CHECK_STATE_STATUS; } break; case MMD1100_COMMAND_CLASS_SOFTWARE_RESET: if ( Data == MMD1100_RESPONSE_NAK_LENGTH ) { MMD1100context.PacketCheckState = MMD1100_PACKET_CHECK_STATE_STATUS; } break; default: break; } if ( MMD1100context.PacketCheckState != MMD1100_PACKET_CHECK_STATE_STATUS ) { mmd1100_packet_flush_buffer(); // invaild command sequence MMD1100_DEBUG("invalid received Data : 0x%02X\n", Data); return -1; } break; case MMD1100_PACKET_CHECK_STATE_STATUS: if ( mmd1100_check_response(MMD1100context.pBufferForPacket[MMD1100_PACKET_POSITION_CLASS]/*Class*/, MMD1100context.pBufferForPacket[MMD1100_PACKET_POSITION_FUNCTION]/*Function*/, MMD1100context.pBufferForPacket[MMD1100_PACKET_POSITION_LENGTH]/*Length*/, Data/*Status*/) == 0 ) { MMD1100context.DataLengthInPacket = MMD1100context.pBufferForPacket[MMD1100_PACKET_POSITION_LENGTH]; MMD1100context.PacketCheckState = MMD1100_PACKET_CHECK_STATE_DATA; } else { mmd1100_packet_flush_buffer(); // invaild command sequence MMD1100_DEBUG("invalid received Data : 0x%02X\n", Data); return -1; } break; case MMD1100_PACKET_CHECK_STATE_DATA: MMD1100context.DataLengthInPacket--; if ( MMD1100context.DataLengthInPacket == 0 ) { MMD1100context.PacketCheckState = MMD1100_PACKET_CHECK_STATE_ETX; } break; case MMD1100_PACKET_CHECK_STATE_ETX: if ( Data == MMD1100_DEFINE_ETX ) { MMD1100context.PacketCheckState = MMD1100_PACKET_CHECK_STATE_BCC; MMD1100context.PacketLength = 6 + MMD1100context.pBufferForPacket[MMD1100_PACKET_POSITION_LENGTH]; } else { mmd1100_packet_flush_buffer(); // invaild command sequence MMD1100_INFO("invalid received Data : 0x%02X\n", Data); return -1; } break; case MMD1100_PACKET_CHECK_STATE_BCC: mmd1100_packet_flush_buffer(); MMD1100_DEBUG("received lenght : %d\n", MMD1100context.PacketLength); if ( Data == lrc_xor(MMD1100context.pBufferForPacket, MMD1100context.PacketLength) ) { MMD1100_INFO("BCC : OK\n"); return 0; } else { MMD1100_ERROR("BCC : Fail\n"); return -2; } break; } } // check inter-character timeout if ( MMD1100context.PacketCheckState != MMD1100_PACKET_CHECK_STATE_STX ) { clock_gettime(CLOCK_REALTIME, &CurrentTime); if ( (CurrentTime.tv_nsec - GetTimeToLastData.tv_nsec) > (MMD1100_TIMEOUT_BETWEEN_SERIAL_INPUT_DATA * 1000)/**/ ) { mmd1100_packet_flush_buffer(); // return -4; /* Time out */ } } return -4; /* Not Ready */ } /* receive return: -1: Fail to operation -2: Input Parameter Error -4: Timeout or Not Ready 0: OK */ int mmd1100_receive(MSRTrack_t *pMSRTrack, MMD1100Response_t *pResponse, uint32_t *pIsNotTrack, uint32_t ResponseMilisecondTimeout) { struct timespec Start; struct timespec Current; uint32_t Position; int Result; if ( pIsNotTrack == (uint32_t *)0 ) { return -2; } clock_gettime(CLOCK_REALTIME, &Start); while(1) { Result = mmd1100_packet_make_response_from_serial_buffer(); if ( Result == -4 ) /* Not ready or time-out */ { clock_gettime(CLOCK_REALTIME, &Current); if ( (Current.tv_nsec - Start.tv_nsec) > (ResponseMilisecondTimeout * 1000) ) { // MMD1100_DEBUG("time-out :start - %d nsec, cur = %d nsec, timeout = %d nsec \n", Start, Current, (ResponseMilisecondTimeout * 1000) ); return -4; } continue; } if ( Result != 0 ) { MMD1100_DEBUG("response_check() return - %d\n", Result); return -1; } break; } if( MMD1100context.pBufferForPacket[MMD1100_PACKET_POSITION_CLASS] == MMD1100_DEFINE_TRACK_DATA_MODE_2) { /* Track Data */ *pIsNotTrack = 0; if ( pMSRTrack == (MSRTrack_t *)0 ) { return -2; } memset( pMSRTrack, 0, sizeof(MSRTrack_t) ); { Position = MMD1100_PACKET_POSITION_TRACK_ID; while ( MMD1100context.pBufferForPacket[Position] != MMD1100_DEFINE_ETX ) { int i; if ( MMD1100context.pBufferForPacket[Position] == MMD1100_DEFINE_TRACK_ID_1 ) { if ( MMD1100context.pBufferForPacket[Position + 2] == MMD1100_DEFINE_NAK) { pMSRTrack->ErrorCodeForTrack1 = MMD1100context.pBufferForPacket[Position + 3]; pMSRTrack->LengthOfTrack1 = 0; } else { int temp; pMSRTrack->ErrorCodeForTrack1 = 0; pMSRTrack->LengthOfTrack1 = 0; pMSRTrack->LengthOfRaw1 = MMD1100context.pBufferForPacket[Position + 1]; memcpy( pMSRTrack->pRaw1, &MMD1100context.pBufferForPacket[Position + 3], pMSRTrack->LengthOfRaw1 ); temp = mmd100_convert_rawdata_to_bcd( pMSRTrack->pTrack1, &MMD1100context.pBufferForPacket[Position + 3], MMD1100context.pBufferForPacket[Position + 1], 1); if (temp > 0) { pMSRTrack->LengthOfTrack1 = temp; } } } if ( MMD1100context.pBufferForPacket[Position] == MMD1100_DEFINE_TRACK_ID_2 ) { if ( MMD1100context.pBufferForPacket[Position + 2] == MMD1100_DEFINE_NAK) { pMSRTrack->ErrorCodeForTrack2 = MMD1100context.pBufferForPacket[Position + 3]; pMSRTrack->LengthOfTrack2 = 0; } else { int temp; pMSRTrack->ErrorCodeForTrack2 = 0; pMSRTrack->LengthOfTrack2 = 0; pMSRTrack->LengthOfRaw2 = MMD1100context.pBufferForPacket[Position + 1]; memcpy( pMSRTrack->pRaw2, &MMD1100context.pBufferForPacket[Position + 3], pMSRTrack->LengthOfRaw2 ); temp = mmd100_convert_rawdata_to_bcd( pMSRTrack->pTrack2, &MMD1100context.pBufferForPacket[Position + 3], MMD1100context.pBufferForPacket[Position + 1], 1); if (temp > 0) { pMSRTrack->LengthOfTrack2 = temp; } else { MMD1100_ERROR( " mmd100_convert_rawdata_to_bcd() = -%d", -temp); } } } if ( MMD1100context.pBufferForPacket[Position] == MMD1100_DEFINE_TRACK_ID_3 ) { if ( MMD1100context.pBufferForPacket[Position + 2] == MMD1100_DEFINE_NAK) { pMSRTrack->ErrorCodeForTrack3 = MMD1100context.pBufferForPacket[Position + 3]; pMSRTrack->LengthOfTrack3 = 0; } else { int temp; pMSRTrack->ErrorCodeForTrack3 = 0; pMSRTrack->LengthOfTrack3 = 0; pMSRTrack->LengthOfRaw3 = MMD1100context.pBufferForPacket[Position + 1]; memcpy( pMSRTrack->pRaw3, &MMD1100context.pBufferForPacket[Position + 3], pMSRTrack->LengthOfRaw3 ); temp = mmd100_convert_rawdata_to_bcd( pMSRTrack->pTrack3, &MMD1100context.pBufferForPacket[Position + 3], MMD1100context.pBufferForPacket[Position + 1], 1); if (temp > 0) { pMSRTrack->LengthOfTrack3 = temp; } else { MMD1100_ERROR( " mmd100_convert_rawdata_to_bcd() = -%d", -temp); } } } Position += (3 + MMD1100context.pBufferForPacket[Position + 1]); } } } else /* Response */ { *pIsNotTrack = 1; if ( pResponse == (MMD1100Response_t *)0 ) { return -2; } memset( pResponse, 0, sizeof(MMD1100Response_t) ); switch ( MMD1100context.pBufferForPacket[MMD1100_PACKET_POSITION_CLASS] ) { case MMD1100_COMMAND_CLASS_GET_VERSION: pResponse->Command = MMD1100_COMMAND_GET_VERSION; break; case MMD1100_COMMAND_CLASS_LOAD_USER_PARAMETERS: pResponse->Command = MMD1100_COMMAND_LOAD_USER_PARAMETERS; break; case MMD1100_COMMAND_CLASS_UART_CALIBRATION: pResponse->Command = MMD1100_COMMAND_UART_CALIBRATION; break; case MMD1100_COMMAND_CLASS_OTP_WRITE: pResponse->Command = MMD1100_COMMAND_OTP_WRITE; break; case MMD1100_COMMAND_CLASS_GET_STATUS: pResponse->Command = MMD1100_COMMAND_GET_STATUS; break; case MMD1100_COMMAND_CLASS_READ_DATA_RETRY: pResponse->Command = MMD1100_COMMAND_READ_DATA_RETRY; break; case MMD1100_COMMAND_CLASS_SOFTWARE_RESET: pResponse->Command = MMD1100_COMMAND_SOFTWARE_RESET; break; } if ( MMD1100context.pBufferForPacket[MMD1100_PACKET_POSITION_STATUS] == MMD1100_DEFINE_NAK ) { pResponse->ErrorCode = MMD1100context.pBufferForPacket[MMD1100_PACKET_POSITION_DATA_START]; } else /* ( MMD1100context.pBufferForPacket[MMD1100_PACKET_POSITION_STATUS] == MMD1100_DEFINE_ACK ) */ { pResponse->ErrorCode = 0; memcpy( pResponse->pData, &MMD1100context.pBufferForPacket[MMD1100_PACKET_POSITION_DATA_START], MMD1100context.pBufferForPacket[MMD1100_PACKET_POSITION_LENGTH] ); } } return 0; } /* -1: invalid parameter 0 : OK */ int mmd1100_make_packet(uint8_t *pPacket, uint32_t *pMakedPacketByteLength, MMD1100_Command_t Command, uint8_t pArguments[4]) { if((pPacket == (uint8_t *)0) || (pMakedPacketByteLength == (uint32_t *)0)) { return -1; } pPacket[0] = MMD1100_DEFINE_STX; /* STX */ switch(Command) { case MMD1100_COMMAND_GET_VERSION: pPacket[1] = MMD1100_COMMAND_CLASS_GET_VERSION; /* Class */ pPacket[2] = MMD1100_COMMAND_FUNCTION_GET_VERSION; /* Function */ pPacket[3] = MMD1100_COMMAND_LENGTH_GET_VERSION; /* Length */ pPacket[4] = MMD1100_DEFINE_ETX; /* ETX */ *pMakedPacketByteLength = 6; pPacket[5] = lrc_xor(pPacket, (*pMakedPacketByteLength - 1)); /* BCC */ break; case MMD1100_COMMAND_LOAD_USER_PARAMETERS: if(pArguments == (uint8_t *)0) { return -1; } pPacket[1] = MMD1100_COMMAND_CLASS_LOAD_USER_PARAMETERS; /* Class */ pPacket[2] = MMD1100_COMMAND_FUNCTION_LOAD_USER_PARAMETERS; /* Function */ pPacket[3] = MMD1100_COMMAND_LENGTH_LOAD_USER_PARAMETERS; /* Length */ pPacket[4] = pArguments[0]; /* Tx Mode 1 Byte */ pPacket[5] = pArguments[1]; /* Reserved 1 Byte */ pPacket[6] = pArguments[2]; /* All Track Error 1 Byte */ pPacket[7] = pArguments[3]; /* Reserved 1 Byte */ pPacket[8] = MMD1100_DEFINE_ETX; /* ETX */ *pMakedPacketByteLength = 10; pPacket[9] = lrc_xor(pPacket, (*pMakedPacketByteLength - 1)); /* BCC */ break; case MMD1100_COMMAND_UART_CALIBRATION: pPacket[1] = MMD1100_COMMAND_CLASS_UART_CALIBRATION; /* Class */ pPacket[2] = MMD1100_COMMAND_FUNCTION_UART_CALIBRATION; /* Function */ pPacket[3] = MMD1100_COMMAND_LENGTH_UART_CALIBRATION; /* Length */ pPacket[4] = 0xAA; /* */ pPacket[5] = 0xAA; /* */ pPacket[6] = 0xAA; /* */ pPacket[7] = 0xAA; /* */ pPacket[8] = 0xAA; /* */ pPacket[9] = MMD1100_DEFINE_ETX; /* ETX */ *pMakedPacketByteLength = 11; pPacket[10] = lrc_xor(pPacket, (*pMakedPacketByteLength - 1)); /* BCC */ break; case MMD1100_COMMAND_OTP_WRITE: pPacket[1] = MMD1100_COMMAND_CLASS_OTP_WRITE; /* Class */ pPacket[2] = MMD1100_COMMAND_FUNCTION_OTP_WRITE; /* Function */ pPacket[3] = MMD1100_COMMAND_LENGTH_OTP_WRITE; /* Length */ pPacket[4] = MMD1100_DEFINE_ETX; /* ETX */ *pMakedPacketByteLength = 6; pPacket[5] = lrc_xor(pPacket, (*pMakedPacketByteLength - 1)); /* BCC */ break; case MMD1100_COMMAND_GET_STATUS: pPacket[1] = MMD1100_COMMAND_CLASS_GET_STATUS; /* Class */ pPacket[2] = MMD1100_COMMAND_FUNCTION_GET_STATUS; /* Function */ pPacket[3] = MMD1100_COMMAND_LENGTH_GET_STATUS; /* Length */ pPacket[4] = MMD1100_DEFINE_ETX; /* ETX */ *pMakedPacketByteLength = 6; pPacket[5] = lrc_xor(pPacket, (*pMakedPacketByteLength - 1) ); /* BCC */ break; case MMD1100_COMMAND_READ_DATA_RETRY: pPacket[1] = MMD1100_COMMAND_CLASS_READ_DATA_RETRY; /* Class */ pPacket[2] = MMD1100_COMMAND_FUNCTION_READ_DATA_RETRY; /* Function */ pPacket[3] = MMD1100_COMMAND_LENGTH_READ_DATA_RETRY; /* Length */ pPacket[4] = MMD1100_DEFINE_ETX; /* ETX */ *pMakedPacketByteLength = 6; pPacket[5] = lrc_xor(pPacket, (*pMakedPacketByteLength - 1) ); /* BCC */ break; case MMD1100_COMMAND_SOFTWARE_RESET: pPacket[1] = MMD1100_COMMAND_CLASS_SOFTWARE_RESET; /* Class */ pPacket[2] = MMD1100_COMMAND_FUNCTION_SOFTWARE_RESET; /* Function */ pPacket[3] = MMD1100_COMMAND_LENGTH_SOFTWARE_RESET; /* Length */ pPacket[4] = MMD1100_DEFINE_ETX; /* ETX */ *pMakedPacketByteLength = 6; pPacket[5] = lrc_xor(pPacket, (*pMakedPacketByteLength - 1) ); /* BCC */ break; default: return -1; break; } return 0; } /* -1: invalid parameter -2: send error 0 : Not open + : Send length */ int mmd1100_send(uint8_t *pPacket, uint8_t PacketByteLength) { uint32_t Index; if ( ( pPacket == (uint8_t *)0 ) || ( PacketByteLength == 0 ) ) { return -1; } if ( MMD1100context.Device == -1 ) { return 0; } mmd1100_serial_flush(); for ( Index = 0; Index < PacketByteLength; Index ++ ) { if ( write( MMD1100context.Device, &pPacket[Index], 1 ) != 1 ) { MMD1100_ERROR( "failed to send packet to MSR controller(MMD1100) %s\n", DEVICE_MMD1100 ); return -2; } } tcdrain( MMD1100context.Device ); return Index; }