External domain memory.
authorFlorian Pose <fp@igh-essen.com>
Thu, 21 Feb 2008 15:49:02 +0000
changeset 809 ec4ef8911824
parent 808 1feddbd65608
child 810 7d7c59e1427e
External domain memory.
TODO
examples/mini/mini.c
include/ecrt.h
master/datagram.c
master/datagram.h
master/domain.c
master/domain.h
master/globals.h
--- a/TODO	Thu Feb 21 12:43:55 2008 +0000
+++ b/TODO	Thu Feb 21 15:49:02 2008 +0000
@@ -10,7 +10,6 @@
 
 * Realtime interface changes:
     - SDO access.
-    - External memory for domains.
 * Mailbox handler
 * Read PDO mapping via CoE during bus scan.
 * SDO write access in sysfs.
--- a/examples/mini/mini.c	Thu Feb 21 12:43:55 2008 +0000
+++ b/examples/mini/mini.c	Thu Feb 21 15:49:02 2008 +0000
@@ -46,6 +46,7 @@
 //#define KBUS
 #define PDOS
 #define MAPPING
+#define EXTERNAL_MEMORY
 
 /*****************************************************************************/
 
@@ -74,19 +75,34 @@
     {0x3102, 2, 16}  // value
 };
 
-const ec_pdo_info_t mapping[] = {
+const ec_pdo_info_t el3162_mapping[] = {
     {EC_DIR_INPUT, 0x1A00, 2, el3162_channel1},
     {EC_DIR_INPUT, 0x1A01, 2, el3162_channel2},
 };
+
+const ec_pdo_entry_info_t el2004_channels[] = {
+    {0x3001, 1, 1}, // Value 1
+    {0x3001, 2, 1}, // Value 2
+    {0x3001, 3, 1}, // Value 3
+    {0x3001, 4, 1}  // Value 4
+};
+
+const ec_pdo_info_t el2004_mapping[] = {
+    {EC_DIR_OUTPUT, 0x1600, 1, &el2004_channels[0]},
+    {EC_DIR_OUTPUT, 0x1601, 1, &el2004_channels[1]},
+    {EC_DIR_OUTPUT, 0x1602, 1, &el2004_channels[2]},
+    {EC_DIR_OUTPUT, 0x1603, 1, &el2004_channels[3]},
+};
 #endif
 
 #ifdef PDOS
-static uint8_t off_ana_in;
-//static uint8_t off_ana_out;
+static uint8_t *pd; /**< Process data. */
+static unsigned int off_ana_in;
+static unsigned int off_dig_out;
 
 const static ec_pdo_entry_reg_t domain1_regs[] = {
     {0, 1, Beckhoff_EL3162, 0x3101, 2, &off_ana_in},
-    //{0, 2, Beckhoff_EL4102, 0x3001, 1, &off_ana_out},
+    {0, 3, Beckhoff_EL2004, 0x3001, 1, &off_dig_out},
     {}
 };
 #endif
@@ -105,8 +121,7 @@
     spin_unlock(&master_lock);
 
     // process data
-    // k_pos = EC_READ_U32(r_ssi);
-    //EC_WRITE_U8(r_dig_out, blink ? 0x0F : 0x00);
+    EC_WRITE_U8(pd + off_dig_out, blink ? 0x0F : 0x00);
 
     if (counter) {
         counter--;
@@ -176,6 +191,9 @@
 #ifdef MAPPING
     ec_slave_config_t *sc;
 #endif
+#ifdef EXTERNAL_MEMORY
+    unsigned int size;
+#endif
     
     printk(KERN_INFO PFX "Starting...\n");
 
@@ -199,7 +217,17 @@
         goto out_release_master;
     }
 
-    if (ecrt_slave_config_mapping(sc, 2, mapping)) {
+    if (ecrt_slave_config_mapping(sc, 2, el3162_mapping)) {
+        printk(KERN_ERR PFX "Failed to configure Pdo mapping.\n");
+        goto out_release_master;
+    }
+
+    if (!(sc = ecrt_master_slave_config(master, 0, 3, Beckhoff_EL2004))) {
+        printk(KERN_ERR PFX "Failed to get slave configuration.\n");
+        goto out_release_master;
+    }
+
+    if (ecrt_slave_config_mapping(sc, 4, el2004_mapping)) {
         printk(KERN_ERR PFX "Failed to configure Pdo mapping.\n");
         goto out_release_master;
     }
@@ -213,11 +241,31 @@
     }
 #endif
 
+#ifdef EXTERNAL_MEMORY
+    if ((size = ecrt_domain_size(domain1))) {
+        if (!(pd = (uint8_t *) kmalloc(size, GFP_KERNEL))) {
+            printk(KERN_ERR PFX "Failed to allocate %u bytes of process data"
+                    " memory!\n", size);
+            goto out_release_master;
+        }
+        ecrt_domain_external_memory(domain1, pd);
+    }
+#endif
+
     printk(KERN_INFO PFX "Activating master...\n");
     if (ecrt_master_activate(master)) {
         printk(KERN_ERR PFX "Failed to activate master!\n");
-        goto out_release_master;
-    }
+#ifdef EXTERNAL_MEMORY
+        goto out_free_process_data;
+#else
+        goto out_release_master;
+#endif
+    }
+
+#ifndef EXTERNAL_MEMORY
+    // Get internal process data for domain
+    pd = ecrt_domain_data(domain1);
+#endif
 
     printk(KERN_INFO PFX "Starting cyclic sample thread.\n");
     init_timer(&timer);
@@ -228,10 +276,14 @@
     printk(KERN_INFO PFX "Started.\n");
     return 0;
 
- out_release_master:
+#ifdef EXTERNAL_MEMORY
+out_free_process_data:
+    kfree(pd);
+#endif
+out_release_master:
     printk(KERN_ERR PFX "Releasing master...\n");
     ecrt_release_master(master);
- out_return:
+out_return:
     printk(KERN_ERR PFX "Failed to load. Aborting.\n");
     return -1;
 }
@@ -243,6 +295,11 @@
     printk(KERN_INFO PFX "Stopping...\n");
 
     del_timer_sync(&timer);
+
+#ifdef EXTERNAL_MEMORY
+    kfree(pd);
+#endif
+
     printk(KERN_INFO PFX "Releasing master...\n");
     ecrt_release_master(master);
 
--- a/include/ecrt.h	Thu Feb 21 12:43:55 2008 +0000
+++ b/include/ecrt.h	Thu Feb 21 15:49:02 2008 +0000
@@ -55,13 +55,14 @@
  *   ecrt_slave_config_state().
  * - Process data memory for a domain can now be allocated externally. This
  *   offers the possibility to use a shared-memory-region. Therefore,
- *   added the domain methods ecrt_domain_size() and ecrt_domain_memory().
+ *   added the domain methods ecrt_domain_size() and
+ *   ecrt_domain_external_memory().
  * - Replaced the process data pointers in the Pdo entry registration
  *   functions with a process data offset, that is now returned by
  *   ecrt_slave_config_reg_pdo_entry(). This was necessary for the external
  *   domain memory. An additional advantage is, that the returned offset value
- *   is directly usable. The domain's process data offset can be retrieved
- *   with ecrt_domain_data().
+ *   is directly usable. If the domain's process data is allocated internally,
+ *   the start address can be retrieved with ecrt_domain_data().
  * - Replaced ecrt_slave_pdo_mapping/add/clear() with
  *   ecrt_slave_config_mapping() that is now able to specify Pdo mapping and
  *   Pdo configuration. Pdo entries mapped in this way can now immediately be
@@ -241,7 +242,7 @@
     uint32_t product_code; /**< Slave product code. */
     uint16_t index; /**< Pdo entry index. */
     uint8_t subindex; /**< Pdo entry subindex. */
-    uint8_t *offset; /**< Pointer to a variable to store the Pdo's
+    unsigned int *offset; /**< Pointer to a variable to store the Pdo's
                        offset in the process data. */
 } ec_pdo_entry_reg_t;
 
@@ -503,21 +504,32 @@
         ec_domain_t *domain /**< Domain. */
         );
 
-/** Provide memory to store the domain's process data.
- *
- * Call this after all Pdo entries have been registered. Since interface
- * version 1.4, you'll have to provide an external memory for the domain
- * process data.
- *
- * The size of the allocated memory must be at least the return value of
- * ecrt_domain_size(), after all Pdo entries have been registered.
- */
-void ecrt_domain_memory(
+/** Provide external memory to store the domain's process data.
+ *
+ * Call this after all Pdo entries have been registered and before activating
+ * the master.
+ *
+ * The size of the allocated memory must be at least ecrt_domain_size(), after
+ * all Pdo entries have been registered.
+ */
+void ecrt_domain_external_memory(
         ec_domain_t *domain, /**< Domain. */
         uint8_t *memory /**< Address of the memory to store the process
                           data in. */
         );
 
+/** Returns the domain's process data.
+ *
+ * If external memory was provided with ecrt_domain_external_memory(), the
+ * returned pointer will contain the address of that memory. Otherwise it will
+ * point to the internally allocated memory.
+ *
+ * \return Pointer to the process data memory.
+ */
+uint8_t *ecrt_domain_data(
+        ec_domain_t *domain /**< Domain. */
+        );
+
 /** Processes received datagrams.
  *
  * \todo doc
--- a/master/datagram.c	Thu Feb 21 12:43:55 2008 +0000
+++ b/master/datagram.c	Thu Feb 21 15:49:02 2008 +0000
@@ -63,20 +63,19 @@
 
 /*****************************************************************************/
 
-/**
-   Datagram constructor.
-*/
-
+/** Constructor.
+ */
 void ec_datagram_init(ec_datagram_t *datagram /**< EtherCAT datagram */)
 {
     INIT_LIST_HEAD(&datagram->queue); // mark as unqueued
     datagram->type = EC_DATAGRAM_NONE;
     memset(datagram->address, 0x00, EC_ADDR_LEN);
     datagram->data = NULL;
+    datagram->data_origin = EC_ORIG_INTERNAL;
     datagram->mem_size = 0;
     datagram->data_size = 0;
     datagram->index = 0x00;
-    datagram->working_counter = 0x00;
+    datagram->working_counter = 0x0000;
     datagram->state = EC_DATAGRAM_INIT;
     datagram->cycles_sent = 0;
     datagram->jiffies_sent = 0;
@@ -84,33 +83,37 @@
     datagram->jiffies_received = 0;
     datagram->skip_count = 0;
     datagram->stats_output_jiffies = 0;
-    datagram->name[0] = 0x00;
-}
-
-/*****************************************************************************/
-
-/**
-   Datagram destructor.
-*/
-
+    memset(datagram->name, 0x00, EC_DATAGRAM_NAME_SIZE);
+}
+
+/*****************************************************************************/
+
+/** Destructor.
+ */
 void ec_datagram_clear(ec_datagram_t *datagram /**< EtherCAT datagram */)
 {
-    if (datagram->data) kfree(datagram->data);
-}
-
-/*****************************************************************************/
-
-/**
-   Allocates datagram data memory.
-   If the allocated memory is already larger than requested, nothing ist done.
-   \return 0 in case of success, else < 0
-*/
-
+    if (datagram->data_origin == EC_ORIG_INTERNAL && datagram->data)
+        kfree(datagram->data);
+}
+
+/*****************************************************************************/
+
+/** Allocates internal payload memory.
+ *
+ * If the allocated memory is already larger than requested, nothing ist done.
+ *
+ * \attention If external payload memory has been provided, no range checking
+ *            is done!
+ *
+ * \return 0 in case of success, else < 0
+ */
 int ec_datagram_prealloc(ec_datagram_t *datagram, /**< EtherCAT datagram */
-                         size_t size /**< New size in bytes */
-                         )
-{
-    if (size <= datagram->mem_size) return 0;
+        size_t size /**< New size in bytes */
+        )
+{
+    if (datagram->data_origin == EC_ORIG_EXTERNAL
+            || size <= datagram->mem_size)
+        return 0;
 
     if (datagram->data) {
         kfree(datagram->data);
@@ -129,12 +132,12 @@
 
 /*****************************************************************************/
 
-/**
-   Initializes an EtherCAT NPRD datagram.
-   Node-adressed physical read.
-   \return 0 in case of success, else < 0
-*/
-
+/** Initializes an EtherCAT NPRD datagram.
+ *
+ * Node-adressed physical read.
+ *
+ * \return 0 in case of success, else < 0
+ */
 int ec_datagram_nprd(ec_datagram_t *datagram,
                      /**< EtherCAT datagram */
                      uint16_t node_address,
@@ -157,12 +160,12 @@
 
 /*****************************************************************************/
 
-/**
-   Initializes an EtherCAT NPWR datagram.
-   Node-adressed physical write.
-   \return 0 in case of success, else < 0
-*/
-
+/** Initializes an EtherCAT NPWR datagram.
+ *
+ * Node-adressed physical write.
+ *
+ * \return 0 in case of success, else < 0
+ */
 int ec_datagram_npwr(ec_datagram_t *datagram,
                      /**< EtherCAT datagram */
                      uint16_t node_address,
@@ -185,12 +188,12 @@
 
 /*****************************************************************************/
 
-/**
-   Initializes an EtherCAT APRD datagram.
-   Autoincrement physical read.
-   \return 0 in case of success, else < 0
-*/
-
+/** Initializes an EtherCAT APRD datagram.
+ *
+ * Autoincrement physical read.
+ *
+ * \return 0 in case of success, else < 0
+ */
 int ec_datagram_aprd(ec_datagram_t *datagram,
                      /**< EtherCAT datagram */
                      uint16_t ring_position,
@@ -210,12 +213,12 @@
 
 /*****************************************************************************/
 
-/**
-   Initializes an EtherCAT APWR datagram.
-   Autoincrement physical write.
-   \return 0 in case of success, else < 0
-*/
-
+/** Initializes an EtherCAT APWR datagram.
+ *
+ * Autoincrement physical write.
+ *
+ * \return 0 in case of success, else < 0
+ */
 int ec_datagram_apwr(ec_datagram_t *datagram,
                      /**< EtherCAT datagram */
                      uint16_t ring_position,
@@ -235,12 +238,12 @@
 
 /*****************************************************************************/
 
-/**
-   Initializes an EtherCAT BRD datagram.
-   Broadcast read.
-   \return 0 in case of success, else < 0
-*/
-
+/** Initializes an EtherCAT BRD datagram.
+ *
+ * Broadcast read.
+ *
+ * \return 0 in case of success, else < 0
+ */
 int ec_datagram_brd(ec_datagram_t *datagram,
                     /**< EtherCAT datagram */
                     uint16_t offset,
@@ -258,12 +261,12 @@
 
 /*****************************************************************************/
 
-/**
-   Initializes an EtherCAT BWR datagram.
-   Broadcast write.
-   \return 0 in case of success, else < 0
-*/
-
+/** Initializes an EtherCAT BWR datagram.
+ *
+ * Broadcast write.
+ *
+ * \return 0 in case of success, else < 0
+ */
 int ec_datagram_bwr(ec_datagram_t *datagram,
                     /**< EtherCAT datagram */
                     uint16_t offset,
@@ -281,20 +284,24 @@
 
 /*****************************************************************************/
 
-/**
-   Initializes an EtherCAT LRW datagram.
-   Logical read write.
-   \return 0 in case of success, else < 0
-*/
-
-int ec_datagram_lrw(ec_datagram_t *datagram,
-                    /**< EtherCAT datagram */
-                    uint32_t offset,
-                    /**< logical address */
-                    size_t data_size
-                    /**< number of bytes to read/write */
-                    )
-{
+/** Initializes an EtherCAT LRW datagram.
+ *
+ * Logical read write.
+ *
+ * \attention It is assumed, that the external memory is at least \a data_size
+ *            bytes large.
+ *
+ * \return 0 in case of success, else < 0
+ */
+int ec_datagram_lrw(
+        ec_datagram_t *datagram, /**< EtherCAT datagram */
+        uint32_t offset, /**< Logical address. */
+        size_t data_size, /**< Number of bytes to read/write. */
+        uint8_t *external_memory /**< Pointer to the memory to use. */
+        )
+{
+    datagram->data = external_memory;
+    datagram->data_origin = EC_ORIG_EXTERNAL;
     EC_FUNC_HEADER;
     datagram->type = EC_DATAGRAM_LRW;
     EC_WRITE_U32(datagram->address, offset);
@@ -303,11 +310,10 @@
 
 /*****************************************************************************/
 
-/**
- * Evaluates the working counter of a single-cast datagram.
+/** Evaluates the working counter of a single-cast datagram.
+ *
  * Outputs an error message.
  */
-
 void ec_datagram_print_wc_error(
         const ec_datagram_t *datagram /**< EtherCAT datagram */
         )
@@ -323,10 +329,8 @@
 
 /*****************************************************************************/
 
-/**
- * Outputs datagram statistics at most every second.
- */
-
+/** Outputs datagram statistics at most every second.
+ */
 void ec_datagram_output_stats(
         ec_datagram_t *datagram
         )
--- a/master/datagram.h	Thu Feb 21 12:43:55 2008 +0000
+++ b/master/datagram.h	Thu Feb 21 15:49:02 2008 +0000
@@ -54,66 +54,58 @@
 
 /*****************************************************************************/
 
-/**
-   EtherCAT datagram type.
-*/
-
-typedef enum
-{
-    EC_DATAGRAM_NONE = 0x00, /**< Dummy */
-    EC_DATAGRAM_APRD = 0x01, /**< Auto-increment physical read */
-    EC_DATAGRAM_APWR = 0x02, /**< Auto-increment physical write */
-    EC_DATAGRAM_NPRD = 0x04, /**< Node-addressed physical read */
-    EC_DATAGRAM_NPWR = 0x05, /**< Node-addressed physical write */
-    EC_DATAGRAM_BRD  = 0x07, /**< Broadcast read */
-    EC_DATAGRAM_BWR  = 0x08, /**< Broadcast write */
-    EC_DATAGRAM_LRW  = 0x0C  /**< Logical read/write */
-}
-ec_datagram_type_t;
-
-/**
-   EtherCAT datagram state.
-*/
-
-typedef enum
-{
-    EC_DATAGRAM_INIT,      /**< new datagram */
-    EC_DATAGRAM_QUEUED,    /**< datagram queued for sending */
-    EC_DATAGRAM_SENT,      /**< datagram has been sent (still in the queue) */
-    EC_DATAGRAM_RECEIVED,  /**< datagram has been received (dequeued) */
-    EC_DATAGRAM_TIMED_OUT, /**< datagram timed out (dequeued) */
-    EC_DATAGRAM_ERROR      /**< error while sending/receiving (dequeued) */
-}
-ec_datagram_state_t;
+/** EtherCAT datagram type.
+ */
+typedef enum {
+    EC_DATAGRAM_NONE = 0x00, /**< Dummy. */
+    EC_DATAGRAM_APRD = 0x01, /**< Auto-increment physical read. */
+    EC_DATAGRAM_APWR = 0x02, /**< Auto-increment physical write. */
+    EC_DATAGRAM_NPRD = 0x04, /**< Node-addressed physical read. */
+    EC_DATAGRAM_NPWR = 0x05, /**< Node-addressed physical write. */
+    EC_DATAGRAM_BRD  = 0x07, /**< Broadcast read. */
+    EC_DATAGRAM_BWR  = 0x08, /**< Broadcast write. */
+    EC_DATAGRAM_LRW  = 0x0C  /**< Logical read/write. */
+} ec_datagram_type_t;
 
 /*****************************************************************************/
 
-/**
-   EtherCAT datagram.
-*/
+/** EtherCAT datagram state.
+ */
+typedef enum {
+    EC_DATAGRAM_INIT,      /**< Initial state of a new datagram. */
+    EC_DATAGRAM_QUEUED,    /**< Queued for sending. */
+    EC_DATAGRAM_SENT,      /**< Sent (still in the queue). */
+    EC_DATAGRAM_RECEIVED,  /**< Received (dequeued). */
+    EC_DATAGRAM_TIMED_OUT, /**< Timed out (dequeued). */
+    EC_DATAGRAM_ERROR      /**< Error while sending/receiving (dequeued). */
+} ec_datagram_state_t;
 
-typedef struct
-{
-    struct list_head list; /**< needed by domain datagram lists */
-    struct list_head queue; /**< master datagram queue item */
-    struct list_head sent; /**< master list item for sent datagrams */
-    ec_datagram_type_t type; /**< datagram type (APRD, BWR, etc) */
-    uint8_t address[EC_ADDR_LEN]; /**< recipient address */
-    uint8_t *data; /**< datagram data */
-    size_t mem_size; /**< datagram \a data memory size */
-    size_t data_size; /**< size of the data in \a data */
-    uint8_t index; /**< datagram index (set by master) */
-    uint16_t working_counter; /**< working counter */
-    ec_datagram_state_t state; /**< datagram state */
-    cycles_t cycles_sent; /**< time, the datagram was sent */
-    unsigned long jiffies_sent; /**< jiffies, when the datagram was sent */
-    cycles_t cycles_received; /**< time, when the datagram was received */
-    unsigned long jiffies_received; /**< jiffies the datagram was received */
-    unsigned int skip_count; /**< number of requeues when not yet received */
-    unsigned long stats_output_jiffies; /**< last statistics output */
-    char name[EC_DATAGRAM_NAME_SIZE]; /**< description of the datagram */
-}
-ec_datagram_t;
+/*****************************************************************************/
+
+/** EtherCAT datagram.
+ */
+typedef struct {
+    struct list_head list; /**< Needed by domain datagram lists. */
+    struct list_head queue; /**< Master datagram queue item. */
+    struct list_head sent; /**< Master list item for sent datagrams. */
+    ec_datagram_type_t type; /**< Datagram type (APRD, BWR, etc.). */
+    uint8_t address[EC_ADDR_LEN]; /**< Recipient address. */
+    uint8_t *data; /**< Datagram payload. */
+    ec_origin_t data_origin; /**< Origin of the \a data memory. */
+    size_t mem_size; /**< Datagram \a data memory size. */
+    size_t data_size; /**< Size of the data in \a data. */
+    uint8_t index; /**< Index (set by master). */
+    uint16_t working_counter; /**< Working counter. */
+    ec_datagram_state_t state; /**< State. */
+    cycles_t cycles_sent; /**< Time, when the datagram was sent. */
+    unsigned long jiffies_sent; /**< Jiffies, when the datagram was sent. */
+    cycles_t cycles_received; /**< Time, when the datagram was received. */
+    unsigned long jiffies_received; /**< Jiffies, when the datagram was
+                                      received. */
+    unsigned int skip_count; /**< Number of requeues when not yet received. */
+    unsigned long stats_output_jiffies; /**< Last statistics output. */
+    char name[EC_DATAGRAM_NAME_SIZE]; /**< Description of the datagram. */
+} ec_datagram_t;
 
 /*****************************************************************************/
 
@@ -127,7 +119,7 @@
 int ec_datagram_apwr(ec_datagram_t *, uint16_t, uint16_t, size_t);
 int ec_datagram_brd(ec_datagram_t *, uint16_t, size_t);
 int ec_datagram_bwr(ec_datagram_t *, uint16_t, size_t);
-int ec_datagram_lrw(ec_datagram_t *, uint32_t, size_t);
+int ec_datagram_lrw(ec_datagram_t *, uint32_t, size_t, uint8_t *);
 
 void ec_datagram_print_wc_error(const ec_datagram_t *);
 void ec_datagram_output_stats(ec_datagram_t *datagram);
--- a/master/domain.c	Thu Feb 21 12:43:55 2008 +0000
+++ b/master/domain.c	Thu Feb 21 15:49:02 2008 +0000
@@ -49,6 +49,7 @@
 /*****************************************************************************/
 
 void ec_domain_clear(struct kobject *);
+void ec_domain_clear_data(ec_domain_t *);
 ssize_t ec_show_domain_attribute(struct kobject *, struct attribute *, char *);
 
 /*****************************************************************************/
@@ -90,7 +91,9 @@
     domain->master = master;
     domain->index = index;
     domain->data_size = 0;
-    domain->base_address = 0;
+    domain->data = NULL;
+    domain->data_origin = EC_ORIG_INTERNAL;
+    domain->logical_base_address = 0L;
     domain->working_counter = 0xFFFFFFFF;
     domain->notify_jiffies = 0;
     domain->working_counter_changes = 0;
@@ -158,19 +161,36 @@
         kfree(datagram);
     }
 
+    ec_domain_clear_data(domain);
+
     kfree(domain);
 }
 
 /*****************************************************************************/
 
+/** Frees internally allocated memory.
+ */
+void ec_domain_clear_data(
+        ec_domain_t *domain /**< EtherCAT domain. */
+        )
+{
+    if (domain->data_origin == EC_ORIG_INTERNAL && domain->data)
+        kfree(domain->data);
+    domain->data = NULL;
+    domain->data_origin = EC_ORIG_INTERNAL;
+}
+
+/*****************************************************************************/
+
 /** Allocates a domain datagram and appends it to the list.
  *
  * \return 0 in case of success, else < 0
  */
 int ec_domain_add_datagram(
         ec_domain_t *domain, /**< EtherCAT domain. */
-        uint32_t offset, /**< Logical offset. */
-        size_t data_size /**< Size of the data. */
+        uint32_t logical_offset, /**< Logical offset. */
+        size_t data_size, /**< Size of the data. */
+        uint8_t *data /**< Process data. */
         )
 {
     ec_datagram_t *datagram;
@@ -182,9 +202,9 @@
 
     ec_datagram_init(datagram);
     snprintf(datagram->name, EC_DATAGRAM_NAME_SIZE,
-            "domain%u-%u", domain->index, offset);
-
-    if (ec_datagram_lrw(datagram, offset, data_size)) {
+            "domain%u-%u", domain->index, logical_offset);
+
+    if (ec_datagram_lrw(datagram, logical_offset, data_size, data)) {
         kfree(datagram);
         return -1;
     }
@@ -200,6 +220,8 @@
  * This allocates the necessary datagrams and writes the correct logical
  * addresses to every configured FMMU.
  *
+ * \todo Check for FMMUs that do not fit into any datagram.
+ *
  * \retval 0 in case of success
  * \retval <0 on failure.
  */
@@ -209,17 +231,26 @@
         )
 {
     uint32_t datagram_offset;
-    size_t datagram_data_size;
+    size_t datagram_size;
     unsigned int datagram_count, i;
     ec_slave_config_t *sc;
     ec_fmmu_config_t *fmmu;
 
-    domain->base_address = base_address;
-
-    // Cycle through all domain FMMUS, add the logical base address and assign
-    // as many PDO entries as possible to the datagrams.
-    datagram_offset = base_address;
-    datagram_data_size = 0;
+    domain->logical_base_address = base_address;
+
+    if (domain->data_size && domain->data_origin == EC_ORIG_INTERNAL) {
+        if (!(domain->data =
+                    (uint8_t *) kmalloc(domain->data_size, GFP_KERNEL))) {
+            EC_ERR("Failed to allocate %u bytes internal memory for"
+                    " domain %u!\n", domain->data_size, domain->index);
+            return -1;
+        }
+    }
+
+    // Cycle through all domain FMMUS, correct the logical base addresses and
+    // set up the datagrams to carry the process data.
+    datagram_offset = 0;
+    datagram_size = 0;
     datagram_count = 0;
     list_for_each_entry(sc, &domain->master->configs, list) {
         for (i = 0; i < sc->used_fmmus; i++) {
@@ -227,28 +258,36 @@
             if (fmmu->domain != domain)
                 continue;
 
+            // Correct logical FMMU address
             fmmu->logical_start_address += base_address;
-            if (datagram_data_size + fmmu->data_size > EC_MAX_DATA_SIZE) {
-                if (ec_domain_add_datagram(domain, datagram_offset,
-                            datagram_data_size)) return -1;
-                datagram_offset += datagram_data_size;
-                datagram_data_size = 0;
+
+            // If the current FMMU's data do not fit in the current datagram,
+            // allocate a new one.
+            if (datagram_size + fmmu->data_size > EC_MAX_DATA_SIZE) {
+                if (ec_domain_add_datagram(domain,
+                            domain->logical_base_address + datagram_offset,
+                            datagram_size, domain->data + datagram_offset))
+                    return -1;
+                datagram_offset += datagram_size;
+                datagram_size = 0;
                 datagram_count++;
             }
-            datagram_data_size += fmmu->data_size;
-        }
-    }
-
-    // allocate last datagram
-    if (datagram_data_size) {
-        if (ec_domain_add_datagram(domain, datagram_offset,
-                                   datagram_data_size))
+
+            datagram_size += fmmu->data_size;
+        }
+    }
+
+    // allocate last datagram, if data are left
+    if (datagram_size) {
+        if (ec_domain_add_datagram(domain,
+                    domain->logical_base_address + datagram_offset,
+                    datagram_size, domain->data + datagram_offset))
             return -1;
         datagram_count++;
     }
 
     EC_INFO("Domain %u with logical offset %u contains %u bytes in %u"
-            " datagram%s.\n", domain->index, domain->base_address,
+            " datagram%s.\n", domain->index, domain->logical_base_address,
             domain->data_size, datagram_count, datagram_count == 1 ? "" : "s");
     return 0;
 }
@@ -302,6 +341,30 @@
 
 /*****************************************************************************/
 
+size_t ecrt_domain_size(ec_domain_t *domain)
+{
+    return domain->data_size;
+}
+
+/*****************************************************************************/
+
+void ecrt_domain_external_memory(ec_domain_t *domain, uint8_t *mem)
+{
+    ec_domain_clear_data(domain);
+
+    domain->data = mem;
+    domain->data_origin = EC_ORIG_EXTERNAL;
+}
+
+/*****************************************************************************/
+
+uint8_t *ecrt_domain_data(ec_domain_t *domain)
+{
+    return domain->data;
+}
+
+/*****************************************************************************/
+
 void ecrt_domain_process(ec_domain_t *domain)
 {
     unsigned int working_counter_sum;
@@ -364,8 +427,9 @@
 /** \cond */
 
 EXPORT_SYMBOL(ecrt_domain_reg_pdo_entry_list);
-//EXPORT_SYMBOL(ecrt_domain_size);
-//EXPORT_SYMBOL(ecrt_domain_memory);
+EXPORT_SYMBOL(ecrt_domain_size);
+EXPORT_SYMBOL(ecrt_domain_external_memory);
+EXPORT_SYMBOL(ecrt_domain_data);
 EXPORT_SYMBOL(ecrt_domain_process);
 EXPORT_SYMBOL(ecrt_domain_queue);
 EXPORT_SYMBOL(ecrt_domain_state);
--- a/master/domain.h	Thu Feb 21 12:43:55 2008 +0000
+++ b/master/domain.h	Thu Feb 21 15:49:02 2008 +0000
@@ -60,11 +60,14 @@
 {
     struct kobject kobj; /**< kobject. */
     struct list_head list; /**< List item. */
+    ec_master_t *master; /**< EtherCAT master owning the domain. */
     unsigned int index; /**< Index (just a number). */
-    ec_master_t *master; /**< EtherCAT master owning the domain. */
     size_t data_size; /**< Size of the process data. */
+    uint8_t *data; /**< Memory for the process data. */
+    ec_origin_t data_origin; /**< Origin of the \a data memory. */
     struct list_head datagrams; /**< Datagrams for process data exchange. */
-    uint32_t base_address; /**< Logical offset address of the process data. */
+    uint32_t logical_base_address; /**< Logical offset address of the
+                                     process data. */
     unsigned int working_counter; /**< Last working counter value. */
     unsigned int state; /**< Error state. */
     unsigned int working_counter_changes; /**< Working counter changes
--- a/master/globals.h	Thu Feb 21 12:43:55 2008 +0000
+++ b/master/globals.h	Thu Feb 21 15:49:02 2008 +0000
@@ -179,7 +179,7 @@
 
 /*****************************************************************************/
 
-/** Code - Message pair.
+/** Code/Message pair.
  *
  * Some EtherCAT datagrams support reading a status code to display a certain
  * message. This type allows to map a code to a message string.
@@ -187,8 +187,7 @@
 typedef struct {
     uint32_t code; /**< Code. */
     const char *message; /**< Message belonging to \a code. */
-}
-ec_code_msg_t;
+} ec_code_msg_t;
 
 /*****************************************************************************/
 
@@ -204,6 +203,15 @@
 
 /*****************************************************************************/
 
+/** Origin type.
+ */
+typedef enum {
+    EC_ORIG_INTERNAL, /**< Internal. */
+    EC_ORIG_EXTERNAL /**< External. */
+} ec_origin_t;
+
+/*****************************************************************************/
+
 typedef struct ec_slave ec_slave_t; /**< \see ec_slave. */
 typedef struct ec_sdo ec_sdo_t; /**< \see ec_sdo */