int 
SPIDevice_Read(LPSKYETEK_DEVICE device,
		unsigned char* buffer,
		unsigned int length,
    unsigned int timeout
    )
{
  LPSPI_INFO info;
	int bytes = 0;
  unsigned char req = 0x00;
  unsigned int i;
  unsigned char bit;
  aa_u16 len = (aa_u16)length;

  if( (device == NULL) || (buffer == NULL) || (device->user == NULL) )
		return 0;
  if( length == 0 )
    return 0;

  info = (LPSPI_INFO)device->user;
  if( info->spiHandle < 1 )
    return SKYETEK_INVALID_PARAMETER;

  SKYETEK_Sleep(100);

	MUTEX_LOCK(&info->lock);
  if( info->type == AA_FEATURE_I2C )
  {
    bytes = aa_i2c_read(info->spiHandle, 0x007F, 0, len, buffer);
  }
  else
  {
    for( i = 0; i < length; i++ )
    {
      bit = aa_gpio_get(info->spiHandle);
      if( bit & 0x01 )
        bytes += aa_spi_write(info->spiHandle, 1, &req, &buffer[i]);
      else
        SKYETEK_Sleep(10);
    }
  }
  MUTEX_UNLOCK(&info->lock);
  return bytes;
}
int 
GetReaderVersion(
  LPSKYETEK_DEVICE    lpDevice
  )
{
  LPDEVICEIMPL lpDI = NULL;
  LPSKYETEK_DATA lpData = NULL;
  TCHAR *msg = NULL;
  int bytes = 0, len = 0;

  if( lpDevice == NULL || lpDevice->internal == NULL )
    return 0;

  lpDI = (LPDEVICEIMPL)lpDevice->internal;

  lpData = SkyeTek_AllocateData(64);
  lpData->data[0] = 0x02;
  lpData->data[1] = 0x00;
  lpData->data[2] = 0x01;

  bytes = lpDI->Write(lpDevice,lpData->data,3,500);
  if( bytes != 3 )
  {
    SkyeTek_FreeData(lpData);
    return 0;
  }
  lpDI->Flush(lpDevice);
  SkyeTek_Debug(_T("Sent: 020001\r\n"));

  SKYETEK_Sleep(100);
  bytes = lpDI->Read(lpDevice,lpData->data,3,500);

  lpData->size = bytes;
  msg = SkyeTek_GetStringFromData(lpData);
  if( msg != NULL )
  {
    SkyeTek_Debug(_T("Read: %s\r\n"), msg);
    SkyeTek_FreeString(msg);
    msg = NULL;
  }
  
  // not a reader
  if( bytes == 0 )
  {
    SkyeTek_FreeData(lpData);
    return 0;
  }

  // bootloader mode
  if( bytes == 2 && lpData->data[1] == 0xFF )
  {
    SkyeTek_FreeData(lpData);
    return -1;
  }
  else if( bytes == 3 && lpData->data[0] == 0x00 && lpData->data[1] == 0x03 )
  {
    SkyeTek_FreeData(lpData);
    return -1;
  }

  // something else?
  if( bytes != 3 )
  {
    SkyeTek_FreeData(lpData);
    return 0;
  }
  
  /* Version 2 */
  if( lpData->data[0] == 0x02 && lpData->data[1] == 0x01 && lpData->data[2] == 0x82 )
  {
    SkyeTek_FreeData(lpData);
    return 2;
  }
  if( lpData->data[0] == 0x02 && lpData->data[1] == 0x01 && lpData->data[2] == 0x81 )
  {
    SkyeTek_FreeData(lpData);
    return 2;
  }
  if( lpData->data[0] == 0x02 && lpData->data[1] == 0x04 && lpData->data[2] == 0x88 )
  {
    bytes = lpDI->Read(lpDevice,lpData->data+3,3,500);
    lpData->size = 3 + bytes;
    SkyeTek_FreeData(lpData);
    return 2;
  }
  if( lpData->data[0] == 0x02 && lpData->data[1] == 0x03 && lpData->data[2] == 0x88 )
  {
    bytes = lpDI->Read(lpDevice,lpData->data+3,2,500);
    lpData->size = 3 + bytes;
    SkyeTek_FreeData(lpData);
    return 2;
  }

  /* Sanity check */
  if( lpData->data[0] != 0x02 && lpData->data[1] != 0x00 )
  {
    SkyeTek_FreeData(lpData);
    return 0;
  }

  /* Get length */
  len = lpData->data[2];
  if( len > 60 || len == 0 )
  {
    /* Garbage */
    SkyeTek_FreeData(lpData);
    return 0;
  }

  /* Assume Version 3 and read remaining bytes */
  bytes = lpDI->Read(lpDevice,lpData->data+3,len,500);
  lpData->size = 3+ bytes;

  msg = SkyeTek_GetStringFromData(lpData);
  if( msg != NULL )
  {
    SkyeTek_Debug(_T("Read: %s\r\n"), msg);
    SkyeTek_FreeString(msg);
    msg = NULL;
  }

  if( bytes != len )
  {
    SkyeTek_FreeData(lpData);
    return 0;
  }

  /* Version 3 */
  if( lpData->data[3] == 0x80 || lpData->data[3] == 0x90 )
  {
    SkyeTek_FreeData(lpData);
    return 3;
  }
  SkyeTek_FreeData(lpData);
  return 0;
}
LPSKYETEK_READER 
GetReader(
  LPSKYETEK_DEVICE    lpDevice, 
  LPPROTOCOLIMPL      lpPI,
  unsigned int        ver
  )
{
  LPSKYETEK_READER lpReader = NULL;
  SKYETEK_READER tmpReader;
  SKYETEK_STATUS status;
  SKYETEK_ADDRESS addr;
  LPSKYETEK_DATA lpData = NULL;
  TCHAR *str = NULL;
  int i = 0;
  unsigned int ix = 0;

  if( lpDevice == NULL )
    return NULL;

  /* Fill temp reader for system calls */
  if( ver == 2 )
  {
    tmpReader.id = SkyeTek_AllocateID(1);
    if( tmpReader.id != NULL )
      tmpReader.id->id[0] = 0xFF;
  }
  else
  {
    tmpReader.id = SkyeTek_AllocateID(4);
    if( tmpReader.id != NULL )
      for( i = 0; i < 4; i++ )
        tmpReader.id->id[i] = 0xFF;
  }
  tmpReader.sendRID = 1;
  tmpReader.lpDevice = lpDevice;
  tmpReader.internal = &SkyetekReaderImpl;

  status = STR_GetSystemAddrForParm(SYS_FIRMWARE,&addr,ver);
  if( status != SKYETEK_SUCCESS )
    goto failure;
  status = lpPI->GetSystemParameter(&tmpReader, &addr, &lpData,100);
  if( status != SKYETEK_SUCCESS || lpData == NULL )
    goto failure;

  lpReader = (LPSKYETEK_READER)malloc(sizeof(SKYETEK_READER));
  if( lpReader == NULL )
  {
    SkyeTek_FreeData(lpData);
    goto failure;
  }
  memset(lpReader, 0, sizeof(SKYETEK_READER));

  str = SkyeTek_GetStringFromData(lpData);
  if( str != NULL )
  {
    _tcscpy(lpReader->firmware,str);
    SkyeTek_FreeString(str);
  }

  if( ver == 2 && lpData != NULL && lpData->data != NULL && lpData->size > 0 )
  {
    switch(lpData->data[0])
    {
      case 0x00:
      case 0x01:
      case 0x50:
      case 0x60:
      case 0xA0:
      case 0xC0:
      case 0xD0:
        _tcscpy(lpReader->model, _T("M1")); 
        break;
      case 0xE0:
        _tcscpy(lpReader->model, _T("M8")); 
        break;
		  case 0x20:
        _tcscpy(lpReader->model, _T("M2")); 
        break;
		  default:
        _tcscpy(lpReader->model, _T("??")); 
    }
  }
  SkyeTek_FreeData(lpData);

  if( ver == 3 )
  {
    SKYETEK_Sleep(100);
    status = STR_GetSystemAddrForParm(SYS_PRODUCT,&addr,ver);
    if( status != SKYETEK_SUCCESS )
      goto failure;
    status = lpPI->GetSystemParameter(&tmpReader, &addr, &lpData,100);
    if( status == SKYETEK_SUCCESS && lpData->data != NULL && lpData->size >= 2 )
    {
      switch(lpData->data[1])
      {
      case 0x02:
        _tcscpy(lpReader->model, _T("M2"));
        break;
      case 0x04:
        _tcscpy(lpReader->model, _T("M4"));
        break;
      case 0x2A:
        _tcscpy(lpReader->model, _T("M2A"));
        break;
      case 0x07:
        _tcscpy(lpReader->model, _T("M7"));
        break;
      case 0x09:
        _tcscpy(lpReader->model, _T("M9"));
        break;
	  case 0x0A:
		_tcscpy(lpReader->model, _T("M10"));
		break;
      default:
        _tcscpy(lpReader->model, _T("??"));
        break;
      }
      SkyeTek_FreeData(lpData);
    }
    else
      _tcscpy(lpReader->model, _T("??"));
  }

  SKYETEK_Sleep(100);
  status = STR_GetSystemAddrForParm(SYS_READER_NAME,&addr,ver);
  if( status != SKYETEK_SUCCESS )
    goto failure;
  status = lpPI->GetSystemParameter(&tmpReader, &addr, &lpData,100);
  if( status != SKYETEK_SUCCESS )
  {
    free(lpReader);
    goto failure;
  }
  if( lpData != NULL && lpData->size > 0 && lpData->data != NULL )
  {
    /* make sure we have a null pointer */
    i = 0;
    for( ix = 0; ix < lpData->size; ix++ )
    {
      if( lpData->data[ix] == '\0' )
      {
        i = 1;
        break;
      }
    }
    if( i == 1 )
	{
		//Reader name is stored in standard ascii
		for(ix = 0; ix < lpData->size; ix++)
			lpReader->readerName[ix] = (TCHAR)lpData->data[ix];
	}
  }
  SkyeTek_FreeData(lpData);

  SKYETEK_Sleep(100);
  status = STR_GetSystemAddrForParm(SYS_SERIALNUMBER,&addr,ver);
  if( status != SKYETEK_SUCCESS )
    goto failure;
  status = lpPI->GetSystemParameter(&tmpReader, &addr, &lpData,100);
  if( status != SKYETEK_SUCCESS )
  {
    free(lpReader);
    goto failure;
  }
  str = SkyeTek_GetStringFromData(lpData);
  if( str != NULL )
  {
    _tcscpy(lpReader->serialNumber, str);
    SkyeTek_FreeString(str);
  }
  SkyeTek_FreeData(lpData);

  SKYETEK_Sleep(100);
  status = STR_GetSystemAddrForParm(SYS_RID,&addr,ver);
  if( status != SKYETEK_SUCCESS )
    goto failure;
  status = lpPI->GetSystemParameter(&tmpReader, &addr, &lpData,100);
  if( status != SKYETEK_SUCCESS )
	{
	  free(lpReader);
	  goto failure;
	}
	lpReader->id = (LPSKYETEK_ID)lpData;
  lpReader->internal = &SkyetekReaderImpl;
  lpReader->lpDevice = lpDevice;
  lpReader->lpProtocol = (LPSKYETEK_PROTOCOL)malloc(sizeof(SKYETEK_PROTOCOL));
  if( lpReader->lpProtocol == NULL )
  {
    free(lpReader);
    goto failure;
  }
  lpReader->lpProtocol->version = ver;
  lpReader->lpProtocol->internal = lpPI;
  _tcscpy(lpReader->manufacturer, _T("SkyeTek"));
  str = SkyeTek_GetStringFromID(lpReader->id);
  if( str == NULL )
  {
    free(lpReader);
    goto failure;
  }
  _tcscpy(lpReader->rid,str);

  /* TODO: See if Reader Name can be used */
  _stprintf(lpReader->friendly, _T("%s-%s-%s"), lpReader->manufacturer, lpReader->model, str);
  SkyeTek_FreeString(str);

  return lpReader;
failure:
  SkyeTek_FreeID(tmpReader.id);
  return NULL;
}