First published version
diff --git a/doc/README.md b/doc/README.md
new file mode 100644
index 0000000..cc2c14c
--- /dev/null
+++ b/doc/README.md
@@ -0,0 +1,91 @@
+<!---
+##############################################################################
+# Copyright (c) 2000-2017 Ericsson Telecom AB
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+# Lenard Nagy
+#
+##############################################################################
+-->
+
+# GPIO testport for use with Titan test executor on Raspberry Pi
+
+## General introduction
+Titan is a TTCN-3 compiler and test executor, which is able to run on the Raspberry Pi (model 2B and 3B were tested). GPIO (general purpose input/output) pins on the Raspberry Pi are able to control and/or receive signals from physical computing devices (leds, buttons, valves, etc...). Testports are programmable connections between Titan and any physical interface on a device, in this case GPIO pins on the Raspberry Pi.
+
+The current implementation is using the sysfs interface to access GPIO pins.
+
+## Usage
+
+### Preparations
+
+* In Your TTCN-3 module the GPIOPinPort module must be included:
+
+ import from GPIOPinPort all;
+
+* Create a component in the module:
+** Important: the name of the port instance MUST be either gpio# or in case of port arrays gpio[#], where # is an integer between (and including) 2 and 27, which will be the actual pin number of the GPIO!
+
+ type component GPIO {
+ port GPIO_Pin_Port gpio27;
+ port GPIO_Pin_Port gpio[2..26];
+ timer t_short;
+ }
+
+* Map any or all the ports You want to use:
+
+ map(self:gpio[11], system:gpio[11]);
+ map(self:gpio27, system:gpio27);
+ map(self:gpio[26], system:gpio[26]);
+
+### Setting a pin to output, and setting the value of the pin
+
+* To set direction on the gpio pin to OUT a "send" operation is to be performed:
+
+ var GPIO_PIN_DIRECTION gpio27_direction := OUT;
+ gpio27.send(gpio27_direction);
+
+* Similarly, to set the value (HIGH/LOW) a "send" operation is to be performed:
+
+ var GPIO_PIN_VALUE gpio27_value := HIGH;
+ gpio27.send(gpio27_value);
+
+### Listening for input changes on pin(s)
+
+ var integer v_index;
+
+ var GPIO_PIN_DIRECTION gpio_direction_in := IN;
+ gpio[26].send(gpio_direction_in);
+ gpio27.send(gpio_direction_in);
+
+ t_short.start(5.0);
+
+ alt {
+ [] gpio27.receive {
+ setverdict(pass,"message received on port index 27");
+ }
+ [] any from gpio.receive -> @index value v_index {
+ setverdict(fail,"unexpected message received on port index: ",v_index);
+ }
+ [] t_short.timeout {
+ log("Timeout while waiting for input to change");
+ setverdict(inconc);
+ }
+ }
+
+### Unmap all the used ports
+
+ unmap(self:gpio27, system:gpio27);
+ unmap(self:gpio[11], system:gpio[11]);
+ unmap(self:gpio[26], system:gpio[26]);
+
+## References
+* About Titan: https://projects.eclipse.org/projects/tools.titan
+* About TTCN-3: http://www.ttcn-3.org/
+* About Raspberry Pi: https://www.raspberrypi.org/
+* About GPIO on Raspberry Pi: https://www.raspberrypi.org/documentation/usage/gpio/README.md
+
diff --git a/src/GPIOPinPort.ttcn b/src/GPIOPinPort.ttcn
new file mode 100644
index 0000000..a78d24b
--- /dev/null
+++ b/src/GPIOPinPort.ttcn
@@ -0,0 +1,44 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2000-2017 Ericsson Telecom AB
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// which accompanies this distribution, and is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// Contributors:
+// Lenard Nagy
+//
+///////////////////////////////////////////////////////////////////////////////
+module GPIOPinPort {
+
+ type enumerated GPIO_PIN_DIRECTION {
+ IN, //"in"
+ OUT //"out"
+ }
+
+ type enumerated GPIO_PIN_VALUE {
+ LOW, //"0"
+ HIGH //"1"
+ }
+
+ type record GPIO_PIN_STATUS {
+ boolean is_exported,
+ GPIO_PIN_DIRECTION direction,
+ GPIO_PIN_VALUE val
+ }
+
+ // Raspberry Pi GPIO port
+ // When a port of this type is used it MUST be named as "gpio[2-27]"
+ //
+ // E.g.:
+ // type component GPIO {
+ // port GPIO_Pin_Port gpio24;
+ // }
+ //
+ type port GPIO_Pin_Port message {
+ out
+ GPIO_PIN_DIRECTION, GPIO_PIN_VALUE, GPIO_PIN_STATUS
+ in
+ GPIO_PIN_VALUE, GPIO_PIN_STATUS;
+ }
+}
diff --git a/src/GPIO_Pin_Port.cc b/src/GPIO_Pin_Port.cc
new file mode 100644
index 0000000..2397647
--- /dev/null
+++ b/src/GPIO_Pin_Port.cc
@@ -0,0 +1,322 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2000-2017 Ericsson Telecom AB
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// which accompanies this distribution, and is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// Contributors:
+// Lenard Nagy
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include "GPIO_Pin_Port.hh"
+
+namespace GPIOPinPort {
+
+ static const char *gpio_dir = "/sys/class/gpio/";
+ static const char *export_file = "/sys/class/gpio/export";
+ static const char *unexport_file = "/sys/class/gpio/unexport";
+
+ //DEBUG
+ //const char *export_file = "/home/elnrnag/gpio/export";
+ //const char *unexport_file = "/home/elnrnag/gpio/unexport";
+ //DEBUG
+
+ static const string GPIO_DIRECTION_OUT = "out";
+ static const string GPIO_DIRECTION_IN = "in";
+ static const string GPIO_VALUE_HIGH = "1";
+ static const string GPIO_VALUE_LOW = "0";
+ static const string GPIO_EDGE_NONE = "none";
+ static const string GPIO_EDGE_BOTH = "both";
+
+ static const int NUMBER_OF_PINS = 26;
+
+GPIO__Pin__Port::GPIO__Pin__Port(const char *par_port_name)
+ : GPIO__Pin__Port_BASE(par_port_name)
+{
+
+}
+
+GPIO__Pin__Port::~GPIO__Pin__Port()
+{
+
+}
+
+void GPIO__Pin__Port::set_parameter(const char * /*parameter_name*/,
+ const char * /*parameter_value*/)
+{
+
+}
+
+/*void GPIO__Pin__Port::Handle_Fd_Event(int fd, boolean is_readable,
+ boolean is_writable, boolean is_error) {}*/
+
+void GPIO__Pin__Port::Handle_Fd_Event_Error(int fd)
+{
+ Handle_Fd_Event_Readable(fd);
+}
+
+void GPIO__Pin__Port::Handle_Fd_Event_Writable(int /*fd*/)
+{
+
+}
+
+void GPIO__Pin__Port::Handle_Fd_Event_Readable(int fd)
+{
+ //unregister_fd_handler();
+ //set_edge_on_pin(GPIO_EDGE_NONE);
+ //printf("FileDescriptor: %d",fd);
+
+ GPIOPinPort::GPIO__PIN__VALUE ret_val;
+ if (get_value_of_pin() == GPIO_VALUE_HIGH) {
+ ret_val = GPIO__PIN__VALUE::HIGH;
+ } else {
+ ret_val = GPIO__PIN__VALUE::LOW;
+ }
+
+ if (ret_val != original_value) {
+ original_value = ret_val;
+ incoming_message(ret_val);
+ }
+
+}
+
+/*void GPIO__Pin__Port::Handle_Timeout(double time_since_last_call) {}*/
+
+void GPIO__Pin__Port::user_map(const char * system_port)
+{
+ if ((strlen(system_port) >= 5) && (strlen(system_port) <= 8)) {
+ char* str = (char*)malloc(strlen(system_port)+1);
+ strcpy(str, system_port);
+
+ removechars(str, '[');
+ removechars(str, ']');
+
+ pin.assign(str);
+ printf("length: %zd\n",strlen(pin.c_str()));
+
+ pin_num.assign(str+4);
+
+ printf("Mapping %s\n", pin.c_str());
+ export_pin();
+ } else {
+ TTCN_error("Invalid gpio pin name: %s\nThe port variable name in TTCN code MUST be \"gpio[2-27]\"\n", pin.c_str());
+
+ }
+}
+
+void GPIO__Pin__Port::user_unmap(const char * system_port)
+{
+ unregister_fd_handler(fd_edge);
+ unexport_pin();
+}
+
+void GPIO__Pin__Port::user_start()
+{
+
+}
+
+void GPIO__Pin__Port::user_stop()
+{
+
+}
+
+void GPIO__Pin__Port::outgoing_send(const GPIO__PIN__DIRECTION& direction)
+{
+ if (direction == GPIO__PIN__DIRECTION::IN) {
+ set_direction_of_pin(GPIO_DIRECTION_IN);
+ set_edge_on_pin(GPIO_EDGE_BOTH);
+
+ if (get_value_of_pin() == GPIO_VALUE_HIGH) {
+ original_value = GPIO__PIN__VALUE::HIGH;
+ } else {
+ original_value = GPIO__PIN__VALUE::LOW;
+ }
+
+ } else if (direction == GPIO__PIN__DIRECTION::OUT) {
+ set_edge_on_pin(GPIO_EDGE_NONE);
+ set_direction_of_pin(GPIO_DIRECTION_OUT);
+ } else {
+ //TODO: ERROR, must be set
+ }
+}
+
+void GPIO__Pin__Port::outgoing_send(const GPIO__PIN__VALUE& value)
+{
+ if (value == GPIO__PIN__VALUE::LOW) {
+ set_value_of_pin(GPIO_VALUE_LOW);
+ } else if (value == GPIO__PIN__VALUE::HIGH) {
+ set_value_of_pin(GPIO_VALUE_HIGH);
+ } else {
+ TTCN_error("Value for %s must be set", pin.c_str());
+ }
+}
+
+void GPIO__Pin__Port::outgoing_send(const GPIO__PIN__STATUS& stat)
+{
+ GPIO__PIN__STATUS status;
+ if (file_exists(get_pin_dir())){
+ status.is__exported() = true;
+
+ string direction = get_direction_of_pin();
+ if (0 == direction.compare(GPIO_DIRECTION_IN)) {
+ status.direction() = GPIO__PIN__DIRECTION::IN;
+ } else if (0 == direction.compare(GPIO_DIRECTION_OUT)) {
+ status.direction() = GPIO__PIN__DIRECTION::OUT;
+ } else {
+ TTCN_error("Invalid direction when reading direction of %s", pin.c_str());
+ }
+
+ string value = get_value_of_pin();
+ if (0 == value.compare(GPIO_VALUE_LOW)) {
+ status.val() = GPIO__PIN__VALUE::LOW;
+ } else if (0 == value.compare(GPIO_VALUE_HIGH)) {
+ status.val() = GPIO__PIN__VALUE::HIGH;
+ } else {
+ TTCN_error("Invalid value when reading value of %s\n", pin.c_str());
+ }
+ } else {
+ status.is__exported() = false;
+ }
+ incoming_message(status);
+}
+
+//Utility functions
+void GPIO__Pin__Port::export_pin() {
+ if (!file_exists(get_pin_dir().c_str())) {
+ printf("Exporting gpio%s... (export file: %s)\n", pin_num.c_str(), export_file);
+ ofstream exfile;
+ exfile.exceptions(ofstream::failbit | ofstream::badbit);
+ exfile.open (export_file);
+ exfile << pin_num.c_str();
+ } else {
+ printf("===WARNING=== %s was already exported!\n",pin.c_str());
+ }
+}
+
+void GPIO__Pin__Port::unexport_pin() {
+ ofstream unexfile;
+ unexfile.exceptions(ofstream::failbit | ofstream::badbit);
+ unexfile.open (unexport_file);
+ string pin_s(pin_num);
+ unexfile << pin_s.c_str();
+
+}
+
+
+void GPIO__Pin__Port::set_direction_of_pin(const string direction) {
+ string pin_direction_filename = get_pin_dir()+"/direction";
+ printf("Set direction of %s to %s\n", pin.c_str(), direction.c_str());
+ ofstream pin_direction_file;
+ pin_direction_file.exceptions(ofstream::failbit | ofstream::badbit);
+ pin_direction_file.open (pin_direction_filename.c_str());
+ pin_direction_file << direction.c_str();
+ pin_direction_file.close();
+}
+
+
+string GPIO__Pin__Port::get_direction_of_pin() {
+ string pin_direction_filename = get_pin_dir()+"/direction";
+ string line;
+ ifstream pin_direction_file;
+ pin_direction_file.exceptions(ifstream::failbit | ifstream::badbit);
+ pin_direction_file.open (pin_direction_filename.c_str());
+ getline(pin_direction_file,line);
+ pin_direction_file.close();
+ return line;
+}
+
+void GPIO__Pin__Port::set_value_of_pin(const string value) {
+ string pin_value_filename = get_pin_dir()+"/value";
+ printf("Set value of %s to %s\n", pin.c_str(), value.c_str());
+ ofstream pin_value_file;
+ pin_value_file.exceptions(ofstream::failbit | ofstream::badbit);
+ pin_value_file.open (pin_value_filename.c_str());
+ pin_value_file << value.c_str();
+ pin_value_file.close();
+}
+
+string GPIO__Pin__Port::get_value_of_pin() {
+ string pin_value_filename = get_pin_dir()+"/value";
+ string line;
+ ifstream pin_value_file;
+ pin_value_file.exceptions(ifstream::failbit | ifstream::badbit);
+ pin_value_file.open (pin_value_filename.c_str());
+ getline(pin_value_file,line);
+ pin_value_file.close();
+ return line;
+}
+
+void GPIO__Pin__Port::set_edge_on_pin(const string edge) {
+ string pin_edge_filename = get_pin_dir()+"/edge";
+ printf("Set edge to \"%s\" on %s\n", edge.c_str(),pin.c_str());
+ ofstream pin_edge_file;
+ pin_edge_file.exceptions(ofstream::failbit | ofstream::badbit);
+ pin_edge_file.open (pin_edge_filename.c_str());
+ pin_edge_file << edge.c_str();
+ pin_edge_file.close();
+ if (edge != GPIO_EDGE_NONE) {
+ int fd = open(pin_edge_filename.c_str(),O_RDONLY);
+ register_fd_handler(fd);
+ }
+
+}
+
+void GPIO__Pin__Port::register_fd_handler(int fd) {
+ if (fd > 0) {
+ Handler_Add_Fd_Read(fd);
+ fd_edge = fd;
+ }
+}
+
+void GPIO__Pin__Port::unregister_fd_handler(int fd) {
+ if (fd > 0) {
+ Handler_Remove_Fd_Read(fd);
+ close(fd);
+ fd_edge = -1;
+ }
+}
+
+
+string GPIO__Pin__Port::int2string(int i) {
+ string str;
+ stringstream ss;
+ ss << i;
+ ss >> str;
+ return str;
+}
+
+int GPIO__Pin__Port::string2int(const char* str) {
+ stringstream strValue;
+ strValue << str;
+
+ unsigned int intValue;
+ strValue >> intValue;
+
+ return intValue;
+}
+
+void GPIO__Pin__Port::removechars(char* s, char c) {
+ int writer = 0, reader = 0;
+ while (s[reader])
+ {
+ if (s[reader]!=c)
+ {
+ s[writer++] = s[reader];
+ }
+ reader++;
+ }
+ s[writer]=0;
+}
+
+bool GPIO__Pin__Port::file_exists (const std::string& name) {
+ return ( access( name.c_str(), F_OK ) != -1 );
+}
+
+string GPIO__Pin__Port::get_pin_dir() {
+ return string(gpio_dir)+pin;
+}
+
+} /* end of namespace */
+
diff --git a/src/GPIO_Pin_Port.hh b/src/GPIO_Pin_Port.hh
new file mode 100644
index 0000000..41783cc
--- /dev/null
+++ b/src/GPIO_Pin_Port.hh
@@ -0,0 +1,79 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2000-2017 Ericsson Telecom AB
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// which accompanies this distribution, and is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// Contributors:
+// Lenard Nagy
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef GPIO__Pin__Port_HH
+#define GPIO__Pin__Port_HH
+
+#include "GPIOPinPort.hh"
+#include <iostream>
+#include <fstream>
+#include <string>
+#include <sstream>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+using namespace std;
+
+
+namespace GPIOPinPort {
+
+class GPIO__Pin__Port : public GPIO__Pin__Port_BASE {
+public:
+ GPIO__Pin__Port(const char *par_port_name = NULL);
+ ~GPIO__Pin__Port();
+
+ void set_parameter(const char *parameter_name,
+ const char *parameter_value);
+
+private:
+ /* void Handle_Fd_Event(int fd, boolean is_readable,
+ boolean is_writable, boolean is_error); */
+ void Handle_Fd_Event_Error(int fd);
+ void Handle_Fd_Event_Writable(int fd);
+ void Handle_Fd_Event_Readable(int fd);
+ /* void Handle_Timeout(double time_since_last_call); */
+protected:
+ string pin; //Will contain the gpio pin name, e.g. "gpio24"
+ string pin_num; //Will contain only the gpio pin number, e.g. "24"
+
+ int fd_edge;
+
+ void user_map(const char *system_port);
+ void user_unmap(const char *system_port);
+
+ void user_start();
+ void user_stop();
+
+ void outgoing_send(const GPIO__PIN__DIRECTION& send_par);
+ void outgoing_send(const GPIO__PIN__VALUE& send_par);
+ void outgoing_send(const GPIO__PIN__STATUS& send_par);
+
+ void export_pin();
+ void unexport_pin();
+ void set_direction_of_pin(const string direction);
+ string get_direction_of_pin();
+ void set_value_of_pin(const string value);
+ string get_value_of_pin();
+ void set_edge_on_pin(const string edge);
+ void register_fd_handler(int fd);
+ void unregister_fd_handler(int fd);
+ string int2string(int i);
+ int string2int(const char* str);
+ void removechars(char* s, char c);
+ bool file_exists (const std::string& name);
+ string get_pin_dir();
+ GPIO__PIN__DIRECTION original_value;
+ };
+
+} /* end of namespace */
+
+#endif