Linux receive signal break for ttycontrol program - c

I would like to receive SIGINT if my process controlling /dev/ttyS2 receives BREAK on a serial port. I run this program from a shell. From what I discovered only "the terminal is the controlling terminal of a foreground process group, it will cause a SIGINT to be sent to this foreground process group" I tried make process making controller of terminal but it fails.
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
#include <stdio.h>
#include <signal.h>
#include <string.h>
#define BAUDRATE B115200
#define MODEMDEVICE "/dev/ttyS2"
#define _POSIX_SOURCE 1 /* POSIX compliant source */
#define FALSE 0
#define TRUE 1
__sighandler_t sighandle(int signum, __sighandler_t h) {
fprintf(stderr, "BREAK DETECTED\n");
signal(SIGINT, (__sighandler_t) sighandle);
return SIG_IGN;
}
volatile int STOP=FALSE;
int main()
{
int fd,c, res;
struct termios oldtio,newtio;
char buf[255];
pid_t pid;
signal(SIGINT, (__sighandler_t) sighandle);
fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY );
if (fd <0) {perror(MODEMDEVICE); return (-1); }
tcgetattr(fd,&oldtio); /* save current port settings */
memset(&newtio, 0,sizeof(newtio));
newtio.c_cflag |= BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD | BRKINT;
newtio.c_iflag &= ~IGNBRK ;
newtio.c_oflag = 0;
/* set input mode (non-canonical, no echo,...) */
newtio.c_lflag = 0;
newtio.c_cc[VTIME] = 0; /* inter-character timer unused */
newtio.c_cc[VMIN] = 1; /* blocking read until 5 chars received */
tcflush(fd, TCIFLUSH);
tcsetattr(fd,TCSANOW,&newtio);
if( ioctl(fd, TIOCSCTTY, 1) <0 )
{
printf("Error number: %d\n", errno);
}
if ( tcsetpgrp(fd, tcgetpgrp(0) ) < 0 )
{
syslog(LOG_PERROR,"tcsetpgrp failed: %d " ,errno);
syslog(LOG_PERROR,"EBADF is %d " ,EBADF);
syslog(LOG_PERROR,"EINVAL is %d " ,EINVAL);
syslog(LOG_PERROR,"ENOTTY is %d " ,ENOTTY);
syslog(LOG_PERROR,"EPERM is %d " ,EPERM);
}
while (STOP==FALSE) { /* loop for input */
res = read(fd,buf,255); /* returns after 5 chars have been input */
buf[res]=0; /* so we can printf... */
printf(":%s:%d\n", buf, res);
if (buf[0]=='z') {STOP=TRUE;}
}
tcsetattr(fd,TCSANOW,&oldtio);
return 0;
}

I finally found answer to my problem. I am using custom board so it don't have to work always but when serial port is configured correctly user can get number of breaks by serial port by cat /proc/tty/device/serial, that kind of functionality I was hoping to get

#define _POSIX_SOURCE 1 has no effect unless it precedes the #includes.
With a C99 compiler, you may #include <stdbool.h> instead of #define your own TRUE and FALSE.
/* from signal.h, */
typedef void(*sighandler_t)(int);
/* which means the handler must be declared thusly */
void sighandle(int);
/* as a result, no casting is necessary */
sighandler_t p_sighandle = &sighandle;
The foreground process group is set by tcsetpgrp, which you appear to be missing.

Related

Can not achieve real time SCHED_FIFO thread within process

Good day everyone !
I am trying to get a 2nd thread (the serial thread) to run as near real time as possible.
Within my 2nd spawned serial thread I select() with timeout of 3 mS on a serial port.
I also get real time before select() ... and then after to get select() delta time.
Problem is that sometimes I get no indication of select returning 0 (what I call a timeout due to the 3 mS elapsing) ... but I do occasionally get a total time much greater than the 3 mS (4.447 for example).
I have to conclude that the serial thread is getting preempted?
Any ideas?
What Linux commands can I use to see if the thread is preempted?
Thanks!
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <signal.h>
#include <termios.h>
#include <fcntl.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/resource.h>
//-----------------------------------
// signal Handler stuff.
//-----------------------------------
static
struct sigaction mySigActTerm;
volatile
int myTerminate = 0;
void terminateHandler(int signum, siginfo_t *info, void *ptr)
{
// set a flag here and get out.
myTerminate = 1;
}
void getNowTime(char* str)
{
time_t rawtime;
time(&rawtime);
ctime_r(&rawtime, str);
// clobber the unwanted newline.
str[24] = '\0';
}
void myResLimit()
{
struct
rlimit procLimit;
char strNowTime[26];
getrlimit(RLIMIT_RTTIME, &procLimit);
getNowTime(strNowTime);
fprintf(stderr, "%s - RLIMIT_RTTIME: soft=%lld, hard=%lld\n", strNowTime, (long long) procLimit.rlim_cur, (long long)procLimit.rlim_max);
getrlimit(RLIMIT_RTPRIO, &procLimit);
getNowTime(strNowTime);
fprintf(stderr, "%s - RLIMIT_RTPRIO: soft=%lld, hard=%lld\n", strNowTime, (long long) procLimit.rlim_cur, (long long) procLimit.rlim_max);
getrlimit(RLIMIT_CPU, &procLimit);
getNowTime(strNowTime);
fprintf(stderr, "%s - RLIMIT_CPU: soft=%lld, hard=%lld\n", strNowTime, (long long) procLimit.rlim_cur, (long long) procLimit.rlim_max);
}
void* serialThread(void* arg)
{
int sfd; // serial file descriptor.
char myChar;
int rtn;
fd_set myfds;
struct
timeval tm_out,
start,
end,
delta;
struct
termios oldtio,
newtio;
sfd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY);
tcgetattr(sfd, &oldtio);
newtio = oldtio;
cfmakeraw(&newtio);
newtio.c_cflag |= CREAD;
newtio.c_cflag |= CLOCAL;
newtio.c_cflag &= ~CSTOPB;
newtio.c_cflag &= ~CRTSCTS;
newtio.c_cflag &= ~CSIZE;
newtio.c_cflag |= CS7;
newtio.c_cflag |= PARENB;
newtio.c_cflag &= ~PARODD;
newtio.c_cc[VTIME] = 1;
newtio.c_cc[VMIN] = 1;
tcflush(sfd, TCIFLUSH);
while (1) {
FD_ZERO(&myfds);
FD_SET(sfd, &myfds);
tm_out.tv_sec = 0;
tm_out.tv_usec = 3000;
// get sys call start time.
gettimeofday(&start, NULL);
rtn = select(sfd+1 ,&myfds, NULL, NULL, &tm_out);
// get sys call end time.
gettimeofday(&end, NULL);
timersub(&end, &start, &delta);
if (rtn == 0) {
fprintf(stderr, "tm_out = %02d.%06d delta = %02d.%06d\n", tm_out.tv_sec, tm_out.tv_usec, delta.tv_sec, delta.tv_usec);
}
else
read(sfd, &myChar, 1);
}
}
//-----------------------------------
// the one and only MAIN.
//-----------------------------------
int main()
{
//-----------------------------------------------
// locals.
int rtn;
char strNowTime[26];
pthread_t serialThdID;
pthread_attr_t serialAttr;
struct
sched_param serialParam;
//-----------------------------------------------
// Log OS resource limits.
myResLimit();
//-----------------------------------------------
// initialize the signals struct.
// ... and setup signals.
memset(&mySigActTerm, 0, sizeof(mySigActTerm));
mySigActTerm.sa_sigaction = terminateHandler;
mySigActTerm.sa_flags = SA_SIGINFO;
sigaction(SIGTERM, &mySigActTerm, NULL);
//-----------------------------------------------
// set initial default pthread attr values.
if ((rtn = pthread_attr_init(&serialAttr)) != 0) {
getNowTime(strNowTime);
fprintf(stderr, "%s - main() - pthread_attr_init()\n%s\n", strNowTime, strerror(rtn));
exit(EXIT_FAILURE);
}
//-----------------------------------------------
// set for best near real time policy.
if ((rtn = pthread_attr_setschedpolicy(&serialAttr, SCHED_FIFO)) !=0) {
getNowTime(strNowTime);
fprintf(stderr, "%s - main() - pthread_attr_setschedpolicy()\n%s\n", strNowTime, strerror(rtn));
exit(EXIT_FAILURE);
}
//-----------------------------------------------
// set to explicit inherit or attr obj will be ignored.
if ((rtn = pthread_attr_setinheritsched(&serialAttr, PTHREAD_EXPLICIT_SCHED)) !=0) {
getNowTime(strNowTime);
fprintf(stderr, "%s - main() - pthread_attr_setinheritsched()\n%s\n", strNowTime, strerror(rtn));
exit(EXIT_FAILURE);
}
//-----------------------------------------------
// set to un-limited thread priority.
serialParam.sched_priority = 0;
if ((rtn = pthread_attr_setschedparam(&serialAttr, &serialParam)) !=0) {
getNowTime(strNowTime);
fprintf(stderr, "%s - main() - pthread_attr_setschedparam()\n%s\n", strNowTime, strerror(rtn));
exit(EXIT_FAILURE);
}
//-----------------------------------------------
// start the new thread.
if ((rtn = pthread_create(&serialThdID, &serialAttr, serialThread, NULL)) == 0) {
getNowTime(strNowTime);
fprintf(stderr, "%s - starting serial thread.\n", strNowTime);
}
else {
getNowTime(strNowTime);
fprintf(stderr, "%s - main() - pthread_create() returned %d\n%s\n", strNowTime, rtn, strerror(rtn));
exit(EXIT_FAILURE);
}
//-----------------------------------------------
// no need to keep this junk if pthread_create() succeeded.
if ((rtn = pthread_attr_destroy(&serialAttr)) != 0) {
getNowTime(strNowTime);
fprintf(stderr, "%s - main() - pthread_attr_destroy()\n%s\n", strNowTime, strerror(rtn));
}
while (myTerminate == 0) {
}
}
Well no one seems to know why this is happening to me but ...
... after nearly a year struggling ...
... I found a post that is exactly what is happening to me.
I post a link so no one else will suffer my fate.
C - select() seems to block for longer than timeout

Why is Linux's pty driver replacing VEOFs with NULs?

It seems that the pty driver on Linux is replacing VEOF characters (^D, \4) with NUL bytes (\0) in the data already written from the master side if the terminal settings are changed with tcsetattr(TCSANOW) to non-canonical mode before reading it on the slave side.
Why is this happening? Does it have any justification or it's simply a bug?
Is there any way to avoid it? -- other than waiting for input from the slave on the master side before writing anything, which is not practical, because on the slave side there may be another program -- the routine of setting the terminal into raw mode which I've simplified here is usually what any shell with line-editing capabilities does.
While having eg \r replaced by \n could be expected (because the ICRNL flag was already applied), I cannot make any rationale for those NUL bytes appearing out of nowhere.
Test case below: it will print foo\x00\x00\x00\x00 on Linux, but foo\x04\x04\x04\x04 on *BSD and foo on Solaris.
#define _XOPEN_SOURCE 600
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <ctype.h>
#include <stdio.h>
#include <termios.h>
#include <err.h>
#ifdef __sun
#include <stropts.h>
#define push_streams(fd)\
if(ioctl(fd, I_PUSH, "ptem")) err(1, "ioctl(I_PUSH, ptem)");\
if(ioctl(fd, I_PUSH, "ldterm")) err(1, "ioctl(I_PUSH, ldterm)");
#else
#define push_streams(sd) /* no need */
#endif
int main(void){
int mt, st; char *sname;
/* openpty()-like boilerplate */
if((mt = posix_openpt(O_RDWR|O_NOCTTY)) == -1) err(1, "posix_openpt");
if(grantpt(mt)) err(1, "grantpt");
if(unlockpt(mt)) err(1, "unlockpt");
if(!(sname = ptsname(mt))) err(1, "ptsname");
if((st = open(sname, O_RDWR|O_NOCTTY)) == -1) err(1, "open %s", sname);
push_streams(st);
/* master */ {
char test[] = "foo\4\4\4\4";
if(write(mt, test, sizeof test - 1) < sizeof test - 1)
err(1, "write");
}
/* slave */ {
unsigned char buf[512]; int i, r;
struct termios ts;
usleep(1000);
if(tcgetattr(st, &ts)) err(1, "tcgetattr");
ts.c_lflag &= ~ICANON;
if(tcsetattr(st, TCSANOW, &ts)) err(1, "tcsetattr");
if((r = read(st, buf, sizeof buf)) < 0)
err(1, "read");
for(i = 0; i < r; i++)
if(isprint(buf[i])) putchar(buf[i]);
else printf("\\x%02x", buf[i]);
putchar('\n');
}
return 0;
}
This conversion is done by the line discipline driver when the data is written by the master, not when the slave reads the data. The relevant code is:
https://elixir.bootlin.com/linux/latest/source/drivers/tty/n_tty.c#L1344

Serial modem is not returning '>'

I'm communicating GSM modem through Serial Port. And send AT commands to control GSM modem. Using below code. Working fine except when sending SMS.
To Send SMS:
AT+CMGS="+31628870634"
> This is the text message.→
+CMGS: 198
OK
When u send AT+CMGS="+31628870634" it must immediately return >. But my code doesn't return > and returning > with ERROR. Where am i making mistake ?
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#define BAUDRATE B38400
#define dev "/dev/ttyUSB0"
#define _POSIX_SOURCE 1
#define FALSE 0
#define TRUE 1
volatile int STOP=FALSE;
main()
{
char pinn[20];
char buf[255];
int fd,res=0;
printf("%s\n", dev);
struct termios oldtio,newtio;
fd = open(dev, O_RDWR | O_NOCTTY );
if (fd <0) {perror(dev); exit(-1); }
tcgetattr(fd,&oldtio); /* save current serial port settings */
bzero(&newtio, sizeof(newtio));
newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;
newtio.c_iflag = IGNPAR | ICRNL;
newtio.c_oflag = 0;
newtio.c_lflag = ICANON;
newtio.c_cc[VMIN] = 1;
tcflush(fd, TCIFLUSH);
tcsetattr(fd,TCSANOW,&newtio);
if (fd < 0)
{
printf("Error opening serial port\n");
exit(1);
}
// while(1){
scanf("%s",pinn);
strcat(pinn,"\r");
if (write(fd, pinn, strlen(pinn)) < strlen(pinn)) printf("Write error - %s \n", strerror(errno));
pinn[strlen(pinn)-1]=0;
while(1){
res = read(fd,buf,255);
buf[res]=0;
buf[res-1]=0;
if (res>1&&NULL==strstr(buf,pinn)) break;
}
printf("\"%s\"\n", buf);
tcsetattr(fd,TCSANOW,&oldtio);
close(fd);
}
Here's Output of my code:
./serial_test
AT+CMGS="99928626"
After a while returns:
"> "
"ERROR"

Unable to read data from serial port in C on Ubuntu

I have two C Programs 1. To Write Data to Serial Port and 2. To Read Data from Serial Port. I have connected a USB-Serial converter to my laptop and shorted Tx & Rx to test the program.
Program 1 is getting executed successfully.
Program 2 blocks at read.
Can anyone please suggest a solution for this.
Thanks in advance.
Program to Write to Serial Port:
#include <stdio.h> /* Standard input/output definitions */
#include <string.h> /* String function definitions */
#include <unistd.h> /* UNIX standard function definitions */
#include <fcntl.h> /* File control definitions */
#include <errno.h> /* Error number definitions */
#include <termios.h> /* POSIX terminal control definitions */
/*
* 'open_port()' - Open serial port 1.
*
* Returns the file descriptor on success or -1 on error.
*/
int write_port(void)
{
int fd; /* File descriptor for the port */
fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1)
{
/* Could not open the port. */
perror("open_port: Unable to open /dev/ttyUSB0 - ");
}
else
fcntl(fd, F_SETFL, 0);
int n = write(fd, "ATZ\r", 4);
if (n < 0)
fputs("write() of 4 bytes failed!\n", stderr);
return (fd);
}
void main(){
printf("\nAccessing Serial Port to Write\n");
int fd=write_port();
if(fd<0){
printf("\nSerial Port Access Failed\n");
return;
}
printf("\n%d\tSuccess\n",fd);
}
Program to Read from Serial Port:
#include <stdio.h> /* Standard input/output definitions */
#include <string.h> /* String function definitions */
#include <unistd.h> /* UNIX standard function definitions */
#include <fcntl.h> /* File control definitions */
#include <errno.h> /* Error number definitions */
#include <termios.h> /* POSIX terminal control definitions */
/*
* 'read_port()' - Reads serial port 1.
*
* Returns the file descriptor on success or -1 on error.
*/
int read_port(void)
{
int fd = open("/dev/ttyUSB0", O_RDONLY | O_NOCTTY);
if (fd == -1)
{
/* Could not open the port. */
perror("open_port: Unable to open /dev/ttyUSB0 - ");
}
char buffer[32];
int n = read(fd, buffer, sizeof(buffer));
if (n < 0)
fputs("read failed!\n", stderr);
return (fd);
}
void main(){
printf("\nAccessing Serial Port to Read\n");
int fd=read_port();
if(fd<0){
printf("\nSerial Port Access Failed\n");
return;
}
printf("\n%d\tSuccess\n",fd);
}

C program to read data from USB device connected to the system

I am trying to fetch data from the USB device (say pendrive) connected to the USB port of a system. Here, I am able to open the device file and read some random raw data. But I want to fetch data like minicom/teraterm.
Please let me know what methods and libraries I can use to do it successfully and how can it be done.
#include <stdio.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
#include <signal.h>
#include <sys/time.h>
int main()
{
short portfd=-1;
int n,f,len;
char buf[256],*s;
alarm(2);
#if defined(O_NDELAY) && defined(F_SETFL)
portfd = open("/dev/ttyUSB0", O_RDWR|O_NDELAY);
if (portfd >= 0){
/* Cancel the O_NDELAY flag. */
printf("port openend\n");
n = fcntl(portfd, F_GETFL, 0);
(void) fcntl(portfd, F_SETFL, n & ~O_NDELAY);
}
#else
portfd = open("/dev/ttyUSB0", O_RDWR);
#endif
if (portfd >= 0)
{
if (len == 0) len = strlen(s);
for(f = 0; f < len && f <100; f++)
buf[f] = *s++ | 0x80;
write(portfd, buf, f);
printf("Do write\n");
while(portfd>=0){
printf("%s\n",buf);
}
}
alarm(0);
signal(SIGALRM, SIG_IGN);
if (portfd < 0) {
printf("cannot open %s. Sorry.\n", "/dev/ttyUSB0");
}
}
Log of the output:
���������鉀�������������������鍀���������������������������������������������������������������2
����������鉀�������������������鍀���������������������������������������������������������������2
you will need to set the correct port configuration...
struct termios oldtio,newtio;
// open port...
// save existing attributes
tcgetattr(fd,&oldtio);
// set attributes - these flags may change for your device
#define BAUDRATE B9600
memset(&newtio, 0x00, sizeof(newtio));
newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;
newtio.c_iflag = IGNPAR | ICRNL;
newtio.c_oflag = 0;
tcflush(fd, TCIFLUSH);
tcsetattr(fd,TCSANOW,&newtio);
//reset attributes
tcsetattr(fd,TCSANOW,&oldtio);
I have a rough working example here... http://file-hub.com/cmd:thread/142300

Resources