├── Arduino-Aime-Reader.ino ├── README.md ├── cmd.h ├── doc ├── aime_example.mct ├── example.txt └── nfc.txt └── tools ├── DTR-RTS.c ├── DTR-RTS.exe ├── ReaderTest └── ReaderTest.ino ├── chunihook.dll └── sg-cmd.c /Arduino-Aime-Reader.ino: -------------------------------------------------------------------------------- 1 | #include "cmd.h" 2 | bool high_baudrate = true;//high_baudrate=true 3 | 4 | void setup() { 5 | FastLED.addLeds(leds, NUM_LEDS); 6 | FastLED.setBrightness(50); 7 | FastLED.showColor(0); 8 | nfc.begin(); 9 | while (!nfc.getFirmwareVersion()) { 10 | FastLED.showColor(0xFF0000); 11 | delay(500); 12 | FastLED.showColor(0); 13 | delay(500); 14 | } 15 | nfc.setPassiveActivationRetries(0x10);//Set wait times 16 | nfc.SAMConfig(); 17 | memset(&req, 0, sizeof(req.bytes)); 18 | memset(&res, 0, sizeof(res.bytes)); 19 | 20 | SerialDevice.begin(high_baudrate ? 115200 : 38400); 21 | FastLED.showColor(high_baudrate ? 0x0000FF : 0x00FF00); 22 | } 23 | 24 | void loop() { 25 | SerialCheck(); 26 | packet_write(); 27 | } 28 | 29 | static uint8_t len, r, checksum; 30 | static bool escape = false; 31 | 32 | static uint8_t packet_read() { 33 | 34 | while (SerialDevice.available()) { 35 | r = SerialDevice.read(); 36 | if (r == 0xE0) { 37 | req.frame_len = 0xFF; 38 | continue; 39 | } 40 | if (req.frame_len == 0xFF) { 41 | req.frame_len = r; 42 | len = 0; 43 | checksum = r; 44 | continue; 45 | } 46 | if (r == 0xD0) { 47 | escape = true; 48 | continue; 49 | } 50 | if (escape) { 51 | r++; 52 | escape = false; 53 | } 54 | req.bytes[++len] = r; 55 | if (len == req.frame_len && checksum == r) { 56 | return req.cmd; 57 | } 58 | checksum += r; 59 | } 60 | return 0; 61 | } 62 | 63 | static void packet_write() { 64 | uint8_t checksum = 0, len = 0; 65 | if (res.cmd == 0) { 66 | return; 67 | } 68 | SerialDevice.write(0xE0); 69 | while (len <= res.frame_len) { 70 | uint8_t w; 71 | if (len == res.frame_len) { 72 | w = checksum; 73 | } else { 74 | w = res.bytes[len]; 75 | checksum += w; 76 | } 77 | if (w == 0xE0 || w == 0xD0) { 78 | SerialDevice.write(0xD0); 79 | SerialDevice.write(--w); 80 | } else { 81 | SerialDevice.write(w); 82 | } 83 | len++; 84 | } 85 | res.cmd = 0; 86 | } 87 | 88 | void SerialCheck() { 89 | switch (packet_read()) { 90 | case SG_NFC_CMD_RESET: 91 | sg_nfc_cmd_reset(); 92 | break; 93 | case SG_NFC_CMD_GET_FW_VERSION: 94 | sg_nfc_cmd_get_fw_version(); 95 | break; 96 | case SG_NFC_CMD_GET_HW_VERSION: 97 | sg_nfc_cmd_get_hw_version(); 98 | break; 99 | case SG_NFC_CMD_POLL: 100 | sg_nfc_cmd_poll(); 101 | break; 102 | case SG_NFC_CMD_MIFARE_READ_BLOCK: 103 | sg_nfc_cmd_mifare_read_block(); 104 | break; 105 | case SG_NFC_CMD_FELICA_ENCAP: 106 | sg_nfc_cmd_felica_encap(); 107 | break; 108 | case SG_NFC_CMD_AIME_AUTHENTICATE: 109 | sg_nfc_cmd_aime_authenticate(); 110 | break; 111 | case SG_NFC_CMD_BANA_AUTHENTICATE: 112 | sg_nfc_cmd_bana_authenticate(); 113 | break; 114 | case SG_NFC_CMD_MIFARE_SELECT_TAG: 115 | sg_nfc_cmd_mifare_select_tag(); 116 | break; 117 | case SG_NFC_CMD_MIFARE_SET_KEY_AIME: 118 | sg_nfc_cmd_mifare_set_key_aime(); 119 | break; 120 | case SG_NFC_CMD_MIFARE_SET_KEY_BANA: 121 | sg_nfc_cmd_mifare_set_key_bana(); 122 | break; 123 | case SG_NFC_CMD_RADIO_ON: 124 | sg_nfc_cmd_radio_on(); 125 | break; 126 | case SG_NFC_CMD_RADIO_OFF: 127 | sg_nfc_cmd_radio_off(); 128 | break; 129 | case SG_RGB_CMD_RESET: 130 | sg_led_cmd_reset(); 131 | break; 132 | case SG_RGB_CMD_GET_INFO: 133 | sg_led_cmd_get_info(); 134 | break; 135 | case SG_RGB_CMD_SET_COLOR: 136 | sg_led_cmd_set_color(); 137 | break; 138 | case 0: 139 | break; 140 | default: 141 | sg_res_init(); 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # **Arduino-Aime-Reader** 2 | **All the main features are implemented now, so if there are no more bugs there should be no more updates.** 3 | 4 | 中文: [Sucareto/Arduino-Aime-Reader](https://github.com/Sucareto/Arduino-Aime-Reader) 5 | 6 | Aime compatible card reader made using Arduino + PN532 + WS2812B. 7 | Supported card types: [FeliCa](https://en.wikipedia.org/wiki/FeliCa) (Amusement IC, Suica, Octopus, etc.) and [MIFARE](https://en.wikipedia.org/wiki/MIFARE) (Aime, Banapassport). 8 | The implementation logic is the official card reader serial port data comparison + brain compensation, the correct implementation is not guaranteed. 9 | The communication data format is referenced from [Segatools](https://github.com/rakisaionji/segatools) and the official card reader packet capture data, which can be viewed at [example.txt](doc/example.txt) and [nfc.txt](doc/nfc.txt). 10 | An example on how one could use this: [ESP32-CardReader](https://github.com/Sucareto/ESP32-CardReader) 11 | 12 | 13 | ### **How To Use:** 14 | 1. Follow the prompts in [PN532](https://github.com/elechouse/PN532) to install the library. 15 | 2. Connect the cable(s) (I2C or SPI) between Arduino and PN532 according to your usage, and adjust the dip switch on the PN532. 16 | 3. Connect the WS2812B light bar (optional) 17 | 4. Upload [ReaderTest](tools/ReaderTest/ReaderTest.ino) to test if the hardware is working correctly or not. 18 | 5. If the card reading is normal, you can open the device manager and set the COM port number according to the support list 19 | 6. Set the `high_baudrate` option of the code according to the baud rate of the game, `115200` is `true` and `38400` is `false` 20 | 7. Upload the program and open the game to test 21 | 8. Install [MifareClassicTool](https://github.com/ikarus23/MifareClassicTool), modify [Aime card example](doc/aime_example.mct) and write to a blank MIFARE UID/CUID card. 22 | 23 | Some Arduinos may need to send DTR/RTS to the serial port at the correct baud rate before the main game is connected. You may need to open the Arduino serial monitor once before starting the main program. 24 | If the game is SDBT, you can run [DTR-RTS.exe](tools/DTR-RTS.exe) once before starting to send DTR/RTS to COM1 and COM12 25 | If you need to send to other ports and specific baud rates, you can modify [DTR-RTS.c](tools/DTR-RTS.c) then compile. 26 | 27 | 28 | ### **Support List:** 29 | | Game ID | COM Port Number | Supported Cards | Default Baud Rate | 30 | | - | - | - | - | 31 | | SDDT/SDEZ | COM1 | FeliCa,MIFARE | 115200 | 32 | | SDEY | COM2 | MIFARE | 38400 | 33 | | SDHD | COM4 | FeliCa,MIFARE | cvt=38400,sp=115200 | 34 | | SBZV/SDDF | COM10 | FeliCa,MIFARE | 38400 | 35 | | SDBT | COM12 | FeliCa,MIFARE | 38400 | 36 | 37 | ### **Game ID Reference:** 38 | | Game ID | Game Name | 39 | | - | - | 40 | | SDDT/SDEZ | ONGEKI, maimaiでらくっす | 41 | | SDEY | maimai FiNALE | 42 | | SDHD | CHUNITHM (ALLS) | 43 | | SBZV/SDDF | Project DIVA, Initial D | 44 | | SDBT | CHUNITHM (Nu) | 45 | 46 | - If the card reader is not working properly, you can switch the baud rate to try and fix it. 47 | - If you are using amdaemon, you can refer to aime > unit > port in config_common.json to confirm the port number. 48 | - If `"high_baudrate": true`, then the baud rate is `115200`, otherwise it is `38400`. 49 | 50 | ### **Tested Development Boards:** 51 | - SparkFun Pro Micro (ATmega32u4), need to send DTR/RTS 52 | - SparkFun SAMD21 Dev Breakout(ATSAMD21G18) 53 | - NodeMCU 1.0(ESP-12E + CP2102 & CH340), SDA=D2, SCL=D1 54 | - NodeMCU-32S(ESP32-S + CH340) 55 | 56 | ### **Known Issues:** 57 | - The write Felica operation of the NDA_08 command was not implemented because it was not confirmed whether it would affect the subsequent use of the card. 58 | - The meaning of `res.status` is not determined, so `res.status = 1; ` may be wrong. 59 | - `mifare_select_tag` is not implemented, multi-card selection is not supported, only the first recognized card will be read. 60 | 61 | 62 | ### **Reference Library:** 63 | - Driver WS2812B: [FastLED](https://github.com/FastLED/FastLED) 64 | - Driver PN532: [PN532](https://github.com/elechouse/PN532) 65 | - Reference for reading FeliCa data: [How to read FeliCa student ID card with Arduino using PN532](https://qiita.com/gpioblink/items/91597a5275862f7ffb3c) 66 | - Program for reading FeliCa data: [NFC TagInfo](https://play.google.com/store/apps/details?id=at.mroland.android.apps.nfctaginfo), [NFC TagInfo by NXP](https://play.google.com/store/apps/details?id=com.nxp.taginfolite) 67 | -------------------------------------------------------------------------------- /cmd.h: -------------------------------------------------------------------------------- 1 | #include "FastLED.h" 2 | #define NUM_LEDS 6 3 | CRGB leds[NUM_LEDS]; 4 | 5 | #if defined(__AVR_ATmega32U4__) || defined(ARDUINO_SAMD_ZERO) 6 | #pragma message "The current development board is ATmega32U4 or SAMD_ZERO" 7 | #define SerialDevice SerialUSB 8 | #define LED_PIN A3 9 | #define PN532_SPI_SS 10 //When 32U4 does not use SPI, executing ReadWithoutEncryption will fail 10 | 11 | #elif defined(ARDUINO_ESP8266_NODEMCU_ESP12E) 12 | #pragma message "Current development board is NODEMCU_ESP12E" 13 | #define SerialDevice Serial 14 | #define LED_PIN D5 15 | 16 | #elif defined(ARDUINO_NodeMCU_32S) 17 | #pragma message "Current development board is NodeMCU_32S" 18 | #define SerialDevice Serial 19 | #define LED_PIN 13 20 | #define PN532_SPI_SS 5 21 | 22 | #else 23 | #error "Untested development board, please check the serial port and pin definitions" 24 | #endif 25 | 26 | #if defined(PN532_SPI_SS) 27 | #pragma message "Use SPI to connect to PN532" 28 | #include 29 | #include 30 | PN532_SPI pn532(SPI, PN532_SPI_SS); 31 | #else 32 | #include 33 | #include 34 | PN532_I2C pn532(Wire); 35 | #endif 36 | 37 | #include "PN532.h" 38 | PN532 nfc(pn532); 39 | 40 | uint8_t AimeKey[6], BanaKey[6]; 41 | 42 | enum { 43 | SG_NFC_CMD_GET_FW_VERSION = 0x30, 44 | SG_NFC_CMD_GET_HW_VERSION = 0x32, 45 | SG_NFC_CMD_RADIO_ON = 0x40, 46 | SG_NFC_CMD_RADIO_OFF = 0x41, 47 | SG_NFC_CMD_POLL = 0x42, 48 | SG_NFC_CMD_MIFARE_SELECT_TAG = 0x43, 49 | SG_NFC_CMD_MIFARE_SET_KEY_BANA = 0x50, 50 | SG_NFC_CMD_BANA_AUTHENTICATE = 0x51, 51 | SG_NFC_CMD_MIFARE_READ_BLOCK = 0x52, 52 | SG_NFC_CMD_MIFARE_SET_KEY_AIME = 0x54, 53 | SG_NFC_CMD_AIME_AUTHENTICATE = 0x55, 54 | SG_NFC_CMD_TO_UPDATER_MODE = 0x60, 55 | SG_NFC_CMD_SEND_HEX_DATA = 0x61, 56 | SG_NFC_CMD_RESET = 0x62, 57 | SG_NFC_CMD_FELICA_ENCAP = 0x71, 58 | SG_RGB_CMD_SET_COLOR = 0x81, 59 | SG_RGB_CMD_GET_INFO = 0xF0, 60 | SG_RGB_CMD_RESET = 0xF5, 61 | 62 | //FELICA_ENCAP 63 | FELICA_CMD_POLL = 0x00, 64 | FELICA_CMD_NDA_06 = 0x06, 65 | FELICA_CMD_NDA_08 = 0x08, 66 | FELICA_CMD_GET_SYSTEM_CODE = 0x0C, 67 | FELICA_CMD_NDA_A4 = 0xA4, 68 | }; 69 | 70 | typedef union packet_req { 71 | uint8_t bytes[128]; 72 | struct { 73 | uint8_t frame_len; 74 | uint8_t addr; 75 | uint8_t seq_no; 76 | uint8_t cmd; 77 | uint8_t payload_len; 78 | union { 79 | uint8_t key[6]; //sg_nfc_req_mifare_set_key(bana or aime) 80 | uint8_t color_payload[3];//sg_led_req_set_color 81 | struct { //sg_nfc_cmd_mifare_select_tag,sg_nfc_cmd_mifare_authenticate,sg_nfc_cmd_mifare_read_block 82 | uint8_t uid[4]; 83 | uint8_t block_no; 84 | }; 85 | struct { //sg_nfc_req_felica_encap 86 | uint8_t encap_IDm[8]; 87 | uint8_t encap_len; 88 | uint8_t encap_code; 89 | union { 90 | struct { //FELICA_CMD_POLL,Guess 91 | uint8_t poll_systemCode[2]; 92 | uint8_t poll_requestCode; 93 | uint8_t poll_timeout; 94 | }; 95 | struct { //NDA_06,NDA_08,NDA_A4 96 | uint8_t RW_IDm[8]; 97 | uint8_t numService;//and NDA_A4 unknown byte 98 | uint8_t serviceCodeList[2]; 99 | uint8_t numBlock; 100 | uint8_t blockList[1][2];//Variable length 101 | uint8_t blockData[16];//WriteWithoutEncryption,ignore 102 | }; 103 | uint8_t felica_payload[1]; 104 | }; 105 | }; 106 | }; 107 | }; 108 | } packet_req_t; 109 | 110 | typedef union packet_res { 111 | uint8_t bytes[128]; 112 | struct { 113 | uint8_t frame_len; 114 | uint8_t addr; 115 | uint8_t seq_no; 116 | uint8_t cmd; 117 | uint8_t status; 118 | uint8_t payload_len; 119 | union { 120 | char version[23]; //sg_nfc_res_get_fw_version,sg_nfc_res_get_hw_version 121 | uint8_t info_payload[9]; //sg_led_res_get_info 122 | uint8_t block[16]; //sg_nfc_res_mifare_read_block 123 | struct { //sg_nfc_res_poll 124 | uint8_t count; 125 | uint8_t type; 126 | uint8_t id_len; 127 | union { 128 | uint8_t mifare_uid[4]; 129 | struct { 130 | uint8_t IDm[8]; 131 | uint8_t PMm[8]; 132 | }; 133 | }; 134 | }; 135 | struct { //sg_nfc_res_felica_encap 136 | uint8_t encap_len; 137 | uint8_t encap_code; 138 | uint8_t encap_IDm[8]; 139 | union { 140 | struct {//FELICA_CMD_POLL 141 | uint8_t poll_PMm[8]; 142 | uint8_t poll_systemCode[2]; 143 | }; 144 | struct { 145 | uint8_t RW_status[2];//Guess,NDA_06,NDA_08 146 | uint8_t numBlock;//NDA_06 147 | uint8_t blockData[1][1][16];//NDA_06 148 | }; 149 | uint8_t felica_payload[1]; 150 | }; 151 | }; 152 | }; 153 | }; 154 | } packet_res_t; 155 | 156 | static packet_req_t req; 157 | static packet_res_t res; 158 | 159 | static void sg_res_init(uint8_t payload_len = 0) { //Initialization template 160 | res.frame_len = 6 + payload_len; 161 | res.addr = req.addr; 162 | res.seq_no = req.seq_no; 163 | res.cmd = req.cmd; 164 | res.status = 0; 165 | res.payload_len = payload_len; 166 | } 167 | 168 | static void sg_nfc_cmd_reset() { //Reset the card reader 169 | nfc.begin(); 170 | nfc.setPassiveActivationRetries(0x01); //Set the number of waits, 0xFF wait forever 171 | nfc.SAMConfig(); 172 | if (nfc.getFirmwareVersion()) { 173 | nfc.SAMConfig(); 174 | sg_res_init(); 175 | res.status = 3; 176 | return; 177 | } 178 | FastLED.showColor(0xFF0000); 179 | } 180 | 181 | static void sg_nfc_cmd_get_fw_version() { 182 | sg_res_init(23); 183 | memcpy(res.version, "TN32MSEC003S F/W Ver1.2", 23); 184 | } 185 | 186 | static void sg_nfc_cmd_get_hw_version() { 187 | sg_res_init(23); 188 | memcpy(res.version, "TN32MSEC003S H/W Ver3.0", 23); 189 | } 190 | 191 | static void sg_nfc_cmd_mifare_set_key_aime() { 192 | sg_res_init(); 193 | memcpy(AimeKey, req.key, 6); 194 | } 195 | 196 | static void sg_nfc_cmd_mifare_set_key_bana() { 197 | sg_res_init(); 198 | memcpy(BanaKey, req.key, 6); 199 | } 200 | 201 | static void sg_led_cmd_reset() { 202 | sg_res_init(); 203 | } 204 | 205 | static void sg_led_cmd_get_info() { 206 | sg_res_init(9); 207 | static uint8_t info[9] = {'1', '5', '0', '8', '4', 0xFF, 0x10, 0x00, 0x12}; 208 | memcpy(res.info_payload, info, 9); 209 | } 210 | 211 | static void sg_led_cmd_set_color() { 212 | FastLED.showColor(CRGB(req.color_payload[0], req.color_payload[1], req.color_payload[2])); 213 | } 214 | 215 | static void sg_nfc_cmd_radio_on() { 216 | sg_res_init(); 217 | nfc.setRFField(0x00, 0x01); 218 | } 219 | 220 | static void sg_nfc_cmd_radio_off() { 221 | sg_res_init(); 222 | nfc.setRFField(0x00, 0x00); 223 | } 224 | 225 | static void sg_nfc_cmd_poll() { //Card number sent 226 | uint16_t SystemCode; 227 | if (nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, res.mifare_uid, &res.id_len)) { 228 | sg_res_init(0x07); 229 | res.count = 1; 230 | res.type = 0x10; 231 | return; 232 | } 233 | else if (nfc.felica_Polling(0xFFFF, 0x00, res.IDm, res.PMm, &SystemCode, 200) == 1) {//< 0: error 234 | sg_res_init(0x13); 235 | res.count = 1; 236 | res.type = 0x20; 237 | res.id_len = 0x10; 238 | return; 239 | } else { 240 | sg_res_init(1); 241 | res.count = 0; 242 | return; 243 | } 244 | } 245 | 246 | static void sg_nfc_cmd_mifare_select_tag() { 247 | sg_res_init(); 248 | } 249 | 250 | static void sg_nfc_cmd_aime_authenticate() { 251 | sg_res_init(); 252 | //AuthenticateBlock(uid,uidLen,block,keyType(A=0,B=1),keyData) 253 | if (nfc.mifareclassic_AuthenticateBlock(req.uid, 4, req.block_no, 1, AimeKey)) { 254 | return; 255 | } else { 256 | res.status = 1; 257 | } 258 | } 259 | 260 | static void sg_nfc_cmd_bana_authenticate() { 261 | sg_res_init(); 262 | //AuthenticateBlock(uid,uidLen,block,keyType(A=0,B=1),keyData) 263 | if (nfc.mifareclassic_AuthenticateBlock(req.uid, 4, req.block_no, 0, BanaKey)) { 264 | return; 265 | } else { 266 | res.status = 1; 267 | } 268 | } 269 | 270 | static void sg_nfc_cmd_mifare_read_block() {//Read card sector data 271 | if (nfc.mifareclassic_ReadDataBlock(req.block_no, res.block)) { 272 | sg_res_init(0x10); 273 | return; 274 | } 275 | sg_res_init(); 276 | res.status = 1; 277 | } 278 | 279 | static void sg_nfc_cmd_felica_encap() { 280 | uint16_t SystemCode; 281 | if (nfc.felica_Polling(0xFFFF, 0x01, res.encap_IDm, res.poll_PMm, &SystemCode, 200) == 1) { 282 | SystemCode = SystemCode >> 8 | SystemCode << 8;//SystemCode,Reversed endianness note 283 | } 284 | else { 285 | sg_res_init(); 286 | res.status = 1; 287 | return; 288 | } 289 | uint8_t code = req.encap_code; 290 | res.encap_code = code + 1; 291 | switch (code) { 292 | case FELICA_CMD_POLL: 293 | { 294 | sg_res_init(0x14); 295 | res.poll_systemCode[0] = SystemCode; 296 | res.poll_systemCode[1] = SystemCode >> 8; 297 | } 298 | break; 299 | case FELICA_CMD_GET_SYSTEM_CODE: 300 | { 301 | sg_res_init(0x0D); 302 | res.felica_payload[0] = 0x01;//Guess 303 | res.felica_payload[1] = SystemCode;//SystemCode 304 | res.felica_payload[2] = SystemCode >> 8; 305 | } 306 | break; 307 | case FELICA_CMD_NDA_A4: 308 | { 309 | sg_res_init(0x0B); 310 | res.felica_payload[0] = 0x00; 311 | } 312 | break; 313 | case FELICA_CMD_NDA_06: 314 | { 315 | uint16_t serviceCodeList[1] = {(uint16_t)(req.serviceCodeList[1] << 8 | req.serviceCodeList[0])};//Reversed endianness note 316 | for (uint8_t i = 0; i < req.numBlock; i++) { 317 | uint16_t blockList[1] = {(uint16_t)(req.blockList[i][0] << 8 | req.blockList[i][1])}; 318 | if (nfc.felica_ReadWithoutEncryption(1, serviceCodeList, 1, blockList, res.blockData[i]) != 1) { 319 | memset(res.blockData[i], 0, 16);//dummy data 320 | } 321 | } 322 | res.RW_status[0] = 0; 323 | res.RW_status[1] = 0; 324 | res.numBlock = req.numBlock; 325 | sg_res_init(0x0D + req.numBlock * 16); 326 | } 327 | break; 328 | case FELICA_CMD_NDA_08: 329 | { 330 | sg_res_init(0x0C);//There should be a write card here, but it's not going to be implemented 331 | res.RW_status[0] = 0; 332 | res.RW_status[1] = 0; 333 | } 334 | break; 335 | default: 336 | sg_res_init(); 337 | res.status = 1; 338 | } 339 | res.encap_len = res.payload_len; 340 | } 341 | -------------------------------------------------------------------------------- /doc/aime_example.mct: -------------------------------------------------------------------------------- 1 | +Sector: 0 2 | 1DB40F0DAB880400C832002000000016 3 | 00000000000000000000000000000000 4 | 00000000000010114514191981023333 5 | 57434346763208778F11574343467632 6 | +Sector: 1 7 | 00000000000000000000000000000000 8 | 00000000000000000000000000000000 9 | 00000000000000000000000000000000 10 | 57434346763208778F11574343467632 11 | +Sector: 2 12 | 00000000000000000000000000000000 13 | 00000000000000000000000000000000 14 | 00000000000000000000000000000000 15 | 57434346763208778F11574343467632 16 | +Sector: 3 17 | 00000000000000000000000000000000 18 | 00000000000000000000000000000000 19 | 00000000000000000000000000000000 20 | 57434346763208778F11574343467632 21 | +Sector: 4 22 | 00000000000000000000000000000000 23 | 00000000000000000000000000000000 24 | 00000000000000000000000000000000 25 | 57434346763208778F11574343467632 26 | +Sector: 5 27 | 00000000000000000000000000000000 28 | 00000000000000000000000000000000 29 | 00000000000000000000000000000000 30 | 57434346763208778F11574343467632 31 | +Sector: 6 32 | 00000000000000000000000000000000 33 | 00000000000000000000000000000000 34 | 00000000000000000000000000000000 35 | 57434346763208778F11574343467632 36 | +Sector: 7 37 | 00000000000000000000000000000000 38 | 00000000000000000000000000000000 39 | 00000000000000000000000000000000 40 | 57434346763208778F11574343467632 41 | +Sector: 8 42 | 00000000000000000000000000000000 43 | 00000000000000000000000000000000 44 | 00000000000000000000000000000000 45 | 57434346763208778F11574343467632 46 | +Sector: 9 47 | 00000000000000000000000000000000 48 | 00000000000000000000000000000000 49 | 00000000000000000000000000000000 50 | 57434346763208778F11574343467632 51 | +Sector: 10 52 | 00000000000000000000000000000000 53 | 00000000000000000000000000000000 54 | 00000000000000000000000000000000 55 | 57434346763208778F11574343467632 56 | +Sector: 11 57 | 00000000000000000000000000000000 58 | 00000000000000000000000000000000 59 | 00000000000000000000000000000000 60 | 57434346763208778F11574343467632 61 | +Sector: 12 62 | 00000000000000000000000000000000 63 | 00000000000000000000000000000000 64 | 00000000000000000000000000000000 65 | 57434346763208778F11574343467632 66 | +Sector: 13 67 | 00000000000000000000000000000000 68 | 00000000000000000000000000000000 69 | 00000000000000000000000000000000 70 | 57434346763208778F11574343467632 71 | +Sector: 14 72 | 00000000000000000000000000000000 73 | 00000000000000000000000000000000 74 | 00000000000000000000000000000000 75 | 57434346763208778F11574343467632 76 | +Sector: 15 77 | 00000000000000000000000000000000 78 | 00000000000000000000000000000000 79 | 00000000000000000000000000000000 80 | 57434346763208778F11574343467632 81 | -------------------------------------------------------------------------------- /doc/example.txt: -------------------------------------------------------------------------------- 1 | //SG_NFC_CMD_GET_FW_VERSION 2 | E0 [05] 00 09 (30) 00 [ ] 3 | E0 [1D] 00 [ ] (30) 00 17 [54 4E 33 32 4D 53 45 43 30 30 33 53 20 46 2F 57 20 56 65 72 31 2E 32] [ ] 4 | 5 | //SG_NFC_CMD_GET_HW_VERSION 6 | E0 [05] 00 [ ] (32) 00 [ ] 7 | E0 [1D] 00 [ ] (32) 00 17 [54 4E 33 32 4D 53 45 43 30 30 33 53 20 48 2F 57 20 56 65 72 33 2E 30] [ ] 8 | 9 | //SG_RGB_CMD_GET_INFO 10 | E0 [05] 08 [ ] (F0) 00 [ ] 11 | E0 [0F] 08 [ ] (F0) 00 [09 31 35 30 38 34 FF 10 00 12] [ ] 12 | 13 | //SG_NFC_CMD_POLL,FELICA 14 | E0 [05] 00 [ ] (42) 00 [ ] 15 | E0 [19] 00 [ ] (42) 00 13 01 20 10 [ 8 byte IDm ] [ 8 byte PMm ] [ ] 16 | 17 | //SG_NFC_CMD_FELICA_ENCAP,Skip reply 18 | E0 [13] 00 [ ] (71) [ ] [ 8 byte IDm ] [ payload_len ] ( ) [ any data ] [ ] 19 | E0 [06] 00 [ ] (71) [01] 00 [ ] 20 | 21 | //SG_NFC_CMD_FELICA_ENCAP,FELICA_CMD_POLL 22 | E0 [13] 00 [ ] (71) 0E [ 8 byte IDm ] 06 (00) [FF FF 01 0F] [ ] 23 | E0 [1A] 00 [ ] (71) 00 [14 14] (01) [ 8 byte IDm ] [ 8 byte PMm ] [ 2 byte system_code ] [ ] 24 | 25 | //SG_NFC_CMD_FELICA_ENCAP,FELICA_CMD_GET_SYSTEM_CODE 26 | E0 [17] 00 [ ] (71) 12 [ 8 byte IDm ] 0A (0C) [ 8 byte IDm ] [ ] 27 | E0 [13] 00 [ ] (71) 00 [0D 0D] (0D) [ 8 byte IDm ] 01 00 00 [ ] 28 | 29 | //SG_NFC_CMD_FELICA_ENCAP,FELICA_CMD_NDA_A4 30 | E0 [18] 00 [ ] (71) 13 [ 8 byte IDm ] 0B (A4) [ 8 byte IDm ] 00 [ ] 31 | E0 [11] 00 [ ] (71) 00 [0B 0B] (A5) [ 8 byte IDm ] 00 [ ] 32 | 33 | //SG_NFC_CMD_FELICA_ENCAP,FELICA_CMD_NDA_06,read block 34 | E0 [1D] 00 [ ] (71) 18 [ 8 byte IDm ] 10 (06) [ 8 byte IDm ] [ Service len ] [ Service Code(little-endian) ] [ Block len ] [ Block Code(big-endian) ] [ ] 35 | E0 [23] 00 [ ] (71) 00 [1D 1D] (07) [ 8 byte IDm ] [00 00] [ Block len ] [ 16 byte Block Data ] [ ] 36 | 37 | //SG_NFC_CMD_FELICA_ENCAP,FELICA_CMD_NDA_06, read multiple blocks,Service Code=0x000B(read-only),Block Code={0x8082,0x8086,0x8090,0x8091} 38 | E0 [23] 00 [ ] (71) 1E [ 8 byte IDm ] 16 (06) [ 8 byte IDm ] [01] [0B 00] [04] [80 82] [80 86] [80 90] [80 91] [ ] 39 | E0 [53] 00 [ ] (71) 00 [4D 4D] (07) [ 8 byte IDm ] [00 00] [04] [ 8 byte IDm ] [ 16*len byte Block Data ] [ ] 40 | 41 | //SG_NFC_CMD_FELICA_ENCAP,FELICA_CMD_NDA_08,write block,Service Code=0x0009(read/write),Block Code=0x8080 42 | E0 [2D] 00 [ ] (71) 28 [ 8 byte IDm ] 20 (08) [ 8 byte IDm ] [01] [09 00] [01] [80 80] [ 16 byte Block Data ] [ ] 43 | E0 [12] 00 [ ] (71) 00 [0C 0C] (09) [ 8 byte IDm ] [00 00] [ ] 44 | 45 | //SG_NFC_CMD_POLL,MIFARE 46 | E0 [05] 00 [ ] (42) 00 [ ] 47 | E0 [0D] 00 [ ] (42) 00 07 01 10 04 [ 4 byte UID ] [ ] 48 | 49 | //SG_NFC_CMD_MIFARE_READ_BLOCK 50 | E0 [0A] 00 [ ] (52) 05 [ 4 byte UID ] [ block_no ] 84 51 | E0 [16] 00 [ ] (52) 00 10 [ 16 byte block data ] [ ] 52 | -------------------------------------------------------------------------------- /doc/nfc.txt: -------------------------------------------------------------------------------- 1 | N.B. Quoted strings are NOT NUL-terminated unless otherwise noted. 2 | Useful reading: https://www.nxp.com/docs/en/data-sheet/MF1S50YYX_V1.pdf 3 | 4 | (AiMe branded cards are Mifare Classic cards. Other technologies exist though) 5 | 6 | Summary 7 | ------- 8 | 9 | Hardware: 10 | Assembly connector: 11 | 5V Host in 12 | Tx;Rx;GND Host RS232 in 13 | Tx;Rx;GND Daisy-chain out 14 | Main board (probably LED controller): 15 | Silk "837-15084" 16 | CN1: Host 5V power in, ?V NFC-sub power out 17 | CN2: Host RS232 Tx;Rx;GND in, NFC-sub Tx;Rx out 18 | CN3: LED-Sub power and data(?) out 19 | DIPSW1: Set to hex nibble 8. 20 | Contains ADM3222 RS232 transceiver IC 21 | Contains ATMega32 MCU 22 | NFC subboard: 23 | Sticker: Model "TN32MSEC003S" 24 | CN1: ?V power and Tx;Rx;GND in, Tx;Rx;GND ass'y CN daisy out. 25 | DIPSW1: Set to hex nibble 0. 26 | Contains ATmega168 MCU 27 | Contains ADM3202A RS232 transceiver IC 28 | Contains shielded RF circuit 29 | Entire non-ground-plane PCB area is visible through the chassis 30 | torx screws lol 31 | LED subboard: 32 | Silk: "837-15120" 33 | CN1: ?V power and Tx;Rx;GND in. 34 | Five RGB LEDs and a bunch of resistors 35 | No visible logic ICs..? 36 | No DIPSW. 37 | 38 | JVS framing: 39 | E0 sync 40 | D0 escape (+1 to unescape) 41 | Checksum is sum of bytes after unescaping 42 | 43 | Frame header: 44 | Frame length (including length byte itself) 45 | Address 46 | Sequence no, hopefully loops before hitting esc byte... 47 | Command byte 48 | 49 | Bus addressing: 50 | Low nibble set using DIPSWs 51 | High nibble ??? 52 | Daisy chaining mechanism unknown (RS232 wires probably multi-tap) 53 | 54 | Startup 55 | ------- 56 | 57 | Addr 00 Command 62: 58 | Req: 59 | 00 Payload length 60 | Resp: 61 | 00 Status byte 62 | 00 Payload length 63 | Description: 64 | Unknown. Reset? 65 | 66 | Addr 00 Command 30: 67 | Req: 68 | 00 Payload length 69 | Resp: 70 | 00 Status byte 71 | 17 Payload length 72 | .. "TN32MSEC003S F/W Ver1.2E" 73 | Description: 74 | Get firmware version 75 | 76 | Addr 00 Command 32: 77 | Req: 78 | 00 Payload length 79 | Resp: 80 | 00 Status byte 81 | 17 Payload length 82 | .. "TN32MSEC003S H/W Ver3.0J" 83 | Description: 84 | Get hardware version 85 | 86 | Addr 08 Command f5: 87 | Req: 88 | 00 Payload length 89 | Resp: 90 | 00 Status byte 91 | 00 Payload length 92 | Description: 93 | LED sub-board reset. 94 | Won't accept LED commands until you do this. 95 | 96 | Addr 08 Command f0: 97 | Req: 98 | 00 Payload length 99 | Resp: 100 | 00 Status byte 101 | 09 Payload length 102 | .. "15084" (part nr for LED board) 103 | FF ?? 104 | 11 ?? 105 | 00 ?? 106 | 12 ?? 107 | Description: 108 | Get board "info" 109 | 110 | Addr 00 Command 54: 111 | Req: 112 | 06 Payload length 113 | 57 'W' 114 | 43 'C' 115 | 43 'C' 116 | 46 'F' 117 | 76 'v' 118 | 32 '2' 119 | Resp: 120 | 00 Status byte? 121 | 00 ?? 122 | Description: 123 | Set Mifare KeyA. 124 | "WCCF" might refer this this SEGA arcade game: 125 | https://en.wikipedia.org/wiki/World_Club_Champion_Football 126 | It's quite old and has AiMe readers, maybe where they first appeared? 127 | 128 | Addr 00 Command 50: 129 | Req: 130 | 06 Payload length 131 | 60 ?? 132 | 90 ?? 133 | D0 ?? (This is escaped of course) 134 | 06 ?? 135 | 32 ?? 136 | F5 ?? 137 | Resp: 138 | 00 Status byte 139 | 00 Payload length 140 | Description: 141 | Possibly Mifare KeyB. 142 | 143 | Polling 144 | ------- 145 | 146 | Addr 00 Command 40: 147 | Req: 148 | 01 Payload length 149 | 03 ?? 150 | Resp: 151 | 00 Status byte 152 | 00 Payload length 153 | Description: 154 | Poll some other NFC technology? 155 | 156 | Addr 00 Command 42: 157 | Req: 158 | 00 Payload length 159 | Resp if no MiFare card: 160 | 00 Status byte 161 | 01 Payload length 162 | 00 (represents nothing i guess) 163 | Resp if MiFare card: 164 | 00 Status byte? 165 | 07 Payload length 166 | 01 Chunk length? 167 | 10 ?? Block size maybe? 168 | 04 Chunk length? 169 | .. Mifare UID, four bytes. 170 | Description: 171 | Check for Mifare card presence? 172 | 173 | Addr 00 Command 41: 174 | Req: 175 | 00 Payload length 176 | Resp: 177 | 00 Status byte 178 | 00 Payload length 179 | Description: 180 | Unknown. Poll some other NFC technology? 181 | 182 | Card read 183 | --------- 184 | 185 | Addr 00 Command 43: 186 | Req: 187 | 04 Payload length 188 | .. Mifare UID, four bytes. 189 | Resp: 190 | 00 Status byte 191 | 00 Payload length 192 | Description: 193 | Select MiFare by UID? 194 | 195 | Addr 00 Command 55: 196 | Req: 197 | 05 Payload length 198 | .. Mifare UID, four bytes. 199 | 03 ?? 200 | Resp: 201 | 00 Status byte 202 | 00 Payload length 203 | Description: 204 | Unknown. 205 | Block 3 on a Mifare sector contains keys and an access control list. 206 | It is generally not accessed directly (unless being provisioned?) 207 | 208 | Addr 00 Command 52: 209 | Req: 210 | 05 Payload length 211 | .. Mifare UID, four bytes. 212 | .. Block number, 1 or 2. 213 | Resp for Block 1: 214 | 00 Status byte 215 | 10 Payload length (1 block) 216 | .. "SBSD" 217 | 00 00 00 00 218 | 00 00 00 00 219 | 00 4E C6 22 220 | Resp for Block 2: 221 | 00 Status byte 222 | 10 Payload length (1 block) 223 | .. 00 00 00 00 00 00 xx xx 224 | xx xx xx xx xx xx xx xx 225 | Description: 226 | Probably reads blocks 1 and 2 from Mifare sector 0. 227 | Block 0 contains the "vendor information" and UID. 228 | Block 1 contents are unknown, probably AiMe DB info. 229 | Block 2 last 10 bytes hex are printed on the card ("local unique id"). 230 | (Block 3 contains encryption keys so is not allowed to be read) 231 | 232 | LED 233 | --- 234 | 235 | Addr 08 Command 81: 236 | Req: 237 | 03 Payload length 238 | ff Red intensity 239 | ff Green intensity 240 | ff Blue intensity 241 | Resp: 242 | None! Command is not acknowledged 243 | Description: 244 | Set LED color 245 | -------------------------------------------------------------------------------- /tools/DTR-RTS.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() { 4 | HANDLE com; 5 | com = CreateFile("COM1", GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 6 | FILE_ATTRIBUTE_NORMAL, 0); 7 | DCB dcbSerialParams = {0}; 8 | COMMTIMEOUTS timeouts = {0}; 9 | dcbSerialParams.DCBlength = sizeof(dcbSerialParams); 10 | GetCommState(com, &dcbSerialParams); 11 | dcbSerialParams.BaudRate = 115200; 12 | dcbSerialParams.ByteSize = 8; 13 | dcbSerialParams.StopBits = ONESTOPBIT; 14 | dcbSerialParams.Parity = NOPARITY; 15 | SetCommState(com, &dcbSerialParams); 16 | timeouts.ReadIntervalTimeout = 1; 17 | timeouts.ReadTotalTimeoutConstant = 1; 18 | timeouts.ReadTotalTimeoutMultiplier = 1; 19 | timeouts.WriteTotalTimeoutConstant = 1; 20 | timeouts.WriteTotalTimeoutMultiplier = 1; 21 | SetCommTimeouts(com, &timeouts); 22 | EscapeCommFunction(com, SETDTR); 23 | EscapeCommFunction(com, SETRTS); 24 | 25 | com = CreateFile("\\\\.\\COM12", GENERIC_READ | GENERIC_WRITE, 0, 0, 26 | OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); 27 | dcbSerialParams.DCBlength = sizeof(dcbSerialParams); 28 | GetCommState(com, &dcbSerialParams); 29 | dcbSerialParams.BaudRate = 38400; 30 | dcbSerialParams.ByteSize = 8; 31 | dcbSerialParams.StopBits = ONESTOPBIT; 32 | dcbSerialParams.Parity = NOPARITY; 33 | SetCommState(com, &dcbSerialParams); 34 | timeouts.ReadIntervalTimeout = 1; 35 | timeouts.ReadTotalTimeoutConstant = 1; 36 | timeouts.ReadTotalTimeoutMultiplier = 1; 37 | timeouts.WriteTotalTimeoutConstant = 1; 38 | timeouts.WriteTotalTimeoutMultiplier = 1; 39 | SetCommTimeouts(com, &timeouts); 40 | EscapeCommFunction(com, SETDTR); 41 | EscapeCommFunction(com, SETRTS); 42 | 43 | return 0; 44 | } 45 | 46 | // 47 | -------------------------------------------------------------------------------- /tools/DTR-RTS.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/necoarcdotcom/Arduino-Aime-Reader/a68763bb8993ec3cd3ed6f34a2f38ec63369c37c/tools/DTR-RTS.exe -------------------------------------------------------------------------------- /tools/ReaderTest/ReaderTest.ino: -------------------------------------------------------------------------------- 1 | #if defined(__AVR_ATmega32U4__) || defined(ARDUINO_SAMD_ZERO) 2 | #pragma message "Current development boards are ATmega32U4 or SAMD ZERO" 3 | #define SerialDevice SerialUSB 4 | #define PN532_SPI_SS 10 //When 32U4 does not use GPU, executing Read Without Encryption will fail 5 | 6 | #elif defined(ARDUINO_ESP8266_NODEMCU_ESP12E) 7 | #pragma message "The current development board is NODEMCU ESP12E" 8 | #define SerialDevice Serial 9 | 10 | #elif defined(ARDUINO_NodeMCU_32S) 11 | #pragma message "The current development board is NodeMCU 32S" 12 | #define SerialDevice Serial 13 | #define PN532_SPI_SS 5 14 | 15 | #else 16 | #error "Untested development board, please check the serial port and pin definitions" 17 | #endif 18 | 19 | #if defined(PN532_SPI_SS) 20 | #pragma message "Connect PN532 using SPI" 21 | #include 22 | #include 23 | PN532_SPI pn532(SPI, PN532_SPI_SS); 24 | #else 25 | #include 26 | #include 27 | PN532_I2C pn532(Wire); 28 | #endif 29 | 30 | #include "PN532.h" 31 | PN532 nfc(pn532); 32 | 33 | typedef union { 34 | uint8_t block[18]; 35 | struct { 36 | uint8_t IDm[8]; 37 | uint8_t PMm[8]; 38 | union { 39 | uint16_t SystemCode; 40 | uint8_t System_Code[2]; 41 | }; 42 | }; 43 | } Card; 44 | Card card; 45 | 46 | uint8_t AimeKey[6] = {0x57, 0x43, 0x43, 0x46, 0x76, 0x32}; 47 | uint8_t BanaKey[6] = {0x60, 0x90, 0xD0, 0x06, 0x32, 0xF5}; 48 | uint8_t MifareKey[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; 49 | #define M2F_B 1 50 | uint16_t blockList[4] = {0x8080, 0x8081, 0x8082, 0x8083}; 51 | uint16_t serviceCodeList[1] = {0x000B}; 52 | uint8_t blockData[1][16]; 53 | 54 | void setup() { 55 | SerialDevice.begin(115200); 56 | // Wire.setClock(800000); 57 | while (!SerialDevice); 58 | nfc.begin(); 59 | while (!nfc.getFirmwareVersion()) { 60 | SerialDevice.println("Didn't find PN53x board"); 61 | delay(500); 62 | } 63 | SerialDevice.println("START!"); 64 | nfc.setPassiveActivationRetries(0x10); 65 | nfc.SAMConfig(); 66 | } 67 | 68 | void loop() { 69 | uint8_t uid[4], uL; 70 | 71 | if (nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uL) && nfc.mifareclassic_AuthenticateBlock(uid, uL, 1, 1, AimeKey)) { 72 | SerialDevice.println("Aime card!"); 73 | SerialDevice.print("UID Value:"); 74 | nfc.PrintHex(uid, uL); 75 | SerialDevice.print("Block 2 Data:"); 76 | if (nfc.mifareclassic_ReadDataBlock(2, card.block)) { 77 | nfc.PrintHex(card.block, 16); 78 | } 79 | delay(2000); 80 | return; 81 | } 82 | if (nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uL) && nfc.mifareclassic_AuthenticateBlock(uid, uL, 1, 0, BanaKey)) { 83 | SerialDevice.println("Banapassport card!"); 84 | SerialDevice.print("UID Value:"); 85 | nfc.PrintHex(uid, uL); 86 | SerialDevice.print("Block 2 Data:"); 87 | if (nfc.mifareclassic_ReadDataBlock(2, card.block)) { 88 | nfc.PrintHex(card.block, 16); 89 | } 90 | delay(2000); 91 | return; 92 | } 93 | if (nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uL) && nfc.mifareclassic_AuthenticateBlock(uid, uL, M2F_B, 0, MifareKey)) { 94 | SerialDevice.println("Default Key Mifare!"); 95 | if (nfc.mifareclassic_ReadDataBlock(2, card.block)) { 96 | SerialDevice.print("Fake IDm:"); 97 | nfc.PrintHex(card.IDm, 8); 98 | SerialDevice.print("Fake PMm:"); 99 | nfc.PrintHex(card.PMm, 8); 100 | } 101 | delay(2000); 102 | return; 103 | } 104 | if (nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uL)) { 105 | SerialDevice.println("Unknown key Mifare."); 106 | SerialDevice.print("UID Value:"); 107 | nfc.PrintHex(uid, uL); 108 | delay(2000); 109 | return; 110 | } 111 | 112 | if (nfc.felica_Polling(0xFFFF, 0x01, card.IDm, card.PMm, &card.SystemCode, 200)) { 113 | SerialDevice.println("FeliCa card!"); 114 | SerialDevice.print("IDm:"); 115 | nfc.PrintHex(card.IDm, 8); 116 | SerialDevice.print("PMm:"); 117 | nfc.PrintHex(card.PMm, 8); 118 | SerialDevice.print("SystemCode:"); 119 | card.SystemCode = card.SystemCode >> 8 | card.SystemCode << 8; 120 | nfc.PrintHex(card.System_Code, 2); 121 | 122 | 123 | Serial.println("FeliCa Block:"); 124 | for (uint8_t i = 0; i < 4; i++) { 125 | if (nfc.felica_ReadWithoutEncryption(1, serviceCodeList, 1, &blockList[i], blockData) == 1) { 126 | Serial.println(blockList[i], HEX); 127 | nfc.PrintHex(blockData[0], 16); 128 | } else { 129 | Serial.println("error"); 130 | } 131 | } 132 | delay(2000); 133 | return; 134 | } 135 | SerialDevice.println("Didn't find card"); 136 | delay(500); 137 | } 138 | -------------------------------------------------------------------------------- /tools/chunihook.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/necoarcdotcom/Arduino-Aime-Reader/a68763bb8993ec3cd3ed6f34a2f38ec63369c37c/tools/chunihook.dll -------------------------------------------------------------------------------- /tools/sg-cmd.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "board/sg-cmd.h" 4 | #include "board/sg-frame.h" 5 | 6 | #include "hook/iobuf.h" 7 | 8 | #include "util/dprintf.h" 9 | 10 | union sg_req_any { 11 | struct sg_req_header req; 12 | uint8_t bytes[256]; 13 | }; 14 | 15 | union sg_res_any { 16 | struct sg_res_header res; 17 | uint8_t bytes[256]; 18 | }; 19 | 20 | static HRESULT sg_req_validate(const void *ptr, size_t nbytes); 21 | 22 | static void sg_res_error( 23 | struct sg_res_header *res, 24 | const struct sg_req_header *req); 25 | 26 | static HRESULT sg_req_validate(const void *ptr, size_t nbytes) 27 | { 28 | const struct sg_req_header *req; 29 | size_t payload_len; 30 | 31 | assert(ptr != NULL); 32 | 33 | if (nbytes < sizeof(*req)) { 34 | dprintf("SG Cmd: Request header truncated\n"); 35 | 36 | return E_FAIL; 37 | } 38 | 39 | req = ptr; 40 | 41 | if (req->hdr.frame_len != nbytes) { 42 | dprintf("SG Cmd: Frame length mismatch: got %i exp %i\n", 43 | req->hdr.frame_len, 44 | (int) nbytes); 45 | 46 | return E_FAIL; 47 | } 48 | 49 | payload_len = req->hdr.frame_len - sizeof(*req); 50 | 51 | if (req->payload_len != payload_len) { 52 | dprintf("SG Cmd: Payload length mismatch: got %i exp %i\n", 53 | req->payload_len, 54 | (int) payload_len); 55 | 56 | return E_FAIL; 57 | } 58 | 59 | return S_OK; 60 | } 61 | 62 | void sg_req_transact( 63 | struct iobuf *res_frame, 64 | const uint8_t *req_bytes, 65 | size_t req_nbytes, 66 | sg_dispatch_fn_t dispatch, 67 | void *ctx) 68 | { 69 | struct iobuf req_span; 70 | union sg_req_any req; 71 | union sg_res_any res; 72 | HRESULT hr; 73 | 74 | assert(res_frame != NULL); 75 | assert(req_bytes != NULL); 76 | assert(dispatch != NULL); 77 | 78 | req_span.bytes = req.bytes; 79 | req_span.nbytes = sizeof(req.bytes); 80 | req_span.pos = 0; 81 | 82 | hr = sg_frame_decode(&req_span, req_bytes, req_nbytes); 83 | 84 | if (FAILED(hr)) { 85 | return; 86 | } 87 | 88 | hr = sg_req_validate(req.bytes, req_span.pos); 89 | 90 | if (FAILED(hr)) { 91 | return; 92 | } 93 | 94 | hr = dispatch(ctx, &req, &res); 95 | 96 | if (hr != S_FALSE) { 97 | if (FAILED(hr)) { 98 | sg_res_error(&res.res, &req.req); 99 | } 100 | 101 | sg_frame_encode(res_frame, res.bytes, res.res.hdr.frame_len); 102 | printf("req: "); 103 | for (uint8_t i = 0; i < req_nbytes; i++) 104 | printf("%02X ", req_bytes[i]); 105 | printf("\n"); 106 | printf("res: "); 107 | for (uint8_t i = 0; i < res_frame->pos; i++) 108 | printf("%02X ", res_frame->bytes[i]); 109 | printf("\n"); 110 | } 111 | } 112 | 113 | void sg_res_init( 114 | struct sg_res_header *res, 115 | const struct sg_req_header *req, 116 | size_t payload_len) 117 | { 118 | assert(res != NULL); 119 | assert(req != NULL); 120 | 121 | res->hdr.frame_len = sizeof(*res) + payload_len; 122 | res->hdr.addr = req->hdr.addr; 123 | res->hdr.seq_no = req->hdr.seq_no; 124 | res->hdr.cmd = req->hdr.cmd; 125 | res->status = 0; 126 | res->payload_len = payload_len; 127 | } 128 | 129 | static void sg_res_error( 130 | struct sg_res_header *res, 131 | const struct sg_req_header *req) 132 | { 133 | assert(res != NULL); 134 | assert(req != NULL); 135 | 136 | res->hdr.frame_len = sizeof(*res); 137 | res->hdr.addr = req->hdr.addr; 138 | res->hdr.seq_no = req->hdr.seq_no; 139 | res->hdr.cmd = req->hdr.cmd; 140 | res->status = 1; 141 | res->payload_len = 0; 142 | } 143 | 144 | --------------------------------------------------------------------------------