libcoap  4.1.1
 All Data Structures Files Functions Variables Typedefs Macros Groups Pages
block.c
Go to the documentation of this file.
1 /* block.c -- block transfer
2  *
3  * Copyright (C) 2010--2012 Olaf Bergmann <bergmann@tzi.org>
4  *
5  * This file is part of the CoAP library libcoap. Please see
6  * README for terms of use.
7  */
8 
9 #include "config.h"
10 
11 #if defined(HAVE_ASSERT_H) && !defined(assert)
12 # include <assert.h>
13 #endif
14 
15 #include "debug.h"
16 #include "block.h"
17 
18 #define min(a,b) ((a) < (b) ? (a) : (b))
19 
20 #ifndef WITHOUT_BLOCK
21 unsigned int
22 coap_opt_block_num(const coap_opt_t *block_opt) {
23  unsigned int num = 0;
24  unsigned short len;
25 
26  len = coap_opt_length(block_opt);
27 
28  if (len == 0) {
29  return 0;
30  }
31 
32  if (len > 1) {
33  num = coap_decode_var_bytes(COAP_OPT_VALUE(block_opt),
34  COAP_OPT_LENGTH(block_opt) - 1);
35  }
36 
37  return (num << 4) | ((*COAP_OPT_BLOCK_LAST(block_opt) & 0xF0) >> 4);
38 }
39 
40 int
41 coap_get_block(coap_pdu_t *pdu, unsigned short type, coap_block_t *block) {
42  coap_opt_iterator_t opt_iter;
43  coap_opt_t *option;
44 
45  assert(block);
46  memset(block, 0, sizeof(coap_block_t));
47 
48  if (pdu && (option = coap_check_option(pdu, type, &opt_iter))) {
49  block->szx = COAP_OPT_BLOCK_SZX(option);
50  if (COAP_OPT_BLOCK_MORE(option))
51  block->m = 1;
52  block->num = coap_opt_block_num(option);
53  return 1;
54  }
55 
56  return 0;
57 }
58 
59 int
60 coap_write_block_opt(coap_block_t *block, unsigned short type,
61  coap_pdu_t *pdu, size_t data_length) {
62  size_t start, want, avail;
63  unsigned char buf[3];
64 
65  assert(pdu);
66 
67  /* Block2 */
68  if (type != COAP_OPTION_BLOCK2) {
69  warn("coap_write_block_opt: skipped unknown option\n");
70  return -1;
71  }
72 
73  start = block->num << (block->szx + 4);
74  if (data_length <= start) {
75  debug("illegal block requested\n");
76  return -2;
77  }
78 
79  avail = pdu->max_size - pdu->length - 4;
80  want = 1 << (block->szx + 4);
81 
82  /* check if entire block fits in message */
83  if (want <= avail) {
84  block->m = want < data_length - start;
85  } else {
86  /* Sender has requested a block that is larger than the remaining
87  * space in pdu. This is ok if the remaining data fits into the pdu
88  * anyway. The block size needs to be adjusted only if there is more
89  * data left that cannot be delivered in this message. */
90 
91  if (data_length - start <= avail) {
92 
93  /* it's the final block and everything fits in the message */
94  block->m = 0;
95  } else {
96  unsigned char szx;
97 
98  /* we need to decrease the block size */
99  if (avail < 16) { /* bad luck, this is the smallest block size */
100  debug("not enough space, even the smallest block does not fit");
101  return -3;
102  }
103  debug("decrease block size for %d to %d\n", avail, coap_fls(avail) - 5);
104  szx = block->szx;
105  block->szx = coap_fls(avail) - 5;
106  block->m = 1;
107  block->num <<= szx - block->szx;
108  }
109  }
110 
111  /* to re-encode the block option */
112  coap_add_option(pdu, type, coap_encode_var_bytes(buf, ((block->num << 4) |
113  (block->m << 3) |
114  block->szx)),
115  buf);
116 
117  return 1;
118 }
119 
120 int
121 coap_add_block(coap_pdu_t *pdu, unsigned int len, const unsigned char *data,
122  unsigned int block_num, unsigned char block_szx) {
123  size_t start;
124  start = block_num << (block_szx + 4);
125 
126  if (len <= start)
127  return 0;
128 
129  return coap_add_data(pdu,
130  min(len - start, (unsigned int)(1 << (block_szx + 4))),
131  data + start);
132 }
133 #endif /* WITHOUT_BLOCK */