1 /*
2 * Copyright (c) 2019 Vestas Wind Systems A/S
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/settings/settings.h>
8
9 #include <CANopen.h>
10 #include <CO_Emergency.h>
11 #include <CO_SDO.h>
12
13 #include <canopennode.h>
14
15 #define LOG_LEVEL CONFIG_CANOPEN_LOG_LEVEL
16 #include <zephyr/logging/log.h>
17 LOG_MODULE_REGISTER(canopen_storage);
18
19 /* 's', 'a', 'v', 'e' from LSB to MSB */
20 #define STORE_PARAM_MAGIC 0x65766173UL
21
22 /* 'l', 'o', 'a', 'd' from LSB to MSB */
23 #define RESTORE_PARAM_MAGIC 0x64616F6CUL
24
25 /* Variables for reporting errors through CANopen once the stack is up */
26 static int canopen_storage_rom_error;
27 static int canopen_storage_eeprom_error;
28
canopen_odf_1010(CO_ODF_arg_t * odf_arg)29 static CO_SDO_abortCode_t canopen_odf_1010(CO_ODF_arg_t *odf_arg)
30 {
31 CO_EM_t *em = odf_arg->object;
32 uint32_t value;
33 int err;
34
35 value = CO_getUint32(odf_arg->data);
36
37 if (odf_arg->reading) {
38 return CO_SDO_AB_NONE;
39 }
40
41 /* Preserve old value */
42 memcpy(odf_arg->data, odf_arg->ODdataStorage, sizeof(uint32_t));
43
44 if (odf_arg->subIndex != 1U) {
45 return CO_SDO_AB_NONE;
46 }
47
48 if (value != STORE_PARAM_MAGIC) {
49 return CO_SDO_AB_DATA_TRANSF;
50 }
51
52 err = canopen_storage_save(CANOPEN_STORAGE_ROM);
53 if (err) {
54 LOG_ERR("failed to save object dictionary ROM entries (err %d)",
55 err);
56 CO_errorReport(em, CO_EM_NON_VOLATILE_MEMORY, CO_EMC_HARDWARE,
57 err);
58 return CO_SDO_AB_HW;
59 } else {
60 LOG_DBG("saved object dictionary ROM entries");
61 }
62
63 return CO_SDO_AB_NONE;
64 }
65
canopen_odf_1011(CO_ODF_arg_t * odf_arg)66 static CO_SDO_abortCode_t canopen_odf_1011(CO_ODF_arg_t *odf_arg)
67 {
68 CO_EM_t *em = odf_arg->object;
69 bool failed = false;
70 uint32_t value;
71 int err;
72
73 value = CO_getUint32(odf_arg->data);
74
75 if (odf_arg->reading) {
76 return CO_SDO_AB_NONE;
77 }
78
79 /* Preserve old value */
80 memcpy(odf_arg->data, odf_arg->ODdataStorage, sizeof(uint32_t));
81
82 if (odf_arg->subIndex < 1U) {
83 return CO_SDO_AB_NONE;
84 }
85
86 if (value != RESTORE_PARAM_MAGIC) {
87 return CO_SDO_AB_DATA_TRANSF;
88 }
89
90 err = canopen_storage_erase(CANOPEN_STORAGE_ROM);
91 if (err == -ENOENT) {
92 LOG_DBG("no object dictionary ROM entries to delete");
93 } else if (err) {
94 LOG_ERR("failed to delete object dictionary ROM entries"
95 " (err %d)", err);
96 CO_errorReport(em, CO_EM_NON_VOLATILE_MEMORY, CO_EMC_HARDWARE,
97 err);
98 failed = true;
99 } else {
100 LOG_DBG("deleted object dictionary ROM entries");
101 }
102
103 #ifdef CONFIG_CANOPENNODE_STORAGE_HANDLER_ERASES_EEPROM
104 err = canopen_storage_erase(CANOPEN_STORAGE_EEPROM);
105 if (err == -ENOENT) {
106 LOG_DBG("no object dictionary EEPROM entries to delete");
107 } else if (err) {
108 LOG_ERR("failed to delete object dictionary EEPROM entries"
109 " (err %d)", err);
110 CO_errorReport(em, CO_EM_NON_VOLATILE_MEMORY, CO_EMC_HARDWARE,
111 err);
112 failed = true;
113 } else {
114 LOG_DBG("deleted object dictionary EEPROM entries");
115 }
116 #endif
117
118 if (failed) {
119 return CO_SDO_AB_HW;
120 }
121
122 return CO_SDO_AB_NONE;
123 }
124
canopen_settings_set(const char * key,size_t len_rd,settings_read_cb read_cb,void * cb_arg)125 static int canopen_settings_set(const char *key, size_t len_rd,
126 settings_read_cb read_cb, void *cb_arg)
127 {
128 const char *next;
129 int nlen;
130 ssize_t len;
131
132 nlen = settings_name_next(key, &next);
133
134 if (!strncmp(key, "eeprom", nlen)) {
135 struct sCO_OD_EEPROM eeprom;
136
137 len = read_cb(cb_arg, &eeprom, sizeof(eeprom));
138 if (len < 0) {
139 LOG_ERR("failed to restore object dictionary EEPROM"
140 " entries (err %zu)", len);
141 canopen_storage_eeprom_error = len;
142 } else {
143 if ((eeprom.FirstWord == CO_OD_FIRST_LAST_WORD) &&
144 (eeprom.LastWord == CO_OD_FIRST_LAST_WORD)) {
145 memcpy(&CO_OD_EEPROM, &eeprom,
146 sizeof(CO_OD_EEPROM));
147 LOG_DBG("restored object dictionary EEPROM"
148 " entries");
149 } else {
150 LOG_WRN("object dictionary EEPROM entries"
151 " signature mismatch, skipping"
152 " restore");
153 }
154 }
155
156 return 0;
157 } else if (!strncmp(key, "rom", nlen)) {
158 struct sCO_OD_ROM rom;
159
160 len = read_cb(cb_arg, &rom, sizeof(rom));
161 if (len < 0) {
162 LOG_ERR("failed to restore object dictionary ROM"
163 " entries (err %zu)", len);
164 canopen_storage_rom_error = len;
165 } else {
166 if ((rom.FirstWord == CO_OD_FIRST_LAST_WORD) &&
167 (rom.LastWord == CO_OD_FIRST_LAST_WORD)) {
168 memcpy(&CO_OD_ROM, &rom, sizeof(CO_OD_ROM));
169 LOG_DBG("restored object dictionary ROM"
170 " entries");
171 } else {
172 LOG_WRN("object dictionary ROM entries"
173 " signature mismatch, skipping"
174 " restore");
175 }
176 }
177
178 return 0;
179 }
180
181 return 0;
182 }
183
184 SETTINGS_STATIC_HANDLER_DEFINE(canopen, "canopen", NULL,
185 canopen_settings_set, NULL, NULL);
186
canopen_storage_attach(CO_SDO_t * sdo,CO_EM_t * em)187 void canopen_storage_attach(CO_SDO_t *sdo, CO_EM_t *em)
188 {
189 CO_OD_configure(sdo, OD_H1010_STORE_PARAM_FUNC, canopen_odf_1010,
190 em, 0U, 0U);
191 CO_OD_configure(sdo, OD_H1011_REST_PARAM_FUNC, canopen_odf_1011,
192 em, 0U, 0U);
193
194 if (canopen_storage_eeprom_error) {
195 CO_errorReport(em, CO_EM_NON_VOLATILE_MEMORY, CO_EMC_HARDWARE,
196 canopen_storage_eeprom_error);
197 }
198
199 if (canopen_storage_rom_error) {
200 CO_errorReport(em, CO_EM_NON_VOLATILE_MEMORY, CO_EMC_HARDWARE,
201 canopen_storage_rom_error);
202 }
203 }
204
canopen_storage_save(enum canopen_storage storage)205 int canopen_storage_save(enum canopen_storage storage)
206 {
207 int ret = 0;
208
209 if (storage == CANOPEN_STORAGE_ROM) {
210 ret = settings_save_one("canopen/rom", &CO_OD_ROM,
211 sizeof(CO_OD_ROM));
212 } else if (storage == CANOPEN_STORAGE_EEPROM) {
213 ret = settings_save_one("canopen/eeprom", &CO_OD_EEPROM,
214 sizeof(CO_OD_EEPROM));
215 }
216
217 return ret;
218 }
219
canopen_storage_erase(enum canopen_storage storage)220 int canopen_storage_erase(enum canopen_storage storage)
221 {
222 int ret = 0;
223
224 if (storage == CANOPEN_STORAGE_ROM) {
225 ret = settings_delete("canopen/rom");
226 } else if (storage == CANOPEN_STORAGE_EEPROM) {
227 ret = settings_delete("canopen/eeprom");
228 }
229
230 return ret;
231 }
232