/* * Copyright (C) 2017 Fundacion Inria Chile * * This file is subject to the terms and conditions of the GNU Lesser * General Public License v2.1. See the file LICENSE in the top level * directory for more details. */ /** * @{ * @ingroup net * @file * @brief Netdev adoption for OpenThread * * @author Jose Ignacio Alamos * @author Baptiste Clenet * @} */ #include #include #include #include "msg.h" #include "openthread/cli.h" #include "openthread/instance.h" #include "openthread/ip6.h" #include "openthread/platform/alarm-milli.h" #include "openthread/platform/uart.h" #include "openthread/tasklet.h" #include "openthread/thread.h" #include "random.h" #include "ot.h" #define ENABLE_DEBUG (0) #include "debug.h" #define OPENTHREAD_QUEUE_LEN (8) static msg_t _queue[OPENTHREAD_QUEUE_LEN]; static kernel_pid_t _pid; static otInstance *sInstance; uint8_t ot_call_command(char* command, void *arg, void* answer) { ot_job_t job; job.command = command; job.arg = arg; job.answer = answer; msg_t msg, reply; msg.type = OPENTHREAD_JOB_MSG_TYPE_EVENT; msg.content.ptr = &job; msg_send_receive(&msg, &reply, openthread_get_pid()); return (uint8_t)reply.content.value; } /* OpenThread will call this when switching state from empty tasklet to non-empty tasklet. */ void otTaskletsSignalPending(otInstance *aInstance) { (void) aInstance; } static void *_openthread_event_loop(void *arg) { (void)arg; _pid = thread_getpid(); /* enable OpenThread UART */ otPlatUartEnable(); /* init OpenThread */ sInstance = otInstanceInitSingle(); msg_init_queue(_queue, OPENTHREAD_QUEUE_LEN); netdev_t *dev; msg_t msg, reply; #if defined(MODULE_OPENTHREAD_CLI_FTD) || defined(MODULE_OPENTHREAD_CLI_MTD) otCliUartInit(sInstance); /* Init default parameters */ otPanId panid = OPENTHREAD_PANID; uint8_t channel = OPENTHREAD_CHANNEL; otLinkSetPanId(sInstance, panid); otLinkSetChannel(sInstance, channel); /* Bring up the IPv6 interface */ otIp6SetEnabled(sInstance, true); /* Start Thread protocol operation */ otThreadSetEnabled(sInstance, true); #endif #if OPENTHREAD_ENABLE_DIAG diagInit(sInstance); #endif ot_job_t *job; serial_msg_t* serialBuffer; while (1) { otTaskletsProcess(sInstance); if (otTaskletsArePending(sInstance) == false) { msg_receive(&msg); switch (msg.type) { case OPENTHREAD_XTIMER_MSG_TYPE_EVENT: /* Tell OpenThread a time event was received */ otPlatAlarmMilliFired(sInstance); break; case OPENTHREAD_NETDEV_MSG_TYPE_EVENT: /* Received an event from driver */ dev = msg.content.ptr; dev->driver->isr(dev); break; case OPENTHREAD_SERIAL_MSG_TYPE_EVENT: /* Tell OpenThread about the reception of a CLI command */ serialBuffer = (serial_msg_t*)msg.content.ptr; otPlatUartReceived((uint8_t*) serialBuffer->buf,serialBuffer->length); serialBuffer->serial_buffer_status = OPENTHREAD_SERIAL_BUFFER_STATUS_FREE; break; case OPENTHREAD_JOB_MSG_TYPE_EVENT: job = msg.content.ptr; reply.content.value = ot_exec_command(sInstance, job->command, job->arg, job->answer); msg_reply(&msg, &reply); break; } } } return NULL; } static void _event_cb(netdev_t *dev, netdev_event_t event) { switch (event) { case NETDEV_EVENT_ISR: { msg_t msg; assert(_pid != KERNEL_PID_UNDEF); msg.type = OPENTHREAD_NETDEV_MSG_TYPE_EVENT; msg.content.ptr = dev; if (msg_send(&msg, _pid) <= 0) { DEBUG("openthread_netdev: possibly lost interrupt.\n"); } break; } case NETDEV_EVENT_RX_COMPLETE: DEBUG("openthread_netdev: Reception of a packet\n"); recv_pkt(sInstance, dev); break; case NETDEV_EVENT_TX_COMPLETE: case NETDEV_EVENT_TX_NOACK: case NETDEV_EVENT_TX_MEDIUM_BUSY: DEBUG("openthread_netdev: Transmission of a packet\n"); send_pkt(sInstance, dev, event); break; default: break; } } /* get OpenThread thread pid */ kernel_pid_t openthread_get_pid(void) { return _pid; } /* starts OpenThread thread */ int openthread_netdev_init(char *stack, int stacksize, char priority, const char *name, netdev_t *netdev) { netdev->driver->init(netdev); netdev->event_callback = _event_cb; netopt_enable_t enable = NETOPT_ENABLE; netdev->driver->set(netdev, NETOPT_TX_END_IRQ, &enable, sizeof(enable)); netdev->driver->set(netdev, NETOPT_RX_END_IRQ, &enable, sizeof(enable)); _pid = thread_create(stack, stacksize, priority, THREAD_CREATE_STACKTEST, _openthread_event_loop, NULL, name); if (_pid <= 0) { return -EINVAL; } return _pid; }