diff --git a/.github/update.log b/.github/update.log index a4c273b745..ff8b2a0176 100644 --- a/.github/update.log +++ b/.github/update.log @@ -1140,3 +1140,4 @@ Update On Tue Sep 30 20:35:53 CEST 2025 Update On Wed Oct 1 20:41:09 CEST 2025 Update On Thu Oct 2 20:40:58 CEST 2025 Update On Fri Oct 3 20:39:08 CEST 2025 +Update On Sat Oct 4 20:34:01 CEST 2025 diff --git a/clash-meta/go.mod b/clash-meta/go.mod index 9ce928ce6c..86e34b4949 100644 --- a/clash-meta/go.mod +++ b/clash-meta/go.mod @@ -24,12 +24,12 @@ require ( github.com/metacubex/fswatch v0.1.1 github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 github.com/metacubex/kcp-go v0.0.0-20250923001605-1ba6f691c45b - github.com/metacubex/quic-go v0.54.1-0.20250926001022-e2a3ce003b3a + github.com/metacubex/quic-go v0.55.1-0.20251004050223-450bd9e32033 github.com/metacubex/randv2 v0.2.0 github.com/metacubex/restls-client-go v0.1.7 github.com/metacubex/sing v0.5.6 github.com/metacubex/sing-mux v0.3.4 - github.com/metacubex/sing-quic v0.0.0-20250909002258-06122df8f231 + github.com/metacubex/sing-quic v0.0.0-20251004051927-c45ee18473bb github.com/metacubex/sing-shadowsocks v0.2.12 github.com/metacubex/sing-shadowsocks2 v0.2.7 github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2 @@ -105,7 +105,6 @@ require ( github.com/vishvananda/netns v0.0.4 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect - go.uber.org/mock v0.4.0 // indirect golang.org/x/mod v0.20.0 // indirect golang.org/x/text v0.22.0 // indirect golang.org/x/time v0.7.0 // indirect diff --git a/clash-meta/go.sum b/clash-meta/go.sum index 7a00f21136..d22b475cdc 100644 --- a/clash-meta/go.sum +++ b/clash-meta/go.sum @@ -112,8 +112,8 @@ github.com/metacubex/kcp-go v0.0.0-20250923001605-1ba6f691c45b h1:z7JLKjugnQ1qvD github.com/metacubex/kcp-go v0.0.0-20250923001605-1ba6f691c45b/go.mod h1:HIJZW4QMhbBqXuqC1ly6Hn0TEYT2SzRw58ns1yGhXTs= github.com/metacubex/nftables v0.0.0-20250503052935-30a69ab87793 h1:1Qpuy+sU3DmyX9HwI+CrBT/oLNJngvBorR2RbajJcqo= github.com/metacubex/nftables v0.0.0-20250503052935-30a69ab87793/go.mod h1:RjRNb4G52yAgfR+Oe/kp9G4PJJ97Fnj89eY1BFO3YyA= -github.com/metacubex/quic-go v0.54.1-0.20250926001022-e2a3ce003b3a h1:l7BWjOifmqM2zMi+AMrgIx1z4KJt0oY/6cHW11kA9IQ= -github.com/metacubex/quic-go v0.54.1-0.20250926001022-e2a3ce003b3a/go.mod h1:1lktQFtCD17FZliVypbrDHwbsFSsmz2xz2TRXydvB5c= +github.com/metacubex/quic-go v0.55.1-0.20251004050223-450bd9e32033 h1:LEzvR5AmHEatqE6IWgMBUJHnaiz9VJfZeDGOiHFuWZU= +github.com/metacubex/quic-go v0.55.1-0.20251004050223-450bd9e32033/go.mod h1:1lktQFtCD17FZliVypbrDHwbsFSsmz2xz2TRXydvB5c= github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs= github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY= github.com/metacubex/restls-client-go v0.1.7 h1:eCwiXCTQb5WJu9IlgYvDBA1OgrINv58dEe7hcN5H15k= @@ -123,8 +123,8 @@ github.com/metacubex/sing v0.5.6 h1:mEPDCadsCj3DB8gn+t/EtposlYuALEkExa/LUguw6/c= github.com/metacubex/sing v0.5.6/go.mod h1:ypf0mjwlZm0sKdQSY+yQvmsbWa0hNPtkeqyRMGgoN+w= github.com/metacubex/sing-mux v0.3.4 h1:tf4r27CIkzaxq9kBlAXQkgMXq2HPp5Mta60Kb4RCZF0= github.com/metacubex/sing-mux v0.3.4/go.mod h1:SEJfAuykNj/ozbPqngEYqyggwSr81+L7Nu09NRD5mh4= -github.com/metacubex/sing-quic v0.0.0-20250909002258-06122df8f231 h1:dGvo7UahC/gYBQNBoictr14baJzBjAKUAorP63QFFtg= -github.com/metacubex/sing-quic v0.0.0-20250909002258-06122df8f231/go.mod h1:B60FxaPHjR1SeQB0IiLrgwgvKsaoASfOWdiqhLjmMGA= +github.com/metacubex/sing-quic v0.0.0-20251004051927-c45ee18473bb h1:gxrJmnxuEAel+kh3V7ntqkHjURif0xKDu76nzr/BF5Y= +github.com/metacubex/sing-quic v0.0.0-20251004051927-c45ee18473bb/go.mod h1:JK4+PYUKps6pnlicKjsSUAjAcvIUjhorIjdNZGg930M= github.com/metacubex/sing-shadowsocks v0.2.12 h1:Wqzo8bYXrK5aWqxu/TjlTnYZzAKtKsaFQBdr6IHFaBE= github.com/metacubex/sing-shadowsocks v0.2.12/go.mod h1:2e5EIaw0rxKrm1YTRmiMnDulwbGxH9hAFlrwQLQMQkU= github.com/metacubex/sing-shadowsocks2 v0.2.7 h1:hSuuc0YpsfiqYqt1o+fP4m34BQz4e6wVj3PPBVhor3A= @@ -219,7 +219,6 @@ gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec/go.mod h1:BZ1RAo go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= -go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M= go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= diff --git a/clash-meta/transport/hysteria/congestion/brutal.go b/clash-meta/transport/hysteria/congestion/brutal.go index 601949dec2..737bc1fb46 100644 --- a/clash-meta/transport/hysteria/congestion/brutal.go +++ b/clash-meta/transport/hysteria/congestion/brutal.go @@ -2,6 +2,8 @@ package congestion import ( "github.com/metacubex/quic-go/congestion" + "github.com/metacubex/quic-go/monotime" + "time" ) @@ -47,11 +49,11 @@ func (b *BrutalSender) SetRTTStatsProvider(rttStats congestion.RTTStatsProvider) b.rttStats = rttStats } -func (b *BrutalSender) TimeUntilSend(bytesInFlight congestion.ByteCount) time.Time { +func (b *BrutalSender) TimeUntilSend(bytesInFlight congestion.ByteCount) monotime.Time { return b.pacer.TimeUntilSend() } -func (b *BrutalSender) HasPacingBudget(now time.Time) bool { +func (b *BrutalSender) HasPacingBudget(now monotime.Time) bool { return b.pacer.Budget(now) >= b.maxDatagramSize } @@ -67,13 +69,13 @@ func (b *BrutalSender) GetCongestionWindow() congestion.ByteCount { return congestion.ByteCount(float64(b.bps) * rtt.Seconds() * 1.5 / b.ackRate) } -func (b *BrutalSender) OnPacketSent(sentTime time.Time, bytesInFlight congestion.ByteCount, +func (b *BrutalSender) OnPacketSent(sentTime monotime.Time, bytesInFlight congestion.ByteCount, packetNumber congestion.PacketNumber, bytes congestion.ByteCount, isRetransmittable bool) { b.pacer.SentPacket(sentTime, bytes) } func (b *BrutalSender) OnPacketAcked(number congestion.PacketNumber, ackedBytes congestion.ByteCount, - priorInFlight congestion.ByteCount, eventTime time.Time) { + priorInFlight congestion.ByteCount, eventTime monotime.Time) { // Stub } @@ -82,8 +84,8 @@ func (b *BrutalSender) OnCongestionEvent(number congestion.PacketNumber, lostByt // Stub } -func (b *BrutalSender) OnCongestionEventEx(priorInFlight congestion.ByteCount, eventTime time.Time, ackedPackets []congestion.AckedPacketInfo, lostPackets []congestion.LostPacketInfo) { - currentTimestamp := eventTime.Unix() +func (b *BrutalSender) OnCongestionEventEx(priorInFlight congestion.ByteCount, eventTime monotime.Time, ackedPackets []congestion.AckedPacketInfo, lostPackets []congestion.LostPacketInfo) { + currentTimestamp := int64(eventTime) slot := currentTimestamp % pktInfoSlotCount if b.pktInfoSlots[slot].Timestamp == currentTimestamp { b.pktInfoSlots[slot].LossCount += uint64(len(lostPackets)) diff --git a/clash-meta/transport/hysteria/congestion/pacer.go b/clash-meta/transport/hysteria/congestion/pacer.go index 2dff53008d..83c9322810 100644 --- a/clash-meta/transport/hysteria/congestion/pacer.go +++ b/clash-meta/transport/hysteria/congestion/pacer.go @@ -2,6 +2,8 @@ package congestion import ( "github.com/metacubex/quic-go/congestion" + "github.com/metacubex/quic-go/monotime" + "math" "time" ) @@ -15,7 +17,7 @@ const ( type pacer struct { budgetAtLastSent congestion.ByteCount maxDatagramSize congestion.ByteCount - lastSentTime time.Time + lastSentTime monotime.Time getBandwidth func() congestion.ByteCount // in bytes/s } @@ -28,7 +30,7 @@ func newPacer(getBandwidth func() congestion.ByteCount) *pacer { return p } -func (p *pacer) SentPacket(sendTime time.Time, size congestion.ByteCount) { +func (p *pacer) SentPacket(sendTime monotime.Time, size congestion.ByteCount) { budget := p.Budget(sendTime) if size > budget { p.budgetAtLastSent = 0 @@ -38,7 +40,7 @@ func (p *pacer) SentPacket(sendTime time.Time, size congestion.ByteCount) { p.lastSentTime = sendTime } -func (p *pacer) Budget(now time.Time) congestion.ByteCount { +func (p *pacer) Budget(now monotime.Time) congestion.ByteCount { if p.lastSentTime.IsZero() { return p.maxBurstSize() } @@ -54,10 +56,10 @@ func (p *pacer) maxBurstSize() congestion.ByteCount { } // TimeUntilSend returns when the next packet should be sent. -// It returns the zero value of time.Time if a packet can be sent immediately. -func (p *pacer) TimeUntilSend() time.Time { +// It returns the zero value of monotime.Time if a packet can be sent immediately. +func (p *pacer) TimeUntilSend() monotime.Time { if p.budgetAtLastSent >= p.maxDatagramSize { - return time.Time{} + return monotime.Time(0) } return p.lastSentTime.Add(maxDuration( minPacingDelay, diff --git a/clash-meta/transport/tuic/congestion/bandwidth_sampler.go b/clash-meta/transport/tuic/congestion/bandwidth_sampler.go index e415fe7a6e..ed7ce0727b 100644 --- a/clash-meta/transport/tuic/congestion/bandwidth_sampler.go +++ b/clash-meta/transport/tuic/congestion/bandwidth_sampler.go @@ -5,6 +5,7 @@ import ( "time" "github.com/metacubex/quic-go/congestion" + "github.com/metacubex/quic-go/monotime" ) var ( @@ -36,7 +37,7 @@ type SendTimeState struct { type ConnectionStateOnSentPacket struct { packetNumber congestion.PacketNumber // Time at which the packet is sent. - sendTime time.Time + sendTime monotime.Time // Size of the packet. size congestion.ByteCount // The value of |totalBytesSentAtLastAckedPacket| at the time the @@ -44,10 +45,10 @@ type ConnectionStateOnSentPacket struct { totalBytesSentAtLastAckedPacket congestion.ByteCount // The value of |lastAckedPacketSentTime| at the time the packet was // sent. - lastAckedPacketSentTime time.Time + lastAckedPacketSentTime monotime.Time // The value of |lastAckedPacketAckTime| at the time the packet was // sent. - lastAckedPacketAckTime time.Time + lastAckedPacketAckTime monotime.Time // Send time states that are returned to the congestion controller when the // packet is acked or lost. sendTimeState SendTimeState @@ -166,9 +167,9 @@ type BandwidthSampler struct { totalBytesSentAtLastAckedPacket congestion.ByteCount // The time at which the last acknowledged packet was sent. Set to // QuicTime::Zero() if no valid timestamp is available. - lastAckedPacketSentTime time.Time + lastAckedPacketSentTime monotime.Time // The time at which the most recent packet was acknowledged. - lastAckedPacketAckTime time.Time + lastAckedPacketAckTime monotime.Time // The most recently sent packet. lastSendPacket congestion.PacketNumber // Indicates whether the bandwidth sampler is currently in an app-limited @@ -194,7 +195,7 @@ func NewBandwidthSampler() *BandwidthSampler { // packets are sent in order. The information about the packet will not be // released from the sampler until it the packet is either acknowledged or // declared lost. -func (s *BandwidthSampler) OnPacketSent(sentTime time.Time, lastSentPacket congestion.PacketNumber, sentBytes, bytesInFlight congestion.ByteCount, hasRetransmittableData bool) { +func (s *BandwidthSampler) OnPacketSent(sentTime monotime.Time, lastSentPacket congestion.PacketNumber, sentBytes, bytesInFlight congestion.ByteCount, hasRetransmittableData bool) { s.lastSendPacket = lastSentPacket if !hasRetransmittableData { @@ -224,7 +225,7 @@ func (s *BandwidthSampler) OnPacketSent(sentTime time.Time, lastSentPacket conge // OnPacketAcked Notifies the sampler that the |lastAckedPacket| is acknowledged. Returns a // bandwidth sample. If no bandwidth sample is available, // QuicBandwidth::Zero() is returned. -func (s *BandwidthSampler) OnPacketAcked(ackTime time.Time, lastAckedPacket congestion.PacketNumber) *BandwidthSample { +func (s *BandwidthSampler) OnPacketAcked(ackTime monotime.Time, lastAckedPacket congestion.PacketNumber) *BandwidthSample { sentPacketState := s.connectionStats.Get(lastAckedPacket) if sentPacketState == nil { return NewBandwidthSample() @@ -238,7 +239,7 @@ func (s *BandwidthSampler) OnPacketAcked(ackTime time.Time, lastAckedPacket cong // onPacketAckedInner Handles the actual bandwidth calculations, whereas the outer method handles // retrieving and removing |sentPacket|. -func (s *BandwidthSampler) onPacketAckedInner(ackTime time.Time, lastAckedPacket congestion.PacketNumber, sentPacket *ConnectionStateOnSentPacket) *BandwidthSample { +func (s *BandwidthSampler) onPacketAckedInner(ackTime monotime.Time, lastAckedPacket congestion.PacketNumber, sentPacket *ConnectionStateOnSentPacket) *BandwidthSample { s.totalBytesAcked += sentPacket.size s.totalBytesSentAtLastAckedPacket = sentPacket.sendTimeState.totalBytesSent @@ -336,7 +337,7 @@ type ConnectionStates struct { stats map[congestion.PacketNumber]*ConnectionStateOnSentPacket } -func (s *ConnectionStates) Insert(packetNumber congestion.PacketNumber, sentTime time.Time, bytes congestion.ByteCount, sampler *BandwidthSampler) bool { +func (s *ConnectionStates) Insert(packetNumber congestion.PacketNumber, sentTime monotime.Time, bytes congestion.ByteCount, sampler *BandwidthSampler) bool { if _, ok := s.stats[packetNumber]; ok { return false } @@ -357,7 +358,7 @@ func (s *ConnectionStates) Remove(packetNumber congestion.PacketNumber) (bool, * return ok, state } -func NewConnectionStateOnSentPacket(packetNumber congestion.PacketNumber, sentTime time.Time, bytes congestion.ByteCount, sampler *BandwidthSampler) *ConnectionStateOnSentPacket { +func NewConnectionStateOnSentPacket(packetNumber congestion.PacketNumber, sentTime monotime.Time, bytes congestion.ByteCount, sampler *BandwidthSampler) *ConnectionStateOnSentPacket { return &ConnectionStateOnSentPacket{ packetNumber: packetNumber, sendTime: sentTime, diff --git a/clash-meta/transport/tuic/congestion/bbr_sender.go b/clash-meta/transport/tuic/congestion/bbr_sender.go index 93f90ba877..7f41d5be7e 100644 --- a/clash-meta/transport/tuic/congestion/bbr_sender.go +++ b/clash-meta/transport/tuic/congestion/bbr_sender.go @@ -9,6 +9,7 @@ import ( "github.com/metacubex/quic-go" "github.com/metacubex/quic-go/congestion" + "github.com/metacubex/quic-go/monotime" "github.com/metacubex/randv2" ) @@ -121,13 +122,13 @@ type bbrSender struct { // Tracks the maximum number of bytes acked faster than the sending rate. maxAckHeight *WindowedFilter // The time this aggregation started and the number of bytes acked during it. - aggregationEpochStartTime time.Time + aggregationEpochStartTime monotime.Time aggregationEpochBytes congestion.ByteCount // Minimum RTT estimate. Automatically expires within 10 seconds (and // triggers PROBE_RTT mode) if no new value is sampled during that period. minRtt time.Duration // The time at which the current value of |min_rtt_| was assigned. - minRttTimestamp time.Time + minRttTimestamp monotime.Time // The maximum allowed number of bytes in flight. congestionWindow congestion.ByteCount // The initial value of the |congestion_window_|. @@ -160,7 +161,7 @@ type bbrSender struct { // pacing gain cycle. cycleCurrentOffset int // The time at which the last pacing gain cycle was started. - lastCycleStart time.Time + lastCycleStart monotime.Time // Indicates whether the connection has reached the full bandwidth mode. isAtFullBandwidth bool // Number of rounds during which there was no significant bandwidth increase. @@ -172,7 +173,7 @@ type bbrSender struct { // Time at which PROBE_RTT has to be exited. Setting it to zero indicates // that the time is yet unknown as the number of packets in flight has not // reached the required value. - exitProbeRttAt time.Time + exitProbeRttAt monotime.Time // Indicates whether a round-trip has passed since PROBE_RTT became active. probeRttRoundPassed bool // Indicates whether the most recent bandwidth sample was marked as @@ -277,12 +278,12 @@ func (b *bbrSender) GetBytesInFlight() congestion.ByteCount { } // TimeUntilSend returns when the next packet should be sent. -func (b *bbrSender) TimeUntilSend(bytesInFlight congestion.ByteCount) time.Time { +func (b *bbrSender) TimeUntilSend(bytesInFlight congestion.ByteCount) monotime.Time { b.bytesInFlight = bytesInFlight return b.pacer.TimeUntilSend() } -func (b *bbrSender) HasPacingBudget(now time.Time) bool { +func (b *bbrSender) HasPacingBudget(now monotime.Time) bool { return b.pacer.Budget(now) >= b.maxDatagramSize } @@ -298,7 +299,7 @@ func (b *bbrSender) SetMaxDatagramSize(s congestion.ByteCount) { b.pacer.SetMaxDatagramSize(s) } -func (b *bbrSender) OnPacketSent(sentTime time.Time, bytesInFlight congestion.ByteCount, packetNumber congestion.PacketNumber, bytes congestion.ByteCount, isRetransmittable bool) { +func (b *bbrSender) OnPacketSent(sentTime monotime.Time, bytesInFlight congestion.ByteCount, packetNumber congestion.PacketNumber, bytes congestion.ByteCount, isRetransmittable bool) { b.pacer.SentPacket(sentTime, bytes) b.lastSendPacket = packetNumber @@ -335,7 +336,7 @@ func (b *bbrSender) MaybeExitSlowStart() { } -func (b *bbrSender) OnPacketAcked(number congestion.PacketNumber, ackedBytes congestion.ByteCount, priorInFlight congestion.ByteCount, eventTime time.Time) { +func (b *bbrSender) OnPacketAcked(number congestion.PacketNumber, ackedBytes congestion.ByteCount, priorInFlight congestion.ByteCount, eventTime monotime.Time) { // Stub } @@ -343,7 +344,7 @@ func (b *bbrSender) OnCongestionEvent(number congestion.PacketNumber, lostBytes // Stub } -func (b *bbrSender) OnCongestionEventEx(priorInFlight congestion.ByteCount, eventTime time.Time, ackedPackets []congestion.AckedPacketInfo, lostPackets []congestion.LostPacketInfo) { +func (b *bbrSender) OnCongestionEventEx(priorInFlight congestion.ByteCount, eventTime monotime.Time, ackedPackets []congestion.AckedPacketInfo, lostPackets []congestion.LostPacketInfo) { totalBytesAckedBefore := b.sampler.totalBytesAcked isRoundStart, minRttExpired := false, false @@ -490,7 +491,7 @@ func (b *bbrSender) UpdateRoundTripCounter(lastAckedPacket congestion.PacketNumb return false } -func (b *bbrSender) UpdateBandwidthAndMinRtt(now time.Time, ackedPackets []congestion.AckedPacketInfo) bool { +func (b *bbrSender) UpdateBandwidthAndMinRtt(now monotime.Time, ackedPackets []congestion.AckedPacketInfo) bool { sampleMinRtt := InfiniteRTT for _, packet := range ackedPackets { @@ -610,7 +611,7 @@ func (b *bbrSender) UpdateRecoveryState(hasLosses, isRoundStart bool) { } } -func (b *bbrSender) UpdateAckAggregationBytes(ackTime time.Time, ackedBytes congestion.ByteCount) congestion.ByteCount { +func (b *bbrSender) UpdateAckAggregationBytes(ackTime monotime.Time, ackedBytes congestion.ByteCount) congestion.ByteCount { // Compute how many bytes are expected to be delivered, assuming max bandwidth // is correct. expectedAckedBytes := congestion.ByteCount(b.maxBandwidth.GetBest()) * @@ -630,7 +631,7 @@ func (b *bbrSender) UpdateAckAggregationBytes(ackTime time.Time, ackedBytes cong return b.aggregationEpochBytes - expectedAckedBytes } -func (b *bbrSender) UpdateGainCyclePhase(now time.Time, priorInFlight congestion.ByteCount, hasLosses bool) { +func (b *bbrSender) UpdateGainCyclePhase(now monotime.Time, priorInFlight congestion.ByteCount, hasLosses bool) { bytesInFlight := b.GetBytesInFlight() // In most cases, the cycle is advanced after an RTT passes. shouldAdvanceGainCycling := now.Sub(b.lastCycleStart) > b.GetMinRtt() @@ -697,7 +698,7 @@ func (b *bbrSender) CheckIfFullBandwidthReached() { } } -func (b *bbrSender) MaybeExitStartupOrDrain(now time.Time) { +func (b *bbrSender) MaybeExitStartupOrDrain(now monotime.Time) { if b.mode == STARTUP && b.isAtFullBandwidth { b.OnExitStartup(now) b.mode = DRAIN @@ -709,7 +710,7 @@ func (b *bbrSender) MaybeExitStartupOrDrain(now time.Time) { } } -func (b *bbrSender) EnterProbeBandwidthMode(now time.Time) { +func (b *bbrSender) EnterProbeBandwidthMode(now monotime.Time) { b.mode = PROBE_BW b.congestionWindowGain = b.congestionWindowGainConst @@ -725,7 +726,7 @@ func (b *bbrSender) EnterProbeBandwidthMode(now time.Time) { b.pacingGain = PacingGain[b.cycleCurrentOffset] } -func (b *bbrSender) MaybeEnterOrExitProbeRtt(now time.Time, isRoundStart, minRttExpired bool) { +func (b *bbrSender) MaybeEnterOrExitProbeRtt(now monotime.Time, isRoundStart, minRttExpired bool) { if minRttExpired && !b.exitingQuiescence && b.mode != PROBE_RTT { if b.InSlowStart() { b.OnExitStartup(now) @@ -734,7 +735,7 @@ func (b *bbrSender) MaybeEnterOrExitProbeRtt(now time.Time, isRoundStart, minRtt b.pacingGain = 1.0 // Do not decide on the time to exit PROBE_RTT until the |bytes_in_flight| // is at the target small value. - b.exitProbeRttAt = time.Time{} + b.exitProbeRttAt = monotime.Time(0) } if b.mode == PROBE_RTT { @@ -773,7 +774,7 @@ func (b *bbrSender) ProbeRttCongestionWindow() congestion.ByteCount { } } -func (b *bbrSender) EnterStartupMode(now time.Time) { +func (b *bbrSender) EnterStartupMode(now monotime.Time) { // if b.rttStats != nil { // TODO: slow start. // } @@ -782,7 +783,7 @@ func (b *bbrSender) EnterStartupMode(now time.Time) { b.congestionWindowGain = b.highCwndGain } -func (b *bbrSender) OnExitStartup(now time.Time) { +func (b *bbrSender) OnExitStartup(now monotime.Time) { if b.rttStats == nil { return } diff --git a/clash-meta/transport/tuic/congestion/cubic.go b/clash-meta/transport/tuic/congestion/cubic.go index a9bed43aa1..35c3f0cba1 100644 --- a/clash-meta/transport/tuic/congestion/cubic.go +++ b/clash-meta/transport/tuic/congestion/cubic.go @@ -5,6 +5,7 @@ import ( "time" "github.com/metacubex/quic-go/congestion" + "github.com/metacubex/quic-go/monotime" ) // This cubic implementation is based on the one found in Chromiums's QUIC @@ -42,7 +43,7 @@ type Cubic struct { numConnections int // Time when this cycle started, after last loss event. - epoch time.Time + epoch monotime.Time // Max congestion window used just before last loss event. // Note: to improve fairness to other streams an additional back off is @@ -77,7 +78,7 @@ func NewCubic(clock Clock) *Cubic { // Reset is called after a timeout to reset the cubic state func (c *Cubic) Reset() { - c.epoch = time.Time{} + c.epoch = monotime.Time(0) c.lastMaxCongestionWindow = 0 c.ackedBytesCount = 0 c.estimatedTCPcongestionWindow = 0 @@ -121,7 +122,7 @@ func (c *Cubic) OnApplicationLimited() { // in such a period. This reset effectively freezes congestion window growth // through application-limited periods and allows Cubic growth to continue // when the entire window is being used. - c.epoch = time.Time{} + c.epoch = monotime.Time(0) } // CongestionWindowAfterPacketLoss computes a new congestion window to use after @@ -135,7 +136,7 @@ func (c *Cubic) CongestionWindowAfterPacketLoss(currentCongestionWindow congesti } else { c.lastMaxCongestionWindow = currentCongestionWindow } - c.epoch = time.Time{} // Reset time. + c.epoch = monotime.Time(0) // Reset time. return congestion.ByteCount(float32(currentCongestionWindow) * c.beta()) } @@ -147,7 +148,7 @@ func (c *Cubic) CongestionWindowAfterAck( ackedBytes congestion.ByteCount, currentCongestionWindow congestion.ByteCount, delayMin time.Duration, - eventTime time.Time, + eventTime monotime.Time, ) congestion.ByteCount { c.ackedBytesCount += ackedBytes diff --git a/clash-meta/transport/tuic/congestion/cubic_sender.go b/clash-meta/transport/tuic/congestion/cubic_sender.go index f544cd749c..dcf63a0a8d 100644 --- a/clash-meta/transport/tuic/congestion/cubic_sender.go +++ b/clash-meta/transport/tuic/congestion/cubic_sender.go @@ -2,9 +2,9 @@ package congestion import ( "fmt" - "time" "github.com/metacubex/quic-go/congestion" + "github.com/metacubex/quic-go/monotime" ) const ( @@ -103,11 +103,11 @@ func (c *cubicSender) SetRTTStatsProvider(provider congestion.RTTStatsProvider) } // TimeUntilSend returns when the next packet should be sent. -func (c *cubicSender) TimeUntilSend(_ congestion.ByteCount) time.Time { +func (c *cubicSender) TimeUntilSend(_ congestion.ByteCount) monotime.Time { return c.pacer.TimeUntilSend() } -func (c *cubicSender) HasPacingBudget(now time.Time) bool { +func (c *cubicSender) HasPacingBudget(now monotime.Time) bool { return c.pacer.Budget(now) >= c.maxDatagramSize } @@ -120,7 +120,7 @@ func (c *cubicSender) minCongestionWindow() congestion.ByteCount { } func (c *cubicSender) OnPacketSent( - sentTime time.Time, + sentTime monotime.Time, _ congestion.ByteCount, packetNumber congestion.PacketNumber, bytes congestion.ByteCount, @@ -162,7 +162,7 @@ func (c *cubicSender) OnPacketAcked( ackedPacketNumber congestion.PacketNumber, ackedBytes congestion.ByteCount, priorInFlight congestion.ByteCount, - eventTime time.Time, + eventTime monotime.Time, ) { c.largestAckedPacketNumber = Max(ackedPacketNumber, c.largestAckedPacketNumber) if c.InRecovery() { @@ -197,7 +197,7 @@ func (c *cubicSender) OnCongestionEvent(packetNumber congestion.PacketNumber, lo c.numAckedPackets = 0 } -func (b *cubicSender) OnCongestionEventEx(priorInFlight congestion.ByteCount, eventTime time.Time, ackedPackets []congestion.AckedPacketInfo, lostPackets []congestion.LostPacketInfo) { +func (b *cubicSender) OnCongestionEventEx(priorInFlight congestion.ByteCount, eventTime monotime.Time, ackedPackets []congestion.AckedPacketInfo, lostPackets []congestion.LostPacketInfo) { // Stub } @@ -207,7 +207,7 @@ func (c *cubicSender) maybeIncreaseCwnd( _ congestion.PacketNumber, ackedBytes congestion.ByteCount, priorInFlight congestion.ByteCount, - eventTime time.Time, + eventTime monotime.Time, ) { // Do not increase the congestion window unless the sender is close to using // the current window. diff --git a/clash-meta/transport/tuic/congestion/pacer.go b/clash-meta/transport/tuic/congestion/pacer.go index f60ef5fe1b..598f9dac99 100644 --- a/clash-meta/transport/tuic/congestion/pacer.go +++ b/clash-meta/transport/tuic/congestion/pacer.go @@ -5,6 +5,7 @@ import ( "time" "github.com/metacubex/quic-go/congestion" + "github.com/metacubex/quic-go/monotime" ) const initialMaxDatagramSize = congestion.ByteCount(1252) @@ -16,7 +17,7 @@ const maxBurstSizePackets = 10 type pacer struct { budgetAtLastSent congestion.ByteCount maxDatagramSize congestion.ByteCount - lastSentTime time.Time + lastSentTime monotime.Time getAdjustedBandwidth func() uint64 // in bytes/s } @@ -37,7 +38,7 @@ func newPacer(getBandwidth func() Bandwidth) *pacer { return p } -func (p *pacer) SentPacket(sendTime time.Time, size congestion.ByteCount) { +func (p *pacer) SentPacket(sendTime monotime.Time, size congestion.ByteCount) { budget := p.Budget(sendTime) if size > budget { p.budgetAtLastSent = 0 @@ -47,7 +48,7 @@ func (p *pacer) SentPacket(sendTime time.Time, size congestion.ByteCount) { p.lastSentTime = sendTime } -func (p *pacer) Budget(now time.Time) congestion.ByteCount { +func (p *pacer) Budget(now monotime.Time) congestion.ByteCount { if p.lastSentTime.IsZero() { return p.maxBurstSize() } @@ -63,10 +64,10 @@ func (p *pacer) maxBurstSize() congestion.ByteCount { } // TimeUntilSend returns when the next packet should be sent. -// It returns the zero value of time.Time if a packet can be sent immediately. -func (p *pacer) TimeUntilSend() time.Time { +// It returns the zero value of monotime.Time if a packet can be sent immediately. +func (p *pacer) TimeUntilSend() monotime.Time { if p.budgetAtLastSent >= p.maxDatagramSize { - return time.Time{} + return monotime.Time(0) } return p.lastSentTime.Add(Max( MinPacingDelay, diff --git a/clash-meta/transport/tuic/congestion_v2/bandwidth_sampler.go b/clash-meta/transport/tuic/congestion_v2/bandwidth_sampler.go index 9028df64c3..d546aec5bf 100644 --- a/clash-meta/transport/tuic/congestion_v2/bandwidth_sampler.go +++ b/clash-meta/transport/tuic/congestion_v2/bandwidth_sampler.go @@ -5,6 +5,7 @@ import ( "time" "github.com/metacubex/quic-go/congestion" + "github.com/metacubex/quic-go/monotime" ) const ( @@ -103,7 +104,7 @@ type maxAckHeightTracker struct { // bandwidth. maxAckHeightFilter *WindowedFilter[extraAckedEvent, roundTripCount] // The time this aggregation started and the number of bytes acked during it. - aggregationEpochStartTime time.Time + aggregationEpochStartTime monotime.Time aggregationEpochBytes congestion.ByteCount // The last sent packet number before the current aggregation epoch started. lastSentPacketNumberBeforeEpoch congestion.PacketNumber @@ -133,7 +134,7 @@ func (m *maxAckHeightTracker) Update( roundTripCount roundTripCount, lastSentPacketNumber congestion.PacketNumber, lastAckedPacketNumber congestion.PacketNumber, - ackTime time.Time, + ackTime monotime.Time, bytesAcked congestion.ByteCount, ) congestion.ByteCount { forceNewEpoch := false @@ -241,7 +242,7 @@ func (m *maxAckHeightTracker) NumAckAggregationEpochs() uint64 { // AckPoint represents a point on the ack line. type ackPoint struct { - ackTime time.Time + ackTime monotime.Time totalBytesAcked congestion.ByteCount } @@ -250,7 +251,7 @@ type recentAckPoints struct { ackPoints [2]ackPoint } -func (r *recentAckPoints) Update(ackTime time.Time, totalBytesAcked congestion.ByteCount) { +func (r *recentAckPoints) Update(ackTime monotime.Time, totalBytesAcked congestion.ByteCount) { if ackTime.Before(r.ackPoints[1].ackTime) { r.ackPoints[1].ackTime = ackTime } else if ackTime.After(r.ackPoints[1].ackTime) { @@ -284,7 +285,7 @@ func (r *recentAckPoints) LessRecentPoint() *ackPoint { // that moment. type connectionStateOnSentPacket struct { // Time at which the packet is sent. - sentTime time.Time + sentTime monotime.Time // Size of the packet. size congestion.ByteCount // The value of |totalBytesSentAtLastAckedPacket| at the time the @@ -292,10 +293,10 @@ type connectionStateOnSentPacket struct { totalBytesSentAtLastAckedPacket congestion.ByteCount // The value of |lastAckedPacketSentTime| at the time the packet was // sent. - lastAckedPacketSentTime time.Time + lastAckedPacketSentTime monotime.Time // The value of |lastAckedPacketAckTime| at the time the packet was // sent. - lastAckedPacketAckTime time.Time + lastAckedPacketAckTime monotime.Time // Send time states that are returned to the congestion controller when the // packet is acked or lost. sendTimeState sendTimeState @@ -305,7 +306,7 @@ type connectionStateOnSentPacket struct { // sampler. // |bytes_in_flight| is the bytes in flight right after the packet is sent. func newConnectionStateOnSentPacket( - sentTime time.Time, + sentTime monotime.Time, size congestion.ByteCount, bytesInFlight congestion.ByteCount, sampler *bandwidthSampler, @@ -456,10 +457,10 @@ type bandwidthSampler struct { // The time at which the last acknowledged packet was sent. Set to // QuicTime::Zero() if no valid timestamp is available. - lastAckedPacketSentTime time.Time + lastAckedPacketSentTime monotime.Time // The time at which the most recent packet was acknowledged. - lastAckedPacketAckTime time.Time + lastAckedPacketAckTime monotime.Time // The most recently sent packet. lastSentPacket congestion.PacketNumber @@ -551,7 +552,7 @@ func (b *bandwidthSampler) IsOverestimateAvoidanceEnabled() bool { } func (b *bandwidthSampler) OnPacketSent( - sentTime time.Time, + sentTime monotime.Time, packetNumber congestion.PacketNumber, bytes congestion.ByteCount, bytesInFlight congestion.ByteCount, @@ -595,7 +596,7 @@ func (b *bandwidthSampler) OnPacketSent( } func (b *bandwidthSampler) OnCongestionEvent( - ackTime time.Time, + ackTime monotime.Time, ackedPackets []congestion.AckedPacketInfo, lostPackets []congestion.LostPacketInfo, maxBandwidth Bandwidth, @@ -758,7 +759,7 @@ func (b *bandwidthSampler) chooseA0Point(totalBytesAcked congestion.ByteCount, a return true } -func (b *bandwidthSampler) onPacketAcknowledged(ackTime time.Time, packetNumber congestion.PacketNumber) bandwidthSample { +func (b *bandwidthSampler) onPacketAcknowledged(ackTime monotime.Time, packetNumber congestion.PacketNumber) bandwidthSample { sample := newBandwidthSample() b.lastAckedPacket = packetNumber sentPacketPointer := b.connectionStateMap.GetEntry(packetNumber) diff --git a/clash-meta/transport/tuic/congestion_v2/bbr_sender.go b/clash-meta/transport/tuic/congestion_v2/bbr_sender.go index a515c2cc44..6020ab391f 100644 --- a/clash-meta/transport/tuic/congestion_v2/bbr_sender.go +++ b/clash-meta/transport/tuic/congestion_v2/bbr_sender.go @@ -8,6 +8,7 @@ import ( "github.com/metacubex/quic-go" "github.com/metacubex/quic-go/congestion" + "github.com/metacubex/quic-go/monotime" "github.com/metacubex/randv2" ) @@ -127,7 +128,7 @@ type bbrSender struct { // triggers PROBE_RTT mode) if no new value is sampled during that period. minRtt time.Duration // The time at which the current value of |min_rtt_| was assigned. - minRttTimestamp time.Time + minRttTimestamp monotime.Time // The maximum allowed number of bytes in flight. congestionWindow congestion.ByteCount @@ -168,7 +169,7 @@ type bbrSender struct { // pacing gain cycle. cycleCurrentOffset int // The time at which the last pacing gain cycle was started. - lastCycleStart time.Time + lastCycleStart monotime.Time // Indicates whether the connection has reached the full bandwidth mode. isAtFullBandwidth bool @@ -183,7 +184,7 @@ type bbrSender struct { // Time at which PROBE_RTT has to be exited. Setting it to zero indicates // that the time is yet unknown as the number of packets in flight has not // reached the required value. - exitProbeRttAt time.Time + exitProbeRttAt monotime.Time // Indicates whether a round-trip has passed since PROBE_RTT became active. probeRttRoundPassed bool @@ -307,18 +308,18 @@ func (b *bbrSender) SetRTTStatsProvider(provider congestion.RTTStatsProvider) { } // TimeUntilSend implements the SendAlgorithm interface. -func (b *bbrSender) TimeUntilSend(bytesInFlight congestion.ByteCount) time.Time { +func (b *bbrSender) TimeUntilSend(bytesInFlight congestion.ByteCount) monotime.Time { return b.pacer.TimeUntilSend() } // HasPacingBudget implements the SendAlgorithm interface. -func (b *bbrSender) HasPacingBudget(now time.Time) bool { +func (b *bbrSender) HasPacingBudget(now monotime.Time) bool { return b.pacer.Budget(now) >= b.maxDatagramSize } // OnPacketSent implements the SendAlgorithm interface. func (b *bbrSender) OnPacketSent( - sentTime time.Time, + sentTime monotime.Time, bytesInFlight congestion.ByteCount, packetNumber congestion.PacketNumber, bytes congestion.ByteCount, @@ -349,7 +350,7 @@ func (b *bbrSender) MaybeExitSlowStart() { } // OnPacketAcked implements the SendAlgorithm interface. -func (b *bbrSender) OnPacketAcked(number congestion.PacketNumber, ackedBytes, priorInFlight congestion.ByteCount, eventTime time.Time) { +func (b *bbrSender) OnPacketAcked(number congestion.PacketNumber, ackedBytes, priorInFlight congestion.ByteCount, eventTime monotime.Time) { // Do nothing. } @@ -403,7 +404,7 @@ func (b *bbrSender) OnCongestionEvent(number congestion.PacketNumber, lostBytes, // Do nothing. } -func (b *bbrSender) OnCongestionEventEx(priorInFlight congestion.ByteCount, eventTime time.Time, ackedPackets []congestion.AckedPacketInfo, lostPackets []congestion.LostPacketInfo) { +func (b *bbrSender) OnCongestionEventEx(priorInFlight congestion.ByteCount, eventTime monotime.Time, ackedPackets []congestion.AckedPacketInfo, lostPackets []congestion.LostPacketInfo) { totalBytesAckedBefore := b.sampler.TotalBytesAcked() totalBytesLostBefore := b.sampler.TotalBytesLost() @@ -592,7 +593,7 @@ func (b *bbrSender) probeRttCongestionWindow() congestion.ByteCount { return b.minCongestionWindow } -func (b *bbrSender) maybeUpdateMinRtt(now time.Time, sampleMinRtt time.Duration) bool { +func (b *bbrSender) maybeUpdateMinRtt(now monotime.Time, sampleMinRtt time.Duration) bool { // Do not expire min_rtt if none was ever available. minRttExpired := b.minRtt != 0 && now.After(b.minRttTimestamp.Add(minRttExpiry)) if minRttExpired || sampleMinRtt < b.minRtt || b.minRtt == 0 { @@ -604,7 +605,7 @@ func (b *bbrSender) maybeUpdateMinRtt(now time.Time, sampleMinRtt time.Duration) } // Enters the STARTUP mode. -func (b *bbrSender) enterStartupMode(now time.Time) { +func (b *bbrSender) enterStartupMode(now monotime.Time) { b.mode = bbrModeStartup // b.maybeTraceStateChange(logging.CongestionStateStartup) b.pacingGain = b.highGain @@ -612,7 +613,7 @@ func (b *bbrSender) enterStartupMode(now time.Time) { } // Enters the PROBE_BW mode. -func (b *bbrSender) enterProbeBandwidthMode(now time.Time) { +func (b *bbrSender) enterProbeBandwidthMode(now monotime.Time) { b.mode = bbrModeProbeBw // b.maybeTraceStateChange(logging.CongestionStateProbeBw) b.congestionWindowGain = b.congestionWindowGainConstant @@ -641,7 +642,7 @@ func (b *bbrSender) updateRoundTripCounter(lastAckedPacket congestion.PacketNumb } // Updates the current gain used in PROBE_BW mode. -func (b *bbrSender) updateGainCyclePhase(now time.Time, priorInFlight congestion.ByteCount, hasLosses bool) { +func (b *bbrSender) updateGainCyclePhase(now monotime.Time, priorInFlight congestion.ByteCount, hasLosses bool) { // In most cases, the cycle is advanced after an RTT passes. shouldAdvanceGainCycling := now.After(b.lastCycleStart.Add(b.getMinRtt())) // If the pacing gain is above 1.0, the connection is trying to probe the @@ -713,7 +714,7 @@ func (b *bbrSender) maybeAppLimited(bytesInFlight congestion.ByteCount) { // Transitions from STARTUP to DRAIN and from DRAIN to PROBE_BW if // appropriate. -func (b *bbrSender) maybeExitStartupOrDrain(now time.Time) { +func (b *bbrSender) maybeExitStartupOrDrain(now monotime.Time) { if b.mode == bbrModeStartup && b.isAtFullBandwidth { b.mode = bbrModeDrain // b.maybeTraceStateChange(logging.CongestionStateDrain) @@ -726,14 +727,14 @@ func (b *bbrSender) maybeExitStartupOrDrain(now time.Time) { } // Decides whether to enter or exit PROBE_RTT. -func (b *bbrSender) maybeEnterOrExitProbeRtt(now time.Time, isRoundStart, minRttExpired bool) { +func (b *bbrSender) maybeEnterOrExitProbeRtt(now monotime.Time, isRoundStart, minRttExpired bool) { if minRttExpired && !b.exitingQuiescence && b.mode != bbrModeProbeRtt { b.mode = bbrModeProbeRtt // b.maybeTraceStateChange(logging.CongestionStateProbRtt) b.pacingGain = 1.0 // Do not decide on the time to exit PROBE_RTT until the |bytes_in_flight| // is at the target small value. - b.exitProbeRttAt = time.Time{} + b.exitProbeRttAt = monotime.Time(0) } if b.mode == bbrModeProbeRtt { diff --git a/clash-meta/transport/tuic/congestion_v2/clock.go b/clash-meta/transport/tuic/congestion_v2/clock.go index 405fae70f9..e6ea35dbd4 100644 --- a/clash-meta/transport/tuic/congestion_v2/clock.go +++ b/clash-meta/transport/tuic/congestion_v2/clock.go @@ -1,10 +1,12 @@ package congestion -import "time" +import ( + "github.com/metacubex/quic-go/monotime" +) // A Clock returns the current time type Clock interface { - Now() time.Time + Now() monotime.Time } // DefaultClock implements the Clock interface using the Go stdlib clock. @@ -13,6 +15,6 @@ type DefaultClock struct{} var _ Clock = DefaultClock{} // Now gets the current time -func (DefaultClock) Now() time.Time { - return time.Now() +func (DefaultClock) Now() monotime.Time { + return monotime.Now() } diff --git a/clash-meta/transport/tuic/congestion_v2/pacer.go b/clash-meta/transport/tuic/congestion_v2/pacer.go index 174b3dbe3f..6158bf5891 100644 --- a/clash-meta/transport/tuic/congestion_v2/pacer.go +++ b/clash-meta/transport/tuic/congestion_v2/pacer.go @@ -5,6 +5,7 @@ import ( "time" "github.com/metacubex/quic-go/congestion" + "github.com/metacubex/quic-go/monotime" ) const ( @@ -15,7 +16,7 @@ const ( type Pacer struct { budgetAtLastSent congestion.ByteCount maxDatagramSize congestion.ByteCount - lastSentTime time.Time + lastSentTime monotime.Time getBandwidth func() congestion.ByteCount // in bytes/s } @@ -28,7 +29,7 @@ func NewPacer(getBandwidth func() congestion.ByteCount) *Pacer { return p } -func (p *Pacer) SentPacket(sendTime time.Time, size congestion.ByteCount) { +func (p *Pacer) SentPacket(sendTime monotime.Time, size congestion.ByteCount) { budget := p.Budget(sendTime) if size > budget { p.budgetAtLastSent = 0 @@ -38,7 +39,7 @@ func (p *Pacer) SentPacket(sendTime time.Time, size congestion.ByteCount) { p.lastSentTime = sendTime } -func (p *Pacer) Budget(now time.Time) congestion.ByteCount { +func (p *Pacer) Budget(now monotime.Time) congestion.ByteCount { if p.lastSentTime.IsZero() { return p.maxBurstSize() } @@ -57,10 +58,10 @@ func (p *Pacer) maxBurstSize() congestion.ByteCount { } // TimeUntilSend returns when the next packet should be sent. -// It returns the zero value of time.Time if a packet can be sent immediately. -func (p *Pacer) TimeUntilSend() time.Time { +// It returns the zero value of monotime.Time if a packet can be sent immediately. +func (p *Pacer) TimeUntilSend() monotime.Time { if p.budgetAtLastSent >= p.maxDatagramSize { - return time.Time{} + return monotime.Time(0) } return p.lastSentTime.Add(Max( congestion.MinPacingDelay, diff --git a/lede/package/kernel/linux/modules/netdevices.mk b/lede/package/kernel/linux/modules/netdevices.mk index c4851ce0ab..75d2caa5f9 100644 --- a/lede/package/kernel/linux/modules/netdevices.mk +++ b/lede/package/kernel/linux/modules/netdevices.mk @@ -469,10 +469,27 @@ endef $(eval $(call KernelPackage,phy-vitesse)) +define KernelPackage/phy-aeonsemi-as21xxx + SUBMENU:=$(NETWORK_DEVICES_MENU) + TITLE:=Aeonsemi AS21xxx 10G Ethernet PHY + DEPENDS:=+aeonsemi-as21xxx-firmware +kmod-libphy + KCONFIG:=CONFIG_AS21XXX_PHY + FILES:= \ + $(LINUX_DIR)/drivers/net/phy/as21xxx.ko + AUTOLOAD:=$(call AutoLoad,18,as21xxx) +endef + +define KernelPackage/phy-aeonsemi-as21xxx/description + Kernel modules for Aeonsemi AS21x1x 10G Ethernet PHY +endef + +$(eval $(call KernelPackage,phy-aeonsemi-as21xxx)) + + define KernelPackage/phy-airoha-en8811h SUBMENU:=$(NETWORK_DEVICES_MENU) TITLE:=Airoha EN8811H 2.5G Ethernet PHY - DEPENDS:=+en8811h-firmware +kmod-libphy + DEPENDS:=+airoha-en8811h-firmware +kmod-libphy KCONFIG:=CONFIG_AIR_EN8811H_PHY FILES:= \ $(LINUX_DIR)/drivers/net/phy/air_en8811h.ko diff --git a/lede/target/linux/generic/backport-6.12/782-01-v6.16-net-phy-pass-PHY-driver-to-.match_phy_device-OP.patch b/lede/target/linux/generic/backport-6.12/782-01-v6.16-net-phy-pass-PHY-driver-to-.match_phy_device-OP.patch new file mode 100644 index 0000000000..cda5226904 --- /dev/null +++ b/lede/target/linux/generic/backport-6.12/782-01-v6.16-net-phy-pass-PHY-driver-to-.match_phy_device-OP.patch @@ -0,0 +1,303 @@ +From 31afd6bc55cc0093c3e5b0a368319e423d4de8ea Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Sat, 17 May 2025 22:13:45 +0200 +Subject: [PATCH] net: phy: pass PHY driver to .match_phy_device OP + +Pass PHY driver pointer to .match_phy_device OP in addition to phydev. +Having access to the PHY driver struct might be useful to check the +PHY ID of the driver is being matched for in case the PHY ID scanned in +the phydev is not consistent. + +A scenario for this is a PHY that change PHY ID after a firmware is +loaded, in such case, the PHY ID stored in PHY device struct is not +valid anymore and PHY will manually scan the ID in the match_phy_device +function. + +Having the PHY driver info is also useful for those PHY driver that +implement multiple simple .match_phy_device OP to match specific MMD PHY +ID. With this extra info if the parsing logic is the same, the matching +function can be generalized by using the phy_id in the PHY driver +instead of hardcoding. + +Rust wrapper callback is updated to align to the new match_phy_device +arguments. + +Suggested-by: Russell King (Oracle) +Reviewed-by: Russell King (Oracle) +Signed-off-by: Christian Marangi +Reviewed-by: Benno Lossin # for Rust +Reviewed-by: FUJITA Tomonori +Link: https://patch.msgid.link/20250517201353.5137-2-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/bcm87xx.c | 6 ++++-- + drivers/net/phy/icplus.c | 6 ++++-- + drivers/net/phy/marvell10g.c | 12 ++++++++---- + drivers/net/phy/micrel.c | 6 ++++-- + drivers/net/phy/nxp-c45-tja11xx.c | 12 ++++++++---- + drivers/net/phy/nxp-tja11xx.c | 6 ++++-- + drivers/net/phy/phy_device.c | 2 +- + drivers/net/phy/realtek/realtek_main.c | 27 +++++++++++++++++--------- + drivers/net/phy/teranetics.c | 3 ++- + include/linux/phy.h | 3 ++- + rust/kernel/net/phy.rs | 1 + + 11 files changed, 56 insertions(+), 28 deletions(-) + +--- a/drivers/net/phy/bcm87xx.c ++++ b/drivers/net/phy/bcm87xx.c +@@ -185,12 +185,14 @@ static irqreturn_t bcm87xx_handle_interr + return IRQ_HANDLED; + } + +-static int bcm8706_match_phy_device(struct phy_device *phydev) ++static int bcm8706_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return phydev->c45_ids.device_ids[4] == PHY_ID_BCM8706; + } + +-static int bcm8727_match_phy_device(struct phy_device *phydev) ++static int bcm8727_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return phydev->c45_ids.device_ids[4] == PHY_ID_BCM8727; + } +--- a/drivers/net/phy/icplus.c ++++ b/drivers/net/phy/icplus.c +@@ -520,12 +520,14 @@ static int ip101a_g_match_phy_device(str + return ip101a == !ret; + } + +-static int ip101a_match_phy_device(struct phy_device *phydev) ++static int ip101a_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return ip101a_g_match_phy_device(phydev, true); + } + +-static int ip101g_match_phy_device(struct phy_device *phydev) ++static int ip101g_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return ip101a_g_match_phy_device(phydev, false); + } +--- a/drivers/net/phy/marvell10g.c ++++ b/drivers/net/phy/marvell10g.c +@@ -1284,7 +1284,8 @@ static int mv3310_get_number_of_ports(st + return ret + 1; + } + +-static int mv3310_match_phy_device(struct phy_device *phydev) ++static int mv3310_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + if ((phydev->c45_ids.device_ids[MDIO_MMD_PMAPMD] & + MARVELL_PHY_ID_MASK) != MARVELL_PHY_ID_88X3310) +@@ -1293,7 +1294,8 @@ static int mv3310_match_phy_device(struc + return mv3310_get_number_of_ports(phydev) == 1; + } + +-static int mv3340_match_phy_device(struct phy_device *phydev) ++static int mv3340_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + if ((phydev->c45_ids.device_ids[MDIO_MMD_PMAPMD] & + MARVELL_PHY_ID_MASK) != MARVELL_PHY_ID_88X3310) +@@ -1317,12 +1319,14 @@ static int mv211x_match_phy_device(struc + return !!(val & MDIO_PCS_SPEED_5G) == has_5g; + } + +-static int mv2110_match_phy_device(struct phy_device *phydev) ++static int mv2110_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return mv211x_match_phy_device(phydev, true); + } + +-static int mv2111_match_phy_device(struct phy_device *phydev) ++static int mv2111_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return mv211x_match_phy_device(phydev, false); + } +--- a/drivers/net/phy/micrel.c ++++ b/drivers/net/phy/micrel.c +@@ -768,7 +768,8 @@ static int ksz8051_ksz8795_match_phy_dev + return !ret; + } + +-static int ksz8051_match_phy_device(struct phy_device *phydev) ++static int ksz8051_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return ksz8051_ksz8795_match_phy_device(phydev, true); + } +@@ -888,7 +889,8 @@ static int ksz8061_config_init(struct ph + return kszphy_config_init(phydev); + } + +-static int ksz8795_match_phy_device(struct phy_device *phydev) ++static int ksz8795_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return ksz8051_ksz8795_match_phy_device(phydev, false); + } +--- a/drivers/net/phy/nxp-c45-tja11xx.c ++++ b/drivers/net/phy/nxp-c45-tja11xx.c +@@ -1944,13 +1944,15 @@ static int nxp_c45_macsec_ability(struct + return macsec_ability; + } + +-static int tja1103_match_phy_device(struct phy_device *phydev) ++static int tja1103_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return phy_id_compare(phydev->phy_id, PHY_ID_TJA_1103, PHY_ID_MASK) && + !nxp_c45_macsec_ability(phydev); + } + +-static int tja1104_match_phy_device(struct phy_device *phydev) ++static int tja1104_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return phy_id_compare(phydev->phy_id, PHY_ID_TJA_1103, PHY_ID_MASK) && + nxp_c45_macsec_ability(phydev); +--- a/drivers/net/phy/nxp-tja11xx.c ++++ b/drivers/net/phy/nxp-tja11xx.c +@@ -646,12 +646,14 @@ static int tja1102_match_phy_device(stru + return !ret; + } + +-static int tja1102_p0_match_phy_device(struct phy_device *phydev) ++static int tja1102_p0_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return tja1102_match_phy_device(phydev, true); + } + +-static int tja1102_p1_match_phy_device(struct phy_device *phydev) ++static int tja1102_p1_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return tja1102_match_phy_device(phydev, false); + } +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -600,7 +600,7 @@ static int phy_bus_match(struct device * + return 0; + + if (phydrv->match_phy_device) +- return phydrv->match_phy_device(phydev); ++ return phydrv->match_phy_device(phydev, phydrv); + + if (phydev->is_c45) { + for (i = 1; i < num_ids; i++) { +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -1343,13 +1343,15 @@ static bool rtlgen_supports_mmd(struct p + return val > 0; + } + +-static int rtlgen_match_phy_device(struct phy_device *phydev) ++static int rtlgen_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return phydev->phy_id == RTL_GENERIC_PHYID && + !rtlgen_supports_2_5gbps(phydev); + } + +-static int rtl8226_match_phy_device(struct phy_device *phydev) ++static int rtl8226_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return phydev->phy_id == RTL_GENERIC_PHYID && + rtlgen_supports_2_5gbps(phydev) && +@@ -1365,32 +1367,38 @@ static int rtlgen_is_c45_match(struct ph + return !is_c45 && (id == phydev->phy_id); + } + +-static int rtl8221b_match_phy_device(struct phy_device *phydev) ++static int rtl8221b_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return phydev->phy_id == RTL_8221B && rtlgen_supports_mmd(phydev); + } + +-static int rtl8221b_vb_cg_c22_match_phy_device(struct phy_device *phydev) ++static int rtl8221b_vb_cg_c22_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return rtlgen_is_c45_match(phydev, RTL_8221B_VB_CG, false); + } + +-static int rtl8221b_vb_cg_c45_match_phy_device(struct phy_device *phydev) ++static int rtl8221b_vb_cg_c45_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return rtlgen_is_c45_match(phydev, RTL_8221B_VB_CG, true); + } + +-static int rtl8221b_vn_cg_c22_match_phy_device(struct phy_device *phydev) ++static int rtl8221b_vn_cg_c22_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return rtlgen_is_c45_match(phydev, RTL_8221B_VN_CG, false); + } + +-static int rtl8221b_vn_cg_c45_match_phy_device(struct phy_device *phydev) ++static int rtl8221b_vn_cg_c45_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return rtlgen_is_c45_match(phydev, RTL_8221B_VN_CG, true); + } + +-static int rtl_internal_nbaset_match_phy_device(struct phy_device *phydev) ++static int rtl_internal_nbaset_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + if (phydev->is_c45) + return false; +@@ -1409,7 +1417,8 @@ static int rtl_internal_nbaset_match_phy + return rtlgen_supports_2_5gbps(phydev) && !rtlgen_supports_mmd(phydev); + } + +-static int rtl8251b_c45_match_phy_device(struct phy_device *phydev) ++static int rtl8251b_c45_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return rtlgen_is_c45_match(phydev, RTL_8251B, true); + } +--- a/drivers/net/phy/teranetics.c ++++ b/drivers/net/phy/teranetics.c +@@ -67,7 +67,8 @@ static int teranetics_read_status(struct + return 0; + } + +-static int teranetics_match_phy_device(struct phy_device *phydev) ++static int teranetics_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return phydev->c45_ids.device_ids[3] == PHY_ID_TN2020; + } +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -1004,7 +1004,8 @@ struct phy_driver { + * driver for the given phydev. If NULL, matching is based on + * phy_id and phy_id_mask. + */ +- int (*match_phy_device)(struct phy_device *phydev); ++ int (*match_phy_device)(struct phy_device *phydev, ++ const struct phy_driver *phydrv); + + /** + * @set_wol: Some devices (e.g. qnap TS-119P II) require PHY +--- a/rust/kernel/net/phy.rs ++++ b/rust/kernel/net/phy.rs +@@ -421,6 +421,7 @@ impl Adapter { + /// `phydev` must be passed by the corresponding callback in `phy_driver`. + unsafe extern "C" fn match_phy_device_callback( + phydev: *mut bindings::phy_device, ++ _phydrv: *const bindings::phy_driver, + ) -> crate::ffi::c_int { + // SAFETY: This callback is called only in contexts + // where we hold `phy_device->lock`, so the accessors on diff --git a/lede/target/linux/generic/backport-6.12/782-04-v6.16-net-phy-introduce-genphy_match_phy_device.patch b/lede/target/linux/generic/backport-6.12/782-04-v6.16-net-phy-introduce-genphy_match_phy_device.patch new file mode 100644 index 0000000000..9e28011b0c --- /dev/null +++ b/lede/target/linux/generic/backport-6.12/782-04-v6.16-net-phy-introduce-genphy_match_phy_device.patch @@ -0,0 +1,109 @@ +From d6c45707ac84c2d9f274ece1cea4dddb97996bde Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Sat, 17 May 2025 22:13:48 +0200 +Subject: [PATCH 4/5] net: phy: introduce genphy_match_phy_device() + +Introduce new API, genphy_match_phy_device(), to provide a way to check +to match a PHY driver for a PHY device based on the info stored in the +PHY device struct. + +The function generalize the logic used in phy_bus_match() to check the +PHY ID whether if C45 or C22 ID should be used for matching. + +This is useful for custom .match_phy_device function that wants to use +the generic logic under some condition. (example a PHY is already setup +and provide the correct PHY ID) + +Reviewed-by: Russell King (Oracle) +Signed-off-by: Christian Marangi +Link: https://patch.msgid.link/20250517201353.5137-5-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/phy_device.c | 52 +++++++++++++++++++++++++----------- + include/linux/phy.h | 3 +++ + 2 files changed, 40 insertions(+), 15 deletions(-) + +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -589,20 +589,26 @@ static int phy_scan_fixups(struct phy_de + return 0; + } + +-static int phy_bus_match(struct device *dev, const struct device_driver *drv) ++/** ++ * genphy_match_phy_device - match a PHY device with a PHY driver ++ * @phydev: target phy_device struct ++ * @phydrv: target phy_driver struct ++ * ++ * Description: Checks whether the given PHY device matches the specified ++ * PHY driver. For Clause 45 PHYs, iterates over the available device ++ * identifiers and compares them against the driver's expected PHY ID, ++ * applying the provided mask. For Clause 22 PHYs, a direct ID comparison ++ * is performed. ++ * ++ * Return: 1 if the PHY device matches the driver, 0 otherwise. ++ */ ++int genphy_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { +- struct phy_device *phydev = to_phy_device(dev); +- const struct phy_driver *phydrv = to_phy_driver(drv); +- const int num_ids = ARRAY_SIZE(phydev->c45_ids.device_ids); +- int i; +- +- if (!(phydrv->mdiodrv.flags & MDIO_DEVICE_IS_PHY)) +- return 0; +- +- if (phydrv->match_phy_device) +- return phydrv->match_phy_device(phydev, phydrv); +- + if (phydev->is_c45) { ++ const int num_ids = ARRAY_SIZE(phydev->c45_ids.device_ids); ++ int i; ++ + for (i = 1; i < num_ids; i++) { + if (phydev->c45_ids.device_ids[i] == 0xffffffff) + continue; +@@ -611,11 +617,27 @@ static int phy_bus_match(struct device * + phydrv->phy_id, phydrv->phy_id_mask)) + return 1; + } ++ + return 0; +- } else { +- return phy_id_compare(phydev->phy_id, phydrv->phy_id, +- phydrv->phy_id_mask); + } ++ ++ return phy_id_compare(phydev->phy_id, phydrv->phy_id, ++ phydrv->phy_id_mask); ++} ++EXPORT_SYMBOL_GPL(genphy_match_phy_device); ++ ++static int phy_bus_match(struct device *dev, const struct device_driver *drv) ++{ ++ struct phy_device *phydev = to_phy_device(dev); ++ const struct phy_driver *phydrv = to_phy_driver(drv); ++ ++ if (!(phydrv->mdiodrv.flags & MDIO_DEVICE_IS_PHY)) ++ return 0; ++ ++ if (phydrv->match_phy_device) ++ return phydrv->match_phy_device(phydev, phydrv); ++ ++ return genphy_match_phy_device(phydev, phydrv); + } + + static ssize_t +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -1906,6 +1906,9 @@ char *phy_attached_info_irq(struct phy_d + __malloc; + void phy_attached_info(struct phy_device *phydev); + ++int genphy_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv); ++ + /* Clause 22 PHY */ + int genphy_read_abilities(struct phy_device *phydev); + int genphy_setup_forced(struct phy_device *phydev); diff --git a/lede/target/linux/generic/backport-6.12/782-05-v6.16-net-phy-Add-support-for-Aeonsemi-AS21xxx-PHYs.patch b/lede/target/linux/generic/backport-6.12/782-05-v6.16-net-phy-Add-support-for-Aeonsemi-AS21xxx-PHYs.patch new file mode 100644 index 0000000000..bcf9b1a6e5 --- /dev/null +++ b/lede/target/linux/generic/backport-6.12/782-05-v6.16-net-phy-Add-support-for-Aeonsemi-AS21xxx-PHYs.patch @@ -0,0 +1,1165 @@ +From 830877d89edcd834e4b4d0fcc021ff619d89505e Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Sat, 17 May 2025 22:13:49 +0200 +Subject: [PATCH 5/5] net: phy: Add support for Aeonsemi AS21xxx PHYs + +Add support for Aeonsemi AS21xxx 10G C45 PHYs. These PHYs integrate +an IPC to setup some configuration and require special handling to +sync with the parity bit. The parity bit is a way the IPC use to +follow correct order of command sent. + +Supported PHYs AS21011JB1, AS21011PB1, AS21010JB1, AS21010PB1, +AS21511JB1, AS21511PB1, AS21510JB1, AS21510PB1, AS21210JB1, +AS21210PB1 that all register with the PHY ID 0x7500 0x7510 +before the firmware is loaded. + +They all support up to 5 LEDs with various HW mode supported. + +While implementing it was found some strange coincidence with using the +same logic for implementing C22 in MMD regs in Broadcom PHYs. + +For reference here the AS21xxx PHY name logic: + +AS21x1xxB1 + ^ ^^ + | |J: Supports SyncE/PTP + | |P: No SyncE/PTP support + | 1: Supports 2nd Serdes + | 2: Not 2nd Serdes support + 0: 10G, 5G, 2.5G + 5: 5G, 2.5G + 2: 2.5G + +Reviewed-by: Andrew Lunn +Signed-off-by: Christian Marangi +Link: https://patch.msgid.link/20250517201353.5137-6-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +--- + MAINTAINERS | 6 + + drivers/net/phy/Kconfig | 12 + + drivers/net/phy/Makefile | 1 + + drivers/net/phy/as21xxx.c | 1087 +++++++++++++++++++++++++++++++++++++ + 4 files changed, 1106 insertions(+) + create mode 100644 drivers/net/phy/as21xxx.c + +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -79,6 +79,18 @@ config SFP + + comment "MII PHY device drivers" + ++config AS21XXX_PHY ++ tristate "Aeonsemi AS21xxx PHYs" ++ help ++ Currently supports the Aeonsemi AS21xxx PHY. ++ ++ These are C45 PHYs 10G that require all a generic firmware. ++ ++ Supported PHYs AS21011JB1, AS21011PB1, AS21010JB1, AS21010PB1, ++ AS21511JB1, AS21511PB1, AS21510JB1, AS21510PB1, AS21210JB1, ++ AS21210PB1 that all register with the PHY ID 0x7500 0x7500 ++ before the firmware is loaded. ++ + config AIR_EN8811H_PHY + tristate "Airoha EN8811H 2.5 Gigabit PHY" + help +--- a/drivers/net/phy/Makefile ++++ b/drivers/net/phy/Makefile +@@ -39,6 +39,7 @@ obj-$(CONFIG_AIR_EN8811H_PHY) += air_e + obj-$(CONFIG_AMD_PHY) += amd.o + obj-$(CONFIG_AMCC_QT2025_PHY) += qt2025.o + obj-$(CONFIG_AQUANTIA_PHY) += aquantia/ ++obj-$(CONFIG_AS21XXX_PHY) += as21xxx.o + ifdef CONFIG_AX88796B_RUST_PHY + obj-$(CONFIG_AX88796B_PHY) += ax88796b_rust.o + else +--- /dev/null ++++ b/drivers/net/phy/as21xxx.c +@@ -0,0 +1,1087 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Aeonsemi AS21XXxX PHY Driver ++ * ++ * Author: Christian Marangi ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#define VEND1_GLB_REG_CPU_RESET_ADDR_LO_BASEADDR 0x3 ++#define VEND1_GLB_REG_CPU_RESET_ADDR_HI_BASEADDR 0x4 ++ ++#define VEND1_GLB_REG_CPU_CTRL 0xe ++#define VEND1_GLB_CPU_CTRL_MASK GENMASK(4, 0) ++#define VEND1_GLB_CPU_CTRL_LED_POLARITY_MASK GENMASK(12, 8) ++#define VEND1_GLB_CPU_CTRL_LED_POLARITY(_n) FIELD_PREP(VEND1_GLB_CPU_CTRL_LED_POLARITY_MASK, \ ++ BIT(_n)) ++ ++#define VEND1_FW_START_ADDR 0x100 ++ ++#define VEND1_GLB_REG_MDIO_INDIRECT_ADDRCMD 0x101 ++#define VEND1_GLB_REG_MDIO_INDIRECT_LOAD 0x102 ++ ++#define VEND1_GLB_REG_MDIO_INDIRECT_STATUS 0x103 ++ ++#define VEND1_PTP_CLK 0x142 ++#define VEND1_PTP_CLK_EN BIT(6) ++ ++/* 5 LED at step of 0x20 ++ * FE: Fast-Ethernet (10/100) ++ * GE: Gigabit-Ethernet (1000) ++ * NG: New-Generation (2500/5000/10000) ++ */ ++#define VEND1_LED_REG(_n) (0x1800 + ((_n) * 0x10)) ++#define VEND1_LED_REG_A_EVENT GENMASK(15, 11) ++#define VEND1_LED_CONF 0x1881 ++#define VEND1_LED_CONFG_BLINK GENMASK(7, 0) ++ ++#define VEND1_SPEED_STATUS 0x4002 ++#define VEND1_SPEED_MASK GENMASK(7, 0) ++#define VEND1_SPEED_10000 FIELD_PREP_CONST(VEND1_SPEED_MASK, 0x3) ++#define VEND1_SPEED_5000 FIELD_PREP_CONST(VEND1_SPEED_MASK, 0x5) ++#define VEND1_SPEED_2500 FIELD_PREP_CONST(VEND1_SPEED_MASK, 0x9) ++#define VEND1_SPEED_1000 FIELD_PREP_CONST(VEND1_SPEED_MASK, 0x10) ++#define VEND1_SPEED_100 FIELD_PREP_CONST(VEND1_SPEED_MASK, 0x20) ++#define VEND1_SPEED_10 FIELD_PREP_CONST(VEND1_SPEED_MASK, 0x0) ++ ++#define VEND1_IPC_CMD 0x5801 ++#define AEON_IPC_CMD_PARITY BIT(15) ++#define AEON_IPC_CMD_SIZE GENMASK(10, 6) ++#define AEON_IPC_CMD_OPCODE GENMASK(5, 0) ++ ++#define IPC_CMD_NOOP 0x0 /* Do nothing */ ++#define IPC_CMD_INFO 0x1 /* Get Firmware Version */ ++#define IPC_CMD_SYS_CPU 0x2 /* SYS_CPU */ ++#define IPC_CMD_BULK_DATA 0xa /* Pass bulk data in ipc registers. */ ++#define IPC_CMD_BULK_WRITE 0xc /* Write bulk data to memory */ ++#define IPC_CMD_CFG_PARAM 0x1a /* Write config parameters to memory */ ++#define IPC_CMD_NG_TESTMODE 0x1b /* Set NG test mode and tone */ ++#define IPC_CMD_TEMP_MON 0x15 /* Temperature monitoring function */ ++#define IPC_CMD_SET_LED 0x23 /* Set led */ ++ ++#define VEND1_IPC_STS 0x5802 ++#define AEON_IPC_STS_PARITY BIT(15) ++#define AEON_IPC_STS_SIZE GENMASK(14, 10) ++#define AEON_IPC_STS_OPCODE GENMASK(9, 4) ++#define AEON_IPC_STS_STATUS GENMASK(3, 0) ++#define AEON_IPC_STS_STATUS_RCVD FIELD_PREP_CONST(AEON_IPC_STS_STATUS, 0x1) ++#define AEON_IPC_STS_STATUS_PROCESS FIELD_PREP_CONST(AEON_IPC_STS_STATUS, 0x2) ++#define AEON_IPC_STS_STATUS_SUCCESS FIELD_PREP_CONST(AEON_IPC_STS_STATUS, 0x4) ++#define AEON_IPC_STS_STATUS_ERROR FIELD_PREP_CONST(AEON_IPC_STS_STATUS, 0x8) ++#define AEON_IPC_STS_STATUS_BUSY FIELD_PREP_CONST(AEON_IPC_STS_STATUS, 0xe) ++#define AEON_IPC_STS_STATUS_READY FIELD_PREP_CONST(AEON_IPC_STS_STATUS, 0xf) ++ ++#define VEND1_IPC_DATA0 0x5808 ++#define VEND1_IPC_DATA1 0x5809 ++#define VEND1_IPC_DATA2 0x580a ++#define VEND1_IPC_DATA3 0x580b ++#define VEND1_IPC_DATA4 0x580c ++#define VEND1_IPC_DATA5 0x580d ++#define VEND1_IPC_DATA6 0x580e ++#define VEND1_IPC_DATA7 0x580f ++#define VEND1_IPC_DATA(_n) (VEND1_IPC_DATA0 + (_n)) ++ ++/* Sub command of CMD_INFO */ ++#define IPC_INFO_VERSION 0x1 ++ ++/* Sub command of CMD_SYS_CPU */ ++#define IPC_SYS_CPU_REBOOT 0x3 ++#define IPC_SYS_CPU_IMAGE_OFST 0x4 ++#define IPC_SYS_CPU_IMAGE_CHECK 0x5 ++#define IPC_SYS_CPU_PHY_ENABLE 0x6 ++ ++/* Sub command of CMD_CFG_PARAM */ ++#define IPC_CFG_PARAM_DIRECT 0x4 ++ ++/* CFG DIRECT sub command */ ++#define IPC_CFG_PARAM_DIRECT_NG_PHYCTRL 0x1 ++#define IPC_CFG_PARAM_DIRECT_CU_AN 0x2 ++#define IPC_CFG_PARAM_DIRECT_SDS_PCS 0x3 ++#define IPC_CFG_PARAM_DIRECT_AUTO_EEE 0x4 ++#define IPC_CFG_PARAM_DIRECT_SDS_PMA 0x5 ++#define IPC_CFG_PARAM_DIRECT_DPC_RA 0x6 ++#define IPC_CFG_PARAM_DIRECT_DPC_PKT_CHK 0x7 ++#define IPC_CFG_PARAM_DIRECT_DPC_SDS_WAIT_ETH 0x8 ++#define IPC_CFG_PARAM_DIRECT_WDT 0x9 ++#define IPC_CFG_PARAM_DIRECT_SDS_RESTART_AN 0x10 ++#define IPC_CFG_PARAM_DIRECT_TEMP_MON 0x11 ++#define IPC_CFG_PARAM_DIRECT_WOL 0x12 ++ ++/* Sub command of CMD_TEMP_MON */ ++#define IPC_CMD_TEMP_MON_GET 0x4 ++ ++#define AS21XXX_MDIO_AN_C22 0xffe0 ++ ++#define PHY_ID_AS21XXX 0x75009410 ++/* AS21xxx ID Legend ++ * AS21x1xxB1 ++ * ^ ^^ ++ * | |J: Supports SyncE/PTP ++ * | |P: No SyncE/PTP support ++ * | 1: Supports 2nd Serdes ++ * | 2: Not 2nd Serdes support ++ * 0: 10G, 5G, 2.5G ++ * 5: 5G, 2.5G ++ * 2: 2.5G ++ */ ++#define PHY_ID_AS21011JB1 0x75009402 ++#define PHY_ID_AS21011PB1 0x75009412 ++#define PHY_ID_AS21010JB1 0x75009422 ++#define PHY_ID_AS21010PB1 0x75009432 ++#define PHY_ID_AS21511JB1 0x75009442 ++#define PHY_ID_AS21511PB1 0x75009452 ++#define PHY_ID_AS21510JB1 0x75009462 ++#define PHY_ID_AS21510PB1 0x75009472 ++#define PHY_ID_AS21210JB1 0x75009482 ++#define PHY_ID_AS21210PB1 0x75009492 ++#define PHY_VENDOR_AEONSEMI 0x75009400 ++ ++#define AEON_MAX_LEDS 5 ++#define AEON_IPC_DELAY 10000 ++#define AEON_IPC_TIMEOUT (AEON_IPC_DELAY * 100) ++#define AEON_IPC_DATA_NUM_REGISTERS 8 ++#define AEON_IPC_DATA_MAX (AEON_IPC_DATA_NUM_REGISTERS * sizeof(u16)) ++ ++#define AEON_BOOT_ADDR 0x1000 ++#define AEON_CPU_BOOT_ADDR 0x2000 ++#define AEON_CPU_CTRL_FW_LOAD (BIT(4) | BIT(2) | BIT(1) | BIT(0)) ++#define AEON_CPU_CTRL_FW_START BIT(0) ++ ++enum as21xxx_led_event { ++ VEND1_LED_REG_A_EVENT_ON_10 = 0x0, ++ VEND1_LED_REG_A_EVENT_ON_100, ++ VEND1_LED_REG_A_EVENT_ON_1000, ++ VEND1_LED_REG_A_EVENT_ON_2500, ++ VEND1_LED_REG_A_EVENT_ON_5000, ++ VEND1_LED_REG_A_EVENT_ON_10000, ++ VEND1_LED_REG_A_EVENT_ON_FE_GE, ++ VEND1_LED_REG_A_EVENT_ON_NG, ++ VEND1_LED_REG_A_EVENT_ON_FULL_DUPLEX, ++ VEND1_LED_REG_A_EVENT_ON_COLLISION, ++ VEND1_LED_REG_A_EVENT_BLINK_TX, ++ VEND1_LED_REG_A_EVENT_BLINK_RX, ++ VEND1_LED_REG_A_EVENT_BLINK_ACT, ++ VEND1_LED_REG_A_EVENT_ON_LINK, ++ VEND1_LED_REG_A_EVENT_ON_LINK_BLINK_ACT, ++ VEND1_LED_REG_A_EVENT_ON_LINK_BLINK_RX, ++ VEND1_LED_REG_A_EVENT_ON_FE_GE_BLINK_ACT, ++ VEND1_LED_REG_A_EVENT_ON_NG_BLINK_ACT, ++ VEND1_LED_REG_A_EVENT_ON_NG_BLINK_FE_GE, ++ VEND1_LED_REG_A_EVENT_ON_FD_BLINK_COLLISION, ++ VEND1_LED_REG_A_EVENT_ON, ++ VEND1_LED_REG_A_EVENT_OFF, ++}; ++ ++struct as21xxx_led_pattern_info { ++ unsigned int pattern; ++ u16 val; ++}; ++ ++struct as21xxx_priv { ++ bool parity_status; ++ /* Protect concurrent IPC access */ ++ struct mutex ipc_lock; ++}; ++ ++static struct as21xxx_led_pattern_info as21xxx_led_supported_pattern[] = { ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_10), ++ .val = VEND1_LED_REG_A_EVENT_ON_10 ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_100), ++ .val = VEND1_LED_REG_A_EVENT_ON_100 ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_1000), ++ .val = VEND1_LED_REG_A_EVENT_ON_1000 ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_2500), ++ .val = VEND1_LED_REG_A_EVENT_ON_2500 ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_5000), ++ .val = VEND1_LED_REG_A_EVENT_ON_5000 ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_10000), ++ .val = VEND1_LED_REG_A_EVENT_ON_10000 ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK), ++ .val = VEND1_LED_REG_A_EVENT_ON_LINK ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_10) | ++ BIT(TRIGGER_NETDEV_LINK_100) | ++ BIT(TRIGGER_NETDEV_LINK_1000), ++ .val = VEND1_LED_REG_A_EVENT_ON_FE_GE ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_2500) | ++ BIT(TRIGGER_NETDEV_LINK_5000) | ++ BIT(TRIGGER_NETDEV_LINK_10000), ++ .val = VEND1_LED_REG_A_EVENT_ON_NG ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_FULL_DUPLEX), ++ .val = VEND1_LED_REG_A_EVENT_ON_FULL_DUPLEX ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_TX), ++ .val = VEND1_LED_REG_A_EVENT_BLINK_TX ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_RX), ++ .val = VEND1_LED_REG_A_EVENT_BLINK_RX ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_TX) | ++ BIT(TRIGGER_NETDEV_RX), ++ .val = VEND1_LED_REG_A_EVENT_BLINK_ACT ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_10) | ++ BIT(TRIGGER_NETDEV_LINK_100) | ++ BIT(TRIGGER_NETDEV_LINK_1000) | ++ BIT(TRIGGER_NETDEV_LINK_2500) | ++ BIT(TRIGGER_NETDEV_LINK_5000) | ++ BIT(TRIGGER_NETDEV_LINK_10000), ++ .val = VEND1_LED_REG_A_EVENT_ON_LINK ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_10) | ++ BIT(TRIGGER_NETDEV_LINK_100) | ++ BIT(TRIGGER_NETDEV_LINK_1000) | ++ BIT(TRIGGER_NETDEV_LINK_2500) | ++ BIT(TRIGGER_NETDEV_LINK_5000) | ++ BIT(TRIGGER_NETDEV_LINK_10000) | ++ BIT(TRIGGER_NETDEV_TX) | ++ BIT(TRIGGER_NETDEV_RX), ++ .val = VEND1_LED_REG_A_EVENT_ON_LINK_BLINK_ACT ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_10) | ++ BIT(TRIGGER_NETDEV_LINK_100) | ++ BIT(TRIGGER_NETDEV_LINK_1000) | ++ BIT(TRIGGER_NETDEV_LINK_2500) | ++ BIT(TRIGGER_NETDEV_LINK_5000) | ++ BIT(TRIGGER_NETDEV_LINK_10000) | ++ BIT(TRIGGER_NETDEV_RX), ++ .val = VEND1_LED_REG_A_EVENT_ON_LINK_BLINK_RX ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_10) | ++ BIT(TRIGGER_NETDEV_LINK_100) | ++ BIT(TRIGGER_NETDEV_LINK_1000) | ++ BIT(TRIGGER_NETDEV_TX) | ++ BIT(TRIGGER_NETDEV_RX), ++ .val = VEND1_LED_REG_A_EVENT_ON_FE_GE_BLINK_ACT ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_2500) | ++ BIT(TRIGGER_NETDEV_LINK_5000) | ++ BIT(TRIGGER_NETDEV_LINK_10000) | ++ BIT(TRIGGER_NETDEV_TX) | ++ BIT(TRIGGER_NETDEV_RX), ++ .val = VEND1_LED_REG_A_EVENT_ON_NG_BLINK_ACT ++ } ++}; ++ ++static int aeon_firmware_boot(struct phy_device *phydev, const u8 *data, ++ size_t size) ++{ ++ int i, ret; ++ u16 val; ++ ++ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLB_REG_CPU_CTRL, ++ VEND1_GLB_CPU_CTRL_MASK, AEON_CPU_CTRL_FW_LOAD); ++ if (ret) ++ return ret; ++ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_FW_START_ADDR, ++ AEON_BOOT_ADDR); ++ if (ret) ++ return ret; ++ ++ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND1, ++ VEND1_GLB_REG_MDIO_INDIRECT_ADDRCMD, ++ 0x3ffc, 0xc000); ++ if (ret) ++ return ret; ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_VEND1, ++ VEND1_GLB_REG_MDIO_INDIRECT_STATUS); ++ if (val > 1) { ++ phydev_err(phydev, "wrong origin mdio_indirect_status: %x\n", val); ++ return -EINVAL; ++ } ++ ++ /* Firmware is always aligned to u16 */ ++ for (i = 0; i < size; i += 2) { ++ val = data[i + 1] << 8 | data[i]; ++ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, ++ VEND1_GLB_REG_MDIO_INDIRECT_LOAD, val); ++ if (ret) ++ return ret; ++ } ++ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, ++ VEND1_GLB_REG_CPU_RESET_ADDR_LO_BASEADDR, ++ lower_16_bits(AEON_CPU_BOOT_ADDR)); ++ if (ret) ++ return ret; ++ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, ++ VEND1_GLB_REG_CPU_RESET_ADDR_HI_BASEADDR, ++ upper_16_bits(AEON_CPU_BOOT_ADDR)); ++ if (ret) ++ return ret; ++ ++ return phy_modify_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLB_REG_CPU_CTRL, ++ VEND1_GLB_CPU_CTRL_MASK, AEON_CPU_CTRL_FW_START); ++} ++ ++static int aeon_firmware_load(struct phy_device *phydev) ++{ ++ struct device *dev = &phydev->mdio.dev; ++ const struct firmware *fw; ++ const char *fw_name; ++ int ret; ++ ++ ret = of_property_read_string(dev->of_node, "firmware-name", ++ &fw_name); ++ if (ret) ++ return ret; ++ ++ ret = request_firmware(&fw, fw_name, dev); ++ if (ret) { ++ phydev_err(phydev, "failed to find FW file %s (%d)\n", ++ fw_name, ret); ++ return ret; ++ } ++ ++ ret = aeon_firmware_boot(phydev, fw->data, fw->size); ++ ++ release_firmware(fw); ++ ++ return ret; ++} ++ ++static bool aeon_ipc_ready(u16 val, bool parity_status) ++{ ++ u16 status; ++ ++ if (FIELD_GET(AEON_IPC_STS_PARITY, val) != parity_status) ++ return false; ++ ++ status = val & AEON_IPC_STS_STATUS; ++ ++ return status != AEON_IPC_STS_STATUS_RCVD && ++ status != AEON_IPC_STS_STATUS_PROCESS && ++ status != AEON_IPC_STS_STATUS_BUSY; ++} ++ ++static int aeon_ipc_wait_cmd(struct phy_device *phydev, bool parity_status) ++{ ++ u16 val; ++ ++ /* Exit condition logic: ++ * - Wait for parity bit equal ++ * - Wait for status success, error OR ready ++ */ ++ return phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, VEND1_IPC_STS, val, ++ aeon_ipc_ready(val, parity_status), ++ AEON_IPC_DELAY, AEON_IPC_TIMEOUT, false); ++} ++ ++static int aeon_ipc_send_cmd(struct phy_device *phydev, ++ struct as21xxx_priv *priv, ++ u16 cmd, u16 *ret_sts) ++{ ++ bool curr_parity; ++ int ret; ++ ++ /* The IPC sync by using a single parity bit. ++ * Each CMD have alternately this bit set or clear ++ * to understand correct flow and packet order. ++ */ ++ curr_parity = priv->parity_status; ++ if (priv->parity_status) ++ cmd |= AEON_IPC_CMD_PARITY; ++ ++ /* Always update parity for next packet */ ++ priv->parity_status = !priv->parity_status; ++ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_IPC_CMD, cmd); ++ if (ret) ++ return ret; ++ ++ /* Wait for packet to be processed */ ++ usleep_range(AEON_IPC_DELAY, AEON_IPC_DELAY + 5000); ++ ++ /* With no ret_sts, ignore waiting for packet completion ++ * (ipc parity bit sync) ++ */ ++ if (!ret_sts) ++ return 0; ++ ++ ret = aeon_ipc_wait_cmd(phydev, curr_parity); ++ if (ret) ++ return ret; ++ ++ ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_IPC_STS); ++ if (ret < 0) ++ return ret; ++ ++ *ret_sts = ret; ++ if ((*ret_sts & AEON_IPC_STS_STATUS) != AEON_IPC_STS_STATUS_SUCCESS) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++/* If data is NULL, return 0 or negative error. ++ * If data not NULL, return number of Bytes received from IPC or ++ * a negative error. ++ */ ++static int aeon_ipc_send_msg(struct phy_device *phydev, ++ u16 opcode, u16 *data, unsigned int data_len, ++ u16 *ret_data) ++{ ++ struct as21xxx_priv *priv = phydev->priv; ++ unsigned int ret_size; ++ u16 cmd, ret_sts; ++ int ret; ++ int i; ++ ++ /* IPC have a max of 8 register to transfer data, ++ * make sure we never exceed this. ++ */ ++ if (data_len > AEON_IPC_DATA_MAX) ++ return -EINVAL; ++ ++ for (i = 0; i < data_len / sizeof(u16); i++) ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_IPC_DATA(i), ++ data[i]); ++ ++ cmd = FIELD_PREP(AEON_IPC_CMD_SIZE, data_len) | ++ FIELD_PREP(AEON_IPC_CMD_OPCODE, opcode); ++ ++ mutex_lock(&priv->ipc_lock); ++ ++ ret = aeon_ipc_send_cmd(phydev, priv, cmd, &ret_sts); ++ if (ret) { ++ phydev_err(phydev, "failed to send ipc msg for %x: %d\n", ++ opcode, ret); ++ goto out; ++ } ++ ++ if (!data) ++ goto out; ++ ++ if ((ret_sts & AEON_IPC_STS_STATUS) == AEON_IPC_STS_STATUS_ERROR) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ /* Prevent IPC from stack smashing the kernel. ++ * We can't trust IPC to return a good value and we always ++ * preallocate space for 16 Bytes. ++ */ ++ ret_size = FIELD_GET(AEON_IPC_STS_SIZE, ret_sts); ++ if (ret_size > AEON_IPC_DATA_MAX) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ /* Read data from IPC data register for ret_size value from IPC */ ++ for (i = 0; i < DIV_ROUND_UP(ret_size, sizeof(u16)); i++) { ++ ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_IPC_DATA(i)); ++ if (ret < 0) ++ goto out; ++ ++ ret_data[i] = ret; ++ } ++ ++ ret = ret_size; ++ ++out: ++ mutex_unlock(&priv->ipc_lock); ++ ++ return ret; ++} ++ ++static int aeon_ipc_noop(struct phy_device *phydev, ++ struct as21xxx_priv *priv, u16 *ret_sts) ++{ ++ u16 cmd; ++ ++ cmd = FIELD_PREP(AEON_IPC_CMD_SIZE, 0) | ++ FIELD_PREP(AEON_IPC_CMD_OPCODE, IPC_CMD_NOOP); ++ ++ return aeon_ipc_send_cmd(phydev, priv, cmd, ret_sts); ++} ++ ++/* Logic to sync parity bit with IPC. ++ * We send 2 NOP cmd with same partity and we wait for IPC ++ * to handle the packet only for the second one. This way ++ * we make sure we are sync for every next cmd. ++ */ ++static int aeon_ipc_sync_parity(struct phy_device *phydev, ++ struct as21xxx_priv *priv) ++{ ++ u16 ret_sts; ++ int ret; ++ ++ mutex_lock(&priv->ipc_lock); ++ ++ /* Send NOP with no parity */ ++ aeon_ipc_noop(phydev, priv, NULL); ++ ++ /* Reset packet parity */ ++ priv->parity_status = false; ++ ++ /* Send second NOP with no parity */ ++ ret = aeon_ipc_noop(phydev, priv, &ret_sts); ++ ++ mutex_unlock(&priv->ipc_lock); ++ ++ /* We expect to return -EINVAL */ ++ if (ret != -EINVAL) ++ return ret; ++ ++ if ((ret_sts & AEON_IPC_STS_STATUS) != AEON_IPC_STS_STATUS_READY) { ++ phydev_err(phydev, "Invalid IPC status on sync parity: %x\n", ++ ret_sts); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int aeon_ipc_get_fw_version(struct phy_device *phydev) ++{ ++ u16 ret_data[AEON_IPC_DATA_NUM_REGISTERS], data[1]; ++ char fw_version[AEON_IPC_DATA_MAX + 1]; ++ int ret; ++ ++ data[0] = IPC_INFO_VERSION; ++ ++ ret = aeon_ipc_send_msg(phydev, IPC_CMD_INFO, data, ++ sizeof(data), ret_data); ++ if (ret < 0) ++ return ret; ++ ++ /* Make sure FW version is NULL terminated */ ++ memcpy(fw_version, ret_data, ret); ++ fw_version[ret] = '\0'; ++ ++ phydev_info(phydev, "Firmware Version: %s\n", fw_version); ++ ++ return 0; ++} ++ ++static int aeon_dpc_ra_enable(struct phy_device *phydev) ++{ ++ u16 data[2]; ++ ++ data[0] = IPC_CFG_PARAM_DIRECT; ++ data[1] = IPC_CFG_PARAM_DIRECT_DPC_RA; ++ ++ return aeon_ipc_send_msg(phydev, IPC_CMD_CFG_PARAM, data, ++ sizeof(data), NULL); ++} ++ ++static int as21xxx_probe(struct phy_device *phydev) ++{ ++ struct as21xxx_priv *priv; ++ int ret; ++ ++ priv = devm_kzalloc(&phydev->mdio.dev, ++ sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ phydev->priv = priv; ++ ++ ret = devm_mutex_init(&phydev->mdio.dev, ++ &priv->ipc_lock); ++ if (ret) ++ return ret; ++ ++ ret = aeon_ipc_sync_parity(phydev, priv); ++ if (ret) ++ return ret; ++ ++ ret = aeon_ipc_get_fw_version(phydev); ++ if (ret) ++ return ret; ++ ++ /* Enable PTP clk if not already Enabled */ ++ ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_PTP_CLK, ++ VEND1_PTP_CLK_EN); ++ if (ret) ++ return ret; ++ ++ return aeon_dpc_ra_enable(phydev); ++} ++ ++static int as21xxx_read_link(struct phy_device *phydev, int *bmcr) ++{ ++ int status; ++ ++ /* Normal C22 BMCR report inconsistent data, use ++ * the mapped C22 in C45 to have more consistent link info. ++ */ ++ *bmcr = phy_read_mmd(phydev, MDIO_MMD_AN, ++ AS21XXX_MDIO_AN_C22 + MII_BMCR); ++ if (*bmcr < 0) ++ return *bmcr; ++ ++ /* Autoneg is being started, therefore disregard current ++ * link status and report link as down. ++ */ ++ if (*bmcr & BMCR_ANRESTART) { ++ phydev->link = 0; ++ return 0; ++ } ++ ++ status = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1); ++ if (status < 0) ++ return status; ++ ++ phydev->link = !!(status & MDIO_STAT1_LSTATUS); ++ ++ return 0; ++} ++ ++static int as21xxx_read_c22_lpa(struct phy_device *phydev) ++{ ++ int lpagb; ++ ++ /* MII_STAT1000 are only filled in the mapped C22 ++ * in C45, use that to fill lpagb values and check. ++ */ ++ lpagb = phy_read_mmd(phydev, MDIO_MMD_AN, ++ AS21XXX_MDIO_AN_C22 + MII_STAT1000); ++ if (lpagb < 0) ++ return lpagb; ++ ++ if (lpagb & LPA_1000MSFAIL) { ++ int adv = phy_read_mmd(phydev, MDIO_MMD_AN, ++ AS21XXX_MDIO_AN_C22 + MII_CTRL1000); ++ ++ if (adv < 0) ++ return adv; ++ ++ if (adv & CTL1000_ENABLE_MASTER) ++ phydev_err(phydev, "Master/Slave resolution failed, maybe conflicting manual settings?\n"); ++ else ++ phydev_err(phydev, "Master/Slave resolution failed\n"); ++ return -ENOLINK; ++ } ++ ++ mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, ++ lpagb); ++ ++ return 0; ++} ++ ++static int as21xxx_read_status(struct phy_device *phydev) ++{ ++ int bmcr, old_link = phydev->link; ++ int ret; ++ ++ ret = as21xxx_read_link(phydev, &bmcr); ++ if (ret) ++ return ret; ++ ++ /* why bother the PHY if nothing can have changed */ ++ if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link) ++ return 0; ++ ++ phydev->speed = SPEED_UNKNOWN; ++ phydev->duplex = DUPLEX_UNKNOWN; ++ phydev->pause = 0; ++ phydev->asym_pause = 0; ++ ++ if (phydev->autoneg == AUTONEG_ENABLE) { ++ ret = genphy_c45_read_lpa(phydev); ++ if (ret) ++ return ret; ++ ++ ret = as21xxx_read_c22_lpa(phydev); ++ if (ret) ++ return ret; ++ ++ phy_resolve_aneg_linkmode(phydev); ++ } else { ++ int speed; ++ ++ linkmode_zero(phydev->lp_advertising); ++ ++ speed = phy_read_mmd(phydev, MDIO_MMD_VEND1, ++ VEND1_SPEED_STATUS); ++ if (speed < 0) ++ return speed; ++ ++ switch (speed & VEND1_SPEED_STATUS) { ++ case VEND1_SPEED_10000: ++ phydev->speed = SPEED_10000; ++ phydev->duplex = DUPLEX_FULL; ++ break; ++ case VEND1_SPEED_5000: ++ phydev->speed = SPEED_5000; ++ phydev->duplex = DUPLEX_FULL; ++ break; ++ case VEND1_SPEED_2500: ++ phydev->speed = SPEED_2500; ++ phydev->duplex = DUPLEX_FULL; ++ break; ++ case VEND1_SPEED_1000: ++ phydev->speed = SPEED_1000; ++ if (bmcr & BMCR_FULLDPLX) ++ phydev->duplex = DUPLEX_FULL; ++ else ++ phydev->duplex = DUPLEX_HALF; ++ break; ++ case VEND1_SPEED_100: ++ phydev->speed = SPEED_100; ++ phydev->duplex = DUPLEX_FULL; ++ break; ++ case VEND1_SPEED_10: ++ phydev->speed = SPEED_10; ++ phydev->duplex = DUPLEX_FULL; ++ break; ++ default: ++ return -EINVAL; ++ } ++ } ++ ++ return 0; ++} ++ ++static int as21xxx_led_brightness_set(struct phy_device *phydev, ++ u8 index, enum led_brightness value) ++{ ++ u16 val = VEND1_LED_REG_A_EVENT_OFF; ++ ++ if (index > AEON_MAX_LEDS) ++ return -EINVAL; ++ ++ if (value) ++ val = VEND1_LED_REG_A_EVENT_ON; ++ ++ return phy_modify_mmd(phydev, MDIO_MMD_VEND1, ++ VEND1_LED_REG(index), ++ VEND1_LED_REG_A_EVENT, ++ FIELD_PREP(VEND1_LED_REG_A_EVENT, val)); ++} ++ ++static int as21xxx_led_hw_is_supported(struct phy_device *phydev, u8 index, ++ unsigned long rules) ++{ ++ int i; ++ ++ if (index > AEON_MAX_LEDS) ++ return -EINVAL; ++ ++ for (i = 0; i < ARRAY_SIZE(as21xxx_led_supported_pattern); i++) ++ if (rules == as21xxx_led_supported_pattern[i].pattern) ++ return 0; ++ ++ return -EOPNOTSUPP; ++} ++ ++static int as21xxx_led_hw_control_get(struct phy_device *phydev, u8 index, ++ unsigned long *rules) ++{ ++ int i, val; ++ ++ if (index > AEON_MAX_LEDS) ++ return -EINVAL; ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_LED_REG(index)); ++ if (val < 0) ++ return val; ++ ++ val = FIELD_GET(VEND1_LED_REG_A_EVENT, val); ++ for (i = 0; i < ARRAY_SIZE(as21xxx_led_supported_pattern); i++) ++ if (val == as21xxx_led_supported_pattern[i].val) { ++ *rules = as21xxx_led_supported_pattern[i].pattern; ++ return 0; ++ } ++ ++ /* Should be impossible */ ++ return -EINVAL; ++} ++ ++static int as21xxx_led_hw_control_set(struct phy_device *phydev, u8 index, ++ unsigned long rules) ++{ ++ u16 val = 0; ++ int i; ++ ++ if (index > AEON_MAX_LEDS) ++ return -EINVAL; ++ ++ for (i = 0; i < ARRAY_SIZE(as21xxx_led_supported_pattern); i++) ++ if (rules == as21xxx_led_supported_pattern[i].pattern) { ++ val = as21xxx_led_supported_pattern[i].val; ++ break; ++ } ++ ++ return phy_modify_mmd(phydev, MDIO_MMD_VEND1, ++ VEND1_LED_REG(index), ++ VEND1_LED_REG_A_EVENT, ++ FIELD_PREP(VEND1_LED_REG_A_EVENT, val)); ++} ++ ++static int as21xxx_led_polarity_set(struct phy_device *phydev, int index, ++ unsigned long modes) ++{ ++ bool led_active_low = false; ++ u16 mask, val = 0; ++ u32 mode; ++ ++ if (index > AEON_MAX_LEDS) ++ return -EINVAL; ++ ++ for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) { ++ switch (mode) { ++ case PHY_LED_ACTIVE_LOW: ++ led_active_low = true; ++ break; ++ case PHY_LED_ACTIVE_HIGH: /* default mode */ ++ led_active_low = false; ++ break; ++ default: ++ return -EINVAL; ++ } ++ } ++ ++ mask = VEND1_GLB_CPU_CTRL_LED_POLARITY(index); ++ if (led_active_low) ++ val = VEND1_GLB_CPU_CTRL_LED_POLARITY(index); ++ ++ return phy_modify_mmd(phydev, MDIO_MMD_VEND1, ++ VEND1_GLB_REG_CPU_CTRL, ++ mask, val); ++} ++ ++static int as21xxx_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) ++{ ++ struct as21xxx_priv *priv; ++ u16 ret_sts; ++ u32 phy_id; ++ int ret; ++ ++ /* Skip PHY that are not AS21xxx or already have firmware loaded */ ++ if (phydev->c45_ids.device_ids[MDIO_MMD_PCS] != PHY_ID_AS21XXX) ++ return genphy_match_phy_device(phydev, (struct phy_driver *)phydrv); ++ ++ /* Read PHY ID to handle firmware just loaded */ ++ ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MII_PHYSID1); ++ if (ret < 0) ++ return ret; ++ phy_id = ret << 16; ++ ++ ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MII_PHYSID2); ++ if (ret < 0) ++ return ret; ++ phy_id |= ret; ++ ++ /* With PHY ID not the generic AS21xxx one assume ++ * the firmware just loaded ++ */ ++ if (phy_id != PHY_ID_AS21XXX) ++ return phy_id == phydrv->phy_id; ++ ++ /* Allocate temp priv and load the firmware */ ++ priv = kzalloc(sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ mutex_init(&priv->ipc_lock); ++ ++ ret = aeon_firmware_load(phydev); ++ if (ret) ++ goto out; ++ ++ /* Sync parity... */ ++ ret = aeon_ipc_sync_parity(phydev, priv); ++ if (ret) ++ goto out; ++ ++ /* ...and send a third NOOP cmd to wait for firmware finish loading */ ++ ret = aeon_ipc_noop(phydev, priv, &ret_sts); ++ if (ret) ++ goto out; ++ ++out: ++ mutex_destroy(&priv->ipc_lock); ++ kfree(priv); ++ ++ /* Return can either be 0 or a negative error code. ++ * Returning 0 here means THIS is NOT a suitable PHY. ++ * ++ * For the specific case of the generic Aeonsemi PHY ID that ++ * needs the firmware the be loaded first to have a correct PHY ID, ++ * this is OK as a matching PHY ID will be found right after. ++ * This relies on the driver probe order where the first PHY driver ++ * probed is the generic one. ++ */ ++ return ret; ++} ++ ++static struct phy_driver as21xxx_drivers[] = { ++ { ++ /* PHY expose in C45 as 0x7500 0x9410 ++ * before firmware is loaded. ++ * This driver entry must be attempted first to load ++ * the firmware and thus update the ID registers. ++ */ ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21XXX), ++ .name = "Aeonsemi AS21xxx", ++ .match_phy_device = as21xxx_match_phy_device, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21011JB1), ++ .name = "Aeonsemi AS21011JB1", ++ .probe = as21xxx_probe, ++ .match_phy_device = as21xxx_match_phy_device, ++ .read_status = as21xxx_read_status, ++ .led_brightness_set = as21xxx_led_brightness_set, ++ .led_hw_is_supported = as21xxx_led_hw_is_supported, ++ .led_hw_control_set = as21xxx_led_hw_control_set, ++ .led_hw_control_get = as21xxx_led_hw_control_get, ++ .led_polarity_set = as21xxx_led_polarity_set, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21011PB1), ++ .name = "Aeonsemi AS21011PB1", ++ .probe = as21xxx_probe, ++ .match_phy_device = as21xxx_match_phy_device, ++ .read_status = as21xxx_read_status, ++ .led_brightness_set = as21xxx_led_brightness_set, ++ .led_hw_is_supported = as21xxx_led_hw_is_supported, ++ .led_hw_control_set = as21xxx_led_hw_control_set, ++ .led_hw_control_get = as21xxx_led_hw_control_get, ++ .led_polarity_set = as21xxx_led_polarity_set, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21010PB1), ++ .name = "Aeonsemi AS21010PB1", ++ .probe = as21xxx_probe, ++ .match_phy_device = as21xxx_match_phy_device, ++ .read_status = as21xxx_read_status, ++ .led_brightness_set = as21xxx_led_brightness_set, ++ .led_hw_is_supported = as21xxx_led_hw_is_supported, ++ .led_hw_control_set = as21xxx_led_hw_control_set, ++ .led_hw_control_get = as21xxx_led_hw_control_get, ++ .led_polarity_set = as21xxx_led_polarity_set, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21010JB1), ++ .name = "Aeonsemi AS21010JB1", ++ .probe = as21xxx_probe, ++ .match_phy_device = as21xxx_match_phy_device, ++ .read_status = as21xxx_read_status, ++ .led_brightness_set = as21xxx_led_brightness_set, ++ .led_hw_is_supported = as21xxx_led_hw_is_supported, ++ .led_hw_control_set = as21xxx_led_hw_control_set, ++ .led_hw_control_get = as21xxx_led_hw_control_get, ++ .led_polarity_set = as21xxx_led_polarity_set, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21210PB1), ++ .name = "Aeonsemi AS21210PB1", ++ .probe = as21xxx_probe, ++ .match_phy_device = as21xxx_match_phy_device, ++ .read_status = as21xxx_read_status, ++ .led_brightness_set = as21xxx_led_brightness_set, ++ .led_hw_is_supported = as21xxx_led_hw_is_supported, ++ .led_hw_control_set = as21xxx_led_hw_control_set, ++ .led_hw_control_get = as21xxx_led_hw_control_get, ++ .led_polarity_set = as21xxx_led_polarity_set, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21510JB1), ++ .name = "Aeonsemi AS21510JB1", ++ .probe = as21xxx_probe, ++ .match_phy_device = as21xxx_match_phy_device, ++ .read_status = as21xxx_read_status, ++ .led_brightness_set = as21xxx_led_brightness_set, ++ .led_hw_is_supported = as21xxx_led_hw_is_supported, ++ .led_hw_control_set = as21xxx_led_hw_control_set, ++ .led_hw_control_get = as21xxx_led_hw_control_get, ++ .led_polarity_set = as21xxx_led_polarity_set, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21510PB1), ++ .name = "Aeonsemi AS21510PB1", ++ .probe = as21xxx_probe, ++ .match_phy_device = as21xxx_match_phy_device, ++ .read_status = as21xxx_read_status, ++ .led_brightness_set = as21xxx_led_brightness_set, ++ .led_hw_is_supported = as21xxx_led_hw_is_supported, ++ .led_hw_control_set = as21xxx_led_hw_control_set, ++ .led_hw_control_get = as21xxx_led_hw_control_get, ++ .led_polarity_set = as21xxx_led_polarity_set, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21511JB1), ++ .name = "Aeonsemi AS21511JB1", ++ .probe = as21xxx_probe, ++ .match_phy_device = as21xxx_match_phy_device, ++ .read_status = as21xxx_read_status, ++ .led_brightness_set = as21xxx_led_brightness_set, ++ .led_hw_is_supported = as21xxx_led_hw_is_supported, ++ .led_hw_control_set = as21xxx_led_hw_control_set, ++ .led_hw_control_get = as21xxx_led_hw_control_get, ++ .led_polarity_set = as21xxx_led_polarity_set, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21210JB1), ++ .name = "Aeonsemi AS21210JB1", ++ .probe = as21xxx_probe, ++ .match_phy_device = as21xxx_match_phy_device, ++ .read_status = as21xxx_read_status, ++ .led_brightness_set = as21xxx_led_brightness_set, ++ .led_hw_is_supported = as21xxx_led_hw_is_supported, ++ .led_hw_control_set = as21xxx_led_hw_control_set, ++ .led_hw_control_get = as21xxx_led_hw_control_get, ++ .led_polarity_set = as21xxx_led_polarity_set, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21511PB1), ++ .name = "Aeonsemi AS21511PB1", ++ .probe = as21xxx_probe, ++ .match_phy_device = as21xxx_match_phy_device, ++ .read_status = as21xxx_read_status, ++ .led_brightness_set = as21xxx_led_brightness_set, ++ .led_hw_is_supported = as21xxx_led_hw_is_supported, ++ .led_hw_control_set = as21xxx_led_hw_control_set, ++ .led_hw_control_get = as21xxx_led_hw_control_get, ++ .led_polarity_set = as21xxx_led_polarity_set, ++ }, ++}; ++module_phy_driver(as21xxx_drivers); ++ ++static struct mdio_device_id __maybe_unused as21xxx_tbl[] = { ++ { PHY_ID_MATCH_VENDOR(PHY_VENDOR_AEONSEMI) }, ++ { } ++}; ++MODULE_DEVICE_TABLE(mdio, as21xxx_tbl); ++ ++MODULE_DESCRIPTION("Aeonsemi AS21xxx PHY driver"); ++MODULE_AUTHOR("Christian Marangi "); ++MODULE_LICENSE("GPL"); diff --git a/lede/target/linux/generic/backport-6.6/782-01-v6.16-net-phy-pass-PHY-driver-to-.match_phy_device-OP.patch b/lede/target/linux/generic/backport-6.6/782-01-v6.16-net-phy-pass-PHY-driver-to-.match_phy_device-OP.patch new file mode 100644 index 0000000000..b8460a2b5e --- /dev/null +++ b/lede/target/linux/generic/backport-6.6/782-01-v6.16-net-phy-pass-PHY-driver-to-.match_phy_device-OP.patch @@ -0,0 +1,273 @@ +From 31afd6bc55cc0093c3e5b0a368319e423d4de8ea Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Sat, 17 May 2025 22:13:45 +0200 +Subject: [PATCH 1/5] net: phy: pass PHY driver to .match_phy_device OP + +Pass PHY driver pointer to .match_phy_device OP in addition to phydev. +Having access to the PHY driver struct might be useful to check the +PHY ID of the driver is being matched for in case the PHY ID scanned in +the phydev is not consistent. + +A scenario for this is a PHY that change PHY ID after a firmware is +loaded, in such case, the PHY ID stored in PHY device struct is not +valid anymore and PHY will manually scan the ID in the match_phy_device +function. + +Having the PHY driver info is also useful for those PHY driver that +implement multiple simple .match_phy_device OP to match specific MMD PHY +ID. With this extra info if the parsing logic is the same, the matching +function can be generalized by using the phy_id in the PHY driver +instead of hardcoding. + +Rust wrapper callback is updated to align to the new match_phy_device +arguments. + +Suggested-by: Russell King (Oracle) +Reviewed-by: Russell King (Oracle) +Signed-off-by: Christian Marangi +Reviewed-by: Benno Lossin # for Rust +Reviewed-by: FUJITA Tomonori +Link: https://patch.msgid.link/20250517201353.5137-2-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/bcm87xx.c | 6 ++++-- + drivers/net/phy/icplus.c | 6 ++++-- + drivers/net/phy/marvell10g.c | 12 ++++++++---- + drivers/net/phy/micrel.c | 6 ++++-- + drivers/net/phy/nxp-c45-tja11xx.c | 12 ++++++++---- + drivers/net/phy/nxp-tja11xx.c | 6 ++++-- + drivers/net/phy/phy_device.c | 2 +- + drivers/net/phy/realtek/realtek_main.c | 27 +++++++++++++++++--------- + drivers/net/phy/teranetics.c | 3 ++- + include/linux/phy.h | 3 ++- + rust/kernel/net/phy.rs | 1 + + 11 files changed, 56 insertions(+), 28 deletions(-) + +--- a/drivers/net/phy/bcm87xx.c ++++ b/drivers/net/phy/bcm87xx.c +@@ -185,12 +185,14 @@ static irqreturn_t bcm87xx_handle_interr + return IRQ_HANDLED; + } + +-static int bcm8706_match_phy_device(struct phy_device *phydev) ++static int bcm8706_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return phydev->c45_ids.device_ids[4] == PHY_ID_BCM8706; + } + +-static int bcm8727_match_phy_device(struct phy_device *phydev) ++static int bcm8727_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return phydev->c45_ids.device_ids[4] == PHY_ID_BCM8727; + } +--- a/drivers/net/phy/icplus.c ++++ b/drivers/net/phy/icplus.c +@@ -520,12 +520,14 @@ static int ip101a_g_match_phy_device(str + return ip101a == !ret; + } + +-static int ip101a_match_phy_device(struct phy_device *phydev) ++static int ip101a_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return ip101a_g_match_phy_device(phydev, true); + } + +-static int ip101g_match_phy_device(struct phy_device *phydev) ++static int ip101g_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return ip101a_g_match_phy_device(phydev, false); + } +--- a/drivers/net/phy/marvell10g.c ++++ b/drivers/net/phy/marvell10g.c +@@ -1221,7 +1221,8 @@ static int mv3310_get_number_of_ports(st + return ret + 1; + } + +-static int mv3310_match_phy_device(struct phy_device *phydev) ++static int mv3310_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + if ((phydev->c45_ids.device_ids[MDIO_MMD_PMAPMD] & + MARVELL_PHY_ID_MASK) != MARVELL_PHY_ID_88X3310) +@@ -1230,7 +1231,8 @@ static int mv3310_match_phy_device(struc + return mv3310_get_number_of_ports(phydev) == 1; + } + +-static int mv3340_match_phy_device(struct phy_device *phydev) ++static int mv3340_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + if ((phydev->c45_ids.device_ids[MDIO_MMD_PMAPMD] & + MARVELL_PHY_ID_MASK) != MARVELL_PHY_ID_88X3310) +@@ -1254,12 +1256,14 @@ static int mv211x_match_phy_device(struc + return !!(val & MDIO_PCS_SPEED_5G) == has_5g; + } + +-static int mv2110_match_phy_device(struct phy_device *phydev) ++static int mv2110_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return mv211x_match_phy_device(phydev, true); + } + +-static int mv2111_match_phy_device(struct phy_device *phydev) ++static int mv2111_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return mv211x_match_phy_device(phydev, false); + } +--- a/drivers/net/phy/micrel.c ++++ b/drivers/net/phy/micrel.c +@@ -670,7 +670,8 @@ static int ksz8051_ksz8795_match_phy_dev + return !ret; + } + +-static int ksz8051_match_phy_device(struct phy_device *phydev) ++static int ksz8051_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return ksz8051_ksz8795_match_phy_device(phydev, true); + } +@@ -790,7 +791,8 @@ static int ksz8061_config_init(struct ph + return kszphy_config_init(phydev); + } + +-static int ksz8795_match_phy_device(struct phy_device *phydev) ++static int ksz8795_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return ksz8051_ksz8795_match_phy_device(phydev, false); + } +--- a/drivers/net/phy/nxp-tja11xx.c ++++ b/drivers/net/phy/nxp-tja11xx.c +@@ -648,12 +648,14 @@ static int tja1102_match_phy_device(stru + return !ret; + } + +-static int tja1102_p0_match_phy_device(struct phy_device *phydev) ++static int tja1102_p0_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return tja1102_match_phy_device(phydev, true); + } + +-static int tja1102_p1_match_phy_device(struct phy_device *phydev) ++static int tja1102_p1_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return tja1102_match_phy_device(phydev, false); + } +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -533,7 +533,7 @@ static int phy_bus_match(struct device * + return 0; + + if (phydrv->match_phy_device) +- return phydrv->match_phy_device(phydev); ++ return phydrv->match_phy_device(phydev, phydrv); + + if (phydev->is_c45) { + for (i = 1; i < num_ids; i++) { +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -1315,13 +1315,15 @@ static bool rtlgen_supports_mmd(struct p + return val > 0; + } + +-static int rtlgen_match_phy_device(struct phy_device *phydev) ++static int rtlgen_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return phydev->phy_id == RTL_GENERIC_PHYID && + !rtlgen_supports_2_5gbps(phydev); + } + +-static int rtl8226_match_phy_device(struct phy_device *phydev) ++static int rtl8226_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return phydev->phy_id == RTL_GENERIC_PHYID && + rtlgen_supports_2_5gbps(phydev) && +@@ -1337,32 +1339,38 @@ static int rtlgen_is_c45_match(struct ph + return !is_c45 && (id == phydev->phy_id); + } + +-static int rtl8221b_match_phy_device(struct phy_device *phydev) ++static int rtl8221b_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return phydev->phy_id == RTL_8221B && rtlgen_supports_mmd(phydev); + } + +-static int rtl8221b_vb_cg_c22_match_phy_device(struct phy_device *phydev) ++static int rtl8221b_vb_cg_c22_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return rtlgen_is_c45_match(phydev, RTL_8221B_VB_CG, false); + } + +-static int rtl8221b_vb_cg_c45_match_phy_device(struct phy_device *phydev) ++static int rtl8221b_vb_cg_c45_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return rtlgen_is_c45_match(phydev, RTL_8221B_VB_CG, true); + } + +-static int rtl8221b_vn_cg_c22_match_phy_device(struct phy_device *phydev) ++static int rtl8221b_vn_cg_c22_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return rtlgen_is_c45_match(phydev, RTL_8221B_VN_CG, false); + } + +-static int rtl8221b_vn_cg_c45_match_phy_device(struct phy_device *phydev) ++static int rtl8221b_vn_cg_c45_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return rtlgen_is_c45_match(phydev, RTL_8221B_VN_CG, true); + } + +-static int rtl_internal_nbaset_match_phy_device(struct phy_device *phydev) ++static int rtl_internal_nbaset_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + if (phydev->is_c45) + return false; +@@ -1381,7 +1389,8 @@ static int rtl_internal_nbaset_match_phy + return rtlgen_supports_2_5gbps(phydev) && !rtlgen_supports_mmd(phydev); + } + +-static int rtl8251b_c45_match_phy_device(struct phy_device *phydev) ++static int rtl8251b_c45_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return rtlgen_is_c45_match(phydev, RTL_8251B, true); + } +--- a/drivers/net/phy/teranetics.c ++++ b/drivers/net/phy/teranetics.c +@@ -67,7 +67,8 @@ static int teranetics_read_status(struct + return 0; + } + +-static int teranetics_match_phy_device(struct phy_device *phydev) ++static int teranetics_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return phydev->c45_ids.device_ids[3] == PHY_ID_TN2020; + } +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -972,7 +972,8 @@ struct phy_driver { + * driver for the given phydev. If NULL, matching is based on + * phy_id and phy_id_mask. + */ +- int (*match_phy_device)(struct phy_device *phydev); ++ int (*match_phy_device)(struct phy_device *phydev, ++ const struct phy_driver *phydrv); + + /** + * @set_wol: Some devices (e.g. qnap TS-119P II) require PHY diff --git a/lede/target/linux/generic/backport-6.6/782-04-v6.16-net-phy-introduce-genphy_match_phy_device.patch b/lede/target/linux/generic/backport-6.6/782-04-v6.16-net-phy-introduce-genphy_match_phy_device.patch new file mode 100644 index 0000000000..1c0b5d836d --- /dev/null +++ b/lede/target/linux/generic/backport-6.6/782-04-v6.16-net-phy-introduce-genphy_match_phy_device.patch @@ -0,0 +1,109 @@ +From d6c45707ac84c2d9f274ece1cea4dddb97996bde Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Sat, 17 May 2025 22:13:48 +0200 +Subject: [PATCH 4/5] net: phy: introduce genphy_match_phy_device() + +Introduce new API, genphy_match_phy_device(), to provide a way to check +to match a PHY driver for a PHY device based on the info stored in the +PHY device struct. + +The function generalize the logic used in phy_bus_match() to check the +PHY ID whether if C45 or C22 ID should be used for matching. + +This is useful for custom .match_phy_device function that wants to use +the generic logic under some condition. (example a PHY is already setup +and provide the correct PHY ID) + +Reviewed-by: Russell King (Oracle) +Signed-off-by: Christian Marangi +Link: https://patch.msgid.link/20250517201353.5137-5-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/phy_device.c | 52 +++++++++++++++++++++++++----------- + include/linux/phy.h | 3 +++ + 2 files changed, 40 insertions(+), 15 deletions(-) + +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -522,20 +522,26 @@ static int phy_scan_fixups(struct phy_de + return 0; + } + +-static int phy_bus_match(struct device *dev, struct device_driver *drv) ++/** ++ * genphy_match_phy_device - match a PHY device with a PHY driver ++ * @phydev: target phy_device struct ++ * @phydrv: target phy_driver struct ++ * ++ * Description: Checks whether the given PHY device matches the specified ++ * PHY driver. For Clause 45 PHYs, iterates over the available device ++ * identifiers and compares them against the driver's expected PHY ID, ++ * applying the provided mask. For Clause 22 PHYs, a direct ID comparison ++ * is performed. ++ * ++ * Return: 1 if the PHY device matches the driver, 0 otherwise. ++ */ ++int genphy_match_phy_device(struct phy_device *phydev, ++ struct phy_driver *phydrv) + { +- struct phy_device *phydev = to_phy_device(dev); +- struct phy_driver *phydrv = to_phy_driver(drv); +- const int num_ids = ARRAY_SIZE(phydev->c45_ids.device_ids); +- int i; +- +- if (!(phydrv->mdiodrv.flags & MDIO_DEVICE_IS_PHY)) +- return 0; +- +- if (phydrv->match_phy_device) +- return phydrv->match_phy_device(phydev, phydrv); +- + if (phydev->is_c45) { ++ const int num_ids = ARRAY_SIZE(phydev->c45_ids.device_ids); ++ int i; ++ + for (i = 1; i < num_ids; i++) { + if (phydev->c45_ids.device_ids[i] == 0xffffffff) + continue; +@@ -544,11 +550,27 @@ static int phy_bus_match(struct device * + phydrv->phy_id, phydrv->phy_id_mask)) + return 1; + } ++ + return 0; +- } else { +- return phy_id_compare(phydev->phy_id, phydrv->phy_id, +- phydrv->phy_id_mask); + } ++ ++ return phy_id_compare(phydev->phy_id, phydrv->phy_id, ++ phydrv->phy_id_mask); ++} ++EXPORT_SYMBOL_GPL(genphy_match_phy_device); ++ ++static int phy_bus_match(struct device *dev, struct device_driver *drv) ++{ ++ struct phy_device *phydev = to_phy_device(dev); ++ struct phy_driver *phydrv = to_phy_driver(drv); ++ ++ if (!(phydrv->mdiodrv.flags & MDIO_DEVICE_IS_PHY)) ++ return 0; ++ ++ if (phydrv->match_phy_device) ++ return phydrv->match_phy_device(phydev, phydrv); ++ ++ return genphy_match_phy_device(phydev, phydrv); + } + + static ssize_t +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -1812,6 +1812,9 @@ char *phy_attached_info_irq(struct phy_d + __malloc; + void phy_attached_info(struct phy_device *phydev); + ++int genphy_match_phy_device(struct phy_device *phydev, ++ struct phy_driver *phydrv); ++ + /* Clause 22 PHY */ + int genphy_read_abilities(struct phy_device *phydev); + int genphy_setup_forced(struct phy_device *phydev); diff --git a/lede/target/linux/generic/backport-6.6/782-05-v6.16-net-phy-Add-support-for-Aeonsemi-AS21xxx-PHYs.patch b/lede/target/linux/generic/backport-6.6/782-05-v6.16-net-phy-Add-support-for-Aeonsemi-AS21xxx-PHYs.patch new file mode 100644 index 0000000000..cf33b7bfdf --- /dev/null +++ b/lede/target/linux/generic/backport-6.6/782-05-v6.16-net-phy-Add-support-for-Aeonsemi-AS21xxx-PHYs.patch @@ -0,0 +1,1165 @@ +From 830877d89edcd834e4b4d0fcc021ff619d89505e Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Sat, 17 May 2025 22:13:49 +0200 +Subject: [PATCH 5/5] net: phy: Add support for Aeonsemi AS21xxx PHYs + +Add support for Aeonsemi AS21xxx 10G C45 PHYs. These PHYs integrate +an IPC to setup some configuration and require special handling to +sync with the parity bit. The parity bit is a way the IPC use to +follow correct order of command sent. + +Supported PHYs AS21011JB1, AS21011PB1, AS21010JB1, AS21010PB1, +AS21511JB1, AS21511PB1, AS21510JB1, AS21510PB1, AS21210JB1, +AS21210PB1 that all register with the PHY ID 0x7500 0x7510 +before the firmware is loaded. + +They all support up to 5 LEDs with various HW mode supported. + +While implementing it was found some strange coincidence with using the +same logic for implementing C22 in MMD regs in Broadcom PHYs. + +For reference here the AS21xxx PHY name logic: + +AS21x1xxB1 + ^ ^^ + | |J: Supports SyncE/PTP + | |P: No SyncE/PTP support + | 1: Supports 2nd Serdes + | 2: Not 2nd Serdes support + 0: 10G, 5G, 2.5G + 5: 5G, 2.5G + 2: 2.5G + +Reviewed-by: Andrew Lunn +Signed-off-by: Christian Marangi +Link: https://patch.msgid.link/20250517201353.5137-6-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +--- + MAINTAINERS | 6 + + drivers/net/phy/Kconfig | 12 + + drivers/net/phy/Makefile | 1 + + drivers/net/phy/as21xxx.c | 1087 +++++++++++++++++++++++++++++++++++++ + 4 files changed, 1106 insertions(+) + create mode 100644 drivers/net/phy/as21xxx.c + +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -68,6 +68,18 @@ config SFP + + comment "MII PHY device drivers" + ++config AS21XXX_PHY ++ tristate "Aeonsemi AS21xxx PHYs" ++ help ++ Currently supports the Aeonsemi AS21xxx PHY. ++ ++ These are C45 PHYs 10G that require all a generic firmware. ++ ++ Supported PHYs AS21011JB1, AS21011PB1, AS21010JB1, AS21010PB1, ++ AS21511JB1, AS21511PB1, AS21510JB1, AS21510PB1, AS21210JB1, ++ AS21210PB1 that all register with the PHY ID 0x7500 0x7500 ++ before the firmware is loaded. ++ + config AMD_PHY + tristate "AMD PHYs" + help +--- a/drivers/net/phy/Makefile ++++ b/drivers/net/phy/Makefile +@@ -36,6 +36,7 @@ obj-$(CONFIG_ADIN_PHY) += adin.o + obj-$(CONFIG_ADIN1100_PHY) += adin1100.o + obj-$(CONFIG_AMD_PHY) += amd.o + obj-$(CONFIG_AQUANTIA_PHY) += aquantia/ ++obj-$(CONFIG_AS21XXX_PHY) += as21xxx.o + obj-$(CONFIG_AX88796B_PHY) += ax88796b.o + obj-$(CONFIG_BCM54140_PHY) += bcm54140.o + obj-$(CONFIG_BCM63XX_PHY) += bcm63xx.o +--- /dev/null ++++ b/drivers/net/phy/as21xxx.c +@@ -0,0 +1,1087 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Aeonsemi AS21XXxX PHY Driver ++ * ++ * Author: Christian Marangi ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#define VEND1_GLB_REG_CPU_RESET_ADDR_LO_BASEADDR 0x3 ++#define VEND1_GLB_REG_CPU_RESET_ADDR_HI_BASEADDR 0x4 ++ ++#define VEND1_GLB_REG_CPU_CTRL 0xe ++#define VEND1_GLB_CPU_CTRL_MASK GENMASK(4, 0) ++#define VEND1_GLB_CPU_CTRL_LED_POLARITY_MASK GENMASK(12, 8) ++#define VEND1_GLB_CPU_CTRL_LED_POLARITY(_n) FIELD_PREP(VEND1_GLB_CPU_CTRL_LED_POLARITY_MASK, \ ++ BIT(_n)) ++ ++#define VEND1_FW_START_ADDR 0x100 ++ ++#define VEND1_GLB_REG_MDIO_INDIRECT_ADDRCMD 0x101 ++#define VEND1_GLB_REG_MDIO_INDIRECT_LOAD 0x102 ++ ++#define VEND1_GLB_REG_MDIO_INDIRECT_STATUS 0x103 ++ ++#define VEND1_PTP_CLK 0x142 ++#define VEND1_PTP_CLK_EN BIT(6) ++ ++/* 5 LED at step of 0x20 ++ * FE: Fast-Ethernet (10/100) ++ * GE: Gigabit-Ethernet (1000) ++ * NG: New-Generation (2500/5000/10000) ++ */ ++#define VEND1_LED_REG(_n) (0x1800 + ((_n) * 0x10)) ++#define VEND1_LED_REG_A_EVENT GENMASK(15, 11) ++#define VEND1_LED_CONF 0x1881 ++#define VEND1_LED_CONFG_BLINK GENMASK(7, 0) ++ ++#define VEND1_SPEED_STATUS 0x4002 ++#define VEND1_SPEED_MASK GENMASK(7, 0) ++#define VEND1_SPEED_10000 FIELD_PREP_CONST(VEND1_SPEED_MASK, 0x3) ++#define VEND1_SPEED_5000 FIELD_PREP_CONST(VEND1_SPEED_MASK, 0x5) ++#define VEND1_SPEED_2500 FIELD_PREP_CONST(VEND1_SPEED_MASK, 0x9) ++#define VEND1_SPEED_1000 FIELD_PREP_CONST(VEND1_SPEED_MASK, 0x10) ++#define VEND1_SPEED_100 FIELD_PREP_CONST(VEND1_SPEED_MASK, 0x20) ++#define VEND1_SPEED_10 FIELD_PREP_CONST(VEND1_SPEED_MASK, 0x0) ++ ++#define VEND1_IPC_CMD 0x5801 ++#define AEON_IPC_CMD_PARITY BIT(15) ++#define AEON_IPC_CMD_SIZE GENMASK(10, 6) ++#define AEON_IPC_CMD_OPCODE GENMASK(5, 0) ++ ++#define IPC_CMD_NOOP 0x0 /* Do nothing */ ++#define IPC_CMD_INFO 0x1 /* Get Firmware Version */ ++#define IPC_CMD_SYS_CPU 0x2 /* SYS_CPU */ ++#define IPC_CMD_BULK_DATA 0xa /* Pass bulk data in ipc registers. */ ++#define IPC_CMD_BULK_WRITE 0xc /* Write bulk data to memory */ ++#define IPC_CMD_CFG_PARAM 0x1a /* Write config parameters to memory */ ++#define IPC_CMD_NG_TESTMODE 0x1b /* Set NG test mode and tone */ ++#define IPC_CMD_TEMP_MON 0x15 /* Temperature monitoring function */ ++#define IPC_CMD_SET_LED 0x23 /* Set led */ ++ ++#define VEND1_IPC_STS 0x5802 ++#define AEON_IPC_STS_PARITY BIT(15) ++#define AEON_IPC_STS_SIZE GENMASK(14, 10) ++#define AEON_IPC_STS_OPCODE GENMASK(9, 4) ++#define AEON_IPC_STS_STATUS GENMASK(3, 0) ++#define AEON_IPC_STS_STATUS_RCVD FIELD_PREP_CONST(AEON_IPC_STS_STATUS, 0x1) ++#define AEON_IPC_STS_STATUS_PROCESS FIELD_PREP_CONST(AEON_IPC_STS_STATUS, 0x2) ++#define AEON_IPC_STS_STATUS_SUCCESS FIELD_PREP_CONST(AEON_IPC_STS_STATUS, 0x4) ++#define AEON_IPC_STS_STATUS_ERROR FIELD_PREP_CONST(AEON_IPC_STS_STATUS, 0x8) ++#define AEON_IPC_STS_STATUS_BUSY FIELD_PREP_CONST(AEON_IPC_STS_STATUS, 0xe) ++#define AEON_IPC_STS_STATUS_READY FIELD_PREP_CONST(AEON_IPC_STS_STATUS, 0xf) ++ ++#define VEND1_IPC_DATA0 0x5808 ++#define VEND1_IPC_DATA1 0x5809 ++#define VEND1_IPC_DATA2 0x580a ++#define VEND1_IPC_DATA3 0x580b ++#define VEND1_IPC_DATA4 0x580c ++#define VEND1_IPC_DATA5 0x580d ++#define VEND1_IPC_DATA6 0x580e ++#define VEND1_IPC_DATA7 0x580f ++#define VEND1_IPC_DATA(_n) (VEND1_IPC_DATA0 + (_n)) ++ ++/* Sub command of CMD_INFO */ ++#define IPC_INFO_VERSION 0x1 ++ ++/* Sub command of CMD_SYS_CPU */ ++#define IPC_SYS_CPU_REBOOT 0x3 ++#define IPC_SYS_CPU_IMAGE_OFST 0x4 ++#define IPC_SYS_CPU_IMAGE_CHECK 0x5 ++#define IPC_SYS_CPU_PHY_ENABLE 0x6 ++ ++/* Sub command of CMD_CFG_PARAM */ ++#define IPC_CFG_PARAM_DIRECT 0x4 ++ ++/* CFG DIRECT sub command */ ++#define IPC_CFG_PARAM_DIRECT_NG_PHYCTRL 0x1 ++#define IPC_CFG_PARAM_DIRECT_CU_AN 0x2 ++#define IPC_CFG_PARAM_DIRECT_SDS_PCS 0x3 ++#define IPC_CFG_PARAM_DIRECT_AUTO_EEE 0x4 ++#define IPC_CFG_PARAM_DIRECT_SDS_PMA 0x5 ++#define IPC_CFG_PARAM_DIRECT_DPC_RA 0x6 ++#define IPC_CFG_PARAM_DIRECT_DPC_PKT_CHK 0x7 ++#define IPC_CFG_PARAM_DIRECT_DPC_SDS_WAIT_ETH 0x8 ++#define IPC_CFG_PARAM_DIRECT_WDT 0x9 ++#define IPC_CFG_PARAM_DIRECT_SDS_RESTART_AN 0x10 ++#define IPC_CFG_PARAM_DIRECT_TEMP_MON 0x11 ++#define IPC_CFG_PARAM_DIRECT_WOL 0x12 ++ ++/* Sub command of CMD_TEMP_MON */ ++#define IPC_CMD_TEMP_MON_GET 0x4 ++ ++#define AS21XXX_MDIO_AN_C22 0xffe0 ++ ++#define PHY_ID_AS21XXX 0x75009410 ++/* AS21xxx ID Legend ++ * AS21x1xxB1 ++ * ^ ^^ ++ * | |J: Supports SyncE/PTP ++ * | |P: No SyncE/PTP support ++ * | 1: Supports 2nd Serdes ++ * | 2: Not 2nd Serdes support ++ * 0: 10G, 5G, 2.5G ++ * 5: 5G, 2.5G ++ * 2: 2.5G ++ */ ++#define PHY_ID_AS21011JB1 0x75009402 ++#define PHY_ID_AS21011PB1 0x75009412 ++#define PHY_ID_AS21010JB1 0x75009422 ++#define PHY_ID_AS21010PB1 0x75009432 ++#define PHY_ID_AS21511JB1 0x75009442 ++#define PHY_ID_AS21511PB1 0x75009452 ++#define PHY_ID_AS21510JB1 0x75009462 ++#define PHY_ID_AS21510PB1 0x75009472 ++#define PHY_ID_AS21210JB1 0x75009482 ++#define PHY_ID_AS21210PB1 0x75009492 ++#define PHY_VENDOR_AEONSEMI 0x75009400 ++ ++#define AEON_MAX_LEDS 5 ++#define AEON_IPC_DELAY 10000 ++#define AEON_IPC_TIMEOUT (AEON_IPC_DELAY * 100) ++#define AEON_IPC_DATA_NUM_REGISTERS 8 ++#define AEON_IPC_DATA_MAX (AEON_IPC_DATA_NUM_REGISTERS * sizeof(u16)) ++ ++#define AEON_BOOT_ADDR 0x1000 ++#define AEON_CPU_BOOT_ADDR 0x2000 ++#define AEON_CPU_CTRL_FW_LOAD (BIT(4) | BIT(2) | BIT(1) | BIT(0)) ++#define AEON_CPU_CTRL_FW_START BIT(0) ++ ++enum as21xxx_led_event { ++ VEND1_LED_REG_A_EVENT_ON_10 = 0x0, ++ VEND1_LED_REG_A_EVENT_ON_100, ++ VEND1_LED_REG_A_EVENT_ON_1000, ++ VEND1_LED_REG_A_EVENT_ON_2500, ++ VEND1_LED_REG_A_EVENT_ON_5000, ++ VEND1_LED_REG_A_EVENT_ON_10000, ++ VEND1_LED_REG_A_EVENT_ON_FE_GE, ++ VEND1_LED_REG_A_EVENT_ON_NG, ++ VEND1_LED_REG_A_EVENT_ON_FULL_DUPLEX, ++ VEND1_LED_REG_A_EVENT_ON_COLLISION, ++ VEND1_LED_REG_A_EVENT_BLINK_TX, ++ VEND1_LED_REG_A_EVENT_BLINK_RX, ++ VEND1_LED_REG_A_EVENT_BLINK_ACT, ++ VEND1_LED_REG_A_EVENT_ON_LINK, ++ VEND1_LED_REG_A_EVENT_ON_LINK_BLINK_ACT, ++ VEND1_LED_REG_A_EVENT_ON_LINK_BLINK_RX, ++ VEND1_LED_REG_A_EVENT_ON_FE_GE_BLINK_ACT, ++ VEND1_LED_REG_A_EVENT_ON_NG_BLINK_ACT, ++ VEND1_LED_REG_A_EVENT_ON_NG_BLINK_FE_GE, ++ VEND1_LED_REG_A_EVENT_ON_FD_BLINK_COLLISION, ++ VEND1_LED_REG_A_EVENT_ON, ++ VEND1_LED_REG_A_EVENT_OFF, ++}; ++ ++struct as21xxx_led_pattern_info { ++ unsigned int pattern; ++ u16 val; ++}; ++ ++struct as21xxx_priv { ++ bool parity_status; ++ /* Protect concurrent IPC access */ ++ struct mutex ipc_lock; ++}; ++ ++static struct as21xxx_led_pattern_info as21xxx_led_supported_pattern[] = { ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_10), ++ .val = VEND1_LED_REG_A_EVENT_ON_10 ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_100), ++ .val = VEND1_LED_REG_A_EVENT_ON_100 ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_1000), ++ .val = VEND1_LED_REG_A_EVENT_ON_1000 ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_2500), ++ .val = VEND1_LED_REG_A_EVENT_ON_2500 ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_5000), ++ .val = VEND1_LED_REG_A_EVENT_ON_5000 ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_10000), ++ .val = VEND1_LED_REG_A_EVENT_ON_10000 ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK), ++ .val = VEND1_LED_REG_A_EVENT_ON_LINK ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_10) | ++ BIT(TRIGGER_NETDEV_LINK_100) | ++ BIT(TRIGGER_NETDEV_LINK_1000), ++ .val = VEND1_LED_REG_A_EVENT_ON_FE_GE ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_2500) | ++ BIT(TRIGGER_NETDEV_LINK_5000) | ++ BIT(TRIGGER_NETDEV_LINK_10000), ++ .val = VEND1_LED_REG_A_EVENT_ON_NG ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_FULL_DUPLEX), ++ .val = VEND1_LED_REG_A_EVENT_ON_FULL_DUPLEX ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_TX), ++ .val = VEND1_LED_REG_A_EVENT_BLINK_TX ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_RX), ++ .val = VEND1_LED_REG_A_EVENT_BLINK_RX ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_TX) | ++ BIT(TRIGGER_NETDEV_RX), ++ .val = VEND1_LED_REG_A_EVENT_BLINK_ACT ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_10) | ++ BIT(TRIGGER_NETDEV_LINK_100) | ++ BIT(TRIGGER_NETDEV_LINK_1000) | ++ BIT(TRIGGER_NETDEV_LINK_2500) | ++ BIT(TRIGGER_NETDEV_LINK_5000) | ++ BIT(TRIGGER_NETDEV_LINK_10000), ++ .val = VEND1_LED_REG_A_EVENT_ON_LINK ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_10) | ++ BIT(TRIGGER_NETDEV_LINK_100) | ++ BIT(TRIGGER_NETDEV_LINK_1000) | ++ BIT(TRIGGER_NETDEV_LINK_2500) | ++ BIT(TRIGGER_NETDEV_LINK_5000) | ++ BIT(TRIGGER_NETDEV_LINK_10000) | ++ BIT(TRIGGER_NETDEV_TX) | ++ BIT(TRIGGER_NETDEV_RX), ++ .val = VEND1_LED_REG_A_EVENT_ON_LINK_BLINK_ACT ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_10) | ++ BIT(TRIGGER_NETDEV_LINK_100) | ++ BIT(TRIGGER_NETDEV_LINK_1000) | ++ BIT(TRIGGER_NETDEV_LINK_2500) | ++ BIT(TRIGGER_NETDEV_LINK_5000) | ++ BIT(TRIGGER_NETDEV_LINK_10000) | ++ BIT(TRIGGER_NETDEV_RX), ++ .val = VEND1_LED_REG_A_EVENT_ON_LINK_BLINK_RX ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_10) | ++ BIT(TRIGGER_NETDEV_LINK_100) | ++ BIT(TRIGGER_NETDEV_LINK_1000) | ++ BIT(TRIGGER_NETDEV_TX) | ++ BIT(TRIGGER_NETDEV_RX), ++ .val = VEND1_LED_REG_A_EVENT_ON_FE_GE_BLINK_ACT ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_2500) | ++ BIT(TRIGGER_NETDEV_LINK_5000) | ++ BIT(TRIGGER_NETDEV_LINK_10000) | ++ BIT(TRIGGER_NETDEV_TX) | ++ BIT(TRIGGER_NETDEV_RX), ++ .val = VEND1_LED_REG_A_EVENT_ON_NG_BLINK_ACT ++ } ++}; ++ ++static int aeon_firmware_boot(struct phy_device *phydev, const u8 *data, ++ size_t size) ++{ ++ int i, ret; ++ u16 val; ++ ++ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLB_REG_CPU_CTRL, ++ VEND1_GLB_CPU_CTRL_MASK, AEON_CPU_CTRL_FW_LOAD); ++ if (ret) ++ return ret; ++ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_FW_START_ADDR, ++ AEON_BOOT_ADDR); ++ if (ret) ++ return ret; ++ ++ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND1, ++ VEND1_GLB_REG_MDIO_INDIRECT_ADDRCMD, ++ 0x3ffc, 0xc000); ++ if (ret) ++ return ret; ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_VEND1, ++ VEND1_GLB_REG_MDIO_INDIRECT_STATUS); ++ if (val > 1) { ++ phydev_err(phydev, "wrong origin mdio_indirect_status: %x\n", val); ++ return -EINVAL; ++ } ++ ++ /* Firmware is always aligned to u16 */ ++ for (i = 0; i < size; i += 2) { ++ val = data[i + 1] << 8 | data[i]; ++ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, ++ VEND1_GLB_REG_MDIO_INDIRECT_LOAD, val); ++ if (ret) ++ return ret; ++ } ++ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, ++ VEND1_GLB_REG_CPU_RESET_ADDR_LO_BASEADDR, ++ lower_16_bits(AEON_CPU_BOOT_ADDR)); ++ if (ret) ++ return ret; ++ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, ++ VEND1_GLB_REG_CPU_RESET_ADDR_HI_BASEADDR, ++ upper_16_bits(AEON_CPU_BOOT_ADDR)); ++ if (ret) ++ return ret; ++ ++ return phy_modify_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLB_REG_CPU_CTRL, ++ VEND1_GLB_CPU_CTRL_MASK, AEON_CPU_CTRL_FW_START); ++} ++ ++static int aeon_firmware_load(struct phy_device *phydev) ++{ ++ struct device *dev = &phydev->mdio.dev; ++ const struct firmware *fw; ++ const char *fw_name; ++ int ret; ++ ++ ret = of_property_read_string(dev->of_node, "firmware-name", ++ &fw_name); ++ if (ret) ++ return ret; ++ ++ ret = request_firmware(&fw, fw_name, dev); ++ if (ret) { ++ phydev_err(phydev, "failed to find FW file %s (%d)\n", ++ fw_name, ret); ++ return ret; ++ } ++ ++ ret = aeon_firmware_boot(phydev, fw->data, fw->size); ++ ++ release_firmware(fw); ++ ++ return ret; ++} ++ ++static bool aeon_ipc_ready(u16 val, bool parity_status) ++{ ++ u16 status; ++ ++ if (FIELD_GET(AEON_IPC_STS_PARITY, val) != parity_status) ++ return false; ++ ++ status = val & AEON_IPC_STS_STATUS; ++ ++ return status != AEON_IPC_STS_STATUS_RCVD && ++ status != AEON_IPC_STS_STATUS_PROCESS && ++ status != AEON_IPC_STS_STATUS_BUSY; ++} ++ ++static int aeon_ipc_wait_cmd(struct phy_device *phydev, bool parity_status) ++{ ++ u16 val; ++ ++ /* Exit condition logic: ++ * - Wait for parity bit equal ++ * - Wait for status success, error OR ready ++ */ ++ return phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, VEND1_IPC_STS, val, ++ aeon_ipc_ready(val, parity_status), ++ AEON_IPC_DELAY, AEON_IPC_TIMEOUT, false); ++} ++ ++static int aeon_ipc_send_cmd(struct phy_device *phydev, ++ struct as21xxx_priv *priv, ++ u16 cmd, u16 *ret_sts) ++{ ++ bool curr_parity; ++ int ret; ++ ++ /* The IPC sync by using a single parity bit. ++ * Each CMD have alternately this bit set or clear ++ * to understand correct flow and packet order. ++ */ ++ curr_parity = priv->parity_status; ++ if (priv->parity_status) ++ cmd |= AEON_IPC_CMD_PARITY; ++ ++ /* Always update parity for next packet */ ++ priv->parity_status = !priv->parity_status; ++ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_IPC_CMD, cmd); ++ if (ret) ++ return ret; ++ ++ /* Wait for packet to be processed */ ++ usleep_range(AEON_IPC_DELAY, AEON_IPC_DELAY + 5000); ++ ++ /* With no ret_sts, ignore waiting for packet completion ++ * (ipc parity bit sync) ++ */ ++ if (!ret_sts) ++ return 0; ++ ++ ret = aeon_ipc_wait_cmd(phydev, curr_parity); ++ if (ret) ++ return ret; ++ ++ ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_IPC_STS); ++ if (ret < 0) ++ return ret; ++ ++ *ret_sts = ret; ++ if ((*ret_sts & AEON_IPC_STS_STATUS) != AEON_IPC_STS_STATUS_SUCCESS) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++/* If data is NULL, return 0 or negative error. ++ * If data not NULL, return number of Bytes received from IPC or ++ * a negative error. ++ */ ++static int aeon_ipc_send_msg(struct phy_device *phydev, ++ u16 opcode, u16 *data, unsigned int data_len, ++ u16 *ret_data) ++{ ++ struct as21xxx_priv *priv = phydev->priv; ++ unsigned int ret_size; ++ u16 cmd, ret_sts; ++ int ret; ++ int i; ++ ++ /* IPC have a max of 8 register to transfer data, ++ * make sure we never exceed this. ++ */ ++ if (data_len > AEON_IPC_DATA_MAX) ++ return -EINVAL; ++ ++ for (i = 0; i < data_len / sizeof(u16); i++) ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_IPC_DATA(i), ++ data[i]); ++ ++ cmd = FIELD_PREP(AEON_IPC_CMD_SIZE, data_len) | ++ FIELD_PREP(AEON_IPC_CMD_OPCODE, opcode); ++ ++ mutex_lock(&priv->ipc_lock); ++ ++ ret = aeon_ipc_send_cmd(phydev, priv, cmd, &ret_sts); ++ if (ret) { ++ phydev_err(phydev, "failed to send ipc msg for %x: %d\n", ++ opcode, ret); ++ goto out; ++ } ++ ++ if (!data) ++ goto out; ++ ++ if ((ret_sts & AEON_IPC_STS_STATUS) == AEON_IPC_STS_STATUS_ERROR) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ /* Prevent IPC from stack smashing the kernel. ++ * We can't trust IPC to return a good value and we always ++ * preallocate space for 16 Bytes. ++ */ ++ ret_size = FIELD_GET(AEON_IPC_STS_SIZE, ret_sts); ++ if (ret_size > AEON_IPC_DATA_MAX) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ /* Read data from IPC data register for ret_size value from IPC */ ++ for (i = 0; i < DIV_ROUND_UP(ret_size, sizeof(u16)); i++) { ++ ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_IPC_DATA(i)); ++ if (ret < 0) ++ goto out; ++ ++ ret_data[i] = ret; ++ } ++ ++ ret = ret_size; ++ ++out: ++ mutex_unlock(&priv->ipc_lock); ++ ++ return ret; ++} ++ ++static int aeon_ipc_noop(struct phy_device *phydev, ++ struct as21xxx_priv *priv, u16 *ret_sts) ++{ ++ u16 cmd; ++ ++ cmd = FIELD_PREP(AEON_IPC_CMD_SIZE, 0) | ++ FIELD_PREP(AEON_IPC_CMD_OPCODE, IPC_CMD_NOOP); ++ ++ return aeon_ipc_send_cmd(phydev, priv, cmd, ret_sts); ++} ++ ++/* Logic to sync parity bit with IPC. ++ * We send 2 NOP cmd with same partity and we wait for IPC ++ * to handle the packet only for the second one. This way ++ * we make sure we are sync for every next cmd. ++ */ ++static int aeon_ipc_sync_parity(struct phy_device *phydev, ++ struct as21xxx_priv *priv) ++{ ++ u16 ret_sts; ++ int ret; ++ ++ mutex_lock(&priv->ipc_lock); ++ ++ /* Send NOP with no parity */ ++ aeon_ipc_noop(phydev, priv, NULL); ++ ++ /* Reset packet parity */ ++ priv->parity_status = false; ++ ++ /* Send second NOP with no parity */ ++ ret = aeon_ipc_noop(phydev, priv, &ret_sts); ++ ++ mutex_unlock(&priv->ipc_lock); ++ ++ /* We expect to return -EINVAL */ ++ if (ret != -EINVAL) ++ return ret; ++ ++ if ((ret_sts & AEON_IPC_STS_STATUS) != AEON_IPC_STS_STATUS_READY) { ++ phydev_err(phydev, "Invalid IPC status on sync parity: %x\n", ++ ret_sts); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int aeon_ipc_get_fw_version(struct phy_device *phydev) ++{ ++ u16 ret_data[AEON_IPC_DATA_NUM_REGISTERS], data[1]; ++ char fw_version[AEON_IPC_DATA_MAX + 1]; ++ int ret; ++ ++ data[0] = IPC_INFO_VERSION; ++ ++ ret = aeon_ipc_send_msg(phydev, IPC_CMD_INFO, data, ++ sizeof(data), ret_data); ++ if (ret < 0) ++ return ret; ++ ++ /* Make sure FW version is NULL terminated */ ++ memcpy(fw_version, ret_data, ret); ++ fw_version[ret] = '\0'; ++ ++ phydev_info(phydev, "Firmware Version: %s\n", fw_version); ++ ++ return 0; ++} ++ ++static int aeon_dpc_ra_enable(struct phy_device *phydev) ++{ ++ u16 data[2]; ++ ++ data[0] = IPC_CFG_PARAM_DIRECT; ++ data[1] = IPC_CFG_PARAM_DIRECT_DPC_RA; ++ ++ return aeon_ipc_send_msg(phydev, IPC_CMD_CFG_PARAM, data, ++ sizeof(data), NULL); ++} ++ ++static int as21xxx_probe(struct phy_device *phydev) ++{ ++ struct as21xxx_priv *priv; ++ int ret; ++ ++ priv = devm_kzalloc(&phydev->mdio.dev, ++ sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ phydev->priv = priv; ++ ++ ret = devm_mutex_init(&phydev->mdio.dev, ++ &priv->ipc_lock); ++ if (ret) ++ return ret; ++ ++ ret = aeon_ipc_sync_parity(phydev, priv); ++ if (ret) ++ return ret; ++ ++ ret = aeon_ipc_get_fw_version(phydev); ++ if (ret) ++ return ret; ++ ++ /* Enable PTP clk if not already Enabled */ ++ ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_PTP_CLK, ++ VEND1_PTP_CLK_EN); ++ if (ret) ++ return ret; ++ ++ return aeon_dpc_ra_enable(phydev); ++} ++ ++static int as21xxx_read_link(struct phy_device *phydev, int *bmcr) ++{ ++ int status; ++ ++ /* Normal C22 BMCR report inconsistent data, use ++ * the mapped C22 in C45 to have more consistent link info. ++ */ ++ *bmcr = phy_read_mmd(phydev, MDIO_MMD_AN, ++ AS21XXX_MDIO_AN_C22 + MII_BMCR); ++ if (*bmcr < 0) ++ return *bmcr; ++ ++ /* Autoneg is being started, therefore disregard current ++ * link status and report link as down. ++ */ ++ if (*bmcr & BMCR_ANRESTART) { ++ phydev->link = 0; ++ return 0; ++ } ++ ++ status = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1); ++ if (status < 0) ++ return status; ++ ++ phydev->link = !!(status & MDIO_STAT1_LSTATUS); ++ ++ return 0; ++} ++ ++static int as21xxx_read_c22_lpa(struct phy_device *phydev) ++{ ++ int lpagb; ++ ++ /* MII_STAT1000 are only filled in the mapped C22 ++ * in C45, use that to fill lpagb values and check. ++ */ ++ lpagb = phy_read_mmd(phydev, MDIO_MMD_AN, ++ AS21XXX_MDIO_AN_C22 + MII_STAT1000); ++ if (lpagb < 0) ++ return lpagb; ++ ++ if (lpagb & LPA_1000MSFAIL) { ++ int adv = phy_read_mmd(phydev, MDIO_MMD_AN, ++ AS21XXX_MDIO_AN_C22 + MII_CTRL1000); ++ ++ if (adv < 0) ++ return adv; ++ ++ if (adv & CTL1000_ENABLE_MASTER) ++ phydev_err(phydev, "Master/Slave resolution failed, maybe conflicting manual settings?\n"); ++ else ++ phydev_err(phydev, "Master/Slave resolution failed\n"); ++ return -ENOLINK; ++ } ++ ++ mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, ++ lpagb); ++ ++ return 0; ++} ++ ++static int as21xxx_read_status(struct phy_device *phydev) ++{ ++ int bmcr, old_link = phydev->link; ++ int ret; ++ ++ ret = as21xxx_read_link(phydev, &bmcr); ++ if (ret) ++ return ret; ++ ++ /* why bother the PHY if nothing can have changed */ ++ if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link) ++ return 0; ++ ++ phydev->speed = SPEED_UNKNOWN; ++ phydev->duplex = DUPLEX_UNKNOWN; ++ phydev->pause = 0; ++ phydev->asym_pause = 0; ++ ++ if (phydev->autoneg == AUTONEG_ENABLE) { ++ ret = genphy_c45_read_lpa(phydev); ++ if (ret) ++ return ret; ++ ++ ret = as21xxx_read_c22_lpa(phydev); ++ if (ret) ++ return ret; ++ ++ phy_resolve_aneg_linkmode(phydev); ++ } else { ++ int speed; ++ ++ linkmode_zero(phydev->lp_advertising); ++ ++ speed = phy_read_mmd(phydev, MDIO_MMD_VEND1, ++ VEND1_SPEED_STATUS); ++ if (speed < 0) ++ return speed; ++ ++ switch (speed & VEND1_SPEED_STATUS) { ++ case VEND1_SPEED_10000: ++ phydev->speed = SPEED_10000; ++ phydev->duplex = DUPLEX_FULL; ++ break; ++ case VEND1_SPEED_5000: ++ phydev->speed = SPEED_5000; ++ phydev->duplex = DUPLEX_FULL; ++ break; ++ case VEND1_SPEED_2500: ++ phydev->speed = SPEED_2500; ++ phydev->duplex = DUPLEX_FULL; ++ break; ++ case VEND1_SPEED_1000: ++ phydev->speed = SPEED_1000; ++ if (bmcr & BMCR_FULLDPLX) ++ phydev->duplex = DUPLEX_FULL; ++ else ++ phydev->duplex = DUPLEX_HALF; ++ break; ++ case VEND1_SPEED_100: ++ phydev->speed = SPEED_100; ++ phydev->duplex = DUPLEX_FULL; ++ break; ++ case VEND1_SPEED_10: ++ phydev->speed = SPEED_10; ++ phydev->duplex = DUPLEX_FULL; ++ break; ++ default: ++ return -EINVAL; ++ } ++ } ++ ++ return 0; ++} ++ ++static int as21xxx_led_brightness_set(struct phy_device *phydev, ++ u8 index, enum led_brightness value) ++{ ++ u16 val = VEND1_LED_REG_A_EVENT_OFF; ++ ++ if (index > AEON_MAX_LEDS) ++ return -EINVAL; ++ ++ if (value) ++ val = VEND1_LED_REG_A_EVENT_ON; ++ ++ return phy_modify_mmd(phydev, MDIO_MMD_VEND1, ++ VEND1_LED_REG(index), ++ VEND1_LED_REG_A_EVENT, ++ FIELD_PREP(VEND1_LED_REG_A_EVENT, val)); ++} ++ ++static int as21xxx_led_hw_is_supported(struct phy_device *phydev, u8 index, ++ unsigned long rules) ++{ ++ int i; ++ ++ if (index > AEON_MAX_LEDS) ++ return -EINVAL; ++ ++ for (i = 0; i < ARRAY_SIZE(as21xxx_led_supported_pattern); i++) ++ if (rules == as21xxx_led_supported_pattern[i].pattern) ++ return 0; ++ ++ return -EOPNOTSUPP; ++} ++ ++static int as21xxx_led_hw_control_get(struct phy_device *phydev, u8 index, ++ unsigned long *rules) ++{ ++ int i, val; ++ ++ if (index > AEON_MAX_LEDS) ++ return -EINVAL; ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_LED_REG(index)); ++ if (val < 0) ++ return val; ++ ++ val = FIELD_GET(VEND1_LED_REG_A_EVENT, val); ++ for (i = 0; i < ARRAY_SIZE(as21xxx_led_supported_pattern); i++) ++ if (val == as21xxx_led_supported_pattern[i].val) { ++ *rules = as21xxx_led_supported_pattern[i].pattern; ++ return 0; ++ } ++ ++ /* Should be impossible */ ++ return -EINVAL; ++} ++ ++static int as21xxx_led_hw_control_set(struct phy_device *phydev, u8 index, ++ unsigned long rules) ++{ ++ u16 val = 0; ++ int i; ++ ++ if (index > AEON_MAX_LEDS) ++ return -EINVAL; ++ ++ for (i = 0; i < ARRAY_SIZE(as21xxx_led_supported_pattern); i++) ++ if (rules == as21xxx_led_supported_pattern[i].pattern) { ++ val = as21xxx_led_supported_pattern[i].val; ++ break; ++ } ++ ++ return phy_modify_mmd(phydev, MDIO_MMD_VEND1, ++ VEND1_LED_REG(index), ++ VEND1_LED_REG_A_EVENT, ++ FIELD_PREP(VEND1_LED_REG_A_EVENT, val)); ++} ++ ++static int as21xxx_led_polarity_set(struct phy_device *phydev, int index, ++ unsigned long modes) ++{ ++ bool led_active_low = false; ++ u16 mask, val = 0; ++ u32 mode; ++ ++ if (index > AEON_MAX_LEDS) ++ return -EINVAL; ++ ++ for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) { ++ switch (mode) { ++ case PHY_LED_ACTIVE_LOW: ++ led_active_low = true; ++ break; ++ case PHY_LED_ACTIVE_HIGH: /* default mode */ ++ led_active_low = false; ++ break; ++ default: ++ return -EINVAL; ++ } ++ } ++ ++ mask = VEND1_GLB_CPU_CTRL_LED_POLARITY(index); ++ if (led_active_low) ++ val = VEND1_GLB_CPU_CTRL_LED_POLARITY(index); ++ ++ return phy_modify_mmd(phydev, MDIO_MMD_VEND1, ++ VEND1_GLB_REG_CPU_CTRL, ++ mask, val); ++} ++ ++static int as21xxx_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) ++{ ++ struct as21xxx_priv *priv; ++ u16 ret_sts; ++ u32 phy_id; ++ int ret; ++ ++ /* Skip PHY that are not AS21xxx or already have firmware loaded */ ++ if (phydev->c45_ids.device_ids[MDIO_MMD_PCS] != PHY_ID_AS21XXX) ++ return genphy_match_phy_device(phydev, (struct phy_driver *)phydrv); ++ ++ /* Read PHY ID to handle firmware just loaded */ ++ ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MII_PHYSID1); ++ if (ret < 0) ++ return ret; ++ phy_id = ret << 16; ++ ++ ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MII_PHYSID2); ++ if (ret < 0) ++ return ret; ++ phy_id |= ret; ++ ++ /* With PHY ID not the generic AS21xxx one assume ++ * the firmware just loaded ++ */ ++ if (phy_id != PHY_ID_AS21XXX) ++ return phy_id == phydrv->phy_id; ++ ++ /* Allocate temp priv and load the firmware */ ++ priv = kzalloc(sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ mutex_init(&priv->ipc_lock); ++ ++ ret = aeon_firmware_load(phydev); ++ if (ret) ++ goto out; ++ ++ /* Sync parity... */ ++ ret = aeon_ipc_sync_parity(phydev, priv); ++ if (ret) ++ goto out; ++ ++ /* ...and send a third NOOP cmd to wait for firmware finish loading */ ++ ret = aeon_ipc_noop(phydev, priv, &ret_sts); ++ if (ret) ++ goto out; ++ ++out: ++ mutex_destroy(&priv->ipc_lock); ++ kfree(priv); ++ ++ /* Return can either be 0 or a negative error code. ++ * Returning 0 here means THIS is NOT a suitable PHY. ++ * ++ * For the specific case of the generic Aeonsemi PHY ID that ++ * needs the firmware the be loaded first to have a correct PHY ID, ++ * this is OK as a matching PHY ID will be found right after. ++ * This relies on the driver probe order where the first PHY driver ++ * probed is the generic one. ++ */ ++ return ret; ++} ++ ++static struct phy_driver as21xxx_drivers[] = { ++ { ++ /* PHY expose in C45 as 0x7500 0x9410 ++ * before firmware is loaded. ++ * This driver entry must be attempted first to load ++ * the firmware and thus update the ID registers. ++ */ ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21XXX), ++ .name = "Aeonsemi AS21xxx", ++ .match_phy_device = as21xxx_match_phy_device, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21011JB1), ++ .name = "Aeonsemi AS21011JB1", ++ .probe = as21xxx_probe, ++ .match_phy_device = as21xxx_match_phy_device, ++ .read_status = as21xxx_read_status, ++ .led_brightness_set = as21xxx_led_brightness_set, ++ .led_hw_is_supported = as21xxx_led_hw_is_supported, ++ .led_hw_control_set = as21xxx_led_hw_control_set, ++ .led_hw_control_get = as21xxx_led_hw_control_get, ++ .led_polarity_set = as21xxx_led_polarity_set, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21011PB1), ++ .name = "Aeonsemi AS21011PB1", ++ .probe = as21xxx_probe, ++ .match_phy_device = as21xxx_match_phy_device, ++ .read_status = as21xxx_read_status, ++ .led_brightness_set = as21xxx_led_brightness_set, ++ .led_hw_is_supported = as21xxx_led_hw_is_supported, ++ .led_hw_control_set = as21xxx_led_hw_control_set, ++ .led_hw_control_get = as21xxx_led_hw_control_get, ++ .led_polarity_set = as21xxx_led_polarity_set, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21010PB1), ++ .name = "Aeonsemi AS21010PB1", ++ .probe = as21xxx_probe, ++ .match_phy_device = as21xxx_match_phy_device, ++ .read_status = as21xxx_read_status, ++ .led_brightness_set = as21xxx_led_brightness_set, ++ .led_hw_is_supported = as21xxx_led_hw_is_supported, ++ .led_hw_control_set = as21xxx_led_hw_control_set, ++ .led_hw_control_get = as21xxx_led_hw_control_get, ++ .led_polarity_set = as21xxx_led_polarity_set, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21010JB1), ++ .name = "Aeonsemi AS21010JB1", ++ .probe = as21xxx_probe, ++ .match_phy_device = as21xxx_match_phy_device, ++ .read_status = as21xxx_read_status, ++ .led_brightness_set = as21xxx_led_brightness_set, ++ .led_hw_is_supported = as21xxx_led_hw_is_supported, ++ .led_hw_control_set = as21xxx_led_hw_control_set, ++ .led_hw_control_get = as21xxx_led_hw_control_get, ++ .led_polarity_set = as21xxx_led_polarity_set, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21210PB1), ++ .name = "Aeonsemi AS21210PB1", ++ .probe = as21xxx_probe, ++ .match_phy_device = as21xxx_match_phy_device, ++ .read_status = as21xxx_read_status, ++ .led_brightness_set = as21xxx_led_brightness_set, ++ .led_hw_is_supported = as21xxx_led_hw_is_supported, ++ .led_hw_control_set = as21xxx_led_hw_control_set, ++ .led_hw_control_get = as21xxx_led_hw_control_get, ++ .led_polarity_set = as21xxx_led_polarity_set, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21510JB1), ++ .name = "Aeonsemi AS21510JB1", ++ .probe = as21xxx_probe, ++ .match_phy_device = as21xxx_match_phy_device, ++ .read_status = as21xxx_read_status, ++ .led_brightness_set = as21xxx_led_brightness_set, ++ .led_hw_is_supported = as21xxx_led_hw_is_supported, ++ .led_hw_control_set = as21xxx_led_hw_control_set, ++ .led_hw_control_get = as21xxx_led_hw_control_get, ++ .led_polarity_set = as21xxx_led_polarity_set, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21510PB1), ++ .name = "Aeonsemi AS21510PB1", ++ .probe = as21xxx_probe, ++ .match_phy_device = as21xxx_match_phy_device, ++ .read_status = as21xxx_read_status, ++ .led_brightness_set = as21xxx_led_brightness_set, ++ .led_hw_is_supported = as21xxx_led_hw_is_supported, ++ .led_hw_control_set = as21xxx_led_hw_control_set, ++ .led_hw_control_get = as21xxx_led_hw_control_get, ++ .led_polarity_set = as21xxx_led_polarity_set, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21511JB1), ++ .name = "Aeonsemi AS21511JB1", ++ .probe = as21xxx_probe, ++ .match_phy_device = as21xxx_match_phy_device, ++ .read_status = as21xxx_read_status, ++ .led_brightness_set = as21xxx_led_brightness_set, ++ .led_hw_is_supported = as21xxx_led_hw_is_supported, ++ .led_hw_control_set = as21xxx_led_hw_control_set, ++ .led_hw_control_get = as21xxx_led_hw_control_get, ++ .led_polarity_set = as21xxx_led_polarity_set, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21210JB1), ++ .name = "Aeonsemi AS21210JB1", ++ .probe = as21xxx_probe, ++ .match_phy_device = as21xxx_match_phy_device, ++ .read_status = as21xxx_read_status, ++ .led_brightness_set = as21xxx_led_brightness_set, ++ .led_hw_is_supported = as21xxx_led_hw_is_supported, ++ .led_hw_control_set = as21xxx_led_hw_control_set, ++ .led_hw_control_get = as21xxx_led_hw_control_get, ++ .led_polarity_set = as21xxx_led_polarity_set, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21511PB1), ++ .name = "Aeonsemi AS21511PB1", ++ .probe = as21xxx_probe, ++ .match_phy_device = as21xxx_match_phy_device, ++ .read_status = as21xxx_read_status, ++ .led_brightness_set = as21xxx_led_brightness_set, ++ .led_hw_is_supported = as21xxx_led_hw_is_supported, ++ .led_hw_control_set = as21xxx_led_hw_control_set, ++ .led_hw_control_get = as21xxx_led_hw_control_get, ++ .led_polarity_set = as21xxx_led_polarity_set, ++ }, ++}; ++module_phy_driver(as21xxx_drivers); ++ ++static struct mdio_device_id __maybe_unused as21xxx_tbl[] = { ++ { PHY_ID_MATCH_VENDOR(PHY_VENDOR_AEONSEMI) }, ++ { } ++}; ++MODULE_DEVICE_TABLE(mdio, as21xxx_tbl); ++ ++MODULE_DESCRIPTION("Aeonsemi AS21xxx PHY driver"); ++MODULE_AUTHOR("Christian Marangi "); ++MODULE_LICENSE("GPL"); diff --git a/lede/target/linux/generic/backport-6.6/798-v6.10-net-phy-air_en8811h-Add-the-Airoha-EN8811H-PHY-drive.patch b/lede/target/linux/generic/backport-6.6/798-v6.10-net-phy-air_en8811h-Add-the-Airoha-EN8811H-PHY-drive.patch index 5b627cf449..ecea0e987d 100644 --- a/lede/target/linux/generic/backport-6.6/798-v6.10-net-phy-air_en8811h-Add-the-Airoha-EN8811H-PHY-drive.patch +++ b/lede/target/linux/generic/backport-6.6/798-v6.10-net-phy-air_en8811h-Add-the-Airoha-EN8811H-PHY-drive.patch @@ -27,9 +27,9 @@ Signed-off-by: Jakub Kicinski --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig -@@ -68,6 +68,11 @@ config SFP - - comment "MII PHY device drivers" +@@ -80,6 +80,11 @@ config AS21XXX_PHY + AS21210PB1 that all register with the PHY ID 0x7500 0x7500 + before the firmware is loaded. +config AIR_EN8811H_PHY + tristate "Airoha EN8811H 2.5 Gigabit PHY" @@ -48,7 +48,7 @@ Signed-off-by: Jakub Kicinski +obj-$(CONFIG_AIR_EN8811H_PHY) += air_en8811h.o obj-$(CONFIG_AMD_PHY) += amd.o obj-$(CONFIG_AQUANTIA_PHY) += aquantia/ - obj-$(CONFIG_AX88796B_PHY) += ax88796b.o + obj-$(CONFIG_AS21XXX_PHY) += as21xxx.o --- /dev/null +++ b/drivers/net/phy/air_en8811h.c @@ -0,0 +1,1086 @@ diff --git a/lede/target/linux/generic/backport-6.6/832-v6.7-net-phy-amd-Support-the-Altima-AMI101L.patch b/lede/target/linux/generic/backport-6.6/832-v6.7-net-phy-amd-Support-the-Altima-AMI101L.patch index 07287206f6..1ed1008ee5 100644 --- a/lede/target/linux/generic/backport-6.6/832-v6.7-net-phy-amd-Support-the-Altima-AMI101L.patch +++ b/lede/target/linux/generic/backport-6.6/832-v6.7-net-phy-amd-Support-the-Altima-AMI101L.patch @@ -16,7 +16,7 @@ Signed-off-by: Linus Walleij --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig -@@ -74,9 +74,9 @@ config AIR_EN8811H_PHY +@@ -86,9 +86,9 @@ config AIR_EN8811H_PHY Currently supports the Airoha EN8811H PHY. config AMD_PHY diff --git a/lede/target/linux/generic/backport-6.6/835-v6.9-net-phy-add-support-for-PHY-LEDs-polarity-modes.patch b/lede/target/linux/generic/backport-6.6/835-v6.9-net-phy-add-support-for-PHY-LEDs-polarity-modes.patch index 1fe0e3f890..8e39c56fd2 100644 --- a/lede/target/linux/generic/backport-6.6/835-v6.9-net-phy-add-support-for-PHY-LEDs-polarity-modes.patch +++ b/lede/target/linux/generic/backport-6.6/835-v6.9-net-phy-add-support-for-PHY-LEDs-polarity-modes.patch @@ -28,7 +28,7 @@ Signed-off-by: Jakub Kicinski --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c -@@ -3202,6 +3202,7 @@ static int of_phy_led(struct phy_device +@@ -3226,6 +3226,7 @@ static int of_phy_led(struct phy_device struct device *dev = &phydev->mdio.dev; struct led_init_data init_data = {}; struct led_classdev *cdev; @@ -36,7 +36,7 @@ Signed-off-by: Jakub Kicinski struct phy_led *phyled; u32 index; int err; -@@ -3219,6 +3220,21 @@ static int of_phy_led(struct phy_device +@@ -3243,6 +3244,21 @@ static int of_phy_led(struct phy_device if (index > U8_MAX) return -EINVAL; @@ -76,7 +76,7 @@ Signed-off-by: Jakub Kicinski /** * struct phy_driver - Driver structure for a particular PHY type * -@@ -1146,6 +1155,19 @@ struct phy_driver { +@@ -1144,6 +1153,19 @@ struct phy_driver { int (*led_hw_control_get)(struct phy_device *dev, u8 index, unsigned long *rules); diff --git a/lede/target/linux/generic/backport-6.6/841-v6.13-net-phy-support-active-high-property-for-PHY-LEDs.patch b/lede/target/linux/generic/backport-6.6/841-v6.13-net-phy-support-active-high-property-for-PHY-LEDs.patch index 729a315623..c63d48464c 100644 --- a/lede/target/linux/generic/backport-6.6/841-v6.13-net-phy-support-active-high-property-for-PHY-LEDs.patch +++ b/lede/target/linux/generic/backport-6.6/841-v6.13-net-phy-support-active-high-property-for-PHY-LEDs.patch @@ -19,7 +19,7 @@ Signed-off-by: Paolo Abeni --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c -@@ -3220,11 +3220,17 @@ static int of_phy_led(struct phy_device +@@ -3244,11 +3244,17 @@ static int of_phy_led(struct phy_device if (index > U8_MAX) return -EINVAL; diff --git a/lede/target/linux/generic/backport-6.6/895-01-v6.8-net-phy-add-possible-interfaces.patch b/lede/target/linux/generic/backport-6.6/895-01-v6.8-net-phy-add-possible-interfaces.patch index b86dbea898..43e2c92ef0 100644 --- a/lede/target/linux/generic/backport-6.6/895-01-v6.8-net-phy-add-possible-interfaces.patch +++ b/lede/target/linux/generic/backport-6.6/895-01-v6.8-net-phy-add-possible-interfaces.patch @@ -30,7 +30,7 @@ Signed-off-by: Jakub Kicinski --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c -@@ -1247,6 +1247,8 @@ int phy_init_hw(struct phy_device *phyde +@@ -1269,6 +1269,8 @@ int phy_init_hw(struct phy_device *phyde if (ret < 0) return ret; diff --git a/lede/target/linux/generic/hack-6.12/700-swconfig_switch_drivers.patch b/lede/target/linux/generic/hack-6.12/700-swconfig_switch_drivers.patch index 4591a42f78..9b1f53af3e 100644 --- a/lede/target/linux/generic/hack-6.12/700-swconfig_switch_drivers.patch +++ b/lede/target/linux/generic/hack-6.12/700-swconfig_switch_drivers.patch @@ -92,7 +92,7 @@ Signed-off-by: Felix Fietkau + comment "MII PHY device drivers" - config AIR_EN8811H_PHY + config AS21XXX_PHY --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -27,6 +27,21 @@ libphy-$(CONFIG_OPEN_ALLIANCE_HELPERS) + diff --git a/lede/target/linux/generic/hack-6.6/700-swconfig_switch_drivers.patch b/lede/target/linux/generic/hack-6.6/700-swconfig_switch_drivers.patch index 4428ebbb5a..59cc16cb75 100644 --- a/lede/target/linux/generic/hack-6.6/700-swconfig_switch_drivers.patch +++ b/lede/target/linux/generic/hack-6.6/700-swconfig_switch_drivers.patch @@ -92,7 +92,7 @@ Signed-off-by: Felix Fietkau + comment "MII PHY device drivers" - config AIR_EN8811H_PHY + config AS21XXX_PHY --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -26,6 +26,21 @@ libphy-$(CONFIG_LED_TRIGGER_PHY) += phy_ diff --git a/lede/target/linux/generic/pending-6.12/703-phy-add-detach-callback-to-struct-phy_driver.patch b/lede/target/linux/generic/pending-6.12/703-phy-add-detach-callback-to-struct-phy_driver.patch index b8d20d8610..ed6c9070b2 100644 --- a/lede/target/linux/generic/pending-6.12/703-phy-add-detach-callback-to-struct-phy_driver.patch +++ b/lede/target/linux/generic/pending-6.12/703-phy-add-detach-callback-to-struct-phy_driver.patch @@ -11,7 +11,7 @@ Signed-off-by: Gabor Juhos --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c -@@ -2015,6 +2015,9 @@ void phy_detach(struct phy_device *phyde +@@ -2037,6 +2037,9 @@ void phy_detach(struct phy_device *phyde phydev->devlink = NULL; } diff --git a/lede/target/linux/generic/pending-6.12/720-05-net-phy-realtek-detect-early-version-of-RTL8221B.patch b/lede/target/linux/generic/pending-6.12/720-05-net-phy-realtek-detect-early-version-of-RTL8221B.patch index 0918794d87..67e81a38a7 100644 --- a/lede/target/linux/generic/pending-6.12/720-05-net-phy-realtek-detect-early-version-of-RTL8221B.patch +++ b/lede/target/linux/generic/pending-6.12/720-05-net-phy-realtek-detect-early-version-of-RTL8221B.patch @@ -49,4 +49,4 @@ Signed-off-by: Mieczyslaw Nalewaj + } } - static int rtl8221b_match_phy_device(struct phy_device *phydev) + static int rtl8221b_match_phy_device(struct phy_device *phydev, diff --git a/lede/target/linux/generic/pending-6.12/741-net-phy-broadcom-update-dependency-condition.patch b/lede/target/linux/generic/pending-6.12/741-net-phy-broadcom-update-dependency-condition.patch index f5621e34ee..7a52bd5521 100644 --- a/lede/target/linux/generic/pending-6.12/741-net-phy-broadcom-update-dependency-condition.patch +++ b/lede/target/linux/generic/pending-6.12/741-net-phy-broadcom-update-dependency-condition.patch @@ -24,7 +24,7 @@ Signed-off-by: David Bauer --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig -@@ -139,7 +139,7 @@ config BROADCOM_PHY +@@ -151,7 +151,7 @@ config BROADCOM_PHY tristate "Broadcom 54XX PHYs" select BCM_NET_PHYLIB select BCM_NET_PHYPTP if NETWORK_PHY_TIMESTAMPING diff --git a/lede/target/linux/generic/pending-6.6/703-phy-add-detach-callback-to-struct-phy_driver.patch b/lede/target/linux/generic/pending-6.6/703-phy-add-detach-callback-to-struct-phy_driver.patch index a556a9cc40..efa76572f8 100644 --- a/lede/target/linux/generic/pending-6.6/703-phy-add-detach-callback-to-struct-phy_driver.patch +++ b/lede/target/linux/generic/pending-6.6/703-phy-add-detach-callback-to-struct-phy_driver.patch @@ -11,9 +11,9 @@ Signed-off-by: Gabor Juhos --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c -@@ -1910,6 +1910,9 @@ void phy_detach(struct phy_device *phyde - if (phydev->devlink) - device_link_del(phydev->devlink); +@@ -1934,6 +1934,9 @@ void phy_detach(struct phy_device *phyde + phydev->devlink = NULL; + } + if (phydev->drv && phydev->drv->detach) + phydev->drv->detach(phydev); @@ -23,7 +23,7 @@ Signed-off-by: Gabor Juhos sysfs_remove_link(&dev->dev.kobj, "phydev"); --- a/include/linux/phy.h +++ b/include/linux/phy.h -@@ -980,6 +980,12 @@ struct phy_driver { +@@ -977,6 +977,12 @@ struct phy_driver { /** @handle_interrupt: Override default interrupt handling */ irqreturn_t (*handle_interrupt)(struct phy_device *phydev); diff --git a/lede/target/linux/generic/pending-6.6/720-05-net-phy-realtek-detect-early-version-of-RTL8221B.patch b/lede/target/linux/generic/pending-6.6/720-05-net-phy-realtek-detect-early-version-of-RTL8221B.patch index ca08dcafdf..49c172a137 100644 --- a/lede/target/linux/generic/pending-6.6/720-05-net-phy-realtek-detect-early-version-of-RTL8221B.patch +++ b/lede/target/linux/generic/pending-6.6/720-05-net-phy-realtek-detect-early-version-of-RTL8221B.patch @@ -49,4 +49,4 @@ Signed-off-by: Mieczyslaw Nalewaj + } } - static int rtl8221b_match_phy_device(struct phy_device *phydev) + static int rtl8221b_match_phy_device(struct phy_device *phydev, diff --git a/lede/target/linux/generic/pending-6.6/741-net-phy-broadcom-update-dependency-condition.patch b/lede/target/linux/generic/pending-6.6/741-net-phy-broadcom-update-dependency-condition.patch index 80b69920e9..449b27e617 100644 --- a/lede/target/linux/generic/pending-6.6/741-net-phy-broadcom-update-dependency-condition.patch +++ b/lede/target/linux/generic/pending-6.6/741-net-phy-broadcom-update-dependency-condition.patch @@ -24,7 +24,7 @@ Signed-off-by: David Bauer --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig -@@ -113,7 +113,7 @@ config BROADCOM_PHY +@@ -125,7 +125,7 @@ config BROADCOM_PHY tristate "Broadcom 54XX PHYs" select BCM_NET_PHYLIB select BCM_NET_PHYPTP if NETWORK_PHY_TIMESTAMPING diff --git a/lede/target/linux/mediatek/patches-6.12/500-gsw-rtl8367s-mt7622-support.patch b/lede/target/linux/mediatek/patches-6.12/500-gsw-rtl8367s-mt7622-support.patch index 72a1464966..8f911f6fd8 100644 --- a/lede/target/linux/mediatek/patches-6.12/500-gsw-rtl8367s-mt7622-support.patch +++ b/lede/target/linux/mediatek/patches-6.12/500-gsw-rtl8367s-mt7622-support.patch @@ -1,6 +1,6 @@ --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig -@@ -431,6 +431,12 @@ config ROCKCHIP_PHY +@@ -443,6 +443,12 @@ config ROCKCHIP_PHY help Currently supports the integrated Ethernet PHY. @@ -15,7 +15,7 @@ select CRC16 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile -@@ -113,6 +113,7 @@ obj-$(CONFIG_REALTEK_PHY) += realtek/ +@@ -114,6 +114,7 @@ obj-$(CONFIG_REALTEK_PHY) += realtek/ obj-y += rtl8261n/ obj-$(CONFIG_RENESAS_PHY) += uPD60620.o obj-$(CONFIG_ROCKCHIP_PHY) += rockchip.o diff --git a/lede/target/linux/mediatek/patches-6.12/734-net-phy-add-Airoha-EN8801SC-PHY.patch b/lede/target/linux/mediatek/patches-6.12/734-net-phy-add-Airoha-EN8801SC-PHY.patch index 079351b7a2..4ebaffd1dd 100644 --- a/lede/target/linux/mediatek/patches-6.12/734-net-phy-add-Airoha-EN8801SC-PHY.patch +++ b/lede/target/linux/mediatek/patches-6.12/734-net-phy-add-Airoha-EN8801SC-PHY.patch @@ -14,9 +14,9 @@ Signed-off-by: Robert Marko --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig -@@ -153,6 +153,11 @@ endif # RTL8366_SMI - - comment "MII PHY device drivers" +@@ -165,6 +165,11 @@ config AS21XXX_PHY + AS21210PB1 that all register with the PHY ID 0x7500 0x7500 + before the firmware is loaded. +config AIROHA_EN8801SC_PHY + tristate "Airoha EN8801SC Gigabit PHY" diff --git a/lede/target/linux/mediatek/patches-6.12/737-net-dsa-add-Airoha-AN8855.patch b/lede/target/linux/mediatek/patches-6.12/737-net-dsa-add-Airoha-AN8855.patch index b0adf04a5b..d31215ec27 100644 --- a/lede/target/linux/mediatek/patches-6.12/737-net-dsa-add-Airoha-AN8855.patch +++ b/lede/target/linux/mediatek/patches-6.12/737-net-dsa-add-Airoha-AN8855.patch @@ -254,7 +254,7 @@ Christian Marangi (9): obj-$(CONFIG_MDIO_BCM_UNIMAC) += mdio-bcm-unimac.o --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig -@@ -158,6 +158,11 @@ config AIROHA_EN8801SC_PHY +@@ -170,6 +170,11 @@ config AIROHA_EN8801SC_PHY help Currently supports the Airoha EN8801SC PHY. diff --git a/lede/target/linux/mediatek/patches-6.12/739-net-add-negotiation-of-in-band-capabilities.patch b/lede/target/linux/mediatek/patches-6.12/739-net-add-negotiation-of-in-band-capabilities.patch index 632aad0ed2..12638978b3 100644 --- a/lede/target/linux/mediatek/patches-6.12/739-net-add-negotiation-of-in-band-capabilities.patch +++ b/lede/target/linux/mediatek/patches-6.12/739-net-add-negotiation-of-in-band-capabilities.patch @@ -880,7 +880,7 @@ publishing the in-band capabilities from the BCM84881 PHY driver. * @get_rate_matching: Get the supported type of rate matching for a * particular phy interface. This is used by phy consumers to determine * whether to advertise lower-speed modes for that interface. It is -@@ -1839,6 +1870,9 @@ int phy_config_aneg(struct phy_device *p +@@ -1840,6 +1871,9 @@ int phy_config_aneg(struct phy_device *p int _phy_start_aneg(struct phy_device *phydev); int phy_start_aneg(struct phy_device *phydev); int phy_aneg_done(struct phy_device *phydev); diff --git a/mihomo/go.mod b/mihomo/go.mod index 9ce928ce6c..86e34b4949 100644 --- a/mihomo/go.mod +++ b/mihomo/go.mod @@ -24,12 +24,12 @@ require ( github.com/metacubex/fswatch v0.1.1 github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 github.com/metacubex/kcp-go v0.0.0-20250923001605-1ba6f691c45b - github.com/metacubex/quic-go v0.54.1-0.20250926001022-e2a3ce003b3a + github.com/metacubex/quic-go v0.55.1-0.20251004050223-450bd9e32033 github.com/metacubex/randv2 v0.2.0 github.com/metacubex/restls-client-go v0.1.7 github.com/metacubex/sing v0.5.6 github.com/metacubex/sing-mux v0.3.4 - github.com/metacubex/sing-quic v0.0.0-20250909002258-06122df8f231 + github.com/metacubex/sing-quic v0.0.0-20251004051927-c45ee18473bb github.com/metacubex/sing-shadowsocks v0.2.12 github.com/metacubex/sing-shadowsocks2 v0.2.7 github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2 @@ -105,7 +105,6 @@ require ( github.com/vishvananda/netns v0.0.4 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect - go.uber.org/mock v0.4.0 // indirect golang.org/x/mod v0.20.0 // indirect golang.org/x/text v0.22.0 // indirect golang.org/x/time v0.7.0 // indirect diff --git a/mihomo/go.sum b/mihomo/go.sum index 7a00f21136..d22b475cdc 100644 --- a/mihomo/go.sum +++ b/mihomo/go.sum @@ -112,8 +112,8 @@ github.com/metacubex/kcp-go v0.0.0-20250923001605-1ba6f691c45b h1:z7JLKjugnQ1qvD github.com/metacubex/kcp-go v0.0.0-20250923001605-1ba6f691c45b/go.mod h1:HIJZW4QMhbBqXuqC1ly6Hn0TEYT2SzRw58ns1yGhXTs= github.com/metacubex/nftables v0.0.0-20250503052935-30a69ab87793 h1:1Qpuy+sU3DmyX9HwI+CrBT/oLNJngvBorR2RbajJcqo= github.com/metacubex/nftables v0.0.0-20250503052935-30a69ab87793/go.mod h1:RjRNb4G52yAgfR+Oe/kp9G4PJJ97Fnj89eY1BFO3YyA= -github.com/metacubex/quic-go v0.54.1-0.20250926001022-e2a3ce003b3a h1:l7BWjOifmqM2zMi+AMrgIx1z4KJt0oY/6cHW11kA9IQ= -github.com/metacubex/quic-go v0.54.1-0.20250926001022-e2a3ce003b3a/go.mod h1:1lktQFtCD17FZliVypbrDHwbsFSsmz2xz2TRXydvB5c= +github.com/metacubex/quic-go v0.55.1-0.20251004050223-450bd9e32033 h1:LEzvR5AmHEatqE6IWgMBUJHnaiz9VJfZeDGOiHFuWZU= +github.com/metacubex/quic-go v0.55.1-0.20251004050223-450bd9e32033/go.mod h1:1lktQFtCD17FZliVypbrDHwbsFSsmz2xz2TRXydvB5c= github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs= github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY= github.com/metacubex/restls-client-go v0.1.7 h1:eCwiXCTQb5WJu9IlgYvDBA1OgrINv58dEe7hcN5H15k= @@ -123,8 +123,8 @@ github.com/metacubex/sing v0.5.6 h1:mEPDCadsCj3DB8gn+t/EtposlYuALEkExa/LUguw6/c= github.com/metacubex/sing v0.5.6/go.mod h1:ypf0mjwlZm0sKdQSY+yQvmsbWa0hNPtkeqyRMGgoN+w= github.com/metacubex/sing-mux v0.3.4 h1:tf4r27CIkzaxq9kBlAXQkgMXq2HPp5Mta60Kb4RCZF0= github.com/metacubex/sing-mux v0.3.4/go.mod h1:SEJfAuykNj/ozbPqngEYqyggwSr81+L7Nu09NRD5mh4= -github.com/metacubex/sing-quic v0.0.0-20250909002258-06122df8f231 h1:dGvo7UahC/gYBQNBoictr14baJzBjAKUAorP63QFFtg= -github.com/metacubex/sing-quic v0.0.0-20250909002258-06122df8f231/go.mod h1:B60FxaPHjR1SeQB0IiLrgwgvKsaoASfOWdiqhLjmMGA= +github.com/metacubex/sing-quic v0.0.0-20251004051927-c45ee18473bb h1:gxrJmnxuEAel+kh3V7ntqkHjURif0xKDu76nzr/BF5Y= +github.com/metacubex/sing-quic v0.0.0-20251004051927-c45ee18473bb/go.mod h1:JK4+PYUKps6pnlicKjsSUAjAcvIUjhorIjdNZGg930M= github.com/metacubex/sing-shadowsocks v0.2.12 h1:Wqzo8bYXrK5aWqxu/TjlTnYZzAKtKsaFQBdr6IHFaBE= github.com/metacubex/sing-shadowsocks v0.2.12/go.mod h1:2e5EIaw0rxKrm1YTRmiMnDulwbGxH9hAFlrwQLQMQkU= github.com/metacubex/sing-shadowsocks2 v0.2.7 h1:hSuuc0YpsfiqYqt1o+fP4m34BQz4e6wVj3PPBVhor3A= @@ -219,7 +219,6 @@ gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec/go.mod h1:BZ1RAo go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= -go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M= go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= diff --git a/mihomo/transport/hysteria/congestion/brutal.go b/mihomo/transport/hysteria/congestion/brutal.go index 601949dec2..737bc1fb46 100644 --- a/mihomo/transport/hysteria/congestion/brutal.go +++ b/mihomo/transport/hysteria/congestion/brutal.go @@ -2,6 +2,8 @@ package congestion import ( "github.com/metacubex/quic-go/congestion" + "github.com/metacubex/quic-go/monotime" + "time" ) @@ -47,11 +49,11 @@ func (b *BrutalSender) SetRTTStatsProvider(rttStats congestion.RTTStatsProvider) b.rttStats = rttStats } -func (b *BrutalSender) TimeUntilSend(bytesInFlight congestion.ByteCount) time.Time { +func (b *BrutalSender) TimeUntilSend(bytesInFlight congestion.ByteCount) monotime.Time { return b.pacer.TimeUntilSend() } -func (b *BrutalSender) HasPacingBudget(now time.Time) bool { +func (b *BrutalSender) HasPacingBudget(now monotime.Time) bool { return b.pacer.Budget(now) >= b.maxDatagramSize } @@ -67,13 +69,13 @@ func (b *BrutalSender) GetCongestionWindow() congestion.ByteCount { return congestion.ByteCount(float64(b.bps) * rtt.Seconds() * 1.5 / b.ackRate) } -func (b *BrutalSender) OnPacketSent(sentTime time.Time, bytesInFlight congestion.ByteCount, +func (b *BrutalSender) OnPacketSent(sentTime monotime.Time, bytesInFlight congestion.ByteCount, packetNumber congestion.PacketNumber, bytes congestion.ByteCount, isRetransmittable bool) { b.pacer.SentPacket(sentTime, bytes) } func (b *BrutalSender) OnPacketAcked(number congestion.PacketNumber, ackedBytes congestion.ByteCount, - priorInFlight congestion.ByteCount, eventTime time.Time) { + priorInFlight congestion.ByteCount, eventTime monotime.Time) { // Stub } @@ -82,8 +84,8 @@ func (b *BrutalSender) OnCongestionEvent(number congestion.PacketNumber, lostByt // Stub } -func (b *BrutalSender) OnCongestionEventEx(priorInFlight congestion.ByteCount, eventTime time.Time, ackedPackets []congestion.AckedPacketInfo, lostPackets []congestion.LostPacketInfo) { - currentTimestamp := eventTime.Unix() +func (b *BrutalSender) OnCongestionEventEx(priorInFlight congestion.ByteCount, eventTime monotime.Time, ackedPackets []congestion.AckedPacketInfo, lostPackets []congestion.LostPacketInfo) { + currentTimestamp := int64(eventTime) slot := currentTimestamp % pktInfoSlotCount if b.pktInfoSlots[slot].Timestamp == currentTimestamp { b.pktInfoSlots[slot].LossCount += uint64(len(lostPackets)) diff --git a/mihomo/transport/hysteria/congestion/pacer.go b/mihomo/transport/hysteria/congestion/pacer.go index 2dff53008d..83c9322810 100644 --- a/mihomo/transport/hysteria/congestion/pacer.go +++ b/mihomo/transport/hysteria/congestion/pacer.go @@ -2,6 +2,8 @@ package congestion import ( "github.com/metacubex/quic-go/congestion" + "github.com/metacubex/quic-go/monotime" + "math" "time" ) @@ -15,7 +17,7 @@ const ( type pacer struct { budgetAtLastSent congestion.ByteCount maxDatagramSize congestion.ByteCount - lastSentTime time.Time + lastSentTime monotime.Time getBandwidth func() congestion.ByteCount // in bytes/s } @@ -28,7 +30,7 @@ func newPacer(getBandwidth func() congestion.ByteCount) *pacer { return p } -func (p *pacer) SentPacket(sendTime time.Time, size congestion.ByteCount) { +func (p *pacer) SentPacket(sendTime monotime.Time, size congestion.ByteCount) { budget := p.Budget(sendTime) if size > budget { p.budgetAtLastSent = 0 @@ -38,7 +40,7 @@ func (p *pacer) SentPacket(sendTime time.Time, size congestion.ByteCount) { p.lastSentTime = sendTime } -func (p *pacer) Budget(now time.Time) congestion.ByteCount { +func (p *pacer) Budget(now monotime.Time) congestion.ByteCount { if p.lastSentTime.IsZero() { return p.maxBurstSize() } @@ -54,10 +56,10 @@ func (p *pacer) maxBurstSize() congestion.ByteCount { } // TimeUntilSend returns when the next packet should be sent. -// It returns the zero value of time.Time if a packet can be sent immediately. -func (p *pacer) TimeUntilSend() time.Time { +// It returns the zero value of monotime.Time if a packet can be sent immediately. +func (p *pacer) TimeUntilSend() monotime.Time { if p.budgetAtLastSent >= p.maxDatagramSize { - return time.Time{} + return monotime.Time(0) } return p.lastSentTime.Add(maxDuration( minPacingDelay, diff --git a/mihomo/transport/tuic/congestion/bandwidth_sampler.go b/mihomo/transport/tuic/congestion/bandwidth_sampler.go index e415fe7a6e..ed7ce0727b 100644 --- a/mihomo/transport/tuic/congestion/bandwidth_sampler.go +++ b/mihomo/transport/tuic/congestion/bandwidth_sampler.go @@ -5,6 +5,7 @@ import ( "time" "github.com/metacubex/quic-go/congestion" + "github.com/metacubex/quic-go/monotime" ) var ( @@ -36,7 +37,7 @@ type SendTimeState struct { type ConnectionStateOnSentPacket struct { packetNumber congestion.PacketNumber // Time at which the packet is sent. - sendTime time.Time + sendTime monotime.Time // Size of the packet. size congestion.ByteCount // The value of |totalBytesSentAtLastAckedPacket| at the time the @@ -44,10 +45,10 @@ type ConnectionStateOnSentPacket struct { totalBytesSentAtLastAckedPacket congestion.ByteCount // The value of |lastAckedPacketSentTime| at the time the packet was // sent. - lastAckedPacketSentTime time.Time + lastAckedPacketSentTime monotime.Time // The value of |lastAckedPacketAckTime| at the time the packet was // sent. - lastAckedPacketAckTime time.Time + lastAckedPacketAckTime monotime.Time // Send time states that are returned to the congestion controller when the // packet is acked or lost. sendTimeState SendTimeState @@ -166,9 +167,9 @@ type BandwidthSampler struct { totalBytesSentAtLastAckedPacket congestion.ByteCount // The time at which the last acknowledged packet was sent. Set to // QuicTime::Zero() if no valid timestamp is available. - lastAckedPacketSentTime time.Time + lastAckedPacketSentTime monotime.Time // The time at which the most recent packet was acknowledged. - lastAckedPacketAckTime time.Time + lastAckedPacketAckTime monotime.Time // The most recently sent packet. lastSendPacket congestion.PacketNumber // Indicates whether the bandwidth sampler is currently in an app-limited @@ -194,7 +195,7 @@ func NewBandwidthSampler() *BandwidthSampler { // packets are sent in order. The information about the packet will not be // released from the sampler until it the packet is either acknowledged or // declared lost. -func (s *BandwidthSampler) OnPacketSent(sentTime time.Time, lastSentPacket congestion.PacketNumber, sentBytes, bytesInFlight congestion.ByteCount, hasRetransmittableData bool) { +func (s *BandwidthSampler) OnPacketSent(sentTime monotime.Time, lastSentPacket congestion.PacketNumber, sentBytes, bytesInFlight congestion.ByteCount, hasRetransmittableData bool) { s.lastSendPacket = lastSentPacket if !hasRetransmittableData { @@ -224,7 +225,7 @@ func (s *BandwidthSampler) OnPacketSent(sentTime time.Time, lastSentPacket conge // OnPacketAcked Notifies the sampler that the |lastAckedPacket| is acknowledged. Returns a // bandwidth sample. If no bandwidth sample is available, // QuicBandwidth::Zero() is returned. -func (s *BandwidthSampler) OnPacketAcked(ackTime time.Time, lastAckedPacket congestion.PacketNumber) *BandwidthSample { +func (s *BandwidthSampler) OnPacketAcked(ackTime monotime.Time, lastAckedPacket congestion.PacketNumber) *BandwidthSample { sentPacketState := s.connectionStats.Get(lastAckedPacket) if sentPacketState == nil { return NewBandwidthSample() @@ -238,7 +239,7 @@ func (s *BandwidthSampler) OnPacketAcked(ackTime time.Time, lastAckedPacket cong // onPacketAckedInner Handles the actual bandwidth calculations, whereas the outer method handles // retrieving and removing |sentPacket|. -func (s *BandwidthSampler) onPacketAckedInner(ackTime time.Time, lastAckedPacket congestion.PacketNumber, sentPacket *ConnectionStateOnSentPacket) *BandwidthSample { +func (s *BandwidthSampler) onPacketAckedInner(ackTime monotime.Time, lastAckedPacket congestion.PacketNumber, sentPacket *ConnectionStateOnSentPacket) *BandwidthSample { s.totalBytesAcked += sentPacket.size s.totalBytesSentAtLastAckedPacket = sentPacket.sendTimeState.totalBytesSent @@ -336,7 +337,7 @@ type ConnectionStates struct { stats map[congestion.PacketNumber]*ConnectionStateOnSentPacket } -func (s *ConnectionStates) Insert(packetNumber congestion.PacketNumber, sentTime time.Time, bytes congestion.ByteCount, sampler *BandwidthSampler) bool { +func (s *ConnectionStates) Insert(packetNumber congestion.PacketNumber, sentTime monotime.Time, bytes congestion.ByteCount, sampler *BandwidthSampler) bool { if _, ok := s.stats[packetNumber]; ok { return false } @@ -357,7 +358,7 @@ func (s *ConnectionStates) Remove(packetNumber congestion.PacketNumber) (bool, * return ok, state } -func NewConnectionStateOnSentPacket(packetNumber congestion.PacketNumber, sentTime time.Time, bytes congestion.ByteCount, sampler *BandwidthSampler) *ConnectionStateOnSentPacket { +func NewConnectionStateOnSentPacket(packetNumber congestion.PacketNumber, sentTime monotime.Time, bytes congestion.ByteCount, sampler *BandwidthSampler) *ConnectionStateOnSentPacket { return &ConnectionStateOnSentPacket{ packetNumber: packetNumber, sendTime: sentTime, diff --git a/mihomo/transport/tuic/congestion/bbr_sender.go b/mihomo/transport/tuic/congestion/bbr_sender.go index 93f90ba877..7f41d5be7e 100644 --- a/mihomo/transport/tuic/congestion/bbr_sender.go +++ b/mihomo/transport/tuic/congestion/bbr_sender.go @@ -9,6 +9,7 @@ import ( "github.com/metacubex/quic-go" "github.com/metacubex/quic-go/congestion" + "github.com/metacubex/quic-go/monotime" "github.com/metacubex/randv2" ) @@ -121,13 +122,13 @@ type bbrSender struct { // Tracks the maximum number of bytes acked faster than the sending rate. maxAckHeight *WindowedFilter // The time this aggregation started and the number of bytes acked during it. - aggregationEpochStartTime time.Time + aggregationEpochStartTime monotime.Time aggregationEpochBytes congestion.ByteCount // Minimum RTT estimate. Automatically expires within 10 seconds (and // triggers PROBE_RTT mode) if no new value is sampled during that period. minRtt time.Duration // The time at which the current value of |min_rtt_| was assigned. - minRttTimestamp time.Time + minRttTimestamp monotime.Time // The maximum allowed number of bytes in flight. congestionWindow congestion.ByteCount // The initial value of the |congestion_window_|. @@ -160,7 +161,7 @@ type bbrSender struct { // pacing gain cycle. cycleCurrentOffset int // The time at which the last pacing gain cycle was started. - lastCycleStart time.Time + lastCycleStart monotime.Time // Indicates whether the connection has reached the full bandwidth mode. isAtFullBandwidth bool // Number of rounds during which there was no significant bandwidth increase. @@ -172,7 +173,7 @@ type bbrSender struct { // Time at which PROBE_RTT has to be exited. Setting it to zero indicates // that the time is yet unknown as the number of packets in flight has not // reached the required value. - exitProbeRttAt time.Time + exitProbeRttAt monotime.Time // Indicates whether a round-trip has passed since PROBE_RTT became active. probeRttRoundPassed bool // Indicates whether the most recent bandwidth sample was marked as @@ -277,12 +278,12 @@ func (b *bbrSender) GetBytesInFlight() congestion.ByteCount { } // TimeUntilSend returns when the next packet should be sent. -func (b *bbrSender) TimeUntilSend(bytesInFlight congestion.ByteCount) time.Time { +func (b *bbrSender) TimeUntilSend(bytesInFlight congestion.ByteCount) monotime.Time { b.bytesInFlight = bytesInFlight return b.pacer.TimeUntilSend() } -func (b *bbrSender) HasPacingBudget(now time.Time) bool { +func (b *bbrSender) HasPacingBudget(now monotime.Time) bool { return b.pacer.Budget(now) >= b.maxDatagramSize } @@ -298,7 +299,7 @@ func (b *bbrSender) SetMaxDatagramSize(s congestion.ByteCount) { b.pacer.SetMaxDatagramSize(s) } -func (b *bbrSender) OnPacketSent(sentTime time.Time, bytesInFlight congestion.ByteCount, packetNumber congestion.PacketNumber, bytes congestion.ByteCount, isRetransmittable bool) { +func (b *bbrSender) OnPacketSent(sentTime monotime.Time, bytesInFlight congestion.ByteCount, packetNumber congestion.PacketNumber, bytes congestion.ByteCount, isRetransmittable bool) { b.pacer.SentPacket(sentTime, bytes) b.lastSendPacket = packetNumber @@ -335,7 +336,7 @@ func (b *bbrSender) MaybeExitSlowStart() { } -func (b *bbrSender) OnPacketAcked(number congestion.PacketNumber, ackedBytes congestion.ByteCount, priorInFlight congestion.ByteCount, eventTime time.Time) { +func (b *bbrSender) OnPacketAcked(number congestion.PacketNumber, ackedBytes congestion.ByteCount, priorInFlight congestion.ByteCount, eventTime monotime.Time) { // Stub } @@ -343,7 +344,7 @@ func (b *bbrSender) OnCongestionEvent(number congestion.PacketNumber, lostBytes // Stub } -func (b *bbrSender) OnCongestionEventEx(priorInFlight congestion.ByteCount, eventTime time.Time, ackedPackets []congestion.AckedPacketInfo, lostPackets []congestion.LostPacketInfo) { +func (b *bbrSender) OnCongestionEventEx(priorInFlight congestion.ByteCount, eventTime monotime.Time, ackedPackets []congestion.AckedPacketInfo, lostPackets []congestion.LostPacketInfo) { totalBytesAckedBefore := b.sampler.totalBytesAcked isRoundStart, minRttExpired := false, false @@ -490,7 +491,7 @@ func (b *bbrSender) UpdateRoundTripCounter(lastAckedPacket congestion.PacketNumb return false } -func (b *bbrSender) UpdateBandwidthAndMinRtt(now time.Time, ackedPackets []congestion.AckedPacketInfo) bool { +func (b *bbrSender) UpdateBandwidthAndMinRtt(now monotime.Time, ackedPackets []congestion.AckedPacketInfo) bool { sampleMinRtt := InfiniteRTT for _, packet := range ackedPackets { @@ -610,7 +611,7 @@ func (b *bbrSender) UpdateRecoveryState(hasLosses, isRoundStart bool) { } } -func (b *bbrSender) UpdateAckAggregationBytes(ackTime time.Time, ackedBytes congestion.ByteCount) congestion.ByteCount { +func (b *bbrSender) UpdateAckAggregationBytes(ackTime monotime.Time, ackedBytes congestion.ByteCount) congestion.ByteCount { // Compute how many bytes are expected to be delivered, assuming max bandwidth // is correct. expectedAckedBytes := congestion.ByteCount(b.maxBandwidth.GetBest()) * @@ -630,7 +631,7 @@ func (b *bbrSender) UpdateAckAggregationBytes(ackTime time.Time, ackedBytes cong return b.aggregationEpochBytes - expectedAckedBytes } -func (b *bbrSender) UpdateGainCyclePhase(now time.Time, priorInFlight congestion.ByteCount, hasLosses bool) { +func (b *bbrSender) UpdateGainCyclePhase(now monotime.Time, priorInFlight congestion.ByteCount, hasLosses bool) { bytesInFlight := b.GetBytesInFlight() // In most cases, the cycle is advanced after an RTT passes. shouldAdvanceGainCycling := now.Sub(b.lastCycleStart) > b.GetMinRtt() @@ -697,7 +698,7 @@ func (b *bbrSender) CheckIfFullBandwidthReached() { } } -func (b *bbrSender) MaybeExitStartupOrDrain(now time.Time) { +func (b *bbrSender) MaybeExitStartupOrDrain(now monotime.Time) { if b.mode == STARTUP && b.isAtFullBandwidth { b.OnExitStartup(now) b.mode = DRAIN @@ -709,7 +710,7 @@ func (b *bbrSender) MaybeExitStartupOrDrain(now time.Time) { } } -func (b *bbrSender) EnterProbeBandwidthMode(now time.Time) { +func (b *bbrSender) EnterProbeBandwidthMode(now monotime.Time) { b.mode = PROBE_BW b.congestionWindowGain = b.congestionWindowGainConst @@ -725,7 +726,7 @@ func (b *bbrSender) EnterProbeBandwidthMode(now time.Time) { b.pacingGain = PacingGain[b.cycleCurrentOffset] } -func (b *bbrSender) MaybeEnterOrExitProbeRtt(now time.Time, isRoundStart, minRttExpired bool) { +func (b *bbrSender) MaybeEnterOrExitProbeRtt(now monotime.Time, isRoundStart, minRttExpired bool) { if minRttExpired && !b.exitingQuiescence && b.mode != PROBE_RTT { if b.InSlowStart() { b.OnExitStartup(now) @@ -734,7 +735,7 @@ func (b *bbrSender) MaybeEnterOrExitProbeRtt(now time.Time, isRoundStart, minRtt b.pacingGain = 1.0 // Do not decide on the time to exit PROBE_RTT until the |bytes_in_flight| // is at the target small value. - b.exitProbeRttAt = time.Time{} + b.exitProbeRttAt = monotime.Time(0) } if b.mode == PROBE_RTT { @@ -773,7 +774,7 @@ func (b *bbrSender) ProbeRttCongestionWindow() congestion.ByteCount { } } -func (b *bbrSender) EnterStartupMode(now time.Time) { +func (b *bbrSender) EnterStartupMode(now monotime.Time) { // if b.rttStats != nil { // TODO: slow start. // } @@ -782,7 +783,7 @@ func (b *bbrSender) EnterStartupMode(now time.Time) { b.congestionWindowGain = b.highCwndGain } -func (b *bbrSender) OnExitStartup(now time.Time) { +func (b *bbrSender) OnExitStartup(now monotime.Time) { if b.rttStats == nil { return } diff --git a/mihomo/transport/tuic/congestion/cubic.go b/mihomo/transport/tuic/congestion/cubic.go index a9bed43aa1..35c3f0cba1 100644 --- a/mihomo/transport/tuic/congestion/cubic.go +++ b/mihomo/transport/tuic/congestion/cubic.go @@ -5,6 +5,7 @@ import ( "time" "github.com/metacubex/quic-go/congestion" + "github.com/metacubex/quic-go/monotime" ) // This cubic implementation is based on the one found in Chromiums's QUIC @@ -42,7 +43,7 @@ type Cubic struct { numConnections int // Time when this cycle started, after last loss event. - epoch time.Time + epoch monotime.Time // Max congestion window used just before last loss event. // Note: to improve fairness to other streams an additional back off is @@ -77,7 +78,7 @@ func NewCubic(clock Clock) *Cubic { // Reset is called after a timeout to reset the cubic state func (c *Cubic) Reset() { - c.epoch = time.Time{} + c.epoch = monotime.Time(0) c.lastMaxCongestionWindow = 0 c.ackedBytesCount = 0 c.estimatedTCPcongestionWindow = 0 @@ -121,7 +122,7 @@ func (c *Cubic) OnApplicationLimited() { // in such a period. This reset effectively freezes congestion window growth // through application-limited periods and allows Cubic growth to continue // when the entire window is being used. - c.epoch = time.Time{} + c.epoch = monotime.Time(0) } // CongestionWindowAfterPacketLoss computes a new congestion window to use after @@ -135,7 +136,7 @@ func (c *Cubic) CongestionWindowAfterPacketLoss(currentCongestionWindow congesti } else { c.lastMaxCongestionWindow = currentCongestionWindow } - c.epoch = time.Time{} // Reset time. + c.epoch = monotime.Time(0) // Reset time. return congestion.ByteCount(float32(currentCongestionWindow) * c.beta()) } @@ -147,7 +148,7 @@ func (c *Cubic) CongestionWindowAfterAck( ackedBytes congestion.ByteCount, currentCongestionWindow congestion.ByteCount, delayMin time.Duration, - eventTime time.Time, + eventTime monotime.Time, ) congestion.ByteCount { c.ackedBytesCount += ackedBytes diff --git a/mihomo/transport/tuic/congestion/cubic_sender.go b/mihomo/transport/tuic/congestion/cubic_sender.go index f544cd749c..dcf63a0a8d 100644 --- a/mihomo/transport/tuic/congestion/cubic_sender.go +++ b/mihomo/transport/tuic/congestion/cubic_sender.go @@ -2,9 +2,9 @@ package congestion import ( "fmt" - "time" "github.com/metacubex/quic-go/congestion" + "github.com/metacubex/quic-go/monotime" ) const ( @@ -103,11 +103,11 @@ func (c *cubicSender) SetRTTStatsProvider(provider congestion.RTTStatsProvider) } // TimeUntilSend returns when the next packet should be sent. -func (c *cubicSender) TimeUntilSend(_ congestion.ByteCount) time.Time { +func (c *cubicSender) TimeUntilSend(_ congestion.ByteCount) monotime.Time { return c.pacer.TimeUntilSend() } -func (c *cubicSender) HasPacingBudget(now time.Time) bool { +func (c *cubicSender) HasPacingBudget(now monotime.Time) bool { return c.pacer.Budget(now) >= c.maxDatagramSize } @@ -120,7 +120,7 @@ func (c *cubicSender) minCongestionWindow() congestion.ByteCount { } func (c *cubicSender) OnPacketSent( - sentTime time.Time, + sentTime monotime.Time, _ congestion.ByteCount, packetNumber congestion.PacketNumber, bytes congestion.ByteCount, @@ -162,7 +162,7 @@ func (c *cubicSender) OnPacketAcked( ackedPacketNumber congestion.PacketNumber, ackedBytes congestion.ByteCount, priorInFlight congestion.ByteCount, - eventTime time.Time, + eventTime monotime.Time, ) { c.largestAckedPacketNumber = Max(ackedPacketNumber, c.largestAckedPacketNumber) if c.InRecovery() { @@ -197,7 +197,7 @@ func (c *cubicSender) OnCongestionEvent(packetNumber congestion.PacketNumber, lo c.numAckedPackets = 0 } -func (b *cubicSender) OnCongestionEventEx(priorInFlight congestion.ByteCount, eventTime time.Time, ackedPackets []congestion.AckedPacketInfo, lostPackets []congestion.LostPacketInfo) { +func (b *cubicSender) OnCongestionEventEx(priorInFlight congestion.ByteCount, eventTime monotime.Time, ackedPackets []congestion.AckedPacketInfo, lostPackets []congestion.LostPacketInfo) { // Stub } @@ -207,7 +207,7 @@ func (c *cubicSender) maybeIncreaseCwnd( _ congestion.PacketNumber, ackedBytes congestion.ByteCount, priorInFlight congestion.ByteCount, - eventTime time.Time, + eventTime monotime.Time, ) { // Do not increase the congestion window unless the sender is close to using // the current window. diff --git a/mihomo/transport/tuic/congestion/pacer.go b/mihomo/transport/tuic/congestion/pacer.go index f60ef5fe1b..598f9dac99 100644 --- a/mihomo/transport/tuic/congestion/pacer.go +++ b/mihomo/transport/tuic/congestion/pacer.go @@ -5,6 +5,7 @@ import ( "time" "github.com/metacubex/quic-go/congestion" + "github.com/metacubex/quic-go/monotime" ) const initialMaxDatagramSize = congestion.ByteCount(1252) @@ -16,7 +17,7 @@ const maxBurstSizePackets = 10 type pacer struct { budgetAtLastSent congestion.ByteCount maxDatagramSize congestion.ByteCount - lastSentTime time.Time + lastSentTime monotime.Time getAdjustedBandwidth func() uint64 // in bytes/s } @@ -37,7 +38,7 @@ func newPacer(getBandwidth func() Bandwidth) *pacer { return p } -func (p *pacer) SentPacket(sendTime time.Time, size congestion.ByteCount) { +func (p *pacer) SentPacket(sendTime monotime.Time, size congestion.ByteCount) { budget := p.Budget(sendTime) if size > budget { p.budgetAtLastSent = 0 @@ -47,7 +48,7 @@ func (p *pacer) SentPacket(sendTime time.Time, size congestion.ByteCount) { p.lastSentTime = sendTime } -func (p *pacer) Budget(now time.Time) congestion.ByteCount { +func (p *pacer) Budget(now monotime.Time) congestion.ByteCount { if p.lastSentTime.IsZero() { return p.maxBurstSize() } @@ -63,10 +64,10 @@ func (p *pacer) maxBurstSize() congestion.ByteCount { } // TimeUntilSend returns when the next packet should be sent. -// It returns the zero value of time.Time if a packet can be sent immediately. -func (p *pacer) TimeUntilSend() time.Time { +// It returns the zero value of monotime.Time if a packet can be sent immediately. +func (p *pacer) TimeUntilSend() monotime.Time { if p.budgetAtLastSent >= p.maxDatagramSize { - return time.Time{} + return monotime.Time(0) } return p.lastSentTime.Add(Max( MinPacingDelay, diff --git a/mihomo/transport/tuic/congestion_v2/bandwidth_sampler.go b/mihomo/transport/tuic/congestion_v2/bandwidth_sampler.go index 9028df64c3..d546aec5bf 100644 --- a/mihomo/transport/tuic/congestion_v2/bandwidth_sampler.go +++ b/mihomo/transport/tuic/congestion_v2/bandwidth_sampler.go @@ -5,6 +5,7 @@ import ( "time" "github.com/metacubex/quic-go/congestion" + "github.com/metacubex/quic-go/monotime" ) const ( @@ -103,7 +104,7 @@ type maxAckHeightTracker struct { // bandwidth. maxAckHeightFilter *WindowedFilter[extraAckedEvent, roundTripCount] // The time this aggregation started and the number of bytes acked during it. - aggregationEpochStartTime time.Time + aggregationEpochStartTime monotime.Time aggregationEpochBytes congestion.ByteCount // The last sent packet number before the current aggregation epoch started. lastSentPacketNumberBeforeEpoch congestion.PacketNumber @@ -133,7 +134,7 @@ func (m *maxAckHeightTracker) Update( roundTripCount roundTripCount, lastSentPacketNumber congestion.PacketNumber, lastAckedPacketNumber congestion.PacketNumber, - ackTime time.Time, + ackTime monotime.Time, bytesAcked congestion.ByteCount, ) congestion.ByteCount { forceNewEpoch := false @@ -241,7 +242,7 @@ func (m *maxAckHeightTracker) NumAckAggregationEpochs() uint64 { // AckPoint represents a point on the ack line. type ackPoint struct { - ackTime time.Time + ackTime monotime.Time totalBytesAcked congestion.ByteCount } @@ -250,7 +251,7 @@ type recentAckPoints struct { ackPoints [2]ackPoint } -func (r *recentAckPoints) Update(ackTime time.Time, totalBytesAcked congestion.ByteCount) { +func (r *recentAckPoints) Update(ackTime monotime.Time, totalBytesAcked congestion.ByteCount) { if ackTime.Before(r.ackPoints[1].ackTime) { r.ackPoints[1].ackTime = ackTime } else if ackTime.After(r.ackPoints[1].ackTime) { @@ -284,7 +285,7 @@ func (r *recentAckPoints) LessRecentPoint() *ackPoint { // that moment. type connectionStateOnSentPacket struct { // Time at which the packet is sent. - sentTime time.Time + sentTime monotime.Time // Size of the packet. size congestion.ByteCount // The value of |totalBytesSentAtLastAckedPacket| at the time the @@ -292,10 +293,10 @@ type connectionStateOnSentPacket struct { totalBytesSentAtLastAckedPacket congestion.ByteCount // The value of |lastAckedPacketSentTime| at the time the packet was // sent. - lastAckedPacketSentTime time.Time + lastAckedPacketSentTime monotime.Time // The value of |lastAckedPacketAckTime| at the time the packet was // sent. - lastAckedPacketAckTime time.Time + lastAckedPacketAckTime monotime.Time // Send time states that are returned to the congestion controller when the // packet is acked or lost. sendTimeState sendTimeState @@ -305,7 +306,7 @@ type connectionStateOnSentPacket struct { // sampler. // |bytes_in_flight| is the bytes in flight right after the packet is sent. func newConnectionStateOnSentPacket( - sentTime time.Time, + sentTime monotime.Time, size congestion.ByteCount, bytesInFlight congestion.ByteCount, sampler *bandwidthSampler, @@ -456,10 +457,10 @@ type bandwidthSampler struct { // The time at which the last acknowledged packet was sent. Set to // QuicTime::Zero() if no valid timestamp is available. - lastAckedPacketSentTime time.Time + lastAckedPacketSentTime monotime.Time // The time at which the most recent packet was acknowledged. - lastAckedPacketAckTime time.Time + lastAckedPacketAckTime monotime.Time // The most recently sent packet. lastSentPacket congestion.PacketNumber @@ -551,7 +552,7 @@ func (b *bandwidthSampler) IsOverestimateAvoidanceEnabled() bool { } func (b *bandwidthSampler) OnPacketSent( - sentTime time.Time, + sentTime monotime.Time, packetNumber congestion.PacketNumber, bytes congestion.ByteCount, bytesInFlight congestion.ByteCount, @@ -595,7 +596,7 @@ func (b *bandwidthSampler) OnPacketSent( } func (b *bandwidthSampler) OnCongestionEvent( - ackTime time.Time, + ackTime monotime.Time, ackedPackets []congestion.AckedPacketInfo, lostPackets []congestion.LostPacketInfo, maxBandwidth Bandwidth, @@ -758,7 +759,7 @@ func (b *bandwidthSampler) chooseA0Point(totalBytesAcked congestion.ByteCount, a return true } -func (b *bandwidthSampler) onPacketAcknowledged(ackTime time.Time, packetNumber congestion.PacketNumber) bandwidthSample { +func (b *bandwidthSampler) onPacketAcknowledged(ackTime monotime.Time, packetNumber congestion.PacketNumber) bandwidthSample { sample := newBandwidthSample() b.lastAckedPacket = packetNumber sentPacketPointer := b.connectionStateMap.GetEntry(packetNumber) diff --git a/mihomo/transport/tuic/congestion_v2/bbr_sender.go b/mihomo/transport/tuic/congestion_v2/bbr_sender.go index a515c2cc44..6020ab391f 100644 --- a/mihomo/transport/tuic/congestion_v2/bbr_sender.go +++ b/mihomo/transport/tuic/congestion_v2/bbr_sender.go @@ -8,6 +8,7 @@ import ( "github.com/metacubex/quic-go" "github.com/metacubex/quic-go/congestion" + "github.com/metacubex/quic-go/monotime" "github.com/metacubex/randv2" ) @@ -127,7 +128,7 @@ type bbrSender struct { // triggers PROBE_RTT mode) if no new value is sampled during that period. minRtt time.Duration // The time at which the current value of |min_rtt_| was assigned. - minRttTimestamp time.Time + minRttTimestamp monotime.Time // The maximum allowed number of bytes in flight. congestionWindow congestion.ByteCount @@ -168,7 +169,7 @@ type bbrSender struct { // pacing gain cycle. cycleCurrentOffset int // The time at which the last pacing gain cycle was started. - lastCycleStart time.Time + lastCycleStart monotime.Time // Indicates whether the connection has reached the full bandwidth mode. isAtFullBandwidth bool @@ -183,7 +184,7 @@ type bbrSender struct { // Time at which PROBE_RTT has to be exited. Setting it to zero indicates // that the time is yet unknown as the number of packets in flight has not // reached the required value. - exitProbeRttAt time.Time + exitProbeRttAt monotime.Time // Indicates whether a round-trip has passed since PROBE_RTT became active. probeRttRoundPassed bool @@ -307,18 +308,18 @@ func (b *bbrSender) SetRTTStatsProvider(provider congestion.RTTStatsProvider) { } // TimeUntilSend implements the SendAlgorithm interface. -func (b *bbrSender) TimeUntilSend(bytesInFlight congestion.ByteCount) time.Time { +func (b *bbrSender) TimeUntilSend(bytesInFlight congestion.ByteCount) monotime.Time { return b.pacer.TimeUntilSend() } // HasPacingBudget implements the SendAlgorithm interface. -func (b *bbrSender) HasPacingBudget(now time.Time) bool { +func (b *bbrSender) HasPacingBudget(now monotime.Time) bool { return b.pacer.Budget(now) >= b.maxDatagramSize } // OnPacketSent implements the SendAlgorithm interface. func (b *bbrSender) OnPacketSent( - sentTime time.Time, + sentTime monotime.Time, bytesInFlight congestion.ByteCount, packetNumber congestion.PacketNumber, bytes congestion.ByteCount, @@ -349,7 +350,7 @@ func (b *bbrSender) MaybeExitSlowStart() { } // OnPacketAcked implements the SendAlgorithm interface. -func (b *bbrSender) OnPacketAcked(number congestion.PacketNumber, ackedBytes, priorInFlight congestion.ByteCount, eventTime time.Time) { +func (b *bbrSender) OnPacketAcked(number congestion.PacketNumber, ackedBytes, priorInFlight congestion.ByteCount, eventTime monotime.Time) { // Do nothing. } @@ -403,7 +404,7 @@ func (b *bbrSender) OnCongestionEvent(number congestion.PacketNumber, lostBytes, // Do nothing. } -func (b *bbrSender) OnCongestionEventEx(priorInFlight congestion.ByteCount, eventTime time.Time, ackedPackets []congestion.AckedPacketInfo, lostPackets []congestion.LostPacketInfo) { +func (b *bbrSender) OnCongestionEventEx(priorInFlight congestion.ByteCount, eventTime monotime.Time, ackedPackets []congestion.AckedPacketInfo, lostPackets []congestion.LostPacketInfo) { totalBytesAckedBefore := b.sampler.TotalBytesAcked() totalBytesLostBefore := b.sampler.TotalBytesLost() @@ -592,7 +593,7 @@ func (b *bbrSender) probeRttCongestionWindow() congestion.ByteCount { return b.minCongestionWindow } -func (b *bbrSender) maybeUpdateMinRtt(now time.Time, sampleMinRtt time.Duration) bool { +func (b *bbrSender) maybeUpdateMinRtt(now monotime.Time, sampleMinRtt time.Duration) bool { // Do not expire min_rtt if none was ever available. minRttExpired := b.minRtt != 0 && now.After(b.minRttTimestamp.Add(minRttExpiry)) if minRttExpired || sampleMinRtt < b.minRtt || b.minRtt == 0 { @@ -604,7 +605,7 @@ func (b *bbrSender) maybeUpdateMinRtt(now time.Time, sampleMinRtt time.Duration) } // Enters the STARTUP mode. -func (b *bbrSender) enterStartupMode(now time.Time) { +func (b *bbrSender) enterStartupMode(now monotime.Time) { b.mode = bbrModeStartup // b.maybeTraceStateChange(logging.CongestionStateStartup) b.pacingGain = b.highGain @@ -612,7 +613,7 @@ func (b *bbrSender) enterStartupMode(now time.Time) { } // Enters the PROBE_BW mode. -func (b *bbrSender) enterProbeBandwidthMode(now time.Time) { +func (b *bbrSender) enterProbeBandwidthMode(now monotime.Time) { b.mode = bbrModeProbeBw // b.maybeTraceStateChange(logging.CongestionStateProbeBw) b.congestionWindowGain = b.congestionWindowGainConstant @@ -641,7 +642,7 @@ func (b *bbrSender) updateRoundTripCounter(lastAckedPacket congestion.PacketNumb } // Updates the current gain used in PROBE_BW mode. -func (b *bbrSender) updateGainCyclePhase(now time.Time, priorInFlight congestion.ByteCount, hasLosses bool) { +func (b *bbrSender) updateGainCyclePhase(now monotime.Time, priorInFlight congestion.ByteCount, hasLosses bool) { // In most cases, the cycle is advanced after an RTT passes. shouldAdvanceGainCycling := now.After(b.lastCycleStart.Add(b.getMinRtt())) // If the pacing gain is above 1.0, the connection is trying to probe the @@ -713,7 +714,7 @@ func (b *bbrSender) maybeAppLimited(bytesInFlight congestion.ByteCount) { // Transitions from STARTUP to DRAIN and from DRAIN to PROBE_BW if // appropriate. -func (b *bbrSender) maybeExitStartupOrDrain(now time.Time) { +func (b *bbrSender) maybeExitStartupOrDrain(now monotime.Time) { if b.mode == bbrModeStartup && b.isAtFullBandwidth { b.mode = bbrModeDrain // b.maybeTraceStateChange(logging.CongestionStateDrain) @@ -726,14 +727,14 @@ func (b *bbrSender) maybeExitStartupOrDrain(now time.Time) { } // Decides whether to enter or exit PROBE_RTT. -func (b *bbrSender) maybeEnterOrExitProbeRtt(now time.Time, isRoundStart, minRttExpired bool) { +func (b *bbrSender) maybeEnterOrExitProbeRtt(now monotime.Time, isRoundStart, minRttExpired bool) { if minRttExpired && !b.exitingQuiescence && b.mode != bbrModeProbeRtt { b.mode = bbrModeProbeRtt // b.maybeTraceStateChange(logging.CongestionStateProbRtt) b.pacingGain = 1.0 // Do not decide on the time to exit PROBE_RTT until the |bytes_in_flight| // is at the target small value. - b.exitProbeRttAt = time.Time{} + b.exitProbeRttAt = monotime.Time(0) } if b.mode == bbrModeProbeRtt { diff --git a/mihomo/transport/tuic/congestion_v2/clock.go b/mihomo/transport/tuic/congestion_v2/clock.go index 405fae70f9..e6ea35dbd4 100644 --- a/mihomo/transport/tuic/congestion_v2/clock.go +++ b/mihomo/transport/tuic/congestion_v2/clock.go @@ -1,10 +1,12 @@ package congestion -import "time" +import ( + "github.com/metacubex/quic-go/monotime" +) // A Clock returns the current time type Clock interface { - Now() time.Time + Now() monotime.Time } // DefaultClock implements the Clock interface using the Go stdlib clock. @@ -13,6 +15,6 @@ type DefaultClock struct{} var _ Clock = DefaultClock{} // Now gets the current time -func (DefaultClock) Now() time.Time { - return time.Now() +func (DefaultClock) Now() monotime.Time { + return monotime.Now() } diff --git a/mihomo/transport/tuic/congestion_v2/pacer.go b/mihomo/transport/tuic/congestion_v2/pacer.go index 174b3dbe3f..6158bf5891 100644 --- a/mihomo/transport/tuic/congestion_v2/pacer.go +++ b/mihomo/transport/tuic/congestion_v2/pacer.go @@ -5,6 +5,7 @@ import ( "time" "github.com/metacubex/quic-go/congestion" + "github.com/metacubex/quic-go/monotime" ) const ( @@ -15,7 +16,7 @@ const ( type Pacer struct { budgetAtLastSent congestion.ByteCount maxDatagramSize congestion.ByteCount - lastSentTime time.Time + lastSentTime monotime.Time getBandwidth func() congestion.ByteCount // in bytes/s } @@ -28,7 +29,7 @@ func NewPacer(getBandwidth func() congestion.ByteCount) *Pacer { return p } -func (p *Pacer) SentPacket(sendTime time.Time, size congestion.ByteCount) { +func (p *Pacer) SentPacket(sendTime monotime.Time, size congestion.ByteCount) { budget := p.Budget(sendTime) if size > budget { p.budgetAtLastSent = 0 @@ -38,7 +39,7 @@ func (p *Pacer) SentPacket(sendTime time.Time, size congestion.ByteCount) { p.lastSentTime = sendTime } -func (p *Pacer) Budget(now time.Time) congestion.ByteCount { +func (p *Pacer) Budget(now monotime.Time) congestion.ByteCount { if p.lastSentTime.IsZero() { return p.maxBurstSize() } @@ -57,10 +58,10 @@ func (p *Pacer) maxBurstSize() congestion.ByteCount { } // TimeUntilSend returns when the next packet should be sent. -// It returns the zero value of time.Time if a packet can be sent immediately. -func (p *Pacer) TimeUntilSend() time.Time { +// It returns the zero value of monotime.Time if a packet can be sent immediately. +func (p *Pacer) TimeUntilSend() monotime.Time { if p.budgetAtLastSent >= p.maxDatagramSize { - return time.Time{} + return monotime.Time(0) } return p.lastSentTime.Add(Max( congestion.MinPacingDelay, diff --git a/sing-box/dns/client_truncate.go b/sing-box/dns/client_truncate.go index f00023f27b..19165f993a 100644 --- a/sing-box/dns/client_truncate.go +++ b/sing-box/dns/client_truncate.go @@ -15,8 +15,7 @@ func TruncateDNSMessage(request *dns.Msg, response *dns.Msg, headroom int) (*buf } responseLen := response.Len() if responseLen > maxLen { - copyResponse := *response - response = ©Response + response = response.Copy() response.Truncate(maxLen) } buffer := buf.NewSize(headroom*2 + 1 + responseLen) diff --git a/small/luci-app-homeproxy/root/etc/homeproxy/scripts/generate_client.uc b/small/luci-app-homeproxy/root/etc/homeproxy/scripts/generate_client.uc index fe87b2f658..b20bf6579f 100755 --- a/small/luci-app-homeproxy/root/etc/homeproxy/scripts/generate_client.uc +++ b/small/luci-app-homeproxy/root/etc/homeproxy/scripts/generate_client.uc @@ -157,7 +157,7 @@ function parse_dnserver(server_addr, default_protocol) { return null; if (!match(server_addr, /:\/\//)) - server_addr = (default_protocol || 'udp') + '://' + server_addr; + server_addr = (default_protocol || 'udp') + '://' + (validation('ip6addr', dns_server) ? `[${dns_server}]` : dns_server); server_addr = parseURL(server_addr); return { @@ -407,12 +407,13 @@ config.log = { }; /* NTP */ -config.ntp = { - enabled: true, - server: ntp_server, - detour: 'direct-out', - domain_resolver: 'default-dns', -}; +if (!isEmpty(ntp_server)) + config.ntp = { + enabled: true, + server: ntp_server, + detour: 'direct-out', + domain_resolver: 'default-dns', + }; /* DNS start */ /* Default settings */ diff --git a/small/luci-app-homeproxy/root/etc/homeproxy/scripts/migrate_config.uc b/small/luci-app-homeproxy/root/etc/homeproxy/scripts/migrate_config.uc index 1645b488f5..dbb6761d5e 100755 --- a/small/luci-app-homeproxy/root/etc/homeproxy/scripts/migrate_config.uc +++ b/small/luci-app-homeproxy/root/etc/homeproxy/scripts/migrate_config.uc @@ -96,7 +96,7 @@ const dns_server_migration = {}; uci.foreach(uciconfig, ucidnsserver, (cfg) => { /* legacy format was deprecated in sb 1.12 */ if (cfg.address) { - const addr = parseURL((!match(cfg.address, /:\/\//) ? 'udp://' : '') + cfg.address); + const addr = parseURL((!match(cfg.address, /:\/\//) ? 'udp://' : '') + (validation('ip6addr', cfg.address) ? `[${cfg.address}]` : cfg.address)); /* RCode was moved into DNS rules */ if (addr.protocol === 'rcode') { dns_server_migration[cfg['.name']] = { action: 'predefined' }; diff --git a/small/ssocks/Makefile b/small/ssocks/Makefile index cb02254e64..8439e4f656 100644 --- a/small/ssocks/Makefile +++ b/small/ssocks/Makefile @@ -76,3 +76,4 @@ endef $(eval $(call BuildPackage,ssocks)) $(eval $(call BuildPackage,ssocksd)) + diff --git a/small/v2ray-core/Makefile b/small/v2ray-core/Makefile index c0c7188eae..5c146b0c65 100644 --- a/small/v2ray-core/Makefile +++ b/small/v2ray-core/Makefile @@ -5,12 +5,12 @@ include $(TOPDIR)/rules.mk PKG_NAME:=v2ray-core -PKG_VERSION:=5.39.0 +PKG_VERSION:=5.40.0 PKG_RELEASE:=1 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=https://codeload.github.com/v2fly/v2ray-core/tar.gz/v$(PKG_VERSION)? -PKG_HASH:=a6f6db8a6911dbaaf9c140a73e9f9d56706e046bf2cf234da3d06bb4ee4310a6 +PKG_HASH:=14e333c7454781f0b44fe9cba1616e25accfb04cf0d9d31db7acdd33e2e8d0ac PKG_LICENSE:=MIT PKG_LICENSE_FILES:=LICENSE diff --git a/small/v2ray-plugin/Makefile b/small/v2ray-plugin/Makefile index 091904d11c..e0b66a0d36 100644 --- a/small/v2ray-plugin/Makefile +++ b/small/v2ray-plugin/Makefile @@ -6,12 +6,12 @@ include $(TOPDIR)/rules.mk PKG_NAME:=v2ray-plugin -PKG_VERSION:=5.37.0 +PKG_VERSION:=5.40.0 PKG_RELEASE:=1 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=https://codeload.github.com/teddysun/v2ray-plugin/tar.gz/v$(PKG_VERSION)? -PKG_HASH:=7796924dc2dbd1ec392953467b3ff432cdcd266bf8d1c61b5a693fdc9e9a9230 +PKG_HASH:=4c13f0bfa8217bdb81cf82c5d138a55833d10191cf0347b85f7633c68d5da819 PKG_LICENSE:=MIT PKG_LICENSE_FILES:=LICENSE diff --git a/v2ray-core/core.go b/v2ray-core/core.go index d8e14d7308..48380d9067 100644 --- a/v2ray-core/core.go +++ b/v2ray-core/core.go @@ -18,7 +18,7 @@ import ( ) var ( - version = "5.39.0" + version = "5.40.0" build = "Custom" codename = "V2Fly, a community-driven edition of V2Ray." intro = "A unified platform for anti-censorship." diff --git a/v2ray-core/go.mod b/v2ray-core/go.mod index 7731768a1a..5dc74c200f 100644 --- a/v2ray-core/go.mod +++ b/v2ray-core/go.mod @@ -42,7 +42,7 @@ require ( golang.org/x/sync v0.17.0 golang.org/x/sys v0.36.0 google.golang.org/grpc v1.75.1 - google.golang.org/protobuf v1.36.9 + google.golang.org/protobuf v1.36.10 gopkg.in/yaml.v3 v3.0.1 gvisor.dev/gvisor v0.0.0-20231020174304-b8a429915ff1 h12.io/socks v1.0.3 diff --git a/v2ray-core/go.sum b/v2ray-core/go.sum index edccae1872..cc6d063cc6 100644 --- a/v2ray-core/go.sum +++ b/v2ray-core/go.sum @@ -835,8 +835,8 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw= -google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= +google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= +google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/v2rayn/v2rayN/Directory.Packages.props b/v2rayn/v2rayN/Directory.Packages.props index 9dcfbe61a0..e9d2f2b3e8 100644 --- a/v2rayn/v2rayN/Directory.Packages.props +++ b/v2rayn/v2rayN/Directory.Packages.props @@ -6,10 +6,10 @@ - - - - + + + + @@ -19,9 +19,9 @@ - + - + diff --git a/v2rayn/v2rayN/ServiceLib/Handler/Fmt/BaseFmt.cs b/v2rayn/v2rayN/ServiceLib/Handler/Fmt/BaseFmt.cs index 965efbb1a1..25435fce1c 100644 --- a/v2rayn/v2rayN/ServiceLib/Handler/Fmt/BaseFmt.cs +++ b/v2rayn/v2rayN/ServiceLib/Handler/Fmt/BaseFmt.cs @@ -155,61 +155,60 @@ public class BaseFmt protected static int ResolveStdTransport(NameValueCollection query, ref ProfileItem item) { - item.Flow = query["flow"] ?? ""; - item.StreamSecurity = query["security"] ?? ""; - item.Sni = query["sni"] ?? ""; - item.Alpn = Utils.UrlDecode(query["alpn"] ?? ""); - item.Fingerprint = Utils.UrlDecode(query["fp"] ?? ""); - item.PublicKey = Utils.UrlDecode(query["pbk"] ?? ""); - item.ShortId = Utils.UrlDecode(query["sid"] ?? ""); - item.SpiderX = Utils.UrlDecode(query["spx"] ?? ""); - item.Mldsa65Verify = Utils.UrlDecode(query["pqv"] ?? ""); - item.AllowInsecure = (query["allowInsecure"] ?? "") == "1" ? "true" : ""; + item.Flow = GetQueryValue(query, "flow"); + item.StreamSecurity = GetQueryValue(query, "security"); + item.Sni = GetQueryValue(query, "sni"); + item.Alpn = GetQueryDecoded(query, "alpn"); + item.Fingerprint = GetQueryDecoded(query, "fp"); + item.PublicKey = GetQueryDecoded(query, "pbk"); + item.ShortId = GetQueryDecoded(query, "sid"); + item.SpiderX = GetQueryDecoded(query, "spx"); + item.Mldsa65Verify = GetQueryDecoded(query, "pqv"); + item.AllowInsecure = new[] { "allowInsecure", "allow_insecure", "insecure" }.Any(k => (query[k] ?? "") == "1") ? "true" : ""; - item.Network = query["type"] ?? nameof(ETransport.tcp); + item.Network = GetQueryValue(query, "type", nameof(ETransport.tcp)); switch (item.Network) { case nameof(ETransport.tcp): - item.HeaderType = query["headerType"] ?? Global.None; - item.RequestHost = Utils.UrlDecode(query["host"] ?? ""); - + item.HeaderType = GetQueryValue(query, "headerType", Global.None); + item.RequestHost = GetQueryDecoded(query, "host"); break; case nameof(ETransport.kcp): - item.HeaderType = query["headerType"] ?? Global.None; - item.Path = Utils.UrlDecode(query["seed"] ?? ""); + item.HeaderType = GetQueryValue(query, "headerType", Global.None); + item.Path = GetQueryDecoded(query, "seed"); break; case nameof(ETransport.ws): case nameof(ETransport.httpupgrade): - item.RequestHost = Utils.UrlDecode(query["host"] ?? ""); - item.Path = Utils.UrlDecode(query["path"] ?? "/"); + item.RequestHost = GetQueryDecoded(query, "host"); + item.Path = GetQueryDecoded(query, "path", "/"); break; case nameof(ETransport.xhttp): - item.RequestHost = Utils.UrlDecode(query["host"] ?? ""); - item.Path = Utils.UrlDecode(query["path"] ?? "/"); - item.HeaderType = Utils.UrlDecode(query["mode"] ?? ""); - item.Extra = Utils.UrlDecode(query["extra"] ?? ""); + item.RequestHost = GetQueryDecoded(query, "host"); + item.Path = GetQueryDecoded(query, "path", "/"); + item.HeaderType = GetQueryDecoded(query, "mode"); + item.Extra = GetQueryDecoded(query, "extra"); break; case nameof(ETransport.http): case nameof(ETransport.h2): item.Network = nameof(ETransport.h2); - item.RequestHost = Utils.UrlDecode(query["host"] ?? ""); - item.Path = Utils.UrlDecode(query["path"] ?? "/"); + item.RequestHost = GetQueryDecoded(query, "host"); + item.Path = GetQueryDecoded(query, "path", "/"); break; case nameof(ETransport.quic): - item.HeaderType = query["headerType"] ?? Global.None; - item.RequestHost = query["quicSecurity"] ?? Global.None; - item.Path = Utils.UrlDecode(query["key"] ?? ""); + item.HeaderType = GetQueryValue(query, "headerType", Global.None); + item.RequestHost = GetQueryValue(query, "quicSecurity", Global.None); + item.Path = GetQueryDecoded(query, "key"); break; case nameof(ETransport.grpc): - item.RequestHost = Utils.UrlDecode(query["authority"] ?? ""); - item.Path = Utils.UrlDecode(query["serviceName"] ?? ""); - item.HeaderType = Utils.UrlDecode(query["mode"] ?? Global.GrpcGunMode); + item.RequestHost = GetQueryDecoded(query, "authority"); + item.Path = GetQueryDecoded(query, "serviceName"); + item.HeaderType = GetQueryDecoded(query, "mode", Global.GrpcGunMode); break; default: @@ -239,4 +238,14 @@ public class BaseFmt var url = $"{Utils.UrlEncode(userInfo)}@{GetIpv6(address)}:{port}"; return $"{Global.ProtocolShares[eConfigType]}{url}{query}{remark}"; } + + protected static string GetQueryValue(NameValueCollection query, string key, string defaultValue = "") + { + return query[key] ?? defaultValue; + } + + protected static string GetQueryDecoded(NameValueCollection query, string key, string defaultValue = "") + { + return Utils.UrlDecode(GetQueryValue(query, key, defaultValue)); + } } diff --git a/v2rayn/v2rayN/ServiceLib/Handler/Fmt/Hysteria2Fmt.cs b/v2rayn/v2rayN/ServiceLib/Handler/Fmt/Hysteria2Fmt.cs index bc56a3024c..32fe81bb65 100644 --- a/v2rayn/v2rayN/ServiceLib/Handler/Fmt/Hysteria2Fmt.cs +++ b/v2rayn/v2rayN/ServiceLib/Handler/Fmt/Hysteria2Fmt.cs @@ -21,10 +21,10 @@ public class Hysteria2Fmt : BaseFmt var query = Utils.ParseQueryString(url.Query); ResolveStdTransport(query, ref item); - item.Path = Utils.UrlDecode(query["obfs-password"] ?? ""); - item.AllowInsecure = (query["insecure"] ?? "") == "1" ? "true" : "false"; + item.Path = GetQueryDecoded(query, "obfs-password"); + item.AllowInsecure = GetQueryValue(query, "insecure") == "1" ? "true" : "false"; - item.Ports = Utils.UrlDecode(query["mport"] ?? ""); + item.Ports = GetQueryDecoded(query, "mport"); return item; } diff --git a/v2rayn/v2rayN/ServiceLib/Handler/Fmt/TuicFmt.cs b/v2rayn/v2rayN/ServiceLib/Handler/Fmt/TuicFmt.cs index 632f49c98f..5dcc15b1c1 100644 --- a/v2rayn/v2rayN/ServiceLib/Handler/Fmt/TuicFmt.cs +++ b/v2rayn/v2rayN/ServiceLib/Handler/Fmt/TuicFmt.cs @@ -30,7 +30,7 @@ public class TuicFmt : BaseFmt var query = Utils.ParseQueryString(url.Query); ResolveStdTransport(query, ref item); - item.HeaderType = query["congestion_control"] ?? ""; + item.HeaderType = GetQueryValue(query, "congestion_control"); return item; } diff --git a/v2rayn/v2rayN/ServiceLib/Handler/Fmt/VLESSFmt.cs b/v2rayn/v2rayN/ServiceLib/Handler/Fmt/VLESSFmt.cs index 4d3bcff0b0..abf8c55a45 100644 --- a/v2rayn/v2rayN/ServiceLib/Handler/Fmt/VLESSFmt.cs +++ b/v2rayn/v2rayN/ServiceLib/Handler/Fmt/VLESSFmt.cs @@ -24,8 +24,8 @@ public class VLESSFmt : BaseFmt item.Id = Utils.UrlDecode(url.UserInfo); var query = Utils.ParseQueryString(url.Query); - item.Security = query["encryption"] ?? Global.None; - item.StreamSecurity = query["security"] ?? ""; + item.Security = GetQueryValue(query, "encryption", Global.None); + item.StreamSecurity = GetQueryValue(query, "security"); _ = ResolveStdTransport(query, ref item); return item; diff --git a/v2rayn/v2rayN/ServiceLib/Handler/Fmt/WireguardFmt.cs b/v2rayn/v2rayN/ServiceLib/Handler/Fmt/WireguardFmt.cs index 8e78f0361e..6ceb945d26 100644 --- a/v2rayn/v2rayN/ServiceLib/Handler/Fmt/WireguardFmt.cs +++ b/v2rayn/v2rayN/ServiceLib/Handler/Fmt/WireguardFmt.cs @@ -24,10 +24,10 @@ public class WireguardFmt : BaseFmt var query = Utils.ParseQueryString(url.Query); - item.PublicKey = Utils.UrlDecode(query["publickey"] ?? ""); - item.Path = Utils.UrlDecode(query["reserved"] ?? ""); - item.RequestHost = Utils.UrlDecode(query["address"] ?? ""); - item.ShortId = Utils.UrlDecode(query["mtu"] ?? ""); + item.PublicKey = GetQueryDecoded(query, "publickey"); + item.Path = GetQueryDecoded(query, "reserved"); + item.RequestHost = GetQueryDecoded(query, "address"); + item.ShortId = GetQueryDecoded(query, "mtu"); return item; } diff --git a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.Designer.cs b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.Designer.cs index 19ca4f0daa..eddaa0a7b6 100644 --- a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.Designer.cs +++ b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.Designer.cs @@ -2932,16 +2932,7 @@ namespace ServiceLib.Resx { } /// - /// 查找类似 The sing-box DoH resolution server can be overwritten 的本地化字符串。 - /// - public static string TbSBDoHOverride { - get { - return ResourceManager.GetString("TbSBDoHOverride", resourceCulture); - } - } - - /// - /// 查找类似 Fallback DNS Resolution, Require IP 的本地化字符串。 + /// 查找类似 Resolve DNS server domains, requires IP 的本地化字符串。 /// public static string TbSBFallbackDNSResolve { get { diff --git a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx index 7be8477454..14b1ff6f33 100644 --- a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx +++ b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx @@ -1429,7 +1429,7 @@ Bootstrap DNS (sing-box) - Fallback DNS Resolution, Require IP + Resolve DNS server domains, requires IP xray Freedom Resolution Strategy @@ -1443,9 +1443,6 @@ Add Common DNS Hosts - - The sing-box DoH resolution server can be overwritten - FakeIP diff --git a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.hu.resx b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.hu.resx index a4701f0ba8..fb8c6a11cb 100644 --- a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.hu.resx +++ b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.hu.resx @@ -1429,7 +1429,7 @@ Bootstrap DNS (sing-box) - Fallback DNS Resolution, Require IP + Resolve DNS server domains, requires IP xray Freedom Resolution Strategy @@ -1443,9 +1443,6 @@ Add Common DNS Hosts - - The sing-box DoH resolution server can be overwritten - FakeIP diff --git a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.resx b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.resx index 94e03415cb..5b145da396 100644 --- a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.resx +++ b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.resx @@ -1429,7 +1429,7 @@ Bootstrap DNS (sing-box) - Fallback DNS Resolution, Require IP + Resolve DNS server domains, requires IP xray Freedom Resolution Strategy @@ -1443,9 +1443,6 @@ Add Common DNS Hosts - - The sing-box DoH resolution server can be overwritten - FakeIP diff --git a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.ru.resx b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.ru.resx index c72c83cf57..27c2d0a3f5 100644 --- a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.ru.resx +++ b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.ru.resx @@ -1429,7 +1429,7 @@ Bootstrap DNS (sing-box) - Fallback DNS Resolution, Require IP + Resolve DNS server domains, requires IP Стратегия резолвинга Freedom (Xray) @@ -1443,9 +1443,6 @@ Добавить стандартные записи hosts (DNS) - - Сервер DoH-резолвера sing-box можно переопределить - FakeIP diff --git a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx index 0d5092d09c..a269c0a59d 100644 --- a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx +++ b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx @@ -1426,7 +1426,7 @@ Bootstrap DNS (sing-box) - 回退 DNS 解析,需指定为 IP + 解析 DNS 服务器域名,需指定为 IP xray freedom 解析策略 @@ -1440,9 +1440,6 @@ 添加常用 DNS Hosts - - 开启后可覆盖 sing-box DoH 解析服务器 - FakeIP diff --git a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx index 4ad280b0ba..81a01cfcad 100644 --- a/v2rayn/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx +++ b/v2rayn/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx @@ -1426,7 +1426,7 @@ Bootstrap DNS (sing-box) - Fallback DNS Resolution, Require IP + Resolve DNS server domains, requires IP xray Freedom Resolution Strategy @@ -1440,9 +1440,6 @@ Add Common DNS Hosts - - The sing-box DoH resolution server can be overwritten - FakeIP diff --git a/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs index b61485807e..24b4110952 100644 --- a/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs +++ b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs @@ -186,7 +186,7 @@ public partial class CoreConfigV2rayService(Config config) ret.Success = true; - ret.Data = await ApplyFullConfigTemplate(v2rayConfig, true); + ret.Data = await ApplyFullConfigTemplate(v2rayConfig); return ret; } catch (Exception ex) diff --git a/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayConfigTemplateService.cs b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayConfigTemplateService.cs index 5d1f7d63ee..459e77de4c 100644 --- a/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayConfigTemplateService.cs +++ b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayConfigTemplateService.cs @@ -4,7 +4,7 @@ namespace ServiceLib.Services.CoreConfig; public partial class CoreConfigV2rayService { - private async Task ApplyFullConfigTemplate(V2rayConfig v2rayConfig, bool handleBalancerAndRules = false) + private async Task ApplyFullConfigTemplate(V2rayConfig v2rayConfig) { var fullConfigTemplate = await AppManager.Instance.GetFullConfigTemplateItem(ECoreType.Xray); if (fullConfigTemplate == null || !fullConfigTemplate.Enabled || fullConfigTemplate.Config.IsNullOrEmpty()) @@ -19,7 +19,7 @@ public partial class CoreConfigV2rayService } // Handle balancer and rules modifications (for multiple load scenarios) - if (handleBalancerAndRules && v2rayConfig.routing?.balancers?.Count > 0) + if (v2rayConfig.routing?.balancers?.Count > 0) { var balancer = v2rayConfig.routing.balancers.First(); @@ -60,6 +60,34 @@ public partial class CoreConfigV2rayService } } + if (v2rayConfig.observatory != null) + { + if (fullConfigTemplateNode["observatory"] == null) + { + fullConfigTemplateNode["observatory"] = JsonNode.Parse(JsonUtils.Serialize(v2rayConfig.observatory)); + } + else + { + var subjectSelector = v2rayConfig.observatory.subjectSelector; + subjectSelector.AddRange(fullConfigTemplateNode["observatory"]?["subjectSelector"]?.AsArray()?.Select(x => x?.GetValue()) ?? []); + fullConfigTemplateNode["observatory"]["subjectSelector"] = JsonNode.Parse(JsonUtils.Serialize(subjectSelector.Distinct().ToList())); + } + } + + if (v2rayConfig.burstObservatory != null) + { + if (fullConfigTemplateNode["burstObservatory"] == null) + { + fullConfigTemplateNode["burstObservatory"] = JsonNode.Parse(JsonUtils.Serialize(v2rayConfig.burstObservatory)); + } + else + { + var subjectSelector = v2rayConfig.burstObservatory.subjectSelector; + subjectSelector.AddRange(fullConfigTemplateNode["burstObservatory"]?["subjectSelector"]?.AsArray()?.Select(x => x?.GetValue()) ?? []); + fullConfigTemplateNode["burstObservatory"]["subjectSelector"] = JsonNode.Parse(JsonUtils.Serialize(subjectSelector.Distinct().ToList())); + } + } + // Handle outbounds - append instead of override var customOutboundsNode = fullConfigTemplateNode["outbounds"] is JsonArray outbounds ? outbounds : new JsonArray(); foreach (var outbound in v2rayConfig.outbounds) diff --git a/v2rayn/v2rayN/v2rayN.Desktop/App.axaml b/v2rayn/v2rayN/v2rayN.Desktop/App.axaml index 222ab4884d..37c5447141 100644 --- a/v2rayn/v2rayN/v2rayN.Desktop/App.axaml +++ b/v2rayn/v2rayN/v2rayN.Desktop/App.axaml @@ -20,7 +20,6 @@ - diff --git a/v2rayn/v2rayN/v2rayN.Desktop/Common/AvaUtils.cs b/v2rayn/v2rayN/v2rayN.Desktop/Common/AvaUtils.cs index 87c974d4f9..f7f03ab3e2 100644 --- a/v2rayn/v2rayN/v2rayN.Desktop/Common/AvaUtils.cs +++ b/v2rayn/v2rayN/v2rayN.Desktop/Common/AvaUtils.cs @@ -1,6 +1,7 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Input; +using Avalonia.Input.Platform; using Avalonia.Media.Imaging; using Avalonia.Platform; @@ -18,7 +19,7 @@ internal class AvaUtils return null; } - return await clipboard.GetTextAsync(); + return await clipboard.TryGetTextAsync(); } catch { @@ -33,9 +34,7 @@ internal class AvaUtils var clipboard = TopLevel.GetTopLevel(visual)?.Clipboard; if (clipboard == null) return; - var dataObject = new DataObject(); - dataObject.Set(DataFormats.Text, strData); - await clipboard.SetDataObjectAsync(dataObject); + await clipboard.SetTextAsync(strData); } catch { diff --git a/v2rayn/v2rayN/v2rayN.Desktop/Controls/AutoCompleteBox.axaml b/v2rayn/v2rayN/v2rayN.Desktop/Controls/AutoCompleteBox.axaml deleted file mode 100644 index 97fec908d4..0000000000 --- a/v2rayn/v2rayN/v2rayN.Desktop/Controls/AutoCompleteBox.axaml +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/v2rayn/v2rayN/v2rayN.Desktop/Controls/AutoCompleteBox.cs b/v2rayn/v2rayN/v2rayN.Desktop/Controls/AutoCompleteBox.cs deleted file mode 100644 index 82e04d4b72..0000000000 --- a/v2rayn/v2rayN/v2rayN.Desktop/Controls/AutoCompleteBox.cs +++ /dev/null @@ -1,40 +0,0 @@ -using Avalonia.Input; -using Avalonia.Interactivity; - -namespace v2rayN.Desktop.Controls; - -public class AutoCompleteBox : Avalonia.Controls.AutoCompleteBox -{ - static AutoCompleteBox() - { - MinimumPrefixLengthProperty.OverrideDefaultValue(0); - } - - public AutoCompleteBox() - { - AddHandler(PointerPressedEvent, OnBoxPointerPressed, RoutingStrategies.Tunnel); - } - - private void OnBoxPointerPressed(object? sender, PointerPressedEventArgs e) - { - if (Equals(sender, this) && e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) - { - SetCurrentValue(IsDropDownOpenProperty, true); - } - } - - protected override void OnGotFocus(GotFocusEventArgs e) - { - base.OnGotFocus(e); - if (IsDropDownOpen) - { - return; - } - SetCurrentValue(IsDropDownOpenProperty, true); - } - - public void Clear() - { - SetCurrentValue(SelectedItemProperty, null); - } -} diff --git a/v2rayn/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml b/v2rayn/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml index cfc36aec52..adaaedb0a2 100644 --- a/v2rayn/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml +++ b/v2rayn/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml @@ -2,7 +2,6 @@ x:Class="v2rayN.Desktop.Views.DNSSettingWindow" xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:ctrls="clr-namespace:v2rayN.Desktop.Controls" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib" @@ -56,13 +55,13 @@ Margin="{StaticResource Margin4}" VerticalAlignment="Center" Text="{x:Static resx:ResUI.TbDomesticDNS}" /> - + IsEditable="True" /> - + IsEditable="True" /> - + IsEditable="True" /> - + IsEditable="True" /> - @@ -260,13 +252,13 @@ Margin="{StaticResource Margin4}" VerticalAlignment="Center" Text="{x:Static resx:ResUI.TbValidateDirectExpectedIPs}" /> - + IsEditable="True" /> - + IsEditable="True" /> @@ -435,11 +427,11 @@ Margin="{StaticResource Margin4}" VerticalAlignment="Center" Text="{x:Static resx:ResUI.TbSettingsDomainDNSAddress}" /> - + IsEditable="True" /> diff --git a/v2rayn/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml.cs b/v2rayn/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml.cs index ce63afa04d..ec3ad883dc 100644 --- a/v2rayn/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml.cs +++ b/v2rayn/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml.cs @@ -40,15 +40,15 @@ public partial class DNSSettingWindow : WindowBase this.Bind(ViewModel, vm => vm.AddCommonHosts, v => v.togAddCommonHosts.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.FakeIP, v => v.togFakeIP.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.BlockBindingQuery, v => v.togBlockBindingQuery.IsChecked).DisposeWith(disposables); - //this.Bind(ViewModel, vm => vm.DirectDNS, v => v.cmbDirectDNS.Text).DisposeWith(disposables); - //this.Bind(ViewModel, vm => vm.RemoteDNS, v => v.cmbRemoteDNS.Text).DisposeWith(disposables); - //this.Bind(ViewModel, vm => vm.SingboxOutboundsResolveDNS, v => v.cmbSBResolverDNS.Text).DisposeWith(disposables); - //this.Bind(ViewModel, vm => vm.SingboxFinalResolveDNS, v => v.cmbSBFinalResolverDNS.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.DirectDNS, v => v.cmbDirectDNS.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.RemoteDNS, v => v.cmbRemoteDNS.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SingboxOutboundsResolveDNS, v => v.cmbSBResolverDNS.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SingboxFinalResolveDNS, v => v.cmbSBFinalResolverDNS.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.RayStrategy4Freedom, v => v.cmbRayFreedomDNSStrategy.SelectedItem).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SingboxStrategy4Direct, v => v.cmbSBDirectDNSStrategy.SelectedItem).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SingboxStrategy4Proxy, v => v.cmbSBRemoteDNSStrategy.SelectedItem).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.Hosts, v => v.txtHosts.Text).DisposeWith(disposables); - //this.Bind(ViewModel, vm => vm.DirectExpectedIPs, v => v.cmbDirectExpectedIPs.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.DirectExpectedIPs, v => v.cmbDirectExpectedIPs.Text).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables); @@ -57,11 +57,11 @@ public partial class DNSSettingWindow : WindowBase this.Bind(ViewModel, vm => vm.UseSystemHostsCompatible, v => v.togUseSystemHostsCompatible.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.DomainStrategy4FreedomCompatible, v => v.cmbdomainStrategy4FreedomCompatible.SelectedItem).DisposeWith(disposables); - //this.Bind(ViewModel, vm => vm.DomainDNSAddressCompatible, v => v.cmbdomainDNSAddressCompatible.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.DomainDNSAddressCompatible, v => v.cmbdomainDNSAddressCompatible.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.NormalDNSCompatible, v => v.txtnormalDNSCompatible.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.DomainStrategy4Freedom2Compatible, v => v.cmbdomainStrategy4OutCompatible.SelectedItem).DisposeWith(disposables); - //this.Bind(ViewModel, vm => vm.DomainDNSAddress2Compatible, v => v.cmbdomainDNSAddress2Compatible.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.DomainDNSAddress2Compatible, v => v.cmbdomainDNSAddress2Compatible.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.NormalDNS2Compatible, v => v.txtnormalDNS2Compatible.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.TunDNS2Compatible, v => v.txttunDNS2Compatible.Text).DisposeWith(disposables); diff --git a/v2rayn/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml b/v2rayn/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml index 3c6e7231b3..727f96a0ab 100644 --- a/v2rayn/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml +++ b/v2rayn/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml @@ -2,7 +2,6 @@ x:Class="v2rayN.Desktop.Views.OptionSettingWindow" xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:ctrls="clr-namespace:v2rayN.Desktop.Controls" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib" @@ -502,12 +501,13 @@ Margin="{StaticResource Margin4}" VerticalAlignment="Center" Text="{x:Static resx:ResUI.TbSettingsCurrentFontFamily}" /> - + Margin="{StaticResource Margin4}" + IsEditable="True" /> - + Margin="{StaticResource Margin4}" + IsEditable="True" /> - + Margin="{StaticResource Margin4}" + IsEditable="True" /> - + Margin="{StaticResource Margin4}" + IsEditable="True" /> + Margin="{StaticResource Margin4}" + IsEditable="True" /> + Margin="{StaticResource Margin4}" + IsEditable="True" /> + Margin="{StaticResource Margin4}" + IsEditable="True" /> - + IsEditable="True" /> + VerticalAlignment="Center" + Orientation="Horizontal">