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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 514d561 was 984a9ba, checked in by Martin Decky <martin@…>, 7 years ago

do not expose the call capability handler from the async framework

Keep the call capability handler encapsulated within the async framework
and do not expose it explicitly via its API. Use the pointer to
ipc_call_t as the sole object identifying an IPC call in the code that
uses the async framework.

This plugs a major leak in the abstraction and also simplifies both the
async framework (slightly) and all IPC servers.

  • 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
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 async_answer_0(icall, EOK);
820
821 while (true) {
822 ipc_call_t call;
823 async_get_call(&call);
824
825 if (!IPC_GET_IMETHOD(call)) {
826 /* Hangup*/
827 goto out;
828 }
829
830 switch (IPC_GET_IMETHOD(call)) {
831 case TCP_EV_CONNECTED:
832 tcp_ev_connected(tcp, &call);
833 break;
834 case TCP_EV_CONN_FAILED:
835 tcp_ev_conn_failed(tcp, &call);
836 break;
837 case TCP_EV_CONN_RESET:
838 tcp_ev_conn_reset(tcp, &call);
839 break;
840 case TCP_EV_DATA:
841 tcp_ev_data(tcp, &call);
842 break;
843 case TCP_EV_URG_DATA:
844 tcp_ev_urg_data(tcp, &call);
845 break;
846 case TCP_EV_NEW_CONN:
847 tcp_ev_new_conn(tcp, &call);
848 break;
849 default:
850 async_answer_0(&call, ENOTSUP);
851 break;
852 }
853 }
854
855out:
856 fibril_mutex_lock(&tcp->lock);
857 tcp->cb_done = true;
858 fibril_mutex_unlock(&tcp->lock);
859 fibril_condvar_broadcast(&tcp->cv);
860}
861
862/** Fibril for handling incoming TCP connection in background.
863 *
864 * @param arg Argument, incoming connection information (@c tcp_in_conn_t)
865 */
866static errno_t tcp_conn_fibril(void *arg)
867{
868 tcp_in_conn_t *cinfo = (tcp_in_conn_t *)arg;
869
870 cinfo->lst->lcb->new_conn(cinfo->lst, cinfo->conn);
871 tcp_conn_destroy(cinfo->conn);
872
873 return EOK;
874}
875
876/** @}
877 */
Note: See TracBrowser for help on using the repository browser.