2019-04-30 17:27:23 +01:00
|
|
|
/**
|
|
|
|
******************************************************************************
|
|
|
|
* @file LwIP/LwIP_IAP/Src/tftpserver.c
|
|
|
|
* @author MCD Application Team
|
|
|
|
* @brief basic tftp server implementation for IAP (only Write Req supported)
|
|
|
|
******************************************************************************
|
|
|
|
* @attention
|
|
|
|
*
|
2021-03-03 14:55:52 +01:00
|
|
|
* <h2><center>© Copyright (c) 2017 STMicroelectronics.
|
2019-04-30 17:27:23 +01:00
|
|
|
* All rights reserved.</center></h2>
|
|
|
|
*
|
2021-03-03 14:55:52 +01:00
|
|
|
* This software component is licensed by ST under Ultimate Liberty license
|
|
|
|
* SLA0044, the "License"; You may not use this file except in compliance with
|
|
|
|
* the License. You may obtain a copy of the License at:
|
|
|
|
* www.st.com/SLA0044
|
2019-04-30 17:27:23 +01:00
|
|
|
*
|
|
|
|
******************************************************************************
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Includes ------------------------------------------------------------------*/
|
|
|
|
#include "tftpserver.h"
|
|
|
|
#include "flash_if.h"
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include "main.h"
|
|
|
|
#include "lcd_log.h"
|
|
|
|
|
|
|
|
#ifdef USE_IAP_TFTP
|
|
|
|
|
|
|
|
/* 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 */
|
|
|
|
{
|
|
|
|
#ifdef USE_LCD
|
|
|
|
LCD_ErrLog("Can not allocate pbuf\n");
|
|
|
|
#endif
|
|
|
|
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;
|
|
|
|
|
|
|
|
#ifdef USE_LCD
|
|
|
|
char message[40];
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (pkt_buf->len != pkt_buf->tot_len)
|
|
|
|
{
|
|
|
|
#ifdef USE_LCD
|
|
|
|
LCD_ErrLog("Invalid data length\n");
|
|
|
|
#endif
|
|
|
|
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);
|
|
|
|
|
|
|
|
/* 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)
|
|
|
|
{
|
|
|
|
IAP_tftp_cleanup_wr(upcb, args);
|
|
|
|
pbuf_free(pkt_buf);
|
|
|
|
|
|
|
|
#ifdef USE_LCD
|
|
|
|
sprintf(message, "%d bytes ",(int)total_count);
|
|
|
|
LCD_UsrLog("Tot bytes Received:, %s\n", message);
|
|
|
|
LCD_UsrLog(" State: Prog Finished \n");
|
|
|
|
LCD_UsrLog("Reset the board \n");
|
|
|
|
#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)
|
|
|
|
{
|
|
|
|
#ifdef USE_LCD
|
|
|
|
LCD_ErrLog("Memory error \n");
|
|
|
|
#endif
|
|
|
|
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(USER_FLASH_FIRST_PAGE_ADDRESS);
|
|
|
|
|
|
|
|
Flash_Write_Address = USER_FLASH_FIRST_PAGE_ADDRESS;
|
|
|
|
/* initiate the write transaction by sending the first ack */
|
|
|
|
IAP_tftp_send_ack_packet(upcb, to, to_port, args->block);
|
|
|
|
#ifdef USE_LCD
|
|
|
|
LCD_UsrLog(" State: Programming... \n");
|
|
|
|
#endif
|
|
|
|
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;
|
|
|
|
|
|
|
|
#ifdef USE_LCD
|
|
|
|
uint32_t i;
|
|
|
|
char filename[40],message[46], *ptr;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* create new UDP PCB structure */
|
|
|
|
upcb_tftp_data = udp_new();
|
|
|
|
if (!upcb_tftp_data)
|
|
|
|
{
|
|
|
|
/* Error creating PCB. Out of Memory */
|
|
|
|
#ifdef USE_LCD
|
|
|
|
LCD_ErrLog("Can not create pcb \n");
|
|
|
|
#endif
|
|
|
|
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 */
|
|
|
|
#ifdef USE_LCD
|
|
|
|
LCD_ErrLog("Can not create pcb \n");
|
|
|
|
#endif
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
op = IAP_tftp_decode_op(pkt_buf->payload);
|
|
|
|
if (op != TFTP_WRQ)
|
|
|
|
{
|
|
|
|
/* remove PCB */
|
|
|
|
#ifdef USE_LCD
|
|
|
|
LCD_ErrLog("Bad TFTP opcode \n");
|
|
|
|
#endif
|
|
|
|
udp_remove(upcb_tftp_data);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
|
|
|
|
#ifdef USE_LCD
|
|
|
|
ptr = pkt_buf->payload;
|
|
|
|
ptr = ptr +2;
|
|
|
|
/*extract file name info */
|
|
|
|
i= 0;
|
|
|
|
while (*(ptr+i)!=0x0)
|
|
|
|
{
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
strncpy(filename, ptr, i+1);
|
|
|
|
|
|
|
|
LCD_UsrLog("IAP using TFTP \n");
|
|
|
|
sprintf(message, "File: %s",filename);
|
|
|
|
LCD_UsrLog("%s\n", message);
|
|
|
|
LCD_UsrLog(" State: Erasing...\n");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* 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 */
|
|
|
|
#ifdef USE_LCD
|
|
|
|
LCD_ErrLog("Can not create pcb \n");
|
|
|
|
#endif
|
|
|
|
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
|
|
|
|
{
|
|
|
|
#ifdef USE_LCD
|
|
|
|
LCD_ErrLog("Can not create pcb \n");
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* USE_IAP_TFTP */
|
|
|
|
|
|
|
|
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
|