Major refactor of ACIO Launcher.
Moved debug stuff into Debug.cpp/h. Moved ACIO driver stuff into its own class.
This commit is contained in:
parent
501b54b98e
commit
77a8d517be
|
@ -0,0 +1,554 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "ACIO.h"
|
||||||
|
#include "Debug.h"
|
||||||
|
|
||||||
|
/* Length of an ACIO packet without payload or checksum */
|
||||||
|
#define HEADER_LENGTH 5
|
||||||
|
|
||||||
|
/* Minimum packet length including header and checksum */
|
||||||
|
#define MINIMUM_PACKET_LENGTH (HEADER_LENGTH + 1)
|
||||||
|
|
||||||
|
/* Start of Message */
|
||||||
|
#define SOM 0xAA
|
||||||
|
|
||||||
|
/* Location of various parts of the protocol */
|
||||||
|
#define ID_LOCATION 0
|
||||||
|
#define COMMAND_HIGH_LOCATION 1
|
||||||
|
#define COMMAND_LOW_LOCATION 2
|
||||||
|
#define LENGTH_HIGH_LOCATION 3
|
||||||
|
#define LENGTH_LOW_LOCATION 4
|
||||||
|
|
||||||
|
/* Number of times to try initializing a reader by sending baud probe */
|
||||||
|
#define INITIALIZATION_TRIES 16
|
||||||
|
|
||||||
|
/* Number of return baud probes to receive before we consider ourselves initialized */
|
||||||
|
#define MIN_PROBES_RECEIVED 5
|
||||||
|
|
||||||
|
#define WAIT() Sleep( 100 )
|
||||||
|
#define LONGWAIT() Sleep( 1000 )
|
||||||
|
|
||||||
|
ACIO::ACIO()
|
||||||
|
{
|
||||||
|
/* Initialize readers first */
|
||||||
|
printf( "Initializing readers" );
|
||||||
|
serial = InitReaders(readerCount);
|
||||||
|
printf( "\n" );
|
||||||
|
|
||||||
|
/* Get version of all readers */
|
||||||
|
for( unsigned int x = 0; x < readerCount; x++ )
|
||||||
|
{
|
||||||
|
/* Print out reader version in debug mode */
|
||||||
|
DEBUG_PRINTF( "Reader %d returned version %s\n", x + 1, getReaderVersion( serial, x ) );
|
||||||
|
|
||||||
|
/* Walk init routine */
|
||||||
|
initReader( serial, x, 0 );
|
||||||
|
initReader( serial, x, 1 );
|
||||||
|
initReader( serial, x, 2 );
|
||||||
|
|
||||||
|
/* Set ready for keys only */
|
||||||
|
setReaderState( serial, x, STATE_EJECT );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* For key input debouncing */
|
||||||
|
old_keypresses = (unsigned int *)malloc(sizeof(unsigned int) * readerCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
ACIO::~ACIO(void)
|
||||||
|
{
|
||||||
|
/* Kill memory used for keypresses */
|
||||||
|
free(old_keypresses);
|
||||||
|
|
||||||
|
/* Close the reader so we can let the game talk to it */
|
||||||
|
for( unsigned int y = 0; y < readerCount; y++ )
|
||||||
|
{
|
||||||
|
setReaderState( serial, y, STATE_EJECT );
|
||||||
|
}
|
||||||
|
CloseHandle( serial );
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ACIO::getCardInserted(int reader)
|
||||||
|
{
|
||||||
|
unsigned int currentpresses;
|
||||||
|
getReaderState( serial, reader, &state, ¤tpresses, cardId );
|
||||||
|
return (state == STATE_READ || state == STATE_INSERTED);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: For some reason this returns one card read in the past. So, on first
|
||||||
|
* read it returns garbage, on second read it returns the first card, etc. Fix
|
||||||
|
* that at some point.
|
||||||
|
*/
|
||||||
|
void ACIO::getCardID(int reader, unsigned char *outCardId)
|
||||||
|
{
|
||||||
|
while( !getCardInserted(reader) ) { requestCardId( serial, reader ); }
|
||||||
|
while( !getCardInserted(reader) ) { requestCardId( serial, reader ); }
|
||||||
|
memcpy(outCardId, cardId, CARD_LENGTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
reader_keypress_t ACIO::getPressedKey(int reader)
|
||||||
|
{
|
||||||
|
unsigned int currentpresses;
|
||||||
|
getReaderState( serial, reader, &state, ¤tpresses, cardId );
|
||||||
|
unsigned int keypresses = currentpresses & (~old_keypresses[reader]);
|
||||||
|
old_keypresses[reader] = currentpresses;
|
||||||
|
|
||||||
|
if (keypresses & 0x0002 )
|
||||||
|
{
|
||||||
|
return KEY_1;
|
||||||
|
}
|
||||||
|
if (keypresses & 0x0020 )
|
||||||
|
{
|
||||||
|
return KEY_2;
|
||||||
|
}
|
||||||
|
if (keypresses & 0x0200 )
|
||||||
|
{
|
||||||
|
return KEY_3;
|
||||||
|
}
|
||||||
|
if (keypresses & 0x0004 )
|
||||||
|
{
|
||||||
|
return KEY_4;
|
||||||
|
}
|
||||||
|
if (keypresses & 0x0040 )
|
||||||
|
{
|
||||||
|
return KEY_5;
|
||||||
|
}
|
||||||
|
if (keypresses & 0x0400 )
|
||||||
|
{
|
||||||
|
return KEY_6;
|
||||||
|
}
|
||||||
|
if (keypresses & 0x0008 )
|
||||||
|
{
|
||||||
|
return KEY_7;
|
||||||
|
}
|
||||||
|
if (keypresses & 0x0080 )
|
||||||
|
{
|
||||||
|
return KEY_8;
|
||||||
|
}
|
||||||
|
if (keypresses & 0x0800 )
|
||||||
|
{
|
||||||
|
return KEY_9;
|
||||||
|
}
|
||||||
|
if (keypresses & 0x0001 )
|
||||||
|
{
|
||||||
|
return KEY_0;
|
||||||
|
}
|
||||||
|
if (keypresses & 0x0010 )
|
||||||
|
{
|
||||||
|
return KEY_00;
|
||||||
|
}
|
||||||
|
if (keypresses & 0x0100 )
|
||||||
|
{
|
||||||
|
return KEY_BLANK;
|
||||||
|
}
|
||||||
|
|
||||||
|
return KEY_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ACIO::requestCardInsert(int reader)
|
||||||
|
{
|
||||||
|
setReaderState( serial, reader, STATE_EJECT );
|
||||||
|
|
||||||
|
/* Walk init routine again. For some reason, it reads the same
|
||||||
|
* card ID the second time through, so I just reinit. */
|
||||||
|
initReader( serial, reader, 0 );
|
||||||
|
initReader( serial, reader, 1 );
|
||||||
|
initReader( serial, reader, 2 );
|
||||||
|
|
||||||
|
setReaderState( serial, reader, STATE_NOT_READY );
|
||||||
|
setReaderState( serial, reader, STATE_GET_READY );
|
||||||
|
setReaderState( serial, reader, STATE_READY );
|
||||||
|
}
|
||||||
|
|
||||||
|
void ACIO::requestCardEject(int reader)
|
||||||
|
{
|
||||||
|
setReaderState( serial, reader, STATE_EJECT );
|
||||||
|
}
|
||||||
|
|
||||||
|
HANDLE ACIO::openSerial( const _TCHAR *arg, int baud )
|
||||||
|
{
|
||||||
|
HANDLE hSerial = CreateFile(arg, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
|
||||||
|
if (hSerial == INVALID_HANDLE_VALUE) { return hSerial; }
|
||||||
|
|
||||||
|
int rate;
|
||||||
|
|
||||||
|
switch( baud )
|
||||||
|
{
|
||||||
|
case 4800:
|
||||||
|
rate = CBR_4800;
|
||||||
|
break;
|
||||||
|
case 9600:
|
||||||
|
rate = CBR_9600;
|
||||||
|
break;
|
||||||
|
case 19200:
|
||||||
|
rate = CBR_19200;
|
||||||
|
break;
|
||||||
|
case 38400:
|
||||||
|
rate = CBR_38400;
|
||||||
|
break;
|
||||||
|
case 57600:
|
||||||
|
rate = CBR_57600;
|
||||||
|
break;
|
||||||
|
case 115200:
|
||||||
|
rate = CBR_115200;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
rate = CBR_9600;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
DCB dcbSerialParams = {0};
|
||||||
|
|
||||||
|
dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
|
||||||
|
dcbSerialParams.BaudRate = rate;
|
||||||
|
dcbSerialParams.ByteSize = 8;
|
||||||
|
dcbSerialParams.StopBits = ONESTOPBIT;
|
||||||
|
dcbSerialParams.Parity = NOPARITY;
|
||||||
|
dcbSerialParams.fOutxCtsFlow = 0;
|
||||||
|
dcbSerialParams.fOutxDsrFlow = 0;
|
||||||
|
dcbSerialParams.fDtrControl = DTR_CONTROL_DISABLE;
|
||||||
|
dcbSerialParams.fDsrSensitivity = 0;
|
||||||
|
dcbSerialParams.fOutX = 0;
|
||||||
|
dcbSerialParams.fInX = 0;
|
||||||
|
dcbSerialParams.fRtsControl = RTS_CONTROL_DISABLE;
|
||||||
|
|
||||||
|
SetCommState(hSerial, &dcbSerialParams);
|
||||||
|
|
||||||
|
COMMTIMEOUTS timeouts = { 0 };
|
||||||
|
|
||||||
|
timeouts.ReadIntervalTimeout = 1;
|
||||||
|
timeouts.ReadTotalTimeoutConstant = 1;
|
||||||
|
timeouts.ReadTotalTimeoutMultiplier = 1;
|
||||||
|
|
||||||
|
SetCommTimeouts(hSerial, &timeouts);
|
||||||
|
return hSerial;
|
||||||
|
}
|
||||||
|
|
||||||
|
const unsigned char * const ACIO::getPacketData(
|
||||||
|
unsigned int *readerId,
|
||||||
|
unsigned int *command,
|
||||||
|
unsigned int *len,
|
||||||
|
unsigned int *checksum,
|
||||||
|
const unsigned char * const packet,
|
||||||
|
unsigned int length
|
||||||
|
) {
|
||||||
|
/* Exit early if we didn't get a good packet */
|
||||||
|
if (length < MINIMUM_PACKET_LENGTH) { return NULL; }
|
||||||
|
|
||||||
|
for( unsigned int i = 0; i <= length - MINIMUM_PACKET_LENGTH; i++ )
|
||||||
|
{
|
||||||
|
const unsigned char * const data = &packet[i];
|
||||||
|
|
||||||
|
if( data[0] != SOM )
|
||||||
|
{
|
||||||
|
/* Get command */
|
||||||
|
*readerId = data[ID_LOCATION];
|
||||||
|
*command = (data[COMMAND_HIGH_LOCATION] << 8) | data[COMMAND_LOW_LOCATION];
|
||||||
|
*len = (data[LENGTH_HIGH_LOCATION] << 8) | data[LENGTH_LOW_LOCATION];
|
||||||
|
*checksum = data[(*len) + HEADER_LENGTH];
|
||||||
|
return data + HEADER_LENGTH;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char ACIO::calcPacketChecksum( const unsigned char * const data )
|
||||||
|
{
|
||||||
|
unsigned char *packet = (unsigned char *)data;
|
||||||
|
|
||||||
|
/* Dirty */
|
||||||
|
while( 1 )
|
||||||
|
{
|
||||||
|
/* Get to start of packet */
|
||||||
|
if( packet[0] == SOM )
|
||||||
|
{
|
||||||
|
packet++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int length = ((packet[LENGTH_HIGH_LOCATION] << 8) | packet[LENGTH_LOW_LOCATION]);
|
||||||
|
unsigned int sum = 0;
|
||||||
|
|
||||||
|
/* Skip SOM, not part of packet CRC */
|
||||||
|
for( unsigned int i = 0; i < length + HEADER_LENGTH; i++ )
|
||||||
|
{
|
||||||
|
sum += packet[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return sum & 0xFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int ACIO::probeReader( HANDLE serial )
|
||||||
|
{
|
||||||
|
const unsigned char packet_probe[] = { 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, };
|
||||||
|
unsigned char data[1024];
|
||||||
|
DWORD length;
|
||||||
|
|
||||||
|
WriteFile( serial, packet_probe, sizeof( packet_probe ), &length, 0 );
|
||||||
|
ReadFile( serial, data, sizeof( data ), &length, 0 );
|
||||||
|
|
||||||
|
if (length < MIN_PROBES_RECEIVED)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < length; i++)
|
||||||
|
{
|
||||||
|
if (data[i] != 0xAA)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear out any additional init the reader sends */
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
ReadFile( serial, data, sizeof( data ), &length, 0 );
|
||||||
|
if (length == 0)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int ACIO::getReaderCount( HANDLE serial )
|
||||||
|
{
|
||||||
|
const unsigned char count_probe[] = { 0xAA, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, };
|
||||||
|
unsigned char data[1024];
|
||||||
|
DWORD length;
|
||||||
|
|
||||||
|
WriteFile( serial, count_probe, sizeof( count_probe ), &length, 0 );
|
||||||
|
WAIT();
|
||||||
|
ReadFile( serial, data, sizeof( data ), &length, 0 );
|
||||||
|
|
||||||
|
unsigned int readerId;
|
||||||
|
unsigned int command;
|
||||||
|
unsigned int len;
|
||||||
|
unsigned int checksum;
|
||||||
|
const unsigned char * const payload = getPacketData( &readerId, &command, &len, &checksum, data, length );
|
||||||
|
|
||||||
|
if( payload == NULL ) { return 0; }
|
||||||
|
if( len != 1 ) { return 0; }
|
||||||
|
|
||||||
|
return payload[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
void ACIO::initReader( HANDLE serial, unsigned int id, int whichInit )
|
||||||
|
{
|
||||||
|
unsigned int init_length[3] = { 7, 7, 8 };
|
||||||
|
unsigned char init_probe[3][8] = { { 0xAA, (id + 1), 0x00, 0x03, 0x00, 0x00, 0xFF, },
|
||||||
|
{ 0xAA, (id + 1), 0x01, 0x00, 0x00, 0x00, 0xFF, },
|
||||||
|
{ 0xAA, (id + 1), 0x01, 0x30, 0x00, 0x01, 0x00, 0xFF, } };
|
||||||
|
unsigned char data[1024];
|
||||||
|
DWORD length;
|
||||||
|
|
||||||
|
/* Fix up checksum since ID is variable */
|
||||||
|
init_probe[whichInit][init_length[whichInit] - 1] = calcPacketChecksum( init_probe[whichInit] );
|
||||||
|
|
||||||
|
WriteFile( serial, init_probe[whichInit], init_length[whichInit], &length, 0 );
|
||||||
|
WAIT();
|
||||||
|
ReadFile( serial, data, sizeof( data ), &length, 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
const char * const ACIO::getReaderVersion( HANDLE serial, unsigned int id )
|
||||||
|
{
|
||||||
|
unsigned char version_probe[] = { 0xAA, (id + 1), 0x00, 0x02, 0x00, 0x00, 0xFF, };
|
||||||
|
static char version[33] = { 0x00 };
|
||||||
|
unsigned char data[1024];
|
||||||
|
DWORD length;
|
||||||
|
|
||||||
|
/* Fix up checksum since ID is variable */
|
||||||
|
version_probe[6] = calcPacketChecksum( version_probe );
|
||||||
|
|
||||||
|
WriteFile( serial, version_probe, sizeof( version_probe ), &length, 0 );
|
||||||
|
WAIT();
|
||||||
|
ReadFile( serial, data, sizeof( data ), &length, 0 );
|
||||||
|
|
||||||
|
unsigned int readerId;
|
||||||
|
unsigned int command;
|
||||||
|
unsigned int len;
|
||||||
|
unsigned int checksum;
|
||||||
|
const unsigned char * const payload = getPacketData( &readerId, &command, &len, &checksum, data, length );
|
||||||
|
|
||||||
|
if( payload != NULL && len == 44 )
|
||||||
|
{
|
||||||
|
memcpy( version, &payload[12], 32 );
|
||||||
|
version[32] = 0x00;
|
||||||
|
|
||||||
|
/* Spaces, for display */
|
||||||
|
for( int i = 0; i < 32; i++ )
|
||||||
|
{
|
||||||
|
if( version[i] == 0x00 ) { version[i] = 0x20; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (const char * const)version;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ACIO::getReaderState( HANDLE serial, unsigned int id, card_state_t *state, unsigned int *keypresses, unsigned char *cardId )
|
||||||
|
{
|
||||||
|
unsigned char state_probe[] = { 0xAA, (id + 1), 0x01, 0x34, 0x00, 0x01, 0x10, 0xFF, };
|
||||||
|
unsigned char data[1024];
|
||||||
|
DWORD length;
|
||||||
|
|
||||||
|
/* Fix up checksum since ID is variable */
|
||||||
|
state_probe[7] = calcPacketChecksum( state_probe );
|
||||||
|
|
||||||
|
/* Sane return */
|
||||||
|
*keypresses = 0;
|
||||||
|
|
||||||
|
WriteFile( serial, state_probe, sizeof( state_probe ), &length, 0 );
|
||||||
|
WAIT();
|
||||||
|
ReadFile( serial, data, sizeof( data ), &length, 0 );
|
||||||
|
|
||||||
|
unsigned int readerId;
|
||||||
|
unsigned int command;
|
||||||
|
unsigned int len;
|
||||||
|
unsigned int checksum;
|
||||||
|
const unsigned char * const payload = getPacketData( &readerId, &command, &len, &checksum, data, length );
|
||||||
|
|
||||||
|
if( payload != NULL && len == 16 )
|
||||||
|
{
|
||||||
|
*keypresses = (payload[14] << 8) | payload[15];
|
||||||
|
|
||||||
|
*state = STATE_UNKNOWN;
|
||||||
|
switch( payload[1] )
|
||||||
|
{
|
||||||
|
case 0x00:
|
||||||
|
*state = STATE_REMOVED;
|
||||||
|
break;
|
||||||
|
case 0x10:
|
||||||
|
*state = STATE_FRONT_SENSOR;
|
||||||
|
break;
|
||||||
|
case 0x30:
|
||||||
|
*state = STATE_INSERTED;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if( VERBOSE_DEBUG ) fprintf( stderr, "Unknown card state %02X!\n", payload[1] );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( payload[0] != 0x00 && payload[11] == 0x00 && *state == STATE_INSERTED )
|
||||||
|
{
|
||||||
|
memcpy( cardId, &payload[2], CARD_LENGTH );
|
||||||
|
*state = STATE_READ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ACIO::setReaderState( HANDLE serial, unsigned int id, reader_state_t state )
|
||||||
|
{
|
||||||
|
unsigned char state_request[] = { 0xAA, (id + 1), 0x01, 0x35, 0x00, 0x02, 0x10, 0xFF, 0xFF, };
|
||||||
|
unsigned char data[1024];
|
||||||
|
DWORD length;
|
||||||
|
|
||||||
|
/* Set state */
|
||||||
|
switch( state )
|
||||||
|
{
|
||||||
|
case STATE_NOT_READY:
|
||||||
|
state_request[7] = 0x00;
|
||||||
|
break;
|
||||||
|
case STATE_GET_READY:
|
||||||
|
state_request[7] = 0x03;
|
||||||
|
break;
|
||||||
|
case STATE_READY:
|
||||||
|
state_request[7] = 0x11;
|
||||||
|
break;
|
||||||
|
case STATE_EJECT:
|
||||||
|
state_request[7] = 0x12;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if( VERBOSE_DEBUG ) fprintf( stderr, "Unknown reader state %02X!\n", state );
|
||||||
|
state_request[7] = 0xFF;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fix up checksum since ID is variable */
|
||||||
|
state_request[8] = calcPacketChecksum( state_request );
|
||||||
|
|
||||||
|
WriteFile( serial, state_request, sizeof( state_request ), &length, 0 );
|
||||||
|
WAIT();
|
||||||
|
ReadFile( serial, data, sizeof( data ), &length, 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
void ACIO::requestCardId( HANDLE serial, unsigned int id )
|
||||||
|
{
|
||||||
|
unsigned char id_request[] = { 0xAA, (id + 1), 0x01, 0x31, 0x00, 0x01, 0x10, 0xFF, };
|
||||||
|
unsigned char data[1024];
|
||||||
|
DWORD length;
|
||||||
|
|
||||||
|
/* Fix up checksum since ID is variable */
|
||||||
|
id_request[7] = calcPacketChecksum( id_request );
|
||||||
|
|
||||||
|
WriteFile( serial, id_request, sizeof( id_request ), &length, 0 );
|
||||||
|
WAIT();
|
||||||
|
ReadFile( serial, data, sizeof( data ), &length, 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
HANDLE ACIO::InitReaders(unsigned int &readerCount)
|
||||||
|
{
|
||||||
|
/* Walk serial chain, finding readers */
|
||||||
|
HANDLE serial = NULL;
|
||||||
|
const _TCHAR *comport[4] = { L"COM1", L"COM2", L"COM3", L"COM4" };
|
||||||
|
const unsigned int baudrate[2] = { 57600, 9600 };
|
||||||
|
const char *pattern[4] = { "\b\b\b ", "\b\b\b.", ".", "." };
|
||||||
|
unsigned int which = 3;
|
||||||
|
unsigned int pno = 0;
|
||||||
|
|
||||||
|
/* Put a space into location where we will be backspacing, for laziness */
|
||||||
|
NON_DEBUG_PRINTF( " " );
|
||||||
|
DEBUG_PRINTF( "...\n" );
|
||||||
|
|
||||||
|
/* Try to open the reader indefinitely */
|
||||||
|
while( 1 )
|
||||||
|
{
|
||||||
|
/* Try next reader */
|
||||||
|
which = (which + 1) % 4;
|
||||||
|
|
||||||
|
for (unsigned int baud = 0; baud < 2; baud++)
|
||||||
|
{
|
||||||
|
NON_DEBUG_PRINTF( "%s", pattern[(pno++) % 4] );
|
||||||
|
DEBUG_PRINTF("Attempting to probe readers on %ls,%d\n", comport[which], baudrate[baud]);
|
||||||
|
serial = openSerial( comport[which], baudrate[baud] );
|
||||||
|
|
||||||
|
if (serial == INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
DEBUG_PRINTF("Couldn't open com port!\n");
|
||||||
|
serial = NULL;
|
||||||
|
LONGWAIT();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure we start fresh */
|
||||||
|
PurgeComm( serial, PURGE_RXABORT | PURGE_RXCLEAR | PURGE_TXABORT | PURGE_TXCLEAR );
|
||||||
|
|
||||||
|
/* Init */
|
||||||
|
for (int i = 0; i < INITIALIZATION_TRIES; i++)
|
||||||
|
{
|
||||||
|
if( probeReader( serial ) )
|
||||||
|
{
|
||||||
|
/* Get count */
|
||||||
|
unsigned int count = getReaderCount( serial );
|
||||||
|
if (count > 0)
|
||||||
|
{
|
||||||
|
DEBUG_PRINTF( "Saw %d readers!\n", count );
|
||||||
|
readerCount = count;
|
||||||
|
return serial;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NON_DEBUG_PRINTF( "%s", pattern[(pno++) % 4] );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Failed, close */
|
||||||
|
CloseHandle( serial );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Failed to probe this port, try again on next one */
|
||||||
|
DEBUG_PRINTF("Failed to probe readers!\n");
|
||||||
|
serial = NULL;
|
||||||
|
LONGWAIT();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <tchar.h>
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
/* Length of the card ID in bytes */
|
||||||
|
#define CARD_LENGTH 8
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
STATE_FRONT_SENSOR,
|
||||||
|
STATE_INSERTED,
|
||||||
|
STATE_READ,
|
||||||
|
STATE_REMOVED,
|
||||||
|
STATE_UNKNOWN,
|
||||||
|
} card_state_t;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
STATE_NOT_READY,
|
||||||
|
STATE_GET_READY,
|
||||||
|
STATE_READY,
|
||||||
|
STATE_EJECT,
|
||||||
|
} reader_state_t;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
KEY_NONE,
|
||||||
|
KEY_1,
|
||||||
|
KEY_2,
|
||||||
|
KEY_3,
|
||||||
|
KEY_4,
|
||||||
|
KEY_5,
|
||||||
|
KEY_6,
|
||||||
|
KEY_7,
|
||||||
|
KEY_8,
|
||||||
|
KEY_9,
|
||||||
|
KEY_0,
|
||||||
|
KEY_00,
|
||||||
|
KEY_BLANK,
|
||||||
|
} reader_keypress_t;
|
||||||
|
|
||||||
|
class ACIO
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ACIO(void);
|
||||||
|
~ACIO(void);
|
||||||
|
|
||||||
|
unsigned int getCount() { return readerCount; }
|
||||||
|
reader_keypress_t getPressedKey(int reader);
|
||||||
|
void requestCardInsert(int reader);
|
||||||
|
void requestCardEject(int reader);
|
||||||
|
bool getCardInserted(int reader);
|
||||||
|
void getCardID(int reader, unsigned char *cardId);
|
||||||
|
|
||||||
|
private:
|
||||||
|
unsigned int readerCount;
|
||||||
|
HANDLE serial;
|
||||||
|
unsigned int *old_keypresses;
|
||||||
|
unsigned char cardId[CARD_LENGTH];
|
||||||
|
card_state_t state;
|
||||||
|
|
||||||
|
HANDLE openSerial( const _TCHAR *arg, int baud );
|
||||||
|
const unsigned char * const getPacketData(
|
||||||
|
unsigned int *readerId,
|
||||||
|
unsigned int *command,
|
||||||
|
unsigned int *len,
|
||||||
|
unsigned int *checksum,
|
||||||
|
const unsigned char * const packet,
|
||||||
|
unsigned int length
|
||||||
|
);
|
||||||
|
unsigned char calcPacketChecksum( const unsigned char * const data );
|
||||||
|
int probeReader( HANDLE serial );
|
||||||
|
unsigned int getReaderCount( HANDLE serial );
|
||||||
|
void initReader( HANDLE serial, unsigned int id, int whichInit );
|
||||||
|
const char * const getReaderVersion( HANDLE serial, unsigned int id );
|
||||||
|
void getReaderState( HANDLE serial, unsigned int id, card_state_t *state, unsigned int *keypresses, unsigned char *cardId );
|
||||||
|
void setReaderState( HANDLE serial, unsigned int id, reader_state_t state );
|
||||||
|
void requestCardId( HANDLE serial, unsigned int id );
|
||||||
|
HANDLE InitReaders( unsigned int &readerCount );
|
||||||
|
};
|
651
ACIOLauncher.cpp
651
ACIOLauncher.cpp
|
@ -5,13 +5,9 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <tchar.h>
|
#include <tchar.h>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
#include "ACIO.h"
|
||||||
#include "Menu.h"
|
#include "Menu.h"
|
||||||
|
#include "Debug.h"
|
||||||
#define WAIT() Sleep( 100 )
|
|
||||||
#define LONGWAIT() Sleep( 1000 )
|
|
||||||
|
|
||||||
/* Define to 1 to get verbose debugging */
|
|
||||||
#define VERBOSE_DEBUG 0
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define to 1 to enable card reader mode, currently only works with
|
* Define to 1 to enable card reader mode, currently only works with
|
||||||
|
@ -22,458 +18,6 @@
|
||||||
*/
|
*/
|
||||||
#define CARD_READER_MODE 0
|
#define CARD_READER_MODE 0
|
||||||
|
|
||||||
/* Length of an ACIO packet without payload or checksum */
|
|
||||||
#define HEADER_LENGTH 5
|
|
||||||
|
|
||||||
/* Minimum packet length including header and checksum */
|
|
||||||
#define MINIMUM_PACKET_LENGTH (HEADER_LENGTH + 1)
|
|
||||||
|
|
||||||
/* Start of Message */
|
|
||||||
#define SOM 0xAA
|
|
||||||
|
|
||||||
/* Location of various parts of the protocol */
|
|
||||||
#define ID_LOCATION 0
|
|
||||||
#define COMMAND_HIGH_LOCATION 1
|
|
||||||
#define COMMAND_LOW_LOCATION 2
|
|
||||||
#define LENGTH_HIGH_LOCATION 3
|
|
||||||
#define LENGTH_LOW_LOCATION 4
|
|
||||||
|
|
||||||
/* Length of the card ID in bytes */
|
|
||||||
#define CARD_LENGTH 8
|
|
||||||
|
|
||||||
/* Number of times to try initializing a reader by sending baud probe */
|
|
||||||
#define INITIALIZATION_TRIES 16
|
|
||||||
|
|
||||||
/* Number of return baud probes to receive before we consider ourselves initialized */
|
|
||||||
#define MIN_PROBES_RECEIVED 5
|
|
||||||
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
STATE_FRONT_SENSOR,
|
|
||||||
STATE_INSERTED,
|
|
||||||
STATE_READ,
|
|
||||||
STATE_REMOVED,
|
|
||||||
STATE_UNKNOWN,
|
|
||||||
} card_state_t;
|
|
||||||
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
STATE_NOT_READY,
|
|
||||||
STATE_GET_READY,
|
|
||||||
STATE_READY,
|
|
||||||
STATE_EJECT,
|
|
||||||
} reader_state_t;
|
|
||||||
|
|
||||||
/* Debug global which can be set via command line flag --debug */
|
|
||||||
bool debug = false;
|
|
||||||
|
|
||||||
/* Debug macros that are enabled via the --debug flag */
|
|
||||||
#define DEBUG_PRINTF(...) do { if(debug) { printf(__VA_ARGS__); } } while(0)
|
|
||||||
#define DEBUG_PRINT_HEX(data, length) do { if(debug) { printHex(data, length); } } while(0)
|
|
||||||
|
|
||||||
/* Macros that are enabled when debug is not enabled */
|
|
||||||
#define NON_DEBUG_PRINTF(...) do { if(!debug) { printf(__VA_ARGS__); } } while(0)
|
|
||||||
|
|
||||||
void printHex(const unsigned char * const data, int length )
|
|
||||||
{
|
|
||||||
printf( "Length: %d bytes\n", length );
|
|
||||||
|
|
||||||
for( int x = 0; x < length; x++ )
|
|
||||||
{
|
|
||||||
printf( "%02X ", data[x] );
|
|
||||||
}
|
|
||||||
|
|
||||||
printf( "\n" );
|
|
||||||
}
|
|
||||||
|
|
||||||
HANDLE OpenSerial( const _TCHAR *arg, int baud )
|
|
||||||
{
|
|
||||||
HANDLE hSerial = CreateFile(arg, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
|
|
||||||
if (hSerial == INVALID_HANDLE_VALUE) { return hSerial; }
|
|
||||||
|
|
||||||
int rate;
|
|
||||||
|
|
||||||
switch( baud )
|
|
||||||
{
|
|
||||||
case 4800:
|
|
||||||
rate = CBR_4800;
|
|
||||||
break;
|
|
||||||
case 9600:
|
|
||||||
rate = CBR_9600;
|
|
||||||
break;
|
|
||||||
case 19200:
|
|
||||||
rate = CBR_19200;
|
|
||||||
break;
|
|
||||||
case 38400:
|
|
||||||
rate = CBR_38400;
|
|
||||||
break;
|
|
||||||
case 57600:
|
|
||||||
rate = CBR_57600;
|
|
||||||
break;
|
|
||||||
case 115200:
|
|
||||||
rate = CBR_115200;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
rate = CBR_9600;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
DCB dcbSerialParams = {0};
|
|
||||||
|
|
||||||
dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
|
|
||||||
dcbSerialParams.BaudRate = rate;
|
|
||||||
dcbSerialParams.ByteSize = 8;
|
|
||||||
dcbSerialParams.StopBits = ONESTOPBIT;
|
|
||||||
dcbSerialParams.Parity = NOPARITY;
|
|
||||||
dcbSerialParams.fOutxCtsFlow = 0;
|
|
||||||
dcbSerialParams.fOutxDsrFlow = 0;
|
|
||||||
dcbSerialParams.fDtrControl = DTR_CONTROL_DISABLE;
|
|
||||||
dcbSerialParams.fDsrSensitivity = 0;
|
|
||||||
dcbSerialParams.fOutX = 0;
|
|
||||||
dcbSerialParams.fInX = 0;
|
|
||||||
dcbSerialParams.fRtsControl = RTS_CONTROL_DISABLE;
|
|
||||||
|
|
||||||
SetCommState(hSerial, &dcbSerialParams);
|
|
||||||
|
|
||||||
COMMTIMEOUTS timeouts = { 0 };
|
|
||||||
|
|
||||||
timeouts.ReadIntervalTimeout = 1;
|
|
||||||
timeouts.ReadTotalTimeoutConstant = 1;
|
|
||||||
timeouts.ReadTotalTimeoutMultiplier = 1;
|
|
||||||
|
|
||||||
SetCommTimeouts(hSerial, &timeouts);
|
|
||||||
return hSerial;
|
|
||||||
}
|
|
||||||
|
|
||||||
const unsigned char * const getPacketData( unsigned int *readerId,
|
|
||||||
unsigned int *command,
|
|
||||||
unsigned int *len,
|
|
||||||
unsigned int *checksum,
|
|
||||||
const unsigned char * const packet,
|
|
||||||
unsigned int length )
|
|
||||||
{
|
|
||||||
/* Exit early if we didn't get a good packet */
|
|
||||||
if (length < MINIMUM_PACKET_LENGTH) { return NULL; }
|
|
||||||
|
|
||||||
for( unsigned int i = 0; i <= length - MINIMUM_PACKET_LENGTH; i++ )
|
|
||||||
{
|
|
||||||
const unsigned char * const data = &packet[i];
|
|
||||||
|
|
||||||
if( data[0] != SOM )
|
|
||||||
{
|
|
||||||
/* Get command */
|
|
||||||
*readerId = data[ID_LOCATION];
|
|
||||||
*command = (data[COMMAND_HIGH_LOCATION] << 8) | data[COMMAND_LOW_LOCATION];
|
|
||||||
*len = (data[LENGTH_HIGH_LOCATION] << 8) | data[LENGTH_LOW_LOCATION];
|
|
||||||
*checksum = data[(*len) + HEADER_LENGTH];
|
|
||||||
return data + HEADER_LENGTH;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static unsigned char calcPacketChecksum( const unsigned char * const data )
|
|
||||||
{
|
|
||||||
unsigned char *packet = (unsigned char *)data;
|
|
||||||
|
|
||||||
/* Dirty */
|
|
||||||
while( 1 )
|
|
||||||
{
|
|
||||||
/* Get to start of packet */
|
|
||||||
if( packet[0] == SOM )
|
|
||||||
{
|
|
||||||
packet++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int length = ((packet[LENGTH_HIGH_LOCATION] << 8) | packet[LENGTH_LOW_LOCATION]);
|
|
||||||
unsigned int sum = 0;
|
|
||||||
|
|
||||||
/* Skip SOM, not part of packet CRC */
|
|
||||||
for( unsigned int i = 0; i < length + HEADER_LENGTH; i++ )
|
|
||||||
{
|
|
||||||
sum += packet[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
return sum & 0xFF;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int probeReader( HANDLE serial )
|
|
||||||
{
|
|
||||||
const unsigned char packet_probe[] = { 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, };
|
|
||||||
unsigned char data[1024];
|
|
||||||
DWORD length;
|
|
||||||
|
|
||||||
WriteFile( serial, packet_probe, sizeof( packet_probe ), &length, 0 );
|
|
||||||
ReadFile( serial, data, sizeof( data ), &length, 0 );
|
|
||||||
|
|
||||||
if (length < MIN_PROBES_RECEIVED)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (unsigned int i = 0; i < length; i++)
|
|
||||||
{
|
|
||||||
if (data[i] != 0xAA)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Clear out any additional init the reader sends */
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
ReadFile( serial, data, sizeof( data ), &length, 0 );
|
|
||||||
if (length == 0)
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int getReaderCount( HANDLE serial )
|
|
||||||
{
|
|
||||||
const unsigned char count_probe[] = { 0xAA, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, };
|
|
||||||
unsigned char data[1024];
|
|
||||||
DWORD length;
|
|
||||||
|
|
||||||
WriteFile( serial, count_probe, sizeof( count_probe ), &length, 0 );
|
|
||||||
WAIT();
|
|
||||||
ReadFile( serial, data, sizeof( data ), &length, 0 );
|
|
||||||
|
|
||||||
unsigned int readerId;
|
|
||||||
unsigned int command;
|
|
||||||
unsigned int len;
|
|
||||||
unsigned int checksum;
|
|
||||||
const unsigned char * const payload = getPacketData( &readerId, &command, &len, &checksum, data, length );
|
|
||||||
|
|
||||||
if( payload == NULL ) { return 0; }
|
|
||||||
if( len != 1 ) { return 0; }
|
|
||||||
|
|
||||||
return payload[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
void initReader( HANDLE serial, unsigned int id, int whichInit )
|
|
||||||
{
|
|
||||||
unsigned int init_length[3] = { 7, 7, 8 };
|
|
||||||
unsigned char init_probe[3][8] = { { 0xAA, (id + 1), 0x00, 0x03, 0x00, 0x00, 0xFF, },
|
|
||||||
{ 0xAA, (id + 1), 0x01, 0x00, 0x00, 0x00, 0xFF, },
|
|
||||||
{ 0xAA, (id + 1), 0x01, 0x30, 0x00, 0x01, 0x00, 0xFF, } };
|
|
||||||
unsigned char data[1024];
|
|
||||||
DWORD length;
|
|
||||||
|
|
||||||
/* Fix up checksum since ID is variable */
|
|
||||||
init_probe[whichInit][init_length[whichInit] - 1] = calcPacketChecksum( init_probe[whichInit] );
|
|
||||||
|
|
||||||
WriteFile( serial, init_probe[whichInit], init_length[whichInit], &length, 0 );
|
|
||||||
WAIT();
|
|
||||||
ReadFile( serial, data, sizeof( data ), &length, 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
const char * const getReaderVersion( HANDLE serial, unsigned int id )
|
|
||||||
{
|
|
||||||
unsigned char version_probe[] = { 0xAA, (id + 1), 0x00, 0x02, 0x00, 0x00, 0xFF, };
|
|
||||||
static char version[33] = { 0x00 };
|
|
||||||
unsigned char data[1024];
|
|
||||||
DWORD length;
|
|
||||||
|
|
||||||
/* Fix up checksum since ID is variable */
|
|
||||||
version_probe[6] = calcPacketChecksum( version_probe );
|
|
||||||
|
|
||||||
WriteFile( serial, version_probe, sizeof( version_probe ), &length, 0 );
|
|
||||||
WAIT();
|
|
||||||
ReadFile( serial, data, sizeof( data ), &length, 0 );
|
|
||||||
|
|
||||||
unsigned int readerId;
|
|
||||||
unsigned int command;
|
|
||||||
unsigned int len;
|
|
||||||
unsigned int checksum;
|
|
||||||
const unsigned char * const payload = getPacketData( &readerId, &command, &len, &checksum, data, length );
|
|
||||||
|
|
||||||
if( payload != NULL && len == 44 )
|
|
||||||
{
|
|
||||||
memcpy( version, &payload[12], 32 );
|
|
||||||
version[32] = 0x00;
|
|
||||||
|
|
||||||
/* Spaces, for display */
|
|
||||||
for( int i = 0; i < 32; i++ )
|
|
||||||
{
|
|
||||||
if( version[i] == 0x00 ) { version[i] = 0x20; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (const char * const)version;
|
|
||||||
}
|
|
||||||
|
|
||||||
void getReaderState( HANDLE serial, unsigned int id, card_state_t *state, unsigned int *keypresses, unsigned char *cardId )
|
|
||||||
{
|
|
||||||
unsigned char state_probe[] = { 0xAA, (id + 1), 0x01, 0x34, 0x00, 0x01, 0x10, 0xFF, };
|
|
||||||
unsigned char data[1024];
|
|
||||||
DWORD length;
|
|
||||||
|
|
||||||
/* Fix up checksum since ID is variable */
|
|
||||||
state_probe[7] = calcPacketChecksum( state_probe );
|
|
||||||
|
|
||||||
/* Sane return */
|
|
||||||
*keypresses = 0;
|
|
||||||
memset( cardId, 0, CARD_LENGTH );
|
|
||||||
|
|
||||||
WriteFile( serial, state_probe, sizeof( state_probe ), &length, 0 );
|
|
||||||
WAIT();
|
|
||||||
ReadFile( serial, data, sizeof( data ), &length, 0 );
|
|
||||||
|
|
||||||
unsigned int readerId;
|
|
||||||
unsigned int command;
|
|
||||||
unsigned int len;
|
|
||||||
unsigned int checksum;
|
|
||||||
const unsigned char * const payload = getPacketData( &readerId, &command, &len, &checksum, data, length );
|
|
||||||
|
|
||||||
if( payload != NULL && len == 16 )
|
|
||||||
{
|
|
||||||
*keypresses = (payload[14] << 8) | payload[15];
|
|
||||||
|
|
||||||
*state = STATE_UNKNOWN;
|
|
||||||
switch( payload[1] )
|
|
||||||
{
|
|
||||||
case 0x00:
|
|
||||||
*state = STATE_REMOVED;
|
|
||||||
break;
|
|
||||||
case 0x10:
|
|
||||||
*state = STATE_FRONT_SENSOR;
|
|
||||||
break;
|
|
||||||
case 0x30:
|
|
||||||
*state = STATE_INSERTED;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if( VERBOSE_DEBUG ) fprintf( stderr, "Unknown card state %02X!\n", payload[1] );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( payload[0] != 0x00 && payload[11] == 0x00 && *state == STATE_INSERTED )
|
|
||||||
{
|
|
||||||
memcpy( cardId, &payload[2], CARD_LENGTH );
|
|
||||||
*state = STATE_READ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void setReaderState( HANDLE serial, unsigned int id, reader_state_t state )
|
|
||||||
{
|
|
||||||
unsigned char state_request[] = { 0xAA, (id + 1), 0x01, 0x35, 0x00, 0x02, 0x10, 0xFF, 0xFF, };
|
|
||||||
unsigned char data[1024];
|
|
||||||
DWORD length;
|
|
||||||
|
|
||||||
/* Set state */
|
|
||||||
switch( state )
|
|
||||||
{
|
|
||||||
case STATE_NOT_READY:
|
|
||||||
state_request[7] = 0x00;
|
|
||||||
break;
|
|
||||||
case STATE_GET_READY:
|
|
||||||
state_request[7] = 0x03;
|
|
||||||
break;
|
|
||||||
case STATE_READY:
|
|
||||||
state_request[7] = 0x11;
|
|
||||||
break;
|
|
||||||
case STATE_EJECT:
|
|
||||||
state_request[7] = 0x12;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if( VERBOSE_DEBUG ) fprintf( stderr, "Unknown reader state %02X!\n", state );
|
|
||||||
state_request[7] = 0xFF;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Fix up checksum since ID is variable */
|
|
||||||
state_request[8] = calcPacketChecksum( state_request );
|
|
||||||
|
|
||||||
WriteFile( serial, state_request, sizeof( state_request ), &length, 0 );
|
|
||||||
WAIT();
|
|
||||||
ReadFile( serial, data, sizeof( data ), &length, 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
void requestCardId( HANDLE serial, unsigned int id )
|
|
||||||
{
|
|
||||||
unsigned char id_request[] = { 0xAA, (id + 1), 0x01, 0x31, 0x00, 0x01, 0x10, 0xFF, };
|
|
||||||
unsigned char data[1024];
|
|
||||||
DWORD length;
|
|
||||||
|
|
||||||
/* Fix up checksum since ID is variable */
|
|
||||||
id_request[7] = calcPacketChecksum( id_request );
|
|
||||||
|
|
||||||
WriteFile( serial, id_request, sizeof( id_request ), &length, 0 );
|
|
||||||
WAIT();
|
|
||||||
ReadFile( serial, data, sizeof( data ), &length, 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
HANDLE InitReaders(unsigned int &readerCount)
|
|
||||||
{
|
|
||||||
/* Walk serial chain, finding readers */
|
|
||||||
HANDLE serial = NULL;
|
|
||||||
const _TCHAR *comport[4] = { L"COM1", L"COM2", L"COM3", L"COM4" };
|
|
||||||
const unsigned int baudrate[2] = { 57600, 9600 };
|
|
||||||
const char *pattern[4] = { "\b\b\b ", "\b\b\b.", ".", "." };
|
|
||||||
unsigned int which = 3;
|
|
||||||
unsigned int pno = 0;
|
|
||||||
|
|
||||||
/* Put a space into location where we will be backspacing, for laziness */
|
|
||||||
NON_DEBUG_PRINTF( " " );
|
|
||||||
DEBUG_PRINTF( "...\n" );
|
|
||||||
|
|
||||||
/* Try to open the reader indefinitely */
|
|
||||||
while( 1 )
|
|
||||||
{
|
|
||||||
/* Try next reader */
|
|
||||||
which = (which + 1) % 4;
|
|
||||||
|
|
||||||
for (unsigned int baud = 0; baud < 2; baud++)
|
|
||||||
{
|
|
||||||
NON_DEBUG_PRINTF( "%s", pattern[(pno++) % 4] );
|
|
||||||
DEBUG_PRINTF("Attempting to probe readers on %ls,%d\n", comport[which], baudrate[baud]);
|
|
||||||
serial = OpenSerial( comport[which], baudrate[baud] );
|
|
||||||
|
|
||||||
if (serial == INVALID_HANDLE_VALUE)
|
|
||||||
{
|
|
||||||
DEBUG_PRINTF("Couldn't open com port!\n");
|
|
||||||
serial = NULL;
|
|
||||||
LONGWAIT();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Ensure we start fresh */
|
|
||||||
PurgeComm( serial, PURGE_RXABORT | PURGE_RXCLEAR | PURGE_TXABORT | PURGE_TXCLEAR );
|
|
||||||
|
|
||||||
/* Init */
|
|
||||||
for (int i = 0; i < INITIALIZATION_TRIES; i++)
|
|
||||||
{
|
|
||||||
if( probeReader( serial ) )
|
|
||||||
{
|
|
||||||
/* Get count */
|
|
||||||
unsigned int count = getReaderCount( serial );
|
|
||||||
if (count > 0)
|
|
||||||
{
|
|
||||||
DEBUG_PRINTF( "Saw %d readers!\n", count );
|
|
||||||
readerCount = count;
|
|
||||||
return serial;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NON_DEBUG_PRINTF( "%s", pattern[(pno++) % 4] );
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Failed, close */
|
|
||||||
CloseHandle( serial );
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Failed to probe this port, try again on next one */
|
|
||||||
DEBUG_PRINTF("Failed to probe readers!\n");
|
|
||||||
serial = NULL;
|
|
||||||
LONGWAIT();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int _tmain(int argc, _TCHAR* argv[])
|
int _tmain(int argc, _TCHAR* argv[])
|
||||||
{
|
{
|
||||||
/* Ensure command is good */
|
/* Ensure command is good */
|
||||||
|
@ -522,157 +66,118 @@ int _tmain(int argc, _TCHAR* argv[])
|
||||||
DEBUG_PRINTF( "Enabling debug mode!\n" );
|
DEBUG_PRINTF( "Enabling debug mode!\n" );
|
||||||
|
|
||||||
/* Initialize menu */
|
/* Initialize menu */
|
||||||
Menu menu = Menu(inifile, debug);
|
Menu *menu = new Menu(inifile);
|
||||||
|
|
||||||
if( menu.NumberOfEntries() < 1 )
|
if( menu->NumberOfEntries() < 1 )
|
||||||
{
|
{
|
||||||
fprintf( stderr, "No games configured to launch!\n" );
|
fprintf( stderr, "No games configured to launch!\n" );
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Walk serial chain, finding readers */
|
/* Walk serial chain, finding readers */
|
||||||
unsigned int count = 0;
|
ACIO *readers = new ACIO();
|
||||||
printf( "Initializing readers" );
|
|
||||||
HANDLE serial = InitReaders(count);
|
|
||||||
printf( "\n" );
|
|
||||||
|
|
||||||
/* Get version of all readers */
|
|
||||||
for( unsigned int x = 0; x < count; x++ )
|
|
||||||
{
|
|
||||||
/* Print out reader version in debug mode */
|
|
||||||
DEBUG_PRINTF( "Reader %d returned version %s\n", x + 1, getReaderVersion( serial, x ) );
|
|
||||||
|
|
||||||
/* Walk init routine */
|
|
||||||
initReader( serial, x, 0 );
|
|
||||||
initReader( serial, x, 1 );
|
|
||||||
initReader( serial, x, 2 );
|
|
||||||
|
|
||||||
/* Set ready for keys only */
|
|
||||||
setReaderState( serial, x, STATE_EJECT );
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Whether we're in read mode */
|
/* Whether we're in read mode */
|
||||||
unsigned int read = 0;
|
unsigned int read = 0;
|
||||||
|
|
||||||
/* For key input debouncing */
|
|
||||||
unsigned int *old_keypresses = (unsigned int *)malloc(sizeof(unsigned int) * count);
|
|
||||||
|
|
||||||
/* Actual game to load */
|
/* Actual game to load */
|
||||||
char *path = NULL;
|
char *path = NULL;
|
||||||
bool selected = false;
|
bool selected = false;
|
||||||
|
|
||||||
/* It may have taken a long time to init */
|
/* It may have taken a long time to init */
|
||||||
menu.ResetTimeout();
|
menu->ResetTimeout();
|
||||||
|
|
||||||
/* Display user prompts */
|
/* Display user prompts */
|
||||||
menu.DisplayPrompt();
|
menu->DisplayPrompt();
|
||||||
menu.DisplayGames();
|
menu->DisplayGames();
|
||||||
|
|
||||||
/* Loop until time's up, then boot */
|
/* Loop until time's up, then boot */
|
||||||
while( !selected )
|
while( !selected )
|
||||||
{
|
{
|
||||||
menu.Tick();
|
menu->Tick();
|
||||||
if (menu.ShouldBootDefault())
|
if (menu->ShouldBootDefault())
|
||||||
{
|
{
|
||||||
printf( "No selection made, booting %s.\n", menu.GetSelectedName() );
|
printf( "No selection made, booting %s.\n", menu->GetSelectedName() );
|
||||||
path = menu.GetSelectedPath();
|
path = menu->GetSelectedPath();
|
||||||
selected = true;
|
selected = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
for( unsigned int x = 0; x < count; x++ )
|
for( unsigned int x = 0; x < readers->getCount(); x++ )
|
||||||
{
|
{
|
||||||
unsigned int game = 0;
|
unsigned int game = 0;
|
||||||
unsigned int currentpresses;
|
bool request_reader_toggle = false;
|
||||||
unsigned char cardId[CARD_LENGTH];
|
|
||||||
card_state_t state;
|
|
||||||
|
|
||||||
/* Find new presses only */
|
switch(readers->getPressedKey(x)) {
|
||||||
getReaderState( serial, x, &state, ¤tpresses, cardId );
|
case KEY_1:
|
||||||
unsigned int keypresses = currentpresses & (~old_keypresses[x]);
|
game = 1;
|
||||||
old_keypresses[x] = currentpresses;
|
break;
|
||||||
|
case KEY_2:
|
||||||
if (keypresses & 0x0002 )
|
game = 2;
|
||||||
{
|
break;
|
||||||
game = 1;
|
case KEY_3:
|
||||||
}
|
game = 3;
|
||||||
if (keypresses & 0x0020 )
|
break;
|
||||||
{
|
case KEY_4:
|
||||||
game = 2;
|
game = 4;
|
||||||
}
|
break;
|
||||||
if (keypresses & 0x0200 )
|
case KEY_5:
|
||||||
{
|
game = 5;
|
||||||
game = 3;
|
break;
|
||||||
}
|
case KEY_6:
|
||||||
if (keypresses & 0x0004 )
|
game = 6;
|
||||||
{
|
break;
|
||||||
game = 4;
|
case KEY_7:
|
||||||
}
|
game = 7;
|
||||||
if (keypresses & 0x0040 )
|
break;
|
||||||
{
|
case KEY_8:
|
||||||
game = 5;
|
game = 8;
|
||||||
}
|
break;
|
||||||
if (keypresses & 0x0400 )
|
case KEY_9:
|
||||||
{
|
game = 9;
|
||||||
game = 6;
|
break;
|
||||||
}
|
case KEY_0:
|
||||||
if (keypresses & 0x0008 )
|
menu->PageUp();
|
||||||
{
|
break;
|
||||||
game = 7;
|
case KEY_00:
|
||||||
}
|
menu->PageDown();
|
||||||
if (keypresses & 0x0080 )
|
break;
|
||||||
{
|
case KEY_BLANK:
|
||||||
game = 8;
|
request_reader_toggle = true;
|
||||||
}
|
break;
|
||||||
if (keypresses & 0x0800 )
|
|
||||||
{
|
|
||||||
game = 9;
|
|
||||||
}
|
|
||||||
if (keypresses & 0x0001 )
|
|
||||||
{
|
|
||||||
menu.PageUp();
|
|
||||||
}
|
|
||||||
if (keypresses & 0x0010 )
|
|
||||||
{
|
|
||||||
menu.PageDown();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (game != 0)
|
if (game != 0)
|
||||||
{
|
{
|
||||||
/* Chose a game! */
|
/* Chose a game! */
|
||||||
if (menu.SelectGame(game))
|
if (menu->SelectGame(game))
|
||||||
{
|
{
|
||||||
/* Time to boot this game! */
|
/* Time to boot this game! */
|
||||||
printf( "Booting %s.\n", menu.GetSelectedName() );
|
printf( "Booting %s.\n", menu->GetSelectedName() );
|
||||||
path = menu.GetSelectedPath();
|
path = menu->GetSelectedPath();
|
||||||
selected = true;
|
selected = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if( CARD_READER_MODE && (keypresses & 0x0100) )
|
if( CARD_READER_MODE && request_reader_toggle )
|
||||||
{
|
{
|
||||||
/* Pressed empty button, go into/out of card read mode */
|
/* Pressed empty button, go into/out of card read mode */
|
||||||
if (!read)
|
if (!read)
|
||||||
{
|
{
|
||||||
printf( "Entering card read mode, insert card!\n" );
|
printf( "Entering card read mode, insert card!\n" );
|
||||||
for( unsigned int y = 0; y < count; y++ )
|
for( unsigned int y = 0; y < readers->getCount(); y++ )
|
||||||
{
|
{
|
||||||
setReaderState( serial, y, STATE_EJECT );
|
readers->requestCardInsert(y);
|
||||||
setReaderState( serial, y, STATE_NOT_READY );
|
|
||||||
setReaderState( serial, y, STATE_GET_READY );
|
|
||||||
setReaderState( serial, y, STATE_READY );
|
|
||||||
}
|
}
|
||||||
read = 1;
|
read = 1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
printf( "Entering menu mode!\n" );
|
printf( "Entering menu mode!\n" );
|
||||||
for( unsigned int y = 0; y < count; y++ )
|
for( unsigned int y = 0; y < readers->getCount(); y++ )
|
||||||
{
|
{
|
||||||
setReaderState( serial, y, STATE_EJECT );
|
readers->requestCardEject(y);
|
||||||
setReaderState( serial, y, STATE_NOT_READY );
|
|
||||||
setReaderState( serial, y, STATE_GET_READY );
|
|
||||||
}
|
}
|
||||||
read = 0;
|
read = 0;
|
||||||
}
|
}
|
||||||
|
@ -683,46 +188,28 @@ int _tmain(int argc, _TCHAR* argv[])
|
||||||
if (!read) { continue; }
|
if (!read) { continue; }
|
||||||
|
|
||||||
/* Reset time, so we stay here forever */
|
/* Reset time, so we stay here forever */
|
||||||
menu.ResetTimeout();
|
menu->ResetTimeout();
|
||||||
|
|
||||||
if( state == STATE_INSERTED )
|
if( readers->getCardInserted(x) )
|
||||||
{
|
{
|
||||||
/* Ask for card */
|
/* Ask for card */
|
||||||
requestCardId( serial, x );
|
unsigned char cardId[CARD_LENGTH];
|
||||||
}
|
readers->getCardID(x, cardId);
|
||||||
|
|
||||||
if( state == STATE_READ )
|
|
||||||
{
|
|
||||||
/* Display card */
|
/* Display card */
|
||||||
printf( "Card read: %02X%02X%02X%02X%02X%02X%02X%02X\n",
|
printf( "Card read: %02X%02X%02X%02X%02X%02X%02X%02X\n",
|
||||||
cardId[0], cardId[1], cardId[2], cardId[3],
|
cardId[0], cardId[1], cardId[2], cardId[3],
|
||||||
cardId[4], cardId[5], cardId[6], cardId[7] );
|
cardId[4], cardId[5], cardId[6], cardId[7] );
|
||||||
|
|
||||||
LONGWAIT();
|
readers->requestCardEject(x);
|
||||||
setReaderState( serial, x, STATE_EJECT );
|
readers->requestCardInsert(x);
|
||||||
LONGWAIT();
|
|
||||||
|
|
||||||
/* Walk init routine again. For some reason, it reads the same
|
|
||||||
* card ID the second time through, so I just reinit. */
|
|
||||||
initReader( serial, x, 0 );
|
|
||||||
initReader( serial, x, 1 );
|
|
||||||
initReader( serial, x, 2 );
|
|
||||||
|
|
||||||
setReaderState( serial, x, STATE_NOT_READY );
|
|
||||||
setReaderState( serial, x, STATE_GET_READY );
|
|
||||||
setReaderState( serial, x, STATE_READY );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Close the reader so we can let the game talk to it */
|
/* Kill menu and ACIO connection */
|
||||||
for( unsigned int y = 0; y < count; y++ )
|
delete readers;
|
||||||
{
|
delete menu;
|
||||||
setReaderState( serial, y, STATE_EJECT );
|
|
||||||
setReaderState( serial, y, STATE_NOT_READY );
|
|
||||||
setReaderState( serial, y, STATE_GET_READY );
|
|
||||||
}
|
|
||||||
CloseHandle( serial );
|
|
||||||
|
|
||||||
if (path != NULL)
|
if (path != NULL)
|
||||||
{
|
{
|
||||||
|
|
|
@ -170,10 +170,18 @@
|
||||||
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
|
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
|
||||||
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
|
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
|
||||||
>
|
>
|
||||||
|
<File
|
||||||
|
RelativePath=".\ACIO.cpp"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath=".\ACIOLauncher.cpp"
|
RelativePath=".\ACIOLauncher.cpp"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\Debug.cpp"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath=".\Menu.cpp"
|
RelativePath=".\Menu.cpp"
|
||||||
>
|
>
|
||||||
|
@ -184,6 +192,14 @@
|
||||||
Filter="h;hpp;hxx;hm;inl;inc;xsd"
|
Filter="h;hpp;hxx;hm;inl;inc;xsd"
|
||||||
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
|
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
|
||||||
>
|
>
|
||||||
|
<File
|
||||||
|
RelativePath=".\ACIO.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\Debug.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath=".\Menu.h"
|
RelativePath=".\Menu.h"
|
||||||
>
|
>
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "Debug.h"
|
||||||
|
|
||||||
|
/* Debug global which can be set via command line flag --debug */
|
||||||
|
bool debug = false;
|
||||||
|
|
||||||
|
void printHex(const unsigned char * const data, int length )
|
||||||
|
{
|
||||||
|
printf( "Length: %d bytes\n", length );
|
||||||
|
|
||||||
|
for( int x = 0; x < length; x++ )
|
||||||
|
{
|
||||||
|
printf( "%02X ", data[x] );
|
||||||
|
}
|
||||||
|
|
||||||
|
printf( "\n" );
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
/* Define to 1 to get verbose debugging */
|
||||||
|
#define VERBOSE_DEBUG 0
|
||||||
|
|
||||||
|
/* Debug global which can be set via command line flag --debug */
|
||||||
|
extern bool debug;
|
||||||
|
|
||||||
|
/* Debug macros that are enabled via the --debug flag */
|
||||||
|
#define DEBUG_PRINTF(...) do { if(debug) { printf(__VA_ARGS__); } } while(0)
|
||||||
|
#define DEBUG_PRINT_HEX(data, length) do { if(debug) { printHex(data, length); } } while(0)
|
||||||
|
|
||||||
|
/* Macros that are enabled when debug is not enabled */
|
||||||
|
#define NON_DEBUG_PRINTF(...) do { if(!debug) { printf(__VA_ARGS__); } } while(0)
|
||||||
|
|
||||||
|
void printHex(const unsigned char * const data, int length );
|
6
Menu.cpp
6
Menu.cpp
|
@ -2,12 +2,10 @@
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
#include "Menu.h"
|
#include "Menu.h"
|
||||||
|
#include "Debug.h"
|
||||||
|
|
||||||
Menu::Menu(_TCHAR *inifile, bool isDebugEnabled)
|
Menu::Menu(_TCHAR *inifile)
|
||||||
{
|
{
|
||||||
/* Set up debug */
|
|
||||||
debug = isDebugEnabled;
|
|
||||||
|
|
||||||
/* Read settings */
|
/* Read settings */
|
||||||
settings = LoadSettings( inifile, &num_programs );
|
settings = LoadSettings( inifile, &num_programs );
|
||||||
|
|
||||||
|
|
3
Menu.h
3
Menu.h
|
@ -22,7 +22,7 @@ typedef struct
|
||||||
class Menu
|
class Menu
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Menu(_TCHAR *inifile, bool isDebugEnabled);
|
Menu(_TCHAR *inifile);
|
||||||
~Menu(void);
|
~Menu(void);
|
||||||
|
|
||||||
unsigned int NumberOfEntries() { return num_programs; }
|
unsigned int NumberOfEntries() { return num_programs; }
|
||||||
|
@ -38,7 +38,6 @@ class Menu
|
||||||
void ResetTimeout();
|
void ResetTimeout();
|
||||||
bool ShouldBootDefault();
|
bool ShouldBootDefault();
|
||||||
private:
|
private:
|
||||||
bool debug;
|
|
||||||
unsigned int num_programs;
|
unsigned int num_programs;
|
||||||
unsigned int start;
|
unsigned int start;
|
||||||
unsigned int end;
|
unsigned int end;
|
||||||
|
|
Loading…
Reference in New Issue