fp@2433: /***************************************************************************** fp@2433: * fp@2433: * $Id$ fp@2433: * fp@2433: * Copyright (C) 2009-2010 Moehwald GmbH B. Benner fp@2433: * 2011 IgH Andreas Stewering-Bone fp@2433: * 2012 Florian Pose fp@2433: * fp@2433: * This file is part of the IgH EtherCAT master. fp@2433: * fp@2433: * The IgH EtherCAT master is free software; you can redistribute it and/or fp@2433: * modify it under the terms of the GNU General Public License as published fp@2433: * by the Free Software Foundation; version 2 of the License. fp@2433: * fp@2433: * The IgH EtherCAT master is distributed in the hope that it will be useful, fp@2433: * but WITHOUT ANY WARRANTY; without even the implied warranty of fp@2433: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General fp@2433: * Public License for more details. fp@2433: * fp@2433: * You should have received a copy of the GNU General Public License along fp@2433: * with the IgH EtherCAT master. If not, see . fp@2433: * fp@2433: * The license mentioned above concerns the source code only. Using the fp@2433: * EtherCAT technology and brand is only permitted in compliance with the fp@2433: * industrial property and similar rights of Beckhoff Automation GmbH. fp@2433: * fp@2433: ****************************************************************************/ fp@2433: fp@2524: /** \file fp@2524: * RTDM interface. fp@2524: */ fp@2524: fp@2433: #include fp@2433: #include fp@2433: #include fp@2433: edouard@2700: #include fp@2433: fp@2433: #include "master.h" fp@2433: #include "ioctl.h" fp@2433: #include "rtdm.h" fp@2433: fp@2433: /** Set to 1 to enable device operations debugging. fp@2433: */ fp@2433: #define DEBUG 0 fp@2433: edouard@2707: edouard@2707: /****************************************************************************/ edouard@2707: edouard@2707: int ec_rtdm_open(struct rtdm_fd *, int); edouard@2707: void ec_rtdm_close(struct rtdm_fd *); edouard@2719: int ec_rtdm_rt_ioctl(struct rtdm_fd *, edouard@2719: unsigned int, void __user *); edouard@2707: int ec_rtdm_ioctl(struct rtdm_fd *, fp@2433: unsigned int, void __user *); edouard@2712: int ec_rtdm_mmap(struct rtdm_fd *fd, struct vm_area_struct *vma); fp@2433: fp@2433: /****************************************************************************/ fp@2433: edouard@2700: static struct rtdm_driver ec_rtdm_driver = { edouard@2700: .profile_info = RTDM_PROFILE_INFO(foo, edouard@2700: RTDM_CLASS_EXPERIMENTAL, edouard@2700: 222, edouard@2700: 1), edouard@2700: .device_flags = RTDM_NAMED_DEVICE, edouard@2700: .device_count = 1, edouard@2707: .context_size = sizeof(ec_ioctl_context_t), edouard@2700: .ops = { edouard@2707: .open = ec_rtdm_open, edouard@2707: .close = ec_rtdm_close, edouard@2719: .ioctl_rt = ec_rtdm_rt_ioctl, edouard@2712: .ioctl_nrt = ec_rtdm_ioctl, edouard@2712: .mmap = ec_rtdm_mmap edouard@2700: }, edouard@2700: }; edouard@2700: fp@2524: /** Initialize an RTDM device. fp@2524: * fp@2524: * \return Zero on success, otherwise a negative error code. fp@2524: */ fp@2433: int ec_rtdm_dev_init( fp@2524: ec_rtdm_dev_t *rtdm_dev, /**< EtherCAT RTDM device. */ fp@2524: ec_master_t *master /**< EtherCAT master. */ fp@2433: ) fp@2433: { fp@2433: int ret; edouard@2707: char* devlabel; edouard@2700: fp@2433: rtdm_dev->master = master; fp@2433: fp@2433: rtdm_dev->dev = kzalloc(sizeof(struct rtdm_device), GFP_KERNEL); fp@2433: if (!rtdm_dev->dev) { fp@2433: EC_MASTER_ERR(master, "Failed to reserve memory for RTDM device.\n"); fp@2433: return -ENOMEM; fp@2433: } edouard@2700: edouard@2707: devlabel = kzalloc(RTDM_MAX_DEVNAME_LEN+1, GFP_KERNEL); edouard@2707: if (!devlabel) { edouard@2700: EC_MASTER_ERR(master, "Failed to reserve memory for RTDM device name.\n"); edouard@2700: return -ENOMEM; edouard@2700: kfree(rtdm_dev->dev); edouard@2700: } edouard@2707: snprintf(devlabel, RTDM_MAX_DEVNAME_LEN, edouard@2707: "EtherCAT%u", master->index); edouard@2707: edouard@2707: rtdm_dev->dev->label = devlabel; edouard@2700: rtdm_dev->dev->driver = &ec_rtdm_driver; fp@2433: rtdm_dev->dev->device_data = rtdm_dev; /* pointer to parent */ fp@2433: edouard@2700: fp@2433: EC_MASTER_INFO(master, "Registering RTDM device %s.\n", edouard@2707: devlabel); fp@2433: ret = rtdm_dev_register(rtdm_dev->dev); fp@2433: if (ret) { fp@2433: EC_MASTER_ERR(master, "Initialization of RTDM interface failed" fp@2433: " (return value %i).\n", ret); edouard@2700: kfree(rtdm_dev->dev->label); fp@2433: kfree(rtdm_dev->dev); fp@2433: } fp@2433: fp@2433: return ret; fp@2433: } fp@2433: fp@2433: /****************************************************************************/ fp@2433: fp@2524: /** Clear an RTDM device. fp@2524: */ fp@2433: void ec_rtdm_dev_clear( fp@2524: ec_rtdm_dev_t *rtdm_dev /**< EtherCAT RTDM device. */ fp@2433: ) fp@2433: { fp@2433: EC_MASTER_INFO(rtdm_dev->master, "Unregistering RTDM device %s.\n", edouard@2700: rtdm_dev->dev->label); edouard@2707: rtdm_dev_unregister(rtdm_dev->dev); fp@2433: edouard@2700: kfree(rtdm_dev->dev->label); fp@2433: kfree(rtdm_dev->dev); fp@2433: } fp@2433: fp@2433: /****************************************************************************/ fp@2433: fp@2433: /** Driver open. fp@2524: * fp@2524: * \return Always zero (success). fp@2433: */ fp@2433: int ec_rtdm_open( edouard@2706: struct rtdm_fd *fd, /**< User data. */ fp@2524: int oflags /**< Open flags. */ fp@2433: ) fp@2433: { edouard@2707: ec_ioctl_context_t *ctx = (ec_ioctl_context_t *) rtdm_fd_to_private(fd); edouard@2707: #if DEBUG edouard@2707: ec_rtdm_dev_t *rtdm_dev = (ec_rtdm_dev_t *) rtdm_fd_device(fd)->device_data; edouard@2707: #endif edouard@2707: edouard@2707: ctx->writable = oflags & O_WRONLY || oflags & O_RDWR; edouard@2707: ctx->requested = 0; edouard@2707: ctx->process_data = NULL; edouard@2707: ctx->process_data_size = 0; fp@2433: fp@2433: #if DEBUG fp@2433: EC_MASTER_INFO(rtdm_dev->master, "RTDM device %s opened.\n", fp@2433: context->device->device_name); fp@2433: #endif fp@2433: return 0; fp@2433: } fp@2433: fp@2433: /****************************************************************************/ fp@2433: fp@2433: /** Driver close. fp@2524: * fp@2524: * \return Always zero (success). fp@2524: */ edouard@2707: void ec_rtdm_close( edouard@2706: struct rtdm_fd *fd /**< User data. */ fp@2524: ) fp@2433: { edouard@2707: ec_ioctl_context_t *ctx = (ec_ioctl_context_t *) rtdm_fd_to_private(fd); edouard@2707: ec_rtdm_dev_t *rtdm_dev = (ec_rtdm_dev_t *) rtdm_fd_device(fd)->device_data; edouard@2707: edouard@2707: if (ctx->requested) { fp@2433: ecrt_release_master(rtdm_dev->master); fp@2433: } fp@2433: fp@2433: #if DEBUG fp@2433: EC_MASTER_INFO(rtdm_dev->master, "RTDM device %s closed.\n", fp@2433: context->device->device_name); fp@2433: #endif fp@2433: } fp@2433: fp@2433: /****************************************************************************/ fp@2433: fp@2433: /** Driver ioctl. fp@2524: * fp@2524: * \return ioctl() return code. fp@2433: */ edouard@2719: int ec_rtdm_rt_ioctl( edouard@2719: struct rtdm_fd *fd, /**< User data. */ edouard@2719: unsigned int request, /**< Request. */ edouard@2719: void __user *arg /**< Argument. */ edouard@2719: ) edouard@2719: { edouard@2719: switch (request) { edouard@2719: /* edouard@2719: Requests to be handled directly in primary domain edouard@2720: edouard@2720: Note: list was made by selecting calls in ioctl.c edouard@2720: that seems not to make calls forbiden in primary mode edouard@2719: */ edouard@2720: case EC_IOCTL_MASTER_RESCAN: edouard@2719: case EC_IOCTL_SEND: edouard@2719: case EC_IOCTL_RECEIVE: edouard@2719: case EC_IOCTL_MASTER_STATE: edouard@2720: case EC_IOCTL_MASTER_LINK_STATE: edouard@2720: case EC_IOCTL_APP_TIME: edouard@2720: case EC_IOCTL_SYNC_REF: edouard@2720: case EC_IOCTL_SYNC_SLAVES: edouard@2720: case EC_IOCTL_REF_CLOCK_TIME: edouard@2720: case EC_IOCTL_SYNC_MON_QUEUE: edouard@2720: case EC_IOCTL_SYNC_MON_PROCESS: edouard@2720: case EC_IOCTL_SC_EMERG_POP: edouard@2720: case EC_IOCTL_SC_EMERG_CLEAR: edouard@2720: case EC_IOCTL_SC_EMERG_OVERRUNS: edouard@2720: case EC_IOCTL_SC_STATE: edouard@2719: case EC_IOCTL_DOMAIN_STATE: edouard@2719: case EC_IOCTL_DOMAIN_PROCESS: edouard@2719: case EC_IOCTL_DOMAIN_QUEUE: edouard@2720: case EC_IOCTL_SDO_REQUEST_INDEX: edouard@2720: case EC_IOCTL_SDO_REQUEST_TIMEOUT: edouard@2720: case EC_IOCTL_SDO_REQUEST_STATE: edouard@2720: case EC_IOCTL_SDO_REQUEST_READ: edouard@2720: case EC_IOCTL_SDO_REQUEST_WRITE: edouard@2720: case EC_IOCTL_SDO_REQUEST_DATA: edouard@2720: case EC_IOCTL_REG_REQUEST_DATA: edouard@2720: case EC_IOCTL_REG_REQUEST_STATE: edouard@2720: case EC_IOCTL_REG_REQUEST_WRITE: edouard@2720: case EC_IOCTL_REG_REQUEST_READ: edouard@2720: case EC_IOCTL_VOE_SEND_HEADER: edouard@2720: case EC_IOCTL_VOE_REC_HEADER: edouard@2720: case EC_IOCTL_VOE_READ: edouard@2720: case EC_IOCTL_VOE_READ_NOSYNC: edouard@2720: case EC_IOCTL_VOE_WRITE: edouard@2720: case EC_IOCTL_VOE_EXEC: edouard@2720: case EC_IOCTL_VOE_DATA: edouard@2719: return ec_rtdm_ioctl(fd, request, arg); edouard@2719: default: edouard@2719: break; edouard@2719: } edouard@2719: /* When a call is not supposed to happen in primary domain, edouard@2719: syscall catches -ENOSYS, then switch to secondary mode, edouard@2719: and calls .ioctl_nrt */ edouard@2719: return -ENOSYS; edouard@2719: } edouard@2719: fp@2433: int ec_rtdm_ioctl( edouard@2706: struct rtdm_fd *fd, /**< User data. */ fp@2433: unsigned int request, /**< Request. */ fp@2433: void __user *arg /**< Argument. */ fp@2433: ) fp@2433: { edouard@2707: ec_ioctl_context_t *ctx = (ec_ioctl_context_t *) rtdm_fd_to_private(fd); edouard@2707: ec_rtdm_dev_t *rtdm_dev = (ec_rtdm_dev_t *) rtdm_fd_device(fd)->device_data; fp@2433: fp@2433: #if DEBUG fp@2433: EC_MASTER_INFO(rtdm_dev->master, "ioctl(request = %u, ctl = %02x)" fp@2433: " on RTDM device %s.\n", request, _IOC_NR(request), fp@2433: context->device->device_name); fp@2433: #endif edouard@2707: return ec_ioctl_rtdm(rtdm_dev->master, ctx, request, arg); fp@2433: } fp@2433: fp@2433: /****************************************************************************/ fp@2433: fp@2433: /** Memory-map process data to user space. fp@2524: * edouard@2712: */ edouard@2712: int ec_rtdm_mmap(struct rtdm_fd *fd, struct vm_area_struct *vma) edouard@2712: { edouard@2713: size_t len; edouard@2713: ec_ioctl_context_t *ctx = (ec_ioctl_context_t *) rtdm_fd_to_private(fd); edouard@2713: len = vma->vm_end - vma->vm_start; edouard@2713: if (ctx->process_data_size != len) edouard@2713: return -EINVAL; edouard@2713: return rtdm_mmap_kmem(vma, (void *)ctx->process_data); edouard@2713: } edouard@2713: edouard@2713: /****************************************************************************/