summaryrefslogtreecommitdiffstats
path: root/drivers/usb/gadget/f_fastboot.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/gadget/f_fastboot.c')
-rw-r--r--drivers/usb/gadget/f_fastboot.c236
1 files changed, 153 insertions, 83 deletions
diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c
index d61920e42d..bdf0c807df 100644
--- a/drivers/usb/gadget/f_fastboot.c
+++ b/drivers/usb/gadget/f_fastboot.c
@@ -80,6 +80,7 @@ struct f_fastboot {
const char *filename, const void *buf, size_t len);
int download_fd;
void *buf;
+ bool active;
size_t download_bytes;
size_t download_size;
@@ -182,15 +183,8 @@ static struct usb_gadget_strings *fastboot_strings[] = {
static void rx_handler_command(struct usb_ep *ep, struct usb_request *req);
-static int in_req_complete;
-
static void fastboot_complete(struct usb_ep *ep, struct usb_request *req)
{
- int status = req->status;
-
- in_req_complete = 1;
-
- pr_debug("status: %d ep '%s' trans: %d\n", status, ep->name, req->actual);
}
static struct usb_request *fastboot_alloc_request(struct usb_ep *ep)
@@ -404,7 +398,6 @@ static int fastboot_bind(struct usb_configuration *c, struct usb_function *f)
return ret;
}
f_fb->in_req->complete = fastboot_complete;
- f_fb->out_req->context = f_fb;
ret = usb_assign_descriptors(f, fb_fs_descs, fb_hs_descs, NULL);
if (ret)
@@ -434,6 +427,8 @@ static void fastboot_unbind(struct usb_configuration *c, struct usb_function *f)
list_del(&var->list);
free(var);
}
+
+ f_fb->active = false;
}
static void fastboot_disable(struct usb_function *f)
@@ -486,13 +481,36 @@ err:
return ret;
}
+static struct f_fastboot *g_f_fb;
+
static void fastboot_free_func(struct usb_function *f)
{
struct f_fastboot *f_fb = container_of(f, struct f_fastboot, func);
+ if (g_f_fb == f_fb)
+ g_f_fb = NULL;
+
free(f_fb);
}
+/*
+ * A "oem exec bootm" or similar commands will stop barebox. Tell the
+ * fastboot command on the other side so that it doesn't run into a
+ * timeout.
+ */
+static void fastboot_shutdown(void)
+{
+ struct f_fastboot *f_fb = g_f_fb;
+
+ if (!f_fb || !f_fb->active)
+ return;
+
+ fastboot_tx_print(f_fb, FASTBOOT_MSG_INFO, "barebox shutting down");
+ fastboot_tx_print(f_fb, FASTBOOT_MSG_OKAY, "");
+}
+
+early_exitcall(fastboot_shutdown);
+
static struct usb_function *fastboot_alloc_func(struct usb_function_instance *fi)
{
struct f_fastboot *f_fb;
@@ -509,6 +527,9 @@ static struct usb_function *fastboot_alloc_func(struct usb_function_instance *fi
f_fb->func.unbind = fastboot_unbind;
f_fb->func.free_func = fastboot_free_func;
+ if (!g_f_fb)
+ g_f_fb = f_fb;
+
return &f_fb->func;
}
@@ -540,30 +561,62 @@ static int fastboot_tx_write(struct f_fastboot *f_fb, const char *buffer, unsign
memcpy(in_req->buf, buffer, buffer_size);
in_req->length = buffer_size;
- in_req_complete = 0;
+
ret = usb_ep_queue(f_fb->in_ep, in_req);
if (ret)
pr_err("Error %d on queue\n", ret);
start = get_time_ns();
- while (!in_req_complete) {
+ while (in_req->status == -EINPROGRESS) {
if (is_timeout(start, 2 * SECOND))
return -ETIMEDOUT;
usb_gadget_poll();
}
+ if (in_req->status)
+ pr_err("Failed to send answer: %d\n", in_req->status);
+
return 0;
}
-int fastboot_tx_print(struct f_fastboot *f_fb, const char *fmt, ...)
+static char *fastboot_msg[] = {
+ [FASTBOOT_MSG_OKAY] = "OKAY",
+ [FASTBOOT_MSG_FAIL] = "FAIL",
+ [FASTBOOT_MSG_INFO] = "INFO",
+ [FASTBOOT_MSG_DATA] = "DATA",
+};
+
+int fastboot_tx_print(struct f_fastboot *f_fb, enum fastboot_msg_type type,
+ const char *fmt, ...)
{
+ struct va_format vaf;
char buf[64];
va_list ap;
int n;
+ const char *msg = fastboot_msg[type];
va_start(ap, fmt);
- n = vsnprintf(buf, 64, fmt, ap);
+ vaf.fmt = fmt;
+ vaf.va = ≈
+
+ n = snprintf(buf, 64, "%s%pV", msg, &vaf);
+
+ switch (type) {
+ case FASTBOOT_MSG_OKAY:
+ f_fb->active = false;
+ break;
+ case FASTBOOT_MSG_FAIL:
+ f_fb->active = false;
+ pr_err("%pV\n", &vaf);
+ break;
+ case FASTBOOT_MSG_INFO:
+ pr_info("%pV\n", &vaf);
+ break;
+ case FASTBOOT_MSG_DATA:
+ break;
+ }
+
va_end(ap);
if (n > 64)
@@ -572,15 +625,9 @@ int fastboot_tx_print(struct f_fastboot *f_fb, const char *fmt, ...)
return fastboot_tx_write(f_fb, buf, n);
}
-static void compl_do_reset(struct usb_ep *ep, struct usb_request *req)
-{
- restart_machine();
-}
-
static void cb_reboot(struct f_fastboot *f_fb, const char *cmd)
{
- f_fb->in_req->complete = compl_do_reset;
- fastboot_tx_print(f_fb, "OKAY");
+ restart_machine();
}
static int strcmp_l1(const char *s1, const char *s2)
@@ -598,20 +645,21 @@ static void cb_getvar(struct f_fastboot *f_fb, const char *cmd)
if (!strcmp_l1(cmd, "all")) {
list_for_each_entry(var, &f_fb->variables, list) {
- fastboot_tx_print(f_fb, "INFO%s: %s", var->name, var->value);
+ fastboot_tx_print(f_fb, FASTBOOT_MSG_INFO, "%s: %s",
+ var->name, var->value);
}
- fastboot_tx_print(f_fb, "OKAY");
+ fastboot_tx_print(f_fb, FASTBOOT_MSG_OKAY, "");
return;
}
list_for_each_entry(var, &f_fb->variables, list) {
if (!strcmp(cmd, var->name)) {
- fastboot_tx_print(f_fb, "OKAY%s", var->value);
+ fastboot_tx_print(f_fb, FASTBOOT_MSG_OKAY, var->value);
return;
}
}
- fastboot_tx_print(f_fb, "OKAY");
+ fastboot_tx_print(f_fb, FASTBOOT_MSG_OKAY, "");
}
static int rx_bytes_expected(struct f_fastboot *f_fb)
@@ -640,7 +688,7 @@ static void rx_handler_dl_image(struct usb_ep *ep, struct usb_request *req)
} else {
ret = write(f_fb->download_fd, buffer, req->actual);
if (ret < 0) {
- fastboot_tx_print(f_fb, "FAIL%s", strerror(-ret));
+ fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, strerror(-ret));
return;
}
}
@@ -657,12 +705,12 @@ static void rx_handler_dl_image(struct usb_ep *ep, struct usb_request *req)
req->length = EP_BUFFER_SIZE;
close(f_fb->download_fd);
- fastboot_tx_print(f_fb, "INFODownloading %d bytes finished",
- f_fb->download_bytes);
+ printf("\n");
- fastboot_tx_print(f_fb, "OKAY");
+ fastboot_tx_print(f_fb, FASTBOOT_MSG_INFO, "Downloading %d bytes finished",
+ f_fb->download_bytes);
- printf("\n");
+ fastboot_tx_print(f_fb, FASTBOOT_MSG_OKAY, "");
}
req->actual = 0;
@@ -674,7 +722,8 @@ static void cb_download(struct f_fastboot *f_fb, const char *cmd)
f_fb->download_size = simple_strtoul(cmd, NULL, 16);
f_fb->download_bytes = 0;
- fastboot_tx_print(f_fb, "INFODownloading %d bytes...", f_fb->download_size);
+ fastboot_tx_print(f_fb, FASTBOOT_MSG_INFO, "Downloading %d bytes...",
+ f_fb->download_size);
init_progression_bar(f_fb->download_size);
@@ -682,28 +731,32 @@ static void cb_download(struct f_fastboot *f_fb, const char *cmd)
free(f_fb->buf);
f_fb->buf = malloc(f_fb->download_size);
if (!f_fb->buf) {
- fastboot_tx_print(f_fb, "FAILnot enough memory");
+ fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL,
+ "not enough memory");
return;
}
} else {
f_fb->download_fd = open(FASTBOOT_TMPFILE, O_WRONLY | O_CREAT | O_TRUNC);
if (f_fb->download_fd < 0) {
- fastboot_tx_print(f_fb, "FAILInternal Error");
+ fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL,
+ "internal error");
return;
}
}
if (!f_fb->download_size) {
- fastboot_tx_print(f_fb, "FAILdata invalid size");
+ fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL,
+ "data invalid size");
} else {
struct usb_request *req = f_fb->out_req;
- fastboot_tx_print(f_fb, "DATA%08x", f_fb->download_size);
+ fastboot_tx_print(f_fb, FASTBOOT_MSG_DATA,
+ "%08x", f_fb->download_size);
req->complete = rx_handler_dl_image;
req->length = rx_bytes_expected(f_fb);
}
}
-static void do_bootm_on_complete(struct usb_ep *ep, struct usb_request *req)
+static void __maybe_unused cb_boot(struct f_fastboot *f_fb, const char *opt)
{
int ret;
struct bootm_data data = {
@@ -711,7 +764,7 @@ static void do_bootm_on_complete(struct usb_ep *ep, struct usb_request *req)
.os_address = UIMAGE_SOME_ADDRESS,
};
- pr_info("Booting kernel..\n");
+ fastboot_tx_print(f_fb, FASTBOOT_MSG_INFO, "Booting kernel..\n");
globalvar_set_match("linux.bootargs.dyn.", "");
globalvar_set_match("bootm.", "");
@@ -719,14 +772,12 @@ static void do_bootm_on_complete(struct usb_ep *ep, struct usb_request *req)
data.os_file = xstrdup(FASTBOOT_TMPFILE);
ret = bootm_boot(&data);
- if (ret)
- pr_err("Booting failed\n");
-}
-static void __maybe_unused cb_boot(struct f_fastboot *f_fb, const char *opt)
-{
- f_fb->in_req->complete = do_bootm_on_complete;
- fastboot_tx_print(f_fb, "OKAY");
+ if (ret)
+ fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, "Booting failed: %s",
+ strerror(-ret));
+ else
+ fastboot_tx_print(f_fb, FASTBOOT_MSG_OKAY, "");
}
static struct mtd_info *get_mtd(struct f_fastboot *f_fb, const char *filename)
@@ -762,7 +813,8 @@ static int do_ubiformat(struct f_fastboot *f_fb, struct mtd_info *mtd,
args.novtbl = 1;
if (!IS_ENABLED(CONFIG_UBIFORMAT)) {
- fastboot_tx_print(f_fb, "FAILubiformat is not available");
+ fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL,
+ "ubiformat is not available");
return -ENODEV;
}
@@ -784,31 +836,37 @@ static int check_ubi(struct f_fastboot *f_fb, struct file_list_entry *fentry,
*/
if (!IS_ERR(mtd) && filetype == filetype_ubi &&
!(fentry->flags & FILE_LIST_FLAG_UBI)) {
- fastboot_tx_print(f_fb, "INFOwriting UBI image to MTD device, "
- "add the 'u' ");
- fastboot_tx_print(f_fb, "INFOflag to the partition description");
- return 0;
+ fastboot_tx_print(f_fb, FASTBOOT_MSG_INFO,
+ "writing UBI image to MTD device, "
+ "add the 'u' ");
+ fastboot_tx_print(f_fb, FASTBOOT_MSG_INFO,
+ "flag to the partition description");
+ return 0;
}
if (!(fentry->flags & FILE_LIST_FLAG_UBI))
return 0;
if (!IS_ENABLED(CONFIG_UBIFORMAT)) {
- fastboot_tx_print(f_fb, "FAILformat not available");
+ fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL,
+ "ubiformat not available");
return -ENOSYS;
}
if (IS_ERR(mtd)) {
- fastboot_tx_print(f_fb, "FAILUBI flag given on non-MTD device");
+ fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL,
+ "UBI flag given on non-MTD device");
return -EINVAL;
}
if (filetype == filetype_ubi) {
- fastboot_tx_print(f_fb, "INFOThis is an UBI image...");
+ fastboot_tx_print(f_fb, FASTBOOT_MSG_INFO,
+ "This is an UBI image...");
return 1;
} else {
- fastboot_tx_print(f_fb, "FAILThis is no UBI image but %s",
- file_type_to_string(filetype));
+ fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL,
+ "This is no UBI image but %s",
+ file_type_to_string(filetype));
return -EINVAL;
}
}
@@ -937,12 +995,14 @@ static void cb_flash(struct f_fastboot *f_fb, const char *cmd)
filetype = file_name_detect_type(FASTBOOT_TMPFILE);
}
- fastboot_tx_print(f_fb, "INFOCopying file to %s...", cmd);
+ fastboot_tx_print(f_fb, FASTBOOT_MSG_INFO, "Copying file to %s...",
+ cmd);
fentry = file_list_entry_by_name(f_fb->files, cmd);
if (!fentry) {
- fastboot_tx_print(f_fb, "FAILNo such partition: %s", cmd);
+ fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, "No such partition: %s",
+ cmd);
ret = -ENOENT;
goto out;
}
@@ -957,20 +1017,18 @@ static void cb_flash(struct f_fastboot *f_fb, const char *cmd)
filename = fentry->filename;
if (filetype == filetype_android_sparse) {
- if (!IS_ENABLED(CONFIG_USB_GADGET_FASTBOOT_SPARSE)) {
- fastboot_tx_print(f_fb, "FAILsparse image not supported");
+ if (!IS_ENABLED(CONFIG_USB_GADGET_FASTBOOT_SPARSE) ||
+ fastboot_download_to_buf(f_fb)) {
+ fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL,
+ "sparse image not supported");
ret = -EOPNOTSUPP;
goto out;
}
- if (fastboot_download_to_buf(f_fb)) {
- fastboot_tx_print(f_fb, "FAILsparse image not supported");
- goto out;
- }
-
ret = fastboot_handle_sparse(f_fb, fentry);
if (ret)
- fastboot_tx_print(f_fb, "FAILwriting sparse image: %s",
+ fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL,
+ "writing sparse image: %s",
strerror(-ret));
goto out;
@@ -988,7 +1046,9 @@ static void cb_flash(struct f_fastboot *f_fb, const char *cmd)
ret = do_ubiformat(f_fb, mtd, sourcefile, f_fb->buf,
f_fb->download_size);
if (ret) {
- fastboot_tx_print(f_fb, "FAILwrite partition: %s", strerror(-ret));
+ fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL,
+ "write partition: %s",
+ strerror(-ret));
goto out;
}
@@ -1006,7 +1066,8 @@ static void cb_flash(struct f_fastboot *f_fb, const char *cmd)
if (!handler)
goto copy;
- fastboot_tx_print(f_fb, "INFOThis is a barebox image...");
+ fastboot_tx_print(f_fb, FASTBOOT_MSG_INFO,
+ "This is a barebox image...");
if (fastboot_download_to_buf(f_fb)) {
data.len = f_fb->download_size;
@@ -1014,7 +1075,8 @@ static void cb_flash(struct f_fastboot *f_fb, const char *cmd)
ret = read_file_2(sourcefile, &data.len, &f_fb->buf,
f_fb->download_size);
if (ret) {
- fastboot_tx_print(f_fb, "FAILreading barebox");
+ fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL,
+ "reading barebox");
goto out;
}
}
@@ -1025,7 +1087,8 @@ static void cb_flash(struct f_fastboot *f_fb, const char *cmd)
ret = barebox_update(&data, handler);
if (ret)
- fastboot_tx_print(f_fb, "FAILupdate barebox: %s", strerror(-ret));
+ fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL,
+ "update barebox: %s", strerror(-ret));
goto out;
}
@@ -1037,11 +1100,12 @@ copy:
ret = copy_file(FASTBOOT_TMPFILE, filename, 1);
if (ret)
- fastboot_tx_print(f_fb, "FAILwrite partition: %s", strerror(-ret));
+ fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL,
+ "write partition: %s", strerror(-ret));
out:
if (!ret)
- fastboot_tx_print(f_fb, "OKAY");
+ fastboot_tx_print(f_fb, FASTBOOT_MSG_OKAY, "");
free(f_fb->buf);
f_fb->buf = NULL;
@@ -1057,7 +1121,7 @@ static void cb_erase(struct f_fastboot *f_fb, const char *cmd)
const char *filename = NULL;
int fd;
- fastboot_tx_print(f_fb, "INFOErasing %s...", cmd);
+ fastboot_tx_print(f_fb, FASTBOOT_MSG_INFO, "Erasing %s...", cmd);
file_list_for_each_entry(f_fb->files, fentry) {
if (!strcmp(cmd, fentry->name)) {
@@ -1067,23 +1131,25 @@ static void cb_erase(struct f_fastboot *f_fb, const char *cmd)
}
if (!filename) {
- fastboot_tx_print(f_fb, "FAILNo such partition: %s", cmd);
+ fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL,
+ "No such partition: %s", cmd);
return;
}
fd = open(filename, O_RDWR);
if (fd < 0)
- fastboot_tx_print(f_fb, "FAIL%s", strerror(-fd));
+ fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, strerror(-fd));
ret = erase(fd, ERASE_SIZE_ALL, 0);
close(fd);
if (ret)
- fastboot_tx_print(f_fb, "FAILcannot erase partition %s: %s",
- filename, strerror(-ret));
+ fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL,
+ "cannot erase partition %s: %s",
+ filename, strerror(-ret));
else
- fastboot_tx_print(f_fb, "OKAY");
+ fastboot_tx_print(f_fb, FASTBOOT_MSG_OKAY, "");
}
struct cmd_dispatch_info {
@@ -1109,7 +1175,8 @@ static void fb_run_command(struct f_fastboot *f_fb, const char *cmdbuf,
}
}
- fastboot_tx_print(f_fb, "FAILunknown command %s", cmdbuf);
+ fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, "unknown command %s",
+ cmdbuf);
}
static void cb_oem_getenv(struct f_fastboot *f_fb, const char *cmd)
@@ -1120,8 +1187,8 @@ static void cb_oem_getenv(struct f_fastboot *f_fb, const char *cmd)
value = getenv(cmd);
- fastboot_tx_print(f_fb, "INFO%s", value ? value : "");
- fastboot_tx_print(f_fb, "OKAY");
+ fastboot_tx_print(f_fb, FASTBOOT_MSG_INFO, value ? value : "");
+ fastboot_tx_print(f_fb, FASTBOOT_MSG_OKAY, "");
}
static void cb_oem_setenv(struct f_fastboot *f_fb, const char *cmd)
@@ -1144,12 +1211,12 @@ static void cb_oem_setenv(struct f_fastboot *f_fb, const char *cmd)
if (ret)
goto out;
- fastboot_tx_print(f_fb, "OKAY");
+ fastboot_tx_print(f_fb, FASTBOOT_MSG_OKAY, "");
out:
free(var);
if (ret)
- fastboot_tx_print(f_fb, "FAIL%s", strerror(-ret));
+ fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, strerror(-ret));
}
static void cb_oem_exec(struct f_fastboot *f_fb, const char *cmd)
@@ -1157,17 +1224,18 @@ static void cb_oem_exec(struct f_fastboot *f_fb, const char *cmd)
int ret;
if (!IS_ENABLED(CONFIG_COMMAND_SUPPORT)) {
- fastboot_tx_print(f_fb, "FAILno command support available");
+ fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL,
+ "no command support available");
return;
}
ret = run_command(cmd);
if (ret < 0)
- fastboot_tx_print(f_fb, "FAIL%s", strerror(-ret));
+ fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, strerror(-ret));
else if (ret > 0)
- fastboot_tx_print(f_fb, "FAIL");
+ fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, "");
else
- fastboot_tx_print(f_fb, "OKAY");
+ fastboot_tx_print(f_fb, FASTBOOT_MSG_OKAY, "");
}
static const struct cmd_dispatch_info cmd_oem_dispatch_info[] = {
@@ -1226,6 +1294,8 @@ static void rx_handler_command(struct usb_ep *ep, struct usb_request *req)
if (req->status != 0)
return;
+ f_fb->active = true;
+
*(cmdbuf + req->actual) = 0;
if (f_fb->cmd_exec) {