source: mainline/uspace/lib/c/generic/inet/tcp.c@ 779541b

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 779541b was 779541b, checked in by Jiri Svoboda <jiri@…>, 10 years ago

TCP transport layer API - somewhat working.

  • Property mode set to 100644
File size: 11.0 KB
Line 
1/*
2 * Copyright (c) 2015 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup libc
30 * @{
31 */
32/** @file TCP API
33 */
34
35#include <errno.h>
36#include <inet/endpoint.h>
37#include <inet/tcp.h>
38#include <ipc/services.h>
39#include <ipc/tcp.h>
40#include <stdlib.h>
41
42#include <stdio.h>
43
44static void tcp_cb_conn(ipc_callid_t, ipc_call_t *, void *);
45
46static int tcp_callback_create(tcp_t *tcp)
47{
48 async_exch_t *exch = async_exchange_begin(tcp->sess);
49
50 printf("tcp_callback_create()\n");
51
52 aid_t req = async_send_0(exch, TCP_CALLBACK_CREATE, NULL);
53 int rc = async_connect_to_me(exch, 0, 0, 0, tcp_cb_conn, tcp);
54 async_exchange_end(exch);
55
56 if (rc != EOK)
57 return rc;
58
59 sysarg_t retval;
60 async_wait_for(req, &retval);
61
62 return retval;
63}
64
65int tcp_create(tcp_t **rtcp)
66{
67 tcp_t *tcp;
68 service_id_t tcp_svcid;
69 int rc;
70
71 printf("tcp_create()\n");
72
73 tcp = calloc(1, sizeof(tcp_t));
74 if (tcp == NULL) {
75 rc = ENOMEM;
76 goto error;
77 }
78
79 list_initialize(&tcp->conn);
80 list_initialize(&tcp->listener);
81
82 rc = loc_service_get_id(SERVICE_NAME_TCP, &tcp_svcid,
83 IPC_FLAG_BLOCKING);
84 if (rc != EOK) {
85 rc = EIO;
86 goto error;
87 }
88
89 tcp->sess = loc_service_connect(EXCHANGE_SERIALIZE, tcp_svcid,
90 IPC_FLAG_BLOCKING);
91 if (tcp->sess == NULL) {
92 rc = EIO;
93 goto error;
94 }
95
96 rc = tcp_callback_create(tcp);
97 if (rc != EOK) {
98 rc = EIO;
99 goto error;
100 }
101
102 *rtcp = tcp;
103 return EOK;
104error:
105 free(tcp);
106 return rc;
107}
108
109void tcp_destroy(tcp_t *tcp)
110{
111 if (tcp == NULL)
112 return;
113
114 async_hangup(tcp->sess);
115 free(tcp);
116}
117
118int tcp_conn_create(tcp_t *tcp, inet_ep2_t *epp, tcp_cb_t *cb, void *arg,
119 tcp_conn_t **rconn)
120{
121 async_exch_t *exch;
122 tcp_conn_t *conn;
123 ipc_call_t answer;
124
125 printf("tcp_conn_create()\n");
126
127 conn = calloc(1, sizeof(tcp_conn_t));
128 if (conn == NULL)
129 return ENOMEM;
130
131 conn->data_avail = false;
132 fibril_mutex_initialize(&conn->lock);
133 fibril_condvar_initialize(&conn->cv);
134
135 exch = async_exchange_begin(tcp->sess);
136 aid_t req = async_send_0(exch, TCP_CONN_CREATE, &answer);
137 sysarg_t rc = async_data_write_start(exch, (void *)epp,
138 sizeof(inet_ep2_t));
139 async_exchange_end(exch);
140
141 if (rc != EOK) {
142 sysarg_t rc_orig;
143 async_wait_for(req, &rc_orig);
144 if (rc_orig != EOK)
145 rc = rc_orig;
146 goto error;
147 }
148
149 async_wait_for(req, &rc);
150 if (rc != EOK)
151 goto error;
152
153 conn->tcp = tcp;
154 conn->id = IPC_GET_ARG1(answer);
155 conn->cb = cb;
156 conn->cb_arg = arg;
157
158 list_append(&conn->ltcp, &tcp->conn);
159 *rconn = conn;
160
161 return EOK;
162error:
163 free(conn);
164 return (int) rc;
165}
166
167void tcp_conn_destroy(tcp_conn_t *conn)
168{
169 async_exch_t *exch;
170
171 printf("tcp_conn_destroy()\n");
172
173 if (conn == NULL)
174 return;
175
176 list_remove(&conn->ltcp);
177
178 exch = async_exchange_begin(conn->tcp->sess);
179 sysarg_t rc = async_req_1_0(exch, TCP_CONN_DESTROY, conn->id);
180 async_exchange_end(exch);
181
182 free(conn);
183 (void) rc;
184}
185
186static int tcp_conn_get(tcp_t *tcp, sysarg_t id, tcp_conn_t **rconn)
187{
188 list_foreach(tcp->conn, ltcp, tcp_conn_t, conn) {
189 if (conn->id == id) {
190 *rconn = conn;
191 return EOK;
192 }
193 }
194
195 return EINVAL;
196}
197
198void *tcp_conn_userptr(tcp_conn_t *conn)
199{
200 return conn->cb_arg;
201}
202
203int tcp_listener_create(tcp_t *tcp, inet_ep_t *ep, tcp_listen_cb_t *lcb,
204 void *larg, tcp_cb_t *cb, void *arg, tcp_listener_t **rlst)
205{
206 async_exch_t *exch;
207 tcp_listener_t *lst;
208 ipc_call_t answer;
209
210 printf("tcp_listener_create()\n");
211
212 lst = calloc(1, sizeof(tcp_listener_t));
213 if (lst == NULL)
214 return ENOMEM;
215
216 exch = async_exchange_begin(tcp->sess);
217 aid_t req = async_send_0(exch, TCP_LISTENER_CREATE, &answer);
218 sysarg_t rc = async_data_write_start(exch, (void *)ep,
219 sizeof(inet_ep_t));
220 async_exchange_end(exch);
221
222 if (rc != EOK) {
223 sysarg_t rc_orig;
224 async_wait_for(req, &rc_orig);
225 if (rc_orig != EOK)
226 rc = rc_orig;
227 goto error;
228 }
229
230 async_wait_for(req, &rc);
231 if (rc != EOK)
232 goto error;
233
234 lst->tcp = tcp;
235 lst->id = IPC_GET_ARG1(answer);
236 lst->lcb = lcb;
237 lst->lcb_arg = larg;
238 lst->cb = cb;
239 lst->cb_arg = arg;
240
241 list_append(&lst->ltcp, &tcp->listener);
242 *rlst = lst;
243
244 return EOK;
245error:
246 free(lst);
247 return (int) rc;
248}
249
250void tcp_listener_destroy(tcp_listener_t *lst)
251{
252 async_exch_t *exch;
253
254 printf("tcp_listener_destroy()\n");
255
256 if (lst == NULL)
257 return;
258
259 list_remove(&lst->ltcp);
260
261 exch = async_exchange_begin(lst->tcp->sess);
262 sysarg_t rc = async_req_1_0(exch, TCP_LISTENER_DESTROY, lst->id);
263 async_exchange_end(exch);
264
265 free(lst);
266 (void) rc;
267}
268
269void *tcp_listener_userptr(tcp_listener_t *lst)
270{
271 return lst->lcb_arg;
272}
273
274int tcp_conn_wait_connected(tcp_conn_t *conn)
275{
276 async_usleep(1000 * 1000);
277 return 0;
278}
279
280int tcp_conn_send(tcp_conn_t *conn, const void *data, size_t bytes)
281{
282 async_exch_t *exch;
283 sysarg_t rc;
284
285 printf("tcp_conn_send()\n");
286
287 exch = async_exchange_begin(conn->tcp->sess);
288 aid_t req = async_send_1(exch, TCP_CONN_SEND, conn->id, NULL);
289
290 rc = async_data_write_start(exch, data, bytes);
291 if (rc != EOK) {
292 async_forget(req);
293 return rc;
294 }
295
296 async_exchange_end(exch);
297
298 if (rc != EOK) {
299 async_forget(req);
300 return rc;
301 }
302
303 async_wait_for(req, &rc);
304 return rc;
305}
306
307
308int tcp_conn_send_fin(tcp_conn_t *conn)
309{
310 async_exch_t *exch;
311
312 printf("tcp_conn_send_fin()\n");
313
314 exch = async_exchange_begin(conn->tcp->sess);
315 sysarg_t rc = async_req_1_0(exch, TCP_CONN_SEND_FIN, conn->id);
316 async_exchange_end(exch);
317
318 return rc;
319}
320
321int tcp_conn_push(tcp_conn_t *conn)
322{
323 async_exch_t *exch;
324
325 printf("tcp_conn_push()\n");
326
327 exch = async_exchange_begin(conn->tcp->sess);
328 sysarg_t rc = async_req_1_0(exch, TCP_CONN_PUSH, conn->id);
329 async_exchange_end(exch);
330
331 return rc;
332}
333
334int tcp_conn_reset(tcp_conn_t *conn)
335{
336 async_exch_t *exch;
337
338 printf("tcp_conn_reset()\n");
339
340 exch = async_exchange_begin(conn->tcp->sess);
341 sysarg_t rc = async_req_1_0(exch, TCP_CONN_RESET, conn->id);
342 async_exchange_end(exch);
343
344 return rc;
345}
346
347int tcp_conn_recv(tcp_conn_t *conn, void *buf, size_t bsize, size_t *nrecv)
348{
349 async_exch_t *exch;
350 ipc_call_t answer;
351
352 printf("tcp_conn_recv() bsize=%zu\n", bsize);
353
354 fibril_mutex_lock(&conn->lock);
355 if (!conn->data_avail) {
356 printf("returning EAGAIN\n");
357 fibril_mutex_unlock(&conn->lock);
358 return EAGAIN;
359 }
360
361 exch = async_exchange_begin(conn->tcp->sess);
362 aid_t req = async_send_1(exch, TCP_CONN_RECV, conn->id, &answer);
363 int rc = async_data_read_start(exch, buf, bsize);
364 async_exchange_end(exch);
365
366 if (rc != EOK) {
367 printf("got rc = %d\n", rc);
368 async_forget(req);
369 fibril_mutex_unlock(&conn->lock);
370 return rc;
371 }
372
373 sysarg_t retval;
374 async_wait_for(req, &retval);
375 if (retval != EOK) {
376 printf("got rc = %d\n", rc);
377 fibril_mutex_unlock(&conn->lock);
378 return retval;
379 }
380
381 *nrecv = IPC_GET_ARG1(answer);
382 fibril_mutex_unlock(&conn->lock);
383 return EOK;
384}
385
386int tcp_conn_recv_wait(tcp_conn_t *conn, void *buf, size_t bsize, size_t *nrecv)
387{
388 async_exch_t *exch;
389 ipc_call_t answer;
390
391 printf("tcp_conn_recv_wait() bsize=%zu\n", bsize);
392again:
393 fibril_mutex_lock(&conn->lock);
394 while (!conn->data_avail) {
395 printf("wait for data to be avail\n");
396 fibril_condvar_wait(&conn->cv, &conn->lock);
397 }
398
399 printf("tcp_conn_recv_wait - get data\n");
400 exch = async_exchange_begin(conn->tcp->sess);
401 aid_t req = async_send_1(exch, TCP_CONN_RECV_WAIT, conn->id, &answer);
402 int rc = async_data_read_start(exch, buf, bsize);
403 printf("tcp_conn_recv_wait - rc = %d\n", rc);
404 async_exchange_end(exch);
405
406 if (rc != EOK) {
407 printf("got rc=%d\n", rc);
408 async_forget(req);
409 if (rc == EAGAIN) {
410 conn->data_avail = false;
411 fibril_mutex_unlock(&conn->lock);
412 goto again;
413 }
414 fibril_mutex_unlock(&conn->lock);
415 return rc;
416 }
417
418 sysarg_t retval;
419 async_wait_for(req, &retval);
420 if (retval != EOK) {
421 printf("got retval != EOK\n");
422 if (rc == EAGAIN) {
423 printf("rc == EAGAIN\n");
424 conn->data_avail = false;
425 }
426 fibril_mutex_unlock(&conn->lock);
427 return retval;
428 }
429
430 *nrecv = IPC_GET_ARG1(answer);
431 fibril_mutex_unlock(&conn->lock);
432 printf("tcp_conn_recv_wait: nrecv=%zu\n", *nrecv);
433 printf("received: '");
434 size_t i;
435 for (i = 0; i < *nrecv; i++) {
436 putchar((char)((char *)buf)[i]);
437 }
438 printf("'\n");
439 return EOK;
440}
441
442static void tcp_ev_connected(tcp_t *tcp, ipc_callid_t iid, ipc_call_t *icall)
443{
444 printf("tcp_ev_connected()\n");
445 async_answer_0(iid, ENOTSUP);
446}
447
448static void tcp_ev_conn_failed(tcp_t *tcp, ipc_callid_t iid, ipc_call_t *icall)
449{
450 printf("tcp_ev_conn_failed()\n");
451 async_answer_0(iid, ENOTSUP);
452}
453
454static void tcp_ev_conn_reset(tcp_t *tcp, ipc_callid_t iid, ipc_call_t *icall)
455{
456 printf("tcp_ev_conn_reset()\n");
457 async_answer_0(iid, ENOTSUP);
458}
459
460static void tcp_ev_data(tcp_t *tcp, ipc_callid_t iid, ipc_call_t *icall)
461{
462 tcp_conn_t *conn;
463 sysarg_t conn_id;
464 int rc;
465
466 printf("tcp_ev_data()\n");
467 conn_id = IPC_GET_ARG1(*icall);
468
469 rc = tcp_conn_get(tcp, conn_id, &conn);
470 if (rc != EOK) {
471 printf("conn ID %zu not found\n",
472 conn_id);
473 async_answer_0(iid, ENOENT);
474 return;
475 }
476
477 conn->data_avail = true;
478 fibril_condvar_broadcast(&conn->cv);
479
480 async_answer_0(iid, EOK);
481}
482
483static void tcp_ev_urg_data(tcp_t *tcp, ipc_callid_t iid, ipc_call_t *icall)
484{
485 printf("tcp_ev_urg_data()\n");
486 async_answer_0(iid, ENOTSUP);
487}
488
489static void tcp_cb_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg)
490{
491 tcp_t *tcp = (tcp_t *)arg;
492
493 async_answer_0(iid, EOK);
494
495 printf("tcp_cb_conn()\n");
496
497 while (true) {
498 ipc_call_t call;
499 ipc_callid_t callid = async_get_call(&call);
500
501 printf("tcp_cb_conn() - msg %d\n",
502 (int)IPC_GET_IMETHOD(call));
503 if (!IPC_GET_IMETHOD(call)) {
504 /* TODO: Handle hangup */
505 return;
506 }
507
508 switch (IPC_GET_IMETHOD(call)) {
509 case TCP_EV_CONNECTED:
510 tcp_ev_connected(tcp, callid, &call);
511 break;
512 case TCP_EV_CONN_FAILED:
513 tcp_ev_conn_failed(tcp, callid, &call);
514 break;
515 case TCP_EV_CONN_RESET:
516 tcp_ev_conn_reset(tcp, callid, &call);
517 break;
518 case TCP_EV_DATA:
519 tcp_ev_data(tcp, callid, &call);
520 break;
521 case TCP_EV_URG_DATA:
522 tcp_ev_urg_data(tcp, callid, &call);
523 break;
524 default:
525 async_answer_0(callid, ENOTSUP);
526 break;
527 }
528 }
529}
530
531
532/** @}
533 */
Note: See TracBrowser for help on using the repository browser.