1/*
2 * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
3 * All rights reserved
4 * www.brocade.com
5 *
6 * Linux driver for Brocade Fibre Channel Host Bus Adapter.
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License (GPL) Version 2 as
10 * published by the Free Software Foundation
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 * General Public License for more details.
16 */
17
18/*
19 *  fcpim.c - FCP initiator mode i-t nexus state machine
20 */
21
22#include "bfad_drv.h"
23#include "bfa_fcs.h"
24#include "bfa_fcbuild.h"
25#include "bfad_im.h"
26
27BFA_TRC_FILE(FCS, FCPIM);
28
29/*
30 * forward declarations
31 */
32static void	bfa_fcs_itnim_timeout(void *arg);
33static void	bfa_fcs_itnim_free(struct bfa_fcs_itnim_s *itnim);
34static void	bfa_fcs_itnim_send_prli(void *itnim_cbarg,
35					struct bfa_fcxp_s *fcxp_alloced);
36static void	bfa_fcs_itnim_prli_response(void *fcsarg,
37			 struct bfa_fcxp_s *fcxp, void *cbarg,
38			    bfa_status_t req_status, u32 rsp_len,
39			    u32 resid_len, struct fchs_s *rsp_fchs);
40static void	bfa_fcs_itnim_aen_post(struct bfa_fcs_itnim_s *itnim,
41			enum bfa_itnim_aen_event event);
42
43static void	bfa_fcs_itnim_sm_offline(struct bfa_fcs_itnim_s *itnim,
44					 enum bfa_fcs_itnim_event event);
45static void	bfa_fcs_itnim_sm_prli_send(struct bfa_fcs_itnim_s *itnim,
46					   enum bfa_fcs_itnim_event event);
47static void	bfa_fcs_itnim_sm_prli(struct bfa_fcs_itnim_s *itnim,
48				      enum bfa_fcs_itnim_event event);
49static void	bfa_fcs_itnim_sm_prli_retry(struct bfa_fcs_itnim_s *itnim,
50					    enum bfa_fcs_itnim_event event);
51static void	bfa_fcs_itnim_sm_hcb_online(struct bfa_fcs_itnim_s *itnim,
52					    enum bfa_fcs_itnim_event event);
53static void	bfa_fcs_itnim_sm_hal_rport_online(struct bfa_fcs_itnim_s *itnim,
54					enum bfa_fcs_itnim_event event);
55static void	bfa_fcs_itnim_sm_online(struct bfa_fcs_itnim_s *itnim,
56					enum bfa_fcs_itnim_event event);
57static void	bfa_fcs_itnim_sm_hcb_offline(struct bfa_fcs_itnim_s *itnim,
58					     enum bfa_fcs_itnim_event event);
59static void	bfa_fcs_itnim_sm_initiator(struct bfa_fcs_itnim_s *itnim,
60					   enum bfa_fcs_itnim_event event);
61
62static struct bfa_sm_table_s itnim_sm_table[] = {
63	{BFA_SM(bfa_fcs_itnim_sm_offline), BFA_ITNIM_OFFLINE},
64	{BFA_SM(bfa_fcs_itnim_sm_prli_send), BFA_ITNIM_PRLI_SEND},
65	{BFA_SM(bfa_fcs_itnim_sm_prli), BFA_ITNIM_PRLI_SENT},
66	{BFA_SM(bfa_fcs_itnim_sm_prli_retry), BFA_ITNIM_PRLI_RETRY},
67	{BFA_SM(bfa_fcs_itnim_sm_hcb_online), BFA_ITNIM_HCB_ONLINE},
68	{BFA_SM(bfa_fcs_itnim_sm_online), BFA_ITNIM_ONLINE},
69	{BFA_SM(bfa_fcs_itnim_sm_hcb_offline), BFA_ITNIM_HCB_OFFLINE},
70	{BFA_SM(bfa_fcs_itnim_sm_initiator), BFA_ITNIM_INITIATIOR},
71};
72
73/*
74 *  fcs_itnim_sm FCS itnim state machine
75 */
76
77static void
78bfa_fcs_itnim_sm_offline(struct bfa_fcs_itnim_s *itnim,
79		 enum bfa_fcs_itnim_event event)
80{
81	bfa_trc(itnim->fcs, itnim->rport->pwwn);
82	bfa_trc(itnim->fcs, event);
83
84	switch (event) {
85	case BFA_FCS_ITNIM_SM_FCS_ONLINE:
86		bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_prli_send);
87		itnim->prli_retries = 0;
88		bfa_fcs_itnim_send_prli(itnim, NULL);
89		break;
90
91	case BFA_FCS_ITNIM_SM_OFFLINE:
92		bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_OFFLINE);
93		break;
94
95	case BFA_FCS_ITNIM_SM_INITIATOR:
96		bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_initiator);
97		break;
98
99	case BFA_FCS_ITNIM_SM_DELETE:
100		bfa_fcs_itnim_free(itnim);
101		break;
102
103	default:
104		bfa_sm_fault(itnim->fcs, event);
105	}
106
107}
108
109static void
110bfa_fcs_itnim_sm_prli_send(struct bfa_fcs_itnim_s *itnim,
111		 enum bfa_fcs_itnim_event event)
112{
113	bfa_trc(itnim->fcs, itnim->rport->pwwn);
114	bfa_trc(itnim->fcs, event);
115
116	switch (event) {
117	case BFA_FCS_ITNIM_SM_FRMSENT:
118		bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_prli);
119		break;
120
121	case BFA_FCS_ITNIM_SM_INITIATOR:
122		bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_initiator);
123		bfa_fcxp_walloc_cancel(itnim->fcs->bfa, &itnim->fcxp_wqe);
124		bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_FCS_ONLINE);
125		break;
126
127	case BFA_FCS_ITNIM_SM_OFFLINE:
128		bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline);
129		bfa_fcxp_walloc_cancel(itnim->fcs->bfa, &itnim->fcxp_wqe);
130		bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_OFFLINE);
131		break;
132
133	case BFA_FCS_ITNIM_SM_DELETE:
134		bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline);
135		bfa_fcxp_walloc_cancel(itnim->fcs->bfa, &itnim->fcxp_wqe);
136		bfa_fcs_itnim_free(itnim);
137		break;
138
139	default:
140		bfa_sm_fault(itnim->fcs, event);
141	}
142}
143
144static void
145bfa_fcs_itnim_sm_prli(struct bfa_fcs_itnim_s *itnim,
146		 enum bfa_fcs_itnim_event event)
147{
148	bfa_trc(itnim->fcs, itnim->rport->pwwn);
149	bfa_trc(itnim->fcs, event);
150
151	switch (event) {
152	case BFA_FCS_ITNIM_SM_RSP_OK:
153		if (itnim->rport->scsi_function == BFA_RPORT_INITIATOR)
154			bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_initiator);
155		else
156			bfa_sm_set_state(itnim,
157				bfa_fcs_itnim_sm_hal_rport_online);
158
159		bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_FCS_ONLINE);
160		break;
161
162	case BFA_FCS_ITNIM_SM_RSP_ERROR:
163		bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_prli_retry);
164		bfa_timer_start(itnim->fcs->bfa, &itnim->timer,
165				bfa_fcs_itnim_timeout, itnim,
166				BFA_FCS_RETRY_TIMEOUT);
167		break;
168
169	case BFA_FCS_ITNIM_SM_RSP_NOT_SUPP:
170		bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline);
171		break;
172
173	case BFA_FCS_ITNIM_SM_OFFLINE:
174		bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline);
175		bfa_fcxp_discard(itnim->fcxp);
176		bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_OFFLINE);
177		break;
178
179	case BFA_FCS_ITNIM_SM_INITIATOR:
180		bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_initiator);
181		bfa_fcxp_discard(itnim->fcxp);
182		bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_FCS_ONLINE);
183		break;
184
185	case BFA_FCS_ITNIM_SM_DELETE:
186		bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline);
187		bfa_fcxp_discard(itnim->fcxp);
188		bfa_fcs_itnim_free(itnim);
189		break;
190
191	default:
192		bfa_sm_fault(itnim->fcs, event);
193	}
194}
195
196static void
197bfa_fcs_itnim_sm_hal_rport_online(struct bfa_fcs_itnim_s *itnim,
198				enum bfa_fcs_itnim_event event)
199{
200	bfa_trc(itnim->fcs, itnim->rport->pwwn);
201	bfa_trc(itnim->fcs, event);
202
203	switch (event) {
204	case BFA_FCS_ITNIM_SM_HAL_ONLINE:
205		if (!itnim->bfa_itnim)
206			itnim->bfa_itnim = bfa_itnim_create(itnim->fcs->bfa,
207					itnim->rport->bfa_rport, itnim);
208
209		if (itnim->bfa_itnim) {
210			bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_hcb_online);
211			bfa_itnim_online(itnim->bfa_itnim, itnim->seq_rec);
212		} else {
213			bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline);
214			bfa_sm_send_event(itnim->rport, RPSM_EVENT_DELETE);
215		}
216
217		break;
218
219	case BFA_FCS_ITNIM_SM_OFFLINE:
220		bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline);
221		bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_OFFLINE);
222		break;
223
224	case BFA_FCS_ITNIM_SM_DELETE:
225		bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline);
226		bfa_fcs_itnim_free(itnim);
227		break;
228
229	default:
230		bfa_sm_fault(itnim->fcs, event);
231	}
232}
233
234static void
235bfa_fcs_itnim_sm_prli_retry(struct bfa_fcs_itnim_s *itnim,
236			    enum bfa_fcs_itnim_event event)
237{
238	bfa_trc(itnim->fcs, itnim->rport->pwwn);
239	bfa_trc(itnim->fcs, event);
240
241	switch (event) {
242	case BFA_FCS_ITNIM_SM_TIMEOUT:
243		if (itnim->prli_retries < BFA_FCS_RPORT_MAX_RETRIES) {
244			itnim->prli_retries++;
245			bfa_trc(itnim->fcs, itnim->prli_retries);
246			bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_prli_send);
247			bfa_fcs_itnim_send_prli(itnim, NULL);
248		} else {
249			/* invoke target offline */
250			bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline);
251			bfa_sm_send_event(itnim->rport, RPSM_EVENT_LOGO_IMP);
252		}
253		break;
254
255
256	case BFA_FCS_ITNIM_SM_OFFLINE:
257		bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline);
258		bfa_timer_stop(&itnim->timer);
259		bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_OFFLINE);
260		break;
261
262	case BFA_FCS_ITNIM_SM_INITIATOR:
263		bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_initiator);
264		bfa_timer_stop(&itnim->timer);
265		bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_FCS_ONLINE);
266		break;
267
268	case BFA_FCS_ITNIM_SM_DELETE:
269		bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline);
270		bfa_timer_stop(&itnim->timer);
271		bfa_fcs_itnim_free(itnim);
272		break;
273
274	default:
275		bfa_sm_fault(itnim->fcs, event);
276	}
277}
278
279static void
280bfa_fcs_itnim_sm_hcb_online(struct bfa_fcs_itnim_s *itnim,
281			    enum bfa_fcs_itnim_event event)
282{
283	struct bfad_s *bfad = (struct bfad_s *)itnim->fcs->bfad;
284	char	lpwwn_buf[BFA_STRING_32];
285	char	rpwwn_buf[BFA_STRING_32];
286
287	bfa_trc(itnim->fcs, itnim->rport->pwwn);
288	bfa_trc(itnim->fcs, event);
289
290	switch (event) {
291	case BFA_FCS_ITNIM_SM_HCB_ONLINE:
292		bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_online);
293		bfa_fcb_itnim_online(itnim->itnim_drv);
294		wwn2str(lpwwn_buf, bfa_fcs_lport_get_pwwn(itnim->rport->port));
295		wwn2str(rpwwn_buf, itnim->rport->pwwn);
296		BFA_LOG(KERN_INFO, bfad, bfa_log_level,
297		"Target (WWN = %s) is online for initiator (WWN = %s)\n",
298		rpwwn_buf, lpwwn_buf);
299		bfa_fcs_itnim_aen_post(itnim, BFA_ITNIM_AEN_ONLINE);
300		break;
301
302	case BFA_FCS_ITNIM_SM_OFFLINE:
303		bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_hcb_offline);
304		bfa_itnim_offline(itnim->bfa_itnim);
305		break;
306
307	case BFA_FCS_ITNIM_SM_DELETE:
308		bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline);
309		bfa_fcs_itnim_free(itnim);
310		break;
311
312	default:
313		bfa_sm_fault(itnim->fcs, event);
314	}
315}
316
317static void
318bfa_fcs_itnim_sm_online(struct bfa_fcs_itnim_s *itnim,
319		 enum bfa_fcs_itnim_event event)
320{
321	struct bfad_s *bfad = (struct bfad_s *)itnim->fcs->bfad;
322	char	lpwwn_buf[BFA_STRING_32];
323	char	rpwwn_buf[BFA_STRING_32];
324
325	bfa_trc(itnim->fcs, itnim->rport->pwwn);
326	bfa_trc(itnim->fcs, event);
327
328	switch (event) {
329	case BFA_FCS_ITNIM_SM_OFFLINE:
330		bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_hcb_offline);
331		bfa_fcb_itnim_offline(itnim->itnim_drv);
332		bfa_itnim_offline(itnim->bfa_itnim);
333		wwn2str(lpwwn_buf, bfa_fcs_lport_get_pwwn(itnim->rport->port));
334		wwn2str(rpwwn_buf, itnim->rport->pwwn);
335		if (bfa_fcs_lport_is_online(itnim->rport->port) == BFA_TRUE) {
336			BFA_LOG(KERN_ERR, bfad, bfa_log_level,
337			"Target (WWN = %s) connectivity lost for "
338			"initiator (WWN = %s)\n", rpwwn_buf, lpwwn_buf);
339			bfa_fcs_itnim_aen_post(itnim, BFA_ITNIM_AEN_DISCONNECT);
340		} else {
341			BFA_LOG(KERN_INFO, bfad, bfa_log_level,
342			"Target (WWN = %s) offlined by initiator (WWN = %s)\n",
343			rpwwn_buf, lpwwn_buf);
344			bfa_fcs_itnim_aen_post(itnim, BFA_ITNIM_AEN_OFFLINE);
345		}
346		break;
347
348	case BFA_FCS_ITNIM_SM_DELETE:
349		bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline);
350		bfa_fcs_itnim_free(itnim);
351		break;
352
353	default:
354		bfa_sm_fault(itnim->fcs, event);
355	}
356}
357
358static void
359bfa_fcs_itnim_sm_hcb_offline(struct bfa_fcs_itnim_s *itnim,
360			     enum bfa_fcs_itnim_event event)
361{
362	bfa_trc(itnim->fcs, itnim->rport->pwwn);
363	bfa_trc(itnim->fcs, event);
364
365	switch (event) {
366	case BFA_FCS_ITNIM_SM_HCB_OFFLINE:
367		bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline);
368		bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_OFFLINE);
369		break;
370
371	case BFA_FCS_ITNIM_SM_DELETE:
372		bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline);
373		bfa_fcs_itnim_free(itnim);
374		break;
375
376	default:
377		bfa_sm_fault(itnim->fcs, event);
378	}
379}
380
381/*
382 * This state is set when a discovered rport is also in intiator mode.
383 * This ITN is marked as no_op and is not active and will not be truned into
384 * online state.
385 */
386static void
387bfa_fcs_itnim_sm_initiator(struct bfa_fcs_itnim_s *itnim,
388		 enum bfa_fcs_itnim_event event)
389{
390	bfa_trc(itnim->fcs, itnim->rport->pwwn);
391	bfa_trc(itnim->fcs, event);
392
393	switch (event) {
394	case BFA_FCS_ITNIM_SM_OFFLINE:
395		bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline);
396		bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_OFFLINE);
397		break;
398
399	/*
400	 * fcs_online is expected here for well known initiator ports
401	 */
402	case BFA_FCS_ITNIM_SM_FCS_ONLINE:
403		bfa_sm_send_event(itnim->rport, RPSM_EVENT_FC4_FCS_ONLINE);
404		break;
405
406	case BFA_FCS_ITNIM_SM_RSP_ERROR:
407	case BFA_FCS_ITNIM_SM_INITIATOR:
408		break;
409
410	case BFA_FCS_ITNIM_SM_DELETE:
411		bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline);
412		bfa_fcs_itnim_free(itnim);
413		break;
414
415	default:
416		bfa_sm_fault(itnim->fcs, event);
417	}
418}
419
420static void
421bfa_fcs_itnim_aen_post(struct bfa_fcs_itnim_s *itnim,
422			enum bfa_itnim_aen_event event)
423{
424	struct bfa_fcs_rport_s *rport = itnim->rport;
425	struct bfad_s *bfad = (struct bfad_s *)itnim->fcs->bfad;
426	struct bfa_aen_entry_s	*aen_entry;
427
428	/* Don't post events for well known addresses */
429	if (BFA_FCS_PID_IS_WKA(rport->pid))
430		return;
431
432	bfad_get_aen_entry(bfad, aen_entry);
433	if (!aen_entry)
434		return;
435
436	aen_entry->aen_data.itnim.vf_id = rport->port->fabric->vf_id;
437	aen_entry->aen_data.itnim.ppwwn = bfa_fcs_lport_get_pwwn(
438					bfa_fcs_get_base_port(itnim->fcs));
439	aen_entry->aen_data.itnim.lpwwn = bfa_fcs_lport_get_pwwn(rport->port);
440	aen_entry->aen_data.itnim.rpwwn = rport->pwwn;
441
442	/* Send the AEN notification */
443	bfad_im_post_vendor_event(aen_entry, bfad, ++rport->fcs->fcs_aen_seq,
444				  BFA_AEN_CAT_ITNIM, event);
445}
446
447static void
448bfa_fcs_itnim_send_prli(void *itnim_cbarg, struct bfa_fcxp_s *fcxp_alloced)
449{
450	struct bfa_fcs_itnim_s *itnim = itnim_cbarg;
451	struct bfa_fcs_rport_s *rport = itnim->rport;
452	struct bfa_fcs_lport_s *port = rport->port;
453	struct fchs_s	fchs;
454	struct bfa_fcxp_s *fcxp;
455	int		len;
456
457	bfa_trc(itnim->fcs, itnim->rport->pwwn);
458
459	fcxp = fcxp_alloced ? fcxp_alloced :
460	       bfa_fcs_fcxp_alloc(port->fcs, BFA_TRUE);
461	if (!fcxp) {
462		itnim->stats.fcxp_alloc_wait++;
463		bfa_fcs_fcxp_alloc_wait(port->fcs->bfa, &itnim->fcxp_wqe,
464				bfa_fcs_itnim_send_prli, itnim, BFA_TRUE);
465		return;
466	}
467	itnim->fcxp = fcxp;
468
469	len = fc_prli_build(&fchs, bfa_fcxp_get_reqbuf(fcxp),
470			    itnim->rport->pid, bfa_fcs_lport_get_fcid(port), 0);
471
472	bfa_fcxp_send(fcxp, rport->bfa_rport, port->fabric->vf_id, port->lp_tag,
473		      BFA_FALSE, FC_CLASS_3, len, &fchs,
474		      bfa_fcs_itnim_prli_response, (void *)itnim,
475		      FC_MAX_PDUSZ, FC_ELS_TOV);
476
477	itnim->stats.prli_sent++;
478	bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_FRMSENT);
479}
480
481static void
482bfa_fcs_itnim_prli_response(void *fcsarg, struct bfa_fcxp_s *fcxp, void *cbarg,
483			    bfa_status_t req_status, u32 rsp_len,
484			    u32 resid_len, struct fchs_s *rsp_fchs)
485{
486	struct bfa_fcs_itnim_s *itnim = (struct bfa_fcs_itnim_s *) cbarg;
487	struct fc_els_cmd_s *els_cmd;
488	struct fc_prli_s *prli_resp;
489	struct fc_ls_rjt_s *ls_rjt;
490	struct fc_prli_params_s *sparams;
491
492	bfa_trc(itnim->fcs, req_status);
493
494	/*
495	 * Sanity Checks
496	 */
497	if (req_status != BFA_STATUS_OK) {
498		itnim->stats.prli_rsp_err++;
499		bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_RSP_ERROR);
500		return;
501	}
502
503	els_cmd = (struct fc_els_cmd_s *) BFA_FCXP_RSP_PLD(fcxp);
504
505	if (els_cmd->els_code == FC_ELS_ACC) {
506		prli_resp = (struct fc_prli_s *) els_cmd;
507
508		if (fc_prli_rsp_parse(prli_resp, rsp_len) != FC_PARSE_OK) {
509			bfa_trc(itnim->fcs, rsp_len);
510			/*
511			 * Check if this  r-port is also in Initiator mode.
512			 * If so, we need to set this ITN as a no-op.
513			 */
514			if (prli_resp->parampage.servparams.initiator) {
515				bfa_trc(itnim->fcs, prli_resp->parampage.type);
516				itnim->rport->scsi_function =
517						BFA_RPORT_INITIATOR;
518				itnim->stats.prli_rsp_acc++;
519				itnim->stats.initiator++;
520				bfa_sm_send_event(itnim,
521						  BFA_FCS_ITNIM_SM_RSP_OK);
522				return;
523			}
524
525			itnim->stats.prli_rsp_parse_err++;
526			return;
527		}
528		itnim->rport->scsi_function = BFA_RPORT_TARGET;
529
530		sparams = &prli_resp->parampage.servparams;
531		itnim->seq_rec	     = sparams->retry;
532		itnim->rec_support   = sparams->rec_support;
533		itnim->task_retry_id = sparams->task_retry_id;
534		itnim->conf_comp     = sparams->confirm;
535
536		itnim->stats.prli_rsp_acc++;
537		bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_RSP_OK);
538	} else {
539		ls_rjt = (struct fc_ls_rjt_s *) BFA_FCXP_RSP_PLD(fcxp);
540
541		bfa_trc(itnim->fcs, ls_rjt->reason_code);
542		bfa_trc(itnim->fcs, ls_rjt->reason_code_expl);
543
544		itnim->stats.prli_rsp_rjt++;
545		if (ls_rjt->reason_code == FC_LS_RJT_RSN_CMD_NOT_SUPP) {
546			bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_RSP_NOT_SUPP);
547			return;
548		}
549		bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_RSP_ERROR);
550	}
551}
552
553static void
554bfa_fcs_itnim_timeout(void *arg)
555{
556	struct bfa_fcs_itnim_s *itnim = (struct bfa_fcs_itnim_s *) arg;
557
558	itnim->stats.timeout++;
559	bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_TIMEOUT);
560}
561
562static void
563bfa_fcs_itnim_free(struct bfa_fcs_itnim_s *itnim)
564{
565	if (itnim->bfa_itnim) {
566		bfa_itnim_delete(itnim->bfa_itnim);
567		itnim->bfa_itnim = NULL;
568	}
569
570	bfa_fcb_itnim_free(itnim->fcs->bfad, itnim->itnim_drv);
571}
572
573
574
575/*
576 *  itnim_public FCS ITNIM public interfaces
577 */
578
579/*
580 *	Called by rport when a new rport is created.
581 *
582 * @param[in] rport	-  remote port.
583 */
584struct bfa_fcs_itnim_s *
585bfa_fcs_itnim_create(struct bfa_fcs_rport_s *rport)
586{
587	struct bfa_fcs_lport_s *port = rport->port;
588	struct bfa_fcs_itnim_s *itnim;
589	struct bfad_itnim_s   *itnim_drv;
590
591	/*
592	 * call bfad to allocate the itnim
593	 */
594	bfa_fcb_itnim_alloc(port->fcs->bfad, &itnim, &itnim_drv);
595	if (itnim == NULL) {
596		bfa_trc(port->fcs, rport->pwwn);
597		return NULL;
598	}
599
600	/*
601	 * Initialize itnim
602	 */
603	itnim->rport = rport;
604	itnim->fcs = rport->fcs;
605	itnim->itnim_drv = itnim_drv;
606
607	itnim->bfa_itnim     = NULL;
608	itnim->seq_rec	     = BFA_FALSE;
609	itnim->rec_support   = BFA_FALSE;
610	itnim->conf_comp     = BFA_FALSE;
611	itnim->task_retry_id = BFA_FALSE;
612
613	/*
614	 * Set State machine
615	 */
616	bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline);
617
618	return itnim;
619}
620
621/*
622 *	Called by rport to delete  the instance of FCPIM.
623 *
624 * @param[in] rport	-  remote port.
625 */
626void
627bfa_fcs_itnim_delete(struct bfa_fcs_itnim_s *itnim)
628{
629	bfa_trc(itnim->fcs, itnim->rport->pid);
630	bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_DELETE);
631}
632
633/*
634 * Notification from rport that PLOGI is complete to initiate FC-4 session.
635 */
636void
637bfa_fcs_itnim_brp_online(struct bfa_fcs_itnim_s *itnim)
638{
639	itnim->stats.onlines++;
640
641	if (!BFA_FCS_PID_IS_WKA(itnim->rport->pid))
642		bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_HAL_ONLINE);
643}
644
645/*
646 * Called by rport to handle a remote device offline.
647 */
648void
649bfa_fcs_itnim_rport_offline(struct bfa_fcs_itnim_s *itnim)
650{
651	itnim->stats.offlines++;
652	bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_OFFLINE);
653}
654
655/*
656 * Called by rport when remote port is known to be an initiator from
657 * PRLI received.
658 */
659void
660bfa_fcs_itnim_is_initiator(struct bfa_fcs_itnim_s *itnim)
661{
662	bfa_trc(itnim->fcs, itnim->rport->pid);
663	itnim->stats.initiator++;
664	bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_INITIATOR);
665}
666
667/*
668 * Called by rport to check if the itnim is online.
669 */
670bfa_status_t
671bfa_fcs_itnim_get_online_state(struct bfa_fcs_itnim_s *itnim)
672{
673	bfa_trc(itnim->fcs, itnim->rport->pid);
674	switch (bfa_sm_to_state(itnim_sm_table, itnim->sm)) {
675	case BFA_ITNIM_ONLINE:
676	case BFA_ITNIM_INITIATIOR:
677		return BFA_STATUS_OK;
678
679	default:
680		return BFA_STATUS_NO_FCPIM_NEXUS;
681	}
682}
683
684/*
685 * BFA completion callback for bfa_itnim_online().
686 */
687void
688bfa_cb_itnim_online(void *cbarg)
689{
690	struct bfa_fcs_itnim_s *itnim = (struct bfa_fcs_itnim_s *) cbarg;
691
692	bfa_trc(itnim->fcs, itnim->rport->pwwn);
693	bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_HCB_ONLINE);
694}
695
696/*
697 * BFA completion callback for bfa_itnim_offline().
698 */
699void
700bfa_cb_itnim_offline(void *cb_arg)
701{
702	struct bfa_fcs_itnim_s *itnim = (struct bfa_fcs_itnim_s *) cb_arg;
703
704	bfa_trc(itnim->fcs, itnim->rport->pwwn);
705	bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_HCB_OFFLINE);
706}
707
708/*
709 * Mark the beginning of PATH TOV handling. IO completion callbacks
710 * are still pending.
711 */
712void
713bfa_cb_itnim_tov_begin(void *cb_arg)
714{
715	struct bfa_fcs_itnim_s *itnim = (struct bfa_fcs_itnim_s *) cb_arg;
716
717	bfa_trc(itnim->fcs, itnim->rport->pwwn);
718}
719
720/*
721 * Mark the end of PATH TOV handling. All pending IOs are already cleaned up.
722 */
723void
724bfa_cb_itnim_tov(void *cb_arg)
725{
726	struct bfa_fcs_itnim_s *itnim = (struct bfa_fcs_itnim_s *) cb_arg;
727	struct bfad_itnim_s *itnim_drv = itnim->itnim_drv;
728
729	bfa_trc(itnim->fcs, itnim->rport->pwwn);
730	itnim_drv->state = ITNIM_STATE_TIMEOUT;
731}
732
733/*
734 *		BFA notification to FCS/driver for second level error recovery.
735 *
736 * Atleast one I/O request has timedout and target is unresponsive to
737 * repeated abort requests. Second level error recovery should be initiated
738 * by starting implicit logout and recovery procedures.
739 */
740void
741bfa_cb_itnim_sler(void *cb_arg)
742{
743	struct bfa_fcs_itnim_s *itnim = (struct bfa_fcs_itnim_s *) cb_arg;
744
745	itnim->stats.sler++;
746	bfa_trc(itnim->fcs, itnim->rport->pwwn);
747	bfa_sm_send_event(itnim->rport, RPSM_EVENT_LOGO_IMP);
748}
749
750struct bfa_fcs_itnim_s *
751bfa_fcs_itnim_lookup(struct bfa_fcs_lport_s *port, wwn_t rpwwn)
752{
753	struct bfa_fcs_rport_s *rport;
754	rport = bfa_fcs_rport_lookup(port, rpwwn);
755
756	if (!rport)
757		return NULL;
758
759	WARN_ON(rport->itnim == NULL);
760	return rport->itnim;
761}
762
763bfa_status_t
764bfa_fcs_itnim_attr_get(struct bfa_fcs_lport_s *port, wwn_t rpwwn,
765		       struct bfa_itnim_attr_s *attr)
766{
767	struct bfa_fcs_itnim_s *itnim = NULL;
768
769	itnim = bfa_fcs_itnim_lookup(port, rpwwn);
770
771	if (itnim == NULL)
772		return BFA_STATUS_NO_FCPIM_NEXUS;
773
774	attr->state	    = bfa_sm_to_state(itnim_sm_table, itnim->sm);
775	attr->retry	    = itnim->seq_rec;
776	attr->rec_support   = itnim->rec_support;
777	attr->conf_comp	    = itnim->conf_comp;
778	attr->task_retry_id = itnim->task_retry_id;
779	return BFA_STATUS_OK;
780}
781
782bfa_status_t
783bfa_fcs_itnim_stats_get(struct bfa_fcs_lport_s *port, wwn_t rpwwn,
784			struct bfa_itnim_stats_s *stats)
785{
786	struct bfa_fcs_itnim_s *itnim = NULL;
787
788	WARN_ON(port == NULL);
789
790	itnim = bfa_fcs_itnim_lookup(port, rpwwn);
791
792	if (itnim == NULL)
793		return BFA_STATUS_NO_FCPIM_NEXUS;
794
795	memcpy(stats, &itnim->stats, sizeof(struct bfa_itnim_stats_s));
796
797	return BFA_STATUS_OK;
798}
799
800bfa_status_t
801bfa_fcs_itnim_stats_clear(struct bfa_fcs_lport_s *port, wwn_t rpwwn)
802{
803	struct bfa_fcs_itnim_s *itnim = NULL;
804
805	WARN_ON(port == NULL);
806
807	itnim = bfa_fcs_itnim_lookup(port, rpwwn);
808
809	if (itnim == NULL)
810		return BFA_STATUS_NO_FCPIM_NEXUS;
811
812	memset(&itnim->stats, 0, sizeof(struct bfa_itnim_stats_s));
813	return BFA_STATUS_OK;
814}
815
816void
817bfa_fcs_fcpim_uf_recv(struct bfa_fcs_itnim_s *itnim,
818			struct fchs_s *fchs, u16 len)
819{
820	struct fc_els_cmd_s *els_cmd;
821
822	bfa_trc(itnim->fcs, fchs->type);
823
824	if (fchs->type != FC_TYPE_ELS)
825		return;
826
827	els_cmd = (struct fc_els_cmd_s *) (fchs + 1);
828
829	bfa_trc(itnim->fcs, els_cmd->els_code);
830
831	switch (els_cmd->els_code) {
832	case FC_ELS_PRLO:
833		bfa_fcs_rport_prlo(itnim->rport, fchs->ox_id);
834		break;
835
836	default:
837		WARN_ON(1);
838	}
839}
840