Как отправить конкретную команду на устройство scsi в Linux?

На самом деле я работаю над считывателем отпечатков пальцев (FP reader), к которому подключаю USB. Этот считыватель FP также подключен к плате stm32f4. Если я правильно понимаю, читатель FP содержит действительно небольшую базу шаблонов FP. Чтобы изменить эти шаблоны, мы подключаем читатель FP к USB и используем программу для Windows, чтобы изменить его. Поскольку я работаю над Linux (и для любопытства), я пытаюсь создать программу, которая позволит нам изменять шаблоны в Linux.

Этот читатель FP рассматривается как читатель CD-ROM. Я пытаюсь связаться с ним с помощью пакета sg (я слежу за этим документом http://www.tldp.org/HOWTO/SCSI-Generic-HOWTO/). Согласно документации считывателя FP (вы можете найти ее здесь http://www.adh-tech.com.tw/files/GT-511C3_datasheet_V1%201_20131127.pdf), я должен отправить буфер (12 байт) подобным образом [55 aa 0001 00000000 0001 0101], чтобы сделать команду "открыть".

Вот мой код для создания этой команды (я попытался сделать читабельный минимальный пример):

#include <errno.h>
#include <fcntl.h>
#include <scsi/sg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

#define FP_PACKET_SZ 12

const uint8_t fp_packet_sz = FP_PACKET_SZ;
static unsigned char sense_buffer[32];

#define OPEN_CMD

static void init_snd(uint8_t buf[fp_packet_sz]) {
  size_t offset = 0;
  const uint16_t deviceID = 1;
  const uint16_t cmd = 1;
  const uint32_t parameter = 0;
  buf[offset++] = 0x55;
  buf[offset++] = 0xAA;
  memcpy(buf + offset, &deviceID, sizeof(deviceID));
  offset += sizeof(deviceID);
  memcpy(buf + offset, &parameter, sizeof(parameter));
  offset += sizeof(parameter);
  memcpy(buf + offset, &cmd, sizeof(cmd));
  offset += sizeof(cmd);

  uint16_t checksum = 0;
  for (unsigned int i = 0 ; i < offset ; i++)
    checksum += buf[i];
  memcpy(buf + offset, &checksum, sizeof(checksum));

}

int main(int argc, char *argv[]) {
  int fd = 0, res = 0;
  char * filename = 0;

  sg_io_hdr_t header;
  uint8_t snd[fp_packet_sz];
  uint8_t rcv[fp_packet_sz];
  memset (snd, 0, sizeof(snd));
  memset (rcv, 0, sizeof(rcv));

  if (argc < 2) {
    fprintf(stderr, "argument missing\n");  
    return EXIT_FAILURE;
  }
  filename = argv[1];
  fd = open(filename, O_RDWR);
  if (fd < 0) {
    fprintf(stderr, "open %s failed\n", filename);
    return EXIT_FAILURE;
  }

  init_snd(snd);
  header.interface_id = 'S';
  header.dxfer_direction = SG_DXFER_TO_FROM_DEV;

  header.cmd_len = fp_packet_sz;
  header.cmdp = snd;

  header.mx_sb_len = sizeof (sense_buffer);
  header.sbp = sense_buffer;

  header.iovec_count = 0;
  header.dxfer_len = fp_packet_sz;
  header.dxferp = rcv;

  header.timeout = 60000;
  header.flags = 0;

  if ((res = ioctl(fd, SG_IO, &header)) < 0) {
    fprintf(stderr, "ioctl failed and return errno: %s \n", strerror(errno));
    exit(EXIT_FAILURE);
  }

  fprintf(stdout, "receive buffer:");
  for (int i = 0 ; i < fp_packet_sz ; i++)
    fprintf(stdout, " %02x", rcv[i]); 
  fprintf(stdout, "\n");


  fprintf(stdout, "sense data:");
  for (int i = 0 ; i < header.sb_len_wr ; i++)
    fprintf(stdout, " %02x", sense_buffer[i]); 
  fprintf(stdout, "\n");

  return EXIT_SUCCESS;
}

То, что я ожидал, было rcv иметь следующее значение [55 aa 00 01 00 00 00 00 00 30 01 30],

Но вместо этого я ничего не получил (или что-то, что я не понимаю) и sense_data получить следующее значение: 70 00 05 00 00 00 00 0A 00 00 00 00 20 00 00 00 00 00 которые соответствуют Illegal Request (по данным http://blog.disksurvey.org/knowledge-base/scsi-sense/ blog). Я также пытался использовать ту же схему, что и scsi_inquiry.c как сказано на этом форуме http://www.linuxquestions.org/questions/programming-9/linux-scsi-passthrough-porting-windows-routine-4175528749/ и я получаю то же самое sense_data, Я думаю, что я не очень понимаю, как работает драйвер SG. Это тот драйвер, который дает sense_data или устройство? Я также пытаюсь сделать некоторые read() а также write() на /dev/sr1 но это не сработало (похоже, я могу только прочитать некоторую информацию о формате памяти считывателя FP)

Некоторая дополнительная информация, предоставляемая командами sg в терминале:

>sg_map
/dev/sg3  /dev/sr1

>sg_inq /dev/sg3
invalid VPD response; probably a STANDARD INQUIRY response
standard INQUIRY:
  PQual=0  Device_type=5  RMB=1  LU_CONG=0  version=0x06  [SPC-4]
  [AERC=0]  [TrmTsk=0]  NormACA=0  HiSUP=0  Resp_data_format=2
  SCCS=0  ACC=0  TPGS=0  3PC=0  Protect=0  [BQue=0]
  EncServ=0  MultiP=0  [MChngr=0]  [ACKREQQ=0]  Addr16=0
  [RelAdr=0]  WBus16=0  Sync=0  [Linked=0]  [TranDis=0]  CmdQue=0
    length=36 (0x24)   Peripheral device type: cd/dvd
 Vendor identification:         
 Product identification: Fingerprint     
 Product revision level: 0.01

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

Возобновление вопроса: Как я могу отправить определенную команду (буфер) в сканер отпечатков пальцев с помощью драйвера scsi в Linux (sg) или любой другой программе?

Спасибо за вашу (может быть) будущую помощь.

EDIT1: Вот точное значение буфера snd, отправленного на устройство (задано gdb)

gdb> x /3xw snd
0x0001aa55  0x00000000  0x01010001

1 ответ

Решение

Возобновление вопроса: Как я могу отправить определенную команду (буфер) в сканер отпечатков пальцев с помощью драйвера scsi в Linux (sg) или любой другой программе?

Не.

К сожалению, "SCSI" часто является синонимом "смутно похожего на SCSI, но не совсем совместимого с SCSI"; и устройства USB часто предоставляют несколько интерфейсов (например, интерфейс "смутно похожий на SCSI, но не SCSI" с ограниченными функциональными возможностями, когда в ОС нет полезного драйвера, плюс собственный интерфейс, который используется, когда есть драйвер для устройства).

Это означает, что весьма вероятно, что вам потребуется написать драйвер устройства USB специально для этого устройства.

Обратите внимание, что если вы посмотрите на таблицу данных этого устройства, то увидите, что ни одна из команд не имеет никакого отношения к SCSI, и единственное, что выглядит как компакт-диск, - это функция "Обновить образ компакт-диска ISO ()", которая задокументирована как "не поддерживается".

Другие вопросы по тегам