Merge https://gitlab.denx.de/u-boot/custodians/u-boot-marvell
[oweals/u-boot.git] / drivers / fastboot / fb_command.c
1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3  * Copyright (C) 2016 The Android Open Source Project
4  */
5
6 #include <common.h>
7 #include <env.h>
8 #include <fastboot.h>
9 #include <fastboot-internal.h>
10 #include <fb_mmc.h>
11 #include <fb_nand.h>
12 #include <part.h>
13 #include <stdlib.h>
14
15 /**
16  * image_size - final fastboot image size
17  */
18 static u32 image_size;
19
20 /**
21  * fastboot_bytes_received - number of bytes received in the current download
22  */
23 static u32 fastboot_bytes_received;
24
25 /**
26  * fastboot_bytes_expected - number of bytes expected in the current download
27  */
28 static u32 fastboot_bytes_expected;
29
30 static void okay(char *, char *);
31 static void getvar(char *, char *);
32 static void download(char *, char *);
33 #if CONFIG_IS_ENABLED(FASTBOOT_FLASH)
34 static void flash(char *, char *);
35 static void erase(char *, char *);
36 #endif
37 static void reboot_bootloader(char *, char *);
38 #if CONFIG_IS_ENABLED(FASTBOOT_CMD_OEM_FORMAT)
39 static void oem_format(char *, char *);
40 #endif
41
42 static const struct {
43         const char *command;
44         void (*dispatch)(char *cmd_parameter, char *response);
45 } commands[FASTBOOT_COMMAND_COUNT] = {
46         [FASTBOOT_COMMAND_GETVAR] = {
47                 .command = "getvar",
48                 .dispatch = getvar
49         },
50         [FASTBOOT_COMMAND_DOWNLOAD] = {
51                 .command = "download",
52                 .dispatch = download
53         },
54 #if CONFIG_IS_ENABLED(FASTBOOT_FLASH)
55         [FASTBOOT_COMMAND_FLASH] =  {
56                 .command = "flash",
57                 .dispatch = flash
58         },
59         [FASTBOOT_COMMAND_ERASE] =  {
60                 .command = "erase",
61                 .dispatch = erase
62         },
63 #endif
64         [FASTBOOT_COMMAND_BOOT] =  {
65                 .command = "boot",
66                 .dispatch = okay
67         },
68         [FASTBOOT_COMMAND_CONTINUE] =  {
69                 .command = "continue",
70                 .dispatch = okay
71         },
72         [FASTBOOT_COMMAND_REBOOT] =  {
73                 .command = "reboot",
74                 .dispatch = okay
75         },
76         [FASTBOOT_COMMAND_REBOOT_BOOTLOADER] =  {
77                 .command = "reboot-bootloader",
78                 .dispatch = reboot_bootloader
79         },
80         [FASTBOOT_COMMAND_SET_ACTIVE] =  {
81                 .command = "set_active",
82                 .dispatch = okay
83         },
84 #if CONFIG_IS_ENABLED(FASTBOOT_CMD_OEM_FORMAT)
85         [FASTBOOT_COMMAND_OEM_FORMAT] = {
86                 .command = "oem format",
87                 .dispatch = oem_format,
88         },
89 #endif
90 };
91
92 /**
93  * fastboot_handle_command - Handle fastboot command
94  *
95  * @cmd_string: Pointer to command string
96  * @response: Pointer to fastboot response buffer
97  *
98  * Return: Executed command, or -1 if not recognized
99  */
100 int fastboot_handle_command(char *cmd_string, char *response)
101 {
102         int i;
103         char *cmd_parameter;
104
105         cmd_parameter = cmd_string;
106         strsep(&cmd_parameter, ":");
107
108         for (i = 0; i < FASTBOOT_COMMAND_COUNT; i++) {
109                 if (!strcmp(commands[i].command, cmd_string)) {
110                         if (commands[i].dispatch) {
111                                 commands[i].dispatch(cmd_parameter,
112                                                         response);
113                                 return i;
114                         } else {
115                                 break;
116                         }
117                 }
118         }
119
120         pr_err("command %s not recognized.\n", cmd_string);
121         fastboot_fail("unrecognized command", response);
122         return -1;
123 }
124
125 /**
126  * okay() - Send bare OKAY response
127  *
128  * @cmd_parameter: Pointer to command parameter
129  * @response: Pointer to fastboot response buffer
130  *
131  * Send a bare OKAY fastboot response. This is used where the command is
132  * valid, but all the work is done after the response has been sent (e.g.
133  * boot, reboot etc.)
134  */
135 static void okay(char *cmd_parameter, char *response)
136 {
137         fastboot_okay(NULL, response);
138 }
139
140 /**
141  * getvar() - Read a config/version variable
142  *
143  * @cmd_parameter: Pointer to command parameter
144  * @response: Pointer to fastboot response buffer
145  */
146 static void getvar(char *cmd_parameter, char *response)
147 {
148         fastboot_getvar(cmd_parameter, response);
149 }
150
151 /**
152  * fastboot_download() - Start a download transfer from the client
153  *
154  * @cmd_parameter: Pointer to command parameter
155  * @response: Pointer to fastboot response buffer
156  */
157 static void download(char *cmd_parameter, char *response)
158 {
159         char *tmp;
160
161         if (!cmd_parameter) {
162                 fastboot_fail("Expected command parameter", response);
163                 return;
164         }
165         fastboot_bytes_received = 0;
166         fastboot_bytes_expected = simple_strtoul(cmd_parameter, &tmp, 16);
167         if (fastboot_bytes_expected == 0) {
168                 fastboot_fail("Expected nonzero image size", response);
169                 return;
170         }
171         /*
172          * Nothing to download yet. Response is of the form:
173          * [DATA|FAIL]$cmd_parameter
174          *
175          * where cmd_parameter is an 8 digit hexadecimal number
176          */
177         if (fastboot_bytes_expected > fastboot_buf_size) {
178                 fastboot_fail(cmd_parameter, response);
179         } else {
180                 printf("Starting download of %d bytes\n",
181                        fastboot_bytes_expected);
182                 fastboot_response("DATA", response, "%s", cmd_parameter);
183         }
184 }
185
186 /**
187  * fastboot_data_remaining() - return bytes remaining in current transfer
188  *
189  * Return: Number of bytes left in the current download
190  */
191 u32 fastboot_data_remaining(void)
192 {
193         return fastboot_bytes_expected - fastboot_bytes_received;
194 }
195
196 /**
197  * fastboot_data_download() - Copy image data to fastboot_buf_addr.
198  *
199  * @fastboot_data: Pointer to received fastboot data
200  * @fastboot_data_len: Length of received fastboot data
201  * @response: Pointer to fastboot response buffer
202  *
203  * Copies image data from fastboot_data to fastboot_buf_addr. Writes to
204  * response. fastboot_bytes_received is updated to indicate the number
205  * of bytes that have been transferred.
206  *
207  * On completion sets image_size and ${filesize} to the total size of the
208  * downloaded image.
209  */
210 void fastboot_data_download(const void *fastboot_data,
211                             unsigned int fastboot_data_len,
212                             char *response)
213 {
214 #define BYTES_PER_DOT   0x20000
215         u32 pre_dot_num, now_dot_num;
216
217         if (fastboot_data_len == 0 ||
218             (fastboot_bytes_received + fastboot_data_len) >
219             fastboot_bytes_expected) {
220                 fastboot_fail("Received invalid data length",
221                               response);
222                 return;
223         }
224         /* Download data to fastboot_buf_addr */
225         memcpy(fastboot_buf_addr + fastboot_bytes_received,
226                fastboot_data, fastboot_data_len);
227
228         pre_dot_num = fastboot_bytes_received / BYTES_PER_DOT;
229         fastboot_bytes_received += fastboot_data_len;
230         now_dot_num = fastboot_bytes_received / BYTES_PER_DOT;
231
232         if (pre_dot_num != now_dot_num) {
233                 putc('.');
234                 if (!(now_dot_num % 74))
235                         putc('\n');
236         }
237         *response = '\0';
238 }
239
240 /**
241  * fastboot_data_complete() - Mark current transfer complete
242  *
243  * @response: Pointer to fastboot response buffer
244  *
245  * Set image_size and ${filesize} to the total size of the downloaded image.
246  */
247 void fastboot_data_complete(char *response)
248 {
249         /* Download complete. Respond with "OKAY" */
250         fastboot_okay(NULL, response);
251         printf("\ndownloading of %d bytes finished\n", fastboot_bytes_received);
252         image_size = fastboot_bytes_received;
253         env_set_hex("filesize", image_size);
254         fastboot_bytes_expected = 0;
255         fastboot_bytes_received = 0;
256 }
257
258 #if CONFIG_IS_ENABLED(FASTBOOT_FLASH)
259 /**
260  * flash() - write the downloaded image to the indicated partition.
261  *
262  * @cmd_parameter: Pointer to partition name
263  * @response: Pointer to fastboot response buffer
264  *
265  * Writes the previously downloaded image to the partition indicated by
266  * cmd_parameter. Writes to response.
267  */
268 static void flash(char *cmd_parameter, char *response)
269 {
270 #if CONFIG_IS_ENABLED(FASTBOOT_FLASH_MMC)
271         fastboot_mmc_flash_write(cmd_parameter, fastboot_buf_addr, image_size,
272                                  response);
273 #endif
274 #if CONFIG_IS_ENABLED(FASTBOOT_FLASH_NAND)
275         fastboot_nand_flash_write(cmd_parameter, fastboot_buf_addr, image_size,
276                                   response);
277 #endif
278 }
279
280 /**
281  * erase() - erase the indicated partition.
282  *
283  * @cmd_parameter: Pointer to partition name
284  * @response: Pointer to fastboot response buffer
285  *
286  * Erases the partition indicated by cmd_parameter (clear to 0x00s). Writes
287  * to response.
288  */
289 static void erase(char *cmd_parameter, char *response)
290 {
291 #if CONFIG_IS_ENABLED(FASTBOOT_FLASH_MMC)
292         fastboot_mmc_erase(cmd_parameter, response);
293 #endif
294 #if CONFIG_IS_ENABLED(FASTBOOT_FLASH_NAND)
295         fastboot_nand_erase(cmd_parameter, response);
296 #endif
297 }
298 #endif
299
300 /**
301  * reboot_bootloader() - Sets reboot bootloader flag.
302  *
303  * @cmd_parameter: Pointer to command parameter
304  * @response: Pointer to fastboot response buffer
305  */
306 static void reboot_bootloader(char *cmd_parameter, char *response)
307 {
308         if (fastboot_set_reboot_flag())
309                 fastboot_fail("Cannot set reboot flag", response);
310         else
311                 fastboot_okay(NULL, response);
312 }
313
314 #if CONFIG_IS_ENABLED(FASTBOOT_CMD_OEM_FORMAT)
315 /**
316  * oem_format() - Execute the OEM format command
317  *
318  * @cmd_parameter: Pointer to command parameter
319  * @response: Pointer to fastboot response buffer
320  */
321 static void oem_format(char *cmd_parameter, char *response)
322 {
323         char cmdbuf[32];
324
325         if (!env_get("partitions")) {
326                 fastboot_fail("partitions not set", response);
327         } else {
328                 sprintf(cmdbuf, "gpt write mmc %x $partitions",
329                         CONFIG_FASTBOOT_FLASH_MMC_DEV);
330                 if (run_command(cmdbuf, 0))
331                         fastboot_fail("", response);
332                 else
333                         fastboot_okay(NULL, response);
334         }
335 }
336 #endif