protocol.cpp revision 2810d00dd95ed78fdd93f2ed68f06ee32bd8e1ac
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#define round_down(a, b) \
30    ({ typeof(a) _a = (a); typeof(b) _b = (b); _a - (_a % _b); })
31
32#include <stdio.h>
33#include <stdlib.h>
34#include <string.h>
35#include <errno.h>
36
37#include <algorithm>
38
39#include <android-base/stringprintf.h>
40#include <sparse/sparse.h>
41
42#include "fastboot.h"
43#include "transport.h"
44
45static std::string g_error;
46
47const std::string fb_get_error() {
48    return g_error;
49}
50
51static int check_response(Transport* transport, uint32_t size, char* response) {
52    char status[65];
53
54    while (true) {
55        int r = transport->Read(status, 64);
56        if (r < 0) {
57            g_error = android::base::StringPrintf("status read failed (%s)", strerror(errno));
58            transport->Close();
59            return -1;
60        }
61        status[r] = 0;
62
63        if (r < 4) {
64            g_error = android::base::StringPrintf("status malformed (%d bytes)", r);
65            transport->Close();
66            return -1;
67        }
68
69        if (!memcmp(status, "INFO", 4)) {
70            fprintf(stderr,"(bootloader) %s\n", status + 4);
71            continue;
72        }
73
74        if (!memcmp(status, "OKAY", 4)) {
75            if (response) {
76                strcpy(response, (char*) status + 4);
77            }
78            return 0;
79        }
80
81        if (!memcmp(status, "FAIL", 4)) {
82            if (r > 4) {
83                g_error = android::base::StringPrintf("remote: %s", status + 4);
84            } else {
85                g_error = "remote failure";
86            }
87            return -1;
88        }
89
90        if (!memcmp(status, "DATA", 4) && size > 0){
91            uint32_t dsize = strtol(status + 4, 0, 16);
92            if (dsize > size) {
93                g_error = android::base::StringPrintf("data size too large (%d)", dsize);
94                transport->Close();
95                return -1;
96            }
97            return dsize;
98        }
99
100        g_error = "unknown status code";
101        transport->Close();
102        break;
103    }
104
105    return -1;
106}
107
108static int _command_start(Transport* transport, const char* cmd, uint32_t size, char* response) {
109    size_t cmdsize = strlen(cmd);
110    if (cmdsize > 64) {
111        g_error = android::base::StringPrintf("command too large (%zu)", cmdsize);
112        return -1;
113    }
114
115    if (response) {
116        response[0] = 0;
117    }
118
119    if (transport->Write(cmd, cmdsize) != static_cast<int>(cmdsize)) {
120        g_error = android::base::StringPrintf("command write failed (%s)", strerror(errno));
121        transport->Close();
122        return -1;
123    }
124
125    return check_response(transport, size, response);
126}
127
128static int _command_data(Transport* transport, const void* data, uint32_t size) {
129    int r = transport->Write(data, size);
130    if (r < 0) {
131        g_error = android::base::StringPrintf("data transfer failure (%s)", strerror(errno));
132        transport->Close();
133        return -1;
134    }
135    if (r != ((int) size)) {
136        g_error = "data transfer failure (short transfer)";
137        transport->Close();
138        return -1;
139    }
140    return r;
141}
142
143static int _command_end(Transport* transport) {
144    return check_response(transport, 0, 0) < 0 ? -1 : 0;
145}
146
147static int _command_send(Transport* transport, const char* cmd, const void* data, uint32_t size,
148                         char* response) {
149    if (size == 0) {
150        return -1;
151    }
152
153    int r = _command_start(transport, cmd, size, response);
154    if (r < 0) {
155        return -1;
156    }
157
158    r = _command_data(transport, data, size);
159    if (r < 0) {
160        return -1;
161    }
162
163    r = _command_end(transport);
164    if (r < 0) {
165        return -1;
166    }
167
168    return size;
169}
170
171static int _command_send_no_data(Transport* transport, const char* cmd, char* response) {
172    return _command_start(transport, cmd, 0, response);
173}
174
175int fb_command(Transport* transport, const char* cmd) {
176    return _command_send_no_data(transport, cmd, 0);
177}
178
179int fb_command_response(Transport* transport, const char* cmd, char* response) {
180    return _command_send_no_data(transport, cmd, response);
181}
182
183int fb_download_data(Transport* transport, const void* data, uint32_t size) {
184    char cmd[64];
185    snprintf(cmd, sizeof(cmd), "download:%08x", size);
186    return _command_send(transport, cmd, data, size, 0) < 0 ? -1 : 0;
187}
188
189#define TRANSPORT_BUF_SIZE 1024
190static char transport_buf[TRANSPORT_BUF_SIZE];
191static int transport_buf_len;
192
193static int fb_download_data_sparse_write(void *priv, const void *data, int len)
194{
195    int r;
196    Transport* transport = reinterpret_cast<Transport*>(priv);
197    int to_write;
198    const char* ptr = reinterpret_cast<const char*>(data);
199
200    if (transport_buf_len) {
201        to_write = std::min(TRANSPORT_BUF_SIZE - transport_buf_len, len);
202
203        memcpy(transport_buf + transport_buf_len, ptr, to_write);
204        transport_buf_len += to_write;
205        ptr += to_write;
206        len -= to_write;
207    }
208
209    if (transport_buf_len == TRANSPORT_BUF_SIZE) {
210        r = _command_data(transport, transport_buf, TRANSPORT_BUF_SIZE);
211        if (r != TRANSPORT_BUF_SIZE) {
212            return -1;
213        }
214        transport_buf_len = 0;
215    }
216
217    if (len > TRANSPORT_BUF_SIZE) {
218        if (transport_buf_len > 0) {
219            g_error = "internal error: transport_buf not empty";
220            return -1;
221        }
222        to_write = round_down(len, TRANSPORT_BUF_SIZE);
223        r = _command_data(transport, ptr, to_write);
224        if (r != to_write) {
225            return -1;
226        }
227        ptr += to_write;
228        len -= to_write;
229    }
230
231    if (len > 0) {
232        if (len > TRANSPORT_BUF_SIZE) {
233            g_error = "internal error: too much left for transport_buf";
234            return -1;
235        }
236        memcpy(transport_buf, ptr, len);
237        transport_buf_len = len;
238    }
239
240    return 0;
241}
242
243static int fb_download_data_sparse_flush(Transport* transport) {
244    if (transport_buf_len > 0) {
245        if (_command_data(transport, transport_buf, transport_buf_len) != transport_buf_len) {
246            return -1;
247        }
248        transport_buf_len = 0;
249    }
250    return 0;
251}
252
253int fb_download_data_sparse(Transport* transport, struct sparse_file* s) {
254    int size = sparse_file_len(s, true, false);
255    if (size <= 0) {
256        return -1;
257    }
258
259    char cmd[64];
260    snprintf(cmd, sizeof(cmd), "download:%08x", size);
261    int r = _command_start(transport, cmd, size, 0);
262    if (r < 0) {
263        return -1;
264    }
265
266    r = sparse_file_callback(s, true, false, fb_download_data_sparse_write, transport);
267    if (r < 0) {
268        return -1;
269    }
270
271    r = fb_download_data_sparse_flush(transport);
272    if (r < 0) {
273        return -1;
274    }
275
276    return _command_end(transport);
277}
278