@@ -19,6 +19,7 @@ import (
1919 "regexp"
2020 "runtime"
2121 "runtime/pprof"
22+ "sort"
2223 "strconv"
2324 "strings"
2425 "sync"
@@ -946,112 +947,130 @@ func touchSerialPortAt1200bps(port string) (err error) {
946947}
947948
948949func flashUF2UsingMSD (volumes []string , tmppath string , options * compileopts.Options ) error {
949- // find standard UF2 info path
950- infoPaths := make ([]string , 0 , len (volumes ))
951- for _ , volume := range volumes {
952- switch runtime .GOOS {
953- case "linux" , "freebsd" :
954- fi , err := os .Stat ("/run/media" )
955- if err != nil || ! fi .IsDir () {
956- infoPaths = append (infoPaths , "/media/*/" + volume + "/INFO_UF2.TXT" )
957- } else {
958- infoPaths = append (infoPaths , "/run/media/*/" + volume + "/INFO_UF2.TXT" )
959- }
960- case "darwin" :
961- infoPaths = append (infoPaths , "/Volumes/" + volume + "/INFO_UF2.TXT" )
962- case "windows" :
963- path , err := windowsFindUSBDrive (volume , options )
964- if err == nil {
965- infoPaths = append (infoPaths , path + "/INFO_UF2.TXT" )
950+ for start := time .Now (); time .Since (start ) < options .Timeout ; {
951+ // Find a UF2 mount point.
952+ mounts , err := findFATMounts (options )
953+ if err != nil {
954+ return err
955+ }
956+ for _ , mount := range mounts {
957+ for _ , volume := range volumes {
958+ if mount .name != volume {
959+ continue
960+ }
961+ if _ , err := os .Stat (filepath .Join (mount .path , "INFO_UF2.TXT" )); err != nil {
962+ // No INFO_UF2.TXT found, which is expected on a UF2
963+ // filesystem.
964+ continue
965+ }
966+ // Found the filesystem, so flash the device!
967+ return moveFile (tmppath , filepath .Join (mount .path , "flash.uf2" ))
966968 }
967969 }
970+ time .Sleep (500 * time .Millisecond )
968971 }
969-
970- d , err := locateDevice (volumes , infoPaths , options .Timeout )
971- if err != nil {
972- return err
973- }
974-
975- return moveFile (tmppath , filepath .Dir (d )+ "/flash.uf2" )
972+ return errors .New ("unable to locate any volume: [" + strings .Join (volumes , "," ) + "]" )
976973}
977974
978975func flashHexUsingMSD (volumes []string , tmppath string , options * compileopts.Options ) error {
979- // find expected volume path
980- destPaths := make ([]string , 0 , len (volumes ))
981- for _ , volume := range volumes {
982- switch runtime .GOOS {
983- case "linux" , "freebsd" :
984- fi , err := os .Stat ("/run/media" )
985- if err != nil || ! fi .IsDir () {
986- destPaths = append (destPaths , "/media/*/" + volume )
987- } else {
988- destPaths = append (destPaths , "/run/media/*/" + volume )
989- }
990- case "darwin" :
991- destPaths = append (destPaths , "/Volumes/" + volume )
992- case "windows" :
993- path , err := windowsFindUSBDrive (volume , options )
994- if err == nil {
995- destPaths = append (destPaths , path + "/" )
976+ for start := time .Now (); time .Since (start ) < options .Timeout ; {
977+ // Find all mount points.
978+ mounts , err := findFATMounts (options )
979+ if err != nil {
980+ return err
981+ }
982+ for _ , mount := range mounts {
983+ for _ , volume := range volumes {
984+ if mount .name != volume {
985+ continue
986+ }
987+ // Found the filesystem, so flash the device!
988+ return moveFile (tmppath , filepath .Join (mount .path , "flash.hex" ))
996989 }
997990 }
991+ time .Sleep (500 * time .Millisecond )
998992 }
993+ return errors .New ("unable to locate any volume: [" + strings .Join (volumes , "," ) + "]" )
994+ }
999995
1000- d , err := locateDevice (volumes , destPaths , options .Timeout )
1001- if err != nil {
1002- return err
1003- }
1004-
1005- return moveFile (tmppath , d + "/flash.hex" )
996+ type mountPoint struct {
997+ name string
998+ path string
1006999}
10071000
1008- func locateDevice (volumes , paths []string , timeout time.Duration ) (string , error ) {
1009- var d []string
1010- var err error
1011- for start := time .Now (); time .Since (start ) < timeout ; {
1012- for _ , path := range paths {
1013- d , err = filepath .Glob (path )
1014- if err != nil {
1015- return "" , err
1001+ // Find all the mount points on the system that use the FAT filesystem.
1002+ func findFATMounts (options * compileopts.Options ) ([]mountPoint , error ) {
1003+ var points []mountPoint
1004+ switch runtime .GOOS {
1005+ case "darwin" :
1006+ list , err := os .ReadDir ("/Volumes" )
1007+ if err != nil {
1008+ return nil , fmt .Errorf ("could not list mount points: %w" , err )
1009+ }
1010+ for _ , elem := range list {
1011+ // TODO: find a way to check for the filesystem type.
1012+ // (Only return FAT filesystems).
1013+ points = append (points , mountPoint {
1014+ name : elem .Name (),
1015+ path : filepath .Join ("/Volumes" , elem .Name ()),
1016+ })
1017+ }
1018+ sort .Slice (points , func (i , j int ) bool {
1019+ return points [i ].path < points [j ].name
1020+ })
1021+ return points , nil
1022+ case "linux" :
1023+ tab , err := os .ReadFile ("/proc/mounts" ) // symlink to /proc/self/mounts on my system
1024+ if err != nil {
1025+ return nil , fmt .Errorf ("could not list mount points: %w" , err )
1026+ }
1027+ for _ , line := range strings .Split (string (tab ), "\n " ) {
1028+ fields := strings .Fields (line )
1029+ if len (fields ) <= 2 {
1030+ continue
10161031 }
1017- if d != nil {
1018- break
1032+ fstype := fields [2 ]
1033+ if fstype != "vfat" {
1034+ continue
10191035 }
1036+ points = append (points , mountPoint {
1037+ name : filepath .Base (fields [1 ]),
1038+ path : fields [1 ],
1039+ })
1040+ }
1041+ return points , nil
1042+ case "windows" :
1043+ // Obtain a list of all currently mounted volumes.
1044+ cmd := executeCommand (options , "wmic" ,
1045+ "PATH" , "Win32_LogicalDisk" ,
1046+ "get" , "DeviceID,VolumeName,FileSystem,DriveType" )
1047+ var out bytes.Buffer
1048+ cmd .Stdout = & out
1049+ err := cmd .Run ()
1050+ if err != nil {
1051+ return nil , fmt .Errorf ("could not list mount points: %w" , err )
10201052 }
10211053
1022- if d != nil {
1023- break
1024- }
1025-
1026- time .Sleep (500 * time .Millisecond )
1027- }
1028- if d == nil {
1029- return "" , errors .New ("unable to locate any volume: [" + strings .Join (volumes , "," ) + "]" )
1030- }
1031- return d [0 ], nil
1032- }
1033-
1034- func windowsFindUSBDrive (volume string , options * compileopts.Options ) (string , error ) {
1035- cmd := executeCommand (options , "wmic" ,
1036- "PATH" , "Win32_LogicalDisk" , "WHERE" , "VolumeName = '" + volume + "'" ,
1037- "get" , "DeviceID,VolumeName,FileSystem,DriveType" )
1038-
1039- var out bytes.Buffer
1040- cmd .Stdout = & out
1041- err := cmd .Run ()
1042- if err != nil {
1043- return "" , err
1044- }
1045-
1046- for _ , line := range strings .Split (out .String (), "\n " ) {
1047- words := strings .Fields (line )
1048- if len (words ) >= 3 {
1049- if words [1 ] == "2" && words [2 ] == "FAT" {
1050- return words [0 ], nil
1054+ // Extract data to convert to a []mountPoint slice.
1055+ for _ , line := range strings .Split (out .String (), "\n " ) {
1056+ words := strings .Fields (line )
1057+ if len (words ) < 3 {
1058+ continue
10511059 }
1060+ if words [1 ] != "2" || words [2 ] != "FAT" {
1061+ // - DriveType 2 is removable (which we're looking for).
1062+ // - We only want to return FAT filesystems.
1063+ continue
1064+ }
1065+ points = append (points , mountPoint {
1066+ name : words [3 ],
1067+ path : words [0 ],
1068+ })
10521069 }
1070+ return points , nil
1071+ default :
1072+ return nil , fmt .Errorf ("unknown GOOS for listing mount points: %s" , runtime .GOOS )
10531073 }
1054- return "" , errors .New ("unable to locate a USB device to be flashed" )
10551074}
10561075
10571076// getDefaultPort returns the default serial port depending on the operating system.
0 commit comments