api: storage: Add the missing write operation support

API_dev_write(va_list ap) is currently lacking the write support
to storage devices because, historically, those devices did not
implement block_write()

The solution has been tested by loading and booting a (patched)
GRUB instance in a QEMU vexpress-a9 environment. The disk write
operations were triggered with GRUB's save_env command.

Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@gmail.com>
diff --git a/api/api.c b/api/api.c
index bc9454e..71fa038 100644
--- a/api/api.c
+++ b/api/api.c
@@ -295,27 +295,31 @@
 
 
 /*
- * Notice: this is for sending network packets only, as U-Boot does not
- * support writing to storage at the moment (12.2007)
- *
  * pseudo signature:
  *
  * int API_dev_write(
  *	struct device_info *di,
  *	void *buf,
- *	int *len
+ *	int *len,
+ *	unsigned long *start
  * )
  *
  * buf:	ptr to buffer from where to get the data to send
  *
- * len: length of packet to be sent (in bytes)
+ * len: ptr to length to be read
+ *      - network: len of packet to be sent (in bytes)
+ *      - storage: # of blocks to write (can vary in size depending on define)
  *
+ * start: ptr to start block (only used for storage devices, ignored for
+ *        network)
  */
 static int API_dev_write(va_list ap)
 {
 	struct device_info *di;
 	void *buf;
-	int *len;
+	lbasize_t *len_stor, act_len_stor;
+	lbastart_t *start;
+	int *len_net;
 	int err = 0;
 
 	/* 1. arg is ptr to the device_info struct */
@@ -333,23 +337,36 @@
 	if (buf == NULL)
 		return API_EINVAL;
 
-	/* 3. arg is length of buffer */
-	len = (int *)va_arg(ap, uintptr_t);
-	if (len == NULL)
-		return API_EINVAL;
-	if (*len <= 0)
-		return API_EINVAL;
+	if (di->type & DEV_TYP_STOR) {
+		/* 3. arg - ptr to var with # of blocks to write */
+		len_stor = (lbasize_t *)va_arg(ap, uintptr_t);
+		if (!len_stor)
+			return API_EINVAL;
+		if (*len_stor <= 0)
+			return API_EINVAL;
 
-	if (di->type & DEV_TYP_STOR)
-		/*
-		 * write to storage is currently not supported by U-Boot:
-		 * no storage device implements block_write() method
-		 */
-		return API_ENODEV;
+		/* 4. arg - ptr to var with start block */
+		start = (lbastart_t *)va_arg(ap, uintptr_t);
 
-	else if (di->type & DEV_TYP_NET)
-		err = dev_write_net(di->cookie, buf, *len);
-	else
+		act_len_stor = dev_write_stor(di->cookie, buf, *len_stor, *start);
+		if (act_len_stor != *len_stor) {
+			debugf("write @ %llu: done %llu out of %llu blocks",
+				   (uint64_t)blk, (uint64_t)act_len_stor,
+				   (uint64_t)len_stor);
+			return API_EIO;
+		}
+
+	} else if (di->type & DEV_TYP_NET) {
+		/* 3. arg points to the var with length of packet to write */
+		len_net = (int *)va_arg(ap, uintptr_t);
+		if (!len_net)
+			return API_EINVAL;
+		if (*len_net <= 0)
+			return API_EINVAL;
+
+		err = dev_write_net(di->cookie, buf, *len_net);
+
+	} else
 		err = API_ENODEV;
 
 	return err;
diff --git a/api/api_private.h b/api/api_private.h
index 8d97ca9..07fd50a 100644
--- a/api/api_private.h
+++ b/api/api_private.h
@@ -22,6 +22,7 @@
 int	dev_close_net(void *);
 
 lbasize_t	dev_read_stor(void *, void *, lbasize_t, lbastart_t);
+lbasize_t	dev_write_stor(void *, void *, lbasize_t, lbastart_t);
 int		dev_read_net(void *, void *, int);
 int		dev_write_net(void *, void *, int);
 
diff --git a/api/api_storage.c b/api/api_storage.c
index 2b90c18..7ae03ac 100644
--- a/api/api_storage.c
+++ b/api/api_storage.c
@@ -349,3 +349,27 @@
 	return dd->block_read(dd, start, len, buf);
 #endif	/* defined(CONFIG_BLK) */
 }
+
+
+lbasize_t dev_write_stor(void *cookie, void *buf, lbasize_t len, lbastart_t start)
+{
+	struct blk_desc *dd = (struct blk_desc *)cookie;
+	int type = dev_stor_type(dd);
+
+	if (type == ENUM_MAX)
+		return 0;
+
+	if (!dev_stor_is_valid(type, dd))
+		return 0;
+
+#ifdef CONFIG_BLK
+	return blk_dwrite(dd, start, len, buf);
+#else
+	if (dd->block_write == NULL) {
+		debugf("no block_write() for device 0x%08x\n", cookie);
+		return 0;
+	}
+
+	return dd->block_write(dd, start, len, buf);
+#endif	/* defined(CONFIG_BLK) */
+}