# HG changeset patch # User Florian Pose # Date 1348147705 -7200 # Node ID 3bdd7a747fae5f3092ce62200427d12dc6b8b59b # Parent f4313f5aba88bbb2526d4ff69a6316b7c8b94730 Re-worked and seamlessly integrated RTDM interface. diff -r f4313f5aba88 -r 3bdd7a747fae .hgignore --- a/.hgignore Thu Sep 20 09:20:51 2012 +0200 +++ b/.hgignore Thu Sep 20 15:28:25 2012 +0200 @@ -100,12 +100,18 @@ examples/rtai/Kbuild examples/rtai/Makefile examples/rtai/Makefile.in +examples/rtai_rtdm/.libs +examples/rtai_rtdm/ec_rtai_rtdm_example examples/user/.deps examples/user/.libs examples/user/Makefile examples/user/Makefile.in examples/user/TAGS examples/user/ec_user_example +examples/xenomai/.libs +examples/xenomai/ec_xenomai_example +examples/xenomai_posix/.libs +examples/xenomai_posix/ec_xenomai_posix_example include/Makefile include/Makefile.in include/TAGS @@ -115,6 +121,7 @@ lib/Makefile.in lib/TAGS lib/libethercat.la +lib/libethercat_rtdm.la libtool m4/Makefile m4/Makefile.in diff -r f4313f5aba88 -r 3bdd7a747fae FEATURES --- a/FEATURES Thu Sep 20 09:20:51 2012 +0200 +++ b/FEATURES Thu Sep 20 15:28:25 2012 +0200 @@ -27,6 +27,7 @@ * Supports any realtime environment through independent architecture. - RTAI, Xenomai, RT-Preempt, etc. + - RTDM Interface for userspace realtime enviroments - Operation possible without any realtime extension at all. * Common API for Realtime-Applications in kernel- and userspace. diff -r f4313f5aba88 -r 3bdd7a747fae configure.ac --- a/configure.ac Thu Sep 20 09:20:51 2012 +0200 +++ b/configure.ac Thu Sep 20 15:28:25 2012 +0200 @@ -426,13 +426,15 @@ AC_ARG_WITH([rtai-dir], AC_HELP_STRING( [--with-rtai-dir=], - [RTAI path (only for RTAI examples)] + [RTAI path, for RTDM interface and RTAI examples] ), [ rtaidir=[$withval] + rtai=1 ], [ rtaidir="" + rtai=0 ] ) @@ -445,9 +447,99 @@ AC_MSG_ERROR([no RTAI installation found in ${rtaidir}!]) fi AC_MSG_RESULT([$rtaidir]) + + rtai_lxrt_cflags=`$rtaidir/bin/rtai-config --lxrt-cflags` + rtai_lxrt_ldflags=`$rtaidir/bin/rtai-config --lxrt-ldflags` fi AC_SUBST(RTAI_DIR,[$rtaidir]) +AM_CONDITIONAL(ENABLE_RTAI, test "x$rtai" = "x1") +AC_SUBST(ENABLE_RTAI,[$rtai]) + +AC_SUBST(RTAI_LXRT_CFLAGS,[$rtai_lxrt_cflags]) +AC_SUBST(RTAI_LXRT_LDFLAGS,[$rtai_lxrt_ldflags]) + +#------------------------------------------------------------------------------ +# Xenomai path (optional) +#------------------------------------------------------------------------------ + +AC_ARG_WITH([xenomai-dir], + AC_HELP_STRING( + [--with-xenomai-dir=], + [Xenomai path, for RTDM interface and Xenomai examples] + ), + [ + xenomaidir=[$withval] + xeno=1 + ], + [ + xenomaidir="" + xeno=0 + ] +) + +AC_MSG_CHECKING([for Xenomai path]) + +if test -z "${xenomaidir}"; then + AC_MSG_RESULT([not specified.]) +else + if test \! -r ${xenomaidir}/include/xeno_config.h; then + AC_MSG_ERROR([no Xenomai installation found in ${xenomaidir}!]) + fi + AC_MSG_RESULT([$xenomaidir]) + + xeno_native_cflags=`$xenomaidir/bin/xeno-config --skin native --cflags` + xeno_native_ldflags=`$xenomaidir/bin/xeno-config --skin native --ldflags` + xeno_posix_cflags=`$xenomaidir/bin/xeno-config --skin posix --cflags` + xeno_posix_ldflags=`$xenomaidir/bin/xeno-config --skin posix --ldflags` + xeno_rtdm_cflags=`$xenomaidir/bin/xeno-config --skin rtdm --cflags` + xeno_rtdm_ldflags=`$xenomaidir/bin/xeno-config --skin rtdm --ldflags` +fi + +AC_SUBST(XENOMAI_DIR,[$xenomaidir]) +AM_CONDITIONAL(ENABLE_XENOMAI, test "x$xeno" = "x1") +AC_SUBST(ENABLE_XENOMAI,[$xeno]) + +AC_SUBST(XENOMAI_NATIVE_CFLAGS,[$xeno_native_cflags]) +AC_SUBST(XENOMAI_NATIVE_LDFLAGS,[$xeno_native_ldflags]) +AC_SUBST(XENOMAI_POSIX_CFLAGS,[$xeno_posix_cflags]) +AC_SUBST(XENOMAI_POSIX_LDFLAGS,[$xeno_posix_ldflags]) +AC_SUBST(XENOMAI_RTDM_CFLAGS,[$xeno_rtdm_cflags]) +AC_SUBST(XENOMAI_RTDM_LDFLAGS,[$xeno_rtdm_ldflags]) + +#------------------------------------------------------------------------------ +# RTDM interface (optional) +#------------------------------------------------------------------------------ + +AC_ARG_ENABLE([rtdm], + AC_HELP_STRING( + [--enable-rtdm], + [Enable RTDM interface, depends on RTAI or Xenomai] + ), + [ + case "${enableval}" in + yes) rtdm=1 + ;; + no) rtdm=0 + ;; + *) AC_MSG_ERROR([Invalid value for --enable-rtdm]) + ;; + esac + ], + [rtdm=0] +) + +AC_MSG_CHECKING([whether to build RTDM interface]) + +if test "x${rtdm}" = "x1"; then + AC_DEFINE([EC_RTDM], [1], [RTDM interface enabled]) + AC_MSG_RESULT([yes]) +else + AC_MSG_RESULT([no]) +fi + +AM_CONDITIONAL(ENABLE_RTDM, test "x$rtdm" = "x1") +AC_SUBST(ENABLE_RTDM,[$rtdm]) #------------------------------------------------------------------------------ # Debug interface @@ -711,9 +803,12 @@ examples/mini/Makefile examples/rtai/Kbuild examples/rtai/Makefile + examples/rtai_rtdm/Makefile examples/tty/Kbuild examples/tty/Makefile examples/user/Makefile + examples/xenomai/Makefile + examples/xenomai_posix/Makefile include/Makefile lib/Makefile m4/Makefile diff -r f4313f5aba88 -r 3bdd7a747fae examples/Kbuild.in --- a/examples/Kbuild.in Thu Sep 20 09:20:51 2012 +0200 +++ b/examples/Kbuild.in Thu Sep 20 15:28:25 2012 +0200 @@ -2,7 +2,7 @@ # # $Id$ # -# Copyright (C) 2006-2008 Florian Pose, Ingenieurgemeinschaft IgH +# Copyright (C) 2006-2012 Florian Pose, Ingenieurgemeinschaft IgH # # This file is part of the IgH EtherCAT Master. # @@ -35,4 +35,8 @@ obj-m += tty/ endif +ifeq (@ENABLE_RTAI@,1) + obj-m += rtai/ dc_rtai/ +endif + #------------------------------------------------------------------------------ diff -r f4313f5aba88 -r 3bdd7a747fae examples/Makefile.am --- a/examples/Makefile.am Thu Sep 20 09:20:51 2012 +0200 +++ b/examples/Makefile.am Thu Sep 20 15:28:25 2012 +0200 @@ -2,7 +2,7 @@ # # $Id$ # -# Copyright (C) 2006-2008 Florian Pose, Ingenieurgemeinschaft IgH +# Copyright (C) 2006-2012 Florian Pose, Ingenieurgemeinschaft IgH # # This file is part of the IgH EtherCAT Master. # @@ -40,13 +40,29 @@ tty endif +if ENABLE_RTDM +if ENABLE_XENOMAI +SUBDIRS += \ + xenomai \ + xenomai_posix +endif + +if ENABLE_RTAI +SUBDIRS += \ + rtai_rtdm +endif +endif + DIST_SUBDIRS = \ dc_rtai \ dc_user \ mini \ rtai \ + rtai_rtdm \ tty \ - user + user \ + xenomai \ + xenomai_posix EXTRA_DIST = \ Kbuild.in diff -r f4313f5aba88 -r 3bdd7a747fae examples/rtai_rtdm/Makefile.am --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/rtai_rtdm/Makefile.am Thu Sep 20 15:28:25 2012 +0200 @@ -0,0 +1,43 @@ +#------------------------------------------------------------------------------ +# +# $Id$ +# +# Copyright (C) 2006-2012 Florian Pose, Ingenieurgemeinschaft IgH +# +# This file is part of the IgH EtherCAT Master. +# +# The IgH EtherCAT Master is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 2, as +# published by the Free Software Foundation. +# +# The IgH EtherCAT Master 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 General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along with +# the IgH EtherCAT Master; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# +# --- +# +# 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. +# +#------------------------------------------------------------------------------ + +noinst_PROGRAMS = ec_rtai_rtdm_example + +ec_rtai_rtdm_example_SOURCES = main.c + +ec_rtai_rtdm_example_CFLAGS = \ + -Wall \ + -I$(top_srcdir)/include \ + $(RTAI_LXRT_CFLAGS) + +ec_rtai_rtdm_example_LDFLAGS = \ + $(RTAI_LXRT_LDFLAGS) -llxrt -lrtdm \ + -L$(top_builddir)/lib/.libs -lethercat_rtdm + +#------------------------------------------------------------------------------ diff -r f4313f5aba88 -r 3bdd7a747fae examples/rtai_rtdm/main.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/rtai_rtdm/main.c Thu Sep 20 15:28:25 2012 +0200 @@ -0,0 +1,289 @@ +/****************************************************************************** + * + * $Id$ + * + * Copyright (C) 2011 IgH Andreas Stewering-Bone + * 2012 Florian Pose + * + * This file is part of the IgH EtherCAT master + * + * The IgH EtherCAT Master is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + * The IgH EtherCAT master 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 General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with the IgH EtherCAT master. 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. + * + *****************************************************************************/ + +#include +#include +#include +#include +#include + +#include +#include + +#include "ecrt.h" + +#define rt_printf(X, Y) + +#define NSEC_PER_SEC 1000000000 + +RT_TASK *task; + +const static unsigned int cycle_us = 1000; /* 1 ms */ + +static int run = 1; + +/****************************************************************************/ + +// EtherCAT +static ec_master_t *master = NULL; +static ec_master_state_t master_state = {}; + +static ec_domain_t *domain1 = NULL; +static ec_domain_state_t domain1_state = {}; + +static uint8_t *domain1_pd = NULL; + +static ec_slave_config_t *sc_dig_out_01 = NULL; + +/****************************************************************************/ + +// process data + +#define BusCoupler01_Pos 0, 0 +#define DigOutSlave01_Pos 0, 1 + +#define Beckhoff_EK1100 0x00000002, 0x044c2c52 +#define Beckhoff_EL2004 0x00000002, 0x07d43052 + +// offsets for PDO entries +static unsigned int off_dig_out0 = 0; + +// process data + +const static ec_pdo_entry_reg_t domain1_regs[] = { + {DigOutSlave01_Pos, Beckhoff_EL2004, 0x7000, 0x01, &off_dig_out0, NULL}, + {} +}; + +/****************************************************************************/ + +/* Slave 1, "EL2004" + * Vendor ID: 0x00000002 + * Product code: 0x07d43052 + * Revision number: 0x00100000 + */ + +ec_pdo_entry_info_t slave_1_pdo_entries[] = { + {0x7000, 0x01, 1}, /* Output */ + {0x7010, 0x01, 1}, /* Output */ + {0x7020, 0x01, 1}, /* Output */ + {0x7030, 0x01, 1}, /* Output */ +}; + +ec_pdo_info_t slave_1_pdos[] = { + {0x1600, 1, slave_1_pdo_entries + 0}, /* Channel 1 */ + {0x1601, 1, slave_1_pdo_entries + 1}, /* Channel 2 */ + {0x1602, 1, slave_1_pdo_entries + 2}, /* Channel 3 */ + {0x1603, 1, slave_1_pdo_entries + 3}, /* Channel 4 */ +}; + +ec_sync_info_t slave_1_syncs[] = { + {0, EC_DIR_OUTPUT, 4, slave_1_pdos + 0, EC_WD_ENABLE}, + {0xff} +}; + +/***************************************************************************** + * Realtime task + ****************************************************************************/ + +void rt_check_domain_state(void) +{ + ec_domain_state_t ds = {}; + + ecrt_domain_state(domain1, &ds); + + if (ds.working_counter != domain1_state.working_counter) { + rt_printf("Domain1: WC %u.\n", ds.working_counter); + } + + if (ds.wc_state != domain1_state.wc_state) { + rt_printf("Domain1: State %u.\n", ds.wc_state); + } + + domain1_state = ds; +} + +/****************************************************************************/ + +void rt_check_master_state(void) +{ + ec_master_state_t ms; + + ecrt_master_state(master, &ms); + + if (ms.slaves_responding != master_state.slaves_responding) { + rt_printf("%u slave(s).\n", ms.slaves_responding); + } + + if (ms.al_states != master_state.al_states) { + rt_printf("AL states: 0x%02X.\n", ms.al_states); + } + + if (ms.link_up != master_state.link_up) { + rt_printf("Link is %s.\n", ms.link_up ? "up" : "down"); + } + + master_state = ms; +} + +/****************************************************************************/ + +void my_cyclic(void) +{ + int cycle_counter = 0; + int period; + unsigned int blink = 0; + + rt_set_periodic_mode(); + period = (int) nano2count((RTIME) cycle_us * 1000); + start_rt_timer(period); + rt_make_hard_real_time(); + rt_task_make_periodic(task, rt_get_time() + 10 * period, period); + + while (run) { + rt_task_wait_period(); + + cycle_counter++; + + // receive EtherCAT + ecrt_master_receive(master); + ecrt_domain_process(domain1); + + rt_check_domain_state(); + + if (!(cycle_counter % 1000)) { + rt_check_master_state(); + } + + if (!(cycle_counter % 200)) { + blink = !blink; + } + + EC_WRITE_U8(domain1_pd + off_dig_out0, blink ? 0x00 : 0x0F); + + // send process data + ecrt_domain_queue(domain1); + ecrt_master_send(master); + } + + rt_make_soft_real_time(); + stop_rt_timer(); +} + +/**************************************************************************** + * Signal handler + ***************************************************************************/ + +void signal_handler(int sig) +{ + run = 0; +} + +/**************************************************************************** + * Main function + ***************************************************************************/ + +int main(int argc, char *argv[]) +{ + ec_slave_config_t *sc; + + signal(SIGTERM, signal_handler); + signal(SIGINT, signal_handler); + + mlockall(MCL_CURRENT | MCL_FUTURE); + + printf("Requesting master...\n"); + master = ecrt_request_master(0); + if (!master) { + return -1; + } + + domain1 = ecrt_master_create_domain(master); + if (!domain1) { + return -1; + } + + printf("Creating slave configurations...\n"); + + // Create configuration for bus coupler + sc = ecrt_master_slave_config(master, BusCoupler01_Pos, Beckhoff_EK1100); + if (!sc) { + return -1; + } + + sc_dig_out_01 = + ecrt_master_slave_config(master, DigOutSlave01_Pos, Beckhoff_EL2004); + if (!sc_dig_out_01) { + fprintf(stderr, "Failed to get slave configuration.\n"); + return -1; + } + + if (ecrt_slave_config_pdos(sc_dig_out_01, EC_END, slave_1_syncs)) { + fprintf(stderr, "Failed to configure PDOs.\n"); + return -1; + } + + if (ecrt_domain_reg_pdo_entry_list(domain1, domain1_regs)) { + fprintf(stderr, "PDO entry registration failed!\n"); + return -1; + } + + printf("Activating master...\n"); + if (ecrt_master_activate(master)) { + return -1; + } + + if (!(domain1_pd = ecrt_domain_data(domain1))) { + fprintf(stderr, "Failed to get domain data pointer.\n"); + return -1; + } + + /* Create cyclic RT-thread */ + struct sched_param param; + param.sched_priority = sched_get_priority_max(SCHED_FIFO) - 1; + if (sched_setscheduler(0, SCHED_FIFO, ¶m) == -1) { + puts("ERROR IN SETTING THE SCHEDULER"); + perror("errno"); + return -1; + } + + task = rt_task_init(nam2num("ec_rtai_rtdm_example"), + 0 /* priority */, 0 /* stack size */, 0 /* msg size */); + + my_cyclic(); + + rt_task_delete(task); + + printf("End of Program\n"); + ecrt_release_master(master); + + return 0; +} + +/****************************************************************************/ diff -r f4313f5aba88 -r 3bdd7a747fae examples/xenomai/Makefile.am --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/xenomai/Makefile.am Thu Sep 20 15:28:25 2012 +0200 @@ -0,0 +1,45 @@ +#------------------------------------------------------------------------------ +# +# $Id$ +# +# Copyright (C) 2006-2012 Florian Pose, Ingenieurgemeinschaft IgH +# +# This file is part of the IgH EtherCAT Master. +# +# The IgH EtherCAT Master is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 2, as +# published by the Free Software Foundation. +# +# The IgH EtherCAT Master 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 General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along with +# the IgH EtherCAT Master; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# +# --- +# +# 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. +# +#------------------------------------------------------------------------------ + +noinst_PROGRAMS = ec_xenomai_example + +ec_xenomai_example_SOURCES = main.c + +ec_xenomai_example_CFLAGS = \ + -Wall \ + -I$(top_srcdir)/include \ + $(XENOMAI_NATIVE_CFLAGS) \ + $(XENOMAI_RTDM_CFLAGS) + +ec_xenomai_example_LDFLAGS = \ + -L$(top_builddir)/lib/.libs -lethercat_rtdm \ + $(XENOMAI_NATIVE_LDFLAGS) \ + $(XENOMAI_RTDM_LDFLAGS) + +#------------------------------------------------------------------------------ diff -r f4313f5aba88 -r 3bdd7a747fae examples/xenomai/main.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/xenomai/main.c Thu Sep 20 15:28:25 2012 +0200 @@ -0,0 +1,292 @@ +/****************************************************************************** + * + * $Id$ + * + * Copyright (C) 2009-2010 Moehwald GmbH B. Benner + * 2011 IgH Andreas Stewering-Bone + * 2012 Florian Pose + * + * This file is part of the IgH EtherCAT master + * + * The IgH EtherCAT Master is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + * The IgH EtherCAT master 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 General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with the IgH EtherCAT master. 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. + * + *****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ecrt.h" + +RT_TASK my_task; + +static int run = 1; + +/****************************************************************************/ + +// EtherCAT +static ec_master_t *master = NULL; +static ec_master_state_t master_state = {}; + +static ec_domain_t *domain1 = NULL; +static ec_domain_state_t domain1_state = {}; + +static uint8_t *domain1_pd = NULL; + +static ec_slave_config_t *sc_dig_out_01 = NULL; + +/****************************************************************************/ + +// process data + +#define BusCoupler01_Pos 0, 0 +#define DigOutSlave01_Pos 0, 1 + +#define Beckhoff_EK1100 0x00000002, 0x044c2c52 +#define Beckhoff_EL2004 0x00000002, 0x07d43052 + +// offsets for PDO entries +static unsigned int off_dig_out0 = 0; + +// process data + +const static ec_pdo_entry_reg_t domain1_regs[] = { + {DigOutSlave01_Pos, Beckhoff_EL2004, 0x7000, 0x01, &off_dig_out0, NULL}, + {} +}; + +/****************************************************************************/ + +/* Slave 1, "EL2004" + * Vendor ID: 0x00000002 + * Product code: 0x07d43052 + * Revision number: 0x00100000 + */ + +ec_pdo_entry_info_t slave_1_pdo_entries[] = { + {0x7000, 0x01, 1}, /* Output */ + {0x7010, 0x01, 1}, /* Output */ + {0x7020, 0x01, 1}, /* Output */ + {0x7030, 0x01, 1}, /* Output */ +}; + +ec_pdo_info_t slave_1_pdos[] = { + {0x1600, 1, slave_1_pdo_entries + 0}, /* Channel 1 */ + {0x1601, 1, slave_1_pdo_entries + 1}, /* Channel 2 */ + {0x1602, 1, slave_1_pdo_entries + 2}, /* Channel 3 */ + {0x1603, 1, slave_1_pdo_entries + 3}, /* Channel 4 */ +}; + +ec_sync_info_t slave_1_syncs[] = { + {0, EC_DIR_OUTPUT, 4, slave_1_pdos + 0, EC_WD_ENABLE}, + {0xff} +}; + +/***************************************************************************** + * Realtime task + ****************************************************************************/ + +void rt_check_domain_state(void) +{ + ec_domain_state_t ds = {}; + + ecrt_domain_state(domain1, &ds); + + if (ds.working_counter != domain1_state.working_counter) { + rt_printf("Domain1: WC %u.\n", ds.working_counter); + } + + if (ds.wc_state != domain1_state.wc_state) { + rt_printf("Domain1: State %u.\n", ds.wc_state); + } + + domain1_state = ds; +} + +/****************************************************************************/ + +void rt_check_master_state(void) +{ + ec_master_state_t ms; + + ecrt_master_state(master, &ms); + + if (ms.slaves_responding != master_state.slaves_responding) { + rt_printf("%u slave(s).\n", ms.slaves_responding); + } + + if (ms.al_states != master_state.al_states) { + rt_printf("AL states: 0x%02X.\n", ms.al_states); + } + + if (ms.link_up != master_state.link_up) { + rt_printf("Link is %s.\n", ms.link_up ? "up" : "down"); + } + + master_state = ms; +} + +/****************************************************************************/ + +void my_task_proc(void *arg) +{ + int cycle_counter = 0; + unsigned int blink = 0; + + rt_task_set_periodic(NULL, TM_NOW, 1000000); // ns + + while (run) { + rt_task_wait_period(NULL); + + cycle_counter++; + + // receive EtherCAT frames + ecrt_master_receive(master); + ecrt_domain_process(domain1); + + rt_check_domain_state(); + + if (!(cycle_counter % 1000)) { + rt_check_master_state(); + } + + if (!(cycle_counter % 200)) { + blink = !blink; + } + + EC_WRITE_U8(domain1_pd + off_dig_out0, blink ? 0x0 : 0x0F); + + // send process data + ecrt_domain_queue(domain1); + ecrt_master_send(master); + } +} + +/**************************************************************************** + * Signal handler + ***************************************************************************/ + +void signal_handler(int sig) +{ + run = 0; +} + +/**************************************************************************** + * Main function + ***************************************************************************/ + +int main(int argc, char *argv[]) +{ + ec_slave_config_t *sc; + int ret; + + /* Perform auto-init of rt_print buffers if the task doesn't do so */ + rt_print_auto_init(1); + + signal(SIGTERM, signal_handler); + signal(SIGINT, signal_handler); + + mlockall(MCL_CURRENT | MCL_FUTURE); + + printf("Requesting master...\n"); + master = ecrt_request_master(0); + if (!master) { + return -1; + } + + domain1 = ecrt_master_create_domain(master); + if (!domain1) { + return -1; + } + + printf("Creating slave configurations...\n"); + + // Create configuration for bus coupler + sc = ecrt_master_slave_config(master, BusCoupler01_Pos, Beckhoff_EK1100); + if (!sc) { + return -1; + } + + sc_dig_out_01 = + ecrt_master_slave_config(master, DigOutSlave01_Pos, Beckhoff_EL2004); + if (!sc_dig_out_01) { + fprintf(stderr, "Failed to get slave configuration.\n"); + return -1; + } + + if (ecrt_slave_config_pdos(sc_dig_out_01, EC_END, slave_1_syncs)) { + fprintf(stderr, "Failed to configure PDOs.\n"); + return -1; + } + + if (ecrt_domain_reg_pdo_entry_list(domain1, domain1_regs)) { + fprintf(stderr, "PDO entry registration failed!\n"); + return -1; + } + + printf("Activating master...\n"); + if (ecrt_master_activate(master)) { + return -1; + } + + if (!(domain1_pd = ecrt_domain_data(domain1))) { + fprintf(stderr, "Failed to get domain data pointer.\n"); + return -1; + } + + ret = rt_task_create(&my_task, "my_task", 0, 80, T_FPU); + if (ret < 0) { + fprintf(stderr, "Failed to create task: %s\n", strerror(-ret)); + return -1; + } + + printf("Starting my_task...\n"); + ret = rt_task_start(&my_task, &my_task_proc, NULL); + if (ret < 0) { + fprintf(stderr, "Failed to start task: %s\n", strerror(-ret)); + return -1; + } + + while (run) { + sched_yield(); + } + + printf("Deleting realtime task...\n"); + rt_task_delete(&my_task); + + printf("End of Program\n"); + ecrt_release_master(master); + + return 0; +} + +/****************************************************************************/ diff -r f4313f5aba88 -r 3bdd7a747fae examples/xenomai_posix/Makefile.am --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/xenomai_posix/Makefile.am Thu Sep 20 15:28:25 2012 +0200 @@ -0,0 +1,45 @@ +#------------------------------------------------------------------------------ +# +# $Id$ +# +# Copyright (C) 2006-2012 Florian Pose, Ingenieurgemeinschaft IgH +# +# This file is part of the IgH EtherCAT Master. +# +# The IgH EtherCAT Master is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 2, as +# published by the Free Software Foundation. +# +# The IgH EtherCAT Master 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 General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along with +# the IgH EtherCAT Master; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# +# --- +# +# 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. +# +#------------------------------------------------------------------------------ + +noinst_PROGRAMS = ec_xenomai_posix_example + +ec_xenomai_posix_example_SOURCES = main.c + +ec_xenomai_posix_example_CFLAGS = \ + -Wall \ + -I$(top_srcdir)/include \ + $(XENOMAI_POSIX_CFLAGS) \ + $(XENOMAI_RTDM_CFLAGS) + +ec_xenomai_posix_example_LDFLAGS = \ + -L$(top_builddir)/lib/.libs -lethercat_rtdm \ + $(XENOMAI_POSIX_LDFLAGS) \ + $(XENOMAI_RTDM_LDFLAGS) + +#------------------------------------------------------------------------------ diff -r f4313f5aba88 -r 3bdd7a747fae examples/xenomai_posix/main.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/xenomai_posix/main.c Thu Sep 20 15:28:25 2012 +0200 @@ -0,0 +1,303 @@ +/****************************************************************************** + * + * $Id$ + * + * Copyright (C) 2011 IgH Andreas Stewering-Bone + * 2012 Florian Pose + * + * This file is part of the IgH EtherCAT master + * + * The IgH EtherCAT Master is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + * The IgH EtherCAT master 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 General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with the IgH EtherCAT master. 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. + * + *****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "ecrt.h" + +#define NSEC_PER_SEC 1000000000 + +static unsigned int cycle_us = 1000; /* 1 ms */ + +static pthread_t cyclic_thread; + +static int run = 1; + +/****************************************************************************/ + +// EtherCAT +static ec_master_t *master = NULL; +static ec_master_state_t master_state = {}; + +static ec_domain_t *domain1 = NULL; +static ec_domain_state_t domain1_state = {}; + +static uint8_t *domain1_pd = NULL; + +static ec_slave_config_t *sc_dig_out_01 = NULL; + +/****************************************************************************/ + +// process data + +#define BusCoupler01_Pos 0, 0 +#define DigOutSlave01_Pos 0, 1 + +#define Beckhoff_EK1100 0x00000002, 0x044c2c52 +#define Beckhoff_EL2004 0x00000002, 0x07d43052 + +// offsets for PDO entries +static unsigned int off_dig_out0 = 0; + +// process data + +const static ec_pdo_entry_reg_t domain1_regs[] = { + {DigOutSlave01_Pos, Beckhoff_EL2004, 0x7000, 0x01, &off_dig_out0, NULL}, + {} +}; + +/****************************************************************************/ + +/* Slave 1, "EL2004" + * Vendor ID: 0x00000002 + * Product code: 0x07d43052 + * Revision number: 0x00100000 + */ + +ec_pdo_entry_info_t slave_1_pdo_entries[] = { + {0x7000, 0x01, 1}, /* Output */ + {0x7010, 0x01, 1}, /* Output */ + {0x7020, 0x01, 1}, /* Output */ + {0x7030, 0x01, 1}, /* Output */ +}; + +ec_pdo_info_t slave_1_pdos[] = { + {0x1600, 1, slave_1_pdo_entries + 0}, /* Channel 1 */ + {0x1601, 1, slave_1_pdo_entries + 1}, /* Channel 2 */ + {0x1602, 1, slave_1_pdo_entries + 2}, /* Channel 3 */ + {0x1603, 1, slave_1_pdo_entries + 3}, /* Channel 4 */ +}; + +ec_sync_info_t slave_1_syncs[] = { + {0, EC_DIR_OUTPUT, 4, slave_1_pdos + 0, EC_WD_ENABLE}, + {0xff} +}; + +/***************************************************************************** + * Realtime task + ****************************************************************************/ + +void rt_check_domain_state(void) +{ + ec_domain_state_t ds = {}; + + ecrt_domain_state(domain1, &ds); + + if (ds.working_counter != domain1_state.working_counter) { + rt_printf("Domain1: WC %u.\n", ds.working_counter); + } + + if (ds.wc_state != domain1_state.wc_state) { + rt_printf("Domain1: State %u.\n", ds.wc_state); + } + + domain1_state = ds; +} + +/****************************************************************************/ + +void rt_check_master_state(void) +{ + ec_master_state_t ms; + + ecrt_master_state(master, &ms); + + if (ms.slaves_responding != master_state.slaves_responding) { + rt_printf("%u slave(s).\n", ms.slaves_responding); + } + + if (ms.al_states != master_state.al_states) { + rt_printf("AL states: 0x%02X.\n", ms.al_states); + } + + if (ms.link_up != master_state.link_up) { + rt_printf("Link is %s.\n", ms.link_up ? "up" : "down"); + } + + master_state = ms; +} + +/****************************************************************************/ + +void *my_thread(void *arg) +{ + struct timespec next_period; + int cycle_counter = 0; + unsigned int blink = 0; + + clock_gettime(CLOCK_REALTIME, &next_period); + + while (run) { + next_period.tv_nsec += cycle_us * 1000; + while (next_period.tv_nsec >= NSEC_PER_SEC) { + next_period.tv_nsec -= NSEC_PER_SEC; + next_period.tv_sec++; + } + + clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &next_period, NULL); + + cycle_counter++; + + // receive EtherCAT + ecrt_master_receive(master); + ecrt_domain_process(domain1); + + rt_check_domain_state(); + + if (!(cycle_counter % 1000)) { + rt_check_master_state(); + } + + if (!(cycle_counter % 200)) { + blink = !blink; + } + + EC_WRITE_U8(domain1_pd + off_dig_out0, blink ? 0x0 : 0x0F); + + // send process data + ecrt_domain_queue(domain1); + ecrt_master_send(master); + } + + return NULL; +} + +/**************************************************************************** + * Signal handler + ***************************************************************************/ + +void signal_handler(int sig) +{ + run = 0; +} + +/**************************************************************************** + * Main function + ***************************************************************************/ + +int main(int argc, char *argv[]) +{ + ec_slave_config_t *sc; + int ret; + + signal(SIGTERM, signal_handler); + signal(SIGINT, signal_handler); + + mlockall(MCL_CURRENT | MCL_FUTURE); + + printf("Requesting master...\n"); + master = ecrt_request_master(0); + if (!master) { + return -1; + } + + domain1 = ecrt_master_create_domain(master); + if (!domain1) { + return -1; + } + + printf("Creating slave configurations...\n"); + + // Create configuration for bus coupler + sc = ecrt_master_slave_config(master, BusCoupler01_Pos, Beckhoff_EK1100); + if (!sc) { + return -1; + } + + sc_dig_out_01 = + ecrt_master_slave_config(master, DigOutSlave01_Pos, Beckhoff_EL2004); + if (!sc_dig_out_01) { + fprintf(stderr, "Failed to get slave configuration.\n"); + return -1; + } + + if (ecrt_slave_config_pdos(sc_dig_out_01, EC_END, slave_1_syncs)) { + fprintf(stderr, "Failed to configure PDOs.\n"); + return -1; + } + + if (ecrt_domain_reg_pdo_entry_list(domain1, domain1_regs)) { + fprintf(stderr, "PDO entry registration failed!\n"); + return -1; + } + + printf("Activating master...\n"); + if (ecrt_master_activate(master)) { + return -1; + } + + if (!(domain1_pd = ecrt_domain_data(domain1))) { + fprintf(stderr, "Failed to get domain data pointer.\n"); + return -1; + } + + /* Create cyclic RT-thread */ + struct sched_param param = { .sched_priority = 82 }; + + pthread_attr_t thattr; + pthread_attr_init(&thattr); + pthread_attr_setdetachstate(&thattr, PTHREAD_CREATE_JOINABLE); + pthread_attr_setinheritsched(&thattr, PTHREAD_EXPLICIT_SCHED); + pthread_attr_setschedpolicy(&thattr, SCHED_FIFO); + pthread_setschedparam(cyclic_thread, SCHED_FIFO, ¶m); + + ret = pthread_create(&cyclic_thread, &thattr, &my_thread, NULL); + if (ret) { + fprintf(stderr, "%s: pthread_create cyclic task failed\n", + strerror(-ret)); + return 1; + } + + while (run) { + sched_yield(); + } + + pthread_join(cyclic_thread, NULL); + + printf("End of Program\n"); + ecrt_release_master(master); + + return 0; +} + +/****************************************************************************/ diff -r f4313f5aba88 -r 3bdd7a747fae include/ecrt.h --- a/include/ecrt.h Thu Sep 20 09:20:51 2012 +0200 +++ b/include/ecrt.h Thu Sep 20 15:28:25 2012 +0200 @@ -2,7 +2,7 @@ * * $Id$ * - * Copyright (C) 2006-2008 Florian Pose, Ingenieurgemeinschaft IgH + * Copyright (C) 2006-2012 Florian Pose, Ingenieurgemeinschaft IgH * * This file is part of the IgH EtherCAT master userspace library. * @@ -781,7 +781,7 @@ * error occurred. * * \retval 0 Success. - * \retval -1 An error occured. + * \retval <0 Error code. */ int ecrt_master_write_idn( ec_master_t *master, /**< EtherCAT master. */ @@ -800,7 +800,7 @@ * error occurred. * * \retval 0 Success. - * \retval -1 An error occured. + * \retval <0 Error code. */ int ecrt_master_read_idn( ec_master_t *master, /**< EtherCAT master. */ @@ -848,6 +848,10 @@ ); /** Set interval between calls to ecrt_master_send(). + * + * \retval 0 on success. + * \retval <0 Error code. + * */ int ecrt_master_set_send_interval( ec_master_t *master, /**< EtherCAT master. */ diff -r f4313f5aba88 -r 3bdd7a747fae lib/Makefile.am --- a/lib/Makefile.am Thu Sep 20 09:20:51 2012 +0200 +++ b/lib/Makefile.am Thu Sep 20 15:28:25 2012 +0200 @@ -2,7 +2,7 @@ # # $Id$ # -# Copyright (C) 2006-2009 Florian Pose, Ingenieurgemeinschaft IgH +# Copyright (C) 2006-2012 Florian Pose, Ingenieurgemeinschaft IgH # # This file is part of the IgH EtherCAT master userspace library. # @@ -32,8 +32,6 @@ #------------------------------------------------------------------------------ -libethercat_la_CFLAGS = -I$(srcdir)/.. -fno-strict-aliasing -Wall -libethercat_la_LDFLAGS = -version-info 1:0:0 libethercat_la_SOURCES = \ common.c \ domain.c \ @@ -49,4 +47,29 @@ slave_config.h \ voe_handler.h +libethercat_la_CFLAGS = -fno-strict-aliasing -Wall +libethercat_la_LDFLAGS = -version-info 1:0:0 + #------------------------------------------------------------------------------ + +if ENABLE_RTDM + +lib_LTLIBRARIES += libethercat_rtdm.la + +libethercat_rtdm_la_SOURCES = $(libethercat_la_SOURCES) +libethercat_rtdm_la_CFLAGS = $(libethercat_la_CFLAGS) -DUSE_RTDM +libethercat_rtdm_la_LDFLAGS = $(libethercat_la_LDFLAGS) + +if ENABLE_XENOMAI +libethercat_rtdm_la_CFLAGS += $(XENOMAI_RTDM_CFLAGS) +libethercat_rtdm_la_LDFLAGS += $(XENOMAI_RTDM_LDFLAGS) +endif + +if ENABLE_RTAI +libethercat_rtdm_la_CFLAGS += $(RTAI_LXRT_CFLAGS) +libethercat_rtdm_la_LDFLAGS += $(RTAI_LXRT_LDFLAGS) +endif + +endif + +#------------------------------------------------------------------------------ diff -r f4313f5aba88 -r 3bdd7a747fae lib/common.c --- a/lib/common.c Thu Sep 20 09:20:51 2012 +0200 +++ b/lib/common.c Thu Sep 20 15:28:25 2012 +0200 @@ -2,7 +2,7 @@ * * $Id$ * - * Copyright (C) 2006-2009 Florian Pose, Ingenieurgemeinschaft IgH + * Copyright (C) 2006-2012 Florian Pose, Ingenieurgemeinschaft IgH * * This file is part of the IgH EtherCAT master userspace library. * @@ -33,15 +33,13 @@ #include #include #include -#include #include -#include #include #include #include +#include "ioctl.h" #include "master.h" -#include "master/ioctl.h" /*****************************************************************************/ @@ -75,6 +73,7 @@ char path[MAX_PATH_LEN]; ec_master_t *master = NULL; ec_ioctl_module_t module_data; + int ret; master = malloc(sizeof(ec_master_t)); if (!master) { @@ -87,17 +86,29 @@ master->first_domain = NULL; master->first_config = NULL; - snprintf(path, MAX_PATH_LEN - 1, "/dev/EtherCAT%u", master_index); + snprintf(path, MAX_PATH_LEN - 1, +#ifdef USE_RTDM + "EtherCAT%u", +#else + "/dev/EtherCAT%u", +#endif + master_index); +#ifdef USE_RTDM + master->fd = rt_dev_open(path, O_RDWR); +#else master->fd = open(path, O_RDWR); - if (master->fd == -1) { - fprintf(stderr, "Failed to open %s: %s\n", path, strerror(errno)); +#endif + if (EC_IOCTL_IS_ERROR(master->fd)) { + fprintf(stderr, "Failed to open %s: %s\n", path, + strerror(EC_IOCTL_ERRNO(master->fd))); goto out_clear; } - if (ioctl(master->fd, EC_IOCTL_MODULE, &module_data) < 0) { + ret = ioctl(master->fd, EC_IOCTL_MODULE, &module_data); + if (EC_IOCTL_IS_ERROR(ret)) { fprintf(stderr, "Failed to get module information from %s: %s\n", - path, strerror(errno)); + path, strerror(EC_IOCTL_ERRNO(ret))); goto out_clear; } diff -r f4313f5aba88 -r 3bdd7a747fae lib/domain.c --- a/lib/domain.c Thu Sep 20 09:20:51 2012 +0200 +++ b/lib/domain.c Thu Sep 20 15:28:25 2012 +0200 @@ -2,7 +2,7 @@ * * $Id$ * - * Copyright (C) 2006-2009 Florian Pose, Ingenieurgemeinschaft IgH + * Copyright (C) 2006-2012 Florian Pose, Ingenieurgemeinschaft IgH * * This file is part of the IgH EtherCAT master userspace library. * @@ -35,15 +35,13 @@ /*****************************************************************************/ -#include -#include #include -#include #include +#include /* ENOENT */ +#include "ioctl.h" #include "domain.h" #include "master.h" -#include "master/ioctl.h" /*****************************************************************************/ @@ -64,11 +62,11 @@ for (reg = regs; reg->index; reg++) { if (!(sc = ecrt_master_slave_config(domain->master, reg->alias, reg->position, reg->vendor_id, reg->product_code))) - return -1; // FIXME + return -ENOENT; if ((ret = ecrt_slave_config_reg_pdo_entry(sc, reg->index, reg->subindex, domain, reg->bit_position)) < 0) - return -1; // FIXME + return ret; *reg->offset = ret; } @@ -85,9 +83,9 @@ offset = ioctl(domain->master->fd, EC_IOCTL_DOMAIN_OFFSET, domain->index); - if (offset == -1) { + if (EC_IOCTL_IS_ERROR(offset)) { fprintf(stderr, "Failed to get domain offset: %s\n", - strerror(errno)); + strerror(EC_IOCTL_ERRNO(offset))); return NULL; } @@ -101,9 +99,12 @@ void ecrt_domain_process(ec_domain_t *domain) { - if (ioctl(domain->master->fd, EC_IOCTL_DOMAIN_PROCESS, - domain->index) == -1) { - fprintf(stderr, "Failed to process domain: %s\n", strerror(errno)); + int ret; + + ret = ioctl(domain->master->fd, EC_IOCTL_DOMAIN_PROCESS, domain->index); + if (EC_IOCTL_IS_ERROR(ret)) { + fprintf(stderr, "Failed to process domain: %s\n", + strerror(EC_IOCTL_ERRNO(ret))); } } @@ -111,9 +112,12 @@ void ecrt_domain_queue(ec_domain_t *domain) { - if (ioctl(domain->master->fd, EC_IOCTL_DOMAIN_QUEUE, - domain->index) == -1) { - fprintf(stderr, "Failed to queue domain: %s\n", strerror(errno)); + int ret; + + ret = ioctl(domain->master->fd, EC_IOCTL_DOMAIN_QUEUE, domain->index); + if (EC_IOCTL_IS_ERROR(ret)) { + fprintf(stderr, "Failed to queue domain: %s\n", + strerror(EC_IOCTL_ERRNO(ret))); } } @@ -122,13 +126,15 @@ void ecrt_domain_state(const ec_domain_t *domain, ec_domain_state_t *state) { ec_ioctl_domain_state_t data; + int ret; data.domain_index = domain->index; data.state = state; - if (ioctl(domain->master->fd, EC_IOCTL_DOMAIN_STATE, &data) == -1) { + ret = ioctl(domain->master->fd, EC_IOCTL_DOMAIN_STATE, &data); + if (EC_IOCTL_IS_ERROR(ret)) { fprintf(stderr, "Failed to get domain state: %s\n", - strerror(errno)); + strerror(EC_IOCTL_ERRNO(ret))); } } diff -r f4313f5aba88 -r 3bdd7a747fae lib/ioctl.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/ioctl.h Thu Sep 20 15:28:25 2012 +0200 @@ -0,0 +1,73 @@ +/****************************************************************************** + * + * $Id$ + * + * Copyright (C) 2006-2012 Florian Pose, 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. + * + *****************************************************************************/ + +#ifndef __EC_LIB_IOCTL_H__ +#define __EC_LIB_IOCTL_H__ + +/*****************************************************************************/ + +#ifdef USE_RTDM +#include +#else +#include +#endif + +/*****************************************************************************/ + +#include "master/ioctl.h" + +/*****************************************************************************/ + +#ifdef USE_RTDM + +#define ioctl rt_dev_ioctl + +/* rt_dev_ioctl() returns negative error code */ +#define EC_IOCTL_IS_ERROR(X) ((X) < 0) +#define EC_IOCTL_ERRNO(X) (-(X)) + +#else + +#define ioctl ioctl + +/* libc's ioctl() always returns -1 on error and sets errno */ +#define EC_IOCTL_IS_ERROR(X) ((X) == -1) +#define EC_IOCTL_ERRNO(X) (errno) + +#include + +#endif + +/*****************************************************************************/ + +#endif /* __EC_LIB_IOCTL_H__ */ + +/*****************************************************************************/ + diff -r f4313f5aba88 -r 3bdd7a747fae lib/master.c --- a/lib/master.c Thu Sep 20 09:20:51 2012 +0200 +++ b/lib/master.c Thu Sep 20 15:28:25 2012 +0200 @@ -2,7 +2,7 @@ * * $Id$ * - * Copyright (C) 2006-2009 Florian Pose, Ingenieurgemeinschaft IgH + * Copyright (C) 2006-2012 Florian Pose, Ingenieurgemeinschaft IgH * * This file is part of the IgH EtherCAT master userspace library. * @@ -30,25 +30,25 @@ #include /* close() */ #include -#include #include #include #include #include +#include "ioctl.h" #include "master.h" #include "domain.h" #include "slave_config.h" -#include "master/ioctl.h" /****************************************************************************/ int ecrt_master_reserve(ec_master_t *master) { - if (ioctl(master->fd, EC_IOCTL_REQUEST, NULL) == -1) { + int ret = ioctl(master->fd, EC_IOCTL_REQUEST, NULL); + if (EC_IOCTL_IS_ERROR(ret)) { fprintf(stderr, "Failed to reserve master: %s\n", - strerror(errno)); - return -1; + strerror(EC_IOCTL_ERRNO(ret))); + return -EC_IOCTL_ERRNO(ret); } return 0; } @@ -88,7 +88,11 @@ ec_master_clear_config(master); if (master->fd != -1) { +#if USE_RTDM + rt_dev_close(master->fd); +#else close(master->fd); +#endif } } @@ -121,8 +125,9 @@ } index = ioctl(master->fd, EC_IOCTL_CREATE_DOMAIN, NULL); - if (index == -1) { - fprintf(stderr, "Failed to create domain: %s\n", strerror(errno)); + if (EC_IOCTL_IS_ERROR(index)) { + fprintf(stderr, "Failed to create domain: %s\n", + strerror(EC_IOCTL_ERRNO(index))); free(domain); return 0; } @@ -160,6 +165,7 @@ { ec_ioctl_config_t data; ec_slave_config_t *sc; + int ret; sc = malloc(sizeof(ec_slave_config_t)); if (!sc) { @@ -172,9 +178,10 @@ data.vendor_id = vendor_id; data.product_code = product_code; - if (ioctl(master->fd, EC_IOCTL_CREATE_SLAVE_CONFIG, &data) == -1) { + ret = ioctl(master->fd, EC_IOCTL_CREATE_SLAVE_CONFIG, &data); + if (EC_IOCTL_IS_ERROR(ret)) { fprintf(stderr, "Failed to create slave config: %s\n", - strerror(errno)); + strerror(EC_IOCTL_ERRNO(ret))); free(sc); return 0; } @@ -194,13 +201,16 @@ /****************************************************************************/ -int ecrt_master(ec_master_t* master, ec_master_info_t *master_info) +int ecrt_master(ec_master_t *master, ec_master_info_t *master_info) { ec_ioctl_master_t data; - - if (ioctl(master->fd, EC_IOCTL_MASTER, &data) < 0) { - fprintf(stderr, "Failed to get master info: %s\n", strerror(errno)); - return -1; + int ret; + + ret = ioctl(master->fd, EC_IOCTL_MASTER, &data); + if (EC_IOCTL_IS_ERROR(ret)) { + fprintf(stderr, "Failed to get master info: %s\n", + strerror(EC_IOCTL_ERRNO(ret))); + return -EC_IOCTL_ERRNO(ret); } master_info->slave_count = data.slave_count; @@ -216,13 +226,15 @@ ec_slave_info_t *slave_info) { ec_ioctl_slave_t data; - int i; + int ret, i; data.position = slave_position; - if (ioctl(master->fd, EC_IOCTL_SLAVE, &data) == -1) { - fprintf(stderr, "Failed to get slave info: %s\n", strerror(errno)); - return -1; + ret = ioctl(master->fd, EC_IOCTL_SLAVE, &data); + if (EC_IOCTL_IS_ERROR(ret)) { + fprintf(stderr, "Failed to get slave info: %s\n", + strerror(EC_IOCTL_ERRNO(ret))); + return -EC_IOCTL_ERRNO(ret); } slave_info->position = data.position; @@ -232,7 +244,7 @@ slave_info->serial_number = data.serial_number; slave_info->alias = data.alias; slave_info->current_on_ebus = data.current_on_ebus; - for ( i = 0; i < EC_MAX_PORTS; i++ ) { + for (i = 0; i < EC_MAX_PORTS; i++) { slave_info->ports[i].desc = data.ports[i].desc; slave_info->ports[i].link.link_up = data.ports[i].link.link_up; slave_info->ports[i].link.loop_closed = @@ -258,6 +270,7 @@ uint8_t sync_index, ec_sync_info_t *sync) { ec_ioctl_slave_sync_t data; + int ret; if (sync_index >= EC_MAX_SYNC_MANAGERS) { return -ENOENT; @@ -267,10 +280,11 @@ data.slave_position = slave_position; data.sync_index = sync_index; - if (ioctl(master->fd, EC_IOCTL_SLAVE_SYNC, &data) == -1) { + ret = ioctl(master->fd, EC_IOCTL_SLAVE_SYNC, &data); + if (EC_IOCTL_IS_ERROR(ret)) { fprintf(stderr, "Failed to get sync manager information: %s\n", - strerror(errno)); - return -1; // FIXME + strerror(EC_IOCTL_ERRNO(ret))); + return -EC_IOCTL_ERRNO(ret); } sync->index = sync_index; @@ -290,6 +304,7 @@ uint8_t sync_index, uint16_t pos, ec_pdo_info_t *pdo) { ec_ioctl_slave_sync_pdo_t data; + int ret; if (sync_index >= EC_MAX_SYNC_MANAGERS) return -ENOENT; @@ -299,10 +314,11 @@ data.sync_index = sync_index; data.pdo_pos = pos; - if (ioctl(master->fd, EC_IOCTL_SLAVE_SYNC_PDO, &data) == -1) { + ret = ioctl(master->fd, EC_IOCTL_SLAVE_SYNC_PDO, &data); + if (EC_IOCTL_IS_ERROR(ret)) { fprintf(stderr, "Failed to get pdo information: %s\n", - strerror(errno)); - return -1; // FIXME + strerror(EC_IOCTL_ERRNO(ret))); + return -EC_IOCTL_ERRNO(ret); } pdo->index = data.index; @@ -319,6 +335,7 @@ ec_pdo_entry_info_t *entry) { ec_ioctl_slave_sync_pdo_entry_t data; + int ret; if (sync_index >= EC_MAX_SYNC_MANAGERS) return -ENOENT; @@ -329,10 +346,11 @@ data.pdo_pos = pdo_pos; data.entry_pos = entry_pos; - if (ioctl(master->fd, EC_IOCTL_SLAVE_SYNC_PDO_ENTRY, &data) == -1) { + ret = ioctl(master->fd, EC_IOCTL_SLAVE_SYNC_PDO_ENTRY, &data); + if (EC_IOCTL_IS_ERROR(ret)) { fprintf(stderr, "Failed to get pdo entry information: %s\n", - strerror(errno)); - return -1; // FIXME + strerror(EC_IOCTL_ERRNO(ret))); + return -EC_IOCTL_ERRNO(ret); } entry->index = data.index; @@ -349,6 +367,7 @@ size_t data_size, uint32_t *abort_code) { ec_ioctl_slave_sdo_download_t download; + int ret; download.slave_position = slave_position; download.sdo_index = index; @@ -357,13 +376,14 @@ download.data_size = data_size; download.data = data; - if (ioctl(master->fd, EC_IOCTL_SLAVE_SDO_DOWNLOAD, &download) == -1) { - if (errno == EIO && abort_code) { + ret = ioctl(master->fd, EC_IOCTL_SLAVE_SDO_DOWNLOAD, &download); + if (EC_IOCTL_IS_ERROR(ret)) { + if (EC_IOCTL_ERRNO(ret) == EIO && abort_code) { *abort_code = download.abort_code; } fprintf(stderr, "Failed to execute SDO download: %s\n", - strerror(errno)); - return -1; + strerror(EC_IOCTL_ERRNO(ret))); + return -EC_IOCTL_ERRNO(ret); } return 0; @@ -376,6 +396,7 @@ size_t data_size, uint32_t *abort_code) { ec_ioctl_slave_sdo_download_t download; + int ret; download.slave_position = slave_position; download.sdo_index = index; @@ -384,13 +405,14 @@ download.data_size = data_size; download.data = data; - if (ioctl(master->fd, EC_IOCTL_SLAVE_SDO_DOWNLOAD, &download) == -1) { - if (errno == EIO && abort_code) { + ret = ioctl(master->fd, EC_IOCTL_SLAVE_SDO_DOWNLOAD, &download); + if (EC_IOCTL_IS_ERROR(ret)) { + if (EC_IOCTL_ERRNO(ret) == EIO && abort_code) { *abort_code = download.abort_code; } fprintf(stderr, "Failed to execute SDO download: %s\n", - strerror(errno)); - return -1; + strerror(EC_IOCTL_ERRNO(ret))); + return -EC_IOCTL_ERRNO(ret); } return 0; @@ -403,6 +425,7 @@ size_t target_size, size_t *result_size, uint32_t *abort_code) { ec_ioctl_slave_sdo_upload_t upload; + int ret; upload.slave_position = slave_position; upload.sdo_index = index; @@ -410,13 +433,14 @@ upload.target_size = target_size; upload.target = target; - if (ioctl(master->fd, EC_IOCTL_SLAVE_SDO_UPLOAD, &upload) == -1) { - if (errno == EIO && abort_code) { + ret = ioctl(master->fd, EC_IOCTL_SLAVE_SDO_UPLOAD, &upload); + if (EC_IOCTL_IS_ERROR(ret)) { + if (EC_IOCTL_ERRNO(ret) == EIO && abort_code) { *abort_code = upload.abort_code; } fprintf(stderr, "Failed to execute SDO upload: %s\n", - strerror(errno)); - return -1; + strerror(EC_IOCTL_ERRNO(ret))); + return -EC_IOCTL_ERRNO(ret); } *result_size = upload.data_size; @@ -430,6 +454,7 @@ uint16_t *error_code) { ec_ioctl_slave_soe_write_t io; + int ret; io.slave_position = slave_position; io.drive_no = drive_no; @@ -437,12 +462,14 @@ io.data_size = data_size; io.data = data; - if (ioctl(master->fd, EC_IOCTL_SLAVE_SOE_WRITE, &io) == -1) { - if (errno == EIO && error_code) { + ret = ioctl(master->fd, EC_IOCTL_SLAVE_SOE_WRITE, &io); + if (EC_IOCTL_IS_ERROR(ret)) { + if (EC_IOCTL_ERRNO(ret) == EIO && error_code) { *error_code = io.error_code; } - fprintf(stderr, "Failed to write IDN: %s\n", strerror(errno)); - return -1; + fprintf(stderr, "Failed to write IDN: %s\n", + strerror(EC_IOCTL_ERRNO(ret))); + return -EC_IOCTL_ERRNO(ret); } return 0; @@ -455,6 +482,7 @@ size_t *result_size, uint16_t *error_code) { ec_ioctl_slave_soe_read_t io; + int ret; io.slave_position = slave_position; io.drive_no = drive_no; @@ -462,12 +490,14 @@ io.mem_size = target_size; io.data = target; - if (ioctl(master->fd, EC_IOCTL_SLAVE_SOE_READ, &io) == -1) { - if (errno == EIO && error_code) { + ret = ioctl(master->fd, EC_IOCTL_SLAVE_SOE_READ, &io); + if (EC_IOCTL_IS_ERROR(ret)) { + if (EC_IOCTL_ERRNO(ret) == EIO && error_code) { *error_code = io.error_code; } - fprintf(stderr, "Failed to read IDN: %s\n", strerror(errno)); - return -1; + fprintf(stderr, "Failed to read IDN: %s\n", + strerror(EC_IOCTL_ERRNO(ret))); + return -EC_IOCTL_ERRNO(ret); } *result_size = io.data_size; @@ -478,23 +508,35 @@ int ecrt_master_activate(ec_master_t *master) { - if (ioctl(master->fd, EC_IOCTL_ACTIVATE, - &master->process_data_size) == -1) { + ec_ioctl_master_activate_t io; + int ret; + + ret = ioctl(master->fd, EC_IOCTL_ACTIVATE, &io); + if (EC_IOCTL_IS_ERROR(ret)) { fprintf(stderr, "Failed to activate master: %s\n", - strerror(errno)); - return -1; // FIXME - } + strerror(EC_IOCTL_ERRNO(ret))); + return -EC_IOCTL_ERRNO(ret); + } + + master->process_data_size = io.process_data_size; if (master->process_data_size) { +#ifdef USE_RTDM + /* memory-mapping was already done in kernel. The user-space addess is + * provided in the ioctl data. + */ + master->process_data = io.process_data; +#else master->process_data = mmap(0, master->process_data_size, PROT_READ | PROT_WRITE, MAP_SHARED, master->fd, 0); if (master->process_data == MAP_FAILED) { - fprintf(stderr, "Failed to map process data: %s", + fprintf(stderr, "Failed to map process data: %s\n", strerror(errno)); master->process_data = NULL; master->process_data_size = 0; - return -1; // FIXME + return -errno; } +#endif // Access the mapped region to cause the initial page fault master->process_data[0] = 0x00; @@ -507,8 +549,12 @@ void ecrt_master_deactivate(ec_master_t *master) { - if (ioctl(master->fd, EC_IOCTL_DEACTIVATE, NULL) == -1) { - fprintf(stderr, "Failed to deactivate master: %s\n", strerror(errno)); + int ret; + + ret = ioctl(master->fd, EC_IOCTL_DEACTIVATE, NULL); + if (EC_IOCTL_IS_ERROR(ret)) { + fprintf(stderr, "Failed to deactivate master: %s\n", + strerror(EC_IOCTL_ERRNO(ret))); return; } @@ -520,12 +566,15 @@ int ecrt_master_set_send_interval(ec_master_t *master, size_t send_interval_us) { - if (ioctl(master->fd, EC_IOCTL_SET_SEND_INTERVAL, - &send_interval_us) == -1) { + int ret; + + ret = ioctl(master->fd, EC_IOCTL_SET_SEND_INTERVAL, &send_interval_us); + if (EC_IOCTL_IS_ERROR(ret)) { fprintf(stderr, "Failed to set send interval: %s\n", - strerror(errno)); - return -1; // FIXME - } + strerror(EC_IOCTL_ERRNO(ret))); + return -EC_IOCTL_ERRNO(ret); + } + return 0; } @@ -533,8 +582,12 @@ void ecrt_master_send(ec_master_t *master) { - if (ioctl(master->fd, EC_IOCTL_SEND, NULL) == -1) { - fprintf(stderr, "Failed to send: %s\n", strerror(errno)); + int ret; + + ret = ioctl(master->fd, EC_IOCTL_SEND, NULL); + if (EC_IOCTL_IS_ERROR(ret)) { + fprintf(stderr, "Failed to send: %s\n", + strerror(EC_IOCTL_ERRNO(ret))); } } @@ -542,8 +595,12 @@ void ecrt_master_receive(ec_master_t *master) { - if (ioctl(master->fd, EC_IOCTL_RECEIVE, NULL) == -1) { - fprintf(stderr, "Failed to receive: %s\n", strerror(errno)); + int ret; + + ret = ioctl(master->fd, EC_IOCTL_RECEIVE, NULL); + if (EC_IOCTL_IS_ERROR(ret)) { + fprintf(stderr, "Failed to receive: %s\n", + strerror(EC_IOCTL_ERRNO(ret))); } } @@ -551,8 +608,12 @@ void ecrt_master_state(const ec_master_t *master, ec_master_state_t *state) { - if (ioctl(master->fd, EC_IOCTL_MASTER_STATE, state) == -1) { - fprintf(stderr, "Failed to get master state: %s\n", strerror(errno)); + int ret; + + ret = ioctl(master->fd, EC_IOCTL_MASTER_STATE, state); + if (EC_IOCTL_IS_ERROR(ret)) { + fprintf(stderr, "Failed to get master state: %s\n", + strerror(EC_IOCTL_ERRNO(ret))); } } @@ -562,12 +623,16 @@ ec_master_link_state_t *state) { ec_ioctl_link_state_t io; + int ret; io.dev_idx = dev_idx; io.state = state; - if (ioctl(master->fd, EC_IOCTL_MASTER_LINK_STATE, &io) == -1) { - fprintf(stderr, "Failed to get link state: %s\n", strerror(errno)); - return -errno; + + ret = ioctl(master->fd, EC_IOCTL_MASTER_LINK_STATE, &io); + if (EC_IOCTL_IS_ERROR(ret)) { + fprintf(stderr, "Failed to get link state: %s\n", + strerror(EC_IOCTL_ERRNO(ret))); + return -EC_IOCTL_ERRNO(ret); } return 0; @@ -578,12 +643,14 @@ void ecrt_master_application_time(ec_master_t *master, uint64_t app_time) { ec_ioctl_app_time_t data; + int ret; data.app_time = app_time; - if (ioctl(master->fd, EC_IOCTL_APP_TIME, &data) == -1) { + ret = ioctl(master->fd, EC_IOCTL_APP_TIME, &data); + if (EC_IOCTL_IS_ERROR(ret)) { fprintf(stderr, "Failed to set application time: %s\n", - strerror(errno)); + strerror(EC_IOCTL_ERRNO(ret))); } } @@ -591,9 +658,12 @@ void ecrt_master_sync_reference_clock(ec_master_t *master) { - if (ioctl(master->fd, EC_IOCTL_SYNC_REF, NULL) == -1) { + int ret; + + ret = ioctl(master->fd, EC_IOCTL_SYNC_REF, NULL); + if (EC_IOCTL_IS_ERROR(ret)) { fprintf(stderr, "Failed to sync reference clock: %s\n", - strerror(errno)); + strerror(EC_IOCTL_ERRNO(ret))); } } @@ -601,8 +671,12 @@ void ecrt_master_sync_slave_clocks(ec_master_t *master) { - if (ioctl(master->fd, EC_IOCTL_SYNC_SLAVES, NULL) == -1) { - fprintf(stderr, "Failed to sync slave clocks: %s\n", strerror(errno)); + int ret; + + ret = ioctl(master->fd, EC_IOCTL_SYNC_SLAVES, NULL); + if (EC_IOCTL_IS_ERROR(ret)) { + fprintf(stderr, "Failed to sync slave clocks: %s\n", + strerror(EC_IOCTL_ERRNO(ret))); } } @@ -610,9 +684,12 @@ void ecrt_master_sync_monitor_queue(ec_master_t *master) { - if (ioctl(master->fd, EC_IOCTL_SYNC_MON_QUEUE, NULL) == -1) { + int ret; + + ret = ioctl(master->fd, EC_IOCTL_SYNC_MON_QUEUE, NULL); + if (EC_IOCTL_IS_ERROR(ret)) { fprintf(stderr, "Failed to queue sync monitor datagram: %s\n", - strerror(errno)); + strerror(EC_IOCTL_ERRNO(ret))); } } @@ -621,11 +698,13 @@ uint32_t ecrt_master_sync_monitor_process(ec_master_t *master) { uint32_t time_diff; - - if (ioctl(master->fd, EC_IOCTL_SYNC_MON_PROCESS, &time_diff) == -1) { + int ret; + + ret = ioctl(master->fd, EC_IOCTL_SYNC_MON_PROCESS, &time_diff); + if (EC_IOCTL_IS_ERROR(ret)) { time_diff = 0xffffffff; fprintf(stderr, "Failed to process sync monitor datagram: %s\n", - strerror(errno)); + strerror(EC_IOCTL_ERRNO(ret))); } return time_diff; @@ -635,9 +714,13 @@ void ecrt_master_reset(ec_master_t *master) { - if (ioctl(master->fd, EC_IOCTL_RESET, NULL) == -1) { - fprintf(stderr, "Failed to reset master: %s\n", strerror(errno)); - } -} - -/****************************************************************************/ + int ret; + + ret = ioctl(master->fd, EC_IOCTL_RESET, NULL); + if (EC_IOCTL_IS_ERROR(ret)) { + fprintf(stderr, "Failed to reset master: %s\n", + strerror(EC_IOCTL_ERRNO(ret))); + } +} + +/****************************************************************************/ diff -r f4313f5aba88 -r 3bdd7a747fae lib/sdo_request.c --- a/lib/sdo_request.c Thu Sep 20 09:20:51 2012 +0200 +++ b/lib/sdo_request.c Thu Sep 20 15:28:25 2012 +0200 @@ -2,7 +2,7 @@ * * $Id$ * - * Copyright (C) 2006-2009 Florian Pose, Ingenieurgemeinschaft IgH + * Copyright (C) 2006-2012 Florian Pose, Ingenieurgemeinschaft IgH * * This file is part of the IgH EtherCAT master userspace library. * @@ -35,12 +35,10 @@ /*****************************************************************************/ #include -#include #include -#include +#include "ioctl.h" #include "sdo_request.h" -#include "master/ioctl.h" #include "slave_config.h" #include "master.h" @@ -60,15 +58,17 @@ void ecrt_sdo_request_timeout(ec_sdo_request_t *req, uint32_t timeout) { ec_ioctl_sdo_request_t data; + int ret; data.config_index = req->config->index; data.request_index = req->index; data.timeout = timeout; - if (ioctl(req->config->master->fd, EC_IOCTL_SDO_REQUEST_TIMEOUT, - &data) == -1) + ret = ioctl(req->config->master->fd, EC_IOCTL_SDO_REQUEST_TIMEOUT, &data); + if (EC_IOCTL_IS_ERROR(ret)) { fprintf(stderr, "Failed to set SDO request timeout: %s\n", - strerror(errno)); + strerror(EC_IOCTL_ERRNO(ret))); + } } /*****************************************************************************/ @@ -90,14 +90,17 @@ ec_request_state_t ecrt_sdo_request_state(ec_sdo_request_t *req) { ec_ioctl_sdo_request_t data; + int ret; data.config_index = req->config->index; data.request_index = req->index; - if (ioctl(req->config->master->fd, EC_IOCTL_SDO_REQUEST_STATE, - &data) == -1) + ret = ioctl(req->config->master->fd, EC_IOCTL_SDO_REQUEST_STATE, &data); + if (EC_IOCTL_IS_ERROR(ret)) { fprintf(stderr, "Failed to get SDO request state: %s\n", - strerror(errno)); + strerror(EC_IOCTL_ERRNO(ret))); + return EC_REQUEST_ERROR; + } if (data.size) { // new data waiting to be copied if (req->mem_size < data.size) { @@ -108,9 +111,11 @@ data.data = req->data; - if (ioctl(req->config->master->fd, EC_IOCTL_SDO_REQUEST_DATA, - &data) == -1) { - fprintf(stderr, "Failed to get SDO data: %s\n", strerror(errno)); + ret = ioctl(req->config->master->fd, + EC_IOCTL_SDO_REQUEST_DATA, &data); + if (EC_IOCTL_IS_ERROR(ret)) { + fprintf(stderr, "Failed to get SDO data: %s\n", + strerror(EC_IOCTL_ERRNO(ret))); return EC_REQUEST_ERROR; } req->data_size = data.size; @@ -124,14 +129,16 @@ void ecrt_sdo_request_read(ec_sdo_request_t *req) { ec_ioctl_sdo_request_t data; + int ret; data.config_index = req->config->index; data.request_index = req->index; - if (ioctl(req->config->master->fd, EC_IOCTL_SDO_REQUEST_READ, - &data) == -1) + ret = ioctl(req->config->master->fd, EC_IOCTL_SDO_REQUEST_READ, &data); + if (EC_IOCTL_IS_ERROR(ret)) { fprintf(stderr, "Failed to command an SDO read operation : %s\n", - strerror(errno)); + strerror(EC_IOCTL_ERRNO(ret))); + } } /*****************************************************************************/ @@ -139,16 +146,18 @@ void ecrt_sdo_request_write(ec_sdo_request_t *req) { ec_ioctl_sdo_request_t data; + int ret; data.config_index = req->config->index; data.request_index = req->index; data.data = req->data; data.size = req->data_size; - if (ioctl(req->config->master->fd, EC_IOCTL_SDO_REQUEST_WRITE, - &data) == -1) + ret = ioctl(req->config->master->fd, EC_IOCTL_SDO_REQUEST_WRITE, &data); + if (EC_IOCTL_IS_ERROR(ret)) { fprintf(stderr, "Failed to command an SDO write operation : %s\n", - strerror(errno)); + strerror(EC_IOCTL_ERRNO(ret))); + } } /*****************************************************************************/ diff -r f4313f5aba88 -r 3bdd7a747fae lib/slave_config.c --- a/lib/slave_config.c Thu Sep 20 09:20:51 2012 +0200 +++ b/lib/slave_config.c Thu Sep 20 15:28:25 2012 +0200 @@ -2,7 +2,7 @@ * * $Id$ * - * Copyright (C) 2006-2009 Florian Pose, Ingenieurgemeinschaft IgH + * Copyright (C) 2006-2012 Florian Pose, Ingenieurgemeinschaft IgH * * This file is part of the IgH EtherCAT master userspace library. * @@ -29,17 +29,16 @@ *****************************************************************************/ #include -#include #include -#include #include - +#include /* ENOENT */ + +#include "ioctl.h" #include "slave_config.h" #include "domain.h" #include "sdo_request.h" #include "voe_handler.h" #include "master.h" -#include "master/ioctl.h" /*****************************************************************************/ @@ -70,6 +69,7 @@ ec_direction_t dir, ec_watchdog_mode_t watchdog_mode) { ec_ioctl_config_t data; + int ret; if (sync_index >= EC_MAX_SYNC_MANAGERS) return -ENOENT; @@ -80,10 +80,11 @@ data.syncs[sync_index].watchdog_mode = watchdog_mode; data.syncs[sync_index].config_this = 1; - if (ioctl(sc->master->fd, EC_IOCTL_SC_SYNC, &data) == -1) { + ret = ioctl(sc->master->fd, EC_IOCTL_SC_SYNC, &data); + if (EC_IOCTL_IS_ERROR(ret)) { fprintf(stderr, "Failed to config sync manager: %s\n", - strerror(errno)); - return -1; // FIXME + strerror(EC_IOCTL_ERRNO(ret))); + return -EC_IOCTL_ERRNO(ret); } return 0; @@ -95,15 +96,17 @@ uint16_t divider, uint16_t intervals) { ec_ioctl_config_t data; + int ret; memset(&data, 0x00, sizeof(ec_ioctl_config_t)); data.config_index = sc->index; data.watchdog_divider = divider; data.watchdog_intervals = intervals; - if (ioctl(sc->master->fd, EC_IOCTL_SC_WATCHDOG, &data) == -1) { + ret = ioctl(sc->master->fd, EC_IOCTL_SC_WATCHDOG, &data); + if (EC_IOCTL_IS_ERROR(ret)) { fprintf(stderr, "Failed to config watchdog: %s\n", - strerror(errno)); + strerror(EC_IOCTL_ERRNO(ret))); } } @@ -113,15 +116,17 @@ uint8_t sync_index, uint16_t pdo_index) { ec_ioctl_config_pdo_t data; + int ret; data.config_index = sc->index; data.sync_index = sync_index; data.index = pdo_index; - if (ioctl(sc->master->fd, EC_IOCTL_SC_ADD_PDO, &data) == -1) { + ret = ioctl(sc->master->fd, EC_IOCTL_SC_ADD_PDO, &data); + if (EC_IOCTL_IS_ERROR(ret)) { fprintf(stderr, "Failed to add PDO: %s\n", - strerror(errno)); - return -1; // FIXME + strerror(EC_IOCTL_ERRNO(ret))); + return -EC_IOCTL_ERRNO(ret); } return 0; @@ -133,13 +138,15 @@ uint8_t sync_index) { ec_ioctl_config_pdo_t data; + int ret; data.config_index = sc->index; data.sync_index = sync_index; - if (ioctl(sc->master->fd, EC_IOCTL_SC_CLEAR_PDOS, &data) == -1) { + ret = ioctl(sc->master->fd, EC_IOCTL_SC_CLEAR_PDOS, &data); + if (EC_IOCTL_IS_ERROR(ret)) { fprintf(stderr, "Failed to clear PDOs: %s\n", - strerror(errno)); + strerror(EC_IOCTL_ERRNO(ret))); } } @@ -150,6 +157,7 @@ uint8_t entry_bit_length) { ec_ioctl_add_pdo_entry_t data; + int ret; data.config_index = sc->index; data.pdo_index = pdo_index; @@ -157,10 +165,11 @@ data.entry_subindex = entry_subindex; data.entry_bit_length = entry_bit_length; - if (ioctl(sc->master->fd, EC_IOCTL_SC_ADD_ENTRY, &data) == -1) { + ret = ioctl(sc->master->fd, EC_IOCTL_SC_ADD_ENTRY, &data); + if (EC_IOCTL_IS_ERROR(ret)) { fprintf(stderr, "Failed to add PDO entry: %s\n", - strerror(errno)); - return -1; // FIXME + strerror(EC_IOCTL_ERRNO(ret))); + return -EC_IOCTL_ERRNO(ret); } return 0; @@ -172,13 +181,15 @@ uint16_t pdo_index) { ec_ioctl_config_pdo_t data; + int ret; data.config_index = sc->index; data.index = pdo_index; - if (ioctl(sc->master->fd, EC_IOCTL_SC_CLEAR_ENTRIES, &data) == -1) { + ret = ioctl(sc->master->fd, EC_IOCTL_SC_CLEAR_ENTRIES, &data); + if (EC_IOCTL_IS_ERROR(ret)) { fprintf(stderr, "Failed to clear PDO entries: %s\n", - strerror(errno)); + strerror(EC_IOCTL_ERRNO(ret))); } } @@ -264,10 +275,10 @@ data.domain_index = domain->index; ret = ioctl(sc->master->fd, EC_IOCTL_SC_REG_PDO_ENTRY, &data); - if (ret == -1) { + if (EC_IOCTL_IS_ERROR(ret)) { fprintf(stderr, "Failed to register PDO entry: %s\n", - strerror(errno)); - return -2; // FIXME + strerror(EC_IOCTL_ERRNO(ret))); + return -EC_IOCTL_ERRNO(ret); } if (bit_position) { @@ -277,7 +288,7 @@ fprintf(stderr, "PDO entry 0x%04X:%02X does not byte-align " "in config %u:%u.\n", index, subindex, sc->alias, sc->position); - return -3; // FIXME + return -EFAULT; } } @@ -291,6 +302,7 @@ uint32_t sync1_cycle_time, uint32_t sync1_shift_time) { ec_ioctl_config_t data; + int ret; data.config_index = sc->index; data.dc_assign_activate = assign_activate; @@ -299,8 +311,10 @@ data.dc_sync[1].cycle_time = sync1_cycle_time; data.dc_sync[1].shift_time = sync1_shift_time; - if (ioctl(sc->master->fd, EC_IOCTL_SC_DC, &data) == -1) { - fprintf(stderr, "Failed to set assign_activate word.\n"); + ret = ioctl(sc->master->fd, EC_IOCTL_SC_DC, &data); + if (EC_IOCTL_IS_ERROR(ret)) { + fprintf(stderr, "Failed to set assign_activate word: %s\n", + strerror(EC_IOCTL_ERRNO(ret))); } } @@ -310,6 +324,7 @@ uint8_t subindex, const uint8_t *sdo_data, size_t size) { ec_ioctl_sc_sdo_t data; + int ret; data.config_index = sc->index; data.index = index; @@ -318,9 +333,11 @@ data.size = size; data.complete_access = 0; - if (ioctl(sc->master->fd, EC_IOCTL_SC_SDO, &data) == -1) { - fprintf(stderr, "Failed to configure SDO.\n"); - return -1; // FIXME + ret = ioctl(sc->master->fd, EC_IOCTL_SC_SDO, &data); + if (EC_IOCTL_IS_ERROR(ret)) { + fprintf(stderr, "Failed to configure SDO: %s\n", + strerror(EC_IOCTL_ERRNO(ret))); + return -EC_IOCTL_ERRNO(ret); } return 0; @@ -332,6 +349,7 @@ const uint8_t *sdo_data, size_t size) { ec_ioctl_sc_sdo_t data; + int ret; data.config_index = sc->index; data.index = index; @@ -340,9 +358,11 @@ data.size = size; data.complete_access = 1; - if (ioctl(sc->master->fd, EC_IOCTL_SC_SDO, &data) == -1) { - fprintf(stderr, "Failed to configure SDO.\n"); - return -1; // FIXME + ret = ioctl(sc->master->fd, EC_IOCTL_SC_SDO, &data); + if (EC_IOCTL_IS_ERROR(ret)) { + fprintf(stderr, "Failed to configure SDO: %s\n", + strerror(EC_IOCTL_ERRNO(ret))); + return -EC_IOCTL_ERRNO(ret); } return 0; @@ -404,6 +424,7 @@ { ec_ioctl_sdo_request_t data; ec_sdo_request_t *req; + int ret; req = malloc(sizeof(ec_sdo_request_t)); if (!req) { @@ -428,9 +449,10 @@ data.sdo_subindex = subindex; data.size = size; - if (ioctl(sc->master->fd, EC_IOCTL_SC_SDO_REQUEST, &data) == -1) { + ret = ioctl(sc->master->fd, EC_IOCTL_SC_SDO_REQUEST, &data); + if (EC_IOCTL_IS_ERROR(ret)) { fprintf(stderr, "Failed to create SDO request: %s\n", - strerror(errno)); + strerror(EC_IOCTL_ERRNO(ret))); ec_sdo_request_clear(req); free(req); return NULL; @@ -472,6 +494,7 @@ { ec_ioctl_voe_t data; ec_voe_handler_t *voe; + int ret; voe = malloc(sizeof(ec_voe_handler_t)); if (!voe) { @@ -494,9 +517,10 @@ data.config_index = sc->index; data.size = size; - if (ioctl(sc->master->fd, EC_IOCTL_SC_VOE, &data) == -1) { + ret = ioctl(sc->master->fd, EC_IOCTL_SC_VOE, &data); + if (EC_IOCTL_IS_ERROR(ret)) { fprintf(stderr, "Failed to create VoE handler: %s\n", - strerror(errno)); + strerror(EC_IOCTL_ERRNO(ret))); ec_voe_handler_clear(voe); free(voe); return NULL; @@ -519,13 +543,15 @@ ec_slave_config_state_t *state) { ec_ioctl_sc_state_t data; + int ret; data.config_index = sc->index; data.state = state; - if (ioctl(sc->master->fd, EC_IOCTL_SC_STATE, &data) == -1) { + ret = ioctl(sc->master->fd, EC_IOCTL_SC_STATE, &data); + if (EC_IOCTL_IS_ERROR(ret)) { fprintf(stderr, "Failed to get slave configuration state: %s\n", - strerror(errno)); + strerror(EC_IOCTL_ERRNO(ret))); } } @@ -535,6 +561,7 @@ uint16_t idn, ec_al_state_t al_state, const uint8_t *data, size_t size) { ec_ioctl_sc_idn_t io; + int ret; io.config_index = sc->index; io.drive_no = drive_no; @@ -543,12 +570,14 @@ io.data = data; io.size = size; - if (ioctl(sc->master->fd, EC_IOCTL_SC_IDN, &io) == -1) { - fprintf(stderr, "Failed to configure IDN.\n"); - return -1; // FIXME - } - - return 0; -} - -/*****************************************************************************/ + ret = ioctl(sc->master->fd, EC_IOCTL_SC_IDN, &io); + if (EC_IOCTL_IS_ERROR(ret)) { + fprintf(stderr, "Failed to configure IDN: %s\n", + strerror(EC_IOCTL_ERRNO(ret))); + return -EC_IOCTL_ERRNO(ret); + } + + return 0; +} + +/*****************************************************************************/ diff -r f4313f5aba88 -r 3bdd7a747fae lib/voe_handler.c --- a/lib/voe_handler.c Thu Sep 20 09:20:51 2012 +0200 +++ b/lib/voe_handler.c Thu Sep 20 15:28:25 2012 +0200 @@ -2,7 +2,7 @@ * * $Id$ * - * Copyright (C) 2006-2009 Florian Pose, Ingenieurgemeinschaft IgH + * Copyright (C) 2006-2012 Florian Pose, Ingenieurgemeinschaft IgH * * This file is part of the IgH EtherCAT master userspace library. * @@ -35,15 +35,13 @@ /*****************************************************************************/ #include -#include #include -#include #include +#include "ioctl.h" #include "voe_handler.h" #include "slave_config.h" #include "master.h" -#include "master/ioctl.h" /*****************************************************************************/ @@ -60,16 +58,17 @@ uint16_t vendor_type) { ec_ioctl_voe_t data; + int ret; data.config_index = voe->config->index; data.voe_index = voe->index; data.vendor_id = &vendor_id; data.vendor_type = &vendor_type; - if (ioctl(voe->config->master->fd, - EC_IOCTL_VOE_SEND_HEADER, &data) == -1) { + ret = ioctl(voe->config->master->fd, EC_IOCTL_VOE_SEND_HEADER, &data); + if (EC_IOCTL_IS_ERROR(ret)) { fprintf(stderr, "Failed to set VoE send header: %s\n", - strerror(errno)); + strerror(EC_IOCTL_ERRNO(ret))); } } @@ -79,16 +78,17 @@ uint32_t *vendor_id, uint16_t *vendor_type) { ec_ioctl_voe_t data; + int ret; data.config_index = voe->config->index; data.voe_index = voe->index; data.vendor_id = vendor_id; data.vendor_type = vendor_type; - if (ioctl(voe->config->master->fd, - EC_IOCTL_VOE_REC_HEADER, &data) == -1) { + ret = ioctl(voe->config->master->fd, EC_IOCTL_VOE_REC_HEADER, &data); + if (EC_IOCTL_IS_ERROR(ret)) { fprintf(stderr, "Failed to get received VoE header: %s\n", - strerror(errno)); + strerror(EC_IOCTL_ERRNO(ret))); } } @@ -111,13 +111,15 @@ void ecrt_voe_handler_read(ec_voe_handler_t *voe) { ec_ioctl_voe_t data; - - data.config_index = voe->config->index; - data.voe_index = voe->index; - - if (ioctl(voe->config->master->fd, EC_IOCTL_VOE_READ, &data) == -1) { + int ret; + + data.config_index = voe->config->index; + data.voe_index = voe->index; + + ret = ioctl(voe->config->master->fd, EC_IOCTL_VOE_READ, &data); + if (EC_IOCTL_IS_ERROR(ret)) { fprintf(stderr, "Failed to initiate VoE reading: %s\n", - strerror(errno)); + strerror(EC_IOCTL_ERRNO(ret))); } } @@ -126,14 +128,15 @@ void ecrt_voe_handler_read_nosync(ec_voe_handler_t *voe) { ec_ioctl_voe_t data; - - data.config_index = voe->config->index; - data.voe_index = voe->index; - - if (ioctl(voe->config->master->fd, - EC_IOCTL_VOE_READ_NOSYNC, &data) == -1) { + int ret; + + data.config_index = voe->config->index; + data.voe_index = voe->index; + + ret = ioctl(voe->config->master->fd, EC_IOCTL_VOE_READ_NOSYNC, &data); + if (EC_IOCTL_IS_ERROR(ret)) { fprintf(stderr, "Failed to initiate VoE reading: %s\n", - strerror(errno)); + strerror(EC_IOCTL_ERRNO(ret))); } } @@ -142,15 +145,17 @@ void ecrt_voe_handler_write(ec_voe_handler_t *voe, size_t size) { ec_ioctl_voe_t data; + int ret; data.config_index = voe->config->index; data.voe_index = voe->index; data.size = size; data.data = voe->data; - if (ioctl(voe->config->master->fd, EC_IOCTL_VOE_WRITE, &data) == -1) { + ret = ioctl(voe->config->master->fd, EC_IOCTL_VOE_WRITE, &data); + if (EC_IOCTL_IS_ERROR(ret)) { fprintf(stderr, "Failed to initiate VoE writing: %s\n", - strerror(errno)); + strerror(EC_IOCTL_ERRNO(ret))); } } @@ -159,13 +164,15 @@ ec_request_state_t ecrt_voe_handler_execute(ec_voe_handler_t *voe) { ec_ioctl_voe_t data; - - data.config_index = voe->config->index; - data.voe_index = voe->index; - - if (ioctl(voe->config->master->fd, EC_IOCTL_VOE_EXEC, &data) == -1) { + int ret; + + data.config_index = voe->config->index; + data.voe_index = voe->index; + + ret = ioctl(voe->config->master->fd, EC_IOCTL_VOE_EXEC, &data); + if (EC_IOCTL_IS_ERROR(ret)) { fprintf(stderr, "Failed to execute VoE handler: %s\n", - strerror(errno)); + strerror(EC_IOCTL_ERRNO(ret))); return EC_REQUEST_ERROR; } @@ -178,8 +185,10 @@ data.data = voe->data; - if (ioctl(voe->config->master->fd, EC_IOCTL_VOE_DATA, &data) == -1) { - fprintf(stderr, "Failed to get VoE data: %s\n", strerror(errno)); + ret = ioctl(voe->config->master->fd, EC_IOCTL_VOE_DATA, &data); + if (EC_IOCTL_IS_ERROR(ret)) { + fprintf(stderr, "Failed to get VoE data: %s\n", + strerror(EC_IOCTL_ERRNO(ret))); return EC_REQUEST_ERROR; } voe->data_size = data.size; diff -r f4313f5aba88 -r 3bdd7a747fae master/Kbuild.in --- a/master/Kbuild.in Thu Sep 20 09:20:51 2012 +0200 +++ b/master/Kbuild.in Thu Sep 20 15:28:25 2012 +0200 @@ -2,7 +2,7 @@ # # $Id$ # -# Copyright (C) 2006-2008 Florian Pose, Ingenieurgemeinschaft IgH +# Copyright (C) 2006-2012 Florian Pose, Ingenieurgemeinschaft IgH # # This file is part of the IgH EtherCAT Master. # @@ -52,6 +52,7 @@ fsm_slave_config.o \ fsm_slave_scan.o \ fsm_soe.o \ + ioctl.o \ mailbox.o \ master.o \ module.o \ @@ -70,12 +71,30 @@ voe_handler.o ifeq (@ENABLE_EOE@,1) - ec_master-objs += ethernet.o +ec_master-objs += ethernet.o endif + ifeq (@ENABLE_DEBUG_IF@,1) - ec_master-objs += debug.o +ec_master-objs += debug.o endif +ifeq (@ENABLE_RTDM@,1) + +ec_master-objs += rtdm.o + +ifeq (@ENABLE_XENOMAI@, 1) +CFLAGS_rtdm.o := -I@XENOMAI_DIR@/include +endif + +ifeq (@ENABLE_RTAI@, 1) +CFLAGS_rtdm.o := -I@RTAI_DIR@/include +endif + +ec_master-objs += rtdm-ioctl.o +CFLAGS_rtdm-ioctl.o := -DEC_IOCTL_RTDM + +endif # ENABLE_RTDM + REV := $(shell if test -s $(src)/../revision; then \ cat $(src)/../revision; \ else \ diff -r f4313f5aba88 -r 3bdd7a747fae master/Makefile.am --- a/master/Makefile.am Thu Sep 20 09:20:51 2012 +0200 +++ b/master/Makefile.am Thu Sep 20 15:28:25 2012 +0200 @@ -2,7 +2,7 @@ # # $Id$ # -# Copyright (C) 2006-2008 Florian Pose, Ingenieurgemeinschaft IgH +# Copyright (C) 2006-2012 Florian Pose, Ingenieurgemeinschaft IgH # # This file is part of the IgH EtherCAT Master. # @@ -32,7 +32,7 @@ cdev.c cdev.h \ datagram.c datagram.h \ datagram_pair.c datagram_pair.h \ - debug.c debug.h \ + debug.c debug.h \ device.c device.h \ domain.c domain.h \ doxygen.c \ @@ -52,13 +52,15 @@ fsm_slave_scan.c fsm_slave_scan.h \ fsm_soe.c fsm_soe.h \ globals.h \ - ioctl.h \ + ioctl.c ioctl.h \ mailbox.c mailbox.h \ master.c master.h \ module.c \ pdo.c pdo.h \ pdo_entry.c pdo_entry.h \ pdo_list.c pdo_list.h \ + rtdm-ioctl.c \ + rtdm.c rtdm.h \ sdo.c sdo.h \ sdo_entry.c sdo_entry.h \ sdo_request.c sdo_request.h \ diff -r f4313f5aba88 -r 3bdd7a747fae master/cdev.c --- a/master/cdev.c Thu Sep 20 09:20:51 2012 +0200 +++ b/master/cdev.c Thu Sep 20 15:28:25 2012 +0200 @@ -2,7 +2,7 @@ * * $Id$ * - * Copyright (C) 2006-2008 Florian Pose, Ingenieurgemeinschaft IgH + * Copyright (C) 2006-2012 Florian Pose, Ingenieurgemeinschaft IgH * * This file is part of the IgH EtherCAT Master. * @@ -45,15 +45,9 @@ #include "ethernet.h" #include "ioctl.h" -/** Set to 1 to enable ioctl() command debugging. - */ -#define DEBUG_IOCTL 0 - -/** Set to 1 to enable ioctl() latency warnings. - * - * Requires CPU timestamp counter! - */ -#define DEBUG_LATENCY 0 +/** Set to 1 to enable device operations debugging. + */ +#define DEBUG 0 /*****************************************************************************/ @@ -102,9 +96,7 @@ */ typedef struct { ec_cdev_t *cdev; /**< Character device. */ - unsigned int requested; /**< Master wac requested via this file handle. */ - uint8_t *process_data; /**< Total process data area. */ - size_t process_data_size; /**< Size of the \a process_data. */ + ec_ioctl_context_t ctx; /**< Context. */ } ec_cdev_priv_t; /*****************************************************************************/ @@ -144,3275 +136,6 @@ cdev_del(&cdev->cdev); } -/*****************************************************************************/ - -/** Copies a string to an ioctl structure. - */ -void ec_cdev_strcpy( - char *target, /**< Target. */ - const char *source /**< Source. */ - ) -{ - if (source) { - strncpy(target, source, EC_IOCTL_STRING_SIZE); - target[EC_IOCTL_STRING_SIZE - 1] = 0; - } else { - target[0] = 0; - } -} - -/*****************************************************************************/ - -/** Get module information. - */ -int ec_cdev_ioctl_module( - unsigned long arg /**< Userspace address to store the results. */ - ) -{ - ec_ioctl_module_t data; - - data.ioctl_version_magic = EC_IOCTL_VERSION_MAGIC; - data.master_count = ec_master_count(); - - if (copy_to_user((void __user *) arg, &data, sizeof(data))) - return -EFAULT; - - return 0; -} - -/*****************************************************************************/ - -/** Get master information. - */ -int ec_cdev_ioctl_master( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg /**< Userspace address to store the results. */ - ) -{ - ec_ioctl_master_t data; - unsigned int i, j; - - if (down_interruptible(&master->master_sem)) - return -EINTR; - - data.slave_count = master->slave_count; - data.config_count = ec_master_config_count(master); - data.domain_count = ec_master_domain_count(master); -#ifdef EC_EOE - data.eoe_handler_count = ec_master_eoe_handler_count(master); -#endif - data.phase = (uint8_t) master->phase; - data.active = (uint8_t) master->active; - data.scan_busy = master->scan_busy; - - up(&master->master_sem); - - if (down_interruptible(&master->device_sem)) - return -EINTR; - - for (i = 0; i < EC_NUM_DEVICES; i++) { - ec_device_t *device = &master->devices[i]; - - if (device->dev) { - memcpy(data.devices[i].address, - device->dev->dev_addr, ETH_ALEN); - } else { - memcpy(data.devices[i].address, master->macs[i], ETH_ALEN); - } - data.devices[i].attached = device->dev ? 1 : 0; - data.devices[i].link_state = device->link_state ? 1 : 0; - data.devices[i].tx_count = device->tx_count; - data.devices[i].rx_count = device->rx_count; - data.devices[i].tx_bytes = device->tx_bytes; - data.devices[i].rx_bytes = device->rx_bytes; - data.devices[i].tx_errors = device->tx_errors; - for (j = 0; j < EC_RATE_COUNT; j++) { - data.devices[i].tx_frame_rates[j] = - device->tx_frame_rates[j]; - data.devices[i].rx_frame_rates[j] = - device->rx_frame_rates[j]; - data.devices[i].tx_byte_rates[j] = - device->tx_byte_rates[j]; - data.devices[i].rx_byte_rates[j] = - device->rx_byte_rates[j]; - } - } - - data.tx_count = master->device_stats.tx_count; - data.rx_count = master->device_stats.rx_count; - data.tx_bytes = master->device_stats.tx_bytes; - data.rx_bytes = master->device_stats.rx_bytes; - for (i = 0; i < EC_RATE_COUNT; i++) { - data.tx_frame_rates[i] = - master->device_stats.tx_frame_rates[i]; - data.rx_frame_rates[i] = - master->device_stats.rx_frame_rates[i]; - data.tx_byte_rates[i] = - master->device_stats.tx_byte_rates[i]; - data.rx_byte_rates[i] = - master->device_stats.rx_byte_rates[i]; - data.loss_rates[i] = - master->device_stats.loss_rates[i]; - } - - up(&master->device_sem); - - data.app_time = master->app_time; - data.ref_clock = - master->dc_ref_clock ? master->dc_ref_clock->ring_position : 0xffff; - - if (copy_to_user((void __user *) arg, &data, sizeof(data))) - return -EFAULT; - - return 0; -} - -/*****************************************************************************/ - -/** Get slave information. - */ -int ec_cdev_ioctl_slave( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg /**< Userspace address to store the results. */ - ) -{ - ec_ioctl_slave_t data; - const ec_slave_t *slave; - int i; - - if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { - return -EFAULT; - } - - if (down_interruptible(&master->master_sem)) - return -EINTR; - - if (!(slave = ec_master_find_slave_const( - master, 0, data.position))) { - up(&master->master_sem); - EC_MASTER_ERR(master, "Slave %u does not exist!\n", data.position); - return -EINVAL; - } - - data.device_index = slave->device_index; - data.vendor_id = slave->sii.vendor_id; - data.product_code = slave->sii.product_code; - data.revision_number = slave->sii.revision_number; - data.serial_number = slave->sii.serial_number; - data.alias = slave->effective_alias; - data.boot_rx_mailbox_offset = slave->sii.boot_rx_mailbox_offset; - data.boot_rx_mailbox_size = slave->sii.boot_rx_mailbox_size; - data.boot_tx_mailbox_offset = slave->sii.boot_tx_mailbox_offset; - data.boot_tx_mailbox_size = slave->sii.boot_tx_mailbox_size; - data.std_rx_mailbox_offset = slave->sii.std_rx_mailbox_offset; - data.std_rx_mailbox_size = slave->sii.std_rx_mailbox_size; - data.std_tx_mailbox_offset = slave->sii.std_tx_mailbox_offset; - data.std_tx_mailbox_size = slave->sii.std_tx_mailbox_size; - data.mailbox_protocols = slave->sii.mailbox_protocols; - data.has_general_category = slave->sii.has_general; - data.coe_details = slave->sii.coe_details; - data.general_flags = slave->sii.general_flags; - data.current_on_ebus = slave->sii.current_on_ebus; - for (i = 0; i < EC_MAX_PORTS; i++) { - data.ports[i].desc = slave->ports[i].desc; - data.ports[i].link.link_up = slave->ports[i].link.link_up; - data.ports[i].link.loop_closed = slave->ports[i].link.loop_closed; - data.ports[i].link.signal_detected = - slave->ports[i].link.signal_detected; - data.ports[i].receive_time = slave->ports[i].receive_time; - if (slave->ports[i].next_slave) { - data.ports[i].next_slave = - slave->ports[i].next_slave->ring_position; - } else { - data.ports[i].next_slave = 0xffff; - } - data.ports[i].delay_to_next_dc = slave->ports[i].delay_to_next_dc; - } - data.fmmu_bit = slave->base_fmmu_bit_operation; - data.dc_supported = slave->base_dc_supported; - data.dc_range = slave->base_dc_range; - data.has_dc_system_time = slave->has_dc_system_time; - data.transmission_delay = slave->transmission_delay; - data.al_state = slave->current_state; - data.error_flag = slave->error_flag; - - data.sync_count = slave->sii.sync_count; - data.sdo_count = ec_slave_sdo_count(slave); - data.sii_nwords = slave->sii_nwords; - ec_cdev_strcpy(data.group, slave->sii.group); - ec_cdev_strcpy(data.image, slave->sii.image); - ec_cdev_strcpy(data.order, slave->sii.order); - ec_cdev_strcpy(data.name, slave->sii.name); - - up(&master->master_sem); - - if (copy_to_user((void __user *) arg, &data, sizeof(data))) - return -EFAULT; - - return 0; -} - -/*****************************************************************************/ - -/** Get slave sync manager information. - */ -int ec_cdev_ioctl_slave_sync( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg /**< Userspace address to store the results. */ - ) -{ - ec_ioctl_slave_sync_t data; - const ec_slave_t *slave; - const ec_sync_t *sync; - - if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { - return -EFAULT; - } - - if (down_interruptible(&master->master_sem)) - return -EINTR; - - if (!(slave = ec_master_find_slave_const( - master, 0, data.slave_position))) { - up(&master->master_sem); - EC_MASTER_ERR(master, "Slave %u does not exist!\n", - data.slave_position); - return -EINVAL; - } - - if (data.sync_index >= slave->sii.sync_count) { - up(&master->master_sem); - EC_SLAVE_ERR(slave, "Sync manager %u does not exist!\n", - data.sync_index); - return -EINVAL; - } - - sync = &slave->sii.syncs[data.sync_index]; - - data.physical_start_address = sync->physical_start_address; - data.default_size = sync->default_length; - data.control_register = sync->control_register; - data.enable = sync->enable; - data.pdo_count = ec_pdo_list_count(&sync->pdos); - - up(&master->master_sem); - - if (copy_to_user((void __user *) arg, &data, sizeof(data))) - return -EFAULT; - - return 0; -} - -/*****************************************************************************/ - -/** Get slave sync manager PDO information. - */ -int ec_cdev_ioctl_slave_sync_pdo( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg /**< Userspace address to store the results. */ - ) -{ - ec_ioctl_slave_sync_pdo_t data; - const ec_slave_t *slave; - const ec_sync_t *sync; - const ec_pdo_t *pdo; - - if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { - return -EFAULT; - } - - if (down_interruptible(&master->master_sem)) - return -EINTR; - - if (!(slave = ec_master_find_slave_const( - master, 0, data.slave_position))) { - up(&master->master_sem); - EC_MASTER_ERR(master, "Slave %u does not exist!\n", - data.slave_position); - return -EINVAL; - } - - if (data.sync_index >= slave->sii.sync_count) { - up(&master->master_sem); - EC_SLAVE_ERR(slave, "Sync manager %u does not exist!\n", - data.sync_index); - return -EINVAL; - } - - sync = &slave->sii.syncs[data.sync_index]; - if (!(pdo = ec_pdo_list_find_pdo_by_pos_const( - &sync->pdos, data.pdo_pos))) { - up(&master->master_sem); - EC_SLAVE_ERR(slave, "Sync manager %u does not contain a PDO with " - "position %u!\n", data.sync_index, data.pdo_pos); - return -EINVAL; - } - - data.index = pdo->index; - data.entry_count = ec_pdo_entry_count(pdo); - ec_cdev_strcpy(data.name, pdo->name); - - up(&master->master_sem); - - if (copy_to_user((void __user *) arg, &data, sizeof(data))) - return -EFAULT; - - return 0; -} - -/*****************************************************************************/ - -/** Get slave sync manager PDO entry information. - */ -int ec_cdev_ioctl_slave_sync_pdo_entry( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg /**< Userspace address to store the results. */ - ) -{ - ec_ioctl_slave_sync_pdo_entry_t data; - const ec_slave_t *slave; - const ec_sync_t *sync; - const ec_pdo_t *pdo; - const ec_pdo_entry_t *entry; - - if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { - return -EFAULT; - } - - if (down_interruptible(&master->master_sem)) - return -EINTR; - - if (!(slave = ec_master_find_slave_const( - master, 0, data.slave_position))) { - up(&master->master_sem); - EC_MASTER_ERR(master, "Slave %u does not exist!\n", - data.slave_position); - return -EINVAL; - } - - if (data.sync_index >= slave->sii.sync_count) { - up(&master->master_sem); - EC_SLAVE_ERR(slave, "Sync manager %u does not exist!\n", - data.sync_index); - return -EINVAL; - } - - sync = &slave->sii.syncs[data.sync_index]; - if (!(pdo = ec_pdo_list_find_pdo_by_pos_const( - &sync->pdos, data.pdo_pos))) { - up(&master->master_sem); - EC_SLAVE_ERR(slave, "Sync manager %u does not contain a PDO with " - "position %u!\n", data.sync_index, data.pdo_pos); - return -EINVAL; - } - - if (!(entry = ec_pdo_find_entry_by_pos_const( - pdo, data.entry_pos))) { - up(&master->master_sem); - EC_SLAVE_ERR(slave, "PDO 0x%04X does not contain an entry with " - "position %u!\n", data.pdo_pos, data.entry_pos); - return -EINVAL; - } - - data.index = entry->index; - data.subindex = entry->subindex; - data.bit_length = entry->bit_length; - ec_cdev_strcpy(data.name, entry->name); - - up(&master->master_sem); - - if (copy_to_user((void __user *) arg, &data, sizeof(data))) - return -EFAULT; - - return 0; -} - -/*****************************************************************************/ - -/** Get domain information. - */ -int ec_cdev_ioctl_domain( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg /**< Userspace address to store the results. */ - ) -{ - ec_ioctl_domain_t data; - const ec_domain_t *domain; - unsigned int dev_idx; - - if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { - return -EFAULT; - } - - if (down_interruptible(&master->master_sem)) - return -EINTR; - - if (!(domain = ec_master_find_domain_const(master, data.index))) { - up(&master->master_sem); - EC_MASTER_ERR(master, "Domain %u does not exist!\n", data.index); - return -EINVAL; - } - - data.data_size = domain->data_size; - data.logical_base_address = domain->logical_base_address; - for (dev_idx = 0; dev_idx < EC_NUM_DEVICES; dev_idx++) { - data.working_counter[dev_idx] = domain->working_counter[dev_idx]; - } - data.expected_working_counter = domain->expected_working_counter; - data.fmmu_count = ec_domain_fmmu_count(domain); - - up(&master->master_sem); - - if (copy_to_user((void __user *) arg, &data, sizeof(data))) - return -EFAULT; - - return 0; -} - -/*****************************************************************************/ - -/** Get domain FMMU information. - */ -int ec_cdev_ioctl_domain_fmmu( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg /**< Userspace address to store the results. */ - ) -{ - ec_ioctl_domain_fmmu_t data; - const ec_domain_t *domain; - const ec_fmmu_config_t *fmmu; - - if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { - return -EFAULT; - } - - if (down_interruptible(&master->master_sem)) - return -EINTR; - - if (!(domain = ec_master_find_domain_const(master, data.domain_index))) { - up(&master->master_sem); - EC_MASTER_ERR(master, "Domain %u does not exist!\n", - data.domain_index); - return -EINVAL; - } - - if (!(fmmu = ec_domain_find_fmmu(domain, data.fmmu_index))) { - up(&master->master_sem); - EC_MASTER_ERR(master, "Domain %u has less than %u" - " fmmu configurations.\n", - data.domain_index, data.fmmu_index + 1); - return -EINVAL; - } - - data.slave_config_alias = fmmu->sc->alias; - data.slave_config_position = fmmu->sc->position; - data.sync_index = fmmu->sync_index; - data.dir = fmmu->dir; - data.logical_address = fmmu->logical_start_address; - data.data_size = fmmu->data_size; - - up(&master->master_sem); - - if (copy_to_user((void __user *) arg, &data, sizeof(data))) - return -EFAULT; - - return 0; -} - -/*****************************************************************************/ - -/** Get domain data. - */ -int ec_cdev_ioctl_domain_data( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg /**< Userspace address to store the results. */ - ) -{ - ec_ioctl_domain_data_t data; - const ec_domain_t *domain; - - if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { - return -EFAULT; - } - - if (down_interruptible(&master->master_sem)) - return -EINTR; - - if (!(domain = ec_master_find_domain_const(master, data.domain_index))) { - up(&master->master_sem); - EC_MASTER_ERR(master, "Domain %u does not exist!\n", - data.domain_index); - return -EINVAL; - } - - if (domain->data_size != data.data_size) { - up(&master->master_sem); - EC_MASTER_ERR(master, "Data size mismatch %u/%zu!\n", - data.data_size, domain->data_size); - return -EFAULT; - } - - if (copy_to_user((void __user *) data.target, domain->data, - domain->data_size)) { - up(&master->master_sem); - return -EFAULT; - } - - up(&master->master_sem); - return 0; -} - -/*****************************************************************************/ - -/** Set master debug level. - */ -int ec_cdev_ioctl_master_debug( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg /**< ioctl() argument. */ - ) -{ - return ec_master_debug_level(master, (unsigned int) arg); -} - -/*****************************************************************************/ - -/** Issue a bus scan. - */ -int ec_cdev_ioctl_master_rescan( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg /**< ioctl() argument. */ - ) -{ - master->fsm.rescan_required = 1; - return 0; -} - -/*****************************************************************************/ - -/** Set slave state. - */ -int ec_cdev_ioctl_slave_state( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg /**< ioctl() argument. */ - ) -{ - ec_ioctl_slave_state_t data; - ec_slave_t *slave; - - if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { - return -EFAULT; - } - - if (down_interruptible(&master->master_sem)) - return -EINTR; - - if (!(slave = ec_master_find_slave( - master, 0, data.slave_position))) { - up(&master->master_sem); - EC_MASTER_ERR(master, "Slave %u does not exist!\n", - data.slave_position); - return -EINVAL; - } - - ec_slave_request_state(slave, data.al_state); - - up(&master->master_sem); - return 0; -} - -/*****************************************************************************/ - -/** Get slave SDO information. - */ -int ec_cdev_ioctl_slave_sdo( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg /**< ioctl() argument. */ - ) -{ - ec_ioctl_slave_sdo_t data; - const ec_slave_t *slave; - const ec_sdo_t *sdo; - - if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { - return -EFAULT; - } - - if (down_interruptible(&master->master_sem)) - return -EINTR; - - if (!(slave = ec_master_find_slave_const( - master, 0, data.slave_position))) { - up(&master->master_sem); - EC_MASTER_ERR(master, "Slave %u does not exist!\n", - data.slave_position); - return -EINVAL; - } - - if (!(sdo = ec_slave_get_sdo_by_pos_const( - slave, data.sdo_position))) { - up(&master->master_sem); - EC_SLAVE_ERR(slave, "SDO %u does not exist!\n", data.sdo_position); - return -EINVAL; - } - - data.sdo_index = sdo->index; - data.max_subindex = sdo->max_subindex; - ec_cdev_strcpy(data.name, sdo->name); - - up(&master->master_sem); - - if (copy_to_user((void __user *) arg, &data, sizeof(data))) - return -EFAULT; - - return 0; -} - -/*****************************************************************************/ - -/** Get slave SDO entry information. - */ -int ec_cdev_ioctl_slave_sdo_entry( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg /**< ioctl() argument. */ - ) -{ - ec_ioctl_slave_sdo_entry_t data; - const ec_slave_t *slave; - const ec_sdo_t *sdo; - const ec_sdo_entry_t *entry; - - if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { - return -EFAULT; - } - - if (down_interruptible(&master->master_sem)) - return -EINTR; - - if (!(slave = ec_master_find_slave_const( - master, 0, data.slave_position))) { - up(&master->master_sem); - EC_MASTER_ERR(master, "Slave %u does not exist!\n", - data.slave_position); - return -EINVAL; - } - - if (data.sdo_spec <= 0) { - if (!(sdo = ec_slave_get_sdo_by_pos_const( - slave, -data.sdo_spec))) { - up(&master->master_sem); - EC_SLAVE_ERR(slave, "SDO %u does not exist!\n", -data.sdo_spec); - return -EINVAL; - } - } else { - if (!(sdo = ec_slave_get_sdo_const( - slave, data.sdo_spec))) { - up(&master->master_sem); - EC_SLAVE_ERR(slave, "SDO 0x%04X does not exist!\n", - data.sdo_spec); - return -EINVAL; - } - } - - if (!(entry = ec_sdo_get_entry_const( - sdo, data.sdo_entry_subindex))) { - up(&master->master_sem); - EC_SLAVE_ERR(slave, "SDO entry 0x%04X:%02X does not exist!\n", - sdo->index, data.sdo_entry_subindex); - return -EINVAL; - } - - data.data_type = entry->data_type; - data.bit_length = entry->bit_length; - data.read_access[EC_SDO_ENTRY_ACCESS_PREOP] = - entry->read_access[EC_SDO_ENTRY_ACCESS_PREOP]; - data.read_access[EC_SDO_ENTRY_ACCESS_SAFEOP] = - entry->read_access[EC_SDO_ENTRY_ACCESS_SAFEOP]; - data.read_access[EC_SDO_ENTRY_ACCESS_OP] = - entry->read_access[EC_SDO_ENTRY_ACCESS_OP]; - data.write_access[EC_SDO_ENTRY_ACCESS_PREOP] = - entry->write_access[EC_SDO_ENTRY_ACCESS_PREOP]; - data.write_access[EC_SDO_ENTRY_ACCESS_SAFEOP] = - entry->write_access[EC_SDO_ENTRY_ACCESS_SAFEOP]; - data.write_access[EC_SDO_ENTRY_ACCESS_OP] = - entry->write_access[EC_SDO_ENTRY_ACCESS_OP]; - ec_cdev_strcpy(data.description, entry->description); - - up(&master->master_sem); - - if (copy_to_user((void __user *) arg, &data, sizeof(data))) - return -EFAULT; - - return 0; -} - -/*****************************************************************************/ - -/** Upload SDO. - */ -int ec_cdev_ioctl_slave_sdo_upload( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg /**< ioctl() argument. */ - ) -{ - ec_ioctl_slave_sdo_upload_t data; - uint8_t *target; - int ret; - - if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { - return -EFAULT; - } - - if (!(target = kmalloc(data.target_size, GFP_KERNEL))) { - EC_MASTER_ERR(master, "Failed to allocate %u bytes" - " for SDO upload.\n", data.target_size); - return -ENOMEM; - } - - ret = ecrt_master_sdo_upload(master, data.slave_position, - data.sdo_index, data.sdo_entry_subindex, target, - data.target_size, &data.data_size, &data.abort_code); - - if (!ret) { - if (copy_to_user((void __user *) data.target, - target, data.data_size)) { - kfree(target); - return -EFAULT; - } - } - - kfree(target); - - if (__copy_to_user((void __user *) arg, &data, sizeof(data))) { - return -EFAULT; - } - - return 0; -} - -/*****************************************************************************/ - -/** Download SDO. - */ -int ec_cdev_ioctl_slave_sdo_download( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg /**< ioctl() argument. */ - ) -{ - ec_ioctl_slave_sdo_download_t data; - uint8_t *sdo_data; - int retval; - - if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { - return -EFAULT; - } - - if (!(sdo_data = kmalloc(data.data_size, GFP_KERNEL))) { - EC_MASTER_ERR(master, "Failed to allocate %u bytes" - " for SDO download.\n", data.data_size); - return -ENOMEM; - } - - if (copy_from_user(sdo_data, (void __user *) data.data, data.data_size)) { - kfree(sdo_data); - return -EFAULT; - } - - if (data.complete_access) { - retval = ecrt_master_sdo_download_complete(master, data.slave_position, - data.sdo_index, sdo_data, data.data_size, &data.abort_code); - } else { - retval = ecrt_master_sdo_download(master, data.slave_position, - data.sdo_index, data.sdo_entry_subindex, sdo_data, - data.data_size, &data.abort_code); - } - - kfree(sdo_data); - - if (__copy_to_user((void __user *) arg, &data, sizeof(data))) { - retval = -EFAULT; - } - - return retval; -} - -/*****************************************************************************/ - -/** Read a slave's SII. - */ -int ec_cdev_ioctl_slave_sii_read( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg /**< ioctl() argument. */ - ) -{ - ec_ioctl_slave_sii_t data; - const ec_slave_t *slave; - int retval; - - if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { - return -EFAULT; - } - - if (down_interruptible(&master->master_sem)) - return -EINTR; - - if (!(slave = ec_master_find_slave_const( - master, 0, data.slave_position))) { - up(&master->master_sem); - EC_MASTER_ERR(master, "Slave %u does not exist!\n", - data.slave_position); - return -EINVAL; - } - - if (!data.nwords - || data.offset + data.nwords > slave->sii_nwords) { - up(&master->master_sem); - EC_SLAVE_ERR(slave, "Invalid SII read offset/size %u/%u for slave SII" - " size %zu!\n", data.offset, data.nwords, slave->sii_nwords); - return -EINVAL; - } - - if (copy_to_user((void __user *) data.words, - slave->sii_words + data.offset, data.nwords * 2)) - retval = -EFAULT; - else - retval = 0; - - up(&master->master_sem); - return retval; -} - -/*****************************************************************************/ - -/** Write a slave's SII. - */ -int ec_cdev_ioctl_slave_sii_write( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg /**< ioctl() argument. */ - ) -{ - ec_ioctl_slave_sii_t data; - ec_slave_t *slave; - unsigned int byte_size; - uint16_t *words; - ec_sii_write_request_t request; - - if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { - return -EFAULT; - } - - if (!data.nwords) - return 0; - - byte_size = sizeof(uint16_t) * data.nwords; - if (!(words = kmalloc(byte_size, GFP_KERNEL))) { - EC_MASTER_ERR(master, "Failed to allocate %u bytes" - " for SII contents.\n", byte_size); - return -ENOMEM; - } - - if (copy_from_user(words, - (void __user *) data.words, byte_size)) { - kfree(words); - return -EFAULT; - } - - if (down_interruptible(&master->master_sem)) - return -EINTR; - - if (!(slave = ec_master_find_slave( - master, 0, data.slave_position))) { - up(&master->master_sem); - EC_MASTER_ERR(master, "Slave %u does not exist!\n", - data.slave_position); - kfree(words); - return -EINVAL; - } - - // init SII write request - INIT_LIST_HEAD(&request.list); - request.slave = slave; - request.words = words; - request.offset = data.offset; - request.nwords = data.nwords; - request.state = EC_INT_REQUEST_QUEUED; - - // schedule SII write request. - list_add_tail(&request.list, &master->sii_requests); - - up(&master->master_sem); - - // wait for processing through FSM - if (wait_event_interruptible(master->sii_queue, - request.state != EC_INT_REQUEST_QUEUED)) { - // interrupted by signal - down(&master->master_sem); - if (request.state == EC_INT_REQUEST_QUEUED) { - // abort request - list_del(&request.list); - up(&master->master_sem); - kfree(words); - return -EINTR; - } - up(&master->master_sem); - } - - // wait until master FSM has finished processing - wait_event(master->sii_queue, request.state != EC_INT_REQUEST_BUSY); - - kfree(words); - - return request.state == EC_INT_REQUEST_SUCCESS ? 0 : -EIO; -} - -/*****************************************************************************/ - -/** Read a slave's registers. - */ -int ec_cdev_ioctl_slave_reg_read( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg /**< ioctl() argument. */ - ) -{ - ec_ioctl_slave_reg_t data; - ec_slave_t *slave; - uint8_t *contents; - ec_reg_request_t request; - - if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { - return -EFAULT; - } - - if (!data.length) - return 0; - - if (!(contents = kmalloc(data.length, GFP_KERNEL))) { - EC_MASTER_ERR(master, "Failed to allocate %u bytes" - " for register data.\n", data.length); - return -ENOMEM; - } - - if (down_interruptible(&master->master_sem)) - return -EINTR; - - if (!(slave = ec_master_find_slave( - master, 0, data.slave_position))) { - up(&master->master_sem); - EC_MASTER_ERR(master, "Slave %u does not exist!\n", - data.slave_position); - return -EINVAL; - } - - // init register request - INIT_LIST_HEAD(&request.list); - request.slave = slave; - request.dir = EC_DIR_INPUT; - request.data = contents; - request.offset = data.offset; - request.length = data.length; - request.state = EC_INT_REQUEST_QUEUED; - - // schedule request. - list_add_tail(&request.list, &master->reg_requests); - - up(&master->master_sem); - - // wait for processing through FSM - if (wait_event_interruptible(master->reg_queue, - request.state != EC_INT_REQUEST_QUEUED)) { - // interrupted by signal - down(&master->master_sem); - if (request.state == EC_INT_REQUEST_QUEUED) { - // abort request - list_del(&request.list); - up(&master->master_sem); - kfree(contents); - return -EINTR; - } - up(&master->master_sem); - } - - // wait until master FSM has finished processing - wait_event(master->reg_queue, request.state != EC_INT_REQUEST_BUSY); - - if (request.state == EC_INT_REQUEST_SUCCESS) { - if (copy_to_user((void __user *) data.data, contents, data.length)) - return -EFAULT; - } - kfree(contents); - - return request.state == EC_INT_REQUEST_SUCCESS ? 0 : -EIO; -} - -/*****************************************************************************/ - -/** Write a slave's registers. - */ -int ec_cdev_ioctl_slave_reg_write( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg /**< ioctl() argument. */ - ) -{ - ec_ioctl_slave_reg_t data; - ec_slave_t *slave; - uint8_t *contents; - ec_reg_request_t request; - - if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { - return -EFAULT; - } - - if (!data.length) - return 0; - - if (!(contents = kmalloc(data.length, GFP_KERNEL))) { - EC_MASTER_ERR(master, "Failed to allocate %u bytes" - " for register data.\n", data.length); - return -ENOMEM; - } - - if (copy_from_user(contents, (void __user *) data.data, data.length)) { - kfree(contents); - return -EFAULT; - } - - if (down_interruptible(&master->master_sem)) - return -EINTR; - - if (!(slave = ec_master_find_slave( - master, 0, data.slave_position))) { - up(&master->master_sem); - EC_MASTER_ERR(master, "Slave %u does not exist!\n", - data.slave_position); - kfree(contents); - return -EINVAL; - } - - // init register request - INIT_LIST_HEAD(&request.list); - request.slave = slave; - request.dir = EC_DIR_OUTPUT; - request.data = contents; - request.offset = data.offset; - request.length = data.length; - request.state = EC_INT_REQUEST_QUEUED; - - // schedule request. - list_add_tail(&request.list, &master->reg_requests); - - up(&master->master_sem); - - // wait for processing through FSM - if (wait_event_interruptible(master->reg_queue, - request.state != EC_INT_REQUEST_QUEUED)) { - // interrupted by signal - down(&master->master_sem); - if (request.state == EC_INT_REQUEST_QUEUED) { - // abort request - list_del(&request.list); - up(&master->master_sem); - kfree(contents); - return -EINTR; - } - up(&master->master_sem); - } - - // wait until master FSM has finished processing - wait_event(master->reg_queue, request.state != EC_INT_REQUEST_BUSY); - - kfree(contents); - - return request.state == EC_INT_REQUEST_SUCCESS ? 0 : -EIO; -} - -/*****************************************************************************/ - -/** Get slave configuration information. - */ -int ec_cdev_ioctl_config( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg /**< ioctl() argument. */ - ) -{ - ec_ioctl_config_t data; - const ec_slave_config_t *sc; - uint8_t i; - - if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { - return -EFAULT; - } - - if (down_interruptible(&master->master_sem)) - return -EINTR; - - if (!(sc = ec_master_get_config_const( - master, data.config_index))) { - up(&master->master_sem); - EC_MASTER_ERR(master, "Slave config %u does not exist!\n", - data.config_index); - return -EINVAL; - } - - data.alias = sc->alias; - data.position = sc->position; - data.vendor_id = sc->vendor_id; - data.product_code = sc->product_code; - for (i = 0; i < EC_MAX_SYNC_MANAGERS; i++) { - data.syncs[i].dir = sc->sync_configs[i].dir; - data.syncs[i].watchdog_mode = sc->sync_configs[i].watchdog_mode; - data.syncs[i].pdo_count = - ec_pdo_list_count(&sc->sync_configs[i].pdos); - } - data.watchdog_divider = sc->watchdog_divider; - data.watchdog_intervals = sc->watchdog_intervals; - data.sdo_count = ec_slave_config_sdo_count(sc); - data.idn_count = ec_slave_config_idn_count(sc); - data.slave_position = sc->slave ? sc->slave->ring_position : -1; - data.dc_assign_activate = sc->dc_assign_activate; - for (i = 0; i < EC_SYNC_SIGNAL_COUNT; i++) { - data.dc_sync[i] = sc->dc_sync[i]; - } - - up(&master->master_sem); - - if (copy_to_user((void __user *) arg, &data, sizeof(data))) - return -EFAULT; - - return 0; -} - -/*****************************************************************************/ - -/** Get slave configuration PDO information. - */ -int ec_cdev_ioctl_config_pdo( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg /**< ioctl() argument. */ - ) -{ - ec_ioctl_config_pdo_t data; - const ec_slave_config_t *sc; - const ec_pdo_t *pdo; - - if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { - return -EFAULT; - } - - if (data.sync_index >= EC_MAX_SYNC_MANAGERS) { - EC_MASTER_ERR(master, "Invalid sync manager index %u!\n", - data.sync_index); - return -EINVAL; - } - - if (down_interruptible(&master->master_sem)) - return -EINTR; - - if (!(sc = ec_master_get_config_const( - master, data.config_index))) { - up(&master->master_sem); - EC_MASTER_ERR(master, "Slave config %u does not exist!\n", - data.config_index); - return -EINVAL; - } - - if (!(pdo = ec_pdo_list_find_pdo_by_pos_const( - &sc->sync_configs[data.sync_index].pdos, - data.pdo_pos))) { - up(&master->master_sem); - EC_MASTER_ERR(master, "Invalid PDO position!\n"); - return -EINVAL; - } - - data.index = pdo->index; - data.entry_count = ec_pdo_entry_count(pdo); - ec_cdev_strcpy(data.name, pdo->name); - - up(&master->master_sem); - - if (copy_to_user((void __user *) arg, &data, sizeof(data))) - return -EFAULT; - - return 0; -} - -/*****************************************************************************/ - -/** Get slave configuration PDO entry information. - */ -int ec_cdev_ioctl_config_pdo_entry( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg /**< ioctl() argument. */ - ) -{ - ec_ioctl_config_pdo_entry_t data; - const ec_slave_config_t *sc; - const ec_pdo_t *pdo; - const ec_pdo_entry_t *entry; - - if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { - return -EFAULT; - } - - if (data.sync_index >= EC_MAX_SYNC_MANAGERS) { - EC_MASTER_ERR(master, "Invalid sync manager index %u!\n", - data.sync_index); - return -EINVAL; - } - - if (down_interruptible(&master->master_sem)) - return -EINTR; - - if (!(sc = ec_master_get_config_const( - master, data.config_index))) { - up(&master->master_sem); - EC_MASTER_ERR(master, "Slave config %u does not exist!\n", - data.config_index); - return -EINVAL; - } - - if (!(pdo = ec_pdo_list_find_pdo_by_pos_const( - &sc->sync_configs[data.sync_index].pdos, - data.pdo_pos))) { - up(&master->master_sem); - EC_MASTER_ERR(master, "Invalid PDO position!\n"); - return -EINVAL; - } - - if (!(entry = ec_pdo_find_entry_by_pos_const( - pdo, data.entry_pos))) { - up(&master->master_sem); - EC_MASTER_ERR(master, "Entry not found!\n"); - return -EINVAL; - } - - data.index = entry->index; - data.subindex = entry->subindex; - data.bit_length = entry->bit_length; - ec_cdev_strcpy(data.name, entry->name); - - up(&master->master_sem); - - if (copy_to_user((void __user *) arg, &data, sizeof(data))) - return -EFAULT; - - return 0; -} - -/*****************************************************************************/ - -/** Get slave configuration SDO information. - */ -int ec_cdev_ioctl_config_sdo( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg /**< ioctl() argument. */ - ) -{ - ec_ioctl_config_sdo_t *ioctl; - const ec_slave_config_t *sc; - const ec_sdo_request_t *req; - - if (!(ioctl = kmalloc(sizeof(*ioctl), GFP_KERNEL))) { - return -ENOMEM; - } - - if (copy_from_user(ioctl, (void __user *) arg, sizeof(*ioctl))) { - kfree(ioctl); - return -EFAULT; - } - - if (down_interruptible(&master->master_sem)) { - kfree(ioctl); - return -EINTR; - } - - if (!(sc = ec_master_get_config_const( - master, ioctl->config_index))) { - up(&master->master_sem); - EC_MASTER_ERR(master, "Slave config %u does not exist!\n", - ioctl->config_index); - kfree(ioctl); - return -EINVAL; - } - - if (!(req = ec_slave_config_get_sdo_by_pos_const( - sc, ioctl->sdo_pos))) { - up(&master->master_sem); - EC_MASTER_ERR(master, "Invalid SDO position!\n"); - kfree(ioctl); - return -EINVAL; - } - - ioctl->index = req->index; - ioctl->subindex = req->subindex; - ioctl->size = req->data_size; - memcpy(ioctl->data, req->data, - min((u32) ioctl->size, (u32) EC_MAX_SDO_DATA_SIZE)); - - up(&master->master_sem); - - if (copy_to_user((void __user *) arg, ioctl, sizeof(*ioctl))) { - kfree(ioctl); - return -EFAULT; - } - - kfree(ioctl); - return 0; -} - -/*****************************************************************************/ - -/** Get slave configuration IDN information. - */ -int ec_cdev_ioctl_config_idn( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg /**< ioctl() argument. */ - ) -{ - ec_ioctl_config_idn_t *ioctl; - const ec_slave_config_t *sc; - const ec_soe_request_t *req; - - if (!(ioctl = kmalloc(sizeof(*ioctl), GFP_KERNEL))) { - return -ENOMEM; - } - - if (copy_from_user(ioctl, (void __user *) arg, sizeof(*ioctl))) { - kfree(ioctl); - return -EFAULT; - } - - if (down_interruptible(&master->master_sem)) { - kfree(ioctl); - return -EINTR; - } - - if (!(sc = ec_master_get_config_const( - master, ioctl->config_index))) { - up(&master->master_sem); - EC_MASTER_ERR(master, "Slave config %u does not exist!\n", - ioctl->config_index); - kfree(ioctl); - return -EINVAL; - } - - if (!(req = ec_slave_config_get_idn_by_pos_const( - sc, ioctl->idn_pos))) { - up(&master->master_sem); - EC_MASTER_ERR(master, "Invalid IDN position!\n"); - kfree(ioctl); - return -EINVAL; - } - - ioctl->drive_no = req->drive_no; - ioctl->idn = req->idn; - ioctl->state = req->state; - ioctl->size = req->data_size; - memcpy(ioctl->data, req->data, - min((u32) ioctl->size, (u32) EC_MAX_IDN_DATA_SIZE)); - - up(&master->master_sem); - - if (copy_to_user((void __user *) arg, ioctl, sizeof(*ioctl))) { - kfree(ioctl); - return -EFAULT; - } - - kfree(ioctl); - return 0; -} - -/*****************************************************************************/ - -#ifdef EC_EOE - -/** Get EoE handler information. - */ -int ec_cdev_ioctl_eoe_handler( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg /**< ioctl() argument. */ - ) -{ - ec_ioctl_eoe_handler_t data; - const ec_eoe_t *eoe; - - if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { - return -EFAULT; - } - - if (down_interruptible(&master->master_sem)) - return -EINTR; - - if (!(eoe = ec_master_get_eoe_handler_const(master, data.eoe_index))) { - up(&master->master_sem); - EC_MASTER_ERR(master, "EoE handler %u does not exist!\n", - data.eoe_index); - return -EINVAL; - } - - if (eoe->slave) { - data.slave_position = eoe->slave->ring_position; - } else { - data.slave_position = 0xffff; - } - snprintf(data.name, EC_DATAGRAM_NAME_SIZE, eoe->dev->name); - data.open = eoe->opened; - data.rx_bytes = eoe->stats.tx_bytes; - data.rx_rate = eoe->tx_rate; - data.tx_bytes = eoe->stats.rx_bytes; - data.tx_rate = eoe->tx_rate; - data.tx_queued_frames = eoe->tx_queued_frames; - data.tx_queue_size = eoe->tx_queue_size; - - up(&master->master_sem); - - if (copy_to_user((void __user *) arg, &data, sizeof(data))) - return -EFAULT; - - return 0; -} - -#endif - -/*****************************************************************************/ - -/** Request the master from userspace. - */ -int ec_cdev_ioctl_request( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg, /**< ioctl() argument. */ - ec_cdev_priv_t *priv /**< Private data structure of file handle. */ - ) -{ - ec_master_t *m; - int ret = 0; - - m = ecrt_request_master_err(master->index); - if (IS_ERR(m)) { - ret = PTR_ERR(m); - } else { - priv->requested = 1; - } - - return ret; -} - -/*****************************************************************************/ - -/** Create a domain. - */ -int ec_cdev_ioctl_create_domain( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg, /**< ioctl() argument. */ - ec_cdev_priv_t *priv /**< Private data structure of file handle. */ - ) -{ - ec_domain_t *domain; - - if (unlikely(!priv->requested)) - return -EPERM; - - domain = ecrt_master_create_domain_err(master); - if (IS_ERR(domain)) - return PTR_ERR(domain); - - return domain->index; -} - -/*****************************************************************************/ - -/** Create a slave configuration. - */ -int ec_cdev_ioctl_create_slave_config( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg, /**< ioctl() argument. */ - ec_cdev_priv_t *priv /**< Private data structure of file handle. */ - ) -{ - ec_ioctl_config_t data; - ec_slave_config_t *sc, *entry; - - if (unlikely(!priv->requested)) - return -EPERM; - - if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { - return -EFAULT; - } - - sc = ecrt_master_slave_config_err(master, data.alias, data.position, - data.vendor_id, data.product_code); - if (IS_ERR(sc)) - return PTR_ERR(sc); - - data.config_index = 0; - - if (down_interruptible(&master->master_sem)) - return -EINTR; - - list_for_each_entry(entry, &master->configs, list) { - if (entry == sc) - break; - data.config_index++; - } - - up(&master->master_sem); - - if (copy_to_user((void __user *) arg, &data, sizeof(data))) - return -EFAULT; - - return 0; -} - -/*****************************************************************************/ - -/** Activates the master. - */ -int ec_cdev_ioctl_activate( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg, /**< ioctl() argument. */ - ec_cdev_priv_t *priv /**< Private data structure of file handle. */ - ) -{ - ec_domain_t *domain; - off_t offset; - int ret; - - if (unlikely(!priv->requested)) - return -EPERM; - - /* Get the sum of the domains' process data sizes. */ - - priv->process_data_size = 0; - - if (down_interruptible(&master->master_sem)) - return -EINTR; - - list_for_each_entry(domain, &master->domains, list) { - priv->process_data_size += ecrt_domain_size(domain); - } - - up(&master->master_sem); - - if (priv->process_data_size) { - priv->process_data = vmalloc(priv->process_data_size); - if (!priv->process_data) { - priv->process_data_size = 0; - return -ENOMEM; - } - - /* Set the memory as external process data memory for the domains. */ - - offset = 0; - list_for_each_entry(domain, &master->domains, list) { - ecrt_domain_external_memory(domain, priv->process_data + offset); - offset += ecrt_domain_size(domain); - } - } - - ecrt_master_callbacks(master, ec_master_internal_send_cb, - ec_master_internal_receive_cb, master); - - ret = ecrt_master_activate(master); - if (ret < 0) - return ret; - - if (copy_to_user((void __user *) arg, - &priv->process_data_size, sizeof(size_t))) - return -EFAULT; - - return 0; -} - -/*****************************************************************************/ - -/** Deactivates the master. - */ -int ec_cdev_ioctl_deactivate( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg, /**< ioctl() argument. */ - ec_cdev_priv_t *priv /**< Private data structure of file handle. */ - ) -{ - if (unlikely(!priv->requested)) - return -EPERM; - - ecrt_master_deactivate(master); - return 0; -} - -/*****************************************************************************/ - -/** Set max. number of databytes in a cycle - */ -int ec_cdev_ioctl_set_send_interval( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg, /**< ioctl() argument. */ - ec_cdev_priv_t *priv /**< Private data structure of file handle. */ - ) -{ - size_t send_interval; - - if (copy_from_user(&send_interval, (void __user *) arg, - sizeof(send_interval))) { - return -EFAULT; - } - - if (down_interruptible(&master->master_sem)) - return -EINTR; - - ec_master_set_send_interval(master, send_interval); - - up(&master->master_sem); - return 0; -} - -/*****************************************************************************/ - -/** Send frames. - */ -int ec_cdev_ioctl_send( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg, /**< ioctl() argument. */ - ec_cdev_priv_t *priv /**< Private data structure of file handle. */ - ) -{ - if (unlikely(!priv->requested)) - return -EPERM; - - down(&master->io_sem); - ecrt_master_send(master); - up(&master->io_sem); - return 0; -} - -/*****************************************************************************/ - -/** Receive frames. - */ -int ec_cdev_ioctl_receive( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg, /**< ioctl() argument. */ - ec_cdev_priv_t *priv /**< Private data structure of file handle. */ - ) -{ - if (unlikely(!priv->requested)) - return -EPERM; - - down(&master->io_sem); - ecrt_master_receive(master); - up(&master->io_sem); - return 0; -} - -/*****************************************************************************/ - -/** Get the master state. - */ -int ec_cdev_ioctl_master_state( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg, /**< ioctl() argument. */ - ec_cdev_priv_t *priv /**< Private data structure of file handle. */ - ) -{ - ec_master_state_t data; - - if (unlikely(!priv->requested)) - return -EPERM; - - ecrt_master_state(master, &data); - - if (copy_to_user((void __user *) arg, &data, sizeof(data))) - return -EFAULT; - - return 0; -} - -/*****************************************************************************/ - -/** Get the master state. - */ -int ec_cdev_ioctl_master_link_state( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg, /**< ioctl() argument. */ - ec_cdev_priv_t *priv /**< Private data structure of file handle. */ - ) -{ - ec_ioctl_link_state_t ioctl; - ec_master_link_state_t state; - int ret; - - if (unlikely(!priv->requested)) { - return -EPERM; - } - - if (copy_from_user(&ioctl, (void __user *) arg, sizeof(ioctl))) { - return -EFAULT; - } - - ret = ecrt_master_link_state(master, ioctl.dev_idx, &state); - if (ret < 0) { - return ret; - } - - if (copy_to_user((void __user *) ioctl.state, &state, sizeof(state))) { - return -EFAULT; - } - - return 0; -} - -/*****************************************************************************/ - -/** Get the master state. - */ -int ec_cdev_ioctl_app_time( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg, /**< ioctl() argument. */ - ec_cdev_priv_t *priv /**< Private data structure of file handle. */ - ) -{ - ec_ioctl_app_time_t data; - - if (unlikely(!priv->requested)) - return -EPERM; - - if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { - return -EFAULT; - } - - ecrt_master_application_time(master, data.app_time); - return 0; -} - -/*****************************************************************************/ - -/** Sync the reference clock. - */ -int ec_cdev_ioctl_sync_ref( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg, /**< ioctl() argument. */ - ec_cdev_priv_t *priv /**< Private data structure of file handle. */ - ) -{ - if (unlikely(!priv->requested)) - return -EPERM; - - down(&master->io_sem); - ecrt_master_sync_reference_clock(master); - up(&master->io_sem); - return 0; -} - -/*****************************************************************************/ - -/** Sync the slave clocks. - */ -int ec_cdev_ioctl_sync_slaves( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg, /**< ioctl() argument. */ - ec_cdev_priv_t *priv /**< Private data structure of file handle. */ - ) -{ - if (unlikely(!priv->requested)) - return -EPERM; - - down(&master->io_sem); - ecrt_master_sync_slave_clocks(master); - up(&master->io_sem); - return 0; -} - -/*****************************************************************************/ - -/** Queue the sync monitoring datagram. - */ -int ec_cdev_ioctl_sync_mon_queue( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg, /**< ioctl() argument. */ - ec_cdev_priv_t *priv /**< Private data structure of file handle. */ - ) -{ - if (unlikely(!priv->requested)) - return -EPERM; - - down(&master->io_sem); - ecrt_master_sync_monitor_queue(master); - up(&master->io_sem); - return 0; -} - -/*****************************************************************************/ - -/** Processes the sync monitoring datagram. - */ -int ec_cdev_ioctl_sync_mon_process( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg, /**< ioctl() argument. */ - ec_cdev_priv_t *priv /**< Private data structure of file handle. */ - ) -{ - uint32_t time_diff; - - if (unlikely(!priv->requested)) - return -EPERM; - - down(&master->io_sem); - time_diff = ecrt_master_sync_monitor_process(master); - up(&master->io_sem); - - if (copy_to_user((void __user *) arg, &time_diff, sizeof(time_diff))) - return -EFAULT; - - return 0; -} - -/*****************************************************************************/ - -/** Reset configuration. - */ -int ec_cdev_ioctl_reset( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg, /**< ioctl() argument. */ - ec_cdev_priv_t *priv /**< Private data structure of file handle. */ - ) -{ - if (unlikely(!priv->requested)) - return -EPERM; - - down(&master->master_sem); - ecrt_master_reset(master); - up(&master->master_sem); - return 0; -} - -/*****************************************************************************/ - -/** Configure a sync manager. - */ -int ec_cdev_ioctl_sc_sync( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg, /**< ioctl() argument. */ - ec_cdev_priv_t *priv /**< Private data structure of file handle. */ - ) -{ - ec_ioctl_config_t data; - ec_slave_config_t *sc; - unsigned int i; - int ret = 0; - - if (unlikely(!priv->requested)) { - ret = -EPERM; - goto out_return; - } - - if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { - ret = -EFAULT; - goto out_return; - } - - if (down_interruptible(&master->master_sem)) { - ret = -EINTR; - goto out_return; - } - - if (!(sc = ec_master_get_config(master, data.config_index))) { - ret = -ENOENT; - goto out_up; - } - - for (i = 0; i < EC_MAX_SYNC_MANAGERS; i++) { - if (data.syncs[i].config_this) { - if (ecrt_slave_config_sync_manager(sc, i, data.syncs[i].dir, - data.syncs[i].watchdog_mode)) { - ret = -EINVAL; - goto out_up; - } - } - } - -out_up: - up(&master->master_sem); -out_return: - return ret; -} - -/*****************************************************************************/ - -/** Configure a slave's watchdogs. - */ -int ec_cdev_ioctl_sc_watchdog( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg, /**< ioctl() argument. */ - ec_cdev_priv_t *priv /**< Private data structure of file handle. */ - ) -{ - ec_ioctl_config_t data; - ec_slave_config_t *sc; - int ret = 0; - - if (unlikely(!priv->requested)) { - ret = -EPERM; - goto out_return; - } - - if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { - ret = -EFAULT; - goto out_return; - } - - if (down_interruptible(&master->master_sem)) { - ret = -EINTR; - goto out_return; - } - - if (!(sc = ec_master_get_config(master, data.config_index))) { - ret = -ENOENT; - goto out_up; - } - - ecrt_slave_config_watchdog(sc, - data.watchdog_divider, data.watchdog_intervals); - -out_up: - up(&master->master_sem); -out_return: - return ret; -} - -/*****************************************************************************/ - -/** Add a PDO to the assignment. - */ -int ec_cdev_ioctl_sc_add_pdo( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg, /**< ioctl() argument. */ - ec_cdev_priv_t *priv /**< Private data structure of file handle. */ - ) -{ - ec_ioctl_config_pdo_t data; - ec_slave_config_t *sc; - - if (unlikely(!priv->requested)) - return -EPERM; - - if (copy_from_user(&data, (void __user *) arg, sizeof(data))) - return -EFAULT; - - if (down_interruptible(&master->master_sem)) - return -EINTR; - - if (!(sc = ec_master_get_config(master, data.config_index))) { - up(&master->master_sem); - return -ENOENT; - } - - up(&master->master_sem); /** \fixme sc could be invalidated */ - - return ecrt_slave_config_pdo_assign_add(sc, data.sync_index, data.index); -} - -/*****************************************************************************/ - -/** Clears the PDO assignment. - */ -int ec_cdev_ioctl_sc_clear_pdos( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg, /**< ioctl() argument. */ - ec_cdev_priv_t *priv /**< Private data structure of file handle. */ - ) -{ - ec_ioctl_config_pdo_t data; - ec_slave_config_t *sc; - - if (unlikely(!priv->requested)) - return -EPERM; - - if (copy_from_user(&data, (void __user *) arg, sizeof(data))) - return -EFAULT; - - if (down_interruptible(&master->master_sem)) - return -EINTR; - - if (!(sc = ec_master_get_config(master, data.config_index))) { - up(&master->master_sem); - return -ENOENT; - } - - up(&master->master_sem); /** \fixme sc could be invalidated */ - - ecrt_slave_config_pdo_assign_clear(sc, data.sync_index); - return 0; -} - -/*****************************************************************************/ - -/** Add an entry to a PDO's mapping. - */ -int ec_cdev_ioctl_sc_add_entry( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg, /**< ioctl() argument. */ - ec_cdev_priv_t *priv /**< Private data structure of file handle. */ - ) -{ - ec_ioctl_add_pdo_entry_t data; - ec_slave_config_t *sc; - - if (unlikely(!priv->requested)) - return -EPERM; - - if (copy_from_user(&data, (void __user *) arg, sizeof(data))) - return -EFAULT; - - if (down_interruptible(&master->master_sem)) - return -EINTR; - - if (!(sc = ec_master_get_config(master, data.config_index))) { - up(&master->master_sem); - return -ENOENT; - } - - up(&master->master_sem); /** \fixme sc could be invalidated */ - - return ecrt_slave_config_pdo_mapping_add(sc, data.pdo_index, - data.entry_index, data.entry_subindex, data.entry_bit_length); -} - -/*****************************************************************************/ - -/** Clears the mapping of a PDO. - */ -int ec_cdev_ioctl_sc_clear_entries( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg, /**< ioctl() argument. */ - ec_cdev_priv_t *priv /**< Private data structure of file handle. */ - ) -{ - ec_ioctl_config_pdo_t data; - ec_slave_config_t *sc; - - if (unlikely(!priv->requested)) - return -EPERM; - - if (copy_from_user(&data, (void __user *) arg, sizeof(data))) - return -EFAULT; - - if (down_interruptible(&master->master_sem)) - return -EINTR; - - if (!(sc = ec_master_get_config(master, data.config_index))) { - up(&master->master_sem); - return -ENOENT; - } - - up(&master->master_sem); /** \fixme sc could be invalidated */ - - ecrt_slave_config_pdo_mapping_clear(sc, data.index); - return 0; -} - -/*****************************************************************************/ - -/** Registers a PDO entry. - */ -int ec_cdev_ioctl_sc_reg_pdo_entry( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg, /**< ioctl() argument. */ - ec_cdev_priv_t *priv /**< Private data structure of file handle. */ - ) -{ - ec_ioctl_reg_pdo_entry_t data; - ec_slave_config_t *sc; - ec_domain_t *domain; - int ret; - - if (unlikely(!priv->requested)) - return -EPERM; - - if (copy_from_user(&data, (void __user *) arg, sizeof(data))) - return -EFAULT; - - if (down_interruptible(&master->master_sem)) - return -EINTR; - - if (!(sc = ec_master_get_config(master, data.config_index))) { - up(&master->master_sem); - return -ENOENT; - } - - if (!(domain = ec_master_find_domain(master, data.domain_index))) { - up(&master->master_sem); - return -ENOENT; - } - - up(&master->master_sem); /** \fixme sc or domain could be invalidated */ - - ret = ecrt_slave_config_reg_pdo_entry(sc, data.entry_index, - data.entry_subindex, domain, &data.bit_position); - - if (copy_to_user((void __user *) arg, &data, sizeof(data))) - return -EFAULT; - - return ret; -} - -/*****************************************************************************/ - -/** Sets the DC AssignActivate word and the sync signal times. - */ -int ec_cdev_ioctl_sc_dc( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg, /**< ioctl() argument. */ - ec_cdev_priv_t *priv /**< Private data structure of file handle. */ - ) -{ - ec_ioctl_config_t data; - ec_slave_config_t *sc; - - if (unlikely(!priv->requested)) - return -EPERM; - - if (copy_from_user(&data, (void __user *) arg, sizeof(data))) - return -EFAULT; - - if (down_interruptible(&master->master_sem)) - return -EINTR; - - if (!(sc = ec_master_get_config(master, data.config_index))) { - up(&master->master_sem); - return -ENOENT; - } - - ecrt_slave_config_dc(sc, data.dc_assign_activate, - data.dc_sync[0].cycle_time, - data.dc_sync[0].shift_time, - data.dc_sync[1].cycle_time, - data.dc_sync[1].shift_time); - - up(&master->master_sem); - - return 0; -} - -/*****************************************************************************/ - -/** Configures an SDO. - */ -int ec_cdev_ioctl_sc_sdo( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg, /**< ioctl() argument. */ - ec_cdev_priv_t *priv /**< Private data structure of file handle. */ - ) -{ - ec_ioctl_sc_sdo_t data; - ec_slave_config_t *sc; - uint8_t *sdo_data = NULL; - int ret; - - if (unlikely(!priv->requested)) - return -EPERM; - - if (copy_from_user(&data, (void __user *) arg, sizeof(data))) - return -EFAULT; - - if (!data.size) - return -EINVAL; - - if (!(sdo_data = kmalloc(data.size, GFP_KERNEL))) { - return -ENOMEM; - } - - if (copy_from_user(sdo_data, (void __user *) data.data, data.size)) { - kfree(sdo_data); - return -EFAULT; - } - - if (down_interruptible(&master->master_sem)) { - kfree(sdo_data); - return -EINTR; - } - - if (!(sc = ec_master_get_config(master, data.config_index))) { - up(&master->master_sem); - kfree(sdo_data); - return -ENOENT; - } - - up(&master->master_sem); /** \fixme sc could be invalidated */ - - if (data.complete_access) { - ret = ecrt_slave_config_complete_sdo(sc, - data.index, sdo_data, data.size); - } else { - ret = ecrt_slave_config_sdo(sc, data.index, data.subindex, sdo_data, - data.size); - } - kfree(sdo_data); - return ret; -} - -/*****************************************************************************/ - -/** Create an SDO request. - */ -int ec_cdev_ioctl_sc_create_sdo_request( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg, /**< ioctl() argument. */ - ec_cdev_priv_t *priv /**< Private data structure of file handle. */ - ) -{ - ec_ioctl_sdo_request_t data; - ec_slave_config_t *sc; - ec_sdo_request_t *req; - - if (unlikely(!priv->requested)) - return -EPERM; - - if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { - return -EFAULT; - } - - data.request_index = 0; - - if (down_interruptible(&master->master_sem)) - return -EINTR; - - sc = ec_master_get_config(master, data.config_index); - if (!sc) { - up(&master->master_sem); - return -ENOENT; - } - - list_for_each_entry(req, &sc->sdo_requests, list) { - data.request_index++; - } - - up(&master->master_sem); /** \fixme sc could be invalidated */ - - req = ecrt_slave_config_create_sdo_request_err(sc, data.sdo_index, - data.sdo_subindex, data.size); - if (IS_ERR(req)) - return PTR_ERR(req); - - if (copy_to_user((void __user *) arg, &data, sizeof(data))) - return -EFAULT; - - return 0; -} - -/*****************************************************************************/ - -/** Create a VoE handler. - */ -int ec_cdev_ioctl_sc_create_voe_handler( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg, /**< ioctl() argument. */ - ec_cdev_priv_t *priv /**< Private data structure of file handle. */ - ) -{ - ec_ioctl_voe_t data; - ec_slave_config_t *sc; - ec_voe_handler_t *voe; - - if (unlikely(!priv->requested)) - return -EPERM; - - if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { - return -EFAULT; - } - - data.voe_index = 0; - - if (down_interruptible(&master->master_sem)) - return -EINTR; - - sc = ec_master_get_config(master, data.config_index); - if (!sc) { - up(&master->master_sem); - return -ENOENT; - } - - list_for_each_entry(voe, &sc->voe_handlers, list) { - data.voe_index++; - } - - up(&master->master_sem); /** \fixme sc could be invalidated */ - - voe = ecrt_slave_config_create_voe_handler_err(sc, data.size); - if (IS_ERR(voe)) - return PTR_ERR(voe); - - if (copy_to_user((void __user *) arg, &data, sizeof(data))) - return -EFAULT; - - return 0; -} - -/*****************************************************************************/ - -/** Get the slave configuration's state. - */ -int ec_cdev_ioctl_sc_state( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg, /**< ioctl() argument. */ - ec_cdev_priv_t *priv /**< Private data structure of file handle. */ - ) -{ - ec_ioctl_sc_state_t data; - const ec_slave_config_t *sc; - ec_slave_config_state_t state; - - if (unlikely(!priv->requested)) - return -EPERM; - - if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { - return -EFAULT; - } - - /* no locking of master_sem needed, because sc will not be deleted in the - * meantime. */ - - if (!(sc = ec_master_get_config_const(master, data.config_index))) { - return -ENOENT; - } - - ecrt_slave_config_state(sc, &state); - - if (copy_to_user((void __user *) data.state, &state, sizeof(state))) - return -EFAULT; - - return 0; -} - -/*****************************************************************************/ - -/** Configures an IDN. - */ -int ec_cdev_ioctl_sc_idn( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg, /**< ioctl() argument. */ - ec_cdev_priv_t *priv /**< Private data structure of file handle. */ - ) -{ - ec_ioctl_sc_idn_t ioctl; - ec_slave_config_t *sc; - uint8_t *data = NULL; - int ret; - - if (unlikely(!priv->requested)) - return -EPERM; - - if (copy_from_user(&ioctl, (void __user *) arg, sizeof(ioctl))) - return -EFAULT; - - if (!ioctl.size) - return -EINVAL; - - if (!(data = kmalloc(ioctl.size, GFP_KERNEL))) { - return -ENOMEM; - } - - if (copy_from_user(data, (void __user *) ioctl.data, ioctl.size)) { - kfree(data); - return -EFAULT; - } - - if (down_interruptible(&master->master_sem)) { - kfree(data); - return -EINTR; - } - - if (!(sc = ec_master_get_config(master, ioctl.config_index))) { - up(&master->master_sem); - kfree(data); - return -ENOENT; - } - - up(&master->master_sem); /** \fixme sc could be invalidated */ - - ret = ecrt_slave_config_idn( - sc, ioctl.drive_no, ioctl.idn, ioctl.al_state, data, ioctl.size); - kfree(data); - return ret; -} - -/*****************************************************************************/ - -/** Gets the domain's offset in the total process data. - */ -int ec_cdev_ioctl_domain_offset( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg, /**< ioctl() argument. */ - ec_cdev_priv_t *priv /**< Private data structure of file handle. */ - ) -{ - int offset = 0; - const ec_domain_t *domain; - - if (unlikely(!priv->requested)) - return -EPERM; - - if (down_interruptible(&master->master_sem)) { - return -EINTR; - } - - list_for_each_entry(domain, &master->domains, list) { - if (domain->index == arg) { - up(&master->master_sem); - return offset; - } - offset += ecrt_domain_size(domain); - } - - up(&master->master_sem); - return -ENOENT; -} - -/*****************************************************************************/ - -/** Process the domain. - */ -int ec_cdev_ioctl_domain_process( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg, /**< ioctl() argument. */ - ec_cdev_priv_t *priv /**< Private data structure of file handle. */ - ) -{ - ec_domain_t *domain; - - if (unlikely(!priv->requested)) - return -EPERM; - - /* no locking of master_sem needed, because domain will not be deleted in - * the meantime. */ - - if (!(domain = ec_master_find_domain(master, arg))) { - return -ENOENT; - } - - ecrt_domain_process(domain); - return 0; -} - -/*****************************************************************************/ - -/** Queue the domain. - */ -int ec_cdev_ioctl_domain_queue( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg, /**< ioctl() argument. */ - ec_cdev_priv_t *priv /**< Private data structure of file handle. */ - ) -{ - ec_domain_t *domain; - - if (unlikely(!priv->requested)) - return -EPERM; - - /* no locking of master_sem needed, because domain will not be deleted in - * the meantime. */ - - if (!(domain = ec_master_find_domain(master, arg))) { - return -ENOENT; - } - - ecrt_domain_queue(domain); - return 0; -} - -/*****************************************************************************/ - -/** Get the domain state. - */ -int ec_cdev_ioctl_domain_state( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg, /**< ioctl() argument. */ - ec_cdev_priv_t *priv /**< Private data structure of file handle. */ - ) -{ - ec_ioctl_domain_state_t data; - const ec_domain_t *domain; - ec_domain_state_t state; - - if (unlikely(!priv->requested)) - return -EPERM; - - if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { - return -EFAULT; - } - - /* no locking of master_sem needed, because domain will not be deleted in - * the meantime. */ - - if (!(domain = ec_master_find_domain_const(master, data.domain_index))) { - return -ENOENT; - } - - ecrt_domain_state(domain, &state); - - if (copy_to_user((void __user *) data.state, &state, sizeof(state))) - return -EFAULT; - - return 0; -} - -/*****************************************************************************/ - -/** Sets an SDO request's timeout. - */ -int ec_cdev_ioctl_sdo_request_timeout( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg, /**< ioctl() argument. */ - ec_cdev_priv_t *priv /**< Private data structure of file handle. */ - ) -{ - ec_ioctl_sdo_request_t data; - ec_slave_config_t *sc; - ec_sdo_request_t *req; - - if (unlikely(!priv->requested)) - return -EPERM; - - if (copy_from_user(&data, (void __user *) arg, sizeof(data))) - return -EFAULT; - - /* no locking of master_sem needed, because neither sc nor req will not be - * deleted in the meantime. */ - - if (!(sc = ec_master_get_config(master, data.config_index))) { - return -ENOENT; - } - - if (!(req = ec_slave_config_find_sdo_request(sc, data.request_index))) { - return -ENOENT; - } - - ecrt_sdo_request_timeout(req, data.timeout); - return 0; -} - -/*****************************************************************************/ - -/** Gets an SDO request's state. - */ -int ec_cdev_ioctl_sdo_request_state( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg, /**< ioctl() argument. */ - ec_cdev_priv_t *priv /**< Private data structure of file handle. */ - ) -{ - ec_ioctl_sdo_request_t data; - ec_slave_config_t *sc; - ec_sdo_request_t *req; - - if (unlikely(!priv->requested)) - return -EPERM; - - if (copy_from_user(&data, (void __user *) arg, sizeof(data))) - return -EFAULT; - - /* no locking of master_sem needed, because neither sc nor req will not be - * deleted in the meantime. */ - - if (!(sc = ec_master_get_config(master, data.config_index))) { - return -ENOENT; - } - - if (!(req = ec_slave_config_find_sdo_request(sc, data.request_index))) { - return -ENOENT; - } - - data.state = ecrt_sdo_request_state(req); - if (data.state == EC_REQUEST_SUCCESS && req->dir == EC_DIR_INPUT) - data.size = ecrt_sdo_request_data_size(req); - else - data.size = 0; - - if (copy_to_user((void __user *) arg, &data, sizeof(data))) - return -EFAULT; - - return 0; -} - -/*****************************************************************************/ - -/** Starts an SDO read operation. - */ -int ec_cdev_ioctl_sdo_request_read( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg, /**< ioctl() argument. */ - ec_cdev_priv_t *priv /**< Private data structure of file handle. */ - ) -{ - ec_ioctl_sdo_request_t data; - ec_slave_config_t *sc; - ec_sdo_request_t *req; - - if (unlikely(!priv->requested)) - return -EPERM; - - if (copy_from_user(&data, (void __user *) arg, sizeof(data))) - return -EFAULT; - - /* no locking of master_sem needed, because neither sc nor req will not be - * deleted in the meantime. */ - - if (!(sc = ec_master_get_config(master, data.config_index))) { - return -ENOENT; - } - - if (!(req = ec_slave_config_find_sdo_request(sc, data.request_index))) { - return -ENOENT; - } - - ecrt_sdo_request_read(req); - return 0; -} - -/*****************************************************************************/ - -/** Starts an SDO write operation. - */ -int ec_cdev_ioctl_sdo_request_write( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg, /**< ioctl() argument. */ - ec_cdev_priv_t *priv /**< Private data structure of file handle. */ - ) -{ - ec_ioctl_sdo_request_t data; - ec_slave_config_t *sc; - ec_sdo_request_t *req; - int ret; - - if (unlikely(!priv->requested)) - return -EPERM; - - if (copy_from_user(&data, (void __user *) arg, sizeof(data))) - return -EFAULT; - - if (!data.size) { - EC_MASTER_ERR(master, "SDO download: Data size may not be zero!\n"); - return -EINVAL; - } - - /* no locking of master_sem needed, because neither sc nor req will not be - * deleted in the meantime. */ - - if (!(sc = ec_master_get_config(master, data.config_index))) { - return -ENOENT; - } - - if (!(req = ec_slave_config_find_sdo_request(sc, data.request_index))) { - return -ENOENT; - } - - ret = ec_sdo_request_alloc(req, data.size); - if (ret) - return ret; - - if (copy_from_user(req->data, (void __user *) data.data, data.size)) - return -EFAULT; - - req->data_size = data.size; - ecrt_sdo_request_write(req); - return 0; -} - -/*****************************************************************************/ - -/** Read SDO data. - */ -int ec_cdev_ioctl_sdo_request_data( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg, /**< ioctl() argument. */ - ec_cdev_priv_t *priv /**< Private data structure of file handle. */ - ) -{ - ec_ioctl_sdo_request_t data; - ec_slave_config_t *sc; - ec_sdo_request_t *req; - - if (unlikely(!priv->requested)) - return -EPERM; - - if (copy_from_user(&data, (void __user *) arg, sizeof(data))) - return -EFAULT; - - /* no locking of master_sem needed, because neither sc nor req will not be - * deleted in the meantime. */ - - if (!(sc = ec_master_get_config(master, data.config_index))) { - return -ENOENT; - } - - if (!(req = ec_slave_config_find_sdo_request(sc, data.request_index))) { - return -ENOENT; - } - - if (copy_to_user((void __user *) data.data, ecrt_sdo_request_data(req), - ecrt_sdo_request_data_size(req))) - return -EFAULT; - - return 0; -} - -/*****************************************************************************/ - -/** Sets the VoE send header. - */ -int ec_cdev_ioctl_voe_send_header( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg, /**< ioctl() argument. */ - ec_cdev_priv_t *priv /**< Private data structure of file handle. */ - ) -{ - ec_ioctl_voe_t data; - ec_slave_config_t *sc; - ec_voe_handler_t *voe; - uint32_t vendor_id; - uint16_t vendor_type; - - if (unlikely(!priv->requested)) - return -EPERM; - - if (copy_from_user(&data, (void __user *) arg, sizeof(data))) - return -EFAULT; - - if (get_user(vendor_id, data.vendor_id)) - return -EFAULT; - - if (get_user(vendor_type, data.vendor_type)) - return -EFAULT; - - /* no locking of master_sem needed, because neither sc nor voe will not be - * deleted in the meantime. */ - - if (!(sc = ec_master_get_config(master, data.config_index))) { - return -ENOENT; - } - - if (!(voe = ec_slave_config_find_voe_handler(sc, data.voe_index))) { - return -ENOENT; - } - - ecrt_voe_handler_send_header(voe, vendor_id, vendor_type); - return 0; -} - -/*****************************************************************************/ - -/** Gets the received VoE header. - */ -int ec_cdev_ioctl_voe_rec_header( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg, /**< ioctl() argument. */ - ec_cdev_priv_t *priv /**< Private data structure of file handle. */ - ) -{ - ec_ioctl_voe_t data; - ec_slave_config_t *sc; - ec_voe_handler_t *voe; - uint32_t vendor_id; - uint16_t vendor_type; - - if (unlikely(!priv->requested)) - return -EPERM; - - if (copy_from_user(&data, (void __user *) arg, sizeof(data))) - return -EFAULT; - - /* no locking of master_sem needed, because neither sc nor voe will not be - * deleted in the meantime. */ - - if (!(sc = ec_master_get_config(master, data.config_index))) { - return -ENOENT; - } - - if (!(voe = ec_slave_config_find_voe_handler(sc, data.voe_index))) { - return -ENOENT; - } - - ecrt_voe_handler_received_header(voe, &vendor_id, &vendor_type); - - if (likely(data.vendor_id)) - if (put_user(vendor_id, data.vendor_id)) - return -EFAULT; - - if (likely(data.vendor_type)) - if (put_user(vendor_type, data.vendor_type)) - return -EFAULT; - - return 0; -} - -/*****************************************************************************/ - -/** Starts a VoE read operation. - */ -int ec_cdev_ioctl_voe_read( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg, /**< ioctl() argument. */ - ec_cdev_priv_t *priv /**< Private data structure of file handle. */ - ) -{ - ec_ioctl_voe_t data; - ec_slave_config_t *sc; - ec_voe_handler_t *voe; - - if (unlikely(!priv->requested)) - return -EPERM; - - if (copy_from_user(&data, (void __user *) arg, sizeof(data))) - return -EFAULT; - - /* no locking of master_sem needed, because neither sc nor voe will not be - * deleted in the meantime. */ - - if (!(sc = ec_master_get_config(master, data.config_index))) { - return -ENOENT; - } - - if (!(voe = ec_slave_config_find_voe_handler(sc, data.voe_index))) { - return -ENOENT; - } - - ecrt_voe_handler_read(voe); - return 0; -} - -/*****************************************************************************/ - -/** Starts a VoE read operation without sending a sync message first. - */ -int ec_cdev_ioctl_voe_read_nosync( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg, /**< ioctl() argument. */ - ec_cdev_priv_t *priv /**< Private data structure of file handle. */ - ) -{ - ec_ioctl_voe_t data; - ec_slave_config_t *sc; - ec_voe_handler_t *voe; - - if (unlikely(!priv->requested)) - return -EPERM; - - if (copy_from_user(&data, (void __user *) arg, sizeof(data))) - return -EFAULT; - - /* no locking of master_sem needed, because neither sc nor voe will not be - * deleted in the meantime. */ - - if (!(sc = ec_master_get_config(master, data.config_index))) { - return -ENOENT; - } - - if (!(voe = ec_slave_config_find_voe_handler(sc, data.voe_index))) { - return -ENOENT; - } - - ecrt_voe_handler_read_nosync(voe); - return 0; -} - -/*****************************************************************************/ - -/** Starts a VoE write operation. - */ -int ec_cdev_ioctl_voe_write( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg, /**< ioctl() argument. */ - ec_cdev_priv_t *priv /**< Private data structure of file handle. */ - ) -{ - ec_ioctl_voe_t data; - ec_slave_config_t *sc; - ec_voe_handler_t *voe; - - if (unlikely(!priv->requested)) - return -EPERM; - - if (copy_from_user(&data, (void __user *) arg, sizeof(data))) - return -EFAULT; - - /* no locking of master_sem needed, because neither sc nor voe will not be - * deleted in the meantime. */ - - if (!(sc = ec_master_get_config(master, data.config_index))) { - return -ENOENT; - } - - if (!(voe = ec_slave_config_find_voe_handler(sc, data.voe_index))) { - return -ENOENT; - } - - if (data.size) { - if (data.size > ec_voe_handler_mem_size(voe)) - return -EOVERFLOW; - - if (copy_from_user(ecrt_voe_handler_data(voe), - (void __user *) data.data, data.size)) - return -EFAULT; - } - - ecrt_voe_handler_write(voe, data.size); - return 0; -} - -/*****************************************************************************/ - -/** Executes the VoE state machine. - */ -int ec_cdev_ioctl_voe_exec( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg, /**< ioctl() argument. */ - ec_cdev_priv_t *priv /**< Private data structure of file handle. */ - ) -{ - ec_ioctl_voe_t data; - ec_slave_config_t *sc; - ec_voe_handler_t *voe; - - if (unlikely(!priv->requested)) - return -EPERM; - - if (copy_from_user(&data, (void __user *) arg, sizeof(data))) - return -EFAULT; - - /* no locking of master_sem needed, because neither sc nor voe will not be - * deleted in the meantime. */ - - if (!(sc = ec_master_get_config(master, data.config_index))) { - return -ENOENT; - } - - if (!(voe = ec_slave_config_find_voe_handler(sc, data.voe_index))) { - return -ENOENT; - } - - data.state = ecrt_voe_handler_execute(voe); - if (data.state == EC_REQUEST_SUCCESS && voe->dir == EC_DIR_INPUT) - data.size = ecrt_voe_handler_data_size(voe); - else - data.size = 0; - - if (copy_to_user((void __user *) arg, &data, sizeof(data))) - return -EFAULT; - - return 0; -} - -/*****************************************************************************/ - -/** Reads the received VoE data. - */ -int ec_cdev_ioctl_voe_data( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg, /**< ioctl() argument. */ - ec_cdev_priv_t *priv /**< Private data structure of file handle. */ - ) -{ - ec_ioctl_voe_t data; - ec_slave_config_t *sc; - ec_voe_handler_t *voe; - - if (unlikely(!priv->requested)) - return -EPERM; - - if (copy_from_user(&data, (void __user *) arg, sizeof(data))) - return -EFAULT; - - /* no locking of master_sem needed, because neither sc nor voe will not be - * deleted in the meantime. */ - - if (!(sc = ec_master_get_config(master, data.config_index))) { - return -ENOENT; - } - - if (!(voe = ec_slave_config_find_voe_handler(sc, data.voe_index))) { - return -ENOENT; - } - - if (copy_to_user((void __user *) data.data, ecrt_voe_handler_data(voe), - ecrt_voe_handler_data_size(voe))) - return -EFAULT; - - return 0; -} - -/*****************************************************************************/ - -/** Read a file from a slave via FoE. - */ -int ec_cdev_ioctl_slave_foe_read( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg /**< ioctl() argument. */ - ) -{ - ec_ioctl_slave_foe_t data; - ec_master_foe_request_t request; - int retval; - - if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { - return -EFAULT; - } - - ec_foe_request_init(&request.req, data.file_name); - ec_foe_request_read(&request.req); - ec_foe_request_alloc(&request.req, 10000); // FIXME - - if (down_interruptible(&master->master_sem)) { - ec_foe_request_clear(&request.req); - return -EINTR; - } - - if (!(request.slave = ec_master_find_slave( - master, 0, data.slave_position))) { - up(&master->master_sem); - ec_foe_request_clear(&request.req); - EC_MASTER_ERR(master, "Slave %u does not exist!\n", - data.slave_position); - return -EINVAL; - } - - // schedule request. - list_add_tail(&request.list, &request.slave->foe_requests); - - up(&master->master_sem); - - EC_SLAVE_DBG(request.slave, 1, "Scheduled FoE read request.\n"); - - // wait for processing through FSM - if (wait_event_interruptible(request.slave->foe_queue, - request.req.state != EC_INT_REQUEST_QUEUED)) { - // interrupted by signal - down(&master->master_sem); - if (request.req.state == EC_INT_REQUEST_QUEUED) { - list_del(&request.list); - up(&master->master_sem); - ec_foe_request_clear(&request.req); - return -EINTR; - } - // request already processing: interrupt not possible. - up(&master->master_sem); - } - - // wait until master FSM has finished processing - wait_event(request.slave->foe_queue, - request.req.state != EC_INT_REQUEST_BUSY); - - data.result = request.req.result; - data.error_code = request.req.error_code; - - EC_SLAVE_DBG(request.slave, 1, "Read %zd bytes via FoE" - " (result = 0x%x).\n", request.req.data_size, request.req.result); - - if (request.req.state != EC_INT_REQUEST_SUCCESS) { - data.data_size = 0; - retval = -EIO; - } else { - if (request.req.data_size > data.buffer_size) { - EC_MASTER_ERR(master, "Buffer too small.\n"); - ec_foe_request_clear(&request.req); - return -EOVERFLOW; - } - data.data_size = request.req.data_size; - if (copy_to_user((void __user *) data.buffer, - request.req.buffer, data.data_size)) { - ec_foe_request_clear(&request.req); - return -EFAULT; - } - retval = 0; - } - - if (__copy_to_user((void __user *) arg, &data, sizeof(data))) { - retval = -EFAULT; - } - - EC_SLAVE_DBG(request.slave, 1, "Finished FoE read request.\n"); - - ec_foe_request_clear(&request.req); - - return retval; -} - -/*****************************************************************************/ - -/** Write a file to a slave via FoE - */ -int ec_cdev_ioctl_slave_foe_write( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg /**< ioctl() argument. */ - ) -{ - ec_ioctl_slave_foe_t data; - ec_master_foe_request_t request; - int retval; - - if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { - return -EFAULT; - } - - INIT_LIST_HEAD(&request.list); - - ec_foe_request_init(&request.req, data.file_name); - - if (ec_foe_request_alloc(&request.req, data.buffer_size)) { - ec_foe_request_clear(&request.req); - return -ENOMEM; - } - if (copy_from_user(request.req.buffer, - (void __user *) data.buffer, data.buffer_size)) { - ec_foe_request_clear(&request.req); - return -EFAULT; - } - request.req.data_size = data.buffer_size; - ec_foe_request_write(&request.req); - - if (down_interruptible(&master->master_sem)) { - ec_foe_request_clear(&request.req); - return -EINTR; - } - - if (!(request.slave = ec_master_find_slave( - master, 0, data.slave_position))) { - up(&master->master_sem); - EC_MASTER_ERR(master, "Slave %u does not exist!\n", - data.slave_position); - ec_foe_request_clear(&request.req); - return -EINVAL; - } - - EC_SLAVE_DBG(request.slave, 1, "Scheduling FoE write request.\n"); - - // schedule FoE write request. - list_add_tail(&request.list, &request.slave->foe_requests); - - up(&master->master_sem); - - // wait for processing through FSM - if (wait_event_interruptible(request.slave->foe_queue, - request.req.state != EC_INT_REQUEST_QUEUED)) { - // interrupted by signal - down(&master->master_sem); - if (request.req.state == EC_INT_REQUEST_QUEUED) { - // abort request - list_del(&request.list); - up(&master->master_sem); - ec_foe_request_clear(&request.req); - return -EINTR; - } - up(&master->master_sem); - } - - // wait until master FSM has finished processing - wait_event(request.slave->foe_queue, - request.req.state != EC_INT_REQUEST_BUSY); - - data.result = request.req.result; - data.error_code = request.req.error_code; - - retval = request.req.state == EC_INT_REQUEST_SUCCESS ? 0 : -EIO; - - if (__copy_to_user((void __user *) arg, &data, sizeof(data))) { - retval = -EFAULT; - } - - ec_foe_request_clear(&request.req); - - EC_SLAVE_DBG(request.slave, 1, "Finished FoE write request.\n"); - - return retval; -} - -/*****************************************************************************/ - -/** Read an SoE IDN. - */ -int ec_cdev_ioctl_slave_soe_read( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg /**< ioctl() argument. */ - ) -{ - ec_ioctl_slave_soe_read_t ioctl; - u8 *data; - int retval; - - if (copy_from_user(&ioctl, (void __user *) arg, sizeof(ioctl))) { - return -EFAULT; - } - - data = kmalloc(ioctl.mem_size, GFP_KERNEL); - if (!data) { - EC_MASTER_ERR(master, "Failed to allocate %u bytes of IDN data.\n", - ioctl.mem_size); - return -ENOMEM; - } - - retval = ecrt_master_read_idn(master, ioctl.slave_position, - ioctl.drive_no, ioctl.idn, data, ioctl.mem_size, &ioctl.data_size, - &ioctl.error_code); - if (retval) { - kfree(data); - return retval; - } - - if (copy_to_user((void __user *) ioctl.data, - data, ioctl.data_size)) { - kfree(data); - return -EFAULT; - } - kfree(data); - - if (__copy_to_user((void __user *) arg, &ioctl, sizeof(ioctl))) { - retval = -EFAULT; - } - - EC_MASTER_DBG(master, 1, "Finished SoE read request.\n"); - return retval; -} - -/*****************************************************************************/ - -/** Write an IDN to a slave via SoE. - */ -int ec_cdev_ioctl_slave_soe_write( - ec_master_t *master, /**< EtherCAT master. */ - unsigned long arg /**< ioctl() argument. */ - ) -{ - ec_ioctl_slave_soe_write_t ioctl; - u8 *data; - int retval; - - if (copy_from_user(&ioctl, (void __user *) arg, sizeof(ioctl))) { - return -EFAULT; - } - - data = kmalloc(ioctl.data_size, GFP_KERNEL); - if (!data) { - EC_MASTER_ERR(master, "Failed to allocate %zu bytes of IDN data.\n", - ioctl.data_size); - return -ENOMEM; - } - if (copy_from_user(data, (void __user *) ioctl.data, ioctl.data_size)) { - kfree(data); - return -EFAULT; - } - - retval = ecrt_master_write_idn(master, ioctl.slave_position, - ioctl.drive_no, ioctl.idn, data, ioctl.data_size, - &ioctl.error_code); - kfree(data); - if (retval) { - return retval; - } - - if (__copy_to_user((void __user *) arg, &ioctl, sizeof(ioctl))) { - retval = -EFAULT; - } - - EC_MASTER_DBG(master, 1, "Finished SoE write request.\n"); - return retval; -} - /****************************************************************************** * File operations *****************************************************************************/ @@ -3432,13 +155,14 @@ } priv->cdev = cdev; - priv->requested = 0; - priv->process_data = NULL; - priv->process_data_size = 0; + priv->ctx.writable = (filp->f_mode & FMODE_WRITE) != 0; + priv->ctx.requested = 0; + priv->ctx.process_data = NULL; + priv->ctx.process_data_size = 0; filp->private_data = priv; -#if DEBUG_IOCTL +#if DEBUG EC_MASTER_DBG(cdev->master, 0, "File opened.\n"); #endif return 0; @@ -3453,13 +177,15 @@ ec_cdev_priv_t *priv = (ec_cdev_priv_t *) filp->private_data; ec_master_t *master = priv->cdev->master; - if (priv->requested) + if (priv->ctx.requested) { ecrt_release_master(master); - - if (priv->process_data) - vfree(priv->process_data); - -#if DEBUG_IOCTL + } + + if (priv->ctx.process_data) { + vfree(priv->ctx.process_data); + } + +#if DEBUG EC_MASTER_DBG(master, 0, "File closed.\n"); #endif @@ -3473,438 +199,15 @@ */ long eccdev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { -#if DEBUG_LATENCY - cycles_t a = get_cycles(), b; - unsigned int t; -#endif ec_cdev_priv_t *priv = (ec_cdev_priv_t *) filp->private_data; - ec_master_t *master = priv->cdev->master; - int ret; - -#if DEBUG_IOCTL - EC_MASTER_DBG(master, 0, "ioctl(filp = 0x%p, cmd = 0x%08x (0x%02x)," - " arg = 0x%lx)\n", filp, cmd, _IOC_NR(cmd), arg); -#endif - - switch (cmd) { - case EC_IOCTL_MODULE: - ret = ec_cdev_ioctl_module(arg); - break; - case EC_IOCTL_MASTER: - ret = ec_cdev_ioctl_master(master, arg); - break; - case EC_IOCTL_SLAVE: - ret = ec_cdev_ioctl_slave(master, arg); - break; - case EC_IOCTL_SLAVE_SYNC: - ret = ec_cdev_ioctl_slave_sync(master, arg); - break; - case EC_IOCTL_SLAVE_SYNC_PDO: - ret = ec_cdev_ioctl_slave_sync_pdo(master, arg); - break; - case EC_IOCTL_SLAVE_SYNC_PDO_ENTRY: - ret = ec_cdev_ioctl_slave_sync_pdo_entry(master, arg); - break; - case EC_IOCTL_DOMAIN: - ret = ec_cdev_ioctl_domain(master, arg); - break; - case EC_IOCTL_DOMAIN_FMMU: - ret = ec_cdev_ioctl_domain_fmmu(master, arg); - break; - case EC_IOCTL_DOMAIN_DATA: - ret = ec_cdev_ioctl_domain_data(master, arg); - break; - case EC_IOCTL_MASTER_DEBUG: - if (!(filp->f_mode & FMODE_WRITE)) { - ret = -EPERM; - break; - } - ret = ec_cdev_ioctl_master_debug(master, arg); - break; - case EC_IOCTL_MASTER_RESCAN: - if (!(filp->f_mode & FMODE_WRITE)) { - ret = -EPERM; - break; - } - ret = ec_cdev_ioctl_master_rescan(master, arg); - break; - case EC_IOCTL_SLAVE_STATE: - if (!(filp->f_mode & FMODE_WRITE)) { - ret = -EPERM; - break; - } - ret = ec_cdev_ioctl_slave_state(master, arg); - break; - case EC_IOCTL_SLAVE_SDO: - ret = ec_cdev_ioctl_slave_sdo(master, arg); - break; - case EC_IOCTL_SLAVE_SDO_ENTRY: - ret = ec_cdev_ioctl_slave_sdo_entry(master, arg); - break; - case EC_IOCTL_SLAVE_SDO_UPLOAD: - ret = ec_cdev_ioctl_slave_sdo_upload(master, arg); - break; - case EC_IOCTL_SLAVE_SDO_DOWNLOAD: - if (!(filp->f_mode & FMODE_WRITE)) { - ret = -EPERM; - break; - } - ret = ec_cdev_ioctl_slave_sdo_download(master, arg); - break; - case EC_IOCTL_SLAVE_SII_READ: - ret = ec_cdev_ioctl_slave_sii_read(master, arg); - break; - case EC_IOCTL_SLAVE_SII_WRITE: - if (!(filp->f_mode & FMODE_WRITE)) { - ret = -EPERM; - break; - } - ret = ec_cdev_ioctl_slave_sii_write(master, arg); - break; - case EC_IOCTL_SLAVE_REG_READ: - ret = ec_cdev_ioctl_slave_reg_read(master, arg); - break; - case EC_IOCTL_SLAVE_REG_WRITE: - if (!(filp->f_mode & FMODE_WRITE)) { - ret = -EPERM; - break; - } - ret = ec_cdev_ioctl_slave_reg_write(master, arg); - break; - case EC_IOCTL_SLAVE_FOE_READ: - ret = ec_cdev_ioctl_slave_foe_read(master, arg); - break; - case EC_IOCTL_SLAVE_FOE_WRITE: - if (!(filp->f_mode & FMODE_WRITE)) { - ret = -EPERM; - break; - } - ret = ec_cdev_ioctl_slave_foe_write(master, arg); - break; - case EC_IOCTL_SLAVE_SOE_READ: - ret = ec_cdev_ioctl_slave_soe_read(master, arg); - break; - case EC_IOCTL_SLAVE_SOE_WRITE: - if (!(filp->f_mode & FMODE_WRITE)) { - ret = -EPERM; - break; - } - ret = ec_cdev_ioctl_slave_soe_write(master, arg); - break; - case EC_IOCTL_CONFIG: - ret = ec_cdev_ioctl_config(master, arg); - break; - case EC_IOCTL_CONFIG_PDO: - ret = ec_cdev_ioctl_config_pdo(master, arg); - break; - case EC_IOCTL_CONFIG_PDO_ENTRY: - ret = ec_cdev_ioctl_config_pdo_entry(master, arg); - break; - case EC_IOCTL_CONFIG_SDO: - ret = ec_cdev_ioctl_config_sdo(master, arg); - break; - case EC_IOCTL_CONFIG_IDN: - ret = ec_cdev_ioctl_config_idn(master, arg); - break; -#ifdef EC_EOE - case EC_IOCTL_EOE_HANDLER: - ret = ec_cdev_ioctl_eoe_handler(master, arg); - break; -#endif - case EC_IOCTL_REQUEST: - if (!(filp->f_mode & FMODE_WRITE)) { - ret = -EPERM; - break; - } - ret = ec_cdev_ioctl_request(master, arg, priv); - break; - case EC_IOCTL_CREATE_DOMAIN: - if (!(filp->f_mode & FMODE_WRITE)) { - ret = -EPERM; - break; - } - ret = ec_cdev_ioctl_create_domain(master, arg, priv); - break; - case EC_IOCTL_CREATE_SLAVE_CONFIG: - if (!(filp->f_mode & FMODE_WRITE)) { - ret = -EPERM; - break; - } - ret = ec_cdev_ioctl_create_slave_config(master, arg, priv); - break; - case EC_IOCTL_ACTIVATE: - if (!(filp->f_mode & FMODE_WRITE)) { - ret = -EPERM; - break; - } - ret = ec_cdev_ioctl_activate(master, arg, priv); - break; - case EC_IOCTL_DEACTIVATE: - if (!(filp->f_mode & FMODE_WRITE)) { - ret = -EPERM; - break; - } - ret = ec_cdev_ioctl_deactivate(master, arg, priv); - break; - case EC_IOCTL_SEND: - if (!(filp->f_mode & FMODE_WRITE)) { - ret = -EPERM; - break; - } - ret = ec_cdev_ioctl_send(master, arg, priv); - break; - case EC_IOCTL_RECEIVE: - if (!(filp->f_mode & FMODE_WRITE)) { - ret = -EPERM; - break; - } - ret = ec_cdev_ioctl_receive(master, arg, priv); - break; - case EC_IOCTL_MASTER_STATE: - ret = ec_cdev_ioctl_master_state(master, arg, priv); - break; - case EC_IOCTL_MASTER_LINK_STATE: - ret = ec_cdev_ioctl_master_link_state(master, arg, priv); - break; - case EC_IOCTL_APP_TIME: - if (!(filp->f_mode & FMODE_WRITE)) { - ret = -EPERM; - break; - } - ret = ec_cdev_ioctl_app_time(master, arg, priv); - break; - case EC_IOCTL_SYNC_REF: - if (!(filp->f_mode & FMODE_WRITE)) { - ret = -EPERM; - break; - } - ret = ec_cdev_ioctl_sync_ref(master, arg, priv); - break; - case EC_IOCTL_SYNC_SLAVES: - if (!(filp->f_mode & FMODE_WRITE)) { - ret = -EPERM; - break; - } - ret = ec_cdev_ioctl_sync_slaves(master, arg, priv); - break; - case EC_IOCTL_SYNC_MON_QUEUE: - if (!(filp->f_mode & FMODE_WRITE)) { - ret = -EPERM; - break; - } - ret = ec_cdev_ioctl_sync_mon_queue(master, arg, priv); - break; - case EC_IOCTL_SYNC_MON_PROCESS: - if (!(filp->f_mode & FMODE_WRITE)) { - ret = -EPERM; - break; - } - ret = ec_cdev_ioctl_sync_mon_process(master, arg, priv); - break; - case EC_IOCTL_RESET: - if (!(filp->f_mode & FMODE_WRITE)) { - ret = -EPERM; - break; - } - ret = ec_cdev_ioctl_reset(master, arg, priv); - break; - case EC_IOCTL_SC_SYNC: - if (!(filp->f_mode & FMODE_WRITE)) { - ret = -EPERM; - break; - } - ret = ec_cdev_ioctl_sc_sync(master, arg, priv); - break; - case EC_IOCTL_SC_WATCHDOG: - if (!(filp->f_mode & FMODE_WRITE)) { - ret = -EPERM; - break; - } - ret = ec_cdev_ioctl_sc_watchdog(master, arg, priv); - break; - case EC_IOCTL_SC_ADD_PDO: - if (!(filp->f_mode & FMODE_WRITE)) { - ret = -EPERM; - break; - } - ret = ec_cdev_ioctl_sc_add_pdo(master, arg, priv); - break; - case EC_IOCTL_SC_CLEAR_PDOS: - if (!(filp->f_mode & FMODE_WRITE)) { - ret = -EPERM; - break; - } - ret = ec_cdev_ioctl_sc_clear_pdos(master, arg, priv); - break; - case EC_IOCTL_SC_ADD_ENTRY: - if (!(filp->f_mode & FMODE_WRITE)) { - ret = -EPERM; - break; - } - ret = ec_cdev_ioctl_sc_add_entry(master, arg, priv); - break; - case EC_IOCTL_SC_CLEAR_ENTRIES: - if (!(filp->f_mode & FMODE_WRITE)) { - ret = -EPERM; - break; - } - ret = ec_cdev_ioctl_sc_clear_entries(master, arg, priv); - break; - case EC_IOCTL_SC_REG_PDO_ENTRY: - if (!(filp->f_mode & FMODE_WRITE)) { - ret = -EPERM; - break; - } - ret = ec_cdev_ioctl_sc_reg_pdo_entry(master, arg, priv); - break; - case EC_IOCTL_SC_DC: - if (!(filp->f_mode & FMODE_WRITE)) { - ret = -EPERM; - break; - } - ret = ec_cdev_ioctl_sc_dc(master, arg, priv); - break; - case EC_IOCTL_SC_SDO: - if (!(filp->f_mode & FMODE_WRITE)) { - ret = -EPERM; - break; - } - ret = ec_cdev_ioctl_sc_sdo(master, arg, priv); - break; - case EC_IOCTL_SC_SDO_REQUEST: - if (!(filp->f_mode & FMODE_WRITE)) { - ret = -EPERM; - break; - } - ret = ec_cdev_ioctl_sc_create_sdo_request(master, arg, priv); - break; - case EC_IOCTL_SC_VOE: - if (!(filp->f_mode & FMODE_WRITE)) { - ret = -EPERM; - break; - } - ret = ec_cdev_ioctl_sc_create_voe_handler(master, arg, priv); - break; - case EC_IOCTL_SC_STATE: - ret = ec_cdev_ioctl_sc_state(master, arg, priv); - break; - case EC_IOCTL_SC_IDN: - if (!(filp->f_mode & FMODE_WRITE)) { - ret = -EPERM; - break; - } - ret = ec_cdev_ioctl_sc_idn(master, arg, priv); - break; - case EC_IOCTL_DOMAIN_OFFSET: - ret = ec_cdev_ioctl_domain_offset(master, arg, priv); - break; - case EC_IOCTL_DOMAIN_PROCESS: - if (!(filp->f_mode & FMODE_WRITE)) { - ret = -EPERM; - break; - } - ret = ec_cdev_ioctl_domain_process(master, arg, priv); - break; - case EC_IOCTL_DOMAIN_QUEUE: - if (!(filp->f_mode & FMODE_WRITE)) { - ret = -EPERM; - break; - } - ret = ec_cdev_ioctl_domain_queue(master, arg, priv); - break; - case EC_IOCTL_DOMAIN_STATE: - ret = ec_cdev_ioctl_domain_state(master, arg, priv); - break; - case EC_IOCTL_SDO_REQUEST_TIMEOUT: - if (!(filp->f_mode & FMODE_WRITE)) { - ret = -EPERM; - break; - } - ret = ec_cdev_ioctl_sdo_request_timeout(master, arg, priv); - break; - case EC_IOCTL_SDO_REQUEST_STATE: - ret = ec_cdev_ioctl_sdo_request_state(master, arg, priv); - break; - case EC_IOCTL_SDO_REQUEST_READ: - if (!(filp->f_mode & FMODE_WRITE)) { - ret = -EPERM; - break; - } - ret = ec_cdev_ioctl_sdo_request_read(master, arg, priv); - break; - case EC_IOCTL_SDO_REQUEST_WRITE: - if (!(filp->f_mode & FMODE_WRITE)) { - ret = -EPERM; - break; - } - ret = ec_cdev_ioctl_sdo_request_write(master, arg, priv); - break; - case EC_IOCTL_SDO_REQUEST_DATA: - ret = ec_cdev_ioctl_sdo_request_data(master, arg, priv); - break; - case EC_IOCTL_VOE_SEND_HEADER: - if (!(filp->f_mode & FMODE_WRITE)) { - ret = -EPERM; - break; - } - ret = ec_cdev_ioctl_voe_send_header(master, arg, priv); - break; - case EC_IOCTL_VOE_REC_HEADER: - ret = ec_cdev_ioctl_voe_rec_header(master, arg, priv); - break; - case EC_IOCTL_VOE_READ: - if (!(filp->f_mode & FMODE_WRITE)) { - ret = -EPERM; - break; - } - ret = ec_cdev_ioctl_voe_read(master, arg, priv); - break; - case EC_IOCTL_VOE_READ_NOSYNC: - if (!(filp->f_mode & FMODE_WRITE)) { - ret = -EPERM; - break; - } - ret = ec_cdev_ioctl_voe_read_nosync(master, arg, priv); - break; - case EC_IOCTL_VOE_WRITE: - if (!(filp->f_mode & FMODE_WRITE)) { - ret = -EPERM; - break; - } - ret = ec_cdev_ioctl_voe_write(master, arg, priv); - break; - case EC_IOCTL_VOE_EXEC: - if (!(filp->f_mode & FMODE_WRITE)) { - ret = -EPERM; - break; - } - ret = ec_cdev_ioctl_voe_exec(master, arg, priv); - break; - case EC_IOCTL_VOE_DATA: - ret = ec_cdev_ioctl_voe_data(master, arg, priv); - break; - case EC_IOCTL_SET_SEND_INTERVAL: - if (!(filp->f_mode & FMODE_WRITE)) { - ret = -EPERM; - break; - } - ret = ec_cdev_ioctl_set_send_interval(master, arg, priv); - break; - default: - ret = -ENOTTY; - break; - } - -#if DEBUG_LATENCY - b = get_cycles(); - t = (unsigned int) ((b - a) * 1000LL) / cpu_khz; - if (t > 50) { - EC_MASTER_WARN(master, "ioctl(0x%02x) took %u us.\n", - _IOC_NR(cmd), t); - } -#endif - - return ret; + +#if DEBUG + EC_MASTER_DBG(priv->cdev->master, 0, + "ioctl(filp = 0x%p, cmd = 0x%08x (0x%02x), arg = 0x%lx)\n", + filp, cmd, _IOC_NR(cmd), arg); +#endif + + return ec_ioctl(priv->cdev->master, &priv->ctx, cmd, (void __user *) arg); } /*****************************************************************************/ @@ -3948,10 +251,10 @@ ec_cdev_priv_t *priv = (ec_cdev_priv_t *) vma->vm_private_data; struct page *page; - if (offset >= priv->process_data_size) + if (offset >= priv->ctx.process_data_size) return VM_FAULT_SIGBUS; - page = vmalloc_to_page(priv->process_data + offset); + page = vmalloc_to_page(priv->ctx.process_data + offset); if (!page) return VM_FAULT_SIGBUS; @@ -3985,10 +288,10 @@ offset = (address - vma->vm_start) + (vma->vm_pgoff << PAGE_SHIFT); - if (offset >= priv->process_data_size) + if (offset >= priv->ctx.process_data_size) return NOPAGE_SIGBUS; - page = vmalloc_to_page(priv->process_data + offset); + page = vmalloc_to_page(priv->ctx.process_data + offset); EC_MASTER_DBG(master, 1, "Nopage fault vma, address = %#lx," " offset = %#lx, page = %p\n", address, offset, page); diff -r f4313f5aba88 -r 3bdd7a747fae master/ioctl.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/master/ioctl.c Thu Sep 20 15:28:25 2012 +0200 @@ -0,0 +1,3781 @@ +/****************************************************************************** + * + * $Id$ + * + * Copyright (C) 2006-2012 Florian Pose, Ingenieurgemeinschaft IgH + * + * This file is part of the IgH EtherCAT Master. + * + * The IgH EtherCAT Master is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + * The IgH EtherCAT Master 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 General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with the IgH EtherCAT Master; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * --- + * + * 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. + * + *****************************************************************************/ + +/** + \file + EtherCAT master character device. +*/ + +/*****************************************************************************/ + +#include + +#include "master.h" +#include "slave_config.h" +#include "voe_handler.h" +#include "ethernet.h" +#include "ioctl.h" + +/** Set to 1 to enable ioctl() latency tracing. + * + * Requires CPU timestamp counter! + */ +#define DEBUG_LATENCY 0 + +/*****************************************************************************/ + +/** Copies a string to an ioctl structure. + */ +static void ec_ioctl_strcpy( + char *target, /**< Target. */ + const char *source /**< Source. */ + ) +{ + if (source) { + strncpy(target, source, EC_IOCTL_STRING_SIZE); + target[EC_IOCTL_STRING_SIZE - 1] = 0; + } else { + target[0] = 0; + } +} + +/*****************************************************************************/ + +/** Get module information. + */ +static int ec_ioctl_module( + void *arg /**< Userspace address to store the results. */ + ) +{ + ec_ioctl_module_t data; + + data.ioctl_version_magic = EC_IOCTL_VERSION_MAGIC; + data.master_count = ec_master_count(); + + if (copy_to_user((void __user *) arg, &data, sizeof(data))) + return -EFAULT; + + return 0; +} + +/*****************************************************************************/ + +/** Get master information. + */ +static int ec_ioctl_master( + ec_master_t *master, /**< EtherCAT master. */ + void *arg /**< Userspace address to store the results. */ + ) +{ + ec_ioctl_master_t data; + unsigned int i, j; + + if (down_interruptible(&master->master_sem)) + return -EINTR; + + data.slave_count = master->slave_count; + data.config_count = ec_master_config_count(master); + data.domain_count = ec_master_domain_count(master); +#ifdef EC_EOE + data.eoe_handler_count = ec_master_eoe_handler_count(master); +#endif + data.phase = (uint8_t) master->phase; + data.active = (uint8_t) master->active; + data.scan_busy = master->scan_busy; + + up(&master->master_sem); + + if (down_interruptible(&master->device_sem)) + return -EINTR; + + for (i = 0; i < EC_NUM_DEVICES; i++) { + ec_device_t *device = &master->devices[i]; + + if (device->dev) { + memcpy(data.devices[i].address, + device->dev->dev_addr, ETH_ALEN); + } else { + memcpy(data.devices[i].address, master->macs[i], ETH_ALEN); + } + data.devices[i].attached = device->dev ? 1 : 0; + data.devices[i].link_state = device->link_state ? 1 : 0; + data.devices[i].tx_count = device->tx_count; + data.devices[i].rx_count = device->rx_count; + data.devices[i].tx_bytes = device->tx_bytes; + data.devices[i].rx_bytes = device->rx_bytes; + data.devices[i].tx_errors = device->tx_errors; + for (j = 0; j < EC_RATE_COUNT; j++) { + data.devices[i].tx_frame_rates[j] = + device->tx_frame_rates[j]; + data.devices[i].rx_frame_rates[j] = + device->rx_frame_rates[j]; + data.devices[i].tx_byte_rates[j] = + device->tx_byte_rates[j]; + data.devices[i].rx_byte_rates[j] = + device->rx_byte_rates[j]; + } + } + + data.tx_count = master->device_stats.tx_count; + data.rx_count = master->device_stats.rx_count; + data.tx_bytes = master->device_stats.tx_bytes; + data.rx_bytes = master->device_stats.rx_bytes; + for (i = 0; i < EC_RATE_COUNT; i++) { + data.tx_frame_rates[i] = + master->device_stats.tx_frame_rates[i]; + data.rx_frame_rates[i] = + master->device_stats.rx_frame_rates[i]; + data.tx_byte_rates[i] = + master->device_stats.tx_byte_rates[i]; + data.rx_byte_rates[i] = + master->device_stats.rx_byte_rates[i]; + data.loss_rates[i] = + master->device_stats.loss_rates[i]; + } + + up(&master->device_sem); + + data.app_time = master->app_time; + data.ref_clock = + master->dc_ref_clock ? master->dc_ref_clock->ring_position : 0xffff; + + if (copy_to_user((void __user *) arg, &data, sizeof(data))) + return -EFAULT; + + return 0; +} + +/*****************************************************************************/ + +/** Get slave information. + */ +static int ec_ioctl_slave( + ec_master_t *master, /**< EtherCAT master. */ + void *arg /**< Userspace address to store the results. */ + ) +{ + ec_ioctl_slave_t data; + const ec_slave_t *slave; + int i; + + if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { + return -EFAULT; + } + + if (down_interruptible(&master->master_sem)) + return -EINTR; + + if (!(slave = ec_master_find_slave_const( + master, 0, data.position))) { + up(&master->master_sem); + EC_MASTER_ERR(master, "Slave %u does not exist!\n", data.position); + return -EINVAL; + } + + data.device_index = slave->device_index; + data.vendor_id = slave->sii.vendor_id; + data.product_code = slave->sii.product_code; + data.revision_number = slave->sii.revision_number; + data.serial_number = slave->sii.serial_number; + data.alias = slave->effective_alias; + data.boot_rx_mailbox_offset = slave->sii.boot_rx_mailbox_offset; + data.boot_rx_mailbox_size = slave->sii.boot_rx_mailbox_size; + data.boot_tx_mailbox_offset = slave->sii.boot_tx_mailbox_offset; + data.boot_tx_mailbox_size = slave->sii.boot_tx_mailbox_size; + data.std_rx_mailbox_offset = slave->sii.std_rx_mailbox_offset; + data.std_rx_mailbox_size = slave->sii.std_rx_mailbox_size; + data.std_tx_mailbox_offset = slave->sii.std_tx_mailbox_offset; + data.std_tx_mailbox_size = slave->sii.std_tx_mailbox_size; + data.mailbox_protocols = slave->sii.mailbox_protocols; + data.has_general_category = slave->sii.has_general; + data.coe_details = slave->sii.coe_details; + data.general_flags = slave->sii.general_flags; + data.current_on_ebus = slave->sii.current_on_ebus; + for (i = 0; i < EC_MAX_PORTS; i++) { + data.ports[i].desc = slave->ports[i].desc; + data.ports[i].link.link_up = slave->ports[i].link.link_up; + data.ports[i].link.loop_closed = slave->ports[i].link.loop_closed; + data.ports[i].link.signal_detected = + slave->ports[i].link.signal_detected; + data.ports[i].receive_time = slave->ports[i].receive_time; + if (slave->ports[i].next_slave) { + data.ports[i].next_slave = + slave->ports[i].next_slave->ring_position; + } else { + data.ports[i].next_slave = 0xffff; + } + data.ports[i].delay_to_next_dc = slave->ports[i].delay_to_next_dc; + } + data.fmmu_bit = slave->base_fmmu_bit_operation; + data.dc_supported = slave->base_dc_supported; + data.dc_range = slave->base_dc_range; + data.has_dc_system_time = slave->has_dc_system_time; + data.transmission_delay = slave->transmission_delay; + data.al_state = slave->current_state; + data.error_flag = slave->error_flag; + + data.sync_count = slave->sii.sync_count; + data.sdo_count = ec_slave_sdo_count(slave); + data.sii_nwords = slave->sii_nwords; + ec_ioctl_strcpy(data.group, slave->sii.group); + ec_ioctl_strcpy(data.image, slave->sii.image); + ec_ioctl_strcpy(data.order, slave->sii.order); + ec_ioctl_strcpy(data.name, slave->sii.name); + + up(&master->master_sem); + + if (copy_to_user((void __user *) arg, &data, sizeof(data))) + return -EFAULT; + + return 0; +} + +/*****************************************************************************/ + +/** Get slave sync manager information. + */ +static int ec_ioctl_slave_sync( + ec_master_t *master, /**< EtherCAT master. */ + void *arg /**< Userspace address to store the results. */ + ) +{ + ec_ioctl_slave_sync_t data; + const ec_slave_t *slave; + const ec_sync_t *sync; + + if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { + return -EFAULT; + } + + if (down_interruptible(&master->master_sem)) + return -EINTR; + + if (!(slave = ec_master_find_slave_const( + master, 0, data.slave_position))) { + up(&master->master_sem); + EC_MASTER_ERR(master, "Slave %u does not exist!\n", + data.slave_position); + return -EINVAL; + } + + if (data.sync_index >= slave->sii.sync_count) { + up(&master->master_sem); + EC_SLAVE_ERR(slave, "Sync manager %u does not exist!\n", + data.sync_index); + return -EINVAL; + } + + sync = &slave->sii.syncs[data.sync_index]; + + data.physical_start_address = sync->physical_start_address; + data.default_size = sync->default_length; + data.control_register = sync->control_register; + data.enable = sync->enable; + data.pdo_count = ec_pdo_list_count(&sync->pdos); + + up(&master->master_sem); + + if (copy_to_user((void __user *) arg, &data, sizeof(data))) + return -EFAULT; + + return 0; +} + +/*****************************************************************************/ + +/** Get slave sync manager PDO information. + */ +static int ec_ioctl_slave_sync_pdo( + ec_master_t *master, /**< EtherCAT master. */ + void *arg /**< Userspace address to store the results. */ + ) +{ + ec_ioctl_slave_sync_pdo_t data; + const ec_slave_t *slave; + const ec_sync_t *sync; + const ec_pdo_t *pdo; + + if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { + return -EFAULT; + } + + if (down_interruptible(&master->master_sem)) + return -EINTR; + + if (!(slave = ec_master_find_slave_const( + master, 0, data.slave_position))) { + up(&master->master_sem); + EC_MASTER_ERR(master, "Slave %u does not exist!\n", + data.slave_position); + return -EINVAL; + } + + if (data.sync_index >= slave->sii.sync_count) { + up(&master->master_sem); + EC_SLAVE_ERR(slave, "Sync manager %u does not exist!\n", + data.sync_index); + return -EINVAL; + } + + sync = &slave->sii.syncs[data.sync_index]; + if (!(pdo = ec_pdo_list_find_pdo_by_pos_const( + &sync->pdos, data.pdo_pos))) { + up(&master->master_sem); + EC_SLAVE_ERR(slave, "Sync manager %u does not contain a PDO with " + "position %u!\n", data.sync_index, data.pdo_pos); + return -EINVAL; + } + + data.index = pdo->index; + data.entry_count = ec_pdo_entry_count(pdo); + ec_ioctl_strcpy(data.name, pdo->name); + + up(&master->master_sem); + + if (copy_to_user((void __user *) arg, &data, sizeof(data))) + return -EFAULT; + + return 0; +} + +/*****************************************************************************/ + +/** Get slave sync manager PDO entry information. + */ +static int ec_ioctl_slave_sync_pdo_entry( + ec_master_t *master, /**< EtherCAT master. */ + void *arg /**< Userspace address to store the results. */ + ) +{ + ec_ioctl_slave_sync_pdo_entry_t data; + const ec_slave_t *slave; + const ec_sync_t *sync; + const ec_pdo_t *pdo; + const ec_pdo_entry_t *entry; + + if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { + return -EFAULT; + } + + if (down_interruptible(&master->master_sem)) + return -EINTR; + + if (!(slave = ec_master_find_slave_const( + master, 0, data.slave_position))) { + up(&master->master_sem); + EC_MASTER_ERR(master, "Slave %u does not exist!\n", + data.slave_position); + return -EINVAL; + } + + if (data.sync_index >= slave->sii.sync_count) { + up(&master->master_sem); + EC_SLAVE_ERR(slave, "Sync manager %u does not exist!\n", + data.sync_index); + return -EINVAL; + } + + sync = &slave->sii.syncs[data.sync_index]; + if (!(pdo = ec_pdo_list_find_pdo_by_pos_const( + &sync->pdos, data.pdo_pos))) { + up(&master->master_sem); + EC_SLAVE_ERR(slave, "Sync manager %u does not contain a PDO with " + "position %u!\n", data.sync_index, data.pdo_pos); + return -EINVAL; + } + + if (!(entry = ec_pdo_find_entry_by_pos_const( + pdo, data.entry_pos))) { + up(&master->master_sem); + EC_SLAVE_ERR(slave, "PDO 0x%04X does not contain an entry with " + "position %u!\n", data.pdo_pos, data.entry_pos); + return -EINVAL; + } + + data.index = entry->index; + data.subindex = entry->subindex; + data.bit_length = entry->bit_length; + ec_ioctl_strcpy(data.name, entry->name); + + up(&master->master_sem); + + if (copy_to_user((void __user *) arg, &data, sizeof(data))) + return -EFAULT; + + return 0; +} + +/*****************************************************************************/ + +/** Get domain information. + */ +static int ec_ioctl_domain( + ec_master_t *master, /**< EtherCAT master. */ + void *arg /**< Userspace address to store the results. */ + ) +{ + ec_ioctl_domain_t data; + const ec_domain_t *domain; + unsigned int dev_idx; + + if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { + return -EFAULT; + } + + if (down_interruptible(&master->master_sem)) + return -EINTR; + + if (!(domain = ec_master_find_domain_const(master, data.index))) { + up(&master->master_sem); + EC_MASTER_ERR(master, "Domain %u does not exist!\n", data.index); + return -EINVAL; + } + + data.data_size = domain->data_size; + data.logical_base_address = domain->logical_base_address; + for (dev_idx = 0; dev_idx < EC_NUM_DEVICES; dev_idx++) { + data.working_counter[dev_idx] = domain->working_counter[dev_idx]; + } + data.expected_working_counter = domain->expected_working_counter; + data.fmmu_count = ec_domain_fmmu_count(domain); + + up(&master->master_sem); + + if (copy_to_user((void __user *) arg, &data, sizeof(data))) + return -EFAULT; + + return 0; +} + +/*****************************************************************************/ + +/** Get domain FMMU information. + */ +static int ec_ioctl_domain_fmmu( + ec_master_t *master, /**< EtherCAT master. */ + void *arg /**< Userspace address to store the results. */ + ) +{ + ec_ioctl_domain_fmmu_t data; + const ec_domain_t *domain; + const ec_fmmu_config_t *fmmu; + + if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { + return -EFAULT; + } + + if (down_interruptible(&master->master_sem)) + return -EINTR; + + if (!(domain = ec_master_find_domain_const(master, data.domain_index))) { + up(&master->master_sem); + EC_MASTER_ERR(master, "Domain %u does not exist!\n", + data.domain_index); + return -EINVAL; + } + + if (!(fmmu = ec_domain_find_fmmu(domain, data.fmmu_index))) { + up(&master->master_sem); + EC_MASTER_ERR(master, "Domain %u has less than %u" + " fmmu configurations.\n", + data.domain_index, data.fmmu_index + 1); + return -EINVAL; + } + + data.slave_config_alias = fmmu->sc->alias; + data.slave_config_position = fmmu->sc->position; + data.sync_index = fmmu->sync_index; + data.dir = fmmu->dir; + data.logical_address = fmmu->logical_start_address; + data.data_size = fmmu->data_size; + + up(&master->master_sem); + + if (copy_to_user((void __user *) arg, &data, sizeof(data))) + return -EFAULT; + + return 0; +} + +/*****************************************************************************/ + +/** Get domain data. + */ +static int ec_ioctl_domain_data( + ec_master_t *master, /**< EtherCAT master. */ + void *arg /**< Userspace address to store the results. */ + ) +{ + ec_ioctl_domain_data_t data; + const ec_domain_t *domain; + + if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { + return -EFAULT; + } + + if (down_interruptible(&master->master_sem)) + return -EINTR; + + if (!(domain = ec_master_find_domain_const(master, data.domain_index))) { + up(&master->master_sem); + EC_MASTER_ERR(master, "Domain %u does not exist!\n", + data.domain_index); + return -EINVAL; + } + + if (domain->data_size != data.data_size) { + up(&master->master_sem); + EC_MASTER_ERR(master, "Data size mismatch %u/%zu!\n", + data.data_size, domain->data_size); + return -EFAULT; + } + + if (copy_to_user((void __user *) data.target, domain->data, + domain->data_size)) { + up(&master->master_sem); + return -EFAULT; + } + + up(&master->master_sem); + return 0; +} + +/*****************************************************************************/ + +/** Set master debug level. + */ +static int ec_ioctl_master_debug( + ec_master_t *master, /**< EtherCAT master. */ + void *arg /**< ioctl() argument. */ + ) +{ + return ec_master_debug_level(master, (unsigned int) arg); +} + +/*****************************************************************************/ + +/** Issue a bus scan. + */ +static int ec_ioctl_master_rescan( + ec_master_t *master, /**< EtherCAT master. */ + void *arg /**< ioctl() argument. */ + ) +{ + master->fsm.rescan_required = 1; + return 0; +} + +/*****************************************************************************/ + +/** Set slave state. + */ +static int ec_ioctl_slave_state( + ec_master_t *master, /**< EtherCAT master. */ + void *arg /**< ioctl() argument. */ + ) +{ + ec_ioctl_slave_state_t data; + ec_slave_t *slave; + + if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { + return -EFAULT; + } + + if (down_interruptible(&master->master_sem)) + return -EINTR; + + if (!(slave = ec_master_find_slave( + master, 0, data.slave_position))) { + up(&master->master_sem); + EC_MASTER_ERR(master, "Slave %u does not exist!\n", + data.slave_position); + return -EINVAL; + } + + ec_slave_request_state(slave, data.al_state); + + up(&master->master_sem); + return 0; +} + +/*****************************************************************************/ + +/** Get slave SDO information. + */ +static int ec_ioctl_slave_sdo( + ec_master_t *master, /**< EtherCAT master. */ + void *arg /**< ioctl() argument. */ + ) +{ + ec_ioctl_slave_sdo_t data; + const ec_slave_t *slave; + const ec_sdo_t *sdo; + + if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { + return -EFAULT; + } + + if (down_interruptible(&master->master_sem)) + return -EINTR; + + if (!(slave = ec_master_find_slave_const( + master, 0, data.slave_position))) { + up(&master->master_sem); + EC_MASTER_ERR(master, "Slave %u does not exist!\n", + data.slave_position); + return -EINVAL; + } + + if (!(sdo = ec_slave_get_sdo_by_pos_const( + slave, data.sdo_position))) { + up(&master->master_sem); + EC_SLAVE_ERR(slave, "SDO %u does not exist!\n", data.sdo_position); + return -EINVAL; + } + + data.sdo_index = sdo->index; + data.max_subindex = sdo->max_subindex; + ec_ioctl_strcpy(data.name, sdo->name); + + up(&master->master_sem); + + if (copy_to_user((void __user *) arg, &data, sizeof(data))) + return -EFAULT; + + return 0; +} + +/*****************************************************************************/ + +/** Get slave SDO entry information. + */ +static int ec_ioctl_slave_sdo_entry( + ec_master_t *master, /**< EtherCAT master. */ + void *arg /**< ioctl() argument. */ + ) +{ + ec_ioctl_slave_sdo_entry_t data; + const ec_slave_t *slave; + const ec_sdo_t *sdo; + const ec_sdo_entry_t *entry; + + if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { + return -EFAULT; + } + + if (down_interruptible(&master->master_sem)) + return -EINTR; + + if (!(slave = ec_master_find_slave_const( + master, 0, data.slave_position))) { + up(&master->master_sem); + EC_MASTER_ERR(master, "Slave %u does not exist!\n", + data.slave_position); + return -EINVAL; + } + + if (data.sdo_spec <= 0) { + if (!(sdo = ec_slave_get_sdo_by_pos_const( + slave, -data.sdo_spec))) { + up(&master->master_sem); + EC_SLAVE_ERR(slave, "SDO %u does not exist!\n", -data.sdo_spec); + return -EINVAL; + } + } else { + if (!(sdo = ec_slave_get_sdo_const( + slave, data.sdo_spec))) { + up(&master->master_sem); + EC_SLAVE_ERR(slave, "SDO 0x%04X does not exist!\n", + data.sdo_spec); + return -EINVAL; + } + } + + if (!(entry = ec_sdo_get_entry_const( + sdo, data.sdo_entry_subindex))) { + up(&master->master_sem); + EC_SLAVE_ERR(slave, "SDO entry 0x%04X:%02X does not exist!\n", + sdo->index, data.sdo_entry_subindex); + return -EINVAL; + } + + data.data_type = entry->data_type; + data.bit_length = entry->bit_length; + data.read_access[EC_SDO_ENTRY_ACCESS_PREOP] = + entry->read_access[EC_SDO_ENTRY_ACCESS_PREOP]; + data.read_access[EC_SDO_ENTRY_ACCESS_SAFEOP] = + entry->read_access[EC_SDO_ENTRY_ACCESS_SAFEOP]; + data.read_access[EC_SDO_ENTRY_ACCESS_OP] = + entry->read_access[EC_SDO_ENTRY_ACCESS_OP]; + data.write_access[EC_SDO_ENTRY_ACCESS_PREOP] = + entry->write_access[EC_SDO_ENTRY_ACCESS_PREOP]; + data.write_access[EC_SDO_ENTRY_ACCESS_SAFEOP] = + entry->write_access[EC_SDO_ENTRY_ACCESS_SAFEOP]; + data.write_access[EC_SDO_ENTRY_ACCESS_OP] = + entry->write_access[EC_SDO_ENTRY_ACCESS_OP]; + ec_ioctl_strcpy(data.description, entry->description); + + up(&master->master_sem); + + if (copy_to_user((void __user *) arg, &data, sizeof(data))) + return -EFAULT; + + return 0; +} + +/*****************************************************************************/ + +/** Upload SDO. + */ +static int ec_ioctl_slave_sdo_upload( + ec_master_t *master, /**< EtherCAT master. */ + void *arg /**< ioctl() argument. */ + ) +{ + ec_ioctl_slave_sdo_upload_t data; + uint8_t *target; + int ret; + + if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { + return -EFAULT; + } + + if (!(target = kmalloc(data.target_size, GFP_KERNEL))) { + EC_MASTER_ERR(master, "Failed to allocate %u bytes" + " for SDO upload.\n", data.target_size); + return -ENOMEM; + } + + ret = ecrt_master_sdo_upload(master, data.slave_position, + data.sdo_index, data.sdo_entry_subindex, target, + data.target_size, &data.data_size, &data.abort_code); + + if (!ret) { + if (copy_to_user((void __user *) data.target, + target, data.data_size)) { + kfree(target); + return -EFAULT; + } + } + + kfree(target); + + if (__copy_to_user((void __user *) arg, &data, sizeof(data))) { + return -EFAULT; + } + + return 0; +} + +/*****************************************************************************/ + +/** Download SDO. + */ +static int ec_ioctl_slave_sdo_download( + ec_master_t *master, /**< EtherCAT master. */ + void *arg /**< ioctl() argument. */ + ) +{ + ec_ioctl_slave_sdo_download_t data; + uint8_t *sdo_data; + int retval; + + if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { + return -EFAULT; + } + + if (!(sdo_data = kmalloc(data.data_size, GFP_KERNEL))) { + EC_MASTER_ERR(master, "Failed to allocate %u bytes" + " for SDO download.\n", data.data_size); + return -ENOMEM; + } + + if (copy_from_user(sdo_data, (void __user *) data.data, data.data_size)) { + kfree(sdo_data); + return -EFAULT; + } + + if (data.complete_access) { + retval = ecrt_master_sdo_download_complete(master, data.slave_position, + data.sdo_index, sdo_data, data.data_size, &data.abort_code); + } else { + retval = ecrt_master_sdo_download(master, data.slave_position, + data.sdo_index, data.sdo_entry_subindex, sdo_data, + data.data_size, &data.abort_code); + } + + kfree(sdo_data); + + if (__copy_to_user((void __user *) arg, &data, sizeof(data))) { + retval = -EFAULT; + } + + return retval; +} + +/*****************************************************************************/ + +/** Read a slave's SII. + */ +static int ec_ioctl_slave_sii_read( + ec_master_t *master, /**< EtherCAT master. */ + void *arg /**< ioctl() argument. */ + ) +{ + ec_ioctl_slave_sii_t data; + const ec_slave_t *slave; + int retval; + + if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { + return -EFAULT; + } + + if (down_interruptible(&master->master_sem)) + return -EINTR; + + if (!(slave = ec_master_find_slave_const( + master, 0, data.slave_position))) { + up(&master->master_sem); + EC_MASTER_ERR(master, "Slave %u does not exist!\n", + data.slave_position); + return -EINVAL; + } + + if (!data.nwords + || data.offset + data.nwords > slave->sii_nwords) { + up(&master->master_sem); + EC_SLAVE_ERR(slave, "Invalid SII read offset/size %u/%u for slave SII" + " size %zu!\n", data.offset, data.nwords, slave->sii_nwords); + return -EINVAL; + } + + if (copy_to_user((void __user *) data.words, + slave->sii_words + data.offset, data.nwords * 2)) + retval = -EFAULT; + else + retval = 0; + + up(&master->master_sem); + return retval; +} + +/*****************************************************************************/ + +/** Write a slave's SII. + */ +static int ec_ioctl_slave_sii_write( + ec_master_t *master, /**< EtherCAT master. */ + void *arg /**< ioctl() argument. */ + ) +{ + ec_ioctl_slave_sii_t data; + ec_slave_t *slave; + unsigned int byte_size; + uint16_t *words; + ec_sii_write_request_t request; + + if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { + return -EFAULT; + } + + if (!data.nwords) + return 0; + + byte_size = sizeof(uint16_t) * data.nwords; + if (!(words = kmalloc(byte_size, GFP_KERNEL))) { + EC_MASTER_ERR(master, "Failed to allocate %u bytes" + " for SII contents.\n", byte_size); + return -ENOMEM; + } + + if (copy_from_user(words, + (void __user *) data.words, byte_size)) { + kfree(words); + return -EFAULT; + } + + if (down_interruptible(&master->master_sem)) + return -EINTR; + + if (!(slave = ec_master_find_slave( + master, 0, data.slave_position))) { + up(&master->master_sem); + EC_MASTER_ERR(master, "Slave %u does not exist!\n", + data.slave_position); + kfree(words); + return -EINVAL; + } + + // init SII write request + INIT_LIST_HEAD(&request.list); + request.slave = slave; + request.words = words; + request.offset = data.offset; + request.nwords = data.nwords; + request.state = EC_INT_REQUEST_QUEUED; + + // schedule SII write request. + list_add_tail(&request.list, &master->sii_requests); + + up(&master->master_sem); + + // wait for processing through FSM + if (wait_event_interruptible(master->sii_queue, + request.state != EC_INT_REQUEST_QUEUED)) { + // interrupted by signal + down(&master->master_sem); + if (request.state == EC_INT_REQUEST_QUEUED) { + // abort request + list_del(&request.list); + up(&master->master_sem); + kfree(words); + return -EINTR; + } + up(&master->master_sem); + } + + // wait until master FSM has finished processing + wait_event(master->sii_queue, request.state != EC_INT_REQUEST_BUSY); + + kfree(words); + + return request.state == EC_INT_REQUEST_SUCCESS ? 0 : -EIO; +} + +/*****************************************************************************/ + +/** Read a slave's registers. + */ +static int ec_ioctl_slave_reg_read( + ec_master_t *master, /**< EtherCAT master. */ + void *arg /**< ioctl() argument. */ + ) +{ + ec_ioctl_slave_reg_t data; + ec_slave_t *slave; + uint8_t *contents; + ec_reg_request_t request; + + if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { + return -EFAULT; + } + + if (!data.length) + return 0; + + if (!(contents = kmalloc(data.length, GFP_KERNEL))) { + EC_MASTER_ERR(master, "Failed to allocate %u bytes" + " for register data.\n", data.length); + return -ENOMEM; + } + + if (down_interruptible(&master->master_sem)) + return -EINTR; + + if (!(slave = ec_master_find_slave( + master, 0, data.slave_position))) { + up(&master->master_sem); + EC_MASTER_ERR(master, "Slave %u does not exist!\n", + data.slave_position); + return -EINVAL; + } + + // init register request + INIT_LIST_HEAD(&request.list); + request.slave = slave; + request.dir = EC_DIR_INPUT; + request.data = contents; + request.offset = data.offset; + request.length = data.length; + request.state = EC_INT_REQUEST_QUEUED; + + // schedule request. + list_add_tail(&request.list, &master->reg_requests); + + up(&master->master_sem); + + // wait for processing through FSM + if (wait_event_interruptible(master->reg_queue, + request.state != EC_INT_REQUEST_QUEUED)) { + // interrupted by signal + down(&master->master_sem); + if (request.state == EC_INT_REQUEST_QUEUED) { + // abort request + list_del(&request.list); + up(&master->master_sem); + kfree(contents); + return -EINTR; + } + up(&master->master_sem); + } + + // wait until master FSM has finished processing + wait_event(master->reg_queue, request.state != EC_INT_REQUEST_BUSY); + + if (request.state == EC_INT_REQUEST_SUCCESS) { + if (copy_to_user((void __user *) data.data, contents, data.length)) + return -EFAULT; + } + kfree(contents); + + return request.state == EC_INT_REQUEST_SUCCESS ? 0 : -EIO; +} + +/*****************************************************************************/ + +/** Write a slave's registers. + */ +static int ec_ioctl_slave_reg_write( + ec_master_t *master, /**< EtherCAT master. */ + void *arg /**< ioctl() argument. */ + ) +{ + ec_ioctl_slave_reg_t data; + ec_slave_t *slave; + uint8_t *contents; + ec_reg_request_t request; + + if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { + return -EFAULT; + } + + if (!data.length) + return 0; + + if (!(contents = kmalloc(data.length, GFP_KERNEL))) { + EC_MASTER_ERR(master, "Failed to allocate %u bytes" + " for register data.\n", data.length); + return -ENOMEM; + } + + if (copy_from_user(contents, (void __user *) data.data, data.length)) { + kfree(contents); + return -EFAULT; + } + + if (down_interruptible(&master->master_sem)) + return -EINTR; + + if (!(slave = ec_master_find_slave( + master, 0, data.slave_position))) { + up(&master->master_sem); + EC_MASTER_ERR(master, "Slave %u does not exist!\n", + data.slave_position); + kfree(contents); + return -EINVAL; + } + + // init register request + INIT_LIST_HEAD(&request.list); + request.slave = slave; + request.dir = EC_DIR_OUTPUT; + request.data = contents; + request.offset = data.offset; + request.length = data.length; + request.state = EC_INT_REQUEST_QUEUED; + + // schedule request. + list_add_tail(&request.list, &master->reg_requests); + + up(&master->master_sem); + + // wait for processing through FSM + if (wait_event_interruptible(master->reg_queue, + request.state != EC_INT_REQUEST_QUEUED)) { + // interrupted by signal + down(&master->master_sem); + if (request.state == EC_INT_REQUEST_QUEUED) { + // abort request + list_del(&request.list); + up(&master->master_sem); + kfree(contents); + return -EINTR; + } + up(&master->master_sem); + } + + // wait until master FSM has finished processing + wait_event(master->reg_queue, request.state != EC_INT_REQUEST_BUSY); + + kfree(contents); + + return request.state == EC_INT_REQUEST_SUCCESS ? 0 : -EIO; +} + +/*****************************************************************************/ + +/** Get slave configuration information. + */ +static int ec_ioctl_config( + ec_master_t *master, /**< EtherCAT master. */ + void *arg /**< ioctl() argument. */ + ) +{ + ec_ioctl_config_t data; + const ec_slave_config_t *sc; + uint8_t i; + + if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { + return -EFAULT; + } + + if (down_interruptible(&master->master_sem)) + return -EINTR; + + if (!(sc = ec_master_get_config_const( + master, data.config_index))) { + up(&master->master_sem); + EC_MASTER_ERR(master, "Slave config %u does not exist!\n", + data.config_index); + return -EINVAL; + } + + data.alias = sc->alias; + data.position = sc->position; + data.vendor_id = sc->vendor_id; + data.product_code = sc->product_code; + for (i = 0; i < EC_MAX_SYNC_MANAGERS; i++) { + data.syncs[i].dir = sc->sync_configs[i].dir; + data.syncs[i].watchdog_mode = sc->sync_configs[i].watchdog_mode; + data.syncs[i].pdo_count = + ec_pdo_list_count(&sc->sync_configs[i].pdos); + } + data.watchdog_divider = sc->watchdog_divider; + data.watchdog_intervals = sc->watchdog_intervals; + data.sdo_count = ec_slave_config_sdo_count(sc); + data.idn_count = ec_slave_config_idn_count(sc); + data.slave_position = sc->slave ? sc->slave->ring_position : -1; + data.dc_assign_activate = sc->dc_assign_activate; + for (i = 0; i < EC_SYNC_SIGNAL_COUNT; i++) { + data.dc_sync[i] = sc->dc_sync[i]; + } + + up(&master->master_sem); + + if (copy_to_user((void __user *) arg, &data, sizeof(data))) + return -EFAULT; + + return 0; +} + +/*****************************************************************************/ + +/** Get slave configuration PDO information. + */ +static int ec_ioctl_config_pdo( + ec_master_t *master, /**< EtherCAT master. */ + void *arg /**< ioctl() argument. */ + ) +{ + ec_ioctl_config_pdo_t data; + const ec_slave_config_t *sc; + const ec_pdo_t *pdo; + + if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { + return -EFAULT; + } + + if (data.sync_index >= EC_MAX_SYNC_MANAGERS) { + EC_MASTER_ERR(master, "Invalid sync manager index %u!\n", + data.sync_index); + return -EINVAL; + } + + if (down_interruptible(&master->master_sem)) + return -EINTR; + + if (!(sc = ec_master_get_config_const( + master, data.config_index))) { + up(&master->master_sem); + EC_MASTER_ERR(master, "Slave config %u does not exist!\n", + data.config_index); + return -EINVAL; + } + + if (!(pdo = ec_pdo_list_find_pdo_by_pos_const( + &sc->sync_configs[data.sync_index].pdos, + data.pdo_pos))) { + up(&master->master_sem); + EC_MASTER_ERR(master, "Invalid PDO position!\n"); + return -EINVAL; + } + + data.index = pdo->index; + data.entry_count = ec_pdo_entry_count(pdo); + ec_ioctl_strcpy(data.name, pdo->name); + + up(&master->master_sem); + + if (copy_to_user((void __user *) arg, &data, sizeof(data))) + return -EFAULT; + + return 0; +} + +/*****************************************************************************/ + +/** Get slave configuration PDO entry information. + */ +static int ec_ioctl_config_pdo_entry( + ec_master_t *master, /**< EtherCAT master. */ + void *arg /**< ioctl() argument. */ + ) +{ + ec_ioctl_config_pdo_entry_t data; + const ec_slave_config_t *sc; + const ec_pdo_t *pdo; + const ec_pdo_entry_t *entry; + + if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { + return -EFAULT; + } + + if (data.sync_index >= EC_MAX_SYNC_MANAGERS) { + EC_MASTER_ERR(master, "Invalid sync manager index %u!\n", + data.sync_index); + return -EINVAL; + } + + if (down_interruptible(&master->master_sem)) + return -EINTR; + + if (!(sc = ec_master_get_config_const( + master, data.config_index))) { + up(&master->master_sem); + EC_MASTER_ERR(master, "Slave config %u does not exist!\n", + data.config_index); + return -EINVAL; + } + + if (!(pdo = ec_pdo_list_find_pdo_by_pos_const( + &sc->sync_configs[data.sync_index].pdos, + data.pdo_pos))) { + up(&master->master_sem); + EC_MASTER_ERR(master, "Invalid PDO position!\n"); + return -EINVAL; + } + + if (!(entry = ec_pdo_find_entry_by_pos_const( + pdo, data.entry_pos))) { + up(&master->master_sem); + EC_MASTER_ERR(master, "Entry not found!\n"); + return -EINVAL; + } + + data.index = entry->index; + data.subindex = entry->subindex; + data.bit_length = entry->bit_length; + ec_ioctl_strcpy(data.name, entry->name); + + up(&master->master_sem); + + if (copy_to_user((void __user *) arg, &data, sizeof(data))) + return -EFAULT; + + return 0; +} + +/*****************************************************************************/ + +/** Get slave configuration SDO information. + */ +static int ec_ioctl_config_sdo( + ec_master_t *master, /**< EtherCAT master. */ + void *arg /**< ioctl() argument. */ + ) +{ + ec_ioctl_config_sdo_t *ioctl; + const ec_slave_config_t *sc; + const ec_sdo_request_t *req; + + if (!(ioctl = kmalloc(sizeof(*ioctl), GFP_KERNEL))) { + return -ENOMEM; + } + + if (copy_from_user(ioctl, (void __user *) arg, sizeof(*ioctl))) { + kfree(ioctl); + return -EFAULT; + } + + if (down_interruptible(&master->master_sem)) { + kfree(ioctl); + return -EINTR; + } + + if (!(sc = ec_master_get_config_const( + master, ioctl->config_index))) { + up(&master->master_sem); + EC_MASTER_ERR(master, "Slave config %u does not exist!\n", + ioctl->config_index); + kfree(ioctl); + return -EINVAL; + } + + if (!(req = ec_slave_config_get_sdo_by_pos_const( + sc, ioctl->sdo_pos))) { + up(&master->master_sem); + EC_MASTER_ERR(master, "Invalid SDO position!\n"); + kfree(ioctl); + return -EINVAL; + } + + ioctl->index = req->index; + ioctl->subindex = req->subindex; + ioctl->size = req->data_size; + memcpy(ioctl->data, req->data, + min((u32) ioctl->size, (u32) EC_MAX_SDO_DATA_SIZE)); + + up(&master->master_sem); + + if (copy_to_user((void __user *) arg, ioctl, sizeof(*ioctl))) { + kfree(ioctl); + return -EFAULT; + } + + kfree(ioctl); + return 0; +} + +/*****************************************************************************/ + +/** Get slave configuration IDN information. + */ +static int ec_ioctl_config_idn( + ec_master_t *master, /**< EtherCAT master. */ + void *arg /**< ioctl() argument. */ + ) +{ + ec_ioctl_config_idn_t *ioctl; + const ec_slave_config_t *sc; + const ec_soe_request_t *req; + + if (!(ioctl = kmalloc(sizeof(*ioctl), GFP_KERNEL))) { + return -ENOMEM; + } + + if (copy_from_user(ioctl, (void __user *) arg, sizeof(*ioctl))) { + kfree(ioctl); + return -EFAULT; + } + + if (down_interruptible(&master->master_sem)) { + kfree(ioctl); + return -EINTR; + } + + if (!(sc = ec_master_get_config_const( + master, ioctl->config_index))) { + up(&master->master_sem); + EC_MASTER_ERR(master, "Slave config %u does not exist!\n", + ioctl->config_index); + kfree(ioctl); + return -EINVAL; + } + + if (!(req = ec_slave_config_get_idn_by_pos_const( + sc, ioctl->idn_pos))) { + up(&master->master_sem); + EC_MASTER_ERR(master, "Invalid IDN position!\n"); + kfree(ioctl); + return -EINVAL; + } + + ioctl->drive_no = req->drive_no; + ioctl->idn = req->idn; + ioctl->state = req->state; + ioctl->size = req->data_size; + memcpy(ioctl->data, req->data, + min((u32) ioctl->size, (u32) EC_MAX_IDN_DATA_SIZE)); + + up(&master->master_sem); + + if (copy_to_user((void __user *) arg, ioctl, sizeof(*ioctl))) { + kfree(ioctl); + return -EFAULT; + } + + kfree(ioctl); + return 0; +} + +/*****************************************************************************/ + +#ifdef EC_EOE + +/** Get EoE handler information. + */ +static int ec_ioctl_eoe_handler( + ec_master_t *master, /**< EtherCAT master. */ + void *arg /**< ioctl() argument. */ + ) +{ + ec_ioctl_eoe_handler_t data; + const ec_eoe_t *eoe; + + if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { + return -EFAULT; + } + + if (down_interruptible(&master->master_sem)) + return -EINTR; + + if (!(eoe = ec_master_get_eoe_handler_const(master, data.eoe_index))) { + up(&master->master_sem); + EC_MASTER_ERR(master, "EoE handler %u does not exist!\n", + data.eoe_index); + return -EINVAL; + } + + if (eoe->slave) { + data.slave_position = eoe->slave->ring_position; + } else { + data.slave_position = 0xffff; + } + snprintf(data.name, EC_DATAGRAM_NAME_SIZE, eoe->dev->name); + data.open = eoe->opened; + data.rx_bytes = eoe->stats.tx_bytes; + data.rx_rate = eoe->tx_rate; + data.tx_bytes = eoe->stats.rx_bytes; + data.tx_rate = eoe->tx_rate; + data.tx_queued_frames = eoe->tx_queued_frames; + data.tx_queue_size = eoe->tx_queue_size; + + up(&master->master_sem); + + if (copy_to_user((void __user *) arg, &data, sizeof(data))) + return -EFAULT; + + return 0; +} + +#endif + +/*****************************************************************************/ + +/** Request the master from userspace. + */ +static int ec_ioctl_request( + ec_master_t *master, /**< EtherCAT master. */ + void *arg, /**< ioctl() argument. */ + ec_ioctl_context_t *ctx /**< Private data structure of file handle. */ + ) +{ + ec_master_t *m; + int ret = 0; + + m = ecrt_request_master_err(master->index); + if (IS_ERR(m)) { + ret = PTR_ERR(m); + } else { + ctx->requested = 1; + } + + return ret; +} + +/*****************************************************************************/ + +/** Create a domain. + */ +static int ec_ioctl_create_domain( + ec_master_t *master, /**< EtherCAT master. */ + void *arg, /**< ioctl() argument. */ + ec_ioctl_context_t *ctx /**< Private data structure of file handle. */ + ) +{ + ec_domain_t *domain; + + if (unlikely(!ctx->requested)) + return -EPERM; + + domain = ecrt_master_create_domain_err(master); + if (IS_ERR(domain)) + return PTR_ERR(domain); + + return domain->index; +} + +/*****************************************************************************/ + +/** Create a slave configuration. + */ +static int ec_ioctl_create_slave_config( + ec_master_t *master, /**< EtherCAT master. */ + void *arg, /**< ioctl() argument. */ + ec_ioctl_context_t *ctx /**< Private data structure of file handle. */ + ) +{ + ec_ioctl_config_t data; + ec_slave_config_t *sc, *entry; + + if (unlikely(!ctx->requested)) + return -EPERM; + + if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { + return -EFAULT; + } + + sc = ecrt_master_slave_config_err(master, data.alias, data.position, + data.vendor_id, data.product_code); + if (IS_ERR(sc)) + return PTR_ERR(sc); + + data.config_index = 0; + + if (down_interruptible(&master->master_sem)) + return -EINTR; + + list_for_each_entry(entry, &master->configs, list) { + if (entry == sc) + break; + data.config_index++; + } + + up(&master->master_sem); + + if (copy_to_user((void __user *) arg, &data, sizeof(data))) + return -EFAULT; + + return 0; +} + +/*****************************************************************************/ + +/** Activates the master. + */ +static int ec_ioctl_activate( + ec_master_t *master, /**< EtherCAT master. */ + void *arg, /**< ioctl() argument. */ + ec_ioctl_context_t *ctx /**< Private data structure of file handle. */ + ) +{ + ec_ioctl_master_activate_t io; + ec_domain_t *domain; + off_t offset; + int ret; + + if (unlikely(!ctx->requested)) + return -EPERM; + + io.process_data = NULL; + + /* Get the sum of the domains' process data sizes. */ + + ctx->process_data_size = 0; + + if (down_interruptible(&master->master_sem)) + return -EINTR; + + list_for_each_entry(domain, &master->domains, list) { + ctx->process_data_size += ecrt_domain_size(domain); + } + + up(&master->master_sem); + + if (ctx->process_data_size) { + ctx->process_data = vmalloc(ctx->process_data_size); + if (!ctx->process_data) { + ctx->process_data_size = 0; + return -ENOMEM; + } + + /* Set the memory as external process data memory for the + * domains. + */ + offset = 0; + list_for_each_entry(domain, &master->domains, list) { + ecrt_domain_external_memory(domain, + ctx->process_data + offset); + offset += ecrt_domain_size(domain); + } + +#ifdef EC_IOCTL_RTDM + /* RTDM uses a different approach for memory-mapping, which has to be + * initiated by the kernel. + */ + ret = ec_rtdm_mmap(ctx, &io.process_data); + if (ret < 0) { + EC_MASTER_ERR(master, "Failed to map process data" + " memory to user space (code %i).\n", ret); + return ret; + } +#endif + } + + io.process_data_size = ctx->process_data_size; + +#ifndef EC_IOCTL_RTDM + ecrt_master_callbacks(master, ec_master_internal_send_cb, + ec_master_internal_receive_cb, master); +#endif + + ret = ecrt_master_activate(master); + if (ret < 0) + return ret; + + if (copy_to_user((void __user *) arg, &io, + sizeof(ec_ioctl_master_activate_t))) + return -EFAULT; + + return 0; +} + +/*****************************************************************************/ + +/** Deactivates the master. + */ +static int ec_ioctl_deactivate( + ec_master_t *master, /**< EtherCAT master. */ + void *arg, /**< ioctl() argument. */ + ec_ioctl_context_t *ctx /**< Private data structure of file handle. */ + ) +{ + if (unlikely(!ctx->requested)) + return -EPERM; + + ecrt_master_deactivate(master); + return 0; +} + +/*****************************************************************************/ + +/** Set max. number of databytes in a cycle + */ +static int ec_ioctl_set_send_interval( + ec_master_t *master, /**< EtherCAT master. */ + void *arg, /**< ioctl() argument. */ + ec_ioctl_context_t *ctx /**< Private data structure of file handle. */ + ) +{ + size_t send_interval; + + if (copy_from_user(&send_interval, (void __user *) arg, + sizeof(send_interval))) { + return -EFAULT; + } + + if (down_interruptible(&master->master_sem)) + return -EINTR; + + ec_master_set_send_interval(master, send_interval); + + up(&master->master_sem); + return 0; +} + +/*****************************************************************************/ + +/** Send frames. + */ +static int ec_ioctl_send( + ec_master_t *master, /**< EtherCAT master. */ + void *arg, /**< ioctl() argument. */ + ec_ioctl_context_t *ctx /**< Private data structure of file handle. */ + ) +{ + if (unlikely(!ctx->requested)) + return -EPERM; + + down(&master->io_sem); + ecrt_master_send(master); + up(&master->io_sem); + return 0; +} + +/*****************************************************************************/ + +/** Receive frames. + */ +static int ec_ioctl_receive( + ec_master_t *master, /**< EtherCAT master. */ + void *arg, /**< ioctl() argument. */ + ec_ioctl_context_t *ctx /**< Private data structure of file handle. */ + ) +{ + if (unlikely(!ctx->requested)) + return -EPERM; + + down(&master->io_sem); + ecrt_master_receive(master); + up(&master->io_sem); + return 0; +} + +/*****************************************************************************/ + +/** Get the master state. + */ +static int ec_ioctl_master_state( + ec_master_t *master, /**< EtherCAT master. */ + void *arg, /**< ioctl() argument. */ + ec_ioctl_context_t *ctx /**< Private data structure of file handle. */ + ) +{ + ec_master_state_t data; + + if (unlikely(!ctx->requested)) + return -EPERM; + + ecrt_master_state(master, &data); + + if (copy_to_user((void __user *) arg, &data, sizeof(data))) + return -EFAULT; + + return 0; +} + +/*****************************************************************************/ + +/** Get the master state. + */ +static int ec_ioctl_master_link_state( + ec_master_t *master, /**< EtherCAT master. */ + void *arg, /**< ioctl() argument. */ + ec_ioctl_context_t *ctx /**< Private data structure of file handle. */ + ) +{ + ec_ioctl_link_state_t ioctl; + ec_master_link_state_t state; + int ret; + + if (unlikely(!ctx->requested)) { + return -EPERM; + } + + if (copy_from_user(&ioctl, (void __user *) arg, sizeof(ioctl))) { + return -EFAULT; + } + + ret = ecrt_master_link_state(master, ioctl.dev_idx, &state); + if (ret < 0) { + return ret; + } + + if (copy_to_user((void __user *) ioctl.state, &state, sizeof(state))) { + return -EFAULT; + } + + return 0; +} + +/*****************************************************************************/ + +/** Set the master dc app time. + */ +static int ec_ioctl_app_time( + ec_master_t *master, /**< EtherCAT master. */ + void *arg, /**< ioctl() argument. */ + ec_ioctl_context_t *ctx /**< Private data structure of file handle. */ + ) +{ + ec_ioctl_app_time_t data; + + if (unlikely(!ctx->requested)) + return -EPERM; + + if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { + return -EFAULT; + } + + ecrt_master_application_time(master, data.app_time); + return 0; +} + +/*****************************************************************************/ + +/** Sync the reference clock. + */ +static int ec_ioctl_sync_ref( + ec_master_t *master, /**< EtherCAT master. */ + void *arg, /**< ioctl() argument. */ + ec_ioctl_context_t *ctx /**< Private data structure of file handle. */ + ) +{ + if (unlikely(!ctx->requested)) + return -EPERM; + + down(&master->io_sem); + ecrt_master_sync_reference_clock(master); + up(&master->io_sem); + return 0; +} + +/*****************************************************************************/ + +/** Sync the slave clocks. + */ +static int ec_ioctl_sync_slaves( + ec_master_t *master, /**< EtherCAT master. */ + void *arg, /**< ioctl() argument. */ + ec_ioctl_context_t *ctx /**< Private data structure of file handle. */ + ) +{ + if (unlikely(!ctx->requested)) + return -EPERM; + + down(&master->io_sem); + ecrt_master_sync_slave_clocks(master); + up(&master->io_sem); + return 0; +} + +/*****************************************************************************/ + +/** Queue the sync monitoring datagram. + */ +static int ec_ioctl_sync_mon_queue( + ec_master_t *master, /**< EtherCAT master. */ + void *arg, /**< ioctl() argument. */ + ec_ioctl_context_t *ctx /**< Private data structure of file handle. */ + ) +{ + if (unlikely(!ctx->requested)) + return -EPERM; + + down(&master->io_sem); + ecrt_master_sync_monitor_queue(master); + up(&master->io_sem); + return 0; +} + +/*****************************************************************************/ + +/** Processes the sync monitoring datagram. + */ +static int ec_ioctl_sync_mon_process( + ec_master_t *master, /**< EtherCAT master. */ + void *arg, /**< ioctl() argument. */ + ec_ioctl_context_t *ctx /**< Private data structure of file handle. */ + ) +{ + uint32_t time_diff; + + if (unlikely(!ctx->requested)) + return -EPERM; + + down(&master->io_sem); + time_diff = ecrt_master_sync_monitor_process(master); + up(&master->io_sem); + + if (copy_to_user((void __user *) arg, &time_diff, sizeof(time_diff))) + return -EFAULT; + + return 0; +} + +/*****************************************************************************/ + +/** Reset configuration. + */ +static int ec_ioctl_reset( + ec_master_t *master, /**< EtherCAT master. */ + void *arg, /**< ioctl() argument. */ + ec_ioctl_context_t *ctx /**< Private data structure of file handle. */ + ) +{ + if (unlikely(!ctx->requested)) + return -EPERM; + + down(&master->master_sem); + ecrt_master_reset(master); + up(&master->master_sem); + return 0; +} + +/*****************************************************************************/ + +/** Configure a sync manager. + */ +static int ec_ioctl_sc_sync( + ec_master_t *master, /**< EtherCAT master. */ + void *arg, /**< ioctl() argument. */ + ec_ioctl_context_t *ctx /**< Private data structure of file handle. */ + ) +{ + ec_ioctl_config_t data; + ec_slave_config_t *sc; + unsigned int i; + int ret = 0; + + if (unlikely(!ctx->requested)) { + ret = -EPERM; + goto out_return; + } + + if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { + ret = -EFAULT; + goto out_return; + } + + if (down_interruptible(&master->master_sem)) { + ret = -EINTR; + goto out_return; + } + + if (!(sc = ec_master_get_config(master, data.config_index))) { + ret = -ENOENT; + goto out_up; + } + + for (i = 0; i < EC_MAX_SYNC_MANAGERS; i++) { + if (data.syncs[i].config_this) { + if (ecrt_slave_config_sync_manager(sc, i, data.syncs[i].dir, + data.syncs[i].watchdog_mode)) { + ret = -EINVAL; + goto out_up; + } + } + } + +out_up: + up(&master->master_sem); +out_return: + return ret; +} + +/*****************************************************************************/ + +/** Configure a slave's watchdogs. + */ +static int ec_ioctl_sc_watchdog( + ec_master_t *master, /**< EtherCAT master. */ + void *arg, /**< ioctl() argument. */ + ec_ioctl_context_t *ctx /**< Private data structure of file handle. */ + ) +{ + ec_ioctl_config_t data; + ec_slave_config_t *sc; + int ret = 0; + + if (unlikely(!ctx->requested)) { + ret = -EPERM; + goto out_return; + } + + if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { + ret = -EFAULT; + goto out_return; + } + + if (down_interruptible(&master->master_sem)) { + ret = -EINTR; + goto out_return; + } + + if (!(sc = ec_master_get_config(master, data.config_index))) { + ret = -ENOENT; + goto out_up; + } + + ecrt_slave_config_watchdog(sc, + data.watchdog_divider, data.watchdog_intervals); + +out_up: + up(&master->master_sem); +out_return: + return ret; +} + +/*****************************************************************************/ + +/** Add a PDO to the assignment. + */ +static int ec_ioctl_sc_add_pdo( + ec_master_t *master, /**< EtherCAT master. */ + void *arg, /**< ioctl() argument. */ + ec_ioctl_context_t *ctx /**< Private data structure of file handle. */ + ) +{ + ec_ioctl_config_pdo_t data; + ec_slave_config_t *sc; + + if (unlikely(!ctx->requested)) + return -EPERM; + + if (copy_from_user(&data, (void __user *) arg, sizeof(data))) + return -EFAULT; + + if (down_interruptible(&master->master_sem)) + return -EINTR; + + if (!(sc = ec_master_get_config(master, data.config_index))) { + up(&master->master_sem); + return -ENOENT; + } + + up(&master->master_sem); /** \fixme sc could be invalidated */ + + return ecrt_slave_config_pdo_assign_add(sc, data.sync_index, data.index); +} + +/*****************************************************************************/ + +/** Clears the PDO assignment. + */ +static int ec_ioctl_sc_clear_pdos( + ec_master_t *master, /**< EtherCAT master. */ + void *arg, /**< ioctl() argument. */ + ec_ioctl_context_t *ctx /**< Private data structure of file handle. */ + ) +{ + ec_ioctl_config_pdo_t data; + ec_slave_config_t *sc; + + if (unlikely(!ctx->requested)) + return -EPERM; + + if (copy_from_user(&data, (void __user *) arg, sizeof(data))) + return -EFAULT; + + if (down_interruptible(&master->master_sem)) + return -EINTR; + + if (!(sc = ec_master_get_config(master, data.config_index))) { + up(&master->master_sem); + return -ENOENT; + } + + up(&master->master_sem); /** \fixme sc could be invalidated */ + + ecrt_slave_config_pdo_assign_clear(sc, data.sync_index); + return 0; +} + +/*****************************************************************************/ + +/** Add an entry to a PDO's mapping. + */ +static int ec_ioctl_sc_add_entry( + ec_master_t *master, /**< EtherCAT master. */ + void *arg, /**< ioctl() argument. */ + ec_ioctl_context_t *ctx /**< Private data structure of file handle. */ + ) +{ + ec_ioctl_add_pdo_entry_t data; + ec_slave_config_t *sc; + + if (unlikely(!ctx->requested)) + return -EPERM; + + if (copy_from_user(&data, (void __user *) arg, sizeof(data))) + return -EFAULT; + + if (down_interruptible(&master->master_sem)) + return -EINTR; + + if (!(sc = ec_master_get_config(master, data.config_index))) { + up(&master->master_sem); + return -ENOENT; + } + + up(&master->master_sem); /** \fixme sc could be invalidated */ + + return ecrt_slave_config_pdo_mapping_add(sc, data.pdo_index, + data.entry_index, data.entry_subindex, data.entry_bit_length); +} + +/*****************************************************************************/ + +/** Clears the mapping of a PDO. + */ +static int ec_ioctl_sc_clear_entries( + ec_master_t *master, /**< EtherCAT master. */ + void *arg, /**< ioctl() argument. */ + ec_ioctl_context_t *ctx /**< Private data structure of file handle. */ + ) +{ + ec_ioctl_config_pdo_t data; + ec_slave_config_t *sc; + + if (unlikely(!ctx->requested)) + return -EPERM; + + if (copy_from_user(&data, (void __user *) arg, sizeof(data))) + return -EFAULT; + + if (down_interruptible(&master->master_sem)) + return -EINTR; + + if (!(sc = ec_master_get_config(master, data.config_index))) { + up(&master->master_sem); + return -ENOENT; + } + + up(&master->master_sem); /** \fixme sc could be invalidated */ + + ecrt_slave_config_pdo_mapping_clear(sc, data.index); + return 0; +} + +/*****************************************************************************/ + +/** Registers a PDO entry. + */ +static int ec_ioctl_sc_reg_pdo_entry( + ec_master_t *master, /**< EtherCAT master. */ + void *arg, /**< ioctl() argument. */ + ec_ioctl_context_t *ctx /**< Private data structure of file handle. */ + ) +{ + ec_ioctl_reg_pdo_entry_t data; + ec_slave_config_t *sc; + ec_domain_t *domain; + int ret; + + if (unlikely(!ctx->requested)) + return -EPERM; + + if (copy_from_user(&data, (void __user *) arg, sizeof(data))) + return -EFAULT; + + if (down_interruptible(&master->master_sem)) + return -EINTR; + + if (!(sc = ec_master_get_config(master, data.config_index))) { + up(&master->master_sem); + return -ENOENT; + } + + if (!(domain = ec_master_find_domain(master, data.domain_index))) { + up(&master->master_sem); + return -ENOENT; + } + + up(&master->master_sem); /** \fixme sc or domain could be invalidated */ + + ret = ecrt_slave_config_reg_pdo_entry(sc, data.entry_index, + data.entry_subindex, domain, &data.bit_position); + + if (copy_to_user((void __user *) arg, &data, sizeof(data))) + return -EFAULT; + + return ret; +} + +/*****************************************************************************/ + +/** Sets the DC AssignActivate word and the sync signal times. + */ +static int ec_ioctl_sc_dc( + ec_master_t *master, /**< EtherCAT master. */ + void *arg, /**< ioctl() argument. */ + ec_ioctl_context_t *ctx /**< Private data structure of file handle. */ + ) +{ + ec_ioctl_config_t data; + ec_slave_config_t *sc; + + if (unlikely(!ctx->requested)) + return -EPERM; + + if (copy_from_user(&data, (void __user *) arg, sizeof(data))) + return -EFAULT; + + if (down_interruptible(&master->master_sem)) + return -EINTR; + + if (!(sc = ec_master_get_config(master, data.config_index))) { + up(&master->master_sem); + return -ENOENT; + } + + ecrt_slave_config_dc(sc, data.dc_assign_activate, + data.dc_sync[0].cycle_time, + data.dc_sync[0].shift_time, + data.dc_sync[1].cycle_time, + data.dc_sync[1].shift_time); + + up(&master->master_sem); + + return 0; +} + +/*****************************************************************************/ + +/** Configures an SDO. + */ +static int ec_ioctl_sc_sdo( + ec_master_t *master, /**< EtherCAT master. */ + void *arg, /**< ioctl() argument. */ + ec_ioctl_context_t *ctx /**< Private data structure of file handle. */ + ) +{ + ec_ioctl_sc_sdo_t data; + ec_slave_config_t *sc; + uint8_t *sdo_data = NULL; + int ret; + + if (unlikely(!ctx->requested)) + return -EPERM; + + if (copy_from_user(&data, (void __user *) arg, sizeof(data))) + return -EFAULT; + + if (!data.size) + return -EINVAL; + + if (!(sdo_data = kmalloc(data.size, GFP_KERNEL))) { + return -ENOMEM; + } + + if (copy_from_user(sdo_data, (void __user *) data.data, data.size)) { + kfree(sdo_data); + return -EFAULT; + } + + if (down_interruptible(&master->master_sem)) { + kfree(sdo_data); + return -EINTR; + } + + if (!(sc = ec_master_get_config(master, data.config_index))) { + up(&master->master_sem); + kfree(sdo_data); + return -ENOENT; + } + + up(&master->master_sem); /** \fixme sc could be invalidated */ + + if (data.complete_access) { + ret = ecrt_slave_config_complete_sdo(sc, + data.index, sdo_data, data.size); + } else { + ret = ecrt_slave_config_sdo(sc, data.index, data.subindex, sdo_data, + data.size); + } + kfree(sdo_data); + return ret; +} + +/*****************************************************************************/ + +/** Create an SDO request. + */ +static int ec_ioctl_sc_create_sdo_request( + ec_master_t *master, /**< EtherCAT master. */ + void *arg, /**< ioctl() argument. */ + ec_ioctl_context_t *ctx /**< Private data structure of file handle. */ + ) +{ + ec_ioctl_sdo_request_t data; + ec_slave_config_t *sc; + ec_sdo_request_t *req; + + if (unlikely(!ctx->requested)) + return -EPERM; + + if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { + return -EFAULT; + } + + data.request_index = 0; + + if (down_interruptible(&master->master_sem)) + return -EINTR; + + sc = ec_master_get_config(master, data.config_index); + if (!sc) { + up(&master->master_sem); + return -ENOENT; + } + + list_for_each_entry(req, &sc->sdo_requests, list) { + data.request_index++; + } + + up(&master->master_sem); /** \fixme sc could be invalidated */ + + req = ecrt_slave_config_create_sdo_request_err(sc, data.sdo_index, + data.sdo_subindex, data.size); + if (IS_ERR(req)) + return PTR_ERR(req); + + if (copy_to_user((void __user *) arg, &data, sizeof(data))) + return -EFAULT; + + return 0; +} + +/*****************************************************************************/ + +/** Create a VoE handler. + */ +static int ec_ioctl_sc_create_voe_handler( + ec_master_t *master, /**< EtherCAT master. */ + void *arg, /**< ioctl() argument. */ + ec_ioctl_context_t *ctx /**< Private data structure of file handle. */ + ) +{ + ec_ioctl_voe_t data; + ec_slave_config_t *sc; + ec_voe_handler_t *voe; + + if (unlikely(!ctx->requested)) + return -EPERM; + + if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { + return -EFAULT; + } + + data.voe_index = 0; + + if (down_interruptible(&master->master_sem)) + return -EINTR; + + sc = ec_master_get_config(master, data.config_index); + if (!sc) { + up(&master->master_sem); + return -ENOENT; + } + + list_for_each_entry(voe, &sc->voe_handlers, list) { + data.voe_index++; + } + + up(&master->master_sem); /** \fixme sc could be invalidated */ + + voe = ecrt_slave_config_create_voe_handler_err(sc, data.size); + if (IS_ERR(voe)) + return PTR_ERR(voe); + + if (copy_to_user((void __user *) arg, &data, sizeof(data))) + return -EFAULT; + + return 0; +} + +/*****************************************************************************/ + +/** Get the slave configuration's state. + */ +static int ec_ioctl_sc_state( + ec_master_t *master, /**< EtherCAT master. */ + void *arg, /**< ioctl() argument. */ + ec_ioctl_context_t *ctx /**< Private data structure of file handle. */ + ) +{ + ec_ioctl_sc_state_t data; + const ec_slave_config_t *sc; + ec_slave_config_state_t state; + + if (unlikely(!ctx->requested)) + return -EPERM; + + if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { + return -EFAULT; + } + + /* no locking of master_sem needed, because sc will not be deleted in the + * meantime. */ + + if (!(sc = ec_master_get_config_const(master, data.config_index))) { + return -ENOENT; + } + + ecrt_slave_config_state(sc, &state); + + if (copy_to_user((void __user *) data.state, &state, sizeof(state))) + return -EFAULT; + + return 0; +} + +/*****************************************************************************/ + +/** Configures an IDN. + */ +static int ec_ioctl_sc_idn( + ec_master_t *master, /**< EtherCAT master. */ + void *arg, /**< ioctl() argument. */ + ec_ioctl_context_t *ctx /**< Private data structure of file handle. */ + ) +{ + ec_ioctl_sc_idn_t ioctl; + ec_slave_config_t *sc; + uint8_t *data = NULL; + int ret; + + if (unlikely(!ctx->requested)) + return -EPERM; + + if (copy_from_user(&ioctl, (void __user *) arg, sizeof(ioctl))) + return -EFAULT; + + if (!ioctl.size) + return -EINVAL; + + if (!(data = kmalloc(ioctl.size, GFP_KERNEL))) { + return -ENOMEM; + } + + if (copy_from_user(data, (void __user *) ioctl.data, ioctl.size)) { + kfree(data); + return -EFAULT; + } + + if (down_interruptible(&master->master_sem)) { + kfree(data); + return -EINTR; + } + + if (!(sc = ec_master_get_config(master, ioctl.config_index))) { + up(&master->master_sem); + kfree(data); + return -ENOENT; + } + + up(&master->master_sem); /** \fixme sc could be invalidated */ + + ret = ecrt_slave_config_idn( + sc, ioctl.drive_no, ioctl.idn, ioctl.al_state, data, ioctl.size); + kfree(data); + return ret; +} + +/*****************************************************************************/ + +/** Gets the domain's offset in the total process data. + */ +static int ec_ioctl_domain_offset( + ec_master_t *master, /**< EtherCAT master. */ + void *arg, /**< ioctl() argument. */ + ec_ioctl_context_t *ctx /**< Private data structure of file handle. */ + ) +{ + int offset = 0; + const ec_domain_t *domain; + + if (unlikely(!ctx->requested)) + return -EPERM; + + if (down_interruptible(&master->master_sem)) { + return -EINTR; + } + + list_for_each_entry(domain, &master->domains, list) { + if (domain->index == (unsigned int) arg) { + up(&master->master_sem); + return offset; + } + offset += ecrt_domain_size(domain); + } + + up(&master->master_sem); + return -ENOENT; +} + +/*****************************************************************************/ + +/** Process the domain. + */ +static int ec_ioctl_domain_process( + ec_master_t *master, /**< EtherCAT master. */ + void *arg, /**< ioctl() argument. */ + ec_ioctl_context_t *ctx /**< Private data structure of file handle. */ + ) +{ + ec_domain_t *domain; + + if (unlikely(!ctx->requested)) + return -EPERM; + + /* no locking of master_sem needed, because domain will not be deleted in + * the meantime. */ + + if (!(domain = ec_master_find_domain(master, (unsigned int) arg))) { + return -ENOENT; + } + + ecrt_domain_process(domain); + return 0; +} + +/*****************************************************************************/ + +/** Queue the domain. + */ +static int ec_ioctl_domain_queue( + ec_master_t *master, /**< EtherCAT master. */ + void *arg, /**< ioctl() argument. */ + ec_ioctl_context_t *ctx /**< Private data structure of file handle. */ + ) +{ + ec_domain_t *domain; + + if (unlikely(!ctx->requested)) + return -EPERM; + + /* no locking of master_sem needed, because domain will not be deleted in + * the meantime. */ + + if (!(domain = ec_master_find_domain(master, (unsigned int) arg))) { + return -ENOENT; + } + + ecrt_domain_queue(domain); + return 0; +} + +/*****************************************************************************/ + +/** Get the domain state. + */ +static int ec_ioctl_domain_state( + ec_master_t *master, /**< EtherCAT master. */ + void *arg, /**< ioctl() argument. */ + ec_ioctl_context_t *ctx /**< Private data structure of file handle. */ + ) +{ + ec_ioctl_domain_state_t data; + const ec_domain_t *domain; + ec_domain_state_t state; + + if (unlikely(!ctx->requested)) + return -EPERM; + + if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { + return -EFAULT; + } + + /* no locking of master_sem needed, because domain will not be deleted in + * the meantime. */ + + if (!(domain = ec_master_find_domain_const(master, data.domain_index))) { + return -ENOENT; + } + + ecrt_domain_state(domain, &state); + + if (copy_to_user((void __user *) data.state, &state, sizeof(state))) + return -EFAULT; + + return 0; +} + +/*****************************************************************************/ + +/** Sets an SDO request's timeout. + */ +static int ec_ioctl_sdo_request_timeout( + ec_master_t *master, /**< EtherCAT master. */ + void *arg, /**< ioctl() argument. */ + ec_ioctl_context_t *ctx /**< Private data structure of file handle. */ + ) +{ + ec_ioctl_sdo_request_t data; + ec_slave_config_t *sc; + ec_sdo_request_t *req; + + if (unlikely(!ctx->requested)) + return -EPERM; + + if (copy_from_user(&data, (void __user *) arg, sizeof(data))) + return -EFAULT; + + /* no locking of master_sem needed, because neither sc nor req will not be + * deleted in the meantime. */ + + if (!(sc = ec_master_get_config(master, data.config_index))) { + return -ENOENT; + } + + if (!(req = ec_slave_config_find_sdo_request(sc, data.request_index))) { + return -ENOENT; + } + + ecrt_sdo_request_timeout(req, data.timeout); + return 0; +} + +/*****************************************************************************/ + +/** Gets an SDO request's state. + */ +static int ec_ioctl_sdo_request_state( + ec_master_t *master, /**< EtherCAT master. */ + void *arg, /**< ioctl() argument. */ + ec_ioctl_context_t *ctx /**< Private data structure of file handle. */ + ) +{ + ec_ioctl_sdo_request_t data; + ec_slave_config_t *sc; + ec_sdo_request_t *req; + + if (unlikely(!ctx->requested)) + return -EPERM; + + if (copy_from_user(&data, (void __user *) arg, sizeof(data))) + return -EFAULT; + + /* no locking of master_sem needed, because neither sc nor req will not be + * deleted in the meantime. */ + + if (!(sc = ec_master_get_config(master, data.config_index))) { + return -ENOENT; + } + + if (!(req = ec_slave_config_find_sdo_request(sc, data.request_index))) { + return -ENOENT; + } + + data.state = ecrt_sdo_request_state(req); + if (data.state == EC_REQUEST_SUCCESS && req->dir == EC_DIR_INPUT) + data.size = ecrt_sdo_request_data_size(req); + else + data.size = 0; + + if (copy_to_user((void __user *) arg, &data, sizeof(data))) + return -EFAULT; + + return 0; +} + +/*****************************************************************************/ + +/** Starts an SDO read operation. + */ +static int ec_ioctl_sdo_request_read( + ec_master_t *master, /**< EtherCAT master. */ + void *arg, /**< ioctl() argument. */ + ec_ioctl_context_t *ctx /**< Private data structure of file handle. */ + ) +{ + ec_ioctl_sdo_request_t data; + ec_slave_config_t *sc; + ec_sdo_request_t *req; + + if (unlikely(!ctx->requested)) + return -EPERM; + + if (copy_from_user(&data, (void __user *) arg, sizeof(data))) + return -EFAULT; + + /* no locking of master_sem needed, because neither sc nor req will not be + * deleted in the meantime. */ + + if (!(sc = ec_master_get_config(master, data.config_index))) { + return -ENOENT; + } + + if (!(req = ec_slave_config_find_sdo_request(sc, data.request_index))) { + return -ENOENT; + } + + ecrt_sdo_request_read(req); + return 0; +} + +/*****************************************************************************/ + +/** Starts an SDO write operation. + */ +static int ec_ioctl_sdo_request_write( + ec_master_t *master, /**< EtherCAT master. */ + void *arg, /**< ioctl() argument. */ + ec_ioctl_context_t *ctx /**< Private data structure of file handle. */ + ) +{ + ec_ioctl_sdo_request_t data; + ec_slave_config_t *sc; + ec_sdo_request_t *req; + int ret; + + if (unlikely(!ctx->requested)) + return -EPERM; + + if (copy_from_user(&data, (void __user *) arg, sizeof(data))) + return -EFAULT; + + if (!data.size) { + EC_MASTER_ERR(master, "SDO download: Data size may not be zero!\n"); + return -EINVAL; + } + + /* no locking of master_sem needed, because neither sc nor req will not be + * deleted in the meantime. */ + + if (!(sc = ec_master_get_config(master, data.config_index))) { + return -ENOENT; + } + + if (!(req = ec_slave_config_find_sdo_request(sc, data.request_index))) { + return -ENOENT; + } + + ret = ec_sdo_request_alloc(req, data.size); + if (ret) + return ret; + + if (copy_from_user(req->data, (void __user *) data.data, data.size)) + return -EFAULT; + + req->data_size = data.size; + ecrt_sdo_request_write(req); + return 0; +} + +/*****************************************************************************/ + +/** Read SDO data. + */ +static int ec_ioctl_sdo_request_data( + ec_master_t *master, /**< EtherCAT master. */ + void *arg, /**< ioctl() argument. */ + ec_ioctl_context_t *ctx /**< Private data structure of file handle. */ + ) +{ + ec_ioctl_sdo_request_t data; + ec_slave_config_t *sc; + ec_sdo_request_t *req; + + if (unlikely(!ctx->requested)) + return -EPERM; + + if (copy_from_user(&data, (void __user *) arg, sizeof(data))) + return -EFAULT; + + /* no locking of master_sem needed, because neither sc nor req will not be + * deleted in the meantime. */ + + if (!(sc = ec_master_get_config(master, data.config_index))) { + return -ENOENT; + } + + if (!(req = ec_slave_config_find_sdo_request(sc, data.request_index))) { + return -ENOENT; + } + + if (copy_to_user((void __user *) data.data, ecrt_sdo_request_data(req), + ecrt_sdo_request_data_size(req))) + return -EFAULT; + + return 0; +} + +/*****************************************************************************/ + +/** Sets the VoE send header. + */ +static int ec_ioctl_voe_send_header( + ec_master_t *master, /**< EtherCAT master. */ + void *arg, /**< ioctl() argument. */ + ec_ioctl_context_t *ctx /**< Private data structure of file handle. */ + ) +{ + ec_ioctl_voe_t data; + ec_slave_config_t *sc; + ec_voe_handler_t *voe; + uint32_t vendor_id; + uint16_t vendor_type; + + if (unlikely(!ctx->requested)) + return -EPERM; + + if (copy_from_user(&data, (void __user *) arg, sizeof(data))) + return -EFAULT; + + if (get_user(vendor_id, data.vendor_id)) + return -EFAULT; + + if (get_user(vendor_type, data.vendor_type)) + return -EFAULT; + + /* no locking of master_sem needed, because neither sc nor voe will not be + * deleted in the meantime. */ + + if (!(sc = ec_master_get_config(master, data.config_index))) { + return -ENOENT; + } + + if (!(voe = ec_slave_config_find_voe_handler(sc, data.voe_index))) { + return -ENOENT; + } + + ecrt_voe_handler_send_header(voe, vendor_id, vendor_type); + return 0; +} + +/*****************************************************************************/ + +/** Gets the received VoE header. + */ +static int ec_ioctl_voe_rec_header( + ec_master_t *master, /**< EtherCAT master. */ + void *arg, /**< ioctl() argument. */ + ec_ioctl_context_t *ctx /**< Private data structure of file handle. */ + ) +{ + ec_ioctl_voe_t data; + ec_slave_config_t *sc; + ec_voe_handler_t *voe; + uint32_t vendor_id; + uint16_t vendor_type; + + if (unlikely(!ctx->requested)) + return -EPERM; + + if (copy_from_user(&data, (void __user *) arg, sizeof(data))) + return -EFAULT; + + /* no locking of master_sem needed, because neither sc nor voe will not be + * deleted in the meantime. */ + + if (!(sc = ec_master_get_config(master, data.config_index))) { + return -ENOENT; + } + + if (!(voe = ec_slave_config_find_voe_handler(sc, data.voe_index))) { + return -ENOENT; + } + + ecrt_voe_handler_received_header(voe, &vendor_id, &vendor_type); + + if (likely(data.vendor_id)) + if (put_user(vendor_id, data.vendor_id)) + return -EFAULT; + + if (likely(data.vendor_type)) + if (put_user(vendor_type, data.vendor_type)) + return -EFAULT; + + return 0; +} + +/*****************************************************************************/ + +/** Starts a VoE read operation. + */ +static int ec_ioctl_voe_read( + ec_master_t *master, /**< EtherCAT master. */ + void *arg, /**< ioctl() argument. */ + ec_ioctl_context_t *ctx /**< Private data structure of file handle. */ + ) +{ + ec_ioctl_voe_t data; + ec_slave_config_t *sc; + ec_voe_handler_t *voe; + + if (unlikely(!ctx->requested)) + return -EPERM; + + if (copy_from_user(&data, (void __user *) arg, sizeof(data))) + return -EFAULT; + + /* no locking of master_sem needed, because neither sc nor voe will not be + * deleted in the meantime. */ + + if (!(sc = ec_master_get_config(master, data.config_index))) { + return -ENOENT; + } + + if (!(voe = ec_slave_config_find_voe_handler(sc, data.voe_index))) { + return -ENOENT; + } + + ecrt_voe_handler_read(voe); + return 0; +} + +/*****************************************************************************/ + +/** Starts a VoE read operation without sending a sync message first. + */ +static int ec_ioctl_voe_read_nosync( + ec_master_t *master, /**< EtherCAT master. */ + void *arg, /**< ioctl() argument. */ + ec_ioctl_context_t *ctx /**< Private data structure of file handle. */ + ) +{ + ec_ioctl_voe_t data; + ec_slave_config_t *sc; + ec_voe_handler_t *voe; + + if (unlikely(!ctx->requested)) + return -EPERM; + + if (copy_from_user(&data, (void __user *) arg, sizeof(data))) + return -EFAULT; + + /* no locking of master_sem needed, because neither sc nor voe will not be + * deleted in the meantime. */ + + if (!(sc = ec_master_get_config(master, data.config_index))) { + return -ENOENT; + } + + if (!(voe = ec_slave_config_find_voe_handler(sc, data.voe_index))) { + return -ENOENT; + } + + ecrt_voe_handler_read_nosync(voe); + return 0; +} + +/*****************************************************************************/ + +/** Starts a VoE write operation. + */ +static int ec_ioctl_voe_write( + ec_master_t *master, /**< EtherCAT master. */ + void *arg, /**< ioctl() argument. */ + ec_ioctl_context_t *ctx /**< Private data structure of file handle. */ + ) +{ + ec_ioctl_voe_t data; + ec_slave_config_t *sc; + ec_voe_handler_t *voe; + + if (unlikely(!ctx->requested)) + return -EPERM; + + if (copy_from_user(&data, (void __user *) arg, sizeof(data))) + return -EFAULT; + + /* no locking of master_sem needed, because neither sc nor voe will not be + * deleted in the meantime. */ + + if (!(sc = ec_master_get_config(master, data.config_index))) { + return -ENOENT; + } + + if (!(voe = ec_slave_config_find_voe_handler(sc, data.voe_index))) { + return -ENOENT; + } + + if (data.size) { + if (data.size > ec_voe_handler_mem_size(voe)) + return -EOVERFLOW; + + if (copy_from_user(ecrt_voe_handler_data(voe), + (void __user *) data.data, data.size)) + return -EFAULT; + } + + ecrt_voe_handler_write(voe, data.size); + return 0; +} + +/*****************************************************************************/ + +/** Executes the VoE state machine. + */ +static int ec_ioctl_voe_exec( + ec_master_t *master, /**< EtherCAT master. */ + void *arg, /**< ioctl() argument. */ + ec_ioctl_context_t *ctx /**< Private data structure of file handle. */ + ) +{ + ec_ioctl_voe_t data; + ec_slave_config_t *sc; + ec_voe_handler_t *voe; + + if (unlikely(!ctx->requested)) + return -EPERM; + + if (copy_from_user(&data, (void __user *) arg, sizeof(data))) + return -EFAULT; + + /* no locking of master_sem needed, because neither sc nor voe will not be + * deleted in the meantime. */ + + if (!(sc = ec_master_get_config(master, data.config_index))) { + return -ENOENT; + } + + if (!(voe = ec_slave_config_find_voe_handler(sc, data.voe_index))) { + return -ENOENT; + } + + data.state = ecrt_voe_handler_execute(voe); + if (data.state == EC_REQUEST_SUCCESS && voe->dir == EC_DIR_INPUT) + data.size = ecrt_voe_handler_data_size(voe); + else + data.size = 0; + + if (copy_to_user((void __user *) arg, &data, sizeof(data))) + return -EFAULT; + + return 0; +} + +/*****************************************************************************/ + +/** Reads the received VoE data. + */ +static int ec_ioctl_voe_data( + ec_master_t *master, /**< EtherCAT master. */ + void *arg, /**< ioctl() argument. */ + ec_ioctl_context_t *ctx /**< Private data structure of file handle. */ + ) +{ + ec_ioctl_voe_t data; + ec_slave_config_t *sc; + ec_voe_handler_t *voe; + + if (unlikely(!ctx->requested)) + return -EPERM; + + if (copy_from_user(&data, (void __user *) arg, sizeof(data))) + return -EFAULT; + + /* no locking of master_sem needed, because neither sc nor voe will not be + * deleted in the meantime. */ + + if (!(sc = ec_master_get_config(master, data.config_index))) { + return -ENOENT; + } + + if (!(voe = ec_slave_config_find_voe_handler(sc, data.voe_index))) { + return -ENOENT; + } + + if (copy_to_user((void __user *) data.data, ecrt_voe_handler_data(voe), + ecrt_voe_handler_data_size(voe))) + return -EFAULT; + + return 0; +} + +/*****************************************************************************/ + +/** Read a file from a slave via FoE. + */ +static int ec_ioctl_slave_foe_read( + ec_master_t *master, /**< EtherCAT master. */ + void *arg /**< ioctl() argument. */ + ) +{ + ec_ioctl_slave_foe_t data; + ec_master_foe_request_t request; + int retval; + + if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { + return -EFAULT; + } + + ec_foe_request_init(&request.req, data.file_name); + ec_foe_request_read(&request.req); + ec_foe_request_alloc(&request.req, 10000); // FIXME + + if (down_interruptible(&master->master_sem)) { + ec_foe_request_clear(&request.req); + return -EINTR; + } + + if (!(request.slave = ec_master_find_slave( + master, 0, data.slave_position))) { + up(&master->master_sem); + ec_foe_request_clear(&request.req); + EC_MASTER_ERR(master, "Slave %u does not exist!\n", + data.slave_position); + return -EINVAL; + } + + // schedule request. + list_add_tail(&request.list, &request.slave->foe_requests); + + up(&master->master_sem); + + EC_SLAVE_DBG(request.slave, 1, "Scheduled FoE read request.\n"); + + // wait for processing through FSM + if (wait_event_interruptible(request.slave->foe_queue, + request.req.state != EC_INT_REQUEST_QUEUED)) { + // interrupted by signal + down(&master->master_sem); + if (request.req.state == EC_INT_REQUEST_QUEUED) { + list_del(&request.list); + up(&master->master_sem); + ec_foe_request_clear(&request.req); + return -EINTR; + } + // request already processing: interrupt not possible. + up(&master->master_sem); + } + + // wait until master FSM has finished processing + wait_event(request.slave->foe_queue, + request.req.state != EC_INT_REQUEST_BUSY); + + data.result = request.req.result; + data.error_code = request.req.error_code; + + EC_SLAVE_DBG(request.slave, 1, "Read %zd bytes via FoE" + " (result = 0x%x).\n", request.req.data_size, request.req.result); + + if (request.req.state != EC_INT_REQUEST_SUCCESS) { + data.data_size = 0; + retval = -EIO; + } else { + if (request.req.data_size > data.buffer_size) { + EC_MASTER_ERR(master, "Buffer too small.\n"); + ec_foe_request_clear(&request.req); + return -EOVERFLOW; + } + data.data_size = request.req.data_size; + if (copy_to_user((void __user *) data.buffer, + request.req.buffer, data.data_size)) { + ec_foe_request_clear(&request.req); + return -EFAULT; + } + retval = 0; + } + + if (__copy_to_user((void __user *) arg, &data, sizeof(data))) { + retval = -EFAULT; + } + + EC_SLAVE_DBG(request.slave, 1, "Finished FoE read request.\n"); + + ec_foe_request_clear(&request.req); + + return retval; +} + +/*****************************************************************************/ + +/** Write a file to a slave via FoE + */ +static int ec_ioctl_slave_foe_write( + ec_master_t *master, /**< EtherCAT master. */ + void *arg /**< ioctl() argument. */ + ) +{ + ec_ioctl_slave_foe_t data; + ec_master_foe_request_t request; + int retval; + + if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { + return -EFAULT; + } + + INIT_LIST_HEAD(&request.list); + + ec_foe_request_init(&request.req, data.file_name); + + if (ec_foe_request_alloc(&request.req, data.buffer_size)) { + ec_foe_request_clear(&request.req); + return -ENOMEM; + } + if (copy_from_user(request.req.buffer, + (void __user *) data.buffer, data.buffer_size)) { + ec_foe_request_clear(&request.req); + return -EFAULT; + } + request.req.data_size = data.buffer_size; + ec_foe_request_write(&request.req); + + if (down_interruptible(&master->master_sem)) { + ec_foe_request_clear(&request.req); + return -EINTR; + } + + if (!(request.slave = ec_master_find_slave( + master, 0, data.slave_position))) { + up(&master->master_sem); + EC_MASTER_ERR(master, "Slave %u does not exist!\n", + data.slave_position); + ec_foe_request_clear(&request.req); + return -EINVAL; + } + + EC_SLAVE_DBG(request.slave, 1, "Scheduling FoE write request.\n"); + + // schedule FoE write request. + list_add_tail(&request.list, &request.slave->foe_requests); + + up(&master->master_sem); + + // wait for processing through FSM + if (wait_event_interruptible(request.slave->foe_queue, + request.req.state != EC_INT_REQUEST_QUEUED)) { + // interrupted by signal + down(&master->master_sem); + if (request.req.state == EC_INT_REQUEST_QUEUED) { + // abort request + list_del(&request.list); + up(&master->master_sem); + ec_foe_request_clear(&request.req); + return -EINTR; + } + up(&master->master_sem); + } + + // wait until master FSM has finished processing + wait_event(request.slave->foe_queue, + request.req.state != EC_INT_REQUEST_BUSY); + + data.result = request.req.result; + data.error_code = request.req.error_code; + + retval = request.req.state == EC_INT_REQUEST_SUCCESS ? 0 : -EIO; + + if (__copy_to_user((void __user *) arg, &data, sizeof(data))) { + retval = -EFAULT; + } + + ec_foe_request_clear(&request.req); + + EC_SLAVE_DBG(request.slave, 1, "Finished FoE write request.\n"); + + return retval; +} + +/*****************************************************************************/ + +/** Read an SoE IDN. + */ +static int ec_ioctl_slave_soe_read( + ec_master_t *master, /**< EtherCAT master. */ + void *arg /**< ioctl() argument. */ + ) +{ + ec_ioctl_slave_soe_read_t ioctl; + u8 *data; + int retval; + + if (copy_from_user(&ioctl, (void __user *) arg, sizeof(ioctl))) { + return -EFAULT; + } + + data = kmalloc(ioctl.mem_size, GFP_KERNEL); + if (!data) { + EC_MASTER_ERR(master, "Failed to allocate %u bytes of IDN data.\n", + ioctl.mem_size); + return -ENOMEM; + } + + retval = ecrt_master_read_idn(master, ioctl.slave_position, + ioctl.drive_no, ioctl.idn, data, ioctl.mem_size, &ioctl.data_size, + &ioctl.error_code); + if (retval) { + kfree(data); + return retval; + } + + if (copy_to_user((void __user *) ioctl.data, + data, ioctl.data_size)) { + kfree(data); + return -EFAULT; + } + kfree(data); + + if (__copy_to_user((void __user *) arg, &ioctl, sizeof(ioctl))) { + retval = -EFAULT; + } + + EC_MASTER_DBG(master, 1, "Finished SoE read request.\n"); + return retval; +} + +/*****************************************************************************/ + +/** Write an IDN to a slave via SoE. + */ +static int ec_ioctl_slave_soe_write( + ec_master_t *master, /**< EtherCAT master. */ + void *arg /**< ioctl() argument. */ + ) +{ + ec_ioctl_slave_soe_write_t ioctl; + u8 *data; + int retval; + + if (copy_from_user(&ioctl, (void __user *) arg, sizeof(ioctl))) { + return -EFAULT; + } + + data = kmalloc(ioctl.data_size, GFP_KERNEL); + if (!data) { + EC_MASTER_ERR(master, "Failed to allocate %zu bytes of IDN data.\n", + ioctl.data_size); + return -ENOMEM; + } + if (copy_from_user(data, (void __user *) ioctl.data, ioctl.data_size)) { + kfree(data); + return -EFAULT; + } + + retval = ecrt_master_write_idn(master, ioctl.slave_position, + ioctl.drive_no, ioctl.idn, data, ioctl.data_size, + &ioctl.error_code); + kfree(data); + if (retval) { + return retval; + } + + if (__copy_to_user((void __user *) arg, &ioctl, sizeof(ioctl))) { + retval = -EFAULT; + } + + EC_MASTER_DBG(master, 1, "Finished SoE write request.\n"); + return retval; +} + +/*****************************************************************************/ + +#ifdef EC_IOCTL_RTDM +#define EC_IOCTL ec_ioctl_rtdm +#else +#define EC_IOCTL ec_ioctl +#endif + +/** Called when an ioctl() command is issued. + */ +long EC_IOCTL(ec_master_t *master, ec_ioctl_context_t *ctx, + unsigned int cmd, void *arg) +{ +#if DEBUG_LATENCY + cycles_t a = get_cycles(), b; + unsigned int t; +#endif + int ret; + + switch (cmd) { + case EC_IOCTL_MODULE: + ret = ec_ioctl_module(arg); + break; + case EC_IOCTL_MASTER: + ret = ec_ioctl_master(master, arg); + break; + case EC_IOCTL_SLAVE: + ret = ec_ioctl_slave(master, arg); + break; + case EC_IOCTL_SLAVE_SYNC: + ret = ec_ioctl_slave_sync(master, arg); + break; + case EC_IOCTL_SLAVE_SYNC_PDO: + ret = ec_ioctl_slave_sync_pdo(master, arg); + break; + case EC_IOCTL_SLAVE_SYNC_PDO_ENTRY: + ret = ec_ioctl_slave_sync_pdo_entry(master, arg); + break; + case EC_IOCTL_DOMAIN: + ret = ec_ioctl_domain(master, arg); + break; + case EC_IOCTL_DOMAIN_FMMU: + ret = ec_ioctl_domain_fmmu(master, arg); + break; + case EC_IOCTL_DOMAIN_DATA: + ret = ec_ioctl_domain_data(master, arg); + break; + case EC_IOCTL_MASTER_DEBUG: + if (!ctx->writable) { + ret = -EPERM; + break; + } + ret = ec_ioctl_master_debug(master, arg); + break; + case EC_IOCTL_MASTER_RESCAN: + if (!ctx->writable) { + ret = -EPERM; + break; + } + ret = ec_ioctl_master_rescan(master, arg); + break; + case EC_IOCTL_SLAVE_STATE: + if (!ctx->writable) { + ret = -EPERM; + break; + } + ret = ec_ioctl_slave_state(master, arg); + break; + case EC_IOCTL_SLAVE_SDO: + ret = ec_ioctl_slave_sdo(master, arg); + break; + case EC_IOCTL_SLAVE_SDO_ENTRY: + ret = ec_ioctl_slave_sdo_entry(master, arg); + break; + case EC_IOCTL_SLAVE_SDO_UPLOAD: + ret = ec_ioctl_slave_sdo_upload(master, arg); + break; + case EC_IOCTL_SLAVE_SDO_DOWNLOAD: + if (!ctx->writable) { + ret = -EPERM; + break; + } + ret = ec_ioctl_slave_sdo_download(master, arg); + break; + case EC_IOCTL_SLAVE_SII_READ: + ret = ec_ioctl_slave_sii_read(master, arg); + break; + case EC_IOCTL_SLAVE_SII_WRITE: + if (!ctx->writable) { + ret = -EPERM; + break; + } + ret = ec_ioctl_slave_sii_write(master, arg); + break; + case EC_IOCTL_SLAVE_REG_READ: + ret = ec_ioctl_slave_reg_read(master, arg); + break; + case EC_IOCTL_SLAVE_REG_WRITE: + if (!ctx->writable) { + ret = -EPERM; + break; + } + ret = ec_ioctl_slave_reg_write(master, arg); + break; + case EC_IOCTL_SLAVE_FOE_READ: + ret = ec_ioctl_slave_foe_read(master, arg); + break; + case EC_IOCTL_SLAVE_FOE_WRITE: + if (!ctx->writable) { + ret = -EPERM; + break; + } + ret = ec_ioctl_slave_foe_write(master, arg); + break; + case EC_IOCTL_SLAVE_SOE_READ: + ret = ec_ioctl_slave_soe_read(master, arg); + break; + case EC_IOCTL_SLAVE_SOE_WRITE: + if (!ctx->writable) { + ret = -EPERM; + break; + } + ret = ec_ioctl_slave_soe_write(master, arg); + break; + case EC_IOCTL_CONFIG: + ret = ec_ioctl_config(master, arg); + break; + case EC_IOCTL_CONFIG_PDO: + ret = ec_ioctl_config_pdo(master, arg); + break; + case EC_IOCTL_CONFIG_PDO_ENTRY: + ret = ec_ioctl_config_pdo_entry(master, arg); + break; + case EC_IOCTL_CONFIG_SDO: + ret = ec_ioctl_config_sdo(master, arg); + break; + case EC_IOCTL_CONFIG_IDN: + ret = ec_ioctl_config_idn(master, arg); + break; +#ifdef EC_EOE + case EC_IOCTL_EOE_HANDLER: + ret = ec_ioctl_eoe_handler(master, arg); + break; +#endif + case EC_IOCTL_REQUEST: + if (!ctx->writable) { + ret = -EPERM; + break; + } + ret = ec_ioctl_request(master, arg, ctx); + break; + case EC_IOCTL_CREATE_DOMAIN: + if (!ctx->writable) { + ret = -EPERM; + break; + } + ret = ec_ioctl_create_domain(master, arg, ctx); + break; + case EC_IOCTL_CREATE_SLAVE_CONFIG: + if (!ctx->writable) { + ret = -EPERM; + break; + } + ret = ec_ioctl_create_slave_config(master, arg, ctx); + break; + case EC_IOCTL_ACTIVATE: + if (!ctx->writable) { + ret = -EPERM; + break; + } + ret = ec_ioctl_activate(master, arg, ctx); + break; + case EC_IOCTL_DEACTIVATE: + if (!ctx->writable) { + ret = -EPERM; + break; + } + ret = ec_ioctl_deactivate(master, arg, ctx); + break; + case EC_IOCTL_SEND: + if (!ctx->writable) { + ret = -EPERM; + break; + } + ret = ec_ioctl_send(master, arg, ctx); + break; + case EC_IOCTL_RECEIVE: + if (!ctx->writable) { + ret = -EPERM; + break; + } + ret = ec_ioctl_receive(master, arg, ctx); + break; + case EC_IOCTL_MASTER_STATE: + ret = ec_ioctl_master_state(master, arg, ctx); + break; + case EC_IOCTL_MASTER_LINK_STATE: + ret = ec_ioctl_master_link_state(master, arg, ctx); + break; + case EC_IOCTL_APP_TIME: + if (!ctx->writable) { + ret = -EPERM; + break; + } + ret = ec_ioctl_app_time(master, arg, ctx); + break; + case EC_IOCTL_SYNC_REF: + if (!ctx->writable) { + ret = -EPERM; + break; + } + ret = ec_ioctl_sync_ref(master, arg, ctx); + break; + case EC_IOCTL_SYNC_SLAVES: + if (!ctx->writable) { + ret = -EPERM; + break; + } + ret = ec_ioctl_sync_slaves(master, arg, ctx); + break; + case EC_IOCTL_SYNC_MON_QUEUE: + if (!ctx->writable) { + ret = -EPERM; + break; + } + ret = ec_ioctl_sync_mon_queue(master, arg, ctx); + break; + case EC_IOCTL_SYNC_MON_PROCESS: + if (!ctx->writable) { + ret = -EPERM; + break; + } + ret = ec_ioctl_sync_mon_process(master, arg, ctx); + break; + case EC_IOCTL_RESET: + if (!ctx->writable) { + ret = -EPERM; + break; + } + ret = ec_ioctl_reset(master, arg, ctx); + break; + case EC_IOCTL_SC_SYNC: + if (!ctx->writable) { + ret = -EPERM; + break; + } + ret = ec_ioctl_sc_sync(master, arg, ctx); + break; + case EC_IOCTL_SC_WATCHDOG: + if (!ctx->writable) { + ret = -EPERM; + break; + } + ret = ec_ioctl_sc_watchdog(master, arg, ctx); + break; + case EC_IOCTL_SC_ADD_PDO: + if (!ctx->writable) { + ret = -EPERM; + break; + } + ret = ec_ioctl_sc_add_pdo(master, arg, ctx); + break; + case EC_IOCTL_SC_CLEAR_PDOS: + if (!ctx->writable) { + ret = -EPERM; + break; + } + ret = ec_ioctl_sc_clear_pdos(master, arg, ctx); + break; + case EC_IOCTL_SC_ADD_ENTRY: + if (!ctx->writable) { + ret = -EPERM; + break; + } + ret = ec_ioctl_sc_add_entry(master, arg, ctx); + break; + case EC_IOCTL_SC_CLEAR_ENTRIES: + if (!ctx->writable) { + ret = -EPERM; + break; + } + ret = ec_ioctl_sc_clear_entries(master, arg, ctx); + break; + case EC_IOCTL_SC_REG_PDO_ENTRY: + if (!ctx->writable) { + ret = -EPERM; + break; + } + ret = ec_ioctl_sc_reg_pdo_entry(master, arg, ctx); + break; + case EC_IOCTL_SC_DC: + if (!ctx->writable) { + ret = -EPERM; + break; + } + ret = ec_ioctl_sc_dc(master, arg, ctx); + break; + case EC_IOCTL_SC_SDO: + if (!ctx->writable) { + ret = -EPERM; + break; + } + ret = ec_ioctl_sc_sdo(master, arg, ctx); + break; + case EC_IOCTL_SC_SDO_REQUEST: + if (!ctx->writable) { + ret = -EPERM; + break; + } + ret = ec_ioctl_sc_create_sdo_request(master, arg, ctx); + break; + case EC_IOCTL_SC_VOE: + if (!ctx->writable) { + ret = -EPERM; + break; + } + ret = ec_ioctl_sc_create_voe_handler(master, arg, ctx); + break; + case EC_IOCTL_SC_STATE: + ret = ec_ioctl_sc_state(master, arg, ctx); + break; + case EC_IOCTL_SC_IDN: + if (!ctx->writable) { + ret = -EPERM; + break; + } + ret = ec_ioctl_sc_idn(master, arg, ctx); + break; + case EC_IOCTL_DOMAIN_OFFSET: + ret = ec_ioctl_domain_offset(master, arg, ctx); + break; + case EC_IOCTL_DOMAIN_PROCESS: + if (!ctx->writable) { + ret = -EPERM; + break; + } + ret = ec_ioctl_domain_process(master, arg, ctx); + break; + case EC_IOCTL_DOMAIN_QUEUE: + if (!ctx->writable) { + ret = -EPERM; + break; + } + ret = ec_ioctl_domain_queue(master, arg, ctx); + break; + case EC_IOCTL_DOMAIN_STATE: + ret = ec_ioctl_domain_state(master, arg, ctx); + break; + case EC_IOCTL_SDO_REQUEST_TIMEOUT: + if (!ctx->writable) { + ret = -EPERM; + break; + } + ret = ec_ioctl_sdo_request_timeout(master, arg, ctx); + break; + case EC_IOCTL_SDO_REQUEST_STATE: + ret = ec_ioctl_sdo_request_state(master, arg, ctx); + break; + case EC_IOCTL_SDO_REQUEST_READ: + if (!ctx->writable) { + ret = -EPERM; + break; + } + ret = ec_ioctl_sdo_request_read(master, arg, ctx); + break; + case EC_IOCTL_SDO_REQUEST_WRITE: + if (!ctx->writable) { + ret = -EPERM; + break; + } + ret = ec_ioctl_sdo_request_write(master, arg, ctx); + break; + case EC_IOCTL_SDO_REQUEST_DATA: + ret = ec_ioctl_sdo_request_data(master, arg, ctx); + break; + case EC_IOCTL_VOE_SEND_HEADER: + if (!ctx->writable) { + ret = -EPERM; + break; + } + ret = ec_ioctl_voe_send_header(master, arg, ctx); + break; + case EC_IOCTL_VOE_REC_HEADER: + ret = ec_ioctl_voe_rec_header(master, arg, ctx); + break; + case EC_IOCTL_VOE_READ: + if (!ctx->writable) { + ret = -EPERM; + break; + } + ret = ec_ioctl_voe_read(master, arg, ctx); + break; + case EC_IOCTL_VOE_READ_NOSYNC: + if (!ctx->writable) { + ret = -EPERM; + break; + } + ret = ec_ioctl_voe_read_nosync(master, arg, ctx); + break; + case EC_IOCTL_VOE_WRITE: + if (!ctx->writable) { + ret = -EPERM; + break; + } + ret = ec_ioctl_voe_write(master, arg, ctx); + break; + case EC_IOCTL_VOE_EXEC: + if (!ctx->writable) { + ret = -EPERM; + break; + } + ret = ec_ioctl_voe_exec(master, arg, ctx); + break; + case EC_IOCTL_VOE_DATA: + ret = ec_ioctl_voe_data(master, arg, ctx); + break; + case EC_IOCTL_SET_SEND_INTERVAL: + if (!ctx->writable) { + ret = -EPERM; + break; + } + ret = ec_ioctl_set_send_interval(master, arg, ctx); + break; + default: + ret = -ENOTTY; + break; + } + +#if DEBUG_LATENCY + b = get_cycles(); + t = (unsigned int) ((b - a) * 1000LL) / cpu_khz; + if (t > 50) { + EC_MASTER_WARN(master, "ioctl(0x%02x) took %u us.\n", + _IOC_NR(cmd), t); + } +#endif + + return ret; +} + +/*****************************************************************************/ diff -r f4313f5aba88 -r 3bdd7a747fae master/ioctl.h --- a/master/ioctl.h Thu Sep 20 09:20:51 2012 +0200 +++ b/master/ioctl.h Thu Sep 20 15:28:25 2012 +0200 @@ -2,7 +2,7 @@ * * $Id$ * - * Copyright (C) 2006-2008 Florian Pose, Ingenieurgemeinschaft IgH + * Copyright (C) 2006-2012 Florian Pose, Ingenieurgemeinschaft IgH * * This file is part of the IgH EtherCAT Master. * @@ -96,7 +96,7 @@ #define EC_IOCTL_REQUEST EC_IO(0x1e) #define EC_IOCTL_CREATE_DOMAIN EC_IO(0x1f) #define EC_IOCTL_CREATE_SLAVE_CONFIG EC_IOWR(0x20, ec_ioctl_config_t) -#define EC_IOCTL_ACTIVATE EC_IOR(0x21, size_t) +#define EC_IOCTL_ACTIVATE EC_IOR(0x21, ec_ioctl_master_activate_t) #define EC_IOCTL_DEACTIVATE EC_IO(0x22) #define EC_IOCTL_SEND EC_IO(0x23) #define EC_IOCTL_RECEIVE EC_IO(0x24) @@ -574,6 +574,14 @@ /*****************************************************************************/ typedef struct { + // outputs + void *process_data; + size_t process_data_size; +} ec_ioctl_master_activate_t; + +/*****************************************************************************/ + +typedef struct { // inputs uint32_t config_index; uint16_t pdo_index; @@ -689,6 +697,32 @@ /*****************************************************************************/ +#ifdef __KERNEL__ + +/** Context data structure for file handles. + */ +typedef struct { + unsigned int writable; /**< Device was opened with write permission. */ + unsigned int requested; /**< Master was requested via this file handle. */ + uint8_t *process_data; /**< Total process data area. */ + size_t process_data_size; /**< Size of the \a process_data. */ +} ec_ioctl_context_t; + +long ec_ioctl(ec_master_t *, ec_ioctl_context_t *, unsigned int, + void __user *); + +#ifdef EC_RTDM + +long ec_ioctl_rtdm(ec_master_t *, ec_ioctl_context_t *, unsigned int, + void __user *); +int ec_rtdm_mmap(ec_ioctl_context_t *, void **); + +#endif + +#endif + +/*****************************************************************************/ + /** \endcond */ #endif diff -r f4313f5aba88 -r 3bdd7a747fae master/master.c --- a/master/master.c Thu Sep 20 09:20:51 2012 +0200 +++ b/master/master.c Thu Sep 20 15:28:25 2012 +0200 @@ -2,7 +2,7 @@ * * $Id$ * - * Copyright (C) 2006-2008 Florian Pose, Ingenieurgemeinschaft IgH + * Copyright (C) 2006-2012 Florian Pose, Ingenieurgemeinschaft IgH * * This file is part of the IgH EtherCAT Master. * @@ -298,8 +298,24 @@ goto out_clear_cdev; } +#ifdef EC_RTDM + // init RTDM device + ret = ec_rtdm_dev_init(&master->rtdm_dev, master); + if (ret) { + goto out_unregister_class_device; + } +#endif + return 0; +#ifdef EC_RTDM +out_unregister_class_device: +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) + device_unregister(master->class_device); +#else + class_device_unregister(master->class_device); +#endif +#endif out_clear_cdev: ec_cdev_clear(&master->cdev); out_clear_sync_mon: @@ -327,6 +343,10 @@ ec_master_t *master /**< EtherCAT master */ ) { +#ifdef EC_RTDM + ec_rtdm_dev_clear(&master->rtdm_dev); +#endif + #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) device_unregister(master->class_device); #else diff -r f4313f5aba88 -r 3bdd7a747fae master/master.h --- a/master/master.h Thu Sep 20 09:20:51 2012 +0200 +++ b/master/master.h Thu Sep 20 15:28:25 2012 +0200 @@ -2,7 +2,7 @@ * * $Id$ * - * Copyright (C) 2006-2008 Florian Pose, Ingenieurgemeinschaft IgH + * Copyright (C) 2006-2012 Florian Pose, Ingenieurgemeinschaft IgH * * This file is part of the IgH EtherCAT Master. * @@ -55,6 +55,10 @@ #include "fsm_master.h" #include "cdev.h" +#ifdef EC_RTDM +#include "rtdm.h" +#endif + /*****************************************************************************/ /** Convenience macro for printing master-specific information to syslog. @@ -183,6 +187,10 @@ struct class_device *class_device; /**< Master class device. */ #endif +#ifdef EC_RTDM + ec_rtdm_dev_t rtdm_dev; /**< RTDM device. */ +#endif + struct semaphore master_sem; /**< Master semaphore. */ ec_device_t devices[EC_NUM_DEVICES]; /**< EtherCAT devices. */ diff -r f4313f5aba88 -r 3bdd7a747fae master/rtdm-ioctl.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/master/rtdm-ioctl.c Thu Sep 20 15:28:25 2012 +0200 @@ -0,0 +1,1 @@ +ioctl.c \ No newline at end of file diff -r f4313f5aba88 -r 3bdd7a747fae master/rtdm.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/master/rtdm.c Thu Sep 20 15:28:25 2012 +0200 @@ -0,0 +1,218 @@ +/***************************************************************************** + * + * $Id$ + * + * Copyright (C) 2009-2010 Moehwald GmbH B. Benner + * 2011 IgH Andreas Stewering-Bone + * 2012 Florian Pose + * + * This file is part of the IgH EtherCAT master. + * + * The IgH EtherCAT master 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; version 2 of the License. + * + * The IgH EtherCAT master 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 General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with the IgH EtherCAT master. 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. + * + ****************************************************************************/ + +#include +#include +#include + +#include + +#include "master.h" +#include "ioctl.h" +#include "rtdm.h" + +/** Set to 1 to enable device operations debugging. + */ +#define DEBUG 0 + +/****************************************************************************/ + +/** Context structure for an open RTDM file handle. + */ +typedef struct { + rtdm_user_info_t *user_info; + ec_ioctl_context_t ioctl_ctx; +} ec_rtdm_context_t; + +/****************************************************************************/ + +int ec_rtdm_open(struct rtdm_dev_context *, rtdm_user_info_t *, int); +int ec_rtdm_close(struct rtdm_dev_context *, rtdm_user_info_t *); +int ec_rtdm_ioctl(struct rtdm_dev_context *, rtdm_user_info_t *, + unsigned int, void __user *); + +/****************************************************************************/ + +int ec_rtdm_dev_init( + ec_rtdm_dev_t *rtdm_dev, + ec_master_t *master + ) +{ + int ret; + + rtdm_dev->master = master; + + rtdm_dev->dev = kzalloc(sizeof(struct rtdm_device), GFP_KERNEL); + if (!rtdm_dev->dev) { + EC_MASTER_ERR(master, "Failed to reserve memory for RTDM device.\n"); + return -ENOMEM; + } + + rtdm_dev->dev->struct_version = RTDM_DEVICE_STRUCT_VER; + rtdm_dev->dev->device_flags = RTDM_NAMED_DEVICE; + rtdm_dev->dev->context_size = sizeof(ec_rtdm_context_t); + snprintf(rtdm_dev->dev->device_name, RTDM_MAX_DEVNAME_LEN, + "EtherCAT%u", master->index); + rtdm_dev->dev->open_nrt = ec_rtdm_open; + rtdm_dev->dev->ops.close_nrt = ec_rtdm_close; + rtdm_dev->dev->ops.ioctl_rt = ec_rtdm_ioctl; + rtdm_dev->dev->ops.ioctl_nrt = ec_rtdm_ioctl; + rtdm_dev->dev->device_class = RTDM_CLASS_EXPERIMENTAL; + rtdm_dev->dev->device_sub_class = 222; + rtdm_dev->dev->driver_name = "EtherCAT"; + rtdm_dev->dev->driver_version = RTDM_DRIVER_VER(1, 0, 2); + rtdm_dev->dev->peripheral_name = rtdm_dev->dev->device_name; + rtdm_dev->dev->provider_name = "EtherLab Community"; + rtdm_dev->dev->proc_name = rtdm_dev->dev->device_name; + rtdm_dev->dev->device_data = rtdm_dev; /* pointer to parent */ + + EC_MASTER_INFO(master, "Registering RTDM device %s.\n", + rtdm_dev->dev->driver_name); + ret = rtdm_dev_register(rtdm_dev->dev); + if (ret) { + EC_MASTER_ERR(master, "Initialization of RTDM interface failed" + " (return value %i).\n", ret); + kfree(rtdm_dev->dev); + } + + return ret; +} + +/****************************************************************************/ + +void ec_rtdm_dev_clear( + ec_rtdm_dev_t *rtdm_dev + ) +{ + int ret; + + EC_MASTER_INFO(rtdm_dev->master, "Unregistering RTDM device %s.\n", + rtdm_dev->dev->driver_name); + ret = rtdm_dev_unregister(rtdm_dev->dev, 1000 /* poll delay [ms] */); + if (ret < 0) { + EC_MASTER_WARN(rtdm_dev->master, + "Failed to unregister RTDM device (code %i).\n", ret); + } + + kfree(rtdm_dev->dev); +} + +/****************************************************************************/ + +/** Driver open. + */ +int ec_rtdm_open( + struct rtdm_dev_context *context, + rtdm_user_info_t *user_info, + int oflags + ) +{ + ec_rtdm_context_t *ctx = (ec_rtdm_context_t *) context->dev_private; +#if DEBUG + ec_rtdm_dev_t *rtdm_dev = (ec_rtdm_dev_t *) context->device->device_data; +#endif + + ctx->user_info = user_info; + ctx->ioctl_ctx.writable = oflags & O_WRONLY || oflags & O_RDWR; + ctx->ioctl_ctx.requested = 0; + ctx->ioctl_ctx.process_data = NULL; + ctx->ioctl_ctx.process_data_size = 0; + +#if DEBUG + EC_MASTER_INFO(rtdm_dev->master, "RTDM device %s opened.\n", + context->device->device_name); +#endif + return 0; +} + +/****************************************************************************/ + +/** Driver close. + */ +int ec_rtdm_close(struct rtdm_dev_context *context, + rtdm_user_info_t *user_info) +{ + ec_rtdm_context_t *ctx = (ec_rtdm_context_t *) context->dev_private; + ec_rtdm_dev_t *rtdm_dev = (ec_rtdm_dev_t *) context->device->device_data; + + if (ctx->ioctl_ctx.requested) { + ecrt_release_master(rtdm_dev->master); + } + +#if DEBUG + EC_MASTER_INFO(rtdm_dev->master, "RTDM device %s closed.\n", + context->device->device_name); +#endif + return 0; +} + +/****************************************************************************/ + +/** Driver ioctl. + */ +int ec_rtdm_ioctl( + struct rtdm_dev_context *context, /**< Context. */ + rtdm_user_info_t *user_info, /**< User data. */ + unsigned int request, /**< Request. */ + void __user *arg /**< Argument. */ + ) +{ + ec_rtdm_context_t *ctx = (ec_rtdm_context_t *) context->dev_private; + ec_rtdm_dev_t *rtdm_dev = (ec_rtdm_dev_t *) context->device->device_data; + +#if DEBUG + EC_MASTER_INFO(rtdm_dev->master, "ioctl(request = %u, ctl = %02x)" + " on RTDM device %s.\n", request, _IOC_NR(request), + context->device->device_name); +#endif + return ec_ioctl_rtdm(rtdm_dev->master, &ctx->ioctl_ctx, request, arg); +} + +/****************************************************************************/ + +/** Memory-map process data to user space. + */ +int ec_rtdm_mmap(ec_ioctl_context_t *ioctl_ctx, void **user_address) +{ + ec_rtdm_context_t *ctx = + container_of(ioctl_ctx, ec_rtdm_context_t, ioctl_ctx); + int ret; + + ret = rtdm_mmap_to_user(ctx->user_info, + ioctl_ctx->process_data, ioctl_ctx->process_data_size, + PROT_READ | PROT_WRITE, + user_address, + NULL, NULL); + if (ret < 0) { + return ret; + } + + return 0; +} + +/****************************************************************************/ diff -r f4313f5aba88 -r 3bdd7a747fae master/rtdm.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/master/rtdm.h Thu Sep 20 15:28:25 2012 +0200 @@ -0,0 +1,50 @@ +/***************************************************************************** + * + * $Id$ + * + * Copyright (C) 2012 Florian Pose + * + * This file is part of the IgH EtherCAT master. + * + * The IgH EtherCAT master 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; version 2 of the License. + * + * The IgH EtherCAT master 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 General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with the IgH EtherCAT master. 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. + * + ****************************************************************************/ + +#ifndef __EC_RTDM_H__ +#define __EC_RTDM_H__ + +#include "../include/ecrt.h" /* ec_master_t */ + +/*****************************************************************************/ + +struct rtdm_device; + +/*****************************************************************************/ + +typedef struct ec_rtdm_dev { + ec_master_t *master; + struct rtdm_device *dev; +} ec_rtdm_dev_t; + +/*****************************************************************************/ + +int ec_rtdm_dev_init(ec_rtdm_dev_t *, ec_master_t *); +void ec_rtdm_dev_clear(ec_rtdm_dev_t *); + +/****************************************************************************/ + +#endif