blob: e5ec05797832d4f3693146c2d9203b55e53d131f [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 <assert.h>
#include "os/mynewt.h"
#include "node/lora.h"
#include "node/lora_priv.h"
/* XXX: for now, auto-join not supported */
#if MYNEWT_VAL(LORA_APP_AUTO_JOIN)
#error "Auto-joining not supported"
#endif
/* Valid app port range */
#define LORA_APP_PORT_MAX_VAL (223)
/* Port structure */
struct lora_app_port
{
uint8_t port_num;
uint8_t opened;
uint8_t retries;
uint8_t pad8;
lora_rxd_func rxd_cb;
lora_txd_func txd_cb;
};
/* Port memory */
static struct lora_app_port lora_app_ports[MYNEWT_VAL(LORA_APP_NUM_PORTS)];
/* Lora APP Receive queue and event */
struct os_mqueue lora_node_app_rx_q;
struct os_mqueue lora_node_app_txd_q;
/* Lora app event queue pointer */
static struct os_eventq *lora_node_app_evq;
/* Join and link check callbacks and events */
lora_join_cb lora_join_cb_func;
lora_link_chk_cb lora_link_chk_cb_func;
struct os_event lora_app_join_ev;
struct os_event lora_app_link_chk_ev;
struct lora_app_join_ev_obj
{
uint8_t attempts;
LoRaMacEventInfoStatus_t status;
};
static struct lora_app_join_ev_obj lora_app_join_ev_data;
struct lora_app_link_chk_ev_obj
{
uint8_t num_gw;
uint8_t demod_margin;
LoRaMacEventInfoStatus_t status;
};
static struct lora_app_link_chk_ev_obj lora_app_link_chk_ev_data;
/* Internal protos */
static int lora_app_port_receive(struct os_mbuf *om);
static int lora_app_port_txd(struct os_mbuf *om);
/* Get the lora app event queue. */
static struct os_eventq *
lora_node_app_evq_get(void)
{
return lora_node_app_evq;
}
/**
* Find an open app port given its port number.
*
* @param port Port number
*
* @return struct lora_app_port*
* NULL if no open port found
* Pointer to port if opened port found.
*/
static struct lora_app_port *
lora_app_port_find_open(uint8_t port)
{
int i;
struct lora_app_port *lap;
lap = NULL;
for (i = 0; i < MYNEWT_VAL(LORA_APP_NUM_PORTS); ++i) {
if ((lora_app_ports[i].opened != 0) &&
(lora_app_ports[i].port_num == port)) {
lap = &lora_app_ports[i];
break;
}
}
return lap;
}
/**
* Process received packet event. This event is processed by the task
* that handles the lora app.
*
* @param ev Pointer to event.
*/
static void
lora_node_proc_app_rxd_event(struct os_event *ev)
{
struct os_mbuf *om;
/* Go through packet queue and call rx callback for all */
while ((om = os_mqueue_get(&lora_node_app_rx_q)) != NULL) {
lora_app_port_receive(om);
}
}
/**
* Process transmit done event. This event is processed by the task
* that handles the lora app.
*
* @param ev Pointer to event.
*/
static void
lora_node_proc_app_txd_event(struct os_event *ev)
{
struct os_mbuf *om;
/* Go through packet queue and call rx callback for all */
while ((om = os_mqueue_get(&lora_node_app_txd_q)) != NULL) {
lora_app_port_txd(om);
}
}
/**
* Open a lora application port. This function will allocate a lora port, set
* port default values for datarate and retries, set the transmit done and
* received data callbacks, and add port to list of open ports.
*
* @param port Port number
* @param txd_cb Transmit done callback
* @param rxd_cb Receive data callback
*
* @return int A return code from set of lora return codes
*/
int
lora_app_port_open(uint8_t port, lora_txd_func txd_cb, lora_rxd_func rxd_cb)
{
int rc;
int i;
int avail;
/* Check valid range */
if ((port == 0) || (port > LORA_APP_PORT_MAX_VAL)) {
return LORA_APP_STATUS_INVALID_PORT;
}
/* Check other parameters */
if ((txd_cb == NULL) || (rxd_cb == NULL)) {
return LORA_APP_STATUS_INVALID_PARAM;
}
/* Make sure port is not opened */
avail = -1;
for (i = 0; i < MYNEWT_VAL(LORA_APP_NUM_PORTS); ++i) {
/* If port not opened, remember first available */
if (lora_app_ports[i].opened == 0) {
if (avail < 0) {
avail = i;
}
} else {
/* Make sure port is not already opened */
if (lora_app_ports[i].port_num == port) {
return LORA_APP_STATUS_ALREADY_OPEN;
}
}
}
/* Open port if available */
if (avail >= 0) {
lora_app_ports[avail].port_num = port;
lora_app_ports[avail].rxd_cb = rxd_cb;
lora_app_ports[avail].txd_cb = txd_cb;
lora_app_ports[avail].retries = 8;
lora_app_ports[avail].opened = 1;
rc = LORA_APP_STATUS_OK;
} else {
rc = LORA_APP_STATUS_ENOMEM;
}
return rc;
}
/**
* Close an open lora port
*
* @param port Port number
*
* @return int A return code from set of lora return codes
*/
int
lora_app_port_close(uint8_t port)
{
int rc;
struct lora_app_port *lap;
rc = LORA_APP_STATUS_NO_PORT;
lap = lora_app_port_find_open(port);
if (lap) {
lap->opened = 0;
rc = LORA_APP_STATUS_OK;
}
return rc;
}
/**
* Configure an application port. This configures the number of retries for
* confirmed packets.
*
* NOTE: The port must be opened or this will return an error
*
* @param port Port number
* @param retries NUmmber of retries for confirmed packets
*
* @return int A return code from set of lora return codes
*/
int
lora_app_port_cfg(uint8_t port, uint8_t retries)
{
int rc;
struct lora_app_port *lap;
/* Verify datarate and retries */
if (retries > MAX_ACK_RETRIES) {
return LORA_APP_STATUS_INVALID_PARAM;
}
rc = LORA_APP_STATUS_NO_PORT;
lap = lora_app_port_find_open(port);
if (lap) {
lap->retries = retries;
rc = LORA_APP_STATUS_OK;
}
return rc;
}
/**
* Send a packet on a port. If this routine returns an error the "transmitted"
* callback will NOT be called; it is the callers responsibility to handle the
* packet appropriately (including freeing any memory if needed).
*
* @param port Port number
* @param pkt_type Type of packet
* @param om Pointer to packet
*
* @return int A return code from set of lora return codes
*/
int
lora_app_port_send(uint8_t port, Mcps_t pkt_type, struct os_mbuf *om)
{
int rc;
struct lora_app_port *lap;
struct lora_pkt_info *lpkt;
/* If no buffer to send, fine. */
if ((om == NULL) || (OS_MBUF_PKTLEN(om) == 0)) {
return LORA_APP_STATUS_INVALID_PARAM;
}
assert(OS_MBUF_USRHDR_LEN(om) >= sizeof(struct lora_pkt_info));
/* Check valid packet type. Only confirmed and unconfirmed for now. */
if ((pkt_type != MCPS_UNCONFIRMED) && (pkt_type != MCPS_CONFIRMED)) {
return LORA_APP_STATUS_INVALID_PARAM;
}
lap = lora_app_port_find_open(port);
if (lap) {
/* Set packet information required by MAC */
lpkt = LORA_PKT_INFO_PTR(om);
lpkt->port = port;
lpkt->pkt_type = pkt_type;
lpkt->txdinfo.retries = lap->retries;
lora_node_mcps_request(om);
rc = LORA_APP_STATUS_OK;
} else {
rc = LORA_APP_STATUS_NO_PORT;
}
return rc;
}
/**
* What's the maximum payload which can be sent on next frame
*
* @return int payload length, negative on error.
*/
int
lora_app_mtu(void)
{
return lora_node_mtu();
}
/**
* Called by lora task when a packet has been received for an application port.
*
* NOTE: This API is not to be called by an application; it is called
* by the task handling lora events.
*
* @param om Pointer to received packet
*
* @return int A return code from set of lora return codes
*/
static int
lora_app_port_receive(struct os_mbuf *om)
{
int rc;
struct lora_app_port *lap;
struct lora_pkt_info *lpkt;
lpkt = LORA_PKT_INFO_PTR(om);
lap = lora_app_port_find_open(lpkt->port);
if (lap) {
lap->rxd_cb(lpkt->port, lpkt->status, lpkt->pkt_type, om);
rc = LORA_APP_STATUS_OK;
} else {
os_mbuf_free_chain(om);
rc = LORA_APP_STATUS_NO_PORT;
}
return rc;
}
/**
* Called by lora task when a packet has been transmitted.
*
* NOTE: This API is not to be called by an application; it is called
* by the task handling lora events.
*
* @param om Pointer to transmited packet
*
* @return int A return code from set of lora return codes
*/
static int
lora_app_port_txd(struct os_mbuf *om)
{
int rc;
struct lora_app_port *lap;
struct lora_pkt_info *lpkt;
lpkt = LORA_PKT_INFO_PTR(om);
lap = lora_app_port_find_open(lpkt->port);
if (lap) {
lap->txd_cb(lpkt->port, lpkt->status, lpkt->pkt_type, om);
rc = LORA_APP_STATUS_OK;
} else {
os_mbuf_free_chain(om);
rc = LORA_APP_STATUS_NO_PORT;
}
return rc;
}
/**
* Called from lower layer when a packet has been received for an application
* port.
*
* @param om Pointer to received packet
*/
void
lora_app_mcps_indicate(struct os_mbuf *om)
{
int rc;
rc = os_mqueue_put(&lora_node_app_rx_q, lora_node_app_evq_get(), om);
assert(rc == 0);
}
/**
* Interface between app and mac for MCPS confirmation. MAC layer calls
* this function when a packet has been transmitted or an error occurred.
*
* @param om Pointer to transmitted packet
*/
void
lora_app_mcps_confirm(struct os_mbuf *om)
{
int rc;
rc = os_mqueue_put(&lora_node_app_txd_q, lora_node_app_evq_get(), om);
assert(rc == 0);
}
#if !MYNEWT_VAL(LORA_APP_AUTO_JOIN)
/* XXX: personalization? */
/**
* Join a lora network. When called this function will attempt to join
* if the end device is not already joined. Join status (success, failure)
* will be reported through the callback. If this function returns an error
* no callback will occur.
*
* @param dev_eui Pointer to device EUI
* @param app_eui Pointer to Application EUI
* @param app_key Pointer to application key
* @param trials Number of join attempts before failure
*
* @return int Lora app return code
*/
int
lora_app_join(uint8_t *dev_eui, uint8_t *app_eui, uint8_t *app_key,
uint8_t trials)
{
int rc;
/* Make sure parameters are valid */
if ((dev_eui == NULL) || (app_eui == NULL) || (app_key == NULL) ||
(trials == 0)) {
return LORA_APP_STATUS_INVALID_PARAM;
}
/* Tell device to start join procedure */
rc = lora_node_join(dev_eui, app_eui, app_key, trials);
return rc;
}
/* Performs a link check */
int
lora_app_link_check(void)
{
int rc;
rc = lora_node_link_check();
return rc;
}
#endif
/**
* Set the join callback. This will be called when joining succeeds or fails.
*
* @param join_cb Pointer to join callback function.
*
* @return int Lora return code
*/
int
lora_app_set_join_cb(lora_join_cb join_cb)
{
assert(join_cb != NULL);
lora_join_cb_func = join_cb;
return LORA_APP_STATUS_OK;
}
/* Set the link check callback */
int
lora_app_set_link_check_cb(lora_link_chk_cb link_chk_cb)
{
assert(link_chk_cb != NULL);
lora_link_chk_cb_func = link_chk_cb;
return LORA_APP_STATUS_OK;
}
void
lora_app_join_ev_cb(struct os_event *ev)
{
struct lora_app_join_ev_obj *evdata;
evdata = (struct lora_app_join_ev_obj *)ev->ev_arg;
if (lora_join_cb_func) {
lora_join_cb_func(evdata->status, evdata->attempts);
}
}
void
lora_app_link_chk_ev_cb(struct os_event *ev)
{
struct lora_app_link_chk_ev_obj *evdata;
evdata = (struct lora_app_link_chk_ev_obj *)ev->ev_arg;
if (lora_link_chk_cb_func) {
lora_link_chk_cb_func(evdata->status, evdata->num_gw,
evdata->demod_margin);
}
}
/**
* Called by the lora mac task when a join confirm occurs. This function fills
* out the event arguments and posts the appropriate event to the lora app task.
*
* @param status
* @param attempts
*/
void
lora_app_join_confirm(LoRaMacEventInfoStatus_t status, uint8_t attempts)
{
lora_app_join_ev_data.status = status;
lora_app_join_ev_data.attempts = attempts;
os_eventq_put(lora_node_app_evq, &lora_app_join_ev);
}
/**
* Called by the lora mac task when a link check confirm occurs. This function
* fills out the event arguments and posts the appropriate event to the lora app
* task.
*
* @param status
* @param num_gw
* @param demod_margin
*/
void
lora_app_link_chk_confirm(LoRaMacEventInfoStatus_t status, uint8_t num_gw,
uint8_t demod_margin)
{
lora_app_link_chk_ev_data.status = status;
lora_app_link_chk_ev_data.num_gw = num_gw;
lora_app_link_chk_ev_data.demod_margin = demod_margin;
os_eventq_put(lora_node_app_evq, &lora_app_link_chk_ev);
}
void
lora_app_init(void)
{
/* For now, the lora app event queue is the default event queue */
lora_node_app_evq = os_eventq_dflt_get();
/* Init join and link check events */
lora_app_join_ev.ev_arg = &lora_app_join_ev_data;
lora_app_join_ev.ev_cb = lora_app_join_ev_cb;
lora_app_link_chk_ev.ev_arg = &lora_app_link_chk_ev_data;
lora_app_link_chk_ev.ev_cb = lora_app_link_chk_ev_cb;
/* Set up receive queue and event */
os_mqueue_init(&lora_node_app_rx_q, lora_node_proc_app_rxd_event, NULL);
/* Set up transmit done queue and event */
os_mqueue_init(&lora_node_app_txd_q, lora_node_proc_app_txd_event, NULL);
}