Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
/******************************************************************************
*
* $Id$
*
* Copyright (C) 2006 Florian Pose, Ingenieurgemeinschaft IgH
*
* This file is part of the IgH EtherCAT Master.
*
* The IgH EtherCAT Master is free software; you can redistribute it
* and/or modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* The IgH EtherCAT Master is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with the IgH EtherCAT Master; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* The right to use EtherCAT Technology is granted and comes free of
* charge under condition of compatibility of product made by
* Licensee. People intending to distribute/sell products based on the
* code, have to sign an agreement to guarantee that products using
* software based on IgH EtherCAT master stay compatible with the actual
* EtherCAT specification (which are released themselves as an open
* standard) as the (only) precondition to have the right to use EtherCAT
* Technology, IP and trade marks.
*
*****************************************************************************/
/**
\file
EtherCAT slave configuration methods.
*/
/*****************************************************************************/
#include <linux/module.h>
#include "globals.h"
#include "master.h"
#include "slave_config.h"
/*****************************************************************************/
void ec_slave_config_clear(struct kobject *);
ssize_t ec_show_slave_config_attribute(struct kobject *, struct attribute *,
char *);
/*****************************************************************************/
/** \cond */
EC_SYSFS_READ_ATTR(info);
static struct attribute *def_attrs[] = {
&attr_info,
NULL,
};
static struct sysfs_ops sysfs_ops = {
.show = ec_show_slave_config_attribute
};
static struct kobj_type ktype_ec_slave_config = {
.release = ec_slave_config_clear,
.sysfs_ops = &sysfs_ops,
.default_attrs = def_attrs
};
/** \endcond */
/*****************************************************************************/
/** Slave configuration constructor.
*
* See ecrt_master_slave_config() for the usage of the \a alias and \a
* position parameters.
*
* \retval 0 Success.
* \retval <0 Failure.
*/
int ec_slave_config_init(ec_slave_config_t *sc, /**< Slave configuration. */
ec_master_t *master, /**< EtherCAT master. */
uint16_t alias, /**< Slave alias. */
uint16_t position, /**< Slave position. */
uint32_t vendor_id, /**< Expected vendor ID. */
uint32_t product_code /**< Expected product code. */
)
{
ec_direction_t dir;
sc->master = master;
sc->alias = alias;
sc->position = position;
sc->vendor_id = vendor_id;
sc->product_code = product_code;
sc->slave = NULL;
for (dir = EC_DIR_OUTPUT; dir <= EC_DIR_INPUT; dir++)
ec_pdo_mapping_init(&sc->mapping[dir]);
INIT_LIST_HEAD(&sc->sdo_configs);
sc->used_fmmus = 0;
// init kobject and add it to the hierarchy
memset(&sc->kobj, 0x00, sizeof(struct kobject));
kobject_init(&sc->kobj);
sc->kobj.ktype = &ktype_ec_slave_config;
sc->kobj.parent = &master->kobj;
if (kobject_set_name(&sc->kobj, "config-%u-%u", sc->alias, sc->position)) {
EC_ERR("Failed to set kobject name for slave config %u:%u.\n",
sc->alias, sc->position);
goto out_put;
}
if (kobject_add(&sc->kobj)) {
EC_ERR("Failed to add kobject for slave config %u:%u.\n",
sc->alias, sc->position);
goto out_put;
}
return 0;
out_put:
kobject_put(&sc->kobj);
return -1;
}
/*****************************************************************************/
/** Slave configuration destructor.
*
* Clears and frees a slave configuration object.
*/
void ec_slave_config_destroy(
ec_slave_config_t *sc /**< Slave configuration. */
)
{
ec_slave_config_detach(sc);
// destroy self
kobject_del(&sc->kobj);
kobject_put(&sc->kobj);
}
/*****************************************************************************/
/** Clear and free the slave configuration.
*
* This method is called by the kobject, once there are no more references to
* it.
*/
void ec_slave_config_clear(struct kobject *kobj /**< kobject of the config. */)
{
ec_slave_config_t *sc;
ec_direction_t dir;
ec_sdo_data_t *sdodata, *next_sdodata;
sc = container_of(kobj, ec_slave_config_t, kobj);
// Free Pdo mappings
for (dir = EC_DIR_OUTPUT; dir <= EC_DIR_INPUT; dir++)
ec_pdo_mapping_clear(&sc->mapping[dir]);
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
list_for_each_entry_safe(sdodata, next_sdodata, &sc->sdo_configs, list) {
list_del(&sdodata->list);
kfree(sdodata->data);
kfree(sdodata);
}
/** \todo */
kfree(sc);
}
/*****************************************************************************/
/** Prepares an FMMU configuration.
*
* Configuration data for the FMMU is saved in the slave config structure and
* is written to the slave during the configuration. The FMMU configuration
* is done in a way, that the complete data range of the corresponding sync
* manager is covered. Seperate FMMUs are configured for each domain. If the
* FMMU configuration is already prepared, the function returns with success.
*
* \retval >=0 Logical offset address.
* \retval -1 FMMU limit reached.
*/
int ec_slave_config_prepare_fmmu(
ec_slave_config_t *sc, /**< Slave configuration. */
ec_domain_t *domain, /**< Domain. */
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
)
{
unsigned int i;
ec_fmmu_config_t *fmmu;
// FMMU configuration already prepared?
for (i = 0; i < sc->used_fmmus; i++) {
fmmu = &sc->fmmu_configs[i];
if (fmmu->domain == domain && fmmu->dir == dir)
return fmmu->logical_start_address;
}
if (sc->used_fmmus == EC_MAX_FMMUS) {
EC_ERR("FMMU limit reached for slave configuration %u:%u!\n",
sc->alias, sc->position);
return -1;
}
fmmu = &sc->fmmu_configs[sc->used_fmmus++];
ec_fmmu_config_init(fmmu, sc, domain, dir);
return fmmu->logical_start_address;
}
/*****************************************************************************/
/** Outputs all information about a certain slave configuration.
*/
ssize_t ec_slave_config_info(
const ec_slave_config_t *sc, /**< Slave configuration. */
char *buffer /**< Output buffer */
)
{
char *buf = buffer;
ec_direction_t dir;
const ec_pdo_mapping_t *map;
const ec_pdo_t *pdo;
const ec_pdo_entry_t *entry;
char str[20];
ec_sdo_data_t *sdodata;
buf += sprintf(buf, "Alias: 0x%04X (%u)\n", sc->alias, sc->alias);
buf += sprintf(buf, "Position: %u\n", sc->position);
for (dir = EC_DIR_OUTPUT; dir <= EC_DIR_INPUT; dir++) {
map = &sc->mapping[dir];
if (!list_empty(&map->pdos)) {
buf += sprintf(buf, "%s mapping:\n",
dir == EC_DIR_OUTPUT ? "Output" : "Input");
list_for_each_entry(pdo, &map->pdos, list) {
buf += sprintf(buf, " %s 0x%04X \"%s\"\n",
pdo->dir == EC_DIR_OUTPUT ? "RxPdo" : "TxPdo",
pdo->index, pdo->name ? pdo->name : "???");
list_for_each_entry(entry, &pdo->entries, list) {
buf += sprintf(buf, " 0x%04X:%X \"%s\", %u bit\n",
entry->index, entry->subindex,
entry->name ? entry->name : "???",
entry->bit_length);
}
}
}
}
// type-cast to avoid warnings on some compilers
if (!list_empty((struct list_head *) &sc->sdo_configs)) {
buf += sprintf(buf, "\nSdo configurations:\n");
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
list_for_each_entry(sdodata, &sc->sdo_configs, list) {
switch (sdodata->size) {
case 1: sprintf(str, "%u", EC_READ_U8(sdodata->data)); break;
case 2: sprintf(str, "%u", EC_READ_U16(sdodata->data)); break;
case 4: sprintf(str, "%u", EC_READ_U32(sdodata->data)); break;
default: sprintf(str, "(invalid size)"); break;
}
buf += sprintf(buf, " 0x%04X:%-3i -> %s\n",
sdodata->index, sdodata->subindex, str);
}
buf += sprintf(buf, "\n");
}
return buf - buffer;
}
/*****************************************************************************/
/** Formats attribute data for SysFS read access.
*
* \return number of bytes to read
*/
ssize_t ec_show_slave_config_attribute(
struct kobject *kobj, /**< Slave configuration's kobject */
struct attribute *attr, /**< Requested attribute. */
char *buffer /**< Memory to store data. */
)
{
ec_slave_config_t *sc = container_of(kobj, ec_slave_config_t, kobj);
if (attr == &attr_info) {
return ec_slave_config_info(sc, buffer);
}
return 0;
}
/*****************************************************************************/
*/
int ec_slave_config_sdo(ec_slave_config_t *sc, uint16_t index,
uint8_t subindex, const uint8_t *data, size_t size)
{
ec_slave_t *slave = sc->slave;
ec_sdo_data_t *sdodata;
if (slave && !(slave->sii.mailbox_protocols & EC_MBOX_COE)) {
EC_ERR("Slave %u does not support CoE!\n", slave->ring_position);
return -1;
}
if (!(sdodata = (ec_sdo_data_t *)
kmalloc(sizeof(ec_sdo_data_t), GFP_KERNEL))) {
EC_ERR("Failed to allocate memory for Sdo configuration object!\n");
return -1;
}
if (!(sdodata->data = (uint8_t *) kmalloc(size, GFP_KERNEL))) {
EC_ERR("Failed to allocate memory for Sdo configuration data!\n");
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
kfree(sdodata);
return -1;
}
sdodata->index = index;
sdodata->subindex = subindex;
memcpy(sdodata->data, data, size);
sdodata->size = size;
list_add_tail(&sdodata->list, &sc->sdo_configs);
return 0;
}
/*****************************************************************************/
/** Attaches the configuration to the addressed slave object.
*
* \retval 0 Success.
* \retval -1 Slave not found.
* \retval -2 Slave already configured.
* \retval -3 Invalid slave type found at the given position.
*/
int ec_slave_config_attach(
ec_slave_config_t *sc /**< Slave configuration. */
)
{
ec_slave_t *slave;
unsigned int alias_found = 0, relative_position = 0;
if (sc->slave)
return 0; // already attached
list_for_each_entry(slave, &sc->master->slaves, list) {
if (!alias_found) {
if (sc->alias && slave->sii.alias != sc->alias)
continue;
alias_found = 1;
relative_position = 0;
}
if (relative_position == sc->position)
goto found;
relative_position++;
}
sc->alias, sc->position);
return -1;
found:
if (slave->config) {
EC_ERR("Failed to attach slave configuration %u:%u. Slave %u"
" already has a configuration!\n", sc->alias,
sc->position, slave->ring_position);
return -2;
}
if (slave->sii.vendor_id != sc->vendor_id
|| slave->sii.product_code != sc->product_code) {
EC_ERR("Slave %u has an invalid type (0x%08X/0x%08X) for"
" configuration %u:%u (0x%08X/0x%08X).\n",
slave->ring_position, slave->sii.vendor_id,
slave->sii.product_code, sc->alias, sc->position,
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
sc->vendor_id, sc->product_code);
return -3;
}
// attach slave
slave->config = sc;
sc->slave = slave;
ec_slave_request_state(slave, EC_SLAVE_STATE_OP);
return 0;
}
/*****************************************************************************/
/** Detaches the configuration from a slave object.
*/
void ec_slave_config_detach(
ec_slave_config_t *sc /**< Slave configuration. */
)
{
if (sc->slave) {
sc->slave->config = NULL;
sc->slave = NULL;
}
}
/*****************************************************************************/
/** Loads the default mapping from the slave object.
*/
void ec_slave_config_load_default_mapping(ec_slave_config_t *sc)
{
ec_direction_t dir;
ec_pdo_mapping_t *map;
ec_sync_t *sync;
if (!sc->slave)
return;
for (dir = EC_DIR_OUTPUT; dir <= EC_DIR_INPUT; dir++) {
map = &sc->mapping[dir];
if (!(sync = ec_slave_get_pdo_sync(sc->slave, dir)))
continue;
ec_pdo_mapping_copy(map, &sync->mapping);
map->default_mapping = 1;
}
}
/*****************************************************************************/
/** Loads the default configuration for a Pdo from the slave object.
*/
void ec_slave_config_load_default_pdo_config(
const ec_slave_config_t *sc,
ec_pdo_t *pdo
)
{
const ec_sync_t *sync;
const ec_pdo_t *default_pdo;
if (sc->master->debug_level)
EC_DBG("Loading default configuration for Pdo 0x%04X in"
" config %u:%u.\n", pdo->index, sc->alias, sc->position);
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
pdo->default_config = 1;
if (!sc->slave) {
EC_WARN("Failed to load default Pdo configuration for %u:%u:"
" Slave not found.\n", sc->alias, sc->position);
return;
}
if (!(sync = ec_slave_get_pdo_sync(sc->slave, pdo->dir))) {
EC_WARN("Slave %u does not provide a default Pdo"
" configuration!\n", sc->slave->ring_position);
return;
}
list_for_each_entry(default_pdo, &sync->mapping.pdos, list) {
if (default_pdo->index != pdo->index)
continue;
if (sc->master->debug_level)
EC_DBG(" Found Pdo name \"%s\".\n",
default_pdo->name);
// try to take Pdo name from mapped one
ec_pdo_set_name(pdo, default_pdo->name);
// copy entries (= default Pdo configuration)
if (ec_pdo_copy_entries(pdo, default_pdo))
return;
if (sc->master->debug_level) {
const ec_pdo_entry_t *entry;
list_for_each_entry(entry, &pdo->entries, list) {
EC_DBG(" Entry 0x%04X:%u.\n",
entry->index, entry->subindex);
}
}
}
}
/******************************************************************************
* Realtime interface
*****************************************************************************/
int ecrt_slave_config_pdo(ec_slave_config_t *sc, ec_direction_t dir,
uint16_t index)
{
ec_pdo_mapping_t *pm = &sc->mapping[dir];
ec_pdo_t *pdo;
if (pm->default_mapping) {
if (sc->master->debug_level)
EC_DBG("Clearing default mapping for dir %u, config %u:%u.\n",
dir, sc->alias, sc->position);
pm->default_mapping = 0;
ec_pdo_mapping_clear_pdos(pm);
}
if (sc->master->debug_level)
EC_DBG("Adding Pdo 0x%04X to mapping for dir %u, config %u:%u.\n",
index, dir, sc->alias, sc->position);
if (!(pdo = ec_pdo_mapping_add_pdo(pm, dir, index)))
return -1;
ec_slave_config_load_default_pdo_config(sc, pdo);
return 0;
}
/*****************************************************************************/
int ecrt_slave_config_pdo_entry(ec_slave_config_t *sc, uint16_t pdo_index,
uint16_t entry_index, uint8_t entry_subindex,
uint8_t entry_bit_length)
{
ec_direction_t dir;
ec_pdo_t *pdo;
if (sc->master->debug_level)
EC_DBG("Adding Pdo entry 0x%04X:%u (%u bit) to configuration of Pdo"
" 0x%04X, config %u:%u.\n", entry_index, entry_subindex,
entry_bit_length, pdo_index, sc->alias, sc->position);
for (dir = EC_DIR_OUTPUT; dir <= EC_DIR_INPUT; dir++)
if ((pdo = ec_pdo_mapping_find_pdo(&sc->mapping[dir], pdo_index)))
break;
if (!pdo) {
EC_ERR("Pdo 0x%04X was not found in the mapping of config %u:%u.\n",
pdo_index, sc->alias, sc->position);
return -1;
}
if (pdo->default_config) {
if (sc->master->debug_level)
EC_DBG("Clearing default configuration of Pdo 0x%04X,"
" config %u:%u.\n", pdo->index, sc->alias, sc->position);
pdo->default_config = 0;
ec_pdo_clear_entries(pdo);
}
return ec_pdo_add_entry(pdo, entry_index, entry_subindex,
entry_bit_length) ? 0 : -1;
}
/*****************************************************************************/
int ecrt_slave_config_mapping(ec_slave_config_t *sc, unsigned int n_infos,
const ec_pdo_info_t pdo_infos[])
{
const ec_pdo_info_t *pi;
ec_pdo_mapping_t *pm;
ec_pdo_t *pdo;
const ec_pdo_entry_info_t *ei;
for (i = 0; i < n_infos; i++) {
pi = &pdo_infos[i];
pm = &sc->mapping[pi->dir];
if (pm->default_mapping) {
if (sc->master->debug_level)
EC_DBG("Clearing default mapping for dir %u, config %u:%u.\n",
pi->dir, sc->alias, sc->position);
pm->default_mapping = 0;
ec_pdo_mapping_clear_pdos(pm);
}
if (sc->master->debug_level)
EC_DBG("Adding Pdo 0x%04X to mapping for dir %u, config %u:%u.\n",
pi->index, pi->dir, sc->alias, sc->position);
if (!(pdo = ec_pdo_mapping_add_pdo(pm, pi->dir, pi->index)))
return -1;
if (pi->n_entries && pi->entries) { // configuration provided
if (sc->master->debug_level)
EC_DBG(" Pdo configuration information provided.\n");
for (j = 0; j < pi->n_entries; j++) {
ei = &pi->entries[j];
if (!ec_pdo_add_entry(pdo, ei->index, ei->subindex,
ei->bit_length))
return -1;
}
} else { // use default Pdo configuration
ec_slave_config_load_default_pdo_config(sc, pdo);
}
}
return 0;
}
/*****************************************************************************/
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
int ecrt_slave_config_reg_pdo_entry(
ec_slave_config_t *sc, /**< Slave configuration. */
uint16_t index, /**< Index of Pdo entry to register. */
uint8_t subindex, /**< Subindex of Pdo entry to register. */
ec_domain_t *domain /**< Domain. */
)
{
ec_direction_t dir;
ec_pdo_mapping_t *map;
unsigned int bit_offset, byte_offset;
ec_pdo_t *pdo;
ec_pdo_entry_t *entry;
int ret;
for (dir = EC_DIR_OUTPUT; dir <= EC_DIR_INPUT; dir++) {
map = &sc->mapping[dir];
bit_offset = 0;
list_for_each_entry(pdo, &map->pdos, list) {
list_for_each_entry(entry, &pdo->entries, list) {
if (entry->index != index || entry->subindex != subindex) {
bit_offset += entry->bit_length;
} else {
goto found;
}
}
}
}
EC_ERR("Pdo entry 0x%04X:%u is not mapped in slave config %u:%u.\n",
index, subindex, sc->alias, sc->position);
return -1;
found:
byte_offset = bit_offset / 8;
if ((ret = ec_slave_config_prepare_fmmu(sc, domain, dir)) < 0)
return -2;
return ret + byte_offset;
}
/*****************************************************************************/
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
int ecrt_slave_config_sdo8(ec_slave_config_t *slave, uint16_t index,
uint8_t subindex, uint8_t value)
{
uint8_t data[1];
EC_WRITE_U8(data, value);
return ec_slave_config_sdo(slave, index, subindex, data, 1);
}
/*****************************************************************************/
int ecrt_slave_config_sdo16(ec_slave_config_t *slave, uint16_t index,
uint8_t subindex, uint16_t value)
{
uint8_t data[2];
EC_WRITE_U16(data, value);
return ec_slave_config_sdo(slave, index, subindex, data, 2);
}
/*****************************************************************************/
int ecrt_slave_config_sdo32(ec_slave_config_t *slave, uint16_t index,
uint8_t subindex, uint32_t value)
{
uint8_t data[4];
EC_WRITE_U32(data, value);
return ec_slave_config_sdo(slave, index, subindex, data, 4);
}
/*****************************************************************************/
/** \cond */
EXPORT_SYMBOL(ecrt_slave_config_pdo);
EXPORT_SYMBOL(ecrt_slave_config_pdo_entry);
EXPORT_SYMBOL(ecrt_slave_config_mapping);
EXPORT_SYMBOL(ecrt_slave_config_reg_pdo_entry);
EXPORT_SYMBOL(ecrt_slave_config_sdo8);
EXPORT_SYMBOL(ecrt_slave_config_sdo16);
EXPORT_SYMBOL(ecrt_slave_config_sdo32);
/** \endcond */
/*****************************************************************************/