X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=drivers%2Fdfu%2Fdfu.c;h=8bd5216017a5689f544266b2f85a81f88e6cc2ab;hb=febabe3ed4f422a39e461ba4c2aff5f0fde6e4d2;hp=675162d927d8863a96952f407c0fced396e9c48c;hpb=7a1af7a79bd79ded6a78d0c1afdbc3353669e313;p=oweals%2Fu-boot.git diff --git a/drivers/dfu/dfu.c b/drivers/dfu/dfu.c index 675162d927..8bd5216017 100644 --- a/drivers/dfu/dfu.c +++ b/drivers/dfu/dfu.c @@ -1,13 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * dfu.c -- DFU back-end routines * * Copyright (C) 2012 Samsung Electronics * author: Lukasz Majewski - * - * SPDX-License-Identifier: GPL-2.0+ */ #include +#include #include #include #include @@ -35,7 +35,11 @@ static struct hash_algo *dfu_hash_algo; */ __weak bool dfu_usb_get_reset(void) { +#ifdef CONFIG_SPL_DFU_NO_RESET + return false; +#else return true; +#endif } static int dfu_find_alt_num(const char *s) @@ -49,34 +53,89 @@ static int dfu_find_alt_num(const char *s) return ++i; } +/* + * treat dfu_alt_info with several interface information + * to allow DFU on several device with one command, + * the string format is + * interface devstring'='alternate list (';' separated) + * and each interface separated by '&' + */ +int dfu_config_interfaces(char *env) +{ + struct dfu_entity *dfu; + char *s, *i, *d, *a, *part; + int ret = -EINVAL; + int n = 1; + + s = env; + for (; *s; s++) { + if (*s == ';') + n++; + if (*s == '&') + n++; + } + ret = dfu_alt_init(n, &dfu); + if (ret) + return ret; + + s = env; + while (s) { + ret = -EINVAL; + i = strsep(&s, " "); + if (!i) + break; + d = strsep(&s, "="); + if (!d) + break; + a = strsep(&s, "&"); + if (!a) + a = s; + do { + part = strsep(&a, ";"); + ret = dfu_alt_add(dfu, i, d, part); + if (ret) + return ret; + } while (a); + } + + return ret; +} + int dfu_init_env_entities(char *interface, char *devstr) { const char *str_env; char *env_bkp; - int ret; + int ret = 0; #ifdef CONFIG_SET_DFU_ALT_INFO set_dfu_alt_info(interface, devstr); #endif - str_env = getenv("dfu_alt_info"); + str_env = env_get("dfu_alt_info"); if (!str_env) { - error("\"dfu_alt_info\" env variable not defined!\n"); + pr_err("\"dfu_alt_info\" env variable not defined!\n"); return -EINVAL; } env_bkp = strdup(str_env); - ret = dfu_config_entities(env_bkp, interface, devstr); + if (!interface && !devstr) + ret = dfu_config_interfaces(env_bkp); + else + ret = dfu_config_entities(env_bkp, interface, devstr); + if (ret) { - error("DFU entities configuration failed!\n"); - return ret; + pr_err("DFU entities configuration failed!\n"); + pr_err("(partition table does not match dfu_alt_info?)\n"); + goto done; } +done: free(env_bkp); - return 0; + return ret; } static unsigned char *dfu_buf; -static unsigned long dfu_buf_size = CONFIG_SYS_DFU_DATA_BUF_SIZE; +static unsigned long dfu_buf_size; +static enum dfu_device_type dfu_buf_device_type; unsigned char *dfu_free_buf(void) { @@ -94,10 +153,14 @@ unsigned char *dfu_get_buf(struct dfu_entity *dfu) { char *s; + /* manage several entity with several contraint */ + if (dfu_buf && dfu->dev_type != dfu_buf_device_type) + dfu_free_buf(); + if (dfu_buf != NULL) return dfu_buf; - s = getenv("dfu_bufsiz"); + s = env_get("dfu_bufsiz"); if (s) dfu_buf_size = (unsigned long)simple_strtol(s, NULL, 0); @@ -112,6 +175,7 @@ unsigned char *dfu_get_buf(struct dfu_entity *dfu) printf("%s: Could not memalign 0x%lx bytes\n", __func__, dfu_buf_size); + dfu_buf_device_type = dfu->dev_type; return dfu_buf; } @@ -119,7 +183,7 @@ static char *dfu_get_hash_algo(void) { char *s; - s = getenv("dfu_hash_algo"); + s = env_get("dfu_hash_algo"); if (!s) return NULL; @@ -128,7 +192,7 @@ static char *dfu_get_hash_algo(void) return s; } - error("DFU hash method: %s not supported!\n", s); + pr_err("DFU hash method: %s not supported!\n", s); return NULL; } @@ -161,19 +225,48 @@ static int dfu_write_buffer_drain(struct dfu_entity *dfu) return ret; } -void dfu_write_transaction_cleanup(struct dfu_entity *dfu) +void dfu_transaction_cleanup(struct dfu_entity *dfu) { /* clear everything */ - dfu_free_buf(); dfu->crc = 0; dfu->offset = 0; dfu->i_blk_seq_num = 0; - dfu->i_buf_start = dfu_buf; - dfu->i_buf_end = dfu_buf; + dfu->i_buf_start = dfu_get_buf(dfu); + dfu->i_buf_end = dfu->i_buf_start; dfu->i_buf = dfu->i_buf_start; + dfu->r_left = 0; + dfu->b_left = 0; + dfu->bad_skip = 0; + dfu->inited = 0; } +int dfu_transaction_initiate(struct dfu_entity *dfu, bool read) +{ + int ret = 0; + + if (dfu->inited) + return 0; + + dfu_transaction_cleanup(dfu); + + if (dfu->i_buf_start == NULL) + return -ENOMEM; + + dfu->i_buf_end = dfu->i_buf_start + dfu_get_buf_size(); + + if (read) { + ret = dfu->get_medium_size(dfu, &dfu->r_left); + if (ret < 0) + return ret; + debug("%s: %s %lld [B]\n", __func__, dfu->name, dfu->r_left); + } + + dfu->inited = 1; + + return 0; +} + int dfu_flush(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num) { int ret = 0; @@ -189,7 +282,7 @@ int dfu_flush(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num) printf("\nDFU complete %s: 0x%08x\n", dfu_hash_algo->name, dfu->crc); - dfu_write_transaction_cleanup(dfu); + dfu_transaction_cleanup(dfu); return ret; } @@ -202,25 +295,14 @@ int dfu_write(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num) __func__, dfu->name, buf, size, blk_seq_num, dfu->offset, (unsigned long)(dfu->i_buf - dfu->i_buf_start)); - if (!dfu->inited) { - /* initial state */ - dfu->crc = 0; - dfu->offset = 0; - dfu->bad_skip = 0; - dfu->i_blk_seq_num = 0; - dfu->i_buf_start = dfu_get_buf(dfu); - if (dfu->i_buf_start == NULL) - return -ENOMEM; - dfu->i_buf_end = dfu_get_buf(dfu) + dfu_buf_size; - dfu->i_buf = dfu->i_buf_start; - - dfu->inited = 1; - } + ret = dfu_transaction_initiate(dfu, false); + if (ret < 0) + return ret; if (dfu->i_blk_seq_num != blk_seq_num) { printf("%s: Wrong sequence number! [%d] [%d]\n", __func__, dfu->i_blk_seq_num, blk_seq_num); - dfu_write_transaction_cleanup(dfu); + dfu_transaction_cleanup(dfu); return -1; } @@ -244,16 +326,16 @@ int dfu_write(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num) if ((dfu->i_buf + size) > dfu->i_buf_end) { ret = dfu_write_buffer_drain(dfu); if (ret) { - dfu_write_transaction_cleanup(dfu); + dfu_transaction_cleanup(dfu); return ret; } } /* we should be in buffer now (if not then size too large) */ if ((dfu->i_buf + size) > dfu->i_buf_end) { - error("Buffer overflow! (0x%p + 0x%x > 0x%p)\n", dfu->i_buf, + pr_err("Buffer overflow! (0x%p + 0x%x > 0x%p)\n", dfu->i_buf, size, dfu->i_buf_end); - dfu_write_transaction_cleanup(dfu); + dfu_transaction_cleanup(dfu); return -1; } @@ -264,7 +346,7 @@ int dfu_write(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num) if (size == 0 || (dfu->i_buf + size) > dfu->i_buf_end) { ret = dfu_write_buffer_drain(dfu); if (ret) { - dfu_write_transaction_cleanup(dfu); + dfu_transaction_cleanup(dfu); return ret; } } @@ -331,39 +413,9 @@ int dfu_read(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num) debug("%s: name: %s buf: 0x%p size: 0x%x p_num: 0x%x i_buf: 0x%p\n", __func__, dfu->name, buf, size, blk_seq_num, dfu->i_buf); - if (!dfu->inited) { - dfu->i_buf_start = dfu_get_buf(dfu); - if (dfu->i_buf_start == NULL) - return -ENOMEM; - - dfu->r_left = dfu->get_medium_size(dfu); - if (dfu->r_left < 0) - return dfu->r_left; - switch (dfu->layout) { - case DFU_RAW_ADDR: - case DFU_RAM_ADDR: - break; - default: - if (dfu->r_left > dfu_buf_size) { - printf("%s: File too big for buffer\n", - __func__); - return -EOVERFLOW; - } - } - - debug("%s: %s %ld [B]\n", __func__, dfu->name, dfu->r_left); - - dfu->i_blk_seq_num = 0; - dfu->crc = 0; - dfu->offset = 0; - dfu->i_buf_end = dfu_get_buf(dfu) + dfu_buf_size; - dfu->i_buf = dfu->i_buf_start; - dfu->b_left = 0; - - dfu->bad_skip = 0; - - dfu->inited = 1; - } + ret = dfu_transaction_initiate(dfu, true); + if (ret < 0) + return ret; if (dfu->i_blk_seq_num != blk_seq_num) { printf("%s: Wrong sequence number! [%d] [%d]\n", @@ -385,18 +437,7 @@ int dfu_read(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num) dfu_hash_algo->name, dfu->crc); puts("\nUPLOAD ... done\nCtrl+C to exit ...\n"); - dfu_free_buf(); - dfu->i_blk_seq_num = 0; - dfu->crc = 0; - dfu->offset = 0; - dfu->i_buf_start = dfu_buf; - dfu->i_buf_end = dfu_buf; - dfu->i_buf = dfu->i_buf_start; - dfu->b_left = 0; - - dfu->bad_skip = 0; - - dfu->inited = 0; + dfu_transaction_cleanup(dfu); } return ret; @@ -433,6 +474,7 @@ static int dfu_fill_entity(struct dfu_entity *dfu, char *s, int alt, __func__, interface); return -1; } + dfu_get_buf(dfu); return 0; } @@ -441,6 +483,7 @@ void dfu_free_entities(void) { struct dfu_entity *dfu, *p, *t = NULL; + dfu_free_buf(); list_for_each_entry_safe_reverse(dfu, p, &dfu_list, list) { list_del(&dfu->list); if (dfu->free_entity) @@ -454,13 +497,12 @@ void dfu_free_entities(void) alt_num_cnt = 0; } -int dfu_config_entities(char *env, char *interface, char *devstr) +int dfu_alt_init(int num, struct dfu_entity **dfu) { - struct dfu_entity *dfu; - int i, ret; char *s; + int ret; - dfu_alt_num = dfu_find_alt_num(env); + dfu_alt_num = num; debug("%s: dfu_alt_num=%d\n", __func__, dfu_alt_num); dfu_hash_algo = NULL; @@ -468,22 +510,52 @@ int dfu_config_entities(char *env, char *interface, char *devstr) if (s) { ret = hash_lookup_algo(s, &dfu_hash_algo); if (ret) - error("Hash algorithm %s not supported\n", s); + pr_err("Hash algorithm %s not supported\n", s); } - dfu = calloc(sizeof(*dfu), dfu_alt_num); - if (!dfu) + *dfu = calloc(sizeof(struct dfu_entity), dfu_alt_num); + if (!*dfu) + return -1; + + return 0; +} + +int dfu_alt_add(struct dfu_entity *dfu, char *interface, char *devstr, char *s) +{ + struct dfu_entity *p_dfu; + int ret; + + if (alt_num_cnt >= dfu_alt_num) + return -1; + + p_dfu = &dfu[alt_num_cnt]; + ret = dfu_fill_entity(p_dfu, s, alt_num_cnt, interface, devstr); + if (ret) + return -1; + + list_add_tail(&p_dfu->list, &dfu_list); + alt_num_cnt++; + + return 0; +} + +int dfu_config_entities(char *env, char *interface, char *devstr) +{ + struct dfu_entity *dfu; + int i, ret; + char *s; + + ret = dfu_alt_init(dfu_find_alt_num(env), &dfu); + if (ret) return -1; - for (i = 0; i < dfu_alt_num; i++) { + for (i = 0; i < dfu_alt_num; i++) { s = strsep(&env, ";"); - ret = dfu_fill_entity(&dfu[i], s, alt_num_cnt, interface, - devstr); - if (ret) + ret = dfu_alt_add(dfu, interface, devstr, s); + if (ret) { + /* We will free "dfu" in dfu_free_entities() */ return -1; - - list_add_tail(&dfu[i].list, &dfu_list); - alt_num_cnt++; + } } return 0; @@ -491,14 +563,15 @@ int dfu_config_entities(char *env, char *interface, char *devstr) const char *dfu_get_dev_type(enum dfu_device_type t) { - const char *dev_t[] = {NULL, "eMMC", "OneNAND", "NAND", "RAM" }; + const char *const dev_t[] = {NULL, "eMMC", "OneNAND", "NAND", "RAM", + "SF"}; return dev_t[t]; } const char *dfu_get_layout(enum dfu_layout l) { - const char *dfu_layout[] = {NULL, "RAW_ADDR", "FAT", "EXT2", - "EXT3", "EXT4", "RAM_ADDR" }; + const char *const dfu_layout[] = {NULL, "RAW_ADDR", "FAT", "EXT2", + "EXT3", "EXT4", "RAM_ADDR" }; return dfu_layout[l]; } @@ -568,3 +641,40 @@ int dfu_get_alt(char *name) return -ENODEV; } + +int dfu_write_from_mem_addr(struct dfu_entity *dfu, void *buf, int size) +{ + unsigned long dfu_buf_size, write, left = size; + int i, ret = 0; + void *dp = buf; + + /* + * Here we must call dfu_get_buf(dfu) first to be sure that dfu_buf_size + * has been properly initialized - e.g. if "dfu_bufsiz" has been taken + * into account. + */ + dfu_get_buf(dfu); + dfu_buf_size = dfu_get_buf_size(); + debug("%s: dfu buf size: %lu\n", __func__, dfu_buf_size); + + for (i = 0; left > 0; i++) { + write = min(dfu_buf_size, left); + + debug("%s: dp: 0x%p left: %lu write: %lu\n", __func__, + dp, left, write); + ret = dfu_write(dfu, dp, write, i); + if (ret) { + pr_err("DFU write failed\n"); + return ret; + } + + dp += write; + left -= write; + } + + ret = dfu_flush(dfu, NULL, 0, i); + if (ret) + pr_err("DFU flush failed!"); + + return ret; +}