aboutsummaryrefslogtreecommitdiffstats
path: root/Projects/Magstripe/Magstripe.c
diff options
context:
space:
mode:
Diffstat (limited to 'Projects/Magstripe/Magstripe.c')
-rw-r--r--Projects/Magstripe/Magstripe.c422
1 files changed, 83 insertions, 339 deletions
diff --git a/Projects/Magstripe/Magstripe.c b/Projects/Magstripe/Magstripe.c
index e860d5aca..5cd107e9a 100644
--- a/Projects/Magstripe/Magstripe.c
+++ b/Projects/Magstripe/Magstripe.c
@@ -28,60 +28,37 @@
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"
-/* 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;
+BitBuffer_t TrackDataBuffers[3];
-/** 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;
+USB_ClassInfo_HID_t Keyboard_HID_Interface =
+ {
+ .InterfaceNumber = 0,
-/** Circular buffer to hold the read bits from track 2 of the inserted magnetic card. */
-BitBuffer_t Track2Data;
+ .ReportINEndpointNumber = KEYBOARD_EPNUM,
+ .ReportINEndpointSize = KEYBOARD_EPSIZE,
+ };
-/** Circular buffer to hold the read bits from track 3 of the inserted magnetic card. */
-BitBuffer_t Track3Data;
+int main(void)
+{
+ SetupHardware();
+
+ for (uint8_t Buffer = 0; Buffer < 3; Buffer++)
+ BitBuffer_Init(&TrackDataBuffers[Buffer]);
-/** Delay counter between successive 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;
+ for (;;)
+ {
+ if (Magstripe_GetStatus() & MAG_CARDPRESENT)
+ ReadMagstripeData();
+ USB_HID_USBTask(&Keyboard_HID_Interface);
+ USB_USBTask();
+ }
+}
-/** 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)
+void SetupHardware(void)
{
/* Disable watchdog if enabled by bootloader/fuses */
MCUSR &= ~(1 << WDRF);
@@ -92,330 +69,97 @@ int main(void)
/* 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. */
-void EVENT_USB_Connect(void)
+void ReadMagstripeData(void)
{
- /* Start USB management task */
- Scheduler_SetTaskMode(USB_USBTask, TASK_RUN);
-}
+ /* Arrays to hold the buffer pointers, clock and data bit masks for the separate card tracks */
+ const struct
+ {
+ uint8_t ClockMask;
+ uint8_t DataMask;
+ } TrackInfo[] = {{MAG_T1_CLOCK, MAG_T1_DATA},
+ {MAG_T2_CLOCK, MAG_T2_DATA},
+ {MAG_T3_CLOCK, MAG_T3_DATA}};
-/** Event handler for the USB_Disconnect event. This stops the USB and keyboard report tasks. */
-void EVENT_USB_Disconnect(void)
-{
- /* 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);
+ uint8_t Magstripe_Prev = 0;
+ uint8_t Magstripe_LCL = Magstripe_GetStatus();
+
+ while (Magstripe_LCL & MAG_CARDPRESENT)
+ {
+ for (uint8_t Track = 0; Track < 3; Track++)
+ {
+ bool DataPinLevel = ((Magstripe_LCL & TrackInfo[Track].DataMask) != 0);
+ bool ClockPinLevel = ((Magstripe_LCL & TrackInfo[Track].ClockMask) != 0);
+ bool ClockLevelChanged = (((Magstripe_LCL ^ Magstripe_Prev) & TrackInfo[Track].ClockMask) != 0);
+
+ if (ClockPinLevel && ClockLevelChanged)
+ BitBuffer_StoreNextBit(&TrackDataBuffers[Track], DataPinLevel);
+ }
+
+ Magstripe_Prev = Magstripe_LCL;
+ Magstripe_LCL = Magstripe_GetStatus();
+ }
}
-/** 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.
- */
void EVENT_USB_ConfigurationChanged(void)
{
- /* 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);
+ USB_HID_ConfigureEndpoints(&Keyboard_HID_Interface);
}
-/** 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.
- */
void EVENT_USB_UnhandledControlPacket(void)
{
- /* Handle HID Class specific requests */
- switch (USB_ControlRequest.bRequest)
- {
- case REQ_GetReport:
- if (USB_ControlRequest.bmRequestType == (REQDIR_DEVICETOHOST | REQTYPE_CLASS | REQREC_INTERFACE))
- {
- USB_KeyboardReport_Data_t KeyboardReportData;
-
- /* Create the next keyboard report for transmission to the host */
- GetNextReport(&KeyboardReportData);
-
- Endpoint_ClearSETUP();
-
- /* Write the report data to the control endpoint */
- Endpoint_Write_Control_Stream_LE(&KeyboardReportData, sizeof(KeyboardReportData));
-
- /* Finalize the stream transfer to send the last packet or clear the host abort */
- Endpoint_ClearOUT();
- }
-
- break;
- case REQ_GetProtocol:
- if (USB_ControlRequest.bmRequestType == (REQDIR_DEVICETOHOST | REQTYPE_CLASS | REQREC_INTERFACE))
- {
- Endpoint_ClearSETUP();
-
- /* Write the current protocol flag to the host */
- Endpoint_Write_Byte(UsingReportProtocol);
-
- /* Send the flag to the host */
- Endpoint_ClearIN();
-
- /* Acknowledge status stage */
- while (!(Endpoint_IsOUTReceived()));
- Endpoint_ClearOUT();
- }
-
- break;
- case REQ_SetProtocol:
- if (USB_ControlRequest.bmRequestType == (REQDIR_HOSTTODEVICE | REQTYPE_CLASS | REQREC_INTERFACE))
- {
- Endpoint_ClearSETUP();
-
- /* Set or clear the flag depending on what the host indicates that the current Protocol should be */
- UsingReportProtocol = (USB_ControlRequest.wValue != 0x0000);
-
- /* Acknowledge status stage */
- while (!(Endpoint_IsINReady()));
- Endpoint_ClearIN();
- }
-
- break;
- case REQ_SetIdle:
- if (USB_ControlRequest.bmRequestType == (REQDIR_HOSTTODEVICE | REQTYPE_CLASS | REQREC_INTERFACE))
- {
- Endpoint_ClearSETUP();
-
- /* Get idle period in MSB */
- IdleCount = (USB_ControlRequest.wValue >> 8);
-
- /* Acknowledge status stage */
- while (!(Endpoint_IsINReady()));
- Endpoint_ClearIN();
- }
-
- break;
- case REQ_GetIdle:
- if (USB_ControlRequest.bmRequestType == (REQDIR_DEVICETOHOST | REQTYPE_CLASS | REQREC_INTERFACE))
- {
- Endpoint_ClearSETUP();
-
- /* Write the current idle duration to the host */
- Endpoint_Write_Byte(IdleCount);
-
- /* Send the flag to the host */
- Endpoint_ClearIN();
-
- /* Acknowledge status stage */
- while (!(Endpoint_IsOUTReceived()));
- Endpoint_ClearOUT();
- }
-
- break;
- }
+ USB_HID_ProcessControlPacket(&Keyboard_HID_Interface);
}
-/** 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)
+void EVENT_USB_StartOfFrame(void)
{
- /* One millisecond has elapsed, decrement the idle time remaining counter if it has not already elapsed */
- if (IdleMSRemaining)
- IdleMSRemaining--;
-
- if (KeyDelayRemaining)
- KeyDelayRemaining--;
+ USB_HID_RegisterStartOfFrame(&Keyboard_HID_Interface);
}
-/** 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)
+uint16_t CALLBACK_USB_HID_CreateNextHIDReport(USB_ClassInfo_HID_t* HIDInterfaceInfo, void* 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));
+ static bool IsKeyReleaseReport;
+ static bool IsNewlineReport;
- /* 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;
+ BitBuffer_t* Buffer = NULL;
+ USB_KeyboardReport_Data_t* KeyboardReport = (USB_KeyboardReport_Data_t*)ReportData;
- /* 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 interspersed with null reports to release keys */
- if (OddReport)
- {
- /* Set the report key code to the key code for the next data bit */
- ReportData->KeyCode = 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 = KEY_ENTER;
- }
- }
+ /* Key reports must be interleaved with 0 Key Code reports to release the keys, or repeated keys will be ignored */
+ IsKeyReleaseReport = !IsKeyReleaseReport;
- return true;
- }
- else if (MustRelease)
+ if (IsKeyReleaseReport)
{
- /* Leave key code to null (0), to release all pressed keys */
- return true;
+ KeyboardReport->KeyCode = 0;
}
-
- return false;
-}
-
-/** Task to read out data from inserted magnetic cards and place the separate 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 separate card tracks */
- const struct
+ else if (IsNewlineReport)
{
- 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)
+ IsNewlineReport = false;
+ KeyboardReport->KeyCode = KEY_ENTER;
+ }
+ else
{
- /* 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;
+ if (TrackDataBuffers[0].Elements)
+ Buffer = &TrackDataBuffers[0];
+ else if (TrackDataBuffers[1].Elements)
+ Buffer = &TrackDataBuffers[1];
+ else if (TrackDataBuffers[2].Elements)
+ Buffer = &TrackDataBuffers[2];
+ else
+ return 0;
+
+ KeyboardReport->KeyCode = BitBuffer_GetNextBit(Buffer) ? KEY_1 : KEY_0;
- /* Retrieve the new card reader control line states */
- Magstripe_LCL = Magstripe_GetStatus();
+ /* If buffer now empty, next report must be a newline to seperate track data */
+ if (!(Buffer->Elements))
+ IsNewlineReport = true;
}
- /* Add terminators to the end of each track buffer */
- BitBuffer_StoreNextBit(&Track1Data, 0);
- BitBuffer_StoreNextBit(&Track2Data, 0);
- BitBuffer_StoreNextBit(&Track3Data, 0);
+ return sizeof(USB_KeyboardReport_Data_t);
}
-/** 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 key presses via keyboard reports.
- */
-TASK(USB_Keyboard_Report)
+void CALLBACK_USB_HID_ProcessReceivedHIDReport(USB_ClassInfo_HID_t* HIDInterfaceInfo, void* ReportData, uint16_t ReportSize)
{
- 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_IsReadWriteAllowed())
- {
- /* 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_ClearIN();
-
- /* Reset the key delay period counter */
- KeyDelayRemaining = 2;
- }
- }
- }
+ // Unused (but mandatory for the HID class driver) in this demo, since there are no Host->Device reports
}