# HG changeset patch # User Florian Pose # Date 1155134324 0 # Node ID 9440f4ff25c76c607bb433945c77cbbfa177329f # Parent e675450f2174152d0b701dda091293beafd706fd MERGE trunk -r494:513 -> branches/stable-1.1 (SDO configuration, bugfixes, EoE frame rates) diff -r e675450f2174 -r 9440f4ff25c7 TODO --- a/TODO Thu Aug 03 12:59:01 2006 +0000 +++ b/TODO Wed Aug 09 14:38:44 2006 +0000 @@ -8,6 +8,12 @@ Important things to do: +* Add Kobjects in constructors. + +* Read AL Status Code with AL Control Response. + +* Coupling of EoE handlers before configuring slaves. + * SysFS interface - Add secondary slave address - Add SDO dictionary diff -r e675450f2174 -r 9440f4ff25c7 examples/mini/mini.c --- a/examples/mini/mini.c Thu Aug 03 12:59:01 2006 +0000 +++ b/examples/mini/mini.c Wed Aug 09 14:38:44 2006 +0000 @@ -64,6 +64,7 @@ ec_pdo_reg_t domain1_pdos[] = { {"1", Beckhoff_EL4132_Output1, &r_ana_out}, + {"8", Beckhoff_EL5001_Value, NULL}, {} }; @@ -123,6 +124,8 @@ int __init init_mini_module(void) { + ec_slave_t *slave; + printk(KERN_INFO "=== Starting Minimal EtherCAT environment... ===\n"); if ((master = ecrt_request_master(0)) == NULL) { @@ -145,6 +148,12 @@ goto out_release_master; } + if (!(slave = ecrt_master_get_slave(master, "8"))) + goto out_release_master; + + if (ecrt_slave_conf_sdo8(slave, 0x4061, 1, 0)) + goto out_release_master; + printk(KERN_INFO "Activating master...\n"); if (ecrt_master_activate(master)) { printk(KERN_ERR "Failed to activate master!\n"); diff -r e675450f2174 -r 9440f4ff25c7 examples/rtai/rtai_sample.c --- a/examples/rtai/rtai_sample.c Thu Aug 03 12:59:01 2006 +0000 +++ b/examples/rtai/rtai_sample.c Wed Aug 09 14:38:44 2006 +0000 @@ -54,6 +54,8 @@ // RTAI task frequency in Hz #define FREQUENCY 10000 +#define INHIBIT_TIME 20 + #define TIMERTICKS (1000000000 / FREQUENCY) /*****************************************************************************/ @@ -61,7 +63,7 @@ // RTAI RT_TASK task; SEM master_sem; -cycles_t t_last_start = 0, t_critical; +cycles_t t_last_cycle = 0, t_critical; // EtherCAT ec_master_t *master = NULL; @@ -84,7 +86,7 @@ { while (1) { - t_last_start = get_cycles(); + t_last_cycle = get_cycles(); rt_sem_wait(&master_sem); ecrt_master_receive(master); @@ -106,7 +108,7 @@ int request_lock(void *data) { // too close to the next RT cycle: deny access... - if (get_cycles() - t_last_start > t_critical) return -1; + if (get_cycles() - t_last_cycle > t_critical) return -1; // allow access rt_sem_wait(&master_sem); @@ -130,13 +132,14 @@ rt_sem_init(&master_sem, 1); - t_critical = cpu_khz * 800 / FREQUENCY; // ticks for 80% - - if ((master = ecrt_request_master(0)) == NULL) { + t_critical = cpu_khz * 1000 / FREQUENCY - cpu_khz * INHIBIT_TIME / 1000; + + if (!(master = ecrt_request_master(0))) { printk(KERN_ERR "Requesting master 0 failed!\n"); goto out_return; } + ecrt_master_callbacks(master, request_lock, release_lock, NULL); printk(KERN_INFO "Registering domain...\n"); diff -r e675450f2174 -r 9440f4ff25c7 include/ecdb.h --- a/include/ecdb.h Thu Aug 03 12:59:01 2006 +0000 +++ b/include/ecdb.h Wed Aug 09 14:38:44 2006 +0000 @@ -46,6 +46,9 @@ #define Beckhoff_EL4132_Output1 0x00000002, 0x10243052, 0x6411, 1 #define Beckhoff_EL4132_Output2 0x00000002, 0x10243052, 0x6411, 2 +#define Beckhoff_EL5001_Status 0x00000002, 0x13893052, 0x3101, 1 +#define Beckhoff_EL5001_Value 0x00000002, 0x13893052, 0x3101, 2 + /*****************************************************************************/ #endif diff -r e675450f2174 -r 9440f4ff25c7 master/device.c --- a/master/device.c Thu Aug 03 12:59:01 2006 +0000 +++ b/master/device.c Wed Aug 09 14:38:44 2006 +0000 @@ -245,7 +245,9 @@ ec_debug_send(&device->dbg, data, size); - ec_master_receive(device->master, data + ETH_HLEN, size - ETH_HLEN); + ec_master_receive_datagrams(device->master, + data + ETH_HLEN, + size - ETH_HLEN); } /*****************************************************************************/ diff -r e675450f2174 -r 9440f4ff25c7 master/domain.c --- a/master/domain.c Thu Aug 03 12:59:01 2006 +0000 +++ b/master/domain.c Wed Aug 09 14:38:44 2006 +0000 @@ -106,6 +106,8 @@ domain->data_size = 0; domain->base_address = 0; domain->response_count = 0xFFFFFFFF; + domain->t_last = 0; + domain->working_counter_changes = 0; INIT_LIST_HEAD(&domain->data_regs); INIT_LIST_HEAD(&domain->datagrams); @@ -151,7 +153,7 @@ /*****************************************************************************/ /** - Registeres a data field in a domain. + Registeres a PDO entry. \return 0 in case of success, else < 0 */ @@ -225,7 +227,7 @@ /*****************************************************************************/ /** - Clears the list of the registered data fields. + Clears the list of the data registrations. */ void ec_domain_clear_data_regs(ec_domain_t *domain /**< EtherCAT domain */) @@ -511,6 +513,7 @@ { unsigned int working_counter_sum; ec_datagram_t *datagram; + cycles_t t_now = get_cycles(); working_counter_sum = 0; list_for_each_entry(datagram, &domain->datagrams, list) { @@ -520,9 +523,23 @@ } if (working_counter_sum != domain->response_count) { + domain->working_counter_changes++; domain->response_count = working_counter_sum; - EC_INFO("Domain %i working counter change: %i\n", domain->index, - domain->response_count); + } + + if (domain->working_counter_changes && + (u32) (t_now - domain->t_last) / cpu_khz > 1000) { + domain->t_last = t_now; + if (domain->working_counter_changes == 1) { + EC_INFO("Domain %i working counter change: %i\n", domain->index, + domain->response_count); + } + else { + EC_INFO("Domain %i: %u WC changes. Current response count: %i\n", + domain->index, domain->working_counter_changes, + domain->response_count); + } + domain->working_counter_changes = 0; } ec_domain_queue(domain); diff -r e675450f2174 -r 9440f4ff25c7 master/domain.h --- a/master/domain.h Thu Aug 03 12:59:01 2006 +0000 +++ b/master/domain.h Wed Aug 09 14:38:44 2006 +0000 @@ -67,6 +67,9 @@ uint32_t base_address; /**< logical offset address of the process data */ unsigned int response_count; /**< number of responding slaves */ struct list_head data_regs; /**< PDO data registrations */ + unsigned int working_counter_changes; /**< working counter changes + since last notification */ + cycles_t t_last; /**< time of last notification */ }; /*****************************************************************************/ diff -r e675450f2174 -r 9440f4ff25c7 master/ethernet.c --- a/master/ethernet.c Thu Aug 03 12:59:01 2006 +0000 +++ b/master/ethernet.c Wed Aug 09 14:38:44 2006 +0000 @@ -101,6 +101,12 @@ eoe->tx_frame_number = 0xFF; memset(&eoe->stats, 0, sizeof(struct net_device_stats)); + eoe->rx_counter = 0; + eoe->tx_counter = 0; + eoe->rx_rate = 0; + eoe->tx_rate = 0; + eoe->t_last = 0; + if (!(eoe->dev = alloc_netdev(sizeof(ec_eoe_t *), "eoe%d", ether_setup))) { EC_ERR("Unable to allocate net_device for EoE handler!\n"); @@ -237,7 +243,7 @@ #if EOE_DEBUG_LEVEL > 1 EC_DBG(""); for (i = 0; i < current_size; i++) { - printk("%02X ", frame->skb->data[eoe->tx_offset + i]); + printk("%02X ", eoe->tx_frame->skb->data[eoe->tx_offset + i]); if ((i + 1) % 16 == 0) { printk("\n"); EC_DBG(""); @@ -272,10 +278,22 @@ void ec_eoe_run(ec_eoe_t *eoe /**< EoE handler */) { + cycles_t t_now; + if (!eoe->opened) return; // call state function eoe->state(eoe); + + // update statistics + t_now = get_cycles(); + if ((u32) (t_now - eoe->t_last) > cpu_khz * 1000) { + eoe->rx_rate = eoe->rx_counter * 8; + eoe->tx_rate = eoe->tx_counter * 8; + eoe->rx_counter = 0; + eoe->tx_counter = 0; + eoe->t_last = t_now; + } } /*****************************************************************************/ @@ -285,7 +303,7 @@ \return 1 if the device is "up", 0 if it is "down" */ -unsigned int ec_eoe_active(const ec_eoe_t *eoe /**< EoE handler */) +int ec_eoe_active(const ec_eoe_t *eoe /**< EoE handler */) { return eoe->slave && eoe->opened; } @@ -350,6 +368,9 @@ uint8_t *data, frame_type, last_fragment, time_appended; uint8_t frame_number, fragment_offset, fragment_number; off_t offset; +#if EOE_DEBUG_LEVEL > 1 + unsigned int i; +#endif if (eoe->datagram.state != EC_DATAGRAM_RECEIVED) { eoe->stats.rx_errors++; @@ -452,6 +473,7 @@ // update statistics eoe->stats.rx_packets++; eoe->stats.rx_bytes += eoe->rx_skb->len; + eoe->rx_counter += eoe->rx_skb->len; #if EOE_DEBUG_LEVEL > 0 EC_DBG("EoE RX frame completed with %u octets.\n", @@ -490,7 +512,7 @@ void ec_eoe_state_tx_start(ec_eoe_t *eoe /**< EoE handler */) { #if EOE_DEBUG_LEVEL > 0 - unsigned int wakeup; + unsigned int wakeup = 0; #endif if (!eoe->slave->online || !eoe->slave->master->device->link_state) @@ -568,6 +590,7 @@ if (eoe->tx_offset >= eoe->tx_frame->skb->len) { eoe->stats.tx_packets++; eoe->stats.tx_bytes += eoe->tx_frame->skb->len; + eoe->tx_counter += eoe->tx_frame->skb->len; dev_kfree_skb(eoe->tx_frame->skb); kfree(eoe->tx_frame); eoe->tx_frame = NULL; diff -r e675450f2174 -r 9440f4ff25c7 master/ethernet.h --- a/master/ethernet.h Thu Aug 03 12:59:01 2006 +0000 +++ b/master/ethernet.h Wed Aug 09 14:38:44 2006 +0000 @@ -78,10 +78,13 @@ struct net_device *dev; /**< net_device for virtual ethernet device */ struct net_device_stats stats; /**< device statistics */ unsigned int opened; /**< net_device is opened */ + cycles_t t_last; /**< time of last rate output */ struct sk_buff *rx_skb; /**< current rx socket buffer */ off_t rx_skb_offset; /**< current write pointer in the socket buffer */ size_t rx_skb_size; /**< size of the allocated socket buffer memory */ uint8_t rx_expected_fragment; /**< next expected fragment number */ + uint32_t rx_counter; /**< octets received during last second */ + uint32_t rx_rate; /**< receive rate (bps) */ struct list_head tx_queue; /**< queue for frames to send */ unsigned int tx_queue_active; /**< kernel netif queue started */ unsigned int tx_queued_frames; /**< number of frames in the queue */ @@ -90,6 +93,8 @@ uint8_t tx_frame_number; /**< number of the transmitted frame */ uint8_t tx_fragment_number; /**< number of the fragment */ size_t tx_offset; /**< number of octets sent */ + uint32_t tx_counter; /**< octets transmitted during last second */ + uint32_t tx_rate; /**< transmit rate (bps) */ }; /*****************************************************************************/ @@ -97,6 +102,6 @@ int ec_eoe_init(ec_eoe_t *); void ec_eoe_clear(ec_eoe_t *); void ec_eoe_run(ec_eoe_t *); -unsigned int ec_eoe_active(const ec_eoe_t *); +int ec_eoe_active(const ec_eoe_t *); /*****************************************************************************/ diff -r e675450f2174 -r 9440f4ff25c7 master/fsm.c --- a/master/fsm.c Thu Aug 03 12:59:01 2006 +0000 +++ b/master/fsm.c Wed Aug 09 14:38:44 2006 +0000 @@ -41,6 +41,7 @@ #include "globals.h" #include "fsm.h" #include "master.h" +#include "mailbox.h" /*****************************************************************************/ @@ -73,6 +74,7 @@ void ec_fsm_slaveconf_sync(ec_fsm_t *); void ec_fsm_slaveconf_preop(ec_fsm_t *); void ec_fsm_slaveconf_fmmu(ec_fsm_t *); +void ec_fsm_slaveconf_sdoconf(ec_fsm_t *); void ec_fsm_slaveconf_saveop(ec_fsm_t *); void ec_fsm_slaveconf_op(ec_fsm_t *); @@ -90,9 +92,16 @@ void ec_fsm_change_ack(ec_fsm_t *); void ec_fsm_change_check_ack(ec_fsm_t *); +void ec_fsm_coe_down_start(ec_fsm_t *); +void ec_fsm_coe_down_request(ec_fsm_t *); +void ec_fsm_coe_down_check(ec_fsm_t *); +void ec_fsm_coe_down_response(ec_fsm_t *); + void ec_fsm_end(ec_fsm_t *); void ec_fsm_error(ec_fsm_t *); +void ec_canopen_abort_msg(uint32_t); + /*****************************************************************************/ /** @@ -1373,11 +1382,18 @@ return; } - if (!slave->base_fmmu_count) { - fsm->slave_state = ec_fsm_slaveconf_saveop; - fsm->change_new = EC_SLAVE_STATE_SAVEOP; - fsm->change_state = ec_fsm_change_start; - fsm->change_state(fsm); // execute immediately + if (!slave->base_fmmu_count) { // skip FMMU configuration + if (list_empty(&slave->sdo_confs)) { // skip SDO configuration + fsm->slave_state = ec_fsm_slaveconf_saveop; + fsm->change_new = EC_SLAVE_STATE_SAVEOP; + fsm->change_state = ec_fsm_change_start; + fsm->change_state(fsm); // execute immediately + return; + } + fsm->slave_state = ec_fsm_slaveconf_sdoconf; + fsm->sdodata = list_entry(slave->sdo_confs.next, ec_sdo_data_t, list); + fsm->coe_state = ec_fsm_coe_down_start; + fsm->coe_state(fsm); // execute immediately return; } @@ -1403,6 +1419,7 @@ void ec_fsm_slaveconf_fmmu(ec_fsm_t *fsm /**< finite state machine */) { ec_datagram_t *datagram = &fsm->datagram; + ec_slave_t *slave = fsm->slave; if (datagram->state != EC_DATAGRAM_RECEIVED || datagram->working_counter != 1) { @@ -1413,6 +1430,51 @@ return; } + // No CoE configuration to be applied? Jump to SAVEOP state. + if (list_empty(&slave->sdo_confs)) { // skip SDO configuration + // set state to SAVEOP + fsm->slave_state = ec_fsm_slaveconf_saveop; + fsm->change_new = EC_SLAVE_STATE_SAVEOP; + fsm->change_state = ec_fsm_change_start; + fsm->change_state(fsm); // execute immediately + return; + } + + fsm->slave_state = ec_fsm_slaveconf_sdoconf; + fsm->sdodata = list_entry(slave->sdo_confs.next, ec_sdo_data_t, list); + fsm->coe_state = ec_fsm_coe_down_start; + fsm->coe_state(fsm); // execute immediately +} + +/*****************************************************************************/ + +/** + Slave state: SDOCONF. +*/ + +void ec_fsm_slaveconf_sdoconf(ec_fsm_t *fsm /**< finite state machine */) +{ + fsm->coe_state(fsm); // execute CoE state machine + + if (fsm->coe_state == ec_fsm_error) { + fsm->slave->error_flag = 1; + fsm->slave_state = ec_fsm_error; + return; + } + + if (fsm->coe_state != ec_fsm_end) return; + + // 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); + fsm->coe_state = ec_fsm_coe_down_start; + fsm->coe_state(fsm); // execute immediately + return; + } + + // All SDOs are now configured. + // set state to SAVEOP fsm->slave_state = ec_fsm_slaveconf_saveop; fsm->change_new = EC_SLAVE_STATE_SAVEOP; @@ -1934,7 +1996,244 @@ ec_master_queue_datagram(fsm->master, datagram); } -/*****************************************************************************/ +/****************************************************************************** + * CoE state machine + *****************************************************************************/ + +/** + CoE state: DOWN_START. +*/ + +void ec_fsm_coe_down_start(ec_fsm_t *fsm /**< finite state machine */) +{ + ec_datagram_t *datagram = &fsm->datagram; + ec_slave_t *slave = fsm->slave; + ec_sdo_data_t *sdodata = fsm->sdodata; + uint8_t *data; + + EC_INFO("Downloading SDO 0x%04X:%i to slave %i.\n", + sdodata->index, sdodata->subindex, slave->ring_position); + + if (slave->sii_rx_mailbox_size < 6 + 10 + sdodata->size) { + EC_ERR("SDO fragmenting not supported yet!\n"); + fsm->coe_state = ec_fsm_error; + return; + } + + if (!(data = ec_slave_mbox_prepare_send(slave, datagram, 0x03, + sdodata->size + 10))) { + fsm->coe_state = ec_fsm_error; + return; + } + + EC_WRITE_U16(data, 0x2 << 12); // SDO request + EC_WRITE_U8 (data + 2, (0x1 // size specified + | 0x1 << 5)); // Download request + EC_WRITE_U16(data + 3, sdodata->index); + EC_WRITE_U8 (data + 5, sdodata->subindex); + EC_WRITE_U32(data + 6, sdodata->size); + memcpy(data + 6, sdodata->data, sdodata->size); + + ec_master_queue_datagram(fsm->master, datagram); + fsm->coe_state = ec_fsm_coe_down_request; +} + +/*****************************************************************************/ + +/** + CoE state: DOWN_REQUEST. +*/ + +void ec_fsm_coe_down_request(ec_fsm_t *fsm /**< finite state machine */) +{ + ec_datagram_t *datagram = &fsm->datagram; + ec_slave_t *slave = fsm->slave; + + if (datagram->state != EC_DATAGRAM_RECEIVED + || datagram->working_counter != 1) { + fsm->coe_state = ec_fsm_error; + EC_ERR("Reception of CoE download request failed.\n"); + return; + } + + fsm->coe_start = get_cycles(); + + ec_slave_mbox_prepare_check(slave, datagram); // can not fail. + ec_master_queue_datagram(fsm->master, datagram); + fsm->coe_state = ec_fsm_coe_down_check; +} + +/*****************************************************************************/ + +/** + CoE state: DOWN_CHECK. +*/ + +void ec_fsm_coe_down_check(ec_fsm_t *fsm /**< finite state machine */) +{ + ec_datagram_t *datagram = &fsm->datagram; + ec_slave_t *slave = fsm->slave; + + if (datagram->state != EC_DATAGRAM_RECEIVED + || datagram->working_counter != 1) { + fsm->coe_state = ec_fsm_error; + EC_ERR("Reception of CoE mailbox check datagram failed.\n"); + return; + } + + if (!ec_slave_mbox_check(datagram)) { + if (get_cycles() - fsm->coe_start >= (cycles_t) 100 * cpu_khz) { + fsm->coe_state = ec_fsm_error; + EC_ERR("Timeout while checking SDO configuration on slave %i.\n", + slave->ring_position); + return; + } + + ec_slave_mbox_prepare_check(slave, datagram); // can not fail. + ec_master_queue_datagram(fsm->master, datagram); + return; + } + + // Fetch response + ec_slave_mbox_prepare_fetch(slave, datagram); // can not fail. + ec_master_queue_datagram(fsm->master, datagram); + fsm->coe_state = ec_fsm_coe_down_response; +} + +/*****************************************************************************/ + +/** + CoE state: DOWN_RESPONSE. +*/ + +void ec_fsm_coe_down_response(ec_fsm_t *fsm /**< finite state machine */) +{ + ec_datagram_t *datagram = &fsm->datagram; + ec_slave_t *slave = fsm->slave; + uint8_t *data; + size_t rec_size; + ec_sdo_data_t *sdodata = fsm->sdodata; + + if (datagram->state != EC_DATAGRAM_RECEIVED + || datagram->working_counter != 1) { + fsm->coe_state = ec_fsm_error; + EC_ERR("Reception of CoE download response failed.\n"); + return; + } + + if (!(data = ec_slave_mbox_fetch(slave, datagram, 0x03, &rec_size))) { + fsm->coe_state = ec_fsm_error; + return; + } + + if (rec_size < 6) { + fsm->coe_state = ec_fsm_error; + EC_ERR("Received data is too small (%i bytes):\n", rec_size); + ec_print_data(data, rec_size); + return; + } + + if (EC_READ_U16(data) >> 12 == 0x2 && // SDO request + EC_READ_U8 (data + 2) >> 5 == 0x4) { // abort SDO transfer request + fsm->coe_state = ec_fsm_error; + EC_ERR("SDO download 0x%04X:%X (%i bytes) aborted on slave %i.\n", + sdodata->index, sdodata->subindex, sdodata->size, + slave->ring_position); + if (rec_size < 10) { + EC_ERR("Incomplete Abort command:\n"); + ec_print_data(data, rec_size); + } + else + ec_canopen_abort_msg(EC_READ_U32(data + 6)); + return; + } + + if (EC_READ_U16(data) >> 12 != 0x3 || // SDO response + EC_READ_U8 (data + 2) >> 5 != 0x3 || // Download response + EC_READ_U16(data + 3) != sdodata->index || // index + EC_READ_U8 (data + 5) != sdodata->subindex) { // subindex + fsm->coe_state = ec_fsm_error; + EC_ERR("SDO download 0x%04X:%X (%i bytes) failed:\n", + sdodata->index, sdodata->subindex, sdodata->size); + EC_ERR("Invalid SDO download response at slave %i!\n", + slave->ring_position); + ec_print_data(data, rec_size); + return; + } + + fsm->coe_state = ec_fsm_end; // success +} + +/*****************************************************************************/ + +/** + SDO abort messages. + The "abort SDO transfer request" supplies an abort code, + which can be translated to clear text. This table does + the mapping of the codes and messages. +*/ + +const ec_code_msg_t sdo_abort_messages[] = { + {0x05030000, "Toggle bit not changed"}, + {0x05040000, "SDO protocol timeout"}, + {0x05040001, "Client/Server command specifier not valid or unknown"}, + {0x05040005, "Out of memory"}, + {0x06010000, "Unsupported access to an object"}, + {0x06010001, "Attempt to read a write-only object"}, + {0x06010002, "Attempt to write a read-only object"}, + {0x06020000, "This object does not exist in the object directory"}, + {0x06040041, "The object cannot be mapped into the PDO"}, + {0x06040042, "The number and length of the objects to be mapped would" + " exceed the PDO length"}, + {0x06040043, "General parameter incompatibility reason"}, + {0x06040047, "Gerneral internal incompatibility in device"}, + {0x06060000, "Access failure due to a hardware error"}, + {0x06070010, "Data type does not match, length of service parameter does" + " not match"}, + {0x06070012, "Data type does not match, length of service parameter too" + " high"}, + {0x06070013, "Data type does not match, length of service parameter too" + " low"}, + {0x06090011, "Subindex does not exist"}, + {0x06090030, "Value range of parameter exceeded"}, + {0x06090031, "Value of parameter written too high"}, + {0x06090032, "Value of parameter written too low"}, + {0x06090036, "Maximum value is less than minimum value"}, + {0x08000000, "General error"}, + {0x08000020, "Data cannot be transferred or stored to the application"}, + {0x08000021, "Data cannot be transferred or stored to the application" + " because of local control"}, + {0x08000022, "Data cannot be transferred or stored to the application" + " because of the present device state"}, + {0x08000023, "Object dictionary dynamic generation fails or no object" + " dictionary is present"}, + {} +}; + +/*****************************************************************************/ + +/** + Outputs an SDO abort message. +*/ + +void ec_canopen_abort_msg(uint32_t abort_code) +{ + const ec_code_msg_t *abort_msg; + + for (abort_msg = sdo_abort_messages; abort_msg->code; abort_msg++) { + if (abort_msg->code == abort_code) { + EC_ERR("SDO abort message 0x%08X: \"%s\".\n", + abort_msg->code, abort_msg->message); + return; + } + } + + EC_ERR("Unknown SDO abort code 0x%08X.\n", abort_code); +} + +/****************************************************************************** + * Common state functions + *****************************************************************************/ /** State: ERROR. diff -r e675450f2174 -r 9440f4ff25c7 master/fsm.h --- a/master/fsm.h Thu Aug 03 12:59:01 2006 +0000 +++ b/master/fsm.h Wed Aug 09 14:38:44 2006 +0000 @@ -74,8 +74,12 @@ cycles_t sii_start; /**< sii start */ void (*change_state)(ec_fsm_t *); /**< slave state change state function */ - uint8_t change_new; /**< input: new state */ + ec_slave_state_t change_new; /**< input: new state */ cycles_t change_start; /**< change start */ + + void (*coe_state)(ec_fsm_t *); /**< CoE state function */ + ec_sdo_data_t *sdodata; /**< input/output: SDO data object */ + cycles_t coe_start; /**< CoE timestamp */ }; /*****************************************************************************/ diff -r e675450f2174 -r 9440f4ff25c7 master/mailbox.h --- a/master/mailbox.h Thu Aug 03 12:59:01 2006 +0000 +++ b/master/mailbox.h Wed Aug 09 14:38:44 2006 +0000 @@ -53,11 +53,6 @@ uint8_t *ec_slave_mbox_fetch(const ec_slave_t *, ec_datagram_t *, uint8_t, size_t *); -uint8_t *ec_slave_mbox_simple_io(const ec_slave_t *, ec_datagram_t *, - size_t *); -uint8_t *ec_slave_mbox_simple_receive(const ec_slave_t *, ec_datagram_t *, - uint8_t, size_t *); - /*****************************************************************************/ #endif diff -r e675450f2174 -r 9440f4ff25c7 master/master.c --- a/master/master.c Thu Aug 03 12:59:01 2006 +0000 +++ b/master/master.c Wed Aug 09 14:38:44 2006 +0000 @@ -65,14 +65,12 @@ /** \cond */ -EC_SYSFS_READ_ATTR(slave_count); -EC_SYSFS_READ_ATTR(mode); +EC_SYSFS_READ_ATTR(info); EC_SYSFS_READ_WRITE_ATTR(eeprom_write_enable); EC_SYSFS_READ_WRITE_ATTR(debug_level); static struct attribute *ec_def_attrs[] = { - &attr_slave_count, - &attr_mode, + &attr_info, &attr_eeprom_write_enable, &attr_debug_level, NULL, @@ -121,6 +119,13 @@ master->eoe_timer.data = (unsigned long) master; master->internal_lock = SPIN_LOCK_UNLOCKED; master->eoe_running = 0; + master->eoe_checked = 0; + for (i = 0; i < HZ; i++) { + master->idle_cycle_times[i] = 0; + master->eoe_cycle_times[i] = 0; + } + master->idle_cycle_time_pos = 0; + master->eoe_cycle_time_pos = 0; // create workqueue if (!(master->workqueue = create_singlethread_workqueue("EtherCAT"))) { @@ -239,6 +244,7 @@ master->stats.timeouts = 0; master->stats.delayed = 0; master->stats.corrupted = 0; + master->stats.skipped = 0; master->stats.unmatched = 0; master->stats.t_last = 0; @@ -286,9 +292,9 @@ // check, if the datagram is already queued list_for_each_entry(queued_datagram, &master->datagram_queue, queue) { if (queued_datagram == datagram) { + master->stats.skipped++; + ec_master_output_stats(master); datagram->state = EC_DATAGRAM_QUEUED; - if (unlikely(master->debug_level)) - EC_WARN("datagram already queued.\n"); return; } } @@ -405,10 +411,10 @@ \return 0 in case of success, else < 0 */ -void ec_master_receive(ec_master_t *master, /**< EtherCAT master */ - const uint8_t *frame_data, /**< received data */ - size_t size /**< size of the received data */ - ) +void ec_master_receive_datagrams(ec_master_t *master, /**< EtherCAT master */ + const uint8_t *frame_data, /**< frame data */ + size_t size /**< size of the received data */ + ) { size_t frame_size, data_size; uint8_t datagram_type, datagram_index; @@ -537,6 +543,10 @@ EC_WARN("%i frame(s) CORRUPTED!\n", master->stats.corrupted); master->stats.corrupted = 0; } + if (master->stats.skipped) { + EC_WARN("%i datagram(s) SKIPPED!\n", master->stats.skipped); + master->stats.skipped = 0; + } if (master->stats.unmatched) { EC_WARN("%i datagram(s) UNMATCHED!\n", master->stats.unmatched); master->stats.unmatched = 0; @@ -600,20 +610,28 @@ void ec_master_idle_run(void *data /**< master pointer */) { ec_master_t *master = (ec_master_t *) data; + cycles_t start, end; // aquire master lock spin_lock_bh(&master->internal_lock); + start = get_cycles(); ecrt_master_receive(master); // execute master state machine ec_fsm_execute(&master->fsm); ecrt_master_send(master); + end = get_cycles(); // release master lock spin_unlock_bh(&master->internal_lock); + master->idle_cycle_times[master->idle_cycle_time_pos] + = (u32) (end - start) * 1000 / cpu_khz; + master->idle_cycle_time_pos++; + master->idle_cycle_time_pos %= HZ; + if (master->mode == EC_MASTER_MODE_IDLE) queue_delayed_work(master->workqueue, &master->idle_work, 1); } @@ -680,6 +698,78 @@ /*****************************************************************************/ /** + Formats master information for SysFS read access. + \return number of bytes written +*/ + +ssize_t ec_master_info(ec_master_t *master, /**< EtherCAT master */ + char *buffer /**< memory to store data */ + ) +{ + off_t off = 0; + ec_eoe_t *eoe; + uint32_t cur, sum, min, max, pos, i; + + off += sprintf(buffer + off, "\nMode: "); + switch (master->mode) { + case EC_MASTER_MODE_ORPHANED: + off += sprintf(buffer + off, "ORPHANED"); + break; + case EC_MASTER_MODE_IDLE: + off += sprintf(buffer + off, "IDLE"); + break; + case EC_MASTER_MODE_OPERATION: + off += sprintf(buffer + off, "OPERATION"); + break; + } + + off += sprintf(buffer + off, "\nSlaves: %i\n", + master->slave_count); + + off += sprintf(buffer + off, "\nTiming (min/avg/max) [us]:\n"); + + sum = 0; + min = 0xFFFFFFFF; + max = 0; + pos = master->idle_cycle_time_pos; + for (i = 0; i < HZ; i++) { + cur = master->idle_cycle_times[(i + pos) % HZ]; + sum += cur; + if (cur < min) min = cur; + if (cur > max) max = cur; + } + off += sprintf(buffer + off, " Idle cycle: %u / %u.%u / %u\n", + min, sum / HZ, (sum * 100 / HZ) % 100, max); + + sum = 0; + min = 0xFFFFFFFF; + max = 0; + pos = master->eoe_cycle_time_pos; + for (i = 0; i < HZ; i++) { + cur = master->eoe_cycle_times[(i + pos) % HZ]; + sum += cur; + if (cur < min) min = cur; + if (cur > max) max = cur; + } + off += sprintf(buffer + off, " EoE cycle: %u / %u.%u / %u\n", + min, sum / HZ, (sum * 100 / HZ) % 100, max); + + if (!list_empty(&master->eoe_handlers)) + off += sprintf(buffer + off, "\nEoE statistics (RX/TX) [bps]:\n"); + list_for_each_entry(eoe, &master->eoe_handlers, list) { + off += sprintf(buffer + off, " %s: %u / %u (%u KB/s)\n", + eoe->dev->name, eoe->rx_rate, eoe->tx_rate, + ((eoe->rx_rate + eoe->tx_rate) / 8 + 512) / 1024); + } + + off += sprintf(buffer + off, "\n"); + + return off; +} + +/*****************************************************************************/ + +/** Formats attribute data for SysFS read access. \return number of bytes to read */ @@ -691,18 +781,8 @@ { ec_master_t *master = container_of(kobj, ec_master_t, kobj); - if (attr == &attr_slave_count) { - return sprintf(buffer, "%i\n", master->slave_count); - } - else if (attr == &attr_mode) { - switch (master->mode) { - case EC_MASTER_MODE_ORPHANED: - return sprintf(buffer, "ORPHANED\n"); - case EC_MASTER_MODE_IDLE: - return sprintf(buffer, "IDLE\n"); - case EC_MASTER_MODE_OPERATION: - return sprintf(buffer, "OPERATION\n"); - } + if (attr == &attr_info) { + return ec_master_info(master, buffer); } else if (attr == &attr_debug_level) { return sprintf(buffer, "%i\n", master->debug_level); @@ -773,7 +853,7 @@ /*****************************************************************************/ /** - Starts/Stops Ethernet-over-EtherCAT processing on demand. + Starts Ethernet-over-EtherCAT processing on demand. */ void ec_master_eoe_start(ec_master_t *master /**< EtherCAT master */) @@ -782,12 +862,17 @@ ec_slave_t *slave; unsigned int coupled, found; - if (master->eoe_running) return; + if (master->eoe_running || master->eoe_checked) return; + + master->eoe_checked = 1; // if the locking callbacks are not set in operation mode, // the EoE timer my not be started. if (master->mode == EC_MASTER_MODE_OPERATION - && (!master->request_cb || !master->release_cb)) return; + && (!master->request_cb || !master->release_cb)) { + EC_INFO("No EoE processing because of missing locking callbacks.\n"); + return; + } // decouple all EoE handlers list_for_each_entry(eoe, &master->eoe_handlers, list) @@ -843,6 +928,8 @@ { ec_eoe_t *eoe; + master->eoe_checked = 0; + if (!master->eoe_running) return; EC_INFO("Stopping EoE processing.\n"); @@ -871,6 +958,7 @@ ec_master_t *master = (ec_master_t *) data; ec_eoe_t *eoe; unsigned int active = 0; + cycles_t start, end; list_for_each_entry(eoe, &master->eoe_handlers, list) { if (ec_eoe_active(eoe)) active++; @@ -890,11 +978,15 @@ goto queue_timer; // actual EoE stuff + start = get_cycles(); ecrt_master_receive(master); + list_for_each_entry(eoe, &master->eoe_handlers, list) { ec_eoe_run(eoe); } + ecrt_master_send(master); + end = get_cycles(); // release lock... if (master->mode == EC_MASTER_MODE_OPERATION) { @@ -904,6 +996,11 @@ spin_unlock(&master->internal_lock); } + master->eoe_cycle_times[master->eoe_cycle_time_pos] + = (u32) (end - start) * 1000 / cpu_khz; + master->eoe_cycle_time_pos++; + master->eoe_cycle_time_pos %= HZ; + queue_timer: master->eoe_timer.expires += HZ / EC_EOE_FREQUENCY; add_timer(&master->eoe_timer); @@ -941,6 +1038,69 @@ } } +/*****************************************************************************/ + +/** + Measures the time, a frame is on the bus. +*/ + +void ec_master_measure_bus_time(ec_master_t *master) +{ + ec_datagram_t datagram; + cycles_t t_start, t_end, t_timeout; + uint32_t times[100], sum, min, max, i; + + ec_datagram_init(&datagram); + + if (ec_datagram_brd(&datagram, 0x130, 2)) { + EC_ERR("Failed to allocate datagram for bus time measuring.\n"); + ec_datagram_clear(&datagram); + return; + } + + t_timeout = (cycles_t) EC_IO_TIMEOUT * (cpu_khz / 1000); + + sum = 0; + min = 0xFFFFFFFF; + max = 0; + + for (i = 0; i < 100; i++) { + ec_master_queue_datagram(master, &datagram); + ecrt_master_send(master); + t_start = get_cycles(); + + while (1) { // active waiting + ec_device_call_isr(master->device); + t_end = get_cycles(); // take current time + + if (datagram.state == EC_DATAGRAM_RECEIVED) { + break; + } + else if (datagram.state == EC_DATAGRAM_ERROR) { + EC_WARN("Failed to measure bus time.\n"); + goto error; + } + else if (t_end - t_start >= t_timeout) { + EC_WARN("Timeout while measuring bus time.\n"); + goto error; + } + } + + times[i] = (unsigned int) (t_end - t_start) * 1000 / cpu_khz; + sum += times[i]; + if (times[i] > max) max = times[i]; + if (times[i] < min) min = times[i]; + } + + EC_INFO("Bus time is (min/avg/max) %u/%u.%u/%u us.\n", + min, sum / 100, sum % 100, max); + + error: + // Dequeue and free datagram + list_del(&datagram.queue); + ec_datagram_clear(&datagram); +} + /****************************************************************************** * Realtime interface *****************************************************************************/ @@ -1163,7 +1323,7 @@ ec_device_call_isr(master->device); t_received = get_cycles(); - t_timeout = (cycles_t) EC_IO_TIMEOUT * (cpu_khz / 1000); + t_timeout = EC_IO_TIMEOUT * cpu_khz / 1000; // dequeue all received datagrams list_for_each_entry_safe(datagram, next, &master->datagram_queue, queue) diff -r e675450f2174 -r 9440f4ff25c7 master/master.h --- a/master/master.h Thu Aug 03 12:59:01 2006 +0000 +++ b/master/master.h Wed Aug 09 14:38:44 2006 +0000 @@ -74,7 +74,10 @@ unsigned int timeouts; /**< datagram timeouts */ unsigned int delayed; /**< delayed datagrams */ unsigned int corrupted; /**< corrupted frames */ - unsigned int unmatched; /**< unmatched datagrams */ + unsigned int skipped; /**< skipped datagrams (the ones that were + requeued when not yet received) */ + unsigned int unmatched; /**< unmatched datagrams (received, but not + queued any longer) */ cycles_t t_last; /**< time of last output */ } ec_stats_t; @@ -94,11 +97,14 @@ struct kobject kobj; /**< kobject */ + ec_device_t *device; /**< EtherCAT device */ + + ec_fsm_t fsm; /**< master state machine */ + ec_master_mode_t mode; /**< master mode */ + struct list_head slaves; /**< list of slaves on the bus */ unsigned int slave_count; /**< number of slaves on the bus */ - ec_device_t *device; /**< EtherCAT device */ - struct list_head datagram_queue; /**< datagram queue */ uint8_t datagram_index; /**< current datagram index */ @@ -109,12 +115,17 @@ struct workqueue_struct *workqueue; /**< master workqueue */ struct work_struct idle_work; /**< free run work object */ - ec_fsm_t fsm; /**< master state machine */ - ec_master_mode_t mode; /**< master mode */ + uint32_t idle_cycle_times[HZ]; /**< Idle cycle times ring */ + unsigned int idle_cycle_time_pos; /**< time ring buffer position */ struct timer_list eoe_timer; /**< EoE timer object */ + uint32_t eoe_cycle_times[HZ]; /**< EoE cycle times ring */ + unsigned int eoe_cycle_time_pos; /**< time ring buffer position */ unsigned int eoe_running; /**< non-zero, if EoE processing is active. */ + unsigned int eoe_checked; /**< non-zero, if EoE processing is not + necessary. */ struct list_head eoe_handlers; /**< Ethernet-over-EtherCAT handlers */ + spinlock_t internal_lock; /**< spinlock used in idle mode */ int (*request_cb)(void *); /**< lock request callback */ void (*release_cb)(void *); /**< lock release callback */ @@ -139,7 +150,7 @@ void ec_master_eoe_stop(ec_master_t *); // IO -void ec_master_receive(ec_master_t *, const uint8_t *, size_t); +void ec_master_receive_datagrams(ec_master_t *, const uint8_t *, size_t); void ec_master_queue_datagram(ec_master_t *, ec_datagram_t *); // slave management @@ -148,6 +159,7 @@ // misc. void ec_master_output_stats(ec_master_t *); void ec_master_clear_slaves(ec_master_t *); +void ec_master_measure_bus_time(ec_master_t *); // other methods void ec_sync_config(const ec_sii_sync_t *, const ec_slave_t *, uint8_t *); diff -r e675450f2174 -r 9440f4ff25c7 master/module.c --- a/master/module.c Thu Aug 03 12:59:01 2006 +0000 +++ b/master/module.c Wed Aug 09 14:38:44 2006 +0000 @@ -369,6 +369,7 @@ return -1; } + ec_master_measure_bus_time(master); ec_master_idle_start(master); return 0; } @@ -429,6 +430,7 @@ goto out_release; } + ec_master_measure_bus_time(master); ec_master_idle_stop(master); ec_master_reset(master); master->mode = EC_MASTER_MODE_OPERATION; diff -r e675450f2174 -r 9440f4ff25c7 master/slave.c --- a/master/slave.c Thu Aug 03 12:59:01 2006 +0000 +++ b/master/slave.c Wed Aug 09 14:38:44 2006 +0000 @@ -155,6 +155,7 @@ INIT_LIST_HEAD(&slave->sii_syncs); INIT_LIST_HEAD(&slave->sii_pdos); INIT_LIST_HEAD(&slave->sdo_dictionary); + INIT_LIST_HEAD(&slave->sdo_confs); INIT_LIST_HEAD(&slave->varsize_fields); for (i = 0; i < 4; i++) { @@ -182,6 +183,7 @@ ec_sii_pdo_entry_t *entry, *next_ent; ec_sdo_t *sdo, *next_sdo; ec_sdo_entry_t *en, *next_en; + ec_sdo_data_t *sdodata, *next_sdodata; ec_varsize_t *var, *next_var; slave = container_of(kobj, ec_slave_t, kobj); @@ -231,6 +233,13 @@ kfree(sdo); } + // 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); + } + // free information about variable sized data fields list_for_each_entry_safe(var, next_var, &slave->varsize_fields, list) { list_del(&var->list); @@ -512,19 +521,18 @@ if (slave->sii_name) off += sprintf(buffer + off, "%s", slave->sii_name); - off += sprintf(buffer + off, "\n\nVendor ID: 0x%08X\n", + off += sprintf(buffer + off, "\nVendor ID: 0x%08X\n", slave->sii_vendor_id); off += sprintf(buffer + off, "Product code: 0x%08X\n\n", slave->sii_product_code); - off += sprintf(buffer + off, "Ring position: %i\n", slave->ring_position); + off += sprintf(buffer + off, "State: "); + off += ec_state_string(slave->current_state, buffer + off); + off += sprintf(buffer + off, "\nRing position: %i\n", + slave->ring_position); off += sprintf(buffer + off, "Advanced position: %i:%i\n\n", slave->coupler_index, slave->coupler_subindex); - off += sprintf(buffer + off, "State: "); - off += ec_state_string(slave->current_state, buffer + off); - off += sprintf(buffer + off, "\n\n"); - off += sprintf(buffer + off, "Data link status:\n"); for (i = 0; i < 4; i++) { off += sprintf(buffer + off, " Port %i (", i); @@ -602,7 +610,7 @@ if (slave->sii_image) off += sprintf(buffer + off, " Image: %s\n", slave->sii_image); if (slave->sii_order) - off += sprintf(buffer + off, " Order#: %s\n", slave->sii_order); + off += sprintf(buffer + off, " Order number: %s\n", slave->sii_order); if (!list_empty(&slave->sii_syncs)) off += sprintf(buffer + off, "\nSync-Managers:\n"); @@ -620,13 +628,13 @@ list_for_each_entry(pdo, &slave->sii_pdos, list) { off += sprintf(buffer + off, - " %s \"%s\" (0x%04X), -> Sync-Manager %i\n", + " %s \"%s\" (0x%04X), Sync-Manager %i\n", pdo->type == EC_RX_PDO ? "RXPDO" : "TXPDO", pdo->name ? pdo->name : "???", pdo->index, pdo->sync_index); list_for_each_entry(pdo_entry, &pdo->entries, list) { - off += sprintf(buffer + off, " \"%s\" 0x%04X:%X, %i Bit\n", + off += sprintf(buffer + off, " \"%s\" 0x%04X:%X, %i bit\n", pdo_entry->name ? pdo_entry->name : "???", pdo_entry->index, pdo_entry->subindex, pdo_entry->bit_length); @@ -840,6 +848,7 @@ ec_slave_t *slave = container_of(kobj, ec_slave_t, kobj); if (attr == &attr_state) { + char state[25]; if (!strcmp(buffer, "INIT\n")) slave->requested_state = EC_SLAVE_STATE_INIT; else if (!strcmp(buffer, "PREOP\n")) @@ -853,8 +862,9 @@ return -EINVAL; } + ec_state_string(slave->requested_state, state); EC_INFO("Accepted new state %s for slave %i.\n", - buffer, slave->ring_position); + state, slave->ring_position); slave->error_flag = 0; return size; } @@ -913,6 +923,47 @@ && slave->sii_product_code == 0x044C2C52; } +/*****************************************************************************/ + +/** + \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 %i 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; +} + /****************************************************************************** * Realtime interface *****************************************************************************/ @@ -922,6 +973,60 @@ \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); +} + +/*****************************************************************************/ + +/** + \return 0 in case of success, else < 0 + \ingroup RealtimeInterface +*/ + int ecrt_slave_pdo_size(ec_slave_t *slave, /**< EtherCAT slave */ uint16_t pdo_index, /**< PDO index */ uint8_t pdo_subindex, /**< PDO subindex */ @@ -989,6 +1094,9 @@ /**< \cond */ +EXPORT_SYMBOL(ecrt_slave_conf_sdo8); +EXPORT_SYMBOL(ecrt_slave_conf_sdo16); +EXPORT_SYMBOL(ecrt_slave_conf_sdo32); EXPORT_SYMBOL(ecrt_slave_pdo_size); /**< \endcond */ diff -r e675450f2174 -r 9440f4ff25c7 master/slave.h --- a/master/slave.h Thu Aug 03 12:59:01 2006 +0000 +++ b/master/slave.h Wed Aug 09 14:38:44 2006 +0000 @@ -199,6 +199,18 @@ /*****************************************************************************/ +typedef struct +{ + struct list_head list; + uint16_t index; + uint8_t subindex; + uint8_t *data; + size_t size; +} +ec_sdo_data_t; + +/*****************************************************************************/ + /** FMMU configuration. */ @@ -291,6 +303,7 @@ uint8_t fmmu_count; /**< number of FMMUs used */ struct list_head sdo_dictionary; /**< SDO directory list */ + struct list_head sdo_confs; /**< list of SDO configurations */ struct list_head varsize_fields; /**< size information for variable-sized data fields. */