I am trying to connect a contactless smart card reader via UART (usbserial) using the termios framework under Linux. The code works fine on a PC, but when I cross-compile and test it on an ARM9 target, it can open the device and even write a command to the device, but the read command is blocked indefinitely. Here is the code snippet:
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 AH below, since it 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 a file descriptor for the serial device, the termios structure, and the various buffers that I use. The device I mentioned is a usb-to-serial device (module: ftdi_sio). It is configured on 38400 @ 8-N-1 in canonical termios mode.
Canonical mode, because responses from the reader end in "\ n", so it’s better to process it in canonical mode, since it reads the device before receiving "\ n" (if you are mistaken, correct me).
First I call init () fn and then get_rdr_version (). The line "cmd sent, read version ..." is printed, so I know that she can write, but does not print the line "read done." after that.
Another thing is that if I remove the card reader and connect this port to gtkterm (serial port terminal program) on another PC, I don’t get "$ 1V \ n" on that gtkterm ?? !!, Then, after a little RnD, I found out that if I reboot the system the reader is connected to, then I only get this cmd "$ 1V \ n" on another Gtkterm. If I try again without a reboot, this cmd is not displayed on this Gkterm ... key, but havnt still not understood.
Is this something like cmd being written to a device file but not merging with the actual device? Is there a way to chk this?
Any help is deeply appreciated since I am stuck on this fr for a while ... thnks.
UPDATE:
Well, it works for me, changing the code a bit, 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 a modified section of code that opens a port in my init () function. Two changes:
1) O_NDELAY is added to the flags in the open () call, which ignores the data carrier detection line (DCD) to find out if the other end is connected and ready to communicate or not. This was originally used for modems that I don’t need, in fact, I don’t have it at all, since I use usbserial. But this flag also calls further read () and write () calls as non-blocking. Let me remind you, I thought that they would take care of this by adding CLOCAL to the cflag termios struct, which I tried but did not work.
2) fcntl (fd, F_SETFL, 0) restores the behavior of blocking subsequent calls to read () and write ().
This combo works great for me. The only reason I am not sending this as an answer is because I still do not understand why it worked on a PC without this modification, as it was the same. In fact, I was able to read data from a smart card reader on an ARM9 TARGET using minicom , but not my program. I am going to check the FT232BL docs to see if DCD status is the default.
Anyway, I found this information about the Serial Programming Guide for POSIX operating systems . Explanation to anyone ??? Of course, I will send an answer when I find out.
Greetings :)