如何在 Linux 中向 scsi 设备发送特定命令?
How send a specific command to a scsi device in Linux?
我实际上正在处理插入 USB 的指纹 reader (FP reader)。这个 FP reader 也插在一块 stm32f4 板上。如果我理解正确的话,FP reader 包含一个非常小的数据库 FP 模板。要修改这些模板,我们将 FP reader 插入 USB 并使用 Windows 上的程序对其进行修改。由于我正在研究 Linux(出于好奇),我正在尝试制作一个程序,允许我们修改 Linux.
上的模板
此 FP reader 被视为 CD-ROM reader。我正在尝试在 sg 包的帮助下与其通信(我正在关注此文档 http://www.tldp.org/HOWTO/SCSI-Generic-HOWTO/). According to the documentation of the FP reader (you can find it here http://www.adh-tech.com.tw/files/GT-511C3_datasheet_V1%201_20131127.pdf)我应该发送一个缓冲区(12 个字节),如 [55 aa 0001 00000000 0001 0101] 来制作"open"命令。
这是我执行此命令的代码(我试图制作一个可读的最小示例):
#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, ¶meter, 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). I also tried to use the same scheme as scsi_inquiry.c
as said in that forum http://www.linuxquestions.org/questions/programming-9/linux-scsi-passthrough-porting-windows-routine-4175528749/ 和我得到相同的 sense_data
。我想我真的不明白 sg 驱动程序是如何工作的。是提供 sense_data
的驱动程序还是设备?我也尝试制作一些read()
和 write()
on /dev/sr1
但它没有用(好像我只能阅读一些关于 FP 内存格式的信息 reader)
终端中 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
如果您需要更多信息,请告诉我,我会添加到 post。
问题复述:如何在 Linux (sg) 或任何其他程序中使用 scsi 驱动程序向指纹 reader 发送特定命令(缓冲区)?
感谢您(也许)对未来的帮助。
编辑1:
这是发送到设备的 snd 缓冲区的确切值(由 gdb 给出)
gdb> x /3xw snd
0x0001aa55 0x00000000 0x01010001
Resume of the question: How can I send a particular command (buffer)
to a fingerprint reader by using scsi driver in Linux (sg) or any
other program ?
不要。
不幸的是 "SCSI" 通常是 "vaguely like SCSI, but not strictly compliant with SCSI" 的同义词;和 USB 设备通常提供多个接口(例如,当 OS 没有有用的驱动程序时,具有残缺功能的 "vaguely like SCSI but not SCSI" 接口,以及当设备有驱动程序时使用的本机接口) .
这意味着您极有可能需要专门为该设备编写 USB 设备驱动程序。
请注意,如果您查看此设备的数据表,您会发现 none 命令与 SCSI 有任何关系,唯一看起来像 CD 的是 "Upgrade ISO CD Image()" 函数记录为 "not supported".
我实际上正在处理插入 USB 的指纹 reader (FP reader)。这个 FP reader 也插在一块 stm32f4 板上。如果我理解正确的话,FP reader 包含一个非常小的数据库 FP 模板。要修改这些模板,我们将 FP reader 插入 USB 并使用 Windows 上的程序对其进行修改。由于我正在研究 Linux(出于好奇),我正在尝试制作一个程序,允许我们修改 Linux.
上的模板此 FP reader 被视为 CD-ROM reader。我正在尝试在 sg 包的帮助下与其通信(我正在关注此文档 http://www.tldp.org/HOWTO/SCSI-Generic-HOWTO/). According to the documentation of the FP reader (you can find it here http://www.adh-tech.com.tw/files/GT-511C3_datasheet_V1%201_20131127.pdf)我应该发送一个缓冲区(12 个字节),如 [55 aa 0001 00000000 0001 0101] 来制作"open"命令。
这是我执行此命令的代码(我试图制作一个可读的最小示例):
#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, ¶meter, 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). I also tried to use the same scheme as scsi_inquiry.c
as said in that forum http://www.linuxquestions.org/questions/programming-9/linux-scsi-passthrough-porting-windows-routine-4175528749/ 和我得到相同的 sense_data
。我想我真的不明白 sg 驱动程序是如何工作的。是提供 sense_data
的驱动程序还是设备?我也尝试制作一些read()
和 write()
on /dev/sr1
但它没有用(好像我只能阅读一些关于 FP 内存格式的信息 reader)
终端中 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
如果您需要更多信息,请告诉我,我会添加到 post。
问题复述:如何在 Linux (sg) 或任何其他程序中使用 scsi 驱动程序向指纹 reader 发送特定命令(缓冲区)?
感谢您(也许)对未来的帮助。
编辑1: 这是发送到设备的 snd 缓冲区的确切值(由 gdb 给出)
gdb> x /3xw snd
0x0001aa55 0x00000000 0x01010001
Resume of the question: How can I send a particular command (buffer) to a fingerprint reader by using scsi driver in Linux (sg) or any other program ?
不要。
不幸的是 "SCSI" 通常是 "vaguely like SCSI, but not strictly compliant with SCSI" 的同义词;和 USB 设备通常提供多个接口(例如,当 OS 没有有用的驱动程序时,具有残缺功能的 "vaguely like SCSI but not SCSI" 接口,以及当设备有驱动程序时使用的本机接口) .
这意味着您极有可能需要专门为该设备编写 USB 设备驱动程序。
请注意,如果您查看此设备的数据表,您会发现 none 命令与 SCSI 有任何关系,唯一看起来像 CD 的是 "Upgrade ISO CD Image()" 函数记录为 "not supported".