

9 changed files with 436 additions and 34 deletions
@ -0,0 +1,3 @@
|
||||
MODULE := emb6_sock_udp
|
||||
|
||||
include $(RIOTBASE)/Makefile.base |
@ -0,0 +1,326 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Freie Universität Berlin |
||||
* |
||||
* 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. |
||||
*/ |
||||
|
||||
/**
|
||||
* @{ |
||||
* |
||||
* @file |
||||
* @author Martine Lenders <m.lenders@fu-berlin.de> |
||||
*/ |
||||
|
||||
#include <assert.h> |
||||
#include <errno.h> |
||||
#include <stdbool.h> |
||||
|
||||
#include "byteorder.h" |
||||
#include "evproc.h" |
||||
#include "msg.h" |
||||
#include "mutex.h" |
||||
#include "net/af.h" |
||||
#include "net/sock/udp.h" |
||||
#include "net/ipv6/hdr.h" |
||||
#include "sched.h" |
||||
#include "uip.h" |
||||
#include "xtimer.h" |
||||
|
||||
#define _MSG_TYPE_CLOSE (0x4123) |
||||
#define _MSG_TYPE_TIMEOUT (0x4124) |
||||
#define _MSG_TYPE_RCV (0x4125) |
||||
|
||||
/* struct to describe a sendto command for emb6 thread */ |
||||
typedef struct { |
||||
mutex_t block; |
||||
struct udp_socket *sock; |
||||
const sock_udp_ep_t *remote; |
||||
int res; |
||||
const void *data; |
||||
size_t len; |
||||
} _send_cmd_t; |
||||
|
||||
extern uint16_t uip_slen; |
||||
|
||||
static bool send_registered = false; |
||||
|
||||
static void _timeout_callback(void *arg); |
||||
static void _input_callback(struct udp_socket *c, void *ptr, |
||||
const uip_ipaddr_t *src_addr, uint16_t src_port, |
||||
const uip_ipaddr_t *dst_addr, uint16_t dst_port, |
||||
const uint8_t *data, uint16_t datalen); |
||||
static void _output_callback(c_event_t c_event, p_data_t p_data); |
||||
|
||||
static int _reg(struct udp_socket *c, void *ptr, udp_socket_input_callback_t cb, |
||||
const sock_udp_ep_t *local, const sock_udp_ep_t *remote) |
||||
{ |
||||
if (((local != NULL) && (local->family != AF_INET6)) || |
||||
((remote != NULL) && (remote->family != AF_INET6))) { |
||||
return -EAFNOSUPPORT; |
||||
} |
||||
if (udp_socket_register(c, ptr, cb) < 0) { |
||||
return -ENOMEM; |
||||
} |
||||
if (local != NULL) { |
||||
if (udp_socket_bind(c, local->port) < 0) { |
||||
udp_socket_close(c); |
||||
return -EADDRINUSE; |
||||
} |
||||
} |
||||
if (remote != NULL) { |
||||
/* check of return value not necessary, since neither c nor
|
||||
* c->udp_conn is NULL (only error case) at this point */ |
||||
udp_socket_connect(c, (uip_ipaddr_t *)&remote->addr, remote->port); |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
int sock_udp_create(sock_udp_t *sock, const sock_udp_ep_t *local, |
||||
const sock_udp_ep_t *remote, uint16_t flags) |
||||
{ |
||||
int res; |
||||
|
||||
(void)flags; |
||||
assert((sock != NULL)); |
||||
assert((local == NULL) || (local->port != 0)); |
||||
assert((remote == NULL) || (remote->port != 0)); |
||||
if (sock->sock.input_callback != NULL) { |
||||
sock_udp_close(sock); |
||||
} |
||||
mutex_init(&sock->mutex); |
||||
mutex_lock(&sock->mutex); |
||||
mbox_init(&sock->mbox, sock->mbox_queue, SOCK_MBOX_SIZE); |
||||
atomic_flag_clear(&sock->receivers); |
||||
if ((res = _reg(&sock->sock, sock, _input_callback, local, remote)) < 0) { |
||||
sock->sock.input_callback = NULL; |
||||
} |
||||
mutex_unlock(&sock->mutex); |
||||
return res; |
||||
} |
||||
|
||||
void sock_udp_close(sock_udp_t *sock) |
||||
{ |
||||
assert(sock != NULL); |
||||
if (sock->sock.input_callback != NULL) { |
||||
while (atomic_fetch_sub(&sock->receivers, 1) > 0) { |
||||
msg_t msg = { .type = _MSG_TYPE_CLOSE }; |
||||
mbox_put(&sock->mbox, &msg); |
||||
} |
||||
mutex_lock(&sock->mutex); |
||||
udp_socket_close(&sock->sock); |
||||
sock->sock.input_callback = NULL; |
||||
mutex_unlock(&sock->mutex); |
||||
} |
||||
} |
||||
|
||||
int sock_udp_get_local(sock_udp_t *sock, sock_udp_ep_t *ep) |
||||
{ |
||||
assert((sock != NULL) && (ep != NULL)); |
||||
if ((sock->sock.input_callback != NULL) && |
||||
(sock->sock.udp_conn->lport != 0)) { |
||||
mutex_lock(&sock->mutex); |
||||
/* local UDP endpoints do not have addresses in emb6 */ |
||||
memset(&ep->addr, 0, sizeof(ipv6_addr_t)); |
||||
ep->port = ntohs(sock->sock.udp_conn->lport); |
||||
mutex_unlock(&sock->mutex); |
||||
return sizeof(ipv6_addr_t); |
||||
} |
||||
return -EADDRNOTAVAIL; |
||||
} |
||||
|
||||
int sock_udp_get_remote(sock_udp_t *sock, sock_udp_ep_t *ep) |
||||
{ |
||||
assert((sock != NULL) && (ep != NULL)); |
||||
if ((sock->sock.input_callback != NULL) && |
||||
(sock->sock.udp_conn->rport != 0)) { |
||||
mutex_lock(&sock->mutex); |
||||
memcpy(&ep->addr, &sock->sock.udp_conn->ripaddr, sizeof(ipv6_addr_t)); |
||||
ep->port = ntohs(sock->sock.udp_conn->rport); |
||||
mutex_unlock(&sock->mutex); |
||||
return sizeof(ipv6_addr_t); |
||||
} |
||||
return -ENOTCONN; |
||||
} |
||||
|
||||
int sock_udp_recv(sock_udp_t *sock, void *data, size_t max_len, |
||||
uint32_t timeout, sock_udp_ep_t *remote) |
||||
{ |
||||
xtimer_t timeout_timer; |
||||
int blocking = BLOCKING; |
||||
int res = -EIO; |
||||
msg_t msg; |
||||
|
||||
assert((sock != NULL) && (data != NULL) && (max_len > 0)); |
||||
if (sock->sock.input_callback == NULL) { |
||||
return -EADDRNOTAVAIL; |
||||
} |
||||
if (timeout == 0) { |
||||
blocking = NON_BLOCKING; |
||||
} |
||||
else if (timeout != SOCK_NO_TIMEOUT) { |
||||
timeout_timer.callback = _timeout_callback; |
||||
timeout_timer.arg = &sock->mbox; |
||||
xtimer_set(&timeout_timer, timeout); |
||||
} |
||||
atomic_fetch_add(&sock->receivers, 1); |
||||
if (_mbox_get(&sock->mbox, &msg, blocking) == 0) { |
||||
/* do not need to remove xtimer, since we only get here in non-blocking
|
||||
* mode (timeout > 0) */ |
||||
return -EAGAIN; |
||||
} |
||||
switch (msg.type) { |
||||
case _MSG_TYPE_CLOSE: |
||||
res = -EADDRNOTAVAIL; |
||||
break; |
||||
case _MSG_TYPE_TIMEOUT: |
||||
res = -ETIMEDOUT; |
||||
break; |
||||
case _MSG_TYPE_RCV: |
||||
mutex_lock(&sock->mutex); |
||||
if (max_len < sock->recv_info.datalen) { |
||||
res = -ENOBUFS; |
||||
mutex_unlock(&sock->mutex); |
||||
break; |
||||
} |
||||
memcpy(data, sock->recv_info.data, sock->recv_info.datalen); |
||||
if (remote != NULL) { |
||||
remote->family = AF_INET6; |
||||
remote->netif = SOCK_ADDR_ANY_NETIF; |
||||
memcpy(&remote->addr, &sock->recv_info.src, sizeof(ipv6_addr_t)); |
||||
remote->port = sock->recv_info.src_port; |
||||
} |
||||
res = (int)sock->recv_info.datalen; |
||||
mutex_unlock(&sock->mutex); |
||||
break; |
||||
} |
||||
atomic_fetch_sub(&sock->receivers, 1); |
||||
return res; |
||||
} |
||||
|
||||
int sock_udp_send(sock_udp_t *sock, const void *data, size_t len, |
||||
const sock_udp_ep_t *remote) |
||||
{ |
||||
struct udp_socket tmp; |
||||
_send_cmd_t send_cmd = { .block = MUTEX_INIT, |
||||
.remote = remote, |
||||
.data = data, |
||||
.len = len }; |
||||
|
||||
assert((sock != NULL) || (remote != NULL)); |
||||
assert((len == 0) || (data != NULL)); /* (len != 0) => (data != NULL) */ |
||||
/* we want the send in the uip thread (which udp_socket_send does not offer)
|
||||
* so we need to do it manually */ |
||||
if (!send_registered) { |
||||
if (evproc_regCallback(EVENT_TYPE_CONN_SEND, _output_callback) != E_SUCCESS) { |
||||
return -ENOMEM; |
||||
} |
||||
else { |
||||
send_registered = true; |
||||
} |
||||
} |
||||
if ((len > (UIP_BUFSIZE - (UIP_LLH_LEN + UIP_IPUDPH_LEN))) || |
||||
(len > UINT16_MAX)) { |
||||
return -ENOMEM; |
||||
} |
||||
if (remote != NULL) { |
||||
if (remote->family != AF_INET6) { |
||||
return -EAFNOSUPPORT; |
||||
} |
||||
if (remote->port == 0) { |
||||
return -EINVAL; |
||||
} |
||||
send_cmd.remote = remote; |
||||
} |
||||
else if (sock->sock.udp_conn->rport == 0) { |
||||
return -ENOTCONN; |
||||
} |
||||
/* cppcheck-supress nullPointerRedundantCheck
|
||||
* remote == NULL implies that sock != NULL (see assert at start of |
||||
* function) * that's why it is okay in the if-statement above to check |
||||
* sock->... without checking (sock != NULL) first => this check afterwards |
||||
* isn't redundant */ |
||||
if (sock == NULL) { |
||||
int res; |
||||
if ((res = _reg(&tmp, NULL, NULL, NULL, NULL)) < 0) { |
||||
return res; |
||||
} |
||||
send_cmd.sock = &tmp; |
||||
} |
||||
else { |
||||
send_cmd.sock = &sock->sock; |
||||
} |
||||
mutex_lock(&send_cmd.block); |
||||
/* change to emb6 thread context */ |
||||
if (evproc_putEvent(E_EVPROC_TAIL, EVENT_TYPE_CONN_SEND, &send_cmd) == E_SUCCESS) { |
||||
/* block thread until data was sent */ |
||||
mutex_lock(&send_cmd.block); |
||||
} |
||||
else { |
||||
/* most likely error: event queue was full */ |
||||
send_cmd.res = -ENOMEM; |
||||
} |
||||
if (send_cmd.sock == &tmp) { |
||||
udp_socket_close(&tmp); |
||||
} |
||||
mutex_unlock(&send_cmd.block); |
||||
|
||||
return send_cmd.res; |
||||
} |
||||
|
||||
static void _timeout_callback(void *arg) |
||||
{ |
||||
msg_t msg = { .type = _MSG_TYPE_TIMEOUT }; |
||||
mbox_t *mbox = arg; |
||||
|
||||
/* should be safe, because otherwise if mbox were filled this callback is
|
||||
* senseless */ |
||||
mbox_try_put(mbox, &msg); |
||||
} |
||||
|
||||
static void _input_callback(struct udp_socket *c, void *ptr, |
||||
const uip_ipaddr_t *src_addr, uint16_t src_port, |
||||
const uip_ipaddr_t *dst_addr, uint16_t dst_port, |
||||
const uint8_t *data, uint16_t datalen) |
||||
{ |
||||
msg_t msg = { .type = _MSG_TYPE_RCV }; |
||||
sock_udp_t *sock = ptr; |
||||
|
||||
(void)dst_addr; |
||||
(void)dst_port; |
||||
mutex_lock(&sock->mutex); |
||||
sock->recv_info.src_port = src_port; |
||||
sock->recv_info.src = (const ipv6_addr_t *)src_addr; |
||||
sock->recv_info.data = data; |
||||
sock->recv_info.datalen = datalen - sizeof(ipv6_hdr_t); |
||||
mutex_unlock(&sock->mutex); |
||||
mbox_put(&sock->mbox, &msg); |
||||
} |
||||
|
||||
static void _output_callback(c_event_t c_event, p_data_t p_data) |
||||
{ |
||||
|
||||
if ((c_event != EVENT_TYPE_CONN_SEND) || (p_data == NULL)) { |
||||
return; |
||||
} |
||||
|
||||
_send_cmd_t *send_cmd = (_send_cmd_t *)p_data; |
||||
|
||||
if (send_cmd->remote != NULL) { |
||||
/* send_cmd->len was previously checked */ |
||||
send_cmd->res = udp_socket_sendto(send_cmd->sock, send_cmd->data, |
||||
(uint16_t)send_cmd->len, |
||||
(uip_ipaddr_t *)&send_cmd->remote->addr, |
||||
send_cmd->remote->port); |
||||
} |
||||
else { |
||||
/* send_cmd->len was previously checked */ |
||||
send_cmd->res = udp_socket_send(send_cmd->sock, send_cmd->data, |
||||
(uint16_t)send_cmd->len); |
||||
} |
||||
send_cmd->res = (send_cmd->res < 0) ? -EHOSTUNREACH : send_cmd->res; |
||||
/* notify notify waiting thread */ |
||||
mutex_unlock(&send_cmd->block); |
||||
} |
||||
/** @} */ |
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Freie Universität Berlin |
||||
* |
||||
* 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. |
||||
*/ |
||||
|
||||
/**
|
||||
* @defgroup pkg_emb6_sock emb6-specific implementation of the sock API |
||||
* @ingroup pkg_emb6 |
||||
* @brief |
||||
* @{ |
||||
* |
||||
* @file |
||||
* @brief |
||||
* |
||||
* @author Martine Lenders <mlenders@inf.fu-berlin.de> |
||||
*/ |
||||
#ifndef SOCK_TYPES_H_ |
||||
#define SOCK_TYPES_H_ |
||||
|
||||
#include <stdatomic.h> |
||||
|
||||
#include "mbox.h" |
||||
#include "mutex.h" |
||||
#include "net/ipv6/addr.h" |
||||
|
||||
#include "uip.h" |
||||
#include "udp-socket.h" |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
#ifndef SOCK_MBOX_SIZE |
||||
#define SOCK_MBOX_SIZE (2) |
||||
#endif |
||||
|
||||
#ifndef SOCK_HAS_IPV6 |
||||
#error "emb6 only runs with IPv6 support" |
||||
#endif |
||||
|
||||
/**
|
||||
* @brief @ref net_sock_udp definition for emb6 |
||||
*/ |
||||
struct sock_udp { |
||||
struct udp_socket sock; /**< emb6 internal socket */ |
||||
mutex_t mutex; /**< mutex for the connection */ |
||||
mbox_t mbox; /**< mbox for receiving */ |
||||
msg_t mbox_queue[SOCK_MBOX_SIZE]; /**< queue for mbox */ |
||||
atomic_int receivers; /**< current number of recv calls */ |
||||
struct { |
||||
const ipv6_addr_t *src; /**< source address */ |
||||
const void *data; /**< data of received packet */ |
||||
size_t datalen; /**< length of received packet data */ |
||||
uint16_t src_port; /**< source port */ |
||||
} recv_info; /**< info on received packet */ |
||||
}; |
||||
|
||||
#ifdef __cplusplus |
||||
} |
||||
#endif |
||||
|
||||
#endif /* SOCK_TYPES_H_ */ |
||||
/** @} */ |
Loading…
Reference in new issue