summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorVinicius Costa Gomes <vinicius.gomes@intel.com>2016-09-14 11:39:15 -0300
committerAnas Nashif <nashif@linux.intel.com>2016-10-25 12:56:51 +0000
commitb7833b9851863b64416b39c93d4540b7ff152bdb (patch)
tree27344e17241774b1b3d10ba1b9dc03275f8e51ce /lib
parent669d581e0adb05209eb342b06968d372af194ffc (diff)
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>
Diffstat (limited to 'lib')
-rw-r--r--lib/iot/zoap/zoap.c216
-rw-r--r--lib/iot/zoap/zoap.h89
2 files changed, 304 insertions, 1 deletions
diff --git a/lib/iot/zoap/zoap.c b/lib/iot/zoap/zoap.c
index d50b00410..2e2d90d9e 100644
--- a/lib/iot/zoap/zoap.c
+++ b/lib/iot/zoap/zoap.c
@@ -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;
+}
diff --git a/lib/iot/zoap/zoap.h b/lib/iot/zoap/zoap.h
index 6f0b32e77..d5e518cfe 100644
--- a/lib/iot/zoap/zoap.h
+++ b/lib/iot/zoap/zoap.h
@@ -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),
@@ -403,6 +408,88 @@ 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.
*/
uint8_t zoap_header_get_version(const struct zoap_packet *pkt);