fp@98: /****************************************************************************** fp@98: * fp@98: * $Id$ fp@98: * fp@197: * Copyright (C) 2006 Florian Pose, Ingenieurgemeinschaft IgH fp@197: * fp@197: * This file is part of the IgH EtherCAT Master. fp@197: * fp@197: * The IgH EtherCAT Master is free software; you can redistribute it fp@197: * and/or modify it under the terms of the GNU General Public License fp@246: * as published by the Free Software Foundation; either version 2 of the fp@246: * License, or (at your option) any later version. fp@197: * fp@197: * The IgH EtherCAT Master is distributed in the hope that it will be fp@197: * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of fp@197: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the fp@197: * GNU General Public License for more details. fp@197: * fp@197: * You should have received a copy of the GNU General Public License fp@197: * along with the IgH EtherCAT Master; if not, write to the Free Software fp@197: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA fp@197: * fp@246: * The right to use EtherCAT Technology is granted and comes free of fp@246: * charge under condition of compatibility of product made by fp@246: * Licensee. People intending to distribute/sell products based on the fp@246: * code, have to sign an agreement to guarantee that products using fp@246: * software based on IgH EtherCAT master stay compatible with the actual fp@246: * EtherCAT specification (which are released themselves as an open fp@246: * standard) as the (only) precondition to have the right to use EtherCAT fp@246: * Technology, IP and trade marks. fp@246: * fp@98: *****************************************************************************/ fp@98: fp@199: /** fp@199: \file fp@293: Methods of an EtherCAT datagram. fp@199: */ fp@199: fp@199: /*****************************************************************************/ fp@199: fp@98: #include fp@98: fp@293: #include "datagram.h" fp@98: #include "master.h" fp@98: fp@98: /*****************************************************************************/ fp@98: fp@199: /** \cond */ fp@199: fp@98: #define EC_FUNC_HEADER \ fp@293: if (unlikely(ec_datagram_prealloc(datagram, data_size))) \ fp@144: return -1; \ fp@293: datagram->index = 0; \ fp@293: datagram->working_counter = 0; \ fp@1715: datagram->state = EC_DATAGRAM_INIT; fp@98: fp@144: #define EC_FUNC_FOOTER \ fp@293: datagram->data_size = data_size; \ fp@293: memset(datagram->data, 0x00, data_size); \ fp@144: return 0; fp@144: fp@199: /** \endcond */ fp@199: fp@144: /*****************************************************************************/ fp@144: fp@144: /** fp@293: Datagram constructor. fp@293: */ fp@293: fp@293: void ec_datagram_init(ec_datagram_t *datagram /**< EtherCAT datagram */) fp@293: { fp@1732: INIT_LIST_HEAD(&datagram->queue); // mark as unqueued fp@1715: datagram->type = EC_DATAGRAM_NONE; fp@1745: memset(datagram->address, 0x00, EC_ADDR_LEN); fp@293: datagram->data = NULL; fp@293: datagram->mem_size = 0; fp@293: datagram->data_size = 0; fp@293: datagram->index = 0x00; fp@293: datagram->working_counter = 0x00; fp@1715: datagram->state = EC_DATAGRAM_INIT; fp@1719: datagram->cycles_sent = 0; fp@1732: datagram->jiffies_sent = 0; fp@1732: datagram->cycles_received = 0; fp@1732: datagram->jiffies_received = 0; fp@1745: datagram->skip_count = 0; fp@1745: datagram->stats_output_jiffies = 0; fp@1745: datagram->name[0] = 0x00; fp@293: } fp@293: fp@293: /*****************************************************************************/ fp@293: fp@293: /** fp@293: Datagram destructor. fp@293: */ fp@293: fp@293: void ec_datagram_clear(ec_datagram_t *datagram /**< EtherCAT datagram */) fp@293: { fp@293: if (datagram->data) kfree(datagram->data); fp@293: } fp@293: fp@293: /*****************************************************************************/ fp@293: fp@293: /** fp@293: Allocates datagram data memory. fp@197: If the allocated memory is already larger than requested, nothing ist done. fp@197: \return 0 in case of success, else < 0 fp@197: */ fp@197: fp@293: int ec_datagram_prealloc(ec_datagram_t *datagram, /**< EtherCAT datagram */ fp@293: size_t size /**< New size in bytes */ fp@293: ) fp@293: { fp@293: if (size <= datagram->mem_size) return 0; fp@293: fp@293: if (datagram->data) { fp@293: kfree(datagram->data); fp@293: datagram->data = NULL; fp@293: datagram->mem_size = 0; fp@144: } fp@144: fp@1739: if (!(datagram->data = kmalloc(size, GFP_ATOMIC))) { fp@293: EC_ERR("Failed to allocate %i bytes of datagram memory!\n", size); fp@144: return -1; fp@144: } fp@144: fp@293: datagram->mem_size = size; fp@144: return 0; fp@144: } fp@98: fp@98: /*****************************************************************************/ fp@98: fp@98: /** fp@293: Initializes an EtherCAT NPRD datagram. fp@98: Node-adressed physical read. fp@195: \return 0 in case of success, else < 0 fp@98: */ fp@98: fp@293: int ec_datagram_nprd(ec_datagram_t *datagram, fp@293: /**< EtherCAT datagram */ fp@293: uint16_t node_address, fp@293: /**< configured station address */ fp@293: uint16_t offset, fp@293: /**< physical memory address */ fp@293: size_t data_size fp@293: /**< number of bytes to read */ fp@293: ) fp@293: { fp@293: if (unlikely(node_address == 0x0000)) fp@293: EC_WARN("Using node address 0x0000!\n"); fp@293: fp@293: EC_FUNC_HEADER; fp@1715: datagram->type = EC_DATAGRAM_NPRD; fp@1745: EC_WRITE_U16(datagram->address, node_address); fp@1745: EC_WRITE_U16(datagram->address + 2, offset); fp@293: EC_FUNC_FOOTER; fp@293: } fp@293: fp@293: /*****************************************************************************/ fp@293: fp@293: /** fp@293: Initializes an EtherCAT NPWR datagram. fp@293: Node-adressed physical write. fp@293: \return 0 in case of success, else < 0 fp@293: */ fp@293: fp@293: int ec_datagram_npwr(ec_datagram_t *datagram, fp@293: /**< EtherCAT datagram */ fp@293: uint16_t node_address, fp@293: /**< configured station address */ fp@293: uint16_t offset, fp@293: /**< physical memory address */ fp@293: size_t data_size fp@293: /**< number of bytes to write */ fp@293: ) fp@293: { fp@293: if (unlikely(node_address == 0x0000)) fp@293: EC_WARN("Using node address 0x0000!\n"); fp@293: fp@293: EC_FUNC_HEADER; fp@1715: datagram->type = EC_DATAGRAM_NPWR; fp@1745: EC_WRITE_U16(datagram->address, node_address); fp@1745: EC_WRITE_U16(datagram->address + 2, offset); fp@293: EC_FUNC_FOOTER; fp@293: } fp@293: fp@293: /*****************************************************************************/ fp@293: fp@293: /** fp@293: Initializes an EtherCAT APRD datagram. fp@293: Autoincrement physical read. fp@293: \return 0 in case of success, else < 0 fp@293: */ fp@293: fp@293: int ec_datagram_aprd(ec_datagram_t *datagram, fp@293: /**< EtherCAT datagram */ fp@293: uint16_t ring_position, fp@293: /**< auto-increment position */ fp@293: uint16_t offset, fp@293: /**< physical memory address */ fp@293: size_t data_size fp@293: /**< number of bytes to read */ fp@293: ) fp@293: { fp@293: EC_FUNC_HEADER; fp@1715: datagram->type = EC_DATAGRAM_APRD; fp@1745: EC_WRITE_S16(datagram->address, (int16_t) ring_position * (-1)); fp@1745: EC_WRITE_U16(datagram->address + 2, offset); fp@293: EC_FUNC_FOOTER; fp@293: } fp@293: fp@293: /*****************************************************************************/ fp@293: fp@293: /** fp@293: Initializes an EtherCAT APWR datagram. fp@293: Autoincrement physical write. fp@293: \return 0 in case of success, else < 0 fp@293: */ fp@293: fp@293: int ec_datagram_apwr(ec_datagram_t *datagram, fp@293: /**< EtherCAT datagram */ fp@293: uint16_t ring_position, fp@293: /**< auto-increment position */ fp@293: uint16_t offset, fp@293: /**< physical memory address */ fp@293: size_t data_size fp@293: /**< number of bytes to write */ fp@293: ) fp@293: { fp@293: EC_FUNC_HEADER; fp@1715: datagram->type = EC_DATAGRAM_APWR; fp@1745: EC_WRITE_S16(datagram->address, (int16_t) ring_position * (-1)); fp@1745: EC_WRITE_U16(datagram->address + 2, offset); fp@293: EC_FUNC_FOOTER; fp@293: } fp@293: fp@293: /*****************************************************************************/ fp@293: fp@293: /** fp@293: Initializes an EtherCAT BRD datagram. fp@293: Broadcast read. fp@293: \return 0 in case of success, else < 0 fp@293: */ fp@293: fp@293: int ec_datagram_brd(ec_datagram_t *datagram, fp@293: /**< EtherCAT datagram */ fp@195: uint16_t offset, fp@195: /**< physical memory address */ fp@195: size_t data_size fp@195: /**< number of bytes to read */ fp@144: ) fp@98: { fp@293: EC_FUNC_HEADER; fp@1715: datagram->type = EC_DATAGRAM_BRD; fp@1745: EC_WRITE_U16(datagram->address, 0x0000); fp@1745: EC_WRITE_U16(datagram->address + 2, offset); fp@293: EC_FUNC_FOOTER; fp@293: } fp@293: fp@293: /*****************************************************************************/ fp@293: fp@293: /** fp@293: Initializes an EtherCAT BWR datagram. fp@293: Broadcast write. fp@293: \return 0 in case of success, else < 0 fp@293: */ fp@293: fp@293: int ec_datagram_bwr(ec_datagram_t *datagram, fp@293: /**< EtherCAT datagram */ fp@195: uint16_t offset, fp@195: /**< physical memory address */ fp@195: size_t data_size fp@195: /**< number of bytes to write */ fp@144: ) fp@98: { fp@293: EC_FUNC_HEADER; fp@1715: datagram->type = EC_DATAGRAM_BWR; fp@1745: EC_WRITE_U16(datagram->address, 0x0000); fp@1745: EC_WRITE_U16(datagram->address + 2, offset); fp@293: EC_FUNC_FOOTER; fp@293: } fp@293: fp@293: /*****************************************************************************/ fp@293: fp@293: /** fp@293: Initializes an EtherCAT LRW datagram. fp@293: Logical read write. fp@293: \return 0 in case of success, else < 0 fp@293: */ fp@293: fp@293: int ec_datagram_lrw(ec_datagram_t *datagram, fp@293: /**< EtherCAT datagram */ fp@293: uint32_t offset, fp@293: /**< logical address */ fp@195: size_t data_size fp@293: /**< number of bytes to read/write */ fp@144: ) fp@144: { fp@144: EC_FUNC_HEADER; fp@1715: datagram->type = EC_DATAGRAM_LRW; fp@1745: EC_WRITE_U32(datagram->address, offset); fp@1745: EC_FUNC_FOOTER; fp@1745: } fp@1745: fp@1745: /*****************************************************************************/ fp@1745: fp@1745: /** fp@1745: * Evaluates the working counter of a single-cast datagram. fp@1745: * Outputs an error message. fp@1745: */ fp@1745: fp@1745: void ec_datagram_print_wc_error( fp@1746: const ec_datagram_t *datagram /**< EtherCAT datagram */ fp@1745: ) fp@1745: { fp@1745: if (datagram->working_counter == 0) fp@1745: printk("No response."); fp@1745: else if (datagram->working_counter > 1) fp@1745: printk("%u slaves responded!", datagram->working_counter); fp@1745: else fp@1745: printk("Success."); fp@1745: printk("\n"); fp@1745: } fp@1745: fp@1745: /*****************************************************************************/ fp@1745: fp@1745: /** fp@1745: * Outputs datagram statistics at most every second. fp@1745: */ fp@1745: fp@1745: void ec_datagram_output_stats( fp@1745: ec_datagram_t *datagram fp@1745: ) fp@1745: { fp@1745: if (jiffies - datagram->stats_output_jiffies < HZ) { fp@1745: datagram->stats_output_jiffies = jiffies; fp@1745: fp@1745: if (unlikely(datagram->skip_count)) { fp@1745: EC_WARN("Datagram %x (%s) was SKIPPED %u time%s.\n", fp@1745: (unsigned int) datagram, datagram->name, fp@1745: datagram->skip_count, fp@1745: datagram->skip_count == 1 ? "" : "s"); fp@1745: datagram->skip_count = 0; fp@1745: } fp@1745: } fp@1745: } fp@1745: fp@1745: /*****************************************************************************/