Magnetic stripe driver for Linux GPIO
Published on February 19, 2011
For those of you working with Neuron insertion-style, single-track card readers, there is a driver available here on the 2.6.35 branch of our kernel repository.
The driver performs stripe decoding internally, which should simplify the task of writing applications for these readers. The driver was tested with the MCR-370T-1R model -- if you're using something else, keep reading and see if the theory of operation matches up with your own device.
The reader connects to the GPIO lines of the Nitrogen board. You will need four open lines for this: one for data, one for the clock, and one each for the front and rear switches. It doesn't really matter which GPIO you use, since the Kconfig included in this commit will allow you to specify which ones you are using.
To use, insmod and then create the device node:
magmajor=`cat /proc/devices | awk "$2=="magstripe" {print $1}"`
mknod /dev/magstripe c $magmajor 0
chmod 664 /dev/magstripe
Theory of operation
On the hardware level, the idea is simple: If the front switch is closed and the rear switch is open, each pulse of the clock line indicates a data bit, and the data line indicates whether that bit is high or low. Opening the front switch or closing the rear switch signals the end of data. The reader itself only supports Track 2 information, so we don't need to worry about multiple tracks.
The driver itself uses two circular buffers to track the operation of the device, one for data and one for events. The events it is interested in are switch transitions and data completion events. In other words, the event buffer gets written to when a switch opens or closes, or when the device is finished reading data. This allows the driver to track data strings within the data buffer and distinguish between inserting a card and removing it. Performing a read simply pops an event off the buffer -- 'f' if the front switch closes, 'F' if it opens, 'r' if the rear switch closes, and 'R' if it opens.
Simple enough, right? Sort of. There is still the task of decoding the data into something usable. The driver will attempt to do this whenever its read function encounters a data completion event.
So, the other circular buffer is used to store bits read off the queue. These bits are packed together in bytes in the array. This is the easiest and least wasteful way to store the information. There are a few reasons this can be complicated: first, the data is stored in 5-bit characters, per the ABA standard for Track 2 -- a 4-bit BCD character plus a parity bit. Secondly, there is no guarantee of alignment; the clock always pulses a bit over invalid data at the start and end of each swipe, so it's up to the developer to sort through the bits and find the data string themselves.
We took care of that for you. The driver's decode method uses the circular buffer indices to determine where data recording started and finished, and then steps through the buffer one bit at a time, looking across byte boundaries for a sentinel character which signifies the start or end of a data string. Once it finds it, it will move five bits at a time and translate each character to ASCII using a look-up table. If it finds an unknown character, the process starts over -- this ensures that we don't accidentally use garbage data as a sentinel character.
The comments explain the whole process in detail, but the important part is, if the decode is successful, then that read will return the whole decoded string, minus the sentinel characters, in ASCII. It will return the same string whether you are inserting or removing a card.
Before you write your app, note that the driver does NOT cover...
- Longitudinal redundancy check - Not every card uses it, so provisions are not made for it.
- Debounce - The switches bounce a lot on the Neuron reader, so if your app needs to do something with the raw switch events, you'll need to implement this yourself.
This should take some of the burden off your shoulders for writing a customer tracking app to go with your reader -- you can even test it out with cat
first. Enjoy!