using System.IO.Ports; using System.Management; using System.Text.RegularExpressions; namespace FirmwareFlasher; /// /// WMI(Win32_PnPEntity)로 COM 포트를 전부 열거하고 VID/PID로 종류만 라벨링한다. /// 어떤 포트도 제외하지 않는다 — JTAG로도, UART(CP210x/CH340/FTDI)로도 플래시할 수 있으므로 /// 전부 보여주고 사용자가 고른다. JTAG(303A:1001)는 맨 위로 올려 자동선택을 돕는다. /// public static partial class PortScanner { [GeneratedRegex(@"\(COM(\d+)\)")] private static partial Regex ComRe(); [GeneratedRegex(@"VID_([0-9A-Fa-f]{4})")] private static partial Regex VidRe(); [GeneratedRegex(@"PID_([0-9A-Fa-f]{4})")] private static partial Regex PidRe(); public static List Scan() { var list = new List(); try { using var searcher = new ManagementObjectSearcher( "SELECT Name, DeviceID FROM Win32_PnPEntity WHERE Name LIKE '%(COM%'"); foreach (ManagementBaseObject mo in searcher.Get()) { var name = mo["Name"] as string ?? ""; var devId = mo["DeviceID"] as string ?? ""; var cm = ComRe().Match(name); if (!cm.Success) continue; string? vid = VidRe().Match(devId) is { Success: true } v ? v.Groups[1].Value.ToUpperInvariant() : null; string? pid = PidRe().Match(devId) is { Success: true } p ? p.Groups[1].Value.ToUpperInvariant() : null; list.Add(new ComPortInfo { PortName = $"COM{cm.Groups[1].Value}", Number = int.TryParse(cm.Groups[1].Value, out var n) ? n : 9999, FriendlyName = name, DeviceId = devId, Vid = vid, Pid = pid, Kind = KindOf(vid, pid), }); mo.Dispose(); } } catch { /* WMI 실패 시 빈 목록 */ } // JTAG는 맨 위에, 그 중 '최신(번호 높은)' 것을 먼저 — 보드 리셋으로 번호가 올라가면 // 옛 번호는 유령 포트가 되므로 높은 번호가 살아있을 확률이 높다. var jtag = list.Where(p => p.IsJtag).OrderByDescending(p => p.Number); var rest = list.Where(p => !p.IsJtag).OrderBy(p => p.Number); return jtag.Concat(rest).ToList(); } /// /// 포트를 실제로 열 수 있는지 빠르게 확인(유령 포트 판별). /// DTR/RTS를 건드리지 않아 보드를 리셋하지 않는다. /// public static bool CanOpen(string portName) { try { using var sp = new SerialPort(portName) { DtrEnable = false, RtsEnable = false }; sp.Open(); return true; } catch { return false; } } static string KindOf(string? vid, string? pid) => (vid, pid) switch { ("303A", "1001") => "USB-JTAG (내장)", ("10C4", _) => "CP210x", ("1A86", "7523") => "CH340", ("1A86", _) => "CH34x/CH9102", ("0403", _) => "FTDI", ("067B", _) => "PL2303", _ => "USB 직렬", }; }