Newer
Older
/******************************************************************************
*
* $Id$
*
Florian Pose
committed
* Copyright (C) 2006-2008 Florian Pose, Ingenieurgemeinschaft IgH
*
* This file is part of the IgH EtherCAT Master.
*
Florian Pose
committed
* The IgH EtherCAT Master is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2, as
* published by the Free Software Foundation.
Florian Pose
committed
* 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.
Florian Pose
committed
* 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 license mentioned above concerns the source code only. Using the
* EtherCAT technology and brand is only permitted in compliance with the
* industrial property and similar rights of Beckhoff Automation GmbH.
*
*****************************************************************************/
/**
\file
EtherCAT master character device.
*/
/*****************************************************************************/
#include <linux/module.h>
#include <linux/vmalloc.h>
#include <linux/mm.h>
#include "cdev.h"
#include "master.h"
#include "slave_config.h"
#include "ioctl.h"
/** Set to 1 to enable ioctl() command debugging.
*/
/** Set to 1 to enable ioctl() latency warnings.
*
* Requires CPU timestamp counter!
*/
#define DEBUG_LATENCY 0
/*****************************************************************************/
static int eccdev_open(struct inode *, struct file *);
static int eccdev_release(struct inode *, struct file *);
static long eccdev_ioctl(struct file *, unsigned int, unsigned long);
static int eccdev_mmap(struct file *, struct vm_area_struct *);
/** This is the kernel version from which the .fault member of the
* vm_operations_struct is usable.
*/
#define PAGE_FAULT_VERSION KERNEL_VERSION(2, 6, 23)
#if LINUX_VERSION_CODE >= PAGE_FAULT_VERSION
static int eccdev_vma_fault(struct vm_area_struct *, struct vm_fault *);
#else
static struct page *eccdev_vma_nopage(
struct vm_area_struct *, unsigned long, int *);
/*****************************************************************************/
/** File operation callbacks for the EtherCAT character device.
*/
static struct file_operations eccdev_fops = {
.owner = THIS_MODULE,
.open = eccdev_open,
.release = eccdev_release,
.unlocked_ioctl = eccdev_ioctl,
.mmap = eccdev_mmap
};
/** Callbacks for a virtual memory area retrieved with ecdevc_mmap().
*/
#if LINUX_VERSION_CODE >= PAGE_FAULT_VERSION
.fault = eccdev_vma_fault
#else
};
/*****************************************************************************/
/** Private data structure for file handles.
*/
typedef struct {
ec_cdev_t *cdev; /**< Character device. */
unsigned int requested; /**< Master wac requested via this file handle. */
uint8_t *process_data; /**< Total process data area. */
size_t process_data_size; /**< Size of the \a process_data. */
} ec_cdev_priv_t;
/*****************************************************************************/
/** Constructor.
*
* \return 0 in case of success, else < 0
*/
int ec_cdev_init(
ec_cdev_t *cdev, /**< EtherCAT master character device. */
ec_master_t *master, /**< Parent master. */
dev_t dev_num /**< Device number. */
)
cdev->master = master;
cdev_init(&cdev->cdev, &eccdev_fops);
cdev->cdev.owner = THIS_MODULE;
ret = cdev_add(&cdev->cdev,
EC_MASTER_ERR(master, "Failed to add character device!\n");
}
/*****************************************************************************/
/** Destructor.
*/
void ec_cdev_clear(ec_cdev_t *cdev /**< EtherCAT XML device */)
{
cdev_del(&cdev->cdev);
}
/*****************************************************************************/
/** Copies a string to an ioctl structure.
*/
void ec_cdev_strcpy(
char *target, /**< Target. */
const char *source /**< Source. */
)
{
if (source) {
strncpy(target, source, EC_IOCTL_STRING_SIZE);
target[EC_IOCTL_STRING_SIZE - 1] = 0;
} else {
target[0] = 0;
}
}
/*****************************************************************************/
/** Get module information.
*/
int ec_cdev_ioctl_module(
unsigned long arg /**< Userspace address to store the results. */
)
{
ec_ioctl_module_t data;
data.ioctl_version_magic = EC_IOCTL_VERSION_MAGIC;
data.master_count = ec_master_count();
if (copy_to_user((void __user *) arg, &data, sizeof(data)))
return -EFAULT;
return 0;
}
/*****************************************************************************/
/** Get master information.
*/
int ec_cdev_ioctl_master(
ec_master_t *master, /**< EtherCAT master. */
unsigned long arg /**< Userspace address to store the results. */
)
{
ec_ioctl_master_t data;
if (down_interruptible(&master->master_sem))
return -EINTR;
data.slave_count = master->slave_count;
data.config_count = ec_master_config_count(master);
data.domain_count = ec_master_domain_count(master);
data.eoe_handler_count = ec_master_eoe_handler_count(master);
data.phase = (uint8_t) master->phase;
data.active = (uint8_t) master->active;
up(&master->master_sem);
if (down_interruptible(&master->device_sem))
return -EINTR;
if (master->main_device.dev) {
memcpy(data.devices[0].address,
master->main_device.dev->dev_addr, ETH_ALEN);
} else {
memcpy(data.devices[0].address, master->main_mac, ETH_ALEN);
}
data.devices[0].attached = master->main_device.dev ? 1 : 0;
data.devices[0].link_state = master->main_device.link_state ? 1 : 0;
data.devices[0].tx_count = master->main_device.tx_count;
data.devices[0].rx_count = master->main_device.rx_count;
data.devices[0].tx_bytes = master->main_device.tx_bytes;
data.devices[0].tx_errors = master->main_device.tx_errors;
for (i = 0; i < EC_RATE_COUNT; i++) {
data.devices[0].tx_frame_rates[i] =
master->main_device.tx_frame_rates[i];
data.devices[0].tx_byte_rates[i] =
master->main_device.tx_byte_rates[i];
data.devices[0].loss_rates[i] = master->main_device.loss_rates[i];
}
if (master->backup_device.dev) {
memcpy(data.devices[1].address,
master->backup_device.dev->dev_addr, ETH_ALEN);
} else {
memcpy(data.devices[1].address, master->backup_mac, ETH_ALEN);
}
data.devices[1].attached = master->backup_device.dev ? 1 : 0;
data.devices[1].link_state = master->backup_device.link_state ? 1 : 0;
data.devices[1].tx_count = master->backup_device.tx_count;
data.devices[1].rx_count = master->backup_device.rx_count;
data.devices[1].tx_bytes = master->backup_device.tx_bytes;
data.devices[1].tx_errors = master->backup_device.tx_errors;
for (i = 0; i < EC_RATE_COUNT; i++) {
data.devices[1].tx_frame_rates[i] =
master->backup_device.tx_frame_rates[i];
data.devices[1].tx_byte_rates[i] =
master->backup_device.tx_byte_rates[i];
data.devices[1].loss_rates[i] = master->backup_device.loss_rates[i];
}
up(&master->device_sem);
data.app_time = master->app_time;
data.ref_clock =
master->dc_ref_clock ? master->dc_ref_clock->ring_position : 0xffff;
if (copy_to_user((void __user *) arg, &data, sizeof(data)))
return -EFAULT;
return 0;
}
/*****************************************************************************/
/** Get slave information.
*/
int ec_cdev_ioctl_slave(
ec_master_t *master, /**< EtherCAT master. */
unsigned long arg /**< Userspace address to store the results. */
)
{
ec_ioctl_slave_t data;
const ec_slave_t *slave;
if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
return -EFAULT;
}
if (down_interruptible(&master->master_sem))
return -EINTR;
if (!(slave = ec_master_find_slave_const(
master, 0, data.position))) {
up(&master->master_sem);
EC_MASTER_ERR(master, "Slave %u does not exist!\n", data.position);
return -EINVAL;
}
data.vendor_id = slave->sii.vendor_id;
data.product_code = slave->sii.product_code;
data.revision_number = slave->sii.revision_number;
data.serial_number = slave->sii.serial_number;
data.alias = slave->effective_alias;
data.boot_rx_mailbox_offset = slave->sii.boot_rx_mailbox_offset;
data.boot_rx_mailbox_size = slave->sii.boot_rx_mailbox_size;
data.boot_tx_mailbox_offset = slave->sii.boot_tx_mailbox_offset;
data.boot_tx_mailbox_size = slave->sii.boot_tx_mailbox_size;
data.std_rx_mailbox_offset = slave->sii.std_rx_mailbox_offset;
data.std_rx_mailbox_size = slave->sii.std_rx_mailbox_size;
data.std_tx_mailbox_offset = slave->sii.std_tx_mailbox_offset;
data.std_tx_mailbox_size = slave->sii.std_tx_mailbox_size;
data.mailbox_protocols = slave->sii.mailbox_protocols;
data.has_general_category = slave->sii.has_general;
data.coe_details = slave->sii.coe_details;
data.general_flags = slave->sii.general_flags;
data.current_on_ebus = slave->sii.current_on_ebus;
for (i = 0; i < EC_MAX_PORTS; i++) {
data.ports[i].desc = slave->ports[i].desc;
data.ports[i].link.link_up = slave->ports[i].link.link_up;
data.ports[i].link.loop_closed = slave->ports[i].link.loop_closed;
data.ports[i].link.signal_detected =
slave->ports[i].link.signal_detected;
data.ports[i].receive_time = slave->ports[i].receive_time;
if (slave->ports[i].next_slave) {
data.ports[i].next_slave =
slave->ports[i].next_slave->ring_position;
data.ports[i].delay_to_next_dc = slave->ports[i].delay_to_next_dc;
}
data.fmmu_bit = slave->base_fmmu_bit_operation;
data.dc_supported = slave->base_dc_supported;
data.dc_range = slave->base_dc_range;
Florian Pose
committed
data.has_dc_system_time = slave->has_dc_system_time;
data.transmission_delay = slave->transmission_delay;
data.error_flag = slave->error_flag;
data.sync_count = slave->sii.sync_count;
data.sdo_count = ec_slave_sdo_count(slave);
data.sii_nwords = slave->sii_nwords;
ec_cdev_strcpy(data.group, slave->sii.group);
ec_cdev_strcpy(data.image, slave->sii.image);
ec_cdev_strcpy(data.order, slave->sii.order);
ec_cdev_strcpy(data.name, slave->sii.name);
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
up(&master->master_sem);
if (copy_to_user((void __user *) arg, &data, sizeof(data)))
return -EFAULT;
return 0;
}
/*****************************************************************************/
/** Get slave sync manager information.
*/
int ec_cdev_ioctl_slave_sync(
ec_master_t *master, /**< EtherCAT master. */
unsigned long arg /**< Userspace address to store the results. */
)
{
ec_ioctl_slave_sync_t data;
const ec_slave_t *slave;
const ec_sync_t *sync;
if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
return -EFAULT;
}
if (down_interruptible(&master->master_sem))
return -EINTR;
if (!(slave = ec_master_find_slave_const(
master, 0, data.slave_position))) {
up(&master->master_sem);
EC_MASTER_ERR(master, "Slave %u does not exist!\n",
data.slave_position);
return -EINVAL;
}
if (data.sync_index >= slave->sii.sync_count) {
up(&master->master_sem);
EC_SLAVE_ERR(slave, "Sync manager %u does not exist!\n",
data.sync_index);
return -EINVAL;
}
sync = &slave->sii.syncs[data.sync_index];
data.physical_start_address = sync->physical_start_address;
data.default_size = sync->default_length;
data.control_register = sync->control_register;
data.enable = sync->enable;
data.pdo_count = ec_pdo_list_count(&sync->pdos);
up(&master->master_sem);
if (copy_to_user((void __user *) arg, &data, sizeof(data)))
return -EFAULT;
return 0;
}
/*****************************************************************************/
/** Get slave sync manager PDO information.
*/
int ec_cdev_ioctl_slave_sync_pdo(
ec_master_t *master, /**< EtherCAT master. */
unsigned long arg /**< Userspace address to store the results. */
)
{
ec_ioctl_slave_sync_pdo_t data;
const ec_slave_t *slave;
const ec_sync_t *sync;
const ec_pdo_t *pdo;
if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
return -EFAULT;
}
if (down_interruptible(&master->master_sem))
return -EINTR;
if (!(slave = ec_master_find_slave_const(
master, 0, data.slave_position))) {
up(&master->master_sem);
EC_MASTER_ERR(master, "Slave %u does not exist!\n",
data.slave_position);
return -EINVAL;
}
if (data.sync_index >= slave->sii.sync_count) {
up(&master->master_sem);
EC_SLAVE_ERR(slave, "Sync manager %u does not exist!\n",
data.sync_index);
return -EINVAL;
}
sync = &slave->sii.syncs[data.sync_index];
if (!(pdo = ec_pdo_list_find_pdo_by_pos_const(
&sync->pdos, data.pdo_pos))) {
up(&master->master_sem);
EC_SLAVE_ERR(slave, "Sync manager %u does not contain a PDO with "
"position %u!\n", data.sync_index, data.pdo_pos);
return -EINVAL;
}
data.index = pdo->index;
data.entry_count = ec_pdo_entry_count(pdo);
up(&master->master_sem);
if (copy_to_user((void __user *) arg, &data, sizeof(data)))
return -EFAULT;
return 0;
}
/*****************************************************************************/
/** Get slave sync manager PDO entry information.
*/
int ec_cdev_ioctl_slave_sync_pdo_entry(
ec_master_t *master, /**< EtherCAT master. */
unsigned long arg /**< Userspace address to store the results. */
)
{
ec_ioctl_slave_sync_pdo_entry_t data;
const ec_slave_t *slave;
const ec_sync_t *sync;
const ec_pdo_t *pdo;
const ec_pdo_entry_t *entry;
if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
return -EFAULT;
}
if (down_interruptible(&master->master_sem))
return -EINTR;
if (!(slave = ec_master_find_slave_const(
master, 0, data.slave_position))) {
up(&master->master_sem);
EC_MASTER_ERR(master, "Slave %u does not exist!\n",
data.slave_position);
return -EINVAL;
}
if (data.sync_index >= slave->sii.sync_count) {
up(&master->master_sem);
EC_SLAVE_ERR(slave, "Sync manager %u does not exist!\n",
data.sync_index);
return -EINVAL;
}
sync = &slave->sii.syncs[data.sync_index];
if (!(pdo = ec_pdo_list_find_pdo_by_pos_const(
&sync->pdos, data.pdo_pos))) {
up(&master->master_sem);
EC_SLAVE_ERR(slave, "Sync manager %u does not contain a PDO with "
"position %u!\n", data.sync_index, data.pdo_pos);
return -EINVAL;
}
if (!(entry = ec_pdo_find_entry_by_pos_const(
pdo, data.entry_pos))) {
up(&master->master_sem);
EC_SLAVE_ERR(slave, "PDO 0x%04X does not contain an entry with "
"position %u!\n", data.pdo_pos, data.entry_pos);
return -EINVAL;
}
data.index = entry->index;
data.subindex = entry->subindex;
data.bit_length = entry->bit_length;
up(&master->master_sem);
if (copy_to_user((void __user *) arg, &data, sizeof(data)))
return -EFAULT;
return 0;
}
/*****************************************************************************/
/** Get domain information.
*/
int ec_cdev_ioctl_domain(
ec_master_t *master, /**< EtherCAT master. */
unsigned long arg /**< Userspace address to store the results. */
)
{
ec_ioctl_domain_t data;
const ec_domain_t *domain;
if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
return -EFAULT;
}
if (down_interruptible(&master->master_sem))
return -EINTR;
if (!(domain = ec_master_find_domain_const(master, data.index))) {
up(&master->master_sem);
EC_MASTER_ERR(master, "Domain %u does not exist!\n", data.index);
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
return -EINVAL;
}
data.data_size = domain->data_size;
data.logical_base_address = domain->logical_base_address;
data.working_counter = domain->working_counter;
data.expected_working_counter = domain->expected_working_counter;
data.fmmu_count = ec_domain_fmmu_count(domain);
up(&master->master_sem);
if (copy_to_user((void __user *) arg, &data, sizeof(data)))
return -EFAULT;
return 0;
}
/*****************************************************************************/
/** Get domain FMMU information.
*/
int ec_cdev_ioctl_domain_fmmu(
ec_master_t *master, /**< EtherCAT master. */
unsigned long arg /**< Userspace address to store the results. */
)
{
ec_ioctl_domain_fmmu_t data;
const ec_domain_t *domain;
const ec_fmmu_config_t *fmmu;
if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
return -EFAULT;
}
if (down_interruptible(&master->master_sem))
return -EINTR;
if (!(domain = ec_master_find_domain_const(master, data.domain_index))) {
up(&master->master_sem);
EC_MASTER_ERR(master, "Domain %u does not exist!\n",
data.domain_index);
return -EINVAL;
}
if (!(fmmu = ec_domain_find_fmmu(domain, data.fmmu_index))) {
up(&master->master_sem);
EC_MASTER_ERR(master, "Domain %u has less than %u"
" fmmu configurations.\n",
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
data.domain_index, data.fmmu_index + 1);
return -EINVAL;
}
data.slave_config_alias = fmmu->sc->alias;
data.slave_config_position = fmmu->sc->position;
data.sync_index = fmmu->sync_index;
data.dir = fmmu->dir;
data.logical_address = fmmu->logical_start_address;
data.data_size = fmmu->data_size;
up(&master->master_sem);
if (copy_to_user((void __user *) arg, &data, sizeof(data)))
return -EFAULT;
return 0;
}
/*****************************************************************************/
/** Get domain data.
*/
int ec_cdev_ioctl_domain_data(
ec_master_t *master, /**< EtherCAT master. */
unsigned long arg /**< Userspace address to store the results. */
)
{
ec_ioctl_domain_data_t data;
const ec_domain_t *domain;
if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
return -EFAULT;
}
if (down_interruptible(&master->master_sem))
return -EINTR;
if (!(domain = ec_master_find_domain_const(master, data.domain_index))) {
up(&master->master_sem);
EC_MASTER_ERR(master, "Domain %u does not exist!\n",
data.domain_index);
return -EINVAL;
}
if (domain->data_size != data.data_size) {
up(&master->master_sem);
EC_MASTER_ERR(master, "Data size mismatch %u/%zu!\n",
data.data_size, domain->data_size);
return -EFAULT;
}
if (copy_to_user((void __user *) data.target, domain->data,
return -EFAULT;
up(&master->master_sem);
return 0;
}
/*****************************************************************************/
/** Set master debug level.
*/
int ec_cdev_ioctl_master_debug(
ec_master_t *master, /**< EtherCAT master. */
unsigned long arg /**< ioctl() argument. */
)
{
return ec_master_debug_level(master, (unsigned int) arg);
}
/*****************************************************************************/
/** Issue a bus scan.
*/
int ec_cdev_ioctl_master_rescan(
ec_master_t *master, /**< EtherCAT master. */
unsigned long arg /**< ioctl() argument. */
)
{
master->fsm.rescan_required = 1;
return 0;
}
/*****************************************************************************/
/** Set slave state.
*/
int ec_cdev_ioctl_slave_state(
ec_master_t *master, /**< EtherCAT master. */
unsigned long arg /**< ioctl() argument. */
)
{
ec_ioctl_slave_state_t data;
ec_slave_t *slave;
if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
return -EFAULT;
}
if (down_interruptible(&master->master_sem))
return -EINTR;
if (!(slave = ec_master_find_slave(
master, 0, data.slave_position))) {
EC_MASTER_ERR(master, "Slave %u does not exist!\n",
data.slave_position);
return -EINVAL;
}
up(&master->master_sem);
return 0;
}
/*****************************************************************************/
/** Get slave SDO information.
*/
int ec_cdev_ioctl_slave_sdo(
ec_master_t *master, /**< EtherCAT master. */
unsigned long arg /**< ioctl() argument. */
)
{
ec_ioctl_slave_sdo_t data;
const ec_slave_t *slave;
const ec_sdo_t *sdo;
if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
return -EFAULT;
}
if (down_interruptible(&master->master_sem))
return -EINTR;
if (!(slave = ec_master_find_slave_const(
master, 0, data.slave_position))) {
up(&master->master_sem);
EC_MASTER_ERR(master, "Slave %u does not exist!\n",
data.slave_position);
return -EINVAL;
}
if (!(sdo = ec_slave_get_sdo_by_pos_const(
slave, data.sdo_position))) {
up(&master->master_sem);
EC_SLAVE_ERR(slave, "SDO %u does not exist!\n", data.sdo_position);
return -EINVAL;
}
data.sdo_index = sdo->index;
data.max_subindex = sdo->max_subindex;
up(&master->master_sem);
if (copy_to_user((void __user *) arg, &data, sizeof(data)))
return -EFAULT;
return 0;
}
/*****************************************************************************/
/** Get slave SDO entry information.
*/
int ec_cdev_ioctl_slave_sdo_entry(
ec_master_t *master, /**< EtherCAT master. */
unsigned long arg /**< ioctl() argument. */
)
{
ec_ioctl_slave_sdo_entry_t data;
const ec_slave_t *slave;
const ec_sdo_t *sdo;
const ec_sdo_entry_t *entry;
if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
return -EFAULT;
}
if (down_interruptible(&master->master_sem))
return -EINTR;
if (!(slave = ec_master_find_slave_const(
master, 0, data.slave_position))) {
up(&master->master_sem);
EC_MASTER_ERR(master, "Slave %u does not exist!\n",
data.slave_position);
return -EINVAL;
}
if (data.sdo_spec <= 0) {
if (!(sdo = ec_slave_get_sdo_by_pos_const(
slave, -data.sdo_spec))) {
up(&master->master_sem);
EC_SLAVE_ERR(slave, "SDO %u does not exist!\n", -data.sdo_spec);
return -EINVAL;
}
} else {
if (!(sdo = ec_slave_get_sdo_const(
slave, data.sdo_spec))) {
up(&master->master_sem);
EC_SLAVE_ERR(slave, "SDO 0x%04X does not exist!\n",
data.sdo_spec);
return -EINVAL;
}
}
if (!(entry = ec_sdo_get_entry_const(
sdo, data.sdo_entry_subindex))) {
up(&master->master_sem);
EC_SLAVE_ERR(slave, "SDO entry 0x%04X:%02X does not exist!\n",
sdo->index, data.sdo_entry_subindex);
return -EINVAL;
}
data.data_type = entry->data_type;
data.bit_length = entry->bit_length;
data.read_access[EC_SDO_ENTRY_ACCESS_PREOP] =
entry->read_access[EC_SDO_ENTRY_ACCESS_PREOP];
data.read_access[EC_SDO_ENTRY_ACCESS_SAFEOP] =
entry->read_access[EC_SDO_ENTRY_ACCESS_SAFEOP];
data.read_access[EC_SDO_ENTRY_ACCESS_OP] =
entry->read_access[EC_SDO_ENTRY_ACCESS_OP];
data.write_access[EC_SDO_ENTRY_ACCESS_PREOP] =
entry->write_access[EC_SDO_ENTRY_ACCESS_PREOP];
data.write_access[EC_SDO_ENTRY_ACCESS_SAFEOP] =
entry->write_access[EC_SDO_ENTRY_ACCESS_SAFEOP];
data.write_access[EC_SDO_ENTRY_ACCESS_OP] =
entry->write_access[EC_SDO_ENTRY_ACCESS_OP];
ec_cdev_strcpy(data.description, entry->description);
up(&master->master_sem);
if (copy_to_user((void __user *) arg, &data, sizeof(data)))
return -EFAULT;
return 0;
}
/*****************************************************************************/
/** Upload SDO.
*/
int ec_cdev_ioctl_slave_sdo_upload(
ec_master_t *master, /**< EtherCAT master. */
unsigned long arg /**< ioctl() argument. */
)
{
ec_ioctl_slave_sdo_upload_t data;
uint8_t *target;
int ret;
if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
return -EFAULT;
}
if (!(target = kmalloc(data.target_size, GFP_KERNEL))) {
EC_MASTER_ERR(master, "Failed to allocate %u bytes"
" for SDO upload.\n", data.target_size);
return -ENOMEM;
}
ret = ecrt_master_sdo_upload(master, data.slave_position,
data.sdo_index, data.sdo_entry_subindex, target,
data.target_size, &data.data_size, &data.abort_code);
if (!ret) {
if (copy_to_user((void __user *) data.target,
target, data.data_size)) {
kfree(target);
return -EFAULT;
}
}
kfree(target);
if (__copy_to_user((void __user *) arg, &data, sizeof(data))) {
return -EFAULT;
}
return 0;
}
/*****************************************************************************/
/** Download SDO.
*/
int ec_cdev_ioctl_slave_sdo_download(
ec_master_t *master, /**< EtherCAT master. */
unsigned long arg /**< ioctl() argument. */
)
{
ec_ioctl_slave_sdo_download_t data;
uint8_t *sdo_data;
int retval;
if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
return -EFAULT;
}
if (!(sdo_data = kmalloc(data.data_size, GFP_KERNEL))) {
EC_MASTER_ERR(master, "Failed to allocate %u bytes"
" for SDO download.\n", data.data_size);
return -ENOMEM;
}
if (copy_from_user(sdo_data, (void __user *) data.data, data.data_size)) {
kfree(sdo_data);
return -EFAULT;
}
if (data.complete_access) {
retval = ecrt_master_sdo_download_complete(master, data.slave_position,
data.sdo_index, sdo_data, data.data_size, &data.abort_code);
} else {
retval = ecrt_master_sdo_download(master, data.slave_position,
data.sdo_index, data.sdo_entry_subindex, sdo_data,
data.data_size, &data.abort_code);
}
Martin Troxler
committed
kfree(sdo_data);
if (__copy_to_user((void __user *) arg, &data, sizeof(data))) {
retval = -EFAULT;
}
return retval;
}
/*****************************************************************************/
/** Read a slave's SII.
*/
int ec_cdev_ioctl_slave_sii_read(
ec_master_t *master, /**< EtherCAT master. */
unsigned long arg /**< ioctl() argument. */
)
{
ec_ioctl_slave_sii_t data;
const ec_slave_t *slave;
int retval;
if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
return -EFAULT;
}
if (down_interruptible(&master->master_sem))
return -EINTR;
if (!(slave = ec_master_find_slave_const(
master, 0, data.slave_position))) {
up(&master->master_sem);
EC_MASTER_ERR(master, "Slave %u does not exist!\n",
data.slave_position);
return -EINVAL;
}
if (!data.nwords
|| data.offset + data.nwords > slave->sii_nwords) {
up(&master->master_sem);
EC_SLAVE_ERR(slave, "Invalid SII read offset/size %u/%u for slave SII"
" size %zu!\n", data.offset, data.nwords, slave->sii_nwords);
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
return -EINVAL;
}
if (copy_to_user((void __user *) data.words,
slave->sii_words + data.offset, data.nwords * 2))
retval = -EFAULT;
else
retval = 0;
up(&master->master_sem);
return retval;
}
/*****************************************************************************/
/** Write a slave's SII.
*/
int ec_cdev_ioctl_slave_sii_write(
ec_master_t *master, /**< EtherCAT master. */
unsigned long arg /**< ioctl() argument. */
)
{
ec_ioctl_slave_sii_t data;
ec_slave_t *slave;
unsigned int byte_size;
uint16_t *words;
ec_sii_write_request_t request;
if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
return -EFAULT;
}
if (!data.nwords)
return 0;
byte_size = sizeof(uint16_t) * data.nwords;
if (!(words = kmalloc(byte_size, GFP_KERNEL))) {
EC_MASTER_ERR(master, "Failed to allocate %u bytes"
" for SII contents.\n", byte_size);
return -ENOMEM;
}