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@571: #include "xmldev.h" fp@54: fp@54: /*****************************************************************************/ fp@54: fp@54: int __init ec_init_module(void); fp@54: void __exit ec_cleanup_module(void); fp@52: fp@576: /*****************************************************************************/ fp@576: fp@576: struct kobject ec_kobj; /**< kobject for master module */ fp@39: fp@573: static char *main; /**< main devices parameter */ fp@573: static char *backup; /**< backup devices parameter */ fp@573: fp@575: static LIST_HEAD(main_ids); /**< list of main device IDs */ fp@575: static LIST_HEAD(backup_ids); /**< list of main device IDs */ fp@573: static LIST_HEAD(masters); /**< list of masters */ fp@571: static dev_t device_number; /**< XML character device number */ fp@571: ec_xmldev_t xmldev; /**< XML character device */ fp@27: fp@444: char *ec_master_version_str = EC_MASTER_VERSION; 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@573: module_param(main, charp, S_IRUGO); fp@573: MODULE_PARM_DESC(main, "main device IDs"); fp@573: module_param(backup, charp, S_IRUGO); fp@573: MODULE_PARM_DESC(backup, "backup device IDs"); fp@195: fp@199: /** \endcond */ fp@199: fp@195: /*****************************************************************************/ fp@195: fp@195: /** fp@195: Module initialization. fp@195: Initializes \a ec_master_count masters. fp@195: \return 0 on success, else < 0 fp@54: */ fp@54: fp@54: int __init ec_init_module(void) fp@27: { fp@178: ec_master_t *master, *next; fp@573: ec_device_id_t *main_dev_id, *backup_dev_id; fp@573: unsigned int master_index = 0; fp@73: fp@382: EC_INFO("Master driver %s\n", EC_MASTER_VERSION); fp@73: fp@576: // init kobject and add it to the hierarchy fp@576: memset(&ec_kobj, 0x00, sizeof(struct kobject)); fp@578: kobject_init(&ec_kobj); // no ktype fp@576: fp@576: if (kobject_set_name(&ec_kobj, "ethercat")) { fp@576: EC_ERR("Failed to set module kobject name.\n"); fp@576: goto out_put; fp@576: } fp@576: fp@576: if (kobject_add(&ec_kobj)) { fp@576: EC_ERR("Failed to add module kobject.\n"); fp@576: goto out_put; fp@576: } fp@576: fp@573: if (alloc_chrdev_region(&device_number, 0, 1, "EtherCAT")) { fp@573: EC_ERR("Failed to obtain device number!\n"); fp@576: goto out_del; fp@361: } fp@361: fp@575: if (ec_device_id_process_params(main, backup, &main_ids, &backup_ids)) fp@575: goto out_cdev; fp@573: fp@575: // create as many masters as main device IDs present fp@575: if (!list_empty(&main_ids)) { fp@575: // main_ids and backup_ids are of equal size at this point fp@573: main_dev_id = fp@575: list_entry(main_ids.next, ec_device_id_t, list); fp@573: backup_dev_id = fp@575: list_entry(backup_ids.next, ec_device_id_t, list); fp@573: fp@573: while (1) { fp@573: if (!(master = (ec_master_t *) fp@573: kmalloc(sizeof(ec_master_t), GFP_KERNEL))) { fp@573: EC_ERR("Failed to allocate memory for EtherCAT master %i.\n", fp@573: master_index); fp@573: goto out_free_masters; fp@573: } fp@573: fp@576: if (ec_master_init(master, &ec_kobj, master_index, fp@573: main_dev_id, backup_dev_id, 0)) fp@573: goto out_free_masters; fp@573: fp@573: list_add_tail(&master->list, &masters); fp@573: master_index++; fp@573: fp@573: // last device IDs? fp@575: if (main_dev_id->list.next == &main_ids) fp@573: break; fp@573: fp@573: // next device IDs fp@573: main_dev_id = fp@573: list_entry(main_dev_id->list.next, ec_device_id_t, list); fp@573: backup_dev_id = fp@573: list_entry(backup_dev_id->list.next, ec_device_id_t, list); fp@573: } fp@573: } fp@573: fp@573: EC_INFO("%u master%s waiting for devices.\n", fp@573: master_index, (master_index == 1 ? "" : "s")); fp@73: return 0; fp@127: fp@573: out_free_masters: fp@573: list_for_each_entry_safe(master, next, &masters, list) { fp@178: list_del(&master->list); fp@575: ec_master_destroy(master); fp@575: } fp@575: ec_device_id_clear_list(&main_ids); fp@575: ec_device_id_clear_list(&backup_ids); fp@575: out_cdev: fp@573: unregister_chrdev_region(device_number, 1); fp@576: out_del: fp@576: kobject_del(&ec_kobj); fp@576: out_put: fp@576: kobject_put(&ec_kobj); fp@127: return -1; 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@178: ec_master_t *master, *next; fp@73: fp@575: EC_INFO("Cleaning up master module...\n"); fp@73: fp@573: list_for_each_entry_safe(master, next, &masters, list) { fp@178: list_del(&master->list); fp@448: ec_master_destroy(master); fp@178: } fp@73: fp@575: ec_device_id_clear_list(&main_ids); fp@575: ec_device_id_clear_list(&backup_ids); fp@573: unregister_chrdev_region(device_number, 1); fp@576: kobject_del(&ec_kobj); fp@576: kobject_put(&ec_kobj); fp@361: fp@575: EC_INFO("Master module cleaned up.\n"); fp@27: } fp@27: fp@178: /*****************************************************************************/ fp@178: fp@178: /** fp@178: Gets a handle to a certain master. fp@178: \returns pointer to master fp@178: */ fp@178: fp@195: ec_master_t *ec_find_master(unsigned int master_index /**< master index */) fp@178: { fp@178: ec_master_t *master; fp@178: fp@573: list_for_each_entry(master, &masters, list) { fp@178: if (master->index == master_index) return master; fp@178: } fp@178: fp@178: EC_ERR("Master %i does not exist!\n", master_index); fp@178: return NULL; 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@325: size_t ec_state_string(uint8_t states, /**< slave states */ fp@403: char *buffer /**< target buffer fp@403: (min. EC_STATE_STRING_SIZE bytes) */ 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@251: if (states & EC_SLAVE_STATE_INIT) { fp@325: off += sprintf(buffer + off, "INIT"); fp@251: first = 0; fp@251: } fp@251: if (states & EC_SLAVE_STATE_PREOP) { fp@325: if (!first) off += sprintf(buffer + off, ", "); fp@325: off += sprintf(buffer + off, "PREOP"); fp@251: first = 0; fp@251: } fp@251: if (states & EC_SLAVE_STATE_SAVEOP) { fp@325: if (!first) off += sprintf(buffer + off, ", "); fp@325: off += sprintf(buffer + off, "SAVEOP"); fp@251: first = 0; fp@251: } fp@251: if (states & EC_SLAVE_STATE_OP) { fp@325: if (!first) off += sprintf(buffer + off, ", "); fp@325: off += sprintf(buffer + off, "OP"); fp@325: } 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@33: /** fp@573: Offers an EtherCAT device to a certain master. fp@573: The master decides, if it wants to use the device for EtherCAT operation, fp@573: or not. It is important, that the offered net_device is not used by fp@573: the kernel IP stack. If the master, accepted the offer, the address of fp@573: the newly created EtherCAT device is written to the ecdev pointer, else fp@573: the pointer is written to zero. fp@195: \return 0 on success, else < 0 fp@199: \ingroup DeviceInterface fp@195: */ fp@195: fp@573: int ecdev_offer(struct net_device *net_dev, /**< net_device to offer */ fp@573: ec_device_t **ecdev, /**< pointer to store a device on success */ fp@573: const char *driver_name, /**< name of the network driver */ fp@573: unsigned int device_index, /**< index of the supported device */ fp@573: ec_pollfunc_t poll, /**< device poll function */ fp@573: struct module *module /**< pointer to the module */ fp@573: ) fp@91: { fp@73: ec_master_t *master; fp@578: char str[50]; // FIXME fp@573: fp@573: list_for_each_entry(master, &masters, list) { fp@575: if (ec_device_id_check(master->main_device_id, net_dev, fp@573: driver_name, device_index)) { fp@578: ec_device_id_print(master->main_device_id, str); fp@578: EC_INFO("Accepting device %s for master %u.\n", fp@578: str, master->index); fp@573: fp@579: if (down_interruptible(&master->device_sem)) { fp@579: EC_ERR("Interrupted while waiting for device semaphore!\n"); fp@579: return -1; fp@579: } fp@579: fp@579: if (master->main_device.dev) { fp@579: EC_ERR("Master %u already has a device attached.\n", fp@579: master->index); fp@579: up(&master->device_sem); fp@579: return -1; fp@573: } fp@573: fp@579: ec_device_attach(&master->main_device, net_dev, poll, module); fp@573: up(&master->device_sem); fp@579: fp@573: sprintf(net_dev->name, "ec%u", master->index); fp@579: *ecdev = &master->main_device; // offer accepted fp@573: return 0; // no error fp@573: } fp@573: } fp@573: fp@573: *ecdev = NULL; // offer declined fp@573: return 0; // no error fp@573: } fp@573: fp@573: /*****************************************************************************/ fp@573: fp@573: /** fp@573: 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@573: void ecdev_withdraw(ec_device_t *device /**< EtherCAT device */) fp@573: { fp@573: ec_master_t *master = device->master; fp@578: char str[50]; // FIXME fp@578: fp@578: ec_device_id_print(master->main_device_id, str); fp@578: fp@578: EC_INFO("Master %u releasing main device %s.\n", master->index, str); fp@578: fp@381: down(&master->device_sem); fp@579: ec_device_detach(device); fp@381: up(&master->device_sem); fp@54: } fp@54: fp@497: /*****************************************************************************/ fp@497: fp@497: /** fp@497: Opens the network device and makes the master enter IDLE mode. fp@497: \return 0 on success, else < 0 fp@497: \ingroup DeviceInterface fp@497: */ fp@497: fp@497: int ecdev_open(ec_device_t *device /**< EtherCAT device */) fp@497: { fp@497: if (ec_device_open(device)) { fp@497: EC_ERR("Failed to open device!\n"); fp@497: return -1; fp@497: } fp@497: fp@525: if (ec_master_enter_idle_mode(device->master)) { fp@525: EC_ERR("Failed to enter idle mode!\n"); fp@525: return -1; fp@525: } fp@525: fp@497: return 0; fp@497: } fp@497: fp@497: /*****************************************************************************/ fp@497: fp@497: /** fp@497: Makes the master leave IDLE mode and closes the network device. fp@497: \return 0 on success, else < 0 fp@497: \ingroup DeviceInterface fp@497: */ fp@497: fp@497: void ecdev_close(ec_device_t *device /**< EtherCAT device */) fp@497: { fp@497: ec_master_leave_idle_mode(device->master); fp@497: fp@497: if (ec_device_close(device)) fp@497: EC_WARN("Failed to close device!\n"); fp@497: } fp@497: fp@54: /****************************************************************************** fp@195: * Realtime interface fp@54: *****************************************************************************/ fp@33: fp@33: /** fp@541: * Returns the version magic of the realtime interface. fp@541: * \return ECRT version magic. fp@541: * \ingroup RealtimeInterface fp@541: */ fp@541: fp@541: unsigned int ecrt_version_magic(void) fp@541: { fp@541: return ECRT_VERSION_MAGIC; fp@541: } fp@541: fp@541: /*****************************************************************************/ fp@541: fp@541: /** 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@178: EC_INFO("Requesting master %i...\n", master_index); fp@178: fp@191: if (!(master = ec_find_master(master_index))) goto out_return; fp@178: fp@378: if (!atomic_dec_and_test(&master->available)) { fp@378: atomic_inc(&master->available); fp@178: EC_ERR("Master %i is already in use!\n", master_index); fp@191: goto out_return; fp@73: } fp@73: fp@381: if (down_interruptible(&master->device_sem)) { fp@381: EC_ERR("Interrupted while waiting for device!\n"); fp@381: goto out_release; fp@381: } fp@381: fp@579: if (master->mode != EC_MASTER_MODE_IDLE) { fp@381: up(&master->device_sem); fp@579: EC_ERR("Master %i still waiting for devices!\n", master_index); fp@191: goto out_release; fp@73: } fp@73: fp@579: if (!try_module_get(master->main_device.module)) { fp@381: up(&master->device_sem); fp@381: EC_ERR("Device module is unloading!\n"); fp@191: goto out_release; fp@191: } fp@191: fp@381: up(&master->device_sem); fp@381: fp@579: if (!master->main_device.link_state) { fp@377: EC_ERR("Link is DOWN.\n"); fp@377: goto out_module_put; fp@377: } fp@377: fp@446: if (ec_master_enter_operation_mode(master)) { fp@446: EC_ERR("Failed to enter OPERATION mode!\n"); fp@446: goto out_module_put; fp@377: } fp@377: fp@377: EC_INFO("Successfully requested master %i.\n", master_index); fp@73: return master; fp@54: fp@191: out_module_put: fp@579: module_put(master->main_device.module); fp@191: out_release: fp@378: atomic_inc(&master->available); fp@191: out_return: fp@73: return NULL; fp@27: } fp@27: fp@39: /*****************************************************************************/ fp@33: fp@33: /** fp@195: Releases a reserved EtherCAT master. fp@199: \ingroup RealtimeInterface fp@195: */ fp@195: fp@195: void ecrt_release_master(ec_master_t *master /**< EtherCAT master */) fp@33: { fp@531: EC_INFO("Releasing master %i...\n", master->index); fp@531: fp@531: if (master->mode != EC_MASTER_MODE_OPERATION) { fp@531: EC_WARN("Master %i was was not requested!\n", master->index); fp@531: return; fp@531: } fp@531: fp@446: ec_master_leave_operation_mode(master); fp@151: fp@579: module_put(master->main_device.module); fp@378: atomic_inc(&master->available); fp@178: fp@446: EC_INFO("Released master %i.\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@573: EXPORT_SYMBOL(ecdev_offer); fp@573: EXPORT_SYMBOL(ecdev_withdraw); fp@497: EXPORT_SYMBOL(ecdev_open); fp@497: EXPORT_SYMBOL(ecdev_close); 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: /*****************************************************************************/