[intel] Forcibly skip PHY reset on some models
[ipxe.git] / src / core / xfer.c
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 */
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 intf_plug ( &tmp, dest );
86 rc = xfer_vreopen ( dest, type, args );
87 if ( rc == 0 ) {
88 xfer_window_changed ( dest );
89 xfer_window_changed ( &tmp );
90 }
91 intf_unplug ( &tmp );
92 }
93
94 if ( rc != 0 ) {
95 DBGC ( INTF_COL ( intf ), "INTF " INTF_INTF_FMT " redirect "
96 "failed: %s\n", INTF_INTF_DBG ( intf, dest ),
97 strerror ( rc ) );
98 }
99
100 intf_put ( dest );
101 return rc;
102 }
103
104 /**
105 * Check flow control window
106 *
107 * @v intf Data transfer interface
108 * @ret len Length of window
109 */
110 size_t xfer_window ( struct interface *intf ) {
111 struct interface *dest;
112 xfer_window_TYPE ( void * ) *op =
113 intf_get_dest_op ( intf, xfer_window, &dest );
114 void *object = intf_object ( dest );
115 size_t len;
116
117 if ( op ) {
118 len = op ( object );
119 } else {
120 /* Default is to provide an unlimited window */
121 len = ~( ( size_t ) 0 );
122 }
123
124 intf_put ( dest );
125 return len;
126 }
127
128 /**
129 * Report change of flow control window
130 *
131 * @v intf Data transfer interface
132 *
133 * Note that this method is used to indicate only unsolicited changes
134 * in the flow control window. In particular, this method must not be
135 * called as part of the response to xfer_deliver(), since that could
136 * easily lead to an infinite loop. Callers of xfer_deliver() should
137 * assume that the flow control window will have changed without
138 * generating an xfer_window_changed() message.
139 */
140 void xfer_window_changed ( struct interface *intf ) {
141
142 intf_poke ( intf, xfer_window_changed );
143 }
144
145 /**
146 * Allocate I/O buffer
147 *
148 * @v intf Data transfer interface
149 * @v len I/O buffer payload length
150 * @ret iobuf I/O buffer
151 */
152 struct io_buffer * xfer_alloc_iob ( struct interface *intf, size_t len ) {
153 struct interface *dest;
154 xfer_alloc_iob_TYPE ( void * ) *op =
155 intf_get_dest_op ( intf, xfer_alloc_iob, &dest );
156 void *object = intf_object ( dest );
157 struct io_buffer *iobuf;
158
159 DBGC ( INTF_COL ( intf ), "INTF " INTF_INTF_FMT " alloc_iob %zd\n",
160 INTF_INTF_DBG ( intf, dest ), len );
161
162 if ( op ) {
163 iobuf = op ( object, len );
164 } else {
165 /* Default is to allocate an I/O buffer with no
166 * reserved space.
167 */
168 iobuf = alloc_iob ( len );
169 }
170
171 if ( ! iobuf ) {
172 DBGC ( INTF_COL ( intf ), "INTF " INTF_INTF_FMT " alloc_iob "
173 "failed\n", INTF_INTF_DBG ( intf, dest ) );
174 }
175
176 intf_put ( dest );
177 return iobuf;
178 }
179
180 /**
181 * Deliver datagram
182 *
183 * @v intf Data transfer interface
184 * @v iobuf Datagram I/O buffer
185 * @v meta Data transfer metadata
186 * @ret rc Return status code
187 */
188 int xfer_deliver ( struct interface *intf,
189 struct io_buffer *iobuf,
190 struct xfer_metadata *meta ) {
191 struct interface *dest;
192 xfer_deliver_TYPE ( void * ) *op =
193 intf_get_dest_op ( intf, xfer_deliver, &dest );
194 void *object = intf_object ( dest );
195 int rc;
196
197 DBGC ( INTF_COL ( intf ), "INTF " INTF_INTF_FMT " deliver %zd\n",
198 INTF_INTF_DBG ( intf, dest ), iob_len ( iobuf ) );
199
200 if ( op ) {
201 rc = op ( object, iobuf, meta );
202 } else {
203 /* Default is to discard the I/O buffer */
204 free_iob ( iobuf );
205 rc = -EPIPE;
206 }
207
208 if ( rc != 0 ) {
209 DBGC ( INTF_COL ( intf ), "INTF " INTF_INTF_FMT
210 " deliver failed: %s\n",
211 INTF_INTF_DBG ( intf, dest ), strerror ( rc ) );
212 }
213
214 intf_put ( dest );
215 return rc;
216 }
217
218 /*****************************************************************************
219 *
220 * Data transfer interface helper functions
221 *
222 */
223
224 /**
225 * Send redirection event
226 *
227 * @v intf Data transfer interface
228 * @v type New location type
229 * @v ... Remaining arguments depend upon location type
230 * @ret rc Return status code
231 */
232 int xfer_redirect ( struct interface *intf, int type, ... ) {
233 va_list args;
234 int rc;
235
236 va_start ( args, type );
237 rc = xfer_vredirect ( intf, type, args );
238 va_end ( args );
239 return rc;
240 }
241
242 /**
243 * Deliver datagram as I/O buffer without metadata
244 *
245 * @v intf Data transfer interface
246 * @v iobuf Datagram I/O buffer
247 * @ret rc Return status code
248 */
249 int xfer_deliver_iob ( struct interface *intf, struct io_buffer *iobuf ) {
250 return xfer_deliver ( intf, iobuf, &dummy_metadata );
251 }
252
253 /**
254 * Deliver datagram as raw data
255 *
256 * @v intf Data transfer interface
257 * @v data Data
258 * @v len Length of data
259 * @v meta Data transfer metadata
260 * @ret rc Return status code
261 */
262 int xfer_deliver_raw_meta ( struct interface *intf, const void *data,
263 size_t len, struct xfer_metadata *meta ) {
264 struct io_buffer *iobuf;
265
266 iobuf = xfer_alloc_iob ( intf, len );
267 if ( ! iobuf )
268 return -ENOMEM;
269
270 memcpy ( iob_put ( iobuf, len ), data, len );
271 return xfer_deliver ( intf, iobuf, meta );
272 }
273
274 /**
275 * Deliver datagram as raw data without metadata
276 *
277 * @v intf Data transfer interface
278 * @v data Data
279 * @v len Length of data
280 * @ret rc Return status code
281 */
282 int xfer_deliver_raw ( struct interface *intf, const void *data, size_t len ) {
283 return xfer_deliver_raw_meta ( intf, data, len, &dummy_metadata );
284 }
285
286 /**
287 * Deliver formatted string
288 *
289 * @v intf Data transfer interface
290 * @v format Format string
291 * @v args Arguments corresponding to the format string
292 * @ret rc Return status code
293 */
294 int xfer_vprintf ( struct interface *intf, const char *format,
295 va_list args ) {
296 va_list args_tmp;
297 char *buf;
298 int len;
299 int rc;
300
301 /* Create temporary string */
302 va_copy ( args_tmp, args );
303 len = vasprintf ( &buf, format, args );
304 if ( len < 0 ) {
305 rc = len;
306 goto err_asprintf;
307 }
308 va_end ( args_tmp );
309
310 /* Transmit string */
311 if ( ( rc = xfer_deliver_raw ( intf, buf, len ) ) != 0 )
312 goto err_deliver;
313
314 err_deliver:
315 free ( buf );
316 err_asprintf:
317 return rc;
318 }
319
320 /**
321 * Deliver formatted string
322 *
323 * @v intf Data transfer interface
324 * @v format Format string
325 * @v ... Arguments corresponding to the format string
326 * @ret rc Return status code
327 */
328 int xfer_printf ( struct interface *intf, const char *format, ... ) {
329 va_list args;
330 int rc;
331
332 va_start ( args, format );
333 rc = xfer_vprintf ( intf, format, args );
334 va_end ( args );
335 return rc;
336 }
337
338 /**
339 * Seek to position
340 *
341 * @v intf Data transfer interface
342 * @v offset Offset to new position
343 * @ret rc Return status code
344 */
345 int xfer_seek ( struct interface *intf, off_t offset ) {
346 struct io_buffer *iobuf;
347 struct xfer_metadata meta = {
348 .flags = XFER_FL_ABS_OFFSET,
349 .offset = offset,
350 };
351
352 DBGC ( INTF_COL ( intf ), "INTF " INTF_FMT " seek to %ld\n",
353 INTF_DBG ( intf ), offset );
354
355 /* Allocate and send a zero-length data buffer */
356 iobuf = xfer_alloc_iob ( intf, 0 );
357 if ( ! iobuf )
358 return -ENOMEM;
359
360 return xfer_deliver ( intf, iobuf, &meta );
361 }
362
363 /**
364 * Check that data is delivered strictly in order
365 *
366 * @v meta Data transfer metadata
367 * @v pos Current position
368 * @v len Length of data
369 * @ret rc Return status code
370 */
371 int xfer_check_order ( struct xfer_metadata *meta, size_t *pos, size_t len ) {
372 size_t new_pos;
373
374 /* Allow out-of-order zero-length packets (as used by xfer_seek()) */
375 if ( len == 0 )
376 return 0;
377
378 /* Calculate position of this delivery */
379 new_pos = *pos;
380 if ( meta->flags & XFER_FL_ABS_OFFSET )
381 new_pos = 0;
382 new_pos += meta->offset;
383
384 /* Fail if delivery position is not equal to current position */
385 if ( new_pos != *pos )
386 return -EPROTO;
387
388 /* Update current position */
389 *pos += len;
390
391 return 0;
392 }