xref: /freebsd/sys/dev/aac/aac.c (revision 6f954fb36b28550c58678125b289ae80ea4d7089)
135863739SMike Smith /*-
235863739SMike Smith  * Copyright (c) 2000 Michael Smith
3c6eafcf2SScott Long  * Copyright (c) 2001 Scott Long
435863739SMike Smith  * Copyright (c) 2000 BSDi
5c6eafcf2SScott Long  * Copyright (c) 2001 Adaptec, Inc.
635863739SMike Smith  * All rights reserved.
735863739SMike Smith  *
835863739SMike Smith  * Redistribution and use in source and binary forms, with or without
935863739SMike Smith  * modification, are permitted provided that the following conditions
1035863739SMike Smith  * are met:
1135863739SMike Smith  * 1. Redistributions of source code must retain the above copyright
1235863739SMike Smith  *    notice, this list of conditions and the following disclaimer.
1335863739SMike Smith  * 2. Redistributions in binary form must reproduce the above copyright
1435863739SMike Smith  *    notice, this list of conditions and the following disclaimer in the
1535863739SMike Smith  *    documentation and/or other materials provided with the distribution.
1635863739SMike Smith  *
1735863739SMike Smith  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1835863739SMike Smith  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1935863739SMike Smith  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2035863739SMike Smith  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2135863739SMike Smith  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2235863739SMike Smith  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2335863739SMike Smith  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2435863739SMike Smith  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2535863739SMike Smith  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2635863739SMike Smith  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2735863739SMike Smith  * SUCH DAMAGE.
2835863739SMike Smith  */
2935863739SMike Smith 
30aad970f1SDavid E. O'Brien #include <sys/cdefs.h>
31aad970f1SDavid E. O'Brien __FBSDID("$FreeBSD$");
32aad970f1SDavid E. O'Brien 
3335863739SMike Smith /*
3435863739SMike Smith  * Driver for the Adaptec 'FSA' family of PCI/SCSI RAID adapters.
3535863739SMike Smith  */
367cb209f5SScott Long #define AAC_DRIVERNAME			"aac"
3735863739SMike Smith 
38f6c4dd3fSScott Long #include "opt_aac.h"
39f6c4dd3fSScott Long 
4036e0bf6eSScott Long /* #include <stddef.h> */
4135863739SMike Smith #include <sys/param.h>
4235863739SMike Smith #include <sys/systm.h>
4335863739SMike Smith #include <sys/malloc.h>
4435863739SMike Smith #include <sys/kernel.h>
4536e0bf6eSScott Long #include <sys/kthread.h>
463d04a9d7SScott Long #include <sys/sysctl.h>
47b3457b51SScott Long #include <sys/poll.h>
48891619a6SPoul-Henning Kamp #include <sys/ioccom.h>
4935863739SMike Smith 
5035863739SMike Smith #include <sys/bus.h>
5135863739SMike Smith #include <sys/conf.h>
5235863739SMike Smith #include <sys/signalvar.h>
530b94a66eSMike Smith #include <sys/time.h>
5436e0bf6eSScott Long #include <sys/eventhandler.h>
557cb209f5SScott Long #include <sys/rman.h>
5635863739SMike Smith 
5735863739SMike Smith #include <machine/bus.h>
58b5f516cdSScott Long #include <sys/bus_dma.h>
5935863739SMike Smith #include <machine/resource.h>
6035863739SMike Smith 
617cb209f5SScott Long #include <dev/pci/pcireg.h>
627cb209f5SScott Long #include <dev/pci/pcivar.h>
637cb209f5SScott Long 
6435863739SMike Smith #include <dev/aac/aacreg.h>
650b0594cdSScott Long #include <sys/aac_ioctl.h>
6635863739SMike Smith #include <dev/aac/aacvar.h>
6735863739SMike Smith #include <dev/aac/aac_tables.h>
6835863739SMike Smith 
6935863739SMike Smith static void	aac_startup(void *arg);
70914da7d0SScott Long static void	aac_add_container(struct aac_softc *sc,
71cbfd045bSScott Long 				  struct aac_mntinforesp *mir, int f);
72fe3cb0e1SScott Long static void	aac_get_bus_info(struct aac_softc *sc);
73ff0991c4SAttilio Rao static void	aac_daemon(void *arg);
7435863739SMike Smith 
7535863739SMike Smith /* Command Processing */
760b94a66eSMike Smith static void	aac_timeout(struct aac_softc *sc);
7735863739SMike Smith static void	aac_complete(void *context, int pending);
7835863739SMike Smith static int	aac_bio_command(struct aac_softc *sc, struct aac_command **cmp);
7935863739SMike Smith static void	aac_bio_complete(struct aac_command *cm);
80d8a0a473SScott Long static int	aac_wait_command(struct aac_command *cm);
8170545d1aSScott Long static void	aac_command_thread(struct aac_softc *sc);
8235863739SMike Smith 
8335863739SMike Smith /* Command Buffer Management */
84cd481291SScott Long static void	aac_map_command_sg(void *arg, bus_dma_segment_t *segs,
85cd481291SScott Long 				   int nseg, int error);
86c6eafcf2SScott Long static void	aac_map_command_helper(void *arg, bus_dma_segment_t *segs,
87c6eafcf2SScott Long 				       int nseg, int error);
880b94a66eSMike Smith static int	aac_alloc_commands(struct aac_softc *sc);
898480cc63SScott Long static void	aac_free_commands(struct aac_softc *sc);
9035863739SMike Smith static void	aac_unmap_command(struct aac_command *cm);
9135863739SMike Smith 
9235863739SMike Smith /* Hardware Interface */
9304f4d586SEd Maste static int	aac_alloc(struct aac_softc *sc);
94c6eafcf2SScott Long static void	aac_common_map(void *arg, bus_dma_segment_t *segs, int nseg,
95c6eafcf2SScott Long 			       int error);
96fe94b852SScott Long static int	aac_check_firmware(struct aac_softc *sc);
9735863739SMike Smith static int	aac_init(struct aac_softc *sc);
9835863739SMike Smith static int	aac_sync_command(struct aac_softc *sc, u_int32_t command,
99c6eafcf2SScott Long 				 u_int32_t arg0, u_int32_t arg1, u_int32_t arg2,
100c6eafcf2SScott Long 				 u_int32_t arg3, u_int32_t *sp);
10104f4d586SEd Maste static int	aac_setup_intr(struct aac_softc *sc);
102c6eafcf2SScott Long static int	aac_enqueue_fib(struct aac_softc *sc, int queue,
103f6c4dd3fSScott Long 				struct aac_command *cm);
104c6eafcf2SScott Long static int	aac_dequeue_fib(struct aac_softc *sc, int queue,
105914da7d0SScott Long 				u_int32_t *fib_size, struct aac_fib **fib_addr);
10636e0bf6eSScott Long static int	aac_enqueue_response(struct aac_softc *sc, int queue,
10736e0bf6eSScott Long 				     struct aac_fib *fib);
10835863739SMike Smith 
10935863739SMike Smith /* StrongARM interface */
11035863739SMike Smith static int	aac_sa_get_fwstatus(struct aac_softc *sc);
11135863739SMike Smith static void	aac_sa_qnotify(struct aac_softc *sc, int qbit);
11235863739SMike Smith static int	aac_sa_get_istatus(struct aac_softc *sc);
11335863739SMike Smith static void	aac_sa_clear_istatus(struct aac_softc *sc, int mask);
11435863739SMike Smith static void	aac_sa_set_mailbox(struct aac_softc *sc, u_int32_t command,
115c6eafcf2SScott Long 				   u_int32_t arg0, u_int32_t arg1,
116c6eafcf2SScott Long 				   u_int32_t arg2, u_int32_t arg3);
117a6d35632SScott Long static int	aac_sa_get_mailbox(struct aac_softc *sc, int mb);
11835863739SMike Smith static void	aac_sa_set_interrupts(struct aac_softc *sc, int enable);
11935863739SMike Smith 
120da4882c2SMarius Strobl const struct aac_interface aac_sa_interface = {
12135863739SMike Smith 	aac_sa_get_fwstatus,
12235863739SMike Smith 	aac_sa_qnotify,
12335863739SMike Smith 	aac_sa_get_istatus,
12435863739SMike Smith 	aac_sa_clear_istatus,
12535863739SMike Smith 	aac_sa_set_mailbox,
126a6d35632SScott Long 	aac_sa_get_mailbox,
1277cb209f5SScott Long 	aac_sa_set_interrupts,
1287cb209f5SScott Long 	NULL, NULL, NULL
12935863739SMike Smith };
13035863739SMike Smith 
13135863739SMike Smith /* i960Rx interface */
13235863739SMike Smith static int	aac_rx_get_fwstatus(struct aac_softc *sc);
13335863739SMike Smith static void	aac_rx_qnotify(struct aac_softc *sc, int qbit);
13435863739SMike Smith static int	aac_rx_get_istatus(struct aac_softc *sc);
13535863739SMike Smith static void	aac_rx_clear_istatus(struct aac_softc *sc, int mask);
13635863739SMike Smith static void	aac_rx_set_mailbox(struct aac_softc *sc, u_int32_t command,
137c6eafcf2SScott Long 				   u_int32_t arg0, u_int32_t arg1,
138c6eafcf2SScott Long 				   u_int32_t arg2, u_int32_t arg3);
139a6d35632SScott Long static int	aac_rx_get_mailbox(struct aac_softc *sc, int mb);
14035863739SMike Smith static void	aac_rx_set_interrupts(struct aac_softc *sc, int enable);
1417cb209f5SScott Long static int aac_rx_send_command(struct aac_softc *sc, struct aac_command *cm);
1427cb209f5SScott Long static int aac_rx_get_outb_queue(struct aac_softc *sc);
1437cb209f5SScott Long static void aac_rx_set_outb_queue(struct aac_softc *sc, int index);
14435863739SMike Smith 
145da4882c2SMarius Strobl const struct aac_interface aac_rx_interface = {
14635863739SMike Smith 	aac_rx_get_fwstatus,
14735863739SMike Smith 	aac_rx_qnotify,
14835863739SMike Smith 	aac_rx_get_istatus,
14935863739SMike Smith 	aac_rx_clear_istatus,
15035863739SMike Smith 	aac_rx_set_mailbox,
151a6d35632SScott Long 	aac_rx_get_mailbox,
1527cb209f5SScott Long 	aac_rx_set_interrupts,
1537cb209f5SScott Long 	aac_rx_send_command,
1547cb209f5SScott Long 	aac_rx_get_outb_queue,
1557cb209f5SScott Long 	aac_rx_set_outb_queue
15635863739SMike Smith };
15735863739SMike Smith 
1584afedc31SScott Long /* Rocket/MIPS interface */
1594afedc31SScott Long static int	aac_rkt_get_fwstatus(struct aac_softc *sc);
1604afedc31SScott Long static void	aac_rkt_qnotify(struct aac_softc *sc, int qbit);
1614afedc31SScott Long static int	aac_rkt_get_istatus(struct aac_softc *sc);
1624afedc31SScott Long static void	aac_rkt_clear_istatus(struct aac_softc *sc, int mask);
1634afedc31SScott Long static void	aac_rkt_set_mailbox(struct aac_softc *sc, u_int32_t command,
1644afedc31SScott Long 				    u_int32_t arg0, u_int32_t arg1,
1654afedc31SScott Long 				    u_int32_t arg2, u_int32_t arg3);
1664afedc31SScott Long static int	aac_rkt_get_mailbox(struct aac_softc *sc, int mb);
1674afedc31SScott Long static void	aac_rkt_set_interrupts(struct aac_softc *sc, int enable);
1687cb209f5SScott Long static int aac_rkt_send_command(struct aac_softc *sc, struct aac_command *cm);
1697cb209f5SScott Long static int aac_rkt_get_outb_queue(struct aac_softc *sc);
1707cb209f5SScott Long static void aac_rkt_set_outb_queue(struct aac_softc *sc, int index);
1714afedc31SScott Long 
172da4882c2SMarius Strobl const struct aac_interface aac_rkt_interface = {
1734afedc31SScott Long 	aac_rkt_get_fwstatus,
1744afedc31SScott Long 	aac_rkt_qnotify,
1754afedc31SScott Long 	aac_rkt_get_istatus,
1764afedc31SScott Long 	aac_rkt_clear_istatus,
1774afedc31SScott Long 	aac_rkt_set_mailbox,
1784afedc31SScott Long 	aac_rkt_get_mailbox,
1797cb209f5SScott Long 	aac_rkt_set_interrupts,
1807cb209f5SScott Long 	aac_rkt_send_command,
1817cb209f5SScott Long 	aac_rkt_get_outb_queue,
1827cb209f5SScott Long 	aac_rkt_set_outb_queue
1834afedc31SScott Long };
1844afedc31SScott Long 
18535863739SMike Smith /* Debugging and Diagnostics */
18635863739SMike Smith static void		aac_describe_controller(struct aac_softc *sc);
187da4882c2SMarius Strobl static const char	*aac_describe_code(const struct aac_code_lookup *table,
188c6eafcf2SScott Long 				   u_int32_t code);
18935863739SMike Smith 
19035863739SMike Smith /* Management Interface */
19135863739SMike Smith static d_open_t		aac_open;
19235863739SMike Smith static d_ioctl_t	aac_ioctl;
193b3457b51SScott Long static d_poll_t		aac_poll;
194dfe2c294SAttilio Rao static void		aac_cdevpriv_dtor(void *arg);
195c6eafcf2SScott Long static int		aac_ioctl_sendfib(struct aac_softc *sc, caddr_t ufib);
196f355c0e0SEd Maste static int		aac_ioctl_send_raw_srb(struct aac_softc *sc, caddr_t arg);
197c6eafcf2SScott Long static void		aac_handle_aif(struct aac_softc *sc,
19836e0bf6eSScott Long 					   struct aac_fib *fib);
199fb0c27d7SScott Long static int		aac_rev_check(struct aac_softc *sc, caddr_t udata);
200a723a548SEd Maste static int		aac_open_aif(struct aac_softc *sc, caddr_t arg);
201a723a548SEd Maste static int		aac_close_aif(struct aac_softc *sc, caddr_t arg);
202fb0c27d7SScott Long static int		aac_getnext_aif(struct aac_softc *sc, caddr_t arg);
203a723a548SEd Maste static int		aac_return_aif(struct aac_softc *sc,
204a723a548SEd Maste 					struct aac_fib_context *ctx, caddr_t uptr);
20536e0bf6eSScott Long static int		aac_query_disk(struct aac_softc *sc, caddr_t uptr);
2067cb209f5SScott Long static int		aac_get_pci_info(struct aac_softc *sc, caddr_t uptr);
2076d307336SEd Maste static int		aac_supported_features(struct aac_softc *sc, caddr_t uptr);
2087cb209f5SScott Long static void		aac_ioctl_event(struct aac_softc *sc,
2097cb209f5SScott Long 					struct aac_event *event, void *arg);
21004f4d586SEd Maste static struct aac_mntinforesp *
21104f4d586SEd Maste 	aac_get_container_info(struct aac_softc *sc, struct aac_fib *fib, int cid);
21235863739SMike Smith 
21335863739SMike Smith static struct cdevsw aac_cdevsw = {
214dc08ffecSPoul-Henning Kamp 	.d_version =	D_VERSION,
215dfe2c294SAttilio Rao 	.d_flags =	D_NEEDGIANT,
2167ac40f5fSPoul-Henning Kamp 	.d_open =	aac_open,
2177ac40f5fSPoul-Henning Kamp 	.d_ioctl =	aac_ioctl,
2187ac40f5fSPoul-Henning Kamp 	.d_poll =	aac_poll,
2197ac40f5fSPoul-Henning Kamp 	.d_name =	"aac",
22035863739SMike Smith };
22135863739SMike Smith 
222d745c852SEd Schouten static MALLOC_DEFINE(M_AACBUF, "aacbuf", "Buffers for the AAC driver");
22336e0bf6eSScott Long 
2243d04a9d7SScott Long /* sysctl node */
225da4882c2SMarius Strobl SYSCTL_NODE(_hw, OID_AUTO, aac, CTLFLAG_RD, 0, "AAC driver parameters");
2263d04a9d7SScott Long 
227914da7d0SScott Long /*
228914da7d0SScott Long  * Device Interface
229914da7d0SScott Long  */
23035863739SMike Smith 
231914da7d0SScott Long /*
2324109ba51SEd Maste  * Initialize the controller and softc
23335863739SMike Smith  */
23435863739SMike Smith int
23535863739SMike Smith aac_attach(struct aac_softc *sc)
23635863739SMike Smith {
23735863739SMike Smith 	int error, unit;
23835863739SMike Smith 
23931a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
24035863739SMike Smith 
24135863739SMike Smith 	/*
2424109ba51SEd Maste 	 * Initialize per-controller queues.
24335863739SMike Smith 	 */
2440b94a66eSMike Smith 	aac_initq_free(sc);
2450b94a66eSMike Smith 	aac_initq_ready(sc);
2460b94a66eSMike Smith 	aac_initq_busy(sc);
2470b94a66eSMike Smith 	aac_initq_bio(sc);
24835863739SMike Smith 
24935863739SMike Smith 	/*
2504109ba51SEd Maste 	 * Initialize command-completion task.
25135863739SMike Smith 	 */
25235863739SMike Smith 	TASK_INIT(&sc->aac_task_complete, 0, aac_complete, sc);
25335863739SMike Smith 
25435863739SMike Smith 	/* mark controller as suspended until we get ourselves organised */
25535863739SMike Smith 	sc->aac_state |= AAC_STATE_SUSPEND;
25635863739SMike Smith 
25735863739SMike Smith 	/*
258fe94b852SScott Long 	 * Check that the firmware on the card is supported.
259fe94b852SScott Long 	 */
260fe94b852SScott Long 	if ((error = aac_check_firmware(sc)) != 0)
261fe94b852SScott Long 		return(error);
262fe94b852SScott Long 
263f6b1c44dSScott Long 	/*
264f6b1c44dSScott Long 	 * Initialize locks
265f6b1c44dSScott Long 	 */
266bb6fe253SScott Long 	mtx_init(&sc->aac_aifq_lock, "AAC AIF lock", NULL, MTX_DEF);
267bb6fe253SScott Long 	mtx_init(&sc->aac_io_lock, "AAC I/O lock", NULL, MTX_DEF);
268bb6fe253SScott Long 	mtx_init(&sc->aac_container_lock, "AAC container lock", NULL, MTX_DEF);
269f6b1c44dSScott Long 	TAILQ_INIT(&sc->aac_container_tqh);
270065dd78cSScott Long 	TAILQ_INIT(&sc->aac_ev_cmfree);
271f6b1c44dSScott Long 
272ff0991c4SAttilio Rao 	/* Initialize the clock daemon callout. */
273ff0991c4SAttilio Rao 	callout_init_mtx(&sc->aac_daemontime, &sc->aac_io_lock, 0);
274ff0991c4SAttilio Rao 
2750b94a66eSMike Smith 	/*
2764109ba51SEd Maste 	 * Initialize the adapter.
27735863739SMike Smith 	 */
27804f4d586SEd Maste 	if ((error = aac_alloc(sc)) != 0)
27904f4d586SEd Maste 		return(error);
2800b94a66eSMike Smith 	if ((error = aac_init(sc)) != 0)
28135863739SMike Smith 		return(error);
28235863739SMike Smith 
28335863739SMike Smith 	/*
2847cb209f5SScott Long 	 * Allocate and connect our interrupt.
2857cb209f5SScott Long 	 */
28604f4d586SEd Maste 	if ((error = aac_setup_intr(sc)) != 0)
28704f4d586SEd Maste 		return(error);
2887cb209f5SScott Long 
2897cb209f5SScott Long 	/*
29035863739SMike Smith 	 * Print a little information about the controller.
29135863739SMike Smith 	 */
29235863739SMike Smith 	aac_describe_controller(sc);
29335863739SMike Smith 
29435863739SMike Smith 	/*
2951423dcd6SEd Maste 	 * Add sysctls.
2961423dcd6SEd Maste 	 */
2971423dcd6SEd Maste 	SYSCTL_ADD_INT(device_get_sysctl_ctx(sc->aac_dev),
2981423dcd6SEd Maste 	    SYSCTL_CHILDREN(device_get_sysctl_tree(sc->aac_dev)),
2991423dcd6SEd Maste 	    OID_AUTO, "firmware_build", CTLFLAG_RD,
3001423dcd6SEd Maste 	    &sc->aac_revision.buildNumber, 0,
3011423dcd6SEd Maste 	    "firmware build number");
3021423dcd6SEd Maste 
3031423dcd6SEd Maste 	/*
304ae543596SScott Long 	 * Register to probe our containers later.
305ae543596SScott Long 	 */
30635863739SMike Smith 	sc->aac_ich.ich_func = aac_startup;
30735863739SMike Smith 	sc->aac_ich.ich_arg = sc;
30835863739SMike Smith 	if (config_intrhook_establish(&sc->aac_ich) != 0) {
309914da7d0SScott Long 		device_printf(sc->aac_dev,
310914da7d0SScott Long 			      "can't establish configuration hook\n");
31135863739SMike Smith 		return(ENXIO);
31235863739SMike Smith 	}
31335863739SMike Smith 
31435863739SMike Smith 	/*
31535863739SMike Smith 	 * Make the control device.
31635863739SMike Smith 	 */
31735863739SMike Smith 	unit = device_get_unit(sc->aac_dev);
3189e9466baSRobert Watson 	sc->aac_dev_t = make_dev(&aac_cdevsw, unit, UID_ROOT, GID_OPERATOR,
3199e9466baSRobert Watson 				 0640, "aac%d", unit);
320157fbb2eSScott Long 	(void)make_dev_alias(sc->aac_dev_t, "afa%d", unit);
3214aa620cdSScott Long 	(void)make_dev_alias(sc->aac_dev_t, "hpn%d", unit);
32235863739SMike Smith 	sc->aac_dev_t->si_drv1 = sc;
32335863739SMike Smith 
32436e0bf6eSScott Long 	/* Create the AIF thread */
3253745c395SJulian Elischer 	if (kproc_create((void(*)(void *))aac_command_thread, sc,
326316ec49aSScott Long 		   &sc->aifthread, 0, 0, "aac%daif", unit))
327a620bad0SEd Maste 		panic("Could not create AIF thread");
32836e0bf6eSScott Long 
32936e0bf6eSScott Long 	/* Register the shutdown method to only be called post-dump */
3305f54d522SScott Long 	if ((sc->eh = EVENTHANDLER_REGISTER(shutdown_final, aac_shutdown,
3315f54d522SScott Long 	    sc->aac_dev, SHUTDOWN_PRI_DEFAULT)) == NULL)
3325f54d522SScott Long 		device_printf(sc->aac_dev,
3335f54d522SScott Long 			      "shutdown event registration failed\n");
33436e0bf6eSScott Long 
335fe3cb0e1SScott Long 	/* Register with CAM for the non-DASD devices */
336a6d35632SScott Long 	if ((sc->flags & AAC_FLAGS_ENABLE_CAM) != 0) {
33770545d1aSScott Long 		TAILQ_INIT(&sc->aac_sim_tqh);
338fe3cb0e1SScott Long 		aac_get_bus_info(sc);
33970545d1aSScott Long 	}
340fe3cb0e1SScott Long 
341ff0991c4SAttilio Rao 	mtx_lock(&sc->aac_io_lock);
342867b1d34SEd Maste 	callout_reset(&sc->aac_daemontime, 60 * hz, aac_daemon, sc);
343ff0991c4SAttilio Rao 	mtx_unlock(&sc->aac_io_lock);
344ff0991c4SAttilio Rao 
34535863739SMike Smith 	return(0);
34635863739SMike Smith }
34735863739SMike Smith 
348ff0991c4SAttilio Rao static void
349ff0991c4SAttilio Rao aac_daemon(void *arg)
350ff0991c4SAttilio Rao {
351ff0991c4SAttilio Rao 	struct timeval tv;
352ff0991c4SAttilio Rao 	struct aac_softc *sc;
353ff0991c4SAttilio Rao 	struct aac_fib *fib;
354ff0991c4SAttilio Rao 
355ff0991c4SAttilio Rao 	sc = arg;
356ff0991c4SAttilio Rao 	mtx_assert(&sc->aac_io_lock, MA_OWNED);
357ff0991c4SAttilio Rao 
358ff0991c4SAttilio Rao 	if (callout_pending(&sc->aac_daemontime) ||
359ff0991c4SAttilio Rao 	    callout_active(&sc->aac_daemontime) == 0)
360ff0991c4SAttilio Rao 		return;
361ff0991c4SAttilio Rao 	getmicrotime(&tv);
362ff0991c4SAttilio Rao 	aac_alloc_sync_fib(sc, &fib);
363ff0991c4SAttilio Rao 	*(uint32_t *)fib->data = tv.tv_sec;
364ff0991c4SAttilio Rao 	aac_sync_fib(sc, SendHostTime, 0, fib, sizeof(uint32_t));
365ff0991c4SAttilio Rao 	aac_release_sync_fib(sc);
366ff0991c4SAttilio Rao 	callout_schedule(&sc->aac_daemontime, 30 * 60 * hz);
367ff0991c4SAttilio Rao }
368ff0991c4SAttilio Rao 
3697cb209f5SScott Long void
3707cb209f5SScott Long aac_add_event(struct aac_softc *sc, struct aac_event *event)
3717cb209f5SScott Long {
3727cb209f5SScott Long 
3737cb209f5SScott Long 	switch (event->ev_type & AAC_EVENT_MASK) {
3747cb209f5SScott Long 	case AAC_EVENT_CMFREE:
3757cb209f5SScott Long 		TAILQ_INSERT_TAIL(&sc->aac_ev_cmfree, event, ev_links);
3767cb209f5SScott Long 		break;
3777cb209f5SScott Long 	default:
3787cb209f5SScott Long 		device_printf(sc->aac_dev, "aac_add event: unknown event %d\n",
3797cb209f5SScott Long 		    event->ev_type);
3807cb209f5SScott Long 		break;
3817cb209f5SScott Long 	}
3827cb209f5SScott Long }
3837cb209f5SScott Long 
384914da7d0SScott Long /*
38504f4d586SEd Maste  * Request information of container #cid
38604f4d586SEd Maste  */
38704f4d586SEd Maste static struct aac_mntinforesp *
38804f4d586SEd Maste aac_get_container_info(struct aac_softc *sc, struct aac_fib *fib, int cid)
38904f4d586SEd Maste {
39004f4d586SEd Maste 	struct aac_mntinfo *mi;
39104f4d586SEd Maste 
39204f4d586SEd Maste 	mi = (struct aac_mntinfo *)&fib->data[0];
393523da39bSEd Maste 	/* use 64-bit LBA if enabled */
394523da39bSEd Maste 	mi->Command = (sc->flags & AAC_FLAGS_LBA_64BIT) ?
395523da39bSEd Maste 	    VM_NameServe64 : VM_NameServe;
39604f4d586SEd Maste 	mi->MntType = FT_FILESYS;
39704f4d586SEd Maste 	mi->MntCount = cid;
39804f4d586SEd Maste 
39904f4d586SEd Maste 	if (aac_sync_fib(sc, ContainerCommand, 0, fib,
40004f4d586SEd Maste 			 sizeof(struct aac_mntinfo))) {
401dbb34a64SEd Maste 		device_printf(sc->aac_dev, "Error probing container %d\n", cid);
40204f4d586SEd Maste 		return (NULL);
40304f4d586SEd Maste 	}
40404f4d586SEd Maste 
40504f4d586SEd Maste 	return ((struct aac_mntinforesp *)&fib->data[0]);
40604f4d586SEd Maste }
40704f4d586SEd Maste 
40804f4d586SEd Maste /*
40935863739SMike Smith  * Probe for containers, create disks.
41035863739SMike Smith  */
41135863739SMike Smith static void
41235863739SMike Smith aac_startup(void *arg)
41335863739SMike Smith {
414914da7d0SScott Long 	struct aac_softc *sc;
415cbfd045bSScott Long 	struct aac_fib *fib;
41604f4d586SEd Maste 	struct aac_mntinforesp *mir;
417795d7dc0SScott Long 	int count = 0, i = 0;
41835863739SMike Smith 
419914da7d0SScott Long 	sc = (struct aac_softc *)arg;
42031a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
421914da7d0SScott Long 
42235863739SMike Smith 	/* disconnect ourselves from the intrhook chain */
42335863739SMike Smith 	config_intrhook_disestablish(&sc->aac_ich);
42435863739SMike Smith 
4257cb209f5SScott Long 	mtx_lock(&sc->aac_io_lock);
42603b5fe51SScott Long 	aac_alloc_sync_fib(sc, &fib);
427cbfd045bSScott Long 
42835863739SMike Smith 	/* loop over possible containers */
42936e0bf6eSScott Long 	do {
43004f4d586SEd Maste 		if ((mir = aac_get_container_info(sc, fib, i)) == NULL)
43135863739SMike Smith 			continue;
43204f4d586SEd Maste 		if (i == 0)
433795d7dc0SScott Long 			count = mir->MntRespCount;
434cbfd045bSScott Long 		aac_add_container(sc, mir, 0);
43536e0bf6eSScott Long 		i++;
436795d7dc0SScott Long 	} while ((i < count) && (i < AAC_MAX_CONTAINERS));
437cbfd045bSScott Long 
438cbfd045bSScott Long 	aac_release_sync_fib(sc);
4397cb209f5SScott Long 	mtx_unlock(&sc->aac_io_lock);
44035863739SMike Smith 
44135863739SMike Smith 	/* poke the bus to actually attach the child devices */
44235863739SMike Smith 	if (bus_generic_attach(sc->aac_dev))
44335863739SMike Smith 		device_printf(sc->aac_dev, "bus_generic_attach failed\n");
44435863739SMike Smith 
44535863739SMike Smith 	/* mark the controller up */
44635863739SMike Smith 	sc->aac_state &= ~AAC_STATE_SUSPEND;
44735863739SMike Smith 
44835863739SMike Smith 	/* enable interrupts now */
44935863739SMike Smith 	AAC_UNMASK_INTERRUPTS(sc);
45035863739SMike Smith }
45135863739SMike Smith 
452914da7d0SScott Long /*
4534109ba51SEd Maste  * Create a device to represent a new container
454914da7d0SScott Long  */
455914da7d0SScott Long static void
456cbfd045bSScott Long aac_add_container(struct aac_softc *sc, struct aac_mntinforesp *mir, int f)
457914da7d0SScott Long {
458914da7d0SScott Long 	struct aac_container *co;
459914da7d0SScott Long 	device_t child;
460914da7d0SScott Long 
461914da7d0SScott Long 	/*
462914da7d0SScott Long 	 * Check container volume type for validity.  Note that many of
463914da7d0SScott Long 	 * the possible types may never show up.
464914da7d0SScott Long 	 */
465914da7d0SScott Long 	if ((mir->Status == ST_OK) && (mir->MntTable[0].VolType != CT_NONE)) {
466a761a1caSScott Long 		co = (struct aac_container *)malloc(sizeof *co, M_AACBUF,
467a761a1caSScott Long 		       M_NOWAIT | M_ZERO);
468914da7d0SScott Long 		if (co == NULL)
469a620bad0SEd Maste 			panic("Out of memory?!");
47031a0399eSEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_INIT_B, "id %x  name '%.16s'  size %u  type %d",
471914da7d0SScott Long 		      mir->MntTable[0].ObjectId,
472914da7d0SScott Long 		      mir->MntTable[0].FileSystemName,
473914da7d0SScott Long 		      mir->MntTable[0].Capacity, mir->MntTable[0].VolType);
474914da7d0SScott Long 
475fe3cb0e1SScott Long 		if ((child = device_add_child(sc->aac_dev, "aacd", -1)) == NULL)
476914da7d0SScott Long 			device_printf(sc->aac_dev, "device_add_child failed\n");
477914da7d0SScott Long 		else
478914da7d0SScott Long 			device_set_ivars(child, co);
479914da7d0SScott Long 		device_set_desc(child, aac_describe_code(aac_container_types,
480914da7d0SScott Long 				mir->MntTable[0].VolType));
481914da7d0SScott Long 		co->co_disk = child;
482914da7d0SScott Long 		co->co_found = f;
483914da7d0SScott Long 		bcopy(&mir->MntTable[0], &co->co_mntobj,
484914da7d0SScott Long 		      sizeof(struct aac_mntobj));
485bb6fe253SScott Long 		mtx_lock(&sc->aac_container_lock);
486914da7d0SScott Long 		TAILQ_INSERT_TAIL(&sc->aac_container_tqh, co, co_link);
487bb6fe253SScott Long 		mtx_unlock(&sc->aac_container_lock);
488914da7d0SScott Long 	}
489914da7d0SScott Long }
490914da7d0SScott Long 
491914da7d0SScott Long /*
49204f4d586SEd Maste  * Allocate resources associated with (sc)
49304f4d586SEd Maste  */
49404f4d586SEd Maste static int
49504f4d586SEd Maste aac_alloc(struct aac_softc *sc)
49604f4d586SEd Maste {
49731a0399eSEd Maste 
49831a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
49931a0399eSEd Maste 
50004f4d586SEd Maste 	/*
50104f4d586SEd Maste 	 * Create DMA tag for mapping buffers into controller-addressable space.
50204f4d586SEd Maste 	 */
50304f4d586SEd Maste 	if (bus_dma_tag_create(sc->aac_parent_dmat, 	/* parent */
50404f4d586SEd Maste 			       1, 0, 			/* algnmnt, boundary */
50504f4d586SEd Maste 			       (sc->flags & AAC_FLAGS_SG_64BIT) ?
50604f4d586SEd Maste 			       BUS_SPACE_MAXADDR :
50704f4d586SEd Maste 			       BUS_SPACE_MAXADDR_32BIT,	/* lowaddr */
50804f4d586SEd Maste 			       BUS_SPACE_MAXADDR, 	/* highaddr */
50904f4d586SEd Maste 			       NULL, NULL, 		/* filter, filterarg */
510*6f954fb3SAlexander Motin 			       sc->aac_max_sectors << 9, /* maxsize */
51104f4d586SEd Maste 			       sc->aac_sg_tablesize,	/* nsegments */
512*6f954fb3SAlexander Motin 			       BUS_SPACE_MAXSIZE_32BIT,	/* maxsegsize */
51304f4d586SEd Maste 			       BUS_DMA_ALLOCNOW,	/* flags */
51404f4d586SEd Maste 			       busdma_lock_mutex,	/* lockfunc */
51504f4d586SEd Maste 			       &sc->aac_io_lock,	/* lockfuncarg */
51604f4d586SEd Maste 			       &sc->aac_buffer_dmat)) {
51704f4d586SEd Maste 		device_printf(sc->aac_dev, "can't allocate buffer DMA tag\n");
51804f4d586SEd Maste 		return (ENOMEM);
51904f4d586SEd Maste 	}
52004f4d586SEd Maste 
52104f4d586SEd Maste 	/*
52204f4d586SEd Maste 	 * Create DMA tag for mapping FIBs into controller-addressable space..
52304f4d586SEd Maste 	 */
52404f4d586SEd Maste 	if (bus_dma_tag_create(sc->aac_parent_dmat,	/* parent */
52504f4d586SEd Maste 			       1, 0, 			/* algnmnt, boundary */
52604f4d586SEd Maste 			       (sc->flags & AAC_FLAGS_4GB_WINDOW) ?
52704f4d586SEd Maste 			       BUS_SPACE_MAXADDR_32BIT :
52804f4d586SEd Maste 			       0x7fffffff,		/* lowaddr */
52904f4d586SEd Maste 			       BUS_SPACE_MAXADDR, 	/* highaddr */
53004f4d586SEd Maste 			       NULL, NULL, 		/* filter, filterarg */
53104f4d586SEd Maste 			       sc->aac_max_fibs_alloc *
53204f4d586SEd Maste 			       sc->aac_max_fib_size,  /* maxsize */
53304f4d586SEd Maste 			       1,			/* nsegments */
53404f4d586SEd Maste 			       sc->aac_max_fibs_alloc *
53504f4d586SEd Maste 			       sc->aac_max_fib_size,	/* maxsize */
53604f4d586SEd Maste 			       0,			/* flags */
53704f4d586SEd Maste 			       NULL, NULL,		/* No locking needed */
53804f4d586SEd Maste 			       &sc->aac_fib_dmat)) {
539c2ede4b3SMartin Blapp 		device_printf(sc->aac_dev, "can't allocate FIB DMA tag\n");
54004f4d586SEd Maste 		return (ENOMEM);
54104f4d586SEd Maste 	}
54204f4d586SEd Maste 
54304f4d586SEd Maste 	/*
54404f4d586SEd Maste 	 * Create DMA tag for the common structure and allocate it.
54504f4d586SEd Maste 	 */
54604f4d586SEd Maste 	if (bus_dma_tag_create(sc->aac_parent_dmat, 	/* parent */
54704f4d586SEd Maste 			       1, 0,			/* algnmnt, boundary */
54804f4d586SEd Maste 			       (sc->flags & AAC_FLAGS_4GB_WINDOW) ?
54904f4d586SEd Maste 			       BUS_SPACE_MAXADDR_32BIT :
55004f4d586SEd Maste 			       0x7fffffff,		/* lowaddr */
55104f4d586SEd Maste 			       BUS_SPACE_MAXADDR, 	/* highaddr */
55204f4d586SEd Maste 			       NULL, NULL, 		/* filter, filterarg */
55304f4d586SEd Maste 			       8192 + sizeof(struct aac_common), /* maxsize */
55404f4d586SEd Maste 			       1,			/* nsegments */
55504f4d586SEd Maste 			       BUS_SPACE_MAXSIZE_32BIT,	/* maxsegsize */
55604f4d586SEd Maste 			       0,			/* flags */
55704f4d586SEd Maste 			       NULL, NULL,		/* No locking needed */
55804f4d586SEd Maste 			       &sc->aac_common_dmat)) {
55904f4d586SEd Maste 		device_printf(sc->aac_dev,
56004f4d586SEd Maste 			      "can't allocate common structure DMA tag\n");
56104f4d586SEd Maste 		return (ENOMEM);
56204f4d586SEd Maste 	}
56304f4d586SEd Maste 	if (bus_dmamem_alloc(sc->aac_common_dmat, (void **)&sc->aac_common,
56404f4d586SEd Maste 			     BUS_DMA_NOWAIT, &sc->aac_common_dmamap)) {
56504f4d586SEd Maste 		device_printf(sc->aac_dev, "can't allocate common structure\n");
56604f4d586SEd Maste 		return (ENOMEM);
56704f4d586SEd Maste 	}
56804f4d586SEd Maste 
56904f4d586SEd Maste 	/*
57004f4d586SEd Maste 	 * Work around a bug in the 2120 and 2200 that cannot DMA commands
57104f4d586SEd Maste 	 * below address 8192 in physical memory.
57204f4d586SEd Maste 	 * XXX If the padding is not needed, can it be put to use instead
57304f4d586SEd Maste 	 * of ignored?
57404f4d586SEd Maste 	 */
57504f4d586SEd Maste 	(void)bus_dmamap_load(sc->aac_common_dmat, sc->aac_common_dmamap,
57604f4d586SEd Maste 			sc->aac_common, 8192 + sizeof(*sc->aac_common),
57704f4d586SEd Maste 			aac_common_map, sc, 0);
57804f4d586SEd Maste 
57904f4d586SEd Maste 	if (sc->aac_common_busaddr < 8192) {
58004f4d586SEd Maste 		sc->aac_common = (struct aac_common *)
58104f4d586SEd Maste 		    ((uint8_t *)sc->aac_common + 8192);
58204f4d586SEd Maste 		sc->aac_common_busaddr += 8192;
58304f4d586SEd Maste 	}
58404f4d586SEd Maste 	bzero(sc->aac_common, sizeof(*sc->aac_common));
58504f4d586SEd Maste 
58604f4d586SEd Maste 	/* Allocate some FIBs and associated command structs */
58704f4d586SEd Maste 	TAILQ_INIT(&sc->aac_fibmap_tqh);
58804f4d586SEd Maste 	sc->aac_commands = malloc(sc->aac_max_fibs * sizeof(struct aac_command),
58904f4d586SEd Maste 				  M_AACBUF, M_WAITOK|M_ZERO);
59023e876b1SJung-uk Kim 	while (sc->total_fibs < sc->aac_max_fibs) {
59104f4d586SEd Maste 		if (aac_alloc_commands(sc) != 0)
59204f4d586SEd Maste 			break;
59304f4d586SEd Maste 	}
59404f4d586SEd Maste 	if (sc->total_fibs == 0)
59504f4d586SEd Maste 		return (ENOMEM);
59604f4d586SEd Maste 
59704f4d586SEd Maste 	return (0);
59804f4d586SEd Maste }
59904f4d586SEd Maste 
60004f4d586SEd Maste /*
60135863739SMike Smith  * Free all of the resources associated with (sc)
60235863739SMike Smith  *
60335863739SMike Smith  * Should not be called if the controller is active.
60435863739SMike Smith  */
60535863739SMike Smith void
60635863739SMike Smith aac_free(struct aac_softc *sc)
60735863739SMike Smith {
608ffb37f33SScott Long 
60931a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
61035863739SMike Smith 
61135863739SMike Smith 	/* remove the control device */
61235863739SMike Smith 	if (sc->aac_dev_t != NULL)
61335863739SMike Smith 		destroy_dev(sc->aac_dev_t);
61435863739SMike Smith 
6150b94a66eSMike Smith 	/* throw away any FIB buffers, discard the FIB DMA tag */
6168480cc63SScott Long 	aac_free_commands(sc);
6170b94a66eSMike Smith 	if (sc->aac_fib_dmat)
6180b94a66eSMike Smith 		bus_dma_tag_destroy(sc->aac_fib_dmat);
61935863739SMike Smith 
620ffb37f33SScott Long 	free(sc->aac_commands, M_AACBUF);
621ffb37f33SScott Long 
62235863739SMike Smith 	/* destroy the common area */
62335863739SMike Smith 	if (sc->aac_common) {
62435863739SMike Smith 		bus_dmamap_unload(sc->aac_common_dmat, sc->aac_common_dmamap);
625c6eafcf2SScott Long 		bus_dmamem_free(sc->aac_common_dmat, sc->aac_common,
626c6eafcf2SScott Long 				sc->aac_common_dmamap);
62735863739SMike Smith 	}
6280b94a66eSMike Smith 	if (sc->aac_common_dmat)
6290b94a66eSMike Smith 		bus_dma_tag_destroy(sc->aac_common_dmat);
63035863739SMike Smith 
63135863739SMike Smith 	/* disconnect the interrupt handler */
63235863739SMike Smith 	if (sc->aac_intr)
63335863739SMike Smith 		bus_teardown_intr(sc->aac_dev, sc->aac_irq, sc->aac_intr);
634bbc03f1bSMarius Strobl 	if (sc->aac_irq != NULL) {
635da4882c2SMarius Strobl 		bus_release_resource(sc->aac_dev, SYS_RES_IRQ,
636da4882c2SMarius Strobl 		    rman_get_rid(sc->aac_irq), sc->aac_irq);
637bbc03f1bSMarius Strobl 		pci_release_msi(sc->aac_dev);
638bbc03f1bSMarius Strobl 	}
63935863739SMike Smith 
64035863739SMike Smith 	/* destroy data-transfer DMA tag */
64135863739SMike Smith 	if (sc->aac_buffer_dmat)
64235863739SMike Smith 		bus_dma_tag_destroy(sc->aac_buffer_dmat);
64335863739SMike Smith 
64435863739SMike Smith 	/* destroy the parent DMA tag */
64535863739SMike Smith 	if (sc->aac_parent_dmat)
64635863739SMike Smith 		bus_dma_tag_destroy(sc->aac_parent_dmat);
64735863739SMike Smith 
64835863739SMike Smith 	/* release the register window mapping */
649ff0991c4SAttilio Rao 	if (sc->aac_regs_res0 != NULL)
650914da7d0SScott Long 		bus_release_resource(sc->aac_dev, SYS_RES_MEMORY,
651da4882c2SMarius Strobl 		    rman_get_rid(sc->aac_regs_res0), sc->aac_regs_res0);
652ff0991c4SAttilio Rao 	if (sc->aac_hwif == AAC_HWIF_NARK && sc->aac_regs_res1 != NULL)
653ff0991c4SAttilio Rao 		bus_release_resource(sc->aac_dev, SYS_RES_MEMORY,
654da4882c2SMarius Strobl 		    rman_get_rid(sc->aac_regs_res1), sc->aac_regs_res1);
65535863739SMike Smith }
65635863739SMike Smith 
657914da7d0SScott Long /*
65835863739SMike Smith  * Disconnect from the controller completely, in preparation for unload.
65935863739SMike Smith  */
66035863739SMike Smith int
66135863739SMike Smith aac_detach(device_t dev)
66235863739SMike Smith {
663914da7d0SScott Long 	struct aac_softc *sc;
66470545d1aSScott Long 	struct aac_container *co;
66570545d1aSScott Long 	struct aac_sim	*sim;
66635863739SMike Smith 	int error;
66735863739SMike Smith 
668914da7d0SScott Long 	sc = device_get_softc(dev);
66931a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
670914da7d0SScott Long 
671ff0991c4SAttilio Rao 	callout_drain(&sc->aac_daemontime);
672ff0991c4SAttilio Rao 
6731bd320ecSAttilio Rao 	mtx_lock(&sc->aac_io_lock);
6741bd320ecSAttilio Rao 	while (sc->aifflags & AAC_AIFFLAGS_RUNNING) {
6751bd320ecSAttilio Rao 		sc->aifflags |= AAC_AIFFLAGS_EXIT;
6761bd320ecSAttilio Rao 		wakeup(sc->aifthread);
6771bd320ecSAttilio Rao 		msleep(sc->aac_dev, &sc->aac_io_lock, PUSER, "aacdch", 0);
6781bd320ecSAttilio Rao 	}
6791bd320ecSAttilio Rao 	mtx_unlock(&sc->aac_io_lock);
6801bd320ecSAttilio Rao 	KASSERT((sc->aifflags & AAC_AIFFLAGS_RUNNING) == 0,
6811bd320ecSAttilio Rao 	    ("%s: invalid detach state", __func__));
6821bd320ecSAttilio Rao 
68370545d1aSScott Long 	/* Remove the child containers */
684a761a1caSScott Long 	while ((co = TAILQ_FIRST(&sc->aac_container_tqh)) != NULL) {
68570545d1aSScott Long 		error = device_delete_child(dev, co->co_disk);
68670545d1aSScott Long 		if (error)
68770545d1aSScott Long 			return (error);
68865ac4ed6SScott Long 		TAILQ_REMOVE(&sc->aac_container_tqh, co, co_link);
689a761a1caSScott Long 		free(co, M_AACBUF);
69070545d1aSScott Long 	}
69170545d1aSScott Long 
69270545d1aSScott Long 	/* Remove the CAM SIMs */
693a761a1caSScott Long 	while ((sim = TAILQ_FIRST(&sc->aac_sim_tqh)) != NULL) {
694a761a1caSScott Long 		TAILQ_REMOVE(&sc->aac_sim_tqh, sim, sim_link);
69570545d1aSScott Long 		error = device_delete_child(dev, sim->sim_dev);
69670545d1aSScott Long 		if (error)
69770545d1aSScott Long 			return (error);
698a761a1caSScott Long 		free(sim, M_AACBUF);
69970545d1aSScott Long 	}
70070545d1aSScott Long 
70135863739SMike Smith 	if ((error = aac_shutdown(dev)))
70235863739SMike Smith 		return(error);
70335863739SMike Smith 
7045f54d522SScott Long 	EVENTHANDLER_DEREGISTER(shutdown_final, sc->eh);
7055f54d522SScott Long 
70635863739SMike Smith 	aac_free(sc);
70735863739SMike Smith 
708dc9efde5SScott Long 	mtx_destroy(&sc->aac_aifq_lock);
709dc9efde5SScott Long 	mtx_destroy(&sc->aac_io_lock);
710dc9efde5SScott Long 	mtx_destroy(&sc->aac_container_lock);
711dc9efde5SScott Long 
71235863739SMike Smith 	return(0);
71335863739SMike Smith }
71435863739SMike Smith 
715914da7d0SScott Long /*
71635863739SMike Smith  * Bring the controller down to a dormant state and detach all child devices.
71735863739SMike Smith  *
71835863739SMike Smith  * This function is called before detach or system shutdown.
71935863739SMike Smith  *
7200b94a66eSMike Smith  * Note that we can assume that the bioq on the controller is empty, as we won't
72135863739SMike Smith  * allow shutdown if any device is open.
72235863739SMike Smith  */
72335863739SMike Smith int
72435863739SMike Smith aac_shutdown(device_t dev)
72535863739SMike Smith {
726914da7d0SScott Long 	struct aac_softc *sc;
727cbfd045bSScott Long 	struct aac_fib *fib;
728cbfd045bSScott Long 	struct aac_close_command *cc;
72935863739SMike Smith 
730914da7d0SScott Long 	sc = device_get_softc(dev);
73131a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
732914da7d0SScott Long 
73335863739SMike Smith 	sc->aac_state |= AAC_STATE_SUSPEND;
73435863739SMike Smith 
73535863739SMike Smith 	/*
73635863739SMike Smith 	 * Send a Container shutdown followed by a HostShutdown FIB to the
73735863739SMike Smith 	 * controller to convince it that we don't want to talk to it anymore.
73835863739SMike Smith 	 * We've been closed and all I/O completed already
73935863739SMike Smith 	 */
74035863739SMike Smith 	device_printf(sc->aac_dev, "shutting down controller...");
74135863739SMike Smith 
7427cb209f5SScott Long 	mtx_lock(&sc->aac_io_lock);
74303b5fe51SScott Long 	aac_alloc_sync_fib(sc, &fib);
744cbfd045bSScott Long 	cc = (struct aac_close_command *)&fib->data[0];
745cbfd045bSScott Long 
74639ee03c3SScott Long 	bzero(cc, sizeof(struct aac_close_command));
747cbfd045bSScott Long 	cc->Command = VM_CloseAll;
748cbfd045bSScott Long 	cc->ContainerId = 0xffffffff;
749cbfd045bSScott Long 	if (aac_sync_fib(sc, ContainerCommand, 0, fib,
750cbfd045bSScott Long 	    sizeof(struct aac_close_command)))
75135863739SMike Smith 		printf("FAILED.\n");
75270545d1aSScott Long 	else
75370545d1aSScott Long 		printf("done\n");
75470545d1aSScott Long #if 0
755914da7d0SScott Long 	else {
756cbfd045bSScott Long 		fib->data[0] = 0;
75736e0bf6eSScott Long 		/*
758914da7d0SScott Long 		 * XXX Issuing this command to the controller makes it shut down
75936e0bf6eSScott Long 		 * but also keeps it from coming back up without a reset of the
76036e0bf6eSScott Long 		 * PCI bus.  This is not desirable if you are just unloading the
76136e0bf6eSScott Long 		 * driver module with the intent to reload it later.
76236e0bf6eSScott Long 		 */
763cbfd045bSScott Long 		if (aac_sync_fib(sc, FsaHostShutdown, AAC_FIBSTATE_SHUTDOWN,
764cbfd045bSScott Long 		    fib, 1)) {
76535863739SMike Smith 			printf("FAILED.\n");
76635863739SMike Smith 		} else {
76735863739SMike Smith 			printf("done.\n");
76835863739SMike Smith 		}
76935863739SMike Smith 	}
77070545d1aSScott Long #endif
77135863739SMike Smith 
77235863739SMike Smith 	AAC_MASK_INTERRUPTS(sc);
7733576af8fSScott Long 	aac_release_sync_fib(sc);
7747cb209f5SScott Long 	mtx_unlock(&sc->aac_io_lock);
77535863739SMike Smith 
77635863739SMike Smith 	return(0);
77735863739SMike Smith }
77835863739SMike Smith 
779914da7d0SScott Long /*
78035863739SMike Smith  * Bring the controller to a quiescent state, ready for system suspend.
78135863739SMike Smith  */
78235863739SMike Smith int
78335863739SMike Smith aac_suspend(device_t dev)
78435863739SMike Smith {
785914da7d0SScott Long 	struct aac_softc *sc;
78635863739SMike Smith 
787914da7d0SScott Long 	sc = device_get_softc(dev);
788914da7d0SScott Long 
78931a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
79035863739SMike Smith 	sc->aac_state |= AAC_STATE_SUSPEND;
79135863739SMike Smith 
79235863739SMike Smith 	AAC_MASK_INTERRUPTS(sc);
79335863739SMike Smith 	return(0);
79435863739SMike Smith }
79535863739SMike Smith 
796914da7d0SScott Long /*
79735863739SMike Smith  * Bring the controller back to a state ready for operation.
79835863739SMike Smith  */
79935863739SMike Smith int
80035863739SMike Smith aac_resume(device_t dev)
80135863739SMike Smith {
802914da7d0SScott Long 	struct aac_softc *sc;
80335863739SMike Smith 
804914da7d0SScott Long 	sc = device_get_softc(dev);
805914da7d0SScott Long 
80631a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
80735863739SMike Smith 	sc->aac_state &= ~AAC_STATE_SUSPEND;
80835863739SMike Smith 	AAC_UNMASK_INTERRUPTS(sc);
80935863739SMike Smith 	return(0);
81035863739SMike Smith }
81135863739SMike Smith 
812914da7d0SScott Long /*
8137cb209f5SScott Long  * Interrupt handler for NEW_COMM interface.
81435863739SMike Smith  */
81535863739SMike Smith void
8167cb209f5SScott Long aac_new_intr(void *arg)
8177cb209f5SScott Long {
8187cb209f5SScott Long 	struct aac_softc *sc;
8197cb209f5SScott Long 	u_int32_t index, fast;
8207cb209f5SScott Long 	struct aac_command *cm;
8217cb209f5SScott Long 	struct aac_fib *fib;
8227cb209f5SScott Long 	int i;
8237cb209f5SScott Long 
8247cb209f5SScott Long 	sc = (struct aac_softc *)arg;
8257cb209f5SScott Long 
82631a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
8277cb209f5SScott Long 	mtx_lock(&sc->aac_io_lock);
8287cb209f5SScott Long 	while (1) {
8297cb209f5SScott Long 		index = AAC_GET_OUTB_QUEUE(sc);
8307cb209f5SScott Long 		if (index == 0xffffffff)
8317cb209f5SScott Long 			index = AAC_GET_OUTB_QUEUE(sc);
8327cb209f5SScott Long 		if (index == 0xffffffff)
8337cb209f5SScott Long 			break;
8347cb209f5SScott Long 		if (index & 2) {
8357cb209f5SScott Long 			if (index == 0xfffffffe) {
8367cb209f5SScott Long 				/* XXX This means that the controller wants
8377cb209f5SScott Long 				 * more work.  Ignore it for now.
8387cb209f5SScott Long 				 */
8397cb209f5SScott Long 				continue;
8407cb209f5SScott Long 			}
8417cb209f5SScott Long 			/* AIF */
8427cb209f5SScott Long 			fib = (struct aac_fib *)malloc(sizeof *fib, M_AACBUF,
8437cb209f5SScott Long 				   M_NOWAIT | M_ZERO);
8447cb209f5SScott Long 			if (fib == NULL) {
8457cb209f5SScott Long 				/* If we're really this short on memory,
8467cb209f5SScott Long 				 * hopefully breaking out of the handler will
8477cb209f5SScott Long 				 * allow something to get freed.  This
8487cb209f5SScott Long 				 * actually sucks a whole lot.
8497cb209f5SScott Long 				 */
8507cb209f5SScott Long 				break;
8517cb209f5SScott Long 			}
8527cb209f5SScott Long 			index &= ~2;
8537cb209f5SScott Long 			for (i = 0; i < sizeof(struct aac_fib)/4; ++i)
854ff0991c4SAttilio Rao 				((u_int32_t *)fib)[i] = AAC_MEM1_GETREG4(sc, index + i*4);
8557cb209f5SScott Long 			aac_handle_aif(sc, fib);
8567cb209f5SScott Long 			free(fib, M_AACBUF);
8577cb209f5SScott Long 
8587cb209f5SScott Long 			/*
8597cb209f5SScott Long 			 * AIF memory is owned by the adapter, so let it
8607cb209f5SScott Long 			 * know that we are done with it.
8617cb209f5SScott Long 			 */
8627cb209f5SScott Long 			AAC_SET_OUTB_QUEUE(sc, index);
8637cb209f5SScott Long 			AAC_CLEAR_ISTATUS(sc, AAC_DB_RESPONSE_READY);
8647cb209f5SScott Long 		} else {
8657cb209f5SScott Long 			fast = index & 1;
8667cb209f5SScott Long 			cm = sc->aac_commands + (index >> 2);
8677cb209f5SScott Long 			fib = cm->cm_fib;
8687cb209f5SScott Long 			if (fast) {
8697cb209f5SScott Long 				fib->Header.XferState |= AAC_FIBSTATE_DONEADAP;
8707cb209f5SScott Long 				*((u_int32_t *)(fib->data)) = AAC_ERROR_NORMAL;
8717cb209f5SScott Long 			}
8727cb209f5SScott Long 			aac_remove_busy(cm);
8737cb209f5SScott Long  			aac_unmap_command(cm);
8747cb209f5SScott Long 			cm->cm_flags |= AAC_CMD_COMPLETED;
8757cb209f5SScott Long 
8767cb209f5SScott Long 			/* is there a completion handler? */
8777cb209f5SScott Long 			if (cm->cm_complete != NULL) {
8787cb209f5SScott Long 				cm->cm_complete(cm);
8797cb209f5SScott Long 			} else {
8807cb209f5SScott Long 				/* assume that someone is sleeping on this
8817cb209f5SScott Long 				 * command
8827cb209f5SScott Long 				 */
8837cb209f5SScott Long 				wakeup(cm);
8847cb209f5SScott Long 			}
8857cb209f5SScott Long 			sc->flags &= ~AAC_QUEUE_FRZN;
8867cb209f5SScott Long 		}
8877cb209f5SScott Long 	}
8887cb209f5SScott Long 	/* see if we can start some more I/O */
8897cb209f5SScott Long 	if ((sc->flags & AAC_QUEUE_FRZN) == 0)
8907cb209f5SScott Long 		aac_startio(sc);
8917cb209f5SScott Long 
8927cb209f5SScott Long 	mtx_unlock(&sc->aac_io_lock);
8937cb209f5SScott Long }
8947cb209f5SScott Long 
895e46b9eeaSEd Maste /*
896e46b9eeaSEd Maste  * Interrupt filter for !NEW_COMM interface.
897e46b9eeaSEd Maste  */
898ef544f63SPaolo Pisati int
899e46b9eeaSEd Maste aac_filter(void *arg)
90035863739SMike Smith {
901914da7d0SScott Long 	struct aac_softc *sc;
90270545d1aSScott Long 	u_int16_t reason;
90335863739SMike Smith 
904914da7d0SScott Long 	sc = (struct aac_softc *)arg;
905914da7d0SScott Long 
90631a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
907f30ac74cSScott Long 	/*
9089148fa21SScott Long 	 * Read the status register directly.  This is faster than taking the
9099148fa21SScott Long 	 * driver lock and reading the queues directly.  It also saves having
9109148fa21SScott Long 	 * to turn parts of the driver lock into a spin mutex, which would be
9119148fa21SScott Long 	 * ugly.
912f30ac74cSScott Long 	 */
91335863739SMike Smith 	reason = AAC_GET_ISTATUS(sc);
914f30ac74cSScott Long 	AAC_CLEAR_ISTATUS(sc, reason);
915f30ac74cSScott Long 
9169c3a7fceSScott Long 	/* handle completion processing */
9179148fa21SScott Long 	if (reason & AAC_DB_RESPONSE_READY)
9189148fa21SScott Long 		taskqueue_enqueue_fast(taskqueue_fast, &sc->aac_task_complete);
91935863739SMike Smith 
9209148fa21SScott Long 	/* controller wants to talk to us */
9219148fa21SScott Long 	if (reason & (AAC_DB_PRINTF | AAC_DB_COMMAND_READY)) {
92270545d1aSScott Long 		/*
9239148fa21SScott Long 		 * XXX Make sure that we don't get fooled by strange messages
9249148fa21SScott Long 		 * that start with a NULL.
92570545d1aSScott Long 		 */
9269148fa21SScott Long 		if ((reason & AAC_DB_PRINTF) &&
9279148fa21SScott Long 			(sc->aac_common->ac_printf[0] == 0))
9289148fa21SScott Long 			sc->aac_common->ac_printf[0] = 32;
92970545d1aSScott Long 
9309148fa21SScott Long 		/*
9319148fa21SScott Long 		 * This might miss doing the actual wakeup.  However, the
932a32a982dSScott Long 		 * msleep that this is waking up has a timeout, so it will
9339148fa21SScott Long 		 * wake up eventually.  AIFs and printfs are low enough
9349148fa21SScott Long 		 * priority that they can handle hanging out for a few seconds
9359148fa21SScott Long 		 * if needed.
9369148fa21SScott Long 		 */
93736e0bf6eSScott Long 		wakeup(sc->aifthread);
93836e0bf6eSScott Long 	}
939ef544f63SPaolo Pisati 	return (FILTER_HANDLED);
9409148fa21SScott Long }
94135863739SMike Smith 
942c6eafcf2SScott Long /*
943914da7d0SScott Long  * Command Processing
944914da7d0SScott Long  */
94535863739SMike Smith 
946914da7d0SScott Long /*
94735863739SMike Smith  * Start as much queued I/O as possible on the controller
94835863739SMike Smith  */
949fe3cb0e1SScott Long void
95035863739SMike Smith aac_startio(struct aac_softc *sc)
95135863739SMike Smith {
95235863739SMike Smith 	struct aac_command *cm;
953397fa34fSScott Long 	int error;
95435863739SMike Smith 
95531a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
95635863739SMike Smith 
95735863739SMike Smith 	for (;;) {
958914da7d0SScott Long 		/*
959397fa34fSScott Long 		 * This flag might be set if the card is out of resources.
960397fa34fSScott Long 		 * Checking it here prevents an infinite loop of deferrals.
961397fa34fSScott Long 		 */
962397fa34fSScott Long 		if (sc->flags & AAC_QUEUE_FRZN)
963397fa34fSScott Long 			break;
964397fa34fSScott Long 
965397fa34fSScott Long 		/*
966914da7d0SScott Long 		 * Try to get a command that's been put off for lack of
967914da7d0SScott Long 		 * resources
968914da7d0SScott Long 		 */
96935863739SMike Smith 		cm = aac_dequeue_ready(sc);
97035863739SMike Smith 
971914da7d0SScott Long 		/*
972914da7d0SScott Long 		 * Try to build a command off the bio queue (ignore error
973914da7d0SScott Long 		 * return)
974914da7d0SScott Long 		 */
9750b94a66eSMike Smith 		if (cm == NULL)
97635863739SMike Smith 			aac_bio_command(sc, &cm);
97735863739SMike Smith 
97835863739SMike Smith 		/* nothing to do? */
97935863739SMike Smith 		if (cm == NULL)
98035863739SMike Smith 			break;
98135863739SMike Smith 
982cd481291SScott Long 		/* don't map more than once */
983cd481291SScott Long 		if (cm->cm_flags & AAC_CMD_MAPPED)
9844102d44bSScott Long 			panic("aac: command %p already mapped", cm);
98535863739SMike Smith 
986397fa34fSScott Long 		/*
987397fa34fSScott Long 		 * Set up the command to go to the controller.  If there are no
988397fa34fSScott Long 		 * data buffers associated with the command then it can bypass
989397fa34fSScott Long 		 * busdma.
990397fa34fSScott Long 		 */
991cd481291SScott Long 		if (cm->cm_datalen != 0) {
9928fce673cSMarius Strobl 			if (cm->cm_flags & AAC_REQ_BIO)
9938fce673cSMarius Strobl 				error = bus_dmamap_load_bio(
9948fce673cSMarius Strobl 				    sc->aac_buffer_dmat, cm->cm_datamap,
9958fce673cSMarius Strobl 				    (struct bio *)cm->cm_private,
9968fce673cSMarius Strobl 				    aac_map_command_sg, cm, 0);
9978fce673cSMarius Strobl 			else
998397fa34fSScott Long 				error = bus_dmamap_load(sc->aac_buffer_dmat,
999397fa34fSScott Long 				    cm->cm_datamap, cm->cm_data,
10008fce673cSMarius Strobl 				    cm->cm_datalen, aac_map_command_sg, cm, 0);
1001cd481291SScott Long 			if (error == EINPROGRESS) {
100231a0399eSEd Maste 				fwprintf(sc, HBA_FLAGS_DBG_COMM_B, "freezing queue\n");
1003cd481291SScott Long 				sc->flags |= AAC_QUEUE_FRZN;
1004614c22b2SScott Long 			} else if (error != 0)
1005397fa34fSScott Long 				panic("aac_startio: unexpected error %d from "
1006a620bad0SEd Maste 				      "busdma", error);
1007397fa34fSScott Long 		} else
10088778f63dSScott Long 			aac_map_command_sg(cm, NULL, 0, 0);
1009cd481291SScott Long 	}
101035863739SMike Smith }
101135863739SMike Smith 
1012914da7d0SScott Long /*
101335863739SMike Smith  * Handle notification of one or more FIBs coming from the controller.
101435863739SMike Smith  */
101535863739SMike Smith static void
101670545d1aSScott Long aac_command_thread(struct aac_softc *sc)
101735863739SMike Smith {
101835863739SMike Smith 	struct aac_fib *fib;
101935863739SMike Smith 	u_int32_t fib_size;
10209148fa21SScott Long 	int size, retval;
102135863739SMike Smith 
102231a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
102335863739SMike Smith 
1024bb6fe253SScott Long 	mtx_lock(&sc->aac_io_lock);
1025a32a982dSScott Long 	sc->aifflags = AAC_AIFFLAGS_RUNNING;
102636e0bf6eSScott Long 
1027a32a982dSScott Long 	while ((sc->aifflags & AAC_AIFFLAGS_EXIT) == 0) {
1028a32a982dSScott Long 
1029a32a982dSScott Long 		retval = 0;
1030a32a982dSScott Long 		if ((sc->aifflags & AAC_AIFFLAGS_PENDING) == 0)
1031a32a982dSScott Long 			retval = msleep(sc->aifthread, &sc->aac_io_lock, PRIBIO,
1032a32a982dSScott Long 					"aifthd", AAC_PERIODIC_INTERVAL * hz);
103336e0bf6eSScott Long 
10349148fa21SScott Long 		/*
10359148fa21SScott Long 		 * First see if any FIBs need to be allocated.  This needs
10369148fa21SScott Long 		 * to be called without the driver lock because contigmalloc
10371bd320ecSAttilio Rao 		 * can sleep.
10389148fa21SScott Long 		 */
10399148fa21SScott Long 		if ((sc->aifflags & AAC_AIFFLAGS_ALLOCFIBS) != 0) {
1040bb6fe253SScott Long 			mtx_unlock(&sc->aac_io_lock);
1041a32a982dSScott Long 			aac_alloc_commands(sc);
1042bb6fe253SScott Long 			mtx_lock(&sc->aac_io_lock);
10434102d44bSScott Long 			sc->aifflags &= ~AAC_AIFFLAGS_ALLOCFIBS;
1044a32a982dSScott Long 			aac_startio(sc);
1045a32a982dSScott Long 		}
10469148fa21SScott Long 
10479148fa21SScott Long 		/*
10489148fa21SScott Long 		 * While we're here, check to see if any commands are stuck.
10499148fa21SScott Long 		 * This is pretty low-priority, so it's ok if it doesn't
10509148fa21SScott Long 		 * always fire.
10519148fa21SScott Long 		 */
10529148fa21SScott Long 		if (retval == EWOULDBLOCK)
105370545d1aSScott Long 			aac_timeout(sc);
105470545d1aSScott Long 
105570545d1aSScott Long 		/* Check the hardware printf message buffer */
10569148fa21SScott Long 		if (sc->aac_common->ac_printf[0] != 0)
105770545d1aSScott Long 			aac_print_printf(sc);
105870545d1aSScott Long 
10599148fa21SScott Long 		/* Also check to see if the adapter has a command for us. */
10607cb209f5SScott Long 		if (sc->flags & AAC_FLAGS_NEW_COMM)
10617cb209f5SScott Long 			continue;
10627cb209f5SScott Long 		for (;;) {
10637cb209f5SScott Long 			if (aac_dequeue_fib(sc, AAC_HOST_NORM_CMD_QUEUE,
10647cb209f5SScott Long 					   &fib_size, &fib))
10657cb209f5SScott Long 				break;
106635863739SMike Smith 
106736e0bf6eSScott Long 			AAC_PRINT_FIB(sc, fib);
106836e0bf6eSScott Long 
106935863739SMike Smith 			switch (fib->Header.Command) {
107035863739SMike Smith 			case AifRequest:
107136e0bf6eSScott Long 				aac_handle_aif(sc, fib);
107235863739SMike Smith 				break;
107335863739SMike Smith 			default:
1074914da7d0SScott Long 				device_printf(sc->aac_dev, "unknown command "
1075914da7d0SScott Long 					      "from controller\n");
107635863739SMike Smith 				break;
107735863739SMike Smith 			}
107835863739SMike Smith 
107936e0bf6eSScott Long 			if ((fib->Header.XferState == 0) ||
10807cb209f5SScott Long 			    (fib->Header.StructType != AAC_FIBTYPE_TFIB)) {
108136e0bf6eSScott Long 				break;
10827cb209f5SScott Long 			}
108336e0bf6eSScott Long 
108470545d1aSScott Long 			/* Return the AIF to the controller. */
108536e0bf6eSScott Long 			if (fib->Header.XferState & AAC_FIBSTATE_FROMADAP) {
108636e0bf6eSScott Long 				fib->Header.XferState |= AAC_FIBSTATE_DONEHOST;
108736e0bf6eSScott Long 				*(AAC_FSAStatus*)fib->data = ST_OK;
108836e0bf6eSScott Long 
108936e0bf6eSScott Long 				/* XXX Compute the Size field? */
109036e0bf6eSScott Long 				size = fib->Header.Size;
109136e0bf6eSScott Long 				if (size > sizeof(struct aac_fib)) {
109236e0bf6eSScott Long 					size = sizeof(struct aac_fib);
109336e0bf6eSScott Long 					fib->Header.Size = size;
109436e0bf6eSScott Long 				}
109536e0bf6eSScott Long 				/*
1096914da7d0SScott Long 				 * Since we did not generate this command, it
1097914da7d0SScott Long 				 * cannot go through the normal
1098914da7d0SScott Long 				 * enqueue->startio chain.
109936e0bf6eSScott Long 				 */
1100914da7d0SScott Long 				aac_enqueue_response(sc,
1101914da7d0SScott Long 						 AAC_ADAP_NORM_RESP_QUEUE,
1102914da7d0SScott Long 						 fib);
110336e0bf6eSScott Long 			}
110436e0bf6eSScott Long 		}
110536e0bf6eSScott Long 	}
110636e0bf6eSScott Long 	sc->aifflags &= ~AAC_AIFFLAGS_RUNNING;
1107bb6fe253SScott Long 	mtx_unlock(&sc->aac_io_lock);
110836e0bf6eSScott Long 	wakeup(sc->aac_dev);
110936e0bf6eSScott Long 
11103745c395SJulian Elischer 	kproc_exit(0);
111135863739SMike Smith }
111235863739SMike Smith 
1113914da7d0SScott Long /*
11149c3a7fceSScott Long  * Process completed commands.
111535863739SMike Smith  */
111635863739SMike Smith static void
11179c3a7fceSScott Long aac_complete(void *context, int pending)
111835863739SMike Smith {
11199c3a7fceSScott Long 	struct aac_softc *sc;
112035863739SMike Smith 	struct aac_command *cm;
112135863739SMike Smith 	struct aac_fib *fib;
112235863739SMike Smith 	u_int32_t fib_size;
112335863739SMike Smith 
11249c3a7fceSScott Long 	sc = (struct aac_softc *)context;
112531a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
11269c3a7fceSScott Long 
1127bb6fe253SScott Long 	mtx_lock(&sc->aac_io_lock);
1128ae543596SScott Long 
11299c3a7fceSScott Long 	/* pull completed commands off the queue */
113035863739SMike Smith 	for (;;) {
113135863739SMike Smith 		/* look for completed FIBs on our queue */
1132914da7d0SScott Long 		if (aac_dequeue_fib(sc, AAC_HOST_NORM_RESP_QUEUE, &fib_size,
1133914da7d0SScott Long 							&fib))
113435863739SMike Smith 			break;	/* nothing to do */
113535863739SMike Smith 
1136ecd1c51fSScott Long 		/* get the command, unmap and hand off for processing */
1137cb0d64b9SScott Long 		cm = sc->aac_commands + fib->Header.SenderData;
113835863739SMike Smith 		if (cm == NULL) {
113935863739SMike Smith 			AAC_PRINT_FIB(sc, fib);
11409c3a7fceSScott Long 			break;
11419c3a7fceSScott Long 		}
11423e507710SEd Maste 		if ((cm->cm_flags & AAC_CMD_TIMEDOUT) != 0)
11433e507710SEd Maste 			device_printf(sc->aac_dev,
11443e507710SEd Maste 			    "COMMAND %p COMPLETED AFTER %d SECONDS\n",
11453e507710SEd Maste 			    cm, (int)(time_uptime-cm->cm_timestamp));
11463e507710SEd Maste 
11470b94a66eSMike Smith 		aac_remove_busy(cm);
11487cb209f5SScott Long 
1149ecd1c51fSScott Long  		aac_unmap_command(cm);
115035863739SMike Smith 		cm->cm_flags |= AAC_CMD_COMPLETED;
115135863739SMike Smith 
115235863739SMike Smith 		/* is there a completion handler? */
115335863739SMike Smith 		if (cm->cm_complete != NULL) {
115435863739SMike Smith 			cm->cm_complete(cm);
115535863739SMike Smith 		} else {
115635863739SMike Smith 			/* assume that someone is sleeping on this command */
115735863739SMike Smith 			wakeup(cm);
115835863739SMike Smith 		}
115935863739SMike Smith 	}
11600b94a66eSMike Smith 
11610b94a66eSMike Smith 	/* see if we can start some more I/O */
1162cd481291SScott Long 	sc->flags &= ~AAC_QUEUE_FRZN;
11630b94a66eSMike Smith 	aac_startio(sc);
1164ae543596SScott Long 
1165bb6fe253SScott Long 	mtx_unlock(&sc->aac_io_lock);
116635863739SMike Smith }
116735863739SMike Smith 
1168914da7d0SScott Long /*
116935863739SMike Smith  * Handle a bio submitted from a disk device.
117035863739SMike Smith  */
117135863739SMike Smith void
117235863739SMike Smith aac_submit_bio(struct bio *bp)
117335863739SMike Smith {
1174914da7d0SScott Long 	struct aac_disk *ad;
1175914da7d0SScott Long 	struct aac_softc *sc;
117635863739SMike Smith 
11777540e65eSScott Long 	ad = (struct aac_disk *)bp->bio_disk->d_drv1;
1178914da7d0SScott Long 	sc = ad->ad_controller;
117931a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
1180914da7d0SScott Long 
118135863739SMike Smith 	/* queue the BIO and try to get some work done */
11820b94a66eSMike Smith 	aac_enqueue_bio(sc, bp);
118335863739SMike Smith 	aac_startio(sc);
118435863739SMike Smith }
118535863739SMike Smith 
1186914da7d0SScott Long /*
118735863739SMike Smith  * Get a bio and build a command to go with it.
118835863739SMike Smith  */
118935863739SMike Smith static int
119035863739SMike Smith aac_bio_command(struct aac_softc *sc, struct aac_command **cmp)
119135863739SMike Smith {
119235863739SMike Smith 	struct aac_command *cm;
119335863739SMike Smith 	struct aac_fib *fib;
119435863739SMike Smith 	struct aac_disk *ad;
119535863739SMike Smith 	struct bio *bp;
119635863739SMike Smith 
119731a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
119835863739SMike Smith 
119935863739SMike Smith 	/* get the resources we will need */
120035863739SMike Smith 	cm = NULL;
1201a32a982dSScott Long 	bp = NULL;
120235863739SMike Smith 	if (aac_alloc_command(sc, &cm))	/* get a command */
120335863739SMike Smith 		goto fail;
1204a32a982dSScott Long 	if ((bp = aac_dequeue_bio(sc)) == NULL)
1205a32a982dSScott Long 		goto fail;
120635863739SMike Smith 
120735863739SMike Smith 	/* fill out the command */
12080b94a66eSMike Smith 	cm->cm_datalen = bp->bio_bcount;
12090b94a66eSMike Smith 	cm->cm_complete = aac_bio_complete;
12108fce673cSMarius Strobl 	cm->cm_flags = AAC_REQ_BIO;
121135863739SMike Smith 	cm->cm_private = bp;
12122b3b0f17SScott Long 	cm->cm_timestamp = time_uptime;
121335863739SMike Smith 
121435863739SMike Smith 	/* build the FIB */
121535863739SMike Smith 	fib = cm->cm_fib;
1216b85f5808SScott Long 	fib->Header.Size = sizeof(struct aac_fib_header);
121735863739SMike Smith 	fib->Header.XferState =
121835863739SMike Smith 		AAC_FIBSTATE_HOSTOWNED   |
121935863739SMike Smith 		AAC_FIBSTATE_INITIALISED |
1220f30ac74cSScott Long 		AAC_FIBSTATE_EMPTY	 |
122135863739SMike Smith 		AAC_FIBSTATE_FROMHOST	 |
122235863739SMike Smith 		AAC_FIBSTATE_REXPECTED   |
1223f30ac74cSScott Long 		AAC_FIBSTATE_NORM	 |
1224f30ac74cSScott Long 		AAC_FIBSTATE_ASYNC	 |
1225f30ac74cSScott Long 		AAC_FIBSTATE_FAST_RESPONSE;
122635863739SMike Smith 
122735863739SMike Smith 	/* build the read/write request */
12287540e65eSScott Long 	ad = (struct aac_disk *)bp->bio_disk->d_drv1;
1229b85f5808SScott Long 
12307cb209f5SScott Long 	if (sc->flags & AAC_FLAGS_RAW_IO) {
12317cb209f5SScott Long 		struct aac_raw_io *raw;
12327cb209f5SScott Long 		raw = (struct aac_raw_io *)&fib->data[0];
12337cb209f5SScott Long 		fib->Header.Command = RawIo;
12347cb209f5SScott Long 		raw->BlockNumber = (u_int64_t)bp->bio_pblkno;
12357cb209f5SScott Long 		raw->ByteCount = bp->bio_bcount;
12367cb209f5SScott Long 		raw->ContainerId = ad->ad_container->co_mntobj.ObjectId;
12377cb209f5SScott Long 		raw->BpTotal = 0;
12387cb209f5SScott Long 		raw->BpComplete = 0;
12397cb209f5SScott Long 		fib->Header.Size += sizeof(struct aac_raw_io);
12407cb209f5SScott Long 		cm->cm_sgtable = (struct aac_sg_table *)&raw->SgMapRaw;
12417cb209f5SScott Long 		if (bp->bio_cmd == BIO_READ) {
12427cb209f5SScott Long 			raw->Flags = 1;
12437cb209f5SScott Long 			cm->cm_flags |= AAC_CMD_DATAIN;
12447cb209f5SScott Long 		} else {
12457cb209f5SScott Long 			raw->Flags = 0;
12467cb209f5SScott Long 			cm->cm_flags |= AAC_CMD_DATAOUT;
12477cb209f5SScott Long 		}
12487cb209f5SScott Long 	} else if ((sc->flags & AAC_FLAGS_SG_64BIT) == 0) {
1249b85f5808SScott Long 		fib->Header.Command = ContainerCommand;
12509e2e96d8SScott Long 		if (bp->bio_cmd == BIO_READ) {
1251b85f5808SScott Long 			struct aac_blockread *br;
125235863739SMike Smith 			br = (struct aac_blockread *)&fib->data[0];
125335863739SMike Smith 			br->Command = VM_CtBlockRead;
125435863739SMike Smith 			br->ContainerId = ad->ad_container->co_mntobj.ObjectId;
125535863739SMike Smith 			br->BlockNumber = bp->bio_pblkno;
125635863739SMike Smith 			br->ByteCount = bp->bio_bcount;
125735863739SMike Smith 			fib->Header.Size += sizeof(struct aac_blockread);
125835863739SMike Smith 			cm->cm_sgtable = &br->SgMap;
125935863739SMike Smith 			cm->cm_flags |= AAC_CMD_DATAIN;
126035863739SMike Smith 		} else {
1261b85f5808SScott Long 			struct aac_blockwrite *bw;
126235863739SMike Smith 			bw = (struct aac_blockwrite *)&fib->data[0];
126335863739SMike Smith 			bw->Command = VM_CtBlockWrite;
126435863739SMike Smith 			bw->ContainerId = ad->ad_container->co_mntobj.ObjectId;
126535863739SMike Smith 			bw->BlockNumber = bp->bio_pblkno;
126635863739SMike Smith 			bw->ByteCount = bp->bio_bcount;
1267b85f5808SScott Long 			bw->Stable = CUNSTABLE;
126835863739SMike Smith 			fib->Header.Size += sizeof(struct aac_blockwrite);
126935863739SMike Smith 			cm->cm_flags |= AAC_CMD_DATAOUT;
127035863739SMike Smith 			cm->cm_sgtable = &bw->SgMap;
127135863739SMike Smith 		}
1272b85f5808SScott Long 	} else {
1273b85f5808SScott Long 		fib->Header.Command = ContainerCommand64;
1274b85f5808SScott Long 		if (bp->bio_cmd == BIO_READ) {
1275b85f5808SScott Long 			struct aac_blockread64 *br;
1276b85f5808SScott Long 			br = (struct aac_blockread64 *)&fib->data[0];
1277b85f5808SScott Long 			br->Command = VM_CtHostRead64;
1278b85f5808SScott Long 			br->ContainerId = ad->ad_container->co_mntobj.ObjectId;
1279b85f5808SScott Long 			br->SectorCount = bp->bio_bcount / AAC_BLOCK_SIZE;
1280b85f5808SScott Long 			br->BlockNumber = bp->bio_pblkno;
1281b85f5808SScott Long 			br->Pad = 0;
1282b85f5808SScott Long 			br->Flags = 0;
1283b85f5808SScott Long 			fib->Header.Size += sizeof(struct aac_blockread64);
128454e2ebdfSEd Maste 			cm->cm_flags |= AAC_CMD_DATAIN;
1285eec256deSAlexander Kabaev 			cm->cm_sgtable = (struct aac_sg_table *)&br->SgMap64;
1286b85f5808SScott Long 		} else {
1287b85f5808SScott Long 			struct aac_blockwrite64 *bw;
1288b85f5808SScott Long 			bw = (struct aac_blockwrite64 *)&fib->data[0];
1289b85f5808SScott Long 			bw->Command = VM_CtHostWrite64;
1290b85f5808SScott Long 			bw->ContainerId = ad->ad_container->co_mntobj.ObjectId;
1291b85f5808SScott Long 			bw->SectorCount = bp->bio_bcount / AAC_BLOCK_SIZE;
1292b85f5808SScott Long 			bw->BlockNumber = bp->bio_pblkno;
1293b85f5808SScott Long 			bw->Pad = 0;
1294b85f5808SScott Long 			bw->Flags = 0;
1295b85f5808SScott Long 			fib->Header.Size += sizeof(struct aac_blockwrite64);
129654e2ebdfSEd Maste 			cm->cm_flags |= AAC_CMD_DATAOUT;
1297eec256deSAlexander Kabaev 			cm->cm_sgtable = (struct aac_sg_table *)&bw->SgMap64;
1298b85f5808SScott Long 		}
1299b85f5808SScott Long 	}
130035863739SMike Smith 
130135863739SMike Smith 	*cmp = cm;
130235863739SMike Smith 	return(0);
130335863739SMike Smith 
130435863739SMike Smith fail:
13057cb209f5SScott Long 	if (bp != NULL)
13067cb209f5SScott Long 		aac_enqueue_bio(sc, bp);
130735863739SMike Smith 	if (cm != NULL)
130835863739SMike Smith 		aac_release_command(cm);
130935863739SMike Smith 	return(ENOMEM);
131035863739SMike Smith }
131135863739SMike Smith 
1312914da7d0SScott Long /*
131335863739SMike Smith  * Handle a bio-instigated command that has been completed.
131435863739SMike Smith  */
131535863739SMike Smith static void
131635863739SMike Smith aac_bio_complete(struct aac_command *cm)
131735863739SMike Smith {
131835863739SMike Smith 	struct aac_blockread_response *brr;
131935863739SMike Smith 	struct aac_blockwrite_response *bwr;
132035863739SMike Smith 	struct bio *bp;
132135863739SMike Smith 	AAC_FSAStatus status;
132235863739SMike Smith 
132335863739SMike Smith 	/* fetch relevant status and then release the command */
132435863739SMike Smith 	bp = (struct bio *)cm->cm_private;
13259e2e96d8SScott Long 	if (bp->bio_cmd == BIO_READ) {
132635863739SMike Smith 		brr = (struct aac_blockread_response *)&cm->cm_fib->data[0];
132735863739SMike Smith 		status = brr->Status;
132835863739SMike Smith 	} else {
132935863739SMike Smith 		bwr = (struct aac_blockwrite_response *)&cm->cm_fib->data[0];
133035863739SMike Smith 		status = bwr->Status;
133135863739SMike Smith 	}
133235863739SMike Smith 	aac_release_command(cm);
133335863739SMike Smith 
133435863739SMike Smith 	/* fix up the bio based on status */
133535863739SMike Smith 	if (status == ST_OK) {
133635863739SMike Smith 		bp->bio_resid = 0;
133735863739SMike Smith 	} else {
133835863739SMike Smith 		bp->bio_error = EIO;
133935863739SMike Smith 		bp->bio_flags |= BIO_ERROR;
134035863739SMike Smith 	}
13410b94a66eSMike Smith 	aac_biodone(bp);
134235863739SMike Smith }
134335863739SMike Smith 
1344914da7d0SScott Long /*
134535863739SMike Smith  * Submit a command to the controller, return when it completes.
1346b3457b51SScott Long  * XXX This is very dangerous!  If the card has gone out to lunch, we could
1347b3457b51SScott Long  *     be stuck here forever.  At the same time, signals are not caught
1348d8a0a473SScott Long  *     because there is a risk that a signal could wakeup the sleep before
1349d8a0a473SScott Long  *     the card has a chance to complete the command.  Since there is no way
1350d8a0a473SScott Long  *     to cancel a command that is in progress, we can't protect against the
1351d8a0a473SScott Long  *     card completing a command late and spamming the command and data
1352d8a0a473SScott Long  *     memory.  So, we are held hostage until the command completes.
135335863739SMike Smith  */
135435863739SMike Smith static int
1355d8a0a473SScott Long aac_wait_command(struct aac_command *cm)
135635863739SMike Smith {
1357ae543596SScott Long 	struct aac_softc *sc;
1358d8a0a473SScott Long 	int error;
135935863739SMike Smith 
1360ae543596SScott Long 	sc = cm->cm_sc;
136131a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
1362ae543596SScott Long 
136335863739SMike Smith 	/* Put the command on the ready queue and get things going */
136435863739SMike Smith 	aac_enqueue_ready(cm);
1365ae543596SScott Long 	aac_startio(sc);
1366ae543596SScott Long 	error = msleep(cm, &sc->aac_io_lock, PRIBIO, "aacwait", 0);
136735863739SMike Smith 	return(error);
136835863739SMike Smith }
136935863739SMike Smith 
1370914da7d0SScott Long /*
1371914da7d0SScott Long  *Command Buffer Management
1372914da7d0SScott Long  */
137335863739SMike Smith 
1374914da7d0SScott Long /*
137535863739SMike Smith  * Allocate a command.
137635863739SMike Smith  */
1377fe3cb0e1SScott Long int
137835863739SMike Smith aac_alloc_command(struct aac_softc *sc, struct aac_command **cmp)
137935863739SMike Smith {
138035863739SMike Smith 	struct aac_command *cm;
138135863739SMike Smith 
138231a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
138335863739SMike Smith 
1384ffb37f33SScott Long 	if ((cm = aac_dequeue_free(sc)) == NULL) {
1385b85f5808SScott Long 		if (sc->total_fibs < sc->aac_max_fibs) {
13861bd320ecSAttilio Rao 			mtx_lock(&sc->aac_io_lock);
1387ae543596SScott Long 			sc->aifflags |= AAC_AIFFLAGS_ALLOCFIBS;
13881bd320ecSAttilio Rao 			mtx_unlock(&sc->aac_io_lock);
1389ae543596SScott Long 			wakeup(sc->aifthread);
1390b85f5808SScott Long 		}
1391ae543596SScott Long 		return (EBUSY);
1392ffb37f33SScott Long 	}
139335863739SMike Smith 
13940b94a66eSMike Smith 	*cmp = cm;
13950b94a66eSMike Smith 	return(0);
13960b94a66eSMike Smith }
13970b94a66eSMike Smith 
1398914da7d0SScott Long /*
13990b94a66eSMike Smith  * Release a command back to the freelist.
14000b94a66eSMike Smith  */
1401fe3cb0e1SScott Long void
14020b94a66eSMike Smith aac_release_command(struct aac_command *cm)
14030b94a66eSMike Smith {
14047cb209f5SScott Long 	struct aac_event *event;
14057cb209f5SScott Long 	struct aac_softc *sc;
14067cb209f5SScott Long 
140731a0399eSEd Maste 	sc = cm->cm_sc;
140831a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
14090b94a66eSMike Smith 
14104109ba51SEd Maste 	/* (re)initialize the command/FIB */
1411b2b39b04SJohn Baldwin 	cm->cm_datalen = 0;
141235863739SMike Smith 	cm->cm_sgtable = NULL;
141335863739SMike Smith 	cm->cm_flags = 0;
141435863739SMike Smith 	cm->cm_complete = NULL;
141535863739SMike Smith 	cm->cm_private = NULL;
1416dbfc5960SEd Maste 	cm->cm_queue = AAC_ADAP_NORM_CMD_QUEUE;
141735863739SMike Smith 	cm->cm_fib->Header.XferState = AAC_FIBSTATE_EMPTY;
141835863739SMike Smith 	cm->cm_fib->Header.StructType = AAC_FIBTYPE_TFIB;
141935863739SMike Smith 	cm->cm_fib->Header.Flags = 0;
14207cb209f5SScott Long 	cm->cm_fib->Header.SenderSize = cm->cm_sc->aac_max_fib_size;
142135863739SMike Smith 
142235863739SMike Smith 	/*
142335863739SMike Smith 	 * These are duplicated in aac_start to cover the case where an
142435863739SMike Smith 	 * intermediate stage may have destroyed them.  They're left
14254109ba51SEd Maste 	 * initialized here for debugging purposes only.
142635863739SMike Smith 	 */
1427f30ac74cSScott Long 	cm->cm_fib->Header.ReceiverFibAddress = (u_int32_t)cm->cm_fibphys;
1428f30ac74cSScott Long 	cm->cm_fib->Header.SenderData = 0;
142935863739SMike Smith 
143035863739SMike Smith 	aac_enqueue_free(cm);
14317cb209f5SScott Long 
14322ad1c92dSEd Maste 	if ((event = TAILQ_FIRST(&sc->aac_ev_cmfree)) != NULL) {
14337cb209f5SScott Long 		TAILQ_REMOVE(&sc->aac_ev_cmfree, event, ev_links);
14347cb209f5SScott Long 		event->ev_callback(sc, event, event->ev_arg);
14357cb209f5SScott Long 	}
143635863739SMike Smith }
143735863739SMike Smith 
1438914da7d0SScott Long /*
14390b94a66eSMike Smith  * Map helper for command/FIB allocation.
144035863739SMike Smith  */
144135863739SMike Smith static void
14420b94a66eSMike Smith aac_map_command_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error)
144335863739SMike Smith {
14447cb209f5SScott Long 	uint64_t	*fibphys;
1445914da7d0SScott Long 
14467cb209f5SScott Long 	fibphys = (uint64_t *)arg;
144735863739SMike Smith 
1448ffb37f33SScott Long 	*fibphys = segs[0].ds_addr;
144935863739SMike Smith }
145035863739SMike Smith 
1451914da7d0SScott Long /*
14524109ba51SEd Maste  * Allocate and initialize commands/FIBs for this adapter.
145335863739SMike Smith  */
14540b94a66eSMike Smith static int
14550b94a66eSMike Smith aac_alloc_commands(struct aac_softc *sc)
145635863739SMike Smith {
145735863739SMike Smith 	struct aac_command *cm;
1458ffb37f33SScott Long 	struct aac_fibmap *fm;
14597cb209f5SScott Long 	uint64_t fibphys;
1460ffb37f33SScott Long 	int i, error;
146135863739SMike Smith 
146231a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
146335863739SMike Smith 
14647cb209f5SScott Long 	if (sc->total_fibs + sc->aac_max_fibs_alloc > sc->aac_max_fibs)
1465ffb37f33SScott Long 		return (ENOMEM);
1466ffb37f33SScott Long 
14678480cc63SScott Long 	fm = malloc(sizeof(struct aac_fibmap), M_AACBUF, M_NOWAIT|M_ZERO);
1468a6d35632SScott Long 	if (fm == NULL)
1469a6d35632SScott Long 		return (ENOMEM);
1470ffb37f33SScott Long 
14710b94a66eSMike Smith 	/* allocate the FIBs in DMAable memory and load them */
1472ffb37f33SScott Long 	if (bus_dmamem_alloc(sc->aac_fib_dmat, (void **)&fm->aac_fibs,
1473ffb37f33SScott Long 			     BUS_DMA_NOWAIT, &fm->aac_fibmap)) {
147470545d1aSScott Long 		device_printf(sc->aac_dev,
147570545d1aSScott Long 			      "Not enough contiguous memory available.\n");
14768480cc63SScott Long 		free(fm, M_AACBUF);
14770b94a66eSMike Smith 		return (ENOMEM);
147835863739SMike Smith 	}
1479128aa5a0SScott Long 
1480cd481291SScott Long 	/* Ignore errors since this doesn't bounce */
1481cd481291SScott Long 	(void)bus_dmamap_load(sc->aac_fib_dmat, fm->aac_fibmap, fm->aac_fibs,
14827cb209f5SScott Long 			      sc->aac_max_fibs_alloc * sc->aac_max_fib_size,
1483ffb37f33SScott Long 			      aac_map_command_helper, &fibphys, 0);
1484128aa5a0SScott Long 
14854109ba51SEd Maste 	/* initialize constant fields in the command structure */
14867cb209f5SScott Long 	bzero(fm->aac_fibs, sc->aac_max_fibs_alloc * sc->aac_max_fib_size);
14877cb209f5SScott Long 	for (i = 0; i < sc->aac_max_fibs_alloc; i++) {
14888480cc63SScott Long 		cm = sc->aac_commands + sc->total_fibs;
1489ffb37f33SScott Long 		fm->aac_commands = cm;
149035863739SMike Smith 		cm->cm_sc = sc;
14917cb209f5SScott Long 		cm->cm_fib = (struct aac_fib *)
14927cb209f5SScott Long 			((u_int8_t *)fm->aac_fibs + i*sc->aac_max_fib_size);
14937cb209f5SScott Long 		cm->cm_fibphys = fibphys + i*sc->aac_max_fib_size;
1494cb0d64b9SScott Long 		cm->cm_index = sc->total_fibs;
149535863739SMike Smith 
1496ffb37f33SScott Long 		if ((error = bus_dmamap_create(sc->aac_buffer_dmat, 0,
149793cfca22SScott Long 					       &cm->cm_datamap)) != 0)
14988480cc63SScott Long 			break;
149993cfca22SScott Long 		mtx_lock(&sc->aac_io_lock);
150093cfca22SScott Long 		aac_release_command(cm);
15018480cc63SScott Long 		sc->total_fibs++;
150293cfca22SScott Long 		mtx_unlock(&sc->aac_io_lock);
150335863739SMike Smith 	}
1504ffb37f33SScott Long 
15058480cc63SScott Long 	if (i > 0) {
150693cfca22SScott Long 		mtx_lock(&sc->aac_io_lock);
1507ffb37f33SScott Long 		TAILQ_INSERT_TAIL(&sc->aac_fibmap_tqh, fm, fm_link);
150831a0399eSEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_COMM_B, "total_fibs= %d\n", sc->total_fibs);
1509bb6fe253SScott Long 		mtx_unlock(&sc->aac_io_lock);
15100b94a66eSMike Smith 		return (0);
151135863739SMike Smith 	}
151235863739SMike Smith 
15138480cc63SScott Long 	bus_dmamap_unload(sc->aac_fib_dmat, fm->aac_fibmap);
15148480cc63SScott Long 	bus_dmamem_free(sc->aac_fib_dmat, fm->aac_fibs, fm->aac_fibmap);
15158480cc63SScott Long 	free(fm, M_AACBUF);
15168480cc63SScott Long 	return (ENOMEM);
15178480cc63SScott Long }
15188480cc63SScott Long 
1519914da7d0SScott Long /*
15200b94a66eSMike Smith  * Free FIBs owned by this adapter.
152135863739SMike Smith  */
152235863739SMike Smith static void
15238480cc63SScott Long aac_free_commands(struct aac_softc *sc)
152435863739SMike Smith {
15258480cc63SScott Long 	struct aac_fibmap *fm;
1526ffb37f33SScott Long 	struct aac_command *cm;
152735863739SMike Smith 	int i;
152835863739SMike Smith 
152931a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
153035863739SMike Smith 
15318480cc63SScott Long 	while ((fm = TAILQ_FIRST(&sc->aac_fibmap_tqh)) != NULL) {
15328480cc63SScott Long 
15338480cc63SScott Long 		TAILQ_REMOVE(&sc->aac_fibmap_tqh, fm, fm_link);
15348480cc63SScott Long 		/*
15358480cc63SScott Long 		 * We check against total_fibs to handle partially
15368480cc63SScott Long 		 * allocated blocks.
15378480cc63SScott Long 		 */
15387cb209f5SScott Long 		for (i = 0; i < sc->aac_max_fibs_alloc && sc->total_fibs--; i++) {
1539ffb37f33SScott Long 			cm = fm->aac_commands + i;
1540ffb37f33SScott Long 			bus_dmamap_destroy(sc->aac_buffer_dmat, cm->cm_datamap);
1541ffb37f33SScott Long 		}
1542ffb37f33SScott Long 		bus_dmamap_unload(sc->aac_fib_dmat, fm->aac_fibmap);
1543ffb37f33SScott Long 		bus_dmamem_free(sc->aac_fib_dmat, fm->aac_fibs, fm->aac_fibmap);
15448480cc63SScott Long 		free(fm, M_AACBUF);
15458480cc63SScott Long 	}
154635863739SMike Smith }
154735863739SMike Smith 
1548914da7d0SScott Long /*
154935863739SMike Smith  * Command-mapping helper function - populate this command's s/g table.
155035863739SMike Smith  */
155135863739SMike Smith static void
155235863739SMike Smith aac_map_command_sg(void *arg, bus_dma_segment_t *segs, int nseg, int error)
155335863739SMike Smith {
1554cd481291SScott Long 	struct aac_softc *sc;
1555914da7d0SScott Long 	struct aac_command *cm;
1556914da7d0SScott Long 	struct aac_fib *fib;
155735863739SMike Smith 	int i;
155835863739SMike Smith 
1559914da7d0SScott Long 	cm = (struct aac_command *)arg;
1560cd481291SScott Long 	sc = cm->cm_sc;
1561914da7d0SScott Long 	fib = cm->cm_fib;
156231a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
1563914da7d0SScott Long 
156435863739SMike Smith 	/* copy into the FIB */
1565b85f5808SScott Long 	if (cm->cm_sgtable != NULL) {
15667cb209f5SScott Long 		if (fib->Header.Command == RawIo) {
15677cb209f5SScott Long 			struct aac_sg_tableraw *sg;
15687cb209f5SScott Long 			sg = (struct aac_sg_tableraw *)cm->cm_sgtable;
15697cb209f5SScott Long 			sg->SgCount = nseg;
15707cb209f5SScott Long 			for (i = 0; i < nseg; i++) {
15717cb209f5SScott Long 				sg->SgEntryRaw[i].SgAddress = segs[i].ds_addr;
15727cb209f5SScott Long 				sg->SgEntryRaw[i].SgByteCount = segs[i].ds_len;
15737cb209f5SScott Long 				sg->SgEntryRaw[i].Next = 0;
15747cb209f5SScott Long 				sg->SgEntryRaw[i].Prev = 0;
15757cb209f5SScott Long 				sg->SgEntryRaw[i].Flags = 0;
15767cb209f5SScott Long 			}
15777cb209f5SScott Long 			/* update the FIB size for the s/g count */
15787cb209f5SScott Long 			fib->Header.Size += nseg*sizeof(struct aac_sg_entryraw);
15797cb209f5SScott Long 		} else if ((cm->cm_sc->flags & AAC_FLAGS_SG_64BIT) == 0) {
1580b85f5808SScott Long 			struct aac_sg_table *sg;
1581b85f5808SScott Long 			sg = cm->cm_sgtable;
158235863739SMike Smith 			sg->SgCount = nseg;
158335863739SMike Smith 			for (i = 0; i < nseg; i++) {
158435863739SMike Smith 				sg->SgEntry[i].SgAddress = segs[i].ds_addr;
158535863739SMike Smith 				sg->SgEntry[i].SgByteCount = segs[i].ds_len;
158635863739SMike Smith 			}
158735863739SMike Smith 			/* update the FIB size for the s/g count */
158835863739SMike Smith 			fib->Header.Size += nseg*sizeof(struct aac_sg_entry);
1589b85f5808SScott Long 		} else {
1590b85f5808SScott Long 			struct aac_sg_table64 *sg;
1591b85f5808SScott Long 			sg = (struct aac_sg_table64 *)cm->cm_sgtable;
1592b85f5808SScott Long 			sg->SgCount = nseg;
1593b85f5808SScott Long 			for (i = 0; i < nseg; i++) {
1594b85f5808SScott Long 				sg->SgEntry64[i].SgAddress = segs[i].ds_addr;
1595b85f5808SScott Long 				sg->SgEntry64[i].SgByteCount = segs[i].ds_len;
159635863739SMike Smith 			}
1597b85f5808SScott Long 			/* update the FIB size for the s/g count */
1598b85f5808SScott Long 			fib->Header.Size += nseg*sizeof(struct aac_sg_entry64);
1599b85f5808SScott Long 		}
1600b85f5808SScott Long 	}
160135863739SMike Smith 
1602cd481291SScott Long 	/* Fix up the address values in the FIB.  Use the command array index
1603cd481291SScott Long 	 * instead of a pointer since these fields are only 32 bits.  Shift
16047cb209f5SScott Long 	 * the SenderFibAddress over to make room for the fast response bit
16057cb209f5SScott Long 	 * and for the AIF bit
160635863739SMike Smith 	 */
16077cb209f5SScott Long 	cm->cm_fib->Header.SenderFibAddress = (cm->cm_index << 2);
16087cb209f5SScott Long 	cm->cm_fib->Header.ReceiverFibAddress = (u_int32_t)cm->cm_fibphys;
160935863739SMike Smith 
1610cd481291SScott Long 	/* save a pointer to the command for speedy reverse-lookup */
1611cd481291SScott Long 	cm->cm_fib->Header.SenderData = cm->cm_index;
161235863739SMike Smith 
161335863739SMike Smith 	if (cm->cm_flags & AAC_CMD_DATAIN)
1614c6eafcf2SScott Long 		bus_dmamap_sync(sc->aac_buffer_dmat, cm->cm_datamap,
1615c6eafcf2SScott Long 				BUS_DMASYNC_PREREAD);
161635863739SMike Smith 	if (cm->cm_flags & AAC_CMD_DATAOUT)
1617c6eafcf2SScott Long 		bus_dmamap_sync(sc->aac_buffer_dmat, cm->cm_datamap,
1618c6eafcf2SScott Long 				BUS_DMASYNC_PREWRITE);
161935863739SMike Smith 	cm->cm_flags |= AAC_CMD_MAPPED;
1620cd481291SScott Long 
16217cb209f5SScott Long 	if (sc->flags & AAC_FLAGS_NEW_COMM) {
16227cb209f5SScott Long 		int count = 10000000L;
16237cb209f5SScott Long 		while (AAC_SEND_COMMAND(sc, cm) != 0) {
16247cb209f5SScott Long 			if (--count == 0) {
16257cb209f5SScott Long 				aac_unmap_command(cm);
16267cb209f5SScott Long 				sc->flags |= AAC_QUEUE_FRZN;
16277cb209f5SScott Long 				aac_requeue_ready(cm);
16287cb209f5SScott Long 			}
16297cb209f5SScott Long 			DELAY(5);			/* wait 5 usec. */
16307cb209f5SScott Long 		}
16317cb209f5SScott Long 	} else {
1632397fa34fSScott Long 		/* Put the FIB on the outbound queue */
16334102d44bSScott Long 		if (aac_enqueue_fib(sc, cm->cm_queue, cm) == EBUSY) {
16344102d44bSScott Long 			aac_unmap_command(cm);
1635397fa34fSScott Long 			sc->flags |= AAC_QUEUE_FRZN;
1636cd481291SScott Long 			aac_requeue_ready(cm);
16374102d44bSScott Long 		}
16387cb209f5SScott Long 	}
163935863739SMike Smith }
164035863739SMike Smith 
1641914da7d0SScott Long /*
164235863739SMike Smith  * Unmap a command from controller-visible space.
164335863739SMike Smith  */
164435863739SMike Smith static void
164535863739SMike Smith aac_unmap_command(struct aac_command *cm)
164635863739SMike Smith {
1647914da7d0SScott Long 	struct aac_softc *sc;
164835863739SMike Smith 
1649914da7d0SScott Long 	sc = cm->cm_sc;
165031a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
1651914da7d0SScott Long 
165235863739SMike Smith 	if (!(cm->cm_flags & AAC_CMD_MAPPED))
165335863739SMike Smith 		return;
165435863739SMike Smith 
165535863739SMike Smith 	if (cm->cm_datalen != 0) {
165635863739SMike Smith 		if (cm->cm_flags & AAC_CMD_DATAIN)
1657c6eafcf2SScott Long 			bus_dmamap_sync(sc->aac_buffer_dmat, cm->cm_datamap,
1658c6eafcf2SScott Long 					BUS_DMASYNC_POSTREAD);
165935863739SMike Smith 		if (cm->cm_flags & AAC_CMD_DATAOUT)
1660c6eafcf2SScott Long 			bus_dmamap_sync(sc->aac_buffer_dmat, cm->cm_datamap,
1661c6eafcf2SScott Long 					BUS_DMASYNC_POSTWRITE);
166235863739SMike Smith 
166335863739SMike Smith 		bus_dmamap_unload(sc->aac_buffer_dmat, cm->cm_datamap);
166435863739SMike Smith 	}
166535863739SMike Smith 	cm->cm_flags &= ~AAC_CMD_MAPPED;
166635863739SMike Smith }
166735863739SMike Smith 
1668914da7d0SScott Long /*
1669914da7d0SScott Long  * Hardware Interface
1670914da7d0SScott Long  */
167135863739SMike Smith 
1672914da7d0SScott Long /*
16734109ba51SEd Maste  * Initialize the adapter.
167435863739SMike Smith  */
167535863739SMike Smith static void
167635863739SMike Smith aac_common_map(void *arg, bus_dma_segment_t *segs, int nseg, int error)
167735863739SMike Smith {
1678914da7d0SScott Long 	struct aac_softc *sc;
167935863739SMike Smith 
1680914da7d0SScott Long 	sc = (struct aac_softc *)arg;
168131a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
1682914da7d0SScott Long 
168335863739SMike Smith 	sc->aac_common_busaddr = segs[0].ds_addr;
168435863739SMike Smith }
168535863739SMike Smith 
1686a6d35632SScott Long static int
1687a6d35632SScott Long aac_check_firmware(struct aac_softc *sc)
1688a6d35632SScott Long {
168904f4d586SEd Maste 	u_int32_t code, major, minor, options = 0, atu_size = 0;
1690da4882c2SMarius Strobl 	int rid, status;
169104f4d586SEd Maste 	time_t then;
1692a6d35632SScott Long 
169331a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
169404f4d586SEd Maste 	/*
169504f4d586SEd Maste 	 * Wait for the adapter to come ready.
169604f4d586SEd Maste 	 */
169704f4d586SEd Maste 	then = time_uptime;
169804f4d586SEd Maste 	do {
169904f4d586SEd Maste 		code = AAC_GET_FWSTATUS(sc);
170004f4d586SEd Maste 		if (code & AAC_SELF_TEST_FAILED) {
170104f4d586SEd Maste 			device_printf(sc->aac_dev, "FATAL: selftest failed\n");
170204f4d586SEd Maste 			return(ENXIO);
170304f4d586SEd Maste 		}
170404f4d586SEd Maste 		if (code & AAC_KERNEL_PANIC) {
170504f4d586SEd Maste 			device_printf(sc->aac_dev,
1706a620bad0SEd Maste 				      "FATAL: controller kernel panic");
170704f4d586SEd Maste 			return(ENXIO);
170804f4d586SEd Maste 		}
170904f4d586SEd Maste 		if (time_uptime > (then + AAC_BOOT_TIMEOUT)) {
171004f4d586SEd Maste 			device_printf(sc->aac_dev,
171104f4d586SEd Maste 				      "FATAL: controller not coming ready, "
171204f4d586SEd Maste 					   "status %x\n", code);
171304f4d586SEd Maste 			return(ENXIO);
171404f4d586SEd Maste 		}
171504f4d586SEd Maste 	} while (!(code & AAC_UP_AND_RUNNING));
1716a6d35632SScott Long 
1717fe94b852SScott Long 	/*
1718fe94b852SScott Long 	 * Retrieve the firmware version numbers.  Dell PERC2/QC cards with
1719fe94b852SScott Long 	 * firmware version 1.x are not compatible with this driver.
1720fe94b852SScott Long 	 */
1721a6d35632SScott Long 	if (sc->flags & AAC_FLAGS_PERC2QC) {
1722fe94b852SScott Long 		if (aac_sync_command(sc, AAC_MONKER_GETKERNVER, 0, 0, 0, 0,
1723fe94b852SScott Long 				     NULL)) {
1724fe94b852SScott Long 			device_printf(sc->aac_dev,
1725fe94b852SScott Long 				      "Error reading firmware version\n");
1726fe94b852SScott Long 			return (EIO);
1727fe94b852SScott Long 		}
1728fe94b852SScott Long 
1729fe94b852SScott Long 		/* These numbers are stored as ASCII! */
1730a6d35632SScott Long 		major = (AAC_GET_MAILBOX(sc, 1) & 0xff) - 0x30;
1731a6d35632SScott Long 		minor = (AAC_GET_MAILBOX(sc, 2) & 0xff) - 0x30;
1732fe94b852SScott Long 		if (major == 1) {
1733fe94b852SScott Long 			device_printf(sc->aac_dev,
1734fe94b852SScott Long 			    "Firmware version %d.%d is not supported.\n",
1735fe94b852SScott Long 			    major, minor);
1736fe94b852SScott Long 			return (EINVAL);
1737fe94b852SScott Long 		}
1738fe94b852SScott Long 	}
1739fe94b852SScott Long 
1740a6d35632SScott Long 	/*
1741a6d35632SScott Long 	 * Retrieve the capabilities/supported options word so we know what
1742a441b3fcSScott Long 	 * work-arounds to enable.  Some firmware revs don't support this
1743a441b3fcSScott Long 	 * command.
1744a6d35632SScott Long 	 */
1745a441b3fcSScott Long 	if (aac_sync_command(sc, AAC_MONKER_GETINFO, 0, 0, 0, 0, &status)) {
1746a441b3fcSScott Long 		if (status != AAC_SRB_STS_INVALID_REQUEST) {
1747a441b3fcSScott Long 			device_printf(sc->aac_dev,
1748a441b3fcSScott Long 			     "RequestAdapterInfo failed\n");
1749a6d35632SScott Long 			return (EIO);
1750a6d35632SScott Long 		}
1751a441b3fcSScott Long 	} else {
1752a6d35632SScott Long 		options = AAC_GET_MAILBOX(sc, 1);
17537cb209f5SScott Long 		atu_size = AAC_GET_MAILBOX(sc, 2);
1754a6d35632SScott Long 		sc->supported_options = options;
1755a6d35632SScott Long 
1756a6d35632SScott Long 		if ((options & AAC_SUPPORTED_4GB_WINDOW) != 0 &&
1757a6d35632SScott Long 		    (sc->flags & AAC_FLAGS_NO4GB) == 0)
1758a6d35632SScott Long 			sc->flags |= AAC_FLAGS_4GB_WINDOW;
1759a6d35632SScott Long 		if (options & AAC_SUPPORTED_NONDASD)
1760a6d35632SScott Long 			sc->flags |= AAC_FLAGS_ENABLE_CAM;
1761cd481291SScott Long 		if ((options & AAC_SUPPORTED_SGMAP_HOST64) != 0
1762cd481291SScott Long 		     && (sizeof(bus_addr_t) > 4)) {
1763a441b3fcSScott Long 			device_printf(sc->aac_dev,
1764a441b3fcSScott Long 			    "Enabling 64-bit address support\n");
1765a6d35632SScott Long 			sc->flags |= AAC_FLAGS_SG_64BIT;
1766a6d35632SScott Long 		}
1767a441b3fcSScott Long 		if ((options & AAC_SUPPORTED_NEW_COMM)
1768da4882c2SMarius Strobl 		 && sc->aac_if->aif_send_command)
17697cb209f5SScott Long 			sc->flags |= AAC_FLAGS_NEW_COMM;
17707cb209f5SScott Long 		if (options & AAC_SUPPORTED_64BIT_ARRAYSIZE)
17717cb209f5SScott Long 			sc->flags |= AAC_FLAGS_ARRAY_64BIT;
1772a441b3fcSScott Long 	}
1773a6d35632SScott Long 
1774a6d35632SScott Long 	/* Check for broken hardware that does a lower number of commands */
17757cb209f5SScott Long 	sc->aac_max_fibs = (sc->flags & AAC_FLAGS_256FIBS ? 256:512);
17767cb209f5SScott Long 
17777cb209f5SScott Long 	/* Remap mem. resource, if required */
17787cb209f5SScott Long 	if ((sc->flags & AAC_FLAGS_NEW_COMM) &&
1779ff0991c4SAttilio Rao 	    atu_size > rman_get_size(sc->aac_regs_res1)) {
1780da4882c2SMarius Strobl 		rid = rman_get_rid(sc->aac_regs_res1);
1781da4882c2SMarius Strobl 		bus_release_resource(sc->aac_dev, SYS_RES_MEMORY, rid,
1782da4882c2SMarius Strobl 		    sc->aac_regs_res1);
1783da4882c2SMarius Strobl 		sc->aac_regs_res1 = bus_alloc_resource(sc->aac_dev,
1784da4882c2SMarius Strobl 		    SYS_RES_MEMORY, &rid, 0ul, ~0ul, atu_size, RF_ACTIVE);
1785ff0991c4SAttilio Rao 		if (sc->aac_regs_res1 == NULL) {
1786ff0991c4SAttilio Rao 			sc->aac_regs_res1 = bus_alloc_resource_any(
1787da4882c2SMarius Strobl 			    sc->aac_dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
1788ff0991c4SAttilio Rao 			if (sc->aac_regs_res1 == NULL) {
17897cb209f5SScott Long 				device_printf(sc->aac_dev,
17907cb209f5SScott Long 				    "couldn't allocate register window\n");
17917cb209f5SScott Long 				return (ENXIO);
17927cb209f5SScott Long 			}
17937cb209f5SScott Long 			sc->flags &= ~AAC_FLAGS_NEW_COMM;
17947cb209f5SScott Long 		}
1795ff0991c4SAttilio Rao 		sc->aac_btag1 = rman_get_bustag(sc->aac_regs_res1);
1796ff0991c4SAttilio Rao 		sc->aac_bhandle1 = rman_get_bushandle(sc->aac_regs_res1);
1797ff0991c4SAttilio Rao 
1798ff0991c4SAttilio Rao 		if (sc->aac_hwif == AAC_HWIF_NARK) {
1799ff0991c4SAttilio Rao 			sc->aac_regs_res0 = sc->aac_regs_res1;
1800ff0991c4SAttilio Rao 			sc->aac_btag0 = sc->aac_btag1;
1801ff0991c4SAttilio Rao 			sc->aac_bhandle0 = sc->aac_bhandle1;
1802ff0991c4SAttilio Rao 		}
18037cb209f5SScott Long 	}
18047cb209f5SScott Long 
18057cb209f5SScott Long 	/* Read preferred settings */
18067cb209f5SScott Long 	sc->aac_max_fib_size = sizeof(struct aac_fib);
18077cb209f5SScott Long 	sc->aac_max_sectors = 128;				/* 64KB */
18087cb209f5SScott Long 	if (sc->flags & AAC_FLAGS_SG_64BIT)
1809a441b3fcSScott Long 		sc->aac_sg_tablesize = (AAC_FIB_DATASIZE
18107e7a458eSEd Maste 		 - sizeof(struct aac_blockwrite64))
18117e7a458eSEd Maste 		 / sizeof(struct aac_sg_entry64);
1812a6d35632SScott Long 	else
1813a441b3fcSScott Long 		sc->aac_sg_tablesize = (AAC_FIB_DATASIZE
18147e7a458eSEd Maste 		 - sizeof(struct aac_blockwrite))
18157e7a458eSEd Maste 		 / sizeof(struct aac_sg_entry);
1816a441b3fcSScott Long 
18177cb209f5SScott Long 	if (!aac_sync_command(sc, AAC_MONKER_GETCOMMPREF, 0, 0, 0, 0, NULL)) {
18187cb209f5SScott Long 		options = AAC_GET_MAILBOX(sc, 1);
18197cb209f5SScott Long 		sc->aac_max_fib_size = (options & 0xFFFF);
18207cb209f5SScott Long 		sc->aac_max_sectors = (options >> 16) << 1;
18217cb209f5SScott Long 		options = AAC_GET_MAILBOX(sc, 2);
18227cb209f5SScott Long 		sc->aac_sg_tablesize = (options >> 16);
18237cb209f5SScott Long 		options = AAC_GET_MAILBOX(sc, 3);
18247cb209f5SScott Long 		sc->aac_max_fibs = (options & 0xFFFF);
18257cb209f5SScott Long 	}
18267cb209f5SScott Long 	if (sc->aac_max_fib_size > PAGE_SIZE)
18277cb209f5SScott Long 		sc->aac_max_fib_size = PAGE_SIZE;
18287cb209f5SScott Long 	sc->aac_max_fibs_alloc = PAGE_SIZE / sc->aac_max_fib_size;
1829a6d35632SScott Long 
1830f355c0e0SEd Maste 	if (sc->aac_max_fib_size > sizeof(struct aac_fib)) {
1831f355c0e0SEd Maste 		sc->flags |= AAC_FLAGS_RAW_IO;
1832f355c0e0SEd Maste 		device_printf(sc->aac_dev, "Enable Raw I/O\n");
1833f355c0e0SEd Maste 	}
1834523da39bSEd Maste 	if ((sc->flags & AAC_FLAGS_RAW_IO) &&
1835523da39bSEd Maste 	    (sc->flags & AAC_FLAGS_ARRAY_64BIT)) {
1836523da39bSEd Maste 		sc->flags |= AAC_FLAGS_LBA_64BIT;
1837523da39bSEd Maste 		device_printf(sc->aac_dev, "Enable 64-bit array\n");
1838523da39bSEd Maste 	}
1839f355c0e0SEd Maste 
1840fe94b852SScott Long 	return (0);
1841fe94b852SScott Long }
1842fe94b852SScott Long 
184335863739SMike Smith static int
184435863739SMike Smith aac_init(struct aac_softc *sc)
184535863739SMike Smith {
184635863739SMike Smith 	struct aac_adapter_init	*ip;
184704f4d586SEd Maste 	u_int32_t qoffset;
1848a6d35632SScott Long 	int error;
184935863739SMike Smith 
185031a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
1851ffb37f33SScott Long 
185235863739SMike Smith 	/*
1853914da7d0SScott Long 	 * Fill in the init structure.  This tells the adapter about the
1854914da7d0SScott Long 	 * physical location of various important shared data structures.
185535863739SMike Smith 	 */
185635863739SMike Smith 	ip = &sc->aac_common->ac_init;
185735863739SMike Smith 	ip->InitStructRevision = AAC_INIT_STRUCT_REVISION;
18587cb209f5SScott Long 	if (sc->aac_max_fib_size > sizeof(struct aac_fib)) {
18597cb209f5SScott Long 		ip->InitStructRevision = AAC_INIT_STRUCT_REVISION_4;
18607cb209f5SScott Long 		sc->flags |= AAC_FLAGS_RAW_IO;
18617cb209f5SScott Long 	}
1862f30ac74cSScott Long 	ip->MiniPortRevision = AAC_INIT_STRUCT_MINIPORT_REVISION;
186335863739SMike Smith 
1864c6eafcf2SScott Long 	ip->AdapterFibsPhysicalAddress = sc->aac_common_busaddr +
1865c6eafcf2SScott Long 					 offsetof(struct aac_common, ac_fibs);
1866149af931SScott Long 	ip->AdapterFibsVirtualAddress = 0;
186735863739SMike Smith 	ip->AdapterFibsSize = AAC_ADAPTER_FIBS * sizeof(struct aac_fib);
186835863739SMike Smith 	ip->AdapterFibAlign = sizeof(struct aac_fib);
186935863739SMike Smith 
1870c6eafcf2SScott Long 	ip->PrintfBufferAddress = sc->aac_common_busaddr +
1871c6eafcf2SScott Long 				  offsetof(struct aac_common, ac_printf);
187235863739SMike Smith 	ip->PrintfBufferSize = AAC_PRINTF_BUFSIZE;
187335863739SMike Smith 
18744b00f859SScott Long 	/*
18754b00f859SScott Long 	 * The adapter assumes that pages are 4K in size, except on some
18764b00f859SScott Long  	 * broken firmware versions that do the page->byte conversion twice,
18774b00f859SScott Long 	 * therefore 'assuming' that this value is in 16MB units (2^24).
18784b00f859SScott Long 	 * Round up since the granularity is so high.
18794b00f859SScott Long 	 */
1880f30ac74cSScott Long 	ip->HostPhysMemPages = ctob(physmem) / AAC_PAGE_SIZE;
18814b00f859SScott Long 	if (sc->flags & AAC_FLAGS_BROKEN_MEMMAP) {
18824b00f859SScott Long 		ip->HostPhysMemPages =
18834b00f859SScott Long 		    (ip->HostPhysMemPages + AAC_PAGE_SIZE) / AAC_PAGE_SIZE;
1884204c0befSScott Long 	}
18852b3b0f17SScott Long 	ip->HostElapsedSeconds = time_uptime;	/* reset later if invalid */
188635863739SMike Smith 
18877cb209f5SScott Long 	ip->InitFlags = 0;
18887cb209f5SScott Long 	if (sc->flags & AAC_FLAGS_NEW_COMM) {
1889e71d3b9cSEd Maste 		ip->InitFlags |= AAC_INITFLAGS_NEW_COMM_SUPPORTED;
18907cb209f5SScott Long 		device_printf(sc->aac_dev, "New comm. interface enabled\n");
18917cb209f5SScott Long 	}
18927cb209f5SScott Long 
18937cb209f5SScott Long 	ip->MaxIoCommands = sc->aac_max_fibs;
18947cb209f5SScott Long 	ip->MaxIoSize = sc->aac_max_sectors << 9;
18957cb209f5SScott Long 	ip->MaxFibSize = sc->aac_max_fib_size;
18967cb209f5SScott Long 
189735863739SMike Smith 	/*
18984109ba51SEd Maste 	 * Initialize FIB queues.  Note that it appears that the layout of the
1899c6eafcf2SScott Long 	 * indexes and the segmentation of the entries may be mandated by the
1900c6eafcf2SScott Long 	 * adapter, which is only told about the base of the queue index fields.
190135863739SMike Smith 	 *
190235863739SMike Smith 	 * The initial values of the indices are assumed to inform the adapter
1903914da7d0SScott Long 	 * of the sizes of the respective queues, and theoretically it could
1904914da7d0SScott Long 	 * work out the entire layout of the queue structures from this.  We
1905914da7d0SScott Long 	 * take the easy route and just lay this area out like everyone else
1906914da7d0SScott Long 	 * does.
190735863739SMike Smith 	 *
1908914da7d0SScott Long 	 * The Linux driver uses a much more complex scheme whereby several
1909914da7d0SScott Long 	 * header records are kept for each queue.  We use a couple of generic
1910914da7d0SScott Long 	 * list manipulation functions which 'know' the size of each list by
1911914da7d0SScott Long 	 * virtue of a table.
191235863739SMike Smith 	 */
1913b88ffdc8SScott Long 	qoffset = offsetof(struct aac_common, ac_qbuf) + AAC_QUEUE_ALIGN;
19140bcbebd6SScott Long 	qoffset &= ~(AAC_QUEUE_ALIGN - 1);
19150bcbebd6SScott Long 	sc->aac_queues =
19160bcbebd6SScott Long 	    (struct aac_queue_table *)((uintptr_t)sc->aac_common + qoffset);
1917b88ffdc8SScott Long 	ip->CommHeaderAddress = sc->aac_common_busaddr + qoffset;
191835863739SMike Smith 
1919c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_NORM_CMD_QUEUE][AAC_PRODUCER_INDEX] =
1920c6eafcf2SScott Long 		AAC_HOST_NORM_CMD_ENTRIES;
1921c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_NORM_CMD_QUEUE][AAC_CONSUMER_INDEX] =
1922c6eafcf2SScott Long 		AAC_HOST_NORM_CMD_ENTRIES;
1923c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_HIGH_CMD_QUEUE][AAC_PRODUCER_INDEX] =
1924c6eafcf2SScott Long 		AAC_HOST_HIGH_CMD_ENTRIES;
1925c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_HIGH_CMD_QUEUE][AAC_CONSUMER_INDEX] =
1926c6eafcf2SScott Long 		AAC_HOST_HIGH_CMD_ENTRIES;
1927c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_NORM_CMD_QUEUE][AAC_PRODUCER_INDEX] =
1928c6eafcf2SScott Long 		AAC_ADAP_NORM_CMD_ENTRIES;
1929c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_NORM_CMD_QUEUE][AAC_CONSUMER_INDEX] =
1930c6eafcf2SScott Long 		AAC_ADAP_NORM_CMD_ENTRIES;
1931c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_HIGH_CMD_QUEUE][AAC_PRODUCER_INDEX] =
1932c6eafcf2SScott Long 		AAC_ADAP_HIGH_CMD_ENTRIES;
1933c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_HIGH_CMD_QUEUE][AAC_CONSUMER_INDEX] =
1934c6eafcf2SScott Long 		AAC_ADAP_HIGH_CMD_ENTRIES;
1935c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_NORM_RESP_QUEUE][AAC_PRODUCER_INDEX]=
1936c6eafcf2SScott Long 		AAC_HOST_NORM_RESP_ENTRIES;
1937c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_NORM_RESP_QUEUE][AAC_CONSUMER_INDEX]=
1938c6eafcf2SScott Long 		AAC_HOST_NORM_RESP_ENTRIES;
1939c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_HIGH_RESP_QUEUE][AAC_PRODUCER_INDEX]=
1940c6eafcf2SScott Long 		AAC_HOST_HIGH_RESP_ENTRIES;
1941c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_HIGH_RESP_QUEUE][AAC_CONSUMER_INDEX]=
1942c6eafcf2SScott Long 		AAC_HOST_HIGH_RESP_ENTRIES;
1943c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_NORM_RESP_QUEUE][AAC_PRODUCER_INDEX]=
1944c6eafcf2SScott Long 		AAC_ADAP_NORM_RESP_ENTRIES;
1945c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_NORM_RESP_QUEUE][AAC_CONSUMER_INDEX]=
1946c6eafcf2SScott Long 		AAC_ADAP_NORM_RESP_ENTRIES;
1947c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_HIGH_RESP_QUEUE][AAC_PRODUCER_INDEX]=
1948c6eafcf2SScott Long 		AAC_ADAP_HIGH_RESP_ENTRIES;
1949c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_HIGH_RESP_QUEUE][AAC_CONSUMER_INDEX]=
1950c6eafcf2SScott Long 		AAC_ADAP_HIGH_RESP_ENTRIES;
1951c6eafcf2SScott Long 	sc->aac_qentries[AAC_HOST_NORM_CMD_QUEUE] =
1952c6eafcf2SScott Long 		&sc->aac_queues->qt_HostNormCmdQueue[0];
1953c6eafcf2SScott Long 	sc->aac_qentries[AAC_HOST_HIGH_CMD_QUEUE] =
1954c6eafcf2SScott Long 		&sc->aac_queues->qt_HostHighCmdQueue[0];
1955c6eafcf2SScott Long 	sc->aac_qentries[AAC_ADAP_NORM_CMD_QUEUE] =
1956c6eafcf2SScott Long 		&sc->aac_queues->qt_AdapNormCmdQueue[0];
1957c6eafcf2SScott Long 	sc->aac_qentries[AAC_ADAP_HIGH_CMD_QUEUE] =
1958c6eafcf2SScott Long 		&sc->aac_queues->qt_AdapHighCmdQueue[0];
1959c6eafcf2SScott Long 	sc->aac_qentries[AAC_HOST_NORM_RESP_QUEUE] =
1960c6eafcf2SScott Long 		&sc->aac_queues->qt_HostNormRespQueue[0];
1961c6eafcf2SScott Long 	sc->aac_qentries[AAC_HOST_HIGH_RESP_QUEUE] =
1962c6eafcf2SScott Long 		&sc->aac_queues->qt_HostHighRespQueue[0];
1963c6eafcf2SScott Long 	sc->aac_qentries[AAC_ADAP_NORM_RESP_QUEUE] =
1964c6eafcf2SScott Long 		&sc->aac_queues->qt_AdapNormRespQueue[0];
1965c6eafcf2SScott Long 	sc->aac_qentries[AAC_ADAP_HIGH_RESP_QUEUE] =
1966c6eafcf2SScott Long 		&sc->aac_queues->qt_AdapHighRespQueue[0];
196735863739SMike Smith 
196835863739SMike Smith 	/*
196935863739SMike Smith 	 * Do controller-type-specific initialisation
197035863739SMike Smith 	 */
197135863739SMike Smith 	switch (sc->aac_hwif) {
197235863739SMike Smith 	case AAC_HWIF_I960RX:
1973ff0991c4SAttilio Rao 		AAC_MEM0_SETREG4(sc, AAC_RX_ODBR, ~0);
197435863739SMike Smith 		break;
19754afedc31SScott Long 	case AAC_HWIF_RKT:
1976ff0991c4SAttilio Rao 		AAC_MEM0_SETREG4(sc, AAC_RKT_ODBR, ~0);
19774afedc31SScott Long 		break;
19784afedc31SScott Long 	default:
19794afedc31SScott Long 		break;
198035863739SMike Smith 	}
198135863739SMike Smith 
198235863739SMike Smith 	/*
198335863739SMike Smith 	 * Give the init structure to the controller.
198435863739SMike Smith 	 */
198535863739SMike Smith 	if (aac_sync_command(sc, AAC_MONKER_INITSTRUCT,
1986914da7d0SScott Long 			     sc->aac_common_busaddr +
1987914da7d0SScott Long 			     offsetof(struct aac_common, ac_init), 0, 0, 0,
1988914da7d0SScott Long 			     NULL)) {
1989914da7d0SScott Long 		device_printf(sc->aac_dev,
1990914da7d0SScott Long 			      "error establishing init structure\n");
1991a6d35632SScott Long 		error = EIO;
1992a6d35632SScott Long 		goto out;
199335863739SMike Smith 	}
199435863739SMike Smith 
1995a6d35632SScott Long 	error = 0;
1996a6d35632SScott Long out:
1997a6d35632SScott Long 	return(error);
199835863739SMike Smith }
199935863739SMike Smith 
200004f4d586SEd Maste static int
200104f4d586SEd Maste aac_setup_intr(struct aac_softc *sc)
200204f4d586SEd Maste {
2003da4882c2SMarius Strobl 
200404f4d586SEd Maste 	if (sc->flags & AAC_FLAGS_NEW_COMM) {
200504f4d586SEd Maste 		if (bus_setup_intr(sc->aac_dev, sc->aac_irq,
200604f4d586SEd Maste 				   INTR_MPSAFE|INTR_TYPE_BIO, NULL,
200704f4d586SEd Maste 				   aac_new_intr, sc, &sc->aac_intr)) {
200804f4d586SEd Maste 			device_printf(sc->aac_dev, "can't set up interrupt\n");
200904f4d586SEd Maste 			return (EINVAL);
201004f4d586SEd Maste 		}
201104f4d586SEd Maste 	} else {
201204f4d586SEd Maste 		if (bus_setup_intr(sc->aac_dev, sc->aac_irq,
2013e46b9eeaSEd Maste 				   INTR_TYPE_BIO, aac_filter, NULL,
201404f4d586SEd Maste 				   sc, &sc->aac_intr)) {
201504f4d586SEd Maste 			device_printf(sc->aac_dev,
2016e46b9eeaSEd Maste 				      "can't set up interrupt filter\n");
201704f4d586SEd Maste 			return (EINVAL);
201804f4d586SEd Maste 		}
201904f4d586SEd Maste 	}
202004f4d586SEd Maste 	return (0);
202104f4d586SEd Maste }
202204f4d586SEd Maste 
2023914da7d0SScott Long /*
202435863739SMike Smith  * Send a synchronous command to the controller and wait for a result.
20257cb209f5SScott Long  * Indicate if the controller completed the command with an error status.
202635863739SMike Smith  */
202735863739SMike Smith static int
202835863739SMike Smith aac_sync_command(struct aac_softc *sc, u_int32_t command,
202935863739SMike Smith 		 u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3,
203035863739SMike Smith 		 u_int32_t *sp)
203135863739SMike Smith {
203235863739SMike Smith 	time_t then;
203335863739SMike Smith 	u_int32_t status;
203435863739SMike Smith 
203531a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
203635863739SMike Smith 
203735863739SMike Smith 	/* populate the mailbox */
203835863739SMike Smith 	AAC_SET_MAILBOX(sc, command, arg0, arg1, arg2, arg3);
203935863739SMike Smith 
204035863739SMike Smith 	/* ensure the sync command doorbell flag is cleared */
204135863739SMike Smith 	AAC_CLEAR_ISTATUS(sc, AAC_DB_SYNC_COMMAND);
204235863739SMike Smith 
204335863739SMike Smith 	/* then set it to signal the adapter */
204435863739SMike Smith 	AAC_QNOTIFY(sc, AAC_DB_SYNC_COMMAND);
204535863739SMike Smith 
204635863739SMike Smith 	/* spin waiting for the command to complete */
20472b3b0f17SScott Long 	then = time_uptime;
204835863739SMike Smith 	do {
20492b3b0f17SScott Long 		if (time_uptime > (then + AAC_IMMEDIATE_TIMEOUT)) {
205031a0399eSEd Maste 			fwprintf(sc, HBA_FLAGS_DBG_ERROR_B, "timed out");
205135863739SMike Smith 			return(EIO);
205235863739SMike Smith 		}
205335863739SMike Smith 	} while (!(AAC_GET_ISTATUS(sc) & AAC_DB_SYNC_COMMAND));
205435863739SMike Smith 
205535863739SMike Smith 	/* clear the completion flag */
205635863739SMike Smith 	AAC_CLEAR_ISTATUS(sc, AAC_DB_SYNC_COMMAND);
205735863739SMike Smith 
205835863739SMike Smith 	/* get the command status */
2059a6d35632SScott Long 	status = AAC_GET_MAILBOX(sc, 0);
206035863739SMike Smith 	if (sp != NULL)
206135863739SMike Smith 		*sp = status;
20627cb209f5SScott Long 
2063a441b3fcSScott Long 	if (status != AAC_SRB_STS_SUCCESS)
20647cb209f5SScott Long 		return (-1);
20650b94a66eSMike Smith 	return(0);
206635863739SMike Smith }
206735863739SMike Smith 
2068cbfd045bSScott Long int
206935863739SMike Smith aac_sync_fib(struct aac_softc *sc, u_int32_t command, u_int32_t xferstate,
2070cbfd045bSScott Long 		 struct aac_fib *fib, u_int16_t datasize)
207135863739SMike Smith {
207231a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
20737cb209f5SScott Long 	mtx_assert(&sc->aac_io_lock, MA_OWNED);
207435863739SMike Smith 
207535863739SMike Smith 	if (datasize > AAC_FIB_DATASIZE)
207635863739SMike Smith 		return(EINVAL);
207735863739SMike Smith 
207835863739SMike Smith 	/*
207935863739SMike Smith 	 * Set up the sync FIB
208035863739SMike Smith 	 */
2081914da7d0SScott Long 	fib->Header.XferState = AAC_FIBSTATE_HOSTOWNED |
2082914da7d0SScott Long 				AAC_FIBSTATE_INITIALISED |
2083c6eafcf2SScott Long 				AAC_FIBSTATE_EMPTY;
208435863739SMike Smith 	fib->Header.XferState |= xferstate;
208535863739SMike Smith 	fib->Header.Command = command;
208635863739SMike Smith 	fib->Header.StructType = AAC_FIBTYPE_TFIB;
208742ef13a2SEd Maste 	fib->Header.Size = sizeof(struct aac_fib_header) + datasize;
208835863739SMike Smith 	fib->Header.SenderSize = sizeof(struct aac_fib);
2089b88ffdc8SScott Long 	fib->Header.SenderFibAddress = 0;	/* Not needed */
2090c6eafcf2SScott Long 	fib->Header.ReceiverFibAddress = sc->aac_common_busaddr +
2091914da7d0SScott Long 					 offsetof(struct aac_common,
2092914da7d0SScott Long 						  ac_sync_fib);
209335863739SMike Smith 
209435863739SMike Smith 	/*
209535863739SMike Smith 	 * Give the FIB to the controller, wait for a response.
209635863739SMike Smith 	 */
2097914da7d0SScott Long 	if (aac_sync_command(sc, AAC_MONKER_SYNCFIB,
2098914da7d0SScott Long 			     fib->Header.ReceiverFibAddress, 0, 0, 0, NULL)) {
209931a0399eSEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_ERROR_B, "IO error");
210035863739SMike Smith 		return(EIO);
210135863739SMike Smith 	}
210235863739SMike Smith 
210335863739SMike Smith 	return (0);
210435863739SMike Smith }
210535863739SMike Smith 
2106914da7d0SScott Long /*
210735863739SMike Smith  * Adapter-space FIB queue manipulation
210835863739SMike Smith  *
210935863739SMike Smith  * Note that the queue implementation here is a little funky; neither the PI or
211035863739SMike Smith  * CI will ever be zero.  This behaviour is a controller feature.
211135863739SMike Smith  */
2112da4882c2SMarius Strobl static const struct {
211335863739SMike Smith 	int		size;
211435863739SMike Smith 	int		notify;
211535863739SMike Smith } aac_qinfo[] = {
211635863739SMike Smith 	{AAC_HOST_NORM_CMD_ENTRIES, AAC_DB_COMMAND_NOT_FULL},
211735863739SMike Smith 	{AAC_HOST_HIGH_CMD_ENTRIES, 0},
211835863739SMike Smith 	{AAC_ADAP_NORM_CMD_ENTRIES, AAC_DB_COMMAND_READY},
211935863739SMike Smith 	{AAC_ADAP_HIGH_CMD_ENTRIES, 0},
212035863739SMike Smith 	{AAC_HOST_NORM_RESP_ENTRIES, AAC_DB_RESPONSE_NOT_FULL},
212135863739SMike Smith 	{AAC_HOST_HIGH_RESP_ENTRIES, 0},
212235863739SMike Smith 	{AAC_ADAP_NORM_RESP_ENTRIES, AAC_DB_RESPONSE_READY},
212335863739SMike Smith 	{AAC_ADAP_HIGH_RESP_ENTRIES, 0}
212435863739SMike Smith };
212535863739SMike Smith 
212635863739SMike Smith /*
2127c6eafcf2SScott Long  * Atomically insert an entry into the nominated queue, returns 0 on success or
2128c6eafcf2SScott Long  * EBUSY if the queue is full.
212935863739SMike Smith  *
21300b94a66eSMike Smith  * Note: it would be more efficient to defer notifying the controller in
2131914da7d0SScott Long  *	 the case where we may be inserting several entries in rapid succession,
2132914da7d0SScott Long  *	 but implementing this usefully may be difficult (it would involve a
2133c6eafcf2SScott Long  *	 separate queue/notify interface).
213435863739SMike Smith  */
213535863739SMike Smith static int
2136f6c4dd3fSScott Long aac_enqueue_fib(struct aac_softc *sc, int queue, struct aac_command *cm)
213735863739SMike Smith {
213835863739SMike Smith 	u_int32_t pi, ci;
21399e2e96d8SScott Long 	int error;
2140f6c4dd3fSScott Long 	u_int32_t fib_size;
2141f6c4dd3fSScott Long 	u_int32_t fib_addr;
2142f6c4dd3fSScott Long 
214331a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
214436e0bf6eSScott Long 
2145f6c4dd3fSScott Long 	fib_size = cm->cm_fib->Header.Size;
2146f6c4dd3fSScott Long 	fib_addr = cm->cm_fib->Header.ReceiverFibAddress;
214735863739SMike Smith 
214835863739SMike Smith 	/* get the producer/consumer indices */
214935863739SMike Smith 	pi = sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX];
215035863739SMike Smith 	ci = sc->aac_queues->qt_qindex[queue][AAC_CONSUMER_INDEX];
215135863739SMike Smith 
215235863739SMike Smith 	/* wrap the queue? */
215335863739SMike Smith 	if (pi >= aac_qinfo[queue].size)
215435863739SMike Smith 		pi = 0;
215535863739SMike Smith 
215635863739SMike Smith 	/* check for queue full */
215735863739SMike Smith 	if ((pi + 1) == ci) {
215835863739SMike Smith 		error = EBUSY;
215935863739SMike Smith 		goto out;
216035863739SMike Smith 	}
216135863739SMike Smith 
2162614c22b2SScott Long 	/*
2163614c22b2SScott Long 	 * To avoid a race with its completion interrupt, place this command on
2164614c22b2SScott Long 	 * the busy queue prior to advertising it to the controller.
2165614c22b2SScott Long 	 */
2166614c22b2SScott Long 	aac_enqueue_busy(cm);
2167614c22b2SScott Long 
216835863739SMike Smith 	/* populate queue entry */
216935863739SMike Smith 	(sc->aac_qentries[queue] + pi)->aq_fib_size = fib_size;
217035863739SMike Smith 	(sc->aac_qentries[queue] + pi)->aq_fib_addr = fib_addr;
217135863739SMike Smith 
217235863739SMike Smith 	/* update producer index */
217335863739SMike Smith 	sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX] = pi + 1;
217435863739SMike Smith 
217535863739SMike Smith 	/* notify the adapter if we know how */
217635863739SMike Smith 	if (aac_qinfo[queue].notify != 0)
217735863739SMike Smith 		AAC_QNOTIFY(sc, aac_qinfo[queue].notify);
217835863739SMike Smith 
217935863739SMike Smith 	error = 0;
218035863739SMike Smith 
218135863739SMike Smith out:
218235863739SMike Smith 	return(error);
218335863739SMike Smith }
218435863739SMike Smith 
218535863739SMike Smith /*
218636e0bf6eSScott Long  * Atomically remove one entry from the nominated queue, returns 0 on
218736e0bf6eSScott Long  * success or ENOENT if the queue is empty.
218835863739SMike Smith  */
218935863739SMike Smith static int
2190c6eafcf2SScott Long aac_dequeue_fib(struct aac_softc *sc, int queue, u_int32_t *fib_size,
2191c6eafcf2SScott Long 		struct aac_fib **fib_addr)
219235863739SMike Smith {
219335863739SMike Smith 	u_int32_t pi, ci;
2194149af931SScott Long 	u_int32_t fib_index;
21959e2e96d8SScott Long 	int error;
2196f6c4dd3fSScott Long 	int notify;
219735863739SMike Smith 
219831a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
219935863739SMike Smith 
220035863739SMike Smith 	/* get the producer/consumer indices */
220135863739SMike Smith 	pi = sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX];
220235863739SMike Smith 	ci = sc->aac_queues->qt_qindex[queue][AAC_CONSUMER_INDEX];
220335863739SMike Smith 
220435863739SMike Smith 	/* check for queue empty */
220535863739SMike Smith 	if (ci == pi) {
220635863739SMike Smith 		error = ENOENT;
220735863739SMike Smith 		goto out;
220835863739SMike Smith 	}
220935863739SMike Smith 
22107753acd2SScott Long 	/* wrap the pi so the following test works */
22117753acd2SScott Long 	if (pi >= aac_qinfo[queue].size)
22127753acd2SScott Long 		pi = 0;
22137753acd2SScott Long 
2214f6c4dd3fSScott Long 	notify = 0;
2215f6c4dd3fSScott Long 	if (ci == pi + 1)
2216f6c4dd3fSScott Long 		notify++;
2217f6c4dd3fSScott Long 
221835863739SMike Smith 	/* wrap the queue? */
221935863739SMike Smith 	if (ci >= aac_qinfo[queue].size)
222035863739SMike Smith 		ci = 0;
222135863739SMike Smith 
222235863739SMike Smith 	/* fetch the entry */
222335863739SMike Smith 	*fib_size = (sc->aac_qentries[queue] + ci)->aq_fib_size;
2224149af931SScott Long 
2225149af931SScott Long 	switch (queue) {
2226149af931SScott Long 	case AAC_HOST_NORM_CMD_QUEUE:
2227149af931SScott Long 	case AAC_HOST_HIGH_CMD_QUEUE:
2228149af931SScott Long 		/*
2229149af931SScott Long 		 * The aq_fib_addr is only 32 bits wide so it can't be counted
2230149af931SScott Long 		 * on to hold an address.  For AIF's, the adapter assumes
2231149af931SScott Long 		 * that it's giving us an address into the array of AIF fibs.
2232149af931SScott Long 		 * Therefore, we have to convert it to an index.
2233149af931SScott Long 		 */
2234149af931SScott Long 		fib_index = (sc->aac_qentries[queue] + ci)->aq_fib_addr /
2235149af931SScott Long 			sizeof(struct aac_fib);
2236149af931SScott Long 		*fib_addr = &sc->aac_common->ac_fibs[fib_index];
2237149af931SScott Long 		break;
2238149af931SScott Long 
2239149af931SScott Long 	case AAC_HOST_NORM_RESP_QUEUE:
2240149af931SScott Long 	case AAC_HOST_HIGH_RESP_QUEUE:
2241149af931SScott Long 	{
2242149af931SScott Long 		struct aac_command *cm;
2243149af931SScott Long 
2244149af931SScott Long 		/*
2245149af931SScott Long 		 * As above, an index is used instead of an actual address.
2246149af931SScott Long 		 * Gotta shift the index to account for the fast response
2247149af931SScott Long 		 * bit.  No other correction is needed since this value was
2248149af931SScott Long 		 * originally provided by the driver via the SenderFibAddress
2249149af931SScott Long 		 * field.
2250149af931SScott Long 		 */
2251149af931SScott Long 		fib_index = (sc->aac_qentries[queue] + ci)->aq_fib_addr;
22527cb209f5SScott Long 		cm = sc->aac_commands + (fib_index >> 2);
2253149af931SScott Long 		*fib_addr = cm->cm_fib;
225435863739SMike Smith 
2255f30ac74cSScott Long 		/*
2256f30ac74cSScott Long 		 * Is this a fast response? If it is, update the fib fields in
2257149af931SScott Long 		 * local memory since the whole fib isn't DMA'd back up.
2258f30ac74cSScott Long 		 */
2259149af931SScott Long 		if (fib_index & 0x01) {
2260f30ac74cSScott Long 			(*fib_addr)->Header.XferState |= AAC_FIBSTATE_DONEADAP;
2261f30ac74cSScott Long 			*((u_int32_t*)((*fib_addr)->data)) = AAC_ERROR_NORMAL;
2262f30ac74cSScott Long 		}
2263149af931SScott Long 		break;
2264149af931SScott Long 	}
2265149af931SScott Long 	default:
2266149af931SScott Long 		panic("Invalid queue in aac_dequeue_fib()");
2267149af931SScott Long 		break;
2268149af931SScott Long 	}
2269149af931SScott Long 
227035863739SMike Smith 	/* update consumer index */
227135863739SMike Smith 	sc->aac_queues->qt_qindex[queue][AAC_CONSUMER_INDEX] = ci + 1;
227235863739SMike Smith 
227335863739SMike Smith 	/* if we have made the queue un-full, notify the adapter */
2274f6c4dd3fSScott Long 	if (notify && (aac_qinfo[queue].notify != 0))
227535863739SMike Smith 		AAC_QNOTIFY(sc, aac_qinfo[queue].notify);
227635863739SMike Smith 	error = 0;
227735863739SMike Smith 
227835863739SMike Smith out:
227935863739SMike Smith 	return(error);
228035863739SMike Smith }
228135863739SMike Smith 
2282914da7d0SScott Long /*
228336e0bf6eSScott Long  * Put our response to an Adapter Initialed Fib on the response queue
228436e0bf6eSScott Long  */
228536e0bf6eSScott Long static int
228636e0bf6eSScott Long aac_enqueue_response(struct aac_softc *sc, int queue, struct aac_fib *fib)
228736e0bf6eSScott Long {
228836e0bf6eSScott Long 	u_int32_t pi, ci;
22899e2e96d8SScott Long 	int error;
229036e0bf6eSScott Long 	u_int32_t fib_size;
229136e0bf6eSScott Long 	u_int32_t fib_addr;
229236e0bf6eSScott Long 
229331a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
229436e0bf6eSScott Long 
229536e0bf6eSScott Long 	/* Tell the adapter where the FIB is */
229636e0bf6eSScott Long 	fib_size = fib->Header.Size;
229736e0bf6eSScott Long 	fib_addr = fib->Header.SenderFibAddress;
229836e0bf6eSScott Long 	fib->Header.ReceiverFibAddress = fib_addr;
229936e0bf6eSScott Long 
230036e0bf6eSScott Long 	/* get the producer/consumer indices */
230136e0bf6eSScott Long 	pi = sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX];
230236e0bf6eSScott Long 	ci = sc->aac_queues->qt_qindex[queue][AAC_CONSUMER_INDEX];
230336e0bf6eSScott Long 
230436e0bf6eSScott Long 	/* wrap the queue? */
230536e0bf6eSScott Long 	if (pi >= aac_qinfo[queue].size)
230636e0bf6eSScott Long 		pi = 0;
230736e0bf6eSScott Long 
230836e0bf6eSScott Long 	/* check for queue full */
230936e0bf6eSScott Long 	if ((pi + 1) == ci) {
231036e0bf6eSScott Long 		error = EBUSY;
231136e0bf6eSScott Long 		goto out;
231236e0bf6eSScott Long 	}
231336e0bf6eSScott Long 
231436e0bf6eSScott Long 	/* populate queue entry */
231536e0bf6eSScott Long 	(sc->aac_qentries[queue] + pi)->aq_fib_size = fib_size;
231636e0bf6eSScott Long 	(sc->aac_qentries[queue] + pi)->aq_fib_addr = fib_addr;
231736e0bf6eSScott Long 
231836e0bf6eSScott Long 	/* update producer index */
231936e0bf6eSScott Long 	sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX] = pi + 1;
232036e0bf6eSScott Long 
232136e0bf6eSScott Long 	/* notify the adapter if we know how */
232236e0bf6eSScott Long 	if (aac_qinfo[queue].notify != 0)
232336e0bf6eSScott Long 		AAC_QNOTIFY(sc, aac_qinfo[queue].notify);
232436e0bf6eSScott Long 
232536e0bf6eSScott Long 	error = 0;
232636e0bf6eSScott Long 
232736e0bf6eSScott Long out:
232836e0bf6eSScott Long 	return(error);
232936e0bf6eSScott Long }
233036e0bf6eSScott Long 
2331914da7d0SScott Long /*
23320b94a66eSMike Smith  * Check for commands that have been outstanding for a suspiciously long time,
23330b94a66eSMike Smith  * and complain about them.
23340b94a66eSMike Smith  */
23350b94a66eSMike Smith static void
23360b94a66eSMike Smith aac_timeout(struct aac_softc *sc)
23370b94a66eSMike Smith {
23380b94a66eSMike Smith 	struct aac_command *cm;
23390b94a66eSMike Smith 	time_t deadline;
234015c37be0SScott Long 	int timedout, code;
23410b94a66eSMike Smith 
2342f6c4dd3fSScott Long 	/*
234370545d1aSScott Long 	 * Traverse the busy command list, bitch about late commands once
2344914da7d0SScott Long 	 * only.
2345914da7d0SScott Long 	 */
234615c37be0SScott Long 	timedout = 0;
23472b3b0f17SScott Long 	deadline = time_uptime - AAC_CMD_TIMEOUT;
23480b94a66eSMike Smith 	TAILQ_FOREACH(cm, &sc->aac_busy, cm_link) {
2349f6c4dd3fSScott Long 		if ((cm->cm_timestamp  < deadline)
23503e507710SEd Maste 		    && !(cm->cm_flags & AAC_CMD_TIMEDOUT)) {
23510b94a66eSMike Smith 			cm->cm_flags |= AAC_CMD_TIMEDOUT;
2352914da7d0SScott Long 			device_printf(sc->aac_dev,
23535aa4bb5bSEd Maste 			    "COMMAND %p (TYPE %d) TIMEOUT AFTER %d SECONDS\n",
23545aa4bb5bSEd Maste 			    cm, cm->cm_fib->Header.Command,
23555aa4bb5bSEd Maste 			    (int)(time_uptime-cm->cm_timestamp));
23560b94a66eSMike Smith 			AAC_PRINT_FIB(sc, cm->cm_fib);
235715c37be0SScott Long 			timedout++;
23580b94a66eSMike Smith 		}
23590b94a66eSMike Smith 	}
23600b94a66eSMike Smith 
236115c37be0SScott Long 	if (timedout) {
236215c37be0SScott Long 		code = AAC_GET_FWSTATUS(sc);
236315c37be0SScott Long 		if (code != AAC_UP_AND_RUNNING) {
236415c37be0SScott Long 			device_printf(sc->aac_dev, "WARNING! Controller is no "
236515c37be0SScott Long 				      "longer running! code= 0x%x\n", code);
236615c37be0SScott Long 		}
236715c37be0SScott Long 	}
23680b94a66eSMike Smith }
23690b94a66eSMike Smith 
2370914da7d0SScott Long /*
2371914da7d0SScott Long  * Interface Function Vectors
2372914da7d0SScott Long  */
237335863739SMike Smith 
2374914da7d0SScott Long /*
237535863739SMike Smith  * Read the current firmware status word.
237635863739SMike Smith  */
237735863739SMike Smith static int
237835863739SMike Smith aac_sa_get_fwstatus(struct aac_softc *sc)
237935863739SMike Smith {
238031a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
238135863739SMike Smith 
2382ff0991c4SAttilio Rao 	return(AAC_MEM0_GETREG4(sc, AAC_SA_FWSTATUS));
238335863739SMike Smith }
238435863739SMike Smith 
238535863739SMike Smith static int
238635863739SMike Smith aac_rx_get_fwstatus(struct aac_softc *sc)
238735863739SMike Smith {
238831a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
238935863739SMike Smith 
23904824be88SEd Maste 	return(AAC_MEM0_GETREG4(sc, sc->flags & AAC_FLAGS_NEW_COMM ?
23914824be88SEd Maste 	    AAC_RX_OMR0 : AAC_RX_FWSTATUS));
239235863739SMike Smith }
239335863739SMike Smith 
2394b3457b51SScott Long static int
23954afedc31SScott Long aac_rkt_get_fwstatus(struct aac_softc *sc)
23964afedc31SScott Long {
239731a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
23984afedc31SScott Long 
23994824be88SEd Maste 	return(AAC_MEM0_GETREG4(sc, sc->flags & AAC_FLAGS_NEW_COMM ?
24004824be88SEd Maste 	    AAC_RKT_OMR0 : AAC_RKT_FWSTATUS));
24014afedc31SScott Long }
24024afedc31SScott Long 
2403914da7d0SScott Long /*
240435863739SMike Smith  * Notify the controller of a change in a given queue
240535863739SMike Smith  */
240635863739SMike Smith 
240735863739SMike Smith static void
240835863739SMike Smith aac_sa_qnotify(struct aac_softc *sc, int qbit)
240935863739SMike Smith {
241031a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
241135863739SMike Smith 
2412ff0991c4SAttilio Rao 	AAC_MEM0_SETREG2(sc, AAC_SA_DOORBELL1_SET, qbit);
241335863739SMike Smith }
241435863739SMike Smith 
241535863739SMike Smith static void
241635863739SMike Smith aac_rx_qnotify(struct aac_softc *sc, int qbit)
241735863739SMike Smith {
241831a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
241935863739SMike Smith 
2420ff0991c4SAttilio Rao 	AAC_MEM0_SETREG4(sc, AAC_RX_IDBR, qbit);
242135863739SMike Smith }
242235863739SMike Smith 
2423b3457b51SScott Long static void
24244afedc31SScott Long aac_rkt_qnotify(struct aac_softc *sc, int qbit)
24254afedc31SScott Long {
242631a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
24274afedc31SScott Long 
2428ff0991c4SAttilio Rao 	AAC_MEM0_SETREG4(sc, AAC_RKT_IDBR, qbit);
24294afedc31SScott Long }
24304afedc31SScott Long 
2431914da7d0SScott Long /*
243235863739SMike Smith  * Get the interrupt reason bits
243335863739SMike Smith  */
243435863739SMike Smith static int
243535863739SMike Smith aac_sa_get_istatus(struct aac_softc *sc)
243635863739SMike Smith {
243731a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
243835863739SMike Smith 
2439ff0991c4SAttilio Rao 	return(AAC_MEM0_GETREG2(sc, AAC_SA_DOORBELL0));
244035863739SMike Smith }
244135863739SMike Smith 
244235863739SMike Smith static int
244335863739SMike Smith aac_rx_get_istatus(struct aac_softc *sc)
244435863739SMike Smith {
244531a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
244635863739SMike Smith 
2447ff0991c4SAttilio Rao 	return(AAC_MEM0_GETREG4(sc, AAC_RX_ODBR));
244835863739SMike Smith }
244935863739SMike Smith 
2450b3457b51SScott Long static int
24514afedc31SScott Long aac_rkt_get_istatus(struct aac_softc *sc)
24524afedc31SScott Long {
245331a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
24544afedc31SScott Long 
2455ff0991c4SAttilio Rao 	return(AAC_MEM0_GETREG4(sc, AAC_RKT_ODBR));
24564afedc31SScott Long }
24574afedc31SScott Long 
2458914da7d0SScott Long /*
245935863739SMike Smith  * Clear some interrupt reason bits
246035863739SMike Smith  */
246135863739SMike Smith static void
246235863739SMike Smith aac_sa_clear_istatus(struct aac_softc *sc, int mask)
246335863739SMike Smith {
246431a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
246535863739SMike Smith 
2466ff0991c4SAttilio Rao 	AAC_MEM0_SETREG2(sc, AAC_SA_DOORBELL0_CLEAR, mask);
246735863739SMike Smith }
246835863739SMike Smith 
246935863739SMike Smith static void
247035863739SMike Smith aac_rx_clear_istatus(struct aac_softc *sc, int mask)
247135863739SMike Smith {
247231a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
247335863739SMike Smith 
2474ff0991c4SAttilio Rao 	AAC_MEM0_SETREG4(sc, AAC_RX_ODBR, mask);
247535863739SMike Smith }
247635863739SMike Smith 
2477b3457b51SScott Long static void
24784afedc31SScott Long aac_rkt_clear_istatus(struct aac_softc *sc, int mask)
24794afedc31SScott Long {
248031a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
24814afedc31SScott Long 
2482ff0991c4SAttilio Rao 	AAC_MEM0_SETREG4(sc, AAC_RKT_ODBR, mask);
24834afedc31SScott Long }
24844afedc31SScott Long 
2485914da7d0SScott Long /*
248635863739SMike Smith  * Populate the mailbox and set the command word
248735863739SMike Smith  */
248835863739SMike Smith static void
248935863739SMike Smith aac_sa_set_mailbox(struct aac_softc *sc, u_int32_t command,
249035863739SMike Smith 		u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3)
249135863739SMike Smith {
249231a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
249335863739SMike Smith 
2494ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, AAC_SA_MAILBOX, command);
2495ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, AAC_SA_MAILBOX + 4, arg0);
2496ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, AAC_SA_MAILBOX + 8, arg1);
2497ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, AAC_SA_MAILBOX + 12, arg2);
2498ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, AAC_SA_MAILBOX + 16, arg3);
249935863739SMike Smith }
250035863739SMike Smith 
250135863739SMike Smith static void
250235863739SMike Smith aac_rx_set_mailbox(struct aac_softc *sc, u_int32_t command,
250335863739SMike Smith 		u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3)
250435863739SMike Smith {
250531a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
250635863739SMike Smith 
2507ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, AAC_RX_MAILBOX, command);
2508ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, AAC_RX_MAILBOX + 4, arg0);
2509ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, AAC_RX_MAILBOX + 8, arg1);
2510ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, AAC_RX_MAILBOX + 12, arg2);
2511ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, AAC_RX_MAILBOX + 16, arg3);
251235863739SMike Smith }
251335863739SMike Smith 
2514b3457b51SScott Long static void
25154afedc31SScott Long aac_rkt_set_mailbox(struct aac_softc *sc, u_int32_t command, u_int32_t arg0,
25164afedc31SScott Long 		    u_int32_t arg1, u_int32_t arg2, u_int32_t arg3)
25174afedc31SScott Long {
251831a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
25194afedc31SScott Long 
2520ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, AAC_RKT_MAILBOX, command);
2521ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, AAC_RKT_MAILBOX + 4, arg0);
2522ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, AAC_RKT_MAILBOX + 8, arg1);
2523ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, AAC_RKT_MAILBOX + 12, arg2);
2524ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, AAC_RKT_MAILBOX + 16, arg3);
25254afedc31SScott Long }
25264afedc31SScott Long 
2527914da7d0SScott Long /*
252835863739SMike Smith  * Fetch the immediate command status word
252935863739SMike Smith  */
253035863739SMike Smith static int
2531a6d35632SScott Long aac_sa_get_mailbox(struct aac_softc *sc, int mb)
253235863739SMike Smith {
253331a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
253435863739SMike Smith 
2535ff0991c4SAttilio Rao 	return(AAC_MEM1_GETREG4(sc, AAC_SA_MAILBOX + (mb * 4)));
253635863739SMike Smith }
253735863739SMike Smith 
253835863739SMike Smith static int
2539a6d35632SScott Long aac_rx_get_mailbox(struct aac_softc *sc, int mb)
254035863739SMike Smith {
254131a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
254235863739SMike Smith 
2543ff0991c4SAttilio Rao 	return(AAC_MEM1_GETREG4(sc, AAC_RX_MAILBOX + (mb * 4)));
254435863739SMike Smith }
254535863739SMike Smith 
2546b3457b51SScott Long static int
25474afedc31SScott Long aac_rkt_get_mailbox(struct aac_softc *sc, int mb)
25484afedc31SScott Long {
254931a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
25504afedc31SScott Long 
2551ff0991c4SAttilio Rao 	return(AAC_MEM1_GETREG4(sc, AAC_RKT_MAILBOX + (mb * 4)));
25524afedc31SScott Long }
25534afedc31SScott Long 
2554914da7d0SScott Long /*
255535863739SMike Smith  * Set/clear interrupt masks
255635863739SMike Smith  */
255735863739SMike Smith static void
255835863739SMike Smith aac_sa_set_interrupts(struct aac_softc *sc, int enable)
255935863739SMike Smith {
256031a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "%sable interrupts", enable ? "en" : "dis");
256135863739SMike Smith 
256235863739SMike Smith 	if (enable) {
2563ff0991c4SAttilio Rao 		AAC_MEM0_SETREG2((sc), AAC_SA_MASK0_CLEAR, AAC_DB_INTERRUPTS);
256435863739SMike Smith 	} else {
2565ff0991c4SAttilio Rao 		AAC_MEM0_SETREG2((sc), AAC_SA_MASK0_SET, ~0);
256635863739SMike Smith 	}
256735863739SMike Smith }
256835863739SMike Smith 
256935863739SMike Smith static void
257035863739SMike Smith aac_rx_set_interrupts(struct aac_softc *sc, int enable)
257135863739SMike Smith {
257231a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "%sable interrupts", enable ? "en" : "dis");
257335863739SMike Smith 
257435863739SMike Smith 	if (enable) {
25757cb209f5SScott Long 		if (sc->flags & AAC_FLAGS_NEW_COMM)
2576ff0991c4SAttilio Rao 			AAC_MEM0_SETREG4(sc, AAC_RX_OIMR, ~AAC_DB_INT_NEW_COMM);
25777cb209f5SScott Long 		else
2578ff0991c4SAttilio Rao 			AAC_MEM0_SETREG4(sc, AAC_RX_OIMR, ~AAC_DB_INTERRUPTS);
257935863739SMike Smith 	} else {
2580ff0991c4SAttilio Rao 		AAC_MEM0_SETREG4(sc, AAC_RX_OIMR, ~0);
258135863739SMike Smith 	}
258235863739SMike Smith }
258335863739SMike Smith 
2584b3457b51SScott Long static void
25854afedc31SScott Long aac_rkt_set_interrupts(struct aac_softc *sc, int enable)
25864afedc31SScott Long {
258731a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "%sable interrupts", enable ? "en" : "dis");
25884afedc31SScott Long 
25894afedc31SScott Long 	if (enable) {
25907cb209f5SScott Long 		if (sc->flags & AAC_FLAGS_NEW_COMM)
2591ff0991c4SAttilio Rao 			AAC_MEM0_SETREG4(sc, AAC_RKT_OIMR, ~AAC_DB_INT_NEW_COMM);
25927cb209f5SScott Long 		else
2593ff0991c4SAttilio Rao 			AAC_MEM0_SETREG4(sc, AAC_RKT_OIMR, ~AAC_DB_INTERRUPTS);
25944afedc31SScott Long 	} else {
2595ff0991c4SAttilio Rao 		AAC_MEM0_SETREG4(sc, AAC_RKT_OIMR, ~0);
25964afedc31SScott Long 	}
25974afedc31SScott Long }
25984afedc31SScott Long 
2599914da7d0SScott Long /*
26007cb209f5SScott Long  * New comm. interface: Send command functions
26017cb209f5SScott Long  */
26027cb209f5SScott Long static int
26037cb209f5SScott Long aac_rx_send_command(struct aac_softc *sc, struct aac_command *cm)
26047cb209f5SScott Long {
26057cb209f5SScott Long 	u_int32_t index, device;
26067cb209f5SScott Long 
260731a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "send command (new comm.)");
26087cb209f5SScott Long 
2609ff0991c4SAttilio Rao 	index = AAC_MEM0_GETREG4(sc, AAC_RX_IQUE);
26107cb209f5SScott Long 	if (index == 0xffffffffL)
2611ff0991c4SAttilio Rao 		index = AAC_MEM0_GETREG4(sc, AAC_RX_IQUE);
26127cb209f5SScott Long 	if (index == 0xffffffffL)
26137cb209f5SScott Long 		return index;
26147cb209f5SScott Long 	aac_enqueue_busy(cm);
26157cb209f5SScott Long 	device = index;
2616ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, device, (u_int32_t)(cm->cm_fibphys & 0xffffffffUL));
26177cb209f5SScott Long 	device += 4;
2618ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, device, (u_int32_t)(cm->cm_fibphys >> 32));
26197cb209f5SScott Long 	device += 4;
2620ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, device, cm->cm_fib->Header.Size);
2621ff0991c4SAttilio Rao 	AAC_MEM0_SETREG4(sc, AAC_RX_IQUE, index);
26227cb209f5SScott Long 	return 0;
26237cb209f5SScott Long }
26247cb209f5SScott Long 
26257cb209f5SScott Long static int
26267cb209f5SScott Long aac_rkt_send_command(struct aac_softc *sc, struct aac_command *cm)
26277cb209f5SScott Long {
26287cb209f5SScott Long 	u_int32_t index, device;
26297cb209f5SScott Long 
263031a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "send command (new comm.)");
26317cb209f5SScott Long 
2632ff0991c4SAttilio Rao 	index = AAC_MEM0_GETREG4(sc, AAC_RKT_IQUE);
26337cb209f5SScott Long 	if (index == 0xffffffffL)
2634ff0991c4SAttilio Rao 		index = AAC_MEM0_GETREG4(sc, AAC_RKT_IQUE);
26357cb209f5SScott Long 	if (index == 0xffffffffL)
26367cb209f5SScott Long 		return index;
26377cb209f5SScott Long 	aac_enqueue_busy(cm);
26387cb209f5SScott Long 	device = index;
2639ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, device, (u_int32_t)(cm->cm_fibphys & 0xffffffffUL));
26407cb209f5SScott Long 	device += 4;
2641ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, device, (u_int32_t)(cm->cm_fibphys >> 32));
26427cb209f5SScott Long 	device += 4;
2643ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, device, cm->cm_fib->Header.Size);
2644ff0991c4SAttilio Rao 	AAC_MEM0_SETREG4(sc, AAC_RKT_IQUE, index);
26457cb209f5SScott Long 	return 0;
26467cb209f5SScott Long }
26477cb209f5SScott Long 
26487cb209f5SScott Long /*
26497cb209f5SScott Long  * New comm. interface: get, set outbound queue index
26507cb209f5SScott Long  */
26517cb209f5SScott Long static int
26527cb209f5SScott Long aac_rx_get_outb_queue(struct aac_softc *sc)
26537cb209f5SScott Long {
265431a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
26557cb209f5SScott Long 
2656ff0991c4SAttilio Rao 	return(AAC_MEM0_GETREG4(sc, AAC_RX_OQUE));
26577cb209f5SScott Long }
26587cb209f5SScott Long 
26597cb209f5SScott Long static int
26607cb209f5SScott Long aac_rkt_get_outb_queue(struct aac_softc *sc)
26617cb209f5SScott Long {
266231a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
26637cb209f5SScott Long 
2664ff0991c4SAttilio Rao 	return(AAC_MEM0_GETREG4(sc, AAC_RKT_OQUE));
26657cb209f5SScott Long }
26667cb209f5SScott Long 
26677cb209f5SScott Long static void
26687cb209f5SScott Long aac_rx_set_outb_queue(struct aac_softc *sc, int index)
26697cb209f5SScott Long {
267031a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
26717cb209f5SScott Long 
2672ff0991c4SAttilio Rao 	AAC_MEM0_SETREG4(sc, AAC_RX_OQUE, index);
26737cb209f5SScott Long }
26747cb209f5SScott Long 
26757cb209f5SScott Long static void
26767cb209f5SScott Long aac_rkt_set_outb_queue(struct aac_softc *sc, int index)
26777cb209f5SScott Long {
267831a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
26797cb209f5SScott Long 
2680ff0991c4SAttilio Rao 	AAC_MEM0_SETREG4(sc, AAC_RKT_OQUE, index);
26817cb209f5SScott Long }
26827cb209f5SScott Long 
26837cb209f5SScott Long /*
2684914da7d0SScott Long  * Debugging and Diagnostics
2685914da7d0SScott Long  */
268635863739SMike Smith 
2687914da7d0SScott Long /*
268835863739SMike Smith  * Print some information about the controller.
268935863739SMike Smith  */
269035863739SMike Smith static void
269135863739SMike Smith aac_describe_controller(struct aac_softc *sc)
269235863739SMike Smith {
2693cbfd045bSScott Long 	struct aac_fib *fib;
269435863739SMike Smith 	struct aac_adapter_info	*info;
26957ea2d558SEd Maste 	char *adapter_type = "Adaptec RAID controller";
269635863739SMike Smith 
269731a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
269835863739SMike Smith 
269981b3da08SScott Long 	mtx_lock(&sc->aac_io_lock);
270003b5fe51SScott Long 	aac_alloc_sync_fib(sc, &fib);
2701cbfd045bSScott Long 
2702cbfd045bSScott Long 	fib->data[0] = 0;
2703cbfd045bSScott Long 	if (aac_sync_fib(sc, RequestAdapterInfo, 0, fib, 1)) {
270435863739SMike Smith 		device_printf(sc->aac_dev, "RequestAdapterInfo failed\n");
2705fe3cb0e1SScott Long 		aac_release_sync_fib(sc);
270681b3da08SScott Long 		mtx_unlock(&sc->aac_io_lock);
270735863739SMike Smith 		return;
270835863739SMike Smith 	}
270935863739SMike Smith 
2710bd971c49SScott Long 	/* save the kernel revision structure for later use */
2711bd971c49SScott Long 	info = (struct aac_adapter_info *)&fib->data[0];
2712bd971c49SScott Long 	sc->aac_revision = info->KernelRevision;
2713bd971c49SScott Long 
2714bd971c49SScott Long 	if (bootverbose) {
2715b1c56c68SScott Long 		device_printf(sc->aac_dev, "%s %dMHz, %dMB memory "
2716b1c56c68SScott Long 		    "(%dMB cache, %dMB execution), %s\n",
2717c6eafcf2SScott Long 		    aac_describe_code(aac_cpu_variant, info->CpuVariant),
2718b1c56c68SScott Long 		    info->ClockSpeed, info->TotalMem / (1024 * 1024),
2719b1c56c68SScott Long 		    info->BufferMem / (1024 * 1024),
2720b1c56c68SScott Long 		    info->ExecutionMem / (1024 * 1024),
2721914da7d0SScott Long 		    aac_describe_code(aac_battery_platform,
2722914da7d0SScott Long 		    info->batteryPlatform));
272335863739SMike Smith 
2724bd971c49SScott Long 		device_printf(sc->aac_dev,
2725bd971c49SScott Long 		    "Kernel %d.%d-%d, Build %d, S/N %6X\n",
272635863739SMike Smith 		    info->KernelRevision.external.comp.major,
272735863739SMike Smith 		    info->KernelRevision.external.comp.minor,
272835863739SMike Smith 		    info->KernelRevision.external.comp.dash,
272936e0bf6eSScott Long 		    info->KernelRevision.buildNumber,
273036e0bf6eSScott Long 		    (u_int32_t)(info->SerialNumber & 0xffffff));
2731fe3cb0e1SScott Long 
2732a6d35632SScott Long 		device_printf(sc->aac_dev, "Supported Options=%b\n",
2733a6d35632SScott Long 			      sc->supported_options,
2734a6d35632SScott Long 			      "\20"
2735a6d35632SScott Long 			      "\1SNAPSHOT"
2736a6d35632SScott Long 			      "\2CLUSTERS"
2737a6d35632SScott Long 			      "\3WCACHE"
2738a6d35632SScott Long 			      "\4DATA64"
2739a6d35632SScott Long 			      "\5HOSTTIME"
2740a6d35632SScott Long 			      "\6RAID50"
2741a6d35632SScott Long 			      "\7WINDOW4GB"
2742a6d35632SScott Long 			      "\10SCSIUPGD"
2743a6d35632SScott Long 			      "\11SOFTERR"
2744a6d35632SScott Long 			      "\12NORECOND"
2745a6d35632SScott Long 			      "\13SGMAP64"
2746a6d35632SScott Long 			      "\14ALARM"
27477cb209f5SScott Long 			      "\15NONDASD"
27487cb209f5SScott Long 			      "\16SCSIMGT"
27497cb209f5SScott Long 			      "\17RAIDSCSI"
27507cb209f5SScott Long 			      "\21ADPTINFO"
27517cb209f5SScott Long 			      "\22NEWCOMM"
27527cb209f5SScott Long 			      "\23ARRAY64BIT"
27537cb209f5SScott Long 			      "\24HEATSENSOR");
2754a6d35632SScott Long 	}
275555aa1136SEd Maste 
275655aa1136SEd Maste 	if (sc->supported_options & AAC_SUPPORTED_SUPPLEMENT_ADAPTER_INFO) {
275755aa1136SEd Maste 		fib->data[0] = 0;
275855aa1136SEd Maste 		if (aac_sync_fib(sc, RequestSupplementAdapterInfo, 0, fib, 1))
275955aa1136SEd Maste 			device_printf(sc->aac_dev,
276055aa1136SEd Maste 			    "RequestSupplementAdapterInfo failed\n");
276155aa1136SEd Maste 		else
276255aa1136SEd Maste 			adapter_type = ((struct aac_supplement_adapter_info *)
276355aa1136SEd Maste 			    &fib->data[0])->AdapterTypeText;
276455aa1136SEd Maste 	}
276555aa1136SEd Maste 	device_printf(sc->aac_dev, "%s, aac driver %d.%d.%d-%d\n",
276655aa1136SEd Maste 		adapter_type,
27678e7e6335SEd Maste 		AAC_DRIVER_MAJOR_VERSION, AAC_DRIVER_MINOR_VERSION,
27688e7e6335SEd Maste 		AAC_DRIVER_BUGFIX_LEVEL, AAC_DRIVER_BUILD);
276955aa1136SEd Maste 
2770bd971c49SScott Long 	aac_release_sync_fib(sc);
277181b3da08SScott Long 	mtx_unlock(&sc->aac_io_lock);
277235863739SMike Smith }
277335863739SMike Smith 
2774914da7d0SScott Long /*
277535863739SMike Smith  * Look up a text description of a numeric error code and return a pointer to
277635863739SMike Smith  * same.
277735863739SMike Smith  */
2778da4882c2SMarius Strobl static const char *
2779da4882c2SMarius Strobl aac_describe_code(const struct aac_code_lookup *table, u_int32_t code)
278035863739SMike Smith {
278135863739SMike Smith 	int i;
278235863739SMike Smith 
278335863739SMike Smith 	for (i = 0; table[i].string != NULL; i++)
278435863739SMike Smith 		if (table[i].code == code)
278535863739SMike Smith 			return(table[i].string);
278635863739SMike Smith 	return(table[i + 1].string);
278735863739SMike Smith }
278835863739SMike Smith 
2789914da7d0SScott Long /*
2790914da7d0SScott Long  * Management Interface
2791914da7d0SScott Long  */
279235863739SMike Smith 
279335863739SMike Smith static int
279400b4e54aSWarner Losh aac_open(struct cdev *dev, int flags, int fmt, struct thread *td)
279535863739SMike Smith {
2796914da7d0SScott Long 	struct aac_softc *sc;
279735863739SMike Smith 
2798914da7d0SScott Long 	sc = dev->si_drv1;
279931a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
280004f798ecSAttilio Rao 	device_busy(sc->aac_dev);
2801dfe2c294SAttilio Rao 	devfs_set_cdevpriv(sc, aac_cdevpriv_dtor);
280235863739SMike Smith 
280335863739SMike Smith 	return 0;
280435863739SMike Smith }
280535863739SMike Smith 
280635863739SMike Smith static int
280700b4e54aSWarner Losh aac_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td)
280835863739SMike Smith {
2809914da7d0SScott Long 	union aac_statrequest *as;
2810914da7d0SScott Long 	struct aac_softc *sc;
28110b94a66eSMike Smith 	int error = 0;
281235863739SMike Smith 
2813914da7d0SScott Long 	as = (union aac_statrequest *)arg;
2814914da7d0SScott Long 	sc = dev->si_drv1;
281531a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
2816914da7d0SScott Long 
281735863739SMike Smith 	switch (cmd) {
28180b94a66eSMike Smith 	case AACIO_STATS:
28190b94a66eSMike Smith 		switch (as->as_item) {
28200b94a66eSMike Smith 		case AACQ_FREE:
28210b94a66eSMike Smith 		case AACQ_BIO:
28220b94a66eSMike Smith 		case AACQ_READY:
28230b94a66eSMike Smith 		case AACQ_BUSY:
2824c6eafcf2SScott Long 			bcopy(&sc->aac_qstat[as->as_item], &as->as_qstat,
2825c6eafcf2SScott Long 			      sizeof(struct aac_qstat));
28260b94a66eSMike Smith 			break;
28270b94a66eSMike Smith 		default:
28280b94a66eSMike Smith 			error = ENOENT;
28290b94a66eSMike Smith 			break;
28300b94a66eSMike Smith 		}
28310b94a66eSMike Smith 	break;
28320b94a66eSMike Smith 
283335863739SMike Smith 	case FSACTL_SENDFIB:
2834f355c0e0SEd Maste 	case FSACTL_SEND_LARGE_FIB:
2835fb0c27d7SScott Long 		arg = *(caddr_t*)arg;
2836fb0c27d7SScott Long 	case FSACTL_LNX_SENDFIB:
2837f355c0e0SEd Maste 	case FSACTL_LNX_SEND_LARGE_FIB:
283831a0399eSEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_SENDFIB");
283935863739SMike Smith 		error = aac_ioctl_sendfib(sc, arg);
284035863739SMike Smith 		break;
2841f355c0e0SEd Maste 	case FSACTL_SEND_RAW_SRB:
2842f355c0e0SEd Maste 		arg = *(caddr_t*)arg;
2843f355c0e0SEd Maste 	case FSACTL_LNX_SEND_RAW_SRB:
284431a0399eSEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_SEND_RAW_SRB");
2845f355c0e0SEd Maste 		error = aac_ioctl_send_raw_srb(sc, arg);
2846f355c0e0SEd Maste 		break;
284735863739SMike Smith 	case FSACTL_AIF_THREAD:
2848fb0c27d7SScott Long 	case FSACTL_LNX_AIF_THREAD:
284931a0399eSEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_AIF_THREAD");
285035863739SMike Smith 		error = EINVAL;
285135863739SMike Smith 		break;
285235863739SMike Smith 	case FSACTL_OPEN_GET_ADAPTER_FIB:
2853fb0c27d7SScott Long 		arg = *(caddr_t*)arg;
2854fb0c27d7SScott Long 	case FSACTL_LNX_OPEN_GET_ADAPTER_FIB:
285531a0399eSEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_OPEN_GET_ADAPTER_FIB");
2856a723a548SEd Maste 		error = aac_open_aif(sc, arg);
285735863739SMike Smith 		break;
285835863739SMike Smith 	case FSACTL_GET_NEXT_ADAPTER_FIB:
2859fb0c27d7SScott Long 		arg = *(caddr_t*)arg;
2860fb0c27d7SScott Long 	case FSACTL_LNX_GET_NEXT_ADAPTER_FIB:
286131a0399eSEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_GET_NEXT_ADAPTER_FIB");
2862fb0c27d7SScott Long 		error = aac_getnext_aif(sc, arg);
286335863739SMike Smith 		break;
286435863739SMike Smith 	case FSACTL_CLOSE_GET_ADAPTER_FIB:
2865a723a548SEd Maste 		arg = *(caddr_t*)arg;
2866fb0c27d7SScott Long 	case FSACTL_LNX_CLOSE_GET_ADAPTER_FIB:
286731a0399eSEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_CLOSE_GET_ADAPTER_FIB");
2868a723a548SEd Maste 		error = aac_close_aif(sc, arg);
286935863739SMike Smith 		break;
287035863739SMike Smith 	case FSACTL_MINIPORT_REV_CHECK:
2871fb0c27d7SScott Long 		arg = *(caddr_t*)arg;
2872fb0c27d7SScott Long 	case FSACTL_LNX_MINIPORT_REV_CHECK:
287331a0399eSEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_MINIPORT_REV_CHECK");
2874fb0c27d7SScott Long 		error = aac_rev_check(sc, arg);
287535863739SMike Smith 		break;
287636e0bf6eSScott Long 	case FSACTL_QUERY_DISK:
287736e0bf6eSScott Long 		arg = *(caddr_t*)arg;
287836e0bf6eSScott Long 	case FSACTL_LNX_QUERY_DISK:
287931a0399eSEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_QUERY_DISK");
288036e0bf6eSScott Long 		error = aac_query_disk(sc, arg);
288136e0bf6eSScott Long 		break;
288236e0bf6eSScott Long 	case FSACTL_DELETE_DISK:
288336e0bf6eSScott Long 	case FSACTL_LNX_DELETE_DISK:
2884914da7d0SScott Long 		/*
2885914da7d0SScott Long 		 * We don't trust the underland to tell us when to delete a
2886914da7d0SScott Long 		 * container, rather we rely on an AIF coming from the
2887914da7d0SScott Long 		 * controller
2888914da7d0SScott Long 		 */
288936e0bf6eSScott Long 		error = 0;
289036e0bf6eSScott Long 		break;
28917cb209f5SScott Long 	case FSACTL_GET_PCI_INFO:
28927cb209f5SScott Long 		arg = *(caddr_t*)arg;
28937cb209f5SScott Long 	case FSACTL_LNX_GET_PCI_INFO:
289431a0399eSEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_GET_PCI_INFO");
28957cb209f5SScott Long 		error = aac_get_pci_info(sc, arg);
28967cb209f5SScott Long 		break;
28976d307336SEd Maste 	case FSACTL_GET_FEATURES:
28986d307336SEd Maste 		arg = *(caddr_t*)arg;
28996d307336SEd Maste 	case FSACTL_LNX_GET_FEATURES:
29006d307336SEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_GET_FEATURES");
29016d307336SEd Maste 		error = aac_supported_features(sc, arg);
29026d307336SEd Maste 		break;
290335863739SMike Smith 	default:
290431a0399eSEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "unsupported cmd 0x%lx\n", cmd);
290535863739SMike Smith 		error = EINVAL;
290635863739SMike Smith 		break;
290735863739SMike Smith 	}
290835863739SMike Smith 	return(error);
290935863739SMike Smith }
291035863739SMike Smith 
2911b3457b51SScott Long static int
291200b4e54aSWarner Losh aac_poll(struct cdev *dev, int poll_events, struct thread *td)
2913b3457b51SScott Long {
2914b3457b51SScott Long 	struct aac_softc *sc;
2915ef0b687cSEd Maste 	struct aac_fib_context *ctx;
2916b3457b51SScott Long 	int revents;
2917b3457b51SScott Long 
2918b3457b51SScott Long 	sc = dev->si_drv1;
2919b3457b51SScott Long 	revents = 0;
2920b3457b51SScott Long 
2921bb6fe253SScott Long 	mtx_lock(&sc->aac_aifq_lock);
2922b3457b51SScott Long 	if ((poll_events & (POLLRDNORM | POLLIN)) != 0) {
2923ef0b687cSEd Maste 		for (ctx = sc->fibctx; ctx; ctx = ctx->next) {
2924ef0b687cSEd Maste 			if (ctx->ctx_idx != sc->aifq_idx || ctx->ctx_wrap) {
2925b3457b51SScott Long 				revents |= poll_events & (POLLIN | POLLRDNORM);
2926ef0b687cSEd Maste 				break;
2927ef0b687cSEd Maste 			}
2928ef0b687cSEd Maste 		}
2929b3457b51SScott Long 	}
2930bb6fe253SScott Long 	mtx_unlock(&sc->aac_aifq_lock);
2931b3457b51SScott Long 
2932b3457b51SScott Long 	if (revents == 0) {
2933b3457b51SScott Long 		if (poll_events & (POLLIN | POLLRDNORM))
2934b3457b51SScott Long 			selrecord(td, &sc->rcv_select);
2935b3457b51SScott Long 	}
2936b3457b51SScott Long 
2937b3457b51SScott Long 	return (revents);
2938b3457b51SScott Long }
2939b3457b51SScott Long 
29407cb209f5SScott Long static void
29417cb209f5SScott Long aac_ioctl_event(struct aac_softc *sc, struct aac_event *event, void *arg)
29427cb209f5SScott Long {
29437cb209f5SScott Long 
29447cb209f5SScott Long 	switch (event->ev_type) {
29457cb209f5SScott Long 	case AAC_EVENT_CMFREE:
29460c40d5beSEd Maste 		mtx_assert(&sc->aac_io_lock, MA_OWNED);
29471a681311SLuoqi Chen 		if (aac_alloc_command(sc, (struct aac_command **)arg)) {
29487cb209f5SScott Long 			aac_add_event(sc, event);
29497cb209f5SScott Long 			return;
29507cb209f5SScott Long 		}
29517cb209f5SScott Long 		free(event, M_AACBUF);
29528eeb2ca6SScott Long 		wakeup(arg);
29537cb209f5SScott Long 		break;
29547cb209f5SScott Long 	default:
29557cb209f5SScott Long 		break;
29567cb209f5SScott Long 	}
29577cb209f5SScott Long }
29587cb209f5SScott Long 
2959914da7d0SScott Long /*
296035863739SMike Smith  * Send a FIB supplied from userspace
296135863739SMike Smith  */
296235863739SMike Smith static int
296335863739SMike Smith aac_ioctl_sendfib(struct aac_softc *sc, caddr_t ufib)
296435863739SMike Smith {
296535863739SMike Smith 	struct aac_command *cm;
296635863739SMike Smith 	int size, error;
296735863739SMike Smith 
296831a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
296935863739SMike Smith 
297035863739SMike Smith 	cm = NULL;
297135863739SMike Smith 
297235863739SMike Smith 	/*
297335863739SMike Smith 	 * Get a command
297435863739SMike Smith 	 */
2975bb6fe253SScott Long 	mtx_lock(&sc->aac_io_lock);
297635863739SMike Smith 	if (aac_alloc_command(sc, &cm)) {
29777cb209f5SScott Long 		struct aac_event *event;
29787cb209f5SScott Long 
29797cb209f5SScott Long 		event = malloc(sizeof(struct aac_event), M_AACBUF,
29807cb209f5SScott Long 		    M_NOWAIT | M_ZERO);
29817cb209f5SScott Long 		if (event == NULL) {
298235863739SMike Smith 			error = EBUSY;
2983f16627aaSEd Maste 			mtx_unlock(&sc->aac_io_lock);
298435863739SMike Smith 			goto out;
298535863739SMike Smith 		}
29867cb209f5SScott Long 		event->ev_type = AAC_EVENT_CMFREE;
29877cb209f5SScott Long 		event->ev_callback = aac_ioctl_event;
29887cb209f5SScott Long 		event->ev_arg = &cm;
29897cb209f5SScott Long 		aac_add_event(sc, event);
29908eeb2ca6SScott Long 		msleep(&cm, &sc->aac_io_lock, 0, "sendfib", 0);
29917cb209f5SScott Long 	}
299293cfca22SScott Long 	mtx_unlock(&sc->aac_io_lock);
299335863739SMike Smith 
299435863739SMike Smith 	/*
299535863739SMike Smith 	 * Fetch the FIB header, then re-copy to get data as well.
299635863739SMike Smith 	 */
2997914da7d0SScott Long 	if ((error = copyin(ufib, cm->cm_fib,
2998914da7d0SScott Long 			    sizeof(struct aac_fib_header))) != 0)
299935863739SMike Smith 		goto out;
300035863739SMike Smith 	size = cm->cm_fib->Header.Size + sizeof(struct aac_fib_header);
3001f355c0e0SEd Maste 	if (size > sc->aac_max_fib_size) {
3002f355c0e0SEd Maste 		device_printf(sc->aac_dev, "incoming FIB oversized (%d > %d)\n",
3003f355c0e0SEd Maste 			      size, sc->aac_max_fib_size);
3004f355c0e0SEd Maste 		size = sc->aac_max_fib_size;
300535863739SMike Smith 	}
300635863739SMike Smith 	if ((error = copyin(ufib, cm->cm_fib, size)) != 0)
300735863739SMike Smith 		goto out;
300835863739SMike Smith 	cm->cm_fib->Header.Size = size;
30092b3b0f17SScott Long 	cm->cm_timestamp = time_uptime;
301035863739SMike Smith 
301135863739SMike Smith 	/*
301235863739SMike Smith 	 * Pass the FIB to the controller, wait for it to complete.
301335863739SMike Smith 	 */
301493cfca22SScott Long 	mtx_lock(&sc->aac_io_lock);
3015f16627aaSEd Maste 	error = aac_wait_command(cm);
3016f16627aaSEd Maste 	mtx_unlock(&sc->aac_io_lock);
3017f16627aaSEd Maste 	if (error != 0) {
301870545d1aSScott Long 		device_printf(sc->aac_dev,
301970545d1aSScott Long 			      "aac_wait_command return %d\n", error);
302035863739SMike Smith 		goto out;
3021b3457b51SScott Long 	}
302235863739SMike Smith 
302335863739SMike Smith 	/*
302435863739SMike Smith 	 * Copy the FIB and data back out to the caller.
302535863739SMike Smith 	 */
302635863739SMike Smith 	size = cm->cm_fib->Header.Size;
3027f355c0e0SEd Maste 	if (size > sc->aac_max_fib_size) {
3028f355c0e0SEd Maste 		device_printf(sc->aac_dev, "outbound FIB oversized (%d > %d)\n",
3029f355c0e0SEd Maste 			      size, sc->aac_max_fib_size);
3030f355c0e0SEd Maste 		size = sc->aac_max_fib_size;
303135863739SMike Smith 	}
303235863739SMike Smith 	error = copyout(cm->cm_fib, ufib, size);
303335863739SMike Smith 
303435863739SMike Smith out:
3035f6c4dd3fSScott Long 	if (cm != NULL) {
3036f16627aaSEd Maste 		mtx_lock(&sc->aac_io_lock);
303735863739SMike Smith 		aac_release_command(cm);
3038bb6fe253SScott Long 		mtx_unlock(&sc->aac_io_lock);
3039f16627aaSEd Maste 	}
304035863739SMike Smith 	return(error);
304135863739SMike Smith }
304235863739SMike Smith 
3043914da7d0SScott Long /*
3044f355c0e0SEd Maste  * Send a passthrough FIB supplied from userspace
3045f355c0e0SEd Maste  */
3046f355c0e0SEd Maste static int
3047f355c0e0SEd Maste aac_ioctl_send_raw_srb(struct aac_softc *sc, caddr_t arg)
3048f355c0e0SEd Maste {
30497b90e5ecSAttilio Rao 	struct aac_command *cm;
30507b90e5ecSAttilio Rao 	struct aac_event *event;
30517b90e5ecSAttilio Rao 	struct aac_fib *fib;
30527b90e5ecSAttilio Rao 	struct aac_srb *srbcmd, *user_srb;
30537b90e5ecSAttilio Rao 	struct aac_sg_entry *sge;
30547b90e5ecSAttilio Rao 	struct aac_sg_entry64 *sge64;
30557b90e5ecSAttilio Rao 	void *srb_sg_address, *ureply;
30567b90e5ecSAttilio Rao 	uint32_t fibsize, srb_sg_bytecount;
30577b90e5ecSAttilio Rao 	int error, transfer_data;
30587b90e5ecSAttilio Rao 
30597b90e5ecSAttilio Rao 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
30607b90e5ecSAttilio Rao 
30617b90e5ecSAttilio Rao 	cm = NULL;
30627b90e5ecSAttilio Rao 	transfer_data = 0;
30637b90e5ecSAttilio Rao 	fibsize = 0;
30647b90e5ecSAttilio Rao 	user_srb = (struct aac_srb *)arg;
30657b90e5ecSAttilio Rao 
30667b90e5ecSAttilio Rao 	mtx_lock(&sc->aac_io_lock);
30677b90e5ecSAttilio Rao 	if (aac_alloc_command(sc, &cm)) {
30687b90e5ecSAttilio Rao 		 event = malloc(sizeof(struct aac_event), M_AACBUF,
30697b90e5ecSAttilio Rao 		    M_NOWAIT | M_ZERO);
30707b90e5ecSAttilio Rao 		if (event == NULL) {
30717b90e5ecSAttilio Rao 			error = EBUSY;
30727b90e5ecSAttilio Rao 			mtx_unlock(&sc->aac_io_lock);
30737b90e5ecSAttilio Rao 			goto out;
30747b90e5ecSAttilio Rao 		}
30757b90e5ecSAttilio Rao 		event->ev_type = AAC_EVENT_CMFREE;
30767b90e5ecSAttilio Rao 		event->ev_callback = aac_ioctl_event;
30777b90e5ecSAttilio Rao 		event->ev_arg = &cm;
30787b90e5ecSAttilio Rao 		aac_add_event(sc, event);
30797b90e5ecSAttilio Rao 		msleep(cm, &sc->aac_io_lock, 0, "aacraw", 0);
30807b90e5ecSAttilio Rao 	}
30817b90e5ecSAttilio Rao 	mtx_unlock(&sc->aac_io_lock);
30827b90e5ecSAttilio Rao 
30837b90e5ecSAttilio Rao 	cm->cm_data = NULL;
30847b90e5ecSAttilio Rao 	fib = cm->cm_fib;
30857b90e5ecSAttilio Rao 	srbcmd = (struct aac_srb *)fib->data;
30867b90e5ecSAttilio Rao 	error = copyin(&user_srb->data_len, &fibsize, sizeof(uint32_t));
30877b90e5ecSAttilio Rao 	if (error != 0)
30887b90e5ecSAttilio Rao 		goto out;
30897b90e5ecSAttilio Rao 	if (fibsize > (sc->aac_max_fib_size - sizeof(struct aac_fib_header))) {
30907b90e5ecSAttilio Rao 		error = EINVAL;
30917b90e5ecSAttilio Rao 		goto out;
30927b90e5ecSAttilio Rao 	}
30937b90e5ecSAttilio Rao 	error = copyin(user_srb, srbcmd, fibsize);
30947b90e5ecSAttilio Rao 	if (error != 0)
30957b90e5ecSAttilio Rao 		goto out;
30967b90e5ecSAttilio Rao 	srbcmd->function = 0;
30977b90e5ecSAttilio Rao 	srbcmd->retry_limit = 0;
30987b90e5ecSAttilio Rao 	if (srbcmd->sg_map.SgCount > 1) {
30997b90e5ecSAttilio Rao 		error = EINVAL;
31007b90e5ecSAttilio Rao 		goto out;
31017b90e5ecSAttilio Rao 	}
31027b90e5ecSAttilio Rao 
31037b90e5ecSAttilio Rao 	/* Retrieve correct SG entries. */
31047b90e5ecSAttilio Rao 	if (fibsize == (sizeof(struct aac_srb) +
31057b90e5ecSAttilio Rao 	    srbcmd->sg_map.SgCount * sizeof(struct aac_sg_entry))) {
31067b90e5ecSAttilio Rao 		sge = srbcmd->sg_map.SgEntry;
31077b90e5ecSAttilio Rao 		sge64 = NULL;
31087b90e5ecSAttilio Rao 		srb_sg_bytecount = sge->SgByteCount;
31096eafba26SAttilio Rao 		srb_sg_address = (void *)(uintptr_t)sge->SgAddress;
31107b90e5ecSAttilio Rao 	}
31117b90e5ecSAttilio Rao #ifdef __amd64__
31127b90e5ecSAttilio Rao 	else if (fibsize == (sizeof(struct aac_srb) +
31137b90e5ecSAttilio Rao 	    srbcmd->sg_map.SgCount * sizeof(struct aac_sg_entry64))) {
31147b90e5ecSAttilio Rao 		sge = NULL;
31157b90e5ecSAttilio Rao 		sge64 = (struct aac_sg_entry64 *)srbcmd->sg_map.SgEntry;
31167b90e5ecSAttilio Rao 		srb_sg_bytecount = sge64->SgByteCount;
31177b90e5ecSAttilio Rao 		srb_sg_address = (void *)sge64->SgAddress;
31187b90e5ecSAttilio Rao 		if (sge64->SgAddress > 0xffffffffull &&
31197b90e5ecSAttilio Rao 		    (sc->flags & AAC_FLAGS_SG_64BIT) == 0) {
31207b90e5ecSAttilio Rao 			error = EINVAL;
31217b90e5ecSAttilio Rao 			goto out;
31227b90e5ecSAttilio Rao 		}
31237b90e5ecSAttilio Rao 	}
31247b90e5ecSAttilio Rao #endif
31257b90e5ecSAttilio Rao 	else {
31267b90e5ecSAttilio Rao 		error = EINVAL;
31277b90e5ecSAttilio Rao 		goto out;
31287b90e5ecSAttilio Rao 	}
31297b90e5ecSAttilio Rao 	ureply = (char *)arg + fibsize;
31307b90e5ecSAttilio Rao 	srbcmd->data_len = srb_sg_bytecount;
31317b90e5ecSAttilio Rao 	if (srbcmd->sg_map.SgCount == 1)
31327b90e5ecSAttilio Rao 		transfer_data = 1;
31337b90e5ecSAttilio Rao 
31347b90e5ecSAttilio Rao 	cm->cm_sgtable = (struct aac_sg_table *)&srbcmd->sg_map;
31357b90e5ecSAttilio Rao 	if (transfer_data) {
31367b90e5ecSAttilio Rao 		cm->cm_datalen = srb_sg_bytecount;
31377b90e5ecSAttilio Rao 		cm->cm_data = malloc(cm->cm_datalen, M_AACBUF, M_NOWAIT);
31387b90e5ecSAttilio Rao 		if (cm->cm_data == NULL) {
31397b90e5ecSAttilio Rao 			error = ENOMEM;
31407b90e5ecSAttilio Rao 			goto out;
31417b90e5ecSAttilio Rao 		}
31427b90e5ecSAttilio Rao 		if (srbcmd->flags & AAC_SRB_FLAGS_DATA_IN)
31437b90e5ecSAttilio Rao 			cm->cm_flags |= AAC_CMD_DATAIN;
31447b90e5ecSAttilio Rao 		if (srbcmd->flags & AAC_SRB_FLAGS_DATA_OUT) {
31457b90e5ecSAttilio Rao 			cm->cm_flags |= AAC_CMD_DATAOUT;
31467b90e5ecSAttilio Rao 			error = copyin(srb_sg_address, cm->cm_data,
31477b90e5ecSAttilio Rao 			    cm->cm_datalen);
31487b90e5ecSAttilio Rao 			if (error != 0)
31497b90e5ecSAttilio Rao 				goto out;
31507b90e5ecSAttilio Rao 		}
31517b90e5ecSAttilio Rao 	}
31527b90e5ecSAttilio Rao 
31537b90e5ecSAttilio Rao 	fib->Header.Size = sizeof(struct aac_fib_header) +
31547b90e5ecSAttilio Rao 	    sizeof(struct aac_srb);
31557b90e5ecSAttilio Rao 	fib->Header.XferState =
31567b90e5ecSAttilio Rao 	    AAC_FIBSTATE_HOSTOWNED   |
31577b90e5ecSAttilio Rao 	    AAC_FIBSTATE_INITIALISED |
31587b90e5ecSAttilio Rao 	    AAC_FIBSTATE_EMPTY       |
31597b90e5ecSAttilio Rao 	    AAC_FIBSTATE_FROMHOST    |
31607b90e5ecSAttilio Rao 	    AAC_FIBSTATE_REXPECTED   |
31617b90e5ecSAttilio Rao 	    AAC_FIBSTATE_NORM        |
31627b90e5ecSAttilio Rao 	    AAC_FIBSTATE_ASYNC       |
31637b90e5ecSAttilio Rao 	    AAC_FIBSTATE_FAST_RESPONSE;
31647b90e5ecSAttilio Rao 	fib->Header.Command = (sc->flags & AAC_FLAGS_SG_64BIT) != 0 ?
31657b90e5ecSAttilio Rao 	    ScsiPortCommandU64 : ScsiPortCommand;
31667b90e5ecSAttilio Rao 
31677b90e5ecSAttilio Rao 	mtx_lock(&sc->aac_io_lock);
31687b90e5ecSAttilio Rao 	aac_wait_command(cm);
31697b90e5ecSAttilio Rao 	mtx_unlock(&sc->aac_io_lock);
31707b90e5ecSAttilio Rao 
31717b90e5ecSAttilio Rao 	if (transfer_data && (srbcmd->flags & AAC_SRB_FLAGS_DATA_IN) != 0) {
31727b90e5ecSAttilio Rao 		error = copyout(cm->cm_data, srb_sg_address, cm->cm_datalen);
31737b90e5ecSAttilio Rao 		if (error != 0)
31747b90e5ecSAttilio Rao 			goto out;
31757b90e5ecSAttilio Rao 	}
31767b90e5ecSAttilio Rao 	error = copyout(fib->data, ureply, sizeof(struct aac_srb_response));
31777b90e5ecSAttilio Rao out:
31787b90e5ecSAttilio Rao 	if (cm != NULL) {
31797b90e5ecSAttilio Rao 		if (cm->cm_data != NULL)
31807b90e5ecSAttilio Rao 			free(cm->cm_data, M_AACBUF);
31817b90e5ecSAttilio Rao 		mtx_lock(&sc->aac_io_lock);
31827b90e5ecSAttilio Rao 		aac_release_command(cm);
31837b90e5ecSAttilio Rao 		mtx_unlock(&sc->aac_io_lock);
31847b90e5ecSAttilio Rao 	}
31857b90e5ecSAttilio Rao 	return(error);
3186f355c0e0SEd Maste }
3187f355c0e0SEd Maste 
3188f355c0e0SEd Maste /*
3189dfe2c294SAttilio Rao  * cdevpriv interface private destructor.
3190dfe2c294SAttilio Rao  */
3191dfe2c294SAttilio Rao static void
3192dfe2c294SAttilio Rao aac_cdevpriv_dtor(void *arg)
3193dfe2c294SAttilio Rao {
3194dfe2c294SAttilio Rao 	struct aac_softc *sc;
3195dfe2c294SAttilio Rao 
3196dfe2c294SAttilio Rao 	sc = arg;
3197dfe2c294SAttilio Rao 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
3198dfe2c294SAttilio Rao 	mtx_lock(&Giant);
3199dfe2c294SAttilio Rao 	device_unbusy(sc->aac_dev);
3200dfe2c294SAttilio Rao 	mtx_unlock(&Giant);
3201dfe2c294SAttilio Rao }
3202dfe2c294SAttilio Rao 
3203dfe2c294SAttilio Rao /*
320435863739SMike Smith  * Handle an AIF sent to us by the controller; queue it for later reference.
320536e0bf6eSScott Long  * If the queue fills up, then drop the older entries.
320635863739SMike Smith  */
320735863739SMike Smith static void
320836e0bf6eSScott Long aac_handle_aif(struct aac_softc *sc, struct aac_fib *fib)
320935863739SMike Smith {
321036e0bf6eSScott Long 	struct aac_aif_command *aif;
321136e0bf6eSScott Long 	struct aac_container *co, *co_next;
3212a723a548SEd Maste 	struct aac_fib_context *ctx;
321304f4d586SEd Maste 	struct aac_mntinforesp *mir;
3214a723a548SEd Maste 	int next, current, found;
3215795d7dc0SScott Long 	int count = 0, added = 0, i = 0;
3216851f59d7SEd Maste 	uint32_t channel;
321735863739SMike Smith 
321831a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
321935863739SMike Smith 
322036e0bf6eSScott Long 	aif = (struct aac_aif_command*)&fib->data[0];
322136e0bf6eSScott Long 	aac_print_aif(sc, aif);
322236e0bf6eSScott Long 
322336e0bf6eSScott Long 	/* Is it an event that we should care about? */
322436e0bf6eSScott Long 	switch (aif->command) {
322536e0bf6eSScott Long 	case AifCmdEventNotify:
322636e0bf6eSScott Long 		switch (aif->data.EN.type) {
322736e0bf6eSScott Long 		case AifEnAddContainer:
322836e0bf6eSScott Long 		case AifEnDeleteContainer:
322936e0bf6eSScott Long 			/*
3230914da7d0SScott Long 			 * A container was added or deleted, but the message
3231914da7d0SScott Long 			 * doesn't tell us anything else!  Re-enumerate the
3232914da7d0SScott Long 			 * containers and sort things out.
323336e0bf6eSScott Long 			 */
323403b5fe51SScott Long 			aac_alloc_sync_fib(sc, &fib);
323536e0bf6eSScott Long 			do {
323636e0bf6eSScott Long 				/*
3237914da7d0SScott Long 				 * Ask the controller for its containers one at
3238914da7d0SScott Long 				 * a time.
3239914da7d0SScott Long 				 * XXX What if the controller's list changes
3240914da7d0SScott Long 				 * midway through this enumaration?
324136e0bf6eSScott Long 				 * XXX This should be done async.
324236e0bf6eSScott Long 				 */
324304f4d586SEd Maste 				if ((mir = aac_get_container_info(sc, fib, i)) == NULL)
324436e0bf6eSScott Long 					continue;
324504f4d586SEd Maste 				if (i == 0)
3246795d7dc0SScott Long 					count = mir->MntRespCount;
324736e0bf6eSScott Long 				/*
3248914da7d0SScott Long 				 * Check the container against our list.
3249914da7d0SScott Long 				 * co->co_found was already set to 0 in a
3250914da7d0SScott Long 				 * previous run.
325136e0bf6eSScott Long 				 */
3252cbfd045bSScott Long 				if ((mir->Status == ST_OK) &&
3253cbfd045bSScott Long 				    (mir->MntTable[0].VolType != CT_NONE)) {
325436e0bf6eSScott Long 					found = 0;
3255914da7d0SScott Long 					TAILQ_FOREACH(co,
3256914da7d0SScott Long 						      &sc->aac_container_tqh,
3257914da7d0SScott Long 						      co_link) {
325836e0bf6eSScott Long 						if (co->co_mntobj.ObjectId ==
3259cbfd045bSScott Long 						    mir->MntTable[0].ObjectId) {
326036e0bf6eSScott Long 							co->co_found = 1;
326136e0bf6eSScott Long 							found = 1;
326236e0bf6eSScott Long 							break;
326336e0bf6eSScott Long 						}
326436e0bf6eSScott Long 					}
3265914da7d0SScott Long 					/*
3266914da7d0SScott Long 					 * If the container matched, continue
3267914da7d0SScott Long 					 * in the list.
3268914da7d0SScott Long 					 */
326936e0bf6eSScott Long 					if (found) {
327036e0bf6eSScott Long 						i++;
327136e0bf6eSScott Long 						continue;
327236e0bf6eSScott Long 					}
327336e0bf6eSScott Long 
327436e0bf6eSScott Long 					/*
3275914da7d0SScott Long 					 * This is a new container.  Do all the
327670545d1aSScott Long 					 * appropriate things to set it up.
327770545d1aSScott Long 					 */
3278cbfd045bSScott Long 					aac_add_container(sc, mir, 1);
327936e0bf6eSScott Long 					added = 1;
328036e0bf6eSScott Long 				}
328136e0bf6eSScott Long 				i++;
3282795d7dc0SScott Long 			} while ((i < count) && (i < AAC_MAX_CONTAINERS));
3283cbfd045bSScott Long 			aac_release_sync_fib(sc);
328436e0bf6eSScott Long 
328536e0bf6eSScott Long 			/*
3286914da7d0SScott Long 			 * Go through our list of containers and see which ones
3287914da7d0SScott Long 			 * were not marked 'found'.  Since the controller didn't
3288914da7d0SScott Long 			 * list them they must have been deleted.  Do the
3289914da7d0SScott Long 			 * appropriate steps to destroy the device.  Also reset
3290914da7d0SScott Long 			 * the co->co_found field.
329136e0bf6eSScott Long 			 */
329236e0bf6eSScott Long 			co = TAILQ_FIRST(&sc->aac_container_tqh);
329336e0bf6eSScott Long 			while (co != NULL) {
329436e0bf6eSScott Long 				if (co->co_found == 0) {
32957cb209f5SScott Long 					mtx_unlock(&sc->aac_io_lock);
3296a56fe095SJohn Baldwin 					mtx_lock(&Giant);
3297914da7d0SScott Long 					device_delete_child(sc->aac_dev,
3298914da7d0SScott Long 							    co->co_disk);
3299a56fe095SJohn Baldwin 					mtx_unlock(&Giant);
33007cb209f5SScott Long 					mtx_lock(&sc->aac_io_lock);
330136e0bf6eSScott Long 					co_next = TAILQ_NEXT(co, co_link);
3302bb6fe253SScott Long 					mtx_lock(&sc->aac_container_lock);
3303914da7d0SScott Long 					TAILQ_REMOVE(&sc->aac_container_tqh, co,
3304914da7d0SScott Long 						     co_link);
3305bb6fe253SScott Long 					mtx_unlock(&sc->aac_container_lock);
3306ba1d57e7SScott Long 					free(co, M_AACBUF);
330736e0bf6eSScott Long 					co = co_next;
330836e0bf6eSScott Long 				} else {
330936e0bf6eSScott Long 					co->co_found = 0;
331036e0bf6eSScott Long 					co = TAILQ_NEXT(co, co_link);
331136e0bf6eSScott Long 				}
331236e0bf6eSScott Long 			}
331336e0bf6eSScott Long 
331436e0bf6eSScott Long 			/* Attach the newly created containers */
33157cb209f5SScott Long 			if (added) {
33167cb209f5SScott Long 				mtx_unlock(&sc->aac_io_lock);
3317a56fe095SJohn Baldwin 				mtx_lock(&Giant);
331836e0bf6eSScott Long 				bus_generic_attach(sc->aac_dev);
3319a56fe095SJohn Baldwin 				mtx_unlock(&Giant);
33207cb209f5SScott Long 				mtx_lock(&sc->aac_io_lock);
33217cb209f5SScott Long 			}
332236e0bf6eSScott Long 
332336e0bf6eSScott Long 			break;
332436e0bf6eSScott Long 
3325851f59d7SEd Maste 		case AifEnEnclosureManagement:
3326851f59d7SEd Maste 			switch (aif->data.EN.data.EEE.eventType) {
3327851f59d7SEd Maste 			case AIF_EM_DRIVE_INSERTION:
3328851f59d7SEd Maste 			case AIF_EM_DRIVE_REMOVAL:
3329851f59d7SEd Maste 				channel = aif->data.EN.data.EEE.unitID;
3330851f59d7SEd Maste 				if (sc->cam_rescan_cb != NULL)
3331851f59d7SEd Maste 					sc->cam_rescan_cb(sc,
3332851f59d7SEd Maste 					    (channel >> 24) & 0xF,
3333851f59d7SEd Maste 					    (channel & 0xFFFF));
3334851f59d7SEd Maste 				break;
3335851f59d7SEd Maste 			}
3336851f59d7SEd Maste 			break;
3337851f59d7SEd Maste 
3338851f59d7SEd Maste 		case AifEnAddJBOD:
3339851f59d7SEd Maste 		case AifEnDeleteJBOD:
3340851f59d7SEd Maste 			channel = aif->data.EN.data.ECE.container;
3341851f59d7SEd Maste 			if (sc->cam_rescan_cb != NULL)
3342851f59d7SEd Maste 				sc->cam_rescan_cb(sc, (channel >> 24) & 0xF,
3343851f59d7SEd Maste 				    AAC_CAM_TARGET_WILDCARD);
3344851f59d7SEd Maste 			break;
3345851f59d7SEd Maste 
334636e0bf6eSScott Long 		default:
334736e0bf6eSScott Long 			break;
334836e0bf6eSScott Long 		}
334936e0bf6eSScott Long 
335036e0bf6eSScott Long 	default:
335136e0bf6eSScott Long 		break;
335236e0bf6eSScott Long 	}
335336e0bf6eSScott Long 
335436e0bf6eSScott Long 	/* Copy the AIF data to the AIF queue for ioctl retrieval */
3355bb6fe253SScott Long 	mtx_lock(&sc->aac_aifq_lock);
3356a723a548SEd Maste 	current = sc->aifq_idx;
3357a723a548SEd Maste 	next = (current + 1) % AAC_AIFQ_LENGTH;
3358a723a548SEd Maste 	if (next == 0)
3359a723a548SEd Maste 		sc->aifq_filled = 1;
3360a723a548SEd Maste 	bcopy(fib, &sc->aac_aifq[current], sizeof(struct aac_fib));
3361a723a548SEd Maste 	/* modify AIF contexts */
3362a723a548SEd Maste 	if (sc->aifq_filled) {
3363a723a548SEd Maste 		for (ctx = sc->fibctx; ctx; ctx = ctx->next) {
3364a723a548SEd Maste 			if (next == ctx->ctx_idx)
3365a723a548SEd Maste 				ctx->ctx_wrap = 1;
3366a723a548SEd Maste 			else if (current == ctx->ctx_idx && ctx->ctx_wrap)
3367a723a548SEd Maste 				ctx->ctx_idx = next;
3368a723a548SEd Maste 		}
3369a723a548SEd Maste 	}
3370a723a548SEd Maste 	sc->aifq_idx = next;
3371b3457b51SScott Long 	/* On the off chance that someone is sleeping for an aif... */
337235863739SMike Smith 	if (sc->aac_state & AAC_STATE_AIF_SLEEPER)
337335863739SMike Smith 		wakeup(sc->aac_aifq);
3374b3457b51SScott Long 	/* Wakeup any poll()ers */
3375512824f8SSeigo Tanimura 	selwakeuppri(&sc->rcv_select, PRIBIO);
3376bb6fe253SScott Long 	mtx_unlock(&sc->aac_aifq_lock);
337735863739SMike Smith }
337835863739SMike Smith 
3379914da7d0SScott Long /*
33800b94a66eSMike Smith  * Return the Revision of the driver to userspace and check to see if the
338136e0bf6eSScott Long  * userspace app is possibly compatible.  This is extremely bogus since
338236e0bf6eSScott Long  * our driver doesn't follow Adaptec's versioning system.  Cheat by just
338336e0bf6eSScott Long  * returning what the card reported.
338435863739SMike Smith  */
338535863739SMike Smith static int
3386fb0c27d7SScott Long aac_rev_check(struct aac_softc *sc, caddr_t udata)
338735863739SMike Smith {
338835863739SMike Smith 	struct aac_rev_check rev_check;
338935863739SMike Smith 	struct aac_rev_check_resp rev_check_resp;
339035863739SMike Smith 	int error = 0;
339135863739SMike Smith 
339231a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
339335863739SMike Smith 
339435863739SMike Smith 	/*
339535863739SMike Smith 	 * Copyin the revision struct from userspace
339635863739SMike Smith 	 */
3397c6eafcf2SScott Long 	if ((error = copyin(udata, (caddr_t)&rev_check,
3398c6eafcf2SScott Long 			sizeof(struct aac_rev_check))) != 0) {
339935863739SMike Smith 		return error;
340035863739SMike Smith 	}
340135863739SMike Smith 
340231a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "Userland revision= %d\n",
3403914da7d0SScott Long 	      rev_check.callingRevision.buildNumber);
340435863739SMike Smith 
340535863739SMike Smith 	/*
340635863739SMike Smith 	 * Doctor up the response struct.
340735863739SMike Smith 	 */
340835863739SMike Smith 	rev_check_resp.possiblyCompatible = 1;
34098e7e6335SEd Maste 	rev_check_resp.adapterSWRevision.external.comp.major =
34108e7e6335SEd Maste 	    AAC_DRIVER_MAJOR_VERSION;
34118e7e6335SEd Maste 	rev_check_resp.adapterSWRevision.external.comp.minor =
34128e7e6335SEd Maste 	    AAC_DRIVER_MINOR_VERSION;
34138e7e6335SEd Maste 	rev_check_resp.adapterSWRevision.external.comp.type =
34148e7e6335SEd Maste 	    AAC_DRIVER_TYPE;
34158e7e6335SEd Maste 	rev_check_resp.adapterSWRevision.external.comp.dash =
34168e7e6335SEd Maste 	    AAC_DRIVER_BUGFIX_LEVEL;
3417914da7d0SScott Long 	rev_check_resp.adapterSWRevision.buildNumber =
34188e7e6335SEd Maste 	    AAC_DRIVER_BUILD;
341935863739SMike Smith 
3420c6eafcf2SScott Long 	return(copyout((caddr_t)&rev_check_resp, udata,
3421c6eafcf2SScott Long 			sizeof(struct aac_rev_check_resp)));
342235863739SMike Smith }
342335863739SMike Smith 
3424914da7d0SScott Long /*
3425a723a548SEd Maste  * Pass the fib context to the caller
3426a723a548SEd Maste  */
3427a723a548SEd Maste static int
3428a723a548SEd Maste aac_open_aif(struct aac_softc *sc, caddr_t arg)
3429a723a548SEd Maste {
3430a723a548SEd Maste 	struct aac_fib_context *fibctx, *ctx;
3431a723a548SEd Maste 	int error = 0;
3432a723a548SEd Maste 
343331a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
3434a723a548SEd Maste 
3435a723a548SEd Maste 	fibctx = malloc(sizeof(struct aac_fib_context), M_AACBUF, M_NOWAIT|M_ZERO);
3436a723a548SEd Maste 	if (fibctx == NULL)
3437a723a548SEd Maste 		return (ENOMEM);
3438a723a548SEd Maste 
3439a723a548SEd Maste 	mtx_lock(&sc->aac_aifq_lock);
3440a723a548SEd Maste 	/* all elements are already 0, add to queue */
3441a723a548SEd Maste 	if (sc->fibctx == NULL)
3442a723a548SEd Maste 		sc->fibctx = fibctx;
3443a723a548SEd Maste 	else {
3444a723a548SEd Maste 		for (ctx = sc->fibctx; ctx->next; ctx = ctx->next)
3445a723a548SEd Maste 			;
3446a723a548SEd Maste 		ctx->next = fibctx;
3447a723a548SEd Maste 		fibctx->prev = ctx;
3448a723a548SEd Maste 	}
3449a723a548SEd Maste 
3450a723a548SEd Maste 	/* evaluate unique value */
3451a723a548SEd Maste 	fibctx->unique = (*(u_int32_t *)&fibctx & 0xffffffff);
3452a723a548SEd Maste 	ctx = sc->fibctx;
3453a723a548SEd Maste 	while (ctx != fibctx) {
3454a723a548SEd Maste 		if (ctx->unique == fibctx->unique) {
3455a723a548SEd Maste 			fibctx->unique++;
3456a723a548SEd Maste 			ctx = sc->fibctx;
3457a723a548SEd Maste 		} else {
3458a723a548SEd Maste 			ctx = ctx->next;
3459a723a548SEd Maste 		}
3460a723a548SEd Maste 	}
3461a723a548SEd Maste 	mtx_unlock(&sc->aac_aifq_lock);
3462a723a548SEd Maste 
3463a723a548SEd Maste 	error = copyout(&fibctx->unique, (void *)arg, sizeof(u_int32_t));
3464a723a548SEd Maste 	if (error)
3465a723a548SEd Maste 		aac_close_aif(sc, (caddr_t)ctx);
3466a723a548SEd Maste 	return error;
3467a723a548SEd Maste }
3468a723a548SEd Maste 
3469a723a548SEd Maste /*
3470a723a548SEd Maste  * Close the caller's fib context
3471a723a548SEd Maste  */
3472a723a548SEd Maste static int
3473a723a548SEd Maste aac_close_aif(struct aac_softc *sc, caddr_t arg)
3474a723a548SEd Maste {
3475a723a548SEd Maste 	struct aac_fib_context *ctx;
3476a723a548SEd Maste 
347731a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
3478a723a548SEd Maste 
3479a723a548SEd Maste 	mtx_lock(&sc->aac_aifq_lock);
3480a723a548SEd Maste 	for (ctx = sc->fibctx; ctx; ctx = ctx->next) {
3481a723a548SEd Maste 		if (ctx->unique == *(uint32_t *)&arg) {
3482a723a548SEd Maste 			if (ctx == sc->fibctx)
3483a723a548SEd Maste 				sc->fibctx = NULL;
3484a723a548SEd Maste 			else {
3485a723a548SEd Maste 				ctx->prev->next = ctx->next;
3486a723a548SEd Maste 				if (ctx->next)
3487a723a548SEd Maste 					ctx->next->prev = ctx->prev;
3488a723a548SEd Maste 			}
3489a723a548SEd Maste 			break;
3490a723a548SEd Maste 		}
3491a723a548SEd Maste 	}
3492a723a548SEd Maste 	mtx_unlock(&sc->aac_aifq_lock);
3493a723a548SEd Maste 	if (ctx)
3494a723a548SEd Maste 		free(ctx, M_AACBUF);
3495a723a548SEd Maste 
3496a723a548SEd Maste 	return 0;
3497a723a548SEd Maste }
3498a723a548SEd Maste 
3499a723a548SEd Maste /*
350035863739SMike Smith  * Pass the caller the next AIF in their queue
350135863739SMike Smith  */
350235863739SMike Smith static int
3503fb0c27d7SScott Long aac_getnext_aif(struct aac_softc *sc, caddr_t arg)
350435863739SMike Smith {
350535863739SMike Smith 	struct get_adapter_fib_ioctl agf;
3506a723a548SEd Maste 	struct aac_fib_context *ctx;
35079e2e96d8SScott Long 	int error;
350835863739SMike Smith 
350931a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
351035863739SMike Smith 
351135863739SMike Smith 	if ((error = copyin(arg, &agf, sizeof(agf))) == 0) {
3512a723a548SEd Maste 		for (ctx = sc->fibctx; ctx; ctx = ctx->next) {
3513a723a548SEd Maste 			if (agf.AdapterFibContext == ctx->unique)
3514a723a548SEd Maste 				break;
3515a723a548SEd Maste 		}
3516a723a548SEd Maste 		if (!ctx)
3517a723a548SEd Maste 			return (EFAULT);
351835863739SMike Smith 
3519a723a548SEd Maste 		error = aac_return_aif(sc, ctx, agf.AifFib);
3520a723a548SEd Maste 		if (error == EAGAIN && agf.Wait) {
352131a0399eSEd Maste 			fwprintf(sc, HBA_FLAGS_DBG_AIF_B, "aac_getnext_aif(): waiting for AIF");
352235863739SMike Smith 			sc->aac_state |= AAC_STATE_AIF_SLEEPER;
352335863739SMike Smith 			while (error == EAGAIN) {
3524914da7d0SScott Long 				error = tsleep(sc->aac_aifq, PRIBIO |
3525914da7d0SScott Long 					       PCATCH, "aacaif", 0);
352635863739SMike Smith 				if (error == 0)
3527a723a548SEd Maste 					error = aac_return_aif(sc, ctx, agf.AifFib);
352835863739SMike Smith 			}
352935863739SMike Smith 			sc->aac_state &= ~AAC_STATE_AIF_SLEEPER;
353035863739SMike Smith 		}
353135863739SMike Smith 	}
353235863739SMike Smith 	return(error);
353335863739SMike Smith }
353435863739SMike Smith 
3535914da7d0SScott Long /*
35360b94a66eSMike Smith  * Hand the next AIF off the top of the queue out to userspace.
35370b94a66eSMike Smith  */
35380b94a66eSMike Smith static int
3539a723a548SEd Maste aac_return_aif(struct aac_softc *sc, struct aac_fib_context *ctx, caddr_t uptr)
35400b94a66eSMike Smith {
3541a723a548SEd Maste 	int current, error;
35420b94a66eSMike Smith 
354331a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
35440b94a66eSMike Smith 
3545bb6fe253SScott Long 	mtx_lock(&sc->aac_aifq_lock);
3546a723a548SEd Maste 	current = ctx->ctx_idx;
3547a723a548SEd Maste 	if (current == sc->aifq_idx && !ctx->ctx_wrap) {
3548a723a548SEd Maste 		/* empty */
3549bb6fe253SScott Long 		mtx_unlock(&sc->aac_aifq_lock);
35503df780cfSScott Long 		return (EAGAIN);
35513df780cfSScott Long 	}
3552a723a548SEd Maste 	error =
3553a723a548SEd Maste 		copyout(&sc->aac_aifq[current], (void *)uptr, sizeof(struct aac_fib));
355436e0bf6eSScott Long 	if (error)
355570545d1aSScott Long 		device_printf(sc->aac_dev,
355670545d1aSScott Long 		    "aac_return_aif: copyout returned %d\n", error);
3557a723a548SEd Maste 	else {
3558a723a548SEd Maste 		ctx->ctx_wrap = 0;
3559a723a548SEd Maste 		ctx->ctx_idx = (current + 1) % AAC_AIFQ_LENGTH;
3560a723a548SEd Maste 	}
3561bb6fe253SScott Long 	mtx_unlock(&sc->aac_aifq_lock);
35620b94a66eSMike Smith 	return(error);
35630b94a66eSMike Smith }
356436e0bf6eSScott Long 
35657cb209f5SScott Long static int
35667cb209f5SScott Long aac_get_pci_info(struct aac_softc *sc, caddr_t uptr)
35677cb209f5SScott Long {
35687cb209f5SScott Long 	struct aac_pci_info {
35697cb209f5SScott Long 		u_int32_t bus;
35707cb209f5SScott Long 		u_int32_t slot;
35717cb209f5SScott Long 	} pciinf;
35727cb209f5SScott Long 	int error;
35737cb209f5SScott Long 
357431a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
35757cb209f5SScott Long 
35767cb209f5SScott Long 	pciinf.bus = pci_get_bus(sc->aac_dev);
35777cb209f5SScott Long 	pciinf.slot = pci_get_slot(sc->aac_dev);
35787cb209f5SScott Long 
35797cb209f5SScott Long 	error = copyout((caddr_t)&pciinf, uptr,
35807cb209f5SScott Long 			sizeof(struct aac_pci_info));
35817cb209f5SScott Long 
35827cb209f5SScott Long 	return (error);
35837cb209f5SScott Long }
35847cb209f5SScott Long 
35856d307336SEd Maste static int
35866d307336SEd Maste aac_supported_features(struct aac_softc *sc, caddr_t uptr)
35876d307336SEd Maste {
35886d307336SEd Maste 	struct aac_features f;
35896d307336SEd Maste 	int error;
35906d307336SEd Maste 
35916d307336SEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
35926d307336SEd Maste 
35936d307336SEd Maste 	if ((error = copyin(uptr, &f, sizeof (f))) != 0)
35946d307336SEd Maste 		return (error);
35956d307336SEd Maste 
35966d307336SEd Maste 	/*
35976d307336SEd Maste 	 * When the management driver receives FSACTL_GET_FEATURES ioctl with
35986d307336SEd Maste 	 * ALL zero in the featuresState, the driver will return the current
35996d307336SEd Maste 	 * state of all the supported features, the data field will not be
36006d307336SEd Maste 	 * valid.
36016d307336SEd Maste 	 * When the management driver receives FSACTL_GET_FEATURES ioctl with
36026d307336SEd Maste 	 * a specific bit set in the featuresState, the driver will return the
36036d307336SEd Maste 	 * current state of this specific feature and whatever data that are
36046d307336SEd Maste 	 * associated with the feature in the data field or perform whatever
36056d307336SEd Maste 	 * action needed indicates in the data field.
36066d307336SEd Maste 	 */
36076d307336SEd Maste 	if (f.feat.fValue == 0) {
36086d307336SEd Maste 		f.feat.fBits.largeLBA =
36096d307336SEd Maste 		    (sc->flags & AAC_FLAGS_LBA_64BIT) ? 1 : 0;
36106d307336SEd Maste 		/* TODO: In the future, add other features state here as well */
36116d307336SEd Maste 	} else {
36126d307336SEd Maste 		if (f.feat.fBits.largeLBA)
36136d307336SEd Maste 			f.feat.fBits.largeLBA =
36146d307336SEd Maste 			    (sc->flags & AAC_FLAGS_LBA_64BIT) ? 1 : 0;
36156d307336SEd Maste 		/* TODO: Add other features state and data in the future */
36166d307336SEd Maste 	}
36176d307336SEd Maste 
36186d307336SEd Maste 	error = copyout(&f, uptr, sizeof (f));
36196d307336SEd Maste 	return (error);
36206d307336SEd Maste }
36216d307336SEd Maste 
3622914da7d0SScott Long /*
362336e0bf6eSScott Long  * Give the userland some information about the container.  The AAC arch
362436e0bf6eSScott Long  * expects the driver to be a SCSI passthrough type driver, so it expects
362536e0bf6eSScott Long  * the containers to have b:t:l numbers.  Fake it.
362636e0bf6eSScott Long  */
362736e0bf6eSScott Long static int
362836e0bf6eSScott Long aac_query_disk(struct aac_softc *sc, caddr_t uptr)
362936e0bf6eSScott Long {
363036e0bf6eSScott Long 	struct aac_query_disk query_disk;
363136e0bf6eSScott Long 	struct aac_container *co;
3632914da7d0SScott Long 	struct aac_disk	*disk;
363336e0bf6eSScott Long 	int error, id;
363436e0bf6eSScott Long 
363531a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
363636e0bf6eSScott Long 
3637914da7d0SScott Long 	disk = NULL;
3638914da7d0SScott Long 
3639914da7d0SScott Long 	error = copyin(uptr, (caddr_t)&query_disk,
3640914da7d0SScott Long 		       sizeof(struct aac_query_disk));
364136e0bf6eSScott Long 	if (error)
364236e0bf6eSScott Long 		return (error);
364336e0bf6eSScott Long 
364436e0bf6eSScott Long 	id = query_disk.ContainerNumber;
364536e0bf6eSScott Long 	if (id == -1)
364636e0bf6eSScott Long 		return (EINVAL);
364736e0bf6eSScott Long 
3648bb6fe253SScott Long 	mtx_lock(&sc->aac_container_lock);
364936e0bf6eSScott Long 	TAILQ_FOREACH(co, &sc->aac_container_tqh, co_link) {
365036e0bf6eSScott Long 		if (co->co_mntobj.ObjectId == id)
365136e0bf6eSScott Long 			break;
365236e0bf6eSScott Long 		}
365336e0bf6eSScott Long 
365436e0bf6eSScott Long 	if (co == NULL) {
365536e0bf6eSScott Long 			query_disk.Valid = 0;
365636e0bf6eSScott Long 			query_disk.Locked = 0;
365736e0bf6eSScott Long 			query_disk.Deleted = 1;		/* XXX is this right? */
365836e0bf6eSScott Long 	} else {
365936e0bf6eSScott Long 		disk = device_get_softc(co->co_disk);
366036e0bf6eSScott Long 		query_disk.Valid = 1;
3661914da7d0SScott Long 		query_disk.Locked =
3662914da7d0SScott Long 		    (disk->ad_flags & AAC_DISK_OPEN) ? 1 : 0;
366336e0bf6eSScott Long 		query_disk.Deleted = 0;
3664b3457b51SScott Long 		query_disk.Bus = device_get_unit(sc->aac_dev);
366536e0bf6eSScott Long 		query_disk.Target = disk->unit;
366636e0bf6eSScott Long 		query_disk.Lun = 0;
366736e0bf6eSScott Long 		query_disk.UnMapped = 0;
36687540e65eSScott Long 		sprintf(&query_disk.diskDeviceName[0], "%s%d",
36690b7ed341SPoul-Henning Kamp 			disk->ad_disk->d_name, disk->ad_disk->d_unit);
367036e0bf6eSScott Long 	}
3671bb6fe253SScott Long 	mtx_unlock(&sc->aac_container_lock);
367236e0bf6eSScott Long 
3673914da7d0SScott Long 	error = copyout((caddr_t)&query_disk, uptr,
3674914da7d0SScott Long 			sizeof(struct aac_query_disk));
367536e0bf6eSScott Long 
367636e0bf6eSScott Long 	return (error);
367736e0bf6eSScott Long }
367836e0bf6eSScott Long 
3679fe3cb0e1SScott Long static void
3680fe3cb0e1SScott Long aac_get_bus_info(struct aac_softc *sc)
3681fe3cb0e1SScott Long {
3682fe3cb0e1SScott Long 	struct aac_fib *fib;
3683fe3cb0e1SScott Long 	struct aac_ctcfg *c_cmd;
3684fe3cb0e1SScott Long 	struct aac_ctcfg_resp *c_resp;
3685fe3cb0e1SScott Long 	struct aac_vmioctl *vmi;
3686fe3cb0e1SScott Long 	struct aac_vmi_businf_resp *vmi_resp;
3687fe3cb0e1SScott Long 	struct aac_getbusinf businfo;
368870545d1aSScott Long 	struct aac_sim *caminf;
3689fe3cb0e1SScott Long 	device_t child;
3690fe3cb0e1SScott Long 	int i, found, error;
3691fe3cb0e1SScott Long 
36921ffe41c1SChristian S.J. Peron 	mtx_lock(&sc->aac_io_lock);
369303b5fe51SScott Long 	aac_alloc_sync_fib(sc, &fib);
3694fe3cb0e1SScott Long 	c_cmd = (struct aac_ctcfg *)&fib->data[0];
369539ee03c3SScott Long 	bzero(c_cmd, sizeof(struct aac_ctcfg));
3696fe3cb0e1SScott Long 
3697fe3cb0e1SScott Long 	c_cmd->Command = VM_ContainerConfig;
3698fe3cb0e1SScott Long 	c_cmd->cmd = CT_GET_SCSI_METHOD;
3699fe3cb0e1SScott Long 	c_cmd->param = 0;
3700fe3cb0e1SScott Long 
3701fe3cb0e1SScott Long 	error = aac_sync_fib(sc, ContainerCommand, 0, fib,
3702fe3cb0e1SScott Long 	    sizeof(struct aac_ctcfg));
3703fe3cb0e1SScott Long 	if (error) {
3704fe3cb0e1SScott Long 		device_printf(sc->aac_dev, "Error %d sending "
3705fe3cb0e1SScott Long 		    "VM_ContainerConfig command\n", error);
3706fe3cb0e1SScott Long 		aac_release_sync_fib(sc);
37071ffe41c1SChristian S.J. Peron 		mtx_unlock(&sc->aac_io_lock);
3708fe3cb0e1SScott Long 		return;
3709fe3cb0e1SScott Long 	}
3710fe3cb0e1SScott Long 
3711fe3cb0e1SScott Long 	c_resp = (struct aac_ctcfg_resp *)&fib->data[0];
3712fe3cb0e1SScott Long 	if (c_resp->Status != ST_OK) {
3713fe3cb0e1SScott Long 		device_printf(sc->aac_dev, "VM_ContainerConfig returned 0x%x\n",
3714fe3cb0e1SScott Long 		    c_resp->Status);
3715fe3cb0e1SScott Long 		aac_release_sync_fib(sc);
37161ffe41c1SChristian S.J. Peron 		mtx_unlock(&sc->aac_io_lock);
3717fe3cb0e1SScott Long 		return;
3718fe3cb0e1SScott Long 	}
3719fe3cb0e1SScott Long 
3720fe3cb0e1SScott Long 	sc->scsi_method_id = c_resp->param;
3721fe3cb0e1SScott Long 
3722fe3cb0e1SScott Long 	vmi = (struct aac_vmioctl *)&fib->data[0];
372339ee03c3SScott Long 	bzero(vmi, sizeof(struct aac_vmioctl));
372439ee03c3SScott Long 
3725fe3cb0e1SScott Long 	vmi->Command = VM_Ioctl;
3726fe3cb0e1SScott Long 	vmi->ObjType = FT_DRIVE;
3727fe3cb0e1SScott Long 	vmi->MethId = sc->scsi_method_id;
3728fe3cb0e1SScott Long 	vmi->ObjId = 0;
3729fe3cb0e1SScott Long 	vmi->IoctlCmd = GetBusInfo;
3730fe3cb0e1SScott Long 
3731fe3cb0e1SScott Long 	error = aac_sync_fib(sc, ContainerCommand, 0, fib,
373242ef13a2SEd Maste 	    sizeof(struct aac_vmi_businf_resp));
3733fe3cb0e1SScott Long 	if (error) {
3734fe3cb0e1SScott Long 		device_printf(sc->aac_dev, "Error %d sending VMIoctl command\n",
3735fe3cb0e1SScott Long 		    error);
3736fe3cb0e1SScott Long 		aac_release_sync_fib(sc);
37371ffe41c1SChristian S.J. Peron 		mtx_unlock(&sc->aac_io_lock);
3738fe3cb0e1SScott Long 		return;
3739fe3cb0e1SScott Long 	}
3740fe3cb0e1SScott Long 
3741fe3cb0e1SScott Long 	vmi_resp = (struct aac_vmi_businf_resp *)&fib->data[0];
3742fe3cb0e1SScott Long 	if (vmi_resp->Status != ST_OK) {
3743fe3cb0e1SScott Long 		device_printf(sc->aac_dev, "VM_Ioctl returned %d\n",
3744fe3cb0e1SScott Long 		    vmi_resp->Status);
3745fe3cb0e1SScott Long 		aac_release_sync_fib(sc);
37461ffe41c1SChristian S.J. Peron 		mtx_unlock(&sc->aac_io_lock);
3747fe3cb0e1SScott Long 		return;
3748fe3cb0e1SScott Long 	}
3749fe3cb0e1SScott Long 
3750fe3cb0e1SScott Long 	bcopy(&vmi_resp->BusInf, &businfo, sizeof(struct aac_getbusinf));
3751fe3cb0e1SScott Long 	aac_release_sync_fib(sc);
37521ffe41c1SChristian S.J. Peron 	mtx_unlock(&sc->aac_io_lock);
3753fe3cb0e1SScott Long 
3754fe3cb0e1SScott Long 	found = 0;
3755fe3cb0e1SScott Long 	for (i = 0; i < businfo.BusCount; i++) {
3756fe3cb0e1SScott Long 		if (businfo.BusValid[i] != AAC_BUS_VALID)
3757fe3cb0e1SScott Long 			continue;
3758fe3cb0e1SScott Long 
3759a761a1caSScott Long 		caminf = (struct aac_sim *)malloc( sizeof(struct aac_sim),
3760a761a1caSScott Long 		    M_AACBUF, M_NOWAIT | M_ZERO);
3761b5f516cdSScott Long 		if (caminf == NULL) {
3762b5f516cdSScott Long 			device_printf(sc->aac_dev,
3763b5f516cdSScott Long 			    "No memory to add passthrough bus %d\n", i);
3764b5f516cdSScott Long 			break;
37657cb209f5SScott Long 		};
3766fe3cb0e1SScott Long 
3767fe3cb0e1SScott Long 		child = device_add_child(sc->aac_dev, "aacp", -1);
3768fe3cb0e1SScott Long 		if (child == NULL) {
3769b5f516cdSScott Long 			device_printf(sc->aac_dev,
3770b5f516cdSScott Long 			    "device_add_child failed for passthrough bus %d\n",
3771b5f516cdSScott Long 			    i);
3772b5f516cdSScott Long 			free(caminf, M_AACBUF);
3773b5f516cdSScott Long 			break;
3774fe3cb0e1SScott Long 		}
3775fe3cb0e1SScott Long 
3776fe3cb0e1SScott Long 		caminf->TargetsPerBus = businfo.TargetsPerBus;
3777fe3cb0e1SScott Long 		caminf->BusNumber = i;
3778fe3cb0e1SScott Long 		caminf->InitiatorBusId = businfo.InitiatorBusId[i];
3779fe3cb0e1SScott Long 		caminf->aac_sc = sc;
3780ddb8683eSScott Long 		caminf->sim_dev = child;
3781fe3cb0e1SScott Long 
3782fe3cb0e1SScott Long 		device_set_ivars(child, caminf);
3783fe3cb0e1SScott Long 		device_set_desc(child, "SCSI Passthrough Bus");
378470545d1aSScott Long 		TAILQ_INSERT_TAIL(&sc->aac_sim_tqh, caminf, sim_link);
3785fe3cb0e1SScott Long 
3786fe3cb0e1SScott Long 		found = 1;
3787fe3cb0e1SScott Long 	}
3788fe3cb0e1SScott Long 
3789fe3cb0e1SScott Long 	if (found)
3790fe3cb0e1SScott Long 		bus_generic_attach(sc->aac_dev);
3791fe3cb0e1SScott Long }
3792