/**
******************************************************************************
* @file LwIP/LwIP_IAP/Src/tftpserver.c
* @author MCD Application Team
* @brief basic tftp server implementation for IAP (only Write Req supported)
******************************************************************************
* @attention
*
*
© Copyright (c) 2017 STMicroelectronics International N.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted, provided that the following conditions are met:
*
* 1. Redistribution of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of other
* contributors to this software may be used to endorse or promote products
* derived from this software without specific written permission.
* 4. This software, including modifications and/or derivative works of this
* software, must execute solely and exclusively on microcontroller or
* microprocessor devices manufactured by or for STMicroelectronics.
* 5. Redistribution and use of this software other than as permitted under
* this license is void and will automatically terminate your rights under
* this license.
*
* THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY
* RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT
* SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "tftpserver.h"
#include "flash_if.h"
#include
#include
#include "main.h"
#include "crc.h"
/* Private variables ---------------------------------------------------------*/
static uint32_t Flash_Write_Address;
static struct udp_pcb *UDPpcb;
static __IO uint32_t total_count=0;
/* Private function prototypes -----------------------------------------------*/
static void IAP_wrq_recv_callback(void *_args, struct udp_pcb *upcb, struct pbuf *pkt_buf,
const ip_addr_t *addr, u16_t port);
static int IAP_tftp_process_write(struct udp_pcb *upcb, const ip_addr_t *to, int to_port);
static void IAP_tftp_recv_callback(void *arg, struct udp_pcb *Upcb, struct pbuf *pkt_buf,
const ip_addr_t *addr, u16_t port);
static void IAP_tftp_cleanup_wr(struct udp_pcb *upcb, tftp_connection_args *args);
static tftp_opcode IAP_tftp_decode_op(char *buf);
static u16_t IAP_tftp_extract_block(char *buf);
static void IAP_tftp_set_opcode(char *buffer, tftp_opcode opcode);
static void IAP_tftp_set_block(char* packet, u16_t block);
static err_t IAP_tftp_send_ack_packet(struct udp_pcb *upcb, const ip_addr_t *to, int to_port, int block);
/* Private functions ---------------------------------------------------------*/
/**
* @brief Returns the TFTP opcode
* @param buf: pointer on the TFTP packet
* @retval None
*/
static tftp_opcode IAP_tftp_decode_op(char *buf)
{
return (tftp_opcode)(buf[1]);
}
/**
* @brief Extracts the block number
* @param buf: pointer on the TFTP packet
* @retval block number
*/
static u16_t IAP_tftp_extract_block(char *buf)
{
u16_t *b = (u16_t*)buf;
return ntohs(b[1]);
}
/**
* @brief Sets the TFTP opcode
* @param buffer: pointer on the TFTP packet
* @param opcode: TFTP opcode
* @retval None
*/
static void IAP_tftp_set_opcode(char *buffer, tftp_opcode opcode)
{
buffer[0] = 0;
buffer[1] = (u8_t)opcode;
}
/**
* @brief Sets the TFTP block number
* @param packet: pointer on the TFTP packet
* @param block: block number
* @retval None
*/
static void IAP_tftp_set_block(char* packet, u16_t block)
{
u16_t *p = (u16_t *)packet;
p[1] = htons(block);
}
/**
* @brief Sends TFTP ACK packet
* @param upcb: pointer on udp_pcb structure
* @param to: pointer on the receive IP address structure
* @param to_port: receive port number
* @param block: block number
* @retval: err_t: error code
*/
static err_t IAP_tftp_send_ack_packet(struct udp_pcb *upcb, const ip_addr_t *to, int to_port, int block)
{
err_t err;
struct pbuf *pkt_buf; /* Chain of pbuf's to be sent */
/* create the maximum possible size packet that a TFTP ACK packet can be */
char packet[TFTP_ACK_PKT_LEN];
memset(packet, 0, TFTP_ACK_PKT_LEN *sizeof(char));
/* define the first two bytes of the packet */
IAP_tftp_set_opcode(packet, TFTP_ACK);
/* Specify the block number being ACK'd.
* If we are ACK'ing a DATA pkt then the block number echoes that of the DATA pkt being ACK'd (duh)
* If we are ACK'ing a WRQ pkt then the block number is always 0
* RRQ packets are never sent ACK pkts by the server, instead the server sends DATA pkts to the
* host which are, obviously, used as the "acknowledgement". This saves from having to sEndTransferboth
* an ACK packet and a DATA packet for RRQs - see RFC1350 for more info. */
IAP_tftp_set_block(packet, block);
/* PBUF_TRANSPORT - specifies the transport layer */
pkt_buf = pbuf_alloc(PBUF_TRANSPORT, TFTP_ACK_PKT_LEN, PBUF_POOL);
if (!pkt_buf) /*if the packet pbuf == NULL exit and EndTransfertransmission */
{
DEBUG_ERROR("Can not allocate pbuf\r\n");
return ERR_MEM;
}
/* Copy the original data buffer over to the packet buffer's payload */
memcpy(pkt_buf->payload, packet, TFTP_ACK_PKT_LEN);
/* Sending packet by UDP protocol */
err = udp_sendto(upcb, pkt_buf, to, to_port);
/* free the buffer pbuf */
pbuf_free(pkt_buf);
return err;
}
/**
* @brief Processes data transfers after a TFTP write request
* @param _args: used as pointer on TFTP connection args
* @param upcb: pointer on udp_pcb structure
* @param pkt_buf: pointer on a pbuf stucture
* @param ip_addr: pointer on the receive IP_address structure
* @param port: receive port address
* @retval None
*/
static void IAP_wrq_recv_callback(void *_args, struct udp_pcb *upcb, struct pbuf *pkt_buf, const ip_addr_t *addr, u16_t port)
{
tftp_connection_args *args = (tftp_connection_args *)_args;
uint32_t data_buffer[128];
uint16_t count=0;
if (pkt_buf->len != pkt_buf->tot_len)
{
DEBUG_ERROR("Invalid data length\r\n");
return;
}
/* Does this packet have any valid data to write? */
if ((pkt_buf->len > TFTP_DATA_PKT_HDR_LEN) &&
(IAP_tftp_extract_block(pkt_buf->payload) == (args->block + 1)))
{
/* copy packet payload to data_buffer */
pbuf_copy_partial(pkt_buf, data_buffer, pkt_buf->len - TFTP_DATA_PKT_HDR_LEN,
TFTP_DATA_PKT_HDR_LEN);
total_count += pkt_buf->len - TFTP_DATA_PKT_HDR_LEN;
count = (pkt_buf->len - TFTP_DATA_PKT_HDR_LEN)/4;
if (((pkt_buf->len - TFTP_DATA_PKT_HDR_LEN)%4)!=0)
count++;
/* Write received data in Flash */
FLASH_If_Write(Flash_Write_Address, data_buffer ,count);
Flash_Write_Address += count*4;
/* update our block number to match the block number just received */
args->block++;
/* update total bytes */
(args->tot_bytes) += (pkt_buf->len - TFTP_DATA_PKT_HDR_LEN);
/* This is a valid pkt but it has no data. This would occur if the file being
written is an exact multiple of 512 bytes. In this case, the args->block
value must still be updated, but we can skip everything else. */
}
else if (IAP_tftp_extract_block(pkt_buf->payload) == (args->block + 1))
{
/* update our block number to match the block number just received */
args->block++;
}
/* Send the appropriate ACK pkt*/
IAP_tftp_send_ack_packet(upcb, addr, port, args->block);
/* If the last write returned less than the maximum TFTP data pkt length,
* then we've received the whole file and so we can quit (this is how TFTP
* signals the EndTransferof a transfer!)
*/
if (pkt_buf->len < TFTP_DATA_PKT_LEN_MAX)
{
uint8_t endFlag[4]={0x55,0xaa,0x55,0xaa};
IAP_tftp_cleanup_wr(upcb, args);
pbuf_free(pkt_buf);
DEBUG_INFO("Total bytes Received: %d byte\r\n", total_count);
DEBUG_INFO("State: Prog Finished \r\n");
DEBUG_INFO("Reset the board \r\n");
Flash_Write_Address = NEW_CODE_ADDRESS;
uint32_t checksum = HAL_CRC_Calculate(&hcrc, (uint32_t *)Flash_Write_Address, ((FLASH_AP_LENGTH-4)>>2));
Flash_Write_Address = ((uint32_t)(NEW_CODE_ADDRESS+FLASH_AP_LENGTH-4));
#if defined(DEBUG) || defined(RTOS_STAT)
DEBUG_INFO("Firmware transfer end, AP CRC checksum, flash: 0x%x, 0x%x\r\n", checksum, *((uint32_t *)Flash_Write_Address) );
#endif
if(checksum == *((uint32_t *)Flash_Write_Address))
{
if (FLASH_If_Write(UPGRADE_REQ_ADDRESS, (uint32_t *)&endFlag[0], 1) == FLASHIF_OK)
{
#if defined(DEBUG) || defined(RTOS_STAT)
DEBUG_INFO("Firmware Confirm Tag write ok..\n\r");
#endif
NVIC_SystemReset();
}
else
{
#if defined(DEBUG) || defined(RTOS_STAT)
DEBUG_INFO("Firmware Confirm Tag write fail...\n\r");
#endif
}
}
}
else
{
pbuf_free(pkt_buf);
return;
}
}
/**
* @brief Processes TFTP write request
* @param to: pointer on the receive IP address
* @param to_port: receive port number
* @retval None
*/
static int IAP_tftp_process_write(struct udp_pcb *upcb, const ip_addr_t *to, int to_port)
{
tftp_connection_args *args = NULL;
/* This function is called from a callback,
* therefore interrupts are disabled,
* therefore we can use regular malloc */
args = mem_malloc(sizeof *args);
if (!args)
{
DEBUG_ERROR("Memory error \r\n");
IAP_tftp_cleanup_wr(upcb, args);
return 0;
}
args->op = TFTP_WRQ;
args->to_ip.addr = to->addr;
args->to_port = to_port;
/* the block # used as a positive response to a WRQ is _always_ 0!!! (see RFC1350) */
args->block = 0;
args->tot_bytes = 0;
/* set callback for receives on this UDP PCB (Protocol Control Block) */
udp_recv(upcb, IAP_wrq_recv_callback, args);
total_count =0;
/* init flash */
FLASH_If_Init();
/* erase user flash area */
FLASH_If_Erase(NEW_CODE_ADDRESS, 3);
Flash_Write_Address = NEW_CODE_ADDRESS;
/* initiate the write transaction by sending the first ack */
IAP_tftp_send_ack_packet(upcb, to, to_port, args->block);
DEBUG_INFO("State: Programming... \r\n");
return 0;
}
/**
* @brief Processes traffic received on UDP port 69
* @param args: pointer on tftp_connection arguments
* @param upcb: pointer on udp_pcb structure
* @param pbuf: pointer on packet buffer
* @param addr: pointer on the receive IP address
* @param port: receive port number
* @retval None
*/
static void IAP_tftp_recv_callback(void *arg, struct udp_pcb *upcb, struct pbuf *pkt_buf,
const ip_addr_t *addr, u16_t port)
{
tftp_opcode op;
struct udp_pcb *upcb_tftp_data;
err_t err;
uint32_t i;
char filename[40],message[46], *ptr;
/* create new UDP PCB structure */
upcb_tftp_data = udp_new();
if (!upcb_tftp_data)
{
/* Error creating PCB. Out of Memory */
DEBUG_ERROR("Can not create pcb \r\n");
return;
}
/* bind to port 0 to receive next available free port */
/* NOTE: This is how TFTP works. There is a UDP PCB for the standard port
* 69 which al transactions begin communication on, however, _all_ subsequent
* transactions for a given "stream" occur on another port */
err = udp_bind(upcb_tftp_data, IP_ADDR_ANY, 0);
if (err != ERR_OK)
{
/* Unable to bind to port */
DEBUG_ERROR("Can not create pcb \r\n");
return;
}
op = IAP_tftp_decode_op(pkt_buf->payload);
if (op != TFTP_WRQ)
{
/* remove PCB */
DEBUG_ERROR("Bad TFTP opcode \r\n");
udp_remove(upcb_tftp_data);
}
else
{
ptr = pkt_buf->payload;
ptr = ptr +2;
/*extract file name info */
i= 0;
while (*(ptr+i)!=0x0)
{
i++;
}
strncpy(filename, ptr, i+1);
DEBUG_INFO("IAP using TFTP \r\n");
sprintf(message, "File: %s",filename);
DEBUG_INFO("%s\r\n", message);
DEBUG_INFO("State: Erasing...\r\n");
/* Start the TFTP write mode*/
IAP_tftp_process_write(upcb_tftp_data, addr, port);
}
pbuf_free(pkt_buf);
}
/**
* @brief disconnect and close the connection
* @param upcb: pointer on udp_pcb structure
* @param args: pointer on tftp_connection arguments
* @retval None
*/
static void IAP_tftp_cleanup_wr(struct udp_pcb *upcb, tftp_connection_args *args)
{
/* Free the tftp_connection_args structure */
mem_free(args);
/* Disconnect the udp_pcb */
udp_disconnect(upcb);
/* close the connection */
udp_remove(upcb);
/* reset the callback function */
udp_recv(UDPpcb, IAP_tftp_recv_callback, NULL);
}
/* Global functions ---------------------------------------------------------*/
/**
* @brief Creates and initializes a UDP PCB for TFTP receive operation
* @param None
* @retval None
*/
void IAP_tftpd_init(void)
{
err_t err;
unsigned port = 69; /* 69 is the port used for TFTP protocol initial transaction */
/* create a new UDP PCB structure */
UDPpcb = udp_new();
if (!UDPpcb)
{
/* Error creating PCB. Out of Memory */
DEBUG_ERROR("Can not create pcb \r\n");
return;
}
/* Bind this PCB to port 69 */
err = udp_bind(UDPpcb, IP_ADDR_ANY, port);
if (err == ERR_OK)
{
/* Initialize receive callback function */
udp_recv(UDPpcb, IAP_tftp_recv_callback, NULL);
}
else
{
DEBUG_ERROR("Can not create pcb \r\n");
}
}
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/