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

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

Fix spacing in single-line comments using latest ccheck

This found incorrectly formatted section comments (with blocks of
asterisks or dashes). I strongly believe against using section comments
but I am not simply removing them since that would probably be
controversial.

  • Property mode set to 100644
File size: 19.4 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
[984a9ba]43static void tcp_cb_conn(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
[fafb8e5]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;
[fafb8e5]350 lst->id = ipc_get_arg1(&answer);
[779541b]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 rc = async_data_write_start(exch, data, bytes);
[d2e0af47]458 async_exchange_end(exch);
459
[779541b]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;
[fab2746]472}
473
[b10460a]474/** Send FIN.
475 *
476 * Send FIN, indicating no more data will be send over the connection.
477 *
478 * @param conn Connection
[cde999a]479 * @return EOK on success or an error code
[b10460a]480 */
[b7fd2a0]481errno_t tcp_conn_send_fin(tcp_conn_t *conn)
[fab2746]482{
[779541b]483 async_exch_t *exch;
484
485 exch = async_exchange_begin(conn->tcp->sess);
[b7fd2a0]486 errno_t rc = async_req_1_0(exch, TCP_CONN_SEND_FIN, conn->id);
[779541b]487 async_exchange_end(exch);
488
489 return rc;
[fab2746]490}
491
[b10460a]492/** Push connection.
493 *
494 * @param conn Connection
[cde999a]495 * @return EOK on success or an error code
[b10460a]496 */
[b7fd2a0]497errno_t tcp_conn_push(tcp_conn_t *conn)
[fab2746]498{
[779541b]499 async_exch_t *exch;
500
501 exch = async_exchange_begin(conn->tcp->sess);
[b7fd2a0]502 errno_t rc = async_req_1_0(exch, TCP_CONN_PUSH, conn->id);
[779541b]503 async_exchange_end(exch);
504
505 return rc;
[fab2746]506}
507
[b10460a]508/** Reset connection.
509 *
510 * @param conn Connection
[cde999a]511 * @return EOK on success or an error code
[b10460a]512 */
[b7fd2a0]513errno_t tcp_conn_reset(tcp_conn_t *conn)
[fab2746]514{
[779541b]515 async_exch_t *exch;
516
517 exch = async_exchange_begin(conn->tcp->sess);
[b7fd2a0]518 errno_t rc = async_req_1_0(exch, TCP_CONN_RESET, conn->id);
[779541b]519 async_exchange_end(exch);
520
521 return rc;
522}
[fab2746]523
[b10460a]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
[cde999a]539 * error code in case of other error
[b10460a]540 */
[b7fd2a0]541errno_t tcp_conn_recv(tcp_conn_t *conn, void *buf, size_t bsize, size_t *nrecv)
[fab2746]542{
[779541b]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);
[b7fd2a0]554 errno_t rc = async_data_read_start(exch, buf, bsize);
[779541b]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
[b7fd2a0]563 errno_t retval;
[779541b]564 async_wait_for(req, &retval);
565 if (retval != EOK) {
566 fibril_mutex_unlock(&conn->lock);
567 return retval;
568 }
569
[fafb8e5]570 *nrecv = ipc_get_arg1(&answer);
[779541b]571 fibril_mutex_unlock(&conn->lock);
572 return EOK;
[fab2746]573}
574
[b10460a]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 *
[cde999a]587 * @return EOK on success or an error code
[b10460a]588 */
[b7fd2a0]589errno_t tcp_conn_recv_wait(tcp_conn_t *conn, void *buf, size_t bsize,
[b10460a]590 size_t *nrecv)
[fab2746]591{
[779541b]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);
[b7fd2a0]603 errno_t rc = async_data_read_start(exch, buf, bsize);
[779541b]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
[b7fd2a0]617 errno_t retval;
[779541b]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
[fafb8e5]627 *nrecv = ipc_get_arg1(&answer);
[779541b]628 fibril_mutex_unlock(&conn->lock);
629 return EOK;
630}
631
[b10460a]632/** Connection established event.
633 *
[984a9ba]634 * @param tcp TCP client
635 * @param icall Call data
636 *
[b10460a]637 */
[984a9ba]638static void tcp_ev_connected(tcp_t *tcp, ipc_call_t *icall)
[779541b]639{
[1d4b815]640 tcp_conn_t *conn;
641 sysarg_t conn_id;
[b7fd2a0]642 errno_t rc;
[1d4b815]643
[fafb8e5]644 conn_id = ipc_get_arg1(icall);
[1d4b815]645
646 rc = tcp_conn_get(tcp, conn_id, &conn);
647 if (rc != EOK) {
[984a9ba]648 async_answer_0(icall, ENOENT);
[1d4b815]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
[984a9ba]657 async_answer_0(icall, EOK);
[779541b]658}
659
[b10460a]660/** Connection failed event.
661 *
[984a9ba]662 * @param tcp TCP client
663 * @param icall Call data
664 *
[b10460a]665 */
[984a9ba]666static void tcp_ev_conn_failed(tcp_t *tcp, ipc_call_t *icall)
[779541b]667{
[1d4b815]668 tcp_conn_t *conn;
669 sysarg_t conn_id;
[b7fd2a0]670 errno_t rc;
[1d4b815]671
[fafb8e5]672 conn_id = ipc_get_arg1(icall);
[1d4b815]673
674 rc = tcp_conn_get(tcp, conn_id, &conn);
675 if (rc != EOK) {
[984a9ba]676 async_answer_0(icall, ENOENT);
[1d4b815]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
[984a9ba]685 async_answer_0(icall, EOK);
[779541b]686}
687
[b10460a]688/** Connection reset event.
689 *
[984a9ba]690 * @param tcp TCP client
691 * @param icall Call data
692 *
[b10460a]693 */
[984a9ba]694static void tcp_ev_conn_reset(tcp_t *tcp, ipc_call_t *icall)
[779541b]695{
[1d4b815]696 tcp_conn_t *conn;
697 sysarg_t conn_id;
[b7fd2a0]698 errno_t rc;
[1d4b815]699
[fafb8e5]700 conn_id = ipc_get_arg1(icall);
[1d4b815]701
702 rc = tcp_conn_get(tcp, conn_id, &conn);
703 if (rc != EOK) {
[984a9ba]704 async_answer_0(icall, ENOENT);
[1d4b815]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
[984a9ba]713 async_answer_0(icall, EOK);
[779541b]714}
715
[b10460a]716/** Data available event.
717 *
[984a9ba]718 * @param tcp TCP client
719 * @param icall Call data
720 *
[b10460a]721 */
[984a9ba]722static void tcp_ev_data(tcp_t *tcp, ipc_call_t *icall)
[779541b]723{
724 tcp_conn_t *conn;
725 sysarg_t conn_id;
[b7fd2a0]726 errno_t rc;
[779541b]727
[fafb8e5]728 conn_id = ipc_get_arg1(icall);
[779541b]729
730 rc = tcp_conn_get(tcp, conn_id, &conn);
731 if (rc != EOK) {
[984a9ba]732 async_answer_0(icall, ENOENT);
[779541b]733 return;
734 }
735
736 conn->data_avail = true;
737 fibril_condvar_broadcast(&conn->cv);
738
[204ba47]739 if (conn->cb != NULL && conn->cb->data_avail != NULL)
740 conn->cb->data_avail(conn);
741
[984a9ba]742 async_answer_0(icall, EOK);
[779541b]743}
744
[b10460a]745/** Urgent data event.
746 *
[984a9ba]747 * @param tcp TCP client
748 * @param icall Call data
749 *
[b10460a]750 */
[984a9ba]751static void tcp_ev_urg_data(tcp_t *tcp, ipc_call_t *icall)
[779541b]752{
[984a9ba]753 async_answer_0(icall, ENOTSUP);
[779541b]754}
755
[b10460a]756/** New connection event.
757 *
[984a9ba]758 * @param tcp TCP client
759 * @param icall Call data
760 *
[b10460a]761 */
[984a9ba]762static void tcp_ev_new_conn(tcp_t *tcp, ipc_call_t *icall)
[b99f6e2]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;
[b7fd2a0]770 errno_t rc;
[b99f6e2]771
[fafb8e5]772 lst_id = ipc_get_arg1(icall);
773 conn_id = ipc_get_arg2(icall);
[b99f6e2]774
775 rc = tcp_listener_get(tcp, lst_id, &lst);
776 if (rc != EOK) {
[984a9ba]777 async_answer_0(icall, ENOENT);
[b99f6e2]778 return;
779 }
780
781 rc = tcp_conn_new(tcp, conn_id, lst->cb, lst->cb_arg, &conn);
782 if (rc != EOK) {
[984a9ba]783 async_answer_0(icall, ENOMEM);
[b99f6e2]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) {
[984a9ba]790 async_answer_0(icall, ENOMEM);
[b99f6e2]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) {
[984a9ba]799 async_answer_0(icall, ENOMEM);
[b99f6e2]800 }
801
802 fibril_add_ready(fid);
803 }
804
[984a9ba]805 async_answer_0(icall, EOK);
[b99f6e2]806}
807
[b10460a]808/** Callback connection handler.
809 *
[984a9ba]810 * @param icall Connect call data
811 * @param arg Argument, TCP client
812 *
[b10460a]813 */
[984a9ba]814static void tcp_cb_conn(ipc_call_t *icall, void *arg)
[779541b]815{
816 tcp_t *tcp = (tcp_t *)arg;
817
818 while (true) {
819 ipc_call_t call;
[984a9ba]820 async_get_call(&call);
[779541b]821
[fafb8e5]822 if (!ipc_get_imethod(&call)) {
[d1582b50]823 /* Hangup */
[889cdb1]824 async_answer_0(&call, EOK);
[1f2b07a]825 goto out;
[779541b]826 }
827
[fafb8e5]828 switch (ipc_get_imethod(&call)) {
[779541b]829 case TCP_EV_CONNECTED:
[984a9ba]830 tcp_ev_connected(tcp, &call);
[779541b]831 break;
832 case TCP_EV_CONN_FAILED:
[984a9ba]833 tcp_ev_conn_failed(tcp, &call);
[779541b]834 break;
835 case TCP_EV_CONN_RESET:
[984a9ba]836 tcp_ev_conn_reset(tcp, &call);
[779541b]837 break;
838 case TCP_EV_DATA:
[984a9ba]839 tcp_ev_data(tcp, &call);
[779541b]840 break;
841 case TCP_EV_URG_DATA:
[984a9ba]842 tcp_ev_urg_data(tcp, &call);
[779541b]843 break;
[b99f6e2]844 case TCP_EV_NEW_CONN:
[984a9ba]845 tcp_ev_new_conn(tcp, &call);
[b99f6e2]846 break;
[779541b]847 default:
[984a9ba]848 async_answer_0(&call, ENOTSUP);
[779541b]849 break;
850 }
851 }
[984a9ba]852
[1f2b07a]853out:
854 fibril_mutex_lock(&tcp->lock);
855 tcp->cb_done = true;
856 fibril_mutex_unlock(&tcp->lock);
857 fibril_condvar_broadcast(&tcp->cv);
[fab2746]858}
[d9e2e0e]859
[b10460a]860/** Fibril for handling incoming TCP connection in background.
861 *
862 * @param arg Argument, incoming connection information (@c tcp_in_conn_t)
863 */
[b7fd2a0]864static errno_t tcp_conn_fibril(void *arg)
[b99f6e2]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}
[ddfcfeb2]873
874/** @}
875 */
Note: See TracBrowser for help on using the repository browser.