domain.c

Go to the documentation of this file.
00001 /******************************************************************************
00002  *
00003  *  $Id: domain.c 533 2006-09-01 12:35:41Z fp $
00004  *
00005  *  Copyright (C) 2006  Florian Pose, Ingenieurgemeinschaft IgH
00006  *
00007  *  This file is part of the IgH EtherCAT Master.
00008  *
00009  *  The IgH EtherCAT Master is free software; you can redistribute it
00010  *  and/or modify it under the terms of the GNU General Public License
00011  *  as published by the Free Software Foundation; either version 2 of the
00012  *  License, or (at your option) any later version.
00013  *
00014  *  The IgH EtherCAT Master is distributed in the hope that it will be
00015  *  useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017  *  GNU General Public License for more details.
00018  *
00019  *  You should have received a copy of the GNU General Public License
00020  *  along with the IgH EtherCAT Master; if not, write to the Free Software
00021  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00022  *
00023  *  The right to use EtherCAT Technology is granted and comes free of
00024  *  charge under condition of compatibility of product made by
00025  *  Licensee. People intending to distribute/sell products based on the
00026  *  code, have to sign an agreement to guarantee that products using
00027  *  software based on IgH EtherCAT master stay compatible with the actual
00028  *  EtherCAT specification (which are released themselves as an open
00029  *  standard) as the (only) precondition to have the right to use EtherCAT
00030  *  Technology, IP and trade marks.
00031  *
00032  *****************************************************************************/
00033 
00039 /*****************************************************************************/
00040 
00041 #include <linux/module.h>
00042 
00043 #include "globals.h"
00044 #include "domain.h"
00045 #include "master.h"
00046 
00047 /*****************************************************************************/
00048 
00053 typedef struct
00054 {
00055     struct list_head list; 
00056     ec_slave_t *slave; 
00057     const ec_sii_sync_t *sync; 
00058     off_t sync_offset; 
00059     void **data_ptr; 
00060 }
00061 ec_data_reg_t;
00062 
00063 /*****************************************************************************/
00064 
00065 void ec_domain_clear_data_regs(ec_domain_t *);
00066 ssize_t ec_show_domain_attribute(struct kobject *, struct attribute *, char *);
00067 
00068 /*****************************************************************************/
00069 
00072 EC_SYSFS_READ_ATTR(image_size);
00073 
00074 static struct attribute *def_attrs[] = {
00075     &attr_image_size,
00076     NULL,
00077 };
00078 
00079 static struct sysfs_ops sysfs_ops = {
00080     .show = &ec_show_domain_attribute,
00081     .store = NULL
00082 };
00083 
00084 static struct kobj_type ktype_ec_domain = {
00085     .release = ec_domain_clear,
00086     .sysfs_ops = &sysfs_ops,
00087     .default_attrs = def_attrs
00088 };
00089 
00092 /*****************************************************************************/
00093 
00099 int ec_domain_init(ec_domain_t *domain, 
00100                    ec_master_t *master, 
00101                    unsigned int index 
00102                    )
00103 {
00104     domain->master = master;
00105     domain->index = index;
00106     domain->data_size = 0;
00107     domain->base_address = 0;
00108     domain->response_count = 0xFFFFFFFF;
00109     domain->notify_jiffies = 0;
00110     domain->working_counter_changes = 0;
00111 
00112     INIT_LIST_HEAD(&domain->data_regs);
00113     INIT_LIST_HEAD(&domain->datagrams);
00114 
00115     // init kobject and add it to the hierarchy
00116     memset(&domain->kobj, 0x00, sizeof(struct kobject));
00117     kobject_init(&domain->kobj);
00118     domain->kobj.ktype = &ktype_ec_domain;
00119     domain->kobj.parent = &master->kobj;
00120     if (kobject_set_name(&domain->kobj, "domain%i", index)) {
00121         EC_ERR("Failed to set kobj name.\n");
00122         return -1;
00123     }
00124 
00125     return 0;
00126 }
00127 
00128 /*****************************************************************************/
00129 
00134 void ec_domain_clear(struct kobject *kobj )
00135 {
00136     ec_datagram_t *datagram, *next;
00137     ec_domain_t *domain;
00138 
00139     domain = container_of(kobj, ec_domain_t, kobj);
00140 
00141     EC_INFO("Clearing domain %i.\n", domain->index);
00142 
00143     list_for_each_entry_safe(datagram, next, &domain->datagrams, list) {
00144         ec_datagram_clear(datagram);
00145         kfree(datagram);
00146     }
00147 
00148     ec_domain_clear_data_regs(domain);
00149 
00150     kfree(domain);
00151 }
00152 
00153 /*****************************************************************************/
00154 
00160 int ec_domain_reg_pdo_entry(ec_domain_t *domain, 
00161                             ec_slave_t *slave, 
00162                             const ec_sii_pdo_t *pdo, 
00163                             const ec_sii_pdo_entry_t *entry,
00165                             void **data_ptr 
00167                             )
00168 {
00169     ec_data_reg_t *data_reg;
00170     const ec_sii_sync_t *sync;
00171     const ec_sii_pdo_t *other_pdo;
00172     const ec_sii_pdo_entry_t *other_entry;
00173     unsigned int bit_offset, byte_offset, sync_found;
00174 
00175     // Find sync manager for PDO
00176     sync_found = 0;
00177     list_for_each_entry(sync, &slave->sii_syncs, list) {
00178         if (sync->index == pdo->sync_index) {
00179             sync_found = 1;
00180             break;
00181         }
00182     }
00183 
00184     if (!sync_found) {
00185         EC_ERR("No sync manager for PDO 0x%04X:%i.",
00186                pdo->index, entry->subindex);
00187         return -1;
00188     }
00189 
00190     // Calculate offset for process data pointer
00191     bit_offset = 0;
00192     byte_offset = 0;
00193     list_for_each_entry(other_pdo, &slave->sii_pdos, list) {
00194         if (other_pdo->sync_index != sync->index) continue;
00195 
00196         list_for_each_entry(other_entry, &other_pdo->entries, list) {
00197             if (other_entry == entry) {
00198                 byte_offset = bit_offset / 8;
00199                 break;
00200             }
00201             bit_offset += other_entry->bit_length;
00202         }
00203     }
00204 
00205     // Allocate memory for data registration object
00206     if (!(data_reg =
00207           (ec_data_reg_t *) kmalloc(sizeof(ec_data_reg_t), GFP_KERNEL))) {
00208         EC_ERR("Failed to allocate data registration.\n");
00209         return -1;
00210     }
00211 
00212     if (ec_slave_prepare_fmmu(slave, domain, sync)) {
00213         EC_ERR("FMMU configuration failed.\n");
00214         kfree(data_reg);
00215         return -1;
00216     }
00217 
00218     data_reg->slave = slave;
00219     data_reg->sync = sync;
00220     data_reg->sync_offset = byte_offset;
00221     data_reg->data_ptr = data_ptr;
00222 
00223     list_add_tail(&data_reg->list, &domain->data_regs);
00224     return 0;
00225 }
00226 
00227 /*****************************************************************************/
00228 
00233 void ec_domain_clear_data_regs(ec_domain_t *domain )
00234 {
00235     ec_data_reg_t *data_reg, *next;
00236 
00237     list_for_each_entry_safe(data_reg, next, &domain->data_regs, list) {
00238         list_del(&data_reg->list);
00239         kfree(data_reg);
00240     }
00241 }
00242 
00243 /*****************************************************************************/
00244 
00250 int ec_domain_add_datagram(ec_domain_t *domain, 
00251                            uint32_t offset, 
00252                            size_t data_size 
00253                            )
00254 {
00255     ec_datagram_t *datagram;
00256 
00257     if (!(datagram = kmalloc(sizeof(ec_datagram_t), GFP_KERNEL))) {
00258         EC_ERR("Failed to allocate domain datagram!\n");
00259         return -1;
00260     }
00261 
00262     ec_datagram_init(datagram);
00263 
00264     if (ec_datagram_lrw(datagram, offset, data_size)) {
00265         kfree(datagram);
00266         return -1;
00267     }
00268 
00269     list_add_tail(&datagram->list, &domain->datagrams);
00270     return 0;
00271 }
00272 
00273 /*****************************************************************************/
00274 
00283 int ec_domain_alloc(ec_domain_t *domain, 
00284                     uint32_t base_address 
00285                     )
00286 {
00287     ec_data_reg_t *data_reg;
00288     ec_slave_t *slave;
00289     ec_fmmu_t *fmmu;
00290     unsigned int i, j, datagram_count;
00291     uint32_t pdo_off, pdo_off_datagram;
00292     uint32_t datagram_offset;
00293     size_t datagram_data_size, sync_size;
00294     ec_datagram_t *datagram;
00295 
00296     domain->base_address = base_address;
00297 
00298     // calculate size of process data and allocate memory
00299     domain->data_size = 0;
00300     datagram_offset = base_address;
00301     datagram_data_size = 0;
00302     datagram_count = 0;
00303     list_for_each_entry(slave, &domain->master->slaves, list) {
00304         for (j = 0; j < slave->fmmu_count; j++) {
00305             fmmu = &slave->fmmus[j];
00306             if (fmmu->domain == domain) {
00307                 fmmu->logical_start_address = base_address + domain->data_size;
00308                 sync_size = ec_slave_calc_sync_size(slave, fmmu->sync);
00309                 domain->data_size += sync_size;
00310                 if (datagram_data_size + sync_size > EC_MAX_DATA_SIZE) {
00311                     if (ec_domain_add_datagram(domain, datagram_offset,
00312                                                datagram_data_size)) return -1;
00313                     datagram_offset += datagram_data_size;
00314                     datagram_data_size = 0;
00315                     datagram_count++;
00316                 }
00317                 datagram_data_size += sync_size;
00318             }
00319         }
00320     }
00321 
00322     // allocate last datagram
00323     if (datagram_data_size) {
00324         if (ec_domain_add_datagram(domain, datagram_offset,
00325                                    datagram_data_size))
00326             return -1;
00327         datagram_count++;
00328     }
00329 
00330     if (!datagram_count) {
00331         EC_WARN("Domain %i contains no data!\n", domain->index);
00332         ec_domain_clear_data_regs(domain);
00333         return 0;
00334     }
00335 
00336     // set all process data pointers
00337     list_for_each_entry(data_reg, &domain->data_regs, list) {
00338         for (i = 0; i < data_reg->slave->fmmu_count; i++) {
00339             fmmu = &data_reg->slave->fmmus[i];
00340             if (fmmu->domain == domain && fmmu->sync == data_reg->sync) {
00341                 pdo_off = fmmu->logical_start_address + data_reg->sync_offset;
00342                 // search datagram
00343                 list_for_each_entry(datagram, &domain->datagrams, list) {
00344                     pdo_off_datagram = pdo_off - datagram->address.logical;
00345                     if (pdo_off >= datagram->address.logical &&
00346                         pdo_off_datagram < datagram->mem_size) {
00347                         *data_reg->data_ptr = datagram->data +
00348                             pdo_off_datagram;
00349                     }
00350                 }
00351                 if (!data_reg->data_ptr) {
00352                     EC_ERR("Failed to assign data pointer!\n");
00353                     return -1;
00354                 }
00355                 break;
00356             }
00357         }
00358     }
00359 
00360     EC_INFO("Domain %i - Allocated %i bytes in %i datagram%s\n",
00361             domain->index, domain->data_size, datagram_count,
00362             datagram_count == 1 ? "" : "s");
00363 
00364     ec_domain_clear_data_regs(domain);
00365 
00366     return 0;
00367 }
00368 
00369 /*****************************************************************************/
00370 
00375 void ec_domain_queue(ec_domain_t *domain )
00376 {
00377     ec_datagram_t *datagram;
00378 
00379     list_for_each_entry(datagram, &domain->datagrams, list) {
00380         ec_master_queue_datagram(domain->master, datagram);
00381     }
00382 }
00383 
00384 /*****************************************************************************/
00385 
00391 ssize_t ec_show_domain_attribute(struct kobject *kobj, 
00392                                  struct attribute *attr, 
00393                                  char *buffer 
00394                                  )
00395 {
00396     ec_domain_t *domain = container_of(kobj, ec_domain_t, kobj);
00397 
00398     if (attr == &attr_image_size) {
00399         return sprintf(buffer, "%i\n", domain->data_size);
00400     }
00401 
00402     return 0;
00403 }
00404 
00405 /******************************************************************************
00406  *  Realtime interface
00407  *****************************************************************************/
00408 
00416 ec_slave_t *ecrt_domain_register_pdo(ec_domain_t *domain,
00418                                      const char *address,
00421                                      uint32_t vendor_id,
00423                                      uint32_t product_code,
00425                                      uint16_t pdo_index,
00427                                      uint8_t pdo_subindex,
00429                                      void **data_ptr
00432                                      )
00433 {
00434     ec_slave_t *slave;
00435     ec_master_t *master;
00436     const ec_sii_pdo_t *pdo;
00437     const ec_sii_pdo_entry_t *entry;
00438 
00439     master = domain->master;
00440 
00441     // translate address
00442     if (!(slave = ecrt_master_get_slave(master, address))) return NULL;
00443 
00444     if (vendor_id != slave->sii_vendor_id ||
00445         product_code != slave->sii_product_code) {
00446         EC_ERR("Invalid slave type at position %i - Requested: 0x%08X 0x%08X,"
00447                " found: 0x%08X 0x%08X\".\n", slave->ring_position, vendor_id,
00448                product_code, slave->sii_vendor_id, slave->sii_product_code);
00449         return NULL;
00450     }
00451 
00452     if (!data_ptr) {
00453         // data_ptr is NULL => mark slave as "registered" (do not warn)
00454         slave->registered = 1;
00455     }
00456 
00457     list_for_each_entry(pdo, &slave->sii_pdos, list) {
00458         list_for_each_entry(entry, &pdo->entries, list) {
00459             if (entry->index != pdo_index
00460                 || entry->subindex != pdo_subindex) continue;
00461 
00462             if (data_ptr) {
00463                 ec_domain_reg_pdo_entry(domain, slave, pdo, entry, data_ptr);
00464             }
00465 
00466             return slave;
00467         }
00468     }
00469 
00470     EC_ERR("Slave %i does not provide PDO 0x%04X:%i.\n",
00471            slave->ring_position, pdo_index, pdo_subindex);
00472     slave->registered = 0;
00473     return NULL;
00474 }
00475 
00476 /*****************************************************************************/
00477 
00485 int ecrt_domain_register_pdo_list(ec_domain_t *domain,
00487                                   const ec_pdo_reg_t *pdos
00489                                   )
00490 {
00491     const ec_pdo_reg_t *pdo;
00492 
00493     for (pdo = pdos; pdo->slave_address; pdo++)
00494         if (!ecrt_domain_register_pdo(domain, pdo->slave_address,
00495                                       pdo->vendor_id,
00496                                       pdo->product_code,
00497                                       pdo->pdo_index,
00498                                       pdo->pdo_subindex,
00499                                       pdo->data_ptr))
00500             return -1;
00501 
00502     return 0;
00503 }
00504 
00505 /*****************************************************************************/
00506 
00512 void ecrt_domain_process(ec_domain_t *domain )
00513 {
00514     unsigned int working_counter_sum;
00515     ec_datagram_t *datagram;
00516 
00517     working_counter_sum = 0;
00518     list_for_each_entry(datagram, &domain->datagrams, list) {
00519         if (datagram->state == EC_DATAGRAM_RECEIVED) {
00520             working_counter_sum += datagram->working_counter;
00521         }
00522     }
00523 
00524     if (working_counter_sum != domain->response_count) {
00525         domain->working_counter_changes++;
00526         domain->response_count = working_counter_sum;
00527     }
00528 
00529     if (domain->working_counter_changes &&
00530         jiffies - domain->notify_jiffies > HZ) {
00531         domain->notify_jiffies = jiffies;
00532         if (domain->working_counter_changes == 1) {
00533             EC_INFO("Domain %i working counter change: %i\n", domain->index,
00534                     domain->response_count);
00535         }
00536         else {
00537             EC_INFO("Domain %i: %u WC changes. Current response count: %i\n",
00538                     domain->index, domain->working_counter_changes,
00539                     domain->response_count);
00540         }
00541         domain->working_counter_changes = 0;
00542     }
00543 
00544     ec_domain_queue(domain);
00545 }
00546 
00547 /*****************************************************************************/
00548 
00555 int ecrt_domain_state(const ec_domain_t *domain )
00556 {
00557     ec_datagram_t *datagram;
00558 
00559     list_for_each_entry(datagram, &domain->datagrams, list) {
00560         if (datagram->state != EC_DATAGRAM_RECEIVED) return -1;
00561     }
00562 
00563     return 0;
00564 }
00565 
00566 /*****************************************************************************/
00567 
00570 EXPORT_SYMBOL(ecrt_domain_register_pdo);
00571 EXPORT_SYMBOL(ecrt_domain_register_pdo_list);
00572 EXPORT_SYMBOL(ecrt_domain_process);
00573 EXPORT_SYMBOL(ecrt_domain_state);
00574 
00577 /*****************************************************************************/

Generated on Fri Sep 1 14:56:56 2006 for IgH EtherCAT master by  doxygen 1.4.6