xref: /linux/tools/testing/selftests/drivers/net/hw/nic_timestamp.py (revision d8f87aa5fa0a4276491fa8ef436cd22605a3f9ba)
1#!/usr/bin/env python3
2# SPDX-License-Identifier: GPL-2.0
3# pylint: disable=locally-disabled, invalid-name, attribute-defined-outside-init, too-few-public-methods
4
5"""
6Tests related to configuration of HW timestamping
7"""
8
9import errno
10import ctypes
11import fcntl
12import socket
13from lib.py import ksft_run, ksft_exit, ksft_ge, ksft_eq, KsftSkipEx
14from lib.py import NetDrvEnv, EthtoolFamily, NlError
15
16
17SIOCSHWTSTAMP = 0x89b0
18SIOCGHWTSTAMP = 0x89b1
19class hwtstamp_config(ctypes.Structure):
20    """ Python copy of struct hwtstamp_config """
21    _fields_ = [
22        ("flags", ctypes.c_int),
23        ("tx_type", ctypes.c_int),
24        ("rx_filter", ctypes.c_int),
25    ]
26
27
28class ifreq(ctypes.Structure):
29    """ Python copy of struct ifreq """
30    _fields_ = [
31        ("ifr_name", ctypes.c_char * 16),
32        ("ifr_data", ctypes.POINTER(hwtstamp_config)),
33    ]
34
35
36def __get_hwtimestamp_support(cfg):
37    """ Retrieve supported configuration information """
38
39    try:
40        tsinfo = cfg.ethnl.tsinfo_get({'header': {'dev-name': cfg.ifname}})
41    except NlError as e:
42        if e.error == errno.EOPNOTSUPP:
43            raise KsftSkipEx("timestamping configuration is not supported") from e
44        raise
45
46    ctx = {}
47    tx = tsinfo.get('tx-types', {})
48    rx = tsinfo.get('rx-filters', {})
49
50    bits = tx.get('bits', {})
51    ctx['tx'] = bits.get('bit', [])
52    bits = rx.get('bits', {})
53    ctx['rx'] = bits.get('bit', [])
54    return ctx
55
56
57def __get_hwtimestamp_config_ioctl(cfg):
58    """ Retrieve current TS configuration information (via ioctl) """
59
60    config = hwtstamp_config()
61
62    req = ifreq()
63    req.ifr_name = cfg.ifname.encode()
64    req.ifr_data = ctypes.pointer(config)
65
66    try:
67        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
68        fcntl.ioctl(sock.fileno(), SIOCGHWTSTAMP, req)
69        sock.close()
70
71    except OSError as e:
72        if e.errno == errno.EOPNOTSUPP:
73            raise KsftSkipEx("timestamping configuration is not supported via ioctl") from e
74        raise
75    return config
76
77
78def __get_hwtimestamp_config(cfg):
79    """ Retrieve current TS configuration information (via netLink) """
80
81    try:
82        tscfg = cfg.ethnl.tsconfig_get({'header': {'dev-name': cfg.ifname}})
83    except NlError as e:
84        if e.error == errno.EOPNOTSUPP:
85            raise KsftSkipEx("timestamping configuration is not supported via netlink") from e
86        raise
87    return tscfg
88
89
90def __set_hwtimestamp_config_ioctl(cfg, ts):
91    """ Setup new TS configuration information (via ioctl) """
92    config = hwtstamp_config()
93    config.rx_filter = ts['rx-filters']['bits']['bit'][0]['index']
94    config.tx_type = ts['tx-types']['bits']['bit'][0]['index']
95    req = ifreq()
96    req.ifr_name = cfg.ifname.encode()
97    req.ifr_data = ctypes.pointer(config)
98    try:
99        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
100        fcntl.ioctl(sock.fileno(), SIOCSHWTSTAMP, req)
101        sock.close()
102
103    except OSError as e:
104        if e.errno == errno.EOPNOTSUPP:
105            raise KsftSkipEx("timestamping configuration is not supported via ioctl") from e
106        raise
107
108
109def __set_hwtimestamp_config(cfg, ts):
110    """ Setup new TS configuration information (via netlink) """
111
112    ts['header'] = {'dev-name': cfg.ifname}
113    try:
114        res = cfg.ethnl.tsconfig_set(ts)
115    except NlError as e:
116        if e.error == errno.EOPNOTSUPP:
117            raise KsftSkipEx("timestamping configuration is not supported via netlink") from e
118        raise
119    return res
120
121
122def __perform_hwtstamp_tx(cfg, is_ioctl):
123    """
124    Test TX timestamp configuration via either netlink or ioctl.
125    The driver should apply provided config and report back proper state.
126    """
127
128    orig_tscfg = __get_hwtimestamp_config(cfg)
129    ts = __get_hwtimestamp_support(cfg)
130    tx = ts['tx']
131    for t in tx:
132        res = None
133        tscfg = orig_tscfg
134        tscfg['tx-types']['bits']['bit'] = [t]
135        if is_ioctl:
136            __set_hwtimestamp_config_ioctl(cfg, tscfg)
137        else:
138            res = __set_hwtimestamp_config(cfg, tscfg)
139        if res is None:
140            res = __get_hwtimestamp_config(cfg)
141        resioctl = __get_hwtimestamp_config_ioctl(cfg)
142        ksft_eq(res['tx-types']['bits']['bit'], [t])
143        ksft_eq(resioctl.tx_type, t['index'])
144    __set_hwtimestamp_config(cfg, orig_tscfg)
145
146def test_hwtstamp_tx_netlink(cfg):
147    """
148    Test TX timestamp configuration setup via netlink.
149    The driver should apply provided config and report back proper state.
150    """
151    __perform_hwtstamp_tx(cfg, False)
152
153
154def test_hwtstamp_tx_ioctl(cfg):
155    """
156    Test TX timestamp configuration setup via ioctl.
157    The driver should apply provided config and report back proper state.
158    """
159    __perform_hwtstamp_tx(cfg, True)
160
161
162def __perform_hwtstamp_rx(cfg, is_ioctl):
163    """
164    Test RX timestamp configuration.
165    The filter configuration is taken from the list of supported filters.
166    The driver should apply the config without error and report back proper state.
167    Some extension of the timestamping scope is allowed for PTP filters.
168    """
169
170    orig_tscfg = __get_hwtimestamp_config(cfg)
171    ts = __get_hwtimestamp_support(cfg)
172    rx = ts['rx']
173    for r in rx:
174        res = None
175        tscfg = orig_tscfg
176        tscfg['rx-filters']['bits']['bit'] = [r]
177        if is_ioctl:
178            __set_hwtimestamp_config_ioctl(cfg, tscfg)
179        else:
180            res = __set_hwtimestamp_config(cfg, tscfg)
181        if res is None:
182            res = __get_hwtimestamp_config(cfg)
183        resioctl = __get_hwtimestamp_config_ioctl(cfg)
184        ksft_eq(resioctl.rx_filter, res['rx-filters']['bits']['bit'][0]['index'])
185        if r['index'] == 0 or r['index'] == 1:
186            ksft_eq(res['rx-filters']['bits']['bit'][0]['index'], r['index'])
187        else:
188            # the driver can fallback to some value which has higher coverage for timestamping
189            ksft_ge(res['rx-filters']['bits']['bit'][0]['index'], r['index'])
190    __set_hwtimestamp_config(cfg, orig_tscfg)
191
192
193def test_hwtstamp_rx_netlink(cfg):
194    """
195    Test RX timestamp configuration via netlink.
196    The filter configuration is taken from the list of supported filters.
197    The driver should apply the config without error and report back proper state.
198    Some extension of the timestamping scope is allowed for PTP filters.
199    """
200    __perform_hwtstamp_rx(cfg, False)
201
202
203def test_hwtstamp_rx_ioctl(cfg):
204    """
205    Test RX timestamp configuration via ioctl.
206    The filter configuration is taken from the list of supported filters.
207    The driver should apply the config without error and report back proper state.
208    Some extension of the timestamping scope is allowed for PTP filters.
209    """
210    __perform_hwtstamp_rx(cfg, True)
211
212
213def main() -> None:
214    """ Ksft boiler plate main """
215
216    with NetDrvEnv(__file__, nsim_test=False) as cfg:
217        cfg.ethnl = EthtoolFamily()
218        ksft_run([test_hwtstamp_tx_ioctl, test_hwtstamp_tx_netlink,
219                  test_hwtstamp_rx_ioctl, test_hwtstamp_rx_netlink],
220                 args=(cfg,))
221        ksft_exit()
222
223
224if __name__ == "__main__":
225    main()
226