diff options
author | 2016-06-03 03:33:22 +0000 | |
---|---|---|
committer | 2016-06-03 03:33:23 +0000 | |
commit | da27230f80795d0028333713f036d44c53cb0e68 (patch) | |
tree | b3d379eaf000adf72b36cb01cdf4d79c3e3f064c /qemu/roms/ipxe/src/net/udp | |
parent | 0e68cb048bb8aadb14675f5d4286d8ab2fc35449 (diff) | |
parent | 437fd90c0250dee670290f9b714253671a990160 (diff) |
Merge "These changes are the raw update to qemu-2.6."
Diffstat (limited to 'qemu/roms/ipxe/src/net/udp')
-rw-r--r-- | qemu/roms/ipxe/src/net/udp/dhcp.c | 133 | ||||
-rw-r--r-- | qemu/roms/ipxe/src/net/udp/dhcpv6.c | 6 | ||||
-rw-r--r-- | qemu/roms/ipxe/src/net/udp/dns.c | 6 | ||||
-rw-r--r-- | qemu/roms/ipxe/src/net/udp/slam.c | 6 | ||||
-rw-r--r-- | qemu/roms/ipxe/src/net/udp/syslog.c | 6 | ||||
-rw-r--r-- | qemu/roms/ipxe/src/net/udp/tftp.c | 60 |
6 files changed, 133 insertions, 84 deletions
diff --git a/qemu/roms/ipxe/src/net/udp/dhcp.c b/qemu/roms/ipxe/src/net/udp/dhcp.c index 04fad04c2..aed5ee360 100644 --- a/qemu/roms/ipxe/src/net/udp/dhcp.c +++ b/qemu/roms/ipxe/src/net/udp/dhcp.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <string.h> #include <stdlib.h> @@ -44,6 +48,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <ipxe/dhcppkt.h> #include <ipxe/dhcp_arch.h> #include <ipxe/features.h> +#include <config/dhcp.h> /** @file * @@ -149,30 +154,32 @@ struct dhcp_session_state { * @v dhcppkt DHCP packet * @v peer Destination address */ - int ( * tx ) ( struct dhcp_session *dhcp, - struct dhcp_packet *dhcppkt, + int ( * tx ) ( struct dhcp_session *dhcp, struct dhcp_packet *dhcppkt, struct sockaddr_in *peer ); - /** Handle received packet + /** + * Handle received packet * * @v dhcp DHCP session * @v dhcppkt DHCP packet * @v peer DHCP server address * @v msgtype DHCP message type * @v server_id DHCP server ID + * @v pseudo_id DHCP server pseudo-ID */ - void ( * rx ) ( struct dhcp_session *dhcp, - struct dhcp_packet *dhcppkt, - struct sockaddr_in *peer, - uint8_t msgtype, struct in_addr server_id ); - /** Handle timer expiry + void ( * rx ) ( struct dhcp_session *dhcp, struct dhcp_packet *dhcppkt, + struct sockaddr_in *peer, uint8_t msgtype, + struct in_addr server_id, struct in_addr pseudo_id ); + /** + * Handle timer expiry * * @v dhcp DHCP session */ void ( * expired ) ( struct dhcp_session *dhcp ); /** Transmitted message type */ uint8_t tx_msgtype; - /** Apply minimum timeout */ - uint8_t apply_min_timeout; + /** Timeout parameters */ + uint8_t min_timeout_sec; + uint8_t max_timeout_sec; }; static struct dhcp_session_state dhcp_state_discover; @@ -272,9 +279,9 @@ static void dhcp_set_state ( struct dhcp_session *dhcp, dhcp->state = state; dhcp->start = currticks(); stop_timer ( &dhcp->timer ); - dhcp->timer.min_timeout = - ( state->apply_min_timeout ? DHCP_MIN_TIMEOUT : 0 ); - dhcp->timer.max_timeout = DHCP_MAX_TIMEOUT; + set_timer_limits ( &dhcp->timer, + ( state->min_timeout_sec * TICKS_PER_SEC ), + ( state->max_timeout_sec * TICKS_PER_SEC ) ); start_timer_nodelay ( &dhcp->timer ); } @@ -334,11 +341,13 @@ static int dhcp_discovery_tx ( struct dhcp_session *dhcp, * @v peer DHCP server address * @v msgtype DHCP message type * @v server_id DHCP server ID + * @v pseudo_id DHCP server pseudo-ID */ static void dhcp_discovery_rx ( struct dhcp_session *dhcp, struct dhcp_packet *dhcppkt, struct sockaddr_in *peer, uint8_t msgtype, - struct in_addr server_id ) { + struct in_addr server_id, + struct in_addr pseudo_id ) { struct in_addr ip; char vci[9]; /* "PXEClient" */ int vci_len; @@ -350,8 +359,11 @@ static void dhcp_discovery_rx ( struct dhcp_session *dhcp, DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp, dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ), ntohs ( peer->sin_port ) ); - if ( server_id.s_addr != peer->sin_addr.s_addr ) - DBGC ( dhcp, " (%s)", inet_ntoa ( server_id ) ); + if ( ( server_id.s_addr != peer->sin_addr.s_addr ) || + ( pseudo_id.s_addr != peer->sin_addr.s_addr ) ) { + DBGC ( dhcp, " (%s/", inet_ntoa ( server_id ) ); + DBGC ( dhcp, "%s)", inet_ntoa ( pseudo_id ) ); + } /* Identify offered IP address */ ip = dhcppkt->dhcphdr->yiaddr; @@ -392,10 +404,10 @@ static void dhcp_discovery_rx ( struct dhcp_session *dhcp, } /* Select as ProxyDHCP offer, if applicable */ - if ( server_id.s_addr && has_pxeclient && + if ( pseudo_id.s_addr && has_pxeclient && ( priority >= dhcp->proxy_priority ) ) { dhcppkt_put ( dhcp->proxy_offer ); - dhcp->proxy_server = server_id; + dhcp->proxy_server = pseudo_id; dhcp->proxy_offer = dhcppkt_get ( dhcppkt ); dhcp->proxy_priority = priority; } @@ -415,7 +427,7 @@ static void dhcp_discovery_rx ( struct dhcp_session *dhcp, /* If we can't yet transition to DHCPREQUEST, do nothing */ elapsed = ( currticks() - dhcp->start ); if ( ! ( dhcp->no_pxedhcp || dhcp->proxy_offer || - ( elapsed > PROXYDHCP_MAX_TIMEOUT ) ) ) + ( elapsed > DHCP_DISC_PROXY_TIMEOUT_SEC * TICKS_PER_SEC ) ) ) return; /* Transition to DHCPREQUEST */ @@ -430,8 +442,18 @@ static void dhcp_discovery_rx ( struct dhcp_session *dhcp, static void dhcp_discovery_expired ( struct dhcp_session *dhcp ) { unsigned long elapsed = ( currticks() - dhcp->start ); + /* If link is blocked, defer DHCP discovery (and reset timeout) */ + if ( netdev_link_blocked ( dhcp->netdev ) ) { + DBGC ( dhcp, "DHCP %p deferring discovery\n", dhcp ); + start_timer_fixed ( &dhcp->timer, + ( DHCP_DISC_START_TIMEOUT_SEC * + TICKS_PER_SEC ) ); + return; + } + /* Give up waiting for ProxyDHCP before we reach the failure point */ - if ( dhcp->offer.s_addr && ( elapsed > PROXYDHCP_MAX_TIMEOUT ) ) { + if ( dhcp->offer.s_addr && + ( elapsed > DHCP_DISC_PROXY_TIMEOUT_SEC * TICKS_PER_SEC ) ) { dhcp_set_state ( dhcp, &dhcp_state_request ); return; } @@ -447,7 +469,8 @@ static struct dhcp_session_state dhcp_state_discover = { .rx = dhcp_discovery_rx, .expired = dhcp_discovery_expired, .tx_msgtype = DHCPDISCOVER, - .apply_min_timeout = 1, + .min_timeout_sec = DHCP_DISC_START_TIMEOUT_SEC, + .max_timeout_sec = DHCP_DISC_END_TIMEOUT_SEC, }; /** @@ -493,11 +516,13 @@ static int dhcp_request_tx ( struct dhcp_session *dhcp, * @v peer DHCP server address * @v msgtype DHCP message type * @v server_id DHCP server ID + * @v pseudo_id DHCP server pseudo-ID */ static void dhcp_request_rx ( struct dhcp_session *dhcp, struct dhcp_packet *dhcppkt, struct sockaddr_in *peer, uint8_t msgtype, - struct in_addr server_id ) { + struct in_addr server_id, + struct in_addr pseudo_id ) { struct in_addr ip; struct settings *parent; struct settings *settings; @@ -506,8 +531,11 @@ static void dhcp_request_rx ( struct dhcp_session *dhcp, DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp, dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ), ntohs ( peer->sin_port ) ); - if ( server_id.s_addr != peer->sin_addr.s_addr ) - DBGC ( dhcp, " (%s)", inet_ntoa ( server_id ) ); + if ( ( server_id.s_addr != peer->sin_addr.s_addr ) || + ( pseudo_id.s_addr != peer->sin_addr.s_addr ) ) { + DBGC ( dhcp, " (%s/", inet_ntoa ( server_id ) ); + DBGC ( dhcp, "%s)", inet_ntoa ( pseudo_id ) ); + } /* Identify leased IP address */ ip = dhcppkt->dhcphdr->yiaddr; @@ -584,7 +612,8 @@ static struct dhcp_session_state dhcp_state_request = { .rx = dhcp_request_rx, .expired = dhcp_request_expired, .tx_msgtype = DHCPREQUEST, - .apply_min_timeout = 0, + .min_timeout_sec = DHCP_REQ_START_TIMEOUT_SEC, + .max_timeout_sec = DHCP_REQ_END_TIMEOUT_SEC, }; /** @@ -623,19 +652,26 @@ static int dhcp_proxy_tx ( struct dhcp_session *dhcp, * @v peer DHCP server address * @v msgtype DHCP message type * @v server_id DHCP server ID + * @v pseudo_id DHCP server pseudo-ID */ static void dhcp_proxy_rx ( struct dhcp_session *dhcp, struct dhcp_packet *dhcppkt, struct sockaddr_in *peer, uint8_t msgtype, - struct in_addr server_id ) { + struct in_addr server_id, + struct in_addr pseudo_id ) { struct settings *settings = &dhcppkt->settings; int rc; DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp, dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ), ntohs ( peer->sin_port ) ); - if ( server_id.s_addr != peer->sin_addr.s_addr ) - DBGC ( dhcp, " (%s)", inet_ntoa ( server_id ) ); + if ( ( server_id.s_addr != peer->sin_addr.s_addr ) || + ( pseudo_id.s_addr != peer->sin_addr.s_addr ) ) { + DBGC ( dhcp, " (%s/", inet_ntoa ( server_id ) ); + DBGC ( dhcp, "%s)", inet_ntoa ( pseudo_id ) ); + } + if ( dhcp_has_pxeopts ( dhcppkt ) ) + DBGC ( dhcp, " pxe" ); DBGC ( dhcp, "\n" ); /* Filter out unacceptable responses */ @@ -643,8 +679,9 @@ static void dhcp_proxy_rx ( struct dhcp_session *dhcp, return; if ( ( msgtype != DHCPOFFER ) && ( msgtype != DHCPACK ) ) return; - if ( server_id.s_addr /* Linux PXE server omits server ID */ && - ( server_id.s_addr != dhcp->proxy_server.s_addr ) ) + if ( ( pseudo_id.s_addr != dhcp->proxy_server.s_addr ) ) + return; + if ( ! dhcp_has_pxeopts ( dhcppkt ) ) return; /* Register settings */ @@ -669,7 +706,7 @@ static void dhcp_proxy_expired ( struct dhcp_session *dhcp ) { unsigned long elapsed = ( currticks() - dhcp->start ); /* Give up waiting for ProxyDHCP before we reach the failure point */ - if ( elapsed > PROXYDHCP_MAX_TIMEOUT ) { + if ( elapsed > DHCP_REQ_PROXY_TIMEOUT_SEC * TICKS_PER_SEC ) { dhcp_finished ( dhcp, 0 ); return; } @@ -685,7 +722,8 @@ static struct dhcp_session_state dhcp_state_proxy = { .rx = dhcp_proxy_rx, .expired = dhcp_proxy_expired, .tx_msgtype = DHCPREQUEST, - .apply_min_timeout = 0, + .min_timeout_sec = DHCP_PROXY_START_TIMEOUT_SEC, + .max_timeout_sec = DHCP_PROXY_END_TIMEOUT_SEC, }; /** @@ -753,19 +791,24 @@ static int dhcp_pxebs_accept ( struct dhcp_session *dhcp, * @v peer DHCP server address * @v msgtype DHCP message type * @v server_id DHCP server ID + * @v pseudo_id DHCP server pseudo-ID */ static void dhcp_pxebs_rx ( struct dhcp_session *dhcp, struct dhcp_packet *dhcppkt, struct sockaddr_in *peer, uint8_t msgtype, - struct in_addr server_id ) { + struct in_addr server_id, + struct in_addr pseudo_id ) { struct dhcp_pxe_boot_menu_item menu_item = { 0, 0 }; int rc; DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp, dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ), ntohs ( peer->sin_port ) ); - if ( server_id.s_addr != peer->sin_addr.s_addr ) - DBGC ( dhcp, " (%s)", inet_ntoa ( server_id ) ); + if ( ( server_id.s_addr != peer->sin_addr.s_addr ) || + ( pseudo_id.s_addr != peer->sin_addr.s_addr ) ) { + DBGC ( dhcp, " (%s/", inet_ntoa ( server_id ) ); + DBGC ( dhcp, "%s)", inet_ntoa ( pseudo_id ) ); + } /* Identify boot menu item */ dhcppkt_fetch ( dhcppkt, DHCP_PXE_BOOT_MENU_ITEM, @@ -782,8 +825,7 @@ static void dhcp_pxebs_rx ( struct dhcp_session *dhcp, return; if ( menu_item.type != dhcp->pxe_type ) return; - if ( ! dhcp_pxebs_accept ( dhcp, ( server_id.s_addr ? - server_id : peer->sin_addr ) ) ) + if ( ! dhcp_pxebs_accept ( dhcp, pseudo_id ) ) return; /* Register settings */ @@ -810,7 +852,7 @@ static void dhcp_pxebs_expired ( struct dhcp_session *dhcp ) { /* Give up waiting before we reach the failure point, and fail * over to the next server in the attempt list */ - if ( elapsed > PXEBS_MAX_TIMEOUT ) { + if ( elapsed > PXEBS_MAX_TIMEOUT_SEC * TICKS_PER_SEC ) { dhcp->pxe_attempt++; if ( dhcp->pxe_attempt->s_addr ) { dhcp_set_state ( dhcp, &dhcp_state_pxebs ); @@ -832,7 +874,8 @@ static struct dhcp_session_state dhcp_state_pxebs = { .rx = dhcp_pxebs_rx, .expired = dhcp_pxebs_expired, .tx_msgtype = DHCPREQUEST, - .apply_min_timeout = 1, + .min_timeout_sec = PXEBS_START_TIMEOUT_SEC, + .max_timeout_sec = PXEBS_END_TIMEOUT_SEC, }; /**************************************************************************** @@ -1114,6 +1157,7 @@ static int dhcp_deliver ( struct dhcp_session *dhcp, struct dhcphdr *dhcphdr; uint8_t msgtype = 0; struct in_addr server_id = { 0 }; + struct in_addr pseudo_id; int rc = 0; /* Sanity checks */ @@ -1148,6 +1192,13 @@ static int dhcp_deliver ( struct dhcp_session *dhcp, dhcppkt_fetch ( dhcppkt, DHCP_SERVER_IDENTIFIER, &server_id, sizeof ( server_id ) ); + /* Identify server pseudo-ID */ + pseudo_id = server_id; + if ( ! pseudo_id.s_addr ) + pseudo_id = dhcppkt->dhcphdr->siaddr; + if ( ! pseudo_id.s_addr ) + pseudo_id = peer->sin_addr; + /* Check for matching transaction ID */ if ( dhcphdr->xid != dhcp->xid ) { DBGC ( dhcp, "DHCP %p %s from %s:%d has bad transaction " @@ -1170,7 +1221,7 @@ static int dhcp_deliver ( struct dhcp_session *dhcp, } /* Handle packet based on current state */ - dhcp->state->rx ( dhcp, dhcppkt, peer, msgtype, server_id ); + dhcp->state->rx ( dhcp, dhcppkt, peer, msgtype, server_id, pseudo_id ); err_chaddr: err_xid: diff --git a/qemu/roms/ipxe/src/net/udp/dhcpv6.c b/qemu/roms/ipxe/src/net/udp/dhcpv6.c index f7736d08e..a63543775 100644 --- a/qemu/roms/ipxe/src/net/udp/dhcpv6.c +++ b/qemu/roms/ipxe/src/net/udp/dhcpv6.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdlib.h> #include <stdio.h> diff --git a/qemu/roms/ipxe/src/net/udp/dns.c b/qemu/roms/ipxe/src/net/udp/dns.c index fffe6e697..2d77477f6 100644 --- a/qemu/roms/ipxe/src/net/udp/dns.c +++ b/qemu/roms/ipxe/src/net/udp/dns.c @@ -18,9 +18,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdlib.h> diff --git a/qemu/roms/ipxe/src/net/udp/slam.c b/qemu/roms/ipxe/src/net/udp/slam.c index 3cb492d73..8b26bfb3c 100644 --- a/qemu/roms/ipxe/src/net/udp/slam.c +++ b/qemu/roms/ipxe/src/net/udp/slam.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdlib.h> diff --git a/qemu/roms/ipxe/src/net/udp/syslog.c b/qemu/roms/ipxe/src/net/udp/syslog.c index d65d19ab8..b6eee6036 100644 --- a/qemu/roms/ipxe/src/net/udp/syslog.c +++ b/qemu/roms/ipxe/src/net/udp/syslog.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/qemu/roms/ipxe/src/net/udp/tftp.c b/qemu/roms/ipxe/src/net/udp/tftp.c index ee827ae3d..953bcb46a 100644 --- a/qemu/roms/ipxe/src/net/udp/tftp.c +++ b/qemu/roms/ipxe/src/net/udp/tftp.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdlib.h> @@ -149,8 +153,6 @@ enum { TFTP_FL_RRQ_MULTICAST = 0x0004, /** Perform MTFTP recovery on timeout */ TFTP_FL_MTFTP_RECOVERY = 0x0008, - /** Only get filesize and then abort the transfer */ - TFTP_FL_SIZEONLY = 0x0010, }; /** Maximum number of MTFTP open requests before falling back to TFTP */ @@ -759,14 +761,6 @@ static int tftp_rx_oack ( struct tftp_request *tftp, void *buf, size_t len ) { goto done; } - /* Abort request if only trying to determine file size */ - if ( tftp->flags & TFTP_FL_SIZEONLY ) { - rc = 0; - tftp_send_error ( tftp, 0, "TFTP Aborted" ); - tftp_done ( tftp, rc ); - return rc; - } - /* Request next data block */ tftp_send_packet ( tftp ); @@ -794,13 +788,6 @@ static int tftp_rx_data ( struct tftp_request *tftp, size_t data_len; int rc; - if ( tftp->flags & TFTP_FL_SIZEONLY ) { - /* If we get here then server doesn't support SIZE option */ - rc = -ENOTSUP; - tftp_send_error ( tftp, 0, "TFTP Aborted" ); - goto done; - } - /* Sanity check */ if ( iob_len ( iobuf ) < sizeof ( *data ) ) { DBGC ( tftp, "TFTP %p received underlength DATA packet " @@ -1036,10 +1023,25 @@ static size_t tftp_xfer_window ( struct tftp_request *tftp ) { return tftp->blksize; } +/** + * Terminate download + * + * @v tftp TFTP connection + * @v rc Reason for close + */ +static void tftp_close ( struct tftp_request *tftp, int rc ) { + + /* Abort download */ + tftp_send_error ( tftp, 0, "TFTP Aborted" ); + + /* Close TFTP request */ + tftp_done ( tftp, rc ); +} + /** TFTP data transfer interface operations */ static struct interface_operation tftp_xfer_operations[] = { INTF_OP ( xfer_window, struct tftp_request *, tftp_xfer_window ), - INTF_OP ( intf_close, struct tftp_request *, tftp_done ), + INTF_OP ( intf_close, struct tftp_request *, tftp_close ), }; /** TFTP data transfer interface descriptor */ @@ -1126,26 +1128,6 @@ struct uri_opener tftp_uri_opener __uri_opener = { }; /** - * Initiate TFTP-size request - * - * @v xfer Data transfer interface - * @v uri Uniform Resource Identifier - * @ret rc Return status code - */ -static int tftpsize_open ( struct interface *xfer, struct uri *uri ) { - return tftp_core_open ( xfer, uri, TFTP_PORT, NULL, - ( TFTP_FL_RRQ_SIZES | - TFTP_FL_SIZEONLY ) ); - -} - -/** TFTP URI opener */ -struct uri_opener tftpsize_uri_opener __uri_opener = { - .scheme = "tftpsize", - .open = tftpsize_open, -}; - -/** * Initiate TFTM download * * @v xfer Data transfer interface |