xref: /freebsd/sys/dev/aac/aac.c (revision eec256de797ad952d7431e420d2367722d8cbf38)
135863739SMike Smith /*-
235863739SMike Smith  * Copyright (c) 2000 Michael Smith
3c6eafcf2SScott Long  * Copyright (c) 2001 Scott Long
435863739SMike Smith  * Copyright (c) 2000 BSDi
5c6eafcf2SScott Long  * Copyright (c) 2001 Adaptec, Inc.
635863739SMike Smith  * All rights reserved.
735863739SMike Smith  *
835863739SMike Smith  * Redistribution and use in source and binary forms, with or without
935863739SMike Smith  * modification, are permitted provided that the following conditions
1035863739SMike Smith  * are met:
1135863739SMike Smith  * 1. Redistributions of source code must retain the above copyright
1235863739SMike Smith  *    notice, this list of conditions and the following disclaimer.
1335863739SMike Smith  * 2. Redistributions in binary form must reproduce the above copyright
1435863739SMike Smith  *    notice, this list of conditions and the following disclaimer in the
1535863739SMike Smith  *    documentation and/or other materials provided with the distribution.
1635863739SMike Smith  *
1735863739SMike Smith  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1835863739SMike Smith  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1935863739SMike Smith  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2035863739SMike Smith  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2135863739SMike Smith  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2235863739SMike Smith  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2335863739SMike Smith  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2435863739SMike Smith  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2535863739SMike Smith  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2635863739SMike Smith  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2735863739SMike Smith  * SUCH DAMAGE.
2835863739SMike Smith  */
2935863739SMike Smith 
30aad970f1SDavid E. O'Brien #include <sys/cdefs.h>
31aad970f1SDavid E. O'Brien __FBSDID("$FreeBSD$");
32aad970f1SDavid E. O'Brien 
3335863739SMike Smith /*
3435863739SMike Smith  * Driver for the Adaptec 'FSA' family of PCI/SCSI RAID adapters.
3535863739SMike Smith  */
3635863739SMike Smith 
37f6c4dd3fSScott Long #include "opt_aac.h"
38f6c4dd3fSScott Long 
3936e0bf6eSScott Long /* #include <stddef.h> */
4035863739SMike Smith #include <sys/param.h>
4135863739SMike Smith #include <sys/systm.h>
4235863739SMike Smith #include <sys/malloc.h>
4335863739SMike Smith #include <sys/kernel.h>
4436e0bf6eSScott Long #include <sys/kthread.h>
453d04a9d7SScott Long #include <sys/sysctl.h>
46b3457b51SScott Long #include <sys/poll.h>
47891619a6SPoul-Henning Kamp #include <sys/ioccom.h>
4835863739SMike Smith 
4935863739SMike Smith #include <sys/bus.h>
5035863739SMike Smith #include <sys/conf.h>
5135863739SMike Smith #include <sys/signalvar.h>
520b94a66eSMike Smith #include <sys/time.h>
5336e0bf6eSScott Long #include <sys/eventhandler.h>
5435863739SMike Smith 
5535863739SMike Smith #include <machine/bus_memio.h>
5635863739SMike Smith #include <machine/bus.h>
5735863739SMike Smith #include <machine/resource.h>
5835863739SMike Smith 
5935863739SMike Smith #include <dev/aac/aacreg.h>
600b94a66eSMike Smith #include <dev/aac/aac_ioctl.h>
6135863739SMike Smith #include <dev/aac/aacvar.h>
6235863739SMike Smith #include <dev/aac/aac_tables.h>
6335863739SMike Smith 
6435863739SMike Smith static void	aac_startup(void *arg);
65914da7d0SScott Long static void	aac_add_container(struct aac_softc *sc,
66cbfd045bSScott Long 				  struct aac_mntinforesp *mir, int f);
67fe3cb0e1SScott Long static void	aac_get_bus_info(struct aac_softc *sc);
6835863739SMike Smith 
6935863739SMike Smith /* Command Processing */
700b94a66eSMike Smith static void	aac_timeout(struct aac_softc *sc);
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);
74d8a0a473SScott Long static int	aac_wait_command(struct aac_command *cm);
7570545d1aSScott Long static void	aac_command_thread(struct aac_softc *sc);
7635863739SMike Smith 
7735863739SMike Smith /* Command Buffer Management */
78cd481291SScott Long static void	aac_map_command_sg(void *arg, bus_dma_segment_t *segs,
79cd481291SScott Long 				   int nseg, int error);
80c6eafcf2SScott Long static void	aac_map_command_helper(void *arg, bus_dma_segment_t *segs,
81c6eafcf2SScott Long 				       int nseg, int error);
820b94a66eSMike Smith static int	aac_alloc_commands(struct aac_softc *sc);
838480cc63SScott Long static void	aac_free_commands(struct aac_softc *sc);
8435863739SMike Smith static void	aac_unmap_command(struct aac_command *cm);
8535863739SMike Smith 
8635863739SMike Smith /* Hardware Interface */
87c6eafcf2SScott Long static void	aac_common_map(void *arg, bus_dma_segment_t *segs, int nseg,
88c6eafcf2SScott Long 			       int error);
89fe94b852SScott Long static int	aac_check_firmware(struct aac_softc *sc);
9035863739SMike Smith static int	aac_init(struct aac_softc *sc);
9135863739SMike Smith static int	aac_sync_command(struct aac_softc *sc, u_int32_t command,
92c6eafcf2SScott Long 				 u_int32_t arg0, u_int32_t arg1, u_int32_t arg2,
93c6eafcf2SScott Long 				 u_int32_t arg3, u_int32_t *sp);
94c6eafcf2SScott Long static int	aac_enqueue_fib(struct aac_softc *sc, int queue,
95f6c4dd3fSScott Long 				struct aac_command *cm);
96c6eafcf2SScott Long static int	aac_dequeue_fib(struct aac_softc *sc, int queue,
97914da7d0SScott Long 				u_int32_t *fib_size, struct aac_fib **fib_addr);
9836e0bf6eSScott Long static int	aac_enqueue_response(struct aac_softc *sc, int queue,
9936e0bf6eSScott Long 				     struct aac_fib *fib);
10035863739SMike Smith 
101b3457b51SScott Long /* Falcon/PPC interface */
102b3457b51SScott Long static int	aac_fa_get_fwstatus(struct aac_softc *sc);
103b3457b51SScott Long static void	aac_fa_qnotify(struct aac_softc *sc, int qbit);
104b3457b51SScott Long static int	aac_fa_get_istatus(struct aac_softc *sc);
105b3457b51SScott Long static void	aac_fa_clear_istatus(struct aac_softc *sc, int mask);
106b3457b51SScott Long static void	aac_fa_set_mailbox(struct aac_softc *sc, u_int32_t command,
107b3457b51SScott Long 				   u_int32_t arg0, u_int32_t arg1,
108b3457b51SScott Long 				   u_int32_t arg2, u_int32_t arg3);
109a6d35632SScott Long static int	aac_fa_get_mailbox(struct aac_softc *sc, int mb);
110b3457b51SScott Long static void	aac_fa_set_interrupts(struct aac_softc *sc, int enable);
111b3457b51SScott Long 
112b3457b51SScott Long struct aac_interface aac_fa_interface = {
113b3457b51SScott Long 	aac_fa_get_fwstatus,
114b3457b51SScott Long 	aac_fa_qnotify,
115b3457b51SScott Long 	aac_fa_get_istatus,
116b3457b51SScott Long 	aac_fa_clear_istatus,
117b3457b51SScott Long 	aac_fa_set_mailbox,
118a6d35632SScott Long 	aac_fa_get_mailbox,
119b3457b51SScott Long 	aac_fa_set_interrupts
120b3457b51SScott Long };
121b3457b51SScott Long 
12235863739SMike Smith /* StrongARM interface */
12335863739SMike Smith static int	aac_sa_get_fwstatus(struct aac_softc *sc);
12435863739SMike Smith static void	aac_sa_qnotify(struct aac_softc *sc, int qbit);
12535863739SMike Smith static int	aac_sa_get_istatus(struct aac_softc *sc);
12635863739SMike Smith static void	aac_sa_clear_istatus(struct aac_softc *sc, int mask);
12735863739SMike Smith static void	aac_sa_set_mailbox(struct aac_softc *sc, u_int32_t command,
128c6eafcf2SScott Long 				   u_int32_t arg0, u_int32_t arg1,
129c6eafcf2SScott Long 				   u_int32_t arg2, u_int32_t arg3);
130a6d35632SScott Long static int	aac_sa_get_mailbox(struct aac_softc *sc, int mb);
13135863739SMike Smith static void	aac_sa_set_interrupts(struct aac_softc *sc, int enable);
13235863739SMike Smith 
13335863739SMike Smith struct aac_interface aac_sa_interface = {
13435863739SMike Smith 	aac_sa_get_fwstatus,
13535863739SMike Smith 	aac_sa_qnotify,
13635863739SMike Smith 	aac_sa_get_istatus,
13735863739SMike Smith 	aac_sa_clear_istatus,
13835863739SMike Smith 	aac_sa_set_mailbox,
139a6d35632SScott Long 	aac_sa_get_mailbox,
14035863739SMike Smith 	aac_sa_set_interrupts
14135863739SMike Smith };
14235863739SMike Smith 
14335863739SMike Smith /* i960Rx interface */
14435863739SMike Smith static int	aac_rx_get_fwstatus(struct aac_softc *sc);
14535863739SMike Smith static void	aac_rx_qnotify(struct aac_softc *sc, int qbit);
14635863739SMike Smith static int	aac_rx_get_istatus(struct aac_softc *sc);
14735863739SMike Smith static void	aac_rx_clear_istatus(struct aac_softc *sc, int mask);
14835863739SMike Smith static void	aac_rx_set_mailbox(struct aac_softc *sc, u_int32_t command,
149c6eafcf2SScott Long 				   u_int32_t arg0, u_int32_t arg1,
150c6eafcf2SScott Long 				   u_int32_t arg2, u_int32_t arg3);
151a6d35632SScott Long static int	aac_rx_get_mailbox(struct aac_softc *sc, int mb);
15235863739SMike Smith static void	aac_rx_set_interrupts(struct aac_softc *sc, int enable);
15335863739SMike Smith 
15435863739SMike Smith struct aac_interface aac_rx_interface = {
15535863739SMike Smith 	aac_rx_get_fwstatus,
15635863739SMike Smith 	aac_rx_qnotify,
15735863739SMike Smith 	aac_rx_get_istatus,
15835863739SMike Smith 	aac_rx_clear_istatus,
15935863739SMike Smith 	aac_rx_set_mailbox,
160a6d35632SScott Long 	aac_rx_get_mailbox,
16135863739SMike Smith 	aac_rx_set_interrupts
16235863739SMike Smith };
16335863739SMike Smith 
16435863739SMike Smith /* Debugging and Diagnostics */
16535863739SMike Smith static void	aac_describe_controller(struct aac_softc *sc);
1666965a493SScott Long static char	*aac_describe_code(struct aac_code_lookup *table,
167c6eafcf2SScott Long 				   u_int32_t code);
16835863739SMike Smith 
16935863739SMike Smith /* Management Interface */
17035863739SMike Smith static d_open_t		aac_open;
17135863739SMike Smith static d_close_t	aac_close;
17235863739SMike Smith static d_ioctl_t	aac_ioctl;
173b3457b51SScott Long static d_poll_t		aac_poll;
174c6eafcf2SScott Long static int		aac_ioctl_sendfib(struct aac_softc *sc, caddr_t ufib);
175c6eafcf2SScott Long static void		aac_handle_aif(struct aac_softc *sc,
17636e0bf6eSScott Long 					   struct aac_fib *fib);
177fb0c27d7SScott Long static int		aac_rev_check(struct aac_softc *sc, caddr_t udata);
178fb0c27d7SScott Long static int		aac_getnext_aif(struct aac_softc *sc, caddr_t arg);
179fb0c27d7SScott Long static int		aac_return_aif(struct aac_softc *sc, caddr_t uptr);
18036e0bf6eSScott Long static int		aac_query_disk(struct aac_softc *sc, caddr_t uptr);
18135863739SMike Smith 
18235863739SMike Smith static struct cdevsw aac_cdevsw = {
183dc08ffecSPoul-Henning Kamp 	.d_version =	D_VERSION,
184dc08ffecSPoul-Henning Kamp 	.d_flags =	D_NEEDGIANT,
1857ac40f5fSPoul-Henning Kamp 	.d_open =	aac_open,
1867ac40f5fSPoul-Henning Kamp 	.d_close =	aac_close,
1877ac40f5fSPoul-Henning Kamp 	.d_ioctl =	aac_ioctl,
1887ac40f5fSPoul-Henning Kamp 	.d_poll =	aac_poll,
1897ac40f5fSPoul-Henning Kamp 	.d_name =	"aac",
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 	 */
239f6b1c44dSScott Long 	AAC_LOCK_INIT(&sc->aac_aifq_lock, "AAC AIF lock");
240f6b1c44dSScott Long 	AAC_LOCK_INIT(&sc->aac_io_lock, "AAC I/O lock");
241f6b1c44dSScott Long 	AAC_LOCK_INIT(&sc->aac_container_lock, "AAC container lock");
242f6b1c44dSScott Long 	TAILQ_INIT(&sc->aac_container_tqh);
243f6b1c44dSScott Long 
2443df780cfSScott Long 	/* Initialize the local AIF queue pointers */
2453df780cfSScott Long 	sc->aac_aifq_head = sc->aac_aifq_tail = AAC_AIFQ_LENGTH;
246cbfd045bSScott Long 
2470b94a66eSMike Smith 	/*
24835863739SMike Smith 	 * Initialise the adapter.
24935863739SMike Smith 	 */
2500b94a66eSMike Smith 	if ((error = aac_init(sc)) != 0)
25135863739SMike Smith 		return(error);
25235863739SMike Smith 
25335863739SMike Smith 	/*
25435863739SMike Smith 	 * Print a little information about the controller.
25535863739SMike Smith 	 */
25635863739SMike Smith 	aac_describe_controller(sc);
25735863739SMike Smith 
25835863739SMike Smith 	/*
259ae543596SScott Long 	 * Register to probe our containers later.
260ae543596SScott Long 	 */
26135863739SMike Smith 	sc->aac_ich.ich_func = aac_startup;
26235863739SMike Smith 	sc->aac_ich.ich_arg = sc;
26335863739SMike Smith 	if (config_intrhook_establish(&sc->aac_ich) != 0) {
264914da7d0SScott Long 		device_printf(sc->aac_dev,
265914da7d0SScott Long 			      "can't establish configuration hook\n");
26635863739SMike Smith 		return(ENXIO);
26735863739SMike Smith 	}
26835863739SMike Smith 
26935863739SMike Smith 	/*
27035863739SMike Smith 	 * Make the control device.
27135863739SMike Smith 	 */
27235863739SMike Smith 	unit = device_get_unit(sc->aac_dev);
2739e9466baSRobert Watson 	sc->aac_dev_t = make_dev(&aac_cdevsw, unit, UID_ROOT, GID_OPERATOR,
2749e9466baSRobert Watson 				 0640, "aac%d", unit);
275157fbb2eSScott Long 	(void)make_dev_alias(sc->aac_dev_t, "afa%d", unit);
2764aa620cdSScott Long 	(void)make_dev_alias(sc->aac_dev_t, "hpn%d", unit);
27735863739SMike Smith 	sc->aac_dev_t->si_drv1 = sc;
27835863739SMike Smith 
27936e0bf6eSScott Long 	/* Create the AIF thread */
28070545d1aSScott Long 	if (kthread_create((void(*)(void *))aac_command_thread, sc,
281316ec49aSScott Long 			   &sc->aifthread, 0, 0, "aac%daif", unit))
28236e0bf6eSScott Long 		panic("Could not create AIF thread\n");
28336e0bf6eSScott Long 
28436e0bf6eSScott Long 	/* Register the shutdown method to only be called post-dump */
2855f54d522SScott Long 	if ((sc->eh = EVENTHANDLER_REGISTER(shutdown_final, aac_shutdown,
2865f54d522SScott Long 	    sc->aac_dev, SHUTDOWN_PRI_DEFAULT)) == NULL)
2875f54d522SScott Long 		device_printf(sc->aac_dev,
2885f54d522SScott Long 			      "shutdown event registration failed\n");
28936e0bf6eSScott Long 
290fe3cb0e1SScott Long 	/* Register with CAM for the non-DASD devices */
291a6d35632SScott Long 	if ((sc->flags & AAC_FLAGS_ENABLE_CAM) != 0) {
29270545d1aSScott Long 		TAILQ_INIT(&sc->aac_sim_tqh);
293fe3cb0e1SScott Long 		aac_get_bus_info(sc);
29470545d1aSScott Long 	}
295fe3cb0e1SScott Long 
29635863739SMike Smith 	return(0);
29735863739SMike Smith }
29835863739SMike Smith 
299914da7d0SScott Long /*
30035863739SMike Smith  * Probe for containers, create disks.
30135863739SMike Smith  */
30235863739SMike Smith static void
30335863739SMike Smith aac_startup(void *arg)
30435863739SMike Smith {
305914da7d0SScott Long 	struct aac_softc *sc;
306cbfd045bSScott Long 	struct aac_fib *fib;
307cbfd045bSScott Long 	struct aac_mntinfo *mi;
308cbfd045bSScott Long 	struct aac_mntinforesp *mir = NULL;
309795d7dc0SScott Long 	int count = 0, i = 0;
31035863739SMike Smith 
31135863739SMike Smith 	debug_called(1);
31235863739SMike Smith 
313914da7d0SScott Long 	sc = (struct aac_softc *)arg;
314914da7d0SScott Long 
31535863739SMike Smith 	/* disconnect ourselves from the intrhook chain */
31635863739SMike Smith 	config_intrhook_disestablish(&sc->aac_ich);
31735863739SMike Smith 
31803b5fe51SScott Long 	aac_alloc_sync_fib(sc, &fib);
319cbfd045bSScott Long 	mi = (struct aac_mntinfo *)&fib->data[0];
320cbfd045bSScott Long 
32135863739SMike Smith 	/* loop over possible containers */
32236e0bf6eSScott Long 	do {
32335863739SMike Smith 		/* request information on this container */
32439ee03c3SScott Long 		bzero(mi, sizeof(struct aac_mntinfo));
32539ee03c3SScott Long 		mi->Command = VM_NameServe;
32639ee03c3SScott Long 		mi->MntType = FT_FILESYS;
327cbfd045bSScott Long 		mi->MntCount = i;
328cbfd045bSScott Long 		if (aac_sync_fib(sc, ContainerCommand, 0, fib,
329cbfd045bSScott Long 				 sizeof(struct aac_mntinfo))) {
330795d7dc0SScott Long 			printf("error probing container %d", i);
33135863739SMike Smith 			continue;
33235863739SMike Smith 		}
33335863739SMike Smith 
334cbfd045bSScott Long 		mir = (struct aac_mntinforesp *)&fib->data[0];
335795d7dc0SScott Long 		/* XXX Need to check if count changed */
336795d7dc0SScott Long 		count = mir->MntRespCount;
337cbfd045bSScott Long 		aac_add_container(sc, mir, 0);
33836e0bf6eSScott Long 		i++;
339795d7dc0SScott Long 	} while ((i < count) && (i < AAC_MAX_CONTAINERS));
340cbfd045bSScott Long 
341cbfd045bSScott Long 	aac_release_sync_fib(sc);
34235863739SMike Smith 
34335863739SMike Smith 	/* poke the bus to actually attach the child devices */
34435863739SMike Smith 	if (bus_generic_attach(sc->aac_dev))
34535863739SMike Smith 		device_printf(sc->aac_dev, "bus_generic_attach failed\n");
34635863739SMike Smith 
34735863739SMike Smith 	/* mark the controller up */
34835863739SMike Smith 	sc->aac_state &= ~AAC_STATE_SUSPEND;
34935863739SMike Smith 
35035863739SMike Smith 	/* enable interrupts now */
35135863739SMike Smith 	AAC_UNMASK_INTERRUPTS(sc);
35235863739SMike Smith }
35335863739SMike Smith 
354914da7d0SScott Long /*
355914da7d0SScott Long  * Create a device to respresent a new container
356914da7d0SScott Long  */
357914da7d0SScott Long static void
358cbfd045bSScott Long aac_add_container(struct aac_softc *sc, struct aac_mntinforesp *mir, int f)
359914da7d0SScott Long {
360914da7d0SScott Long 	struct aac_container *co;
361914da7d0SScott Long 	device_t child;
362914da7d0SScott Long 
363914da7d0SScott Long 	/*
364914da7d0SScott Long 	 * Check container volume type for validity.  Note that many of
365914da7d0SScott Long 	 * the possible types may never show up.
366914da7d0SScott Long 	 */
367914da7d0SScott Long 	if ((mir->Status == ST_OK) && (mir->MntTable[0].VolType != CT_NONE)) {
368a761a1caSScott Long 		co = (struct aac_container *)malloc(sizeof *co, M_AACBUF,
369a761a1caSScott Long 		       M_NOWAIT | M_ZERO);
370914da7d0SScott Long 		if (co == NULL)
371914da7d0SScott Long 			panic("Out of memory?!\n");
372914da7d0SScott Long 		debug(1, "id %x  name '%.16s'  size %u  type %d",
373914da7d0SScott Long 		      mir->MntTable[0].ObjectId,
374914da7d0SScott Long 		      mir->MntTable[0].FileSystemName,
375914da7d0SScott Long 		      mir->MntTable[0].Capacity, mir->MntTable[0].VolType);
376914da7d0SScott Long 
377fe3cb0e1SScott Long 		if ((child = device_add_child(sc->aac_dev, "aacd", -1)) == NULL)
378914da7d0SScott Long 			device_printf(sc->aac_dev, "device_add_child failed\n");
379914da7d0SScott Long 		else
380914da7d0SScott Long 			device_set_ivars(child, co);
381914da7d0SScott Long 		device_set_desc(child, aac_describe_code(aac_container_types,
382914da7d0SScott Long 				mir->MntTable[0].VolType));
383914da7d0SScott Long 		co->co_disk = child;
384914da7d0SScott Long 		co->co_found = f;
385914da7d0SScott Long 		bcopy(&mir->MntTable[0], &co->co_mntobj,
386914da7d0SScott Long 		      sizeof(struct aac_mntobj));
387c3d15322SScott Long 		AAC_LOCK_ACQUIRE(&sc->aac_container_lock);
388914da7d0SScott Long 		TAILQ_INSERT_TAIL(&sc->aac_container_tqh, co, co_link);
389914da7d0SScott Long 		AAC_LOCK_RELEASE(&sc->aac_container_lock);
390914da7d0SScott Long 	}
391914da7d0SScott Long }
392914da7d0SScott Long 
393914da7d0SScott Long /*
39435863739SMike Smith  * Free all of the resources associated with (sc)
39535863739SMike Smith  *
39635863739SMike Smith  * Should not be called if the controller is active.
39735863739SMike Smith  */
39835863739SMike Smith void
39935863739SMike Smith aac_free(struct aac_softc *sc)
40035863739SMike Smith {
401ffb37f33SScott Long 
40235863739SMike Smith 	debug_called(1);
40335863739SMike Smith 
40435863739SMike Smith 	/* remove the control device */
40535863739SMike Smith 	if (sc->aac_dev_t != NULL)
40635863739SMike Smith 		destroy_dev(sc->aac_dev_t);
40735863739SMike Smith 
4080b94a66eSMike Smith 	/* throw away any FIB buffers, discard the FIB DMA tag */
4098480cc63SScott Long 	aac_free_commands(sc);
4100b94a66eSMike Smith 	if (sc->aac_fib_dmat)
4110b94a66eSMike Smith 		bus_dma_tag_destroy(sc->aac_fib_dmat);
41235863739SMike Smith 
413ffb37f33SScott Long 	free(sc->aac_commands, M_AACBUF);
414ffb37f33SScott Long 
41535863739SMike Smith 	/* destroy the common area */
41635863739SMike Smith 	if (sc->aac_common) {
41735863739SMike Smith 		bus_dmamap_unload(sc->aac_common_dmat, sc->aac_common_dmamap);
418c6eafcf2SScott Long 		bus_dmamem_free(sc->aac_common_dmat, sc->aac_common,
419c6eafcf2SScott Long 				sc->aac_common_dmamap);
42035863739SMike Smith 	}
4210b94a66eSMike Smith 	if (sc->aac_common_dmat)
4220b94a66eSMike Smith 		bus_dma_tag_destroy(sc->aac_common_dmat);
42335863739SMike Smith 
42435863739SMike Smith 	/* disconnect the interrupt handler */
42535863739SMike Smith 	if (sc->aac_intr)
42635863739SMike Smith 		bus_teardown_intr(sc->aac_dev, sc->aac_irq, sc->aac_intr);
42735863739SMike Smith 	if (sc->aac_irq != NULL)
428c6eafcf2SScott Long 		bus_release_resource(sc->aac_dev, SYS_RES_IRQ, sc->aac_irq_rid,
429c6eafcf2SScott Long 				     sc->aac_irq);
43035863739SMike Smith 
43135863739SMike Smith 	/* destroy data-transfer DMA tag */
43235863739SMike Smith 	if (sc->aac_buffer_dmat)
43335863739SMike Smith 		bus_dma_tag_destroy(sc->aac_buffer_dmat);
43435863739SMike Smith 
43535863739SMike Smith 	/* destroy the parent DMA tag */
43635863739SMike Smith 	if (sc->aac_parent_dmat)
43735863739SMike Smith 		bus_dma_tag_destroy(sc->aac_parent_dmat);
43835863739SMike Smith 
43935863739SMike Smith 	/* release the register window mapping */
44035863739SMike Smith 	if (sc->aac_regs_resource != NULL)
441914da7d0SScott Long 		bus_release_resource(sc->aac_dev, SYS_RES_MEMORY,
442914da7d0SScott Long 				     sc->aac_regs_rid, sc->aac_regs_resource);
44335863739SMike Smith }
44435863739SMike Smith 
445914da7d0SScott Long /*
44635863739SMike Smith  * Disconnect from the controller completely, in preparation for unload.
44735863739SMike Smith  */
44835863739SMike Smith int
44935863739SMike Smith aac_detach(device_t dev)
45035863739SMike Smith {
451914da7d0SScott Long 	struct aac_softc *sc;
45270545d1aSScott Long 	struct aac_container *co;
45370545d1aSScott Long 	struct aac_sim	*sim;
45435863739SMike Smith 	int error;
45535863739SMike Smith 
45635863739SMike Smith 	debug_called(1);
45735863739SMike Smith 
458914da7d0SScott Long 	sc = device_get_softc(dev);
459914da7d0SScott Long 
46035863739SMike Smith 	if (sc->aac_state & AAC_STATE_OPEN)
46135863739SMike Smith 		return(EBUSY);
46235863739SMike Smith 
46370545d1aSScott Long 	/* Remove the child containers */
464a761a1caSScott Long 	while ((co = TAILQ_FIRST(&sc->aac_container_tqh)) != NULL) {
46570545d1aSScott Long 		error = device_delete_child(dev, co->co_disk);
46670545d1aSScott Long 		if (error)
46770545d1aSScott Long 			return (error);
46865ac4ed6SScott Long 		TAILQ_REMOVE(&sc->aac_container_tqh, co, co_link);
469a761a1caSScott Long 		free(co, M_AACBUF);
47070545d1aSScott Long 	}
47170545d1aSScott Long 
47270545d1aSScott Long 	/* Remove the CAM SIMs */
473a761a1caSScott Long 	while ((sim = TAILQ_FIRST(&sc->aac_sim_tqh)) != NULL) {
474a761a1caSScott Long 		TAILQ_REMOVE(&sc->aac_sim_tqh, sim, sim_link);
47570545d1aSScott Long 		error = device_delete_child(dev, sim->sim_dev);
47670545d1aSScott Long 		if (error)
47770545d1aSScott Long 			return (error);
478a761a1caSScott Long 		free(sim, M_AACBUF);
47970545d1aSScott Long 	}
48070545d1aSScott Long 
48136e0bf6eSScott Long 	if (sc->aifflags & AAC_AIFFLAGS_RUNNING) {
48236e0bf6eSScott Long 		sc->aifflags |= AAC_AIFFLAGS_EXIT;
48336e0bf6eSScott Long 		wakeup(sc->aifthread);
48436e0bf6eSScott Long 		tsleep(sc->aac_dev, PUSER | PCATCH, "aacdch", 30 * hz);
48536e0bf6eSScott Long 	}
48636e0bf6eSScott Long 
48736e0bf6eSScott Long 	if (sc->aifflags & AAC_AIFFLAGS_RUNNING)
48836e0bf6eSScott Long 		panic("Cannot shutdown AIF thread\n");
48936e0bf6eSScott Long 
49035863739SMike Smith 	if ((error = aac_shutdown(dev)))
49135863739SMike Smith 		return(error);
49235863739SMike Smith 
4935f54d522SScott Long 	EVENTHANDLER_DEREGISTER(shutdown_final, sc->eh);
4945f54d522SScott Long 
49535863739SMike Smith 	aac_free(sc);
49635863739SMike Smith 
49735863739SMike Smith 	return(0);
49835863739SMike Smith }
49935863739SMike Smith 
500914da7d0SScott Long /*
50135863739SMike Smith  * Bring the controller down to a dormant state and detach all child devices.
50235863739SMike Smith  *
50335863739SMike Smith  * This function is called before detach or system shutdown.
50435863739SMike Smith  *
5050b94a66eSMike Smith  * Note that we can assume that the bioq on the controller is empty, as we won't
50635863739SMike Smith  * allow shutdown if any device is open.
50735863739SMike Smith  */
50835863739SMike Smith int
50935863739SMike Smith aac_shutdown(device_t dev)
51035863739SMike Smith {
511914da7d0SScott Long 	struct aac_softc *sc;
512cbfd045bSScott Long 	struct aac_fib *fib;
513cbfd045bSScott Long 	struct aac_close_command *cc;
51435863739SMike Smith 
51535863739SMike Smith 	debug_called(1);
51635863739SMike Smith 
517914da7d0SScott Long 	sc = device_get_softc(dev);
518914da7d0SScott Long 
51935863739SMike Smith 	sc->aac_state |= AAC_STATE_SUSPEND;
52035863739SMike Smith 
52135863739SMike Smith 	/*
52235863739SMike Smith 	 * Send a Container shutdown followed by a HostShutdown FIB to the
52335863739SMike Smith 	 * controller to convince it that we don't want to talk to it anymore.
52435863739SMike Smith 	 * We've been closed and all I/O completed already
52535863739SMike Smith 	 */
52635863739SMike Smith 	device_printf(sc->aac_dev, "shutting down controller...");
52735863739SMike Smith 
52803b5fe51SScott Long 	aac_alloc_sync_fib(sc, &fib);
529cbfd045bSScott Long 	cc = (struct aac_close_command *)&fib->data[0];
530cbfd045bSScott Long 
53139ee03c3SScott Long 	bzero(cc, sizeof(struct aac_close_command));
532cbfd045bSScott Long 	cc->Command = VM_CloseAll;
533cbfd045bSScott Long 	cc->ContainerId = 0xffffffff;
534cbfd045bSScott Long 	if (aac_sync_fib(sc, ContainerCommand, 0, fib,
535cbfd045bSScott Long 	    sizeof(struct aac_close_command)))
53635863739SMike Smith 		printf("FAILED.\n");
53770545d1aSScott Long 	else
53870545d1aSScott Long 		printf("done\n");
53970545d1aSScott Long #if 0
540914da7d0SScott Long 	else {
541cbfd045bSScott Long 		fib->data[0] = 0;
54236e0bf6eSScott Long 		/*
543914da7d0SScott Long 		 * XXX Issuing this command to the controller makes it shut down
54436e0bf6eSScott Long 		 * but also keeps it from coming back up without a reset of the
54536e0bf6eSScott Long 		 * PCI bus.  This is not desirable if you are just unloading the
54636e0bf6eSScott Long 		 * driver module with the intent to reload it later.
54736e0bf6eSScott Long 		 */
548cbfd045bSScott Long 		if (aac_sync_fib(sc, FsaHostShutdown, AAC_FIBSTATE_SHUTDOWN,
549cbfd045bSScott Long 		    fib, 1)) {
55035863739SMike Smith 			printf("FAILED.\n");
55135863739SMike Smith 		} else {
55235863739SMike Smith 			printf("done.\n");
55335863739SMike Smith 		}
55435863739SMike Smith 	}
55570545d1aSScott Long #endif
55635863739SMike Smith 
55735863739SMike Smith 	AAC_MASK_INTERRUPTS(sc);
55835863739SMike Smith 
55935863739SMike Smith 	return(0);
56035863739SMike Smith }
56135863739SMike Smith 
562914da7d0SScott Long /*
56335863739SMike Smith  * Bring the controller to a quiescent state, ready for system suspend.
56435863739SMike Smith  */
56535863739SMike Smith int
56635863739SMike Smith aac_suspend(device_t dev)
56735863739SMike Smith {
568914da7d0SScott Long 	struct aac_softc *sc;
56935863739SMike Smith 
57035863739SMike Smith 	debug_called(1);
571914da7d0SScott Long 
572914da7d0SScott Long 	sc = device_get_softc(dev);
573914da7d0SScott Long 
57435863739SMike Smith 	sc->aac_state |= AAC_STATE_SUSPEND;
57535863739SMike Smith 
57635863739SMike Smith 	AAC_MASK_INTERRUPTS(sc);
57735863739SMike Smith 	return(0);
57835863739SMike Smith }
57935863739SMike Smith 
580914da7d0SScott Long /*
58135863739SMike Smith  * Bring the controller back to a state ready for operation.
58235863739SMike Smith  */
58335863739SMike Smith int
58435863739SMike Smith aac_resume(device_t dev)
58535863739SMike Smith {
586914da7d0SScott Long 	struct aac_softc *sc;
58735863739SMike Smith 
58835863739SMike Smith 	debug_called(1);
589914da7d0SScott Long 
590914da7d0SScott Long 	sc = device_get_softc(dev);
591914da7d0SScott Long 
59235863739SMike Smith 	sc->aac_state &= ~AAC_STATE_SUSPEND;
59335863739SMike Smith 	AAC_UNMASK_INTERRUPTS(sc);
59435863739SMike Smith 	return(0);
59535863739SMike Smith }
59635863739SMike Smith 
597914da7d0SScott Long /*
59835863739SMike Smith  * Take an interrupt.
59935863739SMike Smith  */
60035863739SMike Smith void
60135863739SMike Smith aac_intr(void *arg)
60235863739SMike Smith {
603914da7d0SScott Long 	struct aac_softc *sc;
60470545d1aSScott Long 	u_int16_t reason;
60535863739SMike Smith 
60635863739SMike Smith 	debug_called(2);
60735863739SMike Smith 
608914da7d0SScott Long 	sc = (struct aac_softc *)arg;
609914da7d0SScott Long 
610f30ac74cSScott Long 	/*
6119148fa21SScott Long 	 * Read the status register directly.  This is faster than taking the
6129148fa21SScott Long 	 * driver lock and reading the queues directly.  It also saves having
6139148fa21SScott Long 	 * to turn parts of the driver lock into a spin mutex, which would be
6149148fa21SScott Long 	 * ugly.
615f30ac74cSScott Long 	 */
61635863739SMike Smith 	reason = AAC_GET_ISTATUS(sc);
617f30ac74cSScott Long 	AAC_CLEAR_ISTATUS(sc, reason);
618f30ac74cSScott Long 
6199c3a7fceSScott Long 	/* handle completion processing */
6209148fa21SScott Long 	if (reason & AAC_DB_RESPONSE_READY)
6219148fa21SScott Long 		taskqueue_enqueue_fast(taskqueue_fast, &sc->aac_task_complete);
62235863739SMike Smith 
6239148fa21SScott Long 	/* controller wants to talk to us */
6249148fa21SScott Long 	if (reason & (AAC_DB_PRINTF | AAC_DB_COMMAND_READY)) {
62570545d1aSScott Long 		/*
6269148fa21SScott Long 		 * XXX Make sure that we don't get fooled by strange messages
6279148fa21SScott Long 		 * that start with a NULL.
62870545d1aSScott Long 		 */
6299148fa21SScott Long 		if ((reason & AAC_DB_PRINTF) &&
6309148fa21SScott Long 		    (sc->aac_common->ac_printf[0] == 0))
6319148fa21SScott Long 			sc->aac_common->ac_printf[0] = 32;
63270545d1aSScott Long 
6339148fa21SScott Long 		/*
6349148fa21SScott Long 		 * This might miss doing the actual wakeup.  However, the
635a32a982dSScott Long 		 * msleep that this is waking up has a timeout, so it will
6369148fa21SScott Long 		 * wake up eventually.  AIFs and printfs are low enough
6379148fa21SScott Long 		 * priority that they can handle hanging out for a few seconds
6389148fa21SScott Long 		 * if needed.
6399148fa21SScott Long 		 */
64036e0bf6eSScott Long 		wakeup(sc->aifthread);
64136e0bf6eSScott Long 	}
6429148fa21SScott Long }
64335863739SMike Smith 
644c6eafcf2SScott Long /*
645914da7d0SScott Long  * Command Processing
646914da7d0SScott Long  */
64735863739SMike Smith 
648914da7d0SScott Long /*
64935863739SMike Smith  * Start as much queued I/O as possible on the controller
65035863739SMike Smith  */
651fe3cb0e1SScott Long void
65235863739SMike Smith aac_startio(struct aac_softc *sc)
65335863739SMike Smith {
65435863739SMike Smith 	struct aac_command *cm;
655397fa34fSScott Long 	int error;
65635863739SMike Smith 
65735863739SMike Smith 	debug_called(2);
65835863739SMike Smith 
65935863739SMike Smith 	for (;;) {
660914da7d0SScott Long 		/*
661397fa34fSScott Long 		 * This flag might be set if the card is out of resources.
662397fa34fSScott Long 		 * Checking it here prevents an infinite loop of deferrals.
663397fa34fSScott Long 		 */
664397fa34fSScott Long 		if (sc->flags & AAC_QUEUE_FRZN)
665397fa34fSScott Long 			break;
666397fa34fSScott Long 
667397fa34fSScott Long 		/*
668914da7d0SScott Long 		 * Try to get a command that's been put off for lack of
669914da7d0SScott Long 		 * resources
670914da7d0SScott Long 		 */
67135863739SMike Smith 		cm = aac_dequeue_ready(sc);
67235863739SMike Smith 
673914da7d0SScott Long 		/*
674914da7d0SScott Long 		 * Try to build a command off the bio queue (ignore error
675914da7d0SScott Long 		 * return)
676914da7d0SScott Long 		 */
6770b94a66eSMike Smith 		if (cm == NULL)
67835863739SMike Smith 			aac_bio_command(sc, &cm);
67935863739SMike Smith 
68035863739SMike Smith 		/* nothing to do? */
68135863739SMike Smith 		if (cm == NULL)
68235863739SMike Smith 			break;
68335863739SMike Smith 
684cd481291SScott Long 		/* don't map more than once */
685cd481291SScott Long 		if (cm->cm_flags & AAC_CMD_MAPPED)
6864102d44bSScott Long 			panic("aac: command %p already mapped", cm);
68735863739SMike Smith 
688397fa34fSScott Long 		/*
689397fa34fSScott Long 		 * Set up the command to go to the controller.  If there are no
690397fa34fSScott Long 		 * data buffers associated with the command then it can bypass
691397fa34fSScott Long 		 * busdma.
692397fa34fSScott Long 		 */
693cd481291SScott Long 		if (cm->cm_datalen != 0) {
694397fa34fSScott Long 			error = bus_dmamap_load(sc->aac_buffer_dmat,
695397fa34fSScott Long 						cm->cm_datamap, cm->cm_data,
696397fa34fSScott Long 						cm->cm_datalen,
697cd481291SScott Long 						aac_map_command_sg, cm, 0);
698cd481291SScott Long 			if (error == EINPROGRESS) {
699cd481291SScott Long 				debug(1, "freezing queue\n");
700cd481291SScott Long 				sc->flags |= AAC_QUEUE_FRZN;
701cd481291SScott Long 				error = 0;
702614c22b2SScott Long 			} else if (error != 0)
703397fa34fSScott Long 				panic("aac_startio: unexpected error %d from "
704397fa34fSScott Long 				      "busdma\n", error);
705397fa34fSScott Long 		} else
7068778f63dSScott Long 			aac_map_command_sg(cm, NULL, 0, 0);
707cd481291SScott Long 	}
70835863739SMike Smith }
70935863739SMike Smith 
710914da7d0SScott Long /*
71135863739SMike Smith  * Handle notification of one or more FIBs coming from the controller.
71235863739SMike Smith  */
71335863739SMike Smith static void
71470545d1aSScott Long aac_command_thread(struct aac_softc *sc)
71535863739SMike Smith {
71635863739SMike Smith 	struct aac_fib *fib;
71735863739SMike Smith 	u_int32_t fib_size;
7189148fa21SScott Long 	int size, retval;
71935863739SMike Smith 
72036e0bf6eSScott Long 	debug_called(2);
72135863739SMike Smith 
722a32a982dSScott Long 	AAC_LOCK_ACQUIRE(&sc->aac_io_lock);
723a32a982dSScott Long 	sc->aifflags = AAC_AIFFLAGS_RUNNING;
72436e0bf6eSScott Long 
725a32a982dSScott Long 	while ((sc->aifflags & AAC_AIFFLAGS_EXIT) == 0) {
726a32a982dSScott Long 
727a32a982dSScott Long 		retval = 0;
728a32a982dSScott Long 		if ((sc->aifflags & AAC_AIFFLAGS_PENDING) == 0)
729a32a982dSScott Long 			retval = msleep(sc->aifthread, &sc->aac_io_lock, PRIBIO,
730a32a982dSScott Long 					"aifthd", AAC_PERIODIC_INTERVAL * hz);
73136e0bf6eSScott Long 
7329148fa21SScott Long 		/*
7339148fa21SScott Long 		 * First see if any FIBs need to be allocated.  This needs
7349148fa21SScott Long 		 * to be called without the driver lock because contigmalloc
7359148fa21SScott Long 		 * will grab Giant, and would result in an LOR.
7369148fa21SScott Long 		 */
7379148fa21SScott Long 		if ((sc->aifflags & AAC_AIFFLAGS_ALLOCFIBS) != 0) {
738a32a982dSScott Long 			AAC_LOCK_RELEASE(&sc->aac_io_lock);
739a32a982dSScott Long 			aac_alloc_commands(sc);
7409148fa21SScott Long 			AAC_LOCK_ACQUIRE(&sc->aac_io_lock);
7414102d44bSScott Long 			sc->aifflags &= ~AAC_AIFFLAGS_ALLOCFIBS;
742a32a982dSScott Long 			aac_startio(sc);
743a32a982dSScott Long 		}
7449148fa21SScott Long 
7459148fa21SScott Long 		/*
7469148fa21SScott Long 		 * While we're here, check to see if any commands are stuck.
7479148fa21SScott Long 		 * This is pretty low-priority, so it's ok if it doesn't
7489148fa21SScott Long 		 * always fire.
7499148fa21SScott Long 		 */
7509148fa21SScott Long 		if (retval == EWOULDBLOCK)
75170545d1aSScott Long 			aac_timeout(sc);
75270545d1aSScott Long 
75370545d1aSScott Long 		/* Check the hardware printf message buffer */
7549148fa21SScott Long 		if (sc->aac_common->ac_printf[0] != 0)
75570545d1aSScott Long 			aac_print_printf(sc);
75670545d1aSScott Long 
7579148fa21SScott Long 		/* Also check to see if the adapter has a command for us. */
7589148fa21SScott Long 		while (aac_dequeue_fib(sc, AAC_HOST_NORM_CMD_QUEUE,
7599148fa21SScott Long 				       &fib_size, &fib) == 0) {
76035863739SMike Smith 
76136e0bf6eSScott Long 			AAC_PRINT_FIB(sc, fib);
76236e0bf6eSScott Long 
76335863739SMike Smith 			switch (fib->Header.Command) {
76435863739SMike Smith 			case AifRequest:
76536e0bf6eSScott Long 				aac_handle_aif(sc, fib);
76635863739SMike Smith 				break;
76735863739SMike Smith 			default:
768914da7d0SScott Long 				device_printf(sc->aac_dev, "unknown command "
769914da7d0SScott Long 					      "from controller\n");
77035863739SMike Smith 				break;
77135863739SMike Smith 			}
77235863739SMike Smith 
77336e0bf6eSScott Long 			if ((fib->Header.XferState == 0) ||
77436e0bf6eSScott Long 			    (fib->Header.StructType != AAC_FIBTYPE_TFIB))
77536e0bf6eSScott Long 				break;
77636e0bf6eSScott Long 
77770545d1aSScott Long 			/* Return the AIF to the controller. */
77836e0bf6eSScott Long 			if (fib->Header.XferState & AAC_FIBSTATE_FROMADAP) {
77936e0bf6eSScott Long 				fib->Header.XferState |= AAC_FIBSTATE_DONEHOST;
78036e0bf6eSScott Long 				*(AAC_FSAStatus*)fib->data = ST_OK;
78136e0bf6eSScott Long 
78236e0bf6eSScott Long 				/* XXX Compute the Size field? */
78336e0bf6eSScott Long 				size = fib->Header.Size;
78436e0bf6eSScott Long 				if (size > sizeof(struct aac_fib)) {
78536e0bf6eSScott Long 					size = sizeof(struct aac_fib);
78636e0bf6eSScott Long 					fib->Header.Size = size;
78736e0bf6eSScott Long 				}
78836e0bf6eSScott Long 				/*
789914da7d0SScott Long 				 * Since we did not generate this command, it
790914da7d0SScott Long 				 * cannot go through the normal
791914da7d0SScott Long 				 * enqueue->startio chain.
79236e0bf6eSScott Long 				 */
793914da7d0SScott Long 				aac_enqueue_response(sc,
794914da7d0SScott Long 						     AAC_ADAP_NORM_RESP_QUEUE,
795914da7d0SScott Long 						     fib);
79636e0bf6eSScott Long 			}
79736e0bf6eSScott Long 		}
79836e0bf6eSScott Long 	}
79936e0bf6eSScott Long 	sc->aifflags &= ~AAC_AIFFLAGS_RUNNING;
800a32a982dSScott Long 	AAC_LOCK_RELEASE(&sc->aac_io_lock);
80136e0bf6eSScott Long 	wakeup(sc->aac_dev);
80236e0bf6eSScott Long 
80336e0bf6eSScott Long 	kthread_exit(0);
80435863739SMike Smith }
80535863739SMike Smith 
806914da7d0SScott Long /*
8079c3a7fceSScott Long  * Process completed commands.
80835863739SMike Smith  */
80935863739SMike Smith static void
8109c3a7fceSScott Long aac_complete(void *context, int pending)
81135863739SMike Smith {
8129c3a7fceSScott Long 	struct aac_softc *sc;
81335863739SMike Smith 	struct aac_command *cm;
81435863739SMike Smith 	struct aac_fib *fib;
81535863739SMike Smith 	u_int32_t fib_size;
81635863739SMike Smith 
81735863739SMike Smith 	debug_called(2);
81835863739SMike Smith 
8199c3a7fceSScott Long 	sc = (struct aac_softc *)context;
8209c3a7fceSScott Long 
821ae543596SScott Long 	AAC_LOCK_ACQUIRE(&sc->aac_io_lock);
822ae543596SScott Long 
8239c3a7fceSScott Long 	/* pull completed commands off the queue */
82435863739SMike Smith 	for (;;) {
82535863739SMike Smith 		/* look for completed FIBs on our queue */
826914da7d0SScott Long 		if (aac_dequeue_fib(sc, AAC_HOST_NORM_RESP_QUEUE, &fib_size,
827914da7d0SScott Long 				    &fib))
82835863739SMike Smith 			break;	/* nothing to do */
82935863739SMike Smith 
830ecd1c51fSScott Long 		/* get the command, unmap and hand off for processing */
831cb0d64b9SScott Long 		cm = sc->aac_commands + fib->Header.SenderData;
83235863739SMike Smith 		if (cm == NULL) {
83335863739SMike Smith 			AAC_PRINT_FIB(sc, fib);
8349c3a7fceSScott Long 			break;
8359c3a7fceSScott Long 		}
8369c3a7fceSScott Long 
8370b94a66eSMike Smith 		aac_remove_busy(cm);
838ecd1c51fSScott Long 		aac_unmap_command(cm);
83935863739SMike Smith 		cm->cm_flags |= AAC_CMD_COMPLETED;
84035863739SMike Smith 
84135863739SMike Smith 		/* is there a completion handler? */
84235863739SMike Smith 		if (cm->cm_complete != NULL) {
84335863739SMike Smith 			cm->cm_complete(cm);
84435863739SMike Smith 		} else {
84535863739SMike Smith 			/* assume that someone is sleeping on this command */
84635863739SMike Smith 			wakeup(cm);
84735863739SMike Smith 		}
84835863739SMike Smith 	}
8490b94a66eSMike Smith 
8500b94a66eSMike Smith 	/* see if we can start some more I/O */
851cd481291SScott Long 	sc->flags &= ~AAC_QUEUE_FRZN;
8520b94a66eSMike Smith 	aac_startio(sc);
853ae543596SScott Long 
854ae543596SScott Long 	AAC_LOCK_RELEASE(&sc->aac_io_lock);
85535863739SMike Smith }
85635863739SMike Smith 
857914da7d0SScott Long /*
85835863739SMike Smith  * Handle a bio submitted from a disk device.
85935863739SMike Smith  */
86035863739SMike Smith void
86135863739SMike Smith aac_submit_bio(struct bio *bp)
86235863739SMike Smith {
863914da7d0SScott Long 	struct aac_disk *ad;
864914da7d0SScott Long 	struct aac_softc *sc;
86535863739SMike Smith 
86635863739SMike Smith 	debug_called(2);
86735863739SMike Smith 
8687540e65eSScott Long 	ad = (struct aac_disk *)bp->bio_disk->d_drv1;
869914da7d0SScott Long 	sc = ad->ad_controller;
870914da7d0SScott Long 
87135863739SMike Smith 	/* queue the BIO and try to get some work done */
8720b94a66eSMike Smith 	aac_enqueue_bio(sc, bp);
87335863739SMike Smith 	aac_startio(sc);
87435863739SMike Smith }
87535863739SMike Smith 
876914da7d0SScott Long /*
87735863739SMike Smith  * Get a bio and build a command to go with it.
87835863739SMike Smith  */
87935863739SMike Smith static int
88035863739SMike Smith aac_bio_command(struct aac_softc *sc, struct aac_command **cmp)
88135863739SMike Smith {
88235863739SMike Smith 	struct aac_command *cm;
88335863739SMike Smith 	struct aac_fib *fib;
88435863739SMike Smith 	struct aac_disk *ad;
88535863739SMike Smith 	struct bio *bp;
88635863739SMike Smith 
88735863739SMike Smith 	debug_called(2);
88835863739SMike Smith 
88935863739SMike Smith 	/* get the resources we will need */
89035863739SMike Smith 	cm = NULL;
891a32a982dSScott Long 	bp = NULL;
89235863739SMike Smith 	if (aac_alloc_command(sc, &cm))	/* get a command */
89335863739SMike Smith 		goto fail;
894a32a982dSScott Long 	if ((bp = aac_dequeue_bio(sc)) == NULL)
895a32a982dSScott Long 		goto fail;
89635863739SMike Smith 
89735863739SMike Smith 	/* fill out the command */
8980b94a66eSMike Smith 	cm->cm_data = (void *)bp->bio_data;
8990b94a66eSMike Smith 	cm->cm_datalen = bp->bio_bcount;
9000b94a66eSMike Smith 	cm->cm_complete = aac_bio_complete;
90135863739SMike Smith 	cm->cm_private = bp;
9020b94a66eSMike Smith 	cm->cm_timestamp = time_second;
90336e0bf6eSScott Long 	cm->cm_queue = AAC_ADAP_NORM_CMD_QUEUE;
90435863739SMike Smith 
90535863739SMike Smith 	/* build the FIB */
90635863739SMike Smith 	fib = cm->cm_fib;
907b85f5808SScott Long 	fib->Header.Size = sizeof(struct aac_fib_header);
90835863739SMike Smith 	fib->Header.XferState =
90935863739SMike Smith 		AAC_FIBSTATE_HOSTOWNED   |
91035863739SMike Smith 		AAC_FIBSTATE_INITIALISED |
911f30ac74cSScott Long 		AAC_FIBSTATE_EMPTY	 |
91235863739SMike Smith 		AAC_FIBSTATE_FROMHOST	 |
91335863739SMike Smith 		AAC_FIBSTATE_REXPECTED   |
914f30ac74cSScott Long 		AAC_FIBSTATE_NORM	 |
915f30ac74cSScott Long 		AAC_FIBSTATE_ASYNC	 |
916f30ac74cSScott Long 		AAC_FIBSTATE_FAST_RESPONSE;
91735863739SMike Smith 
91835863739SMike Smith 	/* build the read/write request */
9197540e65eSScott Long 	ad = (struct aac_disk *)bp->bio_disk->d_drv1;
920b85f5808SScott Long 
921b85f5808SScott Long 	if ((sc->flags & AAC_FLAGS_SG_64BIT) == 0) {
922b85f5808SScott Long 		fib->Header.Command = ContainerCommand;
9239e2e96d8SScott Long 		if (bp->bio_cmd == BIO_READ) {
924b85f5808SScott Long 			struct aac_blockread *br;
92535863739SMike Smith 			br = (struct aac_blockread *)&fib->data[0];
92635863739SMike Smith 			br->Command = VM_CtBlockRead;
92735863739SMike Smith 			br->ContainerId = ad->ad_container->co_mntobj.ObjectId;
92835863739SMike Smith 			br->BlockNumber = bp->bio_pblkno;
92935863739SMike Smith 			br->ByteCount = bp->bio_bcount;
93035863739SMike Smith 			fib->Header.Size += sizeof(struct aac_blockread);
93135863739SMike Smith 			cm->cm_sgtable = &br->SgMap;
93235863739SMike Smith 			cm->cm_flags |= AAC_CMD_DATAIN;
93335863739SMike Smith 		} else {
934b85f5808SScott Long 			struct aac_blockwrite *bw;
93535863739SMike Smith 			bw = (struct aac_blockwrite *)&fib->data[0];
93635863739SMike Smith 			bw->Command = VM_CtBlockWrite;
93735863739SMike Smith 			bw->ContainerId = ad->ad_container->co_mntobj.ObjectId;
93835863739SMike Smith 			bw->BlockNumber = bp->bio_pblkno;
93935863739SMike Smith 			bw->ByteCount = bp->bio_bcount;
940b85f5808SScott Long 			bw->Stable = CUNSTABLE;
94135863739SMike Smith 			fib->Header.Size += sizeof(struct aac_blockwrite);
94235863739SMike Smith 			cm->cm_flags |= AAC_CMD_DATAOUT;
94335863739SMike Smith 			cm->cm_sgtable = &bw->SgMap;
94435863739SMike Smith 		}
945b85f5808SScott Long 	} else {
946b85f5808SScott Long 		fib->Header.Command = ContainerCommand64;
947b85f5808SScott Long 		if (bp->bio_cmd == BIO_READ) {
948b85f5808SScott Long 			struct aac_blockread64 *br;
949b85f5808SScott Long 			br = (struct aac_blockread64 *)&fib->data[0];
950b85f5808SScott Long 			br->Command = VM_CtHostRead64;
951b85f5808SScott Long 			br->ContainerId = ad->ad_container->co_mntobj.ObjectId;
952b85f5808SScott Long 			br->SectorCount = bp->bio_bcount / AAC_BLOCK_SIZE;
953b85f5808SScott Long 			br->BlockNumber = bp->bio_pblkno;
954b85f5808SScott Long 			br->Pad = 0;
955b85f5808SScott Long 			br->Flags = 0;
956b85f5808SScott Long 			fib->Header.Size += sizeof(struct aac_blockread64);
957b85f5808SScott Long 			cm->cm_flags |= AAC_CMD_DATAOUT;
958eec256deSAlexander Kabaev 			cm->cm_sgtable = (struct aac_sg_table *)&br->SgMap64;
959b85f5808SScott Long 		} else {
960b85f5808SScott Long 			struct aac_blockwrite64 *bw;
961b85f5808SScott Long 			bw = (struct aac_blockwrite64 *)&fib->data[0];
962b85f5808SScott Long 			bw->Command = VM_CtHostWrite64;
963b85f5808SScott Long 			bw->ContainerId = ad->ad_container->co_mntobj.ObjectId;
964b85f5808SScott Long 			bw->SectorCount = bp->bio_bcount / AAC_BLOCK_SIZE;
965b85f5808SScott Long 			bw->BlockNumber = bp->bio_pblkno;
966b85f5808SScott Long 			bw->Pad = 0;
967b85f5808SScott Long 			bw->Flags = 0;
968b85f5808SScott Long 			fib->Header.Size += sizeof(struct aac_blockwrite64);
969b85f5808SScott Long 			cm->cm_flags |= AAC_CMD_DATAIN;
970eec256deSAlexander Kabaev 			cm->cm_sgtable = (struct aac_sg_table *)&bw->SgMap64;
971b85f5808SScott Long 		}
972b85f5808SScott Long 	}
97335863739SMike Smith 
97435863739SMike Smith 	*cmp = cm;
97535863739SMike Smith 	return(0);
97635863739SMike Smith 
97735863739SMike Smith fail:
97835863739SMike Smith 	if (bp != NULL)
9790b94a66eSMike Smith 		aac_enqueue_bio(sc, bp);
98035863739SMike Smith 	if (cm != NULL)
98135863739SMike Smith 		aac_release_command(cm);
98235863739SMike Smith 	return(ENOMEM);
98335863739SMike Smith }
98435863739SMike Smith 
985914da7d0SScott Long /*
98635863739SMike Smith  * Handle a bio-instigated command that has been completed.
98735863739SMike Smith  */
98835863739SMike Smith static void
98935863739SMike Smith aac_bio_complete(struct aac_command *cm)
99035863739SMike Smith {
99135863739SMike Smith 	struct aac_blockread_response *brr;
99235863739SMike Smith 	struct aac_blockwrite_response *bwr;
99335863739SMike Smith 	struct bio *bp;
99435863739SMike Smith 	AAC_FSAStatus status;
99535863739SMike Smith 
99635863739SMike Smith 	/* fetch relevant status and then release the command */
99735863739SMike Smith 	bp = (struct bio *)cm->cm_private;
9989e2e96d8SScott Long 	if (bp->bio_cmd == BIO_READ) {
99935863739SMike Smith 		brr = (struct aac_blockread_response *)&cm->cm_fib->data[0];
100035863739SMike Smith 		status = brr->Status;
100135863739SMike Smith 	} else {
100235863739SMike Smith 		bwr = (struct aac_blockwrite_response *)&cm->cm_fib->data[0];
100335863739SMike Smith 		status = bwr->Status;
100435863739SMike Smith 	}
100535863739SMike Smith 	aac_release_command(cm);
100635863739SMike Smith 
100735863739SMike Smith 	/* fix up the bio based on status */
100835863739SMike Smith 	if (status == ST_OK) {
100935863739SMike Smith 		bp->bio_resid = 0;
101035863739SMike Smith 	} else {
101135863739SMike Smith 		bp->bio_error = EIO;
101235863739SMike Smith 		bp->bio_flags |= BIO_ERROR;
10130b94a66eSMike Smith 		/* pass an error string out to the disk layer */
1014914da7d0SScott Long 		bp->bio_driver1 = aac_describe_code(aac_command_status_table,
1015914da7d0SScott Long 						    status);
101635863739SMike Smith 	}
10170b94a66eSMike Smith 	aac_biodone(bp);
101835863739SMike Smith }
101935863739SMike Smith 
1020914da7d0SScott Long /*
102135863739SMike Smith  * Submit a command to the controller, return when it completes.
1022b3457b51SScott Long  * XXX This is very dangerous!  If the card has gone out to lunch, we could
1023b3457b51SScott Long  *     be stuck here forever.  At the same time, signals are not caught
1024d8a0a473SScott Long  *     because there is a risk that a signal could wakeup the sleep before
1025d8a0a473SScott Long  *     the card has a chance to complete the command.  Since there is no way
1026d8a0a473SScott Long  *     to cancel a command that is in progress, we can't protect against the
1027d8a0a473SScott Long  *     card completing a command late and spamming the command and data
1028d8a0a473SScott Long  *     memory.  So, we are held hostage until the command completes.
102935863739SMike Smith  */
103035863739SMike Smith static int
1031d8a0a473SScott Long aac_wait_command(struct aac_command *cm)
103235863739SMike Smith {
1033ae543596SScott Long 	struct aac_softc *sc;
1034d8a0a473SScott Long 	int error;
103535863739SMike Smith 
103635863739SMike Smith 	debug_called(2);
103735863739SMike Smith 
1038ae543596SScott Long 	sc = cm->cm_sc;
1039ae543596SScott Long 
104035863739SMike Smith 	/* Put the command on the ready queue and get things going */
104136e0bf6eSScott Long 	cm->cm_queue = AAC_ADAP_NORM_CMD_QUEUE;
104235863739SMike Smith 	aac_enqueue_ready(cm);
1043ae543596SScott Long 	aac_startio(sc);
1044ae543596SScott Long 	error = msleep(cm, &sc->aac_io_lock, PRIBIO, "aacwait", 0);
104535863739SMike Smith 	return(error);
104635863739SMike Smith }
104735863739SMike Smith 
1048914da7d0SScott Long /*
1049914da7d0SScott Long  *Command Buffer Management
1050914da7d0SScott Long  */
105135863739SMike Smith 
1052914da7d0SScott Long /*
105335863739SMike Smith  * Allocate a command.
105435863739SMike Smith  */
1055fe3cb0e1SScott Long int
105635863739SMike Smith aac_alloc_command(struct aac_softc *sc, struct aac_command **cmp)
105735863739SMike Smith {
105835863739SMike Smith 	struct aac_command *cm;
105935863739SMike Smith 
106035863739SMike Smith 	debug_called(3);
106135863739SMike Smith 
1062ffb37f33SScott Long 	if ((cm = aac_dequeue_free(sc)) == NULL) {
1063b85f5808SScott Long 		if (sc->total_fibs < sc->aac_max_fibs) {
1064ae543596SScott Long 			sc->aifflags |= AAC_AIFFLAGS_ALLOCFIBS;
1065ae543596SScott Long 			wakeup(sc->aifthread);
1066b85f5808SScott Long 		}
1067ae543596SScott Long 		return (EBUSY);
1068ffb37f33SScott Long 	}
106935863739SMike Smith 
10700b94a66eSMike Smith 	*cmp = cm;
10710b94a66eSMike Smith 	return(0);
10720b94a66eSMike Smith }
10730b94a66eSMike Smith 
1074914da7d0SScott Long /*
10750b94a66eSMike Smith  * Release a command back to the freelist.
10760b94a66eSMike Smith  */
1077fe3cb0e1SScott Long void
10780b94a66eSMike Smith aac_release_command(struct aac_command *cm)
10790b94a66eSMike Smith {
10800b94a66eSMike Smith 	debug_called(3);
10810b94a66eSMike Smith 
10820b94a66eSMike Smith 	/* (re)initialise the command/FIB */
108335863739SMike Smith 	cm->cm_sgtable = NULL;
108435863739SMike Smith 	cm->cm_flags = 0;
108535863739SMike Smith 	cm->cm_complete = NULL;
108635863739SMike Smith 	cm->cm_private = NULL;
108735863739SMike Smith 	cm->cm_fib->Header.XferState = AAC_FIBSTATE_EMPTY;
108835863739SMike Smith 	cm->cm_fib->Header.StructType = AAC_FIBTYPE_TFIB;
108935863739SMike Smith 	cm->cm_fib->Header.Flags = 0;
109035863739SMike Smith 	cm->cm_fib->Header.SenderSize = sizeof(struct aac_fib);
109135863739SMike Smith 
109235863739SMike Smith 	/*
109335863739SMike Smith 	 * These are duplicated in aac_start to cover the case where an
109435863739SMike Smith 	 * intermediate stage may have destroyed them.  They're left
109535863739SMike Smith 	 * initialised here for debugging purposes only.
109635863739SMike Smith 	 */
1097f30ac74cSScott Long 	cm->cm_fib->Header.ReceiverFibAddress = (u_int32_t)cm->cm_fibphys;
1098f30ac74cSScott Long 	cm->cm_fib->Header.SenderData = 0;
109935863739SMike Smith 
110035863739SMike Smith 	aac_enqueue_free(cm);
110135863739SMike Smith }
110235863739SMike Smith 
1103914da7d0SScott Long /*
11040b94a66eSMike Smith  * Map helper for command/FIB allocation.
110535863739SMike Smith  */
110635863739SMike Smith static void
11070b94a66eSMike Smith aac_map_command_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error)
110835863739SMike Smith {
11098480cc63SScott Long 	uint32_t	*fibphys;
1110914da7d0SScott Long 
11118480cc63SScott Long 	fibphys = (uint32_t *)arg;
111235863739SMike Smith 
111335863739SMike Smith 	debug_called(3);
111435863739SMike Smith 
1115ffb37f33SScott Long 	*fibphys = segs[0].ds_addr;
111635863739SMike Smith }
111735863739SMike Smith 
1118914da7d0SScott Long /*
11190b94a66eSMike Smith  * Allocate and initialise commands/FIBs for this adapter.
112035863739SMike Smith  */
11210b94a66eSMike Smith static int
11220b94a66eSMike Smith aac_alloc_commands(struct aac_softc *sc)
112335863739SMike Smith {
112435863739SMike Smith 	struct aac_command *cm;
1125ffb37f33SScott Long 	struct aac_fibmap *fm;
11268480cc63SScott Long 	uint32_t fibphys;
1127ffb37f33SScott Long 	int i, error;
112835863739SMike Smith 
1129a6d35632SScott Long 	debug_called(2);
113035863739SMike Smith 
1131a6d35632SScott Long 	if (sc->total_fibs + AAC_FIB_COUNT > sc->aac_max_fibs)
1132ffb37f33SScott Long 		return (ENOMEM);
1133ffb37f33SScott Long 
11348480cc63SScott Long 	fm = malloc(sizeof(struct aac_fibmap), M_AACBUF, M_NOWAIT|M_ZERO);
1135a6d35632SScott Long 	if (fm == NULL)
1136a6d35632SScott Long 		return (ENOMEM);
1137ffb37f33SScott Long 
11380b94a66eSMike Smith 	/* allocate the FIBs in DMAable memory and load them */
1139ffb37f33SScott Long 	if (bus_dmamem_alloc(sc->aac_fib_dmat, (void **)&fm->aac_fibs,
1140ffb37f33SScott Long 			     BUS_DMA_NOWAIT, &fm->aac_fibmap)) {
114170545d1aSScott Long 		device_printf(sc->aac_dev,
114270545d1aSScott Long 			      "Not enough contiguous memory available.\n");
11438480cc63SScott Long 		free(fm, M_AACBUF);
11440b94a66eSMike Smith 		return (ENOMEM);
114535863739SMike Smith 	}
1146128aa5a0SScott Long 
1147cd481291SScott Long 	/* Ignore errors since this doesn't bounce */
1148cd481291SScott Long 	(void)bus_dmamap_load(sc->aac_fib_dmat, fm->aac_fibmap, fm->aac_fibs,
1149ffb37f33SScott Long 			      AAC_FIB_COUNT * sizeof(struct aac_fib),
1150ffb37f33SScott Long 			      aac_map_command_helper, &fibphys, 0);
1151128aa5a0SScott Long 
11520b94a66eSMike Smith 	/* initialise constant fields in the command structure */
11539148fa21SScott Long 	AAC_LOCK_ACQUIRE(&sc->aac_io_lock);
1154ffb37f33SScott Long 	bzero(fm->aac_fibs, AAC_FIB_COUNT * sizeof(struct aac_fib));
11550b94a66eSMike Smith 	for (i = 0; i < AAC_FIB_COUNT; i++) {
11568480cc63SScott Long 		cm = sc->aac_commands + sc->total_fibs;
1157ffb37f33SScott Long 		fm->aac_commands = cm;
115835863739SMike Smith 		cm->cm_sc = sc;
1159ffb37f33SScott Long 		cm->cm_fib = fm->aac_fibs + i;
11608480cc63SScott Long 		cm->cm_fibphys = fibphys + (i * sizeof(struct aac_fib));
1161cb0d64b9SScott Long 		cm->cm_index = sc->total_fibs;
116235863739SMike Smith 
1163ffb37f33SScott Long 		if ((error = bus_dmamap_create(sc->aac_buffer_dmat, 0,
1164ffb37f33SScott Long 					       &cm->cm_datamap)) == 0)
116535863739SMike Smith 			aac_release_command(cm);
1166ffb37f33SScott Long 		else
11678480cc63SScott Long 			break;
11688480cc63SScott Long 		sc->total_fibs++;
116935863739SMike Smith 	}
1170ffb37f33SScott Long 
11718480cc63SScott Long 	if (i > 0) {
1172ffb37f33SScott Long 		TAILQ_INSERT_TAIL(&sc->aac_fibmap_tqh, fm, fm_link);
1173a6d35632SScott Long 		debug(1, "total_fibs= %d\n", sc->total_fibs);
11749148fa21SScott Long 		AAC_LOCK_RELEASE(&sc->aac_io_lock);
11750b94a66eSMike Smith 		return (0);
117635863739SMike Smith 	}
117735863739SMike Smith 
11789148fa21SScott Long 	AAC_LOCK_RELEASE(&sc->aac_io_lock);
11798480cc63SScott Long 	bus_dmamap_unload(sc->aac_fib_dmat, fm->aac_fibmap);
11808480cc63SScott Long 	bus_dmamem_free(sc->aac_fib_dmat, fm->aac_fibs, fm->aac_fibmap);
11818480cc63SScott Long 	free(fm, M_AACBUF);
11828480cc63SScott Long 	return (ENOMEM);
11838480cc63SScott Long }
11848480cc63SScott Long 
1185914da7d0SScott Long /*
11860b94a66eSMike Smith  * Free FIBs owned by this adapter.
118735863739SMike Smith  */
118835863739SMike Smith static void
11898480cc63SScott Long aac_free_commands(struct aac_softc *sc)
119035863739SMike Smith {
11918480cc63SScott Long 	struct aac_fibmap *fm;
1192ffb37f33SScott Long 	struct aac_command *cm;
119335863739SMike Smith 	int i;
119435863739SMike Smith 
119535863739SMike Smith 	debug_called(1);
119635863739SMike Smith 
11978480cc63SScott Long 	while ((fm = TAILQ_FIRST(&sc->aac_fibmap_tqh)) != NULL) {
11988480cc63SScott Long 
11998480cc63SScott Long 		TAILQ_REMOVE(&sc->aac_fibmap_tqh, fm, fm_link);
12008480cc63SScott Long 		/*
12018480cc63SScott Long 		 * We check against total_fibs to handle partially
12028480cc63SScott Long 		 * allocated blocks.
12038480cc63SScott Long 		 */
12048480cc63SScott Long 		for (i = 0; i < AAC_FIB_COUNT && sc->total_fibs--; i++) {
1205ffb37f33SScott Long 			cm = fm->aac_commands + i;
1206ffb37f33SScott Long 			bus_dmamap_destroy(sc->aac_buffer_dmat, cm->cm_datamap);
1207ffb37f33SScott Long 		}
1208ffb37f33SScott Long 		bus_dmamap_unload(sc->aac_fib_dmat, fm->aac_fibmap);
1209ffb37f33SScott Long 		bus_dmamem_free(sc->aac_fib_dmat, fm->aac_fibs, fm->aac_fibmap);
12108480cc63SScott Long 		free(fm, M_AACBUF);
12118480cc63SScott Long 	}
121235863739SMike Smith }
121335863739SMike Smith 
1214914da7d0SScott Long /*
121535863739SMike Smith  * Command-mapping helper function - populate this command's s/g table.
121635863739SMike Smith  */
121735863739SMike Smith static void
121835863739SMike Smith aac_map_command_sg(void *arg, bus_dma_segment_t *segs, int nseg, int error)
121935863739SMike Smith {
1220cd481291SScott Long 	struct aac_softc *sc;
1221914da7d0SScott Long 	struct aac_command *cm;
1222914da7d0SScott Long 	struct aac_fib *fib;
122335863739SMike Smith 	int i;
122435863739SMike Smith 
122535863739SMike Smith 	debug_called(3);
122635863739SMike Smith 
1227914da7d0SScott Long 	cm = (struct aac_command *)arg;
1228cd481291SScott Long 	sc = cm->cm_sc;
1229914da7d0SScott Long 	fib = cm->cm_fib;
1230914da7d0SScott Long 
123135863739SMike Smith 	/* copy into the FIB */
1232b85f5808SScott Long 	if (cm->cm_sgtable != NULL) {
1233b85f5808SScott Long 		if ((cm->cm_sc->flags & AAC_FLAGS_SG_64BIT) == 0) {
1234b85f5808SScott Long 			struct aac_sg_table *sg;
1235b85f5808SScott Long 			sg = cm->cm_sgtable;
123635863739SMike Smith 			sg->SgCount = nseg;
123735863739SMike Smith 			for (i = 0; i < nseg; i++) {
123835863739SMike Smith 				sg->SgEntry[i].SgAddress = segs[i].ds_addr;
123935863739SMike Smith 				sg->SgEntry[i].SgByteCount = segs[i].ds_len;
124035863739SMike Smith 			}
124135863739SMike Smith 			/* update the FIB size for the s/g count */
124235863739SMike Smith 			fib->Header.Size += nseg * sizeof(struct aac_sg_entry);
1243b85f5808SScott Long 		} else {
1244b85f5808SScott Long 			struct aac_sg_table64 *sg;
1245b85f5808SScott Long 			sg = (struct aac_sg_table64 *)cm->cm_sgtable;
1246b85f5808SScott Long 			sg->SgCount = nseg;
1247b85f5808SScott Long 			for (i = 0; i < nseg; i++) {
1248b85f5808SScott Long 				sg->SgEntry64[i].SgAddress = segs[i].ds_addr;
1249b85f5808SScott Long 				sg->SgEntry64[i].SgByteCount = segs[i].ds_len;
125035863739SMike Smith 			}
1251b85f5808SScott Long 			/* update the FIB size for the s/g count */
1252b85f5808SScott Long 			fib->Header.Size += nseg*sizeof(struct aac_sg_entry64);
1253b85f5808SScott Long 		}
1254b85f5808SScott Long 	}
125535863739SMike Smith 
1256cd481291SScott Long 	/* Fix up the address values in the FIB.  Use the command array index
1257cd481291SScott Long 	 * instead of a pointer since these fields are only 32 bits.  Shift
1258cd481291SScott Long 	 * the SenderFibAddress over to make room for the fast response bit.
125935863739SMike Smith 	 */
1260cd481291SScott Long 	cm->cm_fib->Header.SenderFibAddress = (cm->cm_index << 1);
1261cd481291SScott Long 	cm->cm_fib->Header.ReceiverFibAddress = cm->cm_fibphys;
126235863739SMike Smith 
1263cd481291SScott Long 	/* save a pointer to the command for speedy reverse-lookup */
1264cd481291SScott Long 	cm->cm_fib->Header.SenderData = cm->cm_index;
126535863739SMike Smith 
126635863739SMike Smith 	if (cm->cm_flags & AAC_CMD_DATAIN)
1267c6eafcf2SScott Long 		bus_dmamap_sync(sc->aac_buffer_dmat, cm->cm_datamap,
1268c6eafcf2SScott Long 				BUS_DMASYNC_PREREAD);
126935863739SMike Smith 	if (cm->cm_flags & AAC_CMD_DATAOUT)
1270c6eafcf2SScott Long 		bus_dmamap_sync(sc->aac_buffer_dmat, cm->cm_datamap,
1271c6eafcf2SScott Long 				BUS_DMASYNC_PREWRITE);
127235863739SMike Smith 	cm->cm_flags |= AAC_CMD_MAPPED;
1273cd481291SScott Long 
1274397fa34fSScott Long 	/* Put the FIB on the outbound queue */
12754102d44bSScott Long 	if (aac_enqueue_fib(sc, cm->cm_queue, cm) == EBUSY) {
12764102d44bSScott Long 		aac_unmap_command(cm);
1277397fa34fSScott Long 		sc->flags |= AAC_QUEUE_FRZN;
1278cd481291SScott Long 		aac_requeue_ready(cm);
12794102d44bSScott Long 	}
1280cd481291SScott Long 
1281cd481291SScott Long 	return;
128235863739SMike Smith }
128335863739SMike Smith 
1284914da7d0SScott Long /*
128535863739SMike Smith  * Unmap a command from controller-visible space.
128635863739SMike Smith  */
128735863739SMike Smith static void
128835863739SMike Smith aac_unmap_command(struct aac_command *cm)
128935863739SMike Smith {
1290914da7d0SScott Long 	struct aac_softc *sc;
129135863739SMike Smith 
129235863739SMike Smith 	debug_called(2);
129335863739SMike Smith 
1294914da7d0SScott Long 	sc = cm->cm_sc;
1295914da7d0SScott Long 
129635863739SMike Smith 	if (!(cm->cm_flags & AAC_CMD_MAPPED))
129735863739SMike Smith 		return;
129835863739SMike Smith 
129935863739SMike Smith 	if (cm->cm_datalen != 0) {
130035863739SMike Smith 		if (cm->cm_flags & AAC_CMD_DATAIN)
1301c6eafcf2SScott Long 			bus_dmamap_sync(sc->aac_buffer_dmat, cm->cm_datamap,
1302c6eafcf2SScott Long 					BUS_DMASYNC_POSTREAD);
130335863739SMike Smith 		if (cm->cm_flags & AAC_CMD_DATAOUT)
1304c6eafcf2SScott Long 			bus_dmamap_sync(sc->aac_buffer_dmat, cm->cm_datamap,
1305c6eafcf2SScott Long 					BUS_DMASYNC_POSTWRITE);
130635863739SMike Smith 
130735863739SMike Smith 		bus_dmamap_unload(sc->aac_buffer_dmat, cm->cm_datamap);
130835863739SMike Smith 	}
130935863739SMike Smith 	cm->cm_flags &= ~AAC_CMD_MAPPED;
131035863739SMike Smith }
131135863739SMike Smith 
1312914da7d0SScott Long /*
1313914da7d0SScott Long  * Hardware Interface
1314914da7d0SScott Long  */
131535863739SMike Smith 
1316914da7d0SScott Long /*
131735863739SMike Smith  * Initialise the adapter.
131835863739SMike Smith  */
131935863739SMike Smith static void
132035863739SMike Smith aac_common_map(void *arg, bus_dma_segment_t *segs, int nseg, int error)
132135863739SMike Smith {
1322914da7d0SScott Long 	struct aac_softc *sc;
132335863739SMike Smith 
132435863739SMike Smith 	debug_called(1);
132535863739SMike Smith 
1326914da7d0SScott Long 	sc = (struct aac_softc *)arg;
1327914da7d0SScott Long 
132835863739SMike Smith 	sc->aac_common_busaddr = segs[0].ds_addr;
132935863739SMike Smith }
133035863739SMike Smith 
1331a6d35632SScott Long static int
1332a6d35632SScott Long aac_check_firmware(struct aac_softc *sc)
1333a6d35632SScott Long {
1334a6d35632SScott Long 	u_int32_t major, minor, options;
1335a6d35632SScott Long 
1336a6d35632SScott Long 	debug_called(1);
1337a6d35632SScott Long 
1338fe94b852SScott Long 	/*
1339fe94b852SScott Long 	 * Retrieve the firmware version numbers.  Dell PERC2/QC cards with
1340fe94b852SScott Long 	 * firmware version 1.x are not compatible with this driver.
1341fe94b852SScott Long 	 */
1342a6d35632SScott Long 	if (sc->flags & AAC_FLAGS_PERC2QC) {
1343fe94b852SScott Long 		if (aac_sync_command(sc, AAC_MONKER_GETKERNVER, 0, 0, 0, 0,
1344fe94b852SScott Long 				     NULL)) {
1345fe94b852SScott Long 			device_printf(sc->aac_dev,
1346fe94b852SScott Long 				      "Error reading firmware version\n");
1347fe94b852SScott Long 			return (EIO);
1348fe94b852SScott Long 		}
1349fe94b852SScott Long 
1350fe94b852SScott Long 		/* These numbers are stored as ASCII! */
1351a6d35632SScott Long 		major = (AAC_GET_MAILBOX(sc, 1) & 0xff) - 0x30;
1352a6d35632SScott Long 		minor = (AAC_GET_MAILBOX(sc, 2) & 0xff) - 0x30;
1353fe94b852SScott Long 		if (major == 1) {
1354fe94b852SScott Long 			device_printf(sc->aac_dev,
1355fe94b852SScott Long 			    "Firmware version %d.%d is not supported.\n",
1356fe94b852SScott Long 			    major, minor);
1357fe94b852SScott Long 			return (EINVAL);
1358fe94b852SScott Long 		}
1359fe94b852SScott Long 	}
1360fe94b852SScott Long 
1361a6d35632SScott Long 	/*
1362a6d35632SScott Long 	 * Retrieve the capabilities/supported options word so we know what
1363a6d35632SScott Long 	 * work-arounds to enable.
1364a6d35632SScott Long 	 */
1365a6d35632SScott Long 	if (aac_sync_command(sc, AAC_MONKER_GETINFO, 0, 0, 0, 0, NULL)) {
1366a6d35632SScott Long 		device_printf(sc->aac_dev, "RequestAdapterInfo failed\n");
1367a6d35632SScott Long 		return (EIO);
1368a6d35632SScott Long 	}
1369a6d35632SScott Long 	options = AAC_GET_MAILBOX(sc, 1);
1370a6d35632SScott Long 	sc->supported_options = options;
1371a6d35632SScott Long 
1372a6d35632SScott Long 	if ((options & AAC_SUPPORTED_4GB_WINDOW) != 0 &&
1373a6d35632SScott Long 	    (sc->flags & AAC_FLAGS_NO4GB) == 0)
1374a6d35632SScott Long 		sc->flags |= AAC_FLAGS_4GB_WINDOW;
1375a6d35632SScott Long 	if (options & AAC_SUPPORTED_NONDASD)
1376a6d35632SScott Long 		sc->flags |= AAC_FLAGS_ENABLE_CAM;
1377cd481291SScott Long 	if ((options & AAC_SUPPORTED_SGMAP_HOST64) != 0
1378cd481291SScott Long 	     && (sizeof(bus_addr_t) > 4)) {
1379a6d35632SScott Long 		device_printf(sc->aac_dev, "Enabling 64-bit address support\n");
1380a6d35632SScott Long 		sc->flags |= AAC_FLAGS_SG_64BIT;
1381a6d35632SScott Long 	}
1382a6d35632SScott Long 
1383a6d35632SScott Long 	/* Check for broken hardware that does a lower number of commands */
1384a6d35632SScott Long 	if ((sc->flags & AAC_FLAGS_256FIBS) == 0)
1385a6d35632SScott Long 		sc->aac_max_fibs = AAC_MAX_FIBS;
1386a6d35632SScott Long 	else
1387a6d35632SScott Long 		sc->aac_max_fibs = 256;
1388a6d35632SScott Long 
1389fe94b852SScott Long 	return (0);
1390fe94b852SScott Long }
1391fe94b852SScott Long 
139235863739SMike Smith static int
139335863739SMike Smith aac_init(struct aac_softc *sc)
139435863739SMike Smith {
139535863739SMike Smith 	struct aac_adapter_init	*ip;
139635863739SMike Smith 	time_t then;
1397b88ffdc8SScott Long 	u_int32_t code, qoffset;
1398a6d35632SScott Long 	int error;
139935863739SMike Smith 
140035863739SMike Smith 	debug_called(1);
140135863739SMike Smith 
140235863739SMike Smith 	/*
140335863739SMike Smith 	 * First wait for the adapter to come ready.
140435863739SMike Smith 	 */
140535863739SMike Smith 	then = time_second;
140635863739SMike Smith 	do {
140735863739SMike Smith 		code = AAC_GET_FWSTATUS(sc);
140835863739SMike Smith 		if (code & AAC_SELF_TEST_FAILED) {
140935863739SMike Smith 			device_printf(sc->aac_dev, "FATAL: selftest failed\n");
141035863739SMike Smith 			return(ENXIO);
141135863739SMike Smith 		}
141235863739SMike Smith 		if (code & AAC_KERNEL_PANIC) {
1413914da7d0SScott Long 			device_printf(sc->aac_dev,
1414914da7d0SScott Long 				      "FATAL: controller kernel panic\n");
141535863739SMike Smith 			return(ENXIO);
141635863739SMike Smith 		}
141735863739SMike Smith 		if (time_second > (then + AAC_BOOT_TIMEOUT)) {
1418914da7d0SScott Long 			device_printf(sc->aac_dev,
1419914da7d0SScott Long 				      "FATAL: controller not coming ready, "
1420c6eafcf2SScott Long 					   "status %x\n", code);
142135863739SMike Smith 			return(ENXIO);
142235863739SMike Smith 		}
142335863739SMike Smith 	} while (!(code & AAC_UP_AND_RUNNING));
142435863739SMike Smith 
1425a6d35632SScott Long 	error = ENOMEM;
1426a6d35632SScott Long 	/*
1427a6d35632SScott Long 	 * Create DMA tag for mapping buffers into controller-addressable space.
1428a6d35632SScott Long 	 */
1429a6d35632SScott Long 	if (bus_dma_tag_create(sc->aac_parent_dmat, 	/* parent */
1430a6d35632SScott Long 			       1, 0, 			/* algnmnt, boundary */
1431a6d35632SScott Long 			       (sc->flags & AAC_FLAGS_SG_64BIT) ?
1432a6d35632SScott Long 			       BUS_SPACE_MAXADDR :
1433a6d35632SScott Long 			       BUS_SPACE_MAXADDR_32BIT,	/* lowaddr */
1434a6d35632SScott Long 			       BUS_SPACE_MAXADDR, 	/* highaddr */
1435a6d35632SScott Long 			       NULL, NULL, 		/* filter, filterarg */
1436a6d35632SScott Long 			       MAXBSIZE,		/* maxsize */
1437a6d35632SScott Long 			       AAC_MAXSGENTRIES,	/* nsegments */
1438a6d35632SScott Long 			       MAXBSIZE,		/* maxsegsize */
1439a6d35632SScott Long 			       BUS_DMA_ALLOCNOW,	/* flags */
1440f6b1c44dSScott Long 			       busdma_lock_mutex,	/* lockfunc */
1441f6b1c44dSScott Long 			       &sc->aac_io_lock,	/* lockfuncarg */
1442a6d35632SScott Long 			       &sc->aac_buffer_dmat)) {
1443a6d35632SScott Long 		device_printf(sc->aac_dev, "can't allocate buffer DMA tag\n");
1444a6d35632SScott Long 		goto out;
1445a6d35632SScott Long 	}
1446a6d35632SScott Long 
1447a6d35632SScott Long 	/*
1448a6d35632SScott Long 	 * Create DMA tag for mapping FIBs into controller-addressable space..
1449a6d35632SScott Long 	 */
1450a6d35632SScott Long 	if (bus_dma_tag_create(sc->aac_parent_dmat,	/* parent */
1451a6d35632SScott Long 			       1, 0, 			/* algnmnt, boundary */
1452a6d35632SScott Long 			       (sc->flags & AAC_FLAGS_4GB_WINDOW) ?
1453a6d35632SScott Long 			       BUS_SPACE_MAXADDR_32BIT :
1454a6d35632SScott Long 			       0x7fffffff,		/* lowaddr */
1455a6d35632SScott Long 			       BUS_SPACE_MAXADDR, 	/* highaddr */
1456a6d35632SScott Long 			       NULL, NULL, 		/* filter, filterarg */
1457a6d35632SScott Long 			       AAC_FIB_COUNT *
1458a6d35632SScott Long 			       sizeof(struct aac_fib),  /* maxsize */
1459a6d35632SScott Long 			       1,			/* nsegments */
1460a6d35632SScott Long 			       AAC_FIB_COUNT *
1461a6d35632SScott Long 			       sizeof(struct aac_fib),	/* maxsegsize */
1462a6d35632SScott Long 			       BUS_DMA_ALLOCNOW,	/* flags */
1463f6b1c44dSScott Long 			       NULL, NULL,		/* No locking needed */
1464a6d35632SScott Long 			       &sc->aac_fib_dmat)) {
1465a6d35632SScott Long 		device_printf(sc->aac_dev, "can't allocate FIB DMA tag\n");;
1466a6d35632SScott Long 		goto out;
1467a6d35632SScott Long 	}
1468a6d35632SScott Long 
146935863739SMike Smith 	/*
147035863739SMike Smith 	 * Create DMA tag for the common structure and allocate it.
147135863739SMike Smith 	 */
147235863739SMike Smith 	if (bus_dma_tag_create(sc->aac_parent_dmat, 	/* parent */
1473c6eafcf2SScott Long 			       1, 0,			/* algnmnt, boundary */
1474a6d35632SScott Long 			       (sc->flags & AAC_FLAGS_4GB_WINDOW) ?
1475a6d35632SScott Long 			       BUS_SPACE_MAXADDR_32BIT :
1476a6d35632SScott Long 			       0x7fffffff,		/* lowaddr */
147735863739SMike Smith 			       BUS_SPACE_MAXADDR, 	/* highaddr */
147835863739SMike Smith 			       NULL, NULL, 		/* filter, filterarg */
1479ffb37f33SScott Long 			       8192 + sizeof(struct aac_common), /* maxsize */
1480914da7d0SScott Long 			       1,			/* nsegments */
148135863739SMike Smith 			       BUS_SPACE_MAXSIZE_32BIT,	/* maxsegsize */
1482a6d35632SScott Long 			       BUS_DMA_ALLOCNOW,	/* flags */
1483f6b1c44dSScott Long 			       NULL, NULL,		/* No locking needed */
148435863739SMike Smith 			       &sc->aac_common_dmat)) {
1485914da7d0SScott Long 		device_printf(sc->aac_dev,
1486914da7d0SScott Long 			      "can't allocate common structure DMA tag\n");
1487a6d35632SScott Long 		goto out;
148835863739SMike Smith 	}
1489c6eafcf2SScott Long 	if (bus_dmamem_alloc(sc->aac_common_dmat, (void **)&sc->aac_common,
1490c6eafcf2SScott Long 			     BUS_DMA_NOWAIT, &sc->aac_common_dmamap)) {
149135863739SMike Smith 		device_printf(sc->aac_dev, "can't allocate common structure\n");
1492a6d35632SScott Long 		goto out;
149335863739SMike Smith 	}
1494ffb37f33SScott Long 
1495ffb37f33SScott Long 	/*
1496ffb37f33SScott Long 	 * Work around a bug in the 2120 and 2200 that cannot DMA commands
1497ffb37f33SScott Long 	 * below address 8192 in physical memory.
1498ffb37f33SScott Long 	 * XXX If the padding is not needed, can it be put to use instead
1499ffb37f33SScott Long 	 * of ignored?
1500ffb37f33SScott Long 	 */
1501cd481291SScott Long 	(void)bus_dmamap_load(sc->aac_common_dmat, sc->aac_common_dmamap,
1502ffb37f33SScott Long 			sc->aac_common, 8192 + sizeof(*sc->aac_common),
1503ffb37f33SScott Long 			aac_common_map, sc, 0);
1504ffb37f33SScott Long 
1505ffb37f33SScott Long 	if (sc->aac_common_busaddr < 8192) {
1506eec256deSAlexander Kabaev 		sc->aac_common = (struct aac_common *)
1507eec256deSAlexander Kabaev 		    ((uint8_t *)sc->aac_common + 8192);
1508ffb37f33SScott Long 		sc->aac_common_busaddr += 8192;
1509ffb37f33SScott Long 	}
151035863739SMike Smith 	bzero(sc->aac_common, sizeof(*sc->aac_common));
151135863739SMike Smith 
1512ffb37f33SScott Long 	/* Allocate some FIBs and associated command structs */
1513ffb37f33SScott Long 	TAILQ_INIT(&sc->aac_fibmap_tqh);
1514ffb37f33SScott Long 	sc->aac_commands = malloc(AAC_MAX_FIBS * sizeof(struct aac_command),
15158480cc63SScott Long 				  M_AACBUF, M_WAITOK|M_ZERO);
15168480cc63SScott Long 	while (sc->total_fibs < AAC_PREALLOCATE_FIBS) {
1517ffb37f33SScott Long 		if (aac_alloc_commands(sc) != 0)
1518ffb37f33SScott Long 			break;
1519ffb37f33SScott Long 	}
1520ffb37f33SScott Long 	if (sc->total_fibs == 0)
1521a6d35632SScott Long 		goto out;
1522ffb37f33SScott Long 
152335863739SMike Smith 	/*
1524914da7d0SScott Long 	 * Fill in the init structure.  This tells the adapter about the
1525914da7d0SScott Long 	 * physical location of various important shared data structures.
152635863739SMike Smith 	 */
152735863739SMike Smith 	ip = &sc->aac_common->ac_init;
152835863739SMike Smith 	ip->InitStructRevision = AAC_INIT_STRUCT_REVISION;
1529f30ac74cSScott Long 	ip->MiniPortRevision = AAC_INIT_STRUCT_MINIPORT_REVISION;
153035863739SMike Smith 
1531c6eafcf2SScott Long 	ip->AdapterFibsPhysicalAddress = sc->aac_common_busaddr +
1532c6eafcf2SScott Long 					 offsetof(struct aac_common, ac_fibs);
1533149af931SScott Long 	ip->AdapterFibsVirtualAddress = 0;
153435863739SMike Smith 	ip->AdapterFibsSize = AAC_ADAPTER_FIBS * sizeof(struct aac_fib);
153535863739SMike Smith 	ip->AdapterFibAlign = sizeof(struct aac_fib);
153635863739SMike Smith 
1537c6eafcf2SScott Long 	ip->PrintfBufferAddress = sc->aac_common_busaddr +
1538c6eafcf2SScott Long 				  offsetof(struct aac_common, ac_printf);
153935863739SMike Smith 	ip->PrintfBufferSize = AAC_PRINTF_BUFSIZE;
154035863739SMike Smith 
15414b00f859SScott Long 	/*
15424b00f859SScott Long 	 * The adapter assumes that pages are 4K in size, except on some
15434b00f859SScott Long  	 * broken firmware versions that do the page->byte conversion twice,
15444b00f859SScott Long 	 * therefore 'assuming' that this value is in 16MB units (2^24).
15454b00f859SScott Long 	 * Round up since the granularity is so high.
15464b00f859SScott Long 	 */
1547f30ac74cSScott Long 	ip->HostPhysMemPages = ctob(physmem) / AAC_PAGE_SIZE;
15484b00f859SScott Long 	if (sc->flags & AAC_FLAGS_BROKEN_MEMMAP) {
15494b00f859SScott Long 		ip->HostPhysMemPages =
15504b00f859SScott Long 		    (ip->HostPhysMemPages + AAC_PAGE_SIZE) / AAC_PAGE_SIZE;
1551204c0befSScott Long 	}
155235863739SMike Smith 	ip->HostElapsedSeconds = time_second;	/* reset later if invalid */
155335863739SMike Smith 
155435863739SMike Smith 	/*
1555c6eafcf2SScott Long 	 * Initialise FIB queues.  Note that it appears that the layout of the
1556c6eafcf2SScott Long 	 * indexes and the segmentation of the entries may be mandated by the
1557c6eafcf2SScott Long 	 * adapter, which is only told about the base of the queue index fields.
155835863739SMike Smith 	 *
155935863739SMike Smith 	 * The initial values of the indices are assumed to inform the adapter
1560914da7d0SScott Long 	 * of the sizes of the respective queues, and theoretically it could
1561914da7d0SScott Long 	 * work out the entire layout of the queue structures from this.  We
1562914da7d0SScott Long 	 * take the easy route and just lay this area out like everyone else
1563914da7d0SScott Long 	 * does.
156435863739SMike Smith 	 *
1565914da7d0SScott Long 	 * The Linux driver uses a much more complex scheme whereby several
1566914da7d0SScott Long 	 * header records are kept for each queue.  We use a couple of generic
1567914da7d0SScott Long 	 * list manipulation functions which 'know' the size of each list by
1568914da7d0SScott Long 	 * virtue of a table.
156935863739SMike Smith 	 */
1570b88ffdc8SScott Long 	qoffset = offsetof(struct aac_common, ac_qbuf) + AAC_QUEUE_ALIGN;
15710bcbebd6SScott Long 	qoffset &= ~(AAC_QUEUE_ALIGN - 1);
15720bcbebd6SScott Long 	sc->aac_queues =
15730bcbebd6SScott Long 	    (struct aac_queue_table *)((uintptr_t)sc->aac_common + qoffset);
1574b88ffdc8SScott Long 	ip->CommHeaderAddress = sc->aac_common_busaddr + qoffset;
157535863739SMike Smith 
1576c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_NORM_CMD_QUEUE][AAC_PRODUCER_INDEX] =
1577c6eafcf2SScott Long 		AAC_HOST_NORM_CMD_ENTRIES;
1578c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_NORM_CMD_QUEUE][AAC_CONSUMER_INDEX] =
1579c6eafcf2SScott Long 		AAC_HOST_NORM_CMD_ENTRIES;
1580c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_HIGH_CMD_QUEUE][AAC_PRODUCER_INDEX] =
1581c6eafcf2SScott Long 		AAC_HOST_HIGH_CMD_ENTRIES;
1582c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_HIGH_CMD_QUEUE][AAC_CONSUMER_INDEX] =
1583c6eafcf2SScott Long 		AAC_HOST_HIGH_CMD_ENTRIES;
1584c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_NORM_CMD_QUEUE][AAC_PRODUCER_INDEX] =
1585c6eafcf2SScott Long 		AAC_ADAP_NORM_CMD_ENTRIES;
1586c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_NORM_CMD_QUEUE][AAC_CONSUMER_INDEX] =
1587c6eafcf2SScott Long 		AAC_ADAP_NORM_CMD_ENTRIES;
1588c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_HIGH_CMD_QUEUE][AAC_PRODUCER_INDEX] =
1589c6eafcf2SScott Long 		AAC_ADAP_HIGH_CMD_ENTRIES;
1590c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_HIGH_CMD_QUEUE][AAC_CONSUMER_INDEX] =
1591c6eafcf2SScott Long 		AAC_ADAP_HIGH_CMD_ENTRIES;
1592c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_NORM_RESP_QUEUE][AAC_PRODUCER_INDEX]=
1593c6eafcf2SScott Long 		AAC_HOST_NORM_RESP_ENTRIES;
1594c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_NORM_RESP_QUEUE][AAC_CONSUMER_INDEX]=
1595c6eafcf2SScott Long 		AAC_HOST_NORM_RESP_ENTRIES;
1596c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_HIGH_RESP_QUEUE][AAC_PRODUCER_INDEX]=
1597c6eafcf2SScott Long 		AAC_HOST_HIGH_RESP_ENTRIES;
1598c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_HIGH_RESP_QUEUE][AAC_CONSUMER_INDEX]=
1599c6eafcf2SScott Long 		AAC_HOST_HIGH_RESP_ENTRIES;
1600c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_NORM_RESP_QUEUE][AAC_PRODUCER_INDEX]=
1601c6eafcf2SScott Long 		AAC_ADAP_NORM_RESP_ENTRIES;
1602c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_NORM_RESP_QUEUE][AAC_CONSUMER_INDEX]=
1603c6eafcf2SScott Long 		AAC_ADAP_NORM_RESP_ENTRIES;
1604c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_HIGH_RESP_QUEUE][AAC_PRODUCER_INDEX]=
1605c6eafcf2SScott Long 		AAC_ADAP_HIGH_RESP_ENTRIES;
1606c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_HIGH_RESP_QUEUE][AAC_CONSUMER_INDEX]=
1607c6eafcf2SScott Long 		AAC_ADAP_HIGH_RESP_ENTRIES;
1608c6eafcf2SScott Long 	sc->aac_qentries[AAC_HOST_NORM_CMD_QUEUE] =
1609c6eafcf2SScott Long 		&sc->aac_queues->qt_HostNormCmdQueue[0];
1610c6eafcf2SScott Long 	sc->aac_qentries[AAC_HOST_HIGH_CMD_QUEUE] =
1611c6eafcf2SScott Long 		&sc->aac_queues->qt_HostHighCmdQueue[0];
1612c6eafcf2SScott Long 	sc->aac_qentries[AAC_ADAP_NORM_CMD_QUEUE] =
1613c6eafcf2SScott Long 		&sc->aac_queues->qt_AdapNormCmdQueue[0];
1614c6eafcf2SScott Long 	sc->aac_qentries[AAC_ADAP_HIGH_CMD_QUEUE] =
1615c6eafcf2SScott Long 		&sc->aac_queues->qt_AdapHighCmdQueue[0];
1616c6eafcf2SScott Long 	sc->aac_qentries[AAC_HOST_NORM_RESP_QUEUE] =
1617c6eafcf2SScott Long 		&sc->aac_queues->qt_HostNormRespQueue[0];
1618c6eafcf2SScott Long 	sc->aac_qentries[AAC_HOST_HIGH_RESP_QUEUE] =
1619c6eafcf2SScott Long 		&sc->aac_queues->qt_HostHighRespQueue[0];
1620c6eafcf2SScott Long 	sc->aac_qentries[AAC_ADAP_NORM_RESP_QUEUE] =
1621c6eafcf2SScott Long 		&sc->aac_queues->qt_AdapNormRespQueue[0];
1622c6eafcf2SScott Long 	sc->aac_qentries[AAC_ADAP_HIGH_RESP_QUEUE] =
1623c6eafcf2SScott Long 		&sc->aac_queues->qt_AdapHighRespQueue[0];
162435863739SMike Smith 
162535863739SMike Smith 	/*
162635863739SMike Smith 	 * Do controller-type-specific initialisation
162735863739SMike Smith 	 */
162835863739SMike Smith 	switch (sc->aac_hwif) {
162935863739SMike Smith 	case AAC_HWIF_I960RX:
163035863739SMike Smith 		AAC_SETREG4(sc, AAC_RX_ODBR, ~0);
163135863739SMike Smith 		break;
163235863739SMike Smith 	}
163335863739SMike Smith 
163435863739SMike Smith 	/*
163535863739SMike Smith 	 * Give the init structure to the controller.
163635863739SMike Smith 	 */
163735863739SMike Smith 	if (aac_sync_command(sc, AAC_MONKER_INITSTRUCT,
1638914da7d0SScott Long 			     sc->aac_common_busaddr +
1639914da7d0SScott Long 			     offsetof(struct aac_common, ac_init), 0, 0, 0,
1640914da7d0SScott Long 			     NULL)) {
1641914da7d0SScott Long 		device_printf(sc->aac_dev,
1642914da7d0SScott Long 			      "error establishing init structure\n");
1643a6d35632SScott Long 		error = EIO;
1644a6d35632SScott Long 		goto out;
164535863739SMike Smith 	}
164635863739SMike Smith 
1647a6d35632SScott Long 	error = 0;
1648a6d35632SScott Long out:
1649a6d35632SScott Long 	return(error);
165035863739SMike Smith }
165135863739SMike Smith 
1652914da7d0SScott Long /*
165335863739SMike Smith  * Send a synchronous command to the controller and wait for a result.
165435863739SMike Smith  */
165535863739SMike Smith static int
165635863739SMike Smith aac_sync_command(struct aac_softc *sc, u_int32_t command,
165735863739SMike Smith 		 u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3,
165835863739SMike Smith 		 u_int32_t *sp)
165935863739SMike Smith {
166035863739SMike Smith 	time_t then;
166135863739SMike Smith 	u_int32_t status;
166235863739SMike Smith 
166335863739SMike Smith 	debug_called(3);
166435863739SMike Smith 
166535863739SMike Smith 	/* populate the mailbox */
166635863739SMike Smith 	AAC_SET_MAILBOX(sc, command, arg0, arg1, arg2, arg3);
166735863739SMike Smith 
166835863739SMike Smith 	/* ensure the sync command doorbell flag is cleared */
166935863739SMike Smith 	AAC_CLEAR_ISTATUS(sc, AAC_DB_SYNC_COMMAND);
167035863739SMike Smith 
167135863739SMike Smith 	/* then set it to signal the adapter */
167235863739SMike Smith 	AAC_QNOTIFY(sc, AAC_DB_SYNC_COMMAND);
167335863739SMike Smith 
167435863739SMike Smith 	/* spin waiting for the command to complete */
167535863739SMike Smith 	then = time_second;
167635863739SMike Smith 	do {
167735863739SMike Smith 		if (time_second > (then + AAC_IMMEDIATE_TIMEOUT)) {
1678a6d35632SScott Long 			debug(1, "timed out");
167935863739SMike Smith 			return(EIO);
168035863739SMike Smith 		}
168135863739SMike Smith 	} while (!(AAC_GET_ISTATUS(sc) & AAC_DB_SYNC_COMMAND));
168235863739SMike Smith 
168335863739SMike Smith 	/* clear the completion flag */
168435863739SMike Smith 	AAC_CLEAR_ISTATUS(sc, AAC_DB_SYNC_COMMAND);
168535863739SMike Smith 
168635863739SMike Smith 	/* get the command status */
1687a6d35632SScott Long 	status = AAC_GET_MAILBOX(sc, 0);
168835863739SMike Smith 	if (sp != NULL)
168935863739SMike Smith 		*sp = status;
16900b94a66eSMike Smith 	return(0);
169135863739SMike Smith }
169235863739SMike Smith 
1693cbfd045bSScott Long int
169435863739SMike Smith aac_sync_fib(struct aac_softc *sc, u_int32_t command, u_int32_t xferstate,
1695cbfd045bSScott Long 		 struct aac_fib *fib, u_int16_t datasize)
169635863739SMike Smith {
169735863739SMike Smith 	debug_called(3);
169835863739SMike Smith 
169935863739SMike Smith 	if (datasize > AAC_FIB_DATASIZE)
170035863739SMike Smith 		return(EINVAL);
170135863739SMike Smith 
170235863739SMike Smith 	/*
170335863739SMike Smith 	 * Set up the sync FIB
170435863739SMike Smith 	 */
1705914da7d0SScott Long 	fib->Header.XferState = AAC_FIBSTATE_HOSTOWNED |
1706914da7d0SScott Long 				AAC_FIBSTATE_INITIALISED |
1707c6eafcf2SScott Long 				AAC_FIBSTATE_EMPTY;
170835863739SMike Smith 	fib->Header.XferState |= xferstate;
170935863739SMike Smith 	fib->Header.Command = command;
171035863739SMike Smith 	fib->Header.StructType = AAC_FIBTYPE_TFIB;
171135863739SMike Smith 	fib->Header.Size = sizeof(struct aac_fib) + datasize;
171235863739SMike Smith 	fib->Header.SenderSize = sizeof(struct aac_fib);
1713b88ffdc8SScott Long 	fib->Header.SenderFibAddress = 0;	/* Not needed */
1714c6eafcf2SScott Long 	fib->Header.ReceiverFibAddress = sc->aac_common_busaddr +
1715914da7d0SScott Long 					 offsetof(struct aac_common,
1716914da7d0SScott Long 						  ac_sync_fib);
171735863739SMike Smith 
171835863739SMike Smith 	/*
171935863739SMike Smith 	 * Give the FIB to the controller, wait for a response.
172035863739SMike Smith 	 */
1721914da7d0SScott Long 	if (aac_sync_command(sc, AAC_MONKER_SYNCFIB,
1722914da7d0SScott Long 			     fib->Header.ReceiverFibAddress, 0, 0, 0, NULL)) {
172335863739SMike Smith 		debug(2, "IO error");
172435863739SMike Smith 		return(EIO);
172535863739SMike Smith 	}
172635863739SMike Smith 
172735863739SMike Smith 	return (0);
172835863739SMike Smith }
172935863739SMike Smith 
1730914da7d0SScott Long /*
173135863739SMike Smith  * Adapter-space FIB queue manipulation
173235863739SMike Smith  *
173335863739SMike Smith  * Note that the queue implementation here is a little funky; neither the PI or
173435863739SMike Smith  * CI will ever be zero.  This behaviour is a controller feature.
173535863739SMike Smith  */
173635863739SMike Smith static struct {
173735863739SMike Smith 	int		size;
173835863739SMike Smith 	int		notify;
173935863739SMike Smith } aac_qinfo[] = {
174035863739SMike Smith 	{AAC_HOST_NORM_CMD_ENTRIES, AAC_DB_COMMAND_NOT_FULL},
174135863739SMike Smith 	{AAC_HOST_HIGH_CMD_ENTRIES, 0},
174235863739SMike Smith 	{AAC_ADAP_NORM_CMD_ENTRIES, AAC_DB_COMMAND_READY},
174335863739SMike Smith 	{AAC_ADAP_HIGH_CMD_ENTRIES, 0},
174435863739SMike Smith 	{AAC_HOST_NORM_RESP_ENTRIES, AAC_DB_RESPONSE_NOT_FULL},
174535863739SMike Smith 	{AAC_HOST_HIGH_RESP_ENTRIES, 0},
174635863739SMike Smith 	{AAC_ADAP_NORM_RESP_ENTRIES, AAC_DB_RESPONSE_READY},
174735863739SMike Smith 	{AAC_ADAP_HIGH_RESP_ENTRIES, 0}
174835863739SMike Smith };
174935863739SMike Smith 
175035863739SMike Smith /*
1751c6eafcf2SScott Long  * Atomically insert an entry into the nominated queue, returns 0 on success or
1752c6eafcf2SScott Long  * EBUSY if the queue is full.
175335863739SMike Smith  *
17540b94a66eSMike Smith  * Note: it would be more efficient to defer notifying the controller in
1755914da7d0SScott Long  *	 the case where we may be inserting several entries in rapid succession,
1756914da7d0SScott Long  *	 but implementing this usefully may be difficult (it would involve a
1757c6eafcf2SScott Long  *	 separate queue/notify interface).
175835863739SMike Smith  */
175935863739SMike Smith static int
1760f6c4dd3fSScott Long aac_enqueue_fib(struct aac_softc *sc, int queue, struct aac_command *cm)
176135863739SMike Smith {
176235863739SMike Smith 	u_int32_t pi, ci;
17639e2e96d8SScott Long 	int error;
1764f6c4dd3fSScott Long 	u_int32_t fib_size;
1765f6c4dd3fSScott Long 	u_int32_t fib_addr;
1766f6c4dd3fSScott Long 
176736e0bf6eSScott Long 	debug_called(3);
176836e0bf6eSScott Long 
1769f6c4dd3fSScott Long 	fib_size = cm->cm_fib->Header.Size;
1770f6c4dd3fSScott Long 	fib_addr = cm->cm_fib->Header.ReceiverFibAddress;
177135863739SMike Smith 
177235863739SMike Smith 	/* get the producer/consumer indices */
177335863739SMike Smith 	pi = sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX];
177435863739SMike Smith 	ci = sc->aac_queues->qt_qindex[queue][AAC_CONSUMER_INDEX];
177535863739SMike Smith 
177635863739SMike Smith 	/* wrap the queue? */
177735863739SMike Smith 	if (pi >= aac_qinfo[queue].size)
177835863739SMike Smith 		pi = 0;
177935863739SMike Smith 
178035863739SMike Smith 	/* check for queue full */
178135863739SMike Smith 	if ((pi + 1) == ci) {
178235863739SMike Smith 		error = EBUSY;
178335863739SMike Smith 		goto out;
178435863739SMike Smith 	}
178535863739SMike Smith 
1786614c22b2SScott Long 	/*
1787614c22b2SScott Long 	 * To avoid a race with its completion interrupt, place this command on
1788614c22b2SScott Long 	 * the busy queue prior to advertising it to the controller.
1789614c22b2SScott Long 	 */
1790614c22b2SScott Long 	aac_enqueue_busy(cm);
1791614c22b2SScott Long 
179235863739SMike Smith 	/* populate queue entry */
179335863739SMike Smith 	(sc->aac_qentries[queue] + pi)->aq_fib_size = fib_size;
179435863739SMike Smith 	(sc->aac_qentries[queue] + pi)->aq_fib_addr = fib_addr;
179535863739SMike Smith 
179635863739SMike Smith 	/* update producer index */
179735863739SMike Smith 	sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX] = pi + 1;
179835863739SMike Smith 
179935863739SMike Smith 	/* notify the adapter if we know how */
180035863739SMike Smith 	if (aac_qinfo[queue].notify != 0)
180135863739SMike Smith 		AAC_QNOTIFY(sc, aac_qinfo[queue].notify);
180235863739SMike Smith 
180335863739SMike Smith 	error = 0;
180435863739SMike Smith 
180535863739SMike Smith out:
180635863739SMike Smith 	return(error);
180735863739SMike Smith }
180835863739SMike Smith 
180935863739SMike Smith /*
181036e0bf6eSScott Long  * Atomically remove one entry from the nominated queue, returns 0 on
181136e0bf6eSScott Long  * success or ENOENT if the queue is empty.
181235863739SMike Smith  */
181335863739SMike Smith static int
1814c6eafcf2SScott Long aac_dequeue_fib(struct aac_softc *sc, int queue, u_int32_t *fib_size,
1815c6eafcf2SScott Long 		struct aac_fib **fib_addr)
181635863739SMike Smith {
181735863739SMike Smith 	u_int32_t pi, ci;
1818149af931SScott Long 	u_int32_t fib_index;
18199e2e96d8SScott Long 	int error;
1820f6c4dd3fSScott Long 	int notify;
182135863739SMike Smith 
182235863739SMike Smith 	debug_called(3);
182335863739SMike Smith 
182435863739SMike Smith 	/* get the producer/consumer indices */
182535863739SMike Smith 	pi = sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX];
182635863739SMike Smith 	ci = sc->aac_queues->qt_qindex[queue][AAC_CONSUMER_INDEX];
182735863739SMike Smith 
182835863739SMike Smith 	/* check for queue empty */
182935863739SMike Smith 	if (ci == pi) {
183035863739SMike Smith 		error = ENOENT;
183135863739SMike Smith 		goto out;
183235863739SMike Smith 	}
183335863739SMike Smith 
18347753acd2SScott Long 	/* wrap the pi so the following test works */
18357753acd2SScott Long 	if (pi >= aac_qinfo[queue].size)
18367753acd2SScott Long 		pi = 0;
18377753acd2SScott Long 
1838f6c4dd3fSScott Long 	notify = 0;
1839f6c4dd3fSScott Long 	if (ci == pi + 1)
1840f6c4dd3fSScott Long 		notify++;
1841f6c4dd3fSScott Long 
184235863739SMike Smith 	/* wrap the queue? */
184335863739SMike Smith 	if (ci >= aac_qinfo[queue].size)
184435863739SMike Smith 		ci = 0;
184535863739SMike Smith 
184635863739SMike Smith 	/* fetch the entry */
184735863739SMike Smith 	*fib_size = (sc->aac_qentries[queue] + ci)->aq_fib_size;
1848149af931SScott Long 
1849149af931SScott Long 	switch (queue) {
1850149af931SScott Long 	case AAC_HOST_NORM_CMD_QUEUE:
1851149af931SScott Long 	case AAC_HOST_HIGH_CMD_QUEUE:
1852149af931SScott Long 		/*
1853149af931SScott Long 		 * The aq_fib_addr is only 32 bits wide so it can't be counted
1854149af931SScott Long 		 * on to hold an address.  For AIF's, the adapter assumes
1855149af931SScott Long 		 * that it's giving us an address into the array of AIF fibs.
1856149af931SScott Long 		 * Therefore, we have to convert it to an index.
1857149af931SScott Long 		 */
1858149af931SScott Long 		fib_index = (sc->aac_qentries[queue] + ci)->aq_fib_addr /
1859149af931SScott Long 			sizeof(struct aac_fib);
1860149af931SScott Long 		*fib_addr = &sc->aac_common->ac_fibs[fib_index];
1861149af931SScott Long 		break;
1862149af931SScott Long 
1863149af931SScott Long 	case AAC_HOST_NORM_RESP_QUEUE:
1864149af931SScott Long 	case AAC_HOST_HIGH_RESP_QUEUE:
1865149af931SScott Long 	{
1866149af931SScott Long 		struct aac_command *cm;
1867149af931SScott Long 
1868149af931SScott Long 		/*
1869149af931SScott Long 		 * As above, an index is used instead of an actual address.
1870149af931SScott Long 		 * Gotta shift the index to account for the fast response
1871149af931SScott Long 		 * bit.  No other correction is needed since this value was
1872149af931SScott Long 		 * originally provided by the driver via the SenderFibAddress
1873149af931SScott Long 		 * field.
1874149af931SScott Long 		 */
1875149af931SScott Long 		fib_index = (sc->aac_qentries[queue] + ci)->aq_fib_addr;
1876149af931SScott Long 		cm = sc->aac_commands + (fib_index >> 1);
1877149af931SScott Long 		*fib_addr = cm->cm_fib;
187835863739SMike Smith 
1879f30ac74cSScott Long 		/*
1880f30ac74cSScott Long 		 * Is this a fast response? If it is, update the fib fields in
1881149af931SScott Long 		 * local memory since the whole fib isn't DMA'd back up.
1882f30ac74cSScott Long 		 */
1883149af931SScott Long 		if (fib_index & 0x01) {
1884f30ac74cSScott Long 			(*fib_addr)->Header.XferState |= AAC_FIBSTATE_DONEADAP;
1885f30ac74cSScott Long 			*((u_int32_t*)((*fib_addr)->data)) = AAC_ERROR_NORMAL;
1886f30ac74cSScott Long 		}
1887149af931SScott Long 		break;
1888149af931SScott Long 	}
1889149af931SScott Long 	default:
1890149af931SScott Long 		panic("Invalid queue in aac_dequeue_fib()");
1891149af931SScott Long 		break;
1892149af931SScott Long 	}
1893149af931SScott Long 
189435863739SMike Smith 	/* update consumer index */
189535863739SMike Smith 	sc->aac_queues->qt_qindex[queue][AAC_CONSUMER_INDEX] = ci + 1;
189635863739SMike Smith 
189735863739SMike Smith 	/* if we have made the queue un-full, notify the adapter */
1898f6c4dd3fSScott Long 	if (notify && (aac_qinfo[queue].notify != 0))
189935863739SMike Smith 		AAC_QNOTIFY(sc, aac_qinfo[queue].notify);
190035863739SMike Smith 	error = 0;
190135863739SMike Smith 
190235863739SMike Smith out:
190335863739SMike Smith 	return(error);
190435863739SMike Smith }
190535863739SMike Smith 
1906914da7d0SScott Long /*
190736e0bf6eSScott Long  * Put our response to an Adapter Initialed Fib on the response queue
190836e0bf6eSScott Long  */
190936e0bf6eSScott Long static int
191036e0bf6eSScott Long aac_enqueue_response(struct aac_softc *sc, int queue, struct aac_fib *fib)
191136e0bf6eSScott Long {
191236e0bf6eSScott Long 	u_int32_t pi, ci;
19139e2e96d8SScott Long 	int error;
191436e0bf6eSScott Long 	u_int32_t fib_size;
191536e0bf6eSScott Long 	u_int32_t fib_addr;
191636e0bf6eSScott Long 
191736e0bf6eSScott Long 	debug_called(1);
191836e0bf6eSScott Long 
191936e0bf6eSScott Long 	/* Tell the adapter where the FIB is */
192036e0bf6eSScott Long 	fib_size = fib->Header.Size;
192136e0bf6eSScott Long 	fib_addr = fib->Header.SenderFibAddress;
192236e0bf6eSScott Long 	fib->Header.ReceiverFibAddress = fib_addr;
192336e0bf6eSScott Long 
192436e0bf6eSScott Long 	/* get the producer/consumer indices */
192536e0bf6eSScott Long 	pi = sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX];
192636e0bf6eSScott Long 	ci = sc->aac_queues->qt_qindex[queue][AAC_CONSUMER_INDEX];
192736e0bf6eSScott Long 
192836e0bf6eSScott Long 	/* wrap the queue? */
192936e0bf6eSScott Long 	if (pi >= aac_qinfo[queue].size)
193036e0bf6eSScott Long 		pi = 0;
193136e0bf6eSScott Long 
193236e0bf6eSScott Long 	/* check for queue full */
193336e0bf6eSScott Long 	if ((pi + 1) == ci) {
193436e0bf6eSScott Long 		error = EBUSY;
193536e0bf6eSScott Long 		goto out;
193636e0bf6eSScott Long 	}
193736e0bf6eSScott Long 
193836e0bf6eSScott Long 	/* populate queue entry */
193936e0bf6eSScott Long 	(sc->aac_qentries[queue] + pi)->aq_fib_size = fib_size;
194036e0bf6eSScott Long 	(sc->aac_qentries[queue] + pi)->aq_fib_addr = fib_addr;
194136e0bf6eSScott Long 
194236e0bf6eSScott Long 	/* update producer index */
194336e0bf6eSScott Long 	sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX] = pi + 1;
194436e0bf6eSScott Long 
194536e0bf6eSScott Long 	/* notify the adapter if we know how */
194636e0bf6eSScott Long 	if (aac_qinfo[queue].notify != 0)
194736e0bf6eSScott Long 		AAC_QNOTIFY(sc, aac_qinfo[queue].notify);
194836e0bf6eSScott Long 
194936e0bf6eSScott Long 	error = 0;
195036e0bf6eSScott Long 
195136e0bf6eSScott Long out:
195236e0bf6eSScott Long 	return(error);
195336e0bf6eSScott Long }
195436e0bf6eSScott Long 
1955914da7d0SScott Long /*
19560b94a66eSMike Smith  * Check for commands that have been outstanding for a suspiciously long time,
19570b94a66eSMike Smith  * and complain about them.
19580b94a66eSMike Smith  */
19590b94a66eSMike Smith static void
19600b94a66eSMike Smith aac_timeout(struct aac_softc *sc)
19610b94a66eSMike Smith {
19620b94a66eSMike Smith 	struct aac_command *cm;
19630b94a66eSMike Smith 	time_t deadline;
19640b94a66eSMike Smith 
1965f6c4dd3fSScott Long 	/*
196670545d1aSScott Long 	 * Traverse the busy command list, bitch about late commands once
1967914da7d0SScott Long 	 * only.
1968914da7d0SScott Long 	 */
19690b94a66eSMike Smith 	deadline = time_second - AAC_CMD_TIMEOUT;
19700b94a66eSMike Smith 	TAILQ_FOREACH(cm, &sc->aac_busy, cm_link) {
1971f6c4dd3fSScott Long 		if ((cm->cm_timestamp  < deadline)
1972f6c4dd3fSScott Long 			/* && !(cm->cm_flags & AAC_CMD_TIMEDOUT) */) {
19730b94a66eSMike Smith 			cm->cm_flags |= AAC_CMD_TIMEDOUT;
1974914da7d0SScott Long 			device_printf(sc->aac_dev,
1975914da7d0SScott Long 				      "COMMAND %p TIMEOUT AFTER %d SECONDS\n",
1976f6c4dd3fSScott Long 				      cm, (int)(time_second-cm->cm_timestamp));
19770b94a66eSMike Smith 			AAC_PRINT_FIB(sc, cm->cm_fib);
19780b94a66eSMike Smith 		}
19790b94a66eSMike Smith 	}
19800b94a66eSMike Smith 
19810b94a66eSMike Smith 	return;
19820b94a66eSMike Smith }
19830b94a66eSMike Smith 
1984914da7d0SScott Long /*
1985914da7d0SScott Long  * Interface Function Vectors
1986914da7d0SScott Long  */
198735863739SMike Smith 
1988914da7d0SScott Long /*
198935863739SMike Smith  * Read the current firmware status word.
199035863739SMike Smith  */
199135863739SMike Smith static int
199235863739SMike Smith aac_sa_get_fwstatus(struct aac_softc *sc)
199335863739SMike Smith {
199435863739SMike Smith 	debug_called(3);
199535863739SMike Smith 
199635863739SMike Smith 	return(AAC_GETREG4(sc, AAC_SA_FWSTATUS));
199735863739SMike Smith }
199835863739SMike Smith 
199935863739SMike Smith static int
200035863739SMike Smith aac_rx_get_fwstatus(struct aac_softc *sc)
200135863739SMike Smith {
200235863739SMike Smith 	debug_called(3);
200335863739SMike Smith 
200435863739SMike Smith 	return(AAC_GETREG4(sc, AAC_RX_FWSTATUS));
200535863739SMike Smith }
200635863739SMike Smith 
2007b3457b51SScott Long static int
2008b3457b51SScott Long aac_fa_get_fwstatus(struct aac_softc *sc)
2009b3457b51SScott Long {
2010b3457b51SScott Long 	int val;
2011b3457b51SScott Long 
2012b3457b51SScott Long 	debug_called(3);
2013b3457b51SScott Long 
2014b3457b51SScott Long 	val = AAC_GETREG4(sc, AAC_FA_FWSTATUS);
2015b3457b51SScott Long 	return (val);
2016b3457b51SScott Long }
2017b3457b51SScott Long 
2018914da7d0SScott Long /*
201935863739SMike Smith  * Notify the controller of a change in a given queue
202035863739SMike Smith  */
202135863739SMike Smith 
202235863739SMike Smith static void
202335863739SMike Smith aac_sa_qnotify(struct aac_softc *sc, int qbit)
202435863739SMike Smith {
202535863739SMike Smith 	debug_called(3);
202635863739SMike Smith 
202735863739SMike Smith 	AAC_SETREG2(sc, AAC_SA_DOORBELL1_SET, qbit);
202835863739SMike Smith }
202935863739SMike Smith 
203035863739SMike Smith static void
203135863739SMike Smith aac_rx_qnotify(struct aac_softc *sc, int qbit)
203235863739SMike Smith {
203335863739SMike Smith 	debug_called(3);
203435863739SMike Smith 
203535863739SMike Smith 	AAC_SETREG4(sc, AAC_RX_IDBR, qbit);
203635863739SMike Smith }
203735863739SMike Smith 
2038b3457b51SScott Long static void
2039b3457b51SScott Long aac_fa_qnotify(struct aac_softc *sc, int qbit)
2040b3457b51SScott Long {
2041b3457b51SScott Long 	debug_called(3);
2042b3457b51SScott Long 
2043b3457b51SScott Long 	AAC_SETREG2(sc, AAC_FA_DOORBELL1, qbit);
2044b3457b51SScott Long 	AAC_FA_HACK(sc);
2045b3457b51SScott Long }
2046b3457b51SScott Long 
2047914da7d0SScott Long /*
204835863739SMike Smith  * Get the interrupt reason bits
204935863739SMike Smith  */
205035863739SMike Smith static int
205135863739SMike Smith aac_sa_get_istatus(struct aac_softc *sc)
205235863739SMike Smith {
205335863739SMike Smith 	debug_called(3);
205435863739SMike Smith 
205535863739SMike Smith 	return(AAC_GETREG2(sc, AAC_SA_DOORBELL0));
205635863739SMike Smith }
205735863739SMike Smith 
205835863739SMike Smith static int
205935863739SMike Smith aac_rx_get_istatus(struct aac_softc *sc)
206035863739SMike Smith {
206135863739SMike Smith 	debug_called(3);
206235863739SMike Smith 
206335863739SMike Smith 	return(AAC_GETREG4(sc, AAC_RX_ODBR));
206435863739SMike Smith }
206535863739SMike Smith 
2066b3457b51SScott Long static int
2067b3457b51SScott Long aac_fa_get_istatus(struct aac_softc *sc)
2068b3457b51SScott Long {
2069b3457b51SScott Long 	int val;
2070b3457b51SScott Long 
2071b3457b51SScott Long 	debug_called(3);
2072b3457b51SScott Long 
2073b3457b51SScott Long 	val = AAC_GETREG2(sc, AAC_FA_DOORBELL0);
2074b3457b51SScott Long 	return (val);
2075b3457b51SScott Long }
2076b3457b51SScott Long 
2077914da7d0SScott Long /*
207835863739SMike Smith  * Clear some interrupt reason bits
207935863739SMike Smith  */
208035863739SMike Smith static void
208135863739SMike Smith aac_sa_clear_istatus(struct aac_softc *sc, int mask)
208235863739SMike Smith {
208335863739SMike Smith 	debug_called(3);
208435863739SMike Smith 
208535863739SMike Smith 	AAC_SETREG2(sc, AAC_SA_DOORBELL0_CLEAR, mask);
208635863739SMike Smith }
208735863739SMike Smith 
208835863739SMike Smith static void
208935863739SMike Smith aac_rx_clear_istatus(struct aac_softc *sc, int mask)
209035863739SMike Smith {
209135863739SMike Smith 	debug_called(3);
209235863739SMike Smith 
209335863739SMike Smith 	AAC_SETREG4(sc, AAC_RX_ODBR, mask);
209435863739SMike Smith }
209535863739SMike Smith 
2096b3457b51SScott Long static void
2097b3457b51SScott Long aac_fa_clear_istatus(struct aac_softc *sc, int mask)
2098b3457b51SScott Long {
2099b3457b51SScott Long 	debug_called(3);
2100b3457b51SScott Long 
2101b3457b51SScott Long 	AAC_SETREG2(sc, AAC_FA_DOORBELL0_CLEAR, mask);
2102b3457b51SScott Long 	AAC_FA_HACK(sc);
2103b3457b51SScott Long }
2104b3457b51SScott Long 
2105914da7d0SScott Long /*
210635863739SMike Smith  * Populate the mailbox and set the command word
210735863739SMike Smith  */
210835863739SMike Smith static void
210935863739SMike Smith aac_sa_set_mailbox(struct aac_softc *sc, u_int32_t command,
211035863739SMike Smith 		u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3)
211135863739SMike Smith {
211235863739SMike Smith 	debug_called(4);
211335863739SMike Smith 
211435863739SMike Smith 	AAC_SETREG4(sc, AAC_SA_MAILBOX, command);
211535863739SMike Smith 	AAC_SETREG4(sc, AAC_SA_MAILBOX + 4, arg0);
211635863739SMike Smith 	AAC_SETREG4(sc, AAC_SA_MAILBOX + 8, arg1);
211735863739SMike Smith 	AAC_SETREG4(sc, AAC_SA_MAILBOX + 12, arg2);
211835863739SMike Smith 	AAC_SETREG4(sc, AAC_SA_MAILBOX + 16, arg3);
211935863739SMike Smith }
212035863739SMike Smith 
212135863739SMike Smith static void
212235863739SMike Smith aac_rx_set_mailbox(struct aac_softc *sc, u_int32_t command,
212335863739SMike Smith 		u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3)
212435863739SMike Smith {
212535863739SMike Smith 	debug_called(4);
212635863739SMike Smith 
212735863739SMike Smith 	AAC_SETREG4(sc, AAC_RX_MAILBOX, command);
212835863739SMike Smith 	AAC_SETREG4(sc, AAC_RX_MAILBOX + 4, arg0);
212935863739SMike Smith 	AAC_SETREG4(sc, AAC_RX_MAILBOX + 8, arg1);
213035863739SMike Smith 	AAC_SETREG4(sc, AAC_RX_MAILBOX + 12, arg2);
213135863739SMike Smith 	AAC_SETREG4(sc, AAC_RX_MAILBOX + 16, arg3);
213235863739SMike Smith }
213335863739SMike Smith 
2134b3457b51SScott Long static void
2135b3457b51SScott Long aac_fa_set_mailbox(struct aac_softc *sc, u_int32_t command,
2136b3457b51SScott Long 		u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3)
2137b3457b51SScott Long {
2138b3457b51SScott Long 	debug_called(4);
2139b3457b51SScott Long 
2140b3457b51SScott Long 	AAC_SETREG4(sc, AAC_FA_MAILBOX, command);
2141b3457b51SScott Long 	AAC_FA_HACK(sc);
2142b3457b51SScott Long 	AAC_SETREG4(sc, AAC_FA_MAILBOX + 4, arg0);
2143b3457b51SScott Long 	AAC_FA_HACK(sc);
2144b3457b51SScott Long 	AAC_SETREG4(sc, AAC_FA_MAILBOX + 8, arg1);
2145b3457b51SScott Long 	AAC_FA_HACK(sc);
2146b3457b51SScott Long 	AAC_SETREG4(sc, AAC_FA_MAILBOX + 12, arg2);
2147b3457b51SScott Long 	AAC_FA_HACK(sc);
2148b3457b51SScott Long 	AAC_SETREG4(sc, AAC_FA_MAILBOX + 16, arg3);
2149b3457b51SScott Long 	AAC_FA_HACK(sc);
2150b3457b51SScott Long }
2151b3457b51SScott Long 
2152914da7d0SScott Long /*
215335863739SMike Smith  * Fetch the immediate command status word
215435863739SMike Smith  */
215535863739SMike Smith static int
2156a6d35632SScott Long aac_sa_get_mailbox(struct aac_softc *sc, int mb)
215735863739SMike Smith {
215835863739SMike Smith 	debug_called(4);
215935863739SMike Smith 
2160a6d35632SScott Long 	return(AAC_GETREG4(sc, AAC_SA_MAILBOX + (mb * 4)));
216135863739SMike Smith }
216235863739SMike Smith 
216335863739SMike Smith static int
2164a6d35632SScott Long aac_rx_get_mailbox(struct aac_softc *sc, int mb)
216535863739SMike Smith {
216635863739SMike Smith 	debug_called(4);
216735863739SMike Smith 
2168a6d35632SScott Long 	return(AAC_GETREG4(sc, AAC_RX_MAILBOX + (mb * 4)));
216935863739SMike Smith }
217035863739SMike Smith 
2171b3457b51SScott Long static int
2172a6d35632SScott Long aac_fa_get_mailbox(struct aac_softc *sc, int mb)
2173b3457b51SScott Long {
2174b3457b51SScott Long 	int val;
2175b3457b51SScott Long 
2176b3457b51SScott Long 	debug_called(4);
2177b3457b51SScott Long 
2178a6d35632SScott Long 	val = AAC_GETREG4(sc, AAC_FA_MAILBOX + (mb * 4));
2179b3457b51SScott Long 	return (val);
2180b3457b51SScott Long }
2181b3457b51SScott Long 
2182914da7d0SScott Long /*
218335863739SMike Smith  * Set/clear interrupt masks
218435863739SMike Smith  */
218535863739SMike Smith static void
218635863739SMike Smith aac_sa_set_interrupts(struct aac_softc *sc, int enable)
218735863739SMike Smith {
218835863739SMike Smith 	debug(2, "%sable interrupts", enable ? "en" : "dis");
218935863739SMike Smith 
219035863739SMike Smith 	if (enable) {
219135863739SMike Smith 		AAC_SETREG2((sc), AAC_SA_MASK0_CLEAR, AAC_DB_INTERRUPTS);
219235863739SMike Smith 	} else {
219335863739SMike Smith 		AAC_SETREG2((sc), AAC_SA_MASK0_SET, ~0);
219435863739SMike Smith 	}
219535863739SMike Smith }
219635863739SMike Smith 
219735863739SMike Smith static void
219835863739SMike Smith aac_rx_set_interrupts(struct aac_softc *sc, int enable)
219935863739SMike Smith {
220035863739SMike Smith 	debug(2, "%sable interrupts", enable ? "en" : "dis");
220135863739SMike Smith 
220235863739SMike Smith 	if (enable) {
220335863739SMike Smith 		AAC_SETREG4(sc, AAC_RX_OIMR, ~AAC_DB_INTERRUPTS);
220435863739SMike Smith 	} else {
220535863739SMike Smith 		AAC_SETREG4(sc, AAC_RX_OIMR, ~0);
220635863739SMike Smith 	}
220735863739SMike Smith }
220835863739SMike Smith 
2209b3457b51SScott Long static void
2210b3457b51SScott Long aac_fa_set_interrupts(struct aac_softc *sc, int enable)
2211b3457b51SScott Long {
2212b3457b51SScott Long 	debug(2, "%sable interrupts", enable ? "en" : "dis");
2213b3457b51SScott Long 
2214b3457b51SScott Long 	if (enable) {
2215b3457b51SScott Long 		AAC_SETREG2((sc), AAC_FA_MASK0_CLEAR, AAC_DB_INTERRUPTS);
2216b3457b51SScott Long 		AAC_FA_HACK(sc);
2217b3457b51SScott Long 	} else {
2218b3457b51SScott Long 		AAC_SETREG2((sc), AAC_FA_MASK0, ~0);
2219b3457b51SScott Long 		AAC_FA_HACK(sc);
2220b3457b51SScott Long 	}
2221b3457b51SScott Long }
2222b3457b51SScott Long 
2223914da7d0SScott Long /*
2224914da7d0SScott Long  * Debugging and Diagnostics
2225914da7d0SScott Long  */
222635863739SMike Smith 
2227914da7d0SScott Long /*
222835863739SMike Smith  * Print some information about the controller.
222935863739SMike Smith  */
223035863739SMike Smith static void
223135863739SMike Smith aac_describe_controller(struct aac_softc *sc)
223235863739SMike Smith {
2233cbfd045bSScott Long 	struct aac_fib *fib;
223435863739SMike Smith 	struct aac_adapter_info	*info;
223535863739SMike Smith 
223635863739SMike Smith 	debug_called(2);
223735863739SMike Smith 
223803b5fe51SScott Long 	aac_alloc_sync_fib(sc, &fib);
2239cbfd045bSScott Long 
2240cbfd045bSScott Long 	fib->data[0] = 0;
2241cbfd045bSScott Long 	if (aac_sync_fib(sc, RequestAdapterInfo, 0, fib, 1)) {
224235863739SMike Smith 		device_printf(sc->aac_dev, "RequestAdapterInfo failed\n");
2243fe3cb0e1SScott Long 		aac_release_sync_fib(sc);
224435863739SMike Smith 		return;
224535863739SMike Smith 	}
2246cbfd045bSScott Long 	info = (struct aac_adapter_info *)&fib->data[0];
224735863739SMike Smith 
224836e0bf6eSScott Long 	device_printf(sc->aac_dev, "%s %dMHz, %dMB cache memory, %s\n",
2249c6eafcf2SScott Long 		      aac_describe_code(aac_cpu_variant, info->CpuVariant),
225036e0bf6eSScott Long 		      info->ClockSpeed, info->BufferMem / (1024 * 1024),
2251914da7d0SScott Long 		      aac_describe_code(aac_battery_platform,
2252914da7d0SScott Long 					info->batteryPlatform));
225335863739SMike Smith 
225435863739SMike Smith 	/* save the kernel revision structure for later use */
225535863739SMike Smith 	sc->aac_revision = info->KernelRevision;
225636e0bf6eSScott Long 	device_printf(sc->aac_dev, "Kernel %d.%d-%d, Build %d, S/N %6X\n",
225735863739SMike Smith 		      info->KernelRevision.external.comp.major,
225835863739SMike Smith 		      info->KernelRevision.external.comp.minor,
225935863739SMike Smith 		      info->KernelRevision.external.comp.dash,
226036e0bf6eSScott Long 		      info->KernelRevision.buildNumber,
226136e0bf6eSScott Long 		      (u_int32_t)(info->SerialNumber & 0xffffff));
2262fe3cb0e1SScott Long 
2263fe3cb0e1SScott Long 	aac_release_sync_fib(sc);
2264a6d35632SScott Long 
2265a6d35632SScott Long 	if (1 || bootverbose) {
2266a6d35632SScott Long 		device_printf(sc->aac_dev, "Supported Options=%b\n",
2267a6d35632SScott Long 			      sc->supported_options,
2268a6d35632SScott Long 			      "\20"
2269a6d35632SScott Long 			      "\1SNAPSHOT"
2270a6d35632SScott Long 			      "\2CLUSTERS"
2271a6d35632SScott Long 			      "\3WCACHE"
2272a6d35632SScott Long 			      "\4DATA64"
2273a6d35632SScott Long 			      "\5HOSTTIME"
2274a6d35632SScott Long 			      "\6RAID50"
2275a6d35632SScott Long 			      "\7WINDOW4GB"
2276a6d35632SScott Long 			      "\10SCSIUPGD"
2277a6d35632SScott Long 			      "\11SOFTERR"
2278a6d35632SScott Long 			      "\12NORECOND"
2279a6d35632SScott Long 			      "\13SGMAP64"
2280a6d35632SScott Long 			      "\14ALARM"
2281a6d35632SScott Long 			      "\15NONDASD");
2282a6d35632SScott Long 	}
228335863739SMike Smith }
228435863739SMike Smith 
2285914da7d0SScott Long /*
228635863739SMike Smith  * Look up a text description of a numeric error code and return a pointer to
228735863739SMike Smith  * same.
228835863739SMike Smith  */
228935863739SMike Smith static char *
229035863739SMike Smith aac_describe_code(struct aac_code_lookup *table, u_int32_t code)
229135863739SMike Smith {
229235863739SMike Smith 	int i;
229335863739SMike Smith 
229435863739SMike Smith 	for (i = 0; table[i].string != NULL; i++)
229535863739SMike Smith 		if (table[i].code == code)
229635863739SMike Smith 			return(table[i].string);
229735863739SMike Smith 	return(table[i + 1].string);
229835863739SMike Smith }
229935863739SMike Smith 
2300914da7d0SScott Long /*
2301914da7d0SScott Long  * Management Interface
2302914da7d0SScott Long  */
230335863739SMike Smith 
230435863739SMike Smith static int
230589c9c53dSPoul-Henning Kamp aac_open(struct cdev *dev, int flags, int fmt, d_thread_t *td)
230635863739SMike Smith {
2307914da7d0SScott Long 	struct aac_softc *sc;
230835863739SMike Smith 
230935863739SMike Smith 	debug_called(2);
231035863739SMike Smith 
2311914da7d0SScott Long 	sc = dev->si_drv1;
2312914da7d0SScott Long 
231335863739SMike Smith 	/* Check to make sure the device isn't already open */
231435863739SMike Smith 	if (sc->aac_state & AAC_STATE_OPEN) {
231535863739SMike Smith 		return EBUSY;
231635863739SMike Smith 	}
231735863739SMike Smith 	sc->aac_state |= AAC_STATE_OPEN;
231835863739SMike Smith 
231935863739SMike Smith 	return 0;
232035863739SMike Smith }
232135863739SMike Smith 
232235863739SMike Smith static int
232389c9c53dSPoul-Henning Kamp aac_close(struct cdev *dev, int flags, int fmt, d_thread_t *td)
232435863739SMike Smith {
2325914da7d0SScott Long 	struct aac_softc *sc;
232635863739SMike Smith 
232735863739SMike Smith 	debug_called(2);
232835863739SMike Smith 
2329914da7d0SScott Long 	sc = dev->si_drv1;
2330914da7d0SScott Long 
233135863739SMike Smith 	/* Mark this unit as no longer open  */
233235863739SMike Smith 	sc->aac_state &= ~AAC_STATE_OPEN;
233335863739SMike Smith 
233435863739SMike Smith 	return 0;
233535863739SMike Smith }
233635863739SMike Smith 
233735863739SMike Smith static int
233889c9c53dSPoul-Henning Kamp aac_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, d_thread_t *td)
233935863739SMike Smith {
2340914da7d0SScott Long 	union aac_statrequest *as;
2341914da7d0SScott Long 	struct aac_softc *sc;
23420b94a66eSMike Smith 	int error = 0;
2343b88ffdc8SScott Long 	uint32_t cookie;
234435863739SMike Smith 
234535863739SMike Smith 	debug_called(2);
234635863739SMike Smith 
2347914da7d0SScott Long 	as = (union aac_statrequest *)arg;
2348914da7d0SScott Long 	sc = dev->si_drv1;
2349914da7d0SScott Long 
235035863739SMike Smith 	switch (cmd) {
23510b94a66eSMike Smith 	case AACIO_STATS:
23520b94a66eSMike Smith 		switch (as->as_item) {
23530b94a66eSMike Smith 		case AACQ_FREE:
23540b94a66eSMike Smith 		case AACQ_BIO:
23550b94a66eSMike Smith 		case AACQ_READY:
23560b94a66eSMike Smith 		case AACQ_BUSY:
2357c6eafcf2SScott Long 			bcopy(&sc->aac_qstat[as->as_item], &as->as_qstat,
2358c6eafcf2SScott Long 			      sizeof(struct aac_qstat));
23590b94a66eSMike Smith 			break;
23600b94a66eSMike Smith 		default:
23610b94a66eSMike Smith 			error = ENOENT;
23620b94a66eSMike Smith 			break;
23630b94a66eSMike Smith 		}
23640b94a66eSMike Smith 	break;
23650b94a66eSMike Smith 
236635863739SMike Smith 	case FSACTL_SENDFIB:
2367fb0c27d7SScott Long 		arg = *(caddr_t*)arg;
2368fb0c27d7SScott Long 	case FSACTL_LNX_SENDFIB:
23690b94a66eSMike Smith 		debug(1, "FSACTL_SENDFIB");
237035863739SMike Smith 		error = aac_ioctl_sendfib(sc, arg);
237135863739SMike Smith 		break;
237235863739SMike Smith 	case FSACTL_AIF_THREAD:
2373fb0c27d7SScott Long 	case FSACTL_LNX_AIF_THREAD:
23740b94a66eSMike Smith 		debug(1, "FSACTL_AIF_THREAD");
237535863739SMike Smith 		error = EINVAL;
237635863739SMike Smith 		break;
237735863739SMike Smith 	case FSACTL_OPEN_GET_ADAPTER_FIB:
2378fb0c27d7SScott Long 		arg = *(caddr_t*)arg;
2379fb0c27d7SScott Long 	case FSACTL_LNX_OPEN_GET_ADAPTER_FIB:
23800b94a66eSMike Smith 		debug(1, "FSACTL_OPEN_GET_ADAPTER_FIB");
238135863739SMike Smith 		/*
238235863739SMike Smith 		 * Pass the caller out an AdapterFibContext.
238335863739SMike Smith 		 *
238435863739SMike Smith 		 * Note that because we only support one opener, we
238535863739SMike Smith 		 * basically ignore this.  Set the caller's context to a magic
238635863739SMike Smith 		 * number just in case.
23870b94a66eSMike Smith 		 *
23880b94a66eSMike Smith 		 * The Linux code hands the driver a pointer into kernel space,
23890b94a66eSMike Smith 		 * and then trusts it when the caller hands it back.  Aiee!
2390914da7d0SScott Long 		 * Here, we give it the proc pointer of the per-adapter aif
2391914da7d0SScott Long 		 * thread. It's only used as a sanity check in other calls.
239235863739SMike Smith 		 */
2393b88ffdc8SScott Long 		cookie = (uint32_t)(uintptr_t)sc->aifthread;
2394b88ffdc8SScott Long 		error = copyout(&cookie, arg, sizeof(cookie));
239535863739SMike Smith 		break;
239635863739SMike Smith 	case FSACTL_GET_NEXT_ADAPTER_FIB:
2397fb0c27d7SScott Long 		arg = *(caddr_t*)arg;
2398fb0c27d7SScott Long 	case FSACTL_LNX_GET_NEXT_ADAPTER_FIB:
23990b94a66eSMike Smith 		debug(1, "FSACTL_GET_NEXT_ADAPTER_FIB");
2400fb0c27d7SScott Long 		error = aac_getnext_aif(sc, arg);
240135863739SMike Smith 		break;
240235863739SMike Smith 	case FSACTL_CLOSE_GET_ADAPTER_FIB:
2403fb0c27d7SScott Long 	case FSACTL_LNX_CLOSE_GET_ADAPTER_FIB:
24040b94a66eSMike Smith 		debug(1, "FSACTL_CLOSE_GET_ADAPTER_FIB");
240535863739SMike Smith 		/* don't do anything here */
240635863739SMike Smith 		break;
240735863739SMike Smith 	case FSACTL_MINIPORT_REV_CHECK:
2408fb0c27d7SScott Long 		arg = *(caddr_t*)arg;
2409fb0c27d7SScott Long 	case FSACTL_LNX_MINIPORT_REV_CHECK:
24100b94a66eSMike Smith 		debug(1, "FSACTL_MINIPORT_REV_CHECK");
2411fb0c27d7SScott Long 		error = aac_rev_check(sc, arg);
241235863739SMike Smith 		break;
241336e0bf6eSScott Long 	case FSACTL_QUERY_DISK:
241436e0bf6eSScott Long 		arg = *(caddr_t*)arg;
241536e0bf6eSScott Long 	case FSACTL_LNX_QUERY_DISK:
241636e0bf6eSScott Long 		debug(1, "FSACTL_QUERY_DISK");
241736e0bf6eSScott Long 		error = aac_query_disk(sc, arg);
241836e0bf6eSScott Long 			break;
241936e0bf6eSScott Long 	case FSACTL_DELETE_DISK:
242036e0bf6eSScott Long 	case FSACTL_LNX_DELETE_DISK:
2421914da7d0SScott Long 		/*
2422914da7d0SScott Long 		 * We don't trust the underland to tell us when to delete a
2423914da7d0SScott Long 		 * container, rather we rely on an AIF coming from the
2424914da7d0SScott Long 		 * controller
2425914da7d0SScott Long 		 */
242636e0bf6eSScott Long 		error = 0;
242736e0bf6eSScott Long 		break;
242835863739SMike Smith 	default:
2429b3457b51SScott Long 		debug(1, "unsupported cmd 0x%lx\n", cmd);
243035863739SMike Smith 		error = EINVAL;
243135863739SMike Smith 		break;
243235863739SMike Smith 	}
243335863739SMike Smith 	return(error);
243435863739SMike Smith }
243535863739SMike Smith 
2436b3457b51SScott Long static int
243789c9c53dSPoul-Henning Kamp aac_poll(struct cdev *dev, int poll_events, d_thread_t *td)
2438b3457b51SScott Long {
2439b3457b51SScott Long 	struct aac_softc *sc;
2440b3457b51SScott Long 	int revents;
2441b3457b51SScott Long 
2442b3457b51SScott Long 	sc = dev->si_drv1;
2443b3457b51SScott Long 	revents = 0;
2444b3457b51SScott Long 
2445c3d15322SScott Long 	AAC_LOCK_ACQUIRE(&sc->aac_aifq_lock);
2446b3457b51SScott Long 	if ((poll_events & (POLLRDNORM | POLLIN)) != 0) {
2447b3457b51SScott Long 		if (sc->aac_aifq_tail != sc->aac_aifq_head)
2448b3457b51SScott Long 			revents |= poll_events & (POLLIN | POLLRDNORM);
2449b3457b51SScott Long 	}
2450b3457b51SScott Long 	AAC_LOCK_RELEASE(&sc->aac_aifq_lock);
2451b3457b51SScott Long 
2452b3457b51SScott Long 	if (revents == 0) {
2453b3457b51SScott Long 		if (poll_events & (POLLIN | POLLRDNORM))
2454b3457b51SScott Long 			selrecord(td, &sc->rcv_select);
2455b3457b51SScott Long 	}
2456b3457b51SScott Long 
2457b3457b51SScott Long 	return (revents);
2458b3457b51SScott Long }
2459b3457b51SScott Long 
2460914da7d0SScott Long /*
246135863739SMike Smith  * Send a FIB supplied from userspace
246235863739SMike Smith  */
246335863739SMike Smith static int
246435863739SMike Smith aac_ioctl_sendfib(struct aac_softc *sc, caddr_t ufib)
246535863739SMike Smith {
246635863739SMike Smith 	struct aac_command *cm;
246735863739SMike Smith 	int size, error;
246835863739SMike Smith 
246935863739SMike Smith 	debug_called(2);
247035863739SMike Smith 
247135863739SMike Smith 	cm = NULL;
247235863739SMike Smith 
247335863739SMike Smith 	/*
247435863739SMike Smith 	 * Get a command
247535863739SMike Smith 	 */
2476ae543596SScott Long 	AAC_LOCK_ACQUIRE(&sc->aac_io_lock);
247735863739SMike Smith 	if (aac_alloc_command(sc, &cm)) {
247835863739SMike Smith 		error = EBUSY;
247935863739SMike Smith 		goto out;
248035863739SMike Smith 	}
248135863739SMike Smith 
248235863739SMike Smith 	/*
248335863739SMike Smith 	 * Fetch the FIB header, then re-copy to get data as well.
248435863739SMike Smith 	 */
2485914da7d0SScott Long 	if ((error = copyin(ufib, cm->cm_fib,
2486914da7d0SScott Long 			    sizeof(struct aac_fib_header))) != 0)
248735863739SMike Smith 		goto out;
248835863739SMike Smith 	size = cm->cm_fib->Header.Size + sizeof(struct aac_fib_header);
248935863739SMike Smith 	if (size > sizeof(struct aac_fib)) {
2490b88ffdc8SScott Long 		device_printf(sc->aac_dev, "incoming FIB oversized (%d > %zd)\n",
2491914da7d0SScott Long 			      size, sizeof(struct aac_fib));
249235863739SMike Smith 		size = sizeof(struct aac_fib);
249335863739SMike Smith 	}
249435863739SMike Smith 	if ((error = copyin(ufib, cm->cm_fib, size)) != 0)
249535863739SMike Smith 		goto out;
249635863739SMike Smith 	cm->cm_fib->Header.Size = size;
2497f6c4dd3fSScott Long 	cm->cm_timestamp = time_second;
249835863739SMike Smith 
249935863739SMike Smith 	/*
250035863739SMike Smith 	 * Pass the FIB to the controller, wait for it to complete.
250135863739SMike Smith 	 */
2502d8a0a473SScott Long 	if ((error = aac_wait_command(cm)) != 0) {
250370545d1aSScott Long 		device_printf(sc->aac_dev,
250470545d1aSScott Long 			      "aac_wait_command return %d\n", error);
250535863739SMike Smith 		goto out;
2506b3457b51SScott Long 	}
250735863739SMike Smith 
250835863739SMike Smith 	/*
250935863739SMike Smith 	 * Copy the FIB and data back out to the caller.
251035863739SMike Smith 	 */
251135863739SMike Smith 	size = cm->cm_fib->Header.Size;
251235863739SMike Smith 	if (size > sizeof(struct aac_fib)) {
2513b88ffdc8SScott Long 		device_printf(sc->aac_dev, "outbound FIB oversized (%d > %zd)\n",
2514914da7d0SScott Long 			      size, sizeof(struct aac_fib));
251535863739SMike Smith 		size = sizeof(struct aac_fib);
251635863739SMike Smith 	}
251735863739SMike Smith 	error = copyout(cm->cm_fib, ufib, size);
251835863739SMike Smith 
251935863739SMike Smith out:
2520f6c4dd3fSScott Long 	if (cm != NULL) {
252135863739SMike Smith 		aac_release_command(cm);
2522f6c4dd3fSScott Long 	}
2523ae543596SScott Long 
2524ae543596SScott Long 	AAC_LOCK_RELEASE(&sc->aac_io_lock);
252535863739SMike Smith 	return(error);
252635863739SMike Smith }
252735863739SMike Smith 
2528914da7d0SScott Long /*
252935863739SMike Smith  * Handle an AIF sent to us by the controller; queue it for later reference.
253036e0bf6eSScott Long  * If the queue fills up, then drop the older entries.
253135863739SMike Smith  */
253235863739SMike Smith static void
253336e0bf6eSScott Long aac_handle_aif(struct aac_softc *sc, struct aac_fib *fib)
253435863739SMike Smith {
253536e0bf6eSScott Long 	struct aac_aif_command *aif;
253636e0bf6eSScott Long 	struct aac_container *co, *co_next;
2537cbfd045bSScott Long 	struct aac_mntinfo *mi;
2538cbfd045bSScott Long 	struct aac_mntinforesp *mir = NULL;
253936e0bf6eSScott Long 	u_int16_t rsize;
2540b3457b51SScott Long 	int next, found;
2541795d7dc0SScott Long 	int count = 0, added = 0, i = 0;
254235863739SMike Smith 
254335863739SMike Smith 	debug_called(2);
254435863739SMike Smith 
254536e0bf6eSScott Long 	aif = (struct aac_aif_command*)&fib->data[0];
254636e0bf6eSScott Long 	aac_print_aif(sc, aif);
254736e0bf6eSScott Long 
254836e0bf6eSScott Long 	/* Is it an event that we should care about? */
254936e0bf6eSScott Long 	switch (aif->command) {
255036e0bf6eSScott Long 	case AifCmdEventNotify:
255136e0bf6eSScott Long 		switch (aif->data.EN.type) {
255236e0bf6eSScott Long 		case AifEnAddContainer:
255336e0bf6eSScott Long 		case AifEnDeleteContainer:
255436e0bf6eSScott Long 			/*
2555914da7d0SScott Long 			 * A container was added or deleted, but the message
2556914da7d0SScott Long 			 * doesn't tell us anything else!  Re-enumerate the
2557914da7d0SScott Long 			 * containers and sort things out.
255836e0bf6eSScott Long 			 */
255903b5fe51SScott Long 			aac_alloc_sync_fib(sc, &fib);
2560cbfd045bSScott Long 			mi = (struct aac_mntinfo *)&fib->data[0];
256136e0bf6eSScott Long 			do {
256236e0bf6eSScott Long 				/*
2563914da7d0SScott Long 				 * Ask the controller for its containers one at
2564914da7d0SScott Long 				 * a time.
2565914da7d0SScott Long 				 * XXX What if the controller's list changes
2566914da7d0SScott Long 				 * midway through this enumaration?
256736e0bf6eSScott Long 				 * XXX This should be done async.
256836e0bf6eSScott Long 				 */
256939ee03c3SScott Long 				bzero(mi, sizeof(struct aac_mntinfo));
257039ee03c3SScott Long 				mi->Command = VM_NameServe;
257139ee03c3SScott Long 				mi->MntType = FT_FILESYS;
2572cbfd045bSScott Long 				mi->MntCount = i;
257336e0bf6eSScott Long 				rsize = sizeof(mir);
2574cbfd045bSScott Long 				if (aac_sync_fib(sc, ContainerCommand, 0, fib,
2575cbfd045bSScott Long 						 sizeof(struct aac_mntinfo))) {
2576795d7dc0SScott Long 					printf("Error probing container %d\n",
2577914da7d0SScott Long 					      i);
257836e0bf6eSScott Long 					continue;
257936e0bf6eSScott Long 				}
2580cbfd045bSScott Long 				mir = (struct aac_mntinforesp *)&fib->data[0];
2581795d7dc0SScott Long 				/* XXX Need to check if count changed */
2582795d7dc0SScott Long 				count = mir->MntRespCount;
258336e0bf6eSScott Long 				/*
2584914da7d0SScott Long 				 * Check the container against our list.
2585914da7d0SScott Long 				 * co->co_found was already set to 0 in a
2586914da7d0SScott Long 				 * previous run.
258736e0bf6eSScott Long 				 */
2588cbfd045bSScott Long 				if ((mir->Status == ST_OK) &&
2589cbfd045bSScott Long 				    (mir->MntTable[0].VolType != CT_NONE)) {
259036e0bf6eSScott Long 					found = 0;
2591914da7d0SScott Long 					TAILQ_FOREACH(co,
2592914da7d0SScott Long 						      &sc->aac_container_tqh,
2593914da7d0SScott Long 						      co_link) {
259436e0bf6eSScott Long 						if (co->co_mntobj.ObjectId ==
2595cbfd045bSScott Long 						    mir->MntTable[0].ObjectId) {
259636e0bf6eSScott Long 							co->co_found = 1;
259736e0bf6eSScott Long 							found = 1;
259836e0bf6eSScott Long 							break;
259936e0bf6eSScott Long 						}
260036e0bf6eSScott Long 					}
2601914da7d0SScott Long 					/*
2602914da7d0SScott Long 					 * If the container matched, continue
2603914da7d0SScott Long 					 * in the list.
2604914da7d0SScott Long 					 */
260536e0bf6eSScott Long 					if (found) {
260636e0bf6eSScott Long 						i++;
260736e0bf6eSScott Long 						continue;
260836e0bf6eSScott Long 					}
260936e0bf6eSScott Long 
261036e0bf6eSScott Long 					/*
2611914da7d0SScott Long 					 * This is a new container.  Do all the
261270545d1aSScott Long 					 * appropriate things to set it up.
261370545d1aSScott Long 					 */
2614cbfd045bSScott Long 					aac_add_container(sc, mir, 1);
261536e0bf6eSScott Long 					added = 1;
261636e0bf6eSScott Long 				}
261736e0bf6eSScott Long 				i++;
2618795d7dc0SScott Long 			} while ((i < count) && (i < AAC_MAX_CONTAINERS));
2619cbfd045bSScott Long 			aac_release_sync_fib(sc);
262036e0bf6eSScott Long 
262136e0bf6eSScott Long 			/*
2622914da7d0SScott Long 			 * Go through our list of containers and see which ones
2623914da7d0SScott Long 			 * were not marked 'found'.  Since the controller didn't
2624914da7d0SScott Long 			 * list them they must have been deleted.  Do the
2625914da7d0SScott Long 			 * appropriate steps to destroy the device.  Also reset
2626914da7d0SScott Long 			 * the co->co_found field.
262736e0bf6eSScott Long 			 */
262836e0bf6eSScott Long 			co = TAILQ_FIRST(&sc->aac_container_tqh);
262936e0bf6eSScott Long 			while (co != NULL) {
263036e0bf6eSScott Long 				if (co->co_found == 0) {
2631914da7d0SScott Long 					device_delete_child(sc->aac_dev,
2632914da7d0SScott Long 							    co->co_disk);
263336e0bf6eSScott Long 					co_next = TAILQ_NEXT(co, co_link);
2634c3d15322SScott Long 					AAC_LOCK_ACQUIRE(&sc->
2635914da7d0SScott Long 							aac_container_lock);
2636914da7d0SScott Long 					TAILQ_REMOVE(&sc->aac_container_tqh, co,
2637914da7d0SScott Long 						     co_link);
2638914da7d0SScott Long 					AAC_LOCK_RELEASE(&sc->
2639914da7d0SScott Long 							 aac_container_lock);
264036e0bf6eSScott Long 					FREE(co, M_AACBUF);
264136e0bf6eSScott Long 					co = co_next;
264236e0bf6eSScott Long 				} else {
264336e0bf6eSScott Long 					co->co_found = 0;
264436e0bf6eSScott Long 					co = TAILQ_NEXT(co, co_link);
264536e0bf6eSScott Long 				}
264636e0bf6eSScott Long 			}
264736e0bf6eSScott Long 
264836e0bf6eSScott Long 			/* Attach the newly created containers */
264936e0bf6eSScott Long 			if (added)
265036e0bf6eSScott Long 				bus_generic_attach(sc->aac_dev);
265136e0bf6eSScott Long 
265236e0bf6eSScott Long 			break;
265336e0bf6eSScott Long 
265436e0bf6eSScott Long 		default:
265536e0bf6eSScott Long 			break;
265636e0bf6eSScott Long 		}
265736e0bf6eSScott Long 
265836e0bf6eSScott Long 	default:
265936e0bf6eSScott Long 		break;
266036e0bf6eSScott Long 	}
266136e0bf6eSScott Long 
266236e0bf6eSScott Long 	/* Copy the AIF data to the AIF queue for ioctl retrieval */
2663c3d15322SScott Long 	AAC_LOCK_ACQUIRE(&sc->aac_aifq_lock);
266435863739SMike Smith 	next = (sc->aac_aifq_head + 1) % AAC_AIFQ_LENGTH;
266535863739SMike Smith 	if (next != sc->aac_aifq_tail) {
266635863739SMike Smith 		bcopy(aif, &sc->aac_aifq[next], sizeof(struct aac_aif_command));
266735863739SMike Smith 		sc->aac_aifq_head = next;
2668b3457b51SScott Long 
2669b3457b51SScott Long 		/* On the off chance that someone is sleeping for an aif... */
267035863739SMike Smith 		if (sc->aac_state & AAC_STATE_AIF_SLEEPER)
267135863739SMike Smith 			wakeup(sc->aac_aifq);
2672b3457b51SScott Long 		/* Wakeup any poll()ers */
2673512824f8SSeigo Tanimura 		selwakeuppri(&sc->rcv_select, PRIBIO);
267435863739SMike Smith 	}
2675b3457b51SScott Long 	AAC_LOCK_RELEASE(&sc->aac_aifq_lock);
267636e0bf6eSScott Long 
267736e0bf6eSScott Long 	return;
267835863739SMike Smith }
267935863739SMike Smith 
2680914da7d0SScott Long /*
26810b94a66eSMike Smith  * Return the Revision of the driver to userspace and check to see if the
268236e0bf6eSScott Long  * userspace app is possibly compatible.  This is extremely bogus since
268336e0bf6eSScott Long  * our driver doesn't follow Adaptec's versioning system.  Cheat by just
268436e0bf6eSScott Long  * returning what the card reported.
268535863739SMike Smith  */
268635863739SMike Smith static int
2687fb0c27d7SScott Long aac_rev_check(struct aac_softc *sc, caddr_t udata)
268835863739SMike Smith {
268935863739SMike Smith 	struct aac_rev_check rev_check;
269035863739SMike Smith 	struct aac_rev_check_resp rev_check_resp;
269135863739SMike Smith 	int error = 0;
269235863739SMike Smith 
269335863739SMike Smith 	debug_called(2);
269435863739SMike Smith 
269535863739SMike Smith 	/*
269635863739SMike Smith 	 * Copyin the revision struct from userspace
269735863739SMike Smith 	 */
2698c6eafcf2SScott Long 	if ((error = copyin(udata, (caddr_t)&rev_check,
2699c6eafcf2SScott Long 			sizeof(struct aac_rev_check))) != 0) {
270035863739SMike Smith 		return error;
270135863739SMike Smith 	}
270235863739SMike Smith 
2703914da7d0SScott Long 	debug(2, "Userland revision= %d\n",
2704914da7d0SScott Long 	      rev_check.callingRevision.buildNumber);
270535863739SMike Smith 
270635863739SMike Smith 	/*
270735863739SMike Smith 	 * Doctor up the response struct.
270835863739SMike Smith 	 */
270935863739SMike Smith 	rev_check_resp.possiblyCompatible = 1;
2710914da7d0SScott Long 	rev_check_resp.adapterSWRevision.external.ul =
2711914da7d0SScott Long 	    sc->aac_revision.external.ul;
2712914da7d0SScott Long 	rev_check_resp.adapterSWRevision.buildNumber =
2713914da7d0SScott Long 	    sc->aac_revision.buildNumber;
271435863739SMike Smith 
2715c6eafcf2SScott Long 	return(copyout((caddr_t)&rev_check_resp, udata,
2716c6eafcf2SScott Long 			sizeof(struct aac_rev_check_resp)));
271735863739SMike Smith }
271835863739SMike Smith 
2719914da7d0SScott Long /*
272035863739SMike Smith  * Pass the caller the next AIF in their queue
272135863739SMike Smith  */
272235863739SMike Smith static int
2723fb0c27d7SScott Long aac_getnext_aif(struct aac_softc *sc, caddr_t arg)
272435863739SMike Smith {
272535863739SMike Smith 	struct get_adapter_fib_ioctl agf;
27269e2e96d8SScott Long 	int error;
272735863739SMike Smith 
272835863739SMike Smith 	debug_called(2);
272935863739SMike Smith 
273035863739SMike Smith 	if ((error = copyin(arg, &agf, sizeof(agf))) == 0) {
273135863739SMike Smith 
273235863739SMike Smith 		/*
273335863739SMike Smith 		 * Check the magic number that we gave the caller.
273435863739SMike Smith 		 */
2735b88ffdc8SScott Long 		if (agf.AdapterFibContext != (int)(uintptr_t)sc->aifthread) {
273635863739SMike Smith 			error = EFAULT;
273735863739SMike Smith 		} else {
2738fb0c27d7SScott Long 			error = aac_return_aif(sc, agf.AifFib);
273935863739SMike Smith 			if ((error == EAGAIN) && (agf.Wait)) {
274035863739SMike Smith 				sc->aac_state |= AAC_STATE_AIF_SLEEPER;
274135863739SMike Smith 				while (error == EAGAIN) {
2742914da7d0SScott Long 					error = tsleep(sc->aac_aifq, PRIBIO |
2743914da7d0SScott Long 						       PCATCH, "aacaif", 0);
274435863739SMike Smith 					if (error == 0)
2745914da7d0SScott Long 						error = aac_return_aif(sc,
2746914da7d0SScott Long 						    agf.AifFib);
274735863739SMike Smith 				}
274835863739SMike Smith 				sc->aac_state &= ~AAC_STATE_AIF_SLEEPER;
274935863739SMike Smith 			}
275035863739SMike Smith 		}
275135863739SMike Smith 	}
275235863739SMike Smith 	return(error);
275335863739SMike Smith }
275435863739SMike Smith 
2755914da7d0SScott Long /*
27560b94a66eSMike Smith  * Hand the next AIF off the top of the queue out to userspace.
27570b94a66eSMike Smith  */
27580b94a66eSMike Smith static int
2759fb0c27d7SScott Long aac_return_aif(struct aac_softc *sc, caddr_t uptr)
27600b94a66eSMike Smith {
27613df780cfSScott Long 	int next, error;
27620b94a66eSMike Smith 
27630b94a66eSMike Smith 	debug_called(2);
27640b94a66eSMike Smith 
2765c3d15322SScott Long 	AAC_LOCK_ACQUIRE(&sc->aac_aifq_lock);
27660b94a66eSMike Smith 	if (sc->aac_aifq_tail == sc->aac_aifq_head) {
27673df780cfSScott Long 		AAC_LOCK_RELEASE(&sc->aac_aifq_lock);
27683df780cfSScott Long 		return (EAGAIN);
27693df780cfSScott Long 	}
27703df780cfSScott Long 
27713df780cfSScott Long 	next = (sc->aac_aifq_tail + 1) % AAC_AIFQ_LENGTH;
27723df780cfSScott Long 	error = copyout(&sc->aac_aifq[next], uptr,
2773c6eafcf2SScott Long 			sizeof(struct aac_aif_command));
277436e0bf6eSScott Long 	if (error)
277570545d1aSScott Long 		device_printf(sc->aac_dev,
277670545d1aSScott Long 		    "aac_return_aif: copyout returned %d\n", error);
27773df780cfSScott Long 	else
27783df780cfSScott Long 		sc->aac_aifq_tail = next;
27793df780cfSScott Long 
2780b3457b51SScott Long 	AAC_LOCK_RELEASE(&sc->aac_aifq_lock);
27810b94a66eSMike Smith 	return(error);
27820b94a66eSMike Smith }
278336e0bf6eSScott Long 
2784914da7d0SScott Long /*
278536e0bf6eSScott Long  * Give the userland some information about the container.  The AAC arch
278636e0bf6eSScott Long  * expects the driver to be a SCSI passthrough type driver, so it expects
278736e0bf6eSScott Long  * the containers to have b:t:l numbers.  Fake it.
278836e0bf6eSScott Long  */
278936e0bf6eSScott Long static int
279036e0bf6eSScott Long aac_query_disk(struct aac_softc *sc, caddr_t uptr)
279136e0bf6eSScott Long {
279236e0bf6eSScott Long 	struct aac_query_disk query_disk;
279336e0bf6eSScott Long 	struct aac_container *co;
2794914da7d0SScott Long 	struct aac_disk	*disk;
279536e0bf6eSScott Long 	int error, id;
279636e0bf6eSScott Long 
279736e0bf6eSScott Long 	debug_called(2);
279836e0bf6eSScott Long 
2799914da7d0SScott Long 	disk = NULL;
2800914da7d0SScott Long 
2801914da7d0SScott Long 	error = copyin(uptr, (caddr_t)&query_disk,
2802914da7d0SScott Long 		       sizeof(struct aac_query_disk));
280336e0bf6eSScott Long 	if (error)
280436e0bf6eSScott Long 		return (error);
280536e0bf6eSScott Long 
280636e0bf6eSScott Long 	id = query_disk.ContainerNumber;
280736e0bf6eSScott Long 	if (id == -1)
280836e0bf6eSScott Long 		return (EINVAL);
280936e0bf6eSScott Long 
2810c3d15322SScott Long 	AAC_LOCK_ACQUIRE(&sc->aac_container_lock);
281136e0bf6eSScott Long 	TAILQ_FOREACH(co, &sc->aac_container_tqh, co_link) {
281236e0bf6eSScott Long 		if (co->co_mntobj.ObjectId == id)
281336e0bf6eSScott Long 			break;
281436e0bf6eSScott Long 		}
281536e0bf6eSScott Long 
281636e0bf6eSScott Long 	if (co == NULL) {
281736e0bf6eSScott Long 			query_disk.Valid = 0;
281836e0bf6eSScott Long 			query_disk.Locked = 0;
281936e0bf6eSScott Long 			query_disk.Deleted = 1;		/* XXX is this right? */
282036e0bf6eSScott Long 	} else {
282136e0bf6eSScott Long 		disk = device_get_softc(co->co_disk);
282236e0bf6eSScott Long 		query_disk.Valid = 1;
2823914da7d0SScott Long 		query_disk.Locked =
2824914da7d0SScott Long 		    (disk->ad_flags & AAC_DISK_OPEN) ? 1 : 0;
282536e0bf6eSScott Long 		query_disk.Deleted = 0;
2826b3457b51SScott Long 		query_disk.Bus = device_get_unit(sc->aac_dev);
282736e0bf6eSScott Long 		query_disk.Target = disk->unit;
282836e0bf6eSScott Long 		query_disk.Lun = 0;
282936e0bf6eSScott Long 		query_disk.UnMapped = 0;
28307540e65eSScott Long 		sprintf(&query_disk.diskDeviceName[0], "%s%d",
28310b7ed341SPoul-Henning Kamp 		        disk->ad_disk->d_name, disk->ad_disk->d_unit);
283236e0bf6eSScott Long 	}
283336e0bf6eSScott Long 	AAC_LOCK_RELEASE(&sc->aac_container_lock);
283436e0bf6eSScott Long 
2835914da7d0SScott Long 	error = copyout((caddr_t)&query_disk, uptr,
2836914da7d0SScott Long 			sizeof(struct aac_query_disk));
283736e0bf6eSScott Long 
283836e0bf6eSScott Long 	return (error);
283936e0bf6eSScott Long }
284036e0bf6eSScott Long 
2841fe3cb0e1SScott Long static void
2842fe3cb0e1SScott Long aac_get_bus_info(struct aac_softc *sc)
2843fe3cb0e1SScott Long {
2844fe3cb0e1SScott Long 	struct aac_fib *fib;
2845fe3cb0e1SScott Long 	struct aac_ctcfg *c_cmd;
2846fe3cb0e1SScott Long 	struct aac_ctcfg_resp *c_resp;
2847fe3cb0e1SScott Long 	struct aac_vmioctl *vmi;
2848fe3cb0e1SScott Long 	struct aac_vmi_businf_resp *vmi_resp;
2849fe3cb0e1SScott Long 	struct aac_getbusinf businfo;
285070545d1aSScott Long 	struct aac_sim *caminf;
2851fe3cb0e1SScott Long 	device_t child;
2852fe3cb0e1SScott Long 	int i, found, error;
2853fe3cb0e1SScott Long 
285403b5fe51SScott Long 	aac_alloc_sync_fib(sc, &fib);
2855fe3cb0e1SScott Long 	c_cmd = (struct aac_ctcfg *)&fib->data[0];
285639ee03c3SScott Long 	bzero(c_cmd, sizeof(struct aac_ctcfg));
2857fe3cb0e1SScott Long 
2858fe3cb0e1SScott Long 	c_cmd->Command = VM_ContainerConfig;
2859fe3cb0e1SScott Long 	c_cmd->cmd = CT_GET_SCSI_METHOD;
2860fe3cb0e1SScott Long 	c_cmd->param = 0;
2861fe3cb0e1SScott Long 
2862fe3cb0e1SScott Long 	error = aac_sync_fib(sc, ContainerCommand, 0, fib,
2863fe3cb0e1SScott Long 	    sizeof(struct aac_ctcfg));
2864fe3cb0e1SScott Long 	if (error) {
2865fe3cb0e1SScott Long 		device_printf(sc->aac_dev, "Error %d sending "
2866fe3cb0e1SScott Long 		    "VM_ContainerConfig command\n", error);
2867fe3cb0e1SScott Long 		aac_release_sync_fib(sc);
2868fe3cb0e1SScott Long 		return;
2869fe3cb0e1SScott Long 	}
2870fe3cb0e1SScott Long 
2871fe3cb0e1SScott Long 	c_resp = (struct aac_ctcfg_resp *)&fib->data[0];
2872fe3cb0e1SScott Long 	if (c_resp->Status != ST_OK) {
2873fe3cb0e1SScott Long 		device_printf(sc->aac_dev, "VM_ContainerConfig returned 0x%x\n",
2874fe3cb0e1SScott Long 		    c_resp->Status);
2875fe3cb0e1SScott Long 		aac_release_sync_fib(sc);
2876fe3cb0e1SScott Long 		return;
2877fe3cb0e1SScott Long 	}
2878fe3cb0e1SScott Long 
2879fe3cb0e1SScott Long 	sc->scsi_method_id = c_resp->param;
2880fe3cb0e1SScott Long 
2881fe3cb0e1SScott Long 	vmi = (struct aac_vmioctl *)&fib->data[0];
288239ee03c3SScott Long 	bzero(vmi, sizeof(struct aac_vmioctl));
288339ee03c3SScott Long 
2884fe3cb0e1SScott Long 	vmi->Command = VM_Ioctl;
2885fe3cb0e1SScott Long 	vmi->ObjType = FT_DRIVE;
2886fe3cb0e1SScott Long 	vmi->MethId = sc->scsi_method_id;
2887fe3cb0e1SScott Long 	vmi->ObjId = 0;
2888fe3cb0e1SScott Long 	vmi->IoctlCmd = GetBusInfo;
2889fe3cb0e1SScott Long 
2890fe3cb0e1SScott Long 	error = aac_sync_fib(sc, ContainerCommand, 0, fib,
2891fe3cb0e1SScott Long 	    sizeof(struct aac_vmioctl));
2892fe3cb0e1SScott Long 	if (error) {
2893fe3cb0e1SScott Long 		device_printf(sc->aac_dev, "Error %d sending VMIoctl command\n",
2894fe3cb0e1SScott Long 		    error);
2895fe3cb0e1SScott Long 		aac_release_sync_fib(sc);
2896fe3cb0e1SScott Long 		return;
2897fe3cb0e1SScott Long 	}
2898fe3cb0e1SScott Long 
2899fe3cb0e1SScott Long 	vmi_resp = (struct aac_vmi_businf_resp *)&fib->data[0];
2900fe3cb0e1SScott Long 	if (vmi_resp->Status != ST_OK) {
2901fe3cb0e1SScott Long 		device_printf(sc->aac_dev, "VM_Ioctl returned %d\n",
2902fe3cb0e1SScott Long 		    vmi_resp->Status);
2903fe3cb0e1SScott Long 		aac_release_sync_fib(sc);
2904fe3cb0e1SScott Long 		return;
2905fe3cb0e1SScott Long 	}
2906fe3cb0e1SScott Long 
2907fe3cb0e1SScott Long 	bcopy(&vmi_resp->BusInf, &businfo, sizeof(struct aac_getbusinf));
2908fe3cb0e1SScott Long 	aac_release_sync_fib(sc);
2909fe3cb0e1SScott Long 
2910fe3cb0e1SScott Long 	found = 0;
2911fe3cb0e1SScott Long 	for (i = 0; i < businfo.BusCount; i++) {
2912fe3cb0e1SScott Long 		if (businfo.BusValid[i] != AAC_BUS_VALID)
2913fe3cb0e1SScott Long 			continue;
2914fe3cb0e1SScott Long 
2915a761a1caSScott Long 		caminf = (struct aac_sim *)malloc( sizeof(struct aac_sim),
2916a761a1caSScott Long 		    M_AACBUF, M_NOWAIT | M_ZERO);
2917fe3cb0e1SScott Long 		if (caminf == NULL)
2918fe3cb0e1SScott Long 			continue;
2919fe3cb0e1SScott Long 
2920fe3cb0e1SScott Long 		child = device_add_child(sc->aac_dev, "aacp", -1);
2921fe3cb0e1SScott Long 		if (child == NULL) {
2922fe3cb0e1SScott Long 			device_printf(sc->aac_dev, "device_add_child failed\n");
2923fe3cb0e1SScott Long 			continue;
2924fe3cb0e1SScott Long 		}
2925fe3cb0e1SScott Long 
2926fe3cb0e1SScott Long 		caminf->TargetsPerBus = businfo.TargetsPerBus;
2927fe3cb0e1SScott Long 		caminf->BusNumber = i;
2928fe3cb0e1SScott Long 		caminf->InitiatorBusId = businfo.InitiatorBusId[i];
2929fe3cb0e1SScott Long 		caminf->aac_sc = sc;
2930ddb8683eSScott Long 		caminf->sim_dev = child;
2931fe3cb0e1SScott Long 
2932fe3cb0e1SScott Long 		device_set_ivars(child, caminf);
2933fe3cb0e1SScott Long 		device_set_desc(child, "SCSI Passthrough Bus");
293470545d1aSScott Long 		TAILQ_INSERT_TAIL(&sc->aac_sim_tqh, caminf, sim_link);
2935fe3cb0e1SScott Long 
2936fe3cb0e1SScott Long 		found = 1;
2937fe3cb0e1SScott Long 	}
2938fe3cb0e1SScott Long 
2939fe3cb0e1SScott Long 	if (found)
2940fe3cb0e1SScott Long 		bus_generic_attach(sc->aac_dev);
2941fe3cb0e1SScott Long 
2942fe3cb0e1SScott Long 	return;
2943fe3cb0e1SScott Long }
2944