| /* |
| * Copyright (c) 2016 Intel Corporation |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #if 1 |
| #define SYS_LOG_DOMAIN "zoap-server" |
| #define NET_SYS_LOG_LEVEL SYS_LOG_LEVEL_DEBUG |
| #define NET_LOG_ENABLED 1 |
| #endif |
| |
| #include <errno.h> |
| |
| #include <zephyr.h> |
| #include <board.h> |
| |
| #include <misc/byteorder.h> |
| #include <net/net_core.h> |
| #include <net/net_ip.h> |
| #include <net/nbuf.h> |
| #include <net/net_context.h> |
| |
| #include <net_private.h> |
| |
| #include <gpio.h> |
| |
| #if defined(CONFIG_NET_L2_BLUETOOTH) |
| #include <bluetooth/bluetooth.h> |
| #include <gatt/ipss.h> |
| #endif |
| |
| #if defined(CONFIG_NET_L2_IEEE802154) |
| #include <ieee802154_settings.h> |
| #endif |
| |
| #include <net/zoap.h> |
| #include <net/zoap_link_format.h> |
| |
| #define MY_COAP_PORT 5683 |
| |
| #define ALL_NODES_LOCAL_COAP_MCAST \ |
| { { { 0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xfd } } } |
| |
| #define MY_IP6ADDR \ |
| { { { 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1 } } } |
| |
| #if defined(LED0_GPIO_PORT) |
| #define LED_GPIO_NAME LED0_GPIO_PORT |
| #define LED_PIN LED0_GPIO_PIN |
| #else |
| #define LED_GPIO_NAME "(fail)" |
| #define LED_PIN 0 |
| #endif |
| |
| static struct net_context *context; |
| |
| static struct device *led0; |
| |
| static const char led_on[] = "LED ON\n"; |
| static const char led_off[] = "LED OFF\n"; |
| static const char led_toggle_on[] = "LED Toggle ON\n"; |
| static const char led_toggle_off[] = "LED Toggle OFF\n"; |
| |
| static bool fake_led; |
| |
| static bool read_led(void) |
| { |
| uint32_t led = 0; |
| int r; |
| |
| if (!led0) { |
| return fake_led; |
| } |
| |
| r = gpio_pin_read(led0, LED_PIN, &led); |
| if (r < 0) { |
| return false; |
| } |
| |
| return !led; |
| } |
| |
| static void write_led(bool led) |
| { |
| if (!led0) { |
| fake_led = led; |
| return; |
| } |
| |
| gpio_pin_write(led0, LED_PIN, !led); |
| } |
| |
| static int led_get(struct zoap_resource *resource, |
| struct zoap_packet *request, |
| const struct sockaddr *from) |
| { |
| struct net_buf *buf, *frag; |
| struct zoap_packet response; |
| const char *str; |
| uint8_t *payload; |
| uint16_t len, id; |
| int r; |
| |
| id = zoap_header_get_id(request); |
| |
| buf = net_nbuf_get_tx(context, K_FOREVER); |
| if (!buf) { |
| return -ENOMEM; |
| } |
| |
| frag = net_nbuf_get_data(context, K_FOREVER); |
| if (!frag) { |
| return -ENOMEM; |
| } |
| |
| net_buf_frag_add(buf, frag); |
| |
| r = zoap_packet_init(&response, buf); |
| if (r < 0) { |
| return -EINVAL; |
| } |
| |
| /* FIXME: Could be that zoap_packet_init() sets some defaults */ |
| zoap_header_set_version(&response, 1); |
| zoap_header_set_type(&response, ZOAP_TYPE_ACK); |
| zoap_header_set_code(&response, ZOAP_RESPONSE_CODE_CONTENT); |
| zoap_header_set_id(&response, id); |
| |
| payload = zoap_packet_get_payload(&response, &len); |
| if (!payload) { |
| return -EINVAL; |
| } |
| |
| if (read_led()) { |
| str = led_on; |
| r = sizeof(led_on); |
| } else { |
| str = led_off; |
| r = sizeof(led_off); |
| } |
| |
| memcpy(payload, str, r); |
| |
| r = zoap_packet_set_used(&response, r); |
| if (r) { |
| return -EINVAL; |
| } |
| |
| r = net_context_sendto(buf, from, sizeof(struct sockaddr_in6), |
| NULL, 0, NULL, NULL); |
| if (r < 0) { |
| net_nbuf_unref(buf); |
| } |
| |
| return r; |
| } |
| |
| static int led_post(struct zoap_resource *resource, |
| struct zoap_packet *request, |
| const struct sockaddr *from) |
| { |
| struct net_buf *buf, *frag; |
| struct zoap_packet response; |
| const char *str; |
| uint8_t *payload; |
| uint16_t len, id; |
| uint32_t led; |
| int r; |
| |
| payload = zoap_packet_get_payload(request, &len); |
| if (!payload) { |
| printk("packet without payload"); |
| return -EINVAL; |
| } |
| |
| id = zoap_header_get_id(request); |
| |
| buf = net_nbuf_get_tx(context, K_FOREVER); |
| if (!buf) { |
| return -ENOMEM; |
| } |
| |
| frag = net_nbuf_get_data(context, K_FOREVER); |
| if (!frag) { |
| return -ENOMEM; |
| } |
| |
| net_buf_frag_add(buf, frag); |
| |
| r = zoap_packet_init(&response, buf); |
| if (r < 0) { |
| return -EINVAL; |
| } |
| |
| /* FIXME: Could be that zoap_packet_init() sets some defaults */ |
| zoap_header_set_version(&response, 1); |
| zoap_header_set_type(&response, ZOAP_TYPE_ACK); |
| zoap_header_set_code(&response, ZOAP_RESPONSE_CODE_CONTENT); |
| zoap_header_set_id(&response, id); |
| |
| payload = zoap_packet_get_payload(&response, &len); |
| if (!payload) { |
| return -EINVAL; |
| } |
| |
| led = read_led(); |
| |
| led = !led; |
| |
| write_led(led); |
| |
| if (led) { |
| str = led_toggle_on; |
| r = sizeof(led_toggle_on); |
| } else { |
| str = led_toggle_off; |
| r = sizeof(led_toggle_off); |
| } |
| |
| memcpy(payload, str, r); |
| |
| r = zoap_packet_set_used(&response, r); |
| if (r) { |
| return -EINVAL; |
| } |
| |
| r = net_context_sendto(buf, from, sizeof(struct sockaddr_in6), |
| NULL, 0, NULL, NULL); |
| if (r < 0) { |
| net_nbuf_unref(buf); |
| } |
| |
| return r; |
| } |
| |
| static int led_put(struct zoap_resource *resource, |
| struct zoap_packet *request, |
| const struct sockaddr *from) |
| { |
| struct net_buf *buf, *frag; |
| struct zoap_packet response; |
| const char *str; |
| uint8_t *payload; |
| uint16_t len, id; |
| uint32_t led; |
| int r; |
| |
| payload = zoap_packet_get_payload(request, &len); |
| if (!payload) { |
| printk("packet without payload"); |
| return -EINVAL; |
| } |
| |
| led = 0; |
| if (len > 0 && payload[0] == '1') { |
| led = 1; |
| } |
| |
| id = zoap_header_get_id(request); |
| |
| buf = net_nbuf_get_tx(context, K_FOREVER); |
| if (!buf) { |
| return -ENOMEM; |
| } |
| |
| frag = net_nbuf_get_data(context, K_FOREVER); |
| if (!frag) { |
| return -ENOMEM; |
| } |
| |
| net_buf_frag_add(buf, frag); |
| |
| r = zoap_packet_init(&response, buf); |
| if (r < 0) { |
| return -EINVAL; |
| } |
| |
| /* FIXME: Could be that zoap_packet_init() sets some defaults */ |
| zoap_header_set_version(&response, 1); |
| zoap_header_set_type(&response, ZOAP_TYPE_ACK); |
| zoap_header_set_code(&response, ZOAP_RESPONSE_CODE_CHANGED); |
| zoap_header_set_id(&response, id); |
| |
| payload = zoap_packet_get_payload(&response, &len); |
| if (!payload) { |
| return -EINVAL; |
| } |
| |
| write_led(led); |
| |
| if (led) { |
| str = led_on; |
| r = sizeof(led_on); |
| } else { |
| str = led_off; |
| r = sizeof(led_off); |
| } |
| |
| memcpy(payload, str, r); |
| |
| r = zoap_packet_set_used(&response, r); |
| if (r) { |
| return -EINVAL; |
| } |
| |
| r = net_context_sendto(buf, from, sizeof(struct sockaddr_in6), |
| NULL, 0, NULL, NULL); |
| if (r < 0) { |
| net_nbuf_unref(buf); |
| } |
| |
| return r; |
| } |
| |
| static int dummy_get(struct zoap_resource *resource, |
| struct zoap_packet *request, |
| const struct sockaddr *from) |
| { |
| static const char dummy_str[] = "Just a test\n"; |
| struct net_buf *buf, *frag; |
| struct zoap_packet response; |
| uint8_t *payload; |
| uint16_t len, id; |
| int r; |
| |
| id = zoap_header_get_id(request); |
| |
| buf = net_nbuf_get_tx(context, K_FOREVER); |
| if (!buf) { |
| return -ENOMEM; |
| } |
| |
| frag = net_nbuf_get_data(context, K_FOREVER); |
| if (!frag) { |
| return -ENOMEM; |
| } |
| |
| net_buf_frag_add(buf, frag); |
| |
| r = zoap_packet_init(&response, buf); |
| if (r < 0) { |
| return -EINVAL; |
| } |
| |
| /* FIXME: Could be that zoap_packet_init() sets some defaults */ |
| zoap_header_set_version(&response, 1); |
| zoap_header_set_type(&response, ZOAP_TYPE_ACK); |
| zoap_header_set_code(&response, ZOAP_RESPONSE_CODE_CONTENT); |
| zoap_header_set_id(&response, id); |
| |
| payload = zoap_packet_get_payload(&response, &len); |
| if (!payload) { |
| return -EINVAL; |
| } |
| |
| memcpy(payload, dummy_str, sizeof(dummy_str)); |
| |
| r = zoap_packet_set_used(&response, sizeof(dummy_str)); |
| if (r) { |
| return -EINVAL; |
| } |
| |
| r = net_context_sendto(buf, from, sizeof(struct sockaddr_in6), |
| NULL, 0, NULL, NULL); |
| if (r < 0) { |
| net_nbuf_unref(buf); |
| } |
| |
| return r; |
| } |
| |
| static const char * const led_default_path[] = { "led", NULL }; |
| static const char * const led_default_attributes[] = { |
| "title=\"LED\"", |
| "rt=Text", |
| NULL }; |
| |
| static const char * const dummy_path[] = { "dummy", NULL }; |
| static const char * const dummy_attributes[] = { |
| "title=\"Dummy\"", |
| "rt=dummy", |
| NULL }; |
| |
| static struct zoap_resource resources[] = { |
| ZOAP_WELL_KNOWN_CORE_RESOURCE, |
| { .get = led_get, |
| .post = led_post, |
| .put = led_put, |
| .path = led_default_path, |
| .user_data = &((struct zoap_core_metadata) { |
| .attributes = led_default_attributes, |
| }), |
| }, |
| { .get = dummy_get, |
| .path = dummy_path, |
| .user_data = &((struct zoap_core_metadata) { |
| .attributes = dummy_attributes, |
| }), |
| }, |
| { }, |
| }; |
| |
| static void udp_receive(struct net_context *context, |
| struct net_buf *buf, |
| int status, |
| void *user_data) |
| { |
| struct zoap_packet request; |
| struct sockaddr_in6 from; |
| int r, header_len; |
| |
| net_ipaddr_copy(&from.sin6_addr, &NET_IPV6_BUF(buf)->src); |
| from.sin6_port = NET_UDP_BUF(buf)->src_port; |
| from.sin6_family = AF_INET6; |
| |
| /* |
| * zoap expects that buffer->data starts at the |
| * beginning of the CoAP header |
| */ |
| header_len = net_nbuf_appdata(buf) - buf->frags->data; |
| net_buf_pull(buf->frags, header_len); |
| |
| r = zoap_packet_parse(&request, buf); |
| if (r < 0) { |
| NET_ERR("Invalid data received (%d)\n", r); |
| net_nbuf_unref(buf); |
| return; |
| } |
| |
| r = zoap_handle_request(&request, resources, |
| (const struct sockaddr *) &from); |
| |
| net_nbuf_unref(buf); |
| |
| if (r < 0) { |
| NET_ERR("No handler for such request (%d)\n", r); |
| return; |
| } |
| } |
| |
| static bool join_coap_multicast_group(void) |
| { |
| static struct in6_addr my_addr = MY_IP6ADDR; |
| static struct sockaddr_in6 mcast_addr = { |
| .sin6_family = AF_INET6, |
| .sin6_addr = ALL_NODES_LOCAL_COAP_MCAST, |
| .sin6_port = htons(MY_COAP_PORT) }; |
| struct net_if_mcast_addr *mcast; |
| struct net_if_addr *ifaddr; |
| struct net_if *iface; |
| |
| iface = net_if_get_default(); |
| if (!iface) { |
| NET_ERR("Could not get te default interface\n"); |
| return false; |
| } |
| |
| ifaddr = net_if_ipv6_addr_add(net_if_get_default(), |
| &my_addr, NET_ADDR_MANUAL, 0); |
| ifaddr->addr_state = NET_ADDR_PREFERRED; |
| |
| mcast = net_if_ipv6_maddr_add(iface, &mcast_addr.sin6_addr); |
| if (!mcast) { |
| NET_ERR("Could not add multicast address to interface\n"); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| void main(void) |
| { |
| static struct sockaddr_in6 any_addr = { |
| .sin6_family = AF_INET6, |
| .sin6_addr = IN6ADDR_ANY_INIT, |
| .sin6_port = htons(MY_COAP_PORT) }; |
| int r; |
| |
| #if defined(CONFIG_NET_L2_BLUETOOTH) |
| if (bt_enable(NULL)) { |
| NET_ERR("Bluetooth init failed"); |
| return; |
| } |
| ipss_init(); |
| ipss_advertise(); |
| #endif |
| |
| #if defined(CONFIG_NET_L2_IEEE802154) |
| if (ieee802154_sample_setup()) { |
| NET_ERR("IEEE 802.15.4 setup failed"); |
| return; |
| } |
| #endif |
| |
| led0 = device_get_binding(LED_GPIO_NAME); |
| /* Want it to be NULL if not available */ |
| |
| if (led0) { |
| gpio_pin_configure(led0, LED_PIN, GPIO_DIR_OUT); |
| } |
| |
| if (!join_coap_multicast_group()) { |
| NET_ERR("Could not join CoAP multicast group\n"); |
| return; |
| } |
| |
| r = net_context_get(PF_INET6, SOCK_DGRAM, IPPROTO_UDP, &context); |
| if (r) { |
| NET_ERR("Could not get an UDP context\n"); |
| return; |
| } |
| |
| r = net_context_bind(context, (struct sockaddr *) &any_addr, |
| sizeof(any_addr)); |
| if (r) { |
| NET_ERR("Could not bind the context\n"); |
| return; |
| } |
| |
| r = net_context_recv(context, udp_receive, 0, NULL); |
| if (r) { |
| NET_ERR("Could not receive in the context\n"); |
| return; |
| } |
| } |