source: mainline/uspace/lib/c/generic/inet/tcp.c@ 309469de

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

Properly wait for connection establishment.

  • Property mode set to 100644
File size: 12.4 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 fibril_mutex_lock(&conn->lock);
277 while (!conn->connected && !conn->conn_failed && !conn->conn_reset)
278 fibril_condvar_wait(&conn->cv, &conn->lock);
279
280 if (conn->connected) {
281 fibril_mutex_unlock(&conn->lock);
282 return EOK;
283 } else {
284 assert(conn->conn_failed || conn->conn_reset);
285 fibril_mutex_unlock(&conn->lock);
286 return EIO;
287 }
288}
289
290int tcp_conn_send(tcp_conn_t *conn, const void *data, size_t bytes)
291{
292 async_exch_t *exch;
293 sysarg_t rc;
294
295 printf("tcp_conn_send()\n");
296
297 exch = async_exchange_begin(conn->tcp->sess);
298 aid_t req = async_send_1(exch, TCP_CONN_SEND, conn->id, NULL);
299
300 rc = async_data_write_start(exch, data, bytes);
301 if (rc != EOK) {
302 async_forget(req);
303 return rc;
304 }
305
306 async_exchange_end(exch);
307
308 if (rc != EOK) {
309 async_forget(req);
310 return rc;
311 }
312
313 async_wait_for(req, &rc);
314 return rc;
315}
316
317
318int tcp_conn_send_fin(tcp_conn_t *conn)
319{
320 async_exch_t *exch;
321
322 printf("tcp_conn_send_fin()\n");
323
324 exch = async_exchange_begin(conn->tcp->sess);
325 sysarg_t rc = async_req_1_0(exch, TCP_CONN_SEND_FIN, conn->id);
326 async_exchange_end(exch);
327
328 return rc;
329}
330
331int tcp_conn_push(tcp_conn_t *conn)
332{
333 async_exch_t *exch;
334
335 printf("tcp_conn_push()\n");
336
337 exch = async_exchange_begin(conn->tcp->sess);
338 sysarg_t rc = async_req_1_0(exch, TCP_CONN_PUSH, conn->id);
339 async_exchange_end(exch);
340
341 return rc;
342}
343
344int tcp_conn_reset(tcp_conn_t *conn)
345{
346 async_exch_t *exch;
347
348 printf("tcp_conn_reset()\n");
349
350 exch = async_exchange_begin(conn->tcp->sess);
351 sysarg_t rc = async_req_1_0(exch, TCP_CONN_RESET, conn->id);
352 async_exchange_end(exch);
353
354 return rc;
355}
356
357int tcp_conn_recv(tcp_conn_t *conn, void *buf, size_t bsize, size_t *nrecv)
358{
359 async_exch_t *exch;
360 ipc_call_t answer;
361
362 printf("tcp_conn_recv() bsize=%zu\n", bsize);
363
364 fibril_mutex_lock(&conn->lock);
365 if (!conn->data_avail) {
366 printf("returning EAGAIN\n");
367 fibril_mutex_unlock(&conn->lock);
368 return EAGAIN;
369 }
370
371 exch = async_exchange_begin(conn->tcp->sess);
372 aid_t req = async_send_1(exch, TCP_CONN_RECV, conn->id, &answer);
373 int rc = async_data_read_start(exch, buf, bsize);
374 async_exchange_end(exch);
375
376 if (rc != EOK) {
377 printf("got rc = %d\n", rc);
378 async_forget(req);
379 fibril_mutex_unlock(&conn->lock);
380 return rc;
381 }
382
383 sysarg_t retval;
384 async_wait_for(req, &retval);
385 if (retval != EOK) {
386 printf("got rc = %d\n", rc);
387 fibril_mutex_unlock(&conn->lock);
388 return retval;
389 }
390
391 *nrecv = IPC_GET_ARG1(answer);
392 fibril_mutex_unlock(&conn->lock);
393 return EOK;
394}
395
396int tcp_conn_recv_wait(tcp_conn_t *conn, void *buf, size_t bsize, size_t *nrecv)
397{
398 async_exch_t *exch;
399 ipc_call_t answer;
400
401 printf("tcp_conn_recv_wait() bsize=%zu\n", bsize);
402again:
403 fibril_mutex_lock(&conn->lock);
404 while (!conn->data_avail) {
405 printf("wait for data to be avail\n");
406 fibril_condvar_wait(&conn->cv, &conn->lock);
407 }
408
409 printf("tcp_conn_recv_wait - get data\n");
410 exch = async_exchange_begin(conn->tcp->sess);
411 aid_t req = async_send_1(exch, TCP_CONN_RECV_WAIT, conn->id, &answer);
412 int rc = async_data_read_start(exch, buf, bsize);
413 printf("tcp_conn_recv_wait - rc = %d\n", rc);
414 async_exchange_end(exch);
415
416 if (rc != EOK) {
417 printf("got rc=%d\n", rc);
418 async_forget(req);
419 if (rc == EAGAIN) {
420 conn->data_avail = false;
421 fibril_mutex_unlock(&conn->lock);
422 goto again;
423 }
424 fibril_mutex_unlock(&conn->lock);
425 return rc;
426 }
427
428 sysarg_t retval;
429 async_wait_for(req, &retval);
430 if (retval != EOK) {
431 printf("got retval != EOK\n");
432 if (rc == EAGAIN) {
433 printf("rc == EAGAIN\n");
434 conn->data_avail = false;
435 }
436 fibril_mutex_unlock(&conn->lock);
437 return retval;
438 }
439
440 *nrecv = IPC_GET_ARG1(answer);
441 fibril_mutex_unlock(&conn->lock);
442 printf("tcp_conn_recv_wait: nrecv=%zu\n", *nrecv);
443 printf("received: '");
444 size_t i;
445 for (i = 0; i < *nrecv; i++) {
446 putchar((char)((char *)buf)[i]);
447 }
448 printf("'\n");
449 return EOK;
450}
451
452static void tcp_ev_connected(tcp_t *tcp, ipc_callid_t iid, ipc_call_t *icall)
453{
454 tcp_conn_t *conn;
455 sysarg_t conn_id;
456 int rc;
457
458 printf("tcp_ev_connected()\n");
459 conn_id = IPC_GET_ARG1(*icall);
460
461 rc = tcp_conn_get(tcp, conn_id, &conn);
462 if (rc != EOK) {
463 printf("conn ID %zu not found\n",
464 conn_id);
465 async_answer_0(iid, ENOENT);
466 return;
467 }
468
469 fibril_mutex_lock(&conn->lock);
470 conn->connected = true;
471 fibril_condvar_broadcast(&conn->cv);
472 fibril_mutex_unlock(&conn->lock);
473
474 async_answer_0(iid, EOK);
475}
476
477static void tcp_ev_conn_failed(tcp_t *tcp, ipc_callid_t iid, ipc_call_t *icall)
478{
479 tcp_conn_t *conn;
480 sysarg_t conn_id;
481 int rc;
482
483 printf("tcp_ev_conn_failed()\n");
484 conn_id = IPC_GET_ARG1(*icall);
485
486 rc = tcp_conn_get(tcp, conn_id, &conn);
487 if (rc != EOK) {
488 printf("conn ID %zu not found\n",
489 conn_id);
490 async_answer_0(iid, ENOENT);
491 return;
492 }
493
494 fibril_mutex_lock(&conn->lock);
495 conn->conn_failed = true;
496 fibril_condvar_broadcast(&conn->cv);
497 fibril_mutex_unlock(&conn->lock);
498
499 async_answer_0(iid, EOK);
500}
501
502static void tcp_ev_conn_reset(tcp_t *tcp, ipc_callid_t iid, ipc_call_t *icall)
503{
504 tcp_conn_t *conn;
505 sysarg_t conn_id;
506 int rc;
507
508 printf("tcp_ev_conn_reset()\n");
509 conn_id = IPC_GET_ARG1(*icall);
510
511 rc = tcp_conn_get(tcp, conn_id, &conn);
512 if (rc != EOK) {
513 printf("conn ID %zu not found\n",
514 conn_id);
515 async_answer_0(iid, ENOENT);
516 return;
517 }
518
519 fibril_mutex_lock(&conn->lock);
520 conn->conn_reset = true;
521 fibril_condvar_broadcast(&conn->cv);
522 fibril_mutex_unlock(&conn->lock);
523
524 async_answer_0(iid, EOK);
525}
526
527static void tcp_ev_data(tcp_t *tcp, ipc_callid_t iid, ipc_call_t *icall)
528{
529 tcp_conn_t *conn;
530 sysarg_t conn_id;
531 int rc;
532
533 printf("tcp_ev_data()\n");
534 conn_id = IPC_GET_ARG1(*icall);
535
536 rc = tcp_conn_get(tcp, conn_id, &conn);
537 if (rc != EOK) {
538 printf("conn ID %zu not found\n",
539 conn_id);
540 async_answer_0(iid, ENOENT);
541 return;
542 }
543
544 conn->data_avail = true;
545 fibril_condvar_broadcast(&conn->cv);
546
547 async_answer_0(iid, EOK);
548}
549
550static void tcp_ev_urg_data(tcp_t *tcp, ipc_callid_t iid, ipc_call_t *icall)
551{
552 printf("tcp_ev_urg_data()\n");
553 async_answer_0(iid, ENOTSUP);
554}
555
556static void tcp_cb_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg)
557{
558 tcp_t *tcp = (tcp_t *)arg;
559
560 async_answer_0(iid, EOK);
561
562 printf("tcp_cb_conn()\n");
563
564 while (true) {
565 ipc_call_t call;
566 ipc_callid_t callid = async_get_call(&call);
567
568 printf("tcp_cb_conn() - msg %d\n",
569 (int)IPC_GET_IMETHOD(call));
570 if (!IPC_GET_IMETHOD(call)) {
571 /* TODO: Handle hangup */
572 return;
573 }
574
575 switch (IPC_GET_IMETHOD(call)) {
576 case TCP_EV_CONNECTED:
577 tcp_ev_connected(tcp, callid, &call);
578 break;
579 case TCP_EV_CONN_FAILED:
580 tcp_ev_conn_failed(tcp, callid, &call);
581 break;
582 case TCP_EV_CONN_RESET:
583 tcp_ev_conn_reset(tcp, callid, &call);
584 break;
585 case TCP_EV_DATA:
586 tcp_ev_data(tcp, callid, &call);
587 break;
588 case TCP_EV_URG_DATA:
589 tcp_ev_urg_data(tcp, callid, &call);
590 break;
591 default:
592 async_answer_0(callid, ENOTSUP);
593 break;
594 }
595 }
596}
597
598
599/** @}
600 */
Note: See TracBrowser for help on using the repository browser.