1 /*
2  * Copyright (c) 2024 BayLibre SAS
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/logging/log.h>
8 LOG_MODULE_REGISTER(net_ptp_btca, CONFIG_PTP_LOG_LEVEL);
9 
10 #include <string.h>
11 
12 #include "btca.h"
13 #include "clock.h"
14 #include "port.h"
15 
16 #define A_BETTER	  (1)
17 #define A_BETTER_TOPOLOGY (2)
18 #define B_BETTER	  (-1)
19 #define B_BETTER_TOPOLOGY (-2)
20 
btca_port_id_cmp(const struct ptp_port_id * p1,const struct ptp_port_id * p2)21 static int btca_port_id_cmp(const struct ptp_port_id *p1, const struct ptp_port_id *p2)
22 {
23 	int diff = memcmp(&p1->clk_id, &p2->clk_id, sizeof(p1->clk_id));
24 
25 	if (diff == 0) {
26 		diff = p1->port_number - p2->port_number;
27 	}
28 
29 	return diff;
30 }
31 
btca_ds_cmp2(const struct ptp_dataset * a,const struct ptp_dataset * b)32 static int btca_ds_cmp2(const struct ptp_dataset *a, const struct ptp_dataset *b)
33 {
34 	int diff;
35 
36 	if (b->steps_rm + 1 < a->steps_rm) {
37 		return B_BETTER;
38 	}
39 	if (a->steps_rm + 1 < b->steps_rm) {
40 		return A_BETTER;
41 	}
42 	if (a->steps_rm > b->steps_rm) {
43 		diff = btca_port_id_cmp(&a->receiver, &a->sender);
44 		if (diff > 0) {
45 			return B_BETTER_TOPOLOGY;
46 		}
47 		if (diff < 0) {
48 			return B_BETTER;
49 		}
50 		/* error-1 */
51 		return 0;
52 	}
53 	if (a->steps_rm < b->steps_rm) {
54 		diff = btca_port_id_cmp(&b->receiver, &b->sender);
55 		if (diff > 0) {
56 			return A_BETTER_TOPOLOGY;
57 		}
58 		if (diff < 0) {
59 			return A_BETTER;
60 		}
61 		/* error-1 */
62 		return 0;
63 	}
64 
65 	diff = btca_port_id_cmp(&a->sender, &b->sender);
66 	if (diff > 0) {
67 		return B_BETTER_TOPOLOGY;
68 	}
69 	if (diff < 0) {
70 		return A_BETTER_TOPOLOGY;
71 	}
72 
73 	if (a->receiver.port_number > b->receiver.port_number) {
74 		return B_BETTER_TOPOLOGY;
75 	}
76 	if (a->receiver.port_number > b->receiver.port_number) {
77 		return A_BETTER_TOPOLOGY;
78 	}
79 	/* error-2 */
80 	return 0;
81 }
82 
ptp_btca_ds_cmp(const struct ptp_dataset * a,const struct ptp_dataset * b)83 int ptp_btca_ds_cmp(const struct ptp_dataset *a, const struct ptp_dataset *b)
84 {
85 	if (a == b) {
86 		return 0;
87 	}
88 	if (a && !b) {
89 		return A_BETTER;
90 	}
91 	if (!a && b) {
92 		return B_BETTER;
93 	}
94 
95 	int id_diff = memcmp(&a->clk_id, &b->clk_id, sizeof(a->clk_id));
96 
97 	if (id_diff == 0) {
98 		return btca_ds_cmp2(a, b);
99 	}
100 	if (a->priority1 > b->priority1) {
101 		return B_BETTER;
102 	}
103 	if (a->clk_quality.class > b->clk_quality.class) {
104 		return B_BETTER;
105 	}
106 	if (a->clk_quality.accuracy > b->clk_quality.accuracy) {
107 		return B_BETTER;
108 	}
109 	if (a->clk_quality.offset_scaled_log_variance > b->clk_quality.offset_scaled_log_variance) {
110 		return B_BETTER;
111 	}
112 	if (a->priority2 > b->priority2) {
113 		return B_BETTER;
114 	}
115 
116 	return id_diff < 0 ? A_BETTER : B_BETTER;
117 }
118 
ptp_btca_state_decision(struct ptp_port * port)119 enum ptp_port_state ptp_btca_state_decision(struct ptp_port *port)
120 {
121 	const struct ptp_foreign_tt_clock *clk_best_foreign = ptp_clock_best_time_transmitter();
122 	const struct ptp_dataset *clk_default, *clk_best, *port_best;
123 
124 	clk_default = ptp_clock_ds();
125 	clk_best = ptp_clock_best_foreign_ds();
126 	port_best = ptp_port_best_foreign_ds(port);
127 
128 	if (!port_best && ptp_port_state(port) == PTP_PS_LISTENING) {
129 		return PTP_PS_LISTENING;
130 	}
131 
132 	if (clk_default->clk_quality.class <= 127) {
133 		if (ptp_btca_ds_cmp(clk_default, port_best) > 0) {
134 			/* M1 */
135 			return PTP_PS_GRAND_MASTER;
136 		}
137 		/* P1 */
138 		return PTP_PS_PASSIVE;
139 	}
140 
141 	if (ptp_btca_ds_cmp(clk_default, clk_best) > 0) {
142 		/* M2 */
143 		return PTP_PS_GRAND_MASTER;
144 	}
145 
146 	if (clk_best_foreign->port == port) {
147 		/* S1 */
148 		return PTP_PS_TIME_RECEIVER;
149 	}
150 
151 	if (ptp_btca_ds_cmp(clk_best, port_best) == A_BETTER_TOPOLOGY) {
152 		/* P2 */
153 		return PTP_PS_PASSIVE;
154 	}
155 	/* M3 */
156 	return PTP_PS_TIME_TRANSMITTER;
157 }
158