Merge tag 'efi-2020-07-rc6' of https://gitlab.denx.de/u-boot/custodians/u-boot-efi
[oweals/u-boot.git] / drivers / dfu / dfu.c
index a3c09334b7b243171994186c2d275b901b406249..a298c2c43999bd0970ba3e7ac55bbb554a97a890 100644 (file)
@@ -7,7 +7,9 @@
  */
 
 #include <common.h>
+#include <env.h>
 #include <errno.h>
+#include <log.h>
 #include <malloc.h>
 #include <mmc.h>
 #include <fat.h>
@@ -20,6 +22,25 @@ static LIST_HEAD(dfu_list);
 static int dfu_alt_num;
 static int alt_num_cnt;
 static struct hash_algo *dfu_hash_algo;
+#ifdef CONFIG_DFU_TIMEOUT
+static unsigned long dfu_timeout = 0;
+#endif
+
+/*
+ * The purpose of the dfu_flush_callback() function is to
+ * provide callback for dfu user
+ */
+__weak void dfu_flush_callback(struct dfu_entity *dfu)
+{
+}
+
+/*
+ * The purpose of the dfu_initiated_callback() function is to
+ * provide callback for dfu user
+ */
+__weak void dfu_initiated_callback(struct dfu_entity *dfu)
+{
+}
 
 /*
  * The purpose of the dfu_usb_get_reset() function is to
@@ -41,6 +62,18 @@ __weak bool dfu_usb_get_reset(void)
 #endif
 }
 
+#ifdef CONFIG_DFU_TIMEOUT
+void dfu_set_timeout(unsigned long timeout)
+{
+       dfu_timeout = timeout;
+}
+
+unsigned long dfu_get_timeout(void)
+{
+       return dfu_timeout;
+}
+#endif
+
 static int dfu_find_alt_num(const char *s)
 {
        int i = 0;
@@ -52,11 +85,59 @@ 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);
@@ -68,18 +149,25 @@ int dfu_init_env_entities(char *interface, char *devstr)
        }
 
        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) {
                pr_err("DFU entities configuration failed!\n");
-               return ret;
+               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;
+static enum dfu_device_type dfu_buf_device_type;
 
 unsigned char *dfu_free_buf(void)
 {
@@ -97,6 +185,10 @@ 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;
 
@@ -115,6 +207,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;
 }
 
@@ -202,6 +295,7 @@ int dfu_transaction_initiate(struct dfu_entity *dfu, bool read)
        }
 
        dfu->inited = 1;
+       dfu_initiated_callback(dfu);
 
        return 0;
 }
@@ -221,6 +315,8 @@ 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_flush_callback(dfu);
+
        dfu_transaction_cleanup(dfu);
 
        return ret;
@@ -335,6 +431,8 @@ static int dfu_read_buffer_fill(struct dfu_entity *dfu, void *buf, int size)
                                debug("%s: Read error!\n", __func__);
                                return ret;
                        }
+                       if (dfu->b_left == 0)
+                               break;
                        dfu->offset += dfu->b_left;
                        dfu->r_left -= dfu->b_left;
 
@@ -399,6 +497,9 @@ static int dfu_fill_entity(struct dfu_entity *dfu, char *s, int alt,
        if (strcmp(interface, "mmc") == 0) {
                if (dfu_fill_entity_mmc(dfu, devstr, s))
                        return -1;
+       } else if (strcmp(interface, "mtd") == 0) {
+               if (dfu_fill_entity_mtd(dfu, devstr, s))
+                       return -1;
        } else if (strcmp(interface, "nand") == 0) {
                if (dfu_fill_entity_nand(dfu, devstr, s))
                        return -1;
@@ -408,6 +509,9 @@ static int dfu_fill_entity(struct dfu_entity *dfu, char *s, int alt,
        } else if (strcmp(interface, "sf") == 0) {
                if (dfu_fill_entity_sf(dfu, devstr, s))
                        return -1;
+       } else if (strcmp(interface, "virt") == 0) {
+               if (dfu_fill_entity_virt(dfu, devstr, s))
+                       return -1;
        } else {
                printf("%s: Device %s not (yet) supported!\n",
                       __func__,  interface);
@@ -436,13 +540,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;
@@ -453,21 +556,49 @@ int dfu_config_entities(char *env, char *interface, char *devstr)
                        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;
-       for (i = 0; i < dfu_alt_num; i++) {
 
+       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++) {
                s = strsep(&env, ";");
-               ret = dfu_fill_entity(&dfu[i], s, alt_num_cnt, interface,
-                                     devstr);
+               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;
@@ -475,14 +606,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", "SF" };
+       const char *const dev_t[] = {NULL, "eMMC", "OneNAND", "NAND", "RAM",
+                                    "SF", "MTD", "VIRT"};
        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];
 }