fp@39: /****************************************************************************** fp@0: * fp@39: * $Id$ fp@0: * 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@39: *****************************************************************************/ fp@0: fp@199: /** fp@199: \file fp@199: EtherCAT device methods. fp@199: */ fp@199: fp@199: /*****************************************************************************/ fp@199: fp@24: #include fp@0: #include fp@0: #include fp@0: #include fp@2: #include fp@0: fp@54: #include "device.h" fp@78: #include "master.h" fp@0: fp@39: /*****************************************************************************/ fp@0: fp@0: /** fp@195: Device constructor. fp@195: \return 0 in case of success, else < 0 fp@195: */ fp@195: fp@195: int ec_device_init(ec_device_t *device, /**< EtherCAT device */ fp@195: ec_master_t *master, /**< master owning the device */ fp@195: struct net_device *net_dev, /**< net_device structure */ fp@195: ec_isr_t isr, /**< pointer to device's ISR */ fp@195: struct module *module /**< pointer to the owning module */ fp@78: ) fp@78: { fp@98: struct ethhdr *eth; fp@98: fp@78: device->master = master; fp@98: device->dev = net_dev; fp@98: device->isr = isr; fp@98: device->module = module; fp@98: fp@78: device->open = 0; fp@96: device->link_state = 0; // down fp@78: fp@493: device->tx_count = 0; fp@493: device->rx_count = 0; fp@493: fp@392: #ifdef EC_DBG_IF fp@231: if (ec_debug_init(&device->dbg)) { fp@231: EC_ERR("Failed to init debug device!\n"); fp@231: goto out_return; fp@231: } fp@392: #endif fp@231: fp@211: if (!(device->tx_skb = dev_alloc_skb(ETH_FRAME_LEN))) { fp@84: EC_ERR("Error allocating device socket buffer!\n"); fp@392: #ifdef EC_DBG_IF fp@231: goto out_debug; fp@392: #else fp@392: goto out_return; fp@392: #endif fp@73: } fp@73: fp@98: device->tx_skb->dev = net_dev; fp@98: fp@195: // add Ethernet-II-header fp@98: skb_reserve(device->tx_skb, ETH_HLEN); fp@98: eth = (struct ethhdr *) skb_push(device->tx_skb, ETH_HLEN); fp@98: eth->h_proto = htons(0x88A4); fp@98: memcpy(eth->h_source, net_dev->dev_addr, net_dev->addr_len); fp@98: memset(eth->h_dest, 0xFF, net_dev->addr_len); fp@98: fp@73: return 0; fp@231: fp@392: #ifdef EC_DBG_IF fp@231: out_debug: fp@231: ec_debug_clear(&device->dbg); fp@392: #endif fp@231: out_return: fp@231: return -1; fp@39: } fp@39: fp@39: /*****************************************************************************/ fp@0: fp@0: /** fp@195: EtherCAT device destuctor. fp@195: */ fp@195: fp@195: void ec_device_clear(ec_device_t *device /**< EtherCAT device */) fp@78: { fp@78: if (device->open) ec_device_close(device); fp@98: if (device->tx_skb) dev_kfree_skb(device->tx_skb); fp@392: #ifdef EC_DBG_IF fp@231: ec_debug_clear(&device->dbg); fp@392: #endif fp@0: } fp@0: fp@39: /*****************************************************************************/ fp@0: fp@0: /** fp@195: Opens the EtherCAT device. fp@195: \return 0 in case of success, else < 0 fp@195: */ fp@195: fp@195: int ec_device_open(ec_device_t *device /**< EtherCAT device */) fp@0: { fp@73: unsigned int i; fp@73: fp@78: if (!device->dev) { fp@84: EC_ERR("No net_device to open!\n"); fp@73: return -1; fp@73: } fp@73: fp@78: if (device->open) { fp@84: EC_WARN("Device already opened!\n"); fp@91: return 0; fp@91: } fp@91: fp@195: // device could have received frames before fp@91: for (i = 0; i < 4; i++) ec_device_call_isr(device); fp@91: fp@96: device->link_state = 0; fp@493: device->tx_count = 0; fp@493: device->rx_count = 0; fp@91: fp@91: if (device->dev->open(device->dev) == 0) device->open = 1; fp@78: fp@78: return device->open ? 0 : -1; fp@0: } fp@0: fp@39: /*****************************************************************************/ fp@0: fp@0: /** fp@195: Stops the EtherCAT device. fp@195: \return 0 in case of success, else < 0 fp@195: */ fp@195: fp@195: int ec_device_close(ec_device_t *device /**< EtherCAT device */) fp@78: { fp@78: if (!device->dev) { fp@84: EC_ERR("No device to close!\n"); fp@73: return -1; fp@73: } fp@73: fp@78: if (!device->open) { fp@84: EC_WARN("Device already closed!\n"); fp@98: return 0; fp@98: } fp@98: fp@98: if (device->dev->stop(device->dev) == 0) device->open = 0; fp@78: fp@78: return !device->open ? 0 : -1; fp@73: } fp@73: fp@73: /*****************************************************************************/ fp@73: fp@73: /** fp@195: Returns a pointer to the device's transmit memory. fp@195: \return pointer to the TX socket buffer fp@195: */ fp@195: fp@195: uint8_t *ec_device_tx_data(ec_device_t *device /**< EtherCAT device */) fp@98: { fp@98: return device->tx_skb->data + ETH_HLEN; fp@0: } fp@0: fp@39: /*****************************************************************************/ fp@0: fp@0: /** fp@195: Sends the content of the transmit socket buffer. fp@195: Cuts the socket buffer content to the (now known) size, and calls the fp@195: start_xmit() function of the assigned net_device. fp@195: */ fp@195: fp@195: void ec_device_send(ec_device_t *device, /**< EtherCAT device */ fp@195: size_t size /**< number of bytes to send */ fp@73: ) fp@73: { fp@97: if (unlikely(!device->link_state)) // Link down fp@96: return; fp@96: fp@195: // set the right length for the data fp@98: device->tx_skb->len = ETH_HLEN + size; fp@78: fp@78: if (unlikely(device->master->debug_level > 1)) { fp@98: EC_DBG("sending frame:\n"); fp@152: ec_print_data(device->tx_skb->data + ETH_HLEN, size); fp@78: } fp@73: fp@392: #ifdef EC_DBG_IF fp@231: ec_debug_send(&device->dbg, device->tx_skb->data, ETH_HLEN + size); fp@392: #endif fp@231: fp@195: // start sending fp@78: device->dev->hard_start_xmit(device->tx_skb, device->dev); fp@493: device->tx_count++; fp@73: } fp@73: fp@73: /*****************************************************************************/ fp@73: fp@73: /** fp@195: Calls the interrupt service routine of the assigned net_device. fp@199: The master itself works without using interrupts. Therefore the processing fp@199: of received data and status changes of the network device has to be fp@199: done by the master calling the ISR "manually". fp@195: */ fp@195: fp@195: void ec_device_call_isr(ec_device_t *device /**< EtherCAT device */) fp@78: { fp@398: device->cycles_isr = get_cycles(); fp@398: device->jiffies_isr = jiffies; fp@78: if (likely(device->isr)) device->isr(0, device->dev, NULL); fp@39: } fp@39: fp@54: /****************************************************************************** fp@195: * Device interface fp@54: *****************************************************************************/ fp@54: fp@78: /** fp@195: Accepts a received frame. fp@199: Forwards the received data to the master. The master will analyze the frame fp@199: and dispatch the received commands to the sending instances. fp@199: \ingroup DeviceInterface fp@195: */ fp@195: fp@195: void ecdev_receive(ec_device_t *device, /**< EtherCAT device */ fp@231: const void *data, /**< pointer to received data */ fp@195: size_t size /**< number of bytes received */ fp@104: ) fp@73: { fp@493: device->rx_count++; fp@493: fp@78: if (unlikely(device->master->debug_level > 1)) { fp@84: EC_DBG("Received frame:\n"); fp@231: ec_print_data_diff(device->tx_skb->data + ETH_HLEN, fp@231: data + ETH_HLEN, size - ETH_HLEN); fp@231: } fp@231: fp@392: #ifdef EC_DBG_IF fp@231: ec_debug_send(&device->dbg, data, size); fp@392: #endif fp@231: fp@331: ec_master_receive_datagrams(device->master, fp@331: data + ETH_HLEN, fp@331: size - ETH_HLEN); fp@54: } fp@54: fp@54: /*****************************************************************************/ fp@54: fp@96: /** fp@195: Sets a new link state. fp@199: If the device notifies the master about the link being down, the master fp@199: will not try to send frames using this device. fp@199: \ingroup DeviceInterface fp@195: */ fp@195: fp@195: void ecdev_link_state(ec_device_t *device, /**< EtherCAT device */ fp@195: uint8_t state /**< new link state */ fp@104: ) fp@104: { fp@191: if (unlikely(!device)) { fp@191: EC_WARN("ecdev_link_state: no device!\n"); fp@191: return; fp@191: } fp@191: fp@104: if (likely(state != device->link_state)) { fp@96: device->link_state = state; fp@96: EC_INFO("Link state changed to %s.\n", (state ? "UP" : "DOWN")); fp@96: } fp@96: } fp@96: fp@96: /*****************************************************************************/ fp@96: fp@199: /** \cond */ fp@199: fp@104: EXPORT_SYMBOL(ecdev_receive); fp@104: EXPORT_SYMBOL(ecdev_link_state); fp@27: fp@199: /** \endcond */ fp@199: fp@199: /*****************************************************************************/