diff options
-rw-r--r-- | lib/iot/zoap/zoap.c | 129 | ||||
-rw-r--r-- | lib/iot/zoap/zoap.h | 61 |
2 files changed, 190 insertions, 0 deletions
diff --git a/lib/iot/zoap/zoap.c b/lib/iot/zoap/zoap.c index 79cf5d750..fa551b65c 100644 --- a/lib/iot/zoap/zoap.c +++ b/lib/iot/zoap/zoap.c @@ -374,6 +374,21 @@ struct zoap_reply *zoap_reply_next_unused( return NULL; } +struct zoap_observer *zoap_observer_next_unused( + struct zoap_observer *observers, size_t len) +{ + struct zoap_observer *o; + size_t i; + + for (i = 0, o = observers; i < len; i++, o++) { + if (uip_is_addr_unspecified(&o->addr)) { + return o; + } + } + + return NULL; +} + static bool match_response(const struct zoap_packet *request, const struct zoap_packet *response) { @@ -542,6 +557,35 @@ int zoap_handle_request(struct zoap_packet *pkt, return -ENOENT; } +static int get_observe_option(const struct zoap_packet *pkt) +{ + struct zoap_option option = {}; + uint16_t count = 1; + int r; + + r = zoap_find_options(pkt, ZOAP_OPTION_OBSERVE, &option, count); + if (r <= 0) { + return -ENOENT; + } + + /* The value is in the network order, and has at max 3 bytes. */ + switch (option.len) { + case 0: + return 0; + case 1: + return option.value[0]; + case 2: + return (option.value[0] << 0) | (option.value[1] << 8); + case 3: + return (option.value[0] << 0) | (option.value[1] << 8) | + (option.value[2] << 16); + default: + return -EINVAL; + } + + return -ENOENT; +} + struct zoap_reply *zoap_response_received( const struct zoap_packet *response, const void *from, struct zoap_reply *replies, size_t len) @@ -554,6 +598,8 @@ struct zoap_reply *zoap_response_received( token = zoap_header_get_token(response, &tkl); for (i = 0, r = replies; i < len; i++, r++) { + int age; + if (r->tkl != tkl) { continue; } @@ -562,6 +608,19 @@ struct zoap_reply *zoap_response_received( continue; } + age = get_observe_option(response); + if (age > 0) { + /* + * age == 2 means that the notifications wrapped, + * or this is the first one + */ + if (r->age > age && age != 2) { + continue; + } + + r->age = age; + } + r->reply(response, r, from); return r; } @@ -574,6 +633,7 @@ void zoap_reply_init(struct zoap_reply *reply, { const uint8_t *token; uint8_t tkl; + int age; token = zoap_header_get_token(request, &tkl); @@ -581,6 +641,13 @@ void zoap_reply_init(struct zoap_reply *reply, memcpy(reply->token, token, tkl); } reply->tkl = tkl; + + age = get_observe_option(request); + + /* It means that the request enabled observing a resource */ + if (age == 0) { + reply->age = 2; + } } void zoap_reply_clear(struct zoap_reply *reply) @@ -588,6 +655,68 @@ void zoap_reply_clear(struct zoap_reply *reply) reply->reply = NULL; } +int zoap_resource_notify(struct zoap_resource *resource) +{ + sys_snode_t *node; + + resource->age++; + + SYS_SLIST_FOR_EACH_NODE(&resource->observers, node) { + struct zoap_observer *o = (struct zoap_observer *) node; + + resource->notify(resource, o); + } + + return 0; +} + +bool zoap_request_is_observe(const struct zoap_packet *request) +{ + return get_observe_option(request) == 0; +} + +void zoap_observer_init(struct zoap_observer *observer, + const struct zoap_packet *request, + const uip_ipaddr_t *addr, + uint16_t port) +{ + const uint8_t *token; + uint8_t tkl; + + token = zoap_header_get_token(request, &tkl); + + if (tkl > 0) { + memcpy(observer->token, token, tkl); + } + + observer->tkl = tkl; + + /* FIXME: new network stack */ + uip_ipaddr_copy(&observer->addr, addr); + observer->port = port; +} + +bool zoap_register_observer(struct zoap_resource *resource, + struct zoap_observer *observer) +{ + bool first; + + sys_slist_append(&resource->observers, &observer->list); + + first = resource->age == 0; + if (first) { + resource->age = 2; + } + + return first; +} + +void zoap_remove_observer(struct zoap_resource *resource, + struct zoap_observer *observer) +{ + sys_slist_find_and_remove(&resource->observers, &observer->list); +} + uint8_t *zoap_packet_get_payload(struct zoap_packet *pkt, uint16_t *len) { struct net_buf *buf = pkt->buf; diff --git a/lib/iot/zoap/zoap.h b/lib/iot/zoap/zoap.h index e6b6e867a..00e20afd6 100644 --- a/lib/iot/zoap/zoap.h +++ b/lib/iot/zoap/zoap.h @@ -139,6 +139,7 @@ enum zoap_response_code { #define ZOAP_CODE_EMPTY (0) +struct zoap_observer; struct zoap_packet; struct zoap_pending; struct zoap_reply; @@ -153,6 +154,13 @@ typedef int (*zoap_method_t)(struct zoap_resource *resource, const void *from); /** + * Type of the callback being called when a resource's has observers to be + * informed when an update happens. + */ +typedef void (*zoap_notify_t)(struct zoap_resource *resource, + struct zoap_observer *observer); + +/** * @brief Description of CoAP resource. * * CoAP servers often want to register resources, so that clients can act on @@ -160,12 +168,25 @@ typedef int (*zoap_method_t)(struct zoap_resource *resource, */ struct zoap_resource { zoap_method_t get, post, put, del; + zoap_notify_t notify; const char * const *path; void *user_data; + sys_slist_t observers; int age; }; /** + * Represents a remote device that is observing a local resource. + */ +struct zoap_observer { + sys_snode_t list; + uip_ipaddr_t addr; + uint16_t port; + uint8_t token[8]; + uint8_t tkl; +}; + +/** * Representation of a CoAP packet. */ struct zoap_packet { @@ -195,11 +216,40 @@ struct zoap_pending { struct zoap_reply { zoap_reply_t reply; void *user_data; + int age; uint8_t token[8]; uint8_t tkl; }; /** + * Indicates that the remote device referenced by @a addr, with @a request, + * wants to observe a resource. + */ +void zoap_observer_init(struct zoap_observer *observer, + const struct zoap_packet *request, + const uip_ipaddr_t *addr, + uint16_t port); + +/** + * After the observer is initialized, associate the observer with an resource. + */ +bool zoap_register_observer(struct zoap_resource *resource, + struct zoap_observer *observer); + +/** + * Remove this observer from the list of registered observers of + * that resource. + */ +void zoap_remove_observer(struct zoap_resource *resource, + struct zoap_observer *observer); + +/** + * Returns the next available observer representation. + */ +struct zoap_observer *zoap_observer_next_unused( + struct zoap_observer *observers, size_t len); + +/** * Indicates that a reply is expected for @a request. */ void zoap_reply_init(struct zoap_reply *reply, @@ -299,6 +349,17 @@ int zoap_handle_request(struct zoap_packet *pkt, const void *from); /** + * Indicates that this resource was updated and that the @a notify callback + * should be called for every registered observer. + */ +int zoap_resource_notify(struct zoap_resource *resource); + +/** + * Returns if this request is enabling observing a resource. + */ +bool zoap_request_is_observe(const struct zoap_packet *request); + +/** * Returns a pointer to the start of the payload, and how much memory * is available (to the payload), it will also insert the * COAP_MARKER (0xFF). |