fp@27: /****************************************************************************** fp@27: * fp@27: * $Id$ fp@27: * 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@27: * 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@27: fp@792: /** \file fp@792: * EtherCAT master driver module. fp@792: */ fp@199: fp@199: /*****************************************************************************/ fp@199: fp@27: #include fp@1014: #include fp@1239: #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@1814: #define MAX_MASTERS 32 /**< Maximum number of masters. */ fp@639: fp@639: /*****************************************************************************/ fp@639: fp@54: int __init ec_init_module(void); fp@54: void __exit ec_cleanup_module(void); fp@52: fp@639: static int ec_mac_parse(uint8_t *, const char *, int); fp@639: fp@639: /*****************************************************************************/ fp@639: fp@806: static char *main_devices[MAX_MASTERS]; /**< Main devices parameter. */ fp@1399: static unsigned int master_count; /**< Number of masters. */ fp@806: static char *backup_devices[MAX_MASTERS]; /**< Backup devices parameter. */ fp@1399: static unsigned int backup_count; /**< Number of backup devices. */ fp@1399: static unsigned int debug_level; /**< Debug level parameter. */ fp@806: fp@806: static ec_master_t *masters; /**< Array of masters. */ fp@806: static struct semaphore master_sem; /**< Master semaphore. */ fp@806: fp@954: dev_t device_number; /**< Device number for master cdevs. */ fp@1012: struct class *class; /**< Device class. */ fp@922: fp@806: static uint8_t macs[MAX_MASTERS][2][ETH_ALEN]; /**< MAC addresses. */ fp@806: fp@806: char *ec_master_version_str = EC_MASTER_VERSION; /**< Version string. */ fp@444: 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@382: MODULE_VERSION(EC_MASTER_VERSION); fp@573: fp@806: module_param_array(main_devices, charp, &master_count, S_IRUGO); fp@806: MODULE_PARM_DESC(main_devices, "MAC addresses of main devices"); fp@806: module_param_array(backup_devices, charp, &backup_count, S_IRUGO); fp@806: MODULE_PARM_DESC(backup_devices, "MAC addresses of backup devices"); fp@1399: module_param_named(debug_level, debug_level, uint, S_IRUGO); fp@1399: MODULE_PARM_DESC(debug_level, "Debug level"); fp@195: fp@199: /** \endcond */ fp@199: fp@195: /*****************************************************************************/ fp@195: fp@806: /** Module initialization. fp@806: * fp@922: * Initializes \a master_count masters. fp@639: * \return 0 on success, else < 0 fp@639: */ fp@54: int __init ec_init_module(void) fp@27: { fp@639: int i, ret = 0; fp@73: fp@382: EC_INFO("Master driver %s\n", EC_MASTER_VERSION); fp@73: martin@1579: sema_init(&master_sem, 1); fp@653: fp@997: if (master_count) { fp@1921: if (alloc_chrdev_region(&device_number, fp@1921: 0, master_count, "EtherCAT")) { fp@997: EC_ERR("Failed to obtain device number(s)!\n"); fp@997: ret = -EBUSY; fp@997: goto out_return; fp@997: } fp@922: } fp@922: fp@1012: class = class_create(THIS_MODULE, "EtherCAT"); fp@1012: if (IS_ERR(class)) { fp@1012: EC_ERR("Failed to create device class.\n"); fp@1313: ret = PTR_ERR(class); fp@1012: goto out_cdev; fp@1012: } fp@1012: fp@639: // zero MAC addresses fp@639: memset(macs, 0x00, sizeof(uint8_t) * MAX_MASTERS * 2 * ETH_ALEN); fp@639: fp@639: // process MAC parameters fp@639: for (i = 0; i < master_count; i++) { fp@1313: ret = ec_mac_parse(macs[i][0], main_devices[i], 0); fp@1313: if (ret) fp@1012: goto out_class; fp@2382: fp@1313: if (i < backup_count) { fp@1313: ret = ec_mac_parse(macs[i][1], backup_devices[i], 1); fp@1313: if (ret) fp@1313: goto out_class; fp@639: } fp@639: } fp@1279: fp@1279: // initialize static master variables fp@1279: ec_master_init_static(); fp@2382: fp@639: if (master_count) { fp@639: if (!(masters = kmalloc(sizeof(ec_master_t) * master_count, fp@639: GFP_KERNEL))) { fp@1921: EC_ERR("Failed to allocate memory" fp@1921: " for EtherCAT masters.\n"); fp@639: ret = -ENOMEM; fp@1012: goto out_class; fp@639: } fp@639: } fp@2382: fp@639: for (i = 0; i < master_count; i++) { fp@1313: ret = ec_master_init(&masters[i], i, macs[i][0], macs[i][1], fp@1399: device_number, class, debug_level); fp@1313: if (ret) fp@639: goto out_free_masters; fp@573: } fp@2382: fp@573: EC_INFO("%u master%s waiting for devices.\n", fp@639: master_count, (master_count == 1 ? "" : "s")); fp@639: return ret; fp@127: fp@573: out_free_masters: fp@997: for (i--; i >= 0; i--) fp@997: ec_master_clear(&masters[i]); fp@639: kfree(masters); fp@1012: out_class: fp@1012: class_destroy(class); fp@922: out_cdev: fp@1012: if (master_count) fp@1012: unregister_chrdev_region(device_number, master_count); fp@997: out_return: fp@639: return ret; fp@27: } fp@27: fp@39: /*****************************************************************************/ fp@27: fp@806: /** Module cleanup. fp@806: * fp@806: * Clears all master instances. fp@806: */ fp@54: void __exit ec_cleanup_module(void) fp@27: { fp@639: unsigned int i; fp@639: fp@639: for (i = 0; i < master_count; i++) { fp@639: ec_master_clear(&masters[i]); fp@639: } fp@1012: fp@1012: if (master_count) fp@639: kfree(masters); fp@2382: fp@1012: class_destroy(class); fp@2382: fp@1012: if (master_count) fp@997: unregister_chrdev_region(device_number, master_count); fp@2382: fp@575: EC_INFO("Master module cleaned up.\n"); fp@27: } fp@27: fp@1826: /*****************************************************************************/ fp@1826: fp@1826: /** Get the number of masters. fp@1826: */ fp@1826: unsigned int ec_master_count(void) fp@1826: { fp@1826: return master_count; fp@1826: } fp@1826: fp@639: /***************************************************************************** fp@639: * MAC address functions fp@639: ****************************************************************************/ fp@639: fp@758: /** fp@758: * \return true, if two MAC addresses are equal. fp@758: */ fp@840: int ec_mac_equal( fp@840: const uint8_t *mac1, /**< First MAC address. */ fp@840: const uint8_t *mac2 /**< Second MAC address. */ fp@840: ) fp@639: { fp@639: unsigned int i; fp@2382: fp@639: for (i = 0; i < ETH_ALEN; i++) fp@639: if (mac1[i] != mac2[i]) fp@639: return 0; fp@639: fp@639: return 1; fp@639: } fp@2382: fp@639: /*****************************************************************************/ fp@639: fp@2267: /** Maximum MAC string size. fp@2267: */ fp@2267: #define EC_MAX_MAC_STRING_SIZE (3 * ETH_ALEN) fp@2267: fp@806: /** Print a MAC address to a buffer. fp@806: * fp@2267: * The buffer size must be at least EC_MAX_MAC_STRING_SIZE. fp@2267: * fp@758: * \return number of bytes written. fp@758: */ fp@758: ssize_t ec_mac_print( fp@758: const uint8_t *mac, /**< MAC address */ fp@2267: char *buffer /**< Target buffer. */ fp@758: ) fp@639: { fp@639: off_t off = 0; fp@639: unsigned int i; fp@2382: fp@639: for (i = 0; i < ETH_ALEN; i++) { fp@639: off += sprintf(buffer + off, "%02X", mac[i]); fp@639: if (i < ETH_ALEN - 1) off += sprintf(buffer + off, ":"); fp@639: } fp@639: fp@639: return off; fp@639: } fp@639: fp@639: /*****************************************************************************/ fp@639: fp@758: /** fp@758: * \return true, if the MAC address is all-zero. fp@758: */ fp@840: int ec_mac_is_zero( fp@840: const uint8_t *mac /**< MAC address. */ fp@840: ) fp@639: { fp@639: unsigned int i; fp@2382: fp@639: for (i = 0; i < ETH_ALEN; i++) fp@639: if (mac[i]) fp@639: return 0; fp@639: fp@639: return 1; fp@639: } fp@639: fp@639: /*****************************************************************************/ fp@639: fp@758: /** fp@758: * \return true, if the given MAC address is the broadcast address. fp@758: */ fp@840: int ec_mac_is_broadcast( fp@840: const uint8_t *mac /**< MAC address. */ fp@840: ) fp@700: { fp@700: unsigned int i; fp@2382: fp@700: for (i = 0; i < ETH_ALEN; i++) fp@700: if (mac[i] != 0xff) fp@700: return 0; fp@700: fp@700: return 1; fp@700: } fp@700: fp@700: /*****************************************************************************/ fp@700: fp@806: /** Parse a MAC address from a string. fp@806: * fp@2129: * The MAC address must match the regular expression fp@758: * "([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}". fp@806: * fp@806: * \return 0 on success, else < 0 fp@806: */ fp@639: static int ec_mac_parse(uint8_t *mac, const char *src, int allow_empty) fp@639: { fp@639: unsigned int i, value; fp@639: const char *orig = src; fp@639: char *rem; fp@639: fp@639: if (!strlen(src)) { fp@639: if (allow_empty){ fp@639: return 0; fp@2129: } else { fp@639: EC_ERR("MAC address may not be empty.\n"); fp@639: return -EINVAL; fp@639: } fp@639: } fp@639: fp@639: for (i = 0; i < ETH_ALEN; i++) { fp@639: value = simple_strtoul(src, &rem, 16); fp@639: if (rem != src + 2 fp@639: || value > 0xFF fp@639: || (i < ETH_ALEN - 1 && *rem != ':')) { fp@639: EC_ERR("Invalid MAC address \"%s\".\n", orig); fp@639: return -EINVAL; fp@639: } fp@639: mac[i] = value; fp@2129: if (i < ETH_ALEN - 1) { fp@639: src = rem + 1; // skip colon fp@2129: } fp@639: } fp@639: fp@639: return 0; fp@178: } fp@178: fp@196: /*****************************************************************************/ fp@196: fp@806: /** Outputs frame contents for debugging purposes. martin@1584: * If the data block is larger than 256 bytes, only the first 128 martin@1584: * and the last 128 bytes will be shown fp@806: */ 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@1930: fp@1469: if ((i + 1) % 16 == 0 && i < size - 1) { fp@196: printk("\n"); fp@196: EC_DBG(""); fp@196: } fp@1930: fp@1930: if (i + 1 == 128 && size > 256) { fp@1930: printk("dropped %zu bytes\n", size - 128 - i); martin@1583: i = size - 128; martin@1584: EC_DBG(""); martin@1583: } fp@196: } fp@196: printk("\n"); fp@196: } fp@196: fp@196: /*****************************************************************************/ fp@196: fp@806: /** Outputs frame contents and differences for debugging purposes. fp@806: */ 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@806: /** Prints slave states in clear text. fp@2522: * fp@2522: * \return Size of the created string. fp@806: */ fp@325: size_t ec_state_string(uint8_t states, /**< slave states */ fp@1337: char *buffer, /**< target buffer fp@403: (min. EC_STATE_STRING_SIZE bytes) */ fp@1337: uint8_t multi /**< Show multi-state mask. */ fp@325: ) fp@325: { fp@325: off_t off = 0; fp@251: unsigned int first = 1; fp@251: fp@251: if (!states) { fp@325: off += sprintf(buffer + off, "(unknown)"); fp@325: return off; fp@251: } fp@251: fp@1337: if (multi) { // multiple slaves fp@1337: if (states & EC_SLAVE_STATE_INIT) { fp@1337: off += sprintf(buffer + off, "INIT"); fp@1337: first = 0; fp@1337: } fp@1337: if (states & EC_SLAVE_STATE_PREOP) { fp@1337: if (!first) off += sprintf(buffer + off, ", "); fp@1337: off += sprintf(buffer + off, "PREOP"); fp@1337: first = 0; fp@1337: } fp@1337: if (states & EC_SLAVE_STATE_SAFEOP) { fp@1337: if (!first) off += sprintf(buffer + off, ", "); fp@1337: off += sprintf(buffer + off, "SAFEOP"); fp@1337: first = 0; fp@1337: } fp@1337: if (states & EC_SLAVE_STATE_OP) { fp@1337: if (!first) off += sprintf(buffer + off, ", "); fp@1337: off += sprintf(buffer + off, "OP"); fp@1337: } fp@1337: } else { // single slave fp@1337: if ((states & EC_SLAVE_STATE_MASK) == EC_SLAVE_STATE_INIT) { fp@1337: off += sprintf(buffer + off, "INIT"); fp@1337: } else if ((states & EC_SLAVE_STATE_MASK) == EC_SLAVE_STATE_PREOP) { fp@1337: off += sprintf(buffer + off, "PREOP"); fp@1337: } else if ((states & EC_SLAVE_STATE_MASK) == EC_SLAVE_STATE_BOOT) { fp@1337: off += sprintf(buffer + off, "BOOT"); fp@1337: } else if ((states & EC_SLAVE_STATE_MASK) == EC_SLAVE_STATE_SAFEOP) { fp@1337: off += sprintf(buffer + off, "SAFEOP"); fp@1337: } else if ((states & EC_SLAVE_STATE_MASK) == EC_SLAVE_STATE_OP) { fp@1337: off += sprintf(buffer + off, "OP"); fp@1337: } else { fp@1337: off += sprintf(buffer + off, "(invalid)"); fp@1337: } fp@251: first = 0; fp@251: } fp@1337: fp@403: if (states & EC_SLAVE_STATE_ACK_ERR) { fp@453: if (!first) off += sprintf(buffer + off, " + "); fp@453: off += sprintf(buffer + off, "ERROR"); fp@403: } fp@325: fp@325: return off; fp@251: } fp@251: fp@54: /****************************************************************************** fp@195: * Device interface fp@54: *****************************************************************************/ fp@33: fp@2267: /** Device names. fp@2267: */ fp@2453: const char *ec_device_names[2] = { fp@2267: "main", fp@2267: "backup" fp@2267: }; fp@2267: fp@806: /** Offers an EtherCAT device to a certain master. fp@806: * fp@806: * The master decides, if it wants to use the device for EtherCAT operation, fp@806: * or not. It is important, that the offered net_device is not used by the fp@806: * kernel IP stack. If the master, accepted the offer, the address of the fp@1011: * newly created EtherCAT device is returned, else \a NULL is returned. fp@1011: * fp@1011: * \return Pointer to device, if accepted, or NULL if declined. fp@806: * \ingroup DeviceInterface fp@806: */ fp@1011: ec_device_t *ecdev_offer( fp@1011: struct net_device *net_dev, /**< net_device to offer */ fp@573: ec_pollfunc_t poll, /**< device poll function */ fp@1011: struct module *module /**< pointer to the module */ fp@573: ) fp@91: { fp@73: ec_master_t *master; fp@2267: char str[EC_MAX_MAC_STRING_SIZE]; fp@2453: unsigned int i, dev_idx; fp@639: fp@639: for (i = 0; i < master_count; i++) { fp@639: master = &masters[i]; fp@2156: ec_mac_print(net_dev->dev_addr, str); fp@700: fp@2453: if (down_interruptible(&master->device_sem)) { fp@2453: EC_MASTER_WARN(master, "%s() interrupted!\n", __func__); fp@2453: return NULL; fp@2453: } fp@2453: fp@2453: for (dev_idx = EC_DEVICE_MAIN; fp@2453: dev_idx < ec_master_num_devices(master); dev_idx++) { fp@2453: if (!master->devices[dev_idx].dev fp@2453: && (ec_mac_equal(master->macs[dev_idx], net_dev->dev_addr) fp@2453: || ec_mac_is_broadcast(master->macs[dev_idx]))) { fp@2267: fp@2267: EC_INFO("Accepting %s as %s device for master %u.\n", fp@2453: str, ec_device_names[dev_idx != 0], master->index); fp@2453: fp@2453: ec_device_attach(&master->devices[dev_idx], fp@2453: net_dev, poll, module); fp@2267: up(&master->device_sem); fp@2267: fp@2267: snprintf(net_dev->name, IFNAMSIZ, "ec%c%u", fp@2453: ec_device_names[dev_idx != 0][0], master->index); fp@2453: fp@2453: return &master->devices[dev_idx]; // offer accepted fp@2267: } fp@2267: } fp@2267: fp@2267: up(&master->device_sem); fp@2267: fp@2267: EC_MASTER_DBG(master, 1, "Master declined device %s.\n", str); fp@573: } fp@573: fp@1011: return NULL; // offer declined fp@497: } fp@497: fp@54: /****************************************************************************** fp@2129: * Application interface fp@54: *****************************************************************************/ fp@33: fp@1312: /** Request a master. fp@1312: * fp@1312: * Same as ecrt_request_master(), but with ERR_PTR() return value. fp@2522: * fp@2522: * \return Requested master. fp@1312: */ fp@1332: ec_master_t *ecrt_request_master_err( fp@1332: unsigned int master_index /**< Master index. */ fp@1332: ) fp@54: { fp@1239: ec_master_t *master, *errptr = NULL; fp@2453: unsigned int dev_idx = EC_DEVICE_MAIN; fp@73: fp@647: EC_INFO("Requesting master %u...\n", master_index); fp@178: fp@639: if (master_index >= master_count) { fp@639: EC_ERR("Invalid master index %u.\n", master_index); fp@1239: errptr = ERR_PTR(-EINVAL); fp@639: goto out_return; fp@639: } fp@639: master = &masters[master_index]; fp@178: fp@1239: if (down_interruptible(&master_sem)) { fp@1239: errptr = ERR_PTR(-EINTR); fp@1189: goto out_return; fp@1239: } fp@1189: fp@647: if (master->reserved) { fp@647: up(&master_sem); fp@1921: EC_MASTER_ERR(master, "Master already in use!\n"); fp@1239: errptr = ERR_PTR(-EBUSY); fp@191: goto out_return; fp@73: } fp@647: master->reserved = 1; fp@647: up(&master_sem); fp@73: fp@1239: if (down_interruptible(&master->device_sem)) { fp@1239: errptr = ERR_PTR(-EINTR); fp@1189: goto out_release; fp@1239: } fp@2382: fp@1029: if (master->phase != EC_IDLE) { fp@381: up(&master->device_sem); fp@1921: EC_MASTER_ERR(master, "Master still waiting for devices!\n"); fp@1239: errptr = ERR_PTR(-ENODEV); fp@191: goto out_release; fp@73: } fp@73: fp@2453: for (; dev_idx < ec_master_num_devices(master); dev_idx++) { fp@2453: ec_device_t *device = &master->devices[dev_idx]; fp@2453: if (!try_module_get(device->module)) { fp@2453: up(&master->device_sem); fp@2453: EC_MASTER_ERR(master, "Device module is unloading!\n"); fp@2453: errptr = ERR_PTR(-ENODEV); fp@2453: goto out_module_put; fp@2453: } fp@191: } fp@191: fp@381: up(&master->device_sem); fp@381: fp@1029: if (ec_master_enter_operation_phase(master)) { fp@1921: EC_MASTER_ERR(master, "Failed to enter OPERATION phase!\n"); fp@1239: errptr = ERR_PTR(-EIO); fp@446: goto out_module_put; fp@377: } fp@377: fp@647: EC_INFO("Successfully requested master %u.\n", master_index); fp@73: return master; fp@54: fp@191: out_module_put: fp@2453: for (; dev_idx > 0; dev_idx--) { fp@2453: ec_device_t *device = &master->devices[dev_idx - 1]; fp@2453: module_put(device->module); fp@2267: } fp@191: out_release: fp@647: master->reserved = 0; fp@191: out_return: fp@1239: return errptr; fp@27: } fp@27: fp@39: /*****************************************************************************/ fp@33: fp@1312: ec_master_t *ecrt_request_master(unsigned int master_index) fp@1312: { fp@1312: ec_master_t *master = ecrt_request_master_err(master_index); fp@1312: return IS_ERR(master) ? NULL : master; fp@1312: } fp@1312: fp@1312: /*****************************************************************************/ fp@1312: fp@792: void ecrt_release_master(ec_master_t *master) fp@33: { fp@2453: unsigned int dev_idx; fp@2267: fp@1921: EC_MASTER_INFO(master, "Releasing master...\n"); fp@531: fp@997: if (!master->reserved) { fp@1921: EC_MASTER_WARN(master, "%s(): Master was was not requested!\n", fp@1921: __func__); fp@531: return; fp@531: } fp@531: fp@1029: ec_master_leave_operation_phase(master); fp@151: fp@2453: for (dev_idx = EC_DEVICE_MAIN; dev_idx < ec_master_num_devices(master); fp@2453: dev_idx++) { fp@2453: module_put(master->devices[dev_idx].module); fp@2267: } fp@2267: fp@647: master->reserved = 0; fp@647: fp@1921: EC_MASTER_INFO(master, "Released.\n"); fp@33: } fp@33: fp@39: /*****************************************************************************/ fp@54: fp@792: unsigned int ecrt_version_magic(void) fp@792: { fp@792: return ECRT_VERSION_MAGIC; fp@792: } fp@792: fp@792: /*****************************************************************************/ fp@792: fp@1209: /** Global request state type translation table. fp@1209: * fp@1209: * Translates an internal request state to an external one. fp@1209: */ fp@1209: const ec_request_state_t ec_request_state_translation_table[] = { fp@1209: EC_REQUEST_UNUSED, // EC_INT_REQUEST_INIT, fp@1209: EC_REQUEST_BUSY, // EC_INT_REQUEST_QUEUED, fp@1209: EC_REQUEST_BUSY, // EC_INT_REQUEST_BUSY, fp@1209: EC_REQUEST_SUCCESS, // EC_INT_REQUEST_SUCCESS, fp@1209: EC_REQUEST_ERROR // EC_INT_REQUEST_FAILURE fp@1209: }; fp@1209: fp@1209: /*****************************************************************************/ fp@1209: fp@199: /** \cond */ fp@199: fp@54: module_init(ec_init_module); fp@54: module_exit(ec_cleanup_module); fp@54: fp@573: EXPORT_SYMBOL(ecdev_offer); fp@997: fp@104: EXPORT_SYMBOL(ecrt_request_master); fp@104: EXPORT_SYMBOL(ecrt_release_master); fp@545: EXPORT_SYMBOL(ecrt_version_magic); fp@54: fp@199: /** \endcond */ fp@199: fp@199: /*****************************************************************************/