1 /*
2 * Copyright (C) 2008 The Android Open Source Project
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in
12 * the documentation and/or other materials provided with the
13 * distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include "fastboot.h"
30 #include "fs.h"
31
32 #include <errno.h>
33 #include <stdarg.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <sys/stat.h>
38 #include <sys/types.h>
39 #include <unistd.h>
40
41 #define OP_DOWNLOAD 1
42 #define OP_COMMAND 2
43 #define OP_QUERY 3
44 #define OP_NOTICE 4
45 #define OP_DOWNLOAD_SPARSE 5
46 #define OP_WAIT_FOR_DISCONNECT 6
47
48 typedef struct Action Action;
49
50 #define CMD_SIZE 64
51
52 struct Action {
53 unsigned op;
54 Action* next;
55
56 char cmd[CMD_SIZE];
57 const char* prod;
58 void* data;
59
60 // The protocol only supports 32-bit sizes, so you'll have to break
61 // anything larger into chunks.
62 uint32_t size;
63
64 const char *msg;
65 int (*func)(Action* a, int status, const char* resp);
66
67 double start;
68 };
69
70 static Action *action_list = 0;
71 static Action *action_last = 0;
72
73
74
75
fb_getvar(Transport * transport,const std::string & key,std::string * value)76 bool fb_getvar(Transport* transport, const std::string& key, std::string* value) {
77 std::string cmd = "getvar:";
78 cmd += key;
79
80 char buf[FB_RESPONSE_SZ + 1];
81 memset(buf, 0, sizeof(buf));
82 if (fb_command_response(transport, cmd.c_str(), buf)) {
83 return false;
84 }
85 *value = buf;
86 return true;
87 }
88
cb_default(Action * a,int status,const char * resp)89 static int cb_default(Action* a, int status, const char* resp) {
90 if (status) {
91 fprintf(stderr,"FAILED (%s)\n", resp);
92 } else {
93 double split = now();
94 fprintf(stderr,"OKAY [%7.3fs]\n", (split - a->start));
95 a->start = split;
96 }
97 return status;
98 }
99
queue_action(unsigned op,const char * fmt,...)100 static Action *queue_action(unsigned op, const char *fmt, ...)
101 {
102 va_list ap;
103 size_t cmdsize;
104
105 Action* a = reinterpret_cast<Action*>(calloc(1, sizeof(Action)));
106 if (a == nullptr) die("out of memory");
107
108 va_start(ap, fmt);
109 cmdsize = vsnprintf(a->cmd, sizeof(a->cmd), fmt, ap);
110 va_end(ap);
111
112 if (cmdsize >= sizeof(a->cmd)) {
113 free(a);
114 die("Command length (%d) exceeds maximum size (%d)", cmdsize, sizeof(a->cmd));
115 }
116
117 if (action_last) {
118 action_last->next = a;
119 } else {
120 action_list = a;
121 }
122 action_last = a;
123 a->op = op;
124 a->func = cb_default;
125
126 a->start = -1;
127
128 return a;
129 }
130
fb_set_active(const char * slot)131 void fb_set_active(const char *slot)
132 {
133 Action *a;
134 a = queue_action(OP_COMMAND, "set_active:%s", slot);
135 a->msg = mkmsg("Setting current slot to '%s'", slot);
136 }
137
fb_queue_erase(const char * ptn)138 void fb_queue_erase(const char *ptn)
139 {
140 Action *a;
141 a = queue_action(OP_COMMAND, "erase:%s", ptn);
142 a->msg = mkmsg("erasing '%s'", ptn);
143 }
144
fb_queue_flash(const char * ptn,void * data,unsigned sz)145 void fb_queue_flash(const char *ptn, void *data, unsigned sz)
146 {
147 Action *a;
148
149 a = queue_action(OP_DOWNLOAD, "");
150 a->data = data;
151 a->size = sz;
152 a->msg = mkmsg("sending '%s' (%d KB)", ptn, sz / 1024);
153
154 a = queue_action(OP_COMMAND, "flash:%s", ptn);
155 a->msg = mkmsg("writing '%s'", ptn);
156 }
157
fb_queue_flash_sparse(const char * ptn,struct sparse_file * s,unsigned sz,size_t current,size_t total)158 void fb_queue_flash_sparse(const char* ptn, struct sparse_file* s, unsigned sz, size_t current,
159 size_t total) {
160 Action *a;
161
162 a = queue_action(OP_DOWNLOAD_SPARSE, "");
163 a->data = s;
164 a->size = 0;
165 a->msg = mkmsg("sending sparse '%s' %zu/%zu (%d KB)", ptn, current, total, sz / 1024);
166
167 a = queue_action(OP_COMMAND, "flash:%s", ptn);
168 a->msg = mkmsg("writing '%s' %zu/%zu", ptn, current, total);
169 }
170
match(const char * str,const char ** value,unsigned count)171 static int match(const char* str, const char** value, unsigned count) {
172 unsigned n;
173
174 for (n = 0; n < count; n++) {
175 const char *val = value[n];
176 int len = strlen(val);
177 int match;
178
179 if ((len > 1) && (val[len-1] == '*')) {
180 len--;
181 match = !strncmp(val, str, len);
182 } else {
183 match = !strcmp(val, str);
184 }
185
186 if (match) return 1;
187 }
188
189 return 0;
190 }
191
192
193
cb_check(Action * a,int status,const char * resp,int invert)194 static int cb_check(Action* a, int status, const char* resp, int invert)
195 {
196 const char** value = reinterpret_cast<const char**>(a->data);
197 unsigned count = a->size;
198 unsigned n;
199 int yes;
200
201 if (status) {
202 fprintf(stderr,"FAILED (%s)\n", resp);
203 return status;
204 }
205
206 if (a->prod) {
207 if (strcmp(a->prod, cur_product) != 0) {
208 double split = now();
209 fprintf(stderr,"IGNORE, product is %s required only for %s [%7.3fs]\n",
210 cur_product, a->prod, (split - a->start));
211 a->start = split;
212 return 0;
213 }
214 }
215
216 yes = match(resp, value, count);
217 if (invert) yes = !yes;
218
219 if (yes) {
220 double split = now();
221 fprintf(stderr,"OKAY [%7.3fs]\n", (split - a->start));
222 a->start = split;
223 return 0;
224 }
225
226 fprintf(stderr,"FAILED\n\n");
227 fprintf(stderr,"Device %s is '%s'.\n", a->cmd + 7, resp);
228 fprintf(stderr,"Update %s '%s'",
229 invert ? "rejects" : "requires", value[0]);
230 for (n = 1; n < count; n++) {
231 fprintf(stderr," or '%s'", value[n]);
232 }
233 fprintf(stderr,".\n\n");
234 return -1;
235 }
236
cb_require(Action * a,int status,const char * resp)237 static int cb_require(Action*a, int status, const char* resp) {
238 return cb_check(a, status, resp, 0);
239 }
240
cb_reject(Action * a,int status,const char * resp)241 static int cb_reject(Action* a, int status, const char* resp) {
242 return cb_check(a, status, resp, 1);
243 }
244
fb_queue_require(const char * prod,const char * var,bool invert,size_t nvalues,const char ** value)245 void fb_queue_require(const char *prod, const char *var,
246 bool invert, size_t nvalues, const char **value)
247 {
248 Action *a;
249 a = queue_action(OP_QUERY, "getvar:%s", var);
250 a->prod = prod;
251 a->data = value;
252 a->size = nvalues;
253 a->msg = mkmsg("checking %s", var);
254 a->func = invert ? cb_reject : cb_require;
255 if (a->data == nullptr) die("out of memory");
256 }
257
cb_display(Action * a,int status,const char * resp)258 static int cb_display(Action* a, int status, const char* resp) {
259 if (status) {
260 fprintf(stderr, "%s FAILED (%s)\n", a->cmd, resp);
261 return status;
262 }
263 fprintf(stderr, "%s: %s\n", (char*) a->data, resp);
264 return 0;
265 }
266
fb_queue_display(const char * var,const char * prettyname)267 void fb_queue_display(const char *var, const char *prettyname)
268 {
269 Action *a;
270 a = queue_action(OP_QUERY, "getvar:%s", var);
271 a->data = strdup(prettyname);
272 if (a->data == nullptr) die("out of memory");
273 a->func = cb_display;
274 }
275
cb_save(Action * a,int status,const char * resp)276 static int cb_save(Action* a, int status, const char* resp) {
277 if (status) {
278 fprintf(stderr, "%s FAILED (%s)\n", a->cmd, resp);
279 return status;
280 }
281 strncpy(reinterpret_cast<char*>(a->data), resp, a->size);
282 return 0;
283 }
284
fb_queue_query_save(const char * var,char * dest,unsigned dest_size)285 void fb_queue_query_save(const char *var, char *dest, unsigned dest_size)
286 {
287 Action *a;
288 a = queue_action(OP_QUERY, "getvar:%s", var);
289 a->data = (void *)dest;
290 a->size = dest_size;
291 a->func = cb_save;
292 }
293
cb_do_nothing(Action *,int,const char *)294 static int cb_do_nothing(Action*, int , const char*) {
295 fprintf(stderr,"\n");
296 return 0;
297 }
298
fb_queue_reboot(void)299 void fb_queue_reboot(void)
300 {
301 Action *a = queue_action(OP_COMMAND, "reboot");
302 a->func = cb_do_nothing;
303 a->msg = "rebooting";
304 }
305
fb_queue_command(const char * cmd,const char * msg)306 void fb_queue_command(const char *cmd, const char *msg)
307 {
308 Action *a = queue_action(OP_COMMAND, cmd);
309 a->msg = msg;
310 }
311
fb_queue_download(const char * name,void * data,unsigned size)312 void fb_queue_download(const char *name, void *data, unsigned size)
313 {
314 Action *a = queue_action(OP_DOWNLOAD, "");
315 a->data = data;
316 a->size = size;
317 a->msg = mkmsg("downloading '%s'", name);
318 }
319
fb_queue_notice(const char * notice)320 void fb_queue_notice(const char *notice)
321 {
322 Action *a = queue_action(OP_NOTICE, "");
323 a->data = (void*) notice;
324 }
325
fb_queue_wait_for_disconnect(void)326 void fb_queue_wait_for_disconnect(void)
327 {
328 queue_action(OP_WAIT_FOR_DISCONNECT, "");
329 }
330
fb_execute_queue(Transport * transport)331 int fb_execute_queue(Transport* transport)
332 {
333 Action *a;
334 char resp[FB_RESPONSE_SZ+1];
335 int status = 0;
336
337 a = action_list;
338 if (!a)
339 return status;
340 resp[FB_RESPONSE_SZ] = 0;
341
342 double start = -1;
343 for (a = action_list; a; a = a->next) {
344 a->start = now();
345 if (start < 0) start = a->start;
346 if (a->msg) {
347 // fprintf(stderr,"%30s... ",a->msg);
348 fprintf(stderr,"%s...\n",a->msg);
349 }
350 if (a->op == OP_DOWNLOAD) {
351 status = fb_download_data(transport, a->data, a->size);
352 status = a->func(a, status, status ? fb_get_error().c_str() : "");
353 if (status) break;
354 } else if (a->op == OP_COMMAND) {
355 status = fb_command(transport, a->cmd);
356 status = a->func(a, status, status ? fb_get_error().c_str() : "");
357 if (status) break;
358 } else if (a->op == OP_QUERY) {
359 status = fb_command_response(transport, a->cmd, resp);
360 status = a->func(a, status, status ? fb_get_error().c_str() : resp);
361 if (status) break;
362 } else if (a->op == OP_NOTICE) {
363 fprintf(stderr,"%s\n",(char*)a->data);
364 } else if (a->op == OP_DOWNLOAD_SPARSE) {
365 status = fb_download_data_sparse(transport, reinterpret_cast<sparse_file*>(a->data));
366 status = a->func(a, status, status ? fb_get_error().c_str() : "");
367 if (status) break;
368 } else if (a->op == OP_WAIT_FOR_DISCONNECT) {
369 transport->WaitForDisconnect();
370 } else {
371 die("bogus action");
372 }
373 }
374
375 fprintf(stderr,"finished. total time: %.3fs\n", (now() - start));
376 return status;
377 }
378