I am trying to interface a contact-less smart card reader over UART (usbserial) using termios framework under Linux. The code works fine on the PC, but when I cross-compile and try it out on an ARM9 target, it is able to open the device and even writing the command to the device, but the read command blocks indefinitely. Here is the code snippet :
我正在尝试使用Linux下的termios框架在UART(usbserial)上连接非接触式智能卡读卡器。代码在PC上运行良好,但是当我在ARM9目标上交叉编译并尝试它时,它能够打开设备甚至将命令写入设备,但是read命令无限期地阻塞。这是代码片段:
int mifare_rdr_init(struct mifare_1K * ptr, char *rdr_devnode)
{
bzero(ptr, sizeof(struct mifare_1K)); // zero the entire structure
// open serial device
int fd = open(rdr_devnode, O_RDWR|O_NOCTTY );
if (fd == -1) {
perror("Failed to open serial device ");
return 1;
}
ptr->serialfd = fd; // save file descriptor
ptr->serialdev.c_iflag = 0; // no i/p flags
ptr->serialdev.c_oflag = 0; // o/p flags
ptr->serialdev.c_cflag = ( CS8 | CREAD | B38400 ); // 8 bits, receive enable, baud for rdr
ptr->serialdev.c_lflag = ( ICANON ); // CANONICAL mode, means read till newline char '\n'.
// control chars
// commented below line as suggested by A.H below, since it's not needed in CANONICAL mode
// ptr->serialdev.c_cc[VMIN] = 1; // read unblocks only after at least one received char.
// flush all i/o garbage data if present
tcflush(ptr->serialfd,TCIOFLUSH);
int ret = 0;
// apply settings
ret = tcsetattr(ptr->serialfd,TCSANOW,&ptr->serialdev);
if (ret == -1) {
perror("tcsetattr() failed ");
return 2;
}
return 0;
}
int get_mifare_rdr_version(struct mifare_1K *ptr, char *data)
{
// flush all i/o garbage data if present
tcflush(ptr->serialfd,TCIOFLUSH);
int chars_written = write(ptr->serialfd,"$1V\n",4);
if( chars_written < 0 ) {
perror("Failed to write serial device ");
return 1;
}
printf("cmd sent, read version...\n"); // this prints, so I know cmd sent...
int chars_read = read(ptr->serialfd,ptr->data_buf,14);
if( chars_read < 0 ) {
perror("Failed to read serial device ");
return 2;
}
// copy data to user buffer
printf("reading done.\n"); // this doesn't print...
return 0;
}
The mifare_1K structure contains the file descriptor for serial device, termios structure, and various buffers I am using. The device as I mentioned is usb-to-serial ( module: ftdi_sio ) device. It's configured at 38400@8-N-1 in Canonical mode of termios.
mifare_1K结构包含串行设备,termios结构和我正在使用的各种缓冲区的文件描述符。我提到的设备是usb-to-serial(模块:ftdi_sio)设备。在termios的Canonical模式下,它配置为38400 @ 8-N-1。
Canonical mode because the responses from the reader end in '\n', so its better handled in Canonical mode since it reads the device till '\n' is received (pls correct me if I'm wrong).
Canonical模式,因为来自阅读器的响应以'\ n'结尾,所以它在Canonical模式下处理得更好,因为它读取设备直到收到'\ n'(如果我错了,请纠正我)。
First I call init() fn and then get_rdr_version(). The string "cmd sent, read version..." is printed so I know its able to write, but does not print string "reading done." after that.
首先,我调用init()fn然后调用get_rdr_version()。打印字符串“cmd sent,read version ...”,因此我知道它能够写入,但不会打印字符串“读取完成”。之后。
Another thing is that if I remove the card reader and connect that port to gtkterm (serial port terminal program) on another PC, I do not receive the "$1V\n" on that gtkterm ??!! . Then after a little RnD I found out that if I reboot the system on which the reader is connected reader then only I get that cmd "$1V\n" on the other Gtkterm. If I try again without a reboot, that cmd is not seen on that Gkterm...a clue, but havnt figured it out yet.
另一件事是,如果我删除读卡器并将该端口连接到另一台PC上的gtkterm(串口终端程序),我就不会收到gtkterm上的“$ 1V \ n”了!! 。然后在一点RnD之后我发现如果我重新启动阅读器连接读取器的系统,那么只有我在另一个Gtkterm上得到那个cmd“$ 1V \ n”。如果我在没有重启的情况下再次尝试,那个gkterm上没有看到那个cmd ...一个线索,但是havnt还没想到它。
Is it something like that the cmd is written to the device file, but not drained to the actual device ? Is there any way to chk this ?
是否类似于将cmd写入设备文件,但没有将其耗尽到实际设备?有没有办法解决这个问题?
Any help is deeply appreciated since I've been stuck on this fr some time now....thnks.
任何帮助都非常受欢迎,因为我现在已经被困在这一段时间了.... thnks。
UPDATE :
更新:
Ok, I have got it working by modifying the code a little as shown below.
好的,我通过稍微修改代码来实现它,如下所示。
// open serial device
int fd = open(rdr_devnode, O_RDWR|O_NOCTTY|O_NDELAY ); // O_NDELAY ignores the status of DCD line, all read/write calls after this will be non-blocking
fcntl(fd,F_SETFL,0); // restore read/write blocking behavior
if (fd == -1) {
perror("Failed to open serial device ");
return 1;
}
This is the modified section of the code which open the port in my init() function. Two changes :
这是代码的修改部分,它在我的init()函数中打开端口。两个变化:
1) O_NDELAY is added to the flags in open() call, which ignores Data Carrier Detect (DCD) line to see if the other end is connected and ready for communication or not. This was used originally for MODEMs, which I did not need, in fact, I do not have it at all since I am using usbserial. But this flag also makes further calls to read() and write() as non-blocking. Point to note, I had thought this would be taken care of by adding CLOCAL to cflag of termios struct, which I had tried but did not work.
1)O_NDELAY被添加到open()调用中的标志,忽略数据载波检测(DCD)线以查看另一端是否已连接并准备好进行通信。这最初用于MODEM,我不需要,事实上,因为我使用usbserial,所以根本没有它。但是这个标志还进一步调用read()和write()作为非阻塞。需要注意的是,我原本以为这会通过将clOCAL添加到termios结构的cflag中来解决,我曾尝试过但没有用。
2) fcntl(fd,F_SETFL,0) restores the blocking behaviour of further read() and write() calls.
2)fcntl(fd,F_SETFL,0)恢复进一步read()和write()调用的阻塞行为。
This combo is working perfectly for me. The only reason I am not posting this as an answer is that I do not yet understand why it worked on PC without this modification, since it was same hardware. In fact, I was able to read data from the smart card reader ON ARM9 TARGET using minicom , but not my program. I am going to check FT232BL docs to see if what is the status of DCD by default.
这个组合对我来说非常合适。我没有将此作为答案发布的唯一原因是我还不知道为什么它在没有这种修改的情况下在PC上工作,因为它是相同的硬件。实际上,我能够使用minicom从ARM9 TARGET上的智能卡读卡器读取数据,但不是我的程序。我将检查FT232BL文档以查看默认情况下DCD的状态是什么。
Anyway, I found this bit of info on Serial programming Guide for POSIX operating systems . Explanation anyone ??? Of course, I will post the answer when I find it out..
无论如何,我在POSIX操作系统的串行编程指南中找到了这方面的信息。解释任何人???当然,我会在找到答案后发布答案。
Cheers :)
干杯:)
2 个解决方案
#1
3
Having just encountered the same symptoms on a Raspberry Pi with a Telegesis USB module, I'm adding this as another data point.
刚刚在带有Telegesis USB模块的Raspberry Pi上遇到相同的症状,我将其添加为另一个数据点。
In my case the cause turned out to be a missing RTS flag. The Telegesis expects CRTSCTS flow control, and would not send any data to the Raspberry without seeing the RTS. The perplexing aspects here were that a) the same code works just fine on a PC, and b) it worked fine on the Raspberry the first time the Telegesis was plugged in, but on subsequent openings of /dev/ttyUSB0 no data would be seen by the Raspberry.
在我的情况下,原因结果是缺少RTS标志。 Telegesis期望CRTSCTS流控制,并且在没有看到RTS的情况下不会向Raspberry发送任何数据。这里令人困惑的方面是:a)相同的代码在PC上工作得很好,并且b)在第一次插入Telegesis时它在Raspberry上运行良好,但是在/ dev / ttyUSB0的后续开放中没有数据可见由覆盆子。
For some reason, it seems that on the ARM the RTS flag gets cleared on device close, but not set again, whereas on x86/x64 the RTS flag stays set. The fix here is simply to set the RTS flag if it isn't already - e.g.
出于某种原因,似乎在ARM上RTS标志在设备关闭时被清除,但是没有再次设置,而在x86 / x64上,RTS标志保持设置。这里的修复只是设置RTS标志,如果它还没有 - 例如
#include <sys/ioctl.h>
//...
int rtscts = 0;
if (ioctl (fd, TIOCMGET, &rtscts) != 0)
{
// handle error
}
else if (!(rtscts & TIOCM_RTS))
{
rtscts |= TIOCM_RTS;
if (ioctl (fd, TIOCMSET, &rtscts) != 0)
{
// handle error
}
}
I do note that in your case you don't use flow control, so the above may very well not be applicable. It was your question and the mentioning that minicom worked that led us to discover the solution to our problem however - so thank you for that!
我注意到在你的情况下,你不使用流量控制,所以上述可能不适用。这是你的问题,并提到minicom工作,这导致我们发现我们的问题的解决方案 - 所以谢谢你!
#2
1
Three things you might check:
你可以检查三件事:
Canonical mode / non canonical mode mixup 1
You are mixing the canonical mode and the the non-canonical mode stuff:
您正在混合规范模式和非规范模式的东西:
ptr->serialdev.c_lflag = ( ICANON );
// ...
ptr->serialdev.c_cc[VMIN] = 1;
The termios(3)
manpage states about VMIN:
termios(3)联机帮助页说明了VMIN:
VMIN Minimum number of characters for non-canonical read.
VMIN非规范读取的最小字符数。
So clearly your timeout will not work the way you think.
很明显,你的超时不会像你想象的那样有效。
Canonical mode / non canonical mode mixup 2
Additionally the manpage says further below:
另外,联机帮助页面在下面进一步说
These symbolic subscript values are all different, except that VTIME, VMIN may have the same value as VEOL, VEOF, respectively. In non- canonical mode the special character meaning is replaced by the timeout meaning. For an explanation of VMIN and VTIME, see the description of non-canonical mode below.
这些符号下标值都是不同的,除了VTIME,VMIN可以分别具有与VEOL,VEOF相同的值。在非规范模式中,特殊字符含义由超时含义替换。有关VMIN和VTIME的说明,请参阅下面的非规范模式的说明。
So please check if the definitions of these constants are different for your two platforms. It might be, that the EOL/EOF logic might be spoiled by the wrong setting. Both EOL and EOF migh cause a return from read
.
因此,请检查这两个平台的这些常量的定义是否不同。可能是,EOL / EOF逻辑可能会被错误的设置破坏。 EOL和EOF都会导致读取返回。
Non-initialized c_cc
Your code does not show a proper initialization of the c_cc
array. You neither read the existing settings not provide suitable defaults for the values required for canonical modes. The code shown so far does not even clear the values. Therefore unpredictable values might be used.
您的代码未显示c_cc数组的正确初始化。您既不会读取现有设置,也不会为规范模式所需的值提供合适的默认值。到目前为止显示的代码甚至没有清除值。因此可能会使用不可预测的值。
#1
3
Having just encountered the same symptoms on a Raspberry Pi with a Telegesis USB module, I'm adding this as another data point.
刚刚在带有Telegesis USB模块的Raspberry Pi上遇到相同的症状,我将其添加为另一个数据点。
In my case the cause turned out to be a missing RTS flag. The Telegesis expects CRTSCTS flow control, and would not send any data to the Raspberry without seeing the RTS. The perplexing aspects here were that a) the same code works just fine on a PC, and b) it worked fine on the Raspberry the first time the Telegesis was plugged in, but on subsequent openings of /dev/ttyUSB0 no data would be seen by the Raspberry.
在我的情况下,原因结果是缺少RTS标志。 Telegesis期望CRTSCTS流控制,并且在没有看到RTS的情况下不会向Raspberry发送任何数据。这里令人困惑的方面是:a)相同的代码在PC上工作得很好,并且b)在第一次插入Telegesis时它在Raspberry上运行良好,但是在/ dev / ttyUSB0的后续开放中没有数据可见由覆盆子。
For some reason, it seems that on the ARM the RTS flag gets cleared on device close, but not set again, whereas on x86/x64 the RTS flag stays set. The fix here is simply to set the RTS flag if it isn't already - e.g.
出于某种原因,似乎在ARM上RTS标志在设备关闭时被清除,但是没有再次设置,而在x86 / x64上,RTS标志保持设置。这里的修复只是设置RTS标志,如果它还没有 - 例如
#include <sys/ioctl.h>
//...
int rtscts = 0;
if (ioctl (fd, TIOCMGET, &rtscts) != 0)
{
// handle error
}
else if (!(rtscts & TIOCM_RTS))
{
rtscts |= TIOCM_RTS;
if (ioctl (fd, TIOCMSET, &rtscts) != 0)
{
// handle error
}
}
I do note that in your case you don't use flow control, so the above may very well not be applicable. It was your question and the mentioning that minicom worked that led us to discover the solution to our problem however - so thank you for that!
我注意到在你的情况下,你不使用流量控制,所以上述可能不适用。这是你的问题,并提到minicom工作,这导致我们发现我们的问题的解决方案 - 所以谢谢你!
#2
1
Three things you might check:
你可以检查三件事:
Canonical mode / non canonical mode mixup 1
You are mixing the canonical mode and the the non-canonical mode stuff:
您正在混合规范模式和非规范模式的东西:
ptr->serialdev.c_lflag = ( ICANON );
// ...
ptr->serialdev.c_cc[VMIN] = 1;
The termios(3)
manpage states about VMIN:
termios(3)联机帮助页说明了VMIN:
VMIN Minimum number of characters for non-canonical read.
VMIN非规范读取的最小字符数。
So clearly your timeout will not work the way you think.
很明显,你的超时不会像你想象的那样有效。
Canonical mode / non canonical mode mixup 2
Additionally the manpage says further below:
另外,联机帮助页面在下面进一步说
These symbolic subscript values are all different, except that VTIME, VMIN may have the same value as VEOL, VEOF, respectively. In non- canonical mode the special character meaning is replaced by the timeout meaning. For an explanation of VMIN and VTIME, see the description of non-canonical mode below.
这些符号下标值都是不同的,除了VTIME,VMIN可以分别具有与VEOL,VEOF相同的值。在非规范模式中,特殊字符含义由超时含义替换。有关VMIN和VTIME的说明,请参阅下面的非规范模式的说明。
So please check if the definitions of these constants are different for your two platforms. It might be, that the EOL/EOF logic might be spoiled by the wrong setting. Both EOL and EOF migh cause a return from read
.
因此,请检查这两个平台的这些常量的定义是否不同。可能是,EOL / EOF逻辑可能会被错误的设置破坏。 EOL和EOF都会导致读取返回。
Non-initialized c_cc
Your code does not show a proper initialization of the c_cc
array. You neither read the existing settings not provide suitable defaults for the values required for canonical modes. The code shown so far does not even clear the values. Therefore unpredictable values might be used.
您的代码未显示c_cc数组的正确初始化。您既不会读取现有设置,也不会为规范模式所需的值提供合适的默认值。到目前为止显示的代码甚至没有清除值。因此可能会使用不可预测的值。