1250963Sachim/*-
2250963Sachim * Copyright (c) 2000 Michael Smith
3250963Sachim * Copyright (c) 2001 Scott Long
4250963Sachim * Copyright (c) 2000 BSDi
5250963Sachim * Copyright (c) 2001-2010 Adaptec, Inc.
6250963Sachim * Copyright (c) 2010-2012 PMC-Sierra, Inc.
7250963Sachim * All rights reserved.
8250963Sachim *
9250963Sachim * Redistribution and use in source and binary forms, with or without
10250963Sachim * modification, are permitted provided that the following conditions
11250963Sachim * are met:
12250963Sachim * 1. Redistributions of source code must retain the above copyright
13250963Sachim *    notice, this list of conditions and the following disclaimer.
14250963Sachim * 2. Redistributions in binary form must reproduce the above copyright
15250963Sachim *    notice, this list of conditions and the following disclaimer in the
16250963Sachim *    documentation and/or other materials provided with the distribution.
17250963Sachim *
18250963Sachim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19250963Sachim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20250963Sachim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21250963Sachim * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22250963Sachim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23250963Sachim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24250963Sachim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25250963Sachim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26250963Sachim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27250963Sachim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28250963Sachim * SUCH DAMAGE.
29250963Sachim */
30250963Sachim
31250963Sachim#include <sys/cdefs.h>
32250963Sachim__FBSDID("$FreeBSD$");
33250963Sachim
34250963Sachim/*
35250963Sachim * Driver for the Adaptec by PMC Series 6,7,8,... families of RAID controllers
36250963Sachim */
37250963Sachim#define AAC_DRIVERNAME			"aacraid"
38250963Sachim
39250963Sachim#include "opt_aacraid.h"
40250963Sachim
41250963Sachim/* #include <stddef.h> */
42250963Sachim#include <sys/param.h>
43250963Sachim#include <sys/systm.h>
44250963Sachim#include <sys/malloc.h>
45250963Sachim#include <sys/kernel.h>
46250963Sachim#include <sys/kthread.h>
47250963Sachim#include <sys/sysctl.h>
48250963Sachim#include <sys/poll.h>
49250963Sachim#include <sys/ioccom.h>
50250963Sachim
51250963Sachim#include <sys/bus.h>
52250963Sachim#include <sys/conf.h>
53250963Sachim#include <sys/signalvar.h>
54250963Sachim#include <sys/time.h>
55250963Sachim#include <sys/eventhandler.h>
56250963Sachim#include <sys/rman.h>
57250963Sachim
58250963Sachim#include <machine/bus.h>
59250963Sachim#include <sys/bus_dma.h>
60250963Sachim#include <machine/resource.h>
61250963Sachim
62250963Sachim#include <dev/pci/pcireg.h>
63250963Sachim#include <dev/pci/pcivar.h>
64250963Sachim
65250963Sachim#include <dev/aacraid/aacraid_reg.h>
66250963Sachim#include <sys/aac_ioctl.h>
67250963Sachim#include <dev/aacraid/aacraid_debug.h>
68250963Sachim#include <dev/aacraid/aacraid_var.h>
69250963Sachim
70250963Sachim#ifndef FILTER_HANDLED
71250963Sachim#define FILTER_HANDLED	0x02
72250963Sachim#endif
73250963Sachim
74250963Sachimstatic void	aac_add_container(struct aac_softc *sc,
75250963Sachim				  struct aac_mntinforesp *mir, int f,
76250963Sachim				  u_int32_t uid);
77250963Sachimstatic void	aac_get_bus_info(struct aac_softc *sc);
78250963Sachimstatic void	aac_container_bus(struct aac_softc *sc);
79250963Sachimstatic void	aac_daemon(void *arg);
80250963Sachimstatic int aac_convert_sgraw2(struct aac_softc *sc, struct aac_raw_io2 *raw,
81250963Sachim							  int pages, int nseg, int nseg_new);
82250963Sachim
83250963Sachim/* Command Processing */
84250963Sachimstatic void	aac_timeout(struct aac_softc *sc);
85250963Sachimstatic void	aac_command_thread(struct aac_softc *sc);
86250963Sachimstatic int	aac_sync_fib(struct aac_softc *sc, u_int32_t command,
87250963Sachim				     u_int32_t xferstate, struct aac_fib *fib,
88250963Sachim				     u_int16_t datasize);
89250963Sachim/* Command Buffer Management */
90250963Sachimstatic void	aac_map_command_helper(void *arg, bus_dma_segment_t *segs,
91250963Sachim				       int nseg, int error);
92250963Sachimstatic int	aac_alloc_commands(struct aac_softc *sc);
93250963Sachimstatic void	aac_free_commands(struct aac_softc *sc);
94250963Sachimstatic void	aac_unmap_command(struct aac_command *cm);
95250963Sachim
96250963Sachim/* Hardware Interface */
97250963Sachimstatic int	aac_alloc(struct aac_softc *sc);
98250963Sachimstatic void	aac_common_map(void *arg, bus_dma_segment_t *segs, int nseg,
99250963Sachim			       int error);
100250963Sachimstatic int	aac_check_firmware(struct aac_softc *sc);
101250963Sachimstatic int	aac_init(struct aac_softc *sc);
102250963Sachimstatic int	aac_setup_intr(struct aac_softc *sc);
103250963Sachim
104250963Sachim/* PMC SRC interface */
105250963Sachimstatic int	aac_src_get_fwstatus(struct aac_softc *sc);
106250963Sachimstatic void	aac_src_qnotify(struct aac_softc *sc, int qbit);
107250963Sachimstatic int	aac_src_get_istatus(struct aac_softc *sc);
108250963Sachimstatic void	aac_src_clear_istatus(struct aac_softc *sc, int mask);
109250963Sachimstatic void	aac_src_set_mailbox(struct aac_softc *sc, u_int32_t command,
110250963Sachim				    u_int32_t arg0, u_int32_t arg1,
111250963Sachim				    u_int32_t arg2, u_int32_t arg3);
112250963Sachimstatic int	aac_src_get_mailbox(struct aac_softc *sc, int mb);
113250963Sachimstatic void	aac_src_set_interrupts(struct aac_softc *sc, int enable);
114250963Sachimstatic int aac_src_send_command(struct aac_softc *sc, struct aac_command *cm);
115250963Sachimstatic int aac_src_get_outb_queue(struct aac_softc *sc);
116250963Sachimstatic void aac_src_set_outb_queue(struct aac_softc *sc, int index);
117250963Sachim
118250963Sachimstruct aac_interface aacraid_src_interface = {
119250963Sachim	aac_src_get_fwstatus,
120250963Sachim	aac_src_qnotify,
121250963Sachim	aac_src_get_istatus,
122250963Sachim	aac_src_clear_istatus,
123250963Sachim	aac_src_set_mailbox,
124250963Sachim	aac_src_get_mailbox,
125250963Sachim	aac_src_set_interrupts,
126250963Sachim	aac_src_send_command,
127250963Sachim	aac_src_get_outb_queue,
128250963Sachim	aac_src_set_outb_queue
129250963Sachim};
130250963Sachim
131250963Sachim/* PMC SRCv interface */
132250963Sachimstatic void	aac_srcv_set_mailbox(struct aac_softc *sc, u_int32_t command,
133250963Sachim				    u_int32_t arg0, u_int32_t arg1,
134250963Sachim				    u_int32_t arg2, u_int32_t arg3);
135250963Sachimstatic int	aac_srcv_get_mailbox(struct aac_softc *sc, int mb);
136250963Sachim
137250963Sachimstruct aac_interface aacraid_srcv_interface = {
138250963Sachim	aac_src_get_fwstatus,
139250963Sachim	aac_src_qnotify,
140250963Sachim	aac_src_get_istatus,
141250963Sachim	aac_src_clear_istatus,
142250963Sachim	aac_srcv_set_mailbox,
143250963Sachim	aac_srcv_get_mailbox,
144250963Sachim	aac_src_set_interrupts,
145250963Sachim	aac_src_send_command,
146250963Sachim	aac_src_get_outb_queue,
147250963Sachim	aac_src_set_outb_queue
148250963Sachim};
149250963Sachim
150250963Sachim/* Debugging and Diagnostics */
151250963Sachimstatic struct aac_code_lookup aac_cpu_variant[] = {
152250963Sachim	{"i960JX",		CPUI960_JX},
153250963Sachim	{"i960CX",		CPUI960_CX},
154250963Sachim	{"i960HX",		CPUI960_HX},
155250963Sachim	{"i960RX",		CPUI960_RX},
156250963Sachim	{"i960 80303",		CPUI960_80303},
157250963Sachim	{"StrongARM SA110",	CPUARM_SA110},
158250963Sachim	{"PPC603e",		CPUPPC_603e},
159250963Sachim	{"XScale 80321",	CPU_XSCALE_80321},
160250963Sachim	{"MIPS 4KC",		CPU_MIPS_4KC},
161250963Sachim	{"MIPS 5KC",		CPU_MIPS_5KC},
162250963Sachim	{"Unknown StrongARM",	CPUARM_xxx},
163250963Sachim	{"Unknown PowerPC",	CPUPPC_xxx},
164250963Sachim	{NULL, 0},
165250963Sachim	{"Unknown processor",	0}
166250963Sachim};
167250963Sachim
168250963Sachimstatic struct aac_code_lookup aac_battery_platform[] = {
169250963Sachim	{"required battery present",		PLATFORM_BAT_REQ_PRESENT},
170250963Sachim	{"REQUIRED BATTERY NOT PRESENT",	PLATFORM_BAT_REQ_NOTPRESENT},
171250963Sachim	{"optional battery present",		PLATFORM_BAT_OPT_PRESENT},
172250963Sachim	{"optional battery not installed",	PLATFORM_BAT_OPT_NOTPRESENT},
173250963Sachim	{"no battery support",			PLATFORM_BAT_NOT_SUPPORTED},
174250963Sachim	{NULL, 0},
175250963Sachim	{"unknown battery platform",		0}
176250963Sachim};
177250963Sachimstatic void	aac_describe_controller(struct aac_softc *sc);
178250963Sachimstatic char	*aac_describe_code(struct aac_code_lookup *table,
179250963Sachim				   u_int32_t code);
180250963Sachim
181250963Sachim/* Management Interface */
182250963Sachimstatic d_open_t		aac_open;
183250963Sachimstatic d_ioctl_t	aac_ioctl;
184250963Sachimstatic d_poll_t		aac_poll;
185250963Sachim#if __FreeBSD_version >= 702000
186250963Sachimstatic void		aac_cdevpriv_dtor(void *arg);
187250963Sachim#else
188250963Sachimstatic d_close_t	aac_close;
189250963Sachim#endif
190250963Sachimstatic int	aac_ioctl_sendfib(struct aac_softc *sc, caddr_t ufib);
191250963Sachimstatic int	aac_ioctl_send_raw_srb(struct aac_softc *sc, caddr_t arg);
192250963Sachimstatic void	aac_handle_aif(struct aac_softc *sc, struct aac_fib *fib);
193250963Sachimstatic void	aac_request_aif(struct aac_softc *sc);
194250963Sachimstatic int	aac_rev_check(struct aac_softc *sc, caddr_t udata);
195250963Sachimstatic int	aac_open_aif(struct aac_softc *sc, caddr_t arg);
196250963Sachimstatic int	aac_close_aif(struct aac_softc *sc, caddr_t arg);
197250963Sachimstatic int	aac_getnext_aif(struct aac_softc *sc, caddr_t arg);
198250963Sachimstatic int	aac_return_aif(struct aac_softc *sc,
199250963Sachim			       struct aac_fib_context *ctx, caddr_t uptr);
200250963Sachimstatic int	aac_query_disk(struct aac_softc *sc, caddr_t uptr);
201250963Sachimstatic int	aac_get_pci_info(struct aac_softc *sc, caddr_t uptr);
202250963Sachimstatic int	aac_supported_features(struct aac_softc *sc, caddr_t uptr);
203250963Sachimstatic void	aac_ioctl_event(struct aac_softc *sc,
204250963Sachim				struct aac_event *event, void *arg);
205250963Sachimstatic int	aac_reset_adapter(struct aac_softc *sc);
206250963Sachimstatic int	aac_get_container_info(struct aac_softc *sc,
207250963Sachim				       struct aac_fib *fib, int cid,
208250963Sachim				       struct aac_mntinforesp *mir,
209250963Sachim				       u_int32_t *uid);
210250963Sachimstatic u_int32_t
211250963Sachim	aac_check_adapter_health(struct aac_softc *sc, u_int8_t *bled);
212250963Sachim
213250963Sachimstatic struct cdevsw aacraid_cdevsw = {
214250963Sachim	.d_version =	D_VERSION,
215250963Sachim	.d_flags =	D_NEEDGIANT,
216250963Sachim	.d_open =	aac_open,
217250963Sachim#if __FreeBSD_version < 702000
218250963Sachim	.d_close =	aac_close,
219250963Sachim#endif
220250963Sachim	.d_ioctl =	aac_ioctl,
221250963Sachim	.d_poll =	aac_poll,
222250963Sachim	.d_name =	"aacraid",
223250963Sachim};
224250963Sachim
225250963SachimMALLOC_DEFINE(M_AACRAIDBUF, "aacraid_buf", "Buffers for the AACRAID driver");
226250963Sachim
227250963Sachim/* sysctl node */
228250963SachimSYSCTL_NODE(_hw, OID_AUTO, aacraid, CTLFLAG_RD, 0, "AACRAID driver parameters");
229250963Sachim
230250963Sachim/*
231250963Sachim * Device Interface
232250963Sachim */
233250963Sachim
234250963Sachim/*
235250963Sachim * Initialize the controller and softc
236250963Sachim */
237250963Sachimint
238250963Sachimaacraid_attach(struct aac_softc *sc)
239250963Sachim{
240250963Sachim	int error, unit;
241250963Sachim	struct aac_fib *fib;
242250963Sachim	struct aac_mntinforesp mir;
243250963Sachim	int count = 0, i = 0;
244250963Sachim	u_int32_t uid;
245250963Sachim
246250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
247250963Sachim	sc->hint_flags = device_get_flags(sc->aac_dev);
248250963Sachim	/*
249250963Sachim	 * Initialize per-controller queues.
250250963Sachim	 */
251250963Sachim	aac_initq_free(sc);
252250963Sachim	aac_initq_ready(sc);
253250963Sachim	aac_initq_busy(sc);
254250963Sachim
255250963Sachim	/* mark controller as suspended until we get ourselves organised */
256250963Sachim	sc->aac_state |= AAC_STATE_SUSPEND;
257250963Sachim
258250963Sachim	/*
259250963Sachim	 * Check that the firmware on the card is supported.
260250963Sachim	 */
261250963Sachim	if ((error = aac_check_firmware(sc)) != 0)
262250963Sachim		return(error);
263250963Sachim
264250963Sachim	/*
265250963Sachim	 * Initialize locks
266250963Sachim	 */
267250963Sachim	mtx_init(&sc->aac_io_lock, "AACRAID I/O lock", NULL, MTX_DEF);
268250963Sachim	TAILQ_INIT(&sc->aac_container_tqh);
269250963Sachim	TAILQ_INIT(&sc->aac_ev_cmfree);
270250963Sachim
271250963Sachim#if __FreeBSD_version >= 800000
272250963Sachim	/* Initialize the clock daemon callout. */
273250963Sachim	callout_init_mtx(&sc->aac_daemontime, &sc->aac_io_lock, 0);
274250963Sachim#endif
275250963Sachim	/*
276250963Sachim	 * Initialize the adapter.
277250963Sachim	 */
278250963Sachim	if ((error = aac_alloc(sc)) != 0)
279250963Sachim		return(error);
280250963Sachim	if (!(sc->flags & AAC_FLAGS_SYNC_MODE)) {
281250963Sachim		if ((error = aac_init(sc)) != 0)
282250963Sachim			return(error);
283250963Sachim	}
284250963Sachim
285250963Sachim	/*
286250963Sachim	 * Allocate and connect our interrupt.
287250963Sachim	 */
288250963Sachim	if ((error = aac_setup_intr(sc)) != 0)
289250963Sachim		return(error);
290250963Sachim
291250963Sachim	/*
292250963Sachim	 * Print a little information about the controller.
293250963Sachim	 */
294250963Sachim	aac_describe_controller(sc);
295250963Sachim
296250963Sachim	/*
297250963Sachim	 * Make the control device.
298250963Sachim	 */
299250963Sachim	unit = device_get_unit(sc->aac_dev);
300250963Sachim	sc->aac_dev_t = make_dev(&aacraid_cdevsw, unit, UID_ROOT, GID_OPERATOR,
301250963Sachim				 0640, "aacraid%d", unit);
302250963Sachim	sc->aac_dev_t->si_drv1 = sc;
303250963Sachim
304250963Sachim	/* Create the AIF thread */
305250963Sachim	if (aac_kthread_create((void(*)(void *))aac_command_thread, sc,
306250963Sachim		   &sc->aifthread, 0, 0, "aacraid%daif", unit))
307250963Sachim		panic("Could not create AIF thread");
308250963Sachim
309250963Sachim	/* Register the shutdown method to only be called post-dump */
310250963Sachim	if ((sc->eh = EVENTHANDLER_REGISTER(shutdown_final, aacraid_shutdown,
311250963Sachim	    sc->aac_dev, SHUTDOWN_PRI_DEFAULT)) == NULL)
312250963Sachim		device_printf(sc->aac_dev,
313250963Sachim			      "shutdown event registration failed\n");
314250963Sachim
315250963Sachim	/* Find containers */
316250963Sachim	mtx_lock(&sc->aac_io_lock);
317250963Sachim	aac_alloc_sync_fib(sc, &fib);
318250963Sachim	/* loop over possible containers */
319250963Sachim	do {
320250963Sachim		if ((aac_get_container_info(sc, fib, i, &mir, &uid)) != 0)
321250963Sachim			continue;
322250963Sachim		if (i == 0)
323250963Sachim			count = mir.MntRespCount;
324250963Sachim		aac_add_container(sc, &mir, 0, uid);
325250963Sachim		i++;
326250963Sachim	} while ((i < count) && (i < AAC_MAX_CONTAINERS));
327250963Sachim	aac_release_sync_fib(sc);
328250963Sachim	mtx_unlock(&sc->aac_io_lock);
329250963Sachim
330250963Sachim	/* Register with CAM for the containers */
331250963Sachim	TAILQ_INIT(&sc->aac_sim_tqh);
332250963Sachim	aac_container_bus(sc);
333250963Sachim	/* Register with CAM for the non-DASD devices */
334250963Sachim	if ((sc->flags & AAC_FLAGS_ENABLE_CAM) != 0)
335250963Sachim		aac_get_bus_info(sc);
336250963Sachim
337250963Sachim	/* poke the bus to actually attach the child devices */
338250963Sachim	bus_generic_attach(sc->aac_dev);
339250963Sachim
340250963Sachim	/* mark the controller up */
341250963Sachim	sc->aac_state &= ~AAC_STATE_SUSPEND;
342250963Sachim
343250963Sachim	/* enable interrupts now */
344250963Sachim	AAC_UNMASK_INTERRUPTS(sc);
345250963Sachim
346250963Sachim#if __FreeBSD_version >= 800000
347250963Sachim	mtx_lock(&sc->aac_io_lock);
348250963Sachim	callout_reset(&sc->aac_daemontime, 60 * hz, aac_daemon, sc);
349250963Sachim	mtx_unlock(&sc->aac_io_lock);
350250963Sachim#else
351250963Sachim	{
352250963Sachim		struct timeval tv;
353250963Sachim		tv.tv_sec = 60;
354250963Sachim		tv.tv_usec = 0;
355250963Sachim		sc->timeout_id = timeout(aac_daemon, (void *)sc, tvtohz(&tv));
356250963Sachim	}
357250963Sachim#endif
358250963Sachim
359250963Sachim	return(0);
360250963Sachim}
361250963Sachim
362250963Sachimstatic void
363250963Sachimaac_daemon(void *arg)
364250963Sachim{
365250963Sachim	struct aac_softc *sc;
366250963Sachim	struct timeval tv;
367250963Sachim	struct aac_command *cm;
368250963Sachim	struct aac_fib *fib;
369250963Sachim
370250963Sachim	sc = arg;
371250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
372250963Sachim
373250963Sachim#if __FreeBSD_version >= 800000
374250963Sachim	mtx_assert(&sc->aac_io_lock, MA_OWNED);
375250963Sachim	if (callout_pending(&sc->aac_daemontime) ||
376250963Sachim	    callout_active(&sc->aac_daemontime) == 0)
377250963Sachim		return;
378250963Sachim#else
379250963Sachim	mtx_lock(&sc->aac_io_lock);
380250963Sachim#endif
381250963Sachim	getmicrotime(&tv);
382250963Sachim
383250963Sachim	if (!aacraid_alloc_command(sc, &cm)) {
384250963Sachim		fib = cm->cm_fib;
385250963Sachim		cm->cm_timestamp = time_uptime;
386250963Sachim		cm->cm_datalen = 0;
387250963Sachim		cm->cm_flags |= AAC_CMD_WAIT;
388250963Sachim
389250963Sachim		fib->Header.Size =
390250963Sachim			sizeof(struct aac_fib_header) + sizeof(u_int32_t);
391250963Sachim		fib->Header.XferState =
392250963Sachim			AAC_FIBSTATE_HOSTOWNED   |
393250963Sachim			AAC_FIBSTATE_INITIALISED |
394250963Sachim			AAC_FIBSTATE_EMPTY	 |
395250963Sachim			AAC_FIBSTATE_FROMHOST	 |
396250963Sachim			AAC_FIBSTATE_REXPECTED   |
397250963Sachim			AAC_FIBSTATE_NORM	 |
398250963Sachim			AAC_FIBSTATE_ASYNC	 |
399250963Sachim			AAC_FIBSTATE_FAST_RESPONSE;
400250963Sachim		fib->Header.Command = SendHostTime;
401250963Sachim		*(uint32_t *)fib->data = tv.tv_sec;
402250963Sachim
403250963Sachim		aacraid_map_command_sg(cm, NULL, 0, 0);
404250963Sachim		aacraid_release_command(cm);
405250963Sachim	}
406250963Sachim
407250963Sachim#if __FreeBSD_version >= 800000
408250963Sachim	callout_schedule(&sc->aac_daemontime, 30 * 60 * hz);
409250963Sachim#else
410250963Sachim	mtx_unlock(&sc->aac_io_lock);
411250963Sachim	tv.tv_sec = 30 * 60;
412250963Sachim	tv.tv_usec = 0;
413250963Sachim	sc->timeout_id = timeout(aac_daemon, (void *)sc, tvtohz(&tv));
414250963Sachim#endif
415250963Sachim}
416250963Sachim
417250963Sachimvoid
418250963Sachimaacraid_add_event(struct aac_softc *sc, struct aac_event *event)
419250963Sachim{
420250963Sachim
421250963Sachim	switch (event->ev_type & AAC_EVENT_MASK) {
422250963Sachim	case AAC_EVENT_CMFREE:
423250963Sachim		TAILQ_INSERT_TAIL(&sc->aac_ev_cmfree, event, ev_links);
424250963Sachim		break;
425250963Sachim	default:
426250963Sachim		device_printf(sc->aac_dev, "aac_add event: unknown event %d\n",
427250963Sachim		    event->ev_type);
428250963Sachim		break;
429250963Sachim	}
430250963Sachim
431250963Sachim	return;
432250963Sachim}
433250963Sachim
434250963Sachim/*
435250963Sachim * Request information of container #cid
436250963Sachim */
437250963Sachimstatic int
438250963Sachimaac_get_container_info(struct aac_softc *sc, struct aac_fib *sync_fib, int cid,
439250963Sachim		       struct aac_mntinforesp *mir, u_int32_t *uid)
440250963Sachim{
441250963Sachim	struct aac_command *cm;
442250963Sachim	struct aac_fib *fib;
443250963Sachim	struct aac_mntinfo *mi;
444250963Sachim	struct aac_cnt_config *ccfg;
445250963Sachim
446250963Sachim	if (sync_fib == NULL) {
447250963Sachim		if (aacraid_alloc_command(sc, &cm)) {
448250963Sachim			device_printf(sc->aac_dev,
449250963Sachim				"Warning, no free command available\n");
450250963Sachim			return (-1);
451250963Sachim		}
452250963Sachim		fib = cm->cm_fib;
453250963Sachim	} else {
454250963Sachim		fib = sync_fib;
455250963Sachim	}
456250963Sachim
457250963Sachim	mi = (struct aac_mntinfo *)&fib->data[0];
458250963Sachim	/* 4KB support?, 64-bit LBA? */
459250963Sachim	if (sc->aac_support_opt2 & AAC_SUPPORTED_VARIABLE_BLOCK_SIZE)
460250963Sachim		mi->Command = VM_NameServeAllBlk;
461250963Sachim	else if (sc->flags & AAC_FLAGS_LBA_64BIT)
462250963Sachim		mi->Command = VM_NameServe64;
463250963Sachim	else
464250963Sachim		mi->Command = VM_NameServe;
465250963Sachim	mi->MntType = FT_FILESYS;
466250963Sachim	mi->MntCount = cid;
467250963Sachim
468250963Sachim	if (sync_fib) {
469250963Sachim		if (aac_sync_fib(sc, ContainerCommand, 0, fib,
470250963Sachim			 sizeof(struct aac_mntinfo))) {
471250963Sachim			device_printf(sc->aac_dev, "Error probing container %d\n", cid);
472250963Sachim			return (-1);
473250963Sachim		}
474250963Sachim	} else {
475250963Sachim		cm->cm_timestamp = time_uptime;
476250963Sachim		cm->cm_datalen = 0;
477250963Sachim
478250963Sachim		fib->Header.Size =
479250963Sachim			sizeof(struct aac_fib_header) + sizeof(struct aac_mntinfo);
480250963Sachim		fib->Header.XferState =
481250963Sachim			AAC_FIBSTATE_HOSTOWNED   |
482250963Sachim			AAC_FIBSTATE_INITIALISED |
483250963Sachim			AAC_FIBSTATE_EMPTY	 |
484250963Sachim			AAC_FIBSTATE_FROMHOST	 |
485250963Sachim			AAC_FIBSTATE_REXPECTED   |
486250963Sachim			AAC_FIBSTATE_NORM	 |
487250963Sachim			AAC_FIBSTATE_ASYNC	 |
488250963Sachim			AAC_FIBSTATE_FAST_RESPONSE;
489250963Sachim		fib->Header.Command = ContainerCommand;
490250963Sachim		if (aacraid_wait_command(cm) != 0) {
491250963Sachim			device_printf(sc->aac_dev, "Error probing container %d\n", cid);
492250963Sachim			aacraid_release_command(cm);
493250963Sachim			return (-1);
494250963Sachim		}
495250963Sachim	}
496250963Sachim	bcopy(&fib->data[0], mir, sizeof(struct aac_mntinforesp));
497250963Sachim
498250963Sachim	/* UID */
499250963Sachim	*uid = cid;
500250963Sachim	if (mir->MntTable[0].VolType != CT_NONE &&
501250963Sachim		!(mir->MntTable[0].ContentState & AAC_FSCS_HIDDEN)) {
502250963Sachim		if (!(sc->aac_support_opt2 & AAC_SUPPORTED_VARIABLE_BLOCK_SIZE))
503250963Sachim			mir->MntTable[0].ObjExtension.BlockSize = 0x200;
504250963Sachim
505250963Sachim		ccfg = (struct aac_cnt_config *)&fib->data[0];
506250963Sachim		bzero(ccfg, sizeof (*ccfg) - CT_PACKET_SIZE);
507250963Sachim		ccfg->Command = VM_ContainerConfig;
508250963Sachim		ccfg->CTCommand.command = CT_CID_TO_32BITS_UID;
509250963Sachim		ccfg->CTCommand.param[0] = cid;
510250963Sachim
511250963Sachim		if (sync_fib) {
512250963Sachim			if (aac_sync_fib(sc, ContainerCommand, 0, fib,
513250963Sachim				sizeof(struct aac_cnt_config) == 0) &&
514250963Sachim				ccfg->CTCommand.param[0] == ST_OK &&
515250963Sachim				mir->MntTable[0].VolType != CT_PASSTHRU)
516250963Sachim				*uid = ccfg->CTCommand.param[1];
517250963Sachim		} else {
518250963Sachim			fib->Header.Size =
519250963Sachim				sizeof(struct aac_fib_header) + sizeof(struct aac_cnt_config);
520250963Sachim			fib->Header.XferState =
521250963Sachim				AAC_FIBSTATE_HOSTOWNED   |
522250963Sachim				AAC_FIBSTATE_INITIALISED |
523250963Sachim				AAC_FIBSTATE_EMPTY	 |
524250963Sachim				AAC_FIBSTATE_FROMHOST	 |
525250963Sachim				AAC_FIBSTATE_REXPECTED   |
526250963Sachim				AAC_FIBSTATE_NORM	 |
527250963Sachim				AAC_FIBSTATE_ASYNC	 |
528250963Sachim				AAC_FIBSTATE_FAST_RESPONSE;
529250963Sachim			fib->Header.Command = ContainerCommand;
530250963Sachim			if (aacraid_wait_command(cm) == 0 &&
531250963Sachim				ccfg->CTCommand.param[0] == ST_OK &&
532250963Sachim				mir->MntTable[0].VolType != CT_PASSTHRU)
533250963Sachim				*uid = ccfg->CTCommand.param[1];
534250963Sachim			aacraid_release_command(cm);
535250963Sachim		}
536250963Sachim	}
537250963Sachim
538250963Sachim	return (0);
539250963Sachim}
540250963Sachim
541250963Sachim/*
542250963Sachim * Create a device to represent a new container
543250963Sachim */
544250963Sachimstatic void
545250963Sachimaac_add_container(struct aac_softc *sc, struct aac_mntinforesp *mir, int f,
546250963Sachim		  u_int32_t uid)
547250963Sachim{
548250963Sachim	struct aac_container *co;
549250963Sachim
550250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
551250963Sachim
552250963Sachim	/*
553250963Sachim	 * Check container volume type for validity.  Note that many of
554250963Sachim	 * the possible types may never show up.
555250963Sachim	 */
556250963Sachim	if ((mir->Status == ST_OK) && (mir->MntTable[0].VolType != CT_NONE)) {
557250963Sachim		co = (struct aac_container *)malloc(sizeof *co, M_AACRAIDBUF,
558250963Sachim		       M_NOWAIT | M_ZERO);
559250963Sachim		if (co == NULL) {
560250963Sachim			panic("Out of memory?!");
561250963Sachim		}
562250963Sachim
563250963Sachim		co->co_found = f;
564250963Sachim		bcopy(&mir->MntTable[0], &co->co_mntobj,
565250963Sachim		      sizeof(struct aac_mntobj));
566250963Sachim		co->co_uid = uid;
567250963Sachim		TAILQ_INSERT_TAIL(&sc->aac_container_tqh, co, co_link);
568250963Sachim	}
569250963Sachim}
570250963Sachim
571250963Sachim/*
572250963Sachim * Allocate resources associated with (sc)
573250963Sachim */
574250963Sachimstatic int
575250963Sachimaac_alloc(struct aac_softc *sc)
576250963Sachim{
577250963Sachim	bus_size_t maxsize;
578250963Sachim
579250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
580250963Sachim
581250963Sachim	/*
582250963Sachim	 * Create DMA tag for mapping buffers into controller-addressable space.
583250963Sachim	 */
584250963Sachim	if (bus_dma_tag_create(sc->aac_parent_dmat, 	/* parent */
585250963Sachim			       1, 0, 			/* algnmnt, boundary */
586250963Sachim			       (sc->flags & AAC_FLAGS_SG_64BIT) ?
587250963Sachim			       BUS_SPACE_MAXADDR :
588250963Sachim			       BUS_SPACE_MAXADDR_32BIT,	/* lowaddr */
589250963Sachim			       BUS_SPACE_MAXADDR, 	/* highaddr */
590250963Sachim			       NULL, NULL, 		/* filter, filterarg */
591250963Sachim			       MAXBSIZE,		/* maxsize */
592250963Sachim			       sc->aac_sg_tablesize,	/* nsegments */
593250963Sachim			       MAXBSIZE,		/* maxsegsize */
594250963Sachim			       BUS_DMA_ALLOCNOW,	/* flags */
595250963Sachim			       busdma_lock_mutex,	/* lockfunc */
596250963Sachim			       &sc->aac_io_lock,	/* lockfuncarg */
597250963Sachim			       &sc->aac_buffer_dmat)) {
598250963Sachim		device_printf(sc->aac_dev, "can't allocate buffer DMA tag\n");
599250963Sachim		return (ENOMEM);
600250963Sachim	}
601250963Sachim
602250963Sachim	/*
603250963Sachim	 * Create DMA tag for mapping FIBs into controller-addressable space..
604250963Sachim	 */
605250963Sachim	if (sc->flags & AAC_FLAGS_NEW_COMM_TYPE1)
606250963Sachim		maxsize = sc->aac_max_fibs_alloc * (sc->aac_max_fib_size +
607250963Sachim			sizeof(struct aac_fib_xporthdr) + 31);
608250963Sachim	else
609250963Sachim		maxsize = sc->aac_max_fibs_alloc * (sc->aac_max_fib_size + 31);
610250963Sachim	if (bus_dma_tag_create(sc->aac_parent_dmat,	/* parent */
611250963Sachim			       1, 0, 			/* algnmnt, boundary */
612250963Sachim			       (sc->flags & AAC_FLAGS_4GB_WINDOW) ?
613250963Sachim			       BUS_SPACE_MAXADDR_32BIT :
614250963Sachim			       0x7fffffff,		/* lowaddr */
615250963Sachim			       BUS_SPACE_MAXADDR, 	/* highaddr */
616250963Sachim			       NULL, NULL, 		/* filter, filterarg */
617250963Sachim			       maxsize,  		/* maxsize */
618250963Sachim			       1,			/* nsegments */
619250963Sachim			       maxsize,			/* maxsize */
620250963Sachim			       0,			/* flags */
621250963Sachim			       NULL, NULL,		/* No locking needed */
622250963Sachim			       &sc->aac_fib_dmat)) {
623250963Sachim		device_printf(sc->aac_dev, "can't allocate FIB DMA tag\n");
624250963Sachim		return (ENOMEM);
625250963Sachim	}
626250963Sachim
627250963Sachim	/*
628250963Sachim	 * Create DMA tag for the common structure and allocate it.
629250963Sachim	 */
630250963Sachim	maxsize = sizeof(struct aac_common);
631250963Sachim	maxsize += sc->aac_max_fibs * sizeof(u_int32_t);
632250963Sachim	if (bus_dma_tag_create(sc->aac_parent_dmat, 	/* parent */
633250963Sachim			       1, 0,			/* algnmnt, boundary */
634250963Sachim			       (sc->flags & AAC_FLAGS_4GB_WINDOW) ?
635250963Sachim			       BUS_SPACE_MAXADDR_32BIT :
636250963Sachim			       0x7fffffff,		/* lowaddr */
637250963Sachim			       BUS_SPACE_MAXADDR, 	/* highaddr */
638250963Sachim			       NULL, NULL, 		/* filter, filterarg */
639250963Sachim			       maxsize, 		/* maxsize */
640250963Sachim			       1,			/* nsegments */
641250963Sachim			       maxsize,			/* maxsegsize */
642250963Sachim			       0,			/* flags */
643250963Sachim			       NULL, NULL,		/* No locking needed */
644250963Sachim			       &sc->aac_common_dmat)) {
645250963Sachim		device_printf(sc->aac_dev,
646250963Sachim			      "can't allocate common structure DMA tag\n");
647250963Sachim		return (ENOMEM);
648250963Sachim	}
649250963Sachim	if (bus_dmamem_alloc(sc->aac_common_dmat, (void **)&sc->aac_common,
650250963Sachim			     BUS_DMA_NOWAIT, &sc->aac_common_dmamap)) {
651250963Sachim		device_printf(sc->aac_dev, "can't allocate common structure\n");
652250963Sachim		return (ENOMEM);
653250963Sachim	}
654250963Sachim
655250963Sachim	(void)bus_dmamap_load(sc->aac_common_dmat, sc->aac_common_dmamap,
656250963Sachim			sc->aac_common, maxsize,
657250963Sachim			aac_common_map, sc, 0);
658250963Sachim	bzero(sc->aac_common, maxsize);
659250963Sachim
660250963Sachim	/* Allocate some FIBs and associated command structs */
661250963Sachim	TAILQ_INIT(&sc->aac_fibmap_tqh);
662250963Sachim	sc->aac_commands = malloc(sc->aac_max_fibs * sizeof(struct aac_command),
663250963Sachim				  M_AACRAIDBUF, M_WAITOK|M_ZERO);
664250963Sachim	mtx_lock(&sc->aac_io_lock);
665250963Sachim	while (sc->total_fibs < sc->aac_max_fibs) {
666250963Sachim		if (aac_alloc_commands(sc) != 0)
667250963Sachim			break;
668250963Sachim	}
669250963Sachim	mtx_unlock(&sc->aac_io_lock);
670250963Sachim	if (sc->total_fibs == 0)
671250963Sachim		return (ENOMEM);
672250963Sachim
673250963Sachim	return (0);
674250963Sachim}
675250963Sachim
676250963Sachim/*
677250963Sachim * Free all of the resources associated with (sc)
678250963Sachim *
679250963Sachim * Should not be called if the controller is active.
680250963Sachim */
681250963Sachimvoid
682250963Sachimaacraid_free(struct aac_softc *sc)
683250963Sachim{
684250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
685250963Sachim
686250963Sachim	/* remove the control device */
687250963Sachim	if (sc->aac_dev_t != NULL)
688250963Sachim		destroy_dev(sc->aac_dev_t);
689250963Sachim
690250963Sachim	/* throw away any FIB buffers, discard the FIB DMA tag */
691250963Sachim	aac_free_commands(sc);
692250963Sachim	if (sc->aac_fib_dmat)
693250963Sachim		bus_dma_tag_destroy(sc->aac_fib_dmat);
694250963Sachim
695250963Sachim	free(sc->aac_commands, M_AACRAIDBUF);
696250963Sachim
697250963Sachim	/* destroy the common area */
698250963Sachim	if (sc->aac_common) {
699250963Sachim		bus_dmamap_unload(sc->aac_common_dmat, sc->aac_common_dmamap);
700250963Sachim		bus_dmamem_free(sc->aac_common_dmat, sc->aac_common,
701250963Sachim				sc->aac_common_dmamap);
702250963Sachim	}
703250963Sachim	if (sc->aac_common_dmat)
704250963Sachim		bus_dma_tag_destroy(sc->aac_common_dmat);
705250963Sachim
706250963Sachim	/* disconnect the interrupt handler */
707250963Sachim	if (sc->aac_intr)
708250963Sachim		bus_teardown_intr(sc->aac_dev, sc->aac_irq, sc->aac_intr);
709250963Sachim	if (sc->aac_irq != NULL)
710250963Sachim		bus_release_resource(sc->aac_dev, SYS_RES_IRQ, sc->aac_irq_rid,
711250963Sachim				     sc->aac_irq);
712250963Sachim
713250963Sachim	/* destroy data-transfer DMA tag */
714250963Sachim	if (sc->aac_buffer_dmat)
715250963Sachim		bus_dma_tag_destroy(sc->aac_buffer_dmat);
716250963Sachim
717250963Sachim	/* destroy the parent DMA tag */
718250963Sachim	if (sc->aac_parent_dmat)
719250963Sachim		bus_dma_tag_destroy(sc->aac_parent_dmat);
720250963Sachim
721250963Sachim	/* release the register window mapping */
722250963Sachim	if (sc->aac_regs_res0 != NULL)
723250963Sachim		bus_release_resource(sc->aac_dev, SYS_RES_MEMORY,
724250963Sachim				     sc->aac_regs_rid0, sc->aac_regs_res0);
725250963Sachim	if (sc->aac_regs_res1 != NULL)
726250963Sachim		bus_release_resource(sc->aac_dev, SYS_RES_MEMORY,
727250963Sachim				     sc->aac_regs_rid1, sc->aac_regs_res1);
728250963Sachim}
729250963Sachim
730250963Sachim/*
731250963Sachim * Disconnect from the controller completely, in preparation for unload.
732250963Sachim */
733250963Sachimint
734250963Sachimaacraid_detach(device_t dev)
735250963Sachim{
736250963Sachim	struct aac_softc *sc;
737250963Sachim	struct aac_container *co;
738250963Sachim	struct aac_sim	*sim;
739250963Sachim	int error;
740250963Sachim
741250963Sachim	sc = device_get_softc(dev);
742250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
743250963Sachim
744250963Sachim#if __FreeBSD_version >= 800000
745250963Sachim	callout_drain(&sc->aac_daemontime);
746250963Sachim#else
747250963Sachim	untimeout(aac_daemon, (void *)sc, sc->timeout_id);
748250963Sachim#endif
749250963Sachim	/* Remove the child containers */
750250963Sachim	while ((co = TAILQ_FIRST(&sc->aac_container_tqh)) != NULL) {
751250963Sachim		TAILQ_REMOVE(&sc->aac_container_tqh, co, co_link);
752250963Sachim		free(co, M_AACRAIDBUF);
753250963Sachim	}
754250963Sachim
755250963Sachim	/* Remove the CAM SIMs */
756250963Sachim	while ((sim = TAILQ_FIRST(&sc->aac_sim_tqh)) != NULL) {
757250963Sachim		TAILQ_REMOVE(&sc->aac_sim_tqh, sim, sim_link);
758250963Sachim		error = device_delete_child(dev, sim->sim_dev);
759250963Sachim		if (error)
760250963Sachim			return (error);
761250963Sachim		free(sim, M_AACRAIDBUF);
762250963Sachim	}
763250963Sachim
764250963Sachim	if (sc->aifflags & AAC_AIFFLAGS_RUNNING) {
765250963Sachim		sc->aifflags |= AAC_AIFFLAGS_EXIT;
766250963Sachim		wakeup(sc->aifthread);
767250963Sachim		tsleep(sc->aac_dev, PUSER | PCATCH, "aac_dch", 30 * hz);
768250963Sachim	}
769250963Sachim
770250963Sachim	if (sc->aifflags & AAC_AIFFLAGS_RUNNING)
771250963Sachim		panic("Cannot shutdown AIF thread");
772250963Sachim
773250963Sachim	if ((error = aacraid_shutdown(dev)))
774250963Sachim		return(error);
775250963Sachim
776250963Sachim	EVENTHANDLER_DEREGISTER(shutdown_final, sc->eh);
777250963Sachim
778250963Sachim	aacraid_free(sc);
779250963Sachim
780250963Sachim	mtx_destroy(&sc->aac_io_lock);
781250963Sachim
782250963Sachim	return(0);
783250963Sachim}
784250963Sachim
785250963Sachim/*
786250963Sachim * Bring the controller down to a dormant state and detach all child devices.
787250963Sachim *
788250963Sachim * This function is called before detach or system shutdown.
789250963Sachim *
790250963Sachim * Note that we can assume that the bioq on the controller is empty, as we won't
791250963Sachim * allow shutdown if any device is open.
792250963Sachim */
793250963Sachimint
794250963Sachimaacraid_shutdown(device_t dev)
795250963Sachim{
796250963Sachim	struct aac_softc *sc;
797250963Sachim	struct aac_fib *fib;
798250963Sachim	struct aac_close_command *cc;
799250963Sachim
800250963Sachim	sc = device_get_softc(dev);
801250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
802250963Sachim
803250963Sachim	sc->aac_state |= AAC_STATE_SUSPEND;
804250963Sachim
805250963Sachim	/*
806250963Sachim	 * Send a Container shutdown followed by a HostShutdown FIB to the
807250963Sachim	 * controller to convince it that we don't want to talk to it anymore.
808250963Sachim	 * We've been closed and all I/O completed already
809250963Sachim	 */
810250963Sachim	device_printf(sc->aac_dev, "shutting down controller...");
811250963Sachim
812250963Sachim	mtx_lock(&sc->aac_io_lock);
813250963Sachim	aac_alloc_sync_fib(sc, &fib);
814250963Sachim	cc = (struct aac_close_command *)&fib->data[0];
815250963Sachim
816250963Sachim	bzero(cc, sizeof(struct aac_close_command));
817250963Sachim	cc->Command = VM_CloseAll;
818250963Sachim	cc->ContainerId = 0xffffffff;
819250963Sachim	if (aac_sync_fib(sc, ContainerCommand, 0, fib,
820250963Sachim	    sizeof(struct aac_close_command)))
821250963Sachim		printf("FAILED.\n");
822250963Sachim	else
823250963Sachim		printf("done\n");
824250963Sachim
825250963Sachim	AAC_MASK_INTERRUPTS(sc);
826250963Sachim	aac_release_sync_fib(sc);
827250963Sachim	mtx_unlock(&sc->aac_io_lock);
828250963Sachim
829250963Sachim	return(0);
830250963Sachim}
831250963Sachim
832250963Sachim/*
833250963Sachim * Bring the controller to a quiescent state, ready for system suspend.
834250963Sachim */
835250963Sachimint
836250963Sachimaacraid_suspend(device_t dev)
837250963Sachim{
838250963Sachim	struct aac_softc *sc;
839250963Sachim
840250963Sachim	sc = device_get_softc(dev);
841250963Sachim
842250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
843250963Sachim	sc->aac_state |= AAC_STATE_SUSPEND;
844250963Sachim
845250963Sachim	AAC_MASK_INTERRUPTS(sc);
846250963Sachim	return(0);
847250963Sachim}
848250963Sachim
849250963Sachim/*
850250963Sachim * Bring the controller back to a state ready for operation.
851250963Sachim */
852250963Sachimint
853250963Sachimaacraid_resume(device_t dev)
854250963Sachim{
855250963Sachim	struct aac_softc *sc;
856250963Sachim
857250963Sachim	sc = device_get_softc(dev);
858250963Sachim
859250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
860250963Sachim	sc->aac_state &= ~AAC_STATE_SUSPEND;
861250963Sachim	AAC_UNMASK_INTERRUPTS(sc);
862250963Sachim	return(0);
863250963Sachim}
864250963Sachim
865250963Sachim/*
866250963Sachim * Interrupt handler for NEW_COMM_TYPE1, NEW_COMM_TYPE2, NEW_COMM_TYPE34 interface.
867250963Sachim */
868250963Sachimvoid
869250963Sachimaacraid_new_intr_type1(void *arg)
870250963Sachim{
871250963Sachim	struct aac_softc *sc;
872250963Sachim	struct aac_command *cm;
873250963Sachim	struct aac_fib *fib;
874250963Sachim	u_int32_t bellbits, bellbits_shifted, index, handle;
875250963Sachim	int isFastResponse, isAif, noMoreAif;
876250963Sachim
877250963Sachim	sc = (struct aac_softc *)arg;
878250963Sachim
879250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
880250963Sachim	mtx_lock(&sc->aac_io_lock);
881250963Sachim	bellbits = AAC_MEM0_GETREG4(sc, AAC_SRC_ODBR_R);
882250963Sachim	if (bellbits & AAC_DB_RESPONSE_SENT_NS) {
883250963Sachim		bellbits = AAC_DB_RESPONSE_SENT_NS;
884250963Sachim		AAC_MEM0_SETREG4(sc, AAC_SRC_ODBR_C, bellbits);
885250963Sachim		AAC_MEM0_GETREG4(sc, AAC_SRC_ODBR_R);	/* ODR readback,Prep #238630 */
886250963Sachim		/* handle async. status */
887250963Sachim		index = sc->aac_host_rrq_idx;
888250963Sachim		for (;;) {
889250963Sachim			isFastResponse = isAif = noMoreAif = 0;
890250963Sachim			/* remove toggle bit (31) */
891250963Sachim			handle = (sc->aac_common->ac_host_rrq[index] & 0x7fffffff);
892250963Sachim			/* check fast response bit (30) */
893250963Sachim			if (handle & 0x40000000)
894250963Sachim				isFastResponse = 1;
895250963Sachim			/* check AIF bit (23) */
896250963Sachim			else if (handle & 0x00800000)
897250963Sachim				isAif = TRUE;
898250963Sachim			handle &= 0x0000ffff;
899250963Sachim			if (handle == 0)
900250963Sachim				break;
901250963Sachim
902250963Sachim			cm = sc->aac_commands + (handle - 1);
903250963Sachim			fib = cm->cm_fib;
904250963Sachim			if (isAif) {
905250963Sachim				noMoreAif = (fib->Header.XferState & AAC_FIBSTATE_NOMOREAIF) ? 1:0;
906250963Sachim				if (!noMoreAif)
907250963Sachim					aac_handle_aif(sc, fib);
908250963Sachim				aac_remove_busy(cm);
909250963Sachim				aacraid_release_command(cm);
910250963Sachim			} else {
911250963Sachim				if (isFastResponse) {
912250963Sachim					fib->Header.XferState |= AAC_FIBSTATE_DONEADAP;
913250963Sachim					*((u_int32_t *)(fib->data)) = ST_OK;
914250963Sachim					cm->cm_flags |= AAC_CMD_FASTRESP;
915250963Sachim				}
916250963Sachim				aac_remove_busy(cm);
917250963Sachim				aac_unmap_command(cm);
918250963Sachim				cm->cm_flags |= AAC_CMD_COMPLETED;
919250963Sachim
920250963Sachim				/* is there a completion handler? */
921250963Sachim				if (cm->cm_complete != NULL) {
922250963Sachim					cm->cm_complete(cm);
923250963Sachim				} else {
924250963Sachim					/* assume that someone is sleeping on this command */
925250963Sachim					wakeup(cm);
926250963Sachim				}
927250963Sachim				sc->flags &= ~AAC_QUEUE_FRZN;
928250963Sachim			}
929250963Sachim
930250963Sachim			sc->aac_common->ac_host_rrq[index++] = 0;
931250963Sachim			if (index == sc->aac_max_fibs)
932250963Sachim				index = 0;
933250963Sachim			sc->aac_host_rrq_idx = index;
934250963Sachim
935250963Sachim			if ((isAif && !noMoreAif) || sc->aif_pending)
936250963Sachim				aac_request_aif(sc);
937250963Sachim		}
938250963Sachim	} else {
939250963Sachim		bellbits_shifted = (bellbits >> AAC_SRC_ODR_SHIFT);
940250963Sachim		AAC_MEM0_SETREG4(sc, AAC_SRC_ODBR_C, bellbits);
941250963Sachim		if (bellbits_shifted & AAC_DB_AIF_PENDING) {
942250963Sachim			/* handle AIF */
943250963Sachim			aac_request_aif(sc);
944250963Sachim		} else if (bellbits_shifted & AAC_DB_SYNC_COMMAND) {
945250963Sachim			if (sc->aac_sync_cm) {
946250963Sachim				cm = sc->aac_sync_cm;
947250963Sachim				cm->cm_flags |= AAC_CMD_COMPLETED;
948250963Sachim				/* is there a completion handler? */
949250963Sachim				if (cm->cm_complete != NULL) {
950250963Sachim					cm->cm_complete(cm);
951250963Sachim				} else {
952250963Sachim					/* assume that someone is sleeping on this command */
953250963Sachim					wakeup(cm);
954250963Sachim				}
955250963Sachim				sc->flags &= ~AAC_QUEUE_FRZN;
956250963Sachim				sc->aac_sync_cm = NULL;
957250963Sachim			}
958250963Sachim		}
959250963Sachim	}
960250963Sachim
961250963Sachim	/* see if we can start some more I/O */
962250963Sachim	if ((sc->flags & AAC_QUEUE_FRZN) == 0)
963250963Sachim		aacraid_startio(sc);
964250963Sachim	mtx_unlock(&sc->aac_io_lock);
965250963Sachim}
966250963Sachim
967250963Sachim/*
968250963Sachim * Handle notification of one or more FIBs coming from the controller.
969250963Sachim */
970250963Sachimstatic void
971250963Sachimaac_command_thread(struct aac_softc *sc)
972250963Sachim{
973250963Sachim	int retval;
974250963Sachim
975250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
976250963Sachim
977250963Sachim	mtx_lock(&sc->aac_io_lock);
978250963Sachim	sc->aifflags = AAC_AIFFLAGS_RUNNING;
979250963Sachim
980250963Sachim	while ((sc->aifflags & AAC_AIFFLAGS_EXIT) == 0) {
981250963Sachim
982250963Sachim		retval = 0;
983250963Sachim		if ((sc->aifflags & AAC_AIFFLAGS_PENDING) == 0)
984250963Sachim			retval = msleep(sc->aifthread, &sc->aac_io_lock, PRIBIO,
985250963Sachim					"aacraid_aifthd", AAC_PERIODIC_INTERVAL * hz);
986250963Sachim
987250963Sachim		/*
988250963Sachim		 * First see if any FIBs need to be allocated.  This needs
989250963Sachim		 * to be called without the driver lock because contigmalloc
990250963Sachim		 * will grab Giant, and would result in an LOR.
991250963Sachim		 */
992250963Sachim		if ((sc->aifflags & AAC_AIFFLAGS_ALLOCFIBS) != 0) {
993250963Sachim			aac_alloc_commands(sc);
994250963Sachim			sc->aifflags &= ~AAC_AIFFLAGS_ALLOCFIBS;
995250963Sachim			aacraid_startio(sc);
996250963Sachim		}
997250963Sachim
998250963Sachim		/*
999250963Sachim		 * While we're here, check to see if any commands are stuck.
1000250963Sachim		 * This is pretty low-priority, so it's ok if it doesn't
1001250963Sachim		 * always fire.
1002250963Sachim		 */
1003250963Sachim		if (retval == EWOULDBLOCK)
1004250963Sachim			aac_timeout(sc);
1005250963Sachim
1006250963Sachim		/* Check the hardware printf message buffer */
1007250963Sachim		if (sc->aac_common->ac_printf[0] != 0)
1008250963Sachim			aac_print_printf(sc);
1009250963Sachim	}
1010250963Sachim	sc->aifflags &= ~AAC_AIFFLAGS_RUNNING;
1011250963Sachim	mtx_unlock(&sc->aac_io_lock);
1012250963Sachim	wakeup(sc->aac_dev);
1013250963Sachim
1014250963Sachim	aac_kthread_exit(0);
1015250963Sachim}
1016250963Sachim
1017250963Sachim/*
1018250963Sachim * Submit a command to the controller, return when it completes.
1019250963Sachim * XXX This is very dangerous!  If the card has gone out to lunch, we could
1020250963Sachim *     be stuck here forever.  At the same time, signals are not caught
1021250963Sachim *     because there is a risk that a signal could wakeup the sleep before
1022250963Sachim *     the card has a chance to complete the command.  Since there is no way
1023250963Sachim *     to cancel a command that is in progress, we can't protect against the
1024250963Sachim *     card completing a command late and spamming the command and data
1025250963Sachim *     memory.  So, we are held hostage until the command completes.
1026250963Sachim */
1027250963Sachimint
1028250963Sachimaacraid_wait_command(struct aac_command *cm)
1029250963Sachim{
1030250963Sachim	struct aac_softc *sc;
1031250963Sachim	int error;
1032250963Sachim
1033250963Sachim	sc = cm->cm_sc;
1034250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
1035250963Sachim	mtx_assert(&sc->aac_io_lock, MA_OWNED);
1036250963Sachim
1037250963Sachim	/* Put the command on the ready queue and get things going */
1038250963Sachim	aac_enqueue_ready(cm);
1039250963Sachim	aacraid_startio(sc);
1040250963Sachim	error = msleep(cm, &sc->aac_io_lock, PRIBIO, "aacraid_wait", 0);
1041250963Sachim	return(error);
1042250963Sachim}
1043250963Sachim
1044250963Sachim/*
1045250963Sachim *Command Buffer Management
1046250963Sachim */
1047250963Sachim
1048250963Sachim/*
1049250963Sachim * Allocate a command.
1050250963Sachim */
1051250963Sachimint
1052250963Sachimaacraid_alloc_command(struct aac_softc *sc, struct aac_command **cmp)
1053250963Sachim{
1054250963Sachim	struct aac_command *cm;
1055250963Sachim
1056250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
1057250963Sachim
1058250963Sachim	if ((cm = aac_dequeue_free(sc)) == NULL) {
1059250963Sachim		if (sc->total_fibs < sc->aac_max_fibs) {
1060250963Sachim			sc->aifflags |= AAC_AIFFLAGS_ALLOCFIBS;
1061250963Sachim			wakeup(sc->aifthread);
1062250963Sachim		}
1063250963Sachim		return (EBUSY);
1064250963Sachim	}
1065250963Sachim
1066250963Sachim	*cmp = cm;
1067250963Sachim	return(0);
1068250963Sachim}
1069250963Sachim
1070250963Sachim/*
1071250963Sachim * Release a command back to the freelist.
1072250963Sachim */
1073250963Sachimvoid
1074250963Sachimaacraid_release_command(struct aac_command *cm)
1075250963Sachim{
1076250963Sachim	struct aac_event *event;
1077250963Sachim	struct aac_softc *sc;
1078250963Sachim
1079250963Sachim	sc = cm->cm_sc;
1080250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
1081250963Sachim	mtx_assert(&sc->aac_io_lock, MA_OWNED);
1082250963Sachim
1083250963Sachim	/* (re)initialize the command/FIB */
1084250963Sachim	cm->cm_sgtable = NULL;
1085250963Sachim	cm->cm_flags = 0;
1086250963Sachim	cm->cm_complete = NULL;
1087250963Sachim	cm->cm_ccb = NULL;
1088250963Sachim	cm->cm_passthr_dmat = 0;
1089250963Sachim	cm->cm_fib->Header.XferState = AAC_FIBSTATE_EMPTY;
1090250963Sachim	cm->cm_fib->Header.StructType = AAC_FIBTYPE_TFIB;
1091250963Sachim	cm->cm_fib->Header.Unused = 0;
1092250963Sachim	cm->cm_fib->Header.SenderSize = cm->cm_sc->aac_max_fib_size;
1093250963Sachim
1094250963Sachim	/*
1095250963Sachim	 * These are duplicated in aac_start to cover the case where an
1096250963Sachim	 * intermediate stage may have destroyed them.  They're left
1097250963Sachim	 * initialized here for debugging purposes only.
1098250963Sachim	 */
1099250963Sachim	cm->cm_fib->Header.u.ReceiverFibAddress = (u_int32_t)cm->cm_fibphys;
1100250963Sachim	cm->cm_fib->Header.Handle = 0;
1101250963Sachim
1102250963Sachim	aac_enqueue_free(cm);
1103250963Sachim
1104250963Sachim	/*
1105250963Sachim	 * Dequeue all events so that there's no risk of events getting
1106250963Sachim	 * stranded.
1107250963Sachim	 */
1108250963Sachim	while ((event = TAILQ_FIRST(&sc->aac_ev_cmfree)) != NULL) {
1109250963Sachim		TAILQ_REMOVE(&sc->aac_ev_cmfree, event, ev_links);
1110250963Sachim		event->ev_callback(sc, event, event->ev_arg);
1111250963Sachim	}
1112250963Sachim}
1113250963Sachim
1114250963Sachim/*
1115250963Sachim * Map helper for command/FIB allocation.
1116250963Sachim */
1117250963Sachimstatic void
1118250963Sachimaac_map_command_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error)
1119250963Sachim{
1120250963Sachim	uint64_t	*fibphys;
1121250963Sachim
1122250963Sachim	fibphys = (uint64_t *)arg;
1123250963Sachim
1124250963Sachim	*fibphys = segs[0].ds_addr;
1125250963Sachim}
1126250963Sachim
1127250963Sachim/*
1128250963Sachim * Allocate and initialize commands/FIBs for this adapter.
1129250963Sachim */
1130250963Sachimstatic int
1131250963Sachimaac_alloc_commands(struct aac_softc *sc)
1132250963Sachim{
1133250963Sachim	struct aac_command *cm;
1134250963Sachim	struct aac_fibmap *fm;
1135250963Sachim	uint64_t fibphys;
1136250963Sachim	int i, error;
1137250963Sachim	u_int32_t maxsize;
1138250963Sachim
1139250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
1140250963Sachim	mtx_assert(&sc->aac_io_lock, MA_OWNED);
1141250963Sachim
1142250963Sachim	if (sc->total_fibs + sc->aac_max_fibs_alloc > sc->aac_max_fibs)
1143250963Sachim		return (ENOMEM);
1144250963Sachim
1145250963Sachim	fm = malloc(sizeof(struct aac_fibmap), M_AACRAIDBUF, M_NOWAIT|M_ZERO);
1146250963Sachim	if (fm == NULL)
1147250963Sachim		return (ENOMEM);
1148250963Sachim
1149250963Sachim	mtx_unlock(&sc->aac_io_lock);
1150250963Sachim	/* allocate the FIBs in DMAable memory and load them */
1151250963Sachim	if (bus_dmamem_alloc(sc->aac_fib_dmat, (void **)&fm->aac_fibs,
1152250963Sachim			     BUS_DMA_NOWAIT, &fm->aac_fibmap)) {
1153250963Sachim		device_printf(sc->aac_dev,
1154250963Sachim			      "Not enough contiguous memory available.\n");
1155250963Sachim		free(fm, M_AACRAIDBUF);
1156250963Sachim		mtx_lock(&sc->aac_io_lock);
1157250963Sachim		return (ENOMEM);
1158250963Sachim	}
1159250963Sachim
1160250963Sachim	maxsize = sc->aac_max_fib_size + 31;
1161250963Sachim	if (sc->flags & AAC_FLAGS_NEW_COMM_TYPE1)
1162250963Sachim		maxsize += sizeof(struct aac_fib_xporthdr);
1163250963Sachim	/* Ignore errors since this doesn't bounce */
1164250963Sachim	(void)bus_dmamap_load(sc->aac_fib_dmat, fm->aac_fibmap, fm->aac_fibs,
1165250963Sachim			      sc->aac_max_fibs_alloc * maxsize,
1166250963Sachim			      aac_map_command_helper, &fibphys, 0);
1167250963Sachim	mtx_lock(&sc->aac_io_lock);
1168250963Sachim
1169250963Sachim	/* initialize constant fields in the command structure */
1170250963Sachim	bzero(fm->aac_fibs, sc->aac_max_fibs_alloc * maxsize);
1171250963Sachim	for (i = 0; i < sc->aac_max_fibs_alloc; i++) {
1172250963Sachim		cm = sc->aac_commands + sc->total_fibs;
1173250963Sachim		fm->aac_commands = cm;
1174250963Sachim		cm->cm_sc = sc;
1175250963Sachim		cm->cm_fib = (struct aac_fib *)
1176250963Sachim			((u_int8_t *)fm->aac_fibs + i * maxsize);
1177250963Sachim		cm->cm_fibphys = fibphys + i * maxsize;
1178250963Sachim		if (sc->flags & AAC_FLAGS_NEW_COMM_TYPE1) {
1179250963Sachim			u_int64_t fibphys_aligned;
1180250963Sachim			fibphys_aligned =
1181250963Sachim				(cm->cm_fibphys + sizeof(struct aac_fib_xporthdr) + 31) & ~31;
1182250963Sachim			cm->cm_fib = (struct aac_fib *)
1183250963Sachim				((u_int8_t *)cm->cm_fib + (fibphys_aligned - cm->cm_fibphys));
1184250963Sachim			cm->cm_fibphys = fibphys_aligned;
1185250963Sachim		} else {
1186250963Sachim			u_int64_t fibphys_aligned;
1187250963Sachim			fibphys_aligned = (cm->cm_fibphys + 31) & ~31;
1188250963Sachim			cm->cm_fib = (struct aac_fib *)
1189250963Sachim				((u_int8_t *)cm->cm_fib + (fibphys_aligned - cm->cm_fibphys));
1190250963Sachim			cm->cm_fibphys = fibphys_aligned;
1191250963Sachim		}
1192250963Sachim		cm->cm_index = sc->total_fibs;
1193250963Sachim
1194250963Sachim		if ((error = bus_dmamap_create(sc->aac_buffer_dmat, 0,
1195250963Sachim					       &cm->cm_datamap)) != 0)
1196250963Sachim			break;
1197250963Sachim		if (sc->aac_max_fibs <= 1 || sc->aac_max_fibs - sc->total_fibs > 1)
1198250963Sachim			aacraid_release_command(cm);
1199250963Sachim		sc->total_fibs++;
1200250963Sachim	}
1201250963Sachim
1202250963Sachim	if (i > 0) {
1203250963Sachim		TAILQ_INSERT_TAIL(&sc->aac_fibmap_tqh, fm, fm_link);
1204250963Sachim		fwprintf(sc, HBA_FLAGS_DBG_COMM_B, "total_fibs= %d\n", sc->total_fibs);
1205250963Sachim		return (0);
1206250963Sachim	}
1207250963Sachim
1208250963Sachim	bus_dmamap_unload(sc->aac_fib_dmat, fm->aac_fibmap);
1209250963Sachim	bus_dmamem_free(sc->aac_fib_dmat, fm->aac_fibs, fm->aac_fibmap);
1210250963Sachim	free(fm, M_AACRAIDBUF);
1211250963Sachim	return (ENOMEM);
1212250963Sachim}
1213250963Sachim
1214250963Sachim/*
1215250963Sachim * Free FIBs owned by this adapter.
1216250963Sachim */
1217250963Sachimstatic void
1218250963Sachimaac_free_commands(struct aac_softc *sc)
1219250963Sachim{
1220250963Sachim	struct aac_fibmap *fm;
1221250963Sachim	struct aac_command *cm;
1222250963Sachim	int i;
1223250963Sachim
1224250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
1225250963Sachim
1226250963Sachim	while ((fm = TAILQ_FIRST(&sc->aac_fibmap_tqh)) != NULL) {
1227250963Sachim
1228250963Sachim		TAILQ_REMOVE(&sc->aac_fibmap_tqh, fm, fm_link);
1229250963Sachim		/*
1230250963Sachim		 * We check against total_fibs to handle partially
1231250963Sachim		 * allocated blocks.
1232250963Sachim		 */
1233250963Sachim		for (i = 0; i < sc->aac_max_fibs_alloc && sc->total_fibs--; i++) {
1234250963Sachim			cm = fm->aac_commands + i;
1235250963Sachim			bus_dmamap_destroy(sc->aac_buffer_dmat, cm->cm_datamap);
1236250963Sachim		}
1237250963Sachim		bus_dmamap_unload(sc->aac_fib_dmat, fm->aac_fibmap);
1238250963Sachim		bus_dmamem_free(sc->aac_fib_dmat, fm->aac_fibs, fm->aac_fibmap);
1239250963Sachim		free(fm, M_AACRAIDBUF);
1240250963Sachim	}
1241250963Sachim}
1242250963Sachim
1243250963Sachim/*
1244250963Sachim * Command-mapping helper function - populate this command's s/g table.
1245250963Sachim */
1246250963Sachimvoid
1247250963Sachimaacraid_map_command_sg(void *arg, bus_dma_segment_t *segs, int nseg, int error)
1248250963Sachim{
1249250963Sachim	struct aac_softc *sc;
1250250963Sachim	struct aac_command *cm;
1251250963Sachim	struct aac_fib *fib;
1252250963Sachim	int i;
1253250963Sachim
1254250963Sachim	cm = (struct aac_command *)arg;
1255250963Sachim	sc = cm->cm_sc;
1256250963Sachim	fib = cm->cm_fib;
1257250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "nseg %d", nseg);
1258250963Sachim	mtx_assert(&sc->aac_io_lock, MA_OWNED);
1259250963Sachim
1260250963Sachim	/* copy into the FIB */
1261250963Sachim	if (cm->cm_sgtable != NULL) {
1262250963Sachim		if (fib->Header.Command == RawIo2) {
1263250963Sachim			struct aac_raw_io2 *raw;
1264250963Sachim			struct aac_sge_ieee1212 *sg;
1265250963Sachim			u_int32_t min_size = PAGE_SIZE, cur_size;
1266250963Sachim			int conformable = TRUE;
1267250963Sachim
1268250963Sachim			raw = (struct aac_raw_io2 *)&fib->data[0];
1269250963Sachim			sg = (struct aac_sge_ieee1212 *)cm->cm_sgtable;
1270250963Sachim			raw->sgeCnt = nseg;
1271250963Sachim
1272250963Sachim			for (i = 0; i < nseg; i++) {
1273250963Sachim				cur_size = segs[i].ds_len;
1274250963Sachim				sg[i].addrHigh = 0;
1275250963Sachim				*(bus_addr_t *)&sg[i].addrLow = segs[i].ds_addr;
1276250963Sachim				sg[i].length = cur_size;
1277250963Sachim				sg[i].flags = 0;
1278250963Sachim				if (i == 0) {
1279250963Sachim					raw->sgeFirstSize = cur_size;
1280250963Sachim				} else if (i == 1) {
1281250963Sachim					raw->sgeNominalSize = cur_size;
1282250963Sachim					min_size = cur_size;
1283250963Sachim				} else if ((i+1) < nseg &&
1284250963Sachim					cur_size != raw->sgeNominalSize) {
1285250963Sachim					conformable = FALSE;
1286250963Sachim					if (cur_size < min_size)
1287250963Sachim						min_size = cur_size;
1288250963Sachim				}
1289250963Sachim			}
1290250963Sachim
1291250963Sachim			/* not conformable: evaluate required sg elements */
1292250963Sachim			if (!conformable) {
1293250963Sachim				int j, err_found, nseg_new = nseg;
1294250963Sachim				for (i = min_size / PAGE_SIZE; i >= 1; --i) {
1295250963Sachim					err_found = FALSE;
1296250963Sachim					nseg_new = 2;
1297250963Sachim					for (j = 1; j < nseg - 1; ++j) {
1298250963Sachim						if (sg[j].length % (i*PAGE_SIZE)) {
1299250963Sachim							err_found = TRUE;
1300250963Sachim							break;
1301250963Sachim						}
1302250963Sachim						nseg_new += (sg[j].length / (i*PAGE_SIZE));
1303250963Sachim					}
1304250963Sachim					if (!err_found)
1305250963Sachim						break;
1306250963Sachim				}
1307250963Sachim				if (i>0 && nseg_new<=sc->aac_sg_tablesize &&
1308250963Sachim					!(sc->hint_flags & 4))
1309250963Sachim					nseg = aac_convert_sgraw2(sc,
1310250963Sachim						raw, i, nseg, nseg_new);
1311250963Sachim			} else {
1312250963Sachim				raw->flags |= RIO2_SGL_CONFORMANT;
1313250963Sachim			}
1314250963Sachim
1315250963Sachim			/* update the FIB size for the s/g count */
1316250963Sachim			fib->Header.Size += nseg *
1317250963Sachim				sizeof(struct aac_sge_ieee1212);
1318250963Sachim
1319250963Sachim		} else if (fib->Header.Command == RawIo) {
1320250963Sachim			struct aac_sg_tableraw *sg;
1321250963Sachim			sg = (struct aac_sg_tableraw *)cm->cm_sgtable;
1322250963Sachim			sg->SgCount = nseg;
1323250963Sachim			for (i = 0; i < nseg; i++) {
1324250963Sachim				sg->SgEntryRaw[i].SgAddress = segs[i].ds_addr;
1325250963Sachim				sg->SgEntryRaw[i].SgByteCount = segs[i].ds_len;
1326250963Sachim				sg->SgEntryRaw[i].Next = 0;
1327250963Sachim				sg->SgEntryRaw[i].Prev = 0;
1328250963Sachim				sg->SgEntryRaw[i].Flags = 0;
1329250963Sachim			}
1330250963Sachim			/* update the FIB size for the s/g count */
1331250963Sachim			fib->Header.Size += nseg*sizeof(struct aac_sg_entryraw);
1332250963Sachim		} else if ((cm->cm_sc->flags & AAC_FLAGS_SG_64BIT) == 0) {
1333250963Sachim			struct aac_sg_table *sg;
1334250963Sachim			sg = cm->cm_sgtable;
1335250963Sachim			sg->SgCount = nseg;
1336250963Sachim			for (i = 0; i < nseg; i++) {
1337250963Sachim				sg->SgEntry[i].SgAddress = segs[i].ds_addr;
1338250963Sachim				sg->SgEntry[i].SgByteCount = segs[i].ds_len;
1339250963Sachim			}
1340250963Sachim			/* update the FIB size for the s/g count */
1341250963Sachim			fib->Header.Size += nseg*sizeof(struct aac_sg_entry);
1342250963Sachim		} else {
1343250963Sachim			struct aac_sg_table64 *sg;
1344250963Sachim			sg = (struct aac_sg_table64 *)cm->cm_sgtable;
1345250963Sachim			sg->SgCount = nseg;
1346250963Sachim			for (i = 0; i < nseg; i++) {
1347250963Sachim				sg->SgEntry64[i].SgAddress = segs[i].ds_addr;
1348250963Sachim				sg->SgEntry64[i].SgByteCount = segs[i].ds_len;
1349250963Sachim			}
1350250963Sachim			/* update the FIB size for the s/g count */
1351250963Sachim			fib->Header.Size += nseg*sizeof(struct aac_sg_entry64);
1352250963Sachim		}
1353250963Sachim	}
1354250963Sachim
1355250963Sachim	/* Fix up the address values in the FIB.  Use the command array index
1356250963Sachim	 * instead of a pointer since these fields are only 32 bits.  Shift
1357250963Sachim	 * the SenderFibAddress over to make room for the fast response bit
1358250963Sachim	 * and for the AIF bit
1359250963Sachim	 */
1360250963Sachim	cm->cm_fib->Header.SenderFibAddress = (cm->cm_index << 2);
1361250963Sachim	cm->cm_fib->Header.u.ReceiverFibAddress = (u_int32_t)cm->cm_fibphys;
1362250963Sachim
1363250963Sachim	/* save a pointer to the command for speedy reverse-lookup */
1364250963Sachim	cm->cm_fib->Header.Handle += cm->cm_index + 1;
1365250963Sachim
1366250963Sachim	if (cm->cm_passthr_dmat == 0) {
1367250963Sachim		if (cm->cm_flags & AAC_CMD_DATAIN)
1368250963Sachim			bus_dmamap_sync(sc->aac_buffer_dmat, cm->cm_datamap,
1369250963Sachim							BUS_DMASYNC_PREREAD);
1370250963Sachim		if (cm->cm_flags & AAC_CMD_DATAOUT)
1371250963Sachim			bus_dmamap_sync(sc->aac_buffer_dmat, cm->cm_datamap,
1372250963Sachim							BUS_DMASYNC_PREWRITE);
1373250963Sachim	}
1374250963Sachim
1375250963Sachim	cm->cm_flags |= AAC_CMD_MAPPED;
1376250963Sachim
1377250963Sachim	if (sc->flags & AAC_FLAGS_SYNC_MODE) {
1378250963Sachim		u_int32_t wait = 0;
1379250963Sachim		aacraid_sync_command(sc, AAC_MONKER_SYNCFIB, cm->cm_fibphys, 0, 0, 0, &wait, NULL);
1380250963Sachim	} else if (cm->cm_flags & AAC_CMD_WAIT) {
1381250963Sachim		aacraid_sync_command(sc, AAC_MONKER_SYNCFIB, cm->cm_fibphys, 0, 0, 0, NULL, NULL);
1382250963Sachim	} else {
1383250963Sachim		int count = 10000000L;
1384250963Sachim		while (AAC_SEND_COMMAND(sc, cm) != 0) {
1385250963Sachim			if (--count == 0) {
1386250963Sachim				aac_unmap_command(cm);
1387250963Sachim				sc->flags |= AAC_QUEUE_FRZN;
1388250963Sachim				aac_requeue_ready(cm);
1389250963Sachim			}
1390250963Sachim			DELAY(5);			/* wait 5 usec. */
1391250963Sachim		}
1392250963Sachim	}
1393250963Sachim}
1394250963Sachim
1395250963Sachim
1396250963Sachimstatic int
1397250963Sachimaac_convert_sgraw2(struct aac_softc *sc, struct aac_raw_io2 *raw,
1398250963Sachim				   int pages, int nseg, int nseg_new)
1399250963Sachim{
1400250963Sachim	struct aac_sge_ieee1212 *sge;
1401250963Sachim	int i, j, pos;
1402250963Sachim	u_int32_t addr_low;
1403250963Sachim
1404250963Sachim	sge = malloc(nseg_new * sizeof(struct aac_sge_ieee1212),
1405250963Sachim		M_AACRAIDBUF, M_NOWAIT|M_ZERO);
1406250963Sachim	if (sge == NULL)
1407250963Sachim		return nseg;
1408250963Sachim
1409250963Sachim	for (i = 1, pos = 1; i < nseg - 1; ++i) {
1410250963Sachim		for (j = 0; j < raw->sge[i].length / (pages*PAGE_SIZE); ++j) {
1411250963Sachim			addr_low = raw->sge[i].addrLow + j * pages * PAGE_SIZE;
1412250963Sachim			sge[pos].addrLow = addr_low;
1413250963Sachim			sge[pos].addrHigh = raw->sge[i].addrHigh;
1414250963Sachim			if (addr_low < raw->sge[i].addrLow)
1415250963Sachim				sge[pos].addrHigh++;
1416250963Sachim			sge[pos].length = pages * PAGE_SIZE;
1417250963Sachim			sge[pos].flags = 0;
1418250963Sachim			pos++;
1419250963Sachim		}
1420250963Sachim	}
1421250963Sachim	sge[pos] = raw->sge[nseg-1];
1422250963Sachim	for (i = 1; i < nseg_new; ++i)
1423250963Sachim		raw->sge[i] = sge[i];
1424250963Sachim
1425250963Sachim	free(sge, M_AACRAIDBUF);
1426250963Sachim	raw->sgeCnt = nseg_new;
1427250963Sachim	raw->flags |= RIO2_SGL_CONFORMANT;
1428250963Sachim	raw->sgeNominalSize = pages * PAGE_SIZE;
1429250963Sachim	return nseg_new;
1430250963Sachim}
1431250963Sachim
1432250963Sachim
1433250963Sachim/*
1434250963Sachim * Unmap a command from controller-visible space.
1435250963Sachim */
1436250963Sachimstatic void
1437250963Sachimaac_unmap_command(struct aac_command *cm)
1438250963Sachim{
1439250963Sachim	struct aac_softc *sc;
1440250963Sachim
1441250963Sachim	sc = cm->cm_sc;
1442250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
1443250963Sachim
1444250963Sachim	if (!(cm->cm_flags & AAC_CMD_MAPPED))
1445250963Sachim		return;
1446250963Sachim
1447250963Sachim	if (cm->cm_datalen != 0 && cm->cm_passthr_dmat == 0) {
1448250963Sachim		if (cm->cm_flags & AAC_CMD_DATAIN)
1449250963Sachim			bus_dmamap_sync(sc->aac_buffer_dmat, cm->cm_datamap,
1450250963Sachim					BUS_DMASYNC_POSTREAD);
1451250963Sachim		if (cm->cm_flags & AAC_CMD_DATAOUT)
1452250963Sachim			bus_dmamap_sync(sc->aac_buffer_dmat, cm->cm_datamap,
1453250963Sachim					BUS_DMASYNC_POSTWRITE);
1454250963Sachim
1455250963Sachim		bus_dmamap_unload(sc->aac_buffer_dmat, cm->cm_datamap);
1456250963Sachim	}
1457250963Sachim	cm->cm_flags &= ~AAC_CMD_MAPPED;
1458250963Sachim}
1459250963Sachim
1460250963Sachim/*
1461250963Sachim * Hardware Interface
1462250963Sachim */
1463250963Sachim
1464250963Sachim/*
1465250963Sachim * Initialize the adapter.
1466250963Sachim */
1467250963Sachimstatic void
1468250963Sachimaac_common_map(void *arg, bus_dma_segment_t *segs, int nseg, int error)
1469250963Sachim{
1470250963Sachim	struct aac_softc *sc;
1471250963Sachim
1472250963Sachim	sc = (struct aac_softc *)arg;
1473250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
1474250963Sachim
1475250963Sachim	sc->aac_common_busaddr = segs[0].ds_addr;
1476250963Sachim}
1477250963Sachim
1478250963Sachimstatic int
1479250963Sachimaac_check_firmware(struct aac_softc *sc)
1480250963Sachim{
1481250963Sachim	u_int32_t code, major, minor, maxsize;
1482250963Sachim	u_int32_t options = 0, atu_size = 0, status;
1483250963Sachim	time_t then;
1484250963Sachim
1485250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
1486250963Sachim	/*
1487250963Sachim	 * Wait for the adapter to come ready.
1488250963Sachim	 */
1489250963Sachim	then = time_uptime;
1490250963Sachim	do {
1491250963Sachim		code = AAC_GET_FWSTATUS(sc);
1492250963Sachim		if (code & AAC_SELF_TEST_FAILED) {
1493250963Sachim			device_printf(sc->aac_dev, "FATAL: selftest failed\n");
1494250963Sachim			return(ENXIO);
1495250963Sachim		}
1496250963Sachim		if (code & AAC_KERNEL_PANIC) {
1497250963Sachim			device_printf(sc->aac_dev,
1498250963Sachim				      "FATAL: controller kernel panic");
1499250963Sachim			return(ENXIO);
1500250963Sachim		}
1501250963Sachim		if (time_uptime > (then + AAC_BOOT_TIMEOUT)) {
1502250963Sachim			device_printf(sc->aac_dev,
1503250963Sachim				      "FATAL: controller not coming ready, "
1504250963Sachim					   "status %x\n", code);
1505250963Sachim			return(ENXIO);
1506250963Sachim		}
1507250963Sachim	} while (!(code & AAC_UP_AND_RUNNING));
1508250963Sachim
1509250963Sachim	/*
1510250963Sachim	 * Retrieve the firmware version numbers.  Dell PERC2/QC cards with
1511250963Sachim	 * firmware version 1.x are not compatible with this driver.
1512250963Sachim	 */
1513250963Sachim	if (sc->flags & AAC_FLAGS_PERC2QC) {
1514250963Sachim		if (aacraid_sync_command(sc, AAC_MONKER_GETKERNVER, 0, 0, 0, 0,
1515250963Sachim				     NULL, NULL)) {
1516250963Sachim			device_printf(sc->aac_dev,
1517250963Sachim				      "Error reading firmware version\n");
1518250963Sachim			return (EIO);
1519250963Sachim		}
1520250963Sachim
1521250963Sachim		/* These numbers are stored as ASCII! */
1522250963Sachim		major = (AAC_GET_MAILBOX(sc, 1) & 0xff) - 0x30;
1523250963Sachim		minor = (AAC_GET_MAILBOX(sc, 2) & 0xff) - 0x30;
1524250963Sachim		if (major == 1) {
1525250963Sachim			device_printf(sc->aac_dev,
1526250963Sachim			    "Firmware version %d.%d is not supported.\n",
1527250963Sachim			    major, minor);
1528250963Sachim			return (EINVAL);
1529250963Sachim		}
1530250963Sachim	}
1531250963Sachim	/*
1532250963Sachim	 * Retrieve the capabilities/supported options word so we know what
1533250963Sachim	 * work-arounds to enable.  Some firmware revs don't support this
1534250963Sachim	 * command.
1535250963Sachim	 */
1536250963Sachim	if (aacraid_sync_command(sc, AAC_MONKER_GETINFO, 0, 0, 0, 0, &status, NULL)) {
1537250963Sachim		if (status != AAC_SRB_STS_INVALID_REQUEST) {
1538250963Sachim			device_printf(sc->aac_dev,
1539250963Sachim			     "RequestAdapterInfo failed\n");
1540250963Sachim			return (EIO);
1541250963Sachim		}
1542250963Sachim	} else {
1543250963Sachim		options = AAC_GET_MAILBOX(sc, 1);
1544250963Sachim		atu_size = AAC_GET_MAILBOX(sc, 2);
1545250963Sachim		sc->supported_options = options;
1546250963Sachim
1547250963Sachim		if ((options & AAC_SUPPORTED_4GB_WINDOW) != 0 &&
1548250963Sachim		    (sc->flags & AAC_FLAGS_NO4GB) == 0)
1549250963Sachim			sc->flags |= AAC_FLAGS_4GB_WINDOW;
1550250963Sachim		if (options & AAC_SUPPORTED_NONDASD)
1551250963Sachim			sc->flags |= AAC_FLAGS_ENABLE_CAM;
1552250963Sachim		if ((options & AAC_SUPPORTED_SGMAP_HOST64) != 0
1553250963Sachim			&& (sizeof(bus_addr_t) > 4)
1554250963Sachim			&& (sc->hint_flags & 0x1)) {
1555250963Sachim			device_printf(sc->aac_dev,
1556250963Sachim			    "Enabling 64-bit address support\n");
1557250963Sachim			sc->flags |= AAC_FLAGS_SG_64BIT;
1558250963Sachim		}
1559250963Sachim		if (sc->aac_if.aif_send_command) {
1560250963Sachim			if ((options & AAC_SUPPORTED_NEW_COMM_TYPE3) ||
1561250963Sachim				(options & AAC_SUPPORTED_NEW_COMM_TYPE4))
1562250963Sachim				sc->flags |= AAC_FLAGS_NEW_COMM | AAC_FLAGS_NEW_COMM_TYPE34;
1563250963Sachim			else if (options & AAC_SUPPORTED_NEW_COMM_TYPE1)
1564250963Sachim				sc->flags |= AAC_FLAGS_NEW_COMM | AAC_FLAGS_NEW_COMM_TYPE1;
1565250963Sachim			else if (options & AAC_SUPPORTED_NEW_COMM_TYPE2)
1566250963Sachim				sc->flags |= AAC_FLAGS_NEW_COMM | AAC_FLAGS_NEW_COMM_TYPE2;
1567250963Sachim		}
1568250963Sachim		if (options & AAC_SUPPORTED_64BIT_ARRAYSIZE)
1569250963Sachim			sc->flags |= AAC_FLAGS_ARRAY_64BIT;
1570250963Sachim	}
1571250963Sachim
1572250963Sachim	if (!(sc->flags & AAC_FLAGS_NEW_COMM)) {
1573250963Sachim		device_printf(sc->aac_dev, "Communication interface not supported!\n");
1574250963Sachim		return (ENXIO);
1575250963Sachim	}
1576250963Sachim
1577250963Sachim	if (sc->hint_flags & 2) {
1578250963Sachim		device_printf(sc->aac_dev,
1579250963Sachim			"Sync. mode enforced by driver parameter. This will cause a significant performance decrease!\n");
1580250963Sachim		sc->flags |= AAC_FLAGS_SYNC_MODE;
1581250963Sachim	} else if (sc->flags & AAC_FLAGS_NEW_COMM_TYPE34) {
1582250963Sachim		device_printf(sc->aac_dev,
1583250963Sachim			"Async. mode not supported by current driver, sync. mode enforced.\nPlease update driver to get full performance.\n");
1584250963Sachim		sc->flags |= AAC_FLAGS_SYNC_MODE;
1585250963Sachim	}
1586250963Sachim
1587250963Sachim	/* Check for broken hardware that does a lower number of commands */
1588250963Sachim	sc->aac_max_fibs = (sc->flags & AAC_FLAGS_256FIBS ? 256:512);
1589250963Sachim
1590250963Sachim	/* Remap mem. resource, if required */
1591250963Sachim	if (atu_size > rman_get_size(sc->aac_regs_res0)) {
1592250963Sachim		bus_release_resource(
1593250963Sachim			sc->aac_dev, SYS_RES_MEMORY,
1594250963Sachim			sc->aac_regs_rid0, sc->aac_regs_res0);
1595250963Sachim		sc->aac_regs_res0 = bus_alloc_resource(
1596250963Sachim			sc->aac_dev, SYS_RES_MEMORY, &sc->aac_regs_rid0,
1597250963Sachim			0ul, ~0ul, atu_size, RF_ACTIVE);
1598250963Sachim		if (sc->aac_regs_res0 == NULL) {
1599250963Sachim			sc->aac_regs_res0 = bus_alloc_resource_any(
1600250963Sachim				sc->aac_dev, SYS_RES_MEMORY,
1601250963Sachim				&sc->aac_regs_rid0, RF_ACTIVE);
1602250963Sachim			if (sc->aac_regs_res0 == NULL) {
1603250963Sachim				device_printf(sc->aac_dev,
1604250963Sachim					"couldn't allocate register window\n");
1605250963Sachim				return (ENXIO);
1606250963Sachim			}
1607250963Sachim		}
1608250963Sachim		sc->aac_btag0 = rman_get_bustag(sc->aac_regs_res0);
1609250963Sachim		sc->aac_bhandle0 = rman_get_bushandle(sc->aac_regs_res0);
1610250963Sachim	}
1611250963Sachim
1612250963Sachim	/* Read preferred settings */
1613250963Sachim	sc->aac_max_fib_size = sizeof(struct aac_fib);
1614250963Sachim	sc->aac_max_sectors = 128;				/* 64KB */
1615250963Sachim	sc->aac_max_aif = 1;
1616250963Sachim	if (sc->flags & AAC_FLAGS_SG_64BIT)
1617250963Sachim		sc->aac_sg_tablesize = (AAC_FIB_DATASIZE
1618250963Sachim		 - sizeof(struct aac_blockwrite64))
1619250963Sachim		 / sizeof(struct aac_sg_entry64);
1620250963Sachim	else
1621250963Sachim		sc->aac_sg_tablesize = (AAC_FIB_DATASIZE
1622250963Sachim		 - sizeof(struct aac_blockwrite))
1623250963Sachim		 / sizeof(struct aac_sg_entry);
1624250963Sachim
1625250963Sachim	if (!aacraid_sync_command(sc, AAC_MONKER_GETCOMMPREF, 0, 0, 0, 0, NULL, NULL)) {
1626250963Sachim		options = AAC_GET_MAILBOX(sc, 1);
1627250963Sachim		sc->aac_max_fib_size = (options & 0xFFFF);
1628250963Sachim		sc->aac_max_sectors = (options >> 16) << 1;
1629250963Sachim		options = AAC_GET_MAILBOX(sc, 2);
1630250963Sachim		sc->aac_sg_tablesize = (options >> 16);
1631250963Sachim		options = AAC_GET_MAILBOX(sc, 3);
1632250963Sachim		sc->aac_max_fibs = (options & 0xFFFF);
1633250963Sachim		options = AAC_GET_MAILBOX(sc, 4);
1634250963Sachim		sc->aac_max_aif = (options & 0xFFFF);
1635250963Sachim	}
1636250963Sachim
1637250963Sachim	maxsize = sc->aac_max_fib_size + 31;
1638250963Sachim	if (sc->flags & AAC_FLAGS_NEW_COMM_TYPE1)
1639250963Sachim		maxsize += sizeof(struct aac_fib_xporthdr);
1640250963Sachim	if (maxsize > PAGE_SIZE) {
1641250963Sachim    	sc->aac_max_fib_size -= (maxsize - PAGE_SIZE);
1642250963Sachim		maxsize = PAGE_SIZE;
1643250963Sachim	}
1644250963Sachim	sc->aac_max_fibs_alloc = PAGE_SIZE / maxsize;
1645250963Sachim
1646250963Sachim	if (sc->aac_max_fib_size > sizeof(struct aac_fib)) {
1647250963Sachim		sc->flags |= AAC_FLAGS_RAW_IO;
1648250963Sachim		device_printf(sc->aac_dev, "Enable Raw I/O\n");
1649250963Sachim	}
1650250963Sachim	if ((sc->flags & AAC_FLAGS_RAW_IO) &&
1651250963Sachim	    (sc->flags & AAC_FLAGS_ARRAY_64BIT)) {
1652250963Sachim		sc->flags |= AAC_FLAGS_LBA_64BIT;
1653250963Sachim		device_printf(sc->aac_dev, "Enable 64-bit array\n");
1654250963Sachim	}
1655250963Sachim
1656250963Sachim	aacraid_get_fw_debug_buffer(sc);
1657250963Sachim	return (0);
1658250963Sachim}
1659250963Sachim
1660250963Sachimstatic int
1661250963Sachimaac_init(struct aac_softc *sc)
1662250963Sachim{
1663250963Sachim	struct aac_adapter_init	*ip;
1664250963Sachim	int error;
1665250963Sachim
1666250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
1667250963Sachim
1668250963Sachim	/* reset rrq index */
1669250963Sachim	sc->aac_host_rrq_idx = 0;
1670250963Sachim
1671250963Sachim	/*
1672250963Sachim	 * Fill in the init structure.  This tells the adapter about the
1673250963Sachim	 * physical location of various important shared data structures.
1674250963Sachim	 */
1675250963Sachim	ip = &sc->aac_common->ac_init;
1676250963Sachim	ip->InitStructRevision = AAC_INIT_STRUCT_REVISION;
1677250963Sachim	if (sc->aac_max_fib_size > sizeof(struct aac_fib)) {
1678250963Sachim		ip->InitStructRevision = AAC_INIT_STRUCT_REVISION_4;
1679250963Sachim		sc->flags |= AAC_FLAGS_RAW_IO;
1680250963Sachim	}
1681250963Sachim	ip->MiniPortRevision = AAC_INIT_STRUCT_MINIPORT_REVISION;
1682250963Sachim
1683250963Sachim	ip->AdapterFibsPhysicalAddress = sc->aac_common_busaddr +
1684250963Sachim					 offsetof(struct aac_common, ac_fibs);
1685250963Sachim	ip->AdapterFibsVirtualAddress = 0;
1686250963Sachim	ip->AdapterFibsSize = AAC_ADAPTER_FIBS * sizeof(struct aac_fib);
1687250963Sachim	ip->AdapterFibAlign = sizeof(struct aac_fib);
1688250963Sachim
1689250963Sachim	ip->PrintfBufferAddress = sc->aac_common_busaddr +
1690250963Sachim				  offsetof(struct aac_common, ac_printf);
1691250963Sachim	ip->PrintfBufferSize = AAC_PRINTF_BUFSIZE;
1692250963Sachim
1693250963Sachim	/*
1694250963Sachim	 * The adapter assumes that pages are 4K in size, except on some
1695250963Sachim 	 * broken firmware versions that do the page->byte conversion twice,
1696250963Sachim	 * therefore 'assuming' that this value is in 16MB units (2^24).
1697250963Sachim	 * Round up since the granularity is so high.
1698250963Sachim	 */
1699250963Sachim	ip->HostPhysMemPages = ctob(physmem) / AAC_PAGE_SIZE;
1700250963Sachim	if (sc->flags & AAC_FLAGS_BROKEN_MEMMAP) {
1701250963Sachim		ip->HostPhysMemPages =
1702250963Sachim		    (ip->HostPhysMemPages + AAC_PAGE_SIZE) / AAC_PAGE_SIZE;
1703250963Sachim	}
1704250963Sachim	ip->HostElapsedSeconds = time_uptime;	/* reset later if invalid */
1705250963Sachim
1706250963Sachim	ip->InitFlags = AAC_INITFLAGS_NEW_COMM_SUPPORTED;
1707250963Sachim	if (sc->flags & AAC_FLAGS_NEW_COMM_TYPE1) {
1708250963Sachim		ip->InitStructRevision = AAC_INIT_STRUCT_REVISION_6;
1709250963Sachim		ip->InitFlags |= (AAC_INITFLAGS_NEW_COMM_TYPE1_SUPPORTED |
1710250963Sachim			AAC_INITFLAGS_FAST_JBOD_SUPPORTED);
1711250963Sachim		ip->MiniPortRevision = 0L;
1712250963Sachim		device_printf(sc->aac_dev, "New comm. interface type1 enabled\n");
1713250963Sachim	} else if (sc->flags & AAC_FLAGS_NEW_COMM_TYPE2) {
1714250963Sachim		ip->InitStructRevision = AAC_INIT_STRUCT_REVISION_7;
1715250963Sachim		ip->InitFlags |= (AAC_INITFLAGS_NEW_COMM_TYPE2_SUPPORTED |
1716250963Sachim			AAC_INITFLAGS_FAST_JBOD_SUPPORTED);
1717250963Sachim		device_printf(sc->aac_dev, "New comm. interface type2 enabled\n");
1718250963Sachim	}
1719250963Sachim	ip->MaxNumAif = sc->aac_max_aif;
1720250963Sachim	ip->HostRRQ_AddrLow =
1721250963Sachim		sc->aac_common_busaddr + offsetof(struct aac_common, ac_host_rrq);
1722250963Sachim	/* always 32-bit address */
1723250963Sachim	ip->HostRRQ_AddrHigh = 0;
1724250963Sachim
1725250963Sachim	if (sc->aac_support_opt2 & AAC_SUPPORTED_POWER_MANAGEMENT) {
1726250963Sachim		ip->InitFlags |= AAC_INITFLAGS_DRIVER_SUPPORTS_PM;
1727250963Sachim		ip->InitFlags |= AAC_INITFLAGS_DRIVER_USES_UTC_TIME;
1728250963Sachim		device_printf(sc->aac_dev, "Power Management enabled\n");
1729250963Sachim	}
1730250963Sachim
1731250963Sachim	ip->MaxIoCommands = sc->aac_max_fibs;
1732250963Sachim	ip->MaxIoSize = sc->aac_max_sectors << 9;
1733250963Sachim	ip->MaxFibSize = sc->aac_max_fib_size;
1734250963Sachim
1735250963Sachim	/*
1736250963Sachim	 * Do controller-type-specific initialisation
1737250963Sachim	 */
1738250963Sachim	AAC_MEM0_SETREG4(sc, AAC_SRC_ODBR_C, ~0);
1739250963Sachim
1740250963Sachim	/*
1741250963Sachim	 * Give the init structure to the controller.
1742250963Sachim	 */
1743250963Sachim	if (aacraid_sync_command(sc, AAC_MONKER_INITSTRUCT,
1744250963Sachim			     sc->aac_common_busaddr +
1745250963Sachim			     offsetof(struct aac_common, ac_init), 0, 0, 0,
1746250963Sachim			     NULL, NULL)) {
1747250963Sachim		device_printf(sc->aac_dev,
1748250963Sachim			      "error establishing init structure\n");
1749250963Sachim		error = EIO;
1750250963Sachim		goto out;
1751250963Sachim	}
1752250963Sachim
1753250963Sachim	error = 0;
1754250963Sachimout:
1755250963Sachim	return(error);
1756250963Sachim}
1757250963Sachim
1758250963Sachimstatic int
1759250963Sachimaac_setup_intr(struct aac_softc *sc)
1760250963Sachim{
1761250963Sachim	sc->aac_irq_rid = 0;
1762250963Sachim	if ((sc->aac_irq = bus_alloc_resource_any(sc->aac_dev, SYS_RES_IRQ,
1763250963Sachim			   			  &sc->aac_irq_rid,
1764250963Sachim			   			  RF_SHAREABLE |
1765250963Sachim						  RF_ACTIVE)) == NULL) {
1766250963Sachim		device_printf(sc->aac_dev, "can't allocate interrupt\n");
1767250963Sachim		return (EINVAL);
1768250963Sachim	}
1769250963Sachim	if (aac_bus_setup_intr(sc->aac_dev, sc->aac_irq,
1770250963Sachim			   INTR_MPSAFE|INTR_TYPE_BIO, NULL,
1771250963Sachim			   aacraid_new_intr_type1, sc, &sc->aac_intr)) {
1772250963Sachim		device_printf(sc->aac_dev, "can't set up interrupt\n");
1773250963Sachim		return (EINVAL);
1774250963Sachim	}
1775250963Sachim	return (0);
1776250963Sachim}
1777250963Sachim
1778250963Sachim/*
1779250963Sachim * Send a synchronous command to the controller and wait for a result.
1780250963Sachim * Indicate if the controller completed the command with an error status.
1781250963Sachim */
1782250963Sachimint
1783250963Sachimaacraid_sync_command(struct aac_softc *sc, u_int32_t command,
1784250963Sachim		 u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3,
1785250963Sachim		 u_int32_t *sp, u_int32_t *r1)
1786250963Sachim{
1787250963Sachim	time_t then;
1788250963Sachim	u_int32_t status;
1789250963Sachim
1790250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
1791250963Sachim
1792250963Sachim	/* populate the mailbox */
1793250963Sachim	AAC_SET_MAILBOX(sc, command, arg0, arg1, arg2, arg3);
1794250963Sachim
1795250963Sachim	/* ensure the sync command doorbell flag is cleared */
1796250963Sachim	AAC_CLEAR_ISTATUS(sc, AAC_DB_SYNC_COMMAND);
1797250963Sachim
1798250963Sachim	/* then set it to signal the adapter */
1799250963Sachim	AAC_QNOTIFY(sc, AAC_DB_SYNC_COMMAND);
1800250963Sachim
1801250963Sachim	if ((command != AAC_MONKER_SYNCFIB) || (sp == NULL) || (*sp != 0)) {
1802250963Sachim		/* spin waiting for the command to complete */
1803250963Sachim		then = time_uptime;
1804250963Sachim		do {
1805250963Sachim			if (time_uptime > (then + AAC_IMMEDIATE_TIMEOUT)) {
1806250963Sachim				fwprintf(sc, HBA_FLAGS_DBG_ERROR_B, "timed out");
1807250963Sachim				return(EIO);
1808250963Sachim			}
1809250963Sachim		} while (!(AAC_GET_ISTATUS(sc) & AAC_DB_SYNC_COMMAND));
1810250963Sachim
1811250963Sachim		/* clear the completion flag */
1812250963Sachim		AAC_CLEAR_ISTATUS(sc, AAC_DB_SYNC_COMMAND);
1813250963Sachim
1814250963Sachim		/* get the command status */
1815250963Sachim		status = AAC_GET_MAILBOX(sc, 0);
1816250963Sachim		if (sp != NULL)
1817250963Sachim			*sp = status;
1818250963Sachim
1819250963Sachim		/* return parameter */
1820250963Sachim		if (r1 != NULL)
1821250963Sachim			*r1 = AAC_GET_MAILBOX(sc, 1);
1822250963Sachim
1823250963Sachim		if (status != AAC_SRB_STS_SUCCESS)
1824250963Sachim			return (-1);
1825250963Sachim	}
1826250963Sachim	return(0);
1827250963Sachim}
1828250963Sachim
1829250963Sachimstatic int
1830250963Sachimaac_sync_fib(struct aac_softc *sc, u_int32_t command, u_int32_t xferstate,
1831250963Sachim		 struct aac_fib *fib, u_int16_t datasize)
1832250963Sachim{
1833250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
1834250963Sachim	mtx_assert(&sc->aac_io_lock, MA_OWNED);
1835250963Sachim
1836250963Sachim	if (datasize > AAC_FIB_DATASIZE)
1837250963Sachim		return(EINVAL);
1838250963Sachim
1839250963Sachim	/*
1840250963Sachim	 * Set up the sync FIB
1841250963Sachim	 */
1842250963Sachim	fib->Header.XferState = AAC_FIBSTATE_HOSTOWNED |
1843250963Sachim				AAC_FIBSTATE_INITIALISED |
1844250963Sachim				AAC_FIBSTATE_EMPTY;
1845250963Sachim	fib->Header.XferState |= xferstate;
1846250963Sachim	fib->Header.Command = command;
1847250963Sachim	fib->Header.StructType = AAC_FIBTYPE_TFIB;
1848250963Sachim	fib->Header.Size = sizeof(struct aac_fib_header) + datasize;
1849250963Sachim	fib->Header.SenderSize = sizeof(struct aac_fib);
1850250963Sachim	fib->Header.SenderFibAddress = 0;	/* Not needed */
1851250963Sachim	fib->Header.u.ReceiverFibAddress = sc->aac_common_busaddr +
1852250963Sachim					 offsetof(struct aac_common,
1853250963Sachim						  ac_sync_fib);
1854250963Sachim
1855250963Sachim	/*
1856250963Sachim	 * Give the FIB to the controller, wait for a response.
1857250963Sachim	 */
1858250963Sachim	if (aacraid_sync_command(sc, AAC_MONKER_SYNCFIB,
1859250963Sachim			     fib->Header.u.ReceiverFibAddress, 0, 0, 0, NULL, NULL)) {
1860250963Sachim		fwprintf(sc, HBA_FLAGS_DBG_ERROR_B, "IO error");
1861250963Sachim		return(EIO);
1862250963Sachim	}
1863250963Sachim
1864250963Sachim	return (0);
1865250963Sachim}
1866250963Sachim
1867250963Sachim/*
1868250963Sachim * Check for commands that have been outstanding for a suspiciously long time,
1869250963Sachim * and complain about them.
1870250963Sachim */
1871250963Sachimstatic void
1872250963Sachimaac_timeout(struct aac_softc *sc)
1873250963Sachim{
1874250963Sachim	struct aac_command *cm;
1875250963Sachim	time_t deadline;
1876250963Sachim	int timedout, code;
1877250963Sachim
1878250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
1879250963Sachim	/*
1880250963Sachim	 * Traverse the busy command list, bitch about late commands once
1881250963Sachim	 * only.
1882250963Sachim	 */
1883250963Sachim	timedout = 0;
1884250963Sachim	deadline = time_uptime - AAC_CMD_TIMEOUT;
1885250963Sachim	TAILQ_FOREACH(cm, &sc->aac_busy, cm_link) {
1886250963Sachim		if ((cm->cm_timestamp  < deadline)
1887250963Sachim			/* && !(cm->cm_flags & AAC_CMD_TIMEDOUT) */) {
1888250963Sachim			cm->cm_flags |= AAC_CMD_TIMEDOUT;
1889250963Sachim			device_printf(sc->aac_dev,
1890250963Sachim				      "COMMAND %p TIMEOUT AFTER %d SECONDS\n",
1891250963Sachim				      cm, (int)(time_uptime-cm->cm_timestamp));
1892250963Sachim			AAC_PRINT_FIB(sc, cm->cm_fib);
1893250963Sachim			timedout++;
1894250963Sachim		}
1895250963Sachim	}
1896250963Sachim
1897250963Sachim	if (timedout) {
1898250963Sachim		code = AAC_GET_FWSTATUS(sc);
1899250963Sachim		if (code != AAC_UP_AND_RUNNING) {
1900250963Sachim			device_printf(sc->aac_dev, "WARNING! Controller is no "
1901250963Sachim				      "longer running! code= 0x%x\n", code);
1902250963Sachim			aac_reset_adapter(sc);
1903250963Sachim		}
1904250963Sachim	}
1905250963Sachim	aacraid_print_queues(sc);
1906250963Sachim}
1907250963Sachim
1908250963Sachim/*
1909250963Sachim * Interface Function Vectors
1910250963Sachim */
1911250963Sachim
1912250963Sachim/*
1913250963Sachim * Read the current firmware status word.
1914250963Sachim */
1915250963Sachimstatic int
1916250963Sachimaac_src_get_fwstatus(struct aac_softc *sc)
1917250963Sachim{
1918250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
1919250963Sachim
1920250963Sachim	return(AAC_MEM0_GETREG4(sc, AAC_SRC_OMR));
1921250963Sachim}
1922250963Sachim
1923250963Sachim/*
1924250963Sachim * Notify the controller of a change in a given queue
1925250963Sachim */
1926250963Sachimstatic void
1927250963Sachimaac_src_qnotify(struct aac_softc *sc, int qbit)
1928250963Sachim{
1929250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
1930250963Sachim
1931250963Sachim	AAC_MEM0_SETREG4(sc, AAC_SRC_IDBR, qbit << AAC_SRC_IDR_SHIFT);
1932250963Sachim}
1933250963Sachim
1934250963Sachim/*
1935250963Sachim * Get the interrupt reason bits
1936250963Sachim */
1937250963Sachimstatic int
1938250963Sachimaac_src_get_istatus(struct aac_softc *sc)
1939250963Sachim{
1940250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
1941250963Sachim
1942250963Sachim	return(AAC_MEM0_GETREG4(sc, AAC_SRC_ODBR_R) >> AAC_SRC_ODR_SHIFT);
1943250963Sachim}
1944250963Sachim
1945250963Sachim/*
1946250963Sachim * Clear some interrupt reason bits
1947250963Sachim */
1948250963Sachimstatic void
1949250963Sachimaac_src_clear_istatus(struct aac_softc *sc, int mask)
1950250963Sachim{
1951250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
1952250963Sachim
1953250963Sachim	AAC_MEM0_SETREG4(sc, AAC_SRC_ODBR_C, mask << AAC_SRC_ODR_SHIFT);
1954250963Sachim}
1955250963Sachim
1956250963Sachim/*
1957250963Sachim * Populate the mailbox and set the command word
1958250963Sachim */
1959250963Sachimstatic void
1960250963Sachimaac_src_set_mailbox(struct aac_softc *sc, u_int32_t command, u_int32_t arg0,
1961250963Sachim		    u_int32_t arg1, u_int32_t arg2, u_int32_t arg3)
1962250963Sachim{
1963250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
1964250963Sachim
1965250963Sachim	AAC_MEM0_SETREG4(sc, AAC_SRC_MAILBOX, command);
1966250963Sachim	AAC_MEM0_SETREG4(sc, AAC_SRC_MAILBOX + 4, arg0);
1967250963Sachim	AAC_MEM0_SETREG4(sc, AAC_SRC_MAILBOX + 8, arg1);
1968250963Sachim	AAC_MEM0_SETREG4(sc, AAC_SRC_MAILBOX + 12, arg2);
1969250963Sachim	AAC_MEM0_SETREG4(sc, AAC_SRC_MAILBOX + 16, arg3);
1970250963Sachim}
1971250963Sachim
1972250963Sachimstatic void
1973250963Sachimaac_srcv_set_mailbox(struct aac_softc *sc, u_int32_t command, u_int32_t arg0,
1974250963Sachim		    u_int32_t arg1, u_int32_t arg2, u_int32_t arg3)
1975250963Sachim{
1976250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
1977250963Sachim
1978250963Sachim	AAC_MEM0_SETREG4(sc, AAC_SRCV_MAILBOX, command);
1979250963Sachim	AAC_MEM0_SETREG4(sc, AAC_SRCV_MAILBOX + 4, arg0);
1980250963Sachim	AAC_MEM0_SETREG4(sc, AAC_SRCV_MAILBOX + 8, arg1);
1981250963Sachim	AAC_MEM0_SETREG4(sc, AAC_SRCV_MAILBOX + 12, arg2);
1982250963Sachim	AAC_MEM0_SETREG4(sc, AAC_SRCV_MAILBOX + 16, arg3);
1983250963Sachim}
1984250963Sachim
1985250963Sachim/*
1986250963Sachim * Fetch the immediate command status word
1987250963Sachim */
1988250963Sachimstatic int
1989250963Sachimaac_src_get_mailbox(struct aac_softc *sc, int mb)
1990250963Sachim{
1991250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
1992250963Sachim
1993250963Sachim	return(AAC_MEM0_GETREG4(sc, AAC_SRC_MAILBOX + (mb * 4)));
1994250963Sachim}
1995250963Sachim
1996250963Sachimstatic int
1997250963Sachimaac_srcv_get_mailbox(struct aac_softc *sc, int mb)
1998250963Sachim{
1999250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
2000250963Sachim
2001250963Sachim	return(AAC_MEM0_GETREG4(sc, AAC_SRCV_MAILBOX + (mb * 4)));
2002250963Sachim}
2003250963Sachim
2004250963Sachim/*
2005250963Sachim * Set/clear interrupt masks
2006250963Sachim */
2007250963Sachimstatic void
2008250963Sachimaac_src_set_interrupts(struct aac_softc *sc, int enable)
2009250963Sachim{
2010250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "%sable interrupts", enable ? "en" : "dis");
2011250963Sachim
2012250963Sachim	if (enable) {
2013250963Sachim		AAC_MEM0_SETREG4(sc, AAC_SRC_OIMR, ~AAC_DB_INT_NEW_COMM_TYPE1);
2014250963Sachim	} else {
2015250963Sachim		AAC_MEM0_SETREG4(sc, AAC_SRC_OIMR, ~0);
2016250963Sachim	}
2017250963Sachim}
2018250963Sachim
2019250963Sachim/*
2020250963Sachim * New comm. interface: Send command functions
2021250963Sachim */
2022250963Sachimstatic int
2023250963Sachimaac_src_send_command(struct aac_softc *sc, struct aac_command *cm)
2024250963Sachim{
2025250963Sachim	struct aac_fib_xporthdr *pFibX;
2026250963Sachim	u_int32_t fibsize, high_addr;
2027250963Sachim	u_int64_t address;
2028250963Sachim
2029250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "send command (new comm. type1)");
2030250963Sachim
2031250963Sachim	if (sc->flags & AAC_FLAGS_NEW_COMM_TYPE2) {
2032250963Sachim		/* Calculate the amount to the fibsize bits */
2033250963Sachim		fibsize = (cm->cm_fib->Header.Size + 127) / 128 - 1;
2034250963Sachim		/* Fill new FIB header */
2035250963Sachim		address = cm->cm_fibphys;
2036250963Sachim		high_addr = (u_int32_t)(address >> 32);
2037250963Sachim		if (high_addr == 0L) {
2038250963Sachim			cm->cm_fib->Header.StructType = AAC_FIBTYPE_TFIB2;
2039250963Sachim			cm->cm_fib->Header.u.TimeStamp = 0L;
2040250963Sachim		} else {
2041250963Sachim			cm->cm_fib->Header.StructType = AAC_FIBTYPE_TFIB2_64;
2042250963Sachim			cm->cm_fib->Header.u.SenderFibAddressHigh = high_addr;
2043250963Sachim		}
2044250963Sachim		cm->cm_fib->Header.SenderFibAddress = (u_int32_t)address;
2045250963Sachim	} else {
2046250963Sachim		/* Calculate the amount to the fibsize bits */
2047250963Sachim		fibsize = (sizeof(struct aac_fib_xporthdr) +
2048250963Sachim		   cm->cm_fib->Header.Size + 127) / 128 - 1;
2049250963Sachim		/* Fill XPORT header */
2050250963Sachim		pFibX = (struct aac_fib_xporthdr *)
2051250963Sachim			((unsigned char *)cm->cm_fib - sizeof(struct aac_fib_xporthdr));
2052250963Sachim		pFibX->Handle = cm->cm_fib->Header.Handle;
2053250963Sachim		pFibX->HostAddress = cm->cm_fibphys;
2054250963Sachim		pFibX->Size = cm->cm_fib->Header.Size;
2055250963Sachim		address = cm->cm_fibphys - sizeof(struct aac_fib_xporthdr);
2056250963Sachim		high_addr = (u_int32_t)(address >> 32);
2057250963Sachim	}
2058250963Sachim
2059250963Sachim	if (fibsize > 31)
2060250963Sachim		fibsize = 31;
2061250963Sachim	aac_enqueue_busy(cm);
2062250963Sachim	if (high_addr) {
2063250963Sachim		AAC_MEM0_SETREG4(sc, AAC_SRC_IQUE64_H, high_addr);
2064250963Sachim		AAC_MEM0_SETREG4(sc, AAC_SRC_IQUE64_L, (u_int32_t)address + fibsize);
2065250963Sachim	} else {
2066250963Sachim		AAC_MEM0_SETREG4(sc, AAC_SRC_IQUE32, (u_int32_t)address + fibsize);
2067250963Sachim	}
2068250963Sachim	return 0;
2069250963Sachim}
2070250963Sachim
2071250963Sachim/*
2072250963Sachim * New comm. interface: get, set outbound queue index
2073250963Sachim */
2074250963Sachimstatic int
2075250963Sachimaac_src_get_outb_queue(struct aac_softc *sc)
2076250963Sachim{
2077250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
2078250963Sachim
2079250963Sachim	return(-1);
2080250963Sachim}
2081250963Sachim
2082250963Sachimstatic void
2083250963Sachimaac_src_set_outb_queue(struct aac_softc *sc, int index)
2084250963Sachim{
2085250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
2086250963Sachim}
2087250963Sachim
2088250963Sachim/*
2089250963Sachim * Debugging and Diagnostics
2090250963Sachim */
2091250963Sachim
2092250963Sachim/*
2093250963Sachim * Print some information about the controller.
2094250963Sachim */
2095250963Sachimstatic void
2096250963Sachimaac_describe_controller(struct aac_softc *sc)
2097250963Sachim{
2098250963Sachim	struct aac_fib *fib;
2099250963Sachim	struct aac_adapter_info	*info;
2100250963Sachim	char *adapter_type = "Adaptec RAID controller";
2101250963Sachim
2102250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
2103250963Sachim
2104250963Sachim	mtx_lock(&sc->aac_io_lock);
2105250963Sachim	aac_alloc_sync_fib(sc, &fib);
2106250963Sachim
2107250963Sachim	if (sc->supported_options & AAC_SUPPORTED_SUPPLEMENT_ADAPTER_INFO) {
2108250963Sachim		fib->data[0] = 0;
2109250963Sachim		if (aac_sync_fib(sc, RequestSupplementAdapterInfo, 0, fib, 1))
2110250963Sachim			device_printf(sc->aac_dev, "RequestSupplementAdapterInfo failed\n");
2111250963Sachim		else {
2112250963Sachim			struct aac_supplement_adapter_info *supp_info;
2113250963Sachim
2114250963Sachim			supp_info = ((struct aac_supplement_adapter_info *)&fib->data[0]);
2115250963Sachim			adapter_type = (char *)supp_info->AdapterTypeText;
2116250963Sachim			sc->aac_feature_bits = supp_info->FeatureBits;
2117250963Sachim			sc->aac_support_opt2 = supp_info->SupportedOptions2;
2118250963Sachim		}
2119250963Sachim	}
2120250963Sachim	device_printf(sc->aac_dev, "%s, aacraid driver %d.%d.%d-%d\n",
2121250963Sachim		adapter_type,
2122250963Sachim		AAC_DRIVER_MAJOR_VERSION, AAC_DRIVER_MINOR_VERSION,
2123250963Sachim		AAC_DRIVER_BUGFIX_LEVEL, AAC_DRIVER_BUILD);
2124250963Sachim
2125250963Sachim	fib->data[0] = 0;
2126250963Sachim	if (aac_sync_fib(sc, RequestAdapterInfo, 0, fib, 1)) {
2127250963Sachim		device_printf(sc->aac_dev, "RequestAdapterInfo failed\n");
2128250963Sachim		aac_release_sync_fib(sc);
2129250963Sachim		mtx_unlock(&sc->aac_io_lock);
2130250963Sachim		return;
2131250963Sachim	}
2132250963Sachim
2133250963Sachim	/* save the kernel revision structure for later use */
2134250963Sachim	info = (struct aac_adapter_info *)&fib->data[0];
2135250963Sachim	sc->aac_revision = info->KernelRevision;
2136250963Sachim
2137250963Sachim	if (bootverbose) {
2138250963Sachim		device_printf(sc->aac_dev, "%s %dMHz, %dMB memory "
2139250963Sachim		    "(%dMB cache, %dMB execution), %s\n",
2140250963Sachim		    aac_describe_code(aac_cpu_variant, info->CpuVariant),
2141250963Sachim		    info->ClockSpeed, info->TotalMem / (1024 * 1024),
2142250963Sachim		    info->BufferMem / (1024 * 1024),
2143250963Sachim		    info->ExecutionMem / (1024 * 1024),
2144250963Sachim		    aac_describe_code(aac_battery_platform,
2145250963Sachim		    info->batteryPlatform));
2146250963Sachim
2147250963Sachim		device_printf(sc->aac_dev,
2148250963Sachim		    "Kernel %d.%d-%d, Build %d, S/N %6X\n",
2149250963Sachim		    info->KernelRevision.external.comp.major,
2150250963Sachim		    info->KernelRevision.external.comp.minor,
2151250963Sachim		    info->KernelRevision.external.comp.dash,
2152250963Sachim		    info->KernelRevision.buildNumber,
2153250963Sachim		    (u_int32_t)(info->SerialNumber & 0xffffff));
2154250963Sachim
2155250963Sachim		device_printf(sc->aac_dev, "Supported Options=%b\n",
2156250963Sachim			      sc->supported_options,
2157250963Sachim			      "\20"
2158250963Sachim			      "\1SNAPSHOT"
2159250963Sachim			      "\2CLUSTERS"
2160250963Sachim			      "\3WCACHE"
2161250963Sachim			      "\4DATA64"
2162250963Sachim			      "\5HOSTTIME"
2163250963Sachim			      "\6RAID50"
2164250963Sachim			      "\7WINDOW4GB"
2165250963Sachim			      "\10SCSIUPGD"
2166250963Sachim			      "\11SOFTERR"
2167250963Sachim			      "\12NORECOND"
2168250963Sachim			      "\13SGMAP64"
2169250963Sachim			      "\14ALARM"
2170250963Sachim			      "\15NONDASD"
2171250963Sachim			      "\16SCSIMGT"
2172250963Sachim			      "\17RAIDSCSI"
2173250963Sachim			      "\21ADPTINFO"
2174250963Sachim			      "\22NEWCOMM"
2175250963Sachim			      "\23ARRAY64BIT"
2176250963Sachim			      "\24HEATSENSOR");
2177250963Sachim	}
2178250963Sachim
2179250963Sachim	aac_release_sync_fib(sc);
2180250963Sachim	mtx_unlock(&sc->aac_io_lock);
2181250963Sachim}
2182250963Sachim
2183250963Sachim/*
2184250963Sachim * Look up a text description of a numeric error code and return a pointer to
2185250963Sachim * same.
2186250963Sachim */
2187250963Sachimstatic char *
2188250963Sachimaac_describe_code(struct aac_code_lookup *table, u_int32_t code)
2189250963Sachim{
2190250963Sachim	int i;
2191250963Sachim
2192250963Sachim	for (i = 0; table[i].string != NULL; i++)
2193250963Sachim		if (table[i].code == code)
2194250963Sachim			return(table[i].string);
2195250963Sachim	return(table[i + 1].string);
2196250963Sachim}
2197250963Sachim
2198250963Sachim/*
2199250963Sachim * Management Interface
2200250963Sachim */
2201250963Sachim
2202250963Sachimstatic int
2203250963Sachimaac_open(struct cdev *dev, int flags, int fmt, struct thread *td)
2204250963Sachim{
2205250963Sachim	struct aac_softc *sc;
2206250963Sachim
2207250963Sachim	sc = dev->si_drv1;
2208250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
2209250963Sachim#if __FreeBSD_version >= 702000
2210250963Sachim	device_busy(sc->aac_dev);
2211250963Sachim	devfs_set_cdevpriv(sc, aac_cdevpriv_dtor);
2212250963Sachim#endif
2213250963Sachim	return 0;
2214250963Sachim}
2215250963Sachim
2216250963Sachimstatic int
2217250963Sachimaac_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td)
2218250963Sachim{
2219250963Sachim	union aac_statrequest *as;
2220250963Sachim	struct aac_softc *sc;
2221250963Sachim	int error = 0;
2222250963Sachim
2223250963Sachim	as = (union aac_statrequest *)arg;
2224250963Sachim	sc = dev->si_drv1;
2225250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
2226250963Sachim
2227250963Sachim	switch (cmd) {
2228250963Sachim	case AACIO_STATS:
2229250963Sachim		switch (as->as_item) {
2230250963Sachim		case AACQ_FREE:
2231250963Sachim		case AACQ_READY:
2232250963Sachim		case AACQ_BUSY:
2233250963Sachim			bcopy(&sc->aac_qstat[as->as_item], &as->as_qstat,
2234250963Sachim			      sizeof(struct aac_qstat));
2235250963Sachim			break;
2236250963Sachim		default:
2237250963Sachim			error = ENOENT;
2238250963Sachim			break;
2239250963Sachim		}
2240250963Sachim	break;
2241250963Sachim
2242250963Sachim	case FSACTL_SENDFIB:
2243250963Sachim	case FSACTL_SEND_LARGE_FIB:
2244250963Sachim		arg = *(caddr_t*)arg;
2245250963Sachim	case FSACTL_LNX_SENDFIB:
2246250963Sachim	case FSACTL_LNX_SEND_LARGE_FIB:
2247250963Sachim		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_SENDFIB");
2248250963Sachim		error = aac_ioctl_sendfib(sc, arg);
2249250963Sachim		break;
2250250963Sachim	case FSACTL_SEND_RAW_SRB:
2251250963Sachim		arg = *(caddr_t*)arg;
2252250963Sachim	case FSACTL_LNX_SEND_RAW_SRB:
2253250963Sachim		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_SEND_RAW_SRB");
2254250963Sachim		error = aac_ioctl_send_raw_srb(sc, arg);
2255250963Sachim		break;
2256250963Sachim	case FSACTL_AIF_THREAD:
2257250963Sachim	case FSACTL_LNX_AIF_THREAD:
2258250963Sachim		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_AIF_THREAD");
2259250963Sachim		error = EINVAL;
2260250963Sachim		break;
2261250963Sachim	case FSACTL_OPEN_GET_ADAPTER_FIB:
2262250963Sachim		arg = *(caddr_t*)arg;
2263250963Sachim	case FSACTL_LNX_OPEN_GET_ADAPTER_FIB:
2264250963Sachim		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_OPEN_GET_ADAPTER_FIB");
2265250963Sachim		error = aac_open_aif(sc, arg);
2266250963Sachim		break;
2267250963Sachim	case FSACTL_GET_NEXT_ADAPTER_FIB:
2268250963Sachim		arg = *(caddr_t*)arg;
2269250963Sachim	case FSACTL_LNX_GET_NEXT_ADAPTER_FIB:
2270250963Sachim		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_GET_NEXT_ADAPTER_FIB");
2271250963Sachim		error = aac_getnext_aif(sc, arg);
2272250963Sachim		break;
2273250963Sachim	case FSACTL_CLOSE_GET_ADAPTER_FIB:
2274250963Sachim		arg = *(caddr_t*)arg;
2275250963Sachim	case FSACTL_LNX_CLOSE_GET_ADAPTER_FIB:
2276250963Sachim		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_CLOSE_GET_ADAPTER_FIB");
2277250963Sachim		error = aac_close_aif(sc, arg);
2278250963Sachim		break;
2279250963Sachim	case FSACTL_MINIPORT_REV_CHECK:
2280250963Sachim		arg = *(caddr_t*)arg;
2281250963Sachim	case FSACTL_LNX_MINIPORT_REV_CHECK:
2282250963Sachim		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_MINIPORT_REV_CHECK");
2283250963Sachim		error = aac_rev_check(sc, arg);
2284250963Sachim		break;
2285250963Sachim	case FSACTL_QUERY_DISK:
2286250963Sachim		arg = *(caddr_t*)arg;
2287250963Sachim	case FSACTL_LNX_QUERY_DISK:
2288250963Sachim		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_QUERY_DISK");
2289250963Sachim		error = aac_query_disk(sc, arg);
2290250963Sachim		break;
2291250963Sachim	case FSACTL_DELETE_DISK:
2292250963Sachim	case FSACTL_LNX_DELETE_DISK:
2293250963Sachim		/*
2294250963Sachim		 * We don't trust the underland to tell us when to delete a
2295250963Sachim		 * container, rather we rely on an AIF coming from the
2296250963Sachim		 * controller
2297250963Sachim		 */
2298250963Sachim		error = 0;
2299250963Sachim		break;
2300250963Sachim	case FSACTL_GET_PCI_INFO:
2301250963Sachim		arg = *(caddr_t*)arg;
2302250963Sachim	case FSACTL_LNX_GET_PCI_INFO:
2303250963Sachim		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_GET_PCI_INFO");
2304250963Sachim		error = aac_get_pci_info(sc, arg);
2305250963Sachim		break;
2306250963Sachim	case FSACTL_GET_FEATURES:
2307250963Sachim		arg = *(caddr_t*)arg;
2308250963Sachim	case FSACTL_LNX_GET_FEATURES:
2309250963Sachim		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_GET_FEATURES");
2310250963Sachim		error = aac_supported_features(sc, arg);
2311250963Sachim		break;
2312250963Sachim	default:
2313250963Sachim		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "unsupported cmd 0x%lx\n", cmd);
2314250963Sachim		error = EINVAL;
2315250963Sachim		break;
2316250963Sachim	}
2317250963Sachim	return(error);
2318250963Sachim}
2319250963Sachim
2320250963Sachimstatic int
2321250963Sachimaac_poll(struct cdev *dev, int poll_events, struct thread *td)
2322250963Sachim{
2323250963Sachim	struct aac_softc *sc;
2324250963Sachim	struct aac_fib_context *ctx;
2325250963Sachim	int revents;
2326250963Sachim
2327250963Sachim	sc = dev->si_drv1;
2328250963Sachim	revents = 0;
2329250963Sachim
2330250963Sachim	mtx_lock(&sc->aac_io_lock);
2331250963Sachim	if ((poll_events & (POLLRDNORM | POLLIN)) != 0) {
2332250963Sachim		for (ctx = sc->fibctx; ctx; ctx = ctx->next) {
2333250963Sachim			if (ctx->ctx_idx != sc->aifq_idx || ctx->ctx_wrap) {
2334250963Sachim				revents |= poll_events & (POLLIN | POLLRDNORM);
2335250963Sachim				break;
2336250963Sachim			}
2337250963Sachim		}
2338250963Sachim	}
2339250963Sachim	mtx_unlock(&sc->aac_io_lock);
2340250963Sachim
2341250963Sachim	if (revents == 0) {
2342250963Sachim		if (poll_events & (POLLIN | POLLRDNORM))
2343250963Sachim			selrecord(td, &sc->rcv_select);
2344250963Sachim	}
2345250963Sachim
2346250963Sachim	return (revents);
2347250963Sachim}
2348250963Sachim
2349250963Sachimstatic void
2350250963Sachimaac_ioctl_event(struct aac_softc *sc, struct aac_event *event, void *arg)
2351250963Sachim{
2352250963Sachim
2353250963Sachim	switch (event->ev_type) {
2354250963Sachim	case AAC_EVENT_CMFREE:
2355250963Sachim		mtx_assert(&sc->aac_io_lock, MA_OWNED);
2356250963Sachim		if (aacraid_alloc_command(sc, (struct aac_command **)arg)) {
2357250963Sachim			aacraid_add_event(sc, event);
2358250963Sachim			return;
2359250963Sachim		}
2360250963Sachim		free(event, M_AACRAIDBUF);
2361250963Sachim		wakeup(arg);
2362250963Sachim		break;
2363250963Sachim	default:
2364250963Sachim		break;
2365250963Sachim	}
2366250963Sachim}
2367250963Sachim
2368250963Sachim/*
2369250963Sachim * Send a FIB supplied from userspace
2370250963Sachim */
2371250963Sachimstatic int
2372250963Sachimaac_ioctl_sendfib(struct aac_softc *sc, caddr_t ufib)
2373250963Sachim{
2374250963Sachim	struct aac_command *cm;
2375250963Sachim	int size, error;
2376250963Sachim
2377250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
2378250963Sachim
2379250963Sachim	cm = NULL;
2380250963Sachim
2381250963Sachim	/*
2382250963Sachim	 * Get a command
2383250963Sachim	 */
2384250963Sachim	mtx_lock(&sc->aac_io_lock);
2385250963Sachim	if (aacraid_alloc_command(sc, &cm)) {
2386250963Sachim		struct aac_event *event;
2387250963Sachim
2388250963Sachim		event = malloc(sizeof(struct aac_event), M_AACRAIDBUF,
2389250963Sachim		    M_NOWAIT | M_ZERO);
2390250963Sachim		if (event == NULL) {
2391250963Sachim			error = EBUSY;
2392250963Sachim			mtx_unlock(&sc->aac_io_lock);
2393250963Sachim			goto out;
2394250963Sachim		}
2395250963Sachim		event->ev_type = AAC_EVENT_CMFREE;
2396250963Sachim		event->ev_callback = aac_ioctl_event;
2397250963Sachim		event->ev_arg = &cm;
2398250963Sachim		aacraid_add_event(sc, event);
2399250963Sachim		msleep(cm, &sc->aac_io_lock, 0, "aacraid_ctlsfib", 0);
2400250963Sachim	}
2401250963Sachim	mtx_unlock(&sc->aac_io_lock);
2402250963Sachim
2403250963Sachim	/*
2404250963Sachim	 * Fetch the FIB header, then re-copy to get data as well.
2405250963Sachim	 */
2406250963Sachim	if ((error = copyin(ufib, cm->cm_fib,
2407250963Sachim			    sizeof(struct aac_fib_header))) != 0)
2408250963Sachim		goto out;
2409250963Sachim	size = cm->cm_fib->Header.Size + sizeof(struct aac_fib_header);
2410250963Sachim	if (size > sc->aac_max_fib_size) {
2411250963Sachim		device_printf(sc->aac_dev, "incoming FIB oversized (%d > %d)\n",
2412250963Sachim			      size, sc->aac_max_fib_size);
2413250963Sachim		size = sc->aac_max_fib_size;
2414250963Sachim	}
2415250963Sachim	if ((error = copyin(ufib, cm->cm_fib, size)) != 0)
2416250963Sachim		goto out;
2417250963Sachim	cm->cm_fib->Header.Size = size;
2418250963Sachim	cm->cm_timestamp = time_uptime;
2419250963Sachim	cm->cm_datalen = 0;
2420250963Sachim
2421250963Sachim	/*
2422250963Sachim	 * Pass the FIB to the controller, wait for it to complete.
2423250963Sachim	 */
2424250963Sachim	mtx_lock(&sc->aac_io_lock);
2425250963Sachim	error = aacraid_wait_command(cm);
2426250963Sachim	mtx_unlock(&sc->aac_io_lock);
2427250963Sachim	if (error != 0) {
2428250963Sachim		device_printf(sc->aac_dev,
2429250963Sachim			      "aacraid_wait_command return %d\n", error);
2430250963Sachim		goto out;
2431250963Sachim	}
2432250963Sachim
2433250963Sachim	/*
2434250963Sachim	 * Copy the FIB and data back out to the caller.
2435250963Sachim	 */
2436250963Sachim	size = cm->cm_fib->Header.Size;
2437250963Sachim	if (size > sc->aac_max_fib_size) {
2438250963Sachim		device_printf(sc->aac_dev, "outbound FIB oversized (%d > %d)\n",
2439250963Sachim			      size, sc->aac_max_fib_size);
2440250963Sachim		size = sc->aac_max_fib_size;
2441250963Sachim	}
2442250963Sachim	error = copyout(cm->cm_fib, ufib, size);
2443250963Sachim
2444250963Sachimout:
2445250963Sachim	if (cm != NULL) {
2446250963Sachim		mtx_lock(&sc->aac_io_lock);
2447250963Sachim		aacraid_release_command(cm);
2448250963Sachim		mtx_unlock(&sc->aac_io_lock);
2449250963Sachim	}
2450250963Sachim	return(error);
2451250963Sachim}
2452250963Sachim
2453250963Sachim/*
2454250963Sachim * Send a passthrough FIB supplied from userspace
2455250963Sachim */
2456250963Sachimstatic int
2457250963Sachimaac_ioctl_send_raw_srb(struct aac_softc *sc, caddr_t arg)
2458250963Sachim{
2459250963Sachim	struct aac_command *cm;
2460250963Sachim	struct aac_fib *fib;
2461250963Sachim	struct aac_srb *srbcmd;
2462250963Sachim	struct aac_srb *user_srb = (struct aac_srb *)arg;
2463250963Sachim	void *user_reply;
2464250963Sachim	int error, transfer_data = 0;
2465250963Sachim	bus_dmamap_t orig_map = 0;
2466250963Sachim	u_int32_t fibsize = 0;
2467250963Sachim	u_int64_t srb_sg_address;
2468250963Sachim	u_int32_t srb_sg_bytecount;
2469250963Sachim
2470250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
2471250963Sachim
2472250963Sachim	cm = NULL;
2473250963Sachim
2474250963Sachim	mtx_lock(&sc->aac_io_lock);
2475250963Sachim	if (aacraid_alloc_command(sc, &cm)) {
2476250963Sachim		struct aac_event *event;
2477250963Sachim
2478250963Sachim		event = malloc(sizeof(struct aac_event), M_AACRAIDBUF,
2479250963Sachim		    M_NOWAIT | M_ZERO);
2480250963Sachim		if (event == NULL) {
2481250963Sachim			error = EBUSY;
2482250963Sachim			mtx_unlock(&sc->aac_io_lock);
2483250963Sachim			goto out;
2484250963Sachim		}
2485250963Sachim		event->ev_type = AAC_EVENT_CMFREE;
2486250963Sachim		event->ev_callback = aac_ioctl_event;
2487250963Sachim		event->ev_arg = &cm;
2488250963Sachim		aacraid_add_event(sc, event);
2489250963Sachim		msleep(cm, &sc->aac_io_lock, 0, "aacraid_ctlsraw", 0);
2490250963Sachim	}
2491250963Sachim	mtx_unlock(&sc->aac_io_lock);
2492250963Sachim
2493250963Sachim	cm->cm_data = NULL;
2494250963Sachim	/* save original dma map */
2495250963Sachim	orig_map = cm->cm_datamap;
2496250963Sachim
2497250963Sachim	fib = cm->cm_fib;
2498250963Sachim	srbcmd = (struct aac_srb *)fib->data;
2499250963Sachim	if ((error = copyin((void *)&user_srb->data_len, &fibsize,
2500250963Sachim		sizeof (u_int32_t)) != 0))
2501250963Sachim		goto out;
2502250963Sachim	if (fibsize > (sc->aac_max_fib_size-sizeof(struct aac_fib_header))) {
2503250963Sachim		error = EINVAL;
2504250963Sachim		goto out;
2505250963Sachim	}
2506250963Sachim	if ((error = copyin((void *)user_srb, srbcmd, fibsize) != 0))
2507250963Sachim		goto out;
2508250963Sachim
2509250963Sachim	srbcmd->function = 0;		/* SRBF_ExecuteScsi */
2510250963Sachim	srbcmd->retry_limit = 0;	/* obsolete */
2511250963Sachim
2512250963Sachim	/* only one sg element from userspace supported */
2513250963Sachim	if (srbcmd->sg_map.SgCount > 1) {
2514250963Sachim		error = EINVAL;
2515250963Sachim		goto out;
2516250963Sachim	}
2517250963Sachim	/* check fibsize */
2518250963Sachim	if (fibsize == (sizeof(struct aac_srb) +
2519250963Sachim		srbcmd->sg_map.SgCount * sizeof(struct aac_sg_entry))) {
2520250963Sachim		struct aac_sg_entry *sgp = srbcmd->sg_map.SgEntry;
2521250963Sachim		srb_sg_bytecount = sgp->SgByteCount;
2522250963Sachim		srb_sg_address = (u_int64_t)sgp->SgAddress;
2523250963Sachim	} else if (fibsize == (sizeof(struct aac_srb) +
2524250963Sachim		srbcmd->sg_map.SgCount * sizeof(struct aac_sg_entry64))) {
2525251013Smarcel#ifdef __LP64__
2526250963Sachim		struct aac_sg_entry64 *sgp =
2527250963Sachim			(struct aac_sg_entry64 *)srbcmd->sg_map.SgEntry;
2528250963Sachim		srb_sg_bytecount = sgp->SgByteCount;
2529250963Sachim		srb_sg_address = sgp->SgAddress;
2530250963Sachim		if (srb_sg_address > 0xffffffffull &&
2531250963Sachim			!(sc->flags & AAC_FLAGS_SG_64BIT))
2532250963Sachim#endif
2533250963Sachim		{
2534250963Sachim			error = EINVAL;
2535250963Sachim			goto out;
2536250963Sachim		}
2537250963Sachim	} else {
2538250963Sachim		error = EINVAL;
2539250963Sachim		goto out;
2540250963Sachim	}
2541250963Sachim	user_reply = (char *)arg + fibsize;
2542250963Sachim	srbcmd->data_len = srb_sg_bytecount;
2543250963Sachim	if (srbcmd->sg_map.SgCount == 1)
2544250963Sachim		transfer_data = 1;
2545250963Sachim
2546250963Sachim	if (transfer_data) {
2547250963Sachim		/*
2548250963Sachim		 * Create DMA tag for the passthr. data buffer and allocate it.
2549250963Sachim		 */
2550250963Sachim		if (bus_dma_tag_create(sc->aac_parent_dmat, 	/* parent */
2551250963Sachim			1, 0,			/* algnmnt, boundary */
2552250963Sachim			(sc->flags & AAC_FLAGS_SG_64BIT) ?
2553250963Sachim			BUS_SPACE_MAXADDR_32BIT :
2554250963Sachim			0x7fffffff,		/* lowaddr */
2555250963Sachim			BUS_SPACE_MAXADDR, 	/* highaddr */
2556250963Sachim			NULL, NULL, 		/* filter, filterarg */
2557250963Sachim			srb_sg_bytecount, 	/* size */
2558250963Sachim			sc->aac_sg_tablesize,	/* nsegments */
2559250963Sachim			srb_sg_bytecount, 	/* maxsegsize */
2560250963Sachim			0,			/* flags */
2561250963Sachim			NULL, NULL,		/* No locking needed */
2562250963Sachim			&cm->cm_passthr_dmat)) {
2563250963Sachim			error = ENOMEM;
2564250963Sachim			goto out;
2565250963Sachim		}
2566250963Sachim		if (bus_dmamem_alloc(cm->cm_passthr_dmat, (void **)&cm->cm_data,
2567250963Sachim			BUS_DMA_NOWAIT, &cm->cm_datamap)) {
2568250963Sachim			error = ENOMEM;
2569250963Sachim			goto out;
2570250963Sachim		}
2571250963Sachim		/* fill some cm variables */
2572250963Sachim		cm->cm_datalen = srb_sg_bytecount;
2573250963Sachim		if (srbcmd->flags & AAC_SRB_FLAGS_DATA_IN)
2574250963Sachim			cm->cm_flags |= AAC_CMD_DATAIN;
2575250963Sachim		if (srbcmd->flags & AAC_SRB_FLAGS_DATA_OUT)
2576250963Sachim			cm->cm_flags |= AAC_CMD_DATAOUT;
2577250963Sachim
2578250963Sachim		if (srbcmd->flags & AAC_SRB_FLAGS_DATA_OUT) {
2579251013Smarcel			if ((error = copyin((void *)(uintptr_t)srb_sg_address,
2580250963Sachim				cm->cm_data, cm->cm_datalen)) != 0)
2581250963Sachim				goto out;
2582250963Sachim			/* sync required for bus_dmamem_alloc() alloc. mem.? */
2583250963Sachim			bus_dmamap_sync(cm->cm_passthr_dmat, cm->cm_datamap,
2584250963Sachim				BUS_DMASYNC_PREWRITE);
2585250963Sachim		}
2586250963Sachim	}
2587250963Sachim
2588250963Sachim	/* build the FIB */
2589250963Sachim	fib->Header.Size = sizeof(struct aac_fib_header) +
2590250963Sachim		sizeof(struct aac_srb);
2591250963Sachim	fib->Header.XferState =
2592250963Sachim		AAC_FIBSTATE_HOSTOWNED   |
2593250963Sachim		AAC_FIBSTATE_INITIALISED |
2594250963Sachim		AAC_FIBSTATE_EMPTY	 |
2595250963Sachim		AAC_FIBSTATE_FROMHOST	 |
2596250963Sachim		AAC_FIBSTATE_REXPECTED   |
2597250963Sachim		AAC_FIBSTATE_NORM	 |
2598250963Sachim		AAC_FIBSTATE_ASYNC;
2599250963Sachim
2600250963Sachim	fib->Header.Command = (sc->flags & AAC_FLAGS_SG_64BIT) ?
2601250963Sachim		ScsiPortCommandU64 : ScsiPortCommand;
2602250963Sachim	cm->cm_sgtable = (struct aac_sg_table *)&srbcmd->sg_map;
2603250963Sachim
2604250963Sachim	/* send command */
2605250963Sachim	if (transfer_data) {
2606250963Sachim		bus_dmamap_load(cm->cm_passthr_dmat,
2607250963Sachim			cm->cm_datamap, cm->cm_data,
2608250963Sachim			cm->cm_datalen,
2609250963Sachim			aacraid_map_command_sg, cm, 0);
2610250963Sachim	} else {
2611250963Sachim		aacraid_map_command_sg(cm, NULL, 0, 0);
2612250963Sachim	}
2613250963Sachim
2614250963Sachim	/* wait for completion */
2615250963Sachim	mtx_lock(&sc->aac_io_lock);
2616250963Sachim	while (!(cm->cm_flags & AAC_CMD_COMPLETED))
2617250963Sachim		msleep(cm, &sc->aac_io_lock, 0, "aacraid_ctlsrw2", 0);
2618250963Sachim	mtx_unlock(&sc->aac_io_lock);
2619250963Sachim
2620250963Sachim	/* copy data */
2621250963Sachim	if (transfer_data && (srbcmd->flags & AAC_SRB_FLAGS_DATA_IN)) {
2622251013Smarcel		if ((error = copyout(cm->cm_data,
2623251013Smarcel			(void *)(uintptr_t)srb_sg_address,
2624250963Sachim			cm->cm_datalen)) != 0)
2625250963Sachim			goto out;
2626250963Sachim		/* sync required for bus_dmamem_alloc() allocated mem.? */
2627250963Sachim		bus_dmamap_sync(cm->cm_passthr_dmat, cm->cm_datamap,
2628250963Sachim				BUS_DMASYNC_POSTREAD);
2629250963Sachim	}
2630250963Sachim
2631250963Sachim	/* status */
2632250963Sachim	error = copyout(fib->data, user_reply, sizeof(struct aac_srb_response));
2633250963Sachim
2634250963Sachimout:
2635250963Sachim	if (cm && cm->cm_data) {
2636250963Sachim		if (transfer_data)
2637250963Sachim			bus_dmamap_unload(cm->cm_passthr_dmat, cm->cm_datamap);
2638250963Sachim		bus_dmamem_free(cm->cm_passthr_dmat, cm->cm_data, cm->cm_datamap);
2639250963Sachim		cm->cm_datamap = orig_map;
2640250963Sachim	}
2641250963Sachim	if (cm && cm->cm_passthr_dmat)
2642250963Sachim		bus_dma_tag_destroy(cm->cm_passthr_dmat);
2643250963Sachim	if (cm) {
2644250963Sachim		mtx_lock(&sc->aac_io_lock);
2645250963Sachim		aacraid_release_command(cm);
2646250963Sachim		mtx_unlock(&sc->aac_io_lock);
2647250963Sachim	}
2648250963Sachim	return(error);
2649250963Sachim}
2650250963Sachim
2651250963Sachim/*
2652250963Sachim * Request an AIF from the controller (new comm. type1)
2653250963Sachim */
2654250963Sachimstatic void
2655250963Sachimaac_request_aif(struct aac_softc *sc)
2656250963Sachim{
2657250963Sachim	struct aac_command *cm;
2658250963Sachim	struct aac_fib *fib;
2659250963Sachim
2660250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
2661250963Sachim
2662250963Sachim	if (aacraid_alloc_command(sc, &cm)) {
2663250963Sachim		sc->aif_pending = 1;
2664250963Sachim		return;
2665250963Sachim	}
2666250963Sachim	sc->aif_pending = 0;
2667250963Sachim
2668250963Sachim	/* build the FIB */
2669250963Sachim	fib = cm->cm_fib;
2670250963Sachim	fib->Header.Size = sizeof(struct aac_fib);
2671250963Sachim	fib->Header.XferState =
2672250963Sachim        AAC_FIBSTATE_HOSTOWNED   |
2673250963Sachim        AAC_FIBSTATE_INITIALISED |
2674250963Sachim        AAC_FIBSTATE_EMPTY	 |
2675250963Sachim        AAC_FIBSTATE_FROMHOST	 |
2676250963Sachim        AAC_FIBSTATE_REXPECTED   |
2677250963Sachim        AAC_FIBSTATE_NORM	 |
2678250963Sachim        AAC_FIBSTATE_ASYNC;
2679250963Sachim	/* set AIF marker */
2680250963Sachim	fib->Header.Handle = 0x00800000;
2681250963Sachim	fib->Header.Command = AifRequest;
2682250963Sachim	((struct aac_aif_command *)fib->data)->command = AifReqEvent;
2683250963Sachim
2684250963Sachim	aacraid_map_command_sg(cm, NULL, 0, 0);
2685250963Sachim}
2686250963Sachim
2687250963Sachim
2688250963Sachim#if __FreeBSD_version >= 702000
2689250963Sachim/*
2690250963Sachim * cdevpriv interface private destructor.
2691250963Sachim */
2692250963Sachimstatic void
2693250963Sachimaac_cdevpriv_dtor(void *arg)
2694250963Sachim{
2695250963Sachim	struct aac_softc *sc;
2696250963Sachim
2697250963Sachim	sc = arg;
2698250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
2699250963Sachim	mtx_lock(&Giant);
2700250963Sachim	device_unbusy(sc->aac_dev);
2701250963Sachim	mtx_unlock(&Giant);
2702250963Sachim}
2703250963Sachim#else
2704250963Sachimstatic int
2705250963Sachimaac_close(struct cdev *dev, int flags, int fmt, struct thread *td)
2706250963Sachim{
2707250963Sachim	struct aac_softc *sc;
2708250963Sachim
2709250963Sachim	sc = dev->si_drv1;
2710250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
2711250963Sachim	return 0;
2712250963Sachim}
2713250963Sachim#endif
2714250963Sachim
2715250963Sachim/*
2716250963Sachim * Handle an AIF sent to us by the controller; queue it for later reference.
2717250963Sachim * If the queue fills up, then drop the older entries.
2718250963Sachim */
2719250963Sachimstatic void
2720250963Sachimaac_handle_aif(struct aac_softc *sc, struct aac_fib *fib)
2721250963Sachim{
2722250963Sachim	struct aac_aif_command *aif;
2723250963Sachim	struct aac_container *co, *co_next;
2724250963Sachim	struct aac_fib_context *ctx;
2725250963Sachim	struct aac_fib *sync_fib;
2726250963Sachim	struct aac_mntinforesp mir;
2727250963Sachim	int next, current, found;
2728250963Sachim	int count = 0, changed = 0, i = 0;
2729250963Sachim	u_int32_t channel, uid;
2730250963Sachim
2731250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
2732250963Sachim
2733250963Sachim	aif = (struct aac_aif_command*)&fib->data[0];
2734250963Sachim	aacraid_print_aif(sc, aif);
2735250963Sachim
2736250963Sachim	/* Is it an event that we should care about? */
2737250963Sachim	switch (aif->command) {
2738250963Sachim	case AifCmdEventNotify:
2739250963Sachim		switch (aif->data.EN.type) {
2740250963Sachim		case AifEnAddContainer:
2741250963Sachim		case AifEnDeleteContainer:
2742250963Sachim			/*
2743250963Sachim			 * A container was added or deleted, but the message
2744250963Sachim			 * doesn't tell us anything else!  Re-enumerate the
2745250963Sachim			 * containers and sort things out.
2746250963Sachim			 */
2747250963Sachim			aac_alloc_sync_fib(sc, &sync_fib);
2748250963Sachim			do {
2749250963Sachim				/*
2750250963Sachim				 * Ask the controller for its containers one at
2751250963Sachim				 * a time.
2752250963Sachim				 * XXX What if the controller's list changes
2753250963Sachim				 * midway through this enumaration?
2754250963Sachim				 * XXX This should be done async.
2755250963Sachim				 */
2756250963Sachim				if (aac_get_container_info(sc, sync_fib, i,
2757250963Sachim					&mir, &uid) != 0)
2758250963Sachim					continue;
2759250963Sachim				if (i == 0)
2760250963Sachim					count = mir.MntRespCount;
2761250963Sachim				/*
2762250963Sachim				 * Check the container against our list.
2763250963Sachim				 * co->co_found was already set to 0 in a
2764250963Sachim				 * previous run.
2765250963Sachim				 */
2766250963Sachim				if ((mir.Status == ST_OK) &&
2767250963Sachim				    (mir.MntTable[0].VolType != CT_NONE)) {
2768250963Sachim					found = 0;
2769250963Sachim					TAILQ_FOREACH(co,
2770250963Sachim						      &sc->aac_container_tqh,
2771250963Sachim						      co_link) {
2772250963Sachim						if (co->co_mntobj.ObjectId ==
2773250963Sachim						    mir.MntTable[0].ObjectId) {
2774250963Sachim							co->co_found = 1;
2775250963Sachim							found = 1;
2776250963Sachim							break;
2777250963Sachim						}
2778250963Sachim					}
2779250963Sachim					/*
2780250963Sachim					 * If the container matched, continue
2781250963Sachim					 * in the list.
2782250963Sachim					 */
2783250963Sachim					if (found) {
2784250963Sachim						i++;
2785250963Sachim						continue;
2786250963Sachim					}
2787250963Sachim
2788250963Sachim					/*
2789250963Sachim					 * This is a new container.  Do all the
2790250963Sachim					 * appropriate things to set it up.
2791250963Sachim					 */
2792250963Sachim					aac_add_container(sc, &mir, 1, uid);
2793250963Sachim					changed = 1;
2794250963Sachim				}
2795250963Sachim				i++;
2796250963Sachim			} while ((i < count) && (i < AAC_MAX_CONTAINERS));
2797250963Sachim			aac_release_sync_fib(sc);
2798250963Sachim
2799250963Sachim			/*
2800250963Sachim			 * Go through our list of containers and see which ones
2801250963Sachim			 * were not marked 'found'.  Since the controller didn't
2802250963Sachim			 * list them they must have been deleted.  Do the
2803250963Sachim			 * appropriate steps to destroy the device.  Also reset
2804250963Sachim			 * the co->co_found field.
2805250963Sachim			 */
2806250963Sachim			co = TAILQ_FIRST(&sc->aac_container_tqh);
2807250963Sachim			while (co != NULL) {
2808250963Sachim				if (co->co_found == 0) {
2809250963Sachim					co_next = TAILQ_NEXT(co, co_link);
2810250963Sachim					TAILQ_REMOVE(&sc->aac_container_tqh, co,
2811250963Sachim						     co_link);
2812250963Sachim					free(co, M_AACRAIDBUF);
2813250963Sachim					changed = 1;
2814250963Sachim					co = co_next;
2815250963Sachim				} else {
2816250963Sachim					co->co_found = 0;
2817250963Sachim					co = TAILQ_NEXT(co, co_link);
2818250963Sachim				}
2819250963Sachim			}
2820250963Sachim
2821250963Sachim			/* Attach the newly created containers */
2822250963Sachim			if (changed) {
2823250963Sachim				if (sc->cam_rescan_cb != NULL)
2824250963Sachim					sc->cam_rescan_cb(sc, 0,
2825250963Sachim				    	AAC_CAM_TARGET_WILDCARD);
2826250963Sachim			}
2827250963Sachim
2828250963Sachim			break;
2829250963Sachim
2830250963Sachim		case AifEnEnclosureManagement:
2831250963Sachim			switch (aif->data.EN.data.EEE.eventType) {
2832250963Sachim			case AIF_EM_DRIVE_INSERTION:
2833250963Sachim			case AIF_EM_DRIVE_REMOVAL:
2834250963Sachim				channel = aif->data.EN.data.EEE.unitID;
2835250963Sachim				if (sc->cam_rescan_cb != NULL)
2836250963Sachim					sc->cam_rescan_cb(sc,
2837250963Sachim					    ((channel>>24) & 0xF) + 1,
2838250963Sachim					    (channel & 0xFFFF));
2839250963Sachim				break;
2840250963Sachim			}
2841250963Sachim			break;
2842250963Sachim
2843250963Sachim		case AifEnAddJBOD:
2844250963Sachim		case AifEnDeleteJBOD:
2845250963Sachim		case AifRawDeviceRemove:
2846250963Sachim			channel = aif->data.EN.data.ECE.container;
2847250963Sachim			if (sc->cam_rescan_cb != NULL)
2848250963Sachim				sc->cam_rescan_cb(sc, ((channel>>24) & 0xF) + 1,
2849250963Sachim				    AAC_CAM_TARGET_WILDCARD);
2850250963Sachim			break;
2851250963Sachim
2852250963Sachim		default:
2853250963Sachim			break;
2854250963Sachim		}
2855250963Sachim
2856250963Sachim	default:
2857250963Sachim		break;
2858250963Sachim	}
2859250963Sachim
2860250963Sachim	/* Copy the AIF data to the AIF queue for ioctl retrieval */
2861250963Sachim	current = sc->aifq_idx;
2862250963Sachim	next = (current + 1) % AAC_AIFQ_LENGTH;
2863250963Sachim	if (next == 0)
2864250963Sachim		sc->aifq_filled = 1;
2865250963Sachim	bcopy(fib, &sc->aac_aifq[current], sizeof(struct aac_fib));
2866250963Sachim	/* modify AIF contexts */
2867250963Sachim	if (sc->aifq_filled) {
2868250963Sachim		for (ctx = sc->fibctx; ctx; ctx = ctx->next) {
2869250963Sachim			if (next == ctx->ctx_idx)
2870250963Sachim				ctx->ctx_wrap = 1;
2871250963Sachim			else if (current == ctx->ctx_idx && ctx->ctx_wrap)
2872250963Sachim				ctx->ctx_idx = next;
2873250963Sachim		}
2874250963Sachim	}
2875250963Sachim	sc->aifq_idx = next;
2876250963Sachim	/* On the off chance that someone is sleeping for an aif... */
2877250963Sachim	if (sc->aac_state & AAC_STATE_AIF_SLEEPER)
2878250963Sachim		wakeup(sc->aac_aifq);
2879250963Sachim	/* Wakeup any poll()ers */
2880250963Sachim	selwakeuppri(&sc->rcv_select, PRIBIO);
2881250963Sachim
2882250963Sachim	return;
2883250963Sachim}
2884250963Sachim
2885250963Sachim/*
2886250963Sachim * Return the Revision of the driver to userspace and check to see if the
2887250963Sachim * userspace app is possibly compatible.  This is extremely bogus since
2888250963Sachim * our driver doesn't follow Adaptec's versioning system.  Cheat by just
2889250963Sachim * returning what the card reported.
2890250963Sachim */
2891250963Sachimstatic int
2892250963Sachimaac_rev_check(struct aac_softc *sc, caddr_t udata)
2893250963Sachim{
2894250963Sachim	struct aac_rev_check rev_check;
2895250963Sachim	struct aac_rev_check_resp rev_check_resp;
2896250963Sachim	int error = 0;
2897250963Sachim
2898250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
2899250963Sachim
2900250963Sachim	/*
2901250963Sachim	 * Copyin the revision struct from userspace
2902250963Sachim	 */
2903250963Sachim	if ((error = copyin(udata, (caddr_t)&rev_check,
2904250963Sachim			sizeof(struct aac_rev_check))) != 0) {
2905250963Sachim		return error;
2906250963Sachim	}
2907250963Sachim
2908250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "Userland revision= %d\n",
2909250963Sachim	      rev_check.callingRevision.buildNumber);
2910250963Sachim
2911250963Sachim	/*
2912250963Sachim	 * Doctor up the response struct.
2913250963Sachim	 */
2914250963Sachim	rev_check_resp.possiblyCompatible = 1;
2915250963Sachim	rev_check_resp.adapterSWRevision.external.comp.major =
2916250963Sachim	    AAC_DRIVER_MAJOR_VERSION;
2917250963Sachim	rev_check_resp.adapterSWRevision.external.comp.minor =
2918250963Sachim	    AAC_DRIVER_MINOR_VERSION;
2919250963Sachim	rev_check_resp.adapterSWRevision.external.comp.type =
2920250963Sachim	    AAC_DRIVER_TYPE;
2921250963Sachim	rev_check_resp.adapterSWRevision.external.comp.dash =
2922250963Sachim	    AAC_DRIVER_BUGFIX_LEVEL;
2923250963Sachim	rev_check_resp.adapterSWRevision.buildNumber =
2924250963Sachim	    AAC_DRIVER_BUILD;
2925250963Sachim
2926250963Sachim	return(copyout((caddr_t)&rev_check_resp, udata,
2927250963Sachim			sizeof(struct aac_rev_check_resp)));
2928250963Sachim}
2929250963Sachim
2930250963Sachim/*
2931250963Sachim * Pass the fib context to the caller
2932250963Sachim */
2933250963Sachimstatic int
2934250963Sachimaac_open_aif(struct aac_softc *sc, caddr_t arg)
2935250963Sachim{
2936250963Sachim	struct aac_fib_context *fibctx, *ctx;
2937250963Sachim	int error = 0;
2938250963Sachim
2939250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
2940250963Sachim
2941250963Sachim	fibctx = malloc(sizeof(struct aac_fib_context), M_AACRAIDBUF, M_NOWAIT|M_ZERO);
2942250963Sachim	if (fibctx == NULL)
2943250963Sachim		return (ENOMEM);
2944250963Sachim
2945250963Sachim	mtx_lock(&sc->aac_io_lock);
2946250963Sachim	/* all elements are already 0, add to queue */
2947250963Sachim	if (sc->fibctx == NULL)
2948250963Sachim		sc->fibctx = fibctx;
2949250963Sachim	else {
2950250963Sachim		for (ctx = sc->fibctx; ctx->next; ctx = ctx->next)
2951250963Sachim			;
2952250963Sachim		ctx->next = fibctx;
2953250963Sachim		fibctx->prev = ctx;
2954250963Sachim	}
2955250963Sachim
2956250963Sachim	/* evaluate unique value */
2957250963Sachim	fibctx->unique = (*(u_int32_t *)&fibctx & 0xffffffff);
2958250963Sachim	ctx = sc->fibctx;
2959250963Sachim	while (ctx != fibctx) {
2960250963Sachim		if (ctx->unique == fibctx->unique) {
2961250963Sachim			fibctx->unique++;
2962250963Sachim			ctx = sc->fibctx;
2963250963Sachim		} else {
2964250963Sachim			ctx = ctx->next;
2965250963Sachim		}
2966250963Sachim	}
2967250963Sachim
2968250963Sachim	error = copyout(&fibctx->unique, (void *)arg, sizeof(u_int32_t));
2969250963Sachim	mtx_unlock(&sc->aac_io_lock);
2970250963Sachim	if (error)
2971250963Sachim		aac_close_aif(sc, (caddr_t)ctx);
2972250963Sachim	return error;
2973250963Sachim}
2974250963Sachim
2975250963Sachim/*
2976250963Sachim * Close the caller's fib context
2977250963Sachim */
2978250963Sachimstatic int
2979250963Sachimaac_close_aif(struct aac_softc *sc, caddr_t arg)
2980250963Sachim{
2981250963Sachim	struct aac_fib_context *ctx;
2982250963Sachim
2983250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
2984250963Sachim
2985250963Sachim	mtx_lock(&sc->aac_io_lock);
2986250963Sachim	for (ctx = sc->fibctx; ctx; ctx = ctx->next) {
2987250963Sachim		if (ctx->unique == *(uint32_t *)&arg) {
2988250963Sachim			if (ctx == sc->fibctx)
2989250963Sachim				sc->fibctx = NULL;
2990250963Sachim			else {
2991250963Sachim				ctx->prev->next = ctx->next;
2992250963Sachim				if (ctx->next)
2993250963Sachim					ctx->next->prev = ctx->prev;
2994250963Sachim			}
2995250963Sachim			break;
2996250963Sachim		}
2997250963Sachim	}
2998250963Sachim	if (ctx)
2999250963Sachim		free(ctx, M_AACRAIDBUF);
3000250963Sachim
3001250963Sachim	mtx_unlock(&sc->aac_io_lock);
3002250963Sachim	return 0;
3003250963Sachim}
3004250963Sachim
3005250963Sachim/*
3006250963Sachim * Pass the caller the next AIF in their queue
3007250963Sachim */
3008250963Sachimstatic int
3009250963Sachimaac_getnext_aif(struct aac_softc *sc, caddr_t arg)
3010250963Sachim{
3011250963Sachim	struct get_adapter_fib_ioctl agf;
3012250963Sachim	struct aac_fib_context *ctx;
3013250963Sachim	int error;
3014250963Sachim
3015250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
3016250963Sachim
3017250963Sachim	mtx_lock(&sc->aac_io_lock);
3018250963Sachim	if ((error = copyin(arg, &agf, sizeof(agf))) == 0) {
3019250963Sachim		for (ctx = sc->fibctx; ctx; ctx = ctx->next) {
3020250963Sachim			if (agf.AdapterFibContext == ctx->unique)
3021250963Sachim				break;
3022250963Sachim		}
3023250963Sachim		if (!ctx) {
3024250963Sachim			mtx_unlock(&sc->aac_io_lock);
3025250963Sachim			return (EFAULT);
3026250963Sachim		}
3027250963Sachim
3028250963Sachim		error = aac_return_aif(sc, ctx, agf.AifFib);
3029250963Sachim		if (error == EAGAIN && agf.Wait) {
3030250963Sachim			fwprintf(sc, HBA_FLAGS_DBG_AIF_B, "aac_getnext_aif(): waiting for AIF");
3031250963Sachim			sc->aac_state |= AAC_STATE_AIF_SLEEPER;
3032250963Sachim			while (error == EAGAIN) {
3033250963Sachim				mtx_unlock(&sc->aac_io_lock);
3034250963Sachim				error = tsleep(sc->aac_aifq, PRIBIO |
3035250963Sachim					       PCATCH, "aacaif", 0);
3036250963Sachim				mtx_lock(&sc->aac_io_lock);
3037250963Sachim				if (error == 0)
3038250963Sachim					error = aac_return_aif(sc, ctx, agf.AifFib);
3039250963Sachim			}
3040250963Sachim			sc->aac_state &= ~AAC_STATE_AIF_SLEEPER;
3041250963Sachim		}
3042250963Sachim	}
3043250963Sachim	mtx_unlock(&sc->aac_io_lock);
3044250963Sachim	return(error);
3045250963Sachim}
3046250963Sachim
3047250963Sachim/*
3048250963Sachim * Hand the next AIF off the top of the queue out to userspace.
3049250963Sachim */
3050250963Sachimstatic int
3051250963Sachimaac_return_aif(struct aac_softc *sc, struct aac_fib_context *ctx, caddr_t uptr)
3052250963Sachim{
3053250963Sachim	int current, error;
3054250963Sachim
3055250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
3056250963Sachim
3057250963Sachim	current = ctx->ctx_idx;
3058250963Sachim	if (current == sc->aifq_idx && !ctx->ctx_wrap) {
3059250963Sachim		/* empty */
3060250963Sachim		return (EAGAIN);
3061250963Sachim	}
3062250963Sachim	error =
3063250963Sachim		copyout(&sc->aac_aifq[current], (void *)uptr, sizeof(struct aac_fib));
3064250963Sachim	if (error)
3065250963Sachim		device_printf(sc->aac_dev,
3066250963Sachim		    "aac_return_aif: copyout returned %d\n", error);
3067250963Sachim	else {
3068250963Sachim		ctx->ctx_wrap = 0;
3069250963Sachim		ctx->ctx_idx = (current + 1) % AAC_AIFQ_LENGTH;
3070250963Sachim	}
3071250963Sachim	return(error);
3072250963Sachim}
3073250963Sachim
3074250963Sachimstatic int
3075250963Sachimaac_get_pci_info(struct aac_softc *sc, caddr_t uptr)
3076250963Sachim{
3077250963Sachim	struct aac_pci_info {
3078250963Sachim		u_int32_t bus;
3079250963Sachim		u_int32_t slot;
3080250963Sachim	} pciinf;
3081250963Sachim	int error;
3082250963Sachim
3083250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
3084250963Sachim
3085250963Sachim	pciinf.bus = pci_get_bus(sc->aac_dev);
3086250963Sachim	pciinf.slot = pci_get_slot(sc->aac_dev);
3087250963Sachim
3088250963Sachim	error = copyout((caddr_t)&pciinf, uptr,
3089250963Sachim			sizeof(struct aac_pci_info));
3090250963Sachim
3091250963Sachim	return (error);
3092250963Sachim}
3093250963Sachim
3094250963Sachimstatic int
3095250963Sachimaac_supported_features(struct aac_softc *sc, caddr_t uptr)
3096250963Sachim{
3097250963Sachim	struct aac_features f;
3098250963Sachim	int error;
3099250963Sachim
3100250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
3101250963Sachim
3102250963Sachim	if ((error = copyin(uptr, &f, sizeof (f))) != 0)
3103250963Sachim		return (error);
3104250963Sachim
3105250963Sachim	/*
3106250963Sachim	 * When the management driver receives FSACTL_GET_FEATURES ioctl with
3107250963Sachim	 * ALL zero in the featuresState, the driver will return the current
3108250963Sachim	 * state of all the supported features, the data field will not be
3109250963Sachim	 * valid.
3110250963Sachim	 * When the management driver receives FSACTL_GET_FEATURES ioctl with
3111250963Sachim	 * a specific bit set in the featuresState, the driver will return the
3112250963Sachim	 * current state of this specific feature and whatever data that are
3113250963Sachim	 * associated with the feature in the data field or perform whatever
3114250963Sachim	 * action needed indicates in the data field.
3115250963Sachim	 */
3116250963Sachim	 if (f.feat.fValue == 0) {
3117250963Sachim		f.feat.fBits.largeLBA =
3118250963Sachim		    (sc->flags & AAC_FLAGS_LBA_64BIT) ? 1 : 0;
3119250963Sachim		f.feat.fBits.JBODSupport = 1;
3120250963Sachim		/* TODO: In the future, add other features state here as well */
3121250963Sachim	} else {
3122250963Sachim		if (f.feat.fBits.largeLBA)
3123250963Sachim			f.feat.fBits.largeLBA =
3124250963Sachim			    (sc->flags & AAC_FLAGS_LBA_64BIT) ? 1 : 0;
3125250963Sachim		/* TODO: Add other features state and data in the future */
3126250963Sachim	}
3127250963Sachim
3128250963Sachim	error = copyout(&f, uptr, sizeof (f));
3129250963Sachim	return (error);
3130250963Sachim}
3131250963Sachim
3132250963Sachim/*
3133250963Sachim * Give the userland some information about the container.  The AAC arch
3134250963Sachim * expects the driver to be a SCSI passthrough type driver, so it expects
3135250963Sachim * the containers to have b:t:l numbers.  Fake it.
3136250963Sachim */
3137250963Sachimstatic int
3138250963Sachimaac_query_disk(struct aac_softc *sc, caddr_t uptr)
3139250963Sachim{
3140250963Sachim	struct aac_query_disk query_disk;
3141250963Sachim	struct aac_container *co;
3142250963Sachim	int error, id;
3143250963Sachim
3144250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
3145250963Sachim
3146250963Sachim	mtx_lock(&sc->aac_io_lock);
3147250963Sachim	error = copyin(uptr, (caddr_t)&query_disk,
3148250963Sachim		       sizeof(struct aac_query_disk));
3149250963Sachim	if (error) {
3150250963Sachim		mtx_unlock(&sc->aac_io_lock);
3151250963Sachim		return (error);
3152250963Sachim	}
3153250963Sachim
3154250963Sachim	id = query_disk.ContainerNumber;
3155250963Sachim	if (id == -1) {
3156250963Sachim		mtx_unlock(&sc->aac_io_lock);
3157250963Sachim		return (EINVAL);
3158250963Sachim	}
3159250963Sachim
3160250963Sachim	TAILQ_FOREACH(co, &sc->aac_container_tqh, co_link) {
3161250963Sachim		if (co->co_mntobj.ObjectId == id)
3162250963Sachim			break;
3163250963Sachim		}
3164250963Sachim
3165250963Sachim	if (co == NULL) {
3166250963Sachim			query_disk.Valid = 0;
3167250963Sachim			query_disk.Locked = 0;
3168250963Sachim			query_disk.Deleted = 1;		/* XXX is this right? */
3169250963Sachim	} else {
3170250963Sachim		query_disk.Valid = 1;
3171250963Sachim		query_disk.Locked = 1;
3172250963Sachim		query_disk.Deleted = 0;
3173250963Sachim		query_disk.Bus = device_get_unit(sc->aac_dev);
3174250963Sachim		query_disk.Target = 0;
3175250963Sachim		query_disk.Lun = 0;
3176250963Sachim		query_disk.UnMapped = 0;
3177250963Sachim	}
3178250963Sachim
3179250963Sachim	error = copyout((caddr_t)&query_disk, uptr,
3180250963Sachim			sizeof(struct aac_query_disk));
3181250963Sachim
3182250963Sachim	mtx_unlock(&sc->aac_io_lock);
3183250963Sachim	return (error);
3184250963Sachim}
3185250963Sachim
3186250963Sachimstatic void
3187250963Sachimaac_container_bus(struct aac_softc *sc)
3188250963Sachim{
3189250963Sachim	struct aac_sim *sim;
3190250963Sachim	device_t child;
3191250963Sachim
3192250963Sachim	sim =(struct aac_sim *)malloc(sizeof(struct aac_sim),
3193250963Sachim		M_AACRAIDBUF, M_NOWAIT | M_ZERO);
3194250963Sachim	if (sim == NULL) {
3195250963Sachim		device_printf(sc->aac_dev,
3196250963Sachim	    	"No memory to add container bus\n");
3197250963Sachim		panic("Out of memory?!");
3198250963Sachim	};
3199250963Sachim	child = device_add_child(sc->aac_dev, "aacraidp", -1);
3200250963Sachim	if (child == NULL) {
3201250963Sachim		device_printf(sc->aac_dev,
3202250963Sachim	    	"device_add_child failed for container bus\n");
3203250963Sachim		free(sim, M_AACRAIDBUF);
3204250963Sachim		panic("Out of memory?!");
3205250963Sachim	}
3206250963Sachim
3207250963Sachim	sim->TargetsPerBus = AAC_MAX_CONTAINERS;
3208250963Sachim	sim->BusNumber = 0;
3209250963Sachim	sim->BusType = CONTAINER_BUS;
3210250963Sachim	sim->InitiatorBusId = -1;
3211250963Sachim	sim->aac_sc = sc;
3212250963Sachim	sim->sim_dev = child;
3213250963Sachim	sim->aac_cam = NULL;
3214250963Sachim
3215250963Sachim	device_set_ivars(child, sim);
3216250963Sachim	device_set_desc(child, "Container Bus");
3217250963Sachim	TAILQ_INSERT_TAIL(&sc->aac_sim_tqh, sim, sim_link);
3218250963Sachim	/*
3219250963Sachim	device_set_desc(child, aac_describe_code(aac_container_types,
3220250963Sachim			mir->MntTable[0].VolType));
3221250963Sachim	*/
3222250963Sachim	bus_generic_attach(sc->aac_dev);
3223250963Sachim}
3224250963Sachim
3225250963Sachimstatic void
3226250963Sachimaac_get_bus_info(struct aac_softc *sc)
3227250963Sachim{
3228250963Sachim	struct aac_fib *fib;
3229250963Sachim	struct aac_ctcfg *c_cmd;
3230250963Sachim	struct aac_ctcfg_resp *c_resp;
3231250963Sachim	struct aac_vmioctl *vmi;
3232250963Sachim	struct aac_vmi_businf_resp *vmi_resp;
3233250963Sachim	struct aac_getbusinf businfo;
3234250963Sachim	struct aac_sim *caminf;
3235250963Sachim	device_t child;
3236250963Sachim	int i, error;
3237250963Sachim
3238250963Sachim	mtx_lock(&sc->aac_io_lock);
3239250963Sachim	aac_alloc_sync_fib(sc, &fib);
3240250963Sachim	c_cmd = (struct aac_ctcfg *)&fib->data[0];
3241250963Sachim	bzero(c_cmd, sizeof(struct aac_ctcfg));
3242250963Sachim
3243250963Sachim	c_cmd->Command = VM_ContainerConfig;
3244250963Sachim	c_cmd->cmd = CT_GET_SCSI_METHOD;
3245250963Sachim	c_cmd->param = 0;
3246250963Sachim
3247250963Sachim	error = aac_sync_fib(sc, ContainerCommand, 0, fib,
3248250963Sachim	    sizeof(struct aac_ctcfg));
3249250963Sachim	if (error) {
3250250963Sachim		device_printf(sc->aac_dev, "Error %d sending "
3251250963Sachim		    "VM_ContainerConfig command\n", error);
3252250963Sachim		aac_release_sync_fib(sc);
3253250963Sachim		mtx_unlock(&sc->aac_io_lock);
3254250963Sachim		return;
3255250963Sachim	}
3256250963Sachim
3257250963Sachim	c_resp = (struct aac_ctcfg_resp *)&fib->data[0];
3258250963Sachim	if (c_resp->Status != ST_OK) {
3259250963Sachim		device_printf(sc->aac_dev, "VM_ContainerConfig returned 0x%x\n",
3260250963Sachim		    c_resp->Status);
3261250963Sachim		aac_release_sync_fib(sc);
3262250963Sachim		mtx_unlock(&sc->aac_io_lock);
3263250963Sachim		return;
3264250963Sachim	}
3265250963Sachim
3266250963Sachim	sc->scsi_method_id = c_resp->param;
3267250963Sachim
3268250963Sachim	vmi = (struct aac_vmioctl *)&fib->data[0];
3269250963Sachim	bzero(vmi, sizeof(struct aac_vmioctl));
3270250963Sachim
3271250963Sachim	vmi->Command = VM_Ioctl;
3272250963Sachim	vmi->ObjType = FT_DRIVE;
3273250963Sachim	vmi->MethId = sc->scsi_method_id;
3274250963Sachim	vmi->ObjId = 0;
3275250963Sachim	vmi->IoctlCmd = GetBusInfo;
3276250963Sachim
3277250963Sachim	error = aac_sync_fib(sc, ContainerCommand, 0, fib,
3278250963Sachim	    sizeof(struct aac_vmi_businf_resp));
3279250963Sachim	if (error) {
3280250963Sachim		device_printf(sc->aac_dev, "Error %d sending VMIoctl command\n",
3281250963Sachim		    error);
3282250963Sachim		aac_release_sync_fib(sc);
3283250963Sachim		mtx_unlock(&sc->aac_io_lock);
3284250963Sachim		return;
3285250963Sachim	}
3286250963Sachim
3287250963Sachim	vmi_resp = (struct aac_vmi_businf_resp *)&fib->data[0];
3288250963Sachim	if (vmi_resp->Status != ST_OK) {
3289250963Sachim		device_printf(sc->aac_dev, "VM_Ioctl returned %d\n",
3290250963Sachim		    vmi_resp->Status);
3291250963Sachim		aac_release_sync_fib(sc);
3292250963Sachim		mtx_unlock(&sc->aac_io_lock);
3293250963Sachim		return;
3294250963Sachim	}
3295250963Sachim
3296250963Sachim	bcopy(&vmi_resp->BusInf, &businfo, sizeof(struct aac_getbusinf));
3297250963Sachim	aac_release_sync_fib(sc);
3298250963Sachim	mtx_unlock(&sc->aac_io_lock);
3299250963Sachim
3300250963Sachim	for (i = 0; i < businfo.BusCount; i++) {
3301250963Sachim		if (businfo.BusValid[i] != AAC_BUS_VALID)
3302250963Sachim			continue;
3303250963Sachim
3304250963Sachim		caminf = (struct aac_sim *)malloc( sizeof(struct aac_sim),
3305250963Sachim		    M_AACRAIDBUF, M_NOWAIT | M_ZERO);
3306250963Sachim		if (caminf == NULL) {
3307250963Sachim			device_printf(sc->aac_dev,
3308250963Sachim			    "No memory to add passthrough bus %d\n", i);
3309250963Sachim			break;
3310250963Sachim		};
3311250963Sachim
3312250963Sachim		child = device_add_child(sc->aac_dev, "aacraidp", -1);
3313250963Sachim		if (child == NULL) {
3314250963Sachim			device_printf(sc->aac_dev,
3315250963Sachim			    "device_add_child failed for passthrough bus %d\n",
3316250963Sachim			    i);
3317250963Sachim			free(caminf, M_AACRAIDBUF);
3318250963Sachim			break;
3319250963Sachim		}
3320250963Sachim
3321250963Sachim		caminf->TargetsPerBus = businfo.TargetsPerBus;
3322250963Sachim		caminf->BusNumber = i+1;
3323250963Sachim		caminf->BusType = PASSTHROUGH_BUS;
3324250963Sachim		caminf->InitiatorBusId = businfo.InitiatorBusId[i];
3325250963Sachim		caminf->aac_sc = sc;
3326250963Sachim		caminf->sim_dev = child;
3327250963Sachim		caminf->aac_cam = NULL;
3328250963Sachim
3329250963Sachim		device_set_ivars(child, caminf);
3330250963Sachim		device_set_desc(child, "SCSI Passthrough Bus");
3331250963Sachim		TAILQ_INSERT_TAIL(&sc->aac_sim_tqh, caminf, sim_link);
3332250963Sachim	}
3333250963Sachim}
3334250963Sachim
3335250963Sachim/*
3336250963Sachim * Check to see if the kernel is up and running. If we are in a
3337250963Sachim * BlinkLED state, return the BlinkLED code.
3338250963Sachim */
3339250963Sachimstatic u_int32_t
3340250963Sachimaac_check_adapter_health(struct aac_softc *sc, u_int8_t *bled)
3341250963Sachim{
3342250963Sachim	u_int32_t ret;
3343250963Sachim
3344250963Sachim	ret = AAC_GET_FWSTATUS(sc);
3345250963Sachim
3346250963Sachim	if (ret & AAC_UP_AND_RUNNING)
3347250963Sachim		ret = 0;
3348250963Sachim	else if (ret & AAC_KERNEL_PANIC && bled)
3349250963Sachim		*bled = (ret >> 16) & 0xff;
3350250963Sachim
3351250963Sachim	return (ret);
3352250963Sachim}
3353250963Sachim
3354250963Sachim/*
3355250963Sachim * Once do an IOP reset, basically have to re-initialize the card as
3356250963Sachim * if coming up from a cold boot, and the driver is responsible for
3357250963Sachim * any IO that was outstanding to the adapter at the time of the IOP
3358250963Sachim * RESET. And prepare the driver for IOP RESET by making the init code
3359250963Sachim * modular with the ability to call it from multiple places.
3360250963Sachim */
3361250963Sachimstatic int
3362250963Sachimaac_reset_adapter(struct aac_softc *sc)
3363250963Sachim{
3364250963Sachim	struct aac_command *cm;
3365250963Sachim	struct aac_fib *fib;
3366250963Sachim	struct aac_pause_command *pc;
3367250963Sachim	u_int32_t status, old_flags, reset_mask, waitCount;
3368250963Sachim
3369250963Sachim	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
3370250963Sachim
3371250963Sachim	if (sc->aac_state & AAC_STATE_RESET) {
3372250963Sachim		device_printf(sc->aac_dev, "aac_reset_adapter() already in progress\n");
3373250963Sachim		return (EINVAL);
3374250963Sachim	}
3375250963Sachim	sc->aac_state |= AAC_STATE_RESET;
3376250963Sachim
3377250963Sachim	/* disable interrupt */
3378250963Sachim	AAC_MASK_INTERRUPTS(sc);
3379250963Sachim
3380250963Sachim	/*
3381250963Sachim	 * Abort all pending commands:
3382250963Sachim	 * a) on the controller
3383250963Sachim	 */
3384250963Sachim	while ((cm = aac_dequeue_busy(sc)) != NULL) {
3385250963Sachim		cm->cm_flags |= AAC_CMD_RESET;
3386250963Sachim
3387250963Sachim		/* is there a completion handler? */
3388250963Sachim		if (cm->cm_complete != NULL) {
3389250963Sachim			cm->cm_complete(cm);
3390250963Sachim		} else {
3391250963Sachim			/* assume that someone is sleeping on this
3392250963Sachim			 * command
3393250963Sachim			 */
3394250963Sachim			wakeup(cm);
3395250963Sachim		}
3396250963Sachim	}
3397250963Sachim
3398250963Sachim	/* b) in the waiting queues */
3399250963Sachim	while ((cm = aac_dequeue_ready(sc)) != NULL) {
3400250963Sachim		cm->cm_flags |= AAC_CMD_RESET;
3401250963Sachim
3402250963Sachim		/* is there a completion handler? */
3403250963Sachim		if (cm->cm_complete != NULL) {
3404250963Sachim			cm->cm_complete(cm);
3405250963Sachim		} else {
3406250963Sachim			/* assume that someone is sleeping on this
3407250963Sachim			 * command
3408250963Sachim			 */
3409250963Sachim			wakeup(cm);
3410250963Sachim		}
3411250963Sachim	}
3412250963Sachim
3413250963Sachim	/* flush drives */
3414250963Sachim	if (aac_check_adapter_health(sc, NULL) == 0) {
3415250963Sachim		mtx_unlock(&sc->aac_io_lock);
3416250963Sachim		(void) aacraid_shutdown(sc->aac_dev);
3417250963Sachim		mtx_lock(&sc->aac_io_lock);
3418250963Sachim	}
3419250963Sachim
3420250963Sachim	/* execute IOP reset */
3421250963Sachim	if (sc->aac_support_opt2 & AAC_SUPPORTED_MU_RESET) {
3422250963Sachim		AAC_MEM0_SETREG4(sc, AAC_IRCSR, AAC_IRCSR_CORES_RST);
3423250963Sachim
3424250963Sachim		/* We need to wait for 5 seconds before accessing the MU again
3425250963Sachim		 * 10000 * 100us = 1000,000us = 1000ms = 1s
3426250963Sachim		 */
3427250963Sachim		waitCount = 5 * 10000;
3428250963Sachim		while (waitCount) {
3429250963Sachim			DELAY(100);			/* delay 100 microseconds */
3430250963Sachim			waitCount--;
3431250963Sachim		}
3432250963Sachim	} else if ((aacraid_sync_command(sc,
3433250963Sachim		AAC_IOP_RESET_ALWAYS, 0, 0, 0, 0, &status, &reset_mask)) != 0) {
3434250963Sachim		/* call IOP_RESET for older firmware */
3435250963Sachim		if ((aacraid_sync_command(sc,
3436250963Sachim			AAC_IOP_RESET, 0, 0, 0, 0, &status, NULL)) != 0) {
3437250963Sachim
3438250963Sachim			if (status == AAC_SRB_STS_INVALID_REQUEST)
3439250963Sachim				device_printf(sc->aac_dev, "IOP_RESET not supported\n");
3440250963Sachim			else
3441250963Sachim				/* probably timeout */
3442250963Sachim				device_printf(sc->aac_dev, "IOP_RESET failed\n");
3443250963Sachim
3444250963Sachim			/* unwind aac_shutdown() */
3445250963Sachim			aac_alloc_sync_fib(sc, &fib);
3446250963Sachim			pc = (struct aac_pause_command *)&fib->data[0];
3447250963Sachim			pc->Command = VM_ContainerConfig;
3448250963Sachim			pc->Type = CT_PAUSE_IO;
3449250963Sachim			pc->Timeout = 1;
3450250963Sachim			pc->Min = 1;
3451250963Sachim			pc->NoRescan = 1;
3452250963Sachim
3453250963Sachim			(void) aac_sync_fib(sc, ContainerCommand, 0, fib,
3454250963Sachim				sizeof (struct aac_pause_command));
3455250963Sachim			aac_release_sync_fib(sc);
3456250963Sachim
3457250963Sachim			goto finish;
3458250963Sachim		}
3459250963Sachim	} else if (sc->aac_support_opt2 & AAC_SUPPORTED_DOORBELL_RESET) {
3460250963Sachim		AAC_MEM0_SETREG4(sc, AAC_SRC_IDBR, reset_mask);
3461250963Sachim		/* We need to wait for 5 seconds before accessing the doorbell again
3462250963Sachim		 * 10000 * 100us = 1000,000us = 1000ms = 1s
3463250963Sachim		 */
3464250963Sachim		waitCount = 5 * 10000;
3465250963Sachim		while (waitCount) {
3466250963Sachim			DELAY(100);			/* delay 100 microseconds */
3467250963Sachim			waitCount--;
3468250963Sachim		}
3469250963Sachim	}
3470250963Sachim
3471250963Sachim	/*
3472250963Sachim	 * Re-read and renegotiate the FIB parameters, as one of the actions
3473250963Sachim	 * that can result from an IOP reset is the running of a new firmware
3474250963Sachim	 * image.
3475250963Sachim	 */
3476250963Sachim	old_flags = sc->flags;
3477250963Sachim	/*
3478250963Sachim	 * Initialize the adapter.
3479250963Sachim	 */
3480250963Sachim	if (aac_check_firmware(sc) != 0)
3481250963Sachim		goto finish;
3482250963Sachim	if (!(sc->flags & AAC_FLAGS_SYNC_MODE)) {
3483250963Sachim		if (aac_init(sc) != 0)
3484250963Sachim			goto finish;
3485250963Sachim	}
3486250963Sachim
3487250963Sachimfinish:
3488250963Sachim	sc->aac_state &= ~AAC_STATE_RESET;
3489250963Sachim	AAC_UNMASK_INTERRUPTS(sc);
3490250963Sachim	aacraid_startio(sc);
3491250963Sachim	return (0);
3492250963Sachim}
3493