# HG changeset patch # User Florian Pose # Date 1203409340 0 # Node ID 3778920f61e49975074f374b36737720bfdf80d3 # Parent 3b81d074735c8209c5b9e9c4c4e6753d833858d0 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... diff -r 3b81d074735c -r 3778920f61e4 NEWS --- a/NEWS Thu Feb 14 09:18:55 2008 +0000 +++ b/NEWS Tue Feb 19 08:22:20 2008 +0000 @@ -6,6 +6,40 @@ 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. diff -r 3b81d074735c -r 3778920f61e4 TODO --- a/TODO Thu Feb 14 09:18:55 2008 +0000 +++ b/TODO Tue Feb 19 08:22:20 2008 +0000 @@ -11,9 +11,7 @@ * 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 @@ * 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 @@ 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 @@ * Clear sync managers in INIT. * Simplify FSMs with _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 @@ Less important issues: -* Implement all EtherCAT datagram types. * File access over EtherCAT (FoE). * Allow VLAN tagging. * Determine number of frames, the NIC can handle. diff -r 3b81d074735c -r 3778920f61e4 documentation/graphs/fsm_slave_conf.dot --- a/documentation/graphs/fsm_slave_conf.dot Thu Feb 14 09:18:55 2008 +0000 +++ b/documentation/graphs/fsm_slave_conf.dot Tue Feb 19 08:22:20 2008 +0000 @@ -8,6 +8,7 @@ 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 @@ 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 @@ 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 @@ 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."] diff -r 3b81d074735c -r 3778920f61e4 examples/mini/mini.c --- a/examples/mini/mini.c Thu Feb 14 09:18:55 2008 +0000 +++ b/examples/mini/mini.c Tue Feb 19 08:22:20 2008 +0000 @@ -44,6 +44,8 @@ #define FREQUENCY 100 //#define KBUS +#define PDOS +#define MAPPING /*****************************************************************************/ @@ -53,7 +55,7 @@ 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_outputs; #endif -static void *r_dig_out; -static void *r_ana_out; -static void *r_count; -static void *r_freq; - -#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 MAPPING +const ec_pdo_info_t mapping[] = { + {EC_DIR_INPUT, 0x1A00}, // channel 1 + {EC_DIR_INPUT, 0x1A01} // channel 2 +}; +#endif + +#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 @@ // 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 @@ 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 @@ int __init init_mini_module(void) { -#if 1 - ec_slave_t *slave; -#endif - +#ifdef MAPPING + ec_slave_config_t *sc; +#endif + printk(KERN_INFO PFX "Starting...\n"); if (!(master = ecrt_request_master(0))) { @@ -177,49 +182,25 @@ 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))) - 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"); - 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"); - 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 +#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_config_mapping(sc, 2, mapping)) { + printk(KERN_ERR PFX "Failed to configure Pdo mapping.\n"); + goto out_release_master; + } +#endif + + 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; + } #endif printk(KERN_INFO PFX "Activating master...\n"); diff -r 3b81d074735c -r 3778920f61e4 include/ecrt.h --- a/include/ecrt.h Thu Feb 14 09:18:55 2008 +0000 +++ b/include/ecrt.h Tue Feb 19 08:22:20 2008 +0000 @@ -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; - -/*****************************************************************************/ - -/** Master status. - * This is used for the output parameter of ecrt_master_get_status(). + EC_BUS_FAILURE = -1, /**< At least one configured slave is offline. */ + EC_BUS_OK /**< All configured slaves are online. */ +} ec_bus_state_t; + +/*****************************************************************************/ + +/** 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; - -/*****************************************************************************/ - -/** List entry for domain PDO registrations. - * This type is used as a parameter for the ecrt_domain_register_pdo_list() - * convenience function. + 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; + +/*****************************************************************************/ + +/** Slave configuration state. + * + * \see ecrt_slave_config_state(). + */ +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; + +/*****************************************************************************/ + +/** Domain working counter interpretation. + * + * This is used in ec_domain_state_t. + */ +typedef enum { + 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 { - 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; - -/*****************************************************************************/ - -/** Direction type for PDO mapping and range registration functions. + 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 master */ - EC_DIR_INPUT /**< values read by master */ -} -ec_direction_t; + 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); - -ec_domain_t *ecrt_master_create_domain(ec_master_t *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); - -int ecrt_master_activate(ec_master_t *master); - -void ecrt_master_send(ec_master_t *master); -void ecrt_master_receive(ec_master_t *master); - -void ecrt_master_get_status(const ec_master_t *master, ec_master_status_t *s); +/** 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. */ + ); + +/** 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. */ + ); + +/** 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. */ + ); + +/** 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. */ + ); + +/** Sends all datagrams in the queue. + * + * \todo doc + */ +void ecrt_master_send( + ec_master_t *master /**< EtherCAT master. */ + ); + +/** 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); - -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_register_pdo_list(ec_domain_t *domain, - const ec_pdo_reg_t *pdos); - -void ecrt_domain_process(ec_domain_t *domain); -void ecrt_domain_queue(ec_domain_t *domain); - -int ecrt_domain_state(const ec_domain_t *domain); - -/****************************************************************************** - * Slave methods - *****************************************************************************/ - -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); - -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, ...); +/** 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_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. */ + ); + +/** 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. */ + ); + +/** 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. */ + ); + +/** 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. */ + ); + +/** Processes received datagrams. + * + * \todo doc + */ +void ecrt_domain_process( + ec_domain_t *domain /**< Domain. */ + ); + +/** (Re-)queues all domain datagrams in the master's datagram queue. + * + * \todo doc + */ +void ecrt_domain_queue( + ec_domain_t *domain /**< Domain. */ + ); + +/** 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 @@ /*****************************************************************************/ +/** @} */ + #endif diff -r 3b81d074735c -r 3778920f61e4 master/Kbuild.in --- a/master/Kbuild.in Thu Feb 14 09:18:55 2008 +0000 +++ b/master/Kbuild.in Tue Feb 19 08:22:20 2008 +0000 @@ -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 diff -r 3b81d074735c -r 3778920f61e4 master/Makefile.am --- a/master/Makefile.am Thu Feb 14 09:18:55 2008 +0000 +++ b/master/Makefile.am Tue Feb 19 08:22:20 2008 +0000 @@ -39,7 +39,7 @@ 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 @@ 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: diff -r 3b81d074735c -r 3778920f61e4 master/domain.c --- a/master/domain.c Thu Feb 14 09:18:55 2008 +0000 +++ b/master/domain.c Tue Feb 19 08:22:20 2008 +0000 @@ -41,29 +41,14 @@ #include #include "globals.h" +#include "master.h" +#include "slave_config.h" + #include "domain.h" -#include "master.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; /*****************************************************************************/ 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 @@ /*****************************************************************************/ -/** - 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_clear(struct kobject *kobj /**< kobject of the domain */) { + ec_domain_t *domain; ec_datagram_t *datagram, *next; - ec_domain_t *domain; domain = container_of(kobj, ec_domain_t, kobj); @@ -174,91 +158,21 @@ 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; if (!(datagram = kmalloc(sizeof(ec_datagram_t), GFP_KERNEL))) { @@ -281,50 +195,47 @@ /*****************************************************************************/ -/** - 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 */ - ) -{ - 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; +/** 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. */ + ) +{ + 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 @@ 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,143 +278,39 @@ * 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 */ - ) -{ - 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; -} - -/*****************************************************************************/ - -/** - * 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 */ - ) -{ - const ec_pdo_reg_t *reg; - ec_slave_t *slave; +int ecrt_domain_reg_pdo_entry(ec_domain_t *domain, ec_slave_config_t *sc, + uint16_t index, uint8_t subindex) +{ + return ec_slave_config_reg_pdo_entry(sc, domain, index, subindex); +} + +/*****************************************************************************/ + +int ecrt_domain_reg_pdo_entry_list(ec_domain_t *domain, + const ec_pdo_entry_reg_t *regs) +{ + 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; - } - - return 0; -} - -/*****************************************************************************/ - -/** - * 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 */) + + *reg->offset = ret; + } + + return 0; +} + +/*****************************************************************************/ + +void ecrt_domain_process(ec_domain_t *domain) { unsigned int working_counter_sum; ec_datagram_t *datagram; @@ -552,9 +327,9 @@ } } - 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 @@ 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 @@ /*****************************************************************************/ -/** - 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 @@ /*****************************************************************************/ -/** - 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 */) -{ - return domain->state; +void ecrt_domain_state(const ec_domain_t *domain, ec_domain_state_t *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); diff -r 3b81d074735c -r 3778920f61e4 master/domain.h --- a/master/domain.h Thu Feb 14 09:18:55 2008 +0000 +++ b/master/domain.h Tue Feb 19 08:22:20 2008 +0000 @@ -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 @@ 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); /*****************************************************************************/ diff -r 3b81d074735c -r 3778920f61e4 master/fmmu.c --- a/master/fmmu.c Thu Feb 14 09:18:55 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,95 +0,0 @@ -/****************************************************************************** - * - * $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 FMMU methods. -*/ - -/*****************************************************************************/ - -#include "globals.h" -#include "slave.h" -#include "master.h" -#include "fmmu.h" - -/*****************************************************************************/ - -/** - * FMMU Constructor. - */ - -void ec_fmmu_init( - ec_fmmu_t *fmmu, /**< EtherCAT FMMU */ - ec_slave_t *slave, /**< EtherCAT slave */ - unsigned int index /**< FMMU index */ - ) -{ - fmmu->slave = slave; - fmmu->index = index; -} - -/*****************************************************************************/ - -/** - * Initializes an FMMU configuration page. - * The referenced memory (\a data) must be at least EC_FMMU_SIZE bytes. - */ - -void ec_fmmu_config( - const ec_fmmu_t *fmmu, /**< EtherCAT FMMU */ - uint8_t *data /**> configuration 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")); - } - - EC_WRITE_U32(data, fmmu->logical_start_address); - EC_WRITE_U16(data + 4, sync_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_U8 (data + 10, 0x00); // physical start bit - EC_WRITE_U8 (data + 11, ((fmmu->sync->control_register & 0x04) - ? 0x02 : 0x01)); - EC_WRITE_U16(data + 12, 0x0001); // enable - EC_WRITE_U16(data + 14, 0x0000); // reserved -} - -/*****************************************************************************/ diff -r 3b81d074735c -r 3778920f61e4 master/fmmu.h --- a/master/fmmu.h Thu Feb 14 09:18:55 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,77 +0,0 @@ -/****************************************************************************** - * - * $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 FMMU structure. -*/ - -/*****************************************************************************/ - -#ifndef _EC_FMMU_H_ -#define _EC_FMMU_H_ - -#include "../include/ecrt.h" - -#include "globals.h" - -/*****************************************************************************/ - -/** size of an FMMU configuration page */ -#define EC_FMMU_SIZE 16 - -/*****************************************************************************/ - -/** - * 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 */ -} -ec_fmmu_t; - -/*****************************************************************************/ - -void ec_fmmu_init(ec_fmmu_t *, ec_slave_t *, unsigned int); - -void ec_fmmu_config(const ec_fmmu_t *, uint8_t *); - -/*****************************************************************************/ - -#endif diff -r 3b81d074735c -r 3778920f61e4 master/fmmu_config.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/master/fmmu_config.c Tue Feb 19 08:22:20 2008 +0000 @@ -0,0 +1,100 @@ +/****************************************************************************** + * + * $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 FMMU configuration methods. + */ + +/*****************************************************************************/ + +#include "globals.h" +#include "slave_config.h" +#include "master.h" + +#include "fmmu_config.h" + +/*****************************************************************************/ + +/** 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_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->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_PAGE_SIZE bytes. + */ +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. */ + ) +{ + 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, 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, sync->physical_start_address); + EC_WRITE_U8 (data + 10, 0x00); // physical start bit + 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 +} + +/*****************************************************************************/ diff -r 3b81d074735c -r 3778920f61e4 master/fmmu_config.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/master/fmmu_config.h Tue Feb 19 08:22:20 2008 +0000 @@ -0,0 +1,72 @@ +/****************************************************************************** + * + * $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 FMMU configuration structure. + */ + +/*****************************************************************************/ + +#ifndef _EC_FMMU_CONFIG_H_ +#define _EC_FMMU_CONFIG_H_ + +#include "../include/ecrt.h" + +#include "globals.h" + +/*****************************************************************************/ + +/** FMMU configuration. + */ +typedef struct +{ + 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_config_t; + +/*****************************************************************************/ + +void ec_fmmu_config_init(ec_fmmu_config_t *, ec_slave_config_t *, + ec_domain_t *, ec_direction_t); + +void ec_fmmu_config_page(const ec_fmmu_config_t *, const ec_sync_t *, + uint8_t *); + +/*****************************************************************************/ + +#endif diff -r 3b81d074735c -r 3778920f61e4 master/fsm_coe_map.c --- a/master/fsm_coe_map.c Thu Feb 14 09:18:55 2008 +0000 +++ b/master/fsm_coe_map.c Tue Feb 19 08:22:20 2008 +0000 @@ -59,8 +59,6 @@ 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 @@ { 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_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 @@ 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 @@ { 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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); } diff -r 3b81d074735c -r 3778920f61e4 master/fsm_coe_map.h --- a/master/fsm_coe_map.h Thu Feb 14 09:18:55 2008 +0000 +++ b/master/fsm_coe_map.h Tue Feb 19 08:22:20 2008 +0000 @@ -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 @@ 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 */ diff -r 3b81d074735c -r 3778920f61e4 master/fsm_mapping.c --- a/master/fsm_mapping.c Thu Feb 14 09:18:55 2008 +0000 +++ b/master/fsm_mapping.c Tue Feb 19 08:22:20 2008 +0000 @@ -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 @@ /*****************************************************************************/ -/** - * Constructor. - */ - -void ec_fsm_mapping_init(ec_fsm_mapping_t *fsm, /**< mapping state machine */ +/** Constructor. + */ +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 @@ /*****************************************************************************/ -/** - * Destructor. - */ - -void ec_fsm_mapping_clear(ec_fsm_mapping_t *fsm /**< mapping state machine */) -{ -} - -/*****************************************************************************/ - -/** - * Start PDO mapping configuration state machine. - */ - +/** Destructor. + */ +void ec_fsm_mapping_clear( + ec_fsm_mapping_t *fsm /**< mapping 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 @@ /*****************************************************************************/ -/** +/** 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 @@ /*****************************************************************************/ -/** - * 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 @@ /*****************************************************************************/ -/** +/** 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 @@ } /****************************************************************************** - * 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 @@ } 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 @@ /*****************************************************************************/ -/** - * 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 */ - ) -{ - 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; -} - -/*****************************************************************************/ - -/** - * Set the number of mapped PDOs to zero. - */ - + const ec_fsm_mapping_t *fsm, /**< mapping state machine */ + const struct list_head *list /**< current PDO list item */ + ) +{ + 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. + */ void ec_fsm_mapping_state_zero_count( ec_fsm_mapping_t *fsm /**< mapping state machine */ ) @@ -245,8 +240,7 @@ // 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 @@ /*****************************************************************************/ -/** - * 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 @@ } // 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 @@ /*****************************************************************************/ -/** - * 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 @@ * 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 @@ /*****************************************************************************/ -/** - State: END. -*/ - +/** State: END. + */ void ec_fsm_mapping_state_end( ec_fsm_mapping_t *fsm /**< mapping state machine */ ) diff -r 3b81d074735c -r 3778920f61e4 master/fsm_mapping.h --- a/master/fsm_mapping.h Thu Feb 14 09:18:55 2008 +0000 +++ b/master/fsm_mapping.h Tue Feb 19 08:22:20 2008 +0000 @@ -63,6 +63,7 @@ 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 */ diff -r 3b81d074735c -r 3778920f61e4 master/fsm_master.c --- a/master/fsm_master.c Thu Feb 14 09:18:55 2008 +0000 +++ b/master/fsm_master.c Tue Feb 19 08:22:20 2008 +0000 @@ -205,7 +205,7 @@ 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 @@ 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 @@ 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); diff -r 3b81d074735c -r 3778920f61e4 master/fsm_master.h --- a/master/fsm_master.h Thu Feb 14 09:18:55 2008 +0000 +++ b/master/fsm_master.h Tue Feb 19 08:22:20 2008 +0000 @@ -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" diff -r 3b81d074735c -r 3778920f61e4 master/fsm_slave.c --- a/master/fsm_slave.c Thu Feb 14 09:18:55 2008 +0000 +++ b/master/fsm_slave.c Tue Feb 19 08:22:20 2008 +0000 @@ -41,8 +41,9 @@ #include "globals.h" #include "master.h" #include "mailbox.h" +#include "fsm_mapping.h" +#include "slave_config.h" #include "fsm_slave.h" -#include "fsm_mapping.h" /*****************************************************************************/ @@ -569,12 +570,12 @@ 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 @@ // 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 @@ 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 @@ 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 @@ { 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 @@ } // 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 @@ { 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; } + if (slave->sii_mailbox_protocols) { + offset = 2; // slave has mailboxes + } else { + offset = 0; + } + num_syncs = slave->sii_sync_count - offset; + // 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); - - for (i = 0; i < slave->sii_sync_count; i++) { - ec_sync_config(&slave->sii_syncs[i], - datagram->data + EC_SYNC_SIZE * i); + 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 @@ { 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 @@ // 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; diff -r 3b81d074735c -r 3778920f61e4 master/globals.h --- a/master/globals.h Thu Feb 14 09:18:55 2008 +0000 +++ b/master/globals.h Tue Feb 19 08:22:20 2008 +0000 @@ -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 -/*****************************************************************************/ - -/** - 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) -*/ - +/** 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) + */ #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 @@ /*****************************************************************************/ -/** - 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. - */ - -typedef enum -{ +/** Master request state. + */ +typedef enum { EC_REQUEST_QUEUED, EC_REQUEST_IN_PROGRESS, EC_REQUEST_COMPLETE, @@ -201,6 +204,7 @@ /*****************************************************************************/ +typedef struct ec_slave ec_slave_t; /**< \see ec_slave. */ typedef struct ec_sdo ec_sdo_t; /**< \see ec_sdo */ /*****************************************************************************/ diff -r 3b81d074735c -r 3778920f61e4 master/master.c --- a/master/master.c Thu Feb 14 09:18:55 2008 +0000 +++ b/master/master.c Tue Feb 19 08:22:20 2008 +0000 @@ -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 @@ 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 @@ master->datagram_index = 0; INIT_LIST_HEAD(&master->domains); + 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 @@ #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 @@ /*****************************************************************************/ #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 @@ /*****************************************************************************/ -/** - 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; - - list_for_each_entry_safe(slave, next_slave, &master->slaves, list) { + ec_slave_t *slave, *next; + + 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_domains(ec_master_t *master) { - ec_domain_t *domain, *next_d; - - list_for_each_entry_safe(domain, next_d, &master->domains, list) { + ec_domain_t *domain, *next; + + list_for_each_entry_safe(domain, next, &master->domains, list) { list_del(&domain->list); ec_domain_destroy(domain); } @@ -467,8 +484,6 @@ 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 @@ 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 @@ } #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 @@ /*****************************************************************************/ -/** - * 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; - - original = address; - - if (!address[0]) - goto out_invalid; - - if (address[0] == '#') { - alias_requested = 1; - address++; - } - - first = simple_strtoul(address, &remainder, 0); - if (remainder == address) - goto out_invalid; - - 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; - } - } - - 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; + ec_slave_config_t *sc; + + if (!master->configs_attached) + return; + + list_for_each_entry(sc, &master->configs, list) { + ec_slave_config_detach(sc); + } + + master->configs_attached = 0; +} + +/*****************************************************************************/ + +/** 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; + } + + 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 @@ /*****************************************************************************/ -/** - 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 @@ /*****************************************************************************/ -/** - 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 @@ /*****************************************************************************/ -/** - 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 @@ /*****************************************************************************/ -/** - * 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_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 *ecrt_master_slave_config(ec_master_t *master, + uint16_t alias, uint16_t position, uint32_t vendor_id, + uint32_t product_code) +{ + 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; - } - - return ec_slave_validate(slave, vendor_id, product_code) ? NULL : slave; -} - -/*****************************************************************************/ - -/** - 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 */ - ) + 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 sc; +} + +/*****************************************************************************/ + +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 @@ /*****************************************************************************/ -/** - * 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 */ - ) -{ - status->bus_status = +void ecrt_master_state(const ec_master_t *master, ec_master_state_t *state) +{ + 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_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 */ diff -r 3b81d074735c -r 3778920f61e4 master/master.h --- a/master/master.h Thu Feb 14 09:18:55 2008 +0000 +++ b/master/master.h Tue Feb 19 08:22:20 2008 +0000 @@ -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 @@ /*****************************************************************************/ -/** - 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 @@ /*****************************************************************************/ -/** - 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 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_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 *); diff -r 3b81d074735c -r 3778920f61e4 master/module.c --- a/master/module.c Thu Feb 14 09:18:55 2008 +0000 +++ b/master/module.c Tue Feb 19 08:22:20 2008 +0000 @@ -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 @@ * 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 @@ /*****************************************************************************/ -/** - 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 @@ /*****************************************************************************/ +unsigned int ecrt_version_magic(void) +{ + return ECRT_VERSION_MAGIC; +} + +/*****************************************************************************/ + /** \cond */ module_init(ec_init_module); diff -r 3b81d074735c -r 3778920f61e4 master/pdo.c --- a/master/pdo.c Thu Feb 14 09:18:55 2008 +0000 +++ b/master/pdo.c Tue Feb 19 08:22:20 2008 +0000 @@ -44,66 +44,196 @@ /*****************************************************************************/ -/** - * PDO constructor. - */ - -void ec_pdo_init(ec_pdo_t *pdo /**< EtherCAT PDO */) -{ +void ec_pdo_clear_entries(ec_pdo_t *); + +/*****************************************************************************/ + +/** 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. - */ - -int ec_pdo_copy(ec_pdo_t *pdo, const ec_pdo_t *other_pdo) -{ - ec_pdo_entry_t *entry, *other_entry, *next; - - // make flat copy - *pdo = *other_pdo; - - INIT_LIST_HEAD(&pdo->entries); - list_for_each_entry(other_entry, &other_pdo->entries, list) { +/** Set Pdo name. + */ +int ec_pdo_set_name( + ec_pdo_t *pdo, /**< Pdo. */ + const char *name /**< New name. */ + ) +{ + unsigned int len; + + if (pdo->name) + kfree(pdo->name); + + 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; - } - - *entry = *other_entry; // flat copy is sufficient + return -1; + } + + if (ec_pdo_entry_init_copy(entry, other_entry)) { + kfree(entry); + return -1; + } + 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; +} + +/*****************************************************************************/ diff -r 3b81d074735c -r 3778920f61e4 master/pdo.h --- a/master/pdo.h Thu Feb 14 09:18:55 2008 +0000 +++ b/master/pdo.h Tue Feb 19 08:22:20 2008 +0000 @@ -43,59 +43,47 @@ #include +#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 *); /*****************************************************************************/ diff -r 3b81d074735c -r 3778920f61e4 master/pdo_mapping.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/master/pdo_mapping.c Tue Feb 19 08:22:20 2008 +0000 @@ -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 + +#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; +} + +/*****************************************************************************/ diff -r 3b81d074735c -r 3778920f61e4 master/pdo_mapping.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/master/pdo_mapping.h Tue Feb 19 08:22:20 2008 +0000 @@ -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 + +#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 diff -r 3b81d074735c -r 3778920f61e4 master/slave.c --- a/master/slave.c Thu Feb 14 09:18:55 2008 +0000 +++ b/master/slave.c Tue Feb 19 08:22:20 2008 +0000 @@ -42,9 +42,11 @@ #include #include "globals.h" -#include "slave.h" #include "datagram.h" #include "master.h" +#include "slave_config.h" + +#include "slave.h" /*****************************************************************************/ @@ -113,13 +115,12 @@ 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 @@ 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 @@ { 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 @@ { 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 @@ 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); @@ -292,33 +287,6 @@ /*****************************************************************************/ /** - 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 @@ { 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 @@ 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 @@ } 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 @@ 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 @@ // 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 @@ } 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 @@ /*****************************************************************************/ -/** - * 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 @@ 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 @@ 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 @@ 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 @@ ? "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 @@ 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 @@ 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 @@ \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 @@ 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 */ - -/*****************************************************************************/ +/*****************************************************************************/ diff -r 3b81d074735c -r 3778920f61e4 master/slave.h --- a/master/slave.h Thu Feb 14 09:18:55 2008 +0000 +++ b/master/slave.h Tue Feb 19 08:22:20 2008 +0000 @@ -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 @@ /**< 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 @@ /*****************************************************************************/ -/** - * 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 @@ 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 @@ 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_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); diff -r 3b81d074735c -r 3778920f61e4 master/slave_config.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/master/slave_config.c Tue Feb 19 08:22:20 2008 +0000 @@ -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 + +#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 */ + +/*****************************************************************************/ diff -r 3b81d074735c -r 3778920f61e4 master/slave_config.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/master/slave_config.h Tue Feb 19 08:22:20 2008 +0000 @@ -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 +#include + +#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 diff -r 3b81d074735c -r 3778920f61e4 master/sync.c --- a/master/sync.c Thu Feb 14 09:18:55 2008 +0000 +++ b/master/sync.c Tue Feb 19 08:22:20 2008 +0000 @@ -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 */ +void ec_sync_config( + const ec_sync_t *sync, /**< Sync manager. */ + uint16_t data_size, /**< Data size. */ + uint8_t *data /**> Configuration memory. */ ) { - 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 */ - ) -{ - 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 @@ /*****************************************************************************/ -/** - * 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 @@ } 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; } /*****************************************************************************/ diff -r 3b81d074735c -r 3778920f61e4 master/sync.h --- a/master/sync.h Thu Feb 14 09:18:55 2008 +0000 +++ b/master/sync.h Tue Feb 19 08:22:20 2008 +0000 @@ -31,10 +31,9 @@ * *****************************************************************************/ -/** - \file - EtherCAT sync manager. -*/ +/** \file + * EtherCAT sync manager. + */ /*****************************************************************************/ @@ -44,60 +43,46 @@ #include #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. + */ +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_CUSTOM, /**< PDO mapping configured externally. */ +} ec_sync_mapping_source_t; /*****************************************************************************/ -/** - * EtherCAT sync manager PDO mapping information source. +/** Sync manager. */ - -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; - -/*****************************************************************************/ - -/** - * 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 *); /*****************************************************************************/