From 33453517eb6d3535bbf2e13f40495cfcbd2bebcd Mon Sep 17 00:00:00 2001 From: Malash Date: Sat, 10 Oct 2026 17:16:50 +0800 Subject: [PATCH] feat(device): support ml307x device --- internal/device/manager.go | 7 ++++++- internal/modem/discovery.go | 34 ++++++++++++++++++++++++++-------- 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/internal/device/manager.go b/internal/device/manager.go index 8b1b29af..dbea068e 100644 --- a/internal/device/manager.go +++ b/internal/device/manager.go @@ -807,7 +807,12 @@ func (manager *Manager) ExecuteAT( commandCtx, cancel := manager.withTimeout(ctx, manager.commandTimeout) defer cancel() response, err := client.Execute(commandCtx, command) - manager.setResult(id, state, nil, err) + // An unsupported CLCC query does not indicate an unhealthy control channel. + var commandErr *modem.CommandError + if !strings.EqualFold(strings.TrimSpace(command), "AT+CLCC") || + !errors.As(err, &commandErr) || strings.TrimSpace(commandErr.Final) != "+CME ERROR: 4" { + manager.setResult(id, state, nil, err) + } return response, err } diff --git a/internal/modem/discovery.go b/internal/modem/discovery.go index 20a7e925..623c679a 100644 --- a/internal/modem/discovery.go +++ b/internal/modem/discovery.go @@ -21,6 +21,15 @@ const ( quectelVendorID = "2c7c" ) +var ml307USBIDs = [...]struct { + vendorID string + productID string + atInterface int +}{ + {"2ecc", "3012", 2}, + {"2c91", "0002", 3}, +} + type SysFSDiscoverer struct { SysRoot string DevRoot string @@ -166,13 +175,11 @@ func (d *SysFSDiscoverer) Discover(ctx context.Context) ([]Candidate, error) { } return left.Name < right.Name }) - if IsML307(state.candidate) { - // ML307 USB compositions expose AT endpoints on interfaces 02 and 03. - // Prefer interface 02, matching the tested ML307A composition. + if atInterface, ok := ml307ATInterface(state.candidate); ok { for index := range state.candidate.Ports { port := &state.candidate.Ports[index] port.Role = PortRoleUnknown - if port.InterfaceNumber == 2 { + if port.InterfaceNumber == atInterface { port.Role = PortRoleAT state.candidate.ATPort = *port } @@ -210,11 +217,22 @@ func (d *SysFSDiscoverer) Discover(ctx context.Context) ([]Candidate, error) { // IsML307 identifies the ML307 USB modem family by its USB identity or product string. func IsML307(candidate Candidate) bool { - if strings.EqualFold(strings.TrimSpace(candidate.VendorID), "2ecc") && - strings.EqualFold(strings.TrimSpace(candidate.ProductID), "3012") { - return true + _, ok := ml307ATInterface(candidate) + return ok +} + +func ml307ATInterface(candidate Candidate) (int, bool) { + vendorID := strings.TrimSpace(candidate.VendorID) + productID := strings.TrimSpace(candidate.ProductID) + for _, id := range ml307USBIDs { + if vendorID == id.vendorID && productID == id.productID { + return id.atInterface, true + } + } + if strings.Contains(strings.ToUpper(candidate.Product), "ML307") { + return 2, true } - return strings.Contains(strings.ToUpper(candidate.Product), "ML307") + return 0, false } // IsDJI4GUSB reports whether a USB identity belongs to the first-generation