Skip to content

Commit 7c7df36

Browse files
committed
net: coap_client: Add optional payload callback for uploads
Add an optional payload callback field to the coap_client_request structure, allowing the application to provide blocks of payload interactively during the resource upload, instead of having to provide entire payload in a single contigunous memory space. If registered, the CoAP client library will call the payload callback whenever a new PUT/POST message is being generated (note this is also true for retransmissions) instead of using the payload pointer/length. If the payload callback is NULL, then the library operates as usual. Signed-off-by: Robert Lubos <[email protected]>
1 parent 3b0ed56 commit 7c7df36

File tree

2 files changed

+120
-22
lines changed

2 files changed

+120
-22
lines changed

include/zephyr/net/coap_client.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,37 @@ struct coap_client_response_data {
6868
typedef void (*coap_client_response_cb_t)(const struct coap_client_response_data *data,
6969
void *user_data);
7070

71+
/**
72+
* @typedef coap_client_payload_cb_t
73+
* @brief Callback for providing a payload for the CoAP request.
74+
*
75+
* An optional callback for providing a payload for CoAP client requests. If set in
76+
* @ref coap_client_request, the CoAP client library will call this callback when
77+
* preparing a PUT/POST request (note that this is also true for retransmissions).
78+
*
79+
* When called, the library provides the application with the current payload offset
80+
* for the transfer and the payload block size. In return, the application sets the
81+
* payload pointer, payload size and information whether more data blocks are expected.
82+
* Setting the @p last_block parameter to false on the initial callback call triggers
83+
* a block transfer upload. The library will keep calling the callback until the
84+
* @p last_block parameter is set to false.
85+
*
86+
* @note If block transfer is used, the application is expected to provide full blocks of
87+
* payload. Only the final block (i.e. when @p last_block is set to true) can be shorter
88+
* than the requested block size.
89+
*
90+
* @param offset Payload offset from the beginning of a blockwise transfer.
91+
* @param payload A pointer for the buffer containing the payload block.
92+
* @param len Requested (maximum) block size on input. The actual payload length on output.
93+
* @param last_block A pointer to the flag indicating whether more payload blocks are expected.
94+
* @param user_data User provided context.
95+
*
96+
* @return Zero on success, a negative error code to abort upload.
97+
*/
98+
typedef int (*coap_client_payload_cb_t)(size_t offset, const uint8_t **payload,
99+
size_t *len, bool *last_block,
100+
void *user_data);
101+
71102
/**
72103
* @brief Representation of a CoAP client request.
73104
*/
@@ -78,6 +109,7 @@ struct coap_client_request {
78109
enum coap_content_format fmt; /**< Content format to be used */
79110
const uint8_t *payload; /**< User allocated buffer for send request */
80111
size_t len; /**< Length of the payload */
112+
coap_client_payload_cb_t payload_cb; /**< Optional payload callback */
81113
coap_client_response_cb_t cb; /**< Callback when response received */
82114
const struct coap_client_option *options; /**< Extra options to be added to request */
83115
uint8_t num_options; /**< Number of extra options */

subsys/net/lib/coap/coap_client.c

Lines changed: 88 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ static int coap_client_init_request(struct coap_client *client,
231231
}
232232

233233
/* Add content format option only if there is a payload */
234-
if (req->payload) {
234+
if (req->payload != NULL || req->payload_cb != NULL) {
235235
ret = coap_append_option_int(&internal_req->request,
236236
COAP_OPTION_CONTENT_FORMAT, req->fmt);
237237

@@ -272,14 +272,94 @@ static int coap_client_init_request(struct coap_client *client,
272272
}
273273
}
274274

275-
if (req->payload) {
275+
if (req->payload != NULL || req->payload_cb != NULL) {
276+
const uint8_t *payload = NULL;
277+
bool block_transfer = false;
276278
uint16_t payload_len;
277-
uint16_t offset;
278279

279-
/* Blockwise send ongoing, add block1 */
280-
if (internal_req->send_blk_ctx.total_size > 0 ||
281-
(req->len > CONFIG_COAP_CLIENT_MESSAGE_SIZE)) {
280+
/* Block transfer already in progress */
281+
if (internal_req->send_blk_ctx.total_size > 0) {
282+
block_transfer = true;
283+
}
284+
285+
if (req->payload_cb != NULL) {
286+
size_t offset = 0;
287+
size_t block_size = internal_req->send_blk_ctx.total_size > 0 ?
288+
coap_block_size_to_bytes(internal_req->send_blk_ctx.block_size) :
289+
CONFIG_COAP_CLIENT_BLOCK_SIZE;
290+
size_t len = block_size;
291+
bool last_block;
292+
293+
if (block_transfer) {
294+
offset = internal_req->send_blk_ctx.current;
295+
}
296+
297+
ret = req->payload_cb(offset, &payload, &len, &last_block,
298+
req->user_data);
299+
if (ret < 0) {
300+
LOG_ERR("Payload callback reported error, %d", ret);
301+
goto out;
302+
}
303+
304+
if (len > block_size || (len < block_size && !last_block)) {
305+
LOG_ERR("Invalid payload size");
306+
ret = -EINVAL;
307+
goto out;
308+
}
309+
310+
if (payload == NULL) {
311+
LOG_ERR("No payload provided");
312+
ret = -EINVAL;
313+
goto out;
314+
}
315+
316+
payload_len = len;
317+
318+
if (block_transfer && last_block) {
319+
/* If block transfer was used and it's a final
320+
* block, adjust the total size value.
321+
*/
322+
internal_req->send_blk_ctx.total_size =
323+
internal_req->send_blk_ctx.current + len;
324+
}
282325

326+
if (!last_block) {
327+
/* Expecting more blocks, enable block transfer.
328+
* Total length is yet unknown at this point,
329+
* so use SIZE_MAX for now.
330+
*/
331+
block_transfer = true;
332+
req->len = SIZE_MAX;
333+
}
334+
} else {
335+
payload = req->payload;
336+
payload_len = req->len;
337+
338+
if (block_transfer) {
339+
/* Block transfer already in progress, adjust
340+
* payload pointer and length.
341+
*/
342+
uint16_t block_in_bytes = coap_block_size_to_bytes(
343+
internal_req->send_blk_ctx.block_size);
344+
345+
payload_len = internal_req->send_blk_ctx.total_size -
346+
internal_req->send_blk_ctx.current;
347+
if (payload_len > block_in_bytes) {
348+
payload_len = block_in_bytes;
349+
}
350+
351+
payload = req->payload + internal_req->send_blk_ctx.current;
352+
} else if (req->len > CONFIG_COAP_CLIENT_MESSAGE_SIZE) {
353+
/* Otherwise, if payload won't fit, initialize
354+
* block transfer.
355+
*/
356+
block_transfer = true;
357+
payload_len = CONFIG_COAP_CLIENT_BLOCK_SIZE;
358+
}
359+
}
360+
361+
/* Blockwise send ongoing, add block1 */
362+
if (block_transfer) {
283363
if (internal_req->send_blk_ctx.total_size == 0) {
284364
coap_block_transfer_init(&internal_req->send_blk_ctx,
285365
coap_client_default_block_size(),
@@ -289,6 +369,7 @@ static int coap_client_init_request(struct coap_client *client,
289369

290370
memcpy(internal_req->request_tag, tag, COAP_TOKEN_MAX_LEN);
291371
}
372+
292373
ret = coap_append_block1_option(&internal_req->request,
293374
&internal_req->send_blk_ctx);
294375

@@ -314,22 +395,7 @@ static int coap_client_init_request(struct coap_client *client,
314395
goto out;
315396
}
316397

317-
if (internal_req->send_blk_ctx.total_size > 0) {
318-
uint16_t block_in_bytes =
319-
coap_block_size_to_bytes(internal_req->send_blk_ctx.block_size);
320-
321-
payload_len = internal_req->send_blk_ctx.total_size -
322-
internal_req->send_blk_ctx.current;
323-
if (payload_len > block_in_bytes) {
324-
payload_len = block_in_bytes;
325-
}
326-
offset = internal_req->send_blk_ctx.current;
327-
} else {
328-
payload_len = req->len;
329-
offset = 0;
330-
}
331-
332-
ret = coap_packet_append_payload(&internal_req->request, req->payload + offset,
398+
ret = coap_packet_append_payload(&internal_req->request, payload,
333399
payload_len);
334400

335401
if (ret < 0) {

0 commit comments

Comments
 (0)