7 * $Id$ |
7 * $Id$ |
8 * |
8 * |
9 *****************************************************************************/ |
9 *****************************************************************************/ |
10 |
10 |
11 #include <linux/module.h> |
11 #include <linux/module.h> |
|
12 #include <linux/delay.h> |
12 |
13 |
13 #include "globals.h" |
14 #include "globals.h" |
14 #include "slave.h" |
15 #include "slave.h" |
|
16 #include "frame.h" |
15 |
17 |
16 /*****************************************************************************/ |
18 /*****************************************************************************/ |
17 |
19 |
18 /** |
20 /** |
19 EtherCAT-Slave-Konstruktor. |
21 EtherCAT-Slave-Konstruktor. |
20 |
22 */ |
21 Initialisiert einen EtherCAT-Slave. |
23 |
22 |
24 void ec_slave_init(ec_slave_t *slave, /**< EtherCAT-Slave */ |
23 ACHTUNG! Dieser Konstruktor wird quasi nie aufgerufen. Bitte immer das |
25 ec_master_t *master /**< EtherCAT-Master */ |
24 Makro ECAT_INIT_SLAVE() in ec_slave.h anpassen! |
26 ) |
25 |
27 { |
26 @param slave Zeiger auf den zu initialisierenden Slave |
28 slave->master = master; |
27 */ |
29 slave->base_type = 0; |
28 |
30 slave->base_revision = 0; |
29 void ec_slave_init(ec_slave_t *slave) |
31 slave->base_build = 0; |
30 { |
32 slave->base_fmmu_count = 0; |
31 slave->base_type = 0; |
33 slave->base_sync_count = 0; |
32 slave->base_revision = 0; |
34 slave->ring_position = 0; |
33 slave->base_build = 0; |
35 slave->station_address = 0; |
34 slave->ring_position = 0; |
36 slave->sii_vendor_id = 0; |
35 slave->station_address = 0; |
37 slave->sii_product_code = 0; |
36 slave->sii_vendor_id = 0; |
38 slave->sii_revision_number = 0; |
37 slave->sii_product_code = 0; |
39 slave->sii_serial_number = 0; |
38 slave->sii_revision_number = 0; |
40 slave->type = NULL; |
39 slave->sii_serial_number = 0; |
41 slave->registered = 0; |
40 slave->type = NULL; |
42 slave->fmmu_count = 0; |
41 slave->logical_address = 0; |
43 } |
42 slave->process_data = NULL; |
44 |
43 slave->private_data = NULL; |
45 /*****************************************************************************/ |
44 slave->configure = NULL; |
46 |
45 slave->registered = 0; |
47 /** |
46 slave->domain = 0; |
48 EtherCAT-Slave-Destruktor. |
47 slave->error_reported = 0; |
49 */ |
|
50 |
|
51 void ec_slave_clear(ec_slave_t *slave /**< EtherCAT-Slave */) |
|
52 { |
|
53 // Nichts freizugeben |
|
54 } |
|
55 |
|
56 /*****************************************************************************/ |
|
57 |
|
58 /** |
|
59 Liest alle benötigten Informationen aus einem Slave. |
|
60 */ |
|
61 |
|
62 int ec_slave_fetch(ec_slave_t *slave /**< EtherCAT-Slave */) |
|
63 { |
|
64 ec_frame_t frame; |
|
65 |
|
66 // Read base data |
|
67 ec_frame_init_nprd(&frame, slave->master, slave->station_address, |
|
68 0x0000, 6); |
|
69 |
|
70 if (unlikely(ec_frame_send_receive(&frame))) return -1; |
|
71 |
|
72 if (unlikely(frame.working_counter != 1)) { |
|
73 printk(KERN_ERR "EtherCAT: Slave %i did not respond while reading base" |
|
74 " data!\n", slave->ring_position); |
|
75 return -1; |
|
76 } |
|
77 |
|
78 slave->base_type = frame.data[0]; |
|
79 slave->base_revision = frame.data[1]; |
|
80 slave->base_build = frame.data[2] | (frame.data[3] << 8); |
|
81 slave->base_fmmu_count = frame.data[4]; |
|
82 slave->base_sync_count = frame.data[5]; |
|
83 |
|
84 if (slave->base_fmmu_count > EC_MAX_FMMUS) |
|
85 slave->base_fmmu_count = EC_MAX_FMMUS; |
|
86 |
|
87 // Read identification from "Slave Information Interface" (SII) |
|
88 |
|
89 if (unlikely(ec_slave_sii_read(slave, 0x0008, &slave->sii_vendor_id))) { |
|
90 printk(KERN_ERR "EtherCAT: Could not read SII vendor id!\n"); |
|
91 return -1; |
|
92 } |
|
93 |
|
94 if (unlikely(ec_slave_sii_read(slave, 0x000A, &slave->sii_product_code))) { |
|
95 printk(KERN_ERR "EtherCAT: Could not read SII product code!\n"); |
|
96 return -1; |
|
97 } |
|
98 |
|
99 if (unlikely(ec_slave_sii_read(slave, 0x000C, |
|
100 &slave->sii_revision_number))) { |
|
101 printk(KERN_ERR "EtherCAT: Could not read SII revision number!\n"); |
|
102 return -1; |
|
103 } |
|
104 |
|
105 if (unlikely(ec_slave_sii_read(slave, 0x000E, |
|
106 &slave->sii_serial_number))) { |
|
107 printk(KERN_ERR "EtherCAT: Could not read SII serial number!\n"); |
|
108 return -1; |
|
109 } |
|
110 |
|
111 return 0; |
|
112 } |
|
113 |
|
114 /*****************************************************************************/ |
|
115 |
|
116 /** |
|
117 Liest Daten aus dem Slave-Information-Interface |
|
118 eines EtherCAT-Slaves. |
|
119 |
|
120 \return 0 bei Erfolg, sonst < 0 |
|
121 */ |
|
122 |
|
123 int ec_slave_sii_read(ec_slave_t *slave, |
|
124 /**< EtherCAT-Slave */ |
|
125 unsigned short int offset, |
|
126 /**< Adresse des zu lesenden SII-Registers */ |
|
127 unsigned int *target |
|
128 /**< Zeiger auf einen 4 Byte großen Speicher zum Ablegen |
|
129 der Daten */ |
|
130 ) |
|
131 { |
|
132 ec_frame_t frame; |
|
133 unsigned char data[10]; |
|
134 unsigned int tries_left; |
|
135 |
|
136 // Initiate read operation |
|
137 |
|
138 data[0] = 0x00; |
|
139 data[1] = 0x01; |
|
140 data[2] = offset & 0xFF; |
|
141 data[3] = (offset & 0xFF00) >> 8; |
|
142 data[4] = 0x00; |
|
143 data[5] = 0x00; |
|
144 |
|
145 ec_frame_init_npwr(&frame, slave->master, slave->station_address, 0x502, 6, |
|
146 data); |
|
147 |
|
148 if (unlikely(ec_frame_send_receive(&frame))) return -1; |
|
149 |
|
150 if (unlikely(frame.working_counter != 1)) { |
|
151 printk(KERN_ERR "EtherCAT: SII-read - Slave %i did not respond!\n", |
|
152 slave->ring_position); |
|
153 return -1; |
|
154 } |
|
155 |
|
156 // Der Slave legt die Informationen des Slave-Information-Interface |
|
157 // in das Datenregister und löscht daraufhin ein Busy-Bit. Solange |
|
158 // den Status auslesen, bis das Bit weg ist. |
|
159 |
|
160 tries_left = 100; |
|
161 while (likely(tries_left)) |
|
162 { |
|
163 udelay(10); |
|
164 |
|
165 ec_frame_init_nprd(&frame, slave->master, slave->station_address, 0x502, |
|
166 10); |
|
167 |
|
168 if (unlikely(ec_frame_send_receive(&frame))) return -1; |
|
169 |
|
170 if (unlikely(frame.working_counter != 1)) { |
|
171 printk(KERN_ERR "EtherCAT: SII-read status -" |
|
172 " Slave %i did not respond!\n", slave->ring_position); |
|
173 return -1; |
|
174 } |
|
175 |
|
176 if (likely((frame.data[1] & 0x81) == 0)) { |
|
177 memcpy(target, frame.data + 6, 4); |
|
178 break; |
|
179 } |
|
180 |
|
181 tries_left--; |
|
182 } |
|
183 |
|
184 if (unlikely(!tries_left)) { |
|
185 printk(KERN_WARNING "EtherCAT: SSI-read. Slave %i timed out!\n", |
|
186 slave->ring_position); |
|
187 return -1; |
|
188 } |
|
189 |
|
190 return 0; |
|
191 } |
|
192 |
|
193 /*****************************************************************************/ |
|
194 |
|
195 /** |
|
196 Bestätigt einen Fehler beim Zustandswechsel. |
|
197 |
|
198 FIXME Funktioniert noch nicht... |
|
199 */ |
|
200 |
|
201 void ec_slave_state_ack(ec_slave_t *slave, |
|
202 /**< Slave, dessen Zustand geändert werden soll */ |
|
203 uint8_t state |
|
204 /**< Alter Zustand */ |
|
205 ) |
|
206 { |
|
207 ec_frame_t frame; |
|
208 unsigned char data[2]; |
|
209 unsigned int tries_left; |
|
210 |
|
211 data[0] = state | EC_ACK; |
|
212 data[1] = 0x00; |
|
213 |
|
214 ec_frame_init_npwr(&frame, slave->master, slave->station_address, 0x0120, |
|
215 2, data); |
|
216 |
|
217 if (unlikely(ec_frame_send_receive(&frame) != 0)) { |
|
218 printk(KERN_ERR "EtherCAT: Could no acknowledge state %02X - Unable to" |
|
219 " send!\n", state); |
|
220 return; |
|
221 } |
|
222 |
|
223 if (unlikely(frame.working_counter != 1)) { |
|
224 printk(KERN_ERR "EtherCAT: Could not acknowledge state %02X - Slave" |
|
225 " %i did not respond!\n", state, slave->ring_position); |
|
226 return; |
|
227 } |
|
228 |
|
229 tries_left = 100; |
|
230 while (likely(tries_left)) |
|
231 { |
|
232 udelay(10); |
|
233 |
|
234 ec_frame_init_nprd(&frame, slave->master, slave->station_address, |
|
235 0x0130, 2); |
|
236 |
|
237 if (unlikely(ec_frame_send_receive(&frame) != 0)) { |
|
238 printk(KERN_ERR "EtherCAT: Could not check state acknowledgement" |
|
239 " %02X - Unable to send!\n", state); |
|
240 return; |
|
241 } |
|
242 |
|
243 if (unlikely(frame.working_counter != 1)) { |
|
244 printk(KERN_ERR "EtherCAT: Could not check state acknowledgement" |
|
245 " %02X - Slave %i did not respond!\n", state, |
|
246 slave->ring_position); |
|
247 return; |
|
248 } |
|
249 |
|
250 if (unlikely(frame.data[0] != state)) { |
|
251 printk(KERN_ERR "EtherCAT: Could not acknowledge state %02X on" |
|
252 " slave %i (code %02X)!\n", state, slave->ring_position, |
|
253 frame.data[0]); |
|
254 return; |
|
255 } |
|
256 |
|
257 if (likely(frame.data[0] == state)) { |
|
258 printk(KERN_INFO "EtherCAT: Acknowleged state %02X on slave %i.\n", |
|
259 state, slave->ring_position); |
|
260 return; |
|
261 } |
|
262 |
|
263 tries_left--; |
|
264 } |
|
265 |
|
266 if (unlikely(!tries_left)) { |
|
267 printk(KERN_ERR "EtherCAT: Could not check state acknowledgement %02X" |
|
268 " of slave %i - Timeout while checking!\n", state, |
|
269 slave->ring_position); |
|
270 return; |
|
271 } |
|
272 } |
|
273 |
|
274 /*****************************************************************************/ |
|
275 |
|
276 /** |
|
277 Ändert den Zustand eines Slaves. |
|
278 |
|
279 \return 0 bei Erfolg, sonst < 0 |
|
280 */ |
|
281 |
|
282 int ec_slave_state_change(ec_slave_t *slave, |
|
283 /**< Slave, dessen Zustand geändert werden soll */ |
|
284 uint8_t state |
|
285 /**< Neuer Zustand */ |
|
286 ) |
|
287 { |
|
288 ec_frame_t frame; |
|
289 unsigned char data[2]; |
|
290 unsigned int tries_left; |
|
291 |
|
292 data[0] = state; |
|
293 data[1] = 0x00; |
|
294 |
|
295 ec_frame_init_npwr(&frame, slave->master, slave->station_address, 0x0120, |
|
296 2, data); |
|
297 |
|
298 if (unlikely(ec_frame_send_receive(&frame) != 0)) { |
|
299 printk(KERN_ERR "EtherCAT: Could not set state %02X - Unable to" |
|
300 " send!\n", state); |
|
301 return -1; |
|
302 } |
|
303 |
|
304 if (unlikely(frame.working_counter != 1)) { |
|
305 printk(KERN_ERR "EtherCAT: Could not set state %02X - Slave %i did not" |
|
306 " respond!\n", state, slave->ring_position); |
|
307 return -1; |
|
308 } |
|
309 |
|
310 tries_left = 100; |
|
311 while (likely(tries_left)) |
|
312 { |
|
313 udelay(10); |
|
314 |
|
315 ec_frame_init_nprd(&frame, slave->master, slave->station_address, |
|
316 0x0130, 2); |
|
317 |
|
318 if (unlikely(ec_frame_send_receive(&frame) != 0)) { |
|
319 printk(KERN_ERR "EtherCAT: Could not check state %02X - Unable to" |
|
320 " send!\n", state); |
|
321 return -1; |
|
322 } |
|
323 |
|
324 if (unlikely(frame.working_counter != 1)) { |
|
325 printk(KERN_ERR "EtherCAT: Could not check state %02X - Slave %i" |
|
326 " did not respond!\n", state, slave->ring_position); |
|
327 return -1; |
|
328 } |
|
329 |
|
330 if (unlikely(frame.data[0] & 0x10)) { // State change error |
|
331 printk(KERN_ERR "EtherCAT: Could not set state %02X - Slave %i" |
|
332 " refused state change (code %02X)!\n", state, |
|
333 slave->ring_position, frame.data[0]); |
|
334 ec_slave_state_ack(slave, frame.data[0] & 0x0F); |
|
335 return -1; |
|
336 } |
|
337 |
|
338 if (likely(frame.data[0] == (state & 0x0F))) { |
|
339 // State change successful |
|
340 break; |
|
341 } |
|
342 |
|
343 tries_left--; |
|
344 } |
|
345 |
|
346 if (unlikely(!tries_left)) { |
|
347 printk(KERN_ERR "EtherCAT: Could not check state %02X of slave %i -" |
|
348 " Timeout while checking!\n", state, |
|
349 slave->ring_position); |
|
350 return -1; |
|
351 } |
|
352 |
|
353 return 0; |
|
354 } |
|
355 |
|
356 /*****************************************************************************/ |
|
357 |
|
358 /** |
|
359 Merkt eine FMMU-Konfiguration vor. |
|
360 |
|
361 Die FMMU wird so konfiguriert, dass sie den gesamten Datenbereich des |
|
362 entsprechenden Sync-Managers abdeckt. Für jede Domäne werden separate |
|
363 FMMUs konfiguriert. |
|
364 |
|
365 Wenn die entsprechende FMMU bereits konfiguriert ist, wird dies als |
|
366 Erfolg zurückgegeben. |
|
367 |
|
368 \return 0 bei Erfolg, sonst < 0 |
|
369 */ |
|
370 |
|
371 int ec_slave_set_fmmu(ec_slave_t *slave, /**< EtherCAT-Slave */ |
|
372 const ec_domain_t *domain, /**< Domäne */ |
|
373 const ec_sync_t *sync /**< Sync-Manager */ |
|
374 ) |
|
375 { |
|
376 unsigned int i; |
|
377 |
|
378 // FMMU schon vorgemerkt? |
|
379 for (i = 0; i < slave->fmmu_count; i++) |
|
380 if (slave->fmmus[i].domain == domain && slave->fmmus[i].sync == sync) |
|
381 return 0; |
|
382 |
|
383 if (slave->fmmu_count >= slave->base_fmmu_count) { |
|
384 printk(KERN_ERR "EtherCAT: Slave %i supports only %i FMMUs.\n", |
|
385 slave->ring_position, slave->base_fmmu_count); |
|
386 return -1; |
|
387 } |
|
388 |
|
389 slave->fmmus[slave->fmmu_count].domain = domain; |
|
390 slave->fmmus[slave->fmmu_count].sync = sync; |
|
391 slave->fmmus[slave->fmmu_count].logical_start_address = 0; |
|
392 slave->fmmu_count++; |
|
393 slave->registered = 1; |
|
394 |
|
395 return 0; |
|
396 } |
|
397 |
|
398 /*****************************************************************************/ |
|
399 |
|
400 /** |
|
401 Gibt alle Informationen über einen EtherCAT-Slave aus. |
|
402 */ |
|
403 |
|
404 void ec_slave_print(const ec_slave_t *slave /**< EtherCAT-Slave */) |
|
405 { |
|
406 printk(KERN_INFO "--- EtherCAT slave information ---\n"); |
|
407 |
|
408 if (slave->type) { |
|
409 printk(KERN_INFO " Vendor \"%s\", Product \"%s\": %s\n", |
|
410 slave->type->vendor_name, slave->type->product_name, |
|
411 slave->type->description); |
|
412 } |
|
413 else { |
|
414 printk(KERN_INFO " *** This slave has no type information! ***\n"); |
|
415 } |
|
416 |
|
417 printk(KERN_INFO " Ring position: %i, Station address: 0x%04X\n", |
|
418 slave->ring_position, slave->station_address); |
|
419 |
|
420 printk(KERN_INFO " Base information:\n"); |
|
421 printk(KERN_INFO " Type %u, Revision %i, Build %i\n", |
|
422 slave->base_type, slave->base_revision, slave->base_build); |
|
423 printk(KERN_INFO " Supported FMMUs: %i, Sync managers: %i\n", |
|
424 slave->base_fmmu_count, slave->base_sync_count); |
|
425 |
|
426 printk(KERN_INFO " Slave information interface:\n"); |
|
427 printk(KERN_INFO " Vendor-ID: 0x%08X, Product code: 0x%08X\n", |
|
428 slave->sii_vendor_id, slave->sii_product_code); |
|
429 printk(KERN_INFO " Revision number: 0x%08X, Serial number: 0x%08X\n", |
|
430 slave->sii_revision_number, slave->sii_serial_number); |
48 } |
431 } |
49 |
432 |
50 /*****************************************************************************/ |
433 /*****************************************************************************/ |
51 |
434 |
52 /* Emacs-Konfiguration |
435 /* Emacs-Konfiguration |
53 ;;; Local Variables: *** |
436 ;;; Local Variables: *** |
54 ;;; c-basic-offset:2 *** |
437 ;;; c-basic-offset:4 *** |
55 ;;; End: *** |
438 ;;; End: *** |
56 */ |
439 */ |