sg_read_buffer: decode read microcode status page, add --inhex=FN option

git-svn-id: https://svn.bingwo.ca/repos/sg3_utils/trunk@822 6180dd3e-e324-4e3e-922d-17de1ae2f315
diff --git a/ChangeLog b/ChangeLog
index 308990e..6cf7264 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,7 +2,7 @@
 some description at the top of its ".c" file. All utilities in the main
 directory have their own "man" pages. There is also a sg3_utils man page.
 
-Changelog for sg3_utils-1.45 [20190501] [svn: r821]
+Changelog for sg3_utils-1.45 [20190515] [svn: r822]
   - sg_ses: bug: --page= being overridden when --control
     and --data= also given; fix
   - sg_opcodes: expand MLU (spc5r20)
@@ -13,6 +13,8 @@
   - sg_raw: fix --send bug when using stdin
   - sg_vpd: 3pc VPD page add copy group descriptor
     - add --examine option
+  - sg_read_buffer: decode read microcode status page
+    - add --inhex=FN option
   - sg_xcopy: add --fco (fast copy only) (spc5r20)
     - implement --app=1 (append) on regular OFILE type
   - sg_scan (win32): expand limits for big arrays
diff --git a/doc/sg3_utils.8 b/doc/sg3_utils.8
index e2d3f9d..d3f661e 100644
--- a/doc/sg3_utils.8
+++ b/doc/sg3_utils.8
@@ -1,4 +1,4 @@
-.TH SG3_UTILS "8" "April 2019" "sg3_utils\-1.45" SG3_UTILS
+.TH SG3_UTILS "8" "May 2019" "sg3_utils\-1.45" SG3_UTILS
 .SH NAME
 sg3_utils \- a package of utilities for sending SCSI commands
 .SH SYNOPSIS
@@ -573,8 +573,10 @@
 Since the structure of the data returned by SCSI commands varies
 considerably then the usage information or the manpage of the utility being
 used should be checked. In some cases \fI\-\-hex\fR may need to be used
-multiple times (and is more conveniently given as '\-HH' or '\-HHH). In
-other cases the name of this option is \fI\-\-inhex=FN\fR.
+multiple times (and is more conveniently given as '\-HH' or '\-HHH).
+.br
+In some cases the name of the option with this functionality is
+\fI\-\-inhex=FN\fR.
 .TP
 \fB\-m\fR, \fB\-\-maxlen\fR=\fILEN\fR
 several important SCSI commands (e.g. INQUIRY and MODE SENSE) have response
diff --git a/doc/sg_read_buffer.8 b/doc/sg_read_buffer.8
index d133cce..422f0fa 100644
--- a/doc/sg_read_buffer.8
+++ b/doc/sg_read_buffer.8
@@ -1,11 +1,12 @@
-.TH SG_READ_BUFFER "8" "January 2019" "sg3_utils\-1.45" SG3_UTILS
+.TH SG_READ_BUFFER "8" "May 2019" "sg3_utils\-1.45" SG3_UTILS
 .SH NAME
 sg_read_buffer \- send SCSI READ BUFFER command
 .SH SYNOPSIS
 .B sg_read_buffer
-[\fI\-\-help\fR] [\fI\-\-hex\fR] [\fI\-\-id=ID\fR] [\fI\-\-length=LEN\fR]
-[\fI\-\-mode=MO\fR] [\fI\-\-offset=OFF\fR] [\fI\-\-raw\fR]
-[\fI\-\-readonly\fR] [\fI\-\-verbose\fR] [\fI\-\-version\fR] \fIDEVICE\fR
+[\fI\-\-help\fR] [\fI\-\-hex\fR] [\fI\-\-id=ID\fR] [\fI\-\-inhex=FN\fR]
+[\fI\-\-length=LEN\fR] [\fI\-\-mode=MO\fR] [\fI\-\-offset=OFF\fR]
+[\fI\-\-raw\fR] [\fI\-\-readonly\fR] [\fI\-\-specific=MS\fR]
+[\fI\-\-verbose\fR] [\fI\-\-version\fR] \fIDEVICE\fR
 .SH DESCRIPTION
 .\" Add any additional description here
 .PP
@@ -31,6 +32,16 @@
 this option sets the buffer id field in the cdb. \fIID\fR is a value between
 0 (default) and 255 inclusive.
 .TP
+\fB\-I\fR, \fB\-\-inhex\fR=\fIFN\fR
+\fIFN\fR is expected to be a file name (or '\-' for stdin) which contains
+ASCII hexadecimal or binary representing a READ BUFFER response. If known
+this utility will then decode that response. It is preferable to also
+supply the \fI\-\-mode=MO\fR and \fI\-\-specific=MS\fR options, since these
+are not present in the response. The hexadecimal should be arranged as 1 or
+2 digits representing a byte each of which is whitespace or comma separated.
+Anything from and including a hash mark to the end of line is ignored. If the
+\fI\-\-raw\fR option is also given then \fIFN\fR is treated as binary.
+.TP
 \fB\-l\fR, \fB\-\-length\fR=\fILEN\fR
 where \fILEN\fR is the length, in bytes, that is placed in the "allocation
 length" field in the cdb. The default value is 4 (bytes). The device may
@@ -54,6 +65,10 @@
 open the \fIDEVICE\fR read\-only (e.g. in Unix with the O_RDONLY flag).
 The default is to open it read\-write.
 .TP
+\fB\-S\fR, \fB\-\-specific\fR=\fIMS\fR
+this option sets the mode specific field in the cdb. \fIMS\fR is a value
+between 0 and 7 as this is a 3 bit field.
+.TP
 \fB\-v\fR, \fB\-\-verbose\fR
 increase the level of verbosity, (i.e. debug output).
 .TP
diff --git a/src/sg_logs.c b/src/sg_logs.c
index ed24667..a869f0c 100644
--- a/src/sg_logs.c
+++ b/src/sg_logs.c
@@ -36,7 +36,7 @@
 #include "sg_unaligned.h"
 #include "sg_pr2serr.h"
 
-static const char * version_str = "1.73 20190405";    /* spc5r21 + sbc4r17 */
+static const char * version_str = "1.74 20190502";    /* spc5r21 + sbc4r17 */
 
 #define MX_ALLOC_LEN (0xfffc)
 #define SHORT_RESP_LEN 128
@@ -1928,7 +1928,7 @@
         }
         val = sg_get_unaligned_be(pl - 4, bp + 4);
         printf(" = %" PRIu64 "", val);
-        if (val > (1UL << 40))
+        if (val > ((uint64_t)1 << 40))
             printf(" [%" PRIu64 " TB]\n",
                    (val / (1000UL * 1000 * 1000 * 1000)));
         else
diff --git a/src/sg_read_buffer.c b/src/sg_read_buffer.c
index 9f92f25..82682f3 100644
--- a/src/sg_read_buffer.c
+++ b/src/sg_read_buffer.c
@@ -15,15 +15,19 @@
 #include <stdbool.h>
 #include <ctype.h>
 #include <string.h>
+#include <errno.h>
 #include <getopt.h>
 #define __STDC_FORMAT_MACROS 1
 #include <inttypes.h>
+#include <sys/types.h>
+#include <sys/stat.h>
 
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
 
 #include "sg_lib.h"
+#include "sg_lib_data.h"
 #include "sg_cmds_basic.h"
 #include "sg_cmds_extra.h"
 #include "sg_pt.h"
@@ -35,7 +39,7 @@
  * device.
  */
 
-static const char * version_str = "1.27 20190113";      /* spc5r20 */
+static const char * version_str = "1.29 20190515";      /* spc5r22 */
 
 
 #ifndef SG_READ_BUFFER_10_CMD
@@ -47,6 +51,7 @@
 #define SG_READ_BUFFER_16_CMDLEN 16
 #endif
 
+#define MAX_DEF_INHEX_LEN 8192
 #define SENSE_BUFF_LEN  64       /* Arbitrary, could be larger */
 #define DEF_PT_TIMEOUT  60       /* 60 seconds */
 
@@ -56,6 +61,7 @@
         {"help", no_argument, 0, 'h'},
         {"hex", no_argument, 0, 'H'},
         {"id", required_argument, 0, 'i'},
+        {"inhex", required_argument, 0, 'I'},
         {"length", required_argument, 0, 'l'},
         {"long", no_argument, 0, 'L'},
         {"mode", required_argument, 0, 'm'},
@@ -73,23 +79,27 @@
 usage()
 {
     pr2serr("Usage: sg_read_buffer [--16] [--help] [--hex] [--id=ID] "
-            "[--length=LEN]\n"
-            "                      [--long] [--mode=MO] [--offset=OFF] "
-            "[--raw]\n"
-            "                      [--readonly] [--specific=MS] [--verbose] "
-            "[--version]\n"
-            "                      DEVICE\n"
+            "[--inhex=FN]\n"
+            "                      [--length=LEN] [--long] [--mode=MO] "
+            "[--offset=OFF]\n"
+            "                      [--raw] [--readonly] [--specific=MS] "
+            "[--verbose]\n"
+            "                      [--version] DEVICE\n"
             "  where:\n"
             "    --16|-L             issue READ BUFFER(16) (def: 10)\n"
             "    --help|-h           print out usage message\n"
             "    --hex|-H            print output in hex\n"
             "    --id=ID|-i ID       buffer identifier (0 (default) to 255)\n"
+            "    --inhex=FN|-I FN    filename FN contains hex data to "
+            "decode\n"
+            "                        rather than DEVICE. If --raw given "
+            "then binary\n"
             "    --length=LEN|-l LEN    length in bytes to read (def: 4)\n"
             "    --long|-L           issue READ BUFFER(16) (def: 10)\n"
             "    --mode=MO|-m MO     read buffer mode, MO is number or "
             "acronym (def: 0)\n"
             "    --offset=OFF|-o OFF    buffer offset (unit: bytes, def: 0)\n"
-            "    --raw|-r            output response to stdout\n"
+            "    --raw|-r            output response in binary to stdout\n"
             "    --readonly|-R       open DEVICE read-only (def: read-write)\n"
             "    --specific=MS|-S MS    mode specific value; 3 bit field (0 "
             "to 7)\n"
@@ -273,6 +283,315 @@
     return ret;
 }
 
+/* Microcode status: active, redundant and download */
+static const char * act_micro_st_arr[] = {
+    "Microcode status not reported",
+    "Activated microcode is valid",
+    "Activated microcode is not valid",
+    "Activated microcode is not a full microcode image",
+};
+
+static const char * red_micro_st_arr[] = {
+    "Redundant microcode status is not reported",
+    "At least one redundant microcode copy is valid",
+    "No redundant microcode copy is valid",
+    "Redundant microcode is not a full microcode image",
+};
+
+/* Major overlap between this SPC-4 table and SES-4r2 table 63 */
+struct sg_lib_simple_value_name_t down_micro_st_arr[] = {
+    {0x0, "No download microcode operation in progress"},
+    {0x1, "Download in progress, awaiting more"},               /* SES */
+    {0x2, "Download complete, updating storage"},               /* SES */
+    {0x3, "Updating storage with deferred microcode"},          /* SES */
+    {0x10, "Complete, no error, starting now"},                 /* SES */
+    {0x11, "Complete, no error, start after hard reset or power "
+           "cycle"},                                            /* SES */
+    {0x12, "Complete, no error, start after power cycle"},      /* SES */
+    {0x13, "Complete, no error, start after activate_mc, hard reset or "
+           "power cycle"},                                      /* SES */
+    {0x21, "Download in progress, awaiting more"},              /* SPC-6 */
+    {0x22, "Download complete, updating storage"},              /* SPC-6 */
+    {0x23, "Updating storage with deferred microcode"},         /* SPC-6 */
+    {0x30, "Deferred microcode download complete, no reports"}, /* SPC-6 */
+    {0x31, "Deferred download ok, await hard reset or power cycle"},
+    {0x32, "Deferred download ok, await power cycle"},          /* SPC-6 */
+    {0x33, "Deferred download ok, await any event"},            /* SPC-6 */
+    {0x34, "Deferred download ok, await Write buffer command"}, /* SPC-6 */
+    {0x35, "Deferred download ok, await any event, WB only this LU"},
+    {0x80, "Error, discarded, see additional status"},          /* SES */
+    {0x81, "Error, discarded, image error"},                    /* SES */
+    {0x82, "Timeout, discarded"},                               /* SES */
+    {0x83, "Internal error, need new microcode before reset"},  /* SES */
+    {0x84, "Internal error, need new microcode, reset safe"},   /* SES */
+    {0x85, "Unexpected activate_mc received"},                  /* SES */
+    {0x90, "Error, discarded, see additional status"},          /* SPC-6 */
+    {0x91, "Error, discarded, image error"},                    /* SPC-6 */
+    {0x92, "Timeout, discarded"},                               /* SPC-6 */
+    {0x93, "Internal error, need new microcode before reset"},  /* SPC-6 */
+    {0x94, "Internal error, need new microcode, reset safe"},   /* SPC-6 */
+    {0x95, "Unexpected activate_mc received, mcrocode discard"}, /* SPC-6 */
+    {0x1000, NULL},             /* End sentinel */
+};
+
+static void
+decode_microcode_status(uint8_t * resp, int rb_len)
+{
+    int n;
+    uint32_t u;
+    const char * cp;
+    const struct sg_lib_simple_value_name_t * vnp;
+    char b[32];
+
+    if ((NULL == resp) || (rb_len < 1))
+        return;
+    n = resp[0];
+    if (n < (int)SG_ARRAY_SIZE(act_micro_st_arr))
+        cp = act_micro_st_arr[n];
+    else {
+        snprintf(b, sizeof(b), "unknown [0x%x]", n);
+        cp = b;
+    }
+    printf("Activated microcode status: %s\n", cp);
+
+    if (rb_len < 2)
+        return;
+    n = resp[1];
+    if (n < (int)SG_ARRAY_SIZE(red_micro_st_arr))
+        cp = red_micro_st_arr[n];
+    else {
+        snprintf(b, sizeof(b), "unknown [0x%x]", n);
+        cp = b;
+    }
+    printf("Redundant microcode status: %s\n", cp);
+
+    if (rb_len < 3)
+        return;
+    n = resp[2];
+    for (vnp = down_micro_st_arr, cp = NULL; vnp->name; ++vnp) {
+        if (vnp->value == n) {
+            cp = vnp->name;
+            break;
+        }
+    }
+    if (NULL == cp) {
+        snprintf(b, sizeof(b), "unknown [0x%x]", n);
+        cp = b;
+    }
+    printf("Download microcode status: %s\n", cp);
+
+    if (rb_len > 7) {
+        u = sg_get_unaligned_be32(resp + 4);
+        printf("Download microcode maximum size (bytes): %u [0x%x]\n", u, u);
+    }
+    if (rb_len > 15) {
+        u = sg_get_unaligned_be32(resp + 12);
+        printf("Download microcode expected buffer offset (bytes): %u "
+               "[0x%x]\n", u, u);
+    }
+}
+
+/* Read ASCII hex bytes or binary from fname (a file named '-' taken as
+ * stdin). If reading ASCII hex then there should be either one entry per
+ * line or a comma, space or tab separated list of bytes. If no_space is
+ * set then a string of ACSII hex digits is expected, 2 per byte. Everything
+ * from and including a '#' on a line is ignored. Returns 0 if ok, or an
+ * error code. */
+static int
+f2hex_arr(const char * fname, int as_binary, int no_space,
+          uint8_t * mp_arr, int * mp_arr_len, int max_arr_len)
+{
+    int fn_len, in_len, k, j, m, fd, err;
+    bool has_stdin, split_line;
+    unsigned int h;
+    const char * lcp;
+    FILE * fp;
+    char line[512];
+    char carry_over[4];
+    int off = 0;
+    struct stat a_stat;
+
+    if ((NULL == fname) || (NULL == mp_arr) || (NULL == mp_arr_len))
+        return SG_LIB_LOGIC_ERROR;
+    fn_len = strlen(fname);
+    if (0 == fn_len)
+        return SG_LIB_SYNTAX_ERROR;
+    has_stdin = ((1 == fn_len) && ('-' == fname[0]));   /* read from stdin */
+    if (as_binary) {
+        if (has_stdin)
+            fd = STDIN_FILENO;
+        else {
+            fd = open(fname, O_RDONLY);
+            if (fd < 0) {
+                err = errno;
+                pr2serr("unable to open binary file %s: %s\n", fname,
+                         safe_strerror(err));
+                return sg_convert_errno(err);
+            }
+        }
+        k = read(fd, mp_arr, max_arr_len);
+        if (k <= 0) {
+            int ret = SG_LIB_SYNTAX_ERROR;
+
+            if (0 == k)
+                pr2serr("read 0 bytes from binary file %s\n", fname);
+            else {
+                ret = sg_convert_errno(errno);
+                pr2serr("read from binary file %s: %s\n", fname,
+                        safe_strerror(errno));
+            }
+            if (! has_stdin)
+                close(fd);
+            return ret;
+        }
+        if ((0 == fstat(fd, &a_stat)) && S_ISFIFO(a_stat.st_mode)) {
+            /* pipe; keep reading till error or 0 read */
+            while (k < max_arr_len) {
+                m = read(fd, mp_arr + k, max_arr_len - k);
+                if (0 == m)
+                   break;
+                if (m < 0) {
+                    err = errno;
+                    pr2serr("read from binary pipe %s: %s\n", fname,
+                            safe_strerror(err));
+                    if (! has_stdin)
+                        close(fd);
+                    return sg_convert_errno(err);
+                }
+                k += m;
+            }
+        }
+        *mp_arr_len = k;
+        if (! has_stdin)
+            close(fd);
+        return 0;
+    } else {    /* So read the file as ASCII hex */
+        if (has_stdin)
+            fp = stdin;
+        else {
+            fp = fopen(fname, "r");
+            if (NULL == fp) {
+                err = errno;
+                pr2serr("Unable to open %s for reading: %s\n", fname,
+                        safe_strerror(err));
+                return sg_convert_errno(err);
+            }
+        }
+     }
+
+    carry_over[0] = 0;
+    for (j = 0; j < 512; ++j) {
+        if (NULL == fgets(line, sizeof(line), fp))
+            break;
+        in_len = strlen(line);
+        if (in_len > 0) {
+            if ('\n' == line[in_len - 1]) {
+                --in_len;
+                line[in_len] = '\0';
+                split_line = false;
+            } else
+                split_line = true;
+        }
+        if (in_len < 1) {
+            carry_over[0] = 0;
+            continue;
+        }
+        if (carry_over[0]) {
+            if (isxdigit(line[0])) {
+                carry_over[1] = line[0];
+                carry_over[2] = '\0';
+                if (1 == sscanf(carry_over, "%4x", &h))
+                    mp_arr[off - 1] = h;       /* back up and overwrite */
+                else {
+                    pr2serr("%s: carry_over error ['%s'] around line %d\n",
+                            __func__, carry_over, j + 1);
+                    goto bad;
+                }
+                lcp = line + 1;
+                --in_len;
+            } else
+                lcp = line;
+            carry_over[0] = 0;
+        } else
+            lcp = line;
+
+        m = strspn(lcp, " \t");
+        if (m == in_len)
+            continue;
+        lcp += m;
+        in_len -= m;
+        if ('#' == *lcp)
+            continue;
+        k = strspn(lcp, "0123456789aAbBcCdDeEfF ,\t");
+        if ((k < in_len) && ('#' != lcp[k]) && ('\r' != lcp[k])) {
+            pr2serr("%s: syntax error at line %d, pos %d\n", __func__,
+                    j + 1, m + k + 1);
+            goto bad;
+        }
+        if (no_space) {
+            for (k = 0; isxdigit(*lcp) && isxdigit(*(lcp + 1));
+                 ++k, lcp += 2) {
+                if (1 != sscanf(lcp, "%2x", &h)) {
+                    pr2serr("%s: bad hex number in line %d, pos %d\n",
+                            __func__, j + 1, (int)(lcp - line + 1));
+                    goto bad;
+                }
+                if ((off + k) >= max_arr_len) {
+                    pr2serr("%s: array length exceeded\n", __func__);
+                    goto bad;
+                }
+                mp_arr[off + k] = h;
+            }
+            if (isxdigit(*lcp) && (! isxdigit(*(lcp + 1))))
+                carry_over[0] = *lcp;
+            off += k;
+        } else {
+            for (k = 0; k < 1024; ++k) {
+                if (1 == sscanf(lcp, "%10x", &h)) {
+                    if (h > 0xff) {
+                        pr2serr("%s: hex number larger than 0xff in line "
+                                "%d, pos %d\n", __func__, j + 1,
+                                (int)(lcp - line + 1));
+                        goto bad;
+                    }
+                    if (split_line && (1 == strlen(lcp))) {
+                        /* single trailing hex digit might be a split pair */
+                        carry_over[0] = *lcp;
+                    }
+                    if ((off + k) >= max_arr_len) {
+                        pr2serr("%s: array length exceeded\n", __func__);
+                        goto bad;
+                    }
+                    mp_arr[off + k] = h;
+                    lcp = strpbrk(lcp, " ,\t");
+                    if (NULL == lcp)
+                        break;
+                    lcp += strspn(lcp, " ,\t");
+                    if ('\0' == *lcp)
+                        break;
+                } else {
+                    if (('#' == *lcp) || ('\r' == *lcp)) {
+                        --k;
+                        break;
+                    }
+                    pr2serr("%s: error in line %d, at pos %d\n", __func__,
+                            j + 1, (int)(lcp - line + 1));
+                    goto bad;
+                }
+            }
+            off += (k + 1);
+        }
+    }
+    *mp_arr_len = off;
+    if (stdin != fp)
+        fclose(fp);
+    return 0;
+bad:
+    if (stdin != fp)
+        fclose(fp);
+    return 1;
+}
+
 static void
 dStrRaw(const uint8_t * str, int len)
 {
@@ -290,7 +609,7 @@
     bool do_raw = false;
     bool verbose_given = false;
     bool version_given = false;
-    int res, c, len, k;
+    int res, c, len, k, inhex_len;
     int sg_fd = -1;
     int do_help = 0;
     int do_hex = 0;
@@ -304,13 +623,15 @@
     int64_t ll;
     uint64_t rb_offset = 0;
     const char * device_name = NULL;
-    uint8_t * resp;
+    const char * fname = NULL;
+    uint8_t * resp = NULL;
+    uint8_t * free_resp = NULL;
     const struct mode_s * mp;
 
     while (1) {
         int option_index = 0;
 
-        c = getopt_long(argc, argv, "hHi:l:Lm:o:rRS:vV", long_options,
+        c = getopt_long(argc, argv, "hHi:I:l:Lm:o:rRS:vV", long_options,
                         &option_index);
         if (c == -1)
             break;
@@ -331,6 +652,14 @@
                 return SG_LIB_SYNTAX_ERROR;
             }
             break;
+        case 'I':
+            if (fname) {
+                pr2serr("--inhex= option given more than once. Once only "
+                        "please\n");
+                return SG_LIB_SYNTAX_ERROR;
+            } else
+                fname = optarg;
+            break;
         case 'l':
             rb_len = sg_get_num(optarg);
             if (rb_len < 0) {
@@ -444,19 +773,35 @@
         return 0;
     }
 
-    if (NULL == device_name) {
+    rb_len = 0;
+    inhex_len = 0;
+    if (device_name && fname) {
+        pr2serr("Confused: both DEVICE (%s) and --inhex= option given. One "
+                "only please\n", device_name);
+                return SG_LIB_SYNTAX_ERROR;
+    } else if (fname) {
+        rb_len = (rb_len > MAX_DEF_INHEX_LEN) ? rb_len : MAX_DEF_INHEX_LEN;
+        resp = (uint8_t *)sg_memalign(rb_len, 0, &free_resp, false);
+        ret = f2hex_arr(fname, do_raw, 0, resp, &inhex_len, rb_len);
+        if (ret)
+            goto fini;
+        if (do_raw)
+            do_raw = false;     /* only used for input in this case */
+        rb_len = inhex_len;
+        resid = 0;
+        goto decode_result;
+    } else if (NULL == device_name) {
         pr2serr("Missing device name!\n\n");
         usage();
         return SG_LIB_SYNTAX_ERROR;
     }
 
     len = rb_len ? rb_len : 8;
-    resp = (uint8_t *)malloc(len);
+    resp = (uint8_t *)sg_memalign(len, 0, &free_resp, false);
     if (NULL == resp) {
         pr2serr("unable to allocate %d bytes on the heap\n", len);
         return SG_LIB_CAT_OTHER;
     }
-    memset(resp, 0, len);
 
     if (do_raw) {
         if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
@@ -509,6 +854,7 @@
     }
     if (resid > 0)
         rb_len -= resid;        /* got back less than requested */
+decode_result:
     if (rb_len > 0) {
         if (do_raw)
             dStrRaw(resp, rb_len);
@@ -527,6 +873,9 @@
                 printf("EBOS:%d\n", resp[0] & 1 ? 1 : 0);
                 printf("Echo buffer capacity: %d (0x%x)\n", k, k);
                 break;
+            case MODE_READ_MICROCODE_ST:
+                decode_microcode_status(resp, rb_len);
+                break;
             default:
                 hex2stdout((const uint8_t *)resp, rb_len,
                            (verbose > 1 ? 0 : 1));
@@ -536,8 +885,8 @@
     }
 
 fini:
-    if (resp)
-        free(resp);
+    if (free_resp)
+        free(free_resp);
     if (sg_fd >= 0) {
         res = sg_cmds_close_device(sg_fd);
         if (res < 0) {
diff --git a/src/sg_ses_microcode.c b/src/sg_ses_microcode.c
index 44e79b7..36f3659 100644
--- a/src/sg_ses_microcode.c
+++ b/src/sg_ses_microcode.c
@@ -28,6 +28,7 @@
 #endif
 
 #include "sg_lib.h"
+#include "sg_lib_data.h"
 #include "sg_cmds_basic.h"
 #include "sg_cmds_extra.h"
 #include "sg_unaligned.h"
@@ -44,7 +45,7 @@
  * RESULTS commands in order to send microcode to the given SES device.
  */
 
-static const char * version_str = "1.17 20190416";    /* ses4r02 */
+static const char * version_str = "1.18 20190513";    /* ses4r02 */
 
 #define ME "sg_ses_microcode: "
 #define MAX_XFER_LEN (128 * 1024 * 1024)
@@ -120,13 +121,10 @@
     {NULL, 0, NULL},
 };
 
-struct diag_page_code {
-    int page_code;
-    const char * desc;
-};
-
-/* An array of Download microcode status field values and descriptions */
-static struct diag_page_code mc_status_arr[] = {
+/* An array of Download microcode status field values and descriptions.
+ * This table is a subset of one in sg_read_buffer for the read microcode
+ * status page. */
+static struct sg_lib_simple_value_name_t mc_status_arr[] = {
     {0x0, "No download microcode operation in progress"},
     {0x1, "Download in progress, awaiting more"},
     {0x2, "Download complete, updating storage"},
@@ -241,11 +239,11 @@
 static const char *
 get_mc_status_str(uint8_t status_val)
 {
-    const struct diag_page_code * mcsp;
+    const struct sg_lib_simple_value_name_t * mcsp;
 
-    for (mcsp = mc_status_arr; mcsp->desc; ++mcsp) {
-        if (status_val == mcsp->page_code)
-            return mcsp->desc;
+    for (mcsp = mc_status_arr; mcsp->name; ++mcsp) {
+        if (status_val == mcsp->value)
+            return mcsp->name;
     }
     return "";
 }
diff --git a/testing/sg_tst_async.cpp b/testing/sg_tst_async.cpp
index c1067d0..d6b1c5d 100644
--- a/testing/sg_tst_async.cpp
+++ b/testing/sg_tst_async.cpp
@@ -89,7 +89,7 @@
 #include "sg_pt.h"
 #include "sg_cmds.h"
 
-static const char * version_str = "1.32 20190427";
+static const char * version_str = "1.34 20190506";
 static const char * util_name = "sg_tst_async";
 
 /* This is a test program for checking the async usage of the Linux sg
@@ -168,7 +168,9 @@
 static atomic<int> start_e2big_count(0);
 static atomic<int> start_eagain_count(0);
 static atomic<int> fin_eagain_count(0);
+static atomic<int> fin_ebusy_count(0);
 static atomic<int> start_edom_count(0);
+static atomic<int> enomem_count(0);
 static atomic<int> uniq_pack_id(1);
 // static atomic<int> generic_errs(0);
 
@@ -190,6 +192,7 @@
     bool cmd_time;
     bool direct;
     bool generic_sync;
+    bool masync;
     bool mmap_io;
     bool no_xfer;
     bool pack_id_force;
@@ -217,7 +220,9 @@
     myQDiscipline myqd;         /* --qfav= value (def: 2 --> MYQD_HIGH) */
 };
 
-static struct opts_t a_opts;	/* Expect zero fill on simple types */
+static struct opts_t a_opts;    /* Expect zero fill on simple types */
+
+static int pr_rusage(int id);
 
 #if 0
 class Rand_uint {
@@ -262,6 +267,9 @@
 static struct option long_options[] = {
         {"v3", no_argument, 0, '3'},
         {"v4", no_argument, 0, '4'},
+        {"more-async", no_argument, 0, 'a'},
+        {"more_async", no_argument, 0, 'a'},
+        {"masync", no_argument, 0, 'a'},
         {"cmd-time", no_argument, 0, 'c'},
         {"cmd_time", no_argument, 0, 'c'},
         {"direct", no_argument, 0, 'd'},
@@ -302,16 +310,17 @@
 {
     printf("Usage: %s [--cmd-time] [--direct] [--force] [--generic-sync]\n"
            "                    [--help] [--lba=LBA+] [--lbsz=LBSZ] "
-           "[--maxqpt=QPT]\n"
-           "                    [--mmap-io] [--numpt=NPT] [--noxfer] "
-           "[--override=OVN]\n"
-           "                    [--pack-id] [--qat=AT] [-qfav=FAV] [--read] "
-           "[--stats]\n"
-           "                    [--submit] [--szlb=LB[,NLBS]] [--tnum=NT] "
-           "[--tur]\n"
-           "                    [--v3] [--v4] [--verbose] [--version] "
-           "[--wait=MS]\n"
-           "                    [--write]  <sg_disk_device>*\n",
+           "[--masync]\n"
+           "                    [--maxqpt=QPT] [--mmap-io] [--numpt=NPT] "
+           "[--noxfer]\n"
+           "                    [--override=OVN] [--pack-id] [--qat=AT] "
+           "[-qfav=FAV]\n"
+           "                    [--read] [--stats] [--submit] "
+           "[--szlb=LB[,NLBS]]\n"
+           "                    [--tnum=NT] [--tur] [--v3] [--v4] "
+           "[--verbose]\n"
+           "                    [--version] [--wait=MS] [--write]  "
+           "<sg_disk_device>*\n",
            util_name);
     printf("  where\n");
     printf("    --cmd-time|-c    calculate per command average time (ns)\n");
@@ -333,10 +342,10 @@
     printf("    --lbsz=LBSZ|-L LBSZ    logical block size in bytes (def: "
            "512)\n"
            "                           should be power of 2 (0 --> 512)\n");
+    printf("    --masync|-a     set 'more async' flag on devices\n");
     printf("    --maxqpt=QPT|-M QPT    maximum commands queued per thread "
            "(def:%d)\n", MAX_Q_PER_FD);
-    printf("    --mmap-io|-m         mmap-ed IO (1 cmd outstanding per "
-           "thread)\n");
+    printf("    --mmap-io|-m    mmap-ed IO (1 cmd outstanding per thread)\n");
     printf("    --numpt=NPT|-n NPT    number of commands per thread "
            "(def: %d)\n", DEF_NUM_PER_THREAD);
     printf("    --noxfer|-N          no data xfer (def: xfer on READ and "
@@ -344,7 +353,7 @@
     printf("    --override OVN|-O OVN    override FAV=2 when OVN queue "
            "depth\n"
            "                             reached (def: 0 -> no override)\n");
-    printf("    --pack-id|-p         set FORCE_PACK_ID, pack-id input to "
+    printf("    --pack-id|-p    set FORCE_PACK_ID, pack-id input to "
            "read/finish\n");
     printf("    --qat=AT|-q AT       AT=0: q_at_head; AT=1: q_at_tail (def: "
            "(drv): head)\n");
@@ -447,8 +456,8 @@
 static int
 start_sg3_cmd(int sg_fd, command2execute cmd2exe, int pack_id, uint64_t lba,
               uint8_t * lbp, int xfer_bytes, int flags, bool submit,
-              unsigned int & eagains, unsigned int & ebusy,
-              unsigned int & e2big, unsigned int & edom)
+              unsigned int & enomem, unsigned int & eagains,
+              unsigned int & ebusy, unsigned int & e2big, unsigned int & edom)
 {
     struct sg_io_hdr pt;
     struct sg_io_v4 p4t;
@@ -510,6 +519,7 @@
                    write(sg_fd, ptp, sizeof(*ptp)) < 0);
          ++k) {
         if ((ENOMEM == errno) && (k < MAX_CONSEC_NOMEMS)) {
+            ++enomem;
             this_thread::yield();
             continue;
         } else if (EAGAIN == errno) {
@@ -525,6 +535,8 @@
             return 2;
         } else if (EDOM == errno)
             ++edom;
+        else if (ENOMEM == errno)
+            pr_rusage(-1);
         pr_errno_lk(errno, "%s: %s, pack_id=%d", __func__, np, pack_id);
         return -1;
     }
@@ -533,8 +545,9 @@
 
 static int
 finish_sg3_cmd(int sg_fd, command2execute cmd2exe, int & pack_id,
-	       bool receive, int wait_ms, unsigned int & eagains,
-	       unsigned int & nanosecs)
+               bool receive, int wait_ms, unsigned int & enomem,
+               unsigned int & eagains, unsigned int & ebusys,
+               unsigned int & nanosecs)
 {
     bool ok;
     int res, k;
@@ -575,8 +588,13 @@
     k = 0;
     while ((((res = receive ? ioctl(sg_fd, SG_IORECEIVE_V3, ptp) :
                               read(sg_fd, ptp, sizeof(*ptp)))) < 0) &&
-           (EAGAIN == errno)) {
-        ++eagains;
+           ((EAGAIN == errno) || (EBUSY == errno) || (ENOMEM == errno))) {
+        if (ENOMEM == errno)
+            ++enomem;
+        else if (EAGAIN == errno)
+            ++eagains;
+        else
+            ++ebusys;
         ++k;
         if (k > 10000) {
             pr2serr_lk("%s: sg_fd=%d: after %d EAGAINs, unable to find "
@@ -591,6 +609,8 @@
             sleep(0);                   // process yield ??
     }
     if (res < 0) {
+        if (ENOMEM == errno)
+            pr_rusage(-1);
         pr_errno_lk(errno, "%s: %s", __func__, np);
         return -1;
     }
@@ -622,8 +642,8 @@
 static int
 start_sg4_cmd(int sg_fd, command2execute cmd2exe, int pack_id, uint64_t lba,
               uint8_t * lbp, int xfer_bytes, int flags, bool submit,
-              unsigned int & eagains, unsigned int & ebusy,
-              unsigned int & e2big, unsigned int & edom)
+              unsigned int & enomem, unsigned int & eagains,
+              unsigned int & ebusy, unsigned int & e2big, unsigned int & edom)
 {
     struct sg_io_v4 p4t;
     uint8_t turCmdBlk[TUR_CMD_LEN] = {0, 0, 0, 0, 0, 0};
@@ -677,6 +697,7 @@
 
     for (int k = 0; ioctl(sg_fd, SG_IOSUBMIT, ptp) < 0; ++k) {
         if ((ENOMEM == errno) && (k < MAX_CONSEC_NOMEMS)) {
+            ++enomem;
             this_thread::yield();
             continue;
         } else if (EAGAIN == errno) {
@@ -692,6 +713,8 @@
             return 2;
         } else if (EDOM == errno)
             ++edom;
+        else if (ENOMEM == errno)
+            pr_rusage(-1);
         pr_errno_lk(errno, "%s: %s, pack_id=%d", __func__, np, pack_id);
         return -1;
     }
@@ -700,8 +723,9 @@
 
 static int
 finish_sg4_cmd(int sg_fd, command2execute cmd2exe, int & pack_id,
-	       bool receive, int wait_ms, unsigned int & eagains,
-	       unsigned int & nanosecs)
+               bool receive, int wait_ms, unsigned int & enomem,
+               unsigned int & eagains, unsigned int & ebusys,
+               unsigned int & nanosecs)
 {
     bool ok;
     int res, k;
@@ -737,8 +761,11 @@
 
     k = 0;
     while ((((res = ioctl(sg_fd, SG_IORECEIVE, ptp))) < 0) &&
-           (EAGAIN == errno)) {
-        ++eagains;
+           ((EAGAIN == errno) || (EBUSY == errno))) {
+        if (EAGAIN == errno)
+            ++eagains;
+        else
+            ++ebusys;
         ++k;
         if (k > 10000) {
             pr2serr_lk("%s: sg_fd=%d: after %d EAGAINs, unable to find "
@@ -753,6 +780,10 @@
             sleep(0);                   // process yield ??
     }
     if (res < 0) {
+        if (ENOMEM == errno) {
+            ++enomem;
+            pr_rusage(-1);
+        }
         pr_errno_lk(errno, "%s: %s", __func__, np);
         return -1;
     }
@@ -847,6 +878,8 @@
     if ((sg_fd = sg_cmds_open_device(dev_name, false /* ro */, vb)) < 0) {
         pr2serr_lk("id=%d: error opening file: %s: %s\n", id, dev_name,
                    safe_strerror(-sg_fd));
+        if (ENOMEM == -sg_fd)
+            pr_rusage(id);
         goto err_out;
     }
 
@@ -930,10 +963,12 @@
     int k, n, res, sg_fd, num_outstanding, do_inc, npt, pack_id, sg_flags;
     int num_waiting_read, num_to_read, sz, ern, encore_pack_id, ask, j, m, o;
     int prev_pack_id, blk_sz;
+    unsigned int thr_enomem_count = 0;
     unsigned int thr_start_eagain_count = 0;
     unsigned int thr_start_ebusy_count = 0;
     unsigned int thr_start_e2big_count = 0;
     unsigned int thr_fin_eagain_count = 0;
+    unsigned int thr_fin_ebusy_count = 0;
     unsigned int thr_start_edom_count = 0;
     unsigned int seed = 0;
     int needed_sz = op->lb_sz * op->num_lbs;
@@ -960,7 +995,7 @@
     if (op->blk_szs.size() >= (unsigned)n)
         blk_sz = op->blk_szs[id % n];
     else
-	blk_sz = DEF_LB_SZ;
+        blk_sz = DEF_LB_SZ;
     if ((UINT_MAX == op->hi_lba) && (n == (int)op->hi_lbas.size()))
         hi_lba = op->hi_lbas[id % n];
     else
@@ -985,6 +1020,8 @@
     if (sg_fd < 0) {
         pr_errno_lk(errno, "%s: id=%d, error opening file: %s", __func__, id,
                     dev_name);
+        if (ENOMEM == -sg_fd)
+            pr_rusage(id);
         return;
     }
     if (op->pack_id_force) {
@@ -998,7 +1035,7 @@
             if (needed_sz > k)
                 ioctl(sg_fd, SG_SET_RESERVED_SIZE, &needed_sz);
         }
-        if (op->cmd_time) {
+        if (op->cmd_time || op->masync) {
             struct sg_extended_info sei;
             struct sg_extended_info * seip;
 
@@ -1006,14 +1043,21 @@
             memset(seip, 0, sizeof(*seip));
             seip->sei_wr_mask |= SG_SEIM_CTL_FLAGS;
             seip->sei_rd_mask |= SG_SEIM_CTL_FLAGS;
-            seip->ctl_flags_wr_mask |= SG_CTL_FLAGM_TIME_IN_NS;
-            seip->ctl_flags_rd_mask |= SG_CTL_FLAGM_TIME_IN_NS;
-            seip->ctl_flags |= SG_CTL_FLAGM_TIME_IN_NS;
+            if (op->cmd_time) {
+                seip->ctl_flags_wr_mask |= SG_CTL_FLAGM_TIME_IN_NS;
+                seip->ctl_flags_rd_mask |= SG_CTL_FLAGM_TIME_IN_NS;
+                seip->ctl_flags |= SG_CTL_FLAGM_TIME_IN_NS;
+            }
+            if (op->masync) {
+                seip->ctl_flags_wr_mask |= SG_CTL_FLAGM_MORE_ASYNC;
+                seip->ctl_flags |= SG_CTL_FLAGM_MORE_ASYNC;
+            }
             if (ioctl(sg_fd, SG_SET_GET_EXTENDED, seip) < 0) {
                 pr2serr_lk("ioctl(EXTENDED(TIME_IN_NS)) failed, errno=%d %s\n",
                            errno, strerror(errno));
             }
-            if (! (SG_CTL_FLAGM_TIME_IN_NS & seip->ctl_flags)) {
+            if (op->cmd_time &&
+                (! (SG_CTL_FLAGM_TIME_IN_NS & seip->ctl_flags))) {
                 memset(seip, 0, sizeof(*seip));
                 seip->sei_rd_mask |= SG_SEIM_CTL_FLAGS;
                 seip->sei_wr_mask |= SG_SEIM_CTL_FLAGS;
@@ -1231,11 +1275,13 @@
             res = (op->v4) ?
                 start_sg4_cmd(sg_fd, op->c2e, pack_id, lba, lbp,
                               blk_sz * op->num_lbs, sg_flags, op->submit,
-                              thr_start_eagain_count, thr_start_ebusy_count,
-                              thr_start_e2big_count, thr_start_edom_count)  :
+                              thr_enomem_count, thr_start_eagain_count,
+                              thr_start_ebusy_count, thr_start_e2big_count,
+                              thr_start_edom_count)  :
                 start_sg3_cmd(sg_fd, op->c2e, pack_id, lba, lbp,
                               blk_sz * op->num_lbs, sg_flags, op->submit,
-                              thr_start_eagain_count, thr_start_ebusy_count,
+                              thr_enomem_count, thr_start_eagain_count,
+                              thr_start_ebusy_count,
                               thr_start_e2big_count, thr_start_edom_count);
             if (res) {
                 if (res > 1) { /* here if E2BIG, start not done, try finish */
@@ -1423,11 +1469,13 @@
             ask = pack_id;
             res = (op->v4) ?
                     finish_sg4_cmd(sg_fd, op->c2e, pack_id, op->submit,
-				   op->wait_ms, thr_fin_eagain_count,
-				   nanosecs)           :
+                                   op->wait_ms, thr_enomem_count,
+                                   thr_fin_eagain_count, thr_fin_ebusy_count,
+                                   nanosecs)           :
                     finish_sg3_cmd(sg_fd, op->c2e, pack_id, op->submit,
-			  	   op->wait_ms, thr_fin_eagain_count,
-				   nanosecs);
+                                   op->wait_ms, thr_enomem_count,
+                                   thr_fin_eagain_count, thr_fin_ebusy_count,
+                                   nanosecs);
             if (res) {
                 err = "finish_sg3_cmd()";
                 if (ruip && (pack_id > 0)) {
@@ -1522,6 +1570,7 @@
     start_ebusy_count += thr_start_ebusy_count;
     start_e2big_count += thr_start_e2big_count;
     fin_eagain_count += thr_fin_eagain_count;
+    fin_ebusy_count += thr_fin_ebusy_count;
     start_edom_count += thr_start_edom_count;
     if (op->cmd_time && op->sg_vn_ge_30901 && (npt > 0)) {
         pr2serr_lk("t_id=%d average nanosecs per cmd: %" PRId64
@@ -1686,7 +1735,7 @@
 
     op = &a_opts;
 #if 0
-    memset(op, 0, sizeof(*op));		// C++ doesn't like this
+    memset(op, 0, sizeof(*op));         // C++ doesn't like this
 #endif
     op->direct = DEF_DIRECT;
     op->lba = DEF_LBA;
@@ -1708,7 +1757,7 @@
     while (1) {
         int option_index = 0;
 
-        c = getopt_long(argc, argv, "34cdfghl:L:mM:n:NO:pq:Q:Rs:St:TuvVw:W",
+        c = getopt_long(argc, argv, "34acdfghl:L:mM:n:NO:pq:Q:Rs:St:TuvVw:W",
                         long_options, &option_index);
         if (c == -1)
             break;
@@ -1726,6 +1775,9 @@
             op->v3 = false;
             op->v3_given = false;
             break;
+        case 'a':
+            op->masync = true;
+            break;
         case 'c':
             op->cmd_time = true;
             break;
@@ -2106,14 +2158,25 @@
             cout << "Number of async_finishes: " << async_finishes.load() <<
                     endl;
             cout << "Last pack_id: " << n << endl;
-            cout << "Number of EBUSYs: " << start_ebusy_count.load() << endl;
-            cout << "Number of start EAGAINs: " << start_eagain_count.load()
-                 << endl;
-            cout << "Number of finish EAGAINs: " << fin_eagain_count.load()
-                 << endl;
-            cout << "Number of E2BIGs: " << start_e2big_count.load() << endl;
-            cout << "Number of EDOMs: " << start_edom_count.load() << endl;
         }
+        n = start_ebusy_count.load();
+        if (op->verbose || op->stats || (n > 0))
+            cout << "Number of start EBUSYs: " << n << endl;
+        n = fin_ebusy_count.load();
+        if (op->verbose || op->stats || (n > 0))
+            cout << "Number of finish EBUSYs: " << n << endl;
+        n = start_eagain_count.load();
+        if (op->verbose || op->stats || (n > 0))
+            cout << "Number of start EAGAINs: " << n << endl;
+        n = fin_eagain_count.load();
+        if (op->verbose || op->stats || (n > 0))
+            cout << "Number of finish EAGAINs: " << n << endl;
+        n = start_e2big_count.load();
+        if (op->verbose || op->stats || (n > 0))
+            cout << "Number of E2BIGs: " << n << endl;
+        n = start_edom_count.load();
+        if (op->verbose || op->stats || (n > 0))
+            cout << "Number of EDOMs: " << n << endl;
     }
     catch(system_error& e)  {
         cerr << "got a system_error exception: " << e.what() << '\n';
diff --git a/testing/sg_tst_bidi.c b/testing/sg_tst_bidi.c
index 624266c..d2a4ccf 100644
--- a/testing/sg_tst_bidi.c
+++ b/testing/sg_tst_bidi.c
@@ -53,7 +53,7 @@
  is implemented by the scsi_debug driver is used.  */
 
 
-static const char * version_str = "Version: 1.04  20190128";
+static const char * version_str = "Version: 1.05  20190501";
 
 #define INQ_REPLY_LEN 96
 #define INQ_CMD_OP 0x12
@@ -350,12 +350,12 @@
             pr2serr("\n");
         }
         io_v4p->request_len = XDWRITEREAD_10_LEN;
-        io_v4p->request = (uint64_t)xdwrrd10_cdb;
+        io_v4p->request = (uint64_t)(uintptr_t)xdwrrd10_cdb;
         io_v4p->din_xfer_len = din_len;
-        io_v4p->din_xferp = (uint64_t)(dinp + (k * din_len));
+        io_v4p->din_xferp = (uint64_t)(uintptr_t)(dinp + (k * din_len));
         io_v4p->dout_xfer_len = dout_len;
-        io_v4p->dout_xferp = (uint64_t)(doutp + (k * dout_len));
-        io_v4p->response = (uint64_t)sense_buffer[k];
+        io_v4p->dout_xferp = (uint64_t)(uintptr_t)(doutp + (k * dout_len));
+        io_v4p->response = (uint64_t)(uintptr_t)sense_buffer[k];
         io_v4p->max_response_len = SENSE_BUFFER_LEN;
         io_v4p->timeout = 20000;     /* 20000 millisecs == 20 seconds */
         io_v4p->request_extra = 99;  /* so pack_id doesn't start at 0 */
@@ -378,7 +378,7 @@
         cat = sg_err_category_new(rio_v4.device_status,
                                   rio_v4.transport_status,
                                   rio_v4.driver_status,
-                                  (const uint8_t *)rio_v4.response,
+			  (const uint8_t *)(unsigned long)rio_v4.response,
                                   rio_v4.response_len);
         switch (cat) {
         case SG_LIB_CAT_CLEAN:
@@ -392,7 +392,7 @@
             sg_linux_sense_print(NULL, rio_v4.device_status,
                                  rio_v4.transport_status,
                                  rio_v4.driver_status,
-                                 (const uint8_t *)rio_v4.response,
+			 (const uint8_t *)(unsigned long)rio_v4.response,
                                  rio_v4.response_len, true);
             break;
         }
@@ -436,11 +436,11 @@
                 pr2serr("\n");
             }
             io_v4p->request_len = XDWRITEREAD_10_LEN;
-            io_v4p->request = (uint64_t)xdwrrd10_cdb;
+            io_v4p->request = (uint64_t)(uintptr_t)xdwrrd10_cdb;
             io_v4p->din_xfer_len = din_len;
-            io_v4p->din_xferp = (uint64_t)(dinp + (k * din_len));
+            io_v4p->din_xferp = (uint64_t)(uintptr_t)(dinp + (k * din_len));
             io_v4p->dout_xfer_len = dout_len;
-            io_v4p->dout_xferp = (uint64_t)(doutp + (k * dout_len));
+            io_v4p->dout_xferp = (uint64_t)(uintptr_t)(doutp + (k * dout_len));
         } else {
             if (verbose > 2) {
                 pr2serr("    %s cdb: ", "INQUIRY");
@@ -449,11 +449,11 @@
                 pr2serr("\n");
             }
             io_v4p->request_len = sizeof(inq_cdb);
-            io_v4p->request = (uint64_t)inq_cdb;
+            io_v4p->request = (uint64_t)(uintptr_t)inq_cdb;
             io_v4p->din_xfer_len = INQ_REPLY_LEN;
-            io_v4p->din_xferp = (uint64_t)inqBuff[k];
+            io_v4p->din_xferp = (uint64_t)(uintptr_t)inqBuff[k];
         }
-        io_v4p->response = (uint64_t)sense_buffer[k];
+        io_v4p->response = (uint64_t)(uintptr_t)sense_buffer[k];
         io_v4p->max_response_len = SENSE_BUFFER_LEN;
         io_v4p->timeout = 20000;     /* 20000 millisecs == 20 seconds */
         io_v4p->request_extra = k + 3;  /* so pack_id doesn't start at 0 */
@@ -546,7 +546,7 @@
         cat = sg_err_category_new(rio_v4.device_status,
                                   rio_v4.transport_status,
                                   rio_v4.driver_status,
-                                  (const uint8_t *)rio_v4.response,
+			  (const uint8_t *)(unsigned long)rio_v4.response,
                                   rio_v4.response_len);
         switch (cat) {
         case SG_LIB_CAT_CLEAN:
@@ -560,7 +560,7 @@
             sg_linux_sense_print(NULL, rio_v4.device_status,
                                  rio_v4.transport_status,
                                  rio_v4.driver_status,
-                                 (const uint8_t *)rio_v4.response,
+			 (const uint8_t *)(unsigned long)rio_v4.response,
                                  rio_v4.response_len, true);
             break;
         }
diff --git a/testing/sg_tst_ioctl.c b/testing/sg_tst_ioctl.c
index 2dd23c2..a059643 100644
--- a/testing/sg_tst_ioctl.c
+++ b/testing/sg_tst_ioctl.c
@@ -23,6 +23,7 @@
 #include <sys/ioctl.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <sys/utsname.h>
 
 #include <sys/socket.h> /* For passing fd_s via Unix sockets */
 
@@ -56,7 +57,7 @@
  * later of the Linux sg driver.  */
 
 
-static const char * version_str = "Version: 1.09  20190430";
+static const char * version_str = "Version: 1.10  20190506";
 
 #define INQ_REPLY_LEN 128
 #define INQ_CMD_LEN 6
@@ -81,13 +82,16 @@
 static bool is_parent = false;
 static bool do_fork = false;
 static bool ioctl_only = false;
+static bool more_async = false;
 static bool q_at_tail = false;
 static bool write_only = false;
 static bool mrq_immed = false;  /* if set, also sets mrq_iosubmit */
 static bool mrq_half_immed = false;
 static bool mrq_iosubmit = false;
+static bool show_size_value = false;
 
 static int childs_pid = 0;
+static int sg_drv_ver_num = 0;
 static int q_len = DEF_Q_LEN;
 static int sleep_secs = 0;
 static int reserve_buff_sz = DEF_RESERVE_BUFF_SZ;
@@ -100,10 +104,10 @@
 static void
 usage(void)
 {
-    printf("Usage: sg_tst_ioctl [-f] [-h] [-l=Q_LEN] [-m=MRQS[,I|S]] [-r=SZ] "
-           "[-s=SEC]\n"
-           "                    [-t] [-v] [-V] [-w] <sg_device> "
-           "[<sg_device2>]\n"
+    printf("Usage: sg_tst_ioctl [-f] [-h] [-l=Q_LEN] [-m=MRQS[,I|S]] [-M] "
+           "[-o]\n"
+           "                    [-r=SZ] [-s=SEC] [-S] [-t] [-v] [-V] [-w]\n"
+           "                    <sg_device> [<sg_device2>]\n"
            " where:\n"
            "      -f      fork and test share between processes\n"
            "      -h      help: print usage message then exit\n"
@@ -116,10 +120,13 @@
            "receive;\n"
            "                     'S' is appended, then use "
            "ioctl(SG_IOSUBMIT)\n"
+           "      -M      set 'more async' flag\n"
            "      -o      ioctls only, then exit\n"
            "      -r=SZ     reserve buffer size in KB (def: 256 --> 256 "
            "KB)\n"
            "      -s=SEC    sleep between writes and reads (def: 0)\n"
+           "      -S        size of interface structures plus ioctl "
+           "values\n"
            "      -t    queue_at_tail (def: q_at_head)\n"
            "      -v    increase verbosity of output\n"
            "      -V    print version string then exit\n"
@@ -227,6 +234,28 @@
     return size;
 }
 
+static void
+set_more_async(int fd)
+{
+    if (sg_drv_ver_num > 40000) {
+        struct sg_extended_info sei;
+        struct sg_extended_info * seip;
+
+        seip = &sei;
+        memset(seip, 0, sizeof(*seip));
+        seip->sei_wr_mask |= SG_SEIM_CTL_FLAGS;
+        seip->sei_rd_mask |= SG_SEIM_CTL_FLAGS;
+        seip->ctl_flags_wr_mask |= SG_CTL_FLAGM_MORE_ASYNC;
+        seip->ctl_flags = SG_CTL_FLAGM_MORE_ASYNC;
+        if (ioctl(fd, SG_SET_GET_EXTENDED, seip) < 0) {
+            pr2serr("ioctl(SG_SET_GET_EXTENDED, MORE_ASYNC) failed, "
+                    "errno=%d %s\n", errno, strerror(errno));
+            return;
+        }
+    } else
+        pr2serr("sg driver too old for ioctl(SG_SET_GET_EXTENDED)\n");
+}
+
 static int
 tst_ioctl(const char * fnp, int sg_fd, const char * fn2p, int sg_fd2,
           int sock, const char * cp)
@@ -509,17 +538,17 @@
         /* io_hdr[k].iovec_count = 0; */  /* memset takes care of this */
         if (0 == (k % 2)) {
             h4p->request_len = sizeof(sdiag_cdb);
-            h4p->request = (uint64_t)sdiag_cdb;
+            h4p->request = (uint64_t)(uintptr_t)sdiag_cdb;
             /* all din and dout fields are zero */
         } else {
             h4p->request_len = sizeof(inq_cdb);
-            h4p->request = (uint64_t)inq_cdb;
+            h4p->request = (uint64_t)(uintptr_t)inq_cdb;
             h4p->din_xfer_len = INQ_REPLY_LEN;
-            h4p->din_xferp = (uint64_t)inqBuff;
+            h4p->din_xferp = (uint64_t)(uintptr_t)inqBuff;
             if (both)
                 h4p->flags |= SGV4_FLAG_DO_ON_OTHER;
         }
-        h4p->response = (uint64_t)sense_buffer;
+        h4p->response = (uint64_t)(uintptr_t)sense_buffer;
         h4p->max_response_len = sizeof(sense_buffer);
         h4p->timeout = 20000;     /* 20000 millisecs == 20 seconds */
         h4p->request_extra = k + 3;      /* so pack_id doesn't start at 0 */
@@ -529,7 +558,7 @@
         else
             h4p->flags |= SG_FLAG_Q_AT_HEAD;
     }
-    mrq_h4p->dout_xferp = (uint64_t)arr_v4;
+    mrq_h4p->dout_xferp = (uint64_t)(uintptr_t)arr_v4;
     mrq_h4p->dout_xfer_len = arr_v4_sz;
     mrq_h4p->din_xferp = mrq_h4p->dout_xferp;
     mrq_h4p->din_xfer_len = mrq_h4p->dout_xfer_len;
@@ -598,7 +627,7 @@
 main(int argc, char * argv[])
 {
     bool done;
-    int sg_fd, k, ok, ver_num, pack_id, num_waiting;
+    int sg_fd, k, ok, pack_id, num_waiting;
     int res = 0;
     int sg_fd2 = -1;
     int sock = -1;
@@ -657,7 +686,9 @@
                     break;
                 }
             }
-        } else if (0 == memcmp("-o", argv[k], 2))
+        } else if (0 == memcmp("-M", argv[k], 2))
+            more_async = true;
+        else if (0 == memcmp("-o", argv[k], 2))
             ioctl_only = true;
         else if (0 == memcmp("-r=", argv[k], 3)) {
             reserve_buff_sz = atoi(argv[k] + 3);
@@ -673,12 +704,16 @@
                 file_name = 0;
                 break;
             }
-        } else if (0 == memcmp("-t", argv[k], 2))
+        } else if (0 == memcmp("-S", argv[k], 2))
+            show_size_value = true;
+        else if (0 == memcmp("-t", argv[k], 2))
             q_at_tail = true;
+        else if (0 == memcmp("-vvvvvvv", argv[k], 8))
+            verbose += 7;
         else if (0 == memcmp("-vvvvvv", argv[k], 7))
-            verbose += 4;
+            verbose += 6;
         else if (0 == memcmp("-vvvvv", argv[k], 6))
-            verbose += 4;
+            verbose += 5;
         else if (0 == memcmp("-vvvv", argv[k], 5))
             verbose += 4;
         else if (0 == memcmp("-vvv", argv[k], 4))
@@ -708,6 +743,51 @@
             break;
         }
     }
+    if (show_size_value) {
+        struct utsname unam;
+
+        printf("Size in bytes:\n");
+        printf("\t%zu\tsizeof(struct sg_header) Version 2 interface "
+               "structure\n", sizeof(struct sg_header));
+        printf("\t%zu\tsizeof(struct sg_io_hdr) Version 3 interface "
+               "structure\n", sizeof(struct sg_io_hdr));
+        printf("\t%zu\tsizeof(struct sg_io_v4) Version 4 interface "
+               "structure\n", sizeof(struct sg_io_v4));
+        printf("\t%zu\tsizeof(struct sg_iovec) scatter gather element\n",
+               sizeof(struct sg_iovec));
+        printf("\t%zu\tsizeof(struct sg_scsi_id) topological device id\n",
+               sizeof(struct sg_scsi_id));
+        printf("\t%zu\tsizeof(struct sg_req_info) request information\n",
+               sizeof(struct sg_req_info));
+        printf("\t%zu\tsizeof(struct sg_extended_info) for "
+               "SG_SET_GET_EXTENDED\n",
+               sizeof(struct sg_extended_info));
+        printf("\nioctl values (i.e. second argument to ioctl()):\n");
+        printf("\t0x%lx\t\tvalue of SG_GET_NUM_WAITING ioctl\n",
+               (unsigned long)SG_GET_NUM_WAITING);
+        printf("\t0x%lx\t\tvalue of SG_IO ioctl\n",
+               (unsigned long)SG_IO);
+        printf("\t0x%lx\tvalue of SG_IOABORT ioctl\n",
+               (unsigned long)SG_IOABORT);
+        printf("\t0x%lx\tvalue of SG_IORECEIVE ioctl\n",
+               (unsigned long)SG_IORECEIVE);
+        printf("\t0x%lx\tvalue of SG_IORECEIVE_V3 ioctl\n",
+               (unsigned long)SG_IORECEIVE_V3);
+        printf("\t0x%lx\tvalue of SG_IOSUBMIT ioctl\n",
+               (unsigned long)SG_IOSUBMIT);
+        printf("\t0x%lx\tvalue of SG_IOSUBMIT_V3 ioctl\n",
+               (unsigned long)SG_IOSUBMIT_V3);
+        printf("\t0x%lx\tvalue of SG_SET_GET_EXTENDED ioctl\n",
+               (unsigned long)SG_SET_GET_EXTENDED);
+        printf("\n\t0x%x\t\tbase value of most SG_* ioctls\n",
+               SG_IOCTL_MAGIC_NUM);
+        printf("\nsizeof(void *) [a pointer] on this machine: %u bytes\n",
+               (unsigned)sizeof(void *));
+        if (0 == uname(&unam))
+            printf("Machine name: %s\n", unam.machine);
+
+        return 0;
+    }
     if (0 == file_name) {
         printf("No filename (sg device) given\n\n");
         usage();
@@ -725,12 +805,14 @@
         fprintf(stderr, "opened given file: %s successfully, fd=%d\n",
                 file_name, sg_fd);
 
-    if (ioctl(sg_fd, SG_GET_VERSION_NUM, &ver_num) < 0) {
+    if (ioctl(sg_fd, SG_GET_VERSION_NUM, &sg_drv_ver_num) < 0) {
         pr2serr("ioctl(SG_GET_VERSION_NUM) failed, errno=%d %s\n", errno,
                 strerror(errno));
         goto out;
     }
-    printf("Linux sg driver version: %d\n", ver_num);
+    printf("Linux sg driver version: %d\n", sg_drv_ver_num);
+    if (more_async)
+        set_more_async(sg_fd);
 
     if (second_fname) {
         if ((sg_fd2 = open(second_fname, O_RDWR)) < 0) {
@@ -742,6 +824,8 @@
         if (verbose)
             fprintf(stderr, "opened second file: %s successfully, fd=%d\n",
                     second_fname, sg_fd2);
+        if (more_async)
+            set_more_async(sg_fd2);
     }
 
     if (num_mrqs > 0) {
@@ -786,7 +870,7 @@
     if (do_fork && !is_parent)
         return 0;
 
-    printf("start write() calls\n");
+    printf("start iosubmit calls\n");
     for (k = 0; k < q_len; ++k) {
         /* Prepare INQUIRY command */
         memset(&io_hdr[k], 0, sizeof(sg_io_hdr_t));
@@ -866,7 +950,7 @@
     else
         printf("num_waiting: %d\n", num_waiting);
 
-    printf("\nstart read() calls\n");
+    printf("\nstart ioreceive() calls\n");
     for (k = 0, done = false; k < q_len; ++k) {
         if ((! done) && (k == q_len / 2)) {
             done = true;
diff --git a/testing/sgh_dd.cpp b/testing/sgh_dd.cpp
index 2fefc2c..bde865d 100644
--- a/testing/sgh_dd.cpp
+++ b/testing/sgh_dd.cpp
@@ -103,7 +103,7 @@
 
 using namespace std;
 
-static const char * version_str = "1.29 20190430";
+static const char * version_str = "1.30 20190505";
 
 #ifdef __GNUC__
 #ifndef  __clang__
@@ -161,6 +161,7 @@
     bool dsync;
     bool excl;
     bool fua;
+    bool masync;        /* more async sg v4 driver flag */
     bool mmap;
     bool no_dur;
     bool noshare;
@@ -273,6 +274,10 @@
 static atomic<int> mono_pack_id(0);
 static atomic<long int> pos_index(0);
 
+static atomic<int> num_ebusy(0);
+static atomic<int> num_start_eagain(0);
+static atomic<int> num_fin_eagain(0);
+
 static sigset_t signal_set;
 static pthread_t sig_listen_thread_id;
 
@@ -620,18 +625,20 @@
             "    if          file or device to read from (def: stdin)\n"
             "    iflag       comma separated list from: [coe,defres,dio,"
             "direct,dpo,\n"
-            "                dsync,excl,fua,mmap,noshare,noxfer,null,"
-            "same_fds,v3,v4]\n"
+            "                dsync,excl,fua,masync,mmap,noshare,noxfer,null,"
+            "same_fds,\n"
+            "                v3,v4]\n"
             "    of          file or device to write to (def: /dev/null "
             "N.B. different\n"
             "                from dd it defaults to stdout). If 'of=.' "
             "uses /dev/null\n"
             "    of2         second file or device to write to (def: "
             "/dev/null)\n"
-            "    oflag       comma separated list from: [append,coe,dio,"
-            "direct,dpo,\n"
-            "                dsync,excl,fua,mmap,noshare,noxfer,null,"
-            "same_fds,swait,v3,v4]\n"
+            "    oflag       comma separated list from: [append,coe,defres,"
+            "dio,direct,\n"
+            "                dpo,dsync,excl,fua,masync,mmap,noshare,noxfer,"
+            "null,\n"
+            "                same_fds,swait,v3,v4]\n"
             "    seek        block position to start writing to OFILE\n"
             "    skip        block position to start reading from IFILE\n"
             "    --help|-h      output this usage message then exit\n"
@@ -704,6 +711,7 @@
             "    excl        sets the O_EXCL flag on open()\n"
             "    fua         sets the FUA (force unit access) in SCSI READs "
             "and WRITEs\n"
+            "    masync      set 'more async' flag on this sg device\n"
             "    mmap        setup mmap IO on IFILE or OFILE; OFILE only "
             "with noshare\n"
             "    nodur       turns off command duration calculations\n"
@@ -1765,8 +1773,13 @@
     hp->flags = flags;
 
     while (((res = write(fd, hp, sizeof(struct sg_io_hdr))) < 0) &&
-           ((EINTR == errno) || (EAGAIN == errno)))
+           ((EINTR == errno) || (EAGAIN == errno) || (EBUSY == errno))) {
+        if (EAGAIN == errno)
+            ++num_start_eagain;
+        else if (EBUSY == errno)
+            ++num_ebusy;
         sched_yield();  /* another thread may be able to progress */
+    }
     err = errno;
     if (res < 0) {
         if (ENOMEM == err)
@@ -1808,8 +1821,13 @@
         return res;
     }
     while (((res = ioctl(fd, SG_IOSUBMIT, h4p)) < 0) &&
-           ((EINTR == errno) || (EAGAIN == errno)))
+           ((EINTR == errno) || (EAGAIN == errno) || (EBUSY == errno))) {
+        if (EAGAIN == errno)
+            ++num_start_eagain;
+        else if (EBUSY == errno)
+            ++num_ebusy;
         sched_yield();  /* another thread may be able to progress */
+    }
     err = errno;
     if (res < 0) {
         if (ENOMEM == err)
@@ -1876,8 +1894,13 @@
     io_hdr.pack_id = pack_id;
 
     while (((res = read(fd, &io_hdr, sizeof(struct sg_io_hdr))) < 0) &&
-           ((EINTR == errno) || (EAGAIN == errno)))
+           ((EINTR == errno) || (EAGAIN == errno) || (EBUSY == errno))) {
+        if (EAGAIN == errno)
+            ++num_fin_eagain;
+        else if (EBUSY == errno)
+            ++num_ebusy;
         sched_yield();  /* another thread may be able to progress */
+    }
     if (res < 0) {
         perror("finishing io [read(2)] on sg device, error");
         return -1;
@@ -1930,8 +1953,13 @@
     h4p = &rep->io_hdr4;
     h4p->request_extra = pack_id;
     while (((res = ioctl(fd, SG_IORECEIVE, h4p)) < 0) &&
-           ((EINTR == errno) || (EAGAIN == errno)))
+           ((EINTR == errno) || (EAGAIN == errno) || (EBUSY == errno))) {
+        if (EAGAIN == errno)
+            ++num_fin_eagain;
+        else if (EBUSY == errno)
+            ++num_ebusy;
         sched_yield();  /* another thread may be able to progress */
+    }
     if (res < 0) {
         perror("finishing io [SG_IORECEIVE] on sg device, error");
         return -1;
@@ -2154,7 +2182,7 @@
 /* Returns reserved_buffer_size/mmap_size if success, else 0 for failure */
 static int
 sg_prepare_resbuf(int fd, int bs, int bpt, bool def_res, int elem_sz,
-                  bool unit_nano, bool no_dur, uint8_t **mmpp)
+                  bool unit_nano, bool no_dur, bool masync, uint8_t **mmpp)
 {
     int res, t, num;
     uint8_t *mmp;
@@ -2189,11 +2217,17 @@
                 pr2serr_lk("sgh_dd: %s: SG_SET_GET_EXTENDED(SGAT_ELEM_SZ) "
                            "wr error: %s\n", __func__, strerror(errno));
         }
-    } else if (no_dur) {
+    } else if (no_dur || masync) {
         memset(seip, 0, sizeof(*seip));
         seip->sei_wr_mask |= SG_SEIM_CTL_FLAGS;
-        seip->ctl_flags_wr_mask |= SG_CTL_FLAGM_NO_DURATION;
-        seip->ctl_flags |= SG_CTL_FLAGM_NO_DURATION;
+        if (no_dur) {
+            seip->ctl_flags_wr_mask |= SG_CTL_FLAGM_NO_DURATION;
+            seip->ctl_flags |= SG_CTL_FLAGM_NO_DURATION;
+        }
+        if (masync) {
+            seip->ctl_flags_wr_mask |= SG_CTL_FLAGM_MORE_ASYNC;
+            seip->ctl_flags |= SG_CTL_FLAGM_MORE_ASYNC;
+        }
         res = ioctl(fd, SG_SET_GET_EXTENDED, seip);
         if (res < 0)
             pr2serr_lk("sgh_dd: %s: SG_SET_GET_EXTENDED(NO_DURATION) "
@@ -2268,6 +2302,8 @@
             fp->excl = true;
         else if (0 == strcmp(cp, "fua"))
             fp->fua = true;
+        else if (0 == strcmp(cp, "masync"))
+            fp->masync = true;
         else if (0 == strcmp(cp, "mmap"))
             fp->mmap = true;
         else if (0 == strcmp(cp, "nodur"))
@@ -2333,7 +2369,7 @@
     }
     n = sg_prepare_resbuf(fd, clp->bs, clp->bpt, clp->in_flags.defres,
                           clp->elem_sz, clp->unit_nanosec,
-                          clp->in_flags.no_dur, mmpp);
+                          clp->in_flags.no_dur, clp->in_flags.masync, mmpp);
     if (n <= 0)
         return -SG_LIB_FILE_ERROR;
     if (mmap_lenp)
@@ -2364,7 +2400,7 @@
     }
     n = sg_prepare_resbuf(fd, clp->bs, clp->bpt, clp->out_flags.defres,
                           clp->elem_sz, clp->unit_nanosec,
-                          clp->out_flags.no_dur, mmpp);
+                          clp->out_flags.no_dur, clp->out_flags.masync, mmpp);
     if (n <= 0)
         return -SG_LIB_FILE_ERROR;
     if (mmap_lenp)
@@ -3196,5 +3232,13 @@
     if (clp->sum_of_resids)
         pr2serr(">> Non-zero sum of residual counts=%d\n",
                clp->sum_of_resids);
+    if (clp->debug && (num_start_eagain > 0))
+        pr2serr("Number of start EAGAINs: %d\n", num_start_eagain.load());
+    if (clp->debug && (num_fin_eagain > 0))
+        pr2serr("Number of finish EAGAINs: %d\n", num_fin_eagain.load());
+    if (clp->debug && (num_ebusy > 0)) {
+        pr2serr("Number of EBUSYs: %d\n", num_ebusy.load());
+
+    }
     return (res >= 0) ? res : SG_LIB_CAT_OTHER;
 }
diff --git a/testing/sgs_dd.c b/testing/sgs_dd.c
index f806e61..991f801 100644
--- a/testing/sgs_dd.c
+++ b/testing/sgs_dd.c
@@ -78,7 +78,7 @@
 #include "sg_unaligned.h"
 
 
-static const char * version_str = "4.06 20190501";
+static const char * version_str = "4.07 20190503";
 static const char * my_name = "sgs_dd";
 
 #define DEF_BLOCK_SIZE 512
@@ -291,7 +291,7 @@
     if (res < 0) {
         if (ENOMEM == errno)
             return 1;
-        if ((EDOM == errno) || (EAGAIN == errno)) {
+        if ((EDOM == errno) || (EAGAIN == errno) || (EBUSY == errno)) {
             rep->state = SGQ_IO_WAIT;   /* busy so wait */
             return 0;
         }
@@ -307,18 +307,18 @@
     memset(h4p, 0, sizeof(struct sg_io_v4));
     h4p->guard = 'Q';
     h4p->request_len = sizeof(rep->cmd);
-    h4p->request = (uint64_t)rep->cmd;
+    h4p->request = (uint64_t)(uintptr_t)rep->cmd;
     if (rep->wr) {
         h4p->dout_xfer_len = clp->bs * rep->num_blks;
-        h4p->dout_xferp = (uint64_t)rep->buffp;
+        h4p->dout_xferp = (uint64_t)(uintptr_t)rep->buffp;
     } else if (rep->num_blks > 0) {
         h4p->din_xfer_len = clp->bs * rep->num_blks;
-        h4p->din_xferp = (uint64_t)rep->buffp;
+        h4p->din_xferp = (uint64_t)(uintptr_t)rep->buffp;
     }
     h4p->max_response_len = sizeof(rep->sb);
-    h4p->response = (uint64_t)rep->sb;
+    h4p->response = (uint64_t)(uintptr_t)rep->sb;
     h4p->timeout = DEF_TIMEOUT;
-    h4p->usr_ptr = (uint64_t)rep;
+    h4p->usr_ptr = (uint64_t)(uintptr_t)rep;
     h4p->request_extra = rep->blk;/* N.B. blk --> pack_id --> request_extra */
     if (flagp->dio)
         h4p->flags |= SG_FLAG_DIRECT_IO;
@@ -333,7 +333,7 @@
     if (res < 0) {
         if (ENOMEM == errno)
             return 1;
-        if ((EDOM == errno) || (EAGAIN == errno)) {
+        if ((EDOM == errno) || (EAGAIN == errno) || (EBUSY == errno)) {
             rep->state = SGQ_IO_WAIT;   /* busy so wait */
             return 0;
         }
@@ -365,7 +365,7 @@
         goto do_v4;
     memset(&io_hdr, 0 , sizeof(sg_io_hdr_t));
     while (((res = read(fd, &io_hdr, sizeof(sg_io_hdr_t))) < 0) &&
-           (EINTR == errno))
+           ((EINTR == errno) || (EAGAIN == errno)))
         ;
     rep = (Rq_elem *)io_hdr.usr_ptr;
     if (rep)
@@ -414,9 +414,10 @@
 do_v4:
     memset(&io_v4, 0 , sizeof(io_v4));
     io_v4.guard = 'Q';
-    while (((res = ioctl(fd, SG_IORECEIVE, &io_v4)) < 0) && (EINTR == errno))
+    while (((res = ioctl(fd, SG_IORECEIVE, &io_v4)) < 0) &&
+           ((EINTR == errno) || (EAGAIN == errno)))
         ;
-    rep = (Rq_elem *)io_v4.usr_ptr;
+    rep = (Rq_elem *)(unsigned long)io_v4.usr_ptr;
     if (res < 0) {
         fprintf(stderr, "%s: ioctl(SG_IORECEIVE): %s [%d]\n", __func__,
                 strerror(errno),
@@ -438,7 +439,7 @@
 
     res = sg_err_category_new(h4p->device_status, h4p->transport_status,
                               h4p->driver_status,
-                              (const uint8_t *)h4p->response,
+                      (const uint8_t *)(unsigned long)h4p->response,
                               h4p->response_len);
     switch (res) {
         case SG_LIB_CAT_CLEAN:
@@ -453,7 +454,7 @@
             sg_linux_sense_print(rep->wr ? "writing": "reading",
                                  h4p->device_status, h4p->transport_status,
                                  h4p->driver_status,
-                                 (const uint8_t *)h4p->response,
+                         (const uint8_t *)(unsigned long)h4p->response,
                                  h4p->response_len, true);
             rep->state = SGQ_IO_ERR;
             return -1;
diff --git a/testing/uapi_sg.h b/testing/uapi_sg.h
index 87700c3..fa7f2ea 100644
--- a/testing/uapi_sg.h
+++ b/testing/uapi_sg.h
@@ -14,7 +14,7 @@
  * Later extensions (versions 2, 3 and 4) to driver:
  *   Copyright (C) 1998 - 2018 Douglas Gilbert
  *
- * Version 4.0.11 (20190427)
+ * Version 4.0.11 (20190502)
  *  This version is for Linux 4 and 5 series kernels.
  *
  * Documentation
@@ -202,7 +202,8 @@
 #define SG_CTL_FLAGM_MASTER_FINI 0x100	/* wr> 0: setup for repeat slave req */
 #define SG_CTL_FLAGM_MASTER_ERR	0x200	/* rd: sharing, master got error */
 #define SG_CTL_FLAGM_NO_DURATION 0x400	/* don't calc command duration */
-#define SG_CTL_FLAGM_ALL_BITS	0x7ff	/* should be OR of previous items */
+#define SG_CTL_FLAGM_MORE_ASYNC 0x800	/* yield EAGAIN in more cases */
+#define SG_CTL_FLAGM_ALL_BITS	0xfff	/* should be OR of previous items */
 
 /* Write one of the following values to sg_extended_info::read_value, get... */
 #define SG_SEIRV_INT_MASK	0x0	/* get SG_SEIM_ALL_BITS */