Hi all,

I checked in the first take of the socket interface.

I did change it a bit from my original email. Notably; the user of the socket
will not allocate the memory for socket. This would be done by the system.

The API uses callbacks instead of struct os_event automatically.
As discussed socket user can/should convert this to an event and then make
calls to socket from the context of their task.

Also I ended up using os_mbufs for sending/receiving data. This will allow
us to have zero-copy IP stack, assuming the stack uses os_mbufs.

At the moment I’m testing this API using Arduino MKR1000, using it’s internal
IP stack and sockets. The driver for the WiFi chip is checked in to
mynewt_arduino_zero repository, as that’s where the BSP for the board
is as well. From license point of view the driver could be brought over to
mynewt-core, but I felt it should go together with the BSP.

> Begin forwarded message:
> 
> From: [email protected]
> Subject: incubator-mynewt-core git commit: MYNEWT-314; First take on the 
> socket interface.
> Date: July 11, 2016 at 5:03:10 PM PDT
> To: [email protected]
> Reply-To: [email protected]
> 
> Repository: incubator-mynewt-core
> Updated Branches:
>  refs/heads/develop 364bcc3ea -> eb24b4d83
> 
> 
> MYNEWT-314; First take on the socket interface.
> 
> 
> Project: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/repo
> Commit: 
> http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/commit/eb24b4d8
> Tree: 
> http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/tree/eb24b4d8
> Diff: 
> http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/diff/eb24b4d8
> 
> Branch: refs/heads/develop
> Commit: eb24b4d83ed37126c567de62903eac55600a4052
> Parents: 364bcc3
> Author: Marko Kiiskila <[email protected]>
> Authored: Mon Jul 11 16:59:46 2016 -0700
> Committer: Marko Kiiskila <[email protected]>
> Committed: Mon Jul 11 16:59:46 2016 -0700
> 
> ----------------------------------------------------------------------
> sys/mn_socket/include/mn_socket/mn_socket.h     | 141 +++++++++++++++++++
> sys/mn_socket/include/mn_socket/mn_socket_ops.h |  82 +++++++++++
> sys/mn_socket/pkg.yml                           |  31 ++++
> sys/mn_socket/src/mn_socket.c                   | 123 ++++++++++++++++
> sys/mn_socket/src/mn_socket_aconv.c             |  57 ++++++++
> sys/mn_socket/src/test/mn_sock_test.c           |  82 +++++++++++
> 6 files changed, 516 insertions(+)
> ----------------------------------------------------------------------
> 
> 
> http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/eb24b4d8/sys/mn_socket/include/mn_socket/mn_socket.h
> ----------------------------------------------------------------------
> diff --git a/sys/mn_socket/include/mn_socket/mn_socket.h 
> b/sys/mn_socket/include/mn_socket/mn_socket.h
> new file mode 100644
> index 0000000..75ebc8c
> --- /dev/null
> +++ b/sys/mn_socket/include/mn_socket/mn_socket.h
> @@ -0,0 +1,141 @@
> +/**
> + * 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.
> + */
> +#ifndef __SYS_MN_SOCKET_H_
> +#define __SYS_MN_SOCKET_H_
> +
> +#include <inttypes.h>
> +
> +/*
> + * Address/protocol family.
> + */
> +#define MN_AF_INET              4
> +#define MN_PF_INET              MN_AF_INET
> +#define MN_AF_INET6             6
> +#define MN_PF_INET6             MN_AF_INET6
> +
> +/*
> + * Socket types
> + */
> +#define MN_SOCK_STREAM          1
> +#define MN_SOCK_DGRAM           2
> +
> +/*
> + * Error codes from mn_socket interface.
> + */
> +#define MN_EAFNOSUPPORT         1
> +#define MN_EPROTONOSUPPORT      2
> +#define MN_ENOBUFS              3
> +#define MN_EINVAL               4
> +#define MN_ENOTCONN             5
> +#define MN_ECONNABORTED         6
> +#define MN_EDESTADDRREQ         7
> +#define MN_EADDRINUSE           8
> +#define MN_ETIMEDOUT            9
> +#define MN_EAGAIN               10
> +#define MN_EUNKNOWN             11
> +
> +struct mn_socket;
> +struct mn_socket_ops;
> +struct mn_sock_cb;
> +struct os_mbuf;
> +
> +struct mn_socket {
> +    const union mn_socket_cb *ms_cbs;          /* filled in by user */
> +    const struct mn_socket_ops *ms_ops;        /* filled in by mn_socket */
> +};
> +
> +/*
> + * Callbacks. Socket callbacks are for sockets which exchange
> + * data. Listen callback is for TCP listen sockets.
> + */
> +union mn_socket_cb {
> +    struct {
> +        void (*readable)(struct mn_socket *, int err);
> +        void (*writable)(struct mn_socket *, int err);
> +    } socket;
> +    struct {
> +        int (*newconn)(struct mn_socket *listen, struct mn_socket *new);
> +    } listen;
> +};
> +
> +struct mn_sockaddr {
> +    uint8_t msa_len;
> +    uint8_t msa_family;
> +    char    msa_data[2];
> +};
> +
> +struct mn_sockaddr_in {
> +    uint8_t msin_len;
> +    uint8_t msin_family;
> +    uint16_t msin_port;
> +    uint32_t msin_addr;
> +};
> +
> +struct mn_sockaddr_in6 {
> +    uint8_t msin6_len;
> +    uint8_t msin6_family;
> +    uint16_t msin6_port;
> +    uint32_t msin6_flowinfo;
> +    uint32_t msin6_addr[4];
> +};
> +
> +/*
> + * Socket calls.
> + *
> + * mn_connect() for TCP is asynchronous. Once connection has been 
> established,
> + * socket callback (*writable) will be called.
> + *
> + * mn_sendto() is asynchronous as well. If it fails due to buffer shortage,
> + * socket provider should call (*writable) when more data can be sent.
> + *
> + * mn_recvfrom() returns immediatelly if no data is available. If data 
> arrives,
> + * the callback (*readable) will be called. Once that happens, owner of the
> + * socket should keep calling mn_recvfrom() until it has drained all the
> + * data from the socket.
> + *
> + * If remote end closes the socket, socket callback (*readable) will be
> + * called.
> + */
> +int mn_socket(struct mn_socket **, uint8_t domain, uint8_t type, uint8_t 
> proto);
> +int mn_bind(struct mn_socket *, struct mn_sockaddr *);
> +int mn_connect(struct mn_socket *, struct mn_sockaddr *);
> +int mn_listen(struct mn_socket *, uint8_t qlen);
> +
> +int mn_recvfrom(struct mn_socket *, struct os_mbuf **,
> +  struct mn_sockaddr *from);
> +int mn_sendto(struct mn_socket *, struct os_mbuf *, struct mn_sockaddr *to);
> +
> +int mn_getsockopt(struct mn_socket *, uint8_t level, uint8_t optname,
> +  void *optval);
> +int mn_setsockopt(struct mn_socket *, uint8_t level, uint8_t optname,
> +  void *optval);
> +
> +int mn_getsockname(struct mn_socket *, struct mn_sockaddr *);
> +int mn_getpeername(struct mn_socket *, struct mn_sockaddr *);
> +
> +int mn_close(struct mn_socket *);
> +
> +#define mn_socket_set_cbs(sock, cbs) (sock)->ms_cbs = (cbs)
> +
> +/*
> + * Address conversion
> + */
> +int mn_inet_pton(int af, const char *src, void *dst);
> +
> +#endif /* __SYS_MN_SOCKET_H_ */
> 
> http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/eb24b4d8/sys/mn_socket/include/mn_socket/mn_socket_ops.h
> ----------------------------------------------------------------------
> diff --git a/sys/mn_socket/include/mn_socket/mn_socket_ops.h 
> b/sys/mn_socket/include/mn_socket/mn_socket_ops.h
> new file mode 100644
> index 0000000..6e768eb
> --- /dev/null
> +++ b/sys/mn_socket/include/mn_socket/mn_socket_ops.h
> @@ -0,0 +1,82 @@
> +/**
> + * 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.
> + */
> +#ifndef __SYS_MN_SOCKET_OPS_H_
> +#define __SYS_MN_SOCKET_OPS_H_
> +
> +#include <inttypes.h>
> +
> +/*
> + * Interface for socket providers.
> + * - mso_create() creates a socket, memory allocation has to be done by
> + *   the socket provider.
> + * - mso_close() closes the socket, memory should be freed. User should not
> + *   be using the socket pointer once it has been closed.
> + */
> +struct mn_socket_ops {
> +    int (*mso_create)(struct mn_socket **, uint8_t domain, uint8_t type,
> +      uint8_t protocol);
> +    int (*mso_close)(struct mn_socket *);
> +
> +    int (*mso_bind)(struct mn_socket *, struct mn_sockaddr *);
> +    int (*mso_connect)(struct mn_socket *, struct mn_sockaddr *);
> +    int (*mso_listen)(struct mn_socket *, uint8_t qlen);
> +
> +    int (*mso_sendto)(struct mn_socket *, struct os_mbuf *,
> +      struct mn_sockaddr *to);
> +    int (*mso_recvfrom)(struct mn_socket *, struct os_mbuf **,
> +      struct mn_sockaddr *from);
> +
> +    int (*mso_getsockopt)(struct mn_socket *, uint8_t level, uint8_t name,
> +      void *val);
> +    int (*mso_setsockopt)(struct mn_socket *, uint8_t level, uint8_t name,
> +      void *val);
> +
> +    int (*mso_getsockname)(struct mn_socket *, struct mn_sockaddr *);
> +    int (*mso_getpeername)(struct mn_socket *, struct mn_sockaddr *);
> +};
> +
> +int mn_socket_ops_reg(const struct mn_socket_ops *ops);
> +
> +static inline void
> +mn_socket_writable(struct mn_socket *s, int error)
> +{
> +    if (s->ms_cbs && s->ms_cbs->socket.writable) {
> +        s->ms_cbs->socket.writable(s, error);
> +    }
> +}
> +
> +static inline void
> +mn_socket_readable(struct mn_socket *s, int error)
> +{
> +    if (s->ms_cbs && s->ms_cbs->socket.readable) {
> +        s->ms_cbs->socket.readable(s, error);
> +    }
> +}
> +
> +static inline int
> +mn_socket_newconn(struct mn_socket *s, struct mn_socket *new)
> +{
> +    if (s->ms_cbs && s->ms_cbs->listen.newconn) {
> +        return s->ms_cbs->listen.newconn(s, new);
> +    } else {
> +        return -1;
> +    }
> +}
> +
> +#endif /* __SYS_MN_SOCKET_OPS_H_ */
> 
> http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/eb24b4d8/sys/mn_socket/pkg.yml
> ----------------------------------------------------------------------
> diff --git a/sys/mn_socket/pkg.yml b/sys/mn_socket/pkg.yml
> new file mode 100644
> index 0000000..0c9c064
> --- /dev/null
> +++ b/sys/mn_socket/pkg.yml
> @@ -0,0 +1,31 @@
> +#
> +# 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.
> +#
> +
> +pkg.name: sys/mn_socket
> +pkg.description: Socket interface for Mynewt.
> +pkg.author: "Apache Mynewt <[email protected]>"
> +pkg.homepage: "http://mynewt.apache.org/";
> +pkg.keywords:
> +    - socket
> +    - IP
> +
> +pkg.deps:
> +    - libs/os
> +    - libs/util
> +    - libs/testutil
> 
> http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/eb24b4d8/sys/mn_socket/src/mn_socket.c
> ----------------------------------------------------------------------
> diff --git a/sys/mn_socket/src/mn_socket.c b/sys/mn_socket/src/mn_socket.c
> new file mode 100644
> index 0000000..2a3602c
> --- /dev/null
> +++ b/sys/mn_socket/src/mn_socket.c
> @@ -0,0 +1,123 @@
> +/**
> + * 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 <inttypes.h>
> +#include <assert.h>
> +
> +#include <os/os.h>
> +
> +#include "mn_socket/mn_socket.h"
> +#include "mn_socket/mn_socket_ops.h"
> +
> +/*
> + * Currently there can be just one provider of sockets.
> + */
> +static const struct mn_socket_ops *mn_sock_tgt;
> +
> +int
> +mn_socket_ops_reg(const struct mn_socket_ops *ops)
> +{
> +    if (mn_sock_tgt) {
> +        /*
> +         * XXXX for now.
> +         */
> +        return -1;
> +    }
> +    mn_sock_tgt = ops;
> +    return 0;
> +}
> +
> +int
> +mn_socket(struct mn_socket **sp, uint8_t domain, uint8_t type, uint8_t proto)
> +{
> +    int rc;
> +
> +    *sp = NULL;
> +    /*
> +     * XXX Look up where socket should go.
> +     */
> +    if (!mn_sock_tgt) {
> +        return MN_EINVAL;
> +    }
> +    rc = mn_sock_tgt->mso_create(sp, domain, type, proto);
> +    if (*sp) {
> +        (*sp)->ms_ops = mn_sock_tgt;
> +    }
> +    return rc;
> +}
> +
> +int
> +mn_bind(struct mn_socket *s, struct mn_sockaddr *addr)
> +{
> +    return s->ms_ops->mso_bind(s, addr);
> +}
> +
> +int
> +mn_connect(struct mn_socket *s, struct mn_sockaddr *addr)
> +{
> +    return s->ms_ops->mso_connect(s, addr);
> +}
> +
> +int
> +mn_listen(struct mn_socket *s, uint8_t qlen)
> +{
> +    return s->ms_ops->mso_listen(s, qlen);
> +}
> +
> +int
> +mn_recvfrom(struct mn_socket *s, struct os_mbuf **mp, struct mn_sockaddr 
> *from)
> +{
> +    return s->ms_ops->mso_recvfrom(s, mp, from);
> +}
> +
> +int
> +mn_sendto(struct mn_socket *s, struct os_mbuf *m, struct mn_sockaddr *to)
> +{
> +    return s->ms_ops->mso_sendto(s, m, to);
> +}
> +
> +int
> +mn_getsockopt(struct mn_socket *s, uint8_t level, uint8_t name, void *val)
> +{
> +    return s->ms_ops->mso_getsockopt(s, level, name, val);
> +}
> +
> +int
> +mn_setsockopt(struct mn_socket *s, uint8_t level, uint8_t name, void *val)
> +{
> +    return s->ms_ops->mso_setsockopt(s, level, name, val);
> +}
> +
> +int
> +mn_getsockname(struct mn_socket *s, struct mn_sockaddr *addr)
> +{
> +    return s->ms_ops->mso_getsockname(s, addr);
> +}
> +
> +int
> +mn_getpeername(struct mn_socket *s, struct mn_sockaddr *addr)
> +{
> +    return s->ms_ops->mso_getpeername(s, addr);
> +}
> +
> +int
> +mn_close(struct mn_socket *s)
> +{
> +    return s->ms_ops->mso_close(s);
> +}
> 
> http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/eb24b4d8/sys/mn_socket/src/mn_socket_aconv.c
> ----------------------------------------------------------------------
> diff --git a/sys/mn_socket/src/mn_socket_aconv.c 
> b/sys/mn_socket/src/mn_socket_aconv.c
> new file mode 100644
> index 0000000..5053eac
> --- /dev/null
> +++ b/sys/mn_socket/src/mn_socket_aconv.c
> @@ -0,0 +1,57 @@
> +/**
> + * 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 <ctype.h>
> +#include <os/queue.h>
> +#include "mn_socket/mn_socket.h"
> +
> +int
> +mn_inet_pton(int af, const char *src, void *dst)
> +{
> +    const char *ch_src;
> +    uint8_t *ch_tgt;
> +    int val;
> +    int cnt;
> +
> +    if (af == MN_PF_INET) {
> +        cnt = 0;
> +        ch_tgt = dst;
> +        val = 0;
> +        for (ch_src = src; *ch_src; ch_src++) {
> +            if (cnt > 4) {
> +                return 0;
> +            }
> +            if (isdigit(*ch_src)) {
> +                val = val * 10 + *ch_src - '0';
> +                if (val > 255) {
> +                    return 0;
> +                }
> +                *ch_tgt = val;
> +            } else if (*ch_src == '.') {
> +                ++cnt;
> +                val = 0;
> +                ch_tgt++;
> +            } else {
> +                return 0;
> +            }
> +        }
> +        return 1;
> +    } else {
> +        return MN_EAFNOSUPPORT;
> +    }
> +}
> 
> http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/eb24b4d8/sys/mn_socket/src/test/mn_sock_test.c
> ----------------------------------------------------------------------
> diff --git a/sys/mn_socket/src/test/mn_sock_test.c 
> b/sys/mn_socket/src/test/mn_sock_test.c
> new file mode 100644
> index 0000000..a8d9cc2
> --- /dev/null
> +++ b/sys/mn_socket/src/test/mn_sock_test.c
> @@ -0,0 +1,82 @@
> +/**
> + * 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 <stdio.h>
> +#include <string.h>
> +
> +#include <os/os.h>
> +#include <testutil/testutil.h>
> +
> +#include "mn_socket/mn_socket.h"
> +
> +TEST_CASE(inet_pton_test)
> +{
> +    int rc;
> +    uint8_t addr[8];
> +    struct test_vec {
> +        char *str;
> +        uint8_t cmp[4];
> +    };
> +    struct test_vec ok_vec[] = {
> +        { "1.1.1.1", { 1, 1, 1, 1 } },
> +        { "1.2.3.4", { 1, 2, 3, 4 } },
> +        { "010.001.255.255", { 10, 1, 255, 255 } },
> +        { "001.002.005.006", { 1, 2, 5, 6 } }
> +    };
> +    struct test_vec invalid_vec[] = {
> +        { "a.b.c.d" },
> +        { "1a.b3.4.2" },
> +        { "1.3.4.2a" },
> +        { "1111.3.4.2" },
> +        { "3.256.1.0" },
> +    };
> +    int i;
> +
> +    for (i = 0; i < sizeof(ok_vec) / sizeof(ok_vec[0]); i++) {
> +        memset(addr, 0xa5, sizeof(addr));
> +        rc = mn_inet_pton(MN_PF_INET, ok_vec[i].str, addr);
> +        TEST_ASSERT(rc == 1);
> +        TEST_ASSERT(!memcmp(ok_vec[i].cmp, addr, sizeof(uint32_t)));
> +        TEST_ASSERT(addr[5] == 0xa5);
> +    }
> +    for (i = 0; i < sizeof(invalid_vec) / sizeof(invalid_vec[0]); i++) {
> +        rc = mn_inet_pton(MN_PF_INET, invalid_vec[i].str, addr);
> +        TEST_ASSERT(rc == 0);
> +    }
> +}
> +
> +TEST_SUITE(mn_socket_test_all)
> +{
> +    inet_pton_test();
> +}
> +
> +#ifdef MYNEWT_SELFTEST
> +
> +int
> +main(int argc, char **argv)
> +{
> +    tu_config.tc_print_results = 1;
> +    tu_init();
> +
> +    mn_socket_test_all();
> +
> +    return tu_any_failed;
> +}
> +#endif
> +
> 

Reply via email to