fp@39: /****************************************************************************** fp@0: * fp@39: * $Id$ fp@0: * fp@1326: * Copyright (C) 2006-2008 Florian Pose, Ingenieurgemeinschaft IgH fp@197: * fp@197: * This file is part of the IgH EtherCAT Master. fp@197: * fp@1326: * The IgH EtherCAT Master is free software; you can redistribute it and/or fp@1326: * modify it under the terms of the GNU General Public License version 2, as fp@1326: * published by the Free Software Foundation. fp@1326: * fp@1326: * The IgH EtherCAT Master is distributed in the hope that it will be useful, fp@1326: * but WITHOUT ANY WARRANTY; without even the implied warranty of fp@1326: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General fp@1326: * Public License for more details. fp@1326: * fp@1326: * You should have received a copy of the GNU General Public License along fp@1326: * 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@1363: * --- fp@1363: * fp@1363: * The license mentioned above concerns the source code only. Using the fp@1363: * EtherCAT technology and brand is only permitted in compliance with the fp@1363: * industrial property and similar rights of Beckhoff Automation GmbH. 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@692: #ifdef EC_DEBUG_RING fp@692: #define timersub(a, b, result) \ fp@692: do { \ fp@692: (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ fp@692: (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \ fp@692: if ((result)->tv_usec < 0) { \ fp@692: --(result)->tv_sec; \ fp@692: (result)->tv_usec += 1000000; \ fp@692: } \ fp@692: } while (0) fp@692: #endif fp@692: fp@1851: static const unsigned int rate_intervals[] = { fp@1851: 1, 10, 60 fp@1851: }; fp@1851: fp@39: /*****************************************************************************/ fp@0: fp@1011: /** Constructor. fp@1011: * fp@1011: * \return 0 in case of success, else < 0 fp@1011: */ fp@1011: int ec_device_init( fp@1011: ec_device_t *device, /**< EtherCAT device */ fp@579: ec_master_t *master /**< master owning the device */ fp@579: ) fp@579: { fp@1313: int ret; fp@693: unsigned int i; fp@693: struct ethhdr *eth; fp@687: #ifdef EC_DEBUG_IF fp@687: char ifname[10]; fp@687: char mb = 'x'; fp@687: #endif fp@692: #ifdef EC_DEBUG_RING fp@692: device->debug_frame_index = 0; fp@692: device->debug_frame_count = 0; fp@692: #endif fp@687: fp@78: device->master = master; fp@693: device->tx_ring_index = 0; fp@579: fp@679: #ifdef EC_DEBUG_IF fp@687: if (device == &master->main_device) fp@687: mb = 'm'; fp@687: else if (device == &master->backup_device) fp@687: mb = 'b'; fp@687: fp@687: sprintf(ifname, "ecdbg%c%u", mb, master->index); fp@687: fp@1313: ret = ec_debug_init(&device->dbg, ifname); fp@1313: if (ret < 0) { fp@579: EC_ERR("Failed to init debug device!\n"); fp@579: goto out_return; fp@579: } fp@579: #endif fp@579: fp@693: for (i = 0; i < EC_TX_RING_SIZE; i++) fp@693: device->tx_skb[i] = NULL; fp@693: fp@693: for (i = 0; i < EC_TX_RING_SIZE; i++) { fp@693: if (!(device->tx_skb[i] = dev_alloc_skb(ETH_FRAME_LEN))) { fp@693: EC_ERR("Error allocating device socket buffer!\n"); fp@1313: ret = -ENOMEM; fp@693: goto out_tx_ring; fp@693: } fp@693: fp@693: // add Ethernet-II-header fp@693: skb_reserve(device->tx_skb[i], ETH_HLEN); fp@693: eth = (struct ethhdr *) skb_push(device->tx_skb[i], ETH_HLEN); fp@693: eth->h_proto = htons(0x88A4); fp@693: memset(eth->h_dest, 0xFF, ETH_ALEN); fp@693: } fp@579: fp@579: ec_device_detach(device); // resets remaining fields fp@579: return 0; fp@579: fp@693: out_tx_ring: fp@693: for (i = 0; i < EC_TX_RING_SIZE; i++) fp@693: if (device->tx_skb[i]) fp@693: dev_kfree_skb(device->tx_skb[i]); fp@693: #ifdef EC_DEBUG_IF fp@579: ec_debug_clear(&device->dbg); fp@693: out_return: fp@693: #endif fp@1313: return ret; fp@1313: } fp@1313: fp@1313: /*****************************************************************************/ fp@1313: fp@1313: /** Destructor. fp@1011: */ fp@1011: void ec_device_clear( fp@1011: ec_device_t *device /**< EtherCAT device */ fp@1011: ) fp@579: { fp@693: unsigned int i; fp@693: fp@579: if (device->open) ec_device_close(device); fp@693: for (i = 0; i < EC_TX_RING_SIZE; i++) fp@693: dev_kfree_skb(device->tx_skb[i]); fp@679: #ifdef EC_DEBUG_IF fp@579: ec_debug_clear(&device->dbg); fp@579: #endif fp@579: } fp@579: fp@579: /*****************************************************************************/ fp@579: fp@1011: /** Associate with net_device. fp@1011: */ fp@1011: void ec_device_attach( fp@1011: ec_device_t *device, /**< EtherCAT device */ fp@579: struct net_device *net_dev, /**< net_device structure */ fp@579: ec_pollfunc_t poll, /**< pointer to device's poll function */ fp@579: struct module *module /**< the device's module */ fp@579: ) fp@579: { fp@693: unsigned int i; fp@693: struct ethhdr *eth; fp@693: fp@579: ec_device_detach(device); // resets fields fp@579: fp@98: device->dev = net_dev; fp@533: device->poll = poll; fp@539: device->module = module; fp@98: fp@693: for (i = 0; i < EC_TX_RING_SIZE; i++) { fp@693: device->tx_skb[i]->dev = net_dev; fp@759: eth = (struct ethhdr *) (device->tx_skb[i]->data); fp@693: memcpy(eth->h_source, net_dev->dev_addr, ETH_ALEN); fp@693: } fp@1305: fp@1305: #ifdef EC_DEBUG_IF fp@1305: ec_debug_register(&device->dbg, net_dev); fp@1305: #endif fp@579: } fp@579: fp@579: /*****************************************************************************/ fp@579: fp@1011: /** Disconnect from net_device. fp@1011: */ fp@1011: void ec_device_detach( fp@1011: ec_device_t *device /**< EtherCAT device */ fp@1011: ) fp@579: { fp@693: unsigned int i; fp@693: fp@1305: #ifdef EC_DEBUG_IF fp@1305: ec_debug_unregister(&device->dbg); fp@1305: #endif fp@1305: fp@579: device->dev = NULL; fp@579: device->poll = NULL; fp@579: device->module = NULL; fp@78: device->open = 0; fp@96: device->link_state = 0; // down fp@493: device->tx_count = 0; fp@493: device->rx_count = 0; fp@1851: device->last_tx_count = 0; fp@1851: device->last_loss = 0; fp@1851: for (i = 0; i < EC_RATE_COUNT; i++) { fp@1851: device->tx_rates[i] = 0; fp@1851: device->loss_rates[i] = 0; fp@1851: } fp@1851: device->stats_jiffies = 0; fp@693: for (i = 0; i < EC_TX_RING_SIZE; i++) fp@693: device->tx_skb[i]->dev = NULL; fp@0: } fp@0: fp@39: /*****************************************************************************/ fp@0: fp@1011: /** Opens the EtherCAT device. fp@1011: * fp@1011: * \return 0 in case of success, else < 0 fp@1011: */ fp@1011: int ec_device_open( fp@1011: ec_device_t *device /**< EtherCAT device */ fp@1011: ) fp@0: { fp@1313: int ret; fp@1851: unsigned int i; fp@1313: fp@78: if (!device->dev) { fp@84: EC_ERR("No net_device to open!\n"); fp@1313: return -ENODEV; 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@493: device->tx_count = 0; fp@493: device->rx_count = 0; fp@1851: device->last_tx_count = 0; fp@1851: device->last_loss = 0; fp@1851: for (i = 0; i < EC_RATE_COUNT; i++) { fp@1851: device->tx_rates[i] = 0; fp@1851: device->loss_rates[i] = 0; fp@1851: } fp@1851: device->stats_jiffies = 0; fp@91: fp@1476: #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) fp@1476: ret = device->dev->netdev_ops->ndo_open(device->dev); fp@1476: #else fp@1313: ret = device->dev->open(device->dev); fp@1476: #endif fp@1313: if (!ret) fp@1313: device->open = 1; fp@1313: fp@1313: return ret; fp@0: } fp@0: fp@39: /*****************************************************************************/ fp@0: fp@1011: /** Stops the EtherCAT device. fp@1011: * fp@1011: * \return 0 in case of success, else < 0 fp@1011: */ fp@1011: int ec_device_close( fp@1011: ec_device_t *device /**< EtherCAT device */ fp@1011: ) fp@78: { fp@1313: int ret; fp@1313: fp@78: if (!device->dev) { fp@84: EC_ERR("No device to close!\n"); fp@1313: return -ENODEV; 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@1476: #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) fp@1476: ret = device->dev->netdev_ops->ndo_stop(device->dev); fp@1476: #else fp@1313: ret = device->dev->stop(device->dev); fp@1476: #endif fp@1313: if (!ret) fp@1313: device->open = 0; fp@1313: fp@1313: return ret; fp@73: } fp@73: fp@73: /*****************************************************************************/ fp@73: fp@1011: /** Returns a pointer to the device's transmit memory. fp@1011: * fp@1011: * \return pointer to the TX socket buffer fp@1011: */ fp@1011: uint8_t *ec_device_tx_data( fp@1011: ec_device_t *device /**< EtherCAT device */ fp@1011: ) fp@98: { fp@693: /* cycle through socket buffers, because otherwise there is a race fp@693: * condition, if multiple frames are sent and the DMA is not scheduled in fp@693: * between. */ fp@693: device->tx_ring_index++; fp@693: device->tx_ring_index %= EC_TX_RING_SIZE; fp@693: return device->tx_skb[device->tx_ring_index]->data + ETH_HLEN; fp@0: } fp@0: fp@39: /*****************************************************************************/ fp@0: fp@1011: /** Sends the content of the transmit socket buffer. fp@1011: * fp@1011: * Cuts the socket buffer content to the (now known) size, and calls the fp@1011: * start_xmit() function of the assigned net_device. fp@1011: */ fp@1011: void ec_device_send( fp@1011: ec_device_t *device, /**< EtherCAT device */ fp@1011: size_t size /**< number of bytes to send */ fp@1011: ) fp@73: { fp@693: struct sk_buff *skb = device->tx_skb[device->tx_ring_index]; fp@693: fp@1851: // frame statistics fp@1851: if (unlikely(jiffies - device->stats_jiffies >= HZ)) { fp@1851: unsigned int i; fp@1851: unsigned int tx_rate = device->tx_count - device->last_tx_count; fp@1851: int loss = device->tx_count - device->rx_count; fp@1851: int loss_rate = (loss - device->last_loss) * 1000; fp@1851: for (i = 0; i < EC_RATE_COUNT; i++) { fp@1851: unsigned int n = rate_intervals[i]; fp@1851: device->tx_rates[i] = fp@1851: device->tx_rates[i] * (n - 1) + fp@1851: tx_rate * n; fp@1851: device->loss_rates[i] = fp@1851: device->loss_rates[i] * (n - 1) + fp@1851: loss_rate * n; fp@1851: } fp@1851: device->last_tx_count = device->tx_count; fp@1851: device->last_loss = loss; fp@1851: device->stats_jiffies += HZ; fp@1851: } fp@1851: fp@97: if (unlikely(!device->link_state)) // Link down fp@96: return; fp@96: fp@195: // set the right length for the data fp@693: skb->len = ETH_HLEN + size; fp@78: fp@78: if (unlikely(device->master->debug_level > 1)) { fp@1554: EC_DBG("Sending frame:\n"); fp@1554: ec_print_data(skb->data, ETH_HLEN + size); fp@693: } fp@693: fp@195: // start sending fp@1476: #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) fp@1554: if (device->dev->netdev_ops->ndo_start_xmit(skb, device->dev) == fp@1554: NETDEV_TX_OK) fp@1476: #else fp@1554: if (device->dev->hard_start_xmit(skb, device->dev) == NETDEV_TX_OK) fp@1554: #endif fp@1554: { fp@1507: device->tx_count++; fp@1507: #ifdef EC_DEBUG_IF fp@1507: ec_debug_send(&device->dbg, skb->data, ETH_HLEN + size); fp@725: #endif fp@725: #ifdef EC_DEBUG_RING fp@1507: ec_device_debug_ring_append( fp@1507: device, TX, skb->data + ETH_HLEN, size); fp@1507: #endif fp@1507: } fp@73: } fp@73: fp@73: /*****************************************************************************/ fp@73: fp@692: #ifdef EC_DEBUG_RING fp@1011: /** Appends frame data to the debug ring. fp@1011: */ fp@692: void ec_device_debug_ring_append( fp@692: ec_device_t *device, /**< EtherCAT device */ fp@692: ec_debug_frame_dir_t dir, /**< direction */ fp@692: const void *data, /**< frame data */ fp@692: size_t size /**< data size */ fp@692: ) fp@692: { fp@692: ec_debug_frame_t *df = &device->debug_frames[device->debug_frame_index]; fp@692: fp@692: df->dir = dir; fp@692: if (dir == TX) fp@692: do_gettimeofday(&df->t); fp@692: else fp@692: df->t = device->timeval_poll; fp@692: memcpy(df->data, data, size); fp@692: df->data_size = size; fp@692: fp@692: device->debug_frame_index++; fp@692: device->debug_frame_index %= EC_DEBUG_RING_SIZE; fp@692: if (unlikely(device->debug_frame_count < EC_DEBUG_RING_SIZE)) fp@692: device->debug_frame_count++; fp@692: } fp@692: fp@692: /*****************************************************************************/ fp@692: fp@1011: /** Outputs the debug ring. fp@1011: */ fp@692: void ec_device_debug_ring_print( fp@692: const ec_device_t *device /**< EtherCAT device */ fp@692: ) fp@692: { fp@692: int i; fp@692: unsigned int ring_index; fp@692: const ec_debug_frame_t *df; fp@692: struct timeval t0, diff; fp@692: fp@692: // calculate index of the newest frame in the ring to get its time fp@692: ring_index = (device->debug_frame_index + EC_DEBUG_RING_SIZE - 1) fp@692: % EC_DEBUG_RING_SIZE; fp@692: t0 = device->debug_frames[ring_index].t; fp@692: fp@986: EC_DBG("Debug ring %u:\n", ring_index); fp@692: fp@692: // calculate index of the oldest frame in the ring fp@692: ring_index = (device->debug_frame_index + EC_DEBUG_RING_SIZE fp@692: - device->debug_frame_count) % EC_DEBUG_RING_SIZE; fp@692: fp@692: for (i = 0; i < device->debug_frame_count; i++) { fp@692: df = &device->debug_frames[ring_index]; fp@692: timersub(&t0, &df->t, &diff); fp@692: fp@986: EC_DBG("Frame %u, dt=%u.%06u s, %s:\n", fp@692: i + 1 - device->debug_frame_count, fp@692: (unsigned int) diff.tv_sec, fp@692: (unsigned int) diff.tv_usec, fp@692: (df->dir == TX) ? "TX" : "RX"); fp@692: ec_print_data(df->data, df->data_size); fp@692: fp@692: ring_index++; fp@692: ring_index %= EC_DEBUG_RING_SIZE; fp@692: } fp@692: } fp@692: #endif fp@692: fp@692: /*****************************************************************************/ fp@692: fp@1011: /** Calls the poll function of the assigned net_device. fp@1011: * fp@1011: * The master itself works without using interrupts. Therefore the processing fp@1011: * of received data and status changes of the network device has to be fp@1011: * done by the master calling the ISR "manually". fp@1011: */ fp@1011: void ec_device_poll( fp@1011: ec_device_t *device /**< EtherCAT device */ fp@1011: ) fp@533: { fp@1040: #ifdef EC_HAVE_CYCLES fp@533: device->cycles_poll = get_cycles(); fp@1040: #endif fp@533: device->jiffies_poll = jiffies; fp@692: #ifdef EC_DEBUG_RING fp@692: do_gettimeofday(&device->timeval_poll); fp@692: #endif fp@533: device->poll(device->dev); fp@39: } fp@39: fp@54: /****************************************************************************** fp@195: * Device interface fp@54: *****************************************************************************/ fp@54: fp@1011: /** Withdraws an EtherCAT device from the master. fp@1011: * fp@1011: * The device is disconnected from the master and all device ressources fp@1011: * are freed. fp@1011: * fp@1011: * \attention Before calling this function, the ecdev_stop() function has fp@1011: * to be called, to be sure that the master does not use the device fp@1011: * any more. fp@1011: * \ingroup DeviceInterface fp@1011: */ fp@1011: void ecdev_withdraw(ec_device_t *device /**< EtherCAT device */) fp@1011: { fp@1011: ec_master_t *master = device->master; fp@1011: char str[20]; fp@1011: fp@1011: ec_mac_print(device->dev->dev_addr, str); fp@1011: EC_INFO("Master %u releasing main device %s.\n", master->index, str); fp@1011: fp@1011: down(&master->device_sem); fp@1011: ec_device_detach(device); fp@1011: up(&master->device_sem); fp@1011: } fp@1011: fp@1011: /*****************************************************************************/ fp@1011: fp@1029: /** Opens the network device and makes the master enter IDLE phase. fp@1011: * fp@1011: * \return 0 on success, else < 0 fp@1011: * \ingroup DeviceInterface fp@1011: */ fp@1011: int ecdev_open(ec_device_t *device /**< EtherCAT device */) fp@1011: { fp@1313: int ret; fp@1313: fp@1313: ret = ec_device_open(device); fp@1313: if (ret) { fp@1011: EC_ERR("Failed to open device!\n"); fp@1313: return ret; fp@1313: } fp@1313: fp@1313: ret = ec_master_enter_idle_phase(device->master); fp@1313: if (ret) { fp@1029: EC_ERR("Failed to enter IDLE phase!\n"); fp@1313: return ret; fp@1011: } fp@1011: fp@1011: return 0; fp@1011: } fp@1011: fp@1011: /*****************************************************************************/ fp@1011: fp@1029: /** Makes the master leave IDLE phase and closes the network device. fp@1011: * fp@1011: * \return 0 on success, else < 0 fp@1011: * \ingroup DeviceInterface fp@1011: */ fp@1011: void ecdev_close(ec_device_t *device /**< EtherCAT device */) fp@1011: { fp@1029: ec_master_leave_idle_phase(device->master); fp@1011: fp@1011: if (ec_device_close(device)) fp@1011: EC_WARN("Failed to close device!\n"); fp@1011: } fp@1011: fp@1011: /*****************************************************************************/ fp@1011: fp@1011: /** Accepts a received frame. fp@1011: * fp@1011: * Forwards the received data to the master. The master will analyze the frame fp@1011: * and dispatch the received commands to the sending instances. fp@1011: * fp@1011: * \ingroup DeviceInterface fp@1011: */ fp@1011: void ecdev_receive( fp@1011: ec_device_t *device, /**< EtherCAT device */ fp@1011: const void *data, /**< pointer to received data */ fp@1011: size_t size /**< number of bytes received */ fp@1011: ) fp@73: { fp@692: const void *ec_data = data + ETH_HLEN; fp@692: size_t ec_size = size - ETH_HLEN; fp@1521: fp@1521: if (unlikely(!data)) { fp@1521: EC_WARN("%s() called with NULL data.\n", __func__); fp@1521: return; fp@1521: } fp@1521: fp@493: device->rx_count++; fp@493: fp@78: if (unlikely(device->master->debug_level > 1)) { fp@84: EC_DBG("Received frame:\n"); fp@1554: ec_print_data(data, size); fp@231: } fp@231: fp@679: #ifdef EC_DEBUG_IF fp@231: ec_debug_send(&device->dbg, data, size); fp@392: #endif fp@692: #ifdef EC_DEBUG_RING fp@692: ec_device_debug_ring_append(device, RX, ec_data, ec_size); fp@692: #endif fp@692: fp@692: ec_master_receive_datagrams(device->master, ec_data, ec_size); fp@54: } fp@54: fp@54: /*****************************************************************************/ fp@54: fp@1011: /** Sets a new link state. fp@1011: * fp@1011: * If the device notifies the master about the link being down, the master fp@1011: * will not try to send frames using this device. fp@1011: * fp@1011: * \ingroup DeviceInterface fp@1011: */ fp@1011: void ecdev_set_link( fp@1011: ec_device_t *device, /**< EtherCAT device */ fp@670: uint8_t state /**< new link state */ fp@670: ) fp@104: { fp@191: if (unlikely(!device)) { fp@1011: EC_WARN("ecdev_set_link(): 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@1011: /** Reads the link state. fp@1011: * fp@1011: * \ingroup DeviceInterface fp@1011: */ fp@1011: uint8_t ecdev_get_link( fp@1011: const ec_device_t *device /**< EtherCAT device */ fp@1011: ) fp@670: { fp@670: if (unlikely(!device)) { fp@1011: EC_WARN("ecdev_get_link(): No device!\n"); fp@670: return 0; fp@670: } fp@670: fp@670: return device->link_state; fp@670: } fp@670: fp@670: /*****************************************************************************/ fp@670: fp@199: /** \cond */ fp@199: fp@1011: EXPORT_SYMBOL(ecdev_withdraw); fp@1011: EXPORT_SYMBOL(ecdev_open); fp@1011: EXPORT_SYMBOL(ecdev_close); fp@104: EXPORT_SYMBOL(ecdev_receive); fp@670: EXPORT_SYMBOL(ecdev_get_link); fp@670: EXPORT_SYMBOL(ecdev_set_link); fp@27: fp@199: /** \endcond */ fp@199: fp@199: /*****************************************************************************/