blob: 7085eaaa8a530564c5793be5c870003c6e3f4201 [file] [log] [blame]
Shad Ansari2f7f9be2017-06-07 13:34:53 -07001/*
2<:copyright-BRCM:2014:DUAL/GPL:standard
3
4 Copyright (c) 2014 Broadcom Corporation
5 All Rights Reserved
6
7Unless you and Broadcom execute a separate written software license
8agreement governing use of this software, this software is licensed
9to you under the terms of the GNU General Public License version 2
10(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php,
11with the following added to such license:
12
13 As a special exception, the copyright holders of this software give
14 you permission to link this software with independent modules, and
15 to copy and distribute the resulting executable under terms of your
16 choice, provided that you also meet, for each linked independent
17 module, the terms and conditions of the license of that module.
18 An independent module is a module which is not derived from this
19 software. The special exception does not apply to any modifications
20 of the software.
21
22Not withstanding the above, under no circumstances may you combine
23this software in any way with any other Broadcom software provided
24under a license other than the GPL, without Broadcom's express prior
25written consent.
26
27:>
28 */
29
30#include <bcmtr_interface.h>
31#include <bcmcli.h>
32#include <libgen.h>
33#include "bcmolt_api.h"
34#include "bcm_dev_log.h"
35#include "bcmolt_image_transfer.h"
36#include "bcmolt_image_transfer_user.h"
37
38static bcmuser_mftp_block_size_get mftp_block_size_get;
39static bcmuser_mftp_crc_get mftp_crc_get_cb;
40static bcmuser_mftp_image_read mftp_read_data_cb;
41static bcmuser_mftp_notification_rx mftp_notify_user_cb;
42static mftp_context mftp_context_db[BCMTR_MAX_OLTS];
43
44static bcmos_errno mftp_ind_rx_cb_register(bcmolt_devid device);
45static bcmos_errno mftp_ind_rx_cb_unregister(bcmolt_devid device);
46
47/* ========================================================================== */
48static bcmolt_image_transfer_status mftp_err_to_status(bcmos_errno err)
49{
50 bcmolt_image_transfer_status status;
51 switch (err)
52 {
53 case BCM_ERR_OK:
54 status = BCMOLT_IMAGE_TRANSFER_STATUS_SUCCESS;
55 break;
56 case BCM_ERR_NOMEM:
57 status = BCMOLT_IMAGE_TRANSFER_STATUS_MEMORY_ALLOCATION_FAILURE;
58 break;
59 case BCM_ERR_INCOMPLETE_TERMINATION:
60 status = BCMOLT_IMAGE_TRANSFER_STATUS_PREMATURE_TERMINATION;
61 break;
62 case BCM_ERR_CHECKSUM:
63 status = BCMOLT_IMAGE_TRANSFER_STATUS_CRC_ERROR;
64 break;
65 case BCM_ERR_OVERFLOW:
66 status = BCMOLT_IMAGE_TRANSFER_STATUS_INTERNAL_ERROR;
67 break;
68 case BCM_ERR_OUT_OF_SYNC:
69 status = BCMOLT_IMAGE_TRANSFER_STATUS_BLOCK_OUT_OF_SYNC;
70 break;
71 case BCM_ERR_STATE:
72 status = BCMOLT_IMAGE_TRANSFER_STATUS_INVALID_STATE;
73 break;
74 case BCM_ERR_IMAGE_TYPE:
75 status = BCMOLT_IMAGE_TRANSFER_STATUS_UNSUPPORTED_FILE_TYPE;
76 break;
77 default:
78 status = BCMOLT_IMAGE_TRANSFER_STATUS_INTERNAL_ERROR;
79 break;
80 }
81 return status;
82}
83
84/* ========================================================================== */
85const char *bcmolt_user_mftp_status_to_str(bcmolt_image_transfer_status status)
86{
87 static const char *mftp_status_to_str_table[BCMOLT_IMAGE_TRANSFER_STATUS__NUM_OF] =
88 {
89 [BCMOLT_IMAGE_TRANSFER_STATUS_SUCCESS] = "Succeeded",
90 [BCMOLT_IMAGE_TRANSFER_STATUS_MEMORY_ALLOCATION_FAILURE] = "ERROR: Memory allocation",
91 [BCMOLT_IMAGE_TRANSFER_STATUS_UNSUPPORTED_FILE_TYPE] = "ERROR: Invalid image type",
92 [BCMOLT_IMAGE_TRANSFER_STATUS_CRC_ERROR] = "ERROR: Checksum",
93 [BCMOLT_IMAGE_TRANSFER_STATUS_BLOCK_OUT_OF_SYNC] = "ERROR: Block out-of-sync",
94 [BCMOLT_IMAGE_TRANSFER_STATUS_INTERNAL_ERROR] = "ERROR: Buffer overflow",
95 [BCMOLT_IMAGE_TRANSFER_STATUS_INVALID_STATE] = "ERROR: Invalid state",
96 [BCMOLT_IMAGE_TRANSFER_STATUS_PREMATURE_TERMINATION] = "ERROR: Premature termination",
97 [BCMOLT_IMAGE_TRANSFER_STATUS_ACK_TIMEOUT] = "ERROR: ACK timeout"
98 };
99 return (status >= BCMOLT_IMAGE_TRANSFER_STATUS__NUM_OF) ? "<unknown>" : mftp_status_to_str_table[status];
100}
101
102/* ========================================================================== */
103static inline mftp_context *mftp_context_get(bcmolt_devid device)
104{
105 return &mftp_context_db[device];
106}
107
108/* ========================================================================== */
109/** convert device ID to module ID */
110static inline bcmos_module_id mftp_device_id_to_module_id(bcmolt_devid device)
111{
112 return (BCMOS_MODULE_ID_USER_APPL_IMAGE_TRANSFER0 + device);
113}
114
115/* ========================================================================== */
116/** examines the ACK response. */
117static bcmolt_image_transfer_status mftp_check_params(mftp_context *context,
118 bcmolt_device_image_transfer_complete_data *ack_data)
119{
120 bcmolt_image_transfer_status status = BCMOLT_IMAGE_TRANSFER_STATUS_SUCCESS;
121
122 if (context->block_num != ack_data->block_number)
123 {
124 BCM_LOG(DEBUG, context->log_id, "ACK block # mismatch: exp=%u ack=%u\n", context->block_num, ack_data->block_number);
125 status = BCMOLT_IMAGE_TRANSFER_STATUS_BLOCK_OUT_OF_SYNC;
126 }
127 else if (context->image_type != ack_data->image_type)
128 {
129 BCM_LOG(DEBUG, context->log_id, "ACK image type mismatch: exp=%u ack=%u\n", context->image_type, ack_data->image_type);
130 status = BCMOLT_IMAGE_TRANSFER_STATUS_UNSUPPORTED_FILE_TYPE;
131 }
132 else if (ack_data->status != BCMOLT_IMAGE_TRANSFER_STATUS_SUCCESS)
133 {
134 BCM_LOG(DEBUG, context->log_id, "OLT returned an error: %s\n", bcmolt_user_mftp_status_to_str(ack_data->status));
135 status = ack_data->status;
136 }
137 return status;
138}
139
140/* ========================================================================== */
141static void mftp_terminate(bcmolt_devid device, bcmolt_device_image_type image_type,
142 uint32_t block_num, bcmolt_image_transfer_status status)
143{
144 mftp_context *context = mftp_context_get(device);
145 bcmos_errno rc = mftp_ind_rx_cb_unregister(device);
146
147 if (rc != BCM_ERR_OK)
148 {
149 BCM_LOG(ERROR, context->log_id, "Failed to un-register IND callback\n");
150 }
151 mftp_notify_user_cb(device, image_type, block_num, status);
152 context->status = MFTP_STATUS_DISABLED;
153}
154
155/* ========================================================================== */
156/** Sends the image transfer start operation to the OLT. */
157static bcmos_errno mftp_send_wrq(bcmolt_devid device, bcmolt_device_image_type image_type,
158 uint32_t image_size, uint32_t crc32, bcmolt_str_64 *image_name)
159{
160 bcmolt_device_image_transfer_start oper;
161 bcmolt_device_key key = { };
162
163 BCMOLT_OPER_INIT(&oper, device, image_transfer_start, key);
164 BCMOLT_OPER_PROP_SET(&oper, device, image_transfer_start, image_type, image_type);
165 BCMOLT_OPER_PROP_SET(&oper, device, image_transfer_start, image_size, image_size);
166 BCMOLT_OPER_PROP_SET(&oper, device, image_transfer_start, crc32, crc32);
167 BCMOLT_OPER_PROP_SET(&oper, device, image_transfer_start, image_name, *image_name);
168 return bcmolt_oper_submit(device, &oper.hdr);
169}
170
171/* ========================================================================== */
172/** Sends a data block to the OLT. */
173static bcmos_errno mftp_send_data(bcmolt_devid device, uint8_t *buf, uint32_t buf_size,
174 uint32_t block_number, bcmos_bool more_data)
175{
176 bcmolt_device_key key = {.reserved = 0};
177 bcmolt_device_image_transfer_data oper = {};
178 bcmolt_u8_list_u16_hex data;
179 data.len = buf_size;
180 data.val = buf;
181
182 /* Builds a transport message for BCMOLT_DEVICE_OPER_ID_IMAGE_TRANSFER_DATA */
183 BCMOLT_OPER_INIT(&oper, device, image_transfer_data, key);
184 oper.hdr.hdr.type = BCMOLT_MSG_TYPE_SET;
185 BCMOLT_OPER_PROP_SET(&oper, device, image_transfer_data, block_number, block_number);
186 BCMOLT_OPER_PROP_SET(&oper, device, image_transfer_data, more_data, more_data);
187 BCMOLT_OPER_PROP_SET(&oper, device, image_transfer_data, data, data);
188 return bcmolt_oper_submit(device, &oper.hdr);
189}
190
191/* ========================================================================== */
192/* handler for the OPERATION device.image_transfer_start. */
193bcmos_errno bcmolt_user_mftp_start(bcmolt_devid device, bcmolt_device_image_type image_type)
194{
195 mftp_context *context = mftp_context_get(device);
196 uint32_t image_size;
197 uint32_t crc32;
198 const char *path_name;
199 char file_name[MFTP_MAX_PATH_NAME_LEN];
200 char *base_name;
201 bcmolt_str_64 image_name;
202 bcmos_errno rc = BCM_ERR_OK;
203
204 if (context->status != MFTP_STATUS_DISABLED)
205 {
206 return BCM_ERR_IN_PROGRESS;
207 }
208
209 rc = bcmuser_image_transfer_file_size_get(device, image_type, &image_size);
210 if (rc != BCM_ERR_OK)
211 {
212 BCM_LOG(ERROR, context->log_id, "Failed to get the image size\n");
213 return rc;
214 }
215
216 path_name = bcmuser_device_image_name_get(image_type);
217 if (path_name == NULL)
218 {
219 BCM_LOG(ERROR, context->log_id, "File not found\n");
220 return BCM_ERR_PARM;
221 }
222 if (strlen(path_name) >= sizeof(file_name))
223 {
224 BCM_LOG(ERROR, context->log_id, "Path name too long\n");
225 return BCM_ERR_PARM;
226 }
227 (void)strcpy(file_name, path_name); /* to make lint happy. */
228
229 base_name = basename(file_name);
230 if (strlen(base_name) >= sizeof(image_name))
231 {
232 BCM_LOG(ERROR, context->log_id, "File name too long\n");
233 return BCM_ERR_PARM;
234 }
235 (void)strcpy(image_name.str, base_name);
236
237 context->status = MFTP_STATUS_WAITING_ACK;
238 context->block_num = 0;
239 context->image_type = image_type;
240 crc32 = mftp_crc_get_cb(device, image_type);
241 BCM_LOG(DEBUG, context->log_id, "Image size=%u crc=0x%x\n", image_size, crc32);
242
243 rc = mftp_ind_rx_cb_register(device);
244 if (rc != BCM_ERR_OK)
245 {
246 BCM_LOG(ERROR, context->log_id, "Failed to register IND callback\n");
247 BUG();
248 }
249
250 rc = mftp_send_wrq(device, image_type, image_size, crc32, &image_name);
251 if (BCM_ERR_OK != rc)
252 {
253 BCM_LOG(DEBUG, context->log_id, "WRQ OPER failed. %s\n", bcmos_strerror(rc));
254 (void)mftp_ind_rx_cb_unregister(device);
255 context->status = MFTP_STATUS_DISABLED;
256 }
257 else
258 {
259 bcmos_timer_start(&context->timer, MFTP_ACK_TIMEOUT_US);
260 }
261 return rc;
262}
263
264/* ========================================================================== */
265static void mftp_process_last_ack(bcmolt_devid device, bcmolt_device_image_transfer_complete_data *ack_data)
266{
267 bcmolt_image_transfer_status status;
268 mftp_context *context = mftp_context_get(device);
269
270 status = mftp_check_params(context, ack_data);
271 mftp_terminate(device, ack_data->image_type, ack_data->block_number, status);
272}
273
274/* ========================================================================== */
275/* reads a data block using customer-provided callback and sends it to the embedded. */
276static void mftp_process_ack(bcmolt_devid device, bcmolt_device_image_transfer_complete_data *ack_data)
277{
278 uint32_t offset = 0; /* file-position*/
279 uint32_t bytes_read = 0;
280 bcmos_bool more_data;
281 bcmos_errno rc = BCM_ERR_OK;
282 bcmolt_image_transfer_status status;
283 mftp_context *context = mftp_context_get(device);
284
285 status = mftp_check_params(context, ack_data);
286 if (status != BCMOLT_IMAGE_TRANSFER_STATUS_SUCCESS)
287 {
288 mftp_terminate(device, context->image_type, ack_data->block_number, status);
289 return;
290 }
291
292 offset = (context->block_num * context->block_size);
293 context->block_num++;
294
295 /* CALL CUSTOMER-PROVIDED CALLBACK */
296 bytes_read = mftp_read_data_cb(device, context->image_type, offset,
297 context->buf, context->block_size); /* bcmuser_mftp_read_data() */
298
299 more_data = !(bytes_read < context->block_size);
300
301 rc = mftp_send_data(device, context->buf, bytes_read, context->block_num, more_data);
302 if (BCM_ERR_OK != rc)
303 {
304 BCM_LOG(DEBUG, context->log_id, "DATA OPER failed. Block %u. %s\n", context->block_num, bcmos_strerror(rc));
305 mftp_terminate(device, ack_data->image_type, ack_data->block_number, mftp_err_to_status(rc));
306 return;
307 }
308
309 BCM_LOG(DEBUG, context->log_id, "Sent block#=%u buf_size=%u more=%u\n", context->block_num, bytes_read, more_data);
310
311 context->status = (more_data) ? MFTP_STATUS_WAITING_ACK : MFTP_STATUS_WAITING_LAST_ACK;
312
313 bcmos_timer_start(&context->timer, MFTP_ACK_TIMEOUT_US);
314}
315
316/* ========================================================================== */
317/* ACK indication callback */
318static void mftp_process_ind(bcmolt_devid olt, bcmolt_msg *msg)
319{
320 mftp_context *context = mftp_context_get(olt);
321 bcmolt_device_image_transfer_complete *ack;
322 bcmolt_device_image_transfer_complete_data *ack_data;
323
324 ack = (bcmolt_device_image_transfer_complete *)msg;
325 ack_data = &ack->data;
326
327 BCM_LOG(DEBUG, context->log_id, "## ACK: obj=%u, group=%u, image=%u, block=%u, status=%u\n",
328 msg->obj_type, msg->group, ack_data->image_type, ack_data->block_number, ack_data->status);
329
330 switch (context->status)
331 {
332 case MFTP_STATUS_WAITING_ACK:
333 bcmos_timer_stop(&context->timer);
334 mftp_process_ack(olt, ack_data);
335 break;
336 case MFTP_STATUS_WAITING_LAST_ACK:
337 bcmos_timer_stop(&context->timer);
338 mftp_process_last_ack(olt, ack_data);
339 break;
340 default:
341 /* This could happen when the START operation fails. */
342 BCM_LOG(DEBUG, context->log_id, "Unexpected ACK\n");
343 break;
344 }
345
346 bcmolt_msg_free(msg);
347}
348
349/* ========================================================================== */
350static bcmos_errno mftp_ind_rx_cb_register(bcmolt_devid device)
351{
352 bcmtr_handler_parm tparm = {
353 .group = BCMOLT_MGT_GROUP_AUTO,
354 .object = BCMOLT_OBJ_ID_DEVICE,
355 .subgroup = BCMOLT_DEVICE_AUTO_ID_IMAGE_TRANSFER_COMPLETE,
356 .app_cb = mftp_process_ind,
357 .flags = BCMOLT_AUTO_FLAGS_DISPATCH,
358 };
359 mftp_context *context = mftp_context_get(device);
360 uint8_t inst;
361 bcmos_errno rc = BCM_ERR_OK;
362
363 tparm.module = context->module_id;
364
365 for (inst = 0; (inst < BCMTR_MAX_INSTANCES) && (rc == BCM_ERR_OK); inst++)
366 {
367 tparm.instance = inst;
368 rc = bcmtr_msg_handler_unregister(device, &tparm);
369 rc = (rc == BCM_ERR_OK) ? bcmtr_msg_handler_register(device, &tparm) : rc;
370 }
371 return rc;
372}
373
374/* ========================================================================== */
375static bcmos_errno mftp_ind_rx_cb_unregister(bcmolt_devid device)
376{
377 bcmtr_handler_parm tparm = {
378 .group = BCMOLT_MGT_GROUP_AUTO,
379 .object = BCMOLT_OBJ_ID_DEVICE,
380 .subgroup = BCMOLT_DEVICE_AUTO_ID_IMAGE_TRANSFER_COMPLETE,
381 };
382 uint8_t inst;
383 bcmos_errno rc = BCM_ERR_OK;
384
385 for (inst = 0; (inst < BCMTR_MAX_INSTANCES) && (rc == BCM_ERR_OK); inst++)
386 {
387 tparm.instance = inst;
388 rc = bcmtr_msg_handler_unregister(device, &tparm);
389 }
390 return rc;
391}
392
393/* ========================================================================== */
394/* ACK response timeout handler */
395static bcmos_timer_rc mftp_ack_timeout_handler(bcmos_timer *timer, long data)
396{
397 bcmolt_devid device = (bcmolt_devid)data;
398 mftp_context *context = mftp_context_get(device);
399
400 if ((context->status == MFTP_STATUS_WAITING_ACK) || (context->status == MFTP_STATUS_WAITING_LAST_ACK))
401 {
402 mftp_terminate(device, context->image_type, context->block_num, BCMOLT_IMAGE_TRANSFER_STATUS_ACK_TIMEOUT);
403 }
404 else
405 {
406 BCM_LOG(INFO, context->log_id, "timer in irrelevant state\n");
407 }
408 return BCMOS_TIMER_STOP;
409}
410
411/* ========================================================================== */
412static bcmos_errno mftp_init_timers(bcmolt_devid device)
413{
414 mftp_context *context = mftp_context_get(device);
415 bcmos_timer_parm timer_params = {};
416 bcmos_errno rc = BCM_ERR_OK;
417
418 /* If timer was already created, then we don't need to re-create it */
419 if ((context->timer.flags & BCMOS_TIMER_FLAG_VALID) != 0)
420 return BCM_ERR_OK;
421
422 timer_params.name = context->name;
423 timer_params.owner = context->module_id;
424 timer_params.periodic = BCMOS_FALSE;
425 timer_params.handler = mftp_ack_timeout_handler;
426 timer_params.data = (long)device;
427
428 rc = bcmos_timer_create(&context->timer, &timer_params);
429 if (rc != BCM_ERR_OK)
430 {
431 BCM_LOG(ERROR, context->log_id, "Timer creation failed. %s\n", bcmos_strerror(rc));
432 }
433 return rc;
434}
435
436/* ========================================================================== */
437static bcmos_errno mftp_init_tasks(bcmolt_devid device)
438{
439 mftp_context *context = mftp_context_get(device);
440 bcmos_task_parm task_params = {};
441 bcmos_errno rc = BCM_ERR_OK;
442
443 task_params.name = context->name;
444 task_params.priority = TASK_PRIORITY_USER_APPL_IMAGE_TRANSFER;
445 task_params.core = BCMOS_CPU_CORE_ANY; /* No CPU affinity */
446 task_params.init_handler = NULL;
447 task_params.data = (long)device;
448
449 rc = bcmos_task_create(&context->task, &task_params);
450 if (rc != BCM_ERR_OK)
451 {
452 BCM_LOG(ERROR, context->log_id, "Task creation failed. %s \n", bcmos_strerror(rc));
453 }
454 return rc;
455}
456
457/* ========================================================================== */
458static bcmos_errno mftp_init_modules(bcmolt_devid device)
459{
460 mftp_context *context = mftp_context_get(device);
461 bcmos_module_parm module_params = {};
462 bcmos_errno rc = BCM_ERR_OK;
463
464 context->module_id = mftp_device_id_to_module_id(device);
465 module_params.qparm.name = context->name;
466 module_params.qparm.size = BCMOS_MSG_POOL_DEFAULT_SIZE;
467 module_params.init = NULL;
468
469 rc = bcmos_module_create(context->module_id, &context->task, &module_params);
470 if (rc != BCM_ERR_OK)
471 {
472 BCM_LOG(ERROR, context->log_id, "Module creation failed. %s \n", bcmos_strerror(rc));
473 }
474 return rc;
475}
476
477/* ========================================================================== */
478/* Initializes the MFTP parameters. */
479void bcmolt_user_mftp_init(void)
480{
481 mftp_context *context;
482 bcmolt_devid device;
483 bcmos_errno rc = BCM_ERR_OK;
484
485 /* The customer should provide these callback functions. */
486 mftp_read_data_cb = bcmuser_image_transfer_read_data;
487 mftp_notify_user_cb = bcmuser_image_transfer_notification_rx;
488 mftp_block_size_get = bcmuser_image_transfer_block_size_get;
489 mftp_crc_get_cb = bcmuser_image_transfer_crc_get;
490
491 /* initialize some parameters, i.e. the .status and .timer.
492 other parameters are (re)built upon START, so no need to init. */
493 for (device = 0; (device < BCMTR_MAX_OLTS) && (rc == BCM_ERR_OK); device++)
494 {
495 context = mftp_context_get(device);
496 context->status = MFTP_STATUS_DISABLED;
497 context->block_size = mftp_block_size_get();
498 context->buf = bcmos_calloc(context->block_size);
499 rc = (context->buf == NULL) ? BCM_ERR_NOMEM : rc;
500 snprintf(context->name, sizeof(context->name), "mftp%u", device);
501 rc = (rc == BCM_ERR_OK) ? mftp_init_tasks(device) : rc;
502 rc = (rc == BCM_ERR_OK) ? mftp_init_modules(device) : rc;
503 rc = (rc == BCM_ERR_OK) ? mftp_init_timers(device) : rc;
504 context->log_id = bcm_dev_log_id_register(context->name, DEV_LOG_LEVEL_INFO, DEV_LOG_ID_TYPE_BOTH);
505 }
506}
507