xref: /freebsd/sys/dev/aac/aac.c (revision 4b00f859d0ff4542cdd690a3d2b2ee73ed79b9c0)
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  *	$FreeBSD$
3035863739SMike Smith  */
3135863739SMike Smith 
3235863739SMike Smith /*
3335863739SMike Smith  * Driver for the Adaptec 'FSA' family of PCI/SCSI RAID adapters.
3435863739SMike Smith  */
3535863739SMike Smith 
36f6c4dd3fSScott Long #include "opt_aac.h"
37f6c4dd3fSScott Long 
3836e0bf6eSScott Long /* #include <stddef.h> */
3935863739SMike Smith #include <sys/param.h>
4035863739SMike Smith #include <sys/systm.h>
4135863739SMike Smith #include <sys/malloc.h>
4235863739SMike Smith #include <sys/kernel.h>
4336e0bf6eSScott Long #include <sys/kthread.h>
443d04a9d7SScott Long #include <sys/sysctl.h>
45b3457b51SScott Long #include <sys/poll.h>
46891619a6SPoul-Henning Kamp #include <sys/ioccom.h>
4735863739SMike Smith 
4835863739SMike Smith #include <sys/bus.h>
4935863739SMike Smith #include <sys/conf.h>
5035863739SMike Smith #include <sys/signalvar.h>
510b94a66eSMike Smith #include <sys/time.h>
5236e0bf6eSScott Long #include <sys/eventhandler.h>
5335863739SMike Smith 
5435863739SMike Smith #include <machine/bus_memio.h>
5535863739SMike Smith #include <machine/bus.h>
5635863739SMike Smith #include <machine/resource.h>
5735863739SMike Smith 
5835863739SMike Smith #include <dev/aac/aacreg.h>
590b94a66eSMike Smith #include <dev/aac/aac_ioctl.h>
6035863739SMike Smith #include <dev/aac/aacvar.h>
6135863739SMike Smith #include <dev/aac/aac_tables.h>
6235863739SMike Smith 
6335863739SMike Smith static void	aac_startup(void *arg);
64914da7d0SScott Long static void	aac_add_container(struct aac_softc *sc,
65cbfd045bSScott Long 				  struct aac_mntinforesp *mir, int f);
66fe3cb0e1SScott Long static void	aac_get_bus_info(struct aac_softc *sc);
6735863739SMike Smith 
6835863739SMike Smith /* Command Processing */
690b94a66eSMike Smith static void	aac_timeout(struct aac_softc *sc);
7035863739SMike Smith static int	aac_start(struct aac_command *cm);
7135863739SMike Smith static void	aac_complete(void *context, int pending);
7235863739SMike Smith static int	aac_bio_command(struct aac_softc *sc, struct aac_command **cmp);
7335863739SMike Smith static void	aac_bio_complete(struct aac_command *cm);
7435863739SMike Smith static int	aac_wait_command(struct aac_command *cm, int timeout);
7570545d1aSScott Long static void	aac_command_thread(struct aac_softc *sc);
7635863739SMike Smith 
7735863739SMike Smith /* Command Buffer Management */
78c6eafcf2SScott Long static void	aac_map_command_helper(void *arg, bus_dma_segment_t *segs,
79c6eafcf2SScott Long 				       int nseg, int error);
800b94a66eSMike Smith static int	aac_alloc_commands(struct aac_softc *sc);
818480cc63SScott Long static void	aac_free_commands(struct aac_softc *sc);
8235863739SMike Smith static void	aac_map_command(struct aac_command *cm);
8335863739SMike Smith static void	aac_unmap_command(struct aac_command *cm);
8435863739SMike Smith 
8535863739SMike Smith /* Hardware Interface */
86c6eafcf2SScott Long static void	aac_common_map(void *arg, bus_dma_segment_t *segs, int nseg,
87c6eafcf2SScott Long 			       int error);
88fe94b852SScott Long static int	aac_check_firmware(struct aac_softc *sc);
8935863739SMike Smith static int	aac_init(struct aac_softc *sc);
9035863739SMike Smith static int	aac_sync_command(struct aac_softc *sc, u_int32_t command,
91c6eafcf2SScott Long 				 u_int32_t arg0, u_int32_t arg1, u_int32_t arg2,
92c6eafcf2SScott Long 				 u_int32_t arg3, u_int32_t *sp);
93c6eafcf2SScott Long static int	aac_enqueue_fib(struct aac_softc *sc, int queue,
94f6c4dd3fSScott Long 				struct aac_command *cm);
95c6eafcf2SScott Long static int	aac_dequeue_fib(struct aac_softc *sc, int queue,
96914da7d0SScott Long 				u_int32_t *fib_size, struct aac_fib **fib_addr);
9736e0bf6eSScott Long static int	aac_enqueue_response(struct aac_softc *sc, int queue,
9836e0bf6eSScott Long 				     struct aac_fib *fib);
9935863739SMike Smith 
100b3457b51SScott Long /* Falcon/PPC interface */
101b3457b51SScott Long static int	aac_fa_get_fwstatus(struct aac_softc *sc);
102b3457b51SScott Long static void	aac_fa_qnotify(struct aac_softc *sc, int qbit);
103b3457b51SScott Long static int	aac_fa_get_istatus(struct aac_softc *sc);
104b3457b51SScott Long static void	aac_fa_clear_istatus(struct aac_softc *sc, int mask);
105b3457b51SScott Long static void	aac_fa_set_mailbox(struct aac_softc *sc, u_int32_t command,
106b3457b51SScott Long 				   u_int32_t arg0, u_int32_t arg1,
107b3457b51SScott Long 				   u_int32_t arg2, u_int32_t arg3);
108a6d35632SScott Long static int	aac_fa_get_mailbox(struct aac_softc *sc, int mb);
109b3457b51SScott Long static void	aac_fa_set_interrupts(struct aac_softc *sc, int enable);
110b3457b51SScott Long 
111b3457b51SScott Long struct aac_interface aac_fa_interface = {
112b3457b51SScott Long 	aac_fa_get_fwstatus,
113b3457b51SScott Long 	aac_fa_qnotify,
114b3457b51SScott Long 	aac_fa_get_istatus,
115b3457b51SScott Long 	aac_fa_clear_istatus,
116b3457b51SScott Long 	aac_fa_set_mailbox,
117a6d35632SScott Long 	aac_fa_get_mailbox,
118b3457b51SScott Long 	aac_fa_set_interrupts
119b3457b51SScott Long };
120b3457b51SScott Long 
12135863739SMike Smith /* StrongARM interface */
12235863739SMike Smith static int	aac_sa_get_fwstatus(struct aac_softc *sc);
12335863739SMike Smith static void	aac_sa_qnotify(struct aac_softc *sc, int qbit);
12435863739SMike Smith static int	aac_sa_get_istatus(struct aac_softc *sc);
12535863739SMike Smith static void	aac_sa_clear_istatus(struct aac_softc *sc, int mask);
12635863739SMike Smith static void	aac_sa_set_mailbox(struct aac_softc *sc, u_int32_t command,
127c6eafcf2SScott Long 				   u_int32_t arg0, u_int32_t arg1,
128c6eafcf2SScott Long 				   u_int32_t arg2, u_int32_t arg3);
129a6d35632SScott Long static int	aac_sa_get_mailbox(struct aac_softc *sc, int mb);
13035863739SMike Smith static void	aac_sa_set_interrupts(struct aac_softc *sc, int enable);
13135863739SMike Smith 
13235863739SMike Smith struct aac_interface aac_sa_interface = {
13335863739SMike Smith 	aac_sa_get_fwstatus,
13435863739SMike Smith 	aac_sa_qnotify,
13535863739SMike Smith 	aac_sa_get_istatus,
13635863739SMike Smith 	aac_sa_clear_istatus,
13735863739SMike Smith 	aac_sa_set_mailbox,
138a6d35632SScott Long 	aac_sa_get_mailbox,
13935863739SMike Smith 	aac_sa_set_interrupts
14035863739SMike Smith };
14135863739SMike Smith 
14235863739SMike Smith /* i960Rx interface */
14335863739SMike Smith static int	aac_rx_get_fwstatus(struct aac_softc *sc);
14435863739SMike Smith static void	aac_rx_qnotify(struct aac_softc *sc, int qbit);
14535863739SMike Smith static int	aac_rx_get_istatus(struct aac_softc *sc);
14635863739SMike Smith static void	aac_rx_clear_istatus(struct aac_softc *sc, int mask);
14735863739SMike Smith static void	aac_rx_set_mailbox(struct aac_softc *sc, u_int32_t command,
148c6eafcf2SScott Long 				   u_int32_t arg0, u_int32_t arg1,
149c6eafcf2SScott Long 				   u_int32_t arg2, u_int32_t arg3);
150a6d35632SScott Long static int	aac_rx_get_mailbox(struct aac_softc *sc, int mb);
15135863739SMike Smith static void	aac_rx_set_interrupts(struct aac_softc *sc, int enable);
15235863739SMike Smith 
15335863739SMike Smith struct aac_interface aac_rx_interface = {
15435863739SMike Smith 	aac_rx_get_fwstatus,
15535863739SMike Smith 	aac_rx_qnotify,
15635863739SMike Smith 	aac_rx_get_istatus,
15735863739SMike Smith 	aac_rx_clear_istatus,
15835863739SMike Smith 	aac_rx_set_mailbox,
159a6d35632SScott Long 	aac_rx_get_mailbox,
16035863739SMike Smith 	aac_rx_set_interrupts
16135863739SMike Smith };
16235863739SMike Smith 
16335863739SMike Smith /* Debugging and Diagnostics */
16435863739SMike Smith static void	aac_describe_controller(struct aac_softc *sc);
1656965a493SScott Long static char	*aac_describe_code(struct aac_code_lookup *table,
166c6eafcf2SScott Long 				   u_int32_t code);
16735863739SMike Smith 
16835863739SMike Smith /* Management Interface */
16935863739SMike Smith static d_open_t		aac_open;
17035863739SMike Smith static d_close_t	aac_close;
17135863739SMike Smith static d_ioctl_t	aac_ioctl;
172b3457b51SScott Long static d_poll_t		aac_poll;
173c6eafcf2SScott Long static int		aac_ioctl_sendfib(struct aac_softc *sc, caddr_t ufib);
174c6eafcf2SScott Long static void		aac_handle_aif(struct aac_softc *sc,
17536e0bf6eSScott Long 					   struct aac_fib *fib);
176fb0c27d7SScott Long static int		aac_rev_check(struct aac_softc *sc, caddr_t udata);
177fb0c27d7SScott Long static int		aac_getnext_aif(struct aac_softc *sc, caddr_t arg);
178fb0c27d7SScott Long static int		aac_return_aif(struct aac_softc *sc, caddr_t uptr);
17936e0bf6eSScott Long static int		aac_query_disk(struct aac_softc *sc, caddr_t uptr);
18035863739SMike Smith 
18135863739SMike Smith #define AAC_CDEV_MAJOR	150
18235863739SMike Smith 
18335863739SMike Smith static struct cdevsw aac_cdevsw = {
1847ac40f5fSPoul-Henning Kamp 	.d_open =	aac_open,
1857ac40f5fSPoul-Henning Kamp 	.d_close =	aac_close,
1867ac40f5fSPoul-Henning Kamp 	.d_ioctl =	aac_ioctl,
1877ac40f5fSPoul-Henning Kamp 	.d_poll =	aac_poll,
1887ac40f5fSPoul-Henning Kamp 	.d_name =	"aac",
1897ac40f5fSPoul-Henning Kamp 	.d_maj =	AAC_CDEV_MAJOR,
19035863739SMike Smith };
19135863739SMike Smith 
19236e0bf6eSScott Long MALLOC_DEFINE(M_AACBUF, "aacbuf", "Buffers for the AAC driver");
19336e0bf6eSScott Long 
1943d04a9d7SScott Long /* sysctl node */
1953d04a9d7SScott Long SYSCTL_NODE(_hw, OID_AUTO, aac, CTLFLAG_RD, 0, "AAC driver parameters");
1963d04a9d7SScott Long 
197914da7d0SScott Long /*
198914da7d0SScott Long  * Device Interface
199914da7d0SScott Long  */
20035863739SMike Smith 
201914da7d0SScott Long /*
20235863739SMike Smith  * Initialise the controller and softc
20335863739SMike Smith  */
20435863739SMike Smith int
20535863739SMike Smith aac_attach(struct aac_softc *sc)
20635863739SMike Smith {
20735863739SMike Smith 	int error, unit;
20835863739SMike Smith 
20935863739SMike Smith 	debug_called(1);
21035863739SMike Smith 
21135863739SMike Smith 	/*
21235863739SMike Smith 	 * Initialise per-controller queues.
21335863739SMike Smith 	 */
2140b94a66eSMike Smith 	aac_initq_free(sc);
2150b94a66eSMike Smith 	aac_initq_ready(sc);
2160b94a66eSMike Smith 	aac_initq_busy(sc);
2170b94a66eSMike Smith 	aac_initq_bio(sc);
21835863739SMike Smith 
21935863739SMike Smith 	/*
22035863739SMike Smith 	 * Initialise command-completion task.
22135863739SMike Smith 	 */
22235863739SMike Smith 	TASK_INIT(&sc->aac_task_complete, 0, aac_complete, sc);
22335863739SMike Smith 
22435863739SMike Smith 	/* disable interrupts before we enable anything */
22535863739SMike Smith 	AAC_MASK_INTERRUPTS(sc);
22635863739SMike Smith 
22735863739SMike Smith 	/* mark controller as suspended until we get ourselves organised */
22835863739SMike Smith 	sc->aac_state |= AAC_STATE_SUSPEND;
22935863739SMike Smith 
23035863739SMike Smith 	/*
231fe94b852SScott Long 	 * Check that the firmware on the card is supported.
232fe94b852SScott Long 	 */
233fe94b852SScott Long 	if ((error = aac_check_firmware(sc)) != 0)
234fe94b852SScott Long 		return(error);
235fe94b852SScott Long 
236f6b1c44dSScott Long 	/*
237f6b1c44dSScott Long 	 * Initialize locks
238f6b1c44dSScott Long 	 */
239cbfd045bSScott Long 	AAC_LOCK_INIT(&sc->aac_sync_lock, "AAC sync FIB lock");
240f6b1c44dSScott Long 	AAC_LOCK_INIT(&sc->aac_aifq_lock, "AAC AIF lock");
241f6b1c44dSScott Long 	AAC_LOCK_INIT(&sc->aac_io_lock, "AAC I/O lock");
242f6b1c44dSScott Long 	AAC_LOCK_INIT(&sc->aac_container_lock, "AAC container lock");
243f6b1c44dSScott Long 	TAILQ_INIT(&sc->aac_container_tqh);
244f6b1c44dSScott Long 
245cbfd045bSScott Long 
2460b94a66eSMike Smith 	/*
24735863739SMike Smith 	 * Initialise the adapter.
24835863739SMike Smith 	 */
2490b94a66eSMike Smith 	if ((error = aac_init(sc)) != 0)
25035863739SMike Smith 		return(error);
25135863739SMike Smith 
25235863739SMike Smith 	/*
25335863739SMike Smith 	 * Print a little information about the controller.
25435863739SMike Smith 	 */
25535863739SMike Smith 	aac_describe_controller(sc);
25635863739SMike Smith 
25735863739SMike Smith 	/*
258ae543596SScott Long 	 * Register to probe our containers later.
259ae543596SScott Long 	 */
26035863739SMike Smith 	sc->aac_ich.ich_func = aac_startup;
26135863739SMike Smith 	sc->aac_ich.ich_arg = sc;
26235863739SMike Smith 	if (config_intrhook_establish(&sc->aac_ich) != 0) {
263914da7d0SScott Long 		device_printf(sc->aac_dev,
264914da7d0SScott Long 			      "can't establish configuration hook\n");
26535863739SMike Smith 		return(ENXIO);
26635863739SMike Smith 	}
26735863739SMike Smith 
26835863739SMike Smith 	/*
26935863739SMike Smith 	 * Make the control device.
27035863739SMike Smith 	 */
27135863739SMike Smith 	unit = device_get_unit(sc->aac_dev);
2729e9466baSRobert Watson 	sc->aac_dev_t = make_dev(&aac_cdevsw, unit, UID_ROOT, GID_OPERATOR,
2739e9466baSRobert Watson 				 0640, "aac%d", unit);
274157fbb2eSScott Long 	(void)make_dev_alias(sc->aac_dev_t, "afa%d", unit);
2754aa620cdSScott Long 	(void)make_dev_alias(sc->aac_dev_t, "hpn%d", unit);
27635863739SMike Smith 	sc->aac_dev_t->si_drv1 = sc;
27735863739SMike Smith 
27836e0bf6eSScott Long 	/* Create the AIF thread */
27970545d1aSScott Long 	if (kthread_create((void(*)(void *))aac_command_thread, sc,
280316ec49aSScott Long 			   &sc->aifthread, 0, 0, "aac%daif", unit))
28136e0bf6eSScott Long 		panic("Could not create AIF thread\n");
28236e0bf6eSScott Long 
28336e0bf6eSScott Long 	/* Register the shutdown method to only be called post-dump */
2845f54d522SScott Long 	if ((sc->eh = EVENTHANDLER_REGISTER(shutdown_final, aac_shutdown,
2855f54d522SScott Long 	    sc->aac_dev, SHUTDOWN_PRI_DEFAULT)) == NULL)
2865f54d522SScott Long 		device_printf(sc->aac_dev,
2875f54d522SScott Long 			      "shutdown event registration failed\n");
28836e0bf6eSScott Long 
289fe3cb0e1SScott Long 	/* Register with CAM for the non-DASD devices */
290a6d35632SScott Long 	if ((sc->flags & AAC_FLAGS_ENABLE_CAM) != 0) {
29170545d1aSScott Long 		TAILQ_INIT(&sc->aac_sim_tqh);
292fe3cb0e1SScott Long 		aac_get_bus_info(sc);
29370545d1aSScott Long 	}
294fe3cb0e1SScott Long 
29535863739SMike Smith 	return(0);
29635863739SMike Smith }
29735863739SMike Smith 
298914da7d0SScott Long /*
29935863739SMike Smith  * Probe for containers, create disks.
30035863739SMike Smith  */
30135863739SMike Smith static void
30235863739SMike Smith aac_startup(void *arg)
30335863739SMike Smith {
304914da7d0SScott Long 	struct aac_softc *sc;
305cbfd045bSScott Long 	struct aac_fib *fib;
306cbfd045bSScott Long 	struct aac_mntinfo *mi;
307cbfd045bSScott Long 	struct aac_mntinforesp *mir = NULL;
308795d7dc0SScott Long 	int count = 0, i = 0;
30935863739SMike Smith 
31035863739SMike Smith 	debug_called(1);
31135863739SMike Smith 
312914da7d0SScott Long 	sc = (struct aac_softc *)arg;
313914da7d0SScott Long 
31435863739SMike Smith 	/* disconnect ourselves from the intrhook chain */
31535863739SMike Smith 	config_intrhook_disestablish(&sc->aac_ich);
31635863739SMike Smith 
317fe3cb0e1SScott Long 	aac_alloc_sync_fib(sc, &fib, 0);
318cbfd045bSScott Long 	mi = (struct aac_mntinfo *)&fib->data[0];
319cbfd045bSScott Long 
32035863739SMike Smith 	/* loop over possible containers */
32136e0bf6eSScott Long 	do {
32235863739SMike Smith 		/* request information on this container */
32339ee03c3SScott Long 		bzero(mi, sizeof(struct aac_mntinfo));
32439ee03c3SScott Long 		mi->Command = VM_NameServe;
32539ee03c3SScott Long 		mi->MntType = FT_FILESYS;
326cbfd045bSScott Long 		mi->MntCount = i;
327cbfd045bSScott Long 		if (aac_sync_fib(sc, ContainerCommand, 0, fib,
328cbfd045bSScott Long 				 sizeof(struct aac_mntinfo))) {
329795d7dc0SScott Long 			printf("error probing container %d", i);
33035863739SMike Smith 			continue;
33135863739SMike Smith 		}
33235863739SMike Smith 
333cbfd045bSScott Long 		mir = (struct aac_mntinforesp *)&fib->data[0];
334795d7dc0SScott Long 		/* XXX Need to check if count changed */
335795d7dc0SScott Long 		count = mir->MntRespCount;
336cbfd045bSScott Long 		aac_add_container(sc, mir, 0);
33736e0bf6eSScott Long 		i++;
338795d7dc0SScott Long 	} while ((i < count) && (i < AAC_MAX_CONTAINERS));
339cbfd045bSScott Long 
340cbfd045bSScott Long 	aac_release_sync_fib(sc);
34135863739SMike Smith 
34235863739SMike Smith 	/* poke the bus to actually attach the child devices */
34335863739SMike Smith 	if (bus_generic_attach(sc->aac_dev))
34435863739SMike Smith 		device_printf(sc->aac_dev, "bus_generic_attach failed\n");
34535863739SMike Smith 
34635863739SMike Smith 	/* mark the controller up */
34735863739SMike Smith 	sc->aac_state &= ~AAC_STATE_SUSPEND;
34835863739SMike Smith 
34935863739SMike Smith 	/* enable interrupts now */
35035863739SMike Smith 	AAC_UNMASK_INTERRUPTS(sc);
35135863739SMike Smith }
35235863739SMike Smith 
353914da7d0SScott Long /*
354914da7d0SScott Long  * Create a device to respresent a new container
355914da7d0SScott Long  */
356914da7d0SScott Long static void
357cbfd045bSScott Long aac_add_container(struct aac_softc *sc, struct aac_mntinforesp *mir, int f)
358914da7d0SScott Long {
359914da7d0SScott Long 	struct aac_container *co;
360914da7d0SScott Long 	device_t child;
361914da7d0SScott Long 
362914da7d0SScott Long 	/*
363914da7d0SScott Long 	 * Check container volume type for validity.  Note that many of
364914da7d0SScott Long 	 * the possible types may never show up.
365914da7d0SScott Long 	 */
366914da7d0SScott Long 	if ((mir->Status == ST_OK) && (mir->MntTable[0].VolType != CT_NONE)) {
367a761a1caSScott Long 		co = (struct aac_container *)malloc(sizeof *co, M_AACBUF,
368a761a1caSScott Long 		       M_NOWAIT | M_ZERO);
369914da7d0SScott Long 		if (co == NULL)
370914da7d0SScott Long 			panic("Out of memory?!\n");
371914da7d0SScott Long 		debug(1, "id %x  name '%.16s'  size %u  type %d",
372914da7d0SScott Long 		      mir->MntTable[0].ObjectId,
373914da7d0SScott Long 		      mir->MntTable[0].FileSystemName,
374914da7d0SScott Long 		      mir->MntTable[0].Capacity, mir->MntTable[0].VolType);
375914da7d0SScott Long 
376fe3cb0e1SScott Long 		if ((child = device_add_child(sc->aac_dev, "aacd", -1)) == NULL)
377914da7d0SScott Long 			device_printf(sc->aac_dev, "device_add_child failed\n");
378914da7d0SScott Long 		else
379914da7d0SScott Long 			device_set_ivars(child, co);
380914da7d0SScott Long 		device_set_desc(child, aac_describe_code(aac_container_types,
381914da7d0SScott Long 				mir->MntTable[0].VolType));
382914da7d0SScott Long 		co->co_disk = child;
383914da7d0SScott Long 		co->co_found = f;
384914da7d0SScott Long 		bcopy(&mir->MntTable[0], &co->co_mntobj,
385914da7d0SScott Long 		      sizeof(struct aac_mntobj));
386c3d15322SScott Long 		AAC_LOCK_ACQUIRE(&sc->aac_container_lock);
387914da7d0SScott Long 		TAILQ_INSERT_TAIL(&sc->aac_container_tqh, co, co_link);
388914da7d0SScott Long 		AAC_LOCK_RELEASE(&sc->aac_container_lock);
389914da7d0SScott Long 	}
390914da7d0SScott Long }
391914da7d0SScott Long 
392914da7d0SScott Long /*
39335863739SMike Smith  * Free all of the resources associated with (sc)
39435863739SMike Smith  *
39535863739SMike Smith  * Should not be called if the controller is active.
39635863739SMike Smith  */
39735863739SMike Smith void
39835863739SMike Smith aac_free(struct aac_softc *sc)
39935863739SMike Smith {
400ffb37f33SScott Long 
40135863739SMike Smith 	debug_called(1);
40235863739SMike Smith 
40335863739SMike Smith 	/* remove the control device */
40435863739SMike Smith 	if (sc->aac_dev_t != NULL)
40535863739SMike Smith 		destroy_dev(sc->aac_dev_t);
40635863739SMike Smith 
4070b94a66eSMike Smith 	/* throw away any FIB buffers, discard the FIB DMA tag */
4088480cc63SScott Long 	aac_free_commands(sc);
4090b94a66eSMike Smith 	if (sc->aac_fib_dmat)
4100b94a66eSMike Smith 		bus_dma_tag_destroy(sc->aac_fib_dmat);
41135863739SMike Smith 
412ffb37f33SScott Long 	free(sc->aac_commands, M_AACBUF);
413ffb37f33SScott Long 
41435863739SMike Smith 	/* destroy the common area */
41535863739SMike Smith 	if (sc->aac_common) {
41635863739SMike Smith 		bus_dmamap_unload(sc->aac_common_dmat, sc->aac_common_dmamap);
417c6eafcf2SScott Long 		bus_dmamem_free(sc->aac_common_dmat, sc->aac_common,
418c6eafcf2SScott Long 				sc->aac_common_dmamap);
41935863739SMike Smith 	}
4200b94a66eSMike Smith 	if (sc->aac_common_dmat)
4210b94a66eSMike Smith 		bus_dma_tag_destroy(sc->aac_common_dmat);
42235863739SMike Smith 
42335863739SMike Smith 	/* disconnect the interrupt handler */
42435863739SMike Smith 	if (sc->aac_intr)
42535863739SMike Smith 		bus_teardown_intr(sc->aac_dev, sc->aac_irq, sc->aac_intr);
42635863739SMike Smith 	if (sc->aac_irq != NULL)
427c6eafcf2SScott Long 		bus_release_resource(sc->aac_dev, SYS_RES_IRQ, sc->aac_irq_rid,
428c6eafcf2SScott Long 				     sc->aac_irq);
42935863739SMike Smith 
43035863739SMike Smith 	/* destroy data-transfer DMA tag */
43135863739SMike Smith 	if (sc->aac_buffer_dmat)
43235863739SMike Smith 		bus_dma_tag_destroy(sc->aac_buffer_dmat);
43335863739SMike Smith 
43435863739SMike Smith 	/* destroy the parent DMA tag */
43535863739SMike Smith 	if (sc->aac_parent_dmat)
43635863739SMike Smith 		bus_dma_tag_destroy(sc->aac_parent_dmat);
43735863739SMike Smith 
43835863739SMike Smith 	/* release the register window mapping */
43935863739SMike Smith 	if (sc->aac_regs_resource != NULL)
440914da7d0SScott Long 		bus_release_resource(sc->aac_dev, SYS_RES_MEMORY,
441914da7d0SScott Long 				     sc->aac_regs_rid, sc->aac_regs_resource);
44235863739SMike Smith }
44335863739SMike Smith 
444914da7d0SScott Long /*
44535863739SMike Smith  * Disconnect from the controller completely, in preparation for unload.
44635863739SMike Smith  */
44735863739SMike Smith int
44835863739SMike Smith aac_detach(device_t dev)
44935863739SMike Smith {
450914da7d0SScott Long 	struct aac_softc *sc;
45170545d1aSScott Long 	struct aac_container *co;
45270545d1aSScott Long 	struct aac_sim	*sim;
45335863739SMike Smith 	int error;
45435863739SMike Smith 
45535863739SMike Smith 	debug_called(1);
45635863739SMike Smith 
457914da7d0SScott Long 	sc = device_get_softc(dev);
458914da7d0SScott Long 
45935863739SMike Smith 	if (sc->aac_state & AAC_STATE_OPEN)
46035863739SMike Smith 		return(EBUSY);
46135863739SMike Smith 
46270545d1aSScott Long 	/* Remove the child containers */
463a761a1caSScott Long 	while ((co = TAILQ_FIRST(&sc->aac_container_tqh)) != NULL) {
46470545d1aSScott Long 		error = device_delete_child(dev, co->co_disk);
46570545d1aSScott Long 		if (error)
46670545d1aSScott Long 			return (error);
46765ac4ed6SScott Long 		TAILQ_REMOVE(&sc->aac_container_tqh, co, co_link);
468a761a1caSScott Long 		free(co, M_AACBUF);
46970545d1aSScott Long 	}
47070545d1aSScott Long 
47170545d1aSScott Long 	/* Remove the CAM SIMs */
472a761a1caSScott Long 	while ((sim = TAILQ_FIRST(&sc->aac_sim_tqh)) != NULL) {
473a761a1caSScott Long 		TAILQ_REMOVE(&sc->aac_sim_tqh, sim, sim_link);
47470545d1aSScott Long 		error = device_delete_child(dev, sim->sim_dev);
47570545d1aSScott Long 		if (error)
47670545d1aSScott Long 			return (error);
477a761a1caSScott Long 		free(sim, M_AACBUF);
47870545d1aSScott Long 	}
47970545d1aSScott Long 
48036e0bf6eSScott Long 	if (sc->aifflags & AAC_AIFFLAGS_RUNNING) {
48136e0bf6eSScott Long 		sc->aifflags |= AAC_AIFFLAGS_EXIT;
48236e0bf6eSScott Long 		wakeup(sc->aifthread);
48336e0bf6eSScott Long 		tsleep(sc->aac_dev, PUSER | PCATCH, "aacdch", 30 * hz);
48436e0bf6eSScott Long 	}
48536e0bf6eSScott Long 
48636e0bf6eSScott Long 	if (sc->aifflags & AAC_AIFFLAGS_RUNNING)
48736e0bf6eSScott Long 		panic("Cannot shutdown AIF thread\n");
48836e0bf6eSScott Long 
48935863739SMike Smith 	if ((error = aac_shutdown(dev)))
49035863739SMike Smith 		return(error);
49135863739SMike Smith 
4925f54d522SScott Long 	EVENTHANDLER_DEREGISTER(shutdown_final, sc->eh);
4935f54d522SScott Long 
49435863739SMike Smith 	aac_free(sc);
49535863739SMike Smith 
49635863739SMike Smith 	return(0);
49735863739SMike Smith }
49835863739SMike Smith 
499914da7d0SScott Long /*
50035863739SMike Smith  * Bring the controller down to a dormant state and detach all child devices.
50135863739SMike Smith  *
50235863739SMike Smith  * This function is called before detach or system shutdown.
50335863739SMike Smith  *
5040b94a66eSMike Smith  * Note that we can assume that the bioq on the controller is empty, as we won't
50535863739SMike Smith  * allow shutdown if any device is open.
50635863739SMike Smith  */
50735863739SMike Smith int
50835863739SMike Smith aac_shutdown(device_t dev)
50935863739SMike Smith {
510914da7d0SScott Long 	struct aac_softc *sc;
511cbfd045bSScott Long 	struct aac_fib *fib;
512cbfd045bSScott Long 	struct aac_close_command *cc;
51335863739SMike Smith 
51435863739SMike Smith 	debug_called(1);
51535863739SMike Smith 
516914da7d0SScott Long 	sc = device_get_softc(dev);
517914da7d0SScott Long 
51835863739SMike Smith 	sc->aac_state |= AAC_STATE_SUSPEND;
51935863739SMike Smith 
52035863739SMike Smith 	/*
52135863739SMike Smith 	 * Send a Container shutdown followed by a HostShutdown FIB to the
52235863739SMike Smith 	 * controller to convince it that we don't want to talk to it anymore.
52335863739SMike Smith 	 * We've been closed and all I/O completed already
52435863739SMike Smith 	 */
52535863739SMike Smith 	device_printf(sc->aac_dev, "shutting down controller...");
52635863739SMike Smith 
527fe3cb0e1SScott Long 	aac_alloc_sync_fib(sc, &fib, AAC_SYNC_LOCK_FORCE);
528cbfd045bSScott Long 	cc = (struct aac_close_command *)&fib->data[0];
529cbfd045bSScott Long 
53039ee03c3SScott Long 	bzero(cc, sizeof(struct aac_close_command));
531cbfd045bSScott Long 	cc->Command = VM_CloseAll;
532cbfd045bSScott Long 	cc->ContainerId = 0xffffffff;
533cbfd045bSScott Long 	if (aac_sync_fib(sc, ContainerCommand, 0, fib,
534cbfd045bSScott Long 	    sizeof(struct aac_close_command)))
53535863739SMike Smith 		printf("FAILED.\n");
53670545d1aSScott Long 	else
53770545d1aSScott Long 		printf("done\n");
53870545d1aSScott Long #if 0
539914da7d0SScott Long 	else {
540cbfd045bSScott Long 		fib->data[0] = 0;
54136e0bf6eSScott Long 		/*
542914da7d0SScott Long 		 * XXX Issuing this command to the controller makes it shut down
54336e0bf6eSScott Long 		 * but also keeps it from coming back up without a reset of the
54436e0bf6eSScott Long 		 * PCI bus.  This is not desirable if you are just unloading the
54536e0bf6eSScott Long 		 * driver module with the intent to reload it later.
54636e0bf6eSScott Long 		 */
547cbfd045bSScott Long 		if (aac_sync_fib(sc, FsaHostShutdown, AAC_FIBSTATE_SHUTDOWN,
548cbfd045bSScott Long 		    fib, 1)) {
54935863739SMike Smith 			printf("FAILED.\n");
55035863739SMike Smith 		} else {
55135863739SMike Smith 			printf("done.\n");
55235863739SMike Smith 		}
55335863739SMike Smith 	}
55470545d1aSScott Long #endif
55535863739SMike Smith 
55635863739SMike Smith 	AAC_MASK_INTERRUPTS(sc);
55735863739SMike Smith 
55835863739SMike Smith 	return(0);
55935863739SMike Smith }
56035863739SMike Smith 
561914da7d0SScott Long /*
56235863739SMike Smith  * Bring the controller to a quiescent state, ready for system suspend.
56335863739SMike Smith  */
56435863739SMike Smith int
56535863739SMike Smith aac_suspend(device_t dev)
56635863739SMike Smith {
567914da7d0SScott Long 	struct aac_softc *sc;
56835863739SMike Smith 
56935863739SMike Smith 	debug_called(1);
570914da7d0SScott Long 
571914da7d0SScott Long 	sc = device_get_softc(dev);
572914da7d0SScott Long 
57335863739SMike Smith 	sc->aac_state |= AAC_STATE_SUSPEND;
57435863739SMike Smith 
57535863739SMike Smith 	AAC_MASK_INTERRUPTS(sc);
57635863739SMike Smith 	return(0);
57735863739SMike Smith }
57835863739SMike Smith 
579914da7d0SScott Long /*
58035863739SMike Smith  * Bring the controller back to a state ready for operation.
58135863739SMike Smith  */
58235863739SMike Smith int
58335863739SMike Smith aac_resume(device_t dev)
58435863739SMike Smith {
585914da7d0SScott Long 	struct aac_softc *sc;
58635863739SMike Smith 
58735863739SMike Smith 	debug_called(1);
588914da7d0SScott Long 
589914da7d0SScott Long 	sc = device_get_softc(dev);
590914da7d0SScott Long 
59135863739SMike Smith 	sc->aac_state &= ~AAC_STATE_SUSPEND;
59235863739SMike Smith 	AAC_UNMASK_INTERRUPTS(sc);
59335863739SMike Smith 	return(0);
59435863739SMike Smith }
59535863739SMike Smith 
596914da7d0SScott Long /*
59735863739SMike Smith  * Take an interrupt.
59835863739SMike Smith  */
59935863739SMike Smith void
60035863739SMike Smith aac_intr(void *arg)
60135863739SMike Smith {
602914da7d0SScott Long 	struct aac_softc *sc;
603f30ac74cSScott Long 	u_int32_t *resp_queue;
60470545d1aSScott Long 	u_int16_t reason;
60535863739SMike Smith 
60635863739SMike Smith 	debug_called(2);
60735863739SMike Smith 
608914da7d0SScott Long 	sc = (struct aac_softc *)arg;
609914da7d0SScott Long 
610f30ac74cSScott Long 	/*
611f30ac74cSScott Long 	 * Optimize the common case of adapter response interrupts.
612f30ac74cSScott Long 	 * We must read from the card prior to processing the responses
613f30ac74cSScott Long 	 * to ensure the clear is flushed prior to accessing the queues.
614f30ac74cSScott Long 	 * Reading the queues from local memory might save us a PCI read.
615f30ac74cSScott Long 	 */
616f30ac74cSScott Long 	resp_queue = sc->aac_queues->qt_qindex[AAC_HOST_NORM_RESP_QUEUE];
617f30ac74cSScott Long 	if (resp_queue[AAC_PRODUCER_INDEX] != resp_queue[AAC_CONSUMER_INDEX])
618f30ac74cSScott Long 		reason = AAC_DB_RESPONSE_READY;
619f30ac74cSScott Long 	else
62035863739SMike Smith 		reason = AAC_GET_ISTATUS(sc);
621f30ac74cSScott Long 	AAC_CLEAR_ISTATUS(sc, reason);
622f30ac74cSScott Long 	(void)AAC_GET_ISTATUS(sc);
623f30ac74cSScott Long 
624f30ac74cSScott Long 	/* It's not ok to return here because of races with the previous step */
625f30ac74cSScott Long 	if (reason & AAC_DB_RESPONSE_READY)
6269c3a7fceSScott Long 		/* handle completion processing */
627ae543596SScott Long 		taskqueue_enqueue(taskqueue_swi, &sc->aac_task_complete);
62835863739SMike Smith 
629b3457b51SScott Long 	/* controller wants to talk to the log */
63070545d1aSScott Long 	if (reason & AAC_DB_PRINTF) {
63170545d1aSScott Long 		if (sc->aifflags & AAC_AIFFLAGS_RUNNING) {
63270545d1aSScott Long 			sc->aifflags |= AAC_AIFFLAGS_PRINTF;
63370545d1aSScott Long 		} else
63436e0bf6eSScott Long 			aac_print_printf(sc);
63570545d1aSScott Long 	}
63635863739SMike Smith 
63735863739SMike Smith 	/* controller has a message for us? */
63835863739SMike Smith 	if (reason & AAC_DB_COMMAND_READY) {
63936e0bf6eSScott Long 		if (sc->aifflags & AAC_AIFFLAGS_RUNNING) {
64070545d1aSScott Long 			sc->aifflags |= AAC_AIFFLAGS_AIF;
64170545d1aSScott Long 		} else {
64270545d1aSScott Long 			/*
64370545d1aSScott Long 			 * XXX If the kthread is dead and we're at this point,
64470545d1aSScott Long 			 * there are bigger problems than just figuring out
64570545d1aSScott Long 			 * what to do with an AIF.
64670545d1aSScott Long 			 */
64770545d1aSScott Long 		}
64870545d1aSScott Long 
64970545d1aSScott Long 	}
65070545d1aSScott Long 
65170545d1aSScott Long 	if ((sc->aifflags & AAC_AIFFLAGS_PENDING) != 0)
65270545d1aSScott Long 		/* XXX Should this be done with cv_signal? */
65336e0bf6eSScott Long 		wakeup(sc->aifthread);
65436e0bf6eSScott Long }
65535863739SMike Smith 
656c6eafcf2SScott Long /*
657914da7d0SScott Long  * Command Processing
658914da7d0SScott Long  */
65935863739SMike Smith 
660914da7d0SScott Long /*
66135863739SMike Smith  * Start as much queued I/O as possible on the controller
66235863739SMike Smith  */
663fe3cb0e1SScott Long void
66435863739SMike Smith aac_startio(struct aac_softc *sc)
66535863739SMike Smith {
66635863739SMike Smith 	struct aac_command *cm;
66735863739SMike Smith 
66835863739SMike Smith 	debug_called(2);
66935863739SMike Smith 
67035863739SMike Smith 	for (;;) {
671914da7d0SScott Long 		/*
672914da7d0SScott Long 		 * Try to get a command that's been put off for lack of
673914da7d0SScott Long 		 * resources
674914da7d0SScott Long 		 */
67535863739SMike Smith 		cm = aac_dequeue_ready(sc);
67635863739SMike Smith 
677914da7d0SScott Long 		/*
678914da7d0SScott Long 		 * Try to build a command off the bio queue (ignore error
679914da7d0SScott Long 		 * return)
680914da7d0SScott Long 		 */
6810b94a66eSMike Smith 		if (cm == NULL)
68235863739SMike Smith 			aac_bio_command(sc, &cm);
68335863739SMike Smith 
68435863739SMike Smith 		/* nothing to do? */
68535863739SMike Smith 		if (cm == NULL)
68635863739SMike Smith 			break;
68735863739SMike Smith 
68835863739SMike Smith 		/* try to give the command to the controller */
68935863739SMike Smith 		if (aac_start(cm) == EBUSY) {
69035863739SMike Smith 			/* put it on the ready queue for later */
69135863739SMike Smith 			aac_requeue_ready(cm);
69235863739SMike Smith 			break;
69335863739SMike Smith 		}
69435863739SMike Smith 	}
69535863739SMike Smith }
69635863739SMike Smith 
697914da7d0SScott Long /*
69835863739SMike Smith  * Deliver a command to the controller; allocate controller resources at the
69935863739SMike Smith  * last moment when possible.
70035863739SMike Smith  */
70135863739SMike Smith static int
70235863739SMike Smith aac_start(struct aac_command *cm)
70335863739SMike Smith {
704914da7d0SScott Long 	struct aac_softc *sc;
705ed5c5fb4SMike Smith 	int error;
70635863739SMike Smith 
70735863739SMike Smith 	debug_called(2);
70835863739SMike Smith 
709914da7d0SScott Long 	sc = cm->cm_sc;
710914da7d0SScott Long 
71135863739SMike Smith 	/* get the command mapped */
71235863739SMike Smith 	aac_map_command(cm);
71335863739SMike Smith 
714149af931SScott Long 	/* Fix up the address values in the FIB.  Use the command array index
715149af931SScott Long 	 * instead of a pointer since these fields are only 32 bits.  Shift
716149af931SScott Long 	 * the SenderFibAddress over to make room for the fast response bit.
717149af931SScott Long 	 */
718149af931SScott Long 	cm->cm_fib->Header.SenderFibAddress = (cm->cm_index << 1);
71935863739SMike Smith 	cm->cm_fib->Header.ReceiverFibAddress = cm->cm_fibphys;
72035863739SMike Smith 
72135863739SMike Smith 	/* save a pointer to the command for speedy reverse-lookup */
722cb0d64b9SScott Long 	cm->cm_fib->Header.SenderData = cm->cm_index;
72335863739SMike Smith 	/* put the FIB on the outbound queue */
72436e0bf6eSScott Long 	error = aac_enqueue_fib(sc, cm->cm_queue, cm);
7250b94a66eSMike Smith 	return(error);
72635863739SMike Smith }
72735863739SMike Smith 
728914da7d0SScott Long /*
72935863739SMike Smith  * Handle notification of one or more FIBs coming from the controller.
73035863739SMike Smith  */
73135863739SMike Smith static void
73270545d1aSScott Long aac_command_thread(struct aac_softc *sc)
73335863739SMike Smith {
73435863739SMike Smith 	struct aac_fib *fib;
73535863739SMike Smith 	u_int32_t fib_size;
73636e0bf6eSScott Long 	int size;
73735863739SMike Smith 
73836e0bf6eSScott Long 	debug_called(2);
73935863739SMike Smith 
74036e0bf6eSScott Long 	sc->aifflags |= AAC_AIFFLAGS_RUNNING;
74136e0bf6eSScott Long 
74236e0bf6eSScott Long 	while (!(sc->aifflags & AAC_AIFFLAGS_EXIT)) {
74370545d1aSScott Long 		if ((sc->aifflags & AAC_AIFFLAGS_PENDING) == 0)
74470545d1aSScott Long 			tsleep(sc->aifthread, PRIBIO, "aifthd",
74570545d1aSScott Long 			       AAC_PERIODIC_INTERVAL * hz);
74636e0bf6eSScott Long 
74770545d1aSScott Long 		if ((sc->aifflags & AAC_AIFFLAGS_PENDING) == 0)
74870545d1aSScott Long 			aac_timeout(sc);
74970545d1aSScott Long 
75070545d1aSScott Long 		/* Check the hardware printf message buffer */
75170545d1aSScott Long 		if ((sc->aifflags & AAC_AIFFLAGS_PRINTF) != 0) {
75270545d1aSScott Long 			sc->aifflags &= ~AAC_AIFFLAGS_PRINTF;
75370545d1aSScott Long 			aac_print_printf(sc);
75470545d1aSScott Long 		}
75570545d1aSScott Long 
756ae543596SScott Long 		/* See if any FIBs need to be allocated */
757ae543596SScott Long 		if ((sc->aifflags & AAC_AIFFLAGS_ALLOCFIBS) != 0) {
758ae543596SScott Long 			AAC_LOCK_ACQUIRE(&sc->aac_io_lock);
759ae543596SScott Long 			aac_alloc_commands(sc);
760ae543596SScott Long 			sc->aifflags &= ~AAC_AIFFLAGS_ALLOCFIBS;
761ae543596SScott Long 			AAC_LOCK_RELEASE(&sc->aac_io_lock);
762ae543596SScott Long 		}
76370545d1aSScott Long 
764ae543596SScott Long 		/* While we're here, check to see if any commands are stuck */
765ae543596SScott Long 		while (sc->aifflags & AAC_AIFFLAGS_AIF) {
766914da7d0SScott Long 			if (aac_dequeue_fib(sc, AAC_HOST_NORM_CMD_QUEUE,
76770545d1aSScott Long 					    &fib_size, &fib)) {
76870545d1aSScott Long 				sc->aifflags &= ~AAC_AIFFLAGS_AIF;
76935863739SMike Smith 				break;	/* nothing to do */
77070545d1aSScott Long 			}
77135863739SMike Smith 
77236e0bf6eSScott Long 			AAC_PRINT_FIB(sc, fib);
77336e0bf6eSScott Long 
77435863739SMike Smith 			switch (fib->Header.Command) {
77535863739SMike Smith 			case AifRequest:
77636e0bf6eSScott Long 				aac_handle_aif(sc, fib);
77735863739SMike Smith 				break;
77835863739SMike Smith 			default:
779914da7d0SScott Long 				device_printf(sc->aac_dev, "unknown command "
780914da7d0SScott Long 					      "from controller\n");
78135863739SMike Smith 				break;
78235863739SMike Smith 			}
78335863739SMike Smith 
78436e0bf6eSScott Long 			if ((fib->Header.XferState == 0) ||
78536e0bf6eSScott Long 			    (fib->Header.StructType != AAC_FIBTYPE_TFIB))
78636e0bf6eSScott Long 				break;
78736e0bf6eSScott Long 
78870545d1aSScott Long 			/* Return the AIF to the controller. */
78936e0bf6eSScott Long 			if (fib->Header.XferState & AAC_FIBSTATE_FROMADAP) {
79036e0bf6eSScott Long 				fib->Header.XferState |= AAC_FIBSTATE_DONEHOST;
79136e0bf6eSScott Long 				*(AAC_FSAStatus*)fib->data = ST_OK;
79236e0bf6eSScott Long 
79336e0bf6eSScott Long 				/* XXX Compute the Size field? */
79436e0bf6eSScott Long 				size = fib->Header.Size;
79536e0bf6eSScott Long 				if (size > sizeof(struct aac_fib)) {
79636e0bf6eSScott Long 					size = sizeof(struct aac_fib);
79736e0bf6eSScott Long 					fib->Header.Size = size;
79836e0bf6eSScott Long 				}
79936e0bf6eSScott Long 				/*
800914da7d0SScott Long 				 * Since we did not generate this command, it
801914da7d0SScott Long 				 * cannot go through the normal
802914da7d0SScott Long 				 * enqueue->startio chain.
80336e0bf6eSScott Long 				 */
804914da7d0SScott Long 				aac_enqueue_response(sc,
805914da7d0SScott Long 						     AAC_ADAP_NORM_RESP_QUEUE,
806914da7d0SScott Long 						     fib);
80736e0bf6eSScott Long 			}
80836e0bf6eSScott Long 		}
80936e0bf6eSScott Long 	}
81036e0bf6eSScott Long 	sc->aifflags &= ~AAC_AIFFLAGS_RUNNING;
81136e0bf6eSScott Long 	wakeup(sc->aac_dev);
81236e0bf6eSScott Long 
81336e0bf6eSScott Long 	mtx_lock(&Giant);
81436e0bf6eSScott Long 	kthread_exit(0);
81535863739SMike Smith }
81635863739SMike Smith 
817914da7d0SScott Long /*
8189c3a7fceSScott Long  * Process completed commands.
81935863739SMike Smith  */
82035863739SMike Smith static void
8219c3a7fceSScott Long aac_complete(void *context, int pending)
82235863739SMike Smith {
8239c3a7fceSScott Long 	struct aac_softc *sc;
82435863739SMike Smith 	struct aac_command *cm;
82535863739SMike Smith 	struct aac_fib *fib;
82635863739SMike Smith 	u_int32_t fib_size;
82735863739SMike Smith 
82835863739SMike Smith 	debug_called(2);
82935863739SMike Smith 
8309c3a7fceSScott Long 	sc = (struct aac_softc *)context;
8319c3a7fceSScott Long 
832ae543596SScott Long 	AAC_LOCK_ACQUIRE(&sc->aac_io_lock);
833ae543596SScott Long 
8349c3a7fceSScott Long 	/* pull completed commands off the queue */
83535863739SMike Smith 	for (;;) {
83635863739SMike Smith 		/* look for completed FIBs on our queue */
837914da7d0SScott Long 		if (aac_dequeue_fib(sc, AAC_HOST_NORM_RESP_QUEUE, &fib_size,
838914da7d0SScott Long 				    &fib))
83935863739SMike Smith 			break;	/* nothing to do */
84035863739SMike Smith 
84135863739SMike Smith 		/* get the command, unmap and queue for later processing */
842cb0d64b9SScott Long 		cm = sc->aac_commands + fib->Header.SenderData;
84335863739SMike Smith 		if (cm == NULL) {
84435863739SMike Smith 			AAC_PRINT_FIB(sc, fib);
8459c3a7fceSScott Long 			break;
8469c3a7fceSScott Long 		}
8479c3a7fceSScott Long 
8480b94a66eSMike Smith 		aac_remove_busy(cm);
84935863739SMike Smith 		aac_unmap_command(cm);		/* XXX defer? */
85035863739SMike Smith 		cm->cm_flags |= AAC_CMD_COMPLETED;
85135863739SMike Smith 
85235863739SMike Smith 		/* is there a completion handler? */
85335863739SMike Smith 		if (cm->cm_complete != NULL) {
85435863739SMike Smith 			cm->cm_complete(cm);
85535863739SMike Smith 		} else {
85635863739SMike Smith 			/* assume that someone is sleeping on this command */
85735863739SMike Smith 			wakeup(cm);
85835863739SMike Smith 		}
85935863739SMike Smith 	}
8600b94a66eSMike Smith 
8610b94a66eSMike Smith 	/* see if we can start some more I/O */
8620b94a66eSMike Smith 	aac_startio(sc);
863ae543596SScott Long 
864ae543596SScott Long 	AAC_LOCK_RELEASE(&sc->aac_io_lock);
86535863739SMike Smith }
86635863739SMike Smith 
867914da7d0SScott Long /*
86835863739SMike Smith  * Handle a bio submitted from a disk device.
86935863739SMike Smith  */
87035863739SMike Smith void
87135863739SMike Smith aac_submit_bio(struct bio *bp)
87235863739SMike Smith {
873914da7d0SScott Long 	struct aac_disk *ad;
874914da7d0SScott Long 	struct aac_softc *sc;
87535863739SMike Smith 
87635863739SMike Smith 	debug_called(2);
87735863739SMike Smith 
8787540e65eSScott Long 	ad = (struct aac_disk *)bp->bio_disk->d_drv1;
879914da7d0SScott Long 	sc = ad->ad_controller;
880914da7d0SScott Long 
88135863739SMike Smith 	/* queue the BIO and try to get some work done */
8820b94a66eSMike Smith 	aac_enqueue_bio(sc, bp);
88335863739SMike Smith 	aac_startio(sc);
88435863739SMike Smith }
88535863739SMike Smith 
886914da7d0SScott Long /*
88735863739SMike Smith  * Get a bio and build a command to go with it.
88835863739SMike Smith  */
88935863739SMike Smith static int
89035863739SMike Smith aac_bio_command(struct aac_softc *sc, struct aac_command **cmp)
89135863739SMike Smith {
89235863739SMike Smith 	struct aac_command *cm;
89335863739SMike Smith 	struct aac_fib *fib;
89435863739SMike Smith 	struct aac_disk *ad;
89535863739SMike Smith 	struct bio *bp;
89635863739SMike Smith 
89735863739SMike Smith 	debug_called(2);
89835863739SMike Smith 
89935863739SMike Smith 	/* get the resources we will need */
90035863739SMike Smith 	cm = NULL;
9010b94a66eSMike Smith 	if ((bp = aac_dequeue_bio(sc)) == NULL)
90235863739SMike Smith 		goto fail;
90335863739SMike Smith 	if (aac_alloc_command(sc, &cm))	/* get a command */
90435863739SMike Smith 		goto fail;
90535863739SMike Smith 
90635863739SMike Smith 	/* fill out the command */
9070b94a66eSMike Smith 	cm->cm_data = (void *)bp->bio_data;
9080b94a66eSMike Smith 	cm->cm_datalen = bp->bio_bcount;
9090b94a66eSMike Smith 	cm->cm_complete = aac_bio_complete;
91035863739SMike Smith 	cm->cm_private = bp;
9110b94a66eSMike Smith 	cm->cm_timestamp = time_second;
91236e0bf6eSScott Long 	cm->cm_queue = AAC_ADAP_NORM_CMD_QUEUE;
91335863739SMike Smith 
91435863739SMike Smith 	/* build the FIB */
91535863739SMike Smith 	fib = cm->cm_fib;
916b85f5808SScott Long 	fib->Header.Size = sizeof(struct aac_fib_header);
91735863739SMike Smith 	fib->Header.XferState =
91835863739SMike Smith 		AAC_FIBSTATE_HOSTOWNED   |
91935863739SMike Smith 		AAC_FIBSTATE_INITIALISED |
920f30ac74cSScott Long 		AAC_FIBSTATE_EMPTY	 |
92135863739SMike Smith 		AAC_FIBSTATE_FROMHOST	 |
92235863739SMike Smith 		AAC_FIBSTATE_REXPECTED   |
923f30ac74cSScott Long 		AAC_FIBSTATE_NORM	 |
924f30ac74cSScott Long 		AAC_FIBSTATE_ASYNC	 |
925f30ac74cSScott Long 		AAC_FIBSTATE_FAST_RESPONSE;
92635863739SMike Smith 
92735863739SMike Smith 	/* build the read/write request */
9287540e65eSScott Long 	ad = (struct aac_disk *)bp->bio_disk->d_drv1;
929b85f5808SScott Long 
930b85f5808SScott Long 	if ((sc->flags & AAC_FLAGS_SG_64BIT) == 0) {
931b85f5808SScott Long 		fib->Header.Command = ContainerCommand;
9329e2e96d8SScott Long 		if (bp->bio_cmd == BIO_READ) {
933b85f5808SScott Long 			struct aac_blockread *br;
93435863739SMike Smith 			br = (struct aac_blockread *)&fib->data[0];
93535863739SMike Smith 			br->Command = VM_CtBlockRead;
93635863739SMike Smith 			br->ContainerId = ad->ad_container->co_mntobj.ObjectId;
93735863739SMike Smith 			br->BlockNumber = bp->bio_pblkno;
93835863739SMike Smith 			br->ByteCount = bp->bio_bcount;
93935863739SMike Smith 			fib->Header.Size += sizeof(struct aac_blockread);
94035863739SMike Smith 			cm->cm_sgtable = &br->SgMap;
94135863739SMike Smith 			cm->cm_flags |= AAC_CMD_DATAIN;
94235863739SMike Smith 		} else {
943b85f5808SScott Long 			struct aac_blockwrite *bw;
94435863739SMike Smith 			bw = (struct aac_blockwrite *)&fib->data[0];
94535863739SMike Smith 			bw->Command = VM_CtBlockWrite;
94635863739SMike Smith 			bw->ContainerId = ad->ad_container->co_mntobj.ObjectId;
94735863739SMike Smith 			bw->BlockNumber = bp->bio_pblkno;
94835863739SMike Smith 			bw->ByteCount = bp->bio_bcount;
949b85f5808SScott Long 			bw->Stable = CUNSTABLE;
95035863739SMike Smith 			fib->Header.Size += sizeof(struct aac_blockwrite);
95135863739SMike Smith 			cm->cm_flags |= AAC_CMD_DATAOUT;
95235863739SMike Smith 			cm->cm_sgtable = &bw->SgMap;
95335863739SMike Smith 		}
954b85f5808SScott Long 	} else {
955b85f5808SScott Long 		fib->Header.Command = ContainerCommand64;
956b85f5808SScott Long 		if (bp->bio_cmd == BIO_READ) {
957b85f5808SScott Long 			struct aac_blockread64 *br;
958b85f5808SScott Long 			br = (struct aac_blockread64 *)&fib->data[0];
959b85f5808SScott Long 			br->Command = VM_CtHostRead64;
960b85f5808SScott Long 			br->ContainerId = ad->ad_container->co_mntobj.ObjectId;
961b85f5808SScott Long 			br->SectorCount = bp->bio_bcount / AAC_BLOCK_SIZE;
962b85f5808SScott Long 			br->BlockNumber = bp->bio_pblkno;
963b85f5808SScott Long 			br->Pad = 0;
964b85f5808SScott Long 			br->Flags = 0;
965b85f5808SScott Long 			fib->Header.Size += sizeof(struct aac_blockread64);
966b85f5808SScott Long 			cm->cm_flags |= AAC_CMD_DATAOUT;
967b85f5808SScott Long 			(struct aac_sg_table64 *)cm->cm_sgtable = &br->SgMap64;
968b85f5808SScott Long 		} else {
969b85f5808SScott Long 			struct aac_blockwrite64 *bw;
970b85f5808SScott Long 			bw = (struct aac_blockwrite64 *)&fib->data[0];
971b85f5808SScott Long 			bw->Command = VM_CtHostWrite64;
972b85f5808SScott Long 			bw->ContainerId = ad->ad_container->co_mntobj.ObjectId;
973b85f5808SScott Long 			bw->SectorCount = bp->bio_bcount / AAC_BLOCK_SIZE;
974b85f5808SScott Long 			bw->BlockNumber = bp->bio_pblkno;
975b85f5808SScott Long 			bw->Pad = 0;
976b85f5808SScott Long 			bw->Flags = 0;
977b85f5808SScott Long 			fib->Header.Size += sizeof(struct aac_blockwrite64);
978b85f5808SScott Long 			cm->cm_flags |= AAC_CMD_DATAIN;
979b85f5808SScott Long 			(struct aac_sg_table64 *)cm->cm_sgtable = &bw->SgMap64;
980b85f5808SScott Long 		}
981b85f5808SScott Long 	}
98235863739SMike Smith 
98335863739SMike Smith 	*cmp = cm;
98435863739SMike Smith 	return(0);
98535863739SMike Smith 
98635863739SMike Smith fail:
98735863739SMike Smith 	if (bp != NULL)
9880b94a66eSMike Smith 		aac_enqueue_bio(sc, bp);
98935863739SMike Smith 	if (cm != NULL)
99035863739SMike Smith 		aac_release_command(cm);
99135863739SMike Smith 	return(ENOMEM);
99235863739SMike Smith }
99335863739SMike Smith 
994914da7d0SScott Long /*
99535863739SMike Smith  * Handle a bio-instigated command that has been completed.
99635863739SMike Smith  */
99735863739SMike Smith static void
99835863739SMike Smith aac_bio_complete(struct aac_command *cm)
99935863739SMike Smith {
100035863739SMike Smith 	struct aac_blockread_response *brr;
100135863739SMike Smith 	struct aac_blockwrite_response *bwr;
100235863739SMike Smith 	struct bio *bp;
100335863739SMike Smith 	AAC_FSAStatus status;
100435863739SMike Smith 
100535863739SMike Smith 	/* fetch relevant status and then release the command */
100635863739SMike Smith 	bp = (struct bio *)cm->cm_private;
10079e2e96d8SScott Long 	if (bp->bio_cmd == BIO_READ) {
100835863739SMike Smith 		brr = (struct aac_blockread_response *)&cm->cm_fib->data[0];
100935863739SMike Smith 		status = brr->Status;
101035863739SMike Smith 	} else {
101135863739SMike Smith 		bwr = (struct aac_blockwrite_response *)&cm->cm_fib->data[0];
101235863739SMike Smith 		status = bwr->Status;
101335863739SMike Smith 	}
101435863739SMike Smith 	aac_release_command(cm);
101535863739SMike Smith 
101635863739SMike Smith 	/* fix up the bio based on status */
101735863739SMike Smith 	if (status == ST_OK) {
101835863739SMike Smith 		bp->bio_resid = 0;
101935863739SMike Smith 	} else {
102035863739SMike Smith 		bp->bio_error = EIO;
102135863739SMike Smith 		bp->bio_flags |= BIO_ERROR;
10220b94a66eSMike Smith 		/* pass an error string out to the disk layer */
1023914da7d0SScott Long 		bp->bio_driver1 = aac_describe_code(aac_command_status_table,
1024914da7d0SScott Long 						    status);
102535863739SMike Smith 	}
10260b94a66eSMike Smith 	aac_biodone(bp);
102735863739SMike Smith }
102835863739SMike Smith 
1029914da7d0SScott Long /*
103035863739SMike Smith  * Submit a command to the controller, return when it completes.
1031b3457b51SScott Long  * XXX This is very dangerous!  If the card has gone out to lunch, we could
1032b3457b51SScott Long  *     be stuck here forever.  At the same time, signals are not caught
1033b3457b51SScott Long  *     because there is a risk that a signal could wakeup the tsleep before
1034b3457b51SScott Long  *     the card has a chance to complete the command.  The passed in timeout
1035b3457b51SScott Long  *     is ignored for the same reason.  Since there is no way to cancel a
1036b3457b51SScott Long  *     command in progress, we should probably create a 'dead' queue where
1037b3457b51SScott Long  *     commands go that have been interrupted/timed-out/etc, that keeps them
1038b3457b51SScott Long  *     out of the free pool.  That way, if the card is just slow, it won't
1039b3457b51SScott Long  *     spam the memory of a command that has been recycled.
104035863739SMike Smith  */
104135863739SMike Smith static int
104235863739SMike Smith aac_wait_command(struct aac_command *cm, int timeout)
104335863739SMike Smith {
1044ae543596SScott Long 	struct aac_softc *sc;
1045ae543596SScott Long 	int error = 0;
104635863739SMike Smith 
104735863739SMike Smith 	debug_called(2);
104835863739SMike Smith 
1049ae543596SScott Long 	sc = cm->cm_sc;
1050ae543596SScott Long 
105135863739SMike Smith 	/* Put the command on the ready queue and get things going */
105236e0bf6eSScott Long 	cm->cm_queue = AAC_ADAP_NORM_CMD_QUEUE;
105335863739SMike Smith 	aac_enqueue_ready(cm);
1054ae543596SScott Long 	aac_startio(sc);
105535863739SMike Smith 	while (!(cm->cm_flags & AAC_CMD_COMPLETED) && (error != EWOULDBLOCK)) {
1056ae543596SScott Long 		error = msleep(cm, &sc->aac_io_lock, PRIBIO, "aacwait", 0);
105735863739SMike Smith 	}
105835863739SMike Smith 	return(error);
105935863739SMike Smith }
106035863739SMike Smith 
1061914da7d0SScott Long /*
1062914da7d0SScott Long  *Command Buffer Management
1063914da7d0SScott Long  */
106435863739SMike Smith 
1065914da7d0SScott Long /*
106635863739SMike Smith  * Allocate a command.
106735863739SMike Smith  */
1068fe3cb0e1SScott Long int
106935863739SMike Smith aac_alloc_command(struct aac_softc *sc, struct aac_command **cmp)
107035863739SMike Smith {
107135863739SMike Smith 	struct aac_command *cm;
107235863739SMike Smith 
107335863739SMike Smith 	debug_called(3);
107435863739SMike Smith 
1075ffb37f33SScott Long 	if ((cm = aac_dequeue_free(sc)) == NULL) {
1076b85f5808SScott Long 		if (sc->total_fibs < sc->aac_max_fibs) {
1077ae543596SScott Long 			sc->aifflags |= AAC_AIFFLAGS_ALLOCFIBS;
1078ae543596SScott Long 			wakeup(sc->aifthread);
1079b85f5808SScott Long 		}
1080ae543596SScott Long 		return (EBUSY);
1081ffb37f33SScott Long 	}
108235863739SMike Smith 
10830b94a66eSMike Smith 	*cmp = cm;
10840b94a66eSMike Smith 	return(0);
10850b94a66eSMike Smith }
10860b94a66eSMike Smith 
1087914da7d0SScott Long /*
10880b94a66eSMike Smith  * Release a command back to the freelist.
10890b94a66eSMike Smith  */
1090fe3cb0e1SScott Long void
10910b94a66eSMike Smith aac_release_command(struct aac_command *cm)
10920b94a66eSMike Smith {
10930b94a66eSMike Smith 	debug_called(3);
10940b94a66eSMike Smith 
10950b94a66eSMike Smith 	/* (re)initialise the command/FIB */
109635863739SMike Smith 	cm->cm_sgtable = NULL;
109735863739SMike Smith 	cm->cm_flags = 0;
109835863739SMike Smith 	cm->cm_complete = NULL;
109935863739SMike Smith 	cm->cm_private = NULL;
110035863739SMike Smith 	cm->cm_fib->Header.XferState = AAC_FIBSTATE_EMPTY;
110135863739SMike Smith 	cm->cm_fib->Header.StructType = AAC_FIBTYPE_TFIB;
110235863739SMike Smith 	cm->cm_fib->Header.Flags = 0;
110335863739SMike Smith 	cm->cm_fib->Header.SenderSize = sizeof(struct aac_fib);
110435863739SMike Smith 
110535863739SMike Smith 	/*
110635863739SMike Smith 	 * These are duplicated in aac_start to cover the case where an
110735863739SMike Smith 	 * intermediate stage may have destroyed them.  They're left
110835863739SMike Smith 	 * initialised here for debugging purposes only.
110935863739SMike Smith 	 */
111035863739SMike Smith 	cm->cm_fib->Header.SenderFibAddress = (u_int32_t)cm->cm_fib;
1111f30ac74cSScott Long 	cm->cm_fib->Header.ReceiverFibAddress = (u_int32_t)cm->cm_fibphys;
1112f30ac74cSScott Long 	cm->cm_fib->Header.SenderData = 0;
111335863739SMike Smith 
111435863739SMike Smith 	aac_enqueue_free(cm);
111535863739SMike Smith }
111635863739SMike Smith 
1117914da7d0SScott Long /*
11180b94a66eSMike Smith  * Map helper for command/FIB allocation.
111935863739SMike Smith  */
112035863739SMike Smith static void
11210b94a66eSMike Smith aac_map_command_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error)
112235863739SMike Smith {
11238480cc63SScott Long 	uint32_t	*fibphys;
1124914da7d0SScott Long 
11258480cc63SScott Long 	fibphys = (uint32_t *)arg;
112635863739SMike Smith 
112735863739SMike Smith 	debug_called(3);
112835863739SMike Smith 
1129ffb37f33SScott Long 	*fibphys = segs[0].ds_addr;
113035863739SMike Smith }
113135863739SMike Smith 
1132914da7d0SScott Long /*
11330b94a66eSMike Smith  * Allocate and initialise commands/FIBs for this adapter.
113435863739SMike Smith  */
11350b94a66eSMike Smith static int
11360b94a66eSMike Smith aac_alloc_commands(struct aac_softc *sc)
113735863739SMike Smith {
113835863739SMike Smith 	struct aac_command *cm;
1139ffb37f33SScott Long 	struct aac_fibmap *fm;
11408480cc63SScott Long 	uint32_t fibphys;
1141ffb37f33SScott Long 	int i, error;
114235863739SMike Smith 
1143a6d35632SScott Long 	debug_called(2);
114435863739SMike Smith 
1145a6d35632SScott Long 	if (sc->total_fibs + AAC_FIB_COUNT > sc->aac_max_fibs)
1146ffb37f33SScott Long 		return (ENOMEM);
1147ffb37f33SScott Long 
11488480cc63SScott Long 	fm = malloc(sizeof(struct aac_fibmap), M_AACBUF, M_NOWAIT|M_ZERO);
1149a6d35632SScott Long 	if (fm == NULL)
1150a6d35632SScott Long 		return (ENOMEM);
1151ffb37f33SScott Long 
11520b94a66eSMike Smith 	/* allocate the FIBs in DMAable memory and load them */
1153ffb37f33SScott Long 	if (bus_dmamem_alloc(sc->aac_fib_dmat, (void **)&fm->aac_fibs,
1154ffb37f33SScott Long 			     BUS_DMA_NOWAIT, &fm->aac_fibmap)) {
115570545d1aSScott Long 		device_printf(sc->aac_dev,
115670545d1aSScott Long 			      "Not enough contiguous memory available.\n");
11578480cc63SScott Long 		free(fm, M_AACBUF);
11580b94a66eSMike Smith 		return (ENOMEM);
115935863739SMike Smith 	}
1160128aa5a0SScott Long 
1161ffb37f33SScott Long 	bus_dmamap_load(sc->aac_fib_dmat, fm->aac_fibmap, fm->aac_fibs,
1162ffb37f33SScott Long 			AAC_FIB_COUNT * sizeof(struct aac_fib),
1163ffb37f33SScott Long 			aac_map_command_helper, &fibphys, 0);
1164128aa5a0SScott Long 
11650b94a66eSMike Smith 	/* initialise constant fields in the command structure */
1166ffb37f33SScott Long 	bzero(fm->aac_fibs, AAC_FIB_COUNT * sizeof(struct aac_fib));
11670b94a66eSMike Smith 	for (i = 0; i < AAC_FIB_COUNT; i++) {
11688480cc63SScott Long 		cm = sc->aac_commands + sc->total_fibs;
1169ffb37f33SScott Long 		fm->aac_commands = cm;
117035863739SMike Smith 		cm->cm_sc = sc;
1171ffb37f33SScott Long 		cm->cm_fib = fm->aac_fibs + i;
11728480cc63SScott Long 		cm->cm_fibphys = fibphys + (i * sizeof(struct aac_fib));
1173cb0d64b9SScott Long 		cm->cm_index = sc->total_fibs;
117435863739SMike Smith 
1175ffb37f33SScott Long 		if ((error = bus_dmamap_create(sc->aac_buffer_dmat, 0,
1176ffb37f33SScott Long 					       &cm->cm_datamap)) == 0)
117735863739SMike Smith 			aac_release_command(cm);
1178ffb37f33SScott Long 		else
11798480cc63SScott Long 			break;
11808480cc63SScott Long 		sc->total_fibs++;
118135863739SMike Smith 	}
1182ffb37f33SScott Long 
11838480cc63SScott Long 	if (i > 0) {
1184ffb37f33SScott Long 		TAILQ_INSERT_TAIL(&sc->aac_fibmap_tqh, fm, fm_link);
1185a6d35632SScott Long 		debug(1, "total_fibs= %d\n", sc->total_fibs);
11860b94a66eSMike Smith 		return (0);
118735863739SMike Smith 	}
118835863739SMike Smith 
11898480cc63SScott Long 	bus_dmamap_unload(sc->aac_fib_dmat, fm->aac_fibmap);
11908480cc63SScott Long 	bus_dmamem_free(sc->aac_fib_dmat, fm->aac_fibs, fm->aac_fibmap);
11918480cc63SScott Long 	free(fm, M_AACBUF);
11928480cc63SScott Long 	return (ENOMEM);
11938480cc63SScott Long }
11948480cc63SScott Long 
1195914da7d0SScott Long /*
11960b94a66eSMike Smith  * Free FIBs owned by this adapter.
119735863739SMike Smith  */
119835863739SMike Smith static void
11998480cc63SScott Long aac_free_commands(struct aac_softc *sc)
120035863739SMike Smith {
12018480cc63SScott Long 	struct aac_fibmap *fm;
1202ffb37f33SScott Long 	struct aac_command *cm;
120335863739SMike Smith 	int i;
120435863739SMike Smith 
120535863739SMike Smith 	debug_called(1);
120635863739SMike Smith 
12078480cc63SScott Long 	while ((fm = TAILQ_FIRST(&sc->aac_fibmap_tqh)) != NULL) {
12088480cc63SScott Long 
12098480cc63SScott Long 		TAILQ_REMOVE(&sc->aac_fibmap_tqh, fm, fm_link);
12108480cc63SScott Long 		/*
12118480cc63SScott Long 		 * We check against total_fibs to handle partially
12128480cc63SScott Long 		 * allocated blocks.
12138480cc63SScott Long 		 */
12148480cc63SScott Long 		for (i = 0; i < AAC_FIB_COUNT && sc->total_fibs--; i++) {
1215ffb37f33SScott Long 			cm = fm->aac_commands + i;
1216ffb37f33SScott Long 			bus_dmamap_destroy(sc->aac_buffer_dmat, cm->cm_datamap);
1217ffb37f33SScott Long 		}
1218ffb37f33SScott Long 		bus_dmamap_unload(sc->aac_fib_dmat, fm->aac_fibmap);
1219ffb37f33SScott Long 		bus_dmamem_free(sc->aac_fib_dmat, fm->aac_fibs, fm->aac_fibmap);
12208480cc63SScott Long 		free(fm, M_AACBUF);
12218480cc63SScott Long 	}
122235863739SMike Smith }
122335863739SMike Smith 
1224914da7d0SScott Long /*
122535863739SMike Smith  * Command-mapping helper function - populate this command's s/g table.
122635863739SMike Smith  */
122735863739SMike Smith static void
122835863739SMike Smith aac_map_command_sg(void *arg, bus_dma_segment_t *segs, int nseg, int error)
122935863739SMike Smith {
1230914da7d0SScott Long 	struct aac_command *cm;
1231914da7d0SScott Long 	struct aac_fib *fib;
123235863739SMike Smith 	int i;
123335863739SMike Smith 
123435863739SMike Smith 	debug_called(3);
123535863739SMike Smith 
1236914da7d0SScott Long 	cm = (struct aac_command *)arg;
1237914da7d0SScott Long 	fib = cm->cm_fib;
1238914da7d0SScott Long 
123935863739SMike Smith 	/* copy into the FIB */
1240b85f5808SScott Long 	if (cm->cm_sgtable != NULL) {
1241b85f5808SScott Long 		if ((cm->cm_sc->flags & AAC_FLAGS_SG_64BIT) == 0) {
1242b85f5808SScott Long 			struct aac_sg_table *sg;
1243b85f5808SScott Long 			sg = cm->cm_sgtable;
124435863739SMike Smith 			sg->SgCount = nseg;
124535863739SMike Smith 			for (i = 0; i < nseg; i++) {
124635863739SMike Smith 				sg->SgEntry[i].SgAddress = segs[i].ds_addr;
124735863739SMike Smith 				sg->SgEntry[i].SgByteCount = segs[i].ds_len;
124835863739SMike Smith 			}
124935863739SMike Smith 			/* update the FIB size for the s/g count */
125035863739SMike Smith 			fib->Header.Size += nseg * sizeof(struct aac_sg_entry);
1251b85f5808SScott Long 		} else {
1252b85f5808SScott Long 			struct aac_sg_table64 *sg;
1253b85f5808SScott Long 			sg = (struct aac_sg_table64 *)cm->cm_sgtable;
1254b85f5808SScott Long 			sg->SgCount = nseg;
1255b85f5808SScott Long 			for (i = 0; i < nseg; i++) {
1256b85f5808SScott Long 				sg->SgEntry64[i].SgAddress = segs[i].ds_addr;
1257b85f5808SScott Long 				sg->SgEntry64[i].SgByteCount = segs[i].ds_len;
125835863739SMike Smith 			}
1259b85f5808SScott Long 			/* update the FIB size for the s/g count */
1260b85f5808SScott Long 			fib->Header.Size += nseg*sizeof(struct aac_sg_entry64);
1261b85f5808SScott Long 		}
1262b85f5808SScott Long 	}
126335863739SMike Smith }
126435863739SMike Smith 
1265914da7d0SScott Long /*
126635863739SMike Smith  * Map a command into controller-visible space.
126735863739SMike Smith  */
126835863739SMike Smith static void
126935863739SMike Smith aac_map_command(struct aac_command *cm)
127035863739SMike Smith {
1271914da7d0SScott Long 	struct aac_softc *sc;
127235863739SMike Smith 
127335863739SMike Smith 	debug_called(2);
127435863739SMike Smith 
1275914da7d0SScott Long 	sc = cm->cm_sc;
1276914da7d0SScott Long 
127735863739SMike Smith 	/* don't map more than once */
127835863739SMike Smith 	if (cm->cm_flags & AAC_CMD_MAPPED)
127935863739SMike Smith 		return;
128035863739SMike Smith 
128135863739SMike Smith 	if (cm->cm_datalen != 0) {
1282914da7d0SScott Long 		bus_dmamap_load(sc->aac_buffer_dmat, cm->cm_datamap,
1283914da7d0SScott Long 				cm->cm_data, cm->cm_datalen,
1284914da7d0SScott Long 				aac_map_command_sg, cm, 0);
128535863739SMike Smith 
128635863739SMike Smith 		if (cm->cm_flags & AAC_CMD_DATAIN)
1287c6eafcf2SScott Long 			bus_dmamap_sync(sc->aac_buffer_dmat, cm->cm_datamap,
1288c6eafcf2SScott Long 					BUS_DMASYNC_PREREAD);
128935863739SMike Smith 		if (cm->cm_flags & AAC_CMD_DATAOUT)
1290c6eafcf2SScott Long 			bus_dmamap_sync(sc->aac_buffer_dmat, cm->cm_datamap,
1291c6eafcf2SScott Long 					BUS_DMASYNC_PREWRITE);
129235863739SMike Smith 	}
129335863739SMike Smith 	cm->cm_flags |= AAC_CMD_MAPPED;
129435863739SMike Smith }
129535863739SMike Smith 
1296914da7d0SScott Long /*
129735863739SMike Smith  * Unmap a command from controller-visible space.
129835863739SMike Smith  */
129935863739SMike Smith static void
130035863739SMike Smith aac_unmap_command(struct aac_command *cm)
130135863739SMike Smith {
1302914da7d0SScott Long 	struct aac_softc *sc;
130335863739SMike Smith 
130435863739SMike Smith 	debug_called(2);
130535863739SMike Smith 
1306914da7d0SScott Long 	sc = cm->cm_sc;
1307914da7d0SScott Long 
130835863739SMike Smith 	if (!(cm->cm_flags & AAC_CMD_MAPPED))
130935863739SMike Smith 		return;
131035863739SMike Smith 
131135863739SMike Smith 	if (cm->cm_datalen != 0) {
131235863739SMike Smith 		if (cm->cm_flags & AAC_CMD_DATAIN)
1313c6eafcf2SScott Long 			bus_dmamap_sync(sc->aac_buffer_dmat, cm->cm_datamap,
1314c6eafcf2SScott Long 					BUS_DMASYNC_POSTREAD);
131535863739SMike Smith 		if (cm->cm_flags & AAC_CMD_DATAOUT)
1316c6eafcf2SScott Long 			bus_dmamap_sync(sc->aac_buffer_dmat, cm->cm_datamap,
1317c6eafcf2SScott Long 					BUS_DMASYNC_POSTWRITE);
131835863739SMike Smith 
131935863739SMike Smith 		bus_dmamap_unload(sc->aac_buffer_dmat, cm->cm_datamap);
132035863739SMike Smith 	}
132135863739SMike Smith 	cm->cm_flags &= ~AAC_CMD_MAPPED;
132235863739SMike Smith }
132335863739SMike Smith 
1324914da7d0SScott Long /*
1325914da7d0SScott Long  * Hardware Interface
1326914da7d0SScott Long  */
132735863739SMike Smith 
1328914da7d0SScott Long /*
132935863739SMike Smith  * Initialise the adapter.
133035863739SMike Smith  */
133135863739SMike Smith static void
133235863739SMike Smith aac_common_map(void *arg, bus_dma_segment_t *segs, int nseg, int error)
133335863739SMike Smith {
1334914da7d0SScott Long 	struct aac_softc *sc;
133535863739SMike Smith 
133635863739SMike Smith 	debug_called(1);
133735863739SMike Smith 
1338914da7d0SScott Long 	sc = (struct aac_softc *)arg;
1339914da7d0SScott Long 
134035863739SMike Smith 	sc->aac_common_busaddr = segs[0].ds_addr;
134135863739SMike Smith }
134235863739SMike Smith 
1343a6d35632SScott Long static int
1344a6d35632SScott Long aac_check_firmware(struct aac_softc *sc)
1345a6d35632SScott Long {
1346a6d35632SScott Long 	u_int32_t major, minor, options;
1347a6d35632SScott Long 
1348a6d35632SScott Long 	debug_called(1);
1349a6d35632SScott Long 
1350fe94b852SScott Long 	/*
1351fe94b852SScott Long 	 * Retrieve the firmware version numbers.  Dell PERC2/QC cards with
1352fe94b852SScott Long 	 * firmware version 1.x are not compatible with this driver.
1353fe94b852SScott Long 	 */
1354a6d35632SScott Long 	if (sc->flags & AAC_FLAGS_PERC2QC) {
1355fe94b852SScott Long 		if (aac_sync_command(sc, AAC_MONKER_GETKERNVER, 0, 0, 0, 0,
1356fe94b852SScott Long 				     NULL)) {
1357fe94b852SScott Long 			device_printf(sc->aac_dev,
1358fe94b852SScott Long 				      "Error reading firmware version\n");
1359fe94b852SScott Long 			return (EIO);
1360fe94b852SScott Long 		}
1361fe94b852SScott Long 
1362fe94b852SScott Long 		/* These numbers are stored as ASCII! */
1363a6d35632SScott Long 		major = (AAC_GET_MAILBOX(sc, 1) & 0xff) - 0x30;
1364a6d35632SScott Long 		minor = (AAC_GET_MAILBOX(sc, 2) & 0xff) - 0x30;
1365fe94b852SScott Long 		if (major == 1) {
1366fe94b852SScott Long 			device_printf(sc->aac_dev,
1367fe94b852SScott Long 			    "Firmware version %d.%d is not supported.\n",
1368fe94b852SScott Long 			    major, minor);
1369fe94b852SScott Long 			return (EINVAL);
1370fe94b852SScott Long 		}
1371fe94b852SScott Long 	}
1372fe94b852SScott Long 
1373a6d35632SScott Long 	/*
1374a6d35632SScott Long 	 * Retrieve the capabilities/supported options word so we know what
1375a6d35632SScott Long 	 * work-arounds to enable.
1376a6d35632SScott Long 	 */
1377a6d35632SScott Long 	if (aac_sync_command(sc, AAC_MONKER_GETINFO, 0, 0, 0, 0, NULL)) {
1378a6d35632SScott Long 		device_printf(sc->aac_dev, "RequestAdapterInfo failed\n");
1379a6d35632SScott Long 		return (EIO);
1380a6d35632SScott Long 	}
1381a6d35632SScott Long 	options = AAC_GET_MAILBOX(sc, 1);
1382a6d35632SScott Long 	sc->supported_options = options;
1383a6d35632SScott Long 
1384a6d35632SScott Long 	if ((options & AAC_SUPPORTED_4GB_WINDOW) != 0 &&
1385a6d35632SScott Long 	    (sc->flags & AAC_FLAGS_NO4GB) == 0)
1386a6d35632SScott Long 		sc->flags |= AAC_FLAGS_4GB_WINDOW;
1387a6d35632SScott Long 	if (options & AAC_SUPPORTED_NONDASD)
1388a6d35632SScott Long 		sc->flags |= AAC_FLAGS_ENABLE_CAM;
1389b85f5808SScott Long 	if ((options & AAC_SUPPORTED_SGMAP_HOST64) != 0 && (sizeof(bus_addr_t) > 4)) {
1390a6d35632SScott Long 		device_printf(sc->aac_dev, "Enabling 64-bit address support\n");
1391a6d35632SScott Long 		sc->flags |= AAC_FLAGS_SG_64BIT;
1392a6d35632SScott Long 	}
1393a6d35632SScott Long 
1394a6d35632SScott Long 	/* Check for broken hardware that does a lower number of commands */
1395a6d35632SScott Long 	if ((sc->flags & AAC_FLAGS_256FIBS) == 0)
1396a6d35632SScott Long 		sc->aac_max_fibs = AAC_MAX_FIBS;
1397a6d35632SScott Long 	else
1398a6d35632SScott Long 		sc->aac_max_fibs = 256;
1399a6d35632SScott Long 
1400fe94b852SScott Long 	return (0);
1401fe94b852SScott Long }
1402fe94b852SScott Long 
140335863739SMike Smith static int
140435863739SMike Smith aac_init(struct aac_softc *sc)
140535863739SMike Smith {
140635863739SMike Smith 	struct aac_adapter_init	*ip;
140735863739SMike Smith 	time_t then;
140835863739SMike Smith 	u_int32_t code;
140935863739SMike Smith 	u_int8_t *qaddr;
1410a6d35632SScott Long 	int error;
141135863739SMike Smith 
141235863739SMike Smith 	debug_called(1);
141335863739SMike Smith 
141435863739SMike Smith 	/*
141535863739SMike Smith 	 * First wait for the adapter to come ready.
141635863739SMike Smith 	 */
141735863739SMike Smith 	then = time_second;
141835863739SMike Smith 	do {
141935863739SMike Smith 		code = AAC_GET_FWSTATUS(sc);
142035863739SMike Smith 		if (code & AAC_SELF_TEST_FAILED) {
142135863739SMike Smith 			device_printf(sc->aac_dev, "FATAL: selftest failed\n");
142235863739SMike Smith 			return(ENXIO);
142335863739SMike Smith 		}
142435863739SMike Smith 		if (code & AAC_KERNEL_PANIC) {
1425914da7d0SScott Long 			device_printf(sc->aac_dev,
1426914da7d0SScott Long 				      "FATAL: controller kernel panic\n");
142735863739SMike Smith 			return(ENXIO);
142835863739SMike Smith 		}
142935863739SMike Smith 		if (time_second > (then + AAC_BOOT_TIMEOUT)) {
1430914da7d0SScott Long 			device_printf(sc->aac_dev,
1431914da7d0SScott Long 				      "FATAL: controller not coming ready, "
1432c6eafcf2SScott Long 					   "status %x\n", code);
143335863739SMike Smith 			return(ENXIO);
143435863739SMike Smith 		}
143535863739SMike Smith 	} while (!(code & AAC_UP_AND_RUNNING));
143635863739SMike Smith 
1437a6d35632SScott Long 	error = ENOMEM;
1438a6d35632SScott Long 	/*
1439a6d35632SScott Long 	 * Create DMA tag for mapping buffers into controller-addressable space.
1440a6d35632SScott Long 	 */
1441a6d35632SScott Long 	if (bus_dma_tag_create(sc->aac_parent_dmat, 	/* parent */
1442a6d35632SScott Long 			       1, 0, 			/* algnmnt, boundary */
1443a6d35632SScott Long 			       (sc->flags & AAC_FLAGS_SG_64BIT) ?
1444a6d35632SScott Long 			       BUS_SPACE_MAXADDR :
1445a6d35632SScott Long 			       BUS_SPACE_MAXADDR_32BIT,	/* lowaddr */
1446a6d35632SScott Long 			       BUS_SPACE_MAXADDR, 	/* highaddr */
1447a6d35632SScott Long 			       NULL, NULL, 		/* filter, filterarg */
1448a6d35632SScott Long 			       MAXBSIZE,		/* maxsize */
1449a6d35632SScott Long 			       AAC_MAXSGENTRIES,	/* nsegments */
1450a6d35632SScott Long 			       MAXBSIZE,		/* maxsegsize */
1451a6d35632SScott Long 			       BUS_DMA_ALLOCNOW,	/* flags */
1452f6b1c44dSScott Long 			       busdma_lock_mutex,	/* lockfunc */
1453f6b1c44dSScott Long 			       &sc->aac_io_lock,	/* lockfuncarg */
1454a6d35632SScott Long 			       &sc->aac_buffer_dmat)) {
1455a6d35632SScott Long 		device_printf(sc->aac_dev, "can't allocate buffer DMA tag\n");
1456a6d35632SScott Long 		goto out;
1457a6d35632SScott Long 	}
1458a6d35632SScott Long 
1459a6d35632SScott Long 	/*
1460a6d35632SScott Long 	 * Create DMA tag for mapping FIBs into controller-addressable space..
1461a6d35632SScott Long 	 */
1462a6d35632SScott Long 	if (bus_dma_tag_create(sc->aac_parent_dmat,	/* parent */
1463a6d35632SScott Long 			       1, 0, 			/* algnmnt, boundary */
1464a6d35632SScott Long 			       (sc->flags & AAC_FLAGS_4GB_WINDOW) ?
1465a6d35632SScott Long 			       BUS_SPACE_MAXADDR_32BIT :
1466a6d35632SScott Long 			       0x7fffffff,		/* lowaddr */
1467a6d35632SScott Long 			       BUS_SPACE_MAXADDR, 	/* highaddr */
1468a6d35632SScott Long 			       NULL, NULL, 		/* filter, filterarg */
1469a6d35632SScott Long 			       AAC_FIB_COUNT *
1470a6d35632SScott Long 			       sizeof(struct aac_fib),  /* maxsize */
1471a6d35632SScott Long 			       1,			/* nsegments */
1472a6d35632SScott Long 			       AAC_FIB_COUNT *
1473a6d35632SScott Long 			       sizeof(struct aac_fib),	/* maxsegsize */
1474a6d35632SScott Long 			       BUS_DMA_ALLOCNOW,	/* flags */
1475f6b1c44dSScott Long 			       NULL, NULL,		/* No locking needed */
1476a6d35632SScott Long 			       &sc->aac_fib_dmat)) {
1477a6d35632SScott Long 		device_printf(sc->aac_dev, "can't allocate FIB DMA tag\n");;
1478a6d35632SScott Long 		goto out;
1479a6d35632SScott Long 	}
1480a6d35632SScott Long 
148135863739SMike Smith 	/*
148235863739SMike Smith 	 * Create DMA tag for the common structure and allocate it.
148335863739SMike Smith 	 */
148435863739SMike Smith 	if (bus_dma_tag_create(sc->aac_parent_dmat, 	/* parent */
1485c6eafcf2SScott Long 			       1, 0,			/* algnmnt, boundary */
1486a6d35632SScott Long 			       (sc->flags & AAC_FLAGS_4GB_WINDOW) ?
1487a6d35632SScott Long 			       BUS_SPACE_MAXADDR_32BIT :
1488a6d35632SScott Long 			       0x7fffffff,		/* lowaddr */
148935863739SMike Smith 			       BUS_SPACE_MAXADDR, 	/* highaddr */
149035863739SMike Smith 			       NULL, NULL, 		/* filter, filterarg */
1491ffb37f33SScott Long 			       8192 + sizeof(struct aac_common), /* maxsize */
1492914da7d0SScott Long 			       1,			/* nsegments */
149335863739SMike Smith 			       BUS_SPACE_MAXSIZE_32BIT,	/* maxsegsize */
1494a6d35632SScott Long 			       BUS_DMA_ALLOCNOW,	/* flags */
1495f6b1c44dSScott Long 			       NULL, NULL,		/* No locking needed */
149635863739SMike Smith 			       &sc->aac_common_dmat)) {
1497914da7d0SScott Long 		device_printf(sc->aac_dev,
1498914da7d0SScott Long 			      "can't allocate common structure DMA tag\n");
1499a6d35632SScott Long 		goto out;
150035863739SMike Smith 	}
1501c6eafcf2SScott Long 	if (bus_dmamem_alloc(sc->aac_common_dmat, (void **)&sc->aac_common,
1502c6eafcf2SScott Long 			     BUS_DMA_NOWAIT, &sc->aac_common_dmamap)) {
150335863739SMike Smith 		device_printf(sc->aac_dev, "can't allocate common structure\n");
1504a6d35632SScott Long 		goto out;
150535863739SMike Smith 	}
1506ffb37f33SScott Long 
1507ffb37f33SScott Long 	/*
1508ffb37f33SScott Long 	 * Work around a bug in the 2120 and 2200 that cannot DMA commands
1509ffb37f33SScott Long 	 * below address 8192 in physical memory.
1510ffb37f33SScott Long 	 * XXX If the padding is not needed, can it be put to use instead
1511ffb37f33SScott Long 	 * of ignored?
1512ffb37f33SScott Long 	 */
1513914da7d0SScott Long 	bus_dmamap_load(sc->aac_common_dmat, sc->aac_common_dmamap,
1514ffb37f33SScott Long 			sc->aac_common, 8192 + sizeof(*sc->aac_common),
1515ffb37f33SScott Long 			aac_common_map, sc, 0);
1516ffb37f33SScott Long 
1517ffb37f33SScott Long 	if (sc->aac_common_busaddr < 8192) {
1518ffb37f33SScott Long 		(uint8_t *)sc->aac_common += 8192;
1519ffb37f33SScott Long 		sc->aac_common_busaddr += 8192;
1520ffb37f33SScott Long 	}
152135863739SMike Smith 	bzero(sc->aac_common, sizeof(*sc->aac_common));
152235863739SMike Smith 
1523ffb37f33SScott Long 	/* Allocate some FIBs and associated command structs */
1524ffb37f33SScott Long 	TAILQ_INIT(&sc->aac_fibmap_tqh);
1525ffb37f33SScott Long 	sc->aac_commands = malloc(AAC_MAX_FIBS * sizeof(struct aac_command),
15268480cc63SScott Long 				  M_AACBUF, M_WAITOK|M_ZERO);
15278480cc63SScott Long 	while (sc->total_fibs < AAC_PREALLOCATE_FIBS) {
1528ffb37f33SScott Long 		if (aac_alloc_commands(sc) != 0)
1529ffb37f33SScott Long 			break;
1530ffb37f33SScott Long 	}
1531ffb37f33SScott Long 	if (sc->total_fibs == 0)
1532a6d35632SScott Long 		goto out;
1533ffb37f33SScott Long 
153435863739SMike Smith 	/*
1535914da7d0SScott Long 	 * Fill in the init structure.  This tells the adapter about the
1536914da7d0SScott Long 	 * physical location of various important shared data structures.
153735863739SMike Smith 	 */
153835863739SMike Smith 	ip = &sc->aac_common->ac_init;
153935863739SMike Smith 	ip->InitStructRevision = AAC_INIT_STRUCT_REVISION;
1540f30ac74cSScott Long 	ip->MiniPortRevision = AAC_INIT_STRUCT_MINIPORT_REVISION;
154135863739SMike Smith 
1542c6eafcf2SScott Long 	ip->AdapterFibsPhysicalAddress = sc->aac_common_busaddr +
1543c6eafcf2SScott Long 					 offsetof(struct aac_common, ac_fibs);
1544149af931SScott Long 	ip->AdapterFibsVirtualAddress = 0;
154535863739SMike Smith 	ip->AdapterFibsSize = AAC_ADAPTER_FIBS * sizeof(struct aac_fib);
154635863739SMike Smith 	ip->AdapterFibAlign = sizeof(struct aac_fib);
154735863739SMike Smith 
1548c6eafcf2SScott Long 	ip->PrintfBufferAddress = sc->aac_common_busaddr +
1549c6eafcf2SScott Long 				  offsetof(struct aac_common, ac_printf);
155035863739SMike Smith 	ip->PrintfBufferSize = AAC_PRINTF_BUFSIZE;
155135863739SMike Smith 
15524b00f859SScott Long 	/*
15534b00f859SScott Long 	 * The adapter assumes that pages are 4K in size, except on some
15544b00f859SScott Long  	 * broken firmware versions that do the page->byte conversion twice,
15554b00f859SScott Long 	 * therefore 'assuming' that this value is in 16MB units (2^24).
15564b00f859SScott Long 	 * Round up since the granularity is so high.
15574b00f859SScott Long 	 */
1558f30ac74cSScott Long 	ip->HostPhysMemPages = ctob(physmem) / AAC_PAGE_SIZE;
15594b00f859SScott Long 	if (sc->flags & AAC_FLAGS_BROKEN_MEMMAP) {
15604b00f859SScott Long 		ip->HostPhysMemPages =
15614b00f859SScott Long 		    (ip->HostPhysMemPages + AAC_PAGE_SIZE) / AAC_PAGE_SIZE;
156235863739SMike Smith 	ip->HostElapsedSeconds = time_second;	/* reset later if invalid */
156335863739SMike Smith 
156435863739SMike Smith 	/*
1565c6eafcf2SScott Long 	 * Initialise FIB queues.  Note that it appears that the layout of the
1566c6eafcf2SScott Long 	 * indexes and the segmentation of the entries may be mandated by the
1567c6eafcf2SScott Long 	 * adapter, which is only told about the base of the queue index fields.
156835863739SMike Smith 	 *
156935863739SMike Smith 	 * The initial values of the indices are assumed to inform the adapter
1570914da7d0SScott Long 	 * of the sizes of the respective queues, and theoretically it could
1571914da7d0SScott Long 	 * work out the entire layout of the queue structures from this.  We
1572914da7d0SScott Long 	 * take the easy route and just lay this area out like everyone else
1573914da7d0SScott Long 	 * does.
157435863739SMike Smith 	 *
1575914da7d0SScott Long 	 * The Linux driver uses a much more complex scheme whereby several
1576914da7d0SScott Long 	 * header records are kept for each queue.  We use a couple of generic
1577914da7d0SScott Long 	 * list manipulation functions which 'know' the size of each list by
1578914da7d0SScott Long 	 * virtue of a table.
157935863739SMike Smith 	 */
158035863739SMike Smith 	qaddr = &sc->aac_common->ac_qbuf[0] + AAC_QUEUE_ALIGN;
158135863739SMike Smith 	qaddr -= (u_int32_t)qaddr % AAC_QUEUE_ALIGN;
158235863739SMike Smith 	sc->aac_queues = (struct aac_queue_table *)qaddr;
1583914da7d0SScott Long 	ip->CommHeaderAddress = sc->aac_common_busaddr +
1584914da7d0SScott Long 				((u_int32_t)sc->aac_queues -
1585914da7d0SScott Long 				(u_int32_t)sc->aac_common);
158635863739SMike Smith 
1587c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_NORM_CMD_QUEUE][AAC_PRODUCER_INDEX] =
1588c6eafcf2SScott Long 		AAC_HOST_NORM_CMD_ENTRIES;
1589c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_NORM_CMD_QUEUE][AAC_CONSUMER_INDEX] =
1590c6eafcf2SScott Long 		AAC_HOST_NORM_CMD_ENTRIES;
1591c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_HIGH_CMD_QUEUE][AAC_PRODUCER_INDEX] =
1592c6eafcf2SScott Long 		AAC_HOST_HIGH_CMD_ENTRIES;
1593c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_HIGH_CMD_QUEUE][AAC_CONSUMER_INDEX] =
1594c6eafcf2SScott Long 		AAC_HOST_HIGH_CMD_ENTRIES;
1595c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_NORM_CMD_QUEUE][AAC_PRODUCER_INDEX] =
1596c6eafcf2SScott Long 		AAC_ADAP_NORM_CMD_ENTRIES;
1597c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_NORM_CMD_QUEUE][AAC_CONSUMER_INDEX] =
1598c6eafcf2SScott Long 		AAC_ADAP_NORM_CMD_ENTRIES;
1599c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_HIGH_CMD_QUEUE][AAC_PRODUCER_INDEX] =
1600c6eafcf2SScott Long 		AAC_ADAP_HIGH_CMD_ENTRIES;
1601c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_HIGH_CMD_QUEUE][AAC_CONSUMER_INDEX] =
1602c6eafcf2SScott Long 		AAC_ADAP_HIGH_CMD_ENTRIES;
1603c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_NORM_RESP_QUEUE][AAC_PRODUCER_INDEX]=
1604c6eafcf2SScott Long 		AAC_HOST_NORM_RESP_ENTRIES;
1605c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_NORM_RESP_QUEUE][AAC_CONSUMER_INDEX]=
1606c6eafcf2SScott Long 		AAC_HOST_NORM_RESP_ENTRIES;
1607c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_HIGH_RESP_QUEUE][AAC_PRODUCER_INDEX]=
1608c6eafcf2SScott Long 		AAC_HOST_HIGH_RESP_ENTRIES;
1609c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_HIGH_RESP_QUEUE][AAC_CONSUMER_INDEX]=
1610c6eafcf2SScott Long 		AAC_HOST_HIGH_RESP_ENTRIES;
1611c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_NORM_RESP_QUEUE][AAC_PRODUCER_INDEX]=
1612c6eafcf2SScott Long 		AAC_ADAP_NORM_RESP_ENTRIES;
1613c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_NORM_RESP_QUEUE][AAC_CONSUMER_INDEX]=
1614c6eafcf2SScott Long 		AAC_ADAP_NORM_RESP_ENTRIES;
1615c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_HIGH_RESP_QUEUE][AAC_PRODUCER_INDEX]=
1616c6eafcf2SScott Long 		AAC_ADAP_HIGH_RESP_ENTRIES;
1617c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_HIGH_RESP_QUEUE][AAC_CONSUMER_INDEX]=
1618c6eafcf2SScott Long 		AAC_ADAP_HIGH_RESP_ENTRIES;
1619c6eafcf2SScott Long 	sc->aac_qentries[AAC_HOST_NORM_CMD_QUEUE] =
1620c6eafcf2SScott Long 		&sc->aac_queues->qt_HostNormCmdQueue[0];
1621c6eafcf2SScott Long 	sc->aac_qentries[AAC_HOST_HIGH_CMD_QUEUE] =
1622c6eafcf2SScott Long 		&sc->aac_queues->qt_HostHighCmdQueue[0];
1623c6eafcf2SScott Long 	sc->aac_qentries[AAC_ADAP_NORM_CMD_QUEUE] =
1624c6eafcf2SScott Long 		&sc->aac_queues->qt_AdapNormCmdQueue[0];
1625c6eafcf2SScott Long 	sc->aac_qentries[AAC_ADAP_HIGH_CMD_QUEUE] =
1626c6eafcf2SScott Long 		&sc->aac_queues->qt_AdapHighCmdQueue[0];
1627c6eafcf2SScott Long 	sc->aac_qentries[AAC_HOST_NORM_RESP_QUEUE] =
1628c6eafcf2SScott Long 		&sc->aac_queues->qt_HostNormRespQueue[0];
1629c6eafcf2SScott Long 	sc->aac_qentries[AAC_HOST_HIGH_RESP_QUEUE] =
1630c6eafcf2SScott Long 		&sc->aac_queues->qt_HostHighRespQueue[0];
1631c6eafcf2SScott Long 	sc->aac_qentries[AAC_ADAP_NORM_RESP_QUEUE] =
1632c6eafcf2SScott Long 		&sc->aac_queues->qt_AdapNormRespQueue[0];
1633c6eafcf2SScott Long 	sc->aac_qentries[AAC_ADAP_HIGH_RESP_QUEUE] =
1634c6eafcf2SScott Long 		&sc->aac_queues->qt_AdapHighRespQueue[0];
163535863739SMike Smith 
163635863739SMike Smith 	/*
163735863739SMike Smith 	 * Do controller-type-specific initialisation
163835863739SMike Smith 	 */
163935863739SMike Smith 	switch (sc->aac_hwif) {
164035863739SMike Smith 	case AAC_HWIF_I960RX:
164135863739SMike Smith 		AAC_SETREG4(sc, AAC_RX_ODBR, ~0);
164235863739SMike Smith 		break;
164335863739SMike Smith 	}
164435863739SMike Smith 
164535863739SMike Smith 	/*
164635863739SMike Smith 	 * Give the init structure to the controller.
164735863739SMike Smith 	 */
164835863739SMike Smith 	if (aac_sync_command(sc, AAC_MONKER_INITSTRUCT,
1649914da7d0SScott Long 			     sc->aac_common_busaddr +
1650914da7d0SScott Long 			     offsetof(struct aac_common, ac_init), 0, 0, 0,
1651914da7d0SScott Long 			     NULL)) {
1652914da7d0SScott Long 		device_printf(sc->aac_dev,
1653914da7d0SScott Long 			      "error establishing init structure\n");
1654a6d35632SScott Long 		error = EIO;
1655a6d35632SScott Long 		goto out;
165635863739SMike Smith 	}
165735863739SMike Smith 
1658a6d35632SScott Long 	error = 0;
1659a6d35632SScott Long out:
1660a6d35632SScott Long 	return(error);
166135863739SMike Smith }
166235863739SMike Smith 
1663914da7d0SScott Long /*
166435863739SMike Smith  * Send a synchronous command to the controller and wait for a result.
166535863739SMike Smith  */
166635863739SMike Smith static int
166735863739SMike Smith aac_sync_command(struct aac_softc *sc, u_int32_t command,
166835863739SMike Smith 		 u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3,
166935863739SMike Smith 		 u_int32_t *sp)
167035863739SMike Smith {
167135863739SMike Smith 	time_t then;
167235863739SMike Smith 	u_int32_t status;
167335863739SMike Smith 
167435863739SMike Smith 	debug_called(3);
167535863739SMike Smith 
167635863739SMike Smith 	/* populate the mailbox */
167735863739SMike Smith 	AAC_SET_MAILBOX(sc, command, arg0, arg1, arg2, arg3);
167835863739SMike Smith 
167935863739SMike Smith 	/* ensure the sync command doorbell flag is cleared */
168035863739SMike Smith 	AAC_CLEAR_ISTATUS(sc, AAC_DB_SYNC_COMMAND);
168135863739SMike Smith 
168235863739SMike Smith 	/* then set it to signal the adapter */
168335863739SMike Smith 	AAC_QNOTIFY(sc, AAC_DB_SYNC_COMMAND);
168435863739SMike Smith 
168535863739SMike Smith 	/* spin waiting for the command to complete */
168635863739SMike Smith 	then = time_second;
168735863739SMike Smith 	do {
168835863739SMike Smith 		if (time_second > (then + AAC_IMMEDIATE_TIMEOUT)) {
1689a6d35632SScott Long 			debug(1, "timed out");
169035863739SMike Smith 			return(EIO);
169135863739SMike Smith 		}
169235863739SMike Smith 	} while (!(AAC_GET_ISTATUS(sc) & AAC_DB_SYNC_COMMAND));
169335863739SMike Smith 
169435863739SMike Smith 	/* clear the completion flag */
169535863739SMike Smith 	AAC_CLEAR_ISTATUS(sc, AAC_DB_SYNC_COMMAND);
169635863739SMike Smith 
169735863739SMike Smith 	/* get the command status */
1698a6d35632SScott Long 	status = AAC_GET_MAILBOX(sc, 0);
169935863739SMike Smith 	if (sp != NULL)
170035863739SMike Smith 		*sp = status;
17010b94a66eSMike Smith 	return(0);
170235863739SMike Smith }
170335863739SMike Smith 
1704914da7d0SScott Long /*
1705cbfd045bSScott Long  * Grab the sync fib area.
1706cbfd045bSScott Long  */
1707cbfd045bSScott Long int
1708fe3cb0e1SScott Long aac_alloc_sync_fib(struct aac_softc *sc, struct aac_fib **fib, int flags)
1709cbfd045bSScott Long {
1710cbfd045bSScott Long 
1711cbfd045bSScott Long 	/*
1712cbfd045bSScott Long 	 * If the force flag is set, the system is shutting down, or in
1713cbfd045bSScott Long 	 * trouble.  Ignore the mutex.
1714cbfd045bSScott Long 	 */
1715cbfd045bSScott Long 	if (!(flags & AAC_SYNC_LOCK_FORCE))
1716cbfd045bSScott Long 		AAC_LOCK_ACQUIRE(&sc->aac_sync_lock);
1717cbfd045bSScott Long 
1718cbfd045bSScott Long 	*fib = &sc->aac_common->ac_sync_fib;
1719cbfd045bSScott Long 
1720cbfd045bSScott Long 	return (1);
1721cbfd045bSScott Long }
1722cbfd045bSScott Long 
1723cbfd045bSScott Long /*
1724cbfd045bSScott Long  * Release the sync fib area.
1725cbfd045bSScott Long  */
1726cbfd045bSScott Long void
1727cbfd045bSScott Long aac_release_sync_fib(struct aac_softc *sc)
1728cbfd045bSScott Long {
1729cbfd045bSScott Long 
1730cbfd045bSScott Long 	AAC_LOCK_RELEASE(&sc->aac_sync_lock);
1731cbfd045bSScott Long }
1732cbfd045bSScott Long 
1733cbfd045bSScott Long /*
173435863739SMike Smith  * Send a synchronous FIB to the controller and wait for a result.
173535863739SMike Smith  */
1736cbfd045bSScott Long int
173735863739SMike Smith aac_sync_fib(struct aac_softc *sc, u_int32_t command, u_int32_t xferstate,
1738cbfd045bSScott Long 		 struct aac_fib *fib, u_int16_t datasize)
173935863739SMike Smith {
174035863739SMike Smith 	debug_called(3);
174135863739SMike Smith 
174235863739SMike Smith 	if (datasize > AAC_FIB_DATASIZE)
174335863739SMike Smith 		return(EINVAL);
174435863739SMike Smith 
174535863739SMike Smith 	/*
174635863739SMike Smith 	 * Set up the sync FIB
174735863739SMike Smith 	 */
1748914da7d0SScott Long 	fib->Header.XferState = AAC_FIBSTATE_HOSTOWNED |
1749914da7d0SScott Long 				AAC_FIBSTATE_INITIALISED |
1750c6eafcf2SScott Long 				AAC_FIBSTATE_EMPTY;
175135863739SMike Smith 	fib->Header.XferState |= xferstate;
175235863739SMike Smith 	fib->Header.Command = command;
175335863739SMike Smith 	fib->Header.StructType = AAC_FIBTYPE_TFIB;
175435863739SMike Smith 	fib->Header.Size = sizeof(struct aac_fib) + datasize;
175535863739SMike Smith 	fib->Header.SenderSize = sizeof(struct aac_fib);
175635863739SMike Smith 	fib->Header.SenderFibAddress = (u_int32_t)fib;
1757c6eafcf2SScott Long 	fib->Header.ReceiverFibAddress = sc->aac_common_busaddr +
1758914da7d0SScott Long 					 offsetof(struct aac_common,
1759914da7d0SScott Long 						  ac_sync_fib);
176035863739SMike Smith 
176135863739SMike Smith 	/*
176235863739SMike Smith 	 * Give the FIB to the controller, wait for a response.
176335863739SMike Smith 	 */
1764914da7d0SScott Long 	if (aac_sync_command(sc, AAC_MONKER_SYNCFIB,
1765914da7d0SScott Long 			     fib->Header.ReceiverFibAddress, 0, 0, 0, NULL)) {
176635863739SMike Smith 		debug(2, "IO error");
176735863739SMike Smith 		return(EIO);
176835863739SMike Smith 	}
176935863739SMike Smith 
177035863739SMike Smith 	return (0);
177135863739SMike Smith }
177235863739SMike Smith 
1773914da7d0SScott Long /*
177435863739SMike Smith  * Adapter-space FIB queue manipulation
177535863739SMike Smith  *
177635863739SMike Smith  * Note that the queue implementation here is a little funky; neither the PI or
177735863739SMike Smith  * CI will ever be zero.  This behaviour is a controller feature.
177835863739SMike Smith  */
177935863739SMike Smith static struct {
178035863739SMike Smith 	int		size;
178135863739SMike Smith 	int		notify;
178235863739SMike Smith } aac_qinfo[] = {
178335863739SMike Smith 	{AAC_HOST_NORM_CMD_ENTRIES, AAC_DB_COMMAND_NOT_FULL},
178435863739SMike Smith 	{AAC_HOST_HIGH_CMD_ENTRIES, 0},
178535863739SMike Smith 	{AAC_ADAP_NORM_CMD_ENTRIES, AAC_DB_COMMAND_READY},
178635863739SMike Smith 	{AAC_ADAP_HIGH_CMD_ENTRIES, 0},
178735863739SMike Smith 	{AAC_HOST_NORM_RESP_ENTRIES, AAC_DB_RESPONSE_NOT_FULL},
178835863739SMike Smith 	{AAC_HOST_HIGH_RESP_ENTRIES, 0},
178935863739SMike Smith 	{AAC_ADAP_NORM_RESP_ENTRIES, AAC_DB_RESPONSE_READY},
179035863739SMike Smith 	{AAC_ADAP_HIGH_RESP_ENTRIES, 0}
179135863739SMike Smith };
179235863739SMike Smith 
179335863739SMike Smith /*
1794c6eafcf2SScott Long  * Atomically insert an entry into the nominated queue, returns 0 on success or
1795c6eafcf2SScott Long  * EBUSY if the queue is full.
179635863739SMike Smith  *
17970b94a66eSMike Smith  * Note: it would be more efficient to defer notifying the controller in
1798914da7d0SScott Long  *	 the case where we may be inserting several entries in rapid succession,
1799914da7d0SScott Long  *	 but implementing this usefully may be difficult (it would involve a
1800c6eafcf2SScott Long  *	 separate queue/notify interface).
180135863739SMike Smith  */
180235863739SMike Smith static int
1803f6c4dd3fSScott Long aac_enqueue_fib(struct aac_softc *sc, int queue, struct aac_command *cm)
180435863739SMike Smith {
180535863739SMike Smith 	u_int32_t pi, ci;
18069e2e96d8SScott Long 	int error;
1807f6c4dd3fSScott Long 	u_int32_t fib_size;
1808f6c4dd3fSScott Long 	u_int32_t fib_addr;
1809f6c4dd3fSScott Long 
181036e0bf6eSScott Long 	debug_called(3);
181136e0bf6eSScott Long 
1812f6c4dd3fSScott Long 	fib_size = cm->cm_fib->Header.Size;
1813f6c4dd3fSScott Long 	fib_addr = cm->cm_fib->Header.ReceiverFibAddress;
181435863739SMike Smith 
181535863739SMike Smith 	/* get the producer/consumer indices */
181635863739SMike Smith 	pi = sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX];
181735863739SMike Smith 	ci = sc->aac_queues->qt_qindex[queue][AAC_CONSUMER_INDEX];
181835863739SMike Smith 
181935863739SMike Smith 	/* wrap the queue? */
182035863739SMike Smith 	if (pi >= aac_qinfo[queue].size)
182135863739SMike Smith 		pi = 0;
182235863739SMike Smith 
182335863739SMike Smith 	/* check for queue full */
182435863739SMike Smith 	if ((pi + 1) == ci) {
182535863739SMike Smith 		error = EBUSY;
182635863739SMike Smith 		goto out;
182735863739SMike Smith 	}
182835863739SMike Smith 
182935863739SMike Smith 	/* populate queue entry */
183035863739SMike Smith 	(sc->aac_qentries[queue] + pi)->aq_fib_size = fib_size;
183135863739SMike Smith 	(sc->aac_qentries[queue] + pi)->aq_fib_addr = fib_addr;
183235863739SMike Smith 
183335863739SMike Smith 	/* update producer index */
183435863739SMike Smith 	sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX] = pi + 1;
183535863739SMike Smith 
1836f6c4dd3fSScott Long 	/*
1837914da7d0SScott Long 	 * To avoid a race with its completion interrupt, place this command on
1838914da7d0SScott Long 	 * the busy queue prior to advertising it to the controller.
1839f6c4dd3fSScott Long 	 */
1840f6c4dd3fSScott Long 	aac_enqueue_busy(cm);
1841f6c4dd3fSScott Long 
184235863739SMike Smith 	/* notify the adapter if we know how */
184335863739SMike Smith 	if (aac_qinfo[queue].notify != 0)
184435863739SMike Smith 		AAC_QNOTIFY(sc, aac_qinfo[queue].notify);
184535863739SMike Smith 
184635863739SMike Smith 	error = 0;
184735863739SMike Smith 
184835863739SMike Smith out:
184935863739SMike Smith 	return(error);
185035863739SMike Smith }
185135863739SMike Smith 
185235863739SMike Smith /*
185336e0bf6eSScott Long  * Atomically remove one entry from the nominated queue, returns 0 on
185436e0bf6eSScott Long  * success or ENOENT if the queue is empty.
185535863739SMike Smith  */
185635863739SMike Smith static int
1857c6eafcf2SScott Long aac_dequeue_fib(struct aac_softc *sc, int queue, u_int32_t *fib_size,
1858c6eafcf2SScott Long 		struct aac_fib **fib_addr)
185935863739SMike Smith {
186035863739SMike Smith 	u_int32_t pi, ci;
1861149af931SScott Long 	u_int32_t fib_index;
18629e2e96d8SScott Long 	int error;
1863f6c4dd3fSScott Long 	int notify;
186435863739SMike Smith 
186535863739SMike Smith 	debug_called(3);
186635863739SMike Smith 
186735863739SMike Smith 	/* get the producer/consumer indices */
186835863739SMike Smith 	pi = sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX];
186935863739SMike Smith 	ci = sc->aac_queues->qt_qindex[queue][AAC_CONSUMER_INDEX];
187035863739SMike Smith 
187135863739SMike Smith 	/* check for queue empty */
187235863739SMike Smith 	if (ci == pi) {
187335863739SMike Smith 		error = ENOENT;
187435863739SMike Smith 		goto out;
187535863739SMike Smith 	}
187635863739SMike Smith 
1877f6c4dd3fSScott Long 	notify = 0;
1878f6c4dd3fSScott Long 	if (ci == pi + 1)
1879f6c4dd3fSScott Long 		notify++;
1880f6c4dd3fSScott Long 
188135863739SMike Smith 	/* wrap the queue? */
188235863739SMike Smith 	if (ci >= aac_qinfo[queue].size)
188335863739SMike Smith 		ci = 0;
188435863739SMike Smith 
188535863739SMike Smith 	/* fetch the entry */
188635863739SMike Smith 	*fib_size = (sc->aac_qentries[queue] + ci)->aq_fib_size;
1887149af931SScott Long 
1888149af931SScott Long 	switch (queue) {
1889149af931SScott Long 	case AAC_HOST_NORM_CMD_QUEUE:
1890149af931SScott Long 	case AAC_HOST_HIGH_CMD_QUEUE:
1891149af931SScott Long 		/*
1892149af931SScott Long 		 * The aq_fib_addr is only 32 bits wide so it can't be counted
1893149af931SScott Long 		 * on to hold an address.  For AIF's, the adapter assumes
1894149af931SScott Long 		 * that it's giving us an address into the array of AIF fibs.
1895149af931SScott Long 		 * Therefore, we have to convert it to an index.
1896149af931SScott Long 		 */
1897149af931SScott Long 		fib_index = (sc->aac_qentries[queue] + ci)->aq_fib_addr /
1898149af931SScott Long 			sizeof(struct aac_fib);
1899149af931SScott Long 		*fib_addr = &sc->aac_common->ac_fibs[fib_index];
1900149af931SScott Long 		break;
1901149af931SScott Long 
1902149af931SScott Long 	case AAC_HOST_NORM_RESP_QUEUE:
1903149af931SScott Long 	case AAC_HOST_HIGH_RESP_QUEUE:
1904149af931SScott Long 	{
1905149af931SScott Long 		struct aac_command *cm;
1906149af931SScott Long 
1907149af931SScott Long 		/*
1908149af931SScott Long 		 * As above, an index is used instead of an actual address.
1909149af931SScott Long 		 * Gotta shift the index to account for the fast response
1910149af931SScott Long 		 * bit.  No other correction is needed since this value was
1911149af931SScott Long 		 * originally provided by the driver via the SenderFibAddress
1912149af931SScott Long 		 * field.
1913149af931SScott Long 		 */
1914149af931SScott Long 		fib_index = (sc->aac_qentries[queue] + ci)->aq_fib_addr;
1915149af931SScott Long 		cm = sc->aac_commands + (fib_index >> 1);
1916149af931SScott Long 		*fib_addr = cm->cm_fib;
191735863739SMike Smith 
1918f30ac74cSScott Long 		/*
1919f30ac74cSScott Long 		 * Is this a fast response? If it is, update the fib fields in
1920149af931SScott Long 		 * local memory since the whole fib isn't DMA'd back up.
1921f30ac74cSScott Long 		 */
1922149af931SScott Long 		if (fib_index & 0x01) {
1923f30ac74cSScott Long 			(*fib_addr)->Header.XferState |= AAC_FIBSTATE_DONEADAP;
1924f30ac74cSScott Long 			*((u_int32_t*)((*fib_addr)->data)) = AAC_ERROR_NORMAL;
1925f30ac74cSScott Long 		}
1926149af931SScott Long 		break;
1927149af931SScott Long 	}
1928149af931SScott Long 	default:
1929149af931SScott Long 		panic("Invalid queue in aac_dequeue_fib()");
1930149af931SScott Long 		break;
1931149af931SScott Long 	}
1932149af931SScott Long 
193335863739SMike Smith 	/* update consumer index */
193435863739SMike Smith 	sc->aac_queues->qt_qindex[queue][AAC_CONSUMER_INDEX] = ci + 1;
193535863739SMike Smith 
193635863739SMike Smith 	/* if we have made the queue un-full, notify the adapter */
1937f6c4dd3fSScott Long 	if (notify && (aac_qinfo[queue].notify != 0))
193835863739SMike Smith 		AAC_QNOTIFY(sc, aac_qinfo[queue].notify);
193935863739SMike Smith 	error = 0;
194035863739SMike Smith 
194135863739SMike Smith out:
194235863739SMike Smith 	return(error);
194335863739SMike Smith }
194435863739SMike Smith 
1945914da7d0SScott Long /*
194636e0bf6eSScott Long  * Put our response to an Adapter Initialed Fib on the response queue
194736e0bf6eSScott Long  */
194836e0bf6eSScott Long static int
194936e0bf6eSScott Long aac_enqueue_response(struct aac_softc *sc, int queue, struct aac_fib *fib)
195036e0bf6eSScott Long {
195136e0bf6eSScott Long 	u_int32_t pi, ci;
19529e2e96d8SScott Long 	int error;
195336e0bf6eSScott Long 	u_int32_t fib_size;
195436e0bf6eSScott Long 	u_int32_t fib_addr;
195536e0bf6eSScott Long 
195636e0bf6eSScott Long 	debug_called(1);
195736e0bf6eSScott Long 
195836e0bf6eSScott Long 	/* Tell the adapter where the FIB is */
195936e0bf6eSScott Long 	fib_size = fib->Header.Size;
196036e0bf6eSScott Long 	fib_addr = fib->Header.SenderFibAddress;
196136e0bf6eSScott Long 	fib->Header.ReceiverFibAddress = fib_addr;
196236e0bf6eSScott Long 
196336e0bf6eSScott Long 	/* get the producer/consumer indices */
196436e0bf6eSScott Long 	pi = sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX];
196536e0bf6eSScott Long 	ci = sc->aac_queues->qt_qindex[queue][AAC_CONSUMER_INDEX];
196636e0bf6eSScott Long 
196736e0bf6eSScott Long 	/* wrap the queue? */
196836e0bf6eSScott Long 	if (pi >= aac_qinfo[queue].size)
196936e0bf6eSScott Long 		pi = 0;
197036e0bf6eSScott Long 
197136e0bf6eSScott Long 	/* check for queue full */
197236e0bf6eSScott Long 	if ((pi + 1) == ci) {
197336e0bf6eSScott Long 		error = EBUSY;
197436e0bf6eSScott Long 		goto out;
197536e0bf6eSScott Long 	}
197636e0bf6eSScott Long 
197736e0bf6eSScott Long 	/* populate queue entry */
197836e0bf6eSScott Long 	(sc->aac_qentries[queue] + pi)->aq_fib_size = fib_size;
197936e0bf6eSScott Long 	(sc->aac_qentries[queue] + pi)->aq_fib_addr = fib_addr;
198036e0bf6eSScott Long 
198136e0bf6eSScott Long 	/* update producer index */
198236e0bf6eSScott Long 	sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX] = pi + 1;
198336e0bf6eSScott Long 
198436e0bf6eSScott Long 	/* notify the adapter if we know how */
198536e0bf6eSScott Long 	if (aac_qinfo[queue].notify != 0)
198636e0bf6eSScott Long 		AAC_QNOTIFY(sc, aac_qinfo[queue].notify);
198736e0bf6eSScott Long 
198836e0bf6eSScott Long 	error = 0;
198936e0bf6eSScott Long 
199036e0bf6eSScott Long out:
199136e0bf6eSScott Long 	return(error);
199236e0bf6eSScott Long }
199336e0bf6eSScott Long 
1994914da7d0SScott Long /*
19950b94a66eSMike Smith  * Check for commands that have been outstanding for a suspiciously long time,
19960b94a66eSMike Smith  * and complain about them.
19970b94a66eSMike Smith  */
19980b94a66eSMike Smith static void
19990b94a66eSMike Smith aac_timeout(struct aac_softc *sc)
20000b94a66eSMike Smith {
20010b94a66eSMike Smith 	struct aac_command *cm;
20020b94a66eSMike Smith 	time_t deadline;
20030b94a66eSMike Smith 
2004f6c4dd3fSScott Long 	/*
200570545d1aSScott Long 	 * Traverse the busy command list, bitch about late commands once
2006914da7d0SScott Long 	 * only.
2007914da7d0SScott Long 	 */
20080b94a66eSMike Smith 	deadline = time_second - AAC_CMD_TIMEOUT;
20090b94a66eSMike Smith 	TAILQ_FOREACH(cm, &sc->aac_busy, cm_link) {
2010f6c4dd3fSScott Long 		if ((cm->cm_timestamp  < deadline)
2011f6c4dd3fSScott Long 			/* && !(cm->cm_flags & AAC_CMD_TIMEDOUT) */) {
20120b94a66eSMike Smith 			cm->cm_flags |= AAC_CMD_TIMEDOUT;
2013914da7d0SScott Long 			device_printf(sc->aac_dev,
2014914da7d0SScott Long 				      "COMMAND %p TIMEOUT AFTER %d SECONDS\n",
2015f6c4dd3fSScott Long 				      cm, (int)(time_second-cm->cm_timestamp));
20160b94a66eSMike Smith 			AAC_PRINT_FIB(sc, cm->cm_fib);
20170b94a66eSMike Smith 		}
20180b94a66eSMike Smith 	}
20190b94a66eSMike Smith 
20200b94a66eSMike Smith 	return;
20210b94a66eSMike Smith }
20220b94a66eSMike Smith 
2023914da7d0SScott Long /*
2024914da7d0SScott Long  * Interface Function Vectors
2025914da7d0SScott Long  */
202635863739SMike Smith 
2027914da7d0SScott Long /*
202835863739SMike Smith  * Read the current firmware status word.
202935863739SMike Smith  */
203035863739SMike Smith static int
203135863739SMike Smith aac_sa_get_fwstatus(struct aac_softc *sc)
203235863739SMike Smith {
203335863739SMike Smith 	debug_called(3);
203435863739SMike Smith 
203535863739SMike Smith 	return(AAC_GETREG4(sc, AAC_SA_FWSTATUS));
203635863739SMike Smith }
203735863739SMike Smith 
203835863739SMike Smith static int
203935863739SMike Smith aac_rx_get_fwstatus(struct aac_softc *sc)
204035863739SMike Smith {
204135863739SMike Smith 	debug_called(3);
204235863739SMike Smith 
204335863739SMike Smith 	return(AAC_GETREG4(sc, AAC_RX_FWSTATUS));
204435863739SMike Smith }
204535863739SMike Smith 
2046b3457b51SScott Long static int
2047b3457b51SScott Long aac_fa_get_fwstatus(struct aac_softc *sc)
2048b3457b51SScott Long {
2049b3457b51SScott Long 	int val;
2050b3457b51SScott Long 
2051b3457b51SScott Long 	debug_called(3);
2052b3457b51SScott Long 
2053b3457b51SScott Long 	val = AAC_GETREG4(sc, AAC_FA_FWSTATUS);
2054b3457b51SScott Long 	return (val);
2055b3457b51SScott Long }
2056b3457b51SScott Long 
2057914da7d0SScott Long /*
205835863739SMike Smith  * Notify the controller of a change in a given queue
205935863739SMike Smith  */
206035863739SMike Smith 
206135863739SMike Smith static void
206235863739SMike Smith aac_sa_qnotify(struct aac_softc *sc, int qbit)
206335863739SMike Smith {
206435863739SMike Smith 	debug_called(3);
206535863739SMike Smith 
206635863739SMike Smith 	AAC_SETREG2(sc, AAC_SA_DOORBELL1_SET, qbit);
206735863739SMike Smith }
206835863739SMike Smith 
206935863739SMike Smith static void
207035863739SMike Smith aac_rx_qnotify(struct aac_softc *sc, int qbit)
207135863739SMike Smith {
207235863739SMike Smith 	debug_called(3);
207335863739SMike Smith 
207435863739SMike Smith 	AAC_SETREG4(sc, AAC_RX_IDBR, qbit);
207535863739SMike Smith }
207635863739SMike Smith 
2077b3457b51SScott Long static void
2078b3457b51SScott Long aac_fa_qnotify(struct aac_softc *sc, int qbit)
2079b3457b51SScott Long {
2080b3457b51SScott Long 	debug_called(3);
2081b3457b51SScott Long 
2082b3457b51SScott Long 	AAC_SETREG2(sc, AAC_FA_DOORBELL1, qbit);
2083b3457b51SScott Long 	AAC_FA_HACK(sc);
2084b3457b51SScott Long }
2085b3457b51SScott Long 
2086914da7d0SScott Long /*
208735863739SMike Smith  * Get the interrupt reason bits
208835863739SMike Smith  */
208935863739SMike Smith static int
209035863739SMike Smith aac_sa_get_istatus(struct aac_softc *sc)
209135863739SMike Smith {
209235863739SMike Smith 	debug_called(3);
209335863739SMike Smith 
209435863739SMike Smith 	return(AAC_GETREG2(sc, AAC_SA_DOORBELL0));
209535863739SMike Smith }
209635863739SMike Smith 
209735863739SMike Smith static int
209835863739SMike Smith aac_rx_get_istatus(struct aac_softc *sc)
209935863739SMike Smith {
210035863739SMike Smith 	debug_called(3);
210135863739SMike Smith 
210235863739SMike Smith 	return(AAC_GETREG4(sc, AAC_RX_ODBR));
210335863739SMike Smith }
210435863739SMike Smith 
2105b3457b51SScott Long static int
2106b3457b51SScott Long aac_fa_get_istatus(struct aac_softc *sc)
2107b3457b51SScott Long {
2108b3457b51SScott Long 	int val;
2109b3457b51SScott Long 
2110b3457b51SScott Long 	debug_called(3);
2111b3457b51SScott Long 
2112b3457b51SScott Long 	val = AAC_GETREG2(sc, AAC_FA_DOORBELL0);
2113b3457b51SScott Long 	return (val);
2114b3457b51SScott Long }
2115b3457b51SScott Long 
2116914da7d0SScott Long /*
211735863739SMike Smith  * Clear some interrupt reason bits
211835863739SMike Smith  */
211935863739SMike Smith static void
212035863739SMike Smith aac_sa_clear_istatus(struct aac_softc *sc, int mask)
212135863739SMike Smith {
212235863739SMike Smith 	debug_called(3);
212335863739SMike Smith 
212435863739SMike Smith 	AAC_SETREG2(sc, AAC_SA_DOORBELL0_CLEAR, mask);
212535863739SMike Smith }
212635863739SMike Smith 
212735863739SMike Smith static void
212835863739SMike Smith aac_rx_clear_istatus(struct aac_softc *sc, int mask)
212935863739SMike Smith {
213035863739SMike Smith 	debug_called(3);
213135863739SMike Smith 
213235863739SMike Smith 	AAC_SETREG4(sc, AAC_RX_ODBR, mask);
213335863739SMike Smith }
213435863739SMike Smith 
2135b3457b51SScott Long static void
2136b3457b51SScott Long aac_fa_clear_istatus(struct aac_softc *sc, int mask)
2137b3457b51SScott Long {
2138b3457b51SScott Long 	debug_called(3);
2139b3457b51SScott Long 
2140b3457b51SScott Long 	AAC_SETREG2(sc, AAC_FA_DOORBELL0_CLEAR, mask);
2141b3457b51SScott Long 	AAC_FA_HACK(sc);
2142b3457b51SScott Long }
2143b3457b51SScott Long 
2144914da7d0SScott Long /*
214535863739SMike Smith  * Populate the mailbox and set the command word
214635863739SMike Smith  */
214735863739SMike Smith static void
214835863739SMike Smith aac_sa_set_mailbox(struct aac_softc *sc, u_int32_t command,
214935863739SMike Smith 		u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3)
215035863739SMike Smith {
215135863739SMike Smith 	debug_called(4);
215235863739SMike Smith 
215335863739SMike Smith 	AAC_SETREG4(sc, AAC_SA_MAILBOX, command);
215435863739SMike Smith 	AAC_SETREG4(sc, AAC_SA_MAILBOX + 4, arg0);
215535863739SMike Smith 	AAC_SETREG4(sc, AAC_SA_MAILBOX + 8, arg1);
215635863739SMike Smith 	AAC_SETREG4(sc, AAC_SA_MAILBOX + 12, arg2);
215735863739SMike Smith 	AAC_SETREG4(sc, AAC_SA_MAILBOX + 16, arg3);
215835863739SMike Smith }
215935863739SMike Smith 
216035863739SMike Smith static void
216135863739SMike Smith aac_rx_set_mailbox(struct aac_softc *sc, u_int32_t command,
216235863739SMike Smith 		u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3)
216335863739SMike Smith {
216435863739SMike Smith 	debug_called(4);
216535863739SMike Smith 
216635863739SMike Smith 	AAC_SETREG4(sc, AAC_RX_MAILBOX, command);
216735863739SMike Smith 	AAC_SETREG4(sc, AAC_RX_MAILBOX + 4, arg0);
216835863739SMike Smith 	AAC_SETREG4(sc, AAC_RX_MAILBOX + 8, arg1);
216935863739SMike Smith 	AAC_SETREG4(sc, AAC_RX_MAILBOX + 12, arg2);
217035863739SMike Smith 	AAC_SETREG4(sc, AAC_RX_MAILBOX + 16, arg3);
217135863739SMike Smith }
217235863739SMike Smith 
2173b3457b51SScott Long static void
2174b3457b51SScott Long aac_fa_set_mailbox(struct aac_softc *sc, u_int32_t command,
2175b3457b51SScott Long 		u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3)
2176b3457b51SScott Long {
2177b3457b51SScott Long 	debug_called(4);
2178b3457b51SScott Long 
2179b3457b51SScott Long 	AAC_SETREG4(sc, AAC_FA_MAILBOX, command);
2180b3457b51SScott Long 	AAC_FA_HACK(sc);
2181b3457b51SScott Long 	AAC_SETREG4(sc, AAC_FA_MAILBOX + 4, arg0);
2182b3457b51SScott Long 	AAC_FA_HACK(sc);
2183b3457b51SScott Long 	AAC_SETREG4(sc, AAC_FA_MAILBOX + 8, arg1);
2184b3457b51SScott Long 	AAC_FA_HACK(sc);
2185b3457b51SScott Long 	AAC_SETREG4(sc, AAC_FA_MAILBOX + 12, arg2);
2186b3457b51SScott Long 	AAC_FA_HACK(sc);
2187b3457b51SScott Long 	AAC_SETREG4(sc, AAC_FA_MAILBOX + 16, arg3);
2188b3457b51SScott Long 	AAC_FA_HACK(sc);
2189b3457b51SScott Long }
2190b3457b51SScott Long 
2191914da7d0SScott Long /*
219235863739SMike Smith  * Fetch the immediate command status word
219335863739SMike Smith  */
219435863739SMike Smith static int
2195a6d35632SScott Long aac_sa_get_mailbox(struct aac_softc *sc, int mb)
219635863739SMike Smith {
219735863739SMike Smith 	debug_called(4);
219835863739SMike Smith 
2199a6d35632SScott Long 	return(AAC_GETREG4(sc, AAC_SA_MAILBOX + (mb * 4)));
220035863739SMike Smith }
220135863739SMike Smith 
220235863739SMike Smith static int
2203a6d35632SScott Long aac_rx_get_mailbox(struct aac_softc *sc, int mb)
220435863739SMike Smith {
220535863739SMike Smith 	debug_called(4);
220635863739SMike Smith 
2207a6d35632SScott Long 	return(AAC_GETREG4(sc, AAC_RX_MAILBOX + (mb * 4)));
220835863739SMike Smith }
220935863739SMike Smith 
2210b3457b51SScott Long static int
2211a6d35632SScott Long aac_fa_get_mailbox(struct aac_softc *sc, int mb)
2212b3457b51SScott Long {
2213b3457b51SScott Long 	int val;
2214b3457b51SScott Long 
2215b3457b51SScott Long 	debug_called(4);
2216b3457b51SScott Long 
2217a6d35632SScott Long 	val = AAC_GETREG4(sc, AAC_FA_MAILBOX + (mb * 4));
2218b3457b51SScott Long 	return (val);
2219b3457b51SScott Long }
2220b3457b51SScott Long 
2221914da7d0SScott Long /*
222235863739SMike Smith  * Set/clear interrupt masks
222335863739SMike Smith  */
222435863739SMike Smith static void
222535863739SMike Smith aac_sa_set_interrupts(struct aac_softc *sc, int enable)
222635863739SMike Smith {
222735863739SMike Smith 	debug(2, "%sable interrupts", enable ? "en" : "dis");
222835863739SMike Smith 
222935863739SMike Smith 	if (enable) {
223035863739SMike Smith 		AAC_SETREG2((sc), AAC_SA_MASK0_CLEAR, AAC_DB_INTERRUPTS);
223135863739SMike Smith 	} else {
223235863739SMike Smith 		AAC_SETREG2((sc), AAC_SA_MASK0_SET, ~0);
223335863739SMike Smith 	}
223435863739SMike Smith }
223535863739SMike Smith 
223635863739SMike Smith static void
223735863739SMike Smith aac_rx_set_interrupts(struct aac_softc *sc, int enable)
223835863739SMike Smith {
223935863739SMike Smith 	debug(2, "%sable interrupts", enable ? "en" : "dis");
224035863739SMike Smith 
224135863739SMike Smith 	if (enable) {
224235863739SMike Smith 		AAC_SETREG4(sc, AAC_RX_OIMR, ~AAC_DB_INTERRUPTS);
224335863739SMike Smith 	} else {
224435863739SMike Smith 		AAC_SETREG4(sc, AAC_RX_OIMR, ~0);
224535863739SMike Smith 	}
224635863739SMike Smith }
224735863739SMike Smith 
2248b3457b51SScott Long static void
2249b3457b51SScott Long aac_fa_set_interrupts(struct aac_softc *sc, int enable)
2250b3457b51SScott Long {
2251b3457b51SScott Long 	debug(2, "%sable interrupts", enable ? "en" : "dis");
2252b3457b51SScott Long 
2253b3457b51SScott Long 	if (enable) {
2254b3457b51SScott Long 		AAC_SETREG2((sc), AAC_FA_MASK0_CLEAR, AAC_DB_INTERRUPTS);
2255b3457b51SScott Long 		AAC_FA_HACK(sc);
2256b3457b51SScott Long 	} else {
2257b3457b51SScott Long 		AAC_SETREG2((sc), AAC_FA_MASK0, ~0);
2258b3457b51SScott Long 		AAC_FA_HACK(sc);
2259b3457b51SScott Long 	}
2260b3457b51SScott Long }
2261b3457b51SScott Long 
2262914da7d0SScott Long /*
2263914da7d0SScott Long  * Debugging and Diagnostics
2264914da7d0SScott Long  */
226535863739SMike Smith 
2266914da7d0SScott Long /*
226735863739SMike Smith  * Print some information about the controller.
226835863739SMike Smith  */
226935863739SMike Smith static void
227035863739SMike Smith aac_describe_controller(struct aac_softc *sc)
227135863739SMike Smith {
2272cbfd045bSScott Long 	struct aac_fib *fib;
227335863739SMike Smith 	struct aac_adapter_info	*info;
227435863739SMike Smith 
227535863739SMike Smith 	debug_called(2);
227635863739SMike Smith 
2277fe3cb0e1SScott Long 	aac_alloc_sync_fib(sc, &fib, 0);
2278cbfd045bSScott Long 
2279cbfd045bSScott Long 	fib->data[0] = 0;
2280cbfd045bSScott Long 	if (aac_sync_fib(sc, RequestAdapterInfo, 0, fib, 1)) {
228135863739SMike Smith 		device_printf(sc->aac_dev, "RequestAdapterInfo failed\n");
2282fe3cb0e1SScott Long 		aac_release_sync_fib(sc);
228335863739SMike Smith 		return;
228435863739SMike Smith 	}
2285cbfd045bSScott Long 	info = (struct aac_adapter_info *)&fib->data[0];
228635863739SMike Smith 
228736e0bf6eSScott Long 	device_printf(sc->aac_dev, "%s %dMHz, %dMB cache memory, %s\n",
2288c6eafcf2SScott Long 		      aac_describe_code(aac_cpu_variant, info->CpuVariant),
228936e0bf6eSScott Long 		      info->ClockSpeed, info->BufferMem / (1024 * 1024),
2290914da7d0SScott Long 		      aac_describe_code(aac_battery_platform,
2291914da7d0SScott Long 					info->batteryPlatform));
229235863739SMike Smith 
229335863739SMike Smith 	/* save the kernel revision structure for later use */
229435863739SMike Smith 	sc->aac_revision = info->KernelRevision;
229536e0bf6eSScott Long 	device_printf(sc->aac_dev, "Kernel %d.%d-%d, Build %d, S/N %6X\n",
229635863739SMike Smith 		      info->KernelRevision.external.comp.major,
229735863739SMike Smith 		      info->KernelRevision.external.comp.minor,
229835863739SMike Smith 		      info->KernelRevision.external.comp.dash,
229936e0bf6eSScott Long 		      info->KernelRevision.buildNumber,
230036e0bf6eSScott Long 		      (u_int32_t)(info->SerialNumber & 0xffffff));
2301fe3cb0e1SScott Long 
2302fe3cb0e1SScott Long 	aac_release_sync_fib(sc);
2303a6d35632SScott Long 
2304a6d35632SScott Long 	if (1 || bootverbose) {
2305a6d35632SScott Long 		device_printf(sc->aac_dev, "Supported Options=%b\n",
2306a6d35632SScott Long 			      sc->supported_options,
2307a6d35632SScott Long 			      "\20"
2308a6d35632SScott Long 			      "\1SNAPSHOT"
2309a6d35632SScott Long 			      "\2CLUSTERS"
2310a6d35632SScott Long 			      "\3WCACHE"
2311a6d35632SScott Long 			      "\4DATA64"
2312a6d35632SScott Long 			      "\5HOSTTIME"
2313a6d35632SScott Long 			      "\6RAID50"
2314a6d35632SScott Long 			      "\7WINDOW4GB"
2315a6d35632SScott Long 			      "\10SCSIUPGD"
2316a6d35632SScott Long 			      "\11SOFTERR"
2317a6d35632SScott Long 			      "\12NORECOND"
2318a6d35632SScott Long 			      "\13SGMAP64"
2319a6d35632SScott Long 			      "\14ALARM"
2320a6d35632SScott Long 			      "\15NONDASD");
2321a6d35632SScott Long 	}
232235863739SMike Smith }
232335863739SMike Smith 
2324914da7d0SScott Long /*
232535863739SMike Smith  * Look up a text description of a numeric error code and return a pointer to
232635863739SMike Smith  * same.
232735863739SMike Smith  */
232835863739SMike Smith static char *
232935863739SMike Smith aac_describe_code(struct aac_code_lookup *table, u_int32_t code)
233035863739SMike Smith {
233135863739SMike Smith 	int i;
233235863739SMike Smith 
233335863739SMike Smith 	for (i = 0; table[i].string != NULL; i++)
233435863739SMike Smith 		if (table[i].code == code)
233535863739SMike Smith 			return(table[i].string);
233635863739SMike Smith 	return(table[i + 1].string);
233735863739SMike Smith }
233835863739SMike Smith 
2339914da7d0SScott Long /*
2340914da7d0SScott Long  * Management Interface
2341914da7d0SScott Long  */
234235863739SMike Smith 
234335863739SMike Smith static int
2344c3d15322SScott Long aac_open(dev_t dev, int flags, int fmt, d_thread_t *td)
234535863739SMike Smith {
2346914da7d0SScott Long 	struct aac_softc *sc;
234735863739SMike Smith 
234835863739SMike Smith 	debug_called(2);
234935863739SMike Smith 
2350914da7d0SScott Long 	sc = dev->si_drv1;
2351914da7d0SScott Long 
235235863739SMike Smith 	/* Check to make sure the device isn't already open */
235335863739SMike Smith 	if (sc->aac_state & AAC_STATE_OPEN) {
235435863739SMike Smith 		return EBUSY;
235535863739SMike Smith 	}
235635863739SMike Smith 	sc->aac_state |= AAC_STATE_OPEN;
235735863739SMike Smith 
235835863739SMike Smith 	return 0;
235935863739SMike Smith }
236035863739SMike Smith 
236135863739SMike Smith static int
2362c3d15322SScott Long aac_close(dev_t dev, int flags, int fmt, d_thread_t *td)
236335863739SMike Smith {
2364914da7d0SScott Long 	struct aac_softc *sc;
236535863739SMike Smith 
236635863739SMike Smith 	debug_called(2);
236735863739SMike Smith 
2368914da7d0SScott Long 	sc = dev->si_drv1;
2369914da7d0SScott Long 
237035863739SMike Smith 	/* Mark this unit as no longer open  */
237135863739SMike Smith 	sc->aac_state &= ~AAC_STATE_OPEN;
237235863739SMike Smith 
237335863739SMike Smith 	return 0;
237435863739SMike Smith }
237535863739SMike Smith 
237635863739SMike Smith static int
2377c3d15322SScott Long aac_ioctl(dev_t dev, u_long cmd, caddr_t arg, int flag, d_thread_t *td)
237835863739SMike Smith {
2379914da7d0SScott Long 	union aac_statrequest *as;
2380914da7d0SScott Long 	struct aac_softc *sc;
23810b94a66eSMike Smith 	int error = 0;
23820b94a66eSMike Smith 	int i;
238335863739SMike Smith 
238435863739SMike Smith 	debug_called(2);
238535863739SMike Smith 
2386914da7d0SScott Long 	as = (union aac_statrequest *)arg;
2387914da7d0SScott Long 	sc = dev->si_drv1;
2388914da7d0SScott Long 
238935863739SMike Smith 	switch (cmd) {
23900b94a66eSMike Smith 	case AACIO_STATS:
23910b94a66eSMike Smith 		switch (as->as_item) {
23920b94a66eSMike Smith 		case AACQ_FREE:
23930b94a66eSMike Smith 		case AACQ_BIO:
23940b94a66eSMike Smith 		case AACQ_READY:
23950b94a66eSMike Smith 		case AACQ_BUSY:
23960b94a66eSMike Smith 		case AACQ_COMPLETE:
2397c6eafcf2SScott Long 			bcopy(&sc->aac_qstat[as->as_item], &as->as_qstat,
2398c6eafcf2SScott Long 			      sizeof(struct aac_qstat));
23990b94a66eSMike Smith 			break;
24000b94a66eSMike Smith 		default:
24010b94a66eSMike Smith 			error = ENOENT;
24020b94a66eSMike Smith 			break;
24030b94a66eSMike Smith 		}
24040b94a66eSMike Smith 	break;
24050b94a66eSMike Smith 
240635863739SMike Smith 	case FSACTL_SENDFIB:
2407fb0c27d7SScott Long 		arg = *(caddr_t*)arg;
2408fb0c27d7SScott Long 	case FSACTL_LNX_SENDFIB:
24090b94a66eSMike Smith 		debug(1, "FSACTL_SENDFIB");
241035863739SMike Smith 		error = aac_ioctl_sendfib(sc, arg);
241135863739SMike Smith 		break;
241235863739SMike Smith 	case FSACTL_AIF_THREAD:
2413fb0c27d7SScott Long 	case FSACTL_LNX_AIF_THREAD:
24140b94a66eSMike Smith 		debug(1, "FSACTL_AIF_THREAD");
241535863739SMike Smith 		error = EINVAL;
241635863739SMike Smith 		break;
241735863739SMike Smith 	case FSACTL_OPEN_GET_ADAPTER_FIB:
2418fb0c27d7SScott Long 		arg = *(caddr_t*)arg;
2419fb0c27d7SScott Long 	case FSACTL_LNX_OPEN_GET_ADAPTER_FIB:
24200b94a66eSMike Smith 		debug(1, "FSACTL_OPEN_GET_ADAPTER_FIB");
242135863739SMike Smith 		/*
242235863739SMike Smith 		 * Pass the caller out an AdapterFibContext.
242335863739SMike Smith 		 *
242435863739SMike Smith 		 * Note that because we only support one opener, we
242535863739SMike Smith 		 * basically ignore this.  Set the caller's context to a magic
242635863739SMike Smith 		 * number just in case.
24270b94a66eSMike Smith 		 *
24280b94a66eSMike Smith 		 * The Linux code hands the driver a pointer into kernel space,
24290b94a66eSMike Smith 		 * and then trusts it when the caller hands it back.  Aiee!
2430914da7d0SScott Long 		 * Here, we give it the proc pointer of the per-adapter aif
2431914da7d0SScott Long 		 * thread. It's only used as a sanity check in other calls.
243235863739SMike Smith 		 */
243336e0bf6eSScott Long 		i = (int)sc->aifthread;
243435863739SMike Smith 		error = copyout(&i, arg, sizeof(i));
243535863739SMike Smith 		break;
243635863739SMike Smith 	case FSACTL_GET_NEXT_ADAPTER_FIB:
2437fb0c27d7SScott Long 		arg = *(caddr_t*)arg;
2438fb0c27d7SScott Long 	case FSACTL_LNX_GET_NEXT_ADAPTER_FIB:
24390b94a66eSMike Smith 		debug(1, "FSACTL_GET_NEXT_ADAPTER_FIB");
2440fb0c27d7SScott Long 		error = aac_getnext_aif(sc, arg);
244135863739SMike Smith 		break;
244235863739SMike Smith 	case FSACTL_CLOSE_GET_ADAPTER_FIB:
2443fb0c27d7SScott Long 	case FSACTL_LNX_CLOSE_GET_ADAPTER_FIB:
24440b94a66eSMike Smith 		debug(1, "FSACTL_CLOSE_GET_ADAPTER_FIB");
244535863739SMike Smith 		/* don't do anything here */
244635863739SMike Smith 		break;
244735863739SMike Smith 	case FSACTL_MINIPORT_REV_CHECK:
2448fb0c27d7SScott Long 		arg = *(caddr_t*)arg;
2449fb0c27d7SScott Long 	case FSACTL_LNX_MINIPORT_REV_CHECK:
24500b94a66eSMike Smith 		debug(1, "FSACTL_MINIPORT_REV_CHECK");
2451fb0c27d7SScott Long 		error = aac_rev_check(sc, arg);
245235863739SMike Smith 		break;
245336e0bf6eSScott Long 	case FSACTL_QUERY_DISK:
245436e0bf6eSScott Long 		arg = *(caddr_t*)arg;
245536e0bf6eSScott Long 	case FSACTL_LNX_QUERY_DISK:
245636e0bf6eSScott Long 		debug(1, "FSACTL_QUERY_DISK");
245736e0bf6eSScott Long 		error = aac_query_disk(sc, arg);
245836e0bf6eSScott Long 			break;
245936e0bf6eSScott Long 	case FSACTL_DELETE_DISK:
246036e0bf6eSScott Long 	case FSACTL_LNX_DELETE_DISK:
2461914da7d0SScott Long 		/*
2462914da7d0SScott Long 		 * We don't trust the underland to tell us when to delete a
2463914da7d0SScott Long 		 * container, rather we rely on an AIF coming from the
2464914da7d0SScott Long 		 * controller
2465914da7d0SScott Long 		 */
246636e0bf6eSScott Long 		error = 0;
246736e0bf6eSScott Long 		break;
246835863739SMike Smith 	default:
2469b3457b51SScott Long 		debug(1, "unsupported cmd 0x%lx\n", cmd);
247035863739SMike Smith 		error = EINVAL;
247135863739SMike Smith 		break;
247235863739SMike Smith 	}
247335863739SMike Smith 	return(error);
247435863739SMike Smith }
247535863739SMike Smith 
2476b3457b51SScott Long static int
2477c3d15322SScott Long aac_poll(dev_t dev, int poll_events, d_thread_t *td)
2478b3457b51SScott Long {
2479b3457b51SScott Long 	struct aac_softc *sc;
2480b3457b51SScott Long 	int revents;
2481b3457b51SScott Long 
2482b3457b51SScott Long 	sc = dev->si_drv1;
2483b3457b51SScott Long 	revents = 0;
2484b3457b51SScott Long 
2485c3d15322SScott Long 	AAC_LOCK_ACQUIRE(&sc->aac_aifq_lock);
2486b3457b51SScott Long 	if ((poll_events & (POLLRDNORM | POLLIN)) != 0) {
2487b3457b51SScott Long 		if (sc->aac_aifq_tail != sc->aac_aifq_head)
2488b3457b51SScott Long 			revents |= poll_events & (POLLIN | POLLRDNORM);
2489b3457b51SScott Long 	}
2490b3457b51SScott Long 	AAC_LOCK_RELEASE(&sc->aac_aifq_lock);
2491b3457b51SScott Long 
2492b3457b51SScott Long 	if (revents == 0) {
2493b3457b51SScott Long 		if (poll_events & (POLLIN | POLLRDNORM))
2494b3457b51SScott Long 			selrecord(td, &sc->rcv_select);
2495b3457b51SScott Long 	}
2496b3457b51SScott Long 
2497b3457b51SScott Long 	return (revents);
2498b3457b51SScott Long }
2499b3457b51SScott Long 
2500914da7d0SScott Long /*
250135863739SMike Smith  * Send a FIB supplied from userspace
250235863739SMike Smith  */
250335863739SMike Smith static int
250435863739SMike Smith aac_ioctl_sendfib(struct aac_softc *sc, caddr_t ufib)
250535863739SMike Smith {
250635863739SMike Smith 	struct aac_command *cm;
250735863739SMike Smith 	int size, error;
250835863739SMike Smith 
250935863739SMike Smith 	debug_called(2);
251035863739SMike Smith 
251135863739SMike Smith 	cm = NULL;
251235863739SMike Smith 
251335863739SMike Smith 	/*
251435863739SMike Smith 	 * Get a command
251535863739SMike Smith 	 */
2516ae543596SScott Long 	AAC_LOCK_ACQUIRE(&sc->aac_io_lock);
251735863739SMike Smith 	if (aac_alloc_command(sc, &cm)) {
251835863739SMike Smith 		error = EBUSY;
251935863739SMike Smith 		goto out;
252035863739SMike Smith 	}
252135863739SMike Smith 
252235863739SMike Smith 	/*
252335863739SMike Smith 	 * Fetch the FIB header, then re-copy to get data as well.
252435863739SMike Smith 	 */
2525914da7d0SScott Long 	if ((error = copyin(ufib, cm->cm_fib,
2526914da7d0SScott Long 			    sizeof(struct aac_fib_header))) != 0)
252735863739SMike Smith 		goto out;
252835863739SMike Smith 	size = cm->cm_fib->Header.Size + sizeof(struct aac_fib_header);
252935863739SMike Smith 	if (size > sizeof(struct aac_fib)) {
2530914da7d0SScott Long 		device_printf(sc->aac_dev, "incoming FIB oversized (%d > %d)\n",
2531914da7d0SScott Long 			      size, sizeof(struct aac_fib));
253235863739SMike Smith 		size = sizeof(struct aac_fib);
253335863739SMike Smith 	}
253435863739SMike Smith 	if ((error = copyin(ufib, cm->cm_fib, size)) != 0)
253535863739SMike Smith 		goto out;
253635863739SMike Smith 	cm->cm_fib->Header.Size = size;
2537f6c4dd3fSScott Long 	cm->cm_timestamp = time_second;
253835863739SMike Smith 
253935863739SMike Smith 	/*
254035863739SMike Smith 	 * Pass the FIB to the controller, wait for it to complete.
254135863739SMike Smith 	 */
2542b3457b51SScott Long 	if ((error = aac_wait_command(cm, 30)) != 0) {	/* XXX user timeout? */
254370545d1aSScott Long 		device_printf(sc->aac_dev,
254470545d1aSScott Long 			      "aac_wait_command return %d\n", error);
254535863739SMike Smith 		goto out;
2546b3457b51SScott Long 	}
254735863739SMike Smith 
254835863739SMike Smith 	/*
254935863739SMike Smith 	 * Copy the FIB and data back out to the caller.
255035863739SMike Smith 	 */
255135863739SMike Smith 	size = cm->cm_fib->Header.Size;
255235863739SMike Smith 	if (size > sizeof(struct aac_fib)) {
2553914da7d0SScott Long 		device_printf(sc->aac_dev, "outbound FIB oversized (%d > %d)\n",
2554914da7d0SScott Long 			      size, sizeof(struct aac_fib));
255535863739SMike Smith 		size = sizeof(struct aac_fib);
255635863739SMike Smith 	}
255735863739SMike Smith 	error = copyout(cm->cm_fib, ufib, size);
255835863739SMike Smith 
255935863739SMike Smith out:
2560f6c4dd3fSScott Long 	if (cm != NULL) {
256135863739SMike Smith 		aac_release_command(cm);
2562f6c4dd3fSScott Long 	}
2563ae543596SScott Long 
2564ae543596SScott Long 	AAC_LOCK_RELEASE(&sc->aac_io_lock);
256535863739SMike Smith 	return(error);
256635863739SMike Smith }
256735863739SMike Smith 
2568914da7d0SScott Long /*
256935863739SMike Smith  * Handle an AIF sent to us by the controller; queue it for later reference.
257036e0bf6eSScott Long  * If the queue fills up, then drop the older entries.
257135863739SMike Smith  */
257235863739SMike Smith static void
257336e0bf6eSScott Long aac_handle_aif(struct aac_softc *sc, struct aac_fib *fib)
257435863739SMike Smith {
257536e0bf6eSScott Long 	struct aac_aif_command *aif;
257636e0bf6eSScott Long 	struct aac_container *co, *co_next;
2577cbfd045bSScott Long 	struct aac_mntinfo *mi;
2578cbfd045bSScott Long 	struct aac_mntinforesp *mir = NULL;
257936e0bf6eSScott Long 	u_int16_t rsize;
2580b3457b51SScott Long 	int next, found;
2581795d7dc0SScott Long 	int count = 0, added = 0, i = 0;
258235863739SMike Smith 
258335863739SMike Smith 	debug_called(2);
258435863739SMike Smith 
258536e0bf6eSScott Long 	aif = (struct aac_aif_command*)&fib->data[0];
258636e0bf6eSScott Long 	aac_print_aif(sc, aif);
258736e0bf6eSScott Long 
258836e0bf6eSScott Long 	/* Is it an event that we should care about? */
258936e0bf6eSScott Long 	switch (aif->command) {
259036e0bf6eSScott Long 	case AifCmdEventNotify:
259136e0bf6eSScott Long 		switch (aif->data.EN.type) {
259236e0bf6eSScott Long 		case AifEnAddContainer:
259336e0bf6eSScott Long 		case AifEnDeleteContainer:
259436e0bf6eSScott Long 			/*
2595914da7d0SScott Long 			 * A container was added or deleted, but the message
2596914da7d0SScott Long 			 * doesn't tell us anything else!  Re-enumerate the
2597914da7d0SScott Long 			 * containers and sort things out.
259836e0bf6eSScott Long 			 */
2599fe3cb0e1SScott Long 			aac_alloc_sync_fib(sc, &fib, 0);
2600cbfd045bSScott Long 			mi = (struct aac_mntinfo *)&fib->data[0];
260136e0bf6eSScott Long 			do {
260236e0bf6eSScott Long 				/*
2603914da7d0SScott Long 				 * Ask the controller for its containers one at
2604914da7d0SScott Long 				 * a time.
2605914da7d0SScott Long 				 * XXX What if the controller's list changes
2606914da7d0SScott Long 				 * midway through this enumaration?
260736e0bf6eSScott Long 				 * XXX This should be done async.
260836e0bf6eSScott Long 				 */
260939ee03c3SScott Long 				bzero(mi, sizeof(struct aac_mntinfo));
261039ee03c3SScott Long 				mi->Command = VM_NameServe;
261139ee03c3SScott Long 				mi->MntType = FT_FILESYS;
2612cbfd045bSScott Long 				mi->MntCount = i;
261336e0bf6eSScott Long 				rsize = sizeof(mir);
2614cbfd045bSScott Long 				if (aac_sync_fib(sc, ContainerCommand, 0, fib,
2615cbfd045bSScott Long 						 sizeof(struct aac_mntinfo))) {
2616795d7dc0SScott Long 					printf("Error probing container %d\n",
2617914da7d0SScott Long 					      i);
261836e0bf6eSScott Long 					continue;
261936e0bf6eSScott Long 				}
2620cbfd045bSScott Long 				mir = (struct aac_mntinforesp *)&fib->data[0];
2621795d7dc0SScott Long 				/* XXX Need to check if count changed */
2622795d7dc0SScott Long 				count = mir->MntRespCount;
262336e0bf6eSScott Long 				/*
2624914da7d0SScott Long 				 * Check the container against our list.
2625914da7d0SScott Long 				 * co->co_found was already set to 0 in a
2626914da7d0SScott Long 				 * previous run.
262736e0bf6eSScott Long 				 */
2628cbfd045bSScott Long 				if ((mir->Status == ST_OK) &&
2629cbfd045bSScott Long 				    (mir->MntTable[0].VolType != CT_NONE)) {
263036e0bf6eSScott Long 					found = 0;
2631914da7d0SScott Long 					TAILQ_FOREACH(co,
2632914da7d0SScott Long 						      &sc->aac_container_tqh,
2633914da7d0SScott Long 						      co_link) {
263436e0bf6eSScott Long 						if (co->co_mntobj.ObjectId ==
2635cbfd045bSScott Long 						    mir->MntTable[0].ObjectId) {
263636e0bf6eSScott Long 							co->co_found = 1;
263736e0bf6eSScott Long 							found = 1;
263836e0bf6eSScott Long 							break;
263936e0bf6eSScott Long 						}
264036e0bf6eSScott Long 					}
2641914da7d0SScott Long 					/*
2642914da7d0SScott Long 					 * If the container matched, continue
2643914da7d0SScott Long 					 * in the list.
2644914da7d0SScott Long 					 */
264536e0bf6eSScott Long 					if (found) {
264636e0bf6eSScott Long 						i++;
264736e0bf6eSScott Long 						continue;
264836e0bf6eSScott Long 					}
264936e0bf6eSScott Long 
265036e0bf6eSScott Long 					/*
2651914da7d0SScott Long 					 * This is a new container.  Do all the
265270545d1aSScott Long 					 * appropriate things to set it up.
265370545d1aSScott Long 					 */
2654cbfd045bSScott Long 					aac_add_container(sc, mir, 1);
265536e0bf6eSScott Long 					added = 1;
265636e0bf6eSScott Long 				}
265736e0bf6eSScott Long 				i++;
2658795d7dc0SScott Long 			} while ((i < count) && (i < AAC_MAX_CONTAINERS));
2659cbfd045bSScott Long 			aac_release_sync_fib(sc);
266036e0bf6eSScott Long 
266136e0bf6eSScott Long 			/*
2662914da7d0SScott Long 			 * Go through our list of containers and see which ones
2663914da7d0SScott Long 			 * were not marked 'found'.  Since the controller didn't
2664914da7d0SScott Long 			 * list them they must have been deleted.  Do the
2665914da7d0SScott Long 			 * appropriate steps to destroy the device.  Also reset
2666914da7d0SScott Long 			 * the co->co_found field.
266736e0bf6eSScott Long 			 */
266836e0bf6eSScott Long 			co = TAILQ_FIRST(&sc->aac_container_tqh);
266936e0bf6eSScott Long 			while (co != NULL) {
267036e0bf6eSScott Long 				if (co->co_found == 0) {
2671914da7d0SScott Long 					device_delete_child(sc->aac_dev,
2672914da7d0SScott Long 							    co->co_disk);
267336e0bf6eSScott Long 					co_next = TAILQ_NEXT(co, co_link);
2674c3d15322SScott Long 					AAC_LOCK_ACQUIRE(&sc->
2675914da7d0SScott Long 							aac_container_lock);
2676914da7d0SScott Long 					TAILQ_REMOVE(&sc->aac_container_tqh, co,
2677914da7d0SScott Long 						     co_link);
2678914da7d0SScott Long 					AAC_LOCK_RELEASE(&sc->
2679914da7d0SScott Long 							 aac_container_lock);
268036e0bf6eSScott Long 					FREE(co, M_AACBUF);
268136e0bf6eSScott Long 					co = co_next;
268236e0bf6eSScott Long 				} else {
268336e0bf6eSScott Long 					co->co_found = 0;
268436e0bf6eSScott Long 					co = TAILQ_NEXT(co, co_link);
268536e0bf6eSScott Long 				}
268636e0bf6eSScott Long 			}
268736e0bf6eSScott Long 
268836e0bf6eSScott Long 			/* Attach the newly created containers */
268936e0bf6eSScott Long 			if (added)
269036e0bf6eSScott Long 				bus_generic_attach(sc->aac_dev);
269136e0bf6eSScott Long 
269236e0bf6eSScott Long 			break;
269336e0bf6eSScott Long 
269436e0bf6eSScott Long 		default:
269536e0bf6eSScott Long 			break;
269636e0bf6eSScott Long 		}
269736e0bf6eSScott Long 
269836e0bf6eSScott Long 	default:
269936e0bf6eSScott Long 		break;
270036e0bf6eSScott Long 	}
270136e0bf6eSScott Long 
270236e0bf6eSScott Long 	/* Copy the AIF data to the AIF queue for ioctl retrieval */
2703c3d15322SScott Long 	AAC_LOCK_ACQUIRE(&sc->aac_aifq_lock);
270435863739SMike Smith 	next = (sc->aac_aifq_head + 1) % AAC_AIFQ_LENGTH;
270535863739SMike Smith 	if (next != sc->aac_aifq_tail) {
270635863739SMike Smith 		bcopy(aif, &sc->aac_aifq[next], sizeof(struct aac_aif_command));
270735863739SMike Smith 		sc->aac_aifq_head = next;
2708b3457b51SScott Long 
2709b3457b51SScott Long 		/* On the off chance that someone is sleeping for an aif... */
271035863739SMike Smith 		if (sc->aac_state & AAC_STATE_AIF_SLEEPER)
271135863739SMike Smith 			wakeup(sc->aac_aifq);
2712b3457b51SScott Long 		/* Wakeup any poll()ers */
2713b3457b51SScott Long 		selwakeup(&sc->rcv_select);
271435863739SMike Smith 	}
2715b3457b51SScott Long 	AAC_LOCK_RELEASE(&sc->aac_aifq_lock);
271636e0bf6eSScott Long 
271736e0bf6eSScott Long 	return;
271835863739SMike Smith }
271935863739SMike Smith 
2720914da7d0SScott Long /*
27210b94a66eSMike Smith  * Return the Revision of the driver to userspace and check to see if the
272236e0bf6eSScott Long  * userspace app is possibly compatible.  This is extremely bogus since
272336e0bf6eSScott Long  * our driver doesn't follow Adaptec's versioning system.  Cheat by just
272436e0bf6eSScott Long  * returning what the card reported.
272535863739SMike Smith  */
272635863739SMike Smith static int
2727fb0c27d7SScott Long aac_rev_check(struct aac_softc *sc, caddr_t udata)
272835863739SMike Smith {
272935863739SMike Smith 	struct aac_rev_check rev_check;
273035863739SMike Smith 	struct aac_rev_check_resp rev_check_resp;
273135863739SMike Smith 	int error = 0;
273235863739SMike Smith 
273335863739SMike Smith 	debug_called(2);
273435863739SMike Smith 
273535863739SMike Smith 	/*
273635863739SMike Smith 	 * Copyin the revision struct from userspace
273735863739SMike Smith 	 */
2738c6eafcf2SScott Long 	if ((error = copyin(udata, (caddr_t)&rev_check,
2739c6eafcf2SScott Long 			sizeof(struct aac_rev_check))) != 0) {
274035863739SMike Smith 		return error;
274135863739SMike Smith 	}
274235863739SMike Smith 
2743914da7d0SScott Long 	debug(2, "Userland revision= %d\n",
2744914da7d0SScott Long 	      rev_check.callingRevision.buildNumber);
274535863739SMike Smith 
274635863739SMike Smith 	/*
274735863739SMike Smith 	 * Doctor up the response struct.
274835863739SMike Smith 	 */
274935863739SMike Smith 	rev_check_resp.possiblyCompatible = 1;
2750914da7d0SScott Long 	rev_check_resp.adapterSWRevision.external.ul =
2751914da7d0SScott Long 	    sc->aac_revision.external.ul;
2752914da7d0SScott Long 	rev_check_resp.adapterSWRevision.buildNumber =
2753914da7d0SScott Long 	    sc->aac_revision.buildNumber;
275435863739SMike Smith 
2755c6eafcf2SScott Long 	return(copyout((caddr_t)&rev_check_resp, udata,
2756c6eafcf2SScott Long 			sizeof(struct aac_rev_check_resp)));
275735863739SMike Smith }
275835863739SMike Smith 
2759914da7d0SScott Long /*
276035863739SMike Smith  * Pass the caller the next AIF in their queue
276135863739SMike Smith  */
276235863739SMike Smith static int
2763fb0c27d7SScott Long aac_getnext_aif(struct aac_softc *sc, caddr_t arg)
276435863739SMike Smith {
276535863739SMike Smith 	struct get_adapter_fib_ioctl agf;
27669e2e96d8SScott Long 	int error;
276735863739SMike Smith 
276835863739SMike Smith 	debug_called(2);
276935863739SMike Smith 
277035863739SMike Smith 	if ((error = copyin(arg, &agf, sizeof(agf))) == 0) {
277135863739SMike Smith 
277235863739SMike Smith 		/*
277335863739SMike Smith 		 * Check the magic number that we gave the caller.
277435863739SMike Smith 		 */
277536e0bf6eSScott Long 		if (agf.AdapterFibContext != (int)sc->aifthread) {
277635863739SMike Smith 			error = EFAULT;
277735863739SMike Smith 		} else {
2778fb0c27d7SScott Long 			error = aac_return_aif(sc, agf.AifFib);
277935863739SMike Smith 			if ((error == EAGAIN) && (agf.Wait)) {
278035863739SMike Smith 				sc->aac_state |= AAC_STATE_AIF_SLEEPER;
278135863739SMike Smith 				while (error == EAGAIN) {
2782914da7d0SScott Long 					error = tsleep(sc->aac_aifq, PRIBIO |
2783914da7d0SScott Long 						       PCATCH, "aacaif", 0);
278435863739SMike Smith 					if (error == 0)
2785914da7d0SScott Long 						error = aac_return_aif(sc,
2786914da7d0SScott Long 						    agf.AifFib);
278735863739SMike Smith 				}
278835863739SMike Smith 				sc->aac_state &= ~AAC_STATE_AIF_SLEEPER;
278935863739SMike Smith 			}
279035863739SMike Smith 		}
279135863739SMike Smith 	}
279235863739SMike Smith 	return(error);
279335863739SMike Smith }
279435863739SMike Smith 
2795914da7d0SScott Long /*
27960b94a66eSMike Smith  * Hand the next AIF off the top of the queue out to userspace.
27970b94a66eSMike Smith  */
27980b94a66eSMike Smith static int
2799fb0c27d7SScott Long aac_return_aif(struct aac_softc *sc, caddr_t uptr)
28000b94a66eSMike Smith {
2801b3457b51SScott Long 	int error;
28020b94a66eSMike Smith 
28030b94a66eSMike Smith 	debug_called(2);
28040b94a66eSMike Smith 
2805c3d15322SScott Long 	AAC_LOCK_ACQUIRE(&sc->aac_aifq_lock);
28060b94a66eSMike Smith 	if (sc->aac_aifq_tail == sc->aac_aifq_head) {
28070b94a66eSMike Smith 		error = EAGAIN;
28080b94a66eSMike Smith 	} else {
2809c6eafcf2SScott Long 		error = copyout(&sc->aac_aifq[sc->aac_aifq_tail], uptr,
2810c6eafcf2SScott Long 				sizeof(struct aac_aif_command));
281136e0bf6eSScott Long 		if (error)
281270545d1aSScott Long 			device_printf(sc->aac_dev,
281370545d1aSScott Long 			    "aac_return_aif: copyout returned %d\n", error);
28140b94a66eSMike Smith 		if (!error)
2815914da7d0SScott Long 			sc->aac_aifq_tail = (sc->aac_aifq_tail + 1) %
2816914da7d0SScott Long 					    AAC_AIFQ_LENGTH;
28170b94a66eSMike Smith 	}
2818b3457b51SScott Long 	AAC_LOCK_RELEASE(&sc->aac_aifq_lock);
28190b94a66eSMike Smith 	return(error);
28200b94a66eSMike Smith }
282136e0bf6eSScott Long 
2822914da7d0SScott Long /*
282336e0bf6eSScott Long  * Give the userland some information about the container.  The AAC arch
282436e0bf6eSScott Long  * expects the driver to be a SCSI passthrough type driver, so it expects
282536e0bf6eSScott Long  * the containers to have b:t:l numbers.  Fake it.
282636e0bf6eSScott Long  */
282736e0bf6eSScott Long static int
282836e0bf6eSScott Long aac_query_disk(struct aac_softc *sc, caddr_t uptr)
282936e0bf6eSScott Long {
283036e0bf6eSScott Long 	struct aac_query_disk query_disk;
283136e0bf6eSScott Long 	struct aac_container *co;
2832914da7d0SScott Long 	struct aac_disk	*disk;
283336e0bf6eSScott Long 	int error, id;
283436e0bf6eSScott Long 
283536e0bf6eSScott Long 	debug_called(2);
283636e0bf6eSScott Long 
2837914da7d0SScott Long 	disk = NULL;
2838914da7d0SScott Long 
2839914da7d0SScott Long 	error = copyin(uptr, (caddr_t)&query_disk,
2840914da7d0SScott Long 		       sizeof(struct aac_query_disk));
284136e0bf6eSScott Long 	if (error)
284236e0bf6eSScott Long 		return (error);
284336e0bf6eSScott Long 
284436e0bf6eSScott Long 	id = query_disk.ContainerNumber;
284536e0bf6eSScott Long 	if (id == -1)
284636e0bf6eSScott Long 		return (EINVAL);
284736e0bf6eSScott Long 
2848c3d15322SScott Long 	AAC_LOCK_ACQUIRE(&sc->aac_container_lock);
284936e0bf6eSScott Long 	TAILQ_FOREACH(co, &sc->aac_container_tqh, co_link) {
285036e0bf6eSScott Long 		if (co->co_mntobj.ObjectId == id)
285136e0bf6eSScott Long 			break;
285236e0bf6eSScott Long 		}
285336e0bf6eSScott Long 
285436e0bf6eSScott Long 	if (co == NULL) {
285536e0bf6eSScott Long 			query_disk.Valid = 0;
285636e0bf6eSScott Long 			query_disk.Locked = 0;
285736e0bf6eSScott Long 			query_disk.Deleted = 1;		/* XXX is this right? */
285836e0bf6eSScott Long 	} else {
285936e0bf6eSScott Long 		disk = device_get_softc(co->co_disk);
286036e0bf6eSScott Long 		query_disk.Valid = 1;
2861914da7d0SScott Long 		query_disk.Locked =
2862914da7d0SScott Long 		    (disk->ad_flags & AAC_DISK_OPEN) ? 1 : 0;
286336e0bf6eSScott Long 		query_disk.Deleted = 0;
2864b3457b51SScott Long 		query_disk.Bus = device_get_unit(sc->aac_dev);
286536e0bf6eSScott Long 		query_disk.Target = disk->unit;
286636e0bf6eSScott Long 		query_disk.Lun = 0;
286736e0bf6eSScott Long 		query_disk.UnMapped = 0;
28687540e65eSScott Long 		sprintf(&query_disk.diskDeviceName[0], "%s%d",
28697540e65eSScott Long 		        disk->ad_disk.d_name, disk->ad_disk.d_unit);
287036e0bf6eSScott Long 	}
287136e0bf6eSScott Long 	AAC_LOCK_RELEASE(&sc->aac_container_lock);
287236e0bf6eSScott Long 
2873914da7d0SScott Long 	error = copyout((caddr_t)&query_disk, uptr,
2874914da7d0SScott Long 			sizeof(struct aac_query_disk));
287536e0bf6eSScott Long 
287636e0bf6eSScott Long 	return (error);
287736e0bf6eSScott Long }
287836e0bf6eSScott Long 
2879fe3cb0e1SScott Long static void
2880fe3cb0e1SScott Long aac_get_bus_info(struct aac_softc *sc)
2881fe3cb0e1SScott Long {
2882fe3cb0e1SScott Long 	struct aac_fib *fib;
2883fe3cb0e1SScott Long 	struct aac_ctcfg *c_cmd;
2884fe3cb0e1SScott Long 	struct aac_ctcfg_resp *c_resp;
2885fe3cb0e1SScott Long 	struct aac_vmioctl *vmi;
2886fe3cb0e1SScott Long 	struct aac_vmi_businf_resp *vmi_resp;
2887fe3cb0e1SScott Long 	struct aac_getbusinf businfo;
288870545d1aSScott Long 	struct aac_sim *caminf;
2889fe3cb0e1SScott Long 	device_t child;
2890fe3cb0e1SScott Long 	int i, found, error;
2891fe3cb0e1SScott Long 
2892fe3cb0e1SScott Long 	aac_alloc_sync_fib(sc, &fib, 0);
2893fe3cb0e1SScott Long 	c_cmd = (struct aac_ctcfg *)&fib->data[0];
289439ee03c3SScott Long 	bzero(c_cmd, sizeof(struct aac_ctcfg));
2895fe3cb0e1SScott Long 
2896fe3cb0e1SScott Long 	c_cmd->Command = VM_ContainerConfig;
2897fe3cb0e1SScott Long 	c_cmd->cmd = CT_GET_SCSI_METHOD;
2898fe3cb0e1SScott Long 	c_cmd->param = 0;
2899fe3cb0e1SScott Long 
2900fe3cb0e1SScott Long 	error = aac_sync_fib(sc, ContainerCommand, 0, fib,
2901fe3cb0e1SScott Long 	    sizeof(struct aac_ctcfg));
2902fe3cb0e1SScott Long 	if (error) {
2903fe3cb0e1SScott Long 		device_printf(sc->aac_dev, "Error %d sending "
2904fe3cb0e1SScott Long 		    "VM_ContainerConfig command\n", error);
2905fe3cb0e1SScott Long 		aac_release_sync_fib(sc);
2906fe3cb0e1SScott Long 		return;
2907fe3cb0e1SScott Long 	}
2908fe3cb0e1SScott Long 
2909fe3cb0e1SScott Long 	c_resp = (struct aac_ctcfg_resp *)&fib->data[0];
2910fe3cb0e1SScott Long 	if (c_resp->Status != ST_OK) {
2911fe3cb0e1SScott Long 		device_printf(sc->aac_dev, "VM_ContainerConfig returned 0x%x\n",
2912fe3cb0e1SScott Long 		    c_resp->Status);
2913fe3cb0e1SScott Long 		aac_release_sync_fib(sc);
2914fe3cb0e1SScott Long 		return;
2915fe3cb0e1SScott Long 	}
2916fe3cb0e1SScott Long 
2917fe3cb0e1SScott Long 	sc->scsi_method_id = c_resp->param;
2918fe3cb0e1SScott Long 
2919fe3cb0e1SScott Long 	vmi = (struct aac_vmioctl *)&fib->data[0];
292039ee03c3SScott Long 	bzero(vmi, sizeof(struct aac_vmioctl));
292139ee03c3SScott Long 
2922fe3cb0e1SScott Long 	vmi->Command = VM_Ioctl;
2923fe3cb0e1SScott Long 	vmi->ObjType = FT_DRIVE;
2924fe3cb0e1SScott Long 	vmi->MethId = sc->scsi_method_id;
2925fe3cb0e1SScott Long 	vmi->ObjId = 0;
2926fe3cb0e1SScott Long 	vmi->IoctlCmd = GetBusInfo;
2927fe3cb0e1SScott Long 
2928fe3cb0e1SScott Long 	error = aac_sync_fib(sc, ContainerCommand, 0, fib,
2929fe3cb0e1SScott Long 	    sizeof(struct aac_vmioctl));
2930fe3cb0e1SScott Long 	if (error) {
2931fe3cb0e1SScott Long 		device_printf(sc->aac_dev, "Error %d sending VMIoctl command\n",
2932fe3cb0e1SScott Long 		    error);
2933fe3cb0e1SScott Long 		aac_release_sync_fib(sc);
2934fe3cb0e1SScott Long 		return;
2935fe3cb0e1SScott Long 	}
2936fe3cb0e1SScott Long 
2937fe3cb0e1SScott Long 	vmi_resp = (struct aac_vmi_businf_resp *)&fib->data[0];
2938fe3cb0e1SScott Long 	if (vmi_resp->Status != ST_OK) {
2939fe3cb0e1SScott Long 		device_printf(sc->aac_dev, "VM_Ioctl returned %d\n",
2940fe3cb0e1SScott Long 		    vmi_resp->Status);
2941fe3cb0e1SScott Long 		aac_release_sync_fib(sc);
2942fe3cb0e1SScott Long 		return;
2943fe3cb0e1SScott Long 	}
2944fe3cb0e1SScott Long 
2945fe3cb0e1SScott Long 	bcopy(&vmi_resp->BusInf, &businfo, sizeof(struct aac_getbusinf));
2946fe3cb0e1SScott Long 	aac_release_sync_fib(sc);
2947fe3cb0e1SScott Long 
2948fe3cb0e1SScott Long 	found = 0;
2949fe3cb0e1SScott Long 	for (i = 0; i < businfo.BusCount; i++) {
2950fe3cb0e1SScott Long 		if (businfo.BusValid[i] != AAC_BUS_VALID)
2951fe3cb0e1SScott Long 			continue;
2952fe3cb0e1SScott Long 
2953a761a1caSScott Long 		caminf = (struct aac_sim *)malloc( sizeof(struct aac_sim),
2954a761a1caSScott Long 		    M_AACBUF, M_NOWAIT | M_ZERO);
2955fe3cb0e1SScott Long 		if (caminf == NULL)
2956fe3cb0e1SScott Long 			continue;
2957fe3cb0e1SScott Long 
2958fe3cb0e1SScott Long 		child = device_add_child(sc->aac_dev, "aacp", -1);
2959fe3cb0e1SScott Long 		if (child == NULL) {
2960fe3cb0e1SScott Long 			device_printf(sc->aac_dev, "device_add_child failed\n");
2961fe3cb0e1SScott Long 			continue;
2962fe3cb0e1SScott Long 		}
2963fe3cb0e1SScott Long 
2964fe3cb0e1SScott Long 		caminf->TargetsPerBus = businfo.TargetsPerBus;
2965fe3cb0e1SScott Long 		caminf->BusNumber = i;
2966fe3cb0e1SScott Long 		caminf->InitiatorBusId = businfo.InitiatorBusId[i];
2967fe3cb0e1SScott Long 		caminf->aac_sc = sc;
2968ddb8683eSScott Long 		caminf->sim_dev = child;
2969fe3cb0e1SScott Long 
2970fe3cb0e1SScott Long 		device_set_ivars(child, caminf);
2971fe3cb0e1SScott Long 		device_set_desc(child, "SCSI Passthrough Bus");
297270545d1aSScott Long 		TAILQ_INSERT_TAIL(&sc->aac_sim_tqh, caminf, sim_link);
2973fe3cb0e1SScott Long 
2974fe3cb0e1SScott Long 		found = 1;
2975fe3cb0e1SScott Long 	}
2976fe3cb0e1SScott Long 
2977fe3cb0e1SScott Long 	if (found)
2978fe3cb0e1SScott Long 		bus_generic_attach(sc->aac_dev);
2979fe3cb0e1SScott Long 
2980fe3cb0e1SScott Long 	return;
2981fe3cb0e1SScott Long }
2982