@@ -7,9 +7,8 @@ import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi }
77import { DEFAULT_WIRESHARK_IP , DEFAULT_ZEP_UDP_PORT , createWiresharkZEPFrame } from "../src/dev/wireshark" ;
88import { OTRCPDriver , type SourceRouteTableEntry } from "../src/drivers/ot-rcp-driver" ;
99import { SpinelCommandId } from "../src/spinel/commands" ;
10- import { decodeHdlcFrame } from "../src/spinel/hdlc" ;
1110import { SpinelPropertyId } from "../src/spinel/properties" ;
12- import { SPINEL_HEADER_FLG_SPINEL , type SpinelFrame , decodeSpinelFrame , encodeSpinelFrame } from "../src/spinel/spinel" ;
11+ import { SPINEL_HEADER_FLG_SPINEL , encodeSpinelFrame } from "../src/spinel/spinel" ;
1312import { SpinelStatus } from "../src/spinel/statuses" ;
1413import { MACAssociationStatus , type MACCapabilities , type MACHeader , decodeMACFrameControl , decodeMACHeader } from "../src/zigbee/mac" ;
1514import { ZigbeeConsts } from "../src/zigbee/zigbee" ;
@@ -62,7 +61,6 @@ import {
6261
6362const randomBigInt = ( ) : bigint => BigInt ( `0x${ randomBytes ( 8 ) . toString ( "hex" ) } ` ) ;
6463
65- const RESET_POWER_ON_FRAME_HEX = "7e80060070ee747e" ;
6664const COMMON_FFD_MAC_CAP : MACCapabilities = {
6765 alternatePANCoordinator : false ,
6866 deviceType : 1 ,
@@ -80,6 +78,61 @@ const COMMON_RFD_MAC_CAP: MACCapabilities = {
8078 allocateAddress : true ,
8179} ;
8280
81+ const START_FRAMES_SILABS = {
82+ protocolVersion : "7e8106010403db0a7e" ,
83+ ncpVersion :
84+ "7e820602534c2d4f50454e5448524541442f322e352e322e305f4769744875622d3166636562323235623b2045465233323b204d617220313920323032352031333a34353a343400b5dc7e" ,
85+ interfaceType : "7e83060303573a7e" ,
86+ rcpAPIVersion : "7e8406b0010a681f7e" ,
87+ rcpMinHostAPIVersion : "7e8506b101048ea77e" ,
88+ resetPowerOn : "7e80060070ee747e" ,
89+ } ;
90+ const START_FRAMES_TI = {
91+ protocolVersion : "7e8106010403db0a7e" ,
92+ ncpVersion :
93+ "7e8206024f50454e5448524541442f312e342e302d4b6f656e6b6b2d323032352e322e313b204343313358585f4343323658583b2046656220203320323032352032313a30303a303200ef147e" ,
94+ interfaceType : "7e83060303573a7e" ,
95+ rcpAPIVersion : "7e8406b0010be10e7e" ,
96+ rcpMinHostAPIVersion : "7e8506b101048ea77e" ,
97+ resetPowerOn : "7e80060070ee747e" ,
98+ } ;
99+ const FORM_FRAMES_SILABS = {
100+ phyEnabled : "7e87062001f2627e" ,
101+ phyChan : "7e88062114ff8e7e" ,
102+ phyTxPowerSet : "7e8906257d339b817e" ,
103+ mac154LAddr : "7e8a06344d325a6e6f486f5a8f327e" ,
104+ mac154SAddr : "7e8b0635000047f67e" ,
105+ mac154PANId : "7e8c0636d98579727e" ,
106+ macRxOnWhenIdleMode : "7e8d060000e68c7e" ,
107+ macRawStreamEnabled : "7e8e06370108437e" ,
108+ phyTxPowerGet : "7e8106257d3343647e" ,
109+ phyRSSIGet : "7e820626983d517e" ,
110+ phyRXSensitivityGet : "7e8306279c7a127e" ,
111+ phyCCAThresholdGet : "7e840624b5f0d37e" ,
112+ } ;
113+ const FORM_FRAMES_TI = {
114+ phyEnabled : "7e87062001f2627e" ,
115+ phyChan : "7e88062114ff8e7e" ,
116+ phyTxPowerSet : "7e890625052cf47e" ,
117+ mac154LAddr : "7e8a06344d325a6e6f486f5a8f327e" ,
118+ mac154SAddr : "7e8b0635000047f67e" ,
119+ mac154PANId : "7e8c0636d9c57d5d307e" ,
120+ macRxOnWhenIdleMode : "7e8d060000e68c7e" ,
121+ macRawStreamEnabled : "7e8e06370108437e" ,
122+ phyTxPowerGet : "7e81062505f47d317e" ,
123+ phyRSSIGet : "7e820626ef05567e" ,
124+ phyRXSensitivityGet : "7e830627a6a38c7e" ,
125+ phyCCAThresholdGet : "7e8406000297567e" ,
126+ } ;
127+ // const STOP_FRAMES_SILABS = {
128+ // macRawStreamEnabled: "7e8b063700d63c7e",
129+ // phyEnabled: "7e8c0620006eb37e",
130+ // }
131+ // const STOP_FRAMES_TI = {
132+ // macRawStreamEnabled: "7e8c063700f76b7e",
133+ // phyEnabled: "7e8d062000d5af7e",
134+ // }
135+
83136describe ( "OT RCP Driver" , ( ) => {
84137 let wiresharkSeqNum : number ;
85138 let wiresharkSocket : Socket | undefined ;
@@ -146,50 +199,50 @@ describe("OT RCP Driver", () => {
146199 return Buffer . from ( encHdlcFrame . data . subarray ( 0 , encHdlcFrame . length ) ) ;
147200 } ;
148201
149- const mockGetPropertyPayload = ( hex : string ) : SpinelFrame => decodeSpinelFrame ( decodeHdlcFrame ( Buffer . from ( hex , "hex" ) ) ) ;
150-
151- const mockStart = async ( driver : OTRCPDriver , loadState = true , timeoutReset = false ) => {
202+ const mockStart = async ( driver : OTRCPDriver , loadState = true , timeoutReset = false , frames = START_FRAMES_SILABS ) => {
152203 if ( driver ) {
153204 let loadStateSpy : ReturnType < typeof vi . spyOn > | undefined ;
154205
155206 if ( ! loadState ) {
156207 loadStateSpy = vi . spyOn ( driver , "loadState" ) . mockResolvedValue ( undefined ) ;
157208 }
158209
159- const getPropertySpy = vi
160- . spyOn ( driver , "getProperty" )
161- . mockResolvedValueOnce ( mockGetPropertyPayload ( "7e8106010403db0a7e" ) ) // PROTOCOL_VERSION
162- . mockResolvedValueOnce (
163- mockGetPropertyPayload (
164- "7e820602534c2d4f50454e5448524541442f322e352e322e305f4769744875622d3166636562323235623b2045465233323b204d617220313920323032352031333a34353a343400b5dc7e" ,
165- ) ,
166- ) // NCP_VERSION
167- . mockResolvedValueOnce ( mockGetPropertyPayload ( "7e83060303573a7e" ) ) // INTERFACE_TYPE
168- . mockResolvedValueOnce ( mockGetPropertyPayload ( "7e8406b0010a681f7e" ) ) // RCP_API_VERSION
169- . mockResolvedValueOnce ( mockGetPropertyPayload ( "7e8506b101048ea77e" ) ) ; // RCP_MIN_HOST_API_VERSION
170-
171- const waitForResetSpy = vi . spyOn ( driver , "waitForReset" ) . mockImplementationOnce ( async ( ) => {
172- const p = driver . waitForReset ( ) ;
173-
174- if ( timeoutReset ) {
175- await vi . advanceTimersByTimeAsync ( 5500 ) ;
176- } else {
177- driver . parser . _transform ( Buffer . from ( RESET_POWER_ON_FRAME_HEX , "hex" ) , "utf8" , ( ) => { } ) ;
178- await vi . advanceTimersByTimeAsync ( 10 ) ;
210+ let i = - 1 ;
211+ const orderedFrames = [
212+ frames . protocolVersion ,
213+ frames . ncpVersion ,
214+ frames . interfaceType ,
215+ frames . rcpAPIVersion ,
216+ frames . rcpMinHostAPIVersion ,
217+ frames . resetPowerOn ,
218+ ] ;
219+
220+ const reply = async ( ) => {
221+ await vi . advanceTimersByTimeAsync ( 5 ) ;
222+
223+ // skip cancel byte
224+ if ( i >= 0 ) {
225+ if ( i === 5 && timeoutReset ) {
226+ await vi . advanceTimersByTimeAsync ( 5500 ) ;
227+ }
228+
229+ driver . parser . _transform ( Buffer . from ( orderedFrames [ i ] , "hex" ) , "utf8" , ( ) => { } ) ;
230+ await vi . advanceTimersByTimeAsync ( 5 ) ;
179231 }
180232
181- await p ;
182- } ) ;
233+ i ++ ;
183234
184- await driver . start ( ) ;
185-
186- nextTidFromStartup += 1 ; // sendCommand RESET
235+ if ( i === orderedFrames . length ) {
236+ driver . writer . removeListener ( "data" , reply ) ;
237+ }
238+ } ;
187239
240+ driver . writer . on ( "data" , reply ) ;
241+ await driver . start ( ) ;
188242 loadStateSpy ?. mockRestore ( ) ;
189- getPropertySpy . mockRestore ( ) ;
190- waitForResetSpy . mockRestore ( ) ;
191-
192243 await vi . advanceTimersByTimeAsync ( 100 ) ; // flush
244+
245+ nextTidFromStartup = driver . currentSpinelTID + 1 ;
193246 }
194247 } ;
195248
@@ -207,17 +260,41 @@ describe("OT RCP Driver", () => {
207260
208261 await vi . advanceTimersByTimeAsync ( 100 ) ; // flush
209262 }
263+
264+ nextTidFromStartup = 1 ;
210265 } ;
211266
212- const mockFormNetwork = async ( driver : OTRCPDriver , registerTimers = false ) => {
267+ const mockFormNetwork = async ( driver : OTRCPDriver , registerTimers = false , frames = FORM_FRAMES_SILABS ) => {
213268 if ( driver ) {
214- const setPropertySpy = vi . spyOn ( driver , "setProperty" ) . mockResolvedValue ( ) ;
215- const getPropertySpy = vi
216- . spyOn ( driver , "getProperty" )
217- . mockResolvedValueOnce ( mockGetPropertyPayload ( "7e8106257d3343647e" ) ) // PHY_TX_POWER
218- . mockResolvedValueOnce ( mockGetPropertyPayload ( "7e82062695d88a7e" ) ) // PHY_RSSI
219- . mockResolvedValueOnce ( mockGetPropertyPayload ( "7e8306279c7a127e" ) ) // PHY_RX_SENSITIVITY
220- . mockResolvedValueOnce ( mockGetPropertyPayload ( "7e840624b5f0d37e" ) ) ; // PHY_CCA_THRESHOLD
269+ let i = 0 ;
270+ const orderedFrames = [
271+ frames . phyEnabled ,
272+ frames . phyChan ,
273+ frames . phyTxPowerSet ,
274+ frames . mac154LAddr ,
275+ frames . mac154SAddr ,
276+ frames . mac154PANId ,
277+ frames . macRxOnWhenIdleMode ,
278+ frames . macRawStreamEnabled ,
279+ frames . phyTxPowerGet ,
280+ frames . phyRSSIGet ,
281+ frames . phyRXSensitivityGet ,
282+ frames . phyCCAThresholdGet ,
283+ ] ;
284+
285+ const reply = async ( ) => {
286+ await vi . advanceTimersByTimeAsync ( 5 ) ;
287+ driver . parser . _transform ( Buffer . from ( orderedFrames [ i ] , "hex" ) , "utf8" , ( ) => { } ) ;
288+ await vi . advanceTimersByTimeAsync ( 5 ) ;
289+
290+ i ++ ;
291+
292+ if ( i === orderedFrames . length ) {
293+ driver . writer . removeListener ( "data" , reply ) ;
294+ }
295+ } ;
296+
297+ driver . writer . on ( "data" , reply ) ;
221298
222299 let registerTimersSpy : ReturnType < typeof vi . spyOn > | undefined ;
223300
@@ -229,11 +306,11 @@ describe("OT RCP Driver", () => {
229306
230307 await driver . formNetwork ( ) ;
231308
232- setPropertySpy . mockRestore ( ) ;
233- getPropertySpy . mockRestore ( ) ;
234309 registerTimersSpy ?. mockRestore ( ) ;
235310
236311 await vi . advanceTimersByTimeAsync ( 100 ) ; // flush
312+
313+ nextTidFromStartup = driver . currentSpinelTID + 1 ;
237314 }
238315 } ;
239316
@@ -269,7 +346,7 @@ describe("OT RCP Driver", () => {
269346 expect ( sendZigbeeNWKLinkStatusSpy ) . toHaveBeenCalledTimes ( 1 + 1 ) ; // *2 by spy mock
270347 expect ( sendZigbeeNWKRouteReqSpy ) . toHaveBeenCalledTimes ( 1 + 1 ) ; // *2 by spy mock
271348
272- nextTidFromStartup += 2 ;
349+ nextTidFromStartup = driver . currentSpinelTID + 1 ;
273350
274351 return [ linksSpy , manyToOneSpy , destination16Spy ] ;
275352 }
@@ -601,9 +678,11 @@ describe("OT RCP Driver", () => {
601678 await expect ( driver . resetNetwork ( ) ) . rejects . toThrow ( "Cannot reset network after state already loaded" ) ;
602679 } ) ;
603680
604- it ( "forms network" , async ( ) => {
605- await mockStart ( driver ) ;
606- await mockFormNetwork ( driver ) ;
681+ it ( "starts & forms network - Silabs" , async ( ) => {
682+ const consoleInfoSpy = vi . spyOn ( console , "info" ) ;
683+
684+ await mockStart ( driver , true , false , START_FRAMES_SILABS ) ;
685+ await mockFormNetwork ( driver , false , FORM_FRAMES_SILABS ) ;
607686
608687 expect ( driver . isNetworkUp ( ) ) . toStrictEqual ( true ) ;
609688 expect ( driver . protocolVersionMajor ) . toStrictEqual ( 4 ) ;
@@ -612,6 +691,33 @@ describe("OT RCP Driver", () => {
612691 expect ( driver . interfaceType ) . toStrictEqual ( 3 ) ;
613692 expect ( driver . rcpAPIVersion ) . toStrictEqual ( 10 ) ;
614693 expect ( driver . rcpMinHostAPIVersion ) . toStrictEqual ( 4 ) ;
694+
695+ expect ( consoleInfoSpy ) . toHaveBeenCalledWith (
696+ expect . stringContaining (
697+ "ot-rcp-driver: ======== Network started (PHY: txPower=19dBm rssi=-104dBm rxSensitivity=-100dBm ccaThreshold=-75dBm) ========" ,
698+ ) ,
699+ ) ;
700+ } ) ;
701+
702+ it ( "starts & forms network - TI" , async ( ) => {
703+ const consoleInfoSpy = vi . spyOn ( console , "info" ) ;
704+
705+ await mockStart ( driver , true , false , START_FRAMES_TI ) ;
706+ await mockFormNetwork ( driver , false , FORM_FRAMES_TI ) ;
707+
708+ expect ( driver . isNetworkUp ( ) ) . toStrictEqual ( true ) ;
709+ expect ( driver . protocolVersionMajor ) . toStrictEqual ( 4 ) ;
710+ expect ( driver . protocolVersionMinor ) . toStrictEqual ( 3 ) ;
711+ expect ( driver . ncpVersion ) . toStrictEqual ( "OPENTHREAD/1.4.0-Koenkk-2025.2.1; CC13XX_CC26XX; Feb 3 2025 21:00:02" ) ;
712+ expect ( driver . interfaceType ) . toStrictEqual ( 3 ) ;
713+ expect ( driver . rcpAPIVersion ) . toStrictEqual ( 11 ) ;
714+ expect ( driver . rcpMinHostAPIVersion ) . toStrictEqual ( 4 ) ;
715+
716+ expect ( consoleInfoSpy ) . toHaveBeenCalledWith (
717+ expect . stringContaining (
718+ "ot-rcp-driver: ======== Network started (PHY: txPower=5dBm rssi=-17dBm rxSensitivity=-90dBm ccaThreshold=undefineddBm) ========" ,
719+ ) ,
720+ ) ;
615721 } ) ;
616722
617723 it ( "throws when trying to form network before state is loaded" , async ( ) => {
0 commit comments