master/slave.c
changeset 73 9f4ea66d89a3
parent 55 059a9e712aa7
child 74 9bf603942791
--- a/master/slave.c	Wed Feb 22 17:36:28 2006 +0000
+++ b/master/slave.c	Thu Feb 23 09:58:50 2006 +0000
@@ -9,48 +9,431 @@
  *****************************************************************************/
 
 #include <linux/module.h>
+#include <linux/delay.h>
 
 #include "globals.h"
 #include "slave.h"
+#include "frame.h"
 
 /*****************************************************************************/
 
 /**
    EtherCAT-Slave-Konstruktor.
-
-   Initialisiert einen EtherCAT-Slave.
-
-   ACHTUNG! Dieser Konstruktor wird quasi nie aufgerufen. Bitte immer das
-   Makro ECAT_INIT_SLAVE() in ec_slave.h anpassen!
-
-   @param slave Zeiger auf den zu initialisierenden Slave
-*/
-
-void ec_slave_init(ec_slave_t *slave)
-{
-  slave->base_type = 0;
-  slave->base_revision = 0;
-  slave->base_build = 0;
-  slave->ring_position = 0;
-  slave->station_address = 0;
-  slave->sii_vendor_id = 0;
-  slave->sii_product_code = 0;
-  slave->sii_revision_number = 0;
-  slave->sii_serial_number = 0;
-  slave->type = NULL;
-  slave->logical_address = 0;
-  slave->process_data = NULL;
-  slave->private_data = NULL;
-  slave->configure = NULL;
-  slave->registered = 0;
-  slave->domain = 0;
-  slave->error_reported = 0;
+*/
+
+void ec_slave_init(ec_slave_t *slave, /**< EtherCAT-Slave */
+                   ec_master_t *master /**< EtherCAT-Master */
+                   )
+{
+    slave->master = master;
+    slave->base_type = 0;
+    slave->base_revision = 0;
+    slave->base_build = 0;
+    slave->base_fmmu_count = 0;
+    slave->base_sync_count = 0;
+    slave->ring_position = 0;
+    slave->station_address = 0;
+    slave->sii_vendor_id = 0;
+    slave->sii_product_code = 0;
+    slave->sii_revision_number = 0;
+    slave->sii_serial_number = 0;
+    slave->type = NULL;
+    slave->registered = 0;
+    slave->fmmu_count = 0;
+}
+
+/*****************************************************************************/
+
+/**
+   EtherCAT-Slave-Destruktor.
+*/
+
+void ec_slave_clear(ec_slave_t *slave /**< EtherCAT-Slave */)
+{
+    // Nichts freizugeben
+}
+
+/*****************************************************************************/
+
+/**
+   Liest alle benötigten Informationen aus einem Slave.
+*/
+
+int ec_slave_fetch(ec_slave_t *slave /**< EtherCAT-Slave */)
+{
+    ec_frame_t frame;
+
+    // Read base data
+    ec_frame_init_nprd(&frame, slave->master, slave->station_address,
+                       0x0000, 6);
+
+    if (unlikely(ec_frame_send_receive(&frame))) return -1;
+
+    if (unlikely(frame.working_counter != 1)) {
+        printk(KERN_ERR "EtherCAT: Slave %i did not respond while reading base"
+               " data!\n", slave->ring_position);
+        return -1;
+    }
+
+    slave->base_type = frame.data[0];
+    slave->base_revision = frame.data[1];
+    slave->base_build = frame.data[2] | (frame.data[3] << 8);
+    slave->base_fmmu_count = frame.data[4];
+    slave->base_sync_count = frame.data[5];
+
+    if (slave->base_fmmu_count > EC_MAX_FMMUS)
+        slave->base_fmmu_count = EC_MAX_FMMUS;
+
+    // Read identification from "Slave Information Interface" (SII)
+
+    if (unlikely(ec_slave_sii_read(slave, 0x0008, &slave->sii_vendor_id))) {
+        printk(KERN_ERR "EtherCAT: Could not read SII vendor id!\n");
+        return -1;
+    }
+
+    if (unlikely(ec_slave_sii_read(slave, 0x000A, &slave->sii_product_code))) {
+        printk(KERN_ERR "EtherCAT: Could not read SII product code!\n");
+        return -1;
+    }
+
+    if (unlikely(ec_slave_sii_read(slave, 0x000C,
+                                   &slave->sii_revision_number))) {
+        printk(KERN_ERR "EtherCAT: Could not read SII revision number!\n");
+        return -1;
+    }
+
+    if (unlikely(ec_slave_sii_read(slave, 0x000E,
+                                   &slave->sii_serial_number))) {
+        printk(KERN_ERR "EtherCAT: Could not read SII serial number!\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+/*****************************************************************************/
+
+/**
+   Liest Daten aus dem Slave-Information-Interface
+   eines EtherCAT-Slaves.
+
+   \return 0 bei Erfolg, sonst < 0
+*/
+
+int ec_slave_sii_read(ec_slave_t *slave,
+                      /**< EtherCAT-Slave */
+                      unsigned short int offset,
+                      /**< Adresse des zu lesenden SII-Registers */
+                      unsigned int *target
+                      /**< Zeiger auf einen 4 Byte großen Speicher zum Ablegen
+                         der Daten */
+                      )
+{
+    ec_frame_t frame;
+    unsigned char data[10];
+    unsigned int tries_left;
+
+    // Initiate read operation
+
+    data[0] = 0x00;
+    data[1] = 0x01;
+    data[2] = offset & 0xFF;
+    data[3] = (offset & 0xFF00) >> 8;
+    data[4] = 0x00;
+    data[5] = 0x00;
+
+    ec_frame_init_npwr(&frame, slave->master, slave->station_address, 0x502, 6,
+                       data);
+
+    if (unlikely(ec_frame_send_receive(&frame))) return -1;
+
+    if (unlikely(frame.working_counter != 1)) {
+        printk(KERN_ERR "EtherCAT: SII-read - Slave %i did not respond!\n",
+               slave->ring_position);
+        return -1;
+    }
+
+    // Der Slave legt die Informationen des Slave-Information-Interface
+    // in das Datenregister und löscht daraufhin ein Busy-Bit. Solange
+    // den Status auslesen, bis das Bit weg ist.
+
+    tries_left = 100;
+    while (likely(tries_left))
+    {
+        udelay(10);
+
+        ec_frame_init_nprd(&frame, slave->master, slave->station_address, 0x502,
+                           10);
+
+        if (unlikely(ec_frame_send_receive(&frame))) return -1;
+
+        if (unlikely(frame.working_counter != 1)) {
+            printk(KERN_ERR "EtherCAT: SII-read status -"
+                   " Slave %i did not respond!\n", slave->ring_position);
+            return -1;
+        }
+
+        if (likely((frame.data[1] & 0x81) == 0)) {
+            memcpy(target, frame.data + 6, 4);
+            break;
+        }
+
+        tries_left--;
+    }
+
+    if (unlikely(!tries_left)) {
+        printk(KERN_WARNING "EtherCAT: SSI-read. Slave %i timed out!\n",
+               slave->ring_position);
+        return -1;
+    }
+
+    return 0;
+}
+
+/*****************************************************************************/
+
+/**
+   Bestätigt einen Fehler beim Zustandswechsel.
+
+   FIXME Funktioniert noch nicht...
+*/
+
+void ec_slave_state_ack(ec_slave_t *slave,
+                        /**< Slave, dessen Zustand geändert werden soll */
+                        uint8_t state
+                        /**< Alter Zustand */
+                        )
+{
+    ec_frame_t frame;
+    unsigned char data[2];
+    unsigned int tries_left;
+
+    data[0] = state | EC_ACK;
+    data[1] = 0x00;
+
+    ec_frame_init_npwr(&frame, slave->master, slave->station_address, 0x0120,
+                       2, data);
+
+    if (unlikely(ec_frame_send_receive(&frame) != 0)) {
+        printk(KERN_ERR "EtherCAT: Could no acknowledge state %02X - Unable to"
+               " send!\n", state);
+        return;
+    }
+
+    if (unlikely(frame.working_counter != 1)) {
+        printk(KERN_ERR "EtherCAT: Could not acknowledge state %02X - Slave"
+               " %i did not respond!\n", state, slave->ring_position);
+        return;
+    }
+
+    tries_left = 100;
+    while (likely(tries_left))
+    {
+        udelay(10);
+
+        ec_frame_init_nprd(&frame, slave->master, slave->station_address,
+                           0x0130, 2);
+
+        if (unlikely(ec_frame_send_receive(&frame) != 0)) {
+            printk(KERN_ERR "EtherCAT: Could not check state acknowledgement"
+                   " %02X - Unable to send!\n", state);
+            return;
+        }
+
+        if (unlikely(frame.working_counter != 1)) {
+            printk(KERN_ERR "EtherCAT: Could not check state acknowledgement"
+                   " %02X - Slave %i did not respond!\n", state,
+                   slave->ring_position);
+            return;
+        }
+
+        if (unlikely(frame.data[0] != state)) {
+            printk(KERN_ERR "EtherCAT: Could not acknowledge state %02X on"
+                   " slave %i (code %02X)!\n", state, slave->ring_position,
+                   frame.data[0]);
+            return;
+        }
+
+        if (likely(frame.data[0] == state)) {
+            printk(KERN_INFO "EtherCAT: Acknowleged state %02X on slave %i.\n",
+                   state, slave->ring_position);
+            return;
+        }
+
+        tries_left--;
+    }
+
+    if (unlikely(!tries_left)) {
+        printk(KERN_ERR "EtherCAT: Could not check state acknowledgement %02X"
+               " of slave %i - Timeout while checking!\n", state,
+               slave->ring_position);
+        return;
+    }
+}
+
+/*****************************************************************************/
+
+/**
+   Ändert den Zustand eines Slaves.
+
+   \return 0 bei Erfolg, sonst < 0
+*/
+
+int ec_slave_state_change(ec_slave_t *slave,
+                          /**< Slave, dessen Zustand geändert werden soll */
+                          uint8_t state
+                          /**< Neuer Zustand */
+                          )
+{
+    ec_frame_t frame;
+    unsigned char data[2];
+    unsigned int tries_left;
+
+    data[0] = state;
+    data[1] = 0x00;
+
+    ec_frame_init_npwr(&frame, slave->master, slave->station_address, 0x0120,
+                       2, data);
+
+    if (unlikely(ec_frame_send_receive(&frame) != 0)) {
+        printk(KERN_ERR "EtherCAT: Could not set state %02X - Unable to"
+               " send!\n", state);
+        return -1;
+    }
+
+    if (unlikely(frame.working_counter != 1)) {
+        printk(KERN_ERR "EtherCAT: Could not set state %02X - Slave %i did not"
+               " respond!\n", state, slave->ring_position);
+        return -1;
+    }
+
+    tries_left = 100;
+    while (likely(tries_left))
+    {
+        udelay(10);
+
+        ec_frame_init_nprd(&frame, slave->master, slave->station_address,
+                           0x0130, 2);
+
+        if (unlikely(ec_frame_send_receive(&frame) != 0)) {
+            printk(KERN_ERR "EtherCAT: Could not check state %02X - Unable to"
+                   " send!\n", state);
+            return -1;
+        }
+
+        if (unlikely(frame.working_counter != 1)) {
+            printk(KERN_ERR "EtherCAT: Could not check state %02X - Slave %i"
+                   " did not respond!\n", state, slave->ring_position);
+            return -1;
+        }
+
+        if (unlikely(frame.data[0] & 0x10)) { // State change error
+            printk(KERN_ERR "EtherCAT: Could not set state %02X - Slave %i"
+                   " refused state change (code %02X)!\n", state,
+                   slave->ring_position, frame.data[0]);
+            ec_slave_state_ack(slave, frame.data[0] & 0x0F);
+            return -1;
+        }
+
+        if (likely(frame.data[0] == (state & 0x0F))) {
+            // State change successful
+            break;
+        }
+
+        tries_left--;
+    }
+
+    if (unlikely(!tries_left)) {
+        printk(KERN_ERR "EtherCAT: Could not check state %02X of slave %i -"
+               " Timeout while checking!\n", state,
+               slave->ring_position);
+        return -1;
+    }
+
+    return 0;
+}
+
+/*****************************************************************************/
+
+/**
+   Merkt eine FMMU-Konfiguration vor.
+
+   Die FMMU wird so konfiguriert, dass sie den gesamten Datenbereich des
+   entsprechenden Sync-Managers abdeckt. Für jede Domäne werden separate
+   FMMUs konfiguriert.
+
+   Wenn die entsprechende FMMU bereits konfiguriert ist, wird dies als
+   Erfolg zurückgegeben.
+
+   \return 0 bei Erfolg, sonst < 0
+*/
+
+int ec_slave_set_fmmu(ec_slave_t *slave, /**< EtherCAT-Slave */
+                      const ec_domain_t *domain, /**< Domäne */
+                      const ec_sync_t *sync  /**< Sync-Manager */
+                      )
+{
+    unsigned int i;
+
+    // FMMU schon vorgemerkt?
+    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) {
+        printk(KERN_ERR "EtherCAT: Slave %i supports only %i FMMUs.\n",
+               slave->ring_position, slave->base_fmmu_count);
+        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;
+}
+
+/*****************************************************************************/
+
+/**
+   Gibt alle Informationen über einen EtherCAT-Slave aus.
+*/
+
+void ec_slave_print(const ec_slave_t *slave /**< EtherCAT-Slave */)
+{
+    printk(KERN_INFO "--- EtherCAT slave information ---\n");
+
+    if (slave->type) {
+        printk(KERN_INFO "  Vendor \"%s\", Product \"%s\": %s\n",
+               slave->type->vendor_name, slave->type->product_name,
+               slave->type->description);
+    }
+    else {
+        printk(KERN_INFO "  *** This slave has no type information! ***\n");
+    }
+
+    printk(KERN_INFO "  Ring position: %i, Station address: 0x%04X\n",
+           slave->ring_position, slave->station_address);
+
+    printk(KERN_INFO "  Base information:\n");
+    printk(KERN_INFO "    Type %u, Revision %i, Build %i\n",
+           slave->base_type, slave->base_revision, slave->base_build);
+    printk(KERN_INFO "    Supported FMMUs: %i, Sync managers: %i\n",
+           slave->base_fmmu_count, slave->base_sync_count);
+
+    printk(KERN_INFO "  Slave information interface:\n");
+    printk(KERN_INFO "    Vendor-ID: 0x%08X, Product code: 0x%08X\n",
+           slave->sii_vendor_id, slave->sii_product_code);
+    printk(KERN_INFO "    Revision number: 0x%08X, Serial number: 0x%08X\n",
+           slave->sii_revision_number, slave->sii_serial_number);
 }
 
 /*****************************************************************************/
 
 /* Emacs-Konfiguration
 ;;; Local Variables: ***
-;;; c-basic-offset:2 ***
+;;; c-basic-offset:4 ***
 ;;; End: ***
 */