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