5 * Methoden für Gruppen von EtherCAT-Slaves. |
5 * Methoden für Gruppen von EtherCAT-Slaves. |
6 * |
6 * |
7 * $Id$ |
7 * $Id$ |
8 * |
8 * |
9 *****************************************************************************/ |
9 *****************************************************************************/ |
10 |
|
11 #include <linux/module.h> |
|
12 |
10 |
13 #include "globals.h" |
11 #include "globals.h" |
14 #include "domain.h" |
12 #include "domain.h" |
|
13 #include "master.h" |
15 |
14 |
16 /*****************************************************************************/ |
15 /*****************************************************************************/ |
17 |
16 |
18 /** |
17 /** |
19 Konstruktor einer EtherCAT-Domäne. |
18 Konstruktor einer EtherCAT-Domäne. |
20 |
19 */ |
21 @param dom Zeiger auf die zu initialisierende Domäne |
20 |
22 */ |
21 void ec_domain_init(ec_domain_t *domain, /**< Domäne */ |
23 |
22 ec_master_t *master, /**< Zugehöriger Master */ |
24 void ec_domain_init(ec_domain_t *dom) |
23 ec_domain_mode_t mode, /**< Synchron/Asynchron */ |
25 { |
24 unsigned int timeout_us /**< Timeout in Mikrosekunden */ |
26 dom->number = -1; |
25 ) |
27 dom->data_size = 0; |
26 { |
28 dom->logical_offset = 0; |
27 domain->master = master; |
29 dom->response_count = 0xFFFFFFFF; |
28 domain->mode = mode; |
30 |
29 domain->timeout_us = timeout_us; |
31 memset(dom->data, 0x00, EC_FRAME_SIZE); |
30 |
32 } |
31 domain->data = NULL; |
|
32 domain->data_size = 0; |
|
33 domain->base_address = 0; |
|
34 domain->response_count = 0xFFFFFFFF; |
|
35 |
|
36 INIT_LIST_HEAD(&domain->field_regs); |
|
37 } |
|
38 |
|
39 /*****************************************************************************/ |
|
40 |
|
41 /** |
|
42 Destruktor einer EtherCAT-Domäne. |
|
43 */ |
|
44 |
|
45 void ec_domain_clear(ec_domain_t *domain /**< Domäne */) |
|
46 { |
|
47 ec_field_reg_t *field_reg, *next; |
|
48 |
|
49 if (domain->data) { |
|
50 kfree(domain->data); |
|
51 domain->data = NULL; |
|
52 } |
|
53 |
|
54 // Liste der registrierten Datenfelder löschen |
|
55 list_for_each_entry_safe(field_reg, next, &domain->field_regs, list) { |
|
56 kfree(field_reg); |
|
57 } |
|
58 } |
|
59 |
|
60 /*****************************************************************************/ |
|
61 |
|
62 /** |
|
63 Registriert ein Feld in einer Domäne. |
|
64 |
|
65 \returns 0 bei Erfolg, < 0 bei Fehler |
|
66 */ |
|
67 |
|
68 int ec_domain_reg_field(ec_domain_t *domain, /**< Domäne */ |
|
69 ec_slave_t *slave, /**< Slave */ |
|
70 const ec_sync_t *sync, /**< Sync-Manager */ |
|
71 uint32_t field_offset, /**< Datenfeld-Offset */ |
|
72 void **data_ptr /**< Adresse des Prozessdatenzeigers */ |
|
73 ) |
|
74 { |
|
75 ec_field_reg_t *field_reg; |
|
76 |
|
77 if (!(field_reg = (ec_field_reg_t *) kmalloc(sizeof(ec_field_reg_t), |
|
78 GFP_KERNEL))) { |
|
79 printk(KERN_ERR "EtherCAT: Failed to allocate field registration.\n"); |
|
80 return -1; |
|
81 } |
|
82 |
|
83 if (ec_slave_set_fmmu(slave, domain, sync)) { |
|
84 printk(KERN_ERR "EtherCAT: FMMU configuration failed.\n"); |
|
85 kfree(field_reg); |
|
86 return -1; |
|
87 } |
|
88 |
|
89 field_reg->slave = slave; |
|
90 field_reg->sync = sync; |
|
91 field_reg->field_offset = field_offset; |
|
92 field_reg->data_ptr = data_ptr; |
|
93 |
|
94 list_add_tail(&field_reg->list, &domain->field_regs); |
|
95 |
|
96 return 0; |
|
97 } |
|
98 |
|
99 /*****************************************************************************/ |
|
100 |
|
101 /** |
|
102 \returns 0 bei Erfolg, < 0 bei Fehler |
|
103 */ |
|
104 |
|
105 int ec_domain_alloc(ec_domain_t *domain, /**< Domäne */ |
|
106 uint32_t base_address /**< Logische Basisadresse */ |
|
107 ) |
|
108 { |
|
109 ec_field_reg_t *field_reg, *next; |
|
110 ec_slave_t *slave; |
|
111 ec_fmmu_t *fmmu; |
|
112 unsigned int i, j, found, data_offset; |
|
113 |
|
114 if (domain->data) { |
|
115 printk(KERN_ERR "EtherCAT: Domain already allocated!\n"); |
|
116 return -1; |
|
117 } |
|
118 |
|
119 domain->base_address = base_address; |
|
120 |
|
121 // Größe der Prozessdaten berechnen |
|
122 // und logische Adressen der FMMUs setzen |
|
123 domain->data_size = 0; |
|
124 for (i = 0; i < domain->master->slave_count; i++) { |
|
125 slave = &domain->master->slaves[i]; |
|
126 for (j = 0; j < slave->fmmu_count; j++) { |
|
127 fmmu = &slave->fmmus[j]; |
|
128 if (fmmu->domain == domain) { |
|
129 fmmu->logical_start_address = base_address + domain->data_size; |
|
130 domain->data_size += fmmu->sync->size; |
|
131 } |
|
132 } |
|
133 } |
|
134 |
|
135 if (!domain->data_size) { |
|
136 printk(KERN_WARNING "EtherCAT: Domain 0x%08X contains no data!\n", |
|
137 (u32) domain); |
|
138 } |
|
139 else { |
|
140 // Prozessdaten allozieren |
|
141 if (!(domain->data = kmalloc(domain->data_size, GFP_KERNEL))) { |
|
142 printk(KERN_ERR "EtherCAT: Failed to allocate domain data!\n"); |
|
143 return -1; |
|
144 } |
|
145 |
|
146 // Prozessdaten mit Nullen vorbelegen |
|
147 memset(domain->data, 0x00, domain->data_size); |
|
148 |
|
149 // Alle Prozessdatenzeiger setzen |
|
150 list_for_each_entry(field_reg, &domain->field_regs, list) { |
|
151 found = 0; |
|
152 for (i = 0; i < field_reg->slave->fmmu_count; i++) { |
|
153 fmmu = &field_reg->slave->fmmus[i]; |
|
154 if (fmmu->domain == domain && fmmu->sync == field_reg->sync) { |
|
155 data_offset = fmmu->logical_start_address - base_address |
|
156 + field_reg->field_offset; |
|
157 *field_reg->data_ptr = domain->data + data_offset; |
|
158 found = 1; |
|
159 break; |
|
160 } |
|
161 } |
|
162 |
|
163 if (!found) { // Sollte nie passieren |
|
164 printk(KERN_ERR "EtherCAT: FMMU not found. Please report!\n"); |
|
165 return -1; |
|
166 } |
|
167 } |
|
168 } |
|
169 |
|
170 // Registrierungsliste wird jetzt nicht mehr gebraucht. |
|
171 list_for_each_entry_safe(field_reg, next, &domain->field_regs, list) { |
|
172 kfree(field_reg); |
|
173 } |
|
174 INIT_LIST_HEAD(&domain->field_regs); // wichtig! |
|
175 |
|
176 return 0; |
|
177 } |
|
178 |
|
179 /****************************************************************************** |
|
180 * |
|
181 * Echtzeitschnittstelle |
|
182 * |
|
183 *****************************************************************************/ |
|
184 |
|
185 /** |
|
186 Registriert einer Domäne ein Datenfeld hinzu. |
|
187 |
|
188 \return Zeiger auf den Slave bei Erfolg, sonst NULL |
|
189 */ |
|
190 |
|
191 ec_slave_t *EtherCAT_rt_register_slave_field( |
|
192 ec_domain_t *domain, /**< Domäne */ |
|
193 const char *address, /**< ASCII-Addresse des Slaves, siehe ec_address() */ |
|
194 const char *vendor_name, /**< Herstellername */ |
|
195 const char *product_name, /**< Produktname */ |
|
196 void **data_ptr, /**< Adresse des Zeigers auf die Prozessdaten */ |
|
197 ec_field_type_t field_type, /**< Typ des Datenfeldes */ |
|
198 unsigned int field_index, /**< Gibt an, ab welchem Feld mit Typ |
|
199 \a field_type gezählt werden soll. */ |
|
200 unsigned int field_count /**< Anzahl Felder des selben Typs */ |
|
201 ) |
|
202 { |
|
203 ec_slave_t *slave; |
|
204 const ec_slave_type_t *type; |
|
205 ec_master_t *master; |
|
206 const ec_sync_t *sync; |
|
207 const ec_field_t *field; |
|
208 unsigned int field_idx, found, i, j; |
|
209 uint32_t field_offset; |
|
210 |
|
211 if (!field_count) { |
|
212 printk(KERN_ERR "EtherCAT: field_count may not be 0!\n"); |
|
213 return NULL; |
|
214 } |
|
215 |
|
216 master = domain->master; |
|
217 |
|
218 // Adresse übersetzen |
|
219 if ((slave = ec_address(master, address)) == NULL) return NULL; |
|
220 |
|
221 if (!(type = slave->type)) { |
|
222 printk(KERN_ERR "EtherCAT: Slave \"%s\" (position %i) has unknown" |
|
223 " type!\n", address, slave->ring_position); |
|
224 return NULL; |
|
225 } |
|
226 |
|
227 if (strcmp(vendor_name, type->vendor_name) || |
|
228 strcmp(product_name, type->product_name)) { |
|
229 printk(KERN_ERR "EtherCAT: Invalid slave type at position %i -" |
|
230 " Requested: \"%s %s\", found: \"%s %s\".\n", |
|
231 slave->ring_position, vendor_name, product_name, |
|
232 type->vendor_name, type->product_name); |
|
233 return NULL; |
|
234 } |
|
235 |
|
236 field_idx = 0; |
|
237 found = 0; |
|
238 for (i = 0; type->sync_managers[i] && !found; i++) { |
|
239 sync = type->sync_managers[i]; |
|
240 field_offset = 0; |
|
241 for (j = 0; sync->fields[j]; j++) { |
|
242 field = sync->fields[j]; |
|
243 if (field->type == field_type) { |
|
244 if (field_idx == field_index) { |
|
245 ec_domain_reg_field(domain, slave, sync, field_offset, |
|
246 data_ptr++); |
|
247 if (!(--field_count)) return slave; |
|
248 } |
|
249 field_idx++; |
|
250 } |
|
251 field_offset += field->size; |
|
252 } |
|
253 } |
|
254 |
|
255 printk(KERN_ERR "EtherCAT: Slave %i (\"%s %s\") has less than %i fields of" |
|
256 " type %i, starting at %i!\n", slave->ring_position, |
|
257 vendor_name, product_name, field_count, field_type, field_index); |
|
258 return NULL; |
|
259 } |
|
260 |
|
261 /*****************************************************************************/ |
|
262 |
|
263 /** |
|
264 Sendet und empfängt Prozessdaten der angegebenen Domäne |
|
265 |
|
266 \return 0 bei Erfolg, sonst < 0 |
|
267 */ |
|
268 |
|
269 int EtherCAT_rt_domain_xio(ec_domain_t *domain /**< Domäne */) |
|
270 { |
|
271 unsigned int offset, size, working_counter_sum; |
|
272 unsigned long start_ticks, end_ticks, timeout_ticks; |
|
273 ec_master_t *master; |
|
274 ec_frame_t *frame; |
|
275 |
|
276 master = domain->master; |
|
277 frame = &domain->frame; |
|
278 working_counter_sum = 0; |
|
279 |
|
280 ec_output_lost_frames(master); // Evtl. verlorene Frames ausgeben |
|
281 |
|
282 rdtscl(start_ticks); // Sendezeit nehmen |
|
283 timeout_ticks = domain->timeout_us * cpu_khz / 1000; |
|
284 |
|
285 offset = 0; |
|
286 while (offset < domain->data_size) |
|
287 { |
|
288 size = domain->data_size - offset; |
|
289 if (size > EC_MAX_DATA_SIZE) size = EC_MAX_DATA_SIZE; |
|
290 |
|
291 ec_frame_init_lrw(frame, master, domain->base_address + offset, size, |
|
292 domain->data + offset); |
|
293 |
|
294 if (unlikely(ec_frame_send(frame) < 0)) { |
|
295 printk(KERN_ERR "EtherCAT: Could not send process data" |
|
296 " command!\n"); |
|
297 return -1; |
|
298 } |
|
299 |
|
300 // Warten |
|
301 do { |
|
302 ec_device_call_isr(&master->device); |
|
303 rdtscl(end_ticks); // Empfangszeit nehmen |
|
304 } |
|
305 while (unlikely(master->device.state == EC_DEVICE_STATE_SENT |
|
306 && end_ticks - start_ticks < timeout_ticks)); |
|
307 |
|
308 master->bus_time = (end_ticks - start_ticks) * 1000 / cpu_khz; |
|
309 |
|
310 if (unlikely(end_ticks - start_ticks >= timeout_ticks)) { |
|
311 master->device.state = EC_DEVICE_STATE_READY; |
|
312 master->frames_lost++; |
|
313 ec_output_lost_frames(master); |
|
314 return -1; |
|
315 } |
|
316 |
|
317 if (unlikely(ec_frame_receive(frame) < 0)) { |
|
318 printk(KERN_ERR "EtherCAT: Receive error!\n"); |
|
319 return -1; |
|
320 } |
|
321 |
|
322 if (unlikely(frame->state != ec_frame_received)) { |
|
323 printk(KERN_WARNING "EtherCAT: Process data command not" |
|
324 " received!\n"); |
|
325 return -1; |
|
326 } |
|
327 |
|
328 working_counter_sum += frame->working_counter; |
|
329 |
|
330 // Daten vom Rahmen in den Prozessdatenspeicher kopieren |
|
331 memcpy(domain->data + offset, frame->data, size); |
|
332 |
|
333 offset += size; |
|
334 } |
|
335 |
|
336 if (working_counter_sum != domain->response_count) { |
|
337 domain->response_count = working_counter_sum; |
|
338 printk(KERN_INFO "EtherCAT: Domain %08X state change - %i slaves" |
|
339 " responding.\n", (unsigned int) domain, working_counter_sum); |
|
340 } |
|
341 |
|
342 return 0; |
|
343 } |
|
344 |
|
345 /*****************************************************************************/ |
|
346 |
|
347 EXPORT_SYMBOL(EtherCAT_rt_register_slave_field); |
|
348 EXPORT_SYMBOL(EtherCAT_rt_domain_xio); |
33 |
349 |
34 /*****************************************************************************/ |
350 /*****************************************************************************/ |
35 |
351 |
36 /* Emacs-Konfiguration |
352 /* Emacs-Konfiguration |
37 ;;; Local Variables: *** |
353 ;;; Local Variables: *** |
38 ;;; c-basic-offset:2 *** |
354 ;;; c-basic-offset:4 *** |
39 ;;; End: *** |
355 ;;; End: *** |
40 */ |
356 */ |