xref: /freebsd/sys/dev/aac/aac.c (revision aad970f1fee9a2a3e5a0f880be9b87c6193b3bd1)
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  */
3635863739SMike Smith 
37f6c4dd3fSScott Long #include "opt_aac.h"
38f6c4dd3fSScott Long 
3936e0bf6eSScott Long /* #include <stddef.h> */
4035863739SMike Smith #include <sys/param.h>
4135863739SMike Smith #include <sys/systm.h>
4235863739SMike Smith #include <sys/malloc.h>
4335863739SMike Smith #include <sys/kernel.h>
4436e0bf6eSScott Long #include <sys/kthread.h>
453d04a9d7SScott Long #include <sys/sysctl.h>
46b3457b51SScott Long #include <sys/poll.h>
47891619a6SPoul-Henning Kamp #include <sys/ioccom.h>
4835863739SMike Smith 
4935863739SMike Smith #include <sys/bus.h>
5035863739SMike Smith #include <sys/conf.h>
5135863739SMike Smith #include <sys/signalvar.h>
520b94a66eSMike Smith #include <sys/time.h>
5336e0bf6eSScott Long #include <sys/eventhandler.h>
5435863739SMike Smith 
5535863739SMike Smith #include <machine/bus_memio.h>
5635863739SMike Smith #include <machine/bus.h>
5735863739SMike Smith #include <machine/resource.h>
5835863739SMike Smith 
5935863739SMike Smith #include <dev/aac/aacreg.h>
600b94a66eSMike Smith #include <dev/aac/aac_ioctl.h>
6135863739SMike Smith #include <dev/aac/aacvar.h>
6235863739SMike Smith #include <dev/aac/aac_tables.h>
6335863739SMike Smith 
6435863739SMike Smith static void	aac_startup(void *arg);
65914da7d0SScott Long static void	aac_add_container(struct aac_softc *sc,
66cbfd045bSScott Long 				  struct aac_mntinforesp *mir, int f);
67fe3cb0e1SScott Long static void	aac_get_bus_info(struct aac_softc *sc);
6835863739SMike Smith 
6935863739SMike Smith /* Command Processing */
700b94a66eSMike Smith static void	aac_timeout(struct aac_softc *sc);
71cd481291SScott Long static int	aac_map_command(struct aac_command *cm);
7235863739SMike Smith static void	aac_complete(void *context, int pending);
7335863739SMike Smith static int	aac_bio_command(struct aac_softc *sc, struct aac_command **cmp);
7435863739SMike Smith static void	aac_bio_complete(struct aac_command *cm);
7535863739SMike Smith static int	aac_wait_command(struct aac_command *cm, int timeout);
7670545d1aSScott Long static void	aac_command_thread(struct aac_softc *sc);
7735863739SMike Smith 
7835863739SMike Smith /* Command Buffer Management */
79cd481291SScott Long static void	aac_map_command_sg(void *arg, bus_dma_segment_t *segs,
80cd481291SScott Long 				   int nseg, int error);
81c6eafcf2SScott Long static void	aac_map_command_helper(void *arg, bus_dma_segment_t *segs,
82c6eafcf2SScott Long 				       int nseg, int error);
830b94a66eSMike Smith static int	aac_alloc_commands(struct aac_softc *sc);
848480cc63SScott Long static void	aac_free_commands(struct aac_softc *sc);
8535863739SMike Smith static void	aac_unmap_command(struct aac_command *cm);
8635863739SMike Smith 
8735863739SMike Smith /* Hardware Interface */
88c6eafcf2SScott Long static void	aac_common_map(void *arg, bus_dma_segment_t *segs, int nseg,
89c6eafcf2SScott Long 			       int error);
90fe94b852SScott Long static int	aac_check_firmware(struct aac_softc *sc);
9135863739SMike Smith static int	aac_init(struct aac_softc *sc);
9235863739SMike Smith static int	aac_sync_command(struct aac_softc *sc, u_int32_t command,
93c6eafcf2SScott Long 				 u_int32_t arg0, u_int32_t arg1, u_int32_t arg2,
94c6eafcf2SScott Long 				 u_int32_t arg3, u_int32_t *sp);
95c6eafcf2SScott Long static int	aac_enqueue_fib(struct aac_softc *sc, int queue,
96f6c4dd3fSScott Long 				struct aac_command *cm);
97c6eafcf2SScott Long static int	aac_dequeue_fib(struct aac_softc *sc, int queue,
98914da7d0SScott Long 				u_int32_t *fib_size, struct aac_fib **fib_addr);
9936e0bf6eSScott Long static int	aac_enqueue_response(struct aac_softc *sc, int queue,
10036e0bf6eSScott Long 				     struct aac_fib *fib);
10135863739SMike Smith 
102b3457b51SScott Long /* Falcon/PPC interface */
103b3457b51SScott Long static int	aac_fa_get_fwstatus(struct aac_softc *sc);
104b3457b51SScott Long static void	aac_fa_qnotify(struct aac_softc *sc, int qbit);
105b3457b51SScott Long static int	aac_fa_get_istatus(struct aac_softc *sc);
106b3457b51SScott Long static void	aac_fa_clear_istatus(struct aac_softc *sc, int mask);
107b3457b51SScott Long static void	aac_fa_set_mailbox(struct aac_softc *sc, u_int32_t command,
108b3457b51SScott Long 				   u_int32_t arg0, u_int32_t arg1,
109b3457b51SScott Long 				   u_int32_t arg2, u_int32_t arg3);
110a6d35632SScott Long static int	aac_fa_get_mailbox(struct aac_softc *sc, int mb);
111b3457b51SScott Long static void	aac_fa_set_interrupts(struct aac_softc *sc, int enable);
112b3457b51SScott Long 
113b3457b51SScott Long struct aac_interface aac_fa_interface = {
114b3457b51SScott Long 	aac_fa_get_fwstatus,
115b3457b51SScott Long 	aac_fa_qnotify,
116b3457b51SScott Long 	aac_fa_get_istatus,
117b3457b51SScott Long 	aac_fa_clear_istatus,
118b3457b51SScott Long 	aac_fa_set_mailbox,
119a6d35632SScott Long 	aac_fa_get_mailbox,
120b3457b51SScott Long 	aac_fa_set_interrupts
121b3457b51SScott Long };
122b3457b51SScott Long 
12335863739SMike Smith /* StrongARM interface */
12435863739SMike Smith static int	aac_sa_get_fwstatus(struct aac_softc *sc);
12535863739SMike Smith static void	aac_sa_qnotify(struct aac_softc *sc, int qbit);
12635863739SMike Smith static int	aac_sa_get_istatus(struct aac_softc *sc);
12735863739SMike Smith static void	aac_sa_clear_istatus(struct aac_softc *sc, int mask);
12835863739SMike Smith static void	aac_sa_set_mailbox(struct aac_softc *sc, u_int32_t command,
129c6eafcf2SScott Long 				   u_int32_t arg0, u_int32_t arg1,
130c6eafcf2SScott Long 				   u_int32_t arg2, u_int32_t arg3);
131a6d35632SScott Long static int	aac_sa_get_mailbox(struct aac_softc *sc, int mb);
13235863739SMike Smith static void	aac_sa_set_interrupts(struct aac_softc *sc, int enable);
13335863739SMike Smith 
13435863739SMike Smith struct aac_interface aac_sa_interface = {
13535863739SMike Smith 	aac_sa_get_fwstatus,
13635863739SMike Smith 	aac_sa_qnotify,
13735863739SMike Smith 	aac_sa_get_istatus,
13835863739SMike Smith 	aac_sa_clear_istatus,
13935863739SMike Smith 	aac_sa_set_mailbox,
140a6d35632SScott Long 	aac_sa_get_mailbox,
14135863739SMike Smith 	aac_sa_set_interrupts
14235863739SMike Smith };
14335863739SMike Smith 
14435863739SMike Smith /* i960Rx interface */
14535863739SMike Smith static int	aac_rx_get_fwstatus(struct aac_softc *sc);
14635863739SMike Smith static void	aac_rx_qnotify(struct aac_softc *sc, int qbit);
14735863739SMike Smith static int	aac_rx_get_istatus(struct aac_softc *sc);
14835863739SMike Smith static void	aac_rx_clear_istatus(struct aac_softc *sc, int mask);
14935863739SMike Smith static void	aac_rx_set_mailbox(struct aac_softc *sc, u_int32_t command,
150c6eafcf2SScott Long 				   u_int32_t arg0, u_int32_t arg1,
151c6eafcf2SScott Long 				   u_int32_t arg2, u_int32_t arg3);
152a6d35632SScott Long static int	aac_rx_get_mailbox(struct aac_softc *sc, int mb);
15335863739SMike Smith static void	aac_rx_set_interrupts(struct aac_softc *sc, int enable);
15435863739SMike Smith 
15535863739SMike Smith struct aac_interface aac_rx_interface = {
15635863739SMike Smith 	aac_rx_get_fwstatus,
15735863739SMike Smith 	aac_rx_qnotify,
15835863739SMike Smith 	aac_rx_get_istatus,
15935863739SMike Smith 	aac_rx_clear_istatus,
16035863739SMike Smith 	aac_rx_set_mailbox,
161a6d35632SScott Long 	aac_rx_get_mailbox,
16235863739SMike Smith 	aac_rx_set_interrupts
16335863739SMike Smith };
16435863739SMike Smith 
16535863739SMike Smith /* Debugging and Diagnostics */
16635863739SMike Smith static void	aac_describe_controller(struct aac_softc *sc);
1676965a493SScott Long static char	*aac_describe_code(struct aac_code_lookup *table,
168c6eafcf2SScott Long 				   u_int32_t code);
16935863739SMike Smith 
17035863739SMike Smith /* Management Interface */
17135863739SMike Smith static d_open_t		aac_open;
17235863739SMike Smith static d_close_t	aac_close;
17335863739SMike Smith static d_ioctl_t	aac_ioctl;
174b3457b51SScott Long static d_poll_t		aac_poll;
175c6eafcf2SScott Long static int		aac_ioctl_sendfib(struct aac_softc *sc, caddr_t ufib);
176c6eafcf2SScott Long static void		aac_handle_aif(struct aac_softc *sc,
17736e0bf6eSScott Long 					   struct aac_fib *fib);
178fb0c27d7SScott Long static int		aac_rev_check(struct aac_softc *sc, caddr_t udata);
179fb0c27d7SScott Long static int		aac_getnext_aif(struct aac_softc *sc, caddr_t arg);
180fb0c27d7SScott Long static int		aac_return_aif(struct aac_softc *sc, caddr_t uptr);
18136e0bf6eSScott Long static int		aac_query_disk(struct aac_softc *sc, caddr_t uptr);
18235863739SMike Smith 
18335863739SMike Smith #define AAC_CDEV_MAJOR	150
18435863739SMike Smith 
18535863739SMike Smith static struct cdevsw aac_cdevsw = {
1867ac40f5fSPoul-Henning Kamp 	.d_open =	aac_open,
1877ac40f5fSPoul-Henning Kamp 	.d_close =	aac_close,
1887ac40f5fSPoul-Henning Kamp 	.d_ioctl =	aac_ioctl,
1897ac40f5fSPoul-Henning Kamp 	.d_poll =	aac_poll,
1907ac40f5fSPoul-Henning Kamp 	.d_name =	"aac",
1917ac40f5fSPoul-Henning Kamp 	.d_maj =	AAC_CDEV_MAJOR,
19235863739SMike Smith };
19335863739SMike Smith 
19436e0bf6eSScott Long MALLOC_DEFINE(M_AACBUF, "aacbuf", "Buffers for the AAC driver");
19536e0bf6eSScott Long 
1963d04a9d7SScott Long /* sysctl node */
1973d04a9d7SScott Long SYSCTL_NODE(_hw, OID_AUTO, aac, CTLFLAG_RD, 0, "AAC driver parameters");
1983d04a9d7SScott Long 
199914da7d0SScott Long /*
200914da7d0SScott Long  * Device Interface
201914da7d0SScott Long  */
20235863739SMike Smith 
203914da7d0SScott Long /*
20435863739SMike Smith  * Initialise the controller and softc
20535863739SMike Smith  */
20635863739SMike Smith int
20735863739SMike Smith aac_attach(struct aac_softc *sc)
20835863739SMike Smith {
20935863739SMike Smith 	int error, unit;
21035863739SMike Smith 
21135863739SMike Smith 	debug_called(1);
21235863739SMike Smith 
21335863739SMike Smith 	/*
21435863739SMike Smith 	 * Initialise per-controller queues.
21535863739SMike Smith 	 */
2160b94a66eSMike Smith 	aac_initq_free(sc);
2170b94a66eSMike Smith 	aac_initq_ready(sc);
2180b94a66eSMike Smith 	aac_initq_busy(sc);
2190b94a66eSMike Smith 	aac_initq_bio(sc);
22035863739SMike Smith 
22135863739SMike Smith 	/*
22235863739SMike Smith 	 * Initialise command-completion task.
22335863739SMike Smith 	 */
22435863739SMike Smith 	TASK_INIT(&sc->aac_task_complete, 0, aac_complete, sc);
22535863739SMike Smith 
22635863739SMike Smith 	/* disable interrupts before we enable anything */
22735863739SMike Smith 	AAC_MASK_INTERRUPTS(sc);
22835863739SMike Smith 
22935863739SMike Smith 	/* mark controller as suspended until we get ourselves organised */
23035863739SMike Smith 	sc->aac_state |= AAC_STATE_SUSPEND;
23135863739SMike Smith 
23235863739SMike Smith 	/*
233fe94b852SScott Long 	 * Check that the firmware on the card is supported.
234fe94b852SScott Long 	 */
235fe94b852SScott Long 	if ((error = aac_check_firmware(sc)) != 0)
236fe94b852SScott Long 		return(error);
237fe94b852SScott Long 
238f6b1c44dSScott Long 	/*
239f6b1c44dSScott Long 	 * Initialize locks
240f6b1c44dSScott Long 	 */
241cbfd045bSScott Long 	AAC_LOCK_INIT(&sc->aac_sync_lock, "AAC sync FIB lock");
242f6b1c44dSScott Long 	AAC_LOCK_INIT(&sc->aac_aifq_lock, "AAC AIF lock");
243f6b1c44dSScott Long 	AAC_LOCK_INIT(&sc->aac_io_lock, "AAC I/O lock");
244f6b1c44dSScott Long 	AAC_LOCK_INIT(&sc->aac_container_lock, "AAC container lock");
245f6b1c44dSScott Long 	TAILQ_INIT(&sc->aac_container_tqh);
246f6b1c44dSScott Long 
247cbfd045bSScott Long 
2480b94a66eSMike Smith 	/*
24935863739SMike Smith 	 * Initialise the adapter.
25035863739SMike Smith 	 */
2510b94a66eSMike Smith 	if ((error = aac_init(sc)) != 0)
25235863739SMike Smith 		return(error);
25335863739SMike Smith 
25435863739SMike Smith 	/*
25535863739SMike Smith 	 * Print a little information about the controller.
25635863739SMike Smith 	 */
25735863739SMike Smith 	aac_describe_controller(sc);
25835863739SMike Smith 
25935863739SMike Smith 	/*
260ae543596SScott Long 	 * Register to probe our containers later.
261ae543596SScott Long 	 */
26235863739SMike Smith 	sc->aac_ich.ich_func = aac_startup;
26335863739SMike Smith 	sc->aac_ich.ich_arg = sc;
26435863739SMike Smith 	if (config_intrhook_establish(&sc->aac_ich) != 0) {
265914da7d0SScott Long 		device_printf(sc->aac_dev,
266914da7d0SScott Long 			      "can't establish configuration hook\n");
26735863739SMike Smith 		return(ENXIO);
26835863739SMike Smith 	}
26935863739SMike Smith 
27035863739SMike Smith 	/*
27135863739SMike Smith 	 * Make the control device.
27235863739SMike Smith 	 */
27335863739SMike Smith 	unit = device_get_unit(sc->aac_dev);
2749e9466baSRobert Watson 	sc->aac_dev_t = make_dev(&aac_cdevsw, unit, UID_ROOT, GID_OPERATOR,
2759e9466baSRobert Watson 				 0640, "aac%d", unit);
276157fbb2eSScott Long 	(void)make_dev_alias(sc->aac_dev_t, "afa%d", unit);
2774aa620cdSScott Long 	(void)make_dev_alias(sc->aac_dev_t, "hpn%d", unit);
27835863739SMike Smith 	sc->aac_dev_t->si_drv1 = sc;
27935863739SMike Smith 
28036e0bf6eSScott Long 	/* Create the AIF thread */
28170545d1aSScott Long 	if (kthread_create((void(*)(void *))aac_command_thread, sc,
282316ec49aSScott Long 			   &sc->aifthread, 0, 0, "aac%daif", unit))
28336e0bf6eSScott Long 		panic("Could not create AIF thread\n");
28436e0bf6eSScott Long 
28536e0bf6eSScott Long 	/* Register the shutdown method to only be called post-dump */
2865f54d522SScott Long 	if ((sc->eh = EVENTHANDLER_REGISTER(shutdown_final, aac_shutdown,
2875f54d522SScott Long 	    sc->aac_dev, SHUTDOWN_PRI_DEFAULT)) == NULL)
2885f54d522SScott Long 		device_printf(sc->aac_dev,
2895f54d522SScott Long 			      "shutdown event registration failed\n");
29036e0bf6eSScott Long 
291fe3cb0e1SScott Long 	/* Register with CAM for the non-DASD devices */
292a6d35632SScott Long 	if ((sc->flags & AAC_FLAGS_ENABLE_CAM) != 0) {
29370545d1aSScott Long 		TAILQ_INIT(&sc->aac_sim_tqh);
294fe3cb0e1SScott Long 		aac_get_bus_info(sc);
29570545d1aSScott Long 	}
296fe3cb0e1SScott Long 
29735863739SMike Smith 	return(0);
29835863739SMike Smith }
29935863739SMike Smith 
300914da7d0SScott Long /*
30135863739SMike Smith  * Probe for containers, create disks.
30235863739SMike Smith  */
30335863739SMike Smith static void
30435863739SMike Smith aac_startup(void *arg)
30535863739SMike Smith {
306914da7d0SScott Long 	struct aac_softc *sc;
307cbfd045bSScott Long 	struct aac_fib *fib;
308cbfd045bSScott Long 	struct aac_mntinfo *mi;
309cbfd045bSScott Long 	struct aac_mntinforesp *mir = NULL;
310795d7dc0SScott Long 	int count = 0, i = 0;
31135863739SMike Smith 
31235863739SMike Smith 	debug_called(1);
31335863739SMike Smith 
314914da7d0SScott Long 	sc = (struct aac_softc *)arg;
315914da7d0SScott Long 
31635863739SMike Smith 	/* disconnect ourselves from the intrhook chain */
31735863739SMike Smith 	config_intrhook_disestablish(&sc->aac_ich);
31835863739SMike Smith 
319fe3cb0e1SScott Long 	aac_alloc_sync_fib(sc, &fib, 0);
320cbfd045bSScott Long 	mi = (struct aac_mntinfo *)&fib->data[0];
321cbfd045bSScott Long 
32235863739SMike Smith 	/* loop over possible containers */
32336e0bf6eSScott Long 	do {
32435863739SMike Smith 		/* request information on this container */
32539ee03c3SScott Long 		bzero(mi, sizeof(struct aac_mntinfo));
32639ee03c3SScott Long 		mi->Command = VM_NameServe;
32739ee03c3SScott Long 		mi->MntType = FT_FILESYS;
328cbfd045bSScott Long 		mi->MntCount = i;
329cbfd045bSScott Long 		if (aac_sync_fib(sc, ContainerCommand, 0, fib,
330cbfd045bSScott Long 				 sizeof(struct aac_mntinfo))) {
331795d7dc0SScott Long 			printf("error probing container %d", i);
33235863739SMike Smith 			continue;
33335863739SMike Smith 		}
33435863739SMike Smith 
335cbfd045bSScott Long 		mir = (struct aac_mntinforesp *)&fib->data[0];
336795d7dc0SScott Long 		/* XXX Need to check if count changed */
337795d7dc0SScott Long 		count = mir->MntRespCount;
338cbfd045bSScott Long 		aac_add_container(sc, mir, 0);
33936e0bf6eSScott Long 		i++;
340795d7dc0SScott Long 	} while ((i < count) && (i < AAC_MAX_CONTAINERS));
341cbfd045bSScott Long 
342cbfd045bSScott Long 	aac_release_sync_fib(sc);
34335863739SMike Smith 
34435863739SMike Smith 	/* poke the bus to actually attach the child devices */
34535863739SMike Smith 	if (bus_generic_attach(sc->aac_dev))
34635863739SMike Smith 		device_printf(sc->aac_dev, "bus_generic_attach failed\n");
34735863739SMike Smith 
34835863739SMike Smith 	/* mark the controller up */
34935863739SMike Smith 	sc->aac_state &= ~AAC_STATE_SUSPEND;
35035863739SMike Smith 
35135863739SMike Smith 	/* enable interrupts now */
35235863739SMike Smith 	AAC_UNMASK_INTERRUPTS(sc);
35335863739SMike Smith }
35435863739SMike Smith 
355914da7d0SScott Long /*
356914da7d0SScott Long  * Create a device to respresent a new container
357914da7d0SScott Long  */
358914da7d0SScott Long static void
359cbfd045bSScott Long aac_add_container(struct aac_softc *sc, struct aac_mntinforesp *mir, int f)
360914da7d0SScott Long {
361914da7d0SScott Long 	struct aac_container *co;
362914da7d0SScott Long 	device_t child;
363914da7d0SScott Long 
364914da7d0SScott Long 	/*
365914da7d0SScott Long 	 * Check container volume type for validity.  Note that many of
366914da7d0SScott Long 	 * the possible types may never show up.
367914da7d0SScott Long 	 */
368914da7d0SScott Long 	if ((mir->Status == ST_OK) && (mir->MntTable[0].VolType != CT_NONE)) {
369a761a1caSScott Long 		co = (struct aac_container *)malloc(sizeof *co, M_AACBUF,
370a761a1caSScott Long 		       M_NOWAIT | M_ZERO);
371914da7d0SScott Long 		if (co == NULL)
372914da7d0SScott Long 			panic("Out of memory?!\n");
373914da7d0SScott Long 		debug(1, "id %x  name '%.16s'  size %u  type %d",
374914da7d0SScott Long 		      mir->MntTable[0].ObjectId,
375914da7d0SScott Long 		      mir->MntTable[0].FileSystemName,
376914da7d0SScott Long 		      mir->MntTable[0].Capacity, mir->MntTable[0].VolType);
377914da7d0SScott Long 
378fe3cb0e1SScott Long 		if ((child = device_add_child(sc->aac_dev, "aacd", -1)) == NULL)
379914da7d0SScott Long 			device_printf(sc->aac_dev, "device_add_child failed\n");
380914da7d0SScott Long 		else
381914da7d0SScott Long 			device_set_ivars(child, co);
382914da7d0SScott Long 		device_set_desc(child, aac_describe_code(aac_container_types,
383914da7d0SScott Long 				mir->MntTable[0].VolType));
384914da7d0SScott Long 		co->co_disk = child;
385914da7d0SScott Long 		co->co_found = f;
386914da7d0SScott Long 		bcopy(&mir->MntTable[0], &co->co_mntobj,
387914da7d0SScott Long 		      sizeof(struct aac_mntobj));
388c3d15322SScott Long 		AAC_LOCK_ACQUIRE(&sc->aac_container_lock);
389914da7d0SScott Long 		TAILQ_INSERT_TAIL(&sc->aac_container_tqh, co, co_link);
390914da7d0SScott Long 		AAC_LOCK_RELEASE(&sc->aac_container_lock);
391914da7d0SScott Long 	}
392914da7d0SScott Long }
393914da7d0SScott Long 
394914da7d0SScott Long /*
39535863739SMike Smith  * Free all of the resources associated with (sc)
39635863739SMike Smith  *
39735863739SMike Smith  * Should not be called if the controller is active.
39835863739SMike Smith  */
39935863739SMike Smith void
40035863739SMike Smith aac_free(struct aac_softc *sc)
40135863739SMike Smith {
402ffb37f33SScott Long 
40335863739SMike Smith 	debug_called(1);
40435863739SMike Smith 
40535863739SMike Smith 	/* remove the control device */
40635863739SMike Smith 	if (sc->aac_dev_t != NULL)
40735863739SMike Smith 		destroy_dev(sc->aac_dev_t);
40835863739SMike Smith 
4090b94a66eSMike Smith 	/* throw away any FIB buffers, discard the FIB DMA tag */
4108480cc63SScott Long 	aac_free_commands(sc);
4110b94a66eSMike Smith 	if (sc->aac_fib_dmat)
4120b94a66eSMike Smith 		bus_dma_tag_destroy(sc->aac_fib_dmat);
41335863739SMike Smith 
414ffb37f33SScott Long 	free(sc->aac_commands, M_AACBUF);
415ffb37f33SScott Long 
41635863739SMike Smith 	/* destroy the common area */
41735863739SMike Smith 	if (sc->aac_common) {
41835863739SMike Smith 		bus_dmamap_unload(sc->aac_common_dmat, sc->aac_common_dmamap);
419c6eafcf2SScott Long 		bus_dmamem_free(sc->aac_common_dmat, sc->aac_common,
420c6eafcf2SScott Long 				sc->aac_common_dmamap);
42135863739SMike Smith 	}
4220b94a66eSMike Smith 	if (sc->aac_common_dmat)
4230b94a66eSMike Smith 		bus_dma_tag_destroy(sc->aac_common_dmat);
42435863739SMike Smith 
42535863739SMike Smith 	/* disconnect the interrupt handler */
42635863739SMike Smith 	if (sc->aac_intr)
42735863739SMike Smith 		bus_teardown_intr(sc->aac_dev, sc->aac_irq, sc->aac_intr);
42835863739SMike Smith 	if (sc->aac_irq != NULL)
429c6eafcf2SScott Long 		bus_release_resource(sc->aac_dev, SYS_RES_IRQ, sc->aac_irq_rid,
430c6eafcf2SScott Long 				     sc->aac_irq);
43135863739SMike Smith 
43235863739SMike Smith 	/* destroy data-transfer DMA tag */
43335863739SMike Smith 	if (sc->aac_buffer_dmat)
43435863739SMike Smith 		bus_dma_tag_destroy(sc->aac_buffer_dmat);
43535863739SMike Smith 
43635863739SMike Smith 	/* destroy the parent DMA tag */
43735863739SMike Smith 	if (sc->aac_parent_dmat)
43835863739SMike Smith 		bus_dma_tag_destroy(sc->aac_parent_dmat);
43935863739SMike Smith 
44035863739SMike Smith 	/* release the register window mapping */
44135863739SMike Smith 	if (sc->aac_regs_resource != NULL)
442914da7d0SScott Long 		bus_release_resource(sc->aac_dev, SYS_RES_MEMORY,
443914da7d0SScott Long 				     sc->aac_regs_rid, sc->aac_regs_resource);
44435863739SMike Smith }
44535863739SMike Smith 
446914da7d0SScott Long /*
44735863739SMike Smith  * Disconnect from the controller completely, in preparation for unload.
44835863739SMike Smith  */
44935863739SMike Smith int
45035863739SMike Smith aac_detach(device_t dev)
45135863739SMike Smith {
452914da7d0SScott Long 	struct aac_softc *sc;
45370545d1aSScott Long 	struct aac_container *co;
45470545d1aSScott Long 	struct aac_sim	*sim;
45535863739SMike Smith 	int error;
45635863739SMike Smith 
45735863739SMike Smith 	debug_called(1);
45835863739SMike Smith 
459914da7d0SScott Long 	sc = device_get_softc(dev);
460914da7d0SScott Long 
46135863739SMike Smith 	if (sc->aac_state & AAC_STATE_OPEN)
46235863739SMike Smith 		return(EBUSY);
46335863739SMike Smith 
46470545d1aSScott Long 	/* Remove the child containers */
465a761a1caSScott Long 	while ((co = TAILQ_FIRST(&sc->aac_container_tqh)) != NULL) {
46670545d1aSScott Long 		error = device_delete_child(dev, co->co_disk);
46770545d1aSScott Long 		if (error)
46870545d1aSScott Long 			return (error);
46965ac4ed6SScott Long 		TAILQ_REMOVE(&sc->aac_container_tqh, co, co_link);
470a761a1caSScott Long 		free(co, M_AACBUF);
47170545d1aSScott Long 	}
47270545d1aSScott Long 
47370545d1aSScott Long 	/* Remove the CAM SIMs */
474a761a1caSScott Long 	while ((sim = TAILQ_FIRST(&sc->aac_sim_tqh)) != NULL) {
475a761a1caSScott Long 		TAILQ_REMOVE(&sc->aac_sim_tqh, sim, sim_link);
47670545d1aSScott Long 		error = device_delete_child(dev, sim->sim_dev);
47770545d1aSScott Long 		if (error)
47870545d1aSScott Long 			return (error);
479a761a1caSScott Long 		free(sim, M_AACBUF);
48070545d1aSScott Long 	}
48170545d1aSScott Long 
48236e0bf6eSScott Long 	if (sc->aifflags & AAC_AIFFLAGS_RUNNING) {
48336e0bf6eSScott Long 		sc->aifflags |= AAC_AIFFLAGS_EXIT;
48436e0bf6eSScott Long 		wakeup(sc->aifthread);
48536e0bf6eSScott Long 		tsleep(sc->aac_dev, PUSER | PCATCH, "aacdch", 30 * hz);
48636e0bf6eSScott Long 	}
48736e0bf6eSScott Long 
48836e0bf6eSScott Long 	if (sc->aifflags & AAC_AIFFLAGS_RUNNING)
48936e0bf6eSScott Long 		panic("Cannot shutdown AIF thread\n");
49036e0bf6eSScott Long 
49135863739SMike Smith 	if ((error = aac_shutdown(dev)))
49235863739SMike Smith 		return(error);
49335863739SMike Smith 
4945f54d522SScott Long 	EVENTHANDLER_DEREGISTER(shutdown_final, sc->eh);
4955f54d522SScott Long 
49635863739SMike Smith 	aac_free(sc);
49735863739SMike Smith 
49835863739SMike Smith 	return(0);
49935863739SMike Smith }
50035863739SMike Smith 
501914da7d0SScott Long /*
50235863739SMike Smith  * Bring the controller down to a dormant state and detach all child devices.
50335863739SMike Smith  *
50435863739SMike Smith  * This function is called before detach or system shutdown.
50535863739SMike Smith  *
5060b94a66eSMike Smith  * Note that we can assume that the bioq on the controller is empty, as we won't
50735863739SMike Smith  * allow shutdown if any device is open.
50835863739SMike Smith  */
50935863739SMike Smith int
51035863739SMike Smith aac_shutdown(device_t dev)
51135863739SMike Smith {
512914da7d0SScott Long 	struct aac_softc *sc;
513cbfd045bSScott Long 	struct aac_fib *fib;
514cbfd045bSScott Long 	struct aac_close_command *cc;
51535863739SMike Smith 
51635863739SMike Smith 	debug_called(1);
51735863739SMike Smith 
518914da7d0SScott Long 	sc = device_get_softc(dev);
519914da7d0SScott Long 
52035863739SMike Smith 	sc->aac_state |= AAC_STATE_SUSPEND;
52135863739SMike Smith 
52235863739SMike Smith 	/*
52335863739SMike Smith 	 * Send a Container shutdown followed by a HostShutdown FIB to the
52435863739SMike Smith 	 * controller to convince it that we don't want to talk to it anymore.
52535863739SMike Smith 	 * We've been closed and all I/O completed already
52635863739SMike Smith 	 */
52735863739SMike Smith 	device_printf(sc->aac_dev, "shutting down controller...");
52835863739SMike Smith 
529fe3cb0e1SScott Long 	aac_alloc_sync_fib(sc, &fib, AAC_SYNC_LOCK_FORCE);
530cbfd045bSScott Long 	cc = (struct aac_close_command *)&fib->data[0];
531cbfd045bSScott Long 
53239ee03c3SScott Long 	bzero(cc, sizeof(struct aac_close_command));
533cbfd045bSScott Long 	cc->Command = VM_CloseAll;
534cbfd045bSScott Long 	cc->ContainerId = 0xffffffff;
535cbfd045bSScott Long 	if (aac_sync_fib(sc, ContainerCommand, 0, fib,
536cbfd045bSScott Long 	    sizeof(struct aac_close_command)))
53735863739SMike Smith 		printf("FAILED.\n");
53870545d1aSScott Long 	else
53970545d1aSScott Long 		printf("done\n");
54070545d1aSScott Long #if 0
541914da7d0SScott Long 	else {
542cbfd045bSScott Long 		fib->data[0] = 0;
54336e0bf6eSScott Long 		/*
544914da7d0SScott Long 		 * XXX Issuing this command to the controller makes it shut down
54536e0bf6eSScott Long 		 * but also keeps it from coming back up without a reset of the
54636e0bf6eSScott Long 		 * PCI bus.  This is not desirable if you are just unloading the
54736e0bf6eSScott Long 		 * driver module with the intent to reload it later.
54836e0bf6eSScott Long 		 */
549cbfd045bSScott Long 		if (aac_sync_fib(sc, FsaHostShutdown, AAC_FIBSTATE_SHUTDOWN,
550cbfd045bSScott Long 		    fib, 1)) {
55135863739SMike Smith 			printf("FAILED.\n");
55235863739SMike Smith 		} else {
55335863739SMike Smith 			printf("done.\n");
55435863739SMike Smith 		}
55535863739SMike Smith 	}
55670545d1aSScott Long #endif
55735863739SMike Smith 
55835863739SMike Smith 	AAC_MASK_INTERRUPTS(sc);
55935863739SMike Smith 
56035863739SMike Smith 	return(0);
56135863739SMike Smith }
56235863739SMike Smith 
563914da7d0SScott Long /*
56435863739SMike Smith  * Bring the controller to a quiescent state, ready for system suspend.
56535863739SMike Smith  */
56635863739SMike Smith int
56735863739SMike Smith aac_suspend(device_t dev)
56835863739SMike Smith {
569914da7d0SScott Long 	struct aac_softc *sc;
57035863739SMike Smith 
57135863739SMike Smith 	debug_called(1);
572914da7d0SScott Long 
573914da7d0SScott Long 	sc = device_get_softc(dev);
574914da7d0SScott Long 
57535863739SMike Smith 	sc->aac_state |= AAC_STATE_SUSPEND;
57635863739SMike Smith 
57735863739SMike Smith 	AAC_MASK_INTERRUPTS(sc);
57835863739SMike Smith 	return(0);
57935863739SMike Smith }
58035863739SMike Smith 
581914da7d0SScott Long /*
58235863739SMike Smith  * Bring the controller back to a state ready for operation.
58335863739SMike Smith  */
58435863739SMike Smith int
58535863739SMike Smith aac_resume(device_t dev)
58635863739SMike Smith {
587914da7d0SScott Long 	struct aac_softc *sc;
58835863739SMike Smith 
58935863739SMike Smith 	debug_called(1);
590914da7d0SScott Long 
591914da7d0SScott Long 	sc = device_get_softc(dev);
592914da7d0SScott Long 
59335863739SMike Smith 	sc->aac_state &= ~AAC_STATE_SUSPEND;
59435863739SMike Smith 	AAC_UNMASK_INTERRUPTS(sc);
59535863739SMike Smith 	return(0);
59635863739SMike Smith }
59735863739SMike Smith 
598914da7d0SScott Long /*
59935863739SMike Smith  * Take an interrupt.
60035863739SMike Smith  */
60135863739SMike Smith void
60235863739SMike Smith aac_intr(void *arg)
60335863739SMike Smith {
604914da7d0SScott Long 	struct aac_softc *sc;
605f30ac74cSScott Long 	u_int32_t *resp_queue;
60670545d1aSScott Long 	u_int16_t reason;
60735863739SMike Smith 
60835863739SMike Smith 	debug_called(2);
60935863739SMike Smith 
610914da7d0SScott Long 	sc = (struct aac_softc *)arg;
611914da7d0SScott Long 
612f30ac74cSScott Long 	/*
613f30ac74cSScott Long 	 * Optimize the common case of adapter response interrupts.
614f30ac74cSScott Long 	 * We must read from the card prior to processing the responses
615f30ac74cSScott Long 	 * to ensure the clear is flushed prior to accessing the queues.
616f30ac74cSScott Long 	 * Reading the queues from local memory might save us a PCI read.
617f30ac74cSScott Long 	 */
618f30ac74cSScott Long 	resp_queue = sc->aac_queues->qt_qindex[AAC_HOST_NORM_RESP_QUEUE];
619f30ac74cSScott Long 	if (resp_queue[AAC_PRODUCER_INDEX] != resp_queue[AAC_CONSUMER_INDEX])
620f30ac74cSScott Long 		reason = AAC_DB_RESPONSE_READY;
621f30ac74cSScott Long 	else
62235863739SMike Smith 		reason = AAC_GET_ISTATUS(sc);
623f30ac74cSScott Long 	AAC_CLEAR_ISTATUS(sc, reason);
624f30ac74cSScott Long 	(void)AAC_GET_ISTATUS(sc);
625f30ac74cSScott Long 
626f30ac74cSScott Long 	/* It's not ok to return here because of races with the previous step */
627f30ac74cSScott Long 	if (reason & AAC_DB_RESPONSE_READY)
6289c3a7fceSScott Long 		/* handle completion processing */
629ae543596SScott Long 		taskqueue_enqueue(taskqueue_swi, &sc->aac_task_complete);
63035863739SMike Smith 
631b3457b51SScott Long 	/* controller wants to talk to the log */
63270545d1aSScott Long 	if (reason & AAC_DB_PRINTF) {
63370545d1aSScott Long 		if (sc->aifflags & AAC_AIFFLAGS_RUNNING) {
63470545d1aSScott Long 			sc->aifflags |= AAC_AIFFLAGS_PRINTF;
63570545d1aSScott Long 		} else
63636e0bf6eSScott Long 			aac_print_printf(sc);
63770545d1aSScott Long 	}
63835863739SMike Smith 
63935863739SMike Smith 	/* controller has a message for us? */
64035863739SMike Smith 	if (reason & AAC_DB_COMMAND_READY) {
64136e0bf6eSScott Long 		if (sc->aifflags & AAC_AIFFLAGS_RUNNING) {
64270545d1aSScott Long 			sc->aifflags |= AAC_AIFFLAGS_AIF;
64370545d1aSScott Long 		} else {
64470545d1aSScott Long 			/*
64570545d1aSScott Long 			 * XXX If the kthread is dead and we're at this point,
64670545d1aSScott Long 			 * there are bigger problems than just figuring out
64770545d1aSScott Long 			 * what to do with an AIF.
64870545d1aSScott Long 			 */
64970545d1aSScott Long 		}
65070545d1aSScott Long 
65170545d1aSScott Long 	}
65270545d1aSScott Long 
65370545d1aSScott Long 	if ((sc->aifflags & AAC_AIFFLAGS_PENDING) != 0)
65470545d1aSScott Long 		/* XXX Should this be done with cv_signal? */
65536e0bf6eSScott Long 		wakeup(sc->aifthread);
65636e0bf6eSScott Long }
65735863739SMike Smith 
658c6eafcf2SScott Long /*
659914da7d0SScott Long  * Command Processing
660914da7d0SScott Long  */
66135863739SMike Smith 
662914da7d0SScott Long /*
66335863739SMike Smith  * Start as much queued I/O as possible on the controller
66435863739SMike Smith  */
665fe3cb0e1SScott Long void
66635863739SMike Smith aac_startio(struct aac_softc *sc)
66735863739SMike Smith {
66835863739SMike Smith 	struct aac_command *cm;
66935863739SMike Smith 
67035863739SMike Smith 	debug_called(2);
67135863739SMike Smith 
672cd481291SScott Long 	if (sc->flags & AAC_QUEUE_FRZN)
673cd481291SScott Long 		return;
674cd481291SScott Long 
67535863739SMike Smith 	for (;;) {
676914da7d0SScott Long 		/*
677914da7d0SScott Long 		 * Try to get a command that's been put off for lack of
678914da7d0SScott Long 		 * resources
679914da7d0SScott Long 		 */
68035863739SMike Smith 		cm = aac_dequeue_ready(sc);
68135863739SMike Smith 
682914da7d0SScott Long 		/*
683914da7d0SScott Long 		 * Try to build a command off the bio queue (ignore error
684914da7d0SScott Long 		 * return)
685914da7d0SScott Long 		 */
6860b94a66eSMike Smith 		if (cm == NULL)
68735863739SMike Smith 			aac_bio_command(sc, &cm);
68835863739SMike Smith 
68935863739SMike Smith 		/* nothing to do? */
69035863739SMike Smith 		if (cm == NULL)
69135863739SMike Smith 			break;
69235863739SMike Smith 
69335863739SMike Smith 		/* try to give the command to the controller */
694cd481291SScott Long 		if (aac_map_command(cm) == EBUSY) {
69535863739SMike Smith 			/* put it on the ready queue for later */
69635863739SMike Smith 			aac_requeue_ready(cm);
69735863739SMike Smith 			break;
69835863739SMike Smith 		}
69935863739SMike Smith 	}
70035863739SMike Smith }
70135863739SMike Smith 
702914da7d0SScott Long /*
70335863739SMike Smith  * Deliver a command to the controller; allocate controller resources at the
70435863739SMike Smith  * last moment when possible.
70535863739SMike Smith  */
70635863739SMike Smith static int
707cd481291SScott Long aac_map_command(struct aac_command *cm)
70835863739SMike Smith {
709914da7d0SScott Long 	struct aac_softc *sc;
710ed5c5fb4SMike Smith 	int error;
71135863739SMike Smith 
71235863739SMike Smith 	debug_called(2);
71335863739SMike Smith 
714914da7d0SScott Long 	sc = cm->cm_sc;
715cd481291SScott Long 	error = 0;
716914da7d0SScott Long 
717cd481291SScott Long 	/* don't map more than once */
718cd481291SScott Long 	if (cm->cm_flags & AAC_CMD_MAPPED)
719cd481291SScott Long 		return (0);
72035863739SMike Smith 
721cd481291SScott Long 	if (cm->cm_datalen != 0) {
722cd481291SScott Long 		error = bus_dmamap_load(sc->aac_buffer_dmat, cm->cm_datamap,
723cd481291SScott Long 					cm->cm_data, cm->cm_datalen,
724cd481291SScott Long 					aac_map_command_sg, cm, 0);
725cd481291SScott Long 		if (error == EINPROGRESS) {
726cd481291SScott Long 			debug(1, "freezing queue\n");
727cd481291SScott Long 			sc->flags |= AAC_QUEUE_FRZN;
728cd481291SScott Long 			error = 0;
729cd481291SScott Long 		}
730cd481291SScott Long 	}
7310b94a66eSMike Smith 	return (error);
73235863739SMike Smith }
73335863739SMike Smith 
734914da7d0SScott Long /*
73535863739SMike Smith  * Handle notification of one or more FIBs coming from the controller.
73635863739SMike Smith  */
73735863739SMike Smith static void
73870545d1aSScott Long aac_command_thread(struct aac_softc *sc)
73935863739SMike Smith {
74035863739SMike Smith 	struct aac_fib *fib;
74135863739SMike Smith 	u_int32_t fib_size;
74236e0bf6eSScott Long 	int size;
74335863739SMike Smith 
74436e0bf6eSScott Long 	debug_called(2);
74535863739SMike Smith 
74636e0bf6eSScott Long 	sc->aifflags |= AAC_AIFFLAGS_RUNNING;
74736e0bf6eSScott Long 
74836e0bf6eSScott Long 	while (!(sc->aifflags & AAC_AIFFLAGS_EXIT)) {
74970545d1aSScott Long 		if ((sc->aifflags & AAC_AIFFLAGS_PENDING) == 0)
75070545d1aSScott Long 			tsleep(sc->aifthread, PRIBIO, "aifthd",
75170545d1aSScott Long 			       AAC_PERIODIC_INTERVAL * hz);
75236e0bf6eSScott Long 
75370545d1aSScott Long 		if ((sc->aifflags & AAC_AIFFLAGS_PENDING) == 0)
75470545d1aSScott Long 			aac_timeout(sc);
75570545d1aSScott Long 
75670545d1aSScott Long 		/* Check the hardware printf message buffer */
75770545d1aSScott Long 		if ((sc->aifflags & AAC_AIFFLAGS_PRINTF) != 0) {
75870545d1aSScott Long 			sc->aifflags &= ~AAC_AIFFLAGS_PRINTF;
75970545d1aSScott Long 			aac_print_printf(sc);
76070545d1aSScott Long 		}
76170545d1aSScott Long 
762ae543596SScott Long 		/* See if any FIBs need to be allocated */
763ae543596SScott Long 		if ((sc->aifflags & AAC_AIFFLAGS_ALLOCFIBS) != 0) {
764ae543596SScott Long 			AAC_LOCK_ACQUIRE(&sc->aac_io_lock);
765ae543596SScott Long 			aac_alloc_commands(sc);
766ae543596SScott Long 			sc->aifflags &= ~AAC_AIFFLAGS_ALLOCFIBS;
767ae543596SScott Long 			AAC_LOCK_RELEASE(&sc->aac_io_lock);
768ae543596SScott Long 		}
76970545d1aSScott Long 
770ae543596SScott Long 		/* While we're here, check to see if any commands are stuck */
771ae543596SScott Long 		while (sc->aifflags & AAC_AIFFLAGS_AIF) {
772914da7d0SScott Long 			if (aac_dequeue_fib(sc, AAC_HOST_NORM_CMD_QUEUE,
77370545d1aSScott Long 					    &fib_size, &fib)) {
77470545d1aSScott Long 				sc->aifflags &= ~AAC_AIFFLAGS_AIF;
77535863739SMike Smith 				break;	/* nothing to do */
77670545d1aSScott Long 			}
77735863739SMike Smith 
77836e0bf6eSScott Long 			AAC_PRINT_FIB(sc, fib);
77936e0bf6eSScott Long 
78035863739SMike Smith 			switch (fib->Header.Command) {
78135863739SMike Smith 			case AifRequest:
78236e0bf6eSScott Long 				aac_handle_aif(sc, fib);
78335863739SMike Smith 				break;
78435863739SMike Smith 			default:
785914da7d0SScott Long 				device_printf(sc->aac_dev, "unknown command "
786914da7d0SScott Long 					      "from controller\n");
78735863739SMike Smith 				break;
78835863739SMike Smith 			}
78935863739SMike Smith 
79036e0bf6eSScott Long 			if ((fib->Header.XferState == 0) ||
79136e0bf6eSScott Long 			    (fib->Header.StructType != AAC_FIBTYPE_TFIB))
79236e0bf6eSScott Long 				break;
79336e0bf6eSScott Long 
79470545d1aSScott Long 			/* Return the AIF to the controller. */
79536e0bf6eSScott Long 			if (fib->Header.XferState & AAC_FIBSTATE_FROMADAP) {
79636e0bf6eSScott Long 				fib->Header.XferState |= AAC_FIBSTATE_DONEHOST;
79736e0bf6eSScott Long 				*(AAC_FSAStatus*)fib->data = ST_OK;
79836e0bf6eSScott Long 
79936e0bf6eSScott Long 				/* XXX Compute the Size field? */
80036e0bf6eSScott Long 				size = fib->Header.Size;
80136e0bf6eSScott Long 				if (size > sizeof(struct aac_fib)) {
80236e0bf6eSScott Long 					size = sizeof(struct aac_fib);
80336e0bf6eSScott Long 					fib->Header.Size = size;
80436e0bf6eSScott Long 				}
80536e0bf6eSScott Long 				/*
806914da7d0SScott Long 				 * Since we did not generate this command, it
807914da7d0SScott Long 				 * cannot go through the normal
808914da7d0SScott Long 				 * enqueue->startio chain.
80936e0bf6eSScott Long 				 */
810914da7d0SScott Long 				aac_enqueue_response(sc,
811914da7d0SScott Long 						     AAC_ADAP_NORM_RESP_QUEUE,
812914da7d0SScott Long 						     fib);
81336e0bf6eSScott Long 			}
81436e0bf6eSScott Long 		}
81536e0bf6eSScott Long 	}
81636e0bf6eSScott Long 	sc->aifflags &= ~AAC_AIFFLAGS_RUNNING;
81736e0bf6eSScott Long 	wakeup(sc->aac_dev);
81836e0bf6eSScott Long 
81936e0bf6eSScott Long 	mtx_lock(&Giant);
82036e0bf6eSScott Long 	kthread_exit(0);
82135863739SMike Smith }
82235863739SMike Smith 
823914da7d0SScott Long /*
8249c3a7fceSScott Long  * Process completed commands.
82535863739SMike Smith  */
82635863739SMike Smith static void
8279c3a7fceSScott Long aac_complete(void *context, int pending)
82835863739SMike Smith {
8299c3a7fceSScott Long 	struct aac_softc *sc;
83035863739SMike Smith 	struct aac_command *cm;
83135863739SMike Smith 	struct aac_fib *fib;
83235863739SMike Smith 	u_int32_t fib_size;
83335863739SMike Smith 
83435863739SMike Smith 	debug_called(2);
83535863739SMike Smith 
8369c3a7fceSScott Long 	sc = (struct aac_softc *)context;
8379c3a7fceSScott Long 
838ae543596SScott Long 	AAC_LOCK_ACQUIRE(&sc->aac_io_lock);
839ae543596SScott Long 
8409c3a7fceSScott Long 	/* pull completed commands off the queue */
84135863739SMike Smith 	for (;;) {
84235863739SMike Smith 		/* look for completed FIBs on our queue */
843914da7d0SScott Long 		if (aac_dequeue_fib(sc, AAC_HOST_NORM_RESP_QUEUE, &fib_size,
844914da7d0SScott Long 				    &fib))
84535863739SMike Smith 			break;	/* nothing to do */
84635863739SMike Smith 
84735863739SMike Smith 		/* get the command, unmap and queue for later processing */
848cb0d64b9SScott Long 		cm = sc->aac_commands + fib->Header.SenderData;
84935863739SMike Smith 		if (cm == NULL) {
85035863739SMike Smith 			AAC_PRINT_FIB(sc, fib);
8519c3a7fceSScott Long 			break;
8529c3a7fceSScott Long 		}
8539c3a7fceSScott Long 
8540b94a66eSMike Smith 		aac_remove_busy(cm);
85535863739SMike Smith 		aac_unmap_command(cm);		/* XXX defer? */
85635863739SMike Smith 		cm->cm_flags |= AAC_CMD_COMPLETED;
85735863739SMike Smith 
85835863739SMike Smith 		/* is there a completion handler? */
85935863739SMike Smith 		if (cm->cm_complete != NULL) {
86035863739SMike Smith 			cm->cm_complete(cm);
86135863739SMike Smith 		} else {
86235863739SMike Smith 			/* assume that someone is sleeping on this command */
86335863739SMike Smith 			wakeup(cm);
86435863739SMike Smith 		}
86535863739SMike Smith 	}
8660b94a66eSMike Smith 
8670b94a66eSMike Smith 	/* see if we can start some more I/O */
868cd481291SScott Long 	sc->flags &= ~AAC_QUEUE_FRZN;
8690b94a66eSMike Smith 	aac_startio(sc);
870ae543596SScott Long 
871ae543596SScott Long 	AAC_LOCK_RELEASE(&sc->aac_io_lock);
87235863739SMike Smith }
87335863739SMike Smith 
874914da7d0SScott Long /*
87535863739SMike Smith  * Handle a bio submitted from a disk device.
87635863739SMike Smith  */
87735863739SMike Smith void
87835863739SMike Smith aac_submit_bio(struct bio *bp)
87935863739SMike Smith {
880914da7d0SScott Long 	struct aac_disk *ad;
881914da7d0SScott Long 	struct aac_softc *sc;
88235863739SMike Smith 
88335863739SMike Smith 	debug_called(2);
88435863739SMike Smith 
8857540e65eSScott Long 	ad = (struct aac_disk *)bp->bio_disk->d_drv1;
886914da7d0SScott Long 	sc = ad->ad_controller;
887914da7d0SScott Long 
88835863739SMike Smith 	/* queue the BIO and try to get some work done */
8890b94a66eSMike Smith 	aac_enqueue_bio(sc, bp);
89035863739SMike Smith 	aac_startio(sc);
89135863739SMike Smith }
89235863739SMike Smith 
893914da7d0SScott Long /*
89435863739SMike Smith  * Get a bio and build a command to go with it.
89535863739SMike Smith  */
89635863739SMike Smith static int
89735863739SMike Smith aac_bio_command(struct aac_softc *sc, struct aac_command **cmp)
89835863739SMike Smith {
89935863739SMike Smith 	struct aac_command *cm;
90035863739SMike Smith 	struct aac_fib *fib;
90135863739SMike Smith 	struct aac_disk *ad;
90235863739SMike Smith 	struct bio *bp;
90335863739SMike Smith 
90435863739SMike Smith 	debug_called(2);
90535863739SMike Smith 
90635863739SMike Smith 	/* get the resources we will need */
90735863739SMike Smith 	cm = NULL;
9080b94a66eSMike Smith 	if ((bp = aac_dequeue_bio(sc)) == NULL)
90935863739SMike Smith 		goto fail;
91035863739SMike Smith 	if (aac_alloc_command(sc, &cm))	/* get a command */
91135863739SMike Smith 		goto fail;
91235863739SMike Smith 
91335863739SMike Smith 	/* fill out the command */
9140b94a66eSMike Smith 	cm->cm_data = (void *)bp->bio_data;
9150b94a66eSMike Smith 	cm->cm_datalen = bp->bio_bcount;
9160b94a66eSMike Smith 	cm->cm_complete = aac_bio_complete;
91735863739SMike Smith 	cm->cm_private = bp;
9180b94a66eSMike Smith 	cm->cm_timestamp = time_second;
91936e0bf6eSScott Long 	cm->cm_queue = AAC_ADAP_NORM_CMD_QUEUE;
92035863739SMike Smith 
92135863739SMike Smith 	/* build the FIB */
92235863739SMike Smith 	fib = cm->cm_fib;
923b85f5808SScott Long 	fib->Header.Size = sizeof(struct aac_fib_header);
92435863739SMike Smith 	fib->Header.XferState =
92535863739SMike Smith 		AAC_FIBSTATE_HOSTOWNED   |
92635863739SMike Smith 		AAC_FIBSTATE_INITIALISED |
927f30ac74cSScott Long 		AAC_FIBSTATE_EMPTY	 |
92835863739SMike Smith 		AAC_FIBSTATE_FROMHOST	 |
92935863739SMike Smith 		AAC_FIBSTATE_REXPECTED   |
930f30ac74cSScott Long 		AAC_FIBSTATE_NORM	 |
931f30ac74cSScott Long 		AAC_FIBSTATE_ASYNC	 |
932f30ac74cSScott Long 		AAC_FIBSTATE_FAST_RESPONSE;
93335863739SMike Smith 
93435863739SMike Smith 	/* build the read/write request */
9357540e65eSScott Long 	ad = (struct aac_disk *)bp->bio_disk->d_drv1;
936b85f5808SScott Long 
937b85f5808SScott Long 	if ((sc->flags & AAC_FLAGS_SG_64BIT) == 0) {
938b85f5808SScott Long 		fib->Header.Command = ContainerCommand;
9399e2e96d8SScott Long 		if (bp->bio_cmd == BIO_READ) {
940b85f5808SScott Long 			struct aac_blockread *br;
94135863739SMike Smith 			br = (struct aac_blockread *)&fib->data[0];
94235863739SMike Smith 			br->Command = VM_CtBlockRead;
94335863739SMike Smith 			br->ContainerId = ad->ad_container->co_mntobj.ObjectId;
94435863739SMike Smith 			br->BlockNumber = bp->bio_pblkno;
94535863739SMike Smith 			br->ByteCount = bp->bio_bcount;
94635863739SMike Smith 			fib->Header.Size += sizeof(struct aac_blockread);
94735863739SMike Smith 			cm->cm_sgtable = &br->SgMap;
94835863739SMike Smith 			cm->cm_flags |= AAC_CMD_DATAIN;
94935863739SMike Smith 		} else {
950b85f5808SScott Long 			struct aac_blockwrite *bw;
95135863739SMike Smith 			bw = (struct aac_blockwrite *)&fib->data[0];
95235863739SMike Smith 			bw->Command = VM_CtBlockWrite;
95335863739SMike Smith 			bw->ContainerId = ad->ad_container->co_mntobj.ObjectId;
95435863739SMike Smith 			bw->BlockNumber = bp->bio_pblkno;
95535863739SMike Smith 			bw->ByteCount = bp->bio_bcount;
956b85f5808SScott Long 			bw->Stable = CUNSTABLE;
95735863739SMike Smith 			fib->Header.Size += sizeof(struct aac_blockwrite);
95835863739SMike Smith 			cm->cm_flags |= AAC_CMD_DATAOUT;
95935863739SMike Smith 			cm->cm_sgtable = &bw->SgMap;
96035863739SMike Smith 		}
961b85f5808SScott Long 	} else {
962b85f5808SScott Long 		fib->Header.Command = ContainerCommand64;
963b85f5808SScott Long 		if (bp->bio_cmd == BIO_READ) {
964b85f5808SScott Long 			struct aac_blockread64 *br;
965b85f5808SScott Long 			br = (struct aac_blockread64 *)&fib->data[0];
966b85f5808SScott Long 			br->Command = VM_CtHostRead64;
967b85f5808SScott Long 			br->ContainerId = ad->ad_container->co_mntobj.ObjectId;
968b85f5808SScott Long 			br->SectorCount = bp->bio_bcount / AAC_BLOCK_SIZE;
969b85f5808SScott Long 			br->BlockNumber = bp->bio_pblkno;
970b85f5808SScott Long 			br->Pad = 0;
971b85f5808SScott Long 			br->Flags = 0;
972b85f5808SScott Long 			fib->Header.Size += sizeof(struct aac_blockread64);
973b85f5808SScott Long 			cm->cm_flags |= AAC_CMD_DATAOUT;
974b85f5808SScott Long 			(struct aac_sg_table64 *)cm->cm_sgtable = &br->SgMap64;
975b85f5808SScott Long 		} else {
976b85f5808SScott Long 			struct aac_blockwrite64 *bw;
977b85f5808SScott Long 			bw = (struct aac_blockwrite64 *)&fib->data[0];
978b85f5808SScott Long 			bw->Command = VM_CtHostWrite64;
979b85f5808SScott Long 			bw->ContainerId = ad->ad_container->co_mntobj.ObjectId;
980b85f5808SScott Long 			bw->SectorCount = bp->bio_bcount / AAC_BLOCK_SIZE;
981b85f5808SScott Long 			bw->BlockNumber = bp->bio_pblkno;
982b85f5808SScott Long 			bw->Pad = 0;
983b85f5808SScott Long 			bw->Flags = 0;
984b85f5808SScott Long 			fib->Header.Size += sizeof(struct aac_blockwrite64);
985b85f5808SScott Long 			cm->cm_flags |= AAC_CMD_DATAIN;
986b85f5808SScott Long 			(struct aac_sg_table64 *)cm->cm_sgtable = &bw->SgMap64;
987b85f5808SScott Long 		}
988b85f5808SScott Long 	}
98935863739SMike Smith 
99035863739SMike Smith 	*cmp = cm;
99135863739SMike Smith 	return(0);
99235863739SMike Smith 
99335863739SMike Smith fail:
99435863739SMike Smith 	if (bp != NULL)
9950b94a66eSMike Smith 		aac_enqueue_bio(sc, bp);
99635863739SMike Smith 	if (cm != NULL)
99735863739SMike Smith 		aac_release_command(cm);
99835863739SMike Smith 	return(ENOMEM);
99935863739SMike Smith }
100035863739SMike Smith 
1001914da7d0SScott Long /*
100235863739SMike Smith  * Handle a bio-instigated command that has been completed.
100335863739SMike Smith  */
100435863739SMike Smith static void
100535863739SMike Smith aac_bio_complete(struct aac_command *cm)
100635863739SMike Smith {
100735863739SMike Smith 	struct aac_blockread_response *brr;
100835863739SMike Smith 	struct aac_blockwrite_response *bwr;
100935863739SMike Smith 	struct bio *bp;
101035863739SMike Smith 	AAC_FSAStatus status;
101135863739SMike Smith 
101235863739SMike Smith 	/* fetch relevant status and then release the command */
101335863739SMike Smith 	bp = (struct bio *)cm->cm_private;
10149e2e96d8SScott Long 	if (bp->bio_cmd == BIO_READ) {
101535863739SMike Smith 		brr = (struct aac_blockread_response *)&cm->cm_fib->data[0];
101635863739SMike Smith 		status = brr->Status;
101735863739SMike Smith 	} else {
101835863739SMike Smith 		bwr = (struct aac_blockwrite_response *)&cm->cm_fib->data[0];
101935863739SMike Smith 		status = bwr->Status;
102035863739SMike Smith 	}
102135863739SMike Smith 	aac_release_command(cm);
102235863739SMike Smith 
102335863739SMike Smith 	/* fix up the bio based on status */
102435863739SMike Smith 	if (status == ST_OK) {
102535863739SMike Smith 		bp->bio_resid = 0;
102635863739SMike Smith 	} else {
102735863739SMike Smith 		bp->bio_error = EIO;
102835863739SMike Smith 		bp->bio_flags |= BIO_ERROR;
10290b94a66eSMike Smith 		/* pass an error string out to the disk layer */
1030914da7d0SScott Long 		bp->bio_driver1 = aac_describe_code(aac_command_status_table,
1031914da7d0SScott Long 						    status);
103235863739SMike Smith 	}
10330b94a66eSMike Smith 	aac_biodone(bp);
103435863739SMike Smith }
103535863739SMike Smith 
1036914da7d0SScott Long /*
103735863739SMike Smith  * Submit a command to the controller, return when it completes.
1038b3457b51SScott Long  * XXX This is very dangerous!  If the card has gone out to lunch, we could
1039b3457b51SScott Long  *     be stuck here forever.  At the same time, signals are not caught
1040b3457b51SScott Long  *     because there is a risk that a signal could wakeup the tsleep before
1041b3457b51SScott Long  *     the card has a chance to complete the command.  The passed in timeout
1042b3457b51SScott Long  *     is ignored for the same reason.  Since there is no way to cancel a
1043b3457b51SScott Long  *     command in progress, we should probably create a 'dead' queue where
1044b3457b51SScott Long  *     commands go that have been interrupted/timed-out/etc, that keeps them
1045b3457b51SScott Long  *     out of the free pool.  That way, if the card is just slow, it won't
1046b3457b51SScott Long  *     spam the memory of a command that has been recycled.
104735863739SMike Smith  */
104835863739SMike Smith static int
104935863739SMike Smith aac_wait_command(struct aac_command *cm, int timeout)
105035863739SMike Smith {
1051ae543596SScott Long 	struct aac_softc *sc;
1052ae543596SScott Long 	int error = 0;
105335863739SMike Smith 
105435863739SMike Smith 	debug_called(2);
105535863739SMike Smith 
1056ae543596SScott Long 	sc = cm->cm_sc;
1057ae543596SScott Long 
105835863739SMike Smith 	/* Put the command on the ready queue and get things going */
105936e0bf6eSScott Long 	cm->cm_queue = AAC_ADAP_NORM_CMD_QUEUE;
106035863739SMike Smith 	aac_enqueue_ready(cm);
1061ae543596SScott Long 	aac_startio(sc);
106235863739SMike Smith 	while (!(cm->cm_flags & AAC_CMD_COMPLETED) && (error != EWOULDBLOCK)) {
1063ae543596SScott Long 		error = msleep(cm, &sc->aac_io_lock, PRIBIO, "aacwait", 0);
106435863739SMike Smith 	}
106535863739SMike Smith 	return(error);
106635863739SMike Smith }
106735863739SMike Smith 
1068914da7d0SScott Long /*
1069914da7d0SScott Long  *Command Buffer Management
1070914da7d0SScott Long  */
107135863739SMike Smith 
1072914da7d0SScott Long /*
107335863739SMike Smith  * Allocate a command.
107435863739SMike Smith  */
1075fe3cb0e1SScott Long int
107635863739SMike Smith aac_alloc_command(struct aac_softc *sc, struct aac_command **cmp)
107735863739SMike Smith {
107835863739SMike Smith 	struct aac_command *cm;
107935863739SMike Smith 
108035863739SMike Smith 	debug_called(3);
108135863739SMike Smith 
1082ffb37f33SScott Long 	if ((cm = aac_dequeue_free(sc)) == NULL) {
1083b85f5808SScott Long 		if (sc->total_fibs < sc->aac_max_fibs) {
1084ae543596SScott Long 			sc->aifflags |= AAC_AIFFLAGS_ALLOCFIBS;
1085ae543596SScott Long 			wakeup(sc->aifthread);
1086b85f5808SScott Long 		}
1087ae543596SScott Long 		return (EBUSY);
1088ffb37f33SScott Long 	}
108935863739SMike Smith 
10900b94a66eSMike Smith 	*cmp = cm;
10910b94a66eSMike Smith 	return(0);
10920b94a66eSMike Smith }
10930b94a66eSMike Smith 
1094914da7d0SScott Long /*
10950b94a66eSMike Smith  * Release a command back to the freelist.
10960b94a66eSMike Smith  */
1097fe3cb0e1SScott Long void
10980b94a66eSMike Smith aac_release_command(struct aac_command *cm)
10990b94a66eSMike Smith {
11000b94a66eSMike Smith 	debug_called(3);
11010b94a66eSMike Smith 
11020b94a66eSMike Smith 	/* (re)initialise the command/FIB */
110335863739SMike Smith 	cm->cm_sgtable = NULL;
110435863739SMike Smith 	cm->cm_flags = 0;
110535863739SMike Smith 	cm->cm_complete = NULL;
110635863739SMike Smith 	cm->cm_private = NULL;
110735863739SMike Smith 	cm->cm_fib->Header.XferState = AAC_FIBSTATE_EMPTY;
110835863739SMike Smith 	cm->cm_fib->Header.StructType = AAC_FIBTYPE_TFIB;
110935863739SMike Smith 	cm->cm_fib->Header.Flags = 0;
111035863739SMike Smith 	cm->cm_fib->Header.SenderSize = sizeof(struct aac_fib);
111135863739SMike Smith 
111235863739SMike Smith 	/*
111335863739SMike Smith 	 * These are duplicated in aac_start to cover the case where an
111435863739SMike Smith 	 * intermediate stage may have destroyed them.  They're left
111535863739SMike Smith 	 * initialised here for debugging purposes only.
111635863739SMike Smith 	 */
1117f30ac74cSScott Long 	cm->cm_fib->Header.ReceiverFibAddress = (u_int32_t)cm->cm_fibphys;
1118f30ac74cSScott Long 	cm->cm_fib->Header.SenderData = 0;
111935863739SMike Smith 
112035863739SMike Smith 	aac_enqueue_free(cm);
112135863739SMike Smith }
112235863739SMike Smith 
1123914da7d0SScott Long /*
11240b94a66eSMike Smith  * Map helper for command/FIB allocation.
112535863739SMike Smith  */
112635863739SMike Smith static void
11270b94a66eSMike Smith aac_map_command_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error)
112835863739SMike Smith {
11298480cc63SScott Long 	uint32_t	*fibphys;
1130914da7d0SScott Long 
11318480cc63SScott Long 	fibphys = (uint32_t *)arg;
113235863739SMike Smith 
113335863739SMike Smith 	debug_called(3);
113435863739SMike Smith 
1135ffb37f33SScott Long 	*fibphys = segs[0].ds_addr;
113635863739SMike Smith }
113735863739SMike Smith 
1138914da7d0SScott Long /*
11390b94a66eSMike Smith  * Allocate and initialise commands/FIBs for this adapter.
114035863739SMike Smith  */
11410b94a66eSMike Smith static int
11420b94a66eSMike Smith aac_alloc_commands(struct aac_softc *sc)
114335863739SMike Smith {
114435863739SMike Smith 	struct aac_command *cm;
1145ffb37f33SScott Long 	struct aac_fibmap *fm;
11468480cc63SScott Long 	uint32_t fibphys;
1147ffb37f33SScott Long 	int i, error;
114835863739SMike Smith 
1149a6d35632SScott Long 	debug_called(2);
115035863739SMike Smith 
1151a6d35632SScott Long 	if (sc->total_fibs + AAC_FIB_COUNT > sc->aac_max_fibs)
1152ffb37f33SScott Long 		return (ENOMEM);
1153ffb37f33SScott Long 
11548480cc63SScott Long 	fm = malloc(sizeof(struct aac_fibmap), M_AACBUF, M_NOWAIT|M_ZERO);
1155a6d35632SScott Long 	if (fm == NULL)
1156a6d35632SScott Long 		return (ENOMEM);
1157ffb37f33SScott Long 
11580b94a66eSMike Smith 	/* allocate the FIBs in DMAable memory and load them */
1159ffb37f33SScott Long 	if (bus_dmamem_alloc(sc->aac_fib_dmat, (void **)&fm->aac_fibs,
1160ffb37f33SScott Long 			     BUS_DMA_NOWAIT, &fm->aac_fibmap)) {
116170545d1aSScott Long 		device_printf(sc->aac_dev,
116270545d1aSScott Long 			      "Not enough contiguous memory available.\n");
11638480cc63SScott Long 		free(fm, M_AACBUF);
11640b94a66eSMike Smith 		return (ENOMEM);
116535863739SMike Smith 	}
1166128aa5a0SScott Long 
1167cd481291SScott Long 	/* Ignore errors since this doesn't bounce */
1168cd481291SScott Long 	(void)bus_dmamap_load(sc->aac_fib_dmat, fm->aac_fibmap, fm->aac_fibs,
1169ffb37f33SScott Long 			      AAC_FIB_COUNT * sizeof(struct aac_fib),
1170ffb37f33SScott Long 			      aac_map_command_helper, &fibphys, 0);
1171128aa5a0SScott Long 
11720b94a66eSMike Smith 	/* initialise constant fields in the command structure */
1173ffb37f33SScott Long 	bzero(fm->aac_fibs, AAC_FIB_COUNT * sizeof(struct aac_fib));
11740b94a66eSMike Smith 	for (i = 0; i < AAC_FIB_COUNT; i++) {
11758480cc63SScott Long 		cm = sc->aac_commands + sc->total_fibs;
1176ffb37f33SScott Long 		fm->aac_commands = cm;
117735863739SMike Smith 		cm->cm_sc = sc;
1178ffb37f33SScott Long 		cm->cm_fib = fm->aac_fibs + i;
11798480cc63SScott Long 		cm->cm_fibphys = fibphys + (i * sizeof(struct aac_fib));
1180cb0d64b9SScott Long 		cm->cm_index = sc->total_fibs;
118135863739SMike Smith 
1182ffb37f33SScott Long 		if ((error = bus_dmamap_create(sc->aac_buffer_dmat, 0,
1183ffb37f33SScott Long 					       &cm->cm_datamap)) == 0)
118435863739SMike Smith 			aac_release_command(cm);
1185ffb37f33SScott Long 		else
11868480cc63SScott Long 			break;
11878480cc63SScott Long 		sc->total_fibs++;
118835863739SMike Smith 	}
1189ffb37f33SScott Long 
11908480cc63SScott Long 	if (i > 0) {
1191ffb37f33SScott Long 		TAILQ_INSERT_TAIL(&sc->aac_fibmap_tqh, fm, fm_link);
1192a6d35632SScott Long 		debug(1, "total_fibs= %d\n", sc->total_fibs);
11930b94a66eSMike Smith 		return (0);
119435863739SMike Smith 	}
119535863739SMike Smith 
11968480cc63SScott Long 	bus_dmamap_unload(sc->aac_fib_dmat, fm->aac_fibmap);
11978480cc63SScott Long 	bus_dmamem_free(sc->aac_fib_dmat, fm->aac_fibs, fm->aac_fibmap);
11988480cc63SScott Long 	free(fm, M_AACBUF);
11998480cc63SScott Long 	return (ENOMEM);
12008480cc63SScott Long }
12018480cc63SScott Long 
1202914da7d0SScott Long /*
12030b94a66eSMike Smith  * Free FIBs owned by this adapter.
120435863739SMike Smith  */
120535863739SMike Smith static void
12068480cc63SScott Long aac_free_commands(struct aac_softc *sc)
120735863739SMike Smith {
12088480cc63SScott Long 	struct aac_fibmap *fm;
1209ffb37f33SScott Long 	struct aac_command *cm;
121035863739SMike Smith 	int i;
121135863739SMike Smith 
121235863739SMike Smith 	debug_called(1);
121335863739SMike Smith 
12148480cc63SScott Long 	while ((fm = TAILQ_FIRST(&sc->aac_fibmap_tqh)) != NULL) {
12158480cc63SScott Long 
12168480cc63SScott Long 		TAILQ_REMOVE(&sc->aac_fibmap_tqh, fm, fm_link);
12178480cc63SScott Long 		/*
12188480cc63SScott Long 		 * We check against total_fibs to handle partially
12198480cc63SScott Long 		 * allocated blocks.
12208480cc63SScott Long 		 */
12218480cc63SScott Long 		for (i = 0; i < AAC_FIB_COUNT && sc->total_fibs--; i++) {
1222ffb37f33SScott Long 			cm = fm->aac_commands + i;
1223ffb37f33SScott Long 			bus_dmamap_destroy(sc->aac_buffer_dmat, cm->cm_datamap);
1224ffb37f33SScott Long 		}
1225ffb37f33SScott Long 		bus_dmamap_unload(sc->aac_fib_dmat, fm->aac_fibmap);
1226ffb37f33SScott Long 		bus_dmamem_free(sc->aac_fib_dmat, fm->aac_fibs, fm->aac_fibmap);
12278480cc63SScott Long 		free(fm, M_AACBUF);
12288480cc63SScott Long 	}
122935863739SMike Smith }
123035863739SMike Smith 
1231914da7d0SScott Long /*
123235863739SMike Smith  * Command-mapping helper function - populate this command's s/g table.
123335863739SMike Smith  */
123435863739SMike Smith static void
123535863739SMike Smith aac_map_command_sg(void *arg, bus_dma_segment_t *segs, int nseg, int error)
123635863739SMike Smith {
1237cd481291SScott Long 	struct aac_softc *sc;
1238914da7d0SScott Long 	struct aac_command *cm;
1239914da7d0SScott Long 	struct aac_fib *fib;
124035863739SMike Smith 	int i;
124135863739SMike Smith 
124235863739SMike Smith 	debug_called(3);
124335863739SMike Smith 
1244914da7d0SScott Long 	cm = (struct aac_command *)arg;
1245cd481291SScott Long 	sc = cm->cm_sc;
1246914da7d0SScott Long 	fib = cm->cm_fib;
1247914da7d0SScott Long 
124835863739SMike Smith 	/* copy into the FIB */
1249b85f5808SScott Long 	if (cm->cm_sgtable != NULL) {
1250b85f5808SScott Long 		if ((cm->cm_sc->flags & AAC_FLAGS_SG_64BIT) == 0) {
1251b85f5808SScott Long 			struct aac_sg_table *sg;
1252b85f5808SScott Long 			sg = cm->cm_sgtable;
125335863739SMike Smith 			sg->SgCount = nseg;
125435863739SMike Smith 			for (i = 0; i < nseg; i++) {
125535863739SMike Smith 				sg->SgEntry[i].SgAddress = segs[i].ds_addr;
125635863739SMike Smith 				sg->SgEntry[i].SgByteCount = segs[i].ds_len;
125735863739SMike Smith 			}
125835863739SMike Smith 			/* update the FIB size for the s/g count */
125935863739SMike Smith 			fib->Header.Size += nseg * sizeof(struct aac_sg_entry);
1260b85f5808SScott Long 		} else {
1261b85f5808SScott Long 			struct aac_sg_table64 *sg;
1262b85f5808SScott Long 			sg = (struct aac_sg_table64 *)cm->cm_sgtable;
1263b85f5808SScott Long 			sg->SgCount = nseg;
1264b85f5808SScott Long 			for (i = 0; i < nseg; i++) {
1265b85f5808SScott Long 				sg->SgEntry64[i].SgAddress = segs[i].ds_addr;
1266b85f5808SScott Long 				sg->SgEntry64[i].SgByteCount = segs[i].ds_len;
126735863739SMike Smith 			}
1268b85f5808SScott Long 			/* update the FIB size for the s/g count */
1269b85f5808SScott Long 			fib->Header.Size += nseg*sizeof(struct aac_sg_entry64);
1270b85f5808SScott Long 		}
1271b85f5808SScott Long 	}
127235863739SMike Smith 
1273cd481291SScott Long 	/* Fix up the address values in the FIB.  Use the command array index
1274cd481291SScott Long 	 * instead of a pointer since these fields are only 32 bits.  Shift
1275cd481291SScott Long 	 * the SenderFibAddress over to make room for the fast response bit.
127635863739SMike Smith 	 */
1277cd481291SScott Long 	cm->cm_fib->Header.SenderFibAddress = (cm->cm_index << 1);
1278cd481291SScott Long 	cm->cm_fib->Header.ReceiverFibAddress = cm->cm_fibphys;
127935863739SMike Smith 
1280cd481291SScott Long 	/* save a pointer to the command for speedy reverse-lookup */
1281cd481291SScott Long 	cm->cm_fib->Header.SenderData = cm->cm_index;
128235863739SMike Smith 
128335863739SMike Smith 	if (cm->cm_flags & AAC_CMD_DATAIN)
1284c6eafcf2SScott Long 		bus_dmamap_sync(sc->aac_buffer_dmat, cm->cm_datamap,
1285c6eafcf2SScott Long 				BUS_DMASYNC_PREREAD);
128635863739SMike Smith 	if (cm->cm_flags & AAC_CMD_DATAOUT)
1287c6eafcf2SScott Long 		bus_dmamap_sync(sc->aac_buffer_dmat, cm->cm_datamap,
1288c6eafcf2SScott Long 				BUS_DMASYNC_PREWRITE);
128935863739SMike Smith 	cm->cm_flags |= AAC_CMD_MAPPED;
1290cd481291SScott Long 
1291cd481291SScott Long 	/* put the FIB on the outbound queue */
1292cd481291SScott Long 	if (aac_enqueue_fib(sc, cm->cm_queue, cm) == EBUSY)
1293cd481291SScott Long 		aac_requeue_ready(cm);
1294cd481291SScott Long 
1295cd481291SScott Long 	return;
129635863739SMike Smith }
129735863739SMike Smith 
1298914da7d0SScott Long /*
129935863739SMike Smith  * Unmap a command from controller-visible space.
130035863739SMike Smith  */
130135863739SMike Smith static void
130235863739SMike Smith aac_unmap_command(struct aac_command *cm)
130335863739SMike Smith {
1304914da7d0SScott Long 	struct aac_softc *sc;
130535863739SMike Smith 
130635863739SMike Smith 	debug_called(2);
130735863739SMike Smith 
1308914da7d0SScott Long 	sc = cm->cm_sc;
1309914da7d0SScott Long 
131035863739SMike Smith 	if (!(cm->cm_flags & AAC_CMD_MAPPED))
131135863739SMike Smith 		return;
131235863739SMike Smith 
131335863739SMike Smith 	if (cm->cm_datalen != 0) {
131435863739SMike Smith 		if (cm->cm_flags & AAC_CMD_DATAIN)
1315c6eafcf2SScott Long 			bus_dmamap_sync(sc->aac_buffer_dmat, cm->cm_datamap,
1316c6eafcf2SScott Long 					BUS_DMASYNC_POSTREAD);
131735863739SMike Smith 		if (cm->cm_flags & AAC_CMD_DATAOUT)
1318c6eafcf2SScott Long 			bus_dmamap_sync(sc->aac_buffer_dmat, cm->cm_datamap,
1319c6eafcf2SScott Long 					BUS_DMASYNC_POSTWRITE);
132035863739SMike Smith 
132135863739SMike Smith 		bus_dmamap_unload(sc->aac_buffer_dmat, cm->cm_datamap);
132235863739SMike Smith 	}
132335863739SMike Smith 	cm->cm_flags &= ~AAC_CMD_MAPPED;
132435863739SMike Smith }
132535863739SMike Smith 
1326914da7d0SScott Long /*
1327914da7d0SScott Long  * Hardware Interface
1328914da7d0SScott Long  */
132935863739SMike Smith 
1330914da7d0SScott Long /*
133135863739SMike Smith  * Initialise the adapter.
133235863739SMike Smith  */
133335863739SMike Smith static void
133435863739SMike Smith aac_common_map(void *arg, bus_dma_segment_t *segs, int nseg, int error)
133535863739SMike Smith {
1336914da7d0SScott Long 	struct aac_softc *sc;
133735863739SMike Smith 
133835863739SMike Smith 	debug_called(1);
133935863739SMike Smith 
1340914da7d0SScott Long 	sc = (struct aac_softc *)arg;
1341914da7d0SScott Long 
134235863739SMike Smith 	sc->aac_common_busaddr = segs[0].ds_addr;
134335863739SMike Smith }
134435863739SMike Smith 
1345a6d35632SScott Long static int
1346a6d35632SScott Long aac_check_firmware(struct aac_softc *sc)
1347a6d35632SScott Long {
1348a6d35632SScott Long 	u_int32_t major, minor, options;
1349a6d35632SScott Long 
1350a6d35632SScott Long 	debug_called(1);
1351a6d35632SScott Long 
1352fe94b852SScott Long 	/*
1353fe94b852SScott Long 	 * Retrieve the firmware version numbers.  Dell PERC2/QC cards with
1354fe94b852SScott Long 	 * firmware version 1.x are not compatible with this driver.
1355fe94b852SScott Long 	 */
1356a6d35632SScott Long 	if (sc->flags & AAC_FLAGS_PERC2QC) {
1357fe94b852SScott Long 		if (aac_sync_command(sc, AAC_MONKER_GETKERNVER, 0, 0, 0, 0,
1358fe94b852SScott Long 				     NULL)) {
1359fe94b852SScott Long 			device_printf(sc->aac_dev,
1360fe94b852SScott Long 				      "Error reading firmware version\n");
1361fe94b852SScott Long 			return (EIO);
1362fe94b852SScott Long 		}
1363fe94b852SScott Long 
1364fe94b852SScott Long 		/* These numbers are stored as ASCII! */
1365a6d35632SScott Long 		major = (AAC_GET_MAILBOX(sc, 1) & 0xff) - 0x30;
1366a6d35632SScott Long 		minor = (AAC_GET_MAILBOX(sc, 2) & 0xff) - 0x30;
1367fe94b852SScott Long 		if (major == 1) {
1368fe94b852SScott Long 			device_printf(sc->aac_dev,
1369fe94b852SScott Long 			    "Firmware version %d.%d is not supported.\n",
1370fe94b852SScott Long 			    major, minor);
1371fe94b852SScott Long 			return (EINVAL);
1372fe94b852SScott Long 		}
1373fe94b852SScott Long 	}
1374fe94b852SScott Long 
1375a6d35632SScott Long 	/*
1376a6d35632SScott Long 	 * Retrieve the capabilities/supported options word so we know what
1377a6d35632SScott Long 	 * work-arounds to enable.
1378a6d35632SScott Long 	 */
1379a6d35632SScott Long 	if (aac_sync_command(sc, AAC_MONKER_GETINFO, 0, 0, 0, 0, NULL)) {
1380a6d35632SScott Long 		device_printf(sc->aac_dev, "RequestAdapterInfo failed\n");
1381a6d35632SScott Long 		return (EIO);
1382a6d35632SScott Long 	}
1383a6d35632SScott Long 	options = AAC_GET_MAILBOX(sc, 1);
1384a6d35632SScott Long 	sc->supported_options = options;
1385a6d35632SScott Long 
1386a6d35632SScott Long 	if ((options & AAC_SUPPORTED_4GB_WINDOW) != 0 &&
1387a6d35632SScott Long 	    (sc->flags & AAC_FLAGS_NO4GB) == 0)
1388a6d35632SScott Long 		sc->flags |= AAC_FLAGS_4GB_WINDOW;
1389a6d35632SScott Long 	if (options & AAC_SUPPORTED_NONDASD)
1390a6d35632SScott Long 		sc->flags |= AAC_FLAGS_ENABLE_CAM;
1391cd481291SScott Long 	if ((options & AAC_SUPPORTED_SGMAP_HOST64) != 0
1392cd481291SScott Long 	     && (sizeof(bus_addr_t) > 4)) {
1393a6d35632SScott Long 		device_printf(sc->aac_dev, "Enabling 64-bit address support\n");
1394a6d35632SScott Long 		sc->flags |= AAC_FLAGS_SG_64BIT;
1395a6d35632SScott Long 	}
1396a6d35632SScott Long 
1397a6d35632SScott Long 	/* Check for broken hardware that does a lower number of commands */
1398a6d35632SScott Long 	if ((sc->flags & AAC_FLAGS_256FIBS) == 0)
1399a6d35632SScott Long 		sc->aac_max_fibs = AAC_MAX_FIBS;
1400a6d35632SScott Long 	else
1401a6d35632SScott Long 		sc->aac_max_fibs = 256;
1402a6d35632SScott Long 
1403fe94b852SScott Long 	return (0);
1404fe94b852SScott Long }
1405fe94b852SScott Long 
140635863739SMike Smith static int
140735863739SMike Smith aac_init(struct aac_softc *sc)
140835863739SMike Smith {
140935863739SMike Smith 	struct aac_adapter_init	*ip;
141035863739SMike Smith 	time_t then;
1411b88ffdc8SScott Long 	u_int32_t code, qoffset;
1412a6d35632SScott Long 	int error;
141335863739SMike Smith 
141435863739SMike Smith 	debug_called(1);
141535863739SMike Smith 
141635863739SMike Smith 	/*
141735863739SMike Smith 	 * First wait for the adapter to come ready.
141835863739SMike Smith 	 */
141935863739SMike Smith 	then = time_second;
142035863739SMike Smith 	do {
142135863739SMike Smith 		code = AAC_GET_FWSTATUS(sc);
142235863739SMike Smith 		if (code & AAC_SELF_TEST_FAILED) {
142335863739SMike Smith 			device_printf(sc->aac_dev, "FATAL: selftest failed\n");
142435863739SMike Smith 			return(ENXIO);
142535863739SMike Smith 		}
142635863739SMike Smith 		if (code & AAC_KERNEL_PANIC) {
1427914da7d0SScott Long 			device_printf(sc->aac_dev,
1428914da7d0SScott Long 				      "FATAL: controller kernel panic\n");
142935863739SMike Smith 			return(ENXIO);
143035863739SMike Smith 		}
143135863739SMike Smith 		if (time_second > (then + AAC_BOOT_TIMEOUT)) {
1432914da7d0SScott Long 			device_printf(sc->aac_dev,
1433914da7d0SScott Long 				      "FATAL: controller not coming ready, "
1434c6eafcf2SScott Long 					   "status %x\n", code);
143535863739SMike Smith 			return(ENXIO);
143635863739SMike Smith 		}
143735863739SMike Smith 	} while (!(code & AAC_UP_AND_RUNNING));
143835863739SMike Smith 
1439a6d35632SScott Long 	error = ENOMEM;
1440a6d35632SScott Long 	/*
1441a6d35632SScott Long 	 * Create DMA tag for mapping buffers into controller-addressable space.
1442a6d35632SScott Long 	 */
1443a6d35632SScott Long 	if (bus_dma_tag_create(sc->aac_parent_dmat, 	/* parent */
1444a6d35632SScott Long 			       1, 0, 			/* algnmnt, boundary */
1445a6d35632SScott Long 			       (sc->flags & AAC_FLAGS_SG_64BIT) ?
1446a6d35632SScott Long 			       BUS_SPACE_MAXADDR :
1447a6d35632SScott Long 			       BUS_SPACE_MAXADDR_32BIT,	/* lowaddr */
1448a6d35632SScott Long 			       BUS_SPACE_MAXADDR, 	/* highaddr */
1449a6d35632SScott Long 			       NULL, NULL, 		/* filter, filterarg */
1450a6d35632SScott Long 			       MAXBSIZE,		/* maxsize */
1451a6d35632SScott Long 			       AAC_MAXSGENTRIES,	/* nsegments */
1452a6d35632SScott Long 			       MAXBSIZE,		/* maxsegsize */
1453a6d35632SScott Long 			       BUS_DMA_ALLOCNOW,	/* flags */
1454f6b1c44dSScott Long 			       busdma_lock_mutex,	/* lockfunc */
1455f6b1c44dSScott Long 			       &sc->aac_io_lock,	/* lockfuncarg */
1456a6d35632SScott Long 			       &sc->aac_buffer_dmat)) {
1457a6d35632SScott Long 		device_printf(sc->aac_dev, "can't allocate buffer DMA tag\n");
1458a6d35632SScott Long 		goto out;
1459a6d35632SScott Long 	}
1460a6d35632SScott Long 
1461a6d35632SScott Long 	/*
1462a6d35632SScott Long 	 * Create DMA tag for mapping FIBs into controller-addressable space..
1463a6d35632SScott Long 	 */
1464a6d35632SScott Long 	if (bus_dma_tag_create(sc->aac_parent_dmat,	/* parent */
1465a6d35632SScott Long 			       1, 0, 			/* algnmnt, boundary */
1466a6d35632SScott Long 			       (sc->flags & AAC_FLAGS_4GB_WINDOW) ?
1467a6d35632SScott Long 			       BUS_SPACE_MAXADDR_32BIT :
1468a6d35632SScott Long 			       0x7fffffff,		/* lowaddr */
1469a6d35632SScott Long 			       BUS_SPACE_MAXADDR, 	/* highaddr */
1470a6d35632SScott Long 			       NULL, NULL, 		/* filter, filterarg */
1471a6d35632SScott Long 			       AAC_FIB_COUNT *
1472a6d35632SScott Long 			       sizeof(struct aac_fib),  /* maxsize */
1473a6d35632SScott Long 			       1,			/* nsegments */
1474a6d35632SScott Long 			       AAC_FIB_COUNT *
1475a6d35632SScott Long 			       sizeof(struct aac_fib),	/* maxsegsize */
1476a6d35632SScott Long 			       BUS_DMA_ALLOCNOW,	/* flags */
1477f6b1c44dSScott Long 			       NULL, NULL,		/* No locking needed */
1478a6d35632SScott Long 			       &sc->aac_fib_dmat)) {
1479a6d35632SScott Long 		device_printf(sc->aac_dev, "can't allocate FIB DMA tag\n");;
1480a6d35632SScott Long 		goto out;
1481a6d35632SScott Long 	}
1482a6d35632SScott Long 
148335863739SMike Smith 	/*
148435863739SMike Smith 	 * Create DMA tag for the common structure and allocate it.
148535863739SMike Smith 	 */
148635863739SMike Smith 	if (bus_dma_tag_create(sc->aac_parent_dmat, 	/* parent */
1487c6eafcf2SScott Long 			       1, 0,			/* algnmnt, boundary */
1488a6d35632SScott Long 			       (sc->flags & AAC_FLAGS_4GB_WINDOW) ?
1489a6d35632SScott Long 			       BUS_SPACE_MAXADDR_32BIT :
1490a6d35632SScott Long 			       0x7fffffff,		/* lowaddr */
149135863739SMike Smith 			       BUS_SPACE_MAXADDR, 	/* highaddr */
149235863739SMike Smith 			       NULL, NULL, 		/* filter, filterarg */
1493ffb37f33SScott Long 			       8192 + sizeof(struct aac_common), /* maxsize */
1494914da7d0SScott Long 			       1,			/* nsegments */
149535863739SMike Smith 			       BUS_SPACE_MAXSIZE_32BIT,	/* maxsegsize */
1496a6d35632SScott Long 			       BUS_DMA_ALLOCNOW,	/* flags */
1497f6b1c44dSScott Long 			       NULL, NULL,		/* No locking needed */
149835863739SMike Smith 			       &sc->aac_common_dmat)) {
1499914da7d0SScott Long 		device_printf(sc->aac_dev,
1500914da7d0SScott Long 			      "can't allocate common structure DMA tag\n");
1501a6d35632SScott Long 		goto out;
150235863739SMike Smith 	}
1503c6eafcf2SScott Long 	if (bus_dmamem_alloc(sc->aac_common_dmat, (void **)&sc->aac_common,
1504c6eafcf2SScott Long 			     BUS_DMA_NOWAIT, &sc->aac_common_dmamap)) {
150535863739SMike Smith 		device_printf(sc->aac_dev, "can't allocate common structure\n");
1506a6d35632SScott Long 		goto out;
150735863739SMike Smith 	}
1508ffb37f33SScott Long 
1509ffb37f33SScott Long 	/*
1510ffb37f33SScott Long 	 * Work around a bug in the 2120 and 2200 that cannot DMA commands
1511ffb37f33SScott Long 	 * below address 8192 in physical memory.
1512ffb37f33SScott Long 	 * XXX If the padding is not needed, can it be put to use instead
1513ffb37f33SScott Long 	 * of ignored?
1514ffb37f33SScott Long 	 */
1515cd481291SScott Long 	(void)bus_dmamap_load(sc->aac_common_dmat, sc->aac_common_dmamap,
1516ffb37f33SScott Long 			sc->aac_common, 8192 + sizeof(*sc->aac_common),
1517ffb37f33SScott Long 			aac_common_map, sc, 0);
1518ffb37f33SScott Long 
1519ffb37f33SScott Long 	if (sc->aac_common_busaddr < 8192) {
1520ffb37f33SScott Long 		(uint8_t *)sc->aac_common += 8192;
1521ffb37f33SScott Long 		sc->aac_common_busaddr += 8192;
1522ffb37f33SScott Long 	}
152335863739SMike Smith 	bzero(sc->aac_common, sizeof(*sc->aac_common));
152435863739SMike Smith 
1525ffb37f33SScott Long 	/* Allocate some FIBs and associated command structs */
1526ffb37f33SScott Long 	TAILQ_INIT(&sc->aac_fibmap_tqh);
1527ffb37f33SScott Long 	sc->aac_commands = malloc(AAC_MAX_FIBS * sizeof(struct aac_command),
15288480cc63SScott Long 				  M_AACBUF, M_WAITOK|M_ZERO);
15298480cc63SScott Long 	while (sc->total_fibs < AAC_PREALLOCATE_FIBS) {
1530ffb37f33SScott Long 		if (aac_alloc_commands(sc) != 0)
1531ffb37f33SScott Long 			break;
1532ffb37f33SScott Long 	}
1533ffb37f33SScott Long 	if (sc->total_fibs == 0)
1534a6d35632SScott Long 		goto out;
1535ffb37f33SScott Long 
153635863739SMike Smith 	/*
1537914da7d0SScott Long 	 * Fill in the init structure.  This tells the adapter about the
1538914da7d0SScott Long 	 * physical location of various important shared data structures.
153935863739SMike Smith 	 */
154035863739SMike Smith 	ip = &sc->aac_common->ac_init;
154135863739SMike Smith 	ip->InitStructRevision = AAC_INIT_STRUCT_REVISION;
1542f30ac74cSScott Long 	ip->MiniPortRevision = AAC_INIT_STRUCT_MINIPORT_REVISION;
154335863739SMike Smith 
1544c6eafcf2SScott Long 	ip->AdapterFibsPhysicalAddress = sc->aac_common_busaddr +
1545c6eafcf2SScott Long 					 offsetof(struct aac_common, ac_fibs);
1546149af931SScott Long 	ip->AdapterFibsVirtualAddress = 0;
154735863739SMike Smith 	ip->AdapterFibsSize = AAC_ADAPTER_FIBS * sizeof(struct aac_fib);
154835863739SMike Smith 	ip->AdapterFibAlign = sizeof(struct aac_fib);
154935863739SMike Smith 
1550c6eafcf2SScott Long 	ip->PrintfBufferAddress = sc->aac_common_busaddr +
1551c6eafcf2SScott Long 				  offsetof(struct aac_common, ac_printf);
155235863739SMike Smith 	ip->PrintfBufferSize = AAC_PRINTF_BUFSIZE;
155335863739SMike Smith 
15544b00f859SScott Long 	/*
15554b00f859SScott Long 	 * The adapter assumes that pages are 4K in size, except on some
15564b00f859SScott Long  	 * broken firmware versions that do the page->byte conversion twice,
15574b00f859SScott Long 	 * therefore 'assuming' that this value is in 16MB units (2^24).
15584b00f859SScott Long 	 * Round up since the granularity is so high.
15594b00f859SScott Long 	 */
1560f30ac74cSScott Long 	ip->HostPhysMemPages = ctob(physmem) / AAC_PAGE_SIZE;
15614b00f859SScott Long 	if (sc->flags & AAC_FLAGS_BROKEN_MEMMAP) {
15624b00f859SScott Long 		ip->HostPhysMemPages =
15634b00f859SScott Long 		    (ip->HostPhysMemPages + AAC_PAGE_SIZE) / AAC_PAGE_SIZE;
1564204c0befSScott Long 	}
156535863739SMike Smith 	ip->HostElapsedSeconds = time_second;	/* reset later if invalid */
156635863739SMike Smith 
156735863739SMike Smith 	/*
1568c6eafcf2SScott Long 	 * Initialise FIB queues.  Note that it appears that the layout of the
1569c6eafcf2SScott Long 	 * indexes and the segmentation of the entries may be mandated by the
1570c6eafcf2SScott Long 	 * adapter, which is only told about the base of the queue index fields.
157135863739SMike Smith 	 *
157235863739SMike Smith 	 * The initial values of the indices are assumed to inform the adapter
1573914da7d0SScott Long 	 * of the sizes of the respective queues, and theoretically it could
1574914da7d0SScott Long 	 * work out the entire layout of the queue structures from this.  We
1575914da7d0SScott Long 	 * take the easy route and just lay this area out like everyone else
1576914da7d0SScott Long 	 * does.
157735863739SMike Smith 	 *
1578914da7d0SScott Long 	 * The Linux driver uses a much more complex scheme whereby several
1579914da7d0SScott Long 	 * header records are kept for each queue.  We use a couple of generic
1580914da7d0SScott Long 	 * list manipulation functions which 'know' the size of each list by
1581914da7d0SScott Long 	 * virtue of a table.
158235863739SMike Smith 	 */
1583b88ffdc8SScott Long 	qoffset = offsetof(struct aac_common, ac_qbuf) + AAC_QUEUE_ALIGN;
1584b88ffdc8SScott Long 	qoffset &= (AAC_QUEUE_ALIGN - 1);
1585b88ffdc8SScott Long 	sc->aac_queues = (struct aac_queue_table *)((uintptr_t)sc->aac_common + qoffset);
1586b88ffdc8SScott Long 	ip->CommHeaderAddress = sc->aac_common_busaddr + qoffset;
158735863739SMike Smith 
1588c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_NORM_CMD_QUEUE][AAC_PRODUCER_INDEX] =
1589c6eafcf2SScott Long 		AAC_HOST_NORM_CMD_ENTRIES;
1590c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_NORM_CMD_QUEUE][AAC_CONSUMER_INDEX] =
1591c6eafcf2SScott Long 		AAC_HOST_NORM_CMD_ENTRIES;
1592c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_HIGH_CMD_QUEUE][AAC_PRODUCER_INDEX] =
1593c6eafcf2SScott Long 		AAC_HOST_HIGH_CMD_ENTRIES;
1594c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_HIGH_CMD_QUEUE][AAC_CONSUMER_INDEX] =
1595c6eafcf2SScott Long 		AAC_HOST_HIGH_CMD_ENTRIES;
1596c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_NORM_CMD_QUEUE][AAC_PRODUCER_INDEX] =
1597c6eafcf2SScott Long 		AAC_ADAP_NORM_CMD_ENTRIES;
1598c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_NORM_CMD_QUEUE][AAC_CONSUMER_INDEX] =
1599c6eafcf2SScott Long 		AAC_ADAP_NORM_CMD_ENTRIES;
1600c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_HIGH_CMD_QUEUE][AAC_PRODUCER_INDEX] =
1601c6eafcf2SScott Long 		AAC_ADAP_HIGH_CMD_ENTRIES;
1602c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_HIGH_CMD_QUEUE][AAC_CONSUMER_INDEX] =
1603c6eafcf2SScott Long 		AAC_ADAP_HIGH_CMD_ENTRIES;
1604c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_NORM_RESP_QUEUE][AAC_PRODUCER_INDEX]=
1605c6eafcf2SScott Long 		AAC_HOST_NORM_RESP_ENTRIES;
1606c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_NORM_RESP_QUEUE][AAC_CONSUMER_INDEX]=
1607c6eafcf2SScott Long 		AAC_HOST_NORM_RESP_ENTRIES;
1608c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_HIGH_RESP_QUEUE][AAC_PRODUCER_INDEX]=
1609c6eafcf2SScott Long 		AAC_HOST_HIGH_RESP_ENTRIES;
1610c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_HIGH_RESP_QUEUE][AAC_CONSUMER_INDEX]=
1611c6eafcf2SScott Long 		AAC_HOST_HIGH_RESP_ENTRIES;
1612c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_NORM_RESP_QUEUE][AAC_PRODUCER_INDEX]=
1613c6eafcf2SScott Long 		AAC_ADAP_NORM_RESP_ENTRIES;
1614c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_NORM_RESP_QUEUE][AAC_CONSUMER_INDEX]=
1615c6eafcf2SScott Long 		AAC_ADAP_NORM_RESP_ENTRIES;
1616c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_HIGH_RESP_QUEUE][AAC_PRODUCER_INDEX]=
1617c6eafcf2SScott Long 		AAC_ADAP_HIGH_RESP_ENTRIES;
1618c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_HIGH_RESP_QUEUE][AAC_CONSUMER_INDEX]=
1619c6eafcf2SScott Long 		AAC_ADAP_HIGH_RESP_ENTRIES;
1620c6eafcf2SScott Long 	sc->aac_qentries[AAC_HOST_NORM_CMD_QUEUE] =
1621c6eafcf2SScott Long 		&sc->aac_queues->qt_HostNormCmdQueue[0];
1622c6eafcf2SScott Long 	sc->aac_qentries[AAC_HOST_HIGH_CMD_QUEUE] =
1623c6eafcf2SScott Long 		&sc->aac_queues->qt_HostHighCmdQueue[0];
1624c6eafcf2SScott Long 	sc->aac_qentries[AAC_ADAP_NORM_CMD_QUEUE] =
1625c6eafcf2SScott Long 		&sc->aac_queues->qt_AdapNormCmdQueue[0];
1626c6eafcf2SScott Long 	sc->aac_qentries[AAC_ADAP_HIGH_CMD_QUEUE] =
1627c6eafcf2SScott Long 		&sc->aac_queues->qt_AdapHighCmdQueue[0];
1628c6eafcf2SScott Long 	sc->aac_qentries[AAC_HOST_NORM_RESP_QUEUE] =
1629c6eafcf2SScott Long 		&sc->aac_queues->qt_HostNormRespQueue[0];
1630c6eafcf2SScott Long 	sc->aac_qentries[AAC_HOST_HIGH_RESP_QUEUE] =
1631c6eafcf2SScott Long 		&sc->aac_queues->qt_HostHighRespQueue[0];
1632c6eafcf2SScott Long 	sc->aac_qentries[AAC_ADAP_NORM_RESP_QUEUE] =
1633c6eafcf2SScott Long 		&sc->aac_queues->qt_AdapNormRespQueue[0];
1634c6eafcf2SScott Long 	sc->aac_qentries[AAC_ADAP_HIGH_RESP_QUEUE] =
1635c6eafcf2SScott Long 		&sc->aac_queues->qt_AdapHighRespQueue[0];
163635863739SMike Smith 
163735863739SMike Smith 	/*
163835863739SMike Smith 	 * Do controller-type-specific initialisation
163935863739SMike Smith 	 */
164035863739SMike Smith 	switch (sc->aac_hwif) {
164135863739SMike Smith 	case AAC_HWIF_I960RX:
164235863739SMike Smith 		AAC_SETREG4(sc, AAC_RX_ODBR, ~0);
164335863739SMike Smith 		break;
164435863739SMike Smith 	}
164535863739SMike Smith 
164635863739SMike Smith 	/*
164735863739SMike Smith 	 * Give the init structure to the controller.
164835863739SMike Smith 	 */
164935863739SMike Smith 	if (aac_sync_command(sc, AAC_MONKER_INITSTRUCT,
1650914da7d0SScott Long 			     sc->aac_common_busaddr +
1651914da7d0SScott Long 			     offsetof(struct aac_common, ac_init), 0, 0, 0,
1652914da7d0SScott Long 			     NULL)) {
1653914da7d0SScott Long 		device_printf(sc->aac_dev,
1654914da7d0SScott Long 			      "error establishing init structure\n");
1655a6d35632SScott Long 		error = EIO;
1656a6d35632SScott Long 		goto out;
165735863739SMike Smith 	}
165835863739SMike Smith 
1659a6d35632SScott Long 	error = 0;
1660a6d35632SScott Long out:
1661a6d35632SScott Long 	return(error);
166235863739SMike Smith }
166335863739SMike Smith 
1664914da7d0SScott Long /*
166535863739SMike Smith  * Send a synchronous command to the controller and wait for a result.
166635863739SMike Smith  */
166735863739SMike Smith static int
166835863739SMike Smith aac_sync_command(struct aac_softc *sc, u_int32_t command,
166935863739SMike Smith 		 u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3,
167035863739SMike Smith 		 u_int32_t *sp)
167135863739SMike Smith {
167235863739SMike Smith 	time_t then;
167335863739SMike Smith 	u_int32_t status;
167435863739SMike Smith 
167535863739SMike Smith 	debug_called(3);
167635863739SMike Smith 
167735863739SMike Smith 	/* populate the mailbox */
167835863739SMike Smith 	AAC_SET_MAILBOX(sc, command, arg0, arg1, arg2, arg3);
167935863739SMike Smith 
168035863739SMike Smith 	/* ensure the sync command doorbell flag is cleared */
168135863739SMike Smith 	AAC_CLEAR_ISTATUS(sc, AAC_DB_SYNC_COMMAND);
168235863739SMike Smith 
168335863739SMike Smith 	/* then set it to signal the adapter */
168435863739SMike Smith 	AAC_QNOTIFY(sc, AAC_DB_SYNC_COMMAND);
168535863739SMike Smith 
168635863739SMike Smith 	/* spin waiting for the command to complete */
168735863739SMike Smith 	then = time_second;
168835863739SMike Smith 	do {
168935863739SMike Smith 		if (time_second > (then + AAC_IMMEDIATE_TIMEOUT)) {
1690a6d35632SScott Long 			debug(1, "timed out");
169135863739SMike Smith 			return(EIO);
169235863739SMike Smith 		}
169335863739SMike Smith 	} while (!(AAC_GET_ISTATUS(sc) & AAC_DB_SYNC_COMMAND));
169435863739SMike Smith 
169535863739SMike Smith 	/* clear the completion flag */
169635863739SMike Smith 	AAC_CLEAR_ISTATUS(sc, AAC_DB_SYNC_COMMAND);
169735863739SMike Smith 
169835863739SMike Smith 	/* get the command status */
1699a6d35632SScott Long 	status = AAC_GET_MAILBOX(sc, 0);
170035863739SMike Smith 	if (sp != NULL)
170135863739SMike Smith 		*sp = status;
17020b94a66eSMike Smith 	return(0);
170335863739SMike Smith }
170435863739SMike Smith 
1705914da7d0SScott Long /*
1706cbfd045bSScott Long  * Grab the sync fib area.
1707cbfd045bSScott Long  */
1708cbfd045bSScott Long int
1709fe3cb0e1SScott Long aac_alloc_sync_fib(struct aac_softc *sc, struct aac_fib **fib, int flags)
1710cbfd045bSScott Long {
1711cbfd045bSScott Long 
1712cbfd045bSScott Long 	/*
1713cbfd045bSScott Long 	 * If the force flag is set, the system is shutting down, or in
1714cbfd045bSScott Long 	 * trouble.  Ignore the mutex.
1715cbfd045bSScott Long 	 */
1716cbfd045bSScott Long 	if (!(flags & AAC_SYNC_LOCK_FORCE))
1717cbfd045bSScott Long 		AAC_LOCK_ACQUIRE(&sc->aac_sync_lock);
1718cbfd045bSScott Long 
1719cbfd045bSScott Long 	*fib = &sc->aac_common->ac_sync_fib;
1720cbfd045bSScott Long 
1721cbfd045bSScott Long 	return (1);
1722cbfd045bSScott Long }
1723cbfd045bSScott Long 
1724cbfd045bSScott Long /*
1725cbfd045bSScott Long  * Release the sync fib area.
1726cbfd045bSScott Long  */
1727cbfd045bSScott Long void
1728cbfd045bSScott Long aac_release_sync_fib(struct aac_softc *sc)
1729cbfd045bSScott Long {
1730cbfd045bSScott Long 
1731cbfd045bSScott Long 	AAC_LOCK_RELEASE(&sc->aac_sync_lock);
1732cbfd045bSScott Long }
1733cbfd045bSScott Long 
1734cbfd045bSScott Long /*
173535863739SMike Smith  * Send a synchronous FIB to the controller and wait for a result.
173635863739SMike Smith  */
1737cbfd045bSScott Long int
173835863739SMike Smith aac_sync_fib(struct aac_softc *sc, u_int32_t command, u_int32_t xferstate,
1739cbfd045bSScott Long 		 struct aac_fib *fib, u_int16_t datasize)
174035863739SMike Smith {
174135863739SMike Smith 	debug_called(3);
174235863739SMike Smith 
174335863739SMike Smith 	if (datasize > AAC_FIB_DATASIZE)
174435863739SMike Smith 		return(EINVAL);
174535863739SMike Smith 
174635863739SMike Smith 	/*
174735863739SMike Smith 	 * Set up the sync FIB
174835863739SMike Smith 	 */
1749914da7d0SScott Long 	fib->Header.XferState = AAC_FIBSTATE_HOSTOWNED |
1750914da7d0SScott Long 				AAC_FIBSTATE_INITIALISED |
1751c6eafcf2SScott Long 				AAC_FIBSTATE_EMPTY;
175235863739SMike Smith 	fib->Header.XferState |= xferstate;
175335863739SMike Smith 	fib->Header.Command = command;
175435863739SMike Smith 	fib->Header.StructType = AAC_FIBTYPE_TFIB;
175535863739SMike Smith 	fib->Header.Size = sizeof(struct aac_fib) + datasize;
175635863739SMike Smith 	fib->Header.SenderSize = sizeof(struct aac_fib);
1757b88ffdc8SScott Long 	fib->Header.SenderFibAddress = 0;	/* Not needed */
1758c6eafcf2SScott Long 	fib->Header.ReceiverFibAddress = sc->aac_common_busaddr +
1759914da7d0SScott Long 					 offsetof(struct aac_common,
1760914da7d0SScott Long 						  ac_sync_fib);
176135863739SMike Smith 
176235863739SMike Smith 	/*
176335863739SMike Smith 	 * Give the FIB to the controller, wait for a response.
176435863739SMike Smith 	 */
1765914da7d0SScott Long 	if (aac_sync_command(sc, AAC_MONKER_SYNCFIB,
1766914da7d0SScott Long 			     fib->Header.ReceiverFibAddress, 0, 0, 0, NULL)) {
176735863739SMike Smith 		debug(2, "IO error");
176835863739SMike Smith 		return(EIO);
176935863739SMike Smith 	}
177035863739SMike Smith 
177135863739SMike Smith 	return (0);
177235863739SMike Smith }
177335863739SMike Smith 
1774914da7d0SScott Long /*
177535863739SMike Smith  * Adapter-space FIB queue manipulation
177635863739SMike Smith  *
177735863739SMike Smith  * Note that the queue implementation here is a little funky; neither the PI or
177835863739SMike Smith  * CI will ever be zero.  This behaviour is a controller feature.
177935863739SMike Smith  */
178035863739SMike Smith static struct {
178135863739SMike Smith 	int		size;
178235863739SMike Smith 	int		notify;
178335863739SMike Smith } aac_qinfo[] = {
178435863739SMike Smith 	{AAC_HOST_NORM_CMD_ENTRIES, AAC_DB_COMMAND_NOT_FULL},
178535863739SMike Smith 	{AAC_HOST_HIGH_CMD_ENTRIES, 0},
178635863739SMike Smith 	{AAC_ADAP_NORM_CMD_ENTRIES, AAC_DB_COMMAND_READY},
178735863739SMike Smith 	{AAC_ADAP_HIGH_CMD_ENTRIES, 0},
178835863739SMike Smith 	{AAC_HOST_NORM_RESP_ENTRIES, AAC_DB_RESPONSE_NOT_FULL},
178935863739SMike Smith 	{AAC_HOST_HIGH_RESP_ENTRIES, 0},
179035863739SMike Smith 	{AAC_ADAP_NORM_RESP_ENTRIES, AAC_DB_RESPONSE_READY},
179135863739SMike Smith 	{AAC_ADAP_HIGH_RESP_ENTRIES, 0}
179235863739SMike Smith };
179335863739SMike Smith 
179435863739SMike Smith /*
1795c6eafcf2SScott Long  * Atomically insert an entry into the nominated queue, returns 0 on success or
1796c6eafcf2SScott Long  * EBUSY if the queue is full.
179735863739SMike Smith  *
17980b94a66eSMike Smith  * Note: it would be more efficient to defer notifying the controller in
1799914da7d0SScott Long  *	 the case where we may be inserting several entries in rapid succession,
1800914da7d0SScott Long  *	 but implementing this usefully may be difficult (it would involve a
1801c6eafcf2SScott Long  *	 separate queue/notify interface).
180235863739SMike Smith  */
180335863739SMike Smith static int
1804f6c4dd3fSScott Long aac_enqueue_fib(struct aac_softc *sc, int queue, struct aac_command *cm)
180535863739SMike Smith {
180635863739SMike Smith 	u_int32_t pi, ci;
18079e2e96d8SScott Long 	int error;
1808f6c4dd3fSScott Long 	u_int32_t fib_size;
1809f6c4dd3fSScott Long 	u_int32_t fib_addr;
1810f6c4dd3fSScott Long 
181136e0bf6eSScott Long 	debug_called(3);
181236e0bf6eSScott Long 
1813f6c4dd3fSScott Long 	fib_size = cm->cm_fib->Header.Size;
1814f6c4dd3fSScott Long 	fib_addr = cm->cm_fib->Header.ReceiverFibAddress;
181535863739SMike Smith 
181635863739SMike Smith 	/* get the producer/consumer indices */
181735863739SMike Smith 	pi = sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX];
181835863739SMike Smith 	ci = sc->aac_queues->qt_qindex[queue][AAC_CONSUMER_INDEX];
181935863739SMike Smith 
182035863739SMike Smith 	/* wrap the queue? */
182135863739SMike Smith 	if (pi >= aac_qinfo[queue].size)
182235863739SMike Smith 		pi = 0;
182335863739SMike Smith 
182435863739SMike Smith 	/* check for queue full */
182535863739SMike Smith 	if ((pi + 1) == ci) {
182635863739SMike Smith 		error = EBUSY;
182735863739SMike Smith 		goto out;
182835863739SMike Smith 	}
182935863739SMike Smith 
183035863739SMike Smith 	/* populate queue entry */
183135863739SMike Smith 	(sc->aac_qentries[queue] + pi)->aq_fib_size = fib_size;
183235863739SMike Smith 	(sc->aac_qentries[queue] + pi)->aq_fib_addr = fib_addr;
183335863739SMike Smith 
183435863739SMike Smith 	/* update producer index */
183535863739SMike Smith 	sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX] = pi + 1;
183635863739SMike Smith 
1837f6c4dd3fSScott Long 	/*
1838914da7d0SScott Long 	 * To avoid a race with its completion interrupt, place this command on
1839914da7d0SScott Long 	 * the busy queue prior to advertising it to the controller.
1840f6c4dd3fSScott Long 	 */
1841f6c4dd3fSScott Long 	aac_enqueue_busy(cm);
1842f6c4dd3fSScott Long 
184335863739SMike Smith 	/* notify the adapter if we know how */
184435863739SMike Smith 	if (aac_qinfo[queue].notify != 0)
184535863739SMike Smith 		AAC_QNOTIFY(sc, aac_qinfo[queue].notify);
184635863739SMike Smith 
184735863739SMike Smith 	error = 0;
184835863739SMike Smith 
184935863739SMike Smith out:
185035863739SMike Smith 	return(error);
185135863739SMike Smith }
185235863739SMike Smith 
185335863739SMike Smith /*
185436e0bf6eSScott Long  * Atomically remove one entry from the nominated queue, returns 0 on
185536e0bf6eSScott Long  * success or ENOENT if the queue is empty.
185635863739SMike Smith  */
185735863739SMike Smith static int
1858c6eafcf2SScott Long aac_dequeue_fib(struct aac_softc *sc, int queue, u_int32_t *fib_size,
1859c6eafcf2SScott Long 		struct aac_fib **fib_addr)
186035863739SMike Smith {
186135863739SMike Smith 	u_int32_t pi, ci;
1862149af931SScott Long 	u_int32_t fib_index;
18639e2e96d8SScott Long 	int error;
1864f6c4dd3fSScott Long 	int notify;
186535863739SMike Smith 
186635863739SMike Smith 	debug_called(3);
186735863739SMike Smith 
186835863739SMike Smith 	/* get the producer/consumer indices */
186935863739SMike Smith 	pi = sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX];
187035863739SMike Smith 	ci = sc->aac_queues->qt_qindex[queue][AAC_CONSUMER_INDEX];
187135863739SMike Smith 
187235863739SMike Smith 	/* check for queue empty */
187335863739SMike Smith 	if (ci == pi) {
187435863739SMike Smith 		error = ENOENT;
187535863739SMike Smith 		goto out;
187635863739SMike Smith 	}
187735863739SMike Smith 
1878f6c4dd3fSScott Long 	notify = 0;
1879f6c4dd3fSScott Long 	if (ci == pi + 1)
1880f6c4dd3fSScott Long 		notify++;
1881f6c4dd3fSScott Long 
188235863739SMike Smith 	/* wrap the queue? */
188335863739SMike Smith 	if (ci >= aac_qinfo[queue].size)
188435863739SMike Smith 		ci = 0;
188535863739SMike Smith 
188635863739SMike Smith 	/* fetch the entry */
188735863739SMike Smith 	*fib_size = (sc->aac_qentries[queue] + ci)->aq_fib_size;
1888149af931SScott Long 
1889149af931SScott Long 	switch (queue) {
1890149af931SScott Long 	case AAC_HOST_NORM_CMD_QUEUE:
1891149af931SScott Long 	case AAC_HOST_HIGH_CMD_QUEUE:
1892149af931SScott Long 		/*
1893149af931SScott Long 		 * The aq_fib_addr is only 32 bits wide so it can't be counted
1894149af931SScott Long 		 * on to hold an address.  For AIF's, the adapter assumes
1895149af931SScott Long 		 * that it's giving us an address into the array of AIF fibs.
1896149af931SScott Long 		 * Therefore, we have to convert it to an index.
1897149af931SScott Long 		 */
1898149af931SScott Long 		fib_index = (sc->aac_qentries[queue] + ci)->aq_fib_addr /
1899149af931SScott Long 			sizeof(struct aac_fib);
1900149af931SScott Long 		*fib_addr = &sc->aac_common->ac_fibs[fib_index];
1901149af931SScott Long 		break;
1902149af931SScott Long 
1903149af931SScott Long 	case AAC_HOST_NORM_RESP_QUEUE:
1904149af931SScott Long 	case AAC_HOST_HIGH_RESP_QUEUE:
1905149af931SScott Long 	{
1906149af931SScott Long 		struct aac_command *cm;
1907149af931SScott Long 
1908149af931SScott Long 		/*
1909149af931SScott Long 		 * As above, an index is used instead of an actual address.
1910149af931SScott Long 		 * Gotta shift the index to account for the fast response
1911149af931SScott Long 		 * bit.  No other correction is needed since this value was
1912149af931SScott Long 		 * originally provided by the driver via the SenderFibAddress
1913149af931SScott Long 		 * field.
1914149af931SScott Long 		 */
1915149af931SScott Long 		fib_index = (sc->aac_qentries[queue] + ci)->aq_fib_addr;
1916149af931SScott Long 		cm = sc->aac_commands + (fib_index >> 1);
1917149af931SScott Long 		*fib_addr = cm->cm_fib;
191835863739SMike Smith 
1919f30ac74cSScott Long 		/*
1920f30ac74cSScott Long 		 * Is this a fast response? If it is, update the fib fields in
1921149af931SScott Long 		 * local memory since the whole fib isn't DMA'd back up.
1922f30ac74cSScott Long 		 */
1923149af931SScott Long 		if (fib_index & 0x01) {
1924f30ac74cSScott Long 			(*fib_addr)->Header.XferState |= AAC_FIBSTATE_DONEADAP;
1925f30ac74cSScott Long 			*((u_int32_t*)((*fib_addr)->data)) = AAC_ERROR_NORMAL;
1926f30ac74cSScott Long 		}
1927149af931SScott Long 		break;
1928149af931SScott Long 	}
1929149af931SScott Long 	default:
1930149af931SScott Long 		panic("Invalid queue in aac_dequeue_fib()");
1931149af931SScott Long 		break;
1932149af931SScott Long 	}
1933149af931SScott Long 
193435863739SMike Smith 	/* update consumer index */
193535863739SMike Smith 	sc->aac_queues->qt_qindex[queue][AAC_CONSUMER_INDEX] = ci + 1;
193635863739SMike Smith 
193735863739SMike Smith 	/* if we have made the queue un-full, notify the adapter */
1938f6c4dd3fSScott Long 	if (notify && (aac_qinfo[queue].notify != 0))
193935863739SMike Smith 		AAC_QNOTIFY(sc, aac_qinfo[queue].notify);
194035863739SMike Smith 	error = 0;
194135863739SMike Smith 
194235863739SMike Smith out:
194335863739SMike Smith 	return(error);
194435863739SMike Smith }
194535863739SMike Smith 
1946914da7d0SScott Long /*
194736e0bf6eSScott Long  * Put our response to an Adapter Initialed Fib on the response queue
194836e0bf6eSScott Long  */
194936e0bf6eSScott Long static int
195036e0bf6eSScott Long aac_enqueue_response(struct aac_softc *sc, int queue, struct aac_fib *fib)
195136e0bf6eSScott Long {
195236e0bf6eSScott Long 	u_int32_t pi, ci;
19539e2e96d8SScott Long 	int error;
195436e0bf6eSScott Long 	u_int32_t fib_size;
195536e0bf6eSScott Long 	u_int32_t fib_addr;
195636e0bf6eSScott Long 
195736e0bf6eSScott Long 	debug_called(1);
195836e0bf6eSScott Long 
195936e0bf6eSScott Long 	/* Tell the adapter where the FIB is */
196036e0bf6eSScott Long 	fib_size = fib->Header.Size;
196136e0bf6eSScott Long 	fib_addr = fib->Header.SenderFibAddress;
196236e0bf6eSScott Long 	fib->Header.ReceiverFibAddress = fib_addr;
196336e0bf6eSScott Long 
196436e0bf6eSScott Long 	/* get the producer/consumer indices */
196536e0bf6eSScott Long 	pi = sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX];
196636e0bf6eSScott Long 	ci = sc->aac_queues->qt_qindex[queue][AAC_CONSUMER_INDEX];
196736e0bf6eSScott Long 
196836e0bf6eSScott Long 	/* wrap the queue? */
196936e0bf6eSScott Long 	if (pi >= aac_qinfo[queue].size)
197036e0bf6eSScott Long 		pi = 0;
197136e0bf6eSScott Long 
197236e0bf6eSScott Long 	/* check for queue full */
197336e0bf6eSScott Long 	if ((pi + 1) == ci) {
197436e0bf6eSScott Long 		error = EBUSY;
197536e0bf6eSScott Long 		goto out;
197636e0bf6eSScott Long 	}
197736e0bf6eSScott Long 
197836e0bf6eSScott Long 	/* populate queue entry */
197936e0bf6eSScott Long 	(sc->aac_qentries[queue] + pi)->aq_fib_size = fib_size;
198036e0bf6eSScott Long 	(sc->aac_qentries[queue] + pi)->aq_fib_addr = fib_addr;
198136e0bf6eSScott Long 
198236e0bf6eSScott Long 	/* update producer index */
198336e0bf6eSScott Long 	sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX] = pi + 1;
198436e0bf6eSScott Long 
198536e0bf6eSScott Long 	/* notify the adapter if we know how */
198636e0bf6eSScott Long 	if (aac_qinfo[queue].notify != 0)
198736e0bf6eSScott Long 		AAC_QNOTIFY(sc, aac_qinfo[queue].notify);
198836e0bf6eSScott Long 
198936e0bf6eSScott Long 	error = 0;
199036e0bf6eSScott Long 
199136e0bf6eSScott Long out:
199236e0bf6eSScott Long 	return(error);
199336e0bf6eSScott Long }
199436e0bf6eSScott Long 
1995914da7d0SScott Long /*
19960b94a66eSMike Smith  * Check for commands that have been outstanding for a suspiciously long time,
19970b94a66eSMike Smith  * and complain about them.
19980b94a66eSMike Smith  */
19990b94a66eSMike Smith static void
20000b94a66eSMike Smith aac_timeout(struct aac_softc *sc)
20010b94a66eSMike Smith {
20020b94a66eSMike Smith 	struct aac_command *cm;
20030b94a66eSMike Smith 	time_t deadline;
20040b94a66eSMike Smith 
2005f6c4dd3fSScott Long 	/*
200670545d1aSScott Long 	 * Traverse the busy command list, bitch about late commands once
2007914da7d0SScott Long 	 * only.
2008914da7d0SScott Long 	 */
20090b94a66eSMike Smith 	deadline = time_second - AAC_CMD_TIMEOUT;
20100b94a66eSMike Smith 	TAILQ_FOREACH(cm, &sc->aac_busy, cm_link) {
2011f6c4dd3fSScott Long 		if ((cm->cm_timestamp  < deadline)
2012f6c4dd3fSScott Long 			/* && !(cm->cm_flags & AAC_CMD_TIMEDOUT) */) {
20130b94a66eSMike Smith 			cm->cm_flags |= AAC_CMD_TIMEDOUT;
2014914da7d0SScott Long 			device_printf(sc->aac_dev,
2015914da7d0SScott Long 				      "COMMAND %p TIMEOUT AFTER %d SECONDS\n",
2016f6c4dd3fSScott Long 				      cm, (int)(time_second-cm->cm_timestamp));
20170b94a66eSMike Smith 			AAC_PRINT_FIB(sc, cm->cm_fib);
20180b94a66eSMike Smith 		}
20190b94a66eSMike Smith 	}
20200b94a66eSMike Smith 
20210b94a66eSMike Smith 	return;
20220b94a66eSMike Smith }
20230b94a66eSMike Smith 
2024914da7d0SScott Long /*
2025914da7d0SScott Long  * Interface Function Vectors
2026914da7d0SScott Long  */
202735863739SMike Smith 
2028914da7d0SScott Long /*
202935863739SMike Smith  * Read the current firmware status word.
203035863739SMike Smith  */
203135863739SMike Smith static int
203235863739SMike Smith aac_sa_get_fwstatus(struct aac_softc *sc)
203335863739SMike Smith {
203435863739SMike Smith 	debug_called(3);
203535863739SMike Smith 
203635863739SMike Smith 	return(AAC_GETREG4(sc, AAC_SA_FWSTATUS));
203735863739SMike Smith }
203835863739SMike Smith 
203935863739SMike Smith static int
204035863739SMike Smith aac_rx_get_fwstatus(struct aac_softc *sc)
204135863739SMike Smith {
204235863739SMike Smith 	debug_called(3);
204335863739SMike Smith 
204435863739SMike Smith 	return(AAC_GETREG4(sc, AAC_RX_FWSTATUS));
204535863739SMike Smith }
204635863739SMike Smith 
2047b3457b51SScott Long static int
2048b3457b51SScott Long aac_fa_get_fwstatus(struct aac_softc *sc)
2049b3457b51SScott Long {
2050b3457b51SScott Long 	int val;
2051b3457b51SScott Long 
2052b3457b51SScott Long 	debug_called(3);
2053b3457b51SScott Long 
2054b3457b51SScott Long 	val = AAC_GETREG4(sc, AAC_FA_FWSTATUS);
2055b3457b51SScott Long 	return (val);
2056b3457b51SScott Long }
2057b3457b51SScott Long 
2058914da7d0SScott Long /*
205935863739SMike Smith  * Notify the controller of a change in a given queue
206035863739SMike Smith  */
206135863739SMike Smith 
206235863739SMike Smith static void
206335863739SMike Smith aac_sa_qnotify(struct aac_softc *sc, int qbit)
206435863739SMike Smith {
206535863739SMike Smith 	debug_called(3);
206635863739SMike Smith 
206735863739SMike Smith 	AAC_SETREG2(sc, AAC_SA_DOORBELL1_SET, qbit);
206835863739SMike Smith }
206935863739SMike Smith 
207035863739SMike Smith static void
207135863739SMike Smith aac_rx_qnotify(struct aac_softc *sc, int qbit)
207235863739SMike Smith {
207335863739SMike Smith 	debug_called(3);
207435863739SMike Smith 
207535863739SMike Smith 	AAC_SETREG4(sc, AAC_RX_IDBR, qbit);
207635863739SMike Smith }
207735863739SMike Smith 
2078b3457b51SScott Long static void
2079b3457b51SScott Long aac_fa_qnotify(struct aac_softc *sc, int qbit)
2080b3457b51SScott Long {
2081b3457b51SScott Long 	debug_called(3);
2082b3457b51SScott Long 
2083b3457b51SScott Long 	AAC_SETREG2(sc, AAC_FA_DOORBELL1, qbit);
2084b3457b51SScott Long 	AAC_FA_HACK(sc);
2085b3457b51SScott Long }
2086b3457b51SScott Long 
2087914da7d0SScott Long /*
208835863739SMike Smith  * Get the interrupt reason bits
208935863739SMike Smith  */
209035863739SMike Smith static int
209135863739SMike Smith aac_sa_get_istatus(struct aac_softc *sc)
209235863739SMike Smith {
209335863739SMike Smith 	debug_called(3);
209435863739SMike Smith 
209535863739SMike Smith 	return(AAC_GETREG2(sc, AAC_SA_DOORBELL0));
209635863739SMike Smith }
209735863739SMike Smith 
209835863739SMike Smith static int
209935863739SMike Smith aac_rx_get_istatus(struct aac_softc *sc)
210035863739SMike Smith {
210135863739SMike Smith 	debug_called(3);
210235863739SMike Smith 
210335863739SMike Smith 	return(AAC_GETREG4(sc, AAC_RX_ODBR));
210435863739SMike Smith }
210535863739SMike Smith 
2106b3457b51SScott Long static int
2107b3457b51SScott Long aac_fa_get_istatus(struct aac_softc *sc)
2108b3457b51SScott Long {
2109b3457b51SScott Long 	int val;
2110b3457b51SScott Long 
2111b3457b51SScott Long 	debug_called(3);
2112b3457b51SScott Long 
2113b3457b51SScott Long 	val = AAC_GETREG2(sc, AAC_FA_DOORBELL0);
2114b3457b51SScott Long 	return (val);
2115b3457b51SScott Long }
2116b3457b51SScott Long 
2117914da7d0SScott Long /*
211835863739SMike Smith  * Clear some interrupt reason bits
211935863739SMike Smith  */
212035863739SMike Smith static void
212135863739SMike Smith aac_sa_clear_istatus(struct aac_softc *sc, int mask)
212235863739SMike Smith {
212335863739SMike Smith 	debug_called(3);
212435863739SMike Smith 
212535863739SMike Smith 	AAC_SETREG2(sc, AAC_SA_DOORBELL0_CLEAR, mask);
212635863739SMike Smith }
212735863739SMike Smith 
212835863739SMike Smith static void
212935863739SMike Smith aac_rx_clear_istatus(struct aac_softc *sc, int mask)
213035863739SMike Smith {
213135863739SMike Smith 	debug_called(3);
213235863739SMike Smith 
213335863739SMike Smith 	AAC_SETREG4(sc, AAC_RX_ODBR, mask);
213435863739SMike Smith }
213535863739SMike Smith 
2136b3457b51SScott Long static void
2137b3457b51SScott Long aac_fa_clear_istatus(struct aac_softc *sc, int mask)
2138b3457b51SScott Long {
2139b3457b51SScott Long 	debug_called(3);
2140b3457b51SScott Long 
2141b3457b51SScott Long 	AAC_SETREG2(sc, AAC_FA_DOORBELL0_CLEAR, mask);
2142b3457b51SScott Long 	AAC_FA_HACK(sc);
2143b3457b51SScott Long }
2144b3457b51SScott Long 
2145914da7d0SScott Long /*
214635863739SMike Smith  * Populate the mailbox and set the command word
214735863739SMike Smith  */
214835863739SMike Smith static void
214935863739SMike Smith aac_sa_set_mailbox(struct aac_softc *sc, u_int32_t command,
215035863739SMike Smith 		u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3)
215135863739SMike Smith {
215235863739SMike Smith 	debug_called(4);
215335863739SMike Smith 
215435863739SMike Smith 	AAC_SETREG4(sc, AAC_SA_MAILBOX, command);
215535863739SMike Smith 	AAC_SETREG4(sc, AAC_SA_MAILBOX + 4, arg0);
215635863739SMike Smith 	AAC_SETREG4(sc, AAC_SA_MAILBOX + 8, arg1);
215735863739SMike Smith 	AAC_SETREG4(sc, AAC_SA_MAILBOX + 12, arg2);
215835863739SMike Smith 	AAC_SETREG4(sc, AAC_SA_MAILBOX + 16, arg3);
215935863739SMike Smith }
216035863739SMike Smith 
216135863739SMike Smith static void
216235863739SMike Smith aac_rx_set_mailbox(struct aac_softc *sc, u_int32_t command,
216335863739SMike Smith 		u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3)
216435863739SMike Smith {
216535863739SMike Smith 	debug_called(4);
216635863739SMike Smith 
216735863739SMike Smith 	AAC_SETREG4(sc, AAC_RX_MAILBOX, command);
216835863739SMike Smith 	AAC_SETREG4(sc, AAC_RX_MAILBOX + 4, arg0);
216935863739SMike Smith 	AAC_SETREG4(sc, AAC_RX_MAILBOX + 8, arg1);
217035863739SMike Smith 	AAC_SETREG4(sc, AAC_RX_MAILBOX + 12, arg2);
217135863739SMike Smith 	AAC_SETREG4(sc, AAC_RX_MAILBOX + 16, arg3);
217235863739SMike Smith }
217335863739SMike Smith 
2174b3457b51SScott Long static void
2175b3457b51SScott Long aac_fa_set_mailbox(struct aac_softc *sc, u_int32_t command,
2176b3457b51SScott Long 		u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3)
2177b3457b51SScott Long {
2178b3457b51SScott Long 	debug_called(4);
2179b3457b51SScott Long 
2180b3457b51SScott Long 	AAC_SETREG4(sc, AAC_FA_MAILBOX, command);
2181b3457b51SScott Long 	AAC_FA_HACK(sc);
2182b3457b51SScott Long 	AAC_SETREG4(sc, AAC_FA_MAILBOX + 4, arg0);
2183b3457b51SScott Long 	AAC_FA_HACK(sc);
2184b3457b51SScott Long 	AAC_SETREG4(sc, AAC_FA_MAILBOX + 8, arg1);
2185b3457b51SScott Long 	AAC_FA_HACK(sc);
2186b3457b51SScott Long 	AAC_SETREG4(sc, AAC_FA_MAILBOX + 12, arg2);
2187b3457b51SScott Long 	AAC_FA_HACK(sc);
2188b3457b51SScott Long 	AAC_SETREG4(sc, AAC_FA_MAILBOX + 16, arg3);
2189b3457b51SScott Long 	AAC_FA_HACK(sc);
2190b3457b51SScott Long }
2191b3457b51SScott Long 
2192914da7d0SScott Long /*
219335863739SMike Smith  * Fetch the immediate command status word
219435863739SMike Smith  */
219535863739SMike Smith static int
2196a6d35632SScott Long aac_sa_get_mailbox(struct aac_softc *sc, int mb)
219735863739SMike Smith {
219835863739SMike Smith 	debug_called(4);
219935863739SMike Smith 
2200a6d35632SScott Long 	return(AAC_GETREG4(sc, AAC_SA_MAILBOX + (mb * 4)));
220135863739SMike Smith }
220235863739SMike Smith 
220335863739SMike Smith static int
2204a6d35632SScott Long aac_rx_get_mailbox(struct aac_softc *sc, int mb)
220535863739SMike Smith {
220635863739SMike Smith 	debug_called(4);
220735863739SMike Smith 
2208a6d35632SScott Long 	return(AAC_GETREG4(sc, AAC_RX_MAILBOX + (mb * 4)));
220935863739SMike Smith }
221035863739SMike Smith 
2211b3457b51SScott Long static int
2212a6d35632SScott Long aac_fa_get_mailbox(struct aac_softc *sc, int mb)
2213b3457b51SScott Long {
2214b3457b51SScott Long 	int val;
2215b3457b51SScott Long 
2216b3457b51SScott Long 	debug_called(4);
2217b3457b51SScott Long 
2218a6d35632SScott Long 	val = AAC_GETREG4(sc, AAC_FA_MAILBOX + (mb * 4));
2219b3457b51SScott Long 	return (val);
2220b3457b51SScott Long }
2221b3457b51SScott Long 
2222914da7d0SScott Long /*
222335863739SMike Smith  * Set/clear interrupt masks
222435863739SMike Smith  */
222535863739SMike Smith static void
222635863739SMike Smith aac_sa_set_interrupts(struct aac_softc *sc, int enable)
222735863739SMike Smith {
222835863739SMike Smith 	debug(2, "%sable interrupts", enable ? "en" : "dis");
222935863739SMike Smith 
223035863739SMike Smith 	if (enable) {
223135863739SMike Smith 		AAC_SETREG2((sc), AAC_SA_MASK0_CLEAR, AAC_DB_INTERRUPTS);
223235863739SMike Smith 	} else {
223335863739SMike Smith 		AAC_SETREG2((sc), AAC_SA_MASK0_SET, ~0);
223435863739SMike Smith 	}
223535863739SMike Smith }
223635863739SMike Smith 
223735863739SMike Smith static void
223835863739SMike Smith aac_rx_set_interrupts(struct aac_softc *sc, int enable)
223935863739SMike Smith {
224035863739SMike Smith 	debug(2, "%sable interrupts", enable ? "en" : "dis");
224135863739SMike Smith 
224235863739SMike Smith 	if (enable) {
224335863739SMike Smith 		AAC_SETREG4(sc, AAC_RX_OIMR, ~AAC_DB_INTERRUPTS);
224435863739SMike Smith 	} else {
224535863739SMike Smith 		AAC_SETREG4(sc, AAC_RX_OIMR, ~0);
224635863739SMike Smith 	}
224735863739SMike Smith }
224835863739SMike Smith 
2249b3457b51SScott Long static void
2250b3457b51SScott Long aac_fa_set_interrupts(struct aac_softc *sc, int enable)
2251b3457b51SScott Long {
2252b3457b51SScott Long 	debug(2, "%sable interrupts", enable ? "en" : "dis");
2253b3457b51SScott Long 
2254b3457b51SScott Long 	if (enable) {
2255b3457b51SScott Long 		AAC_SETREG2((sc), AAC_FA_MASK0_CLEAR, AAC_DB_INTERRUPTS);
2256b3457b51SScott Long 		AAC_FA_HACK(sc);
2257b3457b51SScott Long 	} else {
2258b3457b51SScott Long 		AAC_SETREG2((sc), AAC_FA_MASK0, ~0);
2259b3457b51SScott Long 		AAC_FA_HACK(sc);
2260b3457b51SScott Long 	}
2261b3457b51SScott Long }
2262b3457b51SScott Long 
2263914da7d0SScott Long /*
2264914da7d0SScott Long  * Debugging and Diagnostics
2265914da7d0SScott Long  */
226635863739SMike Smith 
2267914da7d0SScott Long /*
226835863739SMike Smith  * Print some information about the controller.
226935863739SMike Smith  */
227035863739SMike Smith static void
227135863739SMike Smith aac_describe_controller(struct aac_softc *sc)
227235863739SMike Smith {
2273cbfd045bSScott Long 	struct aac_fib *fib;
227435863739SMike Smith 	struct aac_adapter_info	*info;
227535863739SMike Smith 
227635863739SMike Smith 	debug_called(2);
227735863739SMike Smith 
2278fe3cb0e1SScott Long 	aac_alloc_sync_fib(sc, &fib, 0);
2279cbfd045bSScott Long 
2280cbfd045bSScott Long 	fib->data[0] = 0;
2281cbfd045bSScott Long 	if (aac_sync_fib(sc, RequestAdapterInfo, 0, fib, 1)) {
228235863739SMike Smith 		device_printf(sc->aac_dev, "RequestAdapterInfo failed\n");
2283fe3cb0e1SScott Long 		aac_release_sync_fib(sc);
228435863739SMike Smith 		return;
228535863739SMike Smith 	}
2286cbfd045bSScott Long 	info = (struct aac_adapter_info *)&fib->data[0];
228735863739SMike Smith 
228836e0bf6eSScott Long 	device_printf(sc->aac_dev, "%s %dMHz, %dMB cache memory, %s\n",
2289c6eafcf2SScott Long 		      aac_describe_code(aac_cpu_variant, info->CpuVariant),
229036e0bf6eSScott Long 		      info->ClockSpeed, info->BufferMem / (1024 * 1024),
2291914da7d0SScott Long 		      aac_describe_code(aac_battery_platform,
2292914da7d0SScott Long 					info->batteryPlatform));
229335863739SMike Smith 
229435863739SMike Smith 	/* save the kernel revision structure for later use */
229535863739SMike Smith 	sc->aac_revision = info->KernelRevision;
229636e0bf6eSScott Long 	device_printf(sc->aac_dev, "Kernel %d.%d-%d, Build %d, S/N %6X\n",
229735863739SMike Smith 		      info->KernelRevision.external.comp.major,
229835863739SMike Smith 		      info->KernelRevision.external.comp.minor,
229935863739SMike Smith 		      info->KernelRevision.external.comp.dash,
230036e0bf6eSScott Long 		      info->KernelRevision.buildNumber,
230136e0bf6eSScott Long 		      (u_int32_t)(info->SerialNumber & 0xffffff));
2302fe3cb0e1SScott Long 
2303fe3cb0e1SScott Long 	aac_release_sync_fib(sc);
2304a6d35632SScott Long 
2305a6d35632SScott Long 	if (1 || bootverbose) {
2306a6d35632SScott Long 		device_printf(sc->aac_dev, "Supported Options=%b\n",
2307a6d35632SScott Long 			      sc->supported_options,
2308a6d35632SScott Long 			      "\20"
2309a6d35632SScott Long 			      "\1SNAPSHOT"
2310a6d35632SScott Long 			      "\2CLUSTERS"
2311a6d35632SScott Long 			      "\3WCACHE"
2312a6d35632SScott Long 			      "\4DATA64"
2313a6d35632SScott Long 			      "\5HOSTTIME"
2314a6d35632SScott Long 			      "\6RAID50"
2315a6d35632SScott Long 			      "\7WINDOW4GB"
2316a6d35632SScott Long 			      "\10SCSIUPGD"
2317a6d35632SScott Long 			      "\11SOFTERR"
2318a6d35632SScott Long 			      "\12NORECOND"
2319a6d35632SScott Long 			      "\13SGMAP64"
2320a6d35632SScott Long 			      "\14ALARM"
2321a6d35632SScott Long 			      "\15NONDASD");
2322a6d35632SScott Long 	}
232335863739SMike Smith }
232435863739SMike Smith 
2325914da7d0SScott Long /*
232635863739SMike Smith  * Look up a text description of a numeric error code and return a pointer to
232735863739SMike Smith  * same.
232835863739SMike Smith  */
232935863739SMike Smith static char *
233035863739SMike Smith aac_describe_code(struct aac_code_lookup *table, u_int32_t code)
233135863739SMike Smith {
233235863739SMike Smith 	int i;
233335863739SMike Smith 
233435863739SMike Smith 	for (i = 0; table[i].string != NULL; i++)
233535863739SMike Smith 		if (table[i].code == code)
233635863739SMike Smith 			return(table[i].string);
233735863739SMike Smith 	return(table[i + 1].string);
233835863739SMike Smith }
233935863739SMike Smith 
2340914da7d0SScott Long /*
2341914da7d0SScott Long  * Management Interface
2342914da7d0SScott Long  */
234335863739SMike Smith 
234435863739SMike Smith static int
2345c3d15322SScott Long aac_open(dev_t dev, int flags, int fmt, d_thread_t *td)
234635863739SMike Smith {
2347914da7d0SScott Long 	struct aac_softc *sc;
234835863739SMike Smith 
234935863739SMike Smith 	debug_called(2);
235035863739SMike Smith 
2351914da7d0SScott Long 	sc = dev->si_drv1;
2352914da7d0SScott Long 
235335863739SMike Smith 	/* Check to make sure the device isn't already open */
235435863739SMike Smith 	if (sc->aac_state & AAC_STATE_OPEN) {
235535863739SMike Smith 		return EBUSY;
235635863739SMike Smith 	}
235735863739SMike Smith 	sc->aac_state |= AAC_STATE_OPEN;
235835863739SMike Smith 
235935863739SMike Smith 	return 0;
236035863739SMike Smith }
236135863739SMike Smith 
236235863739SMike Smith static int
2363c3d15322SScott Long aac_close(dev_t dev, int flags, int fmt, d_thread_t *td)
236435863739SMike Smith {
2365914da7d0SScott Long 	struct aac_softc *sc;
236635863739SMike Smith 
236735863739SMike Smith 	debug_called(2);
236835863739SMike Smith 
2369914da7d0SScott Long 	sc = dev->si_drv1;
2370914da7d0SScott Long 
237135863739SMike Smith 	/* Mark this unit as no longer open  */
237235863739SMike Smith 	sc->aac_state &= ~AAC_STATE_OPEN;
237335863739SMike Smith 
237435863739SMike Smith 	return 0;
237535863739SMike Smith }
237635863739SMike Smith 
237735863739SMike Smith static int
2378c3d15322SScott Long aac_ioctl(dev_t dev, u_long cmd, caddr_t arg, int flag, d_thread_t *td)
237935863739SMike Smith {
2380914da7d0SScott Long 	union aac_statrequest *as;
2381914da7d0SScott Long 	struct aac_softc *sc;
23820b94a66eSMike Smith 	int error = 0;
2383b88ffdc8SScott Long 	uint32_t cookie;
238435863739SMike Smith 
238535863739SMike Smith 	debug_called(2);
238635863739SMike Smith 
2387914da7d0SScott Long 	as = (union aac_statrequest *)arg;
2388914da7d0SScott Long 	sc = dev->si_drv1;
2389914da7d0SScott Long 
239035863739SMike Smith 	switch (cmd) {
23910b94a66eSMike Smith 	case AACIO_STATS:
23920b94a66eSMike Smith 		switch (as->as_item) {
23930b94a66eSMike Smith 		case AACQ_FREE:
23940b94a66eSMike Smith 		case AACQ_BIO:
23950b94a66eSMike Smith 		case AACQ_READY:
23960b94a66eSMike Smith 		case AACQ_BUSY:
23970b94a66eSMike Smith 		case AACQ_COMPLETE:
2398c6eafcf2SScott Long 			bcopy(&sc->aac_qstat[as->as_item], &as->as_qstat,
2399c6eafcf2SScott Long 			      sizeof(struct aac_qstat));
24000b94a66eSMike Smith 			break;
24010b94a66eSMike Smith 		default:
24020b94a66eSMike Smith 			error = ENOENT;
24030b94a66eSMike Smith 			break;
24040b94a66eSMike Smith 		}
24050b94a66eSMike Smith 	break;
24060b94a66eSMike Smith 
240735863739SMike Smith 	case FSACTL_SENDFIB:
2408fb0c27d7SScott Long 		arg = *(caddr_t*)arg;
2409fb0c27d7SScott Long 	case FSACTL_LNX_SENDFIB:
24100b94a66eSMike Smith 		debug(1, "FSACTL_SENDFIB");
241135863739SMike Smith 		error = aac_ioctl_sendfib(sc, arg);
241235863739SMike Smith 		break;
241335863739SMike Smith 	case FSACTL_AIF_THREAD:
2414fb0c27d7SScott Long 	case FSACTL_LNX_AIF_THREAD:
24150b94a66eSMike Smith 		debug(1, "FSACTL_AIF_THREAD");
241635863739SMike Smith 		error = EINVAL;
241735863739SMike Smith 		break;
241835863739SMike Smith 	case FSACTL_OPEN_GET_ADAPTER_FIB:
2419fb0c27d7SScott Long 		arg = *(caddr_t*)arg;
2420fb0c27d7SScott Long 	case FSACTL_LNX_OPEN_GET_ADAPTER_FIB:
24210b94a66eSMike Smith 		debug(1, "FSACTL_OPEN_GET_ADAPTER_FIB");
242235863739SMike Smith 		/*
242335863739SMike Smith 		 * Pass the caller out an AdapterFibContext.
242435863739SMike Smith 		 *
242535863739SMike Smith 		 * Note that because we only support one opener, we
242635863739SMike Smith 		 * basically ignore this.  Set the caller's context to a magic
242735863739SMike Smith 		 * number just in case.
24280b94a66eSMike Smith 		 *
24290b94a66eSMike Smith 		 * The Linux code hands the driver a pointer into kernel space,
24300b94a66eSMike Smith 		 * and then trusts it when the caller hands it back.  Aiee!
2431914da7d0SScott Long 		 * Here, we give it the proc pointer of the per-adapter aif
2432914da7d0SScott Long 		 * thread. It's only used as a sanity check in other calls.
243335863739SMike Smith 		 */
2434b88ffdc8SScott Long 		cookie = (uint32_t)(uintptr_t)sc->aifthread;
2435b88ffdc8SScott Long 		error = copyout(&cookie, arg, sizeof(cookie));
243635863739SMike Smith 		break;
243735863739SMike Smith 	case FSACTL_GET_NEXT_ADAPTER_FIB:
2438fb0c27d7SScott Long 		arg = *(caddr_t*)arg;
2439fb0c27d7SScott Long 	case FSACTL_LNX_GET_NEXT_ADAPTER_FIB:
24400b94a66eSMike Smith 		debug(1, "FSACTL_GET_NEXT_ADAPTER_FIB");
2441fb0c27d7SScott Long 		error = aac_getnext_aif(sc, arg);
244235863739SMike Smith 		break;
244335863739SMike Smith 	case FSACTL_CLOSE_GET_ADAPTER_FIB:
2444fb0c27d7SScott Long 	case FSACTL_LNX_CLOSE_GET_ADAPTER_FIB:
24450b94a66eSMike Smith 		debug(1, "FSACTL_CLOSE_GET_ADAPTER_FIB");
244635863739SMike Smith 		/* don't do anything here */
244735863739SMike Smith 		break;
244835863739SMike Smith 	case FSACTL_MINIPORT_REV_CHECK:
2449fb0c27d7SScott Long 		arg = *(caddr_t*)arg;
2450fb0c27d7SScott Long 	case FSACTL_LNX_MINIPORT_REV_CHECK:
24510b94a66eSMike Smith 		debug(1, "FSACTL_MINIPORT_REV_CHECK");
2452fb0c27d7SScott Long 		error = aac_rev_check(sc, arg);
245335863739SMike Smith 		break;
245436e0bf6eSScott Long 	case FSACTL_QUERY_DISK:
245536e0bf6eSScott Long 		arg = *(caddr_t*)arg;
245636e0bf6eSScott Long 	case FSACTL_LNX_QUERY_DISK:
245736e0bf6eSScott Long 		debug(1, "FSACTL_QUERY_DISK");
245836e0bf6eSScott Long 		error = aac_query_disk(sc, arg);
245936e0bf6eSScott Long 			break;
246036e0bf6eSScott Long 	case FSACTL_DELETE_DISK:
246136e0bf6eSScott Long 	case FSACTL_LNX_DELETE_DISK:
2462914da7d0SScott Long 		/*
2463914da7d0SScott Long 		 * We don't trust the underland to tell us when to delete a
2464914da7d0SScott Long 		 * container, rather we rely on an AIF coming from the
2465914da7d0SScott Long 		 * controller
2466914da7d0SScott Long 		 */
246736e0bf6eSScott Long 		error = 0;
246836e0bf6eSScott Long 		break;
246935863739SMike Smith 	default:
2470b3457b51SScott Long 		debug(1, "unsupported cmd 0x%lx\n", cmd);
247135863739SMike Smith 		error = EINVAL;
247235863739SMike Smith 		break;
247335863739SMike Smith 	}
247435863739SMike Smith 	return(error);
247535863739SMike Smith }
247635863739SMike Smith 
2477b3457b51SScott Long static int
2478c3d15322SScott Long aac_poll(dev_t dev, int poll_events, d_thread_t *td)
2479b3457b51SScott Long {
2480b3457b51SScott Long 	struct aac_softc *sc;
2481b3457b51SScott Long 	int revents;
2482b3457b51SScott Long 
2483b3457b51SScott Long 	sc = dev->si_drv1;
2484b3457b51SScott Long 	revents = 0;
2485b3457b51SScott Long 
2486c3d15322SScott Long 	AAC_LOCK_ACQUIRE(&sc->aac_aifq_lock);
2487b3457b51SScott Long 	if ((poll_events & (POLLRDNORM | POLLIN)) != 0) {
2488b3457b51SScott Long 		if (sc->aac_aifq_tail != sc->aac_aifq_head)
2489b3457b51SScott Long 			revents |= poll_events & (POLLIN | POLLRDNORM);
2490b3457b51SScott Long 	}
2491b3457b51SScott Long 	AAC_LOCK_RELEASE(&sc->aac_aifq_lock);
2492b3457b51SScott Long 
2493b3457b51SScott Long 	if (revents == 0) {
2494b3457b51SScott Long 		if (poll_events & (POLLIN | POLLRDNORM))
2495b3457b51SScott Long 			selrecord(td, &sc->rcv_select);
2496b3457b51SScott Long 	}
2497b3457b51SScott Long 
2498b3457b51SScott Long 	return (revents);
2499b3457b51SScott Long }
2500b3457b51SScott Long 
2501914da7d0SScott Long /*
250235863739SMike Smith  * Send a FIB supplied from userspace
250335863739SMike Smith  */
250435863739SMike Smith static int
250535863739SMike Smith aac_ioctl_sendfib(struct aac_softc *sc, caddr_t ufib)
250635863739SMike Smith {
250735863739SMike Smith 	struct aac_command *cm;
250835863739SMike Smith 	int size, error;
250935863739SMike Smith 
251035863739SMike Smith 	debug_called(2);
251135863739SMike Smith 
251235863739SMike Smith 	cm = NULL;
251335863739SMike Smith 
251435863739SMike Smith 	/*
251535863739SMike Smith 	 * Get a command
251635863739SMike Smith 	 */
2517ae543596SScott Long 	AAC_LOCK_ACQUIRE(&sc->aac_io_lock);
251835863739SMike Smith 	if (aac_alloc_command(sc, &cm)) {
251935863739SMike Smith 		error = EBUSY;
252035863739SMike Smith 		goto out;
252135863739SMike Smith 	}
252235863739SMike Smith 
252335863739SMike Smith 	/*
252435863739SMike Smith 	 * Fetch the FIB header, then re-copy to get data as well.
252535863739SMike Smith 	 */
2526914da7d0SScott Long 	if ((error = copyin(ufib, cm->cm_fib,
2527914da7d0SScott Long 			    sizeof(struct aac_fib_header))) != 0)
252835863739SMike Smith 		goto out;
252935863739SMike Smith 	size = cm->cm_fib->Header.Size + sizeof(struct aac_fib_header);
253035863739SMike Smith 	if (size > sizeof(struct aac_fib)) {
2531b88ffdc8SScott Long 		device_printf(sc->aac_dev, "incoming FIB oversized (%d > %zd)\n",
2532914da7d0SScott Long 			      size, sizeof(struct aac_fib));
253335863739SMike Smith 		size = sizeof(struct aac_fib);
253435863739SMike Smith 	}
253535863739SMike Smith 	if ((error = copyin(ufib, cm->cm_fib, size)) != 0)
253635863739SMike Smith 		goto out;
253735863739SMike Smith 	cm->cm_fib->Header.Size = size;
2538f6c4dd3fSScott Long 	cm->cm_timestamp = time_second;
253935863739SMike Smith 
254035863739SMike Smith 	/*
254135863739SMike Smith 	 * Pass the FIB to the controller, wait for it to complete.
254235863739SMike Smith 	 */
2543b3457b51SScott Long 	if ((error = aac_wait_command(cm, 30)) != 0) {	/* XXX user timeout? */
254470545d1aSScott Long 		device_printf(sc->aac_dev,
254570545d1aSScott Long 			      "aac_wait_command return %d\n", error);
254635863739SMike Smith 		goto out;
2547b3457b51SScott Long 	}
254835863739SMike Smith 
254935863739SMike Smith 	/*
255035863739SMike Smith 	 * Copy the FIB and data back out to the caller.
255135863739SMike Smith 	 */
255235863739SMike Smith 	size = cm->cm_fib->Header.Size;
255335863739SMike Smith 	if (size > sizeof(struct aac_fib)) {
2554b88ffdc8SScott Long 		device_printf(sc->aac_dev, "outbound FIB oversized (%d > %zd)\n",
2555914da7d0SScott Long 			      size, sizeof(struct aac_fib));
255635863739SMike Smith 		size = sizeof(struct aac_fib);
255735863739SMike Smith 	}
255835863739SMike Smith 	error = copyout(cm->cm_fib, ufib, size);
255935863739SMike Smith 
256035863739SMike Smith out:
2561f6c4dd3fSScott Long 	if (cm != NULL) {
256235863739SMike Smith 		aac_release_command(cm);
2563f6c4dd3fSScott Long 	}
2564ae543596SScott Long 
2565ae543596SScott Long 	AAC_LOCK_RELEASE(&sc->aac_io_lock);
256635863739SMike Smith 	return(error);
256735863739SMike Smith }
256835863739SMike Smith 
2569914da7d0SScott Long /*
257035863739SMike Smith  * Handle an AIF sent to us by the controller; queue it for later reference.
257136e0bf6eSScott Long  * If the queue fills up, then drop the older entries.
257235863739SMike Smith  */
257335863739SMike Smith static void
257436e0bf6eSScott Long aac_handle_aif(struct aac_softc *sc, struct aac_fib *fib)
257535863739SMike Smith {
257636e0bf6eSScott Long 	struct aac_aif_command *aif;
257736e0bf6eSScott Long 	struct aac_container *co, *co_next;
2578cbfd045bSScott Long 	struct aac_mntinfo *mi;
2579cbfd045bSScott Long 	struct aac_mntinforesp *mir = NULL;
258036e0bf6eSScott Long 	u_int16_t rsize;
2581b3457b51SScott Long 	int next, found;
2582795d7dc0SScott Long 	int count = 0, added = 0, i = 0;
258335863739SMike Smith 
258435863739SMike Smith 	debug_called(2);
258535863739SMike Smith 
258636e0bf6eSScott Long 	aif = (struct aac_aif_command*)&fib->data[0];
258736e0bf6eSScott Long 	aac_print_aif(sc, aif);
258836e0bf6eSScott Long 
258936e0bf6eSScott Long 	/* Is it an event that we should care about? */
259036e0bf6eSScott Long 	switch (aif->command) {
259136e0bf6eSScott Long 	case AifCmdEventNotify:
259236e0bf6eSScott Long 		switch (aif->data.EN.type) {
259336e0bf6eSScott Long 		case AifEnAddContainer:
259436e0bf6eSScott Long 		case AifEnDeleteContainer:
259536e0bf6eSScott Long 			/*
2596914da7d0SScott Long 			 * A container was added or deleted, but the message
2597914da7d0SScott Long 			 * doesn't tell us anything else!  Re-enumerate the
2598914da7d0SScott Long 			 * containers and sort things out.
259936e0bf6eSScott Long 			 */
2600fe3cb0e1SScott Long 			aac_alloc_sync_fib(sc, &fib, 0);
2601cbfd045bSScott Long 			mi = (struct aac_mntinfo *)&fib->data[0];
260236e0bf6eSScott Long 			do {
260336e0bf6eSScott Long 				/*
2604914da7d0SScott Long 				 * Ask the controller for its containers one at
2605914da7d0SScott Long 				 * a time.
2606914da7d0SScott Long 				 * XXX What if the controller's list changes
2607914da7d0SScott Long 				 * midway through this enumaration?
260836e0bf6eSScott Long 				 * XXX This should be done async.
260936e0bf6eSScott Long 				 */
261039ee03c3SScott Long 				bzero(mi, sizeof(struct aac_mntinfo));
261139ee03c3SScott Long 				mi->Command = VM_NameServe;
261239ee03c3SScott Long 				mi->MntType = FT_FILESYS;
2613cbfd045bSScott Long 				mi->MntCount = i;
261436e0bf6eSScott Long 				rsize = sizeof(mir);
2615cbfd045bSScott Long 				if (aac_sync_fib(sc, ContainerCommand, 0, fib,
2616cbfd045bSScott Long 						 sizeof(struct aac_mntinfo))) {
2617795d7dc0SScott Long 					printf("Error probing container %d\n",
2618914da7d0SScott Long 					      i);
261936e0bf6eSScott Long 					continue;
262036e0bf6eSScott Long 				}
2621cbfd045bSScott Long 				mir = (struct aac_mntinforesp *)&fib->data[0];
2622795d7dc0SScott Long 				/* XXX Need to check if count changed */
2623795d7dc0SScott Long 				count = mir->MntRespCount;
262436e0bf6eSScott Long 				/*
2625914da7d0SScott Long 				 * Check the container against our list.
2626914da7d0SScott Long 				 * co->co_found was already set to 0 in a
2627914da7d0SScott Long 				 * previous run.
262836e0bf6eSScott Long 				 */
2629cbfd045bSScott Long 				if ((mir->Status == ST_OK) &&
2630cbfd045bSScott Long 				    (mir->MntTable[0].VolType != CT_NONE)) {
263136e0bf6eSScott Long 					found = 0;
2632914da7d0SScott Long 					TAILQ_FOREACH(co,
2633914da7d0SScott Long 						      &sc->aac_container_tqh,
2634914da7d0SScott Long 						      co_link) {
263536e0bf6eSScott Long 						if (co->co_mntobj.ObjectId ==
2636cbfd045bSScott Long 						    mir->MntTable[0].ObjectId) {
263736e0bf6eSScott Long 							co->co_found = 1;
263836e0bf6eSScott Long 							found = 1;
263936e0bf6eSScott Long 							break;
264036e0bf6eSScott Long 						}
264136e0bf6eSScott Long 					}
2642914da7d0SScott Long 					/*
2643914da7d0SScott Long 					 * If the container matched, continue
2644914da7d0SScott Long 					 * in the list.
2645914da7d0SScott Long 					 */
264636e0bf6eSScott Long 					if (found) {
264736e0bf6eSScott Long 						i++;
264836e0bf6eSScott Long 						continue;
264936e0bf6eSScott Long 					}
265036e0bf6eSScott Long 
265136e0bf6eSScott Long 					/*
2652914da7d0SScott Long 					 * This is a new container.  Do all the
265370545d1aSScott Long 					 * appropriate things to set it up.
265470545d1aSScott Long 					 */
2655cbfd045bSScott Long 					aac_add_container(sc, mir, 1);
265636e0bf6eSScott Long 					added = 1;
265736e0bf6eSScott Long 				}
265836e0bf6eSScott Long 				i++;
2659795d7dc0SScott Long 			} while ((i < count) && (i < AAC_MAX_CONTAINERS));
2660cbfd045bSScott Long 			aac_release_sync_fib(sc);
266136e0bf6eSScott Long 
266236e0bf6eSScott Long 			/*
2663914da7d0SScott Long 			 * Go through our list of containers and see which ones
2664914da7d0SScott Long 			 * were not marked 'found'.  Since the controller didn't
2665914da7d0SScott Long 			 * list them they must have been deleted.  Do the
2666914da7d0SScott Long 			 * appropriate steps to destroy the device.  Also reset
2667914da7d0SScott Long 			 * the co->co_found field.
266836e0bf6eSScott Long 			 */
266936e0bf6eSScott Long 			co = TAILQ_FIRST(&sc->aac_container_tqh);
267036e0bf6eSScott Long 			while (co != NULL) {
267136e0bf6eSScott Long 				if (co->co_found == 0) {
2672914da7d0SScott Long 					device_delete_child(sc->aac_dev,
2673914da7d0SScott Long 							    co->co_disk);
267436e0bf6eSScott Long 					co_next = TAILQ_NEXT(co, co_link);
2675c3d15322SScott Long 					AAC_LOCK_ACQUIRE(&sc->
2676914da7d0SScott Long 							aac_container_lock);
2677914da7d0SScott Long 					TAILQ_REMOVE(&sc->aac_container_tqh, co,
2678914da7d0SScott Long 						     co_link);
2679914da7d0SScott Long 					AAC_LOCK_RELEASE(&sc->
2680914da7d0SScott Long 							 aac_container_lock);
268136e0bf6eSScott Long 					FREE(co, M_AACBUF);
268236e0bf6eSScott Long 					co = co_next;
268336e0bf6eSScott Long 				} else {
268436e0bf6eSScott Long 					co->co_found = 0;
268536e0bf6eSScott Long 					co = TAILQ_NEXT(co, co_link);
268636e0bf6eSScott Long 				}
268736e0bf6eSScott Long 			}
268836e0bf6eSScott Long 
268936e0bf6eSScott Long 			/* Attach the newly created containers */
269036e0bf6eSScott Long 			if (added)
269136e0bf6eSScott Long 				bus_generic_attach(sc->aac_dev);
269236e0bf6eSScott Long 
269336e0bf6eSScott Long 			break;
269436e0bf6eSScott Long 
269536e0bf6eSScott Long 		default:
269636e0bf6eSScott Long 			break;
269736e0bf6eSScott Long 		}
269836e0bf6eSScott Long 
269936e0bf6eSScott Long 	default:
270036e0bf6eSScott Long 		break;
270136e0bf6eSScott Long 	}
270236e0bf6eSScott Long 
270336e0bf6eSScott Long 	/* Copy the AIF data to the AIF queue for ioctl retrieval */
2704c3d15322SScott Long 	AAC_LOCK_ACQUIRE(&sc->aac_aifq_lock);
270535863739SMike Smith 	next = (sc->aac_aifq_head + 1) % AAC_AIFQ_LENGTH;
270635863739SMike Smith 	if (next != sc->aac_aifq_tail) {
270735863739SMike Smith 		bcopy(aif, &sc->aac_aifq[next], sizeof(struct aac_aif_command));
270835863739SMike Smith 		sc->aac_aifq_head = next;
2709b3457b51SScott Long 
2710b3457b51SScott Long 		/* On the off chance that someone is sleeping for an aif... */
271135863739SMike Smith 		if (sc->aac_state & AAC_STATE_AIF_SLEEPER)
271235863739SMike Smith 			wakeup(sc->aac_aifq);
2713b3457b51SScott Long 		/* Wakeup any poll()ers */
2714b3457b51SScott Long 		selwakeup(&sc->rcv_select);
271535863739SMike Smith 	}
2716b3457b51SScott Long 	AAC_LOCK_RELEASE(&sc->aac_aifq_lock);
271736e0bf6eSScott Long 
271836e0bf6eSScott Long 	return;
271935863739SMike Smith }
272035863739SMike Smith 
2721914da7d0SScott Long /*
27220b94a66eSMike Smith  * Return the Revision of the driver to userspace and check to see if the
272336e0bf6eSScott Long  * userspace app is possibly compatible.  This is extremely bogus since
272436e0bf6eSScott Long  * our driver doesn't follow Adaptec's versioning system.  Cheat by just
272536e0bf6eSScott Long  * returning what the card reported.
272635863739SMike Smith  */
272735863739SMike Smith static int
2728fb0c27d7SScott Long aac_rev_check(struct aac_softc *sc, caddr_t udata)
272935863739SMike Smith {
273035863739SMike Smith 	struct aac_rev_check rev_check;
273135863739SMike Smith 	struct aac_rev_check_resp rev_check_resp;
273235863739SMike Smith 	int error = 0;
273335863739SMike Smith 
273435863739SMike Smith 	debug_called(2);
273535863739SMike Smith 
273635863739SMike Smith 	/*
273735863739SMike Smith 	 * Copyin the revision struct from userspace
273835863739SMike Smith 	 */
2739c6eafcf2SScott Long 	if ((error = copyin(udata, (caddr_t)&rev_check,
2740c6eafcf2SScott Long 			sizeof(struct aac_rev_check))) != 0) {
274135863739SMike Smith 		return error;
274235863739SMike Smith 	}
274335863739SMike Smith 
2744914da7d0SScott Long 	debug(2, "Userland revision= %d\n",
2745914da7d0SScott Long 	      rev_check.callingRevision.buildNumber);
274635863739SMike Smith 
274735863739SMike Smith 	/*
274835863739SMike Smith 	 * Doctor up the response struct.
274935863739SMike Smith 	 */
275035863739SMike Smith 	rev_check_resp.possiblyCompatible = 1;
2751914da7d0SScott Long 	rev_check_resp.adapterSWRevision.external.ul =
2752914da7d0SScott Long 	    sc->aac_revision.external.ul;
2753914da7d0SScott Long 	rev_check_resp.adapterSWRevision.buildNumber =
2754914da7d0SScott Long 	    sc->aac_revision.buildNumber;
275535863739SMike Smith 
2756c6eafcf2SScott Long 	return(copyout((caddr_t)&rev_check_resp, udata,
2757c6eafcf2SScott Long 			sizeof(struct aac_rev_check_resp)));
275835863739SMike Smith }
275935863739SMike Smith 
2760914da7d0SScott Long /*
276135863739SMike Smith  * Pass the caller the next AIF in their queue
276235863739SMike Smith  */
276335863739SMike Smith static int
2764fb0c27d7SScott Long aac_getnext_aif(struct aac_softc *sc, caddr_t arg)
276535863739SMike Smith {
276635863739SMike Smith 	struct get_adapter_fib_ioctl agf;
27679e2e96d8SScott Long 	int error;
276835863739SMike Smith 
276935863739SMike Smith 	debug_called(2);
277035863739SMike Smith 
277135863739SMike Smith 	if ((error = copyin(arg, &agf, sizeof(agf))) == 0) {
277235863739SMike Smith 
277335863739SMike Smith 		/*
277435863739SMike Smith 		 * Check the magic number that we gave the caller.
277535863739SMike Smith 		 */
2776b88ffdc8SScott Long 		if (agf.AdapterFibContext != (int)(uintptr_t)sc->aifthread) {
277735863739SMike Smith 			error = EFAULT;
277835863739SMike Smith 		} else {
2779fb0c27d7SScott Long 			error = aac_return_aif(sc, agf.AifFib);
278035863739SMike Smith 			if ((error == EAGAIN) && (agf.Wait)) {
278135863739SMike Smith 				sc->aac_state |= AAC_STATE_AIF_SLEEPER;
278235863739SMike Smith 				while (error == EAGAIN) {
2783914da7d0SScott Long 					error = tsleep(sc->aac_aifq, PRIBIO |
2784914da7d0SScott Long 						       PCATCH, "aacaif", 0);
278535863739SMike Smith 					if (error == 0)
2786914da7d0SScott Long 						error = aac_return_aif(sc,
2787914da7d0SScott Long 						    agf.AifFib);
278835863739SMike Smith 				}
278935863739SMike Smith 				sc->aac_state &= ~AAC_STATE_AIF_SLEEPER;
279035863739SMike Smith 			}
279135863739SMike Smith 		}
279235863739SMike Smith 	}
279335863739SMike Smith 	return(error);
279435863739SMike Smith }
279535863739SMike Smith 
2796914da7d0SScott Long /*
27970b94a66eSMike Smith  * Hand the next AIF off the top of the queue out to userspace.
27980b94a66eSMike Smith  */
27990b94a66eSMike Smith static int
2800fb0c27d7SScott Long aac_return_aif(struct aac_softc *sc, caddr_t uptr)
28010b94a66eSMike Smith {
2802b3457b51SScott Long 	int error;
28030b94a66eSMike Smith 
28040b94a66eSMike Smith 	debug_called(2);
28050b94a66eSMike Smith 
2806c3d15322SScott Long 	AAC_LOCK_ACQUIRE(&sc->aac_aifq_lock);
28070b94a66eSMike Smith 	if (sc->aac_aifq_tail == sc->aac_aifq_head) {
28080b94a66eSMike Smith 		error = EAGAIN;
28090b94a66eSMike Smith 	} else {
2810c6eafcf2SScott Long 		error = copyout(&sc->aac_aifq[sc->aac_aifq_tail], uptr,
2811c6eafcf2SScott Long 				sizeof(struct aac_aif_command));
281236e0bf6eSScott Long 		if (error)
281370545d1aSScott Long 			device_printf(sc->aac_dev,
281470545d1aSScott Long 			    "aac_return_aif: copyout returned %d\n", error);
28150b94a66eSMike Smith 		if (!error)
2816914da7d0SScott Long 			sc->aac_aifq_tail = (sc->aac_aifq_tail + 1) %
2817914da7d0SScott Long 					    AAC_AIFQ_LENGTH;
28180b94a66eSMike Smith 	}
2819b3457b51SScott Long 	AAC_LOCK_RELEASE(&sc->aac_aifq_lock);
28200b94a66eSMike Smith 	return(error);
28210b94a66eSMike Smith }
282236e0bf6eSScott Long 
2823914da7d0SScott Long /*
282436e0bf6eSScott Long  * Give the userland some information about the container.  The AAC arch
282536e0bf6eSScott Long  * expects the driver to be a SCSI passthrough type driver, so it expects
282636e0bf6eSScott Long  * the containers to have b:t:l numbers.  Fake it.
282736e0bf6eSScott Long  */
282836e0bf6eSScott Long static int
282936e0bf6eSScott Long aac_query_disk(struct aac_softc *sc, caddr_t uptr)
283036e0bf6eSScott Long {
283136e0bf6eSScott Long 	struct aac_query_disk query_disk;
283236e0bf6eSScott Long 	struct aac_container *co;
2833914da7d0SScott Long 	struct aac_disk	*disk;
283436e0bf6eSScott Long 	int error, id;
283536e0bf6eSScott Long 
283636e0bf6eSScott Long 	debug_called(2);
283736e0bf6eSScott Long 
2838914da7d0SScott Long 	disk = NULL;
2839914da7d0SScott Long 
2840914da7d0SScott Long 	error = copyin(uptr, (caddr_t)&query_disk,
2841914da7d0SScott Long 		       sizeof(struct aac_query_disk));
284236e0bf6eSScott Long 	if (error)
284336e0bf6eSScott Long 		return (error);
284436e0bf6eSScott Long 
284536e0bf6eSScott Long 	id = query_disk.ContainerNumber;
284636e0bf6eSScott Long 	if (id == -1)
284736e0bf6eSScott Long 		return (EINVAL);
284836e0bf6eSScott Long 
2849c3d15322SScott Long 	AAC_LOCK_ACQUIRE(&sc->aac_container_lock);
285036e0bf6eSScott Long 	TAILQ_FOREACH(co, &sc->aac_container_tqh, co_link) {
285136e0bf6eSScott Long 		if (co->co_mntobj.ObjectId == id)
285236e0bf6eSScott Long 			break;
285336e0bf6eSScott Long 		}
285436e0bf6eSScott Long 
285536e0bf6eSScott Long 	if (co == NULL) {
285636e0bf6eSScott Long 			query_disk.Valid = 0;
285736e0bf6eSScott Long 			query_disk.Locked = 0;
285836e0bf6eSScott Long 			query_disk.Deleted = 1;		/* XXX is this right? */
285936e0bf6eSScott Long 	} else {
286036e0bf6eSScott Long 		disk = device_get_softc(co->co_disk);
286136e0bf6eSScott Long 		query_disk.Valid = 1;
2862914da7d0SScott Long 		query_disk.Locked =
2863914da7d0SScott Long 		    (disk->ad_flags & AAC_DISK_OPEN) ? 1 : 0;
286436e0bf6eSScott Long 		query_disk.Deleted = 0;
2865b3457b51SScott Long 		query_disk.Bus = device_get_unit(sc->aac_dev);
286636e0bf6eSScott Long 		query_disk.Target = disk->unit;
286736e0bf6eSScott Long 		query_disk.Lun = 0;
286836e0bf6eSScott Long 		query_disk.UnMapped = 0;
28697540e65eSScott Long 		sprintf(&query_disk.diskDeviceName[0], "%s%d",
28707540e65eSScott Long 		        disk->ad_disk.d_name, disk->ad_disk.d_unit);
287136e0bf6eSScott Long 	}
287236e0bf6eSScott Long 	AAC_LOCK_RELEASE(&sc->aac_container_lock);
287336e0bf6eSScott Long 
2874914da7d0SScott Long 	error = copyout((caddr_t)&query_disk, uptr,
2875914da7d0SScott Long 			sizeof(struct aac_query_disk));
287636e0bf6eSScott Long 
287736e0bf6eSScott Long 	return (error);
287836e0bf6eSScott Long }
287936e0bf6eSScott Long 
2880fe3cb0e1SScott Long static void
2881fe3cb0e1SScott Long aac_get_bus_info(struct aac_softc *sc)
2882fe3cb0e1SScott Long {
2883fe3cb0e1SScott Long 	struct aac_fib *fib;
2884fe3cb0e1SScott Long 	struct aac_ctcfg *c_cmd;
2885fe3cb0e1SScott Long 	struct aac_ctcfg_resp *c_resp;
2886fe3cb0e1SScott Long 	struct aac_vmioctl *vmi;
2887fe3cb0e1SScott Long 	struct aac_vmi_businf_resp *vmi_resp;
2888fe3cb0e1SScott Long 	struct aac_getbusinf businfo;
288970545d1aSScott Long 	struct aac_sim *caminf;
2890fe3cb0e1SScott Long 	device_t child;
2891fe3cb0e1SScott Long 	int i, found, error;
2892fe3cb0e1SScott Long 
2893fe3cb0e1SScott Long 	aac_alloc_sync_fib(sc, &fib, 0);
2894fe3cb0e1SScott Long 	c_cmd = (struct aac_ctcfg *)&fib->data[0];
289539ee03c3SScott Long 	bzero(c_cmd, sizeof(struct aac_ctcfg));
2896fe3cb0e1SScott Long 
2897fe3cb0e1SScott Long 	c_cmd->Command = VM_ContainerConfig;
2898fe3cb0e1SScott Long 	c_cmd->cmd = CT_GET_SCSI_METHOD;
2899fe3cb0e1SScott Long 	c_cmd->param = 0;
2900fe3cb0e1SScott Long 
2901fe3cb0e1SScott Long 	error = aac_sync_fib(sc, ContainerCommand, 0, fib,
2902fe3cb0e1SScott Long 	    sizeof(struct aac_ctcfg));
2903fe3cb0e1SScott Long 	if (error) {
2904fe3cb0e1SScott Long 		device_printf(sc->aac_dev, "Error %d sending "
2905fe3cb0e1SScott Long 		    "VM_ContainerConfig command\n", error);
2906fe3cb0e1SScott Long 		aac_release_sync_fib(sc);
2907fe3cb0e1SScott Long 		return;
2908fe3cb0e1SScott Long 	}
2909fe3cb0e1SScott Long 
2910fe3cb0e1SScott Long 	c_resp = (struct aac_ctcfg_resp *)&fib->data[0];
2911fe3cb0e1SScott Long 	if (c_resp->Status != ST_OK) {
2912fe3cb0e1SScott Long 		device_printf(sc->aac_dev, "VM_ContainerConfig returned 0x%x\n",
2913fe3cb0e1SScott Long 		    c_resp->Status);
2914fe3cb0e1SScott Long 		aac_release_sync_fib(sc);
2915fe3cb0e1SScott Long 		return;
2916fe3cb0e1SScott Long 	}
2917fe3cb0e1SScott Long 
2918fe3cb0e1SScott Long 	sc->scsi_method_id = c_resp->param;
2919fe3cb0e1SScott Long 
2920fe3cb0e1SScott Long 	vmi = (struct aac_vmioctl *)&fib->data[0];
292139ee03c3SScott Long 	bzero(vmi, sizeof(struct aac_vmioctl));
292239ee03c3SScott Long 
2923fe3cb0e1SScott Long 	vmi->Command = VM_Ioctl;
2924fe3cb0e1SScott Long 	vmi->ObjType = FT_DRIVE;
2925fe3cb0e1SScott Long 	vmi->MethId = sc->scsi_method_id;
2926fe3cb0e1SScott Long 	vmi->ObjId = 0;
2927fe3cb0e1SScott Long 	vmi->IoctlCmd = GetBusInfo;
2928fe3cb0e1SScott Long 
2929fe3cb0e1SScott Long 	error = aac_sync_fib(sc, ContainerCommand, 0, fib,
2930fe3cb0e1SScott Long 	    sizeof(struct aac_vmioctl));
2931fe3cb0e1SScott Long 	if (error) {
2932fe3cb0e1SScott Long 		device_printf(sc->aac_dev, "Error %d sending VMIoctl command\n",
2933fe3cb0e1SScott Long 		    error);
2934fe3cb0e1SScott Long 		aac_release_sync_fib(sc);
2935fe3cb0e1SScott Long 		return;
2936fe3cb0e1SScott Long 	}
2937fe3cb0e1SScott Long 
2938fe3cb0e1SScott Long 	vmi_resp = (struct aac_vmi_businf_resp *)&fib->data[0];
2939fe3cb0e1SScott Long 	if (vmi_resp->Status != ST_OK) {
2940fe3cb0e1SScott Long 		device_printf(sc->aac_dev, "VM_Ioctl returned %d\n",
2941fe3cb0e1SScott Long 		    vmi_resp->Status);
2942fe3cb0e1SScott Long 		aac_release_sync_fib(sc);
2943fe3cb0e1SScott Long 		return;
2944fe3cb0e1SScott Long 	}
2945fe3cb0e1SScott Long 
2946fe3cb0e1SScott Long 	bcopy(&vmi_resp->BusInf, &businfo, sizeof(struct aac_getbusinf));
2947fe3cb0e1SScott Long 	aac_release_sync_fib(sc);
2948fe3cb0e1SScott Long 
2949fe3cb0e1SScott Long 	found = 0;
2950fe3cb0e1SScott Long 	for (i = 0; i < businfo.BusCount; i++) {
2951fe3cb0e1SScott Long 		if (businfo.BusValid[i] != AAC_BUS_VALID)
2952fe3cb0e1SScott Long 			continue;
2953fe3cb0e1SScott Long 
2954a761a1caSScott Long 		caminf = (struct aac_sim *)malloc( sizeof(struct aac_sim),
2955a761a1caSScott Long 		    M_AACBUF, M_NOWAIT | M_ZERO);
2956fe3cb0e1SScott Long 		if (caminf == NULL)
2957fe3cb0e1SScott Long 			continue;
2958fe3cb0e1SScott Long 
2959fe3cb0e1SScott Long 		child = device_add_child(sc->aac_dev, "aacp", -1);
2960fe3cb0e1SScott Long 		if (child == NULL) {
2961fe3cb0e1SScott Long 			device_printf(sc->aac_dev, "device_add_child failed\n");
2962fe3cb0e1SScott Long 			continue;
2963fe3cb0e1SScott Long 		}
2964fe3cb0e1SScott Long 
2965fe3cb0e1SScott Long 		caminf->TargetsPerBus = businfo.TargetsPerBus;
2966fe3cb0e1SScott Long 		caminf->BusNumber = i;
2967fe3cb0e1SScott Long 		caminf->InitiatorBusId = businfo.InitiatorBusId[i];
2968fe3cb0e1SScott Long 		caminf->aac_sc = sc;
2969ddb8683eSScott Long 		caminf->sim_dev = child;
2970fe3cb0e1SScott Long 
2971fe3cb0e1SScott Long 		device_set_ivars(child, caminf);
2972fe3cb0e1SScott Long 		device_set_desc(child, "SCSI Passthrough Bus");
297370545d1aSScott Long 		TAILQ_INSERT_TAIL(&sc->aac_sim_tqh, caminf, sim_link);
2974fe3cb0e1SScott Long 
2975fe3cb0e1SScott Long 		found = 1;
2976fe3cb0e1SScott Long 	}
2977fe3cb0e1SScott Long 
2978fe3cb0e1SScott Long 	if (found)
2979fe3cb0e1SScott Long 		bus_generic_attach(sc->aac_dev);
2980fe3cb0e1SScott Long 
2981fe3cb0e1SScott Long 	return;
2982fe3cb0e1SScott Long }
2983