xref: /freebsd/sys/dev/aac/aac.c (revision a620bad028b89666a48f9ebff475ef0e4165bf9f)
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  */
367cb209f5SScott Long #define AAC_DRIVER_VERSION		0x02000000
377cb209f5SScott Long #define AAC_DRIVERNAME			"aac"
3835863739SMike Smith 
39f6c4dd3fSScott Long #include "opt_aac.h"
40f6c4dd3fSScott Long 
4136e0bf6eSScott Long /* #include <stddef.h> */
4235863739SMike Smith #include <sys/param.h>
4335863739SMike Smith #include <sys/systm.h>
4435863739SMike Smith #include <sys/malloc.h>
4535863739SMike Smith #include <sys/kernel.h>
4636e0bf6eSScott Long #include <sys/kthread.h>
473d04a9d7SScott Long #include <sys/sysctl.h>
48b3457b51SScott Long #include <sys/poll.h>
49891619a6SPoul-Henning Kamp #include <sys/ioccom.h>
5035863739SMike Smith 
5135863739SMike Smith #include <sys/bus.h>
5235863739SMike Smith #include <sys/conf.h>
5335863739SMike Smith #include <sys/signalvar.h>
540b94a66eSMike Smith #include <sys/time.h>
5536e0bf6eSScott Long #include <sys/eventhandler.h>
567cb209f5SScott Long #include <sys/rman.h>
5735863739SMike Smith 
5835863739SMike Smith #include <machine/bus.h>
59b5f516cdSScott Long #include <sys/bus_dma.h>
6035863739SMike Smith #include <machine/resource.h>
6135863739SMike Smith 
627cb209f5SScott Long #include <dev/pci/pcireg.h>
637cb209f5SScott Long #include <dev/pci/pcivar.h>
647cb209f5SScott Long 
6535863739SMike Smith #include <dev/aac/aacreg.h>
660b0594cdSScott Long #include <sys/aac_ioctl.h>
6735863739SMike Smith #include <dev/aac/aacvar.h>
6835863739SMike Smith #include <dev/aac/aac_tables.h>
6935863739SMike Smith 
7035863739SMike Smith static void	aac_startup(void *arg);
71914da7d0SScott Long static void	aac_add_container(struct aac_softc *sc,
72cbfd045bSScott Long 				  struct aac_mntinforesp *mir, int f);
73fe3cb0e1SScott Long static void	aac_get_bus_info(struct aac_softc *sc);
7435863739SMike Smith 
7535863739SMike Smith /* Command Processing */
760b94a66eSMike Smith static void	aac_timeout(struct aac_softc *sc);
7735863739SMike Smith static void	aac_complete(void *context, int pending);
7835863739SMike Smith static int	aac_bio_command(struct aac_softc *sc, struct aac_command **cmp);
7935863739SMike Smith static void	aac_bio_complete(struct aac_command *cm);
80d8a0a473SScott Long static int	aac_wait_command(struct aac_command *cm);
8170545d1aSScott Long static void	aac_command_thread(struct aac_softc *sc);
8235863739SMike Smith 
8335863739SMike Smith /* Command Buffer Management */
84cd481291SScott Long static void	aac_map_command_sg(void *arg, bus_dma_segment_t *segs,
85cd481291SScott Long 				   int nseg, int error);
86c6eafcf2SScott Long static void	aac_map_command_helper(void *arg, bus_dma_segment_t *segs,
87c6eafcf2SScott Long 				       int nseg, int error);
880b94a66eSMike Smith static int	aac_alloc_commands(struct aac_softc *sc);
898480cc63SScott Long static void	aac_free_commands(struct aac_softc *sc);
9035863739SMike Smith static void	aac_unmap_command(struct aac_command *cm);
9135863739SMike Smith 
9235863739SMike Smith /* Hardware Interface */
9304f4d586SEd Maste static int	aac_alloc(struct aac_softc *sc);
94c6eafcf2SScott Long static void	aac_common_map(void *arg, bus_dma_segment_t *segs, int nseg,
95c6eafcf2SScott Long 			       int error);
96fe94b852SScott Long static int	aac_check_firmware(struct aac_softc *sc);
9735863739SMike Smith static int	aac_init(struct aac_softc *sc);
9835863739SMike Smith static int	aac_sync_command(struct aac_softc *sc, u_int32_t command,
99c6eafcf2SScott Long 				 u_int32_t arg0, u_int32_t arg1, u_int32_t arg2,
100c6eafcf2SScott Long 				 u_int32_t arg3, u_int32_t *sp);
10104f4d586SEd Maste static int	aac_setup_intr(struct aac_softc *sc);
102c6eafcf2SScott Long static int	aac_enqueue_fib(struct aac_softc *sc, int queue,
103f6c4dd3fSScott Long 				struct aac_command *cm);
104c6eafcf2SScott Long static int	aac_dequeue_fib(struct aac_softc *sc, int queue,
105914da7d0SScott Long 				u_int32_t *fib_size, struct aac_fib **fib_addr);
10636e0bf6eSScott Long static int	aac_enqueue_response(struct aac_softc *sc, int queue,
10736e0bf6eSScott Long 				     struct aac_fib *fib);
10835863739SMike Smith 
109b3457b51SScott Long /* Falcon/PPC interface */
110b3457b51SScott Long static int	aac_fa_get_fwstatus(struct aac_softc *sc);
111b3457b51SScott Long static void	aac_fa_qnotify(struct aac_softc *sc, int qbit);
112b3457b51SScott Long static int	aac_fa_get_istatus(struct aac_softc *sc);
113b3457b51SScott Long static void	aac_fa_clear_istatus(struct aac_softc *sc, int mask);
114b3457b51SScott Long static void	aac_fa_set_mailbox(struct aac_softc *sc, u_int32_t command,
115b3457b51SScott Long 				   u_int32_t arg0, u_int32_t arg1,
116b3457b51SScott Long 				   u_int32_t arg2, u_int32_t arg3);
117a6d35632SScott Long static int	aac_fa_get_mailbox(struct aac_softc *sc, int mb);
118b3457b51SScott Long static void	aac_fa_set_interrupts(struct aac_softc *sc, int enable);
119b3457b51SScott Long 
120b3457b51SScott Long struct aac_interface aac_fa_interface = {
121b3457b51SScott Long 	aac_fa_get_fwstatus,
122b3457b51SScott Long 	aac_fa_qnotify,
123b3457b51SScott Long 	aac_fa_get_istatus,
124b3457b51SScott Long 	aac_fa_clear_istatus,
125b3457b51SScott Long 	aac_fa_set_mailbox,
126a6d35632SScott Long 	aac_fa_get_mailbox,
1277cb209f5SScott Long 	aac_fa_set_interrupts,
1287cb209f5SScott Long 	NULL, NULL, NULL
129b3457b51SScott Long };
130b3457b51SScott Long 
13135863739SMike Smith /* StrongARM interface */
13235863739SMike Smith static int	aac_sa_get_fwstatus(struct aac_softc *sc);
13335863739SMike Smith static void	aac_sa_qnotify(struct aac_softc *sc, int qbit);
13435863739SMike Smith static int	aac_sa_get_istatus(struct aac_softc *sc);
13535863739SMike Smith static void	aac_sa_clear_istatus(struct aac_softc *sc, int mask);
13635863739SMike Smith static void	aac_sa_set_mailbox(struct aac_softc *sc, u_int32_t command,
137c6eafcf2SScott Long 				   u_int32_t arg0, u_int32_t arg1,
138c6eafcf2SScott Long 				   u_int32_t arg2, u_int32_t arg3);
139a6d35632SScott Long static int	aac_sa_get_mailbox(struct aac_softc *sc, int mb);
14035863739SMike Smith static void	aac_sa_set_interrupts(struct aac_softc *sc, int enable);
14135863739SMike Smith 
14235863739SMike Smith struct aac_interface aac_sa_interface = {
14335863739SMike Smith 	aac_sa_get_fwstatus,
14435863739SMike Smith 	aac_sa_qnotify,
14535863739SMike Smith 	aac_sa_get_istatus,
14635863739SMike Smith 	aac_sa_clear_istatus,
14735863739SMike Smith 	aac_sa_set_mailbox,
148a6d35632SScott Long 	aac_sa_get_mailbox,
1497cb209f5SScott Long 	aac_sa_set_interrupts,
1507cb209f5SScott Long 	NULL, NULL, NULL
15135863739SMike Smith };
15235863739SMike Smith 
15335863739SMike Smith /* i960Rx interface */
15435863739SMike Smith static int	aac_rx_get_fwstatus(struct aac_softc *sc);
15535863739SMike Smith static void	aac_rx_qnotify(struct aac_softc *sc, int qbit);
15635863739SMike Smith static int	aac_rx_get_istatus(struct aac_softc *sc);
15735863739SMike Smith static void	aac_rx_clear_istatus(struct aac_softc *sc, int mask);
15835863739SMike Smith static void	aac_rx_set_mailbox(struct aac_softc *sc, u_int32_t command,
159c6eafcf2SScott Long 				   u_int32_t arg0, u_int32_t arg1,
160c6eafcf2SScott Long 				   u_int32_t arg2, u_int32_t arg3);
161a6d35632SScott Long static int	aac_rx_get_mailbox(struct aac_softc *sc, int mb);
16235863739SMike Smith static void	aac_rx_set_interrupts(struct aac_softc *sc, int enable);
1637cb209f5SScott Long static int aac_rx_send_command(struct aac_softc *sc, struct aac_command *cm);
1647cb209f5SScott Long static int aac_rx_get_outb_queue(struct aac_softc *sc);
1657cb209f5SScott Long static void aac_rx_set_outb_queue(struct aac_softc *sc, int index);
16635863739SMike Smith 
16735863739SMike Smith struct aac_interface aac_rx_interface = {
16835863739SMike Smith 	aac_rx_get_fwstatus,
16935863739SMike Smith 	aac_rx_qnotify,
17035863739SMike Smith 	aac_rx_get_istatus,
17135863739SMike Smith 	aac_rx_clear_istatus,
17235863739SMike Smith 	aac_rx_set_mailbox,
173a6d35632SScott Long 	aac_rx_get_mailbox,
1747cb209f5SScott Long 	aac_rx_set_interrupts,
1757cb209f5SScott Long 	aac_rx_send_command,
1767cb209f5SScott Long 	aac_rx_get_outb_queue,
1777cb209f5SScott Long 	aac_rx_set_outb_queue
17835863739SMike Smith };
17935863739SMike Smith 
1804afedc31SScott Long /* Rocket/MIPS interface */
1814afedc31SScott Long static int	aac_rkt_get_fwstatus(struct aac_softc *sc);
1824afedc31SScott Long static void	aac_rkt_qnotify(struct aac_softc *sc, int qbit);
1834afedc31SScott Long static int	aac_rkt_get_istatus(struct aac_softc *sc);
1844afedc31SScott Long static void	aac_rkt_clear_istatus(struct aac_softc *sc, int mask);
1854afedc31SScott Long static void	aac_rkt_set_mailbox(struct aac_softc *sc, u_int32_t command,
1864afedc31SScott Long 				    u_int32_t arg0, u_int32_t arg1,
1874afedc31SScott Long 				    u_int32_t arg2, u_int32_t arg3);
1884afedc31SScott Long static int	aac_rkt_get_mailbox(struct aac_softc *sc, int mb);
1894afedc31SScott Long static void	aac_rkt_set_interrupts(struct aac_softc *sc, int enable);
1907cb209f5SScott Long static int aac_rkt_send_command(struct aac_softc *sc, struct aac_command *cm);
1917cb209f5SScott Long static int aac_rkt_get_outb_queue(struct aac_softc *sc);
1927cb209f5SScott Long static void aac_rkt_set_outb_queue(struct aac_softc *sc, int index);
1934afedc31SScott Long 
1944afedc31SScott Long struct aac_interface aac_rkt_interface = {
1954afedc31SScott Long 	aac_rkt_get_fwstatus,
1964afedc31SScott Long 	aac_rkt_qnotify,
1974afedc31SScott Long 	aac_rkt_get_istatus,
1984afedc31SScott Long 	aac_rkt_clear_istatus,
1994afedc31SScott Long 	aac_rkt_set_mailbox,
2004afedc31SScott Long 	aac_rkt_get_mailbox,
2017cb209f5SScott Long 	aac_rkt_set_interrupts,
2027cb209f5SScott Long 	aac_rkt_send_command,
2037cb209f5SScott Long 	aac_rkt_get_outb_queue,
2047cb209f5SScott Long 	aac_rkt_set_outb_queue
2054afedc31SScott Long };
2064afedc31SScott Long 
20735863739SMike Smith /* Debugging and Diagnostics */
20835863739SMike Smith static void	aac_describe_controller(struct aac_softc *sc);
2096965a493SScott Long static char	*aac_describe_code(struct aac_code_lookup *table,
210c6eafcf2SScott Long 				   u_int32_t code);
21135863739SMike Smith 
21235863739SMike Smith /* Management Interface */
21335863739SMike Smith static d_open_t		aac_open;
21435863739SMike Smith static d_close_t	aac_close;
21535863739SMike Smith static d_ioctl_t	aac_ioctl;
216b3457b51SScott Long static d_poll_t		aac_poll;
217c6eafcf2SScott Long static int		aac_ioctl_sendfib(struct aac_softc *sc, caddr_t ufib);
218f355c0e0SEd Maste static int		aac_ioctl_send_raw_srb(struct aac_softc *sc, caddr_t arg);
219c6eafcf2SScott Long static void		aac_handle_aif(struct aac_softc *sc,
22036e0bf6eSScott Long 					   struct aac_fib *fib);
221fb0c27d7SScott Long static int		aac_rev_check(struct aac_softc *sc, caddr_t udata);
222a723a548SEd Maste static int		aac_open_aif(struct aac_softc *sc, caddr_t arg);
223a723a548SEd Maste static int		aac_close_aif(struct aac_softc *sc, caddr_t arg);
224fb0c27d7SScott Long static int		aac_getnext_aif(struct aac_softc *sc, caddr_t arg);
225a723a548SEd Maste static int		aac_return_aif(struct aac_softc *sc,
226a723a548SEd Maste 					struct aac_fib_context *ctx, caddr_t uptr);
22736e0bf6eSScott Long static int		aac_query_disk(struct aac_softc *sc, caddr_t uptr);
2287cb209f5SScott Long static int		aac_get_pci_info(struct aac_softc *sc, caddr_t uptr);
2297cb209f5SScott Long static void		aac_ioctl_event(struct aac_softc *sc,
2307cb209f5SScott Long 				        struct aac_event *event, void *arg);
23104f4d586SEd Maste static struct aac_mntinforesp *
23204f4d586SEd Maste 	aac_get_container_info(struct aac_softc *sc, struct aac_fib *fib, int cid);
23335863739SMike Smith 
23435863739SMike Smith static struct cdevsw aac_cdevsw = {
235dc08ffecSPoul-Henning Kamp 	.d_version =	D_VERSION,
236dc08ffecSPoul-Henning Kamp 	.d_flags =	D_NEEDGIANT,
2377ac40f5fSPoul-Henning Kamp 	.d_open =	aac_open,
2387ac40f5fSPoul-Henning Kamp 	.d_close =	aac_close,
2397ac40f5fSPoul-Henning Kamp 	.d_ioctl =	aac_ioctl,
2407ac40f5fSPoul-Henning Kamp 	.d_poll =	aac_poll,
2417ac40f5fSPoul-Henning Kamp 	.d_name =	"aac",
24235863739SMike Smith };
24335863739SMike Smith 
24436e0bf6eSScott Long MALLOC_DEFINE(M_AACBUF, "aacbuf", "Buffers for the AAC driver");
24536e0bf6eSScott Long 
2463d04a9d7SScott Long /* sysctl node */
2473d04a9d7SScott Long SYSCTL_NODE(_hw, OID_AUTO, aac, CTLFLAG_RD, 0, "AAC driver parameters");
2483d04a9d7SScott Long 
249914da7d0SScott Long /*
250914da7d0SScott Long  * Device Interface
251914da7d0SScott Long  */
25235863739SMike Smith 
253914da7d0SScott Long /*
2544109ba51SEd Maste  * Initialize the controller and softc
25535863739SMike Smith  */
25635863739SMike Smith int
25735863739SMike Smith aac_attach(struct aac_softc *sc)
25835863739SMike Smith {
25935863739SMike Smith 	int error, unit;
26035863739SMike Smith 
26131a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
26235863739SMike Smith 
26335863739SMike Smith 	/*
2644109ba51SEd Maste 	 * Initialize per-controller queues.
26535863739SMike Smith 	 */
2660b94a66eSMike Smith 	aac_initq_free(sc);
2670b94a66eSMike Smith 	aac_initq_ready(sc);
2680b94a66eSMike Smith 	aac_initq_busy(sc);
2690b94a66eSMike Smith 	aac_initq_bio(sc);
27035863739SMike Smith 
27135863739SMike Smith 	/*
2724109ba51SEd Maste 	 * Initialize command-completion task.
27335863739SMike Smith 	 */
27435863739SMike Smith 	TASK_INIT(&sc->aac_task_complete, 0, aac_complete, sc);
27535863739SMike Smith 
27635863739SMike Smith 	/* mark controller as suspended until we get ourselves organised */
27735863739SMike Smith 	sc->aac_state |= AAC_STATE_SUSPEND;
27835863739SMike Smith 
27935863739SMike Smith 	/*
280fe94b852SScott Long 	 * Check that the firmware on the card is supported.
281fe94b852SScott Long 	 */
282fe94b852SScott Long 	if ((error = aac_check_firmware(sc)) != 0)
283fe94b852SScott Long 		return(error);
284fe94b852SScott Long 
285f6b1c44dSScott Long 	/*
286f6b1c44dSScott Long 	 * Initialize locks
287f6b1c44dSScott Long 	 */
288bb6fe253SScott Long 	mtx_init(&sc->aac_aifq_lock, "AAC AIF lock", NULL, MTX_DEF);
289bb6fe253SScott Long 	mtx_init(&sc->aac_io_lock, "AAC I/O lock", NULL, MTX_DEF);
290bb6fe253SScott Long 	mtx_init(&sc->aac_container_lock, "AAC container lock", NULL, MTX_DEF);
291f6b1c44dSScott Long 	TAILQ_INIT(&sc->aac_container_tqh);
292065dd78cSScott Long 	TAILQ_INIT(&sc->aac_ev_cmfree);
293f6b1c44dSScott Long 
2940b94a66eSMike Smith 	/*
2954109ba51SEd Maste 	 * Initialize the adapter.
29635863739SMike Smith 	 */
29704f4d586SEd Maste 	if ((error = aac_alloc(sc)) != 0)
29804f4d586SEd Maste 		return(error);
2990b94a66eSMike Smith 	if ((error = aac_init(sc)) != 0)
30035863739SMike Smith 		return(error);
30135863739SMike Smith 
30235863739SMike Smith 	/*
3037cb209f5SScott Long 	 * Allocate and connect our interrupt.
3047cb209f5SScott Long 	 */
30504f4d586SEd Maste 	if ((error = aac_setup_intr(sc)) != 0)
30604f4d586SEd Maste 		return(error);
3077cb209f5SScott Long 
3087cb209f5SScott Long 	/*
30935863739SMike Smith 	 * Print a little information about the controller.
31035863739SMike Smith 	 */
31135863739SMike Smith 	aac_describe_controller(sc);
31235863739SMike Smith 
31335863739SMike Smith 	/*
314ae543596SScott Long 	 * Register to probe our containers later.
315ae543596SScott Long 	 */
31635863739SMike Smith 	sc->aac_ich.ich_func = aac_startup;
31735863739SMike Smith 	sc->aac_ich.ich_arg = sc;
31835863739SMike Smith 	if (config_intrhook_establish(&sc->aac_ich) != 0) {
319914da7d0SScott Long 		device_printf(sc->aac_dev,
320914da7d0SScott Long 			      "can't establish configuration hook\n");
32135863739SMike Smith 		return(ENXIO);
32235863739SMike Smith 	}
32335863739SMike Smith 
32435863739SMike Smith 	/*
32535863739SMike Smith 	 * Make the control device.
32635863739SMike Smith 	 */
32735863739SMike Smith 	unit = device_get_unit(sc->aac_dev);
3289e9466baSRobert Watson 	sc->aac_dev_t = make_dev(&aac_cdevsw, unit, UID_ROOT, GID_OPERATOR,
3299e9466baSRobert Watson 				 0640, "aac%d", unit);
330157fbb2eSScott Long 	(void)make_dev_alias(sc->aac_dev_t, "afa%d", unit);
3314aa620cdSScott Long 	(void)make_dev_alias(sc->aac_dev_t, "hpn%d", unit);
33235863739SMike Smith 	sc->aac_dev_t->si_drv1 = sc;
33335863739SMike Smith 
33436e0bf6eSScott Long 	/* Create the AIF thread */
3353745c395SJulian Elischer 	if (kproc_create((void(*)(void *))aac_command_thread, sc,
336316ec49aSScott Long 		   &sc->aifthread, 0, 0, "aac%daif", unit))
337a620bad0SEd Maste 		panic("Could not create AIF thread");
33836e0bf6eSScott Long 
33936e0bf6eSScott Long 	/* Register the shutdown method to only be called post-dump */
3405f54d522SScott Long 	if ((sc->eh = EVENTHANDLER_REGISTER(shutdown_final, aac_shutdown,
3415f54d522SScott Long 	    sc->aac_dev, SHUTDOWN_PRI_DEFAULT)) == NULL)
3425f54d522SScott Long 		device_printf(sc->aac_dev,
3435f54d522SScott Long 			      "shutdown event registration failed\n");
34436e0bf6eSScott Long 
345fe3cb0e1SScott Long 	/* Register with CAM for the non-DASD devices */
346a6d35632SScott Long 	if ((sc->flags & AAC_FLAGS_ENABLE_CAM) != 0) {
34770545d1aSScott Long 		TAILQ_INIT(&sc->aac_sim_tqh);
348fe3cb0e1SScott Long 		aac_get_bus_info(sc);
34970545d1aSScott Long 	}
350fe3cb0e1SScott Long 
35135863739SMike Smith 	return(0);
35235863739SMike Smith }
35335863739SMike Smith 
3547cb209f5SScott Long void
3557cb209f5SScott Long aac_add_event(struct aac_softc *sc, struct aac_event *event)
3567cb209f5SScott Long {
3577cb209f5SScott Long 
3587cb209f5SScott Long 	switch (event->ev_type & AAC_EVENT_MASK) {
3597cb209f5SScott Long 	case AAC_EVENT_CMFREE:
3607cb209f5SScott Long 		TAILQ_INSERT_TAIL(&sc->aac_ev_cmfree, event, ev_links);
3617cb209f5SScott Long 		break;
3627cb209f5SScott Long 	default:
3637cb209f5SScott Long 		device_printf(sc->aac_dev, "aac_add event: unknown event %d\n",
3647cb209f5SScott Long 		    event->ev_type);
3657cb209f5SScott Long 		break;
3667cb209f5SScott Long 	}
3677cb209f5SScott Long 
3687cb209f5SScott Long 	return;
3697cb209f5SScott Long }
3707cb209f5SScott Long 
371914da7d0SScott Long /*
37204f4d586SEd Maste  * Request information of container #cid
37304f4d586SEd Maste  */
37404f4d586SEd Maste static struct aac_mntinforesp *
37504f4d586SEd Maste aac_get_container_info(struct aac_softc *sc, struct aac_fib *fib, int cid)
37604f4d586SEd Maste {
37704f4d586SEd Maste 	struct aac_mntinfo *mi;
37804f4d586SEd Maste 
37904f4d586SEd Maste 	mi = (struct aac_mntinfo *)&fib->data[0];
380523da39bSEd Maste 	/* use 64-bit LBA if enabled */
381523da39bSEd Maste 	mi->Command = (sc->flags & AAC_FLAGS_LBA_64BIT) ?
382523da39bSEd Maste 	    VM_NameServe64 : VM_NameServe;
38304f4d586SEd Maste 	mi->MntType = FT_FILESYS;
38404f4d586SEd Maste 	mi->MntCount = cid;
38504f4d586SEd Maste 
38604f4d586SEd Maste 	if (aac_sync_fib(sc, ContainerCommand, 0, fib,
38704f4d586SEd Maste 			 sizeof(struct aac_mntinfo))) {
388a620bad0SEd Maste 		printf("Error probing container %d\n", cid);
38904f4d586SEd Maste 		return (NULL);
39004f4d586SEd Maste 	}
39104f4d586SEd Maste 
39204f4d586SEd Maste 	return ((struct aac_mntinforesp *)&fib->data[0]);
39304f4d586SEd Maste }
39404f4d586SEd Maste 
39504f4d586SEd Maste /*
39635863739SMike Smith  * Probe for containers, create disks.
39735863739SMike Smith  */
39835863739SMike Smith static void
39935863739SMike Smith aac_startup(void *arg)
40035863739SMike Smith {
401914da7d0SScott Long 	struct aac_softc *sc;
402cbfd045bSScott Long 	struct aac_fib *fib;
40304f4d586SEd Maste 	struct aac_mntinforesp *mir;
404795d7dc0SScott Long 	int count = 0, i = 0;
40535863739SMike Smith 
406914da7d0SScott Long 	sc = (struct aac_softc *)arg;
40731a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
408914da7d0SScott Long 
40935863739SMike Smith 	/* disconnect ourselves from the intrhook chain */
41035863739SMike Smith 	config_intrhook_disestablish(&sc->aac_ich);
41135863739SMike Smith 
4127cb209f5SScott Long 	mtx_lock(&sc->aac_io_lock);
41303b5fe51SScott Long 	aac_alloc_sync_fib(sc, &fib);
414cbfd045bSScott Long 
41535863739SMike Smith 	/* loop over possible containers */
41636e0bf6eSScott Long 	do {
41704f4d586SEd Maste 		if ((mir = aac_get_container_info(sc, fib, i)) == NULL)
41835863739SMike Smith 			continue;
41904f4d586SEd Maste 		if (i == 0)
420795d7dc0SScott Long 			count = mir->MntRespCount;
421cbfd045bSScott Long 		aac_add_container(sc, mir, 0);
42236e0bf6eSScott Long 		i++;
423795d7dc0SScott Long 	} while ((i < count) && (i < AAC_MAX_CONTAINERS));
424cbfd045bSScott Long 
425cbfd045bSScott Long 	aac_release_sync_fib(sc);
4267cb209f5SScott Long 	mtx_unlock(&sc->aac_io_lock);
42735863739SMike Smith 
42835863739SMike Smith 	/* poke the bus to actually attach the child devices */
42935863739SMike Smith 	if (bus_generic_attach(sc->aac_dev))
43035863739SMike Smith 		device_printf(sc->aac_dev, "bus_generic_attach failed\n");
43135863739SMike Smith 
43235863739SMike Smith 	/* mark the controller up */
43335863739SMike Smith 	sc->aac_state &= ~AAC_STATE_SUSPEND;
43435863739SMike Smith 
43535863739SMike Smith 	/* enable interrupts now */
43635863739SMike Smith 	AAC_UNMASK_INTERRUPTS(sc);
43735863739SMike Smith }
43835863739SMike Smith 
439914da7d0SScott Long /*
4404109ba51SEd Maste  * Create a device to represent a new container
441914da7d0SScott Long  */
442914da7d0SScott Long static void
443cbfd045bSScott Long aac_add_container(struct aac_softc *sc, struct aac_mntinforesp *mir, int f)
444914da7d0SScott Long {
445914da7d0SScott Long 	struct aac_container *co;
446914da7d0SScott Long 	device_t child;
447914da7d0SScott Long 
448914da7d0SScott Long 	/*
449914da7d0SScott Long 	 * Check container volume type for validity.  Note that many of
450914da7d0SScott Long 	 * the possible types may never show up.
451914da7d0SScott Long 	 */
452914da7d0SScott Long 	if ((mir->Status == ST_OK) && (mir->MntTable[0].VolType != CT_NONE)) {
453a761a1caSScott Long 		co = (struct aac_container *)malloc(sizeof *co, M_AACBUF,
454a761a1caSScott Long 		       M_NOWAIT | M_ZERO);
455914da7d0SScott Long 		if (co == NULL)
456a620bad0SEd Maste 			panic("Out of memory?!");
45731a0399eSEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_INIT_B, "id %x  name '%.16s'  size %u  type %d",
458914da7d0SScott Long 		      mir->MntTable[0].ObjectId,
459914da7d0SScott Long 		      mir->MntTable[0].FileSystemName,
460914da7d0SScott Long 		      mir->MntTable[0].Capacity, mir->MntTable[0].VolType);
461914da7d0SScott Long 
462fe3cb0e1SScott Long 		if ((child = device_add_child(sc->aac_dev, "aacd", -1)) == NULL)
463914da7d0SScott Long 			device_printf(sc->aac_dev, "device_add_child failed\n");
464914da7d0SScott Long 		else
465914da7d0SScott Long 			device_set_ivars(child, co);
466914da7d0SScott Long 		device_set_desc(child, aac_describe_code(aac_container_types,
467914da7d0SScott Long 				mir->MntTable[0].VolType));
468914da7d0SScott Long 		co->co_disk = child;
469914da7d0SScott Long 		co->co_found = f;
470914da7d0SScott Long 		bcopy(&mir->MntTable[0], &co->co_mntobj,
471914da7d0SScott Long 		      sizeof(struct aac_mntobj));
472bb6fe253SScott Long 		mtx_lock(&sc->aac_container_lock);
473914da7d0SScott Long 		TAILQ_INSERT_TAIL(&sc->aac_container_tqh, co, co_link);
474bb6fe253SScott Long 		mtx_unlock(&sc->aac_container_lock);
475914da7d0SScott Long 	}
476914da7d0SScott Long }
477914da7d0SScott Long 
478914da7d0SScott Long /*
47904f4d586SEd Maste  * Allocate resources associated with (sc)
48004f4d586SEd Maste  */
48104f4d586SEd Maste static int
48204f4d586SEd Maste aac_alloc(struct aac_softc *sc)
48304f4d586SEd Maste {
48431a0399eSEd Maste 
48531a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
48631a0399eSEd Maste 
48704f4d586SEd Maste 	/*
48804f4d586SEd Maste 	 * Create DMA tag for mapping buffers into controller-addressable space.
48904f4d586SEd Maste 	 */
49004f4d586SEd Maste 	if (bus_dma_tag_create(sc->aac_parent_dmat, 	/* parent */
49104f4d586SEd Maste 			       1, 0, 			/* algnmnt, boundary */
49204f4d586SEd Maste 			       (sc->flags & AAC_FLAGS_SG_64BIT) ?
49304f4d586SEd Maste 			       BUS_SPACE_MAXADDR :
49404f4d586SEd Maste 			       BUS_SPACE_MAXADDR_32BIT,	/* lowaddr */
49504f4d586SEd Maste 			       BUS_SPACE_MAXADDR, 	/* highaddr */
49604f4d586SEd Maste 			       NULL, NULL, 		/* filter, filterarg */
49704f4d586SEd Maste 			       MAXBSIZE,		/* maxsize */
49804f4d586SEd Maste 			       sc->aac_sg_tablesize,	/* nsegments */
49904f4d586SEd Maste 			       MAXBSIZE,		/* maxsegsize */
50004f4d586SEd Maste 			       BUS_DMA_ALLOCNOW,	/* flags */
50104f4d586SEd Maste 			       busdma_lock_mutex,	/* lockfunc */
50204f4d586SEd Maste 			       &sc->aac_io_lock,	/* lockfuncarg */
50304f4d586SEd Maste 			       &sc->aac_buffer_dmat)) {
50404f4d586SEd Maste 		device_printf(sc->aac_dev, "can't allocate buffer DMA tag\n");
50504f4d586SEd Maste 		return (ENOMEM);
50604f4d586SEd Maste 	}
50704f4d586SEd Maste 
50804f4d586SEd Maste 	/*
50904f4d586SEd Maste 	 * Create DMA tag for mapping FIBs into controller-addressable space..
51004f4d586SEd Maste 	 */
51104f4d586SEd Maste 	if (bus_dma_tag_create(sc->aac_parent_dmat,	/* parent */
51204f4d586SEd Maste 			       1, 0, 			/* algnmnt, boundary */
51304f4d586SEd Maste 			       (sc->flags & AAC_FLAGS_4GB_WINDOW) ?
51404f4d586SEd Maste 			       BUS_SPACE_MAXADDR_32BIT :
51504f4d586SEd Maste 			       0x7fffffff,		/* lowaddr */
51604f4d586SEd Maste 			       BUS_SPACE_MAXADDR, 	/* highaddr */
51704f4d586SEd Maste 			       NULL, NULL, 		/* filter, filterarg */
51804f4d586SEd Maste 			       sc->aac_max_fibs_alloc *
51904f4d586SEd Maste 			       sc->aac_max_fib_size,  /* maxsize */
52004f4d586SEd Maste 			       1,			/* nsegments */
52104f4d586SEd Maste 			       sc->aac_max_fibs_alloc *
52204f4d586SEd Maste 			       sc->aac_max_fib_size,	/* maxsize */
52304f4d586SEd Maste 			       0,			/* flags */
52404f4d586SEd Maste 			       NULL, NULL,		/* No locking needed */
52504f4d586SEd Maste 			       &sc->aac_fib_dmat)) {
52604f4d586SEd Maste 		device_printf(sc->aac_dev, "can't allocate FIB DMA tag\n");;
52704f4d586SEd Maste 		return (ENOMEM);
52804f4d586SEd Maste 	}
52904f4d586SEd Maste 
53004f4d586SEd Maste 	/*
53104f4d586SEd Maste 	 * Create DMA tag for the common structure and allocate it.
53204f4d586SEd Maste 	 */
53304f4d586SEd Maste 	if (bus_dma_tag_create(sc->aac_parent_dmat, 	/* parent */
53404f4d586SEd Maste 			       1, 0,			/* algnmnt, boundary */
53504f4d586SEd Maste 			       (sc->flags & AAC_FLAGS_4GB_WINDOW) ?
53604f4d586SEd Maste 			       BUS_SPACE_MAXADDR_32BIT :
53704f4d586SEd Maste 			       0x7fffffff,		/* lowaddr */
53804f4d586SEd Maste 			       BUS_SPACE_MAXADDR, 	/* highaddr */
53904f4d586SEd Maste 			       NULL, NULL, 		/* filter, filterarg */
54004f4d586SEd Maste 			       8192 + sizeof(struct aac_common), /* maxsize */
54104f4d586SEd Maste 			       1,			/* nsegments */
54204f4d586SEd Maste 			       BUS_SPACE_MAXSIZE_32BIT,	/* maxsegsize */
54304f4d586SEd Maste 			       0,			/* flags */
54404f4d586SEd Maste 			       NULL, NULL,		/* No locking needed */
54504f4d586SEd Maste 			       &sc->aac_common_dmat)) {
54604f4d586SEd Maste 		device_printf(sc->aac_dev,
54704f4d586SEd Maste 			      "can't allocate common structure DMA tag\n");
54804f4d586SEd Maste 		return (ENOMEM);
54904f4d586SEd Maste 	}
55004f4d586SEd Maste 	if (bus_dmamem_alloc(sc->aac_common_dmat, (void **)&sc->aac_common,
55104f4d586SEd Maste 			     BUS_DMA_NOWAIT, &sc->aac_common_dmamap)) {
55204f4d586SEd Maste 		device_printf(sc->aac_dev, "can't allocate common structure\n");
55304f4d586SEd Maste 		return (ENOMEM);
55404f4d586SEd Maste 	}
55504f4d586SEd Maste 
55604f4d586SEd Maste 	/*
55704f4d586SEd Maste 	 * Work around a bug in the 2120 and 2200 that cannot DMA commands
55804f4d586SEd Maste 	 * below address 8192 in physical memory.
55904f4d586SEd Maste 	 * XXX If the padding is not needed, can it be put to use instead
56004f4d586SEd Maste 	 * of ignored?
56104f4d586SEd Maste 	 */
56204f4d586SEd Maste 	(void)bus_dmamap_load(sc->aac_common_dmat, sc->aac_common_dmamap,
56304f4d586SEd Maste 			sc->aac_common, 8192 + sizeof(*sc->aac_common),
56404f4d586SEd Maste 			aac_common_map, sc, 0);
56504f4d586SEd Maste 
56604f4d586SEd Maste 	if (sc->aac_common_busaddr < 8192) {
56704f4d586SEd Maste 		sc->aac_common = (struct aac_common *)
56804f4d586SEd Maste 		    ((uint8_t *)sc->aac_common + 8192);
56904f4d586SEd Maste 		sc->aac_common_busaddr += 8192;
57004f4d586SEd Maste 	}
57104f4d586SEd Maste 	bzero(sc->aac_common, sizeof(*sc->aac_common));
57204f4d586SEd Maste 
57304f4d586SEd Maste 	/* Allocate some FIBs and associated command structs */
57404f4d586SEd Maste 	TAILQ_INIT(&sc->aac_fibmap_tqh);
57504f4d586SEd Maste 	sc->aac_commands = malloc(sc->aac_max_fibs * sizeof(struct aac_command),
57604f4d586SEd Maste 				  M_AACBUF, M_WAITOK|M_ZERO);
57704f4d586SEd Maste 	while (sc->total_fibs < AAC_PREALLOCATE_FIBS) {
57804f4d586SEd Maste 		if (aac_alloc_commands(sc) != 0)
57904f4d586SEd Maste 			break;
58004f4d586SEd Maste 	}
58104f4d586SEd Maste 	if (sc->total_fibs == 0)
58204f4d586SEd Maste 		return (ENOMEM);
58304f4d586SEd Maste 
58404f4d586SEd Maste 	return (0);
58504f4d586SEd Maste }
58604f4d586SEd Maste 
58704f4d586SEd Maste /*
58835863739SMike Smith  * Free all of the resources associated with (sc)
58935863739SMike Smith  *
59035863739SMike Smith  * Should not be called if the controller is active.
59135863739SMike Smith  */
59235863739SMike Smith void
59335863739SMike Smith aac_free(struct aac_softc *sc)
59435863739SMike Smith {
595ffb37f33SScott Long 
59631a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
59735863739SMike Smith 
59835863739SMike Smith 	/* remove the control device */
59935863739SMike Smith 	if (sc->aac_dev_t != NULL)
60035863739SMike Smith 		destroy_dev(sc->aac_dev_t);
60135863739SMike Smith 
6020b94a66eSMike Smith 	/* throw away any FIB buffers, discard the FIB DMA tag */
6038480cc63SScott Long 	aac_free_commands(sc);
6040b94a66eSMike Smith 	if (sc->aac_fib_dmat)
6050b94a66eSMike Smith 		bus_dma_tag_destroy(sc->aac_fib_dmat);
60635863739SMike Smith 
607ffb37f33SScott Long 	free(sc->aac_commands, M_AACBUF);
608ffb37f33SScott Long 
60935863739SMike Smith 	/* destroy the common area */
61035863739SMike Smith 	if (sc->aac_common) {
61135863739SMike Smith 		bus_dmamap_unload(sc->aac_common_dmat, sc->aac_common_dmamap);
612c6eafcf2SScott Long 		bus_dmamem_free(sc->aac_common_dmat, sc->aac_common,
613c6eafcf2SScott Long 				sc->aac_common_dmamap);
61435863739SMike Smith 	}
6150b94a66eSMike Smith 	if (sc->aac_common_dmat)
6160b94a66eSMike Smith 		bus_dma_tag_destroy(sc->aac_common_dmat);
61735863739SMike Smith 
61835863739SMike Smith 	/* disconnect the interrupt handler */
61935863739SMike Smith 	if (sc->aac_intr)
62035863739SMike Smith 		bus_teardown_intr(sc->aac_dev, sc->aac_irq, sc->aac_intr);
62135863739SMike Smith 	if (sc->aac_irq != NULL)
622c6eafcf2SScott Long 		bus_release_resource(sc->aac_dev, SYS_RES_IRQ, sc->aac_irq_rid,
623c6eafcf2SScott Long 				     sc->aac_irq);
62435863739SMike Smith 
62535863739SMike Smith 	/* destroy data-transfer DMA tag */
62635863739SMike Smith 	if (sc->aac_buffer_dmat)
62735863739SMike Smith 		bus_dma_tag_destroy(sc->aac_buffer_dmat);
62835863739SMike Smith 
62935863739SMike Smith 	/* destroy the parent DMA tag */
63035863739SMike Smith 	if (sc->aac_parent_dmat)
63135863739SMike Smith 		bus_dma_tag_destroy(sc->aac_parent_dmat);
63235863739SMike Smith 
63335863739SMike Smith 	/* release the register window mapping */
63435863739SMike Smith 	if (sc->aac_regs_resource != NULL)
635914da7d0SScott Long 		bus_release_resource(sc->aac_dev, SYS_RES_MEMORY,
636914da7d0SScott Long 				     sc->aac_regs_rid, sc->aac_regs_resource);
63735863739SMike Smith }
63835863739SMike Smith 
639914da7d0SScott Long /*
64035863739SMike Smith  * Disconnect from the controller completely, in preparation for unload.
64135863739SMike Smith  */
64235863739SMike Smith int
64335863739SMike Smith aac_detach(device_t dev)
64435863739SMike Smith {
645914da7d0SScott Long 	struct aac_softc *sc;
64670545d1aSScott Long 	struct aac_container *co;
64770545d1aSScott Long 	struct aac_sim	*sim;
64835863739SMike Smith 	int error;
64935863739SMike Smith 
650914da7d0SScott Long 	sc = device_get_softc(dev);
65131a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
652914da7d0SScott Long 
65335863739SMike Smith 	if (sc->aac_state & AAC_STATE_OPEN)
65435863739SMike Smith 		return(EBUSY);
65535863739SMike Smith 
65670545d1aSScott Long 	/* Remove the child containers */
657a761a1caSScott Long 	while ((co = TAILQ_FIRST(&sc->aac_container_tqh)) != NULL) {
65870545d1aSScott Long 		error = device_delete_child(dev, co->co_disk);
65970545d1aSScott Long 		if (error)
66070545d1aSScott Long 			return (error);
66165ac4ed6SScott Long 		TAILQ_REMOVE(&sc->aac_container_tqh, co, co_link);
662a761a1caSScott Long 		free(co, M_AACBUF);
66370545d1aSScott Long 	}
66470545d1aSScott Long 
66570545d1aSScott Long 	/* Remove the CAM SIMs */
666a761a1caSScott Long 	while ((sim = TAILQ_FIRST(&sc->aac_sim_tqh)) != NULL) {
667a761a1caSScott Long 		TAILQ_REMOVE(&sc->aac_sim_tqh, sim, sim_link);
66870545d1aSScott Long 		error = device_delete_child(dev, sim->sim_dev);
66970545d1aSScott Long 		if (error)
67070545d1aSScott Long 			return (error);
671a761a1caSScott Long 		free(sim, M_AACBUF);
67270545d1aSScott Long 	}
67370545d1aSScott Long 
67436e0bf6eSScott Long 	if (sc->aifflags & AAC_AIFFLAGS_RUNNING) {
67536e0bf6eSScott Long 		sc->aifflags |= AAC_AIFFLAGS_EXIT;
67636e0bf6eSScott Long 		wakeup(sc->aifthread);
67736e0bf6eSScott Long 		tsleep(sc->aac_dev, PUSER | PCATCH, "aacdch", 30 * hz);
67836e0bf6eSScott Long 	}
67936e0bf6eSScott Long 
68036e0bf6eSScott Long 	if (sc->aifflags & AAC_AIFFLAGS_RUNNING)
681a620bad0SEd Maste 		panic("Cannot shutdown AIF thread");
68236e0bf6eSScott Long 
68335863739SMike Smith 	if ((error = aac_shutdown(dev)))
68435863739SMike Smith 		return(error);
68535863739SMike Smith 
6865f54d522SScott Long 	EVENTHANDLER_DEREGISTER(shutdown_final, sc->eh);
6875f54d522SScott Long 
68835863739SMike Smith 	aac_free(sc);
68935863739SMike Smith 
690dc9efde5SScott Long 	mtx_destroy(&sc->aac_aifq_lock);
691dc9efde5SScott Long 	mtx_destroy(&sc->aac_io_lock);
692dc9efde5SScott Long 	mtx_destroy(&sc->aac_container_lock);
693dc9efde5SScott Long 
69435863739SMike Smith 	return(0);
69535863739SMike Smith }
69635863739SMike Smith 
697914da7d0SScott Long /*
69835863739SMike Smith  * Bring the controller down to a dormant state and detach all child devices.
69935863739SMike Smith  *
70035863739SMike Smith  * This function is called before detach or system shutdown.
70135863739SMike Smith  *
7020b94a66eSMike Smith  * Note that we can assume that the bioq on the controller is empty, as we won't
70335863739SMike Smith  * allow shutdown if any device is open.
70435863739SMike Smith  */
70535863739SMike Smith int
70635863739SMike Smith aac_shutdown(device_t dev)
70735863739SMike Smith {
708914da7d0SScott Long 	struct aac_softc *sc;
709cbfd045bSScott Long 	struct aac_fib *fib;
710cbfd045bSScott Long 	struct aac_close_command *cc;
71135863739SMike Smith 
712914da7d0SScott Long 	sc = device_get_softc(dev);
71331a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
714914da7d0SScott Long 
71535863739SMike Smith 	sc->aac_state |= AAC_STATE_SUSPEND;
71635863739SMike Smith 
71735863739SMike Smith 	/*
71835863739SMike Smith 	 * Send a Container shutdown followed by a HostShutdown FIB to the
71935863739SMike Smith 	 * controller to convince it that we don't want to talk to it anymore.
72035863739SMike Smith 	 * We've been closed and all I/O completed already
72135863739SMike Smith 	 */
72235863739SMike Smith 	device_printf(sc->aac_dev, "shutting down controller...");
72335863739SMike Smith 
7247cb209f5SScott Long 	mtx_lock(&sc->aac_io_lock);
72503b5fe51SScott Long 	aac_alloc_sync_fib(sc, &fib);
726cbfd045bSScott Long 	cc = (struct aac_close_command *)&fib->data[0];
727cbfd045bSScott Long 
72839ee03c3SScott Long 	bzero(cc, sizeof(struct aac_close_command));
729cbfd045bSScott Long 	cc->Command = VM_CloseAll;
730cbfd045bSScott Long 	cc->ContainerId = 0xffffffff;
731cbfd045bSScott Long 	if (aac_sync_fib(sc, ContainerCommand, 0, fib,
732cbfd045bSScott Long 	    sizeof(struct aac_close_command)))
73335863739SMike Smith 		printf("FAILED.\n");
73470545d1aSScott Long 	else
73570545d1aSScott Long 		printf("done\n");
73670545d1aSScott Long #if 0
737914da7d0SScott Long 	else {
738cbfd045bSScott Long 		fib->data[0] = 0;
73936e0bf6eSScott Long 		/*
740914da7d0SScott Long 		 * XXX Issuing this command to the controller makes it shut down
74136e0bf6eSScott Long 		 * but also keeps it from coming back up without a reset of the
74236e0bf6eSScott Long 		 * PCI bus.  This is not desirable if you are just unloading the
74336e0bf6eSScott Long 		 * driver module with the intent to reload it later.
74436e0bf6eSScott Long 		 */
745cbfd045bSScott Long 		if (aac_sync_fib(sc, FsaHostShutdown, AAC_FIBSTATE_SHUTDOWN,
746cbfd045bSScott Long 		    fib, 1)) {
74735863739SMike Smith 			printf("FAILED.\n");
74835863739SMike Smith 		} else {
74935863739SMike Smith 			printf("done.\n");
75035863739SMike Smith 		}
75135863739SMike Smith 	}
75270545d1aSScott Long #endif
75335863739SMike Smith 
75435863739SMike Smith 	AAC_MASK_INTERRUPTS(sc);
7553576af8fSScott Long 	aac_release_sync_fib(sc);
7567cb209f5SScott Long 	mtx_unlock(&sc->aac_io_lock);
75735863739SMike Smith 
75835863739SMike Smith 	return(0);
75935863739SMike Smith }
76035863739SMike Smith 
761914da7d0SScott Long /*
76235863739SMike Smith  * Bring the controller to a quiescent state, ready for system suspend.
76335863739SMike Smith  */
76435863739SMike Smith int
76535863739SMike Smith aac_suspend(device_t dev)
76635863739SMike Smith {
767914da7d0SScott Long 	struct aac_softc *sc;
76835863739SMike Smith 
769914da7d0SScott Long 	sc = device_get_softc(dev);
770914da7d0SScott Long 
77131a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
77235863739SMike Smith 	sc->aac_state |= AAC_STATE_SUSPEND;
77335863739SMike Smith 
77435863739SMike Smith 	AAC_MASK_INTERRUPTS(sc);
77535863739SMike Smith 	return(0);
77635863739SMike Smith }
77735863739SMike Smith 
778914da7d0SScott Long /*
77935863739SMike Smith  * Bring the controller back to a state ready for operation.
78035863739SMike Smith  */
78135863739SMike Smith int
78235863739SMike Smith aac_resume(device_t dev)
78335863739SMike Smith {
784914da7d0SScott Long 	struct aac_softc *sc;
78535863739SMike Smith 
786914da7d0SScott Long 	sc = device_get_softc(dev);
787914da7d0SScott Long 
78831a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
78935863739SMike Smith 	sc->aac_state &= ~AAC_STATE_SUSPEND;
79035863739SMike Smith 	AAC_UNMASK_INTERRUPTS(sc);
79135863739SMike Smith 	return(0);
79235863739SMike Smith }
79335863739SMike Smith 
794914da7d0SScott Long /*
7957cb209f5SScott Long  * Interrupt handler for NEW_COMM interface.
79635863739SMike Smith  */
79735863739SMike Smith void
7987cb209f5SScott Long aac_new_intr(void *arg)
7997cb209f5SScott Long {
8007cb209f5SScott Long 	struct aac_softc *sc;
8017cb209f5SScott Long 	u_int32_t index, fast;
8027cb209f5SScott Long 	struct aac_command *cm;
8037cb209f5SScott Long 	struct aac_fib *fib;
8047cb209f5SScott Long 	int i;
8057cb209f5SScott Long 
8067cb209f5SScott Long 	sc = (struct aac_softc *)arg;
8077cb209f5SScott Long 
80831a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
8097cb209f5SScott Long 	mtx_lock(&sc->aac_io_lock);
8107cb209f5SScott Long 	while (1) {
8117cb209f5SScott Long 		index = AAC_GET_OUTB_QUEUE(sc);
8127cb209f5SScott Long 		if (index == 0xffffffff)
8137cb209f5SScott Long 			index = AAC_GET_OUTB_QUEUE(sc);
8147cb209f5SScott Long 		if (index == 0xffffffff)
8157cb209f5SScott Long 			break;
8167cb209f5SScott Long 		if (index & 2) {
8177cb209f5SScott Long 			if (index == 0xfffffffe) {
8187cb209f5SScott Long 				/* XXX This means that the controller wants
8197cb209f5SScott Long 				 * more work.  Ignore it for now.
8207cb209f5SScott Long 				 */
8217cb209f5SScott Long 				continue;
8227cb209f5SScott Long 			}
8237cb209f5SScott Long 			/* AIF */
8247cb209f5SScott Long 			fib = (struct aac_fib *)malloc(sizeof *fib, M_AACBUF,
8257cb209f5SScott Long 				   M_NOWAIT | M_ZERO);
8267cb209f5SScott Long 			if (fib == NULL) {
8277cb209f5SScott Long 				/* If we're really this short on memory,
8287cb209f5SScott Long 				 * hopefully breaking out of the handler will
8297cb209f5SScott Long 				 * allow something to get freed.  This
8307cb209f5SScott Long 				 * actually sucks a whole lot.
8317cb209f5SScott Long 				 */
8327cb209f5SScott Long 				break;
8337cb209f5SScott Long 			}
8347cb209f5SScott Long 			index &= ~2;
8357cb209f5SScott Long 			for (i = 0; i < sizeof(struct aac_fib)/4; ++i)
8367cb209f5SScott Long 				((u_int32_t *)fib)[i] = AAC_GETREG4(sc, index + i*4);
8377cb209f5SScott Long 			aac_handle_aif(sc, fib);
8387cb209f5SScott Long 			free(fib, M_AACBUF);
8397cb209f5SScott Long 
8407cb209f5SScott Long 			/*
8417cb209f5SScott Long 			 * AIF memory is owned by the adapter, so let it
8427cb209f5SScott Long 			 * know that we are done with it.
8437cb209f5SScott Long 			 */
8447cb209f5SScott Long 			AAC_SET_OUTB_QUEUE(sc, index);
8457cb209f5SScott Long 			AAC_CLEAR_ISTATUS(sc, AAC_DB_RESPONSE_READY);
8467cb209f5SScott Long 		} else {
8477cb209f5SScott Long 			fast = index & 1;
8487cb209f5SScott Long 			cm = sc->aac_commands + (index >> 2);
8497cb209f5SScott Long 			fib = cm->cm_fib;
8507cb209f5SScott Long 			if (fast) {
8517cb209f5SScott Long 				fib->Header.XferState |= AAC_FIBSTATE_DONEADAP;
8527cb209f5SScott Long 				*((u_int32_t *)(fib->data)) = AAC_ERROR_NORMAL;
8537cb209f5SScott Long 			}
8547cb209f5SScott Long 			aac_remove_busy(cm);
8557cb209f5SScott Long  			aac_unmap_command(cm);
8567cb209f5SScott Long 			cm->cm_flags |= AAC_CMD_COMPLETED;
8577cb209f5SScott Long 
8587cb209f5SScott Long 			/* is there a completion handler? */
8597cb209f5SScott Long 			if (cm->cm_complete != NULL) {
8607cb209f5SScott Long 				cm->cm_complete(cm);
8617cb209f5SScott Long 			} else {
8627cb209f5SScott Long 				/* assume that someone is sleeping on this
8637cb209f5SScott Long 				 * command
8647cb209f5SScott Long 				 */
8657cb209f5SScott Long 				wakeup(cm);
8667cb209f5SScott Long 			}
8677cb209f5SScott Long 			sc->flags &= ~AAC_QUEUE_FRZN;
8687cb209f5SScott Long 		}
8697cb209f5SScott Long 	}
8707cb209f5SScott Long 	/* see if we can start some more I/O */
8717cb209f5SScott Long 	if ((sc->flags & AAC_QUEUE_FRZN) == 0)
8727cb209f5SScott Long 		aac_startio(sc);
8737cb209f5SScott Long 
8747cb209f5SScott Long 	mtx_unlock(&sc->aac_io_lock);
8757cb209f5SScott Long }
8767cb209f5SScott Long 
877ef544f63SPaolo Pisati int
8787cb209f5SScott Long aac_fast_intr(void *arg)
87935863739SMike Smith {
880914da7d0SScott Long 	struct aac_softc *sc;
88170545d1aSScott Long 	u_int16_t reason;
88235863739SMike Smith 
883914da7d0SScott Long 	sc = (struct aac_softc *)arg;
884914da7d0SScott Long 
88531a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
886f30ac74cSScott Long 	/*
8879148fa21SScott Long 	 * Read the status register directly.  This is faster than taking the
8889148fa21SScott Long 	 * driver lock and reading the queues directly.  It also saves having
8899148fa21SScott Long 	 * to turn parts of the driver lock into a spin mutex, which would be
8909148fa21SScott Long 	 * ugly.
891f30ac74cSScott Long 	 */
89235863739SMike Smith 	reason = AAC_GET_ISTATUS(sc);
893f30ac74cSScott Long 	AAC_CLEAR_ISTATUS(sc, reason);
894f30ac74cSScott Long 
8959c3a7fceSScott Long 	/* handle completion processing */
8969148fa21SScott Long 	if (reason & AAC_DB_RESPONSE_READY)
8979148fa21SScott Long 		taskqueue_enqueue_fast(taskqueue_fast, &sc->aac_task_complete);
89835863739SMike Smith 
8999148fa21SScott Long 	/* controller wants to talk to us */
9009148fa21SScott Long 	if (reason & (AAC_DB_PRINTF | AAC_DB_COMMAND_READY)) {
90170545d1aSScott Long 		/*
9029148fa21SScott Long 		 * XXX Make sure that we don't get fooled by strange messages
9039148fa21SScott Long 		 * that start with a NULL.
90470545d1aSScott Long 		 */
9059148fa21SScott Long 		if ((reason & AAC_DB_PRINTF) &&
9069148fa21SScott Long 			(sc->aac_common->ac_printf[0] == 0))
9079148fa21SScott Long 			sc->aac_common->ac_printf[0] = 32;
90870545d1aSScott Long 
9099148fa21SScott Long 		/*
9109148fa21SScott Long 		 * This might miss doing the actual wakeup.  However, the
911a32a982dSScott Long 		 * msleep that this is waking up has a timeout, so it will
9129148fa21SScott Long 		 * wake up eventually.  AIFs and printfs are low enough
9139148fa21SScott Long 		 * priority that they can handle hanging out for a few seconds
9149148fa21SScott Long 		 * if needed.
9159148fa21SScott Long 		 */
91636e0bf6eSScott Long 		wakeup(sc->aifthread);
91736e0bf6eSScott Long 	}
918ef544f63SPaolo Pisati 	return (FILTER_HANDLED);
9199148fa21SScott Long }
92035863739SMike Smith 
921c6eafcf2SScott Long /*
922914da7d0SScott Long  * Command Processing
923914da7d0SScott Long  */
92435863739SMike Smith 
925914da7d0SScott Long /*
92635863739SMike Smith  * Start as much queued I/O as possible on the controller
92735863739SMike Smith  */
928fe3cb0e1SScott Long void
92935863739SMike Smith aac_startio(struct aac_softc *sc)
93035863739SMike Smith {
93135863739SMike Smith 	struct aac_command *cm;
932397fa34fSScott Long 	int error;
93335863739SMike Smith 
93431a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
93535863739SMike Smith 
93635863739SMike Smith 	for (;;) {
937914da7d0SScott Long 		/*
938397fa34fSScott Long 		 * This flag might be set if the card is out of resources.
939397fa34fSScott Long 		 * Checking it here prevents an infinite loop of deferrals.
940397fa34fSScott Long 		 */
941397fa34fSScott Long 		if (sc->flags & AAC_QUEUE_FRZN)
942397fa34fSScott Long 			break;
943397fa34fSScott Long 
944397fa34fSScott Long 		/*
945914da7d0SScott Long 		 * Try to get a command that's been put off for lack of
946914da7d0SScott Long 		 * resources
947914da7d0SScott Long 		 */
94835863739SMike Smith 		cm = aac_dequeue_ready(sc);
94935863739SMike Smith 
950914da7d0SScott Long 		/*
951914da7d0SScott Long 		 * Try to build a command off the bio queue (ignore error
952914da7d0SScott Long 		 * return)
953914da7d0SScott Long 		 */
9540b94a66eSMike Smith 		if (cm == NULL)
95535863739SMike Smith 			aac_bio_command(sc, &cm);
95635863739SMike Smith 
95735863739SMike Smith 		/* nothing to do? */
95835863739SMike Smith 		if (cm == NULL)
95935863739SMike Smith 			break;
96035863739SMike Smith 
961cd481291SScott Long 		/* don't map more than once */
962cd481291SScott Long 		if (cm->cm_flags & AAC_CMD_MAPPED)
9634102d44bSScott Long 			panic("aac: command %p already mapped", cm);
96435863739SMike Smith 
965397fa34fSScott Long 		/*
966397fa34fSScott Long 		 * Set up the command to go to the controller.  If there are no
967397fa34fSScott Long 		 * data buffers associated with the command then it can bypass
968397fa34fSScott Long 		 * busdma.
969397fa34fSScott Long 		 */
970cd481291SScott Long 		if (cm->cm_datalen != 0) {
971397fa34fSScott Long 			error = bus_dmamap_load(sc->aac_buffer_dmat,
972397fa34fSScott Long 						cm->cm_datamap, cm->cm_data,
973397fa34fSScott Long 						cm->cm_datalen,
974cd481291SScott Long 						aac_map_command_sg, cm, 0);
975cd481291SScott Long 			if (error == EINPROGRESS) {
97631a0399eSEd Maste 				fwprintf(sc, HBA_FLAGS_DBG_COMM_B, "freezing queue\n");
977cd481291SScott Long 				sc->flags |= AAC_QUEUE_FRZN;
978cd481291SScott Long 				error = 0;
979614c22b2SScott Long 			} else if (error != 0)
980397fa34fSScott Long 				panic("aac_startio: unexpected error %d from "
981a620bad0SEd Maste 				      "busdma", error);
982397fa34fSScott Long 		} else
9838778f63dSScott Long 			aac_map_command_sg(cm, NULL, 0, 0);
984cd481291SScott Long 	}
98535863739SMike Smith }
98635863739SMike Smith 
987914da7d0SScott Long /*
98835863739SMike Smith  * Handle notification of one or more FIBs coming from the controller.
98935863739SMike Smith  */
99035863739SMike Smith static void
99170545d1aSScott Long aac_command_thread(struct aac_softc *sc)
99235863739SMike Smith {
99335863739SMike Smith 	struct aac_fib *fib;
99435863739SMike Smith 	u_int32_t fib_size;
9959148fa21SScott Long 	int size, retval;
99635863739SMike Smith 
99731a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
99835863739SMike Smith 
999bb6fe253SScott Long 	mtx_lock(&sc->aac_io_lock);
1000a32a982dSScott Long 	sc->aifflags = AAC_AIFFLAGS_RUNNING;
100136e0bf6eSScott Long 
1002a32a982dSScott Long 	while ((sc->aifflags & AAC_AIFFLAGS_EXIT) == 0) {
1003a32a982dSScott Long 
1004a32a982dSScott Long 		retval = 0;
1005a32a982dSScott Long 		if ((sc->aifflags & AAC_AIFFLAGS_PENDING) == 0)
1006a32a982dSScott Long 			retval = msleep(sc->aifthread, &sc->aac_io_lock, PRIBIO,
1007a32a982dSScott Long 					"aifthd", AAC_PERIODIC_INTERVAL * hz);
100836e0bf6eSScott Long 
10099148fa21SScott Long 		/*
10109148fa21SScott Long 		 * First see if any FIBs need to be allocated.  This needs
10119148fa21SScott Long 		 * to be called without the driver lock because contigmalloc
10129148fa21SScott Long 		 * will grab Giant, and would result in an LOR.
10139148fa21SScott Long 		 */
10149148fa21SScott Long 		if ((sc->aifflags & AAC_AIFFLAGS_ALLOCFIBS) != 0) {
1015bb6fe253SScott Long 			mtx_unlock(&sc->aac_io_lock);
1016a32a982dSScott Long 			aac_alloc_commands(sc);
1017bb6fe253SScott Long 			mtx_lock(&sc->aac_io_lock);
10184102d44bSScott Long 			sc->aifflags &= ~AAC_AIFFLAGS_ALLOCFIBS;
1019a32a982dSScott Long 			aac_startio(sc);
1020a32a982dSScott Long 		}
10219148fa21SScott Long 
10229148fa21SScott Long 		/*
10239148fa21SScott Long 		 * While we're here, check to see if any commands are stuck.
10249148fa21SScott Long 		 * This is pretty low-priority, so it's ok if it doesn't
10259148fa21SScott Long 		 * always fire.
10269148fa21SScott Long 		 */
10279148fa21SScott Long 		if (retval == EWOULDBLOCK)
102870545d1aSScott Long 			aac_timeout(sc);
102970545d1aSScott Long 
103070545d1aSScott Long 		/* Check the hardware printf message buffer */
10319148fa21SScott Long 		if (sc->aac_common->ac_printf[0] != 0)
103270545d1aSScott Long 			aac_print_printf(sc);
103370545d1aSScott Long 
10349148fa21SScott Long 		/* Also check to see if the adapter has a command for us. */
10357cb209f5SScott Long 		if (sc->flags & AAC_FLAGS_NEW_COMM)
10367cb209f5SScott Long 			continue;
10377cb209f5SScott Long 		for (;;) {
10387cb209f5SScott Long 			if (aac_dequeue_fib(sc, AAC_HOST_NORM_CMD_QUEUE,
10397cb209f5SScott Long 					   &fib_size, &fib))
10407cb209f5SScott Long 				break;
104135863739SMike Smith 
104236e0bf6eSScott Long 			AAC_PRINT_FIB(sc, fib);
104336e0bf6eSScott Long 
104435863739SMike Smith 			switch (fib->Header.Command) {
104535863739SMike Smith 			case AifRequest:
104636e0bf6eSScott Long 				aac_handle_aif(sc, fib);
104735863739SMike Smith 				break;
104835863739SMike Smith 			default:
1049914da7d0SScott Long 				device_printf(sc->aac_dev, "unknown command "
1050914da7d0SScott Long 					      "from controller\n");
105135863739SMike Smith 				break;
105235863739SMike Smith 			}
105335863739SMike Smith 
105436e0bf6eSScott Long 			if ((fib->Header.XferState == 0) ||
10557cb209f5SScott Long 			    (fib->Header.StructType != AAC_FIBTYPE_TFIB)) {
105636e0bf6eSScott Long 				break;
10577cb209f5SScott Long 			}
105836e0bf6eSScott Long 
105970545d1aSScott Long 			/* Return the AIF to the controller. */
106036e0bf6eSScott Long 			if (fib->Header.XferState & AAC_FIBSTATE_FROMADAP) {
106136e0bf6eSScott Long 				fib->Header.XferState |= AAC_FIBSTATE_DONEHOST;
106236e0bf6eSScott Long 				*(AAC_FSAStatus*)fib->data = ST_OK;
106336e0bf6eSScott Long 
106436e0bf6eSScott Long 				/* XXX Compute the Size field? */
106536e0bf6eSScott Long 				size = fib->Header.Size;
106636e0bf6eSScott Long 				if (size > sizeof(struct aac_fib)) {
106736e0bf6eSScott Long 					size = sizeof(struct aac_fib);
106836e0bf6eSScott Long 					fib->Header.Size = size;
106936e0bf6eSScott Long 				}
107036e0bf6eSScott Long 				/*
1071914da7d0SScott Long 				 * Since we did not generate this command, it
1072914da7d0SScott Long 				 * cannot go through the normal
1073914da7d0SScott Long 				 * enqueue->startio chain.
107436e0bf6eSScott Long 				 */
1075914da7d0SScott Long 				aac_enqueue_response(sc,
1076914da7d0SScott Long 						 AAC_ADAP_NORM_RESP_QUEUE,
1077914da7d0SScott Long 						 fib);
107836e0bf6eSScott Long 			}
107936e0bf6eSScott Long 		}
108036e0bf6eSScott Long 	}
108136e0bf6eSScott Long 	sc->aifflags &= ~AAC_AIFFLAGS_RUNNING;
1082bb6fe253SScott Long 	mtx_unlock(&sc->aac_io_lock);
108336e0bf6eSScott Long 	wakeup(sc->aac_dev);
108436e0bf6eSScott Long 
10853745c395SJulian Elischer 	kproc_exit(0);
108635863739SMike Smith }
108735863739SMike Smith 
1088914da7d0SScott Long /*
10899c3a7fceSScott Long  * Process completed commands.
109035863739SMike Smith  */
109135863739SMike Smith static void
10929c3a7fceSScott Long aac_complete(void *context, int pending)
109335863739SMike Smith {
10949c3a7fceSScott Long 	struct aac_softc *sc;
109535863739SMike Smith 	struct aac_command *cm;
109635863739SMike Smith 	struct aac_fib *fib;
109735863739SMike Smith 	u_int32_t fib_size;
109835863739SMike Smith 
10999c3a7fceSScott Long 	sc = (struct aac_softc *)context;
110031a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
11019c3a7fceSScott Long 
1102bb6fe253SScott Long 	mtx_lock(&sc->aac_io_lock);
1103ae543596SScott Long 
11049c3a7fceSScott Long 	/* pull completed commands off the queue */
110535863739SMike Smith 	for (;;) {
110635863739SMike Smith 		/* look for completed FIBs on our queue */
1107914da7d0SScott Long 		if (aac_dequeue_fib(sc, AAC_HOST_NORM_RESP_QUEUE, &fib_size,
1108914da7d0SScott Long 							&fib))
110935863739SMike Smith 			break;	/* nothing to do */
111035863739SMike Smith 
1111ecd1c51fSScott Long 		/* get the command, unmap and hand off for processing */
1112cb0d64b9SScott Long 		cm = sc->aac_commands + fib->Header.SenderData;
111335863739SMike Smith 		if (cm == NULL) {
111435863739SMike Smith 			AAC_PRINT_FIB(sc, fib);
11159c3a7fceSScott Long 			break;
11169c3a7fceSScott Long 		}
11170b94a66eSMike Smith 		aac_remove_busy(cm);
11187cb209f5SScott Long 
1119ecd1c51fSScott Long  		aac_unmap_command(cm);
112035863739SMike Smith 		cm->cm_flags |= AAC_CMD_COMPLETED;
112135863739SMike Smith 
112235863739SMike Smith 		/* is there a completion handler? */
112335863739SMike Smith 		if (cm->cm_complete != NULL) {
112435863739SMike Smith 			cm->cm_complete(cm);
112535863739SMike Smith 		} else {
112635863739SMike Smith 			/* assume that someone is sleeping on this command */
112735863739SMike Smith 			wakeup(cm);
112835863739SMike Smith 		}
112935863739SMike Smith 	}
11300b94a66eSMike Smith 
11310b94a66eSMike Smith 	/* see if we can start some more I/O */
1132cd481291SScott Long 	sc->flags &= ~AAC_QUEUE_FRZN;
11330b94a66eSMike Smith 	aac_startio(sc);
1134ae543596SScott Long 
1135bb6fe253SScott Long 	mtx_unlock(&sc->aac_io_lock);
113635863739SMike Smith }
113735863739SMike Smith 
1138914da7d0SScott Long /*
113935863739SMike Smith  * Handle a bio submitted from a disk device.
114035863739SMike Smith  */
114135863739SMike Smith void
114235863739SMike Smith aac_submit_bio(struct bio *bp)
114335863739SMike Smith {
1144914da7d0SScott Long 	struct aac_disk *ad;
1145914da7d0SScott Long 	struct aac_softc *sc;
114635863739SMike Smith 
11477540e65eSScott Long 	ad = (struct aac_disk *)bp->bio_disk->d_drv1;
1148914da7d0SScott Long 	sc = ad->ad_controller;
114931a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
1150914da7d0SScott Long 
115135863739SMike Smith 	/* queue the BIO and try to get some work done */
11520b94a66eSMike Smith 	aac_enqueue_bio(sc, bp);
115335863739SMike Smith 	aac_startio(sc);
115435863739SMike Smith }
115535863739SMike Smith 
1156914da7d0SScott Long /*
115735863739SMike Smith  * Get a bio and build a command to go with it.
115835863739SMike Smith  */
115935863739SMike Smith static int
116035863739SMike Smith aac_bio_command(struct aac_softc *sc, struct aac_command **cmp)
116135863739SMike Smith {
116235863739SMike Smith 	struct aac_command *cm;
116335863739SMike Smith 	struct aac_fib *fib;
116435863739SMike Smith 	struct aac_disk *ad;
116535863739SMike Smith 	struct bio *bp;
116635863739SMike Smith 
116731a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
116835863739SMike Smith 
116935863739SMike Smith 	/* get the resources we will need */
117035863739SMike Smith 	cm = NULL;
1171a32a982dSScott Long 	bp = NULL;
117235863739SMike Smith 	if (aac_alloc_command(sc, &cm))	/* get a command */
117335863739SMike Smith 		goto fail;
1174a32a982dSScott Long 	if ((bp = aac_dequeue_bio(sc)) == NULL)
1175a32a982dSScott Long 		goto fail;
117635863739SMike Smith 
117735863739SMike Smith 	/* fill out the command */
11780b94a66eSMike Smith 	cm->cm_data = (void *)bp->bio_data;
11790b94a66eSMike Smith 	cm->cm_datalen = bp->bio_bcount;
11800b94a66eSMike Smith 	cm->cm_complete = aac_bio_complete;
118135863739SMike Smith 	cm->cm_private = bp;
11822b3b0f17SScott Long 	cm->cm_timestamp = time_uptime;
118336e0bf6eSScott Long 	cm->cm_queue = AAC_ADAP_NORM_CMD_QUEUE;
118435863739SMike Smith 
118535863739SMike Smith 	/* build the FIB */
118635863739SMike Smith 	fib = cm->cm_fib;
1187b85f5808SScott Long 	fib->Header.Size = sizeof(struct aac_fib_header);
118835863739SMike Smith 	fib->Header.XferState =
118935863739SMike Smith 		AAC_FIBSTATE_HOSTOWNED   |
119035863739SMike Smith 		AAC_FIBSTATE_INITIALISED |
1191f30ac74cSScott Long 		AAC_FIBSTATE_EMPTY	 |
119235863739SMike Smith 		AAC_FIBSTATE_FROMHOST	 |
119335863739SMike Smith 		AAC_FIBSTATE_REXPECTED   |
1194f30ac74cSScott Long 		AAC_FIBSTATE_NORM	 |
1195f30ac74cSScott Long 		AAC_FIBSTATE_ASYNC	 |
1196f30ac74cSScott Long 		AAC_FIBSTATE_FAST_RESPONSE;
119735863739SMike Smith 
119835863739SMike Smith 	/* build the read/write request */
11997540e65eSScott Long 	ad = (struct aac_disk *)bp->bio_disk->d_drv1;
1200b85f5808SScott Long 
12017cb209f5SScott Long 	if (sc->flags & AAC_FLAGS_RAW_IO) {
12027cb209f5SScott Long 		struct aac_raw_io *raw;
12037cb209f5SScott Long 		raw = (struct aac_raw_io *)&fib->data[0];
12047cb209f5SScott Long 		fib->Header.Command = RawIo;
12057cb209f5SScott Long 		raw->BlockNumber = (u_int64_t)bp->bio_pblkno;
12067cb209f5SScott Long 		raw->ByteCount = bp->bio_bcount;
12077cb209f5SScott Long 		raw->ContainerId = ad->ad_container->co_mntobj.ObjectId;
12087cb209f5SScott Long 		raw->BpTotal = 0;
12097cb209f5SScott Long 		raw->BpComplete = 0;
12107cb209f5SScott Long 		fib->Header.Size += sizeof(struct aac_raw_io);
12117cb209f5SScott Long 		cm->cm_sgtable = (struct aac_sg_table *)&raw->SgMapRaw;
12127cb209f5SScott Long 		if (bp->bio_cmd == BIO_READ) {
12137cb209f5SScott Long 			raw->Flags = 1;
12147cb209f5SScott Long 			cm->cm_flags |= AAC_CMD_DATAIN;
12157cb209f5SScott Long 		} else {
12167cb209f5SScott Long 			raw->Flags = 0;
12177cb209f5SScott Long 			cm->cm_flags |= AAC_CMD_DATAOUT;
12187cb209f5SScott Long 		}
12197cb209f5SScott Long 	} else if ((sc->flags & AAC_FLAGS_SG_64BIT) == 0) {
1220b85f5808SScott Long 		fib->Header.Command = ContainerCommand;
12219e2e96d8SScott Long 		if (bp->bio_cmd == BIO_READ) {
1222b85f5808SScott Long 			struct aac_blockread *br;
122335863739SMike Smith 			br = (struct aac_blockread *)&fib->data[0];
122435863739SMike Smith 			br->Command = VM_CtBlockRead;
122535863739SMike Smith 			br->ContainerId = ad->ad_container->co_mntobj.ObjectId;
122635863739SMike Smith 			br->BlockNumber = bp->bio_pblkno;
122735863739SMike Smith 			br->ByteCount = bp->bio_bcount;
122835863739SMike Smith 			fib->Header.Size += sizeof(struct aac_blockread);
122935863739SMike Smith 			cm->cm_sgtable = &br->SgMap;
123035863739SMike Smith 			cm->cm_flags |= AAC_CMD_DATAIN;
123135863739SMike Smith 		} else {
1232b85f5808SScott Long 			struct aac_blockwrite *bw;
123335863739SMike Smith 			bw = (struct aac_blockwrite *)&fib->data[0];
123435863739SMike Smith 			bw->Command = VM_CtBlockWrite;
123535863739SMike Smith 			bw->ContainerId = ad->ad_container->co_mntobj.ObjectId;
123635863739SMike Smith 			bw->BlockNumber = bp->bio_pblkno;
123735863739SMike Smith 			bw->ByteCount = bp->bio_bcount;
1238b85f5808SScott Long 			bw->Stable = CUNSTABLE;
123935863739SMike Smith 			fib->Header.Size += sizeof(struct aac_blockwrite);
124035863739SMike Smith 			cm->cm_flags |= AAC_CMD_DATAOUT;
124135863739SMike Smith 			cm->cm_sgtable = &bw->SgMap;
124235863739SMike Smith 		}
1243b85f5808SScott Long 	} else {
1244b85f5808SScott Long 		fib->Header.Command = ContainerCommand64;
1245b85f5808SScott Long 		if (bp->bio_cmd == BIO_READ) {
1246b85f5808SScott Long 			struct aac_blockread64 *br;
1247b85f5808SScott Long 			br = (struct aac_blockread64 *)&fib->data[0];
1248b85f5808SScott Long 			br->Command = VM_CtHostRead64;
1249b85f5808SScott Long 			br->ContainerId = ad->ad_container->co_mntobj.ObjectId;
1250b85f5808SScott Long 			br->SectorCount = bp->bio_bcount / AAC_BLOCK_SIZE;
1251b85f5808SScott Long 			br->BlockNumber = bp->bio_pblkno;
1252b85f5808SScott Long 			br->Pad = 0;
1253b85f5808SScott Long 			br->Flags = 0;
1254b85f5808SScott Long 			fib->Header.Size += sizeof(struct aac_blockread64);
125554e2ebdfSEd Maste 			cm->cm_flags |= AAC_CMD_DATAIN;
1256eec256deSAlexander Kabaev 			cm->cm_sgtable = (struct aac_sg_table *)&br->SgMap64;
1257b85f5808SScott Long 		} else {
1258b85f5808SScott Long 			struct aac_blockwrite64 *bw;
1259b85f5808SScott Long 			bw = (struct aac_blockwrite64 *)&fib->data[0];
1260b85f5808SScott Long 			bw->Command = VM_CtHostWrite64;
1261b85f5808SScott Long 			bw->ContainerId = ad->ad_container->co_mntobj.ObjectId;
1262b85f5808SScott Long 			bw->SectorCount = bp->bio_bcount / AAC_BLOCK_SIZE;
1263b85f5808SScott Long 			bw->BlockNumber = bp->bio_pblkno;
1264b85f5808SScott Long 			bw->Pad = 0;
1265b85f5808SScott Long 			bw->Flags = 0;
1266b85f5808SScott Long 			fib->Header.Size += sizeof(struct aac_blockwrite64);
126754e2ebdfSEd Maste 			cm->cm_flags |= AAC_CMD_DATAOUT;
1268eec256deSAlexander Kabaev 			cm->cm_sgtable = (struct aac_sg_table *)&bw->SgMap64;
1269b85f5808SScott Long 		}
1270b85f5808SScott Long 	}
127135863739SMike Smith 
127235863739SMike Smith 	*cmp = cm;
127335863739SMike Smith 	return(0);
127435863739SMike Smith 
127535863739SMike Smith fail:
12767cb209f5SScott Long 	if (bp != NULL)
12777cb209f5SScott Long 		aac_enqueue_bio(sc, bp);
127835863739SMike Smith 	if (cm != NULL)
127935863739SMike Smith 		aac_release_command(cm);
128035863739SMike Smith 	return(ENOMEM);
128135863739SMike Smith }
128235863739SMike Smith 
1283914da7d0SScott Long /*
128435863739SMike Smith  * Handle a bio-instigated command that has been completed.
128535863739SMike Smith  */
128635863739SMike Smith static void
128735863739SMike Smith aac_bio_complete(struct aac_command *cm)
128835863739SMike Smith {
128935863739SMike Smith 	struct aac_blockread_response *brr;
129035863739SMike Smith 	struct aac_blockwrite_response *bwr;
129135863739SMike Smith 	struct bio *bp;
129235863739SMike Smith 	AAC_FSAStatus status;
129335863739SMike Smith 
129435863739SMike Smith 	/* fetch relevant status and then release the command */
129535863739SMike Smith 	bp = (struct bio *)cm->cm_private;
12969e2e96d8SScott Long 	if (bp->bio_cmd == BIO_READ) {
129735863739SMike Smith 		brr = (struct aac_blockread_response *)&cm->cm_fib->data[0];
129835863739SMike Smith 		status = brr->Status;
129935863739SMike Smith 	} else {
130035863739SMike Smith 		bwr = (struct aac_blockwrite_response *)&cm->cm_fib->data[0];
130135863739SMike Smith 		status = bwr->Status;
130235863739SMike Smith 	}
130335863739SMike Smith 	aac_release_command(cm);
130435863739SMike Smith 
130535863739SMike Smith 	/* fix up the bio based on status */
130635863739SMike Smith 	if (status == ST_OK) {
130735863739SMike Smith 		bp->bio_resid = 0;
130835863739SMike Smith 	} else {
130935863739SMike Smith 		bp->bio_error = EIO;
131035863739SMike Smith 		bp->bio_flags |= BIO_ERROR;
13110b94a66eSMike Smith 		/* pass an error string out to the disk layer */
1312914da7d0SScott Long 		bp->bio_driver1 = aac_describe_code(aac_command_status_table,
1313914da7d0SScott Long 						    status);
131435863739SMike Smith 	}
13150b94a66eSMike Smith 	aac_biodone(bp);
131635863739SMike Smith }
131735863739SMike Smith 
1318914da7d0SScott Long /*
131935863739SMike Smith  * Submit a command to the controller, return when it completes.
1320b3457b51SScott Long  * XXX This is very dangerous!  If the card has gone out to lunch, we could
1321b3457b51SScott Long  *     be stuck here forever.  At the same time, signals are not caught
1322d8a0a473SScott Long  *     because there is a risk that a signal could wakeup the sleep before
1323d8a0a473SScott Long  *     the card has a chance to complete the command.  Since there is no way
1324d8a0a473SScott Long  *     to cancel a command that is in progress, we can't protect against the
1325d8a0a473SScott Long  *     card completing a command late and spamming the command and data
1326d8a0a473SScott Long  *     memory.  So, we are held hostage until the command completes.
132735863739SMike Smith  */
132835863739SMike Smith static int
1329d8a0a473SScott Long aac_wait_command(struct aac_command *cm)
133035863739SMike Smith {
1331ae543596SScott Long 	struct aac_softc *sc;
1332d8a0a473SScott Long 	int error;
133335863739SMike Smith 
1334ae543596SScott Long 	sc = cm->cm_sc;
133531a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
1336ae543596SScott Long 
133735863739SMike Smith 	/* Put the command on the ready queue and get things going */
133836e0bf6eSScott Long 	cm->cm_queue = AAC_ADAP_NORM_CMD_QUEUE;
133935863739SMike Smith 	aac_enqueue_ready(cm);
1340ae543596SScott Long 	aac_startio(sc);
1341ae543596SScott Long 	error = msleep(cm, &sc->aac_io_lock, PRIBIO, "aacwait", 0);
134235863739SMike Smith 	return(error);
134335863739SMike Smith }
134435863739SMike Smith 
1345914da7d0SScott Long /*
1346914da7d0SScott Long  *Command Buffer Management
1347914da7d0SScott Long  */
134835863739SMike Smith 
1349914da7d0SScott Long /*
135035863739SMike Smith  * Allocate a command.
135135863739SMike Smith  */
1352fe3cb0e1SScott Long int
135335863739SMike Smith aac_alloc_command(struct aac_softc *sc, struct aac_command **cmp)
135435863739SMike Smith {
135535863739SMike Smith 	struct aac_command *cm;
135635863739SMike Smith 
135731a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
135835863739SMike Smith 
1359ffb37f33SScott Long 	if ((cm = aac_dequeue_free(sc)) == NULL) {
1360b85f5808SScott Long 		if (sc->total_fibs < sc->aac_max_fibs) {
1361ae543596SScott Long 			sc->aifflags |= AAC_AIFFLAGS_ALLOCFIBS;
1362ae543596SScott Long 			wakeup(sc->aifthread);
1363b85f5808SScott Long 		}
1364ae543596SScott Long 		return (EBUSY);
1365ffb37f33SScott Long 	}
136635863739SMike Smith 
13670b94a66eSMike Smith 	*cmp = cm;
13680b94a66eSMike Smith 	return(0);
13690b94a66eSMike Smith }
13700b94a66eSMike Smith 
1371914da7d0SScott Long /*
13720b94a66eSMike Smith  * Release a command back to the freelist.
13730b94a66eSMike Smith  */
1374fe3cb0e1SScott Long void
13750b94a66eSMike Smith aac_release_command(struct aac_command *cm)
13760b94a66eSMike Smith {
13777cb209f5SScott Long 	struct aac_event *event;
13787cb209f5SScott Long 	struct aac_softc *sc;
13797cb209f5SScott Long 
138031a0399eSEd Maste 	sc = cm->cm_sc;
138131a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
13820b94a66eSMike Smith 
13834109ba51SEd Maste 	/* (re)initialize the command/FIB */
138435863739SMike Smith 	cm->cm_sgtable = NULL;
138535863739SMike Smith 	cm->cm_flags = 0;
138635863739SMike Smith 	cm->cm_complete = NULL;
138735863739SMike Smith 	cm->cm_private = NULL;
138835863739SMike Smith 	cm->cm_fib->Header.XferState = AAC_FIBSTATE_EMPTY;
138935863739SMike Smith 	cm->cm_fib->Header.StructType = AAC_FIBTYPE_TFIB;
139035863739SMike Smith 	cm->cm_fib->Header.Flags = 0;
13917cb209f5SScott Long 	cm->cm_fib->Header.SenderSize = cm->cm_sc->aac_max_fib_size;
139235863739SMike Smith 
139335863739SMike Smith 	/*
139435863739SMike Smith 	 * These are duplicated in aac_start to cover the case where an
139535863739SMike Smith 	 * intermediate stage may have destroyed them.  They're left
13964109ba51SEd Maste 	 * initialized here for debugging purposes only.
139735863739SMike Smith 	 */
1398f30ac74cSScott Long 	cm->cm_fib->Header.ReceiverFibAddress = (u_int32_t)cm->cm_fibphys;
1399f30ac74cSScott Long 	cm->cm_fib->Header.SenderData = 0;
140035863739SMike Smith 
140135863739SMike Smith 	aac_enqueue_free(cm);
14027cb209f5SScott Long 
1403eb5cbaa0SEd Maste 	/*
1404eb5cbaa0SEd Maste 	 * Dequeue all events so that there's no risk of events getting
1405eb5cbaa0SEd Maste 	 * stranded.
1406eb5cbaa0SEd Maste 	 */
1407eb5cbaa0SEd Maste 	while ((event = TAILQ_FIRST(&sc->aac_ev_cmfree)) != NULL) {
14087cb209f5SScott Long 		TAILQ_REMOVE(&sc->aac_ev_cmfree, event, ev_links);
14097cb209f5SScott Long 		event->ev_callback(sc, event, event->ev_arg);
14107cb209f5SScott Long 	}
141135863739SMike Smith }
141235863739SMike Smith 
1413914da7d0SScott Long /*
14140b94a66eSMike Smith  * Map helper for command/FIB allocation.
141535863739SMike Smith  */
141635863739SMike Smith static void
14170b94a66eSMike Smith aac_map_command_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error)
141835863739SMike Smith {
14197cb209f5SScott Long 	uint64_t	*fibphys;
1420914da7d0SScott Long 
14217cb209f5SScott Long 	fibphys = (uint64_t *)arg;
142235863739SMike Smith 
1423ffb37f33SScott Long 	*fibphys = segs[0].ds_addr;
142435863739SMike Smith }
142535863739SMike Smith 
1426914da7d0SScott Long /*
14274109ba51SEd Maste  * Allocate and initialize commands/FIBs for this adapter.
142835863739SMike Smith  */
14290b94a66eSMike Smith static int
14300b94a66eSMike Smith aac_alloc_commands(struct aac_softc *sc)
143135863739SMike Smith {
143235863739SMike Smith 	struct aac_command *cm;
1433ffb37f33SScott Long 	struct aac_fibmap *fm;
14347cb209f5SScott Long 	uint64_t fibphys;
1435ffb37f33SScott Long 	int i, error;
143635863739SMike Smith 
143731a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
143835863739SMike Smith 
14397cb209f5SScott Long 	if (sc->total_fibs + sc->aac_max_fibs_alloc > sc->aac_max_fibs)
1440ffb37f33SScott Long 		return (ENOMEM);
1441ffb37f33SScott Long 
14428480cc63SScott Long 	fm = malloc(sizeof(struct aac_fibmap), M_AACBUF, M_NOWAIT|M_ZERO);
1443a6d35632SScott Long 	if (fm == NULL)
1444a6d35632SScott Long 		return (ENOMEM);
1445ffb37f33SScott Long 
14460b94a66eSMike Smith 	/* allocate the FIBs in DMAable memory and load them */
1447ffb37f33SScott Long 	if (bus_dmamem_alloc(sc->aac_fib_dmat, (void **)&fm->aac_fibs,
1448ffb37f33SScott Long 			     BUS_DMA_NOWAIT, &fm->aac_fibmap)) {
144970545d1aSScott Long 		device_printf(sc->aac_dev,
145070545d1aSScott Long 			      "Not enough contiguous memory available.\n");
14518480cc63SScott Long 		free(fm, M_AACBUF);
14520b94a66eSMike Smith 		return (ENOMEM);
145335863739SMike Smith 	}
1454128aa5a0SScott Long 
1455cd481291SScott Long 	/* Ignore errors since this doesn't bounce */
1456cd481291SScott Long 	(void)bus_dmamap_load(sc->aac_fib_dmat, fm->aac_fibmap, fm->aac_fibs,
14577cb209f5SScott Long 			      sc->aac_max_fibs_alloc * sc->aac_max_fib_size,
1458ffb37f33SScott Long 			      aac_map_command_helper, &fibphys, 0);
1459128aa5a0SScott Long 
14604109ba51SEd Maste 	/* initialize constant fields in the command structure */
14617cb209f5SScott Long 	bzero(fm->aac_fibs, sc->aac_max_fibs_alloc * sc->aac_max_fib_size);
14627cb209f5SScott Long 	for (i = 0; i < sc->aac_max_fibs_alloc; i++) {
14638480cc63SScott Long 		cm = sc->aac_commands + sc->total_fibs;
1464ffb37f33SScott Long 		fm->aac_commands = cm;
146535863739SMike Smith 		cm->cm_sc = sc;
14667cb209f5SScott Long 		cm->cm_fib = (struct aac_fib *)
14677cb209f5SScott Long 			((u_int8_t *)fm->aac_fibs + i*sc->aac_max_fib_size);
14687cb209f5SScott Long 		cm->cm_fibphys = fibphys + i*sc->aac_max_fib_size;
1469cb0d64b9SScott Long 		cm->cm_index = sc->total_fibs;
147035863739SMike Smith 
1471ffb37f33SScott Long 		if ((error = bus_dmamap_create(sc->aac_buffer_dmat, 0,
147293cfca22SScott Long 					       &cm->cm_datamap)) != 0)
14738480cc63SScott Long 			break;
147493cfca22SScott Long 		mtx_lock(&sc->aac_io_lock);
147593cfca22SScott Long 		aac_release_command(cm);
14768480cc63SScott Long 		sc->total_fibs++;
147793cfca22SScott Long 		mtx_unlock(&sc->aac_io_lock);
147835863739SMike Smith 	}
1479ffb37f33SScott Long 
14808480cc63SScott Long 	if (i > 0) {
148193cfca22SScott Long 		mtx_lock(&sc->aac_io_lock);
1482ffb37f33SScott Long 		TAILQ_INSERT_TAIL(&sc->aac_fibmap_tqh, fm, fm_link);
148331a0399eSEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_COMM_B, "total_fibs= %d\n", sc->total_fibs);
1484bb6fe253SScott Long 		mtx_unlock(&sc->aac_io_lock);
14850b94a66eSMike Smith 		return (0);
148635863739SMike Smith 	}
148735863739SMike Smith 
14888480cc63SScott Long 	bus_dmamap_unload(sc->aac_fib_dmat, fm->aac_fibmap);
14898480cc63SScott Long 	bus_dmamem_free(sc->aac_fib_dmat, fm->aac_fibs, fm->aac_fibmap);
14908480cc63SScott Long 	free(fm, M_AACBUF);
14918480cc63SScott Long 	return (ENOMEM);
14928480cc63SScott Long }
14938480cc63SScott Long 
1494914da7d0SScott Long /*
14950b94a66eSMike Smith  * Free FIBs owned by this adapter.
149635863739SMike Smith  */
149735863739SMike Smith static void
14988480cc63SScott Long aac_free_commands(struct aac_softc *sc)
149935863739SMike Smith {
15008480cc63SScott Long 	struct aac_fibmap *fm;
1501ffb37f33SScott Long 	struct aac_command *cm;
150235863739SMike Smith 	int i;
150335863739SMike Smith 
150431a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
150535863739SMike Smith 
15068480cc63SScott Long 	while ((fm = TAILQ_FIRST(&sc->aac_fibmap_tqh)) != NULL) {
15078480cc63SScott Long 
15088480cc63SScott Long 		TAILQ_REMOVE(&sc->aac_fibmap_tqh, fm, fm_link);
15098480cc63SScott Long 		/*
15108480cc63SScott Long 		 * We check against total_fibs to handle partially
15118480cc63SScott Long 		 * allocated blocks.
15128480cc63SScott Long 		 */
15137cb209f5SScott Long 		for (i = 0; i < sc->aac_max_fibs_alloc && sc->total_fibs--; i++) {
1514ffb37f33SScott Long 			cm = fm->aac_commands + i;
1515ffb37f33SScott Long 			bus_dmamap_destroy(sc->aac_buffer_dmat, cm->cm_datamap);
1516ffb37f33SScott Long 		}
1517ffb37f33SScott Long 		bus_dmamap_unload(sc->aac_fib_dmat, fm->aac_fibmap);
1518ffb37f33SScott Long 		bus_dmamem_free(sc->aac_fib_dmat, fm->aac_fibs, fm->aac_fibmap);
15198480cc63SScott Long 		free(fm, M_AACBUF);
15208480cc63SScott Long 	}
152135863739SMike Smith }
152235863739SMike Smith 
1523914da7d0SScott Long /*
152435863739SMike Smith  * Command-mapping helper function - populate this command's s/g table.
152535863739SMike Smith  */
152635863739SMike Smith static void
152735863739SMike Smith aac_map_command_sg(void *arg, bus_dma_segment_t *segs, int nseg, int error)
152835863739SMike Smith {
1529cd481291SScott Long 	struct aac_softc *sc;
1530914da7d0SScott Long 	struct aac_command *cm;
1531914da7d0SScott Long 	struct aac_fib *fib;
153235863739SMike Smith 	int i;
153335863739SMike Smith 
1534914da7d0SScott Long 	cm = (struct aac_command *)arg;
1535cd481291SScott Long 	sc = cm->cm_sc;
1536914da7d0SScott Long 	fib = cm->cm_fib;
153731a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
1538914da7d0SScott Long 
153935863739SMike Smith 	/* copy into the FIB */
1540b85f5808SScott Long 	if (cm->cm_sgtable != NULL) {
15417cb209f5SScott Long 		if (fib->Header.Command == RawIo) {
15427cb209f5SScott Long 			struct aac_sg_tableraw *sg;
15437cb209f5SScott Long 			sg = (struct aac_sg_tableraw *)cm->cm_sgtable;
15447cb209f5SScott Long 			sg->SgCount = nseg;
15457cb209f5SScott Long 			for (i = 0; i < nseg; i++) {
15467cb209f5SScott Long 				sg->SgEntryRaw[i].SgAddress = segs[i].ds_addr;
15477cb209f5SScott Long 				sg->SgEntryRaw[i].SgByteCount = segs[i].ds_len;
15487cb209f5SScott Long 				sg->SgEntryRaw[i].Next = 0;
15497cb209f5SScott Long 				sg->SgEntryRaw[i].Prev = 0;
15507cb209f5SScott Long 				sg->SgEntryRaw[i].Flags = 0;
15517cb209f5SScott Long 			}
15527cb209f5SScott Long 			/* update the FIB size for the s/g count */
15537cb209f5SScott Long 			fib->Header.Size += nseg*sizeof(struct aac_sg_entryraw);
15547cb209f5SScott Long 		} else if ((cm->cm_sc->flags & AAC_FLAGS_SG_64BIT) == 0) {
1555b85f5808SScott Long 			struct aac_sg_table *sg;
1556b85f5808SScott Long 			sg = cm->cm_sgtable;
155735863739SMike Smith 			sg->SgCount = nseg;
155835863739SMike Smith 			for (i = 0; i < nseg; i++) {
155935863739SMike Smith 				sg->SgEntry[i].SgAddress = segs[i].ds_addr;
156035863739SMike Smith 				sg->SgEntry[i].SgByteCount = segs[i].ds_len;
156135863739SMike Smith 			}
156235863739SMike Smith 			/* update the FIB size for the s/g count */
156335863739SMike Smith 			fib->Header.Size += nseg*sizeof(struct aac_sg_entry);
1564b85f5808SScott Long 		} else {
1565b85f5808SScott Long 			struct aac_sg_table64 *sg;
1566b85f5808SScott Long 			sg = (struct aac_sg_table64 *)cm->cm_sgtable;
1567b85f5808SScott Long 			sg->SgCount = nseg;
1568b85f5808SScott Long 			for (i = 0; i < nseg; i++) {
1569b85f5808SScott Long 				sg->SgEntry64[i].SgAddress = segs[i].ds_addr;
1570b85f5808SScott Long 				sg->SgEntry64[i].SgByteCount = segs[i].ds_len;
157135863739SMike Smith 			}
1572b85f5808SScott Long 			/* update the FIB size for the s/g count */
1573b85f5808SScott Long 			fib->Header.Size += nseg*sizeof(struct aac_sg_entry64);
1574b85f5808SScott Long 		}
1575b85f5808SScott Long 	}
157635863739SMike Smith 
1577cd481291SScott Long 	/* Fix up the address values in the FIB.  Use the command array index
1578cd481291SScott Long 	 * instead of a pointer since these fields are only 32 bits.  Shift
15797cb209f5SScott Long 	 * the SenderFibAddress over to make room for the fast response bit
15807cb209f5SScott Long 	 * and for the AIF bit
158135863739SMike Smith 	 */
15827cb209f5SScott Long 	cm->cm_fib->Header.SenderFibAddress = (cm->cm_index << 2);
15837cb209f5SScott Long 	cm->cm_fib->Header.ReceiverFibAddress = (u_int32_t)cm->cm_fibphys;
158435863739SMike Smith 
1585cd481291SScott Long 	/* save a pointer to the command for speedy reverse-lookup */
1586cd481291SScott Long 	cm->cm_fib->Header.SenderData = cm->cm_index;
158735863739SMike Smith 
158835863739SMike Smith 	if (cm->cm_flags & AAC_CMD_DATAIN)
1589c6eafcf2SScott Long 		bus_dmamap_sync(sc->aac_buffer_dmat, cm->cm_datamap,
1590c6eafcf2SScott Long 				BUS_DMASYNC_PREREAD);
159135863739SMike Smith 	if (cm->cm_flags & AAC_CMD_DATAOUT)
1592c6eafcf2SScott Long 		bus_dmamap_sync(sc->aac_buffer_dmat, cm->cm_datamap,
1593c6eafcf2SScott Long 				BUS_DMASYNC_PREWRITE);
159435863739SMike Smith 	cm->cm_flags |= AAC_CMD_MAPPED;
1595cd481291SScott Long 
15967cb209f5SScott Long 	if (sc->flags & AAC_FLAGS_NEW_COMM) {
15977cb209f5SScott Long 		int count = 10000000L;
15987cb209f5SScott Long 		while (AAC_SEND_COMMAND(sc, cm) != 0) {
15997cb209f5SScott Long 			if (--count == 0) {
16007cb209f5SScott Long 				aac_unmap_command(cm);
16017cb209f5SScott Long 				sc->flags |= AAC_QUEUE_FRZN;
16027cb209f5SScott Long 				aac_requeue_ready(cm);
16037cb209f5SScott Long 			}
16047cb209f5SScott Long 			DELAY(5);			/* wait 5 usec. */
16057cb209f5SScott Long 		}
16067cb209f5SScott Long 	} else {
1607397fa34fSScott Long 		/* Put the FIB on the outbound queue */
16084102d44bSScott Long 		if (aac_enqueue_fib(sc, cm->cm_queue, cm) == EBUSY) {
16094102d44bSScott Long 			aac_unmap_command(cm);
1610397fa34fSScott Long 			sc->flags |= AAC_QUEUE_FRZN;
1611cd481291SScott Long 			aac_requeue_ready(cm);
16124102d44bSScott Long 		}
16137cb209f5SScott Long 	}
1614cd481291SScott Long 
1615cd481291SScott Long 	return;
161635863739SMike Smith }
161735863739SMike Smith 
1618914da7d0SScott Long /*
161935863739SMike Smith  * Unmap a command from controller-visible space.
162035863739SMike Smith  */
162135863739SMike Smith static void
162235863739SMike Smith aac_unmap_command(struct aac_command *cm)
162335863739SMike Smith {
1624914da7d0SScott Long 	struct aac_softc *sc;
162535863739SMike Smith 
1626914da7d0SScott Long 	sc = cm->cm_sc;
162731a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
1628914da7d0SScott Long 
162935863739SMike Smith 	if (!(cm->cm_flags & AAC_CMD_MAPPED))
163035863739SMike Smith 		return;
163135863739SMike Smith 
163235863739SMike Smith 	if (cm->cm_datalen != 0) {
163335863739SMike Smith 		if (cm->cm_flags & AAC_CMD_DATAIN)
1634c6eafcf2SScott Long 			bus_dmamap_sync(sc->aac_buffer_dmat, cm->cm_datamap,
1635c6eafcf2SScott Long 					BUS_DMASYNC_POSTREAD);
163635863739SMike Smith 		if (cm->cm_flags & AAC_CMD_DATAOUT)
1637c6eafcf2SScott Long 			bus_dmamap_sync(sc->aac_buffer_dmat, cm->cm_datamap,
1638c6eafcf2SScott Long 					BUS_DMASYNC_POSTWRITE);
163935863739SMike Smith 
164035863739SMike Smith 		bus_dmamap_unload(sc->aac_buffer_dmat, cm->cm_datamap);
164135863739SMike Smith 	}
164235863739SMike Smith 	cm->cm_flags &= ~AAC_CMD_MAPPED;
164335863739SMike Smith }
164435863739SMike Smith 
1645914da7d0SScott Long /*
1646914da7d0SScott Long  * Hardware Interface
1647914da7d0SScott Long  */
164835863739SMike Smith 
1649914da7d0SScott Long /*
16504109ba51SEd Maste  * Initialize the adapter.
165135863739SMike Smith  */
165235863739SMike Smith static void
165335863739SMike Smith aac_common_map(void *arg, bus_dma_segment_t *segs, int nseg, int error)
165435863739SMike Smith {
1655914da7d0SScott Long 	struct aac_softc *sc;
165635863739SMike Smith 
1657914da7d0SScott Long 	sc = (struct aac_softc *)arg;
165831a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
1659914da7d0SScott Long 
166035863739SMike Smith 	sc->aac_common_busaddr = segs[0].ds_addr;
166135863739SMike Smith }
166235863739SMike Smith 
1663a6d35632SScott Long static int
1664a6d35632SScott Long aac_check_firmware(struct aac_softc *sc)
1665a6d35632SScott Long {
166604f4d586SEd Maste 	u_int32_t code, major, minor, options = 0, atu_size = 0;
1667a441b3fcSScott Long 	int status;
166804f4d586SEd Maste 	time_t then;
1669a6d35632SScott Long 
167031a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
167104f4d586SEd Maste 	/*
167204f4d586SEd Maste 	 * Wait for the adapter to come ready.
167304f4d586SEd Maste 	 */
167404f4d586SEd Maste 	then = time_uptime;
167504f4d586SEd Maste 	do {
167604f4d586SEd Maste 		code = AAC_GET_FWSTATUS(sc);
167704f4d586SEd Maste 		if (code & AAC_SELF_TEST_FAILED) {
167804f4d586SEd Maste 			device_printf(sc->aac_dev, "FATAL: selftest failed\n");
167904f4d586SEd Maste 			return(ENXIO);
168004f4d586SEd Maste 		}
168104f4d586SEd Maste 		if (code & AAC_KERNEL_PANIC) {
168204f4d586SEd Maste 			device_printf(sc->aac_dev,
1683a620bad0SEd Maste 				      "FATAL: controller kernel panic");
168404f4d586SEd Maste 			return(ENXIO);
168504f4d586SEd Maste 		}
168604f4d586SEd Maste 		if (time_uptime > (then + AAC_BOOT_TIMEOUT)) {
168704f4d586SEd Maste 			device_printf(sc->aac_dev,
168804f4d586SEd Maste 				      "FATAL: controller not coming ready, "
168904f4d586SEd Maste 					   "status %x\n", code);
169004f4d586SEd Maste 			return(ENXIO);
169104f4d586SEd Maste 		}
169204f4d586SEd Maste 	} while (!(code & AAC_UP_AND_RUNNING));
1693a6d35632SScott Long 
1694fe94b852SScott Long 	/*
1695fe94b852SScott Long 	 * Retrieve the firmware version numbers.  Dell PERC2/QC cards with
1696fe94b852SScott Long 	 * firmware version 1.x are not compatible with this driver.
1697fe94b852SScott Long 	 */
1698a6d35632SScott Long 	if (sc->flags & AAC_FLAGS_PERC2QC) {
1699fe94b852SScott Long 		if (aac_sync_command(sc, AAC_MONKER_GETKERNVER, 0, 0, 0, 0,
1700fe94b852SScott Long 				     NULL)) {
1701fe94b852SScott Long 			device_printf(sc->aac_dev,
1702fe94b852SScott Long 				      "Error reading firmware version\n");
1703fe94b852SScott Long 			return (EIO);
1704fe94b852SScott Long 		}
1705fe94b852SScott Long 
1706fe94b852SScott Long 		/* These numbers are stored as ASCII! */
1707a6d35632SScott Long 		major = (AAC_GET_MAILBOX(sc, 1) & 0xff) - 0x30;
1708a6d35632SScott Long 		minor = (AAC_GET_MAILBOX(sc, 2) & 0xff) - 0x30;
1709fe94b852SScott Long 		if (major == 1) {
1710fe94b852SScott Long 			device_printf(sc->aac_dev,
1711fe94b852SScott Long 			    "Firmware version %d.%d is not supported.\n",
1712fe94b852SScott Long 			    major, minor);
1713fe94b852SScott Long 			return (EINVAL);
1714fe94b852SScott Long 		}
1715fe94b852SScott Long 	}
1716fe94b852SScott Long 
1717a6d35632SScott Long 	/*
1718a6d35632SScott Long 	 * Retrieve the capabilities/supported options word so we know what
1719a441b3fcSScott Long 	 * work-arounds to enable.  Some firmware revs don't support this
1720a441b3fcSScott Long 	 * command.
1721a6d35632SScott Long 	 */
1722a441b3fcSScott Long 	if (aac_sync_command(sc, AAC_MONKER_GETINFO, 0, 0, 0, 0, &status)) {
1723a441b3fcSScott Long 		if (status != AAC_SRB_STS_INVALID_REQUEST) {
1724a441b3fcSScott Long 			device_printf(sc->aac_dev,
1725a441b3fcSScott Long 			     "RequestAdapterInfo failed\n");
1726a6d35632SScott Long 			return (EIO);
1727a6d35632SScott Long 		}
1728a441b3fcSScott Long 	} else {
1729a6d35632SScott Long 		options = AAC_GET_MAILBOX(sc, 1);
17307cb209f5SScott Long 		atu_size = AAC_GET_MAILBOX(sc, 2);
1731a6d35632SScott Long 		sc->supported_options = options;
1732a6d35632SScott Long 
1733a6d35632SScott Long 		if ((options & AAC_SUPPORTED_4GB_WINDOW) != 0 &&
1734a6d35632SScott Long 		    (sc->flags & AAC_FLAGS_NO4GB) == 0)
1735a6d35632SScott Long 			sc->flags |= AAC_FLAGS_4GB_WINDOW;
1736a6d35632SScott Long 		if (options & AAC_SUPPORTED_NONDASD)
1737a6d35632SScott Long 			sc->flags |= AAC_FLAGS_ENABLE_CAM;
1738cd481291SScott Long 		if ((options & AAC_SUPPORTED_SGMAP_HOST64) != 0
1739cd481291SScott Long 		     && (sizeof(bus_addr_t) > 4)) {
1740a441b3fcSScott Long 			device_printf(sc->aac_dev,
1741a441b3fcSScott Long 			    "Enabling 64-bit address support\n");
1742a6d35632SScott Long 			sc->flags |= AAC_FLAGS_SG_64BIT;
1743a6d35632SScott Long 		}
1744a441b3fcSScott Long 		if ((options & AAC_SUPPORTED_NEW_COMM)
1745a441b3fcSScott Long 		 && sc->aac_if.aif_send_command)
17467cb209f5SScott Long 			sc->flags |= AAC_FLAGS_NEW_COMM;
17477cb209f5SScott Long 		if (options & AAC_SUPPORTED_64BIT_ARRAYSIZE)
17487cb209f5SScott Long 			sc->flags |= AAC_FLAGS_ARRAY_64BIT;
1749a441b3fcSScott Long 	}
1750a6d35632SScott Long 
1751a6d35632SScott Long 	/* Check for broken hardware that does a lower number of commands */
17527cb209f5SScott Long 	sc->aac_max_fibs = (sc->flags & AAC_FLAGS_256FIBS ? 256:512);
17537cb209f5SScott Long 
17547cb209f5SScott Long 	/* Remap mem. resource, if required */
17557cb209f5SScott Long 	if ((sc->flags & AAC_FLAGS_NEW_COMM) &&
17567cb209f5SScott Long 		atu_size > rman_get_size(sc->aac_regs_resource)) {
17577cb209f5SScott Long 		bus_release_resource(
17587cb209f5SScott Long 			sc->aac_dev, SYS_RES_MEMORY,
17597cb209f5SScott Long 			sc->aac_regs_rid, sc->aac_regs_resource);
17607cb209f5SScott Long 		sc->aac_regs_resource = bus_alloc_resource(
17617cb209f5SScott Long 			sc->aac_dev, SYS_RES_MEMORY, &sc->aac_regs_rid,
17627cb209f5SScott Long 			0ul, ~0ul, atu_size, RF_ACTIVE);
17637cb209f5SScott Long 		if (sc->aac_regs_resource == NULL) {
17647cb209f5SScott Long 			sc->aac_regs_resource = bus_alloc_resource_any(
17657cb209f5SScott Long 				sc->aac_dev, SYS_RES_MEMORY,
17667cb209f5SScott Long 				&sc->aac_regs_rid, RF_ACTIVE);
17677cb209f5SScott Long 			if (sc->aac_regs_resource == NULL) {
17687cb209f5SScott Long 				device_printf(sc->aac_dev,
17697cb209f5SScott Long 				    "couldn't allocate register window\n");
17707cb209f5SScott Long 				return (ENXIO);
17717cb209f5SScott Long 			}
17727cb209f5SScott Long 			sc->flags &= ~AAC_FLAGS_NEW_COMM;
17737cb209f5SScott Long 		}
17747cb209f5SScott Long 		sc->aac_btag = rman_get_bustag(sc->aac_regs_resource);
17757cb209f5SScott Long 		sc->aac_bhandle = rman_get_bushandle(sc->aac_regs_resource);
17767cb209f5SScott Long 	}
17777cb209f5SScott Long 
17787cb209f5SScott Long 	/* Read preferred settings */
17797cb209f5SScott Long 	sc->aac_max_fib_size = sizeof(struct aac_fib);
17807cb209f5SScott Long 	sc->aac_max_sectors = 128;				/* 64KB */
17817cb209f5SScott Long 	if (sc->flags & AAC_FLAGS_SG_64BIT)
1782a441b3fcSScott Long 		sc->aac_sg_tablesize = (AAC_FIB_DATASIZE
17837e7a458eSEd Maste 		 - sizeof(struct aac_blockwrite64))
17847e7a458eSEd Maste 		 / sizeof(struct aac_sg_entry64);
1785a6d35632SScott Long 	else
1786a441b3fcSScott Long 		sc->aac_sg_tablesize = (AAC_FIB_DATASIZE
17877e7a458eSEd Maste 		 - sizeof(struct aac_blockwrite))
17887e7a458eSEd Maste 		 / sizeof(struct aac_sg_entry);
1789a441b3fcSScott Long 
17907cb209f5SScott Long 	if (!aac_sync_command(sc, AAC_MONKER_GETCOMMPREF, 0, 0, 0, 0, NULL)) {
17917cb209f5SScott Long 		options = AAC_GET_MAILBOX(sc, 1);
17927cb209f5SScott Long 		sc->aac_max_fib_size = (options & 0xFFFF);
17937cb209f5SScott Long 		sc->aac_max_sectors = (options >> 16) << 1;
17947cb209f5SScott Long 		options = AAC_GET_MAILBOX(sc, 2);
17957cb209f5SScott Long 		sc->aac_sg_tablesize = (options >> 16);
17967cb209f5SScott Long 		options = AAC_GET_MAILBOX(sc, 3);
17977cb209f5SScott Long 		sc->aac_max_fibs = (options & 0xFFFF);
17987cb209f5SScott Long 	}
17997cb209f5SScott Long 	if (sc->aac_max_fib_size > PAGE_SIZE)
18007cb209f5SScott Long 		sc->aac_max_fib_size = PAGE_SIZE;
18017cb209f5SScott Long 	sc->aac_max_fibs_alloc = PAGE_SIZE / sc->aac_max_fib_size;
1802a6d35632SScott Long 
1803f355c0e0SEd Maste 	if (sc->aac_max_fib_size > sizeof(struct aac_fib)) {
1804f355c0e0SEd Maste 		sc->flags |= AAC_FLAGS_RAW_IO;
1805f355c0e0SEd Maste 		device_printf(sc->aac_dev, "Enable Raw I/O\n");
1806f355c0e0SEd Maste 	}
1807523da39bSEd Maste 	if ((sc->flags & AAC_FLAGS_RAW_IO) &&
1808523da39bSEd Maste 	    (sc->flags & AAC_FLAGS_ARRAY_64BIT)) {
1809523da39bSEd Maste 		sc->flags |= AAC_FLAGS_LBA_64BIT;
1810523da39bSEd Maste 		device_printf(sc->aac_dev, "Enable 64-bit array\n");
1811523da39bSEd Maste 	}
1812f355c0e0SEd Maste 
1813fe94b852SScott Long 	return (0);
1814fe94b852SScott Long }
1815fe94b852SScott Long 
181635863739SMike Smith static int
181735863739SMike Smith aac_init(struct aac_softc *sc)
181835863739SMike Smith {
181935863739SMike Smith 	struct aac_adapter_init	*ip;
182004f4d586SEd Maste 	u_int32_t qoffset;
1821a6d35632SScott Long 	int error;
182235863739SMike Smith 
182331a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
1824ffb37f33SScott Long 
182535863739SMike Smith 	/*
1826914da7d0SScott Long 	 * Fill in the init structure.  This tells the adapter about the
1827914da7d0SScott Long 	 * physical location of various important shared data structures.
182835863739SMike Smith 	 */
182935863739SMike Smith 	ip = &sc->aac_common->ac_init;
183035863739SMike Smith 	ip->InitStructRevision = AAC_INIT_STRUCT_REVISION;
18317cb209f5SScott Long 	if (sc->aac_max_fib_size > sizeof(struct aac_fib)) {
18327cb209f5SScott Long 		ip->InitStructRevision = AAC_INIT_STRUCT_REVISION_4;
18337cb209f5SScott Long 		sc->flags |= AAC_FLAGS_RAW_IO;
18347cb209f5SScott Long 	}
1835f30ac74cSScott Long 	ip->MiniPortRevision = AAC_INIT_STRUCT_MINIPORT_REVISION;
183635863739SMike Smith 
1837c6eafcf2SScott Long 	ip->AdapterFibsPhysicalAddress = sc->aac_common_busaddr +
1838c6eafcf2SScott Long 					 offsetof(struct aac_common, ac_fibs);
1839149af931SScott Long 	ip->AdapterFibsVirtualAddress = 0;
184035863739SMike Smith 	ip->AdapterFibsSize = AAC_ADAPTER_FIBS * sizeof(struct aac_fib);
184135863739SMike Smith 	ip->AdapterFibAlign = sizeof(struct aac_fib);
184235863739SMike Smith 
1843c6eafcf2SScott Long 	ip->PrintfBufferAddress = sc->aac_common_busaddr +
1844c6eafcf2SScott Long 				  offsetof(struct aac_common, ac_printf);
184535863739SMike Smith 	ip->PrintfBufferSize = AAC_PRINTF_BUFSIZE;
184635863739SMike Smith 
18474b00f859SScott Long 	/*
18484b00f859SScott Long 	 * The adapter assumes that pages are 4K in size, except on some
18494b00f859SScott Long  	 * broken firmware versions that do the page->byte conversion twice,
18504b00f859SScott Long 	 * therefore 'assuming' that this value is in 16MB units (2^24).
18514b00f859SScott Long 	 * Round up since the granularity is so high.
18524b00f859SScott Long 	 */
1853f30ac74cSScott Long 	ip->HostPhysMemPages = ctob(physmem) / AAC_PAGE_SIZE;
18544b00f859SScott Long 	if (sc->flags & AAC_FLAGS_BROKEN_MEMMAP) {
18554b00f859SScott Long 		ip->HostPhysMemPages =
18564b00f859SScott Long 		    (ip->HostPhysMemPages + AAC_PAGE_SIZE) / AAC_PAGE_SIZE;
1857204c0befSScott Long 	}
18582b3b0f17SScott Long 	ip->HostElapsedSeconds = time_uptime;	/* reset later if invalid */
185935863739SMike Smith 
18607cb209f5SScott Long 	ip->InitFlags = 0;
18617cb209f5SScott Long 	if (sc->flags & AAC_FLAGS_NEW_COMM) {
18627cb209f5SScott Long 		ip->InitFlags = INITFLAGS_NEW_COMM_SUPPORTED;
18637cb209f5SScott Long 		device_printf(sc->aac_dev, "New comm. interface enabled\n");
18647cb209f5SScott Long 	}
18657cb209f5SScott Long 
18667cb209f5SScott Long 	ip->MaxIoCommands = sc->aac_max_fibs;
18677cb209f5SScott Long 	ip->MaxIoSize = sc->aac_max_sectors << 9;
18687cb209f5SScott Long 	ip->MaxFibSize = sc->aac_max_fib_size;
18697cb209f5SScott Long 
187035863739SMike Smith 	/*
18714109ba51SEd Maste 	 * Initialize FIB queues.  Note that it appears that the layout of the
1872c6eafcf2SScott Long 	 * indexes and the segmentation of the entries may be mandated by the
1873c6eafcf2SScott Long 	 * adapter, which is only told about the base of the queue index fields.
187435863739SMike Smith 	 *
187535863739SMike Smith 	 * The initial values of the indices are assumed to inform the adapter
1876914da7d0SScott Long 	 * of the sizes of the respective queues, and theoretically it could
1877914da7d0SScott Long 	 * work out the entire layout of the queue structures from this.  We
1878914da7d0SScott Long 	 * take the easy route and just lay this area out like everyone else
1879914da7d0SScott Long 	 * does.
188035863739SMike Smith 	 *
1881914da7d0SScott Long 	 * The Linux driver uses a much more complex scheme whereby several
1882914da7d0SScott Long 	 * header records are kept for each queue.  We use a couple of generic
1883914da7d0SScott Long 	 * list manipulation functions which 'know' the size of each list by
1884914da7d0SScott Long 	 * virtue of a table.
188535863739SMike Smith 	 */
1886b88ffdc8SScott Long 	qoffset = offsetof(struct aac_common, ac_qbuf) + AAC_QUEUE_ALIGN;
18870bcbebd6SScott Long 	qoffset &= ~(AAC_QUEUE_ALIGN - 1);
18880bcbebd6SScott Long 	sc->aac_queues =
18890bcbebd6SScott Long 	    (struct aac_queue_table *)((uintptr_t)sc->aac_common + qoffset);
1890b88ffdc8SScott Long 	ip->CommHeaderAddress = sc->aac_common_busaddr + qoffset;
189135863739SMike Smith 
1892c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_NORM_CMD_QUEUE][AAC_PRODUCER_INDEX] =
1893c6eafcf2SScott Long 		AAC_HOST_NORM_CMD_ENTRIES;
1894c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_NORM_CMD_QUEUE][AAC_CONSUMER_INDEX] =
1895c6eafcf2SScott Long 		AAC_HOST_NORM_CMD_ENTRIES;
1896c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_HIGH_CMD_QUEUE][AAC_PRODUCER_INDEX] =
1897c6eafcf2SScott Long 		AAC_HOST_HIGH_CMD_ENTRIES;
1898c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_HIGH_CMD_QUEUE][AAC_CONSUMER_INDEX] =
1899c6eafcf2SScott Long 		AAC_HOST_HIGH_CMD_ENTRIES;
1900c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_NORM_CMD_QUEUE][AAC_PRODUCER_INDEX] =
1901c6eafcf2SScott Long 		AAC_ADAP_NORM_CMD_ENTRIES;
1902c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_NORM_CMD_QUEUE][AAC_CONSUMER_INDEX] =
1903c6eafcf2SScott Long 		AAC_ADAP_NORM_CMD_ENTRIES;
1904c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_HIGH_CMD_QUEUE][AAC_PRODUCER_INDEX] =
1905c6eafcf2SScott Long 		AAC_ADAP_HIGH_CMD_ENTRIES;
1906c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_HIGH_CMD_QUEUE][AAC_CONSUMER_INDEX] =
1907c6eafcf2SScott Long 		AAC_ADAP_HIGH_CMD_ENTRIES;
1908c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_NORM_RESP_QUEUE][AAC_PRODUCER_INDEX]=
1909c6eafcf2SScott Long 		AAC_HOST_NORM_RESP_ENTRIES;
1910c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_NORM_RESP_QUEUE][AAC_CONSUMER_INDEX]=
1911c6eafcf2SScott Long 		AAC_HOST_NORM_RESP_ENTRIES;
1912c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_HIGH_RESP_QUEUE][AAC_PRODUCER_INDEX]=
1913c6eafcf2SScott Long 		AAC_HOST_HIGH_RESP_ENTRIES;
1914c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_HIGH_RESP_QUEUE][AAC_CONSUMER_INDEX]=
1915c6eafcf2SScott Long 		AAC_HOST_HIGH_RESP_ENTRIES;
1916c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_NORM_RESP_QUEUE][AAC_PRODUCER_INDEX]=
1917c6eafcf2SScott Long 		AAC_ADAP_NORM_RESP_ENTRIES;
1918c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_NORM_RESP_QUEUE][AAC_CONSUMER_INDEX]=
1919c6eafcf2SScott Long 		AAC_ADAP_NORM_RESP_ENTRIES;
1920c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_HIGH_RESP_QUEUE][AAC_PRODUCER_INDEX]=
1921c6eafcf2SScott Long 		AAC_ADAP_HIGH_RESP_ENTRIES;
1922c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_HIGH_RESP_QUEUE][AAC_CONSUMER_INDEX]=
1923c6eafcf2SScott Long 		AAC_ADAP_HIGH_RESP_ENTRIES;
1924c6eafcf2SScott Long 	sc->aac_qentries[AAC_HOST_NORM_CMD_QUEUE] =
1925c6eafcf2SScott Long 		&sc->aac_queues->qt_HostNormCmdQueue[0];
1926c6eafcf2SScott Long 	sc->aac_qentries[AAC_HOST_HIGH_CMD_QUEUE] =
1927c6eafcf2SScott Long 		&sc->aac_queues->qt_HostHighCmdQueue[0];
1928c6eafcf2SScott Long 	sc->aac_qentries[AAC_ADAP_NORM_CMD_QUEUE] =
1929c6eafcf2SScott Long 		&sc->aac_queues->qt_AdapNormCmdQueue[0];
1930c6eafcf2SScott Long 	sc->aac_qentries[AAC_ADAP_HIGH_CMD_QUEUE] =
1931c6eafcf2SScott Long 		&sc->aac_queues->qt_AdapHighCmdQueue[0];
1932c6eafcf2SScott Long 	sc->aac_qentries[AAC_HOST_NORM_RESP_QUEUE] =
1933c6eafcf2SScott Long 		&sc->aac_queues->qt_HostNormRespQueue[0];
1934c6eafcf2SScott Long 	sc->aac_qentries[AAC_HOST_HIGH_RESP_QUEUE] =
1935c6eafcf2SScott Long 		&sc->aac_queues->qt_HostHighRespQueue[0];
1936c6eafcf2SScott Long 	sc->aac_qentries[AAC_ADAP_NORM_RESP_QUEUE] =
1937c6eafcf2SScott Long 		&sc->aac_queues->qt_AdapNormRespQueue[0];
1938c6eafcf2SScott Long 	sc->aac_qentries[AAC_ADAP_HIGH_RESP_QUEUE] =
1939c6eafcf2SScott Long 		&sc->aac_queues->qt_AdapHighRespQueue[0];
194035863739SMike Smith 
194135863739SMike Smith 	/*
194235863739SMike Smith 	 * Do controller-type-specific initialisation
194335863739SMike Smith 	 */
194435863739SMike Smith 	switch (sc->aac_hwif) {
194535863739SMike Smith 	case AAC_HWIF_I960RX:
194635863739SMike Smith 		AAC_SETREG4(sc, AAC_RX_ODBR, ~0);
194735863739SMike Smith 		break;
19484afedc31SScott Long 	case AAC_HWIF_RKT:
19494afedc31SScott Long 		AAC_SETREG4(sc, AAC_RKT_ODBR, ~0);
19504afedc31SScott Long 		break;
19514afedc31SScott Long 	default:
19524afedc31SScott Long 		break;
195335863739SMike Smith 	}
195435863739SMike Smith 
195535863739SMike Smith 	/*
195635863739SMike Smith 	 * Give the init structure to the controller.
195735863739SMike Smith 	 */
195835863739SMike Smith 	if (aac_sync_command(sc, AAC_MONKER_INITSTRUCT,
1959914da7d0SScott Long 			     sc->aac_common_busaddr +
1960914da7d0SScott Long 			     offsetof(struct aac_common, ac_init), 0, 0, 0,
1961914da7d0SScott Long 			     NULL)) {
1962914da7d0SScott Long 		device_printf(sc->aac_dev,
1963914da7d0SScott Long 			      "error establishing init structure\n");
1964a6d35632SScott Long 		error = EIO;
1965a6d35632SScott Long 		goto out;
196635863739SMike Smith 	}
196735863739SMike Smith 
1968a6d35632SScott Long 	error = 0;
1969a6d35632SScott Long out:
1970a6d35632SScott Long 	return(error);
197135863739SMike Smith }
197235863739SMike Smith 
197304f4d586SEd Maste static int
197404f4d586SEd Maste aac_setup_intr(struct aac_softc *sc)
197504f4d586SEd Maste {
197604f4d586SEd Maste 	sc->aac_irq_rid = 0;
197704f4d586SEd Maste 	if ((sc->aac_irq = bus_alloc_resource_any(sc->aac_dev, SYS_RES_IRQ,
197804f4d586SEd Maste 			   			  &sc->aac_irq_rid,
197904f4d586SEd Maste 			   			  RF_SHAREABLE |
198004f4d586SEd Maste 						  RF_ACTIVE)) == NULL) {
198104f4d586SEd Maste 		device_printf(sc->aac_dev, "can't allocate interrupt\n");
198204f4d586SEd Maste 		return (EINVAL);
198304f4d586SEd Maste 	}
198404f4d586SEd Maste 	if (sc->flags & AAC_FLAGS_NEW_COMM) {
198504f4d586SEd Maste 		if (bus_setup_intr(sc->aac_dev, sc->aac_irq,
198604f4d586SEd Maste 				   INTR_MPSAFE|INTR_TYPE_BIO, NULL,
198704f4d586SEd Maste 				   aac_new_intr, sc, &sc->aac_intr)) {
198804f4d586SEd Maste 			device_printf(sc->aac_dev, "can't set up interrupt\n");
198904f4d586SEd Maste 			return (EINVAL);
199004f4d586SEd Maste 		}
199104f4d586SEd Maste 	} else {
199204f4d586SEd Maste 		if (bus_setup_intr(sc->aac_dev, sc->aac_irq,
199304f4d586SEd Maste 				   INTR_TYPE_BIO, aac_fast_intr, NULL,
199404f4d586SEd Maste 				   sc, &sc->aac_intr)) {
199504f4d586SEd Maste 			device_printf(sc->aac_dev,
199604f4d586SEd Maste 				      "can't set up FAST interrupt\n");
199704f4d586SEd Maste 			if (bus_setup_intr(sc->aac_dev, sc->aac_irq,
199804f4d586SEd Maste 					   INTR_MPSAFE|INTR_TYPE_BIO,
199904f4d586SEd Maste 					   NULL, (driver_intr_t *)aac_fast_intr,
200004f4d586SEd Maste 					   sc, &sc->aac_intr)) {
200104f4d586SEd Maste 				device_printf(sc->aac_dev,
200204f4d586SEd Maste 					     "can't set up MPSAFE interrupt\n");
200304f4d586SEd Maste 				return (EINVAL);
200404f4d586SEd Maste 			}
200504f4d586SEd Maste 		}
200604f4d586SEd Maste 	}
200704f4d586SEd Maste 	return (0);
200804f4d586SEd Maste }
200904f4d586SEd Maste 
2010914da7d0SScott Long /*
201135863739SMike Smith  * Send a synchronous command to the controller and wait for a result.
20127cb209f5SScott Long  * Indicate if the controller completed the command with an error status.
201335863739SMike Smith  */
201435863739SMike Smith static int
201535863739SMike Smith aac_sync_command(struct aac_softc *sc, u_int32_t command,
201635863739SMike Smith 		 u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3,
201735863739SMike Smith 		 u_int32_t *sp)
201835863739SMike Smith {
201935863739SMike Smith 	time_t then;
202035863739SMike Smith 	u_int32_t status;
202135863739SMike Smith 
202231a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
202335863739SMike Smith 
202435863739SMike Smith 	/* populate the mailbox */
202535863739SMike Smith 	AAC_SET_MAILBOX(sc, command, arg0, arg1, arg2, arg3);
202635863739SMike Smith 
202735863739SMike Smith 	/* ensure the sync command doorbell flag is cleared */
202835863739SMike Smith 	AAC_CLEAR_ISTATUS(sc, AAC_DB_SYNC_COMMAND);
202935863739SMike Smith 
203035863739SMike Smith 	/* then set it to signal the adapter */
203135863739SMike Smith 	AAC_QNOTIFY(sc, AAC_DB_SYNC_COMMAND);
203235863739SMike Smith 
203335863739SMike Smith 	/* spin waiting for the command to complete */
20342b3b0f17SScott Long 	then = time_uptime;
203535863739SMike Smith 	do {
20362b3b0f17SScott Long 		if (time_uptime > (then + AAC_IMMEDIATE_TIMEOUT)) {
203731a0399eSEd Maste 			fwprintf(sc, HBA_FLAGS_DBG_ERROR_B, "timed out");
203835863739SMike Smith 			return(EIO);
203935863739SMike Smith 		}
204035863739SMike Smith 	} while (!(AAC_GET_ISTATUS(sc) & AAC_DB_SYNC_COMMAND));
204135863739SMike Smith 
204235863739SMike Smith 	/* clear the completion flag */
204335863739SMike Smith 	AAC_CLEAR_ISTATUS(sc, AAC_DB_SYNC_COMMAND);
204435863739SMike Smith 
204535863739SMike Smith 	/* get the command status */
2046a6d35632SScott Long 	status = AAC_GET_MAILBOX(sc, 0);
204735863739SMike Smith 	if (sp != NULL)
204835863739SMike Smith 		*sp = status;
20497cb209f5SScott Long 
2050a441b3fcSScott Long 	if (status != AAC_SRB_STS_SUCCESS)
20517cb209f5SScott Long 		return (-1);
20520b94a66eSMike Smith 	return(0);
205335863739SMike Smith }
205435863739SMike Smith 
2055cbfd045bSScott Long int
205635863739SMike Smith aac_sync_fib(struct aac_softc *sc, u_int32_t command, u_int32_t xferstate,
2057cbfd045bSScott Long 		 struct aac_fib *fib, u_int16_t datasize)
205835863739SMike Smith {
205931a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
20607cb209f5SScott Long 	mtx_assert(&sc->aac_io_lock, MA_OWNED);
206135863739SMike Smith 
206235863739SMike Smith 	if (datasize > AAC_FIB_DATASIZE)
206335863739SMike Smith 		return(EINVAL);
206435863739SMike Smith 
206535863739SMike Smith 	/*
206635863739SMike Smith 	 * Set up the sync FIB
206735863739SMike Smith 	 */
2068914da7d0SScott Long 	fib->Header.XferState = AAC_FIBSTATE_HOSTOWNED |
2069914da7d0SScott Long 				AAC_FIBSTATE_INITIALISED |
2070c6eafcf2SScott Long 				AAC_FIBSTATE_EMPTY;
207135863739SMike Smith 	fib->Header.XferState |= xferstate;
207235863739SMike Smith 	fib->Header.Command = command;
207335863739SMike Smith 	fib->Header.StructType = AAC_FIBTYPE_TFIB;
207442ef13a2SEd Maste 	fib->Header.Size = sizeof(struct aac_fib_header) + datasize;
207535863739SMike Smith 	fib->Header.SenderSize = sizeof(struct aac_fib);
2076b88ffdc8SScott Long 	fib->Header.SenderFibAddress = 0;	/* Not needed */
2077c6eafcf2SScott Long 	fib->Header.ReceiverFibAddress = sc->aac_common_busaddr +
2078914da7d0SScott Long 					 offsetof(struct aac_common,
2079914da7d0SScott Long 						  ac_sync_fib);
208035863739SMike Smith 
208135863739SMike Smith 	/*
208235863739SMike Smith 	 * Give the FIB to the controller, wait for a response.
208335863739SMike Smith 	 */
2084914da7d0SScott Long 	if (aac_sync_command(sc, AAC_MONKER_SYNCFIB,
2085914da7d0SScott Long 			     fib->Header.ReceiverFibAddress, 0, 0, 0, NULL)) {
208631a0399eSEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_ERROR_B, "IO error");
208735863739SMike Smith 		return(EIO);
208835863739SMike Smith 	}
208935863739SMike Smith 
209035863739SMike Smith 	return (0);
209135863739SMike Smith }
209235863739SMike Smith 
2093914da7d0SScott Long /*
209435863739SMike Smith  * Adapter-space FIB queue manipulation
209535863739SMike Smith  *
209635863739SMike Smith  * Note that the queue implementation here is a little funky; neither the PI or
209735863739SMike Smith  * CI will ever be zero.  This behaviour is a controller feature.
209835863739SMike Smith  */
209935863739SMike Smith static struct {
210035863739SMike Smith 	int		size;
210135863739SMike Smith 	int		notify;
210235863739SMike Smith } aac_qinfo[] = {
210335863739SMike Smith 	{AAC_HOST_NORM_CMD_ENTRIES, AAC_DB_COMMAND_NOT_FULL},
210435863739SMike Smith 	{AAC_HOST_HIGH_CMD_ENTRIES, 0},
210535863739SMike Smith 	{AAC_ADAP_NORM_CMD_ENTRIES, AAC_DB_COMMAND_READY},
210635863739SMike Smith 	{AAC_ADAP_HIGH_CMD_ENTRIES, 0},
210735863739SMike Smith 	{AAC_HOST_NORM_RESP_ENTRIES, AAC_DB_RESPONSE_NOT_FULL},
210835863739SMike Smith 	{AAC_HOST_HIGH_RESP_ENTRIES, 0},
210935863739SMike Smith 	{AAC_ADAP_NORM_RESP_ENTRIES, AAC_DB_RESPONSE_READY},
211035863739SMike Smith 	{AAC_ADAP_HIGH_RESP_ENTRIES, 0}
211135863739SMike Smith };
211235863739SMike Smith 
211335863739SMike Smith /*
2114c6eafcf2SScott Long  * Atomically insert an entry into the nominated queue, returns 0 on success or
2115c6eafcf2SScott Long  * EBUSY if the queue is full.
211635863739SMike Smith  *
21170b94a66eSMike Smith  * Note: it would be more efficient to defer notifying the controller in
2118914da7d0SScott Long  *	 the case where we may be inserting several entries in rapid succession,
2119914da7d0SScott Long  *	 but implementing this usefully may be difficult (it would involve a
2120c6eafcf2SScott Long  *	 separate queue/notify interface).
212135863739SMike Smith  */
212235863739SMike Smith static int
2123f6c4dd3fSScott Long aac_enqueue_fib(struct aac_softc *sc, int queue, struct aac_command *cm)
212435863739SMike Smith {
212535863739SMike Smith 	u_int32_t pi, ci;
21269e2e96d8SScott Long 	int error;
2127f6c4dd3fSScott Long 	u_int32_t fib_size;
2128f6c4dd3fSScott Long 	u_int32_t fib_addr;
2129f6c4dd3fSScott Long 
213031a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
213136e0bf6eSScott Long 
2132f6c4dd3fSScott Long 	fib_size = cm->cm_fib->Header.Size;
2133f6c4dd3fSScott Long 	fib_addr = cm->cm_fib->Header.ReceiverFibAddress;
213435863739SMike Smith 
213535863739SMike Smith 	/* get the producer/consumer indices */
213635863739SMike Smith 	pi = sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX];
213735863739SMike Smith 	ci = sc->aac_queues->qt_qindex[queue][AAC_CONSUMER_INDEX];
213835863739SMike Smith 
213935863739SMike Smith 	/* wrap the queue? */
214035863739SMike Smith 	if (pi >= aac_qinfo[queue].size)
214135863739SMike Smith 		pi = 0;
214235863739SMike Smith 
214335863739SMike Smith 	/* check for queue full */
214435863739SMike Smith 	if ((pi + 1) == ci) {
214535863739SMike Smith 		error = EBUSY;
214635863739SMike Smith 		goto out;
214735863739SMike Smith 	}
214835863739SMike Smith 
2149614c22b2SScott Long 	/*
2150614c22b2SScott Long 	 * To avoid a race with its completion interrupt, place this command on
2151614c22b2SScott Long 	 * the busy queue prior to advertising it to the controller.
2152614c22b2SScott Long 	 */
2153614c22b2SScott Long 	aac_enqueue_busy(cm);
2154614c22b2SScott Long 
215535863739SMike Smith 	/* populate queue entry */
215635863739SMike Smith 	(sc->aac_qentries[queue] + pi)->aq_fib_size = fib_size;
215735863739SMike Smith 	(sc->aac_qentries[queue] + pi)->aq_fib_addr = fib_addr;
215835863739SMike Smith 
215935863739SMike Smith 	/* update producer index */
216035863739SMike Smith 	sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX] = pi + 1;
216135863739SMike Smith 
216235863739SMike Smith 	/* notify the adapter if we know how */
216335863739SMike Smith 	if (aac_qinfo[queue].notify != 0)
216435863739SMike Smith 		AAC_QNOTIFY(sc, aac_qinfo[queue].notify);
216535863739SMike Smith 
216635863739SMike Smith 	error = 0;
216735863739SMike Smith 
216835863739SMike Smith out:
216935863739SMike Smith 	return(error);
217035863739SMike Smith }
217135863739SMike Smith 
217235863739SMike Smith /*
217336e0bf6eSScott Long  * Atomically remove one entry from the nominated queue, returns 0 on
217436e0bf6eSScott Long  * success or ENOENT if the queue is empty.
217535863739SMike Smith  */
217635863739SMike Smith static int
2177c6eafcf2SScott Long aac_dequeue_fib(struct aac_softc *sc, int queue, u_int32_t *fib_size,
2178c6eafcf2SScott Long 		struct aac_fib **fib_addr)
217935863739SMike Smith {
218035863739SMike Smith 	u_int32_t pi, ci;
2181149af931SScott Long 	u_int32_t fib_index;
21829e2e96d8SScott Long 	int error;
2183f6c4dd3fSScott Long 	int notify;
218435863739SMike Smith 
218531a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
218635863739SMike Smith 
218735863739SMike Smith 	/* get the producer/consumer indices */
218835863739SMike Smith 	pi = sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX];
218935863739SMike Smith 	ci = sc->aac_queues->qt_qindex[queue][AAC_CONSUMER_INDEX];
219035863739SMike Smith 
219135863739SMike Smith 	/* check for queue empty */
219235863739SMike Smith 	if (ci == pi) {
219335863739SMike Smith 		error = ENOENT;
219435863739SMike Smith 		goto out;
219535863739SMike Smith 	}
219635863739SMike Smith 
21977753acd2SScott Long 	/* wrap the pi so the following test works */
21987753acd2SScott Long 	if (pi >= aac_qinfo[queue].size)
21997753acd2SScott Long 		pi = 0;
22007753acd2SScott Long 
2201f6c4dd3fSScott Long 	notify = 0;
2202f6c4dd3fSScott Long 	if (ci == pi + 1)
2203f6c4dd3fSScott Long 		notify++;
2204f6c4dd3fSScott Long 
220535863739SMike Smith 	/* wrap the queue? */
220635863739SMike Smith 	if (ci >= aac_qinfo[queue].size)
220735863739SMike Smith 		ci = 0;
220835863739SMike Smith 
220935863739SMike Smith 	/* fetch the entry */
221035863739SMike Smith 	*fib_size = (sc->aac_qentries[queue] + ci)->aq_fib_size;
2211149af931SScott Long 
2212149af931SScott Long 	switch (queue) {
2213149af931SScott Long 	case AAC_HOST_NORM_CMD_QUEUE:
2214149af931SScott Long 	case AAC_HOST_HIGH_CMD_QUEUE:
2215149af931SScott Long 		/*
2216149af931SScott Long 		 * The aq_fib_addr is only 32 bits wide so it can't be counted
2217149af931SScott Long 		 * on to hold an address.  For AIF's, the adapter assumes
2218149af931SScott Long 		 * that it's giving us an address into the array of AIF fibs.
2219149af931SScott Long 		 * Therefore, we have to convert it to an index.
2220149af931SScott Long 		 */
2221149af931SScott Long 		fib_index = (sc->aac_qentries[queue] + ci)->aq_fib_addr /
2222149af931SScott Long 			sizeof(struct aac_fib);
2223149af931SScott Long 		*fib_addr = &sc->aac_common->ac_fibs[fib_index];
2224149af931SScott Long 		break;
2225149af931SScott Long 
2226149af931SScott Long 	case AAC_HOST_NORM_RESP_QUEUE:
2227149af931SScott Long 	case AAC_HOST_HIGH_RESP_QUEUE:
2228149af931SScott Long 	{
2229149af931SScott Long 		struct aac_command *cm;
2230149af931SScott Long 
2231149af931SScott Long 		/*
2232149af931SScott Long 		 * As above, an index is used instead of an actual address.
2233149af931SScott Long 		 * Gotta shift the index to account for the fast response
2234149af931SScott Long 		 * bit.  No other correction is needed since this value was
2235149af931SScott Long 		 * originally provided by the driver via the SenderFibAddress
2236149af931SScott Long 		 * field.
2237149af931SScott Long 		 */
2238149af931SScott Long 		fib_index = (sc->aac_qentries[queue] + ci)->aq_fib_addr;
22397cb209f5SScott Long 		cm = sc->aac_commands + (fib_index >> 2);
2240149af931SScott Long 		*fib_addr = cm->cm_fib;
224135863739SMike Smith 
2242f30ac74cSScott Long 		/*
2243f30ac74cSScott Long 		 * Is this a fast response? If it is, update the fib fields in
2244149af931SScott Long 		 * local memory since the whole fib isn't DMA'd back up.
2245f30ac74cSScott Long 		 */
2246149af931SScott Long 		if (fib_index & 0x01) {
2247f30ac74cSScott Long 			(*fib_addr)->Header.XferState |= AAC_FIBSTATE_DONEADAP;
2248f30ac74cSScott Long 			*((u_int32_t*)((*fib_addr)->data)) = AAC_ERROR_NORMAL;
2249f30ac74cSScott Long 		}
2250149af931SScott Long 		break;
2251149af931SScott Long 	}
2252149af931SScott Long 	default:
2253149af931SScott Long 		panic("Invalid queue in aac_dequeue_fib()");
2254149af931SScott Long 		break;
2255149af931SScott Long 	}
2256149af931SScott Long 
225735863739SMike Smith 	/* update consumer index */
225835863739SMike Smith 	sc->aac_queues->qt_qindex[queue][AAC_CONSUMER_INDEX] = ci + 1;
225935863739SMike Smith 
226035863739SMike Smith 	/* if we have made the queue un-full, notify the adapter */
2261f6c4dd3fSScott Long 	if (notify && (aac_qinfo[queue].notify != 0))
226235863739SMike Smith 		AAC_QNOTIFY(sc, aac_qinfo[queue].notify);
226335863739SMike Smith 	error = 0;
226435863739SMike Smith 
226535863739SMike Smith out:
226635863739SMike Smith 	return(error);
226735863739SMike Smith }
226835863739SMike Smith 
2269914da7d0SScott Long /*
227036e0bf6eSScott Long  * Put our response to an Adapter Initialed Fib on the response queue
227136e0bf6eSScott Long  */
227236e0bf6eSScott Long static int
227336e0bf6eSScott Long aac_enqueue_response(struct aac_softc *sc, int queue, struct aac_fib *fib)
227436e0bf6eSScott Long {
227536e0bf6eSScott Long 	u_int32_t pi, ci;
22769e2e96d8SScott Long 	int error;
227736e0bf6eSScott Long 	u_int32_t fib_size;
227836e0bf6eSScott Long 	u_int32_t fib_addr;
227936e0bf6eSScott Long 
228031a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
228136e0bf6eSScott Long 
228236e0bf6eSScott Long 	/* Tell the adapter where the FIB is */
228336e0bf6eSScott Long 	fib_size = fib->Header.Size;
228436e0bf6eSScott Long 	fib_addr = fib->Header.SenderFibAddress;
228536e0bf6eSScott Long 	fib->Header.ReceiverFibAddress = fib_addr;
228636e0bf6eSScott Long 
228736e0bf6eSScott Long 	/* get the producer/consumer indices */
228836e0bf6eSScott Long 	pi = sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX];
228936e0bf6eSScott Long 	ci = sc->aac_queues->qt_qindex[queue][AAC_CONSUMER_INDEX];
229036e0bf6eSScott Long 
229136e0bf6eSScott Long 	/* wrap the queue? */
229236e0bf6eSScott Long 	if (pi >= aac_qinfo[queue].size)
229336e0bf6eSScott Long 		pi = 0;
229436e0bf6eSScott Long 
229536e0bf6eSScott Long 	/* check for queue full */
229636e0bf6eSScott Long 	if ((pi + 1) == ci) {
229736e0bf6eSScott Long 		error = EBUSY;
229836e0bf6eSScott Long 		goto out;
229936e0bf6eSScott Long 	}
230036e0bf6eSScott Long 
230136e0bf6eSScott Long 	/* populate queue entry */
230236e0bf6eSScott Long 	(sc->aac_qentries[queue] + pi)->aq_fib_size = fib_size;
230336e0bf6eSScott Long 	(sc->aac_qentries[queue] + pi)->aq_fib_addr = fib_addr;
230436e0bf6eSScott Long 
230536e0bf6eSScott Long 	/* update producer index */
230636e0bf6eSScott Long 	sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX] = pi + 1;
230736e0bf6eSScott Long 
230836e0bf6eSScott Long 	/* notify the adapter if we know how */
230936e0bf6eSScott Long 	if (aac_qinfo[queue].notify != 0)
231036e0bf6eSScott Long 		AAC_QNOTIFY(sc, aac_qinfo[queue].notify);
231136e0bf6eSScott Long 
231236e0bf6eSScott Long 	error = 0;
231336e0bf6eSScott Long 
231436e0bf6eSScott Long out:
231536e0bf6eSScott Long 	return(error);
231636e0bf6eSScott Long }
231736e0bf6eSScott Long 
2318914da7d0SScott Long /*
23190b94a66eSMike Smith  * Check for commands that have been outstanding for a suspiciously long time,
23200b94a66eSMike Smith  * and complain about them.
23210b94a66eSMike Smith  */
23220b94a66eSMike Smith static void
23230b94a66eSMike Smith aac_timeout(struct aac_softc *sc)
23240b94a66eSMike Smith {
23250b94a66eSMike Smith 	struct aac_command *cm;
23260b94a66eSMike Smith 	time_t deadline;
232715c37be0SScott Long 	int timedout, code;
23280b94a66eSMike Smith 
2329f6c4dd3fSScott Long 	/*
233070545d1aSScott Long 	 * Traverse the busy command list, bitch about late commands once
2331914da7d0SScott Long 	 * only.
2332914da7d0SScott Long 	 */
233315c37be0SScott Long 	timedout = 0;
23342b3b0f17SScott Long 	deadline = time_uptime - AAC_CMD_TIMEOUT;
23350b94a66eSMike Smith 	TAILQ_FOREACH(cm, &sc->aac_busy, cm_link) {
2336f6c4dd3fSScott Long 		if ((cm->cm_timestamp  < deadline)
2337f6c4dd3fSScott Long 			/* && !(cm->cm_flags & AAC_CMD_TIMEDOUT) */) {
23380b94a66eSMike Smith 			cm->cm_flags |= AAC_CMD_TIMEDOUT;
2339914da7d0SScott Long 			device_printf(sc->aac_dev,
2340914da7d0SScott Long 				      "COMMAND %p TIMEOUT AFTER %d SECONDS\n",
23412b3b0f17SScott Long 				      cm, (int)(time_uptime-cm->cm_timestamp));
23420b94a66eSMike Smith 			AAC_PRINT_FIB(sc, cm->cm_fib);
234315c37be0SScott Long 			timedout++;
23440b94a66eSMike Smith 		}
23450b94a66eSMike Smith 	}
23460b94a66eSMike Smith 
234715c37be0SScott Long 	if (timedout) {
234815c37be0SScott Long 		code = AAC_GET_FWSTATUS(sc);
234915c37be0SScott Long 		if (code != AAC_UP_AND_RUNNING) {
235015c37be0SScott Long 			device_printf(sc->aac_dev, "WARNING! Controller is no "
235115c37be0SScott Long 				      "longer running! code= 0x%x\n", code);
235215c37be0SScott Long 		}
235315c37be0SScott Long 	}
23540b94a66eSMike Smith 	return;
23550b94a66eSMike Smith }
23560b94a66eSMike Smith 
2357914da7d0SScott Long /*
2358914da7d0SScott Long  * Interface Function Vectors
2359914da7d0SScott Long  */
236035863739SMike Smith 
2361914da7d0SScott Long /*
236235863739SMike Smith  * Read the current firmware status word.
236335863739SMike Smith  */
236435863739SMike Smith static int
236535863739SMike Smith aac_sa_get_fwstatus(struct aac_softc *sc)
236635863739SMike Smith {
236731a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
236835863739SMike Smith 
236935863739SMike Smith 	return(AAC_GETREG4(sc, AAC_SA_FWSTATUS));
237035863739SMike Smith }
237135863739SMike Smith 
237235863739SMike Smith static int
237335863739SMike Smith aac_rx_get_fwstatus(struct aac_softc *sc)
237435863739SMike Smith {
237531a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
237635863739SMike Smith 
237735863739SMike Smith 	return(AAC_GETREG4(sc, AAC_RX_FWSTATUS));
237835863739SMike Smith }
237935863739SMike Smith 
2380b3457b51SScott Long static int
2381b3457b51SScott Long aac_fa_get_fwstatus(struct aac_softc *sc)
2382b3457b51SScott Long {
2383b3457b51SScott Long 	int val;
2384b3457b51SScott Long 
238531a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
2386b3457b51SScott Long 
2387b3457b51SScott Long 	val = AAC_GETREG4(sc, AAC_FA_FWSTATUS);
2388b3457b51SScott Long 	return (val);
2389b3457b51SScott Long }
2390b3457b51SScott Long 
23914afedc31SScott Long static int
23924afedc31SScott Long aac_rkt_get_fwstatus(struct aac_softc *sc)
23934afedc31SScott Long {
239431a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
23954afedc31SScott Long 
23964afedc31SScott Long 	return(AAC_GETREG4(sc, AAC_RKT_FWSTATUS));
23974afedc31SScott Long }
23984afedc31SScott Long 
2399914da7d0SScott Long /*
240035863739SMike Smith  * Notify the controller of a change in a given queue
240135863739SMike Smith  */
240235863739SMike Smith 
240335863739SMike Smith static void
240435863739SMike Smith aac_sa_qnotify(struct aac_softc *sc, int qbit)
240535863739SMike Smith {
240631a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
240735863739SMike Smith 
240835863739SMike Smith 	AAC_SETREG2(sc, AAC_SA_DOORBELL1_SET, qbit);
240935863739SMike Smith }
241035863739SMike Smith 
241135863739SMike Smith static void
241235863739SMike Smith aac_rx_qnotify(struct aac_softc *sc, int qbit)
241335863739SMike Smith {
241431a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
241535863739SMike Smith 
241635863739SMike Smith 	AAC_SETREG4(sc, AAC_RX_IDBR, qbit);
241735863739SMike Smith }
241835863739SMike Smith 
2419b3457b51SScott Long static void
2420b3457b51SScott Long aac_fa_qnotify(struct aac_softc *sc, int qbit)
2421b3457b51SScott Long {
242231a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
2423b3457b51SScott Long 
2424b3457b51SScott Long 	AAC_SETREG2(sc, AAC_FA_DOORBELL1, qbit);
2425b3457b51SScott Long 	AAC_FA_HACK(sc);
2426b3457b51SScott Long }
2427b3457b51SScott Long 
24284afedc31SScott Long static void
24294afedc31SScott Long aac_rkt_qnotify(struct aac_softc *sc, int qbit)
24304afedc31SScott Long {
243131a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
24324afedc31SScott Long 
24334afedc31SScott Long 	AAC_SETREG4(sc, AAC_RKT_IDBR, qbit);
24344afedc31SScott Long }
24354afedc31SScott Long 
2436914da7d0SScott Long /*
243735863739SMike Smith  * Get the interrupt reason bits
243835863739SMike Smith  */
243935863739SMike Smith static int
244035863739SMike Smith aac_sa_get_istatus(struct aac_softc *sc)
244135863739SMike Smith {
244231a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
244335863739SMike Smith 
244435863739SMike Smith 	return(AAC_GETREG2(sc, AAC_SA_DOORBELL0));
244535863739SMike Smith }
244635863739SMike Smith 
244735863739SMike Smith static int
244835863739SMike Smith aac_rx_get_istatus(struct aac_softc *sc)
244935863739SMike Smith {
245031a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
245135863739SMike Smith 
245235863739SMike Smith 	return(AAC_GETREG4(sc, AAC_RX_ODBR));
245335863739SMike Smith }
245435863739SMike Smith 
2455b3457b51SScott Long static int
2456b3457b51SScott Long aac_fa_get_istatus(struct aac_softc *sc)
2457b3457b51SScott Long {
2458b3457b51SScott Long 	int val;
2459b3457b51SScott Long 
246031a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
2461b3457b51SScott Long 
2462b3457b51SScott Long 	val = AAC_GETREG2(sc, AAC_FA_DOORBELL0);
2463b3457b51SScott Long 	return (val);
2464b3457b51SScott Long }
2465b3457b51SScott Long 
24664afedc31SScott Long static int
24674afedc31SScott Long aac_rkt_get_istatus(struct aac_softc *sc)
24684afedc31SScott Long {
246931a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
24704afedc31SScott Long 
24714afedc31SScott Long 	return(AAC_GETREG4(sc, AAC_RKT_ODBR));
24724afedc31SScott Long }
24734afedc31SScott Long 
2474914da7d0SScott Long /*
247535863739SMike Smith  * Clear some interrupt reason bits
247635863739SMike Smith  */
247735863739SMike Smith static void
247835863739SMike Smith aac_sa_clear_istatus(struct aac_softc *sc, int mask)
247935863739SMike Smith {
248031a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
248135863739SMike Smith 
248235863739SMike Smith 	AAC_SETREG2(sc, AAC_SA_DOORBELL0_CLEAR, mask);
248335863739SMike Smith }
248435863739SMike Smith 
248535863739SMike Smith static void
248635863739SMike Smith aac_rx_clear_istatus(struct aac_softc *sc, int mask)
248735863739SMike Smith {
248831a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
248935863739SMike Smith 
249035863739SMike Smith 	AAC_SETREG4(sc, AAC_RX_ODBR, mask);
249135863739SMike Smith }
249235863739SMike Smith 
2493b3457b51SScott Long static void
2494b3457b51SScott Long aac_fa_clear_istatus(struct aac_softc *sc, int mask)
2495b3457b51SScott Long {
249631a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
2497b3457b51SScott Long 
2498b3457b51SScott Long 	AAC_SETREG2(sc, AAC_FA_DOORBELL0_CLEAR, mask);
2499b3457b51SScott Long 	AAC_FA_HACK(sc);
2500b3457b51SScott Long }
2501b3457b51SScott Long 
25024afedc31SScott Long static void
25034afedc31SScott Long aac_rkt_clear_istatus(struct aac_softc *sc, int mask)
25044afedc31SScott Long {
250531a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
25064afedc31SScott Long 
25074afedc31SScott Long 	AAC_SETREG4(sc, AAC_RKT_ODBR, mask);
25084afedc31SScott Long }
25094afedc31SScott Long 
2510914da7d0SScott Long /*
251135863739SMike Smith  * Populate the mailbox and set the command word
251235863739SMike Smith  */
251335863739SMike Smith static void
251435863739SMike Smith aac_sa_set_mailbox(struct aac_softc *sc, u_int32_t command,
251535863739SMike Smith 		u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3)
251635863739SMike Smith {
251731a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
251835863739SMike Smith 
251935863739SMike Smith 	AAC_SETREG4(sc, AAC_SA_MAILBOX, command);
252035863739SMike Smith 	AAC_SETREG4(sc, AAC_SA_MAILBOX + 4, arg0);
252135863739SMike Smith 	AAC_SETREG4(sc, AAC_SA_MAILBOX + 8, arg1);
252235863739SMike Smith 	AAC_SETREG4(sc, AAC_SA_MAILBOX + 12, arg2);
252335863739SMike Smith 	AAC_SETREG4(sc, AAC_SA_MAILBOX + 16, arg3);
252435863739SMike Smith }
252535863739SMike Smith 
252635863739SMike Smith static void
252735863739SMike Smith aac_rx_set_mailbox(struct aac_softc *sc, u_int32_t command,
252835863739SMike Smith 		u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3)
252935863739SMike Smith {
253031a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
253135863739SMike Smith 
253235863739SMike Smith 	AAC_SETREG4(sc, AAC_RX_MAILBOX, command);
253335863739SMike Smith 	AAC_SETREG4(sc, AAC_RX_MAILBOX + 4, arg0);
253435863739SMike Smith 	AAC_SETREG4(sc, AAC_RX_MAILBOX + 8, arg1);
253535863739SMike Smith 	AAC_SETREG4(sc, AAC_RX_MAILBOX + 12, arg2);
253635863739SMike Smith 	AAC_SETREG4(sc, AAC_RX_MAILBOX + 16, arg3);
253735863739SMike Smith }
253835863739SMike Smith 
2539b3457b51SScott Long static void
2540b3457b51SScott Long aac_fa_set_mailbox(struct aac_softc *sc, u_int32_t command,
2541b3457b51SScott Long 		u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3)
2542b3457b51SScott Long {
254331a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
2544b3457b51SScott Long 
2545b3457b51SScott Long 	AAC_SETREG4(sc, AAC_FA_MAILBOX, command);
2546b3457b51SScott Long 	AAC_FA_HACK(sc);
2547b3457b51SScott Long 	AAC_SETREG4(sc, AAC_FA_MAILBOX + 4, arg0);
2548b3457b51SScott Long 	AAC_FA_HACK(sc);
2549b3457b51SScott Long 	AAC_SETREG4(sc, AAC_FA_MAILBOX + 8, arg1);
2550b3457b51SScott Long 	AAC_FA_HACK(sc);
2551b3457b51SScott Long 	AAC_SETREG4(sc, AAC_FA_MAILBOX + 12, arg2);
2552b3457b51SScott Long 	AAC_FA_HACK(sc);
2553b3457b51SScott Long 	AAC_SETREG4(sc, AAC_FA_MAILBOX + 16, arg3);
2554b3457b51SScott Long 	AAC_FA_HACK(sc);
2555b3457b51SScott Long }
2556b3457b51SScott Long 
25574afedc31SScott Long static void
25584afedc31SScott Long aac_rkt_set_mailbox(struct aac_softc *sc, u_int32_t command, u_int32_t arg0,
25594afedc31SScott Long 		    u_int32_t arg1, u_int32_t arg2, u_int32_t arg3)
25604afedc31SScott Long {
256131a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
25624afedc31SScott Long 
25634afedc31SScott Long 	AAC_SETREG4(sc, AAC_RKT_MAILBOX, command);
25644afedc31SScott Long 	AAC_SETREG4(sc, AAC_RKT_MAILBOX + 4, arg0);
25654afedc31SScott Long 	AAC_SETREG4(sc, AAC_RKT_MAILBOX + 8, arg1);
25664afedc31SScott Long 	AAC_SETREG4(sc, AAC_RKT_MAILBOX + 12, arg2);
25674afedc31SScott Long 	AAC_SETREG4(sc, AAC_RKT_MAILBOX + 16, arg3);
25684afedc31SScott Long }
25694afedc31SScott Long 
2570914da7d0SScott Long /*
257135863739SMike Smith  * Fetch the immediate command status word
257235863739SMike Smith  */
257335863739SMike Smith static int
2574a6d35632SScott Long aac_sa_get_mailbox(struct aac_softc *sc, int mb)
257535863739SMike Smith {
257631a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
257735863739SMike Smith 
2578a6d35632SScott Long 	return(AAC_GETREG4(sc, AAC_SA_MAILBOX + (mb * 4)));
257935863739SMike Smith }
258035863739SMike Smith 
258135863739SMike Smith static int
2582a6d35632SScott Long aac_rx_get_mailbox(struct aac_softc *sc, int mb)
258335863739SMike Smith {
258431a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
258535863739SMike Smith 
2586a6d35632SScott Long 	return(AAC_GETREG4(sc, AAC_RX_MAILBOX + (mb * 4)));
258735863739SMike Smith }
258835863739SMike Smith 
2589b3457b51SScott Long static int
2590a6d35632SScott Long aac_fa_get_mailbox(struct aac_softc *sc, int mb)
2591b3457b51SScott Long {
2592b3457b51SScott Long 	int val;
2593b3457b51SScott Long 
259431a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
2595b3457b51SScott Long 
2596a6d35632SScott Long 	val = AAC_GETREG4(sc, AAC_FA_MAILBOX + (mb * 4));
2597b3457b51SScott Long 	return (val);
2598b3457b51SScott Long }
2599b3457b51SScott Long 
26004afedc31SScott Long static int
26014afedc31SScott Long aac_rkt_get_mailbox(struct aac_softc *sc, int mb)
26024afedc31SScott Long {
260331a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
26044afedc31SScott Long 
26054afedc31SScott Long 	return(AAC_GETREG4(sc, AAC_RKT_MAILBOX + (mb * 4)));
26064afedc31SScott Long }
26074afedc31SScott Long 
2608914da7d0SScott Long /*
260935863739SMike Smith  * Set/clear interrupt masks
261035863739SMike Smith  */
261135863739SMike Smith static void
261235863739SMike Smith aac_sa_set_interrupts(struct aac_softc *sc, int enable)
261335863739SMike Smith {
261431a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "%sable interrupts", enable ? "en" : "dis");
261535863739SMike Smith 
261635863739SMike Smith 	if (enable) {
261735863739SMike Smith 		AAC_SETREG2((sc), AAC_SA_MASK0_CLEAR, AAC_DB_INTERRUPTS);
261835863739SMike Smith 	} else {
261935863739SMike Smith 		AAC_SETREG2((sc), AAC_SA_MASK0_SET, ~0);
262035863739SMike Smith 	}
262135863739SMike Smith }
262235863739SMike Smith 
262335863739SMike Smith static void
262435863739SMike Smith aac_rx_set_interrupts(struct aac_softc *sc, int enable)
262535863739SMike Smith {
262631a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "%sable interrupts", enable ? "en" : "dis");
262735863739SMike Smith 
262835863739SMike Smith 	if (enable) {
26297cb209f5SScott Long 		if (sc->flags & AAC_FLAGS_NEW_COMM)
26307cb209f5SScott Long 			AAC_SETREG4(sc, AAC_RX_OIMR, ~AAC_DB_INT_NEW_COMM);
26317cb209f5SScott Long 		else
263235863739SMike Smith 			AAC_SETREG4(sc, AAC_RX_OIMR, ~AAC_DB_INTERRUPTS);
263335863739SMike Smith 	} else {
263435863739SMike Smith 		AAC_SETREG4(sc, AAC_RX_OIMR, ~0);
263535863739SMike Smith 	}
263635863739SMike Smith }
263735863739SMike Smith 
2638b3457b51SScott Long static void
2639b3457b51SScott Long aac_fa_set_interrupts(struct aac_softc *sc, int enable)
2640b3457b51SScott Long {
264131a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "%sable interrupts", enable ? "en" : "dis");
2642b3457b51SScott Long 
2643b3457b51SScott Long 	if (enable) {
2644b3457b51SScott Long 		AAC_SETREG2((sc), AAC_FA_MASK0_CLEAR, AAC_DB_INTERRUPTS);
2645b3457b51SScott Long 		AAC_FA_HACK(sc);
2646b3457b51SScott Long 	} else {
2647b3457b51SScott Long 		AAC_SETREG2((sc), AAC_FA_MASK0, ~0);
2648b3457b51SScott Long 		AAC_FA_HACK(sc);
2649b3457b51SScott Long 	}
2650b3457b51SScott Long }
2651b3457b51SScott Long 
26524afedc31SScott Long static void
26534afedc31SScott Long aac_rkt_set_interrupts(struct aac_softc *sc, int enable)
26544afedc31SScott Long {
265531a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "%sable interrupts", enable ? "en" : "dis");
26564afedc31SScott Long 
26574afedc31SScott Long 	if (enable) {
26587cb209f5SScott Long 		if (sc->flags & AAC_FLAGS_NEW_COMM)
26597cb209f5SScott Long 			AAC_SETREG4(sc, AAC_RKT_OIMR, ~AAC_DB_INT_NEW_COMM);
26607cb209f5SScott Long 		else
26614afedc31SScott Long 			AAC_SETREG4(sc, AAC_RKT_OIMR, ~AAC_DB_INTERRUPTS);
26624afedc31SScott Long 	} else {
26634afedc31SScott Long 		AAC_SETREG4(sc, AAC_RKT_OIMR, ~0);
26644afedc31SScott Long 	}
26654afedc31SScott Long }
26664afedc31SScott Long 
2667914da7d0SScott Long /*
26687cb209f5SScott Long  * New comm. interface: Send command functions
26697cb209f5SScott Long  */
26707cb209f5SScott Long static int
26717cb209f5SScott Long aac_rx_send_command(struct aac_softc *sc, struct aac_command *cm)
26727cb209f5SScott Long {
26737cb209f5SScott Long 	u_int32_t index, device;
26747cb209f5SScott Long 
267531a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "send command (new comm.)");
26767cb209f5SScott Long 
26777cb209f5SScott Long 	index = AAC_GETREG4(sc, AAC_RX_IQUE);
26787cb209f5SScott Long 	if (index == 0xffffffffL)
26797cb209f5SScott Long 		index = AAC_GETREG4(sc, AAC_RX_IQUE);
26807cb209f5SScott Long 	if (index == 0xffffffffL)
26817cb209f5SScott Long 		return index;
26827cb209f5SScott Long 	aac_enqueue_busy(cm);
26837cb209f5SScott Long 	device = index;
26847cb209f5SScott Long 	AAC_SETREG4(sc, device, (u_int32_t)(cm->cm_fibphys & 0xffffffffUL));
26857cb209f5SScott Long 	device += 4;
26867cb209f5SScott Long 	AAC_SETREG4(sc, device, (u_int32_t)(cm->cm_fibphys >> 32));
26877cb209f5SScott Long 	device += 4;
26887cb209f5SScott Long 	AAC_SETREG4(sc, device, cm->cm_fib->Header.Size);
26897cb209f5SScott Long 	AAC_SETREG4(sc, AAC_RX_IQUE, index);
26907cb209f5SScott Long 	return 0;
26917cb209f5SScott Long }
26927cb209f5SScott Long 
26937cb209f5SScott Long static int
26947cb209f5SScott Long aac_rkt_send_command(struct aac_softc *sc, struct aac_command *cm)
26957cb209f5SScott Long {
26967cb209f5SScott Long 	u_int32_t index, device;
26977cb209f5SScott Long 
269831a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "send command (new comm.)");
26997cb209f5SScott Long 
27007cb209f5SScott Long 	index = AAC_GETREG4(sc, AAC_RKT_IQUE);
27017cb209f5SScott Long 	if (index == 0xffffffffL)
27027cb209f5SScott Long 		index = AAC_GETREG4(sc, AAC_RKT_IQUE);
27037cb209f5SScott Long 	if (index == 0xffffffffL)
27047cb209f5SScott Long 		return index;
27057cb209f5SScott Long 	aac_enqueue_busy(cm);
27067cb209f5SScott Long 	device = index;
27077cb209f5SScott Long 	AAC_SETREG4(sc, device, (u_int32_t)(cm->cm_fibphys & 0xffffffffUL));
27087cb209f5SScott Long 	device += 4;
27097cb209f5SScott Long 	AAC_SETREG4(sc, device, (u_int32_t)(cm->cm_fibphys >> 32));
27107cb209f5SScott Long 	device += 4;
27117cb209f5SScott Long 	AAC_SETREG4(sc, device, cm->cm_fib->Header.Size);
27127cb209f5SScott Long 	AAC_SETREG4(sc, AAC_RKT_IQUE, index);
27137cb209f5SScott Long 	return 0;
27147cb209f5SScott Long }
27157cb209f5SScott Long 
27167cb209f5SScott Long /*
27177cb209f5SScott Long  * New comm. interface: get, set outbound queue index
27187cb209f5SScott Long  */
27197cb209f5SScott Long static int
27207cb209f5SScott Long aac_rx_get_outb_queue(struct aac_softc *sc)
27217cb209f5SScott Long {
272231a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
27237cb209f5SScott Long 
27247cb209f5SScott Long 	return(AAC_GETREG4(sc, AAC_RX_OQUE));
27257cb209f5SScott Long }
27267cb209f5SScott Long 
27277cb209f5SScott Long static int
27287cb209f5SScott Long aac_rkt_get_outb_queue(struct aac_softc *sc)
27297cb209f5SScott Long {
273031a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
27317cb209f5SScott Long 
27327cb209f5SScott Long 	return(AAC_GETREG4(sc, AAC_RKT_OQUE));
27337cb209f5SScott Long }
27347cb209f5SScott Long 
27357cb209f5SScott Long static void
27367cb209f5SScott Long aac_rx_set_outb_queue(struct aac_softc *sc, int index)
27377cb209f5SScott Long {
273831a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
27397cb209f5SScott Long 
27407cb209f5SScott Long 	AAC_SETREG4(sc, AAC_RX_OQUE, index);
27417cb209f5SScott Long }
27427cb209f5SScott Long 
27437cb209f5SScott Long static void
27447cb209f5SScott Long aac_rkt_set_outb_queue(struct aac_softc *sc, int index)
27457cb209f5SScott Long {
274631a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
27477cb209f5SScott Long 
27487cb209f5SScott Long 	AAC_SETREG4(sc, AAC_RKT_OQUE, index);
27497cb209f5SScott Long }
27507cb209f5SScott Long 
27517cb209f5SScott Long /*
2752914da7d0SScott Long  * Debugging and Diagnostics
2753914da7d0SScott Long  */
275435863739SMike Smith 
2755914da7d0SScott Long /*
275635863739SMike Smith  * Print some information about the controller.
275735863739SMike Smith  */
275835863739SMike Smith static void
275935863739SMike Smith aac_describe_controller(struct aac_softc *sc)
276035863739SMike Smith {
2761cbfd045bSScott Long 	struct aac_fib *fib;
276235863739SMike Smith 	struct aac_adapter_info	*info;
27637ea2d558SEd Maste 	char *adapter_type = "Adaptec RAID controller";
276435863739SMike Smith 
276531a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
276635863739SMike Smith 
276781b3da08SScott Long 	mtx_lock(&sc->aac_io_lock);
276803b5fe51SScott Long 	aac_alloc_sync_fib(sc, &fib);
2769cbfd045bSScott Long 
27707ea2d558SEd Maste 	if (sc->supported_options & AAC_SUPPORTED_SUPPLEMENT_ADAPTER_INFO) {
27717ea2d558SEd Maste 		fib->data[0] = 0;
27727ea2d558SEd Maste 		if (aac_sync_fib(sc, RequestSupplementAdapterInfo, 0, fib, 1))
27737ea2d558SEd Maste 			device_printf(sc->aac_dev,
27747ea2d558SEd Maste 			    "RequestSupplementAdapterInfo failed\n");
27757ea2d558SEd Maste 		else
27767ea2d558SEd Maste 			adapter_type = ((struct aac_supplement_adapter_info *)
27777ea2d558SEd Maste 			    &fib->data[0])->AdapterTypeText;
27787ea2d558SEd Maste 	}
27797ea2d558SEd Maste 	device_printf(sc->aac_dev, "%s, aac driver %d.%d.%d-%d\n",
27807ea2d558SEd Maste 		adapter_type,
27817ea2d558SEd Maste 		AAC_DRIVER_VERSION >> 24,
27827ea2d558SEd Maste 		(AAC_DRIVER_VERSION >> 16) & 0xFF,
27837ea2d558SEd Maste 		AAC_DRIVER_VERSION & 0xFF,
27847ea2d558SEd Maste 		AAC_DRIVER_BUILD);
27857ea2d558SEd Maste 
2786cbfd045bSScott Long 	fib->data[0] = 0;
2787cbfd045bSScott Long 	if (aac_sync_fib(sc, RequestAdapterInfo, 0, fib, 1)) {
278835863739SMike Smith 		device_printf(sc->aac_dev, "RequestAdapterInfo failed\n");
2789fe3cb0e1SScott Long 		aac_release_sync_fib(sc);
279081b3da08SScott Long 		mtx_unlock(&sc->aac_io_lock);
279135863739SMike Smith 		return;
279235863739SMike Smith 	}
279335863739SMike Smith 
2794bd971c49SScott Long 	/* save the kernel revision structure for later use */
2795bd971c49SScott Long 	info = (struct aac_adapter_info *)&fib->data[0];
2796bd971c49SScott Long 	sc->aac_revision = info->KernelRevision;
2797bd971c49SScott Long 
27987cb209f5SScott Long 
2799bd971c49SScott Long 	if (bootverbose) {
2800b1c56c68SScott Long 		device_printf(sc->aac_dev, "%s %dMHz, %dMB memory "
2801b1c56c68SScott Long 		    "(%dMB cache, %dMB execution), %s\n",
2802c6eafcf2SScott Long 		    aac_describe_code(aac_cpu_variant, info->CpuVariant),
2803b1c56c68SScott Long 		    info->ClockSpeed, info->TotalMem / (1024 * 1024),
2804b1c56c68SScott Long 		    info->BufferMem / (1024 * 1024),
2805b1c56c68SScott Long 		    info->ExecutionMem / (1024 * 1024),
2806914da7d0SScott Long 		    aac_describe_code(aac_battery_platform,
2807914da7d0SScott Long 		    info->batteryPlatform));
280835863739SMike Smith 
2809bd971c49SScott Long 		device_printf(sc->aac_dev,
2810bd971c49SScott Long 		    "Kernel %d.%d-%d, Build %d, S/N %6X\n",
281135863739SMike Smith 		    info->KernelRevision.external.comp.major,
281235863739SMike Smith 		    info->KernelRevision.external.comp.minor,
281335863739SMike Smith 		    info->KernelRevision.external.comp.dash,
281436e0bf6eSScott Long 		    info->KernelRevision.buildNumber,
281536e0bf6eSScott Long 		    (u_int32_t)(info->SerialNumber & 0xffffff));
2816fe3cb0e1SScott Long 
2817a6d35632SScott Long 		device_printf(sc->aac_dev, "Supported Options=%b\n",
2818a6d35632SScott Long 			      sc->supported_options,
2819a6d35632SScott Long 			      "\20"
2820a6d35632SScott Long 			      "\1SNAPSHOT"
2821a6d35632SScott Long 			      "\2CLUSTERS"
2822a6d35632SScott Long 			      "\3WCACHE"
2823a6d35632SScott Long 			      "\4DATA64"
2824a6d35632SScott Long 			      "\5HOSTTIME"
2825a6d35632SScott Long 			      "\6RAID50"
2826a6d35632SScott Long 			      "\7WINDOW4GB"
2827a6d35632SScott Long 			      "\10SCSIUPGD"
2828a6d35632SScott Long 			      "\11SOFTERR"
2829a6d35632SScott Long 			      "\12NORECOND"
2830a6d35632SScott Long 			      "\13SGMAP64"
2831a6d35632SScott Long 			      "\14ALARM"
28327cb209f5SScott Long 			      "\15NONDASD"
28337cb209f5SScott Long 			      "\16SCSIMGT"
28347cb209f5SScott Long 			      "\17RAIDSCSI"
28357cb209f5SScott Long 			      "\21ADPTINFO"
28367cb209f5SScott Long 			      "\22NEWCOMM"
28377cb209f5SScott Long 			      "\23ARRAY64BIT"
28387cb209f5SScott Long 			      "\24HEATSENSOR");
2839a6d35632SScott Long 	}
2840bd971c49SScott Long 	aac_release_sync_fib(sc);
284181b3da08SScott Long 	mtx_unlock(&sc->aac_io_lock);
284235863739SMike Smith }
284335863739SMike Smith 
2844914da7d0SScott Long /*
284535863739SMike Smith  * Look up a text description of a numeric error code and return a pointer to
284635863739SMike Smith  * same.
284735863739SMike Smith  */
284835863739SMike Smith static char *
284935863739SMike Smith aac_describe_code(struct aac_code_lookup *table, u_int32_t code)
285035863739SMike Smith {
285135863739SMike Smith 	int i;
285235863739SMike Smith 
285335863739SMike Smith 	for (i = 0; table[i].string != NULL; i++)
285435863739SMike Smith 		if (table[i].code == code)
285535863739SMike Smith 			return(table[i].string);
285635863739SMike Smith 	return(table[i + 1].string);
285735863739SMike Smith }
285835863739SMike Smith 
2859914da7d0SScott Long /*
2860914da7d0SScott Long  * Management Interface
2861914da7d0SScott Long  */
286235863739SMike Smith 
286335863739SMike Smith static int
286489c9c53dSPoul-Henning Kamp aac_open(struct cdev *dev, int flags, int fmt, d_thread_t *td)
286535863739SMike Smith {
2866914da7d0SScott Long 	struct aac_softc *sc;
286735863739SMike Smith 
2868914da7d0SScott Long 	sc = dev->si_drv1;
286931a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
2870a723a548SEd Maste 	sc->aac_open_cnt++;
287135863739SMike Smith 	sc->aac_state |= AAC_STATE_OPEN;
287235863739SMike Smith 
287335863739SMike Smith 	return 0;
287435863739SMike Smith }
287535863739SMike Smith 
287635863739SMike Smith static int
287789c9c53dSPoul-Henning Kamp aac_close(struct cdev *dev, int flags, int fmt, d_thread_t *td)
287835863739SMike Smith {
2879914da7d0SScott Long 	struct aac_softc *sc;
288035863739SMike Smith 
2881914da7d0SScott Long 	sc = dev->si_drv1;
288231a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
2883a723a548SEd Maste 	sc->aac_open_cnt--;
288435863739SMike Smith 	/* Mark this unit as no longer open  */
2885a723a548SEd Maste 	if (sc->aac_open_cnt == 0)
288635863739SMike Smith 		sc->aac_state &= ~AAC_STATE_OPEN;
288735863739SMike Smith 
288835863739SMike Smith 	return 0;
288935863739SMike Smith }
289035863739SMike Smith 
289135863739SMike Smith static int
289289c9c53dSPoul-Henning Kamp aac_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, d_thread_t *td)
289335863739SMike Smith {
2894914da7d0SScott Long 	union aac_statrequest *as;
2895914da7d0SScott Long 	struct aac_softc *sc;
28960b94a66eSMike Smith 	int error = 0;
289735863739SMike Smith 
2898914da7d0SScott Long 	as = (union aac_statrequest *)arg;
2899914da7d0SScott Long 	sc = dev->si_drv1;
290031a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
2901914da7d0SScott Long 
290235863739SMike Smith 	switch (cmd) {
29030b94a66eSMike Smith 	case AACIO_STATS:
29040b94a66eSMike Smith 		switch (as->as_item) {
29050b94a66eSMike Smith 		case AACQ_FREE:
29060b94a66eSMike Smith 		case AACQ_BIO:
29070b94a66eSMike Smith 		case AACQ_READY:
29080b94a66eSMike Smith 		case AACQ_BUSY:
2909c6eafcf2SScott Long 			bcopy(&sc->aac_qstat[as->as_item], &as->as_qstat,
2910c6eafcf2SScott Long 			      sizeof(struct aac_qstat));
29110b94a66eSMike Smith 			break;
29120b94a66eSMike Smith 		default:
29130b94a66eSMike Smith 			error = ENOENT;
29140b94a66eSMike Smith 			break;
29150b94a66eSMike Smith 		}
29160b94a66eSMike Smith 	break;
29170b94a66eSMike Smith 
291835863739SMike Smith 	case FSACTL_SENDFIB:
2919f355c0e0SEd Maste 	case FSACTL_SEND_LARGE_FIB:
2920fb0c27d7SScott Long 		arg = *(caddr_t*)arg;
2921fb0c27d7SScott Long 	case FSACTL_LNX_SENDFIB:
2922f355c0e0SEd Maste 	case FSACTL_LNX_SEND_LARGE_FIB:
292331a0399eSEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_SENDFIB");
292435863739SMike Smith 		error = aac_ioctl_sendfib(sc, arg);
292535863739SMike Smith 		break;
2926f355c0e0SEd Maste 	case FSACTL_SEND_RAW_SRB:
2927f355c0e0SEd Maste 		arg = *(caddr_t*)arg;
2928f355c0e0SEd Maste 	case FSACTL_LNX_SEND_RAW_SRB:
292931a0399eSEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_SEND_RAW_SRB");
2930f355c0e0SEd Maste 		error = aac_ioctl_send_raw_srb(sc, arg);
2931f355c0e0SEd Maste 		break;
293235863739SMike Smith 	case FSACTL_AIF_THREAD:
2933fb0c27d7SScott Long 	case FSACTL_LNX_AIF_THREAD:
293431a0399eSEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_AIF_THREAD");
293535863739SMike Smith 		error = EINVAL;
293635863739SMike Smith 		break;
293735863739SMike Smith 	case FSACTL_OPEN_GET_ADAPTER_FIB:
2938fb0c27d7SScott Long 		arg = *(caddr_t*)arg;
2939fb0c27d7SScott Long 	case FSACTL_LNX_OPEN_GET_ADAPTER_FIB:
294031a0399eSEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_OPEN_GET_ADAPTER_FIB");
2941a723a548SEd Maste 		error = aac_open_aif(sc, arg);
294235863739SMike Smith 		break;
294335863739SMike Smith 	case FSACTL_GET_NEXT_ADAPTER_FIB:
2944fb0c27d7SScott Long 		arg = *(caddr_t*)arg;
2945fb0c27d7SScott Long 	case FSACTL_LNX_GET_NEXT_ADAPTER_FIB:
294631a0399eSEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_GET_NEXT_ADAPTER_FIB");
2947fb0c27d7SScott Long 		error = aac_getnext_aif(sc, arg);
294835863739SMike Smith 		break;
294935863739SMike Smith 	case FSACTL_CLOSE_GET_ADAPTER_FIB:
2950a723a548SEd Maste 		arg = *(caddr_t*)arg;
2951fb0c27d7SScott Long 	case FSACTL_LNX_CLOSE_GET_ADAPTER_FIB:
295231a0399eSEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_CLOSE_GET_ADAPTER_FIB");
2953a723a548SEd Maste 		error = aac_close_aif(sc, arg);
295435863739SMike Smith 		break;
295535863739SMike Smith 	case FSACTL_MINIPORT_REV_CHECK:
2956fb0c27d7SScott Long 		arg = *(caddr_t*)arg;
2957fb0c27d7SScott Long 	case FSACTL_LNX_MINIPORT_REV_CHECK:
295831a0399eSEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_MINIPORT_REV_CHECK");
2959fb0c27d7SScott Long 		error = aac_rev_check(sc, arg);
296035863739SMike Smith 		break;
296136e0bf6eSScott Long 	case FSACTL_QUERY_DISK:
296236e0bf6eSScott Long 		arg = *(caddr_t*)arg;
296336e0bf6eSScott Long 	case FSACTL_LNX_QUERY_DISK:
296431a0399eSEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_QUERY_DISK");
296536e0bf6eSScott Long 		error = aac_query_disk(sc, arg);
296636e0bf6eSScott Long 		break;
296736e0bf6eSScott Long 	case FSACTL_DELETE_DISK:
296836e0bf6eSScott Long 	case FSACTL_LNX_DELETE_DISK:
2969914da7d0SScott Long 		/*
2970914da7d0SScott Long 		 * We don't trust the underland to tell us when to delete a
2971914da7d0SScott Long 		 * container, rather we rely on an AIF coming from the
2972914da7d0SScott Long 		 * controller
2973914da7d0SScott Long 		 */
297436e0bf6eSScott Long 		error = 0;
297536e0bf6eSScott Long 		break;
29767cb209f5SScott Long 	case FSACTL_GET_PCI_INFO:
29777cb209f5SScott Long 		arg = *(caddr_t*)arg;
29787cb209f5SScott Long 	case FSACTL_LNX_GET_PCI_INFO:
297931a0399eSEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_GET_PCI_INFO");
29807cb209f5SScott Long 		error = aac_get_pci_info(sc, arg);
29817cb209f5SScott Long 		break;
298235863739SMike Smith 	default:
298331a0399eSEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "unsupported cmd 0x%lx\n", cmd);
298435863739SMike Smith 		error = EINVAL;
298535863739SMike Smith 		break;
298635863739SMike Smith 	}
298735863739SMike Smith 	return(error);
298835863739SMike Smith }
298935863739SMike Smith 
2990b3457b51SScott Long static int
299189c9c53dSPoul-Henning Kamp aac_poll(struct cdev *dev, int poll_events, d_thread_t *td)
2992b3457b51SScott Long {
2993b3457b51SScott Long 	struct aac_softc *sc;
2994b3457b51SScott Long 	int revents;
2995b3457b51SScott Long 
2996b3457b51SScott Long 	sc = dev->si_drv1;
2997b3457b51SScott Long 	revents = 0;
2998b3457b51SScott Long 
2999bb6fe253SScott Long 	mtx_lock(&sc->aac_aifq_lock);
3000b3457b51SScott Long 	if ((poll_events & (POLLRDNORM | POLLIN)) != 0) {
3001a723a548SEd Maste 		if (sc->aifq_idx != 0 || sc->aifq_filled)
3002b3457b51SScott Long 			revents |= poll_events & (POLLIN | POLLRDNORM);
3003b3457b51SScott Long 	}
3004bb6fe253SScott Long 	mtx_unlock(&sc->aac_aifq_lock);
3005b3457b51SScott Long 
3006b3457b51SScott Long 	if (revents == 0) {
3007b3457b51SScott Long 		if (poll_events & (POLLIN | POLLRDNORM))
3008b3457b51SScott Long 			selrecord(td, &sc->rcv_select);
3009b3457b51SScott Long 	}
3010b3457b51SScott Long 
3011b3457b51SScott Long 	return (revents);
3012b3457b51SScott Long }
3013b3457b51SScott Long 
30147cb209f5SScott Long static void
30157cb209f5SScott Long aac_ioctl_event(struct aac_softc *sc, struct aac_event *event, void *arg)
30167cb209f5SScott Long {
30177cb209f5SScott Long 
30187cb209f5SScott Long 	switch (event->ev_type) {
30197cb209f5SScott Long 	case AAC_EVENT_CMFREE:
30200c40d5beSEd Maste 		mtx_assert(&sc->aac_io_lock, MA_OWNED);
30211a681311SLuoqi Chen 		if (aac_alloc_command(sc, (struct aac_command **)arg)) {
30227cb209f5SScott Long 			aac_add_event(sc, event);
30237cb209f5SScott Long 			return;
30247cb209f5SScott Long 		}
30257cb209f5SScott Long 		free(event, M_AACBUF);
30268eeb2ca6SScott Long 		wakeup(arg);
30277cb209f5SScott Long 		break;
30287cb209f5SScott Long 	default:
30297cb209f5SScott Long 		break;
30307cb209f5SScott Long 	}
30317cb209f5SScott Long }
30327cb209f5SScott Long 
3033914da7d0SScott Long /*
303435863739SMike Smith  * Send a FIB supplied from userspace
303535863739SMike Smith  */
303635863739SMike Smith static int
303735863739SMike Smith aac_ioctl_sendfib(struct aac_softc *sc, caddr_t ufib)
303835863739SMike Smith {
303935863739SMike Smith 	struct aac_command *cm;
304035863739SMike Smith 	int size, error;
304135863739SMike Smith 
304231a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
304335863739SMike Smith 
304435863739SMike Smith 	cm = NULL;
304535863739SMike Smith 
304635863739SMike Smith 	/*
304735863739SMike Smith 	 * Get a command
304835863739SMike Smith 	 */
3049bb6fe253SScott Long 	mtx_lock(&sc->aac_io_lock);
305035863739SMike Smith 	if (aac_alloc_command(sc, &cm)) {
30517cb209f5SScott Long 		struct aac_event *event;
30527cb209f5SScott Long 
30537cb209f5SScott Long 		event = malloc(sizeof(struct aac_event), M_AACBUF,
30547cb209f5SScott Long 		    M_NOWAIT | M_ZERO);
30557cb209f5SScott Long 		if (event == NULL) {
305635863739SMike Smith 			error = EBUSY;
3057f16627aaSEd Maste 			mtx_unlock(&sc->aac_io_lock);
305835863739SMike Smith 			goto out;
305935863739SMike Smith 		}
30607cb209f5SScott Long 		event->ev_type = AAC_EVENT_CMFREE;
30617cb209f5SScott Long 		event->ev_callback = aac_ioctl_event;
30627cb209f5SScott Long 		event->ev_arg = &cm;
30637cb209f5SScott Long 		aac_add_event(sc, event);
30648eeb2ca6SScott Long 		msleep(&cm, &sc->aac_io_lock, 0, "sendfib", 0);
30657cb209f5SScott Long 	}
306693cfca22SScott Long 	mtx_unlock(&sc->aac_io_lock);
306735863739SMike Smith 
306835863739SMike Smith 	/*
306935863739SMike Smith 	 * Fetch the FIB header, then re-copy to get data as well.
307035863739SMike Smith 	 */
3071914da7d0SScott Long 	if ((error = copyin(ufib, cm->cm_fib,
3072914da7d0SScott Long 			    sizeof(struct aac_fib_header))) != 0)
307335863739SMike Smith 		goto out;
307435863739SMike Smith 	size = cm->cm_fib->Header.Size + sizeof(struct aac_fib_header);
3075f355c0e0SEd Maste 	if (size > sc->aac_max_fib_size) {
3076f355c0e0SEd Maste 		device_printf(sc->aac_dev, "incoming FIB oversized (%d > %d)\n",
3077f355c0e0SEd Maste 			      size, sc->aac_max_fib_size);
3078f355c0e0SEd Maste 		size = sc->aac_max_fib_size;
307935863739SMike Smith 	}
308035863739SMike Smith 	if ((error = copyin(ufib, cm->cm_fib, size)) != 0)
308135863739SMike Smith 		goto out;
308235863739SMike Smith 	cm->cm_fib->Header.Size = size;
30832b3b0f17SScott Long 	cm->cm_timestamp = time_uptime;
308435863739SMike Smith 
308535863739SMike Smith 	/*
308635863739SMike Smith 	 * Pass the FIB to the controller, wait for it to complete.
308735863739SMike Smith 	 */
308893cfca22SScott Long 	mtx_lock(&sc->aac_io_lock);
3089f16627aaSEd Maste 	error = aac_wait_command(cm);
3090f16627aaSEd Maste 	mtx_unlock(&sc->aac_io_lock);
3091f16627aaSEd Maste 	if (error != 0) {
309270545d1aSScott Long 		device_printf(sc->aac_dev,
309370545d1aSScott Long 			      "aac_wait_command return %d\n", error);
309435863739SMike Smith 		goto out;
3095b3457b51SScott Long 	}
309635863739SMike Smith 
309735863739SMike Smith 	/*
309835863739SMike Smith 	 * Copy the FIB and data back out to the caller.
309935863739SMike Smith 	 */
310035863739SMike Smith 	size = cm->cm_fib->Header.Size;
3101f355c0e0SEd Maste 	if (size > sc->aac_max_fib_size) {
3102f355c0e0SEd Maste 		device_printf(sc->aac_dev, "outbound FIB oversized (%d > %d)\n",
3103f355c0e0SEd Maste 			      size, sc->aac_max_fib_size);
3104f355c0e0SEd Maste 		size = sc->aac_max_fib_size;
310535863739SMike Smith 	}
310635863739SMike Smith 	error = copyout(cm->cm_fib, ufib, size);
310735863739SMike Smith 
310835863739SMike Smith out:
3109f6c4dd3fSScott Long 	if (cm != NULL) {
3110f16627aaSEd Maste 		mtx_lock(&sc->aac_io_lock);
311135863739SMike Smith 		aac_release_command(cm);
3112bb6fe253SScott Long 		mtx_unlock(&sc->aac_io_lock);
3113f16627aaSEd Maste 	}
311435863739SMike Smith 	return(error);
311535863739SMike Smith }
311635863739SMike Smith 
3117914da7d0SScott Long /*
3118f355c0e0SEd Maste  * Send a passthrough FIB supplied from userspace
3119f355c0e0SEd Maste  */
3120f355c0e0SEd Maste static int
3121f355c0e0SEd Maste aac_ioctl_send_raw_srb(struct aac_softc *sc, caddr_t arg)
3122f355c0e0SEd Maste {
3123f355c0e0SEd Maste 	return (EINVAL);
3124f355c0e0SEd Maste }
3125f355c0e0SEd Maste 
3126f355c0e0SEd Maste /*
312735863739SMike Smith  * Handle an AIF sent to us by the controller; queue it for later reference.
312836e0bf6eSScott Long  * If the queue fills up, then drop the older entries.
312935863739SMike Smith  */
313035863739SMike Smith static void
313136e0bf6eSScott Long aac_handle_aif(struct aac_softc *sc, struct aac_fib *fib)
313235863739SMike Smith {
313336e0bf6eSScott Long 	struct aac_aif_command *aif;
313436e0bf6eSScott Long 	struct aac_container *co, *co_next;
3135a723a548SEd Maste 	struct aac_fib_context *ctx;
313604f4d586SEd Maste 	struct aac_mntinforesp *mir;
3137a723a548SEd Maste 	int next, current, found;
3138795d7dc0SScott Long 	int count = 0, added = 0, i = 0;
313935863739SMike Smith 
314031a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
314135863739SMike Smith 
314236e0bf6eSScott Long 	aif = (struct aac_aif_command*)&fib->data[0];
314336e0bf6eSScott Long 	aac_print_aif(sc, aif);
314436e0bf6eSScott Long 
314536e0bf6eSScott Long 	/* Is it an event that we should care about? */
314636e0bf6eSScott Long 	switch (aif->command) {
314736e0bf6eSScott Long 	case AifCmdEventNotify:
314836e0bf6eSScott Long 		switch (aif->data.EN.type) {
314936e0bf6eSScott Long 		case AifEnAddContainer:
315036e0bf6eSScott Long 		case AifEnDeleteContainer:
315136e0bf6eSScott Long 			/*
3152914da7d0SScott Long 			 * A container was added or deleted, but the message
3153914da7d0SScott Long 			 * doesn't tell us anything else!  Re-enumerate the
3154914da7d0SScott Long 			 * containers and sort things out.
315536e0bf6eSScott Long 			 */
315603b5fe51SScott Long 			aac_alloc_sync_fib(sc, &fib);
315736e0bf6eSScott Long 			do {
315836e0bf6eSScott Long 				/*
3159914da7d0SScott Long 				 * Ask the controller for its containers one at
3160914da7d0SScott Long 				 * a time.
3161914da7d0SScott Long 				 * XXX What if the controller's list changes
3162914da7d0SScott Long 				 * midway through this enumaration?
316336e0bf6eSScott Long 				 * XXX This should be done async.
316436e0bf6eSScott Long 				 */
316504f4d586SEd Maste 				if ((mir = aac_get_container_info(sc, fib, i)) == NULL)
316636e0bf6eSScott Long 					continue;
316704f4d586SEd Maste 				if (i == 0)
3168795d7dc0SScott Long 					count = mir->MntRespCount;
316936e0bf6eSScott Long 				/*
3170914da7d0SScott Long 				 * Check the container against our list.
3171914da7d0SScott Long 				 * co->co_found was already set to 0 in a
3172914da7d0SScott Long 				 * previous run.
317336e0bf6eSScott Long 				 */
3174cbfd045bSScott Long 				if ((mir->Status == ST_OK) &&
3175cbfd045bSScott Long 				    (mir->MntTable[0].VolType != CT_NONE)) {
317636e0bf6eSScott Long 					found = 0;
3177914da7d0SScott Long 					TAILQ_FOREACH(co,
3178914da7d0SScott Long 						      &sc->aac_container_tqh,
3179914da7d0SScott Long 						      co_link) {
318036e0bf6eSScott Long 						if (co->co_mntobj.ObjectId ==
3181cbfd045bSScott Long 						    mir->MntTable[0].ObjectId) {
318236e0bf6eSScott Long 							co->co_found = 1;
318336e0bf6eSScott Long 							found = 1;
318436e0bf6eSScott Long 							break;
318536e0bf6eSScott Long 						}
318636e0bf6eSScott Long 					}
3187914da7d0SScott Long 					/*
3188914da7d0SScott Long 					 * If the container matched, continue
3189914da7d0SScott Long 					 * in the list.
3190914da7d0SScott Long 					 */
319136e0bf6eSScott Long 					if (found) {
319236e0bf6eSScott Long 						i++;
319336e0bf6eSScott Long 						continue;
319436e0bf6eSScott Long 					}
319536e0bf6eSScott Long 
319636e0bf6eSScott Long 					/*
3197914da7d0SScott Long 					 * This is a new container.  Do all the
319870545d1aSScott Long 					 * appropriate things to set it up.
319970545d1aSScott Long 					 */
3200cbfd045bSScott Long 					aac_add_container(sc, mir, 1);
320136e0bf6eSScott Long 					added = 1;
320236e0bf6eSScott Long 				}
320336e0bf6eSScott Long 				i++;
3204795d7dc0SScott Long 			} while ((i < count) && (i < AAC_MAX_CONTAINERS));
3205cbfd045bSScott Long 			aac_release_sync_fib(sc);
320636e0bf6eSScott Long 
320736e0bf6eSScott Long 			/*
3208914da7d0SScott Long 			 * Go through our list of containers and see which ones
3209914da7d0SScott Long 			 * were not marked 'found'.  Since the controller didn't
3210914da7d0SScott Long 			 * list them they must have been deleted.  Do the
3211914da7d0SScott Long 			 * appropriate steps to destroy the device.  Also reset
3212914da7d0SScott Long 			 * the co->co_found field.
321336e0bf6eSScott Long 			 */
321436e0bf6eSScott Long 			co = TAILQ_FIRST(&sc->aac_container_tqh);
321536e0bf6eSScott Long 			while (co != NULL) {
321636e0bf6eSScott Long 				if (co->co_found == 0) {
32177cb209f5SScott Long 					mtx_unlock(&sc->aac_io_lock);
32187cb209f5SScott Long 					mtx_lock(&Giant);
3219914da7d0SScott Long 					device_delete_child(sc->aac_dev,
3220914da7d0SScott Long 							    co->co_disk);
32217cb209f5SScott Long 					mtx_unlock(&Giant);
32227cb209f5SScott Long 					mtx_lock(&sc->aac_io_lock);
322336e0bf6eSScott Long 					co_next = TAILQ_NEXT(co, co_link);
3224bb6fe253SScott Long 					mtx_lock(&sc->aac_container_lock);
3225914da7d0SScott Long 					TAILQ_REMOVE(&sc->aac_container_tqh, co,
3226914da7d0SScott Long 						     co_link);
3227bb6fe253SScott Long 					mtx_unlock(&sc->aac_container_lock);
3228ba1d57e7SScott Long 					free(co, M_AACBUF);
322936e0bf6eSScott Long 					co = co_next;
323036e0bf6eSScott Long 				} else {
323136e0bf6eSScott Long 					co->co_found = 0;
323236e0bf6eSScott Long 					co = TAILQ_NEXT(co, co_link);
323336e0bf6eSScott Long 				}
323436e0bf6eSScott Long 			}
323536e0bf6eSScott Long 
323636e0bf6eSScott Long 			/* Attach the newly created containers */
32377cb209f5SScott Long 			if (added) {
32387cb209f5SScott Long 				mtx_unlock(&sc->aac_io_lock);
32397cb209f5SScott Long 				mtx_lock(&Giant);
324036e0bf6eSScott Long 				bus_generic_attach(sc->aac_dev);
32417cb209f5SScott Long 				mtx_unlock(&Giant);
32427cb209f5SScott Long 				mtx_lock(&sc->aac_io_lock);
32437cb209f5SScott Long 			}
324436e0bf6eSScott Long 
324536e0bf6eSScott Long 			break;
324636e0bf6eSScott Long 
324736e0bf6eSScott Long 		default:
324836e0bf6eSScott Long 			break;
324936e0bf6eSScott Long 		}
325036e0bf6eSScott Long 
325136e0bf6eSScott Long 	default:
325236e0bf6eSScott Long 		break;
325336e0bf6eSScott Long 	}
325436e0bf6eSScott Long 
325536e0bf6eSScott Long 	/* Copy the AIF data to the AIF queue for ioctl retrieval */
3256bb6fe253SScott Long 	mtx_lock(&sc->aac_aifq_lock);
3257a723a548SEd Maste 	current = sc->aifq_idx;
3258a723a548SEd Maste 	next = (current + 1) % AAC_AIFQ_LENGTH;
3259a723a548SEd Maste 	if (next == 0)
3260a723a548SEd Maste 		sc->aifq_filled = 1;
3261a723a548SEd Maste 	bcopy(fib, &sc->aac_aifq[current], sizeof(struct aac_fib));
3262a723a548SEd Maste 	/* modify AIF contexts */
3263a723a548SEd Maste 	if (sc->aifq_filled) {
3264a723a548SEd Maste 		for (ctx = sc->fibctx; ctx; ctx = ctx->next) {
3265a723a548SEd Maste 			if (next == ctx->ctx_idx)
3266a723a548SEd Maste 				ctx->ctx_wrap = 1;
3267a723a548SEd Maste 			else if (current == ctx->ctx_idx && ctx->ctx_wrap)
3268a723a548SEd Maste 				ctx->ctx_idx = next;
3269a723a548SEd Maste 		}
3270a723a548SEd Maste 	}
3271a723a548SEd Maste 	sc->aifq_idx = next;
3272b3457b51SScott Long 	/* On the off chance that someone is sleeping for an aif... */
327335863739SMike Smith 	if (sc->aac_state & AAC_STATE_AIF_SLEEPER)
327435863739SMike Smith 		wakeup(sc->aac_aifq);
3275b3457b51SScott Long 	/* Wakeup any poll()ers */
3276512824f8SSeigo Tanimura 	selwakeuppri(&sc->rcv_select, PRIBIO);
3277bb6fe253SScott Long 	mtx_unlock(&sc->aac_aifq_lock);
327836e0bf6eSScott Long 
327936e0bf6eSScott Long 	return;
328035863739SMike Smith }
328135863739SMike Smith 
3282914da7d0SScott Long /*
32830b94a66eSMike Smith  * Return the Revision of the driver to userspace and check to see if the
328436e0bf6eSScott Long  * userspace app is possibly compatible.  This is extremely bogus since
328536e0bf6eSScott Long  * our driver doesn't follow Adaptec's versioning system.  Cheat by just
328636e0bf6eSScott Long  * returning what the card reported.
328735863739SMike Smith  */
328835863739SMike Smith static int
3289fb0c27d7SScott Long aac_rev_check(struct aac_softc *sc, caddr_t udata)
329035863739SMike Smith {
329135863739SMike Smith 	struct aac_rev_check rev_check;
329235863739SMike Smith 	struct aac_rev_check_resp rev_check_resp;
329335863739SMike Smith 	int error = 0;
329435863739SMike Smith 
329531a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
329635863739SMike Smith 
329735863739SMike Smith 	/*
329835863739SMike Smith 	 * Copyin the revision struct from userspace
329935863739SMike Smith 	 */
3300c6eafcf2SScott Long 	if ((error = copyin(udata, (caddr_t)&rev_check,
3301c6eafcf2SScott Long 			sizeof(struct aac_rev_check))) != 0) {
330235863739SMike Smith 		return error;
330335863739SMike Smith 	}
330435863739SMike Smith 
330531a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "Userland revision= %d\n",
3306914da7d0SScott Long 	      rev_check.callingRevision.buildNumber);
330735863739SMike Smith 
330835863739SMike Smith 	/*
330935863739SMike Smith 	 * Doctor up the response struct.
331035863739SMike Smith 	 */
331135863739SMike Smith 	rev_check_resp.possiblyCompatible = 1;
3312914da7d0SScott Long 	rev_check_resp.adapterSWRevision.external.ul =
3313914da7d0SScott Long 	    sc->aac_revision.external.ul;
3314914da7d0SScott Long 	rev_check_resp.adapterSWRevision.buildNumber =
3315914da7d0SScott Long 	    sc->aac_revision.buildNumber;
331635863739SMike Smith 
3317c6eafcf2SScott Long 	return(copyout((caddr_t)&rev_check_resp, udata,
3318c6eafcf2SScott Long 			sizeof(struct aac_rev_check_resp)));
331935863739SMike Smith }
332035863739SMike Smith 
3321914da7d0SScott Long /*
3322a723a548SEd Maste  * Pass the fib context to the caller
3323a723a548SEd Maste  */
3324a723a548SEd Maste static int
3325a723a548SEd Maste aac_open_aif(struct aac_softc *sc, caddr_t arg)
3326a723a548SEd Maste {
3327a723a548SEd Maste 	struct aac_fib_context *fibctx, *ctx;
3328a723a548SEd Maste 	int error = 0;
3329a723a548SEd Maste 
333031a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
3331a723a548SEd Maste 
3332a723a548SEd Maste 	fibctx = malloc(sizeof(struct aac_fib_context), M_AACBUF, M_NOWAIT|M_ZERO);
3333a723a548SEd Maste 	if (fibctx == NULL)
3334a723a548SEd Maste 		return (ENOMEM);
3335a723a548SEd Maste 
3336a723a548SEd Maste 	mtx_lock(&sc->aac_aifq_lock);
3337a723a548SEd Maste 	/* all elements are already 0, add to queue */
3338a723a548SEd Maste 	if (sc->fibctx == NULL)
3339a723a548SEd Maste 		sc->fibctx = fibctx;
3340a723a548SEd Maste 	else {
3341a723a548SEd Maste 		for (ctx = sc->fibctx; ctx->next; ctx = ctx->next)
3342a723a548SEd Maste 			;
3343a723a548SEd Maste 		ctx->next = fibctx;
3344a723a548SEd Maste 		fibctx->prev = ctx;
3345a723a548SEd Maste 	}
3346a723a548SEd Maste 
3347a723a548SEd Maste 	/* evaluate unique value */
3348a723a548SEd Maste 	fibctx->unique = (*(u_int32_t *)&fibctx & 0xffffffff);
3349a723a548SEd Maste 	ctx = sc->fibctx;
3350a723a548SEd Maste 	while (ctx != fibctx) {
3351a723a548SEd Maste 		if (ctx->unique == fibctx->unique) {
3352a723a548SEd Maste 			fibctx->unique++;
3353a723a548SEd Maste 			ctx = sc->fibctx;
3354a723a548SEd Maste 		} else {
3355a723a548SEd Maste 			ctx = ctx->next;
3356a723a548SEd Maste 		}
3357a723a548SEd Maste 	}
3358a723a548SEd Maste 	mtx_unlock(&sc->aac_aifq_lock);
3359a723a548SEd Maste 
3360a723a548SEd Maste 	error = copyout(&fibctx->unique, (void *)arg, sizeof(u_int32_t));
3361a723a548SEd Maste 	if (error)
3362a723a548SEd Maste 		aac_close_aif(sc, (caddr_t)ctx);
3363a723a548SEd Maste 	return error;
3364a723a548SEd Maste }
3365a723a548SEd Maste 
3366a723a548SEd Maste /*
3367a723a548SEd Maste  * Close the caller's fib context
3368a723a548SEd Maste  */
3369a723a548SEd Maste static int
3370a723a548SEd Maste aac_close_aif(struct aac_softc *sc, caddr_t arg)
3371a723a548SEd Maste {
3372a723a548SEd Maste 	struct aac_fib_context *ctx;
3373a723a548SEd Maste 
337431a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
3375a723a548SEd Maste 
3376a723a548SEd Maste 	mtx_lock(&sc->aac_aifq_lock);
3377a723a548SEd Maste 	for (ctx = sc->fibctx; ctx; ctx = ctx->next) {
3378a723a548SEd Maste 		if (ctx->unique == *(uint32_t *)&arg) {
3379a723a548SEd Maste 			if (ctx == sc->fibctx)
3380a723a548SEd Maste 				sc->fibctx = NULL;
3381a723a548SEd Maste 			else {
3382a723a548SEd Maste 				ctx->prev->next = ctx->next;
3383a723a548SEd Maste 				if (ctx->next)
3384a723a548SEd Maste 					ctx->next->prev = ctx->prev;
3385a723a548SEd Maste 			}
3386a723a548SEd Maste 			break;
3387a723a548SEd Maste 		}
3388a723a548SEd Maste 	}
3389a723a548SEd Maste 	mtx_unlock(&sc->aac_aifq_lock);
3390a723a548SEd Maste 	if (ctx)
3391a723a548SEd Maste 		free(ctx, M_AACBUF);
3392a723a548SEd Maste 
3393a723a548SEd Maste 	return 0;
3394a723a548SEd Maste }
3395a723a548SEd Maste 
3396a723a548SEd Maste /*
339735863739SMike Smith  * Pass the caller the next AIF in their queue
339835863739SMike Smith  */
339935863739SMike Smith static int
3400fb0c27d7SScott Long aac_getnext_aif(struct aac_softc *sc, caddr_t arg)
340135863739SMike Smith {
340235863739SMike Smith 	struct get_adapter_fib_ioctl agf;
3403a723a548SEd Maste 	struct aac_fib_context *ctx;
34049e2e96d8SScott Long 	int error;
340535863739SMike Smith 
340631a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
340735863739SMike Smith 
340835863739SMike Smith 	if ((error = copyin(arg, &agf, sizeof(agf))) == 0) {
3409a723a548SEd Maste 		for (ctx = sc->fibctx; ctx; ctx = ctx->next) {
3410a723a548SEd Maste 			if (agf.AdapterFibContext == ctx->unique)
3411a723a548SEd Maste 				break;
3412a723a548SEd Maste 		}
3413a723a548SEd Maste 		if (!ctx)
3414a723a548SEd Maste 			return (EFAULT);
341535863739SMike Smith 
3416a723a548SEd Maste 		error = aac_return_aif(sc, ctx, agf.AifFib);
3417a723a548SEd Maste 		if (error == EAGAIN && agf.Wait) {
341831a0399eSEd Maste 			fwprintf(sc, HBA_FLAGS_DBG_AIF_B, "aac_getnext_aif(): waiting for AIF");
341935863739SMike Smith 			sc->aac_state |= AAC_STATE_AIF_SLEEPER;
342035863739SMike Smith 			while (error == EAGAIN) {
3421914da7d0SScott Long 				error = tsleep(sc->aac_aifq, PRIBIO |
3422914da7d0SScott Long 					       PCATCH, "aacaif", 0);
342335863739SMike Smith 				if (error == 0)
3424a723a548SEd Maste 					error = aac_return_aif(sc, ctx, agf.AifFib);
342535863739SMike Smith 			}
342635863739SMike Smith 			sc->aac_state &= ~AAC_STATE_AIF_SLEEPER;
342735863739SMike Smith 		}
342835863739SMike Smith 	}
342935863739SMike Smith 	return(error);
343035863739SMike Smith }
343135863739SMike Smith 
3432914da7d0SScott Long /*
34330b94a66eSMike Smith  * Hand the next AIF off the top of the queue out to userspace.
34340b94a66eSMike Smith  */
34350b94a66eSMike Smith static int
3436a723a548SEd Maste aac_return_aif(struct aac_softc *sc, struct aac_fib_context *ctx, caddr_t uptr)
34370b94a66eSMike Smith {
3438a723a548SEd Maste 	int current, error;
34390b94a66eSMike Smith 
344031a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
34410b94a66eSMike Smith 
3442bb6fe253SScott Long 	mtx_lock(&sc->aac_aifq_lock);
3443a723a548SEd Maste 	current = ctx->ctx_idx;
3444a723a548SEd Maste 	if (current == sc->aifq_idx && !ctx->ctx_wrap) {
3445a723a548SEd Maste 		/* empty */
3446bb6fe253SScott Long 		mtx_unlock(&sc->aac_aifq_lock);
34473df780cfSScott Long 		return (EAGAIN);
34483df780cfSScott Long 	}
3449a723a548SEd Maste 	error =
3450a723a548SEd Maste 		copyout(&sc->aac_aifq[current], (void *)uptr, sizeof(struct aac_fib));
345136e0bf6eSScott Long 	if (error)
345270545d1aSScott Long 		device_printf(sc->aac_dev,
345370545d1aSScott Long 		    "aac_return_aif: copyout returned %d\n", error);
3454a723a548SEd Maste 	else {
3455a723a548SEd Maste 		ctx->ctx_wrap = 0;
3456a723a548SEd Maste 		ctx->ctx_idx = (current + 1) % AAC_AIFQ_LENGTH;
3457a723a548SEd Maste 	}
3458bb6fe253SScott Long 	mtx_unlock(&sc->aac_aifq_lock);
34590b94a66eSMike Smith 	return(error);
34600b94a66eSMike Smith }
346136e0bf6eSScott Long 
34627cb209f5SScott Long static int
34637cb209f5SScott Long aac_get_pci_info(struct aac_softc *sc, caddr_t uptr)
34647cb209f5SScott Long {
34657cb209f5SScott Long 	struct aac_pci_info {
34667cb209f5SScott Long 		u_int32_t bus;
34677cb209f5SScott Long 		u_int32_t slot;
34687cb209f5SScott Long 	} pciinf;
34697cb209f5SScott Long 	int error;
34707cb209f5SScott Long 
347131a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
34727cb209f5SScott Long 
34737cb209f5SScott Long 	pciinf.bus = pci_get_bus(sc->aac_dev);
34747cb209f5SScott Long 	pciinf.slot = pci_get_slot(sc->aac_dev);
34757cb209f5SScott Long 
34767cb209f5SScott Long 	error = copyout((caddr_t)&pciinf, uptr,
34777cb209f5SScott Long 			sizeof(struct aac_pci_info));
34787cb209f5SScott Long 
34797cb209f5SScott Long 	return (error);
34807cb209f5SScott Long }
34817cb209f5SScott Long 
3482914da7d0SScott Long /*
348336e0bf6eSScott Long  * Give the userland some information about the container.  The AAC arch
348436e0bf6eSScott Long  * expects the driver to be a SCSI passthrough type driver, so it expects
348536e0bf6eSScott Long  * the containers to have b:t:l numbers.  Fake it.
348636e0bf6eSScott Long  */
348736e0bf6eSScott Long static int
348836e0bf6eSScott Long aac_query_disk(struct aac_softc *sc, caddr_t uptr)
348936e0bf6eSScott Long {
349036e0bf6eSScott Long 	struct aac_query_disk query_disk;
349136e0bf6eSScott Long 	struct aac_container *co;
3492914da7d0SScott Long 	struct aac_disk	*disk;
349336e0bf6eSScott Long 	int error, id;
349436e0bf6eSScott Long 
349531a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
349636e0bf6eSScott Long 
3497914da7d0SScott Long 	disk = NULL;
3498914da7d0SScott Long 
3499914da7d0SScott Long 	error = copyin(uptr, (caddr_t)&query_disk,
3500914da7d0SScott Long 		       sizeof(struct aac_query_disk));
350136e0bf6eSScott Long 	if (error)
350236e0bf6eSScott Long 		return (error);
350336e0bf6eSScott Long 
350436e0bf6eSScott Long 	id = query_disk.ContainerNumber;
350536e0bf6eSScott Long 	if (id == -1)
350636e0bf6eSScott Long 		return (EINVAL);
350736e0bf6eSScott Long 
3508bb6fe253SScott Long 	mtx_lock(&sc->aac_container_lock);
350936e0bf6eSScott Long 	TAILQ_FOREACH(co, &sc->aac_container_tqh, co_link) {
351036e0bf6eSScott Long 		if (co->co_mntobj.ObjectId == id)
351136e0bf6eSScott Long 			break;
351236e0bf6eSScott Long 		}
351336e0bf6eSScott Long 
351436e0bf6eSScott Long 	if (co == NULL) {
351536e0bf6eSScott Long 			query_disk.Valid = 0;
351636e0bf6eSScott Long 			query_disk.Locked = 0;
351736e0bf6eSScott Long 			query_disk.Deleted = 1;		/* XXX is this right? */
351836e0bf6eSScott Long 	} else {
351936e0bf6eSScott Long 		disk = device_get_softc(co->co_disk);
352036e0bf6eSScott Long 		query_disk.Valid = 1;
3521914da7d0SScott Long 		query_disk.Locked =
3522914da7d0SScott Long 		    (disk->ad_flags & AAC_DISK_OPEN) ? 1 : 0;
352336e0bf6eSScott Long 		query_disk.Deleted = 0;
3524b3457b51SScott Long 		query_disk.Bus = device_get_unit(sc->aac_dev);
352536e0bf6eSScott Long 		query_disk.Target = disk->unit;
352636e0bf6eSScott Long 		query_disk.Lun = 0;
352736e0bf6eSScott Long 		query_disk.UnMapped = 0;
35287540e65eSScott Long 		sprintf(&query_disk.diskDeviceName[0], "%s%d",
35290b7ed341SPoul-Henning Kamp 		        disk->ad_disk->d_name, disk->ad_disk->d_unit);
353036e0bf6eSScott Long 	}
3531bb6fe253SScott Long 	mtx_unlock(&sc->aac_container_lock);
353236e0bf6eSScott Long 
3533914da7d0SScott Long 	error = copyout((caddr_t)&query_disk, uptr,
3534914da7d0SScott Long 			sizeof(struct aac_query_disk));
353536e0bf6eSScott Long 
353636e0bf6eSScott Long 	return (error);
353736e0bf6eSScott Long }
353836e0bf6eSScott Long 
3539fe3cb0e1SScott Long static void
3540fe3cb0e1SScott Long aac_get_bus_info(struct aac_softc *sc)
3541fe3cb0e1SScott Long {
3542fe3cb0e1SScott Long 	struct aac_fib *fib;
3543fe3cb0e1SScott Long 	struct aac_ctcfg *c_cmd;
3544fe3cb0e1SScott Long 	struct aac_ctcfg_resp *c_resp;
3545fe3cb0e1SScott Long 	struct aac_vmioctl *vmi;
3546fe3cb0e1SScott Long 	struct aac_vmi_businf_resp *vmi_resp;
3547fe3cb0e1SScott Long 	struct aac_getbusinf businfo;
354870545d1aSScott Long 	struct aac_sim *caminf;
3549fe3cb0e1SScott Long 	device_t child;
3550fe3cb0e1SScott Long 	int i, found, error;
3551fe3cb0e1SScott Long 
35521ffe41c1SChristian S.J. Peron 	mtx_lock(&sc->aac_io_lock);
355303b5fe51SScott Long 	aac_alloc_sync_fib(sc, &fib);
3554fe3cb0e1SScott Long 	c_cmd = (struct aac_ctcfg *)&fib->data[0];
355539ee03c3SScott Long 	bzero(c_cmd, sizeof(struct aac_ctcfg));
3556fe3cb0e1SScott Long 
3557fe3cb0e1SScott Long 	c_cmd->Command = VM_ContainerConfig;
3558fe3cb0e1SScott Long 	c_cmd->cmd = CT_GET_SCSI_METHOD;
3559fe3cb0e1SScott Long 	c_cmd->param = 0;
3560fe3cb0e1SScott Long 
3561fe3cb0e1SScott Long 	error = aac_sync_fib(sc, ContainerCommand, 0, fib,
3562fe3cb0e1SScott Long 	    sizeof(struct aac_ctcfg));
3563fe3cb0e1SScott Long 	if (error) {
3564fe3cb0e1SScott Long 		device_printf(sc->aac_dev, "Error %d sending "
3565fe3cb0e1SScott Long 		    "VM_ContainerConfig command\n", error);
3566fe3cb0e1SScott Long 		aac_release_sync_fib(sc);
35671ffe41c1SChristian S.J. Peron 		mtx_unlock(&sc->aac_io_lock);
3568fe3cb0e1SScott Long 		return;
3569fe3cb0e1SScott Long 	}
3570fe3cb0e1SScott Long 
3571fe3cb0e1SScott Long 	c_resp = (struct aac_ctcfg_resp *)&fib->data[0];
3572fe3cb0e1SScott Long 	if (c_resp->Status != ST_OK) {
3573fe3cb0e1SScott Long 		device_printf(sc->aac_dev, "VM_ContainerConfig returned 0x%x\n",
3574fe3cb0e1SScott Long 		    c_resp->Status);
3575fe3cb0e1SScott Long 		aac_release_sync_fib(sc);
35761ffe41c1SChristian S.J. Peron 		mtx_unlock(&sc->aac_io_lock);
3577fe3cb0e1SScott Long 		return;
3578fe3cb0e1SScott Long 	}
3579fe3cb0e1SScott Long 
3580fe3cb0e1SScott Long 	sc->scsi_method_id = c_resp->param;
3581fe3cb0e1SScott Long 
3582fe3cb0e1SScott Long 	vmi = (struct aac_vmioctl *)&fib->data[0];
358339ee03c3SScott Long 	bzero(vmi, sizeof(struct aac_vmioctl));
358439ee03c3SScott Long 
3585fe3cb0e1SScott Long 	vmi->Command = VM_Ioctl;
3586fe3cb0e1SScott Long 	vmi->ObjType = FT_DRIVE;
3587fe3cb0e1SScott Long 	vmi->MethId = sc->scsi_method_id;
3588fe3cb0e1SScott Long 	vmi->ObjId = 0;
3589fe3cb0e1SScott Long 	vmi->IoctlCmd = GetBusInfo;
3590fe3cb0e1SScott Long 
3591fe3cb0e1SScott Long 	error = aac_sync_fib(sc, ContainerCommand, 0, fib,
359242ef13a2SEd Maste 	    sizeof(struct aac_vmi_businf_resp));
3593fe3cb0e1SScott Long 	if (error) {
3594fe3cb0e1SScott Long 		device_printf(sc->aac_dev, "Error %d sending VMIoctl command\n",
3595fe3cb0e1SScott Long 		    error);
3596fe3cb0e1SScott Long 		aac_release_sync_fib(sc);
35971ffe41c1SChristian S.J. Peron 		mtx_unlock(&sc->aac_io_lock);
3598fe3cb0e1SScott Long 		return;
3599fe3cb0e1SScott Long 	}
3600fe3cb0e1SScott Long 
3601fe3cb0e1SScott Long 	vmi_resp = (struct aac_vmi_businf_resp *)&fib->data[0];
3602fe3cb0e1SScott Long 	if (vmi_resp->Status != ST_OK) {
3603fe3cb0e1SScott Long 		device_printf(sc->aac_dev, "VM_Ioctl returned %d\n",
3604fe3cb0e1SScott Long 		    vmi_resp->Status);
3605fe3cb0e1SScott Long 		aac_release_sync_fib(sc);
36061ffe41c1SChristian S.J. Peron 		mtx_unlock(&sc->aac_io_lock);
3607fe3cb0e1SScott Long 		return;
3608fe3cb0e1SScott Long 	}
3609fe3cb0e1SScott Long 
3610fe3cb0e1SScott Long 	bcopy(&vmi_resp->BusInf, &businfo, sizeof(struct aac_getbusinf));
3611fe3cb0e1SScott Long 	aac_release_sync_fib(sc);
36121ffe41c1SChristian S.J. Peron 	mtx_unlock(&sc->aac_io_lock);
3613fe3cb0e1SScott Long 
3614fe3cb0e1SScott Long 	found = 0;
3615fe3cb0e1SScott Long 	for (i = 0; i < businfo.BusCount; i++) {
3616fe3cb0e1SScott Long 		if (businfo.BusValid[i] != AAC_BUS_VALID)
3617fe3cb0e1SScott Long 			continue;
3618fe3cb0e1SScott Long 
3619a761a1caSScott Long 		caminf = (struct aac_sim *)malloc( sizeof(struct aac_sim),
3620a761a1caSScott Long 		    M_AACBUF, M_NOWAIT | M_ZERO);
3621b5f516cdSScott Long 		if (caminf == NULL) {
3622b5f516cdSScott Long 			device_printf(sc->aac_dev,
3623b5f516cdSScott Long 			    "No memory to add passthrough bus %d\n", i);
3624b5f516cdSScott Long 			break;
36257cb209f5SScott Long 		};
3626fe3cb0e1SScott Long 
3627fe3cb0e1SScott Long 		child = device_add_child(sc->aac_dev, "aacp", -1);
3628fe3cb0e1SScott Long 		if (child == NULL) {
3629b5f516cdSScott Long 			device_printf(sc->aac_dev,
3630b5f516cdSScott Long 			    "device_add_child failed for passthrough bus %d\n",
3631b5f516cdSScott Long 			    i);
3632b5f516cdSScott Long 			free(caminf, M_AACBUF);
3633b5f516cdSScott Long 			break;
3634fe3cb0e1SScott Long 		}
3635fe3cb0e1SScott Long 
3636fe3cb0e1SScott Long 		caminf->TargetsPerBus = businfo.TargetsPerBus;
3637fe3cb0e1SScott Long 		caminf->BusNumber = i;
3638fe3cb0e1SScott Long 		caminf->InitiatorBusId = businfo.InitiatorBusId[i];
3639fe3cb0e1SScott Long 		caminf->aac_sc = sc;
3640ddb8683eSScott Long 		caminf->sim_dev = child;
3641fe3cb0e1SScott Long 
3642fe3cb0e1SScott Long 		device_set_ivars(child, caminf);
3643fe3cb0e1SScott Long 		device_set_desc(child, "SCSI Passthrough Bus");
364470545d1aSScott Long 		TAILQ_INSERT_TAIL(&sc->aac_sim_tqh, caminf, sim_link);
3645fe3cb0e1SScott Long 
3646fe3cb0e1SScott Long 		found = 1;
3647fe3cb0e1SScott Long 	}
3648fe3cb0e1SScott Long 
3649fe3cb0e1SScott Long 	if (found)
3650fe3cb0e1SScott Long 		bus_generic_attach(sc->aac_dev);
3651fe3cb0e1SScott Long 
3652fe3cb0e1SScott Long 	return;
3653fe3cb0e1SScott Long }
3654