Forum: PC-Programmierung serielle Schnittstellen-Parameter werden durch open() verändert (Linux)


von Erwin M. (nobodyy)


Lesenswert?

Um die zwischen zwei Maschinen seriell (genauer: Per RS-422) 
ausgetauschten Nachrichten aufzuzeichnen habe ich die Schnittstellen an 
einem Messrechner entsprechend konfiguriert, aber schon bei nur einer 
Schnittstelle zeigt sich das Problem das sich das die Parameter (Baud 
Rate etc.) unmittelbar nach dem Start vom Programmm, z. B. jpnevulator, 
verändert sind, so das ich nichts oder nur Müll empfange.

Deshalb musste ich das verwendete Skript um eine Hintergrund-Funktion 
ergänzen, die die Parameter eine Sekunde nach dem Start vom Programm 
nochmals setzt und damit geht es:
1
#!/bin/bash
2
# With the serial port $DEVICE: Sniff.
3
4
# no unset variables
5
set -u
6
7
# serial port type, e. g. ttyS0, ttyUSB0, ...
8
DEVICE=/dev/$1
9
10
11
# Set the configuration a second time just after the start of jpnevulator because some adapters, e. g. an USB
12
# adapter with a FT232, do lose some parameters like parenb or the baud rate although the program does not
13
# change the parameters. I could see this under
14
# 3.19.0-22-lowlatency #22-Ubuntu SMP PREEMPT Tue Jun 16 17:47:20 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux
15
# and the resuls is no input with a changed baud rate and ghost bytes of type 0xFF at unset parenb.
16
backgroundfunction ()
17
{
18
  sleep 1
19
  stty 19200 -parodd cs8 parenb -cstopb -crtscts -ixon -clocal -echo cread -F $DEVICE
20
}
21
22
# configure: 19200 baud, 8 bits/byte, even parity, one stop bit, no RTS/CTS handshaking,
23
# no XON/XOFF flow control, no modem control signals, no echo, open device /dev/$DEVICE
24
#stty 19200 -parodd cs8 parenb -cstopb -crtscts -ixon -clocal -echo cread -cdtrdsr -F $DEVICE
25
stty 19200 -parodd cs8 parenb -cstopb -crtscts -ixon -clocal -echo cread -F $DEVICE
26
27
# sniffing: only reading, print a line with the current time and device before the bytes,
28
# print up 99 bytes per line, extra column with the current index number of the byte in the output,
29
# first device $DEVICE0 with alias Nr1, and copy the output to the log file
30
backgroundfunction &
31
echo starting jpnevulator ...
32
jpnevulator --read --timing-print --width=99 --byte-count --tty $DEVICE:Nr1 2>&1 | tee log_single_x25_`date +%F_%H.%M.%S`.txt
33
34
exit 0

Ohne diesen Workaroung zeigt sich beispielsweise, am USB-RS-422-Adapter 
von Delock, der Fehler das der Parameter parenb fehlte (geändert zu 
-parenb). Das verursachte verlorene Daten-Bytes und Geister-Bytes vom 
Typ 0xff, also Datenmüll.
Mit einem anderen, auch nur lesenden Programm (multithreaded_logger), 
zeigt sich das die Baudrate geändert ist von 19200 auf 9600, mit dem 
Resultat das nichts empfangen wurde.
Von einer anderen Konsole zeigte sich diese Verstellung mittels

stty -a -F $DEVICE

und lsof zeigte keinen anderen Prozess der auf die Schnittstelle 
zugriff.

Woher kommt dieses verdeckte Verstellen der Parameter?

In den Kernel-Nachrichten steht nichts dazu und auf dem Messrechner 
läuft Ubuntu Server ohne GUI.

: Verschoben durch User
von Peter II (Gast)


Lesenswert?

Erwin M. schrieb:
> Woher kommt dieses verdeckte Verstellen der Parameter?

jedes Programm was auf die Serielle Schnittstelle zugreift sollte auch 
die Parameter passend setzen und schon gibt es dein Problem nicht mehr.

von Noch einer (Gast)


Lesenswert?

Würde ich erst mal schauen, ob der jpnevulator die Parameter absichtlich 
verändert. Eingebauter Default, wenn du den Parameter weg lässt.

Wenn sich in Doku oder Quelltext nichts finden lässt, halt mit strace 
trace=ioctl

von Erwin M. (nobodyy)


Lesenswert?

Noch einer schrieb:
> Würde ich erst mal schauen, ob der jpnevulator die Parameter absichtlich
> verändert.

Da ist nichts, ebenso bei multithreaded_logger.
Deshalb ist es ja misteriös.
Das müssten andere doch auch beobachtet haben.

von Noch einer (Gast)


Lesenswert?

Lange Nachtschicht...

Alles mal durchprobieren, was strace beim logger ausgibt.
So was in der Art.
1
#include <fcntl.h>
2
main(int argc, char*argv[]) {
3
  system("stty -a -F /dev/ttyACM0");
4
  fopen("/dev/ttyACM0", "r");
5
  open("/dev/ttyACM0", O_RDONLY);
6
  system("stty -a -F /dev/ttyACM0");
7
}

von asdfasd (Gast)


Lesenswert?

Aus dem Source von jpnevaluator:
1
static int ttyOpen(char *name,int length) {
2
        int fd;
3
        struct termios options;
4
        fd=open(name,O_RDWR);
5
        if(fd!=-1) {
6
                /* Make sure we send to a raw serial interface! */
7
                fcntl(fd,F_SETFL,0);
8
                tcgetattr(fd,&options);
9
                cfmakeraw(&options);
10
                tcsetattr(fd,TCSANOW,&options);
11
        }
12
        return(fd);
13
}

Der relevante Aufruf ist cfmakeraw (ne alte BSD-Funktion).  Aus dessen 
man-Page:
1
       cfmakeraw()  sets  the terminal to something like the "raw" mode of the
2
       old Version 7 terminal driver: input is available character by  charac‐
3
       ter,  echoing is disabled, and all special processing of terminal input
4
       and output characters is disabled.  The terminal attributes are set  as
5
       follows:
6
7
           termios_p->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
8
                           | INLCR | IGNCR | ICRNL | IXON);
9
           termios_p->c_oflag &= ~OPOST;
10
           termios_p->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
11
           termios_p->c_cflag &= ~(CSIZE | PARENB);
12
           termios_p->c_cflag |= CS8;

Hier geht also deine Parity flöten.  Das einfachste dürfte das Ändern 
vom Source sein.  Alterntiv über LD_PRELOAD ein eigenes cfmakeraw 
reinpatchen.

von Klaus (Gast)


Lesenswert?

Au Mist.
Ich hatte so ein Problem schon mal vor ein paar Jahren auf einem 
Suse-System beim sniffen auf der RS232.

Ich erinnere mich leider nicht mehr so recht, was die Ursache war.

Ich muss morgen mal schauen ob ich meine Notizen dazu noch finde. Sorry. 
Heute wird das vermutlich nichts mehr.

von Klaus (Gast)


Lesenswert?

Wenn ich mich jetzt so daran erinnere, fällt mir nur ein, dass ich 
ziemlich lange (so 2 Stunden oder so) die man page mit den Parametern 
gelesen habe und einige (hoffentlich) intelligente Veränderungen 
vorgenommen habe.

Tut mir leid. Mehr fällt mir gerade nicht ein.

von Klaus (Gast)


Lesenswert?

Ooops. asdfasd hat ja schon in diesem Sinne geantwortet. Sorry.

von Erwin M. (nobodyy)


Lesenswert?

asdfasd schrieb:
...
> Der relevante Aufruf ist cfmakeraw (ne alte BSD-Funktion).  Aus dessen
> man-Page:
...
> Hier geht also deine Parity flöten.  Das einfachste dürfte das Ändern
> vom Source sein.  Alterntiv über LD_PRELOAD ein eigenes cfmakeraw
> reinpatchen.

Aha, danke.
Der Autor von dem jpnevulator hat nie ein Problem gehabt, offenbar weil 
der immer ohne parenb gearbeitet hat.

Gegen den Workaround eine Sekunde nach dem Programmstart die Parameter 
erneut zu setzen spricht ja nichts, oder?

von Klaus (Gast)


Lesenswert?

Erwin M. schrieb:

> Gegen den Workaround eine Sekunde nach dem Programmstart die Parameter
> erneut zu setzen spricht ja nichts, oder?

"Nichts" würde ich nicht sagen. Die Vorgehensweise ist "unsauber", was 
hier ganz klar als "ein Parameter wird gesetzt und dann verändert" 
beschrieben werden kann. Du brätst ja auch kein Schnitzel um unmittelbar 
danach daraus eine Schwarzwälder Kirschtorte zu machen. :-)

Für eine einmalige Sache, würde ich das so lassen. Auf Dauer aber einen 
Sniffer nehmen, der solche "Hacks" überflüssig macht.

von asdfasd (Gast)


Lesenswert?

> Gegen den Workaround eine Sekunde nach dem Programmstart die Parameter
> erneut zu setzen spricht ja nichts, oder?

Die sinnvolle Methode wäre, den Source so zu ändern, dass nur auf 
raw-Modus umgeschaltet wird und die Leitungsparameter (c_cflag) 
unangetastet bleiben, und dann das als Patch zum Author schicken ...

von Konrad S. (maybee)


Lesenswert?

Erwin M. schrieb im Beitrag #4202778:
> sleep 1

Erwin M. schrieb im Beitrag #4203202:
> Gegen den Workaround eine Sekunde nach dem Programmstart die Parameter
> erneut zu setzen spricht ja nichts, oder?

"sleep" löst keine Probleme, es verzögert sie nur.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.