diff --git a/master/foe_request.c b/master/foe_request.c
index 66a65e8f..440beab3 100644
--- a/master/foe_request.c
+++ b/master/foe_request.c
@@ -61,6 +61,7 @@ void ec_foe_request_init(
INIT_LIST_HEAD(&req->list);
req->buffer = NULL;
req->file_name = file_name;
+ req->password = 0;
req->buffer_size = 0;
req->data_size = 0;
req->dir = EC_DIR_INVALID;
diff --git a/master/foe_request.h b/master/foe_request.h
index 25b9e638..c2a4982d 100644
--- a/master/foe_request.h
+++ b/master/foe_request.h
@@ -65,6 +65,7 @@ typedef struct {
unsigned long jiffies_sent; /**< Jiffies, when the upload/download
request was sent. */
uint8_t *file_name; /**< Pointer to the filename. */
+ uint32_t password; /**< Password needed for FoE. */
uint32_t result; /**< FoE request abort code. Zero on success. */
uint32_t error_code; /**< Error code from an FoE Error Request. */
} ec_foe_request_t;
diff --git a/master/fsm_foe.c b/master/fsm_foe.c
index dd998234..a26c2fd1 100644
--- a/master/fsm_foe.c
+++ b/master/fsm_foe.c
@@ -297,7 +297,7 @@ int ec_foe_prepare_wrq_send(
}
EC_WRITE_U16( data, EC_FOE_OPCODE_WRQ); // fsm write request
- EC_WRITE_U32( data + 2, fsm->tx_packet_no );
+ EC_WRITE_U32( data + 2, fsm->request->password );
#ifdef DEBUG_FOE
EC_SLAVE_DBG(fsm->slave, 0, "sending opcode %u packet %u\n",
EC_FOE_OPCODE_WRQ, fsm->tx_packet_no);
@@ -589,7 +589,7 @@ int ec_foe_prepare_rrq_send(
}
EC_WRITE_U16(data, EC_FOE_OPCODE_RRQ); // fsm read request
- EC_WRITE_U32(data + 2, 0x00000000); // no passwd
+ EC_WRITE_U32(data + 2, fsm->request->password);
memcpy(data + EC_FOE_HEADER_SIZE, fsm->rx_filename, current_size);
#ifdef DEBUG_FOE
EC_SLAVE_DBG(fsm->slave, 0, "sending opcode %u\n", EC_FOE_OPCODE_RRQ);
diff --git a/master/ioctl.c b/master/ioctl.c
index 01020687..ee54733e 100755
--- a/master/ioctl.c
+++ b/master/ioctl.c
@@ -4100,12 +4100,13 @@ static ATTRIBUTES int ec_ioctl_slave_foe_read(
}
ec_foe_request_init(&request, io.file_name);
- ret = ec_foe_request_alloc(&request, 10000); // FIXME
+ ret = ec_foe_request_alloc(&request, io.buffer_size); // FIXME
if (ret) {
ec_foe_request_clear(&request);
return ret;
}
+ request.password = io.password;
ec_foe_request_read(&request);
if (down_interruptible(&master->master_sem)) {
@@ -4210,6 +4211,7 @@ static ATTRIBUTES int ec_ioctl_slave_foe_write(
}
request.data_size = io.buffer_size;
+ request.password = io.password;
ec_foe_request_write(&request);
if (down_interruptible(&master->master_sem)) {
diff --git a/master/ioctl.h b/master/ioctl.h
index 67ea95ff..67fefd7d 100644
--- a/master/ioctl.h
+++ b/master/ioctl.h
@@ -426,6 +426,7 @@ typedef struct {
typedef struct {
// inputs
+ uint32_t password;
uint16_t slave_position;
uint16_t offset;
size_t buffer_size;
diff --git a/script/ethercat.bash_completion b/script/ethercat.bash_completion
new file mode 100644
index 00000000..7c9e6104
--- /dev/null
+++ b/script/ethercat.bash_completion
@@ -0,0 +1,86 @@
+# Copyright (C) 2022 Bjarne von Horn, Ingenieurgemeinschaft IgH
+#
+# This file is part of the IgH EtherCAT master userspace library.
+#
+# The IgH EtherCAT master userspace library is free software; you can
+# redistribute it and/or modify it under the terms of the GNU Lesser General
+# Public License as published by the Free Software Foundation; version 2.1
+# of the License.
+#
+# The IgH EtherCAT master userspace library is distributed in the hope that
+# it will be useful, but WITHOUT ANY WARRANTY; without even the implied
+# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with the IgH EtherCAT master userspace library. If not, see
+# .
+#
+# ---
+#
+# The license mentioned above concerns the source code only. Using the
+# EtherCAT technology and brand is only permitted in compliance with the
+# industrial property and similar rights of Beckhoff Automation GmbH.
+
+
+_ethercat_completions()
+{
+ local ethercat_commands="alias confic crc cstruct data debug domains download eoe foe_read foe_write graph master pdos reg_read reg_write rescan sdos sii_read sii_write slaves soe_read soe_write states upload version xml"
+ local options="--help --force --quiet --verbose --master "
+ if [ "$COMP_CWORD" -eq 1 ] ; then
+ COMPREPLY=($(compgen -W "$ethercat_commands --help" -- "${COMP_WORDS[1]}"))
+ elif [[ "${COMP_WORDS[1]}" != "--help" && ! "${COMP_WORDS[COMP_CWORD-1]}" =~ ^-a|-p|--alias|--position$ ]] ; then
+ case "${COMP_WORDS[1]}" in
+ "alias" | "config" | "cstruct" | "slaves" | "sdos" | "sii_read" | "upload" | "xml")
+ options+="--alias --position"
+ ;;
+ "crc")
+ options+="reset"
+ ;;
+ "debug")
+ options+="0 1 2"
+ ;;
+ "domains")
+ options+="--domain"
+ ;;
+ "download" | "reg_read" | "soe_read" | "soe_write")
+ if [[ "${COMP_WORDS[COMP_CWORD-1]}" =~ ^-t|--type$ ]] ; then
+ options="bool int8 int16 int32 int64 uint8 uint16 uint32 uint64 float double string octet_string unicode_string sm8 sm16 sm32 sm64"
+ else
+ options+="--alias --position --type"
+ fi
+ ;;
+ "foe_read" | "foe_write")
+ if [[ "${COMP_WORDS[COMP_CWORD-1]}" =~ ^-o|--output-file$ ]] ; then
+ COMPREPLY=($(compgen -o filenames -A file -- "${COMP_WORDS[$COMP_CWORD]}"))
+ else
+ options+="--alias --position --output-file"
+ COMPREPLY=($(compgen -o filenames -A file -W "$options" -- "${COMP_WORDS[$COMP_CWORD]}"))
+ fi
+ return
+ ;;
+ "graph")
+ options+="DC CRC"
+ ;;
+ "pdos")
+ if [[ "${COMP_WORDS[COMP_CWORD-1]}" =~ ^-s|--skin$ ]] ; then
+ options="default etherlab"
+ else
+ options+="--alias --position --skin"
+ fi
+ ;;
+ "sii_write")
+ options+="--alias --position"
+ COMPREPLY=($(compgen -o filenames -A file -W "$options" -- "${COMP_WORDS[$COMP_CWORD]}"))
+ return
+ ;;
+ "states")
+ options+="--alias --position INIT PREOP BOOT SAFEOP OP"
+ ;;
+
+ esac
+ COMPREPLY+=($(compgen -W "$options" -- "${COMP_WORDS[$COMP_CWORD]}"))
+ fi
+}
+
+complete -F _ethercat_completions ethercat
diff --git a/tool/CommandFoeRead.cpp b/tool/CommandFoeRead.cpp
index c0666a44..308ffa0f 100644
--- a/tool/CommandFoeRead.cpp
+++ b/tool/CommandFoeRead.cpp
@@ -31,6 +31,7 @@
#include
#include
+#include
using namespace std;
#include "CommandFoeRead.h"
@@ -51,7 +52,7 @@ string CommandFoeRead::helpString(const string &binaryBaseName) const
stringstream str;
str << binaryBaseName << " " << getName()
- << " [OPTIONS] " << endl
+ << " [OPTIONS] []" << endl
<< endl
<< getBriefDescription() << endl
<< endl
@@ -59,6 +60,7 @@ string CommandFoeRead::helpString(const string &binaryBaseName) const
<< endl
<< "Arguments:" << endl
<< " SOURCEFILE is the name of the source file on the slave." << endl
+ << " PASSWORD is the numeric password defined by the vendor." << endl
<< endl
<< "Command-specific options:" << endl
<< " --output-file -o Local target filename. If" << endl
@@ -80,11 +82,12 @@ void CommandFoeRead::execute(const StringVector &args)
SlaveList slaves;
ec_ioctl_slave_t *slave;
ec_ioctl_slave_foe_t data;
- unsigned int i;
stringstream err;
+ fstream out_file;
+ ostream* out = &cout;
- if (args.size() != 1) {
- err << "'" << getName() << "' takes exactly one argument!";
+ if (args.size() < 1 || args.size() > 2) {
+ err << "'" << getName() << "' takes one or two arguments!";
throwInvalidUsageException(err);
}
@@ -98,14 +101,36 @@ void CommandFoeRead::execute(const StringVector &args)
slave = &slaves.front();
data.slave_position = slave->position;
+ if (!getOutputFile().empty() && getOutputFile() != "-") {
+ out_file.open(getOutputFile().c_str(), ios::out | ios::trunc | ios::binary);
+ if (!out_file.good()) {
+ err << "Failed to open '" << getOutputFile() << "'";
+ throwCommandException(err);
+ }
+ out = &out_file;
+ }
+
/* FIXME: No good idea to have a fixed buffer size.
* Read in chunks and fill a buffer instead.
*/
+ data.password = 0;
data.offset = 0;
data.buffer_size = 0x8800;
data.buffer = new uint8_t[data.buffer_size];
strncpy(data.file_name, args[0].c_str(), sizeof(data.file_name) - 1);
+ data.file_name[sizeof(data.file_name)-1] = 0;
+ if (args.size() >= 2) {
+ stringstream strPassword;
+ strPassword << args[1];
+ strPassword
+ >> resetiosflags(ios::basefield) // guess base from prefix
+ >> data.password;
+ if (strPassword.fail()) {
+ err << "Invalid password '" << args[1] << "'!";
+ throwInvalidUsageException(err);
+ }
+ }
try {
m.readFoe(&data);
@@ -126,12 +151,7 @@ void CommandFoeRead::execute(const StringVector &args)
}
}
- // TODO --output-file
- for (i = 0; i < data.data_size; i++) {
- uint8_t *w = data.buffer + i;
- cout << *(uint8_t *) w ;
- }
-
+ out->write((const char*) data.buffer, data.data_size);
delete [] data.buffer;
}
diff --git a/tool/CommandFoeWrite.cpp b/tool/CommandFoeWrite.cpp
index 58a12117..c24ba8e5 100644
--- a/tool/CommandFoeWrite.cpp
+++ b/tool/CommandFoeWrite.cpp
@@ -53,7 +53,7 @@ string CommandFoeWrite::helpString(const string &binaryBaseName) const
stringstream str;
str << binaryBaseName << " " << getName()
- << " [OPTIONS] " << endl
+ << " [OPTIONS] []" << endl
<< endl
<< getBriefDescription() << endl
<< endl
@@ -63,6 +63,7 @@ string CommandFoeWrite::helpString(const string &binaryBaseName) const
<< " FILENAME can either be a path to a file, or '-'. In" << endl
<< " the latter case, data are read from stdin and" << endl
<< " the --output-file option has to be specified." << endl
+ << " PASSWORD is the numeric password defined by the vendor." << endl
<< endl
<< "Command-specific options:" << endl
<< " --output-file -o Target filename on the slave." << endl
@@ -89,8 +90,8 @@ void CommandFoeWrite::execute(const StringVector &args)
SlaveList slaves;
string storeFileName;
- if (args.size() != 1) {
- err << "'" << getName() << "' takes exactly one argument!";
+ if (args.size() < 1 || args.size() > 2) {
+ err << "'" << getName() << "' takes one or two arguments!";
throwInvalidUsageException(err);
}
@@ -140,8 +141,21 @@ void CommandFoeWrite::execute(const StringVector &args)
// write data via foe to the slave
data.offset = 0;
+ data.password = 0;
strncpy(data.file_name, storeFileName.c_str(),
sizeof(data.file_name) - 1);
+ data.file_name[sizeof(data.file_name)-1] = 0;
+ if (args.size() >= 2) {
+ stringstream strPassword;
+ strPassword << args[1];
+ strPassword
+ >> resetiosflags(ios::basefield) // guess base from prefix
+ >> data.password;
+ if (strPassword.fail()) {
+ err << "Invalid password '" << args[1] << "'!";
+ throwInvalidUsageException(err);
+ }
+ }
try {
m.writeFoe(&data);