iot/zoap: Add support for block sized transfers
This will add basic support for sending bodies of data that exceed the size of a single UDP packet. Block-wise transfers are defined in the recently adopted RFC 7959[1]. The RFC defines four options, two negotiating the block transfer, and two for informing the size of the transfer. Depending whether the packet is a request or a response, each option is defined as "control" (informative) or descriptive (describes what's in the payload). Change-Id: Ic71275558c4afed0298d20e8712f76d53904f89f Signed-off-by: Vinicius Costa Gomes <vinicius.gomes@intel.com>
This commit is contained in:
parent
669d581e0a
commit
b7833b9851
2 changed files with 304 additions and 1 deletions
|
@ -1028,3 +1028,219 @@ void zoap_header_set_id(struct zoap_packet *pkt, uint16_t id)
|
|||
|
||||
sys_put_be16(id, &appdata[2]);
|
||||
}
|
||||
|
||||
int zoap_block_transfer_init(struct zoap_block_context *ctx,
|
||||
enum zoap_block_size block_size,
|
||||
size_t total_size)
|
||||
{
|
||||
ctx->block_size = block_size;
|
||||
ctx->total_size = total_size;
|
||||
ctx->current = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define GET_BLOCK_SIZE(v) (((v) & 0x7))
|
||||
#define GET_MORE(v) (!!((v) & 0x08))
|
||||
#define GET_NUM(v) ((v) >> 4)
|
||||
|
||||
#define SET_BLOCK_SIZE(v, b) (v |= ((b) & 0x07))
|
||||
#define SET_MORE(v, m) ((v) |= (m) ? 0x08 : 0x00)
|
||||
#define SET_NUM(v, n) ((v) |= ((n) << 4))
|
||||
|
||||
static bool is_request(struct zoap_packet *pkt)
|
||||
{
|
||||
uint8_t code = zoap_header_get_code(pkt);
|
||||
|
||||
return !(code & ~ZOAP_REQUEST_MASK);
|
||||
}
|
||||
|
||||
int zoap_add_block1_option(struct zoap_packet *pkt,
|
||||
struct zoap_block_context *ctx)
|
||||
{
|
||||
uint16_t bytes = zoap_block_size_to_bytes(ctx->block_size);
|
||||
unsigned int val = 0;
|
||||
int r;
|
||||
|
||||
if (is_request(pkt)) {
|
||||
SET_BLOCK_SIZE(val, ctx->block_size);
|
||||
SET_MORE(val, ctx->current + bytes < ctx->total_size);
|
||||
SET_NUM(val, ctx->current / bytes);
|
||||
} else {
|
||||
SET_BLOCK_SIZE(val, ctx->block_size);
|
||||
SET_NUM(val, ctx->current / bytes);
|
||||
}
|
||||
|
||||
r = zoap_add_option_int(pkt, ZOAP_OPTION_BLOCK1, val);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int zoap_add_block2_option(struct zoap_packet *pkt,
|
||||
struct zoap_block_context *ctx)
|
||||
{
|
||||
int r, val = 0;
|
||||
uint16_t bytes = zoap_block_size_to_bytes(ctx->block_size);
|
||||
|
||||
if (is_request(pkt)) {
|
||||
SET_BLOCK_SIZE(val, ctx->block_size);
|
||||
SET_NUM(val, ctx->current / bytes);
|
||||
} else {
|
||||
SET_BLOCK_SIZE(val, ctx->block_size);
|
||||
SET_MORE(val, ctx->current + bytes < ctx->total_size);
|
||||
SET_NUM(val, ctx->current / bytes);
|
||||
}
|
||||
|
||||
r = zoap_add_option_int(pkt, ZOAP_OPTION_BLOCK2, val);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int zoap_add_size1_option(struct zoap_packet *pkt,
|
||||
struct zoap_block_context *ctx)
|
||||
{
|
||||
return zoap_add_option_int(pkt, ZOAP_OPTION_SIZE1, ctx->total_size);
|
||||
}
|
||||
|
||||
int zoap_add_size2_option(struct zoap_packet *pkt,
|
||||
struct zoap_block_context *ctx)
|
||||
{
|
||||
return zoap_add_option_int(pkt, ZOAP_OPTION_SIZE2, ctx->total_size);
|
||||
}
|
||||
|
||||
struct block_transfer {
|
||||
int num;
|
||||
int block_size;
|
||||
bool more;
|
||||
};
|
||||
|
||||
static unsigned int get_block_option(struct zoap_packet *pkt, uint16_t code)
|
||||
{
|
||||
struct zoap_option option;
|
||||
unsigned int val;
|
||||
int count = 1;
|
||||
|
||||
count = zoap_find_options(pkt, code, &option, count);
|
||||
if (count <= 0)
|
||||
return 0;
|
||||
|
||||
val = zoap_option_value_to_int(&option);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static int update_descriptive_block(struct zoap_block_context *ctx,
|
||||
int block, int size)
|
||||
{
|
||||
size_t new_current = GET_NUM(block) << (GET_BLOCK_SIZE(block) + 4);
|
||||
|
||||
if (block == 0) {
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
if (size && ctx->total_size && ctx->total_size != size) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ctx->block_size > 0 && GET_BLOCK_SIZE(block) > ctx->block_size) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ctx->total_size && new_current > ctx->total_size) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (size) {
|
||||
ctx->total_size = size;
|
||||
}
|
||||
ctx->current = new_current;
|
||||
ctx->block_size = GET_BLOCK_SIZE(block);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int update_control_block1(struct zoap_block_context *ctx,
|
||||
int block, int size)
|
||||
{
|
||||
size_t new_current = GET_NUM(block) << (GET_BLOCK_SIZE(block) + 4);
|
||||
|
||||
if (block == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (new_current != ctx->current) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (GET_BLOCK_SIZE(block) > ctx->block_size) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ctx->block_size = GET_BLOCK_SIZE(block);
|
||||
ctx->total_size = size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int update_control_block2(struct zoap_block_context *ctx,
|
||||
int block, int size)
|
||||
{
|
||||
size_t new_current = GET_NUM(block) << (GET_BLOCK_SIZE(block) + 4);
|
||||
|
||||
if (block == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (GET_MORE(block)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (GET_NUM(block) > 0 && GET_BLOCK_SIZE(block) != ctx->block_size) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ctx->current = new_current;
|
||||
ctx->block_size = GET_BLOCK_SIZE(block);
|
||||
ctx->total_size = size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int zoap_update_from_block(struct zoap_packet *pkt,
|
||||
struct zoap_block_context *ctx)
|
||||
{
|
||||
unsigned int block1, block2, size1, size2;
|
||||
int r;
|
||||
|
||||
block1 = get_block_option(pkt, ZOAP_OPTION_BLOCK1);
|
||||
block2 = get_block_option(pkt, ZOAP_OPTION_BLOCK2);
|
||||
size1 = get_block_option(pkt, ZOAP_OPTION_SIZE1);
|
||||
size2 = get_block_option(pkt, ZOAP_OPTION_SIZE2);
|
||||
|
||||
if (is_request(pkt)) {
|
||||
r = update_control_block2(ctx, block2, size2);
|
||||
if (r) {
|
||||
return r;
|
||||
}
|
||||
|
||||
return update_descriptive_block(ctx, block1, size1);
|
||||
}
|
||||
|
||||
r = update_control_block1(ctx, block1, size1);
|
||||
if (r) {
|
||||
return r;
|
||||
}
|
||||
|
||||
return update_descriptive_block(ctx, block2, size2);
|
||||
}
|
||||
|
||||
size_t zoap_next_block(struct zoap_block_context *ctx)
|
||||
{
|
||||
if (ctx->current >= ctx->total_size) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ctx->current += zoap_block_size_to_bytes(ctx->block_size);
|
||||
|
||||
return ctx->current;
|
||||
}
|
||||
|
|
|
@ -54,8 +54,12 @@ enum zoap_option_num {
|
|||
ZOAP_OPTION_URI_QUERY = 15,
|
||||
ZOAP_OPTION_ACCEPT = 17,
|
||||
ZOAP_OPTION_LOCATION_QUERY = 20,
|
||||
ZOAP_OPTION_BLOCK2 = 23,
|
||||
ZOAP_OPTION_BLOCK1 = 27,
|
||||
ZOAP_OPTION_SIZE2 = 28,
|
||||
ZOAP_OPTION_PROXY_URI = 35,
|
||||
ZOAP_OPTION_PROXY_SCHEME = 39
|
||||
ZOAP_OPTION_PROXY_SCHEME = 39,
|
||||
ZOAP_OPTION_SIZE1 = 60,
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -127,6 +131,7 @@ enum zoap_response_code {
|
|||
ZOAP_RESPONSE_CODE_NOT_FOUND = zoap_make_response_code(4, 4),
|
||||
ZOAP_RESPONSE_CODE_NOT_ALLOWED = zoap_make_response_code(4, 5),
|
||||
ZOAP_RESPONSE_CODE_NOT_ACCEPTABLE = zoap_make_response_code(4, 6),
|
||||
ZOAP_RESPONSE_CODE_INCOMPLETE = zoap_make_response_code(4, 8),
|
||||
ZOAP_RESPONSE_CODE_PRECONDITION_FAILED = zoap_make_response_code(4, 12),
|
||||
ZOAP_RESPONSE_CODE_REQUEST_TOO_LARGE = zoap_make_response_code(4, 13),
|
||||
ZOAP_RESPONSE_CODE_INTERNAL_ERROR = zoap_make_response_code(5, 0),
|
||||
|
@ -402,6 +407,88 @@ int zoap_add_option_int(struct zoap_packet *pkt, uint16_t code,
|
|||
int zoap_find_options(const struct zoap_packet *pkt, uint16_t code,
|
||||
struct zoap_option *options, uint16_t veclen);
|
||||
|
||||
/**
|
||||
* Represents the size of each block that will be transferred using
|
||||
* block-wise transfers [RFC7959]:
|
||||
*
|
||||
* Each entry maps directly to the value that is used in the wire.
|
||||
*
|
||||
* https://tools.ietf.org/html/rfc7959
|
||||
*/
|
||||
enum zoap_block_size {
|
||||
ZOAP_BLOCK_16,
|
||||
ZOAP_BLOCK_32,
|
||||
ZOAP_BLOCK_64,
|
||||
ZOAP_BLOCK_128,
|
||||
ZOAP_BLOCK_256,
|
||||
ZOAP_BLOCK_512,
|
||||
ZOAP_BLOCK_1024,
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper for converting the enumeration to the size expressed in bytes.
|
||||
*/
|
||||
static inline uint16_t zoap_block_size_to_bytes(
|
||||
enum zoap_block_size block_size)
|
||||
{
|
||||
return (1 << (block_size + 4));
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the current state of a block-wise transaction.
|
||||
*/
|
||||
struct zoap_block_context {
|
||||
size_t total_size;
|
||||
size_t current;
|
||||
enum zoap_block_size block_size;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initializes the context of a block-wise transfer.
|
||||
*/
|
||||
int zoap_block_transfer_init(struct zoap_block_context *ctx,
|
||||
enum zoap_block_size block_size,
|
||||
size_t total_size);
|
||||
|
||||
/**
|
||||
* Add BLOCK1 option to the packet.
|
||||
*/
|
||||
int zoap_add_block1_option(struct zoap_packet *pkt,
|
||||
struct zoap_block_context *ctx);
|
||||
|
||||
/**
|
||||
* Add BLOCK2 option to the packet.
|
||||
*/
|
||||
int zoap_add_block2_option(struct zoap_packet *pkt,
|
||||
struct zoap_block_context *ctx);
|
||||
|
||||
/**
|
||||
* Add SIZE1 option to the packet.
|
||||
*/
|
||||
int zoap_add_size1_option(struct zoap_packet *pkt,
|
||||
struct zoap_block_context *ctx);
|
||||
|
||||
/**
|
||||
* Add SIZE2 option to the packet.
|
||||
*/
|
||||
int zoap_add_size2_option(struct zoap_packet *pkt,
|
||||
struct zoap_block_context *ctx);
|
||||
|
||||
/**
|
||||
* Retrieves BLOCK{1,2} and SIZE{1,2} from @a pkt and updates
|
||||
* @a ctx accordingly.
|
||||
*
|
||||
* Returns an error if the packet contains invalid options.
|
||||
*/
|
||||
int zoap_update_from_block(struct zoap_packet *pkt,
|
||||
struct zoap_block_context *ctx);
|
||||
/**
|
||||
* Updates @a ctx so after this is called the current entry
|
||||
* indicates the correct offset in the body of data being
|
||||
* transferred.
|
||||
*/
|
||||
size_t zoap_next_block(struct zoap_block_context *ctx);
|
||||
|
||||
/**
|
||||
* Returns the version present in a CoAP packet.
|
||||
*/
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue