Skip to content
Snippets Groups Projects
slave.c 41.5 KiB
Newer Older
                       (int) PAGE_SIZE);
            }
            else {
                memcpy(buffer, slave->eeprom_data, slave->eeprom_size);
                return slave->eeprom_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, "SAVEOP\n"))
Florian Pose's avatar
Florian Pose committed
            ec_slave_request_state(slave, EC_SLAVE_STATE_SAVEOP);
        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 %i.\n",
                state, slave->ring_position);
        return size;
Florian Pose's avatar
Florian Pose committed
    else if (attr == &attr_eeprom) {
        return ec_slave_write_eeprom(slave, buffer, size);
    else if (attr == &attr_alias) {
        return ec_slave_write_alias(slave, buffer, size);
    }
/*****************************************************************************/

/**
   Calculates the size of a sync manager by evaluating PDO sizes.
   \return sync manager size
*/

uint16_t ec_slave_calc_sync_size(const ec_slave_t *slave,
                                 /**< EtherCAT slave */
                                 const ec_sii_sync_t *sync
                                 /**< sync manager */
                                 )
    ec_pdo_t *pdo;
    ec_pdo_entry_t *pdo_entry;
    unsigned int bit_size, byte_size;

    if (sync->length) return sync->length;
    if (sync->est_length) return sync->est_length;
    list_for_each_entry(pdo, &slave->sii_pdos, list) {
        if (pdo->sync_index != sync->index) continue;

        list_for_each_entry(pdo_entry, &pdo->entries, list) {
            bit_size += pdo_entry->bit_length;
    if (bit_size % 8) // round up to full bytes
        byte_size = bit_size / 8 + 1;
        byte_size = bit_size / 8;

    return byte_size;
/*****************************************************************************/
/**
 */

ec_sii_sync_t *ec_slave_get_pdo_sync(
        ec_slave_t *slave, /**< EtherCAT slave */
        ec_direction_t dir /**< input or output */
        )
{
    unsigned int sync_index;

    switch (dir) {
        case EC_DIR_OUTPUT: sync_index = 0; break;
        case EC_DIR_INPUT:  sync_index = 1; break;
        default:
            EC_ERR("Invalid direction!\n");
            return NULL;
    }

    if (slave->sii_mailbox_protocols) sync_index += 2;

    if (sync_index >= slave->sii_sync_count) {
        EC_ERR("No appropriate sync manager found.\n");
        return NULL;
    }

    return &slave->sii_syncs[sync_index];
}

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

/**
   Initializes a sync manager configuration page with EEPROM data.
   The referenced memory (\a data) must be at least EC_SYNC_SIZE bytes.
*/

void ec_slave_sync_config(const ec_slave_t *slave, /**< EtherCAT slave */
        const ec_sii_sync_t *sync, /**< sync manager */
        uint8_t *data /**> configuration memory */
        )
{
    size_t sync_size;

    sync_size = ec_slave_calc_sync_size(slave, sync);

    if (slave->master->debug_level) {
        EC_DBG("Slave %3i, SM %i: Addr 0x%04X, Size %3i, Ctrl 0x%02X, En %i\n",
               slave->ring_position, sync->index, sync->physical_start_address,
               sync_size, sync->control_register, sync->enable);
    }

    EC_WRITE_U16(data,     sync->physical_start_address);
    EC_WRITE_U16(data + 2, sync_size);
    EC_WRITE_U8 (data + 4, sync->control_register);
    EC_WRITE_U8 (data + 5, 0x00); // status byte (read only)
    EC_WRITE_U16(data + 6, sync->enable ? 0x0001 : 0x0000); // enable
}

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

/**
   Initializes an FMMU configuration page.
   The referenced memory (\a data) must be at least EC_FMMU_SIZE bytes.
*/

void ec_slave_fmmu_config(const ec_slave_t *slave, /**< EtherCAT slave */
        const ec_fmmu_t *fmmu, /**< FMMU */
        uint8_t *data /**> configuration memory */
        )
{
    size_t sync_size;

    sync_size = ec_slave_calc_sync_size(slave, fmmu->sync);

    if (slave->master->debug_level) {
        EC_DBG("Slave %3i, FMMU %2i:"
               " LogAddr 0x%08X, Size %3i, PhysAddr 0x%04X, Dir %s\n",
               slave->ring_position, fmmu->index, fmmu->logical_start_address,
               sync_size, fmmu->sync->physical_start_address,
               ((fmmu->sync->control_register & 0x04) ? "out" : "in"));
    }

    EC_WRITE_U32(data,      fmmu->logical_start_address);
    EC_WRITE_U16(data + 4,  sync_size); // size of fmmu
    EC_WRITE_U8 (data + 6,  0x00); // logical start bit
    EC_WRITE_U8 (data + 7,  0x07); // logical end bit
    EC_WRITE_U16(data + 8,  fmmu->sync->physical_start_address);
    EC_WRITE_U8 (data + 10, 0x00); // physical start bit
    EC_WRITE_U8 (data + 11, ((fmmu->sync->control_register & 0x04)
                             ? 0x02 : 0x01));
    EC_WRITE_U16(data + 12, 0x0001); // enable
    EC_WRITE_U16(data + 14, 0x0000); // reserved
}

/*****************************************************************************/
   \return non-zero if slave is a bus coupler
int ec_slave_is_coupler(const ec_slave_t *slave /**< EtherCAT slave */)
    // TODO: Better bus coupler criterion
    return slave->sii_vendor_id == 0x00000002
        && 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;
}

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

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

int ec_slave_validate(const ec_slave_t *slave, /**< EtherCAT slave */
                      uint32_t vendor_id, /**< vendor ID */
                      uint32_t product_code /**< product code */
                      )
{
    if (vendor_id != slave->sii_vendor_id ||
        product_code != slave->sii_product_code) {
        EC_ERR("Invalid slave type at position %i - Requested: 0x%08X 0x%08X,"
               " found: 0x%08X 0x%08X\".\n", slave->ring_position, vendor_id,
               product_code, slave->sii_vendor_id, slave->sii_product_code);
        return -1;
    }
    return 0;
}

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

/**
   Counts the total number of SDOs and entries in the dictionary.
*/

void ec_slave_sdo_dict_info(const ec_slave_t *slave, /**< EtherCAT slave */
                            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;
}

/******************************************************************************
 *  Realtime interface
 *****************************************************************************/
/**
   \return 0 in case of success, else < 0
   \ingroup RealtimeInterface
*/

int ecrt_slave_conf_sdo8(ec_slave_t *slave, /**< EtherCAT slave */
                         uint16_t sdo_index, /**< SDO index */
                         uint8_t sdo_subindex, /**< SDO subindex */
                         uint8_t value /**< new SDO value */
                         )
{
    uint8_t data[1];
    EC_WRITE_U8(data, value);
    return ec_slave_conf_sdo(slave, sdo_index, sdo_subindex, data, 1);
}

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

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

int ecrt_slave_conf_sdo16(ec_slave_t *slave, /**< EtherCAT slave */
                          uint16_t sdo_index, /**< SDO index */
                          uint8_t sdo_subindex, /**< SDO subindex */
                          uint16_t value /**< new SDO value */
                          )
{
    uint8_t data[2];
    EC_WRITE_U16(data, value);
    return ec_slave_conf_sdo(slave, sdo_index, sdo_subindex, data, 2);
}

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

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

int ecrt_slave_conf_sdo32(ec_slave_t *slave, /**< EtherCAT slave */
                          uint16_t sdo_index, /**< SDO index */
                          uint8_t sdo_subindex, /**< SDO subindex */
                          uint32_t value /**< new SDO value */
                          )
{
    uint8_t data[4];
    EC_WRITE_U32(data, value);
    return ec_slave_conf_sdo(slave, sdo_index, sdo_subindex, data, 4);
}

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

Florian Pose's avatar
Florian Pose committed
/** \cond */
EXPORT_SYMBOL(ecrt_slave_conf_sdo8);
EXPORT_SYMBOL(ecrt_slave_conf_sdo16);
EXPORT_SYMBOL(ecrt_slave_conf_sdo32);
Florian Pose's avatar
Florian Pose committed
/** \endcond */
/*****************************************************************************/