xref: /freebsd/sys/dev/aac/aac.c (revision 4102d44b77c477613ed3bf6e352bd1ed6215b763)
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 static struct cdevsw aac_cdevsw = {
1847ac40f5fSPoul-Henning Kamp 	.d_open =	aac_open,
1857ac40f5fSPoul-Henning Kamp 	.d_close =	aac_close,
1867ac40f5fSPoul-Henning Kamp 	.d_ioctl =	aac_ioctl,
1877ac40f5fSPoul-Henning Kamp 	.d_poll =	aac_poll,
1887ac40f5fSPoul-Henning Kamp 	.d_name =	"aac",
18935863739SMike Smith };
19035863739SMike Smith 
19136e0bf6eSScott Long MALLOC_DEFINE(M_AACBUF, "aacbuf", "Buffers for the AAC driver");
19236e0bf6eSScott Long 
1933d04a9d7SScott Long /* sysctl node */
1943d04a9d7SScott Long SYSCTL_NODE(_hw, OID_AUTO, aac, CTLFLAG_RD, 0, "AAC driver parameters");
1953d04a9d7SScott Long 
196914da7d0SScott Long /*
197914da7d0SScott Long  * Device Interface
198914da7d0SScott Long  */
19935863739SMike Smith 
200914da7d0SScott Long /*
20135863739SMike Smith  * Initialise the controller and softc
20235863739SMike Smith  */
20335863739SMike Smith int
20435863739SMike Smith aac_attach(struct aac_softc *sc)
20535863739SMike Smith {
20635863739SMike Smith 	int error, unit;
20735863739SMike Smith 
20835863739SMike Smith 	debug_called(1);
20935863739SMike Smith 
21035863739SMike Smith 	/*
21135863739SMike Smith 	 * Initialise per-controller queues.
21235863739SMike Smith 	 */
2130b94a66eSMike Smith 	aac_initq_free(sc);
2140b94a66eSMike Smith 	aac_initq_ready(sc);
2150b94a66eSMike Smith 	aac_initq_busy(sc);
2160b94a66eSMike Smith 	aac_initq_bio(sc);
21735863739SMike Smith 
21835863739SMike Smith 	/*
21935863739SMike Smith 	 * Initialise command-completion task.
22035863739SMike Smith 	 */
22135863739SMike Smith 	TASK_INIT(&sc->aac_task_complete, 0, aac_complete, sc);
22235863739SMike Smith 
22335863739SMike Smith 	/* disable interrupts before we enable anything */
22435863739SMike Smith 	AAC_MASK_INTERRUPTS(sc);
22535863739SMike Smith 
22635863739SMike Smith 	/* mark controller as suspended until we get ourselves organised */
22735863739SMike Smith 	sc->aac_state |= AAC_STATE_SUSPEND;
22835863739SMike Smith 
22935863739SMike Smith 	/*
230fe94b852SScott Long 	 * Check that the firmware on the card is supported.
231fe94b852SScott Long 	 */
232fe94b852SScott Long 	if ((error = aac_check_firmware(sc)) != 0)
233fe94b852SScott Long 		return(error);
234fe94b852SScott Long 
235f6b1c44dSScott Long 	/*
236f6b1c44dSScott Long 	 * Initialize locks
237f6b1c44dSScott Long 	 */
238cbfd045bSScott Long 	AAC_LOCK_INIT(&sc->aac_sync_lock, "AAC sync FIB lock");
239f6b1c44dSScott Long 	AAC_LOCK_INIT(&sc->aac_aifq_lock, "AAC AIF lock");
240f6b1c44dSScott Long 	AAC_LOCK_INIT(&sc->aac_io_lock, "AAC I/O lock");
241f6b1c44dSScott Long 	AAC_LOCK_INIT(&sc->aac_container_lock, "AAC container lock");
242f6b1c44dSScott Long 	TAILQ_INIT(&sc->aac_container_tqh);
243f6b1c44dSScott Long 
2443df780cfSScott Long 	/* Initialize the local AIF queue pointers */
2453df780cfSScott Long 	sc->aac_aifq_head = sc->aac_aifq_tail = AAC_AIFQ_LENGTH;
246cbfd045bSScott Long 
2470b94a66eSMike Smith 	/*
24835863739SMike Smith 	 * Initialise the adapter.
24935863739SMike Smith 	 */
2500b94a66eSMike Smith 	if ((error = aac_init(sc)) != 0)
25135863739SMike Smith 		return(error);
25235863739SMike Smith 
25335863739SMike Smith 	/*
25435863739SMike Smith 	 * Print a little information about the controller.
25535863739SMike Smith 	 */
25635863739SMike Smith 	aac_describe_controller(sc);
25735863739SMike Smith 
25835863739SMike Smith 	/*
259ae543596SScott Long 	 * Register to probe our containers later.
260ae543596SScott Long 	 */
26135863739SMike Smith 	sc->aac_ich.ich_func = aac_startup;
26235863739SMike Smith 	sc->aac_ich.ich_arg = sc;
26335863739SMike Smith 	if (config_intrhook_establish(&sc->aac_ich) != 0) {
264914da7d0SScott Long 		device_printf(sc->aac_dev,
265914da7d0SScott Long 			      "can't establish configuration hook\n");
26635863739SMike Smith 		return(ENXIO);
26735863739SMike Smith 	}
26835863739SMike Smith 
26935863739SMike Smith 	/*
27035863739SMike Smith 	 * Make the control device.
27135863739SMike Smith 	 */
27235863739SMike Smith 	unit = device_get_unit(sc->aac_dev);
2739e9466baSRobert Watson 	sc->aac_dev_t = make_dev(&aac_cdevsw, unit, UID_ROOT, GID_OPERATOR,
2749e9466baSRobert Watson 				 0640, "aac%d", unit);
275157fbb2eSScott Long 	(void)make_dev_alias(sc->aac_dev_t, "afa%d", unit);
2764aa620cdSScott Long 	(void)make_dev_alias(sc->aac_dev_t, "hpn%d", unit);
27735863739SMike Smith 	sc->aac_dev_t->si_drv1 = sc;
27835863739SMike Smith 
27936e0bf6eSScott Long 	/* Create the AIF thread */
28070545d1aSScott Long 	if (kthread_create((void(*)(void *))aac_command_thread, sc,
281316ec49aSScott Long 			   &sc->aifthread, 0, 0, "aac%daif", unit))
28236e0bf6eSScott Long 		panic("Could not create AIF thread\n");
28336e0bf6eSScott Long 
28436e0bf6eSScott Long 	/* Register the shutdown method to only be called post-dump */
2855f54d522SScott Long 	if ((sc->eh = EVENTHANDLER_REGISTER(shutdown_final, aac_shutdown,
2865f54d522SScott Long 	    sc->aac_dev, SHUTDOWN_PRI_DEFAULT)) == NULL)
2875f54d522SScott Long 		device_printf(sc->aac_dev,
2885f54d522SScott Long 			      "shutdown event registration failed\n");
28936e0bf6eSScott Long 
290fe3cb0e1SScott Long 	/* Register with CAM for the non-DASD devices */
291a6d35632SScott Long 	if ((sc->flags & AAC_FLAGS_ENABLE_CAM) != 0) {
29270545d1aSScott Long 		TAILQ_INIT(&sc->aac_sim_tqh);
293fe3cb0e1SScott Long 		aac_get_bus_info(sc);
29470545d1aSScott Long 	}
295fe3cb0e1SScott Long 
29635863739SMike Smith 	return(0);
29735863739SMike Smith }
29835863739SMike Smith 
299914da7d0SScott Long /*
30035863739SMike Smith  * Probe for containers, create disks.
30135863739SMike Smith  */
30235863739SMike Smith static void
30335863739SMike Smith aac_startup(void *arg)
30435863739SMike Smith {
305914da7d0SScott Long 	struct aac_softc *sc;
306cbfd045bSScott Long 	struct aac_fib *fib;
307cbfd045bSScott Long 	struct aac_mntinfo *mi;
308cbfd045bSScott Long 	struct aac_mntinforesp *mir = NULL;
309795d7dc0SScott Long 	int count = 0, i = 0;
31035863739SMike Smith 
31135863739SMike Smith 	debug_called(1);
31235863739SMike Smith 
313914da7d0SScott Long 	sc = (struct aac_softc *)arg;
314914da7d0SScott Long 
31535863739SMike Smith 	/* disconnect ourselves from the intrhook chain */
31635863739SMike Smith 	config_intrhook_disestablish(&sc->aac_ich);
31735863739SMike Smith 
318fe3cb0e1SScott Long 	aac_alloc_sync_fib(sc, &fib, 0);
319cbfd045bSScott Long 	mi = (struct aac_mntinfo *)&fib->data[0];
320cbfd045bSScott Long 
32135863739SMike Smith 	/* loop over possible containers */
32236e0bf6eSScott Long 	do {
32335863739SMike Smith 		/* request information on this container */
32439ee03c3SScott Long 		bzero(mi, sizeof(struct aac_mntinfo));
32539ee03c3SScott Long 		mi->Command = VM_NameServe;
32639ee03c3SScott Long 		mi->MntType = FT_FILESYS;
327cbfd045bSScott Long 		mi->MntCount = i;
328cbfd045bSScott Long 		if (aac_sync_fib(sc, ContainerCommand, 0, fib,
329cbfd045bSScott Long 				 sizeof(struct aac_mntinfo))) {
330795d7dc0SScott Long 			printf("error probing container %d", i);
33135863739SMike Smith 			continue;
33235863739SMike Smith 		}
33335863739SMike Smith 
334cbfd045bSScott Long 		mir = (struct aac_mntinforesp *)&fib->data[0];
335795d7dc0SScott Long 		/* XXX Need to check if count changed */
336795d7dc0SScott Long 		count = mir->MntRespCount;
337cbfd045bSScott Long 		aac_add_container(sc, mir, 0);
33836e0bf6eSScott Long 		i++;
339795d7dc0SScott Long 	} while ((i < count) && (i < AAC_MAX_CONTAINERS));
340cbfd045bSScott Long 
341cbfd045bSScott Long 	aac_release_sync_fib(sc);
34235863739SMike Smith 
34335863739SMike Smith 	/* poke the bus to actually attach the child devices */
34435863739SMike Smith 	if (bus_generic_attach(sc->aac_dev))
34535863739SMike Smith 		device_printf(sc->aac_dev, "bus_generic_attach failed\n");
34635863739SMike Smith 
34735863739SMike Smith 	/* mark the controller up */
34835863739SMike Smith 	sc->aac_state &= ~AAC_STATE_SUSPEND;
34935863739SMike Smith 
35035863739SMike Smith 	/* enable interrupts now */
35135863739SMike Smith 	AAC_UNMASK_INTERRUPTS(sc);
35235863739SMike Smith }
35335863739SMike Smith 
354914da7d0SScott Long /*
355914da7d0SScott Long  * Create a device to respresent a new container
356914da7d0SScott Long  */
357914da7d0SScott Long static void
358cbfd045bSScott Long aac_add_container(struct aac_softc *sc, struct aac_mntinforesp *mir, int f)
359914da7d0SScott Long {
360914da7d0SScott Long 	struct aac_container *co;
361914da7d0SScott Long 	device_t child;
362914da7d0SScott Long 
363914da7d0SScott Long 	/*
364914da7d0SScott Long 	 * Check container volume type for validity.  Note that many of
365914da7d0SScott Long 	 * the possible types may never show up.
366914da7d0SScott Long 	 */
367914da7d0SScott Long 	if ((mir->Status == ST_OK) && (mir->MntTable[0].VolType != CT_NONE)) {
368a761a1caSScott Long 		co = (struct aac_container *)malloc(sizeof *co, M_AACBUF,
369a761a1caSScott Long 		       M_NOWAIT | M_ZERO);
370914da7d0SScott Long 		if (co == NULL)
371914da7d0SScott Long 			panic("Out of memory?!\n");
372914da7d0SScott Long 		debug(1, "id %x  name '%.16s'  size %u  type %d",
373914da7d0SScott Long 		      mir->MntTable[0].ObjectId,
374914da7d0SScott Long 		      mir->MntTable[0].FileSystemName,
375914da7d0SScott Long 		      mir->MntTable[0].Capacity, mir->MntTable[0].VolType);
376914da7d0SScott Long 
377fe3cb0e1SScott Long 		if ((child = device_add_child(sc->aac_dev, "aacd", -1)) == NULL)
378914da7d0SScott Long 			device_printf(sc->aac_dev, "device_add_child failed\n");
379914da7d0SScott Long 		else
380914da7d0SScott Long 			device_set_ivars(child, co);
381914da7d0SScott Long 		device_set_desc(child, aac_describe_code(aac_container_types,
382914da7d0SScott Long 				mir->MntTable[0].VolType));
383914da7d0SScott Long 		co->co_disk = child;
384914da7d0SScott Long 		co->co_found = f;
385914da7d0SScott Long 		bcopy(&mir->MntTable[0], &co->co_mntobj,
386914da7d0SScott Long 		      sizeof(struct aac_mntobj));
387c3d15322SScott Long 		AAC_LOCK_ACQUIRE(&sc->aac_container_lock);
388914da7d0SScott Long 		TAILQ_INSERT_TAIL(&sc->aac_container_tqh, co, co_link);
389914da7d0SScott Long 		AAC_LOCK_RELEASE(&sc->aac_container_lock);
390914da7d0SScott Long 	}
391914da7d0SScott Long }
392914da7d0SScott Long 
393914da7d0SScott Long /*
39435863739SMike Smith  * Free all of the resources associated with (sc)
39535863739SMike Smith  *
39635863739SMike Smith  * Should not be called if the controller is active.
39735863739SMike Smith  */
39835863739SMike Smith void
39935863739SMike Smith aac_free(struct aac_softc *sc)
40035863739SMike Smith {
401ffb37f33SScott Long 
40235863739SMike Smith 	debug_called(1);
40335863739SMike Smith 
40435863739SMike Smith 	/* remove the control device */
40535863739SMike Smith 	if (sc->aac_dev_t != NULL)
40635863739SMike Smith 		destroy_dev(sc->aac_dev_t);
40735863739SMike Smith 
4080b94a66eSMike Smith 	/* throw away any FIB buffers, discard the FIB DMA tag */
4098480cc63SScott Long 	aac_free_commands(sc);
4100b94a66eSMike Smith 	if (sc->aac_fib_dmat)
4110b94a66eSMike Smith 		bus_dma_tag_destroy(sc->aac_fib_dmat);
41235863739SMike Smith 
413ffb37f33SScott Long 	free(sc->aac_commands, M_AACBUF);
414ffb37f33SScott Long 
41535863739SMike Smith 	/* destroy the common area */
41635863739SMike Smith 	if (sc->aac_common) {
41735863739SMike Smith 		bus_dmamap_unload(sc->aac_common_dmat, sc->aac_common_dmamap);
418c6eafcf2SScott Long 		bus_dmamem_free(sc->aac_common_dmat, sc->aac_common,
419c6eafcf2SScott Long 				sc->aac_common_dmamap);
42035863739SMike Smith 	}
4210b94a66eSMike Smith 	if (sc->aac_common_dmat)
4220b94a66eSMike Smith 		bus_dma_tag_destroy(sc->aac_common_dmat);
42335863739SMike Smith 
42435863739SMike Smith 	/* disconnect the interrupt handler */
42535863739SMike Smith 	if (sc->aac_intr)
42635863739SMike Smith 		bus_teardown_intr(sc->aac_dev, sc->aac_irq, sc->aac_intr);
42735863739SMike Smith 	if (sc->aac_irq != NULL)
428c6eafcf2SScott Long 		bus_release_resource(sc->aac_dev, SYS_RES_IRQ, sc->aac_irq_rid,
429c6eafcf2SScott Long 				     sc->aac_irq);
43035863739SMike Smith 
43135863739SMike Smith 	/* destroy data-transfer DMA tag */
43235863739SMike Smith 	if (sc->aac_buffer_dmat)
43335863739SMike Smith 		bus_dma_tag_destroy(sc->aac_buffer_dmat);
43435863739SMike Smith 
43535863739SMike Smith 	/* destroy the parent DMA tag */
43635863739SMike Smith 	if (sc->aac_parent_dmat)
43735863739SMike Smith 		bus_dma_tag_destroy(sc->aac_parent_dmat);
43835863739SMike Smith 
43935863739SMike Smith 	/* release the register window mapping */
44035863739SMike Smith 	if (sc->aac_regs_resource != NULL)
441914da7d0SScott Long 		bus_release_resource(sc->aac_dev, SYS_RES_MEMORY,
442914da7d0SScott Long 				     sc->aac_regs_rid, sc->aac_regs_resource);
44335863739SMike Smith }
44435863739SMike Smith 
445914da7d0SScott Long /*
44635863739SMike Smith  * Disconnect from the controller completely, in preparation for unload.
44735863739SMike Smith  */
44835863739SMike Smith int
44935863739SMike Smith aac_detach(device_t dev)
45035863739SMike Smith {
451914da7d0SScott Long 	struct aac_softc *sc;
45270545d1aSScott Long 	struct aac_container *co;
45370545d1aSScott Long 	struct aac_sim	*sim;
45435863739SMike Smith 	int error;
45535863739SMike Smith 
45635863739SMike Smith 	debug_called(1);
45735863739SMike Smith 
458914da7d0SScott Long 	sc = device_get_softc(dev);
459914da7d0SScott Long 
46035863739SMike Smith 	if (sc->aac_state & AAC_STATE_OPEN)
46135863739SMike Smith 		return(EBUSY);
46235863739SMike Smith 
46370545d1aSScott Long 	/* Remove the child containers */
464a761a1caSScott Long 	while ((co = TAILQ_FIRST(&sc->aac_container_tqh)) != NULL) {
46570545d1aSScott Long 		error = device_delete_child(dev, co->co_disk);
46670545d1aSScott Long 		if (error)
46770545d1aSScott Long 			return (error);
46865ac4ed6SScott Long 		TAILQ_REMOVE(&sc->aac_container_tqh, co, co_link);
469a761a1caSScott Long 		free(co, M_AACBUF);
47070545d1aSScott Long 	}
47170545d1aSScott Long 
47270545d1aSScott Long 	/* Remove the CAM SIMs */
473a761a1caSScott Long 	while ((sim = TAILQ_FIRST(&sc->aac_sim_tqh)) != NULL) {
474a761a1caSScott Long 		TAILQ_REMOVE(&sc->aac_sim_tqh, sim, sim_link);
47570545d1aSScott Long 		error = device_delete_child(dev, sim->sim_dev);
47670545d1aSScott Long 		if (error)
47770545d1aSScott Long 			return (error);
478a761a1caSScott Long 		free(sim, M_AACBUF);
47970545d1aSScott Long 	}
48070545d1aSScott Long 
48136e0bf6eSScott Long 	if (sc->aifflags & AAC_AIFFLAGS_RUNNING) {
48236e0bf6eSScott Long 		sc->aifflags |= AAC_AIFFLAGS_EXIT;
48336e0bf6eSScott Long 		wakeup(sc->aifthread);
48436e0bf6eSScott Long 		tsleep(sc->aac_dev, PUSER | PCATCH, "aacdch", 30 * hz);
48536e0bf6eSScott Long 	}
48636e0bf6eSScott Long 
48736e0bf6eSScott Long 	if (sc->aifflags & AAC_AIFFLAGS_RUNNING)
48836e0bf6eSScott Long 		panic("Cannot shutdown AIF thread\n");
48936e0bf6eSScott Long 
49035863739SMike Smith 	if ((error = aac_shutdown(dev)))
49135863739SMike Smith 		return(error);
49235863739SMike Smith 
4935f54d522SScott Long 	EVENTHANDLER_DEREGISTER(shutdown_final, sc->eh);
4945f54d522SScott Long 
49535863739SMike Smith 	aac_free(sc);
49635863739SMike Smith 
49735863739SMike Smith 	return(0);
49835863739SMike Smith }
49935863739SMike Smith 
500914da7d0SScott Long /*
50135863739SMike Smith  * Bring the controller down to a dormant state and detach all child devices.
50235863739SMike Smith  *
50335863739SMike Smith  * This function is called before detach or system shutdown.
50435863739SMike Smith  *
5050b94a66eSMike Smith  * Note that we can assume that the bioq on the controller is empty, as we won't
50635863739SMike Smith  * allow shutdown if any device is open.
50735863739SMike Smith  */
50835863739SMike Smith int
50935863739SMike Smith aac_shutdown(device_t dev)
51035863739SMike Smith {
511914da7d0SScott Long 	struct aac_softc *sc;
512cbfd045bSScott Long 	struct aac_fib *fib;
513cbfd045bSScott Long 	struct aac_close_command *cc;
51435863739SMike Smith 
51535863739SMike Smith 	debug_called(1);
51635863739SMike Smith 
517914da7d0SScott Long 	sc = device_get_softc(dev);
518914da7d0SScott Long 
51935863739SMike Smith 	sc->aac_state |= AAC_STATE_SUSPEND;
52035863739SMike Smith 
52135863739SMike Smith 	/*
52235863739SMike Smith 	 * Send a Container shutdown followed by a HostShutdown FIB to the
52335863739SMike Smith 	 * controller to convince it that we don't want to talk to it anymore.
52435863739SMike Smith 	 * We've been closed and all I/O completed already
52535863739SMike Smith 	 */
52635863739SMike Smith 	device_printf(sc->aac_dev, "shutting down controller...");
52735863739SMike Smith 
528fe3cb0e1SScott Long 	aac_alloc_sync_fib(sc, &fib, AAC_SYNC_LOCK_FORCE);
529cbfd045bSScott Long 	cc = (struct aac_close_command *)&fib->data[0];
530cbfd045bSScott Long 
53139ee03c3SScott Long 	bzero(cc, sizeof(struct aac_close_command));
532cbfd045bSScott Long 	cc->Command = VM_CloseAll;
533cbfd045bSScott Long 	cc->ContainerId = 0xffffffff;
534cbfd045bSScott Long 	if (aac_sync_fib(sc, ContainerCommand, 0, fib,
535cbfd045bSScott Long 	    sizeof(struct aac_close_command)))
53635863739SMike Smith 		printf("FAILED.\n");
53770545d1aSScott Long 	else
53870545d1aSScott Long 		printf("done\n");
53970545d1aSScott Long #if 0
540914da7d0SScott Long 	else {
541cbfd045bSScott Long 		fib->data[0] = 0;
54236e0bf6eSScott Long 		/*
543914da7d0SScott Long 		 * XXX Issuing this command to the controller makes it shut down
54436e0bf6eSScott Long 		 * but also keeps it from coming back up without a reset of the
54536e0bf6eSScott Long 		 * PCI bus.  This is not desirable if you are just unloading the
54636e0bf6eSScott Long 		 * driver module with the intent to reload it later.
54736e0bf6eSScott Long 		 */
548cbfd045bSScott Long 		if (aac_sync_fib(sc, FsaHostShutdown, AAC_FIBSTATE_SHUTDOWN,
549cbfd045bSScott Long 		    fib, 1)) {
55035863739SMike Smith 			printf("FAILED.\n");
55135863739SMike Smith 		} else {
55235863739SMike Smith 			printf("done.\n");
55335863739SMike Smith 		}
55435863739SMike Smith 	}
55570545d1aSScott Long #endif
55635863739SMike Smith 
55735863739SMike Smith 	AAC_MASK_INTERRUPTS(sc);
55835863739SMike Smith 
55935863739SMike Smith 	return(0);
56035863739SMike Smith }
56135863739SMike Smith 
562914da7d0SScott Long /*
56335863739SMike Smith  * Bring the controller to a quiescent state, ready for system suspend.
56435863739SMike Smith  */
56535863739SMike Smith int
56635863739SMike Smith aac_suspend(device_t dev)
56735863739SMike Smith {
568914da7d0SScott Long 	struct aac_softc *sc;
56935863739SMike Smith 
57035863739SMike Smith 	debug_called(1);
571914da7d0SScott Long 
572914da7d0SScott Long 	sc = device_get_softc(dev);
573914da7d0SScott Long 
57435863739SMike Smith 	sc->aac_state |= AAC_STATE_SUSPEND;
57535863739SMike Smith 
57635863739SMike Smith 	AAC_MASK_INTERRUPTS(sc);
57735863739SMike Smith 	return(0);
57835863739SMike Smith }
57935863739SMike Smith 
580914da7d0SScott Long /*
58135863739SMike Smith  * Bring the controller back to a state ready for operation.
58235863739SMike Smith  */
58335863739SMike Smith int
58435863739SMike Smith aac_resume(device_t dev)
58535863739SMike Smith {
586914da7d0SScott Long 	struct aac_softc *sc;
58735863739SMike Smith 
58835863739SMike Smith 	debug_called(1);
589914da7d0SScott Long 
590914da7d0SScott Long 	sc = device_get_softc(dev);
591914da7d0SScott Long 
59235863739SMike Smith 	sc->aac_state &= ~AAC_STATE_SUSPEND;
59335863739SMike Smith 	AAC_UNMASK_INTERRUPTS(sc);
59435863739SMike Smith 	return(0);
59535863739SMike Smith }
59635863739SMike Smith 
597914da7d0SScott Long /*
59835863739SMike Smith  * Take an interrupt.
59935863739SMike Smith  */
60035863739SMike Smith void
60135863739SMike Smith aac_intr(void *arg)
60235863739SMike Smith {
603914da7d0SScott Long 	struct aac_softc *sc;
60470545d1aSScott Long 	u_int16_t reason;
60535863739SMike Smith 
60635863739SMike Smith 	debug_called(2);
60735863739SMike Smith 
608914da7d0SScott Long 	sc = (struct aac_softc *)arg;
609914da7d0SScott Long 
610f30ac74cSScott Long 	/*
6119148fa21SScott Long 	 * Read the status register directly.  This is faster than taking the
6129148fa21SScott Long 	 * driver lock and reading the queues directly.  It also saves having
6139148fa21SScott Long 	 * to turn parts of the driver lock into a spin mutex, which would be
6149148fa21SScott Long 	 * ugly.
615f30ac74cSScott Long 	 */
61635863739SMike Smith 	reason = AAC_GET_ISTATUS(sc);
617f30ac74cSScott Long 	AAC_CLEAR_ISTATUS(sc, reason);
618f30ac74cSScott Long 
6199c3a7fceSScott Long 	/* handle completion processing */
6209148fa21SScott Long 	if (reason & AAC_DB_RESPONSE_READY)
6219148fa21SScott Long 		taskqueue_enqueue_fast(taskqueue_fast, &sc->aac_task_complete);
62235863739SMike Smith 
6239148fa21SScott Long 	/* controller wants to talk to us */
6249148fa21SScott Long 	if (reason & (AAC_DB_PRINTF | AAC_DB_COMMAND_READY)) {
62570545d1aSScott Long 		/*
6269148fa21SScott Long 		 * XXX Make sure that we don't get fooled by strange messages
6279148fa21SScott Long 		 * that start with a NULL.
62870545d1aSScott Long 		 */
6299148fa21SScott Long 		if ((reason & AAC_DB_PRINTF) &&
6309148fa21SScott Long 		    (sc->aac_common->ac_printf[0] == 0))
6319148fa21SScott Long 			sc->aac_common->ac_printf[0] = 32;
63270545d1aSScott Long 
6339148fa21SScott Long 		/*
6349148fa21SScott Long 		 * This might miss doing the actual wakeup.  However, the
635a32a982dSScott Long 		 * msleep that this is waking up has a timeout, so it will
6369148fa21SScott Long 		 * wake up eventually.  AIFs and printfs are low enough
6379148fa21SScott Long 		 * priority that they can handle hanging out for a few seconds
6389148fa21SScott Long 		 * if needed.
6399148fa21SScott Long 		 */
64036e0bf6eSScott Long 		wakeup(sc->aifthread);
64136e0bf6eSScott Long 	}
6429148fa21SScott Long }
64335863739SMike Smith 
644c6eafcf2SScott Long /*
645914da7d0SScott Long  * Command Processing
646914da7d0SScott Long  */
64735863739SMike Smith 
648914da7d0SScott Long /*
64935863739SMike Smith  * Start as much queued I/O as possible on the controller
65035863739SMike Smith  */
651fe3cb0e1SScott Long void
65235863739SMike Smith aac_startio(struct aac_softc *sc)
65335863739SMike Smith {
65435863739SMike Smith 	struct aac_command *cm;
65535863739SMike Smith 
65635863739SMike Smith 	debug_called(2);
65735863739SMike Smith 
658cd481291SScott Long 	if (sc->flags & AAC_QUEUE_FRZN)
659cd481291SScott Long 		return;
660cd481291SScott Long 
66135863739SMike Smith 	for (;;) {
662914da7d0SScott Long 		/*
663914da7d0SScott Long 		 * Try to get a command that's been put off for lack of
664914da7d0SScott Long 		 * resources
665914da7d0SScott Long 		 */
66635863739SMike Smith 		cm = aac_dequeue_ready(sc);
66735863739SMike Smith 
668914da7d0SScott Long 		/*
669914da7d0SScott Long 		 * Try to build a command off the bio queue (ignore error
670914da7d0SScott Long 		 * return)
671914da7d0SScott Long 		 */
6720b94a66eSMike Smith 		if (cm == NULL)
67335863739SMike Smith 			aac_bio_command(sc, &cm);
67435863739SMike Smith 
67535863739SMike Smith 		/* nothing to do? */
67635863739SMike Smith 		if (cm == NULL)
67735863739SMike Smith 			break;
67835863739SMike Smith 
6794102d44bSScott Long 		/*
6804102d44bSScott Long 		 * Try to give the command to the controller.  Any error is
6814102d44bSScott Long 		 * catastrophic since it means that bus_dmamap_load() failed.
6824102d44bSScott Long 		 */
6834102d44bSScott Long 		if (aac_map_command(cm) != 0)
6844102d44bSScott Long 			panic("aac: error mapping command %p\n", cm);
68535863739SMike Smith 	}
68635863739SMike Smith }
68735863739SMike Smith 
688914da7d0SScott Long /*
68935863739SMike Smith  * Deliver a command to the controller; allocate controller resources at the
69035863739SMike Smith  * last moment when possible.
69135863739SMike Smith  */
69235863739SMike Smith static int
693cd481291SScott Long aac_map_command(struct aac_command *cm)
69435863739SMike Smith {
695914da7d0SScott Long 	struct aac_softc *sc;
696ed5c5fb4SMike Smith 	int error;
69735863739SMike Smith 
69835863739SMike Smith 	debug_called(2);
69935863739SMike Smith 
700914da7d0SScott Long 	sc = cm->cm_sc;
701cd481291SScott Long 	error = 0;
702914da7d0SScott Long 
703cd481291SScott Long 	/* don't map more than once */
704cd481291SScott Long 	if (cm->cm_flags & AAC_CMD_MAPPED)
7054102d44bSScott Long 		panic("aac: command %p already mapped", cm);
70635863739SMike Smith 
707cd481291SScott Long 	if (cm->cm_datalen != 0) {
708cd481291SScott Long 		error = bus_dmamap_load(sc->aac_buffer_dmat, cm->cm_datamap,
709cd481291SScott Long 					cm->cm_data, cm->cm_datalen,
710cd481291SScott Long 					aac_map_command_sg, cm, 0);
711cd481291SScott Long 		if (error == EINPROGRESS) {
712cd481291SScott Long 			debug(1, "freezing queue\n");
713cd481291SScott Long 			sc->flags |= AAC_QUEUE_FRZN;
714cd481291SScott Long 			error = 0;
715cd481291SScott Long 		}
7168778f63dSScott Long 	} else {
7178778f63dSScott Long 		aac_map_command_sg(cm, NULL, 0, 0);
718cd481291SScott Long 	}
7190b94a66eSMike Smith 	return (error);
72035863739SMike Smith }
72135863739SMike Smith 
722914da7d0SScott Long /*
72335863739SMike Smith  * Handle notification of one or more FIBs coming from the controller.
72435863739SMike Smith  */
72535863739SMike Smith static void
72670545d1aSScott Long aac_command_thread(struct aac_softc *sc)
72735863739SMike Smith {
72835863739SMike Smith 	struct aac_fib *fib;
72935863739SMike Smith 	u_int32_t fib_size;
7309148fa21SScott Long 	int size, retval;
73135863739SMike Smith 
73236e0bf6eSScott Long 	debug_called(2);
73335863739SMike Smith 
734a32a982dSScott Long 	AAC_LOCK_ACQUIRE(&sc->aac_io_lock);
735a32a982dSScott Long 	sc->aifflags = AAC_AIFFLAGS_RUNNING;
73636e0bf6eSScott Long 
737a32a982dSScott Long 	while ((sc->aifflags & AAC_AIFFLAGS_EXIT) == 0) {
738a32a982dSScott Long 
739a32a982dSScott Long 		retval = 0;
740a32a982dSScott Long 		if ((sc->aifflags & AAC_AIFFLAGS_PENDING) == 0)
741a32a982dSScott Long 			retval = msleep(sc->aifthread, &sc->aac_io_lock, PRIBIO,
742a32a982dSScott Long 					"aifthd", AAC_PERIODIC_INTERVAL * hz);
74336e0bf6eSScott Long 
7449148fa21SScott Long 		/*
7459148fa21SScott Long 		 * First see if any FIBs need to be allocated.  This needs
7469148fa21SScott Long 		 * to be called without the driver lock because contigmalloc
7479148fa21SScott Long 		 * will grab Giant, and would result in an LOR.
7489148fa21SScott Long 		 */
7499148fa21SScott Long 		if ((sc->aifflags & AAC_AIFFLAGS_ALLOCFIBS) != 0) {
750a32a982dSScott Long 			AAC_LOCK_RELEASE(&sc->aac_io_lock);
751a32a982dSScott Long 			aac_alloc_commands(sc);
7529148fa21SScott Long 			AAC_LOCK_ACQUIRE(&sc->aac_io_lock);
7534102d44bSScott Long 			sc->aifflags &= ~AAC_AIFFLAGS_ALLOCFIBS;
754a32a982dSScott Long 			aac_startio(sc);
755a32a982dSScott Long 		}
7569148fa21SScott Long 
7579148fa21SScott Long 		/*
7589148fa21SScott Long 		 * While we're here, check to see if any commands are stuck.
7599148fa21SScott Long 		 * This is pretty low-priority, so it's ok if it doesn't
7609148fa21SScott Long 		 * always fire.
7619148fa21SScott Long 		 */
7629148fa21SScott Long 		if (retval == EWOULDBLOCK)
76370545d1aSScott Long 			aac_timeout(sc);
76470545d1aSScott Long 
76570545d1aSScott Long 		/* Check the hardware printf message buffer */
7669148fa21SScott Long 		if (sc->aac_common->ac_printf[0] != 0)
76770545d1aSScott Long 			aac_print_printf(sc);
76870545d1aSScott Long 
7699148fa21SScott Long 		/* Also check to see if the adapter has a command for us. */
7709148fa21SScott Long 		while (aac_dequeue_fib(sc, AAC_HOST_NORM_CMD_QUEUE,
7719148fa21SScott Long 				       &fib_size, &fib) == 0) {
77235863739SMike Smith 
77336e0bf6eSScott Long 			AAC_PRINT_FIB(sc, fib);
77436e0bf6eSScott Long 
77535863739SMike Smith 			switch (fib->Header.Command) {
77635863739SMike Smith 			case AifRequest:
77736e0bf6eSScott Long 				aac_handle_aif(sc, fib);
77835863739SMike Smith 				break;
77935863739SMike Smith 			default:
780914da7d0SScott Long 				device_printf(sc->aac_dev, "unknown command "
781914da7d0SScott Long 					      "from controller\n");
78235863739SMike Smith 				break;
78335863739SMike Smith 			}
78435863739SMike Smith 
78536e0bf6eSScott Long 			if ((fib->Header.XferState == 0) ||
78636e0bf6eSScott Long 			    (fib->Header.StructType != AAC_FIBTYPE_TFIB))
78736e0bf6eSScott Long 				break;
78836e0bf6eSScott Long 
78970545d1aSScott Long 			/* Return the AIF to the controller. */
79036e0bf6eSScott Long 			if (fib->Header.XferState & AAC_FIBSTATE_FROMADAP) {
79136e0bf6eSScott Long 				fib->Header.XferState |= AAC_FIBSTATE_DONEHOST;
79236e0bf6eSScott Long 				*(AAC_FSAStatus*)fib->data = ST_OK;
79336e0bf6eSScott Long 
79436e0bf6eSScott Long 				/* XXX Compute the Size field? */
79536e0bf6eSScott Long 				size = fib->Header.Size;
79636e0bf6eSScott Long 				if (size > sizeof(struct aac_fib)) {
79736e0bf6eSScott Long 					size = sizeof(struct aac_fib);
79836e0bf6eSScott Long 					fib->Header.Size = size;
79936e0bf6eSScott Long 				}
80036e0bf6eSScott Long 				/*
801914da7d0SScott Long 				 * Since we did not generate this command, it
802914da7d0SScott Long 				 * cannot go through the normal
803914da7d0SScott Long 				 * enqueue->startio chain.
80436e0bf6eSScott Long 				 */
805914da7d0SScott Long 				aac_enqueue_response(sc,
806914da7d0SScott Long 						     AAC_ADAP_NORM_RESP_QUEUE,
807914da7d0SScott Long 						     fib);
80836e0bf6eSScott Long 			}
80936e0bf6eSScott Long 		}
81036e0bf6eSScott Long 	}
81136e0bf6eSScott Long 	sc->aifflags &= ~AAC_AIFFLAGS_RUNNING;
812a32a982dSScott Long 	AAC_LOCK_RELEASE(&sc->aac_io_lock);
81336e0bf6eSScott Long 	wakeup(sc->aac_dev);
81436e0bf6eSScott Long 
81536e0bf6eSScott Long 	mtx_lock(&Giant);
81636e0bf6eSScott Long 	kthread_exit(0);
81735863739SMike Smith }
81835863739SMike Smith 
819914da7d0SScott Long /*
8209c3a7fceSScott Long  * Process completed commands.
82135863739SMike Smith  */
82235863739SMike Smith static void
8239c3a7fceSScott Long aac_complete(void *context, int pending)
82435863739SMike Smith {
8259c3a7fceSScott Long 	struct aac_softc *sc;
82635863739SMike Smith 	struct aac_command *cm;
82735863739SMike Smith 	struct aac_fib *fib;
82835863739SMike Smith 	u_int32_t fib_size;
82935863739SMike Smith 
83035863739SMike Smith 	debug_called(2);
83135863739SMike Smith 
8329c3a7fceSScott Long 	sc = (struct aac_softc *)context;
8339c3a7fceSScott Long 
834ae543596SScott Long 	AAC_LOCK_ACQUIRE(&sc->aac_io_lock);
835ae543596SScott Long 
8369c3a7fceSScott Long 	/* pull completed commands off the queue */
83735863739SMike Smith 	for (;;) {
83835863739SMike Smith 		/* look for completed FIBs on our queue */
839914da7d0SScott Long 		if (aac_dequeue_fib(sc, AAC_HOST_NORM_RESP_QUEUE, &fib_size,
840914da7d0SScott Long 				    &fib))
84135863739SMike Smith 			break;	/* nothing to do */
84235863739SMike Smith 
84335863739SMike Smith 		/* get the command, unmap and queue for later processing */
844cb0d64b9SScott Long 		cm = sc->aac_commands + fib->Header.SenderData;
84535863739SMike Smith 		if (cm == NULL) {
84635863739SMike Smith 			AAC_PRINT_FIB(sc, fib);
8479c3a7fceSScott Long 			break;
8489c3a7fceSScott Long 		}
8499c3a7fceSScott Long 
8500b94a66eSMike Smith 		aac_remove_busy(cm);
85135863739SMike Smith 		aac_unmap_command(cm);		/* XXX defer? */
85235863739SMike Smith 		cm->cm_flags |= AAC_CMD_COMPLETED;
85335863739SMike Smith 
85435863739SMike Smith 		/* is there a completion handler? */
85535863739SMike Smith 		if (cm->cm_complete != NULL) {
85635863739SMike Smith 			cm->cm_complete(cm);
85735863739SMike Smith 		} else {
85835863739SMike Smith 			/* assume that someone is sleeping on this command */
85935863739SMike Smith 			wakeup(cm);
86035863739SMike Smith 		}
86135863739SMike Smith 	}
8620b94a66eSMike Smith 
8630b94a66eSMike Smith 	/* see if we can start some more I/O */
864cd481291SScott Long 	sc->flags &= ~AAC_QUEUE_FRZN;
8650b94a66eSMike Smith 	aac_startio(sc);
866ae543596SScott Long 
867ae543596SScott Long 	AAC_LOCK_RELEASE(&sc->aac_io_lock);
86835863739SMike Smith }
86935863739SMike Smith 
870914da7d0SScott Long /*
87135863739SMike Smith  * Handle a bio submitted from a disk device.
87235863739SMike Smith  */
87335863739SMike Smith void
87435863739SMike Smith aac_submit_bio(struct bio *bp)
87535863739SMike Smith {
876914da7d0SScott Long 	struct aac_disk *ad;
877914da7d0SScott Long 	struct aac_softc *sc;
87835863739SMike Smith 
87935863739SMike Smith 	debug_called(2);
88035863739SMike Smith 
8817540e65eSScott Long 	ad = (struct aac_disk *)bp->bio_disk->d_drv1;
882914da7d0SScott Long 	sc = ad->ad_controller;
883914da7d0SScott Long 
88435863739SMike Smith 	/* queue the BIO and try to get some work done */
8850b94a66eSMike Smith 	aac_enqueue_bio(sc, bp);
88635863739SMike Smith 	aac_startio(sc);
88735863739SMike Smith }
88835863739SMike Smith 
889914da7d0SScott Long /*
89035863739SMike Smith  * Get a bio and build a command to go with it.
89135863739SMike Smith  */
89235863739SMike Smith static int
89335863739SMike Smith aac_bio_command(struct aac_softc *sc, struct aac_command **cmp)
89435863739SMike Smith {
89535863739SMike Smith 	struct aac_command *cm;
89635863739SMike Smith 	struct aac_fib *fib;
89735863739SMike Smith 	struct aac_disk *ad;
89835863739SMike Smith 	struct bio *bp;
89935863739SMike Smith 
90035863739SMike Smith 	debug_called(2);
90135863739SMike Smith 
90235863739SMike Smith 	/* get the resources we will need */
90335863739SMike Smith 	cm = NULL;
904a32a982dSScott Long 	bp = NULL;
90535863739SMike Smith 	if (aac_alloc_command(sc, &cm))	/* get a command */
90635863739SMike Smith 		goto fail;
907a32a982dSScott Long 	if ((bp = aac_dequeue_bio(sc)) == NULL)
908a32a982dSScott Long 		goto fail;
90935863739SMike Smith 
91035863739SMike Smith 	/* fill out the command */
9110b94a66eSMike Smith 	cm->cm_data = (void *)bp->bio_data;
9120b94a66eSMike Smith 	cm->cm_datalen = bp->bio_bcount;
9130b94a66eSMike Smith 	cm->cm_complete = aac_bio_complete;
91435863739SMike Smith 	cm->cm_private = bp;
9150b94a66eSMike Smith 	cm->cm_timestamp = time_second;
91636e0bf6eSScott Long 	cm->cm_queue = AAC_ADAP_NORM_CMD_QUEUE;
91735863739SMike Smith 
91835863739SMike Smith 	/* build the FIB */
91935863739SMike Smith 	fib = cm->cm_fib;
920b85f5808SScott Long 	fib->Header.Size = sizeof(struct aac_fib_header);
92135863739SMike Smith 	fib->Header.XferState =
92235863739SMike Smith 		AAC_FIBSTATE_HOSTOWNED   |
92335863739SMike Smith 		AAC_FIBSTATE_INITIALISED |
924f30ac74cSScott Long 		AAC_FIBSTATE_EMPTY	 |
92535863739SMike Smith 		AAC_FIBSTATE_FROMHOST	 |
92635863739SMike Smith 		AAC_FIBSTATE_REXPECTED   |
927f30ac74cSScott Long 		AAC_FIBSTATE_NORM	 |
928f30ac74cSScott Long 		AAC_FIBSTATE_ASYNC	 |
929f30ac74cSScott Long 		AAC_FIBSTATE_FAST_RESPONSE;
93035863739SMike Smith 
93135863739SMike Smith 	/* build the read/write request */
9327540e65eSScott Long 	ad = (struct aac_disk *)bp->bio_disk->d_drv1;
933b85f5808SScott Long 
934b85f5808SScott Long 	if ((sc->flags & AAC_FLAGS_SG_64BIT) == 0) {
935b85f5808SScott Long 		fib->Header.Command = ContainerCommand;
9369e2e96d8SScott Long 		if (bp->bio_cmd == BIO_READ) {
937b85f5808SScott Long 			struct aac_blockread *br;
93835863739SMike Smith 			br = (struct aac_blockread *)&fib->data[0];
93935863739SMike Smith 			br->Command = VM_CtBlockRead;
94035863739SMike Smith 			br->ContainerId = ad->ad_container->co_mntobj.ObjectId;
94135863739SMike Smith 			br->BlockNumber = bp->bio_pblkno;
94235863739SMike Smith 			br->ByteCount = bp->bio_bcount;
94335863739SMike Smith 			fib->Header.Size += sizeof(struct aac_blockread);
94435863739SMike Smith 			cm->cm_sgtable = &br->SgMap;
94535863739SMike Smith 			cm->cm_flags |= AAC_CMD_DATAIN;
94635863739SMike Smith 		} else {
947b85f5808SScott Long 			struct aac_blockwrite *bw;
94835863739SMike Smith 			bw = (struct aac_blockwrite *)&fib->data[0];
94935863739SMike Smith 			bw->Command = VM_CtBlockWrite;
95035863739SMike Smith 			bw->ContainerId = ad->ad_container->co_mntobj.ObjectId;
95135863739SMike Smith 			bw->BlockNumber = bp->bio_pblkno;
95235863739SMike Smith 			bw->ByteCount = bp->bio_bcount;
953b85f5808SScott Long 			bw->Stable = CUNSTABLE;
95435863739SMike Smith 			fib->Header.Size += sizeof(struct aac_blockwrite);
95535863739SMike Smith 			cm->cm_flags |= AAC_CMD_DATAOUT;
95635863739SMike Smith 			cm->cm_sgtable = &bw->SgMap;
95735863739SMike Smith 		}
958b85f5808SScott Long 	} else {
959b85f5808SScott Long 		fib->Header.Command = ContainerCommand64;
960b85f5808SScott Long 		if (bp->bio_cmd == BIO_READ) {
961b85f5808SScott Long 			struct aac_blockread64 *br;
962b85f5808SScott Long 			br = (struct aac_blockread64 *)&fib->data[0];
963b85f5808SScott Long 			br->Command = VM_CtHostRead64;
964b85f5808SScott Long 			br->ContainerId = ad->ad_container->co_mntobj.ObjectId;
965b85f5808SScott Long 			br->SectorCount = bp->bio_bcount / AAC_BLOCK_SIZE;
966b85f5808SScott Long 			br->BlockNumber = bp->bio_pblkno;
967b85f5808SScott Long 			br->Pad = 0;
968b85f5808SScott Long 			br->Flags = 0;
969b85f5808SScott Long 			fib->Header.Size += sizeof(struct aac_blockread64);
970b85f5808SScott Long 			cm->cm_flags |= AAC_CMD_DATAOUT;
971b85f5808SScott Long 			(struct aac_sg_table64 *)cm->cm_sgtable = &br->SgMap64;
972b85f5808SScott Long 		} else {
973b85f5808SScott Long 			struct aac_blockwrite64 *bw;
974b85f5808SScott Long 			bw = (struct aac_blockwrite64 *)&fib->data[0];
975b85f5808SScott Long 			bw->Command = VM_CtHostWrite64;
976b85f5808SScott Long 			bw->ContainerId = ad->ad_container->co_mntobj.ObjectId;
977b85f5808SScott Long 			bw->SectorCount = bp->bio_bcount / AAC_BLOCK_SIZE;
978b85f5808SScott Long 			bw->BlockNumber = bp->bio_pblkno;
979b85f5808SScott Long 			bw->Pad = 0;
980b85f5808SScott Long 			bw->Flags = 0;
981b85f5808SScott Long 			fib->Header.Size += sizeof(struct aac_blockwrite64);
982b85f5808SScott Long 			cm->cm_flags |= AAC_CMD_DATAIN;
983b85f5808SScott Long 			(struct aac_sg_table64 *)cm->cm_sgtable = &bw->SgMap64;
984b85f5808SScott Long 		}
985b85f5808SScott Long 	}
98635863739SMike Smith 
98735863739SMike Smith 	*cmp = cm;
98835863739SMike Smith 	return(0);
98935863739SMike Smith 
99035863739SMike Smith fail:
99135863739SMike Smith 	if (bp != NULL)
9920b94a66eSMike Smith 		aac_enqueue_bio(sc, bp);
99335863739SMike Smith 	if (cm != NULL)
99435863739SMike Smith 		aac_release_command(cm);
99535863739SMike Smith 	return(ENOMEM);
99635863739SMike Smith }
99735863739SMike Smith 
998914da7d0SScott Long /*
99935863739SMike Smith  * Handle a bio-instigated command that has been completed.
100035863739SMike Smith  */
100135863739SMike Smith static void
100235863739SMike Smith aac_bio_complete(struct aac_command *cm)
100335863739SMike Smith {
100435863739SMike Smith 	struct aac_blockread_response *brr;
100535863739SMike Smith 	struct aac_blockwrite_response *bwr;
100635863739SMike Smith 	struct bio *bp;
100735863739SMike Smith 	AAC_FSAStatus status;
100835863739SMike Smith 
100935863739SMike Smith 	/* fetch relevant status and then release the command */
101035863739SMike Smith 	bp = (struct bio *)cm->cm_private;
10119e2e96d8SScott Long 	if (bp->bio_cmd == BIO_READ) {
101235863739SMike Smith 		brr = (struct aac_blockread_response *)&cm->cm_fib->data[0];
101335863739SMike Smith 		status = brr->Status;
101435863739SMike Smith 	} else {
101535863739SMike Smith 		bwr = (struct aac_blockwrite_response *)&cm->cm_fib->data[0];
101635863739SMike Smith 		status = bwr->Status;
101735863739SMike Smith 	}
101835863739SMike Smith 	aac_release_command(cm);
101935863739SMike Smith 
102035863739SMike Smith 	/* fix up the bio based on status */
102135863739SMike Smith 	if (status == ST_OK) {
102235863739SMike Smith 		bp->bio_resid = 0;
102335863739SMike Smith 	} else {
102435863739SMike Smith 		bp->bio_error = EIO;
102535863739SMike Smith 		bp->bio_flags |= BIO_ERROR;
10260b94a66eSMike Smith 		/* pass an error string out to the disk layer */
1027914da7d0SScott Long 		bp->bio_driver1 = aac_describe_code(aac_command_status_table,
1028914da7d0SScott Long 						    status);
102935863739SMike Smith 	}
10300b94a66eSMike Smith 	aac_biodone(bp);
103135863739SMike Smith }
103235863739SMike Smith 
1033914da7d0SScott Long /*
103435863739SMike Smith  * Submit a command to the controller, return when it completes.
1035b3457b51SScott Long  * XXX This is very dangerous!  If the card has gone out to lunch, we could
1036b3457b51SScott Long  *     be stuck here forever.  At the same time, signals are not caught
1037b3457b51SScott Long  *     because there is a risk that a signal could wakeup the tsleep before
1038b3457b51SScott Long  *     the card has a chance to complete the command.  The passed in timeout
1039b3457b51SScott Long  *     is ignored for the same reason.  Since there is no way to cancel a
1040b3457b51SScott Long  *     command in progress, we should probably create a 'dead' queue where
1041b3457b51SScott Long  *     commands go that have been interrupted/timed-out/etc, that keeps them
1042b3457b51SScott Long  *     out of the free pool.  That way, if the card is just slow, it won't
1043b3457b51SScott Long  *     spam the memory of a command that has been recycled.
104435863739SMike Smith  */
104535863739SMike Smith static int
104635863739SMike Smith aac_wait_command(struct aac_command *cm, int timeout)
104735863739SMike Smith {
1048ae543596SScott Long 	struct aac_softc *sc;
1049ae543596SScott Long 	int error = 0;
105035863739SMike Smith 
105135863739SMike Smith 	debug_called(2);
105235863739SMike Smith 
1053ae543596SScott Long 	sc = cm->cm_sc;
1054ae543596SScott Long 
105535863739SMike Smith 	/* Put the command on the ready queue and get things going */
105636e0bf6eSScott Long 	cm->cm_queue = AAC_ADAP_NORM_CMD_QUEUE;
105735863739SMike Smith 	aac_enqueue_ready(cm);
1058ae543596SScott Long 	aac_startio(sc);
105935863739SMike Smith 	while (!(cm->cm_flags & AAC_CMD_COMPLETED) && (error != EWOULDBLOCK)) {
1060ae543596SScott Long 		error = msleep(cm, &sc->aac_io_lock, PRIBIO, "aacwait", 0);
106135863739SMike Smith 	}
106235863739SMike Smith 	return(error);
106335863739SMike Smith }
106435863739SMike Smith 
1065914da7d0SScott Long /*
1066914da7d0SScott Long  *Command Buffer Management
1067914da7d0SScott Long  */
106835863739SMike Smith 
1069914da7d0SScott Long /*
107035863739SMike Smith  * Allocate a command.
107135863739SMike Smith  */
1072fe3cb0e1SScott Long int
107335863739SMike Smith aac_alloc_command(struct aac_softc *sc, struct aac_command **cmp)
107435863739SMike Smith {
107535863739SMike Smith 	struct aac_command *cm;
107635863739SMike Smith 
107735863739SMike Smith 	debug_called(3);
107835863739SMike Smith 
1079ffb37f33SScott Long 	if ((cm = aac_dequeue_free(sc)) == NULL) {
1080b85f5808SScott Long 		if (sc->total_fibs < sc->aac_max_fibs) {
1081ae543596SScott Long 			sc->aifflags |= AAC_AIFFLAGS_ALLOCFIBS;
1082ae543596SScott Long 			wakeup(sc->aifthread);
1083b85f5808SScott Long 		}
1084ae543596SScott Long 		return (EBUSY);
1085ffb37f33SScott Long 	}
108635863739SMike Smith 
10870b94a66eSMike Smith 	*cmp = cm;
10880b94a66eSMike Smith 	return(0);
10890b94a66eSMike Smith }
10900b94a66eSMike Smith 
1091914da7d0SScott Long /*
10920b94a66eSMike Smith  * Release a command back to the freelist.
10930b94a66eSMike Smith  */
1094fe3cb0e1SScott Long void
10950b94a66eSMike Smith aac_release_command(struct aac_command *cm)
10960b94a66eSMike Smith {
10970b94a66eSMike Smith 	debug_called(3);
10980b94a66eSMike Smith 
10990b94a66eSMike Smith 	/* (re)initialise the command/FIB */
110035863739SMike Smith 	cm->cm_sgtable = NULL;
110135863739SMike Smith 	cm->cm_flags = 0;
110235863739SMike Smith 	cm->cm_complete = NULL;
110335863739SMike Smith 	cm->cm_private = NULL;
110435863739SMike Smith 	cm->cm_fib->Header.XferState = AAC_FIBSTATE_EMPTY;
110535863739SMike Smith 	cm->cm_fib->Header.StructType = AAC_FIBTYPE_TFIB;
110635863739SMike Smith 	cm->cm_fib->Header.Flags = 0;
110735863739SMike Smith 	cm->cm_fib->Header.SenderSize = sizeof(struct aac_fib);
110835863739SMike Smith 
110935863739SMike Smith 	/*
111035863739SMike Smith 	 * These are duplicated in aac_start to cover the case where an
111135863739SMike Smith 	 * intermediate stage may have destroyed them.  They're left
111235863739SMike Smith 	 * initialised here for debugging purposes only.
111335863739SMike Smith 	 */
1114f30ac74cSScott Long 	cm->cm_fib->Header.ReceiverFibAddress = (u_int32_t)cm->cm_fibphys;
1115f30ac74cSScott Long 	cm->cm_fib->Header.SenderData = 0;
111635863739SMike Smith 
111735863739SMike Smith 	aac_enqueue_free(cm);
111835863739SMike Smith }
111935863739SMike Smith 
1120914da7d0SScott Long /*
11210b94a66eSMike Smith  * Map helper for command/FIB allocation.
112235863739SMike Smith  */
112335863739SMike Smith static void
11240b94a66eSMike Smith aac_map_command_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error)
112535863739SMike Smith {
11268480cc63SScott Long 	uint32_t	*fibphys;
1127914da7d0SScott Long 
11288480cc63SScott Long 	fibphys = (uint32_t *)arg;
112935863739SMike Smith 
113035863739SMike Smith 	debug_called(3);
113135863739SMike Smith 
1132ffb37f33SScott Long 	*fibphys = segs[0].ds_addr;
113335863739SMike Smith }
113435863739SMike Smith 
1135914da7d0SScott Long /*
11360b94a66eSMike Smith  * Allocate and initialise commands/FIBs for this adapter.
113735863739SMike Smith  */
11380b94a66eSMike Smith static int
11390b94a66eSMike Smith aac_alloc_commands(struct aac_softc *sc)
114035863739SMike Smith {
114135863739SMike Smith 	struct aac_command *cm;
1142ffb37f33SScott Long 	struct aac_fibmap *fm;
11438480cc63SScott Long 	uint32_t fibphys;
1144ffb37f33SScott Long 	int i, error;
114535863739SMike Smith 
1146a6d35632SScott Long 	debug_called(2);
114735863739SMike Smith 
1148a6d35632SScott Long 	if (sc->total_fibs + AAC_FIB_COUNT > sc->aac_max_fibs)
1149ffb37f33SScott Long 		return (ENOMEM);
1150ffb37f33SScott Long 
11518480cc63SScott Long 	fm = malloc(sizeof(struct aac_fibmap), M_AACBUF, M_NOWAIT|M_ZERO);
1152a6d35632SScott Long 	if (fm == NULL)
1153a6d35632SScott Long 		return (ENOMEM);
1154ffb37f33SScott Long 
11550b94a66eSMike Smith 	/* allocate the FIBs in DMAable memory and load them */
1156ffb37f33SScott Long 	if (bus_dmamem_alloc(sc->aac_fib_dmat, (void **)&fm->aac_fibs,
1157ffb37f33SScott Long 			     BUS_DMA_NOWAIT, &fm->aac_fibmap)) {
115870545d1aSScott Long 		device_printf(sc->aac_dev,
115970545d1aSScott Long 			      "Not enough contiguous memory available.\n");
11608480cc63SScott Long 		free(fm, M_AACBUF);
11610b94a66eSMike Smith 		return (ENOMEM);
116235863739SMike Smith 	}
1163128aa5a0SScott Long 
1164cd481291SScott Long 	/* Ignore errors since this doesn't bounce */
1165cd481291SScott Long 	(void)bus_dmamap_load(sc->aac_fib_dmat, fm->aac_fibmap, fm->aac_fibs,
1166ffb37f33SScott Long 			      AAC_FIB_COUNT * sizeof(struct aac_fib),
1167ffb37f33SScott Long 			      aac_map_command_helper, &fibphys, 0);
1168128aa5a0SScott Long 
11690b94a66eSMike Smith 	/* initialise constant fields in the command structure */
11709148fa21SScott Long 	AAC_LOCK_ACQUIRE(&sc->aac_io_lock);
1171ffb37f33SScott Long 	bzero(fm->aac_fibs, AAC_FIB_COUNT * sizeof(struct aac_fib));
11720b94a66eSMike Smith 	for (i = 0; i < AAC_FIB_COUNT; i++) {
11738480cc63SScott Long 		cm = sc->aac_commands + sc->total_fibs;
1174ffb37f33SScott Long 		fm->aac_commands = cm;
117535863739SMike Smith 		cm->cm_sc = sc;
1176ffb37f33SScott Long 		cm->cm_fib = fm->aac_fibs + i;
11778480cc63SScott Long 		cm->cm_fibphys = fibphys + (i * sizeof(struct aac_fib));
1178cb0d64b9SScott Long 		cm->cm_index = sc->total_fibs;
117935863739SMike Smith 
1180ffb37f33SScott Long 		if ((error = bus_dmamap_create(sc->aac_buffer_dmat, 0,
1181ffb37f33SScott Long 					       &cm->cm_datamap)) == 0)
118235863739SMike Smith 			aac_release_command(cm);
1183ffb37f33SScott Long 		else
11848480cc63SScott Long 			break;
11858480cc63SScott Long 		sc->total_fibs++;
118635863739SMike Smith 	}
1187ffb37f33SScott Long 
11888480cc63SScott Long 	if (i > 0) {
1189ffb37f33SScott Long 		TAILQ_INSERT_TAIL(&sc->aac_fibmap_tqh, fm, fm_link);
1190a6d35632SScott Long 		debug(1, "total_fibs= %d\n", sc->total_fibs);
11919148fa21SScott Long 		AAC_LOCK_RELEASE(&sc->aac_io_lock);
11920b94a66eSMike Smith 		return (0);
119335863739SMike Smith 	}
119435863739SMike Smith 
11959148fa21SScott Long 	AAC_LOCK_RELEASE(&sc->aac_io_lock);
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 */
12924102d44bSScott Long 	if (aac_enqueue_fib(sc, cm->cm_queue, cm) == EBUSY) {
12934102d44bSScott Long 		aac_unmap_command(cm);
1294cd481291SScott Long 		aac_requeue_ready(cm);
12954102d44bSScott Long 	}
1296cd481291SScott Long 
1297cd481291SScott Long 	return;
129835863739SMike Smith }
129935863739SMike Smith 
1300914da7d0SScott Long /*
130135863739SMike Smith  * Unmap a command from controller-visible space.
130235863739SMike Smith  */
130335863739SMike Smith static void
130435863739SMike Smith aac_unmap_command(struct aac_command *cm)
130535863739SMike Smith {
1306914da7d0SScott Long 	struct aac_softc *sc;
130735863739SMike Smith 
130835863739SMike Smith 	debug_called(2);
130935863739SMike Smith 
1310914da7d0SScott Long 	sc = cm->cm_sc;
1311914da7d0SScott Long 
131235863739SMike Smith 	if (!(cm->cm_flags & AAC_CMD_MAPPED))
131335863739SMike Smith 		return;
131435863739SMike Smith 
131535863739SMike Smith 	if (cm->cm_datalen != 0) {
131635863739SMike Smith 		if (cm->cm_flags & AAC_CMD_DATAIN)
1317c6eafcf2SScott Long 			bus_dmamap_sync(sc->aac_buffer_dmat, cm->cm_datamap,
1318c6eafcf2SScott Long 					BUS_DMASYNC_POSTREAD);
131935863739SMike Smith 		if (cm->cm_flags & AAC_CMD_DATAOUT)
1320c6eafcf2SScott Long 			bus_dmamap_sync(sc->aac_buffer_dmat, cm->cm_datamap,
1321c6eafcf2SScott Long 					BUS_DMASYNC_POSTWRITE);
132235863739SMike Smith 
132335863739SMike Smith 		bus_dmamap_unload(sc->aac_buffer_dmat, cm->cm_datamap);
132435863739SMike Smith 	}
132535863739SMike Smith 	cm->cm_flags &= ~AAC_CMD_MAPPED;
132635863739SMike Smith }
132735863739SMike Smith 
1328914da7d0SScott Long /*
1329914da7d0SScott Long  * Hardware Interface
1330914da7d0SScott Long  */
133135863739SMike Smith 
1332914da7d0SScott Long /*
133335863739SMike Smith  * Initialise the adapter.
133435863739SMike Smith  */
133535863739SMike Smith static void
133635863739SMike Smith aac_common_map(void *arg, bus_dma_segment_t *segs, int nseg, int error)
133735863739SMike Smith {
1338914da7d0SScott Long 	struct aac_softc *sc;
133935863739SMike Smith 
134035863739SMike Smith 	debug_called(1);
134135863739SMike Smith 
1342914da7d0SScott Long 	sc = (struct aac_softc *)arg;
1343914da7d0SScott Long 
134435863739SMike Smith 	sc->aac_common_busaddr = segs[0].ds_addr;
134535863739SMike Smith }
134635863739SMike Smith 
1347a6d35632SScott Long static int
1348a6d35632SScott Long aac_check_firmware(struct aac_softc *sc)
1349a6d35632SScott Long {
1350a6d35632SScott Long 	u_int32_t major, minor, options;
1351a6d35632SScott Long 
1352a6d35632SScott Long 	debug_called(1);
1353a6d35632SScott Long 
1354fe94b852SScott Long 	/*
1355fe94b852SScott Long 	 * Retrieve the firmware version numbers.  Dell PERC2/QC cards with
1356fe94b852SScott Long 	 * firmware version 1.x are not compatible with this driver.
1357fe94b852SScott Long 	 */
1358a6d35632SScott Long 	if (sc->flags & AAC_FLAGS_PERC2QC) {
1359fe94b852SScott Long 		if (aac_sync_command(sc, AAC_MONKER_GETKERNVER, 0, 0, 0, 0,
1360fe94b852SScott Long 				     NULL)) {
1361fe94b852SScott Long 			device_printf(sc->aac_dev,
1362fe94b852SScott Long 				      "Error reading firmware version\n");
1363fe94b852SScott Long 			return (EIO);
1364fe94b852SScott Long 		}
1365fe94b852SScott Long 
1366fe94b852SScott Long 		/* These numbers are stored as ASCII! */
1367a6d35632SScott Long 		major = (AAC_GET_MAILBOX(sc, 1) & 0xff) - 0x30;
1368a6d35632SScott Long 		minor = (AAC_GET_MAILBOX(sc, 2) & 0xff) - 0x30;
1369fe94b852SScott Long 		if (major == 1) {
1370fe94b852SScott Long 			device_printf(sc->aac_dev,
1371fe94b852SScott Long 			    "Firmware version %d.%d is not supported.\n",
1372fe94b852SScott Long 			    major, minor);
1373fe94b852SScott Long 			return (EINVAL);
1374fe94b852SScott Long 		}
1375fe94b852SScott Long 	}
1376fe94b852SScott Long 
1377a6d35632SScott Long 	/*
1378a6d35632SScott Long 	 * Retrieve the capabilities/supported options word so we know what
1379a6d35632SScott Long 	 * work-arounds to enable.
1380a6d35632SScott Long 	 */
1381a6d35632SScott Long 	if (aac_sync_command(sc, AAC_MONKER_GETINFO, 0, 0, 0, 0, NULL)) {
1382a6d35632SScott Long 		device_printf(sc->aac_dev, "RequestAdapterInfo failed\n");
1383a6d35632SScott Long 		return (EIO);
1384a6d35632SScott Long 	}
1385a6d35632SScott Long 	options = AAC_GET_MAILBOX(sc, 1);
1386a6d35632SScott Long 	sc->supported_options = options;
1387a6d35632SScott Long 
1388a6d35632SScott Long 	if ((options & AAC_SUPPORTED_4GB_WINDOW) != 0 &&
1389a6d35632SScott Long 	    (sc->flags & AAC_FLAGS_NO4GB) == 0)
1390a6d35632SScott Long 		sc->flags |= AAC_FLAGS_4GB_WINDOW;
1391a6d35632SScott Long 	if (options & AAC_SUPPORTED_NONDASD)
1392a6d35632SScott Long 		sc->flags |= AAC_FLAGS_ENABLE_CAM;
1393cd481291SScott Long 	if ((options & AAC_SUPPORTED_SGMAP_HOST64) != 0
1394cd481291SScott Long 	     && (sizeof(bus_addr_t) > 4)) {
1395a6d35632SScott Long 		device_printf(sc->aac_dev, "Enabling 64-bit address support\n");
1396a6d35632SScott Long 		sc->flags |= AAC_FLAGS_SG_64BIT;
1397a6d35632SScott Long 	}
1398a6d35632SScott Long 
1399a6d35632SScott Long 	/* Check for broken hardware that does a lower number of commands */
1400a6d35632SScott Long 	if ((sc->flags & AAC_FLAGS_256FIBS) == 0)
1401a6d35632SScott Long 		sc->aac_max_fibs = AAC_MAX_FIBS;
1402a6d35632SScott Long 	else
1403a6d35632SScott Long 		sc->aac_max_fibs = 256;
1404a6d35632SScott Long 
1405fe94b852SScott Long 	return (0);
1406fe94b852SScott Long }
1407fe94b852SScott Long 
140835863739SMike Smith static int
140935863739SMike Smith aac_init(struct aac_softc *sc)
141035863739SMike Smith {
141135863739SMike Smith 	struct aac_adapter_init	*ip;
141235863739SMike Smith 	time_t then;
1413b88ffdc8SScott Long 	u_int32_t code, qoffset;
1414a6d35632SScott Long 	int error;
141535863739SMike Smith 
141635863739SMike Smith 	debug_called(1);
141735863739SMike Smith 
141835863739SMike Smith 	/*
141935863739SMike Smith 	 * First wait for the adapter to come ready.
142035863739SMike Smith 	 */
142135863739SMike Smith 	then = time_second;
142235863739SMike Smith 	do {
142335863739SMike Smith 		code = AAC_GET_FWSTATUS(sc);
142435863739SMike Smith 		if (code & AAC_SELF_TEST_FAILED) {
142535863739SMike Smith 			device_printf(sc->aac_dev, "FATAL: selftest failed\n");
142635863739SMike Smith 			return(ENXIO);
142735863739SMike Smith 		}
142835863739SMike Smith 		if (code & AAC_KERNEL_PANIC) {
1429914da7d0SScott Long 			device_printf(sc->aac_dev,
1430914da7d0SScott Long 				      "FATAL: controller kernel panic\n");
143135863739SMike Smith 			return(ENXIO);
143235863739SMike Smith 		}
143335863739SMike Smith 		if (time_second > (then + AAC_BOOT_TIMEOUT)) {
1434914da7d0SScott Long 			device_printf(sc->aac_dev,
1435914da7d0SScott Long 				      "FATAL: controller not coming ready, "
1436c6eafcf2SScott Long 					   "status %x\n", code);
143735863739SMike Smith 			return(ENXIO);
143835863739SMike Smith 		}
143935863739SMike Smith 	} while (!(code & AAC_UP_AND_RUNNING));
144035863739SMike Smith 
1441a6d35632SScott Long 	error = ENOMEM;
1442a6d35632SScott Long 	/*
1443a6d35632SScott Long 	 * Create DMA tag for mapping buffers into controller-addressable space.
1444a6d35632SScott Long 	 */
1445a6d35632SScott Long 	if (bus_dma_tag_create(sc->aac_parent_dmat, 	/* parent */
1446a6d35632SScott Long 			       1, 0, 			/* algnmnt, boundary */
1447a6d35632SScott Long 			       (sc->flags & AAC_FLAGS_SG_64BIT) ?
1448a6d35632SScott Long 			       BUS_SPACE_MAXADDR :
1449a6d35632SScott Long 			       BUS_SPACE_MAXADDR_32BIT,	/* lowaddr */
1450a6d35632SScott Long 			       BUS_SPACE_MAXADDR, 	/* highaddr */
1451a6d35632SScott Long 			       NULL, NULL, 		/* filter, filterarg */
1452a6d35632SScott Long 			       MAXBSIZE,		/* maxsize */
1453a6d35632SScott Long 			       AAC_MAXSGENTRIES,	/* nsegments */
1454a6d35632SScott Long 			       MAXBSIZE,		/* maxsegsize */
1455a6d35632SScott Long 			       BUS_DMA_ALLOCNOW,	/* flags */
1456f6b1c44dSScott Long 			       busdma_lock_mutex,	/* lockfunc */
1457f6b1c44dSScott Long 			       &sc->aac_io_lock,	/* lockfuncarg */
1458a6d35632SScott Long 			       &sc->aac_buffer_dmat)) {
1459a6d35632SScott Long 		device_printf(sc->aac_dev, "can't allocate buffer DMA tag\n");
1460a6d35632SScott Long 		goto out;
1461a6d35632SScott Long 	}
1462a6d35632SScott Long 
1463a6d35632SScott Long 	/*
1464a6d35632SScott Long 	 * Create DMA tag for mapping FIBs into controller-addressable space..
1465a6d35632SScott Long 	 */
1466a6d35632SScott Long 	if (bus_dma_tag_create(sc->aac_parent_dmat,	/* parent */
1467a6d35632SScott Long 			       1, 0, 			/* algnmnt, boundary */
1468a6d35632SScott Long 			       (sc->flags & AAC_FLAGS_4GB_WINDOW) ?
1469a6d35632SScott Long 			       BUS_SPACE_MAXADDR_32BIT :
1470a6d35632SScott Long 			       0x7fffffff,		/* lowaddr */
1471a6d35632SScott Long 			       BUS_SPACE_MAXADDR, 	/* highaddr */
1472a6d35632SScott Long 			       NULL, NULL, 		/* filter, filterarg */
1473a6d35632SScott Long 			       AAC_FIB_COUNT *
1474a6d35632SScott Long 			       sizeof(struct aac_fib),  /* maxsize */
1475a6d35632SScott Long 			       1,			/* nsegments */
1476a6d35632SScott Long 			       AAC_FIB_COUNT *
1477a6d35632SScott Long 			       sizeof(struct aac_fib),	/* maxsegsize */
1478a6d35632SScott Long 			       BUS_DMA_ALLOCNOW,	/* flags */
1479f6b1c44dSScott Long 			       NULL, NULL,		/* No locking needed */
1480a6d35632SScott Long 			       &sc->aac_fib_dmat)) {
1481a6d35632SScott Long 		device_printf(sc->aac_dev, "can't allocate FIB DMA tag\n");;
1482a6d35632SScott Long 		goto out;
1483a6d35632SScott Long 	}
1484a6d35632SScott Long 
148535863739SMike Smith 	/*
148635863739SMike Smith 	 * Create DMA tag for the common structure and allocate it.
148735863739SMike Smith 	 */
148835863739SMike Smith 	if (bus_dma_tag_create(sc->aac_parent_dmat, 	/* parent */
1489c6eafcf2SScott Long 			       1, 0,			/* algnmnt, boundary */
1490a6d35632SScott Long 			       (sc->flags & AAC_FLAGS_4GB_WINDOW) ?
1491a6d35632SScott Long 			       BUS_SPACE_MAXADDR_32BIT :
1492a6d35632SScott Long 			       0x7fffffff,		/* lowaddr */
149335863739SMike Smith 			       BUS_SPACE_MAXADDR, 	/* highaddr */
149435863739SMike Smith 			       NULL, NULL, 		/* filter, filterarg */
1495ffb37f33SScott Long 			       8192 + sizeof(struct aac_common), /* maxsize */
1496914da7d0SScott Long 			       1,			/* nsegments */
149735863739SMike Smith 			       BUS_SPACE_MAXSIZE_32BIT,	/* maxsegsize */
1498a6d35632SScott Long 			       BUS_DMA_ALLOCNOW,	/* flags */
1499f6b1c44dSScott Long 			       NULL, NULL,		/* No locking needed */
150035863739SMike Smith 			       &sc->aac_common_dmat)) {
1501914da7d0SScott Long 		device_printf(sc->aac_dev,
1502914da7d0SScott Long 			      "can't allocate common structure DMA tag\n");
1503a6d35632SScott Long 		goto out;
150435863739SMike Smith 	}
1505c6eafcf2SScott Long 	if (bus_dmamem_alloc(sc->aac_common_dmat, (void **)&sc->aac_common,
1506c6eafcf2SScott Long 			     BUS_DMA_NOWAIT, &sc->aac_common_dmamap)) {
150735863739SMike Smith 		device_printf(sc->aac_dev, "can't allocate common structure\n");
1508a6d35632SScott Long 		goto out;
150935863739SMike Smith 	}
1510ffb37f33SScott Long 
1511ffb37f33SScott Long 	/*
1512ffb37f33SScott Long 	 * Work around a bug in the 2120 and 2200 that cannot DMA commands
1513ffb37f33SScott Long 	 * below address 8192 in physical memory.
1514ffb37f33SScott Long 	 * XXX If the padding is not needed, can it be put to use instead
1515ffb37f33SScott Long 	 * of ignored?
1516ffb37f33SScott Long 	 */
1517cd481291SScott Long 	(void)bus_dmamap_load(sc->aac_common_dmat, sc->aac_common_dmamap,
1518ffb37f33SScott Long 			sc->aac_common, 8192 + sizeof(*sc->aac_common),
1519ffb37f33SScott Long 			aac_common_map, sc, 0);
1520ffb37f33SScott Long 
1521ffb37f33SScott Long 	if (sc->aac_common_busaddr < 8192) {
1522ffb37f33SScott Long 		(uint8_t *)sc->aac_common += 8192;
1523ffb37f33SScott Long 		sc->aac_common_busaddr += 8192;
1524ffb37f33SScott Long 	}
152535863739SMike Smith 	bzero(sc->aac_common, sizeof(*sc->aac_common));
152635863739SMike Smith 
1527ffb37f33SScott Long 	/* Allocate some FIBs and associated command structs */
1528ffb37f33SScott Long 	TAILQ_INIT(&sc->aac_fibmap_tqh);
1529ffb37f33SScott Long 	sc->aac_commands = malloc(AAC_MAX_FIBS * sizeof(struct aac_command),
15308480cc63SScott Long 				  M_AACBUF, M_WAITOK|M_ZERO);
15318480cc63SScott Long 	while (sc->total_fibs < AAC_PREALLOCATE_FIBS) {
1532ffb37f33SScott Long 		if (aac_alloc_commands(sc) != 0)
1533ffb37f33SScott Long 			break;
1534ffb37f33SScott Long 	}
1535ffb37f33SScott Long 	if (sc->total_fibs == 0)
1536a6d35632SScott Long 		goto out;
1537ffb37f33SScott Long 
153835863739SMike Smith 	/*
1539914da7d0SScott Long 	 * Fill in the init structure.  This tells the adapter about the
1540914da7d0SScott Long 	 * physical location of various important shared data structures.
154135863739SMike Smith 	 */
154235863739SMike Smith 	ip = &sc->aac_common->ac_init;
154335863739SMike Smith 	ip->InitStructRevision = AAC_INIT_STRUCT_REVISION;
1544f30ac74cSScott Long 	ip->MiniPortRevision = AAC_INIT_STRUCT_MINIPORT_REVISION;
154535863739SMike Smith 
1546c6eafcf2SScott Long 	ip->AdapterFibsPhysicalAddress = sc->aac_common_busaddr +
1547c6eafcf2SScott Long 					 offsetof(struct aac_common, ac_fibs);
1548149af931SScott Long 	ip->AdapterFibsVirtualAddress = 0;
154935863739SMike Smith 	ip->AdapterFibsSize = AAC_ADAPTER_FIBS * sizeof(struct aac_fib);
155035863739SMike Smith 	ip->AdapterFibAlign = sizeof(struct aac_fib);
155135863739SMike Smith 
1552c6eafcf2SScott Long 	ip->PrintfBufferAddress = sc->aac_common_busaddr +
1553c6eafcf2SScott Long 				  offsetof(struct aac_common, ac_printf);
155435863739SMike Smith 	ip->PrintfBufferSize = AAC_PRINTF_BUFSIZE;
155535863739SMike Smith 
15564b00f859SScott Long 	/*
15574b00f859SScott Long 	 * The adapter assumes that pages are 4K in size, except on some
15584b00f859SScott Long  	 * broken firmware versions that do the page->byte conversion twice,
15594b00f859SScott Long 	 * therefore 'assuming' that this value is in 16MB units (2^24).
15604b00f859SScott Long 	 * Round up since the granularity is so high.
15614b00f859SScott Long 	 */
1562f30ac74cSScott Long 	ip->HostPhysMemPages = ctob(physmem) / AAC_PAGE_SIZE;
15634b00f859SScott Long 	if (sc->flags & AAC_FLAGS_BROKEN_MEMMAP) {
15644b00f859SScott Long 		ip->HostPhysMemPages =
15654b00f859SScott Long 		    (ip->HostPhysMemPages + AAC_PAGE_SIZE) / AAC_PAGE_SIZE;
1566204c0befSScott Long 	}
156735863739SMike Smith 	ip->HostElapsedSeconds = time_second;	/* reset later if invalid */
156835863739SMike Smith 
156935863739SMike Smith 	/*
1570c6eafcf2SScott Long 	 * Initialise FIB queues.  Note that it appears that the layout of the
1571c6eafcf2SScott Long 	 * indexes and the segmentation of the entries may be mandated by the
1572c6eafcf2SScott Long 	 * adapter, which is only told about the base of the queue index fields.
157335863739SMike Smith 	 *
157435863739SMike Smith 	 * The initial values of the indices are assumed to inform the adapter
1575914da7d0SScott Long 	 * of the sizes of the respective queues, and theoretically it could
1576914da7d0SScott Long 	 * work out the entire layout of the queue structures from this.  We
1577914da7d0SScott Long 	 * take the easy route and just lay this area out like everyone else
1578914da7d0SScott Long 	 * does.
157935863739SMike Smith 	 *
1580914da7d0SScott Long 	 * The Linux driver uses a much more complex scheme whereby several
1581914da7d0SScott Long 	 * header records are kept for each queue.  We use a couple of generic
1582914da7d0SScott Long 	 * list manipulation functions which 'know' the size of each list by
1583914da7d0SScott Long 	 * virtue of a table.
158435863739SMike Smith 	 */
1585b88ffdc8SScott Long 	qoffset = offsetof(struct aac_common, ac_qbuf) + AAC_QUEUE_ALIGN;
15860bcbebd6SScott Long 	qoffset &= ~(AAC_QUEUE_ALIGN - 1);
15870bcbebd6SScott Long 	sc->aac_queues =
15880bcbebd6SScott Long 	    (struct aac_queue_table *)((uintptr_t)sc->aac_common + qoffset);
1589b88ffdc8SScott Long 	ip->CommHeaderAddress = sc->aac_common_busaddr + qoffset;
159035863739SMike Smith 
1591c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_NORM_CMD_QUEUE][AAC_PRODUCER_INDEX] =
1592c6eafcf2SScott Long 		AAC_HOST_NORM_CMD_ENTRIES;
1593c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_NORM_CMD_QUEUE][AAC_CONSUMER_INDEX] =
1594c6eafcf2SScott Long 		AAC_HOST_NORM_CMD_ENTRIES;
1595c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_HIGH_CMD_QUEUE][AAC_PRODUCER_INDEX] =
1596c6eafcf2SScott Long 		AAC_HOST_HIGH_CMD_ENTRIES;
1597c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_HIGH_CMD_QUEUE][AAC_CONSUMER_INDEX] =
1598c6eafcf2SScott Long 		AAC_HOST_HIGH_CMD_ENTRIES;
1599c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_NORM_CMD_QUEUE][AAC_PRODUCER_INDEX] =
1600c6eafcf2SScott Long 		AAC_ADAP_NORM_CMD_ENTRIES;
1601c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_NORM_CMD_QUEUE][AAC_CONSUMER_INDEX] =
1602c6eafcf2SScott Long 		AAC_ADAP_NORM_CMD_ENTRIES;
1603c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_HIGH_CMD_QUEUE][AAC_PRODUCER_INDEX] =
1604c6eafcf2SScott Long 		AAC_ADAP_HIGH_CMD_ENTRIES;
1605c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_HIGH_CMD_QUEUE][AAC_CONSUMER_INDEX] =
1606c6eafcf2SScott Long 		AAC_ADAP_HIGH_CMD_ENTRIES;
1607c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_NORM_RESP_QUEUE][AAC_PRODUCER_INDEX]=
1608c6eafcf2SScott Long 		AAC_HOST_NORM_RESP_ENTRIES;
1609c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_NORM_RESP_QUEUE][AAC_CONSUMER_INDEX]=
1610c6eafcf2SScott Long 		AAC_HOST_NORM_RESP_ENTRIES;
1611c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_HIGH_RESP_QUEUE][AAC_PRODUCER_INDEX]=
1612c6eafcf2SScott Long 		AAC_HOST_HIGH_RESP_ENTRIES;
1613c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_HIGH_RESP_QUEUE][AAC_CONSUMER_INDEX]=
1614c6eafcf2SScott Long 		AAC_HOST_HIGH_RESP_ENTRIES;
1615c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_NORM_RESP_QUEUE][AAC_PRODUCER_INDEX]=
1616c6eafcf2SScott Long 		AAC_ADAP_NORM_RESP_ENTRIES;
1617c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_NORM_RESP_QUEUE][AAC_CONSUMER_INDEX]=
1618c6eafcf2SScott Long 		AAC_ADAP_NORM_RESP_ENTRIES;
1619c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_HIGH_RESP_QUEUE][AAC_PRODUCER_INDEX]=
1620c6eafcf2SScott Long 		AAC_ADAP_HIGH_RESP_ENTRIES;
1621c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_HIGH_RESP_QUEUE][AAC_CONSUMER_INDEX]=
1622c6eafcf2SScott Long 		AAC_ADAP_HIGH_RESP_ENTRIES;
1623c6eafcf2SScott Long 	sc->aac_qentries[AAC_HOST_NORM_CMD_QUEUE] =
1624c6eafcf2SScott Long 		&sc->aac_queues->qt_HostNormCmdQueue[0];
1625c6eafcf2SScott Long 	sc->aac_qentries[AAC_HOST_HIGH_CMD_QUEUE] =
1626c6eafcf2SScott Long 		&sc->aac_queues->qt_HostHighCmdQueue[0];
1627c6eafcf2SScott Long 	sc->aac_qentries[AAC_ADAP_NORM_CMD_QUEUE] =
1628c6eafcf2SScott Long 		&sc->aac_queues->qt_AdapNormCmdQueue[0];
1629c6eafcf2SScott Long 	sc->aac_qentries[AAC_ADAP_HIGH_CMD_QUEUE] =
1630c6eafcf2SScott Long 		&sc->aac_queues->qt_AdapHighCmdQueue[0];
1631c6eafcf2SScott Long 	sc->aac_qentries[AAC_HOST_NORM_RESP_QUEUE] =
1632c6eafcf2SScott Long 		&sc->aac_queues->qt_HostNormRespQueue[0];
1633c6eafcf2SScott Long 	sc->aac_qentries[AAC_HOST_HIGH_RESP_QUEUE] =
1634c6eafcf2SScott Long 		&sc->aac_queues->qt_HostHighRespQueue[0];
1635c6eafcf2SScott Long 	sc->aac_qentries[AAC_ADAP_NORM_RESP_QUEUE] =
1636c6eafcf2SScott Long 		&sc->aac_queues->qt_AdapNormRespQueue[0];
1637c6eafcf2SScott Long 	sc->aac_qentries[AAC_ADAP_HIGH_RESP_QUEUE] =
1638c6eafcf2SScott Long 		&sc->aac_queues->qt_AdapHighRespQueue[0];
163935863739SMike Smith 
164035863739SMike Smith 	/*
164135863739SMike Smith 	 * Do controller-type-specific initialisation
164235863739SMike Smith 	 */
164335863739SMike Smith 	switch (sc->aac_hwif) {
164435863739SMike Smith 	case AAC_HWIF_I960RX:
164535863739SMike Smith 		AAC_SETREG4(sc, AAC_RX_ODBR, ~0);
164635863739SMike Smith 		break;
164735863739SMike Smith 	}
164835863739SMike Smith 
164935863739SMike Smith 	/*
165035863739SMike Smith 	 * Give the init structure to the controller.
165135863739SMike Smith 	 */
165235863739SMike Smith 	if (aac_sync_command(sc, AAC_MONKER_INITSTRUCT,
1653914da7d0SScott Long 			     sc->aac_common_busaddr +
1654914da7d0SScott Long 			     offsetof(struct aac_common, ac_init), 0, 0, 0,
1655914da7d0SScott Long 			     NULL)) {
1656914da7d0SScott Long 		device_printf(sc->aac_dev,
1657914da7d0SScott Long 			      "error establishing init structure\n");
1658a6d35632SScott Long 		error = EIO;
1659a6d35632SScott Long 		goto out;
166035863739SMike Smith 	}
166135863739SMike Smith 
1662a6d35632SScott Long 	error = 0;
1663a6d35632SScott Long out:
1664a6d35632SScott Long 	return(error);
166535863739SMike Smith }
166635863739SMike Smith 
1667914da7d0SScott Long /*
166835863739SMike Smith  * Send a synchronous command to the controller and wait for a result.
166935863739SMike Smith  */
167035863739SMike Smith static int
167135863739SMike Smith aac_sync_command(struct aac_softc *sc, u_int32_t command,
167235863739SMike Smith 		 u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3,
167335863739SMike Smith 		 u_int32_t *sp)
167435863739SMike Smith {
167535863739SMike Smith 	time_t then;
167635863739SMike Smith 	u_int32_t status;
167735863739SMike Smith 
167835863739SMike Smith 	debug_called(3);
167935863739SMike Smith 
168035863739SMike Smith 	/* populate the mailbox */
168135863739SMike Smith 	AAC_SET_MAILBOX(sc, command, arg0, arg1, arg2, arg3);
168235863739SMike Smith 
168335863739SMike Smith 	/* ensure the sync command doorbell flag is cleared */
168435863739SMike Smith 	AAC_CLEAR_ISTATUS(sc, AAC_DB_SYNC_COMMAND);
168535863739SMike Smith 
168635863739SMike Smith 	/* then set it to signal the adapter */
168735863739SMike Smith 	AAC_QNOTIFY(sc, AAC_DB_SYNC_COMMAND);
168835863739SMike Smith 
168935863739SMike Smith 	/* spin waiting for the command to complete */
169035863739SMike Smith 	then = time_second;
169135863739SMike Smith 	do {
169235863739SMike Smith 		if (time_second > (then + AAC_IMMEDIATE_TIMEOUT)) {
1693a6d35632SScott Long 			debug(1, "timed out");
169435863739SMike Smith 			return(EIO);
169535863739SMike Smith 		}
169635863739SMike Smith 	} while (!(AAC_GET_ISTATUS(sc) & AAC_DB_SYNC_COMMAND));
169735863739SMike Smith 
169835863739SMike Smith 	/* clear the completion flag */
169935863739SMike Smith 	AAC_CLEAR_ISTATUS(sc, AAC_DB_SYNC_COMMAND);
170035863739SMike Smith 
170135863739SMike Smith 	/* get the command status */
1702a6d35632SScott Long 	status = AAC_GET_MAILBOX(sc, 0);
170335863739SMike Smith 	if (sp != NULL)
170435863739SMike Smith 		*sp = status;
17050b94a66eSMike Smith 	return(0);
170635863739SMike Smith }
170735863739SMike Smith 
1708914da7d0SScott Long /*
1709cbfd045bSScott Long  * Grab the sync fib area.
1710cbfd045bSScott Long  */
1711cbfd045bSScott Long int
1712fe3cb0e1SScott Long aac_alloc_sync_fib(struct aac_softc *sc, struct aac_fib **fib, int flags)
1713cbfd045bSScott Long {
1714cbfd045bSScott Long 
1715cbfd045bSScott Long 	/*
1716cbfd045bSScott Long 	 * If the force flag is set, the system is shutting down, or in
1717cbfd045bSScott Long 	 * trouble.  Ignore the mutex.
1718cbfd045bSScott Long 	 */
1719cbfd045bSScott Long 	if (!(flags & AAC_SYNC_LOCK_FORCE))
1720cbfd045bSScott Long 		AAC_LOCK_ACQUIRE(&sc->aac_sync_lock);
1721cbfd045bSScott Long 
1722cbfd045bSScott Long 	*fib = &sc->aac_common->ac_sync_fib;
1723cbfd045bSScott Long 
1724cbfd045bSScott Long 	return (1);
1725cbfd045bSScott Long }
1726cbfd045bSScott Long 
1727cbfd045bSScott Long /*
1728cbfd045bSScott Long  * Release the sync fib area.
1729cbfd045bSScott Long  */
1730cbfd045bSScott Long void
1731cbfd045bSScott Long aac_release_sync_fib(struct aac_softc *sc)
1732cbfd045bSScott Long {
1733cbfd045bSScott Long 
1734cbfd045bSScott Long 	AAC_LOCK_RELEASE(&sc->aac_sync_lock);
1735cbfd045bSScott Long }
1736cbfd045bSScott Long 
1737cbfd045bSScott Long /*
173835863739SMike Smith  * Send a synchronous FIB to the controller and wait for a result.
173935863739SMike Smith  */
1740cbfd045bSScott Long int
174135863739SMike Smith aac_sync_fib(struct aac_softc *sc, u_int32_t command, u_int32_t xferstate,
1742cbfd045bSScott Long 		 struct aac_fib *fib, u_int16_t datasize)
174335863739SMike Smith {
174435863739SMike Smith 	debug_called(3);
174535863739SMike Smith 
174635863739SMike Smith 	if (datasize > AAC_FIB_DATASIZE)
174735863739SMike Smith 		return(EINVAL);
174835863739SMike Smith 
174935863739SMike Smith 	/*
175035863739SMike Smith 	 * Set up the sync FIB
175135863739SMike Smith 	 */
1752914da7d0SScott Long 	fib->Header.XferState = AAC_FIBSTATE_HOSTOWNED |
1753914da7d0SScott Long 				AAC_FIBSTATE_INITIALISED |
1754c6eafcf2SScott Long 				AAC_FIBSTATE_EMPTY;
175535863739SMike Smith 	fib->Header.XferState |= xferstate;
175635863739SMike Smith 	fib->Header.Command = command;
175735863739SMike Smith 	fib->Header.StructType = AAC_FIBTYPE_TFIB;
175835863739SMike Smith 	fib->Header.Size = sizeof(struct aac_fib) + datasize;
175935863739SMike Smith 	fib->Header.SenderSize = sizeof(struct aac_fib);
1760b88ffdc8SScott Long 	fib->Header.SenderFibAddress = 0;	/* Not needed */
1761c6eafcf2SScott Long 	fib->Header.ReceiverFibAddress = sc->aac_common_busaddr +
1762914da7d0SScott Long 					 offsetof(struct aac_common,
1763914da7d0SScott Long 						  ac_sync_fib);
176435863739SMike Smith 
176535863739SMike Smith 	/*
176635863739SMike Smith 	 * Give the FIB to the controller, wait for a response.
176735863739SMike Smith 	 */
1768914da7d0SScott Long 	if (aac_sync_command(sc, AAC_MONKER_SYNCFIB,
1769914da7d0SScott Long 			     fib->Header.ReceiverFibAddress, 0, 0, 0, NULL)) {
177035863739SMike Smith 		debug(2, "IO error");
177135863739SMike Smith 		return(EIO);
177235863739SMike Smith 	}
177335863739SMike Smith 
177435863739SMike Smith 	return (0);
177535863739SMike Smith }
177635863739SMike Smith 
1777914da7d0SScott Long /*
177835863739SMike Smith  * Adapter-space FIB queue manipulation
177935863739SMike Smith  *
178035863739SMike Smith  * Note that the queue implementation here is a little funky; neither the PI or
178135863739SMike Smith  * CI will ever be zero.  This behaviour is a controller feature.
178235863739SMike Smith  */
178335863739SMike Smith static struct {
178435863739SMike Smith 	int		size;
178535863739SMike Smith 	int		notify;
178635863739SMike Smith } aac_qinfo[] = {
178735863739SMike Smith 	{AAC_HOST_NORM_CMD_ENTRIES, AAC_DB_COMMAND_NOT_FULL},
178835863739SMike Smith 	{AAC_HOST_HIGH_CMD_ENTRIES, 0},
178935863739SMike Smith 	{AAC_ADAP_NORM_CMD_ENTRIES, AAC_DB_COMMAND_READY},
179035863739SMike Smith 	{AAC_ADAP_HIGH_CMD_ENTRIES, 0},
179135863739SMike Smith 	{AAC_HOST_NORM_RESP_ENTRIES, AAC_DB_RESPONSE_NOT_FULL},
179235863739SMike Smith 	{AAC_HOST_HIGH_RESP_ENTRIES, 0},
179335863739SMike Smith 	{AAC_ADAP_NORM_RESP_ENTRIES, AAC_DB_RESPONSE_READY},
179435863739SMike Smith 	{AAC_ADAP_HIGH_RESP_ENTRIES, 0}
179535863739SMike Smith };
179635863739SMike Smith 
179735863739SMike Smith /*
1798c6eafcf2SScott Long  * Atomically insert an entry into the nominated queue, returns 0 on success or
1799c6eafcf2SScott Long  * EBUSY if the queue is full.
180035863739SMike Smith  *
18010b94a66eSMike Smith  * Note: it would be more efficient to defer notifying the controller in
1802914da7d0SScott Long  *	 the case where we may be inserting several entries in rapid succession,
1803914da7d0SScott Long  *	 but implementing this usefully may be difficult (it would involve a
1804c6eafcf2SScott Long  *	 separate queue/notify interface).
180535863739SMike Smith  */
180635863739SMike Smith static int
1807f6c4dd3fSScott Long aac_enqueue_fib(struct aac_softc *sc, int queue, struct aac_command *cm)
180835863739SMike Smith {
180935863739SMike Smith 	u_int32_t pi, ci;
18109e2e96d8SScott Long 	int error;
1811f6c4dd3fSScott Long 	u_int32_t fib_size;
1812f6c4dd3fSScott Long 	u_int32_t fib_addr;
1813f6c4dd3fSScott Long 
181436e0bf6eSScott Long 	debug_called(3);
181536e0bf6eSScott Long 
1816f6c4dd3fSScott Long 	fib_size = cm->cm_fib->Header.Size;
1817f6c4dd3fSScott Long 	fib_addr = cm->cm_fib->Header.ReceiverFibAddress;
181835863739SMike Smith 
181935863739SMike Smith 	/* get the producer/consumer indices */
182035863739SMike Smith 	pi = sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX];
182135863739SMike Smith 	ci = sc->aac_queues->qt_qindex[queue][AAC_CONSUMER_INDEX];
182235863739SMike Smith 
182335863739SMike Smith 	/* wrap the queue? */
182435863739SMike Smith 	if (pi >= aac_qinfo[queue].size)
182535863739SMike Smith 		pi = 0;
182635863739SMike Smith 
182735863739SMike Smith 	/* check for queue full */
182835863739SMike Smith 	if ((pi + 1) == ci) {
182935863739SMike Smith 		error = EBUSY;
183035863739SMike Smith 		goto out;
183135863739SMike Smith 	}
183235863739SMike Smith 
183335863739SMike Smith 	/* populate queue entry */
183435863739SMike Smith 	(sc->aac_qentries[queue] + pi)->aq_fib_size = fib_size;
183535863739SMike Smith 	(sc->aac_qentries[queue] + pi)->aq_fib_addr = fib_addr;
183635863739SMike Smith 
183735863739SMike Smith 	/* update producer index */
183835863739SMike Smith 	sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX] = pi + 1;
183935863739SMike Smith 
1840f6c4dd3fSScott Long 	/*
1841914da7d0SScott Long 	 * To avoid a race with its completion interrupt, place this command on
1842914da7d0SScott Long 	 * the busy queue prior to advertising it to the controller.
1843f6c4dd3fSScott Long 	 */
1844f6c4dd3fSScott Long 	aac_enqueue_busy(cm);
1845f6c4dd3fSScott Long 
184635863739SMike Smith 	/* notify the adapter if we know how */
184735863739SMike Smith 	if (aac_qinfo[queue].notify != 0)
184835863739SMike Smith 		AAC_QNOTIFY(sc, aac_qinfo[queue].notify);
184935863739SMike Smith 
185035863739SMike Smith 	error = 0;
185135863739SMike Smith 
185235863739SMike Smith out:
185335863739SMike Smith 	return(error);
185435863739SMike Smith }
185535863739SMike Smith 
185635863739SMike Smith /*
185736e0bf6eSScott Long  * Atomically remove one entry from the nominated queue, returns 0 on
185836e0bf6eSScott Long  * success or ENOENT if the queue is empty.
185935863739SMike Smith  */
186035863739SMike Smith static int
1861c6eafcf2SScott Long aac_dequeue_fib(struct aac_softc *sc, int queue, u_int32_t *fib_size,
1862c6eafcf2SScott Long 		struct aac_fib **fib_addr)
186335863739SMike Smith {
186435863739SMike Smith 	u_int32_t pi, ci;
1865149af931SScott Long 	u_int32_t fib_index;
18669e2e96d8SScott Long 	int error;
1867f6c4dd3fSScott Long 	int notify;
186835863739SMike Smith 
186935863739SMike Smith 	debug_called(3);
187035863739SMike Smith 
187135863739SMike Smith 	/* get the producer/consumer indices */
187235863739SMike Smith 	pi = sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX];
187335863739SMike Smith 	ci = sc->aac_queues->qt_qindex[queue][AAC_CONSUMER_INDEX];
187435863739SMike Smith 
187535863739SMike Smith 	/* check for queue empty */
187635863739SMike Smith 	if (ci == pi) {
187735863739SMike Smith 		error = ENOENT;
187835863739SMike Smith 		goto out;
187935863739SMike Smith 	}
188035863739SMike Smith 
18817753acd2SScott Long 	/* wrap the pi so the following test works */
18827753acd2SScott Long 	if (pi >= aac_qinfo[queue].size)
18837753acd2SScott Long 		pi = 0;
18847753acd2SScott Long 
1885f6c4dd3fSScott Long 	notify = 0;
1886f6c4dd3fSScott Long 	if (ci == pi + 1)
1887f6c4dd3fSScott Long 		notify++;
1888f6c4dd3fSScott Long 
188935863739SMike Smith 	/* wrap the queue? */
189035863739SMike Smith 	if (ci >= aac_qinfo[queue].size)
189135863739SMike Smith 		ci = 0;
189235863739SMike Smith 
189335863739SMike Smith 	/* fetch the entry */
189435863739SMike Smith 	*fib_size = (sc->aac_qentries[queue] + ci)->aq_fib_size;
1895149af931SScott Long 
1896149af931SScott Long 	switch (queue) {
1897149af931SScott Long 	case AAC_HOST_NORM_CMD_QUEUE:
1898149af931SScott Long 	case AAC_HOST_HIGH_CMD_QUEUE:
1899149af931SScott Long 		/*
1900149af931SScott Long 		 * The aq_fib_addr is only 32 bits wide so it can't be counted
1901149af931SScott Long 		 * on to hold an address.  For AIF's, the adapter assumes
1902149af931SScott Long 		 * that it's giving us an address into the array of AIF fibs.
1903149af931SScott Long 		 * Therefore, we have to convert it to an index.
1904149af931SScott Long 		 */
1905149af931SScott Long 		fib_index = (sc->aac_qentries[queue] + ci)->aq_fib_addr /
1906149af931SScott Long 			sizeof(struct aac_fib);
1907149af931SScott Long 		*fib_addr = &sc->aac_common->ac_fibs[fib_index];
1908149af931SScott Long 		break;
1909149af931SScott Long 
1910149af931SScott Long 	case AAC_HOST_NORM_RESP_QUEUE:
1911149af931SScott Long 	case AAC_HOST_HIGH_RESP_QUEUE:
1912149af931SScott Long 	{
1913149af931SScott Long 		struct aac_command *cm;
1914149af931SScott Long 
1915149af931SScott Long 		/*
1916149af931SScott Long 		 * As above, an index is used instead of an actual address.
1917149af931SScott Long 		 * Gotta shift the index to account for the fast response
1918149af931SScott Long 		 * bit.  No other correction is needed since this value was
1919149af931SScott Long 		 * originally provided by the driver via the SenderFibAddress
1920149af931SScott Long 		 * field.
1921149af931SScott Long 		 */
1922149af931SScott Long 		fib_index = (sc->aac_qentries[queue] + ci)->aq_fib_addr;
1923149af931SScott Long 		cm = sc->aac_commands + (fib_index >> 1);
1924149af931SScott Long 		*fib_addr = cm->cm_fib;
192535863739SMike Smith 
1926f30ac74cSScott Long 		/*
1927f30ac74cSScott Long 		 * Is this a fast response? If it is, update the fib fields in
1928149af931SScott Long 		 * local memory since the whole fib isn't DMA'd back up.
1929f30ac74cSScott Long 		 */
1930149af931SScott Long 		if (fib_index & 0x01) {
1931f30ac74cSScott Long 			(*fib_addr)->Header.XferState |= AAC_FIBSTATE_DONEADAP;
1932f30ac74cSScott Long 			*((u_int32_t*)((*fib_addr)->data)) = AAC_ERROR_NORMAL;
1933f30ac74cSScott Long 		}
1934149af931SScott Long 		break;
1935149af931SScott Long 	}
1936149af931SScott Long 	default:
1937149af931SScott Long 		panic("Invalid queue in aac_dequeue_fib()");
1938149af931SScott Long 		break;
1939149af931SScott Long 	}
1940149af931SScott Long 
194135863739SMike Smith 	/* update consumer index */
194235863739SMike Smith 	sc->aac_queues->qt_qindex[queue][AAC_CONSUMER_INDEX] = ci + 1;
194335863739SMike Smith 
194435863739SMike Smith 	/* if we have made the queue un-full, notify the adapter */
1945f6c4dd3fSScott Long 	if (notify && (aac_qinfo[queue].notify != 0))
194635863739SMike Smith 		AAC_QNOTIFY(sc, aac_qinfo[queue].notify);
194735863739SMike Smith 	error = 0;
194835863739SMike Smith 
194935863739SMike Smith out:
195035863739SMike Smith 	return(error);
195135863739SMike Smith }
195235863739SMike Smith 
1953914da7d0SScott Long /*
195436e0bf6eSScott Long  * Put our response to an Adapter Initialed Fib on the response queue
195536e0bf6eSScott Long  */
195636e0bf6eSScott Long static int
195736e0bf6eSScott Long aac_enqueue_response(struct aac_softc *sc, int queue, struct aac_fib *fib)
195836e0bf6eSScott Long {
195936e0bf6eSScott Long 	u_int32_t pi, ci;
19609e2e96d8SScott Long 	int error;
196136e0bf6eSScott Long 	u_int32_t fib_size;
196236e0bf6eSScott Long 	u_int32_t fib_addr;
196336e0bf6eSScott Long 
196436e0bf6eSScott Long 	debug_called(1);
196536e0bf6eSScott Long 
196636e0bf6eSScott Long 	/* Tell the adapter where the FIB is */
196736e0bf6eSScott Long 	fib_size = fib->Header.Size;
196836e0bf6eSScott Long 	fib_addr = fib->Header.SenderFibAddress;
196936e0bf6eSScott Long 	fib->Header.ReceiverFibAddress = fib_addr;
197036e0bf6eSScott Long 
197136e0bf6eSScott Long 	/* get the producer/consumer indices */
197236e0bf6eSScott Long 	pi = sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX];
197336e0bf6eSScott Long 	ci = sc->aac_queues->qt_qindex[queue][AAC_CONSUMER_INDEX];
197436e0bf6eSScott Long 
197536e0bf6eSScott Long 	/* wrap the queue? */
197636e0bf6eSScott Long 	if (pi >= aac_qinfo[queue].size)
197736e0bf6eSScott Long 		pi = 0;
197836e0bf6eSScott Long 
197936e0bf6eSScott Long 	/* check for queue full */
198036e0bf6eSScott Long 	if ((pi + 1) == ci) {
198136e0bf6eSScott Long 		error = EBUSY;
198236e0bf6eSScott Long 		goto out;
198336e0bf6eSScott Long 	}
198436e0bf6eSScott Long 
198536e0bf6eSScott Long 	/* populate queue entry */
198636e0bf6eSScott Long 	(sc->aac_qentries[queue] + pi)->aq_fib_size = fib_size;
198736e0bf6eSScott Long 	(sc->aac_qentries[queue] + pi)->aq_fib_addr = fib_addr;
198836e0bf6eSScott Long 
198936e0bf6eSScott Long 	/* update producer index */
199036e0bf6eSScott Long 	sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX] = pi + 1;
199136e0bf6eSScott Long 
199236e0bf6eSScott Long 	/* notify the adapter if we know how */
199336e0bf6eSScott Long 	if (aac_qinfo[queue].notify != 0)
199436e0bf6eSScott Long 		AAC_QNOTIFY(sc, aac_qinfo[queue].notify);
199536e0bf6eSScott Long 
199636e0bf6eSScott Long 	error = 0;
199736e0bf6eSScott Long 
199836e0bf6eSScott Long out:
199936e0bf6eSScott Long 	return(error);
200036e0bf6eSScott Long }
200136e0bf6eSScott Long 
2002914da7d0SScott Long /*
20030b94a66eSMike Smith  * Check for commands that have been outstanding for a suspiciously long time,
20040b94a66eSMike Smith  * and complain about them.
20050b94a66eSMike Smith  */
20060b94a66eSMike Smith static void
20070b94a66eSMike Smith aac_timeout(struct aac_softc *sc)
20080b94a66eSMike Smith {
20090b94a66eSMike Smith 	struct aac_command *cm;
20100b94a66eSMike Smith 	time_t deadline;
20110b94a66eSMike Smith 
2012f6c4dd3fSScott Long 	/*
201370545d1aSScott Long 	 * Traverse the busy command list, bitch about late commands once
2014914da7d0SScott Long 	 * only.
2015914da7d0SScott Long 	 */
20160b94a66eSMike Smith 	deadline = time_second - AAC_CMD_TIMEOUT;
20170b94a66eSMike Smith 	TAILQ_FOREACH(cm, &sc->aac_busy, cm_link) {
2018f6c4dd3fSScott Long 		if ((cm->cm_timestamp  < deadline)
2019f6c4dd3fSScott Long 			/* && !(cm->cm_flags & AAC_CMD_TIMEDOUT) */) {
20200b94a66eSMike Smith 			cm->cm_flags |= AAC_CMD_TIMEDOUT;
2021914da7d0SScott Long 			device_printf(sc->aac_dev,
2022914da7d0SScott Long 				      "COMMAND %p TIMEOUT AFTER %d SECONDS\n",
2023f6c4dd3fSScott Long 				      cm, (int)(time_second-cm->cm_timestamp));
20240b94a66eSMike Smith 			AAC_PRINT_FIB(sc, cm->cm_fib);
20250b94a66eSMike Smith 		}
20260b94a66eSMike Smith 	}
20270b94a66eSMike Smith 
20280b94a66eSMike Smith 	return;
20290b94a66eSMike Smith }
20300b94a66eSMike Smith 
2031914da7d0SScott Long /*
2032914da7d0SScott Long  * Interface Function Vectors
2033914da7d0SScott Long  */
203435863739SMike Smith 
2035914da7d0SScott Long /*
203635863739SMike Smith  * Read the current firmware status word.
203735863739SMike Smith  */
203835863739SMike Smith static int
203935863739SMike Smith aac_sa_get_fwstatus(struct aac_softc *sc)
204035863739SMike Smith {
204135863739SMike Smith 	debug_called(3);
204235863739SMike Smith 
204335863739SMike Smith 	return(AAC_GETREG4(sc, AAC_SA_FWSTATUS));
204435863739SMike Smith }
204535863739SMike Smith 
204635863739SMike Smith static int
204735863739SMike Smith aac_rx_get_fwstatus(struct aac_softc *sc)
204835863739SMike Smith {
204935863739SMike Smith 	debug_called(3);
205035863739SMike Smith 
205135863739SMike Smith 	return(AAC_GETREG4(sc, AAC_RX_FWSTATUS));
205235863739SMike Smith }
205335863739SMike Smith 
2054b3457b51SScott Long static int
2055b3457b51SScott Long aac_fa_get_fwstatus(struct aac_softc *sc)
2056b3457b51SScott Long {
2057b3457b51SScott Long 	int val;
2058b3457b51SScott Long 
2059b3457b51SScott Long 	debug_called(3);
2060b3457b51SScott Long 
2061b3457b51SScott Long 	val = AAC_GETREG4(sc, AAC_FA_FWSTATUS);
2062b3457b51SScott Long 	return (val);
2063b3457b51SScott Long }
2064b3457b51SScott Long 
2065914da7d0SScott Long /*
206635863739SMike Smith  * Notify the controller of a change in a given queue
206735863739SMike Smith  */
206835863739SMike Smith 
206935863739SMike Smith static void
207035863739SMike Smith aac_sa_qnotify(struct aac_softc *sc, int qbit)
207135863739SMike Smith {
207235863739SMike Smith 	debug_called(3);
207335863739SMike Smith 
207435863739SMike Smith 	AAC_SETREG2(sc, AAC_SA_DOORBELL1_SET, qbit);
207535863739SMike Smith }
207635863739SMike Smith 
207735863739SMike Smith static void
207835863739SMike Smith aac_rx_qnotify(struct aac_softc *sc, int qbit)
207935863739SMike Smith {
208035863739SMike Smith 	debug_called(3);
208135863739SMike Smith 
208235863739SMike Smith 	AAC_SETREG4(sc, AAC_RX_IDBR, qbit);
208335863739SMike Smith }
208435863739SMike Smith 
2085b3457b51SScott Long static void
2086b3457b51SScott Long aac_fa_qnotify(struct aac_softc *sc, int qbit)
2087b3457b51SScott Long {
2088b3457b51SScott Long 	debug_called(3);
2089b3457b51SScott Long 
2090b3457b51SScott Long 	AAC_SETREG2(sc, AAC_FA_DOORBELL1, qbit);
2091b3457b51SScott Long 	AAC_FA_HACK(sc);
2092b3457b51SScott Long }
2093b3457b51SScott Long 
2094914da7d0SScott Long /*
209535863739SMike Smith  * Get the interrupt reason bits
209635863739SMike Smith  */
209735863739SMike Smith static int
209835863739SMike Smith aac_sa_get_istatus(struct aac_softc *sc)
209935863739SMike Smith {
210035863739SMike Smith 	debug_called(3);
210135863739SMike Smith 
210235863739SMike Smith 	return(AAC_GETREG2(sc, AAC_SA_DOORBELL0));
210335863739SMike Smith }
210435863739SMike Smith 
210535863739SMike Smith static int
210635863739SMike Smith aac_rx_get_istatus(struct aac_softc *sc)
210735863739SMike Smith {
210835863739SMike Smith 	debug_called(3);
210935863739SMike Smith 
211035863739SMike Smith 	return(AAC_GETREG4(sc, AAC_RX_ODBR));
211135863739SMike Smith }
211235863739SMike Smith 
2113b3457b51SScott Long static int
2114b3457b51SScott Long aac_fa_get_istatus(struct aac_softc *sc)
2115b3457b51SScott Long {
2116b3457b51SScott Long 	int val;
2117b3457b51SScott Long 
2118b3457b51SScott Long 	debug_called(3);
2119b3457b51SScott Long 
2120b3457b51SScott Long 	val = AAC_GETREG2(sc, AAC_FA_DOORBELL0);
2121b3457b51SScott Long 	return (val);
2122b3457b51SScott Long }
2123b3457b51SScott Long 
2124914da7d0SScott Long /*
212535863739SMike Smith  * Clear some interrupt reason bits
212635863739SMike Smith  */
212735863739SMike Smith static void
212835863739SMike Smith aac_sa_clear_istatus(struct aac_softc *sc, int mask)
212935863739SMike Smith {
213035863739SMike Smith 	debug_called(3);
213135863739SMike Smith 
213235863739SMike Smith 	AAC_SETREG2(sc, AAC_SA_DOORBELL0_CLEAR, mask);
213335863739SMike Smith }
213435863739SMike Smith 
213535863739SMike Smith static void
213635863739SMike Smith aac_rx_clear_istatus(struct aac_softc *sc, int mask)
213735863739SMike Smith {
213835863739SMike Smith 	debug_called(3);
213935863739SMike Smith 
214035863739SMike Smith 	AAC_SETREG4(sc, AAC_RX_ODBR, mask);
214135863739SMike Smith }
214235863739SMike Smith 
2143b3457b51SScott Long static void
2144b3457b51SScott Long aac_fa_clear_istatus(struct aac_softc *sc, int mask)
2145b3457b51SScott Long {
2146b3457b51SScott Long 	debug_called(3);
2147b3457b51SScott Long 
2148b3457b51SScott Long 	AAC_SETREG2(sc, AAC_FA_DOORBELL0_CLEAR, mask);
2149b3457b51SScott Long 	AAC_FA_HACK(sc);
2150b3457b51SScott Long }
2151b3457b51SScott Long 
2152914da7d0SScott Long /*
215335863739SMike Smith  * Populate the mailbox and set the command word
215435863739SMike Smith  */
215535863739SMike Smith static void
215635863739SMike Smith aac_sa_set_mailbox(struct aac_softc *sc, u_int32_t command,
215735863739SMike Smith 		u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3)
215835863739SMike Smith {
215935863739SMike Smith 	debug_called(4);
216035863739SMike Smith 
216135863739SMike Smith 	AAC_SETREG4(sc, AAC_SA_MAILBOX, command);
216235863739SMike Smith 	AAC_SETREG4(sc, AAC_SA_MAILBOX + 4, arg0);
216335863739SMike Smith 	AAC_SETREG4(sc, AAC_SA_MAILBOX + 8, arg1);
216435863739SMike Smith 	AAC_SETREG4(sc, AAC_SA_MAILBOX + 12, arg2);
216535863739SMike Smith 	AAC_SETREG4(sc, AAC_SA_MAILBOX + 16, arg3);
216635863739SMike Smith }
216735863739SMike Smith 
216835863739SMike Smith static void
216935863739SMike Smith aac_rx_set_mailbox(struct aac_softc *sc, u_int32_t command,
217035863739SMike Smith 		u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3)
217135863739SMike Smith {
217235863739SMike Smith 	debug_called(4);
217335863739SMike Smith 
217435863739SMike Smith 	AAC_SETREG4(sc, AAC_RX_MAILBOX, command);
217535863739SMike Smith 	AAC_SETREG4(sc, AAC_RX_MAILBOX + 4, arg0);
217635863739SMike Smith 	AAC_SETREG4(sc, AAC_RX_MAILBOX + 8, arg1);
217735863739SMike Smith 	AAC_SETREG4(sc, AAC_RX_MAILBOX + 12, arg2);
217835863739SMike Smith 	AAC_SETREG4(sc, AAC_RX_MAILBOX + 16, arg3);
217935863739SMike Smith }
218035863739SMike Smith 
2181b3457b51SScott Long static void
2182b3457b51SScott Long aac_fa_set_mailbox(struct aac_softc *sc, u_int32_t command,
2183b3457b51SScott Long 		u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3)
2184b3457b51SScott Long {
2185b3457b51SScott Long 	debug_called(4);
2186b3457b51SScott Long 
2187b3457b51SScott Long 	AAC_SETREG4(sc, AAC_FA_MAILBOX, command);
2188b3457b51SScott Long 	AAC_FA_HACK(sc);
2189b3457b51SScott Long 	AAC_SETREG4(sc, AAC_FA_MAILBOX + 4, arg0);
2190b3457b51SScott Long 	AAC_FA_HACK(sc);
2191b3457b51SScott Long 	AAC_SETREG4(sc, AAC_FA_MAILBOX + 8, arg1);
2192b3457b51SScott Long 	AAC_FA_HACK(sc);
2193b3457b51SScott Long 	AAC_SETREG4(sc, AAC_FA_MAILBOX + 12, arg2);
2194b3457b51SScott Long 	AAC_FA_HACK(sc);
2195b3457b51SScott Long 	AAC_SETREG4(sc, AAC_FA_MAILBOX + 16, arg3);
2196b3457b51SScott Long 	AAC_FA_HACK(sc);
2197b3457b51SScott Long }
2198b3457b51SScott Long 
2199914da7d0SScott Long /*
220035863739SMike Smith  * Fetch the immediate command status word
220135863739SMike Smith  */
220235863739SMike Smith static int
2203a6d35632SScott Long aac_sa_get_mailbox(struct aac_softc *sc, int mb)
220435863739SMike Smith {
220535863739SMike Smith 	debug_called(4);
220635863739SMike Smith 
2207a6d35632SScott Long 	return(AAC_GETREG4(sc, AAC_SA_MAILBOX + (mb * 4)));
220835863739SMike Smith }
220935863739SMike Smith 
221035863739SMike Smith static int
2211a6d35632SScott Long aac_rx_get_mailbox(struct aac_softc *sc, int mb)
221235863739SMike Smith {
221335863739SMike Smith 	debug_called(4);
221435863739SMike Smith 
2215a6d35632SScott Long 	return(AAC_GETREG4(sc, AAC_RX_MAILBOX + (mb * 4)));
221635863739SMike Smith }
221735863739SMike Smith 
2218b3457b51SScott Long static int
2219a6d35632SScott Long aac_fa_get_mailbox(struct aac_softc *sc, int mb)
2220b3457b51SScott Long {
2221b3457b51SScott Long 	int val;
2222b3457b51SScott Long 
2223b3457b51SScott Long 	debug_called(4);
2224b3457b51SScott Long 
2225a6d35632SScott Long 	val = AAC_GETREG4(sc, AAC_FA_MAILBOX + (mb * 4));
2226b3457b51SScott Long 	return (val);
2227b3457b51SScott Long }
2228b3457b51SScott Long 
2229914da7d0SScott Long /*
223035863739SMike Smith  * Set/clear interrupt masks
223135863739SMike Smith  */
223235863739SMike Smith static void
223335863739SMike Smith aac_sa_set_interrupts(struct aac_softc *sc, int enable)
223435863739SMike Smith {
223535863739SMike Smith 	debug(2, "%sable interrupts", enable ? "en" : "dis");
223635863739SMike Smith 
223735863739SMike Smith 	if (enable) {
223835863739SMike Smith 		AAC_SETREG2((sc), AAC_SA_MASK0_CLEAR, AAC_DB_INTERRUPTS);
223935863739SMike Smith 	} else {
224035863739SMike Smith 		AAC_SETREG2((sc), AAC_SA_MASK0_SET, ~0);
224135863739SMike Smith 	}
224235863739SMike Smith }
224335863739SMike Smith 
224435863739SMike Smith static void
224535863739SMike Smith aac_rx_set_interrupts(struct aac_softc *sc, int enable)
224635863739SMike Smith {
224735863739SMike Smith 	debug(2, "%sable interrupts", enable ? "en" : "dis");
224835863739SMike Smith 
224935863739SMike Smith 	if (enable) {
225035863739SMike Smith 		AAC_SETREG4(sc, AAC_RX_OIMR, ~AAC_DB_INTERRUPTS);
225135863739SMike Smith 	} else {
225235863739SMike Smith 		AAC_SETREG4(sc, AAC_RX_OIMR, ~0);
225335863739SMike Smith 	}
225435863739SMike Smith }
225535863739SMike Smith 
2256b3457b51SScott Long static void
2257b3457b51SScott Long aac_fa_set_interrupts(struct aac_softc *sc, int enable)
2258b3457b51SScott Long {
2259b3457b51SScott Long 	debug(2, "%sable interrupts", enable ? "en" : "dis");
2260b3457b51SScott Long 
2261b3457b51SScott Long 	if (enable) {
2262b3457b51SScott Long 		AAC_SETREG2((sc), AAC_FA_MASK0_CLEAR, AAC_DB_INTERRUPTS);
2263b3457b51SScott Long 		AAC_FA_HACK(sc);
2264b3457b51SScott Long 	} else {
2265b3457b51SScott Long 		AAC_SETREG2((sc), AAC_FA_MASK0, ~0);
2266b3457b51SScott Long 		AAC_FA_HACK(sc);
2267b3457b51SScott Long 	}
2268b3457b51SScott Long }
2269b3457b51SScott Long 
2270914da7d0SScott Long /*
2271914da7d0SScott Long  * Debugging and Diagnostics
2272914da7d0SScott Long  */
227335863739SMike Smith 
2274914da7d0SScott Long /*
227535863739SMike Smith  * Print some information about the controller.
227635863739SMike Smith  */
227735863739SMike Smith static void
227835863739SMike Smith aac_describe_controller(struct aac_softc *sc)
227935863739SMike Smith {
2280cbfd045bSScott Long 	struct aac_fib *fib;
228135863739SMike Smith 	struct aac_adapter_info	*info;
228235863739SMike Smith 
228335863739SMike Smith 	debug_called(2);
228435863739SMike Smith 
2285fe3cb0e1SScott Long 	aac_alloc_sync_fib(sc, &fib, 0);
2286cbfd045bSScott Long 
2287cbfd045bSScott Long 	fib->data[0] = 0;
2288cbfd045bSScott Long 	if (aac_sync_fib(sc, RequestAdapterInfo, 0, fib, 1)) {
228935863739SMike Smith 		device_printf(sc->aac_dev, "RequestAdapterInfo failed\n");
2290fe3cb0e1SScott Long 		aac_release_sync_fib(sc);
229135863739SMike Smith 		return;
229235863739SMike Smith 	}
2293cbfd045bSScott Long 	info = (struct aac_adapter_info *)&fib->data[0];
229435863739SMike Smith 
229536e0bf6eSScott Long 	device_printf(sc->aac_dev, "%s %dMHz, %dMB cache memory, %s\n",
2296c6eafcf2SScott Long 		      aac_describe_code(aac_cpu_variant, info->CpuVariant),
229736e0bf6eSScott Long 		      info->ClockSpeed, info->BufferMem / (1024 * 1024),
2298914da7d0SScott Long 		      aac_describe_code(aac_battery_platform,
2299914da7d0SScott Long 					info->batteryPlatform));
230035863739SMike Smith 
230135863739SMike Smith 	/* save the kernel revision structure for later use */
230235863739SMike Smith 	sc->aac_revision = info->KernelRevision;
230336e0bf6eSScott Long 	device_printf(sc->aac_dev, "Kernel %d.%d-%d, Build %d, S/N %6X\n",
230435863739SMike Smith 		      info->KernelRevision.external.comp.major,
230535863739SMike Smith 		      info->KernelRevision.external.comp.minor,
230635863739SMike Smith 		      info->KernelRevision.external.comp.dash,
230736e0bf6eSScott Long 		      info->KernelRevision.buildNumber,
230836e0bf6eSScott Long 		      (u_int32_t)(info->SerialNumber & 0xffffff));
2309fe3cb0e1SScott Long 
2310fe3cb0e1SScott Long 	aac_release_sync_fib(sc);
2311a6d35632SScott Long 
2312a6d35632SScott Long 	if (1 || bootverbose) {
2313a6d35632SScott Long 		device_printf(sc->aac_dev, "Supported Options=%b\n",
2314a6d35632SScott Long 			      sc->supported_options,
2315a6d35632SScott Long 			      "\20"
2316a6d35632SScott Long 			      "\1SNAPSHOT"
2317a6d35632SScott Long 			      "\2CLUSTERS"
2318a6d35632SScott Long 			      "\3WCACHE"
2319a6d35632SScott Long 			      "\4DATA64"
2320a6d35632SScott Long 			      "\5HOSTTIME"
2321a6d35632SScott Long 			      "\6RAID50"
2322a6d35632SScott Long 			      "\7WINDOW4GB"
2323a6d35632SScott Long 			      "\10SCSIUPGD"
2324a6d35632SScott Long 			      "\11SOFTERR"
2325a6d35632SScott Long 			      "\12NORECOND"
2326a6d35632SScott Long 			      "\13SGMAP64"
2327a6d35632SScott Long 			      "\14ALARM"
2328a6d35632SScott Long 			      "\15NONDASD");
2329a6d35632SScott Long 	}
233035863739SMike Smith }
233135863739SMike Smith 
2332914da7d0SScott Long /*
233335863739SMike Smith  * Look up a text description of a numeric error code and return a pointer to
233435863739SMike Smith  * same.
233535863739SMike Smith  */
233635863739SMike Smith static char *
233735863739SMike Smith aac_describe_code(struct aac_code_lookup *table, u_int32_t code)
233835863739SMike Smith {
233935863739SMike Smith 	int i;
234035863739SMike Smith 
234135863739SMike Smith 	for (i = 0; table[i].string != NULL; i++)
234235863739SMike Smith 		if (table[i].code == code)
234335863739SMike Smith 			return(table[i].string);
234435863739SMike Smith 	return(table[i + 1].string);
234535863739SMike Smith }
234635863739SMike Smith 
2347914da7d0SScott Long /*
2348914da7d0SScott Long  * Management Interface
2349914da7d0SScott Long  */
235035863739SMike Smith 
235135863739SMike Smith static int
2352c3d15322SScott Long aac_open(dev_t dev, int flags, int fmt, d_thread_t *td)
235335863739SMike Smith {
2354914da7d0SScott Long 	struct aac_softc *sc;
235535863739SMike Smith 
235635863739SMike Smith 	debug_called(2);
235735863739SMike Smith 
2358914da7d0SScott Long 	sc = dev->si_drv1;
2359914da7d0SScott Long 
236035863739SMike Smith 	/* Check to make sure the device isn't already open */
236135863739SMike Smith 	if (sc->aac_state & AAC_STATE_OPEN) {
236235863739SMike Smith 		return EBUSY;
236335863739SMike Smith 	}
236435863739SMike Smith 	sc->aac_state |= AAC_STATE_OPEN;
236535863739SMike Smith 
236635863739SMike Smith 	return 0;
236735863739SMike Smith }
236835863739SMike Smith 
236935863739SMike Smith static int
2370c3d15322SScott Long aac_close(dev_t dev, int flags, int fmt, d_thread_t *td)
237135863739SMike Smith {
2372914da7d0SScott Long 	struct aac_softc *sc;
237335863739SMike Smith 
237435863739SMike Smith 	debug_called(2);
237535863739SMike Smith 
2376914da7d0SScott Long 	sc = dev->si_drv1;
2377914da7d0SScott Long 
237835863739SMike Smith 	/* Mark this unit as no longer open  */
237935863739SMike Smith 	sc->aac_state &= ~AAC_STATE_OPEN;
238035863739SMike Smith 
238135863739SMike Smith 	return 0;
238235863739SMike Smith }
238335863739SMike Smith 
238435863739SMike Smith static int
2385c3d15322SScott Long aac_ioctl(dev_t dev, u_long cmd, caddr_t arg, int flag, d_thread_t *td)
238635863739SMike Smith {
2387914da7d0SScott Long 	union aac_statrequest *as;
2388914da7d0SScott Long 	struct aac_softc *sc;
23890b94a66eSMike Smith 	int error = 0;
2390b88ffdc8SScott Long 	uint32_t cookie;
239135863739SMike Smith 
239235863739SMike Smith 	debug_called(2);
239335863739SMike Smith 
2394914da7d0SScott Long 	as = (union aac_statrequest *)arg;
2395914da7d0SScott Long 	sc = dev->si_drv1;
2396914da7d0SScott Long 
239735863739SMike Smith 	switch (cmd) {
23980b94a66eSMike Smith 	case AACIO_STATS:
23990b94a66eSMike Smith 		switch (as->as_item) {
24000b94a66eSMike Smith 		case AACQ_FREE:
24010b94a66eSMike Smith 		case AACQ_BIO:
24020b94a66eSMike Smith 		case AACQ_READY:
24030b94a66eSMike Smith 		case AACQ_BUSY:
24040b94a66eSMike Smith 		case AACQ_COMPLETE:
2405c6eafcf2SScott Long 			bcopy(&sc->aac_qstat[as->as_item], &as->as_qstat,
2406c6eafcf2SScott Long 			      sizeof(struct aac_qstat));
24070b94a66eSMike Smith 			break;
24080b94a66eSMike Smith 		default:
24090b94a66eSMike Smith 			error = ENOENT;
24100b94a66eSMike Smith 			break;
24110b94a66eSMike Smith 		}
24120b94a66eSMike Smith 	break;
24130b94a66eSMike Smith 
241435863739SMike Smith 	case FSACTL_SENDFIB:
2415fb0c27d7SScott Long 		arg = *(caddr_t*)arg;
2416fb0c27d7SScott Long 	case FSACTL_LNX_SENDFIB:
24170b94a66eSMike Smith 		debug(1, "FSACTL_SENDFIB");
241835863739SMike Smith 		error = aac_ioctl_sendfib(sc, arg);
241935863739SMike Smith 		break;
242035863739SMike Smith 	case FSACTL_AIF_THREAD:
2421fb0c27d7SScott Long 	case FSACTL_LNX_AIF_THREAD:
24220b94a66eSMike Smith 		debug(1, "FSACTL_AIF_THREAD");
242335863739SMike Smith 		error = EINVAL;
242435863739SMike Smith 		break;
242535863739SMike Smith 	case FSACTL_OPEN_GET_ADAPTER_FIB:
2426fb0c27d7SScott Long 		arg = *(caddr_t*)arg;
2427fb0c27d7SScott Long 	case FSACTL_LNX_OPEN_GET_ADAPTER_FIB:
24280b94a66eSMike Smith 		debug(1, "FSACTL_OPEN_GET_ADAPTER_FIB");
242935863739SMike Smith 		/*
243035863739SMike Smith 		 * Pass the caller out an AdapterFibContext.
243135863739SMike Smith 		 *
243235863739SMike Smith 		 * Note that because we only support one opener, we
243335863739SMike Smith 		 * basically ignore this.  Set the caller's context to a magic
243435863739SMike Smith 		 * number just in case.
24350b94a66eSMike Smith 		 *
24360b94a66eSMike Smith 		 * The Linux code hands the driver a pointer into kernel space,
24370b94a66eSMike Smith 		 * and then trusts it when the caller hands it back.  Aiee!
2438914da7d0SScott Long 		 * Here, we give it the proc pointer of the per-adapter aif
2439914da7d0SScott Long 		 * thread. It's only used as a sanity check in other calls.
244035863739SMike Smith 		 */
2441b88ffdc8SScott Long 		cookie = (uint32_t)(uintptr_t)sc->aifthread;
2442b88ffdc8SScott Long 		error = copyout(&cookie, arg, sizeof(cookie));
244335863739SMike Smith 		break;
244435863739SMike Smith 	case FSACTL_GET_NEXT_ADAPTER_FIB:
2445fb0c27d7SScott Long 		arg = *(caddr_t*)arg;
2446fb0c27d7SScott Long 	case FSACTL_LNX_GET_NEXT_ADAPTER_FIB:
24470b94a66eSMike Smith 		debug(1, "FSACTL_GET_NEXT_ADAPTER_FIB");
2448fb0c27d7SScott Long 		error = aac_getnext_aif(sc, arg);
244935863739SMike Smith 		break;
245035863739SMike Smith 	case FSACTL_CLOSE_GET_ADAPTER_FIB:
2451fb0c27d7SScott Long 	case FSACTL_LNX_CLOSE_GET_ADAPTER_FIB:
24520b94a66eSMike Smith 		debug(1, "FSACTL_CLOSE_GET_ADAPTER_FIB");
245335863739SMike Smith 		/* don't do anything here */
245435863739SMike Smith 		break;
245535863739SMike Smith 	case FSACTL_MINIPORT_REV_CHECK:
2456fb0c27d7SScott Long 		arg = *(caddr_t*)arg;
2457fb0c27d7SScott Long 	case FSACTL_LNX_MINIPORT_REV_CHECK:
24580b94a66eSMike Smith 		debug(1, "FSACTL_MINIPORT_REV_CHECK");
2459fb0c27d7SScott Long 		error = aac_rev_check(sc, arg);
246035863739SMike Smith 		break;
246136e0bf6eSScott Long 	case FSACTL_QUERY_DISK:
246236e0bf6eSScott Long 		arg = *(caddr_t*)arg;
246336e0bf6eSScott Long 	case FSACTL_LNX_QUERY_DISK:
246436e0bf6eSScott Long 		debug(1, "FSACTL_QUERY_DISK");
246536e0bf6eSScott Long 		error = aac_query_disk(sc, arg);
246636e0bf6eSScott Long 			break;
246736e0bf6eSScott Long 	case FSACTL_DELETE_DISK:
246836e0bf6eSScott Long 	case FSACTL_LNX_DELETE_DISK:
2469914da7d0SScott Long 		/*
2470914da7d0SScott Long 		 * We don't trust the underland to tell us when to delete a
2471914da7d0SScott Long 		 * container, rather we rely on an AIF coming from the
2472914da7d0SScott Long 		 * controller
2473914da7d0SScott Long 		 */
247436e0bf6eSScott Long 		error = 0;
247536e0bf6eSScott Long 		break;
247635863739SMike Smith 	default:
2477b3457b51SScott Long 		debug(1, "unsupported cmd 0x%lx\n", cmd);
247835863739SMike Smith 		error = EINVAL;
247935863739SMike Smith 		break;
248035863739SMike Smith 	}
248135863739SMike Smith 	return(error);
248235863739SMike Smith }
248335863739SMike Smith 
2484b3457b51SScott Long static int
2485c3d15322SScott Long aac_poll(dev_t dev, int poll_events, d_thread_t *td)
2486b3457b51SScott Long {
2487b3457b51SScott Long 	struct aac_softc *sc;
2488b3457b51SScott Long 	int revents;
2489b3457b51SScott Long 
2490b3457b51SScott Long 	sc = dev->si_drv1;
2491b3457b51SScott Long 	revents = 0;
2492b3457b51SScott Long 
2493c3d15322SScott Long 	AAC_LOCK_ACQUIRE(&sc->aac_aifq_lock);
2494b3457b51SScott Long 	if ((poll_events & (POLLRDNORM | POLLIN)) != 0) {
2495b3457b51SScott Long 		if (sc->aac_aifq_tail != sc->aac_aifq_head)
2496b3457b51SScott Long 			revents |= poll_events & (POLLIN | POLLRDNORM);
2497b3457b51SScott Long 	}
2498b3457b51SScott Long 	AAC_LOCK_RELEASE(&sc->aac_aifq_lock);
2499b3457b51SScott Long 
2500b3457b51SScott Long 	if (revents == 0) {
2501b3457b51SScott Long 		if (poll_events & (POLLIN | POLLRDNORM))
2502b3457b51SScott Long 			selrecord(td, &sc->rcv_select);
2503b3457b51SScott Long 	}
2504b3457b51SScott Long 
2505b3457b51SScott Long 	return (revents);
2506b3457b51SScott Long }
2507b3457b51SScott Long 
2508914da7d0SScott Long /*
250935863739SMike Smith  * Send a FIB supplied from userspace
251035863739SMike Smith  */
251135863739SMike Smith static int
251235863739SMike Smith aac_ioctl_sendfib(struct aac_softc *sc, caddr_t ufib)
251335863739SMike Smith {
251435863739SMike Smith 	struct aac_command *cm;
251535863739SMike Smith 	int size, error;
251635863739SMike Smith 
251735863739SMike Smith 	debug_called(2);
251835863739SMike Smith 
251935863739SMike Smith 	cm = NULL;
252035863739SMike Smith 
252135863739SMike Smith 	/*
252235863739SMike Smith 	 * Get a command
252335863739SMike Smith 	 */
2524ae543596SScott Long 	AAC_LOCK_ACQUIRE(&sc->aac_io_lock);
252535863739SMike Smith 	if (aac_alloc_command(sc, &cm)) {
252635863739SMike Smith 		error = EBUSY;
252735863739SMike Smith 		goto out;
252835863739SMike Smith 	}
252935863739SMike Smith 
253035863739SMike Smith 	/*
253135863739SMike Smith 	 * Fetch the FIB header, then re-copy to get data as well.
253235863739SMike Smith 	 */
2533914da7d0SScott Long 	if ((error = copyin(ufib, cm->cm_fib,
2534914da7d0SScott Long 			    sizeof(struct aac_fib_header))) != 0)
253535863739SMike Smith 		goto out;
253635863739SMike Smith 	size = cm->cm_fib->Header.Size + sizeof(struct aac_fib_header);
253735863739SMike Smith 	if (size > sizeof(struct aac_fib)) {
2538b88ffdc8SScott Long 		device_printf(sc->aac_dev, "incoming FIB oversized (%d > %zd)\n",
2539914da7d0SScott Long 			      size, sizeof(struct aac_fib));
254035863739SMike Smith 		size = sizeof(struct aac_fib);
254135863739SMike Smith 	}
254235863739SMike Smith 	if ((error = copyin(ufib, cm->cm_fib, size)) != 0)
254335863739SMike Smith 		goto out;
254435863739SMike Smith 	cm->cm_fib->Header.Size = size;
2545f6c4dd3fSScott Long 	cm->cm_timestamp = time_second;
254635863739SMike Smith 
254735863739SMike Smith 	/*
254835863739SMike Smith 	 * Pass the FIB to the controller, wait for it to complete.
254935863739SMike Smith 	 */
2550b3457b51SScott Long 	if ((error = aac_wait_command(cm, 30)) != 0) {	/* XXX user timeout? */
255170545d1aSScott Long 		device_printf(sc->aac_dev,
255270545d1aSScott Long 			      "aac_wait_command return %d\n", error);
255335863739SMike Smith 		goto out;
2554b3457b51SScott Long 	}
255535863739SMike Smith 
255635863739SMike Smith 	/*
255735863739SMike Smith 	 * Copy the FIB and data back out to the caller.
255835863739SMike Smith 	 */
255935863739SMike Smith 	size = cm->cm_fib->Header.Size;
256035863739SMike Smith 	if (size > sizeof(struct aac_fib)) {
2561b88ffdc8SScott Long 		device_printf(sc->aac_dev, "outbound FIB oversized (%d > %zd)\n",
2562914da7d0SScott Long 			      size, sizeof(struct aac_fib));
256335863739SMike Smith 		size = sizeof(struct aac_fib);
256435863739SMike Smith 	}
256535863739SMike Smith 	error = copyout(cm->cm_fib, ufib, size);
256635863739SMike Smith 
256735863739SMike Smith out:
2568f6c4dd3fSScott Long 	if (cm != NULL) {
256935863739SMike Smith 		aac_release_command(cm);
2570f6c4dd3fSScott Long 	}
2571ae543596SScott Long 
2572ae543596SScott Long 	AAC_LOCK_RELEASE(&sc->aac_io_lock);
257335863739SMike Smith 	return(error);
257435863739SMike Smith }
257535863739SMike Smith 
2576914da7d0SScott Long /*
257735863739SMike Smith  * Handle an AIF sent to us by the controller; queue it for later reference.
257836e0bf6eSScott Long  * If the queue fills up, then drop the older entries.
257935863739SMike Smith  */
258035863739SMike Smith static void
258136e0bf6eSScott Long aac_handle_aif(struct aac_softc *sc, struct aac_fib *fib)
258235863739SMike Smith {
258336e0bf6eSScott Long 	struct aac_aif_command *aif;
258436e0bf6eSScott Long 	struct aac_container *co, *co_next;
2585cbfd045bSScott Long 	struct aac_mntinfo *mi;
2586cbfd045bSScott Long 	struct aac_mntinforesp *mir = NULL;
258736e0bf6eSScott Long 	u_int16_t rsize;
2588b3457b51SScott Long 	int next, found;
2589795d7dc0SScott Long 	int count = 0, added = 0, i = 0;
259035863739SMike Smith 
259135863739SMike Smith 	debug_called(2);
259235863739SMike Smith 
259336e0bf6eSScott Long 	aif = (struct aac_aif_command*)&fib->data[0];
259436e0bf6eSScott Long 	aac_print_aif(sc, aif);
259536e0bf6eSScott Long 
259636e0bf6eSScott Long 	/* Is it an event that we should care about? */
259736e0bf6eSScott Long 	switch (aif->command) {
259836e0bf6eSScott Long 	case AifCmdEventNotify:
259936e0bf6eSScott Long 		switch (aif->data.EN.type) {
260036e0bf6eSScott Long 		case AifEnAddContainer:
260136e0bf6eSScott Long 		case AifEnDeleteContainer:
260236e0bf6eSScott Long 			/*
2603914da7d0SScott Long 			 * A container was added or deleted, but the message
2604914da7d0SScott Long 			 * doesn't tell us anything else!  Re-enumerate the
2605914da7d0SScott Long 			 * containers and sort things out.
260636e0bf6eSScott Long 			 */
2607fe3cb0e1SScott Long 			aac_alloc_sync_fib(sc, &fib, 0);
2608cbfd045bSScott Long 			mi = (struct aac_mntinfo *)&fib->data[0];
260936e0bf6eSScott Long 			do {
261036e0bf6eSScott Long 				/*
2611914da7d0SScott Long 				 * Ask the controller for its containers one at
2612914da7d0SScott Long 				 * a time.
2613914da7d0SScott Long 				 * XXX What if the controller's list changes
2614914da7d0SScott Long 				 * midway through this enumaration?
261536e0bf6eSScott Long 				 * XXX This should be done async.
261636e0bf6eSScott Long 				 */
261739ee03c3SScott Long 				bzero(mi, sizeof(struct aac_mntinfo));
261839ee03c3SScott Long 				mi->Command = VM_NameServe;
261939ee03c3SScott Long 				mi->MntType = FT_FILESYS;
2620cbfd045bSScott Long 				mi->MntCount = i;
262136e0bf6eSScott Long 				rsize = sizeof(mir);
2622cbfd045bSScott Long 				if (aac_sync_fib(sc, ContainerCommand, 0, fib,
2623cbfd045bSScott Long 						 sizeof(struct aac_mntinfo))) {
2624795d7dc0SScott Long 					printf("Error probing container %d\n",
2625914da7d0SScott Long 					      i);
262636e0bf6eSScott Long 					continue;
262736e0bf6eSScott Long 				}
2628cbfd045bSScott Long 				mir = (struct aac_mntinforesp *)&fib->data[0];
2629795d7dc0SScott Long 				/* XXX Need to check if count changed */
2630795d7dc0SScott Long 				count = mir->MntRespCount;
263136e0bf6eSScott Long 				/*
2632914da7d0SScott Long 				 * Check the container against our list.
2633914da7d0SScott Long 				 * co->co_found was already set to 0 in a
2634914da7d0SScott Long 				 * previous run.
263536e0bf6eSScott Long 				 */
2636cbfd045bSScott Long 				if ((mir->Status == ST_OK) &&
2637cbfd045bSScott Long 				    (mir->MntTable[0].VolType != CT_NONE)) {
263836e0bf6eSScott Long 					found = 0;
2639914da7d0SScott Long 					TAILQ_FOREACH(co,
2640914da7d0SScott Long 						      &sc->aac_container_tqh,
2641914da7d0SScott Long 						      co_link) {
264236e0bf6eSScott Long 						if (co->co_mntobj.ObjectId ==
2643cbfd045bSScott Long 						    mir->MntTable[0].ObjectId) {
264436e0bf6eSScott Long 							co->co_found = 1;
264536e0bf6eSScott Long 							found = 1;
264636e0bf6eSScott Long 							break;
264736e0bf6eSScott Long 						}
264836e0bf6eSScott Long 					}
2649914da7d0SScott Long 					/*
2650914da7d0SScott Long 					 * If the container matched, continue
2651914da7d0SScott Long 					 * in the list.
2652914da7d0SScott Long 					 */
265336e0bf6eSScott Long 					if (found) {
265436e0bf6eSScott Long 						i++;
265536e0bf6eSScott Long 						continue;
265636e0bf6eSScott Long 					}
265736e0bf6eSScott Long 
265836e0bf6eSScott Long 					/*
2659914da7d0SScott Long 					 * This is a new container.  Do all the
266070545d1aSScott Long 					 * appropriate things to set it up.
266170545d1aSScott Long 					 */
2662cbfd045bSScott Long 					aac_add_container(sc, mir, 1);
266336e0bf6eSScott Long 					added = 1;
266436e0bf6eSScott Long 				}
266536e0bf6eSScott Long 				i++;
2666795d7dc0SScott Long 			} while ((i < count) && (i < AAC_MAX_CONTAINERS));
2667cbfd045bSScott Long 			aac_release_sync_fib(sc);
266836e0bf6eSScott Long 
266936e0bf6eSScott Long 			/*
2670914da7d0SScott Long 			 * Go through our list of containers and see which ones
2671914da7d0SScott Long 			 * were not marked 'found'.  Since the controller didn't
2672914da7d0SScott Long 			 * list them they must have been deleted.  Do the
2673914da7d0SScott Long 			 * appropriate steps to destroy the device.  Also reset
2674914da7d0SScott Long 			 * the co->co_found field.
267536e0bf6eSScott Long 			 */
267636e0bf6eSScott Long 			co = TAILQ_FIRST(&sc->aac_container_tqh);
267736e0bf6eSScott Long 			while (co != NULL) {
267836e0bf6eSScott Long 				if (co->co_found == 0) {
2679914da7d0SScott Long 					device_delete_child(sc->aac_dev,
2680914da7d0SScott Long 							    co->co_disk);
268136e0bf6eSScott Long 					co_next = TAILQ_NEXT(co, co_link);
2682c3d15322SScott Long 					AAC_LOCK_ACQUIRE(&sc->
2683914da7d0SScott Long 							aac_container_lock);
2684914da7d0SScott Long 					TAILQ_REMOVE(&sc->aac_container_tqh, co,
2685914da7d0SScott Long 						     co_link);
2686914da7d0SScott Long 					AAC_LOCK_RELEASE(&sc->
2687914da7d0SScott Long 							 aac_container_lock);
268836e0bf6eSScott Long 					FREE(co, M_AACBUF);
268936e0bf6eSScott Long 					co = co_next;
269036e0bf6eSScott Long 				} else {
269136e0bf6eSScott Long 					co->co_found = 0;
269236e0bf6eSScott Long 					co = TAILQ_NEXT(co, co_link);
269336e0bf6eSScott Long 				}
269436e0bf6eSScott Long 			}
269536e0bf6eSScott Long 
269636e0bf6eSScott Long 			/* Attach the newly created containers */
269736e0bf6eSScott Long 			if (added)
269836e0bf6eSScott Long 				bus_generic_attach(sc->aac_dev);
269936e0bf6eSScott Long 
270036e0bf6eSScott Long 			break;
270136e0bf6eSScott Long 
270236e0bf6eSScott Long 		default:
270336e0bf6eSScott Long 			break;
270436e0bf6eSScott Long 		}
270536e0bf6eSScott Long 
270636e0bf6eSScott Long 	default:
270736e0bf6eSScott Long 		break;
270836e0bf6eSScott Long 	}
270936e0bf6eSScott Long 
271036e0bf6eSScott Long 	/* Copy the AIF data to the AIF queue for ioctl retrieval */
2711c3d15322SScott Long 	AAC_LOCK_ACQUIRE(&sc->aac_aifq_lock);
271235863739SMike Smith 	next = (sc->aac_aifq_head + 1) % AAC_AIFQ_LENGTH;
271335863739SMike Smith 	if (next != sc->aac_aifq_tail) {
271435863739SMike Smith 		bcopy(aif, &sc->aac_aifq[next], sizeof(struct aac_aif_command));
271535863739SMike Smith 		sc->aac_aifq_head = next;
2716b3457b51SScott Long 
2717b3457b51SScott Long 		/* On the off chance that someone is sleeping for an aif... */
271835863739SMike Smith 		if (sc->aac_state & AAC_STATE_AIF_SLEEPER)
271935863739SMike Smith 			wakeup(sc->aac_aifq);
2720b3457b51SScott Long 		/* Wakeup any poll()ers */
2721512824f8SSeigo Tanimura 		selwakeuppri(&sc->rcv_select, PRIBIO);
272235863739SMike Smith 	}
2723b3457b51SScott Long 	AAC_LOCK_RELEASE(&sc->aac_aifq_lock);
272436e0bf6eSScott Long 
272536e0bf6eSScott Long 	return;
272635863739SMike Smith }
272735863739SMike Smith 
2728914da7d0SScott Long /*
27290b94a66eSMike Smith  * Return the Revision of the driver to userspace and check to see if the
273036e0bf6eSScott Long  * userspace app is possibly compatible.  This is extremely bogus since
273136e0bf6eSScott Long  * our driver doesn't follow Adaptec's versioning system.  Cheat by just
273236e0bf6eSScott Long  * returning what the card reported.
273335863739SMike Smith  */
273435863739SMike Smith static int
2735fb0c27d7SScott Long aac_rev_check(struct aac_softc *sc, caddr_t udata)
273635863739SMike Smith {
273735863739SMike Smith 	struct aac_rev_check rev_check;
273835863739SMike Smith 	struct aac_rev_check_resp rev_check_resp;
273935863739SMike Smith 	int error = 0;
274035863739SMike Smith 
274135863739SMike Smith 	debug_called(2);
274235863739SMike Smith 
274335863739SMike Smith 	/*
274435863739SMike Smith 	 * Copyin the revision struct from userspace
274535863739SMike Smith 	 */
2746c6eafcf2SScott Long 	if ((error = copyin(udata, (caddr_t)&rev_check,
2747c6eafcf2SScott Long 			sizeof(struct aac_rev_check))) != 0) {
274835863739SMike Smith 		return error;
274935863739SMike Smith 	}
275035863739SMike Smith 
2751914da7d0SScott Long 	debug(2, "Userland revision= %d\n",
2752914da7d0SScott Long 	      rev_check.callingRevision.buildNumber);
275335863739SMike Smith 
275435863739SMike Smith 	/*
275535863739SMike Smith 	 * Doctor up the response struct.
275635863739SMike Smith 	 */
275735863739SMike Smith 	rev_check_resp.possiblyCompatible = 1;
2758914da7d0SScott Long 	rev_check_resp.adapterSWRevision.external.ul =
2759914da7d0SScott Long 	    sc->aac_revision.external.ul;
2760914da7d0SScott Long 	rev_check_resp.adapterSWRevision.buildNumber =
2761914da7d0SScott Long 	    sc->aac_revision.buildNumber;
276235863739SMike Smith 
2763c6eafcf2SScott Long 	return(copyout((caddr_t)&rev_check_resp, udata,
2764c6eafcf2SScott Long 			sizeof(struct aac_rev_check_resp)));
276535863739SMike Smith }
276635863739SMike Smith 
2767914da7d0SScott Long /*
276835863739SMike Smith  * Pass the caller the next AIF in their queue
276935863739SMike Smith  */
277035863739SMike Smith static int
2771fb0c27d7SScott Long aac_getnext_aif(struct aac_softc *sc, caddr_t arg)
277235863739SMike Smith {
277335863739SMike Smith 	struct get_adapter_fib_ioctl agf;
27749e2e96d8SScott Long 	int error;
277535863739SMike Smith 
277635863739SMike Smith 	debug_called(2);
277735863739SMike Smith 
277835863739SMike Smith 	if ((error = copyin(arg, &agf, sizeof(agf))) == 0) {
277935863739SMike Smith 
278035863739SMike Smith 		/*
278135863739SMike Smith 		 * Check the magic number that we gave the caller.
278235863739SMike Smith 		 */
2783b88ffdc8SScott Long 		if (agf.AdapterFibContext != (int)(uintptr_t)sc->aifthread) {
278435863739SMike Smith 			error = EFAULT;
278535863739SMike Smith 		} else {
2786fb0c27d7SScott Long 			error = aac_return_aif(sc, agf.AifFib);
278735863739SMike Smith 			if ((error == EAGAIN) && (agf.Wait)) {
278835863739SMike Smith 				sc->aac_state |= AAC_STATE_AIF_SLEEPER;
278935863739SMike Smith 				while (error == EAGAIN) {
2790914da7d0SScott Long 					error = tsleep(sc->aac_aifq, PRIBIO |
2791914da7d0SScott Long 						       PCATCH, "aacaif", 0);
279235863739SMike Smith 					if (error == 0)
2793914da7d0SScott Long 						error = aac_return_aif(sc,
2794914da7d0SScott Long 						    agf.AifFib);
279535863739SMike Smith 				}
279635863739SMike Smith 				sc->aac_state &= ~AAC_STATE_AIF_SLEEPER;
279735863739SMike Smith 			}
279835863739SMike Smith 		}
279935863739SMike Smith 	}
280035863739SMike Smith 	return(error);
280135863739SMike Smith }
280235863739SMike Smith 
2803914da7d0SScott Long /*
28040b94a66eSMike Smith  * Hand the next AIF off the top of the queue out to userspace.
28050b94a66eSMike Smith  */
28060b94a66eSMike Smith static int
2807fb0c27d7SScott Long aac_return_aif(struct aac_softc *sc, caddr_t uptr)
28080b94a66eSMike Smith {
28093df780cfSScott Long 	int next, error;
28100b94a66eSMike Smith 
28110b94a66eSMike Smith 	debug_called(2);
28120b94a66eSMike Smith 
2813c3d15322SScott Long 	AAC_LOCK_ACQUIRE(&sc->aac_aifq_lock);
28140b94a66eSMike Smith 	if (sc->aac_aifq_tail == sc->aac_aifq_head) {
28153df780cfSScott Long 		AAC_LOCK_RELEASE(&sc->aac_aifq_lock);
28163df780cfSScott Long 		return (EAGAIN);
28173df780cfSScott Long 	}
28183df780cfSScott Long 
28193df780cfSScott Long 	next = (sc->aac_aifq_tail + 1) % AAC_AIFQ_LENGTH;
28203df780cfSScott Long 	error = copyout(&sc->aac_aifq[next], uptr,
2821c6eafcf2SScott Long 			sizeof(struct aac_aif_command));
282236e0bf6eSScott Long 	if (error)
282370545d1aSScott Long 		device_printf(sc->aac_dev,
282470545d1aSScott Long 		    "aac_return_aif: copyout returned %d\n", error);
28253df780cfSScott Long 	else
28263df780cfSScott Long 		sc->aac_aifq_tail = next;
28273df780cfSScott Long 
2828b3457b51SScott Long 	AAC_LOCK_RELEASE(&sc->aac_aifq_lock);
28290b94a66eSMike Smith 	return(error);
28300b94a66eSMike Smith }
283136e0bf6eSScott Long 
2832914da7d0SScott Long /*
283336e0bf6eSScott Long  * Give the userland some information about the container.  The AAC arch
283436e0bf6eSScott Long  * expects the driver to be a SCSI passthrough type driver, so it expects
283536e0bf6eSScott Long  * the containers to have b:t:l numbers.  Fake it.
283636e0bf6eSScott Long  */
283736e0bf6eSScott Long static int
283836e0bf6eSScott Long aac_query_disk(struct aac_softc *sc, caddr_t uptr)
283936e0bf6eSScott Long {
284036e0bf6eSScott Long 	struct aac_query_disk query_disk;
284136e0bf6eSScott Long 	struct aac_container *co;
2842914da7d0SScott Long 	struct aac_disk	*disk;
284336e0bf6eSScott Long 	int error, id;
284436e0bf6eSScott Long 
284536e0bf6eSScott Long 	debug_called(2);
284636e0bf6eSScott Long 
2847914da7d0SScott Long 	disk = NULL;
2848914da7d0SScott Long 
2849914da7d0SScott Long 	error = copyin(uptr, (caddr_t)&query_disk,
2850914da7d0SScott Long 		       sizeof(struct aac_query_disk));
285136e0bf6eSScott Long 	if (error)
285236e0bf6eSScott Long 		return (error);
285336e0bf6eSScott Long 
285436e0bf6eSScott Long 	id = query_disk.ContainerNumber;
285536e0bf6eSScott Long 	if (id == -1)
285636e0bf6eSScott Long 		return (EINVAL);
285736e0bf6eSScott Long 
2858c3d15322SScott Long 	AAC_LOCK_ACQUIRE(&sc->aac_container_lock);
285936e0bf6eSScott Long 	TAILQ_FOREACH(co, &sc->aac_container_tqh, co_link) {
286036e0bf6eSScott Long 		if (co->co_mntobj.ObjectId == id)
286136e0bf6eSScott Long 			break;
286236e0bf6eSScott Long 		}
286336e0bf6eSScott Long 
286436e0bf6eSScott Long 	if (co == NULL) {
286536e0bf6eSScott Long 			query_disk.Valid = 0;
286636e0bf6eSScott Long 			query_disk.Locked = 0;
286736e0bf6eSScott Long 			query_disk.Deleted = 1;		/* XXX is this right? */
286836e0bf6eSScott Long 	} else {
286936e0bf6eSScott Long 		disk = device_get_softc(co->co_disk);
287036e0bf6eSScott Long 		query_disk.Valid = 1;
2871914da7d0SScott Long 		query_disk.Locked =
2872914da7d0SScott Long 		    (disk->ad_flags & AAC_DISK_OPEN) ? 1 : 0;
287336e0bf6eSScott Long 		query_disk.Deleted = 0;
2874b3457b51SScott Long 		query_disk.Bus = device_get_unit(sc->aac_dev);
287536e0bf6eSScott Long 		query_disk.Target = disk->unit;
287636e0bf6eSScott Long 		query_disk.Lun = 0;
287736e0bf6eSScott Long 		query_disk.UnMapped = 0;
28787540e65eSScott Long 		sprintf(&query_disk.diskDeviceName[0], "%s%d",
28797540e65eSScott Long 		        disk->ad_disk.d_name, disk->ad_disk.d_unit);
288036e0bf6eSScott Long 	}
288136e0bf6eSScott Long 	AAC_LOCK_RELEASE(&sc->aac_container_lock);
288236e0bf6eSScott Long 
2883914da7d0SScott Long 	error = copyout((caddr_t)&query_disk, uptr,
2884914da7d0SScott Long 			sizeof(struct aac_query_disk));
288536e0bf6eSScott Long 
288636e0bf6eSScott Long 	return (error);
288736e0bf6eSScott Long }
288836e0bf6eSScott Long 
2889fe3cb0e1SScott Long static void
2890fe3cb0e1SScott Long aac_get_bus_info(struct aac_softc *sc)
2891fe3cb0e1SScott Long {
2892fe3cb0e1SScott Long 	struct aac_fib *fib;
2893fe3cb0e1SScott Long 	struct aac_ctcfg *c_cmd;
2894fe3cb0e1SScott Long 	struct aac_ctcfg_resp *c_resp;
2895fe3cb0e1SScott Long 	struct aac_vmioctl *vmi;
2896fe3cb0e1SScott Long 	struct aac_vmi_businf_resp *vmi_resp;
2897fe3cb0e1SScott Long 	struct aac_getbusinf businfo;
289870545d1aSScott Long 	struct aac_sim *caminf;
2899fe3cb0e1SScott Long 	device_t child;
2900fe3cb0e1SScott Long 	int i, found, error;
2901fe3cb0e1SScott Long 
2902fe3cb0e1SScott Long 	aac_alloc_sync_fib(sc, &fib, 0);
2903fe3cb0e1SScott Long 	c_cmd = (struct aac_ctcfg *)&fib->data[0];
290439ee03c3SScott Long 	bzero(c_cmd, sizeof(struct aac_ctcfg));
2905fe3cb0e1SScott Long 
2906fe3cb0e1SScott Long 	c_cmd->Command = VM_ContainerConfig;
2907fe3cb0e1SScott Long 	c_cmd->cmd = CT_GET_SCSI_METHOD;
2908fe3cb0e1SScott Long 	c_cmd->param = 0;
2909fe3cb0e1SScott Long 
2910fe3cb0e1SScott Long 	error = aac_sync_fib(sc, ContainerCommand, 0, fib,
2911fe3cb0e1SScott Long 	    sizeof(struct aac_ctcfg));
2912fe3cb0e1SScott Long 	if (error) {
2913fe3cb0e1SScott Long 		device_printf(sc->aac_dev, "Error %d sending "
2914fe3cb0e1SScott Long 		    "VM_ContainerConfig command\n", error);
2915fe3cb0e1SScott Long 		aac_release_sync_fib(sc);
2916fe3cb0e1SScott Long 		return;
2917fe3cb0e1SScott Long 	}
2918fe3cb0e1SScott Long 
2919fe3cb0e1SScott Long 	c_resp = (struct aac_ctcfg_resp *)&fib->data[0];
2920fe3cb0e1SScott Long 	if (c_resp->Status != ST_OK) {
2921fe3cb0e1SScott Long 		device_printf(sc->aac_dev, "VM_ContainerConfig returned 0x%x\n",
2922fe3cb0e1SScott Long 		    c_resp->Status);
2923fe3cb0e1SScott Long 		aac_release_sync_fib(sc);
2924fe3cb0e1SScott Long 		return;
2925fe3cb0e1SScott Long 	}
2926fe3cb0e1SScott Long 
2927fe3cb0e1SScott Long 	sc->scsi_method_id = c_resp->param;
2928fe3cb0e1SScott Long 
2929fe3cb0e1SScott Long 	vmi = (struct aac_vmioctl *)&fib->data[0];
293039ee03c3SScott Long 	bzero(vmi, sizeof(struct aac_vmioctl));
293139ee03c3SScott Long 
2932fe3cb0e1SScott Long 	vmi->Command = VM_Ioctl;
2933fe3cb0e1SScott Long 	vmi->ObjType = FT_DRIVE;
2934fe3cb0e1SScott Long 	vmi->MethId = sc->scsi_method_id;
2935fe3cb0e1SScott Long 	vmi->ObjId = 0;
2936fe3cb0e1SScott Long 	vmi->IoctlCmd = GetBusInfo;
2937fe3cb0e1SScott Long 
2938fe3cb0e1SScott Long 	error = aac_sync_fib(sc, ContainerCommand, 0, fib,
2939fe3cb0e1SScott Long 	    sizeof(struct aac_vmioctl));
2940fe3cb0e1SScott Long 	if (error) {
2941fe3cb0e1SScott Long 		device_printf(sc->aac_dev, "Error %d sending VMIoctl command\n",
2942fe3cb0e1SScott Long 		    error);
2943fe3cb0e1SScott Long 		aac_release_sync_fib(sc);
2944fe3cb0e1SScott Long 		return;
2945fe3cb0e1SScott Long 	}
2946fe3cb0e1SScott Long 
2947fe3cb0e1SScott Long 	vmi_resp = (struct aac_vmi_businf_resp *)&fib->data[0];
2948fe3cb0e1SScott Long 	if (vmi_resp->Status != ST_OK) {
2949fe3cb0e1SScott Long 		device_printf(sc->aac_dev, "VM_Ioctl returned %d\n",
2950fe3cb0e1SScott Long 		    vmi_resp->Status);
2951fe3cb0e1SScott Long 		aac_release_sync_fib(sc);
2952fe3cb0e1SScott Long 		return;
2953fe3cb0e1SScott Long 	}
2954fe3cb0e1SScott Long 
2955fe3cb0e1SScott Long 	bcopy(&vmi_resp->BusInf, &businfo, sizeof(struct aac_getbusinf));
2956fe3cb0e1SScott Long 	aac_release_sync_fib(sc);
2957fe3cb0e1SScott Long 
2958fe3cb0e1SScott Long 	found = 0;
2959fe3cb0e1SScott Long 	for (i = 0; i < businfo.BusCount; i++) {
2960fe3cb0e1SScott Long 		if (businfo.BusValid[i] != AAC_BUS_VALID)
2961fe3cb0e1SScott Long 			continue;
2962fe3cb0e1SScott Long 
2963a761a1caSScott Long 		caminf = (struct aac_sim *)malloc( sizeof(struct aac_sim),
2964a761a1caSScott Long 		    M_AACBUF, M_NOWAIT | M_ZERO);
2965fe3cb0e1SScott Long 		if (caminf == NULL)
2966fe3cb0e1SScott Long 			continue;
2967fe3cb0e1SScott Long 
2968fe3cb0e1SScott Long 		child = device_add_child(sc->aac_dev, "aacp", -1);
2969fe3cb0e1SScott Long 		if (child == NULL) {
2970fe3cb0e1SScott Long 			device_printf(sc->aac_dev, "device_add_child failed\n");
2971fe3cb0e1SScott Long 			continue;
2972fe3cb0e1SScott Long 		}
2973fe3cb0e1SScott Long 
2974fe3cb0e1SScott Long 		caminf->TargetsPerBus = businfo.TargetsPerBus;
2975fe3cb0e1SScott Long 		caminf->BusNumber = i;
2976fe3cb0e1SScott Long 		caminf->InitiatorBusId = businfo.InitiatorBusId[i];
2977fe3cb0e1SScott Long 		caminf->aac_sc = sc;
2978ddb8683eSScott Long 		caminf->sim_dev = child;
2979fe3cb0e1SScott Long 
2980fe3cb0e1SScott Long 		device_set_ivars(child, caminf);
2981fe3cb0e1SScott Long 		device_set_desc(child, "SCSI Passthrough Bus");
298270545d1aSScott Long 		TAILQ_INSERT_TAIL(&sc->aac_sim_tqh, caminf, sim_link);
2983fe3cb0e1SScott Long 
2984fe3cb0e1SScott Long 		found = 1;
2985fe3cb0e1SScott Long 	}
2986fe3cb0e1SScott Long 
2987fe3cb0e1SScott Long 	if (found)
2988fe3cb0e1SScott Long 		bus_generic_attach(sc->aac_dev);
2989fe3cb0e1SScott Long 
2990fe3cb0e1SScott Long 	return;
2991fe3cb0e1SScott Long }
2992