2

I need to write a driver for a bar code reader for Linux in C. The bar code reader works through the serial bus. When I send a set of commands to the bar code reader, the bar code reader should return status messages to me. I managed to configure the port and to create a signal handler. In the signal handler, I read the data which the serial bus receives.

So the question is: Should I read the data in buffer and then work with it? And can I even write the data in the buffer with the port configured in this way? When the device replies to me, according to that reply data, I need to send another command to the device. Also, can I use write() to write the messages? If I can’t use that, what command should I use? And can you help me a little with the write command?

The command that I send to the device is always 7 bytes, but the reply data varies between 7-32 bytes. If the read function sends different number of bytes that are read, how can I be sure that I received all of the data, so I can work with it?

Here is some code that I've written. Am I going in the right direction? Once more, the idea is very simple: I am sending a command to a device, and the device interupt me and replies. I read what was send, I work with the reply data, and according to that data, I am send another command. Thanks in forward.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <sys/signal.h>
#include <errno.h>
#include <termios.h>

void signal_handler_IO (int status);   /* definition of signal handler */

int n;
int fd;
int connected;
char buffer[14];
int bytes;
struct termios termAttr;
struct sigaction saio;

int main(int argc, char *argv[])
{
     fd = open("/dev/ttyUSB1", O_RDWR | O_NOCTTY | O_NDELAY);
     if (fd == -1)
     {
        perror("open_port: Unable to open /dev/ttyO1\n");
        exit(1);
     }

     saio.sa_handler = signal_handler_IO;
     saio.sa_flags = 0;
     saio.sa_restorer = NULL; 
     sigaction(SIGIO,&saio,NULL);

     fcntl(fd, F_SETFL, FNDELAY);
     fcntl(fd, F_SETOWN, getpid());
     fcntl(fd, F_SETFL,  O_ASYNC ); 

     tcgetattr(fd,&termAttr);
     //baudRate = B115200; 
     cfsetispeed(&termAttr,B115200);
     cfsetospeed(&termAttr,B115200);
     termAttr.c_cflag &= ~PARENB;
     termAttr.c_cflag &= ~CSTOPB;
     termAttr.c_cflag &= ~CSIZE;
     termAttr.c_cflag |= CS8;
     termAttr.c_cflag |= (CLOCAL | CREAD);
     termAttr.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
     termAttr.c_iflag &= ~(IXON | IXOFF | IXANY);
     termAttr.c_oflag &= ~OPOST;
     tcsetattr(fd,TCSANOW,&termAttr);
     printf("UART1 configured....\n");

     connected = 1;
     while(connected == 1){
         //write function and read data analyze(Processing)
     }

     close(fd);
     exit(0);             
}

void signal_handler_IO (int status)
{
    bytes = read(fd, &buffer, sizeof(buffer));
    printf("%s\n", buffer);
}
Junseok Lee
  • 705
  • 5
  • 18

1 Answers1

7

Once more, the idea is very simple: I am sending a command to a device, and the device interupt me and replays. I read what was send, I work with the replay data, and according to that data, I am sending another command.

You are describing a common and simple arrangement of master & slave devices: the master device sends a command or request message, and the slave device has to reply with a response message.

You are using the proper POSIX method of modifying the flags. However the system calls fcntl(), tcgetattr() and tcsetattr() should have their return codes checked to ensure there were no errors.

You are configuring the serial port for non-canonical (aka raw) input & output. (So your receiving buffer should be unsigned char rather than signed char.) This mode offers a method of reading based on byte count and/or silence on the serial link. There is no reason to use async read involving a signal handler. (Your signal handler has the same bug as this question.) The master/slave device relationship dictates a request/response message exchange.

To properly read the complete response message, you need to define the VMIN and VTIME values. If the response message is 7 to 32 bytes in length, then

termAttr.c_cc[VMIN] = 32;
termAttr.c_cc[VTIME] = 5;

should do the trick. You would be expecting a response message of no more than 32 bytes, or when bytes stop arriving after a half second, then assume the message is complete.

#define CMDLEN  7
unsigned char buffer[32];
unsigned char cmd[CMDLEN];

while (connected == 1) {

    /* construct a new command */

    /* send request */
    nbytes = write(fd, cmd, CMDLEN);
    if (nbytes != CMDLEN) {
        /* problem! */
    }
    /* get response, wait if necessary */
    nbytes = read(fd, buffer, sizeof(buffer));
    if (nbytes < 7) {
        /* problem! */
    }

    /* process response of nbytes */
}

Addendum: response to questions in comments

First, can I leave the filling of the buffer in the signal handler with defined values for VMIN and VTIME?

You have not described any requirement for asynchronous reading of the serial port. (BTW this is not a "serial bus"; USB is a serial bus; EIA/RS-232 is a serial link.) If this is not a master/slave configuration, then please say so. Otherwise a signal handler for reading is an unnecessary complication, and unnecessary complexity is (usually) an indication of poor design. (Exception would be if this code is part of a system that is exclusively event driven.)

Be aware that the serial port is fully buffered for both reading and writing. Your program does not have to have a pending or active read() request when data arrives at the serial port. The kernel stores the serial data in an internal buffer as the data is received. Your program will not lose or miss any data even if it does not use async reading and a signal handler.

The last question is about fcntl(), tcgetattr() and tcsetattr() sistem call. Can you please give me some examples about what error flag I should check?

Such information is documented in the Linux man pages.
If you are using a Linux development system (and you should), then make sure that the man pages are installed. Otherwise you can google for the man page, e.g. "fcntl linux man page"
Check the function prototype.
The returned value is typically the syscall status. If an error is indicated (often by a negative value such as -1), then the global variable errno should be accessed to get more detail.

rc = tcgetattr(fd, &termAttr);
if (rc < 0) {
    printf("failed to get attr: %d, %s", rc, strerror(errno));
    close(fd);
    exit (-2);
}

P.S I am sending an example of the message that I am sending to the device, and an response, so that you can see for what I am talking about.
tx_buffer = 0xFC 0x05 0x11 0x27 0x56 rx_buffer = 0xFC 0x05 0x40 0x27 0x56 0xFC always same 0x05 number of bytes 0x11 command 0x40 response code 0x27 crc_low 0x56 crc_high
example of one command packet and response packet

That is a typical message format for a binary protocol.
As part of the read processing, each received message must be validated before any message processing is performed.
The 0xFC header byte must be checked to ensure that you have message frame synchronization. If the first byte is not equal to 0xFC, then the program has to go into a "hunt for synchonization" mode to find the start of a message (starting with the second byte in this buffer).
The message length should be checked for reasonableness and then used to locate the two CRC bytes, and the message contents can be validated. If the CRC check fails, then the "message" should be ignored and a "hunt for sync" should commence (starting with the second or third byte of this "message").

Addendum 2: response to question on "final code

So the question is now how to create a timer?

Userland can schedule a periodic timer using a POSIX timer and a signal, i.e. timer_create().
But you'll have to check the available clock resolutions to determine if it will let you do 1ms.

But IMO you are going down the wrong path.
You have ignored my advice on not using async I/O.
You are calling read() in a signal handler, which is a potential problem if it tries to sleep.
Your signal handler for async reading has no synchronization or buffer management with the main thread, so the program is at risk of losing data that was read.
You have ignored my advice on checking return codes of syscalls (except for write()s).

If I was writing this program, I would probably use a state machine and there would be only one write() and a read() using the serial port.

Addendum 3: response to 3rd set of questions in comments

Can you please give me a simple example of how can I create a timer, with the timer_create() function?

Apparently you didn't heed my previous advice on using man pages.
There's a code example in the timer_create() man page

About this: “Your signal handler for async reading has no synchronization or buffer management with the main thread, so the program is at risk of losing data that was read.” I think I can solve this If I call clearRXbuffer function after every read() function in the signal handler, so in this case the buffer will only contain the last message data which can be from 7 to 32 bytes. So if new message arrives it will be written in the beginning of the buffer. What do you thing, is this idea good, and am I going in right direction?If not, can you please give me some other buffer management idea.

That is not a reliable solution.

The signal handler executes asynchronously with respect to the main thread.
All shared variables and buffers are therefore critical regions.
Critical regions have to be protected with synchronizing constructs such as mutual exclusion locks (aka mutexes) or semaphores or conditional variables.
Leaving a critical region (like the receive buffer) unprotected might seem to work okay some of the time, but will be unreliable and cause (undetected) loss of data or "strange" program behavior.
You can try various "band-aids" like clearing the buffer, but in the end only proper synchronizing constructs (provided by the OS that execute atomically) will work reliably.

The system maintains a FIFO buffer for the data as it arrives from the serial port, so that no data is lost. Your signal handler counteracts that FIFO by using a single destination buffer that overwrites its buffer everytime the signal handler is executed. The only way this program won't lose any data is to somehow ensure that every write(), read() and CheckRXbuffer() are perfectly coordinated, and the remote device behaves perfectly and responds in a timely manner all the time.
But those are not conditions that a program can depend on when you have asynchronous reads and no synchronizing mechanisms in place!

Tutorials on multithreading usually cover these concepts.

Can I use one write()function, and then another one write() function, because I tried to do that, but it only is written once. Do you know why is that happening?

No.

... am I going in right direction?

Strange that you bother to ask for my opinion on a minor item, when you choose to completely ignore my advice on the major topic of (mis)using async I/O.

Addendum 4: response to 4th set of questions in comments

I want to tell you that in the datasheet of the device is mentioned that I need to use async I/O because ...

Wow, after 2 requests by me (original response & 1st addendum), you finally mention 2 days later why you think you need to use async I/O.

because when I try to power up the device, or reset the device, I should sent N number of commands, and after N number of commands the device response to me.(power up device sequence).

That does not require "async I/O".

Something like this also can happen when I am sending a status to the device, and the device for some reason won’t response to me, in amount period of time, so I should resend the command (async I/O).

That does not require "async I/O" either, nor does that describe async I/O.

because it’s written in the datasheet

Is this datasheet available in English online?

Are you confusing "asynchronous communication" with "asynchronous I/O"?

If this device only sends data in response to a command, and never sends data spontaneously on its own accord (i.e. client/server model), then that means that your program can expect or knows when it can expect messages from the device.
These would be messages that were "solicited" or requested from the device.
The device (from what you have described so far) never sends unsolicited messages.

If this device did send unsolicited messages, then yes, your program might need to have async I/O capability to process these message in a timely manner.

But again, the device (from what you have described so far) only sends solicited messages or no messages when it receives a command.

A device should not be dictating how the host program should be structured. It can only specify the protocol.

sawdust
  • 16,103
  • 3
  • 40
  • 50
  • First of all I want to thank you. You helped me a lot. Thanks for the idea, and for the suggestions. First I fixed the bug in the signal handler, and the idea for defining a VMIN and VTIME values is just what I need it. But know I have a few more questions, and I hope you can help me. – Шијаковски Глигор Jun 26 '13 at 22:19
  • First, can I leave the filling of the buffer in the signal handler with defined values for VMIN and VTIME? Will that mean when an interrupt occur, or when we get something on the serial bus, we will read, but, because now we already defined VMIN and VTIME the read () system call won’t come out as long as we read the minimum value which we defined with VMIN or as long as the time defined with VTIME is up? After is finished all of this, we are out of the handler, and we have the data, that we need to work with. This is one solution. – Шијаковски Глигор Jun 26 '13 at 22:19
  • Or the second solution is after write() to put read() function with defined values for VTIME and VMIN so that means that this read() function will not finished until the minimum VMIN bytes are not read for maximum VTIME. After that we will have the needed data in the buffer. The write()system call is clear to me. The last question is about fcntl(), tcgetattr() and tcsetattr() sistem call. Can you please give me some examples about what error flag I should check? Thank for all your time and help. – Шијаковски Глигор Jun 26 '13 at 22:20
  • P.S I am sending an example of the message that I am sending to the device, and an response, so that you can see for what I am talking about. – Шијаковски Глигор Jun 26 '13 at 22:20
  • tx_buffer = 0xFC 0x05 0x11 0x27 0x56 rx_buffer = 0xFC 0x05 0x40 0x27 0x56 0xFC always same 0x05 number of bytes 0x11 command 0x40 response code 0x27 crc_low 0x56 crc_high – Шијаковски Глигор Jun 26 '13 at 22:21
  • Thank a lot again. I wrote the code, and I am sending the code along with this comment. The code is almost finished, but I have one more question. Because I call the function in a certain time, I would need a timer, so can you tell me how can I make a timer that would interrupt me in every millisecond, and then I would have a variable in which I would count the milliseconds, and then in every 20 milliseconds I would call the function moneyfunction. – Шијаковски Глигор Jun 27 '13 at 19:31
  • The moneyfunction first will check the buffer and It’ll erase everything from it, then the function will request for a response, and then the function is processing the response and in the end the function will make a decision of the next action,and it will create a new tx_paker, or better said a new command, and it sent that command to the device. And then all over again, it will wait a new response etc. – Шијаковски Глигор Jun 27 '13 at 19:31
  • So the question is now how to create a timer? and another question:in while(1) i send j one command, and after that I go to sleep(1), but I notice that I send the first packet, but after that nothing is send, but is logic to send the same message in every second. So this is not logical to me, I hope you can help me. code: – Шијаковски Глигор Jun 27 '13 at 19:32
  • while(connected == 1){ nbytes = write(fd, tx_buffer, sizeof(tx_buffer)); if (nbytes != sizeof(tx_buffer)) { printf("error writing"); } sleep(1); } close(fd); exit(0); } – Шијаковски Глигор Jun 27 '13 at 19:33
  • Thank you for your response. I understand what are you trying to say, but I am new in this, so I would need some more help. Can you please give me a simple example of how can I create a timer, with the timer_create() function? Does this function will through signal, so that in the signal handler I can make something. I understand the problem with the read() function in the signal handler, but I am not going to sleep. – Шијаковски Глигор Jun 28 '13 at 13:56
  • About this: “Your signal handler for async reading has no synchronization or buffer management with the main thread, so the program is at risk of losing data that was read.” I think I can solve this If I call clearRXbuffer function after every read() function in the signal handler, so in this case the buffer will only contain the last message data which can be from 7 to 32 bytes. So if new message arrives it will be written in the beginning of the buffer. What do you thing, is this idea good, and am I going in right direction?If not, can you please give me some other buffer management idea. – Шијаковски Глигор Jun 28 '13 at 13:57
  • About the advice on checking return codes of syscalls, I accept the advice, but it isn’t put in the code that I sent to you, but I will use that. And one last question.Can I use one write()function, and then another one write() function, because I tried to do that, but it only is written once. Do you know why is that happening? – Шијаковски Глигор Jun 28 '13 at 13:57
  • Thanks for all your help, and your time. I try to follow your advice and I am not ignoring your advice at all. I am trying to implement them. First of all about the advice to not use async I/O, I want to tell you that in the datasheet of the device is mentioned that I need to use async I/O because when I try to power up the device, or reset the device, I should sent N number of commands, and after N number of commands the device response to me.(power up device sequence). – Шијаковски Глигор Jun 29 '13 at 09:37
  • Something like this also can happen when I am sending a status to the device, and the device for some reason won’t response to me, in amount period of time, so I should resend the command (async I/O). So that’s why I am using async I/O, not that I don’t want to follow your advice. If you know some way, (and I don’t, because it’s written in the datasheet) so I can solve this problem, I would use your advice happily! – Шијаковски Глигор Jun 29 '13 at 09:37
  • I would use your man page, and I would create the timer using it. If I have any trouble (I hope I wouldn’t) I will contact you. – Шијаковски Глигор Jun 29 '13 at 09:38
  • "The signal handler executes asynchronously with respect to the main thread. All shared variables and buffers are therefore critical regions. Critical regions have to be protected with synchronizing constructs such as mutual exclusion locks (aka mutexes) or semaphores or conditional variables." – Шијаковски Глигор Jun 29 '13 at 09:39
  • "Leaving a critical region (like the receive buffer) unprotected might seem to work okay some of the time, but will be unreliable and cause (undetected) loss of data or "strange" program behavior. You can try various "band-aids" like clearing the buffer, but in the end only proper synchronizing constructs (provided by the OS that execute atomically) will work reliably. " – Шијаковски Глигор Jun 29 '13 at 09:39
  • About this: I am new in linux programming, and I should read all of this, so that I figure out how can I implement some proper synchronizing constructs that are provided by the OS. If you have a short example I would be grateful if you could send me. – Шијаковски Глигор Jun 29 '13 at 09:40
  • First of all I want to say an enormous thank you for all your help, all your time, and your patience to help me. I want to inform you that I used the link that you gave me for create_timer and I create a timer, and it’s working perfectly. – Шијаковски Глигор Jul 02 '13 at 20:58
  • “Are you confusing "asynchronous communication" with "asynchronous I/O"?” I must say that obviously I have been confused about this, but you help me a lot to understand, so now everything is crystal clear. I am sorry that I did not understand better, but like I said I am new in this, but I finally understand everything. So I wrote the program with sync I/O, and I am testing it to see is this works better. – Шијаковски Глигор Jul 02 '13 at 20:59
  • Thanks again for all the help, and I hope that soon I will write you that the result are ok. Best regards. – Шијаковски Глигор Jul 02 '13 at 21:00