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

Last change on this file since 76c8209 was 5353f50, checked in by Matthieu Riolo <matthieu.riolo@…>, 6 years ago

net: Start network service on boot

  • Create unit files
  • Create network.tgt
  • Enable autostart of directly brokered net services

Conflicts:

boot/Makefile.common
uspace/lib/c/generic/inet.c
uspace/lib/c/generic/inet/tcp.c
uspace/lib/c/generic/inetping.c
uspace/lib/c/include/ipc/services.h
uspace/srv/net/loopip/loopip.c

  • 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_AUTOSTART);
109 if (rc != EOK) {
110 rc = EIO;
111 goto error;
112 }
113
114 tcp->sess = loc_service_connect(tcp_svcid, INTERFACE_TCP,
115 IPC_AUTOSTART);
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
458 rc = async_data_write_start(exch, data, bytes);
459 if (rc != EOK) {
460 async_forget(req);
461 return rc;
462 }
463
464 async_exchange_end(exch);
465
466 if (rc != EOK) {
467 async_forget(req);
468 return rc;
469 }
470
471 async_wait_for(req, &rc);
472 return rc;
473}
474
475/** Send FIN.
476 *
477 * Send FIN, indicating no more data will be send over the connection.
478 *
479 * @param conn Connection
480 * @return EOK on success or an error code
481 */
482errno_t tcp_conn_send_fin(tcp_conn_t *conn)
483{
484 async_exch_t *exch;
485
486 exch = async_exchange_begin(conn->tcp->sess);
487 errno_t rc = async_req_1_0(exch, TCP_CONN_SEND_FIN, conn->id);
488 async_exchange_end(exch);
489
490 return rc;
491}
492
493/** Push connection.
494 *
495 * @param conn Connection
496 * @return EOK on success or an error code
497 */
498errno_t tcp_conn_push(tcp_conn_t *conn)
499{
500 async_exch_t *exch;
501
502 exch = async_exchange_begin(conn->tcp->sess);
503 errno_t rc = async_req_1_0(exch, TCP_CONN_PUSH, conn->id);
504 async_exchange_end(exch);
505
506 return rc;
507}
508
509/** Reset connection.
510 *
511 * @param conn Connection
512 * @return EOK on success or an error code
513 */
514errno_t tcp_conn_reset(tcp_conn_t *conn)
515{
516 async_exch_t *exch;
517
518 exch = async_exchange_begin(conn->tcp->sess);
519 errno_t rc = async_req_1_0(exch, TCP_CONN_RESET, conn->id);
520 async_exchange_end(exch);
521
522 return rc;
523}
524
525/** Read received data from connection without blocking.
526 *
527 * If any received data is pending on the connection, up to @a bsize bytes
528 * are copied to @a buf and the acutal number is stored in @a *nrecv.
529 * The entire buffer of @a bsize bytes is filled except when less data
530 * is currently available or FIN is received. EOK is returned.
531 *
532 * If no received data is pending, returns EAGAIN.
533 *
534 * @param conn Connection
535 * @param buf Buffer
536 * @param bsize Buffer size
537 * @param nrecv Place to store actual number of received bytes
538 *
539 * @return EOK on success, EAGAIN if no received data is pending, or other
540 * error code in case of other error
541 */
542errno_t tcp_conn_recv(tcp_conn_t *conn, void *buf, size_t bsize, size_t *nrecv)
543{
544 async_exch_t *exch;
545 ipc_call_t answer;
546
547 fibril_mutex_lock(&conn->lock);
548 if (!conn->data_avail) {
549 fibril_mutex_unlock(&conn->lock);
550 return EAGAIN;
551 }
552
553 exch = async_exchange_begin(conn->tcp->sess);
554 aid_t req = async_send_1(exch, TCP_CONN_RECV, conn->id, &answer);
555 errno_t rc = async_data_read_start(exch, buf, bsize);
556 async_exchange_end(exch);
557
558 if (rc != EOK) {
559 async_forget(req);
560 fibril_mutex_unlock(&conn->lock);
561 return rc;
562 }
563
564 errno_t retval;
565 async_wait_for(req, &retval);
566 if (retval != EOK) {
567 fibril_mutex_unlock(&conn->lock);
568 return retval;
569 }
570
571 *nrecv = ipc_get_arg1(&answer);
572 fibril_mutex_unlock(&conn->lock);
573 return EOK;
574}
575
576/** Read received data from connection with blocking.
577 *
578 * Wait for @a bsize bytes of data to be received and copy them to
579 * @a buf. Less data may be returned if FIN is received on the connection.
580 * The actual If any received data is written to @a *nrecv and EOK
581 * is returned on success.
582 *
583 * @param conn Connection
584 * @param buf Buffer
585 * @param bsize Buffer size
586 * @param nrecv Place to store actual number of received bytes
587 *
588 * @return EOK on success or an error code
589 */
590errno_t tcp_conn_recv_wait(tcp_conn_t *conn, void *buf, size_t bsize,
591 size_t *nrecv)
592{
593 async_exch_t *exch;
594 ipc_call_t answer;
595
596again:
597 fibril_mutex_lock(&conn->lock);
598 while (!conn->data_avail) {
599 fibril_condvar_wait(&conn->cv, &conn->lock);
600 }
601
602 exch = async_exchange_begin(conn->tcp->sess);
603 aid_t req = async_send_1(exch, TCP_CONN_RECV_WAIT, conn->id, &answer);
604 errno_t rc = async_data_read_start(exch, buf, bsize);
605 async_exchange_end(exch);
606
607 if (rc != EOK) {
608 async_forget(req);
609 if (rc == EAGAIN) {
610 conn->data_avail = false;
611 fibril_mutex_unlock(&conn->lock);
612 goto again;
613 }
614 fibril_mutex_unlock(&conn->lock);
615 return rc;
616 }
617
618 errno_t retval;
619 async_wait_for(req, &retval);
620 if (retval != EOK) {
621 if (rc == EAGAIN) {
622 conn->data_avail = false;
623 }
624 fibril_mutex_unlock(&conn->lock);
625 return retval;
626 }
627
628 *nrecv = ipc_get_arg1(&answer);
629 fibril_mutex_unlock(&conn->lock);
630 return EOK;
631}
632
633/** Connection established event.
634 *
635 * @param tcp TCP client
636 * @param icall Call data
637 *
638 */
639static void tcp_ev_connected(tcp_t *tcp, ipc_call_t *icall)
640{
641 tcp_conn_t *conn;
642 sysarg_t conn_id;
643 errno_t rc;
644
645 conn_id = ipc_get_arg1(icall);
646
647 rc = tcp_conn_get(tcp, conn_id, &conn);
648 if (rc != EOK) {
649 async_answer_0(icall, ENOENT);
650 return;
651 }
652
653 fibril_mutex_lock(&conn->lock);
654 conn->connected = true;
655 fibril_condvar_broadcast(&conn->cv);
656 fibril_mutex_unlock(&conn->lock);
657
658 async_answer_0(icall, EOK);
659}
660
661/** Connection failed event.
662 *
663 * @param tcp TCP client
664 * @param icall Call data
665 *
666 */
667static void tcp_ev_conn_failed(tcp_t *tcp, ipc_call_t *icall)
668{
669 tcp_conn_t *conn;
670 sysarg_t conn_id;
671 errno_t rc;
672
673 conn_id = ipc_get_arg1(icall);
674
675 rc = tcp_conn_get(tcp, conn_id, &conn);
676 if (rc != EOK) {
677 async_answer_0(icall, ENOENT);
678 return;
679 }
680
681 fibril_mutex_lock(&conn->lock);
682 conn->conn_failed = true;
683 fibril_condvar_broadcast(&conn->cv);
684 fibril_mutex_unlock(&conn->lock);
685
686 async_answer_0(icall, EOK);
687}
688
689/** Connection reset event.
690 *
691 * @param tcp TCP client
692 * @param icall Call data
693 *
694 */
695static void tcp_ev_conn_reset(tcp_t *tcp, ipc_call_t *icall)
696{
697 tcp_conn_t *conn;
698 sysarg_t conn_id;
699 errno_t rc;
700
701 conn_id = ipc_get_arg1(icall);
702
703 rc = tcp_conn_get(tcp, conn_id, &conn);
704 if (rc != EOK) {
705 async_answer_0(icall, ENOENT);
706 return;
707 }
708
709 fibril_mutex_lock(&conn->lock);
710 conn->conn_reset = true;
711 fibril_condvar_broadcast(&conn->cv);
712 fibril_mutex_unlock(&conn->lock);
713
714 async_answer_0(icall, EOK);
715}
716
717/** Data available event.
718 *
719 * @param tcp TCP client
720 * @param icall Call data
721 *
722 */
723static void tcp_ev_data(tcp_t *tcp, ipc_call_t *icall)
724{
725 tcp_conn_t *conn;
726 sysarg_t conn_id;
727 errno_t rc;
728
729 conn_id = ipc_get_arg1(icall);
730
731 rc = tcp_conn_get(tcp, conn_id, &conn);
732 if (rc != EOK) {
733 async_answer_0(icall, ENOENT);
734 return;
735 }
736
737 conn->data_avail = true;
738 fibril_condvar_broadcast(&conn->cv);
739
740 if (conn->cb != NULL && conn->cb->data_avail != NULL)
741 conn->cb->data_avail(conn);
742
743 async_answer_0(icall, EOK);
744}
745
746/** Urgent data event.
747 *
748 * @param tcp TCP client
749 * @param icall Call data
750 *
751 */
752static void tcp_ev_urg_data(tcp_t *tcp, ipc_call_t *icall)
753{
754 async_answer_0(icall, ENOTSUP);
755}
756
757/** New connection event.
758 *
759 * @param tcp TCP client
760 * @param icall Call data
761 *
762 */
763static void tcp_ev_new_conn(tcp_t *tcp, ipc_call_t *icall)
764{
765 tcp_listener_t *lst;
766 tcp_conn_t *conn;
767 sysarg_t lst_id;
768 sysarg_t conn_id;
769 fid_t fid;
770 tcp_in_conn_t *cinfo;
771 errno_t rc;
772
773 lst_id = ipc_get_arg1(icall);
774 conn_id = ipc_get_arg2(icall);
775
776 rc = tcp_listener_get(tcp, lst_id, &lst);
777 if (rc != EOK) {
778 async_answer_0(icall, ENOENT);
779 return;
780 }
781
782 rc = tcp_conn_new(tcp, conn_id, lst->cb, lst->cb_arg, &conn);
783 if (rc != EOK) {
784 async_answer_0(icall, ENOMEM);
785 return;
786 }
787
788 if (lst->lcb != NULL && lst->lcb->new_conn != NULL) {
789 cinfo = calloc(1, sizeof(tcp_in_conn_t));
790 if (cinfo == NULL) {
791 async_answer_0(icall, ENOMEM);
792 return;
793 }
794
795 cinfo->lst = lst;
796 cinfo->conn = conn;
797
798 fid = fibril_create(tcp_conn_fibril, cinfo);
799 if (fid == 0) {
800 async_answer_0(icall, ENOMEM);
801 }
802
803 fibril_add_ready(fid);
804 }
805
806 async_answer_0(icall, EOK);
807}
808
809/** Callback connection handler.
810 *
811 * @param icall Connect call data
812 * @param arg Argument, TCP client
813 *
814 */
815static void tcp_cb_conn(ipc_call_t *icall, void *arg)
816{
817 tcp_t *tcp = (tcp_t *)arg;
818
819 while (true) {
820 ipc_call_t call;
821 async_get_call(&call);
822
823 if (!ipc_get_imethod(&call)) {
824 /* Hangup*/
825 async_answer_0(&call, EOK);
826 goto out;
827 }
828
829 switch (ipc_get_imethod(&call)) {
830 case TCP_EV_CONNECTED:
831 tcp_ev_connected(tcp, &call);
832 break;
833 case TCP_EV_CONN_FAILED:
834 tcp_ev_conn_failed(tcp, &call);
835 break;
836 case TCP_EV_CONN_RESET:
837 tcp_ev_conn_reset(tcp, &call);
838 break;
839 case TCP_EV_DATA:
840 tcp_ev_data(tcp, &call);
841 break;
842 case TCP_EV_URG_DATA:
843 tcp_ev_urg_data(tcp, &call);
844 break;
845 case TCP_EV_NEW_CONN:
846 tcp_ev_new_conn(tcp, &call);
847 break;
848 default:
849 async_answer_0(&call, ENOTSUP);
850 break;
851 }
852 }
853
854out:
855 fibril_mutex_lock(&tcp->lock);
856 tcp->cb_done = true;
857 fibril_mutex_unlock(&tcp->lock);
858 fibril_condvar_broadcast(&tcp->cv);
859}
860
861/** Fibril for handling incoming TCP connection in background.
862 *
863 * @param arg Argument, incoming connection information (@c tcp_in_conn_t)
864 */
865static errno_t tcp_conn_fibril(void *arg)
866{
867 tcp_in_conn_t *cinfo = (tcp_in_conn_t *)arg;
868
869 cinfo->lst->lcb->new_conn(cinfo->lst, cinfo->conn);
870 tcp_conn_destroy(cinfo->conn);
871
872 return EOK;
873}
874
875/** @}
876 */
Note: See TracBrowser for help on using the repository browser.