fp@39: /****************************************************************************** fp@0: * fp@39: * $Id$ fp@0: * fp@1618: * Copyright (C) 2006 Florian Pose, Ingenieurgemeinschaft IgH fp@1618: * fp@1618: * This file is part of the IgH EtherCAT Master. fp@1618: * fp@1618: * The IgH EtherCAT Master is free software; you can redistribute it fp@1618: * and/or modify it under the terms of the GNU General Public License fp@1618: * as published by the Free Software Foundation; version 2 of the License. fp@1618: * fp@1618: * The IgH EtherCAT Master is distributed in the hope that it will be fp@1618: * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of fp@1618: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the fp@1618: * GNU General Public License for more details. fp@1618: * fp@1618: * You should have received a copy of the GNU General Public License fp@1618: * along with the IgH EtherCAT Master; if not, write to the Free Software fp@1618: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA fp@1618: * fp@39: *****************************************************************************/ fp@0: fp@1618: /** fp@1618: \file fp@1618: EtherCAT device methods. fp@1618: */ fp@1618: fp@1618: /*****************************************************************************/ fp@1618: 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@98: if (!(device->tx_skb = dev_alloc_skb(ETH_HLEN + EC_MAX_FRAME_SIZE))) { fp@84: EC_ERR("Error allocating device socket buffer!\n"); fp@73: return -1; 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@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@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@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@195: // start sending fp@78: device->dev->hard_start_xmit(device->tx_skb, device->dev); fp@73: } fp@73: fp@73: /*****************************************************************************/ fp@73: fp@73: /** fp@195: Calls the interrupt service routine of the assigned net_device. fp@1618: The master itself works without using interrupts. Therefore the processing fp@1618: of received data and status changes of the network device has to be fp@1618: 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@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@1618: Forwards the received data to the master. The master will analyze the frame fp@1618: and dispatch the received commands to the sending instances. fp@1618: \ingroup DeviceInterface fp@195: */ fp@195: fp@195: void ecdev_receive(ec_device_t *device, /**< EtherCAT device */ fp@195: const void *data, /**< pointer to receibed data */ fp@195: size_t size /**< number of bytes received */ fp@104: ) fp@73: { fp@78: if (unlikely(device->master->debug_level > 1)) { fp@84: EC_DBG("Received frame:\n"); fp@152: ec_print_data_diff(device->tx_skb->data + ETH_HLEN, data, size); fp@98: } fp@98: fp@98: ec_master_receive(device->master, data, size); fp@54: } fp@54: fp@54: /*****************************************************************************/ fp@54: fp@96: /** fp@195: Sets a new link state. fp@1618: If the device notifies the master about the link being down, the master fp@1618: will not try to send frames using this device. fp@1618: \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@1618: /** \cond */ fp@1618: fp@104: EXPORT_SYMBOL(ecdev_receive); fp@104: EXPORT_SYMBOL(ecdev_link_state); fp@27: fp@1618: /** \endcond */ fp@1618: fp@1618: /*****************************************************************************/