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.
|
@ -75,6 +75,7 @@ src/radio/osmoradio
|
||||||
src/datenklo/datenklo
|
src/datenklo/datenklo
|
||||||
src/zeitansage/zeitansage
|
src/zeitansage/zeitansage
|
||||||
src/sim/cnetz_sim
|
src/sim/cnetz_sim
|
||||||
|
src/magnetic/cnetz_magnetic
|
||||||
src/fuvst/fuvst
|
src/fuvst/fuvst
|
||||||
src/fuvst/fuvst_sniffer
|
src/fuvst/fuvst_sniffer
|
||||||
extra/cnetz_memory_card_generator
|
extra/cnetz_memory_card_generator
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
$fn = 100;
|
||||||
|
durchmesser=30;
|
||||||
|
rille=8;
|
||||||
|
wand=1;
|
||||||
|
|
||||||
|
module torus(d1, d2) {
|
||||||
|
rotate_extrude(convexity = 10)
|
||||||
|
translate([d2/2+d1/2, 0, 0])
|
||||||
|
circle(r = d1/2);
|
||||||
|
}
|
||||||
|
|
||||||
|
intersection() {
|
||||||
|
difference() {
|
||||||
|
torus(rille+wand*2,durchmesser-wand*2);
|
||||||
|
torus(rille,durchmesser);
|
||||||
|
}
|
||||||
|
translate([0,0,-5]) cylinder(h=10, r=(durchmesser+rille)/2);
|
||||||
|
}
|
|
@ -110,6 +110,7 @@ AC_OUTPUT(
|
||||||
src/datenklo/Makefile
|
src/datenklo/Makefile
|
||||||
src/zeitansage/Makefile
|
src/zeitansage/Makefile
|
||||||
src/sim/Makefile
|
src/sim/Makefile
|
||||||
|
src/magnetic/Makefile
|
||||||
src/fuvst/Makefile
|
src/fuvst/Makefile
|
||||||
src/test/Makefile
|
src/test/Makefile
|
||||||
src/Makefile
|
src/Makefile
|
||||||
|
|
|
@ -122,6 +122,7 @@ Additional features:
|
||||||
<li><a href="datenklo.html">Das Datenklo</a></li>
|
<li><a href="datenklo.html">Das Datenklo</a></li>
|
||||||
<li><a href="tv.html">Osmo TV</a></li>
|
<li><a href="tv.html">Osmo TV</a></li>
|
||||||
<li><a href="sim.html">C-Netz Sim Card</a></li>
|
<li><a href="sim.html">C-Netz Sim Card</a></li>
|
||||||
|
<li><a href="magnetic.html">C-Netz Magnetic Card</a></li>
|
||||||
<li>Zeitansage (German talking clock)</li>
|
<li>Zeitansage (German talking clock)</li>
|
||||||
<li>C-Netz FuVSt (MSC to control a real base station)</li>
|
<li>C-Netz FuVSt (MSC to control a real base station)</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
After Width: | Height: | Size: 189 KiB |
After Width: | Height: | Size: 235 KiB |
After Width: | Height: | Size: 262 KiB |
After Width: | Height: | Size: 56 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 8.0 KiB |
|
@ -0,0 +1,304 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link href="style.css" rel="stylesheet" type="text/css" />
|
||||||
|
<title>osmocom-analog</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<center><table><tr><td>
|
||||||
|
|
||||||
|
<h2><center>C-Netz Magnetic Card Emulator</center></h2>
|
||||||
|
|
||||||
|
<center><img src="magnetic.jpg"/></center>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Why emulating a magnetic card with a magnetic strip?
|
||||||
|
Maybe you got a C-Netz phone with magnetic card reader from the attic, friend or Ebay?
|
||||||
|
But a card is missing. Without the card you cannot use that C-Netz phone at all.
|
||||||
|
Then you buy at high price on Ebay to get a used card.
|
||||||
|
This card might make your phone work, but 'BSA 44' phone requires service reset to disable theft protection.
|
||||||
|
<font color="red">The BSA 44 phone requires a magnetic service card to unlock the phone after power loss.</font>
|
||||||
|
The emulator can help you in this case.
|
||||||
|
And it is really a cool way.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li><a href="#emu">Emulating Magnetic Card</a>
|
||||||
|
<li><a href="#byo">Build Your Own Magnetic Card</a>
|
||||||
|
<li><a href="#usage">Using the Magnetic Card</a>
|
||||||
|
<li><a href="#service">BSA 44 Service Cards</a>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p class="toppic">
|
||||||
|
<a name="emu"></a>
|
||||||
|
Emulating Magnetic Card
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<center><img src="mag4.jpg"/></center>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
I put a card inside a bowl with fine rust (iron oxide) and water.
|
||||||
|
As depicted above, the third track of the card attracts tiny patricles of iron oxide.
|
||||||
|
The bits can be seen under a microsope.
|
||||||
|
The rusy-looking lines on the left represent a 0-bit per line.
|
||||||
|
The wider lines which appear brighter are actually two lines, representing a 1-bit.
|
||||||
|
Each line is a reversal in the magnetic flux.
|
||||||
|
For deeper understanding, refer to ISO 7811 standard.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
The idea is to transmit a strong magnetic signal to the card reader's head from outside the case, rather than connecting wires inside the reader.
|
||||||
|
This way no modification to the card reader is required.
|
||||||
|
This idea is not new.
|
||||||
|
'MagSpoof' is a project to emulate credit and debit cards.
|
||||||
|
The MagSpoof hardware and my hardware is almost the same.
|
||||||
|
You can run the C-Netz magnetic card emulator on MagSpoof hardware, if you just add another switch.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<font color="red">
|
||||||
|
I strongly suggest connecting an oscilloscope to the output (not directly to the head) of the phone's card reader.
|
||||||
|
This helps you to find out if your transmitter is strong enough and the phone's card reader receives a signal.
|
||||||
|
</font>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
You can generate the signal with the sound output of your PC.
|
||||||
|
Your PC must be connected to an amplifier.
|
||||||
|
Instead of using a speaker, connect a coil to your amplifier.
|
||||||
|
I used a 0.5mm (diameter!!) copper wire with 50 turns and a diameter of 30mm.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
|
||||||
|
# src/magnetic/cnetz_magnetic -a hw:0,0 1234567
|
||||||
|
String to send: ;10234567=123450000?
|
||||||
|
2^0: ... 0 0 0 0 1 1 0 0 1 0 1 0 1 1 1 0 1 0 1 0 0 0 0 1 0 0 0 0 0 ...
|
||||||
|
2^1: ... 0 0 0 0 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 0 0 0 0 1 0 0 0 0 0 ...
|
||||||
|
2^2: ... 0 0 0 0 0 0 0 0 0 1 1 1 1 1 0 0 0 1 1 0 0 0 0 1 0 0 0 0 0 ...
|
||||||
|
2^3: ... 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 ...
|
||||||
|
Par: ... 0 0 0 0 0 0 1 0 1 0 1 1 0 0 0 0 1 0 1 1 1 1 1 1 0 0 0 0 0 ...
|
||||||
|
main.c: 258 info : Total bits: 915
|
||||||
|
main.c: 259 info : Samples per bit: 20
|
||||||
|
main.c: 260 info : Total samples: 18300 (duration: 0.381 seconds)
|
||||||
|
-> TX <-
|
||||||
|
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
In this example the first sound card is used.
|
||||||
|
The card to be emulated has the phone number 1234567.
|
||||||
|
See <a href="software.html">Software usage</a> for more info about selecting sound card.
|
||||||
|
A cracking sound, followed by rectangular tone is played.
|
||||||
|
During the tone, the switch inside the card reader must be briefly closed for a short fraction of a second.
|
||||||
|
The switch is used to detect insertion and removal of the card.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<center><img src="mag2.jpg"/></center>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
In order to close the switch inside the card reader, just punch a hole into some platic card.
|
||||||
|
Don't use anything other than an ISO platic card.
|
||||||
|
I tried cardboard, but it did not work.
|
||||||
|
The thickness of the card must be close to 0.8mm to make the switch work correctly.
|
||||||
|
I just drilled four holes into the card, so I do not need to care what orientation the card must be inserted.
|
||||||
|
The center of the hole must be exactly 10mm away from each edge of the card.
|
||||||
|
The diameter is 5mm, but use 6mm, so you don't need to be exactly 10mm away from the edge.
|
||||||
|
Don't forget to deburr the edge inside the hole.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Slide the card half way into the card reader.
|
||||||
|
Match the hole with the arrow or dot shown on the slot of the card reader.
|
||||||
|
On the other side of the card reader you will find the head.
|
||||||
|
(Just open it to see where it is located.)
|
||||||
|
Hold the coil close to the head. Try different angles of the coil.
|
||||||
|
When you hear the cracking noise or you see the 'TX' flashing on the terminal, quickly push the card all the way in.
|
||||||
|
This needs to be done before the sound or 'TX' disappears.
|
||||||
|
It takes a little parctice.
|
||||||
|
You may also pull card quickly half way out, after fully inserting it.
|
||||||
|
It does not matter what direction the card is moved, as long as the switch is only closed for a short time.
|
||||||
|
If you do it slowly, you will hear the two clicks, one when the switch is closed and one when it is opened.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p class="toppic">
|
||||||
|
<a name="byo"></a>
|
||||||
|
Build Your Own Magnetic Card
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
The idea is to drive a coil with a step motor driver IC.
|
||||||
|
This way you actually transmit the magnetic card's signal over several centimeters to the card reader's head.
|
||||||
|
The coil is a 0.5mm thick wire and has 50 windings with about 30 mm in diameter.
|
||||||
|
In the 'cad' directory of the git repository You will find the OpenSCAD and STL files of a 'thing' to wind the coil around.
|
||||||
|
Alternatively wind the coil around two fingers 50 times and then fix the windings by wrapping tape around it..
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<center><img src="magnetic-schematic.png"/></center>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
It is a quite simple schematics.
|
||||||
|
The difference between Magspoof and this one is just an additional switch.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<center><img src="magnetic-layout.png"/></center>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
You find the PCB drawings inside the "layout" directory of the git repository.
|
||||||
|
Be sure to print it without scaling!
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>ATTINY85
|
||||||
|
<li>L293D (or stronger)
|
||||||
|
<li>two switches
|
||||||
|
<li>LED of you choise
|
||||||
|
<li>500 Ohms resistor (if LED is not that bright)
|
||||||
|
<li>100uF buffering capacitor for power input
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<center><img src="mag1.jpg"/></center>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Leave the fuses of the ATTINY85 as it is shipped by default.
|
||||||
|
The fuses are set to use the internal 8 MHz clock with scaling to 1 MHz.
|
||||||
|
The Arduino file is located inside the "src/magnetic" directory of the git repository.
|
||||||
|
It runs at 8 MHz.
|
||||||
|
You don't need to disable the 1 MHz scaling via fuses.
|
||||||
|
It is done in software.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p class="toppic">
|
||||||
|
<a name="usage"></a>
|
||||||
|
Using the Magnetic Card
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Power it up using three AA batteries.
|
||||||
|
Be sure not to reverse the power, it will destroy both ICs.
|
||||||
|
The LED will blink 3 times, which takes about 1 second.
|
||||||
|
If it does not take about 1 second, check fuses!
|
||||||
|
Afterwards it will flash every two seconds, to indicate that the power is on.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Normal mode:
|
||||||
|
<ul>
|
||||||
|
<li>Turn power on.
|
||||||
|
<li>The LED will flash 3 times.
|
||||||
|
<li>Press the switch 1 ("Card") to send a subscriber card sequence (1234567 by default).
|
||||||
|
<li>The LED will light up for a fraction of a second.
|
||||||
|
<li>Press the switch 2 ("Service") to send a BSA44 service card sequence.
|
||||||
|
<li>The LED will light up for a fraction of a second.
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Quickly after pressing the switch, insert the card.
|
||||||
|
If the card is inserted within 150 ms after pressing the switch, you may hear a beep from the phone.
|
||||||
|
This means that the card was accepted.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<font color="red">
|
||||||
|
Test the emulator by location the coil close to an AM radio and you should hear the signal when you press a button.
|
||||||
|
You may also connect some coil to an oscilloscope/headphone and hold the emulator's coil close to it.
|
||||||
|
</font>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Debug mode:
|
||||||
|
<ul>
|
||||||
|
<li>Press and hold switch 1 or 2.
|
||||||
|
<li>Turn power on.
|
||||||
|
<li>Release the switch.
|
||||||
|
<li>The LED will continuously light.
|
||||||
|
<li>Hold switch 1 ("Card") to send sequence of 0s.
|
||||||
|
<li>Hold switch 2 ("Service") to send sequence of 1s.
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<font color="red">
|
||||||
|
Do not hold longer than two seconds, as the H-bridge becomes hot very quickly.
|
||||||
|
Touch it, to feel when it becomes too hot.
|
||||||
|
</font>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Programming mode:
|
||||||
|
<ul>
|
||||||
|
<li>Turn power on.
|
||||||
|
<li>The LED will flash 3 times.
|
||||||
|
<li>Press both switches simultaniously and release.
|
||||||
|
<li>The LED will continuously flash.
|
||||||
|
<li>Press switch 1 ("Card") to change subscriber number.
|
||||||
|
<li>or Press switch 2 ("Service") to change security code. (Sicherungsschlüssel)
|
||||||
|
<li>The LED will blink several times, indicating the first digit. '0' is indicated by blinking 10 times.
|
||||||
|
<li>After blinking the first digit, a pause is made and then the LED will blink the second digit and so on.
|
||||||
|
<li>After blinking all digits, the LED will light half a second, to indicate input of the first digit.
|
||||||
|
<li>Press switch 1 as many times as your first digit's value you want to enter. 10 times for a '0'.
|
||||||
|
<li>Press switch 2 to complete your digit input. The LED will light half a second to indicate input the next digit.
|
||||||
|
<li>If you have entered all digits, press switch 2 again to store the entered sequence.
|
||||||
|
<li>The LED will softly flash one time. This inidcates correct number. The number is stored.
|
||||||
|
<li>On error, LED will start flashing again. Repeat input!
|
||||||
|
<li>To abort input and return to normal mode: Press both switches simultaniously and release.
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Example to program subscriber number '3839349":
|
||||||
|
<ul>
|
||||||
|
<li>Press both switches simultaniously and release.
|
||||||
|
<li>The LED will continuously flash.
|
||||||
|
<li>Press switch 1 ("Card") to change subscriber number.
|
||||||
|
<li>The current subscriber number will show up blinks of the LED.
|
||||||
|
<li>Press any switch to abort blinking and directly enter new number.
|
||||||
|
<li>The LED light half a second to indicate input.
|
||||||
|
<li>Press switch 1 three times.
|
||||||
|
<li>Press switch 2, the LED will light half a second.
|
||||||
|
<li>Press switch 1 eight times.
|
||||||
|
<li>Press switch 2, the LED will light half a second.
|
||||||
|
<li>Press switch 1 three times.
|
||||||
|
<li>Press switch 2, the LED will light half a second.
|
||||||
|
<li>Press switch 1 nine times.
|
||||||
|
<li>Press switch 2, the LED will light half a second.
|
||||||
|
<li>Press switch 1 three times.
|
||||||
|
<li>Press switch 2, the LED will light half a second.
|
||||||
|
<li>Press switch 1 four times.
|
||||||
|
<li>Press switch 2, the LED will light half a second.
|
||||||
|
<li>Press switch 1 nine times.
|
||||||
|
<li>Press switch 2, the LED will light half a second.
|
||||||
|
<li>Press switch 2 again. The LED will softly flash one time. The number is stored.
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
<p>
|
||||||
|
The Subscriber number entered must conform to a 7-digits or 8-digits C-Netz subscriber number.
|
||||||
|
The Security code must be a 16 bit unsigned integer, entered in decimal notation (from 0 to 65535).
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p class="toppic">
|
||||||
|
<a name="service"></a>
|
||||||
|
BSA 44 Service Cards
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
When inserting (simulating) a service card, a BSA44 phone will show "Wartungskarte" on its LC display.
|
||||||
|
Turn off the phone and then turn it on again, but leave card inserted and power connected.
|
||||||
|
Press the top-left button two times, so that is shows "SA....".
|
||||||
|
Press X 0 X 1 X 2 to reset password to '0000' and disable theft protection.
|
||||||
|
Turn off the phone and remove card, but leave power connected.
|
||||||
|
If the power is disconnected, the theft protection will be activated and unlocking as described above is required again.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Older 'LED' display phones directly show "SA....", so they must not be restarted before using the service menu.
|
||||||
|
You may directly press X 0 X 1 X 2 to reset it.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
<hr><center>[<a href="index.html">Back to main page</a>]</center><hr>
|
||||||
|
</td></tr></table></center>
|
||||||
|
</body>
|
||||||
|
</html>
|
After Width: | Height: | Size: 207 KiB |
|
@ -59,6 +59,7 @@ SUBDIRS += \
|
||||||
radio \
|
radio \
|
||||||
zeitansage \
|
zeitansage \
|
||||||
sim \
|
sim \
|
||||||
|
magnetic \
|
||||||
fuvst
|
fuvst
|
||||||
|
|
||||||
if HAVE_ALSA
|
if HAVE_ALSA
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 */
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 */
|