Commit of new class abstraction APIs for all device demos other than the MIDI demo - not documented yet.

Removed scheduler and memory allocation libraries.

Added new EVENT_USB_StartOfFrame event in the library to indicate the start of each USB frame (when generated).

Removed Tx interrupt from the USBtoSerial demo; now sends characters via polling to ensure more time for the Rx interrupt.
This commit is contained in:
Dean Camera 2009-06-01 11:03:39 +00:00
parent 2440ca268a
commit d1e5266036
106 changed files with 3072 additions and 5760 deletions

View file

@ -142,7 +142,7 @@ USB_Descriptor_Configuration_t PROGMEM ConfigurationDescriptor =
.HIDSpec = VERSION_BCD(01.11),
.CountryCode = 0x00,
.TotalHIDDescriptors = 1,
.TotalReportDescriptors = 1,
.HIDReportType = DTYPE_Report,
.HIDReportLength = sizeof(KeyboardReport)
},

View file

@ -38,34 +38,12 @@
#define _DESCRIPTORS_H_
/* Includes: */
#include <LUFA/Drivers/USB/USB.h>
#include <avr/pgmspace.h>
#include <LUFA/Drivers/USB/USB.h>
#include <LUFA/Drivers/USB/Class/Device/HID.h>
/* Type Defines: */
/** Type define for the HID class specific HID descriptor. A HID descriptor is used in HID class devices
* to give information about the HID device, including the HID specification used, and the report descriptors
* the device contains to describe how the HID device should be controlled.
*/
typedef struct
{
USB_Descriptor_Header_t Header; /**< Standard USB descriptor header */
uint16_t HIDSpec; /**< HID specification implemented by the device, in BCD form */
uint8_t CountryCode; /**< Country code for the country the HID device is localised for */
uint8_t TotalHIDDescriptors; /**< Total number of HID reports linked to this HID interface */
uint8_t HIDReportType; /**< Type of the first HID report descriptor */
uint16_t HIDReportLength; /**< Length of the first HID report descriptor */
} USB_Descriptor_HID_t;
/** Type define for the data type used for the HID Report descriptor data elements. A HID report
* descriptor contains an array of this data type, indicating how the reports from and to the
* device are formatted and how the report data is to be used by the host.
*/
typedef uint8_t USB_Descriptor_HIDReport_Datatype_t;
/** Type define for the device configuration descriptor structure. This must be defined in the
* application code, as the configuration descriptor contains several sub-descriptors which
* vary between devices, and which describe the device's usage to the host.
@ -85,14 +63,8 @@
/** Size of the keyboard report endpoints, in bytes. */
#define KEYBOARD_EPSIZE 8
/** Descriptor type value for a HID descriptor. */
#define DTYPE_HID 0x21
/** Descriptor type value for a HID report. */
#define DTYPE_Report 0x22
/* Function Prototypes: */
uint16_t CALLBACK_USB_GetDescriptor(const uint16_t wValue, const uint8_t wIndex, void** const DescriptorAddress)
ATTR_WARN_UNUSED_RESULT ATTR_NON_NULL_PTR_ARG(3);
ATTR_WARN_UNUSED_RESULT ATTR_NON_NULL_PTR_ARG(3);
#endif

View file

@ -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 },
};
BitBuffer_t TrackDataBuffers[3];
/* 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;
USB_ClassInfo_HID_t Keyboard_HID_Interface =
{
.InterfaceNumber = 0,
/** 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;
.ReportINEndpointNumber = KEYBOARD_EPNUM,
.ReportINEndpointSize = KEYBOARD_EPSIZE,
};
/** 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 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;
/** 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)
{
SetupHardware();
for (uint8_t Buffer = 0; Buffer < 3; Buffer++)
BitBuffer_Init(&TrackDataBuffers[Buffer]);
for (;;)
{
if (Magstripe_GetStatus() & MAG_CARDPRESENT)
ReadMagstripeData();
USB_HID_USBTask(&Keyboard_HID_Interface);
USB_USBTask();
}
}
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)
{
/* 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. */
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);
}
/** 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);
}
/** 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;
}
}
/** 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 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;
}
}
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 separate track data into their respective
* data buffers for later sending to the host as keyboard key presses.
*/
TASK(Magstripe_Read)
void ReadMagstripeData(void)
{
/* Arrays to hold the buffer pointers, clock and data bit masks for the separate 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}};
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}};
/* 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);
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);
/* 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);
if (ClockPinLevel && ClockLevelChanged)
BitBuffer_StoreNextBit(&TrackDataBuffers[Track], DataPinLevel);
}
/* 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 key presses via keyboard reports.
*/
TASK(USB_Keyboard_Report)
void EVENT_USB_ConfigurationChanged(void)
{
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;
}
}
}
USB_HID_ConfigureEndpoints(&Keyboard_HID_Interface);
}
void EVENT_USB_UnhandledControlPacket(void)
{
USB_HID_ProcessControlPacket(&Keyboard_HID_Interface);
}
void EVENT_USB_StartOfFrame(void)
{
USB_HID_RegisterStartOfFrame(&Keyboard_HID_Interface);
}
uint16_t CALLBACK_USB_HID_CreateNextHIDReport(USB_ClassInfo_HID_t* HIDInterfaceInfo, void* ReportData)
{
static bool IsKeyReleaseReport;
static bool IsNewlineReport;
BitBuffer_t* Buffer = NULL;
USB_KeyboardReport_Data_t* KeyboardReport = (USB_KeyboardReport_Data_t*)ReportData;
/* Key reports must be interleaved with 0 Key Code reports to release the keys, or repeated keys will be ignored */
IsKeyReleaseReport = !IsKeyReleaseReport;
if (IsKeyReleaseReport)
{
KeyboardReport->KeyCode = 0;
}
else if (IsNewlineReport)
{
IsNewlineReport = false;
KeyboardReport->KeyCode = KEY_ENTER;
}
else
{
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;
/* If buffer now empty, next report must be a newline to seperate track data */
if (!(Buffer->Elements))
IsNewlineReport = true;
}
return sizeof(USB_KeyboardReport_Data_t);
}
void CALLBACK_USB_HID_ProcessReceivedHIDReport(USB_ClassInfo_HID_t* HIDInterfaceInfo, void* ReportData, uint16_t ReportSize)
{
// Unused (but mandatory for the HID class driver) in this demo, since there are no Host->Device reports
}

View file

@ -40,46 +40,17 @@
/* Includes: */
#include <avr/io.h>
#include <avr/wdt.h>
#include <avr/interrupt.h>
#include <avr/power.h>
#include <stdbool.h>
#include <string.h>
#include "Descriptors.h"
#include "Lib/MagstripeHW.h"
#include "Lib/CircularBitBuffer.h"
#include <LUFA/Version.h> // Library Version Information
#include <LUFA/Drivers/USB/USB.h> // USB Functionality
#include <LUFA/Scheduler/Scheduler.h> // Simple scheduler for task management
/* Task Definitions: */
/** Task definition for the keyboard and magnetic card reading task. */
TASK(USB_Keyboard_Report);
TASK(Magstripe_Read);
#include <LUFA/Version.h>
#include <LUFA/Drivers/USB/USB.h>
#include <LUFA/Drivers/USB/Class/Device/HID.h>
/* Macros: */
/** HID Class Specific Request to get the current HID report from the device. */
#define REQ_GetReport 0x01
/** HID Class Specific Request to get the current device idle count. */
#define REQ_GetIdle 0x02
/** HID Class Specific Request to set the current HID report to the device. */
#define REQ_SetReport 0x09
/** HID Class Specific Request to set the device's idle count. */
#define REQ_SetIdle 0x0A
/** HID Class Specific Request to get the current HID report protocol mode. */
#define REQ_GetProtocol 0x03
/** HID Class Specific Request to set the current HID report protocol mode. */
#define REQ_SetProtocol 0x0B
/** HID keyboard keycode to indicate that the "1" key is currently pressed. */
#define KEY_1 30
@ -102,11 +73,15 @@
} USB_KeyboardReport_Data_t;
/* Function Prototypes: */
void EVENT_USB_Connect(void);
void EVENT_USB_Disconnect(void);
void SetupHardware(void);
void ReadMagstripeData(void);
void EVENT_USB_ConfigurationChanged(void);
void EVENT_USB_UnhandledControlPacket(void);
bool GetNextReport(USB_KeyboardReport_Data_t* ReportData);
void EVENT_USB_StartOfFrame(void);
uint16_t CALLBACK_USB_HID_CreateNextHIDReport(USB_ClassInfo_HID_t* HIDInterfaceInfo, void* ReportData);
void CALLBACK_USB_HID_ProcessReceivedHIDReport(USB_ClassInfo_HID_t* HIDInterfaceInfo,
void* ReportData, uint16_t ReportSize);
#endif

View file

@ -126,7 +126,6 @@ LUFA_PATH = ../..
SRC = $(TARGET).c \
Descriptors.c \
Lib/CircularBitBuffer.c \
$(LUFA_PATH)/LUFA/Scheduler/Scheduler.c \
$(LUFA_PATH)/LUFA/Drivers/USB/LowLevel/DevChapter9.c \
$(LUFA_PATH)/LUFA/Drivers/USB/LowLevel/Endpoint.c \
$(LUFA_PATH)/LUFA/Drivers/USB/LowLevel/Host.c \
@ -137,7 +136,7 @@ SRC = $(TARGET).c \
$(LUFA_PATH)/LUFA/Drivers/USB/HighLevel/USBInterrupt.c \
$(LUFA_PATH)/LUFA/Drivers/USB/HighLevel/USBTask.c \
$(LUFA_PATH)/LUFA/Drivers/USB/HighLevel/ConfigDescriptor.c \
$(LUFA_PATH)/LUFA/Drivers/USB/Class/HIDParser.c \
$(LUFA_PATH)/LUFA/Drivers/USB/Class/Device/HID.c \
# List C++ source files here. (C dependencies are automatically generated.)