1 /*
2  * Copyright (c) 2020 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 #include <string.h>
7 #include <zephyr/bluetooth/mesh.h>
8 #include <common/bt_str.h>
9 #include "mesh.h"
10 #include "blob.h"
11 #include "net.h"
12 #include "transport.h"
13 
14 #define LOG_LEVEL CONFIG_BT_MESH_MODEL_LOG_LEVEL
15 #include <zephyr/logging/log.h>
16 LOG_MODULE_REGISTER(bt_mesh_blob_cli);
17 
18 #define TARGETS_FOR_EACH(cli, target)                                          \
19 	SYS_SLIST_FOR_EACH_CONTAINER((sys_slist_t *)&(cli)->inputs->targets,   \
20 				     target, n)
21 
22 #define CHUNK_SIZE_MAX BLOB_CHUNK_SIZE_MAX(BT_MESH_TX_SDU_MAX)
23 
24 /* The Maximum BLOB Poll Interval - T_MBPI */
25 #define BLOB_POLL_TIME_MAX_SECS 30
26 
27 #define CLIENT_TIMEOUT_MSEC(cli) (10 * MSEC_PER_SEC * (cli->inputs->timeout_base + 2) + \
28 				  100 * cli->inputs->ttl)
29 #define BLOCK_REPORT_TIME_MSEC ((BLOB_POLL_TIME_MAX_SECS * 2 + 7) * 1000)
30 
31 /* BLOB Client is running Send Data State Machine from section 6.2.4.2. */
32 #define SENDING_CHUNKS_IN_PULL_MODE(cli) ((cli)->state == BT_MESH_BLOB_CLI_STATE_BLOCK_SEND && \
33 					  (cli)->xfer->mode == BT_MESH_BLOB_XFER_MODE_PULL)
34 #define UNICAST_MODE(cli) ((cli)->inputs->group == BT_MESH_ADDR_UNASSIGNED || \
35 			   (cli)->tx.ctx.force_unicast)
36 
37 BUILD_ASSERT((BLOB_XFER_STATUS_MSG_MAXLEN + BT_MESH_MODEL_OP_LEN(BT_MESH_BLOB_OP_XFER_STATUS) +
38 	      BT_MESH_MIC_SHORT) <= BT_MESH_RX_SDU_MAX,
39 	     "The BLOB Transfer Status message does not fit into the maximum incoming SDU size.");
40 
41 BUILD_ASSERT((BLOB_BLOCK_REPORT_STATUS_MSG_MAXLEN +
42 	      BT_MESH_MODEL_OP_LEN(BT_MESH_BLOB_OP_BLOCK_REPORT) + BT_MESH_MIC_SHORT)
43 	     <= BT_MESH_RX_SDU_MAX,
44 	     "The BLOB Partial Block Report message does not fit into the maximum incoming SDU "
45 	     "size.");
46 
47 BUILD_ASSERT((BLOB_BLOCK_STATUS_MSG_MAXLEN + BT_MESH_MODEL_OP_LEN(BT_MESH_BLOB_OP_BLOCK_STATUS) +
48 	      BT_MESH_MIC_SHORT) <= BT_MESH_RX_SDU_MAX,
49 	     "The BLOB Block Status message does not fit into the maximum incoming SDU size.");
50 
51 
52 struct block_status {
53 	enum bt_mesh_blob_status status;
54 	enum bt_mesh_blob_chunks_missing missing;
55 	struct bt_mesh_blob_block block;
56 };
57 
58 static struct bt_mesh_blob_target *next_target(struct bt_mesh_blob_cli *cli,
59 					       struct bt_mesh_blob_target **current);
60 static void transfer_cancel(struct bt_mesh_blob_cli *cli);
61 
start_retry_timer(struct bt_mesh_blob_cli * cli)62 static void start_retry_timer(struct bt_mesh_blob_cli *cli)
63 {
64 	k_timeout_t next_timeout;
65 
66 	if (SENDING_CHUNKS_IN_PULL_MODE(cli)) {
67 		int64_t next_timeout_ms = cli->tx.cli_timestamp;
68 		struct bt_mesh_blob_target *target = NULL;
69 
70 		TARGETS_FOR_EACH(cli, target) {
71 			if (!target->procedure_complete &&
72 			    target->status == BT_MESH_BLOB_SUCCESS &&
73 			    target->pull->block_report_timestamp < next_timeout_ms) {
74 				next_timeout_ms = target->pull->block_report_timestamp;
75 			}
76 		}
77 
78 		/* cli_timestamp and block_report_timestamp represent absolute time, while
79 		 * k_work_* functions use relative time.
80 		 */
81 		next_timeout_ms -= k_uptime_get();
82 		next_timeout = next_timeout_ms <= 0 ? K_NO_WAIT : K_MSEC(next_timeout_ms);
83 	} else {
84 		next_timeout = K_MSEC(CLIENT_TIMEOUT_MSEC(cli) /
85 				      CONFIG_BT_MESH_BLOB_CLI_BLOCK_RETRIES);
86 	}
87 
88 	(void)k_work_reschedule(&cli->tx.retry, next_timeout);
89 }
90 
cli_state_reset(struct bt_mesh_blob_cli * cli)91 static void cli_state_reset(struct bt_mesh_blob_cli *cli)
92 {
93 	k_work_cancel_delayable(&cli->tx.retry);
94 	cli->xfer = NULL;
95 	cli->state = BT_MESH_BLOB_CLI_STATE_NONE;
96 	cli->tx.ctx.is_inited = 0;
97 	cli->tx.cli_timestamp = 0ll;
98 	cli->tx.sending = 0;
99 }
100 
target_get(struct bt_mesh_blob_cli * cli,uint16_t addr)101 static struct bt_mesh_blob_target *target_get(struct bt_mesh_blob_cli *cli,
102 					      uint16_t addr)
103 {
104 	struct bt_mesh_blob_target *target;
105 
106 	TARGETS_FOR_EACH(cli, target) {
107 		if (target->addr == addr) {
108 			return target;
109 		}
110 	}
111 
112 	LOG_ERR("Unknown target 0x%04x", addr);
113 	return NULL;
114 }
115 
target_drop(struct bt_mesh_blob_cli * cli,struct bt_mesh_blob_target * target,enum bt_mesh_blob_status reason)116 static void target_drop(struct bt_mesh_blob_cli *cli,
117 			struct bt_mesh_blob_target *target,
118 			enum bt_mesh_blob_status reason)
119 {
120 	LOG_WRN("Dropping 0x%04x: %u", target->addr, reason);
121 
122 	target->status = reason;
123 	if (cli->cb && cli->cb->lost_target) {
124 		cli->cb->lost_target(cli, target, reason);
125 	}
126 }
127 
targets_reset(struct bt_mesh_blob_cli * cli)128 static uint32_t targets_reset(struct bt_mesh_blob_cli *cli)
129 {
130 	struct bt_mesh_blob_target *target;
131 	uint32_t count = 0;
132 
133 	TARGETS_FOR_EACH(cli, target) {
134 		if (target->status == BT_MESH_BLOB_SUCCESS) {
135 			target->acked = 0U;
136 			count++;
137 		}
138 	}
139 
140 	return count;
141 }
142 
targets_active(struct bt_mesh_blob_cli * cli)143 static bool targets_active(struct bt_mesh_blob_cli *cli)
144 {
145 	struct bt_mesh_blob_target *target;
146 
147 	TARGETS_FOR_EACH(cli, target) {
148 		if (target->status == BT_MESH_BLOB_SUCCESS) {
149 			return true;
150 		}
151 	}
152 
153 	return false;
154 }
155 
targets_timedout(struct bt_mesh_blob_cli * cli)156 static bool targets_timedout(struct bt_mesh_blob_cli *cli)
157 {
158 	struct bt_mesh_blob_target *target;
159 
160 	TARGETS_FOR_EACH(cli, target) {
161 		if (!!target->timedout) {
162 			return true;
163 		}
164 	}
165 
166 	return false;
167 }
168 
io_open(struct bt_mesh_blob_cli * cli)169 static int io_open(struct bt_mesh_blob_cli *cli)
170 {
171 	if (!cli->io->open) {
172 		return 0;
173 	}
174 
175 	return cli->io->open(cli->io, cli->xfer, BT_MESH_BLOB_READ);
176 }
177 
io_close(struct bt_mesh_blob_cli * cli)178 static void io_close(struct bt_mesh_blob_cli *cli)
179 {
180 	if (!cli->io->close) {
181 		return;
182 	}
183 
184 	cli->io->close(cli->io, cli->xfer);
185 }
186 
next_missing_chunk(struct bt_mesh_blob_cli * cli,const uint8_t * missing_chunks,uint16_t idx)187 static uint16_t next_missing_chunk(struct bt_mesh_blob_cli *cli,
188 				   const uint8_t *missing_chunks,
189 				   uint16_t idx)
190 {
191 	do {
192 		if (blob_chunk_missing_get(missing_chunks, idx)) {
193 			break;
194 		}
195 	} while (++idx < cli->block.chunk_count);
196 
197 	return idx;
198 }
199 
200 /* Used in Pull mode to collect all missing chunks from each target in cli->block.missing. */
update_missing_chunks(struct bt_mesh_blob_cli * cli)201 static void update_missing_chunks(struct bt_mesh_blob_cli *cli)
202 {
203 	struct bt_mesh_blob_target *target;
204 
205 	memset(cli->block.missing, 0, sizeof(cli->block.missing));
206 
207 	TARGETS_FOR_EACH(cli, target) {
208 		if (target->procedure_complete || target->timedout) {
209 			continue;
210 		}
211 
212 		for (size_t idx = 0; idx < cli->block.chunk_count; idx++) {
213 			bool missing = blob_chunk_missing_get(cli->block.missing, idx) ||
214 				       blob_chunk_missing_get(target->pull->missing, idx);
215 			blob_chunk_missing_set(cli->block.missing, idx, missing);
216 		}
217 	}
218 }
219 
chunk_size(const struct bt_mesh_blob_xfer * xfer,const struct bt_mesh_blob_block * block,uint16_t chunk_idx)220 static inline size_t chunk_size(const struct bt_mesh_blob_xfer *xfer,
221 				const struct bt_mesh_blob_block *block,
222 				uint16_t chunk_idx)
223 {
224 	if ((chunk_idx == block->chunk_count - 1) &&
225 	    (block->size % xfer->chunk_size)) {
226 		return block->size % xfer->chunk_size;
227 	}
228 
229 	return xfer->chunk_size;
230 }
231 
chunk_idx_decode(struct net_buf_simple * buf)232 static int chunk_idx_decode(struct net_buf_simple *buf)
233 {
234 	uint16_t data;
235 	uint8_t byte;
236 
237 	if (buf->len == 0) {
238 		return -EINVAL;
239 	}
240 
241 	byte = net_buf_simple_pull_u8(buf);
242 
243 	/* utf-8 decoding */
244 	if ((byte & 0xf0) == 0xe0) { /* 0x800 - 0xffff */
245 		if (buf->len < 2) {
246 			return -EINVAL;
247 		}
248 
249 		data = (byte & 0x0f) << 12;
250 		data |= (net_buf_simple_pull_u8(buf) & 0x3f) << 6;
251 		data |= (net_buf_simple_pull_u8(buf) & 0x3f);
252 	} else if ((byte & 0xe0) == 0xc0) { /* 0x80 - 0x7ff */
253 		if (buf->len < 1) {
254 			return -EINVAL;
255 		}
256 
257 		data = (byte & 0x1f) << 6;
258 		data |= (net_buf_simple_pull_u8(buf) & 0x3f);
259 	} else { /* 0x00 - 0x7f */
260 		data = byte & 0x7f;
261 	}
262 
263 	return data;
264 }
265 
block_set(struct bt_mesh_blob_cli * cli,uint16_t block_idx)266 static void block_set(struct bt_mesh_blob_cli *cli, uint16_t block_idx)
267 {
268 	cli->block.number = block_idx;
269 	cli->block.offset = block_idx * (1UL << cli->xfer->block_size_log);
270 	cli->block.size = blob_block_size(cli->xfer->size, cli->xfer->block_size_log,
271 					  block_idx);
272 	cli->block.chunk_count =
273 		DIV_ROUND_UP(cli->block.size, cli->xfer->chunk_size);
274 
275 	if (cli->xfer->mode == BT_MESH_BLOB_XFER_MODE_PUSH) {
276 		blob_chunk_missing_set_all(&cli->block);
277 	} else {
278 		struct bt_mesh_blob_target *target;
279 
280 		/* In pull mode, the server will tell us which blocks are missing. */
281 		memset(cli->block.missing, 0, sizeof(cli->block.missing));
282 
283 		TARGETS_FOR_EACH(cli, target) {
284 			memset(target->pull->missing, 0, sizeof(target->pull->missing));
285 		}
286 	}
287 
288 	LOG_DBG("%u size: %u chunks: %u", block_idx, cli->block.size,
289 		cli->block.chunk_count);
290 }
291 
suspend(struct bt_mesh_blob_cli * cli)292 static void suspend(struct bt_mesh_blob_cli *cli)
293 {
294 	cli->state = BT_MESH_BLOB_CLI_STATE_SUSPENDED;
295 
296 	if (cli->cb && cli->cb->suspended) {
297 		cli->cb->suspended(cli);
298 	}
299 }
300 
end(struct bt_mesh_blob_cli * cli,bool success)301 static void end(struct bt_mesh_blob_cli *cli, bool success)
302 {
303 	const struct bt_mesh_blob_xfer *xfer = cli->xfer;
304 
305 	LOG_DBG("%u", success);
306 
307 	io_close(cli);
308 	cli_state_reset(cli);
309 	if (cli->cb && cli->cb->end) {
310 		cli->cb->end(cli, xfer, success);
311 	}
312 }
313 
caps_adjust(struct bt_mesh_blob_cli * cli,const struct bt_mesh_blob_cli_caps * in)314 static enum bt_mesh_blob_status caps_adjust(struct bt_mesh_blob_cli *cli,
315 					    const struct bt_mesh_blob_cli_caps *in)
316 {
317 	if (!(in->modes & cli->caps.modes)) {
318 		return BT_MESH_BLOB_ERR_UNSUPPORTED_MODE;
319 	}
320 
321 	if ((in->min_block_size_log > cli->caps.max_block_size_log) ||
322 	    (in->max_block_size_log < cli->caps.min_block_size_log)) {
323 		return BT_MESH_BLOB_ERR_INVALID_BLOCK_SIZE;
324 	}
325 
326 	cli->caps.min_block_size_log =
327 		MAX(cli->caps.min_block_size_log, in->min_block_size_log);
328 	cli->caps.max_block_size_log =
329 		MIN(cli->caps.max_block_size_log, in->max_block_size_log);
330 	cli->caps.max_chunks = MIN(cli->caps.max_chunks, in->max_chunks);
331 	cli->caps.mtu_size = MIN(cli->caps.mtu_size, in->mtu_size);
332 	cli->caps.max_chunk_size = MIN(cli->caps.max_chunk_size, in->max_chunk_size);
333 	cli->caps.modes &= in->modes;
334 	cli->caps.max_size = MIN(cli->caps.max_size, in->max_size);
335 
336 	return BT_MESH_BLOB_SUCCESS;
337 }
338 
339 /*******************************************************************************
340  * TX State machine
341  *
342  * All messages in the transfer are going out to all the targets, either through
343  * group messaging or directly to each. The TX state machine implements this
344  * pattern for the transfer state machine to use. It will send the messages to
345  * all devices (through the group or directly), repeating until it receives a
346  * response from each device, or the attempts run out. Messages may also be
347  * marked as unacked if they require no response.
348  ******************************************************************************/
349 
next_target(struct bt_mesh_blob_cli * cli,struct bt_mesh_blob_target ** current)350 static struct bt_mesh_blob_target *next_target(struct bt_mesh_blob_cli *cli,
351 					       struct bt_mesh_blob_target **current)
352 {
353 	if (*current) {
354 		*current = SYS_SLIST_PEEK_NEXT_CONTAINER(*current, n);
355 	} else {
356 		*current = SYS_SLIST_PEEK_HEAD_CONTAINER(
357 			(sys_slist_t *)&cli->inputs->targets, *current, n);
358 	}
359 
360 	while (*current) {
361 		if ((*current)->acked || (*current)->procedure_complete ||
362 		    (*current)->status != BT_MESH_BLOB_SUCCESS || (*current)->timedout ||
363 		    (*current)->skip) {
364 			goto next;
365 		}
366 
367 		if (SENDING_CHUNKS_IN_PULL_MODE(cli) &&
368 		    (k_uptime_get() < (*current)->pull->block_report_timestamp ||
369 		     !blob_chunk_missing_get((*current)->pull->missing, cli->chunk_idx))) {
370 			/* Skip targets that didn't time out or timed out, but confirmed
371 			 * the currently transmitted chunk (cli->chunk_idx).
372 			 */
373 			goto next;
374 		}
375 
376 		break;
377 
378 next:
379 		*current = SYS_SLIST_PEEK_NEXT_CONTAINER(*current, n);
380 	}
381 
382 	return *current;
383 }
384 
send(struct bt_mesh_blob_cli * cli)385 static void send(struct bt_mesh_blob_cli *cli)
386 {
387 	cli->tx.sending = 1U;
388 	if (UNICAST_MODE(cli)) {
389 		cli->tx.ctx.send(cli, cli->tx.target->addr);
390 	} else {
391 		cli->tx.ctx.send(cli, cli->inputs->group);
392 	}
393 }
394 
broadcast_complete(struct bt_mesh_blob_cli * cli)395 static void broadcast_complete(struct bt_mesh_blob_cli *cli)
396 {
397 	LOG_DBG("%s", cli->tx.cancelled ? "cancelling" : "continuing");
398 
399 	cli->tx.ctx.is_inited = 0;
400 	k_work_cancel_delayable(&cli->tx.retry);
401 
402 	if (cli->tx.cancelled) {
403 		transfer_cancel(cli);
404 	} else {
405 		__ASSERT(cli->tx.ctx.next, "No next callback");
406 		cli->tx.ctx.next(cli);
407 	}
408 }
409 
tx_complete(struct k_work * work)410 static void tx_complete(struct k_work *work)
411 {
412 	struct bt_mesh_blob_cli *cli =
413 		CONTAINER_OF(work, struct bt_mesh_blob_cli, tx.complete);
414 
415 	if (!cli->tx.ctx.is_inited || !cli->tx.sending) {
416 		return;
417 	}
418 
419 	cli->tx.sending = 0U;
420 
421 	if (cli->tx.cancelled) {
422 		broadcast_complete(cli);
423 		return;
424 	}
425 
426 	if (cli->tx.ctx.send_complete) {
427 		cli->tx.ctx.send_complete(cli, cli->tx.target->addr);
428 	}
429 
430 	if (UNICAST_MODE(cli) && next_target(cli, &cli->tx.target)) {
431 		send(cli);
432 		return;
433 	}
434 
435 	if (cli->tx.ctx.acked && cli->tx.pending) {
436 		start_retry_timer(cli);
437 		return;
438 	}
439 
440 	broadcast_complete(cli);
441 }
442 
drop_remaining_targets(struct bt_mesh_blob_cli * cli)443 static void drop_remaining_targets(struct bt_mesh_blob_cli *cli)
444 {
445 	struct bt_mesh_blob_target *target;
446 
447 	LOG_DBG("");
448 
449 	cli->tx.pending = 0;
450 
451 	TARGETS_FOR_EACH(cli, target) {
452 		if (!target->acked && !target->timedout && !target->procedure_complete &&
453 		    !target->skip) {
454 			target->timedout = 1U;
455 			target_drop(cli, target, BT_MESH_BLOB_ERR_INTERNAL);
456 		}
457 	}
458 
459 	/* Update missing chunks to exclude chunks from dropped targets. */
460 	if (SENDING_CHUNKS_IN_PULL_MODE(cli)) {
461 		update_missing_chunks(cli);
462 	}
463 }
464 
retry_timeout(struct k_work * work)465 static void retry_timeout(struct k_work *work)
466 {
467 	struct bt_mesh_blob_cli *cli =
468 		CONTAINER_OF(work, struct bt_mesh_blob_cli, tx.retry.work);
469 
470 	/* When sending chunks in Pull mode, timeout is handled differently. Client will drop all
471 	 * non-responsive servers by cli_timestamp. By calling broadcast_complete(), client will
472 	 * either retransmit the missing chunks (if any), or proceed to the next block, or suspend
473 	 * the transfer if all targets timed out. All this is handled in block_check_end().
474 	 * Retry logic for all other procedures in Pull mode is handled as in Push mode.
475 	 */
476 	if (SENDING_CHUNKS_IN_PULL_MODE(cli)) {
477 		if (k_uptime_get() >= cli->tx.cli_timestamp) {
478 			LOG_DBG("Transfer timed out.");
479 
480 			if (!cli->tx.ctx.optional) {
481 				drop_remaining_targets(cli);
482 			}
483 		}
484 
485 		broadcast_complete(cli);
486 		return;
487 	}
488 
489 	LOG_DBG("%u", cli->tx.retries);
490 
491 	cli->tx.retries--;
492 	cli->tx.target = NULL;
493 
494 	__ASSERT(!cli->tx.sending, "still sending");
495 	__ASSERT(cli->tx.ctx.is_inited, "ctx is not initialized");
496 
497 	if (!cli->tx.retries) {
498 		LOG_DBG("Transfer timed out.");
499 
500 		if (!cli->tx.ctx.optional) {
501 			drop_remaining_targets(cli);
502 		}
503 
504 		broadcast_complete(cli);
505 		return;
506 	}
507 
508 	if (!cli->tx.ctx.acked || !next_target(cli, &cli->tx.target) || cli->tx.cancelled) {
509 		broadcast_complete(cli);
510 		return;
511 	}
512 
513 	send(cli);
514 }
515 
blob_cli_broadcast(struct bt_mesh_blob_cli * cli,const struct blob_cli_broadcast_ctx * ctx)516 void blob_cli_broadcast(struct bt_mesh_blob_cli *cli,
517 			const struct blob_cli_broadcast_ctx *ctx)
518 {
519 	if (cli->tx.ctx.is_inited || cli->tx.sending) {
520 		LOG_ERR("BLOB cli busy");
521 		return;
522 	}
523 
524 	cli->tx.cancelled = 0U;
525 	cli->tx.retries = CONFIG_BT_MESH_BLOB_CLI_BLOCK_RETRIES;
526 	cli->tx.ctx = *ctx;
527 	cli->tx.ctx.is_inited = 1U;
528 
529 	cli->tx.pending = targets_reset(cli);
530 
531 	LOG_DBG("%u targets", cli->tx.pending);
532 
533 	cli->tx.target = NULL;
534 	if (!next_target(cli, &cli->tx.target)) {
535 		LOG_DBG("No active targets");
536 		broadcast_complete(cli);
537 		return;
538 	}
539 
540 	send(cli);
541 }
542 
blob_cli_broadcast_tx_complete(struct bt_mesh_blob_cli * cli)543 void blob_cli_broadcast_tx_complete(struct bt_mesh_blob_cli *cli)
544 {
545 	k_work_submit(&cli->tx.complete);
546 }
547 
blob_cli_broadcast_rsp(struct bt_mesh_blob_cli * cli,struct bt_mesh_blob_target * target)548 void blob_cli_broadcast_rsp(struct bt_mesh_blob_cli *cli,
549 			    struct bt_mesh_blob_target *target)
550 {
551 	if (target->acked) {
552 		return;
553 	}
554 
555 	LOG_DBG("0x%04x, pending: %d", target->addr, cli->tx.pending);
556 
557 	target->acked = 1U;
558 
559 	if (!--cli->tx.pending && !cli->tx.sending) {
560 		broadcast_complete(cli);
561 	}
562 }
563 
blob_cli_broadcast_abort(struct bt_mesh_blob_cli * cli)564 void blob_cli_broadcast_abort(struct bt_mesh_blob_cli *cli)
565 {
566 	if (!cli->tx.ctx.is_inited) {
567 		return;
568 	}
569 
570 	if ((cli)->state >= BT_MESH_BLOB_CLI_STATE_START) {
571 		io_close(cli);
572 	}
573 
574 	cli_state_reset(cli);
575 }
576 
577 static void send_start(uint16_t duration, int err, void *cb_data);
578 static void send_end(int err, void *user_data);
579 
tx(struct bt_mesh_blob_cli * cli,uint16_t addr,struct net_buf_simple * buf)580 static int tx(struct bt_mesh_blob_cli *cli, uint16_t addr,
581 	      struct net_buf_simple *buf)
582 {
583 	static const struct bt_mesh_send_cb end_cb = {
584 		.start = send_start,
585 		.end = send_end,
586 	};
587 	struct bt_mesh_msg_ctx ctx = {
588 		.app_idx = cli->inputs->app_idx,
589 		.addr = addr,
590 		.send_ttl = cli->inputs->ttl,
591 	};
592 	int err;
593 
594 	err = bt_mesh_model_send(cli->mod, &ctx, buf, &end_cb, cli);
595 	if (err) {
596 		LOG_ERR("Send err: %d", err);
597 		send_end(err, cli);
598 		return err;
599 	}
600 
601 	return 0;
602 }
603 
send_start(uint16_t duration,int err,void * cb_data)604 static void send_start(uint16_t duration, int err, void *cb_data)
605 {
606 	if (err) {
607 		LOG_ERR("TX Start failed: %d", err);
608 		send_end(err, cb_data);
609 	}
610 }
611 
send_end(int err,void * user_data)612 static void send_end(int err, void *user_data)
613 {
614 	struct bt_mesh_blob_cli *cli = user_data;
615 
616 	if (!cli->tx.ctx.is_inited) {
617 		return;
618 	}
619 
620 	blob_cli_broadcast_tx_complete(cli);
621 }
622 
623 /*******************************************************************************
624  * TX
625  ******************************************************************************/
626 
info_get_tx(struct bt_mesh_blob_cli * cli,uint16_t dst)627 static void info_get_tx(struct bt_mesh_blob_cli *cli, uint16_t dst)
628 {
629 	BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_BLOB_OP_INFO_GET, 0);
630 	bt_mesh_model_msg_init(&buf, BT_MESH_BLOB_OP_INFO_GET);
631 
632 	tx(cli, dst, &buf);
633 }
634 
xfer_start_tx(struct bt_mesh_blob_cli * cli,uint16_t dst)635 static void xfer_start_tx(struct bt_mesh_blob_cli *cli, uint16_t dst)
636 {
637 	BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_BLOB_OP_XFER_START, 16);
638 	bt_mesh_model_msg_init(&buf, BT_MESH_BLOB_OP_XFER_START);
639 	net_buf_simple_add_u8(&buf, cli->xfer->mode << 6);
640 	net_buf_simple_add_le64(&buf, cli->xfer->id);
641 	net_buf_simple_add_le32(&buf, cli->xfer->size);
642 	net_buf_simple_add_u8(&buf, cli->xfer->block_size_log);
643 	net_buf_simple_add_le16(&buf, BT_MESH_TX_SDU_MAX);
644 
645 	tx(cli, dst, &buf);
646 }
647 
xfer_get_tx(struct bt_mesh_blob_cli * cli,uint16_t dst)648 static void xfer_get_tx(struct bt_mesh_blob_cli *cli, uint16_t dst)
649 {
650 	BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_BLOB_OP_XFER_GET, 0);
651 	bt_mesh_model_msg_init(&buf, BT_MESH_BLOB_OP_XFER_GET);
652 
653 	tx(cli, dst, &buf);
654 }
655 
xfer_cancel_tx(struct bt_mesh_blob_cli * cli,uint16_t dst)656 static void xfer_cancel_tx(struct bt_mesh_blob_cli *cli, uint16_t dst)
657 {
658 	BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_BLOB_OP_XFER_CANCEL, 8);
659 	bt_mesh_model_msg_init(&buf, BT_MESH_BLOB_OP_XFER_CANCEL);
660 	net_buf_simple_add_le64(&buf, cli->xfer->id);
661 
662 	tx(cli, dst, &buf);
663 }
664 
block_start_tx(struct bt_mesh_blob_cli * cli,uint16_t dst)665 static void block_start_tx(struct bt_mesh_blob_cli *cli, uint16_t dst)
666 {
667 	BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_BLOB_OP_BLOCK_START, 4);
668 	bt_mesh_model_msg_init(&buf, BT_MESH_BLOB_OP_BLOCK_START);
669 	net_buf_simple_add_le16(&buf, cli->block.number);
670 	net_buf_simple_add_le16(&buf, cli->xfer->chunk_size);
671 
672 	tx(cli, dst, &buf);
673 }
674 
chunk_tx(struct bt_mesh_blob_cli * cli,uint16_t dst)675 static void chunk_tx(struct bt_mesh_blob_cli *cli, uint16_t dst)
676 {
677 	NET_BUF_SIMPLE_DEFINE(buf, BT_MESH_TX_SDU_MAX);
678 	struct bt_mesh_blob_chunk chunk;
679 	int err;
680 
681 	bt_mesh_model_msg_init(&buf, BT_MESH_BLOB_OP_CHUNK);
682 	net_buf_simple_add_le16(&buf, cli->chunk_idx);
683 
684 	chunk.size = chunk_size(cli->xfer, &cli->block, cli->chunk_idx);
685 	chunk.offset = cli->xfer->chunk_size * cli->chunk_idx;
686 	chunk.data = net_buf_simple_add(&buf, chunk.size);
687 
688 	err = cli->io->rd(cli->io, cli->xfer, &cli->block, &chunk);
689 	if (err || cli->state == BT_MESH_BLOB_CLI_STATE_NONE) {
690 		bt_mesh_blob_cli_cancel(cli);
691 		return;
692 	}
693 
694 	tx(cli, dst, &buf);
695 }
696 
block_get_tx(struct bt_mesh_blob_cli * cli,uint16_t dst)697 static void block_get_tx(struct bt_mesh_blob_cli *cli, uint16_t dst)
698 {
699 	BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_BLOB_OP_BLOCK_GET, 0);
700 	bt_mesh_model_msg_init(&buf, BT_MESH_BLOB_OP_BLOCK_GET);
701 
702 	tx(cli, dst, &buf);
703 }
704 
705 /**************************************************************************************************
706  * State machine
707  *
708  * The BLOB Client state machine walks through the steps in the BLOB transfer in the following
709  * fashion:
710  *
711  *                                                 .---------------------------------------.
712  *                                                 V                                       |
713  * xfer_start -> block_set -> block_start -> chunk_send -> chunk_send_end                  |
714  *                   A                                           |                         |
715  *                   |                                           V                         |
716  *                   |                                [more missing chunks?]-----[Yes]-----+
717  *                   |                                           |                         |
718  *                   |                                         [No]                        |
719  *                   |                                           |                         |
720  *                   |                                           V                         |
721  *                   |                                         [mode?]                     |
722  *                   |                             .---[Push]---'   '---[Pull]---.         |
723  *                   |                             |                             |         |
724  *                   |                             V                             V         |
725  *                   |                        block_check               block_report_wait  |
726  *                   |                             |                             |         |
727  *                   |                             '-----------.   .-------------'         |
728  *                   |                                         |   |                       |
729  *                   |                                         V   V                       |
730  *                   |                                    block_check_end                  |
731  *                   |                                           |                         |
732  *                   |                                           V                         |
733  *                   |                                   [block completed?]------[No]------'
734  *                   |                                           |
735  *                   |                                         [Yes]
736  *                   |                                           |
737  *                   |                                           V
738  *                   '-------------------[No]------------[last block sent?]
739  *                                                               |
740  *                                                             [Yes]
741  *                                                               |
742  *                                                               V
743  *                                                        confirm_transfer
744  *                                                               |
745  *                                                               V
746  *                                                        transfer_complete
747  *
748  * In each state, the Client transmits a message to all target nodes. In each state, except when
749  * sending chunks (chunk_send), the Client expects a response from all target nodes, before
750  * proceeding to the next state.
751  *
752  * When a target node responds, the Client calls @ref blob_cli_broadcast_rsp for the corresponding
753  * target. Once all target nodes has responded, the Client proceeds to the next state.
754  *
755  * When sending chunks in Push mode, the Client will proceed to the next state (block_check) after
756  * transmitting all missing chunks. In the block_check state, the Client will request a block status
757  * from all target nodes. If any targets have missing chunks, the Client will resend them.
758  *
759  * When sending chunks in Pull mode, the Client addresses each target node individually using
760  * @ref bt_mesh_blob_target_pull structure. The Client uses @ref bt_mesh_blob_cli::block::missing
761  * to keep all missing chunks for the current block. Missing chunks for an individual target
762  * is kept in @ref bt_mesh_blob_target_pull::missing. The Client uses @ref
763  * bt_mesh_blob_target_pull::block_report_timeout to decide if it can send a chunk to this target.
764  *
765  * After sending all reported missing chunks to each target, the Client updates
766  * @ref bt_mesh_blob_target_pull::block_report_timestamp value for every target individually in
767  * chunk_tx_complete. The Client then proceedes to block_report_wait state and uses the earliest of
768  * all block_report_timestamp and cli_timestamp to schedule the retry timer. When the retry
769  * timer expires, the Client proceedes to the block_check_end state.
770  *
771  * In Pull mode, target nodes send a Partial Block Report message to the Client to inform about
772  * missing chunks. The Client doesn't control when these messages are sent by target nodes, and
773  * therefore it can't use @ref blob_cli_broadcast_rsp when it receives them. When the Client
774  * receives the Partial Block Report message, it updates missing chunks, resets
775  * block_report_timestamp, and explicitly calls @ref broadcast_complete to proceed to
776  * block_check_end state.
777  *
778  **************************************************************************************************/
779 static void caps_collected(struct bt_mesh_blob_cli *cli);
780 static void block_start(struct bt_mesh_blob_cli *cli);
781 static void chunk_send(struct bt_mesh_blob_cli *cli);
782 static void block_check(struct bt_mesh_blob_cli *cli);
783 static void block_check_end(struct bt_mesh_blob_cli *cli);
784 static void block_report_wait(struct bt_mesh_blob_cli *cli);
785 static void chunk_send_end(struct bt_mesh_blob_cli *cli);
786 static void confirm_transfer(struct bt_mesh_blob_cli *cli);
787 static void transfer_complete(struct bt_mesh_blob_cli *cli);
788 
caps_get(struct bt_mesh_blob_cli * cli)789 static void caps_get(struct bt_mesh_blob_cli *cli)
790 {
791 	const struct blob_cli_broadcast_ctx ctx = {
792 		.send = info_get_tx,
793 		.next = caps_collected,
794 		.acked = true,
795 	};
796 
797 	cli->state = BT_MESH_BLOB_CLI_STATE_CAPS_GET;
798 	blob_cli_broadcast(cli, &ctx);
799 }
800 
caps_collected(struct bt_mesh_blob_cli * cli)801 static void caps_collected(struct bt_mesh_blob_cli *cli)
802 {
803 	struct bt_mesh_blob_target *target;
804 	bool success = false;
805 
806 	cli->state = BT_MESH_BLOB_CLI_STATE_NONE;
807 
808 	cli_state_reset(cli);
809 
810 	TARGETS_FOR_EACH(cli, target) {
811 		if (target->status == BT_MESH_BLOB_SUCCESS) {
812 			success = true;
813 			break;
814 		}
815 	}
816 
817 	while (success &&
818 	       (1UL << cli->caps.max_block_size_log) >
819 	       (cli->caps.max_chunk_size * cli->caps.max_chunks)) {
820 		cli->caps.max_block_size_log--;
821 	}
822 
823 	if (cli->cb && cli->cb->caps) {
824 		cli->cb->caps(cli, success ? &cli->caps : NULL);
825 	}
826 }
827 
xfer_start(struct bt_mesh_blob_cli * cli)828 static int xfer_start(struct bt_mesh_blob_cli *cli)
829 {
830 	const struct blob_cli_broadcast_ctx ctx = {
831 		.send = xfer_start_tx,
832 		.next = block_start,
833 		.acked = true,
834 	};
835 	int err;
836 
837 	err = io_open(cli);
838 	if (err) {
839 		return -EIO;
840 	}
841 
842 	cli->state = BT_MESH_BLOB_CLI_STATE_START;
843 
844 	blob_cli_broadcast(cli, &ctx);
845 	return 0;
846 }
847 
block_start(struct bt_mesh_blob_cli * cli)848 static void block_start(struct bt_mesh_blob_cli *cli)
849 {
850 	const struct blob_cli_broadcast_ctx ctx = {
851 		.send = block_start_tx,
852 		.next = chunk_send,
853 		.acked = true,
854 	};
855 	struct bt_mesh_blob_target *target;
856 
857 
858 	if (!targets_active(cli)) {
859 		if (targets_timedout(cli)) {
860 			suspend(cli);
861 			return;
862 		}
863 
864 		end(cli, false);
865 		return;
866 	}
867 
868 	LOG_DBG("%u (%u chunks, %u/%u)", cli->block.number,
869 		cli->block.chunk_count, cli->block.number + 1, cli->block_count);
870 
871 	cli->chunk_idx = 0;
872 	cli->state = BT_MESH_BLOB_CLI_STATE_BLOCK_START;
873 	/* Client Timeout Timer in Send Data State Machine is initialized initially after
874 	 * transmitting the first bunch of chunks (see block_report_wait()). Next time it will be
875 	 * updated after every Partial Block Report message.
876 	 */
877 	cli->tx.cli_timestamp = 0ll;
878 
879 	TARGETS_FOR_EACH(cli, target) {
880 		target->procedure_complete = 0U;
881 
882 		if (cli->xfer->mode == BT_MESH_BLOB_XFER_MODE_PULL) {
883 			target->pull->block_report_timestamp = 0ll;
884 		}
885 	}
886 
887 	if (cli->io->block_start) {
888 		cli->io->block_start(cli->io, cli->xfer, &cli->block);
889 		if (cli->state == BT_MESH_BLOB_CLI_STATE_NONE) {
890 			return;
891 		}
892 	}
893 
894 	blob_cli_broadcast(cli, &ctx);
895 }
896 
chunk_tx_complete(struct bt_mesh_blob_cli * cli,uint16_t dst)897 static void chunk_tx_complete(struct bt_mesh_blob_cli *cli, uint16_t dst)
898 {
899 	if (cli->xfer->mode != BT_MESH_BLOB_XFER_MODE_PULL) {
900 		return;
901 	}
902 
903 	/* Update Block Report Timer individually for each target after sending out the last chunk
904 	 * in current iteration.
905 	 */
906 	uint16_t chunk_idx = next_missing_chunk(cli, cli->tx.target->pull->missing,
907 						cli->chunk_idx + 1);
908 	if (chunk_idx < cli->block.chunk_count) {
909 		/* Will send more chunks to this target in this iteration. */
910 		return;
911 	}
912 
913 	/* This was the last chunk sent for this target. Now start the Block Report Timeout Timer.
914 	 */
915 	struct bt_mesh_blob_target *target;
916 	int64_t timestamp = k_uptime_get() + BLOCK_REPORT_TIME_MSEC;
917 
918 	if (!UNICAST_MODE(cli)) {
919 		/* If using group adressing, reset timestamp for all targets after all chunks are
920 		 * sent to the group address
921 		 */
922 		TARGETS_FOR_EACH(cli, target) {
923 			target->pull->block_report_timestamp = timestamp;
924 		}
925 		return;
926 	}
927 
928 	cli->tx.target->pull->block_report_timestamp = timestamp;
929 }
930 
chunk_send(struct bt_mesh_blob_cli * cli)931 static void chunk_send(struct bt_mesh_blob_cli *cli)
932 {
933 	struct blob_cli_broadcast_ctx ctx = {
934 		.send = chunk_tx,
935 		.next = chunk_send_end,
936 		.acked = false,
937 	};
938 
939 	if (cli->xfer->mode == BT_MESH_BLOB_XFER_MODE_PULL) {
940 		ctx.send_complete = chunk_tx_complete;
941 	}
942 
943 	if (!targets_active(cli)) {
944 		if (targets_timedout(cli)) {
945 			suspend(cli);
946 			return;
947 		}
948 
949 		end(cli, false);
950 		return;
951 	}
952 
953 	LOG_DBG("%u / %u size: %u", cli->chunk_idx + 1, cli->block.chunk_count,
954 		chunk_size(cli->xfer, &cli->block, cli->chunk_idx));
955 
956 	cli->state = BT_MESH_BLOB_CLI_STATE_BLOCK_SEND;
957 	blob_cli_broadcast(cli, &ctx);
958 }
959 
chunk_send_end(struct bt_mesh_blob_cli * cli)960 static void chunk_send_end(struct bt_mesh_blob_cli *cli)
961 {
962 	/* In pull mode, the partial block reports are used to confirm which
963 	 * chunks have been received, while in push mode, we just assume that a
964 	 * sent chunk has been received.
965 	 */
966 	if (cli->xfer->mode == BT_MESH_BLOB_XFER_MODE_PUSH) {
967 		blob_chunk_missing_set(cli->block.missing, cli->chunk_idx, false);
968 	}
969 
970 	cli->chunk_idx = next_missing_chunk(cli, cli->block.missing, cli->chunk_idx + 1);
971 	if (cli->chunk_idx < cli->block.chunk_count) {
972 		chunk_send(cli);
973 		return;
974 	}
975 
976 	if (cli->xfer->mode == BT_MESH_BLOB_XFER_MODE_PUSH) {
977 		block_check(cli);
978 	} else {
979 		block_report_wait(cli);
980 	}
981 }
982 
983 /* The block checking pair(block_check - block_check_end)
984  * is relevant only for Push mode.
985  */
block_check(struct bt_mesh_blob_cli * cli)986 static void block_check(struct bt_mesh_blob_cli *cli)
987 {
988 	const struct blob_cli_broadcast_ctx ctx = {
989 		.send = block_get_tx,
990 		.next = block_check_end,
991 		.acked = true,
992 	};
993 
994 	cli->state = BT_MESH_BLOB_CLI_STATE_BLOCK_CHECK;
995 
996 	LOG_DBG("");
997 
998 	blob_cli_broadcast(cli, &ctx);
999 }
1000 
block_report_wait(struct bt_mesh_blob_cli * cli)1001 static void block_report_wait(struct bt_mesh_blob_cli *cli)
1002 {
1003 	const struct blob_cli_broadcast_ctx ctx = {
1004 		.next = block_check_end,
1005 		.acked = false,
1006 	};
1007 
1008 	/* Check if all servers already confirmed all chunks during the transmission. */
1009 	if (next_missing_chunk(cli, cli->block.missing, 0) >= cli->block.chunk_count) {
1010 		block_check_end(cli);
1011 		return;
1012 	}
1013 
1014 	LOG_DBG("Waiting for partial block report...");
1015 	cli->tx.ctx = ctx;
1016 
1017 	/* Start Client Timeout Timer in Send Data sub-procedure for the first time. */
1018 	if (!cli->tx.cli_timestamp) {
1019 		cli->tx.cli_timestamp = k_uptime_get() + CLIENT_TIMEOUT_MSEC(cli);
1020 	}
1021 
1022 	start_retry_timer(cli);
1023 }
1024 
block_check_end(struct bt_mesh_blob_cli * cli)1025 static void block_check_end(struct bt_mesh_blob_cli *cli)
1026 {
1027 	LOG_DBG("");
1028 
1029 	if (!targets_active(cli)) {
1030 		if (targets_timedout(cli)) {
1031 			suspend(cli);
1032 			return;
1033 		}
1034 
1035 		end(cli, false);
1036 		return;
1037 	}
1038 
1039 	cli->chunk_idx = next_missing_chunk(cli, cli->block.missing, 0);
1040 	if (cli->chunk_idx < cli->block.chunk_count) {
1041 		chunk_send(cli);
1042 		return;
1043 	}
1044 
1045 	LOG_DBG("No more missing chunks for block %u", cli->block.number);
1046 
1047 	if (cli->io->block_end) {
1048 		cli->io->block_end(cli->io, cli->xfer, &cli->block);
1049 		if (cli->state == BT_MESH_BLOB_CLI_STATE_NONE) {
1050 			return;
1051 		}
1052 	}
1053 
1054 	if (cli->block.number == cli->block_count - 1) {
1055 		struct bt_mesh_blob_target *target;
1056 
1057 		TARGETS_FOR_EACH(cli, target) {
1058 			target->procedure_complete = 0U;
1059 		}
1060 
1061 		confirm_transfer(cli);
1062 		return;
1063 	}
1064 
1065 	block_set(cli, cli->block.number + 1);
1066 	block_start(cli);
1067 }
1068 
confirm_transfer(struct bt_mesh_blob_cli * cli)1069 static void confirm_transfer(struct bt_mesh_blob_cli *cli)
1070 {
1071 	const struct blob_cli_broadcast_ctx ctx = {
1072 		.send = xfer_get_tx,
1073 		.next = transfer_complete,
1074 		.acked = true,
1075 	};
1076 
1077 	LOG_DBG("");
1078 
1079 	cli->state = BT_MESH_BLOB_CLI_STATE_XFER_CHECK;
1080 
1081 	blob_cli_broadcast(cli, &ctx);
1082 }
1083 
progress_checked(struct bt_mesh_blob_cli * cli)1084 static void progress_checked(struct bt_mesh_blob_cli *cli)
1085 {
1086 	LOG_DBG("");
1087 
1088 	cli->state = BT_MESH_BLOB_CLI_STATE_NONE;
1089 
1090 	if (cli->cb && cli->cb->end) {
1091 		cli->cb->xfer_progress_complete(cli);
1092 	}
1093 }
1094 
check_transfer(struct bt_mesh_blob_cli * cli)1095 static void check_transfer(struct bt_mesh_blob_cli *cli)
1096 {
1097 	const struct blob_cli_broadcast_ctx ctx = {
1098 		.send = xfer_get_tx,
1099 		.next = progress_checked,
1100 		.acked = true,
1101 	};
1102 
1103 	LOG_DBG("");
1104 
1105 	cli->state = BT_MESH_BLOB_CLI_STATE_XFER_PROGRESS_GET;
1106 
1107 	blob_cli_broadcast(cli, &ctx);
1108 }
1109 
transfer_cancel(struct bt_mesh_blob_cli * cli)1110 static void transfer_cancel(struct bt_mesh_blob_cli *cli)
1111 {
1112 	const struct blob_cli_broadcast_ctx ctx = {
1113 		.send = xfer_cancel_tx,
1114 		.next = transfer_complete,
1115 		.acked = true,
1116 	};
1117 
1118 	LOG_DBG("");
1119 
1120 	cli->state = BT_MESH_BLOB_CLI_STATE_CANCEL;
1121 
1122 	blob_cli_broadcast(cli, &ctx);
1123 }
1124 
transfer_complete(struct bt_mesh_blob_cli * cli)1125 static void transfer_complete(struct bt_mesh_blob_cli *cli)
1126 {
1127 	bool success = targets_active(cli) &&
1128 		       cli->state == BT_MESH_BLOB_CLI_STATE_XFER_CHECK;
1129 
1130 	end(cli, success);
1131 }
1132 
1133 /*******************************************************************************
1134  * RX
1135  ******************************************************************************/
1136 
rx_block_status(struct bt_mesh_blob_cli * cli,struct bt_mesh_blob_target * target,struct block_status * block)1137 static void rx_block_status(struct bt_mesh_blob_cli *cli,
1138 			    struct bt_mesh_blob_target *target,
1139 			    struct block_status *block)
1140 {
1141 	if (cli->state != BT_MESH_BLOB_CLI_STATE_BLOCK_START &&
1142 	    cli->state != BT_MESH_BLOB_CLI_STATE_BLOCK_SEND &&
1143 	    cli->state != BT_MESH_BLOB_CLI_STATE_BLOCK_CHECK) {
1144 		LOG_WRN("Invalid state %u", cli->state);
1145 		return;
1146 	}
1147 
1148 	LOG_DBG("0x%04x: block: %u status: %u", target->addr, block->block.number, block->status);
1149 
1150 	if (block->status != BT_MESH_BLOB_SUCCESS) {
1151 		target_drop(cli, target, block->status);
1152 		blob_cli_broadcast_rsp(cli, target);
1153 		return;
1154 	}
1155 
1156 	if (block->block.number != cli->block.number) {
1157 		LOG_DBG("Invalid block num (expected %u)", cli->block.number);
1158 		return;
1159 	}
1160 
1161 	if (block->missing == BT_MESH_BLOB_CHUNKS_MISSING_NONE) {
1162 		target->procedure_complete = 1U;
1163 
1164 		if (cli->xfer->mode == BT_MESH_BLOB_XFER_MODE_PULL) {
1165 			memset(target->pull->missing, 0, sizeof(target->pull->missing));
1166 			update_missing_chunks(cli);
1167 		}
1168 
1169 		LOG_DBG("Target 0x%04x received all chunks", target->addr);
1170 	} else if (block->missing == BT_MESH_BLOB_CHUNKS_MISSING_ALL) {
1171 		blob_chunk_missing_set_all(&cli->block);
1172 	} else if (cli->xfer->mode == BT_MESH_BLOB_XFER_MODE_PULL) {
1173 		memcpy(target->pull->missing, block->block.missing, sizeof(block->block.missing));
1174 
1175 		LOG_DBG("Missing: %s", bt_hex(target->pull->missing, cli->block.chunk_count));
1176 
1177 		update_missing_chunks(cli);
1178 
1179 		/* Target has responded. Reset the timestamp so that client can start transmitting
1180 		 * missing chunks to it.
1181 		 */
1182 		target->pull->block_report_timestamp = 0ll;
1183 	} else {
1184 		for (int i = 0; i < ARRAY_SIZE(block->block.missing); ++i) {
1185 			cli->block.missing[i] |= block->block.missing[i];
1186 		}
1187 	}
1188 
1189 	if (SENDING_CHUNKS_IN_PULL_MODE(cli)) {
1190 		if (!cli->tx.sending) {
1191 			/* If not sending, then the retry timer is running. Call
1192 			 * broadcast_complete() to proceed to block_check_end() and start
1193 			 * transmitting missing chunks.
1194 			 */
1195 			broadcast_complete(cli);
1196 		}
1197 
1198 		/* When sending chunks in Pull mode, we don't confirm transaction when receiving
1199 		 * Partial Block Report message.
1200 		 */
1201 		return;
1202 	}
1203 
1204 	blob_cli_broadcast_rsp(cli, target);
1205 }
1206 
handle_xfer_status(const struct bt_mesh_model * mod,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)1207 static int handle_xfer_status(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx,
1208 			      struct net_buf_simple *buf)
1209 {
1210 	struct bt_mesh_blob_cli *cli = mod->rt->user_data;
1211 	enum bt_mesh_blob_xfer_phase expected_phase;
1212 	struct bt_mesh_blob_target *target;
1213 	struct bt_mesh_blob_xfer_info info = { 0 };
1214 	uint8_t status_and_mode;
1215 
1216 	status_and_mode = net_buf_simple_pull_u8(buf);
1217 	info.status = status_and_mode & BIT_MASK(4);
1218 	info.mode = status_and_mode >> 6;
1219 	info.phase = net_buf_simple_pull_u8(buf);
1220 
1221 	if (buf->len) {
1222 		info.id = net_buf_simple_pull_le64(buf);
1223 	}
1224 
1225 	if (buf->len >= 7) {
1226 		info.size = net_buf_simple_pull_le32(buf);
1227 		info.block_size_log = net_buf_simple_pull_u8(buf);
1228 		info.mtu_size = net_buf_simple_pull_le16(buf);
1229 		info.missing_blocks = net_buf_simple_pull(buf, buf->len);
1230 	}
1231 
1232 	LOG_DBG("status: %u %s phase: %u %s", info.status,
1233 		info.mode == BT_MESH_BLOB_XFER_MODE_PUSH ? "push" : "pull",
1234 		info.phase, bt_hex(&info.id, 8));
1235 
1236 
1237 	if (cli->state != BT_MESH_BLOB_CLI_STATE_START &&
1238 	    cli->state != BT_MESH_BLOB_CLI_STATE_XFER_CHECK &&
1239 	    cli->state != BT_MESH_BLOB_CLI_STATE_CANCEL &&
1240 	    cli->state != BT_MESH_BLOB_CLI_STATE_XFER_PROGRESS_GET) {
1241 		LOG_WRN("Wrong state: %d", cli->state);
1242 		return -EBUSY;
1243 	}
1244 
1245 	target = target_get(cli, ctx->addr);
1246 	if (!target) {
1247 		return -ENOENT;
1248 	}
1249 
1250 	if (cli->state == BT_MESH_BLOB_CLI_STATE_START) {
1251 		expected_phase = BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_BLOCK;
1252 	} else if (cli->state == BT_MESH_BLOB_CLI_STATE_XFER_CHECK) {
1253 		expected_phase = BT_MESH_BLOB_XFER_PHASE_COMPLETE;
1254 	} else if (cli->state != BT_MESH_BLOB_CLI_STATE_XFER_PROGRESS_GET) {
1255 		expected_phase = BT_MESH_BLOB_XFER_PHASE_INACTIVE;
1256 	} else { /* cli->state == BT_MESH_BLOB_CLI_STATE_XFER_PROGRESS_GET */
1257 		blob_cli_broadcast_rsp(cli, target);
1258 		if (cli->cb && cli->cb->xfer_progress) {
1259 			cli->cb->xfer_progress(cli, target, &info);
1260 		}
1261 		return 0;
1262 	}
1263 
1264 	if (info.status != BT_MESH_BLOB_SUCCESS) {
1265 		target_drop(cli, target, info.status);
1266 	} else if (info.phase != expected_phase) {
1267 		LOG_WRN("Wrong phase: %u != %u", expected_phase, info.phase);
1268 		return -EINVAL;
1269 	} else if (info.phase != BT_MESH_BLOB_XFER_PHASE_INACTIVE &&
1270 		   info.id != cli->xfer->id) {
1271 		target_drop(cli, target, BT_MESH_BLOB_ERR_WRONG_BLOB_ID);
1272 	}
1273 
1274 	blob_cli_broadcast_rsp(cli, target);
1275 
1276 	return 0;
1277 }
1278 
handle_block_report(const struct bt_mesh_model * mod,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)1279 static int handle_block_report(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx,
1280 			       struct net_buf_simple *buf)
1281 {
1282 	struct bt_mesh_blob_cli *cli = mod->rt->user_data;
1283 	struct block_status status = {
1284 		.status = BT_MESH_BLOB_SUCCESS,
1285 		.block.number = cli->block.number,
1286 		.missing = (buf->len ? BT_MESH_BLOB_CHUNKS_MISSING_ENCODED :
1287 				       BT_MESH_BLOB_CHUNKS_MISSING_NONE),
1288 	};
1289 	struct bt_mesh_blob_target *target;
1290 
1291 	if (!cli->xfer) {
1292 		return -EINVAL;
1293 	}
1294 
1295 	if (cli->xfer->mode == BT_MESH_BLOB_XFER_MODE_PUSH) {
1296 		LOG_WRN("Unexpected encoded block report in push mode");
1297 		return -EINVAL;
1298 	}
1299 
1300 	LOG_DBG("");
1301 
1302 	target = target_get(cli, ctx->addr);
1303 	if (!target) {
1304 		return -ENOENT;
1305 	}
1306 
1307 	while (buf->len) {
1308 		int idx;
1309 
1310 		idx = chunk_idx_decode(buf);
1311 		if (idx < 0) {
1312 			return idx;
1313 		}
1314 
1315 		blob_chunk_missing_set(status.block.missing, idx, true);
1316 	}
1317 
1318 	/* If all chunks were already confirmed by this target, Send Data State Machine is in Final
1319 	 * state for this target. Therefore, the message should be ignored.
1320 	 */
1321 	if (next_missing_chunk(cli, target->pull->missing, 0) >= cli->block.chunk_count) {
1322 		LOG_DBG("All chunks already confirmed");
1323 		return 0;
1324 	}
1325 
1326 	cli->tx.cli_timestamp = k_uptime_get() + CLIENT_TIMEOUT_MSEC(cli);
1327 
1328 	rx_block_status(cli, target, &status);
1329 
1330 	return 0;
1331 }
1332 
handle_block_status(const struct bt_mesh_model * mod,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)1333 static int handle_block_status(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx,
1334 			       struct net_buf_simple *buf)
1335 {
1336 	struct bt_mesh_blob_cli *cli = mod->rt->user_data;
1337 	struct bt_mesh_blob_target *target;
1338 	struct block_status status = { 0 };
1339 	uint8_t status_and_format;
1340 	uint16_t chunk_size;
1341 	size_t len;
1342 	int idx;
1343 
1344 	target = target_get(cli, ctx->addr);
1345 	if (!target) {
1346 		return -ENOENT;
1347 	}
1348 
1349 	status_and_format = net_buf_simple_pull_u8(buf);
1350 	status.status = status_and_format & BIT_MASK(4);
1351 	status.missing = status_and_format >> 6;
1352 	status.block.number = net_buf_simple_pull_le16(buf);
1353 	chunk_size = net_buf_simple_pull_le16(buf);
1354 	status.block.chunk_count =
1355 		DIV_ROUND_UP(cli->block.size, chunk_size);
1356 
1357 	LOG_DBG("status: %u block: %u encoding: %u", status.status,
1358 		status.block.number, status.missing);
1359 
1360 	switch (status.missing) {
1361 	case BT_MESH_BLOB_CHUNKS_MISSING_ALL:
1362 		blob_chunk_missing_set_all(&status.block);
1363 		break;
1364 	case BT_MESH_BLOB_CHUNKS_MISSING_NONE:
1365 		break;
1366 	case BT_MESH_BLOB_CHUNKS_MISSING_SOME:
1367 		if (buf->len > sizeof(status.block.missing)) {
1368 			return -EINVAL;
1369 		}
1370 
1371 		len = buf->len;
1372 		memcpy(status.block.missing, net_buf_simple_pull_mem(buf, len),
1373 		       len);
1374 
1375 		LOG_DBG("Missing: %s", bt_hex(status.block.missing, len));
1376 		break;
1377 	case BT_MESH_BLOB_CHUNKS_MISSING_ENCODED:
1378 		/** MshMBTv1.0: 5.3.8: An empty Missing Chunks field entails that there are no
1379 		 * missing chunks for this block.
1380 		 */
1381 		if (!buf->len) {
1382 			status.missing = BT_MESH_BLOB_CHUNKS_MISSING_NONE;
1383 		}
1384 
1385 		while (buf->len) {
1386 			idx = chunk_idx_decode(buf);
1387 			if (idx < 0 || idx >= status.block.chunk_count) {
1388 				LOG_ERR("Invalid encoding");
1389 				return -EINVAL;
1390 			}
1391 
1392 			LOG_DBG("Missing %d", idx);
1393 
1394 			blob_chunk_missing_set(status.block.missing, idx, true);
1395 		}
1396 		break;
1397 	}
1398 
1399 	rx_block_status(cli, target, &status);
1400 
1401 	return 0;
1402 }
1403 
handle_info_status(const struct bt_mesh_model * mod,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)1404 static int handle_info_status(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx,
1405 			      struct net_buf_simple *buf)
1406 {
1407 	struct bt_mesh_blob_cli *cli = mod->rt->user_data;
1408 	struct bt_mesh_blob_cli_caps caps;
1409 	enum bt_mesh_blob_status status;
1410 	struct bt_mesh_blob_target *target;
1411 
1412 	if (cli->state != BT_MESH_BLOB_CLI_STATE_CAPS_GET) {
1413 		return -EBUSY;
1414 	}
1415 
1416 	caps.min_block_size_log = net_buf_simple_pull_u8(buf);
1417 	caps.max_block_size_log = net_buf_simple_pull_u8(buf);
1418 	caps.max_chunks = net_buf_simple_pull_le16(buf);
1419 	caps.max_chunk_size = net_buf_simple_pull_le16(buf);
1420 	caps.max_size = net_buf_simple_pull_le32(buf);
1421 	caps.mtu_size = net_buf_simple_pull_le16(buf);
1422 	caps.modes = net_buf_simple_pull_u8(buf);
1423 
1424 	if (caps.min_block_size_log < 0x06 ||
1425 	    caps.max_block_size_log > 0x20 ||
1426 	    caps.max_block_size_log < caps.min_block_size_log ||
1427 	    caps.max_chunks == 0 || caps.max_chunk_size < 8 ||
1428 	    caps.max_size == 0 || caps.mtu_size < 0x14) {
1429 		return -EINVAL;
1430 	}
1431 
1432 	LOG_DBG("0x%04x\n\tblock size: %u - %u\n\tchunks: %u\n\tchunk size: %u\n"
1433 	       "\tblob size: %u\n\tmtu size: %u\n\tmodes: %x",
1434 	       ctx->addr, caps.min_block_size_log, caps.max_block_size_log,
1435 	       caps.max_chunks, caps.max_chunk_size, caps.max_size,
1436 	       caps.mtu_size, caps.modes);
1437 
1438 	target = target_get(cli, ctx->addr);
1439 	if (!target) {
1440 		return -ENOENT;
1441 	}
1442 
1443 	status = caps_adjust(cli, &caps);
1444 	if (status != BT_MESH_BLOB_SUCCESS) {
1445 		target_drop(cli, target, status);
1446 	}
1447 
1448 	blob_cli_broadcast_rsp(cli, target);
1449 
1450 	return 0;
1451 }
1452 
1453 const struct bt_mesh_model_op _bt_mesh_blob_cli_op[] = {
1454 	{ BT_MESH_BLOB_OP_XFER_STATUS, BT_MESH_LEN_MIN(2), handle_xfer_status },
1455 	{ BT_MESH_BLOB_OP_BLOCK_REPORT, BT_MESH_LEN_MIN(0), handle_block_report },
1456 	{ BT_MESH_BLOB_OP_BLOCK_STATUS, BT_MESH_LEN_MIN(5), handle_block_status },
1457 	{ BT_MESH_BLOB_OP_INFO_STATUS, BT_MESH_LEN_EXACT(13), handle_info_status },
1458 	BT_MESH_MODEL_OP_END,
1459 };
1460 
blob_cli_init(const struct bt_mesh_model * mod)1461 static int blob_cli_init(const struct bt_mesh_model *mod)
1462 {
1463 	struct bt_mesh_blob_cli *cli = mod->rt->user_data;
1464 
1465 	cli->mod = mod;
1466 
1467 	cli->tx.cli_timestamp = 0ll;
1468 	k_work_init_delayable(&cli->tx.retry, retry_timeout);
1469 	k_work_init(&cli->tx.complete, tx_complete);
1470 
1471 	return 0;
1472 }
1473 
blob_cli_reset(const struct bt_mesh_model * mod)1474 static void blob_cli_reset(const struct bt_mesh_model *mod)
1475 {
1476 	struct bt_mesh_blob_cli *cli = mod->rt->user_data;
1477 
1478 	cli_state_reset(cli);
1479 }
1480 
1481 const struct bt_mesh_model_cb _bt_mesh_blob_cli_cb = {
1482 	.init = blob_cli_init,
1483 	.reset = blob_cli_reset,
1484 };
1485 
1486 
bt_mesh_blob_cli_caps_get(struct bt_mesh_blob_cli * cli,const struct bt_mesh_blob_cli_inputs * inputs)1487 int bt_mesh_blob_cli_caps_get(struct bt_mesh_blob_cli *cli,
1488 			      const struct bt_mesh_blob_cli_inputs *inputs)
1489 {
1490 	if (bt_mesh_blob_cli_is_busy(cli)) {
1491 		return -EBUSY;
1492 	}
1493 
1494 	cli->inputs = inputs;
1495 
1496 	cli->caps.min_block_size_log = 0x06;
1497 	cli->caps.max_block_size_log = 0x20;
1498 	cli->caps.max_chunks = CONFIG_BT_MESH_BLOB_CHUNK_COUNT_MAX;
1499 	cli->caps.max_chunk_size = CHUNK_SIZE_MAX;
1500 	cli->caps.max_size = 0xffffffff;
1501 	cli->caps.mtu_size = 0xffff;
1502 	cli->caps.modes = BT_MESH_BLOB_XFER_MODE_ALL;
1503 
1504 	if (!targets_reset(cli)) {
1505 		LOG_ERR("No valid targets");
1506 		return -ENODEV;
1507 	}
1508 
1509 	caps_get(cli);
1510 
1511 	return 0;
1512 }
1513 
bt_mesh_blob_cli_send(struct bt_mesh_blob_cli * cli,const struct bt_mesh_blob_cli_inputs * inputs,const struct bt_mesh_blob_xfer * xfer,const struct bt_mesh_blob_io * io)1514 int bt_mesh_blob_cli_send(struct bt_mesh_blob_cli *cli,
1515 			  const struct bt_mesh_blob_cli_inputs *inputs,
1516 			  const struct bt_mesh_blob_xfer *xfer,
1517 			  const struct bt_mesh_blob_io *io)
1518 {
1519 	if (bt_mesh_blob_cli_is_busy(cli)) {
1520 		LOG_ERR("BLOB Client is busy");
1521 		return -EBUSY;
1522 	}
1523 
1524 	if (!(xfer->mode & BT_MESH_BLOB_XFER_MODE_ALL) ||
1525 	    xfer->block_size_log < 0x06 || xfer->block_size_log > 0x20 ||
1526 	    xfer->chunk_size < 8 || xfer->chunk_size > CHUNK_SIZE_MAX) {
1527 		LOG_ERR("Incompatible transfer parameters");
1528 		return -EINVAL;
1529 	}
1530 
1531 	cli->xfer = xfer;
1532 	cli->inputs = inputs;
1533 	cli->io = io;
1534 
1535 	if (cli->xfer->block_size_log == 0x20) {
1536 		cli->block_count = 1;
1537 	} else {
1538 		cli->block_count = DIV_ROUND_UP(cli->xfer->size, (1U << cli->xfer->block_size_log));
1539 	}
1540 
1541 	block_set(cli, 0);
1542 
1543 	if (cli->block.chunk_count > CONFIG_BT_MESH_BLOB_CHUNK_COUNT_MAX) {
1544 		LOG_ERR("Too many chunks");
1545 		return -EINVAL;
1546 	}
1547 
1548 	if (!targets_reset(cli)) {
1549 		LOG_ERR("No valid targets");
1550 		return -ENODEV;
1551 	}
1552 
1553 	LOG_DBG("\n\tblock size log: %u\n\tchunk size: %u\n"
1554 		"\tblob size: %u\n\tmode: %x",
1555 		cli->xfer->block_size_log, cli->xfer->chunk_size,
1556 		cli->xfer->size, cli->xfer->mode);
1557 
1558 	return xfer_start(cli);
1559 }
1560 
bt_mesh_blob_cli_suspend(struct bt_mesh_blob_cli * cli)1561 int bt_mesh_blob_cli_suspend(struct bt_mesh_blob_cli *cli)
1562 {
1563 	if (cli->state == BT_MESH_BLOB_CLI_STATE_SUSPENDED) {
1564 		return 0;
1565 	}
1566 
1567 	if (cli->state != BT_MESH_BLOB_CLI_STATE_BLOCK_START &&
1568 	    cli->state != BT_MESH_BLOB_CLI_STATE_BLOCK_SEND &&
1569 	    cli->state != BT_MESH_BLOB_CLI_STATE_BLOCK_CHECK) {
1570 		LOG_WRN("BLOB xfer not started: %d", cli->state);
1571 		return -EINVAL;
1572 	}
1573 
1574 	cli->state = BT_MESH_BLOB_CLI_STATE_SUSPENDED;
1575 	(void)k_work_cancel_delayable(&cli->tx.retry);
1576 	cli->tx.ctx.is_inited = 0;
1577 	cli->tx.sending = 0;
1578 	cli->tx.cli_timestamp = 0ll;
1579 	return 0;
1580 }
1581 
bt_mesh_blob_cli_resume(struct bt_mesh_blob_cli * cli)1582 int bt_mesh_blob_cli_resume(struct bt_mesh_blob_cli *cli)
1583 {
1584 	struct bt_mesh_blob_target *target;
1585 
1586 	if (cli->state != BT_MESH_BLOB_CLI_STATE_SUSPENDED) {
1587 		LOG_WRN("Not suspended");
1588 		return -EINVAL;
1589 	}
1590 
1591 	/* Restore timed out targets. */
1592 	TARGETS_FOR_EACH(cli, target) {
1593 		if (!!target->timedout) {
1594 			target->status = BT_MESH_BLOB_SUCCESS;
1595 			target->timedout = 0U;
1596 		}
1597 	}
1598 
1599 	if (!targets_reset(cli)) {
1600 		LOG_ERR("No valid targets");
1601 		return -ENODEV;
1602 	}
1603 
1604 	block_set(cli, 0);
1605 	return xfer_start(cli);
1606 }
1607 
bt_mesh_blob_cli_cancel(struct bt_mesh_blob_cli * cli)1608 void bt_mesh_blob_cli_cancel(struct bt_mesh_blob_cli *cli)
1609 {
1610 	if (!bt_mesh_blob_cli_is_busy(cli)) {
1611 		LOG_WRN("BLOB xfer already cancelled");
1612 		return;
1613 	}
1614 
1615 	LOG_DBG("");
1616 
1617 	if (cli->state == BT_MESH_BLOB_CLI_STATE_CAPS_GET ||
1618 	    cli->state == BT_MESH_BLOB_CLI_STATE_SUSPENDED) {
1619 		cli_state_reset(cli);
1620 		return;
1621 	}
1622 
1623 	cli->tx.cancelled = 1U;
1624 	cli->state = BT_MESH_BLOB_CLI_STATE_CANCEL;
1625 }
1626 
bt_mesh_blob_cli_xfer_progress_get(struct bt_mesh_blob_cli * cli,const struct bt_mesh_blob_cli_inputs * inputs)1627 int bt_mesh_blob_cli_xfer_progress_get(struct bt_mesh_blob_cli *cli,
1628 				       const struct bt_mesh_blob_cli_inputs *inputs)
1629 {
1630 	if (bt_mesh_blob_cli_is_busy(cli)) {
1631 		return -EBUSY;
1632 	}
1633 
1634 	cli->inputs = inputs;
1635 
1636 	check_transfer(cli);
1637 
1638 	return 0;
1639 }
1640 
bt_mesh_blob_cli_xfer_progress_active_get(struct bt_mesh_blob_cli * cli)1641 uint8_t bt_mesh_blob_cli_xfer_progress_active_get(struct bt_mesh_blob_cli *cli)
1642 {
1643 	if (cli->state < BT_MESH_BLOB_CLI_STATE_START) {
1644 		return 0;
1645 	}
1646 
1647 	return (100U * cli->block.number) / cli->block_count;
1648 }
1649 
bt_mesh_blob_cli_is_busy(struct bt_mesh_blob_cli * cli)1650 bool bt_mesh_blob_cli_is_busy(struct bt_mesh_blob_cli *cli)
1651 {
1652 	return cli->state != BT_MESH_BLOB_CLI_STATE_NONE;
1653 }
1654