14
14
from pathlib import Path
15
15
from typing import Dict , Type
16
16
from socket import gethostname , getfqdn
17
+ from pexpect import TIMEOUT
17
18
import attr
18
19
from autobahn .asyncio .wamp import ApplicationRunner , ApplicationSession
19
20
20
21
from .config import ResourceConfig
21
22
from .common import ResourceEntry , enable_tcp_nodelay
22
23
from ..util import get_free_port , labgrid_version
24
+ from ..util import Timeout
23
25
24
26
25
27
__version__ = labgrid_version ()
@@ -500,6 +502,80 @@ def __attrs_post_init__(self):
500
502
super ().__attrs_post_init__ ()
501
503
self .data ['cls' ] = f"Remote{ self .cls } " .replace ("Network" , "" )
502
504
505
+ class USBJLinkExport (USBGenericExport ):
506
+ """Export J-Link device using the J-Link Remote Server"""
507
+
508
+ def __attrs_post_init__ (self ):
509
+ super ().__attrs_post_init__ ()
510
+ self .child = None
511
+ self .port = None
512
+ self .tool = '/opt/SEGGER/JLink/JLinkRemoteServer'
513
+
514
+ def _get_params (self ):
515
+ """Helper function to return parameters"""
516
+ return {
517
+ 'host' : self .host ,
518
+ 'port' : self .port ,
519
+ 'busnum' : self .local .busnum ,
520
+ 'devnum' : self .local .devnum ,
521
+ 'path' : self .local .path ,
522
+ 'vendor_id' : self .local .vendor_id ,
523
+ 'model_id' : self .local .model_id ,
524
+ }
525
+
526
+ def __del__ (self ):
527
+ if self .child is not None :
528
+ self .stop ()
529
+
530
+ def _start (self , start_params ):
531
+ """Start ``JLinkRemoteServer`` subprocess"""
532
+ assert self .local .avail
533
+ assert self .child is None
534
+ self .port = get_free_port ()
535
+
536
+ cmd = [
537
+ self .tool ,
538
+ "-Port" ,
539
+ f"{ self .port } " ,
540
+ "-select" ,
541
+ f"USB={ self .local .serial } " ,
542
+ ]
543
+ self .logger .info ("Starting JLinkRemoteServer with: %s" , " " .join (cmd ))
544
+ self .child = subprocess .Popen (cmd , stdout = subprocess .PIPE , universal_newlines = True )
545
+
546
+ # Wait for the server to be ready for incoming connections
547
+ # Waiting to open the socket with Python does not work
548
+ timeout = Timeout (10.0 )
549
+ while not timeout .expired :
550
+ line = self .child .stdout .readline ().rstrip ()
551
+ self .logger .debug (line )
552
+ if "Waiting for client connections..." in line :
553
+ break
554
+
555
+ if timeout .expired :
556
+ raise TIMEOUT (
557
+ f"Timeout of { timeout .timeout } seconds exceeded during waiting for J-Link Remote Server startup"
558
+ )
559
+ self .logger .info ("started JLinkRemoteServer for %s on port %d" , self .local .serial , self .port )
560
+
561
+ def _stop (self , start_params ):
562
+ """Stop ``JLinkRemoteServer`` subprocess"""
563
+ assert self .child
564
+ child = self .child
565
+ self .child = None
566
+ port = self .port
567
+ self .port = None
568
+ child .terminate ()
569
+ try :
570
+ child .wait (2.0 ) # JLinkRemoteServer takes about a second to react
571
+ except subprocess .TimeoutExpired :
572
+ self .logger .warning ("JLinkRemoteServer for %s still running after SIGTERM" , self .local .serial )
573
+ log_subprocess_kernel_stack (self .logger , child )
574
+ child .kill ()
575
+ child .wait (1.0 )
576
+ self .logger .info ("stopped JLinkRemoteServer for %s on port %d" , self .local .serial , port )
577
+
578
+
503
579
exports ["AndroidFastboot" ] = USBGenericExport
504
580
exports ["AndroidUSBFastboot" ] = USBGenericRemoteExport
505
581
exports ["DFUDevice" ] = USBGenericExport
@@ -512,6 +588,7 @@ def __attrs_post_init__(self):
512
588
exports ["USBSDMuxDevice" ] = USBSDMuxExport
513
589
exports ["USBSDWireDevice" ] = USBSDWireExport
514
590
exports ["USBDebugger" ] = USBGenericExport
591
+ exports ["JLinkDevice" ] = USBJLinkExport
515
592
516
593
exports ["USBMassStorage" ] = USBGenericExport
517
594
exports ["USBVideo" ] = USBGenericExport
0 commit comments