From 9ea04956fa62996db780f7084a01ac28292d58f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Fri, 24 Apr 2026 05:07:15 +0800 Subject: [PATCH] Fix Darwin USB/IP availability and doorbell handling --- service/usbip/client_darwin.go | 11 ++++-- service/usbip/darwin_integration_test.go | 46 ++++++++++++++++++++---- service/usbip/server_darwin.go | 9 ++++- 3 files changed, 56 insertions(+), 10 deletions(-) diff --git a/service/usbip/client_darwin.go b/service/usbip/client_darwin.go index fe8cf2eef..0624e89a5 100644 --- a/service/usbip/client_darwin.go +++ b/service/usbip/client_darwin.go @@ -882,15 +882,22 @@ func (c *darwinVirtualController) handleDoorbell(doorbell uint32) { c.logger.Debug("process doorbell: ", err) return } + var previousNoResponse unsafe.Pointer for { transfer := endpoint.currentTransfer() if transfer.ptr == nil || !transfer.message.valid() { return } - status, length := c.handleTransfer(key, transfer.message) if transfer.message.noResponse() { - return + if transfer.ptr == previousNoResponse { + return + } + previousNoResponse = transfer.ptr + c.handleTransfer(key, transfer.message) + continue } + previousNoResponse = nil + status, length := c.handleTransfer(key, transfer.message) if err := endpoint.complete(transfer, darwinUSBIPStatusToCIStatus(status), length); err != nil { c.logger.Debug("complete transfer: ", err) c.Close() diff --git a/service/usbip/darwin_integration_test.go b/service/usbip/darwin_integration_test.go index 6768b5575..07e3aa0bc 100644 --- a/service/usbip/darwin_integration_test.go +++ b/service/usbip/darwin_integration_test.go @@ -205,8 +205,8 @@ func TestWaitDarwinControllerClosesOnContextCancel(t *testing.T) { } type fakeDarwinEndpointStateMachine struct { - transfer darwinCITransfer - currentRead bool + transfers []darwinCITransfer + currentRead int completeCalled int } @@ -221,11 +221,12 @@ func (f *fakeDarwinEndpointStateMachine) processDoorbell(uint32) error { } func (f *fakeDarwinEndpointStateMachine) currentTransfer() darwinCITransfer { - if f.currentRead { + if f.currentRead >= len(f.transfers) { return darwinCITransfer{} } - f.currentRead = true - return f.transfer + transfer := f.transfers[f.currentRead] + f.currentRead++ + return transfer } func (f *fakeDarwinEndpointStateMachine) complete(darwinCITransfer, int, int) error { @@ -242,10 +243,10 @@ func TestDarwinHandleDoorbellSkipsNoResponseCompletion(t *testing.T) { data0: (uint32(2) << 8) | 1, } endpoint := &fakeDarwinEndpointStateMachine{ - transfer: darwinCITransfer{ + transfers: []darwinCITransfer{{ ptr: unsafe.Pointer(&message), message: message, - }, + }}, } controller.endpoints[darwinEndpointKey{device: 1, endpoint: 2}] = endpoint @@ -253,6 +254,37 @@ func TestDarwinHandleDoorbellSkipsNoResponseCompletion(t *testing.T) { require.Zero(t, endpoint.completeCalled) } +func TestDarwinHandleDoorbellContinuesAfterNoResponseTransfer(t *testing.T) { + t.Parallel() + + controller := newDarwinVirtualController(context.Background(), newTestLogger(), nil, DeviceInfoTruncated{}) + noResponseMessage := darwinCIMessage{ + control: (1 << 15) | (1 << 14) | 0x3c, + data0: (uint32(2) << 8) | 1, + } + responseMessage := darwinCIMessage{ + control: (1 << 15) | 0x3c, + data0: (uint32(2) << 8) | 1, + } + endpoint := &fakeDarwinEndpointStateMachine{ + transfers: []darwinCITransfer{ + { + ptr: unsafe.Pointer(&noResponseMessage), + message: noResponseMessage, + }, + { + ptr: unsafe.Pointer(&responseMessage), + message: responseMessage, + }, + }, + } + controller.endpoints[darwinEndpointKey{device: 1, endpoint: 2}] = endpoint + + controller.handleDoorbell((uint32(2) << 8) | 1) + require.Equal(t, 1, endpoint.completeCalled) + require.Equal(t, 2, endpoint.currentRead) +} + func startDarwinFakeUSBIPServer(t *testing.T) *darwinFakeUSBIPServer { t.Helper() diff --git a/service/usbip/server_darwin.go b/service/usbip/server_darwin.go index 9ef05b399..30b3fa7a8 100644 --- a/service/usbip/server_darwin.go +++ b/service/usbip/server_darwin.go @@ -384,7 +384,12 @@ func (s *ServerService) handleImport(conn net.Conn) { _ = WriteOpRepImport(conn, OpStatusError, nil) return } - defer s.releaseClaim(busid) + releaseClaim := true + defer func() { + if releaseClaim { + s.releaseClaim(busid) + } + }() info := export.entry.Info if err := WriteOpRepImport(conn, OpStatusOK, &info); err != nil { s.logger.Warn("reply import ", busid, ": ", err) @@ -395,6 +400,8 @@ func (s *ServerService) handleImport(conn net.Conn) { if err := session.serve(); err != nil && s.ctx.Err() == nil { s.logger.Debug("data session ", busid, ": ", err) } + s.releaseClaim(busid) + releaseClaim = false s.broadcastChanged() }