blob: cd92c92b7bb03da48db423cc2ff8528e14cc3443 [file] [log] [blame]
Shad Ansari2f7f9be2017-06-07 13:34:53 -07001/*
2<:copyright-BRCM:2016:DUAL/GPL:standard
3
4 Broadcom Proprietary and Confidential.(c) 2016 Broadcom
5 All Rights Reserved
6
7Unless you and Broadcom execute a separate written software license
8agreement governing use of this software, this software is licensed
9to you under the terms of the GNU General Public License version 2
10(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php,
11with the following added to such license:
12
13 As a special exception, the copyright holders of this software give
14 you permission to link this software with independent modules, and
15 to copy and distribute the resulting executable under terms of your
16 choice, provided that you also meet, for each linked independent
17 module, the terms and conditions of the license of that module.
18 An independent module is a module which is not derived from this
19 software. The special exception does not apply to any modifications
20 of the software.
21
22Not withstanding the above, under no circumstances may you combine
23this software in any way with any other Broadcom software provided
24under a license other than the GPL, without Broadcom's express prior
25written consent.
26
27:>
28*/
29
30/* This file contains the pieces of the OAM negotiation state machine that are common to all OAM. */
31
32#include "oam_common.h"
33#include "bcmolt_utils.h"
34#include "bcmolt_math.h"
35#include "bcmolt_eon_private.h"
36#include "bcmolt_user_appl_epon_oam.h"
37#include "dpoe/dpoe.h"
38#include "brcm/brcm.h"
39#include "ctc/ctc.h"
40
41static const oam_info_add_tlv add_org_spec_tlv[EON_OAM_SET_ID_COUNT] =
42{
43 [EON_OAM_SET_ID_DPOE] = dpoe_tx_add_tlv,
44 [EON_OAM_SET_ID_BRCM] = brcm_tx_add_tlv,
45 [EON_OAM_SET_ID_CTC] = ctc_tx_add_tlv
46};
47
48static const oam_rx_org_spec_tlv rx_org_spec_tlv[EON_OAM_SET_ID_COUNT] =
49{
50 [EON_OAM_SET_ID_DPOE] = dpoe_rx_tlv,
51 [EON_OAM_SET_ID_BRCM] = brcm_rx_tlv,
52 [EON_OAM_SET_ID_CTC] = ctc_rx_tlv
53};
54
55static uint8_t get_oam_version(void)
56{
57 return 0x01; /* only version currently supported */
58}
59
60static void fill_local_tlv(const eon_link_state *link_state, bcmolt_epon_oam_local_remote_info *tlv)
61{
62 tlv->oam_version = get_oam_version();
63 tlv->revision = link_state->revision;
64 tlv->state =
65 BCMOLT_EPON_OAM_LOCAL_REMOTE_INFO_STATE_PARSER_ACTION1 |
66 BCMOLT_EPON_OAM_LOCAL_REMOTE_INFO_STATE_MULTIPLEXER_ACTION;
67 tlv->oam_config =
68 BCMOLT_EPON_OAM_LOCAL_REMOTE_INFO_CONFIG_OAM_MODE |
69 BCMOLT_EPON_OAM_LOCAL_REMOTE_INFO_CONFIG_UNIDIRECTIONAL_SUPPORT |
70 BCMOLT_EPON_OAM_LOCAL_REMOTE_INFO_CONFIG_LINK_EVENTS;
71 tlv->max_pdu_size = link_state->max_pdu_size;
72 tlv->oui = BCMOLT_EPON_OAM_WELL_KNOWN_OUI_TEK;
73 tlv->vendor_specific_information[0] = 0x06;
74 tlv->vendor_specific_information[1] = 0x86;
75 tlv->vendor_specific_information[2] = 0x20;
76 tlv->vendor_specific_information[3] = 0xA0;
77}
78
79static void dump_std_info_tlv(const eon_link_key *link_key, const char *type, bcmolt_epon_oam_local_remote_info *info)
80{
81 EON_LINK_LOG(
82 DEBUG,
83 link_key,
84 "%s: ver %02x rev %04x state %02x cfg %02x pdu %5u oui %06x vendor %02x%02x%02x%02x\n",
85 type,
86 info->oam_version,
87 info->revision,
88 info->state,
89 info->oam_config,
90 info->max_pdu_size,
91 info->oui,
92 info->vendor_specific_information[0],
93 info->vendor_specific_information[1],
94 info->vendor_specific_information[2],
95 info->vendor_specific_information[3]);
96}
97
98static void dump_org_spec_tlv(const eon_link_key *link_key, bcmolt_epon_oam_organization_specific_info *org_spec)
99{
100 switch (org_spec->oui)
101 {
102 case BCMOLT_EPON_OAM_WELL_KNOWN_OUI_TEK:
103 if (BCMOLT_EPON_OAM_TEK_INFO_TLV_TYPE_EXTENSION_SUPPORT == org_spec->u.tek.tlvs.type)
104 {
105 EON_LINK_LOG(
106 DEBUG, link_key, "BRCM Ext Support: ver %02x rpt %02x pref %02x\n",
107 org_spec->u.tek.tlvs.u.extension_support.version,
108 org_spec->u.tek.tlvs.u.extension_support.report_mode,
109 org_spec->u.tek.tlvs.u.extension_support.preferred_report_mode);
110 }
111 break;
112 case BCMOLT_EPON_OAM_WELL_KNOWN_OUI_DPOE:
113 if (BCMOLT_EPON_OAM_DPOE_INFO_TLV_TYPE_DPOE_OAM_SUPPORT == org_spec->u.dpoe.dpoe_info_tlv.type)
114 {
115 EON_LINK_LOG(
116 DEBUG, link_key, "DPoE Support: ver %02x\n",
117 org_spec->u.dpoe.dpoe_info_tlv.u.dpoe_oam_support.dpoe_oam_version);
118 }
119 break;
120 case BCMOLT_EPON_OAM_WELL_KNOWN_OUI_CTC:
121 EON_LINK_LOG(
122 DEBUG, link_key, "CTC Extension Support %u, Preferred Version 0x%02x, Supports (%u):\n",
123 org_spec->u.ctc.extension_support,
124 org_spec->u.ctc.version,
125 org_spec->u.ctc.supp_version_count);
126 for (uint32_t i = 0; i < org_spec->u.ctc.supp_version_count; i++)
127 {
128 EON_LINK_LOG(
129 DEBUG, link_key, "\tOUI %06x, Version 0x%02x\n",
130 org_spec->u.ctc.supp_version[i].oui, org_spec->u.ctc.supp_version[i].version);
131 }
132 break;
133 default:
134 EON_LINK_LOG(WARNING, link_key, "No support for %06x\n", org_spec->oui);
135 break;
136 }
137}
138
139static void dump_info_frame(const bcmolt_epon_oam_ethernet_frame *oam, const eon_link_state* link_state)
140{
141 static const char* entname[] = {"ONU","OLT"};
142 char da_buf[MAC_STR_LEN];
143 char sa_buf[MAC_STR_LEN];
144 bcmolt_epon_oam_oam_flags flags = oam->protocols[0].u.slow_protocol.value.u.oam.flags;
145 uint8_t is_olt = (0 == memcmp(link_state->epon_ni_mac_addr.u8, oam->sa.u8, BCMOS_ETH_ALEN));
146 uint8_t olt_stab = 0 !=
147 (flags & (is_olt ? BCMOLT_EPON_OAM_OAM_FLAGS_LOCAL_STABLE : BCMOLT_EPON_OAM_OAM_FLAGS_REMOTE_STABLE));
148 uint8_t onu_stab = 0 !=
149 (flags & (is_olt ? BCMOLT_EPON_OAM_OAM_FLAGS_REMOTE_STABLE : BCMOLT_EPON_OAM_OAM_FLAGS_LOCAL_STABLE));
150 uint8_t olt_eval = 0 !=
151 (flags & (is_olt ? BCMOLT_EPON_OAM_OAM_FLAGS_LOCAL_EVALUATING : BCMOLT_EPON_OAM_OAM_FLAGS_REMOTE_EVALUATING));
152 uint8_t onu_eval = 0 !=
153 (flags & (is_olt ? BCMOLT_EPON_OAM_OAM_FLAGS_REMOTE_EVALUATING : BCMOLT_EPON_OAM_OAM_FLAGS_LOCAL_EVALUATING));
154
155 EON_LINK_LOG(
156 DEBUG, &link_state->link_key, "from %s: OLT %s ONU %s\n", entname[is_olt],
157 olt_stab ? "stab" : (olt_eval ? "eval" : "NONE"),
158 onu_stab ? "stab" : (onu_eval ? "eval" : "NONE"));
159
160 EON_LINK_LOG(DEBUG, &link_state->link_key, " da sa eth su flgs cd\n");
161 EON_LINK_LOG(
162 DEBUG, &link_state->link_key, "%s %s %04x %02x %04x %02x\n",
163 bcmos_mac_2_str(&oam->da, da_buf), bcmos_mac_2_str(&oam->sa, sa_buf),
164 oam->protocols[0].ethertype, oam->protocols[0].u.slow_protocol.value.subtype,
165 oam->protocols[0].u.slow_protocol.value.u.oam.flags,
166 oam->protocols[0].u.slow_protocol.value.u.oam.content.code);
167 EON_LINK_LOG(
168 DEBUG, &link_state->link_key, "TLVS: (%u)\n",
169 oam->protocols[0].u.slow_protocol.value.u.oam.content.u.info.tlvs_count);
170 for (uint8_t i = 0; i < oam->protocols[0].u.slow_protocol.value.u.oam.content.u.info.tlvs_count; i++)
171 {
172 switch (oam->protocols[0].u.slow_protocol.value.u.oam.content.u.info.tlvs[i].type)
173 {
174 case BCMOLT_EPON_OAM_INFO_TLV_TYPE_LOCAL:
175 dump_std_info_tlv(
176 &link_state->link_key,
177 "LOCAL",
178 &oam->protocols[0].u.slow_protocol.value.u.oam.content.u.info.tlvs[i].u.local.info);
179 break;
180 case BCMOLT_EPON_OAM_INFO_TLV_TYPE_REMOTE:
181 dump_std_info_tlv(
182 &link_state->link_key,
183 "REMOTE",
184 &oam->protocols[0].u.slow_protocol.value.u.oam.content.u.info.tlvs[i].u.remote.info);
185 break;
186 case BCMOLT_EPON_OAM_INFO_TLV_TYPE_ORGANIZATION_SPECIFIC:
187 dump_org_spec_tlv(
188 &link_state->link_key,
189 &oam->protocols[0].u.slow_protocol.value.u.oam.content.u.info.tlvs[i].u.organization_specific.value);
190 break;
191 default:
192 break;
193 }
194 }
195}
196
197bcmos_errno build_tx_info_frame(eon_link_state *link_state, eon_frame_data *frame_data)
198{
199 bcmos_bool have_remote_info = link_state->remote_info.oam_version == get_oam_version();
200 bcmolt_epon_oam_ethernet_frame oam_frame = {};
201 bcmolt_epon_oam_ethernet_protocol protocol = {};
202 bcmolt_epon_oam_info_tlv_base tlvs[4] = {};
203
204 EON_LINK_LOG(DEBUG, &link_state->link_key, "BUILDING INFO FRAME, current state %04x\n", link_state->oam_state);
205
206 oam_frame.da = slow_protocol_multicast_mac;
207 oam_frame.sa = link_state->epon_ni_mac_addr;
208 oam_frame.protocols_count = 1;
209 oam_frame.protocols = &protocol;
210 protocol.ethertype = BCMOLT_EPON_OAM_PROTOCOL_TYPE_SLOW_PROTOCOL;
211 protocol.u.slow_protocol.value.subtype = BCMOLT_EPON_OAM_SLOW_PROTOCOL_SUBTYPE_OAM;
212 protocol.u.slow_protocol.value.u.oam.flags = link_state->oam_state;
213 protocol.u.slow_protocol.value.u.oam.content.code = BCMOLT_EPON_OAM_OAM_OPCODE_INFO;
214 protocol.u.slow_protocol.value.u.oam.content.u.info.tlvs_count = 1;
215 protocol.u.slow_protocol.value.u.oam.content.u.info.tlvs = tlvs;
216 tlvs[0].type = BCMOLT_EPON_OAM_INFO_TLV_TYPE_LOCAL;
217 fill_local_tlv(link_state, &tlvs[0].u.local.info);
218 if (have_remote_info)
219 {
220 tlvs[1].type = BCMOLT_EPON_OAM_INFO_TLV_TYPE_REMOTE;
221 tlvs[1].u.remote.info = link_state->remote_info;
222 protocol.u.slow_protocol.value.u.oam.content.u.info.tlvs_count++;
223 }
224 add_org_spec_tlv[link_state->oam_set](link_state, &protocol.u.slow_protocol.value.u.oam.content);
225 tlvs[protocol.u.slow_protocol.value.u.oam.content.u.info.tlvs_count].type = BCMOLT_EPON_OAM_INFO_TLV_TYPE_END;
226 protocol.u.slow_protocol.value.u.oam.content.u.info.tlvs_count++;
227
228 dump_info_frame(&oam_frame, link_state);
229
230 return epon_oam_pack_frame(&oam_frame, &frame_data->payload, &frame_data->length);
231}
232
233/* OK = done
234 in_progress = send next frame
235 parse = ignore
236 other = stop
237 */
238bcmos_errno handle_rx_info_frame(eon_link_state *link_state, uint16_t rx_length, uint8_t *rx_payload)
239{
240 bcmos_errno rc = BCM_ERR_IN_PROGRESS;
241 bcmolt_epon_oam_oam_flags start_state;
242 bcmolt_epon_oam_ethernet_frame *frame;
243 bcmolt_epon_oam_slow_protocol *sp;
244 bcmolt_epon_oam_organization_specific_info *org_spec = NULL;
245
246 start_state = link_state->oam_state;
247 EON_LINK_LOG(DEBUG, &link_state->link_key, "RECEIVED INFO FRAME, current state %04x\n", start_state);
248
249 frame = epon_oam_unpack(link_state->link_key.device_id, rx_length, rx_payload);
250
251 if ((frame->protocols_count == 0) ||
252 (frame->protocols[0].ethertype != BCMOLT_EPON_OAM_PROTOCOL_TYPE_SLOW_PROTOCOL) ||
253 (frame->protocols[0].u.slow_protocol.value.subtype != BCMOLT_EPON_OAM_SLOW_PROTOCOL_SUBTYPE_OAM) ||
254 (frame->protocols[0].u.slow_protocol.value.u.oam.content.code != BCMOLT_EPON_OAM_OAM_OPCODE_INFO))
255 {
256 /* not an info frame, ignore */
257 bcmos_free(frame);
258 return BCM_ERR_PARSE;
259 }
260
261 dump_info_frame(frame, link_state);
262 sp = &frame->protocols[0].u.slow_protocol.value;
263
264 /* capture link's TLV for subsequent retransmission */
265 if ((sp->u.oam.content.u.info.tlvs_count > 0) &&
266 (sp->u.oam.content.u.info.tlvs[0].type == BCMOLT_EPON_OAM_INFO_TLV_TYPE_LOCAL))
267 {
268 link_state->remote_info = sp->u.oam.content.u.info.tlvs[0].u.local.info;
269 }
270 else
271 {
272 bcmos_free(frame);
273 return BCM_ERR_ONU_ERR_RESP; /* no local info */
274 }
275
276 if (0 != memcmp(frame->da.u8, slow_protocol_multicast_mac.u8, sizeof(slow_protocol_multicast_mac)))
277 {
278 rc = BCM_ERR_ONU_ERR_RESP;
279 EON_LINK_LOG(WARNING, &link_state->link_key,
280 "DA mismatch "BCMOS_MACADDR_FMT_STR" expected vs. "BCMOS_MACADDR_FMT_STR" actual\n",
281 BCMOS_MACADDR_PARAMS(&slow_protocol_multicast_mac), BCMOS_MACADDR_PARAMS(&frame->da));
282 }
283
284 if (0 != memcmp(frame->sa.u8, &link_state->link_key.link.mac_address, sizeof(frame->sa)))
285 {
286 rc = BCM_ERR_ONU_ERR_RESP;
287 EON_LINK_LOG(WARNING, &link_state->link_key,
288 "SA mismatch "BCMOS_MACADDR_FMT_STR" expected vs. "BCMOS_MACADDR_FMT_STR" actual\n",
289 BCMOS_MACADDR_PARAMS(&link_state->link_key.link.mac_address), BCMOS_MACADDR_PARAMS(&frame->sa));
290 }
291
292 if (sp->u.oam.content.u.info.tlvs[0].u.local.info.oam_version != get_oam_version())
293 {
294 rc = BCM_ERR_ONU_ERR_RESP;
295 EON_LINK_LOG(WARNING, &link_state->link_key, "oam version mismatch 0x%02x expected vs. 0x%02x actual\n",
296 get_oam_version(), sp->u.oam.content.u.info.tlvs[0].u.local.info.oam_version);
297 }
298
299 /* Is the far end echoing the TLV we sent? */
300 if ((sp->u.oam.content.u.info.tlvs_count > 1) &&
301 (sp->u.oam.content.u.info.tlvs[1].type == BCMOLT_EPON_OAM_INFO_TLV_TYPE_REMOTE))
302 {
303 if (sp->u.oam.content.u.info.tlvs[0].u.local.info.max_pdu_size !=
304 sp->u.oam.content.u.info.tlvs[1].u.remote.info.max_pdu_size)
305 {
306 link_state->max_pdu_size = MIN(
307 sp->u.oam.content.u.info.tlvs[0].u.local.info.max_pdu_size,
308 sp->u.oam.content.u.info.tlvs[1].u.remote.info.max_pdu_size);
309 link_state->revision++;
310 }
311 else
312 {
313 bcmolt_epon_oam_local_remote_info loc_tlv = {};
314 fill_local_tlv(link_state, &loc_tlv);
315 if (0 == memcmp(&loc_tlv, &sp->u.oam.content.u.info.tlvs[1].u.remote.info, sizeof(loc_tlv)))
316 {
317 link_state->oam_state = (link_state->oam_state | BCMOLT_EPON_OAM_OAM_FLAGS_LOCAL_STABLE) &
318 (~BCMOLT_EPON_OAM_OAM_FLAGS_LOCAL_EVALUATING);
319 EON_LINK_LOG(DEBUG, &link_state->link_key, "setting LOCAL STABLE flag\n");
320 }
321 else
322 {
323 EON_LINK_LOG(WARNING, &link_state->link_key, "Remote TLV from ONU does NOT match what we sent\n");
324 }
325 }
326 }
327
328 if (sp->u.oam.flags & BCMOLT_EPON_OAM_OAM_FLAGS_LOCAL_EVALUATING)
329 {
330 link_state->oam_state = (link_state->oam_state | BCMOLT_EPON_OAM_OAM_FLAGS_REMOTE_EVALUATING) &
331 (~BCMOLT_EPON_OAM_OAM_FLAGS_REMOTE_STABLE);
332 EON_LINK_LOG(DEBUG, &link_state->link_key, "setting REMOTE EVAL flag\n");
333 }
334
335 if (sp->u.oam.flags & BCMOLT_EPON_OAM_OAM_FLAGS_LOCAL_STABLE)
336 {
337 link_state->oam_state = (link_state->oam_state | BCMOLT_EPON_OAM_OAM_FLAGS_REMOTE_STABLE) &
338 (~BCMOLT_EPON_OAM_OAM_FLAGS_REMOTE_EVALUATING);
339 EON_LINK_LOG(DEBUG, &link_state->link_key, "setting REMOTE STABLE flag\n");
340 }
341
342 if (LOCAL_STABLE_REMOTE_STABLE == link_state->oam_state)
343 {
344 rc = BCM_ERR_OK; /* basic negotiation complete */
345 }
346
347 if ((sp->u.oam.content.u.info.tlvs_count > 2) &&
348 (sp->u.oam.content.u.info.tlvs[2].type == BCMOLT_EPON_OAM_INFO_TLV_TYPE_ORGANIZATION_SPECIFIC))
349 {
350 org_spec = &sp->u.oam.content.u.info.tlvs[2].u.organization_specific.value;
351 }
352 rx_org_spec_tlv[link_state->oam_set](link_state, org_spec, &rc);
353
354 EON_LINK_LOG(DEBUG, &link_state->link_key, "state %04x -> %04x (%s)\n",
355 start_state, link_state->oam_state, bcmos_strerror(rc));
356
357 bcmos_free(frame);
358
359 return rc;
360}
361