# HG changeset patch # User Florian Pose # Date 1223627655 0 # Node ID 900f1124e8f8c5cb8d32acb8b60bd222934b69c2 # Parent 9844ac1262752796f37580c3baf53885cd40772e Memory-mapped process data. diff -r 9844ac126275 -r 900f1124e8f8 examples/user/main.c --- a/examples/user/main.c Fri Oct 10 07:58:48 2008 +0000 +++ b/examples/user/main.c Fri Oct 10 08:34:15 2008 +0000 @@ -46,13 +46,29 @@ /****************************************************************************/ +// Application parameters +#define PRIORITY 1 + // Optional features #define CONFIGURE_PDOS 1 -#define EL3152_ALT_PDOS 0 -#define SDO_ACCESS 0 -#define VOE_ACCESS 0 - -#define PRIORITY 1 + +/****************************************************************************/ + +// EtherCAT +static ec_master_t *master = NULL; + +static ec_domain_t *domain1 = NULL; + +static ec_slave_config_t *sc_ana_in = NULL; + +// Timer +static unsigned int sig_alarms = 0; +static unsigned int user_alarms = 0; + +/****************************************************************************/ + +// process data +static uint8_t *domain1_pd = NULL; #define BusCouplerPos 0, 0 #define AnaOutSlavePos 0, 1 @@ -64,29 +80,20 @@ #define Beckhoff_EL3152 0x00000002, 0x0c503052 #define Beckhoff_EL4102 0x00000002, 0x10063052 -/****************************************************************************/ - -static unsigned int sig_alarms = 0; -static unsigned int user_alarms = 0; - // offsets for Pdo entries -static unsigned int off_ana_in; +static unsigned int off_ana_in_status; +static unsigned int off_ana_in_value; static unsigned int off_ana_out; static unsigned int off_dig_out; const static ec_pdo_entry_reg_t domain1_regs[] = { -#if EL3152_ALT_PDOS - {AnaInSlavePos, Beckhoff_EL3152, 0x6401, 1, &off_ana_in}, -#else - {AnaInSlavePos, Beckhoff_EL3152, 0x3101, 2, &off_ana_in}, -#endif + {AnaInSlavePos, Beckhoff_EL3152, 0x3101, 1, &off_ana_in_status}, + {AnaInSlavePos, Beckhoff_EL3152, 0x3101, 2, &off_ana_in_value}, {AnaOutSlavePos, Beckhoff_EL4102, 0x3001, 1, &off_ana_out}, {DigOutSlavePos, Beckhoff_EL2004, 0x3001, 1, &off_dig_out}, {} }; -static ec_slave_config_t *sc_ana_in = NULL; - /*****************************************************************************/ #if CONFIGURE_PDOS @@ -102,17 +109,6 @@ {0x6401, 2, 16} // channel 2 value (alt.) }; -#if EL3152_ALT_PDOS -static ec_pdo_info_t el3152_pdos[] = { - {0x1A10, 2, el3152_pdo_entries + 4}, -}; - -static ec_sync_info_t el3152_syncs[] = { - {2, EC_DIR_OUTPUT}, - {3, EC_DIR_INPUT, 1, el3152_pdos}, - {0xff} -}; -#else static ec_pdo_info_t el3152_pdos[] = { {0x1A00, 2, el3152_pdo_entries}, {0x1A01, 2, el3152_pdo_entries + 2} @@ -123,7 +119,6 @@ {3, EC_DIR_INPUT, 2, el3152_pdos}, {0xff} }; -#endif // Analog out ------------------------- @@ -168,6 +163,28 @@ /****************************************************************************/ +void cyclic_task() +{ + ecrt_master_receive(master); + ecrt_domain_process(domain1); + +#if 0 +#if EL3152_ALT_PDOS + printf("AnaIn: value %u\n", + EC_READ_U16(domain1_pd + off_ana_in_value)); +#else + printf("AnaIn: state %u value %u\n", + EC_READ_U8(domain1_pd + off_ana_in_status), + EC_READ_U16(domain1_pd + off_ana_in_value)); +#endif +#endif + + ecrt_domain_queue(domain1); + ecrt_master_send(master); +} + +/****************************************************************************/ + void signal_handler(int signum) { switch (signum) { case SIGALRM: @@ -180,8 +197,6 @@ int main(int argc, char **argv) { - ec_master_t *master; - ec_domain_t *domain1; ec_slave_config_t *sc; struct sigaction sa; struct itimerval tv; @@ -244,6 +259,10 @@ if (ecrt_master_activate(master)) return -1; + if (!(domain1_pd = ecrt_domain_data(domain1))) { + return -1; + } + #if PRIORITY pid_t pid = getpid(); if (setpriority(PRIO_PROCESS, pid, -19)) @@ -271,7 +290,7 @@ printf("Started.\n"); while (1) { - sleep(1); + sleep(1); // always interrupted by SIGALRM #if 0 struct timeval t; @@ -280,9 +299,7 @@ #endif while (sig_alarms != user_alarms) { - ecrt_master_receive(master); - ecrt_master_send(master); - + cyclic_task(); user_alarms++; } } diff -r 9844ac126275 -r 900f1124e8f8 include/ecrt.h --- a/include/ecrt.h Fri Oct 10 07:58:48 2008 +0000 +++ b/include/ecrt.h Fri Oct 10 08:34:15 2008 +0000 @@ -780,6 +780,8 @@ registrations. */ ); +#ifdef __KERNEL__ + /** Returns the current size of the domain's process data. * * \return Size of the process data image. @@ -788,8 +790,6 @@ const ec_domain_t *domain /**< Domain. */ ); -#ifdef __KERNEL__ - /** Provide external memory to store the domain's process data. * * Call this after all Pdo entries have been registered and before activating @@ -808,12 +808,14 @@ /** 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. - * - * \attention In case of internal domain memory (default), this method may not - * be called before ecrt_master_activate(). + * - In kernel context: 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. In the latter case, this method may not be called before + * ecrt_master_activate(). + * + * - In userspace context: This method has to be called after + * ecrt_master_activate() to get the mapped domain process data memory. * * \return Pointer to the process data memory. */ diff -r 9844ac126275 -r 900f1124e8f8 lib/common.c --- a/lib/common.c Fri Oct 10 07:58:48 2008 +0000 +++ b/lib/common.c Fri Oct 10 08:34:15 2008 +0000 @@ -39,6 +39,7 @@ #include #include #include +#include #include "master.h" #include "master/ioctl.h" @@ -65,6 +66,9 @@ return 0; } + master->process_data = NULL; + master->process_data_size = 0; + snprintf(path, MAX_PATH_LEN - 1, "/dev/EtherCAT%u", master_index); master->fd = open(path, O_RDWR); @@ -89,6 +93,10 @@ void ecrt_release_master(ec_master_t *master) { + if (master->process_data) { + munmap(master->process_data, master->process_data_size); + } + close(master->fd); free(master); } diff -r 9844ac126275 -r 900f1124e8f8 lib/domain.c --- a/lib/domain.c Fri Oct 10 07:58:48 2008 +0000 +++ b/lib/domain.c Fri Oct 10 08:34:15 2008 +0000 @@ -38,7 +38,15 @@ /*****************************************************************************/ +#include +#include +#include +#include +#include + #include "domain.h" +#include "master.h" +#include "master/ioctl.h" /*****************************************************************************/ @@ -68,19 +76,43 @@ uint8_t *ecrt_domain_data(ec_domain_t *domain) { - return 0; + if (!domain->process_data) { + int offset = 0; + + offset = ioctl(domain->master->fd, EC_IOCTL_DOMAIN_OFFSET, + domain->index); + if (offset == -1) { + fprintf(stderr, "Failed to get domain offset: %s\n", + strerror(errno)); + return NULL; + } + + domain->process_data = domain->master->process_data + offset; + } + + return domain->process_data; } /*****************************************************************************/ void ecrt_domain_process(ec_domain_t *domain) { + if (ioctl(domain->master->fd, EC_IOCTL_DOMAIN_PROCESS, + domain->index) == -1) { + fprintf(stderr, "Failed to process domain offset: %s\n", + strerror(errno)); + } } /*****************************************************************************/ void ecrt_domain_queue(ec_domain_t *domain) { + if (ioctl(domain->master->fd, EC_IOCTL_DOMAIN_QUEUE, + domain->index) == -1) { + fprintf(stderr, "Failed to queue domain offset: %s\n", + strerror(errno)); + } } /*****************************************************************************/ diff -r 9844ac126275 -r 900f1124e8f8 lib/domain.h --- a/lib/domain.h Fri Oct 10 07:58:48 2008 +0000 +++ b/lib/domain.h Fri Oct 10 08:34:15 2008 +0000 @@ -38,6 +38,7 @@ struct ec_domain { unsigned int index; ec_master_t *master; + uint8_t *process_data; }; /*****************************************************************************/ diff -r 9844ac126275 -r 900f1124e8f8 lib/master.c --- a/lib/master.c Fri Oct 10 07:58:48 2008 +0000 +++ b/lib/master.c Fri Oct 10 08:34:15 2008 +0000 @@ -36,6 +36,7 @@ #include #include #include +#include #include "master.h" #include "domain.h" @@ -64,6 +65,7 @@ domain->index = (unsigned int) index; domain->master = master; + domain->process_data = NULL; return domain; } @@ -106,12 +108,27 @@ int ecrt_master_activate(ec_master_t *master) { - if (ioctl(master->fd, EC_IOCTL_ACTIVATE, NULL) == -1) { + if (ioctl(master->fd, EC_IOCTL_ACTIVATE, + &master->process_data_size) == -1) { fprintf(stderr, "Failed to activate master: %s\n", strerror(errno)); return -1; } + if (master->process_data_size) { + master->process_data = mmap(0, master->process_data_size, + PROT_READ | PROT_WRITE, MAP_PRIVATE, master->fd, 0); + if (master->process_data == MAP_FAILED) { + fprintf(stderr, "Failed to map process data: %s", strerror(errno)); + master->process_data = NULL; + master->process_data_size = 0; + return -1; + } + + // Access the mapped region to cause the initial page fault + printf("pd: %x\n", master->process_data[0]); + } + return 0; } diff -r 9844ac126275 -r 900f1124e8f8 lib/master.h --- a/lib/master.h Fri Oct 10 07:58:48 2008 +0000 +++ b/lib/master.h Fri Oct 10 08:34:15 2008 +0000 @@ -37,6 +37,8 @@ struct ec_master { int fd; + uint8_t *process_data; + size_t process_data_size; }; /*****************************************************************************/ diff -r 9844ac126275 -r 900f1124e8f8 master/cdev.c --- a/master/cdev.c Fri Oct 10 07:58:48 2008 +0000 +++ b/master/cdev.c Fri Oct 10 08:34:15 2008 +0000 @@ -39,6 +39,8 @@ /*****************************************************************************/ #include +#include +#include #include "cdev.h" #include "master.h" @@ -52,6 +54,10 @@ int eccdev_open(struct inode *, struct file *); int eccdev_release(struct inode *, struct file *); long eccdev_ioctl(struct file *, unsigned int, unsigned long); +int eccdev_mmap(struct file *, struct vm_area_struct *); + +static struct page *eccdev_vma_nopage( + struct vm_area_struct *, unsigned long, int *); /*****************************************************************************/ @@ -59,7 +65,12 @@ .owner = THIS_MODULE, .open = eccdev_open, .release = eccdev_release, - .unlocked_ioctl = eccdev_ioctl + .unlocked_ioctl = eccdev_ioctl, + .mmap = eccdev_mmap +}; + +struct vm_operations_struct eccdev_vm_ops = { + .nopage = eccdev_vma_nopage }; /** \endcond */ @@ -71,6 +82,8 @@ typedef struct { ec_cdev_t *cdev; unsigned int requested; + uint8_t *process_data; + size_t process_data_size; } ec_cdev_priv_t; /*****************************************************************************/ @@ -1462,12 +1475,48 @@ ec_cdev_priv_t *priv /**< Private data structure of file handle. */ ) { + ec_domain_t *domain; + off_t offset; + if (unlikely(!priv->requested)) return -EPERM; + /* Get the sum of the domains' process data sizes. */ + + priv->process_data_size = 0; + + if (down_interruptible(&master->master_sem)) + return -EINTR; + + list_for_each_entry(domain, &master->domains, list) { + priv->process_data_size += ecrt_domain_size(domain); + } + + up(&master->master_sem); + + if (priv->process_data_size) { + priv->process_data = vmalloc(priv->process_data_size); + if (!priv->process_data) { + priv->process_data_size = 0; + return -ENOMEM; + } + + /* Set the memory as external process data memory for the domains. */ + + offset = 0; + list_for_each_entry(domain, &master->domains, list) { + ecrt_domain_external_memory(domain, priv->process_data + offset); + offset += ecrt_domain_size(domain); + } + } + if (ecrt_master_activate(master)) return -EIO; + if (copy_to_user((void __user *) arg, + &priv->process_data_size, sizeof(size_t))) + return -EFAULT; + return 0; } @@ -1788,6 +1837,94 @@ return ret; } +/*****************************************************************************/ + +/** Gets the domain's offset in the total process data. + */ +int ec_cdev_ioctl_domain_offset( + ec_master_t *master, /**< EtherCAT master. */ + unsigned long arg, /**< ioctl() argument. */ + ec_cdev_priv_t *priv /**< Private data structure of file handle. */ + ) +{ + int offset = 0; + const ec_domain_t *domain; + + if (unlikely(!priv->requested)) + return -EPERM; + + if (down_interruptible(&master->master_sem)) { + return -EINTR; + } + + list_for_each_entry(domain, &master->domains, list) { + if (domain->index == arg) { + up(&master->master_sem); + return offset; + } + offset += ecrt_domain_size(domain); + } + + up(&master->master_sem); + return -ESRCH; +} + +/*****************************************************************************/ + +/** Process the domain. + */ +int ec_cdev_ioctl_domain_process( + ec_master_t *master, /**< EtherCAT master. */ + unsigned long arg, /**< ioctl() argument. */ + ec_cdev_priv_t *priv /**< Private data structure of file handle. */ + ) +{ + ec_domain_t *domain; + + if (unlikely(!priv->requested)) + return -EPERM; + + if (down_interruptible(&master->master_sem)) + return -EINTR; + + if (!(domain = ec_master_find_domain(master, arg))) { + up(&master->master_sem); + return -ESRCH; + } + + ecrt_domain_process(domain); + up(&master->master_sem); + return 0; +} + +/*****************************************************************************/ + +/** Queue the domain. + */ +int ec_cdev_ioctl_domain_queue( + ec_master_t *master, /**< EtherCAT master. */ + unsigned long arg, /**< ioctl() argument. */ + ec_cdev_priv_t *priv /**< Private data structure of file handle. */ + ) +{ + ec_domain_t *domain; + + if (unlikely(!priv->requested)) + return -EPERM; + + if (down_interruptible(&master->master_sem)) + return -EINTR; + + if (!(domain = ec_master_find_domain(master, arg))) { + up(&master->master_sem); + return -ESRCH; + } + + ecrt_domain_queue(domain); + up(&master->master_sem); + return 0; +} + /****************************************************************************** * File operations *****************************************************************************/ @@ -1808,6 +1945,8 @@ priv->cdev = cdev; priv->requested = 0; + priv->process_data = NULL; + priv->process_data_size = 0; filp->private_data = priv; if (master->debug_level) @@ -1827,6 +1966,9 @@ if (priv->requested) ecrt_release_master(master); + if (priv->process_data) + vfree(priv->process_data); + if (master->debug_level) EC_DBG("File closed.\n"); kfree(priv); @@ -1953,9 +2095,68 @@ if (!(filp->f_mode & FMODE_WRITE)) return -EPERM; return ec_cdev_ioctl_sc_sdo(master, arg, priv); + case EC_IOCTL_DOMAIN_OFFSET: + return ec_cdev_ioctl_domain_offset(master, arg, priv); + case EC_IOCTL_DOMAIN_PROCESS: + if (!(filp->f_mode & FMODE_WRITE)) + return -EPERM; + return ec_cdev_ioctl_domain_process(master, arg, priv); + case EC_IOCTL_DOMAIN_QUEUE: + if (!(filp->f_mode & FMODE_WRITE)) + return -EPERM; + return ec_cdev_ioctl_domain_queue(master, arg, priv); default: return -ENOTTY; } } /*****************************************************************************/ + +int eccdev_mmap( + struct file *filp, + struct vm_area_struct *vma + ) +{ + ec_cdev_priv_t *priv = (ec_cdev_priv_t *) filp->private_data; + + if (priv->cdev->master->debug_level) + EC_DBG("mmap()\n"); + + vma->vm_ops = &eccdev_vm_ops; + vma->vm_flags |= VM_RESERVED; /* Pages will not be swapped out */ + vma->vm_private_data = priv; + + return 0; +} + +/*****************************************************************************/ + +struct page *eccdev_vma_nopage( + struct vm_area_struct *vma, + unsigned long address, + int *type + ) +{ + unsigned long offset; + struct page *page = NOPAGE_SIGBUS; + ec_cdev_priv_t *priv = (ec_cdev_priv_t *) vma->vm_private_data; + + offset = (address - vma->vm_start) + (vma->vm_pgoff << PAGE_SHIFT); + + if (offset >= priv->process_data_size) + return NOPAGE_SIGBUS; + + page = vmalloc_to_page(priv->process_data + offset); + + if (priv->cdev->master->debug_level) + EC_DBG("Nopage fault vma, address = %#lx, offset = %#lx, page = %p\n", + address, offset, page); + + get_page(page); + if (type) + *type = VM_FAULT_MINOR; + + return page; +} + +/*****************************************************************************/ diff -r 9844ac126275 -r 900f1124e8f8 master/ioctl.h --- a/master/ioctl.h Fri Oct 10 07:58:48 2008 +0000 +++ b/master/ioctl.h Fri Oct 10 08:34:15 2008 +0000 @@ -82,7 +82,7 @@ #define EC_IOCTL_REQUEST EC_IO(0x16) #define EC_IOCTL_CREATE_DOMAIN EC_IO(0x17) #define EC_IOCTL_CREATE_SLAVE_CONFIG EC_IOWR(0x18, ec_ioctl_config_t) -#define EC_IOCTL_ACTIVATE EC_IO(0x19) +#define EC_IOCTL_ACTIVATE EC_IOR(0x19, size_t) #define EC_IOCTL_SEND EC_IO(0x1a) #define EC_IOCTL_RECEIVE EC_IO(0x1b) #define EC_IOCTL_SC_SYNC EC_IOW(0x1c, ec_ioctl_config_t) @@ -92,6 +92,9 @@ #define EC_IOCTL_SC_CLEAR_ENTRIES EC_IOW(0x1f, ec_ioctl_config_pdo_t) #define EC_IOCTL_SC_REG_PDO_ENTRY EC_IOWR(0x20, ec_ioctl_reg_pdo_entry_t) #define EC_IOCTL_SC_SDO EC_IOW(0x21, ec_ioctl_sc_sdo_t) +#define EC_IOCTL_DOMAIN_OFFSET EC_IO(0x22) +#define EC_IOCTL_DOMAIN_PROCESS EC_IO(0x23) +#define EC_IOCTL_DOMAIN_QUEUE EC_IO(0x24) #define EC_IOCTL_STRING_SIZE 64