1 /*
2  * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301, USA.
18  *
19  * You can also choose to distribute this program under the terms of
20  * the Unmodified Binary Distribution Licence (as given in the file
21  * COPYING.UBDL), provided that you have satisfied its requirements.
22  */
23 
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25 
26 #include <string.h>
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <errno.h>
30 #include <ipxe/iobuf.h>
31 #include <ipxe/xfer.h>
32 #include <ipxe/open.h>
33 
34 /** @file
35  *
36  * Data transfer interfaces
37  *
38  */
39 
40 /**
41  * Dummy transfer metadata
42  *
43  * This gets passed to xfer_interface::deliver() and equivalents when
44  * no metadata is available.
45  */
46 static struct xfer_metadata dummy_metadata;
47 
48 /*****************************************************************************
49  *
50  * Data transfer interface operations
51  *
52  */
53 
54 /**
55  * Send redirection event
56  *
57  * @v intf		Data transfer interface
58  * @v type		New location type
59  * @v args		Remaining arguments depend upon location type
60  * @ret rc		Return status code
61  */
xfer_vredirect(struct interface * intf,int type,va_list args)62 int xfer_vredirect ( struct interface *intf, int type, va_list args ) {
63 	struct interface tmp = INTF_INIT ( null_intf_desc );
64 	struct interface *dest;
65 	xfer_vredirect_TYPE ( void * ) *op =
66 		intf_get_dest_op_no_passthru ( intf, xfer_vredirect, &dest );
67 	void *object = intf_object ( dest );
68 	int rc;
69 
70 	DBGC ( INTF_COL ( intf ), "INTF " INTF_INTF_FMT " redirect\n",
71 	       INTF_INTF_DBG ( intf, dest ) );
72 
73 	if ( op ) {
74 		rc = op ( object, type, args );
75 	} else {
76 		/* Default is to reopen the interface as instructed,
77 		 * then send xfer_window_changed() messages to both
78 		 * new child and parent interfaces.  Since our
79 		 * original child interface is likely to be closed and
80 		 * unplugged as a result of the call to
81 		 * xfer_vreopen(), we create a temporary interface in
82 		 * order to be able to send xfer_window_changed() to
83 		 * the parent.
84 		 *
85 		 * If redirection fails, then send intf_close() to the
86 		 * parent interface.
87 		 */
88 		intf_plug ( &tmp, dest );
89 		rc = xfer_vreopen ( dest, type, args );
90 		if ( rc == 0 ) {
91 			xfer_window_changed ( dest );
92 			xfer_window_changed ( &tmp );
93 		} else {
94 			intf_close ( &tmp, rc );
95 		}
96 		intf_unplug ( &tmp );
97 	}
98 
99 	if ( rc != 0 ) {
100 		DBGC ( INTF_COL ( intf ), "INTF " INTF_INTF_FMT " redirect "
101 		       "failed: %s\n", INTF_INTF_DBG ( intf, dest ),
102 		       strerror ( rc ) );
103 	}
104 
105 	intf_put ( dest );
106 	return rc;
107 }
108 
109 /**
110  * Check flow control window
111  *
112  * @v intf		Data transfer interface
113  * @ret len		Length of window
114  */
xfer_window(struct interface * intf)115 size_t xfer_window ( struct interface *intf ) {
116 	struct interface *dest;
117 	xfer_window_TYPE ( void * ) *op =
118 		intf_get_dest_op ( intf, xfer_window, &dest );
119 	void *object = intf_object ( dest );
120 	size_t len;
121 
122 	if ( op ) {
123 		len = op ( object );
124 	} else {
125 		/* Default is to provide an unlimited window */
126 		len = ~( ( size_t ) 0 );
127 	}
128 
129 	intf_put ( dest );
130 	return len;
131 }
132 
133 /**
134  * Report change of flow control window
135  *
136  * @v intf		Data transfer interface
137  *
138  * Note that this method is used to indicate only unsolicited changes
139  * in the flow control window.  In particular, this method must not be
140  * called as part of the response to xfer_deliver(), since that could
141  * easily lead to an infinite loop.  Callers of xfer_deliver() should
142  * assume that the flow control window will have changed without
143  * generating an xfer_window_changed() message.
144  */
xfer_window_changed(struct interface * intf)145 void xfer_window_changed ( struct interface *intf ) {
146 
147 	intf_poke ( intf, xfer_window_changed );
148 }
149 
150 /**
151  * Allocate I/O buffer
152  *
153  * @v intf		Data transfer interface
154  * @v len		I/O buffer payload length
155  * @ret iobuf		I/O buffer
156  */
xfer_alloc_iob(struct interface * intf,size_t len)157 struct io_buffer * xfer_alloc_iob ( struct interface *intf, size_t len ) {
158 	struct interface *dest;
159 	xfer_alloc_iob_TYPE ( void * ) *op =
160 		intf_get_dest_op ( intf, xfer_alloc_iob, &dest );
161 	void *object = intf_object ( dest );
162 	struct io_buffer *iobuf;
163 
164 	DBGC ( INTF_COL ( intf ), "INTF " INTF_INTF_FMT " alloc_iob %zd\n",
165 	       INTF_INTF_DBG ( intf, dest ), len );
166 
167 	if ( op ) {
168 		iobuf = op ( object, len );
169 	} else {
170 		/* Default is to allocate an I/O buffer with no
171 		 * reserved space.
172 		 */
173 		iobuf = alloc_iob ( len );
174 	}
175 
176 	if ( ! iobuf ) {
177 		DBGC ( INTF_COL ( intf ), "INTF " INTF_INTF_FMT " alloc_iob "
178 		       "failed\n", INTF_INTF_DBG ( intf, dest ) );
179 	}
180 
181 	intf_put ( dest );
182 	return iobuf;
183 }
184 
185 /**
186  * Deliver datagram
187  *
188  * @v intf		Data transfer interface
189  * @v iobuf		Datagram I/O buffer
190  * @v meta		Data transfer metadata
191  * @ret rc		Return status code
192  */
xfer_deliver(struct interface * intf,struct io_buffer * iobuf,struct xfer_metadata * meta)193 int xfer_deliver ( struct interface *intf,
194 		   struct io_buffer *iobuf,
195 		   struct xfer_metadata *meta ) {
196 	struct interface *dest;
197 	xfer_deliver_TYPE ( void * ) *op =
198 		intf_get_dest_op ( intf, xfer_deliver, &dest );
199 	void *object = intf_object ( dest );
200 	int rc;
201 
202 	DBGC ( INTF_COL ( intf ), "INTF " INTF_INTF_FMT " deliver %zd\n",
203 	       INTF_INTF_DBG ( intf, dest ), iob_len ( iobuf ) );
204 
205 	if ( op ) {
206 		rc = op ( object, iobuf, meta );
207 	} else {
208 		/* Default is to discard the I/O buffer */
209 		free_iob ( iobuf );
210 		rc = -EPIPE;
211 	}
212 
213 	if ( rc != 0 ) {
214 		DBGC ( INTF_COL ( intf ), "INTF " INTF_INTF_FMT
215 		       " deliver failed: %s\n",
216 		       INTF_INTF_DBG ( intf, dest ), strerror ( rc ) );
217 	}
218 
219 	intf_put ( dest );
220 	return rc;
221 }
222 
223 /*****************************************************************************
224  *
225  * Data transfer interface helper functions
226  *
227  */
228 
229 /**
230  * Send redirection event
231  *
232  * @v intf		Data transfer interface
233  * @v type		New location type
234  * @v ...		Remaining arguments depend upon location type
235  * @ret rc		Return status code
236  */
xfer_redirect(struct interface * intf,int type,...)237 int xfer_redirect ( struct interface *intf, int type, ... ) {
238 	va_list args;
239 	int rc;
240 
241 	va_start ( args, type );
242 	rc = xfer_vredirect ( intf, type, args );
243 	va_end ( args );
244 	return rc;
245 }
246 
247 /**
248  * Deliver datagram as I/O buffer without metadata
249  *
250  * @v intf		Data transfer interface
251  * @v iobuf		Datagram I/O buffer
252  * @ret rc		Return status code
253  */
xfer_deliver_iob(struct interface * intf,struct io_buffer * iobuf)254 int xfer_deliver_iob ( struct interface *intf, struct io_buffer *iobuf ) {
255 	return xfer_deliver ( intf, iobuf, &dummy_metadata );
256 }
257 
258 /**
259  * Deliver datagram as raw data
260  *
261  * @v intf		Data transfer interface
262  * @v data		Data
263  * @v len		Length of data
264  * @v meta		Data transfer metadata
265  * @ret rc		Return status code
266  */
xfer_deliver_raw_meta(struct interface * intf,const void * data,size_t len,struct xfer_metadata * meta)267 int xfer_deliver_raw_meta ( struct interface *intf, const void *data,
268 			    size_t len, struct xfer_metadata *meta ) {
269 	struct io_buffer *iobuf;
270 
271 	iobuf = xfer_alloc_iob ( intf, len );
272 	if ( ! iobuf )
273 		return -ENOMEM;
274 
275 	memcpy ( iob_put ( iobuf, len ), data, len );
276 	return xfer_deliver ( intf, iobuf, meta );
277 }
278 
279 /**
280  * Deliver datagram as raw data without metadata
281  *
282  * @v intf		Data transfer interface
283  * @v data		Data
284  * @v len		Length of data
285  * @ret rc		Return status code
286  */
xfer_deliver_raw(struct interface * intf,const void * data,size_t len)287 int xfer_deliver_raw ( struct interface *intf, const void *data, size_t len ) {
288 	return xfer_deliver_raw_meta ( intf, data, len, &dummy_metadata );
289 }
290 
291 /**
292  * Deliver formatted string
293  *
294  * @v intf		Data transfer interface
295  * @v format		Format string
296  * @v args		Arguments corresponding to the format string
297  * @ret rc		Return status code
298  */
xfer_vprintf(struct interface * intf,const char * format,va_list args)299 int xfer_vprintf ( struct interface *intf, const char *format,
300 		   va_list args ) {
301 	va_list args_tmp;
302 	char *buf;
303 	int len;
304 	int rc;
305 
306 	/* Create temporary string */
307 	va_copy ( args_tmp, args );
308 	len = vasprintf ( &buf, format, args );
309 	va_end ( args_tmp );
310 	if ( len < 0 ) {
311 		rc = len;
312 		goto err_asprintf;
313 	}
314 
315 	/* Transmit string */
316 	if ( ( rc = xfer_deliver_raw ( intf, buf, len ) ) != 0 )
317 		goto err_deliver;
318 
319  err_deliver:
320 	free ( buf );
321  err_asprintf:
322 	return rc;
323 }
324 
325 /**
326  * Deliver formatted string
327  *
328  * @v intf		Data transfer interface
329  * @v format		Format string
330  * @v ...		Arguments corresponding to the format string
331  * @ret rc		Return status code
332  */
xfer_printf(struct interface * intf,const char * format,...)333 int xfer_printf ( struct interface *intf, const char *format, ... ) {
334 	va_list args;
335 	int rc;
336 
337 	va_start ( args, format );
338 	rc = xfer_vprintf ( intf, format, args );
339 	va_end ( args );
340 	return rc;
341 }
342 
343 /**
344  * Seek to position
345  *
346  * @v intf		Data transfer interface
347  * @v offset		Offset to new position
348  * @ret rc		Return status code
349  */
xfer_seek(struct interface * intf,off_t offset)350 int xfer_seek ( struct interface *intf, off_t offset ) {
351 	struct io_buffer *iobuf;
352 	struct xfer_metadata meta = {
353 		.flags = XFER_FL_ABS_OFFSET,
354 		.offset = offset,
355 	};
356 
357 	DBGC ( INTF_COL ( intf ), "INTF " INTF_FMT " seek to %ld\n",
358 	       INTF_DBG ( intf ), offset );
359 
360 	/* Allocate and send a zero-length data buffer */
361 	iobuf = xfer_alloc_iob ( intf, 0 );
362 	if ( ! iobuf )
363 		return -ENOMEM;
364 
365 	return xfer_deliver ( intf, iobuf, &meta );
366 }
367 
368 /**
369  * Check that data is delivered strictly in order
370  *
371  * @v meta		Data transfer metadata
372  * @v pos		Current position
373  * @v len		Length of data
374  * @ret rc		Return status code
375  */
xfer_check_order(struct xfer_metadata * meta,size_t * pos,size_t len)376 int xfer_check_order ( struct xfer_metadata *meta, size_t *pos, size_t len ) {
377 	size_t new_pos;
378 
379 	/* Allow out-of-order zero-length packets (as used by xfer_seek()) */
380 	if ( len == 0 )
381 		return 0;
382 
383 	/* Calculate position of this delivery */
384 	new_pos = *pos;
385 	if ( meta->flags & XFER_FL_ABS_OFFSET )
386 		new_pos = 0;
387 	new_pos += meta->offset;
388 
389 	/* Fail if delivery position is not equal to current position */
390 	if ( new_pos != *pos )
391 		return -EPROTO;
392 
393 	/* Update current position */
394 	*pos += len;
395 
396 	return 0;
397 }
398