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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since b752a31 was b752a31, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

Strip trailing whitespace.

  • Property mode set to 100644
File size: 20.2 KB
RevLine 
[ddfcfeb2]1/*
[fab2746]2 * Copyright (c) 2015 Jiri Svoboda
[ddfcfeb2]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
[d9e2e0e]29/** @addtogroup libc
[fab2746]30 * @{
[ddfcfeb2]31 */
[779541b]32/** @file TCP API
[ddfcfeb2]33 */
34
[d9e2e0e]35#include <errno.h>
[b99f6e2]36#include <fibril.h>
[fab2746]37#include <inet/endpoint.h>
38#include <inet/tcp.h>
[779541b]39#include <ipc/services.h>
40#include <ipc/tcp.h>
41#include <stdlib.h>
42
[3be9d10]43static void tcp_cb_conn(cap_call_handle_t, ipc_call_t *, void *);
[b7fd2a0]44static errno_t tcp_conn_fibril(void *);
[b99f6e2]45
[b10460a]46/** Incoming TCP connection info
47 *
48 * Used to pass information about incoming TCP connection to the connection
49 * fibril
50 */
[b99f6e2]51typedef struct {
[b10460a]52 /** Listener who received the connection */
[b99f6e2]53 tcp_listener_t *lst;
[b10460a]54 /** Incoming connection */
[b99f6e2]55 tcp_conn_t *conn;
56} tcp_in_conn_t;
[779541b]57
[b10460a]58/** Create callback connection from TCP service.
59 *
60 * @param tcp TCP service
[cde999a]61 * @return EOK on success or an error code
[b10460a]62 */
[b7fd2a0]63static errno_t tcp_callback_create(tcp_t *tcp)
[779541b]64{
65 async_exch_t *exch = async_exchange_begin(tcp->sess);
66
67 aid_t req = async_send_0(exch, TCP_CALLBACK_CREATE, NULL);
[a35b458]68
[78bb04b]69 port_id_t port;
[b7fd2a0]70 errno_t rc = async_create_callback_port(exch, INTERFACE_TCP_CB, 0, 0,
[78bb04b]71 tcp_cb_conn, tcp, &port);
[a35b458]72
[779541b]73 async_exchange_end(exch);
74
75 if (rc != EOK)
76 return rc;
77
[b7fd2a0]78 errno_t retval;
[779541b]79 async_wait_for(req, &retval);
80
81 return retval;
82}
[fab2746]83
[b10460a]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 */
[b7fd2a0]90errno_t tcp_create(tcp_t **rtcp)
[fab2746]91{
[779541b]92 tcp_t *tcp;
93 service_id_t tcp_svcid;
[b7fd2a0]94 errno_t rc;
[779541b]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);
[1f2b07a]104 fibril_mutex_initialize(&tcp->lock);
105 fibril_condvar_initialize(&tcp->cv);
[779541b]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
[f9b2cb4c]114 tcp->sess = loc_service_connect(tcp_svcid, INTERFACE_TCP,
[779541b]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;
[fab2746]132}
133
[b10460a]134/** Destroy TCP client instance.
135 *
136 * @param tcp TCP client
137 */
[fab2746]138void tcp_destroy(tcp_t *tcp)
139{
[779541b]140 if (tcp == NULL)
141 return;
142
143 async_hangup(tcp->sess);
[1f2b07a]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
[779541b]150 free(tcp);
[fab2746]151}
152
[b10460a]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 */
[b7fd2a0]163static errno_t tcp_conn_new(tcp_t *tcp, sysarg_t id, tcp_cb_t *cb, void *arg,
[fab2746]164 tcp_conn_t **rconn)
165{
[779541b]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
[b99f6e2]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
[b10460a]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 *
[cde999a]207 * @return EOK on success or an error code.
[b10460a]208 */
[b7fd2a0]209errno_t tcp_conn_create(tcp_t *tcp, inet_ep2_t *epp, tcp_cb_t *cb, void *arg,
[b99f6e2]210 tcp_conn_t **rconn)
211{
212 async_exch_t *exch;
213 ipc_call_t answer;
214 sysarg_t conn_id;
215
[779541b]216 exch = async_exchange_begin(tcp->sess);
217 aid_t req = async_send_0(exch, TCP_CONN_CREATE, &answer);
[b7fd2a0]218 errno_t rc = async_data_write_start(exch, (void *)epp,
[779541b]219 sizeof(inet_ep2_t));
220 async_exchange_end(exch);
221
222 if (rc != EOK) {
[b7fd2a0]223 errno_t rc_orig;
[779541b]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
[b99f6e2]234 conn_id = IPC_GET_ARG1(answer);
[779541b]235
[b99f6e2]236 rc = tcp_conn_new(tcp, conn_id, cb, arg, rconn);
237 if (rc != EOK)
238 return rc;
[779541b]239
240 return EOK;
241error:
[b7fd2a0]242 return (errno_t) rc;
[fab2746]243}
244
[b10460a]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 */
[fab2746]252void tcp_conn_destroy(tcp_conn_t *conn)
253{
[779541b]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);
[b7fd2a0]262 errno_t rc = async_req_1_0(exch, TCP_CONN_DESTROY, conn->id);
[779541b]263 async_exchange_end(exch);
264
265 free(conn);
266 (void) rc;
267}
268
[b10460a]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 */
[b7fd2a0]277static errno_t tcp_conn_get(tcp_t *tcp, sysarg_t id, tcp_conn_t **rconn)
[779541b]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;
[fab2746]287}
288
[b10460a]289/** Get the user/callback argument for a connection.
290 *
291 * @param conn TCP connection
292 * @return User argument associated with connection
293 */
[fab2746]294void *tcp_conn_userptr(tcp_conn_t *conn)
295{
[779541b]296 return conn->cb_arg;
[fab2746]297}
298
[b10460a]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 *
[cde999a]318 * @return EOK on success or an error code
[b10460a]319 */
[b7fd2a0]320errno_t tcp_listener_create(tcp_t *tcp, inet_ep_t *ep, tcp_listen_cb_t *lcb,
[fab2746]321 void *larg, tcp_cb_t *cb, void *arg, tcp_listener_t **rlst)
322{
[779541b]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);
[b7fd2a0]333 errno_t rc = async_data_write_start(exch, (void *)ep,
[779541b]334 sizeof(inet_ep_t));
335 async_exchange_end(exch);
336
337 if (rc != EOK) {
[b7fd2a0]338 errno_t rc_orig;
[779541b]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);
[b7fd2a0]362 return (errno_t) rc;
[fab2746]363}
364
[b10460a]365/** Destroy TCP connection listener.
366 *
367 * @param lst Listener
368 */
[fab2746]369void tcp_listener_destroy(tcp_listener_t *lst)
370{
[779541b]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);
[b7fd2a0]379 errno_t rc = async_req_1_0(exch, TCP_LISTENER_DESTROY, lst->id);
[779541b]380 async_exchange_end(exch);
381
382 free(lst);
383 (void) rc;
[fab2746]384}
385
[b10460a]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 */
[b7fd2a0]394static errno_t tcp_listener_get(tcp_t *tcp, sysarg_t id, tcp_listener_t **rlst)
[b99f6e2]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
[b10460a]406/** Get callback/user argument associated with listener.
407 *
408 * @param lst Listener
409 * @return Callback/user argument
410 */
[fab2746]411void *tcp_listener_userptr(tcp_listener_t *lst)
412{
[779541b]413 return lst->lcb_arg;
[fab2746]414}
415
[b10460a]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 */
[b7fd2a0]426errno_t tcp_conn_wait_connected(tcp_conn_t *conn)
[fab2746]427{
[1d4b815]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 }
[fab2746]440}
441
[b10460a]442/** Send data over TCP connection.
443 *
444 * @param conn Connection
445 * @param data Data
446 * @param bytes Data size in bytes
447 *
[cde999a]448 * @return EOK on success or an error code
[b10460a]449 */
[b7fd2a0]450errno_t tcp_conn_send(tcp_conn_t *conn, const void *data, size_t bytes)
[fab2746]451{
[779541b]452 async_exch_t *exch;
[b7fd2a0]453 errno_t rc;
[779541b]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;
[fab2746]473}
474
[b10460a]475/** Send FIN.
476 *
477 * Send FIN, indicating no more data will be send over the connection.
478 *
479 * @param conn Connection
[cde999a]480 * @return EOK on success or an error code
[b10460a]481 */
[b7fd2a0]482errno_t tcp_conn_send_fin(tcp_conn_t *conn)
[fab2746]483{
[779541b]484 async_exch_t *exch;
485
486 exch = async_exchange_begin(conn->tcp->sess);
[b7fd2a0]487 errno_t rc = async_req_1_0(exch, TCP_CONN_SEND_FIN, conn->id);
[779541b]488 async_exchange_end(exch);
489
490 return rc;
[fab2746]491}
492
[b10460a]493/** Push connection.
494 *
495 * @param conn Connection
[cde999a]496 * @return EOK on success or an error code
[b10460a]497 */
[b7fd2a0]498errno_t tcp_conn_push(tcp_conn_t *conn)
[fab2746]499{
[779541b]500 async_exch_t *exch;
501
502 exch = async_exchange_begin(conn->tcp->sess);
[b7fd2a0]503 errno_t rc = async_req_1_0(exch, TCP_CONN_PUSH, conn->id);
[779541b]504 async_exchange_end(exch);
505
506 return rc;
[fab2746]507}
508
[b10460a]509/** Reset connection.
510 *
511 * @param conn Connection
[cde999a]512 * @return EOK on success or an error code
[b10460a]513 */
[b7fd2a0]514errno_t tcp_conn_reset(tcp_conn_t *conn)
[fab2746]515{
[779541b]516 async_exch_t *exch;
517
518 exch = async_exchange_begin(conn->tcp->sess);
[b7fd2a0]519 errno_t rc = async_req_1_0(exch, TCP_CONN_RESET, conn->id);
[779541b]520 async_exchange_end(exch);
521
522 return rc;
523}
[fab2746]524
[b10460a]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
[cde999a]540 * error code in case of other error
[b10460a]541 */
[b7fd2a0]542errno_t tcp_conn_recv(tcp_conn_t *conn, void *buf, size_t bsize, size_t *nrecv)
[fab2746]543{
[779541b]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);
[b7fd2a0]555 errno_t rc = async_data_read_start(exch, buf, bsize);
[779541b]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
[b7fd2a0]564 errno_t retval;
[779541b]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;
[fab2746]574}
575
[b10460a]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 *
[cde999a]588 * @return EOK on success or an error code
[b10460a]589 */
[b7fd2a0]590errno_t tcp_conn_recv_wait(tcp_conn_t *conn, void *buf, size_t bsize,
[b10460a]591 size_t *nrecv)
[fab2746]592{
[779541b]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);
[b7fd2a0]604 errno_t rc = async_data_read_start(exch, buf, bsize);
[779541b]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
[b7fd2a0]618 errno_t retval;
[779541b]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
[b10460a]633/** Connection established event.
634 *
[eed4139]635 * @param tcp TCP client
636 * @param icall_handle Call handle
637 * @param icall Call data
[b10460a]638 */
[eed4139]639static void
640tcp_ev_connected(tcp_t *tcp, cap_call_handle_t icall_handle, ipc_call_t *icall)
[779541b]641{
[1d4b815]642 tcp_conn_t *conn;
643 sysarg_t conn_id;
[b7fd2a0]644 errno_t rc;
[1d4b815]645
646 conn_id = IPC_GET_ARG1(*icall);
647
648 rc = tcp_conn_get(tcp, conn_id, &conn);
649 if (rc != EOK) {
[a46e56b]650 async_answer_0(icall_handle, ENOENT);
[1d4b815]651 return;
652 }
653
654 fibril_mutex_lock(&conn->lock);
655 conn->connected = true;
656 fibril_condvar_broadcast(&conn->cv);
657 fibril_mutex_unlock(&conn->lock);
658
[a46e56b]659 async_answer_0(icall_handle, EOK);
[779541b]660}
661
[b10460a]662/** Connection failed event.
663 *
[eed4139]664 * @param tcp TCP client
665 * @param icall_handle Call handle
666 * @param icall Call data
[b10460a]667 */
[eed4139]668static void
669tcp_ev_conn_failed(tcp_t *tcp, cap_call_handle_t icall_handle,
670 ipc_call_t *icall)
[779541b]671{
[1d4b815]672 tcp_conn_t *conn;
673 sysarg_t conn_id;
[b7fd2a0]674 errno_t rc;
[1d4b815]675
676 conn_id = IPC_GET_ARG1(*icall);
677
678 rc = tcp_conn_get(tcp, conn_id, &conn);
679 if (rc != EOK) {
[a46e56b]680 async_answer_0(icall_handle, ENOENT);
[1d4b815]681 return;
682 }
683
684 fibril_mutex_lock(&conn->lock);
685 conn->conn_failed = true;
686 fibril_condvar_broadcast(&conn->cv);
687 fibril_mutex_unlock(&conn->lock);
688
[a46e56b]689 async_answer_0(icall_handle, EOK);
[779541b]690}
691
[b10460a]692/** Connection reset event.
693 *
[eed4139]694 * @param tcp TCP client
695 * @param icall_handle Call handle
696 * @param icall Call data
[b10460a]697 */
[a46e56b]698static void tcp_ev_conn_reset(tcp_t *tcp, cap_call_handle_t icall_handle, ipc_call_t *icall)
[779541b]699{
[1d4b815]700 tcp_conn_t *conn;
701 sysarg_t conn_id;
[b7fd2a0]702 errno_t rc;
[1d4b815]703
704 conn_id = IPC_GET_ARG1(*icall);
705
706 rc = tcp_conn_get(tcp, conn_id, &conn);
707 if (rc != EOK) {
[a46e56b]708 async_answer_0(icall_handle, ENOENT);
[1d4b815]709 return;
710 }
711
712 fibril_mutex_lock(&conn->lock);
713 conn->conn_reset = true;
714 fibril_condvar_broadcast(&conn->cv);
715 fibril_mutex_unlock(&conn->lock);
716
[a46e56b]717 async_answer_0(icall_handle, EOK);
[779541b]718}
719
[b10460a]720/** Data available event.
721 *
[eed4139]722 * @param tcp TCP client
[b752a31]723 * @param icall_handle Call handle
[eed4139]724 * @param icall Call data
[b10460a]725 */
[eed4139]726static void
727tcp_ev_data(tcp_t *tcp, cap_call_handle_t icall_handle, ipc_call_t *icall)
[779541b]728{
729 tcp_conn_t *conn;
730 sysarg_t conn_id;
[b7fd2a0]731 errno_t rc;
[779541b]732
733 conn_id = IPC_GET_ARG1(*icall);
734
735 rc = tcp_conn_get(tcp, conn_id, &conn);
736 if (rc != EOK) {
[a46e56b]737 async_answer_0(icall_handle, ENOENT);
[779541b]738 return;
739 }
740
741 conn->data_avail = true;
742 fibril_condvar_broadcast(&conn->cv);
743
[204ba47]744 if (conn->cb != NULL && conn->cb->data_avail != NULL)
745 conn->cb->data_avail(conn);
746
[a46e56b]747 async_answer_0(icall_handle, EOK);
[779541b]748}
749
[b10460a]750/** Urgent data event.
751 *
[eed4139]752 * @param tcp TCP client
753 * @param icall_handle Call handle
754 * @param icall Call data
[b10460a]755 */
[eed4139]756static void
757tcp_ev_urg_data(tcp_t *tcp, cap_call_handle_t icall_handle, ipc_call_t *icall)
[779541b]758{
[a46e56b]759 async_answer_0(icall_handle, ENOTSUP);
[779541b]760}
761
[b10460a]762/** New connection event.
763 *
[eed4139]764 * @param tcp TCP client
765 * @param icall_handle Call handle
766 * @param icall Call data
[b10460a]767 */
[eed4139]768static void
769tcp_ev_new_conn(tcp_t *tcp, cap_call_handle_t icall_handle, ipc_call_t *icall)
[b99f6e2]770{
771 tcp_listener_t *lst;
772 tcp_conn_t *conn;
773 sysarg_t lst_id;
774 sysarg_t conn_id;
775 fid_t fid;
776 tcp_in_conn_t *cinfo;
[b7fd2a0]777 errno_t rc;
[b99f6e2]778
779 lst_id = IPC_GET_ARG1(*icall);
780 conn_id = IPC_GET_ARG2(*icall);
781
782 rc = tcp_listener_get(tcp, lst_id, &lst);
783 if (rc != EOK) {
[a46e56b]784 async_answer_0(icall_handle, ENOENT);
[b99f6e2]785 return;
786 }
787
788 rc = tcp_conn_new(tcp, conn_id, lst->cb, lst->cb_arg, &conn);
789 if (rc != EOK) {
[a46e56b]790 async_answer_0(icall_handle, ENOMEM);
[b99f6e2]791 return;
792 }
793
794 if (lst->lcb != NULL && lst->lcb->new_conn != NULL) {
795 cinfo = calloc(1, sizeof(tcp_in_conn_t));
796 if (cinfo == NULL) {
[a46e56b]797 async_answer_0(icall_handle, ENOMEM);
[b99f6e2]798 return;
799 }
800
801 cinfo->lst = lst;
802 cinfo->conn = conn;
803
804 fid = fibril_create(tcp_conn_fibril, cinfo);
805 if (fid == 0) {
[a46e56b]806 async_answer_0(icall_handle, ENOMEM);
[b99f6e2]807 }
808
809 fibril_add_ready(fid);
810 }
811
[a46e56b]812 async_answer_0(icall_handle, EOK);
[b99f6e2]813}
814
[b10460a]815/** Callback connection handler.
816 *
[eed4139]817 * @param icall_handle Connect call handle
818 * @param icall Connect call data
819 * @param arg Argument, TCP client
[b10460a]820 */
[eed4139]821static void
822tcp_cb_conn(cap_call_handle_t icall_handle, ipc_call_t *icall, void *arg)
[779541b]823{
824 tcp_t *tcp = (tcp_t *)arg;
825
[a46e56b]826 async_answer_0(icall_handle, EOK);
[779541b]827
828 while (true) {
829 ipc_call_t call;
[a46e56b]830 cap_call_handle_t chandle = async_get_call(&call);
[779541b]831
832 if (!IPC_GET_IMETHOD(call)) {
[1f2b07a]833 /* Hangup*/
834 goto out;
[779541b]835 }
836
837 switch (IPC_GET_IMETHOD(call)) {
838 case TCP_EV_CONNECTED:
[a46e56b]839 tcp_ev_connected(tcp, chandle, &call);
[779541b]840 break;
841 case TCP_EV_CONN_FAILED:
[a46e56b]842 tcp_ev_conn_failed(tcp, chandle, &call);
[779541b]843 break;
844 case TCP_EV_CONN_RESET:
[a46e56b]845 tcp_ev_conn_reset(tcp, chandle, &call);
[779541b]846 break;
847 case TCP_EV_DATA:
[a46e56b]848 tcp_ev_data(tcp, chandle, &call);
[779541b]849 break;
850 case TCP_EV_URG_DATA:
[a46e56b]851 tcp_ev_urg_data(tcp, chandle, &call);
[779541b]852 break;
[b99f6e2]853 case TCP_EV_NEW_CONN:
[a46e56b]854 tcp_ev_new_conn(tcp, chandle, &call);
[b99f6e2]855 break;
[779541b]856 default:
[a46e56b]857 async_answer_0(chandle, ENOTSUP);
[779541b]858 break;
859 }
860 }
[1f2b07a]861out:
862 fibril_mutex_lock(&tcp->lock);
863 tcp->cb_done = true;
864 fibril_mutex_unlock(&tcp->lock);
865 fibril_condvar_broadcast(&tcp->cv);
[fab2746]866}
[d9e2e0e]867
[b10460a]868/** Fibril for handling incoming TCP connection in background.
869 *
870 * @param arg Argument, incoming connection information (@c tcp_in_conn_t)
871 */
[b7fd2a0]872static errno_t tcp_conn_fibril(void *arg)
[b99f6e2]873{
874 tcp_in_conn_t *cinfo = (tcp_in_conn_t *)arg;
875
876 cinfo->lst->lcb->new_conn(cinfo->lst, cinfo->conn);
877 tcp_conn_destroy(cinfo->conn);
878
879 return EOK;
880}
[ddfcfeb2]881
882/** @}
883 */
Note: See TracBrowser for help on using the repository browser.