paulo@0: /* paulo@0: * $Id: tx_layer.c,v 1.7 2004/03/24 06:37:30 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: 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: struct tx_layer *gt_tx_layer_new (GtTxStack *stack, const char *name, paulo@0: struct tx_layer_ops *ops) paulo@0: { paulo@0: struct tx_layer *tx; paulo@0: paulo@0: if (!(tx = NEW (struct tx_layer))) paulo@0: return NULL; paulo@0: paulo@0: tx->ops = ops; paulo@0: tx->name = name; paulo@0: tx->stack = stack; paulo@0: tx->partial_buf = NULL; paulo@0: paulo@0: if (!ops->init (tx)) paulo@0: { paulo@0: free (tx); paulo@0: return NULL; paulo@0: } paulo@0: paulo@0: return tx; paulo@0: } paulo@0: paulo@0: void gt_tx_layer_free (struct tx_layer *layer) paulo@0: { paulo@0: if (!layer) paulo@0: return; paulo@0: paulo@0: io_buf_free (layer->partial_buf); paulo@0: paulo@0: layer->ops->destroy (layer); paulo@0: paulo@0: FREE (layer); paulo@0: } paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: static tx_status_t queue_data (struct tx_layer *tx, struct io_buf *io_buf) paulo@0: { paulo@0: tx_status_t ret; paulo@0: paulo@0: ret = tx->ops->queue (tx, io_buf); paulo@0: paulo@0: /* paulo@0: * If the message didn't get completely written, buffer the partial paulo@0: * message buffer until it's finished. paulo@0: */ paulo@0: if (ret == TX_PARTIAL) paulo@0: { paulo@0: assert (io_buf_read_avail (io_buf) > 0); paulo@0: paulo@0: tx->partial_buf = io_buf; paulo@0: return TX_OK; paulo@0: } paulo@0: paulo@0: return ret; paulo@0: } paulo@0: paulo@0: /* send a message to the layer underneath this one */ paulo@0: tx_status_t gt_tx_layer_queue (struct tx_layer *tx, struct io_buf *io_buf) paulo@0: { paulo@0: struct tx_layer *lower = tx->lower; paulo@0: paulo@0: if (lower->partial_buf) paulo@0: return TX_FULL; paulo@0: paulo@0: return queue_data (lower, io_buf); paulo@0: } paulo@0: paulo@0: /* let upper layer know we're writable and get data from it */ paulo@0: tx_status_t gt_tx_layer_ready (struct tx_layer *tx) paulo@0: { paulo@0: struct tx_layer *upper; paulo@0: tx_status_t ret; paulo@0: paulo@0: upper = tx->upper; paulo@0: paulo@0: /* paulo@0: * If there is a partially written buffer on the layer, try to finish it paulo@0: * off before asking for more data by pretending the upper layer tried to paulo@0: * write it. paulo@0: * paulo@0: * Doing this avoids a special case in each layer where a partial buffer paulo@0: * is kept set aside and checked before calling gt_tx_layer_ready(), leading paulo@0: * to less code. paulo@0: */ paulo@0: if (tx->partial_buf) paulo@0: { paulo@0: struct io_buf *io_buf = tx->partial_buf; paulo@0: paulo@0: tx->partial_buf = NULL; paulo@0: paulo@0: /* this ends up calling this layer's queue func */ paulo@0: ret = queue_data (tx, io_buf); paulo@0: paulo@0: /* paulo@0: * Can't happen because layer wouldn't have invoked us. paulo@0: */ paulo@0: assert (ret != TX_FULL); paulo@0: assert (ret != TX_EMPTY); paulo@0: paulo@0: /* paulo@0: * Upper layer can't be safely invoked again, even if the partial paulo@0: * buffer was completed, because we don't know if the lower layer has paulo@0: * any more room. So, the lower layer must reinvoke tx_layer_ready() paulo@0: * to write more data. paulo@0: */ paulo@0: return ret; paulo@0: } paulo@0: paulo@0: ret = upper->ops->ready (upper); paulo@0: assert (ret != TX_FULL); paulo@0: paulo@0: return ret; paulo@0: } paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: void gt_tx_layer_enable (struct tx_layer *layer) paulo@0: { paulo@0: layer->ops->enable (layer); paulo@0: } paulo@0: paulo@0: void gt_tx_layer_disable (struct tx_layer *layer) paulo@0: { paulo@0: layer->ops->disable (layer); paulo@0: }