1/*
2 * Copyright (c) 2004-2009 Voltaire Inc.  All rights reserved.
3 * Copyright (c) 2007 Xsigo Systems Inc.  All rights reserved.
4 * Copyright (c) 2008 Lawrence Livermore National Lab.  All rights reserved.
5 * Copyright (c) 2010 HNR Consulting.  All rights reserved.
6 *
7 * This software is available to you under a choice of one of two
8 * licenses.  You may choose to be licensed under the terms of the GNU
9 * General Public License (GPL) Version 2, available from the file
10 * COPYING in the main directory of this source tree, or the
11 * OpenIB.org BSD license below:
12 *
13 *     Redistribution and use in source and binary forms, with or
14 *     without modification, are permitted provided that the following
15 *     conditions are met:
16 *
17 *      - Redistributions of source code must retain the above
18 *        copyright notice, this list of conditions and the following
19 *        disclaimer.
20 *
21 *      - Redistributions in binary form must reproduce the above
22 *        copyright notice, this list of conditions and the following
23 *        disclaimer in the documentation and/or other materials
24 *        provided with the distribution.
25 *
26 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
30 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
31 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
32 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
33 * SOFTWARE.
34 *
35 */
36
37/*========================================================*/
38/*               FABRIC SCANNER SPECIFIC DATA             */
39/*========================================================*/
40
41#if HAVE_CONFIG_H
42#include <config.h>
43#endif				/* HAVE_CONFIG_H */
44
45#include <stdlib.h>
46#include <inttypes.h>
47
48#include <infiniband/mad.h>
49
50#include "internal.h"
51#include "chassis.h"
52
53static char *ChassisTypeStr[] =
54{ "", "ISR9288", "ISR9096", "ISR2012", "ISR2004", "ISR4700", "ISR4200" };
55static char *ChassisSlotTypeStr[] = { "", "Line", "Spine", "SRBD" };
56
57typedef struct chassis_scan {
58	ibnd_chassis_t *first_chassis;
59	ibnd_chassis_t *current_chassis;
60	ibnd_chassis_t *last_chassis;
61} chassis_scan_t;
62
63char *ibnd_get_chassis_type(ibnd_node_t * node)
64{
65	int chassis_type;
66
67	if (!node) {
68		IBND_DEBUG("node parameter NULL\n");
69		return NULL;
70	}
71
72	if (!node->chassis)
73		return NULL;
74
75	chassis_type = mad_get_field(node->info, 0, IB_NODE_VENDORID_F);
76
77	switch (chassis_type)
78	{
79		case VTR_VENDOR_ID: /* Voltaire chassis */
80		{
81			if (node->ch_type == UNRESOLVED_CT || node->ch_type > ISR4200_CT)
82				return NULL;
83			return ChassisTypeStr[node->ch_type];
84		}
85		case MLX_VENDOR_ID:
86		{
87			if (node->ch_type_str[0] == '\0')
88				return NULL;
89			return node->ch_type_str;
90		}
91		default:
92		{
93			break;
94		}
95	}
96	return NULL;
97}
98
99char *ibnd_get_chassis_slot_str(ibnd_node_t * node, char *str, size_t size)
100{
101	int vendor_id;
102
103	if (!node) {
104		IBND_DEBUG("node parameter NULL\n");
105		return NULL;
106	}
107
108	/* Currently, only if Voltaire or Mellanox chassis */
109	vendor_id = mad_get_field(node->info, 0,IB_NODE_VENDORID_F);
110
111	if ((vendor_id != VTR_VENDOR_ID) && (vendor_id != MLX_VENDOR_ID))
112		return NULL;
113	if (!node->chassis)
114		return NULL;
115	if (node->ch_slot == UNRESOLVED_CS || node->ch_slot > SRBD_CS)
116		return NULL;
117	if (!str)
118		return NULL;
119	snprintf(str, size, "%s %d Chip %d", ChassisSlotTypeStr[node->ch_slot],
120		 node->ch_slotnum, node->ch_anafanum);
121	return str;
122}
123
124static ibnd_chassis_t *find_chassisnum(ibnd_fabric_t * fabric,
125				       unsigned char chassisnum)
126{
127	ibnd_chassis_t *current;
128
129	for (current = fabric->chassis; current; current = current->next)
130		if (current->chassisnum == chassisnum)
131			return current;
132
133	return NULL;
134}
135
136static uint64_t topspin_chassisguid(uint64_t guid)
137{
138	/* Byte 3 in system image GUID is chassis type, and */
139	/* Byte 4 is location ID (slot) so just mask off byte 4 */
140	return guid & 0xffffffff00ffffffULL;
141}
142
143int ibnd_is_xsigo_guid(uint64_t guid)
144{
145	if ((guid & 0xffffff0000000000ULL) == 0x0013970000000000ULL)
146		return 1;
147	else
148		return 0;
149}
150
151static int is_xsigo_leafone(uint64_t guid)
152{
153	if ((guid & 0xffffffffff000000ULL) == 0x0013970102000000ULL)
154		return 1;
155	else
156		return 0;
157}
158
159int ibnd_is_xsigo_hca(uint64_t guid)
160{
161	/* NodeType 2 is HCA */
162	if ((guid & 0xffffffff00000000ULL) == 0x0013970200000000ULL)
163		return 1;
164	else
165		return 0;
166}
167
168int ibnd_is_xsigo_tca(uint64_t guid)
169{
170	/* NodeType 3 is TCA */
171	if ((guid & 0xffffffff00000000ULL) == 0x0013970300000000ULL)
172		return 1;
173	else
174		return 0;
175}
176
177static int is_xsigo_ca(uint64_t guid)
178{
179	if (ibnd_is_xsigo_hca(guid) || ibnd_is_xsigo_tca(guid))
180		return 1;
181	else
182		return 0;
183}
184
185static int is_xsigo_switch(uint64_t guid)
186{
187	if ((guid & 0xffffffff00000000ULL) == 0x0013970100000000ULL)
188		return 1;
189	else
190		return 0;
191}
192
193static uint64_t xsigo_chassisguid(ibnd_node_t * node)
194{
195	uint64_t sysimgguid =
196	    mad_get_field64(node->info, 0, IB_NODE_SYSTEM_GUID_F);
197	uint64_t remote_sysimgguid;
198
199	if (!is_xsigo_ca(sysimgguid)) {
200		/* Byte 3 is NodeType and byte 4 is PortType */
201		/* If NodeType is 1 (switch), PortType is masked */
202		if (is_xsigo_switch(sysimgguid))
203			return sysimgguid & 0xffffffff00ffffffULL;
204		else
205			return sysimgguid;
206	} else {
207		if (!node->ports || !node->ports[1])
208			return 0;
209
210		/* Is there a peer port ? */
211		if (!node->ports[1]->remoteport)
212			return sysimgguid;
213
214		/* If peer port is Leaf 1, use its chassis GUID */
215		remote_sysimgguid =
216		    mad_get_field64(node->ports[1]->remoteport->node->info, 0,
217				    IB_NODE_SYSTEM_GUID_F);
218		if (is_xsigo_leafone(remote_sysimgguid))
219			return remote_sysimgguid & 0xffffffff00ffffffULL;
220		else
221			return sysimgguid;
222	}
223}
224
225static uint64_t get_chassisguid(ibnd_node_t * node)
226{
227	uint32_t vendid = mad_get_field(node->info, 0, IB_NODE_VENDORID_F);
228	uint64_t sysimgguid =
229	    mad_get_field64(node->info, 0, IB_NODE_SYSTEM_GUID_F);
230
231	if (vendid == TS_VENDOR_ID || vendid == SS_VENDOR_ID)
232		return topspin_chassisguid(sysimgguid);
233	else if (vendid == XS_VENDOR_ID || ibnd_is_xsigo_guid(sysimgguid))
234		return xsigo_chassisguid(node);
235	else
236		return sysimgguid;
237}
238
239static ibnd_chassis_t *find_chassisguid(ibnd_fabric_t * fabric,
240					ibnd_node_t * node)
241{
242	ibnd_chassis_t *current;
243	uint64_t chguid;
244
245	chguid = get_chassisguid(node);
246	for (current = fabric->chassis; current; current = current->next)
247		if (current->chassisguid == chguid)
248			return current;
249
250	return NULL;
251}
252
253uint64_t ibnd_get_chassis_guid(ibnd_fabric_t * fabric, unsigned char chassisnum)
254{
255	ibnd_chassis_t *chassis;
256
257	if (!fabric) {
258		IBND_DEBUG("fabric parameter NULL\n");
259		return 0;
260	}
261
262	chassis = find_chassisnum(fabric, chassisnum);
263	if (chassis)
264		return chassis->chassisguid;
265	else
266		return 0;
267}
268
269static int is_router(ibnd_node_t * n)
270{
271	uint32_t devid = mad_get_field(n->info, 0, IB_NODE_DEVID_F);
272	return (devid == VTR_DEVID_IB_FC_ROUTER ||
273		devid == VTR_DEVID_IB_IP_ROUTER);
274}
275
276static int is_spine_9096(ibnd_node_t * n)
277{
278	uint32_t devid = mad_get_field(n->info, 0, IB_NODE_DEVID_F);
279	return (devid == VTR_DEVID_SFB4 || devid == VTR_DEVID_SFB4_DDR);
280}
281
282static int is_spine_9288(ibnd_node_t * n)
283{
284	uint32_t devid = mad_get_field(n->info, 0, IB_NODE_DEVID_F);
285	return (devid == VTR_DEVID_SFB12 || devid == VTR_DEVID_SFB12_DDR);
286}
287
288static int is_spine_2004(ibnd_node_t * n)
289{
290	uint32_t devid = mad_get_field(n->info, 0, IB_NODE_DEVID_F);
291	return (devid == VTR_DEVID_SFB2004);
292}
293
294static int is_spine_2012(ibnd_node_t * n)
295{
296	uint32_t devid = mad_get_field(n->info, 0, IB_NODE_DEVID_F);
297	return (devid == VTR_DEVID_SFB2012);
298}
299
300static int is_spine_4700(ibnd_node_t * n)
301{
302	uint32_t devid = mad_get_field(n->info, 0, IB_NODE_DEVID_F);
303	return (devid == VTR_DEVID_SFB4700);
304}
305
306static int is_spine_4700x2(ibnd_node_t * n)
307{
308	uint32_t devid = mad_get_field(n->info, 0, IB_NODE_DEVID_F);
309	return (devid == VTR_DEVID_SFB4700X2);
310}
311
312static int is_spine_4200(ibnd_node_t * n)
313{
314	uint32_t devid = mad_get_field(n->info, 0, IB_NODE_DEVID_F);
315	return (devid == VTR_DEVID_SFB4200);
316}
317
318static int is_spine(ibnd_node_t * n)
319{
320	return (is_spine_9096(n) || is_spine_9288(n) ||
321		is_spine_2004(n) || is_spine_2012(n) ||
322		is_spine_4700(n) || is_spine_4700x2(n) ||
323		is_spine_4200(n));
324}
325
326static int is_line_24(ibnd_node_t * n)
327{
328	uint32_t devid = mad_get_field(n->info, 0, IB_NODE_DEVID_F);
329	return (devid == VTR_DEVID_SLB24 ||
330		devid == VTR_DEVID_SLB24_DDR || devid == VTR_DEVID_SRB2004);
331}
332
333static int is_line_8(ibnd_node_t * n)
334{
335	uint32_t devid = mad_get_field(n->info, 0, IB_NODE_DEVID_F);
336	return (devid == VTR_DEVID_SLB8);
337}
338
339static int is_line_2024(ibnd_node_t * n)
340{
341	uint32_t devid = mad_get_field(n->info, 0, IB_NODE_DEVID_F);
342	return (devid == VTR_DEVID_SLB2024);
343}
344
345static int is_line_4700(ibnd_node_t * n)
346{
347	uint32_t devid = mad_get_field(n->info, 0, IB_NODE_DEVID_F);
348	return (devid == VTR_DEVID_SLB4018);
349}
350
351static int is_line(ibnd_node_t * n)
352{
353	return (is_line_24(n) || is_line_8(n) ||
354		is_line_2024(n) || is_line_4700(n));
355}
356
357int is_chassis_switch(ibnd_node_t * n)
358{
359	return (is_spine(n) || is_line(n));
360}
361
362/* these structs help find Line (Anafa) slot number while using spine portnum */
363char line_slot_2_sfb4[37] = {
364	0,
365	1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3,
366	4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
367};
368char anafa_line_slot_2_sfb4[37] = {
369	0,
370	1, 1, 1, 2, 2, 2, 1, 1, 1, 2, 2, 2, 1, 1, 1, 2, 2, 2,
371	1, 1, 1, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
372};
373
374char line_slot_2_sfb12[37] = {
375	0,
376	1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9,
377	10, 10, 11, 11, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
378};
379char anafa_line_slot_2_sfb12[37] = {
380	0,
381	1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2,
382	1, 2, 1, 2, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
383};
384
385/* LB slot = table[spine port] */
386char line_slot_2_sfb18[37] = {
387	0,
388	1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9,
389	10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18};
390/* LB asic num = table[spine port] */
391char anafa_line_slot_2_sfb18[37] = {
392	0,
393	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
394	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
395};
396
397/* LB slot = table[spine port] */
398char line_slot_2_sfb18x2[37] = {
399	0,
400	1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
401	0, 0, 0, 0, 0, 0, 0, 0, 0,  0,  0,  0,  0,  0,  0,  0,  0,  0};
402/* LB asic num = table[spine port] */
403char anafa_line_slot_2_sfb18x2[37] = {
404	0,
405	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
406	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
407};
408
409/* LB slot = table[spine port] */
410char line_slot_2_sfb4200[37] = {
411	0,
412	1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5,
413	5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9};
414/* LB asic num = table[spine port] */
415char anafa_line_slot_2_sfb4200[37] = {
416	0,
417	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
418	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
419};
420
421/* IPR FCR modules connectivity while using sFB4 port as reference */
422char ipr_slot_2_sfb4_port[37] = {
423	0,
424	3, 2, 1, 3, 2, 1, 3, 2, 1, 3, 2, 1, 3, 2, 1, 3, 2, 1,
425	3, 2, 1, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
426};
427
428/* these structs help find Spine (Anafa) slot number while using spine portnum */
429char spine12_slot_2_slb[37] = {
430	0,
431	1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 0, 0, 0, 0, 0, 0,
432	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
433};
434char anafa_spine12_slot_2_slb[37] = {
435	0,
436	1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 0, 0, 0, 0, 0, 0,
437	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
438};
439
440char spine4_slot_2_slb[37] = {
441	0,
442	1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 0, 0, 0, 0, 0, 0,
443	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
444};
445char anafa_spine4_slot_2_slb[37] = {
446	0,
447	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
448	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
449};
450
451/* FB slot = table[line port] */
452char spine18_slot_2_slb[37] = {
453	0,
454	1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9,
455	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
456};
457/* FB asic = table[line port] */
458char anafa_spine18_slot_2_slb[37] = {
459	0,
460	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
461	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
462};
463char anafa_spine18x2_slot_2_slb[37] = {
464	0,
465	2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1,
466	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
467};
468
469/* FB slot = table[line port] */
470char sfb4200_slot_2_slb[37] = {
471	0,
472	1, 1, 1, 1, 0, 0, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4,
473	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
474};
475/* FB asic = table[line port] */
476char anafa_sfb4200_slot_2_slb[37] = {
477	0,
478	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
479	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
480};
481
482/*	reference                     { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }; */
483
484static int get_sfb_slot(ibnd_node_t * n, ibnd_port_t * lineport)
485{
486	n->ch_slot = SPINE_CS;
487	if (is_spine_9096(n)) {
488		n->ch_type = ISR9096_CT;
489		n->ch_slotnum = spine4_slot_2_slb[lineport->portnum];
490		n->ch_anafanum = anafa_spine4_slot_2_slb[lineport->portnum];
491	} else if (is_spine_9288(n)) {
492		n->ch_type = ISR9288_CT;
493		n->ch_slotnum = spine12_slot_2_slb[lineport->portnum];
494		n->ch_anafanum = anafa_spine12_slot_2_slb[lineport->portnum];
495	} else if (is_spine_2012(n)) {
496		n->ch_type = ISR2012_CT;
497		n->ch_slotnum = spine12_slot_2_slb[lineport->portnum];
498		n->ch_anafanum = anafa_spine12_slot_2_slb[lineport->portnum];
499	} else if (is_spine_2004(n)) {
500		n->ch_type = ISR2004_CT;
501		n->ch_slotnum = spine4_slot_2_slb[lineport->portnum];
502		n->ch_anafanum = anafa_spine4_slot_2_slb[lineport->portnum];
503	} else if (is_spine_4700(n)) {
504		n->ch_type = ISR4700_CT;
505		n->ch_slotnum = spine18_slot_2_slb[lineport->portnum];
506		n->ch_anafanum = anafa_spine18_slot_2_slb[lineport->portnum];
507	} else if (is_spine_4700x2(n)) {
508		n->ch_type = ISR4700_CT;
509		n->ch_slotnum = spine18_slot_2_slb[lineport->portnum];
510		n->ch_anafanum = anafa_spine18x2_slot_2_slb[lineport->portnum];
511	} else if (is_spine_4200(n)) {
512		n->ch_type = ISR4200_CT;
513		n->ch_slotnum = sfb4200_slot_2_slb[lineport->portnum];
514		n->ch_anafanum = anafa_sfb4200_slot_2_slb[lineport->portnum];
515	} else {
516		IBND_ERROR("Unexpected node found: guid 0x%016" PRIx64 "\n",
517			   n->guid);
518	}
519	return 0;
520}
521
522static int get_router_slot(ibnd_node_t * n, ibnd_port_t * spineport)
523{
524	uint64_t guessnum = 0;
525
526	n->ch_found = 1;
527
528	n->ch_slot = SRBD_CS;
529	if (is_spine_9096(spineport->node)) {
530		n->ch_type = ISR9096_CT;
531		n->ch_slotnum = line_slot_2_sfb4[spineport->portnum];
532		n->ch_anafanum = ipr_slot_2_sfb4_port[spineport->portnum];
533	} else if (is_spine_9288(spineport->node)) {
534		n->ch_type = ISR9288_CT;
535		n->ch_slotnum = line_slot_2_sfb12[spineport->portnum];
536		/* this is a smart guess based on nodeguids order on sFB-12 module */
537		guessnum = spineport->node->guid % 4;
538		/* module 1 <--> remote anafa 3 */
539		/* module 2 <--> remote anafa 2 */
540		/* module 3 <--> remote anafa 1 */
541		n->ch_anafanum = (guessnum == 3 ? 1 : (guessnum == 1 ? 3 : 2));
542	} else if (is_spine_2012(spineport->node)) {
543		n->ch_type = ISR2012_CT;
544		n->ch_slotnum = line_slot_2_sfb12[spineport->portnum];
545		/* this is a smart guess based on nodeguids order on sFB-12 module */
546		guessnum = spineport->node->guid % 4;
547		// module 1 <--> remote anafa 3
548		// module 2 <--> remote anafa 2
549		// module 3 <--> remote anafa 1
550		n->ch_anafanum = (guessnum == 3 ? 1 : (guessnum == 1 ? 3 : 2));
551	} else if (is_spine_2004(spineport->node)) {
552		n->ch_type = ISR2004_CT;
553		n->ch_slotnum = line_slot_2_sfb4[spineport->portnum];
554		n->ch_anafanum = ipr_slot_2_sfb4_port[spineport->portnum];
555	} else {
556		IBND_ERROR("Unexpected node found: guid 0x%016" PRIx64 "\n",
557			   spineport->node->guid);
558	}
559	return 0;
560}
561
562static int get_slb_slot(ibnd_node_t * n, ibnd_port_t * spineport)
563{
564	n->ch_slot = LINE_CS;
565	if (is_spine_9096(spineport->node)) {
566		n->ch_type = ISR9096_CT;
567		n->ch_slotnum = line_slot_2_sfb4[spineport->portnum];
568		n->ch_anafanum = anafa_line_slot_2_sfb4[spineport->portnum];
569	} else if (is_spine_9288(spineport->node)) {
570		n->ch_type = ISR9288_CT;
571		n->ch_slotnum = line_slot_2_sfb12[spineport->portnum];
572		n->ch_anafanum = anafa_line_slot_2_sfb12[spineport->portnum];
573	} else if (is_spine_2012(spineport->node)) {
574		n->ch_type = ISR2012_CT;
575		n->ch_slotnum = line_slot_2_sfb12[spineport->portnum];
576		n->ch_anafanum = anafa_line_slot_2_sfb12[spineport->portnum];
577	} else if (is_spine_2004(spineport->node)) {
578		n->ch_type = ISR2004_CT;
579		n->ch_slotnum = line_slot_2_sfb4[spineport->portnum];
580		n->ch_anafanum = anafa_line_slot_2_sfb4[spineport->portnum];
581	} else if (is_spine_4700(spineport->node)) {
582		n->ch_type = ISR4700_CT;
583		n->ch_slotnum = line_slot_2_sfb18[spineport->portnum];
584		n->ch_anafanum = anafa_line_slot_2_sfb18[spineport->portnum];
585	} else if (is_spine_4700x2(spineport->node)) {
586		n->ch_type = ISR4700_CT;
587		n->ch_slotnum = line_slot_2_sfb18x2[spineport->portnum];
588		n->ch_anafanum = anafa_line_slot_2_sfb18x2[spineport->portnum];
589	} else if (is_spine_4200(spineport->node)) {
590		n->ch_type = ISR4200_CT;
591		n->ch_slotnum = line_slot_2_sfb4200[spineport->portnum];
592		n->ch_anafanum = anafa_line_slot_2_sfb4200[spineport->portnum];
593	} else {
594		IBND_ERROR("Unexpected node found: guid 0x%016" PRIx64 "\n",
595			   spineport->node->guid);
596	}
597	return 0;
598}
599
600
601/*
602	This function called for every Mellanox node in fabric
603*/
604static int fill_mellanox_chassis_record(ibnd_node_t * node)
605{
606	int p = 0;
607	ibnd_port_t *port;
608
609	char node_desc[IB_SMP_DATA_SIZE];
610	char *system_name;
611	char *system_type;
612	char *system_slot_name;
613	char *node_index;
614	char *iter;
615	int dev_id;
616
617	/*
618	The node description has the following format:
619
620	'MF0;<system name>:<system type>/<system slot name>[:board type]/U<node index>'
621
622     - System slot name in our systems can be L[01-36] , S[01-18]
623     - Node index is always 1 (we don.t have boards with multiple IS4 chips).
624     - System name is taken from the currently configured host name.
625     -The board type is optional and we don.t set it currently  - A leaf or spine slot can currently hold a single type of board.
626	 */
627
628	memcpy(node_desc, node->nodedesc, IB_SMP_DATA_SIZE);
629
630	IBND_DEBUG("fill_mellanox_chassis_record: node_desc:%s \n",node_desc);
631
632	if (node->ch_found)	/* somehow this node has already been passed */
633		return 0;
634
635	/* All mellanox IS4 switches have the same vendor id*/
636	dev_id = mad_get_field(node->info, 0,IB_NODE_DEVID_F);
637	if (dev_id != MLX_DEVID_IS4)
638		return 0;
639
640	if((node_desc[0] != 'M') ||
641	   (node_desc[1] != 'F') ||
642	   (node_desc[2] != '0') ||
643	   (node_desc[3] != ';')) {
644		IBND_DEBUG("fill_mellanox_chassis_record: Unsupported node description format:%s \n",node_desc);
645		return 0;
646	}
647
648	/* parse system name*/
649	system_name = &node_desc[4];
650	for (iter = system_name ; (*iter != ':') && (*iter != '\0') ; iter++);
651	if(*iter == '\0'){
652		IBND_DEBUG("fill_mellanox_chassis_record: Unsupported node description format:%s - (get system_name failed) \n",node_desc);
653		return 0;
654	}
655	*iter = '\0';
656	iter++;
657	/* parse system type*/
658	system_type = iter;
659	for ( ; (*iter != '/') && (*iter != '\0') ; iter++);
660	if(*iter == '\0'){
661		IBND_DEBUG("fill_mellanox_chassis_record: Unsupported node description format:%s - (get system_type failed) \n",node_desc);
662		return 0;
663	}
664	*iter = '\0';
665	iter++;
666	/* parse system slot name*/
667	system_slot_name = iter;
668	for ( ; (*iter != '/') && (*iter != ':') && (*iter != '\0') ; iter++);
669	if(*iter == '\0'){
670		IBND_DEBUG("fill_mellanox_chassis_record: Unsupported node description format:%s - (get system_slot_name failed) \n",node_desc);
671		return 0;
672	}
673	if(*iter == ':'){
674		*iter = '\0';
675		iter++;
676		for ( ; (*iter != '/') && (*iter != '\0') ; iter++);
677		if(*iter == '\0'){
678			IBND_DEBUG("fill_mellanox_chassis_record: Unsupported node description format:%s - (get board type failed) \n",node_desc);
679			return 0;
680		}
681	}
682	*iter = '\0';
683	iter++;
684	node_index = iter;
685	if(node_index[0] != 'U'){
686		IBND_DEBUG("fill_mellanox_chassis_record: Unsupported node description format:%s - (get node index) \n",node_desc);
687		return 0;
688	}
689
690	/* set Chip number (node index) */
691	node->ch_anafanum = (unsigned char) atoi(&node_index[1]);
692	if(node->ch_anafanum != 1){
693		IBND_DEBUG("Unexpected Chip number:%d \n",node->ch_anafanum);
694	}
695
696
697	/* set Line Spine numbers */
698	if(system_slot_name[0] == 'L')
699		node->ch_slot = LINE_CS;
700	else if(system_slot_name[0] == 'S')
701		node->ch_slot = SPINE_CS;
702	else{
703		IBND_DEBUG("fill_mellanox_chassis_record: Unsupported system_slot_name:%s \n",system_slot_name);
704		return 0;
705	}
706
707	/* The switch will be displayed under Line or Spine and not under Chassis switches */
708	node->ch_found = 1;
709
710	node->ch_slotnum = (unsigned char) atoi(&system_slot_name[1]);
711	if((node->ch_slot == LINE_CS && (node->ch_slotnum >  (LINES_MAX_NUM + 1))) ||
712	   (node->ch_slot == SPINE_CS && (node->ch_slotnum > (SPINES_MAX_NUM + 1)))){
713		IBND_ERROR("fill_mellanox_chassis_record: invalid slot number:%d \n",node->ch_slotnum);
714		node->ch_slotnum = 0;
715		return 0;
716	}
717
718	/*set ch_type_str*/
719	strncpy(node->ch_type_str , system_type, sizeof(node->ch_type_str)-1);
720
721	/* Line ports 1-18 are mapped to external ports 1-18*/
722	if(node->ch_slot == LINE_CS)
723	{
724		for (p = 1; p <= node->numports && p <= 18 ; p++) {
725			port = node->ports[p];
726			if (!port)
727				continue;
728			port->ext_portnum = p;
729		}
730	}
731
732	return 0;
733}
734
735static int insert_mellanox_line_and_spine(ibnd_node_t * node, ibnd_chassis_t * chassis)
736{
737	if (node->ch_slot == LINE_CS){
738
739		if (chassis->linenode[node->ch_slotnum])
740			return 0;	/* already filled slot */
741
742		chassis->linenode[node->ch_slotnum] = node;
743	}
744	else if (node->ch_slot == SPINE_CS){
745
746		if (chassis->spinenode[node->ch_slotnum])
747			return 0;	/* already filled slot */
748
749		chassis->spinenode[node->ch_slotnum] = node;
750	}
751	else
752		return 0;
753
754	node->chassis = chassis;
755
756	return 0;
757}
758
759
760/* forward declare this */
761static void voltaire_portmap(ibnd_port_t * port);
762/*
763	This function called for every Voltaire node in fabric
764	It could be optimized so, but time overhead is very small
765	and its only diag.util
766*/
767static int fill_voltaire_chassis_record(ibnd_node_t * node)
768{
769	int p = 0;
770	ibnd_port_t *port;
771	ibnd_node_t *remnode = 0;
772
773	if (node->ch_found)	/* somehow this node has already been passed */
774		return 0;
775	node->ch_found = 1;
776
777	/* node is router only in case of using unique lid */
778	/* (which is lid of chassis router port) */
779	/* in such case node->ports is actually a requested port... */
780	if (is_router(node))
781		/* find the remote node */
782		for (p = 1; p <= node->numports; p++) {
783			port = node->ports[p];
784			if (port && is_spine(port->remoteport->node))
785				get_router_slot(node, port->remoteport);
786		}
787	else if (is_spine(node)) {
788		int is_4700x2 = is_spine_4700x2(node);
789
790		for (p = 1; p <= node->numports; p++) {
791			port = node->ports[p];
792			if (!port || !port->remoteport)
793				continue;
794
795			/*
796			 * Skip ISR4700 double density fabric boards ports 19-36
797			 * as they are chassis external ports
798			 */
799			if (is_4700x2 && (port->portnum > 18))
800				continue;
801
802			remnode = port->remoteport->node;
803			if (remnode->type != IB_NODE_SWITCH) {
804				if (!remnode->ch_found)
805					get_router_slot(remnode, port);
806				continue;
807			}
808			if (!node->ch_type)
809				/* we assume here that remoteport belongs to line */
810				get_sfb_slot(node, port->remoteport);
811
812			/* we could break here, but need to find if more routers connected */
813		}
814
815	} else if (is_line(node)) {
816		int is_4700_line = is_line_4700(node);
817
818		for (p = 1; p <= node->numports; p++) {
819			port = node->ports[p];
820			if (!port || !port->remoteport)
821				continue;
822
823			if ((is_4700_line && (port->portnum > 18)) ||
824			    (!is_4700_line && (port->portnum > 12)))
825				continue;
826
827			/* we assume here that remoteport belongs to spine */
828			get_slb_slot(node, port->remoteport);
829			break;
830		}
831	}
832
833	/* for each port of this node, map external ports */
834	for (p = 1; p <= node->numports; p++) {
835		port = node->ports[p];
836		if (!port)
837			continue;
838		voltaire_portmap(port);
839	}
840
841	return 0;
842}
843
844static int get_line_index(ibnd_node_t * node)
845{
846	int retval;
847
848	if (is_line_4700(node))
849		retval = node->ch_slotnum;
850	else
851		retval = 3 * (node->ch_slotnum - 1) + node->ch_anafanum;
852
853	if (retval > LINES_MAX_NUM || retval < 1) {
854		printf("%s: retval = %d\n", __FUNCTION__, retval);
855		IBND_ERROR("Internal error\n");
856		return -1;
857	}
858	return retval;
859}
860
861static int get_spine_index(ibnd_node_t * node)
862{
863	int retval;
864
865	if (is_spine_9288(node) || is_spine_2012(node))
866		retval = 3 * (node->ch_slotnum - 1) + node->ch_anafanum;
867	else if (is_spine_4700(node) || is_spine_4700x2(node))
868		retval = 2 * (node->ch_slotnum - 1) + node->ch_anafanum;
869	else
870		retval = node->ch_slotnum;
871
872	if (retval > SPINES_MAX_NUM || retval < 1) {
873		IBND_ERROR("Internal error\n");
874		return -1;
875	}
876	return retval;
877}
878
879static int insert_line_router(ibnd_node_t * node, ibnd_chassis_t * chassis)
880{
881	int i = get_line_index(node);
882
883	if (i < 0)
884		return i;
885
886	if (chassis->linenode[i])
887		return 0;	/* already filled slot */
888
889	chassis->linenode[i] = node;
890	node->chassis = chassis;
891	return 0;
892}
893
894static int insert_spine(ibnd_node_t * node, ibnd_chassis_t * chassis)
895{
896	int i = get_spine_index(node);
897
898	if (i < 0)
899		return i;
900
901	if (chassis->spinenode[i])
902		return 0;	/* already filled slot */
903
904	chassis->spinenode[i] = node;
905	node->chassis = chassis;
906	return 0;
907}
908
909static int pass_on_lines_catch_spines(ibnd_chassis_t * chassis)
910{
911	ibnd_node_t *node, *remnode;
912	ibnd_port_t *port;
913	int i, p;
914
915	for (i = 1; i <= LINES_MAX_NUM; i++) {
916		int is_4700_line;
917
918		node = chassis->linenode[i];
919
920		if (!(node && is_line(node)))
921			continue;	/* empty slot or router */
922
923		is_4700_line = is_line_4700(node);
924
925		for (p = 1; p <= node->numports; p++) {
926
927			port = node->ports[p];
928			if (!port || !port->remoteport)
929				continue;
930
931			if ((is_4700_line && (port->portnum > 18)) ||
932			    (!is_4700_line && (port->portnum > 12)))
933				continue;
934
935			remnode = port->remoteport->node;
936
937			if (!remnode->ch_found)
938				continue;	/* some error - spine not initialized ? FIXME */
939			if (insert_spine(remnode, chassis))
940				return -1;
941		}
942	}
943	return 0;
944}
945
946static int pass_on_spines_catch_lines(ibnd_chassis_t * chassis)
947{
948	ibnd_node_t *node, *remnode;
949	ibnd_port_t *port;
950	int i, p;
951
952	for (i = 1; i <= SPINES_MAX_NUM; i++) {
953		int is_4700x2;
954
955		node = chassis->spinenode[i];
956		if (!node)
957			continue;	/* empty slot */
958
959		is_4700x2 = is_spine_4700x2(node);
960
961		for (p = 1; p <= node->numports; p++) {
962			port = node->ports[p];
963			if (!port || !port->remoteport)
964				continue;
965
966			/*
967			 * ISR4700 double density fabric board ports 19-36 are
968			 * chassis external ports, so skip them
969			 */
970			if (is_4700x2 && (port->portnum > 18))
971				continue;
972
973			remnode = port->remoteport->node;
974
975			if (!remnode->ch_found)
976				continue;	/* some error - line/router not initialized ? FIXME */
977
978			if (insert_line_router(remnode, chassis))
979				return -1;
980		}
981	}
982	return 0;
983}
984
985/*
986	Stupid interpolation algorithm...
987	But nothing to do - have to be compliant with VoltaireSM/NMS
988*/
989static void pass_on_spines_interpolate_chguid(ibnd_chassis_t * chassis)
990{
991	ibnd_node_t *node;
992	int i;
993
994	for (i = 1; i <= SPINES_MAX_NUM; i++) {
995		node = chassis->spinenode[i];
996		if (!node)
997			continue;	/* skip the empty slots */
998
999		/* take first guid minus one to be consistent with SM */
1000		chassis->chassisguid = node->guid - 1;
1001		break;
1002	}
1003}
1004
1005/*
1006	This function fills chassis structure with all nodes
1007	in that chassis
1008	chassis structure = structure of one standalone chassis
1009*/
1010static int build_chassis(ibnd_node_t * node, ibnd_chassis_t * chassis)
1011{
1012	int p = 0;
1013	ibnd_node_t *remnode = 0;
1014	ibnd_port_t *port = 0;
1015
1016	/* we get here with node = chassis_spine */
1017	if (insert_spine(node, chassis))
1018		return -1;
1019
1020	/* loop: pass on all ports of node */
1021	for (p = 1; p <= node->numports; p++) {
1022
1023		port = node->ports[p];
1024		if (!port || !port->remoteport)
1025			continue;
1026
1027		/*
1028		 * ISR4700 double density fabric board ports 19-36 are
1029		 * chassis external ports, so skip them
1030		 */
1031		if (is_spine_4700x2(node) && (port->portnum > 18))
1032			continue;
1033
1034		remnode = port->remoteport->node;
1035
1036		if (!remnode->ch_found)
1037			continue;	/* some error - line or router not initialized ? FIXME */
1038
1039		insert_line_router(remnode, chassis);
1040	}
1041
1042	if (pass_on_lines_catch_spines(chassis))
1043		return -1;
1044	/* this pass needed for to catch routers, since routers connected only */
1045	/* to spines in slot 1 or 4 and we could miss them first time */
1046	if (pass_on_spines_catch_lines(chassis))
1047		return -1;
1048
1049	/* additional 2 passes needed for to overcome a problem of pure "in-chassis" */
1050	/* connectivity - extra pass to ensure that all related chips/modules */
1051	/* inserted into the chassis */
1052	if (pass_on_lines_catch_spines(chassis))
1053		return -1;
1054	if (pass_on_spines_catch_lines(chassis))
1055		return -1;
1056	pass_on_spines_interpolate_chguid(chassis);
1057
1058	return 0;
1059}
1060
1061/*========================================================*/
1062/*                INTERNAL TO EXTERNAL PORT MAPPING       */
1063/*========================================================*/
1064
1065/*
1066Description : On ISR9288/9096 external ports indexing
1067              is not matching the internal ( anafa ) port
1068              indexes. Use this MAP to translate the data you get from
1069              the OpenIB diagnostics (smpquery, ibroute, ibtracert, etc.)
1070
1071Module : sLB-24
1072                anafa 1             anafa 2
1073ext port | 13 14 15 16 17 18 | 19 20 21 22 23 24
1074int port | 22 23 24 18 17 16 | 22 23 24 18 17 16
1075ext port | 1  2  3  4  5  6  | 7  8  9  10 11 12
1076int port | 19 20 21 15 14 13 | 19 20 21 15 14 13
1077------------------------------------------------
1078
1079Module : sLB-8
1080                anafa 1             anafa 2
1081ext port | 13 14 15 16 17 18 | 19 20 21 22 23 24
1082int port | 24 23 22 18 17 16 | 24 23 22 18 17 16
1083ext port | 1  2  3  4  5  6  | 7  8  9  10 11 12
1084int port | 21 20 19 15 14 13 | 21 20 19 15 14 13
1085
1086----------->
1087                anafa 1             anafa 2
1088ext port | -  -  5  -  -  6  | -  -  7  -  -  8
1089int port | 24 23 22 18 17 16 | 24 23 22 18 17 16
1090ext port | -  -  1  -  -  2  | -  -  3  -  -  4
1091int port | 21 20 19 15 14 13 | 21 20 19 15 14 13
1092------------------------------------------------
1093
1094Module : sLB-2024
1095
1096ext port | 13 14 15 16 17 18 19 20 21 22 23 24
1097A1 int port| 13 14 15 16 17 18 19 20 21 22 23 24
1098ext port | 1 2 3 4 5 6 7 8 9 10 11 12
1099A2 int port| 13 14 15 16 17 18 19 20 21 22 23 24
1100---------------------------------------------------
1101
1102Module : sLB-4018
1103
1104int port | 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
1105ext port |  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18
1106---------------------------------------------------
1107
1108Module : sFB-4700X2
1109
1110  12X port -> 3 x 4X ports:
1111
1112A1 int port | 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
1113   ext port |  7  7  7  8  8  8  9  9  9 10 10 10 11 11 11 12 12 12
1114A2 int port | 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
1115   ext port |  1  1  1  2  2  2  3  3  3  4  4  4  5  5  5  6  6  6
1116
1117*/
1118
1119int int2ext_map_slb24[2][25] = {
1120	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 5, 4, 18, 17, 16, 1, 2, 3,
1121	 13, 14, 15},
1122	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 11, 10, 24, 23, 22, 7, 8, 9,
1123	 19, 20, 21}
1124};
1125
1126int int2ext_map_slb8[2][25] = {
1127	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 6, 6, 6, 1, 1, 1, 5, 5,
1128	 5},
1129	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 8, 8, 8, 3, 3, 3, 7, 7,
1130	 7}
1131};
1132
1133int int2ext_map_slb2024[2][25] = {
1134	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 14, 15, 16, 17, 18, 19, 20,
1135	 21, 22, 23, 24},
1136	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
1137	 11, 12}
1138};
1139
1140int int2ext_map_slb4018[37] = {
1141	0,
1142	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1143	1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18
1144};
1145
1146int int2ext_map_sfb4700x2[2][37] = {
1147	{0,
1148	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1149	 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 11, 12, 12, 12},
1150	{0,
1151	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1152	 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6}
1153};
1154
1155/*	reference			{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }; */
1156
1157/* map internal ports to external ports if appropriate */
1158static void voltaire_portmap(ibnd_port_t * port)
1159{
1160	int portnum = port->portnum;
1161	int chipnum = 0;
1162	ibnd_node_t *node = port->node;
1163	int is_4700_line = is_line_4700(node);
1164	int is_4700x2_spine = is_spine_4700x2(node);
1165
1166	if (!node->ch_found || (!is_line(node) && !is_4700x2_spine)) {
1167		port->ext_portnum = 0;
1168		return;
1169	}
1170
1171	if (((is_4700_line || is_4700x2_spine) &&
1172	     (portnum < 19 || portnum > 36)) ||
1173	    ((!is_4700_line && !is_4700x2_spine) &&
1174	     (portnum < 13 || portnum > 24))) {
1175			port->ext_portnum = 0;
1176		return;
1177	}
1178
1179	if (port->node->ch_anafanum < 1 || port->node->ch_anafanum > 2) {
1180		port->ext_portnum = 0;
1181		return;
1182	}
1183
1184	chipnum = port->node->ch_anafanum - 1;
1185
1186	if (is_line_24(node))
1187		port->ext_portnum = int2ext_map_slb24[chipnum][portnum];
1188	else if (is_line_2024(node))
1189		port->ext_portnum = int2ext_map_slb2024[chipnum][portnum];
1190	/* sLB-4018: Only one asic per LB */
1191	else if (is_4700_line)
1192		port->ext_portnum = int2ext_map_slb4018[portnum];
1193	/* sFB-4700X2 4X port */
1194	else if (is_4700x2_spine)
1195		port->ext_portnum = int2ext_map_sfb4700x2[chipnum][portnum];
1196	else
1197		port->ext_portnum = int2ext_map_slb8[chipnum][portnum];
1198}
1199
1200static int add_chassis(chassis_scan_t * chassis_scan)
1201{
1202	if (!(chassis_scan->current_chassis =
1203	      calloc(1, sizeof(ibnd_chassis_t)))) {
1204		IBND_ERROR("OOM: failed to allocate chassis object\n");
1205		return -1;
1206	}
1207
1208	if (chassis_scan->first_chassis == NULL) {
1209		chassis_scan->first_chassis = chassis_scan->current_chassis;
1210		chassis_scan->last_chassis = chassis_scan->current_chassis;
1211	} else {
1212		chassis_scan->last_chassis->next =
1213		    chassis_scan->current_chassis;
1214		chassis_scan->last_chassis = chassis_scan->current_chassis;
1215	}
1216	return 0;
1217}
1218
1219static void add_node_to_chassis(ibnd_chassis_t * chassis, ibnd_node_t * node)
1220{
1221	node->chassis = chassis;
1222	node->next_chassis_node = chassis->nodes;
1223	chassis->nodes = node;
1224}
1225
1226/*
1227	Main grouping function
1228	Algorithm:
1229	1. pass on every Voltaire node
1230	2. catch spine chip for every Voltaire node
1231		2.1 build/interpolate chassis around this chip
1232		2.2 go to 1.
1233	3. pass on non Voltaire nodes (SystemImageGUID based grouping)
1234	4. now group non Voltaire nodes by SystemImageGUID
1235	Returns:
1236	0 on success, -1 on failure
1237*/
1238int group_nodes(ibnd_fabric_t * fabric)
1239{
1240	ibnd_node_t *node;
1241	int chassisnum = 0;
1242	ibnd_chassis_t *chassis;
1243	ibnd_chassis_t *ch, *ch_next;
1244	chassis_scan_t chassis_scan;
1245	int vendor_id;
1246
1247	chassis_scan.first_chassis = NULL;
1248	chassis_scan.current_chassis = NULL;
1249	chassis_scan.last_chassis = NULL;
1250
1251	/* first pass on switches and build for every Voltaire node */
1252	/* an appropriate chassis record (slotnum and position) */
1253	/* according to internal connectivity */
1254	/* not very efficient but clear code so... */
1255	for (node = fabric->switches; node; node = node->type_next) {
1256
1257		vendor_id = mad_get_field(node->info, 0,IB_NODE_VENDORID_F);
1258
1259		if (vendor_id == VTR_VENDOR_ID
1260		    && fill_voltaire_chassis_record(node))
1261			goto cleanup;
1262		else if (vendor_id == MLX_VENDOR_ID
1263			&& fill_mellanox_chassis_record(node))
1264			goto cleanup;
1265
1266	}
1267
1268	/* separate every Voltaire chassis from each other and build linked list of them */
1269	/* algorithm: catch spine and find all surrounding nodes */
1270	for (node = fabric->switches; node; node = node->type_next) {
1271		if (mad_get_field(node->info, 0,
1272				  IB_NODE_VENDORID_F) != VTR_VENDOR_ID)
1273			continue;
1274		if (!node->ch_found
1275		    || (node->chassis && node->chassis->chassisnum)
1276		    || !is_spine(node))
1277			continue;
1278		if (add_chassis(&chassis_scan))
1279			goto cleanup;
1280		chassis_scan.current_chassis->chassisnum = ++chassisnum;
1281		if (build_chassis(node, chassis_scan.current_chassis))
1282			goto cleanup;
1283	}
1284
1285	/* now make pass on nodes for chassis which are not Voltaire */
1286	/* grouped by common SystemImageGUID */
1287	for (node = fabric->nodes; node; node = node->next) {
1288		if (mad_get_field(node->info, 0,
1289				  IB_NODE_VENDORID_F) == VTR_VENDOR_ID)
1290			continue;
1291		if (mad_get_field64(node->info, 0, IB_NODE_SYSTEM_GUID_F)) {
1292			chassis = find_chassisguid(fabric, node);
1293			if (chassis)
1294				chassis->nodecount++;
1295			else {
1296				/* Possible new chassis */
1297				if (add_chassis(&chassis_scan))
1298					goto cleanup;
1299				chassis_scan.current_chassis->chassisguid =
1300				    get_chassisguid(node);
1301				chassis_scan.current_chassis->nodecount = 1;
1302				if (!fabric->chassis)
1303					fabric->chassis = chassis_scan.first_chassis;
1304			}
1305		}
1306	}
1307
1308	/* now, make another pass to see which nodes are part of chassis */
1309	/* (defined as chassis->nodecount > 1) */
1310	for (node = fabric->nodes; node; node = node->next) {
1311
1312		vendor_id = mad_get_field(node->info, 0,IB_NODE_VENDORID_F);
1313
1314		if (vendor_id == VTR_VENDOR_ID)
1315			continue;
1316		if (mad_get_field64(node->info, 0, IB_NODE_SYSTEM_GUID_F)) {
1317			chassis = find_chassisguid(fabric, node);
1318			if (chassis && chassis->nodecount > 1) {
1319				if (!chassis->chassisnum)
1320					chassis->chassisnum = ++chassisnum;
1321				if (!node->ch_found) {
1322					node->ch_found = 1;
1323					add_node_to_chassis(chassis, node);
1324				}
1325				else if (vendor_id == MLX_VENDOR_ID){
1326					insert_mellanox_line_and_spine(node, chassis);
1327				}
1328			}
1329		}
1330	}
1331
1332	fabric->chassis = chassis_scan.first_chassis;
1333	return 0;
1334
1335cleanup:
1336	ch = chassis_scan.first_chassis;
1337	while (ch) {
1338		ch_next = ch->next;
1339		free(ch);
1340		ch = ch_next;
1341	}
1342	fabric->chassis = NULL;
1343	return -1;
1344}
1345