| 1 | /* |
|---|
| 2 | * Driver for Towitoko readers |
|---|
| 3 | * |
|---|
| 4 | * Copyright (C) 2003, Olaf Kirch <okir@suse.de> |
|---|
| 5 | */ |
|---|
| 6 | |
|---|
| 7 | #include "internal.h" |
|---|
| 8 | #include <unistd.h> |
|---|
| 9 | #include <stdio.h> |
|---|
| 10 | #include <string.h> |
|---|
| 11 | |
|---|
| 12 | static int twt_led(ifd_reader_t *, int); |
|---|
| 13 | static int twt_try_reset(ifd_reader_t *, const void *, size_t, void *, size_t); |
|---|
| 14 | static int twt_command(ifd_reader_t *, const void *, size_t, void *, size_t); |
|---|
| 15 | static int twt_recv_checksum(const unsigned char *, size_t); |
|---|
| 16 | static size_t twt_send_checksum(unsigned char *, size_t); |
|---|
| 17 | |
|---|
| 18 | enum { |
|---|
| 19 | TWT_LED_OFF = 0, |
|---|
| 20 | TWT_LED_RED, |
|---|
| 21 | TWT_LED_GREEN, |
|---|
| 22 | TWT_LED_YELLOW |
|---|
| 23 | }; |
|---|
| 24 | |
|---|
| 25 | #define TWT_PAGESIZE 15 |
|---|
| 26 | |
|---|
| 27 | /* |
|---|
| 28 | * Initialize the reader |
|---|
| 29 | */ |
|---|
| 30 | static int twt_open(ifd_reader_t * reader, const char *device_name) |
|---|
| 31 | { |
|---|
| 32 | ifd_device_params_t params; |
|---|
| 33 | ifd_device_t *dev; |
|---|
| 34 | unsigned char buffer[256]; |
|---|
| 35 | |
|---|
| 36 | ifd_debug(1, "called, device=%s", device_name); |
|---|
| 37 | |
|---|
| 38 | reader->name = "Towitoko Reader"; |
|---|
| 39 | reader->nslots = 1; |
|---|
| 40 | |
|---|
| 41 | if (!(dev = ifd_device_open(device_name))) |
|---|
| 42 | return -1; |
|---|
| 43 | reader->device = dev; |
|---|
| 44 | |
|---|
| 45 | if (dev->type == IFD_DEVICE_TYPE_SERIAL) { |
|---|
| 46 | if (ifd_device_get_parameters(dev, ¶ms) < 0) |
|---|
| 47 | return -1; |
|---|
| 48 | |
|---|
| 49 | params.serial.speed = 9600; |
|---|
| 50 | params.serial.bits = 8; |
|---|
| 51 | params.serial.stopbits = 2; |
|---|
| 52 | params.serial.parity = IFD_SERIAL_PARITY_EVEN; |
|---|
| 53 | params.serial.dtr = 1; |
|---|
| 54 | params.serial.rts = 1; |
|---|
| 55 | |
|---|
| 56 | if (ifd_device_set_parameters(dev, ¶ms) < 0) |
|---|
| 57 | return -1; |
|---|
| 58 | } |
|---|
| 59 | |
|---|
| 60 | sleep(1); |
|---|
| 61 | ifd_device_flush(dev); |
|---|
| 62 | |
|---|
| 63 | if (twt_command(reader, "\x00", 1, buffer, 2) < 0) |
|---|
| 64 | goto failed; |
|---|
| 65 | |
|---|
| 66 | ifd_debug(1, "towitoko reader type 0x%02x", buffer[0]); |
|---|
| 67 | |
|---|
| 68 | /* Special handling for some towitoko readers |
|---|
| 69 | * (according to SCEZ) */ |
|---|
| 70 | switch (buffer[0]) { |
|---|
| 71 | case 0x61: |
|---|
| 72 | reader->name = "Towitoko Chipdrive Micro"; |
|---|
| 73 | break; |
|---|
| 74 | case 0x80: /* Kartenzwerg */ |
|---|
| 75 | reader->name = "Towitoko Kartenzwerg"; |
|---|
| 76 | params.serial.stopbits = 1; |
|---|
| 77 | params.serial.parity = IFD_SERIAL_PARITY_NONE; |
|---|
| 78 | /* XXX - Kartenzwerg is for synchronous cards |
|---|
| 79 | * only. Should we have a flag for this? */ |
|---|
| 80 | break; |
|---|
| 81 | case 0x64: |
|---|
| 82 | reader->name = "Towitoko Kartenzwerg II"; |
|---|
| 83 | params.serial.stopbits = 1; |
|---|
| 84 | params.serial.parity = IFD_SERIAL_PARITY_NONE; |
|---|
| 85 | break; |
|---|
| 86 | case 0x84: |
|---|
| 87 | reader->name = "Towitoko Chipdrive External"; |
|---|
| 88 | break; |
|---|
| 89 | case 0x88: /* Twin */ |
|---|
| 90 | reader->name = "Towitoko Chipdrive Twin"; |
|---|
| 91 | reader->nslots = 2; |
|---|
| 92 | params.serial.rts = 0; |
|---|
| 93 | break; |
|---|
| 94 | case 0x90: |
|---|
| 95 | reader->name = "Towitoko Chipdrive Internal"; |
|---|
| 96 | break; |
|---|
| 97 | default: |
|---|
| 98 | reader->name = "Towitoko"; |
|---|
| 99 | } |
|---|
| 100 | |
|---|
| 101 | if (ifd_device_set_parameters(dev, ¶ms) < 0) |
|---|
| 102 | return -1; |
|---|
| 103 | |
|---|
| 104 | return 0; |
|---|
| 105 | |
|---|
| 106 | failed: |
|---|
| 107 | ct_error("towitoko: failed to initialize device"); |
|---|
| 108 | return -1; |
|---|
| 109 | } |
|---|
| 110 | |
|---|
| 111 | /* |
|---|
| 112 | * Activate the reader |
|---|
| 113 | */ |
|---|
| 114 | static int twt_activate(ifd_reader_t * reader) |
|---|
| 115 | { |
|---|
| 116 | unsigned char cmd[2] = { 0x60, 0x0F }; |
|---|
| 117 | |
|---|
| 118 | ifd_debug(1, "called."); |
|---|
| 119 | if (twt_command(reader, cmd, sizeof(cmd), NULL, 0) < 0) |
|---|
| 120 | return -1; |
|---|
| 121 | |
|---|
| 122 | return 0; |
|---|
| 123 | } |
|---|
| 124 | |
|---|
| 125 | static int twt_deactivate(ifd_reader_t * reader) |
|---|
| 126 | { |
|---|
| 127 | unsigned char cmd[2] = { 0x61, 0x0F }; |
|---|
| 128 | |
|---|
| 129 | ifd_debug(1, "called."); |
|---|
| 130 | if (twt_command(reader, cmd, sizeof(cmd), NULL, 0) < 0) |
|---|
| 131 | return -1; |
|---|
| 132 | return 0; |
|---|
| 133 | } |
|---|
| 134 | |
|---|
| 135 | static int twt_close(ifd_reader_t * reader) |
|---|
| 136 | { |
|---|
| 137 | twt_led(reader, TWT_LED_OFF); |
|---|
| 138 | return 0; |
|---|
| 139 | } |
|---|
| 140 | |
|---|
| 141 | /* |
|---|
| 142 | * Check card status |
|---|
| 143 | */ |
|---|
| 144 | static int twt_card_status(ifd_reader_t * reader, int slot, int *status) |
|---|
| 145 | { |
|---|
| 146 | unsigned char byte; |
|---|
| 147 | int r; |
|---|
| 148 | |
|---|
| 149 | if (slot != 0) { |
|---|
| 150 | ct_error("towitoko: bad slot index %u", slot); |
|---|
| 151 | return IFD_ERROR_INVALID_SLOT; |
|---|
| 152 | } |
|---|
| 153 | |
|---|
| 154 | if ((r = twt_command(reader, "\x03", 1, &byte, 1)) < 0) |
|---|
| 155 | return r; |
|---|
| 156 | |
|---|
| 157 | *status = 0; |
|---|
| 158 | if (byte & 0x40) |
|---|
| 159 | *status |= IFD_CARD_PRESENT; |
|---|
| 160 | if (byte & 0x80) |
|---|
| 161 | *status |= IFD_CARD_STATUS_CHANGED; |
|---|
| 162 | |
|---|
| 163 | twt_led(reader, (byte & 0x40) ? TWT_LED_RED : TWT_LED_OFF); |
|---|
| 164 | |
|---|
| 165 | return 0; |
|---|
| 166 | } |
|---|
| 167 | |
|---|
| 168 | /* |
|---|
| 169 | * Reset the card and get the ATR |
|---|
| 170 | */ |
|---|
| 171 | static int twt_card_reset(ifd_reader_t * reader, int slot, void *atr, |
|---|
| 172 | size_t size) |
|---|
| 173 | { |
|---|
| 174 | static unsigned char reset1[] = { 0x80, 0x6F, 0x00, 0x05, 0x76 }; |
|---|
| 175 | static unsigned char reset2[] = { 0xA0, 0x6F, 0x00, 0x05, 0x74 }; |
|---|
| 176 | int r, i, n = 0, status; |
|---|
| 177 | |
|---|
| 178 | ifd_debug(1, "called."); |
|---|
| 179 | |
|---|
| 180 | if (slot != 0) { |
|---|
| 181 | ct_error("towitoko: bad slot index %u", slot); |
|---|
| 182 | return IFD_ERROR_INVALID_SLOT; |
|---|
| 183 | } |
|---|
| 184 | |
|---|
| 185 | /* Activate the reader */ |
|---|
| 186 | if ((r = twt_activate(reader)) < 0) |
|---|
| 187 | return r; |
|---|
| 188 | |
|---|
| 189 | /* Get the card status */ |
|---|
| 190 | if ((r = twt_card_status(reader, slot, &status)) < 0) |
|---|
| 191 | return r; |
|---|
| 192 | |
|---|
| 193 | if (!(status & IFD_CARD_PRESENT)) |
|---|
| 194 | return IFD_ERROR_NO_CARD; |
|---|
| 195 | |
|---|
| 196 | /* SCEZ does this three times - I have no clue why */ |
|---|
| 197 | for (i = 0; i < 1; i++) { |
|---|
| 198 | n = twt_try_reset(reader, reset1, sizeof(reset1), atr, size); |
|---|
| 199 | if (n != 0) |
|---|
| 200 | return n; |
|---|
| 201 | n = twt_try_reset(reader, reset2, sizeof(reset2), atr, size); |
|---|
| 202 | if (n != 0) |
|---|
| 203 | return n; |
|---|
| 204 | } |
|---|
| 205 | |
|---|
| 206 | /* See if this is a synchronous card */ |
|---|
| 207 | return ifd_sync_detect_icc(reader, slot, atr, size); |
|---|
| 208 | } |
|---|
| 209 | |
|---|
| 210 | static int twt_try_reset(ifd_reader_t * reader, const void *cmd, size_t cmd_len, |
|---|
| 211 | void *atr, size_t atr_len) |
|---|
| 212 | { |
|---|
| 213 | ifd_device_t *dev = reader->device; |
|---|
| 214 | int rc; |
|---|
| 215 | |
|---|
| 216 | ifd_debug(2, "sending %s", ct_hexdump(cmd, cmd_len)); |
|---|
| 217 | |
|---|
| 218 | ct_config.suppress_errors++; |
|---|
| 219 | if (ifd_device_type(dev) != IFD_DEVICE_TYPE_SERIAL) { |
|---|
| 220 | rc = ifd_device_transceive(dev, cmd, cmd_len, |
|---|
| 221 | atr, atr_len, 1000); |
|---|
| 222 | } else { |
|---|
| 223 | if (ifd_device_send(dev, (const unsigned char *)cmd, cmd_len) < |
|---|
| 224 | 0) |
|---|
| 225 | return -1; |
|---|
| 226 | rc = ifd_device_recv(dev, (unsigned char *)atr, 1, 1000); |
|---|
| 227 | } |
|---|
| 228 | ct_config.suppress_errors--; |
|---|
| 229 | |
|---|
| 230 | if (rc == IFD_ERROR_TIMEOUT) |
|---|
| 231 | return 0; |
|---|
| 232 | |
|---|
| 233 | if (rc == 1) { |
|---|
| 234 | unsigned char c = *(unsigned char *)atr; |
|---|
| 235 | |
|---|
| 236 | ifd_debug(1, "received first ATR byte: 0x%02x", c); |
|---|
| 237 | if (c != 0x3f && c != 0x3b && c != 0x03) |
|---|
| 238 | return 0; |
|---|
| 239 | } |
|---|
| 240 | |
|---|
| 241 | return rc; |
|---|
| 242 | } |
|---|
| 243 | |
|---|
| 244 | /* |
|---|
| 245 | * Change the parity |
|---|
| 246 | */ |
|---|
| 247 | static int twt_change_parity(ifd_reader_t * reader, int parity) |
|---|
| 248 | { |
|---|
| 249 | unsigned char cmd[] = { 0x6F, 0x00, 0x6A, 0x0F }; |
|---|
| 250 | ifd_device_t *dev = reader->device; |
|---|
| 251 | ifd_device_params_t params; |
|---|
| 252 | int r; |
|---|
| 253 | |
|---|
| 254 | if (dev->type != IFD_DEVICE_TYPE_SERIAL) |
|---|
| 255 | return IFD_ERROR_NOT_SUPPORTED; |
|---|
| 256 | |
|---|
| 257 | if (ifd_device_get_parameters(dev, ¶ms) < 0) |
|---|
| 258 | return -1; |
|---|
| 259 | |
|---|
| 260 | switch (parity) { |
|---|
| 261 | case IFD_SERIAL_PARITY_EVEN: |
|---|
| 262 | cmd[1] = 0x40; |
|---|
| 263 | break; |
|---|
| 264 | case IFD_SERIAL_PARITY_ODD: |
|---|
| 265 | cmd[1] = 0x80; |
|---|
| 266 | break; |
|---|
| 267 | default: |
|---|
| 268 | ct_error("towitoko: parity NONE not supported"); |
|---|
| 269 | return IFD_ERROR_NOT_SUPPORTED; |
|---|
| 270 | } |
|---|
| 271 | |
|---|
| 272 | if ((r = twt_command(reader, cmd, 4, NULL, 0)) < 0) { |
|---|
| 273 | ct_error("towitoko: failed to change parity"); |
|---|
| 274 | return r; |
|---|
| 275 | } |
|---|
| 276 | |
|---|
| 277 | params.serial.parity = parity; |
|---|
| 278 | return ifd_device_set_parameters(dev, ¶ms); |
|---|
| 279 | } |
|---|
| 280 | |
|---|
| 281 | /* |
|---|
| 282 | * Change the serial speed |
|---|
| 283 | */ |
|---|
| 284 | static struct twt_speed { |
|---|
| 285 | unsigned int value; |
|---|
| 286 | unsigned char c1, c2; |
|---|
| 287 | } twt_speed[] = { |
|---|
| 288 | { |
|---|
| 289 | 1200, 0x60, 0x07}, { |
|---|
| 290 | 2400, 0x2E, 0x03}, { |
|---|
| 291 | 4800, 0x17, 0x05}, { |
|---|
| 292 | 9600, 0x0B, 0x02}, { |
|---|
| 293 | 14400, 0x07, 0x01}, { |
|---|
| 294 | 19200, 0x05, 0x02}, { |
|---|
| 295 | 28800, 0x03, 0x00}, { |
|---|
| 296 | 38400, 0x02, 0x00}, { |
|---|
| 297 | 57600, 0x01, 0x00}, { |
|---|
| 298 | 115200, 0x80, 0x00}, { |
|---|
| 299 | 0} |
|---|
| 300 | }; |
|---|
| 301 | |
|---|
| 302 | static int twt_change_speed(ifd_reader_t * reader, unsigned int speed) |
|---|
| 303 | { |
|---|
| 304 | unsigned char cmd[] = { 0x6E, 0x00, 0x00, 0x00, 0x08 }; |
|---|
| 305 | ifd_device_t *dev = reader->device; |
|---|
| 306 | struct twt_speed *spd; |
|---|
| 307 | ifd_device_params_t params; |
|---|
| 308 | int r; |
|---|
| 309 | |
|---|
| 310 | if (dev->type != IFD_DEVICE_TYPE_SERIAL) |
|---|
| 311 | return IFD_ERROR_NOT_SUPPORTED; |
|---|
| 312 | |
|---|
| 313 | if ((r = ifd_device_get_parameters(dev, ¶ms)) < 0) |
|---|
| 314 | return r; |
|---|
| 315 | |
|---|
| 316 | for (spd = twt_speed; spd->value; spd++) { |
|---|
| 317 | if (speed <= spd->value) |
|---|
| 318 | break; |
|---|
| 319 | } |
|---|
| 320 | if (spd->value == 0) |
|---|
| 321 | return IFD_ERROR_NOT_SUPPORTED; |
|---|
| 322 | |
|---|
| 323 | params.serial.speed = spd->value; |
|---|
| 324 | cmd[1] = spd->c1; |
|---|
| 325 | cmd[3] = spd->c2; |
|---|
| 326 | |
|---|
| 327 | if ((r = twt_command(reader, cmd, sizeof(cmd), NULL, 0)) < 0) { |
|---|
| 328 | ct_error("towitoko: failed to change speed"); |
|---|
| 329 | return r; |
|---|
| 330 | } |
|---|
| 331 | |
|---|
| 332 | return ifd_device_set_parameters(dev, ¶ms); |
|---|
| 333 | } |
|---|
| 334 | |
|---|
| 335 | /* |
|---|
| 336 | * Send command to IFD |
|---|
| 337 | */ |
|---|
| 338 | static int twt_send(ifd_reader_t * reader, unsigned int dad, |
|---|
| 339 | const unsigned char *buffer, size_t len) |
|---|
| 340 | { |
|---|
| 341 | unsigned char cmd[] = { 0x6F, 0x00, 0x05, 0x00 }; |
|---|
| 342 | unsigned int count; |
|---|
| 343 | ifd_device_t *dev; |
|---|
| 344 | |
|---|
| 345 | if (!(dev = reader->device)) |
|---|
| 346 | return -1; |
|---|
| 347 | |
|---|
| 348 | ifd_debug(3, "data:%s", ct_hexdump(buffer, len)); |
|---|
| 349 | while (len) { |
|---|
| 350 | if ((count = len) > 255) |
|---|
| 351 | count = 255; |
|---|
| 352 | |
|---|
| 353 | cmd[1] = count; |
|---|
| 354 | twt_send_checksum(cmd, 3); |
|---|
| 355 | |
|---|
| 356 | if (ifd_device_send(dev, cmd, 4) < 0 |
|---|
| 357 | || ifd_device_send(dev, buffer, count) < 0) |
|---|
| 358 | return -1; |
|---|
| 359 | |
|---|
| 360 | buffer += count; |
|---|
| 361 | len -= count; |
|---|
| 362 | } |
|---|
| 363 | |
|---|
| 364 | return 0; |
|---|
| 365 | } |
|---|
| 366 | |
|---|
| 367 | /* |
|---|
| 368 | * Receive data from IFD |
|---|
| 369 | */ |
|---|
| 370 | static int twt_recv(ifd_reader_t * reader, unsigned int dad, |
|---|
| 371 | unsigned char *buffer, size_t len, long timeout) |
|---|
| 372 | { |
|---|
| 373 | int n; |
|---|
| 374 | |
|---|
| 375 | n = ifd_device_recv(reader->device, buffer, len, timeout); |
|---|
| 376 | if (n < 0) |
|---|
| 377 | return -1; |
|---|
| 378 | ifd_debug(3, "data:%s", ct_hexdump(buffer, len)); |
|---|
| 379 | return n; |
|---|
| 380 | } |
|---|
| 381 | |
|---|
| 382 | /* |
|---|
| 383 | * Read synchronous card |
|---|
| 384 | */ |
|---|
| 385 | static int twt_sync_read_buffer(ifd_reader_t * reader, int slot, int proto, |
|---|
| 386 | unsigned char *buffer, size_t len) |
|---|
| 387 | { |
|---|
| 388 | size_t total = 0; |
|---|
| 389 | int r; |
|---|
| 390 | |
|---|
| 391 | while (total < len) { |
|---|
| 392 | unsigned char cmd; |
|---|
| 393 | size_t cnt; |
|---|
| 394 | |
|---|
| 395 | if ((cnt = len - total) > TWT_PAGESIZE) |
|---|
| 396 | cnt = TWT_PAGESIZE; |
|---|
| 397 | cmd = (cnt - 1) | 0x10; |
|---|
| 398 | |
|---|
| 399 | r = twt_command(reader, &cmd, 1, buffer + total, cnt); |
|---|
| 400 | if (r < 0) { |
|---|
| 401 | if (total) |
|---|
| 402 | return total; |
|---|
| 403 | return r; |
|---|
| 404 | } |
|---|
| 405 | |
|---|
| 406 | total += cnt; |
|---|
| 407 | } |
|---|
| 408 | |
|---|
| 409 | return total; |
|---|
| 410 | } |
|---|
| 411 | |
|---|
| 412 | static int twt_sync_set_read_address(ifd_reader_t * reader, int slot, int proto, |
|---|
| 413 | unsigned short addr) |
|---|
| 414 | { |
|---|
| 415 | unsigned char cmd_i2c_short[] = |
|---|
| 416 | { 0x7C, 0x64, 0x41, 0x00, 0x00, 0x64, 0x40, 0x00, 0x0F }; |
|---|
| 417 | unsigned char cmd_i2c_long[] = |
|---|
| 418 | { 0x7C, 0x64, 0x42, 0xA0, 0x00, 0x00, 0x64, 0x40, 0xA1, 0x0F }; |
|---|
| 419 | unsigned char cmd_2wire[] = |
|---|
| 420 | { 0x70, 0x64, 0x42, 0x30, 0x00, 0x00, 0x65, 0x0F }; |
|---|
| 421 | unsigned char cmd_3wire[] = |
|---|
| 422 | { 0x70, 0xA0, 0x42, 0x00, 0x00, 0x00, 0x80, 0x50, 0x0F }; |
|---|
| 423 | unsigned char hi, lo, *cmd; |
|---|
| 424 | size_t len; |
|---|
| 425 | |
|---|
| 426 | hi = addr >> 8; |
|---|
| 427 | lo = addr & 0xFF; |
|---|
| 428 | |
|---|
| 429 | switch (proto) { |
|---|
| 430 | case IFD_PROTOCOL_I2C_SHORT: |
|---|
| 431 | cmd = cmd_i2c_short; |
|---|
| 432 | len = sizeof(cmd_i2c_short); |
|---|
| 433 | cmd[3] = (hi << 1) | 0xA0; |
|---|
| 434 | cmd[4] = lo; |
|---|
| 435 | cmd[7] = (hi << 1) | 0xA1; |
|---|
| 436 | break; |
|---|
| 437 | |
|---|
| 438 | case IFD_PROTOCOL_I2C_LONG: |
|---|
| 439 | cmd = cmd_i2c_long; |
|---|
| 440 | len = sizeof(cmd_i2c_long); |
|---|
| 441 | cmd[4] = hi; |
|---|
| 442 | cmd[5] = lo; |
|---|
| 443 | break; |
|---|
| 444 | |
|---|
| 445 | case IFD_PROTOCOL_2WIRE: |
|---|
| 446 | cmd = cmd_2wire; |
|---|
| 447 | len = sizeof(cmd_2wire); |
|---|
| 448 | cmd[4] = lo; |
|---|
| 449 | break; |
|---|
| 450 | |
|---|
| 451 | case IFD_PROTOCOL_3WIRE: |
|---|
| 452 | cmd = cmd_3wire; |
|---|
| 453 | len = sizeof(cmd_3wire); |
|---|
| 454 | cmd[3] = (hi << 6) | 0x0e; |
|---|
| 455 | cmd[4] = lo; |
|---|
| 456 | break; |
|---|
| 457 | |
|---|
| 458 | default: |
|---|
| 459 | return IFD_ERROR_NOT_SUPPORTED; |
|---|
| 460 | } |
|---|
| 461 | |
|---|
| 462 | return twt_command(reader, cmd, len, NULL, 0); |
|---|
| 463 | } |
|---|
| 464 | |
|---|
| 465 | static int twt_sync_read(ifd_reader_t * reader, int slot, int proto, |
|---|
| 466 | unsigned short addr, unsigned char *buffer, size_t len) |
|---|
| 467 | { |
|---|
| 468 | int r; |
|---|
| 469 | |
|---|
| 470 | if ((r = twt_sync_set_read_address(reader, slot, proto, addr)) < 0) |
|---|
| 471 | return r; |
|---|
| 472 | |
|---|
| 473 | return twt_sync_read_buffer(reader, slot, proto, buffer, len); |
|---|
| 474 | } |
|---|
| 475 | |
|---|
| 476 | /* |
|---|
| 477 | * Write synchronous card |
|---|
| 478 | */ |
|---|
| 479 | static int twt_sync_write_buffer(ifd_reader_t * reader, int slot, int proto, |
|---|
| 480 | const unsigned char *buffer, size_t len) |
|---|
| 481 | { |
|---|
| 482 | size_t total = 0; |
|---|
| 483 | int r; |
|---|
| 484 | |
|---|
| 485 | while (total < len) { |
|---|
| 486 | unsigned char cmd[TWT_PAGESIZE + 2]; |
|---|
| 487 | size_t cnt; |
|---|
| 488 | |
|---|
| 489 | if ((cnt = len - total) > TWT_PAGESIZE) |
|---|
| 490 | cnt = TWT_PAGESIZE; |
|---|
| 491 | cmd[0] = (cnt - 1) | 0x40; |
|---|
| 492 | memcpy(cmd + 1, buffer + total, cnt); |
|---|
| 493 | cmd[cnt + 1] = 0x0F; |
|---|
| 494 | |
|---|
| 495 | r = twt_command(reader, cmd, cnt + 2, NULL, 0); |
|---|
| 496 | if (r < 0) { |
|---|
| 497 | if (total) |
|---|
| 498 | return total; |
|---|
| 499 | return r; |
|---|
| 500 | } |
|---|
| 501 | |
|---|
| 502 | total += cnt; |
|---|
| 503 | } |
|---|
| 504 | |
|---|
| 505 | return total; |
|---|
| 506 | } |
|---|
| 507 | |
|---|
| 508 | static int twt_sync_set_write_address(ifd_reader_t * reader, int slot, |
|---|
| 509 | int proto, unsigned short addr) |
|---|
| 510 | { |
|---|
| 511 | unsigned char cmd_i2c_short1[] = |
|---|
| 512 | { 0x7C, 0x64, 0x41, 0xA0, 0x00, 0x64, 0x40, 0xA1, 0x0F }; |
|---|
| 513 | unsigned char cmd_i2c_short2[] = { 0x7E, 0x10 }; |
|---|
| 514 | unsigned char cmd_i2c_short3[] = |
|---|
| 515 | { 0x7E, 0x66, 0x6E, 0x00, 0x00, 0x10, 0x0F }; |
|---|
| 516 | unsigned char cmd_i2c_long1[] = |
|---|
| 517 | { 0x7C, 0x64, 0x42, 0xA0, 0x00, 0x00, 0x64, 0x40, 0xA1, 0x0F }; |
|---|
| 518 | unsigned char cmd_i2c_long2[] = { 0x7E, 0x10 }; |
|---|
| 519 | unsigned char cmd_i2c_long3[] = |
|---|
| 520 | { 0x7F, 0x66, 0x6E, 0x00, 0x00, 0xA0, 0x0F }; |
|---|
| 521 | unsigned char cmd_2wire[] = { 0x72, 0x6E, 0x00, 0x38, 0x03, 0x0F }; |
|---|
| 522 | unsigned char cmd_3wire[] = |
|---|
| 523 | { 0x73, 0x67, 0x6E, 0x00, 0x00, 0x02, 0x0F }; |
|---|
| 524 | unsigned char hi, lo, *cmd, status; |
|---|
| 525 | size_t len; |
|---|
| 526 | int r; |
|---|
| 527 | |
|---|
| 528 | hi = addr >> 8; |
|---|
| 529 | lo = addr & 0xFF; |
|---|
| 530 | |
|---|
| 531 | switch (proto) { |
|---|
| 532 | case IFD_PROTOCOL_I2C_SHORT: |
|---|
| 533 | if ((r = |
|---|
| 534 | twt_command(reader, cmd_i2c_short1, sizeof(cmd_i2c_short1), |
|---|
| 535 | NULL, 0)) < 0) |
|---|
| 536 | return r; |
|---|
| 537 | if ((r = |
|---|
| 538 | twt_command(reader, cmd_i2c_short2, sizeof(cmd_i2c_short2), |
|---|
| 539 | &status, 1)) < 0) |
|---|
| 540 | return r; |
|---|
| 541 | |
|---|
| 542 | cmd = cmd_i2c_short3; |
|---|
| 543 | len = sizeof(cmd_i2c_short3); |
|---|
| 544 | cmd[3] = lo; |
|---|
| 545 | cmd[4] = (hi << 1) | 0xA0; |
|---|
| 546 | cmd[5] = 0x00 /* pagemode */ ; |
|---|
| 547 | break; |
|---|
| 548 | |
|---|
| 549 | case IFD_PROTOCOL_I2C_LONG: |
|---|
| 550 | if ((r = |
|---|
| 551 | twt_command(reader, cmd_i2c_long1, sizeof(cmd_i2c_long1), |
|---|
| 552 | NULL, 0)) < 0) |
|---|
| 553 | return r; |
|---|
| 554 | if ((r = |
|---|
| 555 | twt_command(reader, cmd_i2c_long2, sizeof(cmd_i2c_long2), |
|---|
| 556 | &status, 1)) < 0) |
|---|
| 557 | return r; |
|---|
| 558 | |
|---|
| 559 | cmd = cmd_i2c_long3; |
|---|
| 560 | len = sizeof(cmd_i2c_long3); |
|---|
| 561 | cmd[3] = lo; |
|---|
| 562 | cmd[4] = hi; |
|---|
| 563 | break; |
|---|
| 564 | |
|---|
| 565 | case IFD_PROTOCOL_2WIRE: |
|---|
| 566 | cmd = cmd_2wire; |
|---|
| 567 | len = sizeof(cmd_2wire); |
|---|
| 568 | cmd[2] = lo; |
|---|
| 569 | break; |
|---|
| 570 | |
|---|
| 571 | case IFD_PROTOCOL_3WIRE: |
|---|
| 572 | cmd = cmd_3wire; |
|---|
| 573 | len = sizeof(cmd_3wire); |
|---|
| 574 | cmd[3] = lo; |
|---|
| 575 | cmd[4] = (hi << 6) | 0x33; |
|---|
| 576 | break; |
|---|
| 577 | |
|---|
| 578 | default: |
|---|
| 579 | return IFD_ERROR_NOT_SUPPORTED; |
|---|
| 580 | } |
|---|
| 581 | |
|---|
| 582 | return twt_command(reader, cmd, len, NULL, 0); |
|---|
| 583 | } |
|---|
| 584 | |
|---|
| 585 | static int twt_sync_write(ifd_reader_t * reader, int slot, int proto, |
|---|
| 586 | unsigned short addr, const unsigned char *buffer, |
|---|
| 587 | size_t len) |
|---|
| 588 | { |
|---|
| 589 | int r; |
|---|
| 590 | |
|---|
| 591 | if ((r = twt_sync_set_write_address(reader, slot, proto, addr)) < 0) |
|---|
| 592 | return r; |
|---|
| 593 | |
|---|
| 594 | return twt_sync_write_buffer(reader, slot, proto, buffer, len); |
|---|
| 595 | } |
|---|
| 596 | |
|---|
| 597 | /* |
|---|
| 598 | * Turn LED on/off |
|---|
| 599 | */ |
|---|
| 600 | static int twt_led(ifd_reader_t * reader, int what) |
|---|
| 601 | { |
|---|
| 602 | unsigned char cmd[] = { 0x6F, 0x00, 0x6A, 0x0F }; |
|---|
| 603 | |
|---|
| 604 | cmd[1] = what; |
|---|
| 605 | return twt_command(reader, cmd, sizeof(cmd), NULL, 0); |
|---|
| 606 | } |
|---|
| 607 | |
|---|
| 608 | /* |
|---|
| 609 | * Helper functions |
|---|
| 610 | */ |
|---|
| 611 | static int twt_command(ifd_reader_t * reader, const void *cmd, size_t cmd_len, |
|---|
| 612 | void *res, size_t res_len) |
|---|
| 613 | { |
|---|
| 614 | unsigned char buffer[254]; |
|---|
| 615 | int rc; |
|---|
| 616 | |
|---|
| 617 | if (res_len > sizeof(buffer) - 1 || cmd_len > sizeof(buffer) - 1) |
|---|
| 618 | return IFD_ERROR_BUFFER_TOO_SMALL; |
|---|
| 619 | |
|---|
| 620 | memcpy(buffer, cmd, cmd_len); |
|---|
| 621 | cmd_len = twt_send_checksum(buffer, cmd_len); |
|---|
| 622 | |
|---|
| 623 | if (ct_config.debug > 1) |
|---|
| 624 | ifd_debug(3, "sending:%s", ct_hexdump(buffer, cmd_len)); |
|---|
| 625 | |
|---|
| 626 | rc = ifd_device_transceive(reader->device, |
|---|
| 627 | buffer, cmd_len, buffer, res_len + 1, -1); |
|---|
| 628 | if (rc < 0) { |
|---|
| 629 | ct_error("towitoko: transceive error: %s", ct_strerror(rc)); |
|---|
| 630 | return rc; |
|---|
| 631 | } |
|---|
| 632 | |
|---|
| 633 | if (ct_config.debug > 1) |
|---|
| 634 | ifd_debug(3, "received:%s", ct_hexdump(buffer, res_len + 1)); |
|---|
| 635 | |
|---|
| 636 | if (!twt_recv_checksum(buffer, res_len + 1)) { |
|---|
| 637 | ct_error("towitoko: command failed (bad checksum)"); |
|---|
| 638 | return -1; |
|---|
| 639 | } |
|---|
| 640 | |
|---|
| 641 | if (res && res_len) |
|---|
| 642 | memcpy(res, buffer, res_len); |
|---|
| 643 | |
|---|
| 644 | return 0; |
|---|
| 645 | } |
|---|
| 646 | |
|---|
| 647 | static unsigned char twt_checksum(unsigned char cs, const unsigned char *data, |
|---|
| 648 | size_t len) |
|---|
| 649 | { |
|---|
| 650 | unsigned char b; |
|---|
| 651 | |
|---|
| 652 | while (len--) { |
|---|
| 653 | b = cs ^ *data++; |
|---|
| 654 | /* rotate left one bit and toggle LSB */ |
|---|
| 655 | cs = ((b << 1) | (b >> 7)) ^ 0x01; |
|---|
| 656 | } |
|---|
| 657 | return cs; |
|---|
| 658 | } |
|---|
| 659 | |
|---|
| 660 | static int twt_recv_checksum(const unsigned char *data, size_t len) |
|---|
| 661 | { |
|---|
| 662 | if (len == 0) |
|---|
| 663 | return 0; |
|---|
| 664 | |
|---|
| 665 | return data[len - 1] == twt_checksum(0x01, data, len - 1); |
|---|
| 666 | } |
|---|
| 667 | |
|---|
| 668 | static size_t twt_send_checksum(unsigned char *data, size_t len) |
|---|
| 669 | { |
|---|
| 670 | data[len] = twt_checksum(0x00, data, len); |
|---|
| 671 | return len + 1; |
|---|
| 672 | } |
|---|
| 673 | |
|---|
| 674 | /* |
|---|
| 675 | * Driver operations |
|---|
| 676 | */ |
|---|
| 677 | static struct ifd_driver_ops towitoko_driver; |
|---|
| 678 | |
|---|
| 679 | void ifd_towitoko_register(void) |
|---|
| 680 | { |
|---|
| 681 | towitoko_driver.open = twt_open; |
|---|
| 682 | towitoko_driver.close = twt_close; |
|---|
| 683 | towitoko_driver.change_parity = twt_change_parity; |
|---|
| 684 | towitoko_driver.change_speed = twt_change_speed; |
|---|
| 685 | towitoko_driver.activate = twt_activate; |
|---|
| 686 | towitoko_driver.deactivate = twt_deactivate; |
|---|
| 687 | towitoko_driver.card_status = twt_card_status; |
|---|
| 688 | towitoko_driver.card_reset = twt_card_reset; |
|---|
| 689 | towitoko_driver.send = twt_send; |
|---|
| 690 | towitoko_driver.recv = twt_recv; |
|---|
| 691 | towitoko_driver.sync_read = twt_sync_read; |
|---|
| 692 | towitoko_driver.sync_write = twt_sync_write; |
|---|
| 693 | |
|---|
| 694 | ifd_driver_register("towitoko", &towitoko_driver); |
|---|
| 695 | } |
|---|