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@0: fp@54: #include "device.h" fp@78: #include "master.h" fp@0: fp@1744: #ifdef EC_DEBUG_RING fp@1744: #define timersub(a, b, result) \ fp@1744: do { \ fp@1744: (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ fp@1744: (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \ fp@1744: if ((result)->tv_usec < 0) { \ fp@1744: --(result)->tv_sec; \ fp@1744: (result)->tv_usec += 1000000; \ fp@1744: } \ fp@1744: } while (0) fp@1744: #endif fp@1744: 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@1744: ec_master_t *master /**< master owning the device */ fp@1744: ) fp@1744: { fp@1744: unsigned int i; fp@98: struct ethhdr *eth; fp@1744: #ifdef EC_DEBUG_IF fp@1744: char ifname[10]; fp@1744: char mb = 'x'; fp@1744: #endif fp@1744: #ifdef EC_DEBUG_RING fp@1744: device->debug_frame_index = 0; fp@1744: device->debug_frame_count = 0; fp@1744: #endif fp@98: fp@78: device->master = master; fp@1744: device->tx_ring_index = 0; fp@1744: fp@1744: #ifdef EC_DEBUG_IF fp@1744: if (device == &master->main_device) fp@1744: mb = 'm'; fp@1744: else if (device == &master->backup_device) fp@1744: mb = 'b'; fp@1744: fp@1744: sprintf(ifname, "ecdbg%c%u", mb, master->index); fp@1744: fp@1744: if (ec_debug_init(&device->dbg, ifname)) { fp@1744: EC_ERR("Failed to init debug device!\n"); fp@1744: goto out_return; fp@1744: } fp@1744: #endif fp@1744: fp@1744: for (i = 0; i < EC_TX_RING_SIZE; i++) fp@1744: device->tx_skb[i] = NULL; fp@1744: fp@1744: for (i = 0; i < EC_TX_RING_SIZE; i++) { fp@1744: if (!(device->tx_skb[i] = dev_alloc_skb(ETH_FRAME_LEN))) { fp@1744: EC_ERR("Error allocating device socket buffer!\n"); fp@1744: goto out_tx_ring; fp@1744: } fp@1744: fp@1744: // add Ethernet-II-header fp@1744: skb_reserve(device->tx_skb[i], ETH_HLEN); fp@1744: eth = (struct ethhdr *) skb_push(device->tx_skb[i], ETH_HLEN); fp@1744: eth->h_proto = htons(0x88A4); fp@1744: memset(eth->h_dest, 0xFF, ETH_ALEN); fp@1744: } fp@1744: fp@1744: ec_device_detach(device); // resets remaining fields fp@1744: return 0; fp@1744: fp@1744: out_tx_ring: fp@1744: for (i = 0; i < EC_TX_RING_SIZE; i++) fp@1744: if (device->tx_skb[i]) fp@1744: dev_kfree_skb(device->tx_skb[i]); fp@1744: #ifdef EC_DEBUG_IF fp@1744: ec_debug_clear(&device->dbg); fp@1744: out_return: fp@1744: #endif fp@1744: return -1; fp@1744: } fp@1744: fp@1744: /*****************************************************************************/ fp@1744: fp@1744: /** fp@1744: EtherCAT device destuctor. fp@1744: */ fp@1744: fp@1744: void ec_device_clear(ec_device_t *device /**< EtherCAT device */) fp@1744: { fp@1744: unsigned int i; fp@1744: fp@1744: if (device->open) ec_device_close(device); fp@1744: for (i = 0; i < EC_TX_RING_SIZE; i++) fp@1744: dev_kfree_skb(device->tx_skb[i]); fp@1744: #ifdef EC_DEBUG_IF fp@1744: ec_debug_clear(&device->dbg); fp@1744: #endif fp@1744: } fp@1744: fp@1744: /*****************************************************************************/ fp@1744: fp@1744: /** fp@1744: Associate with net_device. fp@1744: */ fp@1744: fp@1744: void ec_device_attach(ec_device_t *device, /**< EtherCAT device */ fp@1744: struct net_device *net_dev, /**< net_device structure */ fp@1744: ec_pollfunc_t poll, /**< pointer to device's poll function */ fp@1744: struct module *module /**< the device's module */ fp@1744: ) fp@1744: { fp@1744: unsigned int i; fp@1744: struct ethhdr *eth; fp@1744: fp@1744: ec_device_detach(device); // resets fields fp@1744: fp@98: device->dev = net_dev; fp@1739: device->poll = poll; fp@98: device->module = module; fp@98: fp@1744: for (i = 0; i < EC_TX_RING_SIZE; i++) { fp@1744: device->tx_skb[i]->dev = net_dev; fp@1746: eth = (struct ethhdr *) (device->tx_skb[i]->data); fp@1744: memcpy(eth->h_source, net_dev->dev_addr, ETH_ALEN); fp@1744: } fp@1744: } fp@1744: fp@1744: /*****************************************************************************/ fp@1744: fp@1744: /** fp@1744: Disconnect from net_device. fp@1744: */ fp@1744: fp@1744: void ec_device_detach(ec_device_t *device /**< EtherCAT device */) fp@1744: { fp@1744: unsigned int i; fp@1744: fp@1744: device->dev = NULL; fp@1744: device->poll = NULL; fp@1744: device->module = NULL; fp@78: device->open = 0; fp@96: device->link_state = 0; // down fp@1739: device->tx_count = 0; fp@1739: device->rx_count = 0; fp@1744: for (i = 0; i < EC_TX_RING_SIZE; i++) fp@1744: device->tx_skb[i]->dev = NULL; 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@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@96: device->link_state = 0; fp@1739: device->tx_count = 0; fp@1739: 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@1744: /* cycle through socket buffers, because otherwise there is a race fp@1744: * condition, if multiple frames are sent and the DMA is not scheduled in fp@1744: * between. */ fp@1744: device->tx_ring_index++; fp@1744: device->tx_ring_index %= EC_TX_RING_SIZE; fp@1744: return device->tx_skb[device->tx_ring_index]->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@1744: struct sk_buff *skb = device->tx_skb[device->tx_ring_index]; fp@1744: fp@97: if (unlikely(!device->link_state)) // Link down fp@96: return; fp@96: fp@195: // set the right length for the data fp@1744: skb->len = ETH_HLEN + size; fp@78: fp@78: if (unlikely(device->master->debug_level > 1)) { fp@98: EC_DBG("sending frame:\n"); fp@1744: ec_print_data(skb->data + ETH_HLEN, size); fp@1744: } fp@1744: fp@195: // start sending fp@1746: if (device->dev->hard_start_xmit(skb, device->dev) == NETDEV_TX_OK) { fp@1746: device->tx_count++; fp@1746: #ifdef EC_DEBUG_IF fp@1746: ec_debug_send(&device->dbg, skb->data, ETH_HLEN + size); fp@1746: #endif fp@1746: #ifdef EC_DEBUG_RING fp@1746: ec_device_debug_ring_append( fp@1746: device, TX, skb->data + ETH_HLEN, size); fp@1746: #endif fp@1746: } fp@1739: } fp@1739: fp@1739: /*****************************************************************************/ fp@1739: fp@1744: #ifdef EC_DEBUG_RING fp@1744: /** fp@1744: * Appends frame data to the debug ring. fp@1744: */ fp@1744: fp@1744: void ec_device_debug_ring_append( fp@1744: ec_device_t *device, /**< EtherCAT device */ fp@1744: ec_debug_frame_dir_t dir, /**< direction */ fp@1744: const void *data, /**< frame data */ fp@1744: size_t size /**< data size */ fp@1744: ) fp@1744: { fp@1744: ec_debug_frame_t *df = &device->debug_frames[device->debug_frame_index]; fp@1744: fp@1744: df->dir = dir; fp@1744: if (dir == TX) fp@1744: do_gettimeofday(&df->t); fp@1744: else fp@1744: df->t = device->timeval_poll; fp@1744: memcpy(df->data, data, size); fp@1744: df->data_size = size; fp@1744: fp@1744: device->debug_frame_index++; fp@1744: device->debug_frame_index %= EC_DEBUG_RING_SIZE; fp@1744: if (unlikely(device->debug_frame_count < EC_DEBUG_RING_SIZE)) fp@1744: device->debug_frame_count++; fp@1744: } fp@1744: fp@1744: /*****************************************************************************/ fp@1744: fp@1744: /** fp@1744: * Outputs the debug ring. fp@1744: */ fp@1744: fp@1744: void ec_device_debug_ring_print( fp@1744: const ec_device_t *device /**< EtherCAT device */ fp@1744: ) fp@1744: { fp@1744: int i; fp@1744: unsigned int ring_index; fp@1744: const ec_debug_frame_t *df; fp@1744: struct timeval t0, diff; fp@1744: fp@1744: // calculate index of the newest frame in the ring to get its time fp@1744: ring_index = (device->debug_frame_index + EC_DEBUG_RING_SIZE - 1) fp@1744: % EC_DEBUG_RING_SIZE; fp@1744: t0 = device->debug_frames[ring_index].t; fp@1744: fp@1744: EC_DBG("Debug ring %i:\n", ring_index); fp@1744: fp@1744: // calculate index of the oldest frame in the ring fp@1744: ring_index = (device->debug_frame_index + EC_DEBUG_RING_SIZE fp@1744: - device->debug_frame_count) % EC_DEBUG_RING_SIZE; fp@1744: fp@1744: for (i = 0; i < device->debug_frame_count; i++) { fp@1744: df = &device->debug_frames[ring_index]; fp@1744: timersub(&t0, &df->t, &diff); fp@1744: fp@1744: EC_DBG("Frame %i, dt=%u.%06u s, %s:\n", fp@1744: i + 1 - device->debug_frame_count, fp@1744: (unsigned int) diff.tv_sec, fp@1744: (unsigned int) diff.tv_usec, fp@1744: (df->dir == TX) ? "TX" : "RX"); fp@1744: ec_print_data(df->data, df->data_size); fp@1744: fp@1744: ring_index++; fp@1744: ring_index %= EC_DEBUG_RING_SIZE; fp@1744: } fp@1744: } fp@1744: #endif fp@1744: fp@1744: /*****************************************************************************/ fp@1744: fp@1739: /** fp@1739: Calls the poll function 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@1739: void ec_device_poll(ec_device_t *device /**< EtherCAT device */) fp@1739: { fp@1739: device->cycles_poll = get_cycles(); fp@1739: device->jiffies_poll = jiffies; fp@1744: #ifdef EC_DEBUG_RING fp@1744: do_gettimeofday(&device->timeval_poll); fp@1744: #endif fp@1739: device->poll(device->dev); 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@1744: const void *ec_data = data + ETH_HLEN; fp@1744: size_t ec_size = size - ETH_HLEN; fp@1739: device->rx_count++; fp@1739: fp@78: if (unlikely(device->master->debug_level > 1)) { fp@84: EC_DBG("Received frame:\n"); fp@1744: ec_print_data(ec_data, ec_size); fp@1744: } fp@1744: fp@1744: #ifdef EC_DEBUG_IF fp@231: ec_debug_send(&device->dbg, data, size); fp@1731: #endif fp@1744: #ifdef EC_DEBUG_RING fp@1744: ec_device_debug_ring_append(device, RX, ec_data, ec_size); fp@1744: #endif fp@1744: fp@1744: ec_master_receive_datagrams(device->master, ec_data, ec_size); 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@1744: void ecdev_set_link(ec_device_t *device, /**< EtherCAT device */ fp@1744: uint8_t state /**< new link state */ fp@1744: ) 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@1744: /** fp@1744: Reads the link state. fp@1744: \ingroup DeviceInterface fp@1744: */ fp@1744: fp@1744: uint8_t ecdev_get_link(ec_device_t *device /**< EtherCAT device */) fp@1744: { fp@1744: if (unlikely(!device)) { fp@1744: EC_WARN("ecdev_link_state: no device!\n"); fp@1744: return 0; fp@1744: } fp@1744: fp@1744: return device->link_state; fp@1744: } fp@1744: fp@1744: /*****************************************************************************/ fp@1744: fp@199: /** \cond */ fp@199: fp@104: EXPORT_SYMBOL(ecdev_receive); fp@1744: EXPORT_SYMBOL(ecdev_get_link); fp@1744: EXPORT_SYMBOL(ecdev_set_link); fp@27: fp@199: /** \endcond */ fp@199: fp@199: /*****************************************************************************/