dRonin  adbada4
dRonin firmware
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
pios_tcp.c
Go to the documentation of this file.
1 
16 /*
17  * This program is free software; you can redistribute it and/or modify
18  * it under the terms of the GNU General Public License as published by
19  * the Free Software Foundation; either version 3 of the License, or
20  * (at your option) any later version.
21  *
22  * This program is distributed in the hope that it will be useful, but
23  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
24  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
25  * for more details.
26  *
27  * You should have received a copy of the GNU General Public License along
28  * with this program; if not, see <http://www.gnu.org/licenses/>
29  *
30  * Additional note on redistribution: The copyright and license notices above
31  * must be maintained in each individual source file that is a derivative work
32  * of this source file; otherwise redistribution is prohibited.
33  */
34 
35 
36 /* Project Includes */
37 #include "pios.h"
38 
39 #if defined(PIOS_INCLUDE_TCP)
40 
41 #include <pios_tcp_priv.h>
42 #include "pios_thread.h"
43 #include <unistd.h>
44 #include <sys/types.h>
45 #include <errno.h>
46 #include <fcntl.h>
47 
48 #ifndef INVALID_SOCKET
49 #define INVALID_SOCKET (-1)
50 #endif
51 
52 /* Provide a COM driver */
53 static void PIOS_TCP_ChangeBaud(uintptr_t tcp_id, uint32_t baud);
54 static void PIOS_TCP_RegisterRxCallback(uintptr_t tcp_id, pios_com_callback rx_in_cb, uintptr_t context);
55 static void PIOS_TCP_RegisterTxCallback(uintptr_t tcp_id, pios_com_callback tx_out_cb, uintptr_t context);
56 static void PIOS_TCP_TxStart(uintptr_t tcp_id, uint16_t tx_bytes_avail);
57 static void PIOS_TCP_RxStart(uintptr_t tcp_id, uint16_t rx_bytes_avail);
58 static bool PIOS_TCP_Available(uintptr_t tcp_id);
59 
60 typedef struct {
61  const struct pios_tcp_cfg * cfg;
62 
63  int socket;
64  struct sockaddr_in6 server;
65  struct sockaddr_in6 client;
66  int socket_connection;
67 
68  pios_com_callback tx_out_cb;
69  uintptr_t tx_out_context;
70  pios_com_callback rx_in_cb;
71  uintptr_t rx_in_context;
72 
73  uint8_t rx_buffer[PIOS_TCP_RX_BUFFER_SIZE];
74  uint8_t tx_buffer[PIOS_TCP_RX_BUFFER_SIZE];
75 } pios_tcp_dev;
76 
77 const struct pios_com_driver pios_tcp_com_driver = {
78  .set_baud = PIOS_TCP_ChangeBaud,
79  .tx_start = PIOS_TCP_TxStart,
80  .rx_start = PIOS_TCP_RxStart,
81  .bind_tx_cb = PIOS_TCP_RegisterTxCallback,
82  .bind_rx_cb = PIOS_TCP_RegisterRxCallback,
83  .available = PIOS_TCP_Available,
84 };
85 
86 
87 static pios_tcp_dev * find_tcp_dev_by_id(uintptr_t tcp)
88 {
89  return (pios_tcp_dev *) tcp;
90 }
91 
92 static void rx_cb_all(pios_tcp_dev *tcp_dev, uint8_t *incoming_buffer,
93  int len) {
94  int sent = 0;
95 
96  bool rx_need_yield = false;
97 
98  sent = tcp_dev->rx_in_cb(tcp_dev->rx_in_context, incoming_buffer, len,
99  NULL, &rx_need_yield);
100 
101  if (sent < 0) {
102  return;
103  }
104 
105  while (sent < len) {
107 
108  int sent_chunk = tcp_dev->rx_in_cb(tcp_dev->rx_in_context,
109  incoming_buffer, len, NULL, &rx_need_yield);
110 
111  if (sent_chunk < 0) {
112  return;
113  }
114 
115  sent += sent_chunk;
116  }
117 }
118 
122 static void PIOS_TCP_RxTask(void *tcp_dev_n)
123 {
124  pios_tcp_dev *tcp_dev = (pios_tcp_dev*)tcp_dev_n;
125 
126  const int INCOMING_BUFFER_SIZE = 16;
127  uint8_t incoming_buffer[INCOMING_BUFFER_SIZE];
128  int error;
129 
130  while (1) {
131 
132  do
133  {
134  tcp_dev->socket_connection = accept(tcp_dev->socket, NULL, NULL);
135  error = errno;
136 
138  } while (tcp_dev->socket_connection == INVALID_SOCKET && (error == EINTR || error == EAGAIN));
139 
140  if (tcp_dev->socket_connection < 0) {
141  int error = errno;
142  (void)error;
143  perror("Accept failed");
144  close(tcp_dev->socket);
145  exit(EXIT_FAILURE);
146  }
147 
148  fprintf(stderr, "Connection accepted\n");
149 
150  while (1) {
151  // Received is used to track the scoket whereas the dev variable is only updated when it can be
152 
153  int result = recv(tcp_dev->socket_connection,
154  (void *) incoming_buffer,
155  INCOMING_BUFFER_SIZE, 0);
156  error = errno;
157 
158  if (result > 0 && tcp_dev->rx_in_cb) {
159  /* While on other drivers it may be desirable to
160  * spill immediately if the consumer is not
161  * keeping up, TCP is self-regulating in speed
162  * and GCS may want to really hammer us. Let's
163  * not let the client run-ahead and force us to
164  * drop stuff that we've read.
165  */
166 
167  rx_cb_all(tcp_dev, incoming_buffer, result);
168 
169  }
170 
171  if (result == 0) {
172  break;
173  }
174 
175  if (result == -1) {
176  if (error == EAGAIN)
178  else if (error == EINTR)
179  (void)error;
180  else
181  break;
182  }
183  }
184 
185  close(tcp_dev->socket_connection);
186  tcp_dev->socket_connection = INVALID_SOCKET;
187  }
188 }
189 
190 
194 struct pios_thread *tcpRxTaskHandle;
195 int32_t PIOS_TCP_Init(uintptr_t *tcp_id, const struct pios_tcp_cfg * cfg)
196 {
197  pios_tcp_dev *tcp_dev = PIOS_malloc(sizeof(pios_tcp_dev));
198 
199  memset(tcp_dev, 0, sizeof(*tcp_dev));
200 
201  /* initialize */
202  tcp_dev->rx_in_cb = NULL;
203  tcp_dev->tx_out_cb = NULL;
204  tcp_dev->cfg=cfg;
205 
206  /* assign socket */
207  tcp_dev->socket = socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP);
208  tcp_dev->socket_connection = INVALID_SOCKET;
209 
210 #if defined(_WIN32) || defined(WIN32) || defined(__MINGW32__)
211  char optval = 1;
212  char optoff = 0;
213 
214  setsockopt(tcp_dev->socket, IPPROTO_IPV6, IPV6_V6ONLY, &optoff, sizeof(optoff));
215 #else
216  int optval = 1;
217 #endif
218 
219  /* Allow reuse of address if you restart. */
220  setsockopt(tcp_dev->socket, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
221 
222  /* Also request low-latency (don't store up data to conserve packets */
223  setsockopt(tcp_dev->socket, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval));
224 
225  memset(&tcp_dev->server, 0, sizeof(tcp_dev->server));
226  memset(&tcp_dev->client, 0, sizeof(tcp_dev->client));
227 
228  tcp_dev->server.sin6_family = AF_INET6;
229  tcp_dev->server.sin6_addr = in6addr_any;
230  tcp_dev->server.sin6_port = htons(tcp_dev->cfg->port);
231 
232  int res = bind(tcp_dev->socket, (struct sockaddr*)&tcp_dev->server, sizeof(tcp_dev->server));
233  if (res == -1) {
234  perror("Binding socket failed");
235  exit(EXIT_FAILURE);
236  }
237 
238  res = listen(tcp_dev->socket, 10);
239  if (res == -1) {
240  perror("Socket listen failed");
241  exit(EXIT_FAILURE);
242  }
243 
244  tcpRxTaskHandle = PIOS_Thread_Create(
245  PIOS_TCP_RxTask, "pios_tcp_rx", PIOS_THREAD_STACK_SIZE_MIN, tcp_dev, PIOS_THREAD_PRIO_HIGHEST);
246 
247  printf("tcp dev %p - socket %i opened - result %i\n", tcp_dev, tcp_dev->socket, res);
248 
249  *tcp_id = (uintptr_t) tcp_dev;
250 
251  return res;
252 }
253 
254 
255 void PIOS_TCP_ChangeBaud(uintptr_t tcp_id, uint32_t baud)
256 {
260 }
261 
262 
263 static void PIOS_TCP_RxStart(uintptr_t tp_id, uint16_t rx_bytes_avail)
264 {
268 }
269 
270 
271 static void PIOS_TCP_TxStart(uintptr_t tcp_id, uint16_t tx_bytes_avail)
272 {
273  pios_tcp_dev *tcp_dev = find_tcp_dev_by_id(tcp_id);
274 
275  PIOS_Assert(tcp_dev);
276 
277  int32_t length,rem;
278 
282  if (tcp_dev->tx_out_cb) {
283  while (tx_bytes_avail > 0) {
284  bool tx_need_yield = false;
285  length = (tcp_dev->tx_out_cb)(tcp_dev->tx_out_context, tcp_dev->tx_buffer, PIOS_TCP_RX_BUFFER_SIZE, NULL, &tx_need_yield);
286  rem = length;
287  while (rem > 0) {
288  ssize_t len = 0;
289  if (tcp_dev->socket_connection != INVALID_SOCKET) {
290  len = send(tcp_dev->socket_connection, (char *) tcp_dev->tx_buffer, length, 0);
291  }
292  if (len <= 0) {
293  rem = 0;
294  } else {
295  rem -= len;
296  }
297  }
298  tx_bytes_avail -= length;
299  }
300  }
301 }
302 
303 static void PIOS_TCP_RegisterRxCallback(uintptr_t tcp_id, pios_com_callback rx_in_cb, uintptr_t context)
304 {
305  pios_tcp_dev *tcp_dev = find_tcp_dev_by_id(tcp_id);
306 
307  PIOS_Assert(tcp_dev);
308 
309  /*
310  * Order is important in these assignments since ISR uses _cb
311  * field to determine if it's ok to dereference _cb and _context
312  */
313  tcp_dev->rx_in_context = context;
314  tcp_dev->rx_in_cb = rx_in_cb;
315 }
316 
317 static void PIOS_TCP_RegisterTxCallback(uintptr_t tcp_id, pios_com_callback tx_out_cb, uintptr_t context)
318 {
319  pios_tcp_dev *tcp_dev = find_tcp_dev_by_id(tcp_id);
320 
321  PIOS_Assert(tcp_dev);
322 
323  /*
324  * Order is important in these assignments since ISR uses _cb
325  * field to determine if it's ok to dereference _cb and _context
326  */
327  tcp_dev->tx_out_context = context;
328  tcp_dev->tx_out_cb = tx_out_cb;
329 }
330 
331 
332 static bool PIOS_TCP_Available(uintptr_t tcp_id)
333 {
334  pios_tcp_dev *tcp_dev = find_tcp_dev_by_id(tcp_id);
335 
336  PIOS_Assert(tcp_dev);
337 
338  return tcp_dev->socket_connection != INVALID_SOCKET;
339 }
340 
341 #endif
342 
Main PiOS header to include all the compiled in PiOS options.
TCP private definitions.
void * PIOS_malloc(size_t size)
Definition: pios_heap.c:125
uint8_t length
static struct flyingpicmd_cfg_fa cfg
Definition: main.c:49
#define PIOS_THREAD_STACK_SIZE_MIN
Definition: pios_thread.h:55
#define INVALID_SOCKET
struct pios_thread * PIOS_Thread_Create(void(*fp)(void *), const char *namep, size_t stack_bytes, void *argp, enum pios_thread_prio_e prio)
Definition: pios_thread.c:89
void PIOS_Thread_Sleep(uint32_t time_ms)
Definition: pios_thread.c:229
int32_t PIOS_TCP_Init(uintptr_t *tcp_id, const struct pios_tcp_cfg *cfg)
int printf(const char *format,...)
const struct pios_com_driver pios_tcp_com_driver
#define PIOS_Assert(test)
Definition: pios_debug.h:52
void error(int)
Definition: main.c:130
#define PIOS_TCP_RX_BUFFER_SIZE
Definition: pios_board.h:53
uint16_t(* pios_com_callback)(uintptr_t context, uint8_t *buf, uint16_t buf_len, uint16_t *headroom, bool *task_woken)
Definition: pios_com.h:41
void(* set_baud)(uintptr_t id, uint32_t baud)
Definition: pios_com.h:44