Index: README
===================================================================
--- README	(Revision 1765)
+++ README	(Arbeitskopie)
@@ -224,7 +224,10 @@ Konfiguration
       Yes/No
 
     improve-nmradcc-timing
-      Yes/No recommended: Yes
+      Yes/No/termios2 recommendation: see "man 5 srcpd.conf"
+      
+    nmradcc_baudrate
+      custom baudrate in case of improve-nmradcc-timing=termios2
 
     nmradcc-translation-routine
       1, 2 or 3 recommended: 3
Index: README.i2c-23017
===================================================================
--- README.i2c-23017	(nicht existent)
+++ README.i2c-23017	(Arbeitskopie)
@@ -0,0 +1,48 @@
+Author: Bernd Wagner
+Datum: 2019-2021
+Version: SRCP 2.1.5
+Licence: GPL v2 for the whole package 
+
+The i2c interface described here is a low-cost self-made interface for the
+control of accessories (GAs).
+
+Basic principle:
+
+
+               ------------          ----------
+              |            |        |    4*    |
+              | MCP23017   |        | ULN2803A |
+I2C-Bus ------| 16-Bit I/O |---16*--|Darlington|----- 16* --- accessories
+              | -Expander  |        |Transistor|              (Maerklin)    
+              |            |        |  Arrays  |
+               ------------          ---------- 
+
+This driver controls GAs (accessories) by the I/O ports of MCP23017
+16Bit-I/O-Expanders driven by the i2c-dev interface of the Linux kernel.
+In order to deliver enough current for the accessories, the outputs are boosted
+by ULN2803A Darlington Transistor Arrays (2 Darlington pairs in parallel for 
+each output).
+
+As this circuitry is DC only, The Maerklin accessories (originally built for AC)
+are DC driven. The ULN2803A provide internal free wheeling diodes for the
+inductive loads.
+
+The MCP23017 can be hardware-wired to have one of the I2C addresses 32, ..., 39.
+The MCP23017's I2C-address is its SRCP-GA address.
+The bits of MCP23017's GPIO Port A correspond to SRCP ports 0, ..., 7,
+the bits of GPIOB correspond to SRCP ports 8, ..., 15.
+
+You may find a more explicit circuit diagram in README_i2c-23017_circuit_diagram.jpeg.
+The original circuits are driven by the i2c bus of a Raspberry Pi.
+
+Warning:
+
+This wiring connects your Raspberry Pi/ your computer galvanically to your model train.
+If there are other such connections, e.g. by a booster, DC isolation may be needed
+(e.g. by opto-couplers), particularly if a DDL generated signal has a
+different Groud Level.
+
+Furthermore, bear in mind to use fuses to prevent for short circuits.
+
+This is a hobbyist hook-up, so all possible disclaimers apply.
+
Index: README.raspberrypi
===================================================================
--- README.raspberrypi	(nicht existent)
+++ README.raspberrypi	(Arbeitskopie)
@@ -0,0 +1,62 @@
+Author: Bernd Wagner
+Datum: 22.03.2021
+Version: SRCP 2.1.5
+Licence: GPL v2 for the whole package 
+
+The DDL driver of srcpd uses the transmit line of a RS232 interface of a PC to
+output the digital signal for model train control.
+
+Besides this transmit (and a receive) line, a RS232 has several control lines
+e.g. for handshaking during communication and these lines may be needed for
+communication with a booster.
+
+Raspberry Pi has UART interface(s) (i.e. transmit and receive), but only 
+limited control lines (CTS/RTS, but not DTR, DSR or RI). On a Raspberry Pi, 
+these control lines can be emulated by programming GPIOs.
+
+The files src/ddl_raspi.[hc] contain this programming for the control lines
+DTR, DSR, RI, RTS and CTS. The switch between PC specific functions and
+Raspberry Pi specific functions is done by the macro IOCTL defined in src/ddl.h.
+The activation of of the Raspberry Pi specific parts is done by the preprocessor
+variable RASPBERRYPI, which is set during ./configure when ./configure detects
+to be processed on a Raspberry Pi.
+
+For the GPIO programming the bcm2835 library from
+http://www.airspayce.com/mikem/bcm2835/ is used, which must be installed on the
+Raspberry Pi (and is requested by ./configure). This library uses the GPIO numbers
+of the BCM2835 SoC (_not_ the Raspberry's pin numbers). See
+https://elinux.org/RPi_BCM2835_GPIOs for a list.
+
+The configuration of the GPIO numbers used for the emulation of RS232 control
+lines is done in srcpd.conf - see manual page for a description.
+In my test environment, I used the following GPIO configuration
+on a Raspberry Pi 1:
+<rpi_rts>27</rpi_rts>
+<rpi_cts>18</rpi_cts>
+<rpi_dsr>17</rpi_dsr>
+<rpi_ri>22</rpi_ri>
+<rpi_dtr>4</rpi_dtr>
+Look at your Raspberry Pi's description to choose which GPIOs are free for 
+use in your case, and check at your booster which RS232 control lines are really used.
+DTR is a hot candidate to switch train power on and off.
+Look at the srcpd.conf manual page for information how RI and DSR should be
+used by srcpd if activated in the configuration and if the booster provides them.
+Furthermore, RI is used by srcpd for ACK detection on a NMRA programming track.
+
+Please respect that different electrical conditions may apply for Raspbery Pi
+(3,3 V) and booster (RS232 uses +- 12V). An example for interface hardware
+for an individual setup can be found at 
+http://siggsoftware.ch/wordpress/wp-content/uploads/2016/11/booster.pdf
+
+In case inversion of logics (invert HIGH and LOW) is needed, this can be done
+by hardware or by configuration in srcpd.conf (or one can be used to compensate
+the other).
+
+Furthermore optocouplers may be needed for potential separation between
+Raspberry PI and model train, particularly if there are other interfaces
+between them like i2c.
+
+This work is based on the srcpd fork to be found on
+http://siggsoftware.ch/wordpress/srcpd-mit-mfx-und-anderen-erweiterungen/
+but encapsulated to minimize changes in src/ddl.[ch]  and modified to
+allow flexible configuration in srcpd.conf.
Index: README_i2c-23017_circuit_diagram.jpeg
===================================================================
Kann nicht anzeigen: Dateityp ist als binär angegeben.
svn:mime-type = application/octet-stream
Index: README_i2c-23017_circuit_diagram.jpeg
===================================================================
--- README_i2c-23017_circuit_diagram.jpeg	(nicht existent)
+++ README_i2c-23017_circuit_diagram.jpeg	(Arbeitskopie)

Eigenschaftsänderungen: README_i2c-23017_circuit_diagram.jpeg
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: configure.ac
===================================================================
--- configure.ac	(Revision 1765)
+++ configure.ac	(Arbeitskopie)
@@ -161,10 +161,23 @@ if test "$with_LOCONET" = "yes"; then
         [AC_MSG_ERROR(getaddrinfo support missing)])
 fi
 
+dnl Option to disable I2C23017 support
+AC_ARG_ENABLE(i2c23017,
+    AC_HELP_STRING([--disable-i2c23017], [do not compile i2c-23017 module]),
+    [with_I2C23017="$enableval"], [with_I2C23017=yes])
+if test "$with_I2C23017" = "yes"; then
+   AC_DEFINE(USE_I2C23017,1, [define to 1 if you want i2c-23017 support])
+   NUM_MODULES=`expr $NUM_MODULES + 1`
+fi
+AM_CONDITIONAL(USE_I2C23017, test "$with_I2C23017" = yes)
 
 dnl Checks for host dependend libraries.
 case $host in
   *-*-linux*)
+    if test "$with_I2C23017" = "yes"; then
+      AC_CHECK_LIB(i2c, i2c_smbus_read_byte, , AC_MSG_WARN(library i2c is missing))
+    fi
+
     ;;
   *-*-cygwin*)
     if test "$with_DDLS88" = "yes"; then
@@ -177,6 +190,16 @@ case $host in
     AC_MSG_WARN(System $host not known.)
     ;;
 esac
+case $host in
+  arm*-*-linux*)
+    if [grep 'Raspberry' /proc/device-tree/model >/dev/null 2>&1]; then
+      AC_DEFINE([RASPBERRYPI], [1], [Raspberry PI found])
+      AC_CHECK_LIB(bcm2835, bcm2835_gpio_write, , AC_MSG_ERROR(lib bcm2835 is missing))
+    fi
+    ;;
+  *)
+    ;;
+esac
 
 
 dnl Checks for header files.
@@ -196,12 +219,22 @@ case $host in
           AC_CHECK_HEADERS(sys/io.h, ,
                        AC_MSG_ERROR(essential header file is missing))
     fi
+
     AC_CHECK_HEADERS(linux/serial.h, ,
                  AC_MSG_ERROR(essential header file is missing))
-    if test "$with_I2C" = "yes"; then
+
+    if test "$with_I2C" = "yes" || test "$with_I2C23017" = "yes"; then
     AC_CHECK_HEADERS(linux/i2c-dev.h, ,
                  AC_MSG_ERROR(essential header file is missing))
     fi
+
+    # for some older Raspbian, package libi2c-dev changes linux/i2c-dev.h and moves original one to i2c-dev.h.kernel
+    # for other Raspbians or Ubuntu, package libi2c-dev installs smbus.h
+    if test "$with_I2C23017" = "yes"; then
+      AC_CHECK_HEADERS(i2c/smbus.h, ,
+        AC_MSG_WARN(potentially essential header file i2c/smbus.h missing - \
+        please make sure that the package libi2c-dev is installed))
+    fi
     ;;
   *-*-cygwin*)
     AC_CHECK_HEADERS(sys/io.h, ,
@@ -323,6 +356,7 @@ if test "$with_M605X"     = "yes"; then
 if test "$with_SELECTRIX" = "yes"; then echo "    selectrix"; fi
 if test "$with_ZIMO"      = "yes"; then echo "    zimo"; fi
 if test "$with_DCCAR"     = "yes"; then echo "    dccar"; fi
+if test "$with_I2C23017"  = "yes"; then echo "    i2c-23017"; fi
 echo
 echo "Unsupported devices:"
 if test "$with_DDL"       != "yes"; then echo "    ddl"; fi
@@ -337,11 +371,12 @@ if test "$with_M605X"     != "yes"; then
 if test "$with_SELECTRIX" != "yes"; then echo "    selectrix"; fi
 if test "$with_ZIMO"      != "yes"; then echo "    zimo"; fi
 if test "$with_DCCAR"     != "yes"; then echo "    dccar"; fi
+if test "$with_I2C23017"  != "yes"; then echo "    i2c-23017"; fi
 
 echo
 if test $NUM_MODULES == 0; then
-    echo 'Warning: No device module configured; srcpd will not run propperly!'
+    echo 'Warning: No device module configured; srcpd will not run properly!'
 else
     echo "Run 'make' to continue."
 fi
-echo
+echo $host
Index: man/de/srcpd.conf.5
===================================================================
--- man/de/srcpd.conf.5	(Revision 1765)
+++ man/de/srcpd.conf.5	(Arbeitskopie)
@@ -448,16 +448,31 @@ CPU-Last ist das nicht benötigte Protok
 .\"
 .TP
 improve_nmradcc_timing
-Dieser Wert sollte im reinen NMRA-DCC-Betrieb auf
-.IR yes
-gesetzt werden.  In allen anderen Situation - also falls
-.IR enable_marklin
-auf
-.IR yes
-gesetzt ist - muss(!) dieser Wert auf
-.IR no
-gesetzt werden.  Der voreingestellte Wert ist
-.IR no .
+Gültige Werte sind \fByes\fR, \fBno\fR oder \fBtermios2\fR.
+Die voreingestellte Baudrate für DDL (Wert \fBno\fR) ist 19200\ baud, was 
+etwas mehr ist als
+vom NMRA DCC Standard erlaubt. Mit dem UART 16550A kann dieser Wert durch die
+Einstellung \fByes\fR auf
+16457\ baud gesetzt werden, was in der Spezifikation liegt. Dies ist nur bei
+Timing Problemen mit der Voreinstellung nötig.
+
+\fBtermios2\fR ist eine
+alternative Möglichkeit unter Linux, Nicht-Standard Baudraten zu setzen, wenn 
+die Hardware
+das unterstützt. Im Gegensatz zu \fByes\fR unterstützt diese Methode auch neuere
+Linux Kernels.
+Mit dieser Einstellung kann die Baudrate individuell mittels
+des Parameters nmradcc_baudrate gesetzt werden. Auch diese Einstellung sowie
+die sich daraus tatsächlich ergebende Baudrate sind Hardware-abhängig.
+(Auf einem Raspberry Pi mit baud base 3000000 können durch Teilung granularere
+Baudraten erzielt werden als mit der voreingestellten baud base 115200 des
+UART 16550A eines Standard PCs.) 
+.\"
+.TP
+nmradcc_baudrate
+Individuelle Baudrate für NMRA DCC im Falle improve_nmradcc_timing=\fBtermios2\fR. 
+Voreinstellung=17287.
+Bei einer Änderung der Voreinstellung den NMRA-Standard beachten!
 .\"
 .TP
 nmra_ga_offset
@@ -536,6 +551,36 @@ was bedeutet, dass Prpgrammierbefehle st
 weitergeleitet werden.
 .\"
 .TP
+rpi_dtr
+.TQ
+rpi_rts
+.TQ
+rpi_cts
+.TQ
+rpi_dsr
+.TQ
+rpi_ri
+.TQ
+.I
+(nur Raspberry Pi)
+Diese Parameter ermöglichen die Verwendung der GPIOs des Raspberry Pi für die
+RS232 Steuerleitungen wie in README.raspberrypi beschrieben (logisch - 
+Hardware-Anpassung erforderlich).
+Der angegebene Paramater wird als GPIO Nummer für die im Tag angegebene
+Steuerleitung verwendet, z.B.
+.br
+<rpi_dtr>4</rpi_dtr>
+.br
+gibt an, dass GPIO 4 für die DTR-Leitung verwendet werden soll (High = active).
+Addition von 1000 bzw. 2000 zum Parameter gibt an, dass der interne pulldown
+bzw. Pullup-Widerstand des GPIO aktiviert werden soll (für Eingänge).
+Ein Minuszeichen stellt auf inverse Logik um (Low = aktiv). So bedeutet
+.br
+<rpi_ri>-2022</rpi_ri>
+.br
+dass GPIO 22 für RI verwendet werden soll mit internem Pullup und inverser Logik.
+.\"
+.TP
 Beispiel Märklin/Motorola
 .nf
 <bus>
@@ -736,7 +781,7 @@ Beispiel
 .PP
 Bustreiber für das i2c-dev-Interface des Linux Kernels.  Er kann
 beispielsweise mit der unter http://www.matronix.de/ zu findenden
-Hardware genutzt werden.
+Hardware genutzt werden (auf Basis PCF8574 I/O Expander)).
 .TP
 multiplex_buses
 TODO
@@ -748,6 +793,44 @@ ga_reset_device
 TODO
 .\"
 .\"
+.SS i2c-23017
+.PP
+Dieser Treiber steuert GAs (Zubehör) mittels I/O ports von MCP23017
+16Bit-I/O-Expandern an einem i2c-dev interface des Linux Kernels.
+Um genug Strom zum Antrieb des Zubehörs zu liefern, werden die Ausgänge
+mit ULN2803A Darlington Transistor Arrays verstärkt(je 2 parallel).
+Das Märklin-Zubehör wird mit Gleichstrom betätigt.
+
+Die SRCP GA-Adresse eines MCP23017 ist seine I2C-Adresse
+(32 bis 39 sind möglich). Die Bits des GPIO Port A eines MCP23017
+entsprechen den SRCP Ports 0,...7, die Bits von GPIOB den SRCP Ports
+8, ..., 15.
+.TP
+ga_invert_outputs
+Boolean: alle Ausgänge invertieren.
+.TP
+number_ga
+maximale GA Addresse (Default=40, GA Adressen 32..39 möglich)
+.TP
+i2c_maxrepeat
+Maximale Anzahl der Schreibversuche auf den I2C bus im Fehlerfall. Dies kann mit "verbosity" Level 5 verfolgt werden.
+Voreinstellung 1000.
+.TP
+Beispiel
+.nf
+<bus>
+ 	<auto_power_on>yes</auto_power_on>
+ 	<verbosity>5</verbosity>
+	<device>/dev/i2c-1</device>
+	<i2c-23017>
+		<ga_invert_outputs>false</ga_invert_outputs>
+		<number_ga>40</number_ga>
+        <i2c_maxrepeat>1000</i2c_maxrepeat>
+	</i2c-23017>
+</bus>
+.fi
+.\"
+.\"
 .SS intellibox
 .\"
 .PP
Index: man/srcpd.conf.5
===================================================================
--- man/srcpd.conf.5	(Revision 1765)
+++ man/srcpd.conf.5	(Arbeitskopie)
@@ -369,13 +369,28 @@ disabled.
 .\"
 .TP
 improve_nmradcc_timing
-The default baudrate of DDL is 19200\ baud, which is slightly higher
-than allowed by NMRA DCC standard.  With a UART 16550A you can change the
-baudrate to  16457\ baud, which is within the specification.  This is
+Valid input values are \fByes\fR, \fBno\fR or \fBtermios2\fR.
+The default baudrate of DDL (setting \fBno\fR) is 19200\ baud, 
+which is slightly higher
+than allowed by NMRA DCC standard.  With a UART 16550A
+you can change the
+baudrate to  16457\ baud  by setting this parameter to \fByes\fR, so the
+baudrate is within the specification.  This is
 only needed if you have timing problems with the default.
-Valid input values are \fByes\fR or \fBno\fR.  The default value is
-\fBno\fR, which
-corresponds to 19200\ baud.
+
+\fBtermios2\fR is an alternative method in linux to set a non-standard
+baudrate if the hardware supports it. In contrast to \fByes\fR it supports
+newer linux kernels as well.
+With this method, the baudrate can be set by the nmradcc_baudrate parameter.
+This setting is hardware-dependent as well as the resulting real baudrate.
+(On a Raspberry Pi with baud base 3000000 more granular baudrates can be got
+by division than with the preset baud base 115200 of a
+standard PC's UART 16550A.)
+.\"
+.TP
+nmradcc_baudrate
+Custom baudrate for NMRA DCC in case of improve_nmradcc_timing=\fBtermios2\fR. Default=17287.
+Please observe the NMRA standard if you change the default setting!
 .\"
 .TP
 nmra_ga_offset
@@ -443,6 +458,35 @@ input values are \fByes\fR or \fBno\fR.
 i.e. all program track commands will be executed by default.
 .\"
 .TP
+rpi_dtr
+.TQ
+rpi_rts
+.TQ
+rpi_cts
+.TQ
+rpi_dsr
+.TQ
+rpi_ri
+.TQ
+.I
+(Raspberry Pi only)
+These parameters enable to use Raspberry Pi's GPIOs as RS232 control lines,
+as indicated in README.raspberrypi (logically - hardware adaption needed).
+The parameter given is taken as GPIO number used for the control line indicated
+by the tag, e.g.
+.br
+<rpi_dtr>4</rpi_dtr>
+.br
+indicates that GPIO 4 is to be taken for the DTR control line (High = active).
+Adding 1000 or 2000 to the parameter indicates activation of the GPIOs internal
+pulldown or pullup resistor respectively (for inputs). A minus indicates to use
+inverse logics (Low = active). So
+.br
+<rpi_ri>-2022</rpi_ri>
+.br
+means that GPIO 22 shall be used for RI with internal pullup and inverse logics.
+.\"
+.TP
 Example Maerklin/Motorola
 .nf
 <bus>
@@ -646,7 +690,7 @@ Example
 .SS i2c-dev
 .PP
 Bus driver for i2c-dev interface of the Linux kernel, can be used to
-access hardware found on http://www.matronix.de/.
+access hardware found on http://www.matronix.de/ (based on PCF8574 I/O Expander).
 .TP
 multiplex_buses
 TODO
@@ -658,6 +702,43 @@ ga_reset_device
 TODO
 .\"
 .\"
+.SS i2c-23017
+.PP
+This driver controls GAs (accessories) by the I/O ports of MCP23017
+16Bit-I/O-Expanders driven by the i2c-dev interface of the Linux kernel.
+In order to deliver enough current for the accessories, the outputs are boosted
+by ULN2803A Darlington Transistor Arrays (2 in parallel). The Maerklin
+accessories are DC driven.
+
+The SRCP-GA address of a MCP23017 is its I2C-address (32 to 39 are possible).
+The bits of MCP23017's GPIO Port A correspond to SRCP ports 0, ..., 7,
+the bits of GPIOB correspond to SRCP ports 8, ..., 15.
+.TP
+ga_invert_outputs
+Boolean: globally invert all outputs or not.
+.TP
+number_ga
+maximal GA address (default=40, GA addresses 32..39 are possible)
+.TP
+i2c_maxrepeat
+maximal number of trials to write to I2C bus in case of errors. This can be tracked by setting the verbosity to 5.
+Default 1000.
+.TP
+Example
+.nf
+<bus>
+ 	<auto_power_on>yes</auto_power_on>
+ 	<verbosity>5</verbosity>
+	<device>/dev/i2c-1</device>
+	<i2c-23017>
+		<ga_invert_outputs>false</ga_invert_outputs>
+		<number_ga>40</number_ga>
+        <i2c_maxrepeat>1000</i2c_maxrepeat>
+	</i2c-23017>
+</bus>
+.fi
+.\"
+.\"
 .SS intellibox
 .PP
 This driver supports the Intellibox device from Uhlenbrock (IB) connected
Index: src/Makefile.am
===================================================================
--- src/Makefile.am	(Revision 1765)
+++ src/Makefile.am	(Arbeitskopie)
@@ -35,7 +35,9 @@ if USE_DDL
 srcpd_SOURCES += \
 	ddl.c ddl.h \
 	ddl_maerklin.c ddl_maerklin.h \
-	ddl_nmra.c ddl_nmra.h
+	ddl_nmra.c ddl_nmra.h \
+	custom_termios2.c custom_termios2.h \
+	ddl_raspi.c ddl_raspi.h
 endif
 
 if USE_DDLS88
@@ -46,6 +48,10 @@ if USE_I2C
 srcpd_SOURCES += i2c-dev.c i2c-dev.h
 endif
 
+if USE_I2C23017
+srcpd_SOURCES += i2c-23017.c i2c-23017.h
+endif
+
 if USE_IB
 srcpd_SOURCES += ib.c ib.h
 endif
Index: src/config-srcpd.c
===================================================================
--- src/config-srcpd.c	(Revision 1765)
+++ src/config-srcpd.c	(Arbeitskopie)
@@ -39,6 +39,10 @@
 #include "i2c-dev.h"
 #endif
 
+#if defined USE_I2C23017
+#include "i2c-23017.h"
+#endif
+
 #ifdef USE_IB
 #include "ib.h"
 #endif
@@ -244,6 +248,20 @@ static bus_t register_bus(bus_t busnumbe
                        "Linux (yet).\n");
 #endif
 #else
+            syslog_bus(0, DBG_ERROR, DISABLE_MSG, child->name);
+#endif
+        }
+
+        else if (xmlStrcmp(child->name, BAD_CAST "i2c-23017") == 0) {
+#if defined USE_I2C23017
+
+#ifdef linux
+            busnumber += readconfig_I2C23017(doc, child, busnumber);
+#else
+            syslog_bus(0, DBG_ERROR, "Sorry, I2C-23017 is only available on "
+                       "Linux (yet).\n");
+#endif
+#else
             syslog_bus(0, DBG_ERROR, DISABLE_MSG, child->name);
 #endif
         }
Index: src/config-srcpd.h
===================================================================
--- src/config-srcpd.h	(Revision 1765)
+++ src/config-srcpd.h	(Arbeitskopie)
@@ -56,6 +56,7 @@ typedef unsigned long int sessionid_t;
 #define SERVER_SELECTRIX       11    /* Selectrix-server */
 #define SERVER_LOCONET         12    /* Loconet Gateway */
 #define SERVER_DCCAR           13    /* DC-Car Gateway */
+#define SERVER_I2C23017        14    /* I2C-23017-server */
 
 /* generic flags */
 #define USE_WATCHDOG          0x0001 /* use watchdog */
Index: src/custom_termios2.c
===================================================================
--- src/custom_termios2.c	(nicht existent)
+++ src/custom_termios2.c	(Arbeitskopie)
@@ -0,0 +1,57 @@
+/* from https://github.com/linux4sam/9bit */
+/*
+ * Copyright (C) 2019 Microchip Technology Inc.  All rights reserved.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * SPDX-License-Identifier: MIT
+ */
+#include "custom_termios2.h"
+#include <asm/ioctls.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+
+int tcgetattr2(int fd, struct termios2 *termios)
+{
+	return ioctl(fd, TCGETS2, termios);
+}
+
+int tcsetattr2(int fd, int optional_actions, const struct termios2 *termios)
+{
+	unsigned long int cmd;
+
+	switch (optional_actions)
+	{
+	case TCSANOW:
+		cmd = TCSETS2;
+		break;
+	case TCSADRAIN:
+		cmd = TCSETSW2;
+		break;
+	case TCSAFLUSH:
+		cmd = TCSETSF2;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return ioctl(fd, cmd, termios);
+}
Index: src/custom_termios2.h
===================================================================
--- src/custom_termios2.h	(nicht existent)
+++ src/custom_termios2.h	(Arbeitskopie)
@@ -0,0 +1,79 @@
+/* from https://github.com/linux4sam/9bit */
+/*
+ * Copyright (C) 2019 Microchip Technology Inc.  All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ * 
+ * SPDX-License-Identifier: MIT
+ */
+#ifndef CUSTOM_TERMIOS2_H
+#define CUSTOM_TERMIOS2_H
+
+/*
+ * struct termios2 provides 2 new members that allow us to set any baud rate we
+ * want (as long as the hardware supports it) outside of the predefined ones.
+ *
+ * If you just use struct termios2 to set the baud to something custom, and then
+ * turn around and use the standard struct termios and tcsetattr(), you'll lose
+ * the baud setting.  So, it makes sense to just use struct termios2 everywhere.
+ *
+ * Going straight for <asm/termios.h> causes conflicts with other necessary
+ * headers (like <termios.h>), so we are stuck trying to get to struct termios2
+ * with that header.
+ *
+ * The result is this header provides:
+ *  1. Bring struct termios2 definition here.
+ *  2. Define some helper functions tcgetattr2() and tcsetattr2() which are
+ *     equal to tcgetattr() and tcsetattr(), but for struct termios2.
+ */
+
+#include <termios.h>
+
+/*
+#if !defined(__arm__)
+#warning "Only tested on ARM arch.  This may or may not work."
+#endif
+*/
+/*
+ * Definition from linux/include/uapi/asm-generic/termbits.h
+ */
+
+#define _NCCS 19
+struct termios2 {
+	tcflag_t c_iflag;               /* input mode flags */
+	tcflag_t c_oflag;               /* output mode flags */
+	tcflag_t c_cflag;               /* control mode flags */
+	tcflag_t c_lflag;               /* local mode flags */
+	cc_t c_line;                    /* line discipline */
+	cc_t c_cc[_NCCS];               /* control characters */
+	speed_t c_ispeed;               /* input speed */
+	speed_t c_ospeed;               /* output speed */
+};
+
+#ifndef BOTHER
+#define BOTHER CBAUDEX
+#endif
+
+int tcgetattr2(int fd, struct termios2 *termios);
+
+int tcsetattr2(int fd, int optional_actions, const struct termios2 *termios);
+
+#endif
Index: src/ddl.c
===================================================================
--- src/ddl.c	(Revision 1765)
+++ src/ddl.c	(Arbeitskopie)
@@ -18,6 +18,8 @@
 /* | gpl-license@vogt-it.com so we can mail you a copy immediately.       | */
 /* +----------------------------------------------------------------------+ */
 
+/* 2021 changes by Bernd Wagner to support custom baudrates for NMRA DCC */
+
 /***************************************************************/
 /* erddcd - Electric Railroad Direct Digital Command Daemon    */
 /*    generates without any other hardware digital commands    */
@@ -52,6 +54,9 @@
 /* Berthold Benning: usleep patch for SuSE kernels             */
 /***************************************************************/
 
+/* 18.03.2021 Bernd Wagner
+    adaptions to include Raspberry Pi support */
+
 #include <sys/utsname.h>
 #include <stdbool.h>
 
@@ -59,7 +64,7 @@
 #include "ddl.h"
 #include "ddl_maerklin.h"
 #include "ddl_nmra.h"
-#include "io.h"
+/* #include "io.h" */
 #include "srcp-error.h"
 #include "srcp-fb.h"
 #include "srcp-ga.h"
@@ -69,6 +74,7 @@
 #include "srcp-power.h"
 #include "srcp-server.h"
 #include "syslogmessage.h"
+#include "ddl_raspi.h"
 
 #define __DDL ((DDL_DATA*)buses[busnumber].driverdata)
 #define __DDLt ((DDL_DATA*)buses[btd->bus].driverdata)
@@ -244,46 +250,78 @@ int setSerialMode(bus_t busnumber, int m
     switch (mode) {
         case SDM_MAERKLIN:
             if (__DDL->SERIAL_DEVICE_MODE != SDM_MAERKLIN) {
-                if (tcsetattr
-                    (buses[busnumber].device.file.fd, TCSANOW,
-                     &__DDL->maerklin_dev_termios) != 0) {
+                if (__DDL->IMPROVE_NMRADCC_TIMING != TERMIOS2_METHOD)
+                { /*IMPROVE_NMRADCC_TIMING in {0,1}*/
+                    if (tcsetattr(buses[busnumber].device.file.fd, TCSANOW,
+                                  &__DDL->maerklin_dev_termios) != 0)
+                    {
                     syslog_bus(busnumber, DBG_ERROR,
                                "Error setting serial device mode to Maerklin!");
                     return -1;
                 }
 #if linux
-                if (__DDL->IMPROVE_NMRADCC_TIMING) {
-                    if (set_customdivisor
-                        (buses[busnumber].device.file.fd,
-                         __DDL->serinfo_marklin) != 0) {
+                    if (__DDL->IMPROVE_NMRADCC_TIMING==DIVISOR_METHOD)
+                    { /*IMPROVE_NMRADCC_TIMING in {1}*/
+                        if (set_customdivisor(buses[busnumber].device.file.fd,
+                                              __DDL->serinfo_marklin) != 0)
+                        {
                         syslog_bus(busnumber, DBG_ERROR,
                                    "Cannot set custom divisor for maerklin of serial device!");
                         return -1;
                     }
+                    } /*IMPROVE_NMRADCC_TIMING in {1}*/
+#endif
+                } /*IMPROVE_NMRADCC_TIMING in {0,1}*/
+#if linux
+                else
+                { /*IMPROVE_NMRADCC_TIMING in {2}*/
+                    if (tcsetattr2(buses[busnumber].device.file.fd, TCSANOW,
+                                   &__DDL->maerklin_dev_termios2) != 0)
+                    {
+                        syslog_bus(busnumber, DBG_ERROR,
+                                   "Error setting serial device mode to Maerklin using termios2!");
+                        return -1;
                 }
+                } /*IMPROVE_NMRADCC_TIMING in {2}*/
 #endif
                 __DDL->SERIAL_DEVICE_MODE = SDM_MAERKLIN;
             }
             break;
         case SDM_NMRA:
             if (__DDL->SERIAL_DEVICE_MODE != SDM_NMRA) {
-                if (tcsetattr
-                    (buses[busnumber].device.file.fd, TCSANOW,
-                     &__DDL->nmra_dev_termios) != 0) {
+                if (__DDL->IMPROVE_NMRADCC_TIMING != TERMIOS2_METHOD)
+                { /*IMPROVE_NMRADCC_TIMING in {0,1}*/
+                    if (tcsetattr(buses[busnumber].device.file.fd, TCSANOW,
+                                  &__DDL->nmra_dev_termios) != 0)
+                    {
                     syslog_bus(busnumber, DBG_ERROR,
                                "Error setting serial device mode to NMRA!");
                     return -1;
                 }
 #if linux
-                if (__DDL->IMPROVE_NMRADCC_TIMING) {
-                    if (set_customdivisor
-                        (buses[busnumber].device.file.fd,
-                         __DDL->serinfo_nmradcc) != 0) {
+                    if (__DDL->IMPROVE_NMRADCC_TIMING==DIVISOR_METHOD)
+                    { /*IMPROVE_NMRADCC_TIMING in {1}*/
+                        if (set_customdivisor(buses[busnumber].device.file.fd,
+                                              __DDL->serinfo_nmradcc) != 0)
+                        {
                         syslog_bus(busnumber, DBG_ERROR,
                                    "Cannot set custom divisor for nmra dcc of serial device!");
                         return -1;
                     }
+                    } /*IMPROVE_NMRADCC_TIMING in {1}*/
+#endif
+                } /*IMPROVE_NMRADCC_TIMING in {0,1}*/  
+#if linux      
+                else
+                { /*IMPROVE_NMRADCC_TIMING in {2}*/
+                    if (tcsetattr2(buses[busnumber].device.file.fd, TCSANOW,
+                                   &__DDL->nmra_dev_termios2) != 0)
+                    {
+                        syslog_bus(busnumber, DBG_ERROR,
+                                   "Error setting serial device mode to NMRA using termios2 method!");
+                        return -1;
                 }
+                } /*IMPROVE_NMRADCC_TIMING in {2}*/
 #endif
                 __DDL->SERIAL_DEVICE_MODE = SDM_NMRA;
             }
@@ -316,7 +354,8 @@ int init_lineDDL(bus_t busnumber)
         exit(EXIT_FAILURE);
     }
 #if linux
-    if ((rc = reset_customdivisor(dev)) != 0) {
+    if ((__DDL->IMPROVE_NMRADCC_TIMING == DIVISOR_METHOD)&&((rc = reset_customdivisor(dev)) != 0)) {
+        /* reset customdivisor only when it was used B.W. */
         syslog_bus(busnumber, DBG_FATAL,
                    "Error initializing device %s (reset custom "
                    "divisor %d). Abort!",
@@ -341,8 +380,11 @@ int init_lineDDL(bus_t busnumber)
         exit(EXIT_FAILURE);
     }
 
+    if (__DDL->IMPROVE_NMRADCC_TIMING != TERMIOS2_METHOD) 
+    { /*IMPROVE_NMRADCC_TIMING in {0,1}*/
     result = tcgetattr(dev, &__DDL->maerklin_dev_termios);
-    if (result == -1) {
+        if (result == -1)
+        {
         syslog_bus(busnumber, DBG_FATAL,
                    "tcgetattr() failed: %s (errno = %d, device = %s).",
                    strerror(result), result,
@@ -351,7 +393,8 @@ int init_lineDDL(bus_t busnumber)
     }
 
     result = tcgetattr(dev, &__DDL->nmra_dev_termios);
-    if (result == -1) {
+        if (result == -1)
+        {
         syslog_bus(busnumber, DBG_FATAL,
                    "tcgetattr() failed: %s (errno = %d, device = %s).",
                    strerror(result), result,
@@ -377,8 +420,9 @@ int init_lineDDL(bus_t busnumber)
         ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
     __DDL->nmra_dev_termios.c_cflag &= ~(CSIZE | PARENB);
     __DDL->nmra_dev_termios.c_cflag |= CS8;     /* 8 data bits      */
-    if (__DDL->IMPROVE_NMRADCC_TIMING) {
-        /* IMPROVE_NMRADCC_TIMING
+        if (__DDL->IMPROVE_NMRADCC_TIMING==DIVISOR_METHOD)
+        {
+            /* IMPROVE_NMRADCC_TIMING == DIVISOR_METHOD
            With 19200 baud we are already outside of the specification of NMRA.
            19200 baud means we have pulses of 52 microseconds - the specification
            for generating DCC signals is in the range between 55 and 61
@@ -401,30 +445,85 @@ int init_lineDDL(bus_t busnumber)
          */
         cfsetospeed(&__DDL->nmra_dev_termios, B38400);  /* baud rate: 38400 */
         cfsetispeed(&__DDL->nmra_dev_termios, B38400);  /* baud rate: 38400 */
-    }
-    else {
+        } /* IMPROVE_NMRADCC_TIMING == DIVISOR_METHOD */
+        else
+        { /* IMPROVE_NMRADCC_TIMING == 0 */
         cfsetospeed(&__DDL->nmra_dev_termios, B19200);  /* baud rate: 19200 */
         cfsetispeed(&__DDL->nmra_dev_termios, B19200);  /* baud rate: 19200 */
-    }
+        } /* IMPROVE_NMRADCC_TIMING == 0 */
 
 #if linux
     /* if IMPROVE_NMRADCC_TIMING is set, we have to initialize some */
     /* structures */
-    if (__DDL->IMPROVE_NMRADCC_TIMING) {
-        if (init_serinfo(dev, 3, &__DDL->serinfo_marklin) != 0) {
+        if (__DDL->IMPROVE_NMRADCC_TIMING == DIVISOR_METHOD)
+        { /* IMPROVE_NMRADCC_TIMING == DIVISOR_METHOD */
+            if (init_serinfo(dev, 3, &__DDL->serinfo_marklin) != 0)
+            {
             syslog_bus(busnumber, DBG_FATAL,
                        "Error initializing device %s (init_serinfo mm). Abort!",
                        buses[busnumber].device.file.path);
             exit(EXIT_FAILURE);
         }
-        if (init_serinfo(dev, 7, &__DDL->serinfo_nmradcc) != 0) {
+            if (init_serinfo(dev, 7, &__DDL->serinfo_nmradcc) != 0)
+            {
             syslog_bus(busnumber, DBG_FATAL,
                        "Error initializing device %s (init_serinfo dcc). Abort!",
                        buses[busnumber].device.file.path);
             exit(EXIT_FAILURE);
         }
+        } /* IMPROVE_NMRADCC_TIMING == DIVISOR_METHOD */
+#endif
+    }  /*IMPROVE_NMRADCC_TIMING in {0,1}*/
+#if linux
+    else 
+    {    /* use termios2 method */
+        result = tcgetattr2(dev, &__DDL->maerklin_dev_termios2);
+        if (result == -1)
+        {
+            syslog_bus(busnumber, DBG_FATAL,
+                       "tcgetattr2() failed for maerklin: %s (errno = %d, device = %s).",
+                       strerror(result), result,
+                       buses[busnumber].device.file.path);
+            exit(EXIT_FAILURE);
     }
+
+        result = tcgetattr2(dev, &__DDL->nmra_dev_termios2);
+        if (result == -1)
+        {
+            syslog_bus(busnumber, DBG_FATAL,
+                       "tcgetattr2() failed for nmra: %s (errno = %d, device = %s).",
+                       strerror(result), result,
+                       buses[busnumber].device.file.path);
+            exit(EXIT_FAILURE);
+    }
+
+        /* init termios2 structure for Maerklin mode */
+        __DDL->maerklin_dev_termios2.c_lflag &=
+            ~(ECHO | ICANON | IEXTEN | ISIG);
+        __DDL->maerklin_dev_termios2.c_oflag &= ~(OPOST);
+        __DDL->maerklin_dev_termios2.c_iflag &=
+            ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
+        __DDL->maerklin_dev_termios2.c_cflag &= ~(CSIZE | PARENB);
+        __DDL->maerklin_dev_termios2.c_cflag |= CS6;        /* 6 data bits      */
+        __DDL->maerklin_dev_termios2.c_cflag &= ~CBAUD;
+        __DDL->maerklin_dev_termios2.c_cflag |= BOTHER;
+        __DDL->maerklin_dev_termios2.c_ospeed = 38400;  /* baud rate: 38400 */
+        __DDL->maerklin_dev_termios2.c_ispeed = 38400;  /* baud rate: 38400 */
+
+        /* init termios2 structure for NMRA mode */
+        __DDL->nmra_dev_termios2.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
+        __DDL->nmra_dev_termios2.c_oflag &= ~(OPOST);
+        __DDL->nmra_dev_termios2.c_iflag &=
+            ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
+        __DDL->nmra_dev_termios2.c_cflag &= ~(CSIZE | PARENB);
+        __DDL->nmra_dev_termios2.c_cflag |= CS8; /* 8 data bits      */
+        __DDL->nmra_dev_termios2.c_cflag &= ~CBAUD;
+        __DDL->nmra_dev_termios2.c_cflag |= BOTHER;
+        __DDL->nmra_dev_termios2.c_ospeed = __DDL->NMRADCC_BAUDRATE ; /* baud rate */
+        __DDL->nmra_dev_termios2.c_ispeed = __DDL->NMRADCC_BAUDRATE ; /* baud rate */
+    } /* use termios2 method */
 #endif
+
     buses[busnumber].device.file.fd = dev;      /* we need that value at the next step */
     /* setting serial device to default mode */
     if (!setSerialMode(busnumber, SDM_DEFAULT) == 0) {
@@ -433,7 +532,24 @@ int init_lineDDL(bus_t busnumber)
                    buses[busnumber].device.file.path);
         exit(EXIT_FAILURE);
     }
+#if linux
+    switch (__DDL->IMPROVE_NMRADCC_TIMING) {
+        case false:
+        case DIVISOR_METHOD: {
+            struct termios tio;
+            tcgetattr(buses[busnumber].device.file.fd, &tio);
+            syslog_bus(busnumber, DBG_DEBUG, "speed code reported from termios: %u", cfgetospeed(&tio));
+            }
+            break;
 
+        case TERMIOS2_METHOD: {
+            struct termios2 tio;
+            tcgetattr2(buses[busnumber].device.file.fd, &tio);
+            syslog_bus(busnumber, DBG_DEBUG, "baudrate reported by termios2: %u", tio.c_ospeed);
+            }
+            break;
+    }
+#endif
     return dev;
 }
 
@@ -709,8 +825,7 @@ static void waitUARTempty_COMMON_USLEEPP
 
     do {
 #if linux
-        result =
-            ioctl(buses[busnumber].device.file.fd, TIOCSERGETLSR, &value);
+        result = ioctl(buses[busnumber].device.file.fd, TIOCSERGETLSR, &value);
 #else
         result = ioctl(buses[busnumber].device.file.fd, TCSADRAIN, &value);
 #endif
@@ -760,7 +875,7 @@ static int checkRingIndicator(bus_t busn
 {
     int result, arg;
 
-    result = ioctl(buses[busnumber].device.file.fd, TIOCMGET, &arg);
+    result = IOCTL(buses[busnumber].device.file.fd, TIOCMGET, &arg);
     if (result >= 0) {
         if (arg & TIOCM_RI) {
             syslog_bus(busnumber, DBG_INFO,
@@ -783,7 +898,7 @@ static int checkShortcut(bus_t busnumber
     time_t short_now = 0;
     struct timeval tv_shortcut = { 0, 0 };
 
-    result = ioctl(buses[busnumber].device.file.fd, TIOCMGET, &arg);
+    result = IOCTL(buses[busnumber].device.file.fd, TIOCMGET, &arg);
     if (result >= 0) {
         if (((arg & TIOCM_DSR) && (!__DDL->DSR_INVERSE))
             || ((~arg & TIOCM_DSR) && (__DDL->DSR_INVERSE))) {
@@ -1021,7 +1136,7 @@ static void set_SerialLine(bus_t busnumb
 {
     int result, arg;
 
-    result = ioctl(buses[busnumber].device.file.fd, TIOCMGET, &arg);
+    result = IOCTL(buses[busnumber].device.file.fd, TIOCMGET, &arg);
     if (result == -1) {
         syslog_bus(busnumber, DBG_ERROR,
                    "ioctl() failed: %s (errno = %d). "
@@ -1061,7 +1176,7 @@ static void set_SerialLine(bus_t busnumb
                     arg |= TIOCM_CTS;
                 break;
         }
-        result = ioctl(buses[busnumber].device.file.fd, TIOCMSET, &arg);
+        result = IOCTL(buses[busnumber].device.file.fd, TIOCMSET, &arg);
         if (result == -1) {
             syslog_bus(busnumber, DBG_FATAL,
                        "ioctl() failed: %s (errno = %d).",
@@ -1523,6 +1638,7 @@ int readconfig_DDL(xmlDocPtr doc, xmlNod
     /*                             translation routine(1, 2 or 3) */
     __DDL->ENABLED_PROTOCOLS = (EP_MAERKLIN | EP_NMRADCC);      /* enabled p's */
     __DDL->IMPROVE_NMRADCC_TIMING = 0;  /* NMRA DCC: improve timing    */
+    __DDL->NMRADCC_BAUDRATE = 17287;  /* NMRA DCC: baudrate when using termios2 */
 
     __DDL->WAITUART_USLEEP_PATCH = true;        /* enable/disable usleep patch */
     __DDL->WAITUART_USLEEP_USEC = 100;  /* usecs for usleep patch      */
@@ -1531,6 +1647,13 @@ int readconfig_DDL(xmlDocPtr doc, xmlNod
 
     __DDL->SERIAL_DEVICE_MODE = SDM_NOTINITIALIZED;
 
+#ifdef RASPBERRYPI
+    for (int i=0; i < NO_OF_CONTROLLINES; i++) {
+        __DDL->rpi_GPIO_used[i] = false; 
+    }
+    int i;
+#endif
+
     xmlNodePtr child = node->children;
     xmlChar *txt = NULL;
 
@@ -1617,7 +1740,19 @@ int readconfig_DDL(xmlDocPtr doc, xmlNod
             if (txt != NULL) {
                 __DDL->IMPROVE_NMRADCC_TIMING = (xmlStrcmp(txt,
                                                            BAD_CAST "yes")
-                                                 == 0) ? true : false;
+                                                 == 0) ? DIVISOR_METHOD : 
+                        ((xmlStrcmp(txt, BAD_CAST "termios2") == 0) ? 
+                        TERMIOS2_METHOD : false);
+                xmlFree(txt);
+            }
+        }
+
+        else if (xmlStrcmp
+                 (child->name, BAD_CAST "nmradcc_baudrate")
+                 == 0) {
+            txt = xmlNodeListGetString(doc, child->xmlChildrenNode, 1);
+            if (txt != NULL) {
+                __DDL->NMRADCC_BAUDRATE = atoi((char *) txt);
                 xmlFree(txt);
             }
         }
@@ -1676,6 +1811,14 @@ int readconfig_DDL(xmlDocPtr doc, xmlNod
                 xmlFree(txt);
             }
         }
+
+#ifdef RASPBERRYPI
+        else if ((i=check_rpi_opts(child))<=NO_OF_CONTROLLINES) {
+            txt = xmlNodeListGetString(doc, child->xmlChildrenNode, 1);
+            interpret_rpi_parameter(i, txt, busnumber, __DDL);
+            xmlFree(txt);
+        }
+#endif
         else
             syslog_bus(busnumber, DBG_WARN,
                        "WARNING, unknown tag found: \"%s\"!\n",
@@ -1695,13 +1838,17 @@ int readconfig_DDL(xmlDocPtr doc, xmlNod
         syslog_bus(busnumber, DBG_ERROR,
                    "Can't create array for locomotives");
     }
-    if (__DDL->IMPROVE_NMRADCC_TIMING && __DDL->oslevel == 1) {
+    if ((__DDL->IMPROVE_NMRADCC_TIMING==DIVISOR_METHOD) && __DDL->oslevel == 1) {
         syslog_bus(busnumber, DBG_NONE,
                    "Improve nmra dcc timing causes changes on serial "
                    "lines custom divisor."
                    "This is deprecated on kernel 2.6 or later. You "
                    "better disable this feature in srcpd.conf");
     }
+    if (__DDL->ENABLED_PROTOCOLS==0){
+        syslog_bus(busnumber, DBG_ERROR, "No protocol enabled - "
+                    "please review .conf file");
+    }
 
     return (1);
 }
@@ -1714,11 +1861,21 @@ int init_bus_DDL(bus_t busnumber)
     static char protocols[3] = { '\0', '\0', '\0' };
     int protocol = 0;
 
+    if (__DDL->ENABLED_PROTOCOLS==0){
+                syslog_bus(busnumber, DBG_ERROR, "No protocol enabled - "
+                    "cannot initialize bus");
+                return 1;
+    }
+
     syslog_bus(busnumber, DBG_INFO, "DDL init with debug level %d",
                buses[busnumber].debuglevel);
 
     buses[busnumber].device.file.fd = init_lineDDL(busnumber);
 
+#ifdef RASPBERRYPI
+    init_raspi(busnumber, __DDL);
+#endif
+
     __DDL->short_detected = 0;
 
     __DDL->queue_initialized = false;
Index: src/ddl.h
===================================================================
--- src/ddl.h	(Revision 1765)
+++ src/ddl.h	(Arbeitskopie)
@@ -27,16 +27,27 @@
 #include <linux/serial.h>
 #endif
 
+#include "config.h"
+#ifdef RASPBERRYPI
+#define IOCTL(a, b, c) ioctl_raspi(a, b, c, busnumber, (DDL_DATA*)buses[busnumber].driverdata)
+#else
+#define IOCTL ioctl
+#endif
+
 #include <libxml/tree.h>        /*xmlDocPtr, xmlNodePtr */
 
 #include "config-srcpd.h"
 #include "netservice.h"
+#include "custom_termios2.h"
 
 #define QSIZE       2000
 /* large packet size reserved for F0-F28 packets */
 #define PKTSIZE     80
 #define MAXDATA     20
 
+#define DIVISOR_METHOD 1  /* custom divisor method for custom baudrate */
+#define TERMIOS2_METHOD 2  /* termios2 method for custom baudrate */
+
 typedef struct _tQData {
     int packet_type;
     int packet_size;
@@ -88,7 +99,8 @@ typedef struct _DDL_DATA {
     int NMRADCC_TR_V;           /* version of the nmra dcc      */
     /*                             translation routine(1,2 or 3) */
     int ENABLED_PROTOCOLS;      /* enabled p's                  */
-    int IMPROVE_NMRADCC_TIMING; /* NMRA DCC: improve timing     */
+    int IMPROVE_NMRADCC_TIMING; /* NMRA DCC: improve timing  0 = no, 1 = custom-divisor method, 2 = termios2 method */
+    int NMRADCC_BAUDRATE;       /* NMRA DCC: baudrate in case of IMPROVE_NMRADCC_TIMING==2 */
 
     bool WAITUART_USLEEP_PATCH;  /* enable/disbable usleep patch */
     int WAITUART_USLEEP_USEC;   /* usecs for usleep patch       */
@@ -99,9 +111,19 @@ typedef struct _DDL_DATA {
     struct termios nmra_dev_termios;
 
 #if linux
+    struct termios2 maerklin_dev_termios2;
+    struct termios2 nmra_dev_termios2;
     struct serial_struct *serinfo_marklin;
     struct serial_struct *serinfo_nmradcc;
 #endif
+#ifdef RASPBERRYPI
+#define NO_OF_CONTROLLINES 5  /* number of supported RS232 control lines, length of rpi_controllines[] */
+    int rpi_GPIO[NO_OF_CONTROLLINES];
+    bool rpi_GPIO_inverted[NO_OF_CONTROLLINES];
+    bool rpi_GPIO_pullup[NO_OF_CONTROLLINES];
+    bool rpi_GPIO_pulldown[NO_OF_CONTROLLINES];
+    bool rpi_GPIO_used[NO_OF_CONTROLLINES];
+#endif
     pthread_mutex_t queue_mutex;        /* mutex to synchronize queue inserts */
     int queue_initialized;
     int queue_out, queue_in;
Index: src/ddl_nmra.c
===================================================================
--- src/ddl_nmra.c	(Revision 1765)
+++ src/ddl_nmra.c	(Arbeitskopie)
@@ -268,6 +268,7 @@
 #include "ddl.h"
 #include "ddl_nmra.h"
 #include "syslogmessage.h"
+#include "ddl_raspi.h"
 
 
 typedef struct {
@@ -1312,7 +1313,7 @@ static void sm_init(bus_t busnumber)
 static int scanACK(bus_t busnumber)
 {
     int result, arg;
-    result = ioctl(buses[busnumber].device.file.fd, TIOCMGET, &arg);
+    result = IOCTL(buses[busnumber].device.file.fd, TIOCMGET, &arg);
     if (result == -1) {
         syslog_bus(busnumber, DBG_ERROR,
                    "ioctl() failed: %s (errno = %d)\n",
Index: src/ddl_raspi.c
===================================================================
--- src/ddl_raspi.c	(nicht existent)
+++ src/ddl_raspi.c	(Arbeitskopie)
@@ -0,0 +1,124 @@
+/*
+* This software is published under the terms of the GNU General Public
+* License, Version 2, 1991. (c) Bernd Wagner 18.03.2021.
+*/
+/* for further information see README.raspberrypi */
+
+#include <stdlib.h>
+#include <sys/ioctl.h>
+
+#include "ddl_raspi.h"
+
+#ifdef RASPBERRYPI
+
+#include "syslogmessage.h"
+
+const char rpi_controllines[NO_OF_CONTROLLINES][8] = {"rpi_dtr", "rpi_rts", "rpi_cts", "rpi_dsr", "rpi_ri"};
+const unsigned int TIOCM_bit[NO_OF_CONTROLLINES] =   {TIOCM_DTR, TIOCM_RTS, TIOCM_CTS, TIOCM_DSR, TIOCM_RI};
+const bool controllines_isoutput[NO_OF_CONTROLLINES] = {true,    true,      false,     false,     false}; /* direction of RS232 control lines */
+
+int ioctl_raspi (int fd, unsigned long int request, int* argptr, bus_t busnumber, DDL_DATA* ddlptr) 
+{
+    
+    switch (request) {
+
+        case TIOCMGET:
+
+            *argptr = 0;
+            for (int i=0; i<NO_OF_CONTROLLINES; i++ ){
+                *argptr |= (ddlptr->rpi_GPIO_used[i] ? 
+                    (ddlptr->rpi_GPIO_inverted[i] ? 
+                          (bcm2835_gpio_lev(ddlptr->rpi_GPIO[i]) == LOW ? TIOCM_bit[i] : 0)
+                        : (bcm2835_gpio_lev(ddlptr->rpi_GPIO[i]) == HIGH ? TIOCM_bit[i] : 0)
+                        )
+                    : 0);
+            }
+
+            syslog_bus(busnumber, DBG_DEBUG, "reading gpio: TIOCM modem bits = 0x%2x", *argptr);
+
+            break;
+
+        case TIOCMSET:
+
+            syslog_bus(busnumber, DBG_DEBUG, "write gpio: TIOCM modem bits = 0x%2x", *argptr);
+
+            for (int i=0; i<NO_OF_CONTROLLINES; i++ ){
+                if (ddlptr->rpi_GPIO_used[i]&controllines_isoutput[i]) {
+                    if (ddlptr->rpi_GPIO_inverted[i]) {
+                        bcm2835_gpio_write(ddlptr->rpi_GPIO[i], ((*argptr & TIOCM_bit[i]) != 0 ? LOW : HIGH));
+                    }
+                    else{
+                        bcm2835_gpio_write(ddlptr->rpi_GPIO[i], ((*argptr & TIOCM_bit[i]) != 0 ? HIGH : LOW));
+                    }
+                };
+
+            }
+            break;
+    }
+
+    return 0;
+}
+  
+void init_raspi (bus_t busnumber, DDL_DATA* ddlptr)
+ {
+    if (!bcm2835_init()) {
+        syslog_bus(0, DBG_ERROR, "Raspberry PI bcm2835 Lib Init Fail!\n");
+        exit(EXIT_FAILURE);
+    }
+    syslog_bus(busnumber, DBG_DEBUG, "init gpio");
+
+    //GPIO In-Output für Boosterstuerung setzen:
+    for (int i=0; i<NO_OF_CONTROLLINES; i++ ){
+                if (ddlptr->rpi_GPIO_used[i]) {
+                    bcm2835_gpio_fsel(ddlptr->rpi_GPIO[i], 
+                        (controllines_isoutput[i] ? 
+                            BCM2835_GPIO_FSEL_OUTP 
+                            : BCM2835_GPIO_FSEL_INPT)
+                    );
+                    bcm2835_gpio_set_pud(ddlptr->rpi_GPIO[i],
+                        (ddlptr->rpi_GPIO_pullup[i] ?
+                            BCM2835_GPIO_PUD_UP
+                            : (ddlptr->rpi_GPIO_pulldown[i] ?
+                                BCM2835_GPIO_PUD_DOWN
+                                : BCM2835_GPIO_PUD_OFF))
+                    );
+                }
+    }
+ }
+
+int check_rpi_opts(xmlNodePtr child) {
+    int i;
+    for (i=0; i < NO_OF_CONTROLLINES; i++){
+        if (xmlStrcmp(child->name, BAD_CAST rpi_controllines[i])== 0) {
+            break;
+            }
+        }
+    return i;
+}
+
+void interpret_rpi_parameter(int i, xmlChar *txt, bus_t busnumber, DDL_DATA* ddlptr){
+            if (txt != NULL) {
+                ddlptr->rpi_GPIO_used[i] = true;
+                int value = atoi((char *) txt);
+                bool val1 = (value < 0) ;
+                ddlptr->rpi_GPIO_inverted[i] = val1;
+                value = (val1 ? -value : value);
+                ddlptr->rpi_GPIO_pullup[i] = (value >=2000 );
+                value = (value >= 2000 ? value -2000 : value);
+                ddlptr->rpi_GPIO_pulldown[i] = (value >=1000 );
+                ddlptr->rpi_GPIO[i] = (value >= 1000 ? value -1000 : value);
+                syslog_bus(busnumber, DBG_DEBUG, 
+                    "RS232 line %s: Raspberry Pi GPIO %d, inverted: %d, pullup: %d, pulldown: %d", 
+                    rpi_controllines[i],
+                    ddlptr->rpi_GPIO[i], 
+                    (int) ddlptr->rpi_GPIO_inverted[i], 
+                    (int) ddlptr->rpi_GPIO_pullup[i],
+                    (int) ddlptr->rpi_GPIO_pulldown[i]);
+                if (ddlptr->rpi_GPIO[i]>53) {
+                    syslog_bus(busnumber, DBG_ERROR, "Raspberry Pi GPIO out of range");
+                }
+            }
+
+}
+
+#endif
\ Kein Zeilenumbruch am Dateiende.
Index: src/ddl_raspi.h
===================================================================
--- src/ddl_raspi.h	(nicht existent)
+++ src/ddl_raspi.h	(Arbeitskopie)
@@ -0,0 +1,26 @@
+/*
+* This software is published under the terms of the GNU General Public
+* License, Version 2, 1991. (c) Bernd Wagner 18.03.2021.
+*/
+/* for further information see README.raspberrypi */
+
+#include <stdbool.h>
+#include <libxml/tree.h>        /*xmlDocPtr, xmlNodePtr */
+
+#include "config.h"
+#include "config-srcpd.h"
+#include "ddl.h"
+
+#ifdef RASPBERRYPI
+
+#include <bcm2835.h>
+
+extern int ioctl_raspi (int fd, unsigned long int request, int* argptr, bus_t busnumber, DDL_DATA* ddlptr) ;
+
+extern void init_raspi (bus_t busnumber, DDL_DATA* ddlptr) ;
+
+extern int check_rpi_opts(xmlNodePtr child);
+
+extern void interpret_rpi_parameter(int i, xmlChar *txt, bus_t busnumber, DDL_DATA* ddlptr);
+
+#endif
Index: src/i2c-23017.c
===================================================================
--- src/i2c-23017.c	(nicht existent)
+++ src/i2c-23017.c	(Arbeitskopie)
@@ -0,0 +1,467 @@
+/***************************************************************************
+*                                                                         *
+*   This program is free software; you can redistribute it and/or modify  *
+*   it under the terms of the GNU General Public License as published by  *
+*   the Free Software Foundation; either version 2 of the License, or     *
+*   (at your option) any later version.                                   *
+*                                                                         *
+***************************************************************************/
+/**
+* i2c-23017: i2c bus driver for MCP23017.
+* This driver controls GAs (accessories) by the I/O ports of MCP23017  16Bit-I/O-Expanders 
+* driven by the i2c-dev interface of the Linux kernel. 
+* In order to deliver enough current for the accessories, the outputs are boosted by 
+* ULN2803A Darlington Transistor Arrays (2 in parallel).
+*
+* Measures are taken in case of disturbances on the I2C bus (repetitions in case of write errors, 
+* reinitializations of I2C devices).
+*
+* Copyright (c) 2019-2021 by Bernd Wagner <berwag@users.sourceforge.net>
+*
+**/
+#include "config.h"
+
+#include <errno.h>
+
+#include "config-srcpd.h"
+#include "i2c-23017.h"
+#include "toolbox.h"
+#include "srcp-ga.h"
+#include "srcp-power.h"
+#include "srcp-server.h"
+#include "srcp-info.h"
+#include "srcp-error.h"
+#include "syslogmessage.h"
+#ifdef HAVE_LINUX_I2C_DEV_H
+#include <linux/i2c-dev.h>
+#endif
+#ifdef HAVE_I2C_SMBUS_H
+#include <i2c/smbus.h>
+/*
+  Including libi2c-dev package will allow the following I2C SMBus functions
+  - i2c_smbus_access
+  - i2c_smbus_write_quick
+  - i2c_smbus_read_byte
+  - i2c_smbus_write_byte
+  - i2c_smbus_read_byte_data
+  - i2c_smbus_write_byte_data
+  - i2c_smbus_read_word_data
+  - i2c_smbus_write_word_data
+  - i2c_smbus_process_call
+  - i2c_smbus_read_block_data
+  - i2c_smbus_write_block_data
+  - i2c_smbus_read_i2c_block_data
+  - i2c_smbus_write_i2c_block_data
+  - i2c_smbus_block_process_call
+*/
+#endif
+#include <sys/ioctl.h>
+/*#include <sys/stat.h>*/
+#include <fcntl.h>
+#include <unistd.h>
+
+#define __i2c23017 ((I2C23017_DATA*)buses[busnumber].driverdata)
+#define __i2c23017t ((I2C23017_DATA*)buses[btd->bus].driverdata)
+
+int readconfig_I2C23017(xmlDocPtr doc, xmlNodePtr node, bus_t busnumber)
+{
+    syslog_bus(busnumber, DBG_INFO, "Reading configuration for bus '%s'",
+               node->name);
+
+    buses[busnumber].driverdata = malloc(sizeof(struct _I2C23017_DATA));
+
+    if (buses[busnumber].driverdata == NULL) {
+        syslog_bus(busnumber, DBG_ERROR,
+                   "Memory allocation error in module '%s'.", node->name);
+        return 0;
+    }
+
+    buses[busnumber].type = SERVER_I2C23017;
+    buses[busnumber].init_func = &init_bus_I2C23017;
+    buses[busnumber].thr_func = &thr_sendrec_I2C23017;
+    buses[busnumber].init_ga_func = &init_ga_I2C23017;
+    strcpy(buses[busnumber].description,
+           "GA POWER DESCRIPTION");
+
+    buses[busnumber].device.file.fd = 0;
+
+    __i2c23017->number_ga = I2CBUS_ADDRESS_BASE + MAX_23017_DEVICES; /* maximal possible GA address +1 */
+    __i2c23017->ga_invert_outputs = false;
+    __i2c23017->i2c_maxrepeat = 1000;
+
+    xmlNodePtr child = node->children;
+    xmlChar *txt = NULL;
+
+    while (child != NULL) {
+
+        if ((xmlStrncmp(child->name, BAD_CAST "text", 4) == 0) ||
+            (xmlStrncmp(child->name, BAD_CAST "comment", 7) == 0)) {
+            /* just do nothing, it is only formatting text or a comment */
+        }
+
+ /*       else if (xmlStrcmp(child->name, BAD_CAST "number_fb") == 0) {
+            txt = xmlNodeListGetString(doc, child->xmlChildrenNode, 1);
+            if (txt != NULL) {
+                __i2c23017->number_fb = atoi((char *) txt);
+                xmlFree(txt);
+            }
+        }
+*/
+        else if (xmlStrcmp(child->name, BAD_CAST "number_ga") == 0) {
+            txt = xmlNodeListGetString(doc, child->xmlChildrenNode, 1);
+            if (txt != NULL) {
+                __i2c23017->number_ga = atoi((char *) txt);
+                xmlFree(txt);
+            }
+        }
+        else if (xmlStrcmp(child->name, BAD_CAST "ga_invert_outputs") == 0) {
+            txt = xmlNodeListGetString(doc, child->xmlChildrenNode, 1);
+            if (txt != NULL) {
+                __i2c23017->ga_invert_outputs = atoi((char *) txt);
+                xmlFree(txt);
+            }
+        }
+        else if (xmlStrcmp(child->name, BAD_CAST "i2c_maxrepeat") == 0) {
+            txt = xmlNodeListGetString(doc, child->xmlChildrenNode, 1);
+            if (txt != NULL) {
+                __i2c23017->i2c_maxrepeat = atoi((char *) txt);
+                xmlFree(txt);
+            }
+        }
+        else
+            syslog_bus(busnumber, DBG_WARN,
+                       "WARNING, unknown tag found: \"%s\"!\n",
+                       child->name);;
+
+        child = child->next;
+    }
+
+    if (init_GA(busnumber, __i2c23017->number_ga)) {
+        __i2c23017->number_ga = 0;
+        syslog_bus(busnumber, DBG_ERROR,
+                   "Can't create array for accessoires");
+    }
+/*
+    if (init_FB(busnumber, __i2c23017->number_fb)) {
+        __i2c23017->number_fb = 0;
+        syslog_bus(busnumber, DBG_ERROR,
+                   "Can't create array for feedback");
+    }
+*/
+    if (__i2c23017->ga_invert_outputs) {
+    	__i2c23017->ga_hw_defaultbyte = (I2CBUS_VALUE)0xff;
+    }
+    else {
+    	__i2c23017->ga_hw_defaultbyte = (I2CBUS_VALUE)0x00;
+    }
+    return (1);
+}
+
+
+
+/**
+ * initGA: modifies the ga data used to initialize the device
+ **/
+int init_ga_I2C23017(ga_data_t * ga)
+{
+/*	syslog(LOG_INFO,
+        "found protocol %s for i2c-23017", ga->protocol);*/
+
+    if (ga->protocol == 'P') return SRCP_OK;
+    return SRCP_UNSUPPORTEDDEVICEPROTOCOL;
+}
+
+static int status_i2cdevice(bus_t busnumber, I2CBUS_ADDR i2caddress)
+{   int rc, gpioa, iodira, gpiob, iodirb;
+
+	rc = 0;
+
+    if (ioctl(buses[busnumber].device.file.fd, I2C_SLAVE, i2caddress) < 0)
+    	   syslog_bus(busnumber, DBG_INFO, "status_i2cdevice: ioctl() I2C_SLAVE failed at address 0x%x", i2caddress);
+    else
+    {
+        gpioa = i2c_smbus_read_byte_data(buses[busnumber].device.file.fd, I2C23017_GPIOA);
+        iodira = i2c_smbus_read_byte_data(buses[busnumber].device.file.fd, I2C23017_IODIRA);
+        gpiob = i2c_smbus_read_byte_data(buses[busnumber].device.file.fd, I2C23017_GPIOB);
+        iodirb = i2c_smbus_read_byte_data(buses[busnumber].device.file.fd, I2C23017_IODIRB);
+        syslog_bus(busnumber, DBG_DEBUG, "status_i2cdevice address=0x%x,  GPIOA=0x%x,  IODIRA=0x%x,  GPIOB=0x%x, IODIRB=0x%x\n",
+        		i2caddress, gpioa, iodira, gpiob, iodirb);
+        if ((iodira != 0) || (iodirb !=0))
+			{
+        	rc = 1;
+			}
+    }
+    return rc;
+}
+
+
+static int 	write_byte(bus_t busnumber, I2CBUS_ADDR address, I2CBUS_REGISTER reg, I2CBUS_VALUE value)
+{
+	int rc, i;
+
+    if (ioctl(buses[busnumber].device.file.fd, I2C_SLAVE, address) < 0) {
+     	   syslog_bus(busnumber, DBG_INFO, "write_byte: ioctl() I2C_SLAVE failed at address 0x%x", address);
+   	       return(1);
+   }
+
+	i=0;
+
+	do {
+		rc=i2c_smbus_write_byte_data(buses[busnumber].device.file.fd, reg, value);
+		i++;
+	}	while ( (rc < 0) && (i < __i2c23017->i2c_maxrepeat));
+
+	if ( i > 1 )
+		syslog_bus(busnumber, DBG_DEBUG, "%d write errors to i2c-device 0x%x", i, address);
+
+	if (rc < 0)
+	    {
+		syslog_bus(busnumber, DBG_ERROR, "failed writing to i2c-device 0x%x", address);
+	    }
+	return 0;
+}
+
+static int init_i2cdevice(bus_t busnumber, I2CBUS_ADDR address)
+{
+	int rc, dev;
+
+	syslog_bus(busnumber, DBG_INFO, "initializing i2c-device 0x%x", address);
+
+ 	rc = write_byte(busnumber, address, I2C23017_GPIOA, __i2c23017->ga_hw_defaultbyte); /* set to non-active state */
+	rc = write_byte(busnumber, address, I2C23017_IODIRA, 0x00);   /* set to output */
+	rc = write_byte(busnumber, address, I2C23017_GPIOB, __i2c23017->ga_hw_defaultbyte); /* set to non-active state */
+	rc = write_byte(busnumber, address, I2C23017_IODIRB, 0x00);   /* set to output */
+
+	return rc;
+
+}
+
+/* initialize bus
+ * called once
+ */
+int init_bus_I2C23017(bus_t busnumber)
+{
+    static char *protocols = "P";
+    I2CBUS_ADDR devices_found;
+    int dev, res;
+
+    buses[busnumber].protocols = protocols;
+
+    syslog_bus(busnumber, DBG_INFO,
+               "i2c-23017 start initialization (verbosity = %d).",
+               buses[busnumber].debuglevel);
+
+    if (buses[busnumber].device.file.fd == 0) {
+        if ((dev = open(buses[busnumber].device.file.path, O_RDWR)) < 0)
+    		    {
+    	        syslog_bus(busnumber, DBG_ERROR, "open() failed");
+    		    exit (1);
+    		    }
+    buses[busnumber].device.file.fd = dev;
+    }
+
+/* scan bus */
+    devices_found = 0;
+    for (I2CBUS_ADDR i2caddress = I2CBUS_ADDRESS_BASE; i2caddress < I2CBUS_ADDRESS_BASE + MAX_23017_DEVICES; i2caddress++)
+        {
+        if (ioctl(buses[busnumber].device.file.fd, I2C_SLAVE, i2caddress) < 0)
+        	   syslog_bus(busnumber, DBG_INFO, "init_bus_I2C23017: ioctl() I2C_SLAVE failed at address 0x%x", i2caddress);
+        else
+          {
+          /* test reading */
+          res = i2c_smbus_read_byte(buses[busnumber].device.file.fd);
+          if (res >= 0) {
+        	  __i2c23017->i2cdevices[devices_found]=i2caddress;
+         	  devices_found++;
+      	      syslog_bus(busnumber, DBG_INFO, "i2c chip found at: 0x%x, val = 0x%x\n", i2caddress, res);
+			  }
+          }
+        }
+
+    __i2c23017->i2cdevices[devices_found]=0;
+
+    for (I2CBUS_ADDR i = 0; __i2c23017->i2cdevices[i] > 0; i++)
+		{
+    	init_i2cdevice(busnumber, __i2c23017->i2cdevices[i]);
+		}
+
+    syslog_bus(busnumber, DBG_INFO, "i2c-23017 initialization done.");
+    return 0;
+}
+
+static int send_pulse(bus_t busnumber, I2CBUS_ADDR address, unsigned int port, unsigned int delay)
+{
+	int rc;
+	I2CBUS_REGISTER reg;
+	I2CBUS_VALUE val;
+
+	if ((port < 0) || ( port >15) )
+		{
+		syslog_bus(busnumber, DBG_ERROR, "invalid port %d", port);
+		return 1;
+		}
+
+	if (port <8 )
+		reg = I2C23017_GPIOA;
+	else
+		reg = I2C23017_GPIOB;
+
+	port = port % 8;
+
+	val = __i2c23017->ga_hw_defaultbyte ^ (1 << port)  ;
+
+	if (status_i2cdevice(busnumber, address)!=0)
+		init_i2cdevice(busnumber, address);    /* reinitialize i2c device */
+
+	rc = write_byte(busnumber, address, reg, val) ;
+
+	usleep(1000*delay);
+
+	rc = write_byte(busnumber, address, reg, __i2c23017->ga_hw_defaultbyte) ;
+
+}
+
+static void handle_power_command(bus_t bus)
+{
+    syslog_bus(bus, DBG_INFO, "handling power command for bus");
+
+    buses[bus].power_changed = 0;
+    char msg[110];
+
+    infoPower(bus, msg);
+    enqueueInfoMessage(msg);
+    buses[bus].watchdog++;
+}
+
+
+static void handle_ga_command(bus_t busnumber)
+{
+    ga_data_t gatmp;
+    int address, i;
+    struct timeval akt_time;
+
+    dequeueNextGA(busnumber, &gatmp);
+    address = gatmp.id;
+
+    syslog_bus(busnumber, DBG_INFO, "handling GA command address=0x%x, action=%d, port=%d, activetime=%d", address, gatmp.action, gatmp.port, gatmp.activetime);
+    send_pulse(busnumber, address, gatmp.port, gatmp.activetime);
+
+    buses[busnumber].watchdog++;
+}
+
+
+/*thread cleanup routine for this bus*/
+static void end_bus_thread(bus_thread_t * btd)
+{
+    int result;
+
+    syslog_bus(btd->bus, DBG_INFO, "i2c-23017 bus terminated.");
+
+    result = pthread_mutex_destroy(&buses[btd->bus].transmit_mutex);
+    if (result != 0) {
+        syslog_bus(btd->bus, DBG_WARN,
+                   "pthread_mutex_destroy() failed: %s (errno = %d).",
+                   strerror(result), result);
+    }
+
+    result = pthread_cond_destroy(&buses[btd->bus].transmit_cond);
+    if (result != 0) {
+        syslog_bus(btd->bus, DBG_WARN,
+                   "pthread_mutex_init() failed: %s (errno = %d).",
+                   strerror(result), result);
+    }
+
+    free(buses[btd->bus].driverdata);
+    free(btd);
+}
+
+/*main thread routine for this bus*/
+void *thr_sendrec_I2C23017(void *v)
+{
+    int addr, ctr;
+    struct timeval akt_time, cmp_time;
+    ga_data_t gatmp;
+    int last_cancel_state, last_cancel_type;
+
+    bus_thread_t *btd = (bus_thread_t *) malloc(sizeof(bus_thread_t));
+
+    if (btd == NULL)
+        pthread_exit((void *) 1);
+    btd->bus = (bus_t) v;
+    btd->fd = -1;
+
+    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &last_cancel_state);
+    pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &last_cancel_type);
+
+    /*register cleanup routine */
+    pthread_cleanup_push((void *) end_bus_thread, (void *) btd);
+
+    /* initialize tga-structure */
+    for (ctr = 0; ctr < 50; ctr++)
+        __i2c23017t->tga[ctr].id = 0;
+
+    syslog_bus(btd->bus, DBG_INFO, "i2c-23017 bus started (device = %s).",
+               buses[btd->bus].device.file.path);
+
+    /*enter endless loop to process work tasks */
+    while (true) {
+
+        buses[btd->bus].watchdog = 1;
+
+        /*POWER action arrived */
+        if (buses[btd->bus].power_changed == 1)
+            handle_power_command(btd->bus);
+
+        /* loop shortcut to prevent processing of GA, GL (and FB)
+         * without power on; arriving commands will flood the command
+         * queue */
+        if (buses[btd->bus].power_state == 0) {
+
+            /* wait 1 ms */
+            if (usleep(1000) == -1) {
+                syslog_bus(btd->bus, DBG_ERROR,
+                           "usleep() failed: %s (errno = %d)\n",
+                           strerror(errno), errno);
+            }
+            continue;
+        }
+
+
+        /* handle delayed switching of GAs (there is a better place) */
+        gettimeofday(&akt_time, NULL);
+        for (ctr = 0; ctr < 50; ctr++) {
+            if (__i2c23017t->tga[ctr].id) {
+                cmp_time = __i2c23017t->tga[ctr].t;
+
+                /* switch off time reached? */
+                if (cmpTime(&cmp_time, &akt_time)) {
+                    gatmp = __i2c23017t->tga[ctr];
+                    addr = gatmp.id;
+                    gatmp.action = 0;
+                    setGA(btd->bus, addr, gatmp);
+                    __i2c23017t->tga[ctr].id = 0;
+                }
+            }
+        }
+
+        /*GA action arrived */
+        if (!queue_GA_isempty(btd->bus))
+            handle_ga_command(btd->bus);
+
+        /*FB action arrived */
+        /* currently nothing to do here */
+        buses[btd->bus].watchdog++;
+
+        /* busy wait and continue loop */
+        /* wait 1 ms */
+        if (usleep(1000) == -1) {
+            syslog_bus(btd->bus, DBG_ERROR,
+                       "usleep() failed: %s (errno = %d)\n",
+                       strerror(errno), errno);
+        }
+    }
+
+    /*run the cleanup routine */
+    pthread_cleanup_pop(1);
+    return NULL;
+}
Index: src/i2c-23017.h
===================================================================
--- src/i2c-23017.h	(nicht existent)
+++ src/i2c-23017.h	(Arbeitskopie)
@@ -0,0 +1,61 @@
+/***************************************************************************
+*                                                                         *
+*   This program is free software; you can redistribute it and/or modify  *
+*   it under the terms of the GNU General Public License as published by  *
+*   the Free Software Foundation; either version 2 of the License, or     *
+*   (at your option) any later version.                                   *
+*                                                                         *
+***************************************************************************/
+
+/**
+* i2c-23017: i2c bus driver for MCP23017.
+* This driver controls GAs (accessories) by the I/O ports of MCP23017  16Bit-I/O-Expanders 
+* driven by the i2c-dev interface of the Linux kernel. 
+* In order to deliver enough current for the accessories, the outputs are boosted by 
+* ULN2803A Darlington Transistor Arrays (2 in parallel).
+*
+* Copyright  (c) 2019-2021 by Bernd Wagner <berwag@users.sourceforge.net>
+*
+**/
+#ifndef _I2C23017_H
+#define _I2C23017_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#define MAX_23017_DEVICES  8            /* MC23017 has 3 I2C-Address-Bits */
+#define I2CBUS_ADDRESS_BASE 0x20      /* MC23017 can have i2c addresses 0x20 .. 0x27 */
+
+typedef uint8_t  I2CBUS_VALUE;
+typedef uint8_t  I2CBUS_ADDR;
+typedef uint8_t  I2CBUS_REGISTER;
+
+#define I2C23017_IODIRA  0x00 /* IO direction register for GPA */
+#define I2C23017_IODIRB  0x01 /* IO direction register for GPB */
+#define I2C23017_OLATA  0x1A /* output latch register (GPA) */
+#define I2C23017_OLATB  0x15 /* output latch register (GPB) */
+#define I2C23017_GPIOA  0x12 /* IO register GPA */
+#define I2C23017_GPIOB  0x13 /* IO register (GPB) */
+
+
+#include <libxml/tree.h> /*xmlDocPtr, xmlNodePtr*/
+
+typedef struct _I2C23017_DATA {
+    unsigned int number_ga;
+    bool ga_invert_outputs; /* flag */
+    unsigned int i2c_maxrepeat;      /* max number of repetitions in case of write error */
+    ga_data_t tga[50];
+    I2CBUS_ADDR i2cdevices[MAX_23017_DEVICES+1] ;
+    I2CBUS_VALUE ga_hw_defaultbyte ; /* 0x00 or 0xff dependent of ga_invert_outputs */
+/*    I2CBUS_VALUE dataBuffer[MAX_23017_DEVICES*2]; //MCP23017 delivers 2 Byte */
+} I2C23017_DATA;
+
+
+int readconfig_I2C23017(xmlDocPtr doc, xmlNodePtr node, bus_t busnumber);
+int init_bus_I2C23017(bus_t );
+/* int init_gl_I2C23017(gl_data_t *);*/
+int init_ga_I2C23017(ga_data_t *);
+/* int getDescription_I2C23017(char *reply); */
+void* thr_sendrec_I2C23017(void *);
+
+#endif
