]> Git Repo - J-u-boot.git/blob - net/fastboot_tcp.c
Merge patch series "some serial rx buffer patches"
[J-u-boot.git] / net / fastboot_tcp.c
1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3  * Copyright (C) 2023 The Android Open Source Project
4  */
5
6 #include <fastboot.h>
7 #include <net.h>
8 #include <net/fastboot_tcp.h>
9 #include <net/tcp.h>
10
11 static char command[FASTBOOT_COMMAND_LEN] = {0};
12 static char response[FASTBOOT_RESPONSE_LEN] = {0};
13
14 static const unsigned short handshake_length = 4;
15 static const uchar *handshake = "FB01";
16
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 {
23         FASTBOOT_CLOSED,
24         FASTBOOT_CONNECTED,
25         FASTBOOT_DISCONNECTING
26 } state = FASTBOOT_CLOSED;
27
28 static void fastboot_tcp_answer(u8 action, unsigned int len)
29 {
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);
33
34         net_send_tcp_packet(len, htons(curr_sport), htons(curr_dport),
35                             action, response_seq_num, response_ack_num);
36 }
37
38 static void fastboot_tcp_reset(void)
39 {
40         fastboot_tcp_answer(TCP_RST, 0);
41         state = FASTBOOT_CLOSED;
42 }
43
44 static void fastboot_tcp_send_packet(u8 action, const uchar *data, unsigned int len)
45 {
46         uchar *pkt = net_get_async_tx_pkt_buf();
47
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);
53 }
54
55 static void fastboot_tcp_send_message(const char *message, unsigned int len)
56 {
57         __be64 len_be = __cpu_to_be64(len);
58         uchar *pkt = net_get_async_tx_pkt_buf();
59
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);
64         pkt += 8;
65         memcpy(pkt, message, len);
66         fastboot_tcp_answer(TCP_ACK | TCP_PUSH, len + 8);
67         memset(pkt, '\0', PKTSIZE);
68 }
69
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)
74 {
75         int fastboot_command_id;
76         u64 command_size;
77         u8 tcp_fin = action & TCP_FIN;
78         u8 tcp_push = action & TCP_PUSH;
79
80         curr_sport = sport;
81         curr_dport = dport;
82         curr_tcp_seq_num = tcp_seq_num;
83         curr_tcp_ack_num = tcp_ack_num;
84         curr_request_len = len;
85
86         switch (state) {
87         case FASTBOOT_CLOSED:
88                 if (tcp_push) {
89                         if (len != handshake_length ||
90                             strlen(pkt) != handshake_length ||
91                             memcmp(pkt, handshake, handshake_length) != 0) {
92                                 fastboot_tcp_reset();
93                                 break;
94                         }
95                         fastboot_tcp_send_packet(TCP_ACK | TCP_PUSH,
96                                                  handshake, handshake_length);
97                         state = FASTBOOT_CONNECTED;
98                 }
99                 break;
100         case FASTBOOT_CONNECTED:
101                 if (tcp_fin) {
102                         fastboot_tcp_answer(TCP_FIN | TCP_ACK, 0);
103                         state = FASTBOOT_DISCONNECTING;
104                         break;
105                 }
106                 if (tcp_push) {
107                         // First 8 bytes is big endian message length
108                         command_size = __be64_to_cpu(*(u64 *)pkt);
109                         len -= 8;
110                         pkt += 8;
111
112                         // Only single packet messages are supported ATM
113                         if (strlen(pkt) != command_size) {
114                                 fastboot_tcp_reset();
115                                 break;
116                         }
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);
122                 }
123                 break;
124         case FASTBOOT_DISCONNECTING:
125                 if (tcp_push)
126                         state = FASTBOOT_CLOSED;
127                 break;
128         }
129
130         memset(command, 0, FASTBOOT_COMMAND_LEN);
131         memset(response, 0, FASTBOOT_RESPONSE_LEN);
132         curr_sport = 0;
133         curr_dport = 0;
134         curr_tcp_seq_num = 0;
135         curr_tcp_ack_num = 0;
136         curr_request_len = 0;
137 }
138
139 void fastboot_tcp_start_server(void)
140 {
141         printf("Using %s device\n", eth_get_name());
142         printf("Listening for fastboot command on tcp %pI4\n", &net_ip);
143
144         tcp_set_tcp_handler(fastboot_tcp_handler_ipv4);
145 }
This page took 0.031192 seconds and 4 git commands to generate.