fp@42: /****************************************************************************** fp@42: * fp@42: * $Id$ fp@42: * fp@1618: * Copyright (C) 2006 Florian Pose, Ingenieurgemeinschaft IgH fp@1618: * fp@1618: * This file is part of the IgH EtherCAT Master. fp@1618: * fp@1618: * The IgH EtherCAT Master is free software; you can redistribute it fp@1618: * and/or modify it under the terms of the GNU General Public License fp@1619: * as published by the Free Software Foundation; either version 2 of the fp@1619: * License, or (at your option) any later version. fp@1618: * fp@1618: * The IgH EtherCAT Master is distributed in the hope that it will be fp@1618: * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of fp@1618: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the fp@1618: * GNU General Public License for more details. fp@1618: * fp@1618: * You should have received a copy of the GNU General Public License fp@1618: * along with the IgH EtherCAT Master; if not, write to the Free Software fp@1618: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA fp@1618: * fp@1619: * The right to use EtherCAT Technology is granted and comes free of fp@1619: * charge under condition of compatibility of product made by fp@1619: * Licensee. People intending to distribute/sell products based on the fp@1619: * code, have to sign an agreement to guarantee that products using fp@1619: * software based on IgH EtherCAT master stay compatible with the actual fp@1619: * EtherCAT specification (which are released themselves as an open fp@1619: * standard) as the (only) precondition to have the right to use EtherCAT fp@1619: * Technology, IP and trade marks. fp@1619: * fp@42: *****************************************************************************/ fp@42: fp@1618: /** fp@1618: \file fp@1618: EtherCAT domain methods. fp@1618: */ fp@1618: fp@1618: /*****************************************************************************/ fp@1618: fp@1624: #include fp@1624: fp@54: #include "globals.h" fp@54: #include "domain.h" fp@73: #include "master.h" fp@42: fp@178: /*****************************************************************************/ fp@178: fp@98: void ec_domain_clear_field_regs(ec_domain_t *); fp@179: ssize_t ec_show_domain_attribute(struct kobject *, struct attribute *, char *); fp@98: fp@42: /*****************************************************************************/ fp@42: fp@1618: /** \cond */ fp@1618: fp@1624: EC_SYSFS_READ_ATTR(image_size); fp@178: fp@178: static struct attribute *def_attrs[] = { fp@1624: &attr_image_size, fp@178: NULL, fp@178: }; fp@178: fp@178: static struct sysfs_ops sysfs_ops = { fp@178: .show = &ec_show_domain_attribute, fp@178: .store = NULL fp@178: }; fp@178: fp@178: static struct kobj_type ktype_ec_domain = { fp@178: .release = ec_domain_clear, fp@178: .sysfs_ops = &sysfs_ops, fp@178: .default_attrs = def_attrs fp@178: }; fp@178: fp@1618: /** \endcond */ fp@1618: fp@178: /*****************************************************************************/ fp@178: fp@42: /** fp@195: Domain constructor. fp@195: \return 0 in case of success, else < 0 fp@195: */ fp@195: fp@195: int ec_domain_init(ec_domain_t *domain, /**< EtherCAT domain */ fp@195: ec_master_t *master, /**< owning master */ fp@195: unsigned int index /**< domain index */ fp@178: ) fp@73: { fp@73: domain->master = master; fp@178: domain->index = index; fp@73: domain->data_size = 0; fp@73: domain->base_address = 0; fp@73: domain->response_count = 0xFFFFFFFF; fp@73: fp@73: INIT_LIST_HEAD(&domain->field_regs); fp@1624: INIT_LIST_HEAD(&domain->datagrams); fp@178: fp@195: // init kobject and add it to the hierarchy fp@178: memset(&domain->kobj, 0x00, sizeof(struct kobject)); fp@178: kobject_init(&domain->kobj); fp@178: domain->kobj.ktype = &ktype_ec_domain; fp@178: domain->kobj.parent = &master->kobj; fp@178: if (kobject_set_name(&domain->kobj, "domain%i", index)) { fp@178: EC_ERR("Failed to set kobj name.\n"); fp@178: return -1; fp@178: } fp@178: fp@178: return 0; fp@73: } fp@73: fp@73: /*****************************************************************************/ fp@73: fp@73: /** fp@195: Domain destructor. fp@195: */ fp@195: fp@195: void ec_domain_clear(struct kobject *kobj /**< kobject of the domain */) fp@73: { fp@1624: ec_datagram_t *datagram, *next; fp@178: ec_domain_t *domain; fp@178: fp@178: domain = container_of(kobj, ec_domain_t, kobj); fp@178: fp@178: EC_INFO("Clearing domain %i.\n", domain->index); fp@144: fp@1624: list_for_each_entry_safe(datagram, next, &domain->datagrams, list) { fp@1624: ec_datagram_clear(datagram); fp@1624: kfree(datagram); fp@144: } fp@98: fp@98: ec_domain_clear_field_regs(domain); fp@178: fp@178: kfree(domain); fp@73: } fp@73: fp@73: /*****************************************************************************/ fp@73: fp@73: /** fp@195: Registeres a data field in a domain. fp@195: \return 0 in case of success, else < 0 fp@195: */ fp@195: fp@195: int ec_domain_reg_field(ec_domain_t *domain, /**< EtherCAT domain */ fp@195: ec_slave_t *slave, /**< slave */ fp@195: const ec_sync_t *sync, /**< sync manager */ fp@195: uint32_t field_offset, /**< data field offset */ fp@195: void **data_ptr /**< pointer to the process data fp@195: pointer */ fp@73: ) fp@73: { fp@73: ec_field_reg_t *field_reg; fp@73: fp@144: if (!(field_reg = fp@144: (ec_field_reg_t *) kmalloc(sizeof(ec_field_reg_t), GFP_KERNEL))) { fp@84: EC_ERR("Failed to allocate field registration.\n"); fp@73: return -1; fp@73: } fp@73: fp@160: if (ec_slave_prepare_fmmu(slave, domain, sync)) { fp@84: EC_ERR("FMMU configuration failed.\n"); fp@73: kfree(field_reg); fp@73: return -1; fp@73: } fp@73: fp@73: field_reg->slave = slave; fp@73: field_reg->sync = sync; fp@73: field_reg->field_offset = field_offset; fp@73: field_reg->data_ptr = data_ptr; fp@73: fp@73: list_add_tail(&field_reg->list, &domain->field_regs); fp@73: return 0; fp@73: } fp@73: fp@73: /*****************************************************************************/ fp@73: fp@73: /** fp@195: Clears the list of the registered data fields. fp@195: */ fp@195: fp@195: void ec_domain_clear_field_regs(ec_domain_t *domain /**< EtherCAT domain */) fp@98: { fp@98: ec_field_reg_t *field_reg, *next; fp@98: fp@98: list_for_each_entry_safe(field_reg, next, &domain->field_regs, list) { fp@98: list_del(&field_reg->list); fp@98: kfree(field_reg); fp@98: } fp@98: } fp@98: fp@98: /*****************************************************************************/ fp@98: fp@98: /** fp@1624: Allocates a process data datagram and appends it to the list. fp@195: \return 0 in case of success, else < 0 fp@195: */ fp@195: fp@1624: int ec_domain_add_datagram(ec_domain_t *domain, /**< EtherCAT domain */ fp@1624: uint32_t offset, /**< logical offset */ fp@1624: size_t data_size /**< size of the datagram data */ fp@1624: ) fp@1624: { fp@1624: ec_datagram_t *datagram; fp@1624: fp@1624: if (!(datagram = kmalloc(sizeof(ec_datagram_t), GFP_KERNEL))) { fp@1624: EC_ERR("Failed to allocate domain datagram!\n"); fp@144: return -1; fp@144: } fp@144: fp@1624: ec_datagram_init(datagram); fp@1624: fp@1624: if (ec_datagram_lrw(datagram, offset, data_size)) { fp@1624: kfree(datagram); fp@144: return -1; fp@144: } fp@144: fp@1624: list_add_tail(&datagram->list, &domain->datagrams); fp@144: return 0; fp@144: } fp@144: fp@144: /*****************************************************************************/ fp@144: fp@144: /** fp@195: Creates a domain. fp@195: Reserves domain memory, calculates the logical addresses of the fp@195: corresponding FMMUs and sets the process data pointer of the registered fp@195: data fields. fp@195: \return 0 in case of success, else < 0 fp@195: */ fp@195: fp@195: int ec_domain_alloc(ec_domain_t *domain, /**< EtherCAT domain */ fp@1621: uint32_t base_address /**< logical base address */ fp@73: ) fp@73: { fp@98: ec_field_reg_t *field_reg; fp@73: ec_slave_t *slave; fp@73: ec_fmmu_t *fmmu; fp@144: unsigned int i, j, cmd_count; fp@144: uint32_t field_off, field_off_cmd; fp@144: uint32_t cmd_offset; fp@1621: size_t cmd_data_size, sync_size; fp@1624: ec_datagram_t *datagram; fp@73: fp@73: domain->base_address = base_address; fp@73: fp@1621: // calculate size of process data and allocate memory fp@73: domain->data_size = 0; fp@144: cmd_offset = base_address; fp@144: cmd_data_size = 0; fp@144: cmd_count = 0; fp@182: list_for_each_entry(slave, &domain->master->slaves, list) { fp@73: for (j = 0; j < slave->fmmu_count; j++) { fp@73: fmmu = &slave->fmmus[j]; fp@73: if (fmmu->domain == domain) { fp@73: fmmu->logical_start_address = base_address + domain->data_size; fp@1621: sync_size = ec_slave_calc_sync_size(slave, fmmu->sync); fp@1621: domain->data_size += sync_size; fp@1621: if (cmd_data_size + sync_size > EC_MAX_DATA_SIZE) { fp@1624: if (ec_domain_add_datagram(domain, cmd_offset, fp@1624: cmd_data_size)) return -1; fp@144: cmd_offset += cmd_data_size; fp@144: cmd_data_size = 0; fp@144: cmd_count++; fp@144: } fp@1621: cmd_data_size += sync_size; fp@73: } fp@73: } fp@73: } fp@73: fp@1624: // allocate last datagram fp@144: if (cmd_data_size) { fp@1624: if (ec_domain_add_datagram(domain, cmd_offset, cmd_data_size)) fp@144: return -1; fp@144: cmd_count++; fp@144: } fp@144: fp@144: if (!cmd_count) { fp@178: EC_WARN("Domain %i contains no data!\n", domain->index); fp@98: ec_domain_clear_field_regs(domain); fp@98: return 0; fp@98: } fp@98: fp@1621: // set all process data pointers fp@98: list_for_each_entry(field_reg, &domain->field_regs, list) { fp@98: for (i = 0; i < field_reg->slave->fmmu_count; i++) { fp@98: fmmu = &field_reg->slave->fmmus[i]; fp@98: if (fmmu->domain == domain && fmmu->sync == field_reg->sync) { fp@144: field_off = fmmu->logical_start_address + fp@144: field_reg->field_offset; fp@1624: // search datagram fp@1624: list_for_each_entry(datagram, &domain->datagrams, list) { fp@1624: field_off_cmd = field_off - datagram->address.logical; fp@1624: if (field_off >= datagram->address.logical && fp@1624: field_off_cmd < datagram->mem_size) { fp@1624: *field_reg->data_ptr = datagram->data + field_off_cmd; fp@144: } fp@144: } fp@144: if (!field_reg->data_ptr) { fp@144: EC_ERR("Failed to assign data pointer!\n"); fp@144: return -1; fp@144: } fp@98: break; fp@98: } fp@98: } fp@98: } fp@98: fp@1624: EC_INFO("Domain %i - Allocated %i bytes in %i datagram%s\n", fp@178: domain->index, domain->data_size, cmd_count, fp@178: cmd_count == 1 ? "" : "s"); fp@98: fp@98: ec_domain_clear_field_regs(domain); fp@73: fp@73: return 0; fp@73: } fp@73: fp@97: /*****************************************************************************/ fp@97: fp@97: /** fp@195: Sets the number of responding slaves and outputs it on demand. fp@195: This number isn't really the number of responding slaves, but the sum of fp@1624: the working counters of all domain datagrams. Some slaves increase the fp@195: working counter by 2, some by 1. fp@195: */ fp@195: fp@195: void ec_domain_response_count(ec_domain_t *domain, /**< EtherCAT domain */ fp@195: unsigned int count /**< new WC sum */ fp@97: ) fp@97: { fp@97: if (count != domain->response_count) { fp@97: domain->response_count = count; fp@195: EC_INFO("Domain %i working counter change: %i\n", domain->index, fp@195: count); fp@195: } fp@195: } fp@195: fp@195: /*****************************************************************************/ fp@195: fp@195: /** fp@195: Formats attribute data for SysFS reading. fp@195: \return number of bytes to read fp@195: */ fp@195: fp@195: ssize_t ec_show_domain_attribute(struct kobject *kobj, /**< kobject */ fp@195: struct attribute *attr, /**< attribute */ fp@195: char *buffer /**< memory to store data in */ fp@178: ) fp@178: { fp@178: ec_domain_t *domain = container_of(kobj, ec_domain_t, kobj); fp@178: fp@1624: if (attr == &attr_image_size) { fp@178: return sprintf(buffer, "%i\n", domain->data_size); fp@178: } fp@178: fp@178: return 0; fp@97: } fp@97: fp@73: /****************************************************************************** fp@195: * Realtime interface fp@73: *****************************************************************************/ fp@73: fp@73: /** fp@195: Registers a data field in a domain. fp@195: - If \a data_ptr is NULL, the slave is only checked against its type. fp@195: - If \a field_count is 0, it is assumed that one data field is to be fp@195: registered. fp@195: - If \a field_count is greater then 1, it is assumed that \a data_ptr fp@195: is an array of the respective size. fp@195: \return pointer to the slave on success, else NULL fp@1618: \ingroup RealtimeInterface fp@73: */ fp@73: fp@104: ec_slave_t *ecrt_domain_register_field(ec_domain_t *domain, fp@195: /**< EtherCAT domain */ fp@104: const char *address, fp@195: /**< ASCII address of the slave, fp@195: see ecrt_master_get_slave() */ fp@104: const char *vendor_name, fp@195: /**< vendor name */ fp@104: const char *product_name, fp@195: /**< product name */ fp@104: void **data_ptr, fp@195: /**< address of the process data fp@195: pointer */ fp@104: const char *field_name, fp@195: /**< data field name */ fp@104: unsigned int field_index, fp@195: /**< offset of data fields with fp@195: \a field_type */ fp@104: unsigned int field_count fp@195: /**< number of data fields (with fp@195: the same type) to register */ fp@104: ) fp@73: { fp@73: ec_slave_t *slave; fp@73: const ec_slave_type_t *type; fp@73: ec_master_t *master; fp@73: const ec_sync_t *sync; fp@73: const ec_field_t *field; fp@108: unsigned int field_counter, i, j, orig_field_index, orig_field_count; fp@73: uint32_t field_offset; fp@73: fp@73: master = domain->master; fp@73: fp@195: // translate address fp@138: if (!(slave = ecrt_master_get_slave(master, address))) return NULL; fp@73: fp@73: if (!(type = slave->type)) { fp@84: EC_ERR("Slave \"%s\" (position %i) has unknown type!\n", address, fp@84: slave->ring_position); fp@73: return NULL; fp@73: } fp@73: fp@73: if (strcmp(vendor_name, type->vendor_name) || fp@73: strcmp(product_name, type->product_name)) { fp@84: EC_ERR("Invalid slave type at position %i - Requested: \"%s %s\"," fp@84: " found: \"%s %s\".\n", slave->ring_position, vendor_name, fp@84: product_name, type->vendor_name, type->product_name); fp@73: return NULL; fp@73: } fp@73: fp@112: if (!data_ptr) { fp@195: // data_ptr is NULL => mark slave as "registered" (do not warn) fp@112: slave->registered = 1; fp@112: } fp@112: fp@112: if (!field_count) field_count = 1; fp@108: orig_field_index = field_index; fp@108: orig_field_count = field_count; fp@108: fp@108: field_counter = 0; fp@76: for (i = 0; type->sync_managers[i]; i++) { fp@73: sync = type->sync_managers[i]; fp@73: field_offset = 0; fp@73: for (j = 0; sync->fields[j]; j++) { fp@73: field = sync->fields[j]; fp@104: if (!strcmp(field->name, field_name)) { fp@108: if (field_counter++ == field_index) { fp@112: if (data_ptr) fp@112: ec_domain_reg_field(domain, slave, sync, field_offset, fp@112: data_ptr++); fp@73: if (!(--field_count)) return slave; fp@108: field_index++; fp@73: } fp@73: } fp@73: field_offset += field->size; fp@73: } fp@73: } fp@73: fp@104: EC_ERR("Slave %i (\"%s %s\") registration mismatch: Field \"%s\"," fp@104: " index %i, count %i.\n", slave->ring_position, vendor_name, fp@108: product_name, field_name, orig_field_index, orig_field_count); fp@73: return NULL; fp@73: } fp@73: fp@73: /*****************************************************************************/ fp@73: fp@73: /** fp@195: Registeres a bunch of data fields. fp@195: Caution! The list has to be terminated with a NULL structure ({})! fp@195: \return 0 in case of success, else < 0 fp@1618: \ingroup RealtimeInterface fp@73: */ fp@73: fp@104: int ecrt_domain_register_field_list(ec_domain_t *domain, fp@195: /**< EtherCAT domain */ fp@150: const ec_field_init_t *fields fp@195: /**< array of data field registrations */ fp@104: ) fp@98: { fp@150: const ec_field_init_t *field; fp@98: fp@112: for (field = fields; field->slave_address; field++) fp@104: if (!ecrt_domain_register_field(domain, field->slave_address, fp@104: field->vendor_name, fp@104: field->product_name, field->data_ptr, fp@104: field->field_name, field->field_index, fp@104: field->field_count)) fp@98: return -1; fp@98: fp@98: return 0; fp@98: } fp@98: fp@98: /*****************************************************************************/ fp@98: fp@98: /** fp@1624: Places all process data datagrams in the masters datagram queue. fp@1618: \ingroup RealtimeInterface fp@195: */ fp@195: fp@195: void ecrt_domain_queue(ec_domain_t *domain /**< EtherCAT domain */) fp@98: { fp@1624: ec_datagram_t *datagram; fp@1624: fp@1624: list_for_each_entry(datagram, &domain->datagrams, list) { fp@1624: ec_master_queue_datagram(domain->master, datagram); fp@73: } fp@98: } fp@98: fp@98: /*****************************************************************************/ fp@98: fp@98: /** fp@195: Processes received process data. fp@1618: \ingroup RealtimeInterface fp@195: */ fp@195: fp@195: void ecrt_domain_process(ec_domain_t *domain /**< EtherCAT domain */) fp@98: { fp@144: unsigned int working_counter_sum; fp@1624: ec_datagram_t *datagram; fp@98: fp@98: working_counter_sum = 0; fp@98: fp@1624: list_for_each_entry(datagram, &domain->datagrams, list) { fp@1624: if (datagram->state == EC_CMD_RECEIVED) { fp@1624: working_counter_sum += datagram->working_counter; fp@98: } fp@98: } fp@73: fp@97: ec_domain_response_count(domain, working_counter_sum); fp@73: } fp@73: fp@73: /*****************************************************************************/ fp@73: fp@105: /** fp@195: Returns the state of a domain. fp@1624: \return 0 if all datagrams were received, else -1. fp@1618: \ingroup RealtimeInterface fp@195: */ fp@195: fp@195: int ecrt_domain_state(ec_domain_t *domain /**< EtherCAT domain */) fp@105: { fp@1624: ec_datagram_t *datagram; fp@1624: fp@1624: list_for_each_entry(datagram, &domain->datagrams, list) { fp@1624: if (datagram->state != EC_CMD_RECEIVED) return -1; fp@105: } fp@105: fp@105: return 0; fp@105: } fp@105: fp@105: /*****************************************************************************/ fp@105: fp@1618: /** \cond */ fp@1618: fp@104: EXPORT_SYMBOL(ecrt_domain_register_field); fp@104: EXPORT_SYMBOL(ecrt_domain_register_field_list); fp@104: EXPORT_SYMBOL(ecrt_domain_queue); fp@104: EXPORT_SYMBOL(ecrt_domain_process); fp@105: EXPORT_SYMBOL(ecrt_domain_state); fp@42: fp@1618: /** \endcond */ fp@1618: fp@1618: /*****************************************************************************/