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