[etherlab-users] Antw: ec_master_simple_io() implementation

matthias luescher matthias.luescher at komaxgroup.com
Tue Jun 3 09:53:52 CEST 2008


Hello Anthony

>> Have anyone done any userspace interfacing? I would appreciate any
>> hint on how
>> to do it in a least tiresome way.

As I do not know exactly what you are planning to do I am not sure if the following sample might help anything:
I also wanted to do some user space tests (with a PREEMPT_RT kernel) with the current 1.3.2. EtherLab master. The code below is a very quick and dirty merge of the mini.c sample and a character device sample. After inserting the module ec_mini.ko you can do some very basic IO toggling by reading something from /dev/ecrouter (e.g. by executing "cat /dev/ecrouter").

Below the C code is a shell script to insert ec_mini.ko and to add the appropriate node in the /dev directory.

Regards
Matthias


/******************************************************************************
  *
 *  Copyright (C) 2006  Florian Pose, Ingenieurgemeinschaft IgH
 *  Copyright (C) 2008  Matthias Luescher
 *
 *  This file is derived from the IgH EtherCAT Master code.
 *
 *  The IgH EtherCAT Master is free software; you can redistribute it
 *  and/or modify it under the terms of the GNU General Public License
 *  as published by the Free Software Foundation; either version 2 of the
 *  License, or (at your option) any later version.
 *
 *  The IgH EtherCAT Master is distributed in the hope that it will be
 *  useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with the IgH EtherCAT Master; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *  The right to use EtherCAT Technology is granted and comes free of
 *  charge under condition of compatibility of product made by
 *  Licensee. People intending to distribute/sell products based on the
 *  code, have to sign an agreement to guarantee that products using
 *  software based on IgH EtherCAT master stay compatible with the actual
 *  EtherCAT specification (which are released themselves as an open
 *  standard) as the (only) precondition to have the right to use EtherCAT
 *  Technology, IP and trade marks.
 *
 *****************************************************************************/

#include <linux/kernel.h>
#include <asm/uaccess.h>  /* for put_user */
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/timer.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>

#include "../../include/ecrt.h" // EtherCAT realtime interface
#include "../../include/ecdb.h" // EtherCAT slave database

#define PFX "ec_mini: "

#define FREQUENCY 100

//#define KBUS

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

static int device_open(struct inode *, struct file *);
static int device_release(struct inode *, struct file *);
static ssize_t device_read(struct file *, char *, size_t, loff_t *);
static ssize_t device_write(struct file *, const char *, size_t, loff_t *);

#define SUCCESS 0
#define DEVICE_NAME "ecrouter" /* Dev name as it appears in /proc/devices   */

/* Global variables are declared as static, so are global within the file. */
static int Major;            /* Major number assigned to our device driver */
static int Device_Open = 0;  /* Is device open?  Used to prevent multiple  */
	                     /* access to the device                       */
	
static struct file_operations fops = {
  .owner = THIS_MODULE, 
  .read = device_read, 
  .write = device_write,
  .open = device_open,
  .release = device_release
};

#define USE_TIMER 0
#if USE_TIMER
static struct timer_list timer;
#endif

// EtherCAT
static ec_master_t *master = NULL;
static ec_domain_t *domain1 = NULL;
spinlock_t master_lock = SPIN_LOCK_UNLOCKED;
static ec_master_status_t master_status, old_status = {};

// data fields
#ifdef KBUS
static void *r_inputs;
static void *r_outputs;
#endif

static void *r_dig_in;
static void *r_dig_out;
static void *r_ana_out;

#if 1
const static ec_pdo_reg_t domain1_pdo_regs[] = {
    {"1",      Beckhoff_EL1014v2_Inputs,    &r_dig_in},
    {"2",      Beckhoff_EL2004v2_Outputs,   &r_dig_out},
    {"3",      Beckhoff_EL4132v2_Output1,   &r_ana_out},
    {}
};
#endif

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

void run(unsigned long data)
{
#if USE_TIMER
    static unsigned int counter = 0;
    static unsigned int blink = 0;

    // receive
    spin_lock(&master_lock);
    ecrt_master_receive(master);
    ecrt_domain_process(domain1);
    spin_unlock(&master_lock);

    // process data
    // k_pos = EC_READ_U32(r_ssi);
    EC_WRITE_U8(r_dig_out, 1 << blink);

    if (counter) {
        counter--;
    }
    else {
        counter = FREQUENCY / 10;
        blink = (blink + 1) % 4;

        spin_lock(&master_lock);
        ecrt_master_get_status(master, &master_status);
        spin_unlock(&master_lock);

        if (master_status.bus_status != old_status.bus_status) {
            printk(KERN_INFO PFX "bus status changed to %i.\n",
                    master_status.bus_status);
        }
        if (master_status.bus_tainted != old_status.bus_tainted) {
            printk(KERN_INFO PFX "tainted flag changed to %u.\n",
                    master_status.bus_tainted);
        }
        if (master_status.slaves_responding !=
                old_status.slaves_responding) {
            printk(KERN_INFO PFX "slaves_responding changed to %u.\n",
                    master_status.slaves_responding);
        }
       
        old_status = master_status;
    }

#ifdef KBUS
    EC_WRITE_U8(r_outputs + 2, blink ? 0xFF : 0x00);
#endif

    // send
    spin_lock(&master_lock);
    ecrt_domain_queue(domain1);
    spin_unlock(&master_lock);

    spin_lock(&master_lock);
    ecrt_master_send(master);
    spin_unlock(&master_lock);

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

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

int request_lock(void *data)
{
    spin_lock(&master_lock);
    return 0; // access allowed
}

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

void release_lock(void *data)
{
    spin_unlock(&master_lock);
}

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

int __init init_mini_module(void)
{
#if 1
    ec_slave_t *slave;
#endif

    printk(KERN_INFO PFX "Starting...\n");

    if (!(master = ecrt_request_master(0))) {
        printk(KERN_ERR PFX "Requesting master 0 failed!\n");
        goto out_return;
    }

    ecrt_master_callbacks(master, request_lock, release_lock, NULL);

    printk(KERN_INFO PFX "Registering domain...\n");
    if (!(domain1 = ecrt_master_create_domain(master))) {
        printk(KERN_ERR PFX "Domain creation failed!\n");
        goto out_release_master;
    }

#if 0
    printk(KERN_INFO PFX "Configuring alternative PDO mapping...\n");
    if (!(slave = ecrt_master_get_slave(master, "4", Beckhoff_EL5101)))
        goto out_release_master;

    if (ecrt_slave_pdo_mapping(slave, EC_DIR_INPUT, 2, 0x1A00, 0x1A02))
        goto out_release_master;
#endif

    printk(KERN_INFO PFX "Registering PDOs...\n");
#if 1
    if (ecrt_domain_register_pdo_list(domain1, domain1_pdo_regs)) {
        printk(KERN_ERR PFX "PDO registration failed!\n");
        goto out_release_master;
    }
#endif

#ifdef KBUS
    if (!(slave = ecrt_master_get_slave(master, "0", Beckhoff_BK1120)))
        goto out_release_master;
    
    if (!ecrt_domain_register_pdo_range(
                domain1, slave, EC_DIR_OUTPUT, 0, 4, &r_outputs)) {
        printk(KERN_ERR PFX "PDO registration failed!\n");
        goto out_release_master;
    }
    
    if (!ecrt_domain_register_pdo_range(
                domain1, slave, EC_DIR_INPUT, 0, 4, &r_inputs)) {
        printk(KERN_ERR PFX "PDO registration failed!\n");
        goto out_release_master;
    }
#endif

#if 0
    if (!(slave = ecrt_master_get_slave(master, "4", Beckhoff_EL5001)))
        goto out_release_master;

    if (ecrt_slave_conf_sdo8(slave, 0x4061, 1, 0))
        goto out_release_master;
#endif

#if 1
#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;
    }

#if USE_TIMER
    printk(KERN_INFO PFX "Starting cyclic sample thread.\n");
    init_timer(&timer);
    timer.function = run;
    timer.expires = jiffies + 10;
    add_timer(&timer);
#endif

    printk(KERN_INFO PFX "Started.\n");

    Major = register_chrdev(0, DEVICE_NAME, &fops);

    if (Major < 0) {
        printk(KERN_ALERT "Registering char device failed with %d\n", Major);
        goto out_release_master;
    }

    printk(KERN_INFO "I was assigned major number %d. To talk to\n", Major);
    printk(KERN_INFO "the driver, create a dev file with\n");
    printk(KERN_INFO "'mknod /dev/%s c %d 0'.\n", DEVICE_NAME, Major);
    printk(KERN_INFO "Remove the device file and module when done.\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 -1;
}

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

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

#if USE_TIMER
    del_timer_sync(&timer);
#endif

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

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

    /* 
    * Unregister the device 
    */
    unregister_chrdev(Major, DEVICE_NAME);
}

/*
 * Methods
 */

/* 
 * Called when a process tries to open the device file, like
 * "cat /dev/mycharfile"
 */
static int device_open(struct inode *inode, struct file *file)
{
    if (Device_Open)
        return -EBUSY;

    Device_Open++;
    try_module_get(THIS_MODULE);

    return SUCCESS;
}

/* 
 * Called when a process closes the device file.
 */
static int device_release(struct inode *inode, struct file *file)
{
    Device_Open--;          /* We're now ready for our next caller */

    /* 
     * Decrement the usage count, or else once you opened the file, you'll
     * never get get rid of the module. 
     */
    module_put(THIS_MODULE);

    return 0;
}

/* 
 * Called when a process, which already opened the dev file, attempts to
 * read from it.
 */
static ssize_t device_read(struct file *filp,   /* see include/linux/fs.h   */
                           char *buffer,        /* buffer to fill with data */
                           size_t length,       /* length of the buffer     */
                           loff_t * offset)
{
    /*
     * Number of bytes actually written to the buffer 
     */
    int bytes_read = 0;

    /* 
     * Actually put the data into the buffer 
     */
    while (length) {
        /* 
         * The buffer is in the user data segment, not the kernel 
         * segment so "*" assignment won't work.  We have to use 
         * put_user which copies data from the kernel data segment to
         * the user data segment. 
         */
        put_user('a', buffer++);

        length--;
        bytes_read++;
    }

    static unsigned int callCounter = 0;
    unsigned int out1, out2, out3, out4;

    // receive
    spin_lock(&master_lock);
    ecrt_master_receive(master);
    ecrt_domain_process(domain1);
    spin_unlock(&master_lock);

    // process data
    unsigned int blink = ((callCounter & 1) < 1)  ? 0xF : 0x0;
    callCounter++;

    // k_pos = EC_READ_U32(r_ssi);
    EC_WRITE_U8(r_dig_out, blink);

    if ( (callCounter & 1) == 0 )
    {
        EC_WRITE_S16(r_ana_out, 5000);
    }
    else
    {
        EC_WRITE_S16(r_ana_out, -5000);
    }

    spin_lock(&master_lock);
    ecrt_master_get_status(master, &master_status);
    spin_unlock(&master_lock);

    if (master_status.bus_status != old_status.bus_status) {
        printk(KERN_INFO PFX "bus status changed to %i.\n",
               master_status.bus_status);
    }
    if (master_status.bus_tainted != old_status.bus_tainted) {
        printk(KERN_INFO PFX "tainted flag changed to %u.\n",
               master_status.bus_tainted);
    }
    if (master_status.slaves_responding !=
            old_status.slaves_responding) {
        printk(KERN_INFO PFX "slaves_responding changed to %u.\n",
        master_status.slaves_responding);
    }
       
    old_status = master_status;

#ifdef KBUS
    EC_WRITE_U8(r_outputs + 2, blink ? 0xFF : 0x00);
#endif

    // send
    spin_lock(&master_lock);
    ecrt_domain_queue(domain1);
    spin_unlock(&master_lock);

    spin_lock(&master_lock);
    ecrt_master_send(master);
    spin_unlock(&master_lock);

    /* 
     * Most read functions return the number of bytes put into the buffer
     */
    return bytes_read;
}

/*  
 * Called when a process writes to dev file: echo "hi" > /dev/hello 
 */
static ssize_t
device_write(struct file *filp, const char *buff, size_t len, loff_t * off)
{
     printk(KERN_ALERT "Sorry, this operation isn't supported.\n");
     return -EINVAL;
}

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Florian Pose <fp at igh-essen.com>");
MODULE_DESCRIPTION("EtherCAT minimal test environment");

module_init(init_mini_module);
module_exit(cleanup_mini_module);

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






#!/bin/sh

module="ec_mini"
device="ecrouter"
directory="ethercat-1.3.2/examples/mini"

# invoke insmod
echo Inserting module: insmod ~/${directory}/${module}.ko
sudo insmod ~/${directory}/${module}.ko || exit 1

# remove stale nodes
sudo rm -f /dev/${device}

# create device node
major=$(awk "\$2==\"$device\" {print \$1}" /proc/devices)
echo Creating node: mknod /dev/${device} c $major 0
sudo mknod /dev/${device} c $major 0

exit 0




Note:
This e-mail is for the named person's use only. It may contain confidential and/or privileged information. If you have received this e-mail in error, please notify the sender immediately and delete the material from any system. Any unauthorized copying, disclosure, distribution or other use of this information by persons or entities other than the intended recipient is prohibited.
Thank You.




More information about the Etherlab-users mailing list