xref: /freebsd/sys/dev/ocs_fc/ocs_xport.c (revision 61ba55bcf70f2340f9c943c9571113b3fd8eda69)
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  * FC transport API
35  *
36  */
37 
38 #include "ocs.h"
39 #include "ocs_device.h"
40 
41 static void ocs_xport_link_stats_cb(int32_t status, uint32_t num_counters, ocs_hw_link_stat_counts_t *counters, void *arg);
42 static void ocs_xport_host_stats_cb(int32_t status, uint32_t num_counters, ocs_hw_host_stat_counts_t *counters, void *arg);
43 /**
44  * @brief Post node event callback argument.
45  */
46 typedef struct {
47 	ocs_sem_t sem;
48 	ocs_node_t *node;
49 	ocs_sm_event_t evt;
50 	void *context;
51 } ocs_xport_post_node_event_t;
52 
53 /**
54  * @brief Allocate a transport object.
55  *
56  * @par Description
57  * A transport object is allocated, and associated with a device instance.
58  *
59  * @param ocs Pointer to device instance.
60  *
61  * @return Returns the pointer to the allocated transport object, or NULL if failed.
62  */
63 ocs_xport_t *
64 ocs_xport_alloc(ocs_t *ocs)
65 {
66 	ocs_xport_t *xport;
67 
68 	ocs_assert(ocs, NULL);
69 	xport = ocs_malloc(ocs, sizeof(*xport), OCS_M_ZERO);
70 	if (xport != NULL) {
71 		xport->ocs = ocs;
72 	}
73 	return xport;
74 }
75 
76 /**
77  * @brief Create the RQ threads and the circular buffers used to pass sequences.
78  *
79  * @par Description
80  * Creates the circular buffers and the servicing threads for RQ processing.
81  *
82  * @param xport Pointer to transport object
83  *
84  * @return Returns 0 on success, or a non-zero value on failure.
85  */
86 static void
87 ocs_xport_rq_threads_teardown(ocs_xport_t *xport)
88 {
89 	ocs_t *ocs = xport->ocs;
90 	uint32_t i;
91 
92 	if (xport->num_rq_threads == 0 ||
93 	    xport->rq_thread_info == NULL) {
94 		return;
95 	}
96 
97 	/* Abort any threads */
98 	for (i = 0; i < xport->num_rq_threads; i++) {
99 		if (xport->rq_thread_info[i].thread_started) {
100 			ocs_thread_terminate(&xport->rq_thread_info[i].thread);
101 			/* wait for the thread to exit */
102 			ocs_log_debug(ocs, "wait for thread %d to exit\n", i);
103 			while (xport->rq_thread_info[i].thread_started) {
104 				ocs_udelay(10000);
105 			}
106 			ocs_log_debug(ocs, "thread %d to exited\n", i);
107 		}
108 		if (xport->rq_thread_info[i].seq_cbuf != NULL) {
109 			ocs_cbuf_free(xport->rq_thread_info[i].seq_cbuf);
110 			xport->rq_thread_info[i].seq_cbuf = NULL;
111 		}
112 	}
113 }
114 
115 /**
116  * @brief Create the RQ threads and the circular buffers used to pass sequences.
117  *
118  * @par Description
119  * Creates the circular buffers and the servicing threads for RQ processing.
120  *
121  * @param xport Pointer to transport object.
122  * @param num_rq_threads Number of RQ processing threads that the
123  * driver creates.
124  *
125  * @return Returns 0 on success, or a non-zero value on failure.
126  */
127 static int32_t
128 ocs_xport_rq_threads_create(ocs_xport_t *xport, uint32_t num_rq_threads)
129 {
130 	ocs_t *ocs = xport->ocs;
131 	int32_t rc = 0;
132 	uint32_t i;
133 
134 	xport->num_rq_threads = num_rq_threads;
135 	ocs_log_debug(ocs, "number of RQ threads %d\n", num_rq_threads);
136 	if (num_rq_threads == 0) {
137 		return 0;
138 	}
139 
140 	/* Allocate the space for the thread objects */
141 	xport->rq_thread_info = ocs_malloc(ocs, sizeof(ocs_xport_rq_thread_info_t) * num_rq_threads, OCS_M_ZERO);
142 	if (xport->rq_thread_info == NULL) {
143 		ocs_log_err(ocs, "memory allocation failure\n");
144 		return -1;
145 	}
146 
147 	/* Create the circular buffers and threads. */
148 	for (i = 0; i < num_rq_threads; i++) {
149 		xport->rq_thread_info[i].ocs = ocs;
150 		xport->rq_thread_info[i].seq_cbuf = ocs_cbuf_alloc(ocs, OCS_HW_RQ_NUM_HDR);
151 		if (xport->rq_thread_info[i].seq_cbuf == NULL) {
152 			goto ocs_xport_rq_threads_create_error;
153 		}
154 
155 		ocs_snprintf(xport->rq_thread_info[i].thread_name,
156 			     sizeof(xport->rq_thread_info[i].thread_name),
157 			     "ocs_unsol_rq:%d:%d", ocs->instance_index, i);
158 		rc = ocs_thread_create(ocs, &xport->rq_thread_info[i].thread, ocs_unsol_rq_thread,
159 				       xport->rq_thread_info[i].thread_name,
160 				       &xport->rq_thread_info[i], OCS_THREAD_RUN);
161 		if (rc) {
162 			ocs_log_err(ocs, "ocs_thread_create failed: %d\n", rc);
163 			goto ocs_xport_rq_threads_create_error;
164 		}
165 		xport->rq_thread_info[i].thread_started = TRUE;
166 	}
167 	return 0;
168 
169 ocs_xport_rq_threads_create_error:
170 	ocs_xport_rq_threads_teardown(xport);
171 	return -1;
172 }
173 
174 /**
175  * @brief Do as much allocation as possible, but do not initialization the device.
176  *
177  * @par Description
178  * Performs the functions required to get a device ready to run.
179  *
180  * @param xport Pointer to transport object.
181  *
182  * @return Returns 0 on success, or a non-zero value on failure.
183  */
184 int32_t
185 ocs_xport_attach(ocs_xport_t *xport)
186 {
187 	ocs_t *ocs = xport->ocs;
188 	int32_t rc;
189 	uint32_t max_sgl;
190 	uint32_t n_sgl;
191 	uint32_t i;
192 	uint32_t value;
193 	uint32_t max_remote_nodes;
194 
195 	/* booleans used for cleanup if initialization fails */
196 	uint8_t io_pool_created = FALSE;
197 	uint8_t node_pool_created = FALSE;
198 
199 	ocs_list_init(&ocs->domain_list, ocs_domain_t, link);
200 
201 	for (i = 0; i < SLI4_MAX_FCFI; i++) {
202 		xport->fcfi[i].hold_frames = 1;
203 		ocs_lock_init(ocs, &xport->fcfi[i].pend_frames_lock, "xport pend_frames[%d]", i);
204 		ocs_list_init(&xport->fcfi[i].pend_frames, ocs_hw_sequence_t, link);
205 	}
206 
207 	rc = ocs_hw_set_ptr(&ocs->hw, OCS_HW_WAR_VERSION, ocs->hw_war_version);
208 	if (rc) {
209 		ocs_log_test(ocs, "can't set OCS_HW_WAR_VERSION\n");
210 		return -1;
211 	}
212 
213 	rc = ocs_hw_setup(&ocs->hw, ocs, SLI4_PORT_TYPE_FC);
214 	if (rc) {
215 		ocs_log_err(ocs, "%s: Can't setup hardware\n", ocs->desc);
216 		return -1;
217 	} else if (ocs->ctrlmask & OCS_CTRLMASK_CRASH_RESET) {
218 		ocs_log_debug(ocs, "stopping after ocs_hw_setup\n");
219 		return -1;
220 	}
221 
222 	ocs_hw_set(&ocs->hw, OCS_HW_BOUNCE, ocs->hw_bounce);
223 	ocs_log_debug(ocs, "HW bounce: %d\n", ocs->hw_bounce);
224 
225 	ocs_hw_set(&ocs->hw, OCS_HW_RQ_SELECTION_POLICY, ocs->rq_selection_policy);
226 	ocs_hw_set(&ocs->hw, OCS_HW_RR_QUANTA, ocs->rr_quanta);
227 	ocs_hw_get(&ocs->hw, OCS_HW_RQ_SELECTION_POLICY, &value);
228 	ocs_log_debug(ocs, "RQ Selection Policy: %d\n", value);
229 
230 	ocs_hw_set_ptr(&ocs->hw, OCS_HW_FILTER_DEF, (void*) ocs->filter_def);
231 
232 	ocs_hw_get(&ocs->hw, OCS_HW_MAX_SGL, &max_sgl);
233 	max_sgl -= SLI4_SGE_MAX_RESERVED;
234 	n_sgl = MIN(OCS_FC_MAX_SGL, max_sgl);
235 
236 	/* EVT: For chained SGL testing */
237 	if (ocs->ctrlmask & OCS_CTRLMASK_TEST_CHAINED_SGLS) {
238 		n_sgl = 4;
239 	}
240 
241 	/* Note: number of SGLs must be set for ocs_node_create_pool */
242 	if (ocs_hw_set(&ocs->hw, OCS_HW_N_SGL, n_sgl) != OCS_HW_RTN_SUCCESS) {
243 		ocs_log_err(ocs, "%s: Can't set number of SGLs\n", ocs->desc);
244 		return -1;
245 	} else {
246 		ocs_log_debug(ocs, "%s: Configured for %d SGLs\n", ocs->desc, n_sgl);
247 	}
248 
249 	ocs_hw_get(&ocs->hw, OCS_HW_MAX_NODES, &max_remote_nodes);
250 
251 	if (!ocs->max_remote_nodes)
252 		ocs->max_remote_nodes = max_remote_nodes;
253 
254 	rc = ocs_node_create_pool(ocs, ocs->max_remote_nodes);
255 	if (rc) {
256 		ocs_log_err(ocs, "Can't allocate node pool\n");
257 		goto ocs_xport_attach_cleanup;
258 	} else {
259 		node_pool_created = TRUE;
260 	}
261 
262 	/* EVT: if testing chained SGLs allocate OCS_FC_MAX_SGL SGE's in the IO */
263 	xport->io_pool = ocs_io_pool_create(ocs, ocs->num_scsi_ios,
264 		(ocs->ctrlmask & OCS_CTRLMASK_TEST_CHAINED_SGLS) ? OCS_FC_MAX_SGL : n_sgl);
265 	if (xport->io_pool == NULL) {
266 		ocs_log_err(ocs, "Can't allocate IO pool\n");
267 		goto ocs_xport_attach_cleanup;
268 	} else {
269 		io_pool_created = TRUE;
270 	}
271 
272 	/*
273 	 * setup the RQ processing threads
274 	 */
275 	if (ocs_xport_rq_threads_create(xport, ocs->rq_threads) != 0) {
276 		ocs_log_err(ocs, "failure creating RQ threads\n");
277 		goto ocs_xport_attach_cleanup;
278 	}
279 
280 	return 0;
281 
282 ocs_xport_attach_cleanup:
283 	if (io_pool_created) {
284 		ocs_io_pool_free(xport->io_pool);
285 	}
286 
287 	if (node_pool_created) {
288 		ocs_node_free_pool(ocs);
289 	}
290 
291 	return -1;
292 }
293 
294 /**
295  * @brief Determines how to setup auto Xfer ready.
296  *
297  * @par Description
298  * @param xport Pointer to transport object.
299  *
300  * @return Returns 0 on success or a non-zero value on failure.
301  */
302 static int32_t
303 ocs_xport_initialize_auto_xfer_ready(ocs_xport_t *xport)
304 {
305 	ocs_t *ocs = xport->ocs;
306 	uint32_t auto_xfer_rdy;
307 	char prop_buf[32];
308 	uint32_t ramdisc_blocksize = 512;
309 	uint8_t p_type = 0;
310 
311 	ocs_hw_get(&ocs->hw, OCS_HW_AUTO_XFER_RDY_CAPABLE, &auto_xfer_rdy);
312 	if (!auto_xfer_rdy) {
313 		ocs->auto_xfer_rdy_size = 0;
314 		ocs_log_test(ocs, "Cannot enable auto xfer rdy for this port\n");
315 		return 0;
316 	}
317 
318 	if (ocs_hw_set(&ocs->hw, OCS_HW_AUTO_XFER_RDY_SIZE, ocs->auto_xfer_rdy_size)) {
319 		ocs_log_test(ocs, "%s: Can't set auto xfer rdy mode\n", ocs->desc);
320 		return -1;
321 	}
322 
323 	/*
324 	 * Determine if we are doing protection in the backend. We are looking
325 	 * at the modules parameters here. The backend cannot allow a format
326 	 * command to change the protection mode when using this feature,
327 	 * otherwise the firmware will not do the proper thing.
328 	 */
329 	if (ocs_get_property("p_type", prop_buf, sizeof(prop_buf)) == 0) {
330 		p_type = ocs_strtoul(prop_buf, 0, 0);
331 	}
332 	if (ocs_get_property("ramdisc_blocksize", prop_buf, sizeof(prop_buf)) == 0) {
333 		ramdisc_blocksize = ocs_strtoul(prop_buf, 0, 0);
334 	}
335 	if (ocs_get_property("external_dif", prop_buf, sizeof(prop_buf)) == 0) {
336 		if(ocs_strlen(prop_buf)) {
337 			if (p_type == 0) {
338 				p_type = 1;
339 			}
340 		}
341 	}
342 
343 	if (p_type != 0) {
344 		if (ocs_hw_set(&ocs->hw, OCS_HW_AUTO_XFER_RDY_T10_ENABLE, TRUE)) {
345 			ocs_log_test(ocs, "%s: Can't set auto xfer rdy mode\n", ocs->desc);
346 			return -1;
347 		}
348 		if (ocs_hw_set(&ocs->hw, OCS_HW_AUTO_XFER_RDY_BLK_SIZE, ramdisc_blocksize)) {
349 			ocs_log_test(ocs, "%s: Can't set auto xfer rdy blk size\n", ocs->desc);
350 			return -1;
351 		}
352 		if (ocs_hw_set(&ocs->hw, OCS_HW_AUTO_XFER_RDY_P_TYPE, p_type)) {
353 			ocs_log_test(ocs, "%s: Can't set auto xfer rdy mode\n", ocs->desc);
354 			return -1;
355 		}
356 		if (ocs_hw_set(&ocs->hw, OCS_HW_AUTO_XFER_RDY_REF_TAG_IS_LBA, TRUE)) {
357 			ocs_log_test(ocs, "%s: Can't set auto xfer rdy ref tag\n", ocs->desc);
358 			return -1;
359 		}
360 		if (ocs_hw_set(&ocs->hw, OCS_HW_AUTO_XFER_RDY_APP_TAG_VALID, FALSE)) {
361 			ocs_log_test(ocs, "%s: Can't set auto xfer rdy app tag valid\n", ocs->desc);
362 			return -1;
363 		}
364 	}
365 	ocs_log_debug(ocs, "Auto xfer rdy is enabled, p_type=%d, blksize=%d\n",
366 		p_type, ramdisc_blocksize);
367 	return 0;
368 }
369 
370 /**
371  * @brief Initialize link topology config
372  *
373  * @par Description
374  * Topology can be fetched from mod-param or Persistet Topology(PT).
375  *  a. Mod-param value is used when the value is 1(P2P) or 2(LOOP).
376  *  a. PT is used if mod-param is not provided( i.e, default value of AUTO)
377  * Also, if mod-param is used, update PT.
378  *
379  * @param ocs Pointer to ocs
380  *
381  * @return Returns 0 on success, or a non-zero value on failure.
382  */
383 static int
384 ocs_topology_setup(ocs_t *ocs)
385 {
386         uint32_t topology;
387 
388         if (ocs->topology == OCS_HW_TOPOLOGY_AUTO) {
389                 topology = ocs_hw_get_config_persistent_topology(&ocs->hw);
390         }  else {
391                 topology = ocs->topology;
392                 /* ignore failure here. link will come-up either in auto mode
393                  * if PT is not supported or last saved PT value */
394                 ocs_hw_set_persistent_topology(&ocs->hw, topology, OCS_CMD_POLL);
395         }
396 
397         return ocs_hw_set(&ocs->hw, OCS_HW_TOPOLOGY, topology);
398 }
399 
400 /**
401  * @brief Initializes the device.
402  *
403  * @par Description
404  * Performs the functions required to make a device functional.
405  *
406  * @param xport Pointer to transport object.
407  *
408  * @return Returns 0 on success, or a non-zero value on failure.
409  */
410 int32_t
411 ocs_xport_initialize(ocs_xport_t *xport)
412 {
413 	ocs_t *ocs = xport->ocs;
414 	int32_t rc;
415 	uint32_t i;
416 	uint32_t max_hw_io;
417 	uint32_t max_sgl;
418 	uint32_t hlm;
419 	uint32_t rq_limit;
420 	uint32_t dif_capable;
421 	uint8_t dif_separate = 0;
422 	char prop_buf[32];
423 
424 	/* booleans used for cleanup if initialization fails */
425 	uint8_t ini_device_set = FALSE;
426 	uint8_t tgt_device_set = FALSE;
427 	uint8_t hw_initialized = FALSE;
428 
429 	ocs_hw_get(&ocs->hw, OCS_HW_MAX_IO, &max_hw_io);
430 	if (ocs_hw_set(&ocs->hw, OCS_HW_N_IO, max_hw_io) != OCS_HW_RTN_SUCCESS) {
431 		ocs_log_err(ocs, "%s: Can't set number of IOs\n", ocs->desc);
432 		return -1;
433 	}
434 
435 	ocs_hw_get(&ocs->hw, OCS_HW_MAX_SGL, &max_sgl);
436 	max_sgl -= SLI4_SGE_MAX_RESERVED;
437 
438 	if (ocs->enable_hlm) {
439 		ocs_hw_get(&ocs->hw, OCS_HW_HIGH_LOGIN_MODE, &hlm);
440 		if (!hlm) {
441 			ocs->enable_hlm = FALSE;
442 			ocs_log_err(ocs, "Cannot enable high login mode for this port\n");
443 		} else {
444                         ocs_log_debug(ocs, "High login mode is enabled\n");
445 			if (ocs_hw_set(&ocs->hw, OCS_HW_HIGH_LOGIN_MODE, TRUE)) {
446 				ocs_log_err(ocs, "%s: Can't set high login mode\n", ocs->desc);
447 				return -1;
448 			}
449 		}
450 	}
451 
452 	/* validate the auto xfer_rdy size */
453 	if (ocs->auto_xfer_rdy_size > 0 &&
454 	    (ocs->auto_xfer_rdy_size < 2048 ||
455 	     ocs->auto_xfer_rdy_size > 65536)) {
456 		ocs_log_err(ocs, "Auto XFER_RDY size is out of range (2K-64K)\n");
457 		return -1;
458 	}
459 
460 	ocs_hw_get(&ocs->hw, OCS_HW_MAX_IO, &max_hw_io);
461 
462 	if (ocs->auto_xfer_rdy_size > 0) {
463 		if (ocs_xport_initialize_auto_xfer_ready(xport)) {
464 			ocs_log_err(ocs, "%s: Failed auto xfer ready setup\n", ocs->desc);
465 			return -1;
466 		}
467 		if (ocs->esoc){
468 			ocs_hw_set(&ocs->hw, OCS_ESOC, TRUE);
469 		}
470 	}
471 
472 	if (ocs->explicit_buffer_list) {
473 		/* Are pre-registered SGL's required? */
474 		ocs_hw_get(&ocs->hw, OCS_HW_PREREGISTER_SGL, &i);
475 		if (i == TRUE) {
476 			ocs_log_err(ocs, "Explicit Buffer List not supported on this device, not enabled\n");
477 		} else {
478 			ocs_hw_set(&ocs->hw, OCS_HW_PREREGISTER_SGL, FALSE);
479 		}
480 	}
481 
482 	 /* Setup persistent topology based on topology mod-param value */
483         rc = ocs_topology_setup(ocs);
484         if (rc) {
485                 ocs_log_err(ocs, "%s: Can't set the toplogy\n", ocs->desc);
486                 return -1;
487         }
488 
489 	if (ocs_hw_set(&ocs->hw, OCS_HW_TOPOLOGY, ocs->topology) != OCS_HW_RTN_SUCCESS) {
490 		ocs_log_err(ocs, "%s: Can't set the toplogy\n", ocs->desc);
491 		return -1;
492 	}
493 	ocs_hw_set(&ocs->hw, OCS_HW_RQ_DEFAULT_BUFFER_SIZE, OCS_FC_RQ_SIZE_DEFAULT);
494 
495 	if (ocs_hw_set(&ocs->hw, OCS_HW_LINK_SPEED, ocs->speed) != OCS_HW_RTN_SUCCESS) {
496 		ocs_log_err(ocs, "%s: Can't set the link speed\n", ocs->desc);
497 		return -1;
498 	}
499 
500 	if (ocs_hw_set(&ocs->hw, OCS_HW_ETH_LICENSE, ocs->ethernet_license) != OCS_HW_RTN_SUCCESS) {
501 		ocs_log_err(ocs, "%s: Can't set the ethernet license\n", ocs->desc);
502 		return -1;
503 	}
504 
505 	/* currently only lancer support setting the CRC seed value */
506 	if (ocs->hw.sli.asic_type == SLI4_ASIC_TYPE_LANCER) {
507 		if (ocs_hw_set(&ocs->hw, OCS_HW_DIF_SEED, OCS_FC_DIF_SEED) != OCS_HW_RTN_SUCCESS) {
508 			ocs_log_err(ocs, "%s: Can't set the DIF seed\n", ocs->desc);
509 			return -1;
510 		}
511 	}
512 
513 	/* Set the Dif mode */
514 	if (0 == ocs_hw_get(&ocs->hw, OCS_HW_DIF_CAPABLE, &dif_capable)) {
515 		if (dif_capable) {
516 			if (ocs_get_property("dif_separate", prop_buf, sizeof(prop_buf)) == 0) {
517 				dif_separate = ocs_strtoul(prop_buf, 0, 0);
518 			}
519 
520 			if ((rc = ocs_hw_set(&ocs->hw, OCS_HW_DIF_MODE,
521 			      (dif_separate == 0 ? OCS_HW_DIF_MODE_INLINE : OCS_HW_DIF_MODE_SEPARATE)))) {
522 				ocs_log_err(ocs, "Requested DIF MODE not supported\n");
523 			}
524 		}
525 	}
526 
527 	if (ocs->target_io_timer_sec) {
528 		ocs_log_debug(ocs, "setting target io timer=%d\n", ocs->target_io_timer_sec);
529 		ocs_hw_set(&ocs->hw, OCS_HW_EMULATE_TARGET_WQE_TIMEOUT, TRUE);
530 	}
531 
532 	ocs_hw_callback(&ocs->hw, OCS_HW_CB_DOMAIN, ocs_domain_cb, ocs);
533 	ocs_hw_callback(&ocs->hw, OCS_HW_CB_REMOTE_NODE, ocs_remote_node_cb, ocs);
534 	ocs_hw_callback(&ocs->hw, OCS_HW_CB_UNSOLICITED, ocs_unsolicited_cb, ocs);
535 	ocs_hw_callback(&ocs->hw, OCS_HW_CB_PORT, ocs_port_cb, ocs);
536 
537 	ocs->fw_version = (const char*) ocs_hw_get_ptr(&ocs->hw, OCS_HW_FW_REV);
538 
539 	/* Initialize vport list */
540 	ocs_list_init(&xport->vport_list, ocs_vport_spec_t, link);
541 	ocs_lock_init(ocs, &xport->io_pending_lock, "io_pending_lock[%d]", ocs->instance_index);
542 	ocs_list_init(&xport->io_pending_list, ocs_io_t, io_pending_link);
543 	ocs_atomic_init(&xport->io_active_count, 0);
544 	ocs_atomic_init(&xport->io_pending_count, 0);
545 	ocs_atomic_init(&xport->io_total_free, 0);
546 	ocs_atomic_init(&xport->io_total_pending, 0);
547 	ocs_atomic_init(&xport->io_alloc_failed_count, 0);
548 	ocs_atomic_init(&xport->io_pending_recursing, 0);
549 	ocs_lock_init(ocs, &ocs->hw.watchdog_lock, " Watchdog Lock[%d]", ocs_instance(ocs));
550 	rc = ocs_hw_init(&ocs->hw);
551 	if (rc) {
552 		ocs_log_err(ocs, "ocs_hw_init failure\n");
553 		goto ocs_xport_init_cleanup;
554 	} else {
555 		hw_initialized = TRUE;
556 	}
557 
558 	rq_limit = max_hw_io/2;
559 	if (ocs_hw_set(&ocs->hw, OCS_HW_RQ_PROCESS_LIMIT, rq_limit) != OCS_HW_RTN_SUCCESS) {
560 		ocs_log_err(ocs, "%s: Can't set the RQ process limit\n", ocs->desc);
561 	}
562 
563 	if (ocs->config_tgt) {
564 		rc = ocs_scsi_tgt_new_device(ocs);
565 		if (rc) {
566 			ocs_log_err(ocs, "failed to initialize target\n");
567 			goto ocs_xport_init_cleanup;
568 		} else {
569 			tgt_device_set = TRUE;
570 		}
571 	}
572 
573 	if (ocs->enable_ini) {
574 		rc = ocs_scsi_ini_new_device(ocs);
575 		if (rc) {
576 			ocs_log_err(ocs, "failed to initialize initiator\n");
577 			goto ocs_xport_init_cleanup;
578 		} else {
579 			ini_device_set = TRUE;
580 		}
581 	}
582 
583 	/* Add vports */
584 	if (ocs->num_vports != 0) {
585 		uint32_t max_vports;
586 		ocs_hw_get(&ocs->hw, OCS_HW_MAX_VPORTS, &max_vports);
587 
588 		if (ocs->num_vports < max_vports) {
589 			ocs_log_debug(ocs, "Provisioning %d vports\n", ocs->num_vports);
590 			for (i = 0; i < ocs->num_vports; i++) {
591 				ocs_vport_create_spec(ocs, 0, 0, UINT32_MAX, ocs->enable_ini, ocs->enable_tgt, NULL, NULL);
592 			}
593 		} else {
594 			ocs_log_err(ocs, "failed to create vports. num_vports range should be (1-%d) \n", max_vports-1);
595 			goto ocs_xport_init_cleanup;
596 		}
597 	}
598 
599 	return 0;
600 
601 ocs_xport_init_cleanup:
602 	if (ini_device_set) {
603 		ocs_scsi_ini_del_device(ocs);
604 	}
605 
606 	if (tgt_device_set) {
607 		ocs_scsi_tgt_del_device(ocs);
608 	}
609 
610 	if (hw_initialized) {
611 		/* ocs_hw_teardown can only execute after ocs_hw_init */
612 		ocs_hw_teardown(&ocs->hw);
613 	}
614 
615 	return -1;
616 }
617 
618 /**
619  * @brief Detaches the transport from the device.
620  *
621  * @par Description
622  * Performs the functions required to shut down a device.
623  *
624  * @param xport Pointer to transport object.
625  *
626  * @return Returns 0 on success or a non-zero value on failure.
627  */
628 int32_t
629 ocs_xport_detach(ocs_xport_t *xport)
630 {
631 	ocs_t *ocs = xport->ocs;
632 
633 	/* free resources associated with target-server and initiator-client */
634 	if (ocs->config_tgt)
635 		ocs_scsi_tgt_del_device(ocs);
636 
637 	if (ocs->enable_ini) {
638 		ocs_scsi_ini_del_device(ocs);
639 
640 		/*Shutdown FC Statistics timer*/
641 		if (ocs_timer_pending(&ocs->xport->stats_timer))
642 			ocs_del_timer(&ocs->xport->stats_timer);
643 	}
644 
645 	ocs_hw_teardown(&ocs->hw);
646 
647 	return 0;
648 }
649 
650 /**
651  * @brief domain list empty callback
652  *
653  * @par Description
654  * Function is invoked when the device domain list goes empty. By convention
655  * @c arg points to an ocs_sem_t instance, that is incremented.
656  *
657  * @param ocs Pointer to device object.
658  * @param arg Pointer to semaphore instance.
659  *
660  * @return None.
661  */
662 
663 static void
664 ocs_xport_domain_list_empty_cb(ocs_t *ocs, void *arg)
665 {
666 	ocs_sem_t *sem = arg;
667 
668 	ocs_assert(ocs);
669 	ocs_assert(sem);
670 
671 	ocs_sem_v(sem);
672 }
673 
674 /**
675  * @brief post node event callback
676  *
677  * @par Description
678  * This function is called from the mailbox completion interrupt context to post an
679  * event to a node object. By doing this in the interrupt context, it has
680  * the benefit of only posting events in the interrupt context, deferring the need to
681  * create a per event node lock.
682  *
683  * @param hw Pointer to HW structure.
684  * @param status Completion status for mailbox command.
685  * @param mqe Mailbox queue completion entry.
686  * @param arg Callback argument.
687  *
688  * @return Returns 0 on success, a negative error code value on failure.
689  */
690 
691 static int32_t
692 ocs_xport_post_node_event_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
693 {
694 	ocs_xport_post_node_event_t *payload = arg;
695 
696 	if (payload != NULL) {
697 		ocs_node_post_event(payload->node, payload->evt, payload->context);
698 		ocs_sem_v(&payload->sem);
699 	}
700 
701         return 0;
702 }
703 
704 /**
705  * @brief Initiate force free.
706  *
707  * @par Description
708  * Perform force free of OCS.
709  *
710  * @param xport Pointer to transport object.
711  *
712  * @return None.
713  */
714 
715 static void
716 ocs_xport_force_free(ocs_xport_t *xport)
717 {
718 	ocs_t *ocs = xport->ocs;
719 	ocs_domain_t *domain;
720 	ocs_domain_t *next;
721 
722 	ocs_log_debug(ocs, "reset required, do force shutdown\n");
723 	ocs_device_lock(ocs);
724 		ocs_list_foreach_safe(&ocs->domain_list, domain, next) {
725 			ocs_domain_force_free(domain);
726 		}
727 	ocs_device_unlock(ocs);
728 }
729 
730 /**
731  * @brief Perform transport attach function.
732  *
733  * @par Description
734  * Perform the attach function, which for the FC transport makes a HW call
735  * to bring up the link.
736  *
737  * @param xport pointer to transport object.
738  * @param cmd command to execute.
739  *
740  * ocs_xport_control(ocs_xport_t *xport, OCS_XPORT_PORT_ONLINE)
741  * ocs_xport_control(ocs_xport_t *xport, OCS_XPORT_PORT_OFFLINE)
742  * ocs_xport_control(ocs_xport_t *xport, OCS_XPORT_PORT_SHUTDOWN)
743  * ocs_xport_control(ocs_xport_t *xport, OCS_XPORT_POST_NODE_EVENT, ocs_node_t *node, ocs_sm_event_t, void *context)
744  *
745  * @return Returns 0 on success, or a negative error code value on failure.
746  */
747 
748 int32_t
749 ocs_xport_control(ocs_xport_t *xport, ocs_xport_ctrl_e cmd, ...)
750 {
751 	uint32_t rc = 0;
752 	ocs_t *ocs = NULL;
753 	va_list argp;
754 
755 	ocs_assert(xport, -1);
756 	ocs_assert(xport->ocs, -1);
757 	ocs = xport->ocs;
758 
759 	switch (cmd) {
760 	case OCS_XPORT_PORT_ONLINE: {
761 		/* Bring the port on-line */
762 		rc = ocs_hw_port_control(&ocs->hw, OCS_HW_PORT_INIT, 0, NULL, NULL);
763 		if (rc) {
764 			ocs_log_err(ocs, "%s: Can't init port\n", ocs->desc);
765 		} else {
766 			xport->configured_link_state = cmd;
767 		}
768 		break;
769 	}
770 	case OCS_XPORT_PORT_OFFLINE: {
771 		if (ocs_hw_port_control(&ocs->hw, OCS_HW_PORT_SHUTDOWN, 0, NULL, NULL)) {
772 			ocs_log_err(ocs, "port shutdown failed\n");
773 		} else {
774 			xport->configured_link_state = cmd;
775 		}
776 		break;
777 	}
778 
779 	case OCS_XPORT_SHUTDOWN: {
780 		ocs_sem_t sem;
781 		uint32_t reset_required;
782 
783 		/* if a PHYSDEV reset was performed (e.g. hw dump), will affect
784 		 * all PCI functions; orderly shutdown won't work, just force free
785 		 */
786 		/* TODO: need to poll this regularly... */
787 		if (ocs_hw_get(&ocs->hw, OCS_HW_RESET_REQUIRED, &reset_required) != OCS_HW_RTN_SUCCESS) {
788 			reset_required = 0;
789 		}
790 
791 		if (reset_required) {
792 			ocs_log_debug(ocs, "reset required, do force shutdown\n");
793 			ocs_xport_force_free(xport);
794 			break;
795 		}
796 		ocs_sem_init(&sem, 0, "domain_list_sem");
797 		ocs_register_domain_list_empty_cb(ocs, ocs_xport_domain_list_empty_cb, &sem);
798 
799 		if (ocs_hw_port_control(&ocs->hw, OCS_HW_PORT_SHUTDOWN, 0, NULL, NULL)) {
800 			ocs_log_debug(ocs, "port shutdown failed, do force shutdown\n");
801 			ocs_xport_force_free(xport);
802 		} else {
803 			ocs_log_debug(ocs, "Waiting %d seconds for domain shutdown.\n", (OCS_FC_DOMAIN_SHUTDOWN_TIMEOUT_USEC/1000000));
804 
805 			rc = ocs_sem_p(&sem, OCS_FC_DOMAIN_SHUTDOWN_TIMEOUT_USEC);
806 			if (rc) {
807 				ocs_log_debug(ocs, "Note: Domain shutdown timed out\n");
808 				ocs_xport_force_free(xport);
809 			}
810 		}
811 
812 		ocs_register_domain_list_empty_cb(ocs, NULL, NULL);
813 
814 		/* Free up any saved virtual ports */
815 		ocs_vport_del_all(ocs);
816 		break;
817 	}
818 
819 	/*
820 	 * POST_NODE_EVENT:  post an event to a node object
821 	 *
822 	 * This transport function is used to post an event to a node object. It does
823 	 * this by submitting a NOP mailbox command to defer execution to the
824 	 * interrupt context (thereby enforcing the serialized execution of event posting
825 	 * to the node state machine instances)
826 	 *
827 	 * A counting semaphore is used to make the call synchronous (we wait until
828 	 * the callback increments the semaphore before returning (or times out)
829 	 */
830 	case OCS_XPORT_POST_NODE_EVENT: {
831 		ocs_node_t *node;
832 		ocs_sm_event_t evt;
833 		void *context;
834 		ocs_xport_post_node_event_t payload;
835 		ocs_t *ocs;
836 		ocs_hw_t *hw;
837 
838 		/* Retrieve arguments */
839 		va_start(argp, cmd);
840 		node = va_arg(argp, ocs_node_t*);
841 		evt = va_arg(argp, ocs_sm_event_t);
842 		context = va_arg(argp, void *);
843 		va_end(argp);
844 
845 		ocs_assert(node, -1);
846 		ocs_assert(node->ocs, -1);
847 
848 		ocs = node->ocs;
849 		hw = &ocs->hw;
850 
851 		/* if node's state machine is disabled, don't bother continuing */
852 		if (!node->sm.current_state) {
853 			ocs_log_test(ocs, "node %p state machine disabled\n", node);
854 			return -1;
855 		}
856 
857 		/* Setup payload */
858 		ocs_memset(&payload, 0, sizeof(payload));
859 		ocs_sem_init(&payload.sem, 0, "xport_post_node_Event");
860 		payload.node = node;
861 		payload.evt = evt;
862 		payload.context = context;
863 
864 		if (ocs_hw_async_call(hw, ocs_xport_post_node_event_cb, &payload)) {
865 			ocs_log_test(ocs, "ocs_hw_async_call failed\n");
866 			rc = -1;
867 			break;
868 		}
869 
870 		/* Wait for completion */
871 		if (ocs_sem_p(&payload.sem, OCS_SEM_FOREVER)) {
872 			ocs_log_test(ocs, "POST_NODE_EVENT: sem wait failed\n");
873 			rc = -1;
874 		}
875 
876 		break;
877 	}
878 	/*
879 	 * Set wwnn for the port.  This will be used instead of the default provided by FW.
880 	 */
881 	case OCS_XPORT_WWNN_SET: {
882 		uint64_t wwnn;
883 
884 		/* Retrieve arguments */
885 		va_start(argp, cmd);
886 		wwnn = va_arg(argp, uint64_t);
887 		va_end(argp);
888 
889 		ocs_log_debug(ocs, " WWNN %016" PRIx64 "\n", wwnn);
890 		xport->req_wwnn = wwnn;
891 
892 		break;
893 	}
894 	/*
895 	 * Set wwpn for the port.  This will be used instead of the default provided by FW.
896 	 */
897 	case OCS_XPORT_WWPN_SET: {
898 		uint64_t wwpn;
899 
900 		/* Retrieve arguments */
901 		va_start(argp, cmd);
902 		wwpn = va_arg(argp, uint64_t);
903 		va_end(argp);
904 
905 		ocs_log_debug(ocs, " WWPN %016" PRIx64 "\n", wwpn);
906 		xport->req_wwpn = wwpn;
907 
908 		break;
909 	}
910 
911 	default:
912 		break;
913 	}
914 	return rc;
915 }
916 
917 /**
918  * @brief Return status on a link.
919  *
920  * @par Description
921  * Returns status information about a link.
922  *
923  * @param xport Pointer to transport object.
924  * @param cmd Command to execute.
925  * @param result Pointer to result value.
926  *
927  * ocs_xport_status(ocs_xport_t *xport, OCS_XPORT_PORT_STATUS)
928  * ocs_xport_status(ocs_xport_t *xport, OCS_XPORT_LINK_SPEED, ocs_xport_stats_t *result)
929  *	return link speed in MB/sec
930  * ocs_xport_status(ocs_xport_t *xport, OCS_XPORT_IS_SUPPORTED_LINK_SPEED, ocs_xport_stats_t *result)
931  *	[in] *result is speed to check in MB/s
932  *	returns 1 if supported, 0 if not
933  * ocs_xport_status(ocs_xport_t *xport, OCS_XPORT_LINK_STATISTICS, ocs_xport_stats_t *result)
934  *	return link/host port stats
935  * ocs_xport_status(ocs_xport_t *xport, OCS_XPORT_LINK_STAT_RESET, ocs_xport_stats_t *result)
936  *	resets link/host stats
937  *
938  *
939  * @return Returns 0 on success, or a negative error code value on failure.
940  */
941 
942 int32_t
943 ocs_xport_status(ocs_xport_t *xport, ocs_xport_status_e cmd, ocs_xport_stats_t *result)
944 {
945 	uint32_t rc = 0;
946 	ocs_t *ocs = NULL;
947 	ocs_xport_stats_t value;
948 	ocs_hw_rtn_e hw_rc;
949 
950 	ocs_assert(xport, -1);
951 	ocs_assert(xport->ocs, -1);
952 
953 	ocs = xport->ocs;
954 
955 	switch (cmd) {
956 	case OCS_XPORT_CONFIG_PORT_STATUS:
957 		ocs_assert(result, -1);
958 		if (xport->configured_link_state == 0) {
959 			/* Initial state is offline. configured_link_state is    */
960 			/* set to online explicitly when port is brought online. */
961 			xport->configured_link_state = OCS_XPORT_PORT_OFFLINE;
962 		}
963 		result->value = xport->configured_link_state;
964 		break;
965 
966 	case OCS_XPORT_PORT_STATUS:
967 		ocs_assert(result, -1);
968 		/* Determine port status based on link speed. */
969 		hw_rc = ocs_hw_get(&(ocs->hw), OCS_HW_LINK_SPEED, &value.value);
970 		if (hw_rc == OCS_HW_RTN_SUCCESS) {
971 			if (value.value == 0) {
972 				result->value = 0;
973 			} else {
974 				result->value = 1;
975 			}
976 			rc = 0;
977 		} else {
978 			rc = -1;
979 		}
980 		break;
981 
982 	case OCS_XPORT_LINK_SPEED: {
983 		uint32_t speed;
984 
985 		ocs_assert(result, -1);
986 		result->value = 0;
987 
988 		rc = ocs_hw_get(&ocs->hw, OCS_HW_LINK_SPEED, &speed);
989 		if (rc == 0) {
990 			result->value = speed;
991 		}
992 		break;
993 	}
994 
995 	case OCS_XPORT_IS_SUPPORTED_LINK_SPEED: {
996 		uint32_t speed;
997 		uint32_t link_module_type;
998 
999 		ocs_assert(result, -1);
1000 		speed = result->value;
1001 
1002 		rc = ocs_hw_get(&ocs->hw, OCS_HW_LINK_MODULE_TYPE, &link_module_type);
1003 		if (rc == 0) {
1004 			switch(speed) {
1005 			case 1000:	rc = (link_module_type & OCS_HW_LINK_MODULE_TYPE_1GB) != 0; break;
1006 			case 2000:	rc = (link_module_type & OCS_HW_LINK_MODULE_TYPE_2GB) != 0; break;
1007 			case 4000:	rc = (link_module_type & OCS_HW_LINK_MODULE_TYPE_4GB) != 0; break;
1008 			case 8000:	rc = (link_module_type & OCS_HW_LINK_MODULE_TYPE_8GB) != 0; break;
1009 			case 10000:	rc = (link_module_type & OCS_HW_LINK_MODULE_TYPE_10GB) != 0; break;
1010 			case 16000:	rc = (link_module_type & OCS_HW_LINK_MODULE_TYPE_16GB) != 0; break;
1011 			case 32000:	rc = (link_module_type & OCS_HW_LINK_MODULE_TYPE_32GB) != 0; break;
1012 			default:	rc = 0; break;
1013 			}
1014 		} else {
1015 			rc = 0;
1016 		}
1017 		break;
1018 	}
1019 	case OCS_XPORT_LINK_STATISTICS:
1020 		ocs_device_lock(ocs);
1021 			ocs_memcpy((void *)result, &ocs->xport->fc_xport_stats, sizeof(ocs_xport_stats_t));
1022 		ocs_device_unlock(ocs);
1023 		break;
1024 	case OCS_XPORT_LINK_STAT_RESET: {
1025 		/* Create a semaphore to synchronize the stat reset process. */
1026 		ocs_sem_init(&(result->stats.semaphore), 0, "fc_stats_reset");
1027 
1028 		/* First reset the link stats */
1029 		if ((rc = ocs_hw_get_link_stats(&ocs->hw, 0, 1, 1, ocs_xport_link_stats_cb, result)) != 0) {
1030 			ocs_log_err(ocs, "%s: Failed to reset link statistics\n", __func__);
1031 			break;
1032 		}
1033 
1034 		/* Wait for semaphore to be signaled when the command completes */
1035 		/* TODO:  Should there be a timeout on this?  If so, how long? */
1036 		if (ocs_sem_p(&(result->stats.semaphore), OCS_SEM_FOREVER) != 0) {
1037 			/* Undefined failure */
1038 			ocs_log_test(ocs, "ocs_sem_p failed\n");
1039 			rc = -ENXIO;
1040 			break;
1041 		}
1042 
1043 		/* Next reset the host stats */
1044 		if ((rc = ocs_hw_get_host_stats(&ocs->hw, 1,  ocs_xport_host_stats_cb, result)) != 0) {
1045 			ocs_log_err(ocs, "%s: Failed to reset host statistics\n", __func__);
1046 			break;
1047 		}
1048 
1049 		/* Wait for semaphore to be signaled when the command completes */
1050 		if (ocs_sem_p(&(result->stats.semaphore), OCS_SEM_FOREVER) != 0) {
1051 			/* Undefined failure */
1052 			ocs_log_test(ocs, "ocs_sem_p failed\n");
1053 			rc = -ENXIO;
1054 			break;
1055 		}
1056 		break;
1057 	}
1058 	case OCS_XPORT_IS_QUIESCED:
1059 		ocs_device_lock(ocs);
1060 			result->value = ocs_list_empty(&ocs->domain_list);
1061 		ocs_device_unlock(ocs);
1062 		break;
1063 	default:
1064 		rc = -1;
1065 		break;
1066 	}
1067 
1068 	return rc;
1069 
1070 }
1071 
1072 static void
1073 ocs_xport_link_stats_cb(int32_t status, uint32_t num_counters, ocs_hw_link_stat_counts_t *counters, void *arg)
1074 {
1075         ocs_xport_stats_t *result = arg;
1076 
1077         result->stats.link_stats.link_failure_error_count = counters[OCS_HW_LINK_STAT_LINK_FAILURE_COUNT].counter;
1078         result->stats.link_stats.loss_of_sync_error_count = counters[OCS_HW_LINK_STAT_LOSS_OF_SYNC_COUNT].counter;
1079         result->stats.link_stats.primitive_sequence_error_count = counters[OCS_HW_LINK_STAT_PRIMITIVE_SEQ_COUNT].counter;
1080         result->stats.link_stats.invalid_transmission_word_error_count = counters[OCS_HW_LINK_STAT_INVALID_XMIT_WORD_COUNT].counter;
1081         result->stats.link_stats.crc_error_count = counters[OCS_HW_LINK_STAT_CRC_COUNT].counter;
1082 
1083         ocs_sem_v(&(result->stats.semaphore));
1084 }
1085 
1086 static void
1087 ocs_xport_host_stats_cb(int32_t status, uint32_t num_counters, ocs_hw_host_stat_counts_t *counters, void *arg)
1088 {
1089         ocs_xport_stats_t *result = arg;
1090 
1091         result->stats.host_stats.transmit_kbyte_count = counters[OCS_HW_HOST_STAT_TX_KBYTE_COUNT].counter;
1092         result->stats.host_stats.receive_kbyte_count = counters[OCS_HW_HOST_STAT_RX_KBYTE_COUNT].counter;
1093         result->stats.host_stats.transmit_frame_count = counters[OCS_HW_HOST_STAT_TX_FRAME_COUNT].counter;
1094         result->stats.host_stats.receive_frame_count = counters[OCS_HW_HOST_STAT_RX_FRAME_COUNT].counter;
1095 
1096         ocs_sem_v(&(result->stats.semaphore));
1097 }
1098 
1099 /**
1100  * @brief Free a transport object.
1101  *
1102  * @par Description
1103  * The transport object is freed.
1104  *
1105  * @param xport Pointer to transport object.
1106  *
1107  * @return None.
1108  */
1109 
1110 void
1111 ocs_xport_free(ocs_xport_t *xport)
1112 {
1113 	ocs_t *ocs;
1114 	uint32_t i;
1115 
1116 	if (xport) {
1117 		ocs = xport->ocs;
1118 		ocs_io_pool_free(xport->io_pool);
1119 		ocs_node_free_pool(ocs);
1120 		if(mtx_initialized(&xport->io_pending_lock.lock))
1121 			ocs_lock_free(&xport->io_pending_lock);
1122 
1123 		for (i = 0; i < SLI4_MAX_FCFI; i++) {
1124 			ocs_lock_free(&xport->fcfi[i].pend_frames_lock);
1125 		}
1126 
1127 		ocs_xport_rq_threads_teardown(xport);
1128 
1129 		ocs_free(ocs, xport, sizeof(*xport));
1130 	}
1131 }
1132