| /* |
| * Copyright (c) 2016 Intel Corporation |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <errno.h> |
| #include <misc/printk.h> |
| |
| #include <zephyr.h> |
| |
| #include <misc/byteorder.h> |
| #include <net/buf.h> |
| #include <net/net_pkt.h> |
| #include <net/net_mgmt.h> |
| #include <net/net_ip.h> |
| #include <net/zoap.h> |
| |
| #if defined(CONFIG_NET_L2_BLUETOOTH) |
| #include <bluetooth/bluetooth.h> |
| #include <gatt/ipss.h> |
| #endif |
| |
| #define MY_COAP_PORT 5683 |
| |
| #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 } } } |
| |
| static const struct sockaddr_in6 mcast_addr = { |
| .sin6_addr = ALL_NODES_LOCAL_COAP_MCAST, |
| .sin6_family = AF_INET6, |
| .sin6_port = htons(MY_COAP_PORT)}; |
| |
| static struct net_context *context; |
| |
| struct zoap_pending pendings[NUM_PENDINGS]; |
| struct zoap_reply replies[NUM_REPLIES]; |
| struct k_delayed_work retransmit_work; |
| |
| #if defined(CONFIG_NET_MGMT_EVENT) |
| static struct net_mgmt_event_callback cb; |
| #endif |
| |
| static const char * const test_path[] = { "test", NULL }; |
| |
| static void msg_dump(const char *s, u8_t *data, unsigned len) |
| { |
| unsigned i; |
| |
| printk("%s: ", s); |
| for (i = 0; i < len; i++) |
| printk("%02x ", data[i]); |
| printk("(%u bytes)\n", len); |
| } |
| |
| static int resource_reply_cb(const struct zoap_packet *response, |
| struct zoap_reply *reply, |
| const struct sockaddr *from) |
| { |
| struct net_pkt *pkt = response->pkt; |
| |
| msg_dump("reply", pkt->frags->data, pkt->frags->len); |
| |
| return 0; |
| } |
| |
| static void udp_receive(struct net_context *context, |
| struct net_pkt *pkt, |
| int status, |
| void *user_data) |
| { |
| struct zoap_pending *pending; |
| struct zoap_reply *reply; |
| struct zoap_packet response; |
| struct sockaddr_in6 from; |
| int header_len, r; |
| |
| /* |
| * zoap expects that buffer->data starts at the |
| * beginning of the CoAP header |
| */ |
| header_len = net_pkt_appdata(pkt) - pkt->frags->data; |
| net_buf_pull(pkt->frags, header_len); |
| |
| r = zoap_packet_parse(&response, pkt); |
| if (r < 0) { |
| printk("Invalid data received (%d)\n", r); |
| return; |
| } |
| |
| pending = zoap_pending_received(&response, pendings, |
| NUM_PENDINGS); |
| if (pending) { |
| /* If necessary cancel retransmissions */ |
| } |
| |
| net_ipaddr_copy(&from.sin6_addr, &NET_IPV6_HDR(pkt)->src); |
| from.sin6_port = NET_UDP_HDR(pkt)->src_port; |
| |
| reply = zoap_response_received(&response, |
| (const struct sockaddr *) &from, |
| replies, NUM_REPLIES); |
| if (!reply) { |
| printk("No handler for response (%d)\n", r); |
| return; |
| } |
| } |
| |
| static void retransmit_request(struct k_work *work) |
| { |
| struct zoap_pending *pending; |
| int r; |
| |
| pending = zoap_pending_next_to_expire(pendings, NUM_PENDINGS); |
| if (!pending) { |
| return; |
| } |
| |
| r = net_context_sendto(pending->pkt, (struct sockaddr *) &mcast_addr, |
| sizeof(mcast_addr), NULL, 0, NULL, NULL); |
| if (r < 0) { |
| return; |
| } |
| |
| if (!zoap_pending_cycle(pending)) { |
| zoap_pending_clear(pending); |
| return; |
| } |
| |
| k_delayed_work_submit(&retransmit_work, pending->timeout); |
| } |
| |
| static void event_iface_up(struct net_mgmt_event_callback *cb, |
| u32_t mgmt_event, struct net_if *iface) |
| { |
| static struct sockaddr_in6 any_addr = { .sin6_addr = IN6ADDR_ANY_INIT, |
| .sin6_family = AF_INET6 }; |
| struct zoap_packet request; |
| struct zoap_pending *pending; |
| struct zoap_reply *reply; |
| const char * const *p; |
| struct net_pkt *pkt; |
| struct net_buf *frag; |
| int r; |
| u8_t observe = 0; |
| |
| r = net_context_get(PF_INET6, SOCK_DGRAM, IPPROTO_UDP, &context); |
| if (r) { |
| printk("Could not get an UDP context\n"); |
| return; |
| } |
| |
| r = net_context_bind(context, (struct sockaddr *) &any_addr, |
| sizeof(any_addr)); |
| if (r) { |
| printk("Could not bind the context\n"); |
| return; |
| } |
| |
| r = net_context_recv(context, udp_receive, 0, NULL); |
| if (r) { |
| printk("Could not receive in the context\n"); |
| return; |
| } |
| |
| k_delayed_work_init(&retransmit_work, retransmit_request); |
| |
| pkt = net_pkt_get_tx(context, K_FOREVER); |
| if (!pkt) { |
| printk("Unable to get TX packet, not enough memory.\n"); |
| return; |
| } |
| |
| frag = net_pkt_get_data(context, K_FOREVER); |
| if (!frag) { |
| printk("Unable to get DATA buffer, not enough memory.\n"); |
| return; |
| } |
| |
| net_pkt_frag_add(pkt, frag); |
| |
| r = zoap_packet_init(&request, pkt); |
| 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()); |
| zoap_header_set_token(&request, zoap_next_token(), 8); |
| |
| /* 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, |
| (struct sockaddr *) &mcast_addr); |
| 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_context_sendto(pkt, (struct sockaddr *) &mcast_addr, |
| sizeof(mcast_addr), |
| NULL, 0, NULL, NULL); |
| if (r < 0) { |
| printk("Error sending the packet (%d).\n", r); |
| return; |
| } |
| |
| zoap_pending_cycle(pending); |
| |
| k_delayed_work_submit(&retransmit_work, pending->timeout); |
| |
| } |
| |
| void main(void) |
| { |
| struct net_if *iface = net_if_get_default(); |
| |
| #if defined(CONFIG_NET_L2_BLUETOOTH) |
| if (bt_enable(NULL)) { |
| NET_ERR("Bluetooth init failed\n"); |
| return; |
| } |
| #endif |
| |
| #if defined(CONFIG_NET_MGMT_EVENT) |
| /* Subscribe to NET_IF_UP if interface is not ready */ |
| if (!atomic_test_bit(iface->flags, NET_IF_UP)) { |
| net_mgmt_init_event_callback(&cb, event_iface_up, |
| NET_EVENT_IF_UP); |
| net_mgmt_add_event_callback(&cb); |
| return; |
| } |
| #endif |
| |
| event_iface_up(NULL, NET_EVENT_IF_UP, iface); |
| } |