fp@39: /****************************************************************************** fp@0: * fp@54: * m a s t e r . c fp@0: * fp@0: * Methoden für einen EtherCAT-Master. fp@0: * fp@39: * $Id$ fp@0: * fp@39: *****************************************************************************/ fp@0: fp@24: #include fp@0: #include fp@0: #include fp@0: #include fp@0: #include fp@0: fp@104: #include "../include/ecrt.h" fp@54: #include "globals.h" fp@54: #include "master.h" fp@55: #include "slave.h" fp@55: #include "types.h" fp@54: #include "device.h" fp@98: #include "command.h" fp@0: fp@39: /*****************************************************************************/ fp@0: fp@0: /** fp@0: Konstruktor des EtherCAT-Masters. fp@68: */ fp@68: fp@73: void ec_master_init(ec_master_t *master /**< EtherCAT-Master */) fp@73: { fp@73: master->slaves = NULL; fp@98: master->device = NULL; fp@98: fp@98: INIT_LIST_HEAD(&master->commands); fp@95: INIT_LIST_HEAD(&master->domains); fp@98: fp@98: ec_master_reset(master); fp@0: } fp@0: fp@39: /*****************************************************************************/ fp@0: fp@0: /** fp@0: Destruktor eines EtherCAT-Masters. fp@0: fp@0: Entfernt alle Kommandos aus der Liste, löscht den Zeiger fp@0: auf das Slave-Array und gibt die Prozessdaten frei. fp@68: */ fp@68: fp@73: void ec_master_clear(ec_master_t *master /**< EtherCAT-Master */) fp@73: { fp@73: ec_master_reset(master); fp@98: fp@98: if (master->device) { fp@98: ec_device_clear(master->device); fp@98: kfree(master->device); fp@98: } fp@73: } fp@73: fp@73: /*****************************************************************************/ fp@73: fp@73: /** fp@73: Setzt den Master zurück in den Ausgangszustand. fp@56: fp@56: Bei einem "release" sollte immer diese Funktion aufgerufen werden, fp@56: da sonst Slave-Liste, Domains, etc. weiter existieren. fp@68: */ fp@68: fp@68: void ec_master_reset(ec_master_t *master fp@68: /**< Zeiger auf den zurückzusetzenden Master */ fp@68: ) fp@56: { fp@74: unsigned int i; fp@98: ec_command_t *command, *next_command; fp@98: ec_domain_t *domain, *next_domain; fp@98: fp@98: // Alle Slaves entfernen fp@74: if (master->slaves) { fp@74: for (i = 0; i < master->slave_count; i++) { fp@74: ec_slave_clear(master->slaves + i); fp@74: } fp@74: kfree(master->slaves); fp@74: master->slaves = NULL; fp@74: } fp@74: master->slave_count = 0; fp@98: fp@98: // Kommando-Warteschlange leeren fp@98: list_for_each_entry_safe(command, next_command, &master->commands, list) { fp@98: command->state = EC_CMD_ERROR; fp@98: list_del_init(&command->list); fp@98: } fp@98: fp@98: // Domain-Liste leeren fp@98: list_for_each_entry_safe(domain, next_domain, &master->domains, list) { fp@98: list_del(&domain->list); fp@98: ec_domain_clear(domain); fp@98: kfree(domain); fp@98: } fp@98: fp@98: master->command_index = 0; fp@98: master->debug_level = 0; fp@106: master->timeout = 100; // us fp@130: fp@130: master->slaves_responding = 0; fp@130: master->slave_states = EC_SLAVE_STATE_UNKNOWN; fp@130: fp@98: master->stats.timeouts = 0; fp@98: master->stats.delayed = 0; fp@98: master->stats.corrupted = 0; fp@98: master->stats.unmatched = 0; fp@98: master->stats.t_last = 0; fp@74: } fp@74: fp@74: /*****************************************************************************/ fp@74: fp@74: /** fp@54: Öffnet das EtherCAT-Geraet des Masters. fp@54: fp@91: \return 0 wenn alles ok, < 0 wenn kein Gerät registriert wurde oder fp@91: es nicht geoeffnet werden konnte. fp@68: */ fp@68: fp@68: int ec_master_open(ec_master_t *master /**< Der EtherCAT-Master */) fp@54: { fp@98: if (!master->device) { fp@84: EC_ERR("No device registered!\n"); fp@73: return -1; fp@73: } fp@73: fp@98: if (ec_device_open(master->device)) { fp@84: EC_ERR("Could not open device!\n"); fp@73: return -1; fp@73: } fp@73: fp@73: return 0; fp@54: } fp@54: fp@54: /*****************************************************************************/ fp@54: fp@54: /** fp@54: Schliesst das EtherCAT-Geraet, auf dem der Master arbeitet. fp@68: */ fp@68: fp@68: void ec_master_close(ec_master_t *master /**< EtherCAT-Master */) fp@54: { fp@98: if (!master->device) { fp@84: EC_WARN("Warning - Trying to close an unregistered device!\n"); fp@73: return; fp@73: } fp@73: fp@98: if (ec_device_close(master->device)) fp@84: EC_WARN("Warning - Could not close device!\n"); fp@0: } fp@0: fp@39: /*****************************************************************************/ fp@0: fp@17: /** fp@98: Stellt ein Kommando in die Warteschlange. fp@98: */ fp@98: fp@98: void ec_master_queue_command(ec_master_t *master, /**< EtherCAT-Master */ fp@98: ec_command_t *command /**< Kommando */ fp@98: ) fp@98: { fp@98: ec_command_t *queued_command; fp@98: fp@98: // Ist das Kommando schon in der Warteschlange? fp@98: list_for_each_entry(queued_command, &master->commands, list) { fp@98: if (queued_command == command) { fp@98: command->state = EC_CMD_QUEUED; fp@98: if (unlikely(master->debug_level)) fp@98: EC_WARN("command already queued.\n"); fp@98: return; fp@98: } fp@98: } fp@98: fp@98: list_add_tail(&command->list, &master->commands); fp@98: command->state = EC_CMD_QUEUED; fp@98: } fp@98: fp@98: /*****************************************************************************/ fp@98: fp@98: /** fp@98: Sendet die Kommandos in der Warteschlange. fp@98: fp@98: \return 0 bei Erfolg, sonst < 0 fp@98: */ fp@98: fp@98: void ec_master_send_commands(ec_master_t *master /**< EtherCAT-Master */) fp@98: { fp@98: ec_command_t *command; fp@98: size_t command_size; fp@98: uint8_t *frame_data, *cur_data; fp@98: void *follows_word; fp@98: cycles_t start = 0, end; fp@98: fp@98: if (unlikely(master->debug_level > 0)) { fp@98: EC_DBG("ec_master_send\n"); fp@98: start = get_cycles(); fp@98: } fp@98: fp@98: // Zeiger auf Socket-Buffer holen fp@98: frame_data = ec_device_tx_data(master->device); fp@98: cur_data = frame_data + EC_FRAME_HEADER_SIZE; fp@98: follows_word = NULL; fp@98: fp@98: // Aktuellen Frame mit Kommandos füllen fp@98: list_for_each_entry(command, &master->commands, list) { fp@98: if (command->state != EC_CMD_QUEUED) continue; fp@98: fp@98: // Passt das aktuelle Kommando noch in den aktuellen Rahmen? fp@98: command_size = EC_COMMAND_HEADER_SIZE + command->data_size fp@98: + EC_COMMAND_FOOTER_SIZE; fp@98: if (cur_data - frame_data + command_size > EC_MAX_FRAME_SIZE) break; fp@98: fp@98: command->state = EC_CMD_SENT; fp@98: command->index = master->command_index++; fp@98: fp@98: if (unlikely(master->debug_level > 0)) fp@98: EC_DBG("adding command 0x%02X\n", command->index); fp@98: fp@98: // Command-Following-Flag im letzten Kommando setzen fp@98: if (follows_word) fp@98: EC_WRITE_U16(follows_word, EC_READ_U16(follows_word) | 0x8000); fp@98: fp@98: // EtherCAT command header fp@98: EC_WRITE_U8 (cur_data, command->type); fp@98: EC_WRITE_U8 (cur_data + 1, command->index); fp@98: EC_WRITE_U32(cur_data + 2, command->address.logical); fp@98: EC_WRITE_U16(cur_data + 6, command->data_size & 0x7FF); fp@98: EC_WRITE_U16(cur_data + 8, 0x0000); fp@98: follows_word = cur_data + 6; fp@98: cur_data += EC_COMMAND_HEADER_SIZE; fp@98: fp@98: // EtherCAT command data fp@98: memcpy(cur_data, command->data, command->data_size); fp@98: cur_data += command->data_size; fp@98: fp@98: // EtherCAT command footer fp@98: EC_WRITE_U16(cur_data, command->working_counter); fp@98: cur_data += EC_COMMAND_FOOTER_SIZE; fp@98: } fp@98: fp@98: if (cur_data - frame_data == EC_FRAME_HEADER_SIZE) { fp@98: if (unlikely(master->debug_level > 0)) EC_DBG("nothing to send.\n"); fp@98: return; fp@98: } fp@98: fp@98: // EtherCAT frame header fp@98: EC_WRITE_U16(frame_data, ((cur_data - frame_data fp@98: - EC_FRAME_HEADER_SIZE) & 0x7FF) | 0x1000); fp@98: fp@98: // Rahmen auffüllen fp@98: while (cur_data - frame_data < EC_MIN_FRAME_SIZE) fp@98: EC_WRITE_U8(cur_data++, 0x00); fp@98: fp@98: if (unlikely(master->debug_level > 0)) fp@98: EC_DBG("Frame size: %i\n", cur_data - frame_data); fp@98: fp@98: // Send frame fp@98: ec_device_send(master->device, cur_data - frame_data); fp@98: fp@98: if (unlikely(master->debug_level > 0)) { fp@98: end = get_cycles(); fp@98: EC_DBG("ec_master_send finished in %ius.\n", fp@98: (u32) (end - start) * 1000 / cpu_khz); fp@98: } fp@98: } fp@98: fp@98: /*****************************************************************************/ fp@98: fp@98: /** fp@98: Wertet einen empfangenen Rahmen aus. fp@98: fp@98: \return 0 bei Erfolg, sonst < 0 fp@98: */ fp@98: fp@98: void ec_master_receive(ec_master_t *master, /**< EtherCAT-Master */ fp@98: const uint8_t *frame_data, /**< Empfangene Daten */ fp@98: size_t size /**< Anzahl empfangene Datenbytes */ fp@98: ) fp@98: { fp@98: size_t frame_size, data_size; fp@98: uint8_t command_type, command_index; fp@98: unsigned int cmd_follows, matched; fp@98: const uint8_t *cur_data; fp@98: ec_command_t *command; fp@98: fp@98: if (unlikely(size < EC_FRAME_HEADER_SIZE)) { fp@98: master->stats.corrupted++; fp@98: ec_master_output_stats(master); fp@98: return; fp@98: } fp@98: fp@98: cur_data = frame_data; fp@98: fp@98: // Länge des gesamten Frames prüfen fp@98: frame_size = EC_READ_U16(cur_data) & 0x07FF; fp@98: cur_data += EC_FRAME_HEADER_SIZE; fp@98: fp@98: if (unlikely(frame_size > size)) { fp@98: master->stats.corrupted++; fp@98: ec_master_output_stats(master); fp@98: return; fp@98: } fp@98: fp@98: cmd_follows = 1; fp@98: while (cmd_follows) { fp@98: // Kommando-Header auswerten fp@98: command_type = EC_READ_U8 (cur_data); fp@98: command_index = EC_READ_U8 (cur_data + 1); fp@98: data_size = EC_READ_U16(cur_data + 6) & 0x07FF; fp@98: cmd_follows = EC_READ_U16(cur_data + 6) & 0x8000; fp@98: cur_data += EC_COMMAND_HEADER_SIZE; fp@98: fp@98: if (unlikely(cur_data - frame_data fp@98: + data_size + EC_COMMAND_FOOTER_SIZE > size)) { fp@98: master->stats.corrupted++; fp@98: ec_master_output_stats(master); fp@98: return; fp@98: } fp@98: fp@98: // Suche passendes Kommando in der Liste fp@98: matched = 0; fp@98: list_for_each_entry(command, &master->commands, list) { fp@98: if (command->state == EC_CMD_SENT fp@98: && command->type == command_type fp@98: && command->index == command_index fp@98: && command->data_size == data_size) { fp@98: matched = 1; fp@98: break; fp@98: } fp@98: } fp@98: fp@98: // Kein passendes Kommando in der Liste gefunden fp@98: if (!matched) { fp@98: master->stats.unmatched++; fp@98: ec_master_output_stats(master); fp@98: cur_data += data_size + EC_COMMAND_FOOTER_SIZE; fp@98: continue; fp@98: } fp@98: fp@98: // Empfangene Daten in Kommando-Datenspeicher kopieren fp@98: memcpy(command->data, cur_data, data_size); fp@98: cur_data += data_size; fp@98: fp@98: // Working-Counter setzen fp@98: command->working_counter = EC_READ_U16(cur_data); fp@98: cur_data += EC_COMMAND_FOOTER_SIZE; fp@98: fp@98: // Kommando aus der Liste entfernen fp@98: command->state = EC_CMD_RECEIVED; fp@98: list_del_init(&command->list); fp@98: } fp@98: } fp@98: fp@98: /*****************************************************************************/ fp@98: fp@98: /** fp@98: Sendet ein einzelnes Kommando und wartet auf den Empfang. fp@98: fp@98: Wenn der Slave nicht antwortet, wird das Kommando fp@98: nochmals gesendet. fp@98: fp@98: \return 0 bei Erfolg, sonst < 0 fp@98: */ fp@98: fp@98: int ec_master_simple_io(ec_master_t *master, /**< EtherCAT-Master */ fp@98: ec_command_t *command /**< Kommando */ fp@98: ) fp@98: { fp@98: unsigned int response_tries_left; fp@98: fp@98: response_tries_left = 10; fp@113: fp@113: while (1) fp@98: { fp@98: ec_master_queue_command(master, command); fp@104: ecrt_master_sync_io(master); fp@98: fp@98: if (command->state == EC_CMD_RECEIVED) { fp@113: if (likely(command->working_counter)) fp@113: return 0; fp@98: } fp@98: else if (command->state == EC_CMD_TIMEOUT) { fp@104: EC_ERR("Simple-IO TIMEOUT!\n"); fp@98: return -1; fp@98: } fp@98: else if (command->state == EC_CMD_ERROR) { fp@104: EC_ERR("Simple-IO command error!\n"); fp@98: return -1; fp@98: } fp@104: fp@104: // Keine direkte Antwort. Dem Slave Zeit lassen... fp@130: udelay(100); fp@113: fp@113: if (unlikely(--response_tries_left)) { fp@113: EC_ERR("No response in simple-IO!\n"); fp@113: return -1; fp@113: } fp@113: } fp@98: } fp@98: fp@98: /*****************************************************************************/ fp@98: fp@98: /** fp@91: Durchsucht den EtherCAT-Bus nach Slaves. fp@91: fp@91: Erstellt ein Array mit allen Slave-Informationen die für den fp@91: weiteren Betrieb notwendig sind. fp@91: fp@91: \return 0 bei Erfolg, sonst < 0 fp@17: */ fp@17: fp@98: int ec_master_bus_scan(ec_master_t *master /**< EtherCAT-Master */) fp@98: { fp@98: ec_command_t command; fp@73: ec_slave_t *slave; fp@73: ec_slave_ident_t *ident; fp@73: unsigned int i; fp@101: uint8_t data[2]; fp@73: fp@90: if (master->slaves || master->slave_count) { fp@90: EC_ERR("Slave scan already done!\n"); fp@90: return -1; fp@90: } fp@73: fp@73: // Determine number of slaves on bus fp@98: ec_command_init_brd(&command, 0x0000, 4); fp@98: if (unlikely(ec_master_simple_io(master, &command))) return -1; fp@98: master->slave_count = command.working_counter; fp@84: EC_INFO("Found %i slaves on bus.\n", master->slave_count); fp@73: fp@73: if (!master->slave_count) return 0; fp@73: fp@73: if (!(master->slaves = (ec_slave_t *) kmalloc(master->slave_count fp@90: * sizeof(ec_slave_t), fp@90: GFP_KERNEL))) { fp@90: EC_ERR("Could not allocate memory for slaves!\n"); fp@73: return -1; fp@73: } fp@73: fp@73: // Init slaves fp@73: for (i = 0; i < master->slave_count; i++) { fp@73: slave = master->slaves + i; fp@73: ec_slave_init(slave, master); fp@73: slave->ring_position = i; fp@73: slave->station_address = i + 1; fp@73: } fp@73: fp@73: // For every slave in the list fp@73: for (i = 0; i < master->slave_count; i++) fp@17: { fp@73: slave = master->slaves + i; fp@73: fp@73: // Write station address fp@77: EC_WRITE_U16(data, slave->station_address); fp@98: ec_command_init_apwr(&command, slave->ring_position, fp@98: 0x0010, sizeof(uint16_t), data); fp@98: if (unlikely(ec_master_simple_io(master, &command))) { fp@89: EC_ERR("Writing station address failed on slave %i!\n", i); fp@73: return -1; fp@73: } fp@73: fp@73: // Fetch all slave information fp@73: if (ec_slave_fetch(slave)) return -1; fp@73: fp@73: // Search for identification in "database" fp@73: ident = slave_idents; fp@129: while (ident->type) { fp@73: if (unlikely(ident->vendor_id == slave->sii_vendor_id fp@73: && ident->product_code == slave->sii_product_code)) { fp@73: slave->type = ident->type; fp@73: break; fp@73: } fp@73: ident++; fp@73: } fp@73: fp@84: if (!slave->type) fp@84: EC_WARN("Unknown slave device (vendor 0x%08X, code 0x%08X) at" fp@84: " position %i.\n", slave->sii_vendor_id, fp@84: slave->sii_product_code, i); fp@73: } fp@73: fp@73: return 0; fp@54: } fp@54: fp@54: /*****************************************************************************/ fp@54: fp@54: /** fp@98: Statistik-Ausgaben während des zyklischen Betriebs. fp@98: fp@98: Diese Funktion sorgt dafür, dass Statistiken während des zyklischen fp@98: Betriebs bei Bedarf, aber nicht zu oft ausgegeben werden. fp@91: fp@91: Die Ausgabe erfolgt gesammelt höchstens einmal pro Sekunde. fp@68: */ fp@68: fp@98: void ec_master_output_stats(ec_master_t *master /**< EtherCAT-Master */) fp@98: { fp@98: cycles_t t_now = get_cycles(); fp@98: fp@98: if (unlikely((u32) (t_now - master->stats.t_last) / cpu_khz > 1000)) { fp@98: if (master->stats.timeouts) { fp@98: EC_WARN("%i commands TIMED OUT!\n", master->stats.timeouts); fp@98: master->stats.timeouts = 0; fp@98: } fp@98: if (master->stats.delayed) { fp@98: EC_WARN("%i frame(s) DELAYED!\n", master->stats.delayed); fp@98: master->stats.delayed = 0; fp@98: } fp@98: if (master->stats.corrupted) { fp@98: EC_WARN("%i frame(s) CORRUPTED!\n", master->stats.corrupted); fp@98: master->stats.corrupted = 0; fp@98: } fp@98: if (master->stats.unmatched) { fp@98: EC_WARN("%i command(s) UNMATCHED!\n", master->stats.unmatched); fp@98: master->stats.unmatched = 0; fp@98: } fp@98: master->stats.t_last = t_now; fp@73: } fp@54: } fp@54: fp@68: /*****************************************************************************/ fp@68: fp@68: /** fp@68: Wandelt eine ASCII-kodierte Bus-Adresse in einen Slave-Zeiger. fp@68: fp@68: Gültige Adress-Strings sind Folgende: fp@68: - \a "X" = der X. Slave im Bus, fp@68: - \a "X:Y" = der Y. Slave hinter dem X. Buskoppler, fp@114: - \a "#X" = der Slave mit dem Alias X, fp@114: - \a "#X:Y" = der Y. Slave hinter dem Buskoppler mit dem Alias X. fp@114: fp@114: X und Y fangen immer bei 0 an und können auch hexadezimal oder oktal fp@114: angegeben werden (mit entsprechendem Prefix). fp@68: fp@68: \return Zeiger auf Slave bei Erfolg, sonst NULL fp@68: */ fp@68: fp@138: ec_slave_t *ecrt_master_get_slave(const ec_master_t *master, /**< Master */ fp@138: const char *address /**< Address-String */ fp@138: ) fp@68: { fp@73: unsigned long first, second; fp@73: char *remainder, *remainder2; fp@114: unsigned int i, alias_requested, alias_slave_index, alias_found; fp@73: int coupler_idx, slave_idx; fp@73: ec_slave_t *slave; fp@73: fp@73: if (!address || address[0] == 0) return NULL; fp@73: fp@114: alias_requested = 0; fp@114: alias_slave_index = 0; fp@73: if (address[0] == '#') { fp@114: alias_requested = 1; fp@114: address++; fp@73: } fp@73: fp@73: first = simple_strtoul(address, &remainder, 0); fp@73: if (remainder == address) { fp@114: EC_ERR("Slave address \"%s\" - First number empty!\n", address); fp@73: return NULL; fp@73: } fp@73: fp@114: if (alias_requested) { fp@114: alias_found = 0; fp@114: for (i = 0; i < master->slave_count; i++) { fp@114: if (master->slaves[i].sii_alias == first) { fp@114: alias_slave_index = i; fp@114: alias_found = 1; fp@114: break; fp@114: } fp@114: } fp@114: if (!alias_found) { fp@114: EC_ERR("Slave address \"%s\" - Alias not found!\n", address); fp@114: return NULL; fp@114: } fp@114: } fp@114: fp@73: if (!remainder[0]) { // absolute position fp@114: if (alias_requested) { fp@114: return master->slaves + alias_slave_index; fp@114: } fp@114: else { fp@114: if (first < master->slave_count) { fp@114: return master->slaves + first; fp@114: } fp@114: EC_ERR("Slave address \"%s\" - Absolute position invalid!\n", fp@114: address); fp@114: } fp@114: } fp@73: else if (remainder[0] == ':') { // field position fp@73: remainder++; fp@73: second = simple_strtoul(remainder, &remainder2, 0); fp@73: fp@73: if (remainder2 == remainder) { fp@114: EC_ERR("Slave address \"%s\" - Second number empty!\n", address); fp@73: return NULL; fp@73: } fp@73: fp@73: if (remainder2[0]) { fp@114: EC_ERR("Slave address \"%s\" - Invalid trailer!\n", address); fp@73: return NULL; fp@73: } fp@73: fp@114: if (alias_requested) { fp@114: for (i = alias_slave_index + 1; i < master->slave_count; i++) { fp@114: slave = master->slaves + i; fp@114: if (!slave->type || slave->type->bus_coupler) break; fp@116: if (i - alias_slave_index == second) return slave; fp@73: } fp@114: EC_ERR("Slave address \"%s\" - Bus coupler %i has no %lu. slave" fp@114: " following!\n", address, fp@114: (master->slaves + alias_slave_index)->ring_position, fp@114: second); fp@114: return NULL; fp@114: } fp@114: else { fp@114: coupler_idx = -1; fp@114: slave_idx = 0; fp@114: for (i = 0; i < master->slave_count; i++, slave_idx++) { fp@114: slave = master->slaves + i; fp@116: if (!slave->type) continue; fp@114: if (slave->type->bus_coupler) { fp@114: coupler_idx++; fp@114: slave_idx = 0; fp@114: } fp@114: if (coupler_idx == first && slave_idx == second) return slave; fp@114: } fp@114: } fp@114: } fp@84: else fp@114: EC_ERR("Slave address \"%s\" - Invalid format!\n", address); fp@73: fp@68: return NULL; fp@73: } fp@73: fp@73: /*****************************************************************************/ fp@73: fp@73: /** fp@73: Initialisiert eine Sync-Manager-Konfigurationsseite. fp@73: fp@73: Der mit \a data referenzierte Speicher muss mindestens EC_SYNC_SIZE Bytes fp@73: groß sein. fp@73: */ fp@73: fp@73: void ec_sync_config(const ec_sync_t *sync, /**< Sync-Manager */ fp@73: uint8_t *data /**> Zeiger auf Konfigurationsspeicher */ fp@73: ) fp@73: { fp@77: EC_WRITE_U16(data, sync->physical_start_address); fp@77: EC_WRITE_U16(data + 2, sync->size); fp@77: EC_WRITE_U8 (data + 4, sync->control_byte); fp@77: EC_WRITE_U8 (data + 5, 0x00); // status byte (read only) fp@77: EC_WRITE_U16(data + 6, 0x0001); // enable fp@73: } fp@73: fp@73: /*****************************************************************************/ fp@73: fp@73: /** fp@136: Initialisiert eine Sync-Manager-Konfigurationsseite mit EEPROM-Daten. fp@136: fp@136: Der mit \a data referenzierte Speicher muss mindestens EC_SYNC_SIZE Bytes fp@136: groß sein. fp@136: */ fp@136: fp@136: void ec_eeprom_sync_config(const ec_eeprom_sync_t *sync, /**< Sync-Manager */ fp@136: uint8_t *data /**> Zeiger auf Konfig.-Speicher */ fp@136: ) fp@136: { fp@136: EC_WRITE_U16(data, sync->physical_start_address); fp@136: EC_WRITE_U16(data + 2, sync->length); fp@136: EC_WRITE_U8 (data + 4, sync->control_register); fp@136: EC_WRITE_U8 (data + 5, 0x00); // status byte (read only) fp@136: EC_WRITE_U16(data + 6, sync->enable ? 0x0001 : 0x0000); // enable fp@136: } fp@136: fp@136: /*****************************************************************************/ fp@136: fp@136: /** fp@73: Initialisiert eine FMMU-Konfigurationsseite. fp@73: fp@73: Der mit \a data referenzierte Speicher muss mindestens EC_FMMU_SIZE Bytes fp@73: groß sein. fp@73: */ fp@73: fp@73: void ec_fmmu_config(const ec_fmmu_t *fmmu, /**< Sync-Manager */ fp@73: uint8_t *data /**> Zeiger auf Konfigurationsspeicher */ fp@73: ) fp@73: { fp@77: EC_WRITE_U32(data, fmmu->logical_start_address); fp@77: EC_WRITE_U16(data + 4, fmmu->sync->size); fp@77: EC_WRITE_U8 (data + 6, 0x00); // Logical start bit fp@77: EC_WRITE_U8 (data + 7, 0x07); // Logical end bit fp@77: EC_WRITE_U16(data + 8, fmmu->sync->physical_start_address); fp@77: EC_WRITE_U8 (data + 10, 0x00); // Physical start bit fp@77: EC_WRITE_U8 (data + 11, (fmmu->sync->control_byte & 0x04) ? 0x02 : 0x01); fp@77: EC_WRITE_U16(data + 12, 0x0001); // Enable fp@77: EC_WRITE_U16(data + 14, 0x0000); // res. fp@68: } fp@68: fp@130: /*****************************************************************************/ fp@130: fp@130: /** fp@130: Gibt Überwachungsinformationen aus. fp@130: */ fp@130: fp@130: void ec_master_process_watch_command(ec_master_t *master fp@130: /**< EtherCAT-Master */ fp@130: ) fp@130: { fp@130: unsigned int first; fp@130: fp@130: first = 1; fp@130: fp@130: if (master->watch_command.working_counter != master->slaves_responding || fp@130: master->watch_command.data[0] != master->slave_states) fp@130: { fp@130: master->slaves_responding = master->watch_command.working_counter; fp@130: master->slave_states = master->watch_command.data[0]; fp@130: fp@130: EC_INFO("%i slave%s responding (", master->slaves_responding, fp@130: master->slaves_responding == 1 ? "" : "s"); fp@130: fp@130: if (master->slave_states & EC_SLAVE_STATE_INIT) { fp@130: printk("INIT"); fp@130: first = 0; fp@130: } fp@130: if (master->slave_states & EC_SLAVE_STATE_PREOP) { fp@133: if (!first) printk(", "); fp@130: printk("PREOP"); fp@133: first = 0; fp@130: } fp@130: if (master->slave_states & EC_SLAVE_STATE_SAVEOP) { fp@133: if (!first) printk(", "); fp@130: printk("SAVEOP"); fp@133: first = 0; fp@130: } fp@130: if (master->slave_states & EC_SLAVE_STATE_OP) { fp@133: if (!first) printk(", "); fp@130: printk("OP"); fp@130: } fp@130: printk(")\n"); fp@130: } fp@130: } fp@130: fp@54: /****************************************************************************** fp@54: * fp@54: * Echtzeitschnittstelle fp@54: * fp@54: *****************************************************************************/ fp@54: fp@54: /** fp@104: Erstellt eine neue Domäne. fp@73: fp@73: \return Zeiger auf die Domäne bei Erfolg, sonst NULL. fp@73: */ fp@73: fp@104: ec_domain_t *ecrt_master_create_domain(ec_master_t *master /**< Master */) fp@73: { fp@73: ec_domain_t *domain; fp@73: fp@73: if (!(domain = (ec_domain_t *) kmalloc(sizeof(ec_domain_t), GFP_KERNEL))) { fp@84: EC_ERR("Error allocating domain memory!\n"); fp@73: return NULL; fp@73: } fp@73: fp@104: ec_domain_init(domain, master); fp@95: list_add_tail(&domain->list, &master->domains); fp@73: fp@73: return domain; fp@73: } fp@61: fp@74: /*****************************************************************************/ fp@74: fp@61: /** fp@58: Konfiguriert alle Slaves und setzt den Operational-Zustand. fp@58: fp@58: Führt die komplette Konfiguration und Aktivierunge aller registrierten fp@91: Slaves durch. Setzt Sync-Manager und FMMUs, führt die entsprechenden fp@0: Zustandsübergänge durch, bis der Slave betriebsbereit ist. fp@0: fp@68: \return 0 bei Erfolg, sonst < 0 fp@68: */ fp@68: fp@104: int ecrt_master_activate(ec_master_t *master /**< EtherCAT-Master */) fp@73: { fp@73: unsigned int i, j; fp@73: ec_slave_t *slave; fp@98: ec_command_t command; fp@73: const ec_sync_t *sync; fp@73: const ec_slave_type_t *type; fp@73: const ec_fmmu_t *fmmu; fp@73: uint8_t data[256]; fp@73: uint32_t domain_offset; fp@84: ec_domain_t *domain; fp@136: ec_eeprom_sync_t *eeprom_sync; fp@73: fp@73: // Domains erstellen fp@73: domain_offset = 0; fp@95: list_for_each_entry(domain, &master->domains, list) { fp@73: if (ec_domain_alloc(domain, domain_offset)) { fp@95: EC_ERR("Failed to allocate domain %X!\n", (u32) domain); fp@73: return -1; fp@73: } fp@73: domain_offset += domain->data_size; fp@73: } fp@73: fp@73: // Slaves aktivieren fp@73: for (i = 0; i < master->slave_count; i++) fp@0: { fp@73: slave = master->slaves + i; fp@73: fp@73: // Change state to INIT fp@73: if (unlikely(ec_slave_state_change(slave, EC_SLAVE_STATE_INIT))) fp@73: return -1; fp@73: fp@73: // Check if slave was registered... fp@73: type = slave->type; fp@136: if (!type) EC_WARN("Slave %i has unknown type!\n", i); fp@73: fp@74: // Check and reset CRC fault counters fp@74: ec_slave_check_crc(slave); fp@74: fp@91: // Resetting FMMUs fp@73: if (slave->base_fmmu_count) { fp@73: memset(data, 0x00, EC_FMMU_SIZE * slave->base_fmmu_count); fp@98: ec_command_init_npwr(&command, slave->station_address, 0x0600, fp@98: EC_FMMU_SIZE * slave->base_fmmu_count, data); fp@98: if (unlikely(ec_master_simple_io(master, &command))) { fp@89: EC_ERR("Resetting FMMUs failed on slave %i!\n", fp@84: slave->ring_position); fp@73: return -1; fp@73: } fp@73: } fp@73: fp@73: // Resetting Sync Manager channels fp@73: if (slave->base_sync_count) { fp@73: memset(data, 0x00, EC_SYNC_SIZE * slave->base_sync_count); fp@98: ec_command_init_npwr(&command, slave->station_address, 0x0800, fp@98: EC_SYNC_SIZE * slave->base_sync_count, data); fp@98: if (unlikely(ec_master_simple_io(master, &command))) { fp@89: EC_ERR("Resetting sync managers failed on slave %i!\n", fp@84: slave->ring_position); fp@73: return -1; fp@73: } fp@73: } fp@73: fp@73: // Set Sync Managers fp@136: if (type) { fp@136: for (j = 0; type->sync_managers[j] && j < EC_MAX_SYNC; j++) fp@136: { fp@136: sync = type->sync_managers[j]; fp@136: ec_sync_config(sync, data); fp@136: ec_command_init_npwr(&command, slave->station_address, fp@136: 0x0800 + j * EC_SYNC_SIZE, EC_SYNC_SIZE, fp@136: data); fp@136: if (unlikely(ec_master_simple_io(master, &command))) { fp@136: EC_ERR("Setting sync manager %i failed on slave %i!\n", fp@136: j, slave->ring_position); fp@136: return -1; fp@136: } fp@136: } fp@136: } fp@136: else if (slave->sii_mailbox_protocols) { // Unknown slave, has mailbox fp@136: list_for_each_entry(eeprom_sync, &slave->eeprom_syncs, list) { fp@136: ec_eeprom_sync_config(eeprom_sync, data); fp@136: ec_command_init_npwr(&command, slave->station_address, fp@136: 0x800 + eeprom_sync->index * EC_SYNC_SIZE, fp@136: EC_SYNC_SIZE, data); fp@136: if (unlikely(ec_master_simple_io(master, &command))) { fp@136: EC_ERR("Setting sync manager %i failed on slave %i!\n", fp@136: eeprom_sync->index, slave->ring_position); fp@136: return -1; fp@136: } fp@73: } fp@73: } fp@73: fp@73: // Change state to PREOP fp@73: if (unlikely(ec_slave_state_change(slave, EC_SLAVE_STATE_PREOP))) fp@73: return -1; fp@73: fp@136: // Stop activation here for slaves without type fp@136: if (!type) continue; fp@136: fp@81: // Slaves that are not registered are only brought into PREOP fp@81: // state -> nice blinking and mailbox comm. possible fp@82: if (!slave->registered && !slave->type->bus_coupler) { fp@84: EC_WARN("Slave %i was not registered!\n", slave->ring_position); fp@81: continue; fp@81: } fp@81: fp@73: // Set FMMUs fp@73: for (j = 0; j < slave->fmmu_count; j++) fp@73: { fp@73: fmmu = &slave->fmmus[j]; fp@73: fp@73: ec_fmmu_config(fmmu, data); fp@98: ec_command_init_npwr(&command, slave->station_address, fp@98: 0x0600 + j * EC_FMMU_SIZE, EC_FMMU_SIZE, fp@98: data); fp@98: if (unlikely(ec_master_simple_io(master, &command))) { fp@89: EC_ERR("Setting FMMU %i failed on slave %i!\n", fp@89: j, slave->ring_position); fp@73: return -1; fp@73: } fp@73: } fp@73: fp@73: // Change state to SAVEOP fp@73: if (unlikely(ec_slave_state_change(slave, EC_SLAVE_STATE_SAVEOP))) fp@73: return -1; fp@73: fp@73: // Change state to OP fp@73: if (unlikely(ec_slave_state_change(slave, EC_SLAVE_STATE_OP))) fp@73: return -1; fp@73: } fp@73: fp@130: master->slaves_responding = master->slave_count; fp@130: master->slave_states = EC_SLAVE_STATE_OP; fp@130: fp@73: return 0; fp@73: } fp@73: fp@73: /*****************************************************************************/ fp@73: fp@73: /** fp@73: Setzt alle Slaves zurück in den Init-Zustand. fp@104: */ fp@104: fp@104: void ecrt_master_deactivate(ec_master_t *master /**< EtherCAT-Master */) fp@73: { fp@73: ec_slave_t *slave; fp@73: unsigned int i; fp@73: fp@73: for (i = 0; i < master->slave_count; i++) fp@0: { fp@73: slave = master->slaves + i; fp@74: ec_slave_check_crc(slave); fp@104: ec_slave_state_change(slave, EC_SLAVE_STATE_INIT); fp@104: } fp@104: } fp@104: fp@135: fp@135: /*****************************************************************************/ fp@135: fp@135: /** fp@135: Lädt die SDO-Dictionaries aller Slaves. fp@135: fp@135: Slaves, die kein CoE unterstützen, werden ausgelassen. fp@135: fp@135: \return 0 wenn alles ok, sonst < 0 fp@135: */ fp@135: fp@135: int ecrt_master_fetch_sdo_lists(ec_master_t *master /**< EtherCAT-Master */) fp@135: { fp@135: ec_slave_t *slave; fp@135: unsigned int i; fp@135: fp@135: for (i = 0; i < master->slave_count; i++) { fp@135: slave = master->slaves + i; fp@135: if (slave->sii_mailbox_protocols & EC_MBOX_COE) { fp@135: if (unlikely(ec_slave_fetch_sdo_list(slave))) { fp@135: EC_ERR("Failed to fetch SDO list on slave %i!\n", fp@135: slave->ring_position); fp@135: return -1; fp@135: } fp@135: } fp@135: } fp@135: fp@135: return 0; fp@135: } fp@135: fp@104: /*****************************************************************************/ fp@104: fp@104: /** fp@104: Sendet und empfängt Kommandos synchron. fp@104: */ fp@104: fp@104: void ecrt_master_sync_io(ec_master_t *master) fp@98: { fp@98: ec_command_t *command, *next; fp@98: unsigned int commands_sent; fp@98: cycles_t t_start, t_end, t_timeout; fp@98: fp@98: ec_master_output_stats(master); fp@98: fp@98: if (unlikely(!master->device->link_state)) { fp@98: // Link DOWN, keines der Kommandos kann gesendet werden. fp@98: list_for_each_entry_safe(command, next, &master->commands, list) { fp@98: command->state = EC_CMD_ERROR; fp@98: list_del_init(&command->list); fp@98: } fp@98: fp@98: // Device-Zustand abfragen fp@98: ec_device_call_isr(master->device); fp@98: return; fp@98: } fp@98: fp@98: // Rahmen senden fp@98: ec_master_send_commands(master); fp@98: fp@98: t_start = get_cycles(); // Sendezeit nehmen fp@120: t_timeout = (cycles_t) master->timeout * (cpu_khz / 1000); fp@98: fp@113: while (1) fp@113: { fp@98: ec_device_call_isr(master->device); fp@98: fp@98: t_end = get_cycles(); // Aktuelle Zeit nehmen fp@98: if (t_end - t_start >= t_timeout) break; // Timeout fp@98: fp@98: commands_sent = 0; fp@98: list_for_each_entry_safe(command, next, &master->commands, list) { fp@98: if (command->state == EC_CMD_RECEIVED) fp@98: list_del_init(&command->list); fp@98: else if (command->state == EC_CMD_SENT) fp@98: commands_sent++; fp@98: } fp@113: fp@113: if (!commands_sent) break; fp@113: } fp@98: fp@98: // Zeit abgelaufen. Alle verbleibenden Kommandos entfernen. fp@98: list_for_each_entry_safe(command, next, &master->commands, list) { fp@98: switch (command->state) { fp@98: case EC_CMD_SENT: fp@98: case EC_CMD_QUEUED: fp@98: command->state = EC_CMD_TIMEOUT; fp@98: master->stats.timeouts++; fp@98: ec_master_output_stats(master); fp@98: break; fp@98: case EC_CMD_RECEIVED: fp@98: master->stats.delayed++; fp@98: ec_master_output_stats(master); fp@98: break; fp@98: default: fp@98: break; fp@98: } fp@98: list_del_init(&command->list); fp@98: } fp@98: } fp@98: fp@98: /*****************************************************************************/ fp@98: fp@98: /** fp@104: Sendet Kommandos asynchron. fp@104: */ fp@104: fp@104: void ecrt_master_async_send(ec_master_t *master) fp@104: { fp@104: ec_command_t *command, *next; fp@104: fp@104: ec_master_output_stats(master); fp@104: fp@104: if (unlikely(!master->device->link_state)) { fp@104: // Link DOWN, keines der Kommandos kann gesendet werden. fp@104: list_for_each_entry_safe(command, next, &master->commands, list) { fp@104: command->state = EC_CMD_ERROR; fp@104: list_del_init(&command->list); fp@104: } fp@104: fp@104: // Device-Zustand abfragen fp@104: ec_device_call_isr(master->device); fp@104: return; fp@104: } fp@104: fp@130: // Watch-Kommando hinzufügen fp@130: ec_command_init_brd(&master->watch_command, 0x130, 2); fp@130: ec_master_queue_command(master, &master->watch_command); fp@130: fp@104: // Rahmen senden fp@104: ec_master_send_commands(master); fp@104: } fp@104: fp@104: /*****************************************************************************/ fp@104: fp@104: /** fp@104: Empfängt Kommandos asynchron. fp@104: */ fp@104: fp@104: void ecrt_master_async_receive(ec_master_t *master) fp@104: { fp@104: ec_command_t *command, *next; fp@104: fp@104: ec_device_call_isr(master->device); fp@104: fp@104: // Alle empfangenen Kommandos aus der Liste entfernen fp@104: list_for_each_entry_safe(command, next, &master->commands, list) fp@104: if (command->state == EC_CMD_RECEIVED) fp@104: list_del_init(&command->list); fp@104: fp@104: // Alle verbleibenden Kommandos entfernen. fp@104: list_for_each_entry_safe(command, next, &master->commands, list) { fp@104: switch (command->state) { fp@104: case EC_CMD_SENT: fp@104: case EC_CMD_QUEUED: fp@104: command->state = EC_CMD_TIMEOUT; fp@104: master->stats.timeouts++; fp@104: ec_master_output_stats(master); fp@104: break; fp@104: default: fp@104: break; fp@104: } fp@104: list_del_init(&command->list); fp@104: } fp@130: fp@130: // Watch-Kommando verarbeiten fp@130: ec_master_process_watch_command(master); fp@130: fp@130: // Statistiken ausgeben fp@130: ec_master_output_stats(master); fp@104: } fp@104: fp@104: /*****************************************************************************/ fp@104: fp@104: /** fp@106: Bereitet Synchronen Datenverkehr vor. fp@106: fp@106: Fürgt einmal die Kommandos aller Domains zur Warteschlange hinzu, sendet fp@106: diese ab und wartet so lange, bis diese anschließend problemlos empfangen fp@106: werden können. fp@106: */ fp@106: fp@106: void ecrt_master_prepare_async_io(ec_master_t *master) fp@106: { fp@106: ec_domain_t *domain; fp@106: cycles_t t_start, t_end, t_timeout; fp@106: fp@106: // Alle empfangenen Kommandos aus der Liste entfernen fp@106: list_for_each_entry(domain, &master->domains, list) fp@106: ecrt_domain_queue(domain); fp@106: fp@106: ecrt_master_async_send(master); fp@106: fp@106: t_start = get_cycles(); // Sendezeit nehmen fp@120: t_timeout = (cycles_t) master->timeout * (cpu_khz / 1000); fp@106: fp@106: // Aktiv warten! fp@113: while (1) { fp@106: t_end = get_cycles(); fp@113: if (t_end - t_start >= t_timeout) break; fp@113: } fp@106: } fp@106: fp@106: /*****************************************************************************/ fp@106: fp@106: /** fp@56: Setzt die Debug-Ebene des Masters. fp@68: fp@98: Folgende Debug-Level sind definiert: fp@68: fp@68: - 1: Nur Positionsmarken in bestimmten Funktionen fp@68: - 2: Komplette Frame-Inhalte fp@68: */ fp@68: fp@104: void ecrt_master_debug(ec_master_t *master, /**< EtherCAT-Master */ fp@104: int level /**< Debug-Level */ fp@104: ) fp@73: { fp@98: if (level != master->debug_level) { fp@98: master->debug_level = level; fp@98: EC_INFO("Master debug level set to %i.\n", level); fp@98: } fp@73: } fp@73: fp@73: /*****************************************************************************/ fp@73: fp@73: /** fp@73: Gibt alle Informationen zum Master aus. fp@73: */ fp@73: fp@104: void ecrt_master_print(const ec_master_t *master /**< EtherCAT-Master */) fp@73: { fp@73: unsigned int i; fp@73: fp@84: EC_INFO("*** Begin master information ***\n"); fp@98: for (i = 0; i < master->slave_count; i++) fp@73: ec_slave_print(&master->slaves[i]); fp@84: EC_INFO("*** End master information ***\n"); fp@73: } fp@73: fp@73: /*****************************************************************************/ fp@73: fp@104: EXPORT_SYMBOL(ecrt_master_create_domain); fp@104: EXPORT_SYMBOL(ecrt_master_activate); fp@104: EXPORT_SYMBOL(ecrt_master_deactivate); fp@135: EXPORT_SYMBOL(ecrt_master_fetch_sdo_lists); fp@109: EXPORT_SYMBOL(ecrt_master_prepare_async_io); fp@104: EXPORT_SYMBOL(ecrt_master_sync_io); fp@104: EXPORT_SYMBOL(ecrt_master_async_send); fp@104: EXPORT_SYMBOL(ecrt_master_async_receive); fp@104: EXPORT_SYMBOL(ecrt_master_debug); fp@104: EXPORT_SYMBOL(ecrt_master_print); fp@138: EXPORT_SYMBOL(ecrt_master_get_slave); fp@42: fp@42: /*****************************************************************************/ fp@42: fp@42: /* Emacs-Konfiguration fp@42: ;;; Local Variables: *** fp@73: ;;; c-basic-offset:4 *** fp@42: ;;; End: *** fp@42: */