master/module.c
changeset 2589 2b9c78543663
parent 2411 01370d59233d
--- a/master/module.c	Thu Sep 06 14:21:02 2012 +0200
+++ b/master/module.c	Mon Nov 03 15:20:05 2014 +0100
@@ -61,7 +61,7 @@
 static unsigned int debug_level;  /**< Debug level parameter. */
 
 static ec_master_t *masters; /**< Array of masters. */
-static struct ec_mutex_t master_mutex; /**< Master mutex. */
+static struct semaphore master_sem; /**< Master semaphore. */
 
 dev_t device_number; /**< Device number for master cdevs. */
 struct class *class; /**< Device class. */
@@ -101,7 +101,7 @@
 
     EC_INFO("Master driver %s\n", EC_MASTER_VERSION);
 
-    ec_mutex_init(&master_mutex);
+    sema_init(&master_sem, 1);
 
     if (master_count) {
         if (alloc_chrdev_region(&device_number,
@@ -229,13 +229,19 @@
 
 /*****************************************************************************/
 
+/** Maximum MAC string size.
+ */
+#define EC_MAX_MAC_STRING_SIZE (3 * ETH_ALEN)
+
 /** Print a MAC address to a buffer.
  *
+ * The buffer size must be at least EC_MAX_MAC_STRING_SIZE.
+ *
  * \return number of bytes written.
  */
 ssize_t ec_mac_print(
         const uint8_t *mac, /**< MAC address */
-        char *buffer /**< target buffer */
+        char *buffer /**< Target buffer. */
         )
 {
     off_t off = 0;
@@ -289,7 +295,7 @@
 
 /** Parse a MAC address from a string.
  *
- * The MAC address must follow the regexp
+ * The MAC address must match the regular expression
  * "([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}".
  *
  * \return 0 on success, else < 0
@@ -303,8 +309,7 @@
     if (!strlen(src)) {
         if (allow_empty){
             return 0;
-        }
-        else {
+        } else {
             EC_ERR("MAC address may not be empty.\n");
             return -EINVAL;
         }
@@ -319,8 +324,9 @@
             return -EINVAL;
         }
         mac[i] = value;
-        if (i < ETH_ALEN - 1)
+        if (i < ETH_ALEN - 1) {
             src = rem + 1; // skip colon
+        }
     }
 
     return 0;
@@ -382,6 +388,8 @@
 /*****************************************************************************/
 
 /** Prints slave states in clear text.
+ *
+ * \return Size of the created string.
  */
 size_t ec_state_string(uint8_t states, /**< slave states */
                        char *buffer, /**< target buffer
@@ -445,6 +453,13 @@
  *  Device interface
  *****************************************************************************/
 
+/** Device names.
+ */
+const char *ec_device_names[2] = {
+    "main",
+    "backup"
+};
+
 /** Offers an EtherCAT device to a certain master.
  *
  * The master decides, if it wants to use the device for EtherCAT operation,
@@ -462,58 +477,62 @@
         )
 {
     ec_master_t *master;
-    char str[20];
-    unsigned int i;
+    char str[EC_MAX_MAC_STRING_SIZE];
+    unsigned int i, dev_idx;
 
     for (i = 0; i < master_count; i++) {
         master = &masters[i];
-
-        ec_mutex_lock(&master->device_mutex);
-        if (master->main_device.dev) { // master already has a device
-            ec_mutex_unlock(&master->device_mutex);
-            continue;
-        }
-
-        if (ec_mac_equal(master->main_mac, net_dev->dev_addr)
-                || ec_mac_is_broadcast(master->main_mac)) {
-            ec_mac_print(net_dev->dev_addr, str);
-            EC_INFO("Accepting device %s for master %u.\n",
-                    str, master->index);
-
-            ec_device_attach(&master->main_device, net_dev, poll, module);
-            ec_mutex_unlock(&master->device_mutex);
-
-            snprintf(net_dev->name, IFNAMSIZ, "ec%u", master->index);
-
-            return &master->main_device; // offer accepted
-        }
-        else {
-            ec_mutex_unlock(&master->device_mutex);
-
-            if (master->debug_level) {
-                ec_mac_print(net_dev->dev_addr, str);
-                EC_MASTER_DBG(master, 0, "Master declined device %s.\n",
-                        str);
+        ec_mac_print(net_dev->dev_addr, str);
+
+        if (down_interruptible(&master->device_sem)) {
+            EC_MASTER_WARN(master, "%s() interrupted!\n", __func__);
+            return NULL;
+        }
+
+        for (dev_idx = EC_DEVICE_MAIN;
+                dev_idx < ec_master_num_devices(master); dev_idx++) {
+            if (!master->devices[dev_idx].dev
+                && (ec_mac_equal(master->macs[dev_idx], net_dev->dev_addr)
+                    || ec_mac_is_broadcast(master->macs[dev_idx]))) {
+
+                EC_INFO("Accepting %s as %s device for master %u.\n",
+                        str, ec_device_names[dev_idx != 0], master->index);
+
+                ec_device_attach(&master->devices[dev_idx],
+                        net_dev, poll, module);
+                up(&master->device_sem);
+
+                snprintf(net_dev->name, IFNAMSIZ, "ec%c%u",
+                        ec_device_names[dev_idx != 0][0], master->index);
+
+                return &master->devices[dev_idx]; // offer accepted
             }
         }
+
+        up(&master->device_sem);
+
+        EC_MASTER_DBG(master, 1, "Master declined device %s.\n", str);
     }
 
     return NULL; // offer declined
 }
 
 /******************************************************************************
- *  Realtime interface
+ * Application interface
  *****************************************************************************/
 
 /** Request a master.
  *
  * Same as ecrt_request_master(), but with ERR_PTR() return value.
+ *
+ * \return Requested master.
  */
 ec_master_t *ecrt_request_master_err(
         unsigned int master_index /**< Master index. */
         )
 {
     ec_master_t *master, *errptr = NULL;
+    unsigned int dev_idx = EC_DEVICE_MAIN;
 
     EC_INFO("Requesting master %u...\n", master_index);
 
@@ -524,40 +543,43 @@
     }
     master = &masters[master_index];
 
-    if (ec_mutex_lock_interruptible(&master_mutex)) {
+    if (down_interruptible(&master_sem)) {
         errptr = ERR_PTR(-EINTR);
         goto out_return;
     }
 
     if (master->reserved) {
-        ec_mutex_unlock(&master_mutex);
+        up(&master_sem);
         EC_MASTER_ERR(master, "Master already in use!\n");
         errptr = ERR_PTR(-EBUSY);
         goto out_return;
     }
     master->reserved = 1;
-    ec_mutex_unlock(&master_mutex);
-
-    if (ec_mutex_lock_interruptible(&master->device_mutex)) {
+    up(&master_sem);
+
+    if (down_interruptible(&master->device_sem)) {
         errptr = ERR_PTR(-EINTR);
         goto out_release;
     }
 
     if (master->phase != EC_IDLE) {
-        ec_mutex_unlock(&master->device_mutex);
+        up(&master->device_sem);
         EC_MASTER_ERR(master, "Master still waiting for devices!\n");
         errptr = ERR_PTR(-ENODEV);
         goto out_release;
     }
 
-    if (!try_module_get(master->main_device.module)) {
-        ec_mutex_unlock(&master->device_mutex);
-        EC_ERR("Device module is unloading!\n");
-        errptr = ERR_PTR(-ENODEV);
-        goto out_release;
-    }
-
-    ec_mutex_unlock(&master->device_mutex);
+    for (; dev_idx < ec_master_num_devices(master); dev_idx++) {
+        ec_device_t *device = &master->devices[dev_idx];
+        if (!try_module_get(device->module)) {
+            up(&master->device_sem);
+            EC_MASTER_ERR(master, "Device module is unloading!\n");
+            errptr = ERR_PTR(-ENODEV);
+            goto out_module_put;
+        }
+    }
+
+    up(&master->device_sem);
 
     if (ec_master_enter_operation_phase(master)) {
         EC_MASTER_ERR(master, "Failed to enter OPERATION phase!\n");
@@ -569,7 +591,10 @@
     return master;
 
  out_module_put:
-    module_put(master->main_device.module);
+    for (; dev_idx > 0; dev_idx--) {
+        ec_device_t *device = &master->devices[dev_idx - 1];
+        module_put(device->module);
+    }
  out_release:
     master->reserved = 0;
  out_return:
@@ -588,6 +613,8 @@
 
 void ecrt_release_master(ec_master_t *master)
 {
+    unsigned int dev_idx;
+
     EC_MASTER_INFO(master, "Releasing master...\n");
 
     if (!master->reserved) {
@@ -598,7 +625,11 @@
 
     ec_master_leave_operation_phase(master);
 
-    module_put(master->main_device.module);
+    for (dev_idx = EC_DEVICE_MAIN; dev_idx < ec_master_num_devices(master);
+            dev_idx++) {
+        module_put(master->devices[dev_idx].module);
+    }
+
     master->reserved = 0;
 
     EC_MASTER_INFO(master, "Released.\n");
@@ -613,32 +644,6 @@
 
 /*****************************************************************************/
 
-/** Return pointer to running master
- */
-ec_master_t *ecrt_attach_master(unsigned int master_index)
-{
-    ec_master_t *master = NULL;
-
-    EC_INFO("Requesting master %u...\n", master_index);
-
-    if (master_index >= master_count) {
-        EC_ERR("Invalid master index %u.\n", master_index);
-        return master;
-    }
-
-    master = &masters[master_index];
-    if (master->reserved) {
-       // ok master is attached
-        EC_INFO("attaching Master %u!\n", master_index);
-    } else {
-        EC_ERR("No Master %u in use!\n", master_index);
-        master = NULL;
-    }
-    return master;
-}
-
-/*****************************************************************************/
-
 /** Global request state type translation table.
  *
  * Translates an internal request state to an external one.
@@ -663,7 +668,6 @@
 EXPORT_SYMBOL(ecrt_request_master);
 EXPORT_SYMBOL(ecrt_release_master);
 EXPORT_SYMBOL(ecrt_version_magic);
-EXPORT_SYMBOL(ecrt_attach_master);
 
 /** \endcond */