usbip: poll the devlist periodically in standard static mode

Against a plain usbipd (no control extension) the client synced the
device list exactly once and then slept until shutdown: devices that
changed busid on replug were never rediscovered, and in all-devices
mode a worker that stopped had no path back. Refresh the devlist on an
interval, re-probe the control extension every few minutes in case the
server was upgraded, and warn that serial rules cannot match against
servers whose listings carry no serial numbers.
This commit is contained in:
世界
2026-06-10 09:25:01 +08:00
parent 4e9eedc1e8
commit 001ef124a7
2 changed files with 38 additions and 2 deletions
+11
View File
@@ -51,6 +51,17 @@ func (a *clientAssignment) Matched() bool {
return len(a.targets) > 0
}
// HasSerialTargets reports whether any match rule requires a serial
// number. targets is immutable after construction.
func (a *clientAssignment) HasSerialTargets() bool {
for _, target := range a.targets {
if target.match.Serial != "" {
return true
}
}
return false
}
func (a *clientAssignment) SetActive(busid string, active bool) {
a.access.Lock()
defer a.access.Unlock()
+27 -2
View File
@@ -23,6 +23,13 @@ const (
controlSessionIdleHint = "control session lost"
controlHandshakeBackoffStart = time.Second
controlHandshakeBackoffMax = 30 * time.Second
// Standard usbipd has no change notifications; poll the devlist so
// replugged or re-enumerated devices are discovered, and re-probe
// the control extension occasionally in case the server was
// upgraded to sing-box.
staticModeRefreshInterval = 15 * time.Second
staticModeControlRetryInterval = 10 * time.Minute
)
var (
@@ -115,12 +122,30 @@ func (c *ClientService) run() {
}
func (c *ClientService) runStandardStaticMode() error {
if c.assignment.HasSerialTargets() {
c.logger.Warn("serial device matches cannot be evaluated against a standard usbipd server (its device list carries no serial numbers)")
}
err := c.syncRemoteStateContext(c.ctx)
if err != nil {
return E.Cause(err, "initial static devlist sync")
}
<-c.ctx.Done()
return nil
refresh := time.NewTicker(staticModeRefreshInterval)
defer refresh.Stop()
retryControl := time.NewTimer(staticModeControlRetryInterval)
defer retryControl.Stop()
for {
select {
case <-c.ctx.Done():
return nil
case <-retryControl.C:
return nil
case <-refresh.C:
err = c.syncRemoteStateContext(c.ctx)
if err != nil && c.ctx.Err() == nil {
c.logger.Debug("static devlist refresh: ", err)
}
}
}
}
func (c *ClientService) runControlSession() error {