xref: /freebsd/sys/dev/aac/aac.c (revision 7029da5c36f2d3cf6bb6c81bf551229f416399e8)
135863739SMike Smith /*-
2718cf2ccSPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3718cf2ccSPedro F. Giffuni  *
435863739SMike Smith  * Copyright (c) 2000 Michael Smith
5c6eafcf2SScott Long  * Copyright (c) 2001 Scott Long
635863739SMike Smith  * Copyright (c) 2000 BSDi
7c6eafcf2SScott Long  * Copyright (c) 2001 Adaptec, Inc.
835863739SMike Smith  * All rights reserved.
935863739SMike Smith  *
1035863739SMike Smith  * Redistribution and use in source and binary forms, with or without
1135863739SMike Smith  * modification, are permitted provided that the following conditions
1235863739SMike Smith  * are met:
1335863739SMike Smith  * 1. Redistributions of source code must retain the above copyright
1435863739SMike Smith  *    notice, this list of conditions and the following disclaimer.
1535863739SMike Smith  * 2. Redistributions in binary form must reproduce the above copyright
1635863739SMike Smith  *    notice, this list of conditions and the following disclaimer in the
1735863739SMike Smith  *    documentation and/or other materials provided with the distribution.
1835863739SMike Smith  *
1935863739SMike Smith  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2035863739SMike Smith  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2135863739SMike Smith  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2235863739SMike Smith  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2335863739SMike Smith  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2435863739SMike Smith  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2535863739SMike Smith  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2635863739SMike Smith  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2735863739SMike Smith  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2835863739SMike Smith  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2935863739SMike Smith  * SUCH DAMAGE.
3035863739SMike Smith  */
3135863739SMike Smith 
32aad970f1SDavid E. O'Brien #include <sys/cdefs.h>
33aad970f1SDavid E. O'Brien __FBSDID("$FreeBSD$");
34aad970f1SDavid E. O'Brien 
3535863739SMike Smith /*
3635863739SMike Smith  * Driver for the Adaptec 'FSA' family of PCI/SCSI RAID adapters.
3735863739SMike Smith  */
387cb209f5SScott Long #define AAC_DRIVERNAME			"aac"
3935863739SMike Smith 
40f6c4dd3fSScott Long #include "opt_aac.h"
41f6c4dd3fSScott Long 
4236e0bf6eSScott Long /* #include <stddef.h> */
4335863739SMike Smith #include <sys/param.h>
4435863739SMike Smith #include <sys/systm.h>
4535863739SMike Smith #include <sys/malloc.h>
4635863739SMike Smith #include <sys/kernel.h>
4736e0bf6eSScott Long #include <sys/kthread.h>
48f287c3e4SBrooks Davis #include <sys/proc.h>
493d04a9d7SScott Long #include <sys/sysctl.h>
50f287c3e4SBrooks Davis #include <sys/sysent.h>
51b3457b51SScott Long #include <sys/poll.h>
52891619a6SPoul-Henning Kamp #include <sys/ioccom.h>
5335863739SMike Smith 
5435863739SMike Smith #include <sys/bus.h>
5535863739SMike Smith #include <sys/conf.h>
5635863739SMike Smith #include <sys/signalvar.h>
570b94a66eSMike Smith #include <sys/time.h>
5836e0bf6eSScott Long #include <sys/eventhandler.h>
597cb209f5SScott Long #include <sys/rman.h>
6035863739SMike Smith 
6135863739SMike Smith #include <machine/bus.h>
6235863739SMike Smith #include <machine/resource.h>
6335863739SMike Smith 
647cb209f5SScott Long #include <dev/pci/pcireg.h>
657cb209f5SScott Long #include <dev/pci/pcivar.h>
667cb209f5SScott Long 
6735863739SMike Smith #include <dev/aac/aacreg.h>
680b0594cdSScott Long #include <sys/aac_ioctl.h>
6935863739SMike Smith #include <dev/aac/aacvar.h>
7035863739SMike Smith #include <dev/aac/aac_tables.h>
7135863739SMike Smith 
7235863739SMike Smith static void	aac_startup(void *arg);
73914da7d0SScott Long static void	aac_add_container(struct aac_softc *sc,
74cbfd045bSScott Long 				  struct aac_mntinforesp *mir, int f);
75fe3cb0e1SScott Long static void	aac_get_bus_info(struct aac_softc *sc);
76ff0991c4SAttilio Rao static void	aac_daemon(void *arg);
7735863739SMike Smith 
7835863739SMike Smith /* Command Processing */
790b94a66eSMike Smith static void	aac_timeout(struct aac_softc *sc);
8035863739SMike Smith static void	aac_complete(void *context, int pending);
8135863739SMike Smith static int	aac_bio_command(struct aac_softc *sc, struct aac_command **cmp);
8235863739SMike Smith static void	aac_bio_complete(struct aac_command *cm);
83d8a0a473SScott Long static int	aac_wait_command(struct aac_command *cm);
8470545d1aSScott Long static void	aac_command_thread(struct aac_softc *sc);
8535863739SMike Smith 
8635863739SMike Smith /* Command Buffer Management */
87cd481291SScott Long static void	aac_map_command_sg(void *arg, bus_dma_segment_t *segs,
88cd481291SScott Long 				   int nseg, int error);
89c6eafcf2SScott Long static void	aac_map_command_helper(void *arg, bus_dma_segment_t *segs,
90c6eafcf2SScott Long 				       int nseg, int error);
910b94a66eSMike Smith static int	aac_alloc_commands(struct aac_softc *sc);
928480cc63SScott Long static void	aac_free_commands(struct aac_softc *sc);
9335863739SMike Smith static void	aac_unmap_command(struct aac_command *cm);
9435863739SMike Smith 
9535863739SMike Smith /* Hardware Interface */
9604f4d586SEd Maste static int	aac_alloc(struct aac_softc *sc);
97c6eafcf2SScott Long static void	aac_common_map(void *arg, bus_dma_segment_t *segs, int nseg,
98c6eafcf2SScott Long 			       int error);
99fe94b852SScott Long static int	aac_check_firmware(struct aac_softc *sc);
10035863739SMike Smith static int	aac_init(struct aac_softc *sc);
10135863739SMike Smith static int	aac_sync_command(struct aac_softc *sc, u_int32_t command,
102c6eafcf2SScott Long 				 u_int32_t arg0, u_int32_t arg1, u_int32_t arg2,
103c6eafcf2SScott Long 				 u_int32_t arg3, u_int32_t *sp);
10404f4d586SEd Maste static int	aac_setup_intr(struct aac_softc *sc);
105c6eafcf2SScott Long static int	aac_enqueue_fib(struct aac_softc *sc, int queue,
106f6c4dd3fSScott Long 				struct aac_command *cm);
107c6eafcf2SScott Long static int	aac_dequeue_fib(struct aac_softc *sc, int queue,
108914da7d0SScott Long 				u_int32_t *fib_size, struct aac_fib **fib_addr);
10936e0bf6eSScott Long static int	aac_enqueue_response(struct aac_softc *sc, int queue,
11036e0bf6eSScott Long 				     struct aac_fib *fib);
11135863739SMike Smith 
11235863739SMike Smith /* StrongARM interface */
11335863739SMike Smith static int	aac_sa_get_fwstatus(struct aac_softc *sc);
11435863739SMike Smith static void	aac_sa_qnotify(struct aac_softc *sc, int qbit);
11535863739SMike Smith static int	aac_sa_get_istatus(struct aac_softc *sc);
11635863739SMike Smith static void	aac_sa_clear_istatus(struct aac_softc *sc, int mask);
11735863739SMike Smith static void	aac_sa_set_mailbox(struct aac_softc *sc, u_int32_t command,
118c6eafcf2SScott Long 				   u_int32_t arg0, u_int32_t arg1,
119c6eafcf2SScott Long 				   u_int32_t arg2, u_int32_t arg3);
120a6d35632SScott Long static int	aac_sa_get_mailbox(struct aac_softc *sc, int mb);
12135863739SMike Smith static void	aac_sa_set_interrupts(struct aac_softc *sc, int enable);
12235863739SMike Smith 
123da4882c2SMarius Strobl const struct aac_interface aac_sa_interface = {
12435863739SMike Smith 	aac_sa_get_fwstatus,
12535863739SMike Smith 	aac_sa_qnotify,
12635863739SMike Smith 	aac_sa_get_istatus,
12735863739SMike Smith 	aac_sa_clear_istatus,
12835863739SMike Smith 	aac_sa_set_mailbox,
129a6d35632SScott Long 	aac_sa_get_mailbox,
1307cb209f5SScott Long 	aac_sa_set_interrupts,
1317cb209f5SScott Long 	NULL, NULL, NULL
13235863739SMike Smith };
13335863739SMike Smith 
13435863739SMike Smith /* i960Rx interface */
13535863739SMike Smith static int	aac_rx_get_fwstatus(struct aac_softc *sc);
13635863739SMike Smith static void	aac_rx_qnotify(struct aac_softc *sc, int qbit);
13735863739SMike Smith static int	aac_rx_get_istatus(struct aac_softc *sc);
13835863739SMike Smith static void	aac_rx_clear_istatus(struct aac_softc *sc, int mask);
13935863739SMike Smith static void	aac_rx_set_mailbox(struct aac_softc *sc, u_int32_t command,
140c6eafcf2SScott Long 				   u_int32_t arg0, u_int32_t arg1,
141c6eafcf2SScott Long 				   u_int32_t arg2, u_int32_t arg3);
142a6d35632SScott Long static int	aac_rx_get_mailbox(struct aac_softc *sc, int mb);
14335863739SMike Smith static void	aac_rx_set_interrupts(struct aac_softc *sc, int enable);
1447cb209f5SScott Long static int aac_rx_send_command(struct aac_softc *sc, struct aac_command *cm);
1457cb209f5SScott Long static int aac_rx_get_outb_queue(struct aac_softc *sc);
1467cb209f5SScott Long static void aac_rx_set_outb_queue(struct aac_softc *sc, int index);
14735863739SMike Smith 
148da4882c2SMarius Strobl const struct aac_interface aac_rx_interface = {
14935863739SMike Smith 	aac_rx_get_fwstatus,
15035863739SMike Smith 	aac_rx_qnotify,
15135863739SMike Smith 	aac_rx_get_istatus,
15235863739SMike Smith 	aac_rx_clear_istatus,
15335863739SMike Smith 	aac_rx_set_mailbox,
154a6d35632SScott Long 	aac_rx_get_mailbox,
1557cb209f5SScott Long 	aac_rx_set_interrupts,
1567cb209f5SScott Long 	aac_rx_send_command,
1577cb209f5SScott Long 	aac_rx_get_outb_queue,
1587cb209f5SScott Long 	aac_rx_set_outb_queue
15935863739SMike Smith };
16035863739SMike Smith 
1614afedc31SScott Long /* Rocket/MIPS interface */
1624afedc31SScott Long static int	aac_rkt_get_fwstatus(struct aac_softc *sc);
1634afedc31SScott Long static void	aac_rkt_qnotify(struct aac_softc *sc, int qbit);
1644afedc31SScott Long static int	aac_rkt_get_istatus(struct aac_softc *sc);
1654afedc31SScott Long static void	aac_rkt_clear_istatus(struct aac_softc *sc, int mask);
1664afedc31SScott Long static void	aac_rkt_set_mailbox(struct aac_softc *sc, u_int32_t command,
1674afedc31SScott Long 				    u_int32_t arg0, u_int32_t arg1,
1684afedc31SScott Long 				    u_int32_t arg2, u_int32_t arg3);
1694afedc31SScott Long static int	aac_rkt_get_mailbox(struct aac_softc *sc, int mb);
1704afedc31SScott Long static void	aac_rkt_set_interrupts(struct aac_softc *sc, int enable);
1717cb209f5SScott Long static int aac_rkt_send_command(struct aac_softc *sc, struct aac_command *cm);
1727cb209f5SScott Long static int aac_rkt_get_outb_queue(struct aac_softc *sc);
1737cb209f5SScott Long static void aac_rkt_set_outb_queue(struct aac_softc *sc, int index);
1744afedc31SScott Long 
175da4882c2SMarius Strobl const struct aac_interface aac_rkt_interface = {
1764afedc31SScott Long 	aac_rkt_get_fwstatus,
1774afedc31SScott Long 	aac_rkt_qnotify,
1784afedc31SScott Long 	aac_rkt_get_istatus,
1794afedc31SScott Long 	aac_rkt_clear_istatus,
1804afedc31SScott Long 	aac_rkt_set_mailbox,
1814afedc31SScott Long 	aac_rkt_get_mailbox,
1827cb209f5SScott Long 	aac_rkt_set_interrupts,
1837cb209f5SScott Long 	aac_rkt_send_command,
1847cb209f5SScott Long 	aac_rkt_get_outb_queue,
1857cb209f5SScott Long 	aac_rkt_set_outb_queue
1864afedc31SScott Long };
1874afedc31SScott Long 
18835863739SMike Smith /* Debugging and Diagnostics */
18935863739SMike Smith static void		aac_describe_controller(struct aac_softc *sc);
190da4882c2SMarius Strobl static const char	*aac_describe_code(const struct aac_code_lookup *table,
191c6eafcf2SScott Long 				   u_int32_t code);
19235863739SMike Smith 
19335863739SMike Smith /* Management Interface */
19435863739SMike Smith static d_open_t		aac_open;
19535863739SMike Smith static d_ioctl_t	aac_ioctl;
196b3457b51SScott Long static d_poll_t		aac_poll;
197dfe2c294SAttilio Rao static void		aac_cdevpriv_dtor(void *arg);
198c6eafcf2SScott Long static int		aac_ioctl_sendfib(struct aac_softc *sc, caddr_t ufib);
199f355c0e0SEd Maste static int		aac_ioctl_send_raw_srb(struct aac_softc *sc, caddr_t arg);
200c6eafcf2SScott Long static void		aac_handle_aif(struct aac_softc *sc,
20136e0bf6eSScott Long 					   struct aac_fib *fib);
202fb0c27d7SScott Long static int		aac_rev_check(struct aac_softc *sc, caddr_t udata);
203a723a548SEd Maste static int		aac_open_aif(struct aac_softc *sc, caddr_t arg);
204a723a548SEd Maste static int		aac_close_aif(struct aac_softc *sc, caddr_t arg);
205fb0c27d7SScott Long static int		aac_getnext_aif(struct aac_softc *sc, caddr_t arg);
206a723a548SEd Maste static int		aac_return_aif(struct aac_softc *sc,
207a723a548SEd Maste 					struct aac_fib_context *ctx, caddr_t uptr);
20836e0bf6eSScott Long static int		aac_query_disk(struct aac_softc *sc, caddr_t uptr);
2097cb209f5SScott Long static int		aac_get_pci_info(struct aac_softc *sc, caddr_t uptr);
2106d307336SEd Maste static int		aac_supported_features(struct aac_softc *sc, caddr_t uptr);
2117cb209f5SScott Long static void		aac_ioctl_event(struct aac_softc *sc,
2127cb209f5SScott Long 					struct aac_event *event, void *arg);
21304f4d586SEd Maste static struct aac_mntinforesp *
21404f4d586SEd Maste 	aac_get_container_info(struct aac_softc *sc, struct aac_fib *fib, int cid);
21535863739SMike Smith 
21635863739SMike Smith static struct cdevsw aac_cdevsw = {
217dc08ffecSPoul-Henning Kamp 	.d_version =	D_VERSION,
218dfdbb320SWarner Losh 	.d_flags =	0,
2197ac40f5fSPoul-Henning Kamp 	.d_open =	aac_open,
2207ac40f5fSPoul-Henning Kamp 	.d_ioctl =	aac_ioctl,
2217ac40f5fSPoul-Henning Kamp 	.d_poll =	aac_poll,
2227ac40f5fSPoul-Henning Kamp 	.d_name =	"aac",
22335863739SMike Smith };
22435863739SMike Smith 
225d745c852SEd Schouten static MALLOC_DEFINE(M_AACBUF, "aacbuf", "Buffers for the AAC driver");
22636e0bf6eSScott Long 
2273d04a9d7SScott Long /* sysctl node */
228*7029da5cSPawel Biernacki SYSCTL_NODE(_hw, OID_AUTO, aac, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
229*7029da5cSPawel Biernacki     "AAC driver parameters");
2303d04a9d7SScott Long 
231914da7d0SScott Long /*
232914da7d0SScott Long  * Device Interface
233914da7d0SScott Long  */
23435863739SMike Smith 
235914da7d0SScott Long /*
2364109ba51SEd Maste  * Initialize the controller and softc
23735863739SMike Smith  */
23835863739SMike Smith int
23935863739SMike Smith aac_attach(struct aac_softc *sc)
24035863739SMike Smith {
24135863739SMike Smith 	int error, unit;
24235863739SMike Smith 
24331a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
24435863739SMike Smith 
24535863739SMike Smith 	/*
2464109ba51SEd Maste 	 * Initialize per-controller queues.
24735863739SMike Smith 	 */
2480b94a66eSMike Smith 	aac_initq_free(sc);
2490b94a66eSMike Smith 	aac_initq_ready(sc);
2500b94a66eSMike Smith 	aac_initq_busy(sc);
2510b94a66eSMike Smith 	aac_initq_bio(sc);
25235863739SMike Smith 
25335863739SMike Smith 	/*
2544109ba51SEd Maste 	 * Initialize command-completion task.
25535863739SMike Smith 	 */
25635863739SMike Smith 	TASK_INIT(&sc->aac_task_complete, 0, aac_complete, sc);
25735863739SMike Smith 
25835863739SMike Smith 	/* mark controller as suspended until we get ourselves organised */
25935863739SMike Smith 	sc->aac_state |= AAC_STATE_SUSPEND;
26035863739SMike Smith 
26135863739SMike Smith 	/*
262fe94b852SScott Long 	 * Check that the firmware on the card is supported.
263fe94b852SScott Long 	 */
264fe94b852SScott Long 	if ((error = aac_check_firmware(sc)) != 0)
265fe94b852SScott Long 		return(error);
266fe94b852SScott Long 
267f6b1c44dSScott Long 	/*
268f6b1c44dSScott Long 	 * Initialize locks
269f6b1c44dSScott Long 	 */
270bb6fe253SScott Long 	mtx_init(&sc->aac_aifq_lock, "AAC AIF lock", NULL, MTX_DEF);
271bb6fe253SScott Long 	mtx_init(&sc->aac_io_lock, "AAC I/O lock", NULL, MTX_DEF);
272bb6fe253SScott Long 	mtx_init(&sc->aac_container_lock, "AAC container lock", NULL, MTX_DEF);
273f6b1c44dSScott Long 	TAILQ_INIT(&sc->aac_container_tqh);
274065dd78cSScott Long 	TAILQ_INIT(&sc->aac_ev_cmfree);
275f6b1c44dSScott Long 
276ff0991c4SAttilio Rao 	/* Initialize the clock daemon callout. */
277ff0991c4SAttilio Rao 	callout_init_mtx(&sc->aac_daemontime, &sc->aac_io_lock, 0);
278ff0991c4SAttilio Rao 
2790b94a66eSMike Smith 	/*
2804109ba51SEd Maste 	 * Initialize the adapter.
28135863739SMike Smith 	 */
28204f4d586SEd Maste 	if ((error = aac_alloc(sc)) != 0)
28304f4d586SEd Maste 		return(error);
2840b94a66eSMike Smith 	if ((error = aac_init(sc)) != 0)
28535863739SMike Smith 		return(error);
28635863739SMike Smith 
28735863739SMike Smith 	/*
2887cb209f5SScott Long 	 * Allocate and connect our interrupt.
2897cb209f5SScott Long 	 */
29004f4d586SEd Maste 	if ((error = aac_setup_intr(sc)) != 0)
29104f4d586SEd Maste 		return(error);
2927cb209f5SScott Long 
2937cb209f5SScott Long 	/*
29435863739SMike Smith 	 * Print a little information about the controller.
29535863739SMike Smith 	 */
29635863739SMike Smith 	aac_describe_controller(sc);
29735863739SMike Smith 
29835863739SMike Smith 	/*
2991423dcd6SEd Maste 	 * Add sysctls.
3001423dcd6SEd Maste 	 */
3011423dcd6SEd Maste 	SYSCTL_ADD_INT(device_get_sysctl_ctx(sc->aac_dev),
3021423dcd6SEd Maste 	    SYSCTL_CHILDREN(device_get_sysctl_tree(sc->aac_dev)),
3031423dcd6SEd Maste 	    OID_AUTO, "firmware_build", CTLFLAG_RD,
3041423dcd6SEd Maste 	    &sc->aac_revision.buildNumber, 0,
3051423dcd6SEd Maste 	    "firmware build number");
3061423dcd6SEd Maste 
3071423dcd6SEd Maste 	/*
308ae543596SScott Long 	 * Register to probe our containers later.
309ae543596SScott Long 	 */
31035863739SMike Smith 	sc->aac_ich.ich_func = aac_startup;
31135863739SMike Smith 	sc->aac_ich.ich_arg = sc;
31235863739SMike Smith 	if (config_intrhook_establish(&sc->aac_ich) != 0) {
313914da7d0SScott Long 		device_printf(sc->aac_dev,
314914da7d0SScott Long 			      "can't establish configuration hook\n");
31535863739SMike Smith 		return(ENXIO);
31635863739SMike Smith 	}
31735863739SMike Smith 
31835863739SMike Smith 	/*
31935863739SMike Smith 	 * Make the control device.
32035863739SMike Smith 	 */
32135863739SMike Smith 	unit = device_get_unit(sc->aac_dev);
3229e9466baSRobert Watson 	sc->aac_dev_t = make_dev(&aac_cdevsw, unit, UID_ROOT, GID_OPERATOR,
3239e9466baSRobert Watson 				 0640, "aac%d", unit);
324157fbb2eSScott Long 	(void)make_dev_alias(sc->aac_dev_t, "afa%d", unit);
3254aa620cdSScott Long 	(void)make_dev_alias(sc->aac_dev_t, "hpn%d", unit);
32635863739SMike Smith 	sc->aac_dev_t->si_drv1 = sc;
32735863739SMike Smith 
32836e0bf6eSScott Long 	/* Create the AIF thread */
3293745c395SJulian Elischer 	if (kproc_create((void(*)(void *))aac_command_thread, sc,
330316ec49aSScott Long 		   &sc->aifthread, 0, 0, "aac%daif", unit))
331a620bad0SEd Maste 		panic("Could not create AIF thread");
33236e0bf6eSScott Long 
33336e0bf6eSScott Long 	/* Register the shutdown method to only be called post-dump */
3345f54d522SScott Long 	if ((sc->eh = EVENTHANDLER_REGISTER(shutdown_final, aac_shutdown,
3355f54d522SScott Long 	    sc->aac_dev, SHUTDOWN_PRI_DEFAULT)) == NULL)
3365f54d522SScott Long 		device_printf(sc->aac_dev,
3375f54d522SScott Long 			      "shutdown event registration failed\n");
33836e0bf6eSScott Long 
339fe3cb0e1SScott Long 	/* Register with CAM for the non-DASD devices */
340a6d35632SScott Long 	if ((sc->flags & AAC_FLAGS_ENABLE_CAM) != 0) {
34170545d1aSScott Long 		TAILQ_INIT(&sc->aac_sim_tqh);
342fe3cb0e1SScott Long 		aac_get_bus_info(sc);
34370545d1aSScott Long 	}
344fe3cb0e1SScott Long 
345ff0991c4SAttilio Rao 	mtx_lock(&sc->aac_io_lock);
346867b1d34SEd Maste 	callout_reset(&sc->aac_daemontime, 60 * hz, aac_daemon, sc);
347ff0991c4SAttilio Rao 	mtx_unlock(&sc->aac_io_lock);
348ff0991c4SAttilio Rao 
34935863739SMike Smith 	return(0);
35035863739SMike Smith }
35135863739SMike Smith 
352ff0991c4SAttilio Rao static void
353ff0991c4SAttilio Rao aac_daemon(void *arg)
354ff0991c4SAttilio Rao {
355ff0991c4SAttilio Rao 	struct timeval tv;
356ff0991c4SAttilio Rao 	struct aac_softc *sc;
357ff0991c4SAttilio Rao 	struct aac_fib *fib;
358ff0991c4SAttilio Rao 
359ff0991c4SAttilio Rao 	sc = arg;
360ff0991c4SAttilio Rao 	mtx_assert(&sc->aac_io_lock, MA_OWNED);
361ff0991c4SAttilio Rao 
362ff0991c4SAttilio Rao 	if (callout_pending(&sc->aac_daemontime) ||
363ff0991c4SAttilio Rao 	    callout_active(&sc->aac_daemontime) == 0)
364ff0991c4SAttilio Rao 		return;
365ff0991c4SAttilio Rao 	getmicrotime(&tv);
366ff0991c4SAttilio Rao 	aac_alloc_sync_fib(sc, &fib);
367ff0991c4SAttilio Rao 	*(uint32_t *)fib->data = tv.tv_sec;
368ff0991c4SAttilio Rao 	aac_sync_fib(sc, SendHostTime, 0, fib, sizeof(uint32_t));
369ff0991c4SAttilio Rao 	aac_release_sync_fib(sc);
370ff0991c4SAttilio Rao 	callout_schedule(&sc->aac_daemontime, 30 * 60 * hz);
371ff0991c4SAttilio Rao }
372ff0991c4SAttilio Rao 
3737cb209f5SScott Long void
3747cb209f5SScott Long aac_add_event(struct aac_softc *sc, struct aac_event *event)
3757cb209f5SScott Long {
3767cb209f5SScott Long 
3777cb209f5SScott Long 	switch (event->ev_type & AAC_EVENT_MASK) {
3787cb209f5SScott Long 	case AAC_EVENT_CMFREE:
3797cb209f5SScott Long 		TAILQ_INSERT_TAIL(&sc->aac_ev_cmfree, event, ev_links);
3807cb209f5SScott Long 		break;
3817cb209f5SScott Long 	default:
3827cb209f5SScott Long 		device_printf(sc->aac_dev, "aac_add event: unknown event %d\n",
3837cb209f5SScott Long 		    event->ev_type);
3847cb209f5SScott Long 		break;
3857cb209f5SScott Long 	}
3867cb209f5SScott Long }
3877cb209f5SScott Long 
388914da7d0SScott Long /*
38904f4d586SEd Maste  * Request information of container #cid
39004f4d586SEd Maste  */
39104f4d586SEd Maste static struct aac_mntinforesp *
39204f4d586SEd Maste aac_get_container_info(struct aac_softc *sc, struct aac_fib *fib, int cid)
39304f4d586SEd Maste {
39404f4d586SEd Maste 	struct aac_mntinfo *mi;
39504f4d586SEd Maste 
39604f4d586SEd Maste 	mi = (struct aac_mntinfo *)&fib->data[0];
397523da39bSEd Maste 	/* use 64-bit LBA if enabled */
398523da39bSEd Maste 	mi->Command = (sc->flags & AAC_FLAGS_LBA_64BIT) ?
399523da39bSEd Maste 	    VM_NameServe64 : VM_NameServe;
40004f4d586SEd Maste 	mi->MntType = FT_FILESYS;
40104f4d586SEd Maste 	mi->MntCount = cid;
40204f4d586SEd Maste 
40304f4d586SEd Maste 	if (aac_sync_fib(sc, ContainerCommand, 0, fib,
40404f4d586SEd Maste 			 sizeof(struct aac_mntinfo))) {
405dbb34a64SEd Maste 		device_printf(sc->aac_dev, "Error probing container %d\n", cid);
40604f4d586SEd Maste 		return (NULL);
40704f4d586SEd Maste 	}
40804f4d586SEd Maste 
40904f4d586SEd Maste 	return ((struct aac_mntinforesp *)&fib->data[0]);
41004f4d586SEd Maste }
41104f4d586SEd Maste 
41204f4d586SEd Maste /*
41335863739SMike Smith  * Probe for containers, create disks.
41435863739SMike Smith  */
41535863739SMike Smith static void
41635863739SMike Smith aac_startup(void *arg)
41735863739SMike Smith {
418914da7d0SScott Long 	struct aac_softc *sc;
419cbfd045bSScott Long 	struct aac_fib *fib;
42004f4d586SEd Maste 	struct aac_mntinforesp *mir;
421795d7dc0SScott Long 	int count = 0, i = 0;
42235863739SMike Smith 
423914da7d0SScott Long 	sc = (struct aac_softc *)arg;
42431a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
425914da7d0SScott Long 
4267cb209f5SScott Long 	mtx_lock(&sc->aac_io_lock);
42703b5fe51SScott Long 	aac_alloc_sync_fib(sc, &fib);
428cbfd045bSScott Long 
42935863739SMike Smith 	/* loop over possible containers */
43036e0bf6eSScott Long 	do {
43104f4d586SEd Maste 		if ((mir = aac_get_container_info(sc, fib, i)) == NULL)
43235863739SMike Smith 			continue;
43304f4d586SEd Maste 		if (i == 0)
434795d7dc0SScott Long 			count = mir->MntRespCount;
435cbfd045bSScott Long 		aac_add_container(sc, mir, 0);
43636e0bf6eSScott Long 		i++;
437795d7dc0SScott Long 	} while ((i < count) && (i < AAC_MAX_CONTAINERS));
438cbfd045bSScott Long 
439cbfd045bSScott Long 	aac_release_sync_fib(sc);
4407cb209f5SScott Long 	mtx_unlock(&sc->aac_io_lock);
44135863739SMike Smith 
442cc336c78SScott Long 	/* mark the controller up */
443cc336c78SScott Long 	sc->aac_state &= ~AAC_STATE_SUSPEND;
444cc336c78SScott Long 
44535863739SMike Smith 	/* poke the bus to actually attach the child devices */
44635863739SMike Smith 	if (bus_generic_attach(sc->aac_dev))
44735863739SMike Smith 		device_printf(sc->aac_dev, "bus_generic_attach failed\n");
44835863739SMike Smith 
449cc336c78SScott Long 	/* disconnect ourselves from the intrhook chain */
450cc336c78SScott Long 	config_intrhook_disestablish(&sc->aac_ich);
45135863739SMike Smith 
45235863739SMike Smith 	/* enable interrupts now */
45335863739SMike Smith 	AAC_UNMASK_INTERRUPTS(sc);
45435863739SMike Smith }
45535863739SMike Smith 
456914da7d0SScott Long /*
4574109ba51SEd Maste  * Create a device to represent a new container
458914da7d0SScott Long  */
459914da7d0SScott Long static void
460cbfd045bSScott Long aac_add_container(struct aac_softc *sc, struct aac_mntinforesp *mir, int f)
461914da7d0SScott Long {
462914da7d0SScott Long 	struct aac_container *co;
463914da7d0SScott Long 	device_t child;
464914da7d0SScott Long 
465914da7d0SScott Long 	/*
466914da7d0SScott Long 	 * Check container volume type for validity.  Note that many of
467914da7d0SScott Long 	 * the possible types may never show up.
468914da7d0SScott Long 	 */
469914da7d0SScott Long 	if ((mir->Status == ST_OK) && (mir->MntTable[0].VolType != CT_NONE)) {
470a761a1caSScott Long 		co = (struct aac_container *)malloc(sizeof *co, M_AACBUF,
471a761a1caSScott Long 		       M_NOWAIT | M_ZERO);
472914da7d0SScott Long 		if (co == NULL)
473a620bad0SEd Maste 			panic("Out of memory?!");
47431a0399eSEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_INIT_B, "id %x  name '%.16s'  size %u  type %d",
475914da7d0SScott Long 		      mir->MntTable[0].ObjectId,
476914da7d0SScott Long 		      mir->MntTable[0].FileSystemName,
477914da7d0SScott Long 		      mir->MntTable[0].Capacity, mir->MntTable[0].VolType);
478914da7d0SScott Long 
479fe3cb0e1SScott Long 		if ((child = device_add_child(sc->aac_dev, "aacd", -1)) == NULL)
480914da7d0SScott Long 			device_printf(sc->aac_dev, "device_add_child failed\n");
481914da7d0SScott Long 		else
482914da7d0SScott Long 			device_set_ivars(child, co);
483914da7d0SScott Long 		device_set_desc(child, aac_describe_code(aac_container_types,
484914da7d0SScott Long 				mir->MntTable[0].VolType));
485914da7d0SScott Long 		co->co_disk = child;
486914da7d0SScott Long 		co->co_found = f;
487914da7d0SScott Long 		bcopy(&mir->MntTable[0], &co->co_mntobj,
488914da7d0SScott Long 		      sizeof(struct aac_mntobj));
489bb6fe253SScott Long 		mtx_lock(&sc->aac_container_lock);
490914da7d0SScott Long 		TAILQ_INSERT_TAIL(&sc->aac_container_tqh, co, co_link);
491bb6fe253SScott Long 		mtx_unlock(&sc->aac_container_lock);
492914da7d0SScott Long 	}
493914da7d0SScott Long }
494914da7d0SScott Long 
495914da7d0SScott Long /*
49604f4d586SEd Maste  * Allocate resources associated with (sc)
49704f4d586SEd Maste  */
49804f4d586SEd Maste static int
49904f4d586SEd Maste aac_alloc(struct aac_softc *sc)
50004f4d586SEd Maste {
50131a0399eSEd Maste 
50231a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
50331a0399eSEd Maste 
50404f4d586SEd Maste 	/*
50504f4d586SEd Maste 	 * Create DMA tag for mapping buffers into controller-addressable space.
50604f4d586SEd Maste 	 */
50704f4d586SEd Maste 	if (bus_dma_tag_create(sc->aac_parent_dmat, 	/* parent */
50804f4d586SEd Maste 			       1, 0, 			/* algnmnt, boundary */
50904f4d586SEd Maste 			       (sc->flags & AAC_FLAGS_SG_64BIT) ?
51004f4d586SEd Maste 			       BUS_SPACE_MAXADDR :
51104f4d586SEd Maste 			       BUS_SPACE_MAXADDR_32BIT,	/* lowaddr */
51204f4d586SEd Maste 			       BUS_SPACE_MAXADDR, 	/* highaddr */
51304f4d586SEd Maste 			       NULL, NULL, 		/* filter, filterarg */
5146f954fb3SAlexander Motin 			       sc->aac_max_sectors << 9, /* maxsize */
51504f4d586SEd Maste 			       sc->aac_sg_tablesize,	/* nsegments */
5166f954fb3SAlexander Motin 			       BUS_SPACE_MAXSIZE_32BIT,	/* maxsegsize */
51704f4d586SEd Maste 			       BUS_DMA_ALLOCNOW,	/* flags */
51804f4d586SEd Maste 			       busdma_lock_mutex,	/* lockfunc */
51904f4d586SEd Maste 			       &sc->aac_io_lock,	/* lockfuncarg */
52004f4d586SEd Maste 			       &sc->aac_buffer_dmat)) {
52104f4d586SEd Maste 		device_printf(sc->aac_dev, "can't allocate buffer DMA tag\n");
52204f4d586SEd Maste 		return (ENOMEM);
52304f4d586SEd Maste 	}
52404f4d586SEd Maste 
52504f4d586SEd Maste 	/*
52604f4d586SEd Maste 	 * Create DMA tag for mapping FIBs into controller-addressable space..
52704f4d586SEd Maste 	 */
52804f4d586SEd Maste 	if (bus_dma_tag_create(sc->aac_parent_dmat,	/* parent */
52904f4d586SEd Maste 			       1, 0, 			/* algnmnt, boundary */
53004f4d586SEd Maste 			       (sc->flags & AAC_FLAGS_4GB_WINDOW) ?
53104f4d586SEd Maste 			       BUS_SPACE_MAXADDR_32BIT :
53204f4d586SEd Maste 			       0x7fffffff,		/* lowaddr */
53304f4d586SEd Maste 			       BUS_SPACE_MAXADDR, 	/* highaddr */
53404f4d586SEd Maste 			       NULL, NULL, 		/* filter, filterarg */
53504f4d586SEd Maste 			       sc->aac_max_fibs_alloc *
53604f4d586SEd Maste 			       sc->aac_max_fib_size,  /* maxsize */
53704f4d586SEd Maste 			       1,			/* nsegments */
53804f4d586SEd Maste 			       sc->aac_max_fibs_alloc *
53904f4d586SEd Maste 			       sc->aac_max_fib_size,	/* maxsize */
54004f4d586SEd Maste 			       0,			/* flags */
54104f4d586SEd Maste 			       NULL, NULL,		/* No locking needed */
54204f4d586SEd Maste 			       &sc->aac_fib_dmat)) {
543c2ede4b3SMartin Blapp 		device_printf(sc->aac_dev, "can't allocate FIB DMA tag\n");
54404f4d586SEd Maste 		return (ENOMEM);
54504f4d586SEd Maste 	}
54604f4d586SEd Maste 
54704f4d586SEd Maste 	/*
54804f4d586SEd Maste 	 * Create DMA tag for the common structure and allocate it.
54904f4d586SEd Maste 	 */
55004f4d586SEd Maste 	if (bus_dma_tag_create(sc->aac_parent_dmat, 	/* parent */
55104f4d586SEd Maste 			       1, 0,			/* algnmnt, boundary */
55204f4d586SEd Maste 			       (sc->flags & AAC_FLAGS_4GB_WINDOW) ?
55304f4d586SEd Maste 			       BUS_SPACE_MAXADDR_32BIT :
55404f4d586SEd Maste 			       0x7fffffff,		/* lowaddr */
55504f4d586SEd Maste 			       BUS_SPACE_MAXADDR, 	/* highaddr */
55604f4d586SEd Maste 			       NULL, NULL, 		/* filter, filterarg */
55704f4d586SEd Maste 			       8192 + sizeof(struct aac_common), /* maxsize */
55804f4d586SEd Maste 			       1,			/* nsegments */
55904f4d586SEd Maste 			       BUS_SPACE_MAXSIZE_32BIT,	/* maxsegsize */
56004f4d586SEd Maste 			       0,			/* flags */
56104f4d586SEd Maste 			       NULL, NULL,		/* No locking needed */
56204f4d586SEd Maste 			       &sc->aac_common_dmat)) {
56304f4d586SEd Maste 		device_printf(sc->aac_dev,
56404f4d586SEd Maste 			      "can't allocate common structure DMA tag\n");
56504f4d586SEd Maste 		return (ENOMEM);
56604f4d586SEd Maste 	}
56704f4d586SEd Maste 	if (bus_dmamem_alloc(sc->aac_common_dmat, (void **)&sc->aac_common,
56804f4d586SEd Maste 			     BUS_DMA_NOWAIT, &sc->aac_common_dmamap)) {
56904f4d586SEd Maste 		device_printf(sc->aac_dev, "can't allocate common structure\n");
57004f4d586SEd Maste 		return (ENOMEM);
57104f4d586SEd Maste 	}
57204f4d586SEd Maste 
57304f4d586SEd Maste 	/*
57404f4d586SEd Maste 	 * Work around a bug in the 2120 and 2200 that cannot DMA commands
57504f4d586SEd Maste 	 * below address 8192 in physical memory.
57604f4d586SEd Maste 	 * XXX If the padding is not needed, can it be put to use instead
57704f4d586SEd Maste 	 * of ignored?
57804f4d586SEd Maste 	 */
57904f4d586SEd Maste 	(void)bus_dmamap_load(sc->aac_common_dmat, sc->aac_common_dmamap,
58004f4d586SEd Maste 			sc->aac_common, 8192 + sizeof(*sc->aac_common),
58104f4d586SEd Maste 			aac_common_map, sc, 0);
58204f4d586SEd Maste 
58304f4d586SEd Maste 	if (sc->aac_common_busaddr < 8192) {
58404f4d586SEd Maste 		sc->aac_common = (struct aac_common *)
58504f4d586SEd Maste 		    ((uint8_t *)sc->aac_common + 8192);
58604f4d586SEd Maste 		sc->aac_common_busaddr += 8192;
58704f4d586SEd Maste 	}
58804f4d586SEd Maste 	bzero(sc->aac_common, sizeof(*sc->aac_common));
58904f4d586SEd Maste 
59004f4d586SEd Maste 	/* Allocate some FIBs and associated command structs */
59104f4d586SEd Maste 	TAILQ_INIT(&sc->aac_fibmap_tqh);
59204f4d586SEd Maste 	sc->aac_commands = malloc(sc->aac_max_fibs * sizeof(struct aac_command),
59304f4d586SEd Maste 				  M_AACBUF, M_WAITOK|M_ZERO);
59423e876b1SJung-uk Kim 	while (sc->total_fibs < sc->aac_max_fibs) {
59504f4d586SEd Maste 		if (aac_alloc_commands(sc) != 0)
59604f4d586SEd Maste 			break;
59704f4d586SEd Maste 	}
59804f4d586SEd Maste 	if (sc->total_fibs == 0)
59904f4d586SEd Maste 		return (ENOMEM);
60004f4d586SEd Maste 
60104f4d586SEd Maste 	return (0);
60204f4d586SEd Maste }
60304f4d586SEd Maste 
60404f4d586SEd Maste /*
60535863739SMike Smith  * Free all of the resources associated with (sc)
60635863739SMike Smith  *
60735863739SMike Smith  * Should not be called if the controller is active.
60835863739SMike Smith  */
60935863739SMike Smith void
61035863739SMike Smith aac_free(struct aac_softc *sc)
61135863739SMike Smith {
612ffb37f33SScott Long 
61331a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
61435863739SMike Smith 
61535863739SMike Smith 	/* remove the control device */
61635863739SMike Smith 	if (sc->aac_dev_t != NULL)
61735863739SMike Smith 		destroy_dev(sc->aac_dev_t);
61835863739SMike Smith 
6190b94a66eSMike Smith 	/* throw away any FIB buffers, discard the FIB DMA tag */
6208480cc63SScott Long 	aac_free_commands(sc);
6210b94a66eSMike Smith 	if (sc->aac_fib_dmat)
6220b94a66eSMike Smith 		bus_dma_tag_destroy(sc->aac_fib_dmat);
62335863739SMike Smith 
624ffb37f33SScott Long 	free(sc->aac_commands, M_AACBUF);
625ffb37f33SScott Long 
62635863739SMike Smith 	/* destroy the common area */
62735863739SMike Smith 	if (sc->aac_common) {
62835863739SMike Smith 		bus_dmamap_unload(sc->aac_common_dmat, sc->aac_common_dmamap);
629c6eafcf2SScott Long 		bus_dmamem_free(sc->aac_common_dmat, sc->aac_common,
630c6eafcf2SScott Long 				sc->aac_common_dmamap);
63135863739SMike Smith 	}
6320b94a66eSMike Smith 	if (sc->aac_common_dmat)
6330b94a66eSMike Smith 		bus_dma_tag_destroy(sc->aac_common_dmat);
63435863739SMike Smith 
63535863739SMike Smith 	/* disconnect the interrupt handler */
63635863739SMike Smith 	if (sc->aac_intr)
63735863739SMike Smith 		bus_teardown_intr(sc->aac_dev, sc->aac_irq, sc->aac_intr);
638bbc03f1bSMarius Strobl 	if (sc->aac_irq != NULL) {
639da4882c2SMarius Strobl 		bus_release_resource(sc->aac_dev, SYS_RES_IRQ,
640da4882c2SMarius Strobl 		    rman_get_rid(sc->aac_irq), sc->aac_irq);
641bbc03f1bSMarius Strobl 		pci_release_msi(sc->aac_dev);
642bbc03f1bSMarius Strobl 	}
64335863739SMike Smith 
64435863739SMike Smith 	/* destroy data-transfer DMA tag */
64535863739SMike Smith 	if (sc->aac_buffer_dmat)
64635863739SMike Smith 		bus_dma_tag_destroy(sc->aac_buffer_dmat);
64735863739SMike Smith 
64835863739SMike Smith 	/* destroy the parent DMA tag */
64935863739SMike Smith 	if (sc->aac_parent_dmat)
65035863739SMike Smith 		bus_dma_tag_destroy(sc->aac_parent_dmat);
65135863739SMike Smith 
65235863739SMike Smith 	/* release the register window mapping */
653ff0991c4SAttilio Rao 	if (sc->aac_regs_res0 != NULL)
654914da7d0SScott Long 		bus_release_resource(sc->aac_dev, SYS_RES_MEMORY,
655da4882c2SMarius Strobl 		    rman_get_rid(sc->aac_regs_res0), sc->aac_regs_res0);
656ff0991c4SAttilio Rao 	if (sc->aac_hwif == AAC_HWIF_NARK && sc->aac_regs_res1 != NULL)
657ff0991c4SAttilio Rao 		bus_release_resource(sc->aac_dev, SYS_RES_MEMORY,
658da4882c2SMarius Strobl 		    rman_get_rid(sc->aac_regs_res1), sc->aac_regs_res1);
65935863739SMike Smith }
66035863739SMike Smith 
661914da7d0SScott Long /*
66235863739SMike Smith  * Disconnect from the controller completely, in preparation for unload.
66335863739SMike Smith  */
66435863739SMike Smith int
66535863739SMike Smith aac_detach(device_t dev)
66635863739SMike Smith {
667914da7d0SScott Long 	struct aac_softc *sc;
66870545d1aSScott Long 	struct aac_container *co;
66970545d1aSScott Long 	struct aac_sim	*sim;
67035863739SMike Smith 	int error;
67135863739SMike Smith 
672914da7d0SScott Long 	sc = device_get_softc(dev);
67331a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
674914da7d0SScott Long 
675ff0991c4SAttilio Rao 	callout_drain(&sc->aac_daemontime);
676ff0991c4SAttilio Rao 
6771bd320ecSAttilio Rao 	mtx_lock(&sc->aac_io_lock);
6781bd320ecSAttilio Rao 	while (sc->aifflags & AAC_AIFFLAGS_RUNNING) {
6791bd320ecSAttilio Rao 		sc->aifflags |= AAC_AIFFLAGS_EXIT;
6801bd320ecSAttilio Rao 		wakeup(sc->aifthread);
6811bd320ecSAttilio Rao 		msleep(sc->aac_dev, &sc->aac_io_lock, PUSER, "aacdch", 0);
6821bd320ecSAttilio Rao 	}
6831bd320ecSAttilio Rao 	mtx_unlock(&sc->aac_io_lock);
6841bd320ecSAttilio Rao 	KASSERT((sc->aifflags & AAC_AIFFLAGS_RUNNING) == 0,
6851bd320ecSAttilio Rao 	    ("%s: invalid detach state", __func__));
6861bd320ecSAttilio Rao 
68770545d1aSScott Long 	/* Remove the child containers */
688a761a1caSScott Long 	while ((co = TAILQ_FIRST(&sc->aac_container_tqh)) != NULL) {
68970545d1aSScott Long 		error = device_delete_child(dev, co->co_disk);
69070545d1aSScott Long 		if (error)
69170545d1aSScott Long 			return (error);
69265ac4ed6SScott Long 		TAILQ_REMOVE(&sc->aac_container_tqh, co, co_link);
693a761a1caSScott Long 		free(co, M_AACBUF);
69470545d1aSScott Long 	}
69570545d1aSScott Long 
69670545d1aSScott Long 	/* Remove the CAM SIMs */
697a761a1caSScott Long 	while ((sim = TAILQ_FIRST(&sc->aac_sim_tqh)) != NULL) {
698a761a1caSScott Long 		TAILQ_REMOVE(&sc->aac_sim_tqh, sim, sim_link);
69970545d1aSScott Long 		error = device_delete_child(dev, sim->sim_dev);
70070545d1aSScott Long 		if (error)
70170545d1aSScott Long 			return (error);
702a761a1caSScott Long 		free(sim, M_AACBUF);
70370545d1aSScott Long 	}
70470545d1aSScott Long 
70535863739SMike Smith 	if ((error = aac_shutdown(dev)))
70635863739SMike Smith 		return(error);
70735863739SMike Smith 
7085f54d522SScott Long 	EVENTHANDLER_DEREGISTER(shutdown_final, sc->eh);
7095f54d522SScott Long 
71035863739SMike Smith 	aac_free(sc);
71135863739SMike Smith 
712dc9efde5SScott Long 	mtx_destroy(&sc->aac_aifq_lock);
713dc9efde5SScott Long 	mtx_destroy(&sc->aac_io_lock);
714dc9efde5SScott Long 	mtx_destroy(&sc->aac_container_lock);
715dc9efde5SScott Long 
71635863739SMike Smith 	return(0);
71735863739SMike Smith }
71835863739SMike Smith 
719914da7d0SScott Long /*
72035863739SMike Smith  * Bring the controller down to a dormant state and detach all child devices.
72135863739SMike Smith  *
72235863739SMike Smith  * This function is called before detach or system shutdown.
72335863739SMike Smith  *
7240b94a66eSMike Smith  * Note that we can assume that the bioq on the controller is empty, as we won't
72535863739SMike Smith  * allow shutdown if any device is open.
72635863739SMike Smith  */
72735863739SMike Smith int
72835863739SMike Smith aac_shutdown(device_t dev)
72935863739SMike Smith {
730914da7d0SScott Long 	struct aac_softc *sc;
731cbfd045bSScott Long 	struct aac_fib *fib;
732cbfd045bSScott Long 	struct aac_close_command *cc;
73335863739SMike Smith 
734914da7d0SScott Long 	sc = device_get_softc(dev);
73531a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
736914da7d0SScott Long 
73735863739SMike Smith 	sc->aac_state |= AAC_STATE_SUSPEND;
73835863739SMike Smith 
73935863739SMike Smith 	/*
74035863739SMike Smith 	 * Send a Container shutdown followed by a HostShutdown FIB to the
74135863739SMike Smith 	 * controller to convince it that we don't want to talk to it anymore.
74235863739SMike Smith 	 * We've been closed and all I/O completed already
74335863739SMike Smith 	 */
74435863739SMike Smith 	device_printf(sc->aac_dev, "shutting down controller...");
74535863739SMike Smith 
7467cb209f5SScott Long 	mtx_lock(&sc->aac_io_lock);
74703b5fe51SScott Long 	aac_alloc_sync_fib(sc, &fib);
748cbfd045bSScott Long 	cc = (struct aac_close_command *)&fib->data[0];
749cbfd045bSScott Long 
75039ee03c3SScott Long 	bzero(cc, sizeof(struct aac_close_command));
751cbfd045bSScott Long 	cc->Command = VM_CloseAll;
752cbfd045bSScott Long 	cc->ContainerId = 0xffffffff;
753cbfd045bSScott Long 	if (aac_sync_fib(sc, ContainerCommand, 0, fib,
754cbfd045bSScott Long 	    sizeof(struct aac_close_command)))
75535863739SMike Smith 		printf("FAILED.\n");
75670545d1aSScott Long 	else
75770545d1aSScott Long 		printf("done\n");
75870545d1aSScott Long #if 0
759914da7d0SScott Long 	else {
760cbfd045bSScott Long 		fib->data[0] = 0;
76136e0bf6eSScott Long 		/*
762914da7d0SScott Long 		 * XXX Issuing this command to the controller makes it shut down
76336e0bf6eSScott Long 		 * but also keeps it from coming back up without a reset of the
76436e0bf6eSScott Long 		 * PCI bus.  This is not desirable if you are just unloading the
76536e0bf6eSScott Long 		 * driver module with the intent to reload it later.
76636e0bf6eSScott Long 		 */
767cbfd045bSScott Long 		if (aac_sync_fib(sc, FsaHostShutdown, AAC_FIBSTATE_SHUTDOWN,
768cbfd045bSScott Long 		    fib, 1)) {
76935863739SMike Smith 			printf("FAILED.\n");
77035863739SMike Smith 		} else {
77135863739SMike Smith 			printf("done.\n");
77235863739SMike Smith 		}
77335863739SMike Smith 	}
77470545d1aSScott Long #endif
77535863739SMike Smith 
77635863739SMike Smith 	AAC_MASK_INTERRUPTS(sc);
7773576af8fSScott Long 	aac_release_sync_fib(sc);
7787cb209f5SScott Long 	mtx_unlock(&sc->aac_io_lock);
77935863739SMike Smith 
78035863739SMike Smith 	return(0);
78135863739SMike Smith }
78235863739SMike Smith 
783914da7d0SScott Long /*
78435863739SMike Smith  * Bring the controller to a quiescent state, ready for system suspend.
78535863739SMike Smith  */
78635863739SMike Smith int
78735863739SMike Smith aac_suspend(device_t dev)
78835863739SMike Smith {
789914da7d0SScott Long 	struct aac_softc *sc;
79035863739SMike Smith 
791914da7d0SScott Long 	sc = device_get_softc(dev);
792914da7d0SScott Long 
79331a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
79435863739SMike Smith 	sc->aac_state |= AAC_STATE_SUSPEND;
79535863739SMike Smith 
79635863739SMike Smith 	AAC_MASK_INTERRUPTS(sc);
79735863739SMike Smith 	return(0);
79835863739SMike Smith }
79935863739SMike Smith 
800914da7d0SScott Long /*
80135863739SMike Smith  * Bring the controller back to a state ready for operation.
80235863739SMike Smith  */
80335863739SMike Smith int
80435863739SMike Smith aac_resume(device_t dev)
80535863739SMike Smith {
806914da7d0SScott Long 	struct aac_softc *sc;
80735863739SMike Smith 
808914da7d0SScott Long 	sc = device_get_softc(dev);
809914da7d0SScott Long 
81031a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
81135863739SMike Smith 	sc->aac_state &= ~AAC_STATE_SUSPEND;
81235863739SMike Smith 	AAC_UNMASK_INTERRUPTS(sc);
81335863739SMike Smith 	return(0);
81435863739SMike Smith }
81535863739SMike Smith 
816914da7d0SScott Long /*
8177cb209f5SScott Long  * Interrupt handler for NEW_COMM interface.
81835863739SMike Smith  */
81935863739SMike Smith void
8207cb209f5SScott Long aac_new_intr(void *arg)
8217cb209f5SScott Long {
8227cb209f5SScott Long 	struct aac_softc *sc;
8237cb209f5SScott Long 	u_int32_t index, fast;
8247cb209f5SScott Long 	struct aac_command *cm;
8257cb209f5SScott Long 	struct aac_fib *fib;
8267cb209f5SScott Long 	int i;
8277cb209f5SScott Long 
8287cb209f5SScott Long 	sc = (struct aac_softc *)arg;
8297cb209f5SScott Long 
83031a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
8317cb209f5SScott Long 	mtx_lock(&sc->aac_io_lock);
8327cb209f5SScott Long 	while (1) {
8337cb209f5SScott Long 		index = AAC_GET_OUTB_QUEUE(sc);
8347cb209f5SScott Long 		if (index == 0xffffffff)
8357cb209f5SScott Long 			index = AAC_GET_OUTB_QUEUE(sc);
8367cb209f5SScott Long 		if (index == 0xffffffff)
8377cb209f5SScott Long 			break;
8387cb209f5SScott Long 		if (index & 2) {
8397cb209f5SScott Long 			if (index == 0xfffffffe) {
8407cb209f5SScott Long 				/* XXX This means that the controller wants
8417cb209f5SScott Long 				 * more work.  Ignore it for now.
8427cb209f5SScott Long 				 */
8437cb209f5SScott Long 				continue;
8447cb209f5SScott Long 			}
8457cb209f5SScott Long 			/* AIF */
8467cb209f5SScott Long 			fib = (struct aac_fib *)malloc(sizeof *fib, M_AACBUF,
8477cb209f5SScott Long 				   M_NOWAIT | M_ZERO);
8487cb209f5SScott Long 			if (fib == NULL) {
8497cb209f5SScott Long 				/* If we're really this short on memory,
8507cb209f5SScott Long 				 * hopefully breaking out of the handler will
8517cb209f5SScott Long 				 * allow something to get freed.  This
8527cb209f5SScott Long 				 * actually sucks a whole lot.
8537cb209f5SScott Long 				 */
8547cb209f5SScott Long 				break;
8557cb209f5SScott Long 			}
8567cb209f5SScott Long 			index &= ~2;
8577cb209f5SScott Long 			for (i = 0; i < sizeof(struct aac_fib)/4; ++i)
858ff0991c4SAttilio Rao 				((u_int32_t *)fib)[i] = AAC_MEM1_GETREG4(sc, index + i*4);
8597cb209f5SScott Long 			aac_handle_aif(sc, fib);
8607cb209f5SScott Long 			free(fib, M_AACBUF);
8617cb209f5SScott Long 
8627cb209f5SScott Long 			/*
8637cb209f5SScott Long 			 * AIF memory is owned by the adapter, so let it
8647cb209f5SScott Long 			 * know that we are done with it.
8657cb209f5SScott Long 			 */
8667cb209f5SScott Long 			AAC_SET_OUTB_QUEUE(sc, index);
8677cb209f5SScott Long 			AAC_CLEAR_ISTATUS(sc, AAC_DB_RESPONSE_READY);
8687cb209f5SScott Long 		} else {
8697cb209f5SScott Long 			fast = index & 1;
8707cb209f5SScott Long 			cm = sc->aac_commands + (index >> 2);
8717cb209f5SScott Long 			fib = cm->cm_fib;
8727cb209f5SScott Long 			if (fast) {
8737cb209f5SScott Long 				fib->Header.XferState |= AAC_FIBSTATE_DONEADAP;
8747cb209f5SScott Long 				*((u_int32_t *)(fib->data)) = AAC_ERROR_NORMAL;
8757cb209f5SScott Long 			}
8767cb209f5SScott Long 			aac_remove_busy(cm);
8777cb209f5SScott Long  			aac_unmap_command(cm);
8787cb209f5SScott Long 			cm->cm_flags |= AAC_CMD_COMPLETED;
8797cb209f5SScott Long 
8807cb209f5SScott Long 			/* is there a completion handler? */
8817cb209f5SScott Long 			if (cm->cm_complete != NULL) {
8827cb209f5SScott Long 				cm->cm_complete(cm);
8837cb209f5SScott Long 			} else {
8847cb209f5SScott Long 				/* assume that someone is sleeping on this
8857cb209f5SScott Long 				 * command
8867cb209f5SScott Long 				 */
8877cb209f5SScott Long 				wakeup(cm);
8887cb209f5SScott Long 			}
8897cb209f5SScott Long 			sc->flags &= ~AAC_QUEUE_FRZN;
8907cb209f5SScott Long 		}
8917cb209f5SScott Long 	}
8927cb209f5SScott Long 	/* see if we can start some more I/O */
8937cb209f5SScott Long 	if ((sc->flags & AAC_QUEUE_FRZN) == 0)
8947cb209f5SScott Long 		aac_startio(sc);
8957cb209f5SScott Long 
8967cb209f5SScott Long 	mtx_unlock(&sc->aac_io_lock);
8977cb209f5SScott Long }
8987cb209f5SScott Long 
899e46b9eeaSEd Maste /*
900e46b9eeaSEd Maste  * Interrupt filter for !NEW_COMM interface.
901e46b9eeaSEd Maste  */
902ef544f63SPaolo Pisati int
903e46b9eeaSEd Maste aac_filter(void *arg)
90435863739SMike Smith {
905914da7d0SScott Long 	struct aac_softc *sc;
90670545d1aSScott Long 	u_int16_t reason;
90735863739SMike Smith 
908914da7d0SScott Long 	sc = (struct aac_softc *)arg;
909914da7d0SScott Long 
91031a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
911f30ac74cSScott Long 	/*
9129148fa21SScott Long 	 * Read the status register directly.  This is faster than taking the
9139148fa21SScott Long 	 * driver lock and reading the queues directly.  It also saves having
9149148fa21SScott Long 	 * to turn parts of the driver lock into a spin mutex, which would be
9159148fa21SScott Long 	 * ugly.
916f30ac74cSScott Long 	 */
91735863739SMike Smith 	reason = AAC_GET_ISTATUS(sc);
918f30ac74cSScott Long 	AAC_CLEAR_ISTATUS(sc, reason);
919f30ac74cSScott Long 
9209c3a7fceSScott Long 	/* handle completion processing */
9219148fa21SScott Long 	if (reason & AAC_DB_RESPONSE_READY)
922cbc4d2dbSJohn Baldwin 		taskqueue_enqueue(taskqueue_fast, &sc->aac_task_complete);
92335863739SMike Smith 
9249148fa21SScott Long 	/* controller wants to talk to us */
9259148fa21SScott Long 	if (reason & (AAC_DB_PRINTF | AAC_DB_COMMAND_READY)) {
92670545d1aSScott Long 		/*
9279148fa21SScott Long 		 * XXX Make sure that we don't get fooled by strange messages
9289148fa21SScott Long 		 * that start with a NULL.
92970545d1aSScott Long 		 */
9309148fa21SScott Long 		if ((reason & AAC_DB_PRINTF) &&
9319148fa21SScott Long 			(sc->aac_common->ac_printf[0] == 0))
9329148fa21SScott Long 			sc->aac_common->ac_printf[0] = 32;
93370545d1aSScott Long 
9349148fa21SScott Long 		/*
9359148fa21SScott Long 		 * This might miss doing the actual wakeup.  However, the
936a32a982dSScott Long 		 * msleep that this is waking up has a timeout, so it will
9379148fa21SScott Long 		 * wake up eventually.  AIFs and printfs are low enough
9389148fa21SScott Long 		 * priority that they can handle hanging out for a few seconds
9399148fa21SScott Long 		 * if needed.
9409148fa21SScott Long 		 */
94136e0bf6eSScott Long 		wakeup(sc->aifthread);
94236e0bf6eSScott Long 	}
943ef544f63SPaolo Pisati 	return (FILTER_HANDLED);
9449148fa21SScott Long }
94535863739SMike Smith 
946c6eafcf2SScott Long /*
947914da7d0SScott Long  * Command Processing
948914da7d0SScott Long  */
94935863739SMike Smith 
950914da7d0SScott Long /*
95135863739SMike Smith  * Start as much queued I/O as possible on the controller
95235863739SMike Smith  */
953fe3cb0e1SScott Long void
95435863739SMike Smith aac_startio(struct aac_softc *sc)
95535863739SMike Smith {
95635863739SMike Smith 	struct aac_command *cm;
957397fa34fSScott Long 	int error;
95835863739SMike Smith 
95931a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
96035863739SMike Smith 
96135863739SMike Smith 	for (;;) {
962914da7d0SScott Long 		/*
963397fa34fSScott Long 		 * This flag might be set if the card is out of resources.
964397fa34fSScott Long 		 * Checking it here prevents an infinite loop of deferrals.
965397fa34fSScott Long 		 */
966397fa34fSScott Long 		if (sc->flags & AAC_QUEUE_FRZN)
967397fa34fSScott Long 			break;
968397fa34fSScott Long 
969397fa34fSScott Long 		/*
970914da7d0SScott Long 		 * Try to get a command that's been put off for lack of
971914da7d0SScott Long 		 * resources
972914da7d0SScott Long 		 */
97335863739SMike Smith 		cm = aac_dequeue_ready(sc);
97435863739SMike Smith 
975914da7d0SScott Long 		/*
976914da7d0SScott Long 		 * Try to build a command off the bio queue (ignore error
977914da7d0SScott Long 		 * return)
978914da7d0SScott Long 		 */
9790b94a66eSMike Smith 		if (cm == NULL)
98035863739SMike Smith 			aac_bio_command(sc, &cm);
98135863739SMike Smith 
98235863739SMike Smith 		/* nothing to do? */
98335863739SMike Smith 		if (cm == NULL)
98435863739SMike Smith 			break;
98535863739SMike Smith 
986cd481291SScott Long 		/* don't map more than once */
987cd481291SScott Long 		if (cm->cm_flags & AAC_CMD_MAPPED)
9884102d44bSScott Long 			panic("aac: command %p already mapped", cm);
98935863739SMike Smith 
990397fa34fSScott Long 		/*
991397fa34fSScott Long 		 * Set up the command to go to the controller.  If there are no
992397fa34fSScott Long 		 * data buffers associated with the command then it can bypass
993397fa34fSScott Long 		 * busdma.
994397fa34fSScott Long 		 */
995cd481291SScott Long 		if (cm->cm_datalen != 0) {
9968fce673cSMarius Strobl 			if (cm->cm_flags & AAC_REQ_BIO)
9978fce673cSMarius Strobl 				error = bus_dmamap_load_bio(
9988fce673cSMarius Strobl 				    sc->aac_buffer_dmat, cm->cm_datamap,
9998fce673cSMarius Strobl 				    (struct bio *)cm->cm_private,
10008fce673cSMarius Strobl 				    aac_map_command_sg, cm, 0);
10018fce673cSMarius Strobl 			else
1002397fa34fSScott Long 				error = bus_dmamap_load(sc->aac_buffer_dmat,
1003397fa34fSScott Long 				    cm->cm_datamap, cm->cm_data,
10048fce673cSMarius Strobl 				    cm->cm_datalen, aac_map_command_sg, cm, 0);
1005cd481291SScott Long 			if (error == EINPROGRESS) {
100631a0399eSEd Maste 				fwprintf(sc, HBA_FLAGS_DBG_COMM_B, "freezing queue\n");
1007cd481291SScott Long 				sc->flags |= AAC_QUEUE_FRZN;
1008614c22b2SScott Long 			} else if (error != 0)
1009397fa34fSScott Long 				panic("aac_startio: unexpected error %d from "
1010a620bad0SEd Maste 				      "busdma", error);
1011397fa34fSScott Long 		} else
10128778f63dSScott Long 			aac_map_command_sg(cm, NULL, 0, 0);
1013cd481291SScott Long 	}
101435863739SMike Smith }
101535863739SMike Smith 
1016914da7d0SScott Long /*
101735863739SMike Smith  * Handle notification of one or more FIBs coming from the controller.
101835863739SMike Smith  */
101935863739SMike Smith static void
102070545d1aSScott Long aac_command_thread(struct aac_softc *sc)
102135863739SMike Smith {
102235863739SMike Smith 	struct aac_fib *fib;
102335863739SMike Smith 	u_int32_t fib_size;
10249148fa21SScott Long 	int size, retval;
102535863739SMike Smith 
102631a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
102735863739SMike Smith 
1028bb6fe253SScott Long 	mtx_lock(&sc->aac_io_lock);
1029a32a982dSScott Long 	sc->aifflags = AAC_AIFFLAGS_RUNNING;
103036e0bf6eSScott Long 
1031a32a982dSScott Long 	while ((sc->aifflags & AAC_AIFFLAGS_EXIT) == 0) {
1032a32a982dSScott Long 
1033a32a982dSScott Long 		retval = 0;
1034a32a982dSScott Long 		if ((sc->aifflags & AAC_AIFFLAGS_PENDING) == 0)
1035a32a982dSScott Long 			retval = msleep(sc->aifthread, &sc->aac_io_lock, PRIBIO,
1036a32a982dSScott Long 					"aifthd", AAC_PERIODIC_INTERVAL * hz);
103736e0bf6eSScott Long 
10389148fa21SScott Long 		/*
10399148fa21SScott Long 		 * First see if any FIBs need to be allocated.  This needs
10409148fa21SScott Long 		 * to be called without the driver lock because contigmalloc
10411bd320ecSAttilio Rao 		 * can sleep.
10429148fa21SScott Long 		 */
10439148fa21SScott Long 		if ((sc->aifflags & AAC_AIFFLAGS_ALLOCFIBS) != 0) {
1044bb6fe253SScott Long 			mtx_unlock(&sc->aac_io_lock);
1045a32a982dSScott Long 			aac_alloc_commands(sc);
1046bb6fe253SScott Long 			mtx_lock(&sc->aac_io_lock);
10474102d44bSScott Long 			sc->aifflags &= ~AAC_AIFFLAGS_ALLOCFIBS;
1048a32a982dSScott Long 			aac_startio(sc);
1049a32a982dSScott Long 		}
10509148fa21SScott Long 
10519148fa21SScott Long 		/*
10529148fa21SScott Long 		 * While we're here, check to see if any commands are stuck.
10539148fa21SScott Long 		 * This is pretty low-priority, so it's ok if it doesn't
10549148fa21SScott Long 		 * always fire.
10559148fa21SScott Long 		 */
10569148fa21SScott Long 		if (retval == EWOULDBLOCK)
105770545d1aSScott Long 			aac_timeout(sc);
105870545d1aSScott Long 
105970545d1aSScott Long 		/* Check the hardware printf message buffer */
10609148fa21SScott Long 		if (sc->aac_common->ac_printf[0] != 0)
106170545d1aSScott Long 			aac_print_printf(sc);
106270545d1aSScott Long 
10639148fa21SScott Long 		/* Also check to see if the adapter has a command for us. */
10647cb209f5SScott Long 		if (sc->flags & AAC_FLAGS_NEW_COMM)
10657cb209f5SScott Long 			continue;
10667cb209f5SScott Long 		for (;;) {
10677cb209f5SScott Long 			if (aac_dequeue_fib(sc, AAC_HOST_NORM_CMD_QUEUE,
10687cb209f5SScott Long 					   &fib_size, &fib))
10697cb209f5SScott Long 				break;
107035863739SMike Smith 
107136e0bf6eSScott Long 			AAC_PRINT_FIB(sc, fib);
107236e0bf6eSScott Long 
107335863739SMike Smith 			switch (fib->Header.Command) {
107435863739SMike Smith 			case AifRequest:
107536e0bf6eSScott Long 				aac_handle_aif(sc, fib);
107635863739SMike Smith 				break;
107735863739SMike Smith 			default:
1078914da7d0SScott Long 				device_printf(sc->aac_dev, "unknown command "
1079914da7d0SScott Long 					      "from controller\n");
108035863739SMike Smith 				break;
108135863739SMike Smith 			}
108235863739SMike Smith 
108336e0bf6eSScott Long 			if ((fib->Header.XferState == 0) ||
10847cb209f5SScott Long 			    (fib->Header.StructType != AAC_FIBTYPE_TFIB)) {
108536e0bf6eSScott Long 				break;
10867cb209f5SScott Long 			}
108736e0bf6eSScott Long 
108870545d1aSScott Long 			/* Return the AIF to the controller. */
108936e0bf6eSScott Long 			if (fib->Header.XferState & AAC_FIBSTATE_FROMADAP) {
109036e0bf6eSScott Long 				fib->Header.XferState |= AAC_FIBSTATE_DONEHOST;
109136e0bf6eSScott Long 				*(AAC_FSAStatus*)fib->data = ST_OK;
109236e0bf6eSScott Long 
109336e0bf6eSScott Long 				/* XXX Compute the Size field? */
109436e0bf6eSScott Long 				size = fib->Header.Size;
109536e0bf6eSScott Long 				if (size > sizeof(struct aac_fib)) {
109636e0bf6eSScott Long 					size = sizeof(struct aac_fib);
109736e0bf6eSScott Long 					fib->Header.Size = size;
109836e0bf6eSScott Long 				}
109936e0bf6eSScott Long 				/*
1100914da7d0SScott Long 				 * Since we did not generate this command, it
1101914da7d0SScott Long 				 * cannot go through the normal
1102914da7d0SScott Long 				 * enqueue->startio chain.
110336e0bf6eSScott Long 				 */
1104914da7d0SScott Long 				aac_enqueue_response(sc,
1105914da7d0SScott Long 						 AAC_ADAP_NORM_RESP_QUEUE,
1106914da7d0SScott Long 						 fib);
110736e0bf6eSScott Long 			}
110836e0bf6eSScott Long 		}
110936e0bf6eSScott Long 	}
111036e0bf6eSScott Long 	sc->aifflags &= ~AAC_AIFFLAGS_RUNNING;
1111bb6fe253SScott Long 	mtx_unlock(&sc->aac_io_lock);
111236e0bf6eSScott Long 	wakeup(sc->aac_dev);
111336e0bf6eSScott Long 
11143745c395SJulian Elischer 	kproc_exit(0);
111535863739SMike Smith }
111635863739SMike Smith 
1117914da7d0SScott Long /*
11189c3a7fceSScott Long  * Process completed commands.
111935863739SMike Smith  */
112035863739SMike Smith static void
11219c3a7fceSScott Long aac_complete(void *context, int pending)
112235863739SMike Smith {
11239c3a7fceSScott Long 	struct aac_softc *sc;
112435863739SMike Smith 	struct aac_command *cm;
112535863739SMike Smith 	struct aac_fib *fib;
112635863739SMike Smith 	u_int32_t fib_size;
112735863739SMike Smith 
11289c3a7fceSScott Long 	sc = (struct aac_softc *)context;
112931a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
11309c3a7fceSScott Long 
1131bb6fe253SScott Long 	mtx_lock(&sc->aac_io_lock);
1132ae543596SScott Long 
11339c3a7fceSScott Long 	/* pull completed commands off the queue */
113435863739SMike Smith 	for (;;) {
113535863739SMike Smith 		/* look for completed FIBs on our queue */
1136914da7d0SScott Long 		if (aac_dequeue_fib(sc, AAC_HOST_NORM_RESP_QUEUE, &fib_size,
1137914da7d0SScott Long 							&fib))
113835863739SMike Smith 			break;	/* nothing to do */
113935863739SMike Smith 
1140ecd1c51fSScott Long 		/* get the command, unmap and hand off for processing */
1141cb0d64b9SScott Long 		cm = sc->aac_commands + fib->Header.SenderData;
114235863739SMike Smith 		if (cm == NULL) {
114335863739SMike Smith 			AAC_PRINT_FIB(sc, fib);
11449c3a7fceSScott Long 			break;
11459c3a7fceSScott Long 		}
11463e507710SEd Maste 		if ((cm->cm_flags & AAC_CMD_TIMEDOUT) != 0)
11473e507710SEd Maste 			device_printf(sc->aac_dev,
11483e507710SEd Maste 			    "COMMAND %p COMPLETED AFTER %d SECONDS\n",
11493e507710SEd Maste 			    cm, (int)(time_uptime-cm->cm_timestamp));
11503e507710SEd Maste 
11510b94a66eSMike Smith 		aac_remove_busy(cm);
11527cb209f5SScott Long 
1153ecd1c51fSScott Long  		aac_unmap_command(cm);
115435863739SMike Smith 		cm->cm_flags |= AAC_CMD_COMPLETED;
115535863739SMike Smith 
115635863739SMike Smith 		/* is there a completion handler? */
115735863739SMike Smith 		if (cm->cm_complete != NULL) {
115835863739SMike Smith 			cm->cm_complete(cm);
115935863739SMike Smith 		} else {
116035863739SMike Smith 			/* assume that someone is sleeping on this command */
116135863739SMike Smith 			wakeup(cm);
116235863739SMike Smith 		}
116335863739SMike Smith 	}
11640b94a66eSMike Smith 
11650b94a66eSMike Smith 	/* see if we can start some more I/O */
1166cd481291SScott Long 	sc->flags &= ~AAC_QUEUE_FRZN;
11670b94a66eSMike Smith 	aac_startio(sc);
1168ae543596SScott Long 
1169bb6fe253SScott Long 	mtx_unlock(&sc->aac_io_lock);
117035863739SMike Smith }
117135863739SMike Smith 
1172914da7d0SScott Long /*
117335863739SMike Smith  * Handle a bio submitted from a disk device.
117435863739SMike Smith  */
117535863739SMike Smith void
117635863739SMike Smith aac_submit_bio(struct bio *bp)
117735863739SMike Smith {
1178914da7d0SScott Long 	struct aac_disk *ad;
1179914da7d0SScott Long 	struct aac_softc *sc;
118035863739SMike Smith 
11817540e65eSScott Long 	ad = (struct aac_disk *)bp->bio_disk->d_drv1;
1182914da7d0SScott Long 	sc = ad->ad_controller;
118331a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
1184914da7d0SScott Long 
118535863739SMike Smith 	/* queue the BIO and try to get some work done */
11860b94a66eSMike Smith 	aac_enqueue_bio(sc, bp);
118735863739SMike Smith 	aac_startio(sc);
118835863739SMike Smith }
118935863739SMike Smith 
1190914da7d0SScott Long /*
119135863739SMike Smith  * Get a bio and build a command to go with it.
119235863739SMike Smith  */
119335863739SMike Smith static int
119435863739SMike Smith aac_bio_command(struct aac_softc *sc, struct aac_command **cmp)
119535863739SMike Smith {
119635863739SMike Smith 	struct aac_command *cm;
119735863739SMike Smith 	struct aac_fib *fib;
119835863739SMike Smith 	struct aac_disk *ad;
119935863739SMike Smith 	struct bio *bp;
120035863739SMike Smith 
120131a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
120235863739SMike Smith 
120335863739SMike Smith 	/* get the resources we will need */
120435863739SMike Smith 	cm = NULL;
1205a32a982dSScott Long 	bp = NULL;
120635863739SMike Smith 	if (aac_alloc_command(sc, &cm))	/* get a command */
120735863739SMike Smith 		goto fail;
1208a32a982dSScott Long 	if ((bp = aac_dequeue_bio(sc)) == NULL)
1209a32a982dSScott Long 		goto fail;
121035863739SMike Smith 
121135863739SMike Smith 	/* fill out the command */
12120b94a66eSMike Smith 	cm->cm_datalen = bp->bio_bcount;
12130b94a66eSMike Smith 	cm->cm_complete = aac_bio_complete;
12148fce673cSMarius Strobl 	cm->cm_flags = AAC_REQ_BIO;
121535863739SMike Smith 	cm->cm_private = bp;
12162b3b0f17SScott Long 	cm->cm_timestamp = time_uptime;
121735863739SMike Smith 
121835863739SMike Smith 	/* build the FIB */
121935863739SMike Smith 	fib = cm->cm_fib;
1220b85f5808SScott Long 	fib->Header.Size = sizeof(struct aac_fib_header);
122135863739SMike Smith 	fib->Header.XferState =
122235863739SMike Smith 		AAC_FIBSTATE_HOSTOWNED   |
122335863739SMike Smith 		AAC_FIBSTATE_INITIALISED |
1224f30ac74cSScott Long 		AAC_FIBSTATE_EMPTY	 |
122535863739SMike Smith 		AAC_FIBSTATE_FROMHOST	 |
122635863739SMike Smith 		AAC_FIBSTATE_REXPECTED   |
1227f30ac74cSScott Long 		AAC_FIBSTATE_NORM	 |
1228f30ac74cSScott Long 		AAC_FIBSTATE_ASYNC	 |
1229f30ac74cSScott Long 		AAC_FIBSTATE_FAST_RESPONSE;
123035863739SMike Smith 
123135863739SMike Smith 	/* build the read/write request */
12327540e65eSScott Long 	ad = (struct aac_disk *)bp->bio_disk->d_drv1;
1233b85f5808SScott Long 
12347cb209f5SScott Long 	if (sc->flags & AAC_FLAGS_RAW_IO) {
12357cb209f5SScott Long 		struct aac_raw_io *raw;
12367cb209f5SScott Long 		raw = (struct aac_raw_io *)&fib->data[0];
12377cb209f5SScott Long 		fib->Header.Command = RawIo;
12387cb209f5SScott Long 		raw->BlockNumber = (u_int64_t)bp->bio_pblkno;
12397cb209f5SScott Long 		raw->ByteCount = bp->bio_bcount;
12407cb209f5SScott Long 		raw->ContainerId = ad->ad_container->co_mntobj.ObjectId;
12417cb209f5SScott Long 		raw->BpTotal = 0;
12427cb209f5SScott Long 		raw->BpComplete = 0;
12437cb209f5SScott Long 		fib->Header.Size += sizeof(struct aac_raw_io);
12447cb209f5SScott Long 		cm->cm_sgtable = (struct aac_sg_table *)&raw->SgMapRaw;
12457cb209f5SScott Long 		if (bp->bio_cmd == BIO_READ) {
12467cb209f5SScott Long 			raw->Flags = 1;
12477cb209f5SScott Long 			cm->cm_flags |= AAC_CMD_DATAIN;
12487cb209f5SScott Long 		} else {
12497cb209f5SScott Long 			raw->Flags = 0;
12507cb209f5SScott Long 			cm->cm_flags |= AAC_CMD_DATAOUT;
12517cb209f5SScott Long 		}
12527cb209f5SScott Long 	} else if ((sc->flags & AAC_FLAGS_SG_64BIT) == 0) {
1253b85f5808SScott Long 		fib->Header.Command = ContainerCommand;
12549e2e96d8SScott Long 		if (bp->bio_cmd == BIO_READ) {
1255b85f5808SScott Long 			struct aac_blockread *br;
125635863739SMike Smith 			br = (struct aac_blockread *)&fib->data[0];
125735863739SMike Smith 			br->Command = VM_CtBlockRead;
125835863739SMike Smith 			br->ContainerId = ad->ad_container->co_mntobj.ObjectId;
125935863739SMike Smith 			br->BlockNumber = bp->bio_pblkno;
126035863739SMike Smith 			br->ByteCount = bp->bio_bcount;
126135863739SMike Smith 			fib->Header.Size += sizeof(struct aac_blockread);
126235863739SMike Smith 			cm->cm_sgtable = &br->SgMap;
126335863739SMike Smith 			cm->cm_flags |= AAC_CMD_DATAIN;
126435863739SMike Smith 		} else {
1265b85f5808SScott Long 			struct aac_blockwrite *bw;
126635863739SMike Smith 			bw = (struct aac_blockwrite *)&fib->data[0];
126735863739SMike Smith 			bw->Command = VM_CtBlockWrite;
126835863739SMike Smith 			bw->ContainerId = ad->ad_container->co_mntobj.ObjectId;
126935863739SMike Smith 			bw->BlockNumber = bp->bio_pblkno;
127035863739SMike Smith 			bw->ByteCount = bp->bio_bcount;
1271b85f5808SScott Long 			bw->Stable = CUNSTABLE;
127235863739SMike Smith 			fib->Header.Size += sizeof(struct aac_blockwrite);
127335863739SMike Smith 			cm->cm_flags |= AAC_CMD_DATAOUT;
127435863739SMike Smith 			cm->cm_sgtable = &bw->SgMap;
127535863739SMike Smith 		}
1276b85f5808SScott Long 	} else {
1277b85f5808SScott Long 		fib->Header.Command = ContainerCommand64;
1278b85f5808SScott Long 		if (bp->bio_cmd == BIO_READ) {
1279b85f5808SScott Long 			struct aac_blockread64 *br;
1280b85f5808SScott Long 			br = (struct aac_blockread64 *)&fib->data[0];
1281b85f5808SScott Long 			br->Command = VM_CtHostRead64;
1282b85f5808SScott Long 			br->ContainerId = ad->ad_container->co_mntobj.ObjectId;
1283b85f5808SScott Long 			br->SectorCount = bp->bio_bcount / AAC_BLOCK_SIZE;
1284b85f5808SScott Long 			br->BlockNumber = bp->bio_pblkno;
1285b85f5808SScott Long 			br->Pad = 0;
1286b85f5808SScott Long 			br->Flags = 0;
1287b85f5808SScott Long 			fib->Header.Size += sizeof(struct aac_blockread64);
128854e2ebdfSEd Maste 			cm->cm_flags |= AAC_CMD_DATAIN;
1289eec256deSAlexander Kabaev 			cm->cm_sgtable = (struct aac_sg_table *)&br->SgMap64;
1290b85f5808SScott Long 		} else {
1291b85f5808SScott Long 			struct aac_blockwrite64 *bw;
1292b85f5808SScott Long 			bw = (struct aac_blockwrite64 *)&fib->data[0];
1293b85f5808SScott Long 			bw->Command = VM_CtHostWrite64;
1294b85f5808SScott Long 			bw->ContainerId = ad->ad_container->co_mntobj.ObjectId;
1295b85f5808SScott Long 			bw->SectorCount = bp->bio_bcount / AAC_BLOCK_SIZE;
1296b85f5808SScott Long 			bw->BlockNumber = bp->bio_pblkno;
1297b85f5808SScott Long 			bw->Pad = 0;
1298b85f5808SScott Long 			bw->Flags = 0;
1299b85f5808SScott Long 			fib->Header.Size += sizeof(struct aac_blockwrite64);
130054e2ebdfSEd Maste 			cm->cm_flags |= AAC_CMD_DATAOUT;
1301eec256deSAlexander Kabaev 			cm->cm_sgtable = (struct aac_sg_table *)&bw->SgMap64;
1302b85f5808SScott Long 		}
1303b85f5808SScott Long 	}
130435863739SMike Smith 
130535863739SMike Smith 	*cmp = cm;
130635863739SMike Smith 	return(0);
130735863739SMike Smith 
130835863739SMike Smith fail:
13097cb209f5SScott Long 	if (bp != NULL)
13107cb209f5SScott Long 		aac_enqueue_bio(sc, bp);
131135863739SMike Smith 	if (cm != NULL)
131235863739SMike Smith 		aac_release_command(cm);
131335863739SMike Smith 	return(ENOMEM);
131435863739SMike Smith }
131535863739SMike Smith 
1316914da7d0SScott Long /*
131735863739SMike Smith  * Handle a bio-instigated command that has been completed.
131835863739SMike Smith  */
131935863739SMike Smith static void
132035863739SMike Smith aac_bio_complete(struct aac_command *cm)
132135863739SMike Smith {
132235863739SMike Smith 	struct aac_blockread_response *brr;
132335863739SMike Smith 	struct aac_blockwrite_response *bwr;
132435863739SMike Smith 	struct bio *bp;
132535863739SMike Smith 	AAC_FSAStatus status;
132635863739SMike Smith 
132735863739SMike Smith 	/* fetch relevant status and then release the command */
132835863739SMike Smith 	bp = (struct bio *)cm->cm_private;
13299e2e96d8SScott Long 	if (bp->bio_cmd == BIO_READ) {
133035863739SMike Smith 		brr = (struct aac_blockread_response *)&cm->cm_fib->data[0];
133135863739SMike Smith 		status = brr->Status;
133235863739SMike Smith 	} else {
133335863739SMike Smith 		bwr = (struct aac_blockwrite_response *)&cm->cm_fib->data[0];
133435863739SMike Smith 		status = bwr->Status;
133535863739SMike Smith 	}
133635863739SMike Smith 	aac_release_command(cm);
133735863739SMike Smith 
133835863739SMike Smith 	/* fix up the bio based on status */
133935863739SMike Smith 	if (status == ST_OK) {
134035863739SMike Smith 		bp->bio_resid = 0;
134135863739SMike Smith 	} else {
134235863739SMike Smith 		bp->bio_error = EIO;
134335863739SMike Smith 		bp->bio_flags |= BIO_ERROR;
134435863739SMike Smith 	}
13450b94a66eSMike Smith 	aac_biodone(bp);
134635863739SMike Smith }
134735863739SMike Smith 
1348914da7d0SScott Long /*
134935863739SMike Smith  * Submit a command to the controller, return when it completes.
1350b3457b51SScott Long  * XXX This is very dangerous!  If the card has gone out to lunch, we could
1351b3457b51SScott Long  *     be stuck here forever.  At the same time, signals are not caught
1352d8a0a473SScott Long  *     because there is a risk that a signal could wakeup the sleep before
1353d8a0a473SScott Long  *     the card has a chance to complete the command.  Since there is no way
1354d8a0a473SScott Long  *     to cancel a command that is in progress, we can't protect against the
1355d8a0a473SScott Long  *     card completing a command late and spamming the command and data
1356d8a0a473SScott Long  *     memory.  So, we are held hostage until the command completes.
135735863739SMike Smith  */
135835863739SMike Smith static int
1359d8a0a473SScott Long aac_wait_command(struct aac_command *cm)
136035863739SMike Smith {
1361ae543596SScott Long 	struct aac_softc *sc;
1362d8a0a473SScott Long 	int error;
136335863739SMike Smith 
1364ae543596SScott Long 	sc = cm->cm_sc;
136531a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
1366ae543596SScott Long 
136735863739SMike Smith 	/* Put the command on the ready queue and get things going */
136835863739SMike Smith 	aac_enqueue_ready(cm);
1369ae543596SScott Long 	aac_startio(sc);
1370ae543596SScott Long 	error = msleep(cm, &sc->aac_io_lock, PRIBIO, "aacwait", 0);
137135863739SMike Smith 	return(error);
137235863739SMike Smith }
137335863739SMike Smith 
1374914da7d0SScott Long /*
1375914da7d0SScott Long  *Command Buffer Management
1376914da7d0SScott Long  */
137735863739SMike Smith 
1378914da7d0SScott Long /*
137935863739SMike Smith  * Allocate a command.
138035863739SMike Smith  */
1381fe3cb0e1SScott Long int
138235863739SMike Smith aac_alloc_command(struct aac_softc *sc, struct aac_command **cmp)
138335863739SMike Smith {
138435863739SMike Smith 	struct aac_command *cm;
138535863739SMike Smith 
138631a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
138735863739SMike Smith 
1388ffb37f33SScott Long 	if ((cm = aac_dequeue_free(sc)) == NULL) {
1389b85f5808SScott Long 		if (sc->total_fibs < sc->aac_max_fibs) {
13901bd320ecSAttilio Rao 			mtx_lock(&sc->aac_io_lock);
1391ae543596SScott Long 			sc->aifflags |= AAC_AIFFLAGS_ALLOCFIBS;
13921bd320ecSAttilio Rao 			mtx_unlock(&sc->aac_io_lock);
1393ae543596SScott Long 			wakeup(sc->aifthread);
1394b85f5808SScott Long 		}
1395ae543596SScott Long 		return (EBUSY);
1396ffb37f33SScott Long 	}
139735863739SMike Smith 
13980b94a66eSMike Smith 	*cmp = cm;
13990b94a66eSMike Smith 	return(0);
14000b94a66eSMike Smith }
14010b94a66eSMike Smith 
1402914da7d0SScott Long /*
14030b94a66eSMike Smith  * Release a command back to the freelist.
14040b94a66eSMike Smith  */
1405fe3cb0e1SScott Long void
14060b94a66eSMike Smith aac_release_command(struct aac_command *cm)
14070b94a66eSMike Smith {
14087cb209f5SScott Long 	struct aac_event *event;
14097cb209f5SScott Long 	struct aac_softc *sc;
14107cb209f5SScott Long 
141131a0399eSEd Maste 	sc = cm->cm_sc;
141231a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
14130b94a66eSMike Smith 
14144109ba51SEd Maste 	/* (re)initialize the command/FIB */
1415b2b39b04SJohn Baldwin 	cm->cm_datalen = 0;
141635863739SMike Smith 	cm->cm_sgtable = NULL;
141735863739SMike Smith 	cm->cm_flags = 0;
141835863739SMike Smith 	cm->cm_complete = NULL;
141935863739SMike Smith 	cm->cm_private = NULL;
1420dbfc5960SEd Maste 	cm->cm_queue = AAC_ADAP_NORM_CMD_QUEUE;
142135863739SMike Smith 	cm->cm_fib->Header.XferState = AAC_FIBSTATE_EMPTY;
142235863739SMike Smith 	cm->cm_fib->Header.StructType = AAC_FIBTYPE_TFIB;
142335863739SMike Smith 	cm->cm_fib->Header.Flags = 0;
14247cb209f5SScott Long 	cm->cm_fib->Header.SenderSize = cm->cm_sc->aac_max_fib_size;
142535863739SMike Smith 
142635863739SMike Smith 	/*
142735863739SMike Smith 	 * These are duplicated in aac_start to cover the case where an
142835863739SMike Smith 	 * intermediate stage may have destroyed them.  They're left
14294109ba51SEd Maste 	 * initialized here for debugging purposes only.
143035863739SMike Smith 	 */
1431f30ac74cSScott Long 	cm->cm_fib->Header.ReceiverFibAddress = (u_int32_t)cm->cm_fibphys;
1432f30ac74cSScott Long 	cm->cm_fib->Header.SenderData = 0;
143335863739SMike Smith 
143435863739SMike Smith 	aac_enqueue_free(cm);
14357cb209f5SScott Long 
14362ad1c92dSEd Maste 	if ((event = TAILQ_FIRST(&sc->aac_ev_cmfree)) != NULL) {
14377cb209f5SScott Long 		TAILQ_REMOVE(&sc->aac_ev_cmfree, event, ev_links);
14387cb209f5SScott Long 		event->ev_callback(sc, event, event->ev_arg);
14397cb209f5SScott Long 	}
144035863739SMike Smith }
144135863739SMike Smith 
1442914da7d0SScott Long /*
14430b94a66eSMike Smith  * Map helper for command/FIB allocation.
144435863739SMike Smith  */
144535863739SMike Smith static void
14460b94a66eSMike Smith aac_map_command_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error)
144735863739SMike Smith {
14487cb209f5SScott Long 	uint64_t	*fibphys;
1449914da7d0SScott Long 
14507cb209f5SScott Long 	fibphys = (uint64_t *)arg;
145135863739SMike Smith 
1452ffb37f33SScott Long 	*fibphys = segs[0].ds_addr;
145335863739SMike Smith }
145435863739SMike Smith 
1455914da7d0SScott Long /*
14564109ba51SEd Maste  * Allocate and initialize commands/FIBs for this adapter.
145735863739SMike Smith  */
14580b94a66eSMike Smith static int
14590b94a66eSMike Smith aac_alloc_commands(struct aac_softc *sc)
146035863739SMike Smith {
146135863739SMike Smith 	struct aac_command *cm;
1462ffb37f33SScott Long 	struct aac_fibmap *fm;
14637cb209f5SScott Long 	uint64_t fibphys;
1464ffb37f33SScott Long 	int i, error;
146535863739SMike Smith 
146631a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
146735863739SMike Smith 
14687cb209f5SScott Long 	if (sc->total_fibs + sc->aac_max_fibs_alloc > sc->aac_max_fibs)
1469ffb37f33SScott Long 		return (ENOMEM);
1470ffb37f33SScott Long 
14718480cc63SScott Long 	fm = malloc(sizeof(struct aac_fibmap), M_AACBUF, M_NOWAIT|M_ZERO);
1472a6d35632SScott Long 	if (fm == NULL)
1473a6d35632SScott Long 		return (ENOMEM);
1474ffb37f33SScott Long 
14750b94a66eSMike Smith 	/* allocate the FIBs in DMAable memory and load them */
1476ffb37f33SScott Long 	if (bus_dmamem_alloc(sc->aac_fib_dmat, (void **)&fm->aac_fibs,
1477ffb37f33SScott Long 			     BUS_DMA_NOWAIT, &fm->aac_fibmap)) {
147870545d1aSScott Long 		device_printf(sc->aac_dev,
147970545d1aSScott Long 			      "Not enough contiguous memory available.\n");
14808480cc63SScott Long 		free(fm, M_AACBUF);
14810b94a66eSMike Smith 		return (ENOMEM);
148235863739SMike Smith 	}
1483128aa5a0SScott Long 
1484cd481291SScott Long 	/* Ignore errors since this doesn't bounce */
1485cd481291SScott Long 	(void)bus_dmamap_load(sc->aac_fib_dmat, fm->aac_fibmap, fm->aac_fibs,
14867cb209f5SScott Long 			      sc->aac_max_fibs_alloc * sc->aac_max_fib_size,
1487ffb37f33SScott Long 			      aac_map_command_helper, &fibphys, 0);
1488128aa5a0SScott Long 
14894109ba51SEd Maste 	/* initialize constant fields in the command structure */
14907cb209f5SScott Long 	bzero(fm->aac_fibs, sc->aac_max_fibs_alloc * sc->aac_max_fib_size);
14917cb209f5SScott Long 	for (i = 0; i < sc->aac_max_fibs_alloc; i++) {
14928480cc63SScott Long 		cm = sc->aac_commands + sc->total_fibs;
1493ffb37f33SScott Long 		fm->aac_commands = cm;
149435863739SMike Smith 		cm->cm_sc = sc;
14957cb209f5SScott Long 		cm->cm_fib = (struct aac_fib *)
14967cb209f5SScott Long 			((u_int8_t *)fm->aac_fibs + i*sc->aac_max_fib_size);
14977cb209f5SScott Long 		cm->cm_fibphys = fibphys + i*sc->aac_max_fib_size;
1498cb0d64b9SScott Long 		cm->cm_index = sc->total_fibs;
149935863739SMike Smith 
1500ffb37f33SScott Long 		if ((error = bus_dmamap_create(sc->aac_buffer_dmat, 0,
150193cfca22SScott Long 					       &cm->cm_datamap)) != 0)
15028480cc63SScott Long 			break;
150393cfca22SScott Long 		mtx_lock(&sc->aac_io_lock);
150493cfca22SScott Long 		aac_release_command(cm);
15058480cc63SScott Long 		sc->total_fibs++;
150693cfca22SScott Long 		mtx_unlock(&sc->aac_io_lock);
150735863739SMike Smith 	}
1508ffb37f33SScott Long 
15098480cc63SScott Long 	if (i > 0) {
151093cfca22SScott Long 		mtx_lock(&sc->aac_io_lock);
1511ffb37f33SScott Long 		TAILQ_INSERT_TAIL(&sc->aac_fibmap_tqh, fm, fm_link);
151231a0399eSEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_COMM_B, "total_fibs= %d\n", sc->total_fibs);
1513bb6fe253SScott Long 		mtx_unlock(&sc->aac_io_lock);
15140b94a66eSMike Smith 		return (0);
151535863739SMike Smith 	}
151635863739SMike Smith 
15178480cc63SScott Long 	bus_dmamap_unload(sc->aac_fib_dmat, fm->aac_fibmap);
15188480cc63SScott Long 	bus_dmamem_free(sc->aac_fib_dmat, fm->aac_fibs, fm->aac_fibmap);
15198480cc63SScott Long 	free(fm, M_AACBUF);
15208480cc63SScott Long 	return (ENOMEM);
15218480cc63SScott Long }
15228480cc63SScott Long 
1523914da7d0SScott Long /*
15240b94a66eSMike Smith  * Free FIBs owned by this adapter.
152535863739SMike Smith  */
152635863739SMike Smith static void
15278480cc63SScott Long aac_free_commands(struct aac_softc *sc)
152835863739SMike Smith {
15298480cc63SScott Long 	struct aac_fibmap *fm;
1530ffb37f33SScott Long 	struct aac_command *cm;
153135863739SMike Smith 	int i;
153235863739SMike Smith 
153331a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
153435863739SMike Smith 
15358480cc63SScott Long 	while ((fm = TAILQ_FIRST(&sc->aac_fibmap_tqh)) != NULL) {
15368480cc63SScott Long 
15378480cc63SScott Long 		TAILQ_REMOVE(&sc->aac_fibmap_tqh, fm, fm_link);
15388480cc63SScott Long 		/*
15398480cc63SScott Long 		 * We check against total_fibs to handle partially
15408480cc63SScott Long 		 * allocated blocks.
15418480cc63SScott Long 		 */
15427cb209f5SScott Long 		for (i = 0; i < sc->aac_max_fibs_alloc && sc->total_fibs--; i++) {
1543ffb37f33SScott Long 			cm = fm->aac_commands + i;
1544ffb37f33SScott Long 			bus_dmamap_destroy(sc->aac_buffer_dmat, cm->cm_datamap);
1545ffb37f33SScott Long 		}
1546ffb37f33SScott Long 		bus_dmamap_unload(sc->aac_fib_dmat, fm->aac_fibmap);
1547ffb37f33SScott Long 		bus_dmamem_free(sc->aac_fib_dmat, fm->aac_fibs, fm->aac_fibmap);
15488480cc63SScott Long 		free(fm, M_AACBUF);
15498480cc63SScott Long 	}
155035863739SMike Smith }
155135863739SMike Smith 
1552914da7d0SScott Long /*
155335863739SMike Smith  * Command-mapping helper function - populate this command's s/g table.
155435863739SMike Smith  */
155535863739SMike Smith static void
155635863739SMike Smith aac_map_command_sg(void *arg, bus_dma_segment_t *segs, int nseg, int error)
155735863739SMike Smith {
1558cd481291SScott Long 	struct aac_softc *sc;
1559914da7d0SScott Long 	struct aac_command *cm;
1560914da7d0SScott Long 	struct aac_fib *fib;
156135863739SMike Smith 	int i;
156235863739SMike Smith 
1563914da7d0SScott Long 	cm = (struct aac_command *)arg;
1564cd481291SScott Long 	sc = cm->cm_sc;
1565914da7d0SScott Long 	fib = cm->cm_fib;
156631a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
1567914da7d0SScott Long 
156835863739SMike Smith 	/* copy into the FIB */
1569b85f5808SScott Long 	if (cm->cm_sgtable != NULL) {
15707cb209f5SScott Long 		if (fib->Header.Command == RawIo) {
15717cb209f5SScott Long 			struct aac_sg_tableraw *sg;
15727cb209f5SScott Long 			sg = (struct aac_sg_tableraw *)cm->cm_sgtable;
15737cb209f5SScott Long 			sg->SgCount = nseg;
15747cb209f5SScott Long 			for (i = 0; i < nseg; i++) {
15757cb209f5SScott Long 				sg->SgEntryRaw[i].SgAddress = segs[i].ds_addr;
15767cb209f5SScott Long 				sg->SgEntryRaw[i].SgByteCount = segs[i].ds_len;
15777cb209f5SScott Long 				sg->SgEntryRaw[i].Next = 0;
15787cb209f5SScott Long 				sg->SgEntryRaw[i].Prev = 0;
15797cb209f5SScott Long 				sg->SgEntryRaw[i].Flags = 0;
15807cb209f5SScott Long 			}
15817cb209f5SScott Long 			/* update the FIB size for the s/g count */
15827cb209f5SScott Long 			fib->Header.Size += nseg*sizeof(struct aac_sg_entryraw);
15837cb209f5SScott Long 		} else if ((cm->cm_sc->flags & AAC_FLAGS_SG_64BIT) == 0) {
1584b85f5808SScott Long 			struct aac_sg_table *sg;
1585b85f5808SScott Long 			sg = cm->cm_sgtable;
158635863739SMike Smith 			sg->SgCount = nseg;
158735863739SMike Smith 			for (i = 0; i < nseg; i++) {
158835863739SMike Smith 				sg->SgEntry[i].SgAddress = segs[i].ds_addr;
158935863739SMike Smith 				sg->SgEntry[i].SgByteCount = segs[i].ds_len;
159035863739SMike Smith 			}
159135863739SMike Smith 			/* update the FIB size for the s/g count */
159235863739SMike Smith 			fib->Header.Size += nseg*sizeof(struct aac_sg_entry);
1593b85f5808SScott Long 		} else {
1594b85f5808SScott Long 			struct aac_sg_table64 *sg;
1595b85f5808SScott Long 			sg = (struct aac_sg_table64 *)cm->cm_sgtable;
1596b85f5808SScott Long 			sg->SgCount = nseg;
1597b85f5808SScott Long 			for (i = 0; i < nseg; i++) {
1598b85f5808SScott Long 				sg->SgEntry64[i].SgAddress = segs[i].ds_addr;
1599b85f5808SScott Long 				sg->SgEntry64[i].SgByteCount = segs[i].ds_len;
160035863739SMike Smith 			}
1601b85f5808SScott Long 			/* update the FIB size for the s/g count */
1602b85f5808SScott Long 			fib->Header.Size += nseg*sizeof(struct aac_sg_entry64);
1603b85f5808SScott Long 		}
1604b85f5808SScott Long 	}
160535863739SMike Smith 
1606cd481291SScott Long 	/* Fix up the address values in the FIB.  Use the command array index
1607cd481291SScott Long 	 * instead of a pointer since these fields are only 32 bits.  Shift
16087cb209f5SScott Long 	 * the SenderFibAddress over to make room for the fast response bit
16097cb209f5SScott Long 	 * and for the AIF bit
161035863739SMike Smith 	 */
16117cb209f5SScott Long 	cm->cm_fib->Header.SenderFibAddress = (cm->cm_index << 2);
16127cb209f5SScott Long 	cm->cm_fib->Header.ReceiverFibAddress = (u_int32_t)cm->cm_fibphys;
161335863739SMike Smith 
1614cd481291SScott Long 	/* save a pointer to the command for speedy reverse-lookup */
1615cd481291SScott Long 	cm->cm_fib->Header.SenderData = cm->cm_index;
161635863739SMike Smith 
161735863739SMike Smith 	if (cm->cm_flags & AAC_CMD_DATAIN)
1618c6eafcf2SScott Long 		bus_dmamap_sync(sc->aac_buffer_dmat, cm->cm_datamap,
1619c6eafcf2SScott Long 				BUS_DMASYNC_PREREAD);
162035863739SMike Smith 	if (cm->cm_flags & AAC_CMD_DATAOUT)
1621c6eafcf2SScott Long 		bus_dmamap_sync(sc->aac_buffer_dmat, cm->cm_datamap,
1622c6eafcf2SScott Long 				BUS_DMASYNC_PREWRITE);
162335863739SMike Smith 	cm->cm_flags |= AAC_CMD_MAPPED;
1624cd481291SScott Long 
16257cb209f5SScott Long 	if (sc->flags & AAC_FLAGS_NEW_COMM) {
16267cb209f5SScott Long 		int count = 10000000L;
16277cb209f5SScott Long 		while (AAC_SEND_COMMAND(sc, cm) != 0) {
16287cb209f5SScott Long 			if (--count == 0) {
16297cb209f5SScott Long 				aac_unmap_command(cm);
16307cb209f5SScott Long 				sc->flags |= AAC_QUEUE_FRZN;
16317cb209f5SScott Long 				aac_requeue_ready(cm);
16327cb209f5SScott Long 			}
16337cb209f5SScott Long 			DELAY(5);			/* wait 5 usec. */
16347cb209f5SScott Long 		}
16357cb209f5SScott Long 	} else {
1636397fa34fSScott Long 		/* Put the FIB on the outbound queue */
16374102d44bSScott Long 		if (aac_enqueue_fib(sc, cm->cm_queue, cm) == EBUSY) {
16384102d44bSScott Long 			aac_unmap_command(cm);
1639397fa34fSScott Long 			sc->flags |= AAC_QUEUE_FRZN;
1640cd481291SScott Long 			aac_requeue_ready(cm);
16414102d44bSScott Long 		}
16427cb209f5SScott Long 	}
164335863739SMike Smith }
164435863739SMike Smith 
1645914da7d0SScott Long /*
164635863739SMike Smith  * Unmap a command from controller-visible space.
164735863739SMike Smith  */
164835863739SMike Smith static void
164935863739SMike Smith aac_unmap_command(struct aac_command *cm)
165035863739SMike Smith {
1651914da7d0SScott Long 	struct aac_softc *sc;
165235863739SMike Smith 
1653914da7d0SScott Long 	sc = cm->cm_sc;
165431a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
1655914da7d0SScott Long 
165635863739SMike Smith 	if (!(cm->cm_flags & AAC_CMD_MAPPED))
165735863739SMike Smith 		return;
165835863739SMike Smith 
165935863739SMike Smith 	if (cm->cm_datalen != 0) {
166035863739SMike Smith 		if (cm->cm_flags & AAC_CMD_DATAIN)
1661c6eafcf2SScott Long 			bus_dmamap_sync(sc->aac_buffer_dmat, cm->cm_datamap,
1662c6eafcf2SScott Long 					BUS_DMASYNC_POSTREAD);
166335863739SMike Smith 		if (cm->cm_flags & AAC_CMD_DATAOUT)
1664c6eafcf2SScott Long 			bus_dmamap_sync(sc->aac_buffer_dmat, cm->cm_datamap,
1665c6eafcf2SScott Long 					BUS_DMASYNC_POSTWRITE);
166635863739SMike Smith 
166735863739SMike Smith 		bus_dmamap_unload(sc->aac_buffer_dmat, cm->cm_datamap);
166835863739SMike Smith 	}
166935863739SMike Smith 	cm->cm_flags &= ~AAC_CMD_MAPPED;
167035863739SMike Smith }
167135863739SMike Smith 
1672914da7d0SScott Long /*
1673914da7d0SScott Long  * Hardware Interface
1674914da7d0SScott Long  */
167535863739SMike Smith 
1676914da7d0SScott Long /*
16774109ba51SEd Maste  * Initialize the adapter.
167835863739SMike Smith  */
167935863739SMike Smith static void
168035863739SMike Smith aac_common_map(void *arg, bus_dma_segment_t *segs, int nseg, int error)
168135863739SMike Smith {
1682914da7d0SScott Long 	struct aac_softc *sc;
168335863739SMike Smith 
1684914da7d0SScott Long 	sc = (struct aac_softc *)arg;
168531a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
1686914da7d0SScott Long 
168735863739SMike Smith 	sc->aac_common_busaddr = segs[0].ds_addr;
168835863739SMike Smith }
168935863739SMike Smith 
1690a6d35632SScott Long static int
1691a6d35632SScott Long aac_check_firmware(struct aac_softc *sc)
1692a6d35632SScott Long {
169304f4d586SEd Maste 	u_int32_t code, major, minor, options = 0, atu_size = 0;
1694da4882c2SMarius Strobl 	int rid, status;
169504f4d586SEd Maste 	time_t then;
1696a6d35632SScott Long 
169731a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
169804f4d586SEd Maste 	/*
169904f4d586SEd Maste 	 * Wait for the adapter to come ready.
170004f4d586SEd Maste 	 */
170104f4d586SEd Maste 	then = time_uptime;
170204f4d586SEd Maste 	do {
170304f4d586SEd Maste 		code = AAC_GET_FWSTATUS(sc);
170404f4d586SEd Maste 		if (code & AAC_SELF_TEST_FAILED) {
170504f4d586SEd Maste 			device_printf(sc->aac_dev, "FATAL: selftest failed\n");
170604f4d586SEd Maste 			return(ENXIO);
170704f4d586SEd Maste 		}
170804f4d586SEd Maste 		if (code & AAC_KERNEL_PANIC) {
170904f4d586SEd Maste 			device_printf(sc->aac_dev,
1710a620bad0SEd Maste 				      "FATAL: controller kernel panic");
171104f4d586SEd Maste 			return(ENXIO);
171204f4d586SEd Maste 		}
171304f4d586SEd Maste 		if (time_uptime > (then + AAC_BOOT_TIMEOUT)) {
171404f4d586SEd Maste 			device_printf(sc->aac_dev,
171504f4d586SEd Maste 				      "FATAL: controller not coming ready, "
171604f4d586SEd Maste 					   "status %x\n", code);
171704f4d586SEd Maste 			return(ENXIO);
171804f4d586SEd Maste 		}
171904f4d586SEd Maste 	} while (!(code & AAC_UP_AND_RUNNING));
1720a6d35632SScott Long 
1721fe94b852SScott Long 	/*
1722fe94b852SScott Long 	 * Retrieve the firmware version numbers.  Dell PERC2/QC cards with
1723fe94b852SScott Long 	 * firmware version 1.x are not compatible with this driver.
1724fe94b852SScott Long 	 */
1725a6d35632SScott Long 	if (sc->flags & AAC_FLAGS_PERC2QC) {
1726fe94b852SScott Long 		if (aac_sync_command(sc, AAC_MONKER_GETKERNVER, 0, 0, 0, 0,
1727fe94b852SScott Long 				     NULL)) {
1728fe94b852SScott Long 			device_printf(sc->aac_dev,
1729fe94b852SScott Long 				      "Error reading firmware version\n");
1730fe94b852SScott Long 			return (EIO);
1731fe94b852SScott Long 		}
1732fe94b852SScott Long 
1733fe94b852SScott Long 		/* These numbers are stored as ASCII! */
1734a6d35632SScott Long 		major = (AAC_GET_MAILBOX(sc, 1) & 0xff) - 0x30;
1735a6d35632SScott Long 		minor = (AAC_GET_MAILBOX(sc, 2) & 0xff) - 0x30;
1736fe94b852SScott Long 		if (major == 1) {
1737fe94b852SScott Long 			device_printf(sc->aac_dev,
1738fe94b852SScott Long 			    "Firmware version %d.%d is not supported.\n",
1739fe94b852SScott Long 			    major, minor);
1740fe94b852SScott Long 			return (EINVAL);
1741fe94b852SScott Long 		}
1742fe94b852SScott Long 	}
1743fe94b852SScott Long 
1744a6d35632SScott Long 	/*
1745a6d35632SScott Long 	 * Retrieve the capabilities/supported options word so we know what
1746a441b3fcSScott Long 	 * work-arounds to enable.  Some firmware revs don't support this
1747a441b3fcSScott Long 	 * command.
1748a6d35632SScott Long 	 */
1749a441b3fcSScott Long 	if (aac_sync_command(sc, AAC_MONKER_GETINFO, 0, 0, 0, 0, &status)) {
1750a441b3fcSScott Long 		if (status != AAC_SRB_STS_INVALID_REQUEST) {
1751a441b3fcSScott Long 			device_printf(sc->aac_dev,
1752a441b3fcSScott Long 			     "RequestAdapterInfo failed\n");
1753a6d35632SScott Long 			return (EIO);
1754a6d35632SScott Long 		}
1755a441b3fcSScott Long 	} else {
1756a6d35632SScott Long 		options = AAC_GET_MAILBOX(sc, 1);
17577cb209f5SScott Long 		atu_size = AAC_GET_MAILBOX(sc, 2);
1758a6d35632SScott Long 		sc->supported_options = options;
1759a6d35632SScott Long 
1760a6d35632SScott Long 		if ((options & AAC_SUPPORTED_4GB_WINDOW) != 0 &&
1761a6d35632SScott Long 		    (sc->flags & AAC_FLAGS_NO4GB) == 0)
1762a6d35632SScott Long 			sc->flags |= AAC_FLAGS_4GB_WINDOW;
1763a6d35632SScott Long 		if (options & AAC_SUPPORTED_NONDASD)
1764a6d35632SScott Long 			sc->flags |= AAC_FLAGS_ENABLE_CAM;
1765cd481291SScott Long 		if ((options & AAC_SUPPORTED_SGMAP_HOST64) != 0
1766cd481291SScott Long 		     && (sizeof(bus_addr_t) > 4)) {
1767a441b3fcSScott Long 			device_printf(sc->aac_dev,
1768a441b3fcSScott Long 			    "Enabling 64-bit address support\n");
1769a6d35632SScott Long 			sc->flags |= AAC_FLAGS_SG_64BIT;
1770a6d35632SScott Long 		}
1771a441b3fcSScott Long 		if ((options & AAC_SUPPORTED_NEW_COMM)
1772da4882c2SMarius Strobl 		 && sc->aac_if->aif_send_command)
17737cb209f5SScott Long 			sc->flags |= AAC_FLAGS_NEW_COMM;
17747cb209f5SScott Long 		if (options & AAC_SUPPORTED_64BIT_ARRAYSIZE)
17757cb209f5SScott Long 			sc->flags |= AAC_FLAGS_ARRAY_64BIT;
1776a441b3fcSScott Long 	}
1777a6d35632SScott Long 
1778a6d35632SScott Long 	/* Check for broken hardware that does a lower number of commands */
17797cb209f5SScott Long 	sc->aac_max_fibs = (sc->flags & AAC_FLAGS_256FIBS ? 256:512);
17807cb209f5SScott Long 
17817cb209f5SScott Long 	/* Remap mem. resource, if required */
17827cb209f5SScott Long 	if ((sc->flags & AAC_FLAGS_NEW_COMM) &&
1783ff0991c4SAttilio Rao 	    atu_size > rman_get_size(sc->aac_regs_res1)) {
1784da4882c2SMarius Strobl 		rid = rman_get_rid(sc->aac_regs_res1);
1785da4882c2SMarius Strobl 		bus_release_resource(sc->aac_dev, SYS_RES_MEMORY, rid,
1786da4882c2SMarius Strobl 		    sc->aac_regs_res1);
1787c47476d7SJustin Hibbits 		sc->aac_regs_res1 = bus_alloc_resource_anywhere(sc->aac_dev,
1788c47476d7SJustin Hibbits 		    SYS_RES_MEMORY, &rid, atu_size, RF_ACTIVE);
1789ff0991c4SAttilio Rao 		if (sc->aac_regs_res1 == NULL) {
1790ff0991c4SAttilio Rao 			sc->aac_regs_res1 = bus_alloc_resource_any(
1791da4882c2SMarius Strobl 			    sc->aac_dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
1792ff0991c4SAttilio Rao 			if (sc->aac_regs_res1 == NULL) {
17937cb209f5SScott Long 				device_printf(sc->aac_dev,
17947cb209f5SScott Long 				    "couldn't allocate register window\n");
17957cb209f5SScott Long 				return (ENXIO);
17967cb209f5SScott Long 			}
17977cb209f5SScott Long 			sc->flags &= ~AAC_FLAGS_NEW_COMM;
17987cb209f5SScott Long 		}
1799ff0991c4SAttilio Rao 		sc->aac_btag1 = rman_get_bustag(sc->aac_regs_res1);
1800ff0991c4SAttilio Rao 		sc->aac_bhandle1 = rman_get_bushandle(sc->aac_regs_res1);
1801ff0991c4SAttilio Rao 
1802ff0991c4SAttilio Rao 		if (sc->aac_hwif == AAC_HWIF_NARK) {
1803ff0991c4SAttilio Rao 			sc->aac_regs_res0 = sc->aac_regs_res1;
1804ff0991c4SAttilio Rao 			sc->aac_btag0 = sc->aac_btag1;
1805ff0991c4SAttilio Rao 			sc->aac_bhandle0 = sc->aac_bhandle1;
1806ff0991c4SAttilio Rao 		}
18077cb209f5SScott Long 	}
18087cb209f5SScott Long 
18097cb209f5SScott Long 	/* Read preferred settings */
18107cb209f5SScott Long 	sc->aac_max_fib_size = sizeof(struct aac_fib);
18117cb209f5SScott Long 	sc->aac_max_sectors = 128;				/* 64KB */
18127cb209f5SScott Long 	if (sc->flags & AAC_FLAGS_SG_64BIT)
1813a441b3fcSScott Long 		sc->aac_sg_tablesize = (AAC_FIB_DATASIZE
18147e7a458eSEd Maste 		 - sizeof(struct aac_blockwrite64))
18157e7a458eSEd Maste 		 / sizeof(struct aac_sg_entry64);
1816a6d35632SScott Long 	else
1817a441b3fcSScott Long 		sc->aac_sg_tablesize = (AAC_FIB_DATASIZE
18187e7a458eSEd Maste 		 - sizeof(struct aac_blockwrite))
18197e7a458eSEd Maste 		 / sizeof(struct aac_sg_entry);
1820a441b3fcSScott Long 
18217cb209f5SScott Long 	if (!aac_sync_command(sc, AAC_MONKER_GETCOMMPREF, 0, 0, 0, 0, NULL)) {
18227cb209f5SScott Long 		options = AAC_GET_MAILBOX(sc, 1);
18237cb209f5SScott Long 		sc->aac_max_fib_size = (options & 0xFFFF);
18247cb209f5SScott Long 		sc->aac_max_sectors = (options >> 16) << 1;
18257cb209f5SScott Long 		options = AAC_GET_MAILBOX(sc, 2);
18267cb209f5SScott Long 		sc->aac_sg_tablesize = (options >> 16);
18277cb209f5SScott Long 		options = AAC_GET_MAILBOX(sc, 3);
18287cb209f5SScott Long 		sc->aac_max_fibs = (options & 0xFFFF);
18297cb209f5SScott Long 	}
18307cb209f5SScott Long 	if (sc->aac_max_fib_size > PAGE_SIZE)
18317cb209f5SScott Long 		sc->aac_max_fib_size = PAGE_SIZE;
18327cb209f5SScott Long 	sc->aac_max_fibs_alloc = PAGE_SIZE / sc->aac_max_fib_size;
1833a6d35632SScott Long 
1834f355c0e0SEd Maste 	if (sc->aac_max_fib_size > sizeof(struct aac_fib)) {
1835f355c0e0SEd Maste 		sc->flags |= AAC_FLAGS_RAW_IO;
1836f355c0e0SEd Maste 		device_printf(sc->aac_dev, "Enable Raw I/O\n");
1837f355c0e0SEd Maste 	}
1838523da39bSEd Maste 	if ((sc->flags & AAC_FLAGS_RAW_IO) &&
1839523da39bSEd Maste 	    (sc->flags & AAC_FLAGS_ARRAY_64BIT)) {
1840523da39bSEd Maste 		sc->flags |= AAC_FLAGS_LBA_64BIT;
1841523da39bSEd Maste 		device_printf(sc->aac_dev, "Enable 64-bit array\n");
1842523da39bSEd Maste 	}
1843f355c0e0SEd Maste 
1844fe94b852SScott Long 	return (0);
1845fe94b852SScott Long }
1846fe94b852SScott Long 
184735863739SMike Smith static int
184835863739SMike Smith aac_init(struct aac_softc *sc)
184935863739SMike Smith {
185035863739SMike Smith 	struct aac_adapter_init	*ip;
185104f4d586SEd Maste 	u_int32_t qoffset;
1852a6d35632SScott Long 	int error;
185335863739SMike Smith 
185431a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
1855ffb37f33SScott Long 
185635863739SMike Smith 	/*
1857914da7d0SScott Long 	 * Fill in the init structure.  This tells the adapter about the
1858914da7d0SScott Long 	 * physical location of various important shared data structures.
185935863739SMike Smith 	 */
186035863739SMike Smith 	ip = &sc->aac_common->ac_init;
186135863739SMike Smith 	ip->InitStructRevision = AAC_INIT_STRUCT_REVISION;
18627cb209f5SScott Long 	if (sc->aac_max_fib_size > sizeof(struct aac_fib)) {
18637cb209f5SScott Long 		ip->InitStructRevision = AAC_INIT_STRUCT_REVISION_4;
18647cb209f5SScott Long 		sc->flags |= AAC_FLAGS_RAW_IO;
18657cb209f5SScott Long 	}
1866f30ac74cSScott Long 	ip->MiniPortRevision = AAC_INIT_STRUCT_MINIPORT_REVISION;
186735863739SMike Smith 
1868c6eafcf2SScott Long 	ip->AdapterFibsPhysicalAddress = sc->aac_common_busaddr +
1869c6eafcf2SScott Long 					 offsetof(struct aac_common, ac_fibs);
1870149af931SScott Long 	ip->AdapterFibsVirtualAddress = 0;
187135863739SMike Smith 	ip->AdapterFibsSize = AAC_ADAPTER_FIBS * sizeof(struct aac_fib);
187235863739SMike Smith 	ip->AdapterFibAlign = sizeof(struct aac_fib);
187335863739SMike Smith 
1874c6eafcf2SScott Long 	ip->PrintfBufferAddress = sc->aac_common_busaddr +
1875c6eafcf2SScott Long 				  offsetof(struct aac_common, ac_printf);
187635863739SMike Smith 	ip->PrintfBufferSize = AAC_PRINTF_BUFSIZE;
187735863739SMike Smith 
18784b00f859SScott Long 	/*
18794b00f859SScott Long 	 * The adapter assumes that pages are 4K in size, except on some
18804b00f859SScott Long  	 * broken firmware versions that do the page->byte conversion twice,
18814b00f859SScott Long 	 * therefore 'assuming' that this value is in 16MB units (2^24).
18824b00f859SScott Long 	 * Round up since the granularity is so high.
18834b00f859SScott Long 	 */
1884f30ac74cSScott Long 	ip->HostPhysMemPages = ctob(physmem) / AAC_PAGE_SIZE;
18854b00f859SScott Long 	if (sc->flags & AAC_FLAGS_BROKEN_MEMMAP) {
18864b00f859SScott Long 		ip->HostPhysMemPages =
18874b00f859SScott Long 		    (ip->HostPhysMemPages + AAC_PAGE_SIZE) / AAC_PAGE_SIZE;
1888204c0befSScott Long 	}
18892b3b0f17SScott Long 	ip->HostElapsedSeconds = time_uptime;	/* reset later if invalid */
189035863739SMike Smith 
18917cb209f5SScott Long 	ip->InitFlags = 0;
18927cb209f5SScott Long 	if (sc->flags & AAC_FLAGS_NEW_COMM) {
1893e71d3b9cSEd Maste 		ip->InitFlags |= AAC_INITFLAGS_NEW_COMM_SUPPORTED;
18947cb209f5SScott Long 		device_printf(sc->aac_dev, "New comm. interface enabled\n");
18957cb209f5SScott Long 	}
18967cb209f5SScott Long 
18977cb209f5SScott Long 	ip->MaxIoCommands = sc->aac_max_fibs;
18987cb209f5SScott Long 	ip->MaxIoSize = sc->aac_max_sectors << 9;
18997cb209f5SScott Long 	ip->MaxFibSize = sc->aac_max_fib_size;
19007cb209f5SScott Long 
190135863739SMike Smith 	/*
19024109ba51SEd Maste 	 * Initialize FIB queues.  Note that it appears that the layout of the
1903c6eafcf2SScott Long 	 * indexes and the segmentation of the entries may be mandated by the
1904c6eafcf2SScott Long 	 * adapter, which is only told about the base of the queue index fields.
190535863739SMike Smith 	 *
190635863739SMike Smith 	 * The initial values of the indices are assumed to inform the adapter
1907914da7d0SScott Long 	 * of the sizes of the respective queues, and theoretically it could
1908914da7d0SScott Long 	 * work out the entire layout of the queue structures from this.  We
1909914da7d0SScott Long 	 * take the easy route and just lay this area out like everyone else
1910914da7d0SScott Long 	 * does.
191135863739SMike Smith 	 *
1912914da7d0SScott Long 	 * The Linux driver uses a much more complex scheme whereby several
1913914da7d0SScott Long 	 * header records are kept for each queue.  We use a couple of generic
1914914da7d0SScott Long 	 * list manipulation functions which 'know' the size of each list by
1915914da7d0SScott Long 	 * virtue of a table.
191635863739SMike Smith 	 */
1917b88ffdc8SScott Long 	qoffset = offsetof(struct aac_common, ac_qbuf) + AAC_QUEUE_ALIGN;
19180bcbebd6SScott Long 	qoffset &= ~(AAC_QUEUE_ALIGN - 1);
19190bcbebd6SScott Long 	sc->aac_queues =
19200bcbebd6SScott Long 	    (struct aac_queue_table *)((uintptr_t)sc->aac_common + qoffset);
1921b88ffdc8SScott Long 	ip->CommHeaderAddress = sc->aac_common_busaddr + qoffset;
192235863739SMike Smith 
1923c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_NORM_CMD_QUEUE][AAC_PRODUCER_INDEX] =
1924c6eafcf2SScott Long 		AAC_HOST_NORM_CMD_ENTRIES;
1925c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_NORM_CMD_QUEUE][AAC_CONSUMER_INDEX] =
1926c6eafcf2SScott Long 		AAC_HOST_NORM_CMD_ENTRIES;
1927c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_HIGH_CMD_QUEUE][AAC_PRODUCER_INDEX] =
1928c6eafcf2SScott Long 		AAC_HOST_HIGH_CMD_ENTRIES;
1929c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_HIGH_CMD_QUEUE][AAC_CONSUMER_INDEX] =
1930c6eafcf2SScott Long 		AAC_HOST_HIGH_CMD_ENTRIES;
1931c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_NORM_CMD_QUEUE][AAC_PRODUCER_INDEX] =
1932c6eafcf2SScott Long 		AAC_ADAP_NORM_CMD_ENTRIES;
1933c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_NORM_CMD_QUEUE][AAC_CONSUMER_INDEX] =
1934c6eafcf2SScott Long 		AAC_ADAP_NORM_CMD_ENTRIES;
1935c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_HIGH_CMD_QUEUE][AAC_PRODUCER_INDEX] =
1936c6eafcf2SScott Long 		AAC_ADAP_HIGH_CMD_ENTRIES;
1937c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_HIGH_CMD_QUEUE][AAC_CONSUMER_INDEX] =
1938c6eafcf2SScott Long 		AAC_ADAP_HIGH_CMD_ENTRIES;
1939c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_NORM_RESP_QUEUE][AAC_PRODUCER_INDEX]=
1940c6eafcf2SScott Long 		AAC_HOST_NORM_RESP_ENTRIES;
1941c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_NORM_RESP_QUEUE][AAC_CONSUMER_INDEX]=
1942c6eafcf2SScott Long 		AAC_HOST_NORM_RESP_ENTRIES;
1943c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_HIGH_RESP_QUEUE][AAC_PRODUCER_INDEX]=
1944c6eafcf2SScott Long 		AAC_HOST_HIGH_RESP_ENTRIES;
1945c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_HIGH_RESP_QUEUE][AAC_CONSUMER_INDEX]=
1946c6eafcf2SScott Long 		AAC_HOST_HIGH_RESP_ENTRIES;
1947c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_NORM_RESP_QUEUE][AAC_PRODUCER_INDEX]=
1948c6eafcf2SScott Long 		AAC_ADAP_NORM_RESP_ENTRIES;
1949c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_NORM_RESP_QUEUE][AAC_CONSUMER_INDEX]=
1950c6eafcf2SScott Long 		AAC_ADAP_NORM_RESP_ENTRIES;
1951c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_HIGH_RESP_QUEUE][AAC_PRODUCER_INDEX]=
1952c6eafcf2SScott Long 		AAC_ADAP_HIGH_RESP_ENTRIES;
1953c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_HIGH_RESP_QUEUE][AAC_CONSUMER_INDEX]=
1954c6eafcf2SScott Long 		AAC_ADAP_HIGH_RESP_ENTRIES;
1955c6eafcf2SScott Long 	sc->aac_qentries[AAC_HOST_NORM_CMD_QUEUE] =
1956c6eafcf2SScott Long 		&sc->aac_queues->qt_HostNormCmdQueue[0];
1957c6eafcf2SScott Long 	sc->aac_qentries[AAC_HOST_HIGH_CMD_QUEUE] =
1958c6eafcf2SScott Long 		&sc->aac_queues->qt_HostHighCmdQueue[0];
1959c6eafcf2SScott Long 	sc->aac_qentries[AAC_ADAP_NORM_CMD_QUEUE] =
1960c6eafcf2SScott Long 		&sc->aac_queues->qt_AdapNormCmdQueue[0];
1961c6eafcf2SScott Long 	sc->aac_qentries[AAC_ADAP_HIGH_CMD_QUEUE] =
1962c6eafcf2SScott Long 		&sc->aac_queues->qt_AdapHighCmdQueue[0];
1963c6eafcf2SScott Long 	sc->aac_qentries[AAC_HOST_NORM_RESP_QUEUE] =
1964c6eafcf2SScott Long 		&sc->aac_queues->qt_HostNormRespQueue[0];
1965c6eafcf2SScott Long 	sc->aac_qentries[AAC_HOST_HIGH_RESP_QUEUE] =
1966c6eafcf2SScott Long 		&sc->aac_queues->qt_HostHighRespQueue[0];
1967c6eafcf2SScott Long 	sc->aac_qentries[AAC_ADAP_NORM_RESP_QUEUE] =
1968c6eafcf2SScott Long 		&sc->aac_queues->qt_AdapNormRespQueue[0];
1969c6eafcf2SScott Long 	sc->aac_qentries[AAC_ADAP_HIGH_RESP_QUEUE] =
1970c6eafcf2SScott Long 		&sc->aac_queues->qt_AdapHighRespQueue[0];
197135863739SMike Smith 
197235863739SMike Smith 	/*
197335863739SMike Smith 	 * Do controller-type-specific initialisation
197435863739SMike Smith 	 */
197535863739SMike Smith 	switch (sc->aac_hwif) {
197635863739SMike Smith 	case AAC_HWIF_I960RX:
1977ff0991c4SAttilio Rao 		AAC_MEM0_SETREG4(sc, AAC_RX_ODBR, ~0);
197835863739SMike Smith 		break;
19794afedc31SScott Long 	case AAC_HWIF_RKT:
1980ff0991c4SAttilio Rao 		AAC_MEM0_SETREG4(sc, AAC_RKT_ODBR, ~0);
19814afedc31SScott Long 		break;
19824afedc31SScott Long 	default:
19834afedc31SScott Long 		break;
198435863739SMike Smith 	}
198535863739SMike Smith 
198635863739SMike Smith 	/*
198735863739SMike Smith 	 * Give the init structure to the controller.
198835863739SMike Smith 	 */
198935863739SMike Smith 	if (aac_sync_command(sc, AAC_MONKER_INITSTRUCT,
1990914da7d0SScott Long 			     sc->aac_common_busaddr +
1991914da7d0SScott Long 			     offsetof(struct aac_common, ac_init), 0, 0, 0,
1992914da7d0SScott Long 			     NULL)) {
1993914da7d0SScott Long 		device_printf(sc->aac_dev,
1994914da7d0SScott Long 			      "error establishing init structure\n");
1995a6d35632SScott Long 		error = EIO;
1996a6d35632SScott Long 		goto out;
199735863739SMike Smith 	}
199835863739SMike Smith 
1999a6d35632SScott Long 	error = 0;
2000a6d35632SScott Long out:
2001a6d35632SScott Long 	return(error);
200235863739SMike Smith }
200335863739SMike Smith 
200404f4d586SEd Maste static int
200504f4d586SEd Maste aac_setup_intr(struct aac_softc *sc)
200604f4d586SEd Maste {
2007da4882c2SMarius Strobl 
200804f4d586SEd Maste 	if (sc->flags & AAC_FLAGS_NEW_COMM) {
200904f4d586SEd Maste 		if (bus_setup_intr(sc->aac_dev, sc->aac_irq,
201004f4d586SEd Maste 				   INTR_MPSAFE|INTR_TYPE_BIO, NULL,
201104f4d586SEd Maste 				   aac_new_intr, sc, &sc->aac_intr)) {
201204f4d586SEd Maste 			device_printf(sc->aac_dev, "can't set up interrupt\n");
201304f4d586SEd Maste 			return (EINVAL);
201404f4d586SEd Maste 		}
201504f4d586SEd Maste 	} else {
201604f4d586SEd Maste 		if (bus_setup_intr(sc->aac_dev, sc->aac_irq,
2017e46b9eeaSEd Maste 				   INTR_TYPE_BIO, aac_filter, NULL,
201804f4d586SEd Maste 				   sc, &sc->aac_intr)) {
201904f4d586SEd Maste 			device_printf(sc->aac_dev,
2020e46b9eeaSEd Maste 				      "can't set up interrupt filter\n");
202104f4d586SEd Maste 			return (EINVAL);
202204f4d586SEd Maste 		}
202304f4d586SEd Maste 	}
202404f4d586SEd Maste 	return (0);
202504f4d586SEd Maste }
202604f4d586SEd Maste 
2027914da7d0SScott Long /*
202835863739SMike Smith  * Send a synchronous command to the controller and wait for a result.
20297cb209f5SScott Long  * Indicate if the controller completed the command with an error status.
203035863739SMike Smith  */
203135863739SMike Smith static int
203235863739SMike Smith aac_sync_command(struct aac_softc *sc, u_int32_t command,
203335863739SMike Smith 		 u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3,
203435863739SMike Smith 		 u_int32_t *sp)
203535863739SMike Smith {
203635863739SMike Smith 	time_t then;
203735863739SMike Smith 	u_int32_t status;
203835863739SMike Smith 
203931a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
204035863739SMike Smith 
204135863739SMike Smith 	/* populate the mailbox */
204235863739SMike Smith 	AAC_SET_MAILBOX(sc, command, arg0, arg1, arg2, arg3);
204335863739SMike Smith 
204435863739SMike Smith 	/* ensure the sync command doorbell flag is cleared */
204535863739SMike Smith 	AAC_CLEAR_ISTATUS(sc, AAC_DB_SYNC_COMMAND);
204635863739SMike Smith 
204735863739SMike Smith 	/* then set it to signal the adapter */
204835863739SMike Smith 	AAC_QNOTIFY(sc, AAC_DB_SYNC_COMMAND);
204935863739SMike Smith 
205035863739SMike Smith 	/* spin waiting for the command to complete */
20512b3b0f17SScott Long 	then = time_uptime;
205235863739SMike Smith 	do {
20532b3b0f17SScott Long 		if (time_uptime > (then + AAC_IMMEDIATE_TIMEOUT)) {
205431a0399eSEd Maste 			fwprintf(sc, HBA_FLAGS_DBG_ERROR_B, "timed out");
205535863739SMike Smith 			return(EIO);
205635863739SMike Smith 		}
205735863739SMike Smith 	} while (!(AAC_GET_ISTATUS(sc) & AAC_DB_SYNC_COMMAND));
205835863739SMike Smith 
205935863739SMike Smith 	/* clear the completion flag */
206035863739SMike Smith 	AAC_CLEAR_ISTATUS(sc, AAC_DB_SYNC_COMMAND);
206135863739SMike Smith 
206235863739SMike Smith 	/* get the command status */
2063a6d35632SScott Long 	status = AAC_GET_MAILBOX(sc, 0);
206435863739SMike Smith 	if (sp != NULL)
206535863739SMike Smith 		*sp = status;
20667cb209f5SScott Long 
2067a441b3fcSScott Long 	if (status != AAC_SRB_STS_SUCCESS)
20687cb209f5SScott Long 		return (-1);
20690b94a66eSMike Smith 	return(0);
207035863739SMike Smith }
207135863739SMike Smith 
2072cbfd045bSScott Long int
207335863739SMike Smith aac_sync_fib(struct aac_softc *sc, u_int32_t command, u_int32_t xferstate,
2074cbfd045bSScott Long 		 struct aac_fib *fib, u_int16_t datasize)
207535863739SMike Smith {
207631a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
20777cb209f5SScott Long 	mtx_assert(&sc->aac_io_lock, MA_OWNED);
207835863739SMike Smith 
207935863739SMike Smith 	if (datasize > AAC_FIB_DATASIZE)
208035863739SMike Smith 		return(EINVAL);
208135863739SMike Smith 
208235863739SMike Smith 	/*
208335863739SMike Smith 	 * Set up the sync FIB
208435863739SMike Smith 	 */
2085914da7d0SScott Long 	fib->Header.XferState = AAC_FIBSTATE_HOSTOWNED |
2086914da7d0SScott Long 				AAC_FIBSTATE_INITIALISED |
2087c6eafcf2SScott Long 				AAC_FIBSTATE_EMPTY;
208835863739SMike Smith 	fib->Header.XferState |= xferstate;
208935863739SMike Smith 	fib->Header.Command = command;
209035863739SMike Smith 	fib->Header.StructType = AAC_FIBTYPE_TFIB;
209142ef13a2SEd Maste 	fib->Header.Size = sizeof(struct aac_fib_header) + datasize;
209235863739SMike Smith 	fib->Header.SenderSize = sizeof(struct aac_fib);
2093b88ffdc8SScott Long 	fib->Header.SenderFibAddress = 0;	/* Not needed */
2094c6eafcf2SScott Long 	fib->Header.ReceiverFibAddress = sc->aac_common_busaddr +
2095914da7d0SScott Long 					 offsetof(struct aac_common,
2096914da7d0SScott Long 						  ac_sync_fib);
209735863739SMike Smith 
209835863739SMike Smith 	/*
209935863739SMike Smith 	 * Give the FIB to the controller, wait for a response.
210035863739SMike Smith 	 */
2101914da7d0SScott Long 	if (aac_sync_command(sc, AAC_MONKER_SYNCFIB,
2102914da7d0SScott Long 			     fib->Header.ReceiverFibAddress, 0, 0, 0, NULL)) {
210331a0399eSEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_ERROR_B, "IO error");
210435863739SMike Smith 		return(EIO);
210535863739SMike Smith 	}
210635863739SMike Smith 
210735863739SMike Smith 	return (0);
210835863739SMike Smith }
210935863739SMike Smith 
2110914da7d0SScott Long /*
211135863739SMike Smith  * Adapter-space FIB queue manipulation
211235863739SMike Smith  *
211335863739SMike Smith  * Note that the queue implementation here is a little funky; neither the PI or
211435863739SMike Smith  * CI will ever be zero.  This behaviour is a controller feature.
211535863739SMike Smith  */
2116da4882c2SMarius Strobl static const struct {
211735863739SMike Smith 	int		size;
211835863739SMike Smith 	int		notify;
211935863739SMike Smith } aac_qinfo[] = {
212035863739SMike Smith 	{AAC_HOST_NORM_CMD_ENTRIES, AAC_DB_COMMAND_NOT_FULL},
212135863739SMike Smith 	{AAC_HOST_HIGH_CMD_ENTRIES, 0},
212235863739SMike Smith 	{AAC_ADAP_NORM_CMD_ENTRIES, AAC_DB_COMMAND_READY},
212335863739SMike Smith 	{AAC_ADAP_HIGH_CMD_ENTRIES, 0},
212435863739SMike Smith 	{AAC_HOST_NORM_RESP_ENTRIES, AAC_DB_RESPONSE_NOT_FULL},
212535863739SMike Smith 	{AAC_HOST_HIGH_RESP_ENTRIES, 0},
212635863739SMike Smith 	{AAC_ADAP_NORM_RESP_ENTRIES, AAC_DB_RESPONSE_READY},
212735863739SMike Smith 	{AAC_ADAP_HIGH_RESP_ENTRIES, 0}
212835863739SMike Smith };
212935863739SMike Smith 
213035863739SMike Smith /*
2131c6eafcf2SScott Long  * Atomically insert an entry into the nominated queue, returns 0 on success or
2132c6eafcf2SScott Long  * EBUSY if the queue is full.
213335863739SMike Smith  *
21340b94a66eSMike Smith  * Note: it would be more efficient to defer notifying the controller in
2135914da7d0SScott Long  *	 the case where we may be inserting several entries in rapid succession,
2136914da7d0SScott Long  *	 but implementing this usefully may be difficult (it would involve a
2137c6eafcf2SScott Long  *	 separate queue/notify interface).
213835863739SMike Smith  */
213935863739SMike Smith static int
2140f6c4dd3fSScott Long aac_enqueue_fib(struct aac_softc *sc, int queue, struct aac_command *cm)
214135863739SMike Smith {
214235863739SMike Smith 	u_int32_t pi, ci;
21439e2e96d8SScott Long 	int error;
2144f6c4dd3fSScott Long 	u_int32_t fib_size;
2145f6c4dd3fSScott Long 	u_int32_t fib_addr;
2146f6c4dd3fSScott Long 
214731a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
214836e0bf6eSScott Long 
2149f6c4dd3fSScott Long 	fib_size = cm->cm_fib->Header.Size;
2150f6c4dd3fSScott Long 	fib_addr = cm->cm_fib->Header.ReceiverFibAddress;
215135863739SMike Smith 
215235863739SMike Smith 	/* get the producer/consumer indices */
215335863739SMike Smith 	pi = sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX];
215435863739SMike Smith 	ci = sc->aac_queues->qt_qindex[queue][AAC_CONSUMER_INDEX];
215535863739SMike Smith 
215635863739SMike Smith 	/* wrap the queue? */
215735863739SMike Smith 	if (pi >= aac_qinfo[queue].size)
215835863739SMike Smith 		pi = 0;
215935863739SMike Smith 
216035863739SMike Smith 	/* check for queue full */
216135863739SMike Smith 	if ((pi + 1) == ci) {
216235863739SMike Smith 		error = EBUSY;
216335863739SMike Smith 		goto out;
216435863739SMike Smith 	}
216535863739SMike Smith 
2166614c22b2SScott Long 	/*
2167614c22b2SScott Long 	 * To avoid a race with its completion interrupt, place this command on
2168614c22b2SScott Long 	 * the busy queue prior to advertising it to the controller.
2169614c22b2SScott Long 	 */
2170614c22b2SScott Long 	aac_enqueue_busy(cm);
2171614c22b2SScott Long 
217235863739SMike Smith 	/* populate queue entry */
217335863739SMike Smith 	(sc->aac_qentries[queue] + pi)->aq_fib_size = fib_size;
217435863739SMike Smith 	(sc->aac_qentries[queue] + pi)->aq_fib_addr = fib_addr;
217535863739SMike Smith 
217635863739SMike Smith 	/* update producer index */
217735863739SMike Smith 	sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX] = pi + 1;
217835863739SMike Smith 
217935863739SMike Smith 	/* notify the adapter if we know how */
218035863739SMike Smith 	if (aac_qinfo[queue].notify != 0)
218135863739SMike Smith 		AAC_QNOTIFY(sc, aac_qinfo[queue].notify);
218235863739SMike Smith 
218335863739SMike Smith 	error = 0;
218435863739SMike Smith 
218535863739SMike Smith out:
218635863739SMike Smith 	return(error);
218735863739SMike Smith }
218835863739SMike Smith 
218935863739SMike Smith /*
219036e0bf6eSScott Long  * Atomically remove one entry from the nominated queue, returns 0 on
219136e0bf6eSScott Long  * success or ENOENT if the queue is empty.
219235863739SMike Smith  */
219335863739SMike Smith static int
2194c6eafcf2SScott Long aac_dequeue_fib(struct aac_softc *sc, int queue, u_int32_t *fib_size,
2195c6eafcf2SScott Long 		struct aac_fib **fib_addr)
219635863739SMike Smith {
219735863739SMike Smith 	u_int32_t pi, ci;
2198149af931SScott Long 	u_int32_t fib_index;
21999e2e96d8SScott Long 	int error;
2200f6c4dd3fSScott Long 	int notify;
220135863739SMike Smith 
220231a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
220335863739SMike Smith 
220435863739SMike Smith 	/* get the producer/consumer indices */
220535863739SMike Smith 	pi = sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX];
220635863739SMike Smith 	ci = sc->aac_queues->qt_qindex[queue][AAC_CONSUMER_INDEX];
220735863739SMike Smith 
220835863739SMike Smith 	/* check for queue empty */
220935863739SMike Smith 	if (ci == pi) {
221035863739SMike Smith 		error = ENOENT;
221135863739SMike Smith 		goto out;
221235863739SMike Smith 	}
221335863739SMike Smith 
22147753acd2SScott Long 	/* wrap the pi so the following test works */
22157753acd2SScott Long 	if (pi >= aac_qinfo[queue].size)
22167753acd2SScott Long 		pi = 0;
22177753acd2SScott Long 
2218f6c4dd3fSScott Long 	notify = 0;
2219f6c4dd3fSScott Long 	if (ci == pi + 1)
2220f6c4dd3fSScott Long 		notify++;
2221f6c4dd3fSScott Long 
222235863739SMike Smith 	/* wrap the queue? */
222335863739SMike Smith 	if (ci >= aac_qinfo[queue].size)
222435863739SMike Smith 		ci = 0;
222535863739SMike Smith 
222635863739SMike Smith 	/* fetch the entry */
222735863739SMike Smith 	*fib_size = (sc->aac_qentries[queue] + ci)->aq_fib_size;
2228149af931SScott Long 
2229149af931SScott Long 	switch (queue) {
2230149af931SScott Long 	case AAC_HOST_NORM_CMD_QUEUE:
2231149af931SScott Long 	case AAC_HOST_HIGH_CMD_QUEUE:
2232149af931SScott Long 		/*
2233149af931SScott Long 		 * The aq_fib_addr is only 32 bits wide so it can't be counted
2234149af931SScott Long 		 * on to hold an address.  For AIF's, the adapter assumes
2235149af931SScott Long 		 * that it's giving us an address into the array of AIF fibs.
2236149af931SScott Long 		 * Therefore, we have to convert it to an index.
2237149af931SScott Long 		 */
2238149af931SScott Long 		fib_index = (sc->aac_qentries[queue] + ci)->aq_fib_addr /
2239149af931SScott Long 			sizeof(struct aac_fib);
2240149af931SScott Long 		*fib_addr = &sc->aac_common->ac_fibs[fib_index];
2241149af931SScott Long 		break;
2242149af931SScott Long 
2243149af931SScott Long 	case AAC_HOST_NORM_RESP_QUEUE:
2244149af931SScott Long 	case AAC_HOST_HIGH_RESP_QUEUE:
2245149af931SScott Long 	{
2246149af931SScott Long 		struct aac_command *cm;
2247149af931SScott Long 
2248149af931SScott Long 		/*
2249149af931SScott Long 		 * As above, an index is used instead of an actual address.
2250149af931SScott Long 		 * Gotta shift the index to account for the fast response
2251149af931SScott Long 		 * bit.  No other correction is needed since this value was
2252149af931SScott Long 		 * originally provided by the driver via the SenderFibAddress
2253149af931SScott Long 		 * field.
2254149af931SScott Long 		 */
2255149af931SScott Long 		fib_index = (sc->aac_qentries[queue] + ci)->aq_fib_addr;
22567cb209f5SScott Long 		cm = sc->aac_commands + (fib_index >> 2);
2257149af931SScott Long 		*fib_addr = cm->cm_fib;
225835863739SMike Smith 
2259f30ac74cSScott Long 		/*
2260f30ac74cSScott Long 		 * Is this a fast response? If it is, update the fib fields in
2261149af931SScott Long 		 * local memory since the whole fib isn't DMA'd back up.
2262f30ac74cSScott Long 		 */
2263149af931SScott Long 		if (fib_index & 0x01) {
2264f30ac74cSScott Long 			(*fib_addr)->Header.XferState |= AAC_FIBSTATE_DONEADAP;
2265f30ac74cSScott Long 			*((u_int32_t*)((*fib_addr)->data)) = AAC_ERROR_NORMAL;
2266f30ac74cSScott Long 		}
2267149af931SScott Long 		break;
2268149af931SScott Long 	}
2269149af931SScott Long 	default:
2270149af931SScott Long 		panic("Invalid queue in aac_dequeue_fib()");
2271149af931SScott Long 		break;
2272149af931SScott Long 	}
2273149af931SScott Long 
227435863739SMike Smith 	/* update consumer index */
227535863739SMike Smith 	sc->aac_queues->qt_qindex[queue][AAC_CONSUMER_INDEX] = ci + 1;
227635863739SMike Smith 
227735863739SMike Smith 	/* if we have made the queue un-full, notify the adapter */
2278f6c4dd3fSScott Long 	if (notify && (aac_qinfo[queue].notify != 0))
227935863739SMike Smith 		AAC_QNOTIFY(sc, aac_qinfo[queue].notify);
228035863739SMike Smith 	error = 0;
228135863739SMike Smith 
228235863739SMike Smith out:
228335863739SMike Smith 	return(error);
228435863739SMike Smith }
228535863739SMike Smith 
2286914da7d0SScott Long /*
228736e0bf6eSScott Long  * Put our response to an Adapter Initialed Fib on the response queue
228836e0bf6eSScott Long  */
228936e0bf6eSScott Long static int
229036e0bf6eSScott Long aac_enqueue_response(struct aac_softc *sc, int queue, struct aac_fib *fib)
229136e0bf6eSScott Long {
229236e0bf6eSScott Long 	u_int32_t pi, ci;
22939e2e96d8SScott Long 	int error;
229436e0bf6eSScott Long 	u_int32_t fib_size;
229536e0bf6eSScott Long 	u_int32_t fib_addr;
229636e0bf6eSScott Long 
229731a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
229836e0bf6eSScott Long 
229936e0bf6eSScott Long 	/* Tell the adapter where the FIB is */
230036e0bf6eSScott Long 	fib_size = fib->Header.Size;
230136e0bf6eSScott Long 	fib_addr = fib->Header.SenderFibAddress;
230236e0bf6eSScott Long 	fib->Header.ReceiverFibAddress = fib_addr;
230336e0bf6eSScott Long 
230436e0bf6eSScott Long 	/* get the producer/consumer indices */
230536e0bf6eSScott Long 	pi = sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX];
230636e0bf6eSScott Long 	ci = sc->aac_queues->qt_qindex[queue][AAC_CONSUMER_INDEX];
230736e0bf6eSScott Long 
230836e0bf6eSScott Long 	/* wrap the queue? */
230936e0bf6eSScott Long 	if (pi >= aac_qinfo[queue].size)
231036e0bf6eSScott Long 		pi = 0;
231136e0bf6eSScott Long 
231236e0bf6eSScott Long 	/* check for queue full */
231336e0bf6eSScott Long 	if ((pi + 1) == ci) {
231436e0bf6eSScott Long 		error = EBUSY;
231536e0bf6eSScott Long 		goto out;
231636e0bf6eSScott Long 	}
231736e0bf6eSScott Long 
231836e0bf6eSScott Long 	/* populate queue entry */
231936e0bf6eSScott Long 	(sc->aac_qentries[queue] + pi)->aq_fib_size = fib_size;
232036e0bf6eSScott Long 	(sc->aac_qentries[queue] + pi)->aq_fib_addr = fib_addr;
232136e0bf6eSScott Long 
232236e0bf6eSScott Long 	/* update producer index */
232336e0bf6eSScott Long 	sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX] = pi + 1;
232436e0bf6eSScott Long 
232536e0bf6eSScott Long 	/* notify the adapter if we know how */
232636e0bf6eSScott Long 	if (aac_qinfo[queue].notify != 0)
232736e0bf6eSScott Long 		AAC_QNOTIFY(sc, aac_qinfo[queue].notify);
232836e0bf6eSScott Long 
232936e0bf6eSScott Long 	error = 0;
233036e0bf6eSScott Long 
233136e0bf6eSScott Long out:
233236e0bf6eSScott Long 	return(error);
233336e0bf6eSScott Long }
233436e0bf6eSScott Long 
2335914da7d0SScott Long /*
23360b94a66eSMike Smith  * Check for commands that have been outstanding for a suspiciously long time,
23370b94a66eSMike Smith  * and complain about them.
23380b94a66eSMike Smith  */
23390b94a66eSMike Smith static void
23400b94a66eSMike Smith aac_timeout(struct aac_softc *sc)
23410b94a66eSMike Smith {
23420b94a66eSMike Smith 	struct aac_command *cm;
23430b94a66eSMike Smith 	time_t deadline;
234415c37be0SScott Long 	int timedout, code;
23450b94a66eSMike Smith 
2346f6c4dd3fSScott Long 	/*
234770545d1aSScott Long 	 * Traverse the busy command list, bitch about late commands once
2348914da7d0SScott Long 	 * only.
2349914da7d0SScott Long 	 */
235015c37be0SScott Long 	timedout = 0;
23512b3b0f17SScott Long 	deadline = time_uptime - AAC_CMD_TIMEOUT;
23520b94a66eSMike Smith 	TAILQ_FOREACH(cm, &sc->aac_busy, cm_link) {
2353f6c4dd3fSScott Long 		if ((cm->cm_timestamp  < deadline)
23543e507710SEd Maste 		    && !(cm->cm_flags & AAC_CMD_TIMEDOUT)) {
23550b94a66eSMike Smith 			cm->cm_flags |= AAC_CMD_TIMEDOUT;
2356914da7d0SScott Long 			device_printf(sc->aac_dev,
23575aa4bb5bSEd Maste 			    "COMMAND %p (TYPE %d) TIMEOUT AFTER %d SECONDS\n",
23585aa4bb5bSEd Maste 			    cm, cm->cm_fib->Header.Command,
23595aa4bb5bSEd Maste 			    (int)(time_uptime-cm->cm_timestamp));
23600b94a66eSMike Smith 			AAC_PRINT_FIB(sc, cm->cm_fib);
236115c37be0SScott Long 			timedout++;
23620b94a66eSMike Smith 		}
23630b94a66eSMike Smith 	}
23640b94a66eSMike Smith 
236515c37be0SScott Long 	if (timedout) {
236615c37be0SScott Long 		code = AAC_GET_FWSTATUS(sc);
236715c37be0SScott Long 		if (code != AAC_UP_AND_RUNNING) {
236815c37be0SScott Long 			device_printf(sc->aac_dev, "WARNING! Controller is no "
236915c37be0SScott Long 				      "longer running! code= 0x%x\n", code);
237015c37be0SScott Long 		}
237115c37be0SScott Long 	}
23720b94a66eSMike Smith }
23730b94a66eSMike Smith 
2374914da7d0SScott Long /*
2375914da7d0SScott Long  * Interface Function Vectors
2376914da7d0SScott Long  */
237735863739SMike Smith 
2378914da7d0SScott Long /*
237935863739SMike Smith  * Read the current firmware status word.
238035863739SMike Smith  */
238135863739SMike Smith static int
238235863739SMike Smith aac_sa_get_fwstatus(struct aac_softc *sc)
238335863739SMike Smith {
238431a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
238535863739SMike Smith 
2386ff0991c4SAttilio Rao 	return(AAC_MEM0_GETREG4(sc, AAC_SA_FWSTATUS));
238735863739SMike Smith }
238835863739SMike Smith 
238935863739SMike Smith static int
239035863739SMike Smith aac_rx_get_fwstatus(struct aac_softc *sc)
239135863739SMike Smith {
239231a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
239335863739SMike Smith 
23944824be88SEd Maste 	return(AAC_MEM0_GETREG4(sc, sc->flags & AAC_FLAGS_NEW_COMM ?
23954824be88SEd Maste 	    AAC_RX_OMR0 : AAC_RX_FWSTATUS));
239635863739SMike Smith }
239735863739SMike Smith 
2398b3457b51SScott Long static int
23994afedc31SScott Long aac_rkt_get_fwstatus(struct aac_softc *sc)
24004afedc31SScott Long {
240131a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
24024afedc31SScott Long 
24034824be88SEd Maste 	return(AAC_MEM0_GETREG4(sc, sc->flags & AAC_FLAGS_NEW_COMM ?
24044824be88SEd Maste 	    AAC_RKT_OMR0 : AAC_RKT_FWSTATUS));
24054afedc31SScott Long }
24064afedc31SScott Long 
2407914da7d0SScott Long /*
240835863739SMike Smith  * Notify the controller of a change in a given queue
240935863739SMike Smith  */
241035863739SMike Smith 
241135863739SMike Smith static void
241235863739SMike Smith aac_sa_qnotify(struct aac_softc *sc, int qbit)
241335863739SMike Smith {
241431a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
241535863739SMike Smith 
2416ff0991c4SAttilio Rao 	AAC_MEM0_SETREG2(sc, AAC_SA_DOORBELL1_SET, qbit);
241735863739SMike Smith }
241835863739SMike Smith 
241935863739SMike Smith static void
242035863739SMike Smith aac_rx_qnotify(struct aac_softc *sc, int qbit)
242135863739SMike Smith {
242231a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
242335863739SMike Smith 
2424ff0991c4SAttilio Rao 	AAC_MEM0_SETREG4(sc, AAC_RX_IDBR, qbit);
242535863739SMike Smith }
242635863739SMike Smith 
2427b3457b51SScott Long static void
24284afedc31SScott Long aac_rkt_qnotify(struct aac_softc *sc, int qbit)
24294afedc31SScott Long {
243031a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
24314afedc31SScott Long 
2432ff0991c4SAttilio Rao 	AAC_MEM0_SETREG4(sc, AAC_RKT_IDBR, qbit);
24334afedc31SScott Long }
24344afedc31SScott Long 
2435914da7d0SScott Long /*
243635863739SMike Smith  * Get the interrupt reason bits
243735863739SMike Smith  */
243835863739SMike Smith static int
243935863739SMike Smith aac_sa_get_istatus(struct aac_softc *sc)
244035863739SMike Smith {
244131a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
244235863739SMike Smith 
2443ff0991c4SAttilio Rao 	return(AAC_MEM0_GETREG2(sc, AAC_SA_DOORBELL0));
244435863739SMike Smith }
244535863739SMike Smith 
244635863739SMike Smith static int
244735863739SMike Smith aac_rx_get_istatus(struct aac_softc *sc)
244835863739SMike Smith {
244931a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
245035863739SMike Smith 
2451ff0991c4SAttilio Rao 	return(AAC_MEM0_GETREG4(sc, AAC_RX_ODBR));
245235863739SMike Smith }
245335863739SMike Smith 
2454b3457b51SScott Long static int
24554afedc31SScott Long aac_rkt_get_istatus(struct aac_softc *sc)
24564afedc31SScott Long {
245731a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
24584afedc31SScott Long 
2459ff0991c4SAttilio Rao 	return(AAC_MEM0_GETREG4(sc, AAC_RKT_ODBR));
24604afedc31SScott Long }
24614afedc31SScott Long 
2462914da7d0SScott Long /*
246335863739SMike Smith  * Clear some interrupt reason bits
246435863739SMike Smith  */
246535863739SMike Smith static void
246635863739SMike Smith aac_sa_clear_istatus(struct aac_softc *sc, int mask)
246735863739SMike Smith {
246831a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
246935863739SMike Smith 
2470ff0991c4SAttilio Rao 	AAC_MEM0_SETREG2(sc, AAC_SA_DOORBELL0_CLEAR, mask);
247135863739SMike Smith }
247235863739SMike Smith 
247335863739SMike Smith static void
247435863739SMike Smith aac_rx_clear_istatus(struct aac_softc *sc, int mask)
247535863739SMike Smith {
247631a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
247735863739SMike Smith 
2478ff0991c4SAttilio Rao 	AAC_MEM0_SETREG4(sc, AAC_RX_ODBR, mask);
247935863739SMike Smith }
248035863739SMike Smith 
2481b3457b51SScott Long static void
24824afedc31SScott Long aac_rkt_clear_istatus(struct aac_softc *sc, int mask)
24834afedc31SScott Long {
248431a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
24854afedc31SScott Long 
2486ff0991c4SAttilio Rao 	AAC_MEM0_SETREG4(sc, AAC_RKT_ODBR, mask);
24874afedc31SScott Long }
24884afedc31SScott Long 
2489914da7d0SScott Long /*
249035863739SMike Smith  * Populate the mailbox and set the command word
249135863739SMike Smith  */
249235863739SMike Smith static void
249335863739SMike Smith aac_sa_set_mailbox(struct aac_softc *sc, u_int32_t command,
249435863739SMike Smith 		u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3)
249535863739SMike Smith {
249631a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
249735863739SMike Smith 
2498ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, AAC_SA_MAILBOX, command);
2499ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, AAC_SA_MAILBOX + 4, arg0);
2500ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, AAC_SA_MAILBOX + 8, arg1);
2501ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, AAC_SA_MAILBOX + 12, arg2);
2502ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, AAC_SA_MAILBOX + 16, arg3);
250335863739SMike Smith }
250435863739SMike Smith 
250535863739SMike Smith static void
250635863739SMike Smith aac_rx_set_mailbox(struct aac_softc *sc, u_int32_t command,
250735863739SMike Smith 		u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3)
250835863739SMike Smith {
250931a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
251035863739SMike Smith 
2511ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, AAC_RX_MAILBOX, command);
2512ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, AAC_RX_MAILBOX + 4, arg0);
2513ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, AAC_RX_MAILBOX + 8, arg1);
2514ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, AAC_RX_MAILBOX + 12, arg2);
2515ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, AAC_RX_MAILBOX + 16, arg3);
251635863739SMike Smith }
251735863739SMike Smith 
2518b3457b51SScott Long static void
25194afedc31SScott Long aac_rkt_set_mailbox(struct aac_softc *sc, u_int32_t command, u_int32_t arg0,
25204afedc31SScott Long 		    u_int32_t arg1, u_int32_t arg2, u_int32_t arg3)
25214afedc31SScott Long {
252231a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
25234afedc31SScott Long 
2524ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, AAC_RKT_MAILBOX, command);
2525ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, AAC_RKT_MAILBOX + 4, arg0);
2526ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, AAC_RKT_MAILBOX + 8, arg1);
2527ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, AAC_RKT_MAILBOX + 12, arg2);
2528ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, AAC_RKT_MAILBOX + 16, arg3);
25294afedc31SScott Long }
25304afedc31SScott Long 
2531914da7d0SScott Long /*
253235863739SMike Smith  * Fetch the immediate command status word
253335863739SMike Smith  */
253435863739SMike Smith static int
2535a6d35632SScott Long aac_sa_get_mailbox(struct aac_softc *sc, int mb)
253635863739SMike Smith {
253731a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
253835863739SMike Smith 
2539ff0991c4SAttilio Rao 	return(AAC_MEM1_GETREG4(sc, AAC_SA_MAILBOX + (mb * 4)));
254035863739SMike Smith }
254135863739SMike Smith 
254235863739SMike Smith static int
2543a6d35632SScott Long aac_rx_get_mailbox(struct aac_softc *sc, int mb)
254435863739SMike Smith {
254531a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
254635863739SMike Smith 
2547ff0991c4SAttilio Rao 	return(AAC_MEM1_GETREG4(sc, AAC_RX_MAILBOX + (mb * 4)));
254835863739SMike Smith }
254935863739SMike Smith 
2550b3457b51SScott Long static int
25514afedc31SScott Long aac_rkt_get_mailbox(struct aac_softc *sc, int mb)
25524afedc31SScott Long {
255331a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
25544afedc31SScott Long 
2555ff0991c4SAttilio Rao 	return(AAC_MEM1_GETREG4(sc, AAC_RKT_MAILBOX + (mb * 4)));
25564afedc31SScott Long }
25574afedc31SScott Long 
2558914da7d0SScott Long /*
255935863739SMike Smith  * Set/clear interrupt masks
256035863739SMike Smith  */
256135863739SMike Smith static void
256235863739SMike Smith aac_sa_set_interrupts(struct aac_softc *sc, int enable)
256335863739SMike Smith {
256431a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "%sable interrupts", enable ? "en" : "dis");
256535863739SMike Smith 
256635863739SMike Smith 	if (enable) {
2567ff0991c4SAttilio Rao 		AAC_MEM0_SETREG2((sc), AAC_SA_MASK0_CLEAR, AAC_DB_INTERRUPTS);
256835863739SMike Smith 	} else {
2569ff0991c4SAttilio Rao 		AAC_MEM0_SETREG2((sc), AAC_SA_MASK0_SET, ~0);
257035863739SMike Smith 	}
257135863739SMike Smith }
257235863739SMike Smith 
257335863739SMike Smith static void
257435863739SMike Smith aac_rx_set_interrupts(struct aac_softc *sc, int enable)
257535863739SMike Smith {
257631a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "%sable interrupts", enable ? "en" : "dis");
257735863739SMike Smith 
257835863739SMike Smith 	if (enable) {
25797cb209f5SScott Long 		if (sc->flags & AAC_FLAGS_NEW_COMM)
2580ff0991c4SAttilio Rao 			AAC_MEM0_SETREG4(sc, AAC_RX_OIMR, ~AAC_DB_INT_NEW_COMM);
25817cb209f5SScott Long 		else
2582ff0991c4SAttilio Rao 			AAC_MEM0_SETREG4(sc, AAC_RX_OIMR, ~AAC_DB_INTERRUPTS);
258335863739SMike Smith 	} else {
2584ff0991c4SAttilio Rao 		AAC_MEM0_SETREG4(sc, AAC_RX_OIMR, ~0);
258535863739SMike Smith 	}
258635863739SMike Smith }
258735863739SMike Smith 
2588b3457b51SScott Long static void
25894afedc31SScott Long aac_rkt_set_interrupts(struct aac_softc *sc, int enable)
25904afedc31SScott Long {
259131a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "%sable interrupts", enable ? "en" : "dis");
25924afedc31SScott Long 
25934afedc31SScott Long 	if (enable) {
25947cb209f5SScott Long 		if (sc->flags & AAC_FLAGS_NEW_COMM)
2595ff0991c4SAttilio Rao 			AAC_MEM0_SETREG4(sc, AAC_RKT_OIMR, ~AAC_DB_INT_NEW_COMM);
25967cb209f5SScott Long 		else
2597ff0991c4SAttilio Rao 			AAC_MEM0_SETREG4(sc, AAC_RKT_OIMR, ~AAC_DB_INTERRUPTS);
25984afedc31SScott Long 	} else {
2599ff0991c4SAttilio Rao 		AAC_MEM0_SETREG4(sc, AAC_RKT_OIMR, ~0);
26004afedc31SScott Long 	}
26014afedc31SScott Long }
26024afedc31SScott Long 
2603914da7d0SScott Long /*
26047cb209f5SScott Long  * New comm. interface: Send command functions
26057cb209f5SScott Long  */
26067cb209f5SScott Long static int
26077cb209f5SScott Long aac_rx_send_command(struct aac_softc *sc, struct aac_command *cm)
26087cb209f5SScott Long {
26097cb209f5SScott Long 	u_int32_t index, device;
26107cb209f5SScott Long 
261131a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "send command (new comm.)");
26127cb209f5SScott Long 
2613ff0991c4SAttilio Rao 	index = AAC_MEM0_GETREG4(sc, AAC_RX_IQUE);
26147cb209f5SScott Long 	if (index == 0xffffffffL)
2615ff0991c4SAttilio Rao 		index = AAC_MEM0_GETREG4(sc, AAC_RX_IQUE);
26167cb209f5SScott Long 	if (index == 0xffffffffL)
26177cb209f5SScott Long 		return index;
26187cb209f5SScott Long 	aac_enqueue_busy(cm);
26197cb209f5SScott Long 	device = index;
2620ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, device, (u_int32_t)(cm->cm_fibphys & 0xffffffffUL));
26217cb209f5SScott Long 	device += 4;
2622ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, device, (u_int32_t)(cm->cm_fibphys >> 32));
26237cb209f5SScott Long 	device += 4;
2624ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, device, cm->cm_fib->Header.Size);
2625ff0991c4SAttilio Rao 	AAC_MEM0_SETREG4(sc, AAC_RX_IQUE, index);
26267cb209f5SScott Long 	return 0;
26277cb209f5SScott Long }
26287cb209f5SScott Long 
26297cb209f5SScott Long static int
26307cb209f5SScott Long aac_rkt_send_command(struct aac_softc *sc, struct aac_command *cm)
26317cb209f5SScott Long {
26327cb209f5SScott Long 	u_int32_t index, device;
26337cb209f5SScott Long 
263431a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "send command (new comm.)");
26357cb209f5SScott Long 
2636ff0991c4SAttilio Rao 	index = AAC_MEM0_GETREG4(sc, AAC_RKT_IQUE);
26377cb209f5SScott Long 	if (index == 0xffffffffL)
2638ff0991c4SAttilio Rao 		index = AAC_MEM0_GETREG4(sc, AAC_RKT_IQUE);
26397cb209f5SScott Long 	if (index == 0xffffffffL)
26407cb209f5SScott Long 		return index;
26417cb209f5SScott Long 	aac_enqueue_busy(cm);
26427cb209f5SScott Long 	device = index;
2643ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, device, (u_int32_t)(cm->cm_fibphys & 0xffffffffUL));
26447cb209f5SScott Long 	device += 4;
2645ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, device, (u_int32_t)(cm->cm_fibphys >> 32));
26467cb209f5SScott Long 	device += 4;
2647ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, device, cm->cm_fib->Header.Size);
2648ff0991c4SAttilio Rao 	AAC_MEM0_SETREG4(sc, AAC_RKT_IQUE, index);
26497cb209f5SScott Long 	return 0;
26507cb209f5SScott Long }
26517cb209f5SScott Long 
26527cb209f5SScott Long /*
26537cb209f5SScott Long  * New comm. interface: get, set outbound queue index
26547cb209f5SScott Long  */
26557cb209f5SScott Long static int
26567cb209f5SScott Long aac_rx_get_outb_queue(struct aac_softc *sc)
26577cb209f5SScott Long {
265831a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
26597cb209f5SScott Long 
2660ff0991c4SAttilio Rao 	return(AAC_MEM0_GETREG4(sc, AAC_RX_OQUE));
26617cb209f5SScott Long }
26627cb209f5SScott Long 
26637cb209f5SScott Long static int
26647cb209f5SScott Long aac_rkt_get_outb_queue(struct aac_softc *sc)
26657cb209f5SScott Long {
266631a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
26677cb209f5SScott Long 
2668ff0991c4SAttilio Rao 	return(AAC_MEM0_GETREG4(sc, AAC_RKT_OQUE));
26697cb209f5SScott Long }
26707cb209f5SScott Long 
26717cb209f5SScott Long static void
26727cb209f5SScott Long aac_rx_set_outb_queue(struct aac_softc *sc, int index)
26737cb209f5SScott Long {
267431a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
26757cb209f5SScott Long 
2676ff0991c4SAttilio Rao 	AAC_MEM0_SETREG4(sc, AAC_RX_OQUE, index);
26777cb209f5SScott Long }
26787cb209f5SScott Long 
26797cb209f5SScott Long static void
26807cb209f5SScott Long aac_rkt_set_outb_queue(struct aac_softc *sc, int index)
26817cb209f5SScott Long {
268231a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
26837cb209f5SScott Long 
2684ff0991c4SAttilio Rao 	AAC_MEM0_SETREG4(sc, AAC_RKT_OQUE, index);
26857cb209f5SScott Long }
26867cb209f5SScott Long 
26877cb209f5SScott Long /*
2688914da7d0SScott Long  * Debugging and Diagnostics
2689914da7d0SScott Long  */
269035863739SMike Smith 
2691914da7d0SScott Long /*
269235863739SMike Smith  * Print some information about the controller.
269335863739SMike Smith  */
269435863739SMike Smith static void
269535863739SMike Smith aac_describe_controller(struct aac_softc *sc)
269635863739SMike Smith {
2697cbfd045bSScott Long 	struct aac_fib *fib;
269835863739SMike Smith 	struct aac_adapter_info	*info;
26997ea2d558SEd Maste 	char *adapter_type = "Adaptec RAID controller";
270035863739SMike Smith 
270131a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
270235863739SMike Smith 
270381b3da08SScott Long 	mtx_lock(&sc->aac_io_lock);
270403b5fe51SScott Long 	aac_alloc_sync_fib(sc, &fib);
2705cbfd045bSScott Long 
2706cbfd045bSScott Long 	fib->data[0] = 0;
2707cbfd045bSScott Long 	if (aac_sync_fib(sc, RequestAdapterInfo, 0, fib, 1)) {
270835863739SMike Smith 		device_printf(sc->aac_dev, "RequestAdapterInfo failed\n");
2709fe3cb0e1SScott Long 		aac_release_sync_fib(sc);
271081b3da08SScott Long 		mtx_unlock(&sc->aac_io_lock);
271135863739SMike Smith 		return;
271235863739SMike Smith 	}
271335863739SMike Smith 
2714bd971c49SScott Long 	/* save the kernel revision structure for later use */
2715bd971c49SScott Long 	info = (struct aac_adapter_info *)&fib->data[0];
2716bd971c49SScott Long 	sc->aac_revision = info->KernelRevision;
2717bd971c49SScott Long 
2718bd971c49SScott Long 	if (bootverbose) {
2719b1c56c68SScott Long 		device_printf(sc->aac_dev, "%s %dMHz, %dMB memory "
2720b1c56c68SScott Long 		    "(%dMB cache, %dMB execution), %s\n",
2721c6eafcf2SScott Long 		    aac_describe_code(aac_cpu_variant, info->CpuVariant),
2722b1c56c68SScott Long 		    info->ClockSpeed, info->TotalMem / (1024 * 1024),
2723b1c56c68SScott Long 		    info->BufferMem / (1024 * 1024),
2724b1c56c68SScott Long 		    info->ExecutionMem / (1024 * 1024),
2725914da7d0SScott Long 		    aac_describe_code(aac_battery_platform,
2726914da7d0SScott Long 		    info->batteryPlatform));
272735863739SMike Smith 
2728bd971c49SScott Long 		device_printf(sc->aac_dev,
2729bd971c49SScott Long 		    "Kernel %d.%d-%d, Build %d, S/N %6X\n",
273035863739SMike Smith 		    info->KernelRevision.external.comp.major,
273135863739SMike Smith 		    info->KernelRevision.external.comp.minor,
273235863739SMike Smith 		    info->KernelRevision.external.comp.dash,
273336e0bf6eSScott Long 		    info->KernelRevision.buildNumber,
273436e0bf6eSScott Long 		    (u_int32_t)(info->SerialNumber & 0xffffff));
2735fe3cb0e1SScott Long 
2736a6d35632SScott Long 		device_printf(sc->aac_dev, "Supported Options=%b\n",
2737a6d35632SScott Long 			      sc->supported_options,
2738a6d35632SScott Long 			      "\20"
2739a6d35632SScott Long 			      "\1SNAPSHOT"
2740a6d35632SScott Long 			      "\2CLUSTERS"
2741a6d35632SScott Long 			      "\3WCACHE"
2742a6d35632SScott Long 			      "\4DATA64"
2743a6d35632SScott Long 			      "\5HOSTTIME"
2744a6d35632SScott Long 			      "\6RAID50"
2745a6d35632SScott Long 			      "\7WINDOW4GB"
2746a6d35632SScott Long 			      "\10SCSIUPGD"
2747a6d35632SScott Long 			      "\11SOFTERR"
2748a6d35632SScott Long 			      "\12NORECOND"
2749a6d35632SScott Long 			      "\13SGMAP64"
2750a6d35632SScott Long 			      "\14ALARM"
27517cb209f5SScott Long 			      "\15NONDASD"
27527cb209f5SScott Long 			      "\16SCSIMGT"
27537cb209f5SScott Long 			      "\17RAIDSCSI"
27547cb209f5SScott Long 			      "\21ADPTINFO"
27557cb209f5SScott Long 			      "\22NEWCOMM"
27567cb209f5SScott Long 			      "\23ARRAY64BIT"
27577cb209f5SScott Long 			      "\24HEATSENSOR");
2758a6d35632SScott Long 	}
275955aa1136SEd Maste 
276055aa1136SEd Maste 	if (sc->supported_options & AAC_SUPPORTED_SUPPLEMENT_ADAPTER_INFO) {
276155aa1136SEd Maste 		fib->data[0] = 0;
276255aa1136SEd Maste 		if (aac_sync_fib(sc, RequestSupplementAdapterInfo, 0, fib, 1))
276355aa1136SEd Maste 			device_printf(sc->aac_dev,
276455aa1136SEd Maste 			    "RequestSupplementAdapterInfo failed\n");
276555aa1136SEd Maste 		else
276655aa1136SEd Maste 			adapter_type = ((struct aac_supplement_adapter_info *)
276755aa1136SEd Maste 			    &fib->data[0])->AdapterTypeText;
276855aa1136SEd Maste 	}
276955aa1136SEd Maste 	device_printf(sc->aac_dev, "%s, aac driver %d.%d.%d-%d\n",
277055aa1136SEd Maste 		adapter_type,
27718e7e6335SEd Maste 		AAC_DRIVER_MAJOR_VERSION, AAC_DRIVER_MINOR_VERSION,
27728e7e6335SEd Maste 		AAC_DRIVER_BUGFIX_LEVEL, AAC_DRIVER_BUILD);
277355aa1136SEd Maste 
2774bd971c49SScott Long 	aac_release_sync_fib(sc);
277581b3da08SScott Long 	mtx_unlock(&sc->aac_io_lock);
277635863739SMike Smith }
277735863739SMike Smith 
2778914da7d0SScott Long /*
277935863739SMike Smith  * Look up a text description of a numeric error code and return a pointer to
278035863739SMike Smith  * same.
278135863739SMike Smith  */
2782da4882c2SMarius Strobl static const char *
2783da4882c2SMarius Strobl aac_describe_code(const struct aac_code_lookup *table, u_int32_t code)
278435863739SMike Smith {
278535863739SMike Smith 	int i;
278635863739SMike Smith 
278735863739SMike Smith 	for (i = 0; table[i].string != NULL; i++)
278835863739SMike Smith 		if (table[i].code == code)
278935863739SMike Smith 			return(table[i].string);
279035863739SMike Smith 	return(table[i + 1].string);
279135863739SMike Smith }
279235863739SMike Smith 
2793914da7d0SScott Long /*
2794914da7d0SScott Long  * Management Interface
2795914da7d0SScott Long  */
279635863739SMike Smith 
279735863739SMike Smith static int
279800b4e54aSWarner Losh aac_open(struct cdev *dev, int flags, int fmt, struct thread *td)
279935863739SMike Smith {
2800914da7d0SScott Long 	struct aac_softc *sc;
280135863739SMike Smith 
2802914da7d0SScott Long 	sc = dev->si_drv1;
280331a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
280404f798ecSAttilio Rao 	device_busy(sc->aac_dev);
2805dfe2c294SAttilio Rao 	devfs_set_cdevpriv(sc, aac_cdevpriv_dtor);
280635863739SMike Smith 
280735863739SMike Smith 	return 0;
280835863739SMike Smith }
280935863739SMike Smith 
281035863739SMike Smith static int
281100b4e54aSWarner Losh aac_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td)
281235863739SMike Smith {
2813914da7d0SScott Long 	union aac_statrequest *as;
2814914da7d0SScott Long 	struct aac_softc *sc;
28150b94a66eSMike Smith 	int error = 0;
281635863739SMike Smith 
2817914da7d0SScott Long 	as = (union aac_statrequest *)arg;
2818914da7d0SScott Long 	sc = dev->si_drv1;
281931a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
2820914da7d0SScott Long 
282135863739SMike Smith 	switch (cmd) {
28220b94a66eSMike Smith 	case AACIO_STATS:
28230b94a66eSMike Smith 		switch (as->as_item) {
28240b94a66eSMike Smith 		case AACQ_FREE:
28250b94a66eSMike Smith 		case AACQ_BIO:
28260b94a66eSMike Smith 		case AACQ_READY:
28270b94a66eSMike Smith 		case AACQ_BUSY:
2828c6eafcf2SScott Long 			bcopy(&sc->aac_qstat[as->as_item], &as->as_qstat,
2829c6eafcf2SScott Long 			      sizeof(struct aac_qstat));
28300b94a66eSMike Smith 			break;
28310b94a66eSMike Smith 		default:
28320b94a66eSMike Smith 			error = ENOENT;
28330b94a66eSMike Smith 			break;
28340b94a66eSMike Smith 		}
28350b94a66eSMike Smith 	break;
28360b94a66eSMike Smith 
283735863739SMike Smith 	case FSACTL_SENDFIB:
2838f355c0e0SEd Maste 	case FSACTL_SEND_LARGE_FIB:
2839fb0c27d7SScott Long 		arg = *(caddr_t*)arg;
2840fb0c27d7SScott Long 	case FSACTL_LNX_SENDFIB:
2841f355c0e0SEd Maste 	case FSACTL_LNX_SEND_LARGE_FIB:
284231a0399eSEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_SENDFIB");
284335863739SMike Smith 		error = aac_ioctl_sendfib(sc, arg);
284435863739SMike Smith 		break;
2845f355c0e0SEd Maste 	case FSACTL_SEND_RAW_SRB:
2846f355c0e0SEd Maste 		arg = *(caddr_t*)arg;
2847f355c0e0SEd Maste 	case FSACTL_LNX_SEND_RAW_SRB:
284831a0399eSEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_SEND_RAW_SRB");
2849f355c0e0SEd Maste 		error = aac_ioctl_send_raw_srb(sc, arg);
2850f355c0e0SEd Maste 		break;
285135863739SMike Smith 	case FSACTL_AIF_THREAD:
2852fb0c27d7SScott Long 	case FSACTL_LNX_AIF_THREAD:
285331a0399eSEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_AIF_THREAD");
285435863739SMike Smith 		error = EINVAL;
285535863739SMike Smith 		break;
285635863739SMike Smith 	case FSACTL_OPEN_GET_ADAPTER_FIB:
2857fb0c27d7SScott Long 		arg = *(caddr_t*)arg;
2858fb0c27d7SScott Long 	case FSACTL_LNX_OPEN_GET_ADAPTER_FIB:
285931a0399eSEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_OPEN_GET_ADAPTER_FIB");
2860a723a548SEd Maste 		error = aac_open_aif(sc, arg);
286135863739SMike Smith 		break;
286235863739SMike Smith 	case FSACTL_GET_NEXT_ADAPTER_FIB:
2863fb0c27d7SScott Long 		arg = *(caddr_t*)arg;
2864fb0c27d7SScott Long 	case FSACTL_LNX_GET_NEXT_ADAPTER_FIB:
286531a0399eSEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_GET_NEXT_ADAPTER_FIB");
2866fb0c27d7SScott Long 		error = aac_getnext_aif(sc, arg);
286735863739SMike Smith 		break;
286835863739SMike Smith 	case FSACTL_CLOSE_GET_ADAPTER_FIB:
2869a723a548SEd Maste 		arg = *(caddr_t*)arg;
2870fb0c27d7SScott Long 	case FSACTL_LNX_CLOSE_GET_ADAPTER_FIB:
287131a0399eSEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_CLOSE_GET_ADAPTER_FIB");
2872a723a548SEd Maste 		error = aac_close_aif(sc, arg);
287335863739SMike Smith 		break;
287435863739SMike Smith 	case FSACTL_MINIPORT_REV_CHECK:
2875fb0c27d7SScott Long 		arg = *(caddr_t*)arg;
2876fb0c27d7SScott Long 	case FSACTL_LNX_MINIPORT_REV_CHECK:
287731a0399eSEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_MINIPORT_REV_CHECK");
2878fb0c27d7SScott Long 		error = aac_rev_check(sc, arg);
287935863739SMike Smith 		break;
288036e0bf6eSScott Long 	case FSACTL_QUERY_DISK:
288136e0bf6eSScott Long 		arg = *(caddr_t*)arg;
288236e0bf6eSScott Long 	case FSACTL_LNX_QUERY_DISK:
288331a0399eSEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_QUERY_DISK");
288436e0bf6eSScott Long 		error = aac_query_disk(sc, arg);
288536e0bf6eSScott Long 		break;
288636e0bf6eSScott Long 	case FSACTL_DELETE_DISK:
288736e0bf6eSScott Long 	case FSACTL_LNX_DELETE_DISK:
2888914da7d0SScott Long 		/*
2889914da7d0SScott Long 		 * We don't trust the underland to tell us when to delete a
2890914da7d0SScott Long 		 * container, rather we rely on an AIF coming from the
2891914da7d0SScott Long 		 * controller
2892914da7d0SScott Long 		 */
289336e0bf6eSScott Long 		error = 0;
289436e0bf6eSScott Long 		break;
28957cb209f5SScott Long 	case FSACTL_GET_PCI_INFO:
28967cb209f5SScott Long 		arg = *(caddr_t*)arg;
28977cb209f5SScott Long 	case FSACTL_LNX_GET_PCI_INFO:
289831a0399eSEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_GET_PCI_INFO");
28997cb209f5SScott Long 		error = aac_get_pci_info(sc, arg);
29007cb209f5SScott Long 		break;
29016d307336SEd Maste 	case FSACTL_GET_FEATURES:
29026d307336SEd Maste 		arg = *(caddr_t*)arg;
29036d307336SEd Maste 	case FSACTL_LNX_GET_FEATURES:
29046d307336SEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_GET_FEATURES");
29056d307336SEd Maste 		error = aac_supported_features(sc, arg);
29066d307336SEd Maste 		break;
290735863739SMike Smith 	default:
290831a0399eSEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "unsupported cmd 0x%lx\n", cmd);
290935863739SMike Smith 		error = EINVAL;
291035863739SMike Smith 		break;
291135863739SMike Smith 	}
291235863739SMike Smith 	return(error);
291335863739SMike Smith }
291435863739SMike Smith 
2915b3457b51SScott Long static int
291600b4e54aSWarner Losh aac_poll(struct cdev *dev, int poll_events, struct thread *td)
2917b3457b51SScott Long {
2918b3457b51SScott Long 	struct aac_softc *sc;
2919ef0b687cSEd Maste 	struct aac_fib_context *ctx;
2920b3457b51SScott Long 	int revents;
2921b3457b51SScott Long 
2922b3457b51SScott Long 	sc = dev->si_drv1;
2923b3457b51SScott Long 	revents = 0;
2924b3457b51SScott Long 
2925bb6fe253SScott Long 	mtx_lock(&sc->aac_aifq_lock);
2926b3457b51SScott Long 	if ((poll_events & (POLLRDNORM | POLLIN)) != 0) {
2927ef0b687cSEd Maste 		for (ctx = sc->fibctx; ctx; ctx = ctx->next) {
2928ef0b687cSEd Maste 			if (ctx->ctx_idx != sc->aifq_idx || ctx->ctx_wrap) {
2929b3457b51SScott Long 				revents |= poll_events & (POLLIN | POLLRDNORM);
2930ef0b687cSEd Maste 				break;
2931ef0b687cSEd Maste 			}
2932ef0b687cSEd Maste 		}
2933b3457b51SScott Long 	}
2934bb6fe253SScott Long 	mtx_unlock(&sc->aac_aifq_lock);
2935b3457b51SScott Long 
2936b3457b51SScott Long 	if (revents == 0) {
2937b3457b51SScott Long 		if (poll_events & (POLLIN | POLLRDNORM))
2938b3457b51SScott Long 			selrecord(td, &sc->rcv_select);
2939b3457b51SScott Long 	}
2940b3457b51SScott Long 
2941b3457b51SScott Long 	return (revents);
2942b3457b51SScott Long }
2943b3457b51SScott Long 
29447cb209f5SScott Long static void
29457cb209f5SScott Long aac_ioctl_event(struct aac_softc *sc, struct aac_event *event, void *arg)
29467cb209f5SScott Long {
29477cb209f5SScott Long 
29487cb209f5SScott Long 	switch (event->ev_type) {
29497cb209f5SScott Long 	case AAC_EVENT_CMFREE:
29500c40d5beSEd Maste 		mtx_assert(&sc->aac_io_lock, MA_OWNED);
29511a681311SLuoqi Chen 		if (aac_alloc_command(sc, (struct aac_command **)arg)) {
29527cb209f5SScott Long 			aac_add_event(sc, event);
29537cb209f5SScott Long 			return;
29547cb209f5SScott Long 		}
29557cb209f5SScott Long 		free(event, M_AACBUF);
29568eeb2ca6SScott Long 		wakeup(arg);
29577cb209f5SScott Long 		break;
29587cb209f5SScott Long 	default:
29597cb209f5SScott Long 		break;
29607cb209f5SScott Long 	}
29617cb209f5SScott Long }
29627cb209f5SScott Long 
2963914da7d0SScott Long /*
296435863739SMike Smith  * Send a FIB supplied from userspace
296535863739SMike Smith  */
296635863739SMike Smith static int
296735863739SMike Smith aac_ioctl_sendfib(struct aac_softc *sc, caddr_t ufib)
296835863739SMike Smith {
296935863739SMike Smith 	struct aac_command *cm;
297035863739SMike Smith 	int size, error;
297135863739SMike Smith 
297231a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
297335863739SMike Smith 
297435863739SMike Smith 	cm = NULL;
297535863739SMike Smith 
297635863739SMike Smith 	/*
297735863739SMike Smith 	 * Get a command
297835863739SMike Smith 	 */
2979bb6fe253SScott Long 	mtx_lock(&sc->aac_io_lock);
298035863739SMike Smith 	if (aac_alloc_command(sc, &cm)) {
29817cb209f5SScott Long 		struct aac_event *event;
29827cb209f5SScott Long 
29837cb209f5SScott Long 		event = malloc(sizeof(struct aac_event), M_AACBUF,
29847cb209f5SScott Long 		    M_NOWAIT | M_ZERO);
29857cb209f5SScott Long 		if (event == NULL) {
298635863739SMike Smith 			error = EBUSY;
2987f16627aaSEd Maste 			mtx_unlock(&sc->aac_io_lock);
298835863739SMike Smith 			goto out;
298935863739SMike Smith 		}
29907cb209f5SScott Long 		event->ev_type = AAC_EVENT_CMFREE;
29917cb209f5SScott Long 		event->ev_callback = aac_ioctl_event;
29927cb209f5SScott Long 		event->ev_arg = &cm;
29937cb209f5SScott Long 		aac_add_event(sc, event);
29948eeb2ca6SScott Long 		msleep(&cm, &sc->aac_io_lock, 0, "sendfib", 0);
29957cb209f5SScott Long 	}
299693cfca22SScott Long 	mtx_unlock(&sc->aac_io_lock);
299735863739SMike Smith 
299835863739SMike Smith 	/*
299935863739SMike Smith 	 * Fetch the FIB header, then re-copy to get data as well.
300035863739SMike Smith 	 */
3001914da7d0SScott Long 	if ((error = copyin(ufib, cm->cm_fib,
3002914da7d0SScott Long 			    sizeof(struct aac_fib_header))) != 0)
300335863739SMike Smith 		goto out;
300435863739SMike Smith 	size = cm->cm_fib->Header.Size + sizeof(struct aac_fib_header);
3005f355c0e0SEd Maste 	if (size > sc->aac_max_fib_size) {
3006f355c0e0SEd Maste 		device_printf(sc->aac_dev, "incoming FIB oversized (%d > %d)\n",
3007f355c0e0SEd Maste 			      size, sc->aac_max_fib_size);
3008f355c0e0SEd Maste 		size = sc->aac_max_fib_size;
300935863739SMike Smith 	}
301035863739SMike Smith 	if ((error = copyin(ufib, cm->cm_fib, size)) != 0)
301135863739SMike Smith 		goto out;
301235863739SMike Smith 	cm->cm_fib->Header.Size = size;
30132b3b0f17SScott Long 	cm->cm_timestamp = time_uptime;
301435863739SMike Smith 
301535863739SMike Smith 	/*
301635863739SMike Smith 	 * Pass the FIB to the controller, wait for it to complete.
301735863739SMike Smith 	 */
301893cfca22SScott Long 	mtx_lock(&sc->aac_io_lock);
3019f16627aaSEd Maste 	error = aac_wait_command(cm);
3020f16627aaSEd Maste 	mtx_unlock(&sc->aac_io_lock);
3021f16627aaSEd Maste 	if (error != 0) {
302270545d1aSScott Long 		device_printf(sc->aac_dev,
302370545d1aSScott Long 			      "aac_wait_command return %d\n", error);
302435863739SMike Smith 		goto out;
3025b3457b51SScott Long 	}
302635863739SMike Smith 
302735863739SMike Smith 	/*
302835863739SMike Smith 	 * Copy the FIB and data back out to the caller.
302935863739SMike Smith 	 */
303035863739SMike Smith 	size = cm->cm_fib->Header.Size;
3031f355c0e0SEd Maste 	if (size > sc->aac_max_fib_size) {
3032f355c0e0SEd Maste 		device_printf(sc->aac_dev, "outbound FIB oversized (%d > %d)\n",
3033f355c0e0SEd Maste 			      size, sc->aac_max_fib_size);
3034f355c0e0SEd Maste 		size = sc->aac_max_fib_size;
303535863739SMike Smith 	}
303635863739SMike Smith 	error = copyout(cm->cm_fib, ufib, size);
303735863739SMike Smith 
303835863739SMike Smith out:
3039f6c4dd3fSScott Long 	if (cm != NULL) {
3040f16627aaSEd Maste 		mtx_lock(&sc->aac_io_lock);
304135863739SMike Smith 		aac_release_command(cm);
3042bb6fe253SScott Long 		mtx_unlock(&sc->aac_io_lock);
3043f16627aaSEd Maste 	}
304435863739SMike Smith 	return(error);
304535863739SMike Smith }
304635863739SMike Smith 
3047914da7d0SScott Long /*
3048f355c0e0SEd Maste  * Send a passthrough FIB supplied from userspace
3049f355c0e0SEd Maste  */
3050f355c0e0SEd Maste static int
3051f355c0e0SEd Maste aac_ioctl_send_raw_srb(struct aac_softc *sc, caddr_t arg)
3052f355c0e0SEd Maste {
30537b90e5ecSAttilio Rao 	struct aac_command *cm;
30547b90e5ecSAttilio Rao 	struct aac_event *event;
30557b90e5ecSAttilio Rao 	struct aac_fib *fib;
30567b90e5ecSAttilio Rao 	struct aac_srb *srbcmd, *user_srb;
30577b90e5ecSAttilio Rao 	struct aac_sg_entry *sge;
30587b90e5ecSAttilio Rao 	struct aac_sg_entry64 *sge64;
30597b90e5ecSAttilio Rao 	void *srb_sg_address, *ureply;
30607b90e5ecSAttilio Rao 	uint32_t fibsize, srb_sg_bytecount;
30617b90e5ecSAttilio Rao 	int error, transfer_data;
30627b90e5ecSAttilio Rao 
30637b90e5ecSAttilio Rao 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
30647b90e5ecSAttilio Rao 
30657b90e5ecSAttilio Rao 	cm = NULL;
30667b90e5ecSAttilio Rao 	transfer_data = 0;
30677b90e5ecSAttilio Rao 	fibsize = 0;
30687b90e5ecSAttilio Rao 	user_srb = (struct aac_srb *)arg;
30697b90e5ecSAttilio Rao 
30707b90e5ecSAttilio Rao 	mtx_lock(&sc->aac_io_lock);
30717b90e5ecSAttilio Rao 	if (aac_alloc_command(sc, &cm)) {
30727b90e5ecSAttilio Rao 		 event = malloc(sizeof(struct aac_event), M_AACBUF,
30737b90e5ecSAttilio Rao 		    M_NOWAIT | M_ZERO);
30747b90e5ecSAttilio Rao 		if (event == NULL) {
30757b90e5ecSAttilio Rao 			error = EBUSY;
30767b90e5ecSAttilio Rao 			mtx_unlock(&sc->aac_io_lock);
30777b90e5ecSAttilio Rao 			goto out;
30787b90e5ecSAttilio Rao 		}
30797b90e5ecSAttilio Rao 		event->ev_type = AAC_EVENT_CMFREE;
30807b90e5ecSAttilio Rao 		event->ev_callback = aac_ioctl_event;
30817b90e5ecSAttilio Rao 		event->ev_arg = &cm;
30827b90e5ecSAttilio Rao 		aac_add_event(sc, event);
30837b90e5ecSAttilio Rao 		msleep(cm, &sc->aac_io_lock, 0, "aacraw", 0);
30847b90e5ecSAttilio Rao 	}
30857b90e5ecSAttilio Rao 	mtx_unlock(&sc->aac_io_lock);
30867b90e5ecSAttilio Rao 
30877b90e5ecSAttilio Rao 	cm->cm_data = NULL;
30887b90e5ecSAttilio Rao 	fib = cm->cm_fib;
30897b90e5ecSAttilio Rao 	srbcmd = (struct aac_srb *)fib->data;
30907b90e5ecSAttilio Rao 	error = copyin(&user_srb->data_len, &fibsize, sizeof(uint32_t));
30917b90e5ecSAttilio Rao 	if (error != 0)
30927b90e5ecSAttilio Rao 		goto out;
30937b90e5ecSAttilio Rao 	if (fibsize > (sc->aac_max_fib_size - sizeof(struct aac_fib_header))) {
30947b90e5ecSAttilio Rao 		error = EINVAL;
30957b90e5ecSAttilio Rao 		goto out;
30967b90e5ecSAttilio Rao 	}
30977b90e5ecSAttilio Rao 	error = copyin(user_srb, srbcmd, fibsize);
30987b90e5ecSAttilio Rao 	if (error != 0)
30997b90e5ecSAttilio Rao 		goto out;
31007b90e5ecSAttilio Rao 	srbcmd->function = 0;
31017b90e5ecSAttilio Rao 	srbcmd->retry_limit = 0;
31027b90e5ecSAttilio Rao 	if (srbcmd->sg_map.SgCount > 1) {
31037b90e5ecSAttilio Rao 		error = EINVAL;
31047b90e5ecSAttilio Rao 		goto out;
31057b90e5ecSAttilio Rao 	}
31067b90e5ecSAttilio Rao 
31077b90e5ecSAttilio Rao 	/* Retrieve correct SG entries. */
31087b90e5ecSAttilio Rao 	if (fibsize == (sizeof(struct aac_srb) +
31097b90e5ecSAttilio Rao 	    srbcmd->sg_map.SgCount * sizeof(struct aac_sg_entry))) {
3110f4a18258SSean Bruno 		struct aac_sg_entry sg;
3111f4a18258SSean Bruno 
31127b90e5ecSAttilio Rao 		sge = srbcmd->sg_map.SgEntry;
31137b90e5ecSAttilio Rao 		sge64 = NULL;
3114f4a18258SSean Bruno 
3115f4a18258SSean Bruno 		if ((error = copyin(sge, &sg, sizeof(sg))) != 0)
3116f4a18258SSean Bruno 			goto out;
3117f4a18258SSean Bruno 
3118f4a18258SSean Bruno 		srb_sg_bytecount = sg.SgByteCount;
3119f4a18258SSean Bruno 		srb_sg_address = (void *)(uintptr_t)sg.SgAddress;
31207b90e5ecSAttilio Rao 	}
31217b90e5ecSAttilio Rao #ifdef __amd64__
31227b90e5ecSAttilio Rao 	else if (fibsize == (sizeof(struct aac_srb) +
31237b90e5ecSAttilio Rao 	    srbcmd->sg_map.SgCount * sizeof(struct aac_sg_entry64))) {
3124f4a18258SSean Bruno 		struct aac_sg_entry64 sg;
3125f4a18258SSean Bruno 
31267b90e5ecSAttilio Rao 		sge = NULL;
31277b90e5ecSAttilio Rao 		sge64 = (struct aac_sg_entry64 *)srbcmd->sg_map.SgEntry;
3128f4a18258SSean Bruno 
3129f4a18258SSean Bruno 		if ((error = copyin(sge64, &sg, sizeof(sg))) != 0)
3130f4a18258SSean Bruno 			goto out;
3131f4a18258SSean Bruno 
3132f4a18258SSean Bruno 		srb_sg_bytecount = sg.SgByteCount;
3133f4a18258SSean Bruno 		srb_sg_address = (void *)sg.SgAddress;
31347b90e5ecSAttilio Rao 		if (sge64->SgAddress > 0xffffffffull &&
31357b90e5ecSAttilio Rao 		    (sc->flags & AAC_FLAGS_SG_64BIT) == 0) {
31367b90e5ecSAttilio Rao 			error = EINVAL;
31377b90e5ecSAttilio Rao 			goto out;
31387b90e5ecSAttilio Rao 		}
31397b90e5ecSAttilio Rao 	}
31407b90e5ecSAttilio Rao #endif
31417b90e5ecSAttilio Rao 	else {
31427b90e5ecSAttilio Rao 		error = EINVAL;
31437b90e5ecSAttilio Rao 		goto out;
31447b90e5ecSAttilio Rao 	}
31457b90e5ecSAttilio Rao 	ureply = (char *)arg + fibsize;
31467b90e5ecSAttilio Rao 	srbcmd->data_len = srb_sg_bytecount;
31477b90e5ecSAttilio Rao 	if (srbcmd->sg_map.SgCount == 1)
31487b90e5ecSAttilio Rao 		transfer_data = 1;
31497b90e5ecSAttilio Rao 
31507b90e5ecSAttilio Rao 	cm->cm_sgtable = (struct aac_sg_table *)&srbcmd->sg_map;
31517b90e5ecSAttilio Rao 	if (transfer_data) {
31527b90e5ecSAttilio Rao 		cm->cm_datalen = srb_sg_bytecount;
31537b90e5ecSAttilio Rao 		cm->cm_data = malloc(cm->cm_datalen, M_AACBUF, M_NOWAIT);
31547b90e5ecSAttilio Rao 		if (cm->cm_data == NULL) {
31557b90e5ecSAttilio Rao 			error = ENOMEM;
31567b90e5ecSAttilio Rao 			goto out;
31577b90e5ecSAttilio Rao 		}
31587b90e5ecSAttilio Rao 		if (srbcmd->flags & AAC_SRB_FLAGS_DATA_IN)
31597b90e5ecSAttilio Rao 			cm->cm_flags |= AAC_CMD_DATAIN;
31607b90e5ecSAttilio Rao 		if (srbcmd->flags & AAC_SRB_FLAGS_DATA_OUT) {
31617b90e5ecSAttilio Rao 			cm->cm_flags |= AAC_CMD_DATAOUT;
31627b90e5ecSAttilio Rao 			error = copyin(srb_sg_address, cm->cm_data,
31637b90e5ecSAttilio Rao 			    cm->cm_datalen);
31647b90e5ecSAttilio Rao 			if (error != 0)
31657b90e5ecSAttilio Rao 				goto out;
31667b90e5ecSAttilio Rao 		}
31677b90e5ecSAttilio Rao 	}
31687b90e5ecSAttilio Rao 
31697b90e5ecSAttilio Rao 	fib->Header.Size = sizeof(struct aac_fib_header) +
31707b90e5ecSAttilio Rao 	    sizeof(struct aac_srb);
31717b90e5ecSAttilio Rao 	fib->Header.XferState =
31727b90e5ecSAttilio Rao 	    AAC_FIBSTATE_HOSTOWNED   |
31737b90e5ecSAttilio Rao 	    AAC_FIBSTATE_INITIALISED |
31747b90e5ecSAttilio Rao 	    AAC_FIBSTATE_EMPTY       |
31757b90e5ecSAttilio Rao 	    AAC_FIBSTATE_FROMHOST    |
31767b90e5ecSAttilio Rao 	    AAC_FIBSTATE_REXPECTED   |
31777b90e5ecSAttilio Rao 	    AAC_FIBSTATE_NORM        |
31787b90e5ecSAttilio Rao 	    AAC_FIBSTATE_ASYNC       |
31797b90e5ecSAttilio Rao 	    AAC_FIBSTATE_FAST_RESPONSE;
31807b90e5ecSAttilio Rao 	fib->Header.Command = (sc->flags & AAC_FLAGS_SG_64BIT) != 0 ?
31817b90e5ecSAttilio Rao 	    ScsiPortCommandU64 : ScsiPortCommand;
31827b90e5ecSAttilio Rao 
31837b90e5ecSAttilio Rao 	mtx_lock(&sc->aac_io_lock);
31847b90e5ecSAttilio Rao 	aac_wait_command(cm);
31857b90e5ecSAttilio Rao 	mtx_unlock(&sc->aac_io_lock);
31867b90e5ecSAttilio Rao 
31877b90e5ecSAttilio Rao 	if (transfer_data && (srbcmd->flags & AAC_SRB_FLAGS_DATA_IN) != 0) {
31887b90e5ecSAttilio Rao 		error = copyout(cm->cm_data, srb_sg_address, cm->cm_datalen);
31897b90e5ecSAttilio Rao 		if (error != 0)
31907b90e5ecSAttilio Rao 			goto out;
31917b90e5ecSAttilio Rao 	}
31927b90e5ecSAttilio Rao 	error = copyout(fib->data, ureply, sizeof(struct aac_srb_response));
31937b90e5ecSAttilio Rao out:
31947b90e5ecSAttilio Rao 	if (cm != NULL) {
31957b90e5ecSAttilio Rao 		if (cm->cm_data != NULL)
31967b90e5ecSAttilio Rao 			free(cm->cm_data, M_AACBUF);
31977b90e5ecSAttilio Rao 		mtx_lock(&sc->aac_io_lock);
31987b90e5ecSAttilio Rao 		aac_release_command(cm);
31997b90e5ecSAttilio Rao 		mtx_unlock(&sc->aac_io_lock);
32007b90e5ecSAttilio Rao 	}
32017b90e5ecSAttilio Rao 	return(error);
3202f355c0e0SEd Maste }
3203f355c0e0SEd Maste 
3204f355c0e0SEd Maste /*
3205dfe2c294SAttilio Rao  * cdevpriv interface private destructor.
3206dfe2c294SAttilio Rao  */
3207dfe2c294SAttilio Rao static void
3208dfe2c294SAttilio Rao aac_cdevpriv_dtor(void *arg)
3209dfe2c294SAttilio Rao {
3210dfe2c294SAttilio Rao 	struct aac_softc *sc;
3211dfe2c294SAttilio Rao 
3212dfe2c294SAttilio Rao 	sc = arg;
3213dfe2c294SAttilio Rao 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
3214dfe2c294SAttilio Rao 	device_unbusy(sc->aac_dev);
3215dfe2c294SAttilio Rao }
3216dfe2c294SAttilio Rao 
3217dfe2c294SAttilio Rao /*
321835863739SMike Smith  * Handle an AIF sent to us by the controller; queue it for later reference.
321936e0bf6eSScott Long  * If the queue fills up, then drop the older entries.
322035863739SMike Smith  */
322135863739SMike Smith static void
322236e0bf6eSScott Long aac_handle_aif(struct aac_softc *sc, struct aac_fib *fib)
322335863739SMike Smith {
322436e0bf6eSScott Long 	struct aac_aif_command *aif;
322536e0bf6eSScott Long 	struct aac_container *co, *co_next;
3226a723a548SEd Maste 	struct aac_fib_context *ctx;
322704f4d586SEd Maste 	struct aac_mntinforesp *mir;
3228a723a548SEd Maste 	int next, current, found;
3229795d7dc0SScott Long 	int count = 0, added = 0, i = 0;
3230851f59d7SEd Maste 	uint32_t channel;
323135863739SMike Smith 
323231a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
323335863739SMike Smith 
323436e0bf6eSScott Long 	aif = (struct aac_aif_command*)&fib->data[0];
323536e0bf6eSScott Long 	aac_print_aif(sc, aif);
323636e0bf6eSScott Long 
323736e0bf6eSScott Long 	/* Is it an event that we should care about? */
323836e0bf6eSScott Long 	switch (aif->command) {
323936e0bf6eSScott Long 	case AifCmdEventNotify:
324036e0bf6eSScott Long 		switch (aif->data.EN.type) {
324136e0bf6eSScott Long 		case AifEnAddContainer:
324236e0bf6eSScott Long 		case AifEnDeleteContainer:
324336e0bf6eSScott Long 			/*
3244914da7d0SScott Long 			 * A container was added or deleted, but the message
3245914da7d0SScott Long 			 * doesn't tell us anything else!  Re-enumerate the
3246914da7d0SScott Long 			 * containers and sort things out.
324736e0bf6eSScott Long 			 */
324803b5fe51SScott Long 			aac_alloc_sync_fib(sc, &fib);
324936e0bf6eSScott Long 			do {
325036e0bf6eSScott Long 				/*
3251914da7d0SScott Long 				 * Ask the controller for its containers one at
3252914da7d0SScott Long 				 * a time.
3253914da7d0SScott Long 				 * XXX What if the controller's list changes
3254914da7d0SScott Long 				 * midway through this enumaration?
325536e0bf6eSScott Long 				 * XXX This should be done async.
325636e0bf6eSScott Long 				 */
325704f4d586SEd Maste 				if ((mir = aac_get_container_info(sc, fib, i)) == NULL)
325836e0bf6eSScott Long 					continue;
325904f4d586SEd Maste 				if (i == 0)
3260795d7dc0SScott Long 					count = mir->MntRespCount;
326136e0bf6eSScott Long 				/*
3262914da7d0SScott Long 				 * Check the container against our list.
3263914da7d0SScott Long 				 * co->co_found was already set to 0 in a
3264914da7d0SScott Long 				 * previous run.
326536e0bf6eSScott Long 				 */
3266cbfd045bSScott Long 				if ((mir->Status == ST_OK) &&
3267cbfd045bSScott Long 				    (mir->MntTable[0].VolType != CT_NONE)) {
326836e0bf6eSScott Long 					found = 0;
3269914da7d0SScott Long 					TAILQ_FOREACH(co,
3270914da7d0SScott Long 						      &sc->aac_container_tqh,
3271914da7d0SScott Long 						      co_link) {
327236e0bf6eSScott Long 						if (co->co_mntobj.ObjectId ==
3273cbfd045bSScott Long 						    mir->MntTable[0].ObjectId) {
327436e0bf6eSScott Long 							co->co_found = 1;
327536e0bf6eSScott Long 							found = 1;
327636e0bf6eSScott Long 							break;
327736e0bf6eSScott Long 						}
327836e0bf6eSScott Long 					}
3279914da7d0SScott Long 					/*
3280914da7d0SScott Long 					 * If the container matched, continue
3281914da7d0SScott Long 					 * in the list.
3282914da7d0SScott Long 					 */
328336e0bf6eSScott Long 					if (found) {
328436e0bf6eSScott Long 						i++;
328536e0bf6eSScott Long 						continue;
328636e0bf6eSScott Long 					}
328736e0bf6eSScott Long 
328836e0bf6eSScott Long 					/*
3289914da7d0SScott Long 					 * This is a new container.  Do all the
329070545d1aSScott Long 					 * appropriate things to set it up.
329170545d1aSScott Long 					 */
3292cbfd045bSScott Long 					aac_add_container(sc, mir, 1);
329336e0bf6eSScott Long 					added = 1;
329436e0bf6eSScott Long 				}
329536e0bf6eSScott Long 				i++;
3296795d7dc0SScott Long 			} while ((i < count) && (i < AAC_MAX_CONTAINERS));
3297cbfd045bSScott Long 			aac_release_sync_fib(sc);
329836e0bf6eSScott Long 
329936e0bf6eSScott Long 			/*
3300914da7d0SScott Long 			 * Go through our list of containers and see which ones
3301914da7d0SScott Long 			 * were not marked 'found'.  Since the controller didn't
3302914da7d0SScott Long 			 * list them they must have been deleted.  Do the
3303914da7d0SScott Long 			 * appropriate steps to destroy the device.  Also reset
3304914da7d0SScott Long 			 * the co->co_found field.
330536e0bf6eSScott Long 			 */
330636e0bf6eSScott Long 			co = TAILQ_FIRST(&sc->aac_container_tqh);
330736e0bf6eSScott Long 			while (co != NULL) {
330836e0bf6eSScott Long 				if (co->co_found == 0) {
33097cb209f5SScott Long 					mtx_unlock(&sc->aac_io_lock);
3310a56fe095SJohn Baldwin 					mtx_lock(&Giant);
3311914da7d0SScott Long 					device_delete_child(sc->aac_dev,
3312914da7d0SScott Long 							    co->co_disk);
3313a56fe095SJohn Baldwin 					mtx_unlock(&Giant);
33147cb209f5SScott Long 					mtx_lock(&sc->aac_io_lock);
331536e0bf6eSScott Long 					co_next = TAILQ_NEXT(co, co_link);
3316bb6fe253SScott Long 					mtx_lock(&sc->aac_container_lock);
3317914da7d0SScott Long 					TAILQ_REMOVE(&sc->aac_container_tqh, co,
3318914da7d0SScott Long 						     co_link);
3319bb6fe253SScott Long 					mtx_unlock(&sc->aac_container_lock);
3320ba1d57e7SScott Long 					free(co, M_AACBUF);
332136e0bf6eSScott Long 					co = co_next;
332236e0bf6eSScott Long 				} else {
332336e0bf6eSScott Long 					co->co_found = 0;
332436e0bf6eSScott Long 					co = TAILQ_NEXT(co, co_link);
332536e0bf6eSScott Long 				}
332636e0bf6eSScott Long 			}
332736e0bf6eSScott Long 
332836e0bf6eSScott Long 			/* Attach the newly created containers */
33297cb209f5SScott Long 			if (added) {
33307cb209f5SScott Long 				mtx_unlock(&sc->aac_io_lock);
3331a56fe095SJohn Baldwin 				mtx_lock(&Giant);
333236e0bf6eSScott Long 				bus_generic_attach(sc->aac_dev);
3333a56fe095SJohn Baldwin 				mtx_unlock(&Giant);
33347cb209f5SScott Long 				mtx_lock(&sc->aac_io_lock);
33357cb209f5SScott Long 			}
333636e0bf6eSScott Long 
333736e0bf6eSScott Long 			break;
333836e0bf6eSScott Long 
3339851f59d7SEd Maste 		case AifEnEnclosureManagement:
3340851f59d7SEd Maste 			switch (aif->data.EN.data.EEE.eventType) {
3341851f59d7SEd Maste 			case AIF_EM_DRIVE_INSERTION:
3342851f59d7SEd Maste 			case AIF_EM_DRIVE_REMOVAL:
3343851f59d7SEd Maste 				channel = aif->data.EN.data.EEE.unitID;
3344851f59d7SEd Maste 				if (sc->cam_rescan_cb != NULL)
3345851f59d7SEd Maste 					sc->cam_rescan_cb(sc,
3346851f59d7SEd Maste 					    (channel >> 24) & 0xF,
3347851f59d7SEd Maste 					    (channel & 0xFFFF));
3348851f59d7SEd Maste 				break;
3349851f59d7SEd Maste 			}
3350851f59d7SEd Maste 			break;
3351851f59d7SEd Maste 
3352851f59d7SEd Maste 		case AifEnAddJBOD:
3353851f59d7SEd Maste 		case AifEnDeleteJBOD:
3354851f59d7SEd Maste 			channel = aif->data.EN.data.ECE.container;
3355851f59d7SEd Maste 			if (sc->cam_rescan_cb != NULL)
3356851f59d7SEd Maste 				sc->cam_rescan_cb(sc, (channel >> 24) & 0xF,
3357851f59d7SEd Maste 				    AAC_CAM_TARGET_WILDCARD);
3358851f59d7SEd Maste 			break;
3359851f59d7SEd Maste 
336036e0bf6eSScott Long 		default:
336136e0bf6eSScott Long 			break;
336236e0bf6eSScott Long 		}
336336e0bf6eSScott Long 
336436e0bf6eSScott Long 	default:
336536e0bf6eSScott Long 		break;
336636e0bf6eSScott Long 	}
336736e0bf6eSScott Long 
336836e0bf6eSScott Long 	/* Copy the AIF data to the AIF queue for ioctl retrieval */
3369bb6fe253SScott Long 	mtx_lock(&sc->aac_aifq_lock);
3370a723a548SEd Maste 	current = sc->aifq_idx;
3371a723a548SEd Maste 	next = (current + 1) % AAC_AIFQ_LENGTH;
3372a723a548SEd Maste 	if (next == 0)
3373a723a548SEd Maste 		sc->aifq_filled = 1;
3374a723a548SEd Maste 	bcopy(fib, &sc->aac_aifq[current], sizeof(struct aac_fib));
3375a723a548SEd Maste 	/* modify AIF contexts */
3376a723a548SEd Maste 	if (sc->aifq_filled) {
3377a723a548SEd Maste 		for (ctx = sc->fibctx; ctx; ctx = ctx->next) {
3378a723a548SEd Maste 			if (next == ctx->ctx_idx)
3379a723a548SEd Maste 				ctx->ctx_wrap = 1;
3380a723a548SEd Maste 			else if (current == ctx->ctx_idx && ctx->ctx_wrap)
3381a723a548SEd Maste 				ctx->ctx_idx = next;
3382a723a548SEd Maste 		}
3383a723a548SEd Maste 	}
3384a723a548SEd Maste 	sc->aifq_idx = next;
3385b3457b51SScott Long 	/* On the off chance that someone is sleeping for an aif... */
338635863739SMike Smith 	if (sc->aac_state & AAC_STATE_AIF_SLEEPER)
338735863739SMike Smith 		wakeup(sc->aac_aifq);
3388b3457b51SScott Long 	/* Wakeup any poll()ers */
3389512824f8SSeigo Tanimura 	selwakeuppri(&sc->rcv_select, PRIBIO);
3390bb6fe253SScott Long 	mtx_unlock(&sc->aac_aifq_lock);
339135863739SMike Smith }
339235863739SMike Smith 
3393914da7d0SScott Long /*
33940b94a66eSMike Smith  * Return the Revision of the driver to userspace and check to see if the
339536e0bf6eSScott Long  * userspace app is possibly compatible.  This is extremely bogus since
339636e0bf6eSScott Long  * our driver doesn't follow Adaptec's versioning system.  Cheat by just
339736e0bf6eSScott Long  * returning what the card reported.
339835863739SMike Smith  */
339935863739SMike Smith static int
3400fb0c27d7SScott Long aac_rev_check(struct aac_softc *sc, caddr_t udata)
340135863739SMike Smith {
340235863739SMike Smith 	struct aac_rev_check rev_check;
340335863739SMike Smith 	struct aac_rev_check_resp rev_check_resp;
340435863739SMike Smith 	int error = 0;
340535863739SMike Smith 
340631a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
340735863739SMike Smith 
340835863739SMike Smith 	/*
340935863739SMike Smith 	 * Copyin the revision struct from userspace
341035863739SMike Smith 	 */
3411c6eafcf2SScott Long 	if ((error = copyin(udata, (caddr_t)&rev_check,
3412c6eafcf2SScott Long 			sizeof(struct aac_rev_check))) != 0) {
341335863739SMike Smith 		return error;
341435863739SMike Smith 	}
341535863739SMike Smith 
341631a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "Userland revision= %d\n",
3417914da7d0SScott Long 	      rev_check.callingRevision.buildNumber);
341835863739SMike Smith 
341935863739SMike Smith 	/*
342035863739SMike Smith 	 * Doctor up the response struct.
342135863739SMike Smith 	 */
342235863739SMike Smith 	rev_check_resp.possiblyCompatible = 1;
34238e7e6335SEd Maste 	rev_check_resp.adapterSWRevision.external.comp.major =
34248e7e6335SEd Maste 	    AAC_DRIVER_MAJOR_VERSION;
34258e7e6335SEd Maste 	rev_check_resp.adapterSWRevision.external.comp.minor =
34268e7e6335SEd Maste 	    AAC_DRIVER_MINOR_VERSION;
34278e7e6335SEd Maste 	rev_check_resp.adapterSWRevision.external.comp.type =
34288e7e6335SEd Maste 	    AAC_DRIVER_TYPE;
34298e7e6335SEd Maste 	rev_check_resp.adapterSWRevision.external.comp.dash =
34308e7e6335SEd Maste 	    AAC_DRIVER_BUGFIX_LEVEL;
3431914da7d0SScott Long 	rev_check_resp.adapterSWRevision.buildNumber =
34328e7e6335SEd Maste 	    AAC_DRIVER_BUILD;
343335863739SMike Smith 
3434c6eafcf2SScott Long 	return(copyout((caddr_t)&rev_check_resp, udata,
3435c6eafcf2SScott Long 			sizeof(struct aac_rev_check_resp)));
343635863739SMike Smith }
343735863739SMike Smith 
3438914da7d0SScott Long /*
3439a723a548SEd Maste  * Pass the fib context to the caller
3440a723a548SEd Maste  */
3441a723a548SEd Maste static int
3442a723a548SEd Maste aac_open_aif(struct aac_softc *sc, caddr_t arg)
3443a723a548SEd Maste {
3444a723a548SEd Maste 	struct aac_fib_context *fibctx, *ctx;
3445a723a548SEd Maste 	int error = 0;
3446a723a548SEd Maste 
344731a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
3448a723a548SEd Maste 
3449a723a548SEd Maste 	fibctx = malloc(sizeof(struct aac_fib_context), M_AACBUF, M_NOWAIT|M_ZERO);
3450a723a548SEd Maste 	if (fibctx == NULL)
3451a723a548SEd Maste 		return (ENOMEM);
3452a723a548SEd Maste 
3453a723a548SEd Maste 	mtx_lock(&sc->aac_aifq_lock);
3454a723a548SEd Maste 	/* all elements are already 0, add to queue */
3455a723a548SEd Maste 	if (sc->fibctx == NULL)
3456a723a548SEd Maste 		sc->fibctx = fibctx;
3457a723a548SEd Maste 	else {
3458a723a548SEd Maste 		for (ctx = sc->fibctx; ctx->next; ctx = ctx->next)
3459a723a548SEd Maste 			;
3460a723a548SEd Maste 		ctx->next = fibctx;
3461a723a548SEd Maste 		fibctx->prev = ctx;
3462a723a548SEd Maste 	}
3463a723a548SEd Maste 
3464a723a548SEd Maste 	/* evaluate unique value */
3465a723a548SEd Maste 	fibctx->unique = (*(u_int32_t *)&fibctx & 0xffffffff);
3466a723a548SEd Maste 	ctx = sc->fibctx;
3467a723a548SEd Maste 	while (ctx != fibctx) {
3468a723a548SEd Maste 		if (ctx->unique == fibctx->unique) {
3469a723a548SEd Maste 			fibctx->unique++;
3470a723a548SEd Maste 			ctx = sc->fibctx;
3471a723a548SEd Maste 		} else {
3472a723a548SEd Maste 			ctx = ctx->next;
3473a723a548SEd Maste 		}
3474a723a548SEd Maste 	}
3475a723a548SEd Maste 	mtx_unlock(&sc->aac_aifq_lock);
3476a723a548SEd Maste 
3477a723a548SEd Maste 	error = copyout(&fibctx->unique, (void *)arg, sizeof(u_int32_t));
3478a723a548SEd Maste 	if (error)
3479a723a548SEd Maste 		aac_close_aif(sc, (caddr_t)ctx);
3480a723a548SEd Maste 	return error;
3481a723a548SEd Maste }
3482a723a548SEd Maste 
3483a723a548SEd Maste /*
3484a723a548SEd Maste  * Close the caller's fib context
3485a723a548SEd Maste  */
3486a723a548SEd Maste static int
3487a723a548SEd Maste aac_close_aif(struct aac_softc *sc, caddr_t arg)
3488a723a548SEd Maste {
3489a723a548SEd Maste 	struct aac_fib_context *ctx;
3490a723a548SEd Maste 
349131a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
3492a723a548SEd Maste 
3493a723a548SEd Maste 	mtx_lock(&sc->aac_aifq_lock);
3494a723a548SEd Maste 	for (ctx = sc->fibctx; ctx; ctx = ctx->next) {
3495a723a548SEd Maste 		if (ctx->unique == *(uint32_t *)&arg) {
3496a723a548SEd Maste 			if (ctx == sc->fibctx)
3497a723a548SEd Maste 				sc->fibctx = NULL;
3498a723a548SEd Maste 			else {
3499a723a548SEd Maste 				ctx->prev->next = ctx->next;
3500a723a548SEd Maste 				if (ctx->next)
3501a723a548SEd Maste 					ctx->next->prev = ctx->prev;
3502a723a548SEd Maste 			}
3503a723a548SEd Maste 			break;
3504a723a548SEd Maste 		}
3505a723a548SEd Maste 	}
3506a723a548SEd Maste 	mtx_unlock(&sc->aac_aifq_lock);
3507a723a548SEd Maste 	if (ctx)
3508a723a548SEd Maste 		free(ctx, M_AACBUF);
3509a723a548SEd Maste 
3510a723a548SEd Maste 	return 0;
3511a723a548SEd Maste }
3512a723a548SEd Maste 
3513a723a548SEd Maste /*
351435863739SMike Smith  * Pass the caller the next AIF in their queue
351535863739SMike Smith  */
351635863739SMike Smith static int
3517fb0c27d7SScott Long aac_getnext_aif(struct aac_softc *sc, caddr_t arg)
351835863739SMike Smith {
351935863739SMike Smith 	struct get_adapter_fib_ioctl agf;
3520a723a548SEd Maste 	struct aac_fib_context *ctx;
35219e2e96d8SScott Long 	int error;
352235863739SMike Smith 
352331a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
352435863739SMike Smith 
3525f287c3e4SBrooks Davis #ifdef COMPAT_FREEBSD32
3526f287c3e4SBrooks Davis 	if (SV_CURPROC_FLAG(SV_ILP32)) {
3527f287c3e4SBrooks Davis 		struct get_adapter_fib_ioctl32 agf32;
3528f287c3e4SBrooks Davis 		error = copyin(arg, &agf32, sizeof(agf32));
3529f287c3e4SBrooks Davis 		if (error == 0) {
3530f287c3e4SBrooks Davis 			agf.AdapterFibContext = agf32.AdapterFibContext;
3531f287c3e4SBrooks Davis 			agf.Wait = agf32.Wait;
3532f287c3e4SBrooks Davis 			agf.AifFib = (caddr_t)(uintptr_t)agf32.AifFib;
3533f287c3e4SBrooks Davis 		}
3534f287c3e4SBrooks Davis 	} else
3535f287c3e4SBrooks Davis #endif
3536f287c3e4SBrooks Davis 		error = copyin(arg, &agf, sizeof(agf));
3537f287c3e4SBrooks Davis 	if (error == 0) {
3538a723a548SEd Maste 		for (ctx = sc->fibctx; ctx; ctx = ctx->next) {
3539a723a548SEd Maste 			if (agf.AdapterFibContext == ctx->unique)
3540a723a548SEd Maste 				break;
3541a723a548SEd Maste 		}
3542a723a548SEd Maste 		if (!ctx)
3543a723a548SEd Maste 			return (EFAULT);
354435863739SMike Smith 
3545a723a548SEd Maste 		error = aac_return_aif(sc, ctx, agf.AifFib);
3546a723a548SEd Maste 		if (error == EAGAIN && agf.Wait) {
354731a0399eSEd Maste 			fwprintf(sc, HBA_FLAGS_DBG_AIF_B, "aac_getnext_aif(): waiting for AIF");
354835863739SMike Smith 			sc->aac_state |= AAC_STATE_AIF_SLEEPER;
354935863739SMike Smith 			while (error == EAGAIN) {
3550914da7d0SScott Long 				error = tsleep(sc->aac_aifq, PRIBIO |
3551914da7d0SScott Long 					       PCATCH, "aacaif", 0);
355235863739SMike Smith 				if (error == 0)
3553a723a548SEd Maste 					error = aac_return_aif(sc, ctx, agf.AifFib);
355435863739SMike Smith 			}
355535863739SMike Smith 			sc->aac_state &= ~AAC_STATE_AIF_SLEEPER;
355635863739SMike Smith 		}
355735863739SMike Smith 	}
355835863739SMike Smith 	return(error);
355935863739SMike Smith }
356035863739SMike Smith 
3561914da7d0SScott Long /*
35620b94a66eSMike Smith  * Hand the next AIF off the top of the queue out to userspace.
35630b94a66eSMike Smith  */
35640b94a66eSMike Smith static int
3565a723a548SEd Maste aac_return_aif(struct aac_softc *sc, struct aac_fib_context *ctx, caddr_t uptr)
35660b94a66eSMike Smith {
3567a723a548SEd Maste 	int current, error;
35680b94a66eSMike Smith 
356931a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
35700b94a66eSMike Smith 
3571bb6fe253SScott Long 	mtx_lock(&sc->aac_aifq_lock);
3572a723a548SEd Maste 	current = ctx->ctx_idx;
3573a723a548SEd Maste 	if (current == sc->aifq_idx && !ctx->ctx_wrap) {
3574a723a548SEd Maste 		/* empty */
3575bb6fe253SScott Long 		mtx_unlock(&sc->aac_aifq_lock);
35763df780cfSScott Long 		return (EAGAIN);
35773df780cfSScott Long 	}
3578a723a548SEd Maste 	error =
3579a723a548SEd Maste 		copyout(&sc->aac_aifq[current], (void *)uptr, sizeof(struct aac_fib));
358036e0bf6eSScott Long 	if (error)
358170545d1aSScott Long 		device_printf(sc->aac_dev,
358270545d1aSScott Long 		    "aac_return_aif: copyout returned %d\n", error);
3583a723a548SEd Maste 	else {
3584a723a548SEd Maste 		ctx->ctx_wrap = 0;
3585a723a548SEd Maste 		ctx->ctx_idx = (current + 1) % AAC_AIFQ_LENGTH;
3586a723a548SEd Maste 	}
3587bb6fe253SScott Long 	mtx_unlock(&sc->aac_aifq_lock);
35880b94a66eSMike Smith 	return(error);
35890b94a66eSMike Smith }
359036e0bf6eSScott Long 
35917cb209f5SScott Long static int
35927cb209f5SScott Long aac_get_pci_info(struct aac_softc *sc, caddr_t uptr)
35937cb209f5SScott Long {
35947cb209f5SScott Long 	struct aac_pci_info {
35957cb209f5SScott Long 		u_int32_t bus;
35967cb209f5SScott Long 		u_int32_t slot;
35977cb209f5SScott Long 	} pciinf;
35987cb209f5SScott Long 	int error;
35997cb209f5SScott Long 
360031a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
36017cb209f5SScott Long 
36027cb209f5SScott Long 	pciinf.bus = pci_get_bus(sc->aac_dev);
36037cb209f5SScott Long 	pciinf.slot = pci_get_slot(sc->aac_dev);
36047cb209f5SScott Long 
36057cb209f5SScott Long 	error = copyout((caddr_t)&pciinf, uptr,
36067cb209f5SScott Long 			sizeof(struct aac_pci_info));
36077cb209f5SScott Long 
36087cb209f5SScott Long 	return (error);
36097cb209f5SScott Long }
36107cb209f5SScott Long 
36116d307336SEd Maste static int
36126d307336SEd Maste aac_supported_features(struct aac_softc *sc, caddr_t uptr)
36136d307336SEd Maste {
36146d307336SEd Maste 	struct aac_features f;
36156d307336SEd Maste 	int error;
36166d307336SEd Maste 
36176d307336SEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
36186d307336SEd Maste 
36196d307336SEd Maste 	if ((error = copyin(uptr, &f, sizeof (f))) != 0)
36206d307336SEd Maste 		return (error);
36216d307336SEd Maste 
36226d307336SEd Maste 	/*
36236d307336SEd Maste 	 * When the management driver receives FSACTL_GET_FEATURES ioctl with
36246d307336SEd Maste 	 * ALL zero in the featuresState, the driver will return the current
36256d307336SEd Maste 	 * state of all the supported features, the data field will not be
36266d307336SEd Maste 	 * valid.
36276d307336SEd Maste 	 * When the management driver receives FSACTL_GET_FEATURES ioctl with
36286d307336SEd Maste 	 * a specific bit set in the featuresState, the driver will return the
36296d307336SEd Maste 	 * current state of this specific feature and whatever data that are
36306d307336SEd Maste 	 * associated with the feature in the data field or perform whatever
36316d307336SEd Maste 	 * action needed indicates in the data field.
36326d307336SEd Maste 	 */
36336d307336SEd Maste 	if (f.feat.fValue == 0) {
36346d307336SEd Maste 		f.feat.fBits.largeLBA =
36356d307336SEd Maste 		    (sc->flags & AAC_FLAGS_LBA_64BIT) ? 1 : 0;
36366d307336SEd Maste 		/* TODO: In the future, add other features state here as well */
36376d307336SEd Maste 	} else {
36386d307336SEd Maste 		if (f.feat.fBits.largeLBA)
36396d307336SEd Maste 			f.feat.fBits.largeLBA =
36406d307336SEd Maste 			    (sc->flags & AAC_FLAGS_LBA_64BIT) ? 1 : 0;
36416d307336SEd Maste 		/* TODO: Add other features state and data in the future */
36426d307336SEd Maste 	}
36436d307336SEd Maste 
36446d307336SEd Maste 	error = copyout(&f, uptr, sizeof (f));
36456d307336SEd Maste 	return (error);
36466d307336SEd Maste }
36476d307336SEd Maste 
3648914da7d0SScott Long /*
364936e0bf6eSScott Long  * Give the userland some information about the container.  The AAC arch
365036e0bf6eSScott Long  * expects the driver to be a SCSI passthrough type driver, so it expects
365136e0bf6eSScott Long  * the containers to have b:t:l numbers.  Fake it.
365236e0bf6eSScott Long  */
365336e0bf6eSScott Long static int
365436e0bf6eSScott Long aac_query_disk(struct aac_softc *sc, caddr_t uptr)
365536e0bf6eSScott Long {
365636e0bf6eSScott Long 	struct aac_query_disk query_disk;
365736e0bf6eSScott Long 	struct aac_container *co;
3658914da7d0SScott Long 	struct aac_disk	*disk;
365936e0bf6eSScott Long 	int error, id;
366036e0bf6eSScott Long 
366131a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
366236e0bf6eSScott Long 
3663914da7d0SScott Long 	disk = NULL;
3664914da7d0SScott Long 
3665914da7d0SScott Long 	error = copyin(uptr, (caddr_t)&query_disk,
3666914da7d0SScott Long 		       sizeof(struct aac_query_disk));
366736e0bf6eSScott Long 	if (error)
366836e0bf6eSScott Long 		return (error);
366936e0bf6eSScott Long 
367036e0bf6eSScott Long 	id = query_disk.ContainerNumber;
367136e0bf6eSScott Long 	if (id == -1)
367236e0bf6eSScott Long 		return (EINVAL);
367336e0bf6eSScott Long 
3674bb6fe253SScott Long 	mtx_lock(&sc->aac_container_lock);
367536e0bf6eSScott Long 	TAILQ_FOREACH(co, &sc->aac_container_tqh, co_link) {
367636e0bf6eSScott Long 		if (co->co_mntobj.ObjectId == id)
367736e0bf6eSScott Long 			break;
367836e0bf6eSScott Long 		}
367936e0bf6eSScott Long 
368036e0bf6eSScott Long 	if (co == NULL) {
368136e0bf6eSScott Long 			query_disk.Valid = 0;
368236e0bf6eSScott Long 			query_disk.Locked = 0;
368336e0bf6eSScott Long 			query_disk.Deleted = 1;		/* XXX is this right? */
368436e0bf6eSScott Long 	} else {
368536e0bf6eSScott Long 		disk = device_get_softc(co->co_disk);
368636e0bf6eSScott Long 		query_disk.Valid = 1;
3687914da7d0SScott Long 		query_disk.Locked =
3688914da7d0SScott Long 		    (disk->ad_flags & AAC_DISK_OPEN) ? 1 : 0;
368936e0bf6eSScott Long 		query_disk.Deleted = 0;
3690b3457b51SScott Long 		query_disk.Bus = device_get_unit(sc->aac_dev);
369136e0bf6eSScott Long 		query_disk.Target = disk->unit;
369236e0bf6eSScott Long 		query_disk.Lun = 0;
369336e0bf6eSScott Long 		query_disk.UnMapped = 0;
36947540e65eSScott Long 		sprintf(&query_disk.diskDeviceName[0], "%s%d",
36950b7ed341SPoul-Henning Kamp 			disk->ad_disk->d_name, disk->ad_disk->d_unit);
369636e0bf6eSScott Long 	}
3697bb6fe253SScott Long 	mtx_unlock(&sc->aac_container_lock);
369836e0bf6eSScott Long 
3699914da7d0SScott Long 	error = copyout((caddr_t)&query_disk, uptr,
3700914da7d0SScott Long 			sizeof(struct aac_query_disk));
370136e0bf6eSScott Long 
370236e0bf6eSScott Long 	return (error);
370336e0bf6eSScott Long }
370436e0bf6eSScott Long 
3705fe3cb0e1SScott Long static void
3706fe3cb0e1SScott Long aac_get_bus_info(struct aac_softc *sc)
3707fe3cb0e1SScott Long {
3708fe3cb0e1SScott Long 	struct aac_fib *fib;
3709fe3cb0e1SScott Long 	struct aac_ctcfg *c_cmd;
3710fe3cb0e1SScott Long 	struct aac_ctcfg_resp *c_resp;
3711fe3cb0e1SScott Long 	struct aac_vmioctl *vmi;
3712fe3cb0e1SScott Long 	struct aac_vmi_businf_resp *vmi_resp;
3713fe3cb0e1SScott Long 	struct aac_getbusinf businfo;
371470545d1aSScott Long 	struct aac_sim *caminf;
3715fe3cb0e1SScott Long 	device_t child;
3716fe3cb0e1SScott Long 	int i, found, error;
3717fe3cb0e1SScott Long 
37181ffe41c1SChristian S.J. Peron 	mtx_lock(&sc->aac_io_lock);
371903b5fe51SScott Long 	aac_alloc_sync_fib(sc, &fib);
3720fe3cb0e1SScott Long 	c_cmd = (struct aac_ctcfg *)&fib->data[0];
372139ee03c3SScott Long 	bzero(c_cmd, sizeof(struct aac_ctcfg));
3722fe3cb0e1SScott Long 
3723fe3cb0e1SScott Long 	c_cmd->Command = VM_ContainerConfig;
3724fe3cb0e1SScott Long 	c_cmd->cmd = CT_GET_SCSI_METHOD;
3725fe3cb0e1SScott Long 	c_cmd->param = 0;
3726fe3cb0e1SScott Long 
3727fe3cb0e1SScott Long 	error = aac_sync_fib(sc, ContainerCommand, 0, fib,
3728fe3cb0e1SScott Long 	    sizeof(struct aac_ctcfg));
3729fe3cb0e1SScott Long 	if (error) {
3730fe3cb0e1SScott Long 		device_printf(sc->aac_dev, "Error %d sending "
3731fe3cb0e1SScott Long 		    "VM_ContainerConfig command\n", error);
3732fe3cb0e1SScott Long 		aac_release_sync_fib(sc);
37331ffe41c1SChristian S.J. Peron 		mtx_unlock(&sc->aac_io_lock);
3734fe3cb0e1SScott Long 		return;
3735fe3cb0e1SScott Long 	}
3736fe3cb0e1SScott Long 
3737fe3cb0e1SScott Long 	c_resp = (struct aac_ctcfg_resp *)&fib->data[0];
3738fe3cb0e1SScott Long 	if (c_resp->Status != ST_OK) {
3739fe3cb0e1SScott Long 		device_printf(sc->aac_dev, "VM_ContainerConfig returned 0x%x\n",
3740fe3cb0e1SScott Long 		    c_resp->Status);
3741fe3cb0e1SScott Long 		aac_release_sync_fib(sc);
37421ffe41c1SChristian S.J. Peron 		mtx_unlock(&sc->aac_io_lock);
3743fe3cb0e1SScott Long 		return;
3744fe3cb0e1SScott Long 	}
3745fe3cb0e1SScott Long 
3746fe3cb0e1SScott Long 	sc->scsi_method_id = c_resp->param;
3747fe3cb0e1SScott Long 
3748fe3cb0e1SScott Long 	vmi = (struct aac_vmioctl *)&fib->data[0];
374939ee03c3SScott Long 	bzero(vmi, sizeof(struct aac_vmioctl));
375039ee03c3SScott Long 
3751fe3cb0e1SScott Long 	vmi->Command = VM_Ioctl;
3752fe3cb0e1SScott Long 	vmi->ObjType = FT_DRIVE;
3753fe3cb0e1SScott Long 	vmi->MethId = sc->scsi_method_id;
3754fe3cb0e1SScott Long 	vmi->ObjId = 0;
3755fe3cb0e1SScott Long 	vmi->IoctlCmd = GetBusInfo;
3756fe3cb0e1SScott Long 
3757fe3cb0e1SScott Long 	error = aac_sync_fib(sc, ContainerCommand, 0, fib,
375842ef13a2SEd Maste 	    sizeof(struct aac_vmi_businf_resp));
3759fe3cb0e1SScott Long 	if (error) {
3760fe3cb0e1SScott Long 		device_printf(sc->aac_dev, "Error %d sending VMIoctl command\n",
3761fe3cb0e1SScott Long 		    error);
3762fe3cb0e1SScott Long 		aac_release_sync_fib(sc);
37631ffe41c1SChristian S.J. Peron 		mtx_unlock(&sc->aac_io_lock);
3764fe3cb0e1SScott Long 		return;
3765fe3cb0e1SScott Long 	}
3766fe3cb0e1SScott Long 
3767fe3cb0e1SScott Long 	vmi_resp = (struct aac_vmi_businf_resp *)&fib->data[0];
3768fe3cb0e1SScott Long 	if (vmi_resp->Status != ST_OK) {
3769fe3cb0e1SScott Long 		device_printf(sc->aac_dev, "VM_Ioctl returned %d\n",
3770fe3cb0e1SScott Long 		    vmi_resp->Status);
3771fe3cb0e1SScott Long 		aac_release_sync_fib(sc);
37721ffe41c1SChristian S.J. Peron 		mtx_unlock(&sc->aac_io_lock);
3773fe3cb0e1SScott Long 		return;
3774fe3cb0e1SScott Long 	}
3775fe3cb0e1SScott Long 
3776fe3cb0e1SScott Long 	bcopy(&vmi_resp->BusInf, &businfo, sizeof(struct aac_getbusinf));
3777fe3cb0e1SScott Long 	aac_release_sync_fib(sc);
37781ffe41c1SChristian S.J. Peron 	mtx_unlock(&sc->aac_io_lock);
3779fe3cb0e1SScott Long 
3780fe3cb0e1SScott Long 	found = 0;
3781fe3cb0e1SScott Long 	for (i = 0; i < businfo.BusCount; i++) {
3782fe3cb0e1SScott Long 		if (businfo.BusValid[i] != AAC_BUS_VALID)
3783fe3cb0e1SScott Long 			continue;
3784fe3cb0e1SScott Long 
3785a761a1caSScott Long 		caminf = (struct aac_sim *)malloc( sizeof(struct aac_sim),
3786a761a1caSScott Long 		    M_AACBUF, M_NOWAIT | M_ZERO);
3787b5f516cdSScott Long 		if (caminf == NULL) {
3788b5f516cdSScott Long 			device_printf(sc->aac_dev,
3789b5f516cdSScott Long 			    "No memory to add passthrough bus %d\n", i);
3790b5f516cdSScott Long 			break;
379174b8d63dSPedro F. Giffuni 		}
3792fe3cb0e1SScott Long 
3793fe3cb0e1SScott Long 		child = device_add_child(sc->aac_dev, "aacp", -1);
3794fe3cb0e1SScott Long 		if (child == NULL) {
3795b5f516cdSScott Long 			device_printf(sc->aac_dev,
3796b5f516cdSScott Long 			    "device_add_child failed for passthrough bus %d\n",
3797b5f516cdSScott Long 			    i);
3798b5f516cdSScott Long 			free(caminf, M_AACBUF);
3799b5f516cdSScott Long 			break;
3800fe3cb0e1SScott Long 		}
3801fe3cb0e1SScott Long 
3802fe3cb0e1SScott Long 		caminf->TargetsPerBus = businfo.TargetsPerBus;
3803fe3cb0e1SScott Long 		caminf->BusNumber = i;
3804fe3cb0e1SScott Long 		caminf->InitiatorBusId = businfo.InitiatorBusId[i];
3805fe3cb0e1SScott Long 		caminf->aac_sc = sc;
3806ddb8683eSScott Long 		caminf->sim_dev = child;
3807fe3cb0e1SScott Long 
3808fe3cb0e1SScott Long 		device_set_ivars(child, caminf);
3809fe3cb0e1SScott Long 		device_set_desc(child, "SCSI Passthrough Bus");
381070545d1aSScott Long 		TAILQ_INSERT_TAIL(&sc->aac_sim_tqh, caminf, sim_link);
3811fe3cb0e1SScott Long 
3812fe3cb0e1SScott Long 		found = 1;
3813fe3cb0e1SScott Long 	}
3814fe3cb0e1SScott Long 
3815fe3cb0e1SScott Long 	if (found)
3816fe3cb0e1SScott Long 		bus_generic_attach(sc->aac_dev);
3817fe3cb0e1SScott Long }
3818