mirror of
				https://github.com/smaeul/u-boot.git
				synced 2025-11-04 05:50:17 +00:00 
			
		
		
		
	Introduce reboot, boot and continue commands support to TCP fastboot by moving existing UDP logic into the common module. Signed-off-by: Dmitrii Merkurev <dimorinny@google.com> Cc: Ying-Chun Liu (PaulLiu) <paul.liu@linaro.org> Cc: Simon Glass <sjg@chromium.org> Сс: Joe Hershberger <joe.hershberger@ni.com> Сс: Ramon Fried <rfried.dev@gmail.com> Reviewed-by: Simon Glass <sjg@chromium.org> Reviewed-by: Ramon Fried <rfried.dev@gmail.com> Reviewed-by: Simon Glass <sjg@chromium.org>
		
			
				
	
	
		
			147 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			147 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
// SPDX-License-Identifier: BSD-2-Clause
 | 
						|
/*
 | 
						|
 * Copyright (C) 2023 The Android Open Source Project
 | 
						|
 */
 | 
						|
 | 
						|
#include <common.h>
 | 
						|
#include <fastboot.h>
 | 
						|
#include <net.h>
 | 
						|
#include <net/fastboot_tcp.h>
 | 
						|
#include <net/tcp.h>
 | 
						|
 | 
						|
static char command[FASTBOOT_COMMAND_LEN] = {0};
 | 
						|
static char response[FASTBOOT_RESPONSE_LEN] = {0};
 | 
						|
 | 
						|
static const unsigned short handshake_length = 4;
 | 
						|
static const uchar *handshake = "FB01";
 | 
						|
 | 
						|
static u16 curr_sport;
 | 
						|
static u16 curr_dport;
 | 
						|
static u32 curr_tcp_seq_num;
 | 
						|
static u32 curr_tcp_ack_num;
 | 
						|
static unsigned int curr_request_len;
 | 
						|
static enum fastboot_tcp_state {
 | 
						|
	FASTBOOT_CLOSED,
 | 
						|
	FASTBOOT_CONNECTED,
 | 
						|
	FASTBOOT_DISCONNECTING
 | 
						|
} state = FASTBOOT_CLOSED;
 | 
						|
 | 
						|
static void fastboot_tcp_answer(u8 action, unsigned int len)
 | 
						|
{
 | 
						|
	const u32 response_seq_num = curr_tcp_ack_num;
 | 
						|
	const u32 response_ack_num = curr_tcp_seq_num +
 | 
						|
		  (curr_request_len > 0 ? curr_request_len : 1);
 | 
						|
 | 
						|
	net_send_tcp_packet(len, htons(curr_sport), htons(curr_dport),
 | 
						|
			    action, response_seq_num, response_ack_num);
 | 
						|
}
 | 
						|
 | 
						|
static void fastboot_tcp_reset(void)
 | 
						|
{
 | 
						|
	fastboot_tcp_answer(TCP_RST, 0);
 | 
						|
	state = FASTBOOT_CLOSED;
 | 
						|
}
 | 
						|
 | 
						|
static void fastboot_tcp_send_packet(u8 action, const uchar *data, unsigned int len)
 | 
						|
{
 | 
						|
	uchar *pkt = net_get_async_tx_pkt_buf();
 | 
						|
 | 
						|
	memset(pkt, '\0', PKTSIZE);
 | 
						|
	pkt += net_eth_hdr_size() + IP_TCP_HDR_SIZE + TCP_TSOPT_SIZE + 2;
 | 
						|
	memcpy(pkt, data, len);
 | 
						|
	fastboot_tcp_answer(action, len);
 | 
						|
	memset(pkt, '\0', PKTSIZE);
 | 
						|
}
 | 
						|
 | 
						|
static void fastboot_tcp_send_message(const char *message, unsigned int len)
 | 
						|
{
 | 
						|
	__be64 len_be = __cpu_to_be64(len);
 | 
						|
	uchar *pkt = net_get_async_tx_pkt_buf();
 | 
						|
 | 
						|
	memset(pkt, '\0', PKTSIZE);
 | 
						|
	pkt += net_eth_hdr_size() + IP_TCP_HDR_SIZE + TCP_TSOPT_SIZE + 2;
 | 
						|
	// Put first 8 bytes as a big endian message length
 | 
						|
	memcpy(pkt, &len_be, 8);
 | 
						|
	pkt += 8;
 | 
						|
	memcpy(pkt, message, len);
 | 
						|
	fastboot_tcp_answer(TCP_ACK | TCP_PUSH, len + 8);
 | 
						|
	memset(pkt, '\0', PKTSIZE);
 | 
						|
}
 | 
						|
 | 
						|
static void fastboot_tcp_handler_ipv4(uchar *pkt, u16 dport,
 | 
						|
				      struct in_addr sip, u16 sport,
 | 
						|
				      u32 tcp_seq_num, u32 tcp_ack_num,
 | 
						|
				      u8 action, unsigned int len)
 | 
						|
{
 | 
						|
	int fastboot_command_id;
 | 
						|
	u64 command_size;
 | 
						|
	u8 tcp_fin = action & TCP_FIN;
 | 
						|
	u8 tcp_push = action & TCP_PUSH;
 | 
						|
 | 
						|
	curr_sport = sport;
 | 
						|
	curr_dport = dport;
 | 
						|
	curr_tcp_seq_num = tcp_seq_num;
 | 
						|
	curr_tcp_ack_num = tcp_ack_num;
 | 
						|
	curr_request_len = len;
 | 
						|
 | 
						|
	switch (state) {
 | 
						|
	case FASTBOOT_CLOSED:
 | 
						|
		if (tcp_push) {
 | 
						|
			if (len != handshake_length ||
 | 
						|
			    strlen(pkt) != handshake_length ||
 | 
						|
			    memcmp(pkt, handshake, handshake_length) != 0) {
 | 
						|
				fastboot_tcp_reset();
 | 
						|
				break;
 | 
						|
			}
 | 
						|
			fastboot_tcp_send_packet(TCP_ACK | TCP_PUSH,
 | 
						|
						 handshake, handshake_length);
 | 
						|
			state = FASTBOOT_CONNECTED;
 | 
						|
		}
 | 
						|
		break;
 | 
						|
	case FASTBOOT_CONNECTED:
 | 
						|
		if (tcp_fin) {
 | 
						|
			fastboot_tcp_answer(TCP_FIN | TCP_ACK, 0);
 | 
						|
			state = FASTBOOT_DISCONNECTING;
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		if (tcp_push) {
 | 
						|
			// First 8 bytes is big endian message length
 | 
						|
			command_size = __be64_to_cpu(*(u64 *)pkt);
 | 
						|
			len -= 8;
 | 
						|
			pkt += 8;
 | 
						|
 | 
						|
			// Only single packet messages are supported ATM
 | 
						|
			if (strlen(pkt) != command_size) {
 | 
						|
				fastboot_tcp_reset();
 | 
						|
				break;
 | 
						|
			}
 | 
						|
			strlcpy(command, pkt, len + 1);
 | 
						|
			fastboot_command_id = fastboot_handle_command(command, response);
 | 
						|
			fastboot_tcp_send_message(response, strlen(response));
 | 
						|
			fastboot_handle_boot(fastboot_command_id,
 | 
						|
					     strncmp("OKAY", response, 4) == 0);
 | 
						|
		}
 | 
						|
		break;
 | 
						|
	case FASTBOOT_DISCONNECTING:
 | 
						|
		if (tcp_push)
 | 
						|
			state = FASTBOOT_CLOSED;
 | 
						|
		break;
 | 
						|
	}
 | 
						|
 | 
						|
	memset(command, 0, FASTBOOT_COMMAND_LEN);
 | 
						|
	memset(response, 0, FASTBOOT_RESPONSE_LEN);
 | 
						|
	curr_sport = 0;
 | 
						|
	curr_dport = 0;
 | 
						|
	curr_tcp_seq_num = 0;
 | 
						|
	curr_tcp_ack_num = 0;
 | 
						|
	curr_request_len = 0;
 | 
						|
}
 | 
						|
 | 
						|
void fastboot_tcp_start_server(void)
 | 
						|
{
 | 
						|
	printf("Using %s device\n", eth_get_name());
 | 
						|
	printf("Listening for fastboot command on tcp %pI4\n", &net_ip);
 | 
						|
 | 
						|
	tcp_set_tcp_handler(fastboot_tcp_handler_ipv4);
 | 
						|
}
 |