xref: /freebsd/sys/dev/aac/aac.c (revision dc08ffec870569914f44bcf26aa838310e343764)
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 = {
184dc08ffecSPoul-Henning Kamp 	.d_version =	D_VERSION,
185dc08ffecSPoul-Henning Kamp 	.d_flags =	D_NEEDGIANT,
1867ac40f5fSPoul-Henning Kamp 	.d_open =	aac_open,
1877ac40f5fSPoul-Henning Kamp 	.d_close =	aac_close,
1887ac40f5fSPoul-Henning Kamp 	.d_ioctl =	aac_ioctl,
1897ac40f5fSPoul-Henning Kamp 	.d_poll =	aac_poll,
1907ac40f5fSPoul-Henning Kamp 	.d_name =	"aac",
19135863739SMike Smith };
19235863739SMike Smith 
19336e0bf6eSScott Long MALLOC_DEFINE(M_AACBUF, "aacbuf", "Buffers for the AAC driver");
19436e0bf6eSScott Long 
1953d04a9d7SScott Long /* sysctl node */
1963d04a9d7SScott Long SYSCTL_NODE(_hw, OID_AUTO, aac, CTLFLAG_RD, 0, "AAC driver parameters");
1973d04a9d7SScott Long 
198914da7d0SScott Long /*
199914da7d0SScott Long  * Device Interface
200914da7d0SScott Long  */
20135863739SMike Smith 
202914da7d0SScott Long /*
20335863739SMike Smith  * Initialise the controller and softc
20435863739SMike Smith  */
20535863739SMike Smith int
20635863739SMike Smith aac_attach(struct aac_softc *sc)
20735863739SMike Smith {
20835863739SMike Smith 	int error, unit;
20935863739SMike Smith 
21035863739SMike Smith 	debug_called(1);
21135863739SMike Smith 
21235863739SMike Smith 	/*
21335863739SMike Smith 	 * Initialise per-controller queues.
21435863739SMike Smith 	 */
2150b94a66eSMike Smith 	aac_initq_free(sc);
2160b94a66eSMike Smith 	aac_initq_ready(sc);
2170b94a66eSMike Smith 	aac_initq_busy(sc);
2180b94a66eSMike Smith 	aac_initq_bio(sc);
21935863739SMike Smith 
22035863739SMike Smith 	/*
22135863739SMike Smith 	 * Initialise command-completion task.
22235863739SMike Smith 	 */
22335863739SMike Smith 	TASK_INIT(&sc->aac_task_complete, 0, aac_complete, sc);
22435863739SMike Smith 
22535863739SMike Smith 	/* disable interrupts before we enable anything */
22635863739SMike Smith 	AAC_MASK_INTERRUPTS(sc);
22735863739SMike Smith 
22835863739SMike Smith 	/* mark controller as suspended until we get ourselves organised */
22935863739SMike Smith 	sc->aac_state |= AAC_STATE_SUSPEND;
23035863739SMike Smith 
23135863739SMike Smith 	/*
232fe94b852SScott Long 	 * Check that the firmware on the card is supported.
233fe94b852SScott Long 	 */
234fe94b852SScott Long 	if ((error = aac_check_firmware(sc)) != 0)
235fe94b852SScott Long 		return(error);
236fe94b852SScott Long 
237f6b1c44dSScott Long 	/*
238f6b1c44dSScott Long 	 * Initialize locks
239f6b1c44dSScott Long 	 */
240cbfd045bSScott Long 	AAC_LOCK_INIT(&sc->aac_sync_lock, "AAC sync FIB lock");
241f6b1c44dSScott Long 	AAC_LOCK_INIT(&sc->aac_aifq_lock, "AAC AIF lock");
242f6b1c44dSScott Long 	AAC_LOCK_INIT(&sc->aac_io_lock, "AAC I/O lock");
243f6b1c44dSScott Long 	AAC_LOCK_INIT(&sc->aac_container_lock, "AAC container lock");
244f6b1c44dSScott Long 	TAILQ_INIT(&sc->aac_container_tqh);
245f6b1c44dSScott Long 
2463df780cfSScott Long 	/* Initialize the local AIF queue pointers */
2473df780cfSScott Long 	sc->aac_aifq_head = sc->aac_aifq_tail = AAC_AIFQ_LENGTH;
248cbfd045bSScott Long 
2490b94a66eSMike Smith 	/*
25035863739SMike Smith 	 * Initialise the adapter.
25135863739SMike Smith 	 */
2520b94a66eSMike Smith 	if ((error = aac_init(sc)) != 0)
25335863739SMike Smith 		return(error);
25435863739SMike Smith 
25535863739SMike Smith 	/*
25635863739SMike Smith 	 * Print a little information about the controller.
25735863739SMike Smith 	 */
25835863739SMike Smith 	aac_describe_controller(sc);
25935863739SMike Smith 
26035863739SMike Smith 	/*
261ae543596SScott Long 	 * Register to probe our containers later.
262ae543596SScott Long 	 */
26335863739SMike Smith 	sc->aac_ich.ich_func = aac_startup;
26435863739SMike Smith 	sc->aac_ich.ich_arg = sc;
26535863739SMike Smith 	if (config_intrhook_establish(&sc->aac_ich) != 0) {
266914da7d0SScott Long 		device_printf(sc->aac_dev,
267914da7d0SScott Long 			      "can't establish configuration hook\n");
26835863739SMike Smith 		return(ENXIO);
26935863739SMike Smith 	}
27035863739SMike Smith 
27135863739SMike Smith 	/*
27235863739SMike Smith 	 * Make the control device.
27335863739SMike Smith 	 */
27435863739SMike Smith 	unit = device_get_unit(sc->aac_dev);
2759e9466baSRobert Watson 	sc->aac_dev_t = make_dev(&aac_cdevsw, unit, UID_ROOT, GID_OPERATOR,
2769e9466baSRobert Watson 				 0640, "aac%d", unit);
277157fbb2eSScott Long 	(void)make_dev_alias(sc->aac_dev_t, "afa%d", unit);
2784aa620cdSScott Long 	(void)make_dev_alias(sc->aac_dev_t, "hpn%d", unit);
27935863739SMike Smith 	sc->aac_dev_t->si_drv1 = sc;
28035863739SMike Smith 
28136e0bf6eSScott Long 	/* Create the AIF thread */
28270545d1aSScott Long 	if (kthread_create((void(*)(void *))aac_command_thread, sc,
283316ec49aSScott Long 			   &sc->aifthread, 0, 0, "aac%daif", unit))
28436e0bf6eSScott Long 		panic("Could not create AIF thread\n");
28536e0bf6eSScott Long 
28636e0bf6eSScott Long 	/* Register the shutdown method to only be called post-dump */
2875f54d522SScott Long 	if ((sc->eh = EVENTHANDLER_REGISTER(shutdown_final, aac_shutdown,
2885f54d522SScott Long 	    sc->aac_dev, SHUTDOWN_PRI_DEFAULT)) == NULL)
2895f54d522SScott Long 		device_printf(sc->aac_dev,
2905f54d522SScott Long 			      "shutdown event registration failed\n");
29136e0bf6eSScott Long 
292fe3cb0e1SScott Long 	/* Register with CAM for the non-DASD devices */
293a6d35632SScott Long 	if ((sc->flags & AAC_FLAGS_ENABLE_CAM) != 0) {
29470545d1aSScott Long 		TAILQ_INIT(&sc->aac_sim_tqh);
295fe3cb0e1SScott Long 		aac_get_bus_info(sc);
29670545d1aSScott Long 	}
297fe3cb0e1SScott Long 
29835863739SMike Smith 	return(0);
29935863739SMike Smith }
30035863739SMike Smith 
301914da7d0SScott Long /*
30235863739SMike Smith  * Probe for containers, create disks.
30335863739SMike Smith  */
30435863739SMike Smith static void
30535863739SMike Smith aac_startup(void *arg)
30635863739SMike Smith {
307914da7d0SScott Long 	struct aac_softc *sc;
308cbfd045bSScott Long 	struct aac_fib *fib;
309cbfd045bSScott Long 	struct aac_mntinfo *mi;
310cbfd045bSScott Long 	struct aac_mntinforesp *mir = NULL;
311795d7dc0SScott Long 	int count = 0, i = 0;
31235863739SMike Smith 
31335863739SMike Smith 	debug_called(1);
31435863739SMike Smith 
315914da7d0SScott Long 	sc = (struct aac_softc *)arg;
316914da7d0SScott Long 
31735863739SMike Smith 	/* disconnect ourselves from the intrhook chain */
31835863739SMike Smith 	config_intrhook_disestablish(&sc->aac_ich);
31935863739SMike Smith 
320fe3cb0e1SScott Long 	aac_alloc_sync_fib(sc, &fib, 0);
321cbfd045bSScott Long 	mi = (struct aac_mntinfo *)&fib->data[0];
322cbfd045bSScott Long 
32335863739SMike Smith 	/* loop over possible containers */
32436e0bf6eSScott Long 	do {
32535863739SMike Smith 		/* request information on this container */
32639ee03c3SScott Long 		bzero(mi, sizeof(struct aac_mntinfo));
32739ee03c3SScott Long 		mi->Command = VM_NameServe;
32839ee03c3SScott Long 		mi->MntType = FT_FILESYS;
329cbfd045bSScott Long 		mi->MntCount = i;
330cbfd045bSScott Long 		if (aac_sync_fib(sc, ContainerCommand, 0, fib,
331cbfd045bSScott Long 				 sizeof(struct aac_mntinfo))) {
332795d7dc0SScott Long 			printf("error probing container %d", i);
33335863739SMike Smith 			continue;
33435863739SMike Smith 		}
33535863739SMike Smith 
336cbfd045bSScott Long 		mir = (struct aac_mntinforesp *)&fib->data[0];
337795d7dc0SScott Long 		/* XXX Need to check if count changed */
338795d7dc0SScott Long 		count = mir->MntRespCount;
339cbfd045bSScott Long 		aac_add_container(sc, mir, 0);
34036e0bf6eSScott Long 		i++;
341795d7dc0SScott Long 	} while ((i < count) && (i < AAC_MAX_CONTAINERS));
342cbfd045bSScott Long 
343cbfd045bSScott Long 	aac_release_sync_fib(sc);
34435863739SMike Smith 
34535863739SMike Smith 	/* poke the bus to actually attach the child devices */
34635863739SMike Smith 	if (bus_generic_attach(sc->aac_dev))
34735863739SMike Smith 		device_printf(sc->aac_dev, "bus_generic_attach failed\n");
34835863739SMike Smith 
34935863739SMike Smith 	/* mark the controller up */
35035863739SMike Smith 	sc->aac_state &= ~AAC_STATE_SUSPEND;
35135863739SMike Smith 
35235863739SMike Smith 	/* enable interrupts now */
35335863739SMike Smith 	AAC_UNMASK_INTERRUPTS(sc);
35435863739SMike Smith }
35535863739SMike Smith 
356914da7d0SScott Long /*
357914da7d0SScott Long  * Create a device to respresent a new container
358914da7d0SScott Long  */
359914da7d0SScott Long static void
360cbfd045bSScott Long aac_add_container(struct aac_softc *sc, struct aac_mntinforesp *mir, int f)
361914da7d0SScott Long {
362914da7d0SScott Long 	struct aac_container *co;
363914da7d0SScott Long 	device_t child;
364914da7d0SScott Long 
365914da7d0SScott Long 	/*
366914da7d0SScott Long 	 * Check container volume type for validity.  Note that many of
367914da7d0SScott Long 	 * the possible types may never show up.
368914da7d0SScott Long 	 */
369914da7d0SScott Long 	if ((mir->Status == ST_OK) && (mir->MntTable[0].VolType != CT_NONE)) {
370a761a1caSScott Long 		co = (struct aac_container *)malloc(sizeof *co, M_AACBUF,
371a761a1caSScott Long 		       M_NOWAIT | M_ZERO);
372914da7d0SScott Long 		if (co == NULL)
373914da7d0SScott Long 			panic("Out of memory?!\n");
374914da7d0SScott Long 		debug(1, "id %x  name '%.16s'  size %u  type %d",
375914da7d0SScott Long 		      mir->MntTable[0].ObjectId,
376914da7d0SScott Long 		      mir->MntTable[0].FileSystemName,
377914da7d0SScott Long 		      mir->MntTable[0].Capacity, mir->MntTable[0].VolType);
378914da7d0SScott Long 
379fe3cb0e1SScott Long 		if ((child = device_add_child(sc->aac_dev, "aacd", -1)) == NULL)
380914da7d0SScott Long 			device_printf(sc->aac_dev, "device_add_child failed\n");
381914da7d0SScott Long 		else
382914da7d0SScott Long 			device_set_ivars(child, co);
383914da7d0SScott Long 		device_set_desc(child, aac_describe_code(aac_container_types,
384914da7d0SScott Long 				mir->MntTable[0].VolType));
385914da7d0SScott Long 		co->co_disk = child;
386914da7d0SScott Long 		co->co_found = f;
387914da7d0SScott Long 		bcopy(&mir->MntTable[0], &co->co_mntobj,
388914da7d0SScott Long 		      sizeof(struct aac_mntobj));
389c3d15322SScott Long 		AAC_LOCK_ACQUIRE(&sc->aac_container_lock);
390914da7d0SScott Long 		TAILQ_INSERT_TAIL(&sc->aac_container_tqh, co, co_link);
391914da7d0SScott Long 		AAC_LOCK_RELEASE(&sc->aac_container_lock);
392914da7d0SScott Long 	}
393914da7d0SScott Long }
394914da7d0SScott Long 
395914da7d0SScott Long /*
39635863739SMike Smith  * Free all of the resources associated with (sc)
39735863739SMike Smith  *
39835863739SMike Smith  * Should not be called if the controller is active.
39935863739SMike Smith  */
40035863739SMike Smith void
40135863739SMike Smith aac_free(struct aac_softc *sc)
40235863739SMike Smith {
403ffb37f33SScott Long 
40435863739SMike Smith 	debug_called(1);
40535863739SMike Smith 
40635863739SMike Smith 	/* remove the control device */
40735863739SMike Smith 	if (sc->aac_dev_t != NULL)
40835863739SMike Smith 		destroy_dev(sc->aac_dev_t);
40935863739SMike Smith 
4100b94a66eSMike Smith 	/* throw away any FIB buffers, discard the FIB DMA tag */
4118480cc63SScott Long 	aac_free_commands(sc);
4120b94a66eSMike Smith 	if (sc->aac_fib_dmat)
4130b94a66eSMike Smith 		bus_dma_tag_destroy(sc->aac_fib_dmat);
41435863739SMike Smith 
415ffb37f33SScott Long 	free(sc->aac_commands, M_AACBUF);
416ffb37f33SScott Long 
41735863739SMike Smith 	/* destroy the common area */
41835863739SMike Smith 	if (sc->aac_common) {
41935863739SMike Smith 		bus_dmamap_unload(sc->aac_common_dmat, sc->aac_common_dmamap);
420c6eafcf2SScott Long 		bus_dmamem_free(sc->aac_common_dmat, sc->aac_common,
421c6eafcf2SScott Long 				sc->aac_common_dmamap);
42235863739SMike Smith 	}
4230b94a66eSMike Smith 	if (sc->aac_common_dmat)
4240b94a66eSMike Smith 		bus_dma_tag_destroy(sc->aac_common_dmat);
42535863739SMike Smith 
42635863739SMike Smith 	/* disconnect the interrupt handler */
42735863739SMike Smith 	if (sc->aac_intr)
42835863739SMike Smith 		bus_teardown_intr(sc->aac_dev, sc->aac_irq, sc->aac_intr);
42935863739SMike Smith 	if (sc->aac_irq != NULL)
430c6eafcf2SScott Long 		bus_release_resource(sc->aac_dev, SYS_RES_IRQ, sc->aac_irq_rid,
431c6eafcf2SScott Long 				     sc->aac_irq);
43235863739SMike Smith 
43335863739SMike Smith 	/* destroy data-transfer DMA tag */
43435863739SMike Smith 	if (sc->aac_buffer_dmat)
43535863739SMike Smith 		bus_dma_tag_destroy(sc->aac_buffer_dmat);
43635863739SMike Smith 
43735863739SMike Smith 	/* destroy the parent DMA tag */
43835863739SMike Smith 	if (sc->aac_parent_dmat)
43935863739SMike Smith 		bus_dma_tag_destroy(sc->aac_parent_dmat);
44035863739SMike Smith 
44135863739SMike Smith 	/* release the register window mapping */
44235863739SMike Smith 	if (sc->aac_regs_resource != NULL)
443914da7d0SScott Long 		bus_release_resource(sc->aac_dev, SYS_RES_MEMORY,
444914da7d0SScott Long 				     sc->aac_regs_rid, sc->aac_regs_resource);
44535863739SMike Smith }
44635863739SMike Smith 
447914da7d0SScott Long /*
44835863739SMike Smith  * Disconnect from the controller completely, in preparation for unload.
44935863739SMike Smith  */
45035863739SMike Smith int
45135863739SMike Smith aac_detach(device_t dev)
45235863739SMike Smith {
453914da7d0SScott Long 	struct aac_softc *sc;
45470545d1aSScott Long 	struct aac_container *co;
45570545d1aSScott Long 	struct aac_sim	*sim;
45635863739SMike Smith 	int error;
45735863739SMike Smith 
45835863739SMike Smith 	debug_called(1);
45935863739SMike Smith 
460914da7d0SScott Long 	sc = device_get_softc(dev);
461914da7d0SScott Long 
46235863739SMike Smith 	if (sc->aac_state & AAC_STATE_OPEN)
46335863739SMike Smith 		return(EBUSY);
46435863739SMike Smith 
46570545d1aSScott Long 	/* Remove the child containers */
466a761a1caSScott Long 	while ((co = TAILQ_FIRST(&sc->aac_container_tqh)) != NULL) {
46770545d1aSScott Long 		error = device_delete_child(dev, co->co_disk);
46870545d1aSScott Long 		if (error)
46970545d1aSScott Long 			return (error);
47065ac4ed6SScott Long 		TAILQ_REMOVE(&sc->aac_container_tqh, co, co_link);
471a761a1caSScott Long 		free(co, M_AACBUF);
47270545d1aSScott Long 	}
47370545d1aSScott Long 
47470545d1aSScott Long 	/* Remove the CAM SIMs */
475a761a1caSScott Long 	while ((sim = TAILQ_FIRST(&sc->aac_sim_tqh)) != NULL) {
476a761a1caSScott Long 		TAILQ_REMOVE(&sc->aac_sim_tqh, sim, sim_link);
47770545d1aSScott Long 		error = device_delete_child(dev, sim->sim_dev);
47870545d1aSScott Long 		if (error)
47970545d1aSScott Long 			return (error);
480a761a1caSScott Long 		free(sim, M_AACBUF);
48170545d1aSScott Long 	}
48270545d1aSScott Long 
48336e0bf6eSScott Long 	if (sc->aifflags & AAC_AIFFLAGS_RUNNING) {
48436e0bf6eSScott Long 		sc->aifflags |= AAC_AIFFLAGS_EXIT;
48536e0bf6eSScott Long 		wakeup(sc->aifthread);
48636e0bf6eSScott Long 		tsleep(sc->aac_dev, PUSER | PCATCH, "aacdch", 30 * hz);
48736e0bf6eSScott Long 	}
48836e0bf6eSScott Long 
48936e0bf6eSScott Long 	if (sc->aifflags & AAC_AIFFLAGS_RUNNING)
49036e0bf6eSScott Long 		panic("Cannot shutdown AIF thread\n");
49136e0bf6eSScott Long 
49235863739SMike Smith 	if ((error = aac_shutdown(dev)))
49335863739SMike Smith 		return(error);
49435863739SMike Smith 
4955f54d522SScott Long 	EVENTHANDLER_DEREGISTER(shutdown_final, sc->eh);
4965f54d522SScott Long 
49735863739SMike Smith 	aac_free(sc);
49835863739SMike Smith 
49935863739SMike Smith 	return(0);
50035863739SMike Smith }
50135863739SMike Smith 
502914da7d0SScott Long /*
50335863739SMike Smith  * Bring the controller down to a dormant state and detach all child devices.
50435863739SMike Smith  *
50535863739SMike Smith  * This function is called before detach or system shutdown.
50635863739SMike Smith  *
5070b94a66eSMike Smith  * Note that we can assume that the bioq on the controller is empty, as we won't
50835863739SMike Smith  * allow shutdown if any device is open.
50935863739SMike Smith  */
51035863739SMike Smith int
51135863739SMike Smith aac_shutdown(device_t dev)
51235863739SMike Smith {
513914da7d0SScott Long 	struct aac_softc *sc;
514cbfd045bSScott Long 	struct aac_fib *fib;
515cbfd045bSScott Long 	struct aac_close_command *cc;
51635863739SMike Smith 
51735863739SMike Smith 	debug_called(1);
51835863739SMike Smith 
519914da7d0SScott Long 	sc = device_get_softc(dev);
520914da7d0SScott Long 
52135863739SMike Smith 	sc->aac_state |= AAC_STATE_SUSPEND;
52235863739SMike Smith 
52335863739SMike Smith 	/*
52435863739SMike Smith 	 * Send a Container shutdown followed by a HostShutdown FIB to the
52535863739SMike Smith 	 * controller to convince it that we don't want to talk to it anymore.
52635863739SMike Smith 	 * We've been closed and all I/O completed already
52735863739SMike Smith 	 */
52835863739SMike Smith 	device_printf(sc->aac_dev, "shutting down controller...");
52935863739SMike Smith 
530fe3cb0e1SScott Long 	aac_alloc_sync_fib(sc, &fib, AAC_SYNC_LOCK_FORCE);
531cbfd045bSScott Long 	cc = (struct aac_close_command *)&fib->data[0];
532cbfd045bSScott Long 
53339ee03c3SScott Long 	bzero(cc, sizeof(struct aac_close_command));
534cbfd045bSScott Long 	cc->Command = VM_CloseAll;
535cbfd045bSScott Long 	cc->ContainerId = 0xffffffff;
536cbfd045bSScott Long 	if (aac_sync_fib(sc, ContainerCommand, 0, fib,
537cbfd045bSScott Long 	    sizeof(struct aac_close_command)))
53835863739SMike Smith 		printf("FAILED.\n");
53970545d1aSScott Long 	else
54070545d1aSScott Long 		printf("done\n");
54170545d1aSScott Long #if 0
542914da7d0SScott Long 	else {
543cbfd045bSScott Long 		fib->data[0] = 0;
54436e0bf6eSScott Long 		/*
545914da7d0SScott Long 		 * XXX Issuing this command to the controller makes it shut down
54636e0bf6eSScott Long 		 * but also keeps it from coming back up without a reset of the
54736e0bf6eSScott Long 		 * PCI bus.  This is not desirable if you are just unloading the
54836e0bf6eSScott Long 		 * driver module with the intent to reload it later.
54936e0bf6eSScott Long 		 */
550cbfd045bSScott Long 		if (aac_sync_fib(sc, FsaHostShutdown, AAC_FIBSTATE_SHUTDOWN,
551cbfd045bSScott Long 		    fib, 1)) {
55235863739SMike Smith 			printf("FAILED.\n");
55335863739SMike Smith 		} else {
55435863739SMike Smith 			printf("done.\n");
55535863739SMike Smith 		}
55635863739SMike Smith 	}
55770545d1aSScott Long #endif
55835863739SMike Smith 
55935863739SMike Smith 	AAC_MASK_INTERRUPTS(sc);
56035863739SMike Smith 
56135863739SMike Smith 	return(0);
56235863739SMike Smith }
56335863739SMike Smith 
564914da7d0SScott Long /*
56535863739SMike Smith  * Bring the controller to a quiescent state, ready for system suspend.
56635863739SMike Smith  */
56735863739SMike Smith int
56835863739SMike Smith aac_suspend(device_t dev)
56935863739SMike Smith {
570914da7d0SScott Long 	struct aac_softc *sc;
57135863739SMike Smith 
57235863739SMike Smith 	debug_called(1);
573914da7d0SScott Long 
574914da7d0SScott Long 	sc = device_get_softc(dev);
575914da7d0SScott Long 
57635863739SMike Smith 	sc->aac_state |= AAC_STATE_SUSPEND;
57735863739SMike Smith 
57835863739SMike Smith 	AAC_MASK_INTERRUPTS(sc);
57935863739SMike Smith 	return(0);
58035863739SMike Smith }
58135863739SMike Smith 
582914da7d0SScott Long /*
58335863739SMike Smith  * Bring the controller back to a state ready for operation.
58435863739SMike Smith  */
58535863739SMike Smith int
58635863739SMike Smith aac_resume(device_t dev)
58735863739SMike Smith {
588914da7d0SScott Long 	struct aac_softc *sc;
58935863739SMike Smith 
59035863739SMike Smith 	debug_called(1);
591914da7d0SScott Long 
592914da7d0SScott Long 	sc = device_get_softc(dev);
593914da7d0SScott Long 
59435863739SMike Smith 	sc->aac_state &= ~AAC_STATE_SUSPEND;
59535863739SMike Smith 	AAC_UNMASK_INTERRUPTS(sc);
59635863739SMike Smith 	return(0);
59735863739SMike Smith }
59835863739SMike Smith 
599914da7d0SScott Long /*
60035863739SMike Smith  * Take an interrupt.
60135863739SMike Smith  */
60235863739SMike Smith void
60335863739SMike Smith aac_intr(void *arg)
60435863739SMike Smith {
605914da7d0SScott Long 	struct aac_softc *sc;
60670545d1aSScott Long 	u_int16_t reason;
60735863739SMike Smith 
60835863739SMike Smith 	debug_called(2);
60935863739SMike Smith 
610914da7d0SScott Long 	sc = (struct aac_softc *)arg;
611914da7d0SScott Long 
612f30ac74cSScott Long 	/*
6139148fa21SScott Long 	 * Read the status register directly.  This is faster than taking the
6149148fa21SScott Long 	 * driver lock and reading the queues directly.  It also saves having
6159148fa21SScott Long 	 * to turn parts of the driver lock into a spin mutex, which would be
6169148fa21SScott Long 	 * ugly.
617f30ac74cSScott Long 	 */
61835863739SMike Smith 	reason = AAC_GET_ISTATUS(sc);
619f30ac74cSScott Long 	AAC_CLEAR_ISTATUS(sc, reason);
620f30ac74cSScott Long 
6219c3a7fceSScott Long 	/* handle completion processing */
6229148fa21SScott Long 	if (reason & AAC_DB_RESPONSE_READY)
6239148fa21SScott Long 		taskqueue_enqueue_fast(taskqueue_fast, &sc->aac_task_complete);
62435863739SMike Smith 
6259148fa21SScott Long 	/* controller wants to talk to us */
6269148fa21SScott Long 	if (reason & (AAC_DB_PRINTF | AAC_DB_COMMAND_READY)) {
62770545d1aSScott Long 		/*
6289148fa21SScott Long 		 * XXX Make sure that we don't get fooled by strange messages
6299148fa21SScott Long 		 * that start with a NULL.
63070545d1aSScott Long 		 */
6319148fa21SScott Long 		if ((reason & AAC_DB_PRINTF) &&
6329148fa21SScott Long 		    (sc->aac_common->ac_printf[0] == 0))
6339148fa21SScott Long 			sc->aac_common->ac_printf[0] = 32;
63470545d1aSScott Long 
6359148fa21SScott Long 		/*
6369148fa21SScott Long 		 * This might miss doing the actual wakeup.  However, the
637a32a982dSScott Long 		 * msleep that this is waking up has a timeout, so it will
6389148fa21SScott Long 		 * wake up eventually.  AIFs and printfs are low enough
6399148fa21SScott Long 		 * priority that they can handle hanging out for a few seconds
6409148fa21SScott Long 		 * if needed.
6419148fa21SScott Long 		 */
64236e0bf6eSScott Long 		wakeup(sc->aifthread);
64336e0bf6eSScott Long 	}
6449148fa21SScott Long }
64535863739SMike Smith 
646c6eafcf2SScott Long /*
647914da7d0SScott Long  * Command Processing
648914da7d0SScott Long  */
64935863739SMike Smith 
650914da7d0SScott Long /*
65135863739SMike Smith  * Start as much queued I/O as possible on the controller
65235863739SMike Smith  */
653fe3cb0e1SScott Long void
65435863739SMike Smith aac_startio(struct aac_softc *sc)
65535863739SMike Smith {
65635863739SMike Smith 	struct aac_command *cm;
65735863739SMike Smith 
65835863739SMike Smith 	debug_called(2);
65935863739SMike Smith 
660cd481291SScott Long 	if (sc->flags & AAC_QUEUE_FRZN)
661cd481291SScott Long 		return;
662cd481291SScott Long 
66335863739SMike Smith 	for (;;) {
664914da7d0SScott Long 		/*
665914da7d0SScott Long 		 * Try to get a command that's been put off for lack of
666914da7d0SScott Long 		 * resources
667914da7d0SScott Long 		 */
66835863739SMike Smith 		cm = aac_dequeue_ready(sc);
66935863739SMike Smith 
670914da7d0SScott Long 		/*
671914da7d0SScott Long 		 * Try to build a command off the bio queue (ignore error
672914da7d0SScott Long 		 * return)
673914da7d0SScott Long 		 */
6740b94a66eSMike Smith 		if (cm == NULL)
67535863739SMike Smith 			aac_bio_command(sc, &cm);
67635863739SMike Smith 
67735863739SMike Smith 		/* nothing to do? */
67835863739SMike Smith 		if (cm == NULL)
67935863739SMike Smith 			break;
68035863739SMike Smith 
6814102d44bSScott Long 		/*
6824102d44bSScott Long 		 * Try to give the command to the controller.  Any error is
6834102d44bSScott Long 		 * catastrophic since it means that bus_dmamap_load() failed.
6844102d44bSScott Long 		 */
6854102d44bSScott Long 		if (aac_map_command(cm) != 0)
6864102d44bSScott Long 			panic("aac: error mapping command %p\n", cm);
68735863739SMike Smith 	}
68835863739SMike Smith }
68935863739SMike Smith 
690914da7d0SScott Long /*
69135863739SMike Smith  * Deliver a command to the controller; allocate controller resources at the
69235863739SMike Smith  * last moment when possible.
69335863739SMike Smith  */
69435863739SMike Smith static int
695cd481291SScott Long aac_map_command(struct aac_command *cm)
69635863739SMike Smith {
697914da7d0SScott Long 	struct aac_softc *sc;
698ed5c5fb4SMike Smith 	int error;
69935863739SMike Smith 
70035863739SMike Smith 	debug_called(2);
70135863739SMike Smith 
702914da7d0SScott Long 	sc = cm->cm_sc;
703cd481291SScott Long 	error = 0;
704914da7d0SScott Long 
705cd481291SScott Long 	/* don't map more than once */
706cd481291SScott Long 	if (cm->cm_flags & AAC_CMD_MAPPED)
7074102d44bSScott Long 		panic("aac: command %p already mapped", cm);
70835863739SMike Smith 
709cd481291SScott Long 	if (cm->cm_datalen != 0) {
710cd481291SScott Long 		error = bus_dmamap_load(sc->aac_buffer_dmat, cm->cm_datamap,
711cd481291SScott Long 					cm->cm_data, cm->cm_datalen,
712cd481291SScott Long 					aac_map_command_sg, cm, 0);
713cd481291SScott Long 		if (error == EINPROGRESS) {
714cd481291SScott Long 			debug(1, "freezing queue\n");
715cd481291SScott Long 			sc->flags |= AAC_QUEUE_FRZN;
716cd481291SScott Long 			error = 0;
717cd481291SScott Long 		}
7188778f63dSScott Long 	} else {
7198778f63dSScott Long 		aac_map_command_sg(cm, NULL, 0, 0);
720cd481291SScott Long 	}
7210b94a66eSMike Smith 	return (error);
72235863739SMike Smith }
72335863739SMike Smith 
724914da7d0SScott Long /*
72535863739SMike Smith  * Handle notification of one or more FIBs coming from the controller.
72635863739SMike Smith  */
72735863739SMike Smith static void
72870545d1aSScott Long aac_command_thread(struct aac_softc *sc)
72935863739SMike Smith {
73035863739SMike Smith 	struct aac_fib *fib;
73135863739SMike Smith 	u_int32_t fib_size;
7329148fa21SScott Long 	int size, retval;
73335863739SMike Smith 
73436e0bf6eSScott Long 	debug_called(2);
73535863739SMike Smith 
736a32a982dSScott Long 	AAC_LOCK_ACQUIRE(&sc->aac_io_lock);
737a32a982dSScott Long 	sc->aifflags = AAC_AIFFLAGS_RUNNING;
73836e0bf6eSScott Long 
739a32a982dSScott Long 	while ((sc->aifflags & AAC_AIFFLAGS_EXIT) == 0) {
740a32a982dSScott Long 
741a32a982dSScott Long 		retval = 0;
742a32a982dSScott Long 		if ((sc->aifflags & AAC_AIFFLAGS_PENDING) == 0)
743a32a982dSScott Long 			retval = msleep(sc->aifthread, &sc->aac_io_lock, PRIBIO,
744a32a982dSScott Long 					"aifthd", AAC_PERIODIC_INTERVAL * hz);
74536e0bf6eSScott Long 
7469148fa21SScott Long 		/*
7479148fa21SScott Long 		 * First see if any FIBs need to be allocated.  This needs
7489148fa21SScott Long 		 * to be called without the driver lock because contigmalloc
7499148fa21SScott Long 		 * will grab Giant, and would result in an LOR.
7509148fa21SScott Long 		 */
7519148fa21SScott Long 		if ((sc->aifflags & AAC_AIFFLAGS_ALLOCFIBS) != 0) {
752a32a982dSScott Long 			AAC_LOCK_RELEASE(&sc->aac_io_lock);
753a32a982dSScott Long 			aac_alloc_commands(sc);
7549148fa21SScott Long 			AAC_LOCK_ACQUIRE(&sc->aac_io_lock);
7554102d44bSScott Long 			sc->aifflags &= ~AAC_AIFFLAGS_ALLOCFIBS;
756a32a982dSScott Long 			aac_startio(sc);
757a32a982dSScott Long 		}
7589148fa21SScott Long 
7599148fa21SScott Long 		/*
7609148fa21SScott Long 		 * While we're here, check to see if any commands are stuck.
7619148fa21SScott Long 		 * This is pretty low-priority, so it's ok if it doesn't
7629148fa21SScott Long 		 * always fire.
7639148fa21SScott Long 		 */
7649148fa21SScott Long 		if (retval == EWOULDBLOCK)
76570545d1aSScott Long 			aac_timeout(sc);
76670545d1aSScott Long 
76770545d1aSScott Long 		/* Check the hardware printf message buffer */
7689148fa21SScott Long 		if (sc->aac_common->ac_printf[0] != 0)
76970545d1aSScott Long 			aac_print_printf(sc);
77070545d1aSScott Long 
7719148fa21SScott Long 		/* Also check to see if the adapter has a command for us. */
7729148fa21SScott Long 		while (aac_dequeue_fib(sc, AAC_HOST_NORM_CMD_QUEUE,
7739148fa21SScott Long 				       &fib_size, &fib) == 0) {
77435863739SMike Smith 
77536e0bf6eSScott Long 			AAC_PRINT_FIB(sc, fib);
77636e0bf6eSScott Long 
77735863739SMike Smith 			switch (fib->Header.Command) {
77835863739SMike Smith 			case AifRequest:
77936e0bf6eSScott Long 				aac_handle_aif(sc, fib);
78035863739SMike Smith 				break;
78135863739SMike Smith 			default:
782914da7d0SScott Long 				device_printf(sc->aac_dev, "unknown command "
783914da7d0SScott Long 					      "from controller\n");
78435863739SMike Smith 				break;
78535863739SMike Smith 			}
78635863739SMike Smith 
78736e0bf6eSScott Long 			if ((fib->Header.XferState == 0) ||
78836e0bf6eSScott Long 			    (fib->Header.StructType != AAC_FIBTYPE_TFIB))
78936e0bf6eSScott Long 				break;
79036e0bf6eSScott Long 
79170545d1aSScott Long 			/* Return the AIF to the controller. */
79236e0bf6eSScott Long 			if (fib->Header.XferState & AAC_FIBSTATE_FROMADAP) {
79336e0bf6eSScott Long 				fib->Header.XferState |= AAC_FIBSTATE_DONEHOST;
79436e0bf6eSScott Long 				*(AAC_FSAStatus*)fib->data = ST_OK;
79536e0bf6eSScott Long 
79636e0bf6eSScott Long 				/* XXX Compute the Size field? */
79736e0bf6eSScott Long 				size = fib->Header.Size;
79836e0bf6eSScott Long 				if (size > sizeof(struct aac_fib)) {
79936e0bf6eSScott Long 					size = sizeof(struct aac_fib);
80036e0bf6eSScott Long 					fib->Header.Size = size;
80136e0bf6eSScott Long 				}
80236e0bf6eSScott Long 				/*
803914da7d0SScott Long 				 * Since we did not generate this command, it
804914da7d0SScott Long 				 * cannot go through the normal
805914da7d0SScott Long 				 * enqueue->startio chain.
80636e0bf6eSScott Long 				 */
807914da7d0SScott Long 				aac_enqueue_response(sc,
808914da7d0SScott Long 						     AAC_ADAP_NORM_RESP_QUEUE,
809914da7d0SScott Long 						     fib);
81036e0bf6eSScott Long 			}
81136e0bf6eSScott Long 		}
81236e0bf6eSScott Long 	}
81336e0bf6eSScott Long 	sc->aifflags &= ~AAC_AIFFLAGS_RUNNING;
814a32a982dSScott Long 	AAC_LOCK_RELEASE(&sc->aac_io_lock);
81536e0bf6eSScott Long 	wakeup(sc->aac_dev);
81636e0bf6eSScott Long 
81736e0bf6eSScott Long 	mtx_lock(&Giant);
81836e0bf6eSScott Long 	kthread_exit(0);
81935863739SMike Smith }
82035863739SMike Smith 
821914da7d0SScott Long /*
8229c3a7fceSScott Long  * Process completed commands.
82335863739SMike Smith  */
82435863739SMike Smith static void
8259c3a7fceSScott Long aac_complete(void *context, int pending)
82635863739SMike Smith {
8279c3a7fceSScott Long 	struct aac_softc *sc;
82835863739SMike Smith 	struct aac_command *cm;
82935863739SMike Smith 	struct aac_fib *fib;
83035863739SMike Smith 	u_int32_t fib_size;
83135863739SMike Smith 
83235863739SMike Smith 	debug_called(2);
83335863739SMike Smith 
8349c3a7fceSScott Long 	sc = (struct aac_softc *)context;
8359c3a7fceSScott Long 
836ae543596SScott Long 	AAC_LOCK_ACQUIRE(&sc->aac_io_lock);
837ae543596SScott Long 
8389c3a7fceSScott Long 	/* pull completed commands off the queue */
83935863739SMike Smith 	for (;;) {
84035863739SMike Smith 		/* look for completed FIBs on our queue */
841914da7d0SScott Long 		if (aac_dequeue_fib(sc, AAC_HOST_NORM_RESP_QUEUE, &fib_size,
842914da7d0SScott Long 				    &fib))
84335863739SMike Smith 			break;	/* nothing to do */
84435863739SMike Smith 
845ecd1c51fSScott Long 		/* get the command, unmap and hand off for processing */
846cb0d64b9SScott Long 		cm = sc->aac_commands + fib->Header.SenderData;
84735863739SMike Smith 		if (cm == NULL) {
84835863739SMike Smith 			AAC_PRINT_FIB(sc, fib);
8499c3a7fceSScott Long 			break;
8509c3a7fceSScott Long 		}
8519c3a7fceSScott Long 
8520b94a66eSMike Smith 		aac_remove_busy(cm);
853ecd1c51fSScott Long 		aac_unmap_command(cm);
85435863739SMike Smith 		cm->cm_flags |= AAC_CMD_COMPLETED;
85535863739SMike Smith 
85635863739SMike Smith 		/* is there a completion handler? */
85735863739SMike Smith 		if (cm->cm_complete != NULL) {
85835863739SMike Smith 			cm->cm_complete(cm);
85935863739SMike Smith 		} else {
86035863739SMike Smith 			/* assume that someone is sleeping on this command */
86135863739SMike Smith 			wakeup(cm);
86235863739SMike Smith 		}
86335863739SMike Smith 	}
8640b94a66eSMike Smith 
8650b94a66eSMike Smith 	/* see if we can start some more I/O */
866cd481291SScott Long 	sc->flags &= ~AAC_QUEUE_FRZN;
8670b94a66eSMike Smith 	aac_startio(sc);
868ae543596SScott Long 
869ae543596SScott Long 	AAC_LOCK_RELEASE(&sc->aac_io_lock);
87035863739SMike Smith }
87135863739SMike Smith 
872914da7d0SScott Long /*
87335863739SMike Smith  * Handle a bio submitted from a disk device.
87435863739SMike Smith  */
87535863739SMike Smith void
87635863739SMike Smith aac_submit_bio(struct bio *bp)
87735863739SMike Smith {
878914da7d0SScott Long 	struct aac_disk *ad;
879914da7d0SScott Long 	struct aac_softc *sc;
88035863739SMike Smith 
88135863739SMike Smith 	debug_called(2);
88235863739SMike Smith 
8837540e65eSScott Long 	ad = (struct aac_disk *)bp->bio_disk->d_drv1;
884914da7d0SScott Long 	sc = ad->ad_controller;
885914da7d0SScott Long 
88635863739SMike Smith 	/* queue the BIO and try to get some work done */
8870b94a66eSMike Smith 	aac_enqueue_bio(sc, bp);
88835863739SMike Smith 	aac_startio(sc);
88935863739SMike Smith }
89035863739SMike Smith 
891914da7d0SScott Long /*
89235863739SMike Smith  * Get a bio and build a command to go with it.
89335863739SMike Smith  */
89435863739SMike Smith static int
89535863739SMike Smith aac_bio_command(struct aac_softc *sc, struct aac_command **cmp)
89635863739SMike Smith {
89735863739SMike Smith 	struct aac_command *cm;
89835863739SMike Smith 	struct aac_fib *fib;
89935863739SMike Smith 	struct aac_disk *ad;
90035863739SMike Smith 	struct bio *bp;
90135863739SMike Smith 
90235863739SMike Smith 	debug_called(2);
90335863739SMike Smith 
90435863739SMike Smith 	/* get the resources we will need */
90535863739SMike Smith 	cm = NULL;
906a32a982dSScott Long 	bp = NULL;
90735863739SMike Smith 	if (aac_alloc_command(sc, &cm))	/* get a command */
90835863739SMike Smith 		goto fail;
909a32a982dSScott Long 	if ((bp = aac_dequeue_bio(sc)) == NULL)
910a32a982dSScott Long 		goto fail;
91135863739SMike Smith 
91235863739SMike Smith 	/* fill out the command */
9130b94a66eSMike Smith 	cm->cm_data = (void *)bp->bio_data;
9140b94a66eSMike Smith 	cm->cm_datalen = bp->bio_bcount;
9150b94a66eSMike Smith 	cm->cm_complete = aac_bio_complete;
91635863739SMike Smith 	cm->cm_private = bp;
9170b94a66eSMike Smith 	cm->cm_timestamp = time_second;
91836e0bf6eSScott Long 	cm->cm_queue = AAC_ADAP_NORM_CMD_QUEUE;
91935863739SMike Smith 
92035863739SMike Smith 	/* build the FIB */
92135863739SMike Smith 	fib = cm->cm_fib;
922b85f5808SScott Long 	fib->Header.Size = sizeof(struct aac_fib_header);
92335863739SMike Smith 	fib->Header.XferState =
92435863739SMike Smith 		AAC_FIBSTATE_HOSTOWNED   |
92535863739SMike Smith 		AAC_FIBSTATE_INITIALISED |
926f30ac74cSScott Long 		AAC_FIBSTATE_EMPTY	 |
92735863739SMike Smith 		AAC_FIBSTATE_FROMHOST	 |
92835863739SMike Smith 		AAC_FIBSTATE_REXPECTED   |
929f30ac74cSScott Long 		AAC_FIBSTATE_NORM	 |
930f30ac74cSScott Long 		AAC_FIBSTATE_ASYNC	 |
931f30ac74cSScott Long 		AAC_FIBSTATE_FAST_RESPONSE;
93235863739SMike Smith 
93335863739SMike Smith 	/* build the read/write request */
9347540e65eSScott Long 	ad = (struct aac_disk *)bp->bio_disk->d_drv1;
935b85f5808SScott Long 
936b85f5808SScott Long 	if ((sc->flags & AAC_FLAGS_SG_64BIT) == 0) {
937b85f5808SScott Long 		fib->Header.Command = ContainerCommand;
9389e2e96d8SScott Long 		if (bp->bio_cmd == BIO_READ) {
939b85f5808SScott Long 			struct aac_blockread *br;
94035863739SMike Smith 			br = (struct aac_blockread *)&fib->data[0];
94135863739SMike Smith 			br->Command = VM_CtBlockRead;
94235863739SMike Smith 			br->ContainerId = ad->ad_container->co_mntobj.ObjectId;
94335863739SMike Smith 			br->BlockNumber = bp->bio_pblkno;
94435863739SMike Smith 			br->ByteCount = bp->bio_bcount;
94535863739SMike Smith 			fib->Header.Size += sizeof(struct aac_blockread);
94635863739SMike Smith 			cm->cm_sgtable = &br->SgMap;
94735863739SMike Smith 			cm->cm_flags |= AAC_CMD_DATAIN;
94835863739SMike Smith 		} else {
949b85f5808SScott Long 			struct aac_blockwrite *bw;
95035863739SMike Smith 			bw = (struct aac_blockwrite *)&fib->data[0];
95135863739SMike Smith 			bw->Command = VM_CtBlockWrite;
95235863739SMike Smith 			bw->ContainerId = ad->ad_container->co_mntobj.ObjectId;
95335863739SMike Smith 			bw->BlockNumber = bp->bio_pblkno;
95435863739SMike Smith 			bw->ByteCount = bp->bio_bcount;
955b85f5808SScott Long 			bw->Stable = CUNSTABLE;
95635863739SMike Smith 			fib->Header.Size += sizeof(struct aac_blockwrite);
95735863739SMike Smith 			cm->cm_flags |= AAC_CMD_DATAOUT;
95835863739SMike Smith 			cm->cm_sgtable = &bw->SgMap;
95935863739SMike Smith 		}
960b85f5808SScott Long 	} else {
961b85f5808SScott Long 		fib->Header.Command = ContainerCommand64;
962b85f5808SScott Long 		if (bp->bio_cmd == BIO_READ) {
963b85f5808SScott Long 			struct aac_blockread64 *br;
964b85f5808SScott Long 			br = (struct aac_blockread64 *)&fib->data[0];
965b85f5808SScott Long 			br->Command = VM_CtHostRead64;
966b85f5808SScott Long 			br->ContainerId = ad->ad_container->co_mntobj.ObjectId;
967b85f5808SScott Long 			br->SectorCount = bp->bio_bcount / AAC_BLOCK_SIZE;
968b85f5808SScott Long 			br->BlockNumber = bp->bio_pblkno;
969b85f5808SScott Long 			br->Pad = 0;
970b85f5808SScott Long 			br->Flags = 0;
971b85f5808SScott Long 			fib->Header.Size += sizeof(struct aac_blockread64);
972b85f5808SScott Long 			cm->cm_flags |= AAC_CMD_DATAOUT;
973b85f5808SScott Long 			(struct aac_sg_table64 *)cm->cm_sgtable = &br->SgMap64;
974b85f5808SScott Long 		} else {
975b85f5808SScott Long 			struct aac_blockwrite64 *bw;
976b85f5808SScott Long 			bw = (struct aac_blockwrite64 *)&fib->data[0];
977b85f5808SScott Long 			bw->Command = VM_CtHostWrite64;
978b85f5808SScott Long 			bw->ContainerId = ad->ad_container->co_mntobj.ObjectId;
979b85f5808SScott Long 			bw->SectorCount = bp->bio_bcount / AAC_BLOCK_SIZE;
980b85f5808SScott Long 			bw->BlockNumber = bp->bio_pblkno;
981b85f5808SScott Long 			bw->Pad = 0;
982b85f5808SScott Long 			bw->Flags = 0;
983b85f5808SScott Long 			fib->Header.Size += sizeof(struct aac_blockwrite64);
984b85f5808SScott Long 			cm->cm_flags |= AAC_CMD_DATAIN;
985b85f5808SScott Long 			(struct aac_sg_table64 *)cm->cm_sgtable = &bw->SgMap64;
986b85f5808SScott Long 		}
987b85f5808SScott Long 	}
98835863739SMike Smith 
98935863739SMike Smith 	*cmp = cm;
99035863739SMike Smith 	return(0);
99135863739SMike Smith 
99235863739SMike Smith fail:
99335863739SMike Smith 	if (bp != NULL)
9940b94a66eSMike Smith 		aac_enqueue_bio(sc, bp);
99535863739SMike Smith 	if (cm != NULL)
99635863739SMike Smith 		aac_release_command(cm);
99735863739SMike Smith 	return(ENOMEM);
99835863739SMike Smith }
99935863739SMike Smith 
1000914da7d0SScott Long /*
100135863739SMike Smith  * Handle a bio-instigated command that has been completed.
100235863739SMike Smith  */
100335863739SMike Smith static void
100435863739SMike Smith aac_bio_complete(struct aac_command *cm)
100535863739SMike Smith {
100635863739SMike Smith 	struct aac_blockread_response *brr;
100735863739SMike Smith 	struct aac_blockwrite_response *bwr;
100835863739SMike Smith 	struct bio *bp;
100935863739SMike Smith 	AAC_FSAStatus status;
101035863739SMike Smith 
101135863739SMike Smith 	/* fetch relevant status and then release the command */
101235863739SMike Smith 	bp = (struct bio *)cm->cm_private;
10139e2e96d8SScott Long 	if (bp->bio_cmd == BIO_READ) {
101435863739SMike Smith 		brr = (struct aac_blockread_response *)&cm->cm_fib->data[0];
101535863739SMike Smith 		status = brr->Status;
101635863739SMike Smith 	} else {
101735863739SMike Smith 		bwr = (struct aac_blockwrite_response *)&cm->cm_fib->data[0];
101835863739SMike Smith 		status = bwr->Status;
101935863739SMike Smith 	}
102035863739SMike Smith 	aac_release_command(cm);
102135863739SMike Smith 
102235863739SMike Smith 	/* fix up the bio based on status */
102335863739SMike Smith 	if (status == ST_OK) {
102435863739SMike Smith 		bp->bio_resid = 0;
102535863739SMike Smith 	} else {
102635863739SMike Smith 		bp->bio_error = EIO;
102735863739SMike Smith 		bp->bio_flags |= BIO_ERROR;
10280b94a66eSMike Smith 		/* pass an error string out to the disk layer */
1029914da7d0SScott Long 		bp->bio_driver1 = aac_describe_code(aac_command_status_table,
1030914da7d0SScott Long 						    status);
103135863739SMike Smith 	}
10320b94a66eSMike Smith 	aac_biodone(bp);
103335863739SMike Smith }
103435863739SMike Smith 
1035914da7d0SScott Long /*
103635863739SMike Smith  * Submit a command to the controller, return when it completes.
1037b3457b51SScott Long  * XXX This is very dangerous!  If the card has gone out to lunch, we could
1038b3457b51SScott Long  *     be stuck here forever.  At the same time, signals are not caught
1039b3457b51SScott Long  *     because there is a risk that a signal could wakeup the tsleep before
1040b3457b51SScott Long  *     the card has a chance to complete the command.  The passed in timeout
1041b3457b51SScott Long  *     is ignored for the same reason.  Since there is no way to cancel a
1042b3457b51SScott Long  *     command in progress, we should probably create a 'dead' queue where
1043b3457b51SScott Long  *     commands go that have been interrupted/timed-out/etc, that keeps them
1044b3457b51SScott Long  *     out of the free pool.  That way, if the card is just slow, it won't
1045b3457b51SScott Long  *     spam the memory of a command that has been recycled.
104635863739SMike Smith  */
104735863739SMike Smith static int
104835863739SMike Smith aac_wait_command(struct aac_command *cm, int timeout)
104935863739SMike Smith {
1050ae543596SScott Long 	struct aac_softc *sc;
1051ae543596SScott Long 	int error = 0;
105235863739SMike Smith 
105335863739SMike Smith 	debug_called(2);
105435863739SMike Smith 
1055ae543596SScott Long 	sc = cm->cm_sc;
1056ae543596SScott Long 
105735863739SMike Smith 	/* Put the command on the ready queue and get things going */
105836e0bf6eSScott Long 	cm->cm_queue = AAC_ADAP_NORM_CMD_QUEUE;
105935863739SMike Smith 	aac_enqueue_ready(cm);
1060ae543596SScott Long 	aac_startio(sc);
106135863739SMike Smith 	while (!(cm->cm_flags & AAC_CMD_COMPLETED) && (error != EWOULDBLOCK)) {
1062ae543596SScott Long 		error = msleep(cm, &sc->aac_io_lock, PRIBIO, "aacwait", 0);
106335863739SMike Smith 	}
106435863739SMike Smith 	return(error);
106535863739SMike Smith }
106635863739SMike Smith 
1067914da7d0SScott Long /*
1068914da7d0SScott Long  *Command Buffer Management
1069914da7d0SScott Long  */
107035863739SMike Smith 
1071914da7d0SScott Long /*
107235863739SMike Smith  * Allocate a command.
107335863739SMike Smith  */
1074fe3cb0e1SScott Long int
107535863739SMike Smith aac_alloc_command(struct aac_softc *sc, struct aac_command **cmp)
107635863739SMike Smith {
107735863739SMike Smith 	struct aac_command *cm;
107835863739SMike Smith 
107935863739SMike Smith 	debug_called(3);
108035863739SMike Smith 
1081ffb37f33SScott Long 	if ((cm = aac_dequeue_free(sc)) == NULL) {
1082b85f5808SScott Long 		if (sc->total_fibs < sc->aac_max_fibs) {
1083ae543596SScott Long 			sc->aifflags |= AAC_AIFFLAGS_ALLOCFIBS;
1084ae543596SScott Long 			wakeup(sc->aifthread);
1085b85f5808SScott Long 		}
1086ae543596SScott Long 		return (EBUSY);
1087ffb37f33SScott Long 	}
108835863739SMike Smith 
10890b94a66eSMike Smith 	*cmp = cm;
10900b94a66eSMike Smith 	return(0);
10910b94a66eSMike Smith }
10920b94a66eSMike Smith 
1093914da7d0SScott Long /*
10940b94a66eSMike Smith  * Release a command back to the freelist.
10950b94a66eSMike Smith  */
1096fe3cb0e1SScott Long void
10970b94a66eSMike Smith aac_release_command(struct aac_command *cm)
10980b94a66eSMike Smith {
10990b94a66eSMike Smith 	debug_called(3);
11000b94a66eSMike Smith 
11010b94a66eSMike Smith 	/* (re)initialise the command/FIB */
110235863739SMike Smith 	cm->cm_sgtable = NULL;
110335863739SMike Smith 	cm->cm_flags = 0;
110435863739SMike Smith 	cm->cm_complete = NULL;
110535863739SMike Smith 	cm->cm_private = NULL;
110635863739SMike Smith 	cm->cm_fib->Header.XferState = AAC_FIBSTATE_EMPTY;
110735863739SMike Smith 	cm->cm_fib->Header.StructType = AAC_FIBTYPE_TFIB;
110835863739SMike Smith 	cm->cm_fib->Header.Flags = 0;
110935863739SMike Smith 	cm->cm_fib->Header.SenderSize = sizeof(struct aac_fib);
111035863739SMike Smith 
111135863739SMike Smith 	/*
111235863739SMike Smith 	 * These are duplicated in aac_start to cover the case where an
111335863739SMike Smith 	 * intermediate stage may have destroyed them.  They're left
111435863739SMike Smith 	 * initialised here for debugging purposes only.
111535863739SMike Smith 	 */
1116f30ac74cSScott Long 	cm->cm_fib->Header.ReceiverFibAddress = (u_int32_t)cm->cm_fibphys;
1117f30ac74cSScott Long 	cm->cm_fib->Header.SenderData = 0;
111835863739SMike Smith 
111935863739SMike Smith 	aac_enqueue_free(cm);
112035863739SMike Smith }
112135863739SMike Smith 
1122914da7d0SScott Long /*
11230b94a66eSMike Smith  * Map helper for command/FIB allocation.
112435863739SMike Smith  */
112535863739SMike Smith static void
11260b94a66eSMike Smith aac_map_command_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error)
112735863739SMike Smith {
11288480cc63SScott Long 	uint32_t	*fibphys;
1129914da7d0SScott Long 
11308480cc63SScott Long 	fibphys = (uint32_t *)arg;
113135863739SMike Smith 
113235863739SMike Smith 	debug_called(3);
113335863739SMike Smith 
1134ffb37f33SScott Long 	*fibphys = segs[0].ds_addr;
113535863739SMike Smith }
113635863739SMike Smith 
1137914da7d0SScott Long /*
11380b94a66eSMike Smith  * Allocate and initialise commands/FIBs for this adapter.
113935863739SMike Smith  */
11400b94a66eSMike Smith static int
11410b94a66eSMike Smith aac_alloc_commands(struct aac_softc *sc)
114235863739SMike Smith {
114335863739SMike Smith 	struct aac_command *cm;
1144ffb37f33SScott Long 	struct aac_fibmap *fm;
11458480cc63SScott Long 	uint32_t fibphys;
1146ffb37f33SScott Long 	int i, error;
114735863739SMike Smith 
1148a6d35632SScott Long 	debug_called(2);
114935863739SMike Smith 
1150a6d35632SScott Long 	if (sc->total_fibs + AAC_FIB_COUNT > sc->aac_max_fibs)
1151ffb37f33SScott Long 		return (ENOMEM);
1152ffb37f33SScott Long 
11538480cc63SScott Long 	fm = malloc(sizeof(struct aac_fibmap), M_AACBUF, M_NOWAIT|M_ZERO);
1154a6d35632SScott Long 	if (fm == NULL)
1155a6d35632SScott Long 		return (ENOMEM);
1156ffb37f33SScott Long 
11570b94a66eSMike Smith 	/* allocate the FIBs in DMAable memory and load them */
1158ffb37f33SScott Long 	if (bus_dmamem_alloc(sc->aac_fib_dmat, (void **)&fm->aac_fibs,
1159ffb37f33SScott Long 			     BUS_DMA_NOWAIT, &fm->aac_fibmap)) {
116070545d1aSScott Long 		device_printf(sc->aac_dev,
116170545d1aSScott Long 			      "Not enough contiguous memory available.\n");
11628480cc63SScott Long 		free(fm, M_AACBUF);
11630b94a66eSMike Smith 		return (ENOMEM);
116435863739SMike Smith 	}
1165128aa5a0SScott Long 
1166cd481291SScott Long 	/* Ignore errors since this doesn't bounce */
1167cd481291SScott Long 	(void)bus_dmamap_load(sc->aac_fib_dmat, fm->aac_fibmap, fm->aac_fibs,
1168ffb37f33SScott Long 			      AAC_FIB_COUNT * sizeof(struct aac_fib),
1169ffb37f33SScott Long 			      aac_map_command_helper, &fibphys, 0);
1170128aa5a0SScott Long 
11710b94a66eSMike Smith 	/* initialise constant fields in the command structure */
11729148fa21SScott Long 	AAC_LOCK_ACQUIRE(&sc->aac_io_lock);
1173ffb37f33SScott Long 	bzero(fm->aac_fibs, AAC_FIB_COUNT * sizeof(struct aac_fib));
11740b94a66eSMike Smith 	for (i = 0; i < AAC_FIB_COUNT; i++) {
11758480cc63SScott Long 		cm = sc->aac_commands + sc->total_fibs;
1176ffb37f33SScott Long 		fm->aac_commands = cm;
117735863739SMike Smith 		cm->cm_sc = sc;
1178ffb37f33SScott Long 		cm->cm_fib = fm->aac_fibs + i;
11798480cc63SScott Long 		cm->cm_fibphys = fibphys + (i * sizeof(struct aac_fib));
1180cb0d64b9SScott Long 		cm->cm_index = sc->total_fibs;
118135863739SMike Smith 
1182ffb37f33SScott Long 		if ((error = bus_dmamap_create(sc->aac_buffer_dmat, 0,
1183ffb37f33SScott Long 					       &cm->cm_datamap)) == 0)
118435863739SMike Smith 			aac_release_command(cm);
1185ffb37f33SScott Long 		else
11868480cc63SScott Long 			break;
11878480cc63SScott Long 		sc->total_fibs++;
118835863739SMike Smith 	}
1189ffb37f33SScott Long 
11908480cc63SScott Long 	if (i > 0) {
1191ffb37f33SScott Long 		TAILQ_INSERT_TAIL(&sc->aac_fibmap_tqh, fm, fm_link);
1192a6d35632SScott Long 		debug(1, "total_fibs= %d\n", sc->total_fibs);
11939148fa21SScott Long 		AAC_LOCK_RELEASE(&sc->aac_io_lock);
11940b94a66eSMike Smith 		return (0);
119535863739SMike Smith 	}
119635863739SMike Smith 
11979148fa21SScott Long 	AAC_LOCK_RELEASE(&sc->aac_io_lock);
11988480cc63SScott Long 	bus_dmamap_unload(sc->aac_fib_dmat, fm->aac_fibmap);
11998480cc63SScott Long 	bus_dmamem_free(sc->aac_fib_dmat, fm->aac_fibs, fm->aac_fibmap);
12008480cc63SScott Long 	free(fm, M_AACBUF);
12018480cc63SScott Long 	return (ENOMEM);
12028480cc63SScott Long }
12038480cc63SScott Long 
1204914da7d0SScott Long /*
12050b94a66eSMike Smith  * Free FIBs owned by this adapter.
120635863739SMike Smith  */
120735863739SMike Smith static void
12088480cc63SScott Long aac_free_commands(struct aac_softc *sc)
120935863739SMike Smith {
12108480cc63SScott Long 	struct aac_fibmap *fm;
1211ffb37f33SScott Long 	struct aac_command *cm;
121235863739SMike Smith 	int i;
121335863739SMike Smith 
121435863739SMike Smith 	debug_called(1);
121535863739SMike Smith 
12168480cc63SScott Long 	while ((fm = TAILQ_FIRST(&sc->aac_fibmap_tqh)) != NULL) {
12178480cc63SScott Long 
12188480cc63SScott Long 		TAILQ_REMOVE(&sc->aac_fibmap_tqh, fm, fm_link);
12198480cc63SScott Long 		/*
12208480cc63SScott Long 		 * We check against total_fibs to handle partially
12218480cc63SScott Long 		 * allocated blocks.
12228480cc63SScott Long 		 */
12238480cc63SScott Long 		for (i = 0; i < AAC_FIB_COUNT && sc->total_fibs--; i++) {
1224ffb37f33SScott Long 			cm = fm->aac_commands + i;
1225ffb37f33SScott Long 			bus_dmamap_destroy(sc->aac_buffer_dmat, cm->cm_datamap);
1226ffb37f33SScott Long 		}
1227ffb37f33SScott Long 		bus_dmamap_unload(sc->aac_fib_dmat, fm->aac_fibmap);
1228ffb37f33SScott Long 		bus_dmamem_free(sc->aac_fib_dmat, fm->aac_fibs, fm->aac_fibmap);
12298480cc63SScott Long 		free(fm, M_AACBUF);
12308480cc63SScott Long 	}
123135863739SMike Smith }
123235863739SMike Smith 
1233914da7d0SScott Long /*
123435863739SMike Smith  * Command-mapping helper function - populate this command's s/g table.
123535863739SMike Smith  */
123635863739SMike Smith static void
123735863739SMike Smith aac_map_command_sg(void *arg, bus_dma_segment_t *segs, int nseg, int error)
123835863739SMike Smith {
1239cd481291SScott Long 	struct aac_softc *sc;
1240914da7d0SScott Long 	struct aac_command *cm;
1241914da7d0SScott Long 	struct aac_fib *fib;
124235863739SMike Smith 	int i;
124335863739SMike Smith 
124435863739SMike Smith 	debug_called(3);
124535863739SMike Smith 
1246914da7d0SScott Long 	cm = (struct aac_command *)arg;
1247cd481291SScott Long 	sc = cm->cm_sc;
1248914da7d0SScott Long 	fib = cm->cm_fib;
1249914da7d0SScott Long 
125035863739SMike Smith 	/* copy into the FIB */
1251b85f5808SScott Long 	if (cm->cm_sgtable != NULL) {
1252b85f5808SScott Long 		if ((cm->cm_sc->flags & AAC_FLAGS_SG_64BIT) == 0) {
1253b85f5808SScott Long 			struct aac_sg_table *sg;
1254b85f5808SScott Long 			sg = cm->cm_sgtable;
125535863739SMike Smith 			sg->SgCount = nseg;
125635863739SMike Smith 			for (i = 0; i < nseg; i++) {
125735863739SMike Smith 				sg->SgEntry[i].SgAddress = segs[i].ds_addr;
125835863739SMike Smith 				sg->SgEntry[i].SgByteCount = segs[i].ds_len;
125935863739SMike Smith 			}
126035863739SMike Smith 			/* update the FIB size for the s/g count */
126135863739SMike Smith 			fib->Header.Size += nseg * sizeof(struct aac_sg_entry);
1262b85f5808SScott Long 		} else {
1263b85f5808SScott Long 			struct aac_sg_table64 *sg;
1264b85f5808SScott Long 			sg = (struct aac_sg_table64 *)cm->cm_sgtable;
1265b85f5808SScott Long 			sg->SgCount = nseg;
1266b85f5808SScott Long 			for (i = 0; i < nseg; i++) {
1267b85f5808SScott Long 				sg->SgEntry64[i].SgAddress = segs[i].ds_addr;
1268b85f5808SScott Long 				sg->SgEntry64[i].SgByteCount = segs[i].ds_len;
126935863739SMike Smith 			}
1270b85f5808SScott Long 			/* update the FIB size for the s/g count */
1271b85f5808SScott Long 			fib->Header.Size += nseg*sizeof(struct aac_sg_entry64);
1272b85f5808SScott Long 		}
1273b85f5808SScott Long 	}
127435863739SMike Smith 
1275cd481291SScott Long 	/* Fix up the address values in the FIB.  Use the command array index
1276cd481291SScott Long 	 * instead of a pointer since these fields are only 32 bits.  Shift
1277cd481291SScott Long 	 * the SenderFibAddress over to make room for the fast response bit.
127835863739SMike Smith 	 */
1279cd481291SScott Long 	cm->cm_fib->Header.SenderFibAddress = (cm->cm_index << 1);
1280cd481291SScott Long 	cm->cm_fib->Header.ReceiverFibAddress = cm->cm_fibphys;
128135863739SMike Smith 
1282cd481291SScott Long 	/* save a pointer to the command for speedy reverse-lookup */
1283cd481291SScott Long 	cm->cm_fib->Header.SenderData = cm->cm_index;
128435863739SMike Smith 
128535863739SMike Smith 	if (cm->cm_flags & AAC_CMD_DATAIN)
1286c6eafcf2SScott Long 		bus_dmamap_sync(sc->aac_buffer_dmat, cm->cm_datamap,
1287c6eafcf2SScott Long 				BUS_DMASYNC_PREREAD);
128835863739SMike Smith 	if (cm->cm_flags & AAC_CMD_DATAOUT)
1289c6eafcf2SScott Long 		bus_dmamap_sync(sc->aac_buffer_dmat, cm->cm_datamap,
1290c6eafcf2SScott Long 				BUS_DMASYNC_PREWRITE);
129135863739SMike Smith 	cm->cm_flags |= AAC_CMD_MAPPED;
1292cd481291SScott Long 
1293cd481291SScott Long 	/* put the FIB on the outbound queue */
12944102d44bSScott Long 	if (aac_enqueue_fib(sc, cm->cm_queue, cm) == EBUSY) {
129541a1325dSScott Long 		aac_remove_busy(cm);
12964102d44bSScott Long 		aac_unmap_command(cm);
1297cd481291SScott Long 		aac_requeue_ready(cm);
12984102d44bSScott Long 	}
1299cd481291SScott Long 
1300cd481291SScott Long 	return;
130135863739SMike Smith }
130235863739SMike Smith 
1303914da7d0SScott Long /*
130435863739SMike Smith  * Unmap a command from controller-visible space.
130535863739SMike Smith  */
130635863739SMike Smith static void
130735863739SMike Smith aac_unmap_command(struct aac_command *cm)
130835863739SMike Smith {
1309914da7d0SScott Long 	struct aac_softc *sc;
131035863739SMike Smith 
131135863739SMike Smith 	debug_called(2);
131235863739SMike Smith 
1313914da7d0SScott Long 	sc = cm->cm_sc;
1314914da7d0SScott Long 
131535863739SMike Smith 	if (!(cm->cm_flags & AAC_CMD_MAPPED))
131635863739SMike Smith 		return;
131735863739SMike Smith 
131835863739SMike Smith 	if (cm->cm_datalen != 0) {
131935863739SMike Smith 		if (cm->cm_flags & AAC_CMD_DATAIN)
1320c6eafcf2SScott Long 			bus_dmamap_sync(sc->aac_buffer_dmat, cm->cm_datamap,
1321c6eafcf2SScott Long 					BUS_DMASYNC_POSTREAD);
132235863739SMike Smith 		if (cm->cm_flags & AAC_CMD_DATAOUT)
1323c6eafcf2SScott Long 			bus_dmamap_sync(sc->aac_buffer_dmat, cm->cm_datamap,
1324c6eafcf2SScott Long 					BUS_DMASYNC_POSTWRITE);
132535863739SMike Smith 
132635863739SMike Smith 		bus_dmamap_unload(sc->aac_buffer_dmat, cm->cm_datamap);
132735863739SMike Smith 	}
132835863739SMike Smith 	cm->cm_flags &= ~AAC_CMD_MAPPED;
132935863739SMike Smith }
133035863739SMike Smith 
1331914da7d0SScott Long /*
1332914da7d0SScott Long  * Hardware Interface
1333914da7d0SScott Long  */
133435863739SMike Smith 
1335914da7d0SScott Long /*
133635863739SMike Smith  * Initialise the adapter.
133735863739SMike Smith  */
133835863739SMike Smith static void
133935863739SMike Smith aac_common_map(void *arg, bus_dma_segment_t *segs, int nseg, int error)
134035863739SMike Smith {
1341914da7d0SScott Long 	struct aac_softc *sc;
134235863739SMike Smith 
134335863739SMike Smith 	debug_called(1);
134435863739SMike Smith 
1345914da7d0SScott Long 	sc = (struct aac_softc *)arg;
1346914da7d0SScott Long 
134735863739SMike Smith 	sc->aac_common_busaddr = segs[0].ds_addr;
134835863739SMike Smith }
134935863739SMike Smith 
1350a6d35632SScott Long static int
1351a6d35632SScott Long aac_check_firmware(struct aac_softc *sc)
1352a6d35632SScott Long {
1353a6d35632SScott Long 	u_int32_t major, minor, options;
1354a6d35632SScott Long 
1355a6d35632SScott Long 	debug_called(1);
1356a6d35632SScott Long 
1357fe94b852SScott Long 	/*
1358fe94b852SScott Long 	 * Retrieve the firmware version numbers.  Dell PERC2/QC cards with
1359fe94b852SScott Long 	 * firmware version 1.x are not compatible with this driver.
1360fe94b852SScott Long 	 */
1361a6d35632SScott Long 	if (sc->flags & AAC_FLAGS_PERC2QC) {
1362fe94b852SScott Long 		if (aac_sync_command(sc, AAC_MONKER_GETKERNVER, 0, 0, 0, 0,
1363fe94b852SScott Long 				     NULL)) {
1364fe94b852SScott Long 			device_printf(sc->aac_dev,
1365fe94b852SScott Long 				      "Error reading firmware version\n");
1366fe94b852SScott Long 			return (EIO);
1367fe94b852SScott Long 		}
1368fe94b852SScott Long 
1369fe94b852SScott Long 		/* These numbers are stored as ASCII! */
1370a6d35632SScott Long 		major = (AAC_GET_MAILBOX(sc, 1) & 0xff) - 0x30;
1371a6d35632SScott Long 		minor = (AAC_GET_MAILBOX(sc, 2) & 0xff) - 0x30;
1372fe94b852SScott Long 		if (major == 1) {
1373fe94b852SScott Long 			device_printf(sc->aac_dev,
1374fe94b852SScott Long 			    "Firmware version %d.%d is not supported.\n",
1375fe94b852SScott Long 			    major, minor);
1376fe94b852SScott Long 			return (EINVAL);
1377fe94b852SScott Long 		}
1378fe94b852SScott Long 	}
1379fe94b852SScott Long 
1380a6d35632SScott Long 	/*
1381a6d35632SScott Long 	 * Retrieve the capabilities/supported options word so we know what
1382a6d35632SScott Long 	 * work-arounds to enable.
1383a6d35632SScott Long 	 */
1384a6d35632SScott Long 	if (aac_sync_command(sc, AAC_MONKER_GETINFO, 0, 0, 0, 0, NULL)) {
1385a6d35632SScott Long 		device_printf(sc->aac_dev, "RequestAdapterInfo failed\n");
1386a6d35632SScott Long 		return (EIO);
1387a6d35632SScott Long 	}
1388a6d35632SScott Long 	options = AAC_GET_MAILBOX(sc, 1);
1389a6d35632SScott Long 	sc->supported_options = options;
1390a6d35632SScott Long 
1391a6d35632SScott Long 	if ((options & AAC_SUPPORTED_4GB_WINDOW) != 0 &&
1392a6d35632SScott Long 	    (sc->flags & AAC_FLAGS_NO4GB) == 0)
1393a6d35632SScott Long 		sc->flags |= AAC_FLAGS_4GB_WINDOW;
1394a6d35632SScott Long 	if (options & AAC_SUPPORTED_NONDASD)
1395a6d35632SScott Long 		sc->flags |= AAC_FLAGS_ENABLE_CAM;
1396cd481291SScott Long 	if ((options & AAC_SUPPORTED_SGMAP_HOST64) != 0
1397cd481291SScott Long 	     && (sizeof(bus_addr_t) > 4)) {
1398a6d35632SScott Long 		device_printf(sc->aac_dev, "Enabling 64-bit address support\n");
1399a6d35632SScott Long 		sc->flags |= AAC_FLAGS_SG_64BIT;
1400a6d35632SScott Long 	}
1401a6d35632SScott Long 
1402a6d35632SScott Long 	/* Check for broken hardware that does a lower number of commands */
1403a6d35632SScott Long 	if ((sc->flags & AAC_FLAGS_256FIBS) == 0)
1404a6d35632SScott Long 		sc->aac_max_fibs = AAC_MAX_FIBS;
1405a6d35632SScott Long 	else
1406a6d35632SScott Long 		sc->aac_max_fibs = 256;
1407a6d35632SScott Long 
1408fe94b852SScott Long 	return (0);
1409fe94b852SScott Long }
1410fe94b852SScott Long 
141135863739SMike Smith static int
141235863739SMike Smith aac_init(struct aac_softc *sc)
141335863739SMike Smith {
141435863739SMike Smith 	struct aac_adapter_init	*ip;
141535863739SMike Smith 	time_t then;
1416b88ffdc8SScott Long 	u_int32_t code, qoffset;
1417a6d35632SScott Long 	int error;
141835863739SMike Smith 
141935863739SMike Smith 	debug_called(1);
142035863739SMike Smith 
142135863739SMike Smith 	/*
142235863739SMike Smith 	 * First wait for the adapter to come ready.
142335863739SMike Smith 	 */
142435863739SMike Smith 	then = time_second;
142535863739SMike Smith 	do {
142635863739SMike Smith 		code = AAC_GET_FWSTATUS(sc);
142735863739SMike Smith 		if (code & AAC_SELF_TEST_FAILED) {
142835863739SMike Smith 			device_printf(sc->aac_dev, "FATAL: selftest failed\n");
142935863739SMike Smith 			return(ENXIO);
143035863739SMike Smith 		}
143135863739SMike Smith 		if (code & AAC_KERNEL_PANIC) {
1432914da7d0SScott Long 			device_printf(sc->aac_dev,
1433914da7d0SScott Long 				      "FATAL: controller kernel panic\n");
143435863739SMike Smith 			return(ENXIO);
143535863739SMike Smith 		}
143635863739SMike Smith 		if (time_second > (then + AAC_BOOT_TIMEOUT)) {
1437914da7d0SScott Long 			device_printf(sc->aac_dev,
1438914da7d0SScott Long 				      "FATAL: controller not coming ready, "
1439c6eafcf2SScott Long 					   "status %x\n", code);
144035863739SMike Smith 			return(ENXIO);
144135863739SMike Smith 		}
144235863739SMike Smith 	} while (!(code & AAC_UP_AND_RUNNING));
144335863739SMike Smith 
1444a6d35632SScott Long 	error = ENOMEM;
1445a6d35632SScott Long 	/*
1446a6d35632SScott Long 	 * Create DMA tag for mapping buffers into controller-addressable space.
1447a6d35632SScott Long 	 */
1448a6d35632SScott Long 	if (bus_dma_tag_create(sc->aac_parent_dmat, 	/* parent */
1449a6d35632SScott Long 			       1, 0, 			/* algnmnt, boundary */
1450a6d35632SScott Long 			       (sc->flags & AAC_FLAGS_SG_64BIT) ?
1451a6d35632SScott Long 			       BUS_SPACE_MAXADDR :
1452a6d35632SScott Long 			       BUS_SPACE_MAXADDR_32BIT,	/* lowaddr */
1453a6d35632SScott Long 			       BUS_SPACE_MAXADDR, 	/* highaddr */
1454a6d35632SScott Long 			       NULL, NULL, 		/* filter, filterarg */
1455a6d35632SScott Long 			       MAXBSIZE,		/* maxsize */
1456a6d35632SScott Long 			       AAC_MAXSGENTRIES,	/* nsegments */
1457a6d35632SScott Long 			       MAXBSIZE,		/* maxsegsize */
1458a6d35632SScott Long 			       BUS_DMA_ALLOCNOW,	/* flags */
1459f6b1c44dSScott Long 			       busdma_lock_mutex,	/* lockfunc */
1460f6b1c44dSScott Long 			       &sc->aac_io_lock,	/* lockfuncarg */
1461a6d35632SScott Long 			       &sc->aac_buffer_dmat)) {
1462a6d35632SScott Long 		device_printf(sc->aac_dev, "can't allocate buffer DMA tag\n");
1463a6d35632SScott Long 		goto out;
1464a6d35632SScott Long 	}
1465a6d35632SScott Long 
1466a6d35632SScott Long 	/*
1467a6d35632SScott Long 	 * Create DMA tag for mapping FIBs into controller-addressable space..
1468a6d35632SScott Long 	 */
1469a6d35632SScott Long 	if (bus_dma_tag_create(sc->aac_parent_dmat,	/* parent */
1470a6d35632SScott Long 			       1, 0, 			/* algnmnt, boundary */
1471a6d35632SScott Long 			       (sc->flags & AAC_FLAGS_4GB_WINDOW) ?
1472a6d35632SScott Long 			       BUS_SPACE_MAXADDR_32BIT :
1473a6d35632SScott Long 			       0x7fffffff,		/* lowaddr */
1474a6d35632SScott Long 			       BUS_SPACE_MAXADDR, 	/* highaddr */
1475a6d35632SScott Long 			       NULL, NULL, 		/* filter, filterarg */
1476a6d35632SScott Long 			       AAC_FIB_COUNT *
1477a6d35632SScott Long 			       sizeof(struct aac_fib),  /* maxsize */
1478a6d35632SScott Long 			       1,			/* nsegments */
1479a6d35632SScott Long 			       AAC_FIB_COUNT *
1480a6d35632SScott Long 			       sizeof(struct aac_fib),	/* maxsegsize */
1481a6d35632SScott Long 			       BUS_DMA_ALLOCNOW,	/* flags */
1482f6b1c44dSScott Long 			       NULL, NULL,		/* No locking needed */
1483a6d35632SScott Long 			       &sc->aac_fib_dmat)) {
1484a6d35632SScott Long 		device_printf(sc->aac_dev, "can't allocate FIB DMA tag\n");;
1485a6d35632SScott Long 		goto out;
1486a6d35632SScott Long 	}
1487a6d35632SScott Long 
148835863739SMike Smith 	/*
148935863739SMike Smith 	 * Create DMA tag for the common structure and allocate it.
149035863739SMike Smith 	 */
149135863739SMike Smith 	if (bus_dma_tag_create(sc->aac_parent_dmat, 	/* parent */
1492c6eafcf2SScott Long 			       1, 0,			/* algnmnt, boundary */
1493a6d35632SScott Long 			       (sc->flags & AAC_FLAGS_4GB_WINDOW) ?
1494a6d35632SScott Long 			       BUS_SPACE_MAXADDR_32BIT :
1495a6d35632SScott Long 			       0x7fffffff,		/* lowaddr */
149635863739SMike Smith 			       BUS_SPACE_MAXADDR, 	/* highaddr */
149735863739SMike Smith 			       NULL, NULL, 		/* filter, filterarg */
1498ffb37f33SScott Long 			       8192 + sizeof(struct aac_common), /* maxsize */
1499914da7d0SScott Long 			       1,			/* nsegments */
150035863739SMike Smith 			       BUS_SPACE_MAXSIZE_32BIT,	/* maxsegsize */
1501a6d35632SScott Long 			       BUS_DMA_ALLOCNOW,	/* flags */
1502f6b1c44dSScott Long 			       NULL, NULL,		/* No locking needed */
150335863739SMike Smith 			       &sc->aac_common_dmat)) {
1504914da7d0SScott Long 		device_printf(sc->aac_dev,
1505914da7d0SScott Long 			      "can't allocate common structure DMA tag\n");
1506a6d35632SScott Long 		goto out;
150735863739SMike Smith 	}
1508c6eafcf2SScott Long 	if (bus_dmamem_alloc(sc->aac_common_dmat, (void **)&sc->aac_common,
1509c6eafcf2SScott Long 			     BUS_DMA_NOWAIT, &sc->aac_common_dmamap)) {
151035863739SMike Smith 		device_printf(sc->aac_dev, "can't allocate common structure\n");
1511a6d35632SScott Long 		goto out;
151235863739SMike Smith 	}
1513ffb37f33SScott Long 
1514ffb37f33SScott Long 	/*
1515ffb37f33SScott Long 	 * Work around a bug in the 2120 and 2200 that cannot DMA commands
1516ffb37f33SScott Long 	 * below address 8192 in physical memory.
1517ffb37f33SScott Long 	 * XXX If the padding is not needed, can it be put to use instead
1518ffb37f33SScott Long 	 * of ignored?
1519ffb37f33SScott Long 	 */
1520cd481291SScott Long 	(void)bus_dmamap_load(sc->aac_common_dmat, sc->aac_common_dmamap,
1521ffb37f33SScott Long 			sc->aac_common, 8192 + sizeof(*sc->aac_common),
1522ffb37f33SScott Long 			aac_common_map, sc, 0);
1523ffb37f33SScott Long 
1524ffb37f33SScott Long 	if (sc->aac_common_busaddr < 8192) {
1525ffb37f33SScott Long 		(uint8_t *)sc->aac_common += 8192;
1526ffb37f33SScott Long 		sc->aac_common_busaddr += 8192;
1527ffb37f33SScott Long 	}
152835863739SMike Smith 	bzero(sc->aac_common, sizeof(*sc->aac_common));
152935863739SMike Smith 
1530ffb37f33SScott Long 	/* Allocate some FIBs and associated command structs */
1531ffb37f33SScott Long 	TAILQ_INIT(&sc->aac_fibmap_tqh);
1532ffb37f33SScott Long 	sc->aac_commands = malloc(AAC_MAX_FIBS * sizeof(struct aac_command),
15338480cc63SScott Long 				  M_AACBUF, M_WAITOK|M_ZERO);
15348480cc63SScott Long 	while (sc->total_fibs < AAC_PREALLOCATE_FIBS) {
1535ffb37f33SScott Long 		if (aac_alloc_commands(sc) != 0)
1536ffb37f33SScott Long 			break;
1537ffb37f33SScott Long 	}
1538ffb37f33SScott Long 	if (sc->total_fibs == 0)
1539a6d35632SScott Long 		goto out;
1540ffb37f33SScott Long 
154135863739SMike Smith 	/*
1542914da7d0SScott Long 	 * Fill in the init structure.  This tells the adapter about the
1543914da7d0SScott Long 	 * physical location of various important shared data structures.
154435863739SMike Smith 	 */
154535863739SMike Smith 	ip = &sc->aac_common->ac_init;
154635863739SMike Smith 	ip->InitStructRevision = AAC_INIT_STRUCT_REVISION;
1547f30ac74cSScott Long 	ip->MiniPortRevision = AAC_INIT_STRUCT_MINIPORT_REVISION;
154835863739SMike Smith 
1549c6eafcf2SScott Long 	ip->AdapterFibsPhysicalAddress = sc->aac_common_busaddr +
1550c6eafcf2SScott Long 					 offsetof(struct aac_common, ac_fibs);
1551149af931SScott Long 	ip->AdapterFibsVirtualAddress = 0;
155235863739SMike Smith 	ip->AdapterFibsSize = AAC_ADAPTER_FIBS * sizeof(struct aac_fib);
155335863739SMike Smith 	ip->AdapterFibAlign = sizeof(struct aac_fib);
155435863739SMike Smith 
1555c6eafcf2SScott Long 	ip->PrintfBufferAddress = sc->aac_common_busaddr +
1556c6eafcf2SScott Long 				  offsetof(struct aac_common, ac_printf);
155735863739SMike Smith 	ip->PrintfBufferSize = AAC_PRINTF_BUFSIZE;
155835863739SMike Smith 
15594b00f859SScott Long 	/*
15604b00f859SScott Long 	 * The adapter assumes that pages are 4K in size, except on some
15614b00f859SScott Long  	 * broken firmware versions that do the page->byte conversion twice,
15624b00f859SScott Long 	 * therefore 'assuming' that this value is in 16MB units (2^24).
15634b00f859SScott Long 	 * Round up since the granularity is so high.
15644b00f859SScott Long 	 */
1565f30ac74cSScott Long 	ip->HostPhysMemPages = ctob(physmem) / AAC_PAGE_SIZE;
15664b00f859SScott Long 	if (sc->flags & AAC_FLAGS_BROKEN_MEMMAP) {
15674b00f859SScott Long 		ip->HostPhysMemPages =
15684b00f859SScott Long 		    (ip->HostPhysMemPages + AAC_PAGE_SIZE) / AAC_PAGE_SIZE;
1569204c0befSScott Long 	}
157035863739SMike Smith 	ip->HostElapsedSeconds = time_second;	/* reset later if invalid */
157135863739SMike Smith 
157235863739SMike Smith 	/*
1573c6eafcf2SScott Long 	 * Initialise FIB queues.  Note that it appears that the layout of the
1574c6eafcf2SScott Long 	 * indexes and the segmentation of the entries may be mandated by the
1575c6eafcf2SScott Long 	 * adapter, which is only told about the base of the queue index fields.
157635863739SMike Smith 	 *
157735863739SMike Smith 	 * The initial values of the indices are assumed to inform the adapter
1578914da7d0SScott Long 	 * of the sizes of the respective queues, and theoretically it could
1579914da7d0SScott Long 	 * work out the entire layout of the queue structures from this.  We
1580914da7d0SScott Long 	 * take the easy route and just lay this area out like everyone else
1581914da7d0SScott Long 	 * does.
158235863739SMike Smith 	 *
1583914da7d0SScott Long 	 * The Linux driver uses a much more complex scheme whereby several
1584914da7d0SScott Long 	 * header records are kept for each queue.  We use a couple of generic
1585914da7d0SScott Long 	 * list manipulation functions which 'know' the size of each list by
1586914da7d0SScott Long 	 * virtue of a table.
158735863739SMike Smith 	 */
1588b88ffdc8SScott Long 	qoffset = offsetof(struct aac_common, ac_qbuf) + AAC_QUEUE_ALIGN;
15890bcbebd6SScott Long 	qoffset &= ~(AAC_QUEUE_ALIGN - 1);
15900bcbebd6SScott Long 	sc->aac_queues =
15910bcbebd6SScott Long 	    (struct aac_queue_table *)((uintptr_t)sc->aac_common + qoffset);
1592b88ffdc8SScott Long 	ip->CommHeaderAddress = sc->aac_common_busaddr + qoffset;
159335863739SMike Smith 
1594c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_NORM_CMD_QUEUE][AAC_PRODUCER_INDEX] =
1595c6eafcf2SScott Long 		AAC_HOST_NORM_CMD_ENTRIES;
1596c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_NORM_CMD_QUEUE][AAC_CONSUMER_INDEX] =
1597c6eafcf2SScott Long 		AAC_HOST_NORM_CMD_ENTRIES;
1598c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_HIGH_CMD_QUEUE][AAC_PRODUCER_INDEX] =
1599c6eafcf2SScott Long 		AAC_HOST_HIGH_CMD_ENTRIES;
1600c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_HIGH_CMD_QUEUE][AAC_CONSUMER_INDEX] =
1601c6eafcf2SScott Long 		AAC_HOST_HIGH_CMD_ENTRIES;
1602c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_NORM_CMD_QUEUE][AAC_PRODUCER_INDEX] =
1603c6eafcf2SScott Long 		AAC_ADAP_NORM_CMD_ENTRIES;
1604c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_NORM_CMD_QUEUE][AAC_CONSUMER_INDEX] =
1605c6eafcf2SScott Long 		AAC_ADAP_NORM_CMD_ENTRIES;
1606c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_HIGH_CMD_QUEUE][AAC_PRODUCER_INDEX] =
1607c6eafcf2SScott Long 		AAC_ADAP_HIGH_CMD_ENTRIES;
1608c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_HIGH_CMD_QUEUE][AAC_CONSUMER_INDEX] =
1609c6eafcf2SScott Long 		AAC_ADAP_HIGH_CMD_ENTRIES;
1610c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_NORM_RESP_QUEUE][AAC_PRODUCER_INDEX]=
1611c6eafcf2SScott Long 		AAC_HOST_NORM_RESP_ENTRIES;
1612c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_NORM_RESP_QUEUE][AAC_CONSUMER_INDEX]=
1613c6eafcf2SScott Long 		AAC_HOST_NORM_RESP_ENTRIES;
1614c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_HIGH_RESP_QUEUE][AAC_PRODUCER_INDEX]=
1615c6eafcf2SScott Long 		AAC_HOST_HIGH_RESP_ENTRIES;
1616c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_HIGH_RESP_QUEUE][AAC_CONSUMER_INDEX]=
1617c6eafcf2SScott Long 		AAC_HOST_HIGH_RESP_ENTRIES;
1618c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_NORM_RESP_QUEUE][AAC_PRODUCER_INDEX]=
1619c6eafcf2SScott Long 		AAC_ADAP_NORM_RESP_ENTRIES;
1620c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_NORM_RESP_QUEUE][AAC_CONSUMER_INDEX]=
1621c6eafcf2SScott Long 		AAC_ADAP_NORM_RESP_ENTRIES;
1622c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_HIGH_RESP_QUEUE][AAC_PRODUCER_INDEX]=
1623c6eafcf2SScott Long 		AAC_ADAP_HIGH_RESP_ENTRIES;
1624c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_HIGH_RESP_QUEUE][AAC_CONSUMER_INDEX]=
1625c6eafcf2SScott Long 		AAC_ADAP_HIGH_RESP_ENTRIES;
1626c6eafcf2SScott Long 	sc->aac_qentries[AAC_HOST_NORM_CMD_QUEUE] =
1627c6eafcf2SScott Long 		&sc->aac_queues->qt_HostNormCmdQueue[0];
1628c6eafcf2SScott Long 	sc->aac_qentries[AAC_HOST_HIGH_CMD_QUEUE] =
1629c6eafcf2SScott Long 		&sc->aac_queues->qt_HostHighCmdQueue[0];
1630c6eafcf2SScott Long 	sc->aac_qentries[AAC_ADAP_NORM_CMD_QUEUE] =
1631c6eafcf2SScott Long 		&sc->aac_queues->qt_AdapNormCmdQueue[0];
1632c6eafcf2SScott Long 	sc->aac_qentries[AAC_ADAP_HIGH_CMD_QUEUE] =
1633c6eafcf2SScott Long 		&sc->aac_queues->qt_AdapHighCmdQueue[0];
1634c6eafcf2SScott Long 	sc->aac_qentries[AAC_HOST_NORM_RESP_QUEUE] =
1635c6eafcf2SScott Long 		&sc->aac_queues->qt_HostNormRespQueue[0];
1636c6eafcf2SScott Long 	sc->aac_qentries[AAC_HOST_HIGH_RESP_QUEUE] =
1637c6eafcf2SScott Long 		&sc->aac_queues->qt_HostHighRespQueue[0];
1638c6eafcf2SScott Long 	sc->aac_qentries[AAC_ADAP_NORM_RESP_QUEUE] =
1639c6eafcf2SScott Long 		&sc->aac_queues->qt_AdapNormRespQueue[0];
1640c6eafcf2SScott Long 	sc->aac_qentries[AAC_ADAP_HIGH_RESP_QUEUE] =
1641c6eafcf2SScott Long 		&sc->aac_queues->qt_AdapHighRespQueue[0];
164235863739SMike Smith 
164335863739SMike Smith 	/*
164435863739SMike Smith 	 * Do controller-type-specific initialisation
164535863739SMike Smith 	 */
164635863739SMike Smith 	switch (sc->aac_hwif) {
164735863739SMike Smith 	case AAC_HWIF_I960RX:
164835863739SMike Smith 		AAC_SETREG4(sc, AAC_RX_ODBR, ~0);
164935863739SMike Smith 		break;
165035863739SMike Smith 	}
165135863739SMike Smith 
165235863739SMike Smith 	/*
165335863739SMike Smith 	 * Give the init structure to the controller.
165435863739SMike Smith 	 */
165535863739SMike Smith 	if (aac_sync_command(sc, AAC_MONKER_INITSTRUCT,
1656914da7d0SScott Long 			     sc->aac_common_busaddr +
1657914da7d0SScott Long 			     offsetof(struct aac_common, ac_init), 0, 0, 0,
1658914da7d0SScott Long 			     NULL)) {
1659914da7d0SScott Long 		device_printf(sc->aac_dev,
1660914da7d0SScott Long 			      "error establishing init structure\n");
1661a6d35632SScott Long 		error = EIO;
1662a6d35632SScott Long 		goto out;
166335863739SMike Smith 	}
166435863739SMike Smith 
1665a6d35632SScott Long 	error = 0;
1666a6d35632SScott Long out:
1667a6d35632SScott Long 	return(error);
166835863739SMike Smith }
166935863739SMike Smith 
1670914da7d0SScott Long /*
167135863739SMike Smith  * Send a synchronous command to the controller and wait for a result.
167235863739SMike Smith  */
167335863739SMike Smith static int
167435863739SMike Smith aac_sync_command(struct aac_softc *sc, u_int32_t command,
167535863739SMike Smith 		 u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3,
167635863739SMike Smith 		 u_int32_t *sp)
167735863739SMike Smith {
167835863739SMike Smith 	time_t then;
167935863739SMike Smith 	u_int32_t status;
168035863739SMike Smith 
168135863739SMike Smith 	debug_called(3);
168235863739SMike Smith 
168335863739SMike Smith 	/* populate the mailbox */
168435863739SMike Smith 	AAC_SET_MAILBOX(sc, command, arg0, arg1, arg2, arg3);
168535863739SMike Smith 
168635863739SMike Smith 	/* ensure the sync command doorbell flag is cleared */
168735863739SMike Smith 	AAC_CLEAR_ISTATUS(sc, AAC_DB_SYNC_COMMAND);
168835863739SMike Smith 
168935863739SMike Smith 	/* then set it to signal the adapter */
169035863739SMike Smith 	AAC_QNOTIFY(sc, AAC_DB_SYNC_COMMAND);
169135863739SMike Smith 
169235863739SMike Smith 	/* spin waiting for the command to complete */
169335863739SMike Smith 	then = time_second;
169435863739SMike Smith 	do {
169535863739SMike Smith 		if (time_second > (then + AAC_IMMEDIATE_TIMEOUT)) {
1696a6d35632SScott Long 			debug(1, "timed out");
169735863739SMike Smith 			return(EIO);
169835863739SMike Smith 		}
169935863739SMike Smith 	} while (!(AAC_GET_ISTATUS(sc) & AAC_DB_SYNC_COMMAND));
170035863739SMike Smith 
170135863739SMike Smith 	/* clear the completion flag */
170235863739SMike Smith 	AAC_CLEAR_ISTATUS(sc, AAC_DB_SYNC_COMMAND);
170335863739SMike Smith 
170435863739SMike Smith 	/* get the command status */
1705a6d35632SScott Long 	status = AAC_GET_MAILBOX(sc, 0);
170635863739SMike Smith 	if (sp != NULL)
170735863739SMike Smith 		*sp = status;
17080b94a66eSMike Smith 	return(0);
170935863739SMike Smith }
171035863739SMike Smith 
1711914da7d0SScott Long /*
1712cbfd045bSScott Long  * Grab the sync fib area.
1713cbfd045bSScott Long  */
1714cbfd045bSScott Long int
1715fe3cb0e1SScott Long aac_alloc_sync_fib(struct aac_softc *sc, struct aac_fib **fib, int flags)
1716cbfd045bSScott Long {
1717cbfd045bSScott Long 
1718cbfd045bSScott Long 	/*
1719cbfd045bSScott Long 	 * If the force flag is set, the system is shutting down, or in
1720cbfd045bSScott Long 	 * trouble.  Ignore the mutex.
1721cbfd045bSScott Long 	 */
1722cbfd045bSScott Long 	if (!(flags & AAC_SYNC_LOCK_FORCE))
1723cbfd045bSScott Long 		AAC_LOCK_ACQUIRE(&sc->aac_sync_lock);
1724cbfd045bSScott Long 
1725cbfd045bSScott Long 	*fib = &sc->aac_common->ac_sync_fib;
1726cbfd045bSScott Long 
1727cbfd045bSScott Long 	return (1);
1728cbfd045bSScott Long }
1729cbfd045bSScott Long 
1730cbfd045bSScott Long /*
1731cbfd045bSScott Long  * Release the sync fib area.
1732cbfd045bSScott Long  */
1733cbfd045bSScott Long void
1734cbfd045bSScott Long aac_release_sync_fib(struct aac_softc *sc)
1735cbfd045bSScott Long {
1736cbfd045bSScott Long 
1737cbfd045bSScott Long 	AAC_LOCK_RELEASE(&sc->aac_sync_lock);
1738cbfd045bSScott Long }
1739cbfd045bSScott Long 
1740cbfd045bSScott Long /*
174135863739SMike Smith  * Send a synchronous FIB to the controller and wait for a result.
174235863739SMike Smith  */
1743cbfd045bSScott Long int
174435863739SMike Smith aac_sync_fib(struct aac_softc *sc, u_int32_t command, u_int32_t xferstate,
1745cbfd045bSScott Long 		 struct aac_fib *fib, u_int16_t datasize)
174635863739SMike Smith {
174735863739SMike Smith 	debug_called(3);
174835863739SMike Smith 
174935863739SMike Smith 	if (datasize > AAC_FIB_DATASIZE)
175035863739SMike Smith 		return(EINVAL);
175135863739SMike Smith 
175235863739SMike Smith 	/*
175335863739SMike Smith 	 * Set up the sync FIB
175435863739SMike Smith 	 */
1755914da7d0SScott Long 	fib->Header.XferState = AAC_FIBSTATE_HOSTOWNED |
1756914da7d0SScott Long 				AAC_FIBSTATE_INITIALISED |
1757c6eafcf2SScott Long 				AAC_FIBSTATE_EMPTY;
175835863739SMike Smith 	fib->Header.XferState |= xferstate;
175935863739SMike Smith 	fib->Header.Command = command;
176035863739SMike Smith 	fib->Header.StructType = AAC_FIBTYPE_TFIB;
176135863739SMike Smith 	fib->Header.Size = sizeof(struct aac_fib) + datasize;
176235863739SMike Smith 	fib->Header.SenderSize = sizeof(struct aac_fib);
1763b88ffdc8SScott Long 	fib->Header.SenderFibAddress = 0;	/* Not needed */
1764c6eafcf2SScott Long 	fib->Header.ReceiverFibAddress = sc->aac_common_busaddr +
1765914da7d0SScott Long 					 offsetof(struct aac_common,
1766914da7d0SScott Long 						  ac_sync_fib);
176735863739SMike Smith 
176835863739SMike Smith 	/*
176935863739SMike Smith 	 * Give the FIB to the controller, wait for a response.
177035863739SMike Smith 	 */
1771914da7d0SScott Long 	if (aac_sync_command(sc, AAC_MONKER_SYNCFIB,
1772914da7d0SScott Long 			     fib->Header.ReceiverFibAddress, 0, 0, 0, NULL)) {
177335863739SMike Smith 		debug(2, "IO error");
177435863739SMike Smith 		return(EIO);
177535863739SMike Smith 	}
177635863739SMike Smith 
177735863739SMike Smith 	return (0);
177835863739SMike Smith }
177935863739SMike Smith 
1780914da7d0SScott Long /*
178135863739SMike Smith  * Adapter-space FIB queue manipulation
178235863739SMike Smith  *
178335863739SMike Smith  * Note that the queue implementation here is a little funky; neither the PI or
178435863739SMike Smith  * CI will ever be zero.  This behaviour is a controller feature.
178535863739SMike Smith  */
178635863739SMike Smith static struct {
178735863739SMike Smith 	int		size;
178835863739SMike Smith 	int		notify;
178935863739SMike Smith } aac_qinfo[] = {
179035863739SMike Smith 	{AAC_HOST_NORM_CMD_ENTRIES, AAC_DB_COMMAND_NOT_FULL},
179135863739SMike Smith 	{AAC_HOST_HIGH_CMD_ENTRIES, 0},
179235863739SMike Smith 	{AAC_ADAP_NORM_CMD_ENTRIES, AAC_DB_COMMAND_READY},
179335863739SMike Smith 	{AAC_ADAP_HIGH_CMD_ENTRIES, 0},
179435863739SMike Smith 	{AAC_HOST_NORM_RESP_ENTRIES, AAC_DB_RESPONSE_NOT_FULL},
179535863739SMike Smith 	{AAC_HOST_HIGH_RESP_ENTRIES, 0},
179635863739SMike Smith 	{AAC_ADAP_NORM_RESP_ENTRIES, AAC_DB_RESPONSE_READY},
179735863739SMike Smith 	{AAC_ADAP_HIGH_RESP_ENTRIES, 0}
179835863739SMike Smith };
179935863739SMike Smith 
180035863739SMike Smith /*
1801c6eafcf2SScott Long  * Atomically insert an entry into the nominated queue, returns 0 on success or
1802c6eafcf2SScott Long  * EBUSY if the queue is full.
180335863739SMike Smith  *
18040b94a66eSMike Smith  * Note: it would be more efficient to defer notifying the controller in
1805914da7d0SScott Long  *	 the case where we may be inserting several entries in rapid succession,
1806914da7d0SScott Long  *	 but implementing this usefully may be difficult (it would involve a
1807c6eafcf2SScott Long  *	 separate queue/notify interface).
180835863739SMike Smith  */
180935863739SMike Smith static int
1810f6c4dd3fSScott Long aac_enqueue_fib(struct aac_softc *sc, int queue, struct aac_command *cm)
181135863739SMike Smith {
181235863739SMike Smith 	u_int32_t pi, ci;
18139e2e96d8SScott Long 	int error;
1814f6c4dd3fSScott Long 	u_int32_t fib_size;
1815f6c4dd3fSScott Long 	u_int32_t fib_addr;
1816f6c4dd3fSScott Long 
181736e0bf6eSScott Long 	debug_called(3);
181836e0bf6eSScott Long 
1819f6c4dd3fSScott Long 	fib_size = cm->cm_fib->Header.Size;
1820f6c4dd3fSScott Long 	fib_addr = cm->cm_fib->Header.ReceiverFibAddress;
182135863739SMike Smith 
182235863739SMike Smith 	/* get the producer/consumer indices */
182335863739SMike Smith 	pi = sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX];
182435863739SMike Smith 	ci = sc->aac_queues->qt_qindex[queue][AAC_CONSUMER_INDEX];
182535863739SMike Smith 
182635863739SMike Smith 	/* wrap the queue? */
182735863739SMike Smith 	if (pi >= aac_qinfo[queue].size)
182835863739SMike Smith 		pi = 0;
182935863739SMike Smith 
183035863739SMike Smith 	/* check for queue full */
183135863739SMike Smith 	if ((pi + 1) == ci) {
183235863739SMike Smith 		error = EBUSY;
183335863739SMike Smith 		goto out;
183435863739SMike Smith 	}
183535863739SMike Smith 
183635863739SMike Smith 	/* populate queue entry */
183735863739SMike Smith 	(sc->aac_qentries[queue] + pi)->aq_fib_size = fib_size;
183835863739SMike Smith 	(sc->aac_qentries[queue] + pi)->aq_fib_addr = fib_addr;
183935863739SMike Smith 
184035863739SMike Smith 	/* update producer index */
184135863739SMike Smith 	sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX] = pi + 1;
184235863739SMike Smith 
1843f6c4dd3fSScott Long 	/*
1844914da7d0SScott Long 	 * To avoid a race with its completion interrupt, place this command on
1845914da7d0SScott Long 	 * the busy queue prior to advertising it to the controller.
1846f6c4dd3fSScott Long 	 */
1847f6c4dd3fSScott Long 	aac_enqueue_busy(cm);
1848f6c4dd3fSScott Long 
184935863739SMike Smith 	/* notify the adapter if we know how */
185035863739SMike Smith 	if (aac_qinfo[queue].notify != 0)
185135863739SMike Smith 		AAC_QNOTIFY(sc, aac_qinfo[queue].notify);
185235863739SMike Smith 
185335863739SMike Smith 	error = 0;
185435863739SMike Smith 
185535863739SMike Smith out:
185635863739SMike Smith 	return(error);
185735863739SMike Smith }
185835863739SMike Smith 
185935863739SMike Smith /*
186036e0bf6eSScott Long  * Atomically remove one entry from the nominated queue, returns 0 on
186136e0bf6eSScott Long  * success or ENOENT if the queue is empty.
186235863739SMike Smith  */
186335863739SMike Smith static int
1864c6eafcf2SScott Long aac_dequeue_fib(struct aac_softc *sc, int queue, u_int32_t *fib_size,
1865c6eafcf2SScott Long 		struct aac_fib **fib_addr)
186635863739SMike Smith {
186735863739SMike Smith 	u_int32_t pi, ci;
1868149af931SScott Long 	u_int32_t fib_index;
18699e2e96d8SScott Long 	int error;
1870f6c4dd3fSScott Long 	int notify;
187135863739SMike Smith 
187235863739SMike Smith 	debug_called(3);
187335863739SMike Smith 
187435863739SMike Smith 	/* get the producer/consumer indices */
187535863739SMike Smith 	pi = sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX];
187635863739SMike Smith 	ci = sc->aac_queues->qt_qindex[queue][AAC_CONSUMER_INDEX];
187735863739SMike Smith 
187835863739SMike Smith 	/* check for queue empty */
187935863739SMike Smith 	if (ci == pi) {
188035863739SMike Smith 		error = ENOENT;
188135863739SMike Smith 		goto out;
188235863739SMike Smith 	}
188335863739SMike Smith 
18847753acd2SScott Long 	/* wrap the pi so the following test works */
18857753acd2SScott Long 	if (pi >= aac_qinfo[queue].size)
18867753acd2SScott Long 		pi = 0;
18877753acd2SScott Long 
1888f6c4dd3fSScott Long 	notify = 0;
1889f6c4dd3fSScott Long 	if (ci == pi + 1)
1890f6c4dd3fSScott Long 		notify++;
1891f6c4dd3fSScott Long 
189235863739SMike Smith 	/* wrap the queue? */
189335863739SMike Smith 	if (ci >= aac_qinfo[queue].size)
189435863739SMike Smith 		ci = 0;
189535863739SMike Smith 
189635863739SMike Smith 	/* fetch the entry */
189735863739SMike Smith 	*fib_size = (sc->aac_qentries[queue] + ci)->aq_fib_size;
1898149af931SScott Long 
1899149af931SScott Long 	switch (queue) {
1900149af931SScott Long 	case AAC_HOST_NORM_CMD_QUEUE:
1901149af931SScott Long 	case AAC_HOST_HIGH_CMD_QUEUE:
1902149af931SScott Long 		/*
1903149af931SScott Long 		 * The aq_fib_addr is only 32 bits wide so it can't be counted
1904149af931SScott Long 		 * on to hold an address.  For AIF's, the adapter assumes
1905149af931SScott Long 		 * that it's giving us an address into the array of AIF fibs.
1906149af931SScott Long 		 * Therefore, we have to convert it to an index.
1907149af931SScott Long 		 */
1908149af931SScott Long 		fib_index = (sc->aac_qentries[queue] + ci)->aq_fib_addr /
1909149af931SScott Long 			sizeof(struct aac_fib);
1910149af931SScott Long 		*fib_addr = &sc->aac_common->ac_fibs[fib_index];
1911149af931SScott Long 		break;
1912149af931SScott Long 
1913149af931SScott Long 	case AAC_HOST_NORM_RESP_QUEUE:
1914149af931SScott Long 	case AAC_HOST_HIGH_RESP_QUEUE:
1915149af931SScott Long 	{
1916149af931SScott Long 		struct aac_command *cm;
1917149af931SScott Long 
1918149af931SScott Long 		/*
1919149af931SScott Long 		 * As above, an index is used instead of an actual address.
1920149af931SScott Long 		 * Gotta shift the index to account for the fast response
1921149af931SScott Long 		 * bit.  No other correction is needed since this value was
1922149af931SScott Long 		 * originally provided by the driver via the SenderFibAddress
1923149af931SScott Long 		 * field.
1924149af931SScott Long 		 */
1925149af931SScott Long 		fib_index = (sc->aac_qentries[queue] + ci)->aq_fib_addr;
1926149af931SScott Long 		cm = sc->aac_commands + (fib_index >> 1);
1927149af931SScott Long 		*fib_addr = cm->cm_fib;
192835863739SMike Smith 
1929f30ac74cSScott Long 		/*
1930f30ac74cSScott Long 		 * Is this a fast response? If it is, update the fib fields in
1931149af931SScott Long 		 * local memory since the whole fib isn't DMA'd back up.
1932f30ac74cSScott Long 		 */
1933149af931SScott Long 		if (fib_index & 0x01) {
1934f30ac74cSScott Long 			(*fib_addr)->Header.XferState |= AAC_FIBSTATE_DONEADAP;
1935f30ac74cSScott Long 			*((u_int32_t*)((*fib_addr)->data)) = AAC_ERROR_NORMAL;
1936f30ac74cSScott Long 		}
1937149af931SScott Long 		break;
1938149af931SScott Long 	}
1939149af931SScott Long 	default:
1940149af931SScott Long 		panic("Invalid queue in aac_dequeue_fib()");
1941149af931SScott Long 		break;
1942149af931SScott Long 	}
1943149af931SScott Long 
194435863739SMike Smith 	/* update consumer index */
194535863739SMike Smith 	sc->aac_queues->qt_qindex[queue][AAC_CONSUMER_INDEX] = ci + 1;
194635863739SMike Smith 
194735863739SMike Smith 	/* if we have made the queue un-full, notify the adapter */
1948f6c4dd3fSScott Long 	if (notify && (aac_qinfo[queue].notify != 0))
194935863739SMike Smith 		AAC_QNOTIFY(sc, aac_qinfo[queue].notify);
195035863739SMike Smith 	error = 0;
195135863739SMike Smith 
195235863739SMike Smith out:
195335863739SMike Smith 	return(error);
195435863739SMike Smith }
195535863739SMike Smith 
1956914da7d0SScott Long /*
195736e0bf6eSScott Long  * Put our response to an Adapter Initialed Fib on the response queue
195836e0bf6eSScott Long  */
195936e0bf6eSScott Long static int
196036e0bf6eSScott Long aac_enqueue_response(struct aac_softc *sc, int queue, struct aac_fib *fib)
196136e0bf6eSScott Long {
196236e0bf6eSScott Long 	u_int32_t pi, ci;
19639e2e96d8SScott Long 	int error;
196436e0bf6eSScott Long 	u_int32_t fib_size;
196536e0bf6eSScott Long 	u_int32_t fib_addr;
196636e0bf6eSScott Long 
196736e0bf6eSScott Long 	debug_called(1);
196836e0bf6eSScott Long 
196936e0bf6eSScott Long 	/* Tell the adapter where the FIB is */
197036e0bf6eSScott Long 	fib_size = fib->Header.Size;
197136e0bf6eSScott Long 	fib_addr = fib->Header.SenderFibAddress;
197236e0bf6eSScott Long 	fib->Header.ReceiverFibAddress = fib_addr;
197336e0bf6eSScott Long 
197436e0bf6eSScott Long 	/* get the producer/consumer indices */
197536e0bf6eSScott Long 	pi = sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX];
197636e0bf6eSScott Long 	ci = sc->aac_queues->qt_qindex[queue][AAC_CONSUMER_INDEX];
197736e0bf6eSScott Long 
197836e0bf6eSScott Long 	/* wrap the queue? */
197936e0bf6eSScott Long 	if (pi >= aac_qinfo[queue].size)
198036e0bf6eSScott Long 		pi = 0;
198136e0bf6eSScott Long 
198236e0bf6eSScott Long 	/* check for queue full */
198336e0bf6eSScott Long 	if ((pi + 1) == ci) {
198436e0bf6eSScott Long 		error = EBUSY;
198536e0bf6eSScott Long 		goto out;
198636e0bf6eSScott Long 	}
198736e0bf6eSScott Long 
198836e0bf6eSScott Long 	/* populate queue entry */
198936e0bf6eSScott Long 	(sc->aac_qentries[queue] + pi)->aq_fib_size = fib_size;
199036e0bf6eSScott Long 	(sc->aac_qentries[queue] + pi)->aq_fib_addr = fib_addr;
199136e0bf6eSScott Long 
199236e0bf6eSScott Long 	/* update producer index */
199336e0bf6eSScott Long 	sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX] = pi + 1;
199436e0bf6eSScott Long 
199536e0bf6eSScott Long 	/* notify the adapter if we know how */
199636e0bf6eSScott Long 	if (aac_qinfo[queue].notify != 0)
199736e0bf6eSScott Long 		AAC_QNOTIFY(sc, aac_qinfo[queue].notify);
199836e0bf6eSScott Long 
199936e0bf6eSScott Long 	error = 0;
200036e0bf6eSScott Long 
200136e0bf6eSScott Long out:
200236e0bf6eSScott Long 	return(error);
200336e0bf6eSScott Long }
200436e0bf6eSScott Long 
2005914da7d0SScott Long /*
20060b94a66eSMike Smith  * Check for commands that have been outstanding for a suspiciously long time,
20070b94a66eSMike Smith  * and complain about them.
20080b94a66eSMike Smith  */
20090b94a66eSMike Smith static void
20100b94a66eSMike Smith aac_timeout(struct aac_softc *sc)
20110b94a66eSMike Smith {
20120b94a66eSMike Smith 	struct aac_command *cm;
20130b94a66eSMike Smith 	time_t deadline;
20140b94a66eSMike Smith 
2015f6c4dd3fSScott Long 	/*
201670545d1aSScott Long 	 * Traverse the busy command list, bitch about late commands once
2017914da7d0SScott Long 	 * only.
2018914da7d0SScott Long 	 */
20190b94a66eSMike Smith 	deadline = time_second - AAC_CMD_TIMEOUT;
20200b94a66eSMike Smith 	TAILQ_FOREACH(cm, &sc->aac_busy, cm_link) {
2021f6c4dd3fSScott Long 		if ((cm->cm_timestamp  < deadline)
2022f6c4dd3fSScott Long 			/* && !(cm->cm_flags & AAC_CMD_TIMEDOUT) */) {
20230b94a66eSMike Smith 			cm->cm_flags |= AAC_CMD_TIMEDOUT;
2024914da7d0SScott Long 			device_printf(sc->aac_dev,
2025914da7d0SScott Long 				      "COMMAND %p TIMEOUT AFTER %d SECONDS\n",
2026f6c4dd3fSScott Long 				      cm, (int)(time_second-cm->cm_timestamp));
20270b94a66eSMike Smith 			AAC_PRINT_FIB(sc, cm->cm_fib);
20280b94a66eSMike Smith 		}
20290b94a66eSMike Smith 	}
20300b94a66eSMike Smith 
20310b94a66eSMike Smith 	return;
20320b94a66eSMike Smith }
20330b94a66eSMike Smith 
2034914da7d0SScott Long /*
2035914da7d0SScott Long  * Interface Function Vectors
2036914da7d0SScott Long  */
203735863739SMike Smith 
2038914da7d0SScott Long /*
203935863739SMike Smith  * Read the current firmware status word.
204035863739SMike Smith  */
204135863739SMike Smith static int
204235863739SMike Smith aac_sa_get_fwstatus(struct aac_softc *sc)
204335863739SMike Smith {
204435863739SMike Smith 	debug_called(3);
204535863739SMike Smith 
204635863739SMike Smith 	return(AAC_GETREG4(sc, AAC_SA_FWSTATUS));
204735863739SMike Smith }
204835863739SMike Smith 
204935863739SMike Smith static int
205035863739SMike Smith aac_rx_get_fwstatus(struct aac_softc *sc)
205135863739SMike Smith {
205235863739SMike Smith 	debug_called(3);
205335863739SMike Smith 
205435863739SMike Smith 	return(AAC_GETREG4(sc, AAC_RX_FWSTATUS));
205535863739SMike Smith }
205635863739SMike Smith 
2057b3457b51SScott Long static int
2058b3457b51SScott Long aac_fa_get_fwstatus(struct aac_softc *sc)
2059b3457b51SScott Long {
2060b3457b51SScott Long 	int val;
2061b3457b51SScott Long 
2062b3457b51SScott Long 	debug_called(3);
2063b3457b51SScott Long 
2064b3457b51SScott Long 	val = AAC_GETREG4(sc, AAC_FA_FWSTATUS);
2065b3457b51SScott Long 	return (val);
2066b3457b51SScott Long }
2067b3457b51SScott Long 
2068914da7d0SScott Long /*
206935863739SMike Smith  * Notify the controller of a change in a given queue
207035863739SMike Smith  */
207135863739SMike Smith 
207235863739SMike Smith static void
207335863739SMike Smith aac_sa_qnotify(struct aac_softc *sc, int qbit)
207435863739SMike Smith {
207535863739SMike Smith 	debug_called(3);
207635863739SMike Smith 
207735863739SMike Smith 	AAC_SETREG2(sc, AAC_SA_DOORBELL1_SET, qbit);
207835863739SMike Smith }
207935863739SMike Smith 
208035863739SMike Smith static void
208135863739SMike Smith aac_rx_qnotify(struct aac_softc *sc, int qbit)
208235863739SMike Smith {
208335863739SMike Smith 	debug_called(3);
208435863739SMike Smith 
208535863739SMike Smith 	AAC_SETREG4(sc, AAC_RX_IDBR, qbit);
208635863739SMike Smith }
208735863739SMike Smith 
2088b3457b51SScott Long static void
2089b3457b51SScott Long aac_fa_qnotify(struct aac_softc *sc, int qbit)
2090b3457b51SScott Long {
2091b3457b51SScott Long 	debug_called(3);
2092b3457b51SScott Long 
2093b3457b51SScott Long 	AAC_SETREG2(sc, AAC_FA_DOORBELL1, qbit);
2094b3457b51SScott Long 	AAC_FA_HACK(sc);
2095b3457b51SScott Long }
2096b3457b51SScott Long 
2097914da7d0SScott Long /*
209835863739SMike Smith  * Get the interrupt reason bits
209935863739SMike Smith  */
210035863739SMike Smith static int
210135863739SMike Smith aac_sa_get_istatus(struct aac_softc *sc)
210235863739SMike Smith {
210335863739SMike Smith 	debug_called(3);
210435863739SMike Smith 
210535863739SMike Smith 	return(AAC_GETREG2(sc, AAC_SA_DOORBELL0));
210635863739SMike Smith }
210735863739SMike Smith 
210835863739SMike Smith static int
210935863739SMike Smith aac_rx_get_istatus(struct aac_softc *sc)
211035863739SMike Smith {
211135863739SMike Smith 	debug_called(3);
211235863739SMike Smith 
211335863739SMike Smith 	return(AAC_GETREG4(sc, AAC_RX_ODBR));
211435863739SMike Smith }
211535863739SMike Smith 
2116b3457b51SScott Long static int
2117b3457b51SScott Long aac_fa_get_istatus(struct aac_softc *sc)
2118b3457b51SScott Long {
2119b3457b51SScott Long 	int val;
2120b3457b51SScott Long 
2121b3457b51SScott Long 	debug_called(3);
2122b3457b51SScott Long 
2123b3457b51SScott Long 	val = AAC_GETREG2(sc, AAC_FA_DOORBELL0);
2124b3457b51SScott Long 	return (val);
2125b3457b51SScott Long }
2126b3457b51SScott Long 
2127914da7d0SScott Long /*
212835863739SMike Smith  * Clear some interrupt reason bits
212935863739SMike Smith  */
213035863739SMike Smith static void
213135863739SMike Smith aac_sa_clear_istatus(struct aac_softc *sc, int mask)
213235863739SMike Smith {
213335863739SMike Smith 	debug_called(3);
213435863739SMike Smith 
213535863739SMike Smith 	AAC_SETREG2(sc, AAC_SA_DOORBELL0_CLEAR, mask);
213635863739SMike Smith }
213735863739SMike Smith 
213835863739SMike Smith static void
213935863739SMike Smith aac_rx_clear_istatus(struct aac_softc *sc, int mask)
214035863739SMike Smith {
214135863739SMike Smith 	debug_called(3);
214235863739SMike Smith 
214335863739SMike Smith 	AAC_SETREG4(sc, AAC_RX_ODBR, mask);
214435863739SMike Smith }
214535863739SMike Smith 
2146b3457b51SScott Long static void
2147b3457b51SScott Long aac_fa_clear_istatus(struct aac_softc *sc, int mask)
2148b3457b51SScott Long {
2149b3457b51SScott Long 	debug_called(3);
2150b3457b51SScott Long 
2151b3457b51SScott Long 	AAC_SETREG2(sc, AAC_FA_DOORBELL0_CLEAR, mask);
2152b3457b51SScott Long 	AAC_FA_HACK(sc);
2153b3457b51SScott Long }
2154b3457b51SScott Long 
2155914da7d0SScott Long /*
215635863739SMike Smith  * Populate the mailbox and set the command word
215735863739SMike Smith  */
215835863739SMike Smith static void
215935863739SMike Smith aac_sa_set_mailbox(struct aac_softc *sc, u_int32_t command,
216035863739SMike Smith 		u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3)
216135863739SMike Smith {
216235863739SMike Smith 	debug_called(4);
216335863739SMike Smith 
216435863739SMike Smith 	AAC_SETREG4(sc, AAC_SA_MAILBOX, command);
216535863739SMike Smith 	AAC_SETREG4(sc, AAC_SA_MAILBOX + 4, arg0);
216635863739SMike Smith 	AAC_SETREG4(sc, AAC_SA_MAILBOX + 8, arg1);
216735863739SMike Smith 	AAC_SETREG4(sc, AAC_SA_MAILBOX + 12, arg2);
216835863739SMike Smith 	AAC_SETREG4(sc, AAC_SA_MAILBOX + 16, arg3);
216935863739SMike Smith }
217035863739SMike Smith 
217135863739SMike Smith static void
217235863739SMike Smith aac_rx_set_mailbox(struct aac_softc *sc, u_int32_t command,
217335863739SMike Smith 		u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3)
217435863739SMike Smith {
217535863739SMike Smith 	debug_called(4);
217635863739SMike Smith 
217735863739SMike Smith 	AAC_SETREG4(sc, AAC_RX_MAILBOX, command);
217835863739SMike Smith 	AAC_SETREG4(sc, AAC_RX_MAILBOX + 4, arg0);
217935863739SMike Smith 	AAC_SETREG4(sc, AAC_RX_MAILBOX + 8, arg1);
218035863739SMike Smith 	AAC_SETREG4(sc, AAC_RX_MAILBOX + 12, arg2);
218135863739SMike Smith 	AAC_SETREG4(sc, AAC_RX_MAILBOX + 16, arg3);
218235863739SMike Smith }
218335863739SMike Smith 
2184b3457b51SScott Long static void
2185b3457b51SScott Long aac_fa_set_mailbox(struct aac_softc *sc, u_int32_t command,
2186b3457b51SScott Long 		u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3)
2187b3457b51SScott Long {
2188b3457b51SScott Long 	debug_called(4);
2189b3457b51SScott Long 
2190b3457b51SScott Long 	AAC_SETREG4(sc, AAC_FA_MAILBOX, command);
2191b3457b51SScott Long 	AAC_FA_HACK(sc);
2192b3457b51SScott Long 	AAC_SETREG4(sc, AAC_FA_MAILBOX + 4, arg0);
2193b3457b51SScott Long 	AAC_FA_HACK(sc);
2194b3457b51SScott Long 	AAC_SETREG4(sc, AAC_FA_MAILBOX + 8, arg1);
2195b3457b51SScott Long 	AAC_FA_HACK(sc);
2196b3457b51SScott Long 	AAC_SETREG4(sc, AAC_FA_MAILBOX + 12, arg2);
2197b3457b51SScott Long 	AAC_FA_HACK(sc);
2198b3457b51SScott Long 	AAC_SETREG4(sc, AAC_FA_MAILBOX + 16, arg3);
2199b3457b51SScott Long 	AAC_FA_HACK(sc);
2200b3457b51SScott Long }
2201b3457b51SScott Long 
2202914da7d0SScott Long /*
220335863739SMike Smith  * Fetch the immediate command status word
220435863739SMike Smith  */
220535863739SMike Smith static int
2206a6d35632SScott Long aac_sa_get_mailbox(struct aac_softc *sc, int mb)
220735863739SMike Smith {
220835863739SMike Smith 	debug_called(4);
220935863739SMike Smith 
2210a6d35632SScott Long 	return(AAC_GETREG4(sc, AAC_SA_MAILBOX + (mb * 4)));
221135863739SMike Smith }
221235863739SMike Smith 
221335863739SMike Smith static int
2214a6d35632SScott Long aac_rx_get_mailbox(struct aac_softc *sc, int mb)
221535863739SMike Smith {
221635863739SMike Smith 	debug_called(4);
221735863739SMike Smith 
2218a6d35632SScott Long 	return(AAC_GETREG4(sc, AAC_RX_MAILBOX + (mb * 4)));
221935863739SMike Smith }
222035863739SMike Smith 
2221b3457b51SScott Long static int
2222a6d35632SScott Long aac_fa_get_mailbox(struct aac_softc *sc, int mb)
2223b3457b51SScott Long {
2224b3457b51SScott Long 	int val;
2225b3457b51SScott Long 
2226b3457b51SScott Long 	debug_called(4);
2227b3457b51SScott Long 
2228a6d35632SScott Long 	val = AAC_GETREG4(sc, AAC_FA_MAILBOX + (mb * 4));
2229b3457b51SScott Long 	return (val);
2230b3457b51SScott Long }
2231b3457b51SScott Long 
2232914da7d0SScott Long /*
223335863739SMike Smith  * Set/clear interrupt masks
223435863739SMike Smith  */
223535863739SMike Smith static void
223635863739SMike Smith aac_sa_set_interrupts(struct aac_softc *sc, int enable)
223735863739SMike Smith {
223835863739SMike Smith 	debug(2, "%sable interrupts", enable ? "en" : "dis");
223935863739SMike Smith 
224035863739SMike Smith 	if (enable) {
224135863739SMike Smith 		AAC_SETREG2((sc), AAC_SA_MASK0_CLEAR, AAC_DB_INTERRUPTS);
224235863739SMike Smith 	} else {
224335863739SMike Smith 		AAC_SETREG2((sc), AAC_SA_MASK0_SET, ~0);
224435863739SMike Smith 	}
224535863739SMike Smith }
224635863739SMike Smith 
224735863739SMike Smith static void
224835863739SMike Smith aac_rx_set_interrupts(struct aac_softc *sc, int enable)
224935863739SMike Smith {
225035863739SMike Smith 	debug(2, "%sable interrupts", enable ? "en" : "dis");
225135863739SMike Smith 
225235863739SMike Smith 	if (enable) {
225335863739SMike Smith 		AAC_SETREG4(sc, AAC_RX_OIMR, ~AAC_DB_INTERRUPTS);
225435863739SMike Smith 	} else {
225535863739SMike Smith 		AAC_SETREG4(sc, AAC_RX_OIMR, ~0);
225635863739SMike Smith 	}
225735863739SMike Smith }
225835863739SMike Smith 
2259b3457b51SScott Long static void
2260b3457b51SScott Long aac_fa_set_interrupts(struct aac_softc *sc, int enable)
2261b3457b51SScott Long {
2262b3457b51SScott Long 	debug(2, "%sable interrupts", enable ? "en" : "dis");
2263b3457b51SScott Long 
2264b3457b51SScott Long 	if (enable) {
2265b3457b51SScott Long 		AAC_SETREG2((sc), AAC_FA_MASK0_CLEAR, AAC_DB_INTERRUPTS);
2266b3457b51SScott Long 		AAC_FA_HACK(sc);
2267b3457b51SScott Long 	} else {
2268b3457b51SScott Long 		AAC_SETREG2((sc), AAC_FA_MASK0, ~0);
2269b3457b51SScott Long 		AAC_FA_HACK(sc);
2270b3457b51SScott Long 	}
2271b3457b51SScott Long }
2272b3457b51SScott Long 
2273914da7d0SScott Long /*
2274914da7d0SScott Long  * Debugging and Diagnostics
2275914da7d0SScott Long  */
227635863739SMike Smith 
2277914da7d0SScott Long /*
227835863739SMike Smith  * Print some information about the controller.
227935863739SMike Smith  */
228035863739SMike Smith static void
228135863739SMike Smith aac_describe_controller(struct aac_softc *sc)
228235863739SMike Smith {
2283cbfd045bSScott Long 	struct aac_fib *fib;
228435863739SMike Smith 	struct aac_adapter_info	*info;
228535863739SMike Smith 
228635863739SMike Smith 	debug_called(2);
228735863739SMike Smith 
2288fe3cb0e1SScott Long 	aac_alloc_sync_fib(sc, &fib, 0);
2289cbfd045bSScott Long 
2290cbfd045bSScott Long 	fib->data[0] = 0;
2291cbfd045bSScott Long 	if (aac_sync_fib(sc, RequestAdapterInfo, 0, fib, 1)) {
229235863739SMike Smith 		device_printf(sc->aac_dev, "RequestAdapterInfo failed\n");
2293fe3cb0e1SScott Long 		aac_release_sync_fib(sc);
229435863739SMike Smith 		return;
229535863739SMike Smith 	}
2296cbfd045bSScott Long 	info = (struct aac_adapter_info *)&fib->data[0];
229735863739SMike Smith 
229836e0bf6eSScott Long 	device_printf(sc->aac_dev, "%s %dMHz, %dMB cache memory, %s\n",
2299c6eafcf2SScott Long 		      aac_describe_code(aac_cpu_variant, info->CpuVariant),
230036e0bf6eSScott Long 		      info->ClockSpeed, info->BufferMem / (1024 * 1024),
2301914da7d0SScott Long 		      aac_describe_code(aac_battery_platform,
2302914da7d0SScott Long 					info->batteryPlatform));
230335863739SMike Smith 
230435863739SMike Smith 	/* save the kernel revision structure for later use */
230535863739SMike Smith 	sc->aac_revision = info->KernelRevision;
230636e0bf6eSScott Long 	device_printf(sc->aac_dev, "Kernel %d.%d-%d, Build %d, S/N %6X\n",
230735863739SMike Smith 		      info->KernelRevision.external.comp.major,
230835863739SMike Smith 		      info->KernelRevision.external.comp.minor,
230935863739SMike Smith 		      info->KernelRevision.external.comp.dash,
231036e0bf6eSScott Long 		      info->KernelRevision.buildNumber,
231136e0bf6eSScott Long 		      (u_int32_t)(info->SerialNumber & 0xffffff));
2312fe3cb0e1SScott Long 
2313fe3cb0e1SScott Long 	aac_release_sync_fib(sc);
2314a6d35632SScott Long 
2315a6d35632SScott Long 	if (1 || bootverbose) {
2316a6d35632SScott Long 		device_printf(sc->aac_dev, "Supported Options=%b\n",
2317a6d35632SScott Long 			      sc->supported_options,
2318a6d35632SScott Long 			      "\20"
2319a6d35632SScott Long 			      "\1SNAPSHOT"
2320a6d35632SScott Long 			      "\2CLUSTERS"
2321a6d35632SScott Long 			      "\3WCACHE"
2322a6d35632SScott Long 			      "\4DATA64"
2323a6d35632SScott Long 			      "\5HOSTTIME"
2324a6d35632SScott Long 			      "\6RAID50"
2325a6d35632SScott Long 			      "\7WINDOW4GB"
2326a6d35632SScott Long 			      "\10SCSIUPGD"
2327a6d35632SScott Long 			      "\11SOFTERR"
2328a6d35632SScott Long 			      "\12NORECOND"
2329a6d35632SScott Long 			      "\13SGMAP64"
2330a6d35632SScott Long 			      "\14ALARM"
2331a6d35632SScott Long 			      "\15NONDASD");
2332a6d35632SScott Long 	}
233335863739SMike Smith }
233435863739SMike Smith 
2335914da7d0SScott Long /*
233635863739SMike Smith  * Look up a text description of a numeric error code and return a pointer to
233735863739SMike Smith  * same.
233835863739SMike Smith  */
233935863739SMike Smith static char *
234035863739SMike Smith aac_describe_code(struct aac_code_lookup *table, u_int32_t code)
234135863739SMike Smith {
234235863739SMike Smith 	int i;
234335863739SMike Smith 
234435863739SMike Smith 	for (i = 0; table[i].string != NULL; i++)
234535863739SMike Smith 		if (table[i].code == code)
234635863739SMike Smith 			return(table[i].string);
234735863739SMike Smith 	return(table[i + 1].string);
234835863739SMike Smith }
234935863739SMike Smith 
2350914da7d0SScott Long /*
2351914da7d0SScott Long  * Management Interface
2352914da7d0SScott Long  */
235335863739SMike Smith 
235435863739SMike Smith static int
2355c3d15322SScott Long aac_open(dev_t dev, int flags, int fmt, d_thread_t *td)
235635863739SMike Smith {
2357914da7d0SScott Long 	struct aac_softc *sc;
235835863739SMike Smith 
235935863739SMike Smith 	debug_called(2);
236035863739SMike Smith 
2361914da7d0SScott Long 	sc = dev->si_drv1;
2362914da7d0SScott Long 
236335863739SMike Smith 	/* Check to make sure the device isn't already open */
236435863739SMike Smith 	if (sc->aac_state & AAC_STATE_OPEN) {
236535863739SMike Smith 		return EBUSY;
236635863739SMike Smith 	}
236735863739SMike Smith 	sc->aac_state |= AAC_STATE_OPEN;
236835863739SMike Smith 
236935863739SMike Smith 	return 0;
237035863739SMike Smith }
237135863739SMike Smith 
237235863739SMike Smith static int
2373c3d15322SScott Long aac_close(dev_t dev, int flags, int fmt, d_thread_t *td)
237435863739SMike Smith {
2375914da7d0SScott Long 	struct aac_softc *sc;
237635863739SMike Smith 
237735863739SMike Smith 	debug_called(2);
237835863739SMike Smith 
2379914da7d0SScott Long 	sc = dev->si_drv1;
2380914da7d0SScott Long 
238135863739SMike Smith 	/* Mark this unit as no longer open  */
238235863739SMike Smith 	sc->aac_state &= ~AAC_STATE_OPEN;
238335863739SMike Smith 
238435863739SMike Smith 	return 0;
238535863739SMike Smith }
238635863739SMike Smith 
238735863739SMike Smith static int
2388c3d15322SScott Long aac_ioctl(dev_t dev, u_long cmd, caddr_t arg, int flag, d_thread_t *td)
238935863739SMike Smith {
2390914da7d0SScott Long 	union aac_statrequest *as;
2391914da7d0SScott Long 	struct aac_softc *sc;
23920b94a66eSMike Smith 	int error = 0;
2393b88ffdc8SScott Long 	uint32_t cookie;
239435863739SMike Smith 
239535863739SMike Smith 	debug_called(2);
239635863739SMike Smith 
2397914da7d0SScott Long 	as = (union aac_statrequest *)arg;
2398914da7d0SScott Long 	sc = dev->si_drv1;
2399914da7d0SScott Long 
240035863739SMike Smith 	switch (cmd) {
24010b94a66eSMike Smith 	case AACIO_STATS:
24020b94a66eSMike Smith 		switch (as->as_item) {
24030b94a66eSMike Smith 		case AACQ_FREE:
24040b94a66eSMike Smith 		case AACQ_BIO:
24050b94a66eSMike Smith 		case AACQ_READY:
24060b94a66eSMike Smith 		case AACQ_BUSY:
2407c6eafcf2SScott Long 			bcopy(&sc->aac_qstat[as->as_item], &as->as_qstat,
2408c6eafcf2SScott Long 			      sizeof(struct aac_qstat));
24090b94a66eSMike Smith 			break;
24100b94a66eSMike Smith 		default:
24110b94a66eSMike Smith 			error = ENOENT;
24120b94a66eSMike Smith 			break;
24130b94a66eSMike Smith 		}
24140b94a66eSMike Smith 	break;
24150b94a66eSMike Smith 
241635863739SMike Smith 	case FSACTL_SENDFIB:
2417fb0c27d7SScott Long 		arg = *(caddr_t*)arg;
2418fb0c27d7SScott Long 	case FSACTL_LNX_SENDFIB:
24190b94a66eSMike Smith 		debug(1, "FSACTL_SENDFIB");
242035863739SMike Smith 		error = aac_ioctl_sendfib(sc, arg);
242135863739SMike Smith 		break;
242235863739SMike Smith 	case FSACTL_AIF_THREAD:
2423fb0c27d7SScott Long 	case FSACTL_LNX_AIF_THREAD:
24240b94a66eSMike Smith 		debug(1, "FSACTL_AIF_THREAD");
242535863739SMike Smith 		error = EINVAL;
242635863739SMike Smith 		break;
242735863739SMike Smith 	case FSACTL_OPEN_GET_ADAPTER_FIB:
2428fb0c27d7SScott Long 		arg = *(caddr_t*)arg;
2429fb0c27d7SScott Long 	case FSACTL_LNX_OPEN_GET_ADAPTER_FIB:
24300b94a66eSMike Smith 		debug(1, "FSACTL_OPEN_GET_ADAPTER_FIB");
243135863739SMike Smith 		/*
243235863739SMike Smith 		 * Pass the caller out an AdapterFibContext.
243335863739SMike Smith 		 *
243435863739SMike Smith 		 * Note that because we only support one opener, we
243535863739SMike Smith 		 * basically ignore this.  Set the caller's context to a magic
243635863739SMike Smith 		 * number just in case.
24370b94a66eSMike Smith 		 *
24380b94a66eSMike Smith 		 * The Linux code hands the driver a pointer into kernel space,
24390b94a66eSMike Smith 		 * and then trusts it when the caller hands it back.  Aiee!
2440914da7d0SScott Long 		 * Here, we give it the proc pointer of the per-adapter aif
2441914da7d0SScott Long 		 * thread. It's only used as a sanity check in other calls.
244235863739SMike Smith 		 */
2443b88ffdc8SScott Long 		cookie = (uint32_t)(uintptr_t)sc->aifthread;
2444b88ffdc8SScott Long 		error = copyout(&cookie, arg, sizeof(cookie));
244535863739SMike Smith 		break;
244635863739SMike Smith 	case FSACTL_GET_NEXT_ADAPTER_FIB:
2447fb0c27d7SScott Long 		arg = *(caddr_t*)arg;
2448fb0c27d7SScott Long 	case FSACTL_LNX_GET_NEXT_ADAPTER_FIB:
24490b94a66eSMike Smith 		debug(1, "FSACTL_GET_NEXT_ADAPTER_FIB");
2450fb0c27d7SScott Long 		error = aac_getnext_aif(sc, arg);
245135863739SMike Smith 		break;
245235863739SMike Smith 	case FSACTL_CLOSE_GET_ADAPTER_FIB:
2453fb0c27d7SScott Long 	case FSACTL_LNX_CLOSE_GET_ADAPTER_FIB:
24540b94a66eSMike Smith 		debug(1, "FSACTL_CLOSE_GET_ADAPTER_FIB");
245535863739SMike Smith 		/* don't do anything here */
245635863739SMike Smith 		break;
245735863739SMike Smith 	case FSACTL_MINIPORT_REV_CHECK:
2458fb0c27d7SScott Long 		arg = *(caddr_t*)arg;
2459fb0c27d7SScott Long 	case FSACTL_LNX_MINIPORT_REV_CHECK:
24600b94a66eSMike Smith 		debug(1, "FSACTL_MINIPORT_REV_CHECK");
2461fb0c27d7SScott Long 		error = aac_rev_check(sc, arg);
246235863739SMike Smith 		break;
246336e0bf6eSScott Long 	case FSACTL_QUERY_DISK:
246436e0bf6eSScott Long 		arg = *(caddr_t*)arg;
246536e0bf6eSScott Long 	case FSACTL_LNX_QUERY_DISK:
246636e0bf6eSScott Long 		debug(1, "FSACTL_QUERY_DISK");
246736e0bf6eSScott Long 		error = aac_query_disk(sc, arg);
246836e0bf6eSScott Long 			break;
246936e0bf6eSScott Long 	case FSACTL_DELETE_DISK:
247036e0bf6eSScott Long 	case FSACTL_LNX_DELETE_DISK:
2471914da7d0SScott Long 		/*
2472914da7d0SScott Long 		 * We don't trust the underland to tell us when to delete a
2473914da7d0SScott Long 		 * container, rather we rely on an AIF coming from the
2474914da7d0SScott Long 		 * controller
2475914da7d0SScott Long 		 */
247636e0bf6eSScott Long 		error = 0;
247736e0bf6eSScott Long 		break;
247835863739SMike Smith 	default:
2479b3457b51SScott Long 		debug(1, "unsupported cmd 0x%lx\n", cmd);
248035863739SMike Smith 		error = EINVAL;
248135863739SMike Smith 		break;
248235863739SMike Smith 	}
248335863739SMike Smith 	return(error);
248435863739SMike Smith }
248535863739SMike Smith 
2486b3457b51SScott Long static int
2487c3d15322SScott Long aac_poll(dev_t dev, int poll_events, d_thread_t *td)
2488b3457b51SScott Long {
2489b3457b51SScott Long 	struct aac_softc *sc;
2490b3457b51SScott Long 	int revents;
2491b3457b51SScott Long 
2492b3457b51SScott Long 	sc = dev->si_drv1;
2493b3457b51SScott Long 	revents = 0;
2494b3457b51SScott Long 
2495c3d15322SScott Long 	AAC_LOCK_ACQUIRE(&sc->aac_aifq_lock);
2496b3457b51SScott Long 	if ((poll_events & (POLLRDNORM | POLLIN)) != 0) {
2497b3457b51SScott Long 		if (sc->aac_aifq_tail != sc->aac_aifq_head)
2498b3457b51SScott Long 			revents |= poll_events & (POLLIN | POLLRDNORM);
2499b3457b51SScott Long 	}
2500b3457b51SScott Long 	AAC_LOCK_RELEASE(&sc->aac_aifq_lock);
2501b3457b51SScott Long 
2502b3457b51SScott Long 	if (revents == 0) {
2503b3457b51SScott Long 		if (poll_events & (POLLIN | POLLRDNORM))
2504b3457b51SScott Long 			selrecord(td, &sc->rcv_select);
2505b3457b51SScott Long 	}
2506b3457b51SScott Long 
2507b3457b51SScott Long 	return (revents);
2508b3457b51SScott Long }
2509b3457b51SScott Long 
2510914da7d0SScott Long /*
251135863739SMike Smith  * Send a FIB supplied from userspace
251235863739SMike Smith  */
251335863739SMike Smith static int
251435863739SMike Smith aac_ioctl_sendfib(struct aac_softc *sc, caddr_t ufib)
251535863739SMike Smith {
251635863739SMike Smith 	struct aac_command *cm;
251735863739SMike Smith 	int size, error;
251835863739SMike Smith 
251935863739SMike Smith 	debug_called(2);
252035863739SMike Smith 
252135863739SMike Smith 	cm = NULL;
252235863739SMike Smith 
252335863739SMike Smith 	/*
252435863739SMike Smith 	 * Get a command
252535863739SMike Smith 	 */
2526ae543596SScott Long 	AAC_LOCK_ACQUIRE(&sc->aac_io_lock);
252735863739SMike Smith 	if (aac_alloc_command(sc, &cm)) {
252835863739SMike Smith 		error = EBUSY;
252935863739SMike Smith 		goto out;
253035863739SMike Smith 	}
253135863739SMike Smith 
253235863739SMike Smith 	/*
253335863739SMike Smith 	 * Fetch the FIB header, then re-copy to get data as well.
253435863739SMike Smith 	 */
2535914da7d0SScott Long 	if ((error = copyin(ufib, cm->cm_fib,
2536914da7d0SScott Long 			    sizeof(struct aac_fib_header))) != 0)
253735863739SMike Smith 		goto out;
253835863739SMike Smith 	size = cm->cm_fib->Header.Size + sizeof(struct aac_fib_header);
253935863739SMike Smith 	if (size > sizeof(struct aac_fib)) {
2540b88ffdc8SScott Long 		device_printf(sc->aac_dev, "incoming FIB oversized (%d > %zd)\n",
2541914da7d0SScott Long 			      size, sizeof(struct aac_fib));
254235863739SMike Smith 		size = sizeof(struct aac_fib);
254335863739SMike Smith 	}
254435863739SMike Smith 	if ((error = copyin(ufib, cm->cm_fib, size)) != 0)
254535863739SMike Smith 		goto out;
254635863739SMike Smith 	cm->cm_fib->Header.Size = size;
2547f6c4dd3fSScott Long 	cm->cm_timestamp = time_second;
254835863739SMike Smith 
254935863739SMike Smith 	/*
255035863739SMike Smith 	 * Pass the FIB to the controller, wait for it to complete.
255135863739SMike Smith 	 */
2552b3457b51SScott Long 	if ((error = aac_wait_command(cm, 30)) != 0) {	/* XXX user timeout? */
255370545d1aSScott Long 		device_printf(sc->aac_dev,
255470545d1aSScott Long 			      "aac_wait_command return %d\n", error);
255535863739SMike Smith 		goto out;
2556b3457b51SScott Long 	}
255735863739SMike Smith 
255835863739SMike Smith 	/*
255935863739SMike Smith 	 * Copy the FIB and data back out to the caller.
256035863739SMike Smith 	 */
256135863739SMike Smith 	size = cm->cm_fib->Header.Size;
256235863739SMike Smith 	if (size > sizeof(struct aac_fib)) {
2563b88ffdc8SScott Long 		device_printf(sc->aac_dev, "outbound FIB oversized (%d > %zd)\n",
2564914da7d0SScott Long 			      size, sizeof(struct aac_fib));
256535863739SMike Smith 		size = sizeof(struct aac_fib);
256635863739SMike Smith 	}
256735863739SMike Smith 	error = copyout(cm->cm_fib, ufib, size);
256835863739SMike Smith 
256935863739SMike Smith out:
2570f6c4dd3fSScott Long 	if (cm != NULL) {
257135863739SMike Smith 		aac_release_command(cm);
2572f6c4dd3fSScott Long 	}
2573ae543596SScott Long 
2574ae543596SScott Long 	AAC_LOCK_RELEASE(&sc->aac_io_lock);
257535863739SMike Smith 	return(error);
257635863739SMike Smith }
257735863739SMike Smith 
2578914da7d0SScott Long /*
257935863739SMike Smith  * Handle an AIF sent to us by the controller; queue it for later reference.
258036e0bf6eSScott Long  * If the queue fills up, then drop the older entries.
258135863739SMike Smith  */
258235863739SMike Smith static void
258336e0bf6eSScott Long aac_handle_aif(struct aac_softc *sc, struct aac_fib *fib)
258435863739SMike Smith {
258536e0bf6eSScott Long 	struct aac_aif_command *aif;
258636e0bf6eSScott Long 	struct aac_container *co, *co_next;
2587cbfd045bSScott Long 	struct aac_mntinfo *mi;
2588cbfd045bSScott Long 	struct aac_mntinforesp *mir = NULL;
258936e0bf6eSScott Long 	u_int16_t rsize;
2590b3457b51SScott Long 	int next, found;
2591795d7dc0SScott Long 	int count = 0, added = 0, i = 0;
259235863739SMike Smith 
259335863739SMike Smith 	debug_called(2);
259435863739SMike Smith 
259536e0bf6eSScott Long 	aif = (struct aac_aif_command*)&fib->data[0];
259636e0bf6eSScott Long 	aac_print_aif(sc, aif);
259736e0bf6eSScott Long 
259836e0bf6eSScott Long 	/* Is it an event that we should care about? */
259936e0bf6eSScott Long 	switch (aif->command) {
260036e0bf6eSScott Long 	case AifCmdEventNotify:
260136e0bf6eSScott Long 		switch (aif->data.EN.type) {
260236e0bf6eSScott Long 		case AifEnAddContainer:
260336e0bf6eSScott Long 		case AifEnDeleteContainer:
260436e0bf6eSScott Long 			/*
2605914da7d0SScott Long 			 * A container was added or deleted, but the message
2606914da7d0SScott Long 			 * doesn't tell us anything else!  Re-enumerate the
2607914da7d0SScott Long 			 * containers and sort things out.
260836e0bf6eSScott Long 			 */
2609fe3cb0e1SScott Long 			aac_alloc_sync_fib(sc, &fib, 0);
2610cbfd045bSScott Long 			mi = (struct aac_mntinfo *)&fib->data[0];
261136e0bf6eSScott Long 			do {
261236e0bf6eSScott Long 				/*
2613914da7d0SScott Long 				 * Ask the controller for its containers one at
2614914da7d0SScott Long 				 * a time.
2615914da7d0SScott Long 				 * XXX What if the controller's list changes
2616914da7d0SScott Long 				 * midway through this enumaration?
261736e0bf6eSScott Long 				 * XXX This should be done async.
261836e0bf6eSScott Long 				 */
261939ee03c3SScott Long 				bzero(mi, sizeof(struct aac_mntinfo));
262039ee03c3SScott Long 				mi->Command = VM_NameServe;
262139ee03c3SScott Long 				mi->MntType = FT_FILESYS;
2622cbfd045bSScott Long 				mi->MntCount = i;
262336e0bf6eSScott Long 				rsize = sizeof(mir);
2624cbfd045bSScott Long 				if (aac_sync_fib(sc, ContainerCommand, 0, fib,
2625cbfd045bSScott Long 						 sizeof(struct aac_mntinfo))) {
2626795d7dc0SScott Long 					printf("Error probing container %d\n",
2627914da7d0SScott Long 					      i);
262836e0bf6eSScott Long 					continue;
262936e0bf6eSScott Long 				}
2630cbfd045bSScott Long 				mir = (struct aac_mntinforesp *)&fib->data[0];
2631795d7dc0SScott Long 				/* XXX Need to check if count changed */
2632795d7dc0SScott Long 				count = mir->MntRespCount;
263336e0bf6eSScott Long 				/*
2634914da7d0SScott Long 				 * Check the container against our list.
2635914da7d0SScott Long 				 * co->co_found was already set to 0 in a
2636914da7d0SScott Long 				 * previous run.
263736e0bf6eSScott Long 				 */
2638cbfd045bSScott Long 				if ((mir->Status == ST_OK) &&
2639cbfd045bSScott Long 				    (mir->MntTable[0].VolType != CT_NONE)) {
264036e0bf6eSScott Long 					found = 0;
2641914da7d0SScott Long 					TAILQ_FOREACH(co,
2642914da7d0SScott Long 						      &sc->aac_container_tqh,
2643914da7d0SScott Long 						      co_link) {
264436e0bf6eSScott Long 						if (co->co_mntobj.ObjectId ==
2645cbfd045bSScott Long 						    mir->MntTable[0].ObjectId) {
264636e0bf6eSScott Long 							co->co_found = 1;
264736e0bf6eSScott Long 							found = 1;
264836e0bf6eSScott Long 							break;
264936e0bf6eSScott Long 						}
265036e0bf6eSScott Long 					}
2651914da7d0SScott Long 					/*
2652914da7d0SScott Long 					 * If the container matched, continue
2653914da7d0SScott Long 					 * in the list.
2654914da7d0SScott Long 					 */
265536e0bf6eSScott Long 					if (found) {
265636e0bf6eSScott Long 						i++;
265736e0bf6eSScott Long 						continue;
265836e0bf6eSScott Long 					}
265936e0bf6eSScott Long 
266036e0bf6eSScott Long 					/*
2661914da7d0SScott Long 					 * This is a new container.  Do all the
266270545d1aSScott Long 					 * appropriate things to set it up.
266370545d1aSScott Long 					 */
2664cbfd045bSScott Long 					aac_add_container(sc, mir, 1);
266536e0bf6eSScott Long 					added = 1;
266636e0bf6eSScott Long 				}
266736e0bf6eSScott Long 				i++;
2668795d7dc0SScott Long 			} while ((i < count) && (i < AAC_MAX_CONTAINERS));
2669cbfd045bSScott Long 			aac_release_sync_fib(sc);
267036e0bf6eSScott Long 
267136e0bf6eSScott Long 			/*
2672914da7d0SScott Long 			 * Go through our list of containers and see which ones
2673914da7d0SScott Long 			 * were not marked 'found'.  Since the controller didn't
2674914da7d0SScott Long 			 * list them they must have been deleted.  Do the
2675914da7d0SScott Long 			 * appropriate steps to destroy the device.  Also reset
2676914da7d0SScott Long 			 * the co->co_found field.
267736e0bf6eSScott Long 			 */
267836e0bf6eSScott Long 			co = TAILQ_FIRST(&sc->aac_container_tqh);
267936e0bf6eSScott Long 			while (co != NULL) {
268036e0bf6eSScott Long 				if (co->co_found == 0) {
2681914da7d0SScott Long 					device_delete_child(sc->aac_dev,
2682914da7d0SScott Long 							    co->co_disk);
268336e0bf6eSScott Long 					co_next = TAILQ_NEXT(co, co_link);
2684c3d15322SScott Long 					AAC_LOCK_ACQUIRE(&sc->
2685914da7d0SScott Long 							aac_container_lock);
2686914da7d0SScott Long 					TAILQ_REMOVE(&sc->aac_container_tqh, co,
2687914da7d0SScott Long 						     co_link);
2688914da7d0SScott Long 					AAC_LOCK_RELEASE(&sc->
2689914da7d0SScott Long 							 aac_container_lock);
269036e0bf6eSScott Long 					FREE(co, M_AACBUF);
269136e0bf6eSScott Long 					co = co_next;
269236e0bf6eSScott Long 				} else {
269336e0bf6eSScott Long 					co->co_found = 0;
269436e0bf6eSScott Long 					co = TAILQ_NEXT(co, co_link);
269536e0bf6eSScott Long 				}
269636e0bf6eSScott Long 			}
269736e0bf6eSScott Long 
269836e0bf6eSScott Long 			/* Attach the newly created containers */
269936e0bf6eSScott Long 			if (added)
270036e0bf6eSScott Long 				bus_generic_attach(sc->aac_dev);
270136e0bf6eSScott Long 
270236e0bf6eSScott Long 			break;
270336e0bf6eSScott Long 
270436e0bf6eSScott Long 		default:
270536e0bf6eSScott Long 			break;
270636e0bf6eSScott Long 		}
270736e0bf6eSScott Long 
270836e0bf6eSScott Long 	default:
270936e0bf6eSScott Long 		break;
271036e0bf6eSScott Long 	}
271136e0bf6eSScott Long 
271236e0bf6eSScott Long 	/* Copy the AIF data to the AIF queue for ioctl retrieval */
2713c3d15322SScott Long 	AAC_LOCK_ACQUIRE(&sc->aac_aifq_lock);
271435863739SMike Smith 	next = (sc->aac_aifq_head + 1) % AAC_AIFQ_LENGTH;
271535863739SMike Smith 	if (next != sc->aac_aifq_tail) {
271635863739SMike Smith 		bcopy(aif, &sc->aac_aifq[next], sizeof(struct aac_aif_command));
271735863739SMike Smith 		sc->aac_aifq_head = next;
2718b3457b51SScott Long 
2719b3457b51SScott Long 		/* On the off chance that someone is sleeping for an aif... */
272035863739SMike Smith 		if (sc->aac_state & AAC_STATE_AIF_SLEEPER)
272135863739SMike Smith 			wakeup(sc->aac_aifq);
2722b3457b51SScott Long 		/* Wakeup any poll()ers */
2723512824f8SSeigo Tanimura 		selwakeuppri(&sc->rcv_select, PRIBIO);
272435863739SMike Smith 	}
2725b3457b51SScott Long 	AAC_LOCK_RELEASE(&sc->aac_aifq_lock);
272636e0bf6eSScott Long 
272736e0bf6eSScott Long 	return;
272835863739SMike Smith }
272935863739SMike Smith 
2730914da7d0SScott Long /*
27310b94a66eSMike Smith  * Return the Revision of the driver to userspace and check to see if the
273236e0bf6eSScott Long  * userspace app is possibly compatible.  This is extremely bogus since
273336e0bf6eSScott Long  * our driver doesn't follow Adaptec's versioning system.  Cheat by just
273436e0bf6eSScott Long  * returning what the card reported.
273535863739SMike Smith  */
273635863739SMike Smith static int
2737fb0c27d7SScott Long aac_rev_check(struct aac_softc *sc, caddr_t udata)
273835863739SMike Smith {
273935863739SMike Smith 	struct aac_rev_check rev_check;
274035863739SMike Smith 	struct aac_rev_check_resp rev_check_resp;
274135863739SMike Smith 	int error = 0;
274235863739SMike Smith 
274335863739SMike Smith 	debug_called(2);
274435863739SMike Smith 
274535863739SMike Smith 	/*
274635863739SMike Smith 	 * Copyin the revision struct from userspace
274735863739SMike Smith 	 */
2748c6eafcf2SScott Long 	if ((error = copyin(udata, (caddr_t)&rev_check,
2749c6eafcf2SScott Long 			sizeof(struct aac_rev_check))) != 0) {
275035863739SMike Smith 		return error;
275135863739SMike Smith 	}
275235863739SMike Smith 
2753914da7d0SScott Long 	debug(2, "Userland revision= %d\n",
2754914da7d0SScott Long 	      rev_check.callingRevision.buildNumber);
275535863739SMike Smith 
275635863739SMike Smith 	/*
275735863739SMike Smith 	 * Doctor up the response struct.
275835863739SMike Smith 	 */
275935863739SMike Smith 	rev_check_resp.possiblyCompatible = 1;
2760914da7d0SScott Long 	rev_check_resp.adapterSWRevision.external.ul =
2761914da7d0SScott Long 	    sc->aac_revision.external.ul;
2762914da7d0SScott Long 	rev_check_resp.adapterSWRevision.buildNumber =
2763914da7d0SScott Long 	    sc->aac_revision.buildNumber;
276435863739SMike Smith 
2765c6eafcf2SScott Long 	return(copyout((caddr_t)&rev_check_resp, udata,
2766c6eafcf2SScott Long 			sizeof(struct aac_rev_check_resp)));
276735863739SMike Smith }
276835863739SMike Smith 
2769914da7d0SScott Long /*
277035863739SMike Smith  * Pass the caller the next AIF in their queue
277135863739SMike Smith  */
277235863739SMike Smith static int
2773fb0c27d7SScott Long aac_getnext_aif(struct aac_softc *sc, caddr_t arg)
277435863739SMike Smith {
277535863739SMike Smith 	struct get_adapter_fib_ioctl agf;
27769e2e96d8SScott Long 	int error;
277735863739SMike Smith 
277835863739SMike Smith 	debug_called(2);
277935863739SMike Smith 
278035863739SMike Smith 	if ((error = copyin(arg, &agf, sizeof(agf))) == 0) {
278135863739SMike Smith 
278235863739SMike Smith 		/*
278335863739SMike Smith 		 * Check the magic number that we gave the caller.
278435863739SMike Smith 		 */
2785b88ffdc8SScott Long 		if (agf.AdapterFibContext != (int)(uintptr_t)sc->aifthread) {
278635863739SMike Smith 			error = EFAULT;
278735863739SMike Smith 		} else {
2788fb0c27d7SScott Long 			error = aac_return_aif(sc, agf.AifFib);
278935863739SMike Smith 			if ((error == EAGAIN) && (agf.Wait)) {
279035863739SMike Smith 				sc->aac_state |= AAC_STATE_AIF_SLEEPER;
279135863739SMike Smith 				while (error == EAGAIN) {
2792914da7d0SScott Long 					error = tsleep(sc->aac_aifq, PRIBIO |
2793914da7d0SScott Long 						       PCATCH, "aacaif", 0);
279435863739SMike Smith 					if (error == 0)
2795914da7d0SScott Long 						error = aac_return_aif(sc,
2796914da7d0SScott Long 						    agf.AifFib);
279735863739SMike Smith 				}
279835863739SMike Smith 				sc->aac_state &= ~AAC_STATE_AIF_SLEEPER;
279935863739SMike Smith 			}
280035863739SMike Smith 		}
280135863739SMike Smith 	}
280235863739SMike Smith 	return(error);
280335863739SMike Smith }
280435863739SMike Smith 
2805914da7d0SScott Long /*
28060b94a66eSMike Smith  * Hand the next AIF off the top of the queue out to userspace.
28070b94a66eSMike Smith  */
28080b94a66eSMike Smith static int
2809fb0c27d7SScott Long aac_return_aif(struct aac_softc *sc, caddr_t uptr)
28100b94a66eSMike Smith {
28113df780cfSScott Long 	int next, error;
28120b94a66eSMike Smith 
28130b94a66eSMike Smith 	debug_called(2);
28140b94a66eSMike Smith 
2815c3d15322SScott Long 	AAC_LOCK_ACQUIRE(&sc->aac_aifq_lock);
28160b94a66eSMike Smith 	if (sc->aac_aifq_tail == sc->aac_aifq_head) {
28173df780cfSScott Long 		AAC_LOCK_RELEASE(&sc->aac_aifq_lock);
28183df780cfSScott Long 		return (EAGAIN);
28193df780cfSScott Long 	}
28203df780cfSScott Long 
28213df780cfSScott Long 	next = (sc->aac_aifq_tail + 1) % AAC_AIFQ_LENGTH;
28223df780cfSScott Long 	error = copyout(&sc->aac_aifq[next], uptr,
2823c6eafcf2SScott Long 			sizeof(struct aac_aif_command));
282436e0bf6eSScott Long 	if (error)
282570545d1aSScott Long 		device_printf(sc->aac_dev,
282670545d1aSScott Long 		    "aac_return_aif: copyout returned %d\n", error);
28273df780cfSScott Long 	else
28283df780cfSScott Long 		sc->aac_aifq_tail = next;
28293df780cfSScott Long 
2830b3457b51SScott Long 	AAC_LOCK_RELEASE(&sc->aac_aifq_lock);
28310b94a66eSMike Smith 	return(error);
28320b94a66eSMike Smith }
283336e0bf6eSScott Long 
2834914da7d0SScott Long /*
283536e0bf6eSScott Long  * Give the userland some information about the container.  The AAC arch
283636e0bf6eSScott Long  * expects the driver to be a SCSI passthrough type driver, so it expects
283736e0bf6eSScott Long  * the containers to have b:t:l numbers.  Fake it.
283836e0bf6eSScott Long  */
283936e0bf6eSScott Long static int
284036e0bf6eSScott Long aac_query_disk(struct aac_softc *sc, caddr_t uptr)
284136e0bf6eSScott Long {
284236e0bf6eSScott Long 	struct aac_query_disk query_disk;
284336e0bf6eSScott Long 	struct aac_container *co;
2844914da7d0SScott Long 	struct aac_disk	*disk;
284536e0bf6eSScott Long 	int error, id;
284636e0bf6eSScott Long 
284736e0bf6eSScott Long 	debug_called(2);
284836e0bf6eSScott Long 
2849914da7d0SScott Long 	disk = NULL;
2850914da7d0SScott Long 
2851914da7d0SScott Long 	error = copyin(uptr, (caddr_t)&query_disk,
2852914da7d0SScott Long 		       sizeof(struct aac_query_disk));
285336e0bf6eSScott Long 	if (error)
285436e0bf6eSScott Long 		return (error);
285536e0bf6eSScott Long 
285636e0bf6eSScott Long 	id = query_disk.ContainerNumber;
285736e0bf6eSScott Long 	if (id == -1)
285836e0bf6eSScott Long 		return (EINVAL);
285936e0bf6eSScott Long 
2860c3d15322SScott Long 	AAC_LOCK_ACQUIRE(&sc->aac_container_lock);
286136e0bf6eSScott Long 	TAILQ_FOREACH(co, &sc->aac_container_tqh, co_link) {
286236e0bf6eSScott Long 		if (co->co_mntobj.ObjectId == id)
286336e0bf6eSScott Long 			break;
286436e0bf6eSScott Long 		}
286536e0bf6eSScott Long 
286636e0bf6eSScott Long 	if (co == NULL) {
286736e0bf6eSScott Long 			query_disk.Valid = 0;
286836e0bf6eSScott Long 			query_disk.Locked = 0;
286936e0bf6eSScott Long 			query_disk.Deleted = 1;		/* XXX is this right? */
287036e0bf6eSScott Long 	} else {
287136e0bf6eSScott Long 		disk = device_get_softc(co->co_disk);
287236e0bf6eSScott Long 		query_disk.Valid = 1;
2873914da7d0SScott Long 		query_disk.Locked =
2874914da7d0SScott Long 		    (disk->ad_flags & AAC_DISK_OPEN) ? 1 : 0;
287536e0bf6eSScott Long 		query_disk.Deleted = 0;
2876b3457b51SScott Long 		query_disk.Bus = device_get_unit(sc->aac_dev);
287736e0bf6eSScott Long 		query_disk.Target = disk->unit;
287836e0bf6eSScott Long 		query_disk.Lun = 0;
287936e0bf6eSScott Long 		query_disk.UnMapped = 0;
28807540e65eSScott Long 		sprintf(&query_disk.diskDeviceName[0], "%s%d",
28810b7ed341SPoul-Henning Kamp 		        disk->ad_disk->d_name, disk->ad_disk->d_unit);
288236e0bf6eSScott Long 	}
288336e0bf6eSScott Long 	AAC_LOCK_RELEASE(&sc->aac_container_lock);
288436e0bf6eSScott Long 
2885914da7d0SScott Long 	error = copyout((caddr_t)&query_disk, uptr,
2886914da7d0SScott Long 			sizeof(struct aac_query_disk));
288736e0bf6eSScott Long 
288836e0bf6eSScott Long 	return (error);
288936e0bf6eSScott Long }
289036e0bf6eSScott Long 
2891fe3cb0e1SScott Long static void
2892fe3cb0e1SScott Long aac_get_bus_info(struct aac_softc *sc)
2893fe3cb0e1SScott Long {
2894fe3cb0e1SScott Long 	struct aac_fib *fib;
2895fe3cb0e1SScott Long 	struct aac_ctcfg *c_cmd;
2896fe3cb0e1SScott Long 	struct aac_ctcfg_resp *c_resp;
2897fe3cb0e1SScott Long 	struct aac_vmioctl *vmi;
2898fe3cb0e1SScott Long 	struct aac_vmi_businf_resp *vmi_resp;
2899fe3cb0e1SScott Long 	struct aac_getbusinf businfo;
290070545d1aSScott Long 	struct aac_sim *caminf;
2901fe3cb0e1SScott Long 	device_t child;
2902fe3cb0e1SScott Long 	int i, found, error;
2903fe3cb0e1SScott Long 
2904fe3cb0e1SScott Long 	aac_alloc_sync_fib(sc, &fib, 0);
2905fe3cb0e1SScott Long 	c_cmd = (struct aac_ctcfg *)&fib->data[0];
290639ee03c3SScott Long 	bzero(c_cmd, sizeof(struct aac_ctcfg));
2907fe3cb0e1SScott Long 
2908fe3cb0e1SScott Long 	c_cmd->Command = VM_ContainerConfig;
2909fe3cb0e1SScott Long 	c_cmd->cmd = CT_GET_SCSI_METHOD;
2910fe3cb0e1SScott Long 	c_cmd->param = 0;
2911fe3cb0e1SScott Long 
2912fe3cb0e1SScott Long 	error = aac_sync_fib(sc, ContainerCommand, 0, fib,
2913fe3cb0e1SScott Long 	    sizeof(struct aac_ctcfg));
2914fe3cb0e1SScott Long 	if (error) {
2915fe3cb0e1SScott Long 		device_printf(sc->aac_dev, "Error %d sending "
2916fe3cb0e1SScott Long 		    "VM_ContainerConfig command\n", error);
2917fe3cb0e1SScott Long 		aac_release_sync_fib(sc);
2918fe3cb0e1SScott Long 		return;
2919fe3cb0e1SScott Long 	}
2920fe3cb0e1SScott Long 
2921fe3cb0e1SScott Long 	c_resp = (struct aac_ctcfg_resp *)&fib->data[0];
2922fe3cb0e1SScott Long 	if (c_resp->Status != ST_OK) {
2923fe3cb0e1SScott Long 		device_printf(sc->aac_dev, "VM_ContainerConfig returned 0x%x\n",
2924fe3cb0e1SScott Long 		    c_resp->Status);
2925fe3cb0e1SScott Long 		aac_release_sync_fib(sc);
2926fe3cb0e1SScott Long 		return;
2927fe3cb0e1SScott Long 	}
2928fe3cb0e1SScott Long 
2929fe3cb0e1SScott Long 	sc->scsi_method_id = c_resp->param;
2930fe3cb0e1SScott Long 
2931fe3cb0e1SScott Long 	vmi = (struct aac_vmioctl *)&fib->data[0];
293239ee03c3SScott Long 	bzero(vmi, sizeof(struct aac_vmioctl));
293339ee03c3SScott Long 
2934fe3cb0e1SScott Long 	vmi->Command = VM_Ioctl;
2935fe3cb0e1SScott Long 	vmi->ObjType = FT_DRIVE;
2936fe3cb0e1SScott Long 	vmi->MethId = sc->scsi_method_id;
2937fe3cb0e1SScott Long 	vmi->ObjId = 0;
2938fe3cb0e1SScott Long 	vmi->IoctlCmd = GetBusInfo;
2939fe3cb0e1SScott Long 
2940fe3cb0e1SScott Long 	error = aac_sync_fib(sc, ContainerCommand, 0, fib,
2941fe3cb0e1SScott Long 	    sizeof(struct aac_vmioctl));
2942fe3cb0e1SScott Long 	if (error) {
2943fe3cb0e1SScott Long 		device_printf(sc->aac_dev, "Error %d sending VMIoctl command\n",
2944fe3cb0e1SScott Long 		    error);
2945fe3cb0e1SScott Long 		aac_release_sync_fib(sc);
2946fe3cb0e1SScott Long 		return;
2947fe3cb0e1SScott Long 	}
2948fe3cb0e1SScott Long 
2949fe3cb0e1SScott Long 	vmi_resp = (struct aac_vmi_businf_resp *)&fib->data[0];
2950fe3cb0e1SScott Long 	if (vmi_resp->Status != ST_OK) {
2951fe3cb0e1SScott Long 		device_printf(sc->aac_dev, "VM_Ioctl returned %d\n",
2952fe3cb0e1SScott Long 		    vmi_resp->Status);
2953fe3cb0e1SScott Long 		aac_release_sync_fib(sc);
2954fe3cb0e1SScott Long 		return;
2955fe3cb0e1SScott Long 	}
2956fe3cb0e1SScott Long 
2957fe3cb0e1SScott Long 	bcopy(&vmi_resp->BusInf, &businfo, sizeof(struct aac_getbusinf));
2958fe3cb0e1SScott Long 	aac_release_sync_fib(sc);
2959fe3cb0e1SScott Long 
2960fe3cb0e1SScott Long 	found = 0;
2961fe3cb0e1SScott Long 	for (i = 0; i < businfo.BusCount; i++) {
2962fe3cb0e1SScott Long 		if (businfo.BusValid[i] != AAC_BUS_VALID)
2963fe3cb0e1SScott Long 			continue;
2964fe3cb0e1SScott Long 
2965a761a1caSScott Long 		caminf = (struct aac_sim *)malloc( sizeof(struct aac_sim),
2966a761a1caSScott Long 		    M_AACBUF, M_NOWAIT | M_ZERO);
2967fe3cb0e1SScott Long 		if (caminf == NULL)
2968fe3cb0e1SScott Long 			continue;
2969fe3cb0e1SScott Long 
2970fe3cb0e1SScott Long 		child = device_add_child(sc->aac_dev, "aacp", -1);
2971fe3cb0e1SScott Long 		if (child == NULL) {
2972fe3cb0e1SScott Long 			device_printf(sc->aac_dev, "device_add_child failed\n");
2973fe3cb0e1SScott Long 			continue;
2974fe3cb0e1SScott Long 		}
2975fe3cb0e1SScott Long 
2976fe3cb0e1SScott Long 		caminf->TargetsPerBus = businfo.TargetsPerBus;
2977fe3cb0e1SScott Long 		caminf->BusNumber = i;
2978fe3cb0e1SScott Long 		caminf->InitiatorBusId = businfo.InitiatorBusId[i];
2979fe3cb0e1SScott Long 		caminf->aac_sc = sc;
2980ddb8683eSScott Long 		caminf->sim_dev = child;
2981fe3cb0e1SScott Long 
2982fe3cb0e1SScott Long 		device_set_ivars(child, caminf);
2983fe3cb0e1SScott Long 		device_set_desc(child, "SCSI Passthrough Bus");
298470545d1aSScott Long 		TAILQ_INSERT_TAIL(&sc->aac_sim_tqh, caminf, sim_link);
2985fe3cb0e1SScott Long 
2986fe3cb0e1SScott Long 		found = 1;
2987fe3cb0e1SScott Long 	}
2988fe3cb0e1SScott Long 
2989fe3cb0e1SScott Long 	if (found)
2990fe3cb0e1SScott Long 		bus_generic_attach(sc->aac_dev);
2991fe3cb0e1SScott Long 
2992fe3cb0e1SScott Long 	return;
2993fe3cb0e1SScott Long }
2994