1195972f6Sopenharmony_ci/**
2195972f6Sopenharmony_ci * @file
3195972f6Sopenharmony_ci * Application layered TCP connection API (to be used from TCPIP thread)\n
4195972f6Sopenharmony_ci * This interface mimics the tcp callback API to the application while preventing
5195972f6Sopenharmony_ci * direct linking (much like virtual functions).
6195972f6Sopenharmony_ci * This way, an application can make use of other application layer protocols
7195972f6Sopenharmony_ci * on top of TCP without knowing the details (e.g. TLS, proxy connection).
8195972f6Sopenharmony_ci *
9195972f6Sopenharmony_ci * This file contains the base implementation calling into tcp.
10195972f6Sopenharmony_ci */
11195972f6Sopenharmony_ci
12195972f6Sopenharmony_ci/*
13195972f6Sopenharmony_ci * Copyright (c) 2017 Simon Goldschmidt
14195972f6Sopenharmony_ci * All rights reserved.
15195972f6Sopenharmony_ci *
16195972f6Sopenharmony_ci * Redistribution and use in source and binary forms, with or without modification,
17195972f6Sopenharmony_ci * are permitted provided that the following conditions are met:
18195972f6Sopenharmony_ci *
19195972f6Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright notice,
20195972f6Sopenharmony_ci *    this list of conditions and the following disclaimer.
21195972f6Sopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright notice,
22195972f6Sopenharmony_ci *    this list of conditions and the following disclaimer in the documentation
23195972f6Sopenharmony_ci *    and/or other materials provided with the distribution.
24195972f6Sopenharmony_ci * 3. The name of the author may not be used to endorse or promote products
25195972f6Sopenharmony_ci *    derived from this software without specific prior written permission.
26195972f6Sopenharmony_ci *
27195972f6Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
28195972f6Sopenharmony_ci * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
29195972f6Sopenharmony_ci * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
30195972f6Sopenharmony_ci * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
31195972f6Sopenharmony_ci * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
32195972f6Sopenharmony_ci * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33195972f6Sopenharmony_ci * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34195972f6Sopenharmony_ci * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
35195972f6Sopenharmony_ci * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
36195972f6Sopenharmony_ci * OF SUCH DAMAGE.
37195972f6Sopenharmony_ci *
38195972f6Sopenharmony_ci * This file is part of the lwIP TCP/IP stack.
39195972f6Sopenharmony_ci *
40195972f6Sopenharmony_ci * Author: Simon Goldschmidt <goldsimon@gmx.de>
41195972f6Sopenharmony_ci *
42195972f6Sopenharmony_ci */
43195972f6Sopenharmony_ci
44195972f6Sopenharmony_ci#include "lwip/opt.h"
45195972f6Sopenharmony_ci
46195972f6Sopenharmony_ci#if LWIP_ALTCP /* don't build if not configured for use in lwipopts.h */
47195972f6Sopenharmony_ci
48195972f6Sopenharmony_ci#include "lwip/altcp.h"
49195972f6Sopenharmony_ci#include "lwip/altcp_tcp.h"
50195972f6Sopenharmony_ci#include "lwip/priv/altcp_priv.h"
51195972f6Sopenharmony_ci#include "lwip/tcp.h"
52195972f6Sopenharmony_ci#include "lwip/priv/tcp_priv.h"
53195972f6Sopenharmony_ci#include "lwip/mem.h"
54195972f6Sopenharmony_ci
55195972f6Sopenharmony_ci#include <string.h>
56195972f6Sopenharmony_ci
57195972f6Sopenharmony_ci#define ALTCP_TCP_ASSERT_CONN(conn) do { \
58195972f6Sopenharmony_ci  LWIP_ASSERT("conn->inner_conn == NULL", (conn)->inner_conn == NULL); \
59195972f6Sopenharmony_ci  LWIP_UNUSED_ARG(conn); /* for LWIP_NOASSERT */ } while(0)
60195972f6Sopenharmony_ci#define ALTCP_TCP_ASSERT_CONN_PCB(conn, tpcb) do { \
61195972f6Sopenharmony_ci  LWIP_ASSERT("pcb mismatch", (conn)->state == tpcb); \
62195972f6Sopenharmony_ci  LWIP_UNUSED_ARG(tpcb); /* for LWIP_NOASSERT */ \
63195972f6Sopenharmony_ci  ALTCP_TCP_ASSERT_CONN(conn); } while(0)
64195972f6Sopenharmony_ci
65195972f6Sopenharmony_ci
66195972f6Sopenharmony_ci/* Variable prototype, the actual declaration is at the end of this file
67195972f6Sopenharmony_ci   since it contains pointers to static functions declared here */
68195972f6Sopenharmony_ciextern const struct altcp_functions altcp_tcp_functions;
69195972f6Sopenharmony_ci
70195972f6Sopenharmony_cistatic void altcp_tcp_setup(struct altcp_pcb *conn, struct tcp_pcb *tpcb);
71195972f6Sopenharmony_ci
72195972f6Sopenharmony_ci/* callback functions for TCP */
73195972f6Sopenharmony_cistatic err_t
74195972f6Sopenharmony_cialtcp_tcp_accept(void *arg, struct tcp_pcb *new_tpcb, err_t err)
75195972f6Sopenharmony_ci{
76195972f6Sopenharmony_ci  struct altcp_pcb *listen_conn = (struct altcp_pcb *)arg;
77195972f6Sopenharmony_ci  if (listen_conn && listen_conn->accept) {
78195972f6Sopenharmony_ci    /* create a new altcp_conn to pass to the next 'accept' callback */
79195972f6Sopenharmony_ci    struct altcp_pcb *new_conn = altcp_alloc();
80195972f6Sopenharmony_ci    if (new_conn == NULL) {
81195972f6Sopenharmony_ci      return ERR_MEM;
82195972f6Sopenharmony_ci    }
83195972f6Sopenharmony_ci    altcp_tcp_setup(new_conn, new_tpcb);
84195972f6Sopenharmony_ci    return listen_conn->accept(listen_conn->arg, new_conn, err);
85195972f6Sopenharmony_ci  }
86195972f6Sopenharmony_ci  return ERR_ARG;
87195972f6Sopenharmony_ci}
88195972f6Sopenharmony_ci
89195972f6Sopenharmony_cistatic err_t
90195972f6Sopenharmony_cialtcp_tcp_connected(void *arg, struct tcp_pcb *tpcb, err_t err)
91195972f6Sopenharmony_ci{
92195972f6Sopenharmony_ci  struct altcp_pcb *conn = (struct altcp_pcb *)arg;
93195972f6Sopenharmony_ci  if (conn) {
94195972f6Sopenharmony_ci    ALTCP_TCP_ASSERT_CONN_PCB(conn, tpcb);
95195972f6Sopenharmony_ci    if (conn->connected) {
96195972f6Sopenharmony_ci      return conn->connected(conn->arg, conn, err);
97195972f6Sopenharmony_ci    }
98195972f6Sopenharmony_ci  }
99195972f6Sopenharmony_ci  return ERR_OK;
100195972f6Sopenharmony_ci}
101195972f6Sopenharmony_ci
102195972f6Sopenharmony_cistatic err_t
103195972f6Sopenharmony_cialtcp_tcp_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
104195972f6Sopenharmony_ci{
105195972f6Sopenharmony_ci  struct altcp_pcb *conn = (struct altcp_pcb *)arg;
106195972f6Sopenharmony_ci  if (conn) {
107195972f6Sopenharmony_ci    ALTCP_TCP_ASSERT_CONN_PCB(conn, tpcb);
108195972f6Sopenharmony_ci    if (conn->recv) {
109195972f6Sopenharmony_ci      return conn->recv(conn->arg, conn, p, err);
110195972f6Sopenharmony_ci    }
111195972f6Sopenharmony_ci  }
112195972f6Sopenharmony_ci  if (p != NULL) {
113195972f6Sopenharmony_ci    /* prevent memory leaks */
114195972f6Sopenharmony_ci    pbuf_free(p);
115195972f6Sopenharmony_ci  }
116195972f6Sopenharmony_ci  return ERR_OK;
117195972f6Sopenharmony_ci}
118195972f6Sopenharmony_ci
119195972f6Sopenharmony_cistatic err_t
120195972f6Sopenharmony_cialtcp_tcp_sent(void *arg, struct tcp_pcb *tpcb, u16_t len)
121195972f6Sopenharmony_ci{
122195972f6Sopenharmony_ci  struct altcp_pcb *conn = (struct altcp_pcb *)arg;
123195972f6Sopenharmony_ci  if (conn) {
124195972f6Sopenharmony_ci    ALTCP_TCP_ASSERT_CONN_PCB(conn, tpcb);
125195972f6Sopenharmony_ci    if (conn->sent) {
126195972f6Sopenharmony_ci      return conn->sent(conn->arg, conn, len);
127195972f6Sopenharmony_ci    }
128195972f6Sopenharmony_ci  }
129195972f6Sopenharmony_ci  return ERR_OK;
130195972f6Sopenharmony_ci}
131195972f6Sopenharmony_ci
132195972f6Sopenharmony_cistatic err_t
133195972f6Sopenharmony_cialtcp_tcp_poll(void *arg, struct tcp_pcb *tpcb)
134195972f6Sopenharmony_ci{
135195972f6Sopenharmony_ci  struct altcp_pcb *conn = (struct altcp_pcb *)arg;
136195972f6Sopenharmony_ci  if (conn) {
137195972f6Sopenharmony_ci    ALTCP_TCP_ASSERT_CONN_PCB(conn, tpcb);
138195972f6Sopenharmony_ci    if (conn->poll) {
139195972f6Sopenharmony_ci      return conn->poll(conn->arg, conn);
140195972f6Sopenharmony_ci    }
141195972f6Sopenharmony_ci  }
142195972f6Sopenharmony_ci  return ERR_OK;
143195972f6Sopenharmony_ci}
144195972f6Sopenharmony_ci
145195972f6Sopenharmony_cistatic void
146195972f6Sopenharmony_cialtcp_tcp_err(void *arg, err_t err)
147195972f6Sopenharmony_ci{
148195972f6Sopenharmony_ci  struct altcp_pcb *conn = (struct altcp_pcb *)arg;
149195972f6Sopenharmony_ci  if (conn) {
150195972f6Sopenharmony_ci    conn->state = NULL; /* already freed */
151195972f6Sopenharmony_ci    if (conn->err) {
152195972f6Sopenharmony_ci      conn->err(conn->arg, err);
153195972f6Sopenharmony_ci    }
154195972f6Sopenharmony_ci    altcp_free(conn);
155195972f6Sopenharmony_ci  }
156195972f6Sopenharmony_ci}
157195972f6Sopenharmony_ci
158195972f6Sopenharmony_ci/* setup functions */
159195972f6Sopenharmony_ci
160195972f6Sopenharmony_cistatic void
161195972f6Sopenharmony_cialtcp_tcp_remove_callbacks(struct tcp_pcb *tpcb)
162195972f6Sopenharmony_ci{
163195972f6Sopenharmony_ci  tcp_arg(tpcb, NULL);
164195972f6Sopenharmony_ci  if (tpcb->state != LISTEN) {
165195972f6Sopenharmony_ci    tcp_recv(tpcb, NULL);
166195972f6Sopenharmony_ci    tcp_sent(tpcb, NULL);
167195972f6Sopenharmony_ci    tcp_err(tpcb, NULL);
168195972f6Sopenharmony_ci    tcp_poll(tpcb, NULL, tpcb->pollinterval);
169195972f6Sopenharmony_ci  }
170195972f6Sopenharmony_ci}
171195972f6Sopenharmony_ci
172195972f6Sopenharmony_cistatic void
173195972f6Sopenharmony_cialtcp_tcp_setup_callbacks(struct altcp_pcb *conn, struct tcp_pcb *tpcb)
174195972f6Sopenharmony_ci{
175195972f6Sopenharmony_ci  tcp_arg(tpcb, conn);
176195972f6Sopenharmony_ci  /* this might be called for LISTN when close fails... */
177195972f6Sopenharmony_ci  if (tpcb->state != LISTEN) {
178195972f6Sopenharmony_ci    tcp_recv(tpcb, altcp_tcp_recv);
179195972f6Sopenharmony_ci    tcp_sent(tpcb, altcp_tcp_sent);
180195972f6Sopenharmony_ci    tcp_err(tpcb, altcp_tcp_err);
181195972f6Sopenharmony_ci    /* tcp_poll is set when interval is set by application */
182195972f6Sopenharmony_ci  }
183195972f6Sopenharmony_ci}
184195972f6Sopenharmony_ci
185195972f6Sopenharmony_cistatic void
186195972f6Sopenharmony_cialtcp_tcp_setup(struct altcp_pcb *conn, struct tcp_pcb *tpcb)
187195972f6Sopenharmony_ci{
188195972f6Sopenharmony_ci  altcp_tcp_setup_callbacks(conn, tpcb);
189195972f6Sopenharmony_ci  conn->state = tpcb;
190195972f6Sopenharmony_ci  conn->fns = &altcp_tcp_functions;
191195972f6Sopenharmony_ci}
192195972f6Sopenharmony_ci
193195972f6Sopenharmony_cistruct altcp_pcb *
194195972f6Sopenharmony_cialtcp_tcp_new_ip_type(u8_t ip_type)
195195972f6Sopenharmony_ci{
196195972f6Sopenharmony_ci  /* Allocate the tcp pcb first to invoke the priority handling code
197195972f6Sopenharmony_ci     if we're out of pcbs */
198195972f6Sopenharmony_ci  struct tcp_pcb *tpcb = tcp_new_ip_type(ip_type);
199195972f6Sopenharmony_ci  if (tpcb != NULL) {
200195972f6Sopenharmony_ci    struct altcp_pcb *ret = altcp_alloc();
201195972f6Sopenharmony_ci    if (ret != NULL) {
202195972f6Sopenharmony_ci      altcp_tcp_setup(ret, tpcb);
203195972f6Sopenharmony_ci      return ret;
204195972f6Sopenharmony_ci    } else {
205195972f6Sopenharmony_ci      /* altcp_pcb allocation failed -> free the tcp_pcb too */
206195972f6Sopenharmony_ci      tcp_close(tpcb);
207195972f6Sopenharmony_ci    }
208195972f6Sopenharmony_ci  }
209195972f6Sopenharmony_ci  return NULL;
210195972f6Sopenharmony_ci}
211195972f6Sopenharmony_ci
212195972f6Sopenharmony_ci/** altcp_tcp allocator function fitting to @ref altcp_allocator_t / @ref altcp_new.
213195972f6Sopenharmony_ci*
214195972f6Sopenharmony_ci* arg pointer is not used for TCP.
215195972f6Sopenharmony_ci*/
216195972f6Sopenharmony_cistruct altcp_pcb *
217195972f6Sopenharmony_cialtcp_tcp_alloc(void *arg, u8_t ip_type)
218195972f6Sopenharmony_ci{
219195972f6Sopenharmony_ci  LWIP_UNUSED_ARG(arg);
220195972f6Sopenharmony_ci  return altcp_tcp_new_ip_type(ip_type);
221195972f6Sopenharmony_ci}
222195972f6Sopenharmony_ci
223195972f6Sopenharmony_cistruct altcp_pcb *
224195972f6Sopenharmony_cialtcp_tcp_wrap(struct tcp_pcb *tpcb)
225195972f6Sopenharmony_ci{
226195972f6Sopenharmony_ci  if (tpcb != NULL) {
227195972f6Sopenharmony_ci    struct altcp_pcb *ret = altcp_alloc();
228195972f6Sopenharmony_ci    if (ret != NULL) {
229195972f6Sopenharmony_ci      altcp_tcp_setup(ret, tpcb);
230195972f6Sopenharmony_ci      return ret;
231195972f6Sopenharmony_ci    }
232195972f6Sopenharmony_ci  }
233195972f6Sopenharmony_ci  return NULL;
234195972f6Sopenharmony_ci}
235195972f6Sopenharmony_ci
236195972f6Sopenharmony_ci
237195972f6Sopenharmony_ci/* "virtual" functions calling into tcp */
238195972f6Sopenharmony_cistatic void
239195972f6Sopenharmony_cialtcp_tcp_set_poll(struct altcp_pcb *conn, u8_t interval)
240195972f6Sopenharmony_ci{
241195972f6Sopenharmony_ci  if (conn != NULL) {
242195972f6Sopenharmony_ci    struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
243195972f6Sopenharmony_ci    ALTCP_TCP_ASSERT_CONN(conn);
244195972f6Sopenharmony_ci    tcp_poll(pcb, altcp_tcp_poll, interval);
245195972f6Sopenharmony_ci  }
246195972f6Sopenharmony_ci}
247195972f6Sopenharmony_ci
248195972f6Sopenharmony_cistatic void
249195972f6Sopenharmony_cialtcp_tcp_recved(struct altcp_pcb *conn, u16_t len)
250195972f6Sopenharmony_ci{
251195972f6Sopenharmony_ci  if (conn != NULL) {
252195972f6Sopenharmony_ci    struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
253195972f6Sopenharmony_ci    ALTCP_TCP_ASSERT_CONN(conn);
254195972f6Sopenharmony_ci    tcp_recved(pcb, len);
255195972f6Sopenharmony_ci  }
256195972f6Sopenharmony_ci}
257195972f6Sopenharmony_ci
258195972f6Sopenharmony_cistatic err_t
259195972f6Sopenharmony_cialtcp_tcp_bind(struct altcp_pcb *conn, const ip_addr_t *ipaddr, u16_t port)
260195972f6Sopenharmony_ci{
261195972f6Sopenharmony_ci  struct tcp_pcb *pcb;
262195972f6Sopenharmony_ci  if (conn == NULL) {
263195972f6Sopenharmony_ci    return ERR_VAL;
264195972f6Sopenharmony_ci  }
265195972f6Sopenharmony_ci  ALTCP_TCP_ASSERT_CONN(conn);
266195972f6Sopenharmony_ci  pcb = (struct tcp_pcb *)conn->state;
267195972f6Sopenharmony_ci  return tcp_bind(pcb, ipaddr, port);
268195972f6Sopenharmony_ci}
269195972f6Sopenharmony_ci
270195972f6Sopenharmony_cistatic err_t
271195972f6Sopenharmony_cialtcp_tcp_connect(struct altcp_pcb *conn, const ip_addr_t *ipaddr, u16_t port, altcp_connected_fn connected)
272195972f6Sopenharmony_ci{
273195972f6Sopenharmony_ci  struct tcp_pcb *pcb;
274195972f6Sopenharmony_ci  if (conn == NULL) {
275195972f6Sopenharmony_ci    return ERR_VAL;
276195972f6Sopenharmony_ci  }
277195972f6Sopenharmony_ci  ALTCP_TCP_ASSERT_CONN(conn);
278195972f6Sopenharmony_ci  conn->connected = connected;
279195972f6Sopenharmony_ci  pcb = (struct tcp_pcb *)conn->state;
280195972f6Sopenharmony_ci  return tcp_connect(pcb, ipaddr, port, altcp_tcp_connected);
281195972f6Sopenharmony_ci}
282195972f6Sopenharmony_ci
283195972f6Sopenharmony_cistatic struct altcp_pcb *
284195972f6Sopenharmony_cialtcp_tcp_listen(struct altcp_pcb *conn, u8_t backlog, err_t *err)
285195972f6Sopenharmony_ci{
286195972f6Sopenharmony_ci  struct tcp_pcb *pcb;
287195972f6Sopenharmony_ci  struct tcp_pcb *lpcb;
288195972f6Sopenharmony_ci  if (conn == NULL) {
289195972f6Sopenharmony_ci    return NULL;
290195972f6Sopenharmony_ci  }
291195972f6Sopenharmony_ci  ALTCP_TCP_ASSERT_CONN(conn);
292195972f6Sopenharmony_ci  pcb = (struct tcp_pcb *)conn->state;
293195972f6Sopenharmony_ci  lpcb = tcp_listen_with_backlog_and_err(pcb, backlog, err);
294195972f6Sopenharmony_ci  if (lpcb != NULL) {
295195972f6Sopenharmony_ci    conn->state = lpcb;
296195972f6Sopenharmony_ci    tcp_accept(lpcb, altcp_tcp_accept);
297195972f6Sopenharmony_ci    return conn;
298195972f6Sopenharmony_ci  }
299195972f6Sopenharmony_ci  return NULL;
300195972f6Sopenharmony_ci}
301195972f6Sopenharmony_ci
302195972f6Sopenharmony_cistatic void
303195972f6Sopenharmony_cialtcp_tcp_abort(struct altcp_pcb *conn)
304195972f6Sopenharmony_ci{
305195972f6Sopenharmony_ci  if (conn != NULL) {
306195972f6Sopenharmony_ci    struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
307195972f6Sopenharmony_ci    ALTCP_TCP_ASSERT_CONN(conn);
308195972f6Sopenharmony_ci    if (pcb) {
309195972f6Sopenharmony_ci      tcp_abort(pcb);
310195972f6Sopenharmony_ci    }
311195972f6Sopenharmony_ci  }
312195972f6Sopenharmony_ci}
313195972f6Sopenharmony_ci
314195972f6Sopenharmony_cistatic err_t
315195972f6Sopenharmony_cialtcp_tcp_close(struct altcp_pcb *conn)
316195972f6Sopenharmony_ci{
317195972f6Sopenharmony_ci  struct tcp_pcb *pcb;
318195972f6Sopenharmony_ci  if (conn == NULL) {
319195972f6Sopenharmony_ci    return ERR_VAL;
320195972f6Sopenharmony_ci  }
321195972f6Sopenharmony_ci  ALTCP_TCP_ASSERT_CONN(conn);
322195972f6Sopenharmony_ci  pcb = (struct tcp_pcb *)conn->state;
323195972f6Sopenharmony_ci  if (pcb) {
324195972f6Sopenharmony_ci    err_t err;
325195972f6Sopenharmony_ci    tcp_poll_fn oldpoll = pcb->poll;
326195972f6Sopenharmony_ci    altcp_tcp_remove_callbacks(pcb);
327195972f6Sopenharmony_ci    err = tcp_close(pcb);
328195972f6Sopenharmony_ci    if (err != ERR_OK) {
329195972f6Sopenharmony_ci      /* not closed, set up all callbacks again */
330195972f6Sopenharmony_ci      altcp_tcp_setup_callbacks(conn, pcb);
331195972f6Sopenharmony_ci      /* poll callback is not included in the above */
332195972f6Sopenharmony_ci      tcp_poll(pcb, oldpoll, pcb->pollinterval);
333195972f6Sopenharmony_ci      return err;
334195972f6Sopenharmony_ci    }
335195972f6Sopenharmony_ci    conn->state = NULL; /* unsafe to reference pcb after tcp_close(). */
336195972f6Sopenharmony_ci  }
337195972f6Sopenharmony_ci  altcp_free(conn);
338195972f6Sopenharmony_ci  return ERR_OK;
339195972f6Sopenharmony_ci}
340195972f6Sopenharmony_ci
341195972f6Sopenharmony_cistatic err_t
342195972f6Sopenharmony_cialtcp_tcp_shutdown(struct altcp_pcb *conn, int shut_rx, int shut_tx)
343195972f6Sopenharmony_ci{
344195972f6Sopenharmony_ci  struct tcp_pcb *pcb;
345195972f6Sopenharmony_ci  if (conn == NULL) {
346195972f6Sopenharmony_ci    return ERR_VAL;
347195972f6Sopenharmony_ci  }
348195972f6Sopenharmony_ci  ALTCP_TCP_ASSERT_CONN(conn);
349195972f6Sopenharmony_ci  pcb = (struct tcp_pcb *)conn->state;
350195972f6Sopenharmony_ci  return tcp_shutdown(pcb, shut_rx, shut_tx);
351195972f6Sopenharmony_ci}
352195972f6Sopenharmony_ci
353195972f6Sopenharmony_cistatic err_t
354195972f6Sopenharmony_cialtcp_tcp_write(struct altcp_pcb *conn, const void *dataptr, u16_t len, u8_t apiflags)
355195972f6Sopenharmony_ci{
356195972f6Sopenharmony_ci  struct tcp_pcb *pcb;
357195972f6Sopenharmony_ci  if (conn == NULL) {
358195972f6Sopenharmony_ci    return ERR_VAL;
359195972f6Sopenharmony_ci  }
360195972f6Sopenharmony_ci  ALTCP_TCP_ASSERT_CONN(conn);
361195972f6Sopenharmony_ci  pcb = (struct tcp_pcb *)conn->state;
362195972f6Sopenharmony_ci  return tcp_write(pcb, dataptr, len, apiflags);
363195972f6Sopenharmony_ci}
364195972f6Sopenharmony_ci
365195972f6Sopenharmony_cistatic err_t
366195972f6Sopenharmony_cialtcp_tcp_output(struct altcp_pcb *conn)
367195972f6Sopenharmony_ci{
368195972f6Sopenharmony_ci  struct tcp_pcb *pcb;
369195972f6Sopenharmony_ci  if (conn == NULL) {
370195972f6Sopenharmony_ci    return ERR_VAL;
371195972f6Sopenharmony_ci  }
372195972f6Sopenharmony_ci  ALTCP_TCP_ASSERT_CONN(conn);
373195972f6Sopenharmony_ci  pcb = (struct tcp_pcb *)conn->state;
374195972f6Sopenharmony_ci  return tcp_output(pcb);
375195972f6Sopenharmony_ci}
376195972f6Sopenharmony_ci
377195972f6Sopenharmony_cistatic u16_t
378195972f6Sopenharmony_cialtcp_tcp_mss(struct altcp_pcb *conn)
379195972f6Sopenharmony_ci{
380195972f6Sopenharmony_ci  struct tcp_pcb *pcb;
381195972f6Sopenharmony_ci  if (conn == NULL) {
382195972f6Sopenharmony_ci    return 0;
383195972f6Sopenharmony_ci  }
384195972f6Sopenharmony_ci  ALTCP_TCP_ASSERT_CONN(conn);
385195972f6Sopenharmony_ci  pcb = (struct tcp_pcb *)conn->state;
386195972f6Sopenharmony_ci  return tcp_mss(pcb);
387195972f6Sopenharmony_ci}
388195972f6Sopenharmony_ci
389195972f6Sopenharmony_cistatic u16_t
390195972f6Sopenharmony_cialtcp_tcp_sndbuf(struct altcp_pcb *conn)
391195972f6Sopenharmony_ci{
392195972f6Sopenharmony_ci  struct tcp_pcb *pcb;
393195972f6Sopenharmony_ci  if (conn == NULL) {
394195972f6Sopenharmony_ci    return 0;
395195972f6Sopenharmony_ci  }
396195972f6Sopenharmony_ci  ALTCP_TCP_ASSERT_CONN(conn);
397195972f6Sopenharmony_ci  pcb = (struct tcp_pcb *)conn->state;
398195972f6Sopenharmony_ci  return tcp_sndbuf(pcb);
399195972f6Sopenharmony_ci}
400195972f6Sopenharmony_ci
401195972f6Sopenharmony_cistatic u16_t
402195972f6Sopenharmony_cialtcp_tcp_sndqueuelen(struct altcp_pcb *conn)
403195972f6Sopenharmony_ci{
404195972f6Sopenharmony_ci  struct tcp_pcb *pcb;
405195972f6Sopenharmony_ci  if (conn == NULL) {
406195972f6Sopenharmony_ci    return 0;
407195972f6Sopenharmony_ci  }
408195972f6Sopenharmony_ci  ALTCP_TCP_ASSERT_CONN(conn);
409195972f6Sopenharmony_ci  pcb = (struct tcp_pcb *)conn->state;
410195972f6Sopenharmony_ci  return tcp_sndqueuelen(pcb);
411195972f6Sopenharmony_ci}
412195972f6Sopenharmony_ci
413195972f6Sopenharmony_cistatic void
414195972f6Sopenharmony_cialtcp_tcp_nagle_disable(struct altcp_pcb *conn)
415195972f6Sopenharmony_ci{
416195972f6Sopenharmony_ci  if (conn && conn->state) {
417195972f6Sopenharmony_ci    struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
418195972f6Sopenharmony_ci    ALTCP_TCP_ASSERT_CONN(conn);
419195972f6Sopenharmony_ci    tcp_nagle_disable(pcb);
420195972f6Sopenharmony_ci  }
421195972f6Sopenharmony_ci}
422195972f6Sopenharmony_ci
423195972f6Sopenharmony_cistatic void
424195972f6Sopenharmony_cialtcp_tcp_nagle_enable(struct altcp_pcb *conn)
425195972f6Sopenharmony_ci{
426195972f6Sopenharmony_ci  if (conn && conn->state) {
427195972f6Sopenharmony_ci    struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
428195972f6Sopenharmony_ci    ALTCP_TCP_ASSERT_CONN(conn);
429195972f6Sopenharmony_ci    tcp_nagle_enable(pcb);
430195972f6Sopenharmony_ci  }
431195972f6Sopenharmony_ci}
432195972f6Sopenharmony_ci
433195972f6Sopenharmony_cistatic int
434195972f6Sopenharmony_cialtcp_tcp_nagle_disabled(struct altcp_pcb *conn)
435195972f6Sopenharmony_ci{
436195972f6Sopenharmony_ci  if (conn && conn->state) {
437195972f6Sopenharmony_ci    struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
438195972f6Sopenharmony_ci    ALTCP_TCP_ASSERT_CONN(conn);
439195972f6Sopenharmony_ci    return tcp_nagle_disabled(pcb);
440195972f6Sopenharmony_ci  }
441195972f6Sopenharmony_ci  return 0;
442195972f6Sopenharmony_ci}
443195972f6Sopenharmony_ci
444195972f6Sopenharmony_cistatic void
445195972f6Sopenharmony_cialtcp_tcp_setprio(struct altcp_pcb *conn, u8_t prio)
446195972f6Sopenharmony_ci{
447195972f6Sopenharmony_ci  if (conn != NULL) {
448195972f6Sopenharmony_ci    struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
449195972f6Sopenharmony_ci    ALTCP_TCP_ASSERT_CONN(conn);
450195972f6Sopenharmony_ci    tcp_setprio(pcb, prio);
451195972f6Sopenharmony_ci  }
452195972f6Sopenharmony_ci}
453195972f6Sopenharmony_ci
454195972f6Sopenharmony_ci#if LWIP_TCP_KEEPALIVE
455195972f6Sopenharmony_cistatic void
456195972f6Sopenharmony_cialtcp_tcp_keepalive_disable(struct altcp_pcb *conn)
457195972f6Sopenharmony_ci{
458195972f6Sopenharmony_ci  if (conn && conn->state) {
459195972f6Sopenharmony_ci    struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
460195972f6Sopenharmony_ci    ALTCP_TCP_ASSERT_CONN(conn);
461195972f6Sopenharmony_ci    ip_reset_option(pcb, SOF_KEEPALIVE);
462195972f6Sopenharmony_ci  }
463195972f6Sopenharmony_ci}
464195972f6Sopenharmony_ci
465195972f6Sopenharmony_cistatic void
466195972f6Sopenharmony_cialtcp_tcp_keepalive_enable(struct altcp_pcb *conn, u32_t idle, u32_t intvl, u32_t cnt)
467195972f6Sopenharmony_ci{
468195972f6Sopenharmony_ci  if (conn && conn->state) {
469195972f6Sopenharmony_ci    struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
470195972f6Sopenharmony_ci    ALTCP_TCP_ASSERT_CONN(conn);
471195972f6Sopenharmony_ci    ip_set_option(pcb, SOF_KEEPALIVE);
472195972f6Sopenharmony_ci    pcb->keep_idle = idle ? idle : TCP_KEEPIDLE_DEFAULT;
473195972f6Sopenharmony_ci    pcb->keep_intvl = intvl ? intvl : TCP_KEEPINTVL_DEFAULT;
474195972f6Sopenharmony_ci    pcb->keep_cnt = cnt ? cnt : TCP_KEEPCNT_DEFAULT;
475195972f6Sopenharmony_ci  }
476195972f6Sopenharmony_ci}
477195972f6Sopenharmony_ci#endif
478195972f6Sopenharmony_ci
479195972f6Sopenharmony_cistatic void
480195972f6Sopenharmony_cialtcp_tcp_dealloc(struct altcp_pcb *conn)
481195972f6Sopenharmony_ci{
482195972f6Sopenharmony_ci  LWIP_UNUSED_ARG(conn);
483195972f6Sopenharmony_ci  ALTCP_TCP_ASSERT_CONN(conn);
484195972f6Sopenharmony_ci  /* no private state to clean up */
485195972f6Sopenharmony_ci}
486195972f6Sopenharmony_ci
487195972f6Sopenharmony_cistatic err_t
488195972f6Sopenharmony_cialtcp_tcp_get_tcp_addrinfo(struct altcp_pcb *conn, int local, ip_addr_t *addr, u16_t *port)
489195972f6Sopenharmony_ci{
490195972f6Sopenharmony_ci  if (conn) {
491195972f6Sopenharmony_ci    struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
492195972f6Sopenharmony_ci    ALTCP_TCP_ASSERT_CONN(conn);
493195972f6Sopenharmony_ci    return tcp_tcp_get_tcp_addrinfo(pcb, local, addr, port);
494195972f6Sopenharmony_ci  }
495195972f6Sopenharmony_ci  return ERR_VAL;
496195972f6Sopenharmony_ci}
497195972f6Sopenharmony_ci
498195972f6Sopenharmony_cistatic ip_addr_t *
499195972f6Sopenharmony_cialtcp_tcp_get_ip(struct altcp_pcb *conn, int local)
500195972f6Sopenharmony_ci{
501195972f6Sopenharmony_ci  if (conn) {
502195972f6Sopenharmony_ci    struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
503195972f6Sopenharmony_ci    ALTCP_TCP_ASSERT_CONN(conn);
504195972f6Sopenharmony_ci    if (pcb) {
505195972f6Sopenharmony_ci      if (local) {
506195972f6Sopenharmony_ci        return &pcb->local_ip;
507195972f6Sopenharmony_ci      } else {
508195972f6Sopenharmony_ci        return &pcb->remote_ip;
509195972f6Sopenharmony_ci      }
510195972f6Sopenharmony_ci    }
511195972f6Sopenharmony_ci  }
512195972f6Sopenharmony_ci  return NULL;
513195972f6Sopenharmony_ci}
514195972f6Sopenharmony_ci
515195972f6Sopenharmony_cistatic u16_t
516195972f6Sopenharmony_cialtcp_tcp_get_port(struct altcp_pcb *conn, int local)
517195972f6Sopenharmony_ci{
518195972f6Sopenharmony_ci  if (conn) {
519195972f6Sopenharmony_ci    struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
520195972f6Sopenharmony_ci    ALTCP_TCP_ASSERT_CONN(conn);
521195972f6Sopenharmony_ci    if (pcb) {
522195972f6Sopenharmony_ci      if (local) {
523195972f6Sopenharmony_ci        return pcb->local_port;
524195972f6Sopenharmony_ci      } else {
525195972f6Sopenharmony_ci        return pcb->remote_port;
526195972f6Sopenharmony_ci      }
527195972f6Sopenharmony_ci    }
528195972f6Sopenharmony_ci  }
529195972f6Sopenharmony_ci  return 0;
530195972f6Sopenharmony_ci}
531195972f6Sopenharmony_ci
532195972f6Sopenharmony_ci#ifdef LWIP_DEBUG
533195972f6Sopenharmony_cistatic enum tcp_state
534195972f6Sopenharmony_cialtcp_tcp_dbg_get_tcp_state(struct altcp_pcb *conn)
535195972f6Sopenharmony_ci{
536195972f6Sopenharmony_ci  if (conn) {
537195972f6Sopenharmony_ci    struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
538195972f6Sopenharmony_ci    ALTCP_TCP_ASSERT_CONN(conn);
539195972f6Sopenharmony_ci    if (pcb) {
540195972f6Sopenharmony_ci      return pcb->state;
541195972f6Sopenharmony_ci    }
542195972f6Sopenharmony_ci  }
543195972f6Sopenharmony_ci  return CLOSED;
544195972f6Sopenharmony_ci}
545195972f6Sopenharmony_ci#endif
546195972f6Sopenharmony_ciconst struct altcp_functions altcp_tcp_functions = {
547195972f6Sopenharmony_ci  altcp_tcp_set_poll,
548195972f6Sopenharmony_ci  altcp_tcp_recved,
549195972f6Sopenharmony_ci  altcp_tcp_bind,
550195972f6Sopenharmony_ci  altcp_tcp_connect,
551195972f6Sopenharmony_ci  altcp_tcp_listen,
552195972f6Sopenharmony_ci  altcp_tcp_abort,
553195972f6Sopenharmony_ci  altcp_tcp_close,
554195972f6Sopenharmony_ci  altcp_tcp_shutdown,
555195972f6Sopenharmony_ci  altcp_tcp_write,
556195972f6Sopenharmony_ci  altcp_tcp_output,
557195972f6Sopenharmony_ci  altcp_tcp_mss,
558195972f6Sopenharmony_ci  altcp_tcp_sndbuf,
559195972f6Sopenharmony_ci  altcp_tcp_sndqueuelen,
560195972f6Sopenharmony_ci  altcp_tcp_nagle_disable,
561195972f6Sopenharmony_ci  altcp_tcp_nagle_enable,
562195972f6Sopenharmony_ci  altcp_tcp_nagle_disabled,
563195972f6Sopenharmony_ci  altcp_tcp_setprio,
564195972f6Sopenharmony_ci  altcp_tcp_dealloc,
565195972f6Sopenharmony_ci  altcp_tcp_get_tcp_addrinfo,
566195972f6Sopenharmony_ci  altcp_tcp_get_ip,
567195972f6Sopenharmony_ci  altcp_tcp_get_port
568195972f6Sopenharmony_ci#if LWIP_TCP_KEEPALIVE
569195972f6Sopenharmony_ci  , altcp_tcp_keepalive_disable
570195972f6Sopenharmony_ci  , altcp_tcp_keepalive_enable
571195972f6Sopenharmony_ci#endif
572195972f6Sopenharmony_ci#ifdef LWIP_DEBUG
573195972f6Sopenharmony_ci  , altcp_tcp_dbg_get_tcp_state
574195972f6Sopenharmony_ci#endif
575195972f6Sopenharmony_ci};
576195972f6Sopenharmony_ci
577195972f6Sopenharmony_ci#endif /* LWIP_ALTCP */
578