Bluetooth: Mesh: Keep sending Partial Block Report message
When the BLOB server is in Pull mode and all chunks were received for the current block, and the current block is the last block, then according to section 5.2.4 of MshMBTv1.0 the server should stop sending BLOB Partial Block Report messages if it determines that the client knows that the transfer is complete: ``` While the Pull BLOB State Machine is in the All Chunks Received state, the Pull BLOB State Machine continues to send the BLOB Partial Block Report messages until one of the following happens: • The Receive BLOB Timeout timer expires. • If the current block is not the last block, then the client starts a new block, in which case a new Pull BLOB State Machine is instantiated. • If the current block is the last block, then the server determines that the client knows the transfer is complete. For example, a higher-layer model may indicate that the client considers the transfer complete. ``` We currently don't have any OOB mean (for example, API) to determine whether the client knows that the transfer is complete. We also need to keep in mind that the Partial Block Report message can get lost so one transmission may not be enough. The client could immediately send BLOB Transfer Get message to get the transfer status, but this goes against its state machine defined in section 6.2.4.2, where a Block transmission completes when a BLOB Partial Block Report message is received with an empty list of requested chunks (table 6.4, figure 6.1). Because of this, we need to keep sending Partial Block Report messages. We can keep sending them at least until Block Report timer expires. If the client sends BLOB Transfer Get message, then it finished with sending the block and we can change the phase and finish the transfer. Signed-off-by: Pavel Vasilyev <pavel.vasilyev@nordicsemi.no>
This commit is contained in:
parent
136c5a9d57
commit
6a2c102aca
1 changed files with 56 additions and 14 deletions
|
@ -251,6 +251,37 @@ static void resume(struct bt_mesh_blob_srv *srv)
|
|||
reset_timer(srv);
|
||||
}
|
||||
|
||||
static void end(struct bt_mesh_blob_srv *srv)
|
||||
{
|
||||
phase_set(srv, BT_MESH_BLOB_XFER_PHASE_COMPLETE);
|
||||
k_work_cancel_delayable(&srv->rx_timeout);
|
||||
k_work_cancel_delayable(&srv->pull.report);
|
||||
io_close(srv);
|
||||
erase_state(srv);
|
||||
|
||||
if (srv->cb && srv->cb->end) {
|
||||
srv->cb->end(srv, srv->state.xfer.id, true);
|
||||
}
|
||||
}
|
||||
|
||||
static bool all_blocks_received(struct bt_mesh_blob_srv *srv)
|
||||
{
|
||||
for (int i = 0; i < ARRAY_SIZE(srv->state.blocks); ++i) {
|
||||
if (srv->state.blocks[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool pull_mode_xfer_complete(struct bt_mesh_blob_srv *srv)
|
||||
{
|
||||
return srv->state.xfer.mode == BT_MESH_BLOB_XFER_MODE_PULL &&
|
||||
srv->phase == BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_CHUNK &&
|
||||
all_blocks_received(srv);
|
||||
}
|
||||
|
||||
static void timeout(struct k_work *work)
|
||||
{
|
||||
struct bt_mesh_blob_srv *srv =
|
||||
|
@ -260,6 +291,8 @@ static void timeout(struct k_work *work)
|
|||
|
||||
if (srv->phase == BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_START) {
|
||||
cancel(srv);
|
||||
} else if (pull_mode_xfer_complete(srv)) {
|
||||
end(srv);
|
||||
} else {
|
||||
suspend(srv);
|
||||
}
|
||||
|
@ -388,6 +421,15 @@ static int handle_xfer_get(struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ct
|
|||
struct bt_mesh_blob_srv *srv = mod->user_data;
|
||||
|
||||
LOG_DBG("");
|
||||
|
||||
if (pull_mode_xfer_complete(srv)) {
|
||||
/* The client requested transfer. If we are in Pull mode and all blocks were
|
||||
* received, we should change the Transfer state here to Complete so that the client
|
||||
* receives the correct state.
|
||||
*/
|
||||
end(srv);
|
||||
}
|
||||
|
||||
xfer_status_rsp(srv, ctx, BT_MESH_BLOB_SUCCESS);
|
||||
|
||||
return 0;
|
||||
|
@ -685,7 +727,7 @@ static int handle_chunk(struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx,
|
|||
struct bt_mesh_blob_chunk chunk;
|
||||
size_t expected_size = 0;
|
||||
uint16_t idx;
|
||||
int i, err;
|
||||
int err;
|
||||
|
||||
idx = net_buf_simple_pull_le16(buf);
|
||||
chunk.size = buf->len;
|
||||
|
@ -745,26 +787,26 @@ static int handle_chunk(struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx,
|
|||
|
||||
atomic_clear_bit(srv->state.blocks, srv->block.number);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(srv->state.blocks); ++i) {
|
||||
if (!srv->state.blocks[i]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!all_blocks_received(srv)) {
|
||||
phase_set(srv, BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_BLOCK);
|
||||
store_state(srv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
phase_set(srv, BT_MESH_BLOB_XFER_PHASE_COMPLETE);
|
||||
k_work_cancel_delayable(&srv->rx_timeout);
|
||||
k_work_cancel_delayable(&srv->pull.report);
|
||||
io_close(srv);
|
||||
erase_state(srv);
|
||||
|
||||
if (srv->cb && srv->cb->end) {
|
||||
srv->cb->end(srv, srv->state.xfer.id, true);
|
||||
if (srv->state.xfer.mode == BT_MESH_BLOB_XFER_MODE_PULL) {
|
||||
/* By spec (section 5.2.4), the BLOB Server stops sending BLOB Partial Block Report
|
||||
* messages "If the current block is the last block, then the server determines that
|
||||
* the client knows the transfer is complete. For example, a higher-layer model may
|
||||
* indicate that the client considers the transfer complete."
|
||||
*
|
||||
* We don't have any way for higher-layer model to indicate that the transfer is
|
||||
* complete. Therefore we need to keep sending Partial Block Report messages until
|
||||
* the client sends BLOB Transfer Get message or the Block Timer expires.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
end(srv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue