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 900a844d157434cf9784e82ef0d48f2a7ba11328..a298c2c43999bd0970ba3e7ac55bbb554a97a890 100644 (file)
@@ -9,6 +9,7 @@
 #include <common.h>
 #include <env.h>
 #include <errno.h>
+#include <log.h>
 #include <malloc.h>
 #include <mmc.h>
 #include <fat.h>
@@ -21,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
@@ -42,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;
@@ -53,6 +85,54 @@ 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;
@@ -69,7 +149,11 @@ 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");
                pr_err("(partition table does not match dfu_alt_info?)\n");
@@ -83,6 +167,7 @@ done:
 
 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)
 {
@@ -100,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;
 
@@ -118,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;
 }
 
@@ -205,6 +295,7 @@ int dfu_transaction_initiate(struct dfu_entity *dfu, bool read)
        }
 
        dfu->inited = 1;
+       dfu_initiated_callback(dfu);
 
        return 0;
 }
@@ -224,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;
@@ -338,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;
 
@@ -402,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;
@@ -411,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);
@@ -506,7 +607,7 @@ int dfu_config_entities(char *env, char *interface, char *devstr)
 const char *dfu_get_dev_type(enum dfu_device_type t)
 {
        const char *const dev_t[] = {NULL, "eMMC", "OneNAND", "NAND", "RAM",
-                                    "SF"};
+                                    "SF", "MTD", "VIRT"};
        return dev_t[t];
 }