1/*-
2 * Copyright (c) 2017 Broadcom. All rights reserved.
3 * The term "Broadcom" refers to Broadcom Limited and/or its subsidiaries.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 *    this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 *    this list of conditions and the following disclaimer in the documentation
13 *    and/or other materials provided with the distribution.
14 *
15 * 3. Neither the name of the copyright holder nor the names of its contributors
16 *    may be used to endorse or promote products derived from this software
17 *    without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32/**
33 * @file
34 * Handles the domain object callback from the HW.
35 */
36
37/*!
38@defgroup domain_sm Domain State Machine: States
39*/
40
41#include "ocs.h"
42
43#include "ocs_fabric.h"
44#include "ocs_device.h"
45
46#define domain_sm_trace(domain)  \
47	do { \
48		if (OCS_LOG_ENABLE_DOMAIN_SM_TRACE(domain->ocs)) \
49			ocs_log_info(domain->ocs, "[domain] %-20s %-20s\n", __func__, ocs_sm_event_name(evt)); \
50	} while (0)
51
52#define domain_trace(domain, fmt, ...) \
53	do { \
54		if (OCS_LOG_ENABLE_DOMAIN_SM_TRACE(domain ? domain->ocs : NULL)) \
55			ocs_log_info(domain ? domain->ocs : NULL, fmt, ##__VA_ARGS__); \
56	} while (0)
57
58#define domain_printf(domain, fmt, ...) \
59	do { \
60		ocs_log_info(domain ? domain->ocs : NULL, fmt, ##__VA_ARGS__); \
61	} while (0)
62
63void ocs_mgmt_domain_list(ocs_textbuf_t *textbuf, void *domain);
64void ocs_mgmt_domain_get_all(ocs_textbuf_t *textbuf, void *domain);
65int ocs_mgmt_domain_get(ocs_textbuf_t *textbuf, char *parent, char *name, void *domain);
66int ocs_mgmt_domain_set(char *parent, char *name, char *value, void *domain);
67int ocs_mgmt_domain_exec(char *parent, char *action, void *arg_in, uint32_t arg_in_length,
68		void *arg_out, uint32_t arg_out_length, void *domain);
69
70static ocs_mgmt_functions_t domain_mgmt_functions = {
71	.get_list_handler = ocs_mgmt_domain_list,
72	.get_handler = ocs_mgmt_domain_get,
73	.get_all_handler = ocs_mgmt_domain_get_all,
74	.set_handler = ocs_mgmt_domain_set,
75	.exec_handler = ocs_mgmt_domain_exec,
76};
77
78/**
79 * @brief Accept domain callback events from the HW.
80 *
81 * <h3 class="desc">Description</h3>
82 * HW calls this function with various domain-related events.
83 *
84 * @param arg Application-specified argument.
85 * @param event Domain event.
86 * @param data Event specific data.
87 *
88 * @return Returns 0 on success; or a negative error value on failure.
89 */
90
91int32_t
92ocs_domain_cb(void *arg, ocs_hw_domain_event_e event, void *data)
93{
94	ocs_t *ocs = arg;
95	ocs_domain_t *domain = NULL;
96	int32_t rc = 0;
97
98	ocs_assert(data, -1);
99
100	if (event != OCS_HW_DOMAIN_FOUND) {
101		domain = data;
102	}
103
104	switch (event) {
105	case OCS_HW_DOMAIN_FOUND: {
106		uint64_t fcf_wwn = 0;
107		ocs_domain_record_t *drec = data;
108		ocs_assert(drec, -1);
109
110		/* extract the fcf_wwn */
111		fcf_wwn = ocs_be64toh(*((uint64_t*)drec->wwn));
112
113		/* lookup domain, or allocate a new one if one doesn't exist already */
114		domain = ocs_domain_find(ocs, fcf_wwn);
115		if (domain == NULL) {
116			domain = ocs_domain_alloc(ocs, fcf_wwn);
117			if (domain == NULL) {
118				ocs_log_err(ocs, "ocs_domain_alloc() failed\n");
119				rc = -1;
120				break;
121			}
122			ocs_sm_transition(&domain->drvsm, __ocs_domain_init, NULL);
123		}
124		ocs_domain_post_event(domain, OCS_EVT_DOMAIN_FOUND, drec);
125		break;
126	}
127
128	case OCS_HW_DOMAIN_LOST:
129		domain_trace(domain, "OCS_HW_DOMAIN_LOST:\n");
130		ocs_domain_hold_frames(domain);
131		ocs_domain_post_event(domain, OCS_EVT_DOMAIN_LOST, NULL);
132		break;
133
134	case OCS_HW_DOMAIN_ALLOC_OK: {
135		domain_trace(domain, "OCS_HW_DOMAIN_ALLOC_OK:\n");
136		domain->instance_index = 0;
137		ocs_domain_post_event(domain, OCS_EVT_DOMAIN_ALLOC_OK, NULL);
138		break;
139	}
140
141	case OCS_HW_DOMAIN_ALLOC_FAIL:
142		domain_trace(domain, "OCS_HW_DOMAIN_ALLOC_FAIL:\n");
143		ocs_domain_post_event(domain, OCS_EVT_DOMAIN_ALLOC_FAIL, NULL);
144		break;
145
146	case OCS_HW_DOMAIN_ATTACH_OK:
147		domain_trace(domain, "OCS_HW_DOMAIN_ATTACH_OK:\n");
148		ocs_domain_post_event(domain, OCS_EVT_DOMAIN_ATTACH_OK, NULL);
149		break;
150
151	case OCS_HW_DOMAIN_ATTACH_FAIL:
152		domain_trace(domain, "OCS_HW_DOMAIN_ATTACH_FAIL:\n");
153		ocs_domain_post_event(domain, OCS_EVT_DOMAIN_ATTACH_FAIL, NULL);
154		break;
155
156	case OCS_HW_DOMAIN_FREE_OK:
157		domain_trace(domain, "OCS_HW_DOMAIN_FREE_OK:\n");
158		ocs_domain_post_event(domain, OCS_EVT_DOMAIN_FREE_OK, NULL);
159		break;
160
161	case OCS_HW_DOMAIN_FREE_FAIL:
162		domain_trace(domain, "OCS_HW_DOMAIN_FREE_FAIL:\n");
163		ocs_domain_post_event(domain, OCS_EVT_DOMAIN_FREE_FAIL, NULL);
164		break;
165
166	default:
167		ocs_log_warn(ocs, "unsupported event %#x\n", event);
168	}
169
170	return rc;
171}
172
173/**
174 * @brief Find the domain, given its FCF_WWN.
175 *
176 * <h3 class="desc">Description</h3>
177 * Search the domain_list to find a matching domain object.
178 *
179 * @param ocs Pointer to the OCS device.
180 * @param fcf_wwn FCF WWN to find.
181 *
182 * @return Returns the pointer to the domain if found; or NULL otherwise.
183 */
184
185ocs_domain_t *
186ocs_domain_find(ocs_t *ocs, uint64_t fcf_wwn)
187{
188	ocs_domain_t *domain = NULL;
189
190	/* Check to see if this domain is already allocated */
191	ocs_device_lock(ocs);
192		ocs_list_foreach(&ocs->domain_list, domain) {
193			if (fcf_wwn == domain->fcf_wwn) {
194				break;
195			}
196		}
197	ocs_device_unlock(ocs);
198	return domain;
199}
200
201/**
202 * @brief Allocate a domain object.
203 *
204 * <h3 class="desc">Description</h3>
205 * A domain object is allocated and initialized. It is associated with the
206 * \c ocs argument.
207 *
208 * @param ocs Pointer to the OCS device.
209 * @param fcf_wwn FCF WWN of the domain.
210 *
211 * @return Returns a pointer to the ocs_domain_t object; or NULL.
212 */
213
214ocs_domain_t *
215ocs_domain_alloc(ocs_t *ocs, uint64_t fcf_wwn)
216{
217	ocs_domain_t *domain;
218
219	ocs_assert(ocs, NULL);
220
221	domain = ocs_malloc(ocs, sizeof(*domain), OCS_M_NOWAIT | OCS_M_ZERO);
222	if (domain) {
223		domain->ocs = ocs;
224		domain->instance_index = ocs->domain_instance_count++;
225		domain->drvsm.app = domain;
226		ocs_domain_lock_init(domain);
227		ocs_lock_init(ocs, &domain->lookup_lock, "Domain lookup[%d]", domain->instance_index);
228
229		/* Allocate a sparse vector for sport FC_ID's */
230		domain->lookup = spv_new(ocs);
231		if (domain->lookup == NULL) {
232			ocs_log_err(ocs, "spv_new() failed\n");
233			ocs_free(ocs, domain, sizeof(*domain));
234			return NULL;
235		}
236
237		ocs_list_init(&domain->sport_list, ocs_sport_t, link);
238		domain->fcf_wwn = fcf_wwn;
239		ocs_log_debug(ocs, "Domain allocated: wwn %016" PRIX64 "\n", domain->fcf_wwn);
240		domain->femul_enable = (ocs->ctrlmask & OCS_CTRLMASK_ENABLE_FABRIC_EMULATION) != 0;
241
242		ocs_device_lock(ocs);
243			/* if this is the first domain, then assign it as the "root" domain */
244			if (ocs_list_empty(&ocs->domain_list)) {
245				ocs->domain = domain;
246			}
247			ocs_list_add_tail(&ocs->domain_list, domain);
248		ocs_device_unlock(ocs);
249
250		domain->mgmt_functions = &domain_mgmt_functions;
251	} else {
252		ocs_log_err(ocs, "domain allocation failed\n");
253	}
254
255	return domain;
256}
257
258/**
259 * @brief Free a domain object.
260 *
261 * <h3 class="desc">Description</h3>
262 * The domain object is freed.
263 *
264 * @param domain Domain object to free.
265 *
266 * @return None.
267 */
268
269void
270ocs_domain_free(ocs_domain_t *domain)
271{
272	ocs_t *ocs;
273
274	ocs_assert(domain);
275	ocs_assert(domain->ocs);
276
277	/* Hold frames to clear the domain pointer from the xport lookup */
278	ocs_domain_hold_frames(domain);
279
280	ocs = domain->ocs;
281
282	ocs_log_debug(ocs, "Domain free: wwn %016" PRIX64 "\n", domain->fcf_wwn);
283
284	spv_del(domain->lookup);
285	domain->lookup = NULL;
286
287	ocs_device_lock(ocs);
288		ocs_list_remove(&ocs->domain_list, domain);
289		if (domain == ocs->domain) {
290			/* set global domain to the new head */
291			ocs->domain = ocs_list_get_head(&ocs->domain_list);
292			if (ocs->domain) {
293				ocs_log_debug(ocs, "setting new domain, old=%p new=%p\n",
294						domain, ocs->domain);
295			}
296		}
297
298		if (ocs_list_empty(&ocs->domain_list) && ocs->domain_list_empty_cb ) {
299			(*ocs->domain_list_empty_cb)(ocs, ocs->domain_list_empty_cb_arg);
300		}
301	ocs_device_unlock(ocs);
302
303	ocs_lock_free(&domain->lookup_lock);
304
305	ocs_free(ocs, domain, sizeof(*domain));
306}
307
308/**
309 * @brief Free memory resources of a domain object.
310 *
311 * <h3 class="desc">Description</h3>
312 * After the domain object is freed, its child objects are also freed.
313 *
314 * @param domain Pointer to a domain object.
315 *
316 * @return None.
317 */
318
319void
320ocs_domain_force_free(ocs_domain_t *domain)
321{
322	ocs_sport_t *sport;
323	ocs_sport_t *next;
324
325	/* Shutdown domain sm */
326	ocs_sm_disable(&domain->drvsm);
327
328	ocs_scsi_notify_domain_force_free(domain);
329
330	ocs_domain_lock(domain);
331		ocs_list_foreach_safe(&domain->sport_list, sport, next) {
332			ocs_sport_force_free(sport);
333		}
334	ocs_domain_unlock(domain);
335	ocs_hw_domain_force_free(&domain->ocs->hw, domain);
336	ocs_domain_free(domain);
337}
338
339/**
340 * @brief Register a callback when the domain_list goes empty.
341 *
342 * <h3 class="desc">Description</h3>
343 * A function callback may be registered when the domain_list goes empty.
344 *
345 * @param ocs Pointer to a device object.
346 * @param callback Callback function.
347 * @param arg Callback argument.
348 *
349 * @return None.
350 */
351
352void
353ocs_register_domain_list_empty_cb(ocs_t *ocs, void (*callback)(ocs_t *ocs, void *arg), void *arg)
354{
355	ocs_device_lock(ocs);
356		ocs->domain_list_empty_cb = callback;
357		ocs->domain_list_empty_cb_arg = arg;
358		if (ocs_list_empty(&ocs->domain_list) && callback) {
359			(*callback)(ocs, arg);
360		}
361	ocs_device_unlock(ocs);
362}
363
364/**
365 * @brief Return a pointer to the domain, given the instance index.
366 *
367 * <h3 class="desc">Description</h3>
368 * A pointer to the domain context, given by the index, is returned.
369 *
370 * @param ocs Pointer to the driver instance context.
371 * @param index Instance index.
372 *
373 * @return Returns a pointer to the domain; or NULL.
374 */
375
376ocs_domain_t *
377ocs_domain_get_instance(ocs_t *ocs, uint32_t index)
378{
379	ocs_domain_t *domain = NULL;
380
381	if (index >= OCS_MAX_DOMAINS) {
382		ocs_log_err(ocs, "invalid index: %d\n", index);
383		return NULL;
384	}
385	ocs_device_lock(ocs);
386		ocs_list_foreach(&ocs->domain_list, domain) {
387			if (domain->instance_index == index) {
388				break;
389			}
390		}
391	ocs_device_unlock(ocs);
392	return domain;
393}
394
395/**
396 * @ingroup domain_sm
397 * @brief Domain state machine: Common event handler.
398 *
399 * <h3 class="desc">Description</h3>
400 * Common/shared events are handled here for the domain state machine.
401 *
402 * @param funcname Function name text.
403 * @param ctx Domain state machine context.
404 * @param evt Event to process.
405 * @param arg Per event optional argument.
406 *
407 * @return Returns NULL.
408 */
409
410static void *
411__ocs_domain_common(const char *funcname, ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
412{
413	ocs_domain_t *domain = ctx->app;
414
415	switch(evt) {
416	case OCS_EVT_ENTER:
417	case OCS_EVT_REENTER:
418	case OCS_EVT_EXIT:
419	case OCS_EVT_ALL_CHILD_NODES_FREE:
420		/* this can arise if an FLOGI fails on the SPORT, and the SPORT is shutdown */
421		break;
422	default:
423		ocs_log_warn(domain->ocs, "%-20s %-20s not handled\n", funcname, ocs_sm_event_name(evt));
424		break;
425	}
426
427	return NULL;
428}
429
430/**
431 * @ingroup domain_sm
432 * @brief Domain state machine: Common shutdown.
433 *
434 * <h3 class="desc">Description</h3>
435 * Handles common shutdown events.
436 *
437 * @param funcname Function name text.
438 * @param ctx Remote node state machine context.
439 * @param evt Event to process.
440 * @param arg Per event optional argument.
441 *
442 * @return Returns NULL.
443 */
444
445static void *
446__ocs_domain_common_shutdown(const char *funcname, ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
447{
448	ocs_domain_t *domain = ctx->app;
449
450	switch(evt) {
451	case OCS_EVT_ENTER:
452	case OCS_EVT_REENTER:
453	case OCS_EVT_EXIT:
454		break;
455	case OCS_EVT_DOMAIN_FOUND:
456		ocs_assert(arg, NULL);
457		/* sm: / save drec, mark domain_found_pending */
458		ocs_memcpy(&domain->pending_drec, arg, sizeof(domain->pending_drec));
459		domain->domain_found_pending = TRUE;
460		break;
461	case OCS_EVT_DOMAIN_LOST:
462		/* clear drec available
463		 * sm: unmark domain_found_pending */
464		domain->domain_found_pending = FALSE;
465		break;
466
467	default:
468		ocs_log_warn(domain->ocs, "%-20s %-20s not handled\n", funcname, ocs_sm_event_name(evt));
469		break;
470	}
471
472	return NULL;
473}
474
475#define std_domain_state_decl(...) \
476	ocs_domain_t *domain = NULL; \
477	ocs_t *ocs = NULL; \
478	\
479	ocs_assert(ctx, NULL); \
480	ocs_assert(ctx->app, NULL); \
481	domain = ctx->app; \
482	ocs_assert(domain->ocs, NULL); \
483	ocs = domain->ocs; \
484	ocs_assert(ocs->xport, NULL);
485
486/**
487 * @ingroup domain_sm
488 * @brief Domain state machine: Initial state.
489 *
490 * <h3 class="desc">Description</h3>
491 * The initial state for a domain. Each domain is initialized to
492 * this state at start of day (SOD).
493 *
494 * @param ctx Domain state machine context.
495 * @param evt Event to process.
496 * @param arg Per event optional argument.
497 *
498 * @return Returns NULL.
499 */
500
501void *
502__ocs_domain_init(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
503{
504	std_domain_state_decl();
505
506	domain_sm_trace(domain);
507
508	switch(evt) {
509	case OCS_EVT_ENTER:
510		domain->attached = 0;
511		break;
512
513	case OCS_EVT_DOMAIN_FOUND: {
514		int32_t		vlan = 0;
515		uint32_t	i;
516		ocs_domain_record_t *drec = arg;
517		ocs_sport_t *sport;
518
519		uint64_t	my_wwnn = ocs->xport->req_wwnn;
520		uint64_t	my_wwpn = ocs->xport->req_wwpn;
521		uint64_t	be_wwpn;
522
523		/* For now, user must specify both port name and node name, or we let firmware
524		 * pick both (same as for vports).
525		 * TODO: do we want to allow setting only port name or only node name?
526		 */
527		if ((my_wwpn == 0) || (my_wwnn == 0)) {
528			ocs_log_debug(ocs, "using default hardware WWN configuration \n");
529			my_wwpn = ocs_get_wwn(&ocs->hw, OCS_HW_WWN_PORT);
530			my_wwnn = ocs_get_wwn(&ocs->hw, OCS_HW_WWN_NODE);
531		}
532
533		ocs_log_debug(ocs, "Creating base sport using WWPN %016" PRIx64 " WWNN %016" PRIx64 "\n",
534			my_wwpn, my_wwnn);
535
536		/* Allocate a sport and transition to __ocs_sport_allocated */
537		sport = ocs_sport_alloc(domain, my_wwpn, my_wwnn, UINT32_MAX, ocs->enable_ini, ocs->enable_tgt);
538
539		if (sport == NULL) {
540			ocs_log_err(ocs, "ocs_sport_alloc() failed\n");
541			break;
542		}
543		ocs_sm_transition(&sport->sm, __ocs_sport_allocated, NULL);
544
545		/* If domain is ethernet, then fetch the vlan id value */
546		if (drec->is_ethernet) {
547			vlan = ocs_bitmap_search((void *)drec->map.vlan, TRUE, 512 * 8);
548			if (vlan < 0) {
549				ocs_log_err(ocs, "no VLAN id available (FCF=%d)\n",
550						drec->index);
551				break;
552			}
553		}
554
555		be_wwpn = ocs_htobe64(sport->wwpn);
556
557		/* allocate ocs_sli_port_t object for local port
558		 * Note: drec->fc_id is ALPA from read_topology only if loop
559		 */
560		if (ocs_hw_port_alloc(&ocs->hw, sport, NULL, (uint8_t *)&be_wwpn)) {
561			ocs_log_err(ocs, "Can't allocate port\n");
562			ocs_sport_free(sport);
563			break;
564		}
565
566		/* initialize domain object */
567		domain->is_loop = drec->is_loop;
568		domain->is_fc = drec->is_fc;
569
570		/*
571		 * If the loop position map includes ALPA == 0, then we are in a public loop (NL_PORT)
572		 * Note that the first element of the loopmap[] contains the count of elements, and if
573		 * ALPA == 0 is present, it will occupy the first location after the count.
574		 */
575		domain->is_nlport = drec->map.loop[1] == 0x00;
576
577		if (domain->is_loop) {
578			ocs_log_debug(ocs, "%s fc_id=%#x speed=%d\n",
579					drec->is_loop ? (domain->is_nlport ? "public-loop" : "loop") : "other",
580					drec->fc_id, drec->speed);
581
582			sport->fc_id = drec->fc_id;
583			sport->topology = OCS_SPORT_TOPOLOGY_LOOP;
584			ocs_snprintf(sport->display_name, sizeof(sport->display_name), "s%06x", drec->fc_id);
585
586			if (ocs->enable_ini) {
587				uint32_t count = drec->map.loop[0];
588				ocs_log_debug(ocs, "%d position map entries\n", count);
589				for (i = 1; i <= count; i++) {
590					if (drec->map.loop[i] != drec->fc_id) {
591						ocs_node_t *node;
592
593						ocs_log_debug(ocs, "%#x -> %#x\n",
594								drec->fc_id, drec->map.loop[i]);
595						node = ocs_node_alloc(sport, drec->map.loop[i], FALSE, TRUE);
596						if (node == NULL) {
597							ocs_log_err(ocs, "ocs_node_alloc() failed\n");
598							break;
599						}
600						ocs_node_transition(node, __ocs_d_wait_loop, NULL);
601					}
602				}
603			}
604		}
605
606		/* Initiate HW domain alloc */
607		if (ocs_hw_domain_alloc(&ocs->hw, domain, drec->index, vlan)) {
608			ocs_log_err(ocs, "Failed to initiate HW domain allocation\n");
609			break;
610		}
611		ocs_sm_transition(ctx, __ocs_domain_wait_alloc, arg);
612		break;
613	}
614	default:
615		__ocs_domain_common(__func__, ctx, evt, arg);
616		return NULL;
617	}
618
619	return NULL;
620}
621
622/**
623 * @ingroup domain_sm
624 * @brief Domain state machine: Wait for the domain allocation to complete.
625 *
626 * <h3 class="desc">Description</h3>
627 * Waits for the domain state to be allocated. After the HW domain
628 * allocation process has been initiated, this state waits for
629 * that process to complete (i.e. a domain-alloc-ok event).
630 *
631 * @param ctx Domain state machine context.
632 * @param evt Event to process.
633 * @param arg Per event optional argument.
634 *
635 * @return Returns NULL.
636 */
637
638void *
639__ocs_domain_wait_alloc(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
640{
641	ocs_sport_t *sport;
642	std_domain_state_decl();
643
644	domain_sm_trace(domain);
645
646	switch(evt) {
647	case OCS_EVT_DOMAIN_ALLOC_OK: {
648		char prop_buf[32];
649		uint64_t wwn_bump = 0;
650		fc_plogi_payload_t *sp;
651
652		if (ocs_get_property("wwn_bump", prop_buf, sizeof(prop_buf)) == 0) {
653			wwn_bump = ocs_strtoull(prop_buf, 0, 0);
654		}
655
656		sport = domain->sport;
657		ocs_assert(sport, NULL);
658		sp = (fc_plogi_payload_t*) sport->service_params;
659
660		/* Save the domain service parameters */
661		ocs_memcpy(domain->service_params + 4, domain->dma.virt, sizeof(fc_plogi_payload_t) - 4);
662		ocs_memcpy(sport->service_params + 4, domain->dma.virt, sizeof(fc_plogi_payload_t) - 4);
663
664		/* If we're in fabric emulation mode, the flogi service parameters have not been setup yet,
665		 * so we need some reasonable BB credit value
666		 */
667		if (domain->femul_enable) {
668			ocs_memcpy(domain->flogi_service_params + 4, domain->service_params + 4, sizeof(fc_plogi_payload_t) - 4);
669		}
670
671		/* Update the sport's service parameters, user might have specified non-default names */
672		sp->port_name_hi = ocs_htobe32((uint32_t) (sport->wwpn >> 32ll));
673		sp->port_name_lo = ocs_htobe32((uint32_t) sport->wwpn);
674		sp->node_name_hi = ocs_htobe32((uint32_t) (sport->wwnn >> 32ll));
675		sp->node_name_lo = ocs_htobe32((uint32_t) sport->wwnn);
676
677		if (wwn_bump) {
678			sp->port_name_lo = ocs_htobe32(ocs_be32toh(sp->port_name_lo) ^ ((uint32_t)(wwn_bump)));
679			sp->port_name_hi = ocs_htobe32(ocs_be32toh(sp->port_name_hi) ^ ((uint32_t)(wwn_bump >> 32)));
680			sp->node_name_lo = ocs_htobe32(ocs_be32toh(sp->node_name_lo) ^ ((uint32_t)(wwn_bump)));
681			sp->node_name_hi = ocs_htobe32(ocs_be32toh(sp->node_name_hi) ^ ((uint32_t)(wwn_bump >> 32)));
682			ocs_log_info(ocs, "Overriding WWN\n");
683		}
684
685		/* Take the loop topology path, unless we are an NL_PORT (public loop) */
686		if (domain->is_loop && !domain->is_nlport) {
687			/*
688			 * For loop, we already have our FC ID and don't need fabric login.
689			 * Transition to the allocated state and post an event to attach to
690			 * the domain. Note that this breaks the normal action/transition
691			 * pattern here to avoid a race with the domain attach callback.
692			 */
693			/* sm: is_loop / domain_attach */
694			ocs_sm_transition(ctx, __ocs_domain_allocated, NULL);
695			__ocs_domain_attach_internal(domain, sport->fc_id);
696			break;
697		} else {
698			ocs_node_t *node;
699
700			/* alloc fabric node, send FLOGI */
701			node = ocs_node_find(sport, FC_ADDR_FABRIC);
702			if (node) {
703				ocs_log_err(ocs, "Hmmmm ... Fabric Controller node already exists\n");
704				break;
705			}
706			node = ocs_node_alloc(sport, FC_ADDR_FABRIC, FALSE, FALSE);
707			if (!node) {
708				ocs_log_err(ocs, "Error: ocs_node_alloc() failed\n");
709			} else {
710				if (ocs->nodedb_mask & OCS_NODEDB_PAUSE_FABRIC_LOGIN) {
711					ocs_node_pause(node, __ocs_fabric_init);
712				} else {
713					ocs_node_transition(node, __ocs_fabric_init, NULL);
714				}
715			}
716			/* Accept frames */
717			domain->req_accept_frames = 1;
718		}
719		/* sm: start fabric logins */
720		ocs_sm_transition(ctx, __ocs_domain_allocated, NULL);
721		break;
722	}
723
724	case OCS_EVT_DOMAIN_ALLOC_FAIL:
725		/* TODO: hw/device reset */
726		ocs_log_err(ocs, "%s recv'd waiting for DOMAIN_ALLOC_OK; shutting down domain\n",
727			ocs_sm_event_name(evt));
728		domain->req_domain_free = 1;
729		break;
730
731	case OCS_EVT_DOMAIN_FOUND:
732		/* Should not happen */
733		ocs_assert(evt, NULL);
734		break;
735
736	case OCS_EVT_DOMAIN_LOST:
737		ocs_log_debug(ocs, "%s received while waiting for ocs_hw_domain_alloc() to complete\n", ocs_sm_event_name(evt));
738		ocs_sm_transition(ctx, __ocs_domain_wait_domain_lost, NULL);
739		break;
740
741	default:
742		__ocs_domain_common(__func__, ctx, evt, arg);
743		return NULL;
744	}
745
746	return NULL;
747}
748
749/**
750 * @ingroup domain_sm
751 * @brief Domain state machine: Wait for the domain attach request.
752 *
753 * <h3 class="desc">Description</h3>
754 * In this state, the domain has been allocated and is waiting for a domain attach request.
755 * The attach request comes from a node instance completing the fabric login,
756 * or from a point-to-point negotiation and login.
757 *
758 * @param ctx Remote node state machine context.
759 * @param evt Event to process.
760 * @param arg Per event optional argument.
761 *
762 * @return Returns NULL.
763 */
764
765void *
766__ocs_domain_allocated(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
767{
768	int32_t rc = 0;
769	std_domain_state_decl();
770
771	domain_sm_trace(domain);
772
773	switch(evt) {
774	case OCS_EVT_DOMAIN_REQ_ATTACH: {
775		uint32_t fc_id;
776
777		ocs_assert(arg, NULL);
778
779		fc_id = *((uint32_t*)arg);
780		ocs_log_debug(ocs, "Requesting hw domain attach fc_id x%x\n", fc_id);
781		/* Update sport lookup */
782		ocs_lock(&domain->lookup_lock);
783			spv_set(domain->lookup, fc_id, domain->sport);
784		ocs_unlock(&domain->lookup_lock);
785
786		/* Update display name for the sport */
787		ocs_node_fcid_display(fc_id, domain->sport->display_name, sizeof(domain->sport->display_name));
788
789		/* Issue domain attach call */
790		rc = ocs_hw_domain_attach(&ocs->hw, domain, fc_id);
791		if (rc) {
792			ocs_log_err(ocs, "ocs_hw_domain_attach failed: %d\n", rc);
793			return NULL;
794		}
795		/* sm: / domain_attach */
796		ocs_sm_transition(ctx, __ocs_domain_wait_attach, NULL);
797		break;
798	}
799
800	case OCS_EVT_DOMAIN_FOUND:
801		/* Should not happen */
802		ocs_assert(evt, NULL);
803		break;
804
805	case OCS_EVT_DOMAIN_LOST: {
806		int32_t rc;
807		ocs_log_debug(ocs, "%s received while waiting for OCS_EVT_DOMAIN_REQ_ATTACH\n",
808			ocs_sm_event_name(evt));
809		ocs_domain_lock(domain);
810		if (!ocs_list_empty(&domain->sport_list)) {
811			/* if there are sports, transition to wait state and
812			 * send shutdown to each sport */
813			ocs_sport_t	*sport = NULL;
814			ocs_sport_t	*sport_next = NULL;
815			ocs_sm_transition(ctx, __ocs_domain_wait_sports_free, NULL);
816			ocs_list_foreach_safe(&domain->sport_list, sport, sport_next) {
817				ocs_sm_post_event(&sport->sm, OCS_EVT_SHUTDOWN, NULL);
818			}
819			ocs_domain_unlock(domain);
820		} else {
821			ocs_domain_unlock(domain);
822			/* no sports exist, free domain */
823			ocs_sm_transition(ctx, __ocs_domain_wait_shutdown, NULL);
824			rc = ocs_hw_domain_free(&ocs->hw, domain);
825			if (rc) {
826				ocs_log_err(ocs, "ocs_hw_domain_free() failed: %d\n", rc);
827				/* TODO: hw/device reset needed */
828			}
829		}
830
831		break;
832	}
833
834	default:
835		__ocs_domain_common(__func__, ctx, evt, arg);
836		return NULL;
837	}
838
839	return NULL;
840}
841
842/**
843 * @ingroup domain_sm
844 * @brief Domain state machine: Wait for the HW domain attach to complete.
845 *
846 * <h3 class="desc">Description</h3>
847 * Waits for the HW domain attach to complete. Forwards attach ok event to the
848 * fabric node state machine.
849 *
850 * @param ctx Remote node state machine context.
851 * @param evt Event to process.
852 * @param arg Per event optional argument.
853 *
854 * @return Returns NULL.
855 */
856
857void *
858__ocs_domain_wait_attach(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
859{
860	std_domain_state_decl();
861
862	domain_sm_trace(domain);
863
864	switch(evt) {
865	case OCS_EVT_DOMAIN_ATTACH_OK: {
866		ocs_node_t *node = NULL;
867		ocs_node_t *next_node = NULL;
868		ocs_sport_t *sport;
869		ocs_sport_t *next_sport;
870
871		/* Mark as attached */
872		domain->attached = 1;
873
874		/* Register with SCSI API */
875		if (ocs->enable_tgt)
876			ocs_scsi_tgt_new_domain(domain);
877		if (ocs->enable_ini)
878			ocs_scsi_ini_new_domain(domain);
879
880		/* Transition to ready */
881		/* sm: / forward event to all sports and nodes */
882		ocs_sm_transition(ctx, __ocs_domain_ready, NULL);
883
884		/* We have an FCFI, so we can accept frames */
885		domain->req_accept_frames = 1;
886		/* Set domain notify pending state to avoid duplicate domain event post */
887		domain->domain_notify_pend = 1;
888
889		/* Notify all nodes that the domain attach request has completed
890		 * Note: sport will have already received notification of sport attached
891		 * as a result of the HW's port attach.
892		 */
893		ocs_domain_lock(domain);
894			ocs_list_foreach_safe(&domain->sport_list, sport, next_sport) {
895				ocs_sport_lock(sport);
896					ocs_list_foreach_safe(&sport->node_list, node, next_node) {
897						ocs_node_post_event(node, OCS_EVT_DOMAIN_ATTACH_OK, NULL);
898					}
899				ocs_sport_unlock(sport);
900			}
901		ocs_domain_unlock(domain);
902		domain->domain_notify_pend = 0;
903		break;
904	}
905
906	case OCS_EVT_DOMAIN_ATTACH_FAIL:
907		ocs_log_debug(ocs, "%s received while waiting for hw attach to complete\n", ocs_sm_event_name(evt));
908		/* TODO: hw/device reset */
909		break;
910
911	case OCS_EVT_DOMAIN_FOUND:
912		/* Should not happen */
913		ocs_assert(evt, NULL);
914		break;
915
916	case OCS_EVT_DOMAIN_LOST:
917		/* Domain lost while waiting for an attach to complete, go to a state that waits for
918		 * the domain attach to complete, then handle domain lost
919		 */
920		ocs_sm_transition(ctx, __ocs_domain_wait_domain_lost, NULL);
921		break;
922
923	case OCS_EVT_DOMAIN_REQ_ATTACH:
924		/* In P2P we can get an attach request from the other FLOGI path, so drop this one */
925		break;
926
927	default:
928		__ocs_domain_common(__func__, ctx, evt, arg);
929		return NULL;
930	}
931
932	return NULL;
933}
934
935/**
936 * @ingroup domain_sm
937 * @brief Domain state machine: Ready state.
938 *
939 * <h3 class="desc">Description</h3>
940 * This is a domain ready state. It waits for a domain-lost event, and initiates shutdown.
941 *
942 * @param ctx Remote node state machine context.
943 * @param evt Event to process.
944 * @param arg Per event optional argument.
945 *
946 * @return Returns NULL.
947 */
948
949void *
950__ocs_domain_ready(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
951{
952	std_domain_state_decl();
953
954	domain_sm_trace(domain);
955
956	switch(evt) {
957	case OCS_EVT_ENTER: {
958		/* start any pending vports */
959		if (ocs_vport_start(domain)) {
960			ocs_log_debug(domain->ocs, "ocs_vport_start() did not start all vports\n");
961		}
962		break;
963	}
964	case OCS_EVT_DOMAIN_LOST: {
965		int32_t rc;
966		ocs_domain_lock(domain);
967		if (!ocs_list_empty(&domain->sport_list)) {
968			/* if there are sports, transition to wait state and send
969			* shutdown to each sport */
970			ocs_sport_t	*sport = NULL;
971			ocs_sport_t	*sport_next = NULL;
972			ocs_sm_transition(ctx, __ocs_domain_wait_sports_free, NULL);
973			ocs_list_foreach_safe(&domain->sport_list, sport, sport_next) {
974				ocs_sm_post_event(&sport->sm, OCS_EVT_SHUTDOWN, NULL);
975			}
976			ocs_domain_unlock(domain);
977		} else {
978			ocs_domain_unlock(domain);
979			/* no sports exist, free domain */
980			ocs_sm_transition(ctx, __ocs_domain_wait_shutdown, NULL);
981			rc = ocs_hw_domain_free(&ocs->hw, domain);
982			if (rc) {
983				ocs_log_err(ocs, "ocs_hw_domain_free() failed: %d\n", rc);
984				/* TODO: hw/device reset needed */
985			}
986		}
987		break;
988	}
989
990	case OCS_EVT_DOMAIN_FOUND:
991		/* Should not happen */
992		ocs_assert(evt, NULL);
993		break;
994
995	case OCS_EVT_DOMAIN_REQ_ATTACH: {
996		/* can happen during p2p */
997		uint32_t fc_id;
998
999		ocs_assert(arg, NULL);
1000		fc_id = *((uint32_t*)arg);
1001
1002		/* Assume that the domain is attached */
1003		ocs_assert(domain->attached, NULL);
1004
1005		/* Verify that the requested FC_ID is the same as the one we're working with */
1006		ocs_assert(domain->sport->fc_id == fc_id, NULL);
1007		break;
1008	}
1009
1010	default:
1011		__ocs_domain_common(__func__, ctx, evt, arg);
1012		return NULL;
1013	}
1014
1015	return NULL;
1016}
1017
1018/**
1019 * @ingroup domain_sm
1020 * @brief Domain state machine: Wait for nodes to free prior to the domain shutdown.
1021 *
1022 * <h3 class="desc">Description</h3>
1023 * All nodes are freed, and ready for a domain shutdown.
1024 *
1025 * @param ctx Remote node sm context.
1026 * @param evt Event to process.
1027 * @param arg Per event optional argument.
1028 *
1029 * @return Returns NULL.
1030 */
1031
1032void *
1033__ocs_domain_wait_sports_free(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
1034{
1035	std_domain_state_decl();
1036
1037	domain_sm_trace(domain);
1038
1039	switch(evt) {
1040	case OCS_EVT_ALL_CHILD_NODES_FREE: {
1041		int32_t rc;
1042
1043		/* sm: ocs_hw_domain_free */
1044		ocs_sm_transition(ctx, __ocs_domain_wait_shutdown, NULL);
1045
1046		/* Request ocs_hw_domain_free and wait for completion */
1047		rc = ocs_hw_domain_free(&ocs->hw, domain);
1048		if (rc) {
1049			ocs_log_err(ocs, "ocs_hw_domain_free() failed: %d\n", rc);
1050		}
1051		break;
1052	}
1053	default:
1054		__ocs_domain_common_shutdown(__func__, ctx, evt, arg);
1055		return NULL;
1056	}
1057
1058	return NULL;
1059}
1060
1061/**
1062 * @ingroup domain_sm
1063 * @brief Domain state machine: Complete the domain shutdown.
1064 *
1065 * <h3 class="desc">Description</h3>
1066 * Waits for a HW domain free to complete.
1067 *
1068 * @param ctx Remote node state machine context.
1069 * @param evt Event to process.
1070 * @param arg Per event optional argument.
1071 *
1072 * @return Returns NULL.
1073 */
1074
1075void *
1076__ocs_domain_wait_shutdown(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
1077{
1078	std_domain_state_decl();
1079
1080	domain_sm_trace(domain);
1081
1082	switch(evt) {
1083	case OCS_EVT_DOMAIN_FREE_OK: {
1084		if (ocs->enable_ini)
1085			ocs_scsi_ini_del_domain(domain);
1086		if (ocs->enable_tgt)
1087			ocs_scsi_tgt_del_domain(domain);
1088
1089		/* sm: domain_free */
1090		if (domain->domain_found_pending) {
1091			/* save fcf_wwn and drec from this domain, free current domain and allocate
1092			 * a new one with the same fcf_wwn
1093			 * TODO: could use a SLI-4 "re-register VPI" operation here
1094			 */
1095			uint64_t fcf_wwn = domain->fcf_wwn;
1096			ocs_domain_record_t drec = domain->pending_drec;
1097
1098			ocs_log_debug(ocs, "Reallocating domain\n");
1099			domain->req_domain_free = 1;
1100			domain = ocs_domain_alloc(ocs, fcf_wwn);
1101
1102			if (domain == NULL) {
1103				ocs_log_err(ocs, "ocs_domain_alloc() failed\n");
1104				/* TODO: hw/device reset needed */
1105				return NULL;
1106			}
1107			/*
1108			 * got a new domain; at this point, there are at least two domains
1109			 * once the req_domain_free flag is processed, the associated domain
1110			 * will be removed.
1111			 */
1112			ocs_sm_transition(&domain->drvsm, __ocs_domain_init, NULL);
1113			ocs_sm_post_event(&domain->drvsm, OCS_EVT_DOMAIN_FOUND, &drec);
1114		} else {
1115			domain->req_domain_free = 1;
1116		}
1117		break;
1118	}
1119
1120	default:
1121		__ocs_domain_common_shutdown(__func__, ctx, evt, arg);
1122		return NULL;
1123	}
1124
1125	return NULL;
1126}
1127
1128/**
1129 * @ingroup domain_sm
1130 * @brief Domain state machine: Wait for the domain alloc/attach completion
1131 * after receiving a domain lost.
1132 *
1133 * <h3 class="desc">Description</h3>
1134 * This state is entered when receiving a domain lost while waiting for a domain alloc
1135 * or a domain attach to complete.
1136 *
1137 * @param ctx Remote node state machine context.
1138 * @param evt Event to process.
1139 * @param arg Per event optional argument.
1140 *
1141 * @return Returns NULL.
1142 */
1143
1144void *
1145__ocs_domain_wait_domain_lost(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
1146{
1147	std_domain_state_decl();
1148
1149	domain_sm_trace(domain);
1150
1151	switch(evt) {
1152	case OCS_EVT_DOMAIN_ALLOC_OK:
1153	case OCS_EVT_DOMAIN_ATTACH_OK: {
1154		int32_t rc;
1155		ocs_domain_lock(domain);
1156		if (!ocs_list_empty(&domain->sport_list)) {
1157			/* if there are sports, transition to wait state and send
1158			* shutdown to each sport */
1159			ocs_sport_t	*sport = NULL;
1160			ocs_sport_t	*sport_next = NULL;
1161			ocs_sm_transition(ctx, __ocs_domain_wait_sports_free, NULL);
1162			ocs_list_foreach_safe(&domain->sport_list, sport, sport_next) {
1163				ocs_sm_post_event(&sport->sm, OCS_EVT_SHUTDOWN, NULL);
1164			}
1165			ocs_domain_unlock(domain);
1166		} else {
1167			ocs_domain_unlock(domain);
1168			/* no sports exist, free domain */
1169			ocs_sm_transition(ctx, __ocs_domain_wait_shutdown, NULL);
1170			rc = ocs_hw_domain_free(&ocs->hw, domain);
1171			if (rc) {
1172				ocs_log_err(ocs, "ocs_hw_domain_free() failed: %d\n", rc);
1173				/* TODO: hw/device reset needed */
1174			}
1175		}
1176		break;
1177	}
1178	case OCS_EVT_DOMAIN_ALLOC_FAIL:
1179	case OCS_EVT_DOMAIN_ATTACH_FAIL:
1180		ocs_log_err(ocs, "[domain] %-20s: failed\n", ocs_sm_event_name(evt));
1181		/* TODO: hw/device reset needed */
1182		break;
1183
1184	default:
1185		__ocs_domain_common_shutdown(__func__, ctx, evt, arg);
1186		return NULL;
1187	}
1188
1189	return NULL;
1190}
1191
1192/**
1193 * @brief Save the port's service parameters.
1194 *
1195 * <h3 class="desc">Description</h3>
1196 * Service parameters from the fabric FLOGI are saved in the domain's
1197 * flogi_service_params array.
1198 *
1199 * @param domain Pointer to the domain.
1200 * @param payload Service parameters to save.
1201 *
1202 * @return None.
1203 */
1204
1205void
1206ocs_domain_save_sparms(ocs_domain_t *domain, void *payload)
1207{
1208	ocs_memcpy(domain->flogi_service_params, payload, sizeof (fc_plogi_payload_t));
1209}
1210/**
1211 * @brief Initiator domain attach. (internal call only)
1212 *
1213 * Assumes that the domain SM lock is already locked
1214 *
1215 * <h3 class="desc">Description</h3>
1216 * The HW domain attach function is started.
1217 *
1218 * @param domain Pointer to the domain object.
1219 * @param s_id FC_ID of which to register this domain.
1220 *
1221 * @return None.
1222 */
1223
1224void
1225__ocs_domain_attach_internal(ocs_domain_t *domain, uint32_t s_id)
1226{
1227	ocs_memcpy(domain->dma.virt, ((uint8_t*)domain->flogi_service_params)+4, sizeof (fc_plogi_payload_t) - 4);
1228	(void)ocs_sm_post_event(&domain->drvsm, OCS_EVT_DOMAIN_REQ_ATTACH, &s_id);
1229}
1230
1231/**
1232 * @brief Initiator domain attach.
1233 *
1234 * <h3 class="desc">Description</h3>
1235 * The HW domain attach function is started.
1236 *
1237 * @param domain Pointer to the domain object.
1238 * @param s_id FC_ID of which to register this domain.
1239 *
1240 * @return None.
1241 */
1242
1243void
1244ocs_domain_attach(ocs_domain_t *domain, uint32_t s_id)
1245{
1246	__ocs_domain_attach_internal(domain, s_id);
1247}
1248
1249int
1250ocs_domain_post_event(ocs_domain_t *domain, ocs_sm_event_t event, void *arg)
1251{
1252	int rc;
1253	int accept_frames;
1254	int req_domain_free;
1255
1256	rc = ocs_sm_post_event(&domain->drvsm, event, arg);
1257
1258	req_domain_free = domain->req_domain_free;
1259	domain->req_domain_free = 0;
1260
1261	accept_frames = domain->req_accept_frames;
1262	domain->req_accept_frames = 0;
1263
1264	if (accept_frames) {
1265		ocs_domain_accept_frames(domain);
1266	}
1267
1268	if (req_domain_free) {
1269		ocs_domain_free(domain);
1270	}
1271
1272	return rc;
1273}
1274
1275/**
1276 * @brief Return the WWN as a uint64_t.
1277 *
1278 * <h3 class="desc">Description</h3>
1279 * Calls the HW property function for the WWNN or WWPN, and returns the value
1280 * as a uint64_t.
1281 *
1282 * @param hw Pointer to the HW object.
1283 * @param prop HW property.
1284 *
1285 * @return Returns uint64_t request value.
1286 */
1287
1288uint64_t
1289ocs_get_wwn(ocs_hw_t *hw, ocs_hw_property_e prop)
1290{
1291	uint8_t *p = ocs_hw_get_ptr(hw, prop);
1292	uint64_t value = 0;
1293
1294	if (p) {
1295		uint32_t i;
1296		for (i = 0; i < sizeof(value); i++) {
1297			value = (value << 8) | p[i];
1298		}
1299	}
1300	return value;
1301}
1302
1303/**
1304 * @brief Generate a domain ddump.
1305 *
1306 * <h3 class="desc">Description</h3>
1307 * Generates a domain ddump.
1308 *
1309 * @param textbuf Pointer to the text buffer.
1310 * @param domain Pointer to the domain context.
1311 *
1312 * @return Returns 0 on success, or a negative value on failure.
1313 */
1314
1315int
1316ocs_ddump_domain(ocs_textbuf_t *textbuf, ocs_domain_t *domain)
1317{
1318	ocs_sport_t *sport;
1319	int retval = 0;
1320
1321	ocs_ddump_section(textbuf, "domain", domain->instance_index);
1322	ocs_ddump_value(textbuf, "display_name", "%s", domain->display_name);
1323
1324	ocs_ddump_value(textbuf, "fcf", "%#x", domain->fcf);
1325	ocs_ddump_value(textbuf, "fcf_indicator", "%#x", domain->fcf_indicator);
1326	ocs_ddump_value(textbuf, "vlan_id", "%#x", domain->vlan_id);
1327	ocs_ddump_value(textbuf, "indicator", "%#x", domain->indicator);
1328	ocs_ddump_value(textbuf, "attached", "%d", domain->attached);
1329	ocs_ddump_value(textbuf, "is_loop", "%d", domain->is_loop);
1330	ocs_ddump_value(textbuf, "is_nlport", "%d", domain->is_nlport);
1331
1332	ocs_scsi_ini_ddump(textbuf, OCS_SCSI_DDUMP_DOMAIN, domain);
1333	ocs_scsi_tgt_ddump(textbuf, OCS_SCSI_DDUMP_DOMAIN, domain);
1334
1335	ocs_display_sparams(NULL, "domain_sparms", 1, textbuf, domain->dma.virt);
1336
1337	if (ocs_domain_lock_try(domain) != TRUE) {
1338		/* Didn't get the lock */
1339		return -1;
1340	}
1341		ocs_list_foreach(&domain->sport_list, sport) {
1342			retval = ocs_ddump_sport(textbuf, sport);
1343			if (retval != 0) {
1344				break;
1345			}
1346		}
1347
1348#if defined(ENABLE_FABRIC_EMULATION)
1349		ocs_ddump_ns(textbuf, domain->ocs_ns);
1350#endif
1351
1352	ocs_domain_unlock(domain);
1353
1354	ocs_ddump_endsection(textbuf, "domain", domain->instance_index);
1355
1356	return retval;
1357}
1358
1359void
1360ocs_mgmt_domain_list(ocs_textbuf_t *textbuf, void *object)
1361{
1362	ocs_sport_t *sport;
1363	ocs_domain_t *domain = (ocs_domain_t *)object;
1364
1365	ocs_mgmt_start_section(textbuf, "domain", domain->instance_index);
1366
1367	/* Add my status values to textbuf */
1368	ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "fcf");
1369	ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "fcf_indicator");
1370	ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "vlan_id");
1371	ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "indicator");
1372	ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "attached");
1373	ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "is_loop");
1374	ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "display_name");
1375	ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "num_sports");
1376#if defined(ENABLE_FABRIC_EMULATION)
1377	ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RW, "femul_enable");
1378#endif
1379
1380	if (ocs_domain_lock_try(domain) == TRUE) {
1381		/* If we get here, then we are holding the domain lock */
1382		ocs_list_foreach(&domain->sport_list, sport) {
1383			if ((sport->mgmt_functions) && (sport->mgmt_functions->get_list_handler)) {
1384				sport->mgmt_functions->get_list_handler(textbuf, sport);
1385			}
1386		}
1387		ocs_domain_unlock(domain);
1388	}
1389
1390	ocs_mgmt_end_section(textbuf, "domain", domain->instance_index);
1391}
1392
1393int
1394ocs_mgmt_domain_get(ocs_textbuf_t *textbuf, char *parent, char *name, void *object)
1395{
1396	ocs_sport_t *sport;
1397	ocs_domain_t *domain = (ocs_domain_t *)object;
1398	char qualifier[80];
1399	int retval = -1;
1400
1401	ocs_mgmt_start_section(textbuf, "domain", domain->instance_index);
1402
1403	snprintf(qualifier, sizeof(qualifier), "%s/domain[%d]", parent, domain->instance_index);
1404
1405	/* If it doesn't start with my qualifier I don't know what to do with it */
1406	if (ocs_strncmp(name, qualifier, strlen(qualifier)) == 0) {
1407		char *unqualified_name = name + strlen(qualifier) +1;
1408
1409		/* See if it's a value I can supply */
1410		if (ocs_strcmp(unqualified_name, "display_name") == 0) {
1411			ocs_mgmt_emit_string(textbuf, MGMT_MODE_RD, "display_name", domain->display_name);
1412			retval = 0;
1413		} else if (ocs_strcmp(unqualified_name, "fcf") == 0) {
1414			ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "fcf", "%#x", domain->fcf);
1415			retval = 0;
1416		} else if (ocs_strcmp(unqualified_name, "fcf_indicator") == 0) {
1417			ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "fcf_indicator", "%#x", domain->fcf_indicator);
1418			retval = 0;
1419		} else if (ocs_strcmp(unqualified_name, "vlan_id") == 0) {
1420			ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "vlan_id", "%#x", domain->vlan_id);
1421			retval = 0;
1422		} else if (ocs_strcmp(unqualified_name, "indicator") == 0) {
1423			ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "indicator", "%#x", domain->indicator);
1424			retval = 0;
1425		} else if (ocs_strcmp(unqualified_name, "attached") == 0) {
1426			ocs_mgmt_emit_boolean(textbuf, MGMT_MODE_RD, "attached", domain->attached);
1427			retval = 0;
1428		} else if (ocs_strcmp(unqualified_name, "is_loop") == 0) {
1429			ocs_mgmt_emit_boolean(textbuf, MGMT_MODE_RD, "is_loop",  domain->is_loop);
1430			retval = 0;
1431		} else if (ocs_strcmp(unqualified_name, "is_nlport") == 0) {
1432			ocs_mgmt_emit_boolean(textbuf, MGMT_MODE_RD, "is_nlport",  domain->is_nlport);
1433			retval = 0;
1434		} else if (ocs_strcmp(unqualified_name, "display_name") == 0) {
1435			ocs_mgmt_emit_string(textbuf, MGMT_MODE_RD, "display_name", domain->display_name);
1436			retval = 0;
1437#if defined(ENABLE_FABRIC_EMULATION)
1438		} else if (ocs_strcmp(unqualified_name, "femul_enable") == 0) {
1439			ocs_mgmt_emit_int(textbuf, MGMT_MODE_RW, "femul_enable", "%d", domain->femul_enable);
1440			retval = 0;
1441#endif
1442		} else if (ocs_strcmp(unqualified_name, "num_sports") == 0) {
1443			ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "num_sports", "%d", domain->sport_instance_count);
1444			retval = 0;
1445		} else {
1446			/* If I didn't know the value of this status pass the request to each of my children */
1447
1448			ocs_domain_lock(domain);
1449			ocs_list_foreach(&domain->sport_list, sport) {
1450				if ((sport->mgmt_functions) && (sport->mgmt_functions->get_handler)) {
1451					retval = sport->mgmt_functions->get_handler(textbuf, qualifier, name, sport);
1452				}
1453
1454				if (retval == 0) {
1455					break;
1456				}
1457			}
1458			ocs_domain_unlock(domain);
1459		}
1460	}
1461
1462	ocs_mgmt_end_section(textbuf, "domain", domain->instance_index);
1463	return retval;
1464}
1465
1466void
1467ocs_mgmt_domain_get_all(ocs_textbuf_t *textbuf, void *object)
1468{
1469	ocs_sport_t *sport;
1470	ocs_domain_t *domain = (ocs_domain_t *)object;
1471
1472	ocs_mgmt_start_section(textbuf, "domain", domain->instance_index);
1473
1474	ocs_mgmt_emit_string(textbuf, MGMT_MODE_RD, "display_name", domain->display_name);
1475	ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "fcf", "%#x", domain->fcf);
1476	ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "fcf_indicator", "%#x", domain->fcf_indicator);
1477	ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "vlan_id", "%#x", domain->vlan_id);
1478	ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "indicator", "%#x", domain->indicator);
1479	ocs_mgmt_emit_boolean(textbuf, MGMT_MODE_RD, "attached", domain->attached);
1480	ocs_mgmt_emit_boolean(textbuf, MGMT_MODE_RD, "is_loop",  domain->is_loop);
1481	ocs_mgmt_emit_boolean(textbuf, MGMT_MODE_RD, "is_nlport",  domain->is_nlport);
1482#if defined(ENABLE_FABRIC_EMULATION)
1483	ocs_mgmt_emit_int(textbuf, MGMT_MODE_RW, "femul_enable", "%d", domain->femul_enable);
1484#endif
1485	ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "num_sports",  "%d", domain->sport_instance_count);
1486
1487	ocs_domain_lock(domain);
1488	ocs_list_foreach(&domain->sport_list, sport) {
1489		if ((sport->mgmt_functions) && (sport->mgmt_functions->get_all_handler)) {
1490			sport->mgmt_functions->get_all_handler(textbuf, sport);
1491		}
1492	}
1493	ocs_domain_unlock(domain);
1494
1495	ocs_mgmt_end_unnumbered_section(textbuf, "domain");
1496
1497}
1498
1499int
1500ocs_mgmt_domain_set(char *parent, char *name, char *value, void *object)
1501{
1502	ocs_sport_t *sport;
1503	ocs_domain_t *domain = (ocs_domain_t *)object;
1504	char qualifier[80];
1505	int retval = -1;
1506
1507	snprintf(qualifier, sizeof(qualifier), "%s/domain[%d]", parent, domain->instance_index);
1508
1509	/* If it doesn't start with my qualifier I don't know what to do with it */
1510	if (ocs_strncmp(name, qualifier, strlen(qualifier)) == 0) {
1511		/* See if it's a value I can supply */
1512
1513		/* if (ocs_strcmp(unqualified_name, "display_name") == 0) {
1514		} else */
1515		{
1516			/* If I didn't know the value of this status pass the request to each of my children */
1517			ocs_domain_lock(domain);
1518			ocs_list_foreach(&domain->sport_list, sport) {
1519				if ((sport->mgmt_functions) && (sport->mgmt_functions->set_handler)) {
1520					retval = sport->mgmt_functions->set_handler(qualifier, name, value, sport);
1521				}
1522
1523				if (retval == 0) {
1524					break;
1525				}
1526			}
1527			ocs_domain_unlock(domain);
1528		}
1529	}
1530
1531	return retval;
1532}
1533
1534int
1535ocs_mgmt_domain_exec(char *parent, char *action, void *arg_in, uint32_t arg_in_length,
1536			void *arg_out, uint32_t arg_out_length, void *object)
1537{
1538	ocs_sport_t *sport;
1539	ocs_domain_t *domain = (ocs_domain_t *)object;
1540	char qualifier[80];
1541	int retval = -1;
1542
1543	snprintf(qualifier, sizeof(qualifier), "%s.domain%d", parent, domain->instance_index);
1544
1545	/* If it doesn't start with my qualifier I don't know what to do with it */
1546	if (ocs_strncmp(action, qualifier, strlen(qualifier)) == 0) {
1547		{
1548			/* If I didn't know how to do this action pass the request to each of my children */
1549			ocs_domain_lock(domain);
1550			ocs_list_foreach(&domain->sport_list, sport) {
1551				if ((sport->mgmt_functions) && (sport->mgmt_functions->exec_handler)) {
1552					retval = sport->mgmt_functions->exec_handler(qualifier, action, arg_in, arg_in_length, arg_out, arg_out_length, sport);
1553				}
1554
1555				if (retval == 0) {
1556					break;
1557				}
1558			}
1559			ocs_domain_unlock(domain);
1560		}
1561	}
1562
1563	return retval;
1564}
1565