"Gateway" /dev/ttySx <-> /dev/ttySy?

2 views
Skip to first unread message

Nico Hoffmann

unread,
Dec 9, 2020, 5:13:44 PM12/9/20
to
Hallo,

ich brauche eine Art Gateway zwischen zwei seriellen Schnittstellen,
konkret zwischen /dev/ttyS0 (das ist die RS232) und /dev/ttyUSB0
(dahinter hängt ein spezieller USB-Seriell-Adapter mit FTDI-Chip).

Es sollen kurze (ca 5 - 20 Byte) Datentelegramme bidirektional
übertragen werden.

Ich habe nun versucht, mir was in C zu basteln. Mein Ansatz ist im
groben, die beiden Devices RW zu öffnen und dann zwei Threads zu
starten, an die ich die "File"handles für die Schnittstellen
weitergebe. Der eine Thread liest RS232 und schreibt USB, der zweite
Thread liest USB und schreibt RS232.
Für die beiden Threads starte ich einunddieselbe Funktion zweimal, nur
mit Quell/Ziel-Handle vertauscht.

Mein Programm läßt sich fehlerfrei kompilieren und starten, aber es
funktioniert nur in einer Richtung. D.h. ich kann per RS232 etwas
einfüllen, was über USB rausgeschrieben wird, aber wenn auf USB etwas
ankommt, wird es nicht nach RS232 durchgereicht, bzw. es wird vom
Programm offenbar gar nicht eingelesen.
Ich weiß aber sicher, dass auch über USB etwas ankommt.

Für mich ist das alles neu, ich habe mir den Code aus dem Internet
zusammenkopiert.

Vielleicht zunächst eine grundsätzliche Frage, bevor ich Code wälze:
Ist meine Herangehensweise überhaupt sinnvoll, oder geht das
geschickter, einfacher, etc?

N.
--
Mephisto war kein Sachse.

Bonita Montero

unread,
Dec 10, 2020, 12:52:12 AM12/10/20
to
Dann kopier den Code doch hier rein.
Bei so einer minimalistischen Aufgabe kann das ja nicht viel sein.

Nico Hoffmann

unread,
Dec 10, 2020, 2:46:26 AM12/10/20
to
Bonita Montero schreibt:

> Dann kopier den Code doch hier rein.
> Bei so einer minimalistischen Aufgabe kann das ja nicht viel sein.

Bitte sehr :-)




#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 */
#include <pthread.h>
#include <stdlib.h>




#define SERIAL "/dev/ttyS0"
#define SERIALUSB "/dev/ttyUSB0"

pthread_t tid[2];
struct data {int src; int dst;};

/*
* 'open_port()' - Open serial port 1.
*
* Returns the file descriptor on success or -1 on error.
*/

int open_port(char * portname)
{
int fd; /* File descriptor for the port */


fd = open(portname, O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1)
{
/*
* Could not open the port.
*/
perror(portname);
perror("open_port: Unable to open port");
}
else
{
fcntl(fd, F_SETFL, 0);
printf("open_port successfull on %s\n", portname);
}

return (fd);
}

int set_options(int fd)
{

struct termios options;

/*
* Get the current options for the port...
*/

tcgetattr(fd, &options);

/*
* Set the baud rates to 9600...
*/

cfsetispeed(&options, B9600);
cfsetospeed(&options, B9600);

/*
* Enable the receiver and set local mode...
*/

options.c_cflag |= (CLOCAL | CREAD);

/*
* Set the new options for the port...
*/

tcsetattr(fd, TCSANOW, &options);

/* data size */
options.c_cflag &= ~CSIZE; /* Mask the character size bits */
options.c_cflag |= CS8; /* Select 8 data bits */

/* parity */
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;

/* disable hardware flow control */
options.c_cflag &= ~CRTSCTS;

/* also disable software flow control */
options.c_iflag &= ~(IXON | IXOFF | IXANY);

/* raw input (not line oriented) */
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);

/* choosing raw output */
options.c_oflag &= ~OPOST;

return 0; // show always success, never fail.
}

void *serial_channel(void *arg)
/* int serial_channel(struct data f) */
{
struct data *f = (struct data *)arg;

unsigned char buf[1000];
ssize_t s=0;
int wr_ret = 0;


printf("thread-ID: %ld\n", pthread_self());
printf("file handle src: %i\n", f->src);
printf("file handle dst: %i\n", f->dst);


while(1)
{
/* write(fd, &c,1); */

if((s=read(f->src, &buf, 1000)) != -1)
{
/* printf("0x %s ", buf); */

if( s > 0)
{

wr_ret = write(f->dst, buf, s);
if(wr_ret == -1)
{
printf("writing to %i failed with %i\n", f->dst, errno);
}
else
{
printf("%li bytes from \"%i\" ->", s, f->src);
for (int i = 0; i < s; i++)
{
printf("0x%x ", buf[i]);
}
printf("<- to \"%i\"\n", f->dst);
fflush(stdout);
}
}
}
else
{
perror("read");
}
}

/* return 0; */
}



int main(void)
{

/* unsigned char d; */

struct data *f;
struct data *h;

int fd_ser, fd_usb; /* File descriptor for the port */

if((fd_ser = open_port(SERIAL)) < 0)
{
return fd_ser;
}

if((fd_usb = open_port(SERIALUSB)) < 0)
{
return fd_usb;
}


set_options(fd_ser);
set_options(fd_usb);

/* Speicher fuer Daten anfordern u. m. Werten belegen*/
f = (struct data *)malloc(sizeof(struct data));
if(f == NULL) {
printf("Konnte keinen Speicher reservieren ...!\n");
exit(EXIT_FAILURE);
}

h = (struct data *)malloc(sizeof(struct data));
if(h == NULL) {
printf("Konnte keinen Speicher reservieren ...!\n");
exit(EXIT_FAILURE);
}

f->src = fd_ser;
f->dst = fd_usb;
h->src = fd_usb;
h->dst = fd_ser;


/* serial_channel(&f); */

int i = 0;
int err;
/* while(i < 1) */
/* { */
err = pthread_create(&(tid[i]), NULL, &serial_channel, h);
if (err != 0)
{
printf("\ncan't create thread :[%s]", strerror(err));
}
else
{
printf("\n Thread created successfully\n");
}
i++;
/* } */

err = pthread_create(&(tid[i]), NULL, &serial_channel, f);
if (err != 0)
{
printf("\ncan't create thread :[%s]", strerror(err));
}
else
{
printf("\n Thread created successfully\n");
}



char c;

/* forever loop to keep the treads running */
while((c=getchar()) != 'q')
{
}

close(fd_ser);
close(fd_usb);

return 0;

Enrik Berkhan

unread,
Dec 10, 2020, 7:13:04 AM12/10/20
to
Nico Hoffmann <oxen...@gmx.de> wrote:
> /*
> * Enable the receiver and set local mode...
> */
>
> options.c_cflag |= (CLOCAL | CREAD);
>
> /*
> * Set the new options for the port...
> */
>
> tcsetattr(fd, TCSANOW, &options);
>
> /* data size */
> options.c_cflag &= ~CSIZE; /* Mask the character size bits */
> options.c_cflag |= CS8; /* Select 8 data bits */

Ich habe da jetzt nur ganz flüchtig drüber geschaut, aber ist das
tcsetattr() nicht etwas zu früh?

Gruß,
Enrik

Nico Hoffmann

unread,
Dec 13, 2020, 4:03:17 PM12/13/20
to
Enrik Berkhan schreibt:
In der Tat...!

Ich hab' es korrigiert. Jetzt funktioniert es offenbar :-)

N.

Rainer Weikusat

unread,
Dec 13, 2020, 4:44:51 PM12/13/20
to
Nico Hoffmann <oxen...@gmx.de> writes:

Ein paar allgemeine Bemerkungen:


> int main(void)
> {
>
> /* unsigned char d; */
>
> struct data *f;
> struct data *h;

[...]

> /* Speicher fuer Daten anfordern u. m. Werten belegen*/
> f = (struct data *)malloc(sizeof(struct data));
> if(f == NULL) {
> printf("Konnte keinen Speicher reservieren ...!\n");
> exit(EXIT_FAILURE);
> }
>
> h = (struct data *)malloc(sizeof(struct data));
> if(h == NULL) {
> printf("Konnte keinen Speicher reservieren ...!\n");
> exit(EXIT_FAILURE);
> }

Diese beiden Datenstrukturen kann man auch auf dem Stack anlegen, dh als

struct data f,h;

deklarieren. Dann muß man sich den dafür benötigen Speicher nicht mit
malloc anfordern und dafür eine Fehlerbehandlung machen.

[...]

> err = pthread_create(&(tid[i]), NULL, &serial_channel, h);
> if (err != 0)
> {
> printf("\ncan't create thread :[%s]", strerror(err));
> }
> else
> {
> printf("\n Thread created successfully\n");
> }
> i++;
> /* } */
>
> err = pthread_create(&(tid[i]), NULL, &serial_channel, f);
> if (err != 0)
> {
> printf("\ncan't create thread :[%s]", strerror(err));
> }
> else
> {
> printf("\n Thread created successfully\n");
> }
>
>
>
> char c;
>
> /* forever loop to keep the treads running */
> while((c=getchar()) != 'q')
> {
> }

Es gibt einen Systemaufruf pause (=> pause(2)), der den Thread, der ihn
ausführt, solange im Kernel blockiert, bis der Prozess ein Signal
empfangen hat. Den könnte man hier verwenden und das Programm mit Ctrl-C
abbrechen, wenn man das möchte.

Bessere Idee: Wozu eine Thread haben, der nichts tut außer warten?
Anstatt zwei zusätzliche Threads zu erzeugen, könnte man bloß einen
starten und die Arbeitsfunktion danach aus main direkt aus main heraus
aufrufen.
Reply all
Reply to author
Forward
0 new messages