MERGE branches/sysfs -> trunk (whole SysFS implementation)
authorFlorian Pose <fp@igh-essen.com>
Mon, 10 Apr 2006 14:25:02 +0000
changeset 178 b84f69db8566
parent 177 482200a0659f
child 179 fb4c9dd11ca0
MERGE branches/sysfs -> trunk (whole SysFS implementation)
master/domain.c
master/domain.h
master/master.c
master/master.h
master/module.c
--- a/master/domain.c	Mon Apr 10 11:18:52 2006 +0000
+++ b/master/domain.c	Mon Apr 10 14:25:02 2006 +0000
@@ -12,25 +12,65 @@
 #include "domain.h"
 #include "master.h"
 
+/*****************************************************************************/
+
 void ec_domain_clear_field_regs(ec_domain_t *);
 
 /*****************************************************************************/
 
+static struct attribute attr_data_size = {
+    .name = "data_size",
+    .owner = THIS_MODULE,
+    .mode = S_IRUGO
+};
+
+static struct attribute *def_attrs[] = {
+    &attr_data_size,
+    NULL,
+};
+
+static struct sysfs_ops sysfs_ops = {
+    .show = &ec_show_domain_attribute,
+    .store = NULL
+};
+
+static struct kobj_type ktype_ec_domain = {
+    .release = ec_domain_clear,
+    .sysfs_ops = &sysfs_ops,
+    .default_attrs = def_attrs
+};
+
+/*****************************************************************************/
+
 /**
    Konstruktor einer EtherCAT-Domäne.
 */
 
-void ec_domain_init(ec_domain_t *domain, /**< Domäne */
-                    ec_master_t *master /**< Zugehöriger Master */
-                    )
+int ec_domain_init(ec_domain_t *domain, /**< Domäne */
+                   ec_master_t *master, /**< Zugehöriger Master */
+                   unsigned int index /**< Domänen-Index */
+                   )
 {
     domain->master = master;
+    domain->index = index;
     domain->data_size = 0;
     domain->base_address = 0;
     domain->response_count = 0xFFFFFFFF;
 
     INIT_LIST_HEAD(&domain->field_regs);
     INIT_LIST_HEAD(&domain->commands);
+
+    // Init kobject and add it to the hierarchy
+    memset(&domain->kobj, 0x00, sizeof(struct kobject));
+    kobject_init(&domain->kobj);
+    domain->kobj.ktype = &ktype_ec_domain;
+    domain->kobj.parent = &master->kobj;
+    if (kobject_set_name(&domain->kobj, "domain%i", index)) {
+        EC_ERR("Failed to set kobj name.\n");
+        return -1;
+    }
+
+    return 0;
 }
 
 /*****************************************************************************/
@@ -39,9 +79,14 @@
    Destruktor einer EtherCAT-Domäne.
 */
 
-void ec_domain_clear(ec_domain_t *domain /**< Domäne */)
+void ec_domain_clear(struct kobject *kobj /**< Kobject der Domäne */)
 {
     ec_command_t *command, *next;
+    ec_domain_t *domain;
+
+    domain = container_of(kobj, ec_domain_t, kobj);
+
+    EC_INFO("Clearing domain %i.\n", domain->index);
 
     list_for_each_entry_safe(command, next, &domain->commands, list) {
         ec_command_clear(command);
@@ -49,6 +94,8 @@
     }
 
     ec_domain_clear_field_regs(domain);
+
+    kfree(domain);
 }
 
 /*****************************************************************************/
@@ -192,7 +239,7 @@
     }
 
     if (!cmd_count) {
-        EC_WARN("Domain 0x%08X contains no data!\n", (u32) domain);
+        EC_WARN("Domain %i contains no data!\n", domain->index);
         ec_domain_clear_field_regs(domain);
         return 0;
     }
@@ -221,8 +268,9 @@
         }
     }
 
-    EC_INFO("Domain %X - Allocated %i bytes in %i command(s)\n",
-            (u32) domain, domain->data_size, cmd_count);
+    EC_INFO("Domain %i - Allocated %i bytes in %i command%s\n",
+            domain->index, domain->data_size, cmd_count,
+            cmd_count == 1 ? "" : "s");
 
     ec_domain_clear_field_regs(domain);
 
@@ -241,9 +289,30 @@
 {
     if (count != domain->response_count) {
         domain->response_count = count;
-        EC_INFO("Domain 0x%08X working counter change: %i\n",
-                (u32) domain, count);
-    }
+        EC_INFO("Domain %i working counter change: %i\n", domain->index, count);
+    }
+}
+
+/*****************************************************************************/
+
+/**
+   Formatiert Attribut-Daten für lesenden Zugriff im SysFS
+
+   \return Anzahl Bytes im Speicher
+*/
+
+ssize_t ec_show_domain_attribute(struct kobject *kobj, /**< KObject */
+                                 struct attribute *attr, /**< Attribut */
+                                 char *buffer /**< Speicher für die Daten */
+                                 )
+{
+    ec_domain_t *domain = container_of(kobj, ec_domain_t, kobj);
+
+    if (attr == &attr_data_size) {
+        return sprintf(buffer, "%i\n", domain->data_size);
+    }
+
+    return 0;
 }
 
 /******************************************************************************
--- a/master/domain.h	Mon Apr 10 11:18:52 2006 +0000
+++ b/master/domain.h	Mon Apr 10 14:25:02 2006 +0000
@@ -12,6 +12,7 @@
 #define _EC_DOMAIN_H_
 
 #include <linux/list.h>
+#include <linux/kobject.h>
 
 #include "globals.h"
 #include "slave.h"
@@ -44,7 +45,9 @@
 
 struct ec_domain
 {
+    struct kobject kobj; /**< Kobject der Domäne */
     struct list_head list; /**< Listenkopf */
+    unsigned int index; /**< Domänen-Index */
     ec_master_t *master; /**< EtherCAT-Master, zu der die Domäne gehört. */
     size_t data_size; /**< Größe der Prozessdaten */
     struct list_head commands; /**< EtherCAT-Kommandos für die Prozessdaten */
@@ -55,16 +58,18 @@
 
 /*****************************************************************************/
 
-void ec_domain_init(ec_domain_t *, ec_master_t *);
-void ec_domain_clear(ec_domain_t *);
+int ec_domain_init(ec_domain_t *, ec_master_t *, unsigned int);
+void ec_domain_clear(struct kobject *);
 int ec_domain_alloc(ec_domain_t *, uint32_t);
 
+ssize_t ec_show_domain_attribute(struct kobject *, struct attribute *, char *);
+
 /*****************************************************************************/
 
 #endif
 
 /* Emacs-Konfiguration
-   ;;; Local Variables: ***
-   ;;; c-basic-offset:4 ***
+;;; Local Variables: ***
+;;; c-basic-offset:4 ***
 ;;; End: ***
 */
--- a/master/master.c	Mon Apr 10 11:18:52 2006 +0000
+++ b/master/master.c	Mon Apr 10 14:25:02 2006 +0000
@@ -25,23 +25,64 @@
 
 /*****************************************************************************/
 
+static struct attribute attr_slave_count = {
+    .name = "slave_count",
+    .owner = THIS_MODULE,
+    .mode = S_IRUGO
+};
+
+static struct attribute *ec_def_attrs[] = {
+    &attr_slave_count,
+    NULL,
+};
+
+static struct sysfs_ops ec_sysfs_ops = {
+    .show = &ec_show_master_attribute,
+    .store = NULL
+};
+
+static struct kobj_type ktype_ec_master = {
+    .release = ec_master_clear,
+    .sysfs_ops = &ec_sysfs_ops,
+    .default_attrs = ec_def_attrs
+};
+
+/*****************************************************************************/
+
 /**
    Konstruktor des EtherCAT-Masters.
 */
 
-void ec_master_init(ec_master_t *master /**< EtherCAT-Master */)
-{
+int ec_master_init(ec_master_t *master, /**< EtherCAT-Master */
+                   unsigned int index /**< Master-Index */
+                   )
+{
+    EC_INFO("Initializing master %i.\n", index);
+
+    master->index = index;
     master->slaves = NULL;
     master->device = NULL;
+    master->reserved = 0;
 
     INIT_LIST_HEAD(&master->command_queue);
     INIT_LIST_HEAD(&master->domains);
     INIT_LIST_HEAD(&master->eoe_slaves);
 
+    // Init kobject and add it to the hierarchy
+    memset(&master->kobj, 0x00, sizeof(struct kobject));
+    kobject_init(&master->kobj);
+    master->kobj.ktype = &ktype_ec_master;
+    if (kobject_set_name(&master->kobj, "ethercat%i", index)) {
+        EC_ERR("Failed to set kobj name.\n");
+        kobject_put(&master->kobj);
+        return -1;
+    }
+
     ec_command_init(&master->simple_command);
     ec_command_init(&master->watch_command);
 
     ec_master_reset(master);
+    return 0;
 }
 
 /*****************************************************************************/
@@ -53,8 +94,12 @@
    auf das Slave-Array und gibt die Prozessdaten frei.
 */
 
-void ec_master_clear(ec_master_t *master /**< EtherCAT-Master */)
-{
+void ec_master_clear(struct kobject *kobj /**< KObject des Masters */)
+{
+    ec_master_t *master = container_of(kobj, ec_master_t, kobj);
+
+    EC_INFO("Clearing master %i...\n", master->index);
+
     ec_master_reset(master);
 
     if (master->device) {
@@ -64,6 +109,8 @@
 
     ec_command_clear(&master->simple_command);
     ec_command_clear(&master->watch_command);
+
+    EC_INFO("Master %i cleared.\n", master->index);
 }
 
 /*****************************************************************************/
@@ -102,8 +149,8 @@
     // Domain-Liste leeren
     list_for_each_entry_safe(domain, next_d, &master->domains, list) {
         list_del(&domain->list);
-        ec_domain_clear(domain);
-        kfree(domain);
+        kobject_del(&domain->kobj);
+        kobject_put(&domain->kobj);
     }
 
     // EOE-Liste leeren
@@ -709,6 +756,28 @@
 /*****************************************************************************/
 
 /**
+   Formatiert Attribut-Daten für lesenden Zugriff im SysFS
+
+   \return Anzahl Bytes im Speicher
+*/
+
+ssize_t ec_show_master_attribute(struct kobject *kobj, /**< KObject */
+                                 struct attribute *attr, /**< Attribut */
+                                 char *buffer /**< Speicher für die Daten */
+                                 )
+{
+    ec_master_t *master = container_of(kobj, ec_master_t, kobj);
+
+    if (attr == &attr_slave_count) {
+        return sprintf(buffer, "%i\n", master->slave_count);
+    }
+
+    return 0;
+}
+
+/*****************************************************************************/
+
+/**
    Gibt Überwachungsinformationen aus.
 */
 
@@ -767,17 +836,37 @@
 
 ec_domain_t *ecrt_master_create_domain(ec_master_t *master /**< Master */)
 {
-    ec_domain_t *domain;
+    ec_domain_t *domain, *last_domain;
+    unsigned int index;
 
     if (!(domain = (ec_domain_t *) kmalloc(sizeof(ec_domain_t), GFP_KERNEL))) {
         EC_ERR("Error allocating domain memory!\n");
-        return NULL;
-    }
-
-    ec_domain_init(domain, master);
+        goto out_return;
+    }
+
+    if (list_empty(&master->domains)) index = 0;
+    else {
+        last_domain = list_entry(master->domains.prev, ec_domain_t, list);
+        index = last_domain->index + 1;
+    }
+
+    if (ec_domain_init(domain, master, index)) {
+        EC_ERR("Failed to init domain.\n");
+        goto out_return;
+    }
+
+    if (kobject_add(&domain->kobj)) {
+        EC_ERR("Failed to add domain kobject.\n");
+        goto out_put;
+    }
+
     list_add_tail(&domain->list, &master->domains);
-
     return domain;
+
+ out_put:
+    kobject_put(&domain->kobj);
+ out_return:
+    return NULL;
 }
 
 /*****************************************************************************/
--- a/master/master.h	Mon Apr 10 11:18:52 2006 +0000
+++ b/master/master.h	Mon Apr 10 14:25:02 2006 +0000
@@ -12,6 +12,7 @@
 #define _EC_MASTER_H_
 
 #include <linux/list.h>
+#include <linux/sysfs.h>
 
 #include "device.h"
 #include "slave.h"
@@ -45,6 +46,9 @@
 
 struct ec_master
 {
+    struct list_head list; /**< Noetig fuer Master-Liste */
+    struct kobject kobj; /**< Kernel-Object */
+    unsigned int index; /**< Master-Index */
     ec_slave_t *slaves; /**< Array von Slaves auf dem Bus */
     unsigned int slave_count; /**< Anzahl Slaves auf dem Bus */
     ec_device_t *device; /**< EtherCAT-Gerät */
@@ -59,13 +63,14 @@
     ec_stats_t stats; /**< Rahmen-Statistiken */
     unsigned int timeout; /**< Timeout für synchronen Datenaustausch */
     struct list_head eoe_slaves; /**< Ethernet over EtherCAT Slaves */
+    unsigned int reserved; /**< Master durch Echtzeitprozess reserviert */
 };
 
 /*****************************************************************************/
 
 // Master creation and deletion
-void ec_master_init(ec_master_t *);
-void ec_master_clear(ec_master_t *);
+int ec_master_init(ec_master_t *, unsigned int);
+void ec_master_clear(struct kobject *);
 void ec_master_reset(ec_master_t *);
 
 // IO
@@ -79,8 +84,8 @@
 // Misc
 void ec_master_debug(const ec_master_t *);
 void ec_master_output_stats(ec_master_t *);
-
 void ec_master_run_eoe(ec_master_t *);
+ssize_t ec_show_master_attribute(struct kobject *, struct attribute *, char *);
 
 /*****************************************************************************/
 
--- a/master/module.c	Mon Apr 10 11:18:52 2006 +0000
+++ b/master/module.c	Mon Apr 10 14:25:02 2006 +0000
@@ -41,9 +41,8 @@
 
 /*****************************************************************************/
 
-int ec_master_count = 1;
-ec_master_t *ec_masters = NULL;
-unsigned int *ec_masters_reserved = NULL;
+static int ec_master_count = 1;
+static struct list_head ec_masters;
 
 /*****************************************************************************/
 
@@ -70,6 +69,7 @@
 int __init ec_init_module(void)
 {
     unsigned int i;
+    ec_master_t *master, *next;
 
     EC_INFO("Master driver, %s\n", COMPILE_INFO);
 
@@ -80,28 +80,36 @@
 
     EC_INFO("Initializing %i EtherCAT master(s)...\n", ec_master_count);
 
-    if (!(ec_masters = (ec_master_t *)
-          kmalloc(sizeof(ec_master_t) * ec_master_count, GFP_KERNEL))) {
-        EC_ERR("Failed to allocate master(s)!\n");
-        goto out_return;
-    }
-
-    if (!(ec_masters_reserved = (unsigned int *)
-          kmalloc(sizeof(int) * ec_master_count, GFP_KERNEL))) {
-        EC_ERR("Failed to allocate reservation flags!\n");
-        goto out_free_masters;
-    }
+    INIT_LIST_HEAD(&ec_masters);
 
     for (i = 0; i < ec_master_count; i++) {
-        ec_master_init(ec_masters + i);
-        ec_masters_reserved[i] = 0;
+        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)) // kobject_put is done inside...
+            goto out_free;
+
+        if (kobject_add(&master->kobj)) {
+            EC_ERR("Failed to add kobj.\n");
+            kobject_put(&master->kobj); // free master
+            goto out_free;
+        }
+
+        list_add_tail(&master->list, &ec_masters);
     }
 
     EC_INFO("Master driver initialized.\n");
     return 0;
 
- out_free_masters:
-    kfree(ec_masters);
+ out_free:
+    list_for_each_entry_safe(master, next, &ec_masters, list) {
+        list_del(&master->list);
+        kobject_del(&master->kobj);
+        kobject_put(&master->kobj);
+    }
  out_return:
     return -1;
 }
@@ -116,22 +124,38 @@
 
 void __exit ec_cleanup_module(void)
 {
-    unsigned int i;
+    ec_master_t *master, *next;
 
     EC_INFO("Cleaning up master driver...\n");
 
-    for (i = 0; i < ec_master_count; i++) {
-        if (ec_masters_reserved[i])
-            EC_WARN("Master %i is still in use!\n", i);
-        ec_master_clear(&ec_masters[i]);
-    }
-
-    kfree(ec_masters);
-    kfree(ec_masters_reserved);
+    list_for_each_entry_safe(master, next, &ec_masters, list) {
+        list_del(&master->list);
+        kobject_del(&master->kobj);
+        kobject_put(&master->kobj); // free master
+    }
 
     EC_INFO("Master driver cleaned up.\n");
 }
 
+/*****************************************************************************/
+
+/**
+   Gets a handle to a certain master.
+   \returns pointer to master
+*/
+
+ec_master_t *ec_find_master(unsigned int master_index)
+{
+    ec_master_t *master;
+
+    list_for_each_entry(master, &ec_masters, list) {
+        if (master->index == master_index) return master;
+    }
+
+    EC_ERR("Master %i does not exist!\n", master_index);
+    return NULL;
+}
+
 /******************************************************************************
  *
  * Treiberschnittstelle
@@ -162,23 +186,20 @@
         return NULL;
     }
 
-    if (master_index >= ec_master_count) {
-        EC_ERR("Master %i does not exist!\n", master_index);
-        return NULL;
-    }
-
-    master = ec_masters + master_index;
-
+    if (!(master = ec_find_master(master_index))) return NULL;
+
+    // critical section start
     if (master->device) {
         EC_ERR("Master %i already has a device!\n", master_index);
         return NULL;
     }
 
-    if (!(master->device = (ec_device_t *)
-          kmalloc(sizeof(ec_device_t), GFP_KERNEL))) {
+    if (!(master->device =
+          (ec_device_t *) kmalloc(sizeof(ec_device_t), GFP_KERNEL))) {
         EC_ERR("Failed to allocate device!\n");
         return NULL;
     }
+    // critical section end
 
     if (ec_device_init(master->device, master, net_dev, isr, module)) {
         kfree(master->device);
@@ -208,7 +229,7 @@
         return;
     }
 
-    master = ec_masters + master_index;
+    if (!(master = ec_find_master(master_index))) return;
 
     if (!master->device || master->device != device) {
         EC_WARN("Unable to unregister device!\n");
@@ -234,29 +255,26 @@
    \return Zeiger auf EtherCAT-Master oder NULL, wenn Parameter ungueltig.
 */
 
-ec_master_t *ecrt_request_master(unsigned int index
+ec_master_t *ecrt_request_master(unsigned int master_index
                                  /**< EtherCAT-Master-Index */
                                  )
 {
     ec_master_t *master;
 
-    EC_INFO("Requesting master %i...\n", index);
-
-    if (index < 0 || index >= ec_master_count) {
-        EC_ERR("Master %i does not exist!\n", index);
+    EC_INFO("Requesting master %i...\n", master_index);
+
+    if (!(master = ec_find_master(master_index))) goto req_return;
+
+    // begin critical section
+    if (master->reserved) {
+        EC_ERR("Master %i is already in use!\n", master_index);
         goto req_return;
     }
-
-    if (ec_masters_reserved[index]) {
-        EC_ERR("Master %i is already in use!\n", index);
-        goto req_return;
-    }
-
-    ec_masters_reserved[index] = 1;
-    master = &ec_masters[index];
+    master->reserved = 1;
+    // end critical section
 
     if (!master->device) {
-        EC_ERR("Master %i has no assigned device!\n", index);
+        EC_ERR("Master %i has no assigned device!\n", master_index);
         goto req_release;
     }
 
@@ -270,15 +288,14 @@
         goto req_module_put;
     }
 
-    if (!master->device->link_state)
-        EC_WARN("Link is DOWN.\n");
-
-    if (ec_master_bus_scan(master) != 0) {
+    if (!master->device->link_state) EC_WARN("Link is DOWN.\n");
+
+    if (ec_master_bus_scan(master)) {
         EC_ERR("Bus scan failed!\n");
         goto req_close;
     }
 
-    EC_INFO("Master %i is ready.\n", index);
+    EC_INFO("Master %i is ready.\n", master_index);
     return master;
 
  req_close:
@@ -288,9 +305,9 @@
     module_put(master->device->module);
     ec_master_reset(master);
  req_release:
-    ec_masters_reserved[index] = 0;
+    master->reserved = 0;
  req_return:
-    EC_ERR("Failed requesting master %i.\n", index);
+    EC_ERR("Failed requesting master %i.\n", master_index);
     return NULL;
 }
 
@@ -302,32 +319,22 @@
 
 void ecrt_release_master(ec_master_t *master /**< EtherCAT-Master */)
 {
-    unsigned int i, found;
-
-    found = 0;
-    for (i = 0; i < ec_master_count; i++) {
-        if (&ec_masters[i] == master) {
-            found = 1;
-            break;
-        }
-    }
-
-    if (!found) {
-        EC_WARN("Master 0x%08X was never requested!\n", (u32) master);
+    EC_INFO("Releasing master %i...\n", master->index);
+
+    if (!master->reserved) {
+        EC_ERR("Master %i was never requested!\n", master->index);
         return;
     }
 
-    EC_INFO("Releasing master %i...\n", i);
-
     ec_master_reset(master);
 
     if (ec_device_close(master->device))
         EC_WARN("Warning - Failed to close device!\n");
 
     module_put(master->device->module);
-    ec_masters_reserved[i] = 0;
-
-    EC_INFO("Released master %i.\n", i);
+    master->reserved = 0;
+
+    EC_INFO("Released master %i.\n", master->index);
     return;
 }