You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by cd...@apache.org on 2020/08/19 09:06:48 UTC

[plc4x] branch feature/plc4c created (now fdb2a29)

This is an automated email from the ASF dual-hosted git repository.

cdutz pushed a change to branch feature/plc4c
in repository https://gitbox.apache.org/repos/asf/plc4x.git.


      at fdb2a29  - Implemented most of the connection part of the S7 driver - Implemented a skeleton for the Modbus driver

This branch includes the following new commits:

     new fdb2a29  - Implemented most of the connection part of the S7 driver - Implemented a skeleton for the Modbus driver

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[plc4x] 01/01: - Implemented most of the connection part of the S7 driver - Implemented a skeleton for the Modbus driver

Posted by cd...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

cdutz pushed a commit to branch feature/plc4c
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit fdb2a298b54834f8ce69db3baa844451b7057560
Author: Christofer Dutz <ch...@c-ware.de>
AuthorDate: Wed Aug 19 11:06:40 2020 +0200

    - Implemented most of the connection part of the S7 driver
    - Implemented a skeleton for the Modbus driver
---
 sandbox/plc4c/api/include/plc4c/types.h            |   2 +
 .../drivers/modbus/include/plc4c/driver_modbus.h   |   6 +-
 sandbox/plc4c/drivers/modbus/src/driver_modbus.c   |  89 ++-
 sandbox/plc4c/drivers/s7/include/plc4c/driver_s7.h |  34 +
 sandbox/plc4c/drivers/s7/src/driver_s7.c           | 746 ++++++++++++++++++++-
 .../simulated/include/plc4c/driver_simulated.h     |   6 +-
 .../plc4c/drivers/simulated/src/driver_simulated.c |  13 +-
 sandbox/plc4c/examples/hello-world/CMakeLists.txt  |  11 +-
 .../plc4c/examples/hello-world/src/hello_world.c   |  38 +-
 sandbox/plc4c/spi/include/plc4c/spi/read_buffer.h  |   7 +
 .../plc4c/spi/include/plc4c/spi/types_private.h    |  43 +-
 sandbox/plc4c/spi/src/system.c                     |  24 +-
 .../plc4c/transports/dummy/src/transport_dummy.c   |   5 +
 sandbox/plc4c/transports/serial/CMakeLists.txt     |   4 +-
 .../plc4c/transports/serial/src/transport_serial.c |  23 +
 sandbox/plc4c/transports/tcp/CMakeLists.txt        |   4 +-
 sandbox/plc4c/transports/tcp/src/transport_tcp.c   |  23 +
 17 files changed, 1045 insertions(+), 33 deletions(-)

diff --git a/sandbox/plc4c/api/include/plc4c/types.h b/sandbox/plc4c/api/include/plc4c/types.h
index 7a0fd48..3d58cd0 100644
--- a/sandbox/plc4c/api/include/plc4c/types.h
+++ b/sandbox/plc4c/api/include/plc4c/types.h
@@ -48,6 +48,8 @@ typedef enum plc4c_return_code {
   NO_TRANSPORT_AVAILABLE,
   UNKNOWN_TRANSPORT,
 
+  ALREADY_CONNECTED,
+
   PARSE_ERROR,
 
   UNKNOWN_ERROR,
diff --git a/sandbox/plc4c/drivers/modbus/include/plc4c/driver_modbus.h b/sandbox/plc4c/drivers/modbus/include/plc4c/driver_modbus.h
index 5fa584c..32868c2 100644
--- a/sandbox/plc4c/drivers/modbus/include/plc4c/driver_modbus.h
+++ b/sandbox/plc4c/drivers/modbus/include/plc4c/driver_modbus.h
@@ -16,8 +16,8 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-#ifndef PLC4C_DRIVER_S7_H_
-#define PLC4C_DRIVER_S7_H_
+#ifndef PLC4C_DRIVER_MODBUS_H_
+#define PLC4C_DRIVER_MODBUS_H_
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -29,4 +29,4 @@ plc4c_driver *plc4c_driver_modbus_create();
 #ifdef __cplusplus
 }
 #endif
-#endif  // PLC4C_DRIVER_S7_H_
\ No newline at end of file
+#endif  // PLC4C_DRIVER_MODBUS_H_
\ No newline at end of file
diff --git a/sandbox/plc4c/drivers/modbus/src/driver_modbus.c b/sandbox/plc4c/drivers/modbus/src/driver_modbus.c
index 59e0f6e..cab2dcf 100644
--- a/sandbox/plc4c/drivers/modbus/src/driver_modbus.c
+++ b/sandbox/plc4c/drivers/modbus/src/driver_modbus.c
@@ -18,8 +18,93 @@
 */
 
 #include <plc4c/driver_modbus.h>
+#include <plc4c/plc4c.h>
 #include <plc4c/spi/types_private.h>
 #include <stdlib.h>
+#include <string.h>
+
+enum plc4c_driver_modbus_disconnect_states {
+  PLC4C_DRIVER_MODBUS_DISCONNECT_INIT,
+  PLC4C_DRIVER_MODBUS_DISCONNECT_WAIT_TASKS_FINISHED,
+  PLC4C_DRIVER_MODBUS_DISCONNECT_FINISHED
+};
+
+
+plc4c_return_code plc4c_driver_modbus_connect_machine_function(
+    plc4c_system_task *task) {
+  plc4c_connection *connection = task->context;
+  if (connection == NULL) {
+    return INTERNAL_ERROR;
+  }
+  if (plc4c_connection_get_connected(connection)) {
+    return ALREADY_CONNECTED;
+  }
+  plc4c_connection_set_connected(connection, true);
+  task->completed = true;
+  return OK;
+}
+
+plc4c_return_code plc4c_driver_modbus_disconnect_machine_function(
+    plc4c_system_task *task) {
+  plc4c_connection *connection = task->context;
+  if (connection == NULL) {
+    return INTERNAL_ERROR;
+  }
+
+  switch (task->state_id) {
+    case PLC4C_DRIVER_MODBUS_DISCONNECT_INIT: {
+      plc4c_connection_set_disconnect(connection, true);
+      task->state_id = PLC4C_DRIVER_MODBUS_DISCONNECT_WAIT_TASKS_FINISHED;
+      break;
+    }
+    case PLC4C_DRIVER_MODBUS_DISCONNECT_WAIT_TASKS_FINISHED: {
+      // The disconnect system-task also counts.
+      if (plc4c_connection_get_running_tasks_count(connection) == 1) {
+        plc4c_connection_set_connected(connection, false);
+        task->completed = true;
+        task->state_id = PLC4C_DRIVER_MODBUS_DISCONNECT_FINISHED;
+      }
+      break;
+    }
+    case PLC4C_DRIVER_MODBUS_DISCONNECT_FINISHED: {
+      // Do nothing
+      break;
+    }
+    default: {
+      return INTERNAL_ERROR;
+    }
+  }
+  return OK;
+}
+
+plc4c_return_code plc4c_driver_modbus_connect_function(
+    plc4c_connection *connection, plc4c_system_task **task) {
+  plc4c_system_task *new_task = malloc(sizeof(plc4c_system_task));
+  // There's nothing to do here, so no need for a state-machine.
+  new_task->state_id = -1;
+  new_task->state_machine_function =
+      &plc4c_driver_modbus_connect_machine_function;
+  new_task->completed = false;
+  new_task->context = connection;
+  new_task->connection = connection;
+  *task = new_task;
+  return OK;
+}
+
+plc4c_return_code plc4c_driver_modbus_disconnect_function(
+    plc4c_connection *connection, plc4c_system_task **task) {
+  plc4c_system_task *new_task = malloc(sizeof(plc4c_system_task));
+  new_task->state_id = PLC4C_DRIVER_MODBUS_DISCONNECT_INIT;
+  new_task->state_machine_function =
+      &plc4c_driver_modbus_disconnect_machine_function;
+  new_task->completed = false;
+  new_task->context = connection;
+  new_task->connection = connection;
+  *task = new_task;
+  return OK;
+}
+
+
 
 plc4c_driver *plc4c_driver_modbus_create() {
   plc4c_driver *driver = (plc4c_driver *)malloc(sizeof(plc4c_driver));
@@ -27,8 +112,8 @@ plc4c_driver *plc4c_driver_modbus_create() {
   driver->protocol_name = "Modbus";
   driver->default_transport_code = "tcp";
   driver->parse_address_function = NULL;
-  driver->connect_function = NULL;
-  driver->disconnect_function = NULL;
+  driver->connect_function = &plc4c_driver_modbus_connect_function;
+  driver->disconnect_function = &plc4c_driver_modbus_disconnect_function;
   driver->read_function = NULL;
   driver->write_function = NULL;
   driver->subscribe_function = NULL;
diff --git a/sandbox/plc4c/drivers/s7/include/plc4c/driver_s7.h b/sandbox/plc4c/drivers/s7/include/plc4c/driver_s7.h
index e4c32f3..947c0d8 100644
--- a/sandbox/plc4c/drivers/s7/include/plc4c/driver_s7.h
+++ b/sandbox/plc4c/drivers/s7/include/plc4c/driver_s7.h
@@ -23,6 +23,40 @@ extern "C" {
 #endif
 
 #include <plc4c/types.h>
+#include <stdint.h>
+
+enum plc4c_driver_s7_controller_type {
+  PLC4C_DRIVER_S7_CONTROLLER_TYPE_ANY = 0,
+  PLC4C_DRIVER_S7_CONTROLLER_TYPE_S7_300 = 1,
+  PLC4C_DRIVER_S7_CONTROLLER_TYPE_S7_400 = 2,
+  PLC4C_DRIVER_S7_CONTROLLER_TYPE_S7_1200 = 3,
+  PLC4C_DRIVER_S7_CONTROLLER_TYPE_S7_1500 = 4,
+  PLC4C_DRIVER_S7_CONTROLLER_TYPE_LOGO = 5
+};
+typedef enum plc4c_driver_s7_controller_type plc4c_driver_s7_controller_type;
+
+enum plc4c_driver_s7_device_group {
+  PLC4C_DRIVER_S7_DEVICE_GROUP_PG_OR_PC = 1,
+  PLC4C_DRIVER_S7_DEVICE_GROUP_OS = 2,
+  PLC4C_DRIVER_S7_DEVICE_GROUP_OTHERS = 3
+};
+typedef enum plc4c_driver_s7_device_group plc4c_driver_s7_device_group;
+
+struct plc4c_driver_s7_config {
+  uint8_t local_rack;
+  uint8_t local_slot;
+  uint8_t remote_rack;
+  uint8_t remote_slot;
+  uint16_t calling_tsap_id;
+  uint16_t called_tsap_id;
+  uint16_t cotp_tpdu_size;
+  uint16_t pdu_size;
+  uint8_t max_amq_caller;
+  uint8_t max_amq_callee;
+  plc4c_driver_s7_controller_type controller_type;
+};
+typedef struct plc4c_driver_s7_config plc4c_driver_s7_config;
+
 
 plc4c_driver *plc4c_driver_s7_create();
 
diff --git a/sandbox/plc4c/drivers/s7/src/driver_s7.c b/sandbox/plc4c/drivers/s7/src/driver_s7.c
index 9b3431c..80fad4a 100644
--- a/sandbox/plc4c/drivers/s7/src/driver_s7.c
+++ b/sandbox/plc4c/drivers/s7/src/driver_s7.c
@@ -18,8 +18,742 @@
 */
 
 #include <plc4c/driver_s7.h>
+#include <plc4c/plc4c.h>
 #include <plc4c/spi/types_private.h>
 #include <stdlib.h>
+#include <string.h>
+#include <tpkt_packet.h>
+#include <cotp_protocol_class.h>
+
+// State definitions
+enum plc4c_driver_s7_connect_states {
+  PLC4C_DRIVER_S7_CONNECT_INIT,
+  PLC4C_DRIVER_S7_CONNECT_SEND_COTP_CONNECT_REQUEST,
+  PLC4C_DRIVER_S7_CONNECT_RECEIVE_COTP_CONNECT_RESPONSE,
+  PLC4C_DRIVER_S7_CONNECT_SEND_S7_CONNECT_REQUEST,
+  PLC4C_DRIVER_S7_CONNECT_RECEIVE_S7_CONNECT_RESPONSE,
+  PLC4C_DRIVER_S7_CONNECT_SEND_S7_IDENTIFICATION_REQUEST,
+  PLC4C_DRIVER_S7_CONNECT_RECEIVE_S7_IDENTIFICATION_RESPONSE,
+  PLC4C_DRIVER_S7_CONNECT_FINISHED
+};
+
+enum plc4c_driver_s7_disconnect_states {
+  PLC4C_DRIVER_S7_DISCONNECT_INIT,
+  PLC4C_DRIVER_S7_DISCONNECT_WAIT_TASKS_FINISHED,
+  PLC4C_DRIVER_S7_DISCONNECT_FINISHED
+};
+
+enum plc4c_driver_s7_read_states {
+  PLC4C_DRIVER_S7_READ_INIT,
+  PLC4C_DRIVER_S7_READ_FINISHED
+};
+
+enum plc4c_driver_s7_write_states {
+  PLC4C_DRIVER_S7_WRITE_INIT,
+  PLC4C_DRIVER_S7_WRITE_FINISHED
+};
+
+int16_t plc4c_driver_s7_select_message_function(
+    uint8_t* buffer_data, uint16_t buffer_length) {
+  // If the packet doesn't start with 0x03, it's a corrupt package.
+  if(buffer_length >= 1) {
+    // The buffer seems to be corrupt, try to find a sequence of 0x03 0x00
+    // and return the negative number of bytes needed to find that or the
+    // number of bytes in the buffer so it will simply clean the buffer
+    // completely.
+    if(*buffer_data != 0x03) {
+      for(int i = 1; i < (buffer_length - 1); i++) {
+        buffer_data++;
+        if((*buffer_data == 0x03) && (*(buffer_data + 1) == 0x00)) {
+          // We've found a potential new packet start.
+          return -(i - 1);
+        }
+      }
+      // We didn't find a new start, delete the entire content except the last
+      // byte (as this could be the start of the next frame and we couldn't
+      // confirm this.
+      return -(buffer_length - 1);
+    }
+  }
+  // The length information is located in bytes 3 and 4
+  if(buffer_length >= 4) {
+    uint16_t packet_length =
+        (((uint16_t) *(buffer_data + (uint8_t) 2)) << (uint16_t) 8) |
+        ((uint16_t) *(buffer_data + 3));
+    if(buffer_length >= packet_length) {
+      return packet_length;
+    }
+    // 8192 is the maximum pdu size, so if the value is larger, the packet is
+    // probably corrupt.
+    if(packet_length > 8192) {
+      for(int i = 1; i < (buffer_length - 1); i++) {
+        buffer_data++;
+        if((*buffer_data == 0x03) && (*(buffer_data + 1) == 0x00)) {
+          // We've found a potential new packet start.
+          return -(i - 1);
+        }
+      }
+      return -(buffer_length - 1);
+    }
+  }
+  // In all other cases, we'll just have to wait for the next time.
+  return 0;
+}
+
+uint16_t plc4c_driver_s7_encode_tsap_id(
+    plc4c_driver_s7_device_group device_group, uint8_t rack, uint8_t slot) {
+  return (((uint16_t) device_group) << 8) |
+         (((uint16_t) rack & 0x000F) << 4) |
+         ((uint16_t) slot & 0x000F);
+}
+
+uint16_t plc4c_driver_s7_get_nearest_matching_tpdu_size(uint16_t pdu_size) {
+  // TODO: Implement ...
+  return 0;
+}
+
+plc4c_driver_s7_controller_type decode_controller_type(char* article_number) {
+  char* prefix = "6ES7 ";
+  // If this article-number doesn't start with this prefix, we can't decode it.
+  if(strncmp(prefix, article_number, strlen(prefix)) != 0) {
+    return PLC4C_DRIVER_S7_CONTROLLER_TYPE_ANY;
+  }
+  char model = *(article_number + 5);
+  switch (model) {
+    case 2:
+      return PLC4C_DRIVER_S7_CONTROLLER_TYPE_S7_1200;
+    case 5:
+      return PLC4C_DRIVER_S7_CONTROLLER_TYPE_S7_1500;
+    case 3:
+      return PLC4C_DRIVER_S7_CONTROLLER_TYPE_S7_300;
+    case 4:
+      return PLC4C_DRIVER_S7_CONTROLLER_TYPE_S7_400;
+    default:
+      return PLC4C_DRIVER_S7_CONTROLLER_TYPE_ANY;
+  }
+}
+
+/**
+ * State machine function for establishing a connection to a remote S7 device.
+ * @param task the current system task
+ * @return return code of the current state machine step execution
+ */
+plc4c_return_code plc4c_driver_s7_connect_machine_function(
+    plc4c_system_task *task) {
+  plc4c_connection *connection = task->context;
+  if (connection == NULL) {
+    return INTERNAL_ERROR;
+  }
+  // If we were already connected, return an error
+  if (plc4c_connection_get_connected(connection)) {
+    return ALREADY_CONNECTED;
+  }
+  plc4c_driver_s7_config* configuration = connection->configuration;
+
+  switch (task->state_id) {
+    // Initialize some internal data-structures.
+    case PLC4C_DRIVER_S7_CONNECT_INIT: {
+      configuration->calling_tsap_id = plc4c_driver_s7_encode_tsap_id(
+          PLC4C_DRIVER_S7_DEVICE_GROUP_OTHERS,
+          configuration->local_rack, configuration->local_slot);
+      configuration->called_tsap_id = plc4c_driver_s7_encode_tsap_id(
+          PLC4C_DRIVER_S7_DEVICE_GROUP_PG_OR_PC,
+          configuration->remote_rack, configuration->remote_slot);
+      configuration->cotp_tpdu_size =
+          plc4c_driver_s7_get_nearest_matching_tpdu_size(configuration->pdu_size);
+      configuration->pdu_size = configuration->cotp_tpdu_size - 16;
+      task->state_id = PLC4C_DRIVER_S7_CONNECT_SEND_COTP_CONNECT_REQUEST;
+      break;
+    }
+    // Send a COTP connection request.
+    case PLC4C_DRIVER_S7_CONNECT_SEND_COTP_CONNECT_REQUEST: {
+      plc4c_s7_read_write_tpkt_packet* cotp_connect_request_packet =
+          malloc(sizeof(plc4c_s7_read_write_tpkt_packet));
+      if(cotp_connect_request_packet == NULL) {
+        return NO_MEMORY;
+      }
+      cotp_connect_request_packet->payload = malloc(sizeof(plc4c_s7_read_write_cotp_packet));
+      if(cotp_connect_request_packet->payload == NULL) {
+        return NO_MEMORY;
+      }
+      cotp_connect_request_packet->payload->_type =
+          plc4c_s7_read_write_cotp_packet_type_plc4c_s7_read_write_cotp_packet_connection_request;
+      cotp_connect_request_packet->payload->cotp_packet_connection_request_destination_reference =
+          0x0000;
+      cotp_connect_request_packet->payload->cotp_packet_connection_request_source_reference =
+          0x000F;
+      cotp_connect_request_packet->payload->cotp_packet_connection_request_protocol_class =
+          plc4c_s7_read_write_cotp_protocol_class_CLASS_0;
+
+      // Add the COTP parameters: Called TSAP, Calling TSAP and TPDU Size.
+      plc4c_utils_list_create(&(cotp_connect_request_packet->payload->parameters));
+      plc4c_s7_read_write_cotp_parameter* called_tsap_parameter =
+          malloc(sizeof(plc4c_s7_read_write_cotp_parameter));
+      if(called_tsap_parameter == NULL) {
+        return NO_MEMORY;
+      }
+      called_tsap_parameter->_type =
+          plc4c_s7_read_write_cotp_parameter_type_plc4c_s7_read_write_cotp_parameter_called_tsap;
+      called_tsap_parameter->cotp_parameter_called_tsap_tsap_id =
+          configuration->called_tsap_id;
+
+      plc4c_utils_list_insert_head_value(
+          cotp_connect_request_packet->payload->parameters, called_tsap_parameter);
+      plc4c_s7_read_write_cotp_parameter* calling_tsap_parameter =
+          malloc(sizeof(plc4c_s7_read_write_cotp_parameter));
+      if(calling_tsap_parameter == NULL) {
+        return NO_MEMORY;
+      }
+      calling_tsap_parameter->_type =
+          plc4c_s7_read_write_cotp_parameter_type_plc4c_s7_read_write_cotp_parameter_calling_tsap;
+      calling_tsap_parameter->cotp_parameter_calling_tsap_tsap_id =
+          configuration->calling_tsap_id;
+
+      plc4c_utils_list_insert_head_value(
+          cotp_connect_request_packet->payload->parameters, calling_tsap_parameter);
+      plc4c_s7_read_write_cotp_parameter* tpdu_size_parameter =
+          malloc(sizeof(plc4c_s7_read_write_cotp_parameter));
+      if(tpdu_size_parameter == NULL) {
+        return NO_MEMORY;
+      }
+      tpdu_size_parameter->_type =
+          plc4c_s7_read_write_cotp_parameter_type_plc4c_s7_read_write_cotp_parameter_tpdu_size;
+      tpdu_size_parameter->cotp_parameter_tpdu_size_tpdu_size =
+          configuration->cotp_tpdu_size;
+
+      plc4c_utils_list_insert_head_value(
+          cotp_connect_request_packet->payload->parameters, tpdu_size_parameter);
+
+      // For a COTP connection request, there is no payload.
+      cotp_connect_request_packet->payload->payload = NULL;
+
+      // Get the size required to contain the serialized form of this packet.
+      uint16_t packet_size =
+          plc4c_s7_read_write_tpkt_packet_length_in_bytes(
+          cotp_connect_request_packet);
+
+      // Serialize this message to a byte-array.
+      plc4c_spi_write_buffer* write_buffer;
+      plc4c_return_code return_code =
+          plc4c_spi_write_buffer_create(packet_size, &write_buffer);
+      if (return_code != OK) {
+        return INTERNAL_ERROR;
+      }
+      plc4c_s7_read_write_tpkt_packet_serialize(write_buffer, cotp_connect_request_packet);
+
+      // Now send this to the recipient.
+      return_code = connection->transport->send_message(write_buffer);
+      if (return_code != OK) {
+        return INTERNAL_ERROR;
+      }
+
+      task->state_id = PLC4C_DRIVER_S7_CONNECT_RECEIVE_COTP_CONNECT_RESPONSE;
+      break;
+    }
+    // Receive a tpkt connection response.
+    case PLC4C_DRIVER_S7_CONNECT_RECEIVE_COTP_CONNECT_RESPONSE: {
+      // Get a response from the transport.
+      plc4c_spi_read_buffer* read_buffer;
+      plc4c_return_code return_code = connection->transport->select_message(
+          plc4c_driver_s7_select_message_function, &read_buffer);
+      if (return_code != OK) {
+        // If UNFINISHED was returned, the buffer hasn't received enough
+        // data for a full packet. Just return and come back later.
+        if(return_code == UNFINISHED) {
+          return OK;
+        }
+        return INTERNAL_ERROR;
+      }
+
+      // Parse the given data.
+      plc4c_s7_read_write_tpkt_packet* cotp_connect_response_packet = NULL;
+      return_code = plc4c_s7_read_write_tpkt_packet_parse(
+          read_buffer, &cotp_connect_response_packet);
+      if (return_code != OK) {
+        return INTERNAL_ERROR;
+      }
+
+      // Check if the packet has the right type
+      if(cotp_connect_response_packet->payload->_type !=
+          plc4c_s7_read_write_cotp_packet_type_plc4c_s7_read_write_cotp_packet_connection_response) {
+        return INTERNAL_ERROR;
+      }
+
+      // Extract the information for: called-tsap-id, calling-tsap-id and tpdu-size.
+      plc4c_list_element* parameter_element =
+          cotp_connect_response_packet->payload->parameters->tail;
+      do {
+        plc4c_s7_read_write_cotp_parameter* parameter = parameter_element->value;
+        switch (parameter->_type) {
+          case plc4c_s7_read_write_cotp_parameter_type_plc4c_s7_read_write_cotp_parameter_tpdu_size: {
+            configuration->cotp_tpdu_size =
+                parameter->cotp_parameter_tpdu_size_tpdu_size;
+            break;
+          }
+          case plc4c_s7_read_write_cotp_parameter_type_plc4c_s7_read_write_cotp_parameter_calling_tsap: {
+            configuration->calling_tsap_id =
+                parameter->cotp_parameter_calling_tsap_tsap_id;
+            break;
+          }
+          case plc4c_s7_read_write_cotp_parameter_type_plc4c_s7_read_write_cotp_parameter_called_tsap: {
+            configuration->called_tsap_id =
+                parameter->cotp_parameter_called_tsap_tsap_id;
+            break;
+          }
+          default: {
+            break;
+          }
+        }
+      } while (parameter_element != NULL);
+
+      // If we got the expected response, continue with the next higher level
+      // of connection.
+      task->state_id = PLC4C_DRIVER_S7_CONNECT_SEND_S7_CONNECT_REQUEST;
+      break;
+    }
+    // Send a S7 connection request.
+    case PLC4C_DRIVER_S7_CONNECT_SEND_S7_CONNECT_REQUEST: {
+      plc4c_s7_read_write_tpkt_packet* s7_connect_request_packet =
+          malloc(sizeof(plc4c_s7_read_write_tpkt_packet));
+      if(s7_connect_request_packet == NULL) {
+        return NO_MEMORY;
+      }
+      s7_connect_request_packet->payload =
+          malloc(sizeof(plc4c_s7_read_write_cotp_packet));
+      if(s7_connect_request_packet->payload == NULL) {
+        return NO_MEMORY;
+      }
+      s7_connect_request_packet->payload->_type =
+          plc4c_s7_read_write_cotp_packet_type_plc4c_s7_read_write_cotp_packet_data;
+      s7_connect_request_packet->payload->parameters = NULL;
+      s7_connect_request_packet->payload->cotp_packet_data_eot = true;
+      s7_connect_request_packet->payload->cotp_packet_data_tpdu_ref = 1;
+
+      s7_connect_request_packet->payload->payload =
+          malloc(sizeof(plc4c_s7_read_write_s7_message));
+      if(s7_connect_request_packet->payload->payload == NULL) {
+        return NO_MEMORY;
+      }
+      s7_connect_request_packet->payload->payload->_type =
+          plc4c_s7_read_write_s7_message_type_plc4c_s7_read_write_s7_message_request;
+      s7_connect_request_packet->payload->payload->parameter =
+          malloc(sizeof(plc4c_s7_read_write_s7_parameter));
+      if(s7_connect_request_packet->payload->payload->parameter == NULL) {
+        return NO_MEMORY;
+      }
+      s7_connect_request_packet->payload->payload->parameter->_type =
+          plc4c_s7_read_write_s7_parameter_type_plc4c_s7_read_write_s7_parameter_setup_communication;
+      s7_connect_request_packet->payload->payload->parameter->s7_parameter_setup_communication_max_amq_callee =
+          configuration->max_amq_callee;
+      s7_connect_request_packet->payload->payload->parameter->s7_parameter_setup_communication_max_amq_caller =
+          configuration->max_amq_caller;
+      s7_connect_request_packet->payload->payload->parameter->s7_parameter_setup_communication_pdu_length =
+          configuration->pdu_size;
+
+      // Get the size required to contain the serialized form of this packet.
+      uint16_t packet_size =
+          plc4c_s7_read_write_tpkt_packet_length_in_bytes(
+              s7_connect_request_packet);
+
+      // Serialize this message to a byte-array.
+      plc4c_spi_write_buffer* write_buffer;
+      plc4c_return_code return_code =
+          plc4c_spi_write_buffer_create(packet_size, &write_buffer);
+      if (return_code != OK) {
+        return INTERNAL_ERROR;
+      }
+      plc4c_s7_read_write_tpkt_packet_serialize(write_buffer, s7_connect_request_packet);
+
+      // Now send this to the recipient.
+      return_code = connection->transport->send_message(write_buffer);
+      if (return_code != OK) {
+        return INTERNAL_ERROR;
+      }
+
+      task->state_id = PLC4C_DRIVER_S7_CONNECT_RECEIVE_S7_CONNECT_RESPONSE;
+      break;
+    }
+    // Receive a S7 connection response.
+    case PLC4C_DRIVER_S7_CONNECT_RECEIVE_S7_CONNECT_RESPONSE: {
+      // Get a response from the transport.
+      plc4c_spi_read_buffer* read_buffer;
+      plc4c_return_code return_code = connection->transport->select_message(
+          plc4c_driver_s7_select_message_function, &read_buffer);
+      if (return_code != OK) {
+        // If UNFINISHED was returned, the buffer hasn't received enough
+        // data for a full packet. Just return and come back later.
+        if(return_code == UNFINISHED) {
+          return OK;
+        }
+        return INTERNAL_ERROR;
+      }
+
+      // Parse the given data.
+      plc4c_s7_read_write_tpkt_packet* cotp_connect_response_packet = NULL;
+      return_code = plc4c_s7_read_write_tpkt_packet_parse(
+          read_buffer, &cotp_connect_response_packet);
+      if (return_code != OK) {
+        return INTERNAL_ERROR;
+      }
+
+      // Check if the packet has the right type
+      if(cotp_connect_response_packet->payload->_type !=
+          plc4c_s7_read_write_cotp_packet_type_plc4c_s7_read_write_cotp_packet_data) {
+        return INTERNAL_ERROR;
+      }
+      if(cotp_connect_response_packet->payload->payload->_type !=
+          plc4c_s7_read_write_s7_message_type_plc4c_s7_read_write_s7_message_response) {
+        return INTERNAL_ERROR;
+      }
+      if(cotp_connect_response_packet->payload->payload->parameter->_type !=
+          plc4c_s7_read_write_s7_parameter_type_plc4c_s7_read_write_s7_parameter_setup_communication) {
+        return INTERNAL_ERROR;
+      }
+
+      // Extract and save the information for:
+      // max-amq-caller, max-amq-callee, pdu-size.
+      configuration->pdu_size =
+          cotp_connect_response_packet->payload->payload->parameter->s7_parameter_setup_communication_pdu_length;
+      configuration->max_amq_caller =
+          cotp_connect_response_packet->payload->payload->parameter->s7_parameter_setup_communication_max_amq_caller;
+      configuration->max_amq_callee =
+          cotp_connect_response_packet->payload->payload->parameter->s7_parameter_setup_communication_max_amq_callee;
+
+      // If no controller is explicitly selected, detect it.
+      if(configuration->controller_type != PLC4C_DRIVER_S7_CONTROLLER_TYPE_ANY) {
+        task->state_id = PLC4C_DRIVER_S7_CONNECT_SEND_S7_IDENTIFICATION_REQUEST;
+      }
+      // If a controller is explicitly selected, we're done connecting.
+      else {
+        task->state_id = PLC4C_DRIVER_S7_CONNECT_FINISHED;
+      }
+      break;
+    }
+    // Send a S7 identification request.
+    case PLC4C_DRIVER_S7_CONNECT_SEND_S7_IDENTIFICATION_REQUEST: {
+      plc4c_s7_read_write_tpkt_packet* s7_identify_remote_request_packet =
+          malloc(sizeof(plc4c_s7_read_write_tpkt_packet));
+      if(s7_identify_remote_request_packet == NULL) {
+        return NO_MEMORY;
+      }
+      s7_identify_remote_request_packet->payload = malloc(sizeof(plc4c_s7_read_write_cotp_packet));
+      if(s7_identify_remote_request_packet->payload == NULL) {
+        return NO_MEMORY;
+      }
+      s7_identify_remote_request_packet->payload->_type =
+          plc4c_s7_read_write_cotp_packet_type_plc4c_s7_read_write_cotp_packet_data;
+      s7_identify_remote_request_packet->payload->parameters = NULL;
+      s7_identify_remote_request_packet->payload->cotp_packet_data_eot = true;
+      s7_identify_remote_request_packet->payload->cotp_packet_data_tpdu_ref = 2;
+
+      s7_identify_remote_request_packet->payload->payload =
+          malloc(sizeof(plc4c_s7_read_write_s7_message));
+      if(s7_identify_remote_request_packet->payload->payload == NULL) {
+        return NO_MEMORY;
+      }
+      s7_identify_remote_request_packet->payload->payload->_type =
+          plc4c_s7_read_write_s7_message_type_plc4c_s7_read_write_s7_message_user_data;
+      s7_identify_remote_request_packet->payload->payload->parameter =
+          malloc(sizeof(plc4c_s7_read_write_s7_parameter));
+      if(s7_identify_remote_request_packet->payload->payload->parameter == NULL) {
+        return NO_MEMORY;
+      }
+      s7_identify_remote_request_packet->payload->payload->parameter->_type =
+          plc4c_s7_read_write_s7_parameter_type_plc4c_s7_read_write_s7_parameter_user_data;
+
+      plc4c_utils_list_create(&(s7_identify_remote_request_packet->payload->payload->parameter->s7_parameter_user_data_items));
+      plc4c_s7_read_write_s7_parameter_user_data_item* parameter_item = malloc(sizeof(plc4c_s7_read_write_s7_parameter_user_data_item));
+      parameter_item->_type = plc4c_s7_read_write_s7_parameter_user_data_item_type_plc4c_s7_read_write_s7_parameter_user_data_item_cpu_functions;
+      parameter_item->s7_parameter_user_data_item_cpu_functions_method = 0x11;
+      parameter_item->s7_parameter_user_data_item_cpu_functions_cpu_function_type = 0x4;
+      parameter_item->s7_parameter_user_data_item_cpu_functions_cpu_function_group = 0x4;
+      parameter_item->s7_parameter_user_data_item_cpu_functions_cpu_subfunction = 0x01;
+      parameter_item->s7_parameter_user_data_item_cpu_functions_sequence_number = 0x00;
+      parameter_item->s7_parameter_user_data_item_cpu_functions_data_unit_reference_number = NULL;
+      parameter_item->s7_parameter_user_data_item_cpu_functions_last_data_unit = NULL;
+      parameter_item->s7_parameter_user_data_item_cpu_functions_error_code = NULL;
+      plc4c_utils_list_insert_head_value(s7_identify_remote_request_packet->payload->payload->parameter->s7_parameter_user_data_items, parameter_item);
+
+      plc4c_utils_list_create(&(s7_identify_remote_request_packet->payload->payload->payload->s7_payload_user_data_items));
+      plc4c_s7_read_write_s7_payload_user_data_item* payload_item = malloc(sizeof(plc4c_s7_read_write_s7_payload_user_data_item));
+      payload_item->_type = plc4c_s7_read_write_s7_payload_user_data_item_type_plc4c_s7_read_write_s7_payload_user_data_item_cpu_function_read_szl_request;
+      payload_item->return_code = plc4c_s7_read_write_data_transport_error_code_OK;
+      payload_item->transport_size = plc4c_s7_read_write_data_transport_size_OCTET_STRING;
+      payload_item->szl_index = 0x0000;
+      payload_item->szl_id = malloc(sizeof(plc4c_s7_read_write_szl_id));
+      if(payload_item->szl_id == NULL) {
+        return NO_MEMORY;
+      }
+      payload_item->szl_id->type_class = plc4c_s7_read_write_szl_module_type_class_CPU;
+      payload_item->szl_id->sublist_extract = 0x00;
+      payload_item->szl_id->sublist_list = plc4c_s7_read_write_szl_sublist_MODULE_IDENTIFICATION;
+
+      // Get the size required to contain the serialized form of this packet.
+      uint16_t packet_size =
+          plc4c_s7_read_write_tpkt_packet_length_in_bytes(
+              s7_identify_remote_request_packet);
+
+      // Serialize this message to a byte-array.
+      plc4c_spi_write_buffer* write_buffer;
+      plc4c_return_code return_code =
+          plc4c_spi_write_buffer_create(packet_size, &write_buffer);
+      if (return_code != OK) {
+        return INTERNAL_ERROR;
+      }
+      plc4c_s7_read_write_tpkt_packet_serialize(write_buffer, s7_identify_remote_request_packet);
+
+      // Now send this to the recipient.
+      return_code = connection->transport->send_message(write_buffer);
+      if (return_code != OK) {
+        return INTERNAL_ERROR;
+      }
+
+      task->state_id = PLC4C_DRIVER_S7_CONNECT_RECEIVE_S7_IDENTIFICATION_RESPONSE;
+      break;
+    }
+    // Receive a S7 identification response.
+    case PLC4C_DRIVER_S7_CONNECT_RECEIVE_S7_IDENTIFICATION_RESPONSE: {
+      // Get a response from the transport.
+      plc4c_spi_read_buffer* read_buffer;
+      plc4c_return_code return_code = connection->transport->select_message(
+          plc4c_driver_s7_select_message_function, &read_buffer);
+      if (return_code != OK) {
+        // If UNFINISHED was returned, the buffer hasn't received enough
+        // data for a full packet. Just return and come back later.
+        if(return_code == UNFINISHED) {
+          return OK;
+        }
+        return INTERNAL_ERROR;
+      }
+
+      // Parse the given data.
+      plc4c_s7_read_write_tpkt_packet* cotp_connect_response_packet = NULL;
+      return_code = plc4c_s7_read_write_tpkt_packet_parse(
+          read_buffer, &cotp_connect_response_packet);
+      if (return_code != OK) {
+        return INTERNAL_ERROR;
+      }
+
+      // Check if the packet has the right type
+      if(cotp_connect_response_packet->payload->_type !=
+         plc4c_s7_read_write_cotp_packet_type_plc4c_s7_read_write_cotp_packet_data) {
+        return INTERNAL_ERROR;
+      }
+      if(cotp_connect_response_packet->payload->payload->_type !=
+          plc4c_s7_read_write_s7_message_type_plc4c_s7_read_write_s7_message_user_data) {
+        return INTERNAL_ERROR;
+      }
+      if(cotp_connect_response_packet->payload->payload->payload->_type !=
+          plc4c_s7_read_write_s7_payload_type_plc4c_s7_read_write_s7_payload_user_data) {
+        return INTERNAL_ERROR;
+      }
+
+      plc4c_list_element* cur_item = cotp_connect_response_packet->payload->payload->payload->s7_payload_user_data_items->tail;
+      while(cur_item != NULL) {
+        plc4c_s7_read_write_s7_payload_user_data_item* item = cur_item->value;
+        if(item->_type == plc4c_s7_read_write_s7_payload_user_data_item_type_plc4c_s7_read_write_s7_payload_user_data_item_cpu_function_read_szl_response) {
+          plc4c_list_element* szl_item = item->s7_payload_user_data_item_cpu_function_read_szl_response_items->head;
+          while(szl_item != NULL) {
+            plc4c_s7_read_write_szl_data_tree_item* data_tree_item = szl_item->value;
+            if(data_tree_item->item_index == 0x0001) {
+              char* article_number = list_to_string(data_tree_item->mlfb);
+              if(article_number != NULL) {
+                configuration->controller_type = decode_controller_type(article_number);
+                free(article_number);
+              } else {
+                configuration->controller_type = PLC4C_DRIVER_S7_CONTROLLER_TYPE_ANY;
+              }
+            }
+            szl_item = szl_item->next;
+          }
+        }
+
+        cur_item = cur_item->next;
+      }
+
+      task->state_id = PLC4C_DRIVER_S7_CONNECT_FINISHED;
+      break;
+    }
+    // Clean up some internal data-structures.
+    case PLC4C_DRIVER_S7_CONNECT_FINISHED: {
+      plc4c_connection_set_connected(connection, true);
+      task->completed = true;
+      break;
+    }
+    // If an unexpected state id was received, this is not really something
+    // we can recover from.
+    default: {
+      task->completed = true;
+      return INTERNAL_ERROR;
+    }
+  }
+  return OK;
+}
+
+plc4c_return_code plc4c_driver_s7_disconnect_machine_function(
+    plc4c_system_task *task) {
+  plc4c_connection *connection = task->context;
+  if (connection == NULL) {
+    return INTERNAL_ERROR;
+  }
+
+  switch (task->state_id) {
+    case PLC4C_DRIVER_S7_DISCONNECT_INIT: {
+      plc4c_connection_set_disconnect(connection, true);
+      task->state_id = PLC4C_DRIVER_S7_DISCONNECT_WAIT_TASKS_FINISHED;
+      break;
+    }
+    case PLC4C_DRIVER_S7_DISCONNECT_WAIT_TASKS_FINISHED: {
+      // The disconnect system-task also counts.
+      if (plc4c_connection_get_running_tasks_count(connection) == 1) {
+        plc4c_connection_set_connected(connection, false);
+        task->completed = true;
+        task->state_id = PLC4C_DRIVER_S7_DISCONNECT_FINISHED;
+      }
+      break;
+    }
+    case PLC4C_DRIVER_S7_DISCONNECT_FINISHED: {
+      // Do nothing
+      break;
+    }
+    default: {
+      return INTERNAL_ERROR;
+    }
+  }
+  return OK;
+}
+
+plc4c_return_code plc4c_driver_s7_read_machine_function(
+    plc4c_system_task *task) {
+  plc4c_read_request_execution *read_request_execution = task->context;
+  if (read_request_execution == NULL) {
+    return INTERNAL_ERROR;
+  }
+  if (task->connection == NULL) {
+    return INTERNAL_ERROR;
+  }
+
+  switch (task->state_id) {
+    case PLC4C_DRIVER_S7_READ_INIT: {
+      task->completed = true;
+      // TODO: Implement this ...
+      task->state_id = PLC4C_DRIVER_S7_READ_FINISHED;
+      break;
+    }
+    case PLC4C_DRIVER_S7_READ_FINISHED: {
+      break;
+    }
+  }
+  return OK;
+}
+
+plc4c_return_code plc4c_driver_s7_write_machine_function(
+    plc4c_system_task *task) {
+  plc4c_write_request_execution *write_request_execution = task->context;
+  if (write_request_execution == NULL) {
+    return INTERNAL_ERROR;
+  }
+  if (task->connection == NULL) {
+    return INTERNAL_ERROR;
+  }
+
+  switch (task->state_id) {
+    case PLC4C_DRIVER_S7_WRITE_INIT: {
+      task->completed = true;
+      // TODO: Implement this ...
+      task->state_id = PLC4C_DRIVER_S7_WRITE_FINISHED;
+      break;
+    }
+    case PLC4C_DRIVER_S7_WRITE_FINISHED: {
+      break;
+    }
+  }
+  return OK;
+}
+
+plc4c_return_code plc4c_driver_s7_connect_function(
+    plc4c_connection *connection, plc4c_system_task **task) {
+  plc4c_system_task *new_task = malloc(sizeof(plc4c_system_task));
+  // There's nothing to do here, so no need for a state-machine.
+  new_task->state_id = PLC4C_DRIVER_S7_CONNECT_INIT;
+  new_task->state_machine_function = &plc4c_driver_s7_connect_machine_function;
+  new_task->completed = false;
+  new_task->context = connection;
+  new_task->connection = connection;
+  *task = new_task;
+  return OK;
+}
+
+plc4c_return_code plc4c_driver_s7_disconnect_function(
+    plc4c_connection *connection, plc4c_system_task **task) {
+  plc4c_system_task *new_task = malloc(sizeof(plc4c_system_task));
+  new_task->state_id = PLC4C_DRIVER_S7_DISCONNECT_INIT;
+  new_task->state_machine_function =
+      &plc4c_driver_s7_disconnect_machine_function;
+  new_task->completed = false;
+  new_task->context = connection;
+  new_task->connection = connection;
+  *task = new_task;
+  return OK;
+}
+
+plc4c_return_code plc4c_driver_s7_read_function(
+    plc4c_read_request_execution *read_request_execution,
+    plc4c_system_task **task) {
+  plc4c_system_task *new_task = malloc(sizeof(plc4c_system_task));
+  new_task->state_id = PLC4C_DRIVER_S7_READ_INIT;
+  new_task->state_machine_function = &plc4c_driver_s7_read_machine_function;
+  new_task->completed = false;
+  new_task->context = read_request_execution;
+  new_task->connection = read_request_execution->system_task->connection;
+  *task = new_task;
+  return OK;
+}
+
+void plc4c_driver_s7_free_read_response_item(
+    plc4c_list_element *read_item_element) {
+  plc4c_response_value_item *value_item =
+      (plc4c_response_value_item *)read_item_element->value;
+  plc4c_data_destroy(value_item->value);
+  value_item->value = NULL;
+}
+
+void plc4c_driver_s7_free_read_response(plc4c_read_response *response) {
+  // the request will be cleaned up elsewhere
+  plc4c_utils_list_delete_elements(response->items,
+                                   &plc4c_driver_s7_free_read_response_item);
+}
+
+plc4c_return_code plc4c_driver_s7_write_function(
+    plc4c_write_request_execution *write_request_execution,
+    plc4c_system_task **task) {
+  plc4c_system_task *new_task = malloc(sizeof(plc4c_system_task));
+  new_task->state_id = PLC4C_DRIVER_S7_WRITE_INIT;
+  new_task->state_machine_function = &plc4c_driver_s7_write_machine_function;
+  new_task->completed = false;
+  new_task->context = write_request_execution;
+  new_task->connection = write_request_execution->system_task->connection;
+  *task = new_task;
+  return OK;
+}
+
+void plc4c_driver_s7_free_write_response_item(
+    plc4c_list_element *write_item_element) {
+  plc4c_response_value_item *value_item =
+      (plc4c_response_value_item *)write_item_element->value;
+  // do not delete the plc4c_item
+  // we also, in THIS case don't delete the random value which isn't really
+  // a pointer
+  // free(value_item->value);
+  value_item->value = NULL;
+}
+
+void plc4c_driver_s7_free_write_response(
+    plc4c_write_response *response) {
+  // the request will be cleaned up elsewhere
+  plc4c_utils_list_delete_elements(response->response_items,
+                                   &plc4c_driver_s7_free_write_response_item);
+}
 
 plc4c_driver *plc4c_driver_s7_create() {
   plc4c_driver *driver = (plc4c_driver *)malloc(sizeof(plc4c_driver));
@@ -27,14 +761,14 @@ plc4c_driver *plc4c_driver_s7_create() {
   driver->protocol_name = "Siemens S7 (Basic)";
   driver->default_transport_code = "tcp";
   driver->parse_address_function = NULL;
-  driver->connect_function = NULL;
-  driver->disconnect_function = NULL;
-  driver->read_function = NULL;
-  driver->write_function = NULL;
+  driver->connect_function = &plc4c_driver_s7_connect_function;
+  driver->disconnect_function = &plc4c_driver_s7_disconnect_function;
+  driver->read_function = &plc4c_driver_s7_read_function;
+  driver->write_function = &plc4c_driver_s7_write_function;
   driver->subscribe_function = NULL;
   driver->unsubscribe_function = NULL;
-  driver->free_read_response_function = NULL;
-  driver->free_write_response_function = NULL;
+  driver->free_read_response_function = &plc4c_driver_s7_free_read_response;
+  driver->free_write_response_function = &plc4c_driver_s7_free_write_response;
   driver->free_subscription_response_function = NULL;
   driver->free_unsubscription_response_function = NULL;
   return driver;
diff --git a/sandbox/plc4c/drivers/simulated/include/plc4c/driver_simulated.h b/sandbox/plc4c/drivers/simulated/include/plc4c/driver_simulated.h
index cc77d59..32c0df4 100644
--- a/sandbox/plc4c/drivers/simulated/include/plc4c/driver_simulated.h
+++ b/sandbox/plc4c/drivers/simulated/include/plc4c/driver_simulated.h
@@ -16,8 +16,8 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-#ifndef PLC4C_DRIVER_S7_H_
-#define PLC4C_DRIVER_S7_H_
+#ifndef PLC4C_DRIVER_SIMULATED_H_
+#define PLC4C_DRIVER_SIMULATED_H_
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -29,4 +29,4 @@ plc4c_driver *plc4c_driver_simulated_create();
 #ifdef __cplusplus
 }
 #endif
-#endif  // PLC4C_DRIVER_S7_H_
\ No newline at end of file
+#endif  // PLC4C_DRIVER_SIMULATED_H_
\ No newline at end of file
diff --git a/sandbox/plc4c/drivers/simulated/src/driver_simulated.c b/sandbox/plc4c/drivers/simulated/src/driver_simulated.c
index 48c3ade..13311ac 100644
--- a/sandbox/plc4c/drivers/simulated/src/driver_simulated.c
+++ b/sandbox/plc4c/drivers/simulated/src/driver_simulated.c
@@ -63,7 +63,7 @@ plc4c_return_code plc4c_driver_simulated_connect_machine_function(
     return INTERNAL_ERROR;
   }
   if (plc4c_connection_get_connected(connection)) {
-    return INTERNAL_ERROR;
+    return ALREADY_CONNECTED;
   }
   plc4c_connection_set_connected(connection, true);
   task->completed = true;
@@ -369,7 +369,8 @@ plc4c_return_code plc4c_driver_simulated_write_function(
   return OK;
 }
 
-void free_read_response_item(plc4c_list_element *read_item_element) {
+void plc4c_driver_simulated_free_read_response_item(
+    plc4c_list_element *read_item_element) {
   plc4c_response_value_item *value_item =
       (plc4c_response_value_item *)read_item_element->value;
   plc4c_data_destroy(value_item->value);
@@ -378,10 +379,12 @@ void free_read_response_item(plc4c_list_element *read_item_element) {
 
 void plc4c_driver_simulated_free_read_response(plc4c_read_response *response) {
   // the request will be cleaned up elsewhere
-  plc4c_utils_list_delete_elements(response->items, &free_read_response_item);
+  plc4c_utils_list_delete_elements(response->items,
+                                   &plc4c_driver_simulated_free_read_response_item);
 }
 
-void free_write_response_item(plc4c_list_element *write_item_element) {
+void plc4c_driver_simulated_free_write_response_item(
+    plc4c_list_element *write_item_element) {
   plc4c_response_value_item *value_item =
       (plc4c_response_value_item *)write_item_element->value;
   // do not delete the plc4c_item
@@ -395,7 +398,7 @@ void plc4c_driver_simulated_free_write_response(
     plc4c_write_response *response) {
   // the request will be cleaned up elsewhere
   plc4c_utils_list_delete_elements(response->response_items,
-                                   &free_write_response_item);
+                                   &plc4c_driver_simulated_free_write_response_item);
 }
 
 plc4c_driver *plc4c_driver_simulated_create() {
diff --git a/sandbox/plc4c/examples/hello-world/CMakeLists.txt b/sandbox/plc4c/examples/hello-world/CMakeLists.txt
index 2190e0b..3e59217 100644
--- a/sandbox/plc4c/examples/hello-world/CMakeLists.txt
+++ b/sandbox/plc4c/examples/hello-world/CMakeLists.txt
@@ -17,14 +17,21 @@
   under the License.
 ]]
 
-include_directories("../../api/include" "../../drivers/simulated/include" "../../transports/dummy/include")
+include_directories("../../api/include"
+        "../../drivers/simulated/include" "../../drivers/s7/include" "../../drivers/modbus/include"
+        "../../transports/dummy/include" "../../transports/tcp/include" "../../transports/serial/include")
 
 add_executable(plc4c-examples-hello-world
         src/hello_world.c
         )
 
 target_link_libraries(plc4c-examples-hello-world
-        plc4c-spi plc4c-driver-simulated
+        plc4c-spi
+        plc4c-driver-simulated
+        plc4c-driver-s7
+        plc4c-driver-modbus
         plc4c-transport-dummy
+        plc4c-transport-tcp
+        plc4c-transport-serial
         m
 )
\ No newline at end of file
diff --git a/sandbox/plc4c/examples/hello-world/src/hello_world.c b/sandbox/plc4c/examples/hello-world/src/hello_world.c
index d233bdd..8a7398c 100644
--- a/sandbox/plc4c/examples/hello-world/src/hello_world.c
+++ b/sandbox/plc4c/examples/hello-world/src/hello_world.c
@@ -17,8 +17,12 @@
  * under the License.
  */
 #include <plc4c/driver_simulated.h>
+#include <plc4c/driver_s7.h>
+#include <plc4c/driver_modbus.h>
 #include <plc4c/plc4c.h>
 #include <plc4c/transport_dummy.h>
+#include <plc4c/transport_tcp.h>
+#include <plc4c/transport_serial.h>
 #include <plc4c/utils/list.h>
 #include <stdio.h>
 #include <string.h>
@@ -91,7 +95,21 @@ int main() {
   plc4c_driver *simulated_driver = plc4c_driver_simulated_create();
   result = plc4c_system_add_driver(system, simulated_driver);
   if (result != OK) {
-    printf("FAILED\n");
+    printf("FAILED adding simulated driver\n");
+    return -1;
+  }
+  printf("Registering driver for the 's7' protocol ... ");
+  plc4c_driver *s7_driver = plc4c_driver_s7_create();
+  result = plc4c_system_add_driver(system, s7_driver);
+  if (result != OK) {
+    printf("FAILED adding s7 driver\n");
+    return -1;
+  }
+  printf("Registering driver for the 'modbus' protocol ... ");
+  plc4c_driver *modbus_driver = plc4c_driver_modbus_create();
+  result = plc4c_system_add_driver(system, modbus_driver);
+  if (result != OK) {
+    printf("FAILED adding modbus driver\n");
     return -1;
   }
   printf("SUCCESS\n");
@@ -100,7 +118,19 @@ int main() {
   plc4c_transport *dummy_transport = plc4c_transport_dummy_create();
   result = plc4c_system_add_transport(system, dummy_transport);
   if (result != OK) {
-    printf("FAILED\n");
+    printf("FAILED adding dummy transport\n");
+    return -1;
+  }
+  plc4c_transport *tcp_transport = plc4c_transport_tcp_create();
+  result = plc4c_system_add_transport(system, tcp_transport);
+  if (result != OK) {
+    printf("FAILED adding tcp transport\n");
+    return -1;
+  }
+  plc4c_transport *serial_transport = plc4c_transport_serial_create();
+  result = plc4c_system_add_transport(system, serial_transport);
+  if (result != OK) {
+    printf("FAILED adding serial transport\n");
     return -1;
   }
   printf("SUCCESS\n");
@@ -122,8 +152,8 @@ int main() {
 
   // Establish connections to remote devices
   // you may or may not care about the connection handle
-  printf("Connecting to 'simulated://foo' ... ");
-  result = plc4c_system_connect(system, "simulated://foo", &connection);
+  printf("Connecting to 's7:tcp://192.168.23.30' ... ");
+  result = plc4c_system_connect(system, "s7:tcp://192.168.23.30", &connection);
   if (result != OK) {
     printf("FAILED\n");
     return -1;
diff --git a/sandbox/plc4c/spi/include/plc4c/spi/read_buffer.h b/sandbox/plc4c/spi/include/plc4c/spi/read_buffer.h
index 9604d45..7489d88 100644
--- a/sandbox/plc4c/spi/include/plc4c/spi/read_buffer.h
+++ b/sandbox/plc4c/spi/include/plc4c/spi/read_buffer.h
@@ -88,4 +88,11 @@ plc4c_return_code plc4c_spi_read_double(plc4c_spi_read_buffer* buf, uint8_t num_
 
 plc4c_return_code plc4c_spi_read_string(plc4c_spi_read_buffer* buf, uint8_t num_bits, char* encoding, char** value);
 
+/**
+ * Converts the bytes contained in a list into a '\0' terminated string.
+ * @param list of bytes
+ * @return '\0' terminated string
+ */
+char* list_to_string(plc4c_list* list);
+
 #endif  // PLC4C_READ_BUFFER_H_
\ No newline at end of file
diff --git a/sandbox/plc4c/spi/include/plc4c/spi/types_private.h b/sandbox/plc4c/spi/include/plc4c/spi/types_private.h
index f34dcfb..1ff8d39 100644
--- a/sandbox/plc4c/spi/include/plc4c/spi/types_private.h
+++ b/sandbox/plc4c/spi/include/plc4c/spi/types_private.h
@@ -25,6 +25,9 @@
 #include <plc4c/utils/list.h>
 #include <plc4c/utils/queue.h>
 
+#include "read_buffer.h"
+#include "write_buffer.h"
+
 typedef struct plc4c_item_t plc4c_item;
 typedef struct plc4c_driver_list_item_t plc4c_driver_list_item;
 typedef struct plc4c_transport_list_item_t plc4c_transport_list_item;
@@ -80,6 +83,29 @@ typedef void (*plc4c_connect_free_subscription_response_function)(
 typedef void (*plc4c_connect_free_unsubscription_response_function)(
     plc4c_unsubscription_response *response);
 
+// TODO: Implement the argument.
+typedef plc4c_return_code (*plc4c_transport_open_function)(void* config);
+
+// TODO: Implement the argument.
+typedef plc4c_return_code (*plc4c_transport_close_function)(void* config);
+
+typedef plc4c_return_code (*plc4c_transport_send_message_function)(
+    plc4c_spi_write_buffer* message);
+
+// Helper function that tells the transport what to do with the current input
+// on positive return value: a read-buffer with given number of bytes is
+// returned. RESPONSE_ACCEPT_INCOMPLETE or 0: Currently not enough data is
+// or this content is not applicable for the current task. Don't consume
+// anything and potentially re-check next time. If however the value is negative
+// the buffer seems to contain corrupt data, clean up by skipping the number of
+// bytes you get by making the negative value a positive and not returning any
+// read-buffer.
+typedef int16_t (*accept_message_function)(
+    uint8_t* data, uint8_t length);
+
+typedef plc4c_return_code (*plc4c_transport_select_message_function)(
+    accept_message_function accept_message, plc4c_spi_read_buffer** message);
+
 struct plc4c_system_t {
   /* drivers */
   plc4c_list *driver_list;
@@ -129,10 +155,22 @@ struct plc4c_driver_list_item_t {
   plc4c_driver_list_item *next;
 };
 
+struct plc4c_transport_message_t {
+  int length;
+  uint8_t data[];
+};
+
 struct plc4c_transport_t {
   char *transport_code;
-
-  // TODO: add the send and receive function references here ...
+  char* transport_name;
+
+  plc4c_transport_open_function open;
+  plc4c_transport_close_function close;
+  plc4c_transport_send_message_function send_message;
+  // Function that uses a function passed in to see if a given system-task
+  // will be able to consume the current content of the transports input
+  // buffer.
+  plc4c_transport_select_message_function select_message;
 };
 
 struct plc4c_transport_list_item_t {
@@ -146,6 +184,7 @@ struct plc4c_connection_t {
   char *transport_code;
   char *transport_connect_information;
   char *parameters;
+  void *configuration;
 
   bool connected;
   // Internal flag indicating the connection should be disconnected
diff --git a/sandbox/plc4c/spi/src/system.c b/sandbox/plc4c/spi/src/system.c
index 5e45339..3d8a147 100644
--- a/sandbox/plc4c/spi/src/system.c
+++ b/sandbox/plc4c/spi/src/system.c
@@ -371,7 +371,7 @@ plc4c_return_code plc4c_system_connect(plc4c_system *system,
   if (plc4c_utils_list_empty(system->driver_list)) {
     return NO_DRIVER_AVAILABLE;
   }
-  plc4c_list_element *cur_driver_list_element = system->driver_list->head;
+  plc4c_list_element *cur_driver_list_element = system->driver_list->tail;
   do {
     plc4c_driver *cur_driver = (plc4c_driver *)cur_driver_list_element->value;
     if (strcmp(cur_driver->protocol_code,
@@ -412,7 +412,7 @@ plc4c_return_code plc4c_system_connect(plc4c_system *system,
   if (plc4c_utils_list_empty(system->transport_list)) {
     return NO_TRANSPORT_AVAILABLE;
   }
-  plc4c_list_element *cur_transport_list_element = system->transport_list->head;
+  plc4c_list_element *cur_transport_list_element = system->transport_list->tail;
   do {
     plc4c_transport *cur_transport =
         (plc4c_transport *)cur_transport_list_element->value;
@@ -493,3 +493,23 @@ plc4c_return_code plc4c_system_loop(plc4c_system *system) {
 
   return OK;
 }
+
+char* list_to_string(plc4c_list* list) {
+  uint8_t string_length = plc4c_utils_list_size(list);
+  char* chars = malloc(sizeof(char) * (string_length + 1));
+  if(chars == NULL) {
+    return NULL;
+  }
+  char* cur_pos = chars;
+  plc4c_list_element* cur_element = list->tail;
+  while(cur_element != NULL) {
+    char cur_char = *((char*)(cur_element->value));
+    *cur_pos = cur_char;
+    cur_element = cur_element->next;
+    cur_pos++;
+  }
+  // Terminate the string.
+  *cur_pos = '\0';
+  return chars;
+}
+
diff --git a/sandbox/plc4c/transports/dummy/src/transport_dummy.c b/sandbox/plc4c/transports/dummy/src/transport_dummy.c
index 2c35eb2..610f30d 100644
--- a/sandbox/plc4c/transports/dummy/src/transport_dummy.c
+++ b/sandbox/plc4c/transports/dummy/src/transport_dummy.c
@@ -25,5 +25,10 @@ plc4c_transport *plc4c_transport_dummy_create() {
   plc4c_transport *transport =
       (plc4c_transport *)malloc(sizeof(plc4c_transport));
   transport->transport_code = "dummy";
+  transport->transport_name = "Dummy driver not actually doing any IO";
+  transport->open = NULL;
+  transport->close = NULL;
+  transport->send_message = NULL;
+  transport->select_message = NULL;
   return transport;
 }
diff --git a/sandbox/plc4c/transports/serial/CMakeLists.txt b/sandbox/plc4c/transports/serial/CMakeLists.txt
index 943e090..c486b3b 100644
--- a/sandbox/plc4c/transports/serial/CMakeLists.txt
+++ b/sandbox/plc4c/transports/serial/CMakeLists.txt
@@ -22,6 +22,6 @@ include_directories("include" "../../api/include" "../../spi/include")
 # Add the sources
 file(GLOB sources "src/*.c")
 
-add_library(plc4c-transports-serial ${sources})
+add_library(plc4c-transport-serial ${sources})
 
-target_link_libraries(plc4c-transports-serial ${CMAKE_DL_LIBS})
\ No newline at end of file
+target_link_libraries(plc4c-transport-serial ${CMAKE_DL_LIBS})
\ No newline at end of file
diff --git a/sandbox/plc4c/transports/serial/src/transport_serial.c b/sandbox/plc4c/transports/serial/src/transport_serial.c
index adf0996..38222ef 100644
--- a/sandbox/plc4c/transports/serial/src/transport_serial.c
+++ b/sandbox/plc4c/transports/serial/src/transport_serial.c
@@ -21,9 +21,32 @@
 #include <plc4c/transport_serial.h>
 #include <stdlib.h>
 
+plc4c_return_code plc4c_transport_serial_open_function(void* config) {
+  return OK;
+}
+
+plc4c_return_code plc4c_transport_serial_close_function(void* config) {
+  return OK;
+}
+
+plc4c_return_code plc4c_transport_serial_send_message_function(
+    plc4c_spi_write_buffer* message) {
+  return OK;
+}
+
+plc4c_return_code plc4c_transport_serial_select_message_function(
+    accept_message_function accept_message, plc4c_spi_read_buffer** message) {
+  return OK;
+}
+
 plc4c_transport *plc4c_transport_serial_create() {
   plc4c_transport *transport =
       (plc4c_transport *)malloc(sizeof(plc4c_transport));
   transport->transport_code = "serial";
+  transport->transport_name = "Serial port transport";
+  transport->open = &plc4c_transport_serial_open_function;
+  transport->close = &plc4c_transport_serial_close_function;
+  transport->send_message = &plc4c_transport_serial_send_message_function;
+  transport->select_message = &plc4c_transport_serial_select_message_function;
   return transport;
 }
diff --git a/sandbox/plc4c/transports/tcp/CMakeLists.txt b/sandbox/plc4c/transports/tcp/CMakeLists.txt
index f950cdb..afc8658 100644
--- a/sandbox/plc4c/transports/tcp/CMakeLists.txt
+++ b/sandbox/plc4c/transports/tcp/CMakeLists.txt
@@ -22,6 +22,6 @@ include_directories("include" "../../api/include" "../../spi/include")
 # Add the sources
 file(GLOB sources "src/*.c")
 
-add_library(plc4c-transports-tcp ${sources})
+add_library(plc4c-transport-tcp ${sources})
 
-target_link_libraries(plc4c-transports-tcp ${CMAKE_DL_LIBS})
\ No newline at end of file
+target_link_libraries(plc4c-transport-tcp ${CMAKE_DL_LIBS})
\ No newline at end of file
diff --git a/sandbox/plc4c/transports/tcp/src/transport_tcp.c b/sandbox/plc4c/transports/tcp/src/transport_tcp.c
index 6836ad5..3d049cc 100644
--- a/sandbox/plc4c/transports/tcp/src/transport_tcp.c
+++ b/sandbox/plc4c/transports/tcp/src/transport_tcp.c
@@ -21,9 +21,32 @@
 #include <plc4c/transport_tcp.h>
 #include <stdlib.h>
 
+plc4c_return_code plc4c_transport_tcp_open_function(void* config) {
+  return OK;
+}
+
+plc4c_return_code plc4c_transport_tcp_close_function(void* config) {
+  return OK;
+}
+
+plc4c_return_code plc4c_transport_tcp_send_message_function(
+    plc4c_spi_write_buffer* message) {
+  return OK;
+}
+
+plc4c_return_code plc4c_transport_tcp_select_message_function(
+    accept_message_function accept_message, plc4c_spi_read_buffer** message) {
+  return OK;
+}
+
 plc4c_transport *plc4c_transport_tcp_create() {
   plc4c_transport *transport =
       (plc4c_transport *)malloc(sizeof(plc4c_transport));
   transport->transport_code = "tcp";
+  transport->transport_name = "TCP transport";
+  transport->open = &plc4c_transport_tcp_open_function;
+  transport->close = &plc4c_transport_tcp_close_function;
+  transport->send_message = &plc4c_transport_tcp_send_message_function;
+  transport->select_message = &plc4c_transport_tcp_select_message_function;
   return transport;
 }