fp@39: /****************************************************************************** fp@0: * fp@39: * $Id$ fp@0: * fp@197: * Copyright (C) 2006 Florian Pose, Ingenieurgemeinschaft IgH fp@197: * fp@197: * This file is part of the IgH EtherCAT Master. fp@197: * fp@197: * The IgH EtherCAT Master is free software; you can redistribute it fp@197: * and/or modify it under the terms of the GNU General Public License fp@246: * as published by the Free Software Foundation; either version 2 of the fp@246: * License, or (at your option) any later version. fp@197: * fp@197: * The IgH EtherCAT Master is distributed in the hope that it will be fp@197: * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of fp@197: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the fp@197: * GNU General Public License for more details. fp@197: * fp@197: * You should have received a copy of the GNU General Public License fp@197: * along with the IgH EtherCAT Master; if not, write to the Free Software fp@197: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA fp@197: * fp@246: * The right to use EtherCAT Technology is granted and comes free of fp@246: * charge under condition of compatibility of product made by fp@246: * Licensee. People intending to distribute/sell products based on the fp@246: * code, have to sign an agreement to guarantee that products using fp@246: * software based on IgH EtherCAT master stay compatible with the actual fp@246: * EtherCAT specification (which are released themselves as an open fp@246: * standard) as the (only) precondition to have the right to use EtherCAT fp@246: * Technology, IP and trade marks. fp@246: * fp@39: *****************************************************************************/ fp@0: fp@199: /** fp@199: \file fp@199: EtherCAT slave methods. fp@199: */ fp@199: fp@199: /*****************************************************************************/ fp@199: fp@24: #include fp@73: #include fp@0: fp@54: #include "globals.h" fp@54: #include "slave.h" fp@293: #include "datagram.h" fp@98: #include "master.h" fp@0: fp@39: /*****************************************************************************/ fp@0: fp@251: extern const ec_code_msg_t al_status_messages[]; fp@251: fp@251: /*****************************************************************************/ fp@251: fp@1732: void ec_slave_clear(struct kobject *); fp@1732: void ec_slave_sdos_clear(struct kobject *); fp@182: ssize_t ec_show_slave_attribute(struct kobject *, struct attribute *, char *); fp@256: ssize_t ec_store_slave_attribute(struct kobject *, struct attribute *, fp@256: const char *, size_t); fp@1744: char *ec_slave_sii_string(ec_slave_t *, unsigned int); fp@182: fp@182: /*****************************************************************************/ fp@182: fp@199: /** \cond */ fp@199: fp@1715: EC_SYSFS_READ_ATTR(info); fp@256: EC_SYSFS_READ_WRITE_ATTR(state); fp@269: EC_SYSFS_READ_WRITE_ATTR(eeprom); fp@1744: EC_SYSFS_READ_WRITE_ATTR(alias); fp@182: fp@182: static struct attribute *def_attrs[] = { fp@1715: &attr_info, fp@256: &attr_state, fp@266: &attr_eeprom, fp@1744: &attr_alias, fp@182: NULL, fp@182: }; fp@182: fp@182: static struct sysfs_ops sysfs_ops = { fp@256: .show = ec_show_slave_attribute, fp@256: .store = ec_store_slave_attribute fp@182: }; fp@182: fp@182: static struct kobj_type ktype_ec_slave = { fp@182: .release = ec_slave_clear, fp@182: .sysfs_ops = &sysfs_ops, fp@182: .default_attrs = def_attrs fp@182: }; fp@118: fp@1732: static struct kobj_type ktype_ec_slave_sdos = { fp@1732: .release = ec_slave_sdos_clear fp@1732: }; fp@1732: fp@199: /** \endcond */ fp@199: fp@118: /*****************************************************************************/ fp@118: fp@0: /** fp@195: Slave constructor. fp@195: \return 0 in case of success, else < 0 fp@195: */ fp@195: fp@195: int ec_slave_init(ec_slave_t *slave, /**< EtherCAT slave */ fp@195: ec_master_t *master, /**< EtherCAT master */ fp@195: uint16_t ring_position, /**< ring position */ fp@195: uint16_t station_address /**< station address to configure */ fp@182: ) fp@73: { fp@142: unsigned int i; fp@142: fp@182: slave->ring_position = ring_position; fp@182: slave->station_address = station_address; fp@182: fp@73: slave->master = master; fp@1715: fp@1744: slave->requested_state = EC_SLAVE_STATE_PREOP; fp@1715: slave->current_state = EC_SLAVE_STATE_UNKNOWN; fp@1739: slave->self_configured = 0; fp@1715: slave->error_flag = 0; fp@1744: slave->online_state = EC_SLAVE_ONLINE; fp@1715: slave->fmmu_count = 0; fp@1744: slave->pdos_registered = 0; fp@1715: fp@73: slave->base_type = 0; fp@73: slave->base_revision = 0; fp@73: slave->base_build = 0; fp@73: slave->base_fmmu_count = 0; fp@1715: fp@1715: slave->eeprom_data = NULL; fp@1715: slave->eeprom_size = 0; fp@1715: fp@114: slave->sii_alias = 0; fp@73: slave->sii_vendor_id = 0; fp@73: slave->sii_product_code = 0; fp@73: slave->sii_revision_number = 0; fp@73: slave->sii_serial_number = 0; fp@136: slave->sii_rx_mailbox_offset = 0; fp@136: slave->sii_rx_mailbox_size = 0; fp@136: slave->sii_tx_mailbox_offset = 0; fp@136: slave->sii_tx_mailbox_size = 0; fp@133: slave->sii_mailbox_protocols = 0; fp@1715: slave->sii_group = NULL; fp@1715: slave->sii_image = NULL; fp@1715: slave->sii_order = NULL; fp@1715: slave->sii_name = NULL; fp@1739: slave->sii_current_on_ebus = 0; fp@1715: fp@1744: slave->sii_strings = NULL; fp@1744: slave->sii_string_count = 0; fp@1744: slave->sii_syncs = NULL; fp@1744: slave->sii_sync_count = 0; fp@1715: INIT_LIST_HEAD(&slave->sii_pdos); fp@135: INIT_LIST_HEAD(&slave->sdo_dictionary); fp@1716: INIT_LIST_HEAD(&slave->sdo_confs); fp@1732: fp@1732: slave->sdo_dictionary_fetched = 0; fp@1746: slave->pdo_mapping_fetched = 0; fp@1732: slave->jiffies_preop = 0; fp@145: fp@183: for (i = 0; i < 4; i++) { fp@183: slave->dl_link[i] = 0; fp@183: slave->dl_loop[i] = 0; fp@183: slave->dl_signal[i] = 0; fp@190: slave->sii_physical_layer[i] = 0xFF; fp@145: } fp@182: fp@1732: // init kobject and add it to the hierarchy fp@1732: memset(&slave->kobj, 0x00, sizeof(struct kobject)); fp@1732: kobject_init(&slave->kobj); fp@1732: slave->kobj.ktype = &ktype_ec_slave; fp@1732: slave->kobj.parent = &master->kobj; fp@1732: if (kobject_set_name(&slave->kobj, "slave%03i", slave->ring_position)) { fp@1732: EC_ERR("Failed to set kobject name.\n"); fp@1732: goto out_slave_put; fp@1732: } fp@1732: if (kobject_add(&slave->kobj)) { fp@1732: EC_ERR("Failed to add slave's kobject.\n"); fp@1732: goto out_slave_put; fp@1732: } fp@1732: fp@1732: // init SDO kobject and add it to the hierarchy fp@1732: memset(&slave->sdo_kobj, 0x00, sizeof(struct kobject)); fp@1732: kobject_init(&slave->sdo_kobj); fp@1732: slave->sdo_kobj.ktype = &ktype_ec_slave_sdos; fp@1732: slave->sdo_kobj.parent = &slave->kobj; fp@1732: if (kobject_set_name(&slave->sdo_kobj, "sdos")) { fp@1732: EC_ERR("Failed to set kobject name.\n"); fp@1732: goto out_sdo_put; fp@1732: } fp@1732: if (kobject_add(&slave->sdo_kobj)) { fp@1732: EC_ERR("Failed to add SDOs kobject.\n"); fp@1732: goto out_sdo_put; fp@1732: } fp@1732: fp@182: return 0; fp@1732: fp@1732: out_sdo_put: fp@1732: kobject_put(&slave->sdo_kobj); fp@1732: kobject_del(&slave->kobj); fp@1732: out_slave_put: fp@1732: kobject_put(&slave->kobj); fp@1732: return -1; fp@73: } fp@73: fp@73: /*****************************************************************************/ fp@73: fp@73: /** fp@195: Slave destructor. fp@1732: Clears and frees a slave object. fp@1732: */ fp@1732: fp@1732: void ec_slave_destroy(ec_slave_t *slave /**< EtherCAT slave */) fp@1732: { fp@1732: ec_sdo_t *sdo, *next_sdo; fp@1732: fp@1732: // free all SDOs fp@1732: list_for_each_entry_safe(sdo, next_sdo, &slave->sdo_dictionary, list) { fp@1732: list_del(&sdo->list); fp@1732: ec_sdo_destroy(sdo); fp@1732: } fp@1732: fp@1732: // free SDO kobject fp@1732: kobject_del(&slave->sdo_kobj); fp@1732: kobject_put(&slave->sdo_kobj); fp@1732: fp@1732: // destroy self fp@1732: kobject_del(&slave->kobj); fp@1732: kobject_put(&slave->kobj); fp@1732: } fp@1732: fp@1732: /*****************************************************************************/ fp@1732: fp@1732: /** fp@1732: Clear and free slave. fp@1732: This method is called by the kobject, fp@1732: once there are no more references to it. fp@195: */ fp@195: fp@195: void ec_slave_clear(struct kobject *kobj /**< kobject of the slave */) fp@182: { fp@182: ec_slave_t *slave; fp@1744: ec_pdo_t *pdo, *next_pdo; fp@1716: ec_sdo_data_t *sdodata, *next_sdodata; fp@1744: unsigned int i; fp@118: fp@182: slave = container_of(kobj, ec_slave_t, kobj); fp@182: fp@1744: // free all strings fp@1744: if (slave->sii_strings) { fp@1744: for (i = 0; i < slave->sii_string_count; i++) fp@1744: kfree(slave->sii_strings[i]); fp@1744: kfree(slave->sii_strings); fp@118: } fp@121: fp@195: // free all sync managers fp@1744: if (slave->sii_syncs) { fp@1744: for (i = 0; i < slave->sii_sync_count; i++) { fp@1744: ec_sync_clear(&slave->sii_syncs[i]); fp@1744: } fp@1744: kfree(slave->sii_syncs); fp@1744: } fp@1744: fp@1744: // free all SII PDOs fp@1715: list_for_each_entry_safe(pdo, next_pdo, &slave->sii_pdos, list) { fp@126: list_del(&pdo->list); fp@1744: ec_pdo_clear(pdo); fp@126: kfree(pdo); fp@126: } fp@126: fp@1716: // free all SDO configurations fp@1716: list_for_each_entry_safe(sdodata, next_sdodata, &slave->sdo_confs, list) { fp@1716: list_del(&sdodata->list); fp@1716: kfree(sdodata->data); fp@1716: kfree(sdodata); fp@1716: } fp@1716: fp@266: if (slave->eeprom_data) kfree(slave->eeprom_data); fp@1732: fp@1732: kfree(slave); fp@1732: } fp@1732: fp@1732: /*****************************************************************************/ fp@1732: fp@1732: /** fp@1746: * SDO kobject clear method. fp@1746: */ fp@1732: fp@1732: void ec_slave_sdos_clear(struct kobject *kobj /**< kobject for SDOs */) fp@1732: { fp@1732: } fp@1732: fp@1732: /*****************************************************************************/ fp@1732: fp@1732: /** fp@1732: Reset slave from operation mode. fp@1732: */ fp@1732: fp@1732: void ec_slave_reset(ec_slave_t *slave /**< EtherCAT slave */) fp@1732: { fp@1732: ec_sdo_data_t *sdodata, *next_sdodata; fp@1744: unsigned int i; fp@1744: fp@1732: slave->fmmu_count = 0; fp@1744: slave->pdos_registered = 0; fp@1732: fp@1732: // free all SDO configurations fp@1732: list_for_each_entry_safe(sdodata, next_sdodata, &slave->sdo_confs, list) { fp@1732: list_del(&sdodata->list); fp@1732: kfree(sdodata->data); fp@1732: kfree(sdodata); fp@1732: } fp@1732: fp@1732: // remove estimated sync manager sizes fp@1744: for (i = 0; i < slave->sii_sync_count; i++) { fp@1744: slave->sii_syncs[i].est_length = 0; fp@1744: } fp@1744: } fp@1744: fp@1744: /*****************************************************************************/ fp@1744: fp@1744: /** fp@1744: * Sets the application state of a slave. fp@1732: */ fp@1732: fp@1744: void ec_slave_set_state(ec_slave_t *slave, /**< EtherCAT slave */ fp@1744: ec_slave_state_t new_state /**< new application state */ fp@1744: ) fp@1744: { fp@1744: if (new_state != slave->current_state) { fp@1744: if (slave->master->debug_level) { fp@1744: char old_state[EC_STATE_STRING_SIZE], fp@1744: cur_state[EC_STATE_STRING_SIZE]; fp@1744: ec_state_string(slave->current_state, old_state); fp@1744: ec_state_string(new_state, cur_state); fp@1744: EC_DBG("Slave %i: %s -> %s.\n", fp@1744: slave->ring_position, old_state, cur_state); fp@1744: } fp@1744: slave->current_state = new_state; fp@1744: } fp@1744: } fp@1744: fp@1744: /*****************************************************************************/ fp@1744: fp@1744: /** fp@1744: * Sets the online state of a slave. fp@1744: */ fp@1744: fp@1744: void ec_slave_set_online_state(ec_slave_t *slave, /**< EtherCAT slave */ fp@1744: ec_slave_online_state_t new_state /**< new online state */ fp@1744: ) fp@1744: { fp@1744: if (new_state == EC_SLAVE_OFFLINE && fp@1744: slave->online_state == EC_SLAVE_ONLINE) { fp@1744: if (slave->pdos_registered) fp@1744: slave->master->pdo_slaves_offline++; fp@1744: if (slave->master->debug_level) fp@1744: EC_DBG("Slave %i: offline.\n", slave->ring_position); fp@1744: } fp@1744: else if (new_state == EC_SLAVE_ONLINE && fp@1744: slave->online_state == EC_SLAVE_OFFLINE) { fp@1744: slave->error_flag = 0; // clear error flag fp@1744: if (slave->pdos_registered) fp@1744: slave->master->pdo_slaves_offline--; fp@1744: if (slave->master->debug_level) { fp@1744: char cur_state[EC_STATE_STRING_SIZE]; fp@1744: ec_state_string(slave->current_state, cur_state); fp@1744: EC_DBG("Slave %i: online (%s).\n", fp@1744: slave->ring_position, cur_state); fp@1744: } fp@1744: } fp@1744: fp@1744: slave->online_state = new_state; fp@1744: } fp@1744: fp@1744: /*****************************************************************************/ fp@1744: fp@1744: /** fp@1746: * Request a slave state and resets the error flag. fp@1744: */ fp@1744: fp@1744: void ec_slave_request_state(ec_slave_t *slave, /**< EtherCAT slave */ fp@1732: ec_slave_state_t state /**< new state */ fp@1732: ) fp@1732: { fp@1732: slave->requested_state = state; fp@1732: slave->error_flag = 0; fp@121: } fp@121: fp@121: /*****************************************************************************/ fp@121: fp@121: /** fp@195: Fetches data from a STRING category. fp@1746: \todo range checking fp@195: \return 0 in case of success, else < 0 fp@195: */ fp@195: fp@1744: int ec_slave_fetch_sii_strings( fp@1744: ec_slave_t *slave, /**< EtherCAT slave */ fp@1746: const uint8_t *data, /**< category data */ fp@1746: size_t data_size /**< number of bytes */ fp@1744: ) fp@1744: { fp@1744: int i; fp@118: size_t size; fp@118: off_t offset; fp@1744: fp@1744: slave->sii_string_count = data[0]; fp@1744: fp@1744: if (!slave->sii_string_count) fp@1744: return 0; fp@1744: fp@1744: if (!(slave->sii_strings = fp@1744: kmalloc(sizeof(char *) * slave->sii_string_count, fp@1744: GFP_KERNEL))) { fp@1744: EC_ERR("Failed to allocate string array memory.\n"); fp@1744: goto out_zero; fp@1744: } fp@1744: fp@118: offset = 1; fp@1744: for (i = 0; i < slave->sii_string_count; i++) { fp@118: size = data[offset]; fp@195: // allocate memory for string structure and data at a single blow fp@1744: if (!(slave->sii_strings[i] = fp@1744: kmalloc(sizeof(char) * size + 1, GFP_KERNEL))) { fp@118: EC_ERR("Failed to allocate string memory.\n"); fp@1744: goto out_free; fp@1744: } fp@1744: memcpy(slave->sii_strings[i], data + offset + 1, size); fp@1744: slave->sii_strings[i][size] = 0x00; // append binary zero fp@118: offset += 1 + size; fp@118: } fp@118: fp@118: return 0; fp@1744: fp@1744: out_free: fp@1744: for (i--; i >= 0; i--) kfree(slave->sii_strings[i]); fp@1744: kfree(slave->sii_strings); fp@1744: slave->sii_strings = NULL; fp@1744: out_zero: fp@1744: slave->sii_string_count = 0; fp@1744: return -1; fp@118: } fp@118: fp@118: /*****************************************************************************/ fp@118: fp@118: /** fp@195: Fetches data from a GENERAL category. fp@195: \return 0 in case of success, else < 0 fp@195: */ fp@195: fp@1746: int ec_slave_fetch_sii_general( fp@1744: ec_slave_t *slave, /**< EtherCAT slave */ fp@1746: const uint8_t *data, /**< category data */ fp@1746: size_t data_size /**< size in bytes */ fp@1744: ) fp@123: { fp@190: unsigned int i; fp@190: fp@1746: if (data_size != 32) { fp@1746: EC_ERR("Wrong size of general category (%u/32) in slave %u.\n", fp@1746: data_size, slave->ring_position); fp@1746: return -1; fp@1746: } fp@1746: fp@1744: slave->sii_group = ec_slave_sii_string(slave, data[0]); fp@1744: slave->sii_image = ec_slave_sii_string(slave, data[1]); fp@1744: slave->sii_order = ec_slave_sii_string(slave, data[2]); fp@1744: slave->sii_name = ec_slave_sii_string(slave, data[3]); fp@123: fp@190: for (i = 0; i < 4; i++) fp@195: slave->sii_physical_layer[i] = fp@195: (data[4] & (0x03 << (i * 2))) >> (i * 2); fp@1739: fp@1739: slave->sii_current_on_ebus = EC_READ_S16(data + 0x0C); fp@1746: fp@1746: return 0; fp@118: } fp@118: fp@118: /*****************************************************************************/ fp@118: fp@118: /** fp@195: Fetches data from a SYNC MANAGER category. fp@195: \return 0 in case of success, else < 0 fp@195: */ fp@195: fp@1744: int ec_slave_fetch_sii_syncs( fp@1744: ec_slave_t *slave, /**< EtherCAT slave */ fp@1744: const uint8_t *data, /**< category data */ fp@1746: size_t data_size /**< number of bytes */ fp@1744: ) fp@1744: { fp@1744: unsigned int i; fp@1744: ec_sync_t *sync; fp@1746: size_t memsize; fp@1746: fp@1746: // one sync manager struct is 4 words long fp@1746: if (data_size % 8) { fp@1746: EC_ERR("Invalid SII sync manager size %u in slave %u.\n", fp@1746: data_size, slave->ring_position); fp@1746: return -1; fp@1746: } fp@1746: fp@1746: slave->sii_sync_count = data_size / 8; fp@1746: fp@1746: memsize = sizeof(ec_sync_t) * slave->sii_sync_count; fp@1746: if (!(slave->sii_syncs = kmalloc(memsize, GFP_KERNEL))) { fp@1746: EC_ERR("Failed to allocate %u bytes for sync managers.\n", fp@1746: memsize); fp@1744: slave->sii_sync_count = 0; fp@1744: return -1; fp@1744: } fp@1744: fp@1744: for (i = 0; i < slave->sii_sync_count; i++, data += 8) { fp@1744: sync = &slave->sii_syncs[i]; fp@1744: fp@1744: ec_sync_init(sync, slave, i); fp@1715: sync->physical_start_address = EC_READ_U16(data); fp@1744: sync->length = EC_READ_U16(data + 2); fp@1744: sync->control_register = EC_READ_U8 (data + 4); fp@1744: sync->enable = EC_READ_U8 (data + 6); fp@126: } fp@126: fp@126: return 0; fp@118: } fp@118: fp@118: /*****************************************************************************/ fp@118: fp@118: /** fp@195: Fetches data from a [RT]XPDO category. fp@195: \return 0 in case of success, else < 0 fp@195: */ fp@195: fp@1744: int ec_slave_fetch_sii_pdos( fp@1744: ec_slave_t *slave, /**< EtherCAT slave */ fp@1744: const uint8_t *data, /**< category data */ fp@1746: size_t data_size, /**< number of bytes */ fp@1744: ec_pdo_type_t pdo_type /**< PDO type */ fp@1744: ) fp@1744: { fp@1744: ec_pdo_t *pdo; fp@1744: ec_pdo_entry_t *entry; fp@126: unsigned int entry_count, i; fp@126: fp@1746: while (data_size >= 8) { fp@1744: if (!(pdo = kmalloc(sizeof(ec_pdo_t), GFP_KERNEL))) { fp@126: EC_ERR("Failed to allocate PDO memory.\n"); fp@126: return -1; fp@126: } fp@126: fp@1744: ec_pdo_init(pdo); fp@126: pdo->type = pdo_type; fp@1715: pdo->index = EC_READ_U16(data); fp@1715: entry_count = EC_READ_U8(data + 2); fp@1715: pdo->sync_index = EC_READ_U8(data + 3); fp@1744: pdo->name = ec_slave_sii_string(slave, EC_READ_U8(data + 5)); fp@1715: list_add_tail(&pdo->list, &slave->sii_pdos); fp@126: fp@1746: data_size -= 8; fp@126: data += 8; fp@126: fp@126: for (i = 0; i < entry_count; i++) { fp@1744: if (!(entry = kmalloc(sizeof(ec_pdo_entry_t), GFP_KERNEL))) { fp@126: EC_ERR("Failed to allocate PDO entry memory.\n"); fp@126: return -1; fp@126: } fp@126: fp@1715: entry->index = EC_READ_U16(data); fp@1715: entry->subindex = EC_READ_U8(data + 2); fp@1744: entry->name = ec_slave_sii_string(slave, EC_READ_U8(data + 3)); fp@1715: entry->bit_length = EC_READ_U8(data + 5); fp@126: list_add_tail(&entry->list, &pdo->entries); fp@126: fp@1746: data_size -= 8; fp@126: data += 8; fp@126: } fp@1744: fp@1744: // if sync manager index is positive, the PDO is mapped by default fp@1744: if (pdo->sync_index >= 0) { fp@1749: ec_sync_t *sync; fp@1744: ec_pdo_t *mapped_pdo; fp@1744: fp@1744: if (pdo->sync_index >= slave->sii_sync_count) { fp@1744: EC_ERR("Invalid SM index %i for PDO 0x%04X in slave %u.", fp@1744: pdo->sync_index, pdo->index, slave->ring_position); fp@1744: return -1; fp@1744: } fp@1749: sync = &slave->sii_syncs[pdo->sync_index]; fp@1744: fp@1744: if (!(mapped_pdo = kmalloc(sizeof(ec_pdo_t), GFP_KERNEL))) { fp@1744: EC_ERR("Failed to allocate PDO memory.\n"); fp@1744: return -1; fp@1744: } fp@1744: fp@1744: if (ec_pdo_copy(mapped_pdo, pdo)) { fp@1744: EC_ERR("Failed to copy PDO.\n"); fp@1744: kfree(mapped_pdo); fp@1744: return -1; fp@1744: } fp@1744: fp@1749: list_add_tail(&mapped_pdo->list, &sync->pdos); fp@1749: sync->mapping_source = EC_SYNC_MAPPING_SII; fp@1744: } fp@126: } fp@126: fp@126: return 0; fp@114: } fp@114: fp@114: /*****************************************************************************/ fp@114: fp@114: /** fp@195: Searches the string list for an index and allocates a new string. fp@195: \return 0 in case of success, else < 0 fp@197: \todo documentation fp@197: */ fp@197: fp@1744: char *ec_slave_sii_string( fp@1744: ec_slave_t *slave, /**< EtherCAT slave */ fp@1744: unsigned int index /**< string index */ fp@1744: ) fp@1744: { fp@1744: if (!index--) fp@1744: return NULL; fp@1744: fp@1744: if (index >= slave->sii_string_count) { fp@1744: if (slave->master->debug_level) fp@1744: EC_WARN("String %i not found in slave %i.\n", fp@1744: index, slave->ring_position); fp@1744: return NULL; fp@1744: } fp@1744: fp@1744: return slave->sii_strings[index]; fp@1744: } fp@1744: fp@1744: /*****************************************************************************/ fp@1744: fp@1744: /** fp@1744: * Prepares an FMMU configuration. fp@1744: * Configuration data for the FMMU is saved in the slave structure and is fp@1744: * written to the slave in ecrt_master_activate(). fp@1744: * The FMMU configuration is done in a way, that the complete data range fp@1744: * of the corresponding sync manager is covered. Seperate FMMUs are configured fp@1744: * for each domain. fp@1744: * If the FMMU configuration is already prepared, the function returns with fp@1744: * success. fp@1744: * \return 0 in case of success, else < 0 fp@1744: */ fp@1744: fp@1744: int ec_slave_prepare_fmmu( fp@1744: ec_slave_t *slave, /**< EtherCAT slave */ fp@1744: const ec_domain_t *domain, /**< domain */ fp@1744: const ec_sync_t *sync /**< sync manager */ fp@1744: ) fp@73: { fp@73: unsigned int i; fp@1732: ec_fmmu_t *fmmu; fp@73: fp@195: // FMMU configuration already prepared? fp@1732: for (i = 0; i < slave->fmmu_count; i++) { fp@1732: fmmu = &slave->fmmus[i]; fp@1732: if (fmmu->domain == domain && fmmu->sync == sync) fp@73: return 0; fp@1732: } fp@73: fp@195: // reserve new FMMU... fp@91: fp@73: if (slave->fmmu_count >= slave->base_fmmu_count) { fp@84: EC_ERR("Slave %i FMMU limit reached!\n", slave->ring_position); fp@73: return -1; fp@73: } fp@73: fp@1732: fmmu = &slave->fmmus[slave->fmmu_count]; fp@1732: fp@1744: ec_fmmu_init(fmmu, slave, slave->fmmu_count++); fp@1732: fmmu->domain = domain; fp@1732: fmmu->sync = sync; fp@1732: fmmu->logical_start_address = 0; fp@1732: fp@1744: slave->pdos_registered = 1; fp@1744: fp@1744: ec_slave_request_state(slave, EC_SLAVE_STATE_OP); fp@73: fp@73: return 0; fp@73: } fp@73: fp@73: /*****************************************************************************/ fp@73: fp@73: /** fp@195: Outputs all information about a certain slave. fp@1715: */ fp@1715: fp@1715: size_t ec_slave_info(const ec_slave_t *slave, /**< EtherCAT slave */ fp@1715: char *buffer /**< Output buffer */ fp@1715: ) fp@1715: { fp@1715: off_t off = 0; fp@1744: ec_sync_t *sync; fp@1744: ec_pdo_t *pdo; fp@1744: ec_pdo_entry_t *pdo_entry; fp@142: int first, i; fp@1732: ec_sdo_data_t *sdodata; fp@1732: char str[20]; fp@126: fp@1749: off += sprintf(buffer + off, "Ring position: %i\n", fp@1749: slave->ring_position); fp@1716: off += sprintf(buffer + off, "State: "); fp@1716: off += ec_state_string(slave->current_state, buffer + off); fp@1739: off += sprintf(buffer + off, " ("); fp@1739: off += ec_state_string(slave->requested_state, buffer + off); fp@1749: off += sprintf(buffer + off, ")\n"); fp@1749: off += sprintf(buffer + off, "Flags: %s, %s\n\n", fp@1744: slave->online_state == EC_SLAVE_ONLINE ? "online" : "OFFLINE", fp@1744: slave->error_flag ? "ERROR" : "ok"); fp@1715: fp@1715: off += sprintf(buffer + off, "Data link status:\n"); fp@183: for (i = 0; i < 4; i++) { fp@1751: off += sprintf(buffer + off, " Port %u: Phy %u (", fp@1751: i, slave->sii_physical_layer[i]); fp@190: switch (slave->sii_physical_layer[i]) { fp@190: case 0x00: fp@1715: off += sprintf(buffer + off, "EBUS"); fp@190: break; fp@190: case 0x01: fp@1715: off += sprintf(buffer + off, "100BASE-TX"); fp@190: break; fp@190: case 0x02: fp@1715: off += sprintf(buffer + off, "100BASE-FX"); fp@190: break; fp@190: default: fp@1751: off += sprintf(buffer + off, "unknown"); fp@1751: } fp@1751: off += sprintf(buffer + off, "), Link %s, Loop %s, %s\n", fp@1715: slave->dl_link[i] ? "up" : "down", fp@1715: slave->dl_loop[i] ? "closed" : "open", fp@1715: slave->dl_signal[i] ? "Signal detected" : "No signal"); fp@1715: } fp@1749: off += sprintf(buffer + off, "\n"); fp@1749: fp@1749: if (slave->sii_alias) fp@1749: off += sprintf(buffer + off, "Configured station alias:" fp@1749: " 0x%04X (%i)\n\n", slave->sii_alias, slave->sii_alias); fp@1749: fp@1751: off += sprintf(buffer + off, "Identity:\n"); fp@1751: off += sprintf(buffer + off, " Vendor ID: 0x%08X (%u)\n", fp@1749: slave->sii_vendor_id, slave->sii_vendor_id); fp@1751: off += sprintf(buffer + off, " Product code: 0x%08X (%u)\n", fp@1749: slave->sii_product_code, slave->sii_product_code); fp@1751: off += sprintf(buffer + off, " Revision number: 0x%08X (%u)\n", fp@1749: slave->sii_revision_number, slave->sii_revision_number); fp@1751: off += sprintf(buffer + off, " Serial number: 0x%08X (%u)\n\n", fp@1749: slave->sii_serial_number, slave->sii_serial_number); fp@147: fp@147: if (slave->sii_mailbox_protocols) { fp@1749: off += sprintf(buffer + off, "Mailboxes:\n"); fp@1749: off += sprintf(buffer + off, " RX: 0x%04X/%u, TX: 0x%04X/%u\n", fp@1749: slave->sii_rx_mailbox_offset, slave->sii_rx_mailbox_size, fp@1749: slave->sii_tx_mailbox_offset, slave->sii_tx_mailbox_size); fp@1715: off += sprintf(buffer + off, " Supported protocols: "); fp@147: fp@147: first = 1; fp@147: if (slave->sii_mailbox_protocols & EC_MBOX_AOE) { fp@1715: off += sprintf(buffer + off, "AoE"); fp@147: first = 0; fp@147: } fp@147: if (slave->sii_mailbox_protocols & EC_MBOX_EOE) { fp@1715: if (!first) off += sprintf(buffer + off, ", "); fp@1715: off += sprintf(buffer + off, "EoE"); fp@147: first = 0; fp@147: } fp@147: if (slave->sii_mailbox_protocols & EC_MBOX_COE) { fp@1715: if (!first) off += sprintf(buffer + off, ", "); fp@1715: off += sprintf(buffer + off, "CoE"); fp@147: first = 0; fp@147: } fp@147: if (slave->sii_mailbox_protocols & EC_MBOX_FOE) { fp@1715: if (!first) off += sprintf(buffer + off, ", "); fp@1715: off += sprintf(buffer + off, "FoE"); fp@147: first = 0; fp@147: } fp@147: if (slave->sii_mailbox_protocols & EC_MBOX_SOE) { fp@1715: if (!first) off += sprintf(buffer + off, ", "); fp@1715: off += sprintf(buffer + off, "SoE"); fp@147: first = 0; fp@147: } fp@147: if (slave->sii_mailbox_protocols & EC_MBOX_VOE) { fp@1715: if (!first) off += sprintf(buffer + off, ", "); fp@1715: off += sprintf(buffer + off, "VoE"); fp@1715: } fp@1749: off += sprintf(buffer + off, "\n\n"); fp@1749: } fp@1749: fp@1749: off += sprintf(buffer + off, "Current consumption: %i mA\n\n", fp@1749: slave->sii_current_on_ebus); fp@1749: fp@1749: if (slave->sii_group || slave->sii_image || slave->sii_order fp@1749: || slave->sii_name) { fp@1749: off += sprintf(buffer + off, "General:\n"); fp@1749: fp@1749: if (slave->sii_group) fp@1749: off += sprintf(buffer + off, " Group: %s\n", slave->sii_group); fp@1749: if (slave->sii_image) fp@1749: off += sprintf(buffer + off, " Image: %s\n", slave->sii_image); fp@1749: if (slave->sii_order) fp@1749: off += sprintf(buffer + off, " Order number: %s\n", fp@1749: slave->sii_order); fp@1749: if (slave->sii_name) fp@1749: off += sprintf(buffer + off, " Name: %s\n", slave->sii_name); fp@1715: off += sprintf(buffer + off, "\n"); fp@1715: } fp@1715: fp@1749: if (slave->sii_sync_count) { fp@1749: off += sprintf(buffer + off, "Sync managers / PDO mapping:\n"); fp@1749: fp@1749: for (i = 0; i < slave->sii_sync_count; i++) { fp@1749: sync = &slave->sii_syncs[i]; fp@1749: off += sprintf(buffer + off, fp@1749: " SM%u: addr 0x%04X, size %i, control 0x%02X, %s\n", fp@1749: sync->index, sync->physical_start_address, fp@1749: ec_sync_size(sync), sync->control_register, fp@1749: sync->enable ? "enable" : "disable"); fp@1749: fp@1749: if (list_empty(&sync->pdos)) { fp@1749: off += sprintf(buffer + off, " No PDOs mapped.\n"); fp@1749: } else if (sync->mapping_source != EC_SYNC_MAPPING_NONE) { fp@1749: off += sprintf(buffer + off, fp@1749: " PDO mapping information from %s.\n", fp@1749: sync->mapping_source == EC_SYNC_MAPPING_SII fp@1749: ? "SII" : "CoE"); fp@1749: } fp@1749: fp@1749: list_for_each_entry(pdo, &sync->pdos, list) { fp@1749: off += sprintf(buffer + off, " %s 0x%04X \"%s\"\n", fp@1749: pdo->type == EC_RX_PDO ? "RxPdo" : "TxPdo", fp@1749: pdo->index, pdo->name ? pdo->name : "???"); fp@1749: fp@1749: list_for_each_entry(pdo_entry, &pdo->entries, list) { fp@1749: off += sprintf(buffer + off, fp@1749: " 0x%04X:%X \"%s\", %i bit\n", fp@1749: pdo_entry->index, pdo_entry->subindex, fp@1749: pdo_entry->name ? pdo_entry->name : "???", fp@1749: pdo_entry->bit_length); fp@1749: } fp@1749: } fp@1749: } fp@1749: off += sprintf(buffer + off, "\n"); fp@1749: } fp@1749: fp@1749: // type-cast to avoid warnings on some compilers fp@1749: if (!list_empty((struct list_head *) &slave->sii_pdos)) { fp@1749: off += sprintf(buffer + off, "Available PDOs from SII:\n"); fp@1749: fp@1749: list_for_each_entry(pdo, &slave->sii_pdos, list) { fp@1749: off += sprintf(buffer + off, " %s 0x%04X \"%s\"", fp@1746: pdo->type == EC_RX_PDO ? "RxPdo" : "TxPdo", fp@1744: pdo->index, pdo->name ? pdo->name : "???"); fp@1749: if (pdo->sync_index >= 0) fp@1749: off += sprintf(buffer + off, ", default mapping: SM%u.\n", fp@1749: pdo->sync_index); fp@1749: else fp@1749: off += sprintf(buffer + off, ", no default mapping.\n"); fp@1744: fp@1744: list_for_each_entry(pdo_entry, &pdo->entries, list) { fp@1749: off += sprintf(buffer + off, " 0x%04X:%X \"%s\", %i bit\n", fp@1744: pdo_entry->index, pdo_entry->subindex, fp@1744: pdo_entry->name ? pdo_entry->name : "???", fp@1744: pdo_entry->bit_length); fp@1744: } fp@1744: } fp@1749: off += sprintf(buffer + off, "\n"); fp@1715: } fp@1715: fp@1746: // type-cast to avoid warnings on some compilers fp@1749: if (!list_empty((struct list_head *) &slave->sdo_confs)) { fp@1749: off += sprintf(buffer + off, "SDO configurations:\n"); fp@1749: fp@1749: list_for_each_entry(sdodata, &slave->sdo_confs, list) { fp@1749: switch (sdodata->size) { fp@1749: case 1: sprintf(str, "%i", EC_READ_U8(sdodata->data)); break; fp@1749: case 2: sprintf(str, "%i", EC_READ_U16(sdodata->data)); break; fp@1749: case 4: sprintf(str, "%i", EC_READ_U32(sdodata->data)); break; fp@1749: default: sprintf(str, "(invalid size)"); break; fp@1749: } fp@1749: off += sprintf(buffer + off, " 0x%04X:%-3i -> %s\n", fp@1749: sdodata->index, sdodata->subindex, str); fp@1749: } fp@1749: off += sprintf(buffer + off, "\n"); fp@1749: } fp@1715: fp@1715: return off; fp@1715: } fp@1715: fp@1715: /*****************************************************************************/ fp@1715: fp@133: /** fp@1744: * Schedules an EEPROM write request. fp@1744: * \return 0 case of success, otherwise error code. fp@1744: */ fp@1744: fp@1746: int ec_slave_schedule_eeprom_writing( fp@1746: ec_eeprom_write_request_t *request /**< EEPROM write request */ fp@1746: ) fp@1744: { fp@1744: ec_master_t *master = request->slave->master; fp@1744: fp@1744: request->state = EC_REQUEST_QUEUED; fp@1744: fp@1744: // schedule EEPROM write request. fp@1744: down(&master->eeprom_sem); fp@1744: list_add_tail(&request->list, &master->eeprom_requests); fp@1744: up(&master->eeprom_sem); fp@1744: fp@1744: // wait for processing through FSM fp@1744: if (wait_event_interruptible(master->eeprom_queue, fp@1744: request->state != EC_REQUEST_QUEUED)) { fp@1744: // interrupted by signal fp@1744: down(&master->eeprom_sem); fp@1744: if (request->state == EC_REQUEST_QUEUED) { fp@1744: list_del(&request->list); fp@1744: up(&master->eeprom_sem); fp@1744: return -EINTR; fp@1744: } fp@1744: // request already processing: interrupt not possible. fp@1744: up(&master->eeprom_sem); fp@1744: } fp@1744: fp@1744: // wait until master FSM has finished processing fp@1744: wait_event(master->eeprom_queue, fp@1744: request->state != EC_REQUEST_IN_PROGRESS); fp@1744: fp@1744: return request->state == EC_REQUEST_COMPLETE ? 0 : -EIO; fp@1744: } fp@1744: fp@1744: /*****************************************************************************/ fp@1744: fp@1744: /** fp@1746: * Calculates the EEPROM checksum field. fp@1746: * fp@1746: * The checksum is generated with the polynom x^8+x^2+x+1 (0x07) and an fp@1746: * initial value of 0xff (see IEC 61158-6-12 ch. 5.4). fp@1746: * fp@1746: * The below code was originally generated with PYCRC fp@1746: * http://www.tty1.net/pycrc fp@1746: * fp@1746: * ./pycrc.py --width=8 --poly=0x07 --reflect-in=0 --xor-in=0xff fp@1746: * --reflect-out=0 --xor-out=0 --generate c --algorithm=bit-by-bit fp@1746: * fp@1746: * \return CRC8 fp@1746: */ fp@1746: fp@1746: uint8_t ec_slave_eeprom_crc( fp@1746: const uint8_t *data, /**< pointer to data */ fp@1746: size_t length /**< number of bytes in \a data */ fp@1746: ) fp@1746: { fp@1746: unsigned int i; fp@1746: uint8_t bit, byte, crc = 0x48; fp@1746: fp@1746: while (length--) { fp@1746: byte = *data++; fp@1746: for (i = 0; i < 8; i++) { fp@1746: bit = crc & 0x80; fp@1746: crc = (crc << 1) | ((byte >> (7 - i)) & 0x01); fp@1746: if (bit) crc ^= 0x07; fp@1746: } fp@1746: } fp@1746: fp@1746: for (i = 0; i < 8; i++) { fp@1746: bit = crc & 0x80; fp@1746: crc <<= 1; fp@1746: if (bit) crc ^= 0x07; fp@1746: } fp@1746: fp@1746: return crc; fp@1746: } fp@1746: fp@1746: /*****************************************************************************/ fp@1746: fp@1746: /** fp@1744: * Writes complete EEPROM contents to a slave. fp@1744: * \return data size written in case of success, otherwise error code. fp@1744: */ fp@269: fp@269: ssize_t ec_slave_write_eeprom(ec_slave_t *slave, /**< EtherCAT slave */ fp@1744: const uint8_t *data, /**< new EEPROM data */ fp@1744: size_t size /**< size of data in bytes */ fp@1744: ) fp@1744: { fp@1744: ec_eeprom_write_request_t request; fp@1744: const uint16_t *cat_header; fp@1744: uint16_t cat_type, cat_size; fp@1744: int ret; fp@1746: uint8_t crc; fp@1744: fp@1744: if (slave->master->mode != EC_MASTER_MODE_IDLE) { // FIXME fp@1715: EC_ERR("Writing EEPROMs only allowed in idle mode!\n"); fp@1744: return -EBUSY; fp@1744: } fp@269: fp@269: if (size % 2) { fp@1744: EC_ERR("EEPROM data size is odd! Dropping.\n"); fp@1744: return -EINVAL; fp@1744: } fp@1744: fp@1744: // init EEPROM write request fp@1744: INIT_LIST_HEAD(&request.list); fp@1744: request.slave = slave; fp@1746: request.data = data; fp@1746: request.word_offset = 0; fp@1746: request.word_size = size / 2; fp@1746: fp@1746: if (request.word_size < 0x0041) { fp@269: EC_ERR("EEPROM data too short! Dropping.\n"); fp@1744: return -EINVAL; fp@1744: } fp@1744: fp@1746: // calculate checksum fp@1746: crc = ec_slave_eeprom_crc(data, 14); // CRC over words 0 to 6 fp@1746: if (crc != data[14]) { fp@1746: EC_WARN("EEPROM CRC incorrect. Must be 0x%02x.\n", crc); fp@1746: } fp@1746: fp@1746: cat_header = (const uint16_t *) request.data fp@1746: + EC_FIRST_EEPROM_CATEGORY_OFFSET; fp@1744: cat_type = EC_READ_U16(cat_header); fp@1744: while (cat_type != 0xFFFF) { // cycle through categories fp@1746: if (cat_header + 1 > fp@1746: (const uint16_t *) request.data + request.word_size) { fp@1744: EC_ERR("EEPROM data corrupted! Dropping.\n"); fp@1744: return -EINVAL; fp@1744: } fp@1744: cat_size = EC_READ_U16(cat_header + 1); fp@1746: if (cat_header + cat_size + 2 > fp@1746: (const uint16_t *) request.data + request.word_size) { fp@1744: EC_ERR("EEPROM data corrupted! Dropping.\n"); fp@1744: return -EINVAL; fp@1744: } fp@1744: cat_header += cat_size + 2; fp@1744: cat_type = EC_READ_U16(cat_header); fp@1744: } fp@1744: fp@1744: // EEPROM data ok. schedule writing. fp@1744: if ((ret = ec_slave_schedule_eeprom_writing(&request))) fp@1744: return ret; // error code fp@1744: fp@1744: return size; // success fp@1744: } fp@1744: fp@1744: /*****************************************************************************/ fp@1744: fp@1744: /** fp@1744: * Writes the Secondary slave address (alias) to the slave's EEPROM. fp@1744: * \return data size written in case of success, otherwise error code. fp@1744: */ fp@1744: fp@1744: ssize_t ec_slave_write_alias(ec_slave_t *slave, /**< EtherCAT slave */ fp@1744: const uint8_t *data, /**< alias string */ fp@1744: size_t size /**< size of data in bytes */ fp@1744: ) fp@1744: { fp@1744: ec_eeprom_write_request_t request; fp@1744: char *remainder; fp@1746: uint16_t alias; fp@1744: int ret; fp@1746: uint8_t eeprom_data[16], crc; fp@1744: fp@1744: if (slave->master->mode != EC_MASTER_MODE_IDLE) { // FIXME fp@1746: EC_ERR("Writing to EEPROM is only allowed in idle mode!\n"); fp@1744: return -EBUSY; fp@1744: } fp@1744: fp@1744: alias = simple_strtoul(data, &remainder, 0); fp@1744: if (remainder == (char *) data || (*remainder && *remainder != '\n')) { fp@1744: EC_ERR("Invalid alias value! Dropping.\n"); fp@1744: return -EINVAL; fp@1744: } fp@1746: fp@1746: if (!slave->eeprom_data || slave->eeprom_size < 16) { fp@1746: EC_ERR("Failed to read EEPROM contents from slave %u.\n", fp@1746: slave->ring_position); fp@1746: return -EINVAL; fp@1746: } fp@1746: fp@1746: // copy first 7 words of recent EEPROM contents fp@1746: memcpy(eeprom_data, slave->eeprom_data, 14); fp@1744: fp@1746: // write new alias address in word 4 fp@1746: EC_WRITE_U16(eeprom_data + 8, alias); fp@1746: fp@1746: // calculate new checksum over words 0 to 6 fp@1746: crc = ec_slave_eeprom_crc(eeprom_data, 14); fp@1746: EC_WRITE_U16(eeprom_data + 14, crc); fp@1744: fp@1744: // init EEPROM write request fp@1744: INIT_LIST_HEAD(&request.list); fp@1744: request.slave = slave; fp@1746: request.data = eeprom_data; fp@1746: request.word_offset = 0x0000; fp@1746: request.word_size = 8; fp@1744: fp@1744: if ((ret = ec_slave_schedule_eeprom_writing(&request))) fp@1744: return ret; // error code fp@1744: fp@1744: slave->sii_alias = alias; // FIXME: do this in state machine fp@1744: fp@1744: return size; // success fp@1744: } fp@1744: fp@269: fp@269: /*****************************************************************************/ fp@269: fp@269: /** fp@195: Formats attribute data for SysFS read access. fp@195: \return number of bytes to read fp@195: */ fp@195: fp@195: ssize_t ec_show_slave_attribute(struct kobject *kobj, /**< slave's kobject */ fp@195: struct attribute *attr, /**< attribute */ fp@195: char *buffer /**< memory to store data */ fp@182: ) fp@182: { fp@182: ec_slave_t *slave = container_of(kobj, ec_slave_t, kobj); fp@182: fp@1715: if (attr == &attr_info) { fp@1715: return ec_slave_info(slave, buffer); fp@185: } fp@256: else if (attr == &attr_state) { fp@256: switch (slave->current_state) { fp@256: case EC_SLAVE_STATE_INIT: fp@256: return sprintf(buffer, "INIT\n"); fp@256: case EC_SLAVE_STATE_PREOP: fp@256: return sprintf(buffer, "PREOP\n"); fp@256: case EC_SLAVE_STATE_SAVEOP: fp@256: return sprintf(buffer, "SAVEOP\n"); fp@256: case EC_SLAVE_STATE_OP: fp@256: return sprintf(buffer, "OP\n"); fp@256: default: fp@256: return sprintf(buffer, "UNKNOWN\n"); fp@256: } fp@256: } fp@266: else if (attr == &attr_eeprom) { fp@266: if (slave->eeprom_data) { fp@266: if (slave->eeprom_size > PAGE_SIZE) { fp@266: EC_ERR("EEPROM contents of slave %i exceed 1 page (%i/%i).\n", fp@266: slave->ring_position, slave->eeprom_size, fp@266: (int) PAGE_SIZE); fp@266: } fp@266: else { fp@266: memcpy(buffer, slave->eeprom_data, slave->eeprom_size); fp@266: return slave->eeprom_size; fp@266: } fp@266: } fp@266: } fp@1744: else if (attr == &attr_alias) { fp@1744: return sprintf(buffer, "%u\n", slave->sii_alias); fp@1744: } fp@182: fp@182: return 0; fp@182: } fp@182: fp@256: /*****************************************************************************/ fp@256: fp@256: /** fp@256: Formats attribute data for SysFS write access. fp@256: \return number of bytes processed, or negative error code fp@256: */ fp@256: fp@256: ssize_t ec_store_slave_attribute(struct kobject *kobj, /**< slave's kobject */ fp@256: struct attribute *attr, /**< attribute */ fp@256: const char *buffer, /**< memory with data */ fp@256: size_t size /**< size of data to store */ fp@256: ) fp@256: { fp@256: ec_slave_t *slave = container_of(kobj, ec_slave_t, kobj); fp@256: fp@256: if (attr == &attr_state) { fp@1732: char state[EC_STATE_STRING_SIZE]; fp@292: if (!strcmp(buffer, "INIT\n")) fp@1732: ec_slave_request_state(slave, EC_SLAVE_STATE_INIT); fp@292: else if (!strcmp(buffer, "PREOP\n")) fp@1732: ec_slave_request_state(slave, EC_SLAVE_STATE_PREOP); fp@292: else if (!strcmp(buffer, "SAVEOP\n")) fp@1732: ec_slave_request_state(slave, EC_SLAVE_STATE_SAVEOP); fp@292: else if (!strcmp(buffer, "OP\n")) fp@1732: ec_slave_request_state(slave, EC_SLAVE_STATE_OP); fp@292: else { fp@292: EC_ERR("Invalid slave state \"%s\"!\n", buffer); fp@292: return -EINVAL; fp@292: } fp@292: fp@1716: ec_state_string(slave->requested_state, state); fp@292: EC_INFO("Accepted new state %s for slave %i.\n", fp@1716: state, slave->ring_position); fp@292: return size; fp@256: } fp@269: else if (attr == &attr_eeprom) { fp@1744: return ec_slave_write_eeprom(slave, buffer, size); fp@1744: } fp@1744: else if (attr == &attr_alias) { fp@1744: return ec_slave_write_alias(slave, buffer, size); fp@1744: } fp@1744: fp@1744: return -EIO; fp@1744: } fp@1744: fp@1744: /*****************************************************************************/ fp@1744: fp@1744: /** fp@1746: * Get the sync manager for either Rx- or Tx-PDOs. fp@1746: * \return pointer to sync manager, or NULL. fp@1744: */ fp@1744: fp@1744: ec_sync_t *ec_slave_get_pdo_sync( fp@1744: ec_slave_t *slave, /**< EtherCAT slave */ fp@1744: ec_direction_t dir /**< input or output */ fp@1739: ) fp@1739: { fp@1744: unsigned int sync_index; fp@1744: fp@1744: if (dir != EC_DIR_INPUT && dir != EC_DIR_OUTPUT) { fp@1744: EC_ERR("Invalid direction!\n"); fp@1744: return NULL; fp@1744: } fp@1744: fp@1744: sync_index = (unsigned int) dir; fp@1744: if (slave->sii_mailbox_protocols) sync_index += 2; fp@1744: fp@1744: if (sync_index >= slave->sii_sync_count) fp@1744: return NULL; fp@1744: fp@1744: return &slave->sii_syncs[sync_index]; fp@1731: } fp@1731: fp@1731: /*****************************************************************************/ fp@1731: fp@1731: /** fp@1716: \return 0 in case of success, else < 0 fp@1716: */ fp@1716: fp@1716: int ec_slave_conf_sdo(ec_slave_t *slave, /**< EtherCAT slave */ fp@1716: uint16_t sdo_index, /**< SDO index */ fp@1716: uint8_t sdo_subindex, /**< SDO subindex */ fp@1716: const uint8_t *data, /**< SDO data */ fp@1716: size_t size /**< SDO size in bytes */ fp@1716: ) fp@1716: { fp@1716: ec_sdo_data_t *sdodata; fp@1716: fp@1716: if (!(slave->sii_mailbox_protocols & EC_MBOX_COE)) { fp@1716: EC_ERR("Slave %i does not support CoE!\n", slave->ring_position); fp@1716: return -1; fp@1716: } fp@1716: fp@1716: if (!(sdodata = (ec_sdo_data_t *) fp@1716: kmalloc(sizeof(ec_sdo_data_t), GFP_KERNEL))) { fp@1716: EC_ERR("Failed to allocate memory for SDO configuration object!\n"); fp@1716: return -1; fp@1716: } fp@1716: fp@1716: if (!(sdodata->data = (uint8_t *) kmalloc(size, GFP_KERNEL))) { fp@1716: EC_ERR("Failed to allocate memory for SDO configuration data!\n"); fp@1716: kfree(sdodata); fp@1716: return -1; fp@1716: } fp@1716: fp@1716: sdodata->index = sdo_index; fp@1716: sdodata->subindex = sdo_subindex; fp@1716: memcpy(sdodata->data, data, size); fp@1716: sdodata->size = size; fp@1716: fp@1716: list_add_tail(&sdodata->list, &slave->sdo_confs); fp@1716: return 0; fp@1716: } fp@1716: fp@1732: /*****************************************************************************/ fp@1732: fp@1732: /** fp@1732: \return 0 in case of success, else < 0 fp@1732: */ fp@1732: fp@1732: int ec_slave_validate(const ec_slave_t *slave, /**< EtherCAT slave */ fp@1732: uint32_t vendor_id, /**< vendor ID */ fp@1732: uint32_t product_code /**< product code */ fp@1732: ) fp@1732: { fp@1732: if (vendor_id != slave->sii_vendor_id || fp@1732: product_code != slave->sii_product_code) { fp@1744: EC_ERR("Invalid slave type at position %i:\n", slave->ring_position); fp@1744: EC_ERR(" Requested: 0x%08X 0x%08X\n", vendor_id, product_code); fp@1744: EC_ERR(" Found: 0x%08X 0x%08X\n", fp@1744: slave->sii_vendor_id, slave->sii_product_code); fp@1732: return -1; fp@1732: } fp@1732: return 0; fp@1732: } fp@1732: fp@1732: /*****************************************************************************/ fp@1732: fp@1732: /** fp@1732: Counts the total number of SDOs and entries in the dictionary. fp@1732: */ fp@1732: fp@1732: void ec_slave_sdo_dict_info(const ec_slave_t *slave, /**< EtherCAT slave */ fp@1732: unsigned int *sdo_count, /**< number of SDOs */ fp@1732: unsigned int *entry_count /**< total number of fp@1732: entries */ fp@1732: ) fp@1732: { fp@1732: unsigned int sdos = 0, entries = 0; fp@1732: ec_sdo_t *sdo; fp@1732: ec_sdo_entry_t *entry; fp@1732: fp@1732: list_for_each_entry(sdo, &slave->sdo_dictionary, list) { fp@1732: sdos++; fp@1732: list_for_each_entry(entry, &sdo->entries, list) { fp@1732: entries++; fp@1732: } fp@1732: } fp@1732: fp@1732: *sdo_count = sdos; fp@1732: *entry_count = entries; fp@1732: } fp@1732: fp@1746: /*****************************************************************************/ fp@1746: fp@1746: /** fp@1746: * Get an SDO from the dictionary. fp@1746: * \returns The desired SDO, of NULL. fp@1746: */ fp@1746: fp@1746: ec_sdo_t *ec_slave_get_sdo( fp@1746: ec_slave_t *slave /**< EtherCAT slave */, fp@1746: uint16_t index /**< SDO index */ fp@1746: ) fp@1746: { fp@1746: ec_sdo_t *sdo; fp@1746: fp@1746: list_for_each_entry(sdo, &slave->sdo_dictionary, list) { fp@1746: if (sdo->index != index) continue; fp@1746: return sdo; fp@1746: } fp@1746: fp@1746: return NULL; fp@1746: } fp@1746: fp@199: /****************************************************************************** fp@199: * Realtime interface fp@199: *****************************************************************************/ fp@199: fp@199: /** fp@199: \return 0 in case of success, else < 0 fp@199: \ingroup RealtimeInterface fp@199: */ fp@199: fp@1716: int ecrt_slave_conf_sdo8(ec_slave_t *slave, /**< EtherCAT slave */ fp@1716: uint16_t sdo_index, /**< SDO index */ fp@1716: uint8_t sdo_subindex, /**< SDO subindex */ fp@1716: uint8_t value /**< new SDO value */ fp@1716: ) fp@1716: { fp@1716: uint8_t data[1]; fp@1716: EC_WRITE_U8(data, value); fp@1716: return ec_slave_conf_sdo(slave, sdo_index, sdo_subindex, data, 1); fp@1716: } fp@1716: fp@1716: /*****************************************************************************/ fp@1716: fp@1716: /** fp@1716: \return 0 in case of success, else < 0 fp@1716: \ingroup RealtimeInterface fp@1716: */ fp@1716: fp@1716: int ecrt_slave_conf_sdo16(ec_slave_t *slave, /**< EtherCAT slave */ fp@1716: uint16_t sdo_index, /**< SDO index */ fp@1716: uint8_t sdo_subindex, /**< SDO subindex */ fp@1716: uint16_t value /**< new SDO value */ fp@1716: ) fp@1716: { fp@1716: uint8_t data[2]; fp@1716: EC_WRITE_U16(data, value); fp@1716: return ec_slave_conf_sdo(slave, sdo_index, sdo_subindex, data, 2); fp@1716: } fp@1716: fp@1716: /*****************************************************************************/ fp@1716: fp@1716: /** fp@1716: \return 0 in case of success, else < 0 fp@1716: \ingroup RealtimeInterface fp@1716: */ fp@1716: fp@1716: int ecrt_slave_conf_sdo32(ec_slave_t *slave, /**< EtherCAT slave */ fp@1716: uint16_t sdo_index, /**< SDO index */ fp@1716: uint8_t sdo_subindex, /**< SDO subindex */ fp@1716: uint32_t value /**< new SDO value */ fp@1716: ) fp@1716: { fp@1716: uint8_t data[4]; fp@1716: EC_WRITE_U32(data, value); fp@1716: return ec_slave_conf_sdo(slave, sdo_index, sdo_subindex, data, 4); fp@1716: } fp@1716: fp@1716: /*****************************************************************************/ fp@1716: fp@1746: /** fp@1746: * Clear slave's PDO mapping. fp@1746: */ fp@1746: fp@1744: void ecrt_slave_pdo_mapping_clear( fp@1744: ec_slave_t *slave, /**< EtherCAT slave */ fp@1744: ec_direction_t dir /**< output/input */ fp@1744: ) fp@1744: { fp@1744: ec_sync_t *sync; fp@1744: fp@1744: if (!(slave->sii_mailbox_protocols & EC_MBOX_COE)) { fp@1744: EC_ERR("Slave %i does not support CoE!\n", slave->ring_position); fp@1744: return; fp@1744: } fp@1744: fp@1744: if (!(sync = ec_slave_get_pdo_sync(slave, dir))) fp@1744: return; fp@1744: fp@1744: ec_sync_clear_pdos(sync); fp@1746: sync->alt_mapping = 1; fp@1746: } fp@1746: fp@1746: /*****************************************************************************/ fp@1746: fp@1746: /** fp@1746: * Add a PDO to the list of known mapped PDOs. fp@1746: */ fp@1744: fp@1744: int ecrt_slave_pdo_mapping_add( fp@1744: ec_slave_t *slave, /**< EtherCAT slave */ fp@1744: ec_direction_t dir, /**< input/output */ fp@1746: uint16_t pdo_index /**< Index of mapped PDO */) fp@1744: { fp@1744: ec_pdo_t *pdo; fp@1744: ec_sync_t *sync; fp@1744: unsigned int not_found = 1; fp@1744: fp@1744: if (!(slave->sii_mailbox_protocols & EC_MBOX_COE)) { fp@1744: EC_ERR("Slave %u does not support CoE!\n", slave->ring_position); fp@275: return -1; fp@275: } fp@275: fp@1746: // does the slave provide the PDO? fp@1744: list_for_each_entry(pdo, &slave->sii_pdos, list) { fp@1744: if (pdo->index == pdo_index) { fp@1744: not_found = 0; fp@1744: break; fp@1744: } fp@1744: } fp@1744: fp@1744: if (not_found) { fp@1744: EC_ERR("Slave %u does not provide PDO 0x%04X!\n", fp@1744: slave->ring_position, pdo_index); fp@1744: return -1; fp@1744: } fp@1744: fp@1744: // check direction fp@1744: if ((pdo->type == EC_TX_PDO && dir == EC_DIR_OUTPUT) || fp@1744: (pdo->type == EC_RX_PDO && dir == EC_DIR_INPUT)) { fp@1744: EC_ERR("Invalid direction for PDO 0x%04X.\n", pdo_index); fp@1744: return -1; fp@1744: } fp@1744: fp@1744: fp@1744: if (!(sync = ec_slave_get_pdo_sync(slave, dir))) { fp@1744: EC_ERR("Failed to obtain sync manager for PDO mapping of slave %u!\n", fp@1744: slave->ring_position); fp@1744: return -1; fp@1744: } fp@1744: fp@1746: if (ec_sync_add_pdo(sync, pdo)) fp@1746: return -1; fp@1746: fp@1746: sync->alt_mapping = 1; fp@1746: return 0; fp@1746: } fp@1746: fp@1746: /*****************************************************************************/ fp@1746: fp@1746: /** fp@1746: * Convenience function for ecrt_slave_pdo_mapping_clear() and fp@1746: * ecrt_slave_pdo_mapping_add(). fp@1746: */ fp@1744: fp@1744: int ecrt_slave_pdo_mapping(ec_slave_t *slave, /**< EtherCAT slave */ fp@1744: ec_direction_t dir, /**< input/output */ fp@1744: unsigned int num_args, /**< Number of following arguments */ fp@1744: ... /**< PDO indices to map */ fp@1744: ) fp@1744: { fp@1744: va_list ap; fp@1744: fp@1744: ecrt_slave_pdo_mapping_clear(slave, dir); fp@1744: fp@1744: va_start(ap, num_args); fp@1744: fp@1744: for (; num_args; num_args--) { fp@1744: if (ecrt_slave_pdo_mapping_add( fp@1744: slave, dir, (uint16_t) va_arg(ap, int))) { fp@1744: return -1; fp@1744: } fp@1744: } fp@1744: fp@1744: va_end(ap); fp@1744: return 0; fp@1744: } fp@1744: fp@275: fp@275: /*****************************************************************************/ fp@275: fp@1739: /** \cond */ fp@164: fp@1716: EXPORT_SYMBOL(ecrt_slave_conf_sdo8); fp@1716: EXPORT_SYMBOL(ecrt_slave_conf_sdo16); fp@1716: EXPORT_SYMBOL(ecrt_slave_conf_sdo32); fp@1744: EXPORT_SYMBOL(ecrt_slave_pdo_mapping_clear); fp@1744: EXPORT_SYMBOL(ecrt_slave_pdo_mapping_add); fp@1744: EXPORT_SYMBOL(ecrt_slave_pdo_mapping); fp@138: fp@1739: /** \endcond */ fp@1739: fp@1739: /*****************************************************************************/