Work around MEMERASE ioctl for rk3066 compatibility

Due to a kernel bug and no available sources we have to work around the
MEMERASE ioctl - if used, it hangs and never returns. I straced the original
recovery executable and could see that it is simply calling write() with a
bunch of zeroes instead of using MEMERASE.

Added a hack that does the same and now the resulting TWRP recovery image works.

Change-Id: I1b1c1c9e870e350776346bdca5d442c7ef565aa0
diff --git a/Android.mk b/Android.mk
index 725436a..2f8c194 100644
--- a/Android.mk
+++ b/Android.mk
@@ -206,6 +206,10 @@
     LOCAL_C_INCLUDES += system/extras/ext4_utils external/openssl/include
 endif
 
+ifeq ($(TARGET_BOARD_PLATFORM),rk30xx)
+LOCAL_CFLAGS += -DRK3066
+endif
+
 include $(BUILD_EXECUTABLE)
 
 include $(CLEAR_VARS)
diff --git a/mtdutils/Android.mk b/mtdutils/Android.mk
index 12322b4..0c238e1 100644
--- a/mtdutils/Android.mk
+++ b/mtdutils/Android.mk
@@ -7,9 +7,14 @@
 	mtdutils.c \
 	mounts.c 
 
+ifeq ($(TARGET_BOARD_PLATFORM),rk30xx)
+LOCAL_SRC_FILES += rk30hack.c
+endif
+
 LOCAL_MODULE := libmtdutils
 LOCAL_STATIC_LIBRARIES := libcutils libc
 LOCAL_FORCE_STATIC_EXECUTABLE := true
+
 include $(BUILD_STATIC_LIBRARY)
 
 endif	# !TARGET_SIMULATOR
diff --git a/mtdutils/bml_over_mtd.c b/mtdutils/bml_over_mtd.c
index c401792..a68950e 100644
--- a/mtdutils/bml_over_mtd.c
+++ b/mtdutils/bml_over_mtd.c
@@ -31,6 +31,10 @@
 
 #include "mtdutils.h"
 
+#ifdef RK3066
+    #include "rk30hack.h"
+#endif
+
 typedef struct BmlOverMtdReadContext {
 	const MtdPartition *partition;
 	char *buffer;
@@ -518,11 +522,19 @@
 	erase_info.length = size;
 	int retry;
 	for (retry = 0; retry < 2; ++retry) {
+#ifdef RK3066
+		if (rk30_zero_out(fd, pos, size) < 0) {
+			fprintf(stderr, "mtd: erase failure at 0x%08lx (%s)\n",
+					pos, strerror(errno));
+			continue;
+		}
+#else
 		if (ioctl(fd, MEMERASE, &erase_info) < 0) {
 			fprintf(stderr, "mtd: erase failure at 0x%08lx (%s)\n",
 					pos, strerror(errno));
 			continue;
 		}
+#endif
 		if (lseek(fd, pos, SEEK_SET) != pos ||
 				write(fd, data, size) != size) {
 			fprintf(stderr, "mtd: write error at 0x%08lx (%s)\n",
diff --git a/mtdutils/mtdutils.c b/mtdutils/mtdutils.c
index a76b8e9..032b04b 100644
--- a/mtdutils/mtdutils.c
+++ b/mtdutils/mtdutils.c
@@ -28,6 +28,10 @@
 
 #include "mtdutils.h"
 
+#ifdef RK3066
+    #include "rk30hack.h"
+#endif
+
 struct MtdReadContext {
     const MtdPartition *partition;
     char *buffer;
@@ -423,11 +427,19 @@
         erase_info.length = size;
         int retry;
         for (retry = 0; retry < 2; ++retry) {
+#ifdef RK3066
+            if (rk30_zero_out(fd, pos, size) < 0) {
+                fprintf(stderr, "mtd: erase failure at 0x%08lx (%s)\n",
+                        pos, strerror(errno));
+                continue;
+            }
+#else
             if (ioctl(fd, MEMERASE, &erase_info) < 0) {
                 fprintf(stderr, "mtd: erase failure at 0x%08lx (%s)\n",
                         pos, strerror(errno));
                 continue;
             }
+#endif
             if (lseek(fd, pos, SEEK_SET) != pos ||
                 write(fd, data, size) != size) {
                 fprintf(stderr, "mtd: write error at 0x%08lx (%s)\n",
@@ -457,7 +469,11 @@
         // Try to erase it once more as we give up on this block
         add_bad_block_offset(ctx, pos);
         fprintf(stderr, "mtd: skipping write block at 0x%08lx\n", pos);
+#ifdef RK3066
+        rk30_zero_out(fd, pos, size);
+#else
         ioctl(fd, MEMERASE, &erase_info);
+#endif
         pos += partition->erase_size;
     }
 
@@ -527,9 +543,15 @@
         struct erase_info_user erase_info;
         erase_info.start = pos;
         erase_info.length = ctx->partition->erase_size;
+#ifdef RK3066
+        if (rk30_zero_out(ctx->fd, pos, ctx->partition->erase_size) < 0) {
+            fprintf(stderr, "mtd: erase failure at 0x%08lx\n", pos);
+        }
+#else
         if (ioctl(ctx->fd, MEMERASE, &erase_info) < 0) {
             fprintf(stderr, "mtd: erase failure at 0x%08lx\n", pos);
         }
+#endif
         pos += ctx->partition->erase_size;
     }
 
diff --git a/mtdutils/rk30hack.c b/mtdutils/rk30hack.c
new file mode 100644
index 0000000..e9351b5
--- /dev/null
+++ b/mtdutils/rk30hack.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2013, Sergey 'Jin' Bostandzhyan
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* This is a hack for Rockchip rk30xx based devices. The problem is that
+ * the MEMERASE ioctl is failing (hangs and never returns) in their kernel.
+ * The sources are not fully available, so fixing it in the rk30xxnand_ko driver
+ * is not possible.
+ *
+ * I straced the stock recovery application and it seems to avoid this
+ * particular ioctl, instead it is simply writing zeroes using the write() call.
+ *
+ * This workaround does the same and will replace all MEMERASE occurances in
+ * the recovery code.
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include "rk30hack.h"
+
+int rk30_zero_out(int fd, off_t pos, ssize_t size)
+{
+    if (lseek(fd, pos, SEEK_SET) != pos) {
+        fprintf(stderr, "mtd: erase failure at 0x%08lx (%s)\n",
+                pos, strerror(errno));
+        return -1;
+    }
+
+    unsigned char *zb = (unsigned char *)calloc(1, size);
+    if (zb == NULL) {
+        fprintf(stderr, "mtd: erase failure, could not allocate memory\n");
+        return -1;
+    }
+
+    if (write(fd, zb, size) != size) {
+        fprintf(stderr, "mtd: erase failure at 0x%08lx (%s)\n",
+                pos, strerror(errno));
+        free(zb);
+        return -1;
+    }
+
+    free(zb);
+    return 0;
+}
diff --git a/mtdutils/rk30hack.h b/mtdutils/rk30hack.h
new file mode 100644
index 0000000..d492793
--- /dev/null
+++ b/mtdutils/rk30hack.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2013 Sergey 'Jin' Bostandzhyan
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* This is a hack for Rockchip rk30xx based devices. The problem is that
+ * the MEMERASE ioctl is failing (hangs and never returns) in their kernel.
+ * The sources are not fully available, so fixing it in the rk30xxnand_ko driver
+ * is not possible.
+ *
+ * I straced the stock recovery application and it seems to avoid this
+ * particular ioctl, instead it is simply writing zeroes using the write() call.
+ *
+ * This workaround does the same and will replace all MEMERASE occurances in
+ * the recovery code.
+ */
+
+#ifndef __RK30_HACK_H__
+#define __RK30_HACK_H__
+
+#include <sys/types.h>  // for size_t, etc.
+
+// write zeroes to fd at position pos
+int zero_out(int fd, off_t pos, ssize_t length);
+
+#endif//__RK30_HACK_H__