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:
parent
a1ce00effe
commit
6189092aed
34
NEWS
34
NEWS
|
|
@ -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
14
TODO
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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."]
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
479
include/ecrt.h
479
include/ecrt.h
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
402
master/domain.c
402
master/domain.c
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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 *);
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
152
master/globals.h
152
master/globals.h
|
|
@ -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 */
|
||||
|
||||
/*****************************************************************************/
|
||||
|
|
|
|||
312
master/master.c
312
master/master.c
|
|
@ -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 */
|
||||
|
||||
|
|
|
|||
|
|
@ -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 *);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
178
master/pdo.c
178
master/pdo.c
|
|
@ -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;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
|
|
|||
56
master/pdo.h
56
master/pdo.h
|
|
@ -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 *);
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
|
@ -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
|
||||
392
master/slave.c
392
master/slave.c
|
|
@ -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 */
|
||||
|
||||
/*****************************************************************************/
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
||||
/*****************************************************************************/
|
||||
|
|
@ -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
|
||||
152
master/sync.c
152
master/sync.c
|
|
@ -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;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
|
|
|||
|
|
@ -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 *);
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue