I'd like to write a program that emulates a device on a serial port. I'm trying to use pseudoterminals to accomplish this. I want one distinct process to control the master. This process acts as the serial device emulator. I want another process (kermit for example) to be able to communicate with the master using the slave terminal. Because of the distinct process requirement, I am not using any forks. Almost every pseudoterminal example on the Internet shows the use of fork() for master/slave.
I have it working in one direction. That is, I can have the slave process write data to the slave pseudoterminal, and the master will read it off the master pseudoterminal just fine.
The problem is in the other direction. I can't get the master to write data and the slave to read data.
I'll show both the nonworking bidirectional code and the working unidirectional code.
Nonworking bidirectional master:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc, char* argv[])
{
// get the master fd
int masterfd = open("/dev/ptmx", O_RDWR | O_NOCTTY);
if(masterfd < 0)
{
perror("getpt");
exit(1);
}
// grant access to the slave
if(grantpt(masterfd) < 0)
{
perror("grantpt");
exit(1);
}
// unlock the slave
if(unlockpt(masterfd) < 0)
{
perror("unlockpt");
exit(1);
}
// get the path to the slave
char slavepath[64];
if(ptsname_r(masterfd, slavepath, sizeof(slavepath)) < 0)
{
perror("ptsname_r");
exit(1);
}
printf("Using %s\n", slavepath);
char bufout = 'D';
char bufin;
int c;
while(1)
{
printf("reading\n");
c = read(masterfd, &bufin, 1);
printf("read %i bytes: %c\n", c, bufin);
if(c == -1) break;
if(bufout == 'D') bufout = 'E';
else if(bufout == 'E') bufout = 'D';
printf("writing %c\n", bufout);
c = write(masterfd, &bufout, 1);
printf("wrote %i bytes\n", c);
if(c == -1) break;
sleep(1);
}
close(masterfd);
return 0;
}
Nonworking bidrectional slave:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char* argv[])
{
int fd = open("/dev/pts/15", O_RDWR | O_NOCTTY);
if(fd < 0)
{
perror("open");
exit(1);
}
char bufout = 'A';
char bufin;
int c;
while(1)
{
if(bufout == 'A') bufout = 'B';
else if(bufout == 'B') bufout = 'A';
printf("writing %c\n", bufout);
c = write(fd, &bufout, 1);
if(c == -1) break;
printf("reading\n");
c = read(fd, &bufin, 1);
printf("read %i bytes: %c\n", c, bufin);
if(c == -1) break;
sleep(1);
}
close(fd);
}
Output of nonworking master: (Notice the first character received came from the slave, then the rest are characters written by the master. In other words, the master is reading off the same characters it wrote to the master pts and its ignoring what the slave is writing, except for the first character.)
Using /dev/pts/15
reading
read 1 bytes: B [<--- FROM THE SLAVE]
writing E
wrote 1 bytes
reading
read 1 bytes: E [<--- REST FROM THE MASTER]
writing D
wrote 1 bytes
reading
read 1 bytes: D
writing E
wrote 1 bytes
reading
read 1 bytes: E
writing D
wrote 1 bytes
reading
read 1 bytes: D
^C
Output of nonworking slave: (Never receives what the master is writing.)
writing B
reading
^C
Working unidrectional master:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc, char* argv[])
{
// get the master fd
int masterfd = open("/dev/ptmx", O_RDWR | O_NOCTTY);
if(masterfd < 0)
{
perror("getpt");
exit(1);
}
// grant access to the slave
if(grantpt(masterfd) < 0)
{
perror("grantpt");
exit(1);
}
// unlock the slave
if(unlockpt(masterfd) < 0)
{
perror("unlockpt");
exit(1);
}
// get the path to the slave
char slavepath[64];
if(ptsname_r(masterfd, slavepath, sizeof(slavepath)) < 0)
{
perror("ptsname_r");
exit(1);
}
printf("Using %s\n", slavepath);
char bufout = 'D';
char bufin;
int c;
while(1)
{
printf("reading\n");
c = read(masterfd, &bufin, 1);
printf("read %i bytes: %c\n", c, bufin);
if(c == -1) break;
sleep(1);
}
close(masterfd);
return 0;
}
Working unidrectional slave:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char* argv[])
{
int fd = open("/dev/pts/15", O_RDWR | O_NOCTTY);
if(fd < 0)
{
perror("open");
exit(1);
}
char bufout = 'A';
char bufin;
int c;
while(1)
{
if(bufout == 'A') bufout = 'B';
else if(bufout == 'B') bufout = 'A';
printf("writing %c\n", bufout);
c = write(fd, &bufout, 1);
if(c == -1) break;
sleep(1);
}
close(fd);
}
Output of working master: (Reads what the slave writes successfully.)
Using /dev/pts/15
reading
read 1 bytes: B
reading
read 1 bytes: A
reading
read 1 bytes: B
reading
read 1 bytes: A
reading
read 1 bytes: B
^C
Output of working slave:
writing B
writing A
writing B
writing A
writing B
^C
screen
work? I'm not positive about this. When I tried something like this I ended up having a lot ofxterm
s running and connected to the terminals withscreen -x
and entered commands with xlib. But there HAS to be a better way. – Baluchistan