fp@27: /****************************************************************************** fp@27: * fp@27: * $Id$ fp@27: * 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@27: * 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@27: fp@199: /** fp@199: \file fp@199: EtherCAT master driver module. fp@199: */ fp@199: fp@199: /*****************************************************************************/ fp@199: fp@27: #include fp@27: #include fp@27: #include fp@27: fp@184: #include "globals.h" fp@54: #include "master.h" fp@54: #include "device.h" fp@54: fp@54: /*****************************************************************************/ fp@54: fp@1744: #define MAX_MASTERS 5 /**< maximum number of masters */ fp@1744: fp@1744: /*****************************************************************************/ fp@1744: fp@54: int __init ec_init_module(void); fp@54: void __exit ec_cleanup_module(void); fp@52: fp@1744: static int ec_mac_parse(uint8_t *, const char *, int); fp@1744: fp@1744: /*****************************************************************************/ fp@1744: fp@1744: struct kobject kobj; /**< kobject for master module */ fp@1744: fp@1744: static char *main[MAX_MASTERS]; /**< main devices parameter */ fp@1744: static char *backup[MAX_MASTERS]; /**< backup devices parameter */ fp@1744: fp@1744: static ec_master_t *masters; /**< master array */ fp@1744: static struct semaphore master_sem; /**< master semaphore */ fp@1744: static unsigned int master_count; /**< number of masters */ fp@1744: static unsigned int backup_count; /**< number of backup devices */ fp@1744: fp@1744: static uint8_t macs[MAX_MASTERS][2][ETH_ALEN]; /**< MAC addresses */ fp@27: fp@1746: char *ec_master_version_str = EC_MASTER_VERSION; /**< master version string */ fp@1732: fp@39: /*****************************************************************************/ fp@39: fp@199: /** \cond */ fp@199: fp@251: MODULE_AUTHOR("Florian Pose "); fp@251: MODULE_DESCRIPTION("EtherCAT master driver module"); fp@27: MODULE_LICENSE("GPL"); fp@1731: MODULE_VERSION(EC_MASTER_VERSION); fp@1744: fp@1744: module_param_array(main, charp, &master_count, S_IRUGO); fp@1744: MODULE_PARM_DESC(main, "MAC addresses of main devices"); fp@1744: module_param_array(backup, charp, &backup_count, S_IRUGO); fp@1744: MODULE_PARM_DESC(backup, "MAC addresses of backup devices"); fp@195: fp@199: /** \endcond */ fp@199: fp@195: /*****************************************************************************/ fp@195: fp@195: /** fp@1744: * Module initialization. fp@1744: * Initializes \a ec_master_count masters. fp@1744: * \return 0 on success, else < 0 fp@1744: */ fp@54: fp@54: int __init ec_init_module(void) fp@27: { fp@1744: int i, ret = 0; fp@73: fp@1731: EC_INFO("Master driver %s\n", EC_MASTER_VERSION); fp@73: fp@1744: init_MUTEX(&master_sem); fp@1744: fp@1744: // init kobject and add it to the hierarchy fp@1744: memset(&kobj, 0x00, sizeof(struct kobject)); fp@1744: kobject_init(&kobj); // no ktype fp@1744: fp@1744: if (kobject_set_name(&kobj, "ethercat")) { fp@1744: EC_ERR("Failed to set module kobject name.\n"); fp@1744: ret = -ENOMEM; fp@1744: goto out_put; fp@1744: } fp@1744: fp@1744: if (kobject_add(&kobj)) { fp@1744: EC_ERR("Failed to add module kobject.\n"); fp@1744: ret = -EEXIST; fp@1744: goto out_put; fp@1744: } fp@1744: fp@1744: // zero MAC addresses fp@1744: memset(macs, 0x00, sizeof(uint8_t) * MAX_MASTERS * 2 * ETH_ALEN); fp@1744: fp@1744: // process MAC parameters fp@1744: for (i = 0; i < master_count; i++) { fp@1744: if (ec_mac_parse(macs[i][0], main[i], 0)) { fp@1744: ret = -EINVAL; fp@1744: goto out_del; fp@1744: } fp@1744: fp@1744: if (i < backup_count && ec_mac_parse(macs[i][1], backup[i], 1)) { fp@1744: ret = -EINVAL; fp@1744: goto out_del; fp@1744: } fp@1744: } fp@1744: fp@1744: if (master_count) { fp@1744: if (!(masters = kmalloc(sizeof(ec_master_t) * master_count, fp@1744: GFP_KERNEL))) { fp@1744: EC_ERR("Failed to allocate memory for EtherCAT masters.\n"); fp@1744: ret = -ENOMEM; fp@1744: goto out_del; fp@1744: } fp@1744: } fp@1744: fp@1744: for (i = 0; i < master_count; i++) { fp@1744: if (ec_master_init(&masters[i], &kobj, i, macs[i][0], macs[i][1])) { fp@1744: ret = -EIO; fp@1744: goto out_free_masters; fp@1744: } fp@1744: } fp@1744: fp@1744: EC_INFO("%u master%s waiting for devices.\n", fp@1744: master_count, (master_count == 1 ? "" : "s")); fp@1744: return ret; fp@1744: fp@1744: out_free_masters: fp@1744: for (i--; i >= 0; i--) ec_master_clear(&masters[i]); fp@1744: kfree(masters); fp@1744: out_del: fp@1744: kobject_del(&kobj); fp@1744: out_put: fp@1744: kobject_put(&kobj); fp@1744: return ret; fp@27: } fp@27: fp@39: /*****************************************************************************/ fp@27: fp@27: /** fp@195: Module cleanup. fp@195: Clears all master instances. fp@27: */ fp@27: fp@54: void __exit ec_cleanup_module(void) fp@27: { fp@1744: unsigned int i; fp@1744: fp@1744: for (i = 0; i < master_count; i++) { fp@1744: ec_master_clear(&masters[i]); fp@1744: } fp@1744: if (master_count) fp@1744: kfree(masters); fp@1744: fp@1744: kobject_del(&kobj); fp@1744: kobject_put(&kobj); fp@1744: fp@1744: EC_INFO("Master module cleaned up.\n"); fp@1744: } fp@1744: fp@1744: /***************************************************************************** fp@1744: * MAC address functions fp@1744: ****************************************************************************/ fp@1744: fp@1746: /** fp@1746: * \return true, if two MAC addresses are equal. fp@1746: */ fp@1746: fp@1744: int ec_mac_equal(const uint8_t *mac1, const uint8_t *mac2) fp@1744: { fp@1744: unsigned int i; fp@1744: fp@1744: for (i = 0; i < ETH_ALEN; i++) fp@1744: if (mac1[i] != mac2[i]) fp@1744: return 0; fp@1744: fp@1744: return 1; fp@1744: } fp@1744: fp@1744: /*****************************************************************************/ fp@1744: fp@1746: /** fp@1746: * Print a MAC address to a buffer. fp@1746: * \return number of bytes written. fp@1746: */ fp@1746: fp@1746: ssize_t ec_mac_print( fp@1746: const uint8_t *mac, /**< MAC address */ fp@1746: char *buffer /**< target buffer */ fp@1746: ) fp@1744: { fp@1744: off_t off = 0; fp@1744: unsigned int i; fp@1744: fp@1744: for (i = 0; i < ETH_ALEN; i++) { fp@1744: off += sprintf(buffer + off, "%02X", mac[i]); fp@1744: if (i < ETH_ALEN - 1) off += sprintf(buffer + off, ":"); fp@1744: } fp@1744: fp@1744: return off; fp@1744: } fp@1744: fp@1744: /*****************************************************************************/ fp@1744: fp@1746: /** fp@1746: * \return true, if the MAC address is all-zero. fp@1746: */ fp@1746: fp@1744: int ec_mac_is_zero(const uint8_t *mac) fp@1744: { fp@1744: unsigned int i; fp@1744: fp@1744: for (i = 0; i < ETH_ALEN; i++) fp@1744: if (mac[i]) fp@1744: return 0; fp@1744: fp@1744: return 1; fp@1744: } fp@1744: fp@1744: /*****************************************************************************/ fp@1744: fp@1746: /** fp@1746: * \return true, if the given MAC address is the broadcast address. fp@1746: */ fp@1746: fp@1744: int ec_mac_is_broadcast(const uint8_t *mac) fp@1744: { fp@1744: unsigned int i; fp@1744: fp@1744: for (i = 0; i < ETH_ALEN; i++) fp@1744: if (mac[i] != 0xff) fp@1744: return 0; fp@1744: fp@1744: return 1; fp@1744: } fp@1744: fp@1744: /*****************************************************************************/ fp@1744: fp@1746: /** fp@1746: * Parse a MAC address from a string. fp@1746: * The MAC address must follow the regexp fp@1746: * "([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}". fp@1746: \return 0 on success, else < 0 fp@1746: */ fp@1746: fp@1744: static int ec_mac_parse(uint8_t *mac, const char *src, int allow_empty) fp@1744: { fp@1744: unsigned int i, value; fp@1744: const char *orig = src; fp@1744: char *rem; fp@1744: fp@1744: if (!strlen(src)) { fp@1744: if (allow_empty){ fp@1744: return 0; fp@1744: } fp@1744: else { fp@1744: EC_ERR("MAC address may not be empty.\n"); fp@1744: return -EINVAL; fp@1744: } fp@1744: } fp@1744: fp@1744: for (i = 0; i < ETH_ALEN; i++) { fp@1744: value = simple_strtoul(src, &rem, 16); fp@1744: if (rem != src + 2 fp@1744: || value > 0xFF fp@1744: || (i < ETH_ALEN - 1 && *rem != ':')) { fp@1744: EC_ERR("Invalid MAC address \"%s\".\n", orig); fp@1744: return -EINVAL; fp@1744: } fp@1744: mac[i] = value; fp@1744: if (i < ETH_ALEN - 1) fp@1744: src = rem + 1; // skip colon fp@1744: } fp@1744: fp@1744: return 0; fp@178: } fp@178: fp@196: /*****************************************************************************/ fp@196: fp@196: /** fp@196: Outputs frame contents for debugging purposes. fp@196: */ fp@196: fp@196: void ec_print_data(const uint8_t *data, /**< pointer to data */ fp@196: size_t size /**< number of bytes to output */ fp@196: ) fp@196: { fp@196: unsigned int i; fp@196: fp@196: EC_DBG(""); fp@196: for (i = 0; i < size; i++) { fp@196: printk("%02X ", data[i]); fp@196: if ((i + 1) % 16 == 0) { fp@196: printk("\n"); fp@196: EC_DBG(""); fp@196: } fp@196: } fp@196: printk("\n"); fp@196: } fp@196: fp@196: /*****************************************************************************/ fp@196: fp@196: /** fp@196: Outputs frame contents and differences for debugging purposes. fp@196: */ fp@196: fp@196: void ec_print_data_diff(const uint8_t *d1, /**< first data */ fp@196: const uint8_t *d2, /**< second data */ fp@196: size_t size /** number of bytes to output */ fp@196: ) fp@196: { fp@196: unsigned int i; fp@196: fp@196: EC_DBG(""); fp@196: for (i = 0; i < size; i++) { fp@196: if (d1[i] == d2[i]) printk(".. "); fp@196: else printk("%02X ", d2[i]); fp@196: if ((i + 1) % 16 == 0) { fp@196: printk("\n"); fp@196: EC_DBG(""); fp@196: } fp@196: } fp@196: printk("\n"); fp@196: } fp@196: fp@251: /*****************************************************************************/ fp@251: fp@251: /** fp@251: Prints slave states in clear text. fp@251: */ fp@251: fp@1715: size_t ec_state_string(uint8_t states, /**< slave states */ fp@1732: char *buffer /**< target buffer fp@1732: (min. EC_STATE_STRING_SIZE bytes) */ fp@1715: ) fp@1715: { fp@1715: off_t off = 0; fp@251: unsigned int first = 1; fp@251: fp@251: if (!states) { fp@1715: off += sprintf(buffer + off, "(unknown)"); fp@1715: return off; fp@251: } fp@251: fp@251: if (states & EC_SLAVE_STATE_INIT) { fp@1715: off += sprintf(buffer + off, "INIT"); fp@251: first = 0; fp@251: } fp@251: if (states & EC_SLAVE_STATE_PREOP) { fp@1715: if (!first) off += sprintf(buffer + off, ", "); fp@1715: off += sprintf(buffer + off, "PREOP"); fp@251: first = 0; fp@251: } fp@251: if (states & EC_SLAVE_STATE_SAVEOP) { fp@1715: if (!first) off += sprintf(buffer + off, ", "); fp@1715: off += sprintf(buffer + off, "SAVEOP"); fp@251: first = 0; fp@251: } fp@251: if (states & EC_SLAVE_STATE_OP) { fp@1715: if (!first) off += sprintf(buffer + off, ", "); fp@1715: off += sprintf(buffer + off, "OP"); fp@1715: } fp@1732: if (states & EC_SLAVE_STATE_ACK_ERR) { fp@1732: if (!first) off += sprintf(buffer + off, " + "); fp@1732: off += sprintf(buffer + off, "ERROR"); fp@1732: } fp@1715: fp@1715: return off; fp@251: } fp@251: fp@54: /****************************************************************************** fp@195: * Device interface fp@54: *****************************************************************************/ fp@33: fp@33: /** fp@1744: Offers an EtherCAT device to a certain master. fp@1744: The master decides, if it wants to use the device for EtherCAT operation, fp@1744: or not. It is important, that the offered net_device is not used by fp@1744: the kernel IP stack. If the master, accepted the offer, the address of fp@1744: the newly created EtherCAT device is written to the ecdev pointer, else fp@1744: the pointer is written to zero. fp@195: \return 0 on success, else < 0 fp@199: \ingroup DeviceInterface fp@195: */ fp@195: fp@1744: int ecdev_offer(struct net_device *net_dev, /**< net_device to offer */ fp@1744: ec_pollfunc_t poll, /**< device poll function */ fp@1744: struct module *module, /**< pointer to the module */ fp@1744: ec_device_t **ecdev /**< pointer to store a device on success */ fp@1744: ) fp@91: { fp@73: ec_master_t *master; fp@1744: char str[20]; fp@1744: unsigned int i; fp@1744: fp@1744: for (i = 0; i < master_count; i++) { fp@1744: master = &masters[i]; fp@1744: fp@1744: down(&master->device_sem); fp@1744: if (master->main_device.dev) { // master already has a device fp@1744: up(&master->device_sem); fp@1744: continue; fp@1744: } fp@1744: fp@1744: if (ec_mac_equal(master->main_mac, net_dev->dev_addr) fp@1744: || ec_mac_is_broadcast(master->main_mac)) { fp@1744: ec_mac_print(net_dev->dev_addr, str); fp@1744: EC_INFO("Accepting device %s for master %u.\n", fp@1744: str, master->index); fp@1744: fp@1744: ec_device_attach(&master->main_device, net_dev, poll, module); fp@1744: up(&master->device_sem); fp@1744: fp@1744: sprintf(net_dev->name, "ec%u", master->index); fp@1744: *ecdev = &master->main_device; // offer accepted fp@1744: return 0; // no error fp@1744: } fp@1744: else { fp@1744: up(&master->device_sem); fp@1744: fp@1744: if (master->debug_level) { fp@1744: ec_mac_print(net_dev->dev_addr, str); fp@1744: EC_DBG("Master %u declined device %s.\n", master->index, str); fp@1744: } fp@1744: } fp@1744: } fp@1744: fp@1744: *ecdev = NULL; // offer declined fp@1744: return 0; // no error fp@1744: } fp@1744: fp@1744: /*****************************************************************************/ fp@1744: fp@1744: /** fp@1744: Withdraws an EtherCAT device from the master. fp@199: The device is disconnected from the master and all device ressources fp@199: are freed. fp@199: \attention Before calling this function, the ecdev_stop() function has fp@199: to be called, to be sure that the master does not use the device any more. fp@199: \ingroup DeviceInterface fp@195: */ fp@195: fp@1744: void ecdev_withdraw(ec_device_t *device /**< EtherCAT device */) fp@1744: { fp@1744: ec_master_t *master = device->master; fp@1744: char str[20]; fp@1744: fp@1744: ec_mac_print(device->dev->dev_addr, str); fp@1744: EC_INFO("Master %u releasing main device %s.\n", master->index, str); fp@1744: fp@1731: down(&master->device_sem); fp@1744: ec_device_detach(device); fp@1731: up(&master->device_sem); fp@54: } fp@54: fp@191: /*****************************************************************************/ fp@191: fp@191: /** fp@1739: Opens the network device and makes the master enter IDLE mode. fp@1739: \return 0 on success, else < 0 fp@199: \ingroup DeviceInterface fp@195: */ fp@195: fp@1739: int ecdev_open(ec_device_t *device /**< EtherCAT device */) fp@1739: { fp@1739: if (ec_device_open(device)) { fp@191: EC_ERR("Failed to open device!\n"); fp@191: return -1; fp@191: } fp@191: fp@1739: if (ec_master_enter_idle_mode(device->master)) { fp@1739: EC_ERR("Failed to enter idle mode!\n"); fp@1739: return -1; fp@1739: } fp@1732: fp@191: return 0; fp@191: } fp@191: fp@191: /*****************************************************************************/ fp@191: fp@191: /** fp@1739: Makes the master leave IDLE mode and closes the network device. fp@1739: \return 0 on success, else < 0 fp@199: \ingroup DeviceInterface fp@195: */ fp@195: fp@1739: void ecdev_close(ec_device_t *device /**< EtherCAT device */) fp@1739: { fp@1739: ec_master_leave_idle_mode(device->master); fp@1739: fp@1739: if (ec_device_close(device)) fp@191: EC_WARN("Failed to close device!\n"); fp@191: } fp@191: fp@54: /****************************************************************************** fp@195: * Realtime interface fp@54: *****************************************************************************/ fp@33: fp@33: /** fp@1739: * Returns the version magic of the realtime interface. fp@1739: * \return ECRT version magic. fp@1739: * \ingroup RealtimeInterface fp@1739: */ fp@1739: fp@1739: unsigned int ecrt_version_magic(void) fp@1739: { fp@1739: return ECRT_VERSION_MAGIC; fp@1739: } fp@1739: fp@1739: /*****************************************************************************/ fp@1739: fp@1739: /** fp@195: Reserves an EtherCAT master for realtime operation. fp@195: \return pointer to reserved master, or NULL on error fp@199: \ingroup RealtimeInterface fp@91: */ fp@91: fp@178: ec_master_t *ecrt_request_master(unsigned int master_index fp@195: /**< master index */ fp@104: ) fp@54: { fp@73: ec_master_t *master; fp@73: fp@1744: EC_INFO("Requesting master %u...\n", master_index); fp@1744: fp@1744: if (master_index >= master_count) { fp@1744: EC_ERR("Invalid master index %u.\n", master_index); fp@191: goto out_return; fp@73: } fp@1744: master = &masters[master_index]; fp@1744: fp@1744: down(&master_sem); fp@1744: if (master->reserved) { fp@1744: up(&master_sem); fp@1744: EC_ERR("Master %u is already in use!\n", master_index); fp@1744: goto out_return; fp@1744: } fp@1744: master->reserved = 1; fp@1744: up(&master_sem); fp@1744: fp@1744: down(&master->device_sem); fp@1744: fp@1744: if (master->mode != EC_MASTER_MODE_IDLE) { fp@1744: up(&master->device_sem); fp@1744: EC_ERR("Master %u still waiting for devices!\n", master_index); fp@1731: goto out_release; fp@1731: } fp@1731: fp@1744: if (!try_module_get(master->main_device.module)) { fp@1731: up(&master->device_sem); fp@1731: EC_ERR("Device module is unloading!\n"); fp@191: goto out_release; fp@191: } fp@191: fp@1731: up(&master->device_sem); fp@1731: fp@1744: if (!master->main_device.link_state) { fp@1728: EC_ERR("Link is DOWN.\n"); fp@1728: goto out_module_put; fp@1728: } fp@1728: fp@1732: if (ec_master_enter_operation_mode(master)) { fp@1732: EC_ERR("Failed to enter OPERATION mode!\n"); fp@1732: goto out_module_put; fp@1728: } fp@1728: fp@1744: EC_INFO("Successfully requested master %u.\n", master_index); fp@73: return master; fp@54: fp@191: out_module_put: fp@1744: module_put(master->main_device.module); fp@1728: out_release: fp@1744: master->reserved = 0; fp@1728: out_return: fp@1728: return NULL; fp@1728: } fp@1728: fp@1728: /*****************************************************************************/ fp@1728: fp@1728: /** fp@1728: Releases a reserved EtherCAT master. fp@1728: \ingroup RealtimeInterface fp@1728: */ fp@1728: fp@1728: void ecrt_release_master(ec_master_t *master /**< EtherCAT master */) fp@1728: { fp@1744: EC_INFO("Releasing master %u...\n", master->index); fp@1739: fp@1739: if (master->mode != EC_MASTER_MODE_OPERATION) { fp@1744: EC_WARN("Master %u was was not requested!\n", master->index); fp@1739: return; fp@1739: } fp@1739: fp@1732: ec_master_leave_operation_mode(master); fp@151: fp@1744: module_put(master->main_device.module); fp@1744: master->reserved = 0; fp@1744: fp@1744: EC_INFO("Released master %u.\n", master->index); fp@33: } fp@33: fp@39: /*****************************************************************************/ fp@54: fp@199: /** \cond */ fp@199: fp@54: module_init(ec_init_module); fp@54: module_exit(ec_cleanup_module); fp@54: fp@1744: EXPORT_SYMBOL(ecdev_offer); fp@1744: EXPORT_SYMBOL(ecdev_withdraw); fp@1739: EXPORT_SYMBOL(ecdev_open); fp@1739: EXPORT_SYMBOL(ecdev_close); fp@104: EXPORT_SYMBOL(ecrt_request_master); fp@104: EXPORT_SYMBOL(ecrt_release_master); fp@1739: EXPORT_SYMBOL(ecrt_version_magic); fp@54: fp@199: /** \endcond */ fp@199: fp@199: /*****************************************************************************/