iPXE
xfer.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017  * 02110-1301, USA.
00018  *
00019  * You can also choose to distribute this program under the terms of
00020  * the Unmodified Binary Distribution Licence (as given in the file
00021  * COPYING.UBDL), provided that you have satisfied its requirements.
00022  */
00023 
00024 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
00025 
00026 #include <string.h>
00027 #include <stdlib.h>
00028 #include <stdio.h>
00029 #include <errno.h>
00030 #include <ipxe/iobuf.h>
00031 #include <ipxe/xfer.h>
00032 #include <ipxe/open.h>
00033 
00034 /** @file
00035  *
00036  * Data transfer interfaces
00037  *
00038  */
00039 
00040 /**
00041  * Dummy transfer metadata
00042  *
00043  * This gets passed to xfer_interface::deliver() and equivalents when
00044  * no metadata is available.
00045  */
00046 static struct xfer_metadata dummy_metadata;
00047 
00048 /*****************************************************************************
00049  *
00050  * Data transfer interface operations
00051  *
00052  */
00053 
00054 /**
00055  * Send redirection event
00056  *
00057  * @v intf              Data transfer interface
00058  * @v type              New location type
00059  * @v args              Remaining arguments depend upon location type
00060  * @ret rc              Return status code
00061  */
00062 int xfer_vredirect ( struct interface *intf, int type, va_list args ) {
00063         struct interface tmp = INTF_INIT ( null_intf_desc );
00064         struct interface *dest;
00065         xfer_vredirect_TYPE ( void * ) *op =
00066                 intf_get_dest_op_no_passthru ( intf, xfer_vredirect, &dest );
00067         void *object = intf_object ( dest );
00068         int rc;
00069 
00070         DBGC ( INTF_COL ( intf ), "INTF " INTF_INTF_FMT " redirect\n",
00071                INTF_INTF_DBG ( intf, dest ) );
00072 
00073         if ( op ) {
00074                 rc = op ( object, type, args );
00075         } else {
00076                 /* Default is to reopen the interface as instructed,
00077                  * then send xfer_window_changed() messages to both
00078                  * new child and parent interfaces.  Since our
00079                  * original child interface is likely to be closed and
00080                  * unplugged as a result of the call to
00081                  * xfer_vreopen(), we create a temporary interface in
00082                  * order to be able to send xfer_window_changed() to
00083                  * the parent.
00084                  *
00085                  * If redirection fails, then send intf_close() to the
00086                  * parent interface.
00087                  */
00088                 intf_plug ( &tmp, dest );
00089                 rc = xfer_vreopen ( dest, type, args );
00090                 if ( rc == 0 ) {
00091                         xfer_window_changed ( dest );
00092                         xfer_window_changed ( &tmp );
00093                 } else {
00094                         intf_close ( &tmp, rc );
00095                 }
00096                 intf_unplug ( &tmp );
00097         }
00098 
00099         if ( rc != 0 ) {
00100                 DBGC ( INTF_COL ( intf ), "INTF " INTF_INTF_FMT " redirect "
00101                        "failed: %s\n", INTF_INTF_DBG ( intf, dest ),
00102                        strerror ( rc ) );
00103         }
00104 
00105         intf_put ( dest );
00106         return rc;
00107 }
00108 
00109 /**
00110  * Check flow control window
00111  *
00112  * @v intf              Data transfer interface
00113  * @ret len             Length of window
00114  */
00115 size_t xfer_window ( struct interface *intf ) {
00116         struct interface *dest;
00117         xfer_window_TYPE ( void * ) *op =
00118                 intf_get_dest_op ( intf, xfer_window, &dest );
00119         void *object = intf_object ( dest );
00120         size_t len;
00121 
00122         if ( op ) {
00123                 len = op ( object );
00124         } else {
00125                 /* Default is to provide an unlimited window */
00126                 len = ~( ( size_t ) 0 );
00127         }
00128 
00129         intf_put ( dest );
00130         return len;
00131 }
00132 
00133 /**
00134  * Report change of flow control window
00135  *
00136  * @v intf              Data transfer interface
00137  *
00138  * Note that this method is used to indicate only unsolicited changes
00139  * in the flow control window.  In particular, this method must not be
00140  * called as part of the response to xfer_deliver(), since that could
00141  * easily lead to an infinite loop.  Callers of xfer_deliver() should
00142  * assume that the flow control window will have changed without
00143  * generating an xfer_window_changed() message.
00144  */
00145 void xfer_window_changed ( struct interface *intf ) {
00146 
00147         intf_poke ( intf, xfer_window_changed );
00148 }
00149 
00150 /**
00151  * Allocate I/O buffer
00152  *
00153  * @v intf              Data transfer interface
00154  * @v len               I/O buffer payload length
00155  * @ret iobuf           I/O buffer
00156  */
00157 struct io_buffer * xfer_alloc_iob ( struct interface *intf, size_t len ) {
00158         struct interface *dest;
00159         xfer_alloc_iob_TYPE ( void * ) *op =
00160                 intf_get_dest_op ( intf, xfer_alloc_iob, &dest );
00161         void *object = intf_object ( dest );
00162         struct io_buffer *iobuf;
00163 
00164         DBGC ( INTF_COL ( intf ), "INTF " INTF_INTF_FMT " alloc_iob %zd\n",
00165                INTF_INTF_DBG ( intf, dest ), len );
00166 
00167         if ( op ) {
00168                 iobuf = op ( object, len );
00169         } else {
00170                 /* Default is to allocate an I/O buffer with no
00171                  * reserved space.
00172                  */
00173                 iobuf = alloc_iob ( len );
00174         }
00175 
00176         if ( ! iobuf ) {
00177                 DBGC ( INTF_COL ( intf ), "INTF " INTF_INTF_FMT " alloc_iob "
00178                        "failed\n", INTF_INTF_DBG ( intf, dest ) );
00179         }
00180 
00181         intf_put ( dest );
00182         return iobuf;
00183 }
00184 
00185 /**
00186  * Deliver datagram
00187  *
00188  * @v intf              Data transfer interface
00189  * @v iobuf             Datagram I/O buffer
00190  * @v meta              Data transfer metadata
00191  * @ret rc              Return status code
00192  */
00193 int xfer_deliver ( struct interface *intf,
00194                    struct io_buffer *iobuf,
00195                    struct xfer_metadata *meta ) {
00196         struct interface *dest;
00197         xfer_deliver_TYPE ( void * ) *op =
00198                 intf_get_dest_op ( intf, xfer_deliver, &dest );
00199         void *object = intf_object ( dest );
00200         int rc;
00201 
00202         DBGC ( INTF_COL ( intf ), "INTF " INTF_INTF_FMT " deliver %zd\n",
00203                INTF_INTF_DBG ( intf, dest ), iob_len ( iobuf ) );
00204 
00205         if ( op ) {
00206                 rc = op ( object, iobuf, meta );
00207         } else {
00208                 /* Default is to discard the I/O buffer */
00209                 free_iob ( iobuf );
00210                 rc = -EPIPE;
00211         }
00212 
00213         if ( rc != 0 ) {
00214                 DBGC ( INTF_COL ( intf ), "INTF " INTF_INTF_FMT
00215                        " deliver failed: %s\n",
00216                        INTF_INTF_DBG ( intf, dest ), strerror ( rc ) );
00217         }
00218 
00219         intf_put ( dest );
00220         return rc;
00221 }
00222 
00223 /*****************************************************************************
00224  *
00225  * Data transfer interface helper functions
00226  *
00227  */
00228 
00229 /**
00230  * Send redirection event
00231  *
00232  * @v intf              Data transfer interface
00233  * @v type              New location type
00234  * @v ...               Remaining arguments depend upon location type
00235  * @ret rc              Return status code
00236  */
00237 int xfer_redirect ( struct interface *intf, int type, ... ) {
00238         va_list args;
00239         int rc;
00240 
00241         va_start ( args, type );
00242         rc = xfer_vredirect ( intf, type, args );
00243         va_end ( args );
00244         return rc;
00245 }
00246 
00247 /**
00248  * Deliver datagram as I/O buffer without metadata
00249  *
00250  * @v intf              Data transfer interface
00251  * @v iobuf             Datagram I/O buffer
00252  * @ret rc              Return status code
00253  */
00254 int xfer_deliver_iob ( struct interface *intf, struct io_buffer *iobuf ) {
00255         return xfer_deliver ( intf, iobuf, &dummy_metadata );
00256 }
00257 
00258 /**
00259  * Deliver datagram as raw data
00260  *
00261  * @v intf              Data transfer interface
00262  * @v data              Data
00263  * @v len               Length of data
00264  * @v meta              Data transfer metadata
00265  * @ret rc              Return status code
00266  */
00267 int xfer_deliver_raw_meta ( struct interface *intf, const void *data,
00268                             size_t len, struct xfer_metadata *meta ) {
00269         struct io_buffer *iobuf;
00270 
00271         iobuf = xfer_alloc_iob ( intf, len );
00272         if ( ! iobuf )
00273                 return -ENOMEM;
00274 
00275         memcpy ( iob_put ( iobuf, len ), data, len );
00276         return xfer_deliver ( intf, iobuf, meta );
00277 }
00278 
00279 /**
00280  * Deliver datagram as raw data without metadata
00281  *
00282  * @v intf              Data transfer interface
00283  * @v data              Data
00284  * @v len               Length of data
00285  * @ret rc              Return status code
00286  */
00287 int xfer_deliver_raw ( struct interface *intf, const void *data, size_t len ) {
00288         return xfer_deliver_raw_meta ( intf, data, len, &dummy_metadata );
00289 }
00290 
00291 /**
00292  * Deliver formatted string
00293  *
00294  * @v intf              Data transfer interface
00295  * @v format            Format string
00296  * @v args              Arguments corresponding to the format string
00297  * @ret rc              Return status code
00298  */
00299 int xfer_vprintf ( struct interface *intf, const char *format,
00300                    va_list args ) {
00301         va_list args_tmp;
00302         char *buf;
00303         int len;
00304         int rc;
00305 
00306         /* Create temporary string */
00307         va_copy ( args_tmp, args );
00308         len = vasprintf ( &buf, format, args );
00309         va_end ( args_tmp );
00310         if ( len < 0 ) {
00311                 rc = len;
00312                 goto err_asprintf;
00313         }
00314 
00315         /* Transmit string */
00316         if ( ( rc = xfer_deliver_raw ( intf, buf, len ) ) != 0 )
00317                 goto err_deliver;
00318 
00319  err_deliver:
00320         free ( buf );
00321  err_asprintf:
00322         return rc;
00323 }
00324 
00325 /**
00326  * Deliver formatted string
00327  *
00328  * @v intf              Data transfer interface
00329  * @v format            Format string
00330  * @v ...               Arguments corresponding to the format string
00331  * @ret rc              Return status code
00332  */
00333 int xfer_printf ( struct interface *intf, const char *format, ... ) {
00334         va_list args;
00335         int rc;
00336 
00337         va_start ( args, format );
00338         rc = xfer_vprintf ( intf, format, args );
00339         va_end ( args );
00340         return rc;
00341 }
00342 
00343 /**
00344  * Seek to position
00345  *
00346  * @v intf              Data transfer interface
00347  * @v offset            Offset to new position
00348  * @ret rc              Return status code
00349  */
00350 int xfer_seek ( struct interface *intf, off_t offset ) {
00351         struct io_buffer *iobuf;
00352         struct xfer_metadata meta = {
00353                 .flags = XFER_FL_ABS_OFFSET,
00354                 .offset = offset,
00355         };
00356 
00357         DBGC ( INTF_COL ( intf ), "INTF " INTF_FMT " seek to %ld\n",
00358                INTF_DBG ( intf ), offset );
00359 
00360         /* Allocate and send a zero-length data buffer */
00361         iobuf = xfer_alloc_iob ( intf, 0 );
00362         if ( ! iobuf )
00363                 return -ENOMEM;
00364 
00365         return xfer_deliver ( intf, iobuf, &meta );
00366 }
00367 
00368 /**
00369  * Check that data is delivered strictly in order
00370  *
00371  * @v meta              Data transfer metadata
00372  * @v pos               Current position
00373  * @v len               Length of data
00374  * @ret rc              Return status code
00375  */
00376 int xfer_check_order ( struct xfer_metadata *meta, size_t *pos, size_t len ) {
00377         size_t new_pos;
00378 
00379         /* Allow out-of-order zero-length packets (as used by xfer_seek()) */
00380         if ( len == 0 )
00381                 return 0;
00382 
00383         /* Calculate position of this delivery */
00384         new_pos = *pos;
00385         if ( meta->flags & XFER_FL_ABS_OFFSET )
00386                 new_pos = 0;
00387         new_pos += meta->offset;
00388 
00389         /* Fail if delivery position is not equal to current position */
00390         if ( new_pos != *pos )
00391                 return -EPROTO;
00392 
00393         /* Update current position */
00394         *pos += len;
00395 
00396         return 0;
00397 }