[etherlab-users] e1000e Limited to frame rate of 250/sec

Thomas Bitsky Jr tbj at automateddesign.com
Wed Jul 3 23:25:55 CEST 2013


Hello, all.

I've been working for a week on running the EtherCAT master 1.5.1 on an Intel Atom Desktop with two e1000e Intel cards. I need to get a frame rate of 2000/sec, but with both the generic driver and ec_e1000e driver, I can get no more than 250/sec. There are no errors or warning while running.

The Operating System is Linux 2.6.33 with the RT PREMPT patch. I also tried running the generic driver on 3.2.46-rt67 with the same results.

To get the network statistics, I type "ethercat master" at the command line. When the EtherCAT master starts up, it seems to burst at a massively fast frame rate, then quickly settle to 250/sec. Note that on three different machines, the results are the same, although I think they all have Intel NICs. When I start up the kernel module I wrote and the network goes into Operation, the speed results are unchanged.

I know the e1000e driver can be slower than the 8139too driver, but 250/sec seems excessively slow and the fact that it's ALWAYS 250 regardless of driver or machine leads me to believe I'm doing something wrong. I've tried turning off the InterruptThrottleRate feature of the card (with both the e1000e and ec_e1000e driver), but with no effect.

Below is the code that I'm using to run the test, which is based off the "mini" example. Can anyone tell me what I'm doing wrong, or if there is some feature in the e1000e driver that I need to disable?

Thanks in advance,
Tom


#include <linux/version.h>
#include <linux/module.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/err.h>
#include <linux/semaphore.h>
#include <linux/time.h>

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
#include <linux/semaphore.h>
#else
#include <asm/semaphore.h>
#endif


#define MY_PRIORITY (49) /* we use 49 as the PRREMPT_RT use 50
                            as the priority of kernel tasklets
                            and interrupt handler by default */

#define MAX_SAFE_STACK (8*1024) /* The maximum stack size which is
                                   guaranteed safe to access without
                                   faulting */

// #define NSEC_PER_SEC    (1000000000) /* The number of nsecs per sec. */


#define PFX "durability.c: "

#include "/home/tbj/srcroot/ethercat/include/ecrt.h" // EtherCAT realtime interface
/*
* Import the bus topology generated by EtherLAB
*/
#include "2369topology.h"

/*
* Application Parameters
*/
#define FREQUENCY 2000
#define INHIBIT_TIME 20

// process data
static uint8_t *processData = NULL;

/*
* macros for any of the hardware on the network.
* Use # ethercat slaves to see this info
* You can also use # ethercat cstruct > topology.h to generate a slave header.
* The vendor id and product code for each slave will be listed in comments in that file.
*/
#define Beckhoff_EK1100 0x00000002, 0x044c2c52        // uint32_t vendor_id, uint32_t product_code
#define Beckhoff_EL1004 0x00000002, 0x03ec3052
#define Beckhoff_EL5101 0x00000002, 0x13ed3052
#define Beckhoff_EL2004 0x00000002, 0x07d43052
#define Beckhoff_EL6601 0x00000002, 0x19c93052
#define SMC_EX260SEC3 0x00000114, 0x01000003
#define Beckhoff_EP1809 0x00000002, 0x07114052
#define Beckhoff_EP2028 0x00000002, 0x07ec4052
#define Beckhoff_EP4174 0x00000002, 0x104e4052


/*
* Global variables for interacting with the EtherCAT master.
*/
static ec_master_t *master = NULL;
static ec_master_state_t master_state = {};
struct semaphore master_sem;



static ec_domain_t *domain = NULL;
static ec_domain_state_t domain_state = {};

// Timer
static struct timer_list timer;

struct timespec lastCycle_;
struct timespec lastSend_;
struct timespec lastRead_;
struct sched_param param;
struct timespec interval; // = 50000; /* 50us*/


struct PdoOffset
      {
      unsigned int offset;
      unsigned int bitPosition;
      };


static struct PdoOffset digInOffsets[255];
static struct PdoOffset digOutOffsets[255];
static struct PdoOffset analogInOffsets[255];
static struct PdoOffset analogOutOffsets[255];


const static ec_pdo_entry_reg_t domain_regs[] =
      {
            {0,1, Beckhoff_EL1004, 0x6000, 1, &digInOffsets[0].offset, &digInOffsets[0].bitPosition},
            {0,1, Beckhoff_EL1004, 0x6010, 1, &digInOffsets[1].offset, &digInOffsets[1].bitPosition},
            {0,1, Beckhoff_EL1004, 0x6020, 1, &digInOffsets[2].offset, &digInOffsets[2].bitPosition},
            {0,1, Beckhoff_EL1004, 0x6030, 1, &digInOffsets[3].offset, &digInOffsets[3].bitPosition},

            {0,2, Beckhoff_EL1004, 0x6000, 1, &digInOffsets[4].offset, &digInOffsets[4].bitPosition},
            {0,2, Beckhoff_EL1004, 0x6010, 1, &digInOffsets[5].offset, &digInOffsets[5].bitPosition},
            {0,2, Beckhoff_EL1004, 0x6020, 1, &digInOffsets[6].offset, &digInOffsets[6].bitPosition},
            {0,2, Beckhoff_EL1004, 0x6030, 1, &digInOffsets[7].offset, &digInOffsets[7].bitPosition},

            {0,3, Beckhoff_EL1004, 0x6000, 1, &digInOffsets[8].offset, &digInOffsets[8].bitPosition},
            {0,3, Beckhoff_EL1004, 0x6010, 1, &digInOffsets[9].offset, &digInOffsets[9].bitPosition},
            {0,3, Beckhoff_EL1004, 0x6020, 1, &digInOffsets[10].offset, &digInOffsets[10].bitPosition},
            {0,3, Beckhoff_EL1004, 0x6030, 1, &digInOffsets[11].offset, &digInOffsets[11].bitPosition},

            {0,9, Beckhoff_EP1809, 0x6000, 1, &digInOffsets[12].offset, &digInOffsets[12].bitPosition},
            {0,9, Beckhoff_EP1809, 0x6010, 1, &digInOffsets[13].offset, &digInOffsets[13].bitPosition},
            {0,9, Beckhoff_EP1809, 0x6020, 1, &digInOffsets[14].offset, &digInOffsets[14].bitPosition},
            {0,9, Beckhoff_EP1809, 0x6030, 1, &digInOffsets[15].offset, &digInOffsets[15].bitPosition},
            {0,9, Beckhoff_EP1809, 0x6040, 1, &digInOffsets[16].offset, &digInOffsets[16].bitPosition},
            {0,9, Beckhoff_EP1809, 0x6050, 1, &digInOffsets[17].offset, &digInOffsets[17].bitPosition},
            {0,9, Beckhoff_EP1809, 0x6060, 1, &digInOffsets[18].offset, &digInOffsets[18].bitPosition},
            {0,9, Beckhoff_EP1809, 0x6070, 1, &digInOffsets[19].offset, &digInOffsets[19].bitPosition},
            {0,9, Beckhoff_EP1809, 0x6080, 1, &digInOffsets[20].offset, &digInOffsets[20].bitPosition},
            {0,9, Beckhoff_EP1809, 0x6090, 1, &digInOffsets[21].offset, &digInOffsets[21].bitPosition},
            {0,9, Beckhoff_EP1809, 0x60A0, 1, &digInOffsets[22].offset, &digInOffsets[22].bitPosition},
            {0,9, Beckhoff_EP1809, 0x60B0, 1, &digInOffsets[23].offset, &digInOffsets[23].bitPosition},
            {0,9, Beckhoff_EP1809, 0x60C0, 1, &digInOffsets[24].offset, &digInOffsets[24].bitPosition},
            {0,9, Beckhoff_EP1809, 0x60D0, 1, &digInOffsets[25].offset, &digInOffsets[25].bitPosition},
            {0,9, Beckhoff_EP1809, 0x60E0, 1, &digInOffsets[26].offset, &digInOffsets[26].bitPosition},
            {0,9, Beckhoff_EP1809, 0x60F0, 1, &digInOffsets[27].offset, &digInOffsets[27].bitPosition},

            {0,4, Beckhoff_EL5101, 0x6010, 0x11, &analogInOffsets[0].offset, &analogInOffsets[0].bitPosition},

            {0,5, Beckhoff_EL2004, 0x7000, 1, &digOutOffsets[0].offset, &digOutOffsets[0].bitPosition},
            {0,5, Beckhoff_EL2004, 0x7010, 1, &digOutOffsets[1].offset, &digOutOffsets[1].bitPosition},
            {0,5, Beckhoff_EL2004, 0x7020, 1, &digOutOffsets[2].offset, &digOutOffsets[2].bitPosition},
            {0,5, Beckhoff_EL2004, 0x7030, 1, &digOutOffsets[3].offset, &digOutOffsets[3].bitPosition},

            {0,6, Beckhoff_EL2004, 0x7000, 1, &digOutOffsets[4].offset, &digOutOffsets[4].bitPosition},
            {0,6, Beckhoff_EL2004, 0x7010, 1, &digOutOffsets[5].offset, &digOutOffsets[5].bitPosition},
            {0,6, Beckhoff_EL2004, 0x7020, 1, &digOutOffsets[6].offset, &digOutOffsets[6].bitPosition},
            {0,6, Beckhoff_EL2004, 0x7030, 1, &digOutOffsets[7].offset, &digOutOffsets[7].bitPosition},

            {0,10, Beckhoff_EP2028, 0x7000, 1, &digOutOffsets[8].offset, &digOutOffsets[8].bitPosition},
            {0,10, Beckhoff_EP2028, 0x7010, 1, &digOutOffsets[9].offset, &digOutOffsets[9].bitPosition},
            {0,10, Beckhoff_EP2028, 0x7020, 1, &digOutOffsets[10].offset, &digOutOffsets[10].bitPosition},
            {0,10, Beckhoff_EP2028, 0x7030, 1, &digOutOffsets[11].offset, &digOutOffsets[11].bitPosition},
            {0,10, Beckhoff_EP2028, 0x7040, 1, &digOutOffsets[12].offset, &digOutOffsets[12].bitPosition},
            {0,10, Beckhoff_EP2028, 0x7050, 1, &digOutOffsets[13].offset, &digOutOffsets[13].bitPosition},
            {0,10, Beckhoff_EP2028, 0x7060, 1, &digOutOffsets[14].offset, &digOutOffsets[14].bitPosition},
            {0,10, Beckhoff_EP2028, 0x7070, 1, &digOutOffsets[15].offset, &digOutOffsets[15].bitPosition},


            // Encoder card registers for setting the counter
            {0,4, Beckhoff_EL5101, 0x7010, 0x03, &analogOutOffsets[0].offset, &analogOutOffsets[0].bitPosition},
            {0,4, Beckhoff_EL5101, 0x7010, 0x11, &analogOutOffsets[1].offset, &analogOutOffsets[1].bitPosition},

            // The bits of two bytes covers the valves on the block
            {0,8, SMC_EX260SEC3, 0x3101, 0x01, &analogOutOffsets[2].offset, &analogOutOffsets[2].bitPosition},
            {0,8, SMC_EX260SEC3, 0x3101, 0x02, &analogOutOffsets[3].offset, &analogOutOffsets[3].bitPosition},

            {0,11, Beckhoff_EP4174, 0x7000, 0x11, &analogOutOffsets[4].offset, &analogOutOffsets[4].bitPosition},
            {0,11, Beckhoff_EP4174, 0x7010, 0x11, &analogOutOffsets[5].offset, &analogOutOffsets[5].bitPosition},
            {0,11, Beckhoff_EP4174, 0x7020, 0x11, &analogOutOffsets[6].offset, &analogOutOffsets[6].bitPosition},
            {0,11, Beckhoff_EP4174, 0x7030, 0x11, &analogOutOffsets[7].offset, &analogOutOffsets[7].bitPosition},

            {}

      };


// process variables
static unsigned int counter = 0;

/*
* Check and report on the state of the domain.
*/
void
check_domain_state(void)
      {
      ec_domain_state_t ds;

      down(&master_sem);
      ecrt_domain_state(domain, &ds);
      up(&master_sem);

      if (ds.working_counter != domain_state.working_counter)
                  printk(KERN_INFO PFX "Domain: WC %u.\n", ds.working_counter);
      if (ds.wc_state != domain_state.wc_state)
                  printk(KERN_INFO PFX "Domain: State %u.\n", ds.wc_state);

      domain_state = ds;
      }



/*
* Check and report on the state of the master service.
*/
void
check_master_state(void)
      {
      ec_master_state_t ms;

      down(&master_sem);
      ecrt_master_state(master, &ms);
      up(&master_sem);

      if (ms.slaves_responding != master_state.slaves_responding)
                  printk(KERN_INFO PFX "%u slave(s).\n", ms.slaves_responding);
      if (ms.al_states != master_state.al_states)
                  printk(KERN_INFO PFX "AL states: 0x%02X.\n", ms.al_states);
      if (ms.link_up != master_state.link_up)
                  printk(KERN_INFO PFX "Link is %s.\n", ms.link_up ? "up" : "down");

      master_state = ms;
      }


void
check_slave_config_states(void)
      {
      /*
    ec_slave_config_state_t s;

    ecrt_slave_config_state(sc_ana_in, &s);

    if (s.al_state != sc_ana_in_state.al_state)
        printk(KERN_ERR PFX "AnaIn: State 0x%02X.\n", s.al_state);
    if (s.online != sc_ana_in_state.online)
        printk(KERN_ERR PFX "AnaIn: %s.\n", s.online ? "online" : "offline");
    if (s.operational != sc_ana_in_state.operational)
        printk(KERN_ERR PFX "AnaIn: %soperational.\n",
                s.operational ? "" : "Not ");
      */
      }



void
cyclic_task(unsigned long data)
      {
      // receive process data
      down(&master_sem);
      ecrt_master_receive(master);
      ecrt_domain_process(domain);
      up(&master_sem);


      ktime_get_ts(&lastCycle_);

      // check process data state (optional)
      check_domain_state();

      if (counter)
            {
            counter--;
            }
      else
            {
            counter = FREQUENCY;

            // check for master state
            check_master_state();
            }

      // send process data
      down(&master_sem);
      ecrt_domain_queue(domain);
      ecrt_master_send(master);
      up(&master_sem);

      // restart timer
  timer.expires += HZ / FREQUENCY;
  add_timer(&timer);
      }


void
send_callback(void *cb_data)
      {

      struct timespec delta = timespec_sub(lastCycle_, lastSend_);
      ec_master_t *m = NULL;

      printk(KERN_INFO PFX "send_callback");

      if (timespec_compare(&delta, &interval) > 0)
            {

            printk(KERN_INFO PFX "ecrt_master_send_ext");
            m = (ec_master_t *) cb_data;
            down(&master_sem);
            ecrt_master_send_ext(m);
            up(&master_sem);

            ktime_get_ts(&lastSend_);
            }
      }

void
receive_callback(void *cb_data)
      {

      struct timespec delta = timespec_sub(lastCycle_, lastRead_);
      ec_master_t *m = NULL;

      printk(KERN_INFO PFX "receive_callback");

      if (timespec_compare(&delta, &interval) > 0)
            {

            printk(KERN_INFO PFX "ecrt_master_receive");
            m = (ec_master_t *) cb_data;

            down(&master_sem);
            ecrt_master_receive(m);
            up(&master_sem);

            ktime_get_ts(&lastRead_);
            }
      }

int
__init init_mini_module(void)
      {
  ec_slave_config_t *sc;
  int ret = -1;

      /*
      * Requests an EtherCAT master for realtime operation.
      * Before an application can access an EtherCAT master, it has to reserve one for exclusive use.
      * In userspace, this is a convenience function for ecrt_open_master() and ecrt_master_reserve().
      * This function has to be the first function an application has to call to use EtherCAT.
       * The function takes the index of the master as its argument.
       * The first master has index 0, the n-th master has index n - 1.
       * The number of masters has to be specified when loading the master module.
      */
  master = ecrt_request_master(0);
  if (!master)
            {
            printk(KERN_INFO PFX "Could not connect to the master service. Exiting.\n\n");
            ret = -EBUSY;
            goto out_return;
            }


      ktime_get_ts(&lastCycle_);
      ktime_get_ts(&lastSend_);
      ktime_get_ts(&lastRead_);

      // setup a comparison
      interval.tv_nsec = 50000;


      sema_init(&master_sem, 1);
      ecrt_master_callbacks(
            master,                                   // pointer to master
            send_callback,                // fp to send ext callback
            receive_callback,       // fp to receive callback
            master);                                  // parameter to callback


      /*
      * Creates a new process data domain.
      * For process data exchange, at least one process data domain is needed.
       * This method creates a new process data domain and returns a pointer to the new domain object.
       * This object can be used for registering PDOs and exchanging them in cyclic operation.
      */
      domain = ecrt_master_create_domain(master);
      if (!domain)
            {
            printk(KERN_INFO PFX "Could not create a domain on the master. Exiting.\n\n");
            goto out_release_master;
            }


      /*
      * Register the slaves on the master
      * Doing the method this way means the registration will work whether or
      * not the computer is connected to the network.
      */

      // Main Rack, Slot 1
      if (!(sc = ecrt_master_slave_config(
                  master,
                  0,1,                                            // Bus position
                  Beckhoff_EL1004               // Slave type
                  )))
            {
            printk(KERN_INFO PFX "Failed to get slave configuration.\n");
            ret = -ENOMEM;
            goto out_release_master;
            }

      if (ecrt_slave_config_pdos(sc, EC_END, slave_1_syncs))
            {
            printk(KERN_ERR PFX "Failed to configure PDOs.\n");
            ret = -ENOMEM;
            goto out_release_master;
            }

      // Main Rack, Slot 2
      if (!(sc = ecrt_master_slave_config(
                  master,
                  0,2,                                            // Bus position
                  Beckhoff_EL1004               // Slave type
                  )))
            {
            printk(KERN_ERR PFX "Failed to get slave configuration.\n");
            ret = -ENOMEM;
            goto out_release_master;
            }

      if (ecrt_slave_config_pdos(sc, EC_END, slave_2_syncs))
            {
            printk(KERN_ERR PFX "Failed to configure PDOs.\n");
            ret = -ENOMEM;
            goto out_release_master;
            }

      // Main Rack, Slot 3
      if (!(sc = ecrt_master_slave_config(
                  master,
                  0,3,                                            // Bus position
                  Beckhoff_EL1004               // Slave type
                  )))
            {
            printk(KERN_ERR PFX "Failed to get slave configuration.\n");
            ret = -ENOMEM;
            goto out_release_master;
            }

      if (ecrt_slave_config_pdos(sc, EC_END, slave_3_syncs))
            {
            printk(KERN_ERR PFX "Failed to configure PDOs.\n");
            ret = -ENOMEM;
            goto out_release_master;
            }

      // Main Rack, Slot 4
      if (!(sc = ecrt_master_slave_config(
                  master,
                  0,4,                                            // Bus position
                  Beckhoff_EL5101               // Slave type
                  )))
            {
            printk(KERN_ERR PFX "Failed to get slave configuration.\n");
            ret = -ENOMEM;
            goto out_release_master;
            }

      if (ecrt_slave_config_pdos(sc, EC_END, slave_4_syncs))
            {
            printk(KERN_ERR PFX "Failed to configure PDOs.\n");
            ret = -ENOMEM;
            goto out_release_master;
            }

      // Main Rack, Slot 5
      if (!(sc = ecrt_master_slave_config(
                  master,
                  0,5,                                            // Bus position
                  Beckhoff_EL2004               // Slave type
                  )))
            {
            printk(KERN_ERR PFX "Failed to get slave configuration.\n");
    ret = -ENOMEM;
    goto out_release_master;
            }

      if (ecrt_slave_config_pdos(sc, EC_END, slave_5_syncs))
            {
            printk(KERN_ERR PFX "Failed to configure PDOs.\n");
            ret = -ENOMEM;
            goto out_release_master;
            }

      // Main Rack, Slot 6
      if (!(sc = ecrt_master_slave_config(
                  master,
                  0,6,                                            // Bus position
                  Beckhoff_EL2004               // Slave type
                  )))
            {
            printk(KERN_ERR PFX "Failed to get slave configuration.\n");
            ret = -ENOMEM;
            goto out_release_master;
            }

      if (ecrt_slave_config_pdos(sc, EC_END, slave_6_syncs))
            {
            printk(KERN_ERR PFX "Failed to configure PDOs.\n");
            ret = -ENOMEM;
            goto out_release_master;
            }

      // Main Rack, Slot 7
      if (!(sc = ecrt_master_slave_config(
                  master,
                  0,7,                                            // Bus position
                  Beckhoff_EL6601               // Slave type
                  )))
            {
            printk(KERN_ERR PFX "Failed to get slave configuration.\n");
            ret = -ENOMEM;
            goto out_release_master;
            }

      if (ecrt_slave_config_pdos(sc, EC_END, slave_7_syncs))
            {
            printk(KERN_ERR PFX "Failed to configure PDOs.\n");
            ret = -ENOMEM;
            goto out_release_master;
            }

      // SMC EtherCAT Manifold, Slave 8, "EX260-SEC3"
      if (!(sc = ecrt_master_slave_config(
                  master,
                  0,8,                                            // Bus position
                  SMC_EX260SEC3                       // Slave type
                  )))
            {
            printk(KERN_ERR PFX "Failed to get slave configuration.\n");
            ret = -ENOMEM;
            goto out_release_master;
            }

      if (ecrt_slave_config_pdos(sc, EC_END, slave_8_syncs))
            {
            printk(KERN_ERR PFX "Failed to configure PDOs.\n");
            ret = -ENOMEM;
            goto out_release_master;
            }

      // EP1809 Input Fieldbox, Slave 9,
      if (!(sc = ecrt_master_slave_config(
                  master,
                  0,9,                                            // Bus position
                  Beckhoff_EP1809               // Slave type
                  )))
            {
            printk(KERN_ERR PFX "Failed to get slave configuration.\n");
            ret = -ENOMEM;
            goto out_release_master;
                  }

      if (ecrt_slave_config_pdos(sc, EC_END, slave_9_syncs))
            {
            printk(KERN_ERR PFX "Failed to configure PDOs.\n");
            ret = -ENOMEM;
            goto out_release_master;
            }

      // EP2028 Input Fieldbox, Slave 10,
      if (!(sc = ecrt_master_slave_config(
                  master,
                  0,10,                                           // Bus position
                  Beckhoff_EP2028               // Slave type
                  )))
            {
            printk(KERN_ERR PFX "Failed to get slave configuration.\n");
            ret = -ENOMEM;
            goto out_release_master;
            }

      if (ecrt_slave_config_pdos(sc, EC_END, slave_10_syncs))
            {
            printk(KERN_ERR PFX "Failed to configure PDOs.\n");
            ret = -ENOMEM;
            goto out_release_master;
            }

      // EP4174 Input Fieldbox, Slave 11,
      if (!(sc = ecrt_master_slave_config(
                  master,
                  0,11,                                           // Bus position
                  Beckhoff_EP4174               // Slave type
                  )))
            {
            printk(KERN_ERR PFX "Failed to get slave configuration.\n");
            ret = -ENOMEM;
            goto out_release_master;
            }

      if (ecrt_slave_config_pdos(sc, EC_END, slave_11_syncs))
            {
            printk(KERN_ERR PFX "Failed to configure PDOs.\n");
            ret = -ENOMEM;
            goto out_release_master;
            }

      // Create configuration for bus coupler
      sc = ecrt_master_slave_config(master, 0,0, Beckhoff_EK1100);
  if (!sc)
            {
            ret = -ENOMEM;
            goto out_release_master;
            }

      /*
       * Registers a bunch of PDO entries for a domain.
       */
      printk(KERN_ERR PFX "\nAttempting to register PDOs on domain...\n");
      if (ecrt_domain_reg_pdo_entry_list(domain, domain_regs))
            {
            printk(KERN_ERR PFX "PDO entry registration failed!\n");
            ret = -EBUSY;
            goto out_release_master;
            }

      /*
      * Finishes the configuration phase and prepares for cyclic operation.
      * This function tells the master that the configuration phase is finished and the realtime operation will begin.
      * The function allocates internal memory for the domains and calculates the logical FMMU addresses for domain members.
       * It tells the master state machine that the bus configuration is now to be applied
      */
      if (ecrt_master_activate(master))
            {
            ret = -EBUSY;
            goto out_release_master;;
            }

      /*
      * Returns the domain's process data.
       */
      if (!(processData = ecrt_domain_data(domain)))
            {
            ret = -EBUSY;
            goto out_release_master;
            }

      printk(KERN_INFO PFX "Starting cyclic thread.\n");
      init_timer(&timer);
      timer.function = cyclic_task;
      timer.expires = jiffies + 10;
      add_timer(&timer);

      printk(KERN_INFO PFX "Durability Service Started.\n");
      return 0;

out_release_master:
  printk(KERN_ERR PFX "Releasing master...\n");
  ecrt_release_master(master);
out_return:
  printk(KERN_ERR PFX "Failed to load. Aborting.\n");
      return ret;
      } // main


void
__exit cleanup_mini_module(void)
      {
      printk(KERN_INFO PFX "Stopping...\n");

      del_timer_sync(&timer);

      printk(KERN_INFO PFX "Releasing master...\n");
      ecrt_release_master(master);

      printk(KERN_INFO PFX "Unloading.\n");
      }

/*****************************************************************************/

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Thomas Bitsky <tbj at automateddesign.com>");
MODULE_DESCRIPTION("Durability test environment");

module_init(init_mini_module);
module_exit(cleanup_mini_module);

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.etherlab.org/pipermail/etherlab-users/attachments/20130703/fae31381/attachment-0003.htm>


More information about the Etherlab-users mailing list