54 ec_domain_t *domain, /**< EtherCAT domain. */ |
59 ec_domain_t *domain, /**< EtherCAT domain. */ |
55 ec_master_t *master, /**< Parent master. */ |
60 ec_master_t *master, /**< Parent master. */ |
56 unsigned int index /**< Index. */ |
61 unsigned int index /**< Index. */ |
57 ) |
62 ) |
58 { |
63 { |
|
64 unsigned int dev_idx; |
|
65 |
59 domain->master = master; |
66 domain->master = master; |
60 domain->index = index; |
67 domain->index = index; |
61 INIT_LIST_HEAD(&domain->fmmu_configs); |
68 INIT_LIST_HEAD(&domain->fmmu_configs); |
62 domain->data_size = 0; |
69 domain->data_size = 0; |
63 domain->tx_size = 0; |
|
64 domain->data = NULL; |
70 domain->data = NULL; |
65 domain->data_origin = EC_ORIG_INTERNAL; |
71 domain->data_origin = EC_ORIG_INTERNAL; |
66 domain->logical_base_address = 0x00000000; |
72 domain->logical_base_address = 0x00000000; |
67 INIT_LIST_HEAD(&domain->datagrams); |
73 INIT_LIST_HEAD(&domain->datagram_pairs); |
68 domain->working_counter = 0x0000; |
74 for (dev_idx = EC_DEVICE_MAIN; dev_idx < ec_master_num_devices(master); |
|
75 dev_idx++) { |
|
76 domain->working_counter[dev_idx] = 0x0000; |
|
77 } |
69 domain->expected_working_counter = 0x0000; |
78 domain->expected_working_counter = 0x0000; |
70 domain->working_counter_changes = 0; |
79 domain->working_counter_changes = 0; |
|
80 domain->redundancy_active = 0; |
71 domain->notify_jiffies = 0; |
81 domain->notify_jiffies = 0; |
72 } |
82 } |
73 |
83 |
74 /*****************************************************************************/ |
84 /*****************************************************************************/ |
75 |
85 |
76 /** Domain destructor. |
86 /** Domain destructor. |
77 */ |
87 */ |
78 void ec_domain_clear(ec_domain_t *domain /**< EtherCAT domain */) |
88 void ec_domain_clear(ec_domain_t *domain /**< EtherCAT domain */) |
79 { |
89 { |
80 ec_datagram_t *datagram, *next; |
90 ec_datagram_pair_t *datagram_pair, *next_pair; |
81 |
91 |
82 // dequeue and free datagrams |
92 // dequeue and free datagrams |
83 list_for_each_entry_safe(datagram, next, &domain->datagrams, list) { |
93 list_for_each_entry_safe(datagram_pair, next_pair, |
84 ec_datagram_clear(datagram); |
94 &domain->datagram_pairs, list) { |
85 kfree(datagram); |
95 ec_datagram_pair_clear(datagram_pair); |
|
96 kfree(datagram_pair); |
86 } |
97 } |
87 |
98 |
88 ec_domain_clear_data(domain); |
99 ec_domain_clear_data(domain); |
89 } |
100 } |
90 |
101 |
113 ) |
125 ) |
114 { |
126 { |
115 fmmu->domain = domain; |
127 fmmu->domain = domain; |
116 |
128 |
117 domain->data_size += fmmu->data_size; |
129 domain->data_size += fmmu->data_size; |
118 domain->tx_size += fmmu->tx_size; |
|
119 list_add_tail(&fmmu->list, &domain->fmmu_configs); |
130 list_add_tail(&fmmu->list, &domain->fmmu_configs); |
120 |
131 |
121 EC_MASTER_DBG(domain->master, 1, "Domain %u:" |
132 EC_MASTER_DBG(domain->master, 1, "Domain %u:" |
122 " Added %u bytes, total %zu.\n", |
133 " Added %u bytes, total %zu.\n", |
123 domain->index, fmmu->data_size, domain->data_size); |
134 domain->index, fmmu->data_size, domain->data_size); |
124 } |
135 } |
125 |
136 |
126 /*****************************************************************************/ |
137 /*****************************************************************************/ |
127 |
138 |
128 /** Allocates a domain datagram and appends it to the list. |
139 /** Allocates a domain datagram pair and appends it to the list. |
129 * |
140 * |
130 * The datagram type and expected working counters are determined by the |
141 * The datagrams' types and expected working counters are determined by the |
131 * number of input and output fmmus that share the datagram. |
142 * number of input and output fmmus that share the datagrams. |
132 * |
143 * |
133 * \retval 0 Success. |
144 * \retval 0 Success. |
134 * \retval <0 Error code. |
145 * \retval <0 Error code. |
135 */ |
146 */ |
136 int ec_domain_add_datagram( |
147 int ec_domain_add_datagram_pair( |
137 ec_domain_t *domain, /**< EtherCAT domain. */ |
148 ec_domain_t *domain, /**< EtherCAT domain. */ |
138 uint32_t logical_offset, /**< Logical offset. */ |
149 uint32_t logical_offset, /**< Logical offset. */ |
139 size_t data_size, /**< Size of the data. */ |
150 size_t data_size, /**< Size of the data. */ |
140 uint8_t *data, /**< Process data. */ |
151 uint8_t *data, /**< Process data. */ |
141 const unsigned int used[] /**< Slave config counter for in/out. */ |
152 const unsigned int used[] /**< Slave config counter for in/out. */ |
142 ) |
153 ) |
143 { |
154 { |
144 ec_datagram_t *datagram; |
155 ec_datagram_pair_t *datagram_pair; |
145 int ret; |
156 int ret; |
146 |
157 |
147 if (!(datagram = kmalloc(sizeof(ec_datagram_t), GFP_KERNEL))) { |
158 if (!(datagram_pair = kmalloc(sizeof(ec_datagram_pair_t), GFP_KERNEL))) { |
148 EC_MASTER_ERR(domain->master, |
159 EC_MASTER_ERR(domain->master, |
149 "Failed to allocate domain datagram!\n"); |
160 "Failed to allocate domain datagram pair!\n"); |
150 return -ENOMEM; |
161 return -ENOMEM; |
151 } |
162 } |
152 |
163 |
153 ec_datagram_init(datagram); |
164 ret = ec_datagram_pair_init(datagram_pair, domain, logical_offset, data, |
154 snprintf(datagram->name, EC_DATAGRAM_NAME_SIZE, |
165 data_size, used); |
155 "domain%u-%u", domain->index, logical_offset); |
166 if (ret) { |
156 |
167 kfree(datagram_pair); |
157 if (used[EC_DIR_OUTPUT] && used[EC_DIR_INPUT]) { // inputs and outputs |
168 return ret; |
158 ret = ec_datagram_lrw(datagram, logical_offset, data_size, data); |
169 } |
159 if (ret < 0) { |
170 |
160 kfree(datagram); |
171 domain->expected_working_counter += |
161 return ret; |
172 datagram_pair->expected_working_counter; |
162 } |
173 |
163 // If LRW is used, output FMMUs increment the working counter by 2, |
174 EC_MASTER_DBG(domain->master, 1, |
164 // while input FMMUs increment it by 1. |
175 "Adding datagram pair with expected WC %u.\n", |
165 domain->expected_working_counter += |
176 datagram_pair->expected_working_counter); |
166 used[EC_DIR_OUTPUT] * 2 + used[EC_DIR_INPUT]; |
177 |
167 } else if (used[EC_DIR_OUTPUT]) { // outputs only |
178 |
168 ret = ec_datagram_lwr(datagram, logical_offset, data_size, data); |
179 list_add_tail(&datagram_pair->list, &domain->datagram_pairs); |
169 if (ret < 0) { |
|
170 kfree(datagram); |
|
171 return ret; |
|
172 } |
|
173 domain->expected_working_counter += used[EC_DIR_OUTPUT]; |
|
174 } else { // inputs only (or nothing) |
|
175 ret = ec_datagram_lrd(datagram, logical_offset, data_size, data); |
|
176 if (ret < 0) { |
|
177 kfree(datagram); |
|
178 return ret; |
|
179 } |
|
180 domain->expected_working_counter += used[EC_DIR_INPUT]; |
|
181 } |
|
182 |
|
183 ec_datagram_zero(datagram); |
|
184 list_add_tail(&datagram->list, &domain->datagrams); |
|
185 datagram->domain = domain; |
|
186 return 0; |
180 return 0; |
187 } |
181 } |
188 |
182 |
189 /*****************************************************************************/ |
183 /*****************************************************************************/ |
190 |
184 |
427 |
455 |
428 /*****************************************************************************/ |
456 /*****************************************************************************/ |
429 |
457 |
430 void ecrt_domain_process(ec_domain_t *domain) |
458 void ecrt_domain_process(ec_domain_t *domain) |
431 { |
459 { |
432 uint16_t working_counter_sum; |
460 uint16_t wc_sum[EC_MAX_NUM_DEVICES] = {}, wc_total; |
433 ec_datagram_t *datagram; |
461 ec_datagram_pair_t *pair; |
434 |
462 #if EC_MAX_NUM_DEVICES > 1 |
435 working_counter_sum = 0x0000; |
463 uint16_t datagram_pair_wc, redundant_wc; |
436 list_for_each_entry(datagram, &domain->datagrams, list) { |
464 unsigned int datagram_offset; |
437 ec_datagram_output_stats(datagram); |
465 ec_fmmu_config_t *fmmu = list_first_entry(&domain->fmmu_configs, |
438 if (datagram->state == EC_DATAGRAM_RECEIVED) { |
466 ec_fmmu_config_t, list); |
439 working_counter_sum += datagram->working_counter; |
467 unsigned int redundancy; |
440 } |
468 #endif |
441 } |
469 unsigned int dev_idx; |
442 |
470 #ifdef EC_RT_SYSLOG |
443 if (working_counter_sum != domain->working_counter) { |
471 unsigned int wc_change; |
|
472 #endif |
|
473 |
|
474 #if DEBUG_REDUNDANCY |
|
475 EC_MASTER_DBG(domain->master, 1, "domain %u process\n", domain->index); |
|
476 #endif |
|
477 |
|
478 list_for_each_entry(pair, &domain->datagram_pairs, list) { |
|
479 #if EC_MAX_NUM_DEVICES > 1 |
|
480 datagram_pair_wc = ec_datagram_pair_process(pair, wc_sum); |
|
481 #else |
|
482 ec_datagram_pair_process(pair, wc_sum); |
|
483 #endif |
|
484 |
|
485 #if EC_MAX_NUM_DEVICES > 1 |
|
486 if (ec_master_num_devices(domain->master) > 1) { |
|
487 ec_datagram_t *main_datagram = &pair->datagrams[EC_DEVICE_MAIN]; |
|
488 uint32_t logical_datagram_address = |
|
489 EC_READ_U32(main_datagram->address); |
|
490 size_t datagram_size = main_datagram->data_size; |
|
491 |
|
492 #if DEBUG_REDUNDANCY |
|
493 EC_MASTER_DBG(domain->master, 1, "dgram %s log=%u\n", |
|
494 main_datagram->name, logical_datagram_address); |
|
495 #endif |
|
496 |
|
497 /* Redundancy: Go through FMMU configs to detect data changes. */ |
|
498 list_for_each_entry_from(fmmu, &domain->fmmu_configs, list) { |
|
499 ec_datagram_t *backup_datagram = |
|
500 &pair->datagrams[EC_DEVICE_BACKUP]; |
|
501 |
|
502 if (fmmu->dir != EC_DIR_INPUT) { |
|
503 continue; |
|
504 } |
|
505 |
|
506 if (fmmu->logical_start_address >= |
|
507 logical_datagram_address + datagram_size) { |
|
508 // fmmu data contained in next datagram pair |
|
509 break; |
|
510 } |
|
511 |
|
512 datagram_offset = |
|
513 fmmu->logical_start_address - logical_datagram_address; |
|
514 |
|
515 #if DEBUG_REDUNDANCY |
|
516 EC_MASTER_DBG(domain->master, 1, |
|
517 "input fmmu log=%u size=%u offset=%u\n", |
|
518 fmmu->logical_start_address, fmmu->data_size, |
|
519 datagram_offset); |
|
520 if (domain->master->debug_level > 0) { |
|
521 ec_print_data(pair->send_buffer + datagram_offset, |
|
522 fmmu->data_size); |
|
523 ec_print_data(main_datagram->data + datagram_offset, |
|
524 fmmu->data_size); |
|
525 ec_print_data(backup_datagram->data + datagram_offset, |
|
526 fmmu->data_size); |
|
527 } |
|
528 #endif |
|
529 |
|
530 if (data_changed(pair->send_buffer, main_datagram, |
|
531 datagram_offset, fmmu->data_size)) { |
|
532 /* data changed on main link: no copying necessary. */ |
|
533 #if DEBUG_REDUNDANCY |
|
534 EC_MASTER_DBG(domain->master, 1, "main changed\n"); |
|
535 #endif |
|
536 } else if (data_changed(pair->send_buffer, backup_datagram, |
|
537 datagram_offset, fmmu->data_size)) { |
|
538 /* data changed on backup link: copy to main memory. */ |
|
539 #if DEBUG_REDUNDANCY |
|
540 EC_MASTER_DBG(domain->master, 1, "backup changed\n"); |
|
541 #endif |
|
542 memcpy(main_datagram->data + datagram_offset, |
|
543 backup_datagram->data + datagram_offset, |
|
544 fmmu->data_size); |
|
545 } else if (datagram_pair_wc == |
|
546 pair->expected_working_counter) { |
|
547 /* no change, but WC complete: use main data. */ |
|
548 #if DEBUG_REDUNDANCY |
|
549 EC_MASTER_DBG(domain->master, 1, |
|
550 "no change but complete\n"); |
|
551 #endif |
|
552 } else { |
|
553 /* no change and WC incomplete: mark WC as zero to avoid |
|
554 * data.dependent WC flickering. */ |
|
555 datagram_pair_wc = 0; |
|
556 #if DEBUG_REDUNDANCY |
|
557 EC_MASTER_DBG(domain->master, 1, |
|
558 "no change and incomplete\n"); |
|
559 #endif |
|
560 } |
|
561 } |
|
562 } |
|
563 #endif // EC_MAX_NUM_DEVICES > 1 |
|
564 } |
|
565 |
|
566 #if EC_MAX_NUM_DEVICES > 1 |
|
567 redundant_wc = 0; |
|
568 for (dev_idx = EC_DEVICE_BACKUP; |
|
569 dev_idx < ec_master_num_devices(domain->master); dev_idx++) { |
|
570 redundant_wc += wc_sum[dev_idx]; |
|
571 } |
|
572 |
|
573 redundancy = redundant_wc > 0; |
|
574 if (redundancy != domain->redundancy_active) { |
|
575 #ifdef EC_RT_SYSLOG |
|
576 if (redundancy) { |
|
577 EC_MASTER_WARN(domain->master, |
|
578 "Domain %u: Redundant link in use!\n", |
|
579 domain->index); |
|
580 } else { |
|
581 EC_MASTER_INFO(domain->master, |
|
582 "Domain %u: Redundant link unused again.\n", |
|
583 domain->index); |
|
584 } |
|
585 #endif |
|
586 domain->redundancy_active = redundancy; |
|
587 } |
|
588 #else |
|
589 domain->redundancy_active = 0; |
|
590 #endif |
|
591 |
|
592 #ifdef EC_RT_SYSLOG |
|
593 wc_change = 0; |
|
594 #endif |
|
595 wc_total = 0; |
|
596 for (dev_idx = EC_DEVICE_MAIN; |
|
597 dev_idx < ec_master_num_devices(domain->master); dev_idx++) { |
|
598 if (wc_sum[dev_idx] != domain->working_counter[dev_idx]) { |
|
599 #ifdef EC_RT_SYSLOG |
|
600 wc_change = 1; |
|
601 #endif |
|
602 domain->working_counter[dev_idx] = wc_sum[dev_idx]; |
|
603 } |
|
604 wc_total += wc_sum[dev_idx]; |
|
605 } |
|
606 |
|
607 #ifdef EC_RT_SYSLOG |
|
608 if (wc_change) { |
444 domain->working_counter_changes++; |
609 domain->working_counter_changes++; |
445 domain->working_counter = working_counter_sum; |
|
446 } |
610 } |
447 |
611 |
448 if (domain->working_counter_changes && |
612 if (domain->working_counter_changes && |
449 jiffies - domain->notify_jiffies > HZ) { |
613 jiffies - domain->notify_jiffies > HZ) { |
450 domain->notify_jiffies = jiffies; |
614 domain->notify_jiffies = jiffies; |
451 if (domain->working_counter_changes == 1) { |
615 if (domain->working_counter_changes == 1) { |
452 EC_MASTER_INFO(domain->master, "Domain %u: Working counter" |
616 EC_MASTER_INFO(domain->master, "Domain %u: Working counter" |
453 " changed to %u/%u.\n", domain->index, |
617 " changed to %u/%u", domain->index, |
454 domain->working_counter, domain->expected_working_counter); |
618 wc_total, domain->expected_working_counter); |
455 } else { |
619 } else { |
456 EC_MASTER_INFO(domain->master, "Domain %u: %u working counter" |
620 EC_MASTER_INFO(domain->master, "Domain %u: %u working counter" |
457 " changes - now %u/%u.\n", domain->index, |
621 " changes - now %u/%u", domain->index, |
458 domain->working_counter_changes, domain->working_counter, |
622 domain->working_counter_changes, |
459 domain->expected_working_counter); |
623 wc_total, domain->expected_working_counter); |
460 } |
624 } |
|
625 #if EC_MAX_NUM_DEVICES > 1 |
|
626 if (ec_master_num_devices(domain->master) > 1) { |
|
627 printk(" ("); |
|
628 for (dev_idx = EC_DEVICE_MAIN; |
|
629 dev_idx < ec_master_num_devices(domain->master); |
|
630 dev_idx++) { |
|
631 printk("%u", domain->working_counter[dev_idx]); |
|
632 if (dev_idx + 1 < ec_master_num_devices(domain->master)) { |
|
633 printk("+"); |
|
634 } |
|
635 } |
|
636 printk(")"); |
|
637 } |
|
638 #endif |
|
639 printk(".\n"); |
|
640 |
461 domain->working_counter_changes = 0; |
641 domain->working_counter_changes = 0; |
462 } |
642 } |
|
643 #endif |
463 } |
644 } |
464 |
645 |
465 /*****************************************************************************/ |
646 /*****************************************************************************/ |
466 |
647 |
467 void ecrt_domain_queue(ec_domain_t *domain) |
648 void ecrt_domain_queue(ec_domain_t *domain) |
468 { |
649 { |
469 ec_datagram_t *datagram; |
650 ec_datagram_pair_t *datagram_pair; |
470 |
651 ec_device_index_t dev_idx; |
471 list_for_each_entry(datagram, &domain->datagrams, list) { |
652 |
472 ec_master_queue_datagram(domain->master, datagram); |
653 list_for_each_entry(datagram_pair, &domain->datagram_pairs, list) { |
|
654 |
|
655 #if EC_MAX_NUM_DEVICES > 1 |
|
656 /* copy main data to send buffer */ |
|
657 memcpy(datagram_pair->send_buffer, |
|
658 datagram_pair->datagrams[EC_DEVICE_MAIN].data, |
|
659 datagram_pair->datagrams[EC_DEVICE_MAIN].data_size); |
|
660 #endif |
|
661 ec_master_queue_datagram(domain->master, |
|
662 &datagram_pair->datagrams[EC_DEVICE_MAIN]); |
|
663 |
|
664 /* copy main data to backup datagram */ |
|
665 for (dev_idx = EC_DEVICE_BACKUP; |
|
666 dev_idx < ec_master_num_devices(domain->master); dev_idx++) { |
|
667 memcpy(datagram_pair->datagrams[dev_idx].data, |
|
668 datagram_pair->datagrams[EC_DEVICE_MAIN].data, |
|
669 datagram_pair->datagrams[EC_DEVICE_MAIN].data_size); |
|
670 ec_master_queue_datagram(domain->master, |
|
671 &datagram_pair->datagrams[dev_idx]); |
|
672 } |
473 } |
673 } |
474 } |
674 } |
475 |
675 |
476 /*****************************************************************************/ |
676 /*****************************************************************************/ |
477 |
677 |
478 void ecrt_domain_state(const ec_domain_t *domain, ec_domain_state_t *state) |
678 void ecrt_domain_state(const ec_domain_t *domain, ec_domain_state_t *state) |
479 { |
679 { |
480 state->working_counter = domain->working_counter; |
680 unsigned int dev_idx; |
481 |
681 uint16_t wc = 0; |
482 if (domain->working_counter) { |
682 |
483 if (domain->working_counter == domain->expected_working_counter) { |
683 for (dev_idx = EC_DEVICE_MAIN; |
|
684 dev_idx < ec_master_num_devices(domain->master); dev_idx++) { |
|
685 wc += domain->working_counter[dev_idx]; |
|
686 } |
|
687 |
|
688 state->working_counter = wc; |
|
689 |
|
690 if (wc) { |
|
691 if (wc == domain->expected_working_counter) { |
484 state->wc_state = EC_WC_COMPLETE; |
692 state->wc_state = EC_WC_COMPLETE; |
485 } else { |
693 } else { |
486 state->wc_state = EC_WC_INCOMPLETE; |
694 state->wc_state = EC_WC_INCOMPLETE; |
487 } |
695 } |
488 } else { |
696 } else { |
489 state->wc_state = EC_WC_ZERO; |
697 state->wc_state = EC_WC_ZERO; |
490 } |
698 } |
|
699 |
|
700 state->redundancy_active = domain->redundancy_active; |
491 } |
701 } |
492 |
702 |
493 /*****************************************************************************/ |
703 /*****************************************************************************/ |
494 |
704 |
495 /** \cond */ |
705 /** \cond */ |