paulo@0: /* paulo@0: * $Id: tx_stack.c,v 1.12 2004/04/17 06:07:33 hipnod Exp $ paulo@0: * paulo@0: * Copyright (C) 2003 giFT project (gift.sourceforge.net) paulo@0: * paulo@0: * This program is free software; you can redistribute it and/or modify it paulo@0: * under the terms of the GNU General Public License as published by the paulo@0: * Free Software Foundation; either version 2, or (at your option) any paulo@0: * later version. paulo@0: * paulo@0: * This program is distributed in the hope that it will be useful, but paulo@0: * WITHOUT ANY WARRANTY; without even the implied warranty of paulo@0: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU paulo@0: * General Public License for more details. paulo@0: */ paulo@0: paulo@0: #include "gt_gnutella.h" paulo@0: #include "gt_packet.h" /* gt_packet_log XXX */ paulo@0: paulo@0: #include "io/tx_stack.h" paulo@0: #include "io/tx_layer.h" paulo@0: #include "io/io_buf.h" paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: extern struct tx_layer_ops gt_tx_packet_ops; paulo@0: extern struct tx_layer_ops gt_tx_deflate_ops; paulo@0: extern struct tx_layer_ops gt_tx_link_ops; paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: static struct use_tx_layer paulo@0: { paulo@0: const char *name; paulo@0: struct tx_layer_ops *ops; paulo@0: } tx_layers[] = paulo@0: { paulo@0: { "tx_link", >_tx_link_ops, }, paulo@0: { "tx_deflate", >_tx_deflate_ops }, paulo@0: { "tx_packet", >_tx_packet_ops }, paulo@0: }; paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: static void foreach_tx_child (struct tx_layer *tx, paulo@0: void (*exec) (struct tx_layer *tx)) paulo@0: { paulo@0: struct tx_layer *next; paulo@0: paulo@0: while (tx != NULL) paulo@0: { paulo@0: /* grab the next element first so the callback can call free */ paulo@0: next = tx->lower; paulo@0: paulo@0: exec (tx); paulo@0: paulo@0: tx = next; paulo@0: } paulo@0: } paulo@0: paulo@0: static struct tx_layer *tx_push_layer (struct tx_layer *below, paulo@0: struct tx_layer *above) paulo@0: { paulo@0: if (above) paulo@0: above->lower = below; paulo@0: paulo@0: if (below) paulo@0: below->upper = above; paulo@0: paulo@0: return above; paulo@0: } paulo@0: paulo@0: static void destroy_tx (struct tx_layer *tx) paulo@0: { paulo@0: gt_tx_layer_free (tx); paulo@0: } paulo@0: paulo@0: static void disable_tx (struct tx_layer *tx) paulo@0: { paulo@0: gt_tx_layer_disable (tx); paulo@0: } paulo@0: paulo@0: static void disable_all_tx_layers (struct tx_layer *layers) paulo@0: { paulo@0: if (!layers) paulo@0: return; paulo@0: paulo@0: assert (layers->upper == NULL); paulo@0: paulo@0: foreach_tx_child (layers, disable_tx); paulo@0: } paulo@0: paulo@0: static void free_all_tx_layers (struct tx_layer *layers) paulo@0: { paulo@0: if (!layers) paulo@0: return; paulo@0: paulo@0: disable_all_tx_layers (layers); paulo@0: foreach_tx_child (layers, destroy_tx); paulo@0: } paulo@0: paulo@0: static struct tx_layer *alloc_tx_layers (GtTxStack *stack, BOOL tx_deflated) paulo@0: { paulo@0: struct tx_layer *new_layer; paulo@0: struct tx_layer *layer = NULL; paulo@0: int i; paulo@0: paulo@0: for (i = 0; i < sizeof(tx_layers) / sizeof(tx_layers[0]); i++) paulo@0: { paulo@0: if (!strcmp (tx_layers[i].name, "tx_deflate") && !tx_deflated) paulo@0: continue; paulo@0: paulo@0: if (!(new_layer = gt_tx_layer_new (stack, tx_layers[i].name, paulo@0: tx_layers[i].ops))) paulo@0: { paulo@0: foreach_tx_child (layer, destroy_tx); paulo@0: return NULL; paulo@0: } paulo@0: paulo@0: layer = tx_push_layer (layer, new_layer); paulo@0: } paulo@0: paulo@0: return layer; paulo@0: } paulo@0: paulo@0: GtTxStack *gt_tx_stack_new (TCPC *c, BOOL tx_deflated) paulo@0: { paulo@0: struct gt_tx_stack *stack; paulo@0: int size; paulo@0: paulo@0: if (!(stack = NEW (struct gt_tx_stack))) paulo@0: return NULL; paulo@0: paulo@0: if (!(stack->layers = alloc_tx_layers (stack, tx_deflated))) paulo@0: { paulo@0: free (stack); paulo@0: return NULL; paulo@0: } paulo@0: paulo@0: /* set the send buffer to a not too high value */ paulo@0: size = 256; paulo@0: paulo@0: if (setsockopt (c->fd, SOL_SOCKET, SO_SNDBUF, &size, sizeof (size)) != 0) paulo@0: GT->DBGSOCK (GT, c, "Error setting sndbuf size: %s", GIFT_NETERROR()); paulo@0: paulo@0: stack->c = c; paulo@0: stack->start_time = time (NULL); paulo@0: paulo@0: return stack; paulo@0: } paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: void gt_tx_stack_free (GtTxStack *stack) paulo@0: { paulo@0: if (!stack) paulo@0: return; paulo@0: paulo@0: free_all_tx_layers (stack->layers); paulo@0: FREE (stack); paulo@0: } paulo@0: paulo@0: void gt_tx_stack_abort (GtTxStack *stack) paulo@0: { paulo@0: stack->cleanup (stack, stack->udata); paulo@0: } paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: static void activate_tx (struct tx_layer *tx) paulo@0: { paulo@0: tx->ops->toggle (tx, FALSE); paulo@0: } paulo@0: paulo@0: /* paulo@0: * Start sending data down the stack. Called asynchronously by the topmost paulo@0: * layer. paulo@0: */ paulo@0: void gt_tx_stack_activate (GtTxStack *stack) paulo@0: { paulo@0: foreach_tx_child (stack->layers, activate_tx); paulo@0: } paulo@0: paulo@0: static void deactivate_tx (struct tx_layer *tx) paulo@0: { paulo@0: tx->ops->toggle (tx, TRUE); paulo@0: } paulo@0: paulo@0: void gt_tx_stack_deactivate (GtTxStack *stack) paulo@0: { paulo@0: foreach_tx_child (stack->layers, deactivate_tx); paulo@0: } paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: BOOL gt_tx_stack_queue (GtTxStack *stack, const uint8_t *data, size_t len) paulo@0: { paulo@0: struct io_buf *io_buf; paulo@0: struct tx_layer *tx; paulo@0: uint8_t *ptr; paulo@0: tx_status_t ret; paulo@0: GtPacket pkt; paulo@0: paulo@0: if (!(io_buf = io_buf_new (len))) paulo@0: return FALSE; paulo@0: paulo@0: ptr = io_buf_write_ptr (io_buf); paulo@0: paulo@0: memcpy (ptr, data, len); paulo@0: io_buf_push (io_buf, len); paulo@0: paulo@0: tx = stack->layers; paulo@0: paulo@0: /* send the data on its way down the stack */ paulo@0: if ((ret = tx->ops->queue (tx, io_buf)) != TX_OK) paulo@0: { paulo@0: GT->DBGSOCK (GT, stack->c, "bad txstatus: %d", ret); paulo@0: gt_tx_stack_abort (stack); paulo@0: return FALSE; paulo@0: } paulo@0: paulo@0: pkt.data = (unsigned char *)data; paulo@0: pkt.len = len; paulo@0: paulo@0: gt_packet_log (&pkt, stack->c, TRUE); paulo@0: paulo@0: /* paulo@0: * Activate the stack if not active already. NOTE: this actually paulo@0: * sucks bad when using compression because we end up enabling, then paulo@0: * disabling right away until some data is compressed by nagle timer. paulo@0: */ paulo@0: gt_tx_stack_activate (stack); paulo@0: paulo@0: return TRUE; paulo@0: } paulo@0: paulo@0: int gt_tx_stack_send (GtTxStack *stack, const uint8_t *data, size_t len) paulo@0: { paulo@0: int ret; paulo@0: paulo@0: /* check if the file descriptor has an error */ paulo@0: if (net_sock_error (stack->c->fd)) paulo@0: return -1; paulo@0: paulo@0: ret = tcp_send (stack->c, (unsigned char *)data, len); paulo@0: paulo@0: return ret; paulo@0: } paulo@0: paulo@0: void gt_tx_stack_set_handler (GtTxStack *stack, GtTxStackCleanup cleanup, paulo@0: void *udata) paulo@0: { paulo@0: stack->cleanup = cleanup; paulo@0: stack->udata = udata; paulo@0: }