1/*
2 * Copyright (c) 2004-2007 Voltaire Inc.  All rights reserved.
3 * Copyright (c) 2007 Xsigo Systems Inc.  All rights reserved.
4 *
5 * This software is available to you under a choice of one of two
6 * licenses.  You may choose to be licensed under the terms of the GNU
7 * General Public License (GPL) Version 2, available from the file
8 * COPYING in the main directory of this source tree, or the
9 * OpenIB.org BSD license below:
10 *
11 *     Redistribution and use in source and binary forms, with or
12 *     without modification, are permitted provided that the following
13 *     conditions are met:
14 *
15 *      - Redistributions of source code must retain the above
16 *        copyright notice, this list of conditions and the following
17 *        disclaimer.
18 *
19 *      - Redistributions in binary form must reproduce the above
20 *        copyright notice, this list of conditions and the following
21 *        disclaimer in the documentation and/or other materials
22 *        provided with the distribution.
23 *
24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
28 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
29 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
30 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31 * SOFTWARE.
32 *
33 */
34
35/*========================================================*/
36/*               FABRIC SCANNER SPECIFIC DATA             */
37/*========================================================*/
38
39#if HAVE_CONFIG_H
40#  include <config.h>
41#endif /* HAVE_CONFIG_H */
42
43#include <stdint.h>
44#include <stdlib.h>
45#include <inttypes.h>
46
47#include <infiniband/common.h>
48#include <infiniband/mad.h>
49
50#include "ibnetdiscover.h"
51#include "grouping.h"
52
53#define OUT_BUFFER_SIZE 16
54
55
56extern Node *nodesdist[MAXHOPS+1];	/* last is CA list */
57extern Node *mynode;
58extern Port *myport;
59extern int maxhops_discovered;
60
61AllChassisList mylist;
62
63char *ChassisTypeStr[5] = { "", "ISR9288", "ISR9096", "ISR2012", "ISR2004" };
64char *ChassisSlotStr[4] = { "", "Line", "Spine", "SRBD" };
65
66
67char *get_chassis_type(unsigned char chassistype)
68{
69	if (chassistype == UNRESOLVED_CT || chassistype > ISR2004_CT)
70		return NULL;
71	return ChassisTypeStr[chassistype];
72}
73
74char *get_chassis_slot(unsigned char chassisslot)
75{
76	if (chassisslot == UNRESOLVED_CS || chassisslot > SRBD_CS)
77		return NULL;
78	return ChassisSlotStr[chassisslot];
79}
80
81static struct ChassisList *find_chassisnum(unsigned char chassisnum)
82{
83	ChassisList *current;
84
85	for (current = mylist.first; current; current = current->next) {
86		if (current->chassisnum == chassisnum)
87			return current;
88	}
89
90	return NULL;
91}
92
93static uint64_t topspin_chassisguid(uint64_t guid)
94{
95	/* Byte 3 in system image GUID is chassis type, and */
96	/* Byte 4 is location ID (slot) so just mask off byte 4 */
97	return guid & 0xffffffff00ffffffULL;
98}
99
100int is_xsigo_guid(uint64_t guid)
101{
102	if ((guid & 0xffffff0000000000ULL) == 0x0013970000000000ULL)
103		return 1;
104	else
105		return 0;
106}
107
108static int is_xsigo_leafone(uint64_t guid)
109{
110	if ((guid & 0xffffffffff000000ULL) == 0x0013970102000000ULL)
111		return 1;
112	else
113		return 0;
114}
115
116int is_xsigo_hca(uint64_t guid)
117{
118	/* NodeType 2 is HCA */
119	if ((guid & 0xffffffff00000000ULL) == 0x0013970200000000ULL)
120		return 1;
121	else
122		return 0;
123}
124
125int is_xsigo_tca(uint64_t guid)
126{
127	/* NodeType 3 is TCA */
128	if ((guid & 0xffffffff00000000ULL) == 0x0013970300000000ULL)
129		return 1;
130	else
131		return 0;
132}
133
134static int is_xsigo_ca(uint64_t guid)
135{
136	if (is_xsigo_hca(guid) || is_xsigo_tca(guid))
137		return 1;
138	else
139		return 0;
140}
141
142static int is_xsigo_switch(uint64_t guid)
143{
144	if ((guid & 0xffffffff00000000ULL) == 0x0013970100000000ULL)
145		return 1;
146	else
147		return 0;
148}
149
150static uint64_t xsigo_chassisguid(Node *node)
151{
152	if (!is_xsigo_ca(node->sysimgguid)) {
153		/* Byte 3 is NodeType and byte 4 is PortType */
154		/* If NodeType is 1 (switch), PortType is masked */
155		if (is_xsigo_switch(node->sysimgguid))
156			return node->sysimgguid & 0xffffffff00ffffffULL;
157		else
158			return node->sysimgguid;
159	} else {
160		/* Is there a peer port ? */
161		if (!node->ports->remoteport)
162			return node->sysimgguid;
163
164		/* If peer port is Leaf 1, use its chassis GUID */
165		if (is_xsigo_leafone(node->ports->remoteport->node->sysimgguid))
166			return node->ports->remoteport->node->sysimgguid &
167			       0xffffffff00ffffffULL;
168		else
169			return node->sysimgguid;
170	}
171}
172
173static uint64_t get_chassisguid(Node *node)
174{
175	if (node->vendid == TS_VENDOR_ID || node->vendid == SS_VENDOR_ID)
176		return topspin_chassisguid(node->sysimgguid);
177	else if (node->vendid == XS_VENDOR_ID || is_xsigo_guid(node->sysimgguid))
178		return xsigo_chassisguid(node);
179	else
180		return node->sysimgguid;
181}
182
183static struct ChassisList *find_chassisguid(Node *node)
184{
185	ChassisList *current;
186	uint64_t chguid;
187
188	chguid = get_chassisguid(node);
189	for (current = mylist.first; current; current = current->next) {
190		if (current->chassisguid == chguid)
191			return current;
192	}
193
194	return NULL;
195}
196
197uint64_t get_chassis_guid(unsigned char chassisnum)
198{
199	ChassisList *chassis;
200
201	chassis = find_chassisnum(chassisnum);
202	if (chassis)
203		return chassis->chassisguid;
204	else
205		return 0;
206}
207
208static int is_router(Node *node)
209{
210	return (node->devid == VTR_DEVID_IB_FC_ROUTER ||
211		node->devid == VTR_DEVID_IB_IP_ROUTER);
212}
213
214static int is_spine_9096(Node *node)
215{
216	return (node->devid == VTR_DEVID_SFB4 ||
217		node->devid == VTR_DEVID_SFB4_DDR);
218}
219
220static int is_spine_9288(Node *node)
221{
222	return (node->devid == VTR_DEVID_SFB12 ||
223		node->devid == VTR_DEVID_SFB12_DDR);
224}
225
226static int is_spine_2004(Node *node)
227{
228	return (node->devid == VTR_DEVID_SFB2004);
229}
230
231static int is_spine_2012(Node *node)
232{
233	return (node->devid == VTR_DEVID_SFB2012);
234}
235
236static int is_spine(Node *node)
237{
238	return (is_spine_9096(node) || is_spine_9288(node) ||
239		is_spine_2004(node) || is_spine_2012(node));
240}
241
242static int is_line_24(Node *node)
243{
244	return (node->devid == VTR_DEVID_SLB24 ||
245		node->devid == VTR_DEVID_SLB24_DDR ||
246		node->devid == VTR_DEVID_SRB2004);
247}
248
249static int is_line_8(Node *node)
250{
251	return (node->devid == VTR_DEVID_SLB8);
252}
253
254static int is_line_2024(Node *node)
255{
256	return (node->devid == VTR_DEVID_SLB2024);
257}
258
259static int is_line(Node *node)
260{
261	return (is_line_24(node) || is_line_8(node) || is_line_2024(node));
262}
263
264int is_chassis_switch(Node *node)
265{
266    return (is_spine(node) || is_line(node));
267}
268
269/* these structs help find Line (Anafa) slot number while using spine portnum */
270int line_slot_2_sfb4[25]        = { 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4 };
271int anafa_line_slot_2_sfb4[25]  = { 0, 1, 1, 1, 2, 2, 2, 1, 1, 1, 2, 2, 2, 1, 1, 1, 2, 2, 2, 1, 1, 1, 2, 2, 2 };
272int line_slot_2_sfb12[25]       = { 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9,10, 10, 11, 11, 12, 12 };
273int anafa_line_slot_2_sfb12[25] = { 0, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2 };
274
275/* IPR FCR modules connectivity while using sFB4 port as reference */
276int ipr_slot_2_sfb4_port[25]    = { 0, 3, 2, 1, 3, 2, 1, 3, 2, 1, 3, 2, 1, 3, 2, 1, 3, 2, 1, 3, 2, 1, 3, 2, 1 };
277
278/* these structs help find Spine (Anafa) slot number while using spine portnum */
279int spine12_slot_2_slb[25]      = { 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
280int anafa_spine12_slot_2_slb[25]= { 0, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
281int spine4_slot_2_slb[25]       = { 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
282int anafa_spine4_slot_2_slb[25] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
283/*	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 }; */
284
285static void get_sfb_slot(Node *node, Port *lineport)
286{
287	ChassisRecord *ch = node->chrecord;
288
289	ch->chassisslot = SPINE_CS;
290	if (is_spine_9096(node)) {
291		ch->chassistype = ISR9096_CT;
292		ch->slotnum = spine4_slot_2_slb[lineport->portnum];
293		ch->anafanum = anafa_spine4_slot_2_slb[lineport->portnum];
294	} else if (is_spine_9288(node)) {
295		ch->chassistype = ISR9288_CT;
296		ch->slotnum = spine12_slot_2_slb[lineport->portnum];
297		ch->anafanum = anafa_spine12_slot_2_slb[lineport->portnum];
298	} else if (is_spine_2012(node)) {
299		ch->chassistype = ISR2012_CT;
300		ch->slotnum = spine12_slot_2_slb[lineport->portnum];
301		ch->anafanum = anafa_spine12_slot_2_slb[lineport->portnum];
302	} else if (is_spine_2004(node)) {
303		ch->chassistype = ISR2004_CT;
304		ch->slotnum = spine4_slot_2_slb[lineport->portnum];
305		ch->anafanum = anafa_spine4_slot_2_slb[lineport->portnum];
306	} else {
307		IBPANIC("Unexpected node found: guid 0x%016" PRIx64, node->nodeguid);
308	}
309}
310
311static void get_router_slot(Node *node, Port *spineport)
312{
313	ChassisRecord *ch = node->chrecord;
314	int guessnum = 0;
315
316	if (!ch) {
317		if (!(node->chrecord = calloc(1, sizeof(ChassisRecord))))
318			IBPANIC("out of mem");
319		ch = node->chrecord;
320	}
321
322	ch->chassisslot = SRBD_CS;
323	if (is_spine_9096(spineport->node)) {
324		ch->chassistype = ISR9096_CT;
325		ch->slotnum = line_slot_2_sfb4[spineport->portnum];
326		ch->anafanum = ipr_slot_2_sfb4_port[spineport->portnum];
327	} else if (is_spine_9288(spineport->node)) {
328		ch->chassistype = ISR9288_CT;
329		ch->slotnum = line_slot_2_sfb12[spineport->portnum];
330		/* this is a smart guess based on nodeguids order on sFB-12 module */
331		guessnum = spineport->node->nodeguid % 4;
332		/* module 1 <--> remote anafa 3 */
333		/* module 2 <--> remote anafa 2 */
334		/* module 3 <--> remote anafa 1 */
335		ch->anafanum = (guessnum == 3 ? 1 : (guessnum == 1 ? 3 : 2));
336	} else if (is_spine_2012(spineport->node)) {
337		ch->chassistype = ISR2012_CT;
338		ch->slotnum = line_slot_2_sfb12[spineport->portnum];
339		/* this is a smart guess based on nodeguids order on sFB-12 module */
340		guessnum = spineport->node->nodeguid % 4;
341		// module 1 <--> remote anafa 3
342		// module 2 <--> remote anafa 2
343		// module 3 <--> remote anafa 1
344		ch->anafanum = (guessnum == 3? 1 : (guessnum == 1 ? 3 : 2));
345	} else if (is_spine_2004(spineport->node)) {
346		ch->chassistype = ISR2004_CT;
347		ch->slotnum = line_slot_2_sfb4[spineport->portnum];
348		ch->anafanum = ipr_slot_2_sfb4_port[spineport->portnum];
349	} else {
350		IBPANIC("Unexpected node found: guid 0x%016" PRIx64, spineport->node->nodeguid);
351	}
352}
353
354static void get_slb_slot(ChassisRecord *ch, Port *spineport)
355{
356	ch->chassisslot = LINE_CS;
357	if (is_spine_9096(spineport->node)) {
358		ch->chassistype = ISR9096_CT;
359		ch->slotnum = line_slot_2_sfb4[spineport->portnum];
360		ch->anafanum = anafa_line_slot_2_sfb4[spineport->portnum];
361	} else if (is_spine_9288(spineport->node)) {
362		ch->chassistype = ISR9288_CT;
363		ch->slotnum = line_slot_2_sfb12[spineport->portnum];
364		ch->anafanum = anafa_line_slot_2_sfb12[spineport->portnum];
365	} else if (is_spine_2012(spineport->node)) {
366		ch->chassistype = ISR2012_CT;
367		ch->slotnum = line_slot_2_sfb12[spineport->portnum];
368		ch->anafanum = anafa_line_slot_2_sfb12[spineport->portnum];
369	} else if (is_spine_2004(spineport->node)) {
370		ch->chassistype = ISR2004_CT;
371		ch->slotnum = line_slot_2_sfb4[spineport->portnum];
372		ch->anafanum = anafa_line_slot_2_sfb4[spineport->portnum];
373	} else {
374		IBPANIC("Unexpected node found: guid 0x%016" PRIx64, spineport->node->nodeguid);
375	}
376}
377
378/*
379	This function called for every Voltaire node in fabric
380	It could be optimized so, but time overhead is very small
381	and its only diag.util
382*/
383static void fill_chassis_record(Node *node)
384{
385	Port *port;
386	Node *remnode = 0;
387	ChassisRecord *ch = 0;
388
389	if (node->chrecord) /* somehow this node has already been passed */
390		return;
391
392	if (!(node->chrecord = calloc(1, sizeof(ChassisRecord))))
393		IBPANIC("out of mem");
394
395	ch = node->chrecord;
396
397	/* node is router only in case of using unique lid */
398	/* (which is lid of chassis router port) */
399	/* in such case node->ports is actually a requested port... */
400	if (is_router(node) && is_spine(node->ports->remoteport->node))
401		get_router_slot(node, node->ports->remoteport);
402	else if (is_spine(node)) {
403		for (port = node->ports; port; port = port->next) {
404			if (!port->remoteport)
405				continue;
406			remnode = port->remoteport->node;
407			if (remnode->type != SWITCH_NODE) {
408				if (!remnode->chrecord)
409					get_router_slot(remnode, port);
410				continue;
411			}
412			if (!ch->chassistype)
413				/* we assume here that remoteport belongs to line */
414				get_sfb_slot(node, port->remoteport);
415
416				/* we could break here, but need to find if more routers connected */
417		}
418
419	} else if (is_line(node)) {
420		for (port = node->ports; port; port = port->next) {
421			if (port->portnum > 12)
422				continue;
423			if (!port->remoteport)
424				continue;
425			/* we assume here that remoteport belongs to spine */
426			get_slb_slot(ch, port->remoteport);
427			break;
428		}
429	}
430
431	return;
432}
433
434static int get_line_index(Node *node)
435{
436	int retval = 3 * (node->chrecord->slotnum - 1) + node->chrecord->anafanum;
437
438	if (retval > LINES_MAX_NUM || retval < 1)
439		IBPANIC("Internal error");
440	return retval;
441}
442
443static int get_spine_index(Node *node)
444{
445	int retval;
446
447	if (is_spine_9288(node) || is_spine_2012(node))
448		retval = 3 * (node->chrecord->slotnum - 1) + node->chrecord->anafanum;
449	else
450		retval = node->chrecord->slotnum;
451
452	if (retval > SPINES_MAX_NUM || retval < 1)
453		IBPANIC("Internal error");
454	return retval;
455}
456
457static void insert_line_router(Node *node, ChassisList *chassislist)
458{
459	int i = get_line_index(node);
460
461	if (chassislist->linenode[i])
462		return;		/* already filled slot */
463
464	chassislist->linenode[i] = node;
465	node->chrecord->chassisnum = chassislist->chassisnum;
466}
467
468static void insert_spine(Node *node, ChassisList *chassislist)
469{
470	int i = get_spine_index(node);
471
472	if (chassislist->spinenode[i])
473		return;		/* already filled slot */
474
475	chassislist->spinenode[i] = node;
476	node->chrecord->chassisnum = chassislist->chassisnum;
477}
478
479static void pass_on_lines_catch_spines(ChassisList *chassislist)
480{
481	Node *node, *remnode;
482	Port *port;
483	int i;
484
485	for (i = 1; i <= LINES_MAX_NUM; i++) {
486		node = chassislist->linenode[i];
487
488		if (!(node && is_line(node)))
489			continue;	/* empty slot or router */
490
491		for (port = node->ports; port; port = port->next) {
492			if (port->portnum > 12)
493				continue;
494
495			if (!port->remoteport)
496				continue;
497			remnode = port->remoteport->node;
498
499			if (!remnode->chrecord)
500				continue;	/* some error - spine not initialized ? FIXME */
501			insert_spine(remnode, chassislist);
502		}
503	}
504}
505
506static void pass_on_spines_catch_lines(ChassisList *chassislist)
507{
508	Node *node, *remnode;
509	Port *port;
510	int i;
511
512	for (i = 1; i <= SPINES_MAX_NUM; i++) {
513		node = chassislist->spinenode[i];
514		if (!node)
515			continue;	/* empty slot */
516		for (port = node->ports; port; port = port->next) {
517			if (!port->remoteport)
518				continue;
519			remnode = port->remoteport->node;
520
521			if (!remnode->chrecord)
522				continue;	/* some error - line/router not initialized ? FIXME */
523			insert_line_router(remnode, chassislist);
524		}
525	}
526}
527
528/*
529	Stupid interpolation algorithm...
530	But nothing to do - have to be compliant with VoltaireSM/NMS
531*/
532static void pass_on_spines_interpolate_chguid(ChassisList *chassislist)
533{
534	Node *node;
535	int i;
536
537	for (i = 1; i <= SPINES_MAX_NUM; i++) {
538		node = chassislist->spinenode[i];
539		if (!node)
540			continue;	/* skip the empty slots */
541
542		/* take first guid minus one to be consistent with SM */
543		chassislist->chassisguid = node->nodeguid - 1;
544		break;
545	}
546}
547
548/*
549	This function fills chassislist structure with all nodes
550	in that chassis
551	chassislist structure = structure of one standalone chassis
552*/
553static void build_chassis(Node *node, ChassisList *chassislist)
554{
555	Node *remnode = 0;
556	Port *port = 0;
557
558	/* we get here with node = chassis_spine */
559	chassislist->chassistype = node->chrecord->chassistype;
560	insert_spine(node, chassislist);
561
562	/* loop: pass on all ports of node */
563	for (port = node->ports; port; port = port->next) {
564		if (!port->remoteport)
565			continue;
566		remnode = port->remoteport->node;
567
568		if (!remnode->chrecord)
569			continue; /* some error - line or router not initialized ? FIXME */
570
571		insert_line_router(remnode, chassislist);
572	}
573
574	pass_on_lines_catch_spines(chassislist);
575	/* this pass needed for to catch routers, since routers connected only */
576	/* to spines in slot 1 or 4 and we could miss them first time */
577	pass_on_spines_catch_lines(chassislist);
578
579	/* additional 2 passes needed for to overcome a problem of pure "in-chassis" */
580	/* connectivity - extra pass to ensure that all related chips/modules */
581	/* inserted into the chassislist */
582	pass_on_lines_catch_spines(chassislist);
583	pass_on_spines_catch_lines(chassislist);
584	pass_on_spines_interpolate_chguid(chassislist);
585}
586
587/*========================================================*/
588/*                INTERNAL TO EXTERNAL PORT MAPPING       */
589/*========================================================*/
590
591/*
592Description : On ISR9288/9096 external ports indexing
593              is not matching the internal ( anafa ) port
594              indexes. Use this MAP to translate the data you get from
595              the OpenIB diagnostics (smpquery, ibroute, ibtracert, etc.)
596
597
598Module : sLB-24
599                anafa 1             anafa 2
600ext port | 13 14 15 16 17 18 | 19 20 21 22 23 24
601int port | 22 23 24 18 17 16 | 22 23 24 18 17 16
602ext port | 1  2  3  4  5  6  | 7  8  9  10 11 12
603int port | 19 20 21 15 14 13 | 19 20 21 15 14 13
604------------------------------------------------
605
606Module : sLB-8
607                anafa 1             anafa 2
608ext port | 13 14 15 16 17 18 | 19 20 21 22 23 24
609int port | 24 23 22 18 17 16 | 24 23 22 18 17 16
610ext port | 1  2  3  4  5  6  | 7  8  9  10 11 12
611int port | 21 20 19 15 14 13 | 21 20 19 15 14 13
612
613----------->
614                anafa 1             anafa 2
615ext port | -  -  5  -  -  6  | -  -  7  -  -  8
616int port | 24 23 22 18 17 16 | 24 23 22 18 17 16
617ext port | -  -  1  -  -  2  | -  -  3  -  -  4
618int port | 21 20 19 15 14 13 | 21 20 19 15 14 13
619------------------------------------------------
620
621Module : sLB-2024
622
623ext port | 13 14 15 16 17 18 19 20 21 22 23 24
624A1 int port| 13 14 15 16 17 18 19 20 21 22 23 24
625ext port | 1 2 3 4 5 6 7 8 9 10 11 12
626A2 int port| 13 14 15 16 17 18 19 20 21 22 23 24
627---------------------------------------------------
628
629*/
630
631int int2ext_map_slb24[2][25] = {
632					{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 5, 4, 18, 17, 16, 1, 2, 3, 13, 14, 15 },
633					{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 11, 10, 24, 23, 22, 7, 8, 9, 19, 20, 21 }
634				};
635int int2ext_map_slb8[2][25] = {
636					{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 6, 6, 6, 1, 1, 1, 5, 5, 5 },
637					{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 8, 8, 8, 3, 3, 3, 7, 7, 7 }
638				};
639int int2ext_map_slb2024[2][25] = {
640					{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 },
641					{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }
642				};
643/*	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 }; */
644
645/*
646	This function relevant only for line modules/chips
647	Returns string with external port index
648*/
649char *portmapstring(Port *port)
650{
651	static char mapping[OUT_BUFFER_SIZE];
652	ChassisRecord *ch = port->node->chrecord;
653	int portnum = port->portnum;
654	int chipnum = 0;
655	int pindex = 0;
656	Node *node = port->node;
657
658	if (!ch || !is_line(node) || (portnum < 13 || portnum > 24))
659		return NULL;
660
661	if (ch->anafanum < 1 || ch->anafanum > 2)
662		return NULL;
663
664	memset(mapping, 0, sizeof(mapping));
665
666	chipnum = ch->anafanum - 1;
667
668	if (is_line_24(node))
669		pindex = int2ext_map_slb24[chipnum][portnum];
670	else if (is_line_2024(node))
671		pindex = int2ext_map_slb2024[chipnum][portnum];
672	else
673		pindex = int2ext_map_slb8[chipnum][portnum];
674
675	sprintf(mapping, "[ext %d]", pindex);
676
677	return mapping;
678}
679
680static void add_chassislist()
681{
682	if (!(mylist.current = calloc(1, sizeof(ChassisList))))
683		IBPANIC("out of mem");
684
685	if (mylist.first == NULL) {
686		mylist.first = mylist.current;
687		mylist.last = mylist.current;
688	} else {
689		mylist.last->next = mylist.current;
690		mylist.current->next = NULL;
691		mylist.last = mylist.current;
692	}
693}
694
695/*
696	Main grouping function
697	Algorithm:
698	1. pass on every Voltaire node
699	2. catch spine chip for every Voltaire node
700		2.1 build/interpolate chassis around this chip
701		2.2 go to 1.
702	3. pass on non Voltaire nodes (SystemImageGUID based grouping)
703	4. now group non Voltaire nodes by SystemImageGUID
704*/
705ChassisList *group_nodes()
706{
707	Node *node;
708	int dist;
709	int chassisnum = 0;
710	struct ChassisList *chassis;
711
712	mylist.first = NULL;
713	mylist.current = NULL;
714	mylist.last = NULL;
715
716	/* first pass on switches and build for every Voltaire node */
717	/* an appropriate chassis record (slotnum and position) */
718	/* according to internal connectivity */
719	/* not very efficient but clear code so... */
720	for (dist = 0; dist <= maxhops_discovered; dist++) {
721		for (node = nodesdist[dist]; node; node = node->dnext) {
722			if (node->vendid == VTR_VENDOR_ID)
723				fill_chassis_record(node);
724		}
725	}
726
727	/* separate every Voltaire chassis from each other and build linked list of them */
728	/* algorithm: catch spine and find all surrounding nodes */
729	for (dist = 0; dist <= maxhops_discovered; dist++) {
730		for (node = nodesdist[dist]; node; node = node->dnext) {
731			if (node->vendid != VTR_VENDOR_ID)
732				continue;
733			if (!node->chrecord || node->chrecord->chassisnum || !is_spine(node))
734				continue;
735			add_chassislist();
736			mylist.current->chassisnum = ++chassisnum;
737			build_chassis(node, mylist.current);
738		}
739	}
740
741	/* now make pass on nodes for chassis which are not Voltaire */
742	/* grouped by common SystemImageGUID */
743	for (dist = 0; dist <= maxhops_discovered; dist++) {
744		for (node = nodesdist[dist]; node; node = node->dnext) {
745			if (node->vendid == VTR_VENDOR_ID)
746				continue;
747			if (node->sysimgguid) {
748				chassis = find_chassisguid(node);
749				if (chassis)
750					chassis->nodecount++;
751				else {
752					/* Possible new chassis */
753					add_chassislist();
754					mylist.current->chassisguid = get_chassisguid(node);
755					mylist.current->nodecount = 1;
756				}
757			}
758		}
759	}
760
761	/* now, make another pass to see which nodes are part of chassis */
762	/* (defined as chassis->nodecount > 1) */
763	for (dist = 0; dist <= MAXHOPS; ) {
764		for (node = nodesdist[dist]; node; node = node->dnext) {
765			if (node->vendid == VTR_VENDOR_ID)
766				continue;
767			if (node->sysimgguid) {
768				chassis = find_chassisguid(node);
769				if (chassis && chassis->nodecount > 1) {
770					if (!chassis->chassisnum)
771						chassis->chassisnum = ++chassisnum;
772					if (!node->chrecord) {
773						if (!(node->chrecord = calloc(1, sizeof(ChassisRecord))))
774							IBPANIC("out of mem");
775						node->chrecord->chassisnum = chassis->chassisnum;
776					}
777				}
778			}
779		}
780		if (dist == maxhops_discovered)
781			dist = MAXHOPS;	/* skip to CAs */
782		else
783			dist++;
784	}
785
786	return (mylist.first);
787}
788