Skip to content
Snippets Groups Projects
slave.c 39.5 KiB
Newer Older
        cat_size = EC_READ_U16(cat_header + 1);
        if (cat_header + cat_size + 2 >
				(const uint16_t *) request.data + request.word_size) {
Florian Pose's avatar
Florian Pose committed
            EC_ERR("SII data corrupted! Dropping.\n");
            return -EINVAL;
        }
        cat_header += cat_size + 2;
        cat_type = EC_READ_U16(cat_header);
    }

Florian Pose's avatar
Florian Pose committed
    // SII data ok. schedule writing.
    if ((ret = ec_slave_schedule_sii_writing(&request)))
        return ret; // error code
    return size; // success
}

/*****************************************************************************/

/**
Florian Pose's avatar
Florian Pose committed
 * Writes the Secondary slave address (alias) to the slave's SII.
 * \return data size written in case of success, otherwise error code.
 */

ssize_t ec_slave_write_alias(ec_slave_t *slave, /**< EtherCAT slave */
        const uint8_t *data, /**< alias string */
        size_t size /**< size of data in bytes */
        )
{
Florian Pose's avatar
Florian Pose committed
    ec_sii_write_request_t request;
    char *remainder;
Florian Pose's avatar
Florian Pose committed
    uint8_t sii_data[16], crc;

    if (slave->master->mode != EC_MASTER_MODE_IDLE) { // FIXME
Florian Pose's avatar
Florian Pose committed
        EC_ERR("Writing to SII is only allowed in idle mode!\n");
    alias = simple_strtoul(data, &remainder, 0);
    if (remainder == (char *) data || (*remainder && *remainder != '\n')) {
        EC_ERR("Invalid alias value! Dropping.\n");
        return -EINVAL;
    }
Florian Pose's avatar
Florian Pose committed
    if (!slave->sii_data || slave->sii_size < 16) {
        EC_ERR("Failed to read SII contents from slave %u.\n",
                slave->ring_position);
        return -EINVAL;
    }

Florian Pose's avatar
Florian Pose committed
    // copy first 7 words of recent SII contents
    memcpy(sii_data, slave->sii_data, 14);
    // write new alias address in word 4
Florian Pose's avatar
Florian Pose committed
    EC_WRITE_U16(sii_data + 8, alias);

    // calculate new checksum over words 0 to 6
Florian Pose's avatar
Florian Pose committed
    crc = ec_slave_sii_crc(sii_data, 14);
    EC_WRITE_U16(sii_data + 14, crc);
Florian Pose's avatar
Florian Pose committed
    // init SII write request
    INIT_LIST_HEAD(&request.list);
    request.slave = slave;
Florian Pose's avatar
Florian Pose committed
    request.data = sii_data;
    request.word_offset = 0x0000;
    request.word_size = 8;
Florian Pose's avatar
Florian Pose committed
    if ((ret = ec_slave_schedule_sii_writing(&request)))
        return ret; // error code
    slave->sii.alias = alias; // FIXME: do this in state machine

    return size; // success
Florian Pose's avatar
Florian Pose committed
}

/*****************************************************************************/

   Formats attribute data for SysFS read access.
   \return number of bytes to read
ssize_t ec_show_slave_attribute(struct kobject *kobj, /**< slave's kobject */
                                struct attribute *attr, /**< attribute */
                                char *buffer /**< memory to store data */
                                )
{
    ec_slave_t *slave = container_of(kobj, ec_slave_t, kobj);

    if (attr == &attr_info) {
        return ec_slave_info(slave, buffer);
    else if (attr == &attr_state) {
        switch (slave->current_state) {
            case EC_SLAVE_STATE_INIT:
                return sprintf(buffer, "INIT\n");
            case EC_SLAVE_STATE_PREOP:
                return sprintf(buffer, "PREOP\n");
            case EC_SLAVE_STATE_SAFEOP:
                return sprintf(buffer, "SAFEOP\n");
            case EC_SLAVE_STATE_OP:
                return sprintf(buffer, "OP\n");
            default:
                return sprintf(buffer, "UNKNOWN\n");
        }
    }
Florian Pose's avatar
Florian Pose committed
    else if (attr == &attr_sii) {
        if (slave->sii_data) {
            if (slave->sii_size > PAGE_SIZE) {
                EC_ERR("SII contents of slave %u exceed 1 page (%u/%u).\n",
                       slave->ring_position, slave->sii_size,
Florian Pose's avatar
Florian Pose committed
                memcpy(buffer, slave->sii_data, slave->sii_size);
                return slave->sii_size;
    else if (attr == &attr_alias) {
        return sprintf(buffer, "%u\n", slave->sii.alias);
/*****************************************************************************/

/**
   Formats attribute data for SysFS write access.
   \return number of bytes processed, or negative error code
*/

ssize_t ec_store_slave_attribute(struct kobject *kobj, /**< slave's kobject */
                                 struct attribute *attr, /**< attribute */
                                 const char *buffer, /**< memory with data */
                                 size_t size /**< size of data to store */
                                 )
{
    ec_slave_t *slave = container_of(kobj, ec_slave_t, kobj);

    if (attr == &attr_state) {
        if (!strcmp(buffer, "INIT\n"))
Florian Pose's avatar
Florian Pose committed
            ec_slave_request_state(slave, EC_SLAVE_STATE_INIT);
        else if (!strcmp(buffer, "PREOP\n"))
Florian Pose's avatar
Florian Pose committed
            ec_slave_request_state(slave, EC_SLAVE_STATE_PREOP);
        else if (!strcmp(buffer, "SAFEOP\n"))
            ec_slave_request_state(slave, EC_SLAVE_STATE_SAFEOP);
        else if (!strcmp(buffer, "OP\n"))
Florian Pose's avatar
Florian Pose committed
            ec_slave_request_state(slave, EC_SLAVE_STATE_OP);
        else {
            EC_ERR("Invalid slave state \"%s\"!\n", buffer);
            return -EINVAL;
        ec_state_string(slave->requested_state, state);
        EC_INFO("Accepted new state %s for slave %u.\n",
                state, slave->ring_position);
        return size;
Florian Pose's avatar
Florian Pose committed
    else if (attr == &attr_sii) {
        return ec_slave_write_sii(slave, buffer, size);
    else if (attr == &attr_alias) {
        return ec_slave_write_alias(slave, buffer, size);
    }
/*****************************************************************************/

Florian Pose's avatar
Florian Pose committed
 * Get the sync manager for either Rx- or Tx-Pdos.
 * \return pointer to sync manager, or NULL.
ec_sync_t *ec_slave_get_pdo_sync(
        ec_slave_t *slave, /**< EtherCAT slave */
        ec_direction_t dir /**< input or output */
        )
{
    unsigned int sync_index;

    if (dir != EC_DIR_INPUT && dir != EC_DIR_OUTPUT) {
        EC_ERR("Invalid direction!\n");
        return NULL;
    sync_index = (unsigned int) dir;
    if (slave->sii.mailbox_protocols) sync_index += 2;
    if (sync_index >= slave->sii.sync_count)
    return &slave->sii.syncs[sync_index];
}

/*****************************************************************************/

/**
   \return 0 in case of success, else < 0
*/

Florian Pose's avatar
Florian Pose committed
int ec_slave_validate(const ec_slave_t *slave, /**< EtherCAT slave */
                      uint32_t vendor_id, /**< vendor ID */
                      uint32_t product_code /**< product code */
                      )
{
    if (vendor_id != slave->sii.vendor_id ||
        product_code != slave->sii.product_code) {
        EC_ERR("Invalid slave type at position %u:\n", slave->ring_position);
        EC_ERR("  Requested: 0x%08X 0x%08X\n", vendor_id, product_code);
        EC_ERR("      Found: 0x%08X 0x%08X\n",
                slave->sii.vendor_id, slave->sii.product_code);
Florian Pose's avatar
Florian Pose committed
        return -1;
    }
    return 0;
}

/*****************************************************************************/

/**
Florian Pose's avatar
Florian Pose committed
   Counts the total number of Sdos and entries in the dictionary.
*/

void ec_slave_sdo_dict_info(const ec_slave_t *slave, /**< EtherCAT slave */
Florian Pose's avatar
Florian Pose committed
                            unsigned int *sdo_count, /**< number of Sdos */
                            unsigned int *entry_count /**< total number of
                                                         entries */
                            )
{
    unsigned int sdos = 0, entries = 0;
    ec_sdo_t *sdo;
    ec_sdo_entry_t *entry;

    list_for_each_entry(sdo, &slave->sdo_dictionary, list) {
        sdos++;
        list_for_each_entry(entry, &sdo->entries, list) {
            entries++;
        }
    }

    *sdo_count = sdos;
    *entry_count = entries;
}

/*****************************************************************************/

/**
Florian Pose's avatar
Florian Pose committed
 * Get an Sdo from the dictionary.
 * \returns The desired Sdo, or NULL.
 */

ec_sdo_t *ec_slave_get_sdo(
        ec_slave_t *slave /**< EtherCAT slave */,
Florian Pose's avatar
Florian Pose committed
        uint16_t index /**< Sdo index */
        )
{
    ec_sdo_t *sdo;

    list_for_each_entry(sdo, &slave->sdo_dictionary, list) {
        if (sdo->index != index) continue;
        return sdo;
    }

    return NULL;
}

/*****************************************************************************/
Florian Pose's avatar
Florian Pose committed

/** Finds a mapped Pdo.
 * \returns The desired Pdo object, or NULL.
 */
const ec_pdo_t *ec_slave_find_pdo(
        const ec_slave_t *slave, /**< Slave. */
        uint16_t index /**< Pdo index to find. */
        )
{
    unsigned int i;
    const ec_sync_t *sync;
    const ec_pdo_t *pdo;

    for (i = 0; i < slave->sii.sync_count; i++) {
        sync = &slave->sii.syncs[i];
Florian Pose's avatar
Florian Pose committed

        if (!(pdo = ec_pdo_mapping_find_pdo_const(&sync->mapping, index)))
Florian Pose's avatar
Florian Pose committed
            continue;

        return pdo;
    }

    return NULL;
}

/*****************************************************************************/