source: mainline/uspace/lib/inet/src/tcp.c@ ec7902d

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

Move TCP/IP library support out of libc to separate library

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