Implemented most realtime interface changes for version 1.4, improved

ec_pdo_t and ec_pdo_entry_t classes, added fmmu_config_t, adjusted
minimal example, etc...
This commit is contained in:
Florian Pose 2008-02-19 08:22:20 +00:00
parent a1ce00effe
commit 6189092aed
32 changed files with 2358 additions and 1658 deletions

34
NEWS
View File

@ -6,6 +6,40 @@ $Id$
Changes in version 1.4.0:
* Realtime interface changes:
- Replaced ec_slave_t with ec_slave_config_t, separating the slave objects
from the requested bus configuration. Therefore, renamed
ecrt_master_get_slave() to ecrt_master_slave_config().
- Replaced slave address string with alias and position values. See
ecrt_master_slave_config().
- Removed ecrt_master_get_slave_by_pos(), because it is no longer
necessary (alias/position, slave configurations).
- Added ec_slave_config_state_t for the new method
ecrt_slave_config_state().
- Process data memory for a domain can now be allocated externally. This
offers the possibility to use a shared-memory-region. Therefore,
added the domain methods ecrt_domain_size() and ecrt_domain_memory().
- Replaced the process data pointers in the Pdo entry registration
functions with a process data offset, that is now returned by
ecrt_domain_reg_pdo_entry(). This was necessary for the external
domain memory. An additional advantage is, that the returned offset value
is directly usable.
- Replaced ecrt_slave_pdo_mapping/add/clear() with
ecrt_slave_config_mapping() that is now able to specify Pdo mapping and
Pdo configuration. Pdo entries mapped in this way can now immediately be
registered. The Pdo mapping and the configuration are described with the
new data types ec_pdo_info_t and ec_pdo_entry_info_t.
- Renamed ec_bus_status_t, ec_master_status_t to ec_bus_state_t and
ec_master_state_t, respectively. Renamed ecrt_master_get_status() to
ecrt_master_state(), for consistency reasons.
- Added ec_domain_state_t and ec_wc_state_t for a new output parameter
of ecrt_domain_state().
- Former "Pdo registration" meant Pdo entry registration in fact, therefore
renamed ec_pdo_reg_t to ec_pdo_entry_reg_t and ecrt_domain_register_pdo()
to ecrt_domain_reg_pdo_entry().
- Removed ecrt_domain_register_pdo_range(), because it's functionality can
be reached by specifying an explicit Pdo mapping and registering those
Pdo entries.
* Added 8139too driver for kernel 2.6.22, thanks to Erwin Burgstaller.
* Added e1000 driver for kernel 2.6.22.
* Allow gaps in PDO mapping read from CoE.

14
TODO
View File

@ -11,9 +11,7 @@ Version 1.4.0:
* Realtime interface changes:
- PDO configuration.
- SDO access.
- Replace process data pointers with offset.
- External memory for domains.
- Separate slave configuration from slave structure.
* Mailbox handler
* Read PDO mapping via CoE during bus scan.
* SDO write access in sysfs.
@ -22,6 +20,11 @@ Version 1.4.0:
* Remove ecdb.h and let lsec output PDO information 'cut-and-pastable' for
applications.
* Update documentation.
* READMEs for examples.
* Calculate expected working counter for domains.
* Distributed clocks.
* Support slaves, that don't support the LRW datagram, only LRD/LWR.
* Implement all EtherCAT datagram types.
Future issues:
@ -30,10 +33,7 @@ Future issues:
to replace lsec.
- Step 2: Move kernel threads to user space daemon with a TCP interface
replacing the cdev.
* Distributed clocks.
* Redundancy with 2 network adapters.
* Support slaves, that don't support the LRW datagram, only LRD/LWR.
* PDO reading in IDLE mode.
* Interface/buffers for asynchronous domain IO.
Smaller issues:
@ -42,12 +42,9 @@ Smaller issues:
* Clear sync managers in INIT.
* Simplify FSMs with <state>_enter() functions.
* Read out CRC counters.
* Calculate expected working counter for domains.
* Optimize alignment of process data.
* Evaluate EEPROM contents after writing.
* Configure slave ports to automatically open on link detection.
* Calculate cycle time of operation state machine.
* Locking for serveral slave variables for sysfs access.
* Interrupt master state machines state scan for other jobs.
* Master state machine, slave configuration: Do not check every slave on
a cycle.
@ -55,7 +52,6 @@ Smaller issues:
Less important issues:
* Implement all EtherCAT datagram types.
* File access over EtherCAT (FoE).
* Allow VLAN tagging.
* Determine number of frames, the NIC can handle.

View File

@ -8,6 +8,7 @@ digraph slaveconf {
enter_sdoconf [shape=point,label=""]
enter_mapconf [shape=point,label=""]
enter_pdo_sync [shape=point,label=""]
enter_saveop [shape=point,label=""]
start -> init [weight=10]
init -> init
@ -18,8 +19,8 @@ digraph slaveconf {
clear_fmmus -> error
clear_fmmus -> enter_mbox_sync [weight=10]
enter_mbox_sync -> end [label="INIT req."]
enter_mbox_sync -> preop
enter_mbox_sync -> mbox_sync [label="mailbox SMs", weight=10]
enter_mbox_sync -> preop [label="No mailboxes"]
enter_mbox_sync -> mbox_sync [label="Has mailboxes", weight=10]
mbox_sync -> mbox_sync
mbox_sync -> error
mbox_sync -> preop [weight=10]
@ -27,8 +28,9 @@ digraph slaveconf {
preop -> error
preop -> end [label="PREOP req."]
preop -> enter_sdoconf [weight=10]
enter_sdoconf -> enter_mapconf
enter_sdoconf -> sdoconf [label="SDOs to configure", weight=10]
enter_sdoconf -> enter_saveop [label="No configuration"]
enter_sdoconf -> enter_mapconf [label="No SDO configs"]
enter_sdoconf -> sdoconf [label="SDO configs", weight=10]
sdoconf -> sdoconf
sdoconf -> error
sdoconf -> enter_mapconf [weight=10]
@ -42,11 +44,12 @@ digraph slaveconf {
pdo_sync -> pdo_sync
pdo_sync -> error
pdo_sync -> enter_fmmu [weight=10]
enter_fmmu -> saveop
enter_fmmu -> enter_saveop
enter_fmmu -> fmmu [label="FMMUs to configure", weight=10]
fmmu -> fmmu
fmmu -> error
fmmu -> saveop [weight=10]
fmmu -> enter_saveop [weight=10]
enter_saveop -> saveop
saveop -> saveop
saveop -> error
saveop -> end [label="SAVEOP req."]

View File

@ -44,6 +44,8 @@
#define FREQUENCY 100
//#define KBUS
#define PDOS
#define MAPPING
/*****************************************************************************/
@ -53,7 +55,7 @@ static struct timer_list timer;
static ec_master_t *master = NULL;
static ec_domain_t *domain1 = NULL;
spinlock_t master_lock = SPIN_LOCK_UNLOCKED;
static ec_master_status_t master_status, old_status = {};
static ec_master_state_t master_state, old_state = {};
// data fields
#ifdef KBUS
@ -61,17 +63,20 @@ static void *r_inputs;
static void *r_outputs;
#endif
static void *r_dig_out;
static void *r_ana_out;
static void *r_count;
static void *r_freq;
#ifdef MAPPING
const ec_pdo_info_t mapping[] = {
{EC_DIR_INPUT, 0x1A00}, // channel 1
{EC_DIR_INPUT, 0x1A01} // channel 2
};
#endif
#if 1
const static ec_pdo_reg_t domain1_pdo_regs[] = {
{"2", Beckhoff_EL2004_Outputs, &r_dig_out},
{"3", Beckhoff_EL4132_Output1, &r_ana_out},
{"#888:1", Beckhoff_EL5101_Value, &r_count},
{"4", Beckhoff_EL5101_Frequency, &r_freq},
#ifdef PDOS
static uint8_t off_ana_in;
static uint8_t off_ana_out;
const static ec_pdo_entry_reg_t domain1_regs[] = {
{0, 1, Beckhoff_EL3162, 0x3101, 2, &off_ana_in},
{0, 2, Beckhoff_EL4102, 0x3001, 1, &off_ana_out},
{}
};
#endif
@ -91,7 +96,7 @@ void run(unsigned long data)
// process data
// k_pos = EC_READ_U32(r_ssi);
EC_WRITE_U8(r_dig_out, blink ? 0x0F : 0x00);
//EC_WRITE_U8(r_dig_out, blink ? 0x0F : 0x00);
if (counter) {
counter--;
@ -101,24 +106,24 @@ void run(unsigned long data)
blink = !blink;
spin_lock(&master_lock);
ecrt_master_get_status(master, &master_status);
ecrt_master_state(master, &master_state);
spin_unlock(&master_lock);
if (master_status.bus_status != old_status.bus_status) {
printk(KERN_INFO PFX "bus status changed to %i.\n",
master_status.bus_status);
if (master_state.bus_state != old_state.bus_state) {
printk(KERN_INFO PFX "bus state changed to %i.\n",
master_state.bus_state);
}
if (master_status.bus_tainted != old_status.bus_tainted) {
if (master_state.bus_tainted != old_state.bus_tainted) {
printk(KERN_INFO PFX "tainted flag changed to %u.\n",
master_status.bus_tainted);
master_state.bus_tainted);
}
if (master_status.slaves_responding !=
old_status.slaves_responding) {
if (master_state.slaves_responding !=
old_state.slaves_responding) {
printk(KERN_INFO PFX "slaves_responding changed to %u.\n",
master_status.slaves_responding);
master_state.slaves_responding);
}
old_status = master_status;
old_state = master_state;
}
#ifdef KBUS
@ -158,10 +163,10 @@ void release_lock(void *data)
int __init init_mini_module(void)
{
#if 1
ec_slave_t *slave;
#ifdef MAPPING
ec_slave_config_t *sc;
#endif
printk(KERN_INFO PFX "Starting...\n");
if (!(master = ecrt_request_master(0))) {
@ -177,49 +182,25 @@ int __init init_mini_module(void)
goto out_release_master;
}
#if 1
printk(KERN_INFO PFX "Configuring alternative PDO mapping...\n");
if (!(slave = ecrt_master_get_slave(master, "4", Beckhoff_EL5101)))
#ifdef MAPPING
printk(KERN_INFO PFX "Configuring Pdo mapping...\n");
if (!(sc = ecrt_master_slave_config(master, 0, 1, Beckhoff_EL3162))) {
printk(KERN_ERR PFX "Failed to get slave configuration.\n");
goto out_release_master;
}
if (ecrt_slave_pdo_mapping(slave, EC_DIR_INPUT, 2, 0x1A00, 0x1A02))
goto out_release_master;
#endif
printk(KERN_INFO PFX "Registering PDOs...\n");
#if 1
if (ecrt_domain_register_pdo_list(domain1, domain1_pdo_regs)) {
printk(KERN_ERR PFX "PDO registration failed!\n");
if (ecrt_slave_config_mapping(sc, 2, mapping)) {
printk(KERN_ERR PFX "Failed to configure Pdo mapping.\n");
goto out_release_master;
}
#endif
#ifdef KBUS
if (!(slave = ecrt_master_get_slave(master, "0", Beckhoff_BK1120)))
goto out_release_master;
if (!ecrt_domain_register_pdo_range(
domain1, slave, EC_DIR_OUTPUT, 0, 4, &r_outputs)) {
printk(KERN_ERR PFX "PDO registration failed!\n");
printk(KERN_INFO PFX "Registering PDO entries...\n");
#ifdef PDOS
if (ecrt_domain_reg_pdo_entry_list(domain1, domain1_regs)) {
printk(KERN_ERR PFX "PDO entry registration failed!\n");
goto out_release_master;
}
if (!ecrt_domain_register_pdo_range(
domain1, slave, EC_DIR_INPUT, 0, 4, &r_inputs)) {
printk(KERN_ERR PFX "PDO registration failed!\n");
goto out_release_master;
}
#endif
#if 0
if (!(slave = ecrt_master_get_slave(master, "4", Beckhoff_EL5001)))
goto out_release_master;
if (ecrt_slave_conf_sdo8(slave, 0x4061, 1, 0))
goto out_release_master;
#endif
#if 1
#endif
printk(KERN_INFO PFX "Activating master...\n");

View File

@ -41,6 +41,44 @@
* realtime modules that want to use EtherCAT. There are functions to request
* a master, to map process data, to communicate with slaves via CoE and to
* configure and activate the bus.
*
* Changes in Version 1.4:
*
* - Replaced ec_slave_t with ec_slave_config_t, separating the slave objects
* from the requested bus configuration. Therefore, renamed
* ecrt_master_get_slave() to ecrt_master_slave_config().
* - Replaced slave address string with alias and position values. See
* ecrt_master_slave_config().
* - Removed ecrt_master_get_slave_by_pos(), because it is no longer
* necessary (alias/position, slave configurations).
* - Added ec_slave_config_state_t for the new method
* ecrt_slave_config_state().
* - Process data memory for a domain can now be allocated externally. This
* offers the possibility to use a shared-memory-region. Therefore,
* added the domain methods ecrt_domain_size() and ecrt_domain_memory().
* - Replaced the process data pointers in the Pdo entry registration
* functions with a process data offset, that is now returned by
* ecrt_domain_reg_pdo_entry(). This was necessary for the external
* domain memory. An additional advantage is, that the returned offset value
* is directly usable.
* - Replaced ecrt_slave_pdo_mapping/add/clear() with
* ecrt_slave_config_mapping() that is now able to specify Pdo mapping and
* Pdo configuration. Pdo entries mapped in this way can now immediately be
* registered. The Pdo mapping and the configuration are described with the
* new data types ec_pdo_info_t and ec_pdo_entry_info_t.
* - Renamed ec_bus_status_t, ec_master_status_t to ec_bus_state_t and
* ec_master_state_t, respectively. Renamed ecrt_master_get_status() to
* ecrt_master_state(), for consistency reasons.
* - Added ec_domain_state_t and ec_wc_state_t for a new output parameter
* of ecrt_domain_state().
* - Former "Pdo registration" meant Pdo entry registration in fact, therefore
* renamed ec_pdo_reg_t to ec_pdo_entry_reg_t and ecrt_domain_register_pdo()
* to ecrt_domain_reg_pdo_entry().
* - Removed ecrt_domain_register_pdo_range(), because it's functionality can
* be reached by specifying an explicit Pdo mapping and registering those
* Pdo entries.
*
* @{
*/
/*****************************************************************************/
@ -83,127 +121,422 @@
struct ec_master;
typedef struct ec_master ec_master_t; /**< \see ec_master */
struct ec_slave_config;
typedef struct ec_slave_config ec_slave_config_t; /**< \see ec_slave_config */
struct ec_domain;
typedef struct ec_domain ec_domain_t; /**< \see ec_domain */
struct ec_slave;
typedef struct ec_slave ec_slave_t; /**< \see ec_slave */
/*****************************************************************************/
/** Bus status.
* This is used in ec_master_status_t.
/** Bus state.
*
* This is used in ec_master_state_t.
*/
typedef enum {
EC_BUS_FAILURE = -1, /**< At least one slave with process data exchange
is offline. */
EC_BUS_OK /**< All slaves with process data exchange are
online. */
}
ec_bus_status_t;
EC_BUS_FAILURE = -1, /**< At least one configured slave is offline. */
EC_BUS_OK /**< All configured slaves are online. */
} ec_bus_state_t;
/*****************************************************************************/
/** Master status.
* This is used for the output parameter of ecrt_master_get_status().
/** Master state.
*
* This is used for the output parameter of ecrt_master_state().
*/
typedef struct {
ec_bus_status_t bus_status; /**< \see ec_bus_status_t */
unsigned int bus_tainted; /**< non-zero, if the bus topology is invalid */
unsigned int slaves_responding; /**< number of responding slaves */
}
ec_master_status_t;
ec_bus_state_t bus_state; /**< \see ec_bus_state_t */
unsigned int bus_tainted; /**< Non-zero, if the bus topology differs from
the requested configuration. */
unsigned int slaves_responding; /**< Number of slaves in the bus. */
} ec_master_state_t;
/*****************************************************************************/
/** List entry for domain PDO registrations.
* This type is used as a parameter for the ecrt_domain_register_pdo_list()
* convenience function.
/** Slave configuration state.
*
* \see ecrt_slave_config_state().
*/
typedef struct {
const char *slave_address; /**< slave address string
\see ec_master_parse_slave_address() */
uint32_t vendor_id; /**< vendor ID */
uint32_t product_code; /**< product code */
uint16_t pdo_entry_index; /**< PDO entry index */
uint8_t pdo_entry_subindex; /**< PDO entry subindex */
void **data_ptr; /**< address of the process data pointer */
}
ec_pdo_reg_t;
typedef struct {
unsigned int online : 1; /**< The slave is online. */
unsigned int configured : 1; /**< The slave was configured according to
the specified configuration. */
} ec_slave_config_state_t;
/*****************************************************************************/
/** Direction type for PDO mapping and range registration functions.
/** Domain working counter interpretation.
*
* This is used in ec_domain_state_t.
*/
typedef enum {
EC_DIR_OUTPUT, /**< values written by master */
EC_DIR_INPUT /**< values read by master */
}
ec_direction_t;
EC_WC_ZERO = 0, /**< No Pdos were exchanged. */
EC_WC_INCOMPLETE, /**< Some of the registered Pdos were exchanged. */
EC_WC_COMPLETE /**< All registered Pdos were exchanged. */
} ec_wc_state_t;
/*****************************************************************************/
/** Domain state.
*
* This is used for the output parameter of ecrt_domain_state().
*/
typedef struct {
unsigned int working_counter; /**< Value of the last working counter. */
ec_wc_state_t wc_state; /**< Working counter interpretation. */
} ec_domain_state_t;
/*****************************************************************************/
/** Direction type for Pdo mapping functions.
*/
typedef enum {
EC_DIR_OUTPUT, /**< Values written by the master. */
EC_DIR_INPUT /**< Values read by the master. */
} ec_direction_t;
/*****************************************************************************/
/** Pdo entry mapping.
*
* \see ecrt_slave_config_mapping().
*/
typedef struct {
uint16_t index; /**< Index of the Pdo entry to add to the Pdo
configuration. */
uint8_t subindex; /**< Subindex of the Pdo entry to add to the
Pdo configuration. */
uint8_t bit_length; /**< Size of the Pdo entry in bit. */
} ec_pdo_entry_info_t;
/*****************************************************************************/
/** Pdo information.
*
* \see ecrt_slave_config_mapping().
*/
typedef struct {
ec_direction_t dir; /**< Pdo direction (input/output). */
uint16_t index; /**< Index of the Pdo to map. */
unsigned int n_entries; /**< Number of Pdo entries for the Pdo
configuration. Zero means, that the default Pdo
configuration shall be used. */
const ec_pdo_entry_info_t entries[]; /**< Pdo configuration list. */
} ec_pdo_info_t;
/*****************************************************************************/
/** List record type for Pdo entry mass-registration.
*
* This type is used for the array parameter of the
* ecrt_domain_reg_pdo_entry_list() convenience function.
*/
typedef struct {
uint16_t alias; /**< Slave alias address. */
uint16_t position; /**< Slave position. */
uint32_t vendor_id; /**< Slave vendor ID. */
uint32_t product_code; /**< Slave product code. */
uint16_t index; /**< Pdo entry index. */
uint8_t subindex; /**< Pdo entry subindex. */
uint8_t *offset; /**< Pointer to a variable to store the Pdo's
offset in the process data. */
} ec_pdo_entry_reg_t;
/******************************************************************************
* Global functions
*****************************************************************************/
ec_master_t *ecrt_request_master(unsigned int master_index);
void ecrt_release_master(ec_master_t *master);
/** Returns the version magic of the realtime interface.
*
* \return Value of ECRT_VERSION_MAGIC() at EtherCAT master compile time.
*/
unsigned int ecrt_version_magic(void);
/** Requests an EtherCAT master for realtime operation.
*
* \return pointer to reserved master, or NULL on error
*/
ec_master_t *ecrt_request_master(
unsigned int master_index /**< Index of the master to request. */
);
/** Releases a requested EtherCAT master.
*/
void ecrt_release_master(
ec_master_t *master /**< EtherCAT master */
);
/******************************************************************************
* Master methods
*****************************************************************************/
void ecrt_master_callbacks(ec_master_t *master, int (*request_cb)(void *),
void (*release_cb)(void *), void *cb_data);
/** Sets the locking callbacks.
*
* The request_cb function must return zero, to allow another instance
* (the EoE process for example) to access the master. Non-zero means,
* that access is forbidden at this time.
*/
void ecrt_master_callbacks(
ec_master_t *master, /**< EtherCAT master */
int (*request_cb)(void *), /**< Lock request function. */
void (*release_cb)(void *), /**< Lock release function. */
void *cb_data /**< Arbitrary user data. */
);
ec_domain_t *ecrt_master_create_domain(ec_master_t *master);
/** Creates a new domain.
*
* \return Pointer to the new domain on success, else NULL.
*/
ec_domain_t *ecrt_master_create_domain(
ec_master_t *master /**< EtherCAT master. */
);
ec_slave_t *ecrt_master_get_slave(const ec_master_t *master,
const char *address, uint32_t vendor_id, uint32_t product_code);
ec_slave_t *ecrt_master_get_slave_by_pos(const ec_master_t *master,
uint16_t position, uint32_t vendor_id, uint32_t product_code);
/** Obtains a slave configuration.
*
* Creates a slave configuration object for the given \a alias and \a position
* tuple and returns it. If a configuration with the same \a alias and \a
* position already exists, it will be re-used. In the latter case, the given
* vendor ID and product code are compared to the stored ones. On mismatch, an
* error message is raised and the function returns \a NULL.
*
* Slaves are addressed with the \a alias and \a position parameters.
* - If \a alias is zero, \a position is interpreted as the desired slave's
* ring position.
* - If \a alias is non-zero, it matches a slave with the given alias. In this
* case, \a position is interpreted as ring offset, starting from the
* aliased slave, so a position of zero means the aliased slave itself and a
* positive value matches the n-th slave behind the aliased one.
*
* If the slave with the given address is found during the bus configuration,
* its vendor ID and product code are matched against the given value. On
* mismatch, the slave is not configured and an error message is raised.
*
* If different slave configurations are pointing to the same slave during bus
* configuration, a warning is raised and only the first configuration is
* applied.
*
* \retval >0 Pointer to the slave configuration structure.
* \retval NULL in the error case.
*/
ec_slave_config_t *ecrt_master_slave_config(
ec_master_t *master, /**< EtherCAT master */
uint16_t alias, /**< Slave alias. */
uint16_t position, /**< Slave position. */
uint32_t vendor_id, /**< Expected vendor ID. */
uint32_t product_code /**< Expected product code. */
);
int ecrt_master_activate(ec_master_t *master);
/** Applies the bus configuration and switches to realtime mode.
*
* Does the complete configuration and activation for all slaves. Sets sync
* managers and FMMUs, and does the appropriate transitions, until the slave
* is operational.
*
* \return 0 in case of success, else < 0
*/
int ecrt_master_activate(
ec_master_t *master /**< EtherCAT master. */
);
void ecrt_master_send(ec_master_t *master);
void ecrt_master_receive(ec_master_t *master);
/** Sends all datagrams in the queue.
*
* \todo doc
*/
void ecrt_master_send(
ec_master_t *master /**< EtherCAT master. */
);
void ecrt_master_get_status(const ec_master_t *master, ec_master_status_t *s);
/** Fetches received frames from the hardware and processes the datagrams.
*/
void ecrt_master_receive(
ec_master_t *master /**< EtherCAT master. */
);
/** Reads the current master state.
*
* Stores the master state information in the given \a state structure.
*/
void ecrt_master_state(
const ec_master_t *master, /**< EtherCAT master. */
ec_master_state_t *state /**< Structure to store the information. */
);
/******************************************************************************
* Slave configuration methods
*****************************************************************************/
/** Specify the Pdo mapping and (optionally) the Pdo configuration.
*
* The following example shows, how to specify a complete Pdo mapping
* including the Pdo configuration. With this information, the master is able
* to reserve the complete process data, even if the slave is not present
* at configuration time:
*
* \code
* const ec_pdo_info_t complete_mapping[] = {
* {EC_DIR_INPUT, 0x1600, 2, { // channel 1
* {0x7000, 0, 16}, // value
* {0x7000, 1, 8}, // status
* }},
* {EC_DIR_INPUT, 0x1601, 2, { // channel 2
* {0x7001, 0, 16}, // value
* {0x7001, 1, 8}, // status
* }}
* };
*
* if (ecrt_slave_config_mapping(slave_config_ana_in, 2, complete_mapping)) {
* // error
* }
* \endcode
*
* The next example shows, how to configure only the Pdo mapping. The entries
* for each mapped Pdo are taken from the default Pdo configuration. Please
* note, that Pdo entry registration will fail, if no Pdo configuration is
* specified and the slave is offline.
*
* \code
* const ec_pdo_info_t pdo_mapping[] = {
* {EC_DIR_INPUT, 0x1600}, // Channel 1
* {EC_DIR_INPUT, 0x1601} // Channel 2
* };
*
* if (ecrt_slave_config_mapping(slave_config_ana_in, 2, pdo_mapping)) {
* // error
* }
* \endcode
*
* \return zero on success, else non-zero
*/
int ecrt_slave_config_mapping(
ec_slave_config_t *sc, /**< Slave configuration. */
unsigned int n_entries, /**< Number of Pdos in \a pdos to map. */
const ec_pdo_info_t pdos[] /**< List with Pdo mapping. */
);
/** Add a configuration value for an 8-bit SDO.
*
* \todo doc
* \return 0 in case of success, else < 0
*/
int ecrt_slave_config_sdo8(
ec_slave_config_t *sc, /**< Slave configuration */
uint16_t sdo_index, /**< Index of the SDO to configure. */
uint8_t sdo_subindex, /**< Subindex of the SDO to configure. */
uint8_t value /**< Value to set. */
);
/** Add a configuration value for a 16-bit SDO.
*
* \todo doc
* \return 0 in case of success, else < 0
*/
int ecrt_slave_config_sdo16(
ec_slave_config_t *sc, /**< Slave configuration */
uint16_t sdo_index, /**< Index of the SDO to configure. */
uint8_t sdo_subindex, /**< Subindex of the SDO to configure. */
uint16_t value /**< Value to set. */
);
/** Add a configuration value for a 32-bit SDO.
*
* \todo doc
* \return 0 in case of success, else < 0
*/
int ecrt_slave_config_sdo32(
ec_slave_config_t *sc, /**< Slave configuration */
uint16_t sdo_index, /**< Index of the SDO to configure. */
uint8_t sdo_subindex, /**< Subindex of the SDO to configure. */
uint32_t value /**< Value to set. */
);
/** Outputs the state of the slave configuration.
*
* Stores the state information in the given \a state structure.
*/
void ecrt_slave_config_state(
const ec_slave_config_t *sc, /**< Slave configuration */
ec_slave_config_state_t *state /**< State object to write to. */
);
/******************************************************************************
* Domain methods
*****************************************************************************/
int ecrt_domain_register_pdo(ec_domain_t *domain, ec_slave_t *slave,
uint16_t pdo_entry_index, uint8_t pdo_entry_subindex, void **data_ptr);
/** Registers a single Pdo entry for a domain.
*
* \return On success, the function returns the offset in the domain's process
* data, which can be zero or greater. On failure, it returns a value
* less than zero.
*/
int ecrt_domain_register_pdo_range(ec_domain_t *domain, ec_slave_t *slave,
ec_direction_t direction, uint16_t offset, uint16_t length,
void **data_ptr);
int ecrt_domain_reg_pdo_entry(
ec_domain_t *domain, /**< Domain. */
ec_slave_config_t *sc, /**< Slave configuration. */
uint16_t entry_index, /**< Index of the Pdo entry to register. */
uint8_t entry_subindex /**< Subindex of the Pdo entry to register. */
);
int ecrt_domain_register_pdo_list(ec_domain_t *domain,
const ec_pdo_reg_t *pdos);
/** Registers a bunch of Pdo entries for a domain.
*
* \todo doc
* \attention The registration array has to be terminated with an empty
* structure, or one with the \a index field set to zero!
* \return 0 on success, else non-zero.
*/
int ecrt_domain_reg_pdo_entry_list(
ec_domain_t *domain, /**< Domain. */
const ec_pdo_entry_reg_t *pdo_entry_regs /**< Array of Pdo
registrations. */
);
void ecrt_domain_process(ec_domain_t *domain);
void ecrt_domain_queue(ec_domain_t *domain);
/** Returns the current size of the domain's process data.
*
* \return Size of the process data image.
*/
size_t ecrt_domain_size(
ec_domain_t *domain /**< Domain. */
);
int ecrt_domain_state(const ec_domain_t *domain);
/** Provide memory to store the domain's process data.
*
* Call this after all Pdo entries have been registered. Since interface
* version 1.4, you'll have to provide an external memory for the domain
* process data.
*
* The size of the allocated memory must be at least the return value of
* ecrt_domain_size(), after all Pdo entries have been registered.
*/
void ecrt_domain_memory(
ec_domain_t *domain, /**< Domain. */
uint8_t *memory /**< Address of the memory to store the process
data in. */
);
/******************************************************************************
* Slave methods
*****************************************************************************/
/** Processes received datagrams.
*
* \todo doc
*/
void ecrt_domain_process(
ec_domain_t *domain /**< Domain. */
);
int ecrt_slave_conf_sdo8(ec_slave_t *slave, uint16_t sdo_index,
uint8_t sdo_subindex, uint8_t value);
int ecrt_slave_conf_sdo16(ec_slave_t *slave, uint16_t sdo_index,
uint8_t sdo_subindex, uint16_t value);
int ecrt_slave_conf_sdo32(ec_slave_t *slave, uint16_t sdo_index,
uint8_t sdo_subindex, uint32_t value);
/** (Re-)queues all domain datagrams in the master's datagram queue.
*
* \todo doc
*/
void ecrt_domain_queue(
ec_domain_t *domain /**< Domain. */
);
void ecrt_slave_pdo_mapping_clear(ec_slave_t *, ec_direction_t);
int ecrt_slave_pdo_mapping_add(ec_slave_t *, ec_direction_t, uint16_t);
int ecrt_slave_pdo_mapping(ec_slave_t *, ec_direction_t, unsigned int, ...);
/** Reads the state of a domain.
*
* Stores the domain state in the giveb \a state structure.
*/
void ecrt_domain_state(
const ec_domain_t *domain, /**< Domain. */
ec_domain_state_t *state /**< Pointer to a state object to store the
information. */
);
/******************************************************************************
* Bitwise read/write macros
@ -324,4 +657,6 @@ int ecrt_slave_pdo_mapping(ec_slave_t *, ec_direction_t, unsigned int, ...);
/*****************************************************************************/
/** @} */
#endif

View File

@ -33,9 +33,10 @@
obj-m := ec_master.o
ec_master-objs := module.o master.o device.o pdo.o sync.o fmmu.o slave.o \
datagram.o domain.o mailbox.o canopen.o fsm_sii.o fsm_change.o \
fsm_coe.o fsm_coe_map.o fsm_mapping.o fsm_slave.o fsm_master.o xmldev.o
ec_master-objs := module.o master.o device.o pdo.o sync.o fmmu_config.o \
slave.o slave_config.o pdo_mapping.o datagram.o domain.o mailbox.o \
canopen.o fsm_sii.o fsm_change.o fsm_coe.o fsm_coe_map.o fsm_mapping.o \
fsm_slave.o fsm_master.o xmldev.o
ifeq (@ENABLE_EOE@,1)
ec_master-objs += ethernet.o

View File

@ -39,7 +39,7 @@ EXTRA_DIST = \
device.c device.h \
pdo.c pdo.h \
sync.c sync.h \
fmmu.c fmmu.h \
fmmu_config.c fmmu_config.h \
domain.c domain.h \
doxygen.c \
ethernet.c ethernet.h \
@ -55,6 +55,8 @@ EXTRA_DIST = \
master.c master.h \
module.c \
slave.c slave.h \
slave_config.c slave_config.h \
pdo_mapping.c pdo_mapping.h \
xmldev.c xmldev.h
modules:

View File

@ -41,29 +41,14 @@
#include <linux/module.h>
#include "globals.h"
#include "domain.h"
#include "master.h"
#include "slave_config.h"
/*****************************************************************************/
/**
Data registration type.
*/
typedef struct
{
struct list_head list; /**< list item */
ec_slave_t *slave; /**< slave */
const ec_sync_t *sync; /**< sync manager */
off_t sync_offset; /**< pdo offset */
void **data_ptr; /**< pointer to process data pointer(s) */
}
ec_data_reg_t;
#include "domain.h"
/*****************************************************************************/
void ec_domain_clear(struct kobject *);
void ec_domain_clear_data_regs(ec_domain_t *);
ssize_t ec_show_domain_attribute(struct kobject *, struct attribute *, char *);
/*****************************************************************************/
@ -92,25 +77,24 @@ static struct kobj_type ktype_ec_domain = {
/*****************************************************************************/
/**
Domain constructor.
\return 0 in case of success, else < 0
*/
int ec_domain_init(ec_domain_t *domain, /**< EtherCAT domain */
ec_master_t *master, /**< owning master */
unsigned int index /**< domain index */
)
/** Domain constructor.
*
* \return 0 in case of success, else < 0
*/
int ec_domain_init(
ec_domain_t *domain, /**< EtherCAT domain. */
ec_master_t *master, /**< Parent master. */
unsigned int index /**< Index. */
)
{
domain->master = master;
domain->index = index;
domain->data_size = 0;
domain->base_address = 0;
domain->response_count = 0xFFFFFFFF;
domain->working_counter = 0xFFFFFFFF;
domain->notify_jiffies = 0;
domain->working_counter_changes = 0;
INIT_LIST_HEAD(&domain->data_regs);
INIT_LIST_HEAD(&domain->datagrams);
// init kobject and add it to the hierarchy
@ -164,8 +148,8 @@ void ec_domain_destroy(ec_domain_t *domain /**< EtherCAT domain */)
void ec_domain_clear(struct kobject *kobj /**< kobject of the domain */)
{
ec_datagram_t *datagram, *next;
ec_domain_t *domain;
ec_datagram_t *datagram, *next;
domain = container_of(kobj, ec_domain_t, kobj);
@ -174,90 +158,20 @@ void ec_domain_clear(struct kobject *kobj /**< kobject of the domain */)
kfree(datagram);
}
ec_domain_clear_data_regs(domain);
kfree(domain);
}
/*****************************************************************************/
/**
* Registers a PDO entry.
/** Allocates a domain datagram and appends it to the list.
*
* \return 0 in case of success, else < 0
*/
int ec_domain_reg_pdo_entry(
ec_domain_t *domain, /**< EtherCAT domain */
ec_sync_t *sync, /**< sync manager */
const ec_pdo_entry_t *entry, /**< PDO entry to register */
void **data_ptr /**< pointer to the process data pointer */
int ec_domain_add_datagram(
ec_domain_t *domain, /**< EtherCAT domain. */
uint32_t offset, /**< Logical offset. */
size_t data_size /**< Size of the data. */
)
{
ec_data_reg_t *data_reg;
const ec_pdo_t *other_pdo;
const ec_pdo_entry_t *other_entry;
unsigned int bit_offset, byte_offset;
// Calculate offset (in sync manager) for process data pointer
bit_offset = 0;
list_for_each_entry(other_pdo, &sync->pdos, list) {
list_for_each_entry(other_entry, &other_pdo->entries, list) {
if (other_entry == entry)
goto out;
bit_offset += other_entry->bit_length;
}
}
out:
byte_offset = bit_offset / 8;
// Allocate memory for data registration object
if (!(data_reg =
(ec_data_reg_t *) kmalloc(sizeof(ec_data_reg_t), GFP_KERNEL))) {
EC_ERR("Failed to allocate data registration.\n");
return -1;
}
if (ec_slave_prepare_fmmu(sync->slave, domain, sync)) {
EC_ERR("FMMU configuration failed.\n");
kfree(data_reg);
return -1;
}
data_reg->slave = sync->slave;
data_reg->sync = sync;
data_reg->sync_offset = byte_offset;
data_reg->data_ptr = data_ptr;
list_add_tail(&data_reg->list, &domain->data_regs);
return 0;
}
/*****************************************************************************/
/**
Clears the list of the data registrations.
*/
void ec_domain_clear_data_regs(ec_domain_t *domain /**< EtherCAT domain */)
{
ec_data_reg_t *data_reg, *next;
list_for_each_entry_safe(data_reg, next, &domain->data_regs, list) {
list_del(&data_reg->list);
kfree(data_reg);
}
}
/*****************************************************************************/
/**
Allocates a process data datagram and appends it to the list.
\return 0 in case of success, else < 0
*/
int ec_domain_add_datagram(ec_domain_t *domain, /**< EtherCAT domain */
uint32_t offset, /**< logical offset */
size_t data_size /**< size of the datagram data */
)
{
ec_datagram_t *datagram;
@ -281,50 +195,47 @@ int ec_domain_add_datagram(ec_domain_t *domain, /**< EtherCAT domain */
/*****************************************************************************/
/**
Creates a domain.
Reserves domain memory, calculates the logical addresses of the
corresponding FMMUs and sets the process data pointer of the registered
process data.
\return 0 in case of success, else < 0
*/
int ec_domain_alloc(ec_domain_t *domain, /**< EtherCAT domain */
uint32_t base_address /**< logical base address */
)
/** Finishes a domain.
*
* This allocates the necessary datagrams and writes the correct logical
* addresses to every configured FMMU.
*
* \retval 0 in case of success
* \retval <0 on failure.
*/
int ec_domain_finish(
ec_domain_t *domain, /**< EtherCAT domain. */
uint32_t base_address /**< Logical base address. */
)
{
ec_data_reg_t *data_reg;
ec_slave_t *slave;
ec_fmmu_t *fmmu;
unsigned int i, j, datagram_count;
uint32_t pdo_off, pdo_off_datagram;
uint32_t datagram_offset, log_addr;
size_t datagram_data_size, sync_size;
ec_datagram_t *datagram;
uint32_t datagram_offset;
size_t datagram_data_size;
unsigned int datagram_count, i;
ec_slave_config_t *sc;
ec_fmmu_config_t *fmmu;
domain->base_address = base_address;
// calculate size of process data and allocate memory
domain->data_size = 0;
// Cycle through all domain FMMUS, add the logical base address and assign
// as many PDO entries as possible to the datagrams.
datagram_offset = base_address;
datagram_data_size = 0;
datagram_count = 0;
list_for_each_entry(slave, &domain->master->slaves, list) {
for (j = 0; j < slave->fmmu_count; j++) {
fmmu = &slave->fmmus[j];
if (fmmu->domain == domain) {
fmmu->logical_start_address = base_address + domain->data_size;
sync_size = ec_sync_size(fmmu->sync);
domain->data_size += sync_size;
if (datagram_data_size + sync_size > EC_MAX_DATA_SIZE) {
if (ec_domain_add_datagram(domain, datagram_offset,
datagram_data_size)) return -1;
datagram_offset += datagram_data_size;
datagram_data_size = 0;
datagram_count++;
}
datagram_data_size += sync_size;
list_for_each_entry(sc, &domain->master->configs, list) {
for (i = 0; i < sc->used_fmmus; i++) {
fmmu = &sc->fmmu_configs[i];
if (fmmu->domain != domain)
continue;
fmmu->logical_start_address += base_address;
if (datagram_data_size + fmmu->data_size > EC_MAX_DATA_SIZE) {
if (ec_domain_add_datagram(domain, datagram_offset,
datagram_data_size)) return -1;
datagram_offset += datagram_data_size;
datagram_data_size = 0;
datagram_count++;
}
datagram_data_size += fmmu->data_size;
}
}
@ -336,41 +247,9 @@ int ec_domain_alloc(ec_domain_t *domain, /**< EtherCAT domain */
datagram_count++;
}
if (datagram_count) {
// set all process data pointers
list_for_each_entry(data_reg, &domain->data_regs, list) {
for (i = 0; i < data_reg->slave->fmmu_count; i++) {
fmmu = &data_reg->slave->fmmus[i];
if (fmmu->domain == domain && fmmu->sync == data_reg->sync) {
pdo_off =
fmmu->logical_start_address + data_reg->sync_offset;
// search datagram
list_for_each_entry(datagram, &domain->datagrams, list) {
log_addr = EC_READ_U32(datagram->address);
pdo_off_datagram = pdo_off - log_addr;
if (pdo_off >= log_addr &&
pdo_off_datagram < datagram->mem_size) {
*data_reg->data_ptr = datagram->data +
pdo_off_datagram;
}
}
if (!data_reg->data_ptr) {
EC_ERR("Failed to assign data pointer!\n");
return -1;
}
break;
}
}
}
EC_INFO("Domain %u - Allocated %u bytes in %u datagram%s.\n",
domain->index, domain->data_size, datagram_count,
datagram_count == 1 ? "" : "s");
} else { // !datagram_count
EC_WARN("Domain %u contains no data!\n", domain->index);
}
ec_domain_clear_data_regs(domain);
EC_INFO("Domain %u with logical offset %u contains %u bytes in %u"
" datagram%s.\n", domain->index, domain->base_address,
domain->data_size, datagram_count, datagram_count == 1 ? "" : "s");
return 0;
}
@ -399,72 +278,31 @@ ssize_t ec_show_domain_attribute(struct kobject *kobj, /**< kobject */
* Realtime interface
*****************************************************************************/
/**
* Registers a PDO for a domain.
* \return 0 on success, else non-zero
* \ingroup RealtimeInterface
*/
int ecrt_domain_register_pdo(
ec_domain_t *domain, /**< EtherCAT domain */
ec_slave_t *slave, /**< EtherCAT slave */
uint16_t pdo_entry_index, /**< PDO entry index */
uint8_t pdo_entry_subindex, /**< PDO entry subindex */
void **data_ptr /**< address of the process data pointer */
)
int ecrt_domain_reg_pdo_entry(ec_domain_t *domain, ec_slave_config_t *sc,
uint16_t index, uint8_t subindex)
{
ec_sync_t *sync;
const ec_pdo_t *pdo;
const ec_pdo_entry_t *entry;
unsigned int i;
// search for PDO entry
for (i = 0; i < slave->sii_sync_count; i++) {
sync = &slave->sii_syncs[i];
list_for_each_entry(pdo, &sync->pdos, list) {
list_for_each_entry(entry, &pdo->entries, list) {
if (entry->index != pdo_entry_index ||
entry->subindex != pdo_entry_subindex) continue;
// PDO entry found
if (ec_domain_reg_pdo_entry(domain, sync, entry, data_ptr)) {
return -1;
}
return 0;
}
}
}
EC_ERR("PDO entry 0x%04X:%u is not mapped in slave %u.\n",
pdo_entry_index, pdo_entry_subindex, slave->ring_position);
return -1;
return ec_slave_config_reg_pdo_entry(sc, domain, index, subindex);
}
/*****************************************************************************/
/**
* Registers a bunch of data fields.
* \attention The list has to be terminated with a NULL structure ({})!
* \return 0 in case of success, else < 0
* \ingroup RealtimeInterface
*/
int ecrt_domain_register_pdo_list(
ec_domain_t *domain, /**< EtherCAT domain */
const ec_pdo_reg_t *pdo_regs /**< array of PDO registrations */
)
int ecrt_domain_reg_pdo_entry_list(ec_domain_t *domain,
const ec_pdo_entry_reg_t *regs)
{
const ec_pdo_reg_t *reg;
ec_slave_t *slave;
const ec_pdo_entry_reg_t *reg;
ec_slave_config_t *sc;
int ret;
for (reg = pdo_regs; reg->slave_address; reg++) {
if (!(slave = ecrt_master_get_slave(domain->master,
reg->slave_address, reg->vendor_id,
reg->product_code)))
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;
if (ecrt_domain_register_pdo(domain, slave, reg->pdo_entry_index,
reg->pdo_entry_subindex, reg->data_ptr))
if ((ret = ecrt_domain_reg_pdo_entry(domain, sc, reg->index,
reg->subindex)) < 0)
return -1;
*reg->offset = ret;
}
return 0;
@ -472,70 +310,7 @@ int ecrt_domain_register_pdo_list(
/*****************************************************************************/
/**
* Registers a PDO range in a domain.
* \return 0 on success, else non-zero
* \ingroup RealtimeInterface
*/
int ecrt_domain_register_pdo_range(
ec_domain_t *domain, /**< EtherCAT domain */
ec_slave_t *slave, /**< EtherCAT slave */
ec_direction_t dir, /**< data direction */
uint16_t offset, /**< offset in slave's PDO range */
uint16_t length, /**< length of this range */
void **data_ptr /**< address of the process data pointer */
)
{
ec_data_reg_t *data_reg;
ec_sync_t *sync;
uint16_t sync_length;
if (!(sync = ec_slave_get_pdo_sync(slave, dir))) {
EC_ERR("No sync manager found for PDO range.\n");
return -1;
}
// Allocate memory for data registration object
if (!(data_reg =
(ec_data_reg_t *) kmalloc(sizeof(ec_data_reg_t), GFP_KERNEL))) {
EC_ERR("Failed to allocate data registration.\n");
return -1;
}
if (ec_slave_prepare_fmmu(slave, domain, sync)) {
EC_ERR("FMMU configuration failed.\n");
kfree(data_reg);
return -1;
}
data_reg->slave = slave;
data_reg->sync = sync;
data_reg->sync_offset = offset;
data_reg->data_ptr = data_ptr;
// estimate sync manager length
sync_length = offset + length;
if (sync->est_length < sync_length) {
sync->est_length = sync_length;
if (domain->master->debug_level) {
EC_DBG("Estimating length of sync manager %u of slave %u to %u.\n",
sync->index, slave->ring_position, sync_length);
}
}
list_add_tail(&data_reg->list, &domain->data_regs);
return 0;
}
/*****************************************************************************/
/**
Processes received process data and requeues the domain datagram(s).
\ingroup RealtimeInterface
*/
void ecrt_domain_process(ec_domain_t *domain /**< EtherCAT domain */)
void ecrt_domain_process(ec_domain_t *domain)
{
unsigned int working_counter_sum;
ec_datagram_t *datagram;
@ -552,9 +327,9 @@ void ecrt_domain_process(ec_domain_t *domain /**< EtherCAT domain */)
}
}
if (working_counter_sum != domain->response_count) {
if (working_counter_sum != domain->working_counter) {
domain->working_counter_changes++;
domain->response_count = working_counter_sum;
domain->working_counter = working_counter_sum;
}
if (domain->working_counter_changes &&
@ -562,12 +337,12 @@ void ecrt_domain_process(ec_domain_t *domain /**< EtherCAT domain */)
domain->notify_jiffies = jiffies;
if (domain->working_counter_changes == 1) {
EC_INFO("Domain %u working counter change: %u\n", domain->index,
domain->response_count);
domain->working_counter);
}
else {
EC_INFO("Domain %u: %u WC changes. Current response count: %u\n",
EC_INFO("Domain %u: %u working counter changes. Currently %u\n",
domain->index, domain->working_counter_changes,
domain->response_count);
domain->working_counter);
}
domain->working_counter_changes = 0;
}
@ -575,12 +350,7 @@ void ecrt_domain_process(ec_domain_t *domain /**< EtherCAT domain */)
/*****************************************************************************/
/**
Places all process data datagrams in the masters datagram queue.
\ingroup RealtimeInterface
*/
void ecrt_domain_queue(ec_domain_t *domain /**< EtherCAT domain */)
void ecrt_domain_queue(ec_domain_t *domain)
{
ec_datagram_t *datagram;
@ -591,24 +361,20 @@ void ecrt_domain_queue(ec_domain_t *domain /**< EtherCAT domain */)
/*****************************************************************************/
/**
Returns the state of a domain.
\return 0 if all datagrams were received, else -1.
\ingroup RealtimeInterface
*/
int ecrt_domain_state(const ec_domain_t *domain /**< EtherCAT domain */)
void ecrt_domain_state(const ec_domain_t *domain, ec_domain_state_t *state)
{
return domain->state;
state->working_counter = domain->working_counter;
state->wc_state = EC_WC_ZERO; // FIXME
}
/*****************************************************************************/
/** \cond */
EXPORT_SYMBOL(ecrt_domain_register_pdo);
EXPORT_SYMBOL(ecrt_domain_register_pdo_list);
EXPORT_SYMBOL(ecrt_domain_register_pdo_range);
EXPORT_SYMBOL(ecrt_domain_reg_pdo_entry);
EXPORT_SYMBOL(ecrt_domain_reg_pdo_entry_list);
//EXPORT_SYMBOL(ecrt_domain_size);
//EXPORT_SYMBOL(ecrt_domain_memory);
EXPORT_SYMBOL(ecrt_domain_process);
EXPORT_SYMBOL(ecrt_domain_queue);
EXPORT_SYMBOL(ecrt_domain_state);

View File

@ -58,19 +58,18 @@
struct ec_domain
{
struct kobject kobj; /**< kobject */
struct list_head list; /**< list item */
unsigned int index; /**< domain index (just a number) */
ec_master_t *master; /**< EtherCAT master owning the domain */
size_t data_size; /**< size of the process data */
struct list_head datagrams; /**< process data datagrams */
uint32_t base_address; /**< logical offset address of the process data */
unsigned int response_count; /**< number of responding slaves */
unsigned int state; /**< domain error state */
struct list_head data_regs; /**< PDO data registrations */
unsigned int working_counter_changes; /**< working counter changes
since last notification */
unsigned long notify_jiffies; /**< time of last notification */
struct kobject kobj; /**< kobject. */
struct list_head list; /**< List item. */
unsigned int index; /**< Index (just a number). */
ec_master_t *master; /**< EtherCAT master owning the domain. */
size_t data_size; /**< Size of the process data. */
struct list_head datagrams; /**< Datagrams for process data exchange. */
uint32_t base_address; /**< Logical offset address of the process data. */
unsigned int working_counter; /**< Last working counter value. */
unsigned int state; /**< Error state. */
unsigned int working_counter_changes; /**< Working counter changes
since last notification. */
unsigned long notify_jiffies; /**< Time of last notification. */
};
/*****************************************************************************/
@ -78,7 +77,7 @@ struct ec_domain
int ec_domain_init(ec_domain_t *, ec_master_t *, unsigned int);
void ec_domain_destroy(ec_domain_t *);
int ec_domain_alloc(ec_domain_t *, uint32_t);
int ec_domain_finish(ec_domain_t *, uint32_t);
/*****************************************************************************/

View File

@ -31,63 +31,68 @@
*
*****************************************************************************/
/**
\file
EtherCAT FMMU methods.
*/
/** \file
* EtherCAT FMMU configuration methods.
*/
/*****************************************************************************/
#include "globals.h"
#include "slave.h"
#include "slave_config.h"
#include "master.h"
#include "fmmu.h"
#include "fmmu_config.h"
/*****************************************************************************/
/**
* FMMU Constructor.
/** FMMU configuration constructor.
*
* Inits an FMMU configuration, sets the logical start address and adds the
* process data size for the mapped PDOs of the given direction to the domain
* data size.
*/
void ec_fmmu_init(
ec_fmmu_t *fmmu, /**< EtherCAT FMMU */
ec_slave_t *slave, /**< EtherCAT slave */
unsigned int index /**< FMMU index */
void ec_fmmu_config_init(
ec_fmmu_config_t *fmmu, /**< EtherCAT FMMU configuration. */
ec_slave_config_t *sc, /**< EtherCAT slave configuration. */
ec_domain_t *domain, /**< EtherCAT domain. */
ec_direction_t dir /**< PDO direction. */
)
{
fmmu->slave = slave;
fmmu->index = index;
fmmu->sc = sc;
fmmu->domain = domain;
fmmu->dir = dir;
fmmu->logical_start_address = domain->data_size;
fmmu->data_size = ec_pdo_mapping_total_size(&sc->mapping[dir]);
domain->data_size += fmmu->data_size;
}
/*****************************************************************************/
/**
* Initializes an FMMU configuration page.
* The referenced memory (\a data) must be at least EC_FMMU_SIZE bytes.
/** Initializes an FMMU configuration page.
*
* The referenced memory (\a data) must be at least EC_FMMU_PAGE_SIZE bytes.
*/
void ec_fmmu_config(
const ec_fmmu_t *fmmu, /**< EtherCAT FMMU */
uint8_t *data /**> configuration memory */
void ec_fmmu_config_page(
const ec_fmmu_config_t *fmmu, /**< EtherCAT FMMU configuration. */
const ec_sync_t *sync, /**< Sync manager. */
uint8_t *data /**> Configuration page memory. */
)
{
size_t sync_size = ec_sync_size(fmmu->sync);
if (fmmu->slave->master->debug_level) {
EC_DBG("FMMU%u: LogAddr 0x%08X, Size %3i, PhysAddr 0x%04X, Dir %s\n",
fmmu->index, fmmu->logical_start_address,
sync_size, fmmu->sync->physical_start_address,
((fmmu->sync->control_register & 0x04) ? "out" : "in"));
if (fmmu->sc->master->debug_level) {
EC_DBG("FMMU: LogAddr 0x%08X, Size %3i, PhysAddr 0x%04X, Dir %s\n",
fmmu->logical_start_address, fmmu->data_size,
sync->physical_start_address,
(sync->control_register & 0x04) ? "out" : "in");
}
EC_WRITE_U32(data, fmmu->logical_start_address);
EC_WRITE_U16(data + 4, sync_size); // size of fmmu
EC_WRITE_U16(data + 4, fmmu->data_size); // size of fmmu
EC_WRITE_U8 (data + 6, 0x00); // logical start bit
EC_WRITE_U8 (data + 7, 0x07); // logical end bit
EC_WRITE_U16(data + 8, fmmu->sync->physical_start_address);
EC_WRITE_U16(data + 8, sync->physical_start_address);
EC_WRITE_U8 (data + 10, 0x00); // physical start bit
EC_WRITE_U8 (data + 11, ((fmmu->sync->control_register & 0x04)
? 0x02 : 0x01));
EC_WRITE_U8 (data + 11, (sync->control_register & 0x04) ? 0x02 : 0x01);
EC_WRITE_U16(data + 12, 0x0001); // enable
EC_WRITE_U16(data + 14, 0x0000); // reserved
}

View File

@ -31,15 +31,14 @@
*
*****************************************************************************/
/**
\file
EtherCAT FMMU structure.
*/
/** \file
* EtherCAT FMMU configuration structure.
*/
/*****************************************************************************/
#ifndef _EC_FMMU_H_
#define _EC_FMMU_H_
#ifndef _EC_FMMU_CONFIG_H_
#define _EC_FMMU_CONFIG_H_
#include "../include/ecrt.h"
@ -47,30 +46,26 @@
/*****************************************************************************/
/** size of an FMMU configuration page */
#define EC_FMMU_SIZE 16
/*****************************************************************************/
/**
* FMMU configuration.
/** FMMU configuration.
*/
typedef struct
{
const ec_slave_t *slave; /**< EtherCAT slave, the FMMU belongs to */
unsigned int index; /**< FMMU index */
const ec_domain_t *domain; /**< domain */
const ec_sync_t *sync; /**< sync manager */
uint32_t logical_start_address; /**< logical start address */
const ec_slave_config_t *sc; /**< EtherCAT slave config. */
const ec_domain_t *domain; /**< Domain. */
ec_direction_t dir; /**< PDO direction. */
uint32_t logical_start_address; /**< Logical start address. */
unsigned int data_size; /**< Covered PDO size. */
}
ec_fmmu_t;
ec_fmmu_config_t;
/*****************************************************************************/
void ec_fmmu_init(ec_fmmu_t *, ec_slave_t *, unsigned int);
void ec_fmmu_config_init(ec_fmmu_config_t *, ec_slave_config_t *,
ec_domain_t *, ec_direction_t);
void ec_fmmu_config(const ec_fmmu_t *, uint8_t *);
void ec_fmmu_config_page(const ec_fmmu_config_t *, const ec_sync_t *,
uint8_t *);
/*****************************************************************************/

View File

@ -59,8 +59,6 @@ void ec_fsm_coe_map_action_next_sync(ec_fsm_coe_map_t *);
void ec_fsm_coe_map_action_next_pdo(ec_fsm_coe_map_t *);
void ec_fsm_coe_map_action_next_pdo_entry(ec_fsm_coe_map_t *);
void ec_fsm_coe_map_clear_pdos(ec_fsm_coe_map_t *);
/*****************************************************************************/
/**
@ -74,7 +72,7 @@ void ec_fsm_coe_map_init(
{
fsm->state = NULL;
fsm->fsm_coe = fsm_coe;
INIT_LIST_HEAD(&fsm->pdos);
ec_pdo_mapping_init(&fsm->mapping);
}
/*****************************************************************************/
@ -85,27 +83,7 @@ void ec_fsm_coe_map_init(
void ec_fsm_coe_map_clear(ec_fsm_coe_map_t *fsm /**< finite state machine */)
{
ec_fsm_coe_map_clear_pdos(fsm);
}
/*****************************************************************************/
/**
* Clear FSM PDOs.
*/
void ec_fsm_coe_map_clear_pdos(
ec_fsm_coe_map_t *fsm /**< finite state machine */
)
{
ec_pdo_t *pdo, *next;
// free all PDOs
list_for_each_entry_safe(pdo, next, &fsm->pdos, list) {
list_del(&pdo->list);
ec_pdo_clear(pdo);
kfree(pdo);
}
ec_pdo_mapping_clear(&fsm->mapping);
}
/*****************************************************************************/
@ -188,7 +166,7 @@ void ec_fsm_coe_map_action_next_sync(
EC_DBG("Reading PDO mapping of sync manager %u of slave %u.\n",
fsm->sync_index, slave->ring_position);
ec_fsm_coe_map_clear_pdos(fsm);
ec_pdo_mapping_clear_pdos(&fsm->mapping);
if (!(entry = ec_sdo_get_entry(fsm->sync_sdo, 0))) {
EC_ERR("SDO 0x%04X has no subindex 0 on slave %u.\n",
@ -269,15 +247,14 @@ void ec_fsm_coe_map_action_next_pdo(
{
ec_sync_t *sync = fsm->slave->sii_syncs + fsm->sync_index;
ec_pdo_t *pdo;
// exchange sync manager PDO mapping
ec_sync_clear_pdos(sync);
list_for_each_entry(pdo, &fsm->pdos, list) {
ec_sync_add_pdo(sync, pdo);
if (ec_pdo_mapping_copy(&sync->mapping, &fsm->mapping)) {
fsm->state = ec_fsm_coe_map_state_error;
return;
}
sync->mapping_source = EC_SYNC_MAPPING_COE;
ec_fsm_coe_map_clear_pdos(fsm);
ec_pdo_mapping_clear_pdos(&fsm->mapping);
// next sync manager
fsm->sync_index++;
@ -316,8 +293,8 @@ void ec_fsm_coe_map_state_pdo(
ec_pdo_init(fsm->pdo);
fsm->pdo->index = EC_READ_U16(fsm->request.data);
fsm->pdo->type =
ec_sync_get_pdo_type(fsm->slave->sii_syncs + fsm->sync_index);
fsm->pdo->dir =
ec_sync_direction(fsm->slave->sii_syncs + fsm->sync_index);
if (fsm->slave->master->debug_level)
EC_DBG(" PDO 0x%04X.\n", fsm->pdo->index);
@ -331,19 +308,11 @@ void ec_fsm_coe_map_state_pdo(
return;
}
if (fsm->pdo_sdo->name && strlen(fsm->pdo_sdo->name)) {
if (!(fsm->pdo->name = (char *)
kmalloc(strlen(fsm->pdo_sdo->name) + 1, GFP_KERNEL))) {
EC_ERR("Failed to allocate PDO name.\n");
ec_pdo_clear(fsm->pdo);
kfree(fsm->pdo);
fsm->state = ec_fsm_coe_map_state_error;
return;
}
memcpy(fsm->pdo->name, fsm->pdo_sdo->name,
strlen(fsm->pdo_sdo->name) + 1);
} else {
fsm->pdo->name = NULL;
if (ec_pdo_set_name(fsm->pdo, fsm->pdo_sdo->name)) {
ec_pdo_clear(fsm->pdo);
kfree(fsm->pdo);
fsm->state = ec_fsm_coe_map_state_error;
return;
}
if (!(entry = ec_sdo_get_entry(fsm->pdo_sdo, 0))) {
@ -356,7 +325,7 @@ void ec_fsm_coe_map_state_pdo(
return;
}
list_add_tail(&fsm->pdo->list, &fsm->pdos);
list_add_tail(&fsm->pdo->list, &fsm->mapping.pdos);
ec_sdo_request_init_read(&fsm->request, entry);
fsm->state = ec_fsm_coe_map_state_pdo_entry_count;
@ -463,28 +432,25 @@ void ec_fsm_coe_map_state_pdo_entry(
return;
}
ec_pdo_entry_init(pdo_entry);
pdo_entry->index = pdo_entry_info >> 16;
pdo_entry->subindex = (pdo_entry_info >> 8) & 0xFF;
pdo_entry->bit_length = pdo_entry_info & 0xFF;
if (!pdo_entry->index && !pdo_entry->subindex) {
char gapname[] = "Gap";
// we have a gap in the PDO, next PDO entry
if (fsm->slave->master->debug_level) {
EC_DBG(" PDO entry gap: %u bit.\n",
pdo_entry->bit_length);
}
if (!(pdo_entry->name = (char *)
kmalloc(strlen(gapname) + 1, GFP_KERNEL))) {
EC_ERR("Failed to allocate GAP entry name.\n");
if (ec_pdo_entry_set_name(pdo_entry, "Gap")) {
ec_pdo_entry_clear(pdo_entry);
kfree(pdo_entry);
fsm->state = ec_fsm_coe_map_state_error;
return;
}
memcpy(pdo_entry->name, gapname, strlen(gapname) + 1);
list_add_tail(&pdo_entry->list, &fsm->pdo->entries);
fsm->pdo_subindex++;
ec_fsm_coe_map_action_next_pdo_entry(fsm);
@ -494,6 +460,7 @@ void ec_fsm_coe_map_state_pdo_entry(
if (!(sdo = ec_slave_get_sdo(fsm->slave, pdo_entry->index))) {
EC_ERR("Slave %u has no SDO 0x%04X.\n",
fsm->slave->ring_position, pdo_entry->index);
ec_pdo_entry_clear(pdo_entry);
kfree(pdo_entry);
fsm->state = ec_fsm_coe_map_state_error;
return;
@ -503,27 +470,22 @@ void ec_fsm_coe_map_state_pdo_entry(
EC_ERR("Slave %u has no SDO entry 0x%04X:%u.\n",
fsm->slave->ring_position, pdo_entry->index,
pdo_entry->subindex);
ec_pdo_entry_clear(pdo_entry);
kfree(pdo_entry);
fsm->state = ec_fsm_coe_map_state_error;
return;
}
if (entry->description && strlen(entry->description)) {
if (!(pdo_entry->name = (char *)
kmalloc(strlen(entry->description) + 1, GFP_KERNEL))) {
EC_ERR("Failed to allocate PDO entry name.\n");
kfree(pdo_entry);
fsm->state = ec_fsm_coe_map_state_error;
return;
}
memcpy(pdo_entry->name, entry->description, strlen(entry->description) + 1);
} else {
pdo_entry->name = NULL;
if (ec_pdo_entry_set_name(pdo_entry, entry->description)) {
ec_pdo_entry_clear(pdo_entry);
kfree(pdo_entry);
fsm->state = ec_fsm_coe_map_state_error;
return;
}
if (fsm->slave->master->debug_level) {
EC_DBG(" PDO entry 0x%04X \"%s\" (%u bit).\n",
pdo_entry->index, pdo_entry->name ? pdo_entry->name : "???",
EC_DBG(" PDO entry 0x%04X \"%s\" (%u bit).\n", pdo_entry->index,
pdo_entry->name ? pdo_entry->name : "???",
pdo_entry->bit_length);
}

View File

@ -51,8 +51,8 @@
typedef struct ec_fsm_coe_map ec_fsm_coe_map_t; /**< \see ec_fsm_coe_map */
/**
* \todo doc
*/
struct ec_fsm_coe_map
{
void (*state)(ec_fsm_coe_map_t *); /**< CoE mapping state function */
@ -66,7 +66,7 @@ struct ec_fsm_coe_map
uint8_t sync_subindices; /**< number of mapped PDOs */
uint16_t sync_subindex; /**< current subindex in mapping SDO */
struct list_head pdos; /**< list of mapped PDOs */
ec_pdo_mapping_t mapping; /**< Mapping to apply. */
ec_pdo_t *pdo; /**< current PDO */
ec_sdo_t *pdo_sdo; /**< current PDO SDO */
uint8_t pdo_subindices; /**< number of PDO entries */

View File

@ -31,16 +31,17 @@
*
*****************************************************************************/
/**
\file
EtherCAT PDO mapping state machine.
*/
/** \file
* EtherCAT PDO mapping state machine.
*/
/*****************************************************************************/
#include "globals.h"
#include "master.h"
#include "mailbox.h"
#include "slave_config.h"
#include "fsm_mapping.h"
/*****************************************************************************/
@ -56,11 +57,10 @@ void ec_fsm_mapping_next_sync(ec_fsm_mapping_t *);
/*****************************************************************************/
/**
* Constructor.
/** Constructor.
*/
void ec_fsm_mapping_init(ec_fsm_mapping_t *fsm, /**< mapping state machine */
void ec_fsm_mapping_init(
ec_fsm_mapping_t *fsm, /**< mapping state machine */
ec_fsm_coe_t *fsm_coe /**< CoE state machine to use */
)
{
@ -70,20 +70,18 @@ void ec_fsm_mapping_init(ec_fsm_mapping_t *fsm, /**< mapping state machine */
/*****************************************************************************/
/**
* Destructor.
/** Destructor.
*/
void ec_fsm_mapping_clear(ec_fsm_mapping_t *fsm /**< mapping state machine */)
void ec_fsm_mapping_clear(
ec_fsm_mapping_t *fsm /**< mapping state machine */
)
{
}
/*****************************************************************************/
/**
* Start PDO mapping configuration state machine.
/** Start PDO mapping configuration state machine.
*/
void ec_fsm_mapping_start(
ec_fsm_mapping_t *fsm, /**< mapping state machine */
ec_slave_t *slave /**< slave to configure */
@ -95,10 +93,10 @@ void ec_fsm_mapping_start(
/*****************************************************************************/
/**
/** Get running state.
*
* \return false, if state machine has terminated
*/
int ec_fsm_mapping_running(
const ec_fsm_mapping_t *fsm /**< mapping state machine */
)
@ -109,13 +107,13 @@ int ec_fsm_mapping_running(
/*****************************************************************************/
/**
* Executes the current state of the state machine.
/** Executes the current state of the state machine.
*
* If the state machine's datagram is not sent or received yet, the execution
* of the state machine is delayed to the next cycle.
*
* \return false, if state machine has terminated
*/
int ec_fsm_mapping_exec(
ec_fsm_mapping_t *fsm /**< mapping state machine */
)
@ -126,10 +124,10 @@ int ec_fsm_mapping_exec(
/*****************************************************************************/
/**
/** Get execution result.
*
* \return true, if the state machine terminated gracefully
*/
int ec_fsm_mapping_success(
const ec_fsm_mapping_t *fsm /**< mapping state machine */
)
@ -138,27 +136,28 @@ int ec_fsm_mapping_success(
}
/******************************************************************************
* state functions
* State functions.
*****************************************************************************/
/**
* Start mapping configuration.
/** Start mapping configuration.
*/
void ec_fsm_mapping_state_start(
ec_fsm_mapping_t *fsm /**< mapping state machine */
)
{
if (!fsm->slave->config) {
fsm->state = ec_fsm_mapping_state_end;
return;
}
fsm->dir = EC_DIR_OUTPUT;
ec_fsm_mapping_next_sync(fsm);
}
/*****************************************************************************/
/**
* Process mapping of next sync manager.
/** Process mapping of next sync manager.
*/
void ec_fsm_mapping_next_sync(
ec_fsm_mapping_t *fsm /**< mapping state machine */
)
@ -171,18 +170,25 @@ void ec_fsm_mapping_next_sync(
}
if (!(fsm->sync = ec_slave_get_pdo_sync(fsm->slave, fsm->dir))) {
// no sync manager found for this direction
EC_WARN("next_sync(): No sync manager!\n");
fsm->dir++;
continue;
}
fsm->dir++;
if (fsm->sync->alt_mapping)
fsm->mapping = &fsm->slave->config->mapping[fsm->dir];
if (ec_pdo_mapping_equal(&fsm->sync->mapping, fsm->mapping)) {
// the mapping for this direction does not have to be altered
fsm->dir++;
continue;
} else {
fsm->dir++;
break;
}
}
if (fsm->slave->master->debug_level) {
EC_DBG("Configuring PDO mapping for SM%u of slave %i.\n",
EC_DBG("Configuring PDO mapping for SM%u of slave %u.\n",
fsm->sync->index, fsm->slave->ring_position);
}
@ -201,34 +207,23 @@ void ec_fsm_mapping_next_sync(
/*****************************************************************************/
/**
* Process mapping of next PDO.
/** Process mapping of next PDO.
*/
ec_pdo_t *ec_fsm_mapping_next_pdo(
ec_fsm_mapping_t *fsm, /**< mapping state machine */
struct list_head *list /**< current PDO list item */
const ec_fsm_mapping_t *fsm, /**< mapping state machine */
const struct list_head *list /**< current PDO list item */
)
{
ec_pdo_t *pdo;
do {
list = list->next;
if (list == &fsm->sync->pdos)
return NULL; // no next PDO
pdo = list_entry(list, ec_pdo_t, list);
}
while (pdo->sync_index != fsm->sync->index);
return pdo;
list = list->next;
if (list == &fsm->mapping->pdos)
return NULL; // no next PDO
return list_entry(list, ec_pdo_t, list);
}
/*****************************************************************************/
/**
* Set the number of mapped PDOs to zero.
/** Set the number of mapped PDOs to zero.
*/
void ec_fsm_mapping_state_zero_count(
ec_fsm_mapping_t *fsm /**< mapping state machine */
)
@ -245,8 +240,7 @@ void ec_fsm_mapping_state_zero_count(
// map all PDOs belonging to the current sync manager
// find first PDO
if (!(fsm->pdo = ec_fsm_mapping_next_pdo(
fsm, &fsm->sync->pdos))) {
if (!(fsm->pdo = ec_fsm_mapping_next_pdo(fsm, &fsm->mapping->pdos))) {
if (fsm->slave->master->debug_level)
EC_DBG("No PDOs to map for SM%u of slave %u.\n",
fsm->sync->index, fsm->slave->ring_position);
@ -271,10 +265,8 @@ void ec_fsm_mapping_state_zero_count(
/*****************************************************************************/
/**
* Add a PDO to the sync managers mapping.
/** Add a PDO to the sync managers mapping.
*/
void ec_fsm_mapping_state_add_pdo(
ec_fsm_mapping_t *fsm /**< mapping state machine */
)
@ -289,8 +281,7 @@ void ec_fsm_mapping_state_add_pdo(
}
// find next PDO
if (!(fsm->pdo = ec_fsm_mapping_next_pdo(
fsm, &fsm->pdo->list))) {
if (!(fsm->pdo = ec_fsm_mapping_next_pdo(fsm, &fsm->pdo->list))) {
// no more PDOs to map. write PDO count
fsm->sdodata.subindex = 0;
EC_WRITE_U8(&fsm->sdo_value, fsm->pdo_count);
@ -322,10 +313,8 @@ void ec_fsm_mapping_state_add_pdo(
/*****************************************************************************/
/**
* Set the number of mapped PDOs.
/** Set the number of mapped PDOs.
*/
void ec_fsm_mapping_state_pdo_count(
ec_fsm_mapping_t *fsm /**< mapping state machine */
)
@ -351,10 +340,8 @@ void ec_fsm_mapping_state_pdo_count(
* Common state functions
*****************************************************************************/
/**
State: ERROR.
*/
/** State: ERROR.
*/
void ec_fsm_mapping_state_error(
ec_fsm_mapping_t *fsm /**< mapping state machine */
)
@ -363,10 +350,8 @@ void ec_fsm_mapping_state_error(
/*****************************************************************************/
/**
State: END.
*/
/** State: END.
*/
void ec_fsm_mapping_state_end(
ec_fsm_mapping_t *fsm /**< mapping state machine */
)

View File

@ -63,6 +63,7 @@ struct ec_fsm_mapping
ec_direction_t dir; /**< current PDO direction */
ec_sync_t *sync; /**< current sync manager */
const ec_pdo_mapping_t *mapping; /**< Mapping to assign. */
ec_pdo_t *pdo; /**< current PDO */
ec_sdo_data_t sdodata; /**< SDO configuration data */
uint16_t sdo_value; /**< SDO value */

View File

@ -205,7 +205,7 @@ void ec_fsm_master_state_broadcast(ec_fsm_master_t *fsm /**< master state machin
fsm->topology_change_pending = 1;
fsm->slaves_responding = datagram->working_counter;
EC_INFO("%i slave%s responding.\n",
EC_INFO("%u slave%s responding.\n",
fsm->slaves_responding,
fsm->slaves_responding == 1 ? "" : "s");
@ -483,12 +483,11 @@ void ec_fsm_master_action_process_states(ec_fsm_master_t *fsm
down(&master->config_sem);
if (!master->allow_config) {
up(&master->config_sem);
}
else {
} else {
master->config_state = EC_REQUEST_IN_PROGRESS;
fsm->config_error = 0;
up(&master->config_sem);
// check for pending slave configurations
if (ec_fsm_master_action_configure(fsm))
return;
@ -911,6 +910,9 @@ void ec_fsm_master_state_scan_slaves(
EC_INFO("Bus scanning completed in %u ms.\n",
(u32) (jiffies - fsm->scan_jiffies) * 1000 / HZ);
// Attach slave configurations
ec_master_attach_slave_configs(master);
#ifdef EC_EOE
// check if EoE processing has to be started
ec_master_eoe_start(master);

View File

@ -44,7 +44,7 @@
#include "globals.h"
#include "../include/ecrt.h"
#include "datagram.h"
#include "slave.h"
//#include "slave.h"
#include "canopen.h"
#include "fsm_slave.h"

View File

@ -41,8 +41,9 @@
#include "globals.h"
#include "master.h"
#include "mailbox.h"
#include "fsm_slave.h"
#include "fsm_mapping.h"
#include "slave_config.h"
#include "fsm_slave.h"
/*****************************************************************************/
@ -569,12 +570,12 @@ void ec_fsm_slave_scan_state_eeprom_data(ec_fsm_slave_t *fsm /**< slave state ma
break;
case 0x0032:
if (ec_slave_fetch_sii_pdos( slave, (uint8_t *) cat_word,
cat_size * 2, EC_TX_PDO))
cat_size * 2, EC_DIR_INPUT)) // TxPdo
goto end;
break;
case 0x0033:
if (ec_slave_fetch_sii_pdos( slave, (uint8_t *) cat_word,
cat_size * 2, EC_RX_PDO))
cat_size * 2, EC_DIR_OUTPUT)) // RxPdo
goto end;
break;
default:
@ -662,8 +663,8 @@ void ec_fsm_slave_conf_state_init(ec_fsm_slave_t *fsm /**< slave state machine *
// clear FMMU configurations
ec_datagram_npwr(datagram, slave->station_address,
0x0600, EC_FMMU_SIZE * slave->base_fmmu_count);
memset(datagram->data, 0x00, EC_FMMU_SIZE * slave->base_fmmu_count);
0x0600, EC_FMMU_PAGE_SIZE * slave->base_fmmu_count);
memset(datagram->data, 0x00, EC_FMMU_PAGE_SIZE * slave->base_fmmu_count);
fsm->retries = EC_FSM_RETRIES;
fsm->state = ec_fsm_slave_conf_state_clear_fmmus;
}
@ -729,7 +730,7 @@ void ec_fsm_slave_conf_enter_mbox_sync(
if (!slave->sii_mailbox_protocols) {
// no mailbox protocols supported
if (master->debug_level)
EC_DBG("Slave %i does not support mailbox communication.\n",
EC_DBG("Slave %u does not support mailbox communication.\n",
slave->ring_position);
ec_fsm_slave_conf_enter_preop(fsm);
return;
@ -740,41 +741,41 @@ void ec_fsm_slave_conf_enter_mbox_sync(
slave->ring_position);
}
if (slave->sii_sync_count >= 2) {
// configure sync managers
if (slave->sii_sync_count >= 2) { // mailbox configuration provided
ec_datagram_npwr(datagram, slave->station_address, 0x0800,
EC_SYNC_SIZE * slave->sii_sync_count);
memset(datagram->data, 0x00, EC_SYNC_SIZE * slave->sii_sync_count);
EC_SYNC_PAGE_SIZE * slave->sii_sync_count);
memset(datagram->data, 0x00,
EC_SYNC_PAGE_SIZE * slave->sii_sync_count);
for (i = 0; i < 2; i++) {
ec_sync_config(&slave->sii_syncs[i],
datagram->data + EC_SYNC_SIZE * i);
ec_sync_config(&slave->sii_syncs[i], slave->sii_syncs[i].length,
datagram->data + EC_SYNC_PAGE_SIZE * i);
}
} else { // no mailbox sync manager configurations provided
ec_sync_t sync;
if (master->debug_level)
EC_DBG("Slave %i does not provide"
EC_DBG("Slave %u does not provide"
" mailbox sync manager configurations.\n",
slave->ring_position);
ec_datagram_npwr(datagram, slave->station_address, 0x0800,
EC_SYNC_SIZE * 2);
memset(datagram->data, 0x00, EC_SYNC_SIZE * 2);
EC_SYNC_PAGE_SIZE * 2);
memset(datagram->data, 0x00, EC_SYNC_PAGE_SIZE * 2);
ec_sync_init(&sync, slave, 0);
sync.physical_start_address = slave->sii_rx_mailbox_offset;
sync.length = slave->sii_rx_mailbox_size;
sync.control_register = 0x26;
sync.enable = 1;
ec_sync_config(&sync, datagram->data + EC_SYNC_SIZE * sync.index);
ec_sync_config(&sync, slave->sii_rx_mailbox_size,
datagram->data + EC_SYNC_PAGE_SIZE * sync.index);
ec_sync_init(&sync, slave, 1);
sync.physical_start_address = slave->sii_tx_mailbox_offset;
sync.length = slave->sii_tx_mailbox_size;
sync.control_register = 0x22;
sync.enable = 1;
ec_sync_config(&sync, datagram->data + EC_SYNC_SIZE * sync.index);
ec_sync_config(&sync, slave->sii_tx_mailbox_size,
datagram->data + EC_SYNC_PAGE_SIZE * sync.index);
}
fsm->retries = EC_FSM_RETRIES;
@ -877,15 +878,22 @@ void ec_fsm_slave_conf_enter_sdoconf(ec_fsm_slave_t *fsm /**< slave state machin
{
ec_slave_t *slave = fsm->slave;
// No CoE configuration to be applied?
if (list_empty(&slave->sdo_confs)) { // skip SDO configuration
if (!slave->config) {
EC_DBG("Slave %u is not configured.\n", slave->ring_position);
ec_fsm_slave_conf_enter_saveop(fsm);
return;
}
// No CoE configuration to be applied? FIXME
if (list_empty(&slave->config->sdo_configs)) { // skip SDO configuration
ec_fsm_slave_conf_enter_mapconf(fsm);
return;
}
// start SDO configuration
fsm->state = ec_fsm_slave_conf_state_sdoconf;
fsm->sdodata = list_entry(fsm->slave->sdo_confs.next, ec_sdo_data_t, list);
fsm->sdodata =
list_entry(fsm->slave->config->sdo_configs.next, ec_sdo_data_t, list);
ec_fsm_coe_download(&fsm->fsm_coe, fsm->slave, fsm->sdodata);
ec_fsm_coe_exec(&fsm->fsm_coe); // execute immediately
}
@ -911,9 +919,9 @@ void ec_fsm_slave_conf_state_sdoconf(
}
// Another SDO to configure?
if (fsm->sdodata->list.next != &fsm->slave->sdo_confs) {
fsm->sdodata = list_entry(fsm->sdodata->list.next,
ec_sdo_data_t, list);
if (fsm->sdodata->list.next != &fsm->slave->config->sdo_configs) {
fsm->sdodata =
list_entry(fsm->sdodata->list.next, ec_sdo_data_t, list);
ec_fsm_coe_download(&fsm->fsm_coe, fsm->slave, fsm->sdodata);
ec_fsm_coe_exec(&fsm->fsm_coe); // execute immediately
return;
@ -982,21 +990,34 @@ void ec_fsm_slave_conf_enter_pdo_sync(
{
ec_slave_t *slave = fsm->slave;
ec_datagram_t *datagram = fsm->datagram;
unsigned int i;
unsigned int i, offset, num_syncs;
const ec_sync_t *sync;
ec_direction_t dir;
uint16_t size;
if (!slave->sii_sync_count) {
ec_fsm_slave_conf_enter_fmmu(fsm);
return;
}
// configure sync managers for process data
ec_datagram_npwr(datagram, slave->station_address, 0x0800,
EC_SYNC_SIZE * slave->sii_sync_count);
memset(datagram->data, 0x00, EC_SYNC_SIZE * slave->sii_sync_count);
if (slave->sii_mailbox_protocols) {
offset = 2; // slave has mailboxes
} else {
offset = 0;
}
num_syncs = slave->sii_sync_count - offset;
for (i = 0; i < slave->sii_sync_count; i++) {
ec_sync_config(&slave->sii_syncs[i],
datagram->data + EC_SYNC_SIZE * i);
// configure sync managers for process data
ec_datagram_npwr(datagram, slave->station_address,
0x0800 + EC_SYNC_PAGE_SIZE * offset,
EC_SYNC_PAGE_SIZE * num_syncs);
memset(datagram->data, 0x00, EC_SYNC_PAGE_SIZE * num_syncs);
for (i = 0; i < num_syncs; i++) {
sync = &slave->sii_syncs[i + offset];
dir = ec_sync_direction(sync);
size = ec_pdo_mapping_total_size(&slave->config->mapping[dir]);
ec_sync_config(sync, size, datagram->data + EC_SYNC_PAGE_SIZE * i);
}
fsm->retries = EC_FSM_RETRIES;
@ -1047,7 +1068,9 @@ void ec_fsm_slave_conf_enter_fmmu(ec_fsm_slave_t *fsm /**< slave state machine *
{
ec_slave_t *slave = fsm->slave;
ec_datagram_t *datagram = fsm->datagram;
unsigned int j;
unsigned int i;
const ec_fmmu_config_t *fmmu;
const ec_sync_t *sync;
if (!slave->base_fmmu_count) { // skip FMMU configuration
ec_fsm_slave_conf_enter_saveop(fsm);
@ -1056,10 +1079,18 @@ void ec_fsm_slave_conf_enter_fmmu(ec_fsm_slave_t *fsm /**< slave state machine *
// configure FMMUs
ec_datagram_npwr(datagram, slave->station_address,
0x0600, EC_FMMU_SIZE * slave->base_fmmu_count);
memset(datagram->data, 0x00, EC_FMMU_SIZE * slave->base_fmmu_count);
for (j = 0; j < slave->fmmu_count; j++) {
ec_fmmu_config(&slave->fmmus[j], datagram->data + EC_FMMU_SIZE * j);
0x0600, EC_FMMU_PAGE_SIZE * slave->base_fmmu_count);
memset(datagram->data, 0x00, EC_FMMU_PAGE_SIZE * slave->base_fmmu_count);
for (i = 0; i < slave->config->used_fmmus; i++) {
fmmu = &slave->config->fmmu_configs[i];
if (!(sync = ec_slave_get_pdo_sync(slave, fmmu->dir))) {
fsm->state = ec_fsm_slave_state_error;
EC_ERR("Failed to determine PDO sync manager for FMMU on slave"
" %u!\n", slave->ring_position);
return;
}
ec_fmmu_config_page(fmmu, sync,
datagram->data + EC_FMMU_PAGE_SIZE * i);
}
fsm->retries = EC_FSM_RETRIES;

View File

@ -31,10 +31,9 @@
*
*****************************************************************************/
/**
\file
Global definitions and macros.
*/
/** \file
* Global definitions and macros.
*/
/*****************************************************************************/
@ -46,112 +45,121 @@
#include "../globals.h"
/******************************************************************************
* EtherCAT master
* EtherCAT master
*****************************************************************************/
/** clock frequency for the EoE state machines */
/** Clock frequency for the EoE state machines. */
#define EC_EOE_FREQUENCY 1000
/** datagram timeout in microseconds */
/** Datagram timeout in microseconds. */
#define EC_IO_TIMEOUT 500
/** number of state machine retries on datagram timeout */
/** Number of state machine retries on datagram timeout. */
#define EC_FSM_RETRIES 3
/** Seconds to wait before fetching SDO dictionary
after slave entered PREOP state. */
#define EC_WAIT_SDO_DICT 3
/** minimum size of a buffer used with ec_state_string() */
/** Minimum size of a buffer used with ec_state_string(). */
#define EC_STATE_STRING_SIZE 32
/** maximum EEPROM size in words, to avoid infinite reading. */
/** Maximum EEPROM size in words, to avoid infinite reading. */
#define EC_MAX_EEPROM_SIZE 1024
/******************************************************************************
* EtherCAT protocol
* EtherCAT protocol
*****************************************************************************/
/** size of an EtherCAT frame header */
/** Size of an EtherCAT frame header. */
#define EC_FRAME_HEADER_SIZE 2
/** size of an EtherCAT datagram header */
/** Size of an EtherCAT datagram header. */
#define EC_DATAGRAM_HEADER_SIZE 10
/** size of an EtherCAT datagram footer */
/** Size of an EtherCAT datagram footer. */
#define EC_DATAGRAM_FOOTER_SIZE 2
/** size of the EtherCAT address field */
/** Size of the EtherCAT address field. */
#define EC_ADDR_LEN 4
/** resulting maximum data size of a single datagram in a frame */
/** Resulting maximum data size of a single datagram in a frame. */
#define EC_MAX_DATA_SIZE (ETH_DATA_LEN - EC_FRAME_HEADER_SIZE \
- EC_DATAGRAM_HEADER_SIZE - EC_DATAGRAM_FOOTER_SIZE)
/** word offset of first EEPROM category. */
/** Word offset of first EEPROM category. */
#define EC_FIRST_EEPROM_CATEGORY_OFFSET 0x40
/** Size of a sync manager configuration page. */
#define EC_SYNC_PAGE_SIZE 8
/** Maximum number of FMMUs per slave. */
#define EC_MAX_FMMUS 16
/** Size of an FMMU configuration page. */
#define EC_FMMU_PAGE_SIZE 16
/*****************************************************************************/
/**
Convenience macro for printing EtherCAT-specific information to syslog.
This will print the message in \a fmt with a prefixed "EtherCAT: ".
\param fmt format string (like in printf())
\param args arguments (optional)
*/
/** Convenience macro for printing EtherCAT-specific information to syslog.
*
* This will print the message in \a fmt with a prefixed "EtherCAT: ".
*
* \param fmt format string (like in printf())
* \param args arguments (optional)
*/
#define EC_INFO(fmt, args...) \
printk(KERN_INFO "EtherCAT: " fmt, ##args)
/**
Convenience macro for printing EtherCAT-specific errors to syslog.
This will print the message in \a fmt with a prefixed "EtherCAT ERROR: ".
\param fmt format string (like in printf())
\param args arguments (optional)
*/
/** Convenience macro for printing EtherCAT-specific errors to syslog.
*
* This will print the message in \a fmt with a prefixed "EtherCAT ERROR: ".
*
* \param fmt format string (like in printf())
* \param args arguments (optional)
*/
#define EC_ERR(fmt, args...) \
printk(KERN_ERR "EtherCAT ERROR: " fmt, ##args)
/**
Convenience macro for printing EtherCAT-specific warnings to syslog.
This will print the message in \a fmt with a prefixed "EtherCAT WARNING: ".
\param fmt format string (like in printf())
\param args arguments (optional)
*/
/** Convenience macro for printing EtherCAT-specific warnings to syslog.
*
* This will print the message in \a fmt with a prefixed "EtherCAT WARNING: ".
*
* \param fmt format string (like in printf())
* \param args arguments (optional)
*/
#define EC_WARN(fmt, args...) \
printk(KERN_WARNING "EtherCAT WARNING: " fmt, ##args)
/**
Convenience macro for printing EtherCAT debug messages to syslog.
This will print the message in \a fmt with a prefixed "EtherCAT DEBUG: ".
\param fmt format string (like in printf())
\param args arguments (optional)
*/
/** Convenience macro for printing EtherCAT debug messages to syslog.
*
* This will print the message in \a fmt with a prefixed "EtherCAT DEBUG: ".
*
* \param fmt format string (like in printf())
* \param args arguments (optional)
*/
#define EC_DBG(fmt, args...) \
printk(KERN_DEBUG "EtherCAT DEBUG: " fmt, ##args)
/**
Convenience macro for defining read-only SysFS attributes.
This results in creating a static variable called attr_\a NAME. The SysFS
file will be world-readable.
\param NAME name of the attribute to create.
*/
/** Convenience macro for defining read-only SysFS attributes.
*
* This results in creating a static variable called attr_\a NAME. The SysFS
* file will be world-readable.
*
* \param NAME name of the attribute to create.
*/
#define EC_SYSFS_READ_ATTR(NAME) \
static struct attribute attr_##NAME = { \
.name = EC_STR(NAME), .owner = THIS_MODULE, .mode = S_IRUGO \
}
/**
Convenience macro for defining read-write SysFS attributes.
This results in creating a static variable called attr_\a NAME. The SysFS
file will be word-readable plus owner-writable.
\param NAME name of the attribute to create.
*/
/** Convenience macro for defining read-write SysFS attributes.
*
* This results in creating a static variable called attr_\a NAME. The SysFS
* file will be word-readable plus owner-writable.
*
* \param NAME name of the attribute to create.
*/
#define EC_SYSFS_READ_WRITE_ATTR(NAME) \
static struct attribute attr_##NAME = { \
.name = EC_STR(NAME), .owner = THIS_MODULE, .mode = S_IRUGO | S_IWUSR \
@ -171,27 +179,22 @@ int ec_mac_is_zero(const uint8_t *);
/*****************************************************************************/
/**
Code - Message pair.
Some EtherCAT datagrams support reading a status code to display a certain
message. This type allows to map a code to a message string.
*/
typedef struct
{
uint32_t code; /**< code */
const char *message; /**< message belonging to \a code */
/** Code - Message pair.
*
* Some EtherCAT datagrams support reading a status code to display a certain
* message. This type allows to map a code to a message string.
*/
typedef struct {
uint32_t code; /**< Code. */
const char *message; /**< Message belonging to \a code. */
}
ec_code_msg_t;
/*****************************************************************************/
/**
* Master request state.
/** Master request state.
*/
typedef enum
{
typedef enum {
EC_REQUEST_QUEUED,
EC_REQUEST_IN_PROGRESS,
EC_REQUEST_COMPLETE,
@ -201,6 +204,7 @@ ec_request_state_t;
/*****************************************************************************/
typedef struct ec_slave ec_slave_t; /**< \see ec_slave. */
typedef struct ec_sdo ec_sdo_t; /**< \see ec_sdo */
/*****************************************************************************/

View File

@ -46,16 +46,18 @@
#include "../include/ecrt.h"
#include "globals.h"
#include "master.h"
#include "slave.h"
#include "slave_config.h"
#include "device.h"
#include "datagram.h"
#ifdef EC_EOE
#include "ethernet.h"
#endif
#include "master.h"
/*****************************************************************************/
void ec_master_destroy_slave_configs(ec_master_t *);
void ec_master_destroy_domains(ec_master_t *);
static int ec_master_idle_thread(ec_master_t *);
static int ec_master_operation_thread(ec_master_t *);
@ -122,6 +124,8 @@ int ec_master_init(ec_master_t *master, /**< EtherCAT master */
INIT_LIST_HEAD(&master->slaves);
master->slave_count = 0;
INIT_LIST_HEAD(&master->configs);
master->scan_state = EC_REQUEST_IN_PROGRESS;
master->allow_scan = 1;
init_MUTEX(&master->scan_sem);
@ -136,12 +140,14 @@ int ec_master_init(ec_master_t *master, /**< EtherCAT master */
master->datagram_index = 0;
INIT_LIST_HEAD(&master->domains);
master->debug_level = 0;
master->debug_level = 0;
master->stats.timeouts = 0;
master->stats.corrupted = 0;
master->stats.unmatched = 0;
master->stats.output_jiffies = 0;
master->pdo_slaves_offline = 0; // assume all PDO slaves online
master->frames_timed_out = 0;
for (i = 0; i < HZ; i++) {
master->idle_cycle_times[i] = 0;
@ -236,6 +242,7 @@ void ec_master_clear(
#ifdef EC_EOE
ec_master_clear_eoe_handlers(master);
#endif
ec_master_destroy_slave_configs(master);
ec_master_destroy_slaves(master);
ec_master_destroy_domains(master);
ec_fsm_master_clear(&master->fsm);
@ -251,10 +258,8 @@ void ec_master_clear(
/*****************************************************************************/
#ifdef EC_EOE
/**
* Clear and free all EoE handlers.
/** Clear and free all EoE handlers.
*/
void ec_master_clear_eoe_handlers(
ec_master_t *master /**< EtherCAT master */
)
@ -271,15 +276,27 @@ void ec_master_clear_eoe_handlers(
/*****************************************************************************/
/**
Destroy all slaves.
*/
/** Destroy all slave configurations.
*/
void ec_master_destroy_slave_configs(ec_master_t *master)
{
ec_slave_config_t *sc, *next;
list_for_each_entry_safe(sc, next, &master->configs, list) {
list_del(&sc->list);
ec_slave_config_destroy(sc);
}
}
/*****************************************************************************/
/** Destroy all slaves.
*/
void ec_master_destroy_slaves(ec_master_t *master)
{
ec_slave_t *slave, *next_slave;
ec_slave_t *slave, *next;
list_for_each_entry_safe(slave, next_slave, &master->slaves, list) {
list_for_each_entry_safe(slave, next, &master->slaves, list) {
list_del(&slave->list);
ec_slave_destroy(slave);
}
@ -295,9 +312,9 @@ void ec_master_destroy_slaves(ec_master_t *master)
void ec_master_destroy_domains(ec_master_t *master)
{
ec_domain_t *domain, *next_d;
ec_domain_t *domain, *next;
list_for_each_entry_safe(domain, next_d, &master->domains, list) {
list_for_each_entry_safe(domain, next, &master->domains, list) {
list_del(&domain->list);
ec_domain_destroy(domain);
}
@ -467,8 +484,6 @@ int ec_master_enter_operation_mode(ec_master_t *master /**< EtherCAT master */)
EC_DBG("Switching to operation mode.\n");
master->mode = EC_MASTER_MODE_OPERATION;
master->pdo_slaves_offline = 0; // assume all PDO slaves online
master->frames_timed_out = 0;
master->ext_request_cb = NULL;
master->ext_release_cb = NULL;
master->ext_cb_data = NULL;
@ -505,9 +520,11 @@ void ec_master_leave_operation_mode(ec_master_t *master
master->release_cb = ec_master_release_cb;
master->cb_data = master;
ec_master_destroy_slave_configs(master);
ec_master_destroy_domains(master);
// set states for all slaves
list_for_each_entry(slave, &master->slaves, list) {
ec_slave_reset(slave);
ec_slave_request_state(slave, EC_SLAVE_STATE_PREOP);
}
#ifdef EC_EOE
@ -518,8 +535,6 @@ void ec_master_leave_operation_mode(ec_master_t *master
}
#endif
ec_master_destroy_domains(master);
if (ec_master_thread_start(master, ec_master_idle_thread))
EC_WARN("Failed to restart master thread!\n");
#ifdef EC_EOE
@ -1241,101 +1256,51 @@ void ec_master_prepare(ec_master_t *master /**< EtherCAT master */)
/*****************************************************************************/
/**
* Translates an ASCII coded bus-address to a slave pointer.
* These are the valid addressing schemes:
* - \a "X" = the Xth slave on the bus (ring position),
* - \a "#X" = the slave with alias X,
* - \a "#X:Y" = the Yth slave after the slave with alias X.
* X and Y are zero-based indices and may be provided in hexadecimal or octal
* notation (with appropriate prefix).
* \return pointer to the slave on success, else NULL
/** Detaches the slave configurations from the slaves.
*/
ec_slave_t *ec_master_parse_slave_address(
const ec_master_t *master, /**< EtherCAT master */
const char *address /**< address string */
void ec_master_detach_slave_configs(
ec_master_t *master /**< EtherCAT master. */
)
{
unsigned long first, second;
char *remainder, *remainder2;
const char *original;
unsigned int alias_requested = 0, alias_not_found = 1;
ec_slave_t *alias_slave = NULL, *slave;
ec_slave_config_t *sc;
original = address;
if (!master->configs_attached)
return;
if (!address[0])
goto out_invalid;
if (address[0] == '#') {
alias_requested = 1;
address++;
list_for_each_entry(sc, &master->configs, list) {
ec_slave_config_detach(sc);
}
first = simple_strtoul(address, &remainder, 0);
if (remainder == address)
goto out_invalid;
master->configs_attached = 0;
}
if (alias_requested) {
list_for_each_entry(alias_slave, &master->slaves, list) {
if (alias_slave->sii_alias == first) {
alias_not_found = 0;
break;
}
}
if (alias_not_found) {
EC_ERR("Alias not found!\n");
goto out_invalid;
}
/*****************************************************************************/
/** Attaches the slave configurations to the slaves.
*/
int ec_master_attach_slave_configs(
ec_master_t *master /**< EtherCAT master. */
)
{
ec_slave_config_t *sc;
unsigned int errors = 0;
if (master->configs_attached)
return 0;
list_for_each_entry(sc, &master->configs, list) {
if (ec_slave_config_attach(sc))
errors = 1;
}
if (!remainder[0]) {
if (alias_requested) { // alias addressing
return alias_slave;
}
else { // position addressing
list_for_each_entry(slave, &master->slaves, list) {
if (slave->ring_position == first) return slave;
}
EC_ERR("Slave index out of range!\n");
goto out_invalid;
}
}
else if (alias_requested && remainder[0] == ':') { // field addressing
struct list_head *list;
remainder++;
second = simple_strtoul(remainder, &remainder2, 0);
if (remainder2 == remainder || remainder2[0])
goto out_invalid;
list = &alias_slave->list;
while (second--) {
list = list->next;
if (list == &master->slaves) { // last slave exceeded
EC_ERR("Slave index out of range!\n");
goto out_invalid;
}
}
return list_entry(list, ec_slave_t, list);
}
out_invalid:
EC_ERR("Invalid slave address string \"%s\"!\n", original);
return NULL;
master->configs_attached = !errors;
return errors ? -1 : 0;
}
/******************************************************************************
* Realtime interface
*****************************************************************************/
/**
Creates a domain.
\return pointer to new domain on success, else NULL
\ingroup RealtimeInterface
*/
ec_domain_t *ecrt_master_create_domain(ec_master_t *master /**< master */)
{
ec_domain_t *domain, *last_domain;
@ -1364,25 +1329,16 @@ ec_domain_t *ecrt_master_create_domain(ec_master_t *master /**< master */)
/*****************************************************************************/
/**
Configures all slaves and leads them to the OP state.
Does the complete configuration and activation for all slaves. Sets sync
managers and FMMUs, and does the appropriate transitions, until the slave
is operational.
\return 0 in case of success, else < 0
\ingroup RealtimeInterface
*/
int ecrt_master_activate(ec_master_t *master /**< EtherCAT master */)
int ecrt_master_activate(ec_master_t *master)
{
uint32_t domain_offset;
ec_domain_t *domain;
// allocate all domains
// finish all domains
domain_offset = 0;
list_for_each_entry(domain, &master->domains, list) {
if (ec_domain_alloc(domain, domain_offset)) {
EC_ERR("Failed to allocate domain %X!\n", (u32) domain);
if (ec_domain_finish(domain, domain_offset)) {
EC_ERR("Failed to finish domain %X!\n", (u32) domain);
return -1;
}
domain_offset += domain->data_size;
@ -1435,12 +1391,7 @@ int ecrt_master_activate(ec_master_t *master /**< EtherCAT master */)
/*****************************************************************************/
/**
Asynchronous sending of datagrams.
\ingroup RealtimeInterface
*/
void ecrt_master_send(ec_master_t *master /**< EtherCAT master */)
void ecrt_master_send(ec_master_t *master)
{
ec_datagram_t *datagram, *n;
@ -1468,12 +1419,7 @@ void ecrt_master_send(ec_master_t *master /**< EtherCAT master */)
/*****************************************************************************/
/**
Asynchronous receiving of datagrams.
\ingroup RealtimeInterface
*/
void ecrt_master_receive(ec_master_t *master /**< EtherCAT master */)
void ecrt_master_receive(ec_master_t *master)
{
ec_datagram_t *datagram, *next;
cycles_t cycles_timeout;
@ -1510,80 +1456,65 @@ void ecrt_master_receive(ec_master_t *master /**< EtherCAT master */)
/*****************************************************************************/
/**
* Obtains a slave pointer by its bus address.
* A valid slave pointer is only returned, if vendor ID and product code are
* matching.
* \return pointer to the slave on success, else NULL
* \ingroup RealtimeInterface
*/
ec_slave_t *ecrt_master_get_slave(
const ec_master_t *master, /**< EtherCAT master */
const char *address, /**< address string
\see ec_master_parse_slave_address() */
uint32_t vendor_id, /**< vendor ID */
uint32_t product_code /**< product code */
)
ec_slave_config_t *ecrt_master_slave_config(ec_master_t *master,
uint16_t alias, uint16_t position, uint32_t vendor_id,
uint32_t product_code)
{
ec_slave_t *slave = ec_master_parse_slave_address(master, address);
if (!slave)
return NULL;
return ec_slave_validate(slave, vendor_id, product_code) ? NULL : slave;
}
/*****************************************************************************/
/**
* Obtains a slave pointer by its ring position.
* A valid slave pointer is only returned, if vendor ID and product code are
* matching.
* \return pointer to the slave on success, else NULL
* \ingroup RealtimeInterface
*/
ec_slave_t *ecrt_master_get_slave_by_pos(
const ec_master_t *master, /**< EtherCAT master */
uint16_t ring_position, /**< ring position */
uint32_t vendor_id, /**< vendor ID */
uint32_t product_code /**< product code */
)
{
ec_slave_t *slave;
ec_slave_config_t *sc;
unsigned int found = 0;
list_for_each_entry(slave, &master->slaves, list) {
if (slave->ring_position == ring_position) {
list_for_each_entry(sc, &master->configs, list) {
if (sc->alias == alias && sc->position == position) {
found = 1;
break;
}
}
if (!found) {
EC_ERR("Slave index out of range!\n");
return NULL;
if (found) {
if (master->debug_level) {
EC_INFO("Using existing slave configuration for %u:%u\n",
alias, position);
}
if (sc->vendor_id != vendor_id || sc->product_code != product_code) {
EC_ERR("Slave type mismatch. Slave was configured as"
" 0x%08X/0x%08X before. Now configuring with"
" 0x%08X/0x%08X.\n", sc->vendor_id, sc->product_code,
vendor_id, product_code);
return NULL;
}
} else {
if (master->debug_level) {
EC_INFO("Creating slave configuration for %u:%u,"
" 0x%08X/0x%08X.\n", alias, position, vendor_id,
product_code);
}
if (!(sc = (ec_slave_config_t *) kmalloc(sizeof(ec_slave_config_t),
GFP_KERNEL))) {
EC_ERR("Failed to allocate memory for slave configuration.\n");
return NULL;
}
if (ec_slave_config_init(sc, master, alias, position, vendor_id,
product_code)) {
EC_ERR("Failed to init slave configuration.\n");
return NULL;
}
// try to find the addressed slave
ec_slave_config_attach(sc);
ec_slave_config_load_default_mapping(sc);
list_add_tail(&sc->list, &master->configs);
}
return ec_slave_validate(slave, vendor_id, product_code) ? NULL : slave;
return sc;
}
/*****************************************************************************/
/**
Sets the locking callbacks.
The request_cb function must return zero, to allow another instance
(the EoE process for example) to access the master. Non-zero means,
that access is forbidden at this time.
\ingroup RealtimeInterface
*/
void ecrt_master_callbacks(ec_master_t *master, /**< EtherCAT master */
int (*request_cb)(void *), /**< request lock CB */
void (*release_cb)(void *), /**< release lock CB */
void *cb_data /**< data parameter */
)
void ecrt_master_callbacks(ec_master_t *master, int (*request_cb)(void *),
void (*release_cb)(void *), void *cb_data)
{
master->ext_request_cb = request_cb;
master->ext_release_cb = release_cb;
@ -1592,19 +1523,13 @@ void ecrt_master_callbacks(ec_master_t *master, /**< EtherCAT master */
/*****************************************************************************/
/**
* Reads the current master status.
*/
void ecrt_master_get_status(const ec_master_t *master, /**< EtherCAT master */
ec_master_status_t *status /**< target status object */
)
void ecrt_master_state(const ec_master_t *master, ec_master_state_t *state)
{
status->bus_status =
state->bus_state =
(master->pdo_slaves_offline || master->frames_timed_out)
? EC_BUS_FAILURE : EC_BUS_OK;
status->bus_tainted = master->fsm.tainted;
status->slaves_responding = master->fsm.slaves_responding;
state->bus_tainted = master->fsm.tainted;
state->slaves_responding = master->fsm.slaves_responding;
}
/*****************************************************************************/
@ -1616,9 +1541,8 @@ EXPORT_SYMBOL(ecrt_master_activate);
EXPORT_SYMBOL(ecrt_master_send);
EXPORT_SYMBOL(ecrt_master_receive);
EXPORT_SYMBOL(ecrt_master_callbacks);
EXPORT_SYMBOL(ecrt_master_get_slave);
EXPORT_SYMBOL(ecrt_master_get_slave_by_pos);
EXPORT_SYMBOL(ecrt_master_get_status);
EXPORT_SYMBOL(ecrt_master_slave_config);
EXPORT_SYMBOL(ecrt_master_state);
/** \endcond */

View File

@ -53,12 +53,9 @@
/*****************************************************************************/
/**
EtherCAT master mode.
*/
typedef enum
{
/** EtherCAT master mode.
*/
typedef enum {
EC_MASTER_MODE_ORPHANED,
EC_MASTER_MODE_IDLE,
EC_MASTER_MODE_OPERATION
@ -67,12 +64,9 @@ ec_master_mode_t;
/*****************************************************************************/
/**
Cyclic statistics.
*/
typedef struct
{
/** Cyclic statistics.
*/
typedef struct {
unsigned int timeouts; /**< datagram timeouts */
unsigned int corrupted; /**< corrupted frames */
unsigned int unmatched; /**< unmatched datagrams (received, but not
@ -83,11 +77,10 @@ ec_stats_t;
/*****************************************************************************/
/**
EtherCAT master.
Manages slaves, domains and IO.
*/
/** EtherCAT master.
*
* Manages slaves, domains and IO.
*/
struct ec_master
{
struct kobject kobj; /**< kobject */
@ -110,6 +103,9 @@ struct ec_master
struct list_head slaves; /**< list of slaves on the bus */
unsigned int slave_count; /**< number of slaves on the bus */
struct list_head configs; /**< Bus configuration list. */
unsigned int configs_attached; /**< Slave configurations were attached. */
ec_request_state_t scan_state; /**< current scanning state */
unsigned int allow_scan; /**< non-zero, if slave scanning is allowed */
@ -196,6 +192,7 @@ void ec_master_receive_datagrams(ec_master_t *, const uint8_t *, size_t);
void ec_master_queue_datagram(ec_master_t *, ec_datagram_t *);
// misc.
int ec_master_attach_slave_configs(ec_master_t *);
void ec_master_output_stats(ec_master_t *);
#ifdef EC_EOE
void ec_master_clear_eoe_handlers(ec_master_t *);

View File

@ -31,10 +31,9 @@
*
*****************************************************************************/
/**
\file
EtherCAT master driver module.
*/
/** \file
* EtherCAT master driver module.
*/
/*****************************************************************************/
@ -49,7 +48,7 @@
/*****************************************************************************/
#define MAX_MASTERS 5 /**< maximum number of masters */
#define MAX_MASTERS 5 /**< Maximum number of masters. */
/*****************************************************************************/
@ -541,28 +540,7 @@ void ecdev_close(ec_device_t *device /**< EtherCAT device */)
* Realtime interface
*****************************************************************************/
/**
* Returns the version magic of the realtime interface.
* \return ECRT version magic.
* \ingroup RealtimeInterface
*/
unsigned int ecrt_version_magic(void)
{
return ECRT_VERSION_MAGIC;
}
/*****************************************************************************/
/**
Reserves an EtherCAT master for realtime operation.
\return pointer to reserved master, or NULL on error
\ingroup RealtimeInterface
*/
ec_master_t *ecrt_request_master(unsigned int master_index
/**< master index */
)
ec_master_t *ecrt_request_master(unsigned int master_index)
{
ec_master_t *master;
@ -622,12 +600,7 @@ ec_master_t *ecrt_request_master(unsigned int master_index
/*****************************************************************************/
/**
Releases a reserved EtherCAT master.
\ingroup RealtimeInterface
*/
void ecrt_release_master(ec_master_t *master /**< EtherCAT master */)
void ecrt_release_master(ec_master_t *master)
{
EC_INFO("Releasing master %u...\n", master->index);
@ -646,6 +619,13 @@ void ecrt_release_master(ec_master_t *master /**< EtherCAT master */)
/*****************************************************************************/
unsigned int ecrt_version_magic(void)
{
return ECRT_VERSION_MAGIC;
}
/*****************************************************************************/
/** \cond */
module_init(ec_init_module);

View File

@ -44,66 +44,196 @@
/*****************************************************************************/
/**
* PDO constructor.
*/
void ec_pdo_clear_entries(ec_pdo_t *);
void ec_pdo_init(ec_pdo_t *pdo /**< EtherCAT PDO */)
/*****************************************************************************/
/** PDO constructor.
*/
void ec_pdo_init(
ec_pdo_t *pdo /**< EtherCAT PDO */
)
{
pdo->sync_index = -1; // not assigned
pdo->name = NULL;
INIT_LIST_HEAD(&pdo->entries);
}
/*****************************************************************************/
/**
* PDO destructor.
/** Pdo copy constructor.
*/
int ec_pdo_init_copy(ec_pdo_t *pdo, const ec_pdo_t *other_pdo)
{
pdo->dir = other_pdo->dir;
pdo->index = other_pdo->index;
pdo->sync_index = other_pdo->sync_index;
pdo->name = NULL;
INIT_LIST_HEAD(&pdo->entries);
if (ec_pdo_set_name(pdo, other_pdo->name))
goto out_return;
if (ec_pdo_copy_entries(pdo, other_pdo))
goto out_clear;
return 0;
out_clear:
ec_pdo_clear(pdo);
out_return:
return -1;
}
/*****************************************************************************/
/** PDO destructor.
*/
void ec_pdo_clear(ec_pdo_t *pdo /**< EtherCAT PDO */)
{
if (pdo->name)
kfree(pdo->name);
ec_pdo_clear_entries(pdo);
}
/*****************************************************************************/
/** Clear Pdo entry list.
*/
void ec_pdo_clear_entries(ec_pdo_t *pdo /**< EtherCAT PDO */)
{
ec_pdo_entry_t *entry, *next;
// free all PDO entries
list_for_each_entry_safe(entry, next, &pdo->entries, list) {
list_del(&entry->list);
ec_pdo_entry_clear(entry);
kfree(entry);
}
}
/*****************************************************************************/
/**
* Makes a deep copy of a PDO.
/** Set Pdo name.
*/
int ec_pdo_copy(ec_pdo_t *pdo, const ec_pdo_t *other_pdo)
int ec_pdo_set_name(
ec_pdo_t *pdo, /**< Pdo. */
const char *name /**< New name. */
)
{
ec_pdo_entry_t *entry, *other_entry, *next;
unsigned int len;
// make flat copy
*pdo = *other_pdo;
if (pdo->name)
kfree(pdo->name);
INIT_LIST_HEAD(&pdo->entries);
list_for_each_entry(other_entry, &other_pdo->entries, list) {
if (name && (len = strlen(name))) {
if (!(pdo->name = (char *) kmalloc(len + 1, GFP_KERNEL))) {
EC_ERR("Failed to allocate PDO name.\n");
return -1;
}
memcpy(pdo->name, name, len + 1);
} else {
pdo->name = NULL;
}
return 0;
}
/*****************************************************************************/
/** Copy Pdo entries from another Pdo.
*/
int ec_pdo_copy_entries(ec_pdo_t *pdo, const ec_pdo_t *other)
{
ec_pdo_entry_t *entry, *other_entry;
ec_pdo_clear_entries(pdo);
list_for_each_entry(other_entry, &other->entries, list) {
if (!(entry = (ec_pdo_entry_t *)
kmalloc(sizeof(ec_pdo_entry_t), GFP_KERNEL))) {
EC_ERR("Failed to allocate memory for PDO entry copy.\n");
goto out_free;
return -1;
}
if (ec_pdo_entry_init_copy(entry, other_entry)) {
kfree(entry);
return -1;
}
*entry = *other_entry; // flat copy is sufficient
list_add_tail(&entry->list, &pdo->entries);
}
return 0;
out_free:
list_for_each_entry_safe(entry, next, &pdo->entries, list) {
list_del(&entry->list);
kfree(entry);
}
return -1;
}
/*****************************************************************************/
/** Pdo entry constructor.
*/
void ec_pdo_entry_init(
ec_pdo_entry_t *entry /**< Pdo entry. */
)
{
entry->name = NULL;
}
/*****************************************************************************/
/** Pdo entry copy constructor.
*/
int ec_pdo_entry_init_copy(
ec_pdo_entry_t *entry, /**< Pdo entry. */
const ec_pdo_entry_t *other /**< Pdo entry to copy from. */
)
{
entry->index = other->index;
entry->subindex = other->subindex;
entry->name = NULL;
entry->bit_length = other->bit_length;
if (ec_pdo_entry_set_name(entry, other->name))
return -1;
return 0;
}
/*****************************************************************************/
/** Pdo entry destructor.
*/
void ec_pdo_entry_clear(ec_pdo_entry_t *entry /**< Pdo entry. */)
{
if (entry->name)
kfree(entry->name);
}
/*****************************************************************************/
/** Set Pdo entry name.
*/
int ec_pdo_entry_set_name(
ec_pdo_entry_t *entry, /**< Pdo entry. */
const char *name /**< New name. */
)
{
unsigned int len;
if (entry->name)
kfree(entry->name);
if (name && (len = strlen(name))) {
if (!(entry->name = (char *) kmalloc(len + 1, GFP_KERNEL))) {
EC_ERR("Failed to allocate PDO entry name.\n");
return -1;
}
memcpy(entry->name, name, len + 1);
} else {
entry->name = NULL;
}
return 0;
}
/*****************************************************************************/

View File

@ -43,59 +43,47 @@
#include <linux/list.h>
#include "../include/ecrt.h"
#include "globals.h"
/*****************************************************************************/
/**
* PDO type.
/** PDO description.
*/
typedef enum
{
EC_RX_PDO, /**< Reveive PDO */
EC_TX_PDO /**< Transmit PDO */
}
ec_pdo_type_t;
typedef struct {
struct list_head list; /**< List item. */
ec_direction_t dir; /**< PDO direction. */
uint16_t index; /**< PDO index. */
int8_t sync_index; /**< Assigned sync manager. */
char *name; /**< PDO name. */
struct list_head entries; /**< List of PDO entries. */
} ec_pdo_t;
/*****************************************************************************/
/**
* PDO description.
/** PDO entry description.
*/
typedef struct
{
struct list_head list; /**< list item */
ec_pdo_type_t type; /**< PDO type */
uint16_t index; /**< PDO index */
int8_t sync_index; /**< assigned sync manager */
char *name; /**< PDO name */
struct list_head entries; /**< entry list */
}
ec_pdo_t;
/*****************************************************************************/
/**
* PDO entry description.
*/
typedef struct
{
typedef struct {
struct list_head list; /**< list item */
uint16_t index; /**< PDO entry index */
uint8_t subindex; /**< PDO entry subindex */
char *name; /**< entry name */
uint8_t bit_length; /**< entry length in bit */
}
ec_pdo_entry_t;
} ec_pdo_entry_t;
/*****************************************************************************/
void ec_pdo_init(ec_pdo_t *);
int ec_pdo_init_copy(ec_pdo_t *, const ec_pdo_t *);
void ec_pdo_clear(ec_pdo_t *);
int ec_pdo_copy(ec_pdo_t *, const ec_pdo_t *);
int ec_pdo_set_name(ec_pdo_t *, const char *);
int ec_pdo_copy_entries(ec_pdo_t *, const ec_pdo_t *);
void ec_pdo_entry_init(ec_pdo_entry_t *);
int ec_pdo_entry_init_copy(ec_pdo_entry_t *, const ec_pdo_entry_t *);
void ec_pdo_entry_clear(ec_pdo_entry_t *);
int ec_pdo_entry_set_name(ec_pdo_entry_t *, const char *);
/*****************************************************************************/

319
master/pdo_mapping.c Normal file
View File

@ -0,0 +1,319 @@
/******************************************************************************
*
* $Id$
*
* Copyright (C) 2006 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
* as published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* 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 right to use EtherCAT Technology is granted and comes free of
* charge under condition of compatibility of product made by
* Licensee. People intending to distribute/sell products based on the
* code, have to sign an agreement to guarantee that products using
* software based on IgH EtherCAT master stay compatible with the actual
* EtherCAT specification (which are released themselves as an open
* standard) as the (only) precondition to have the right to use EtherCAT
* Technology, IP and trade marks.
*
*****************************************************************************/
/**
\file
EtherCAT Pdo mapping methods.
*/
/*****************************************************************************/
#include <linux/module.h>
#include "globals.h"
#include "pdo.h"
#include "slave_config.h"
#include "master.h"
#include "pdo_mapping.h"
/*****************************************************************************/
/** Pdo mapping constructor.
*/
void ec_pdo_mapping_init(
ec_pdo_mapping_t *pm /**< Pdo mapping. */
)
{
INIT_LIST_HEAD(&pm->pdos);
pm->default_mapping = 1;
}
/*****************************************************************************/
/** Pdo mapping destructor.
*/
void ec_pdo_mapping_clear(ec_pdo_mapping_t *pm /**< Pdo mapping. */)
{
ec_pdo_mapping_clear_pdos(pm);
}
/*****************************************************************************/
/** Clears the list of mapped Pdos.
*/
void ec_pdo_mapping_clear_pdos(ec_pdo_mapping_t *pm /**< Pdo mapping. */)
{
ec_pdo_t *pdo, *next;
list_for_each_entry_safe(pdo, next, &pm->pdos, list) {
list_del_init(&pdo->list);
ec_pdo_clear(pdo);
kfree(pdo);
}
}
/*****************************************************************************/
/** Calculates the total size of the mapped PDO entries.
*
* \retval Data size in byte.
*/
uint16_t ec_pdo_mapping_total_size(
const ec_pdo_mapping_t *pm /**< Pdo mapping. */
)
{
unsigned int bit_size;
const ec_pdo_t *pdo;
const ec_pdo_entry_t *pdo_entry;
uint16_t byte_size;
bit_size = 0;
list_for_each_entry(pdo, &pm->pdos, list) {
list_for_each_entry(pdo_entry, &pdo->entries, list) {
bit_size += pdo_entry->bit_length;
}
}
if (bit_size % 8) // round up to full bytes
byte_size = bit_size / 8 + 1;
else
byte_size = bit_size / 8;
return byte_size;
}
/*****************************************************************************/
/** Adds a Pdo to the mapping.
*
* \return 0 on success, else < 0
*/
int ec_pdo_mapping_add_pdo(
ec_pdo_mapping_t *pm, /**< Pdo mapping. */
const ec_pdo_t *pdo /**< PDO to add. */
)
{
ec_pdo_t *mapped_pdo;
// PDO already mapped?
list_for_each_entry(mapped_pdo, &pm->pdos, list) {
if (mapped_pdo->index != pdo->index) continue;
EC_ERR("PDO 0x%04X is already mapped!\n", pdo->index);
return -1;
}
if (!(mapped_pdo = kmalloc(sizeof(ec_pdo_t), GFP_KERNEL))) {
EC_ERR("Failed to allocate memory for PDO mapping.\n");
return -1;
}
if (ec_pdo_init_copy(mapped_pdo, pdo)) {
kfree(mapped_pdo);
return -1;
}
list_add_tail(&mapped_pdo->list, &pm->pdos);
return 0;
}
/*****************************************************************************/
/** Add a Pdo to the mapping.
*
* The first call of this method will clear the default mapping.
*
* \retval 0 Success.
* \retval -1 Error.
*/
int ec_pdo_mapping_add_pdo_info(
ec_pdo_mapping_t *pm, /**< Pdo mapping. */
const ec_pdo_info_t *pdo_info, /**< Pdo information. */
const ec_slave_config_t *config /**< Slave configuration, to load
default entries. */
)
{
unsigned int i;
ec_pdo_t *pdo;
ec_pdo_entry_t *entry;
const ec_pdo_entry_info_t *entry_info;
if (pm->default_mapping) {
pm->default_mapping = 0;
ec_pdo_mapping_clear_pdos(pm);
}
if (!(pdo = (ec_pdo_t *) kmalloc(sizeof(ec_pdo_t), GFP_KERNEL))) {
EC_ERR("Failed to allocate memory for Pdo.\n");
goto out_return;
}
ec_pdo_init(pdo);
pdo->dir = pdo_info->dir;
pdo->index = pdo_info->index;
if (config->master->debug_level)
EC_INFO("Adding Pdo 0x%04X to mapping.\n", pdo->index);
if (pdo_info->n_entries) { // Pdo configuration provided
if (config->master->debug_level)
EC_INFO(" Pdo configuration provided.\n");
for (i = 0; i < pdo_info->n_entries; i++) {
entry_info = &pdo_info->entries[i];
if (!(entry = kmalloc(sizeof(ec_pdo_entry_t), GFP_KERNEL))) {
EC_ERR("Failed to allocate memory for PDO entry.\n");
goto out_free;
}
ec_pdo_entry_init(entry);
entry->index = entry_info->index;
entry->subindex = entry_info->subindex;
entry->bit_length = entry_info->bit_length;
list_add_tail(&entry->list, &pdo->entries);
}
} else { // use default Pdo configuration
if (config->master->debug_level)
EC_INFO(" Using default Pdo configuration.\n");
if (config->slave) {
ec_sync_t *sync;
ec_pdo_t *default_pdo;
if ((sync = ec_slave_get_pdo_sync(config->slave, pdo->dir))) {
list_for_each_entry(default_pdo, &sync->mapping.pdos, list) {
if (default_pdo->index != pdo->index)
continue;
if (config->master->debug_level)
EC_INFO(" Found Pdo name \"%s\".\n",
default_pdo->name);
// try to take Pdo name from mapped one
if (ec_pdo_set_name(pdo, default_pdo->name))
goto out_free;
// copy entries (= default Pdo configuration)
if (ec_pdo_copy_entries(pdo, default_pdo))
goto out_free;
if (config->master->debug_level) {
const ec_pdo_entry_t *entry;
list_for_each_entry(entry, &pdo->entries, list) {
EC_INFO(" Entry 0x%04X:%u.\n",
entry->index, entry->subindex);
}
}
}
} else {
EC_WARN("Slave %u does not provide a default Pdo"
" configuration!\n", config->slave->ring_position);
}
} else {
EC_WARN("Failed to load default Pdo configuration for %u:%u:"
" Slave not found.\n", config->alias, config->position);
}
}
list_add_tail(&pdo->list, &pm->pdos);
return 0;
out_free:
ec_pdo_clear(pdo);
kfree(pdo);
out_return:
return -1;
}
/*****************************************************************************/
/** Makes a deep copy of another Pdo mapping.
*
* \return 0 on success, else < 0
*/
int ec_pdo_mapping_copy(
ec_pdo_mapping_t *pm, /**< Pdo mapping. */
const ec_pdo_mapping_t *other /**< PDO mapping to copy from. */
)
{
ec_pdo_t *other_pdo;
ec_pdo_mapping_clear_pdos(pm);
// PDO already mapped?
list_for_each_entry(other_pdo, &other->pdos, list) {
if (ec_pdo_mapping_add_pdo(pm, other_pdo))
return -1;
}
return 0;
}
/*****************************************************************************/
/** Compares two Pdo mappings.
*
* Only the mapping is compared, not the Pdo entries (i. e. the Pdo
* configuration).
*
* \retval 1 The given Pdo mappings are equal.
* \retval 0 The given Pdo mappings differ.
*/
int ec_pdo_mapping_equal(
const ec_pdo_mapping_t *pm1, /**< First mapping. */
const ec_pdo_mapping_t *pm2 /**< Second mapping. */
)
{
const struct list_head *h1, *h2, *l1, *l2;
const ec_pdo_t *p1, *p2;
h1 = l1 = &pm1->pdos;
h2 = l2 = &pm2->pdos;
while (1) {
l1 = l1->next;
l2 = l2->next;
if ((l1 == h1) ^ (l2 == h2)) // unequal lengths
return 0;
if (l1 == h1 && l2 == h2) // both finished
break;
p1 = list_entry(l1, ec_pdo_t, list);
p2 = list_entry(l2, ec_pdo_t, list);
if (p1->index != p2->index)
return 0;
}
return 1;
}
/*****************************************************************************/

77
master/pdo_mapping.h Normal file
View File

@ -0,0 +1,77 @@
/******************************************************************************
*
* $Id$
*
* Copyright (C) 2006 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
* as published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* 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 right to use EtherCAT Technology is granted and comes free of
* charge under condition of compatibility of product made by
* Licensee. People intending to distribute/sell products based on the
* code, have to sign an agreement to guarantee that products using
* software based on IgH EtherCAT master stay compatible with the actual
* EtherCAT specification (which are released themselves as an open
* standard) as the (only) precondition to have the right to use EtherCAT
* Technology, IP and trade marks.
*
*****************************************************************************/
/**
\file
EtherCAT Pdo mapping structure.
*/
/*****************************************************************************/
#ifndef _EC_PDO_MAPPING_H_
#define _EC_PDO_MAPPING_H_
#include <linux/list.h>
#include "../include/ecrt.h"
#include "globals.h"
/*****************************************************************************/
/** EtherCAT PDO mapping.
*/
typedef struct {
struct list_head pdos; /**< List of Pdos. */
unsigned int default_mapping; /**< This is the default mapping. */
} ec_pdo_mapping_t;
/*****************************************************************************/
void ec_pdo_mapping_init(ec_pdo_mapping_t *);
void ec_pdo_mapping_clear(ec_pdo_mapping_t *);
void ec_pdo_mapping_clear_pdos(ec_pdo_mapping_t *);
int ec_pdo_mapping_add_pdo(ec_pdo_mapping_t *, const ec_pdo_t *);
int ec_pdo_mapping_add_pdo_info(ec_pdo_mapping_t *, const ec_pdo_info_t *,
const ec_slave_config_t *);
int ec_pdo_mapping_copy(ec_pdo_mapping_t *, const ec_pdo_mapping_t *);
uint16_t ec_pdo_mapping_total_size(const ec_pdo_mapping_t *);
int ec_pdo_mapping_equal(const ec_pdo_mapping_t *, const ec_pdo_mapping_t *);
/*****************************************************************************/
#endif

View File

@ -42,9 +42,11 @@
#include <linux/delay.h>
#include "globals.h"
#include "slave.h"
#include "datagram.h"
#include "master.h"
#include "slave_config.h"
#include "slave.h"
/*****************************************************************************/
@ -113,13 +115,12 @@ int ec_slave_init(ec_slave_t *slave, /**< EtherCAT slave */
slave->master = master;
slave->config = NULL;
slave->requested_state = EC_SLAVE_STATE_PREOP;
slave->current_state = EC_SLAVE_STATE_UNKNOWN;
slave->online_state = EC_SLAVE_ONLINE;
slave->self_configured = 0;
slave->error_flag = 0;
slave->online_state = EC_SLAVE_ONLINE;
slave->fmmu_count = 0;
slave->pdos_registered = 0;
slave->base_type = 0;
slave->base_revision = 0;
@ -151,7 +152,6 @@ int ec_slave_init(ec_slave_t *slave, /**< EtherCAT slave */
slave->sii_sync_count = 0;
INIT_LIST_HEAD(&slave->sii_pdos);
INIT_LIST_HEAD(&slave->sdo_dictionary);
INIT_LIST_HEAD(&slave->sdo_confs);
slave->sdo_dictionary_fetched = 0;
slave->pdo_mapping_fetched = 0;
@ -213,6 +213,9 @@ void ec_slave_destroy(ec_slave_t *slave /**< EtherCAT slave */)
{
ec_sdo_t *sdo, *next_sdo;
if (slave->config)
ec_slave_config_detach(slave->config);
// free all SDOs
list_for_each_entry_safe(sdo, next_sdo, &slave->sdo_dictionary, list) {
list_del(&sdo->list);
@ -240,7 +243,6 @@ void ec_slave_clear(struct kobject *kobj /**< kobject of the slave */)
{
ec_slave_t *slave;
ec_pdo_t *pdo, *next_pdo;
ec_sdo_data_t *sdodata, *next_sdodata;
unsigned int i;
slave = container_of(kobj, ec_slave_t, kobj);
@ -267,13 +269,6 @@ void ec_slave_clear(struct kobject *kobj /**< kobject of the slave */)
kfree(pdo);
}
// free all SDO configurations
list_for_each_entry_safe(sdodata, next_sdodata, &slave->sdo_confs, list) {
list_del(&sdodata->list);
kfree(sdodata->data);
kfree(sdodata);
}
if (slave->eeprom_data) kfree(slave->eeprom_data);
kfree(slave);
@ -291,33 +286,6 @@ void ec_slave_sdos_clear(struct kobject *kobj /**< kobject for SDOs */)
/*****************************************************************************/
/**
Reset slave from operation mode.
*/
void ec_slave_reset(ec_slave_t *slave /**< EtherCAT slave */)
{
ec_sdo_data_t *sdodata, *next_sdodata;
unsigned int i;
slave->fmmu_count = 0;
slave->pdos_registered = 0;
// free all SDO configurations
list_for_each_entry_safe(sdodata, next_sdodata, &slave->sdo_confs, list) {
list_del(&sdodata->list);
kfree(sdodata->data);
kfree(sdodata);
}
// remove estimated sync manager sizes
for (i = 0; i < slave->sii_sync_count; i++) {
slave->sii_syncs[i].est_length = 0;
}
}
/*****************************************************************************/
/**
* Sets the application state of a slave.
*/
@ -351,16 +319,12 @@ void ec_slave_set_online_state(ec_slave_t *slave, /**< EtherCAT slave */
{
if (new_state == EC_SLAVE_OFFLINE &&
slave->online_state == EC_SLAVE_ONLINE) {
if (slave->pdos_registered)
slave->master->pdo_slaves_offline++;
if (slave->master->debug_level)
EC_DBG("Slave %u: offline.\n", slave->ring_position);
}
else if (new_state == EC_SLAVE_ONLINE &&
slave->online_state == EC_SLAVE_OFFLINE) {
slave->error_flag = 0; // clear error flag
if (slave->pdos_registered)
slave->master->pdo_slaves_offline--;
if (slave->master->debug_level) {
char cur_state[EC_STATE_STRING_SIZE];
ec_state_string(slave->current_state, cur_state);
@ -534,7 +498,7 @@ int ec_slave_fetch_sii_pdos(
ec_slave_t *slave, /**< EtherCAT slave */
const uint8_t *data, /**< category data */
size_t data_size, /**< number of bytes */
ec_pdo_type_t pdo_type /**< PDO type */
ec_direction_t dir /**< PDO direction. */
)
{
ec_pdo_t *pdo;
@ -548,11 +512,16 @@ int ec_slave_fetch_sii_pdos(
}
ec_pdo_init(pdo);
pdo->type = pdo_type;
pdo->dir = dir;
pdo->index = EC_READ_U16(data);
entry_count = EC_READ_U8(data + 2);
pdo->sync_index = EC_READ_U8(data + 3);
pdo->name = ec_slave_sii_string(slave, EC_READ_U8(data + 5));
if (ec_pdo_set_name(pdo,
ec_slave_sii_string(slave, EC_READ_U8(data + 5)))) {
ec_pdo_clear(pdo);
kfree(pdo);
return -1;
}
list_add_tail(&pdo->list, &slave->sii_pdos);
data_size -= 8;
@ -564,9 +533,15 @@ int ec_slave_fetch_sii_pdos(
return -1;
}
ec_pdo_entry_init(entry);
entry->index = EC_READ_U16(data);
entry->subindex = EC_READ_U8(data + 2);
entry->name = ec_slave_sii_string(slave, EC_READ_U8(data + 3));
if (ec_pdo_entry_set_name(entry,
ec_slave_sii_string(slave, EC_READ_U8(data + 3)))) {
ec_pdo_entry_clear(entry);
kfree(entry);
return -1;
}
entry->bit_length = EC_READ_U8(data + 5);
list_add_tail(&entry->list, &pdo->entries);
@ -577,7 +552,6 @@ int ec_slave_fetch_sii_pdos(
// if sync manager index is positive, the PDO is mapped by default
if (pdo->sync_index >= 0) {
ec_sync_t *sync;
ec_pdo_t *mapped_pdo;
if (pdo->sync_index >= slave->sii_sync_count) {
EC_ERR("Invalid SM index %i for PDO 0x%04X in slave %u.",
@ -586,18 +560,9 @@ int ec_slave_fetch_sii_pdos(
}
sync = &slave->sii_syncs[pdo->sync_index];
if (!(mapped_pdo = kmalloc(sizeof(ec_pdo_t), GFP_KERNEL))) {
EC_ERR("Failed to allocate PDO memory.\n");
if (ec_pdo_mapping_add_pdo(&sync->mapping, pdo))
return -1;
}
if (ec_pdo_copy(mapped_pdo, pdo)) {
EC_ERR("Failed to copy PDO.\n");
kfree(mapped_pdo);
return -1;
}
list_add_tail(&mapped_pdo->list, &sync->pdos);
sync->mapping_source = EC_SYNC_MAPPING_SII;
}
}
@ -632,61 +597,8 @@ char *ec_slave_sii_string(
/*****************************************************************************/
/**
* Prepares an FMMU configuration.
* Configuration data for the FMMU is saved in the slave structure and is
* written to the slave in ecrt_master_activate().
* The FMMU configuration is done in a way, that the complete data range
* of the corresponding sync manager is covered. Seperate FMMUs are configured
* for each domain.
* If the FMMU configuration is already prepared, the function returns with
* success.
* \return 0 in case of success, else < 0
/** Outputs all information about a certain slave.
*/
int ec_slave_prepare_fmmu(
ec_slave_t *slave, /**< EtherCAT slave */
const ec_domain_t *domain, /**< domain */
const ec_sync_t *sync /**< sync manager */
)
{
unsigned int i;
ec_fmmu_t *fmmu;
// FMMU configuration already prepared?
for (i = 0; i < slave->fmmu_count; i++) {
fmmu = &slave->fmmus[i];
if (fmmu->domain == domain && fmmu->sync == sync)
return 0;
}
// reserve new FMMU...
if (slave->fmmu_count >= slave->base_fmmu_count) {
EC_ERR("Slave %u FMMU limit reached!\n", slave->ring_position);
return -1;
}
fmmu = &slave->fmmus[slave->fmmu_count];
ec_fmmu_init(fmmu, slave, slave->fmmu_count++);
fmmu->domain = domain;
fmmu->sync = sync;
fmmu->logical_start_address = 0;
slave->pdos_registered = 1;
ec_slave_request_state(slave, EC_SLAVE_STATE_OP);
return 0;
}
/*****************************************************************************/
/**
Outputs all information about a certain slave.
*/
ssize_t ec_slave_info(const ec_slave_t *slave, /**< EtherCAT slave */
char *buffer /**< Output buffer */
)
@ -695,8 +607,6 @@ ssize_t ec_slave_info(const ec_slave_t *slave, /**< EtherCAT slave */
ec_pdo_t *pdo;
ec_pdo_entry_t *pdo_entry;
int first, i;
ec_sdo_data_t *sdodata;
char str[20];
char *large_buffer, *buf;
unsigned int size;
@ -713,9 +623,7 @@ ssize_t ec_slave_info(const ec_slave_t *slave, /**< EtherCAT slave */
buf += sprintf(buf, " (");
buf += ec_state_string(slave->requested_state, buf);
buf += sprintf(buf, ")\n");
buf += sprintf(buf, "Flags: %s, %s\n\n",
slave->online_state == EC_SLAVE_ONLINE ? "online" : "OFFLINE",
slave->error_flag ? "ERROR" : "ok");
buf += sprintf(buf, "Flags: %s\n\n", slave->error_flag ? "ERROR" : "ok");
buf += sprintf(buf, "Data link status:\n");
for (i = 0; i < 4; i++) {
@ -821,10 +729,10 @@ ssize_t ec_slave_info(const ec_slave_t *slave, /**< EtherCAT slave */
buf += sprintf(buf,
" SM%u: addr 0x%04X, size %u, control 0x%02X, %s\n",
sync->index, sync->physical_start_address,
ec_sync_size(sync), sync->control_register,
sync->length, sync->control_register,
sync->enable ? "enable" : "disable");
if (list_empty(&sync->pdos)) {
if (list_empty(&sync->mapping.pdos)) {
buf += sprintf(buf, " No PDOs mapped.\n");
} else if (sync->mapping_source != EC_SYNC_MAPPING_NONE) {
buf += sprintf(buf,
@ -833,9 +741,9 @@ ssize_t ec_slave_info(const ec_slave_t *slave, /**< EtherCAT slave */
? "SII" : "CoE");
}
list_for_each_entry(pdo, &sync->pdos, list) {
list_for_each_entry(pdo, &sync->mapping.pdos, list) {
buf += sprintf(buf, " %s 0x%04X \"%s\"\n",
pdo->type == EC_RX_PDO ? "RxPdo" : "TxPdo",
pdo->dir == EC_DIR_OUTPUT ? "RxPdo" : "TxPdo",
pdo->index, pdo->name ? pdo->name : "???");
list_for_each_entry(pdo_entry, &pdo->entries, list) {
@ -856,7 +764,7 @@ ssize_t ec_slave_info(const ec_slave_t *slave, /**< EtherCAT slave */
list_for_each_entry(pdo, &slave->sii_pdos, list) {
buf += sprintf(buf, " %s 0x%04X \"%s\"",
pdo->type == EC_RX_PDO ? "RxPdo" : "TxPdo",
pdo->dir == EC_DIR_OUTPUT ? "RxPdo" : "TxPdo",
pdo->index, pdo->name ? pdo->name : "???");
if (pdo->sync_index >= 0)
buf += sprintf(buf, ", default mapping: SM%u.\n",
@ -874,23 +782,6 @@ ssize_t ec_slave_info(const ec_slave_t *slave, /**< EtherCAT slave */
buf += sprintf(buf, "\n");
}
// type-cast to avoid warnings on some compilers
if (!list_empty((struct list_head *) &slave->sdo_confs)) {
buf += sprintf(buf, "SDO configurations:\n");
list_for_each_entry(sdodata, &slave->sdo_confs, list) {
switch (sdodata->size) {
case 1: sprintf(str, "%u", EC_READ_U8(sdodata->data)); break;
case 2: sprintf(str, "%u", EC_READ_U16(sdodata->data)); break;
case 4: sprintf(str, "%u", EC_READ_U32(sdodata->data)); break;
default: sprintf(str, "(invalid size)"); break;
}
buf += sprintf(buf, " 0x%04X:%-3i -> %s\n",
sdodata->index, sdodata->subindex, str);
}
buf += sprintf(buf, "\n");
}
size = buf - large_buffer;
if (size >= PAGE_SIZE) {
const char trunc[] = "\n---TRUNCATED---\n";
@ -1251,47 +1142,6 @@ ec_sync_t *ec_slave_get_pdo_sync(
\return 0 in case of success, else < 0
*/
int ec_slave_conf_sdo(ec_slave_t *slave, /**< EtherCAT slave */
uint16_t sdo_index, /**< SDO index */
uint8_t sdo_subindex, /**< SDO subindex */
const uint8_t *data, /**< SDO data */
size_t size /**< SDO size in bytes */
)
{
ec_sdo_data_t *sdodata;
if (!(slave->sii_mailbox_protocols & EC_MBOX_COE)) {
EC_ERR("Slave %u does not support CoE!\n", slave->ring_position);
return -1;
}
if (!(sdodata = (ec_sdo_data_t *)
kmalloc(sizeof(ec_sdo_data_t), GFP_KERNEL))) {
EC_ERR("Failed to allocate memory for SDO configuration object!\n");
return -1;
}
if (!(sdodata->data = (uint8_t *) kmalloc(size, GFP_KERNEL))) {
EC_ERR("Failed to allocate memory for SDO configuration data!\n");
kfree(sdodata);
return -1;
}
sdodata->index = sdo_index;
sdodata->subindex = sdo_subindex;
memcpy(sdodata->data, data, size);
sdodata->size = size;
list_add_tail(&sdodata->list, &slave->sdo_confs);
return 0;
}
/*****************************************************************************/
/**
\return 0 in case of success, else < 0
*/
int ec_slave_validate(const ec_slave_t *slave, /**< EtherCAT slave */
uint32_t vendor_id, /**< vendor ID */
uint32_t product_code /**< product code */
@ -1357,184 +1207,4 @@ ec_sdo_t *ec_slave_get_sdo(
return NULL;
}
/******************************************************************************
* Realtime interface
*****************************************************************************/
/**
\return 0 in case of success, else < 0
\ingroup RealtimeInterface
*/
int ecrt_slave_conf_sdo8(ec_slave_t *slave, /**< EtherCAT slave */
uint16_t sdo_index, /**< SDO index */
uint8_t sdo_subindex, /**< SDO subindex */
uint8_t value /**< new SDO value */
)
{
uint8_t data[1];
EC_WRITE_U8(data, value);
return ec_slave_conf_sdo(slave, sdo_index, sdo_subindex, data, 1);
}
/*****************************************************************************/
/**
\return 0 in case of success, else < 0
\ingroup RealtimeInterface
*/
int ecrt_slave_conf_sdo16(ec_slave_t *slave, /**< EtherCAT slave */
uint16_t sdo_index, /**< SDO index */
uint8_t sdo_subindex, /**< SDO subindex */
uint16_t value /**< new SDO value */
)
{
uint8_t data[2];
EC_WRITE_U16(data, value);
return ec_slave_conf_sdo(slave, sdo_index, sdo_subindex, data, 2);
}
/*****************************************************************************/
/**
\return 0 in case of success, else < 0
\ingroup RealtimeInterface
*/
int ecrt_slave_conf_sdo32(ec_slave_t *slave, /**< EtherCAT slave */
uint16_t sdo_index, /**< SDO index */
uint8_t sdo_subindex, /**< SDO subindex */
uint32_t value /**< new SDO value */
)
{
uint8_t data[4];
EC_WRITE_U32(data, value);
return ec_slave_conf_sdo(slave, sdo_index, sdo_subindex, data, 4);
}
/*****************************************************************************/
/**
* Clear slave's PDO mapping.
*/
void ecrt_slave_pdo_mapping_clear(
ec_slave_t *slave, /**< EtherCAT slave */
ec_direction_t dir /**< output/input */
)
{
ec_sync_t *sync;
if (!(slave->sii_mailbox_protocols & EC_MBOX_COE)) {
EC_ERR("Slave %u does not support CoE!\n", slave->ring_position);
return;
}
if (!(sync = ec_slave_get_pdo_sync(slave, dir)))
return;
ec_sync_clear_pdos(sync);
sync->alt_mapping = 1;
}
/*****************************************************************************/
/**
* Add a PDO to the list of known mapped PDOs.
*/
int ecrt_slave_pdo_mapping_add(
ec_slave_t *slave, /**< EtherCAT slave */
ec_direction_t dir, /**< input/output */
uint16_t pdo_index /**< Index of mapped PDO */)
{
ec_pdo_t *pdo;
ec_sync_t *sync;
unsigned int not_found = 1;
if (!(slave->sii_mailbox_protocols & EC_MBOX_COE)) {
EC_ERR("Slave %u does not support CoE!\n", slave->ring_position);
return -1;
}
// does the slave provide the PDO? FIXME
list_for_each_entry(pdo, &slave->sii_pdos, list) {
if (pdo->index == pdo_index) {
not_found = 0;
break;
}
}
if (not_found) {
EC_ERR("Slave %u does not provide PDO 0x%04X!\n",
slave->ring_position, pdo_index);
return -1;
}
// check direction
if ((pdo->type == EC_TX_PDO && dir == EC_DIR_OUTPUT) ||
(pdo->type == EC_RX_PDO && dir == EC_DIR_INPUT)) {
EC_ERR("Invalid direction for PDO 0x%04X.\n", pdo_index);
return -1;
}
if (!(sync = ec_slave_get_pdo_sync(slave, dir))) {
EC_ERR("Failed to obtain sync manager for PDO mapping of slave %u!\n",
slave->ring_position);
return -1;
}
if (ec_sync_add_pdo(sync, pdo))
return -1;
sync->alt_mapping = 1;
return 0;
}
/*****************************************************************************/
/**
* Convenience function for ecrt_slave_pdo_mapping_clear() and
* ecrt_slave_pdo_mapping_add().
*/
int ecrt_slave_pdo_mapping(ec_slave_t *slave, /**< EtherCAT slave */
ec_direction_t dir, /**< input/output */
unsigned int num_args, /**< Number of following arguments */
... /**< PDO indices to map */
)
{
va_list ap;
ecrt_slave_pdo_mapping_clear(slave, dir);
va_start(ap, num_args);
for (; num_args; num_args--) {
if (ecrt_slave_pdo_mapping_add(
slave, dir, (uint16_t) va_arg(ap, int))) {
return -1;
}
}
va_end(ap);
return 0;
}
/*****************************************************************************/
/** \cond */
EXPORT_SYMBOL(ecrt_slave_conf_sdo8);
EXPORT_SYMBOL(ecrt_slave_conf_sdo16);
EXPORT_SYMBOL(ecrt_slave_conf_sdo32);
EXPORT_SYMBOL(ecrt_slave_pdo_mapping_clear);
EXPORT_SYMBOL(ecrt_slave_pdo_mapping_add);
EXPORT_SYMBOL(ecrt_slave_pdo_mapping);
/** \endcond */
/*****************************************************************************/

View File

@ -50,21 +50,12 @@
#include "datagram.h"
#include "pdo.h"
#include "sync.h"
#include "fmmu.h"
/*****************************************************************************/
/** maximum number of FMMUs per slave */
#define EC_MAX_FMMUS 16
/*****************************************************************************/
/**
* State of an EtherCAT slave.
/** State of an EtherCAT slave.
*/
typedef enum
{
typedef enum {
EC_SLAVE_STATE_UNKNOWN = 0x00,
/**< unknown state */
EC_SLAVE_STATE_INIT = 0x01,
@ -77,29 +68,22 @@ typedef enum
/**< OP (mailbox communication and input/output update) */
EC_SLAVE_STATE_ACK_ERR = 0x10
/**< Acknowledge/Error bit (no actual state) */
}
ec_slave_state_t;
} ec_slave_state_t;
/*****************************************************************************/
/**
* EtherCAT slave online state.
/** EtherCAT slave online state.
*/
typedef enum {
EC_SLAVE_OFFLINE,
EC_SLAVE_ONLINE
}
ec_slave_online_state_t;
} ec_slave_online_state_t;
/*****************************************************************************/
/**
* Supported mailbox protocols.
/** Supported mailbox protocols.
*/
enum
{
enum {
EC_MBOX_AOE = 0x01, /**< ADS-over-EtherCAT */
EC_MBOX_EOE = 0x02, /**< Ethernet-over-EtherCAT */
EC_MBOX_COE = 0x04, /**< CANopen-over-EtherCAT */
@ -110,27 +94,26 @@ enum
/*****************************************************************************/
/**
* EtherCAT slave.
/** EtherCAT slave.
*/
struct ec_slave
{
struct list_head list; /**< list item */
struct kobject kobj; /**< kobject */
ec_master_t *master; /**< master owning the slave */
ec_slave_state_t requested_state; /**< requested application state */
ec_slave_state_t current_state; /**< current application state */
ec_slave_online_state_t online_state; /**< online state */
unsigned int self_configured; /**< slave was configured by this master */
unsigned int error_flag; /**< stop processing after an error */
unsigned int pdos_registered; /**< non-zero, if PDOs were registered */
// addresses
uint16_t ring_position; /**< ring position */
uint16_t station_address; /**< configured station address */
// configuration
ec_slave_config_t *config; /**< Current configuration. */
ec_slave_state_t requested_state; /**< Requested application state. */
ec_slave_state_t current_state; /**< Current application state. */
ec_slave_online_state_t online_state; /**< online state */
unsigned int self_configured; /**< Slave was configured by this master. */
unsigned int error_flag; /**< Stop processing after an error. */
// base data
uint8_t base_type; /**< slave type */
uint8_t base_revision; /**< revision */
@ -169,12 +152,8 @@ struct ec_slave
char *sii_name; /**< slave name acc. to EEPROM */
int16_t sii_current_on_ebus; /**< power consumption */
ec_fmmu_t fmmus[EC_MAX_FMMUS]; /**< FMMU configurations */
uint8_t fmmu_count; /**< number of FMMUs used */
struct kobject sdo_kobj; /**< kobject for SDOs */
struct list_head sdo_dictionary; /**< SDO dictionary list */
struct list_head sdo_confs; /**< list of SDO configurations */
uint8_t sdo_dictionary_fetched; /**< dictionary has been fetched */
unsigned long jiffies_preop; /**< time, the slave went to PREOP */
@ -187,11 +166,6 @@ struct ec_slave
int ec_slave_init(ec_slave_t *, ec_master_t *, uint16_t, uint16_t);
void ec_slave_destroy(ec_slave_t *);
void ec_slave_reset(ec_slave_t *);
int ec_slave_prepare_fmmu(ec_slave_t *, const ec_domain_t *,
const ec_sync_t *);
void ec_slave_request_state(ec_slave_t *, ec_slave_state_t);
void ec_slave_set_state(ec_slave_t *, ec_slave_state_t);
void ec_slave_set_online_state(ec_slave_t *, ec_slave_online_state_t);
@ -201,7 +175,7 @@ int ec_slave_fetch_sii_strings(ec_slave_t *, const uint8_t *, size_t);
int ec_slave_fetch_sii_general(ec_slave_t *, const uint8_t *, size_t);
int ec_slave_fetch_sii_syncs(ec_slave_t *, const uint8_t *, size_t);
int ec_slave_fetch_sii_pdos(ec_slave_t *, const uint8_t *, size_t,
ec_pdo_type_t);
ec_direction_t);
// misc.
ec_sync_t *ec_slave_get_pdo_sync(ec_slave_t *, ec_direction_t);

546
master/slave_config.c Normal file
View File

@ -0,0 +1,546 @@
/******************************************************************************
*
* $Id$
*
* Copyright (C) 2006 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
* as published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* 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 right to use EtherCAT Technology is granted and comes free of
* charge under condition of compatibility of product made by
* Licensee. People intending to distribute/sell products based on the
* code, have to sign an agreement to guarantee that products using
* software based on IgH EtherCAT master stay compatible with the actual
* EtherCAT specification (which are released themselves as an open
* standard) as the (only) precondition to have the right to use EtherCAT
* Technology, IP and trade marks.
*
*****************************************************************************/
/**
\file
EtherCAT slave configuration methods.
*/
/*****************************************************************************/
#include <linux/module.h>
#include "globals.h"
#include "master.h"
#include "slave_config.h"
/*****************************************************************************/
void ec_slave_config_clear(struct kobject *);
ssize_t ec_show_slave_config_attribute(struct kobject *, struct attribute *,
char *);
/*****************************************************************************/
/** \cond */
EC_SYSFS_READ_ATTR(info);
static struct attribute *def_attrs[] = {
&attr_info,
NULL,
};
static struct sysfs_ops sysfs_ops = {
.show = ec_show_slave_config_attribute
};
static struct kobj_type ktype_ec_slave_config = {
.release = ec_slave_config_clear,
.sysfs_ops = &sysfs_ops,
.default_attrs = def_attrs
};
/** \endcond */
/*****************************************************************************/
/** Slave configuration constructor.
*
* See ecrt_master_slave_config() for the usage of the \a alias and \a
* position parameters.
*
* \retval 0 Success.
* \retval <0 Failure.
*/
int ec_slave_config_init(ec_slave_config_t *sc, /**< Slave configuration. */
ec_master_t *master, /**< EtherCAT master. */
uint16_t alias, /**< Slave alias. */
uint16_t position, /**< Slave position. */
uint32_t vendor_id, /**< Expected vendor ID. */
uint32_t product_code /**< Expected product code. */
)
{
ec_direction_t dir;
sc->master = master;
sc->alias = alias;
sc->position = position;
sc->vendor_id = vendor_id;
sc->product_code = product_code;
sc->slave = NULL;
for (dir = EC_DIR_OUTPUT; dir <= EC_DIR_INPUT; dir++)
ec_pdo_mapping_init(&sc->mapping[dir]);
INIT_LIST_HEAD(&sc->sdo_configs);
sc->used_fmmus = 0;
// init kobject and add it to the hierarchy
memset(&sc->kobj, 0x00, sizeof(struct kobject));
kobject_init(&sc->kobj);
sc->kobj.ktype = &ktype_ec_slave_config;
sc->kobj.parent = &master->kobj;
if (kobject_set_name(&sc->kobj, "config-%u-%u", sc->alias, sc->position)) {
EC_ERR("Failed to set kobject name for slave config %u:%u.\n",
sc->alias, sc->position);
goto out_put;
}
if (kobject_add(&sc->kobj)) {
EC_ERR("Failed to add kobject for slave config %u:%u.\n",
sc->alias, sc->position);
goto out_put;
}
return 0;
out_put:
kobject_put(&sc->kobj);
return -1;
}
/*****************************************************************************/
/** Slave configuration destructor.
*
* Clears and frees a slave configuration object.
*/
void ec_slave_config_destroy(
ec_slave_config_t *sc /**< Slave configuration. */
)
{
ec_slave_config_detach(sc);
// destroy self
kobject_del(&sc->kobj);
kobject_put(&sc->kobj);
}
/*****************************************************************************/
/** Clear and free the slave configuration.
*
* This method is called by the kobject, once there are no more references to
* it.
*/
void ec_slave_config_clear(struct kobject *kobj /**< kobject of the config. */)
{
ec_slave_config_t *sc;
ec_direction_t dir;
ec_sdo_data_t *sdodata, *next_sdodata;
sc = container_of(kobj, ec_slave_config_t, kobj);
// Free Pdo mappings
for (dir = EC_DIR_OUTPUT; dir <= EC_DIR_INPUT; dir++)
ec_pdo_mapping_clear(&sc->mapping[dir]);
// free all SDO configurations
list_for_each_entry_safe(sdodata, next_sdodata, &sc->sdo_configs, list) {
list_del(&sdodata->list);
kfree(sdodata->data);
kfree(sdodata);
}
/** \todo */
kfree(sc);
}
/*****************************************************************************/
/** Prepares an FMMU configuration.
*
* Configuration data for the FMMU is saved in the slave config structure and
* is written to the slave during the configuration. The FMMU configuration
* is done in a way, that the complete data range of the corresponding sync
* manager is covered. Seperate FMMUs are configured for each domain. If the
* FMMU configuration is already prepared, the function returns with success.
*
* \retval >=0 Logical offset address.
* \retval -1 FMMU limit reached.
*/
int ec_slave_config_prepare_fmmu(
ec_slave_config_t *sc, /**< Slave configuration. */
ec_domain_t *domain, /**< Domain. */
ec_direction_t dir /**< PDO direction. */
)
{
unsigned int i;
ec_fmmu_config_t *fmmu;
// FMMU configuration already prepared?
for (i = 0; i < sc->used_fmmus; i++) {
fmmu = &sc->fmmu_configs[i];
if (fmmu->domain == domain && fmmu->dir == dir)
return fmmu->logical_start_address;
}
if (sc->used_fmmus == EC_MAX_FMMUS) {
EC_ERR("FMMU limit reached for slave configuration %u:%u!\n",
sc->alias, sc->position);
return -1;
}
fmmu = &sc->fmmu_configs[sc->used_fmmus++];
ec_fmmu_config_init(fmmu, sc, domain, dir);
return fmmu->logical_start_address;
}
/*****************************************************************************/
/** Registers a Pdo entry.
*
* Searches the mapping and the Pdo configurations for the given Pdo entry. If
* found, the curresponding sync manager/FMMU is added to the domain and the
* offset of the Pdo entry's data in the domain process data is returned.
*
* \retval >=0 Offset of the Pdo entry's process data.
* \retval -1 Pdo entry not found.
* \retval -2 Failed to register Pdo entry.
*/
int ec_slave_config_reg_pdo_entry(
ec_slave_config_t *sc, /**< Slave configuration. */
ec_domain_t *domain, /**< Domain. */
uint16_t index, /**< Index of Pdo entry to register. */
uint8_t subindex /**< Subindex of Pdo entry to register. */
)
{
ec_direction_t dir;
ec_pdo_mapping_t *map;
unsigned int bit_offset, byte_offset;
ec_pdo_t *pdo;
ec_pdo_entry_t *entry;
int ret;
for (dir = EC_DIR_OUTPUT; dir <= EC_DIR_INPUT; dir++) {
map = &sc->mapping[dir];
bit_offset = 0;
list_for_each_entry(pdo, &map->pdos, list) {
list_for_each_entry(entry, &pdo->entries, list) {
if (entry->index != index || entry->subindex != subindex) {
bit_offset += entry->bit_length;
} else {
goto found;
}
}
}
}
EC_ERR("PDO entry 0x%04X:%u is not mapped in slave config %u:%u.\n",
index, subindex, sc->alias, sc->position);
return -1;
found:
byte_offset = bit_offset / 8;
if ((ret = ec_slave_config_prepare_fmmu(sc, domain, dir)) < 0)
return -2;
return ret + byte_offset;
}
/*****************************************************************************/
/** Outputs all information about a certain slave configuration.
*/
ssize_t ec_slave_config_info(
const ec_slave_config_t *sc, /**< Slave configuration. */
char *buffer /**< Output buffer */
)
{
char *buf = buffer;
ec_direction_t dir;
const ec_pdo_mapping_t *map;
const ec_pdo_t *pdo;
const ec_pdo_entry_t *entry;
char str[20];
ec_sdo_data_t *sdodata;
buf += sprintf(buf, "Alias: 0x%04X (%u)\n", sc->alias, sc->alias);
buf += sprintf(buf, "Position: %u\n", sc->position);
for (dir = EC_DIR_OUTPUT; dir <= EC_DIR_INPUT; dir++) {
map = &sc->mapping[dir];
if (!list_empty(&map->pdos)) {
buf += sprintf(buf, "%s mapping:\n",
dir == EC_DIR_OUTPUT ? "Output" : "Input");
list_for_each_entry(pdo, &map->pdos, list) {
buf += sprintf(buf, " %s 0x%04X \"%s\"\n",
pdo->dir == EC_DIR_OUTPUT ? "RxPdo" : "TxPdo",
pdo->index, pdo->name ? pdo->name : "???");
list_for_each_entry(entry, &pdo->entries, list) {
buf += sprintf(buf, " 0x%04X:%X \"%s\", %u bit\n",
entry->index, entry->subindex,
entry->name ? entry->name : "???",
entry->bit_length);
}
}
}
}
// type-cast to avoid warnings on some compilers
if (!list_empty((struct list_head *) &sc->sdo_configs)) {
buf += sprintf(buf, "\nSDO configurations:\n");
list_for_each_entry(sdodata, &sc->sdo_configs, list) {
switch (sdodata->size) {
case 1: sprintf(str, "%u", EC_READ_U8(sdodata->data)); break;
case 2: sprintf(str, "%u", EC_READ_U16(sdodata->data)); break;
case 4: sprintf(str, "%u", EC_READ_U32(sdodata->data)); break;
default: sprintf(str, "(invalid size)"); break;
}
buf += sprintf(buf, " 0x%04X:%-3i -> %s\n",
sdodata->index, sdodata->subindex, str);
}
buf += sprintf(buf, "\n");
}
return buf - buffer;
}
/*****************************************************************************/
/** Formats attribute data for SysFS read access.
*
* \return number of bytes to read
*/
ssize_t ec_show_slave_config_attribute(
struct kobject *kobj, /**< Slave configuration's kobject */
struct attribute *attr, /**< Requested attribute. */
char *buffer /**< Memory to store data. */
)
{
ec_slave_config_t *sc = container_of(kobj, ec_slave_config_t, kobj);
if (attr == &attr_info) {
return ec_slave_config_info(sc, buffer);
}
return 0;
}
/*****************************************************************************/
/** Adds an SDO configuration.
*/
int ec_slave_config_sdo(ec_slave_config_t *sc, uint16_t index,
uint8_t subindex, const uint8_t *data, size_t size)
{
ec_slave_t *slave = sc->slave;
ec_sdo_data_t *sdodata;
if (slave && !(slave->sii_mailbox_protocols & EC_MBOX_COE)) {
EC_ERR("Slave %u does not support CoE!\n", slave->ring_position);
return -1;
}
if (!(sdodata = (ec_sdo_data_t *)
kmalloc(sizeof(ec_sdo_data_t), GFP_KERNEL))) {
EC_ERR("Failed to allocate memory for SDO configuration object!\n");
return -1;
}
if (!(sdodata->data = (uint8_t *) kmalloc(size, GFP_KERNEL))) {
EC_ERR("Failed to allocate memory for SDO configuration data!\n");
kfree(sdodata);
return -1;
}
sdodata->index = index;
sdodata->subindex = subindex;
memcpy(sdodata->data, data, size);
sdodata->size = size;
list_add_tail(&sdodata->list, &sc->sdo_configs);
return 0;
}
/*****************************************************************************/
/** Attaches the configuration to the addressed slave object.
*
* \retval 0 Success.
* \retval -1 Slave not found.
* \retval -2 Slave already configured.
* \retval -3 Invalid slave type found at the given position.
*/
int ec_slave_config_attach(
ec_slave_config_t *sc /**< Slave configuration. */
)
{
ec_slave_t *slave;
unsigned int alias_found = 0, relative_position = 0;
if (sc->slave)
return 0; // already attached
list_for_each_entry(slave, &sc->master->slaves, list) {
if (!alias_found) {
if (sc->alias && slave->sii_alias != sc->alias)
continue;
alias_found = 1;
relative_position = 0;
}
if (relative_position == sc->position)
goto found;
relative_position++;
}
EC_ERR("Failed to find slave for configuration %u:%u.\n",
sc->alias, sc->position);
return -1;
found:
if (slave->config) {
EC_ERR("Failed to attach slave configuration %u:%u. Slave %u"
" already has a configuration!\n", sc->alias,
sc->position, slave->ring_position);
return -2;
}
if (slave->sii_vendor_id != sc->vendor_id
|| slave->sii_product_code != sc->product_code) {
EC_ERR("Slave %u has an invalid type (0x%08X/0x%08X) for"
" configuration %u:%u (0x%08X/0x%08X).\n",
slave->ring_position, slave->sii_vendor_id,
slave->sii_product_code, sc->alias, sc->position,
sc->vendor_id, sc->product_code);
return -3;
}
// attach slave
slave->config = sc;
sc->slave = slave;
ec_slave_request_state(slave, EC_SLAVE_STATE_OP);
return 0;
}
/*****************************************************************************/
/** Detaches the configuration from a slave object.
*/
void ec_slave_config_detach(
ec_slave_config_t *sc /**< Slave configuration. */
)
{
if (sc->slave) {
sc->slave->config = NULL;
sc->slave = NULL;
}
}
/*****************************************************************************/
/** Loads the default mapping from the slave object.
*/
void ec_slave_config_load_default_mapping(ec_slave_config_t *sc)
{
ec_direction_t dir;
ec_pdo_mapping_t *map;
ec_sync_t *sync;
if (!sc->slave)
return;
for (dir = EC_DIR_OUTPUT; dir <= EC_DIR_INPUT; dir++) {
map = &sc->mapping[dir];
if (!(sync = ec_slave_get_pdo_sync(sc->slave, dir)))
continue;
ec_pdo_mapping_copy(map, &sync->mapping);
}
}
/******************************************************************************
* Realtime interface
*****************************************************************************/
int ecrt_slave_config_mapping(ec_slave_config_t *sc, unsigned int n_entries,
const ec_pdo_info_t pdo_infos[])
{
unsigned int i;
for (i = 0; i < n_entries; i++)
if (ec_pdo_mapping_add_pdo_info(&sc->mapping[pdo_infos[i].dir],
&pdo_infos[i], sc))
return -1;
return 0;
}
/*****************************************************************************/
int ecrt_slave_config_sdo8(ec_slave_config_t *slave, uint16_t index,
uint8_t subindex, uint8_t value)
{
uint8_t data[1];
EC_WRITE_U8(data, value);
return ec_slave_config_sdo(slave, index, subindex, data, 1);
}
/*****************************************************************************/
int ecrt_slave_config_sdo16(ec_slave_config_t *slave, uint16_t index,
uint8_t subindex, uint16_t value)
{
uint8_t data[2];
EC_WRITE_U16(data, value);
return ec_slave_config_sdo(slave, index, subindex, data, 2);
}
/*****************************************************************************/
int ecrt_slave_config_sdo32(ec_slave_config_t *slave, uint16_t index,
uint8_t subindex, uint32_t value)
{
uint8_t data[4];
EC_WRITE_U32(data, value);
return ec_slave_config_sdo(slave, index, subindex, data, 4);
}
/*****************************************************************************/
/** \cond */
EXPORT_SYMBOL(ecrt_slave_config_mapping);
EXPORT_SYMBOL(ecrt_slave_config_sdo8);
EXPORT_SYMBOL(ecrt_slave_config_sdo16);
EXPORT_SYMBOL(ecrt_slave_config_sdo32);
/** \endcond */
/*****************************************************************************/

96
master/slave_config.h Normal file
View File

@ -0,0 +1,96 @@
/******************************************************************************
*
* $Id$
*
* Copyright (C) 2006 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
* as published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* 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 right to use EtherCAT Technology is granted and comes free of
* charge under condition of compatibility of product made by
* Licensee. People intending to distribute/sell products based on the
* code, have to sign an agreement to guarantee that products using
* software based on IgH EtherCAT master stay compatible with the actual
* EtherCAT specification (which are released themselves as an open
* standard) as the (only) precondition to have the right to use EtherCAT
* Technology, IP and trade marks.
*
*****************************************************************************/
/**
\file
EtherCAT slave configuration structure.
*/
/*****************************************************************************/
#ifndef _EC_SLAVE_CONFIG_H_
#define _EC_SLAVE_CONFIG_H_
#include <linux/list.h>
#include <linux/kobject.h>
#include "../include/ecrt.h"
#include "globals.h"
#include "slave.h"
#include "fmmu_config.h"
#include "pdo_mapping.h"
/*****************************************************************************/
/** EtherCAT slave configuration.
*/
struct ec_slave_config {
struct list_head list; /**< List item. */
struct kobject kobj; /**< kobject. */
ec_master_t *master; /**< Master owning the slave configuration. */
uint16_t alias; /**< Slave alias. */
uint16_t position; /**< Index after alias. If alias is zero, this is the
ring position. */
uint32_t vendor_id; /**< Slave vendor ID. */
uint32_t product_code; /**< Slave product code. */
ec_slave_t *slave; /**< Slave pointer. This is \a NULL, if the slave is
offline. */
ec_pdo_mapping_t mapping[2]; /**< Output and input PDO mapping. */
struct list_head sdo_configs; /**< SDO configurations. */
ec_fmmu_config_t fmmu_configs[EC_MAX_FMMUS]; /**< FMMU configurations. */
uint8_t used_fmmus; /**< Number of FMMUs used. */
};
/*****************************************************************************/
int ec_slave_config_init(ec_slave_config_t *, ec_master_t *, uint16_t,
uint16_t, uint32_t, uint32_t);
void ec_slave_config_destroy(ec_slave_config_t *);
int ec_slave_config_reg_pdo_entry(ec_slave_config_t *, ec_domain_t *,
uint16_t, uint8_t);
int ec_slave_config_attach(ec_slave_config_t *);
void ec_slave_config_detach(ec_slave_config_t *);
void ec_slave_config_load_default_mapping(ec_slave_config_t *);
/*****************************************************************************/
#endif

View File

@ -31,10 +31,9 @@
*
*****************************************************************************/
/**
\file
EtherCAT sync manager methods.
*/
/** \file
* EtherCAT sync manager methods.
*/
/*****************************************************************************/
@ -46,93 +45,52 @@
/*****************************************************************************/
/**
* Constructor.
/** Constructor.
*/
void ec_sync_init(
ec_sync_t *sync, /**< EtherCAT sync manager */
ec_slave_t *slave, /**< EtherCAT slave */
unsigned int index /**< sync manager index */
ec_sync_t *sync, /**< EtherCAT sync manager. */
ec_slave_t *slave, /**< EtherCAT slave. */
unsigned int index /**< Sync manager index. */
)
{
sync->slave = slave;
sync->index = index;
sync->est_length = 0;
INIT_LIST_HEAD(&sync->pdos);
sync->alt_mapping = 0;
ec_pdo_mapping_init(&sync->mapping);
sync->mapping_source = EC_SYNC_MAPPING_NONE;
}
/*****************************************************************************/
/**
* Destructor.
/** Destructor.
*/
void ec_sync_clear(
ec_sync_t *sync /**< EtherCAT sync manager */
ec_sync_t *sync /**< EtherCAT sync manager. */
)
{
ec_sync_clear_pdos(sync);
ec_pdo_mapping_clear(&sync->mapping);
}
/*****************************************************************************/
/**
* Calculates the size of a sync manager by evaluating PDO sizes.
* \return sync manager size
/** Initializes a sync manager configuration page with EEPROM data.
*
* The referenced memory (\a data) must be at least \a EC_SYNC_SIZE bytes.
*/
uint16_t ec_sync_size(
const ec_sync_t *sync /**< sync manager */
)
{
const ec_pdo_t *pdo;
const ec_pdo_entry_t *pdo_entry;
unsigned int bit_size, byte_size;
if (sync->length) return sync->length;
if (sync->est_length) return sync->est_length;
bit_size = 0;
list_for_each_entry(pdo, &sync->pdos, list) {
list_for_each_entry(pdo_entry, &pdo->entries, list) {
bit_size += pdo_entry->bit_length;
}
}
if (bit_size % 8) // round up to full bytes
byte_size = bit_size / 8 + 1;
else
byte_size = bit_size / 8;
return byte_size;
}
/*****************************************************************************/
/**
Initializes a sync manager configuration page with EEPROM data.
The referenced memory (\a data) must be at least EC_SYNC_SIZE bytes.
*/
void ec_sync_config(
const ec_sync_t *sync, /**< sync manager */
uint8_t *data /**> configuration memory */
const ec_sync_t *sync, /**< Sync manager. */
uint16_t data_size, /**< Data size. */
uint8_t *data /**> Configuration memory. */
)
{
size_t sync_size = ec_sync_size(sync);
if (sync->slave->master->debug_level) {
EC_DBG("SM%i: Addr 0x%04X, Size %3i, Ctrl 0x%02X, En %i\n",
sync->index, sync->physical_start_address,
sync_size, sync->control_register, sync->enable);
data_size, sync->control_register, sync->enable);
}
EC_WRITE_U16(data, sync->physical_start_address);
EC_WRITE_U16(data + 2, sync_size);
EC_WRITE_U16(data + 2, data_size);
EC_WRITE_U8 (data + 4, sync->control_register);
EC_WRITE_U8 (data + 5, 0x00); // status byte (read only)
EC_WRITE_U16(data + 6, sync->enable ? 0x0001 : 0x0000); // enable
@ -140,72 +98,26 @@ void ec_sync_config(
/*****************************************************************************/
/**
* Adds a PDO to the list of known mapped PDOs.
/** Adds a PDO to the list of known mapped PDOs.
*
* \return 0 on success, else < 0
*/
int ec_sync_add_pdo(
ec_sync_t *sync, /**< EtherCAT sync manager */
const ec_pdo_t *pdo /**< PDO to map */
ec_sync_t *sync, /**< EtherCAT sync manager. */
const ec_pdo_t *pdo /**< PDO to map. */
)
{
ec_pdo_t *mapped_pdo;
// PDO already mapped?
list_for_each_entry(mapped_pdo, &sync->pdos, list) {
if (mapped_pdo->index != pdo->index) continue;
EC_ERR("PDO 0x%04X is already mapped!\n", pdo->index);
return -1;
}
if (!(mapped_pdo = kmalloc(sizeof(ec_pdo_t), GFP_KERNEL))) {
EC_ERR("Failed to allocate memory for PDO mapping.\n");
return -1;
}
ec_pdo_init(mapped_pdo);
if (ec_pdo_copy(mapped_pdo, pdo)) {
ec_pdo_clear(mapped_pdo);
kfree(mapped_pdo);
return -1;
}
// set appropriate sync manager index
mapped_pdo->sync_index = sync->index;
list_add_tail(&mapped_pdo->list, &sync->pdos);
return 0;
return ec_pdo_mapping_add_pdo(&sync->mapping, pdo);
}
/*****************************************************************************/
/**
* Clears the list of known mapped PDOs.
/** Get direction covered by sync manager.
*
* \return Direction covered by the given sync manager.
*/
void ec_sync_clear_pdos(
ec_sync_t *sync /**< EtherCAT sync manager */
)
{
ec_pdo_t *pdo, *next;
// free all mapped PDOs
list_for_each_entry_safe(pdo, next, &sync->pdos, list) {
list_del(&pdo->list);
ec_pdo_clear(pdo);
kfree(pdo);
}
}
/*****************************************************************************/
/**
* \return Type of PDOs covered by the given sync manager.
*/
ec_pdo_type_t ec_sync_get_pdo_type(
const ec_sync_t *sync /**< EtherCAT sync manager */
ec_direction_t ec_sync_direction(
const ec_sync_t *sync /**< EtherCAT sync manager. */
)
{
int index = sync->index;
@ -215,11 +127,11 @@ ec_pdo_type_t ec_sync_get_pdo_type(
}
if (index < 0 || index > 1) {
EC_WARN("ec_sync_get_pdo_type(): invalid sync manager index.\n");
return EC_RX_PDO;
EC_WARN("ec_sync_get_direction(): invalid sync manager index.\n");
return EC_DIR_OUTPUT;
}
return (ec_pdo_type_t) index;
return (ec_direction_t) index;
}
/*****************************************************************************/

View File

@ -31,10 +31,9 @@
*
*****************************************************************************/
/**
\file
EtherCAT sync manager.
*/
/** \file
* EtherCAT sync manager.
*/
/*****************************************************************************/
@ -44,60 +43,46 @@
#include <linux/list.h>
#include "../include/ecrt.h"
#include "globals.h"
#include "pdo_mapping.h"
/*****************************************************************************/
/** size of a sync manager configuration page */
#define EC_SYNC_SIZE 8
/*****************************************************************************/
/**
* EtherCAT sync manager PDO mapping information source.
/** EtherCAT sync manager PDO mapping information source.
*/
typedef enum {
EC_SYNC_MAPPING_NONE, /**< No PDO mapping information */
EC_SYNC_MAPPING_SII, /**< PDO mapping information from SII */
EC_SYNC_MAPPING_COE /**< PDO mapping information from CoE dictionary */
}
ec_sync_mapping_source_t;
EC_SYNC_MAPPING_NONE, /**< No PDO mapping information. */
EC_SYNC_MAPPING_SII, /**< PDO mapping information from SII. */
EC_SYNC_MAPPING_COE, /**< PDO mapping information from CoE dictionary. */
EC_SYNC_MAPPING_CUSTOM, /**< PDO mapping configured externally. */
} ec_sync_mapping_source_t;
/*****************************************************************************/
/**
* Sync manager.
/** Sync manager.
*/
typedef struct
{
ec_slave_t *slave; /**< slave, the sync manager belongs to */
unsigned int index; /**< sync manager index */
uint16_t physical_start_address; /**< physical start address */
uint16_t length; /**< data length in bytes */
uint8_t control_register; /**< control register value */
uint8_t enable; /**< enable bit */
uint16_t est_length; /**< used to calculate the length via PDO ranges */
struct list_head pdos; /**< list of mapped PDOs */
unsigned int alt_mapping; /**< alternative mapping configured */
ec_sync_mapping_source_t mapping_source; /**< pdo mapping source */
}
ec_sync_t;
typedef struct {
ec_slave_t *slave; /**< Slave, the sync manager belongs to. */
unsigned int index; /**< Sync manager index. */
uint16_t physical_start_address; /**< Physical start address. */
uint16_t length; /**< Data length in bytes. */
uint8_t control_register; /**< Control register value. */
uint8_t enable; /**< Enable bit. */
ec_pdo_mapping_t mapping; /**< Current Pdo mapping. */
ec_sync_mapping_source_t mapping_source; /**< Pdo mapping source. */
} ec_sync_t;
/*****************************************************************************/
void ec_sync_init(ec_sync_t *, ec_slave_t *, unsigned int);
void ec_sync_clear(ec_sync_t *);
uint16_t ec_sync_size(const ec_sync_t *);
void ec_sync_config(const ec_sync_t *, uint8_t *);
void ec_sync_config(const ec_sync_t *, uint16_t, uint8_t *);
int ec_sync_add_pdo(ec_sync_t *, const ec_pdo_t *);
void ec_sync_clear_pdos(ec_sync_t *);
ec_pdo_type_t ec_sync_get_pdo_type(const ec_sync_t *);
ec_direction_t ec_sync_direction(const ec_sync_t *);
/*****************************************************************************/