diff --git a/Marlin/src/gcode/sd/M20.cpp b/Marlin/src/gcode/sd/M20.cpp
index 7ac4affdae9..57318383385 100644
--- a/Marlin/src/gcode/sd/M20.cpp
+++ b/Marlin/src/gcode/sd/M20.cpp
@@ -33,7 +33,7 @@
 void GcodeSuite::M20() {
   if (card.flag.mounted) {
     SERIAL_ECHOLNPGM(STR_BEGIN_FILE_LIST);
-    card.ls();
+    card.ls(TERN_(LONG_FILENAME_HOST_SUPPORT, parser.boolval('L')));
     SERIAL_ECHOLNPGM(STR_END_FILE_LIST);
   }
   else
diff --git a/Marlin/src/sd/cardreader.cpp b/Marlin/src/sd/cardreader.cpp
index 6af9bafc7a8..90c39feaf8b 100644
--- a/Marlin/src/sd/cardreader.cpp
+++ b/Marlin/src/sd/cardreader.cpp
@@ -258,54 +258,84 @@ void CardReader::selectByName(SdFile dir, const char * const match) {
   }
 }
 
-//
-// Recursive method to print all files within a folder in flat
-// DOS 8.3 format. This style of listing is the most compatible
-// with legacy hosts.
-//
-// This method recurses to unlimited depth and lists every
-// G-code file within the given parent. If the hierarchy is
-// very deep this can blow up the stack, so a 'depth' parameter
-// (as with printListingJSON) would be a good addition.
-//
-void CardReader::printListing(SdFile parent, const char * const prepend/*=nullptr*/) {
+/**
+ * Recursive method to print all files within a folder in flat
+ * DOS 8.3 format. This style of listing is the most compatible
+ * with legacy hosts.
+ *
+ * This method recurses to unlimited depth and lists all G-code
+ * files within the given parent. If the hierarchy is very deep
+ * this can blow up the stack, so a 'depth' parameter would be a
+ * good addition.
+ */
+void CardReader::printListing(
+  SdFile parent
+  OPTARG(LONG_FILENAME_HOST_SUPPORT, const bool includeLongNames/*=false*/)
+  , const char * const prepend/*=nullptr*/
+  OPTARG(LONG_FILENAME_HOST_SUPPORT, const char * const prependLong/*=nullptr*/)
+) {
   dir_t p;
   while (parent.readDir(&p, longFilename) > 0) {
     if (DIR_IS_SUBDIR(&p)) {
 
-      // Get the short name for the item, which we know is a folder
-      char dosFilename[FILENAME_LENGTH];
+      size_t lenPrepend = prepend ? strlen(prepend) + 1 : 0;
+      // Allocate enough stack space for the full path including / separator
+      char path[lenPrepend + FILENAME_LENGTH];
+      if (prepend) {
+        strcpy(path, prepend);
+        path[lenPrepend - 1] = '/';
+      }
+      char* dosFilename = path + lenPrepend;
       createFilename(dosFilename, p);
 
-      // Allocate enough stack space for the full path to a folder, trailing slash, and nul
-      const bool prepend_is_empty = (!prepend || prepend[0] == '\0');
-      const int len = (prepend_is_empty ? 1 : strlen(prepend)) + strlen(dosFilename) + 1 + 1;
-      char path[len];
-
-      // Append the FOLDERNAME12/ to the passed string.
-      // It contains the full path to the "parent" argument.
-      // We now have the full path to the item in this folder.
-      strcpy(path, prepend_is_empty ? "/" : prepend); // root slash if prepend is empty
-      strcat(path, dosFilename);                      // FILENAME_LENGTH characters maximum
-      strcat(path, "/");                              // 1 character
-
-      // Serial.print(path);
-
       // Get a new directory object using the full path
       // and dive recursively into it.
       SdFile child; // child.close() in destructor
       if (child.open(&parent, dosFilename, O_READ))
-        printListing(child, path);
+        #if ENABLED(LONG_FILENAME_HOST_SUPPORT)
+          if (includeLongNames) {
+            size_t lenPrependLong = prependLong ? strlen(prependLong) + 1 : 0;
+            // Allocate enough stack space for the full long path including / separator
+            char pathLong[lenPrependLong + strlen(longFilename) + 1];
+            if (prependLong) {
+              strcpy(pathLong, prependLong);
+              pathLong[lenPrependLong - 1] = '/';
+            }
+            strcpy(pathLong + lenPrependLong, longFilename);
+            printListing(child, true, path, pathLong);
+          }
+          else
+            printListing(child, false, path);
+        #else
+          printListing(child, path);
+        #endif
       else {
         SERIAL_ECHO_MSG(STR_SD_CANT_OPEN_SUBDIR, dosFilename);
         return;
       }
     }
     else if (is_dir_or_gcode(p)) {
-      if (prepend) SERIAL_ECHO(prepend);
+      if (prepend) {
+        SERIAL_ECHO(prepend);
+        SERIAL_CHAR('/');
+      }
       SERIAL_ECHO(createFilename(filename, p));
       SERIAL_CHAR(' ');
-      SERIAL_ECHOLN(p.fileSize);
+      #if ENABLED(LONG_FILENAME_HOST_SUPPORT)
+        if (!includeLongNames)
+      #endif
+          SERIAL_ECHOLN(p.fileSize);
+      #if ENABLED(LONG_FILENAME_HOST_SUPPORT)
+        else {
+          SERIAL_ECHO(p.fileSize);
+          SERIAL_CHAR(' ');
+          if (prependLong) {
+            SERIAL_ECHO(prependLong);
+            SERIAL_CHAR('/');
+          }
+          SERIAL_ECHOLN(longFilename[0] ? longFilename : "???");
+        }
+      #endif
     }
   }
 }
@@ -313,10 +343,10 @@ void CardReader::printListing(SdFile parent, const char * const prepend/*=nullpt
 //
 // List all files on the SD card
 //
-void CardReader::ls() {
+void CardReader::ls(TERN_(LONG_FILENAME_HOST_SUPPORT, bool includeLongNames/*=false*/)) {
   if (flag.mounted) {
     root.rewind();
-    printListing(root);
+    printListing(root OPTARG(LONG_FILENAME_HOST_SUPPORT, includeLongNames));
   }
 }
 
diff --git a/Marlin/src/sd/cardreader.h b/Marlin/src/sd/cardreader.h
index 943cdae7418..66cb97baeb9 100644
--- a/Marlin/src/sd/cardreader.h
+++ b/Marlin/src/sd/cardreader.h
@@ -199,7 +199,7 @@ public:
     FORCE_INLINE static void getfilename_sorted(const uint16_t nr) { selectFileByIndex(nr); }
   #endif
 
-  static void ls();
+  static void ls(TERN_(LONG_FILENAME_HOST_SUPPORT, bool includeLongNames=false));
 
   #if ENABLED(POWER_LOSS_RECOVERY)
     static bool jobRecoverFileExists();
@@ -330,7 +330,12 @@ private:
   static int countItems(SdFile dir);
   static void selectByIndex(SdFile dir, const uint8_t index);
   static void selectByName(SdFile dir, const char * const match);
-  static void printListing(SdFile parent, const char * const prepend=nullptr);
+  static void printListing(
+    SdFile parent
+    OPTARG(LONG_FILENAME_HOST_SUPPORT, const bool includeLongNames=false)
+    , const char * const prepend=nullptr
+    OPTARG(LONG_FILENAME_HOST_SUPPORT, const char * const prependLong=nullptr)
+  );
 
   #if ENABLED(SDCARD_SORT_ALPHA)
     static void flush_presort();