am 62f39c10: merge from open-source master

Merge commit '62f39c105af8789fd9308fa6a5b91f0963a7c59b'

* commit '62f39c105af8789fd9308fa6a5b91f0963a7c59b':
  adb: Add "adb disconnect" command for disconnecting TCP/IP devices.
  Add support for Acer devices
  adb: Add USB Vendor IDs for LG and Huawei
  Add NOTICE file and license tag for adb
  adb: Clean up argument passing for create_service_thread()
  Revert "adb: Another attempted workaround for the adb disconnect problem."
  libsysutils: Fix some bugs in NetlinkListener and NetlinkEvent
  added SuperH atomic support to libcutils
diff --git a/adb/commandline.c b/adb/commandline.c
index 055aa10..0edd1e1 100644
--- a/adb/commandline.c
+++ b/adb/commandline.c
@@ -97,7 +97,7 @@
         "                                 returns an error if more than one emulator is running.\n"
         " -s <serial number>            - directs command to the USB device or emulator with\n"
         "                                 the given serial number. Overrides ANDROID_SERIAL\n"
-        "                                 envivornment variable.\n"
+        "                                 environment variable.\n"
         " -p <product name or path>     - simple product name like 'sooner', or\n"
         "                                 a relative/absolute path to a product\n"
         "                                 out directory like 'out/target/product/sooner'.\n"
diff --git a/fastboot/fastboot.c b/fastboot/fastboot.c
index c81222a..911c3f6 100644
--- a/fastboot/fastboot.c
+++ b/fastboot/fastboot.c
@@ -165,6 +165,9 @@
 {
     if (match_fastboot(info) == 0) {
         char* serial = info->serial_number;
+        if (!info->writable) {
+            serial = "no permissions"; // like "adb devices"
+        }
         if (!serial[0]) {
             serial = "????????????";
         }
@@ -554,6 +557,8 @@
         return 0;
     }
 
+    serial = getenv("ANDROID_SERIAL");
+
     while (argc > 0) {
         if(!strcmp(*argv, "-w")) {
             wants_wipe = 1;
diff --git a/fastboot/usb.h b/fastboot/usb.h
index f3ec5bf..cc157d5 100644
--- a/fastboot/usb.h
+++ b/fastboot/usb.h
@@ -50,6 +50,8 @@
     unsigned char has_bulk_in;
     unsigned char has_bulk_out;
     
+    unsigned char writable;
+
     char serial_number[256];
 };
   
diff --git a/fastboot/usb_linux.c b/fastboot/usb_linux.c
index 3b40ba7..2ce53eb 100644
--- a/fastboot/usb_linux.c
+++ b/fastboot/usb_linux.c
@@ -89,7 +89,8 @@
     return 0;
 }
 
-static int filter_usb_device(int fd, char *ptr, int len, ifc_match_func callback,
+static int filter_usb_device(int fd, char *ptr, int len, int writable,
+                             ifc_match_func callback,
                              int *ept_in_id, int *ept_out_id, int *ifc_id)
 {
     struct usb_device_descriptor *dev;
@@ -119,7 +120,8 @@
     info.dev_class = dev->bDeviceClass;
     info.dev_subclass = dev->bDeviceSubClass;
     info.dev_protocol = dev->bDeviceProtocol;
-
+    info.writable = writable;
+    
     // read device serial number (if there is one)
     info.serial_number[0] = 0;
     if (dev->iSerialNumber) {
@@ -201,6 +203,7 @@
     DIR *busdir, *devdir;
     struct dirent *de;
     int fd;
+    int writable;
     
     busdir = opendir(base);
     if(busdir == 0) return 0;
@@ -219,13 +222,20 @@
             sprintf(devname, "%s/%s", busname, de->d_name);
 
 //            DBG("[ scanning %s ]\n", devname);
+            writable = 1;
             if((fd = open(devname, O_RDWR)) < 0) {
-                continue;
+                // Check if we have read-only access, so we can give a helpful
+                // diagnostic like "adb devices" does.
+                writable = 0;
+                if((fd = open(devname, O_RDONLY)) < 0) {
+                    continue;
+                }
             }
 
             n = read(fd, desc, sizeof(desc));
             
-            if(filter_usb_device(fd, desc, n, callback, &in, &out, &ifc) == 0){
+            if(filter_usb_device(fd, desc, n, writable, callback,
+                                 &in, &out, &ifc) == 0) {
                 usb = calloc(1, sizeof(usb_handle));
                 strcpy(usb->fname, devname);
                 usb->ep_in = in;
@@ -375,5 +385,3 @@
 {
     return find_usb_device("/dev/bus/usb", callback);
 }
-
-
diff --git a/fastboot/usb_osx.c b/fastboot/usb_osx.c
index d6a8260..0b0512d 100644
--- a/fastboot/usb_osx.c
+++ b/fastboot/usb_osx.c
@@ -351,6 +351,7 @@
         // device has no serial number
         handle->info.serial_number[0] = 0;
     }
+    handle->info.writable = 1;
 
     if (try_interfaces(dev, handle)) {
         goto error;
@@ -416,8 +417,6 @@
             break;
         }
 
-        usb_ifc_info info;
-
         if (try_device(device, &h) != 0) {
             IOObjectRelease(device);
             ret = -1;
diff --git a/fastboot/usb_windows.c b/fastboot/usb_windows.c
index 9c0a9cb..54008a4 100644
--- a/fastboot/usb_windows.c
+++ b/fastboot/usb_windows.c
@@ -301,6 +301,7 @@
     info.ifc_class = interf_desc.bInterfaceClass;
     info.ifc_subclass = interf_desc.bInterfaceSubClass;
     info.ifc_protocol = interf_desc.bInterfaceProtocol;
+    info.writable = 1;
     
     // read serial number (if there is one)
     unsigned long serial_number_len = sizeof(info.serial_number);
diff --git a/include/cutils/adb_networking.h b/include/cutils/adb_networking.h
deleted file mode 100755
index 409d577..0000000
--- a/include/cutils/adb_networking.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * 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.
- */
-
-#ifndef _ADB_NETWORKING_H
-#define _ADB_NETWORKING_H 1
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <sys/socket.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-extern int adb_networking_connect_fd(int fd, struct sockaddr_in *p_address);
-extern int adb_networking_gethostbyname(const char *name, struct in_addr *p_out_addr);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /*_ADB_NETWORKING_H*/
-
diff --git a/libacc/tests/Android.mk b/libacc/tests/Android.mk
index e9fbe03..b3af6f1 100644
--- a/libacc/tests/Android.mk
+++ b/libacc/tests/Android.mk
@@ -25,7 +25,8 @@
     disassem.cpp
 
 LOCAL_SHARED_LIBRARIES := \
-    libacc
+    libacc \
+    libdl
 
 LOCAL_CFLAGS := -O0 -g 
 
@@ -57,7 +58,8 @@
 	runtimeTest.cpp
 
 LOCAL_SHARED_LIBRARIES := \
-    libacc
+    libacc \
+    libdl
 
 LOCAL_CFLAGS := -O0 -g 
 
diff --git a/libcutils/Android.mk b/libcutils/Android.mk
index 6418590..b219473 100644
--- a/libcutils/Android.mk
+++ b/libcutils/Android.mk
@@ -65,7 +65,6 @@
         mspace.c \
         selector.c \
         tztime.c \
-        adb_networking.c \
         zygote.c
 
     commonHostSources += \
diff --git a/libcutils/adb_networking.c b/libcutils/adb_networking.c
deleted file mode 100644
index d819d44..0000000
--- a/libcutils/adb_networking.c
+++ /dev/null
@@ -1,172 +0,0 @@
-/* libs/utils/adb_networking.c
-**
-** Copyright 2006, The Android Open Source Project
-**
-** 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.
-*/
-
-#define ADB_PORT 5037
-
-#define _GNU_SOURCE     /* for asprintf */
-#include <stdio.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <string.h>
-
-#include <cutils/adb_networking.h>
-#include <cutils/sockets.h>
-#include <cutils/properties.h>
-
-#define ADB_RESPONSE_SIZE 4
-
-/**
- * Unfortunately, java.net.Socket wants to create it's filedescriptor early
- * So, this function takes an fd that must be an unconnected
- * PF_LOCAL SOCK_STREAM
- */
-int adb_networking_connect_fd(int fd, struct sockaddr_in *p_address)
-{
-    struct sockaddr_in local_addr;
-    socklen_t alen;
-    char *cmd;
-    char buf[ADB_RESPONSE_SIZE + 1];
-    ssize_t count_read;
-    int ret;
-    int err;
-    /* for impl of inet_ntoa below*/
-    union {
-        uint8_t  b[4];
-        uint32_t l;
-    } a;
-
-    /* First, connect to adb */
-   
-    memset(&local_addr, 0, sizeof(local_addr));
-    local_addr.sin_family = AF_INET;
-    local_addr.sin_port = htons(ADB_PORT);
-    local_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
-
-    do {
-        err = connect(fd, (struct sockaddr *) &local_addr, sizeof(local_addr));
-    } while (err < 0 && errno == EINTR);
-
-    if (err < 0) {
-        return -1;
-    }
-
-    a.l = p_address->sin_addr.s_addr;
-
-    // compose the command
-    asprintf(&cmd, "tcp:%u:%u.%u.%u.%u", 
-                (unsigned int)ntohs(p_address->sin_port), 
-                a.b[0],a.b[1],a.b[2],a.b[3]);
-
-    // buf is now the ascii hex length of cmd
-    snprintf(buf, sizeof(buf), "%04X", strlen(cmd));
-
-    // write the 4-byte length
-    do {
-        err = write(fd, buf, 4);        
-    } while (err < 0 && errno == EINTR);
-
-    // write the command
-    do {
-        err = write(fd, cmd, strlen(cmd));        
-    } while (err < 0 && errno == EINTR);
-
-    // read the result
-    do {
-        count_read = read(fd, buf, sizeof(buf) - 1);
-    } while (count_read < 0 && errno != EINTR);
-
-    if (count_read == ADB_RESPONSE_SIZE 
-            && 0 == strncmp(buf, "OKAY", ADB_RESPONSE_SIZE)) {
-        ret = 0;
-    } else {
-        /* what errno here? <shrug? */
-        errno = ENETUNREACH;
-        ret = -1;
-    }
-
-    free(cmd);
-    
-    return ret;
-}
-
-/**
- * Fills in *p_out_addr and returns 0 on success
- * Memset's *p_out_addr and returns -1 on fail
- */
-
-int adb_networking_gethostbyname(const char *name, struct in_addr *p_out_addr)
-{
-    int fd;
-    char *cmd = NULL;
-    char buf[ADB_RESPONSE_SIZE + 1];
-    int err;
-    ssize_t count_read;
-    
-    fd = socket_loopback_client(ADB_PORT, SOCK_STREAM);
-
-    if (fd < 0) {
-        return -1;
-    }
-
-    // compose the command
-    asprintf(&cmd, "dns:%s", name);
-
-    // buf is now the ascii hex length of cmd
-    snprintf(buf, sizeof(buf), "%04X", strlen(cmd));
-
-    // write the 4-byte length
-    do {
-        err = write(fd, buf, 4);        
-    } while (err < 0 && errno == EINTR);
-
-    // write the command
-    do {
-        err = write(fd, cmd, strlen(cmd));        
-    } while (err < 0 && errno == EINTR);
-
-    // read the result
-    do {
-        count_read = read(fd, buf, ADB_RESPONSE_SIZE);
-    } while (count_read < 0 && errno != EINTR);
-
-    if (count_read != ADB_RESPONSE_SIZE 
-            || 0 != strncmp(buf, "OKAY", ADB_RESPONSE_SIZE)) {
-        goto error;
-    }
-
-    // read the actual IP address
-    do {
-        count_read = read(fd, &(p_out_addr->s_addr), sizeof(p_out_addr->s_addr));
-    } while (count_read < 0 && errno != EINTR);
-
-    if (count_read != 4) {
-        goto error;
-    }
-
-    free(cmd);
-    close(fd);
-    return 0;
-error:
-    free(cmd);
-    close(fd);
-    memset(p_out_addr, 0, sizeof(struct in_addr));
-    return -1;
-}
-
diff --git a/toolbox/Android.mk b/toolbox/Android.mk
index 70b13dc..122a544 100644
--- a/toolbox/Android.mk
+++ b/toolbox/Android.mk
@@ -50,7 +50,8 @@
 	top \
 	iftop \
 	id \
-	vmstat
+	vmstat \
+	nandread
 
 LOCAL_SRC_FILES:= \
 	toolbox.c \
diff --git a/toolbox/ls.c b/toolbox/ls.c
index b221074..f340a83 100644
--- a/toolbox/ls.c
+++ b/toolbox/ls.c
@@ -86,15 +86,50 @@
     }
 }
 
-static int listfile_size(const char *path, int flags)
+static int show_total_size(const char *dirname, DIR *d, int flags)
+{
+    struct dirent *de;
+    char tmp[1024];
+    struct stat s;
+    int sum = 0;
+
+    /* run through the directory and sum up the file block sizes */
+    while ((de = readdir(d)) != 0) {
+        if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
+            continue;
+        if (de->d_name[0] == '.' && (flags & LIST_ALL) == 0)
+            continue;
+
+        if (strcmp(dirname, "/") == 0)
+            snprintf(tmp, sizeof(tmp), "/%s", de->d_name);
+        else
+            snprintf(tmp, sizeof(tmp), "%s/%s", dirname, de->d_name);
+
+        if (lstat(tmp, &s) < 0) {
+            fprintf(stderr, "stat failed on %s: %s\n", tmp, strerror(errno));
+            rewinddir(d);
+            return -1;
+        }
+
+        sum += s.st_blocks / 2;
+    }
+
+    printf("total %d\n", sum);
+    rewinddir(d);
+    return 0;
+}
+
+static int listfile_size(const char *path, const char *filename, int flags)
 {
     struct stat s;
 
-    if (lstat(path, &s) < 0)
+    if (lstat(path, &s) < 0) {
+        fprintf(stderr, "lstat '%s' failed: %s\n", path, strerror(errno));
         return -1;
+    }
 
     /* blocks are 512 bytes, we want output to be KB */
-    printf("%lld %s\n", s.st_blocks / 2, path);
+    printf("%lld %s\n", s.st_blocks / 2, filename);
     return 0;
 }
 
@@ -189,7 +224,7 @@
     if ((flags & LIST_LONG) != 0) {
         return listfile_long(pathname, flags);
     } else /*((flags & LIST_SIZE) != 0)*/ {
-        return listfile_size(pathname, flags);
+        return listfile_size(pathname, filename, flags);
     }
 }
 
@@ -198,13 +233,17 @@
     char tmp[4096];
     DIR *d;
     struct dirent *de;
-    
+
     d = opendir(name);
     if(d == 0) {
         fprintf(stderr, "opendir failed, %s\n", strerror(errno));
         return -1;
     }
 
+    if ((flags & LIST_SIZE) != 0) {
+        show_total_size(name, d, flags);
+    }
+
     while((de = readdir(d)) != 0){
         if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) continue;
         if(de->d_name[0] == '.' && (flags & LIST_ALL) == 0) continue;
diff --git a/toolbox/nandread.c b/toolbox/nandread.c
new file mode 100644
index 0000000..aacccdb
--- /dev/null
+++ b/toolbox/nandread.c
@@ -0,0 +1,256 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <mtd/mtd-user.h>
+#include <sys/ioctl.h>
+
+int test_empty(const char *buf, size_t size)
+{
+    while(size--) {
+        if (*buf++ != 0xff)
+            return 0;
+    }
+    return 1;
+}
+
+int nandread_main(int argc, char **argv)
+{
+    char *devname = NULL;
+    char *filename = NULL;
+    char *statusfilename = NULL;
+    char *statusext = ".stat";
+    int fd;
+    int outfd = -1;
+    FILE *statusfile = NULL;
+    int ret;
+    int verbose = 0;
+    void *buffer;
+    loff_t pos, opos;
+    int c;
+    int i;
+    int empty_pages = 0;
+    int page_count = 0;
+    int bad_block;
+    uint32_t *oob_data;
+    uint8_t *oob_fixed;
+    size_t spare_size = 64;
+    struct mtd_info_user mtdinfo;
+    struct mtd_ecc_stats initial_ecc, last_ecc, ecc;
+    struct mtd_oob_buf oobbuf;
+    struct nand_ecclayout ecclayout;
+
+    do {
+        c = getopt(argc, argv, "d:f:s:hv");
+        if (c == EOF)
+            break;
+        switch (c) {
+        case 'd':
+            devname = optarg;
+            break;
+        case 'f':
+            filename = optarg;
+            break;
+        case 's':
+            spare_size = atoi(optarg);
+            break;
+        case 'v':
+            verbose++;
+            break;
+        case 'h':
+            fprintf(stderr, "%s [-d <dev>] [-f file] [-s sparesize] [-vh]\n"
+                    "  -d <dev>   Read from <dev>\n"
+                    "  -f <file>  Write to <file>\n"
+                    "  -s <size>  Number of spare bytes in file (default 64)\n"
+                    "  -v         Print info\n"
+                    "  -h         Print help\n", argv[0]);
+            return -1;
+        case '?':
+            fprintf(stderr, "%s: invalid option -%c\n",
+                argv[0], optopt);
+            exit(1);
+        }
+    } while (1);
+
+    if (optind < argc) {
+        fprintf(stderr, "%s: extra arguments\n", argv[0]);
+        return 1;
+    }
+    if (!devname) {
+        fprintf(stderr, "%s: specify device name\n", argv[0]);
+        return 1;
+    }
+
+    fd = open(devname, O_RDONLY);
+    if (fd < 0) {
+        fprintf(stderr, "cannot open %s, %s\n", devname, strerror(errno));
+        return 1;
+    }
+
+    if (filename) {
+        outfd = creat(filename, 0666);
+        if (outfd < 0) {
+            fprintf(stderr, "cannot open %s, %s\n", filename, strerror(errno));
+            return 1;
+        }
+        statusfilename = malloc(strlen(filename) + strlen(statusext) + 1);
+        strcpy(statusfilename, filename);
+        strcat(statusfilename, statusext);
+        statusfile = fopen(statusfilename, "w+");
+        if (!statusfile) {
+            fprintf(stderr, "cannot open %s, %s\n", statusfilename, strerror(errno));
+            return 1;
+        }
+    }
+
+    ret = ioctl(fd, MEMGETINFO, &mtdinfo);
+    if (ret) {
+        fprintf(stderr, "failed get mtd info for %s, %s\n",
+                devname, strerror(errno));
+        return 1;
+    }
+
+    if (verbose) {
+        printf("size: %u\n", mtdinfo.size);
+        printf("erase size: %u\n", mtdinfo.erasesize);
+        printf("write size: %u\n", mtdinfo.writesize);
+        printf("oob size: %u\n", mtdinfo.oobsize);
+    }
+
+    buffer = malloc(mtdinfo.writesize + mtdinfo.oobsize + spare_size);
+    if (!buffer) {
+        fprintf(stderr, "failed allocate readbuffer size %u\n",
+                mtdinfo.writesize + mtdinfo.oobsize);
+        return 1;
+    }
+
+    oobbuf.length = mtdinfo.oobsize;
+    oob_data = (uint32_t *)((uint8_t *)buffer + mtdinfo.writesize);
+    memset(oob_data, 0xff, spare_size);
+    oobbuf.ptr = (uint8_t *)oob_data + spare_size;
+
+    ret = ioctl(fd, ECCGETLAYOUT, &ecclayout);
+    if (ret) {
+        fprintf(stderr, "failed get ecc layout for %s, %s\n",
+                devname, strerror(errno));
+        return 1;
+    }
+    if (verbose) {
+        printf("ecc bytes: %u\n", ecclayout.eccbytes);
+        printf("oobavail: %u\n", ecclayout.oobavail);
+    }
+    if (ecclayout.oobavail > spare_size)
+        printf("oobavail, %d > image spare size, %d\n", ecclayout.oobavail, spare_size);
+
+    ret = ioctl(fd, ECCGETSTATS, &initial_ecc);
+    if (ret) {
+        fprintf(stderr, "failed get ecc stats for %s, %s\n",
+                devname, strerror(errno));
+        return 1;
+    }
+    last_ecc = initial_ecc;
+
+    if (verbose) {
+        printf("initial ecc corrected: %u\n", initial_ecc.corrected);
+        printf("initial ecc failed: %u\n", initial_ecc.failed);
+        printf("initial ecc badblocks: %u\n", initial_ecc.badblocks);
+        printf("initial ecc bbtblocks: %u\n", initial_ecc.bbtblocks);
+    }
+
+    for (pos = 0, opos = 0; pos < mtdinfo.size; pos += mtdinfo.writesize) {
+        bad_block = 0;
+        if (verbose > 3)
+            printf("reading at %llx\n", pos);
+        lseek64(fd, pos, SEEK_SET);
+        ret = read(fd, buffer, mtdinfo.writesize);
+        if (ret < (int)mtdinfo.writesize) {
+            fprintf(stderr, "short read at %llx, %d\n", pos, ret);
+            bad_block = 2;
+        }
+        oobbuf.start = pos;
+        ret = ioctl(fd, MEMREADOOB, &oobbuf);
+        if (ret) {
+            fprintf(stderr, "failed to read oob data at %llx, %d\n", pos, ret);
+            bad_block = 2;
+        }
+        ret = ioctl(fd, ECCGETSTATS, &ecc);
+        if (ret) {
+            fprintf(stderr, "failed get ecc stats for %s, %s\n",
+                    devname, strerror(errno));
+            return 1;
+        }
+        ret = ioctl(fd, MEMGETBADBLOCK, &pos);
+        if (ret && errno != EOPNOTSUPP) {
+            printf("badblock at %llx\n", pos);
+            bad_block = 1;
+        }
+        if (ecc.corrected != last_ecc.corrected)
+            printf("ecc corrected, %u, at %llx\n", ecc.corrected - last_ecc.corrected, pos);
+        if (ecc.failed != last_ecc.failed)
+            printf("ecc failed, %u, at %llx\n", ecc.failed - last_ecc.failed, pos);
+        if (ecc.badblocks != last_ecc.badblocks)
+            printf("ecc badblocks, %u, at %llx\n", ecc.badblocks - last_ecc.badblocks, pos);
+        if (ecc.bbtblocks != last_ecc.bbtblocks)
+            printf("ecc bbtblocks, %u, at %llx\n", ecc.bbtblocks - last_ecc.bbtblocks, pos);
+
+        oob_fixed = (uint8_t *)oob_data;
+        for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES; i++) {
+            int len = ecclayout.oobfree[i].length;
+            if (oob_fixed + len > oobbuf.ptr)
+                len = oobbuf.ptr - oob_fixed;
+            if (len) {
+                memcpy(oob_fixed, oobbuf.ptr + ecclayout.oobfree[i].offset, len);
+                oob_fixed += len;
+            }
+        }
+
+        if (outfd >= 0) {
+            ret = write(outfd, buffer, mtdinfo.writesize + spare_size);
+            if (ret < (int)(mtdinfo.writesize + spare_size)) {
+                fprintf(stderr, "short write at %llx, %d\n", pos, ret);
+                close(outfd);
+                outfd = -1;
+            }
+            if (ecc.corrected != last_ecc.corrected)
+                fprintf(statusfile, "%08llx: ecc corrected\n", opos);
+            if (ecc.failed != last_ecc.failed)
+                fprintf(statusfile, "%08llx: ecc failed\n", opos);
+            if (bad_block == 1)
+                fprintf(statusfile, "%08llx: badblock\n", opos);
+            if (bad_block == 2)
+                fprintf(statusfile, "%08llx: read error\n", opos);
+            opos += mtdinfo.writesize + spare_size;
+        }
+
+        last_ecc = ecc;
+        page_count++;
+        if (test_empty(buffer, mtdinfo.writesize + mtdinfo.oobsize + spare_size))
+            empty_pages++;
+        else if (verbose > 2 || (verbose > 1 && !(pos & (mtdinfo.erasesize - 1))))
+            printf("page at %llx (%d oobbytes): %08x %08x %08x %08x "
+                   "%08x %08x %08x %08x\n", pos, oobbuf.start,
+                   oob_data[0], oob_data[1], oob_data[2], oob_data[3],
+                   oob_data[4], oob_data[5], oob_data[6], oob_data[7]);
+    }
+
+    if (outfd >= 0) {
+        fprintf(statusfile, "read %d pages, %d empty\n", page_count, empty_pages);
+        fprintf(statusfile, "total ecc corrected, %u\n", ecc.corrected - initial_ecc.corrected);
+        fprintf(statusfile, "total ecc failed, %u\n", ecc.failed - initial_ecc.failed);
+        fprintf(statusfile, "total ecc badblocks, %u\n", ecc.badblocks - initial_ecc.badblocks);
+        fprintf(statusfile, "total ecc bbtblocks, %u\n", ecc.bbtblocks - initial_ecc.bbtblocks);
+    }
+    if (verbose) {
+        printf("total ecc corrected, %u\n", ecc.corrected - initial_ecc.corrected);
+        printf("total ecc failed, %u\n", ecc.failed - initial_ecc.failed);
+        printf("total ecc badblocks, %u\n", ecc.badblocks - initial_ecc.badblocks);
+        printf("total ecc bbtblocks, %u\n", ecc.bbtblocks - initial_ecc.bbtblocks);
+    }
+    printf("read %d pages, %d empty\n", page_count, empty_pages);
+
+    return 0;
+}
+