1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * The following notice accompanied the original version of this file:
24 *
25 * BSD LICENSE
26 *
27 * Copyright(c) 2007 Intel Corporation. All rights reserved.
28 * All rights reserved.
29 *
30 * Redistribution and use in source and binary forms, with or without
31 * modification, are permitted provided that the following conditions
32 * are met:
33 *
34 *   * Redistributions of source code must retain the above copyright
35 *     notice, this list of conditions and the following disclaimer.
36 *   * Redistributions in binary form must reproduce the above copyright
37 *     notice, this list of conditions and the following disclaimer in
38 *     the documentation and/or other materials provided with the
39 *     distribution.
40 *   * Neither the name of Intel Corporation nor the names of its
41 *     contributors may be used to endorse or promote products derived
42 *     from this software without specific prior written permission.
43 *
44 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
45 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
46 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
47 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
48 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
49 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
50 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
51 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
52 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
53 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
54 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
55 */
56
57/*
58 * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
59 */
60
61/*
62 * This file defines interface functions between fcoe and fcoei driver.
63 */
64
65#include <sys/conf.h>
66#include <sys/ddi.h>
67#include <sys/stat.h>
68#include <sys/pci.h>
69#include <sys/sunddi.h>
70#include <sys/modctl.h>
71#include <sys/file.h>
72#include <sys/cred.h>
73#include <sys/byteorder.h>
74#include <sys/atomic.h>
75#include <sys/scsi/scsi.h>
76#include <sys/mac_client.h>
77#include <sys/modhash.h>
78
79/*
80 * LEADVILLE header files
81 */
82#include <sys/fibre-channel/fc.h>
83#include <sys/fibre-channel/impl/fc_fcaif.h>
84
85/*
86 * COMSTAR header files
87 */
88#include <sys/stmf_defines.h>
89
90/*
91 * FCOE header files
92 */
93#include <sys/fcoe/fcoe_common.h>
94
95/*
96 * Driver's own header files
97 */
98#include <fcoei.h>
99
100/*
101 * Forward declaration of internal functions
102 */
103static void fcoei_process_unsol_els_req(fcoe_frame_t *frm);
104static void fcoei_process_sol_els_rsp(fcoe_frame_t *frm);
105static void fcoei_process_unsol_abts_req(fcoe_frame_t *frame);
106static void fcoei_process_sol_abts_acc(fcoe_frame_t *frame);
107static void fcoei_process_sol_abts_rjt(fcoe_frame_t *frame);
108static void fcoei_process_sol_ct_rsp(fcoe_frame_t *frame);
109static void fcoei_process_unsol_xfer_rdy(fcoe_frame_t *frame);
110static void fcoei_process_sol_fcp_resp(fcoe_frame_t *frm);
111
112static void fcoei_fill_fcp_resp(uint8_t *src, uint8_t *dest, int size);
113static void fcoei_fill_els_fpkt_resp(fcoe_frame_t *frm, fcoei_exchange_t *xch,
114    int size);
115
116/*
117 * fcoei_rx_frame
118 *	Unsolicited frame is received
119 *
120 * Input:
121 *	frame = unsolicited frame that is received
122 *
123 * Return:
124 *	N/A
125 *
126 * Comment:
127 *	N/A
128 */
129static void
130fcoei_rx_frame(fcoe_frame_t *frm)
131{
132	if (!(FRM2SS(frm)->ss_flags & SS_FLAG_LV_BOUND)) {
133		/*
134		 * Release the frame and netb
135		 */
136		FCOEI_LOG(__FUNCTION__, "not bound now");
137		frm->frm_eport->eport_free_netb(frm->frm_netb);
138		frm->frm_eport->eport_release_frame(frm);
139		return;
140	}
141
142	FRM2IFM(frm)->ifm_ae.ae_type = AE_EVENT_UNSOL_FRAME;
143	FRM2IFM(frm)->ifm_ae.ae_obj = frm;
144
145	mutex_enter(&FRM2SS(frm)->ss_watchdog_mutex);
146	list_insert_tail(&FRM2SS(frm)->ss_event_list, &FRM2IFM(frm)->ifm_ae);
147	if (FRM2SS(frm)->ss_flags & SS_FLAG_WATCHDOG_IDLE) {
148		cv_signal(&FRM2SS(frm)->ss_watchdog_cv);
149	}
150	mutex_exit(&FRM2SS(frm)->ss_watchdog_mutex);
151}
152
153/*
154 * fcoei_release_sol_frame
155 *	Release the solicited frame that has just been sent out
156 *
157 * Input:
158 *	frame = solicited frame that has been sent out
159 *
160 * Returns:
161 *	N/A
162 *
163 * Comments:
164 *	After FCOE sends solicited frames out, it will call this to notify
165 *	FCOEI of the completion.
166 */
167static void
168fcoei_release_sol_frame(fcoe_frame_t *frm)
169{
170	/*
171	 * For request-type frames, it's safe to be handled out of
172	 * watchdog, because it needn't update anything
173	 */
174	switch (FRM2IFM(frm)->ifm_rctl) {
175	case R_CTL_SOLICITED_DATA:
176	case R_CTL_COMMAND:
177	case R_CTL_ELS_REQ:
178	case R_CTL_UNSOL_CONTROL:
179	case R_CTL_LS_ABTS:
180		FRM2SS(frm)->ss_eport->eport_release_frame(frm);
181		break;
182
183	default:
184		FRM2IFM(frm)->ifm_ae.ae_type = AE_EVENT_SOL_FRAME;
185		FRM2IFM(frm)->ifm_ae.ae_obj = frm;
186
187		mutex_enter(&FRM2SS(frm)->ss_watchdog_mutex);
188		list_insert_tail(&FRM2SS(frm)->ss_event_list,
189		    &FRM2IFM(frm)->ifm_ae);
190		if (FRM2SS(frm)->ss_flags & SS_FLAG_WATCHDOG_IDLE) {
191			cv_signal(&FRM2SS(frm)->ss_watchdog_cv);
192		}
193		mutex_exit(&FRM2SS(frm)->ss_watchdog_mutex);
194		break;
195	}
196}
197
198/*
199 * fcoei_process_unsol_xfer_rdy
200 *	XFER_RDY is received
201 *
202 * Input:
203 *	frm = XFER_RDY frame
204 *
205 * Returns:
206 *	N/A
207 *
208 * Comments:
209 *	N/A
210 */
211static void
212fcoei_process_unsol_xfer_rdy(fcoe_frame_t *frm)
213{
214	uint16_t		 sol_oxid;
215	fcoei_exchange_t	*xch;
216	int			 rcv_buf_size;
217	int			 offset;
218	int			 left_size;
219	int			 data_size;
220	int			 frm_num;
221	int			 idx;
222	fcoe_frame_t		*nfrm;
223
224	sol_oxid = FRM_OXID(frm);
225	if (mod_hash_find(FRM2SS(frm)->ss_sol_oxid_hash,
226	    FMHK(sol_oxid), (mod_hash_val_t *)&xch) != 0) {
227		return;
228	}
229
230	/*
231	 * rcv_buf_size is the total size of data that should be transferred
232	 * in this sequence.
233	 * offset is based on the exchange not the sequence.
234	 */
235	xch->xch_rxid = FRM_RXID(frm);
236	rcv_buf_size = FCOE_B2V_4(frm->frm_payload + 4);
237	offset = FCOE_B2V_4(frm->frm_payload);
238	ASSERT(xch->xch_resid >= rcv_buf_size);
239
240	/*
241	 * Local variables initialization
242	 */
243	left_size = rcv_buf_size;
244	data_size = FRM2SS(frm)->ss_fcp_data_payload_size;
245	frm_num = (rcv_buf_size + data_size - 1) / data_size;
246
247	for (idx = 0; idx < frm_num - 1; idx++) {
248		/*
249		 * The first (frm_num -1) frames are always full
250		 */
251		nfrm = FRM2SS(frm)->ss_eport->eport_alloc_frame(
252		    FRM2SS(frm)->ss_eport, data_size + FCFH_SIZE, NULL);
253		if (nfrm == NULL) {
254			FCOEI_LOG(__FUNCTION__, "can't alloc frame");
255			return;
256		}
257
258		/*
259		 * Copy the data payload that will  be transferred
260		 */
261		bcopy(offset + (uint8_t *)xch->xch_fpkt->pkt_data,
262		    nfrm->frm_payload, nfrm->frm_payload_size);
263
264		FFM_R_CTL(R_CTL_SOLICITED_DATA, nfrm);
265		FFM_TYPE(FC_TYPE_SCSI_FCP, nfrm);
266		FFM_F_CTL(0x010008, nfrm);
267		FFM_OXID(xch->xch_oxid, nfrm);
268		FFM_RXID(xch->xch_rxid, nfrm);
269		FFM_S_ID(FRM_D_ID(frm), nfrm);
270		FFM_D_ID(FRM_S_ID(frm), nfrm);
271		FFM_SEQ_CNT(idx, nfrm);
272		FFM_PARAM(offset, nfrm);
273		fcoei_init_ifm(nfrm, xch);
274
275		/*
276		 * Submit the frame
277		 */
278		xch->xch_ss->ss_eport->eport_tx_frame(nfrm);
279
280		/*
281		 * Update offset and left_size
282		 */
283		offset += data_size;
284		left_size -= data_size;
285	}
286
287	/*
288	 * Send the last data frame of this sequence
289	 */
290	data_size = left_size;
291	nfrm = xch->xch_ss->ss_eport->eport_alloc_frame(
292	    xch->xch_ss->ss_eport, data_size + FCFH_SIZE, NULL);
293	if (nfrm != NULL) {
294		fcoei_init_ifm(nfrm, xch);
295	} else {
296		ASSERT(0);
297		return;
298	}
299
300	/*
301	 * Copy the data payload that will be transferred
302	 */
303	bcopy(offset + (uint8_t *)xch->xch_fpkt->pkt_data,
304	    nfrm->frm_payload, nfrm->frm_payload_size);
305
306	/*
307	 * Set ifm_rctl for fcoei_handle_sol_frame_done
308	 */
309	FRM2IFM(nfrm)->ifm_rctl = R_CTL_SOLICITED_DATA;
310
311	/*
312	 * FFM
313	 */
314	FFM_R_CTL(R_CTL_SOLICITED_DATA, nfrm);
315	FFM_TYPE(FC_TYPE_SCSI_FCP, nfrm);
316	FFM_F_CTL(0x090008, nfrm);
317	FFM_OXID(xch->xch_oxid, nfrm);
318	FFM_RXID(xch->xch_rxid, nfrm);
319	FFM_S_ID(FRM_D_ID(frm), nfrm);
320	FFM_D_ID(FRM_S_ID(frm), nfrm);
321	FFM_SEQ_CNT(idx, nfrm);
322	FFM_PARAM(offset, nfrm);
323
324	/*
325	 * Submit the frame
326	 */
327	xch->xch_ss->ss_eport->eport_tx_frame(nfrm);
328
329	/*
330	 * Sequence is a transaction, so we need only update
331	 * xch_remained_bytes in the end.
332	 */
333	xch->xch_resid -= rcv_buf_size;
334}
335
336/*
337 * fcoei_process_unsol_els_req
338 *	els req frame is received
339 *
340 * Input:
341 *	frm = ELS request frame
342 *
343 * Returns:
344 *	N/A
345 *
346 * Comments:
347 *	We will not create exchange data structure at this time,
348 *	and we should create unsolicited buffer, which will only
349 *	contain the exchange's request payload.
350 */
351static void
352fcoei_process_unsol_els_req(fcoe_frame_t *frm)
353{
354	fc_unsol_buf_t		*ub;
355	fc_rscn_t		*rscn;
356	uint32_t		 offset;
357	fcoei_exchange_t	*xch_tmp;
358
359	/*
360	 * Get the unsol rxid first
361	 */
362	FCOEI_SET_UNSOL_FRM_RXID(frm, xch_tmp);
363
364	/*
365	 * Do proper ub initialization
366	 */
367	ub = (fc_unsol_buf_t *)kmem_zalloc(sizeof (fc_unsol_buf_t), KM_SLEEP);
368	ub->ub_class = FC_TRAN_CLASS3;
369	ub->ub_bufsize = frm->frm_payload_size;
370	ub->ub_buffer = kmem_alloc(frm->frm_payload_size, KM_SLEEP);
371	ub->ub_port_handle = FRM2SS(frm);
372	ub->ub_token = (uint64_t)(long)ub;
373
374	/*
375	 * header conversion
376	 * Caution: ub_buffer is big endian, but ub_frame should be host-format
377	 * RSCN is one exception.
378	 */
379	FCOEI_FRM2FHDR(frm, &ub->ub_frame);
380
381	/*
382	 * If it's FLOGI, and our FLOGI failed last time,
383	 * then we post online event
384	 */
385	if ((FRM2SS(frm)->ss_flags & SS_FLAG_FLOGI_FAILED) &&
386	    (frm->frm_payload[0] == LA_ELS_FLOGI)) {
387		frm->frm_eport->eport_flags |=
388		    EPORT_FLAG_IS_DIRECT_P2P;
389		FRM2SS(frm)->ss_bind_info.port_statec_cb(FRM2SS(frm)->ss_port,
390		    FC_STATE_ONLINE);
391	}
392
393	switch (frm->frm_payload[0]) {
394	case LA_ELS_RSCN:
395		/*
396		 * Only RSCN need byte swapping
397		 */
398		rscn = (fc_rscn_t *)(void *)ub->ub_buffer;
399		rscn->rscn_code = frm->frm_payload[0];
400		rscn->rscn_len = frm->frm_payload[1];
401		rscn->rscn_payload_len =
402		    FCOE_B2V_2(frm->frm_payload + 2);
403
404		offset = 4;
405		for (int i = 0; i < rscn->rscn_payload_len - 4; i += 4) {
406			*(uint32_t *)((intptr_t)(uint8_t *)ub->ub_buffer +
407			    offset) = FCOE_B2V_4(frm->frm_payload + offset);
408			offset += 4;
409		}
410		break;
411
412	default:
413		bcopy(frm->frm_payload, ub->ub_buffer, frm->frm_payload_size);
414		break;
415	}
416
417	/*
418	 * Pass this unsol ELS up to Leadville
419	 */
420	FRM2SS(frm)->ss_bind_info.port_unsol_cb(FRM2SS(frm)->ss_port, ub, 0);
421}
422
423/*
424 * fcoei_search_abort_xch
425 *	Find the exchange that should be aborted
426 *
427 * Input:
428 *	key = oxid of the exchange
429 *	val = the exchange
430 *	arg = the soft state
431 *
432 * Returns:
433 *	MH_WALK_TERMINATE = found it, terminate the walk
434 *	MH_WALK_CONTINUE = not found, continue the walk
435 *
436 * Comments:
437 *	N/A
438 */
439/* ARGSUSED */
440static uint32_t
441fcoei_search_abort_xch(mod_hash_key_t key, mod_hash_val_t *val, void *arg)
442{
443	fcoei_walk_arg_t	*wa = (fcoei_walk_arg_t *)arg;
444	fcoei_exchange_t	*xch = (fcoei_exchange_t *)val;
445
446	if (xch->xch_oxid == wa->wa_oxid) {
447		wa->wa_xch = xch;
448		ASSERT(xch->xch_oxid == CMHK(key));
449		return (MH_WALK_TERMINATE);
450	}
451
452	return (MH_WALK_CONTINUE);
453}
454
455/*
456 * fcoei_process_unsol_abts_req
457 *	ABTS request is received
458 *
459 * Input:
460 *	frm = ABTS request frame
461 *
462 * Returns:
463 *	N/A
464 *
465 * Comments:
466 *	The remote side wants to abort one unsolicited exchange.
467 */
468static void
469fcoei_process_unsol_abts_req(fcoe_frame_t *frm)
470{
471	fcoei_exchange_t	*xch = NULL;
472	fcoe_frame_t		*nfrm;
473	int			 payload_size;
474	fcoei_walk_arg_t	 walk_arg;
475
476	/*
477	 * According to spec, the responder could want to ABTS xch too
478	 */
479	if (FRM_SENDER_IS_XCH_RESPONDER(frm)) {
480		uint16_t sol_oxid = FRM_OXID(frm);
481		(void) mod_hash_find(FRM2SS(frm)->ss_sol_oxid_hash,
482		    FMHK(sol_oxid), (mod_hash_val_t *)&xch);
483	} else {
484		/*
485		 * it's a unsolicited exchange, and we need find it out from
486		 * unsolicited hash table. But at this time, RXID in frame could
487		 * still be 0xFFFF in most cases, so we need do exaustive search
488		 */
489		walk_arg.wa_xch = NULL;
490		walk_arg.wa_oxid = FRM_OXID(frm);
491		mod_hash_walk(FRM2SS(frm)->ss_unsol_rxid_hash,
492		    fcoei_search_abort_xch, &walk_arg);
493		xch = walk_arg.wa_xch;
494	}
495
496	if (xch == NULL) {
497		payload_size = 4;
498		nfrm = FRM2SS(frm)->ss_eport->eport_alloc_frame(
499		    FRM2SS(frm)->ss_eport,
500		    payload_size + FCFH_SIZE, NULL);
501		if (nfrm == NULL) {
502			FCOEI_LOG(__FUNCTION__, "can't alloc frame");
503			return;
504		}
505
506		bzero(nfrm->frm_payload, nfrm->frm_payload_size);
507		nfrm->frm_payload[1] = 0x05;
508		nfrm->frm_payload[3] = 0xAA;
509		FFM_R_CTL(R_CTL_LS_BA_RJT, nfrm);
510		fcoei_init_ifm(nfrm, xch);
511	} else {
512		/*
513		 * We should complete the exchange with frm as NULL,
514		 * and we don't care its success or failure
515		 */
516		fcoei_complete_xch(xch, NULL, FC_PKT_FAILURE, FC_REASON_ABTX);
517
518		/*
519		 * Construct ABTS ACC frame
520		 */
521		payload_size = 12;
522		nfrm = FRM2SS(frm)->ss_eport->eport_alloc_frame(
523		    FRM2SS(frm)->ss_eport, payload_size + FCFH_SIZE, NULL);
524		if (nfrm == NULL) {
525			FCOEI_LOG(__FUNCTION__, "can't alloc frame");
526			return;
527		}
528
529		bzero(nfrm->frm_payload, nfrm->frm_payload_size);
530		nfrm->frm_payload[4] = 0xFF & (xch->xch_oxid >> 8);
531		nfrm->frm_payload[5] = 0xFF & (xch->xch_oxid);
532		nfrm->frm_payload[6] = 0xFF & (xch->xch_rxid >> 8);
533		nfrm->frm_payload[7] = 0xFF & (xch->xch_rxid);
534		nfrm->frm_payload[10] = 0xFF;
535		nfrm->frm_payload[11] = 0xFF;
536
537		FFM_R_CTL(R_CTL_LS_BA_ACC, nfrm);
538		fcoei_init_ifm(nfrm, xch);
539	}
540
541	FFM_D_ID(FRM_S_ID(frm), nfrm);
542	FFM_S_ID(FRM_D_ID(frm), nfrm);
543	FFM_TYPE(FRM_TYPE(frm), nfrm);
544	FFM_F_CTL(FRM_F_CTL(frm), nfrm);
545	FFM_OXID(FRM_OXID(frm), nfrm);
546	FFM_RXID(FRM_RXID(frm), nfrm);
547	FRM2SS(frm)->ss_eport->eport_tx_frame(nfrm);
548}
549
550/*
551 * fcoei_process_sol_fcp_resp
552 *	FCP response is received
553 *
554 * Input:
555 *	frm = FCP response frame
556 *
557 * Returns:
558 *	N/A
559 *
560 * Comments:
561 *	N/A
562 */
563static void
564fcoei_process_sol_fcp_resp(fcoe_frame_t *frm)
565{
566	uint16_t		 sol_oxid;
567	uint32_t		 actual_size;
568	fcoei_exchange_t	*xch  = NULL;
569	fc_packet_t		*fpkt = NULL;
570	mod_hash_val_t		 val;
571	uint32_t		 i_fcp_status;
572
573	/*
574	 * Firstly, we search the related exchange
575	 */
576	sol_oxid = FRM_OXID(frm);
577	if (mod_hash_find(FRM2SS(frm)->ss_sol_oxid_hash,
578	    FMHK(sol_oxid), (mod_hash_val_t *)&xch) != 0) {
579		PRT_FRM_HDR(__FUNCTION__, frm);
580		FCOEI_LOG(__FUNCTION__, "can't find the corresponding xch: "
581		    "oxid/%x %lu - %lu", sol_oxid,
582		    CURRENT_CLOCK, frm->frm_clock);
583		return;
584	} else {
585		fpkt = xch->xch_fpkt;
586	}
587
588	/*
589	 * Decide the actual response length
590	 */
591	actual_size = fpkt->pkt_rsplen;
592	if (actual_size > frm->frm_payload_size) {
593		actual_size = frm->frm_payload_size;
594	}
595
596	/*
597	 * Update the exchange and hash table
598	 */
599	(void) mod_hash_remove(FRM2SS(frm)->ss_sol_oxid_hash,
600	    FMHK(xch->xch_oxid), &val);
601	ASSERT((fcoei_exchange_t *)val == xch);
602	xch->xch_flags &= ~XCH_FLAG_IN_SOL_HASH;
603
604	/*
605	 * Upate fpkt related elements
606	 */
607	FCOEI_FRM2FHDR(frm, &fpkt->pkt_resp_fhdr);
608
609	/*
610	 * we should set pkt_reason and pkt_state carefully
611	 */
612	fpkt->pkt_state = FC_PKT_SUCCESS;
613	fpkt->pkt_reason = 0;
614
615	/*
616	 * First we zero the first 12 byte of dest
617	 */
618	bzero(xch->xch_fpkt->pkt_resp, 12);
619	i_fcp_status = BE_IN32(frm->frm_payload + 8);
620	if (i_fcp_status != 0) {
621		fcoei_fill_fcp_resp(frm->frm_payload,
622		    (uint8_t *)xch->xch_fpkt->pkt_resp, actual_size);
623	}
624
625	/*
626	 * Update pkt_resp_resid
627	 */
628	fpkt->pkt_data_resid = xch->xch_resid;
629	if ((xch->xch_resid != 0) && ((xch->xch_resid % 0x200) == 0) &&
630	    ((xch->xch_fpkt->pkt_datalen % 0x200) == 0) &&
631	    (i_fcp_status == 0)) {
632		FCOEI_LOG(__FUNCTION__, "frame lost no pause ? %x/%x",
633		    xch->xch_resid, xch->xch_fpkt->pkt_datalen);
634		fpkt->pkt_state = FC_PKT_LOCAL_RJT;
635		fpkt->pkt_reason = FC_REASON_UNDERRUN;
636	}
637
638	/*
639	 * Notify LV it's over
640	 */
641	if (fpkt->pkt_tran_flags & FC_TRAN_NO_INTR) {
642		FCOEI_LOG(__FUNCTION__, "BEFORE WAKEUP: %p-%p", fpkt, xch);
643		sema_v(&xch->xch_sema);
644		FCOEI_LOG(__FUNCTION__, "AFTERE WAKEUP: %p-%p", fpkt, xch);
645	} else {
646		xch->xch_fpkt->pkt_comp(xch->xch_fpkt);
647	}
648}
649
650/*
651 * fcoei_process_sol_els_rsp
652 *	ELS response is received
653 *
654 * Input:
655 *	frm = ELS response frame
656 *
657 * Returns:
658 *	N/A
659 *
660 * Comments:
661 *	N/A
662 */
663static void
664fcoei_process_sol_els_rsp(fcoe_frame_t *frm)
665{
666	uint16_t		 sol_oxid    = 0;
667	uint32_t		 actual_size = 0;
668	fcoei_exchange_t	*xch;
669	fc_packet_t		*fpkt;
670
671	/*
672	 * Look for the related exchange
673	 */
674	xch = NULL;
675	fpkt = NULL;
676	sol_oxid = FRM_OXID(frm);
677	if (mod_hash_find(FRM2SS(frm)->ss_sol_oxid_hash,
678	    FMHK(sol_oxid), (mod_hash_val_t *)&xch) != 0) {
679		PRT_FRM_HDR(__FUNCTION__, frm);
680		FCOEI_LOG(__FUNCTION__, "can't find the "
681		    "corresponding xch: oxid/%x", sol_oxid);
682		return;
683	}
684
685	xch->xch_rxid = FRM_RXID(frm);
686	fpkt = xch->xch_fpkt;
687
688	/*
689	 * Decide the actual response length
690	 */
691	actual_size = frm->frm_payload_size;
692	if (actual_size > fpkt->pkt_rsplen) {
693		FCOEI_LOG(__FUNCTION__, "pkt_rsplen is smaller"
694		    "0x(%x - %x)", actual_size, fpkt->pkt_rsplen);
695		actual_size = fpkt->pkt_rsplen;
696	}
697
698	/*
699	 * Upate fpkt related elements
700	 */
701	FCOEI_FRM2FHDR(frm, &fpkt->pkt_resp_fhdr);
702	fcoei_fill_els_fpkt_resp(frm, xch, actual_size);
703
704	/*
705	 * we should set pkt_reason and pkt_state carefully now
706	 * Need to analyze pkt_reason according to the response.
707	 * Leave it untouched now.
708	 */
709	if (((ls_code_t *)(void *)xch->xch_fpkt->pkt_resp)->ls_code ==
710	    LA_ELS_RJT) {
711		fcoei_complete_xch(xch, NULL, FC_PKT_FABRIC_RJT,
712		    FC_REASON_INVALID_PARAM);
713	} else {
714		fcoei_complete_xch(xch, NULL, FC_PKT_SUCCESS, 0);
715	}
716}
717
718/*
719 * fcoei_process_sol_ct_rsp
720 *	CT response is received
721 *
722 * Input:
723 *	frm = CT response frame
724 *
725 * Returns:
726 *	N/A
727 *
728 * Comments:
729 *	N/A
730 */
731static void
732fcoei_process_sol_ct_rsp(fcoe_frame_t *frm)
733{
734	uint16_t		 sol_oxid    = 0;
735	uint32_t		 actual_size = 0;
736	fcoei_exchange_t	*xch;
737	fc_packet_t		*fpkt;
738
739	/*
740	 * Look for the related exchange
741	 */
742	xch = NULL;
743	fpkt = NULL;
744	sol_oxid = FRM_OXID(frm);
745	if (mod_hash_find(FRM2SS(frm)->ss_sol_oxid_hash,
746	    FMHK(sol_oxid), (mod_hash_val_t *)&xch) != 0) {
747		FCOEI_LOG(__FUNCTION__, "can't find the "
748		    "corresponding xch: oxid/%x", sol_oxid);
749		return;
750	}
751
752	xch->xch_rxid = FRM_RXID(frm);
753	fpkt = xch->xch_fpkt;
754
755	/*
756	 * Decide the actual response length
757	 */
758	actual_size = fpkt->pkt_rsplen;
759	if (actual_size > frm->frm_payload_size) {
760		FCOEI_LOG(__FUNCTION__, "payload is smaller"
761		    "0x(%x - %x)", actual_size, frm->frm_payload_size);
762		actual_size = frm->frm_payload_size;
763	}
764
765	/*
766	 * Update fpkt related elements
767	 * Caution: we needn't do byte swapping for CT response
768	 */
769	FCOEI_FRM2FHDR(frm, &fpkt->pkt_resp_fhdr);
770	bcopy(FPLD, (uint8_t *)xch->xch_fpkt->pkt_resp, actual_size);
771
772	/*
773	 * Complete it with frm as NULL
774	 */
775	fcoei_complete_xch(xch, NULL, FC_PKT_SUCCESS, 0);
776}
777
778/*
779 * fcoei_process_sol_abts_acc
780 *	ABTS accpet is received
781 *
782 * Input:
783 *	frm = ABTS accept frame
784 *
785 * Returns:
786 *	N/A
787 *
788 * Comments:
789 *	We will always finish the abortion of solicited exchanges,
790 *	so we will not depend on the response from the remote side.
791 *	We just log one message.
792 */
793static void
794fcoei_process_sol_abts_acc(fcoe_frame_t *frm)
795{
796	FCOEI_LOG(__FUNCTION__, "the remote side has agreed to "
797	    "abort the exchange: oxid-%x, rxid-%x",
798	    FCOE_B2V_2(frm->frm_payload + 4),
799	    FCOE_B2V_2(frm->frm_payload + 6));
800}
801
802/*
803 * fcoei_process_sol_abts_rjt
804 *	ABTS reject is received
805 *
806 * Input:
807 *	frm = ABTS reject frame
808 *
809 * Returns:
810 *	N/A
811 *
812 * Comments:
813 *	We will alwayas finish the abortion of solicited exchanges,
814 *	so we will not depend on the response from the remote side.
815 *	We just log one message.
816 */
817static void
818fcoei_process_sol_abts_rjt(fcoe_frame_t *frm)
819{
820	FCOEI_LOG(__FUNCTION__, "the remote side rejected "
821	    "our request to abort one exchange.: %p", frm);
822}
823
824/*
825 * fcoei_fill_els_fpkt_resp
826 *	Fill fpkt ELS response in host format according frm payload
827 *
828 * Input:
829 *	src = frm payload in link format
830 *	dest = fpkt ELS response in host format
831 *	size = Maximum conversion size
832 *	els_op = ELS opcode
833 *
834 * Returns:
835 *	N/A
836 *
837 * Comments:
838 *	fpkt->pkt_resp must be mapped to one data structure, and it's
839 *	different from the content in the raw frame
840 */
841static void
842fcoei_fill_els_fpkt_resp(fcoe_frame_t *frm, fcoei_exchange_t *xch, int size)
843{
844	uint8_t			*src	   = frm->frm_payload;
845	uint8_t			*dest	   = (uint8_t *)xch->xch_fpkt->pkt_resp;
846	ls_code_t		*els_code  = (ls_code_t *)(void *)dest;
847	la_els_logi_t		*els_logi  = (la_els_logi_t *)(void *)dest;
848	la_els_adisc_t		*els_adisc = (la_els_adisc_t *)(void *)dest;
849	la_els_rls_acc_t	*els_rls;
850	la_els_rnid_acc_t	*els_rnid;
851	struct fcp_prli_acc	*prli_acc;
852	int			 offset;
853
854	els_code->ls_code = FCOE_B2V_1(src);
855	if (els_code->ls_code == LA_ELS_RJT) {
856		FCOEI_LOG(__FUNCTION__, "size :%d", size);
857		return;
858	}
859
860	switch (((ls_code_t *)(void *)xch->xch_fpkt->pkt_cmd)->ls_code) {
861	case LA_ELS_FLOGI:
862		bcopy((char *)frm->frm_hdr - 22,
863		    frm->frm_eport->eport_efh_dst, ETHERADDRL);
864		if (frm->frm_payload[8] & 0x10) {
865			/*
866			 * We are in fabric p2p mode
867			 */
868			uint8_t src_addr[ETHERADDRL];
869			frm->frm_eport->eport_flags &=
870			    ~EPORT_FLAG_IS_DIRECT_P2P;
871			FCOE_SET_DEFAULT_OUI(src_addr);
872			bcopy(frm->frm_hdr->hdr_d_id, src_addr + 3, 3);
873			frm->frm_eport->eport_set_mac_address(
874			    frm->frm_eport, src_addr, 1);
875		} else {
876			/*
877			 * We are in direct p2p mode
878			 */
879			frm->frm_eport->eport_flags |=
880			    EPORT_FLAG_IS_DIRECT_P2P;
881		}
882
883		if (!(FRM2SS(frm)->ss_eport->eport_flags &
884		    EPORT_FLAG_IS_DIRECT_P2P)) {
885			FRM2SS(frm)->ss_p2p_info.fca_d_id = FRM_D_ID(frm);
886		}
887
888		/* FALLTHROUGH */
889
890	case LA_ELS_PLOGI:
891		if (FRM2SS(frm)->ss_eport->eport_flags &
892		    EPORT_FLAG_IS_DIRECT_P2P) {
893			FRM2SS(frm)->ss_p2p_info.fca_d_id = FRM_D_ID(frm);
894			FRM2SS(frm)->ss_p2p_info.d_id = FRM_S_ID(frm);
895		}
896
897		offset = offsetof(la_els_logi_t, common_service);
898		els_logi->common_service.fcph_version = FCOE_B2V_2(src +
899		    offset);
900		offset += 2;
901		els_logi->common_service.btob_credit = FCOE_B2V_2(src +
902		    offset);
903		offset += 2;
904		els_logi->common_service.cmn_features = FCOE_B2V_2(src +
905		    offset);
906		offset += 2;
907		els_logi->common_service.rx_bufsize = FCOE_B2V_2(src +
908		    offset);
909		offset += 2;
910		els_logi->common_service.conc_sequences = FCOE_B2V_2(src +
911		    offset);
912		offset += 2;
913		els_logi->common_service.relative_offset = FCOE_B2V_2(src +
914		    offset);
915		offset += 2;
916		els_logi->common_service.e_d_tov = FCOE_B2V_4(src +
917		    offset);
918
919		/*
920		 * port/node WWN
921		 */
922		offset = offsetof(la_els_logi_t, nport_ww_name);
923		bcopy(src + offset, &els_logi->nport_ww_name, 8);
924		offset = offsetof(la_els_logi_t, node_ww_name);
925		bcopy(src + offset, &els_logi->node_ww_name, 8);
926
927		/*
928		 * class_3
929		 */
930		offset = offsetof(la_els_logi_t, class_3);
931		els_logi->class_3.class_opt = FCOE_B2V_2(src + offset);
932		offset += 2;
933		els_logi->class_3.initiator_ctl = FCOE_B2V_2(src + offset);
934		offset += 2;
935		els_logi->class_3.recipient_ctl = FCOE_B2V_2(src + offset);
936		offset += 2;
937		els_logi->class_3.rcv_size = FCOE_B2V_2(src + offset);
938		offset += 2;
939		els_logi->class_3.conc_sequences = FCOE_B2V_2(src + offset);
940		offset += 2;
941		els_logi->class_3.n_port_e_to_e_credit = FCOE_B2V_2(src +
942		    offset);
943		offset += 2;
944		els_logi->class_3.open_seq_per_xchng = FCOE_B2V_2(src + offset);
945
946		break;
947
948	case LA_ELS_PRLI:
949		/*
950		 * PRLI service parameter response page
951		 *
952		 * fcp_prli_acc doesn't include ls_code, don't use offsetof
953		 */
954		offset = 4;
955		prli_acc = (struct fcp_prli_acc *)(void *)(dest + offset);
956		prli_acc->type = FCOE_B2V_1(src + offset);
957		/*
958		 * Type code extension
959		 */
960		offset += 1;
961		/*
962		 * PRLI response flags
963		 */
964		offset += 1;
965		prli_acc->orig_process_assoc_valid =
966		    (FCOE_B2V_2(src + offset) & BIT_15) ? 1 : 0;
967		prli_acc->resp_process_assoc_valid =
968		    (FCOE_B2V_2(src + offset) & BIT_14) ? 1 : 0;
969		prli_acc->image_pair_established =
970		    (FCOE_B2V_2(src + offset) & BIT_13) ? 1 : 0;
971		prli_acc->accept_response_code =
972		    FCOE_B2V_2(src + offset) & 0x0F00;
973		/*
974		 * process associator
975		 */
976		offset += 2;
977		prli_acc->orig_process_associator = FCOE_B2V_4(src + offset);
978		offset += 4;
979		prli_acc->resp_process_associator = FCOE_B2V_4(src + offset);
980		/*
981		 * FC-4 type
982		 */
983		offset += 4;
984		prli_acc->initiator_fn =
985		    (FCOE_B2V_4(src + offset) & BIT_5) ? 1 : 0;
986		prli_acc->target_fn =
987		    (FCOE_B2V_4(src + offset) & BIT_4) ? 1 : 0;
988		prli_acc->cmd_data_mixed =
989		    (FCOE_B2V_4(src + offset) & BIT_3) ? 1 : 0;
990		prli_acc->data_resp_mixed =
991		    (FCOE_B2V_4(src + offset) & BIT_2) ? 1 : 0;
992		prli_acc->read_xfer_rdy_disabled =
993		    (FCOE_B2V_4(src + offset) & BIT_1) ? 1 : 0;
994		prli_acc->write_xfer_rdy_disabled =
995		    (FCOE_B2V_4(src + offset) & BIT_0) ? 1 : 0;
996
997		break;
998
999	case LA_ELS_LOGO:
1000		/*
1001		 * could only be LS_ACC, no additional information
1002		 */
1003		els_code->ls_code = FCOE_B2V_1(src);
1004		break;
1005
1006	case LA_ELS_SCR:
1007		/*
1008		 * LS_ACC/LS_RJT, no additional information
1009		 */
1010		els_code->ls_code = FCOE_B2V_1(src);
1011		break;
1012
1013	case LA_ELS_ADISC:
1014		offset = 5;
1015		els_adisc->hard_addr.hard_addr = FCOE_B2V_3(src + offset);
1016		offset = offsetof(la_els_adisc_t, port_wwn);
1017		bcopy(src + offset, &els_adisc->port_wwn, 8);
1018		offset = offsetof(la_els_adisc_t, node_wwn);
1019		bcopy(src + offset, &els_adisc->node_wwn, 8);
1020		offset += 9;
1021		els_adisc->nport_id.port_id = FCOE_B2V_3(src + offset);
1022		break;
1023	case LA_ELS_RLS:
1024		els_rls = (la_els_rls_acc_t *)(void *)dest;
1025		els_rls->ls_code.ls_code = FCOE_B2V_1(src);
1026		offset = 4;
1027		els_rls->rls_link_params.rls_link_fail =
1028		    FCOE_B2V_4(src + offset);
1029		offset = 8;
1030		els_rls->rls_link_params.rls_sync_loss =
1031		    FCOE_B2V_4(src + offset);
1032		offset = 12;
1033		els_rls->rls_link_params.rls_sig_loss =
1034		    FCOE_B2V_4(src + offset);
1035		offset = 16;
1036		els_rls->rls_link_params.rls_prim_seq_err =
1037		    FCOE_B2V_4(src + offset);
1038		offset = 20;
1039		els_rls->rls_link_params.rls_invalid_word =
1040		    FCOE_B2V_4(src + offset);
1041		offset = 24;
1042		els_rls->rls_link_params.rls_invalid_crc =
1043		    FCOE_B2V_4(src + offset);
1044		break;
1045	case LA_ELS_RNID:
1046		els_rnid = (la_els_rnid_acc_t *)(void *)dest;
1047		els_rnid->ls_code.ls_code = FCOE_B2V_1(src);
1048		offset = 4;
1049		bcopy(src + offset, &els_rnid->hdr.data_format, 1);
1050		offset = 5;
1051		bcopy(src + offset, &els_rnid->hdr.cmn_len, 1);
1052		offset = 7;
1053		bcopy(src + offset, &els_rnid->hdr.specific_len, 1);
1054		offset = 8;
1055		bcopy(src + offset, els_rnid->data, FCIO_RNID_MAX_DATA_LEN);
1056		break;
1057	default:
1058		FCOEI_LOG(__FUNCTION__, "unsupported R_CTL");
1059		break;
1060	}
1061}
1062
1063/*
1064 * fcoei_fill_fcp_resp
1065 *	Fill fpkt FCP response in host format according to frm payload
1066 *
1067 * Input:
1068 *	src - frm payload in link format
1069 *	dest - fpkt FCP response in host format
1070 *	size - Maximum conversion size
1071 *
1072 * Returns:
1073 *	N/A
1074 *
1075 * Comments:
1076 *	This is called only for SCSI response with non good status
1077 */
1078static void
1079fcoei_fill_fcp_resp(uint8_t *src, uint8_t *dest, int size)
1080{
1081	fcp_rsp_t	*fcp_rsp_iu = (fcp_rsp_t *)(void *)dest;
1082	int		 offset;
1083
1084	/*
1085	 * set fcp_status
1086	 */
1087	offset = offsetof(fcp_rsp_t, fcp_u);
1088	offset += 2;
1089	fcp_rsp_iu->fcp_u.fcp_status.resid_under =
1090	    (FCOE_B2V_1(src + offset) & BIT_3) ? 1 : 0;
1091	fcp_rsp_iu->fcp_u.fcp_status.resid_over =
1092	    (FCOE_B2V_1(src + offset) & BIT_2) ? 1 : 0;
1093	fcp_rsp_iu->fcp_u.fcp_status.sense_len_set =
1094	    (FCOE_B2V_1(src + offset) & BIT_1) ? 1 : 0;
1095	fcp_rsp_iu->fcp_u.fcp_status.rsp_len_set =
1096	    (FCOE_B2V_1(src + offset) & BIT_0) ? 1 : 0;
1097	offset += 1;
1098	fcp_rsp_iu->fcp_u.fcp_status.scsi_status = FCOE_B2V_1(src + offset);
1099	/*
1100	 * fcp_resid/fcp_sense_len/fcp_response_len
1101	 */
1102	offset = offsetof(fcp_rsp_t, fcp_resid);
1103	fcp_rsp_iu->fcp_resid = FCOE_B2V_4(src + offset);
1104	offset = offsetof(fcp_rsp_t, fcp_sense_len);
1105	fcp_rsp_iu->fcp_sense_len = FCOE_B2V_4(src + offset);
1106	offset = offsetof(fcp_rsp_t, fcp_response_len);
1107	fcp_rsp_iu->fcp_response_len = FCOE_B2V_4(src + offset);
1108	/*
1109	 * sense or response
1110	 */
1111	offset += 4;
1112	if (fcp_rsp_iu->fcp_sense_len) {
1113		if ((offset + fcp_rsp_iu->fcp_sense_len) > size) {
1114			FCOEI_LOG(__FUNCTION__, "buffer too small - sens");
1115			return;
1116		}
1117		bcopy(src + offset, dest + offset, fcp_rsp_iu->fcp_sense_len);
1118		offset += fcp_rsp_iu->fcp_sense_len;
1119	}
1120
1121	if (fcp_rsp_iu->fcp_response_len) {
1122		if ((offset + fcp_rsp_iu->fcp_response_len) > size) {
1123			FCOEI_LOG(__FUNCTION__, "buffer too small - resp");
1124			return;
1125		}
1126		bcopy(src + offset, dest + offset,
1127		    fcp_rsp_iu->fcp_response_len);
1128	}
1129}
1130
1131void
1132fcoei_init_ect_vectors(fcoe_client_t *ect)
1133{
1134	ect->ect_rx_frame	   = fcoei_rx_frame;
1135	ect->ect_port_event	   = fcoei_port_event;
1136	ect->ect_release_sol_frame = fcoei_release_sol_frame;
1137}
1138
1139/*
1140 * fcoei_process_unsol_frame
1141 *	Unsolicited frame is received
1142 *
1143 * Input:
1144 *	frame = unsolicited frame that is received
1145 *
1146 * Returns:
1147 *	N/A
1148 *
1149 * Comments:
1150 *	watchdog will call this to process unsolicited frames that we
1151 *	just received fcoei_process_xx is used to handle different
1152 *	unsolicited frames
1153 */
1154void
1155fcoei_process_unsol_frame(fcoe_frame_t *frm)
1156{
1157	fcoei_exchange_t	*xch;
1158	uint16_t		 sol_oxid;
1159
1160	switch (FRM_R_CTL(frm)) {
1161	case R_CTL_SOLICITED_DATA:
1162		/*
1163		 * READ data phase frame
1164		 * Find the associated exchange
1165		 */
1166		sol_oxid = FRM_OXID(frm);
1167		if (mod_hash_find(FRM2SS(frm)->ss_sol_oxid_hash,
1168		    FMHK(sol_oxid), (mod_hash_val_t *)&xch) != 0) {
1169			PRT_FRM_HDR(__FUNCTION__, frm);
1170			FCOEI_LOG(__FUNCTION__, "associated xch not found: "
1171			    "oxid/%x %lu - %lu", sol_oxid,
1172			    CURRENT_CLOCK, frm->frm_clock);
1173			break;
1174		}
1175
1176		/*
1177		 * Copy data into fpkt data buffer, and update the counter
1178		 */
1179		bcopy(frm->frm_payload, (uint8_t *)xch->xch_fpkt->pkt_data +
1180		    FRM_PARAM(frm), frm->frm_payload_size);
1181		xch->xch_resid -= frm->frm_payload_size;
1182		xch->xch_rxid = FRM_RXID(frm);
1183		break;
1184
1185	case R_CTL_XFER_RDY:
1186		fcoei_process_unsol_xfer_rdy(frm);
1187		break;
1188
1189	case R_CTL_STATUS:
1190		fcoei_process_sol_fcp_resp(frm);
1191		break;
1192
1193	case R_CTL_ELS_REQ:
1194		fcoei_process_unsol_els_req(frm);
1195		break;
1196
1197	case R_CTL_LS_ABTS:
1198		fcoei_process_unsol_abts_req(frm);
1199		break;
1200
1201	case R_CTL_ELS_RSP:
1202		fcoei_process_sol_els_rsp(frm);
1203		break;
1204
1205	case R_CTL_SOLICITED_CONTROL:
1206		fcoei_process_sol_ct_rsp(frm);
1207		break;
1208
1209	case R_CTL_LS_BA_ACC:
1210		fcoei_process_sol_abts_acc(frm);
1211		break;
1212
1213	case R_CTL_LS_BA_RJT:
1214		fcoei_process_sol_abts_rjt(frm);
1215		break;
1216
1217	default:
1218		/*
1219		 * Unsupported frame
1220		 */
1221		PRT_FRM_HDR("Unsupported unsol frame: ", frm);
1222	}
1223
1224	/*
1225	 * Release the frame and netb
1226	 */
1227	frm->frm_eport->eport_free_netb(frm->frm_netb);
1228	frm->frm_eport->eport_release_frame(frm);
1229}
1230
1231/*
1232 * fcoei_handle_sol_frame_done
1233 *	solicited frame is just sent out
1234 *
1235 * Input:
1236 *	frame = solicited frame that has been sent out
1237 *
1238 * Returns:
1239 *	N/A
1240 *
1241 * Comments:
1242 *	watchdog will call this to handle solicited frames that FCOEI
1243 *	has sent out Non-request frame post handling
1244 */
1245void
1246fcoei_handle_sol_frame_done(fcoe_frame_t *frm)
1247{
1248	/*
1249	 * the corresponding xch could be NULL at this time
1250	 */
1251	fcoei_exchange_t	*xch  = FRM2IFM(frm)->ifm_xch;
1252
1253	switch (FRM2IFM(frm)->ifm_rctl) {
1254	case R_CTL_ELS_RSP:
1255		/*
1256		 * Complete it with frm as NULL
1257		 */
1258		fcoei_complete_xch(xch, NULL, FC_PKT_SUCCESS, 0);
1259		break;
1260
1261	case R_CTL_LS_BA_ACC:
1262		FCOEI_LOG(__FUNCTION__,  "BA_ACC out: xch-%p, frm-%p",
1263		    xch, frm);
1264		PRT_FRM_HDR("LS_BA_ACC", frm);
1265		break;
1266
1267	case R_CTL_LS_BA_RJT:
1268		FCOEI_LOG(__FUNCTION__,  "BA_RJT out: xch-%p, frm-%p",
1269		    xch, frm);
1270		PRT_FRM_HDR("LS_BA_RJT", frm);
1271		break;
1272
1273	default:
1274		/*
1275		 * Unsupported frame
1276		 */
1277		PRT_FRM_HDR("Unsupported sol frame: ", frm);
1278	}
1279
1280	/*
1281	 * We should release only the frame, and we don't care its netb
1282	 */
1283	FRM2SS(frm)->ss_eport->eport_release_frame(frm);
1284}
1285
1286/*
1287 * fcoei_port_event
1288 *	link/port state changed
1289 *
1290 * Input:
1291 *	eport = to indicate which port has changed
1292 *	event = what change
1293 *
1294 * Returns:
1295 *	N/A
1296 *
1297 * Comments:
1298 *	refer fctl.h for ss_link_state value
1299 */
1300void
1301fcoei_port_event(fcoe_port_t *eport, uint32_t event)
1302{
1303	fcoei_event_t	*ae;
1304
1305	if (!(EPORT2SS(eport)->ss_flags & SS_FLAG_LV_BOUND)) {
1306		FCOEI_LOG(__FUNCTION__, "not bound now");
1307		return;
1308	}
1309
1310	mutex_enter(&EPORT2SS(eport)->ss_watchdog_mutex);
1311	switch (event) {
1312	case FCOE_NOTIFY_EPORT_LINK_DOWN:
1313		EPORT2SS(eport)->ss_link_state = FC_STATE_OFFLINE;
1314		cmn_err(CE_NOTE, "%02x%02x%02x%02x%02x%02x%02x%02x Link down",
1315		    eport->eport_portwwn[0], eport->eport_portwwn[1],
1316		    eport->eport_portwwn[2], eport->eport_portwwn[3],
1317		    eport->eport_portwwn[4], eport->eport_portwwn[5],
1318		    eport->eport_portwwn[6], eport->eport_portwwn[7]);
1319		break;
1320
1321	case FCOE_NOTIFY_EPORT_LINK_UP:
1322		if (eport->eport_mtu >= 2200) {
1323			EPORT2SS(eport)->ss_fcp_data_payload_size =
1324			    FCOE_DEFAULT_FCP_DATA_PAYLOAD_SIZE;
1325		} else {
1326			FCOEI_LOG(__FUNCTION__, "fcoei: MTU is not big enough. "
1327			    "we will use 1K frames in FCP data phase.");
1328			EPORT2SS(eport)->ss_fcp_data_payload_size =
1329			    FCOE_MIN_FCP_DATA_PAYLOAD_SIZE;
1330		}
1331
1332		cmn_err(CE_NOTE, "%02x%02x%02x%02x%02x%02x%02x%02x Link up",
1333		    eport->eport_portwwn[0], eport->eport_portwwn[1],
1334		    eport->eport_portwwn[2], eport->eport_portwwn[3],
1335		    eport->eport_portwwn[4], eport->eport_portwwn[5],
1336		    eport->eport_portwwn[6], eport->eport_portwwn[7]);
1337		EPORT2SS(eport)->ss_link_state = FC_STATE_ONLINE;
1338		break;
1339
1340	default:
1341		FCOEI_LOG(__FUNCTION__, "unsupported event");
1342		mutex_exit(&EPORT2SS(eport)->ss_watchdog_mutex);
1343
1344		return;
1345	}
1346
1347	EPORT2SS(eport)->ss_port_event_counter++;
1348	ae = (fcoei_event_t *)kmem_zalloc(sizeof (fcoei_event_t), KM_SLEEP);
1349	ae->ae_type = AE_EVENT_PORT;
1350	ae->ae_obj = EPORT2SS(eport);
1351	ae->ae_specific = EPORT2SS(eport)->ss_link_state;
1352	list_insert_tail(&EPORT2SS(eport)->ss_event_list, ae);
1353	mutex_exit(&EPORT2SS(eport)->ss_watchdog_mutex);
1354}
1355
1356/*
1357 * fcoei_process_event_port
1358 *	link/port state changed
1359 *
1360 * Input:
1361 *	ae = link fcoei_event
1362 *
1363 * Returns:
1364 *	N/A
1365 *
1366 * Comments:
1367 *	asynchronous events from FCOE
1368 */
1369void
1370fcoei_process_event_port(fcoei_event_t *ae)
1371{
1372	fcoei_soft_state_t	*ss = (fcoei_soft_state_t *)ae->ae_obj;
1373
1374	if (ss->ss_eport->eport_link_speed == FCOE_PORT_SPEED_1G) {
1375		ae->ae_specific |= FC_STATE_1GBIT_SPEED;
1376	} else if (ss->ss_eport->eport_link_speed ==
1377	    FCOE_PORT_SPEED_10G) {
1378		ae->ae_specific |= FC_STATE_10GBIT_SPEED;
1379	}
1380
1381	if (ss->ss_flags & SS_FLAG_LV_BOUND) {
1382		ss->ss_bind_info.port_statec_cb(ss->ss_port,
1383		    (uint32_t)ae->ae_specific);
1384	} else {
1385		FCOEI_LOG(__FUNCTION__, "ss %p not bound now", ss);
1386	}
1387
1388	atomic_add_32(&ss->ss_port_event_counter, -1);
1389	kmem_free(ae, sizeof (fcoei_event_t));
1390}
1391