summaryrefslogtreecommitdiff
path: root/samples
diff options
context:
space:
mode:
authorLeandro Pereira <leandro.pereira@intel.com>2017-01-18 16:57:57 -0800
committerJukka Rissanen <jukka.rissanen@linux.intel.com>2017-02-03 15:59:15 +0200
commit2bcd700462a0da2ae52825b6f9914d62be333192 (patch)
tree581f769b4482dcf0926e25d7a00956aeff3b4cb0 /samples
parent3efe6b7ede9377d05e270edcb7fcc0798f270ec6 (diff)
samples: net: Add IRC bot example
This is a sample IRC bot program, written using the new IP stack API. All it does is join an IRC channel, wait for some commands, and react to them: !hello will greet whoever sent the command !random will generate a pseudo-random number and send it back !led_toggle will toggle an LED in the board [1] !led_on will turn the LED on regardless of its current state !led_off will turn the LED off !rejoin will part the current channel and join again !disconnect will quit from the IRC server As far as the IRC protocol goes, it doesn't do much more than this, but it should be straightforward to add support for other things (such as notices, CTCP, DCC, etc) if someone is inclined to do so. However, that's way beyond the scope of this sample, which is to show how to use the network API to write a TCP client. Some things are still missing as an example of how to use the APIs, namely DNS resolution, automatically setting up the network with DHCP, maybe saving settings on EEPROM. These are good candidates to be added in the future. [1] The LED code has been shamelessly stolen from the CoAP sample code. Change-Id: I7152e97c0726f3559db545579ae8ae8d07bf04cd Signed-off-by: Leandro Pereira <leandro.pereira@intel.com> Signed-off-by: Michael Scott <michael.scott@linaro.org>
Diffstat (limited to 'samples')
-rw-r--r--samples/net/irc_bot/Makefile24
-rw-r--r--samples/net/irc_bot/prj_qemu_x86.conf25
-rw-r--r--samples/net/irc_bot/src/Makefile1
-rw-r--r--samples/net/irc_bot/src/irc-bot.c735
4 files changed, 785 insertions, 0 deletions
diff --git a/samples/net/irc_bot/Makefile b/samples/net/irc_bot/Makefile
new file mode 100644
index 000000000..9912ebc2c
--- /dev/null
+++ b/samples/net/irc_bot/Makefile
@@ -0,0 +1,24 @@
+# Makefile - IRC client sample
+
+#
+# Copyright (c) 2017 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.
+#
+
+BOARD ?= qemu_x86
+CONF_FILE ?= prj_$(BOARD).conf
+
+include $(ZEPHYR_BASE)/Makefile.inc
+
+include $(ZEPHYR_BASE)/samples/net/common/Makefile.ipstack
diff --git a/samples/net/irc_bot/prj_qemu_x86.conf b/samples/net/irc_bot/prj_qemu_x86.conf
new file mode 100644
index 000000000..c6713b0bc
--- /dev/null
+++ b/samples/net/irc_bot/prj_qemu_x86.conf
@@ -0,0 +1,25 @@
+CONFIG_INIT_STACKS=y
+
+CONFIG_NET_LOG_ENABLED=y
+CONFIG_SYS_LOG_NET_LEVEL=2
+CONFIG_NET_IF_UNICAST_IPV6_ADDR_COUNT=3
+CONFIG_NET_IPV6=y
+CONFIG_NET_LOG=y
+CONFIG_NET_MAX_CONTEXTS=10
+CONFIG_NET_NBUF_DATA_COUNT=30
+CONFIG_NET_NBUF_RX_COUNT=14
+CONFIG_NET_NBUF_TX_COUNT=14
+CONFIG_NET_SHELL=y
+CONFIG_NET_SLIP_TAP=y
+CONFIG_NET_STATISTICS=y
+CONFIG_NET_TCP=y
+CONFIG_NETWORKING=y
+
+CONFIG_PRINTK=y
+CONFIG_SYS_LOG_SHOW_COLOR=y
+
+CONFIG_NET_SAMPLES_IP_ADDRESSES=y
+CONFIG_NET_SAMPLES_MY_IPV6_ADDR="2001:db8::1"
+CONFIG_NET_SAMPLES_PEER_IPV6_ADDR="2001:db8::2"
+
+CONFIG_TEST_RANDOM_GENERATOR=y
diff --git a/samples/net/irc_bot/src/Makefile b/samples/net/irc_bot/src/Makefile
new file mode 100644
index 000000000..a76c773d3
--- /dev/null
+++ b/samples/net/irc_bot/src/Makefile
@@ -0,0 +1 @@
+obj-y = irc-bot.o
diff --git a/samples/net/irc_bot/src/irc-bot.c b/samples/net/irc_bot/src/irc-bot.c
new file mode 100644
index 000000000..89f1562b3
--- /dev/null
+++ b/samples/net/irc_bot/src/irc-bot.c
@@ -0,0 +1,735 @@
+/*
+ * Copyright (c) 2017 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.
+ */
+
+#if 1
+#define SYS_LOG_DOMAIN "irc"
+#define NET_SYS_LOG_LEVEL SYS_LOG_LEVEL_DEBUG
+#define NET_LOG_ENABLED 1
+#endif
+
+#include <board.h>
+#include <drivers/rand32.h>
+#include <errno.h>
+#include <gpio.h>
+#include <net/nbuf.h>
+#include <net/net_context.h>
+#include <net/net_core.h>
+#include <net/net_if.h>
+#include <stdio.h>
+#include <zephyr.h>
+
+/* LED */
+#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 device *led0;
+static bool fake_led;
+
+/* Network */
+#if !defined(CONFIG_NET_SAMPLES_MY_IPV6_ADDR)
+#define CONFIG_NET_SAMPLES_MY_IPV6_ADDR "2001:db8::1"
+#endif /* CONFIG_NET_SAMPLES_MY_IPV6_ADDR */
+
+#if !defined(CONFIG_NET_SAMPLES_PEER_IPV6_ADDR)
+#define CONFIG_NET_SAMPLES_PEER_IPV6_ADDR "2001:db8::2"
+#endif /* CONFIG_NET_SAMPLES_PEER_IPV6_ADDR */
+
+static struct sockaddr my_sockaddr = { .family = AF_INET6 };
+static struct sockaddr ircd_sockaddr = { .family = AF_INET6 };
+
+/* IRC API */
+#define DEFAULT_CHANNEL "#zephyrbot"
+
+struct zirc_chan;
+
+struct zirc {
+ struct net_context *conn;
+ struct zirc_chan *chans;
+
+ void (*on_connect)(void *data, struct zirc *irc);
+ void *data;
+};
+
+struct zirc_chan {
+ struct zirc *irc;
+ struct zirc_chan *next;
+
+ const char *chan;
+
+ void (*on_privmsg_rcvd)(void *data, struct zirc_chan *chan,
+ char *umask, char *msg);
+ void *data;
+};
+
+static int
+transmit(struct net_context *ctx, char buffer[], size_t len)
+{
+ struct net_buf *send_buf;
+
+ send_buf = net_nbuf_get_tx(ctx);
+ if (!send_buf) {
+ return -ENOMEM;
+ }
+
+ if (!net_nbuf_append(send_buf, len, buffer)) {
+ return -EINVAL;
+ }
+
+ return net_context_send(send_buf, NULL, K_NO_WAIT, NULL, NULL);
+}
+
+static void
+on_cmd_ping(struct zirc *irc, char *umask, char *cmd, size_t len)
+{
+ char pong[32];
+ int ret;
+
+ NET_INFO("Got PING command from server: %s", cmd);
+
+ ret = snprintk(pong, 32, "PONG :%s", cmd + 1);
+ if (ret < sizeof(pong)) {
+ transmit(irc->conn, pong, ret);
+ /* TODO: what to do with the return value of transmit()? */
+ }
+}
+
+static void
+on_cmd_privmsg(struct zirc *irc, char *umask, char *cmd, size_t len)
+{
+ struct zirc_chan *chan;
+ char *space;
+
+ if (!umask) {
+ /* Don't know how this got here, so ignore */
+ return;
+ }
+
+ NET_INFO("Got message from umask %s: %s", umask, cmd);
+
+ space = memchr(cmd, ' ', len);
+ if (!space) {
+ return;
+ }
+
+ *space = '\0';
+
+ if (*(space + 1) != ':') {
+ /* Just ignore messages without a ':' after the space */
+ return;
+ }
+
+ space += 2; /* Jump over the ':', pointing to the message itself */
+ for (chan = irc->chans; chan; chan = chan->next) {
+ if (!strncmp(chan->chan, cmd, space - cmd)) {
+ chan->on_privmsg_rcvd(chan->data, chan, umask, space);
+ return;
+ }
+ }
+
+ /* TODO: could be a private message (from another user) */
+ NET_INFO("Unknown privmsg received: %.*s\n", (int)len, cmd);
+}
+
+#define CMD(cmd_, cb_) { \
+ .cmd = cmd_ " ", \
+ .cmd_len = sizeof(cmd_ " ") - 1, \
+ .func = on_cmd_ ## cb_ \
+}
+static void
+process_command(struct zirc *irc, char *cmd, size_t len)
+{
+ static const struct {
+ const char *cmd;
+ size_t cmd_len;
+ void (*func)(struct zirc *zirc, char *umask, char *cmd,
+ size_t len);
+ } commands[] = {
+ CMD("PING", ping),
+ CMD("PRIVMSG", privmsg),
+ };
+ char *umask;
+ int i;
+
+ if (*cmd == ':') {
+ char *space = memchr(cmd, ' ', len);
+
+ if (!space) {
+ return;
+ }
+
+ umask = cmd + 1;
+ *space = '\0';
+
+ len -= (space - cmd) + 1;
+ if (!len) {
+ return;
+ }
+
+ cmd = space + 1;
+
+ NET_INFO("Received from server, umask=%s: %s", umask, cmd);
+ } else {
+ umask = NULL;
+
+ NET_INFO("Received from server (no umask): %s", cmd);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(commands); i++) {
+ if (len < commands[i].cmd_len) {
+ continue;
+ }
+ if (!strncmp(cmd, commands[i].cmd, commands[i].cmd_len)) {
+ NET_INFO("Command has handler, executing");
+
+ cmd += commands[i].cmd_len;
+ len -= commands[i].cmd_len;
+ commands[i].func(irc, umask, cmd, len);
+
+ return;
+ }
+ }
+
+ /* TODO: handle notices, CTCP, etc */
+ NET_INFO("Could not find handler to handle %s, ignoring", cmd);
+}
+#undef CMD
+
+static void
+on_context_recv(struct net_context *ctx, struct net_buf *buf,
+ int status, void *data)
+{
+ struct zirc *irc = data;
+ struct net_buf *tmp;
+
+ if (!buf && !status) {
+ /* TODO: notify of disconnection, maybe reconnect? */
+ NET_INFO("Disconnected\n");
+ return;
+ }
+ if (status < 0) {
+ /* TODO: handle connection error */
+ NET_INFO("Connection error: %d\n", -status);
+ return;
+ }
+
+ /* tmp points to fragment containing IP header */
+ tmp = buf->frags;
+ /* Adavance tmp fragment to appdata (TCP payload) */
+ net_buf_pull(tmp, net_nbuf_appdata(buf) - tmp->data);
+
+ while (tmp) {
+ while (true) {
+ char *end_of_line = memchr(tmp->data, '\r',
+ tmp->len);
+ size_t cmd_len;
+
+ if (!end_of_line) {
+ break;
+ }
+
+ *end_of_line = '\0';
+ cmd_len = end_of_line - (char *)tmp->data - 1;
+ process_command(irc, tmp->data, cmd_len);
+
+ cmd_len++; /* Account for \n also */
+ tmp->len -= cmd_len;
+ tmp->data += cmd_len;
+ }
+
+ net_buf_frag_del(buf, tmp);
+ tmp = buf->frags;
+
+ /* TODO: handle messages that spans multiple fragments */
+ }
+}
+
+static void
+on_context_connect(struct net_context *ctx, void *data)
+{
+ struct zirc *irc = data;
+
+ irc->conn = ctx;
+ net_context_recv(ctx, on_context_recv, K_NO_WAIT, data);
+ irc->on_connect(irc->data, irc);
+}
+
+static int
+zirc_connect(struct zirc *irc, const char *host, int port,
+ void (*on_connect)(void *data, struct zirc *irc), void *data)
+{
+ /* TODO: DNS lookup for host */
+ struct sockaddr dst_addr, src_addr;
+ socklen_t len = sizeof(struct sockaddr_in6);
+ int ret;
+
+ NET_INFO("Connecting to %s:%d...", host, port);
+
+ if (!on_connect) {
+ NET_DBG("Connection callback not set");
+ return -EINVAL;
+ }
+
+ ret = net_context_get(AF_INET6, SOCK_STREAM, IPPROTO_TCP,
+ &irc->conn);
+ if (ret < 0) {
+ NET_DBG("Could not get new context: %d", -ret);
+ return ret;
+ }
+
+ dst_addr = ircd_sockaddr;
+ net_sin6_ptr(&dst_addr)->sin6_family = AF_INET6;
+ net_sin6_ptr(&dst_addr)->sin6_port = htons(port);
+
+ src_addr = my_sockaddr;
+ net_sin6_ptr(&src_addr)->sin6_family = AF_INET6;
+ net_sin6_ptr(&src_addr)->sin6_port = 0;
+
+ ret = net_context_bind(irc->conn, (struct sockaddr *)&src_addr,
+ sizeof(struct sockaddr_in6));
+ if (ret < 0) {
+ NET_DBG("Could not bind to local address: %d", -ret);
+ return ret;
+ }
+
+ irc->data = data;
+ irc->on_connect = on_connect;
+
+ ret = net_context_connect(irc->conn, (struct sockaddr *)&dst_addr,
+ len, on_context_connect, K_FOREVER, irc);
+ if (ret < 0) {
+ NET_DBG("Could not connect, errno %d", -ret);
+ }
+ return ret;
+}
+
+static int
+zirc_disconnect(struct zirc *irc)
+{
+ NET_INFO("Disconnecting");
+
+ irc->chans = NULL;
+ return net_context_put(irc->conn);
+}
+
+static int
+zirc_nick_set(struct zirc *irc, const char *nick)
+{
+ char buffer[32];
+ int ret;
+
+ NET_INFO("Setting nickname to: %s", nick);
+
+ ret = snprintk(buffer, sizeof(buffer), "NICK %s\r\n", nick);
+ if (ret < 0 || ret >= sizeof(buffer)) {
+ return -EINVAL;
+ }
+
+ return transmit(irc->conn, buffer, ret);
+}
+
+static int
+zirc_user_set(struct zirc *irc, const char *user, const char *realname)
+{
+ char buffer[32];
+ int ret;
+
+ NET_INFO("Setting user to: %s, real name to: %s", user, realname);
+
+ ret = snprintk(buffer, sizeof(buffer), "USER %s * * :%s\r\n",
+ user, realname);
+ if (ret < 0 || ret >= sizeof(buffer)) {
+ return -EINVAL;
+ }
+
+ return transmit(irc->conn, buffer, ret);
+}
+
+static int
+zirc_chan_join(struct zirc *irc, struct zirc_chan *chan,
+ const char *channel,
+ void (*on_privmsg_rcvd)(void *data, struct zirc_chan *chan,
+ char *umask, char *msg),
+ void *data)
+{
+ char buffer[32];
+ int ret;
+
+ NET_INFO("Joining channel: %s", channel);
+
+ if (!on_privmsg_rcvd) {
+ return -EINVAL;
+ }
+
+ ret = snprintk(buffer, sizeof(buffer), "JOIN %s\r\n", channel);
+ if (ret < 0 || ret >= sizeof(buffer)) {
+ return -EINVAL;
+ }
+
+ ret = transmit(chan->irc->conn, buffer, ret);
+ if (ret < 0) {
+ return ret;
+ }
+
+ chan->irc = irc;
+ chan->chan = channel;
+ chan->on_privmsg_rcvd = on_privmsg_rcvd;
+ chan->data = data;
+
+ chan->next = irc->chans;
+ irc->chans = chan;
+
+ return 0;
+}
+
+static int
+zirc_chan_part(struct zirc_chan *chan)
+{
+ struct zirc_chan **cc, *c;
+ char buffer[32];
+ int ret;
+
+ NET_INFO("Leaving channel: %s", chan->chan);
+
+ ret = snprintk(buffer, sizeof(buffer), "PART %s\r\n", chan->chan);
+ if (ret < 0 || ret >= sizeof(buffer)) {
+ return -EINVAL;
+ }
+
+ ret = transmit(chan->irc->conn, buffer, ret);
+ if (ret < 0) {
+ return ret;
+ }
+
+ for (cc = &chan->irc->chans, c = c->irc->chans;
+ c; cc = &c->next, c = c->next) {
+ if (c == chan) {
+ *cc = c->next;
+ return 0;
+ }
+ }
+
+ return -ENOENT;
+}
+
+static int
+zirc_chan_send_msg(const struct zirc_chan *chan, const char *msg)
+{
+ char buffer[80];
+
+ NET_INFO("Sending to channel %s: %s", chan->chan, msg);
+
+ while (*msg) {
+ int msglen, txret;
+
+ msglen = snprintk(buffer, sizeof(buffer), "PRIVMSG %s :%s\r\n",
+ chan->chan, msg);
+
+ if (msglen < 0) {
+ return msglen;
+ }
+
+ txret = transmit(chan->irc->conn, buffer, msglen);
+ if (txret < 0) {
+ return txret;
+ }
+
+ if (msglen < sizeof(buffer)) {
+ return 0;
+ }
+
+ msg += msglen - sizeof("PRIVMSG :\r\n") - 1 +
+ strlen(chan->chan);
+ }
+
+ return 0;
+}
+
+static void
+on_cmd_hello(struct zirc_chan *chan, const char *nick, const char *msg)
+{
+ char buf[64];
+ int ret;
+
+ ret = snprintk(buf, sizeof(buf), "Hello, %s!", nick);
+ if (ret < 0 || ret >= sizeof(buf)) {
+ zirc_chan_send_msg(chan, "Hello, world! (Your nick is "
+ "larger than my stack allows.)");
+ } else {
+ zirc_chan_send_msg(chan, buf);
+ }
+}
+
+static void
+on_cmd_random(struct zirc_chan *chan, const char *nick, const char *msg)
+{
+ char buf[3 * sizeof(int) + 40];
+ int32_t num = sys_rand32_get();
+ int ret;
+
+ switch (num & 3) {
+ case 0:
+ ret = snprintk(buf, sizeof(buf), "Here's a fresh, random "
+ "32-bit integer, %s: %d", nick, num);
+ break;
+ case 1:
+ ret = snprintk(buf, sizeof(buf), "Another number, fresh off "
+ "the PRNG: %d", num);
+ break;
+ case 2:
+ ret = snprintk(buf, sizeof(buf), "Some calculations "
+ "with senseless constants yielded %d", num);
+ break;
+ case 3:
+ ret = snprintk(buf, sizeof(buf), "I rolled a fair dice and "
+ "the result is... %d", num);
+ break;
+ default:
+ /* Shut up the compiler, as this condition is impossible. */
+ ret = -1;
+ }
+
+ if (ret < 0 || ret >= sizeof(buf)) {
+ zirc_chan_send_msg(chan, "I rolled a fair dice and the "
+ "number is... 7.3");
+ } else {
+ zirc_chan_send_msg(chan, buf);
+ }
+}
+
+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;
+ } else {
+ gpio_pin_write(led0, LED_PIN, !led);
+ }
+}
+
+static void
+on_cmd_led_off(struct zirc_chan *chan, const char *nick, const char *msg)
+{
+ zirc_chan_send_msg(chan, "The LED should be *off* now");
+ write_led(false);
+}
+
+static void
+on_cmd_led_on(struct zirc_chan *chan, const char *nick, const char *msg)
+{
+ zirc_chan_send_msg(chan, "The LED should be *on* now");
+ write_led(true);
+}
+
+static void
+on_cmd_led_toggle(struct zirc_chan *chan, const char *nick, const char *msg)
+{
+ if (read_led()) {
+ on_cmd_led_off(chan, nick, msg);
+ } else {
+ on_cmd_led_on(chan, nick, msg);
+ }
+}
+
+static void on_msg_rcvd(void *data, struct zirc_chan *chan, char *umask,
+ char *msg);
+
+static void
+on_cmd_rejoin(struct zirc_chan *chan, const char *nick, const char *msg)
+{
+ zirc_chan_part(chan);
+ zirc_chan_join(chan->irc, chan, DEFAULT_CHANNEL, on_msg_rcvd, NULL);
+}
+
+static void
+on_cmd_disconnect(struct zirc_chan *chan, const char *nick, const char *msg)
+{
+ zirc_disconnect(chan->irc);
+}
+
+#define CMD(c) { \
+ .cmd = "!" #c, \
+ .cmd_len = sizeof(#c) - 1, \
+ .func = on_cmd_ ## c \
+}
+static void
+on_msg_rcvd(void *data, struct zirc_chan *chan, char *umask, char *msg)
+{
+ static const struct {
+ const char *cmd;
+ size_t cmd_len;
+ void (*func)(struct zirc_chan *chan, const char *nick,
+ const char *msg);
+ } commands[] = {
+ CMD(hello),
+ CMD(random),
+ CMD(led_toggle),
+ CMD(led_off),
+ CMD(led_on),
+ CMD(rejoin),
+ CMD(disconnect),
+ };
+ char *nick, *end;
+ int i;
+
+ if (!umask) {
+ return;
+ }
+
+ NET_INFO("Received from umask %s: %s", umask, msg);
+
+ end = strchr(umask, '!');
+ if (!end) {
+ nick = NULL;
+ } else {
+ *end = '\0';
+ nick = umask;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(commands); i++) {
+ if (!strncmp(msg, commands[i].cmd, commands[i].cmd_len)) {
+ msg += commands[i].cmd_len;
+ commands[i].func(chan, nick, msg);
+ return;
+ }
+ }
+
+ if (!strncmp(msg, "!help", 5)) {
+ char msg[32];
+ int ret;
+
+ /* TODO: loop through commands[] and create help text */
+ /* TODO: add help message to command, "!help command"
+ * sends it back
+ */
+
+ ret = snprintk(msg, sizeof(msg), "%s, you're a grown up, figure"
+ " it out", nick);
+ if (ret < 0 || ret >= sizeof(msg)) {
+ zirc_chan_send_msg(chan, "Your nick is too long, and my"
+ " stack is limited. Can't help you");
+ } else {
+ zirc_chan_send_msg(chan, msg);
+ }
+ }
+}
+#undef CMD
+
+static void
+panic(const char *msg)
+{
+ NET_ERR("Panic: %s", msg);
+ for (;;) {
+ k_sleep(K_FOREVER);
+ }
+}
+
+static void
+on_connect(void *data, struct zirc *irc)
+{
+ struct zirc_chan *chan = data;
+
+ if (zirc_nick_set(irc, "zephyrbot") < 0) {
+ panic("Could not set nick");
+ }
+
+ if (zirc_user_set(irc, "zephyrbot", "Zephyr IRC Bot") < 0) {
+ panic("Could not set nick");
+ }
+
+ if (zirc_chan_join(irc, chan, DEFAULT_CHANNEL, on_msg_rcvd, NULL) < 0) {
+ panic("Could not join channel");
+ }
+}
+
+static void
+initialize_network(void)
+{
+ /* TODO: use DHCP here, watch NET_EVENT_IF_UP, zirc_connect() when
+ * available, etc
+ */
+
+ NET_INFO("Initializing network");
+
+#if defined(CONFIG_NET_SAMPLES_MY_IPV6_ADDR)
+ if (net_addr_pton(AF_INET6, CONFIG_NET_SAMPLES_MY_IPV6_ADDR,
+ &my_sockaddr) < 0) {
+ NET_ERR("Invalid IPv6 address: %s",
+ CONFIG_NET_SAMPLES_MY_IPV6_ADDR);
+ }
+#endif /* CONFIG_NET_SAMPLES_MY_IPV6_ADDR */
+#if defined(CONFIG_NET_SAMPLES_PEER_IPV6_ADDR)
+ if (net_addr_pton(AF_INET6, CONFIG_NET_SAMPLES_PEER_IPV6_ADDR,
+ &ircd_sockaddr) < 0) {
+ NET_ERR("Invalid IPv6 address: %s",
+ CONFIG_NET_SAMPLES_PEER_IPV6_ADDR);
+ }
+#endif /* CONFIG_NET_SAMPLES_PEER_IPV6_ADDR */
+
+ net_if_ipv6_addr_add(net_if_get_default(),
+ net_sin6_ptr((const struct sockaddr_ptr *)
+ &my_sockaddr)->sin6_addr,
+ NET_ADDR_MANUAL, 0);
+}
+
+static void
+initialize_hardware(void)
+{
+ NET_INFO("Initializing hardware");
+
+ led0 = device_get_binding(LED_GPIO_NAME);
+ if (led0) {
+ gpio_pin_configure(led0, LED_PIN, GPIO_DIR_OUT);
+ }
+}
+
+void main(void)
+{
+ struct zirc irc = { };
+ struct zirc_chan chan = { };
+
+ NET_INFO("Zephyr IRC bot sample");
+
+ initialize_network();
+ initialize_hardware();
+
+ if (zirc_connect(&irc, "irc.freenode.net", 6667, on_connect, &chan) <
+ 0) {
+ panic("Could not connect");
+ }
+}