blob: 45cff8ba04f2197058b9495cb8922db990035240 [file] [log] [blame]
/*
* Copyright (c) 2016 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <errno.h>
#include <stdio.h>
#include <misc/byteorder.h>
#include <misc/nano_work.h>
#include <net/net_core.h>
#include <net/net_socket.h>
#include <net/net_ip.h>
#include <net/ip_buf.h>
#include <net_testing.h>
#include <zoap.h>
#define MY_COAP_PORT 5683
#define STACKSIZE 2000
#define NUM_PENDINGS 3
#define NUM_REPLIES 3
#define ALL_NODES_LOCAL_COAP_MCAST \
{ { { 0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xfd } } }
char fiberStack[STACKSIZE];
static struct net_context *send_context, *receive_context;
struct zoap_pending pendings[NUM_PENDINGS];
struct zoap_reply replies[NUM_REPLIES];
struct nano_delayed_work retransmit_work;
static const char * const test_path[] = { "test", NULL };
static void msg_dump(const char *s, uint8_t *data, unsigned len)
{
unsigned i;
printf("%s: ", s);
for (i = 0; i < len; i++)
printf("%02x ", data[i]);
printf("(%u bytes)\n", len);
}
static int resource_reply_cb(const struct zoap_packet *response,
struct zoap_reply *reply,
const uip_ipaddr_t *addr,
uint16_t port)
{
struct net_buf *buf = response->buf;
msg_dump("reply", buf->data, buf->len);
return 0;
}
static void udp_receive(void)
{
struct zoap_packet response;
struct zoap_pending *pending;
struct zoap_reply *reply;
struct net_buf *buf;
int r;
while (true) {
struct uip_conn *conn;
buf = net_receive(receive_context, TICKS_UNLIMITED);
if (!buf) {
continue;
}
r = zoap_packet_parse(&response, buf);
if (r < 0) {
printf("Invalid data received (%d)\n", r);
continue;
}
conn = uip_conn(buf);
pending = zoap_pending_received(&response, pendings,
NUM_PENDINGS);
if (pending) {
/* If necessary cancel retransmissions */
}
reply = zoap_response_received(&response,
&conn->ripaddr,
sys_be16_to_cpu(conn->rport),
replies, NUM_REPLIES);
if (!reply) {
printf("No handler for response (%d)\n", r);
continue;
}
}
}
static void retransmit_request(struct nano_work *work)
{
struct zoap_pending *pending;
struct zoap_packet *request;
struct net_buf *buf;
int r, timeout;
pending = zoap_pending_next_to_expire(pendings, NUM_PENDINGS);
if (!pending) {
return;
}
request = &pending->request;
buf = request->buf;
r = net_send(buf);
if (r < 0) {
return;
}
if (!zoap_pending_cycle(pending)) {
net_buf_unref(buf);
zoap_pending_clear(pending);
return;
}
timeout = pending->timeout * (sys_clock_ticks_per_sec / MSEC_PER_SEC);
nano_delayed_work_submit(&retransmit_work, timeout);
}
void main(void)
{
static struct net_addr mcast_addr = {
.in6_addr = ALL_NODES_LOCAL_COAP_MCAST,
.family = AF_INET6 };
static struct net_addr any_addr = { .in6_addr = IN6ADDR_ANY_INIT,
.family = AF_INET6 };
struct zoap_packet request;
struct zoap_pending *pending;
struct zoap_reply *reply;
const char * const *p;
struct net_buf *buf;
int r, timeout;
uint8_t observe = 0;
net_init();
net_testing_setup();
send_context = net_context_get(IPPROTO_UDP,
&mcast_addr, MY_COAP_PORT,
&any_addr, MY_COAP_PORT);
receive_context = net_context_get(IPPROTO_UDP,
&any_addr, MY_COAP_PORT,
&mcast_addr, MY_COAP_PORT);
task_fiber_start(&fiberStack[0], STACKSIZE,
(nano_fiber_entry_t) udp_receive, 0, 0, 7, 0);
nano_delayed_work_init(&retransmit_work, retransmit_request);
buf = ip_buf_get_tx(send_context);
if (!buf) {
printk("Unable to get TX buffer, not enough memory.\n");
return;
}
r = zoap_packet_init(&request, buf);
if (r < 0) {
return;
}
/* FIXME: Could be that zoap_packet_init() sets some defaults */
zoap_header_set_version(&request, 1);
zoap_header_set_type(&request, ZOAP_TYPE_CON);
zoap_header_set_code(&request, ZOAP_METHOD_GET);
zoap_header_set_id(&request, zoap_next_id());
/* Enable observing the resource. */
r = zoap_add_option(&request, ZOAP_OPTION_OBSERVE,
&observe, sizeof(observe));
if (r < 0) {
printk("Unable add option to request.\n");
return;
}
for (p = test_path; p && *p; p++) {
r = zoap_add_option(&request, ZOAP_OPTION_URI_PATH,
*p, strlen(*p));
if (r < 0) {
printk("Unable add option to request.\n");
return;
}
}
pending = zoap_pending_next_unused(pendings, NUM_PENDINGS);
if (!pending) {
printk("Unable to find a free pending to track "
"retransmissions.\n");
return;
}
r = zoap_pending_init(pending, &request);
if (r < 0) {
printk("Unable to initialize a pending retransmission.\n");
return;
}
reply = zoap_reply_next_unused(replies, NUM_REPLIES);
if (!reply) {
printk("No resources for waiting for replies.\n");
return;
}
zoap_reply_init(reply, &request);
reply->reply = resource_reply_cb;
r = net_send(buf);
if (r < 0) {
printk("Error sending the packet (%d).\n", r);
}
zoap_pending_cycle(pending);
timeout = pending->timeout * (sys_clock_ticks_per_sec / MSEC_PER_SEC);
nano_delayed_work_submit(&retransmit_work, timeout);
}