diff --git a/extmod/fsusermount.c b/extmod/fsusermount.c
index 74525ef743670c8d9dd26603aeeef8e23e992241..441711ae2e1323a03b57273c170021072d2d1858 100644
--- a/extmod/fsusermount.c
+++ b/extmod/fsusermount.c
@@ -76,8 +76,15 @@ STATIC mp_obj_t fatfs_mount_mkfs(mp_uint_t n_args, const mp_obj_t *pos_args, mp_
         // load block protocol methods
         mp_load_method(device, MP_QSTR_readblocks, vfs->readblocks);
         mp_load_method_maybe(device, MP_QSTR_writeblocks, vfs->writeblocks);
-        mp_load_method_maybe(device, MP_QSTR_sync, vfs->sync);
-        mp_load_method(device, MP_QSTR_count, vfs->count);
+        mp_load_method_maybe(device, MP_QSTR_ioctl, vfs->u.ioctl);
+        if (vfs->u.ioctl[0] != MP_OBJ_NULL) {
+            // device supports new block protocol, so indicate it
+            vfs->u.old.count[1] = MP_OBJ_SENTINEL;
+        } else {
+            // no ioctl method, so assume the device uses the old block protocol
+            mp_load_method_maybe(device, MP_QSTR_sync, vfs->u.old.sync);
+            mp_load_method(device, MP_QSTR_count, vfs->u.old.count);
+        }
 
         // Read-only device indicated by writeblocks[0] == MP_OBJ_NULL.
         // User can specify read-only device by:
diff --git a/extmod/fsusermount.h b/extmod/fsusermount.h
index c141ecb99af26634542c8e9df33db9cf9c6c078d..53442a36890bd1bf9eb5f2db2939112c1b83bf4f 100644
--- a/extmod/fsusermount.h
+++ b/extmod/fsusermount.h
@@ -29,8 +29,15 @@ typedef struct _fs_user_mount_t {
     mp_uint_t len;
     mp_obj_t readblocks[4];
     mp_obj_t writeblocks[4];
-    mp_obj_t sync[2];
-    mp_obj_t count[2];
+    // new protocol uses just ioctl, old uses sync (optional) and count
+    // if ioctl[3]=count[1]=MP_OBJ_SENTINEL then we have the new protocol, else old
+    union {
+        mp_obj_t ioctl[4];
+        struct {
+            mp_obj_t sync[2];
+            mp_obj_t count[2];
+        } old;
+    } u;
     FATFS fatfs;
 } fs_user_mount_t;
 
diff --git a/py/qstrdefs.h b/py/qstrdefs.h
index bf774e036b6bd189d70a0069af5caff42fd1d011..0a2cc2c8e6adbefccecc896f82ad33d8296d212a 100644
--- a/py/qstrdefs.h
+++ b/py/qstrdefs.h
@@ -675,6 +675,7 @@ Q(readonly)
 Q(mkfs)
 Q(readblocks)
 Q(writeblocks)
+Q(ioctl)
 Q(sync)
 Q(count)
 #endif
diff --git a/stmhal/diskio.c b/stmhal/diskio.c
index 3e53cdaa50107407fef382e5a6d789d0e3fbad2d..14eb9e297ecb51264f7d4194a0b1e5ddf2d1612f 100644
--- a/stmhal/diskio.c
+++ b/stmhal/diskio.c
@@ -39,6 +39,12 @@
 #include "sdcard.h"
 #include "extmod/fsusermount.h"
 
+// constants for block protocol ioctl
+//#define BP_IOCTL_INIT           (1) // unused
+//#define BP_IOCTL_DEINIT         (2) // unused
+#define BP_IOCTL_SYNC           (3)
+#define BP_IOCTL_SEC_COUNT      (4)
+
 /*-----------------------------------------------------------------------*/
 /* Initialize a Drive                                                    */
 /*-----------------------------------------------------------------------*/
@@ -242,29 +248,57 @@ DRESULT disk_ioctl (
             break;
 #endif
 
-        case PD_USER:
-            if (MP_STATE_PORT(fs_user_mount) == NULL) {
+        case PD_USER: {
+            fs_user_mount_t *vfs = MP_STATE_PORT(fs_user_mount);
+            if (vfs == NULL) {
                 // nothing mounted
                 return RES_ERROR;
             }
-            switch (cmd) {
-                case CTRL_SYNC:
-                    if (MP_STATE_PORT(fs_user_mount)->sync[0] != MP_OBJ_NULL) {
-                        mp_call_method_n_kw(0, 0, MP_STATE_PORT(fs_user_mount)->sync);
+            if (vfs->u.old.count[1] == MP_OBJ_SENTINEL) {
+                // new protocol with ioctl
+                switch (cmd) {
+                    case CTRL_SYNC:
+                        vfs->u.ioctl[2] = MP_OBJ_NEW_SMALL_INT(BP_IOCTL_SYNC);
+                        vfs->u.ioctl[3] = MP_OBJ_NEW_SMALL_INT(0); // unused
+                        mp_call_method_n_kw(2, 0, vfs->u.ioctl);
+                        vfs->u.ioctl[3] = MP_OBJ_SENTINEL; // indicate new protocol
+                        return RES_OK;
+
+                    case GET_SECTOR_COUNT: {
+                        vfs->u.ioctl[2] = MP_OBJ_NEW_SMALL_INT(BP_IOCTL_SEC_COUNT);
+                        vfs->u.ioctl[3] = MP_OBJ_NEW_SMALL_INT(0); // unused
+                        mp_obj_t ret = mp_call_method_n_kw(2, 0, vfs->u.ioctl);
+                        *((DWORD*)buff) = mp_obj_get_int(ret);
+                        vfs->u.ioctl[3] = MP_OBJ_SENTINEL; // indicate new protocol
+                        return RES_OK;
                     }
-                    return RES_OK;
 
-                case GET_BLOCK_SIZE:
-                    *((DWORD*)buff) = 1; // high-level sector erase size in units of the small (512) bl
-                    return RES_OK;
+                    case GET_BLOCK_SIZE:
+                        *((DWORD*)buff) = 1; // erase block size in units of sector size
+                        return RES_OK;
+                }
+            } else {
+                // old protocol with sync and count
+                switch (cmd) {
+                    case CTRL_SYNC:
+                        if (vfs->u.old.sync[0] != MP_OBJ_NULL) {
+                            mp_call_method_n_kw(0, 0, vfs->u.old.sync);
+                        }
+                        return RES_OK;
+
+                    case GET_SECTOR_COUNT: {
+                        mp_obj_t ret = mp_call_method_n_kw(0, 0, vfs->u.old.count);
+                        *((DWORD*)buff) = mp_obj_get_int(ret);
+                        return RES_OK;
+                    }
 
-                case GET_SECTOR_COUNT: {
-                    mp_obj_t ret = mp_call_method_n_kw(0, 0, MP_STATE_PORT(fs_user_mount)->count);
-                    *((DWORD*)buff) = mp_obj_get_int(ret);
-                    return RES_OK;
+                    case GET_BLOCK_SIZE:
+                        *((DWORD*)buff) = 1; // erase block size in units of sector size
+                        return RES_OK;
                 }
             }
             break;
+        }
     }
 
     return RES_PARERR;