blob: 71ad0486bf116b5a53a6fcdba12e849b6b884853 [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 <sys/socket.h>
#include <unistd.h>
#include <errno.h>
#include <netinet/in.h>
#include <string.h>
#include <poll.h>
#include <assert.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/un.h>
#include <stdio.h>
#include <signal.h>
#include "os/mynewt.h"
#include "mn_socket/mn_socket.h"
#include "mn_socket/mn_socket_ops.h"
#include "native_sockets/native_sock.h"
#include "native_sock_priv.h"
static struct native_sock {
struct mn_socket ns_sock;
int ns_fd;
unsigned int ns_connect:1; /* Non-blocking connect in progress. */
unsigned int ns_poll:1;
unsigned int ns_listen:1;
uint8_t ns_type;
uint8_t ns_pf;
struct os_sem ns_sem;
STAILQ_HEAD(, os_mbuf_pkthdr) ns_rx;
struct os_mbuf *ns_tx;
} native_socks[MYNEWT_VAL(NATIVE_SOCKETS_MAX)];
static struct native_sock_state {
struct pollfd poll_fds[MYNEWT_VAL(NATIVE_SOCKETS_MAX)];
int poll_fd_cnt;
struct os_mutex mtx;
struct os_task task;
} native_sock_state;
static const struct mn_socket_ops native_sock_ops = {
.mso_create = native_sock_create,
.mso_close = native_sock_close,
.mso_bind = native_sock_bind,
.mso_connect = native_sock_connect,
.mso_listen = native_sock_listen,
.mso_sendto = native_sock_sendto,
.mso_recvfrom = native_sock_recvfrom,
.mso_getsockopt = native_sock_getsockopt,
.mso_setsockopt = native_sock_setsockopt,
.mso_getsockname = native_sock_getsockname,
.mso_getpeername = native_sock_getpeername,
.mso_itf_getnext = native_sock_itf_getnext,
.mso_itf_addr_getnext = native_sock_itf_addr_getnext
};
static struct native_sock *
native_get_sock(void)
{
int i;
struct native_sock *ns;
for (i = 0; i < MYNEWT_VAL(NATIVE_SOCKETS_MAX); i++) {
if (native_socks[i].ns_fd < 0) {
ns = &native_socks[i];
ns->ns_poll = 0;
ns->ns_listen = 0;
return ns;
}
}
return NULL;
}
static struct native_sock *
native_find_sock(int fd)
{
int i;
for (i = 0; i < MYNEWT_VAL(NATIVE_SOCKETS_MAX); i++) {
if (native_socks[i].ns_fd == fd) {
return &native_socks[i];
}
}
return NULL;
}
static void
native_sock_poll_rebuild(struct native_sock_state *nss)
{
struct native_sock *ns;
int i;
int j;
os_mutex_pend(&nss->mtx, OS_WAIT_FOREVER);
for (i = 0, j = 0; i < MYNEWT_VAL(NATIVE_SOCKETS_MAX); i++) {
ns = &native_socks[i];
if (ns->ns_fd < 0) {
continue;
}
if (!ns->ns_poll) {
continue;
}
nss->poll_fds[j].fd = ns->ns_fd;
nss->poll_fds[j].events = POLLIN | POLLOUT;
nss->poll_fds[j].revents = 0;
j++;
}
nss->poll_fd_cnt = j;
os_mutex_release(&nss->mtx);
}
int
native_sock_err_to_mn_err(int err)
{
switch (err) {
case 0:
return 0;
case EAGAIN:
case EINPROGRESS:
return MN_EAGAIN;
case ENOTCONN:
return MN_ENOTCONN;
case ETIMEDOUT:
return MN_ETIMEDOUT;
case ENOMEM:
return MN_ENOBUFS;
case EADDRINUSE:
return MN_EADDRINUSE;
case EADDRNOTAVAIL:
return MN_EADDRNOTAVAIL;
default:
return MN_EINVAL;
}
}
static int
native_sock_mn_addr_to_addr(struct mn_sockaddr *ms, struct sockaddr *sa,
int *sa_len)
{
struct sockaddr_un *sun = (struct sockaddr_un *)sa;
struct sockaddr_in *sin = (struct sockaddr_in *)sa;
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
struct mn_sockaddr_un *msun = (struct mn_sockaddr_un *)ms;
struct mn_sockaddr_in *msin = (struct mn_sockaddr_in *)ms;
struct mn_sockaddr_in6 *msin6 = (struct mn_sockaddr_in6 *)ms;
switch (ms->msa_family) {
case MN_AF_INET:
sin->sin_family = AF_INET;
#ifndef MN_LINUX
sin->sin_len = sizeof(*sin);
#endif
sin->sin_addr.s_addr = msin->msin_addr.s_addr;
sin->sin_port = msin->msin_port;
*sa_len = sizeof(*sin);
break;
case MN_AF_INET6:
sin6->sin6_family = AF_INET6;
#ifndef MN_LINUX
sin6->sin6_len = sizeof(*sin6);
#endif
sin6->sin6_port = msin6->msin6_port;
sin6->sin6_flowinfo = msin6->msin6_flowinfo;
memcpy(&sin6->sin6_addr, &msin6->msin6_addr,
sizeof(msin6->msin6_addr));
sin6->sin6_scope_id = msin6->msin6_scope_id;
*sa_len = sizeof(*sin6);
break;
case MN_AF_LOCAL:
sun->sun_family = AF_LOCAL;
#ifndef MN_LINUX
sun->sun_len = sizeof(*sun);
#endif
sun->sun_path[0] = '\0';
strncat(sun->sun_path, msun->msun_path, sizeof(sun->sun_path) - 1);
if (strcmp(sun->sun_path, msun->msun_path) != 0) {
/* Path too long. */
return MN_EINVAL;
}
*sa_len = sizeof(*sun);
break;
default:
return MN_EPROTONOSUPPORT;
}
return 0;
}
static int
native_sock_addr_to_mn_addr(struct sockaddr *sa, struct mn_sockaddr *ms)
{
struct mn_sockaddr_un *msun = (struct mn_sockaddr_un *)ms;
struct mn_sockaddr_in *msin = (struct mn_sockaddr_in *)ms;
struct mn_sockaddr_in6 *msin6 = (struct mn_sockaddr_in6 *)ms;
struct sockaddr_un *sun = (struct sockaddr_un *)sa;
struct sockaddr_in *sin = (struct sockaddr_in *)sa;
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
switch (sa->sa_family) {
case AF_INET:
msin->msin_family = MN_AF_INET;
msin->msin_len = sizeof(*msin);
msin->msin_addr.s_addr = sin->sin_addr.s_addr;
msin->msin_port = sin->sin_port;
break;
case AF_INET6:
msin6->msin6_family = MN_AF_INET6;
msin6->msin6_len = sizeof(*msin6);
msin6->msin6_port = sin6->sin6_port;
msin6->msin6_flowinfo = sin6->sin6_flowinfo;
memcpy(&msin6->msin6_addr, &sin6->sin6_addr,
sizeof(msin6->msin6_addr));
msin6->msin6_scope_id = sin6->sin6_scope_id;
break;
case AF_LOCAL:
msun->msun_family = MN_AF_LOCAL;
strncpy(msun->msun_path, sun->sun_path, sizeof msun->msun_path);
if (strcmp(msun->msun_path, sun->sun_path) != 0) {
/* Path too long. */
return MN_EINVAL;
}
break;
default:
return MN_EPROTONOSUPPORT;
}
return 0;
}
static void
native_sock_set_nonblocking(struct native_sock *ns)
{
int rc;
rc = fcntl(ns->ns_fd, F_SETFL, fcntl(ns->ns_fd, F_GETFL, 0) | O_NONBLOCK);
assert(rc == 0);
}
int
native_sock_create(struct mn_socket **sp, uint8_t domain,
uint8_t type, uint8_t proto)
{
struct native_sock_state *nss = &native_sock_state;
struct native_sock *ns;
int idx;
switch (domain) {
case MN_PF_INET:
domain = PF_INET;
break;
case MN_PF_INET6:
domain = PF_INET6;
break;
case MN_PF_LOCAL:
domain = PF_LOCAL;
break;
default:
return MN_EPROTONOSUPPORT;
}
switch (type) {
case MN_SOCK_DGRAM:
type = SOCK_DGRAM;
break;
case MN_SOCK_STREAM:
type = SOCK_STREAM;
break;
case 0:
break;
default:
return MN_EPROTONOSUPPORT;
}
os_mutex_pend(&nss->mtx, OS_WAIT_FOREVER);
ns = native_get_sock();
if (!ns) {
os_mutex_release(&nss->mtx);
return MN_ENOBUFS;
}
os_sem_init(&ns->ns_sem, 0);
idx = socket(domain, type, proto);
ns->ns_fd = idx;
ns->ns_pf = domain;
ns->ns_type = type;
native_sock_set_nonblocking(ns);
os_mutex_release(&nss->mtx);
if (idx < 0) {
return MN_ENOBUFS;
}
*sp = &ns->ns_sock;
return 0;
}
int
native_sock_close(struct mn_socket *s)
{
struct native_sock_state *nss = &native_sock_state;
struct native_sock *ns = (struct native_sock *)s;
struct os_mbuf_pkthdr *m;
os_mutex_pend(&nss->mtx, OS_WAIT_FOREVER);
close(ns->ns_fd);
ns->ns_fd = -1;
/*
* When socket is closed, we must free all mbufs which might be
* queued in it.
*/
while ((m = STAILQ_FIRST(&ns->ns_rx))) {
STAILQ_REMOVE_HEAD(&ns->ns_rx, omp_next);
os_mbuf_free_chain(OS_MBUF_PKTHDR_TO_MBUF(m));
}
os_mbuf_free_chain(ns->ns_tx);
native_sock_poll_rebuild(nss);
os_mutex_release(&nss->mtx);
return 0;
}
int
native_sock_connect(struct mn_socket *s, struct mn_sockaddr *addr)
{
struct native_sock_state *nss = &native_sock_state;
struct native_sock *ns = (struct native_sock *)s;
struct sockaddr_storage ss;
struct sockaddr *sa = (struct sockaddr *)&ss;
int rc;
int sa_len;
int in_progress = 0;
rc = native_sock_mn_addr_to_addr(addr, sa, &sa_len);
if (rc) {
return rc;
}
os_mutex_pend(&nss->mtx, OS_WAIT_FOREVER);
rc = connect(ns->ns_fd, sa, sa_len);
if (rc != 0) {
if (errno == EINPROGRESS) {
/* Non-blocking connect initiated. */
in_progress = 1;
ns->ns_connect = 1;
} else {
rc = errno;
os_mutex_release(&nss->mtx);
return native_sock_err_to_mn_err(rc);
}
}
ns->ns_poll = 1;
native_sock_poll_rebuild(nss);
os_mutex_release(&nss->mtx);
/* Indicate writability if connection fully established. */
if (!in_progress) {
mn_socket_writable(s, 0);
}
return 0;
}
int
native_sock_bind(struct mn_socket *s, struct mn_sockaddr *addr)
{
struct native_sock_state *nss = &native_sock_state;
struct native_sock *ns = (struct native_sock *)s;
struct sockaddr_un ss;
struct sockaddr *sa = (struct sockaddr *)&ss;
int rc;
int sa_len;
int val = 1;
rc = native_sock_mn_addr_to_addr(addr, sa, &sa_len);
if (rc) {
return rc;
}
os_mutex_pend(&nss->mtx, OS_WAIT_FOREVER);
if (ns->ns_type == SOCK_STREAM) {
rc = setsockopt(ns->ns_fd, SOL_SOCKET, SO_REUSEADDR, &val,
sizeof(val));
if (rc) {
goto err;
}
}
rc = ioctl(ns->ns_fd, FIONBIO, (char *)&val);
if (rc) {
goto err;
}
if (bind(ns->ns_fd, sa, sa_len)) {
goto err;
}
if (ns->ns_type == SOCK_DGRAM) {
ns->ns_poll = 1;
native_sock_poll_rebuild(nss);
}
os_mutex_release(&nss->mtx);
return 0;
err:
rc = errno;
os_mutex_release(&nss->mtx);
return native_sock_err_to_mn_err(rc);
}
int
native_sock_listen(struct mn_socket *s, uint8_t qlen)
{
struct native_sock_state *nss = &native_sock_state;
struct native_sock *ns = (struct native_sock *)s;
int rc;
os_mutex_pend(&nss->mtx, OS_WAIT_FOREVER);
if (listen(ns->ns_fd, qlen)) {
rc = errno;
os_mutex_release(&nss->mtx);
return native_sock_err_to_mn_err(rc);
}
ns->ns_poll = 1;
ns->ns_listen = 1;
native_sock_poll_rebuild(nss);
os_mutex_release(&nss->mtx);
return 0;
}
/*
* TX routine for stream sockets (TCP). The data to send is pointed
* by ns_tx.
* Keep sending mbufs until socket says that it can't take anymore.
* then wait for send event notification before continuing.
*/
static int
native_sock_stream_tx(struct native_sock *ns, int notify)
{
struct native_sock_state *nss = &native_sock_state;
struct os_mbuf *m;
struct os_mbuf *n;
int rc;
rc = 0;
os_mutex_pend(&nss->mtx, OS_TIMEOUT_NEVER);
while (ns->ns_tx && rc == 0) {
m = ns->ns_tx;
n = SLIST_NEXT(m, om_next);
errno = 0;
rc = write(ns->ns_fd, m->om_data, m->om_len);
if (rc == m->om_len) {
/* Complete write. */
ns->ns_tx = n;
os_mbuf_free(m);
rc = 0;
} else if (rc != -1) {
/* Partial write. */
os_mbuf_adj(m, m->om_len - rc);
rc = 0;
} else {
/* Error. */
rc = errno;
if (rc == EAGAIN) {
rc = 0;
} else {
/*
* Socket had an error. User should close it.
*/
os_mbuf_free_chain(ns->ns_tx);
ns->ns_tx = NULL;
rc = native_sock_err_to_mn_err(rc);
}
break;
}
}
os_mutex_release(&nss->mtx);
if (notify) {
mn_socket_writable(&ns->ns_sock, rc);
}
return rc;
}
static int
native_sock_set_tx_buf(struct native_sock *ns, struct os_mbuf *om)
{
struct native_sock_state *nss = &native_sock_state;
int rc;
os_mutex_pend(&nss->mtx, OS_WAIT_FOREVER);
if (ns->ns_tx) {
rc = MN_EAGAIN;
} else {
ns->ns_tx = om;
rc = 0;
}
os_mutex_release(&nss->mtx);
return rc;
}
int
native_sock_sendto(struct mn_socket *s, struct os_mbuf *m,
struct mn_sockaddr *addr)
{
struct native_sock *ns = (struct native_sock *)s;
struct sockaddr_storage ss;
struct sockaddr *sa = (struct sockaddr *)&ss;
uint8_t tmpbuf[MYNEWT_VAL(NATIVE_SOCKETS_MAX_UDP)];
struct os_mbuf *o;
int sa_len;
int off;
int rc;
if (ns->ns_type == SOCK_DGRAM) {
rc = native_sock_mn_addr_to_addr(addr, sa, &sa_len);
if (rc) {
return rc;
}
off = 0;
for (o = m; o; o = SLIST_NEXT(o, om_next)) {
if (off + o->om_len > sizeof(tmpbuf)) {
return MN_ENOBUFS;
}
os_mbuf_copydata(o, 0, o->om_len, &tmpbuf[off]);
off += o->om_len;
}
rc = sendto(ns->ns_fd, tmpbuf, off, 0, sa, sa_len);
if (rc != off) {
return native_sock_err_to_mn_err(errno);
}
os_mbuf_free_chain(m);
return 0;
} else {
rc = native_sock_set_tx_buf(ns, m);
if (rc != 0) {
return rc;
}
rc = native_sock_stream_tx(ns, 0);
return rc;
}
}
int
native_sock_recvfrom(struct mn_socket *s, struct os_mbuf **mp,
struct mn_sockaddr *addr)
{
struct native_sock *ns = (struct native_sock *)s;
struct sockaddr_storage ss;
struct sockaddr *sa = (struct sockaddr *)&ss;
uint8_t tmpbuf[MYNEWT_VAL(NATIVE_SOCKETS_MAX_UDP)];
struct os_mbuf *m;
socklen_t slen;
int rc;
slen = sizeof(ss);
if (ns->ns_type == SOCK_DGRAM) {
rc = recvfrom(ns->ns_fd, tmpbuf, sizeof(tmpbuf), 0, sa, &slen);
} else {
rc = getpeername(ns->ns_fd, sa, &slen);
if (rc == 0) {
rc = read(ns->ns_fd, tmpbuf, sizeof(tmpbuf));
}
}
if (rc < 0) {
return native_sock_err_to_mn_err(errno);
}
if (ns->ns_type == SOCK_STREAM && rc == 0) {
ns->ns_poll = 0;
native_sock_poll_rebuild(&native_sock_state);
return MN_ECONNABORTED;
}
m = os_msys_get_pkthdr(rc, 0);
if (!m) {
return MN_ENOBUFS;
}
os_mbuf_copyinto(m, 0, tmpbuf, rc);
*mp = m;
if (addr) {
native_sock_addr_to_mn_addr(sa, addr);
}
return 0;
}
int
native_sock_getsockopt(struct mn_socket *s, uint8_t level, uint8_t name,
void *val)
{
return MN_EPROTONOSUPPORT;
}
int
native_sock_setsockopt(struct mn_socket *s, uint8_t level, uint8_t name,
void *val)
{
struct native_sock *ns = (struct native_sock *)s;
int rc;
uint32_t val32;
struct ip_mreq ip_mreq;
struct ipv6_mreq ipv6_mreq;
void *opt;
int opt_len;
struct mn_mreq *mreq;
if (level == MN_SO_LEVEL) {
switch (name) {
case MN_MCAST_JOIN_GROUP:
case MN_MCAST_LEAVE_GROUP:
mreq = val;
if (mreq->mm_family == MN_AF_INET) {
memset(&ip_mreq, 0, sizeof(ip_mreq));
if (native_sock_itf_addr(mreq->mm_idx,
&ip_mreq.imr_interface.s_addr)) {
return MN_EADDRNOTAVAIL;
}
ip_mreq.imr_multiaddr.s_addr = mreq->mm_addr.v4.s_addr;
level = IPPROTO_IP;
if (name == MN_MCAST_JOIN_GROUP) {
name = IP_ADD_MEMBERSHIP;
} else {
name = IP_DROP_MEMBERSHIP;
}
opt = &ip_mreq;
opt_len = sizeof(ip_mreq);
} else {
memset(&ipv6_mreq, 0, sizeof(ipv6_mreq));
ipv6_mreq.ipv6mr_interface = mreq->mm_idx;
memcpy(&ipv6_mreq.ipv6mr_multiaddr, &mreq->mm_addr,
sizeof(struct in6_addr));
level = IPPROTO_IPV6;
if (name == MN_MCAST_JOIN_GROUP) {
name = IPV6_JOIN_GROUP;
} else {
name = IPV6_LEAVE_GROUP;
}
opt = &ipv6_mreq;
opt_len = sizeof(ipv6_mreq);
}
rc = setsockopt(ns->ns_fd, level, name, opt, opt_len);
if (rc) {
return native_sock_err_to_mn_err(errno);
}
return 0;
case MN_MCAST_IF:
if (ns->ns_pf == AF_INET) {
level = IPPROTO_IP;
name = IP_MULTICAST_IF;
rc = native_sock_itf_addr(*(int *)val, &val32);
if (rc) {
return rc;
}
} else {
level = IPPROTO_IPV6;
name = IPV6_MULTICAST_IF;
val32 = *(uint32_t *)val;
}
rc = setsockopt(ns->ns_fd, level, name, &val32, sizeof(val32));
if (rc) {
return native_sock_err_to_mn_err(errno);
}
return 0;
case MN_REUSEADDR:
name = SO_REUSEADDR;
val32 = *(uint32_t *)val;
rc = setsockopt(ns->ns_fd, level, name, &val32, sizeof(val32));
if (rc) {
return native_sock_err_to_mn_err(errno);
}
return 0;
}
}
return MN_EPROTONOSUPPORT;
}
int
native_sock_getsockname(struct mn_socket *s, struct mn_sockaddr *addr)
{
struct native_sock *ns = (struct native_sock *)s;
struct sockaddr_storage ss;
struct sockaddr *sa = (struct sockaddr *)&ss;
socklen_t len;
int rc;
len = sizeof(struct sockaddr_storage);
rc = getsockname(ns->ns_fd, sa, &len);
if (rc) {
return native_sock_err_to_mn_err(errno);
}
rc = native_sock_addr_to_mn_addr(sa, addr);
if (rc) {
return rc;
}
return 0;
}
int
native_sock_getpeername(struct mn_socket *s, struct mn_sockaddr *addr)
{
struct native_sock *ns = (struct native_sock *)s;
struct sockaddr_storage ss;
struct sockaddr *sa = (struct sockaddr *)&ss;
socklen_t len;
int rc;
len = sizeof(struct sockaddr_storage);
rc = getpeername(ns->ns_fd, sa, &len);
if (rc) {
return native_sock_err_to_mn_err(errno);
}
rc = native_sock_addr_to_mn_addr(sa, addr);
if (rc) {
return rc;
}
return 0;
}
/*
* XXX should do this task with SIGIO as well.
*/
static void
socket_task(void *arg)
{
struct native_sock_state *nss = arg;
struct native_sock *ns, *new_ns;
struct sockaddr_storage ss;
struct sockaddr *sa = (struct sockaddr *)&ss;
int revents;
int i;
socklen_t slen;
int sock_err;
int rc;
os_mutex_pend(&nss->mtx, OS_WAIT_FOREVER);
while (1) {
os_mutex_release(&nss->mtx);
os_time_delay(os_time_ms_to_ticks32(MYNEWT_VAL(NATIVE_SOCKETS_POLL_INTERVAL_MS)));
os_mutex_pend(&nss->mtx, OS_WAIT_FOREVER);
if (nss->poll_fd_cnt) {
rc = poll(nss->poll_fds, nss->poll_fd_cnt, 0);
} else {
rc = 0;
}
if (rc == 0) {
continue;
}
for (i = 0; i < nss->poll_fd_cnt; i++) {
if (nss->poll_fds[i].revents == 0) {
continue;
}
revents = nss->poll_fds[i].revents;
nss->poll_fds[i].revents = 0;
ns = native_find_sock(nss->poll_fds[i].fd);
assert(ns);
if (revents & POLLIN) {
if (ns->ns_listen) {
new_ns = native_get_sock();
if (!new_ns) {
continue;
}
slen = sizeof(ss);
new_ns->ns_fd = accept(ns->ns_fd, sa, &slen);
if (new_ns->ns_fd < 0) {
continue;
}
new_ns->ns_type = ns->ns_type;
new_ns->ns_sock.ms_ops = &native_sock_ops;
native_sock_set_nonblocking(new_ns);
os_mutex_release(&nss->mtx);
if (mn_socket_newconn(&ns->ns_sock, &new_ns->ns_sock)) {
/*
* should close
*/
}
os_mutex_pend(&nss->mtx, OS_WAIT_FOREVER);
new_ns->ns_poll = 1;
native_sock_poll_rebuild(nss);
} else {
mn_socket_readable(&ns->ns_sock, 0);
}
}
if (revents & POLLOUT) {
if (ns->ns_connect) {
/*
* The connection attempt has completed. Report whether it
* succeeded.
*/
ns->ns_connect = 0;
slen = sizeof(sock_err);
rc = getsockopt(ns->ns_fd, SOL_SOCKET, SO_ERROR,
&sock_err, &slen);
if (rc != 0) {
rc = native_sock_err_to_mn_err(errno);
} else if (sock_err != 0) {
rc = native_sock_err_to_mn_err(sock_err);
}
mn_socket_writable(&ns->ns_sock, rc);
} else if (ns->ns_type == SOCK_STREAM && ns->ns_tx) {
native_sock_stream_tx(ns, 1);
}
}
}
}
}
int
native_sock_init(void)
{
struct native_sock_state *nss = &native_sock_state;
int i;
os_stack_t *sp;
/* Ensure this function only gets called by sysinit. */
SYSINIT_ASSERT_ACTIVE();
for (i = 0; i < MYNEWT_VAL(NATIVE_SOCKETS_MAX); i++) {
native_socks[i].ns_fd = -1;
STAILQ_INIT(&native_socks[i].ns_rx);
}
sp = malloc(sizeof(os_stack_t) * MYNEWT_VAL(NATIVE_SOCKETS_STACK_SZ));
if (!sp) {
return -1;
}
os_mutex_init(&nss->mtx);
i = os_task_init(&nss->task, "socket", socket_task, &native_sock_state,
MYNEWT_VAL(NATIVE_SOCKETS_PRIO), OS_WAIT_FOREVER, sp,
MYNEWT_VAL(NATIVE_SOCKETS_STACK_SZ));
if (i) {
return -1;
}
i = mn_socket_ops_reg(&native_sock_ops);
if (i) {
return -1;
}
signal(SIGPIPE, SIG_IGN);
return 0;
}