-
Notifications
You must be signed in to change notification settings - Fork 161
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Driver/Resource: Add Segger J-Link support
Add a driver and resource for the Segger J-Link debug probe. Export the J-Link resource over IP using the J-Link Remote Server. Signed-off-by: Paul Vittorino <paul.vittorino@garmin.com>
- Loading branch information
1 parent
c3253c4
commit 8ed0405
Showing
10 changed files
with
291 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
from importlib import import_module | ||
import socket | ||
import attr | ||
|
||
from ..factory import target_factory | ||
from ..resource.remote import NetworkJLinkDevice | ||
from ..util.proxy import proxymanager | ||
from .common import Driver | ||
|
||
@target_factory.reg_driver | ||
@attr.s(eq=False) | ||
class JLinkDriver(Driver): | ||
bindings = {"jlink_device": {"JLinkDevice", "NetworkJLinkDevice"}, } | ||
|
||
def __attrs_post_init__(self): | ||
super().__attrs_post_init__() | ||
self._module = import_module('pylink') | ||
self.jlink = None | ||
|
||
def on_activate(self): | ||
self.jlink = self._module.JLink() | ||
|
||
if isinstance(self.jlink_device, NetworkJLinkDevice): | ||
# we can only forward if the backend knows which port to use | ||
host, port = proxymanager.get_host_and_port(self.jlink_device) | ||
# The J-Link client software does not support host names | ||
ip_addr = socket.gethostbyname(host) | ||
|
||
# Workaround for Debian's /etc/hosts entry | ||
# https://www.debian.org/doc/manuals/debian-reference/ch05.en.html#_the_hostname_resolution | ||
if ip_addr == "127.0.1.1": | ||
ip_addr = "127.0.0.1" | ||
self.jlink.open(ip_addr=f"{ip_addr}:{port}") | ||
else: | ||
self.jlink.open(serial_no=self.jlink_device.serial) | ||
|
||
def on_deactivate(self): | ||
self.jlink.close() | ||
self.jlink = None | ||
|
||
@Driver.check_active | ||
def get_interface(self): | ||
return self.jlink |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
import contextlib | ||
import io | ||
import pytest | ||
import subprocess | ||
from unittest.mock import MagicMock | ||
from unittest.mock import Mock | ||
from unittest.mock import patch | ||
|
||
from labgrid.remote.exporter import USBJLinkExport | ||
from labgrid.resource.remote import NetworkJLinkDevice | ||
from labgrid.resource.udev import JLinkDevice | ||
from labgrid.driver.jlinkdriver import JLinkDriver | ||
|
||
FAKE_SERIAL = 123456789 | ||
MATCH = {"ID_SERIAL_SHORT": f"000{FAKE_SERIAL}"} | ||
|
||
|
||
class Popen_mock(): | ||
"""Mock of Popen object to mimmic JLinkRemoteServer output""" | ||
|
||
def __init__(self, args, **kwargs): | ||
assert "JLinkRemoteServer" in args[0] | ||
assert args[1] == "-Port" | ||
# Since args[2] is dynamic do not check it | ||
assert args[3] == "-select" | ||
assert args[4] == f"USB={FAKE_SERIAL}" | ||
self.wait_called = False | ||
|
||
stdout = io.StringIO( | ||
"SEGGER J-Link Remote Server V7.84a\n" | ||
"Compiled Dec 22 2022 16:13:52\n" | ||
"\n" | ||
"'q' to quit '?' for help\n" | ||
"\n" | ||
f"Connected to J-Link with S/N {FAKE_SERIAL}\n" | ||
"\n" | ||
"Waiting for client connections...\n" | ||
) | ||
|
||
def kill(self): | ||
pass | ||
|
||
def poll(self): | ||
return 0 | ||
|
||
def terminate(self): | ||
pass | ||
|
||
def wait(self, timeout=None): | ||
# Only timeout on the first call to exercise the error handling code. | ||
if not self.wait_called: | ||
self.wait_called = True | ||
raise subprocess.TimeoutExpired("JLinkRemoteServer", timeout) | ||
|
||
|
||
def test_jlink_resource(target): | ||
r = JLinkDevice(target, name=None, match=MATCH) | ||
|
||
|
||
@patch('subprocess.Popen', Popen_mock) | ||
def test_jlink_export_start(target): | ||
config = {'avail': True, 'cls': "JLinkDevice", 'params': {'match': MATCH}, } | ||
e = USBJLinkExport(config) | ||
e.local.avail = True | ||
e.local.serial = FAKE_SERIAL | ||
|
||
e.start() | ||
# Exercise the __del__ method which also exercises stop() | ||
del e | ||
|
||
|
||
@patch('subprocess.Popen', Popen_mock) | ||
def test_jlink_driver(target): | ||
pytest.importorskip("pylink") | ||
device = JLinkDevice(target, name=None, match=MATCH) | ||
device.avail = True | ||
device.serial = FAKE_SERIAL | ||
driver = JLinkDriver(target, name=None) | ||
|
||
with patch('pylink.JLink') as JLinkMock: | ||
instance = JLinkMock.return_value | ||
target.activate(driver) | ||
instance.open.assert_called_once_with(serial_no=FAKE_SERIAL) | ||
intf = driver.get_interface() | ||
assert(isinstance(intf, Mock)) | ||
target.deactivate(driver) | ||
instance.close.assert_called_once_with() | ||
|
||
|
||
@patch('subprocess.Popen', Popen_mock) | ||
def test_jlink_driver_network_device(target): | ||
pytest.importorskip("pylink") | ||
device = NetworkJLinkDevice(target, None, host='127.0.1.1', port=12345, busnum=0, devnum=1, path='0:1', vendor_id=0x0, model_id=0x0,) | ||
device.avail = True | ||
driver = JLinkDriver(target, name=None) | ||
assert (isinstance(driver, JLinkDriver)) | ||
|
||
with patch('pylink.JLink') as JLinkMock: | ||
instance = JLinkMock.return_value | ||
# Call on_activate directly since activating the driver via the target does not work during testing | ||
driver.on_activate() | ||
instance.open.assert_called_once_with(ip_addr='127.0.0.1:12345') | ||
driver.on_deactivate() | ||
instance.close.assert_called_once_with() |