Превращаем Android в smart card reader

Сегодня будем создавать драйвер для pcsc под линукс, который будет соединятся с устройством на базе android.

Для этого понадобится:

Драйвер будет работать по такому алгоритму,

при подключении устройства с определенному vid и pid, который мы укажем, будет вызываться функция, IFDHCreateChannel(DWORD Lun, DWORD Channel),

в функции переводиться устройство в режим accessory и выдает ошибку,

устройство меняет vid и pid и пытаться соединится снова.

Происходит подключение и инициализация устройства.

RESPONSECODE IFDHCreateChannel(DWORD Lun, DWORD Channel)

{

int ret = 0;

int isInit = 0;

int i = 0;

for (i = 0; i < sizeof (UsbIDs) / sizeof (UsbIDs[0]); i++)

{

ret = usb_InitAll(UsbIDs[i].vendorID, UsbIDs[i].productID);

if (ret == 255)

isInit = 1;

}

if (isInit)

{

Log("Initialization devices");

return IFD_COMMUNICATION_ERROR;

}

if (readers[ReaderNum].init != 0)

{

Log("Device is opened");

return IFD_COMMUNICATION_ERROR;

}

ret = usb_OpenReader(&readers[ReaderNum], readers);

if (ret == 0)

{

ret = InitReader(&readers[ReaderNum]);

if (ret != 0)

{

usb_CloseReader(&readers[ReaderNum]);

Logd("Init fail not response to android", ret);

return IFD_COMMUNICATION_ERROR;

}

ogd("Init OK %ld", ReaderNum);

return IFD_SUCCESS;

}

usb_CloseReader(&readers[ReaderNum]);

return IFD_COMMUNICATION_ERROR;

}

Далее pcsc посылает запрос сколько в ридере слотов, у нас он один, а так бывает контактная карта, без контактная и магнитная карта.

RESPONSECODE IFDHGetCapabilities(DWORD Lun, DWORD Tag,

PDWORD Length, PUCHAR Value)

{

Log("");

switch (Tag)

{

case 0x090303://для старых версий pcsc

case TAG_IFD_ATR:

{

syslog(LOG_INFO, "TAG_IFD_ATR\n");

memcpy(Value, mAtr, MAX_ATR_SIZE);

*Length = 18;

break;

}

case TAG_IFD_SIMULTANEOUS_ACCESS:

{

syslog(LOG_INFO, "TAG_IFD_SIMULTANEOUS_ACCESS\n");

*Length = 1;

*Value = 2;

break;

}

case TAG_IFD_SLOTS_NUMBER:

{

syslog(LOG_INFO, "TAG_IFD_SLOTS_NUMBER\n");

*Length = 1;

*Value = 1;

break;

}

default:

*Length = 0;

return IFD_ERROR_TAG;

}

return IFD_SUCCESS;

}

еще в нашей функции pcsc определяет сколько ридеров поддерживает драйвер и запрашивает атр.

После pcsc пытается подать напряжение на карточку и получить atr карточки,

в ней просто возвращаем атр от карточки, которую я скопировал с тестовой карточки у себя.

RESPONSECODE IFDHPowerICC(DWORD Lun, DWORD Action,

PUCHAR Atr, PDWORD AtrLength)

{

Log("");

if (Action == IFD_RESET || Action == IFD_POWER_UP)

{

memcpy(Atr, mAtr, MAX_ATR_SIZE);

*AtrLength = 18;

}

return IFD_SUCCESS;

}

По атр pcsc определят по какому протоколу работает ридер с картой, мы будем работать по T=0.

Теперь pcsc приняла все настройки и работает в штатном режиме, то есть каждые пол секунды считывает вставлена ли карта, вызывая функцию IFDHICCPresence(DWORD Lun), здесь мы будем получать от программы отклик, и тем самым определять работает ли она.

RESPONSECODE IFDHICCPresence(DWORD Lun)

{

int ret = 0;

unsigned char tmpbuf[MAX_BUFFER_SIZE];

int len = 1;

tmpbuf[0] = 0x85;

ret = Send(&readers[ReaderNum], tmpbuf, len);

if (ret != 0)

return IFD_ICC_NOT_PRESENT;

len = MAX_BUFFER_SIZE;

ret = Recv(&readers[ReaderNum], tmpbuf, &len);

return ret == 0 ? IFD_ICC_PRESENT : IFD_ICC_NOT_PRESENT;

}

Еще одна важная функция, это общение с самим «ридром», отсылать и принимать данные.

RESPONSECODE IFDHTransmitToICC(DWORD Lun, SCARD_IO_HEADER SendPci,

PUCHAR TxBuffer, DWORD TxLength,

PUCHAR RxBuffer, PDWORD RxLength,

PSCARD_IO_HEADER RecvPci)

{

Log("");

int ret = 0;

ret = SendTask(&readers[ReaderNum], TxBuffer, (int) TxLength);

if (ret == LIBUSB_ERROR_TIMEOUT)

return IFD_RESPONSE_TIMEOUT;

else

if (ret != 0)

return IFD_COMMUNICATION_ERROR;

unsigned char tmpbuf[MAX_BUFFER_SIZE];

int len = MAX_BUFFER_SIZE;

ret = Recv(&readers[ReaderNum], tmpbuf, &len);

*RxLength = 0;

if (ret == LIBUSB_ERROR_TIMEOUT)

return IFD_RESPONSE_TIMEOUT;

else

if (ret != 0)

return IFD_COMMUNICATION_ERROR;

memcpy(RxBuffer, tmpbuf, len);

*RxLength = len;

return IFD_SUCCESS;

}

функция отсылает данные и сразу принимает. В ответе мы получаем правильно ли распарсило и отработало устройство, так как мне не нужно получать данные от устройства.

Работа с usb

Инициализация и перевод устройства в режим accessory происходит в функции,

int usb_InitAll(int vid, int pid) эта функци посылает управляющие коды устройству, которые честно взяты из примера adk.

Сначала посылается код для получение версии протокола

ret = libusb_control_transfer(handle,

LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR |

LIBUSB_RECIPIENT_DEVICE, ADK_COMMAND_GETPROTOCOL, 0, 0,

(unsigned char *) &version, sizeof (version), 0);

далее отсылаются строки которые характеризую что подключено к андроиду, и следом код активирующий режим адк

ret = libusb_control_transfer(handle,

LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR |

LIBUSB_RECIPIENT_DEVICE, ADK_COMMAND_START, 0, 0, NULL, 0, 0);

usb_OpenReader(usbDevice* reader, usbDevice* allreaders);

usb_CloseReader(usbDevice* reader);

usb_Read(usbDevice* reader, unsigned char *buf, int *len, unsigned int TimeOut);

usb_Write(usbDevice* reader, unsigned char *buf, int len, unsigned int TimeOut);

эти функции выполняют открытие, закрытие и передачу, прием данных, в них осуществляется работа с библиотекой libusb-1.0

Данные на устройство передаются по собственному протоколу,

в начале идет байт начало сообщения, далее 2 байта размер сообщения, само сообщение и 2 байта crc сумма.

продолжение следует...