summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorVinicius Costa Gomes <vinicius.gomes@intel.com>2016-09-02 19:15:30 -0300
committerAnas Nashif <nashif@linux.intel.com>2016-10-01 01:11:24 +0000
commit92a79f735175c862f05ff445e325ff47bd6de3b3 (patch)
tree03255170417916a6419e26d0007d7a02b568ca73 /lib
parent54c90bcbd57a529b544ba3eacd953ad36ddd9641 (diff)
iot/zoap: Add support for observing resources
This adds support for observing resources, for client and server side. For the server side we augment the resource struct so it can hold information about each interested observer. For client side, observing a resource abstracted as an reply that can be called multiple times. Change-Id: If3f0b41e302cff357ab891e6e91ec2d41579fb92 Signed-off-by: Vinicius Costa Gomes <vinicius.gomes@intel.com>
Diffstat (limited to 'lib')
-rw-r--r--lib/iot/zoap/zoap.c129
-rw-r--r--lib/iot/zoap/zoap.h61
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).