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: fp@2433: #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: fp@2433: /****************************************************************************/ fp@2433: fp@2433: /** Context structure for an open RTDM file handle. fp@2433: */ fp@2433: typedef struct { fp@2524: rtdm_user_info_t *user_info; /**< RTDM user data. */ fp@2522: ec_ioctl_context_t ioctl_ctx; /**< Context structure. */ fp@2433: } ec_rtdm_context_t; fp@2433: fp@2433: /****************************************************************************/ fp@2433: fp@2433: int ec_rtdm_open(struct rtdm_dev_context *, rtdm_user_info_t *, int); fp@2433: int ec_rtdm_close(struct rtdm_dev_context *, rtdm_user_info_t *); fp@2433: int ec_rtdm_ioctl(struct rtdm_dev_context *, rtdm_user_info_t *, fp@2433: unsigned int, void __user *); fp@2433: fp@2433: /****************************************************************************/ fp@2433: 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; fp@2433: 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: } fp@2433: fp@2433: rtdm_dev->dev->struct_version = RTDM_DEVICE_STRUCT_VER; fp@2433: rtdm_dev->dev->device_flags = RTDM_NAMED_DEVICE; fp@2433: rtdm_dev->dev->context_size = sizeof(ec_rtdm_context_t); fp@2433: snprintf(rtdm_dev->dev->device_name, RTDM_MAX_DEVNAME_LEN, fp@2433: "EtherCAT%u", master->index); fp@2433: rtdm_dev->dev->open_nrt = ec_rtdm_open; fp@2433: rtdm_dev->dev->ops.close_nrt = ec_rtdm_close; fp@2433: rtdm_dev->dev->ops.ioctl_rt = ec_rtdm_ioctl; fp@2433: rtdm_dev->dev->ops.ioctl_nrt = ec_rtdm_ioctl; fp@2433: rtdm_dev->dev->device_class = RTDM_CLASS_EXPERIMENTAL; fp@2433: rtdm_dev->dev->device_sub_class = 222; fp@2433: rtdm_dev->dev->driver_name = "EtherCAT"; fp@2433: rtdm_dev->dev->driver_version = RTDM_DRIVER_VER(1, 0, 2); fp@2433: rtdm_dev->dev->peripheral_name = rtdm_dev->dev->device_name; fp@2433: rtdm_dev->dev->provider_name = "EtherLab Community"; fp@2433: rtdm_dev->dev->proc_name = rtdm_dev->dev->device_name; fp@2433: rtdm_dev->dev->device_data = rtdm_dev; /* pointer to parent */ fp@2433: fp@2433: EC_MASTER_INFO(master, "Registering RTDM device %s.\n", fp@2433: rtdm_dev->dev->driver_name); 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); 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: int ret; fp@2433: fp@2433: EC_MASTER_INFO(rtdm_dev->master, "Unregistering RTDM device %s.\n", fp@2433: rtdm_dev->dev->driver_name); fp@2433: ret = rtdm_dev_unregister(rtdm_dev->dev, 1000 /* poll delay [ms] */); fp@2433: if (ret < 0) { fp@2433: EC_MASTER_WARN(rtdm_dev->master, fp@2433: "Failed to unregister RTDM device (code %i).\n", ret); fp@2433: } fp@2433: 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( fp@2524: struct rtdm_dev_context *context, /**< Context. */ fp@2524: rtdm_user_info_t *user_info, /**< User data. */ fp@2524: int oflags /**< Open flags. */ fp@2433: ) fp@2433: { fp@2433: ec_rtdm_context_t *ctx = (ec_rtdm_context_t *) context->dev_private; fp@2433: #if DEBUG fp@2433: ec_rtdm_dev_t *rtdm_dev = (ec_rtdm_dev_t *) context->device->device_data; fp@2433: #endif fp@2433: fp@2433: ctx->user_info = user_info; fp@2433: ctx->ioctl_ctx.writable = oflags & O_WRONLY || oflags & O_RDWR; fp@2433: ctx->ioctl_ctx.requested = 0; fp@2433: ctx->ioctl_ctx.process_data = NULL; fp@2433: ctx->ioctl_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: */ fp@2524: int ec_rtdm_close( fp@2524: struct rtdm_dev_context *context, /**< Context. */ fp@2524: rtdm_user_info_t *user_info /**< User data. */ fp@2524: ) fp@2433: { fp@2433: ec_rtdm_context_t *ctx = (ec_rtdm_context_t *) context->dev_private; fp@2433: ec_rtdm_dev_t *rtdm_dev = (ec_rtdm_dev_t *) context->device->device_data; fp@2433: fp@2433: if (ctx->ioctl_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: return 0; fp@2433: } fp@2433: fp@2433: /****************************************************************************/ fp@2433: fp@2433: /** Driver ioctl. fp@2524: * fp@2524: * \return ioctl() return code. fp@2433: */ fp@2433: int ec_rtdm_ioctl( fp@2433: struct rtdm_dev_context *context, /**< Context. */ fp@2433: rtdm_user_info_t *user_info, /**< User data. */ fp@2433: unsigned int request, /**< Request. */ fp@2433: void __user *arg /**< Argument. */ fp@2433: ) fp@2433: { fp@2433: ec_rtdm_context_t *ctx = (ec_rtdm_context_t *) context->dev_private; fp@2433: ec_rtdm_dev_t *rtdm_dev = (ec_rtdm_dev_t *) context->device->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 fp@2433: return ec_ioctl_rtdm(rtdm_dev->master, &ctx->ioctl_ctx, request, arg); fp@2433: } fp@2433: fp@2433: /****************************************************************************/ fp@2433: fp@2433: /** Memory-map process data to user space. fp@2524: * fp@2524: * \return Zero on success, otherwise a negative error code. fp@2524: */ fp@2524: int ec_rtdm_mmap( fp@2524: ec_ioctl_context_t *ioctl_ctx, /**< Context. */ fp@2524: void **user_address /**< Userspace address. */ fp@2524: ) fp@2433: { fp@2433: ec_rtdm_context_t *ctx = fp@2433: container_of(ioctl_ctx, ec_rtdm_context_t, ioctl_ctx); fp@2433: int ret; fp@2433: fp@2433: ret = rtdm_mmap_to_user(ctx->user_info, fp@2433: ioctl_ctx->process_data, ioctl_ctx->process_data_size, fp@2433: PROT_READ | PROT_WRITE, fp@2433: user_address, fp@2433: NULL, NULL); fp@2433: if (ret < 0) { fp@2433: return ret; fp@2433: } fp@2433: fp@2433: return 0; fp@2433: } fp@2433: fp@2433: /****************************************************************************/