summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/test_firmware.c127
1 files changed, 122 insertions, 5 deletions
diff --git a/lib/test_firmware.c b/lib/test_firmware.c
index 2b8c56d7bf37..76115c1a2629 100644
--- a/lib/test_firmware.c
+++ b/lib/test_firmware.c
@@ -117,12 +117,18 @@ struct test_config {
struct device *device);
};
+struct upload_inject_err {
+ const char *prog;
+ enum fw_upload_err err_code;
+};
+
struct test_firmware_upload {
char *name;
struct list_head node;
char *buf;
size_t size;
bool cancel_request;
+ struct upload_inject_err inject;
struct fw_upload *fwl;
};
@@ -1067,20 +1073,105 @@ static void upload_release_all(void)
test_fw_config->upload_name = NULL;
}
+/*
+ * This table is replicated from .../firmware_loader/sysfs_upload.c
+ * and needs to be kept in sync.
+ */
+static const char * const fw_upload_err_str[] = {
+ [FW_UPLOAD_ERR_NONE] = "none",
+ [FW_UPLOAD_ERR_HW_ERROR] = "hw-error",
+ [FW_UPLOAD_ERR_TIMEOUT] = "timeout",
+ [FW_UPLOAD_ERR_CANCELED] = "user-abort",
+ [FW_UPLOAD_ERR_BUSY] = "device-busy",
+ [FW_UPLOAD_ERR_INVALID_SIZE] = "invalid-file-size",
+ [FW_UPLOAD_ERR_RW_ERROR] = "read-write-error",
+ [FW_UPLOAD_ERR_WEAROUT] = "flash-wearout",
+};
+
+static void upload_err_inject_error(struct test_firmware_upload *tst,
+ const u8 *p, const char *prog)
+{
+ enum fw_upload_err err;
+
+ for (err = FW_UPLOAD_ERR_NONE + 1; err < FW_UPLOAD_ERR_MAX; err++) {
+ if (strncmp(p, fw_upload_err_str[err],
+ strlen(fw_upload_err_str[err])) == 0) {
+ tst->inject.prog = prog;
+ tst->inject.err_code = err;
+ return;
+ }
+ }
+}
+
+static void upload_err_inject_prog(struct test_firmware_upload *tst,
+ const u8 *p)
+{
+ static const char * const progs[] = {
+ "preparing:", "transferring:", "programming:"
+ };
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(progs); i++) {
+ if (strncmp(p, progs[i], strlen(progs[i])) == 0) {
+ upload_err_inject_error(tst, p + strlen(progs[i]),
+ progs[i]);
+ return;
+ }
+ }
+}
+
+#define FIVE_MINUTES_MS (5 * 60 * 1000)
+static enum fw_upload_err
+fw_upload_wait_on_cancel(struct test_firmware_upload *tst)
+{
+ int ms_delay;
+
+ for (ms_delay = 0; ms_delay < FIVE_MINUTES_MS; ms_delay += 100) {
+ msleep(100);
+ if (tst->cancel_request)
+ return FW_UPLOAD_ERR_CANCELED;
+ }
+ return FW_UPLOAD_ERR_NONE;
+}
+
static enum fw_upload_err test_fw_upload_prepare(struct fw_upload *fwl,
const u8 *data, u32 size)
{
struct test_firmware_upload *tst = fwl->dd_handle;
+ enum fw_upload_err ret = FW_UPLOAD_ERR_NONE;
+ const char *progress = "preparing:";
tst->cancel_request = false;
- if (!size || size > TEST_UPLOAD_MAX_SIZE)
- return FW_UPLOAD_ERR_INVALID_SIZE;
+ if (!size || size > TEST_UPLOAD_MAX_SIZE) {
+ ret = FW_UPLOAD_ERR_INVALID_SIZE;
+ goto err_out;
+ }
+
+ if (strncmp(data, "inject:", strlen("inject:")) == 0)
+ upload_err_inject_prog(tst, data + strlen("inject:"));
memset(tst->buf, 0, TEST_UPLOAD_MAX_SIZE);
tst->size = size;
- return FW_UPLOAD_ERR_NONE;
+ if (tst->inject.err_code == FW_UPLOAD_ERR_NONE ||
+ strncmp(tst->inject.prog, progress, strlen(progress)) != 0)
+ return FW_UPLOAD_ERR_NONE;
+
+ if (tst->inject.err_code == FW_UPLOAD_ERR_CANCELED)
+ ret = fw_upload_wait_on_cancel(tst);
+ else
+ ret = tst->inject.err_code;
+
+err_out:
+ /*
+ * The cleanup op only executes if the prepare op succeeds.
+ * If the prepare op fails, it must do it's own clean-up.
+ */
+ tst->inject.err_code = FW_UPLOAD_ERR_NONE;
+ tst->inject.prog = NULL;
+
+ return ret;
}
static enum fw_upload_err test_fw_upload_write(struct fw_upload *fwl,
@@ -1088,6 +1179,7 @@ static enum fw_upload_err test_fw_upload_write(struct fw_upload *fwl,
u32 size, u32 *written)
{
struct test_firmware_upload *tst = fwl->dd_handle;
+ const char *progress = "transferring:";
u32 blk_size;
if (tst->cancel_request)
@@ -1097,17 +1189,33 @@ static enum fw_upload_err test_fw_upload_write(struct fw_upload *fwl,
memcpy(tst->buf + offset, data + offset, blk_size);
*written = blk_size;
- return FW_UPLOAD_ERR_NONE;
+
+ if (tst->inject.err_code == FW_UPLOAD_ERR_NONE ||
+ strncmp(tst->inject.prog, progress, strlen(progress)) != 0)
+ return FW_UPLOAD_ERR_NONE;
+
+ if (tst->inject.err_code == FW_UPLOAD_ERR_CANCELED)
+ return fw_upload_wait_on_cancel(tst);
+
+ return tst->inject.err_code;
}
static enum fw_upload_err test_fw_upload_complete(struct fw_upload *fwl)
{
struct test_firmware_upload *tst = fwl->dd_handle;
+ const char *progress = "programming:";
if (tst->cancel_request)
return FW_UPLOAD_ERR_CANCELED;
- return FW_UPLOAD_ERR_NONE;
+ if (tst->inject.err_code == FW_UPLOAD_ERR_NONE ||
+ strncmp(tst->inject.prog, progress, strlen(progress)) != 0)
+ return FW_UPLOAD_ERR_NONE;
+
+ if (tst->inject.err_code == FW_UPLOAD_ERR_CANCELED)
+ return fw_upload_wait_on_cancel(tst);
+
+ return tst->inject.err_code;
}
static void test_fw_upload_cancel(struct fw_upload *fwl)
@@ -1117,11 +1225,20 @@ static void test_fw_upload_cancel(struct fw_upload *fwl)
tst->cancel_request = true;
}
+static void test_fw_cleanup(struct fw_upload *fwl)
+{
+ struct test_firmware_upload *tst = fwl->dd_handle;
+
+ tst->inject.err_code = FW_UPLOAD_ERR_NONE;
+ tst->inject.prog = NULL;
+}
+
static const struct fw_upload_ops upload_test_ops = {
.prepare = test_fw_upload_prepare,
.write = test_fw_upload_write,
.poll_complete = test_fw_upload_complete,
.cancel = test_fw_upload_cancel,
+ .cleanup = test_fw_cleanup
};
static ssize_t upload_register_store(struct device *dev,