Moved all source to the trunk directory.
This commit is contained in:
parent
6a10d6b465
commit
032a83e9e1
26 changed files with 7809 additions and 0 deletions
436
Projects/Magstripe/Magstripe.c
Normal file
436
Projects/Magstripe/Magstripe.c
Normal file
|
@ -0,0 +1,436 @@
|
|||
/*
|
||||
LUFA Library
|
||||
Copyright (C) Dean Camera, 2009.
|
||||
|
||||
dean [at] fourwalledcubicle [dot] com
|
||||
www.fourwalledcubicle.com
|
||||
*/
|
||||
|
||||
/*
|
||||
Copyright 2009 Denver Gingerich (denver [at] ossguy [dot] com)
|
||||
Copyright 2009 Dean Camera (dean [at] fourwalledcubicle [dot] com)
|
||||
|
||||
Permission to use, copy, modify, and distribute this software
|
||||
and its documentation for any purpose and without fee is hereby
|
||||
granted, provided that the above copyright notice appear in all
|
||||
copies and that both that the copyright notice and this
|
||||
permission notice and warranty disclaimer appear in supporting
|
||||
documentation, and that the name of the author not be used in
|
||||
advertising or publicity pertaining to distribution of the
|
||||
software without specific, written prior permission.
|
||||
|
||||
The author disclaim all warranties with regard to this
|
||||
software, including all implied warranties of merchantability
|
||||
and fitness. In no event shall the author be liable for any
|
||||
special, indirect or consequential damages or any damages
|
||||
whatsoever resulting from loss of use, data or profits, whether
|
||||
in an action of contract, negligence or other tortious action,
|
||||
arising out of or in connection with the use or performance of
|
||||
this software.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
*
|
||||
* Main source file for the MagStripe application. This file contains the code which drives
|
||||
* the USB keyboard interface from the magnetic card stripe reader device.
|
||||
*/
|
||||
|
||||
#include "Magstripe.h"
|
||||
|
||||
/* Project Tags, for reading out using the ButtLoad project */
|
||||
BUTTLOADTAG(ProjName, "Magstripe Reader");
|
||||
BUTTLOADTAG(BuildTime, __TIME__);
|
||||
BUTTLOADTAG(BuildDate, __DATE__);
|
||||
BUTTLOADTAG(LUFAVersion, "LUFA V" LUFA_VERSION_STRING);
|
||||
|
||||
/* Scheduler Task List */
|
||||
TASK_LIST
|
||||
{
|
||||
{ Task: USB_USBTask , TaskStatus: TASK_STOP },
|
||||
{ Task: USB_Keyboard_Report , TaskStatus: TASK_STOP },
|
||||
{ Task: Magstripe_Read , TaskStatus: TASK_STOP },
|
||||
};
|
||||
|
||||
/* Global Variables */
|
||||
/** Indicates if the device is using Report Protocol mode, instead of Boot Protocol mode. Boot Protocol mode
|
||||
* is a special reporting mode used by compatible PC BIOS to support USB keyboards before a full OS and USB
|
||||
* driver has been loaded, by using predefined report structures indicated in the USB HID standard.
|
||||
*/
|
||||
bool UsingReportProtocol = true;
|
||||
|
||||
/** Total idle period in milliseconds set by the host via a SetIdle request, used to silence the report endpoint
|
||||
* until the report data changes or the idle period elapsed. Generally used to implement hardware key repeats, or
|
||||
* by some BIOS to reduce the number of reports when in Boot Protocol mode.
|
||||
*/
|
||||
uint8_t IdleCount = 0;
|
||||
|
||||
/** Milliseconds remaining counter for the HID class SetIdle and GetIdle requests, used to silence the report
|
||||
* endpoint for an amount of time indicated by the host or until the report changes.
|
||||
*/
|
||||
uint16_t IdleMSRemaining = 0;
|
||||
|
||||
/** Circular buffer to hold the read bits from track 1 of the inserted magnetic card. */
|
||||
BitBuffer_t Track1Data;
|
||||
|
||||
/** Circular buffer to hold the read bits from track 2 of the inserted magnetic card. */
|
||||
BitBuffer_t Track2Data;
|
||||
|
||||
/** Circular buffer to hold the read bits from track 3 of the inserted magnetic card. */
|
||||
BitBuffer_t Track3Data;
|
||||
|
||||
/** Delay counter between sucessive key strokes. This is to prevent the OS from ignoring multiple keys in a short
|
||||
* period of time due to key repeats. Two milliseconds works for most OSes.
|
||||
*/
|
||||
uint8_t KeyDelayRemaining;
|
||||
|
||||
|
||||
/** Main program entry point. This routine configures the hardware required by the application, then
|
||||
* starts the scheduler to run the application tasks.
|
||||
*/
|
||||
int main(void)
|
||||
{
|
||||
/* Disable watchdog if enabled by bootloader/fuses */
|
||||
MCUSR &= ~(1 << WDRF);
|
||||
wdt_disable();
|
||||
|
||||
/* Disable Clock Division */
|
||||
SetSystemClockPrescaler(0);
|
||||
|
||||
/* Hardware Initialization */
|
||||
Magstripe_Init();
|
||||
|
||||
/* Buffer Initialization */
|
||||
BitBuffer_Init(&Track1Data);
|
||||
BitBuffer_Init(&Track2Data);
|
||||
BitBuffer_Init(&Track3Data);
|
||||
|
||||
/* Millisecond timer initialization, with output compare interrupt enabled for the idle timing */
|
||||
OCR0A = 0xFA;
|
||||
TCCR0A = (1 << WGM01);
|
||||
TCCR0B = ((1 << CS01) | (1 << CS00));
|
||||
TIMSK0 = (1 << OCIE0A);
|
||||
|
||||
/* Initialize Scheduler so that it can be used */
|
||||
Scheduler_Init();
|
||||
|
||||
/* Initialize USB Subsystem */
|
||||
USB_Init();
|
||||
|
||||
/* Scheduling - routine never returns, so put this last in the main function */
|
||||
Scheduler_Start();
|
||||
}
|
||||
|
||||
/** Event handler for the USB_Connect event. This starts the USB task. */
|
||||
EVENT_HANDLER(USB_Connect)
|
||||
{
|
||||
/* Start USB management task */
|
||||
Scheduler_SetTaskMode(USB_USBTask, TASK_RUN);
|
||||
}
|
||||
|
||||
/** Event handler for the USB_Disconnect event. This stops the USB and keyboard report tasks. */
|
||||
EVENT_HANDLER(USB_Disconnect)
|
||||
{
|
||||
/* Stop running keyboard reporting, card reading and USB management tasks */
|
||||
Scheduler_SetTaskMode(USB_Keyboard_Report, TASK_STOP);
|
||||
Scheduler_SetTaskMode(USB_USBTask, TASK_STOP);
|
||||
Scheduler_SetTaskMode(Magstripe_Read, TASK_STOP);
|
||||
}
|
||||
|
||||
/** Event handler for the USB_ConfigurationChanged event. This configures the device's endpoints ready
|
||||
* to relay reports to the host, and starts the keyboard report task.
|
||||
*/
|
||||
EVENT_HANDLER(USB_ConfigurationChanged)
|
||||
{
|
||||
/* Setup Keyboard Keycode Report Endpoint */
|
||||
Endpoint_ConfigureEndpoint(KEYBOARD_EPNUM, EP_TYPE_INTERRUPT,
|
||||
ENDPOINT_DIR_IN, KEYBOARD_EPSIZE,
|
||||
ENDPOINT_BANK_SINGLE);
|
||||
|
||||
/* Default to report protocol on connect */
|
||||
UsingReportProtocol = true;
|
||||
|
||||
/* Start Keyboard reporting and card reading tasks */
|
||||
Scheduler_SetTaskMode(USB_Keyboard_Report, TASK_RUN);
|
||||
Scheduler_SetTaskMode(Magstripe_Read, TASK_RUN);
|
||||
}
|
||||
|
||||
/** Event handler for the USB_UnhandledControlPacket event. This is used to catch standard and class specific
|
||||
* control requests that are not handled internally by the USB library, so that they can be handled appropriately
|
||||
* for the application.
|
||||
*/
|
||||
EVENT_HANDLER(USB_UnhandledControlPacket)
|
||||
{
|
||||
/* Handle HID Class specific requests */
|
||||
switch (bRequest)
|
||||
{
|
||||
case REQ_GetReport:
|
||||
if (bmRequestType == (REQDIR_DEVICETOHOST | REQTYPE_CLASS | REQREC_INTERFACE))
|
||||
{
|
||||
USB_KeyboardReport_Data_t KeyboardReportData;
|
||||
|
||||
/* Create the next keyboard report for transmission to the host */
|
||||
GetNextReport(&KeyboardReportData);
|
||||
|
||||
/* Ignore report type and ID number value */
|
||||
Endpoint_Discard_Word();
|
||||
|
||||
/* Ignore unused Interface number value */
|
||||
Endpoint_Discard_Word();
|
||||
|
||||
/* Read in the number of bytes in the report to send to the host */
|
||||
uint16_t wLength = Endpoint_Read_Word_LE();
|
||||
|
||||
/* If trying to send more bytes than exist to the host, clamp the value at the report size */
|
||||
if (wLength > sizeof(KeyboardReportData))
|
||||
wLength = sizeof(KeyboardReportData);
|
||||
|
||||
Endpoint_ClearSetupReceived();
|
||||
|
||||
/* Write the report data to the control endpoint */
|
||||
Endpoint_Write_Control_Stream_LE(&KeyboardReportData, wLength);
|
||||
|
||||
/* Finalize the stream transfer to send the last packet or clear the host abort */
|
||||
Endpoint_ClearSetupOUT();
|
||||
}
|
||||
|
||||
break;
|
||||
case REQ_GetProtocol:
|
||||
if (bmRequestType == (REQDIR_DEVICETOHOST | REQTYPE_CLASS | REQREC_INTERFACE))
|
||||
{
|
||||
Endpoint_ClearSetupReceived();
|
||||
|
||||
/* Write the current protocol flag to the host */
|
||||
Endpoint_Write_Byte(UsingReportProtocol);
|
||||
|
||||
/* Send the flag to the host */
|
||||
Endpoint_ClearSetupIN();
|
||||
}
|
||||
|
||||
break;
|
||||
case REQ_SetProtocol:
|
||||
if (bmRequestType == (REQDIR_HOSTTODEVICE | REQTYPE_CLASS | REQREC_INTERFACE))
|
||||
{
|
||||
/* Read in the wValue parameter containing the new protocol mode */
|
||||
uint16_t wValue = Endpoint_Read_Word_LE();
|
||||
|
||||
Endpoint_ClearSetupReceived();
|
||||
|
||||
/* Set or clear the flag depending on what the host indicates that the current Protocol should be */
|
||||
UsingReportProtocol = (wValue != 0x0000);
|
||||
|
||||
/* Send an empty packet to acknowedge the command */
|
||||
Endpoint_ClearSetupIN();
|
||||
}
|
||||
|
||||
break;
|
||||
case REQ_SetIdle:
|
||||
if (bmRequestType == (REQDIR_HOSTTODEVICE | REQTYPE_CLASS | REQREC_INTERFACE))
|
||||
{
|
||||
/* Read in the wValue parameter containing the idle period */
|
||||
uint16_t wValue = Endpoint_Read_Word_LE();
|
||||
|
||||
Endpoint_ClearSetupReceived();
|
||||
|
||||
/* Get idle period in MSB */
|
||||
IdleCount = (wValue >> 8);
|
||||
|
||||
/* Send an empty packet to acknowedge the command */
|
||||
Endpoint_ClearSetupIN();
|
||||
}
|
||||
|
||||
break;
|
||||
case REQ_GetIdle:
|
||||
if (bmRequestType == (REQDIR_DEVICETOHOST | REQTYPE_CLASS | REQREC_INTERFACE))
|
||||
{
|
||||
Endpoint_ClearSetupReceived();
|
||||
|
||||
/* Write the current idle duration to the host */
|
||||
Endpoint_Write_Byte(IdleCount);
|
||||
|
||||
/* Send the flag to the host */
|
||||
Endpoint_ClearSetupIN();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/** ISR for the timer 0 compare vector. This ISR fires once each millisecond, and decrements the counter indicating
|
||||
* the number of milliseconds left to idle (not send the host reports) if the device has been instructed to idle
|
||||
* by the host via a SetIdle class specific request.
|
||||
*/
|
||||
ISR(TIMER0_COMPA_vect, ISR_BLOCK)
|
||||
{
|
||||
/* One millisecond has elapsed, decrement the idle time remaining counter if it has not already elapsed */
|
||||
if (IdleMSRemaining)
|
||||
IdleMSRemaining--;
|
||||
|
||||
if (KeyDelayRemaining)
|
||||
KeyDelayRemaining--;
|
||||
}
|
||||
|
||||
/** Constructs a keyboard report indicating the currently pressed keyboard keys to the host.
|
||||
*
|
||||
* \param ReportData Pointer to a USB_KeyboardReport_Data_t report structure where the resulting report should
|
||||
* be stored
|
||||
*
|
||||
* \return Boolean true if the current report is different to the previous report, false otherwise
|
||||
*/
|
||||
bool GetNextReport(USB_KeyboardReport_Data_t* ReportData)
|
||||
{
|
||||
static bool OddReport = false;
|
||||
static bool MustRelease = false;
|
||||
|
||||
BitBuffer_t* Buffer = NULL;
|
||||
|
||||
/* Clear the report contents */
|
||||
memset(ReportData, 0, sizeof(USB_KeyboardReport_Data_t));
|
||||
|
||||
/* Get the next non-empty track data buffer */
|
||||
if (Track1Data.Elements)
|
||||
Buffer = &Track1Data;
|
||||
else if (Track2Data.Elements)
|
||||
Buffer = &Track2Data;
|
||||
else if (Track3Data.Elements)
|
||||
Buffer = &Track3Data;
|
||||
|
||||
if (Buffer != NULL)
|
||||
{
|
||||
/* Toggle the odd report number indicator */
|
||||
OddReport = !OddReport;
|
||||
|
||||
/* Set the flag indicating that a null report must eventually be sent to release all pressed keys */
|
||||
MustRelease = true;
|
||||
|
||||
/* Only send the next key on odd reports, so that they are interpersed with null reports to release keys */
|
||||
if (OddReport)
|
||||
{
|
||||
/* Set the report key code to the key code for the next data bit */
|
||||
ReportData->KeyCode[0] = BitBuffer_GetNextBit(Buffer) ? KEY_1 : KEY_0;
|
||||
|
||||
/* If buffer is now empty, a new line must be sent instead of the terminating bit */
|
||||
if (!(Buffer->Elements))
|
||||
{
|
||||
/* Set the keycode to the code for an enter key press */
|
||||
ReportData->KeyCode[0] = KEY_ENTER;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (MustRelease)
|
||||
{
|
||||
/* Leave key code to null (0), to release all pressed keys */
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Task to read out data from inserted magnetic cards and place the seperate track data into their respective
|
||||
* data buffers for later sending to the host as keyboard key presses.
|
||||
*/
|
||||
TASK(Magstripe_Read)
|
||||
{
|
||||
/* Arrays to hold the buffer pointers, clock and data bit masks for the seperate card tracks */
|
||||
const struct
|
||||
{
|
||||
BitBuffer_t* Buffer;
|
||||
uint8_t ClockMask;
|
||||
uint8_t DataMask;
|
||||
} TrackInfo[] = {{&Track1Data, MAG_T1_CLOCK, MAG_T1_DATA},
|
||||
{&Track2Data, MAG_T2_CLOCK, MAG_T2_DATA},
|
||||
{&Track3Data, MAG_T3_CLOCK, MAG_T3_DATA}};
|
||||
|
||||
/* Previous magnetic card control line' status, for later comparison */
|
||||
uint8_t Magstripe_Prev = 0;
|
||||
|
||||
/* Buffered current card reader control line' status */
|
||||
uint8_t Magstripe_LCL = Magstripe_GetStatus();
|
||||
|
||||
/* Exit the task early if no card is present in the reader */
|
||||
if (!(Magstripe_LCL & MAG_CARDPRESENT))
|
||||
return;
|
||||
|
||||
/* Read out card data while a card is present */
|
||||
while (Magstripe_LCL & MAG_CARDPRESENT)
|
||||
{
|
||||
/* Read out the next bit for each track of the card */
|
||||
for (uint8_t Track = 0; Track < 3; Track++)
|
||||
{
|
||||
/* Current data line status for the current card track */
|
||||
bool DataLevel = ((Magstripe_LCL & TrackInfo[Track].DataMask) != 0);
|
||||
|
||||
/* Current clock line status for the current card track */
|
||||
bool ClockLevel = ((Magstripe_LCL & TrackInfo[Track].ClockMask) != 0);
|
||||
|
||||
/* Current track clock transition check */
|
||||
bool ClockChanged = (((Magstripe_LCL ^ Magstripe_Prev) & TrackInfo[Track].ClockMask) != 0);
|
||||
|
||||
/* Sample the next bit on the falling edge of the track's clock line, store key code into the track's buffer */
|
||||
if (ClockLevel && ClockChanged)
|
||||
BitBuffer_StoreNextBit(TrackInfo[Track].Buffer, DataLevel);
|
||||
}
|
||||
|
||||
/* Retain the current card reader control line states for later edge detection */
|
||||
Magstripe_Prev = Magstripe_LCL;
|
||||
|
||||
/* Retrieve the new card reader control line states */
|
||||
Magstripe_LCL = Magstripe_GetStatus();
|
||||
}
|
||||
|
||||
/* Add terminators to the end of each track buffer */
|
||||
BitBuffer_StoreNextBit(&Track1Data, 0);
|
||||
BitBuffer_StoreNextBit(&Track2Data, 0);
|
||||
BitBuffer_StoreNextBit(&Track3Data, 0);
|
||||
}
|
||||
|
||||
/** Task for the magnetic card reading and keyboard report generation. This task waits until a card is inserted,
|
||||
* then reads off the card data and sends it to the host as a series of keyboard keypresses via keyboard reports.
|
||||
*/
|
||||
TASK(USB_Keyboard_Report)
|
||||
{
|
||||
USB_KeyboardReport_Data_t KeyboardReportData;
|
||||
bool SendReport = false;
|
||||
|
||||
/* Check if the USB system is connected to a host */
|
||||
if (USB_IsConnected)
|
||||
{
|
||||
/* Select the Keyboard Report Endpoint */
|
||||
Endpoint_SelectEndpoint(KEYBOARD_EPNUM);
|
||||
|
||||
/* Check if Keyboard Endpoint Ready for Read/Write */
|
||||
if (Endpoint_ReadWriteAllowed())
|
||||
{
|
||||
/* Only fetch the next key to send once the period between key presses has elapsed */
|
||||
if (!(KeyDelayRemaining))
|
||||
{
|
||||
/* Create the next keyboard report for transmission to the host */
|
||||
SendReport = GetNextReport(&KeyboardReportData);
|
||||
}
|
||||
|
||||
/* Check if the idle period is set and has elapsed */
|
||||
if (IdleCount && !(IdleMSRemaining))
|
||||
{
|
||||
/* Idle period elapsed, indicate that a report must be sent */
|
||||
SendReport = true;
|
||||
|
||||
/* Reset the idle time remaining counter, must multiply by 4 to get the duration in milliseconds */
|
||||
IdleMSRemaining = (IdleCount << 2);
|
||||
}
|
||||
|
||||
/* Write the keyboard report if a report is to be sent to the host */
|
||||
if (SendReport)
|
||||
{
|
||||
/* Write Keyboard Report Data */
|
||||
Endpoint_Write_Stream_LE(&KeyboardReportData, sizeof(USB_KeyboardReport_Data_t));
|
||||
|
||||
/* Finalize the stream transfer to send the last packet */
|
||||
Endpoint_ClearCurrentBank();
|
||||
|
||||
/* Reset the key delay period counter */
|
||||
KeyDelayRemaining = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue