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

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

Add missing docblocks in network code.

  • Property mode set to 100644
File size: 19.7 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 <fibril.h>
37#include <inet/endpoint.h>
38#include <inet/tcp.h>
39#include <ipc/services.h>
40#include <ipc/tcp.h>
41#include <stdlib.h>
42
43static void tcp_cb_conn(ipc_callid_t, ipc_call_t *, void *);
44static int tcp_conn_fibril(void *);
45
46/** Incoming TCP connection info
47 *
48 * Used to pass information about incoming TCP connection to the connection
49 * fibril
50 */
51typedef struct {
52 /** Listener who received the connection */
53 tcp_listener_t *lst;
54 /** Incoming connection */
55 tcp_conn_t *conn;
56} tcp_in_conn_t;
57
58/** Create callback connection from TCP service.
59 *
60 * @param tcp TCP service
61 * @return EOK on success or negative error code
62 */
63static int tcp_callback_create(tcp_t *tcp)
64{
65 async_exch_t *exch = async_exchange_begin(tcp->sess);
66
67 aid_t req = async_send_0(exch, TCP_CALLBACK_CREATE, NULL);
68 int rc = async_connect_to_me(exch, 0, 0, 0, tcp_cb_conn, tcp);
69 async_exchange_end(exch);
70
71 if (rc != EOK)
72 return rc;
73
74 sysarg_t retval;
75 async_wait_for(req, &retval);
76
77 return retval;
78}
79
80/** Create TCP client instance.
81 *
82 * @param rtcp Place to store pointer to new TCP client
83 * @return EOK on success, ENOMEM if out of memory, EIO if service
84 * cannot be contacted
85 */
86int tcp_create(tcp_t **rtcp)
87{
88 tcp_t *tcp;
89 service_id_t tcp_svcid;
90 int rc;
91
92 tcp = calloc(1, sizeof(tcp_t));
93 if (tcp == NULL) {
94 rc = ENOMEM;
95 goto error;
96 }
97
98 list_initialize(&tcp->conn);
99 list_initialize(&tcp->listener);
100 fibril_mutex_initialize(&tcp->lock);
101 fibril_condvar_initialize(&tcp->cv);
102
103 rc = loc_service_get_id(SERVICE_NAME_TCP, &tcp_svcid,
104 IPC_FLAG_BLOCKING);
105 if (rc != EOK) {
106 rc = EIO;
107 goto error;
108 }
109
110 tcp->sess = loc_service_connect(EXCHANGE_SERIALIZE, tcp_svcid,
111 IPC_FLAG_BLOCKING);
112 if (tcp->sess == NULL) {
113 rc = EIO;
114 goto error;
115 }
116
117 rc = tcp_callback_create(tcp);
118 if (rc != EOK) {
119 rc = EIO;
120 goto error;
121 }
122
123 *rtcp = tcp;
124 return EOK;
125error:
126 free(tcp);
127 return rc;
128}
129
130/** Destroy TCP client instance.
131 *
132 * @param tcp TCP client
133 */
134void tcp_destroy(tcp_t *tcp)
135{
136 if (tcp == NULL)
137 return;
138
139 async_hangup(tcp->sess);
140
141 fibril_mutex_lock(&tcp->lock);
142 while (!tcp->cb_done)
143 fibril_condvar_wait(&tcp->cv, &tcp->lock);
144 fibril_mutex_unlock(&tcp->lock);
145
146 free(tcp);
147}
148
149/** Create new TCP connection
150 *
151 * @param tcp TCP client instance
152 * @param id Connection ID
153 * @param cb Callbacks
154 * @param arg Callback argument
155 * @param rconn Place to store pointer to new connection
156 *
157 * @return EOK on success, ENOMEM if out of memory
158 */
159static int tcp_conn_new(tcp_t *tcp, sysarg_t id, tcp_cb_t *cb, void *arg,
160 tcp_conn_t **rconn)
161{
162 tcp_conn_t *conn;
163
164 conn = calloc(1, sizeof(tcp_conn_t));
165 if (conn == NULL)
166 return ENOMEM;
167
168 conn->data_avail = false;
169 fibril_mutex_initialize(&conn->lock);
170 fibril_condvar_initialize(&conn->cv);
171
172 conn->tcp = tcp;
173 conn->id = id;
174 conn->cb = cb;
175 conn->cb_arg = arg;
176
177 list_append(&conn->ltcp, &tcp->conn);
178 *rconn = conn;
179
180 return EOK;
181}
182
183/** Create new TCP connection.
184 *
185 * Open a connection to the specified destination. This function returns
186 * even before the connection is established (or not). When the connection
187 * is established, @a cb->connected is called. If the connection fails,
188 * @a cb->conn_failed is called. Alternatively, the caller can call
189 * @c tcp_conn_wait_connected() to wait for connection to complete or fail.
190 * Other callbacks are available to monitor the changes in connection state.
191 *
192 * @a epp must specify the remote address and port. Both local address and
193 * port are optional. If local address is not specified, address selection
194 * will take place. If local port number is not specified, a suitable
195 * free dynamic port number will be allocated.
196 *
197 * @param tcp TCP client
198 * @param epp Internet endpoint pair
199 * @param cb Callbacks
200 * @param arg Argument to callbacks
201 * @param rconn Place to store pointer to new connection
202 *
203 * @return EOK on success or negative error code.
204 */
205int tcp_conn_create(tcp_t *tcp, inet_ep2_t *epp, tcp_cb_t *cb, void *arg,
206 tcp_conn_t **rconn)
207{
208 async_exch_t *exch;
209 ipc_call_t answer;
210 sysarg_t conn_id;
211
212 exch = async_exchange_begin(tcp->sess);
213 aid_t req = async_send_0(exch, TCP_CONN_CREATE, &answer);
214 sysarg_t rc = async_data_write_start(exch, (void *)epp,
215 sizeof(inet_ep2_t));
216 async_exchange_end(exch);
217
218 if (rc != EOK) {
219 sysarg_t rc_orig;
220 async_wait_for(req, &rc_orig);
221 if (rc_orig != EOK)
222 rc = rc_orig;
223 goto error;
224 }
225
226 async_wait_for(req, &rc);
227 if (rc != EOK)
228 goto error;
229
230 conn_id = IPC_GET_ARG1(answer);
231
232 rc = tcp_conn_new(tcp, conn_id, cb, arg, rconn);
233 if (rc != EOK)
234 return rc;
235
236 return EOK;
237error:
238 return (int) rc;
239}
240
241/** Destroy TCP connection.
242 *
243 * Destroy TCP connection. The caller should destroy all connections
244 * he created before destroying the TCP client and before terminating.
245 *
246 * @param conn TCP connection
247 */
248void tcp_conn_destroy(tcp_conn_t *conn)
249{
250 async_exch_t *exch;
251
252 if (conn == NULL)
253 return;
254
255 list_remove(&conn->ltcp);
256
257 exch = async_exchange_begin(conn->tcp->sess);
258 sysarg_t rc = async_req_1_0(exch, TCP_CONN_DESTROY, conn->id);
259 async_exchange_end(exch);
260
261 free(conn);
262 (void) rc;
263}
264
265/** Get connection based on its ID.
266 *
267 * @param tcp TCP client
268 * @param id Connection ID
269 * @param rconn Place to store pointer to connection
270 *
271 * @return EOK on success, EINVAL if no connection with the given ID exists
272 */
273static int tcp_conn_get(tcp_t *tcp, sysarg_t id, tcp_conn_t **rconn)
274{
275 list_foreach(tcp->conn, ltcp, tcp_conn_t, conn) {
276 if (conn->id == id) {
277 *rconn = conn;
278 return EOK;
279 }
280 }
281
282 return EINVAL;
283}
284
285/** Get the user/callback argument for a connection.
286 *
287 * @param conn TCP connection
288 * @return User argument associated with connection
289 */
290void *tcp_conn_userptr(tcp_conn_t *conn)
291{
292 return conn->cb_arg;
293}
294
295/** Create a TCP connection listener.
296 *
297 * A listener listens for connections on the set of endpoints specified
298 * by @a ep. Each time a new incoming connection is established,
299 * @a lcb->new_conn is called (and passed @a larg). Also, the new connection
300 * will have callbacks set to @a cb and argument to @a arg.
301 *
302 * @a ep must specify a valid port number. @a ep may specify an address
303 * or link to listen on. If it does not, the listener will listen on
304 * all links/addresses.
305 *
306 * @param tcp TCP client
307 * @param ep Internet endpoint
308 * @param lcb Listener callbacks
309 * @param larg Listener callback argument
310 * @param cb Connection callbacks for every new connection
311 * @param arg Connection argument for every new connection
312 * @param rlst Place to store pointer to new listener
313 *
314 * @return EOK on success or negative error code
315 */
316int tcp_listener_create(tcp_t *tcp, inet_ep_t *ep, tcp_listen_cb_t *lcb,
317 void *larg, tcp_cb_t *cb, void *arg, tcp_listener_t **rlst)
318{
319 async_exch_t *exch;
320 tcp_listener_t *lst;
321 ipc_call_t answer;
322
323 lst = calloc(1, sizeof(tcp_listener_t));
324 if (lst == NULL)
325 return ENOMEM;
326
327 exch = async_exchange_begin(tcp->sess);
328 aid_t req = async_send_0(exch, TCP_LISTENER_CREATE, &answer);
329 sysarg_t rc = async_data_write_start(exch, (void *)ep,
330 sizeof(inet_ep_t));
331 async_exchange_end(exch);
332
333 if (rc != EOK) {
334 sysarg_t rc_orig;
335 async_wait_for(req, &rc_orig);
336 if (rc_orig != EOK)
337 rc = rc_orig;
338 goto error;
339 }
340
341 async_wait_for(req, &rc);
342 if (rc != EOK)
343 goto error;
344
345 lst->tcp = tcp;
346 lst->id = IPC_GET_ARG1(answer);
347 lst->lcb = lcb;
348 lst->lcb_arg = larg;
349 lst->cb = cb;
350 lst->cb_arg = arg;
351
352 list_append(&lst->ltcp, &tcp->listener);
353 *rlst = lst;
354
355 return EOK;
356error:
357 free(lst);
358 return (int) rc;
359}
360
361/** Destroy TCP connection listener.
362 *
363 * @param lst Listener
364 */
365void tcp_listener_destroy(tcp_listener_t *lst)
366{
367 async_exch_t *exch;
368
369 if (lst == NULL)
370 return;
371
372 list_remove(&lst->ltcp);
373
374 exch = async_exchange_begin(lst->tcp->sess);
375 sysarg_t rc = async_req_1_0(exch, TCP_LISTENER_DESTROY, lst->id);
376 async_exchange_end(exch);
377
378 free(lst);
379 (void) rc;
380}
381
382/** Get TCP connection listener based on its ID.
383 *
384 * @param tcp TCP client
385 * @param id Listener ID
386 * @param rlst Place to store pointer to listener
387 *
388 * @return EOK on success, EINVAL if no listener with the given ID is found
389 */
390static int tcp_listener_get(tcp_t *tcp, sysarg_t id, tcp_listener_t **rlst)
391{
392 list_foreach(tcp->listener, ltcp, tcp_listener_t, lst) {
393 if (lst->id == id) {
394 *rlst = lst;
395 return EOK;
396 }
397 }
398
399 return EINVAL;
400}
401
402/** Get callback/user argument associated with listener.
403 *
404 * @param lst Listener
405 * @return Callback/user argument
406 */
407void *tcp_listener_userptr(tcp_listener_t *lst)
408{
409 return lst->lcb_arg;
410}
411
412/** Wait until connection is either established or connection fails.
413 *
414 * Can be called after calling tcp_conn_create() to block until connection
415 * either completes or fails. If the connection fails, EIO is returned.
416 * In this case the connection still exists, but is in a failed
417 * state.
418 *
419 * @param conn Connection
420 * @return EOK if connection is established, EIO otherwise
421 */
422int tcp_conn_wait_connected(tcp_conn_t *conn)
423{
424 fibril_mutex_lock(&conn->lock);
425 while (!conn->connected && !conn->conn_failed && !conn->conn_reset)
426 fibril_condvar_wait(&conn->cv, &conn->lock);
427
428 if (conn->connected) {
429 fibril_mutex_unlock(&conn->lock);
430 return EOK;
431 } else {
432 assert(conn->conn_failed || conn->conn_reset);
433 fibril_mutex_unlock(&conn->lock);
434 return EIO;
435 }
436}
437
438/** Send data over TCP connection.
439 *
440 * @param conn Connection
441 * @param data Data
442 * @param bytes Data size in bytes
443 *
444 * @return EOK on success or negative error code
445 */
446int tcp_conn_send(tcp_conn_t *conn, const void *data, size_t bytes)
447{
448 async_exch_t *exch;
449 sysarg_t rc;
450
451 exch = async_exchange_begin(conn->tcp->sess);
452 aid_t req = async_send_1(exch, TCP_CONN_SEND, conn->id, NULL);
453
454 rc = async_data_write_start(exch, data, bytes);
455 if (rc != EOK) {
456 async_forget(req);
457 return rc;
458 }
459
460 async_exchange_end(exch);
461
462 if (rc != EOK) {
463 async_forget(req);
464 return rc;
465 }
466
467 async_wait_for(req, &rc);
468 return rc;
469}
470
471/** Send FIN.
472 *
473 * Send FIN, indicating no more data will be send over the connection.
474 *
475 * @param conn Connection
476 * @return EOK on success or negative error code
477 */
478int tcp_conn_send_fin(tcp_conn_t *conn)
479{
480 async_exch_t *exch;
481
482 exch = async_exchange_begin(conn->tcp->sess);
483 sysarg_t rc = async_req_1_0(exch, TCP_CONN_SEND_FIN, conn->id);
484 async_exchange_end(exch);
485
486 return rc;
487}
488
489/** Push connection.
490 *
491 * @param conn Connection
492 * @return EOK on success or negative error code
493 */
494int tcp_conn_push(tcp_conn_t *conn)
495{
496 async_exch_t *exch;
497
498 exch = async_exchange_begin(conn->tcp->sess);
499 sysarg_t rc = async_req_1_0(exch, TCP_CONN_PUSH, conn->id);
500 async_exchange_end(exch);
501
502 return rc;
503}
504
505/** Reset connection.
506 *
507 * @param conn Connection
508 * @return EOK on success or negative error code
509 */
510int tcp_conn_reset(tcp_conn_t *conn)
511{
512 async_exch_t *exch;
513
514 exch = async_exchange_begin(conn->tcp->sess);
515 sysarg_t rc = async_req_1_0(exch, TCP_CONN_RESET, conn->id);
516 async_exchange_end(exch);
517
518 return rc;
519}
520
521/** Read received data from connection without blocking.
522 *
523 * If any received data is pending on the connection, up to @a bsize bytes
524 * are copied to @a buf and the acutal number is stored in @a *nrecv.
525 * The entire buffer of @a bsize bytes is filled except when less data
526 * is currently available or FIN is received. EOK is returned.
527 *
528 * If no received data is pending, returns EAGAIN.
529 *
530 * @param conn Connection
531 * @param buf Buffer
532 * @param bsize Buffer size
533 * @param nrecv Place to store actual number of received bytes
534 *
535 * @return EOK on success, EAGAIN if no received data is pending, or other
536 * negative error code in case of other error
537 */
538int tcp_conn_recv(tcp_conn_t *conn, void *buf, size_t bsize, size_t *nrecv)
539{
540 async_exch_t *exch;
541 ipc_call_t answer;
542
543 fibril_mutex_lock(&conn->lock);
544 if (!conn->data_avail) {
545 fibril_mutex_unlock(&conn->lock);
546 return EAGAIN;
547 }
548
549 exch = async_exchange_begin(conn->tcp->sess);
550 aid_t req = async_send_1(exch, TCP_CONN_RECV, conn->id, &answer);
551 int rc = async_data_read_start(exch, buf, bsize);
552 async_exchange_end(exch);
553
554 if (rc != EOK) {
555 async_forget(req);
556 fibril_mutex_unlock(&conn->lock);
557 return rc;
558 }
559
560 sysarg_t retval;
561 async_wait_for(req, &retval);
562 if (retval != EOK) {
563 fibril_mutex_unlock(&conn->lock);
564 return retval;
565 }
566
567 *nrecv = IPC_GET_ARG1(answer);
568 fibril_mutex_unlock(&conn->lock);
569 return EOK;
570}
571
572/** Read received data from connection with blocking.
573 *
574 * Wait for @a bsize bytes of data to be received and copy them to
575 * @a buf. Less data may be returned if FIN is received on the connection.
576 * The actual If any received data is written to @a *nrecv and EOK
577 * is returned on success.
578 *
579 * @param conn Connection
580 * @param buf Buffer
581 * @param bsize Buffer size
582 * @param nrecv Place to store actual number of received bytes
583 *
584 * @return EOK on success or negative error code
585 */
586int tcp_conn_recv_wait(tcp_conn_t *conn, void *buf, size_t bsize,
587 size_t *nrecv)
588{
589 async_exch_t *exch;
590 ipc_call_t answer;
591
592again:
593 fibril_mutex_lock(&conn->lock);
594 while (!conn->data_avail) {
595 fibril_condvar_wait(&conn->cv, &conn->lock);
596 }
597
598 exch = async_exchange_begin(conn->tcp->sess);
599 aid_t req = async_send_1(exch, TCP_CONN_RECV_WAIT, conn->id, &answer);
600 int rc = async_data_read_start(exch, buf, bsize);
601 async_exchange_end(exch);
602
603 if (rc != EOK) {
604 async_forget(req);
605 if (rc == EAGAIN) {
606 conn->data_avail = false;
607 fibril_mutex_unlock(&conn->lock);
608 goto again;
609 }
610 fibril_mutex_unlock(&conn->lock);
611 return rc;
612 }
613
614 sysarg_t retval;
615 async_wait_for(req, &retval);
616 if (retval != EOK) {
617 if (rc == EAGAIN) {
618 conn->data_avail = false;
619 }
620 fibril_mutex_unlock(&conn->lock);
621 return retval;
622 }
623
624 *nrecv = IPC_GET_ARG1(answer);
625 fibril_mutex_unlock(&conn->lock);
626 return EOK;
627}
628
629/** Connection established event.
630 *
631 * @param tcp TCP client
632 * @param iid Call ID
633 * @param icall Call data
634 */
635static void tcp_ev_connected(tcp_t *tcp, ipc_callid_t iid, ipc_call_t *icall)
636{
637 tcp_conn_t *conn;
638 sysarg_t conn_id;
639 int rc;
640
641 conn_id = IPC_GET_ARG1(*icall);
642
643 rc = tcp_conn_get(tcp, conn_id, &conn);
644 if (rc != EOK) {
645 async_answer_0(iid, ENOENT);
646 return;
647 }
648
649 fibril_mutex_lock(&conn->lock);
650 conn->connected = true;
651 fibril_condvar_broadcast(&conn->cv);
652 fibril_mutex_unlock(&conn->lock);
653
654 async_answer_0(iid, EOK);
655}
656
657/** Connection failed event.
658 *
659 * @param tcp TCP client
660 * @param iid Call ID
661 * @param icall Call data
662 */
663static void tcp_ev_conn_failed(tcp_t *tcp, ipc_callid_t iid, ipc_call_t *icall)
664{
665 tcp_conn_t *conn;
666 sysarg_t conn_id;
667 int rc;
668
669 conn_id = IPC_GET_ARG1(*icall);
670
671 rc = tcp_conn_get(tcp, conn_id, &conn);
672 if (rc != EOK) {
673 async_answer_0(iid, ENOENT);
674 return;
675 }
676
677 fibril_mutex_lock(&conn->lock);
678 conn->conn_failed = true;
679 fibril_condvar_broadcast(&conn->cv);
680 fibril_mutex_unlock(&conn->lock);
681
682 async_answer_0(iid, EOK);
683}
684
685/** Connection reset event.
686 *
687 * @param tcp TCP client
688 * @param iid Call ID
689 * @param icall Call data
690 */
691static void tcp_ev_conn_reset(tcp_t *tcp, ipc_callid_t iid, ipc_call_t *icall)
692{
693 tcp_conn_t *conn;
694 sysarg_t conn_id;
695 int rc;
696
697 conn_id = IPC_GET_ARG1(*icall);
698
699 rc = tcp_conn_get(tcp, conn_id, &conn);
700 if (rc != EOK) {
701 async_answer_0(iid, ENOENT);
702 return;
703 }
704
705 fibril_mutex_lock(&conn->lock);
706 conn->conn_reset = true;
707 fibril_condvar_broadcast(&conn->cv);
708 fibril_mutex_unlock(&conn->lock);
709
710 async_answer_0(iid, EOK);
711}
712
713/** Data available event.
714 *
715 * @param tcp TCP client
716 * @param iid Call ID
717 * @param icall Call data
718 */
719static void tcp_ev_data(tcp_t *tcp, ipc_callid_t iid, ipc_call_t *icall)
720{
721 tcp_conn_t *conn;
722 sysarg_t conn_id;
723 int rc;
724
725 conn_id = IPC_GET_ARG1(*icall);
726
727 rc = tcp_conn_get(tcp, conn_id, &conn);
728 if (rc != EOK) {
729 async_answer_0(iid, ENOENT);
730 return;
731 }
732
733 conn->data_avail = true;
734 fibril_condvar_broadcast(&conn->cv);
735
736 if (conn->cb != NULL && conn->cb->data_avail != NULL)
737 conn->cb->data_avail(conn);
738
739 async_answer_0(iid, EOK);
740}
741
742/** Urgent data event.
743 *
744 * @param tcp TCP client
745 * @param iid Call ID
746 * @param icall Call data
747 */
748static void tcp_ev_urg_data(tcp_t *tcp, ipc_callid_t iid, ipc_call_t *icall)
749{
750 async_answer_0(iid, ENOTSUP);
751}
752
753/** New connection event.
754 *
755 * @param tcp TCP client
756 * @param iid Call ID
757 * @param icall Call data
758 */
759static void tcp_ev_new_conn(tcp_t *tcp, ipc_callid_t iid, ipc_call_t *icall)
760{
761 tcp_listener_t *lst;
762 tcp_conn_t *conn;
763 sysarg_t lst_id;
764 sysarg_t conn_id;
765 fid_t fid;
766 tcp_in_conn_t *cinfo;
767 int rc;
768
769 lst_id = IPC_GET_ARG1(*icall);
770 conn_id = IPC_GET_ARG2(*icall);
771
772 rc = tcp_listener_get(tcp, lst_id, &lst);
773 if (rc != EOK) {
774 async_answer_0(iid, ENOENT);
775 return;
776 }
777
778 rc = tcp_conn_new(tcp, conn_id, lst->cb, lst->cb_arg, &conn);
779 if (rc != EOK) {
780 async_answer_0(iid, ENOMEM);
781 return;
782 }
783
784 if (lst->lcb != NULL && lst->lcb->new_conn != NULL) {
785 cinfo = calloc(1, sizeof(tcp_in_conn_t));
786 if (cinfo == NULL) {
787 async_answer_0(iid, ENOMEM);
788 return;
789 }
790
791 cinfo->lst = lst;
792 cinfo->conn = conn;
793
794 fid = fibril_create(tcp_conn_fibril, cinfo);
795 if (fid == 0) {
796 async_answer_0(iid, ENOMEM);
797 }
798
799 fibril_add_ready(fid);
800 }
801
802 async_answer_0(iid, EOK);
803}
804
805/** Callback connection handler.
806 *
807 * @param iid Connect call ID
808 * @param icall Connect call data
809 * @param arg Argument, TCP client
810 */
811static void tcp_cb_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg)
812{
813 tcp_t *tcp = (tcp_t *)arg;
814
815 async_answer_0(iid, EOK);
816
817 while (true) {
818 ipc_call_t call;
819 ipc_callid_t callid = async_get_call(&call);
820
821 if (!IPC_GET_IMETHOD(call)) {
822 /* Hangup*/
823 goto out;
824 }
825
826 switch (IPC_GET_IMETHOD(call)) {
827 case TCP_EV_CONNECTED:
828 tcp_ev_connected(tcp, callid, &call);
829 break;
830 case TCP_EV_CONN_FAILED:
831 tcp_ev_conn_failed(tcp, callid, &call);
832 break;
833 case TCP_EV_CONN_RESET:
834 tcp_ev_conn_reset(tcp, callid, &call);
835 break;
836 case TCP_EV_DATA:
837 tcp_ev_data(tcp, callid, &call);
838 break;
839 case TCP_EV_URG_DATA:
840 tcp_ev_urg_data(tcp, callid, &call);
841 break;
842 case TCP_EV_NEW_CONN:
843 tcp_ev_new_conn(tcp, callid, &call);
844 break;
845 default:
846 async_answer_0(callid, ENOTSUP);
847 break;
848 }
849 }
850out:
851 fibril_mutex_lock(&tcp->lock);
852 tcp->cb_done = true;
853 fibril_mutex_unlock(&tcp->lock);
854 fibril_condvar_broadcast(&tcp->cv);
855}
856
857/** Fibril for handling incoming TCP connection in background.
858 *
859 * @param arg Argument, incoming connection information (@c tcp_in_conn_t)
860 */
861static int tcp_conn_fibril(void *arg)
862{
863 tcp_in_conn_t *cinfo = (tcp_in_conn_t *)arg;
864
865 cinfo->lst->lcb->new_conn(cinfo->lst, cinfo->conn);
866 tcp_conn_destroy(cinfo->conn);
867
868 return EOK;
869}
870
871/** @}
872 */
Note: See TracBrowser for help on using the repository browser.