master/module.c
changeset 573 cdee4ea90ce9
parent 572 aef7ea866a41
child 575 9a2121b500b1
--- a/master/module.c	Fri Feb 16 13:30:46 2007 +0000
+++ b/master/module.c	Fri Feb 16 17:13:39 2007 +0000
@@ -54,8 +54,12 @@
 
 /*****************************************************************************/
 
-static int ec_master_count = 1; /**< parameter value, number of masters */
-static struct list_head ec_masters; /**< list of masters */
+static char *main; /**< main devices parameter */
+static char *backup; /**< backup devices parameter */
+
+static LIST_HEAD(main_device_ids); /**< list of main device IDs */
+static LIST_HEAD(backup_device_ids); /**< list of main device IDs */
+static LIST_HEAD(masters); /**< list of masters */
 static dev_t device_number; /**< XML character device number */
 ec_xmldev_t xmldev; /**< XML character device */
 
@@ -65,18 +69,171 @@
 
 /** \cond */
 
-module_param(ec_master_count, int, S_IRUGO);
-
 MODULE_AUTHOR("Florian Pose <fp@igh-essen.com>");
 MODULE_DESCRIPTION("EtherCAT master driver module");
 MODULE_LICENSE("GPL");
 MODULE_VERSION(EC_MASTER_VERSION);
-MODULE_PARM_DESC(ec_master_count, "number of EtherCAT masters to initialize");
+
+module_param(main, charp, S_IRUGO);
+MODULE_PARM_DESC(main, "main device IDs");
+module_param(backup, charp, S_IRUGO);
+MODULE_PARM_DESC(backup, "backup device IDs");
 
 /** \endcond */
 
 /*****************************************************************************/
 
+void clear_device_ids(struct list_head *device_ids)
+{
+    ec_device_id_t *dev_id, *next_dev_id;
+    
+    list_for_each_entry_safe(dev_id, next_dev_id, device_ids, list) {
+        list_del(&dev_id->list);
+        kfree(dev_id);
+    }
+}
+
+/*****************************************************************************/
+
+static int parse_device_id_mac(ec_device_id_t *dev_id,
+        const char *src, const char **remainder)
+{
+    unsigned int i, value;
+    char *rem;
+
+    for (i = 0; i < ETH_ALEN; i++) {
+        value = simple_strtoul(src, &rem, 16);
+        if (rem != src + 2
+                || value > 0xFF
+                || (i < ETH_ALEN - 1 && *rem != ':')) {
+            return -1;
+        }
+        dev_id->octets[i] = value;
+        if (i < ETH_ALEN - 1)
+            src = rem + 1;
+    }
+
+    dev_id->type = ec_device_id_mac;
+    *remainder = rem;
+    return 0;
+}
+
+/*****************************************************************************/
+
+static int parse_device_ids(struct list_head *device_ids, const char *src)
+{
+    const char *rem;
+    ec_device_id_t *dev_id;
+    unsigned int index = 0;
+
+    while (*src) {
+        // allocate new device ID
+        if (!(dev_id = kmalloc(sizeof(ec_device_id_t), GFP_KERNEL))) {
+            EC_ERR("Out of memory!\n");
+            goto out_free;
+        }
+        
+        if (*src == ';') { // empty device ID
+            dev_id->type = ec_device_id_empty;
+        }
+        else if (*src == 'M') {
+            src++;
+            if (parse_device_id_mac(dev_id, src, &rem)) {
+                EC_ERR("Device ID %u: Invalid MAC syntax!\n", index);
+                kfree(dev_id);
+                goto out_free;
+            }
+            src = rem;
+        }
+        else {
+            EC_ERR("Device ID %u: Unknown format \'%c\'!\n", index, *src);
+            kfree(dev_id);
+            goto out_free;
+        }
+        
+        list_add_tail(&dev_id->list, device_ids); 
+        if (*src) {
+            if (*src != ';') {
+                EC_ERR("Invalid delimiter '%c' after device ID %i!\n",
+                        *src, index);
+                goto out_free;
+            }
+            src++; // skip delimiter
+        }
+        index++;
+    }
+
+    return 0;
+
+out_free:
+    clear_device_ids(device_ids);
+    return -1;
+}
+
+/*****************************************************************************/
+
+static int create_device_ids(void)
+{
+    ec_device_id_t *id;
+    unsigned int main_count = 0, backup_count = 0;
+    
+    if (parse_device_ids(&main_device_ids, main))
+        return -1;
+
+    if (parse_device_ids(&backup_device_ids, main))
+        return -1;
+
+    // count main device IDs and check for empty ones
+    list_for_each_entry(id, &main_device_ids, list) {
+        if (id->type == ec_device_id_empty) {
+            EC_ERR("Main device IDs may not be empty!\n");
+            return -1;
+        }
+        main_count++;
+    }
+
+    // count backup device IDs
+    list_for_each_entry(id, &backup_device_ids, list) {
+        backup_count++;
+    }
+
+    // fill up backup device IDs
+    while (backup_count < main_count) {
+        if (!(id = kmalloc(sizeof(ec_device_id_t), GFP_KERNEL))) {
+            EC_ERR("Out of memory!\n");
+            return -1;
+        }
+        
+        id->type = ec_device_id_empty;
+        list_add_tail(&id->list, &backup_device_ids);
+        backup_count++;
+    }
+
+    return 0;
+}
+
+/*****************************************************************************/
+
+static int device_id_check(const ec_device_id_t *dev_id,
+        const struct net_device *dev, const char *driver_name,
+        unsigned int device_index)
+{
+    unsigned int i;
+    
+    switch (dev_id->type) {
+        case ec_device_id_mac:
+            for (i = 0; i < ETH_ALEN; i++)
+                if (dev->dev_addr[i] != dev_id->octets[i])
+                    return 0;
+            return 1;
+        default:
+            return 0;
+    }
+}
+                
+
+/*****************************************************************************/
+
 /**
    Module initialization.
    Initializes \a ec_master_count masters.
@@ -85,48 +242,68 @@
 
 int __init ec_init_module(void)
 {
-    unsigned int i;
     ec_master_t *master, *next;
+    ec_device_id_t *main_dev_id, *backup_dev_id;
+    unsigned int master_index = 0;
 
     EC_INFO("Master driver %s\n", EC_MASTER_VERSION);
 
-    if (ec_master_count < 1) {
-        EC_ERR("Invalid ec_master_count: %i\n", ec_master_count);
+    if (alloc_chrdev_region(&device_number, 0, 1, "EtherCAT")) {
+        EC_ERR("Failed to obtain device number!\n");
         goto out_return;
     }
 
-    if (alloc_chrdev_region(&device_number, 0, ec_master_count, "EtherCAT")) {
-        EC_ERR("Failed to allocate device number!\n");
-        goto out_return;
-    }
-
-    EC_INFO("Initializing %i EtherCAT master(s)...\n", ec_master_count);
-
-    INIT_LIST_HEAD(&ec_masters);
-
-    for (i = 0; i < ec_master_count; i++) {
-        if (!(master =
-              (ec_master_t *) kmalloc(sizeof(ec_master_t), GFP_KERNEL))) {
-            EC_ERR("Failed to allocate memory for EtherCAT master %i.\n", i);
-            goto out_free;
-        }
-
-        if (ec_master_init(master, i, 0))
-            goto out_free;
-
-        list_add_tail(&master->list, &ec_masters);
-    }
-
-    EC_INFO("Master driver initialized.\n");
+    if (create_device_ids())
+        goto out_free_ids;
+    
+    if (!list_empty(&main_device_ids)) {
+        main_dev_id =
+            list_entry(main_device_ids.next, ec_device_id_t, list);
+        backup_dev_id =
+            list_entry(backup_device_ids.next, ec_device_id_t, list);
+        
+        while (1) {
+            if (!(master = (ec_master_t *)
+                        kmalloc(sizeof(ec_master_t), GFP_KERNEL))) {
+                EC_ERR("Failed to allocate memory for EtherCAT master %i.\n",
+                        master_index);
+                goto out_free_masters;
+            }
+
+            if (ec_master_init(master, master_index,
+                        main_dev_id, backup_dev_id, 0))
+                goto out_free_masters;
+
+            list_add_tail(&master->list, &masters);
+            master_index++;
+
+            // last device IDs?
+            if (main_dev_id->list.next == &main_device_ids)
+                break;
+            
+            // next device IDs
+            main_dev_id =
+                list_entry(main_dev_id->list.next, ec_device_id_t, list);
+            backup_dev_id =
+                list_entry(backup_dev_id->list.next, ec_device_id_t, list);
+        }
+    }
+    
+    EC_INFO("%u master%s waiting for devices.\n",
+            master_index, (master_index == 1 ? "" : "s"));
     return 0;
 
- out_free:
-    list_for_each_entry_safe(master, next, &ec_masters, list) {
+out_free_masters:
+    list_for_each_entry_safe(master, next, &masters, list) {
         list_del(&master->list);
         kobject_del(&master->kobj);
         kobject_put(&master->kobj);
     }
- out_return:
+out_free_ids:
+    clear_device_ids(&main_device_ids);
+    clear_device_ids(&backup_device_ids);
+    unregister_chrdev_region(device_number, 1);
+out_return:
     return -1;
 }
 
@@ -143,12 +320,12 @@
 
     EC_INFO("Cleaning up master driver...\n");
 
-    list_for_each_entry_safe(master, next, &ec_masters, list) {
+    list_for_each_entry_safe(master, next, &masters, list) {
         list_del(&master->list);
         ec_master_destroy(master);
     }
 
-    unregister_chrdev_region(device_number, ec_master_count);
+    unregister_chrdev_region(device_number, 1);
 
     EC_INFO("Master driver cleaned up.\n");
 }
@@ -164,7 +341,7 @@
 {
     ec_master_t *master;
 
-    list_for_each_entry(master, &ec_masters, list) {
+    list_for_each_entry(master, &masters, list) {
         if (master->index == master_index) return master;
     }
 
@@ -270,48 +447,71 @@
  *****************************************************************************/
 
 /**
-   Connects an EtherCAT device to a certain master.
-   The master will use the device for sending and receiving frames. It is
-   required that no other instance (for example the kernel IP stack) uses
-   the device.
+   Offers an EtherCAT device to a certain master.
+   The master decides, if it wants to use the device for EtherCAT operation,
+   or not. It is important, that the offered net_device is not used by
+   the kernel IP stack. If the master, accepted the offer, the address of
+   the newly created EtherCAT device is written to the ecdev pointer, else
+   the pointer is written to zero.
    \return 0 on success, else < 0
    \ingroup DeviceInterface
 */
 
-ec_device_t *ecdev_register(unsigned int master_index, /**< master index */
-                            struct net_device *net_dev, /**< net_device of
-                                                           the device */
-                            ec_pollfunc_t poll, /**< device poll function */
-                            struct module *module /**< pointer to the module */
-                            )
+int ecdev_offer(struct net_device *net_dev, /**< net_device to offer */
+        ec_device_t **ecdev, /**< pointer to store a device on success */
+        const char *driver_name, /**< name of the network driver */
+        unsigned int device_index, /**< index of the supported device */
+        ec_pollfunc_t poll, /**< device poll function */
+        struct module *module /**< pointer to the module */
+        )
 {
     ec_master_t *master;
-
-    if (!(master = ec_find_master(master_index))) return NULL;
-
-    if (down_interruptible(&master->device_sem)) {
-        EC_ERR("Interrupted while waiting for device!\n");
-        goto out_return;
-    }
-
-    if (master->device) {
-        EC_ERR("Master %i already has a device!\n", master_index);
-        goto out_up;
-    }
-
-    if (!(master->device =
-          (ec_device_t *) kmalloc(sizeof(ec_device_t), GFP_KERNEL))) {
-        EC_ERR("Failed to allocate device!\n");
-        goto out_up;
-    }
-
-    if (ec_device_init(master->device, master, net_dev, poll, module)) {
-        EC_ERR("Failed to init device!\n");
-        goto out_free;
-    }
-
-    up(&master->device_sem);
-    return master->device;
+    unsigned int i;
+
+    list_for_each_entry(master, &masters, list) {
+        if (down_interruptible(&master->device_sem)) {
+            EC_ERR("Interrupted while waiting for device semaphore!\n");
+            goto out_return;
+        }
+
+        if (device_id_check(master->main_device_id, net_dev,
+                    driver_name, device_index)) {
+
+            EC_INFO("Accepting device %s:%u (", driver_name, device_index);
+            for (i = 0; i < ETH_ALEN; i++) {
+                printk("%02X", net_dev->dev_addr[i]);
+                if (i < ETH_ALEN - 1) printk(":");
+            }
+            printk(") for master %u.\n", master->index);
+
+            if (master->device) {
+                EC_ERR("Master already has a device.\n");
+                goto out_up;
+            }
+            
+            if (!(master->device = (ec_device_t *)
+                        kmalloc(sizeof(ec_device_t), GFP_KERNEL))) {
+                EC_ERR("Failed to allocate device!\n");
+                goto out_up;
+            }
+
+            if (ec_device_init(master->device, master,
+                        net_dev, poll, module)) {
+                EC_ERR("Failed to init device!\n");
+                goto out_free;
+            }
+
+            up(&master->device_sem);
+            sprintf(net_dev->name, "ec%u", master->index);
+            *ecdev = master->device; // offer accepted
+            return 0; // no error
+        }
+
+        up(&master->device_sem);
+    }
+
+    *ecdev = NULL; // offer declined
+    return 0; // no error
 
  out_free:
     kfree(master->device);
@@ -319,13 +519,13 @@
  out_up:
     up(&master->device_sem);
  out_return:
-    return NULL;
-}
-
-/*****************************************************************************/
-
-/**
-   Disconnect an EtherCAT device from the master.
+    return 1;
+}
+
+/*****************************************************************************/
+
+/**
+   Withdraws an EtherCAT device from the master.
    The device is disconnected from the master and all device ressources
    are freed.
    \attention Before calling this function, the ecdev_stop() function has
@@ -333,26 +533,24 @@
    \ingroup DeviceInterface
 */
 
-void ecdev_unregister(unsigned int master_index, /**< master index */
-                      ec_device_t *device /**< EtherCAT device */
-                      )
-{
-    ec_master_t *master;
-
-    if (!(master = ec_find_master(master_index))) return;
+void ecdev_withdraw(ec_device_t *device /**< EtherCAT device */)
+{
+    ec_master_t *master = device->master;
+    unsigned int i;
 
     down(&master->device_sem);
-
-    if (!master->device || master->device != device) {
-        up(&master->device_sem);
-        EC_WARN("Unable to unregister device!\n");
-        return;
-    }
-
+    
+    EC_INFO("Master %u releasing device ", master->index);
+    for (i = 0; i < ETH_ALEN; i++) {
+        printk("%02X", device->dev->dev_addr[i]);
+        if (i < ETH_ALEN - 1) printk(":");
+    }
+    printk(".\n");
+    
     ec_device_clear(master->device);
     kfree(master->device);
     master->device = NULL;
-
+    
     up(&master->device_sem);
 }
 
@@ -505,8 +703,8 @@
 module_init(ec_init_module);
 module_exit(ec_cleanup_module);
 
-EXPORT_SYMBOL(ecdev_register);
-EXPORT_SYMBOL(ecdev_unregister);
+EXPORT_SYMBOL(ecdev_offer);
+EXPORT_SYMBOL(ecdev_withdraw);
 EXPORT_SYMBOL(ecdev_open);
 EXPORT_SYMBOL(ecdev_close);
 EXPORT_SYMBOL(ecrt_request_master);