From b1c35d7a8bf69f18ab1f6c326ef1e15de8f0df22 Mon Sep 17 00:00:00 2001 From: Andre Mauricio Zelak Date: Thu, 9 Oct 2025 18:51:47 -0300 Subject: [PATCH] pynetlink - Add support to configure pin via Netlink Three new APIs were created to configure a pin via Netlink: * _set_pin (internal and debug purpose only) * set_pin_direction * set_pin_priority Test Cases: Pass: Unit tests Pass: In SX system, configure the pin direction and confirm the change via Netlink, and CGU. Pass: In SX system, configure the pin priority and confirm the change via Netlink, and CGU. Change-Id: I25693934f5710aa6ff334290e5a7e3f060de7038 Signed-off-by: Andre Mauricio Zelak --- python/pynetlink/src/pynetlink/dpll/dpll.py | 72 ++++++++ .../src/pynetlink/tests/dpll_test.py | 166 +++++++++++++++++- 2 files changed, 236 insertions(+), 2 deletions(-) diff --git a/python/pynetlink/src/pynetlink/dpll/dpll.py b/python/pynetlink/src/pynetlink/dpll/dpll.py index dfe96da88..5f0ac5866 100644 --- a/python/pynetlink/src/pynetlink/dpll/dpll.py +++ b/python/pynetlink/src/pynetlink/dpll/dpll.py @@ -13,6 +13,7 @@ from ..common import NetlinkException from ..common import NetlinkFactory from .constants import DeviceFields from .constants import PinFields +from .constants import PinParentFields from .devices import DpllDevice from .devices import DpllDevices from .pins import DpllPins @@ -23,6 +24,7 @@ class DPLLCommands: __slots__ = () DEVICE_GET = 'device-get' PIN_GET = 'pin-get' + PIN_SET = 'pin-set' class NetlinkDPLL: @@ -177,3 +179,73 @@ class NetlinkDPLL: """ return DpllPins.loadPins(self._get_all_devices(), self._get_all_pins()) + + def _set_pin(self, pin_info: dict): + """Change the pin configuration. + + Changes the pin configuration according to the given pin information. + Not all pin field are mutable, use _get_pin_by_id() to print the pin + capabilities, and check which pin fields can be changed. + + Raises NetlinkException class in case of error. + """ + + try: + result = self._ynl.do(DPLLCommands.PIN_SET, pin_info) + + return result + + except NlError as e: + raise NetlinkException(e) + + def set_pin_direction(self, pin_id: int, pin_direction: int): + """Change pin direction. + + Change the pin direction. All pin parents are changed at the same time. + + Raises NetlinkException class in case of error. + """ + + pin_info = { + PinFields.ID: pin_id, + PinFields.PARENT_DEVICE: [] + } + + # get pin's parent ids, and set the direction in all of them. + pins = self.get_pins_by_id(pin_id) + parent_ids = list(pin.dev_id for pin in pins) + for parent_id in parent_ids: + pin_info[PinFields.PARENT_DEVICE].append( + { + PinParentFields.PARENT_ID: parent_id, + PinParentFields.DIRECTION: pin_direction + } + ) + + return self._set_pin(pin_info) + + def set_pin_priority(self, pin_id: int, pin_priority: int): + """Change pin priority + + Change the pin priority. All pin parents are changed at the same time. + + Raises NetlinkException class in case of error. + """ + + pin_info = { + PinFields.ID: pin_id, + PinFields.PARENT_DEVICE: [] + } + + # get pin's parent ids, and set the priority in all of them. + pins = self.get_pins_by_id(pin_id) + parent_ids = list(pin.dev_id for pin in pins) + for parent_id in parent_ids: + pin_info[PinFields.PARENT_DEVICE].append( + { + PinParentFields.PARENT_ID: parent_id, + PinParentFields.PRIORITY: pin_priority + } + ) + + return self._set_pin(pin_info) diff --git a/python/pynetlink/src/pynetlink/tests/dpll_test.py b/python/pynetlink/src/pynetlink/tests/dpll_test.py index 31fe20630..d8363d71b 100644 --- a/python/pynetlink/src/pynetlink/tests/dpll_test.py +++ b/python/pynetlink/src/pynetlink/tests/dpll_test.py @@ -23,6 +23,7 @@ from ..dpll import DpllDevice from ..dpll import DeviceFields from ..dpll import DpllPins from ..dpll import PinFields +from ..dpll import PinParentFields class NetlinkDPLLTestCase(TestCase): @@ -157,6 +158,9 @@ class NetlinkDPLLTestCase(TestCase): f'#items: Should be {len(expected_device_ids)} Got {len(devices)}') for device in devices: + self.assertEqual(device.dev_clock_id, + clock_id, + f"Wrong clock ID: Expected {clock_id}. Got {device.dev_clock_id}") self.assertIn(device.dev_id, expected_device_ids, f'Contains wrong device: {device.dev_id}') @@ -242,8 +246,8 @@ class NetlinkDPLLTestCase(TestCase): @patch.object(YnlFamily, 'dump') def test_get_pins_with_specific_id(self, mock_ynl_dump: Mock, mock_ynl_do: Mock): - pin_pict_idx = random.randint(0, 8) - pin_dict = self.dump_get_pins[pin_pict_idx] + pin_dict_idx = random.randint(0, 8) + pin_dict = self.dump_get_pins[pin_dict_idx] pin_id = pin_dict[PinFields.ID] mock_ynl_dump.return_value = self.dump_get_devices @@ -283,6 +287,9 @@ class NetlinkDPLLTestCase(TestCase): f'Should return DpllPins instance. Got {type(pins)}') for pin in pins: + self.assertEqual(pin.dev_clock_id, + clock_id, + f"Wrong clock ID: Expected {clock_id}. Got {pin.dev_clock_id}") self.assertIn(pin.pin_id, expected_pin_ids, f'Contains wrong pin: {pin.pin_id}') @@ -311,3 +318,158 @@ class NetlinkDPLLTestCase(TestCase): call(DPLLCommands.DEVICE_GET, {}), call(DPLLCommands.PIN_GET, {}) ]) + + @patch.object(YnlFamily, 'do') + def test_set_pin(self, mock_ynl_do: Mock): + + expected_result = None + mock_ynl_do.return_value = expected_result + pin_info = { + 'id': 34, + 'parent-device': [ + { + 'parent-id': 2, + 'direction': 1, + 'priority': 2 + }, + { + 'parent-id': 3, + 'direction': 1, + 'priority': 2 + } + ] + } + + dpll = NetlinkDPLL() + result = dpll._set_pin(pin_info) # pylint: disable=W0212 + + self.assertEqual(expected_result, + result, + f'Wrong _set_pin result: ' + f'Expected {expected_result}. Got {result}') + + mock_ynl_do.assert_called_once_with(DPLLCommands.PIN_SET, pin_info) + + @patch.object(YnlFamily, 'do') + @patch.object(YnlFamily, 'dump') + def test_set_pin_direction(self, mock_ynl_dump: Mock, mock_ynl_do: Mock): + + pin_dict_idx = random.randint(0, 8) + pin_dict = self.dump_get_pins[pin_dict_idx] + pin_id = pin_dict[PinFields.ID] + pin_direction = random.randint(0, 2) + parent_ids = list(parent[PinParentFields.PARENT_ID] + for parent in pin_dict[PinFields.PARENT_DEVICE]) + expected_result = None + + mock_ynl_dump.return_value = self.dump_get_devices + mock_ynl_do.side_effect = [pin_dict, None] + + dpll = NetlinkDPLL() + result = dpll.set_pin_direction(pin_id, pin_direction) + + self.assertEqual(expected_result, + result, + f'Wrong _set_pin result: ' + f'Expected {expected_result}. Got {result}') + + mock_ynl_dump.assert_called_once_with(DPLLCommands.DEVICE_GET, {}) + + # Verify the do pin-set command call. + args, _ = mock_ynl_do.call_args + call_command = args[0] + self.assertEqual(DPLLCommands.PIN_SET, + call_command, + f'Wrong do command: ' + f'Expected {DPLLCommands.PIN_SET}. Got {call_command}') + + call_args = args[1] + sorted_call_args = { + PinFields.ID: call_args[PinFields.ID], + PinFields.PARENT_DEVICE: sorted( + call_args[PinFields.PARENT_DEVICE], + key=lambda x: x[PinParentFields.PARENT_ID] + ) + } + expected_call_args = { + PinFields.ID: pin_id, + PinFields.PARENT_DEVICE: list( + { + PinParentFields.PARENT_ID: id, + PinParentFields.DIRECTION: pin_direction + } + for id in sorted(parent_ids) + ) + } + self.assertDictEqual(sorted_call_args, + expected_call_args, + f'Wrong call arguments: ' + f'Expected {expected_call_args}. Got {sorted_call_args}') + + @patch.object(YnlFamily, 'do') + @patch.object(YnlFamily, 'dump') + def test_set_pin_priority(self, mock_ynl_dump: Mock, mock_ynl_do: Mock): + + pin_dict_idx = random.randint(0, 8) + pin_dict = self.dump_get_pins[pin_dict_idx] + pin_id = pin_dict[PinFields.ID] + pin_priority = random.randint(0, 7) + pin_info = { + PinFields.ID: pin_id, + PinFields.PARENT_DEVICE: [] + } + parent_ids = list(parent[PinParentFields.PARENT_ID] + for parent in pin_dict[PinFields.PARENT_DEVICE]) + for parent_id in parent_ids: + pin_info[PinFields.PARENT_DEVICE].append( + { + PinParentFields.PARENT_ID: parent_id, + PinParentFields.PRIORITY: pin_priority + } + ) + expected_result = None + + mock_ynl_dump.return_value = self.dump_get_devices + mock_ynl_do.side_effect = [pin_dict, None] + + dpll = NetlinkDPLL() + result = dpll.set_pin_priority(pin_id, pin_priority) + + self.assertEqual(expected_result, + result, + f'Wrong _set_pin result: ' + f'Expected {expected_result}. Got {result}') + + mock_ynl_dump.assert_called_once_with(DPLLCommands.DEVICE_GET, {}) + + # Verify the do pin-set command call. + args, _ = mock_ynl_do.call_args + call_command = args[0] + # Verify the command is pin-set + self.assertEqual(DPLLCommands.PIN_SET, + call_command, + f'Wrong do command: ' + f'Expected {DPLLCommands.PIN_SET}. Got {call_command}') + # Verify the pin id + call_args = args[1] + sorted_call_args = { + PinFields.ID: call_args[PinFields.ID], + PinFields.PARENT_DEVICE: sorted( + call_args[PinFields.PARENT_DEVICE], + key=lambda x: x[PinParentFields.PARENT_ID] + ) + } + expected_call_args = { + PinFields.ID: pin_id, + PinFields.PARENT_DEVICE: list( + { + PinParentFields.PARENT_ID: id, + PinParentFields.PRIORITY: pin_priority + } + for id in sorted(parent_ids) + ) + } + self.assertDictEqual(sorted_call_args, + expected_call_args, + f'Wrong call arguments: ' + f'Expected {expected_call_args}. Got {sorted_call_args}')