1 // SPDX-License-Identifier: BSD-2-Clause
3 * Copyright (C) 2023 The Android Open Source Project
8 #include <net/fastboot_tcp.h>
11 static char command[FASTBOOT_COMMAND_LEN] = {0};
12 static char response[FASTBOOT_RESPONSE_LEN] = {0};
14 static const unsigned short handshake_length = 4;
15 static const uchar *handshake = "FB01";
17 static u16 curr_sport;
18 static u16 curr_dport;
19 static u32 curr_tcp_seq_num;
20 static u32 curr_tcp_ack_num;
21 static unsigned int curr_request_len;
22 static enum fastboot_tcp_state {
25 FASTBOOT_DISCONNECTING
26 } state = FASTBOOT_CLOSED;
28 static void fastboot_tcp_answer(u8 action, unsigned int len)
30 const u32 response_seq_num = curr_tcp_ack_num;
31 const u32 response_ack_num = curr_tcp_seq_num +
32 (curr_request_len > 0 ? curr_request_len : 1);
34 net_send_tcp_packet(len, htons(curr_sport), htons(curr_dport),
35 action, response_seq_num, response_ack_num);
38 static void fastboot_tcp_reset(void)
40 fastboot_tcp_answer(TCP_RST, 0);
41 state = FASTBOOT_CLOSED;
44 static void fastboot_tcp_send_packet(u8 action, const uchar *data, unsigned int len)
46 uchar *pkt = net_get_async_tx_pkt_buf();
48 memset(pkt, '\0', PKTSIZE);
49 pkt += net_eth_hdr_size() + IP_TCP_HDR_SIZE + TCP_TSOPT_SIZE + 2;
50 memcpy(pkt, data, len);
51 fastboot_tcp_answer(action, len);
52 memset(pkt, '\0', PKTSIZE);
55 static void fastboot_tcp_send_message(const char *message, unsigned int len)
57 __be64 len_be = __cpu_to_be64(len);
58 uchar *pkt = net_get_async_tx_pkt_buf();
60 memset(pkt, '\0', PKTSIZE);
61 pkt += net_eth_hdr_size() + IP_TCP_HDR_SIZE + TCP_TSOPT_SIZE + 2;
62 // Put first 8 bytes as a big endian message length
63 memcpy(pkt, &len_be, 8);
65 memcpy(pkt, message, len);
66 fastboot_tcp_answer(TCP_ACK | TCP_PUSH, len + 8);
67 memset(pkt, '\0', PKTSIZE);
70 static void fastboot_tcp_handler_ipv4(uchar *pkt, u16 dport,
71 struct in_addr sip, u16 sport,
72 u32 tcp_seq_num, u32 tcp_ack_num,
73 u8 action, unsigned int len)
75 int fastboot_command_id;
77 u8 tcp_fin = action & TCP_FIN;
78 u8 tcp_push = action & TCP_PUSH;
82 curr_tcp_seq_num = tcp_seq_num;
83 curr_tcp_ack_num = tcp_ack_num;
84 curr_request_len = len;
89 if (len != handshake_length ||
90 strlen(pkt) != handshake_length ||
91 memcmp(pkt, handshake, handshake_length) != 0) {
95 fastboot_tcp_send_packet(TCP_ACK | TCP_PUSH,
96 handshake, handshake_length);
97 state = FASTBOOT_CONNECTED;
100 case FASTBOOT_CONNECTED:
102 fastboot_tcp_answer(TCP_FIN | TCP_ACK, 0);
103 state = FASTBOOT_DISCONNECTING;
107 // First 8 bytes is big endian message length
108 command_size = __be64_to_cpu(*(u64 *)pkt);
112 // Only single packet messages are supported ATM
113 if (strlen(pkt) != command_size) {
114 fastboot_tcp_reset();
117 strlcpy(command, pkt, len + 1);
118 fastboot_command_id = fastboot_handle_command(command, response);
119 fastboot_tcp_send_message(response, strlen(response));
120 fastboot_handle_boot(fastboot_command_id,
121 strncmp("OKAY", response, 4) == 0);
124 case FASTBOOT_DISCONNECTING:
126 state = FASTBOOT_CLOSED;
130 memset(command, 0, FASTBOOT_COMMAND_LEN);
131 memset(response, 0, FASTBOOT_RESPONSE_LEN);
134 curr_tcp_seq_num = 0;
135 curr_tcp_ack_num = 0;
136 curr_request_len = 0;
139 void fastboot_tcp_start_server(void)
141 printf("Using %s device\n", eth_get_name());
142 printf("Listening for fastboot command on tcp %pI4\n", &net_ip);
144 tcp_set_tcp_handler(fastboot_tcp_handler_ipv4);