Skip to content
Snippets Groups Projects
slave.c 46.1 KiB
Newer Older
                          const ec_sync_t *sync  /**< sync manager */
    // FMMU configuration already prepared?
    for (i = 0; i < slave->fmmu_count; i++)
        if (slave->fmmus[i].domain == domain && slave->fmmus[i].sync == sync)
            return 0;

    if (slave->fmmu_count >= slave->base_fmmu_count) {
        EC_ERR("Slave %i FMMU limit reached!\n", slave->ring_position);
        return -1;
    }

    slave->fmmus[slave->fmmu_count].domain = domain;
    slave->fmmus[slave->fmmu_count].sync = sync;
    slave->fmmus[slave->fmmu_count].logical_start_address = 0;
    slave->fmmu_count++;
    slave->registered = 1;

    return 0;
}

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

/**
   Outputs all information about a certain slave.
   - 0: Only slave types and addresses
   - 1: with EEPROM information
   - >1: with SDO dictionaries
void ec_slave_print(const ec_slave_t *slave, /**< EtherCAT slave */
                    unsigned int verbosity /**< verbosity level */
    ec_eeprom_sync_t *sync;
    ec_eeprom_pdo_t *pdo;
Florian Pose's avatar
Florian Pose committed
    ec_eeprom_pdo_entry_t *pdo_entry;
    ec_sdo_t *sdo;
Florian Pose's avatar
Florian Pose committed
    ec_sdo_entry_t *sdo_entry;
Florian Pose's avatar
Florian Pose committed
    int first, i;
        EC_INFO("%i) %s %s: %s\n", slave->ring_position,
                slave->type->vendor_name, slave->type->product_name,
                slave->type->description);
        EC_INFO("%i) UNKNOWN SLAVE: vendor 0x%08X, product 0x%08X\n",
                slave->ring_position, slave->sii_vendor_id,
                slave->sii_product_code);
    if (!verbosity) return;

    EC_INFO("  Station address: 0x%04X\n", slave->station_address);
    EC_INFO("  Data link status:\n");
    for (i = 0; i < 4; i++) {
        EC_INFO("    Port %i (", i);
        switch (slave->sii_physical_layer[i]) {
            case 0x00:
                printk("EBUS");
                break;
            case 0x01:
                printk("100BASE-TX");
                break;
            case 0x02:
                printk("100BASE-FX");
                break;
            default:
                printk("unknown");
        }
        printk(")\n");
        EC_INFO("      link %s, loop %s, %s\n",
                slave->dl_link[i] ? "up" : "down",
                slave->dl_loop[i] ? "closed" : "open",
                slave->dl_signal[i] ? "signal detected" : "no signal");
    EC_INFO("  Base information:\n");
    EC_INFO("    Type %u, revision %i, build %i\n",
            slave->base_type, slave->base_revision, slave->base_build);
    EC_INFO("    Supported FMMUs: %i, sync managers: %i\n",
            slave->base_fmmu_count, slave->base_sync_count);

    if (slave->sii_mailbox_protocols) {
        EC_INFO("  Mailbox communication:\n");
        EC_INFO("    RX mailbox: 0x%04X/%i, TX mailbox: 0x%04X/%i\n",
                slave->sii_rx_mailbox_offset, slave->sii_rx_mailbox_size,
                slave->sii_tx_mailbox_offset, slave->sii_tx_mailbox_size);
        EC_INFO("    Supported protocols: ");

        first = 1;
        if (slave->sii_mailbox_protocols & EC_MBOX_AOE) {
            printk("AoE");
            first = 0;
        }
        if (slave->sii_mailbox_protocols & EC_MBOX_EOE) {
            if (!first) printk(", ");
            printk("EoE");
            first = 0;
        }
        if (slave->sii_mailbox_protocols & EC_MBOX_COE) {
            if (!first) printk(", ");
            printk("CoE");
            first = 0;
        }
        if (slave->sii_mailbox_protocols & EC_MBOX_FOE) {
            if (!first) printk(", ");
            printk("FoE");
            first = 0;
        if (slave->sii_mailbox_protocols & EC_MBOX_SOE) {
            if (!first) printk(", ");
            printk("SoE");
            first = 0;
        }
        if (slave->sii_mailbox_protocols & EC_MBOX_VOE) {
            if (!first) printk(", ");
            printk("VoE");
        }
        printk("\n");
    }
    EC_INFO("  EEPROM data:\n");
    EC_INFO("    EEPROM content size: %i Bytes\n", slave->eeprom_size);

    if (slave->sii_alias)
        EC_INFO("    Configured station alias: 0x%04X (%i)\n",
                slave->sii_alias, slave->sii_alias);

    EC_INFO("    Vendor-ID: 0x%08X, Product code: 0x%08X\n",
            slave->sii_vendor_id, slave->sii_product_code);
    EC_INFO("    Revision number: 0x%08X, Serial number: 0x%08X\n",
            slave->sii_revision_number, slave->sii_serial_number);

    if (slave->eeprom_group)
        EC_INFO("    Group: %s\n", slave->eeprom_group);
    if (slave->eeprom_image)
        EC_INFO("    Image: %s\n", slave->eeprom_image);
    if (slave->eeprom_order)
        EC_INFO("    Order#: %s\n", slave->eeprom_order);
    if (slave->eeprom_name)
        EC_INFO("    Name: %s\n", slave->eeprom_name);

    if (!list_empty(&slave->eeprom_syncs)) {
        EC_INFO("    Sync-Managers:\n");
        list_for_each_entry(sync, &slave->eeprom_syncs, list) {
            EC_INFO("      %i: 0x%04X, length %i, control 0x%02X, %s\n",
                    sync->index, sync->physical_start_address,
                    sync->length, sync->control_register,
                    sync->enable ? "enable" : "disable");
    list_for_each_entry(pdo, &slave->eeprom_pdos, list) {
        EC_INFO("    %s \"%s\" (0x%04X), -> Sync-Manager %i\n",
                pdo->type == EC_RX_PDO ? "RXPDO" : "TXPDO",
                pdo->name ? pdo->name : "???",
                pdo->index, pdo->sync_manager);

        list_for_each_entry(pdo_entry, &pdo->entries, list) {
            EC_INFO("      \"%s\" 0x%04X:%X, %i Bit\n",
                    pdo_entry->name ? pdo_entry->name : "???",
                    pdo_entry->index, pdo_entry->subindex,
                    pdo_entry->bit_length);
    if (verbosity < 2) return;

    if (!list_empty(&slave->sdo_dictionary)) {
        EC_INFO("    SDO-Dictionary:\n");
        list_for_each_entry(sdo, &slave->sdo_dictionary, list) {
            EC_INFO("      0x%04X \"%s\"\n", sdo->index,
                    sdo->name ? sdo->name : "");
            EC_INFO("        Object code: 0x%02X\n", sdo->object_code);
            list_for_each_entry(sdo_entry, &sdo->entries, list) {
                EC_INFO("        0x%04X:%i \"%s\", type 0x%04X, %i bits\n",
                        sdo->index, sdo_entry->subindex,
                        sdo_entry->name ? sdo_entry->name : "",
                        sdo_entry->data_type, sdo_entry->bit_length);
/*****************************************************************************/
   Outputs the values of the CRC faoult counters and resets them.
   \return 0 in case of success, else < 0
int ec_slave_check_crc(ec_slave_t *slave /**< EtherCAT slave */)
    command = &slave->master->simple_command;

    if (ec_command_nprd(command, slave->station_address, 0x0300, 4)) return -1;
    if (unlikely(ec_master_simple_io(slave->master, command))) {
        EC_WARN("Reading CRC fault counters failed on slave %i!\n",
                slave->ring_position);
    if (!EC_READ_U32(command->data)) return 0; // no CRC faults
    if (EC_READ_U8(command->data))
        EC_WARN("%3i RX-error%s on slave %i, channel A.\n",
                EC_READ_U8(command->data),
                EC_READ_U8(command->data) == 1 ? "" : "s",
                slave->ring_position);
    if (EC_READ_U8(command->data + 1))
        EC_WARN("%3i invalid frame%s on slave %i, channel A.\n",
                EC_READ_U8(command->data + 1),
                EC_READ_U8(command->data + 1) == 1 ? "" : "s",
                slave->ring_position);
    if (EC_READ_U8(command->data + 2))
        EC_WARN("%3i RX-error%s on slave %i, channel B.\n",
                EC_READ_U8(command->data + 2),
                EC_READ_U8(command->data + 2) == 1 ? "" : "s",
                slave->ring_position);
    if (EC_READ_U8(command->data + 3))
        EC_WARN("%3i invalid frame%s on slave %i, channel B.\n",
                EC_READ_U8(command->data + 3),
                EC_READ_U8(command->data + 3) == 1 ? "" : "s",
                slave->ring_position);
    if (ec_command_npwr(command, slave->station_address, 0x0300, 4)) return -1;
    EC_WRITE_U32(command->data, 0x00000000);
    if (unlikely(ec_master_simple_io(slave->master, command))) {
        EC_WARN("Resetting CRC fault counters failed on slave %i!\n",
                slave->ring_position);
        return -1;
    }

    return 0;
}

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

Florian Pose's avatar
Florian Pose committed
/**
   Schedules an EEPROM write operation.
   \return 0 in case of success, else < 0
*/

ssize_t ec_slave_write_eeprom(ec_slave_t *slave, /**< EtherCAT slave */
                              const uint8_t *data, /**< new EEPROM data */
                              size_t size /**< size of data in bytes */
                              )
{
    uint16_t word_size, cat_type, cat_size;
    const uint16_t *data_words, *next_header;
    uint16_t *new_data;

    if (!slave->master->eeprom_write_enable) {
        EC_ERR("Writing EEPROMs not allowed! Enable via"
               " eeprom_write_enable SysFS entry.\n");
        return -1;
    }

    if (slave->master->mode != EC_MASTER_MODE_FREERUN) {
        EC_ERR("Writing EEPROMs only allowed in freerun mode!\n");
        return -1;
    }

    if (slave->new_eeprom_data) {
        EC_ERR("Slave %i already has a pending EEPROM write operation!\n",
               slave->ring_position);
        return -1;
    }

    // coarse check of the data

    if (size % 2) {
        EC_ERR("EEPROM size is odd! Dropping.\n");
        return -1;
    }

    data_words = (const uint16_t *) data;
    word_size = size / 2;

    if (word_size < 0x0041) {
        EC_ERR("EEPROM data too short! Dropping.\n");
        return -1;
    }

    next_header = data_words + 0x0040;
    cat_type = EC_READ_U16(next_header);
    while (cat_type != 0xFFFF) {
        cat_type = EC_READ_U16(next_header);
        cat_size = EC_READ_U16(next_header + 1);
        if ((next_header + cat_size + 2) - data_words >= word_size) {
            EC_ERR("EEPROM data seems to be corrupted! Dropping.\n");
            return -1;
        }
        next_header += cat_size + 2;
        cat_type = EC_READ_U16(next_header);
    }

    // data ok!

    if (!(new_data = (uint16_t *) kmalloc(word_size * 2, GFP_KERNEL))) {
        EC_ERR("Unable to allocate memory for new EEPROM data!\n");
        return -1;
    }
    memcpy(new_data, data, size);

    slave->new_eeprom_size = word_size;
    slave->new_eeprom_data = new_data;

    EC_INFO("EEPROM writing scheduled for slave %i, %i words.\n",
            slave->ring_position, word_size);
    return 0;
}

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

   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_ring_position) {
        return sprintf(buffer, "%i\n", slave->ring_position);
    }
    else if (attr == &attr_coupler_address) {
        return sprintf(buffer, "%i:%i\n", slave->coupler_index,
                       slave->coupler_subindex);
    }
    else if (attr == &attr_vendor_name) {
        if (slave->type)
            return sprintf(buffer, "%s\n", slave->type->vendor_name);
    }
    else if (attr == &attr_product_name) {
        if (slave->type)
            return sprintf(buffer, "%s\n", slave->type->product_name);
    }
    else if (attr == &attr_product_desc) {
        if (slave->type)
            return sprintf(buffer, "%s\n", slave->type->description);
    }
    else if (attr == &attr_sii_name) {
        if (slave->eeprom_name)
            return sprintf(buffer, "%s\n", slave->eeprom_name);
    else if (attr == &attr_type) {
        if (slave->type) {
            if (slave->type->special == EC_TYPE_BUS_COUPLER)
                return sprintf(buffer, "coupler\n");
            else
                return sprintf(buffer, "normal\n");
        }
    }
    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_SAVEOP:
                return sprintf(buffer, "SAVEOP\n");
            case EC_SLAVE_STATE_OP:
                return sprintf(buffer, "OP\n");
            default:
                return sprintf(buffer, "UNKNOWN\n");
        }
    }
    else if (attr == &attr_eeprom) {
        if (slave->eeprom_data) {
            if (slave->eeprom_size > PAGE_SIZE) {
                EC_ERR("EEPROM contents of slave %i exceed 1 page (%i/%i).\n",
                       slave->ring_position, slave->eeprom_size,
                       (int) PAGE_SIZE);
            }
            else {
                memcpy(buffer, slave->eeprom_data, slave->eeprom_size);
                return slave->eeprom_size;
            }
        }
    }
/*****************************************************************************/

/**
   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")) {
            slave->requested_state = EC_SLAVE_STATE_INIT;
            slave->state_error = 0;
            return size;
        }
        else if (!strcmp(buffer, "PREOP\n")) {
            slave->requested_state = EC_SLAVE_STATE_PREOP;
            slave->state_error = 0;
            return size;
        }
        else if (!strcmp(buffer, "SAVEOP\n")) {
            slave->requested_state = EC_SLAVE_STATE_SAVEOP;
            slave->state_error = 0;
            return size;
        }
        else if (!strcmp(buffer, "OP\n")) {
            slave->requested_state = EC_SLAVE_STATE_OP;
            slave->state_error = 0;
            return size;
        }

        EC_ERR("Failed to set slave state!\n");
    }
Florian Pose's avatar
Florian Pose committed
    else if (attr == &attr_eeprom) {
        if (!ec_slave_write_eeprom(slave, buffer, size))
            return size;
    }
/******************************************************************************
 *  Realtime interface
 *****************************************************************************/

/**
   Writes the "configured station alias" to the slave's EEPROM.
   \return 0 in case of success, else < 0
   \ingroup RealtimeInterface
*/

int ecrt_slave_write_alias(ec_slave_t *slave, /**< EtherCAT slave */
                           uint16_t alias /**< new alias */
                           )
{
    return ec_slave_sii_write16(slave, 0x0004, alias);
}

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

EXPORT_SYMBOL(ecrt_slave_write_alias);

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