1139749Simp/*-
2196008Smjacob *  Copyright (c) 1997-2009 by Matthew Jacob
3167403Smjacob *  All rights reserved.
4196008Smjacob *
5167403Smjacob *  Redistribution and use in source and binary forms, with or without
6167403Smjacob *  modification, are permitted provided that the following conditions
7167403Smjacob *  are met:
8196008Smjacob *
9167403Smjacob *  1. Redistributions of source code must retain the above copyright
10167403Smjacob *     notice, this list of conditions and the following disclaimer.
11167403Smjacob *  2. Redistributions in binary form must reproduce the above copyright
12167403Smjacob *     notice, this list of conditions and the following disclaimer in the
13167403Smjacob *     documentation and/or other materials provided with the distribution.
14196008Smjacob *
15167403Smjacob *  THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16167403Smjacob *  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17167403Smjacob *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18167403Smjacob *  ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
19167403Smjacob *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20167403Smjacob *  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21167403Smjacob *  OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22167403Smjacob *  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23167403Smjacob *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24167403Smjacob *  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25167403Smjacob *  SUCH DAMAGE.
26196008Smjacob *
27167403Smjacob */
28167403Smjacob/*
2955373Smjacob * Machine and OS Independent Target Mode Code for the Qlogic SCSI/FC adapters.
3055373Smjacob */
3155373Smjacob/*
3290224Smjacob * Bug fixes gratefully acknowledged from:
3390224Smjacob *	Oded Kedem <oded@kashya.com>
3490224Smjacob */
3590224Smjacob/*
3655373Smjacob * Include header file appropriate for platform we're building on.
3755373Smjacob */
3855373Smjacob
3955373Smjacob#ifdef	__NetBSD__
4055373Smjacob#include <dev/ic/isp_netbsd.h>
4155373Smjacob#endif
4255373Smjacob#ifdef	__FreeBSD__
43160410Smjacob#include <sys/cdefs.h>
44160410Smjacob__FBSDID("$FreeBSD: stable/11/sys/dev/isp/isp_target.c 316168 2017-03-29 16:16:39Z mav $");
4555373Smjacob#include <dev/isp/isp_freebsd.h>
4655373Smjacob#endif
4755373Smjacob#ifdef	__OpenBSD__
4855373Smjacob#include <dev/ic/isp_openbsd.h>
4955373Smjacob#endif
5055373Smjacob#ifdef	__linux__
5155373Smjacob#include "isp_linux.h"
5255373Smjacob#endif
5355373Smjacob
5455373Smjacob#ifdef	ISP_TARGET_MODE
55291013Smavstatic const char atiocope[] = "ATIO returned for LUN %x because it was in the middle of Bus Device Reset on bus %d";
56291013Smavstatic const char atior[] = "ATIO returned for LUN %x from handle 0x%x because a Bus Reset occurred on bus %d";
57196008Smjacobstatic const char rqo[] = "%s: Request Queue Overflow";
5855373Smjacob
59157943Smjacobstatic void isp_got_msg_fc(ispsoftc_t *, in_fcentry_t *);
60163899Smjacobstatic void isp_got_tmf_24xx(ispsoftc_t *, at7_entry_t *);
61316158Smavstatic void isp_handle_abts(ispsoftc_t *, abts_t *);
62157943Smjacobstatic void isp_handle_atio2(ispsoftc_t *, at2_entry_t *);
63157943Smjacobstatic void isp_handle_ctio2(ispsoftc_t *, ct2_entry_t *);
64163899Smjacobstatic void isp_handle_ctio7(ispsoftc_t *, ct7_entry_t *);
65316161Smavstatic void isp_handle_notify(ispsoftc_t *, in_fcentry_t *);
66316161Smavstatic void isp_handle_notify_24xx(ispsoftc_t *, in_fcentry_24xx_t *);
6755373Smjacob
6855373Smjacob/*
6955373Smjacob * The Qlogic driver gets an interrupt to look at response queue entries.
7055373Smjacob * Some of these are status completions for initiatior mode commands, but
7155373Smjacob * if target mode is enabled, we get a whole wad of response queue entries
7255373Smjacob * to be handled here.
7355373Smjacob *
7455373Smjacob * Basically the split into 3 main groups: Lun Enable/Modification responses,
7555373Smjacob * SCSI Command processing, and Immediate Notification events.
7655373Smjacob *
7755373Smjacob * You start by writing a request queue entry to enable target mode (and
7855373Smjacob * establish some resource limitations which you can modify later).
7955373Smjacob * The f/w responds with a LUN ENABLE or LUN MODIFY response with
8055373Smjacob * the status of this action. If the enable was successful, you can expect...
8155373Smjacob *
8255373Smjacob * Response queue entries with SCSI commands encapsulate show up in an ATIO
8355373Smjacob * (Accept Target IO) type- sometimes with enough info to stop the command at
8455373Smjacob * this level. Ultimately the driver has to feed back to the f/w's request
8555373Smjacob * queue a sequence of CTIOs (continue target I/O) that describe data to
8655373Smjacob * be moved and/or status to be sent) and finally finishing with sending
8755373Smjacob * to the f/w's response queue an ATIO which then completes the handshake
8855373Smjacob * with the f/w for that command. There's a lot of variations on this theme,
8955373Smjacob * including flags you can set in the CTIO for the Qlogic 2X00 fibre channel
9055373Smjacob * cards that 'auto-replenish' the f/w's ATIO count, but this is the basic
9155373Smjacob * gist of it.
9255373Smjacob *
9355373Smjacob * The third group that can show up in the response queue are Immediate
9455373Smjacob * Notification events. These include things like notifications of SCSI bus
9555373Smjacob * resets, or Bus Device Reset messages or other messages received. This
9672082Sasmodai * a classic oddbins area. It can get  a little weird because you then turn
9755373Smjacob * around and acknowledge the Immediate Notify by writing an entry onto the
9855373Smjacob * request queue and then the f/w turns around and gives you an acknowledgement
9955373Smjacob * to *your* acknowledgement on the response queue (the idea being to let
10055373Smjacob * the f/w tell you when the event is *really* over I guess).
10155373Smjacob *
10255373Smjacob */
10355373Smjacob
10455373Smjacob
10555373Smjacob/*
10655373Smjacob * A new response queue entry has arrived. The interrupt service code
10755373Smjacob * has already swizzled it into the platform dependent from canonical form.
10855373Smjacob *
10955373Smjacob * Because of the way this driver is designed, unfortunately most of the
11055373Smjacob * actual synchronization work has to be done in the platform specific
11155373Smjacob * code- we have no synchroniation primitives in the common code.
11255373Smjacob */
11355373Smjacob
11455373Smjacobint
115163899Smjacobisp_target_notify(ispsoftc_t *isp, void *vptr, uint32_t *optrp)
11655373Smjacob{
11755373Smjacob	union {
11855373Smjacob		at2_entry_t	*at2iop;
119154704Smjacob		at2e_entry_t	*at2eiop;
120163899Smjacob		at7_entry_t	*at7iop;
12155373Smjacob		ct2_entry_t	*ct2iop;
122154704Smjacob		ct2e_entry_t	*ct2eiop;
123163899Smjacob		ct7_entry_t	*ct7iop;
12455373Smjacob		lun_entry_t	*lunenp;
12555373Smjacob		in_fcentry_t	*inot_fcp;
126154704Smjacob		in_fcentry_e_t	*inote_fcp;
127163899Smjacob		in_fcentry_24xx_t *inot_24xx;
12855373Smjacob		na_fcentry_t	*nack_fcp;
129154704Smjacob		na_fcentry_e_t	*nacke_fcp;
130163899Smjacob		na_fcentry_24xx_t *nack_24xx;
13155373Smjacob		isphdr_t	*hp;
132163899Smjacob		abts_t		*abts;
133163899Smjacob		abts_rsp_t	*abts_rsp;
134163899Smjacob		els_t		*els;
13555373Smjacob		void *		*vp;
13655373Smjacob#define	at2iop		unp.at2iop
137154704Smjacob#define	at2eiop		unp.at2eiop
138163899Smjacob#define	at7iop		unp.at7iop
13955373Smjacob#define	ct2iop		unp.ct2iop
140154704Smjacob#define	ct2eiop		unp.ct2eiop
141163899Smjacob#define	ct7iop		unp.ct7iop
14255373Smjacob#define	lunenp		unp.lunenp
14355373Smjacob#define	inot_fcp	unp.inot_fcp
144154704Smjacob#define	inote_fcp	unp.inote_fcp
145163899Smjacob#define	inot_24xx	unp.inot_24xx
14655373Smjacob#define	nack_fcp	unp.nack_fcp
147154704Smjacob#define	nacke_fcp	unp.nacke_fcp
148163899Smjacob#define	nack_24xx	unp.nack_24xx
149163899Smjacob#define	abts		unp.abts
150163899Smjacob#define	abts_rsp	unp.abts_rsp
151163899Smjacob#define els		unp.els
15255373Smjacob#define	hdrp		unp.hp
15355373Smjacob	} unp;
154155704Smjacob	uint8_t local[QENTRY_LEN];
155316162Smav	int type, len, level, rval = 1;
15655373Smjacob
15787635Smjacob	type = isp_get_response_type(isp, (isphdr_t *)vptr);
15855373Smjacob	unp.vp = vptr;
15955373Smjacob
16055373Smjacob	ISP_TDQE(isp, "isp_target_notify", (int) *optrp, vptr);
16155373Smjacob
162196008Smjacob	switch (type) {
16355373Smjacob	case RQSTYPE_ATIO:
164291188Smav		isp_get_atio7(isp, at7iop, (at7_entry_t *) local);
165291188Smav		at7iop = (at7_entry_t *) local;
166291188Smav		/*
167291188Smav		 * Check for and do something with commands whose
168291188Smav		 * IULEN extends past a single queue entry.
169291188Smav		 */
170299691Smav		len = at7iop->at_ta_len & 0x0fff;
171291188Smav		if (len > (QENTRY_LEN - 8)) {
172291188Smav			len -= (QENTRY_LEN - 8);
173291188Smav			isp_prt(isp, ISP_LOGINFO, "long IU length (%d) ignored", len);
174291188Smav			while (len > 0) {
175291188Smav				*optrp =  ISP_NXT_QENTRY(*optrp, RESULT_QUEUE_LEN(isp));
176291188Smav				len -= QENTRY_LEN;
177163899Smjacob			}
178163899Smjacob		}
179291188Smav		/*
180291188Smav		 * Check for a task management function
181291188Smav		 */
182291188Smav		if (at7iop->at_cmnd.fcp_cmnd_task_management) {
183291188Smav			isp_got_tmf_24xx(isp, at7iop);
184291188Smav			break;
185291188Smav		}
186291188Smav		/*
187291188Smav		 * Just go straight to outer layer for this one.
188291188Smav		 */
189291188Smav		isp_async(isp, ISPASYNC_TARGET_ACTION, local);
19055373Smjacob		break;
191163899Smjacob
19255373Smjacob	case RQSTYPE_ATIO2:
193196008Smjacob		if (ISP_CAP_2KLOGIN(isp)) {
194154704Smjacob			isp_get_atio2e(isp, at2eiop, (at2e_entry_t *) local);
195160251Smjacob		} else {
196154704Smjacob			isp_get_atio2(isp, at2iop, (at2_entry_t *) local);
197163899Smjacob		}
19887635Smjacob		isp_handle_atio2(isp, (at2_entry_t *) local);
19955373Smjacob		break;
200163899Smjacob
201125187Smjacob	case RQSTYPE_CTIO3:
20255373Smjacob	case RQSTYPE_CTIO2:
203196008Smjacob		if (ISP_CAP_2KLOGIN(isp)) {
204154704Smjacob			isp_get_ctio2e(isp, ct2eiop, (ct2e_entry_t *) local);
205160251Smjacob		} else {
206154704Smjacob			isp_get_ctio2(isp, ct2iop, (ct2_entry_t *) local);
207163899Smjacob		}
20887635Smjacob		isp_handle_ctio2(isp, (ct2_entry_t *) local);
20955373Smjacob		break;
210163899Smjacob
211163899Smjacob	case RQSTYPE_CTIO7:
212163899Smjacob		isp_get_ctio7(isp, ct7iop, (ct7_entry_t *) local);
213163899Smjacob		isp_handle_ctio7(isp, (ct7_entry_t *) local);
214163899Smjacob		break;
215163899Smjacob
21655373Smjacob	case RQSTYPE_NOTIFY:
217163899Smjacob		if (IS_24XX(isp)) {
218196008Smjacob			isp_get_notify_24xx(isp, inot_24xx, (in_fcentry_24xx_t *)local);
219316162Smav			isp_handle_notify_24xx(isp, (in_fcentry_24xx_t *)local);
220163899Smjacob			break;
22155373Smjacob		}
222316161Smav		if (ISP_CAP_2KLOGIN(isp))
223316161Smav			isp_get_notify_fc_e(isp, inote_fcp, (in_fcentry_e_t *)local);
224316161Smav		else
225316161Smav			isp_get_notify_fc(isp, inot_fcp, (in_fcentry_t *)local);
226316161Smav		isp_handle_notify(isp, (in_fcentry_t *)local);
22755373Smjacob		break;
22855373Smjacob
22955373Smjacob	case RQSTYPE_NOTIFY_ACK:
23055373Smjacob		/*
23155373Smjacob		 * The ISP is acknowledging our acknowledgement of an
23255373Smjacob		 * Immediate Notify entry for some asynchronous event.
23355373Smjacob		 */
234163899Smjacob		if (IS_24XX(isp)) {
235196008Smjacob			isp_get_notify_ack_24xx(isp, nack_24xx, (na_fcentry_24xx_t *) local);
236163899Smjacob			nack_24xx = (na_fcentry_24xx_t *) local;
237163899Smjacob			if (nack_24xx->na_status != NA_OK) {
238163899Smjacob				level = ISP_LOGINFO;
239163899Smjacob			} else {
240163899Smjacob				level = ISP_LOGTDEBUG1;
241163899Smjacob			}
242196008Smjacob			isp_prt(isp, level, "Notify Ack Status=0x%x; Subcode 0x%x seqid=0x%x", nack_24xx->na_status, nack_24xx->na_status_subcode, nack_24xx->na_rxid);
243291188Smav		} else {
244196008Smjacob			if (ISP_CAP_2KLOGIN(isp)) {
245196008Smjacob				isp_get_notify_ack_fc_e(isp, nacke_fcp, (na_fcentry_e_t *)local);
246160251Smjacob			} else {
247196008Smjacob				isp_get_notify_ack_fc(isp, nack_fcp, (na_fcentry_t *)local);
248163899Smjacob			}
24987635Smjacob			nack_fcp = (na_fcentry_t *)local;
250163899Smjacob			if (nack_fcp->na_status != NA_OK) {
251163899Smjacob				level = ISP_LOGINFO;
252163899Smjacob			} else {
253163899Smjacob				level = ISP_LOGTDEBUG1;
254163899Smjacob			}
255196008Smjacob			isp_prt(isp, level, "Notify Ack Status=0x%x seqid 0x%x", nack_fcp->na_status, nack_fcp->na_seqid);
25655373Smjacob		}
25755373Smjacob		break;
258163899Smjacob
259163899Smjacob	case RQSTYPE_ABTS_RCVD:
260163899Smjacob		isp_get_abts(isp, abts, (abts_t *)local);
261316158Smav		isp_handle_abts(isp, (abts_t *)local);
262163899Smjacob		break;
263163899Smjacob	case RQSTYPE_ABTS_RSP:
264163899Smjacob		isp_get_abts_rsp(isp, abts_rsp, (abts_rsp_t *)local);
265163899Smjacob		abts_rsp = (abts_rsp_t *) local;
266163899Smjacob		if (abts_rsp->abts_rsp_status) {
267163899Smjacob			level = ISP_LOGINFO;
268163899Smjacob		} else {
269163899Smjacob			level = ISP_LOGTDEBUG0;
270163899Smjacob		}
271196008Smjacob		isp_prt(isp, level, "ABTS RSP response[0x%x]: status=0x%x sub=(0x%x 0x%x)", abts_rsp->abts_rsp_rxid_task, abts_rsp->abts_rsp_status,
272196008Smjacob		    abts_rsp->abts_rsp_payload.rsp.subcode1, abts_rsp->abts_rsp_payload.rsp.subcode2);
273163899Smjacob		break;
27455373Smjacob	default:
275196008Smjacob		isp_prt(isp, ISP_LOGERR, "%s: unknown entry type 0x%x", __func__, type);
27698284Smjacob		rval = 0;
27755373Smjacob		break;
27855373Smjacob	}
27955373Smjacob#undef	atiop
28055373Smjacob#undef	at2iop
281154704Smjacob#undef	at2eiop
282163899Smjacob#undef	at7iop
28355373Smjacob#undef	ctiop
28455373Smjacob#undef	ct2iop
285154704Smjacob#undef	ct2eiop
286163899Smjacob#undef	ct7iop
28755373Smjacob#undef	lunenp
28855373Smjacob#undef	inotp
28955373Smjacob#undef	inot_fcp
290154704Smjacob#undef	inote_fcp
291163899Smjacob#undef	inot_24xx
29255373Smjacob#undef	nackp
29355373Smjacob#undef	nack_fcp
294154704Smjacob#undef	nacke_fcp
295163899Smjacob#undef	hack_24xx
296163899Smjacob#undef	abts
297163899Smjacob#undef	abts_rsp
298163899Smjacob#undef	els
29955373Smjacob#undef	hdrp
30055373Smjacob	return (rval);
30155373Smjacob}
30255373Smjacob
30355373Smjacobint
304157943Smjacobisp_target_put_entry(ispsoftc_t *isp, void *ap)
30555373Smjacob{
30655373Smjacob	void *outp;
307155704Smjacob	uint8_t etype = ((isphdr_t *) ap)->rqs_entry_type;
30855373Smjacob
309196008Smjacob	outp = isp_getrqentry(isp);
310196008Smjacob	if (outp == NULL) {
311196008Smjacob		isp_prt(isp, ISP_LOGWARN, rqo, __func__);
31255373Smjacob		return (-1);
31355373Smjacob	}
31455373Smjacob	switch (etype) {
315316152Smav	case RQSTYPE_NOTIFY_ACK:
316316152Smav		if (IS_24XX(isp))
317316152Smav			isp_put_notify_24xx_ack(isp, (na_fcentry_24xx_t *)ap,
318316152Smav			    (na_fcentry_24xx_t *)outp);
319316152Smav		else if (ISP_CAP_2KLOGIN(isp))
320316152Smav			isp_put_notify_ack_fc_e(isp, (na_fcentry_e_t *)ap,
321316152Smav			    (na_fcentry_e_t *)outp);
322316152Smav		else
323316152Smav			isp_put_notify_ack_fc(isp, ap, (na_fcentry_t *)outp);
324316152Smav		break;
32555373Smjacob	case RQSTYPE_ATIO2:
326316152Smav		if (ISP_CAP_2KLOGIN(isp))
327316152Smav			isp_put_atio2e(isp, (at2e_entry_t *)ap,
328316152Smav			    (at2e_entry_t *)outp);
329316152Smav		else
330316152Smav			isp_put_atio2(isp, (at2_entry_t *)ap,
331316152Smav			    (at2_entry_t *)outp);
33255373Smjacob		break;
33355373Smjacob	case RQSTYPE_CTIO2:
334316152Smav		if (ISP_CAP_2KLOGIN(isp))
335316152Smav			isp_put_ctio2e(isp, (ct2e_entry_t *)ap,
336316152Smav			    (ct2e_entry_t *)outp);
337316152Smav		else
338316152Smav			isp_put_ctio2(isp, (ct2_entry_t *)ap,
339316152Smav			    (ct2_entry_t *)outp);
34055373Smjacob		break;
341163899Smjacob	case RQSTYPE_CTIO7:
342316152Smav		isp_put_ctio7(isp, (ct7_entry_t *)ap, (ct7_entry_t *)outp);
343163899Smjacob		break;
344316152Smav	case RQSTYPE_ABTS_RSP:
345316152Smav		isp_put_abts_rsp(isp, (abts_rsp_t *)ap, (abts_rsp_t *)outp);
346316152Smav		break;
34755373Smjacob	default:
348196008Smjacob		isp_prt(isp, ISP_LOGERR, "%s: Unknown type 0x%x", __func__, etype);
34955373Smjacob		return (-1);
35055373Smjacob	}
351196008Smjacob	ISP_TDQE(isp, __func__, isp->isp_reqidx, ap);
352196008Smjacob	ISP_SYNC_REQUEST(isp);
35355373Smjacob	return (0);
35455373Smjacob}
35555373Smjacob
35655373Smjacobint
357157943Smjacobisp_target_put_atio(ispsoftc_t *isp, void *arg)
35855373Smjacob{
359291188Smav	at2_entry_t *aep = arg;
36055373Smjacob	union {
36155373Smjacob		at2_entry_t _atio2;
362154704Smjacob		at2e_entry_t _atio2e;
36355373Smjacob	} atun;
36455373Smjacob
365196008Smjacob	ISP_MEMZERO(&atun, sizeof atun);
366291188Smav	atun._atio2.at_header.rqs_entry_type = RQSTYPE_ATIO2;
367291188Smav	atun._atio2.at_header.rqs_entry_count = 1;
368291188Smav	if (ISP_CAP_SCCFW(isp)) {
369291188Smav		atun._atio2.at_scclun = aep->at_scclun;
37055373Smjacob	} else {
371291188Smav		atun._atio2.at_lun = (uint8_t) aep->at_lun;
37255373Smjacob	}
373291188Smav	if (ISP_CAP_2KLOGIN(isp)) {
374291188Smav		atun._atio2e.at_iid = ((at2e_entry_t *)aep)->at_iid;
375291188Smav	} else {
376291188Smav		atun._atio2.at_iid = aep->at_iid;
377291188Smav	}
378291188Smav	atun._atio2.at_rxid = aep->at_rxid;
379291188Smav	atun._atio2.at_status = CT_OK;
38055373Smjacob	return (isp_target_put_entry(isp, &atun));
38155373Smjacob}
38255373Smjacob
38355373Smjacob/*
38455373Smjacob * Command completion- both for handling cases of no resources or
38555373Smjacob * no blackhole driver, or other cases where we have to, inline,
38655373Smjacob * finish the command sanely, or for normal command completion.
38755373Smjacob *
38855373Smjacob * The 'completion' code value has the scsi status byte in the low 8 bits.
38955373Smjacob * If status is a CHECK CONDITION and bit 8 is nonzero, then bits 12..15 have
39055373Smjacob * the sense key and  bits 16..23 have the ASCQ and bits 24..31 have the ASC
39155373Smjacob * values.
39255373Smjacob *
39355373Smjacob * NB: the key, asc, ascq, cannot be used for parallel SCSI as it doesn't
39475196Smjacob * NB: inline SCSI sense reporting. As such, we lose this information. XXX.
39555373Smjacob *
39655373Smjacob * For both parallel && fibre channel, we use the feature that does
39755373Smjacob * an automatic resource autoreplenish so we don't have then later do
39855373Smjacob * put of an atio to replenish the f/w's resource count.
39955373Smjacob */
40055373Smjacob
40155373Smjacobint
402196008Smjacobisp_endcmd(ispsoftc_t *isp, ...)
40355373Smjacob{
404196008Smjacob	uint32_t code, hdl;
405196008Smjacob	uint8_t sts;
40655373Smjacob	union {
40755373Smjacob		ct2_entry_t _ctio2;
408154704Smjacob		ct2e_entry_t _ctio2e;
409163899Smjacob		ct7_entry_t _ctio7;
41055373Smjacob	} un;
411196008Smjacob	va_list ap;
412314732Smav	int vpidx, nphdl;
41355373Smjacob
414196008Smjacob	ISP_MEMZERO(&un, sizeof un);
41555373Smjacob
416163899Smjacob	if (IS_24XX(isp)) {
417196008Smjacob		at7_entry_t *aep;
418163899Smjacob		ct7_entry_t *cto = &un._ctio7;
419163899Smjacob
420196008Smjacob		va_start(ap, isp);
421196008Smjacob		aep = va_arg(ap, at7_entry_t *);
422196008Smjacob		nphdl = va_arg(ap, int);
423196008Smjacob		/*
424196008Smjacob		 * Note that vpidx may equal 0xff (unknown) here
425196008Smjacob		 */
426196008Smjacob		vpidx = va_arg(ap, int);
427196008Smjacob		code = va_arg(ap, uint32_t);
428196008Smjacob		hdl = va_arg(ap, uint32_t);
429196008Smjacob		va_end(ap);
430196008Smjacob		isp_prt(isp, ISP_LOGTDEBUG0, "%s: [RX_ID 0x%x] chan %d code %x", __func__, aep->at_rxid, vpidx, code);
431196008Smjacob
432196008Smjacob		sts = code & 0xff;
433163899Smjacob		cto->ct_header.rqs_entry_type = RQSTYPE_CTIO7;
434163899Smjacob		cto->ct_header.rqs_entry_count = 1;
435196008Smjacob		cto->ct_nphdl = nphdl;
436163899Smjacob		cto->ct_rxid = aep->at_rxid;
437196008Smjacob		cto->ct_iid_lo = (aep->at_hdr.s_id[1] << 8) | aep->at_hdr.s_id[2];
438163899Smjacob		cto->ct_iid_hi = aep->at_hdr.s_id[0];
439163899Smjacob		cto->ct_oxid = aep->at_hdr.ox_id;
440163899Smjacob		cto->ct_scsi_status = sts;
441196008Smjacob		cto->ct_vpidx = vpidx;
442196008Smjacob		cto->ct_flags = CT7_NOACK;
443196008Smjacob		if (code & ECMD_TERMINATE) {
444196008Smjacob			cto->ct_flags |= CT7_TERMINATE;
445196008Smjacob		} else if (code & ECMD_SVALID) {
446196008Smjacob			cto->ct_flags |= CT7_FLAG_MODE1 | CT7_SENDSTATUS;
447196008Smjacob			cto->ct_scsi_status |= (FCP_SNSLEN_VALID << 8);
448300218Smav			cto->ct_senselen = min(16, MAXRESPLEN_24XX);
449196008Smjacob			ISP_MEMZERO(cto->rsp.m1.ct_resp, sizeof (cto->rsp.m1.ct_resp));
450163899Smjacob			cto->rsp.m1.ct_resp[0] = 0xf0;
451163899Smjacob			cto->rsp.m1.ct_resp[2] = (code >> 12) & 0xf;
452163899Smjacob			cto->rsp.m1.ct_resp[7] = 8;
453238869Smjacob			cto->rsp.m1.ct_resp[12] = (code >> 16) & 0xff;
454238869Smjacob			cto->rsp.m1.ct_resp[13] = (code >> 24) & 0xff;
455300218Smav		} else if (code & ECMD_RVALID) {
456300218Smav			cto->ct_flags |= CT7_FLAG_MODE1 | CT7_SENDSTATUS;
457300218Smav			cto->ct_scsi_status |= (FCP_RSPLEN_VALID << 8);
458300218Smav			cto->rsp.m1.ct_resplen = 4;
459300218Smav			ISP_MEMZERO(cto->rsp.m1.ct_resp, sizeof (cto->rsp.m1.ct_resp));
460300218Smav			cto->rsp.m1.ct_resp[0] = (code >> 12) & 0xf;
461300218Smav			cto->rsp.m1.ct_resp[1] = (code >> 16) & 0xff;
462300218Smav			cto->rsp.m1.ct_resp[2] = (code >> 24) & 0xff;
463300218Smav			cto->rsp.m1.ct_resp[3] = 0;
464196008Smjacob		} else {
465196008Smjacob			cto->ct_flags |= CT7_FLAG_MODE1 | CT7_SENDSTATUS;
466163899Smjacob		}
467314756Smav		if (aep->at_cmnd.cdb_dl.sf.fcp_cmnd_dl != 0) {
468163899Smjacob			cto->ct_resid = aep->at_cmnd.cdb_dl.sf.fcp_cmnd_dl;
469314756Smav			cto->ct_scsi_status |= (FCP_RESID_UNDERFLOW << 8);
470163899Smjacob		}
471163899Smjacob		cto->ct_syshandle = hdl;
472291188Smav	} else {
473196008Smjacob		at2_entry_t *aep;
47455373Smjacob		ct2_entry_t *cto = &un._ctio2;
47555373Smjacob
476196008Smjacob		va_start(ap, isp);
477196008Smjacob		aep = va_arg(ap, at2_entry_t *);
478314732Smav		/* nphdl and vpidx are unused here. */
479314732Smav		nphdl = va_arg(ap, int);
480314732Smav		vpidx = va_arg(ap, int);
481196008Smjacob		code = va_arg(ap, uint32_t);
482196008Smjacob		hdl = va_arg(ap, uint32_t);
483196008Smjacob		va_end(ap);
484196008Smjacob
485196008Smjacob		isp_prt(isp, ISP_LOGTDEBUG0, "%s: [RX_ID 0x%x] code %x", __func__, aep->at_rxid, code);
486196008Smjacob
487196008Smjacob		sts = code & 0xff;
48855373Smjacob		cto->ct_header.rqs_entry_type = RQSTYPE_CTIO2;
48955373Smjacob		cto->ct_header.rqs_entry_count = 1;
490196008Smjacob		if (ISP_CAP_SCCFW(isp) == 0) {
49161774Smjacob			cto->ct_lun = aep->at_lun;
49261774Smjacob		}
493196008Smjacob		if (ISP_CAP_2KLOGIN(isp)) {
494154704Smjacob			un._ctio2e.ct_iid = ((at2e_entry_t *)aep)->at_iid;
495154704Smjacob		} else {
496154704Smjacob			cto->ct_iid = aep->at_iid;
497154704Smjacob		}
49855373Smjacob		cto->ct_rxid = aep->at_rxid;
499125189Smjacob		cto->rsp.m1.ct_scsi_status = sts;
50055373Smjacob		cto->ct_flags = CT2_SENDSTATUS | CT2_NO_DATA | CT2_FLAG_MODE1;
50155373Smjacob		if (hdl == 0) {
50255373Smjacob			cto->ct_flags |= CT2_CCINCR;
50355373Smjacob		}
50455373Smjacob		if (aep->at_datalen) {
50555373Smjacob			cto->ct_resid = aep->at_datalen;
50677365Smjacob			cto->rsp.m1.ct_scsi_status |= CT2_DATA_UNDER;
50755373Smjacob		}
508125189Smjacob		if (sts == SCSI_CHECK && (code & ECMD_SVALID)) {
50955373Smjacob			cto->rsp.m1.ct_resp[0] = 0xf0;
51055373Smjacob			cto->rsp.m1.ct_resp[2] = (code >> 12) & 0xf;
51155373Smjacob			cto->rsp.m1.ct_resp[7] = 8;
51255373Smjacob			cto->rsp.m1.ct_resp[12] = (code >> 24) & 0xff;
51355373Smjacob			cto->rsp.m1.ct_resp[13] = (code >> 16) & 0xff;
51455373Smjacob			cto->rsp.m1.ct_senselen = 16;
51577365Smjacob			cto->rsp.m1.ct_scsi_status |= CT2_SNSLEN_VALID;
51655373Smjacob		}
51774232Smjacob		cto->ct_syshandle = hdl;
51855373Smjacob	}
51955373Smjacob	return (isp_target_put_entry(isp, &un));
52055373Smjacob}
52155373Smjacob
522157943Smjacob/*
523157943Smjacob * These are either broadcast events or specifically CTIO fast completion
524157943Smjacob */
525196008Smjacob
526316092Smavvoid
527157943Smjacobisp_target_async(ispsoftc_t *isp, int bus, int event)
52855373Smjacob{
529196008Smjacob	isp_notify_t notify;
53055373Smjacob
531196008Smjacob	ISP_MEMZERO(&notify, sizeof (isp_notify_t));
532154704Smjacob	notify.nt_hba = isp;
533196008Smjacob	notify.nt_wwn = INI_ANY;
534196008Smjacob	notify.nt_nphdl = NIL_HANDLE;
535196008Smjacob	notify.nt_sid = PORT_ANY;
536196008Smjacob	notify.nt_did = PORT_ANY;
537196008Smjacob	notify.nt_tgt = TGT_ANY;
538196008Smjacob	notify.nt_channel = bus;
539154704Smjacob	notify.nt_lun = LUN_ANY;
540154704Smjacob	notify.nt_tagval = TAG_ANY;
541196008Smjacob	notify.nt_tagval |= (((uint64_t)(isp->isp_serno++)) << 32);
542154704Smjacob
54355373Smjacob	switch (event) {
544154704Smjacob	case ASYNC_LOOP_UP:
545154704Smjacob	case ASYNC_PTPMODE:
546196008Smjacob		isp_prt(isp, ISP_LOGTDEBUG0, "%s: LOOP UP", __func__);
547154704Smjacob		notify.nt_ncode = NT_LINK_UP;
548196008Smjacob		isp_async(isp, ISPASYNC_TARGET_NOTIFY, &notify);
549154704Smjacob		break;
550154704Smjacob	case ASYNC_LOOP_DOWN:
551196008Smjacob		isp_prt(isp, ISP_LOGTDEBUG0, "%s: LOOP DOWN", __func__);
552154704Smjacob		notify.nt_ncode = NT_LINK_DOWN;
553196008Smjacob		isp_async(isp, ISPASYNC_TARGET_NOTIFY, &notify);
554154704Smjacob		break;
555163899Smjacob	case ASYNC_LIP_ERROR:
556291265Smav	case ASYNC_LIP_NOS_OLS_RECV:
55755373Smjacob	case ASYNC_LIP_OCCURRED:
55883027Smjacob	case ASYNC_LOOP_RESET:
559196008Smjacob		isp_prt(isp, ISP_LOGTDEBUG0, "%s: LIP RESET", __func__);
560154704Smjacob		notify.nt_ncode = NT_LIP_RESET;
561196008Smjacob		isp_async(isp, ISPASYNC_TARGET_NOTIFY, &notify);
562154704Smjacob		break;
56355373Smjacob	case ASYNC_BUS_RESET:
564154704Smjacob	case ASYNC_TIMEOUT_RESET:	/* XXX: where does this come from ? */
565196008Smjacob		isp_prt(isp, ISP_LOGTDEBUG0, "%s: BUS RESET", __func__);
566154704Smjacob		notify.nt_ncode = NT_BUS_RESET;
567196008Smjacob		isp_async(isp, ISPASYNC_TARGET_NOTIFY, &notify);
56855373Smjacob		break;
56955373Smjacob	case ASYNC_DEVICE_RESET:
570196008Smjacob		isp_prt(isp, ISP_LOGTDEBUG0, "%s: DEVICE RESET", __func__);
571154704Smjacob		notify.nt_ncode = NT_TARGET_RESET;
572196008Smjacob		isp_async(isp, ISPASYNC_TARGET_NOTIFY, &notify);
573154704Smjacob		break;
574154704Smjacob	case ASYNC_CTIO_DONE:
575154704Smjacob	{
576154704Smjacob		uint8_t storage[QENTRY_LEN];
577196008Smjacob		isp_prt(isp, ISP_LOGTDEBUG0, "%s: CTIO DONE", __func__);
578154704Smjacob		memset(storage, 0, QENTRY_LEN);
579163899Smjacob		if (IS_24XX(isp)) {
580163899Smjacob			ct7_entry_t *ct = (ct7_entry_t *) storage;
581163899Smjacob			ct->ct_header.rqs_entry_type = RQSTYPE_CTIO7;
582163899Smjacob			ct->ct_nphdl = CT7_OK;
583163899Smjacob			ct->ct_syshandle = bus;
584196008Smjacob			ct->ct_flags = CT7_SENDSTATUS;
585291188Smav		} else {
586160251Smjacob            		/* This should also suffice for 2K login code */
587154704Smjacob			ct2_entry_t *ct = (ct2_entry_t *) storage;
588154704Smjacob			ct->ct_header.rqs_entry_type = RQSTYPE_CTIO2;
589154704Smjacob			ct->ct_status = CT_OK;
590154704Smjacob			ct->ct_syshandle = bus;
591154704Smjacob			ct->ct_flags = CT2_SENDSTATUS|CT2_FASTPOST;
59255373Smjacob		}
593196008Smjacob		isp_async(isp, ISPASYNC_TARGET_ACTION, storage);
594163899Smjacob		break;
595154704Smjacob	}
59655373Smjacob	default:
597196008Smjacob		isp_prt(isp, ISP_LOGERR, "%s: unknown event 0x%x", __func__, event);
59855373Smjacob		break;
59955373Smjacob	}
60055373Smjacob}
60155373Smjacob
60255373Smjacob/*
60355373Smjacob * Synthesize a message from the task management flags in a FCP_CMND_IU.
60455373Smjacob */
60555373Smjacobstatic void
606157943Smjacobisp_got_msg_fc(ispsoftc_t *isp, in_fcentry_t *inp)
60755373Smjacob{
608196008Smjacob	isp_notify_t notify;
609316156Smav	static const char f1[] = "%s from N-port handle 0x%x lun %jx seq 0x%x";
610316156Smav	static const char f2[] = "unknown %s 0x%x lun %jx N-Port handle 0x%x task flags 0x%x seq 0x%x\n";
611291013Smav	uint16_t seqid, nphdl;
61255373Smjacob
613196008Smjacob	ISP_MEMZERO(&notify, sizeof (isp_notify_t));
614196008Smjacob	notify.nt_hba = isp;
615196008Smjacob	notify.nt_wwn = INI_ANY;
616196008Smjacob	if (ISP_CAP_2KLOGIN(isp)) {
617196008Smjacob		notify.nt_nphdl = ((in_fcentry_e_t *)inp)->in_iid;
618291013Smav		nphdl = ((in_fcentry_e_t *)inp)->in_iid;
619157943Smjacob		seqid = ((in_fcentry_e_t *)inp)->in_seqid;
620154704Smjacob	} else {
621196008Smjacob		notify.nt_nphdl = inp->in_iid;
622291013Smav		nphdl = inp->in_iid;
623157943Smjacob		seqid = inp->in_seqid;
624154704Smjacob	}
625196008Smjacob	notify.nt_sid = PORT_ANY;
626196008Smjacob	notify.nt_did = PORT_ANY;
627196008Smjacob
628154704Smjacob	/* nt_tgt set in outer layers */
629196008Smjacob	if (ISP_CAP_SCCFW(isp)) {
630196008Smjacob		notify.nt_lun = inp->in_scclun;
63182843Smjacob	} else {
632196008Smjacob		notify.nt_lun = inp->in_lun;
63382843Smjacob	}
634196008Smjacob	notify.nt_tagval = seqid;
635196008Smjacob	notify.nt_tagval |= (((uint64_t)(isp->isp_serno++)) << 32);
636196008Smjacob	notify.nt_need_ack = 1;
637196008Smjacob	notify.nt_lreserved = inp;
63882843Smjacob
63955373Smjacob	if (inp->in_status != IN_MSG_RECEIVED) {
640291013Smav		isp_prt(isp, ISP_LOGINFO, f2, "immediate notify status", inp->in_status, notify.nt_lun, nphdl, inp->in_task_flags, inp->in_seqid);
641238869Smjacob		isp_async(isp, ISPASYNC_TARGET_NOTIFY_ACK, inp);
642154704Smjacob		return;
643154704Smjacob	}
644154704Smjacob
645154704Smjacob	if (inp->in_task_flags & TASK_FLAGS_ABORT_TASK_SET) {
646291013Smav		isp_prt(isp, ISP_LOGINFO, f1, "ABORT TASK SET", nphdl, notify.nt_lun, inp->in_seqid);
647196008Smjacob		notify.nt_ncode = NT_ABORT_TASK_SET;
648154704Smjacob	} else if (inp->in_task_flags & TASK_FLAGS_CLEAR_TASK_SET) {
649291013Smav		isp_prt(isp, ISP_LOGINFO, f1, "CLEAR TASK SET", nphdl, notify.nt_lun, inp->in_seqid);
650196008Smjacob		notify.nt_ncode = NT_CLEAR_TASK_SET;
651154704Smjacob	} else if (inp->in_task_flags & TASK_FLAGS_LUN_RESET) {
652291013Smav		isp_prt(isp, ISP_LOGINFO, f1, "LUN RESET", nphdl, notify.nt_lun, inp->in_seqid);
653196008Smjacob		notify.nt_ncode = NT_LUN_RESET;
654154704Smjacob	} else if (inp->in_task_flags & TASK_FLAGS_TARGET_RESET) {
655291013Smav		isp_prt(isp, ISP_LOGINFO, f1, "TARGET RESET", nphdl, notify.nt_lun, inp->in_seqid);
656196008Smjacob		notify.nt_ncode = NT_TARGET_RESET;
657154704Smjacob	} else if (inp->in_task_flags & TASK_FLAGS_CLEAR_ACA) {
658291013Smav		isp_prt(isp, ISP_LOGINFO, f1, "CLEAR ACA", nphdl, notify.nt_lun, inp->in_seqid);
659196008Smjacob		notify.nt_ncode = NT_CLEAR_ACA;
66055373Smjacob	} else {
661291013Smav		isp_prt(isp, ISP_LOGWARN, f2, "task flag", inp->in_status, notify.nt_lun, nphdl, inp->in_task_flags,  inp->in_seqid);
662238869Smjacob		isp_async(isp, ISPASYNC_TARGET_NOTIFY_ACK, inp);
663154704Smjacob		return;
66455373Smjacob	}
665196008Smjacob	isp_async(isp, ISPASYNC_TARGET_NOTIFY, &notify);
66655373Smjacob}
66755373Smjacob
668163899Smjacobstatic void
669163899Smjacobisp_got_tmf_24xx(ispsoftc_t *isp, at7_entry_t *aep)
670163899Smjacob{
671196008Smjacob	isp_notify_t notify;
672316156Smav	static const char f1[] = "%s from PortID 0x%06x lun %jx seq 0x%08x";
673316156Smav	static const char f2[] = "unknown Task Flag 0x%x lun %jx PortID 0x%x tag 0x%08x";
674300218Smav	fcportdb_t *lp;
675196008Smjacob	uint16_t chan;
676196008Smjacob	uint32_t sid, did;
677163899Smjacob
678196008Smjacob	ISP_MEMZERO(&notify, sizeof (isp_notify_t));
679196008Smjacob	notify.nt_hba = isp;
680196008Smjacob	notify.nt_wwn = INI_ANY;
681316156Smav	notify.nt_lun = CAM_EXTLUN_BYTE_SWIZZLE(be64dec(aep->at_cmnd.fcp_cmnd_lun));
682196008Smjacob	notify.nt_tagval = aep->at_rxid;
683196008Smjacob	notify.nt_tagval |= (((uint64_t)(isp->isp_serno++)) << 32);
684196008Smjacob	notify.nt_lreserved = aep;
685300218Smav	sid = (aep->at_hdr.s_id[0] << 16) | (aep->at_hdr.s_id[1] << 8) | aep->at_hdr.s_id[2];
686196008Smjacob	did = (aep->at_hdr.d_id[0] << 16) | (aep->at_hdr.d_id[1] << 8) | aep->at_hdr.d_id[2];
687300157Smav	if (ISP_CAP_MULTI_ID(isp) && isp->isp_nchan > 1) {
688300157Smav		/* Channel has to be derived from D_ID */
689300157Smav		isp_find_chan_by_did(isp, did, &chan);
690300157Smav		if (chan == ISP_NOCHAN) {
691316168Smav			isp_prt(isp, ISP_LOGWARN,
692316168Smav			    "%s: D_ID 0x%x not found on any channel",
693316168Smav			    __func__, did);
694316168Smav			isp_endcmd(isp, aep, NIL_HANDLE, ISP_NOCHAN,
695316168Smav			    ECMD_TERMINATE, 0);
696300157Smav			return;
697196008Smjacob		}
698300157Smav	} else {
699300157Smav		chan = 0;
700196008Smjacob	}
701300218Smav	if (isp_find_pdb_by_portid(isp, chan, sid, &lp))
702300218Smav		notify.nt_nphdl = lp->handle;
703300218Smav	else
704300218Smav		notify.nt_nphdl = NIL_HANDLE;
705196008Smjacob	notify.nt_sid = sid;
706196008Smjacob	notify.nt_did = did;
707196008Smjacob	notify.nt_channel = chan;
708289843Smav	if (aep->at_cmnd.fcp_cmnd_task_management & FCP_CMND_TMF_QUERY_TASK_SET) {
709289843Smav		isp_prt(isp, ISP_LOGINFO, f1, "QUERY TASK SET", sid, notify.nt_lun, aep->at_rxid);
710289843Smav		notify.nt_ncode = NT_QUERY_TASK_SET;
711289843Smav	} else if (aep->at_cmnd.fcp_cmnd_task_management & FCP_CMND_TMF_ABORT_TASK_SET) {
712196008Smjacob		isp_prt(isp, ISP_LOGINFO, f1, "ABORT TASK SET", sid, notify.nt_lun, aep->at_rxid);
713196008Smjacob		notify.nt_ncode = NT_ABORT_TASK_SET;
714196008Smjacob	} else if (aep->at_cmnd.fcp_cmnd_task_management & FCP_CMND_TMF_CLEAR_TASK_SET) {
715196008Smjacob		isp_prt(isp, ISP_LOGINFO, f1, "CLEAR TASK SET", sid, notify.nt_lun, aep->at_rxid);
716196008Smjacob		notify.nt_ncode = NT_CLEAR_TASK_SET;
717289843Smav	} else if (aep->at_cmnd.fcp_cmnd_task_management & FCP_CMND_TMF_QUERY_ASYNC_EVENT) {
718289843Smav		isp_prt(isp, ISP_LOGINFO, f1, "QUERY ASYNC EVENT", sid, notify.nt_lun, aep->at_rxid);
719289843Smav		notify.nt_ncode = NT_QUERY_ASYNC_EVENT;
720196008Smjacob	} else if (aep->at_cmnd.fcp_cmnd_task_management & FCP_CMND_TMF_LUN_RESET) {
721196008Smjacob		isp_prt(isp, ISP_LOGINFO, f1, "LUN RESET", sid, notify.nt_lun, aep->at_rxid);
722196008Smjacob		notify.nt_ncode = NT_LUN_RESET;
723196008Smjacob	} else if (aep->at_cmnd.fcp_cmnd_task_management & FCP_CMND_TMF_TGT_RESET) {
724196008Smjacob		isp_prt(isp, ISP_LOGINFO, f1, "TARGET RESET", sid, notify.nt_lun, aep->at_rxid);
725196008Smjacob		notify.nt_ncode = NT_TARGET_RESET;
726196008Smjacob	} else if (aep->at_cmnd.fcp_cmnd_task_management & FCP_CMND_TMF_CLEAR_ACA) {
727196008Smjacob		isp_prt(isp, ISP_LOGINFO, f1, "CLEAR ACA", sid, notify.nt_lun, aep->at_rxid);
728196008Smjacob		notify.nt_ncode = NT_CLEAR_ACA;
729163899Smjacob	} else {
730196008Smjacob		isp_prt(isp, ISP_LOGWARN, f2, aep->at_cmnd.fcp_cmnd_task_management, notify.nt_lun, sid, aep->at_rxid);
731196008Smjacob		notify.nt_ncode = NT_UNKNOWN;
732300218Smav		isp_endcmd(isp, aep, notify.nt_nphdl, chan, ECMD_RVALID | (0x4 << 12), 0);
733163899Smjacob		return;
734163899Smjacob	}
735196008Smjacob	isp_async(isp, ISPASYNC_TARGET_NOTIFY, &notify);
736163899Smjacob}
737163899Smjacob
738196008Smjacobint
739157943Smjacobisp_notify_ack(ispsoftc_t *isp, void *arg)
74055373Smjacob{
74155373Smjacob	char storage[QENTRY_LEN];
74255373Smjacob
743196008Smjacob	/*
744196008Smjacob	 * This is in case a Task Management Function ends up here.
745196008Smjacob	 */
746316149Smav	if (IS_24XX(isp) && ((isphdr_t *)arg)->rqs_entry_type == RQSTYPE_ATIO) {
747196008Smjacob		at7_entry_t *aep = arg;
748196008Smjacob		return (isp_endcmd(isp, aep, NIL_HANDLE, 0, 0, 0));
74955373Smjacob	}
75055373Smjacob
751196008Smjacob	ISP_MEMZERO(storage, QENTRY_LEN);
752196008Smjacob	if (IS_24XX(isp)) {
753316149Smav		in_fcentry_24xx_t *in = arg;
754163899Smjacob		na_fcentry_24xx_t *na = (na_fcentry_24xx_t *) storage;
755316149Smav
756238869Smjacob		na->na_header.rqs_entry_type = RQSTYPE_NOTIFY_ACK;
757238869Smjacob		na->na_header.rqs_entry_count = 1;
758316149Smav		na->na_nphdl = in->in_nphdl;
759316149Smav		na->na_flags = in->in_flags;
760316149Smav		na->na_status = in->in_status;
761316149Smav		na->na_status_subcode = in->in_status_subcode;
762316149Smav		na->na_fwhandle = in->in_fwhandle;
763316149Smav		na->na_rxid = in->in_rxid;
764316149Smav		na->na_oxid = in->in_oxid;
765316149Smav		na->na_vpidx = in->in_vpidx;
766316149Smav		if (in->in_status == IN24XX_SRR_RCVD) {
767316149Smav			na->na_srr_rxid = in->in_srr_rxid;
768316149Smav			na->na_srr_reloff_hi = in->in_srr_reloff_hi;
769316149Smav			na->na_srr_reloff_lo = in->in_srr_reloff_lo;
770316149Smav			na->na_srr_iu = in->in_srr_iu;
771316149Smav			/*
772316149Smav			 * Whether we're accepting the SRR or rejecting
773316149Smav			 * it is determined by looking at the in_reserved
774316149Smav			 * field in the original notify structure.
775316149Smav			 */
776316149Smav			if (in->in_reserved) {
777316149Smav				na->na_srr_flags = 1;
778316149Smav				na->na_srr_reject_vunique = 0;
779316149Smav				/* Unable to perform this command at this time. */
780316149Smav				na->na_srr_reject_code = 9;
781316149Smav				/* Unable to supply the requested data. */
782316149Smav				na->na_srr_reject_explanation = 0x2a;
783163899Smjacob			}
784163899Smjacob		}
785291188Smav	} else {
786316149Smav		in_fcentry_t *in = arg;
78755373Smjacob		na_fcentry_t *na = (na_fcentry_t *) storage;
788316149Smav		int iid;
789160978Smjacob
790316149Smav		ISP_MEMCPY(storage, arg, sizeof (isphdr_t));
791316149Smav		if (ISP_CAP_2KLOGIN(isp)) {
792316149Smav			iid = ((in_fcentry_e_t *)in)->in_iid;
793316149Smav			((na_fcentry_e_t *)na)->na_iid = iid;
79455373Smjacob		} else {
795316149Smav			iid = in->in_iid;
796316149Smav			na->na_iid = iid;
797316149Smav		}
798316149Smav		na->na_task_flags = in->in_task_flags & TASK_FLAGS_RESERVED_MASK;
799316149Smav		na->na_seqid = in->in_seqid;
800316149Smav		na->na_status = in->in_status;
801316149Smav		na->na_flags = NAFC_RCOUNT;
802316149Smav		/* We do not modify resource counts for LIP resets */
803316149Smav		if (in->in_status == IN_RESET)
80455373Smjacob			na->na_flags = NAFC_RST_CLRD;
805316149Smav		if (in->in_status == IN_MSG_RECEIVED) {
806316149Smav			na->na_flags |= NAFC_TVALID;
807316149Smav			na->na_response = 0;	/* XXX SUCCEEDED XXX */
80855373Smjacob		}
80959453Smjacob		na->na_header.rqs_entry_type = RQSTYPE_NOTIFY_ACK;
81059453Smjacob		na->na_header.rqs_entry_count = 1;
811291013Smav		isp_prt(isp, ISP_LOGTDEBUG0, "notify ack handle %x seqid %x flags %x tflags %x response %x", iid, na->na_seqid,
812160978Smjacob		    na->na_flags, na->na_task_flags, na->na_response);
81355373Smjacob	}
814316152Smav	return (isp_target_put_entry(isp, &storage));
81555373Smjacob}
81655373Smjacob
817196008Smjacobint
818196008Smjacobisp_acknak_abts(ispsoftc_t *isp, void *arg, int errno)
819196008Smjacob{
820196008Smjacob	char storage[QENTRY_LEN];
821196008Smjacob	uint16_t tmpw;
822196008Smjacob	uint8_t tmpb;
823196008Smjacob	abts_t *abts = arg;
824196008Smjacob	abts_rsp_t *rsp = (abts_rsp_t *) storage;
825196008Smjacob
826196008Smjacob	if (!IS_24XX(isp)) {
827196008Smjacob		isp_prt(isp, ISP_LOGERR, "%s: called for non-24XX card", __func__);
828196008Smjacob		return (0);
829196008Smjacob	}
830196008Smjacob
831196008Smjacob	if (abts->abts_header.rqs_entry_type != RQSTYPE_ABTS_RCVD) {
832196008Smjacob		isp_prt(isp, ISP_LOGERR, "%s: called for non-ABTS entry (0x%x)", __func__, abts->abts_header.rqs_entry_type);
833196008Smjacob		return (0);
834196008Smjacob	}
835196008Smjacob
836196008Smjacob	ISP_MEMCPY(rsp, abts, QENTRY_LEN);
837196008Smjacob	rsp->abts_rsp_header.rqs_entry_type = RQSTYPE_ABTS_RSP;
838196008Smjacob
839196008Smjacob	/*
840196008Smjacob	 * Swap destination and source for response.
841196008Smjacob	 */
842196008Smjacob	rsp->abts_rsp_r_ctl = BA_ACC;
843196008Smjacob	tmpw = rsp->abts_rsp_did_lo;
844196008Smjacob	tmpb = rsp->abts_rsp_did_hi;
845196008Smjacob	rsp->abts_rsp_did_lo = rsp->abts_rsp_sid_lo;
846196008Smjacob	rsp->abts_rsp_did_hi = rsp->abts_rsp_sid_hi;
847196008Smjacob	rsp->abts_rsp_sid_lo = tmpw;
848196008Smjacob	rsp->abts_rsp_sid_hi = tmpb;
849196008Smjacob
850196008Smjacob	rsp->abts_rsp_f_ctl_hi ^= 0x80; 	/* invert Exchange Context */
851196008Smjacob	rsp->abts_rsp_f_ctl_hi &= ~0x7f;	/* clear Sequence Initiator and other bits */
852196008Smjacob	rsp->abts_rsp_f_ctl_hi |= 0x10;		/* abort the whole exchange */
853196008Smjacob	rsp->abts_rsp_f_ctl_hi |= 0x8;		/* last data frame of sequence */
854196008Smjacob	rsp->abts_rsp_f_ctl_hi |= 0x1;		/* transfer Sequence Initiative */
855196008Smjacob	rsp->abts_rsp_f_ctl_lo = 0;
856196008Smjacob
857196008Smjacob	if (errno == 0) {
858196008Smjacob		uint16_t rx_id, ox_id;
859196008Smjacob
860196008Smjacob		rx_id = rsp->abts_rsp_rx_id;
861196008Smjacob		ox_id = rsp->abts_rsp_ox_id;
862196008Smjacob		ISP_MEMZERO(&rsp->abts_rsp_payload.ba_acc, sizeof (rsp->abts_rsp_payload.ba_acc));
863196008Smjacob                isp_prt(isp, ISP_LOGTINFO, "[0x%x] ABTS of 0x%x being BA_ACC'd", rsp->abts_rsp_rxid_abts, rsp->abts_rsp_rxid_task);
864196008Smjacob                rsp->abts_rsp_payload.ba_acc.aborted_rx_id = rx_id;
865196008Smjacob                rsp->abts_rsp_payload.ba_acc.aborted_ox_id = ox_id;
866196008Smjacob                rsp->abts_rsp_payload.ba_acc.high_seq_cnt = 0xffff;
867196008Smjacob	} else {
868196008Smjacob		ISP_MEMZERO(&rsp->abts_rsp_payload.ba_rjt, sizeof (rsp->abts_rsp_payload.ba_acc));
869196008Smjacob		switch (errno) {
870196008Smjacob		case ENOMEM:
871238869Smjacob			rsp->abts_rsp_payload.ba_rjt.reason = 5;	/* Logical Unit Busy */
872196008Smjacob			break;
873196008Smjacob		default:
874196008Smjacob			rsp->abts_rsp_payload.ba_rjt.reason = 9;	/* Unable to perform command request */
875196008Smjacob			break;
876196008Smjacob		}
877196008Smjacob	}
878316152Smav	return (isp_target_put_entry(isp, rsp));
879196008Smjacob}
880196008Smjacob
88155373Smjacobstatic void
882316158Smavisp_handle_abts(ispsoftc_t *isp, abts_t *abts)
883316158Smav{
884316158Smav	isp_notify_t notify, *nt = &notify;
885316158Smav	fcportdb_t *lp;
886316158Smav	uint16_t chan;
887316158Smav	uint32_t sid, did;
888316158Smav
889316158Smav	did = (abts->abts_did_hi << 16) | abts->abts_did_lo;
890316158Smav	sid = (abts->abts_sid_hi << 16) | abts->abts_sid_lo;
891316158Smav	ISP_MEMZERO(nt, sizeof (isp_notify_t));
892316158Smav
893316158Smav	nt->nt_hba = isp;
894316158Smav	nt->nt_did = did;
895316158Smav	nt->nt_nphdl = abts->abts_nphdl;
896316158Smav	nt->nt_sid = sid;
897316168Smav	if (ISP_CAP_MULTI_ID(isp) && isp->isp_nchan > 1) {
898316168Smav		/* Channel has to be derived from D_ID */
899316168Smav		isp_find_chan_by_did(isp, did, &chan);
900316168Smav		if (chan == ISP_NOCHAN) {
901316168Smav			isp_prt(isp, ISP_LOGWARN,
902316168Smav			    "%s: D_ID 0x%x not found on any channel",
903316168Smav			    __func__, did);
904316168Smav			isp_acknak_abts(isp, abts, ENXIO);
905316168Smav			return;
906316158Smav		}
907316168Smav	} else
908316168Smav		chan = 0;
909316168Smav	nt->nt_tgt = FCPARAM(isp, chan)->isp_wwpn;
910316168Smav	if (isp_find_pdb_by_handle(isp, chan, abts->abts_nphdl, &lp))
911316168Smav		nt->nt_wwn = lp->port_wwn;
912316168Smav	else
913316168Smav		nt->nt_wwn = INI_ANY;
914316158Smav	nt->nt_lun = LUN_ANY;
915316158Smav	nt->nt_need_ack = 1;
916316158Smav	nt->nt_tagval = abts->abts_rxid_task;
917316158Smav	nt->nt_tagval |= (((uint64_t) abts->abts_rxid_abts) << 32);
918316158Smav	isp_prt(isp, ISP_LOGTINFO, "[0x%x] ABTS from N-Port handle 0x%x"
919316158Smav	    " Port 0x%06x for task 0x%x (rx_id 0x%04x ox_id 0x%04x)",
920316158Smav	    abts->abts_rxid_abts, abts->abts_nphdl, sid, abts->abts_rxid_task,
921316158Smav	    abts->abts_rx_id, abts->abts_ox_id);
922316158Smav	nt->nt_channel = chan;
923316158Smav	nt->nt_ncode = NT_ABORT_TASK;
924316158Smav	nt->nt_lreserved = abts;
925316158Smav	isp_async(isp, ISPASYNC_TARGET_NOTIFY, &notify);
926316158Smav}
927316158Smav
928316158Smavstatic void
929157943Smjacobisp_handle_atio2(ispsoftc_t *isp, at2_entry_t *aep)
93055373Smjacob{
931316166Smav	fcportdb_t *lp;
932154704Smjacob	int lun, iid;
93361774Smjacob
934196008Smjacob	if (ISP_CAP_SCCFW(isp)) {
93561774Smjacob		lun = aep->at_scclun;
93661774Smjacob	} else {
93761774Smjacob		lun = aep->at_lun;
93861774Smjacob	}
93961774Smjacob
940196008Smjacob	if (ISP_CAP_2KLOGIN(isp)) {
941154704Smjacob		iid = ((at2e_entry_t *)aep)->at_iid;
942154704Smjacob	} else {
943154704Smjacob		iid = aep->at_iid;
944154704Smjacob	}
945154704Smjacob
94655373Smjacob	/*
94755373Smjacob	 * The firmware status (except for the QLTM_SVALID bit) indicates
94855373Smjacob	 * why this ATIO was sent to us.
94955373Smjacob	 *
95055373Smjacob	 * If QLTM_SVALID is set, the firware has recommended Sense Data.
95155373Smjacob	 *
95255373Smjacob	 * If the DISCONNECTS DISABLED bit is set in the flags field,
95355373Smjacob	 * we're still connected on the SCSI bus - i.e. the initiator
95455373Smjacob	 * did not set DiscPriv in the identify message. We don't care
95555373Smjacob	 * about this so it's ignored.
95655373Smjacob	 */
95755373Smjacob
958196008Smjacob	switch (aep->at_status & ~QLTM_SVALID) {
95955373Smjacob	case AT_PATH_INVALID:
96055373Smjacob		/*
96155373Smjacob		 * ATIO rejected by the firmware due to disabled lun.
96255373Smjacob		 */
963289882Smav		isp_prt(isp, ISP_LOGERR, "rejected ATIO2 for disabled lun %x", lun);
96455373Smjacob		break;
96555373Smjacob	case AT_NOCAP:
96655373Smjacob		/*
96755373Smjacob		 * Requested Capability not available
96855373Smjacob		 * We sent an ATIO that overflowed the firmware's
96955373Smjacob		 * command resource count.
97055373Smjacob		 */
971289882Smav		isp_prt(isp, ISP_LOGERR, "rejected ATIO2 for lun %x- command count overflow", lun);
97255373Smjacob		break;
97355373Smjacob
97455373Smjacob	case AT_BDR_MSG:
97555373Smjacob		/*
97655373Smjacob		 * If we send an ATIO to the firmware to increment
97755373Smjacob		 * its command resource count, and the firmware is
97855373Smjacob		 * recovering from a Bus Device Reset, it returns
97955373Smjacob		 * the ATIO with this status. We set the command
98055373Smjacob		 * resource count in the Enable Lun entry and no
98155373Smjacob		 * not increment it. Therefore we should never get
98255373Smjacob		 * this status here.
98355373Smjacob		 */
98483027Smjacob		isp_prt(isp, ISP_LOGERR, atiocope, lun, 0);
98555373Smjacob		break;
98655373Smjacob
98755373Smjacob	case AT_CDB:		/* Got a CDB */
988316166Smav
989316166Smav		/* Make sure we have this inititor in port database. */
990316166Smav		if (!IS_2100(isp) &&
991316166Smav		    (isp_find_pdb_by_handle(isp, 0, iid, &lp) == 0 ||
992316166Smav		     lp->state == FC_PORTDB_STATE_ZOMBIE)) {
993316166Smav		        fcparam *fcp = FCPARAM(isp, 0);
994316166Smav			uint64_t wwpn =
995316166Smav				(((uint64_t) aep->at_wwpn[0]) << 48) |
996316166Smav				(((uint64_t) aep->at_wwpn[1]) << 32) |
997316166Smav				(((uint64_t) aep->at_wwpn[2]) << 16) |
998316166Smav				(((uint64_t) aep->at_wwpn[3]) <<  0);
999316166Smav			isp_add_wwn_entry(isp, 0, wwpn, INI_NONE,
1000316166Smav			    iid, PORT_ANY, 0);
1001316166Smav			if (fcp->isp_loopstate > LOOP_LTEST_DONE)
1002316166Smav				fcp->isp_loopstate = LOOP_LTEST_DONE;
1003316166Smav			isp_async(isp, ISPASYNC_CHANGE_NOTIFY, 0,
1004316166Smav			    ISPASYNC_CHANGE_PDB, iid, 0x06, 0xff);
1005316166Smav		}
1006316166Smav
1007316166Smav		/* Punt to platform specific layer. */
1008196008Smjacob		isp_async(isp, ISPASYNC_TARGET_ACTION, aep);
100955373Smjacob		break;
101055373Smjacob
101155373Smjacob	case AT_RESET:
101255373Smjacob		/*
101355373Smjacob		 * A bus reset came along an blew away this command. Why
101455373Smjacob		 * they do this in addition the async event code stuff,
101555373Smjacob		 * I dunno.
101655373Smjacob		 *
101755373Smjacob		 * Ignore it because the async event will clear things
101855373Smjacob		 * up for us.
101955373Smjacob		 */
1020154704Smjacob		isp_prt(isp, ISP_LOGERR, atior, lun, iid, 0);
102155373Smjacob		break;
102255373Smjacob
102355373Smjacob
102455373Smjacob	default:
1025291013Smav		isp_prt(isp, ISP_LOGERR, "Unknown ATIO2 status 0x%x from handle %d for lun %x", aep->at_status, iid, lun);
102675196Smjacob		(void) isp_target_put_atio(isp, aep);
102755373Smjacob		break;
102855373Smjacob	}
102955373Smjacob}
103055373Smjacob
103155373Smjacobstatic void
1032157943Smjacobisp_handle_ctio2(ispsoftc_t *isp, ct2_entry_t *ct)
103355373Smjacob{
1034163899Smjacob	void *xs;
103564090Smjacob	int pl = ISP_LOGTDEBUG2;
103655373Smjacob	char *fmsg = NULL;
103755373Smjacob
103874232Smjacob	if (ct->ct_syshandle) {
1039292725Smav		xs = isp_find_xs(isp, ct->ct_syshandle);
1040163899Smjacob		if (xs == NULL) {
104164090Smjacob			pl = ISP_LOGALL;
1042163899Smjacob		}
104355373Smjacob	} else {
104455373Smjacob		xs = NULL;
104555373Smjacob	}
104655373Smjacob
1047196008Smjacob	switch (ct->ct_status & ~QLTM_SVALID) {
104877365Smjacob	case CT_BUS_ERROR:
104977365Smjacob		isp_prt(isp, ISP_LOGERR, "PCI DMA Bus Error");
105077365Smjacob		/* FALL Through */
105177365Smjacob	case CT_DATA_OVER:
105277365Smjacob	case CT_DATA_UNDER:
105355373Smjacob	case CT_OK:
105455373Smjacob		/*
105555373Smjacob		 * There are generally 2 possibilities as to why we'd get
105655373Smjacob		 * this condition:
105755373Smjacob		 * 	We sent or received data.
105855373Smjacob		 * 	We sent status & command complete.
105955373Smjacob		 */
106055373Smjacob
106155373Smjacob		break;
106255373Smjacob
106355373Smjacob	case CT_BDR_MSG:
106455373Smjacob		/*
106577365Smjacob		 * Target Reset function received.
106655373Smjacob		 *
1067172568Skevlo		 * The firmware generates an async mailbox interrupt to
106855373Smjacob		 * notify us of this and returns outstanding CTIOs with this
106955373Smjacob		 * status. These CTIOs are handled in that same way as
107055373Smjacob		 * CT_ABORTED ones, so just fall through here.
107155373Smjacob		 */
1072163899Smjacob		fmsg = "TARGET RESET";
107355373Smjacob		/*FALLTHROUGH*/
107455373Smjacob	case CT_RESET:
107555373Smjacob		if (fmsg == NULL)
107677365Smjacob			fmsg = "LIP Reset";
107755373Smjacob		/*FALLTHROUGH*/
107855373Smjacob	case CT_ABORTED:
107955373Smjacob		/*
108055373Smjacob		 * When an Abort message is received the firmware goes to
108155373Smjacob		 * Bus Free and returns all outstanding CTIOs with the status
108255373Smjacob		 * set, then sends us an Immediate Notify entry.
108355373Smjacob		 */
1084163899Smjacob		if (fmsg == NULL) {
1085163899Smjacob			fmsg = "ABORT";
1086163899Smjacob		}
108755373Smjacob
1088196008Smjacob		isp_prt(isp, ISP_LOGTDEBUG0, "CTIO2 destroyed by %s: RX_ID=0x%x", fmsg, ct->ct_rxid);
108955373Smjacob		break;
109055373Smjacob
109155373Smjacob	case CT_INVAL:
109255373Smjacob		/*
109356006Smjacob		 * CTIO rejected by the firmware - invalid data direction.
109455373Smjacob		 */
1095120016Smjacob		isp_prt(isp, ISP_LOGERR, "CTIO2 had wrong data direction");
109655373Smjacob		break;
109755373Smjacob
109855373Smjacob	case CT_RSELTMO:
109977365Smjacob		fmsg = "failure to reconnect to initiator";
110055373Smjacob		/*FALLTHROUGH*/
110155373Smjacob	case CT_TIMEOUT:
110255373Smjacob		if (fmsg == NULL)
110377365Smjacob			fmsg = "command";
1104196008Smjacob		isp_prt(isp, ISP_LOGWARN, "Firmware timed out on %s", fmsg);
110555373Smjacob		break;
110655373Smjacob
110755373Smjacob	case CT_ERR:
110855373Smjacob		fmsg = "Completed with Error";
110955373Smjacob		/*FALLTHROUGH*/
111055373Smjacob	case CT_LOGOUT:
111155373Smjacob		if (fmsg == NULL)
111255373Smjacob			fmsg = "Port Logout";
111355373Smjacob		/*FALLTHROUGH*/
1114163899Smjacob	case CT_PORTUNAVAIL:
111555373Smjacob		if (fmsg == NULL)
111655373Smjacob			fmsg = "Port not available";
1117115521Sphk		/*FALLTHROUGH*/
111877365Smjacob	case CT_PORTCHANGED:
111977365Smjacob		if (fmsg == NULL)
112077365Smjacob			fmsg = "Port Changed";
1121115521Sphk		/*FALLTHROUGH*/
112255373Smjacob	case CT_NOACK:
112355373Smjacob		if (fmsg == NULL)
112455373Smjacob			fmsg = "unacknowledged Immediate Notify pending";
1125163899Smjacob		isp_prt(isp, ISP_LOGWARN, "CTIO returned by f/w- %s", fmsg);
112655373Smjacob		break;
112755373Smjacob
112855373Smjacob	case CT_INVRXID:
112955373Smjacob		/*
113055373Smjacob		 * CTIO rejected by the firmware because an invalid RX_ID.
113155373Smjacob		 * Just print a message.
113255373Smjacob		 */
1133196008Smjacob		isp_prt(isp, ISP_LOGWARN, "CTIO2 completed with Invalid RX_ID 0x%x", ct->ct_rxid);
113455373Smjacob		break;
113555373Smjacob
113655373Smjacob	default:
1137196008Smjacob		isp_prt(isp, ISP_LOGERR, "Unknown CTIO2 status 0x%x", ct->ct_status & ~QLTM_SVALID);
113855373Smjacob		break;
113955373Smjacob	}
114055373Smjacob
114155373Smjacob	if (xs == NULL) {
114255373Smjacob		/*
114355373Smjacob		 * There may be more than one CTIO for a data transfer,
114455373Smjacob		 * or this may be a status CTIO we're not monitoring.
114555373Smjacob		 *
114655373Smjacob		 * The assumption is that they'll all be returned in the
114755373Smjacob		 * order we got them.
114855373Smjacob		 */
114974232Smjacob		if (ct->ct_syshandle == 0) {
1150125187Smjacob			if ((ct->ct_flags & CT2_SENDSTATUS) == 0) {
1151196008Smjacob				isp_prt(isp, pl, "intermediate CTIO completed ok");
115255373Smjacob			} else {
1153196008Smjacob				isp_prt(isp, pl, "unmonitored CTIO completed ok");
115455373Smjacob			}
115555373Smjacob		} else {
1156196008Smjacob			isp_prt(isp, pl, "NO xs for CTIO (handle 0x%x) status 0x%x", ct->ct_syshandle, ct->ct_status & ~QLTM_SVALID);
115755373Smjacob		}
115855373Smjacob	} else {
115977365Smjacob		if ((ct->ct_flags & CT2_DATAMASK) != CT2_NO_DATA) {
116077365Smjacob			ISP_DMAFREE(isp, xs, ct->ct_syshandle);
116177365Smjacob		}
1162125187Smjacob		if (ct->ct_flags & CT2_SENDSTATUS) {
116355373Smjacob			/*
116455373Smjacob			 * Sent status and command complete.
116555373Smjacob			 *
116655373Smjacob			 * We're now really done with this command, so we
116755373Smjacob			 * punt to the platform dependent layers because
116855373Smjacob			 * only there can we do the appropriate command
116955373Smjacob			 * complete thread synchronization.
117055373Smjacob			 */
117164090Smjacob			isp_prt(isp, pl, "status CTIO complete");
117255373Smjacob		} else {
117355373Smjacob			/*
117455373Smjacob			 * Final CTIO completed. Release DMA resources and
117555373Smjacob			 * notify platform dependent layers.
117655373Smjacob			 */
117764090Smjacob			isp_prt(isp, pl, "data CTIO complete");
117855373Smjacob		}
1179196008Smjacob		isp_async(isp, ISPASYNC_TARGET_ACTION, ct);
118055373Smjacob		/*
118155373Smjacob		 * The platform layer will destroy the handle if appropriate.
118255373Smjacob		 */
118355373Smjacob	}
118455373Smjacob}
1185163899Smjacob
1186163899Smjacobstatic void
1187163899Smjacobisp_handle_ctio7(ispsoftc_t *isp, ct7_entry_t *ct)
1188163899Smjacob{
1189163899Smjacob	void *xs;
1190163899Smjacob	int pl = ISP_LOGTDEBUG2;
1191163899Smjacob	char *fmsg = NULL;
1192163899Smjacob
1193163899Smjacob	if (ct->ct_syshandle) {
1194292725Smav		xs = isp_find_xs(isp, ct->ct_syshandle);
1195163899Smjacob		if (xs == NULL) {
1196163899Smjacob			pl = ISP_LOGALL;
1197163899Smjacob		}
1198163899Smjacob	} else {
1199163899Smjacob		xs = NULL;
1200163899Smjacob	}
1201163899Smjacob
1202196008Smjacob	switch (ct->ct_nphdl) {
1203163899Smjacob	case CT7_BUS_ERROR:
1204163899Smjacob		isp_prt(isp, ISP_LOGERR, "PCI DMA Bus Error");
1205163899Smjacob		/* FALL Through */
1206163899Smjacob	case CT7_DATA_OVER:
1207163899Smjacob	case CT7_DATA_UNDER:
1208163899Smjacob	case CT7_OK:
1209163899Smjacob		/*
1210163899Smjacob		 * There are generally 2 possibilities as to why we'd get
1211163899Smjacob		 * this condition:
1212163899Smjacob		 * 	We sent or received data.
1213163899Smjacob		 * 	We sent status & command complete.
1214163899Smjacob		 */
1215163899Smjacob
1216163899Smjacob		break;
1217163899Smjacob
1218163899Smjacob	case CT7_RESET:
1219163899Smjacob		if (fmsg == NULL) {
1220163899Smjacob			fmsg = "LIP Reset";
1221163899Smjacob		}
1222163899Smjacob		/*FALLTHROUGH*/
1223163899Smjacob	case CT7_ABORTED:
1224163899Smjacob		/*
1225163899Smjacob		 * When an Abort message is received the firmware goes to
1226163899Smjacob		 * Bus Free and returns all outstanding CTIOs with the status
1227163899Smjacob		 * set, then sends us an Immediate Notify entry.
1228163899Smjacob		 */
1229163899Smjacob		if (fmsg == NULL) {
1230163899Smjacob			fmsg = "ABORT";
1231163899Smjacob		}
1232196008Smjacob		isp_prt(isp, ISP_LOGTDEBUG0, "CTIO7 destroyed by %s: RX_ID=0x%x", fmsg, ct->ct_rxid);
1233163899Smjacob		break;
1234163899Smjacob
1235163899Smjacob	case CT7_TIMEOUT:
1236163899Smjacob		if (fmsg == NULL) {
1237163899Smjacob			fmsg = "command";
1238163899Smjacob		}
1239196008Smjacob		isp_prt(isp, ISP_LOGWARN, "Firmware timed out on %s", fmsg);
1240163899Smjacob		break;
1241163899Smjacob
1242163899Smjacob	case CT7_ERR:
1243163899Smjacob		fmsg = "Completed with Error";
1244163899Smjacob		/*FALLTHROUGH*/
1245163899Smjacob	case CT7_LOGOUT:
1246163899Smjacob		if (fmsg == NULL) {
1247163899Smjacob			fmsg = "Port Logout";
1248163899Smjacob		}
1249163899Smjacob		/*FALLTHROUGH*/
1250163899Smjacob	case CT7_PORTUNAVAIL:
1251163899Smjacob		if (fmsg == NULL) {
1252163899Smjacob			fmsg = "Port not available";
1253163899Smjacob		}
1254163899Smjacob		/*FALLTHROUGH*/
1255163899Smjacob	case CT7_PORTCHANGED:
1256163899Smjacob		if (fmsg == NULL) {
1257163899Smjacob			fmsg = "Port Changed";
1258163899Smjacob		}
1259163899Smjacob		isp_prt(isp, ISP_LOGWARN, "CTIO returned by f/w- %s", fmsg);
1260163899Smjacob		break;
1261163899Smjacob
1262163899Smjacob	case CT7_INVRXID:
1263163899Smjacob		/*
1264163899Smjacob		 * CTIO rejected by the firmware because an invalid RX_ID.
1265163899Smjacob		 * Just print a message.
1266163899Smjacob		 */
1267196008Smjacob		isp_prt(isp, ISP_LOGWARN, "CTIO7 completed with Invalid RX_ID 0x%x", ct->ct_rxid);
1268163899Smjacob		break;
1269163899Smjacob
1270163899Smjacob	case CT7_REASSY_ERR:
1271163899Smjacob		isp_prt(isp, ISP_LOGWARN, "reassembly error");
1272163899Smjacob		break;
1273163899Smjacob
1274163899Smjacob	case CT7_SRR:
1275238869Smjacob		isp_prt(isp, ISP_LOGTDEBUG0, "SRR received");
1276163899Smjacob		break;
1277163899Smjacob
1278163899Smjacob	default:
1279196008Smjacob		isp_prt(isp, ISP_LOGERR, "Unknown CTIO7 status 0x%x", ct->ct_nphdl);
1280163899Smjacob		break;
1281163899Smjacob	}
1282163899Smjacob
1283163899Smjacob	if (xs == NULL) {
1284163899Smjacob		/*
1285163899Smjacob		 * There may be more than one CTIO for a data transfer,
1286163899Smjacob		 * or this may be a status CTIO we're not monitoring.
1287163899Smjacob		 *
1288163899Smjacob		 * The assumption is that they'll all be returned in the
1289163899Smjacob		 * order we got them.
1290163899Smjacob		 */
1291163899Smjacob		if (ct->ct_syshandle == 0) {
1292163899Smjacob			if (ct->ct_flags & CT7_TERMINATE) {
1293238869Smjacob				isp_prt(isp, ISP_LOGINFO, "termination of [RX_ID 0x%x] complete", ct->ct_rxid);
1294163899Smjacob			} else if ((ct->ct_flags & CT7_SENDSTATUS) == 0) {
1295196008Smjacob				isp_prt(isp, pl, "intermediate CTIO completed ok");
1296163899Smjacob			} else {
1297196008Smjacob				isp_prt(isp, pl, "unmonitored CTIO completed ok");
1298163899Smjacob			}
1299163899Smjacob		} else {
1300196008Smjacob			isp_prt(isp, pl, "NO xs for CTIO (handle 0x%x) status 0x%x", ct->ct_syshandle, ct->ct_nphdl);
1301163899Smjacob		}
1302163899Smjacob	} else {
1303196008Smjacob		if ((ct->ct_flags & CT7_DATAMASK) != CT7_NO_DATA) {
1304163899Smjacob			ISP_DMAFREE(isp, xs, ct->ct_syshandle);
1305163899Smjacob		}
1306196008Smjacob		if (ct->ct_flags & CT7_SENDSTATUS) {
1307163899Smjacob			/*
1308163899Smjacob			 * Sent status and command complete.
1309163899Smjacob			 *
1310163899Smjacob			 * We're now really done with this command, so we
1311163899Smjacob			 * punt to the platform dependent layers because
1312163899Smjacob			 * only there can we do the appropriate command
1313163899Smjacob			 * complete thread synchronization.
1314163899Smjacob			 */
1315163899Smjacob			isp_prt(isp, pl, "status CTIO complete");
1316163899Smjacob		} else {
1317163899Smjacob			/*
1318163899Smjacob			 * Final CTIO completed. Release DMA resources and
1319163899Smjacob			 * notify platform dependent layers.
1320163899Smjacob			 */
1321163899Smjacob			isp_prt(isp, pl, "data CTIO complete");
1322163899Smjacob		}
1323196008Smjacob		isp_async(isp, ISPASYNC_TARGET_ACTION, ct);
1324163899Smjacob		/*
1325163899Smjacob		 * The platform layer will destroy the handle if appropriate.
1326163899Smjacob		 */
1327163899Smjacob	}
1328163899Smjacob}
1329196008Smjacob
1330196008Smjacobstatic void
1331316161Smavisp_handle_notify(ispsoftc_t *isp, in_fcentry_t *inp)
1332196008Smjacob{
1333316161Smav	fcportdb_t *lp;
1334316161Smav	uint64_t wwn;
1335316161Smav	uint32_t sid;
1336316161Smav	uint16_t nphdl, status;
1337316161Smav	isp_notify_t notify;
1338316161Smav
1339316161Smav	status = inp->in_status;
1340316161Smav	isp_prt(isp, ISP_LOGTDEBUG0, "Immediate Notify, status=0x%x seqid=0x%x",
1341316161Smav	    status, inp->in_seqid);
1342316161Smav	switch (status) {
1343316161Smav	case IN_MSG_RECEIVED:
1344316161Smav	case IN_IDE_RECEIVED:
1345316161Smav		isp_got_msg_fc(isp, inp);
1346316161Smav		return;
1347316161Smav	case IN_RSRC_UNAVAIL:
1348316161Smav		isp_prt(isp, ISP_LOGINFO, "Firmware out of ATIOs");
1349316161Smav		isp_async(isp, ISPASYNC_TARGET_NOTIFY_ACK, inp);
1350316161Smav		return;
1351316161Smav	}
1352316161Smav
1353316161Smav	if (ISP_CAP_2KLOGIN(isp))
1354316161Smav		nphdl = ((in_fcentry_e_t *)inp)->in_iid;
1355316161Smav	else
1356316161Smav		nphdl = inp->in_iid;
1357316161Smav	if (isp_find_pdb_by_handle(isp, 0, nphdl, &lp)) {
1358316161Smav		wwn = lp->port_wwn;
1359316161Smav		sid = lp->portid;
1360316161Smav	} else {
1361316161Smav		wwn = INI_ANY;
1362316161Smav		sid = PORT_ANY;
1363316161Smav	}
1364316161Smav
1365316161Smav	ISP_MEMZERO(&notify, sizeof (isp_notify_t));
1366316161Smav	notify.nt_hba = isp;
1367316161Smav	notify.nt_wwn = wwn;
1368316161Smav	notify.nt_tgt = FCPARAM(isp, 0)->isp_wwpn;
1369316161Smav	notify.nt_nphdl = nphdl;
1370316161Smav	notify.nt_sid = sid;
1371316161Smav	notify.nt_did = PORT_ANY;
1372316161Smav	if (ISP_CAP_SCCFW(isp))
1373316161Smav		notify.nt_lun = inp->in_scclun;
1374316161Smav	else
1375316161Smav		notify.nt_lun = inp->in_lun;
1376316161Smav	notify.nt_tagval = inp->in_seqid;
1377316161Smav	notify.nt_tagval |= (((uint64_t)(isp->isp_serno++)) << 32);
1378316161Smav	notify.nt_need_ack = 1;
1379316161Smav	notify.nt_channel = 0;
1380316161Smav	notify.nt_lreserved = inp;
1381316161Smav
1382316161Smav	switch (status) {
1383316161Smav	case IN_RESET:
1384316161Smav		notify.nt_ncode = NT_BUS_RESET;
1385316161Smav		break;
1386316161Smav	case IN_PORT_LOGOUT:
1387316161Smav		notify.nt_ncode = NT_LOGOUT;
1388316161Smav		break;
1389316161Smav	case IN_ABORT_TASK:
1390316161Smav		notify.nt_ncode = NT_ABORT_TASK;
1391316161Smav		break;
1392316161Smav	case IN_GLOBAL_LOGO:
1393316161Smav		notify.nt_ncode = NT_GLOBAL_LOGOUT;
1394316161Smav		break;
1395316161Smav	case IN_PORT_CHANGED:
1396316161Smav		notify.nt_ncode = NT_CHANGED;
1397316161Smav		break;
1398316161Smav	default:
1399316161Smav		isp_prt(isp, ISP_LOGINFO, "%s: unhandled status (0x%x)",
1400316161Smav		    __func__, status);
1401316161Smav		isp_async(isp, ISPASYNC_TARGET_NOTIFY_ACK, inp);
1402316161Smav		return;
1403316161Smav	}
1404316161Smav	isp_async(isp, ISPASYNC_TARGET_NOTIFY, &notify);
1405316161Smav}
1406316161Smav
1407316161Smavstatic void
1408316162Smavisp_handle_notify_24xx(ispsoftc_t *isp, in_fcentry_24xx_t *inot)
1409316161Smav{
1410316162Smav	uint8_t chan;
1411316162Smav	uint16_t nphdl, prli_options = 0;
1412316162Smav	uint32_t portid;
1413316162Smav	fcportdb_t *lp;
1414316162Smav	char *msg = NULL;
1415316162Smav	uint8_t *ptr = (uint8_t *)inot;
1416316162Smav	uint64_t wwpn = INI_NONE, wwnn = INI_NONE;
1417316162Smav	isp_notify_t notify;
1418316162Smav	char buf[16];
1419196008Smjacob
1420316162Smav	nphdl = inot->in_nphdl;
1421316162Smav	if (nphdl != NIL_HANDLE) {
1422316162Smav		portid = inot->in_portid_hi << 16 | inot->in_portid_lo;
1423196008Smjacob	} else {
1424316162Smav		portid = PORT_ANY;
1425316162Smav	}
1426316162Smav
1427316162Smav	chan = ISP_GET_VPIDX(isp, inot->in_vpidx);
1428316162Smav	if (chan >= isp->isp_nchan &&
1429316162Smav	    inot->in_status != IN24XX_LIP_RESET &&
1430316162Smav	    inot->in_status != IN24XX_LINK_RESET &&
1431316162Smav	    inot->in_status != IN24XX_LINK_FAILED) {
1432316162Smav		isp_prt(isp, ISP_LOGWARN, "%s: Received INOT with status %x on VP %x",
1433316162Smav		    __func__, inot->in_status, chan);
1434316162Smav		isp_async(isp, ISPASYNC_TARGET_NOTIFY_ACK, inot);
1435316162Smav		return;
1436316162Smav	}
1437316162Smav
1438316162Smav	switch (inot->in_status) {
1439316162Smav	case IN24XX_ELS_RCVD:
1440316162Smav	{
1441316162Smav		/*
1442316162Smav		 * Note that we're just getting notification that an ELS was
1443316162Smav		 * received (possibly with some associated information sent
1444316162Smav		 * upstream).  This is *not* the same as being given the ELS
1445316162Smav		 * frame to accept or reject.
1446316162Smav		 */
1447316162Smav		switch (inot->in_status_subcode) {
1448316162Smav		case LOGO:
1449316162Smav			msg = "LOGO";
1450316162Smav			wwpn = be64dec(&ptr[IN24XX_PLOGI_WWPN_OFF]);
1451316162Smav			isp_del_wwn_entry(isp, chan, wwpn, nphdl, portid);
1452316162Smav			break;
1453316162Smav		case PRLO:
1454316162Smav			msg = "PRLO";
1455316162Smav			break;
1456316162Smav		case PLOGI:
1457316162Smav			msg = "PLOGI";
1458316162Smav			wwnn = be64dec(&ptr[IN24XX_PLOGI_WWNN_OFF]);
1459316162Smav			wwpn = be64dec(&ptr[IN24XX_PLOGI_WWPN_OFF]);
1460316162Smav			isp_add_wwn_entry(isp, chan, wwpn, wwnn,
1461316162Smav			    nphdl, portid, prli_options);
1462316162Smav			break;
1463316162Smav		case PRLI:
1464316162Smav			msg = "PRLI";
1465316162Smav			prli_options = inot->in_prli_options;
1466316162Smav			if (inot->in_flags & IN24XX_FLAG_PN_NN_VALID)
1467316162Smav				wwnn = be64dec(&ptr[IN24XX_PRLI_WWNN_OFF]);
1468316162Smav			wwpn = be64dec(&ptr[IN24XX_PRLI_WWPN_OFF]);
1469316162Smav			isp_add_wwn_entry(isp, chan, wwpn, wwnn,
1470316162Smav			    nphdl, portid, prli_options);
1471316162Smav			break;
1472316162Smav		case PDISC:
1473316162Smav			msg = "PDISC";
1474316162Smav			break;
1475316162Smav		case ADISC:
1476316162Smav			msg = "ADISC";
1477316162Smav			break;
1478316162Smav		default:
1479316162Smav			ISP_SNPRINTF(buf, sizeof (buf), "ELS 0x%x",
1480316162Smav			    inot->in_status_subcode);
1481316162Smav			msg = buf;
1482316162Smav			break;
1483196008Smjacob		}
1484316162Smav		if (inot->in_flags & IN24XX_FLAG_PUREX_IOCB) {
1485316162Smav			isp_prt(isp, ISP_LOGERR, "%s Chan %d ELS N-port handle %x"
1486316162Smav			    " PortID 0x%06x marked as needing a PUREX response",
1487316162Smav			    msg, chan, nphdl, portid);
1488316162Smav			break;
1489316162Smav		}
1490316162Smav		isp_prt(isp, ISP_LOGTDEBUG0, "%s Chan %d ELS N-port handle %x"
1491316162Smav		    " PortID 0x%06x RX_ID 0x%x OX_ID 0x%x", msg, chan, nphdl,
1492316162Smav		    portid, inot->in_rxid, inot->in_oxid);
1493316162Smav		isp_async(isp, ISPASYNC_TARGET_NOTIFY_ACK, inot);
1494316162Smav		break;
1495196008Smjacob	}
1496316162Smav
1497316149Smav	case IN24XX_PORT_LOGOUT:
1498316162Smav		msg = "PORT LOGOUT";
1499316162Smav		if (isp_find_pdb_by_handle(isp, chan, nphdl, &lp))
1500316162Smav			isp_del_wwn_entry(isp, chan, lp->port_wwn, nphdl, lp->portid);
1501316162Smav		/* FALLTHROUGH */
1502316149Smav	case IN24XX_PORT_CHANGED:
1503316162Smav		if (msg == NULL)
1504316162Smav			msg = "PORT CHANGED";
1505316162Smav		/* FALLTHROUGH */
1506316162Smav	case IN24XX_LIP_RESET:
1507316162Smav		if (msg == NULL)
1508316162Smav			msg = "LIP RESET";
1509316162Smav		isp_prt(isp, ISP_LOGINFO, "Chan %d %s (sub-status 0x%x) for "
1510316162Smav		    "N-port handle 0x%x",
1511316162Smav		    chan, msg, inot->in_status_subcode, nphdl);
1512316162Smav
1513316162Smav		/*
1514316162Smav		 * All subcodes here are irrelevant. What is relevant
1515316162Smav		 * is that we need to terminate all active commands from
1516316162Smav		 * this initiator (known by N-port handle).
1517316162Smav		 */
1518316162Smav		/* XXX IMPLEMENT XXX */
1519316162Smav		isp_async(isp, ISPASYNC_TARGET_NOTIFY_ACK, inot);
1520316162Smav		break;
1521316162Smav
1522316149Smav	case IN24XX_SRR_RCVD:
1523316162Smav#ifdef	ISP_TARGET_MODE
1524316162Smav		ISP_MEMZERO(&notify, sizeof (isp_notify_t));
1525316162Smav		notify.nt_hba = isp;
1526316162Smav		notify.nt_wwn = INI_ANY;
1527316162Smav		notify.nt_tgt = FCPARAM(isp, chan)->isp_wwpn;
1528316162Smav		notify.nt_nphdl = nphdl;
1529316162Smav		notify.nt_sid = portid;
1530316162Smav		notify.nt_did = PORT_ANY;
1531316162Smav		notify.nt_lun = LUN_ANY;
1532316162Smav		notify.nt_tagval = inot->in_rxid;
1533316162Smav		notify.nt_tagval |= ((uint64_t)inot->in_srr_rxid << 32);
1534316162Smav		notify.nt_need_ack = 1;
1535316162Smav		notify.nt_channel = chan;
1536316162Smav		notify.nt_lreserved = inot;
1537316162Smav		notify.nt_ncode = NT_SRR;
1538316162Smav		isp_async(isp, ISPASYNC_TARGET_NOTIFY, &notify);
1539316149Smav		break;
1540316162Smav#else
1541316162Smav		if (msg == NULL)
1542316162Smav			msg = "SRR RCVD";
1543316162Smav		/* FALLTHROUGH */
1544316162Smav#endif
1545316162Smav	case IN24XX_LINK_RESET:
1546316162Smav		if (msg == NULL)
1547316162Smav			msg = "LINK RESET";
1548316162Smav	case IN24XX_LINK_FAILED:
1549316162Smav		if (msg == NULL)
1550316162Smav			msg = "LINK FAILED";
1551316149Smav	default:
1552316162Smav		isp_prt(isp, ISP_LOGWARN, "Chan %d %s", chan, msg);
1553316162Smav		isp_async(isp, ISPASYNC_TARGET_NOTIFY_ACK, inot);
1554316149Smav		break;
1555196008Smjacob	}
1556196008Smjacob}
155755373Smjacob#endif
1558