diff options
author | Andreas Eversberg <jolly@eversberg.eu> | 2021-08-28 20:59:20 +0200 |
---|---|---|
committer | Andreas Eversberg <jolly@eversberg.eu> | 2021-11-07 20:00:41 +0100 |
commit | 465445aac5121949d1a59e02500ce2e874812379 (patch) | |
tree | a55bf60a9ec3f96e036ee1017649dd92708b2018 /src | |
parent | 922b4af3621f333b9d69c37e1bbcc0b16089e58b (diff) |
Add Magnetic card emulation for C-Netz
Emulation can be done with a coil connected to sound card.
Alternatively an Attiny85 can be used to control a coil.
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 1 | ||||
-rw-r--r-- | src/magnetic/Makefile.am | 28 | ||||
-rw-r--r-- | src/magnetic/image.c | 32 | ||||
-rw-r--r-- | src/magnetic/iso7811.c | 162 | ||||
-rw-r--r-- | src/magnetic/iso7811.h | 5 | ||||
-rwxr-xr-x | src/magnetic/magnetic.ino | 486 | ||||
-rw-r--r-- | src/magnetic/main.c | 368 |
7 files changed, 1082 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index e983c4e..57fb1fb 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -59,6 +59,7 @@ SUBDIRS += \ radio \ zeitansage \ sim \ + magnetic \ fuvst if HAVE_ALSA diff --git a/src/magnetic/Makefile.am b/src/magnetic/Makefile.am new file mode 100644 index 0000000..f5e9f83 --- /dev/null +++ b/src/magnetic/Makefile.am @@ -0,0 +1,28 @@ +AM_CPPFLAGS = -Wall -Wextra -g $(all_includes) + +bin_PROGRAMS = \ + cnetz_magnetic + +cnetz_magnetic_SOURCES = \ + iso7811.c \ + image.c \ + main.c + +cnetz_magnetic_LDADD = \ + $(COMMON_LA) \ + $(top_builddir)/src/libdebug/libdebug.a \ + $(top_builddir)/src/liboptions/liboptions.a \ + $(top_builddir)/src/libwave/libwave.a \ + $(top_builddir)/src/libaaimage/libaaimage.a \ + -lm + +if HAVE_ALSA +cnetz_magnetic_LDADD += \ + $(top_builddir)/src/libsound/libsound.a \ + $(ALSA_LIBS) +endif + +if HAVE_ALSA +AM_CPPFLAGS += -DHAVE_ALSA +endif + diff --git a/src/magnetic/image.c b/src/magnetic/image.c new file mode 100644 index 0000000..89212e6 --- /dev/null +++ b/src/magnetic/image.c @@ -0,0 +1,32 @@ +#ifndef ARDUINO + +#include <stdio.h> + +const char *aaimage[] = { + "@w", + " @r___@g___", + " @WC-Netz @r/ _@g_ \\", + " @WMagnetstreifen- @r/ / @g \\ \\", + " @WEmulator @r/ / @g \\ \\", + " @r\\ \\ @g / /", + " @B ________________________@r\\ \\@B__@g/ /@B____________", + " @B|@y_________________________@r\\ \\@g/ /@y_____________@B|", + " @B|@y / / / / / / / / / / / / /@r\\_|@g|_/@y / / / / / / /@B|", + " @B|@y / / / / / / / / / / / / / / / / / / / / / / /@B|", + " @B|@y______________________________________________@B|", + " @B| |", + " @B| @w ____ ____ @B|", + " @B| @w/ \\ / \\ @B|", + " @B| @w\\____/ \\____/ @B|", + " @B| @w|----^ |----^ Die Servicenummer @B|", + " @B| @w . / \\ @B|", + " @B| @w|---\\| \\_/\\_/ rund um die Uhr -----\\ @B|", + " @B| @w ' / \\ @B/ \\ @w\\ @B|", + " @B| @w|----^ \\____/ @B\\ / @w/ @B|", + " @B| @w -----/ @B|", + " @B|______________________________________________|", + "", + NULL +}; + +#endif /* ARDUINO */ diff --git a/src/magnetic/iso7811.c b/src/magnetic/iso7811.c new file mode 100644 index 0000000..214251e --- /dev/null +++ b/src/magnetic/iso7811.c @@ -0,0 +1,162 @@ +/* ISO 7811 encoder/decoder + * + * (C) 2021 by Andreas Eversberg <jolly@eversberg.eu> + * All Rights Reserved + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdint.h> +#include <string.h> +#include "iso7811.h" + +/* Given is a string with or without start and end sentinel. Returned are + * bytes containing 5 bits each. These bits shall be sent LSB first. + * A lead-in and a start sentinel is added prior encoded string data. + * An end sentinel, a LRC and a lead-out is added after string data. + */ +int encode_track(uint8_t *data, const char *string, int lead_in, int lead_out) +{ + int i; + uint8_t bits, lrc; + + i = 0; + lrc = 0; + + /* lead-in */ + for (lead_in += i; i < lead_in; i++) + data[i] = 0; + + /* start sentinel */ + if (*string == ';') + string++; + bits = 0x0b; + data[i++] = bits; + lrc ^= bits; + + /* string */ + while (*string && *string != '?') { + if (*string >= 0x30 && *string < 0x40) + bits = *string - 0x30; + else + bits = 0; + data[i] = bits & 0x0f; + lrc ^= bits; + bits ^= bits >> 2; + bits ^= bits >> 1; + bits &= 1; + data[i] |= (bits ^ 1) << 4; + string++; + i++; + } + + /* end sentinel */ + bits = 0x1f; + data[i++] = bits; + lrc ^= bits; + + /* LRC */ + data[i] = lrc & 0x0f; + lrc ^= lrc >> 2; + lrc ^= lrc >> 1; + lrc &= 1; + data[i] |= (lrc ^ 1) << 4; + i++; + + /* lead-out */ + for (lead_out += i; i < lead_out; i++) + data[i] = 0; + + return i; +} + +/* n0nnnnnn=sssss0000 (in case of 7 digits) */ +int cnetz_card(char *string, const char *number, const char *sicherung) +{ + int len; + + /* number */ + len = strlen(number); + *string++ = *number++; + if (len == 7) + *string++ = '0'; + else if (len == 8) + *string++ = *number++; + else + return 0; + *string++ = *number++; + *string++ = *number++; + *string++ = *number++; + *string++ = *number++; + *string++ = *number++; + *string++ = *number++; + + /* field seperator */ + *string++ = '='; + + /* security code */ + len = strlen(sicherung); + if (len < 5) + *string++ = '0'; + else + *string++ = *sicherung++; + if (len < 4) + *string++ = '0'; + else + *string++ = *sicherung++; + if (len < 3) + *string++ = '0'; + else + *string++ = *sicherung++; + if (len < 2) + *string++ = '0'; + else + *string++ = *sicherung++; + *string++ = *sicherung++; + *string++ = '0'; + *string++ = '0'; + *string++ = '0'; + *string++ = '0'; + + *string++ = '\0'; + + return 18; +} + +/* 0:500000=000000000 */ +int bsa44_service(char *string) +{ + *string++ = '0'; + *string++ = ':'; + *string++ = '5'; + *string++ = '0'; + *string++ = '0'; + *string++ = '0'; + *string++ = '0'; + *string++ = '0'; + *string++ = '='; + *string++ = '0'; + *string++ = '0'; + *string++ = '0'; + *string++ = '0'; + *string++ = '0'; + *string++ = '0'; + *string++ = '0'; + *string++ = '0'; + *string++ = '0'; + *string++ = '\0'; + + return 18; +} + diff --git a/src/magnetic/iso7811.h b/src/magnetic/iso7811.h new file mode 100644 index 0000000..6ffe6ae --- /dev/null +++ b/src/magnetic/iso7811.h @@ -0,0 +1,5 @@ + +int encode_track(uint8_t *data, const char *string, int lead_in, int lead_out); +int cnetz_card(char *string, const char *number, const char *sicherungscode); +int bsa44_service(char *string); + diff --git a/src/magnetic/magnetic.ino b/src/magnetic/magnetic.ino new file mode 100755 index 0000000..3d905ad --- /dev/null +++ b/src/magnetic/magnetic.ino @@ -0,0 +1,486 @@ +/* Magnetic card emulator for ATMEL + * + * This sould work with the original 'MagSpoof' out of the box! + * In this case you should add a second switch, to allow test card and progrmming mode. + * + * (C) 2021 by Andreas Eversberg <jolly@eversberg.eu> + * All Rights Reserved + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/* to set fused for ATTINY85: (This is default when shipped!) + * avrdude -c usbasp-clone -p t85 -U lfuse:w:0xc0:m -U hfuse:w:0xdf:m -U efuse:w:0xff:m + */ + +/* Use a clock speed of 8 MHz. If you change it, also change the fuses!!!! + * The CLKPS bits are set to 0 by software, so that 8 MHz clock is not divided. + * + * Press switch 1 to emulate card, press switch 2 to emulate BSA44 service card. + * The LED will do short flashs, to indicate that power is on. + * + * Hold a switch while powering on, to enter test mode. The LED will light up. + * Press switch 1 to send continuous 0-bits. + * Press switch 2 to send continuous 1-bits. + * Press switch 1 and 2 to send continuos alternating 0- and 1-bits. + * WARNING: In test mode, H-bridge IC becomes quickly very hot. Don't fry it! + * + * To enter programming mode, press both buttons simultaniously. + * The LED will continously blink, to indicate programming mode. + * Press switch 1 to select subscriber number or switch 1 to select security code. + * The LED will then show the digit values by blinking. It can be aborted with any switch. + * After short blinking, a long blink shows that the first digit can be entered. + * Press switch 1 1-10 times to enter the digit. When done, press switch 2 and continue + * with the next digit. When all digits are entered, press switch 2 again. + * A soft flash of the LED (fading in and out) will indicate that the new digits are stored. + * A false input will abort the programming procedure and restart with continuous blinking. + * To abort programming mode, press both buttuns simultaniously. + */ + +#define F_CPU 8000000 // Oscillator frequency + +extern "C" +{ + #include "iso7811.h" +} +#include <avr/eeprom.h> + +#define PORT_ENABLE 3 // PIN 2 -> connect to 1-2EN of L293D and to LED with 1K resistor to ground +#define PORT_COIL1 0 // PIN 5 -> connect to 1A of L293D +#define PORT_COIL2 1 // PIN 6 -> connect to 2A of L293D +#define PORT_SWITCH1 2 // PIN 7 -> connect via switch 1 to ground +#define PORT_SWITCH2 4 // PIN 3 -> connect via switch 2 to ground (optional, leave open when unused) +#define PORT_RESET 5 // PIN 1 -> unused, leave open (Don't disable reset when programming fuses!!!) + +/* see main.c for more info */ +#define CNETZ_LEAD_IN 12 +#define CNETZ_LEAD_OUT 150 + +#define CLOCK_US 200 // Time to wait for half a bit + +#define EEPROM_MAGIC 'c' // not equal to sim emulator, this has a capital 'C' +#define EEPROM_VERSION '0' + +#define SWITCH1 (digitalRead(PORT_SWITCH1) == LOW) +#define SWITCH2 (digitalRead(PORT_SWITCH2) == LOW) + +static char number[9] = "1234567\0"; +static char sicherung[6] = "12345"; +static char string[19]; +static uint8_t flux = 0; + +/* enable H-bridge, but set it to neutral */ +void enable_h_bridge(uint8_t enable) +{ + /* enable H-bridge and LED */ + digitalWrite(PORT_ENABLE, enable); + + /* set bridge to neutral */ + digitalWrite(PORT_COIL1, LOW); + digitalWrite(PORT_COIL2, LOW); +} + +/* send single bit with clock */ +void send_bit(uint8_t bit) +{ + digitalWrite(PORT_COIL1, flux); + flux ^= 1; + digitalWrite(PORT_COIL2, flux); + delayMicroseconds(CLOCK_US); + + if (bit & 1) { + digitalWrite(PORT_COIL1, flux); + flux ^= 1; + digitalWrite(PORT_COIL2, flux); + } + delayMicroseconds(CLOCK_US); +} + +/* blink exactly one second to confirm correct clock speed */ +void blink_led() +{ + int i; + + for (i = 0; i < 3; i++) { + delay(200); + if (SWITCH1 || SWITCH2) + break; + enable_h_bridge(1); + delay(200); + if (SWITCH1 || SWITCH2) + break; + enable_h_bridge(0); + } +} + +/* wait until release of single key, but abort when pressing both keys. + * if 2 is given, wait for release of all keys, don't abort when pressing both keys. + * compensate contact shattering (German: Tastenprellen) + */ +void wait_release(int keys) +{ + int i = 0; + while (i < 50) { + delay(1); + if (keys < 2) { + if (SWITCH1 && SWITCH2) + break; + } + if (SWITCH1 || SWITCH2) + i = 0; + else + i++; + } +} + +/* test mode */ +void test_pattern(void) +{ + /* test pattern */ + while (42) { + if (!SWITCH1 && !SWITCH2) + enable_h_bridge(1); + if (SWITCH1) + send_bit(0); + if (SWITCH2) + send_bit(1); + } +} + +/* read eeprom, if version is correct */ +void read_eeprom(void) +{ + int i; + + if (eeprom_read_byte(0) == EEPROM_MAGIC + && eeprom_read_byte(1) == EEPROM_VERSION) { + for (i = 0; i < 8; i++) + number[i] = eeprom_read_byte(i + 2); + number[i] = '\0'; + for (i = 0; i < 5; i++) + sicherung[i] = eeprom_read_byte(i + 10); + sicherung[i] = '\0'; + } +} + +/* write eeprom, */ +void write_eeprom(void) +{ + int i; + + eeprom_write_byte(0, EEPROM_MAGIC); + eeprom_write_byte(1, EEPROM_VERSION); + for (i = 0; i < 8; i++) + eeprom_write_byte(i + 2, number[i]); + for (i = 0; i < 5; i++) + eeprom_write_byte(i + 10, sicherung[i]); + + /* show soft flash */ + for (i = 0; i < 55; i++) { + enable_h_bridge(1); + delay(i/5); + enable_h_bridge(0); + delay(10 - i/5); + } + for (i = 54; i >= 0; i--) { + enable_h_bridge(1); + delay(i/5); + enable_h_bridge(0); + delay(10 - i/5); + } +} + +void setup() { + /* setup clock speed to 8 MHz */ + CLKPR = _BV(CLKPCE); + CLKPR = 0; + + /* setup ports */ + pinMode(PORT_ENABLE, OUTPUT); + digitalWrite(PORT_ENABLE, LOW); + pinMode(PORT_COIL1, OUTPUT); + digitalWrite(PORT_COIL1, LOW); + pinMode(PORT_COIL2, OUTPUT); + digitalWrite(PORT_COIL2, LOW); + pinMode(PORT_SWITCH1, INPUT_PULLUP); + pinMode(PORT_SWITCH2, INPUT_PULLUP); + + /* blink with LED */ + blink_led(); + + /* transmit test pattern */ + if (SWITCH1 || SWITCH2) { + wait_release(1); + test_pattern(); + } + + /* read subscriber data from eeprom */ + read_eeprom(); +} + +/* send card data */ +void send_string(const char *string) +{ + uint8_t data[CNETZ_LEAD_IN + 21 + CNETZ_LEAD_OUT]; + int length, i; + uint8_t digit; + + length = encode_track(data, string, CNETZ_LEAD_IN, CNETZ_LEAD_OUT); + + /* enable H-bridge and LED */ + enable_h_bridge(1); + + /* send bits */ + for (i = 0; i < length; i++) { + digit = data[i]; + send_bit((digit >> 0) & 1); + send_bit((digit >> 1) & 1); + send_bit((digit >> 2) & 1); + send_bit((digit >> 3) & 1); + send_bit((digit >> 4) & 1); + /* abort when pressing both switches */ + if (SWITCH1 && SWITCH2) + break; + } + + /* disable H-bridge */ + enable_h_bridge(0); +} + +/* send zeros or ones depending on the key pressed, stop by pressing both keys */ +void program_mode(void) +{ + uint8_t blink; + uint8_t edit; + char io[9]; + int i, b, d; + +error: + blink = 0; + edit = 0; + /* flash LED, wait for key press */ + while (!edit) { + blink ^= 1; + enable_h_bridge(blink); + for (d = 0; d < 50; d++) { + delay(1); + if (SWITCH1) { + edit = 1; + break; + } + if (SWITCH2) { + edit = 2; + break; + } + } + } + enable_h_bridge(0); + wait_release(1); + if (SWITCH1 && SWITCH2) + goto done; + + /* copy subscriber data to io-buffer */ + switch (edit) { + case 1: + for (i = 0; i < 8; i++) + io[i] = number[i]; + io[i] = '\0'; + break; + case 2: + for (i = 0; i < 5; i++) + io[i] = sicherung[i]; + io[i] = '\0'; + break; + } + + /* blink the io-buffer data */ + for (i = 0; io[i]; i++) { + for (d = 0; d < 1000; d++) { + delay(1); + if (SWITCH1 || SWITCH2) + goto stop_blink; + } + if (io[i] > '0') + blink = io[i] - '0'; + else + blink = 10; + for (b = 0; b < blink; b++) { + enable_h_bridge(1); + for (d = 0; d < 100; d++) { + delay(1); + if (SWITCH1 || SWITCH2) { + enable_h_bridge(0); + goto stop_blink; + } + } + enable_h_bridge(0); + for (d = 0; d < 400; d++) { + delay(1); + if (SWITCH1 || SWITCH2) + goto stop_blink; + } + } + } + for (d = 0; d < 1000; d++) { + delay(1); + if (SWITCH1 && SWITCH2) + goto stop_blink; + } + stop_blink: + wait_release(1); + if (SWITCH1 && SWITCH2) + goto done; + enable_h_bridge(1); + for (d = 0; d < 500; d++) { + delay(1); + if (SWITCH1 && SWITCH2) + goto done; + } + enable_h_bridge(0); + + /* key in the data to io-buffer */ + i = 0; + b = 0; + while (42) { + if (SWITCH1) { + enable_h_bridge(1); + for (d = 0; d < 100; d++) { + delay(1); + if (SWITCH1 && SWITCH2) + goto done; + } + enable_h_bridge(0); + wait_release(1); + if (SWITCH1 && SWITCH2) + goto done; + b++; + if (b > 10) + goto error; + } + if (SWITCH2) { + wait_release(1); + if (SWITCH1 && SWITCH2) + goto done; + if (b == 0) { + while (i < 9) + io[i++] = '\0'; + break; + } + if (b == 10) + b = 0; + io[i++] = '0' + b; + b = 0; + if (i > 8) + goto error; + enable_h_bridge(1); + for (d = 0; d < 500; d++) { + delay(1); + if (SWITCH1 && SWITCH2) + goto done; + } + enable_h_bridge(0); + } + } + + /* verify input */ + switch (edit) { + case 1: + if (strlen(io) < 7) + goto error; + if (io[0] > '7') + goto error; + if (strlen(io) == 8) { + if ((io[1] - '0') * 10 + io[2] - '0' > 31) + goto error; + if (atoi(io + 3) > 65535) + goto error; + } else { + if (atoi(io + 2) > 65535) + goto error; + } + break; + case 2: + if (strlen(io) > 5) + goto error; + if (!io[0]) + goto error; + if (io[0] == '0' && io[1] != '\0') + goto error; + if (atoi(io) > 65535) + goto error; + break; + } + + /* copy io-buffer data to subscriber data */ + switch (edit) { + case 1: + for (i = 0; i < 8; i++) + number[i] = io[i]; + number[i] = '\0'; + break; + case 2: + for (i = 0; i < 5; i++) + sicherung[i] = io[i]; + sicherung[i] = '\0'; + break; + } + + /* write subscriber data to eeprom */ + write_eeprom(); + +done: + enable_h_bridge(0); + return; +} + +static uint16_t flash = 0; + +void loop() { + /* go programming */ + if (SWITCH1 && SWITCH2) { + flash = 0; + wait_release(2); + program_mode(); + wait_release(2); + return; + } + + /* send card */ + if (SWITCH1) { + flash = 0; + cnetz_card(string, number, sicherung); + send_string(string); + wait_release(1); + return; + } + + /* send service card */ + if (SWITCH2) { + flash = 0; + bsa44_service(string); + send_string(string); + wait_release(1); + return; + } + + /* slow blink to show that the device is powered on */ + delay(1); + flash++; + if (flash == 1980) + enable_h_bridge(1); + if (flash == 2000) { + enable_h_bridge(0); + flash = 0; + } +} diff --git a/src/magnetic/main.c b/src/magnetic/main.c new file mode 100644 index 0000000..2d32231 --- /dev/null +++ b/src/magnetic/main.c @@ -0,0 +1,368 @@ +/* main function + * + * (C) 2021 by Andreas Eversberg <jolly@eversberg.eu> + * All Rights Reserved + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef ARDUINO + +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <signal.h> +#include <errno.h> +#include "../libsample/sample.h" +#include "../libsound/sound.h" +#include "../libwave/wave.h" +#include "../libdebug/debug.h" +#include "../liboptions/options.h" +#include "../libaaimage/aaimage.h" +#include "iso7811.h" + +int num_kanal = 1; +static int quit = 0; +#ifdef HAVE_ALSA +static void *sound = NULL; +static int dsp_buffer = 50; +#endif +static int dsp_samplerate = 48000; +static const char *dsp_audiodev = "hw:0,0"; +static const char *wave_file = NULL; +static int baudrate = 2666; +static const char *sicherung = "12345"; + +/* Measurements done in summer 2021 with an original card, applied with iron oxyde. */ +/* Conforms to Track 3 (210 bpi) with 60 bits lead-in, 20 digits data, about 550 bits lead out */ +/* Note that LEAD_OUT here is longer, because the switch must be manually pressed during lead-out. */ +#define CNETZ_LEAD_IN 12 /* number of zero-digits before start sentinel (60 bits) */ +#define CNETZ_LEAD_OUT 150 /* number of zero-digits after LRC sentinel */ +#define CNETZ_SWITCH_ON 27 /* switch closing during lead-out, in digit-duration */ +#define CNETZ_SWITCH_OFF 42 /* switch opening during lead-out, in digit-duration */ + +void print_help(const char *arg0) +{ + printf("Usage: %s [options] -a hw:0,0 <number> | service\n", arg0); + /* - - */ + printf("General options:\n"); + printf(" -h --help\n"); + printf(" This help\n"); + printf(" --config [~/]<path to config file>\n"); + printf(" Give a config file to use. If it starts with '~/', path is at home dir.\n"); + printf(" Each line in config file is one option, '-' or '--' must not be given!\n"); + debug_print_help(); + printf(" -a --audio-device hw:<card>,<device>\n"); + printf(" Input audio from sound card's device number\n"); + printf(" -s --samplerate <sample rate>\n"); + printf(" Give audio device sample rate in Hz. (default = %d)\n", dsp_samplerate); + printf(" -w --write-wave <filename>\n"); + printf(" Output sound as wave file\n"); + printf("\nMagnetic card simulator options:\n"); + printf(" -B --baud-rate <baud>\n"); + printf(" Playback baud rate (default = %d)\n", baudrate); + printf(" -S --sicherung <security code>\n"); + printf(" Card's security code for simple authentication (default = '%s')\n", sicherung); + printf("\n<number>: Give any valid 7 digit (optionally 8 digit) subscriber number. May\n"); + printf(" be prefixed with 0160.\n"); + printf("\n'service': BSA44 service card (to unlock phone after battery replacement)\n"); +} + +void add_options(void) +{ + option_add('h', "help", 0); + option_add('v', "debug", 1); + option_add('a', "audio-device", 1); + option_add('s', "samplerate", 1); + option_add('w', "write-wave", 1); + option_add('B', "baud-rate", 1); + option_add('S', "sicherung", 1); +}; + +int handle_options(int short_option, int argi, char **argv) +{ + int rc; + + switch (short_option) { + case 'h': + print_help(argv[0]); + return 0; + case 'v': + if (!strcasecmp(argv[argi], "list")) { + debug_list_cat(); + return 0; + } + rc = parse_debug_opt(argv[argi]); + if (rc < 0) { + fprintf(stderr, "Failed to parse debug option, please use -h for help.\n"); + return rc; + } + break; + case 'a': + dsp_audiodev = options_strdup(argv[argi]); + break; + case 's': + dsp_samplerate = atof(argv[argi]); + break; + case 'w': + wave_file = options_strdup(argv[argi]); + break; + case 'B': + baudrate = atoi(argv[argi]); + break; + case 'S': + sicherung = options_strdup(argv[argi]); + break; + default: + return -EINVAL; + } + + return 1; +} + +void sighandler(int sigset) +{ + if (sigset == SIGHUP) + return; + if (sigset == SIGPIPE) + return; + + printf("Signal received: %d\n", sigset); + + quit = -1; +} + +int main(int argc, char *argv[]) +{ + const char *number; + char string[19]; + uint8_t data[CNETZ_LEAD_IN + 21 + CNETZ_LEAD_OUT]; + int length; + int rc, argi; + int i, j; + + debuglevel = DEBUG_INFO; + + add_options(); + rc = options_config_file(argc, argv, "~/.osmocom/analog/magnetic.conf", handle_options); + if (rc < 0) + return 0; + + /* parse command line */ + argi = options_command_line(argc, argv, handle_options); + if (argi <= 0) + return argi; + + if (argi >= argc) { + fprintf(stderr, "Expecting phone number, use '-h' for help!\n"); + return 0; + } else if (!strcmp(argv[argi], "service")) { + bsa44_service(string); + } else { + number = argv[argi]; + /* remove prefix, if given */ + if (strlen(number) >= 10 && !strncmp(number, "0160", 4)) + number += 4; + if (strlen(number) < 7 || strlen(number) > 8) { + fprintf(stderr, "Expecting phone number to be 7 or 8 digits, use '-h' for help!\n"); + return 0; + } + for (i = 0; number[i]; i++) { + if (number[0] < '0' || number[i] > '9') { + fprintf(stderr, "Given phone number has invalid digits, use '-h' for help!\n"); + return 0; + } + } + if (number[0] > '7') { +inval_number: + fprintf(stderr, "Given digits of phone number are out of range for 'C-Netz', use '-h' for help!\n"); + return 0; + } + if (strlen(number) == 8) { + if ((number[1] - '0') * 10 + (number[2] - '0') > 31) + goto inval_number; + if (atoi(number + 3) > 65535) + goto inval_number; + } else { + if (atoi(number + 2) > 65535) + goto inval_number; + } + for (i = 0; sicherung[i]; i++) { + if (sicherung[0] < '0' || sicherung[i] > '9') { + fprintf(stderr, "Given security code has invalid digits, use '-h' for help!\n"); + return 0; + } + } + if (!sicherung[0] || (sicherung[0] == '0' && sicherung[1] == '0') || atoi(sicherung) > 65535) { + fprintf(stderr, "Given security code is out of range, use '-h' for help!\n"); + return 0; + } + cnetz_card(string, number, sicherung); + } + + length = encode_track(data, string, CNETZ_LEAD_IN, CNETZ_LEAD_OUT); + if (length > (int)sizeof(data)) { + fprintf(stderr, "Software error: Array too small, PLEASE FIX!\n"); + return -1; + } + + /* alloc space depending on bit rate (length of half-bit: round up to next integer) */ + int samples_per_halfbit = (dsp_samplerate + (baudrate * 2) - 1) / (baudrate * 2); + int total_samples = samples_per_halfbit * 2 * 5 * length; + sample_t sample[total_samples], *samples[1], silence[dsp_samplerate], level = 1; +#ifdef HAVE_ALSA + int switch_on_samples = samples_per_halfbit * 2 * 5 * (CNETZ_LEAD_IN + 21 + CNETZ_SWITCH_ON); + int switch_off_samples = samples_per_halfbit * 2 * 5 * (CNETZ_LEAD_IN + 21 + CNETZ_SWITCH_OFF); + int buffer_size = dsp_samplerate * dsp_buffer / 1000; +#endif + + /* generate sample */ + int s, ss = 0; + for (i = 0; i < length; i++) { + for (j = 0; j < 5; j++) { + level = -level; + for (s = 0; s < samples_per_halfbit; s++) + sample[ss++] = level; + if (((data[i] >> j) & 1)) + level = -level; + for (s = 0; s < samples_per_halfbit; s++) + sample[ss++] = level; + } + } + memset(silence, 0, sizeof(silence)); + + PDEBUG(DDSP, DEBUG_INFO, "Total bits: %d\n", length * 5); + PDEBUG(DDSP, DEBUG_INFO, "Samples per bit: %d\n", samples_per_halfbit * 2); + PDEBUG(DDSP, DEBUG_INFO, "Total samples: %d (duration: %.3f seconds)\n", total_samples, (double)total_samples / (double)dsp_samplerate); + + if (wave_file) { + wave_rec_t wave_rec; + + /* open wave file */ + rc = wave_create_record(&wave_rec, wave_file, dsp_samplerate, 1, 1.0); + if (rc < 0) { + PDEBUG(DRADIO, DEBUG_ERROR, "Failed to create WAVE record instance!\n"); + goto error; + } + samples[0] = silence; + wave_write(&wave_rec, samples, dsp_samplerate / 2); + samples[0] = sample; + wave_write(&wave_rec, samples, total_samples); + samples[0] = silence; + wave_write(&wave_rec, samples, dsp_samplerate / 2); + wave_destroy_record(&wave_rec); + goto done; + } + +#ifdef HAVE_ALSA + /* open audio device */ + sound = sound_open(dsp_audiodev, NULL, NULL, NULL, 1, 0.0, dsp_samplerate, buffer_size, 1.0, 1.0, 0.0, 2.0); + if (!sound) { + rc = -EIO; + PDEBUG(DRADIO, DEBUG_ERROR, "Failed to open sound device!\n"); + goto error; + } +#else + rc = -ENOTSUP; + PDEBUG(DRADIO, DEBUG_ERROR, "No sound card support compiled in!\n"); + goto error; +#endif + + print_aaimage(); + printf("String to send: ;%s?\n", string); + for (i = 0; i < 5; i++) { + if (i < 4) + printf("2^%d: ...", i); + else + printf("Par: ..."); + for (j = CNETZ_LEAD_IN - 4; j < CNETZ_LEAD_IN + 4 + 21; j++) + printf(" %d", (data[j] >> i) & 1); + printf(" ...\n"); + } + + /* catch signals */ + signal(SIGINT, sighandler); + signal(SIGHUP, sighandler); + signal(SIGTERM, sighandler); + signal(SIGPIPE, sighandler); + +#ifdef HAVE_ALSA + sound_start(sound); + + int count; + while (!quit) { + ss = 0; + while (!quit) { + usleep(1000); + count = sound_get_tosend(sound, buffer_size); + if (count <= 0) + continue; + samples[0] = silence + ss; + ss += count; + if (ss > dsp_samplerate) { + count -= ss - dsp_samplerate; + ss = dsp_samplerate; + } + sound_write(sound, samples, NULL, count, NULL, NULL, 1); + if (ss == dsp_samplerate) + break; + } + printf("\033[0;32m -> \033[1;31mTX\033[0;32m <-\033[0;39m\r"); fflush(stdout); + ss = 0; + while (!quit) { + usleep(1000); + count = sound_get_tosend(sound, buffer_size); + if (count <= 0) + continue; + if ((ss >= 0 && ss < count) || (ss - switch_off_samples >= 0 && ss - switch_off_samples < count)) { + printf("\033[0;32m -> \033[1;31mTX\033[0;32m <- \033[0;39m\r"); fflush(stdout); + } + if (ss - switch_on_samples >= 0 && ss - switch_on_samples < count) { + printf("\033[0;32m -> \033[1;31mTX\033[0;32m <- -> \033[1;33mCLICK\033[0;32m <-\033[0;39m\r"); fflush(stdout); + } + samples[0] = sample + ss; + ss += count; + if (ss > total_samples) { + count -= ss - total_samples; + ss = total_samples; + } + sound_write(sound, samples, NULL, count, NULL, NULL, 1); + if (ss == total_samples) + break; + } + printf(" \r"); fflush(stdout); + } +#endif + + /* reset signals */ + signal(SIGINT, SIG_DFL); + signal(SIGHUP, SIG_DFL); + signal(SIGTERM, SIG_DFL); + signal(SIGPIPE, SIG_DFL); + +error: +#ifdef HAVE_ALSA + if (sound) + sound_close(sound); +#endif + +done: + options_free(); + + return 0; +} + +#endif /* ARDUINO */ |