xref: /freebsd/sys/dev/aac/aac.c (revision 718cf2ccb9956613756ab15d7a0e28f2c8e91cab)
135863739SMike Smith /*-
2*718cf2ccSPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3*718cf2ccSPedro F. Giffuni  *
435863739SMike Smith  * Copyright (c) 2000 Michael Smith
5c6eafcf2SScott Long  * Copyright (c) 2001 Scott Long
635863739SMike Smith  * Copyright (c) 2000 BSDi
7c6eafcf2SScott Long  * Copyright (c) 2001 Adaptec, Inc.
835863739SMike Smith  * All rights reserved.
935863739SMike Smith  *
1035863739SMike Smith  * Redistribution and use in source and binary forms, with or without
1135863739SMike Smith  * modification, are permitted provided that the following conditions
1235863739SMike Smith  * are met:
1335863739SMike Smith  * 1. Redistributions of source code must retain the above copyright
1435863739SMike Smith  *    notice, this list of conditions and the following disclaimer.
1535863739SMike Smith  * 2. Redistributions in binary form must reproduce the above copyright
1635863739SMike Smith  *    notice, this list of conditions and the following disclaimer in the
1735863739SMike Smith  *    documentation and/or other materials provided with the distribution.
1835863739SMike Smith  *
1935863739SMike Smith  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2035863739SMike Smith  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2135863739SMike Smith  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2235863739SMike Smith  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2335863739SMike Smith  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2435863739SMike Smith  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2535863739SMike Smith  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2635863739SMike Smith  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2735863739SMike Smith  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2835863739SMike Smith  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2935863739SMike Smith  * SUCH DAMAGE.
3035863739SMike Smith  */
3135863739SMike Smith 
32aad970f1SDavid E. O'Brien #include <sys/cdefs.h>
33aad970f1SDavid E. O'Brien __FBSDID("$FreeBSD$");
34aad970f1SDavid E. O'Brien 
3535863739SMike Smith /*
3635863739SMike Smith  * Driver for the Adaptec 'FSA' family of PCI/SCSI RAID adapters.
3735863739SMike Smith  */
387cb209f5SScott Long #define AAC_DRIVERNAME			"aac"
3935863739SMike Smith 
40f6c4dd3fSScott Long #include "opt_aac.h"
41f6c4dd3fSScott Long 
4236e0bf6eSScott Long /* #include <stddef.h> */
4335863739SMike Smith #include <sys/param.h>
4435863739SMike Smith #include <sys/systm.h>
4535863739SMike Smith #include <sys/malloc.h>
4635863739SMike Smith #include <sys/kernel.h>
4736e0bf6eSScott Long #include <sys/kthread.h>
483d04a9d7SScott Long #include <sys/sysctl.h>
49b3457b51SScott Long #include <sys/poll.h>
50891619a6SPoul-Henning Kamp #include <sys/ioccom.h>
5135863739SMike Smith 
5235863739SMike Smith #include <sys/bus.h>
5335863739SMike Smith #include <sys/conf.h>
5435863739SMike Smith #include <sys/signalvar.h>
550b94a66eSMike Smith #include <sys/time.h>
5636e0bf6eSScott Long #include <sys/eventhandler.h>
577cb209f5SScott Long #include <sys/rman.h>
5835863739SMike Smith 
5935863739SMike Smith #include <machine/bus.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);
74ff0991c4SAttilio Rao static void	aac_daemon(void *arg);
7535863739SMike Smith 
7635863739SMike Smith /* Command Processing */
770b94a66eSMike Smith static void	aac_timeout(struct aac_softc *sc);
7835863739SMike Smith static void	aac_complete(void *context, int pending);
7935863739SMike Smith static int	aac_bio_command(struct aac_softc *sc, struct aac_command **cmp);
8035863739SMike Smith static void	aac_bio_complete(struct aac_command *cm);
81d8a0a473SScott Long static int	aac_wait_command(struct aac_command *cm);
8270545d1aSScott Long static void	aac_command_thread(struct aac_softc *sc);
8335863739SMike Smith 
8435863739SMike Smith /* Command Buffer Management */
85cd481291SScott Long static void	aac_map_command_sg(void *arg, bus_dma_segment_t *segs,
86cd481291SScott Long 				   int nseg, int error);
87c6eafcf2SScott Long static void	aac_map_command_helper(void *arg, bus_dma_segment_t *segs,
88c6eafcf2SScott Long 				       int nseg, int error);
890b94a66eSMike Smith static int	aac_alloc_commands(struct aac_softc *sc);
908480cc63SScott Long static void	aac_free_commands(struct aac_softc *sc);
9135863739SMike Smith static void	aac_unmap_command(struct aac_command *cm);
9235863739SMike Smith 
9335863739SMike Smith /* Hardware Interface */
9404f4d586SEd Maste static int	aac_alloc(struct aac_softc *sc);
95c6eafcf2SScott Long static void	aac_common_map(void *arg, bus_dma_segment_t *segs, int nseg,
96c6eafcf2SScott Long 			       int error);
97fe94b852SScott Long static int	aac_check_firmware(struct aac_softc *sc);
9835863739SMike Smith static int	aac_init(struct aac_softc *sc);
9935863739SMike Smith static int	aac_sync_command(struct aac_softc *sc, u_int32_t command,
100c6eafcf2SScott Long 				 u_int32_t arg0, u_int32_t arg1, u_int32_t arg2,
101c6eafcf2SScott Long 				 u_int32_t arg3, u_int32_t *sp);
10204f4d586SEd Maste static int	aac_setup_intr(struct aac_softc *sc);
103c6eafcf2SScott Long static int	aac_enqueue_fib(struct aac_softc *sc, int queue,
104f6c4dd3fSScott Long 				struct aac_command *cm);
105c6eafcf2SScott Long static int	aac_dequeue_fib(struct aac_softc *sc, int queue,
106914da7d0SScott Long 				u_int32_t *fib_size, struct aac_fib **fib_addr);
10736e0bf6eSScott Long static int	aac_enqueue_response(struct aac_softc *sc, int queue,
10836e0bf6eSScott Long 				     struct aac_fib *fib);
10935863739SMike Smith 
11035863739SMike Smith /* StrongARM interface */
11135863739SMike Smith static int	aac_sa_get_fwstatus(struct aac_softc *sc);
11235863739SMike Smith static void	aac_sa_qnotify(struct aac_softc *sc, int qbit);
11335863739SMike Smith static int	aac_sa_get_istatus(struct aac_softc *sc);
11435863739SMike Smith static void	aac_sa_clear_istatus(struct aac_softc *sc, int mask);
11535863739SMike Smith static void	aac_sa_set_mailbox(struct aac_softc *sc, u_int32_t command,
116c6eafcf2SScott Long 				   u_int32_t arg0, u_int32_t arg1,
117c6eafcf2SScott Long 				   u_int32_t arg2, u_int32_t arg3);
118a6d35632SScott Long static int	aac_sa_get_mailbox(struct aac_softc *sc, int mb);
11935863739SMike Smith static void	aac_sa_set_interrupts(struct aac_softc *sc, int enable);
12035863739SMike Smith 
121da4882c2SMarius Strobl const struct aac_interface aac_sa_interface = {
12235863739SMike Smith 	aac_sa_get_fwstatus,
12335863739SMike Smith 	aac_sa_qnotify,
12435863739SMike Smith 	aac_sa_get_istatus,
12535863739SMike Smith 	aac_sa_clear_istatus,
12635863739SMike Smith 	aac_sa_set_mailbox,
127a6d35632SScott Long 	aac_sa_get_mailbox,
1287cb209f5SScott Long 	aac_sa_set_interrupts,
1297cb209f5SScott Long 	NULL, NULL, NULL
13035863739SMike Smith };
13135863739SMike Smith 
13235863739SMike Smith /* i960Rx interface */
13335863739SMike Smith static int	aac_rx_get_fwstatus(struct aac_softc *sc);
13435863739SMike Smith static void	aac_rx_qnotify(struct aac_softc *sc, int qbit);
13535863739SMike Smith static int	aac_rx_get_istatus(struct aac_softc *sc);
13635863739SMike Smith static void	aac_rx_clear_istatus(struct aac_softc *sc, int mask);
13735863739SMike Smith static void	aac_rx_set_mailbox(struct aac_softc *sc, u_int32_t command,
138c6eafcf2SScott Long 				   u_int32_t arg0, u_int32_t arg1,
139c6eafcf2SScott Long 				   u_int32_t arg2, u_int32_t arg3);
140a6d35632SScott Long static int	aac_rx_get_mailbox(struct aac_softc *sc, int mb);
14135863739SMike Smith static void	aac_rx_set_interrupts(struct aac_softc *sc, int enable);
1427cb209f5SScott Long static int aac_rx_send_command(struct aac_softc *sc, struct aac_command *cm);
1437cb209f5SScott Long static int aac_rx_get_outb_queue(struct aac_softc *sc);
1447cb209f5SScott Long static void aac_rx_set_outb_queue(struct aac_softc *sc, int index);
14535863739SMike Smith 
146da4882c2SMarius Strobl const struct aac_interface aac_rx_interface = {
14735863739SMike Smith 	aac_rx_get_fwstatus,
14835863739SMike Smith 	aac_rx_qnotify,
14935863739SMike Smith 	aac_rx_get_istatus,
15035863739SMike Smith 	aac_rx_clear_istatus,
15135863739SMike Smith 	aac_rx_set_mailbox,
152a6d35632SScott Long 	aac_rx_get_mailbox,
1537cb209f5SScott Long 	aac_rx_set_interrupts,
1547cb209f5SScott Long 	aac_rx_send_command,
1557cb209f5SScott Long 	aac_rx_get_outb_queue,
1567cb209f5SScott Long 	aac_rx_set_outb_queue
15735863739SMike Smith };
15835863739SMike Smith 
1594afedc31SScott Long /* Rocket/MIPS interface */
1604afedc31SScott Long static int	aac_rkt_get_fwstatus(struct aac_softc *sc);
1614afedc31SScott Long static void	aac_rkt_qnotify(struct aac_softc *sc, int qbit);
1624afedc31SScott Long static int	aac_rkt_get_istatus(struct aac_softc *sc);
1634afedc31SScott Long static void	aac_rkt_clear_istatus(struct aac_softc *sc, int mask);
1644afedc31SScott Long static void	aac_rkt_set_mailbox(struct aac_softc *sc, u_int32_t command,
1654afedc31SScott Long 				    u_int32_t arg0, u_int32_t arg1,
1664afedc31SScott Long 				    u_int32_t arg2, u_int32_t arg3);
1674afedc31SScott Long static int	aac_rkt_get_mailbox(struct aac_softc *sc, int mb);
1684afedc31SScott Long static void	aac_rkt_set_interrupts(struct aac_softc *sc, int enable);
1697cb209f5SScott Long static int aac_rkt_send_command(struct aac_softc *sc, struct aac_command *cm);
1707cb209f5SScott Long static int aac_rkt_get_outb_queue(struct aac_softc *sc);
1717cb209f5SScott Long static void aac_rkt_set_outb_queue(struct aac_softc *sc, int index);
1724afedc31SScott Long 
173da4882c2SMarius Strobl const struct aac_interface aac_rkt_interface = {
1744afedc31SScott Long 	aac_rkt_get_fwstatus,
1754afedc31SScott Long 	aac_rkt_qnotify,
1764afedc31SScott Long 	aac_rkt_get_istatus,
1774afedc31SScott Long 	aac_rkt_clear_istatus,
1784afedc31SScott Long 	aac_rkt_set_mailbox,
1794afedc31SScott Long 	aac_rkt_get_mailbox,
1807cb209f5SScott Long 	aac_rkt_set_interrupts,
1817cb209f5SScott Long 	aac_rkt_send_command,
1827cb209f5SScott Long 	aac_rkt_get_outb_queue,
1837cb209f5SScott Long 	aac_rkt_set_outb_queue
1844afedc31SScott Long };
1854afedc31SScott Long 
18635863739SMike Smith /* Debugging and Diagnostics */
18735863739SMike Smith static void		aac_describe_controller(struct aac_softc *sc);
188da4882c2SMarius Strobl static const char	*aac_describe_code(const struct aac_code_lookup *table,
189c6eafcf2SScott Long 				   u_int32_t code);
19035863739SMike Smith 
19135863739SMike Smith /* Management Interface */
19235863739SMike Smith static d_open_t		aac_open;
19335863739SMike Smith static d_ioctl_t	aac_ioctl;
194b3457b51SScott Long static d_poll_t		aac_poll;
195dfe2c294SAttilio Rao static void		aac_cdevpriv_dtor(void *arg);
196c6eafcf2SScott Long static int		aac_ioctl_sendfib(struct aac_softc *sc, caddr_t ufib);
197f355c0e0SEd Maste static int		aac_ioctl_send_raw_srb(struct aac_softc *sc, caddr_t arg);
198c6eafcf2SScott Long static void		aac_handle_aif(struct aac_softc *sc,
19936e0bf6eSScott Long 					   struct aac_fib *fib);
200fb0c27d7SScott Long static int		aac_rev_check(struct aac_softc *sc, caddr_t udata);
201a723a548SEd Maste static int		aac_open_aif(struct aac_softc *sc, caddr_t arg);
202a723a548SEd Maste static int		aac_close_aif(struct aac_softc *sc, caddr_t arg);
203fb0c27d7SScott Long static int		aac_getnext_aif(struct aac_softc *sc, caddr_t arg);
204a723a548SEd Maste static int		aac_return_aif(struct aac_softc *sc,
205a723a548SEd Maste 					struct aac_fib_context *ctx, caddr_t uptr);
20636e0bf6eSScott Long static int		aac_query_disk(struct aac_softc *sc, caddr_t uptr);
2077cb209f5SScott Long static int		aac_get_pci_info(struct aac_softc *sc, caddr_t uptr);
2086d307336SEd Maste static int		aac_supported_features(struct aac_softc *sc, caddr_t uptr);
2097cb209f5SScott Long static void		aac_ioctl_event(struct aac_softc *sc,
2107cb209f5SScott Long 					struct aac_event *event, void *arg);
21104f4d586SEd Maste static struct aac_mntinforesp *
21204f4d586SEd Maste 	aac_get_container_info(struct aac_softc *sc, struct aac_fib *fib, int cid);
21335863739SMike Smith 
21435863739SMike Smith static struct cdevsw aac_cdevsw = {
215dc08ffecSPoul-Henning Kamp 	.d_version =	D_VERSION,
216dfe2c294SAttilio Rao 	.d_flags =	D_NEEDGIANT,
2177ac40f5fSPoul-Henning Kamp 	.d_open =	aac_open,
2187ac40f5fSPoul-Henning Kamp 	.d_ioctl =	aac_ioctl,
2197ac40f5fSPoul-Henning Kamp 	.d_poll =	aac_poll,
2207ac40f5fSPoul-Henning Kamp 	.d_name =	"aac",
22135863739SMike Smith };
22235863739SMike Smith 
223d745c852SEd Schouten static MALLOC_DEFINE(M_AACBUF, "aacbuf", "Buffers for the AAC driver");
22436e0bf6eSScott Long 
2253d04a9d7SScott Long /* sysctl node */
226da4882c2SMarius Strobl SYSCTL_NODE(_hw, OID_AUTO, aac, CTLFLAG_RD, 0, "AAC driver parameters");
2273d04a9d7SScott Long 
228914da7d0SScott Long /*
229914da7d0SScott Long  * Device Interface
230914da7d0SScott Long  */
23135863739SMike Smith 
232914da7d0SScott Long /*
2334109ba51SEd Maste  * Initialize the controller and softc
23435863739SMike Smith  */
23535863739SMike Smith int
23635863739SMike Smith aac_attach(struct aac_softc *sc)
23735863739SMike Smith {
23835863739SMike Smith 	int error, unit;
23935863739SMike Smith 
24031a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
24135863739SMike Smith 
24235863739SMike Smith 	/*
2434109ba51SEd Maste 	 * Initialize per-controller queues.
24435863739SMike Smith 	 */
2450b94a66eSMike Smith 	aac_initq_free(sc);
2460b94a66eSMike Smith 	aac_initq_ready(sc);
2470b94a66eSMike Smith 	aac_initq_busy(sc);
2480b94a66eSMike Smith 	aac_initq_bio(sc);
24935863739SMike Smith 
25035863739SMike Smith 	/*
2514109ba51SEd Maste 	 * Initialize command-completion task.
25235863739SMike Smith 	 */
25335863739SMike Smith 	TASK_INIT(&sc->aac_task_complete, 0, aac_complete, sc);
25435863739SMike Smith 
25535863739SMike Smith 	/* mark controller as suspended until we get ourselves organised */
25635863739SMike Smith 	sc->aac_state |= AAC_STATE_SUSPEND;
25735863739SMike Smith 
25835863739SMike Smith 	/*
259fe94b852SScott Long 	 * Check that the firmware on the card is supported.
260fe94b852SScott Long 	 */
261fe94b852SScott Long 	if ((error = aac_check_firmware(sc)) != 0)
262fe94b852SScott Long 		return(error);
263fe94b852SScott Long 
264f6b1c44dSScott Long 	/*
265f6b1c44dSScott Long 	 * Initialize locks
266f6b1c44dSScott Long 	 */
267bb6fe253SScott Long 	mtx_init(&sc->aac_aifq_lock, "AAC AIF lock", NULL, MTX_DEF);
268bb6fe253SScott Long 	mtx_init(&sc->aac_io_lock, "AAC I/O lock", NULL, MTX_DEF);
269bb6fe253SScott Long 	mtx_init(&sc->aac_container_lock, "AAC container lock", NULL, MTX_DEF);
270f6b1c44dSScott Long 	TAILQ_INIT(&sc->aac_container_tqh);
271065dd78cSScott Long 	TAILQ_INIT(&sc->aac_ev_cmfree);
272f6b1c44dSScott Long 
273ff0991c4SAttilio Rao 	/* Initialize the clock daemon callout. */
274ff0991c4SAttilio Rao 	callout_init_mtx(&sc->aac_daemontime, &sc->aac_io_lock, 0);
275ff0991c4SAttilio Rao 
2760b94a66eSMike Smith 	/*
2774109ba51SEd Maste 	 * Initialize the adapter.
27835863739SMike Smith 	 */
27904f4d586SEd Maste 	if ((error = aac_alloc(sc)) != 0)
28004f4d586SEd Maste 		return(error);
2810b94a66eSMike Smith 	if ((error = aac_init(sc)) != 0)
28235863739SMike Smith 		return(error);
28335863739SMike Smith 
28435863739SMike Smith 	/*
2857cb209f5SScott Long 	 * Allocate and connect our interrupt.
2867cb209f5SScott Long 	 */
28704f4d586SEd Maste 	if ((error = aac_setup_intr(sc)) != 0)
28804f4d586SEd Maste 		return(error);
2897cb209f5SScott Long 
2907cb209f5SScott Long 	/*
29135863739SMike Smith 	 * Print a little information about the controller.
29235863739SMike Smith 	 */
29335863739SMike Smith 	aac_describe_controller(sc);
29435863739SMike Smith 
29535863739SMike Smith 	/*
2961423dcd6SEd Maste 	 * Add sysctls.
2971423dcd6SEd Maste 	 */
2981423dcd6SEd Maste 	SYSCTL_ADD_INT(device_get_sysctl_ctx(sc->aac_dev),
2991423dcd6SEd Maste 	    SYSCTL_CHILDREN(device_get_sysctl_tree(sc->aac_dev)),
3001423dcd6SEd Maste 	    OID_AUTO, "firmware_build", CTLFLAG_RD,
3011423dcd6SEd Maste 	    &sc->aac_revision.buildNumber, 0,
3021423dcd6SEd Maste 	    "firmware build number");
3031423dcd6SEd Maste 
3041423dcd6SEd Maste 	/*
305ae543596SScott Long 	 * Register to probe our containers later.
306ae543596SScott Long 	 */
30735863739SMike Smith 	sc->aac_ich.ich_func = aac_startup;
30835863739SMike Smith 	sc->aac_ich.ich_arg = sc;
30935863739SMike Smith 	if (config_intrhook_establish(&sc->aac_ich) != 0) {
310914da7d0SScott Long 		device_printf(sc->aac_dev,
311914da7d0SScott Long 			      "can't establish configuration hook\n");
31235863739SMike Smith 		return(ENXIO);
31335863739SMike Smith 	}
31435863739SMike Smith 
31535863739SMike Smith 	/*
31635863739SMike Smith 	 * Make the control device.
31735863739SMike Smith 	 */
31835863739SMike Smith 	unit = device_get_unit(sc->aac_dev);
3199e9466baSRobert Watson 	sc->aac_dev_t = make_dev(&aac_cdevsw, unit, UID_ROOT, GID_OPERATOR,
3209e9466baSRobert Watson 				 0640, "aac%d", unit);
321157fbb2eSScott Long 	(void)make_dev_alias(sc->aac_dev_t, "afa%d", unit);
3224aa620cdSScott Long 	(void)make_dev_alias(sc->aac_dev_t, "hpn%d", unit);
32335863739SMike Smith 	sc->aac_dev_t->si_drv1 = sc;
32435863739SMike Smith 
32536e0bf6eSScott Long 	/* Create the AIF thread */
3263745c395SJulian Elischer 	if (kproc_create((void(*)(void *))aac_command_thread, sc,
327316ec49aSScott Long 		   &sc->aifthread, 0, 0, "aac%daif", unit))
328a620bad0SEd Maste 		panic("Could not create AIF thread");
32936e0bf6eSScott Long 
33036e0bf6eSScott Long 	/* Register the shutdown method to only be called post-dump */
3315f54d522SScott Long 	if ((sc->eh = EVENTHANDLER_REGISTER(shutdown_final, aac_shutdown,
3325f54d522SScott Long 	    sc->aac_dev, SHUTDOWN_PRI_DEFAULT)) == NULL)
3335f54d522SScott Long 		device_printf(sc->aac_dev,
3345f54d522SScott Long 			      "shutdown event registration failed\n");
33536e0bf6eSScott Long 
336fe3cb0e1SScott Long 	/* Register with CAM for the non-DASD devices */
337a6d35632SScott Long 	if ((sc->flags & AAC_FLAGS_ENABLE_CAM) != 0) {
33870545d1aSScott Long 		TAILQ_INIT(&sc->aac_sim_tqh);
339fe3cb0e1SScott Long 		aac_get_bus_info(sc);
34070545d1aSScott Long 	}
341fe3cb0e1SScott Long 
342ff0991c4SAttilio Rao 	mtx_lock(&sc->aac_io_lock);
343867b1d34SEd Maste 	callout_reset(&sc->aac_daemontime, 60 * hz, aac_daemon, sc);
344ff0991c4SAttilio Rao 	mtx_unlock(&sc->aac_io_lock);
345ff0991c4SAttilio Rao 
34635863739SMike Smith 	return(0);
34735863739SMike Smith }
34835863739SMike Smith 
349ff0991c4SAttilio Rao static void
350ff0991c4SAttilio Rao aac_daemon(void *arg)
351ff0991c4SAttilio Rao {
352ff0991c4SAttilio Rao 	struct timeval tv;
353ff0991c4SAttilio Rao 	struct aac_softc *sc;
354ff0991c4SAttilio Rao 	struct aac_fib *fib;
355ff0991c4SAttilio Rao 
356ff0991c4SAttilio Rao 	sc = arg;
357ff0991c4SAttilio Rao 	mtx_assert(&sc->aac_io_lock, MA_OWNED);
358ff0991c4SAttilio Rao 
359ff0991c4SAttilio Rao 	if (callout_pending(&sc->aac_daemontime) ||
360ff0991c4SAttilio Rao 	    callout_active(&sc->aac_daemontime) == 0)
361ff0991c4SAttilio Rao 		return;
362ff0991c4SAttilio Rao 	getmicrotime(&tv);
363ff0991c4SAttilio Rao 	aac_alloc_sync_fib(sc, &fib);
364ff0991c4SAttilio Rao 	*(uint32_t *)fib->data = tv.tv_sec;
365ff0991c4SAttilio Rao 	aac_sync_fib(sc, SendHostTime, 0, fib, sizeof(uint32_t));
366ff0991c4SAttilio Rao 	aac_release_sync_fib(sc);
367ff0991c4SAttilio Rao 	callout_schedule(&sc->aac_daemontime, 30 * 60 * hz);
368ff0991c4SAttilio Rao }
369ff0991c4SAttilio Rao 
3707cb209f5SScott Long void
3717cb209f5SScott Long aac_add_event(struct aac_softc *sc, struct aac_event *event)
3727cb209f5SScott Long {
3737cb209f5SScott Long 
3747cb209f5SScott Long 	switch (event->ev_type & AAC_EVENT_MASK) {
3757cb209f5SScott Long 	case AAC_EVENT_CMFREE:
3767cb209f5SScott Long 		TAILQ_INSERT_TAIL(&sc->aac_ev_cmfree, event, ev_links);
3777cb209f5SScott Long 		break;
3787cb209f5SScott Long 	default:
3797cb209f5SScott Long 		device_printf(sc->aac_dev, "aac_add event: unknown event %d\n",
3807cb209f5SScott Long 		    event->ev_type);
3817cb209f5SScott Long 		break;
3827cb209f5SScott Long 	}
3837cb209f5SScott Long }
3847cb209f5SScott Long 
385914da7d0SScott Long /*
38604f4d586SEd Maste  * Request information of container #cid
38704f4d586SEd Maste  */
38804f4d586SEd Maste static struct aac_mntinforesp *
38904f4d586SEd Maste aac_get_container_info(struct aac_softc *sc, struct aac_fib *fib, int cid)
39004f4d586SEd Maste {
39104f4d586SEd Maste 	struct aac_mntinfo *mi;
39204f4d586SEd Maste 
39304f4d586SEd Maste 	mi = (struct aac_mntinfo *)&fib->data[0];
394523da39bSEd Maste 	/* use 64-bit LBA if enabled */
395523da39bSEd Maste 	mi->Command = (sc->flags & AAC_FLAGS_LBA_64BIT) ?
396523da39bSEd Maste 	    VM_NameServe64 : VM_NameServe;
39704f4d586SEd Maste 	mi->MntType = FT_FILESYS;
39804f4d586SEd Maste 	mi->MntCount = cid;
39904f4d586SEd Maste 
40004f4d586SEd Maste 	if (aac_sync_fib(sc, ContainerCommand, 0, fib,
40104f4d586SEd Maste 			 sizeof(struct aac_mntinfo))) {
402dbb34a64SEd Maste 		device_printf(sc->aac_dev, "Error probing container %d\n", cid);
40304f4d586SEd Maste 		return (NULL);
40404f4d586SEd Maste 	}
40504f4d586SEd Maste 
40604f4d586SEd Maste 	return ((struct aac_mntinforesp *)&fib->data[0]);
40704f4d586SEd Maste }
40804f4d586SEd Maste 
40904f4d586SEd Maste /*
41035863739SMike Smith  * Probe for containers, create disks.
41135863739SMike Smith  */
41235863739SMike Smith static void
41335863739SMike Smith aac_startup(void *arg)
41435863739SMike Smith {
415914da7d0SScott Long 	struct aac_softc *sc;
416cbfd045bSScott Long 	struct aac_fib *fib;
41704f4d586SEd Maste 	struct aac_mntinforesp *mir;
418795d7dc0SScott Long 	int count = 0, i = 0;
41935863739SMike Smith 
420914da7d0SScott Long 	sc = (struct aac_softc *)arg;
42131a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
422914da7d0SScott Long 
4237cb209f5SScott Long 	mtx_lock(&sc->aac_io_lock);
42403b5fe51SScott Long 	aac_alloc_sync_fib(sc, &fib);
425cbfd045bSScott Long 
42635863739SMike Smith 	/* loop over possible containers */
42736e0bf6eSScott Long 	do {
42804f4d586SEd Maste 		if ((mir = aac_get_container_info(sc, fib, i)) == NULL)
42935863739SMike Smith 			continue;
43004f4d586SEd Maste 		if (i == 0)
431795d7dc0SScott Long 			count = mir->MntRespCount;
432cbfd045bSScott Long 		aac_add_container(sc, mir, 0);
43336e0bf6eSScott Long 		i++;
434795d7dc0SScott Long 	} while ((i < count) && (i < AAC_MAX_CONTAINERS));
435cbfd045bSScott Long 
436cbfd045bSScott Long 	aac_release_sync_fib(sc);
4377cb209f5SScott Long 	mtx_unlock(&sc->aac_io_lock);
43835863739SMike Smith 
439cc336c78SScott Long 	/* mark the controller up */
440cc336c78SScott Long 	sc->aac_state &= ~AAC_STATE_SUSPEND;
441cc336c78SScott Long 
44235863739SMike Smith 	/* poke the bus to actually attach the child devices */
44335863739SMike Smith 	if (bus_generic_attach(sc->aac_dev))
44435863739SMike Smith 		device_printf(sc->aac_dev, "bus_generic_attach failed\n");
44535863739SMike Smith 
446cc336c78SScott Long 	/* disconnect ourselves from the intrhook chain */
447cc336c78SScott Long 	config_intrhook_disestablish(&sc->aac_ich);
44835863739SMike Smith 
44935863739SMike Smith 	/* enable interrupts now */
45035863739SMike Smith 	AAC_UNMASK_INTERRUPTS(sc);
45135863739SMike Smith }
45235863739SMike Smith 
453914da7d0SScott Long /*
4544109ba51SEd Maste  * Create a device to represent a new container
455914da7d0SScott Long  */
456914da7d0SScott Long static void
457cbfd045bSScott Long aac_add_container(struct aac_softc *sc, struct aac_mntinforesp *mir, int f)
458914da7d0SScott Long {
459914da7d0SScott Long 	struct aac_container *co;
460914da7d0SScott Long 	device_t child;
461914da7d0SScott Long 
462914da7d0SScott Long 	/*
463914da7d0SScott Long 	 * Check container volume type for validity.  Note that many of
464914da7d0SScott Long 	 * the possible types may never show up.
465914da7d0SScott Long 	 */
466914da7d0SScott Long 	if ((mir->Status == ST_OK) && (mir->MntTable[0].VolType != CT_NONE)) {
467a761a1caSScott Long 		co = (struct aac_container *)malloc(sizeof *co, M_AACBUF,
468a761a1caSScott Long 		       M_NOWAIT | M_ZERO);
469914da7d0SScott Long 		if (co == NULL)
470a620bad0SEd Maste 			panic("Out of memory?!");
47131a0399eSEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_INIT_B, "id %x  name '%.16s'  size %u  type %d",
472914da7d0SScott Long 		      mir->MntTable[0].ObjectId,
473914da7d0SScott Long 		      mir->MntTable[0].FileSystemName,
474914da7d0SScott Long 		      mir->MntTable[0].Capacity, mir->MntTable[0].VolType);
475914da7d0SScott Long 
476fe3cb0e1SScott Long 		if ((child = device_add_child(sc->aac_dev, "aacd", -1)) == NULL)
477914da7d0SScott Long 			device_printf(sc->aac_dev, "device_add_child failed\n");
478914da7d0SScott Long 		else
479914da7d0SScott Long 			device_set_ivars(child, co);
480914da7d0SScott Long 		device_set_desc(child, aac_describe_code(aac_container_types,
481914da7d0SScott Long 				mir->MntTable[0].VolType));
482914da7d0SScott Long 		co->co_disk = child;
483914da7d0SScott Long 		co->co_found = f;
484914da7d0SScott Long 		bcopy(&mir->MntTable[0], &co->co_mntobj,
485914da7d0SScott Long 		      sizeof(struct aac_mntobj));
486bb6fe253SScott Long 		mtx_lock(&sc->aac_container_lock);
487914da7d0SScott Long 		TAILQ_INSERT_TAIL(&sc->aac_container_tqh, co, co_link);
488bb6fe253SScott Long 		mtx_unlock(&sc->aac_container_lock);
489914da7d0SScott Long 	}
490914da7d0SScott Long }
491914da7d0SScott Long 
492914da7d0SScott Long /*
49304f4d586SEd Maste  * Allocate resources associated with (sc)
49404f4d586SEd Maste  */
49504f4d586SEd Maste static int
49604f4d586SEd Maste aac_alloc(struct aac_softc *sc)
49704f4d586SEd Maste {
49831a0399eSEd Maste 
49931a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
50031a0399eSEd Maste 
50104f4d586SEd Maste 	/*
50204f4d586SEd Maste 	 * Create DMA tag for mapping buffers into controller-addressable space.
50304f4d586SEd Maste 	 */
50404f4d586SEd Maste 	if (bus_dma_tag_create(sc->aac_parent_dmat, 	/* parent */
50504f4d586SEd Maste 			       1, 0, 			/* algnmnt, boundary */
50604f4d586SEd Maste 			       (sc->flags & AAC_FLAGS_SG_64BIT) ?
50704f4d586SEd Maste 			       BUS_SPACE_MAXADDR :
50804f4d586SEd Maste 			       BUS_SPACE_MAXADDR_32BIT,	/* lowaddr */
50904f4d586SEd Maste 			       BUS_SPACE_MAXADDR, 	/* highaddr */
51004f4d586SEd Maste 			       NULL, NULL, 		/* filter, filterarg */
5116f954fb3SAlexander Motin 			       sc->aac_max_sectors << 9, /* maxsize */
51204f4d586SEd Maste 			       sc->aac_sg_tablesize,	/* nsegments */
5136f954fb3SAlexander Motin 			       BUS_SPACE_MAXSIZE_32BIT,	/* maxsegsize */
51404f4d586SEd Maste 			       BUS_DMA_ALLOCNOW,	/* flags */
51504f4d586SEd Maste 			       busdma_lock_mutex,	/* lockfunc */
51604f4d586SEd Maste 			       &sc->aac_io_lock,	/* lockfuncarg */
51704f4d586SEd Maste 			       &sc->aac_buffer_dmat)) {
51804f4d586SEd Maste 		device_printf(sc->aac_dev, "can't allocate buffer DMA tag\n");
51904f4d586SEd Maste 		return (ENOMEM);
52004f4d586SEd Maste 	}
52104f4d586SEd Maste 
52204f4d586SEd Maste 	/*
52304f4d586SEd Maste 	 * Create DMA tag for mapping FIBs into controller-addressable space..
52404f4d586SEd Maste 	 */
52504f4d586SEd Maste 	if (bus_dma_tag_create(sc->aac_parent_dmat,	/* parent */
52604f4d586SEd Maste 			       1, 0, 			/* algnmnt, boundary */
52704f4d586SEd Maste 			       (sc->flags & AAC_FLAGS_4GB_WINDOW) ?
52804f4d586SEd Maste 			       BUS_SPACE_MAXADDR_32BIT :
52904f4d586SEd Maste 			       0x7fffffff,		/* lowaddr */
53004f4d586SEd Maste 			       BUS_SPACE_MAXADDR, 	/* highaddr */
53104f4d586SEd Maste 			       NULL, NULL, 		/* filter, filterarg */
53204f4d586SEd Maste 			       sc->aac_max_fibs_alloc *
53304f4d586SEd Maste 			       sc->aac_max_fib_size,  /* maxsize */
53404f4d586SEd Maste 			       1,			/* nsegments */
53504f4d586SEd Maste 			       sc->aac_max_fibs_alloc *
53604f4d586SEd Maste 			       sc->aac_max_fib_size,	/* maxsize */
53704f4d586SEd Maste 			       0,			/* flags */
53804f4d586SEd Maste 			       NULL, NULL,		/* No locking needed */
53904f4d586SEd Maste 			       &sc->aac_fib_dmat)) {
540c2ede4b3SMartin Blapp 		device_printf(sc->aac_dev, "can't allocate FIB DMA tag\n");
54104f4d586SEd Maste 		return (ENOMEM);
54204f4d586SEd Maste 	}
54304f4d586SEd Maste 
54404f4d586SEd Maste 	/*
54504f4d586SEd Maste 	 * Create DMA tag for the common structure and allocate it.
54604f4d586SEd Maste 	 */
54704f4d586SEd Maste 	if (bus_dma_tag_create(sc->aac_parent_dmat, 	/* parent */
54804f4d586SEd Maste 			       1, 0,			/* algnmnt, boundary */
54904f4d586SEd Maste 			       (sc->flags & AAC_FLAGS_4GB_WINDOW) ?
55004f4d586SEd Maste 			       BUS_SPACE_MAXADDR_32BIT :
55104f4d586SEd Maste 			       0x7fffffff,		/* lowaddr */
55204f4d586SEd Maste 			       BUS_SPACE_MAXADDR, 	/* highaddr */
55304f4d586SEd Maste 			       NULL, NULL, 		/* filter, filterarg */
55404f4d586SEd Maste 			       8192 + sizeof(struct aac_common), /* maxsize */
55504f4d586SEd Maste 			       1,			/* nsegments */
55604f4d586SEd Maste 			       BUS_SPACE_MAXSIZE_32BIT,	/* maxsegsize */
55704f4d586SEd Maste 			       0,			/* flags */
55804f4d586SEd Maste 			       NULL, NULL,		/* No locking needed */
55904f4d586SEd Maste 			       &sc->aac_common_dmat)) {
56004f4d586SEd Maste 		device_printf(sc->aac_dev,
56104f4d586SEd Maste 			      "can't allocate common structure DMA tag\n");
56204f4d586SEd Maste 		return (ENOMEM);
56304f4d586SEd Maste 	}
56404f4d586SEd Maste 	if (bus_dmamem_alloc(sc->aac_common_dmat, (void **)&sc->aac_common,
56504f4d586SEd Maste 			     BUS_DMA_NOWAIT, &sc->aac_common_dmamap)) {
56604f4d586SEd Maste 		device_printf(sc->aac_dev, "can't allocate common structure\n");
56704f4d586SEd Maste 		return (ENOMEM);
56804f4d586SEd Maste 	}
56904f4d586SEd Maste 
57004f4d586SEd Maste 	/*
57104f4d586SEd Maste 	 * Work around a bug in the 2120 and 2200 that cannot DMA commands
57204f4d586SEd Maste 	 * below address 8192 in physical memory.
57304f4d586SEd Maste 	 * XXX If the padding is not needed, can it be put to use instead
57404f4d586SEd Maste 	 * of ignored?
57504f4d586SEd Maste 	 */
57604f4d586SEd Maste 	(void)bus_dmamap_load(sc->aac_common_dmat, sc->aac_common_dmamap,
57704f4d586SEd Maste 			sc->aac_common, 8192 + sizeof(*sc->aac_common),
57804f4d586SEd Maste 			aac_common_map, sc, 0);
57904f4d586SEd Maste 
58004f4d586SEd Maste 	if (sc->aac_common_busaddr < 8192) {
58104f4d586SEd Maste 		sc->aac_common = (struct aac_common *)
58204f4d586SEd Maste 		    ((uint8_t *)sc->aac_common + 8192);
58304f4d586SEd Maste 		sc->aac_common_busaddr += 8192;
58404f4d586SEd Maste 	}
58504f4d586SEd Maste 	bzero(sc->aac_common, sizeof(*sc->aac_common));
58604f4d586SEd Maste 
58704f4d586SEd Maste 	/* Allocate some FIBs and associated command structs */
58804f4d586SEd Maste 	TAILQ_INIT(&sc->aac_fibmap_tqh);
58904f4d586SEd Maste 	sc->aac_commands = malloc(sc->aac_max_fibs * sizeof(struct aac_command),
59004f4d586SEd Maste 				  M_AACBUF, M_WAITOK|M_ZERO);
59123e876b1SJung-uk Kim 	while (sc->total_fibs < sc->aac_max_fibs) {
59204f4d586SEd Maste 		if (aac_alloc_commands(sc) != 0)
59304f4d586SEd Maste 			break;
59404f4d586SEd Maste 	}
59504f4d586SEd Maste 	if (sc->total_fibs == 0)
59604f4d586SEd Maste 		return (ENOMEM);
59704f4d586SEd Maste 
59804f4d586SEd Maste 	return (0);
59904f4d586SEd Maste }
60004f4d586SEd Maste 
60104f4d586SEd Maste /*
60235863739SMike Smith  * Free all of the resources associated with (sc)
60335863739SMike Smith  *
60435863739SMike Smith  * Should not be called if the controller is active.
60535863739SMike Smith  */
60635863739SMike Smith void
60735863739SMike Smith aac_free(struct aac_softc *sc)
60835863739SMike Smith {
609ffb37f33SScott Long 
61031a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
61135863739SMike Smith 
61235863739SMike Smith 	/* remove the control device */
61335863739SMike Smith 	if (sc->aac_dev_t != NULL)
61435863739SMike Smith 		destroy_dev(sc->aac_dev_t);
61535863739SMike Smith 
6160b94a66eSMike Smith 	/* throw away any FIB buffers, discard the FIB DMA tag */
6178480cc63SScott Long 	aac_free_commands(sc);
6180b94a66eSMike Smith 	if (sc->aac_fib_dmat)
6190b94a66eSMike Smith 		bus_dma_tag_destroy(sc->aac_fib_dmat);
62035863739SMike Smith 
621ffb37f33SScott Long 	free(sc->aac_commands, M_AACBUF);
622ffb37f33SScott Long 
62335863739SMike Smith 	/* destroy the common area */
62435863739SMike Smith 	if (sc->aac_common) {
62535863739SMike Smith 		bus_dmamap_unload(sc->aac_common_dmat, sc->aac_common_dmamap);
626c6eafcf2SScott Long 		bus_dmamem_free(sc->aac_common_dmat, sc->aac_common,
627c6eafcf2SScott Long 				sc->aac_common_dmamap);
62835863739SMike Smith 	}
6290b94a66eSMike Smith 	if (sc->aac_common_dmat)
6300b94a66eSMike Smith 		bus_dma_tag_destroy(sc->aac_common_dmat);
63135863739SMike Smith 
63235863739SMike Smith 	/* disconnect the interrupt handler */
63335863739SMike Smith 	if (sc->aac_intr)
63435863739SMike Smith 		bus_teardown_intr(sc->aac_dev, sc->aac_irq, sc->aac_intr);
635bbc03f1bSMarius Strobl 	if (sc->aac_irq != NULL) {
636da4882c2SMarius Strobl 		bus_release_resource(sc->aac_dev, SYS_RES_IRQ,
637da4882c2SMarius Strobl 		    rman_get_rid(sc->aac_irq), sc->aac_irq);
638bbc03f1bSMarius Strobl 		pci_release_msi(sc->aac_dev);
639bbc03f1bSMarius Strobl 	}
64035863739SMike Smith 
64135863739SMike Smith 	/* destroy data-transfer DMA tag */
64235863739SMike Smith 	if (sc->aac_buffer_dmat)
64335863739SMike Smith 		bus_dma_tag_destroy(sc->aac_buffer_dmat);
64435863739SMike Smith 
64535863739SMike Smith 	/* destroy the parent DMA tag */
64635863739SMike Smith 	if (sc->aac_parent_dmat)
64735863739SMike Smith 		bus_dma_tag_destroy(sc->aac_parent_dmat);
64835863739SMike Smith 
64935863739SMike Smith 	/* release the register window mapping */
650ff0991c4SAttilio Rao 	if (sc->aac_regs_res0 != NULL)
651914da7d0SScott Long 		bus_release_resource(sc->aac_dev, SYS_RES_MEMORY,
652da4882c2SMarius Strobl 		    rman_get_rid(sc->aac_regs_res0), sc->aac_regs_res0);
653ff0991c4SAttilio Rao 	if (sc->aac_hwif == AAC_HWIF_NARK && sc->aac_regs_res1 != NULL)
654ff0991c4SAttilio Rao 		bus_release_resource(sc->aac_dev, SYS_RES_MEMORY,
655da4882c2SMarius Strobl 		    rman_get_rid(sc->aac_regs_res1), sc->aac_regs_res1);
65635863739SMike Smith }
65735863739SMike Smith 
658914da7d0SScott Long /*
65935863739SMike Smith  * Disconnect from the controller completely, in preparation for unload.
66035863739SMike Smith  */
66135863739SMike Smith int
66235863739SMike Smith aac_detach(device_t dev)
66335863739SMike Smith {
664914da7d0SScott Long 	struct aac_softc *sc;
66570545d1aSScott Long 	struct aac_container *co;
66670545d1aSScott Long 	struct aac_sim	*sim;
66735863739SMike Smith 	int error;
66835863739SMike Smith 
669914da7d0SScott Long 	sc = device_get_softc(dev);
67031a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
671914da7d0SScott Long 
672ff0991c4SAttilio Rao 	callout_drain(&sc->aac_daemontime);
673ff0991c4SAttilio Rao 
6741bd320ecSAttilio Rao 	mtx_lock(&sc->aac_io_lock);
6751bd320ecSAttilio Rao 	while (sc->aifflags & AAC_AIFFLAGS_RUNNING) {
6761bd320ecSAttilio Rao 		sc->aifflags |= AAC_AIFFLAGS_EXIT;
6771bd320ecSAttilio Rao 		wakeup(sc->aifthread);
6781bd320ecSAttilio Rao 		msleep(sc->aac_dev, &sc->aac_io_lock, PUSER, "aacdch", 0);
6791bd320ecSAttilio Rao 	}
6801bd320ecSAttilio Rao 	mtx_unlock(&sc->aac_io_lock);
6811bd320ecSAttilio Rao 	KASSERT((sc->aifflags & AAC_AIFFLAGS_RUNNING) == 0,
6821bd320ecSAttilio Rao 	    ("%s: invalid detach state", __func__));
6831bd320ecSAttilio Rao 
68470545d1aSScott Long 	/* Remove the child containers */
685a761a1caSScott Long 	while ((co = TAILQ_FIRST(&sc->aac_container_tqh)) != NULL) {
68670545d1aSScott Long 		error = device_delete_child(dev, co->co_disk);
68770545d1aSScott Long 		if (error)
68870545d1aSScott Long 			return (error);
68965ac4ed6SScott Long 		TAILQ_REMOVE(&sc->aac_container_tqh, co, co_link);
690a761a1caSScott Long 		free(co, M_AACBUF);
69170545d1aSScott Long 	}
69270545d1aSScott Long 
69370545d1aSScott Long 	/* Remove the CAM SIMs */
694a761a1caSScott Long 	while ((sim = TAILQ_FIRST(&sc->aac_sim_tqh)) != NULL) {
695a761a1caSScott Long 		TAILQ_REMOVE(&sc->aac_sim_tqh, sim, sim_link);
69670545d1aSScott Long 		error = device_delete_child(dev, sim->sim_dev);
69770545d1aSScott Long 		if (error)
69870545d1aSScott Long 			return (error);
699a761a1caSScott Long 		free(sim, M_AACBUF);
70070545d1aSScott Long 	}
70170545d1aSScott Long 
70235863739SMike Smith 	if ((error = aac_shutdown(dev)))
70335863739SMike Smith 		return(error);
70435863739SMike Smith 
7055f54d522SScott Long 	EVENTHANDLER_DEREGISTER(shutdown_final, sc->eh);
7065f54d522SScott Long 
70735863739SMike Smith 	aac_free(sc);
70835863739SMike Smith 
709dc9efde5SScott Long 	mtx_destroy(&sc->aac_aifq_lock);
710dc9efde5SScott Long 	mtx_destroy(&sc->aac_io_lock);
711dc9efde5SScott Long 	mtx_destroy(&sc->aac_container_lock);
712dc9efde5SScott Long 
71335863739SMike Smith 	return(0);
71435863739SMike Smith }
71535863739SMike Smith 
716914da7d0SScott Long /*
71735863739SMike Smith  * Bring the controller down to a dormant state and detach all child devices.
71835863739SMike Smith  *
71935863739SMike Smith  * This function is called before detach or system shutdown.
72035863739SMike Smith  *
7210b94a66eSMike Smith  * Note that we can assume that the bioq on the controller is empty, as we won't
72235863739SMike Smith  * allow shutdown if any device is open.
72335863739SMike Smith  */
72435863739SMike Smith int
72535863739SMike Smith aac_shutdown(device_t dev)
72635863739SMike Smith {
727914da7d0SScott Long 	struct aac_softc *sc;
728cbfd045bSScott Long 	struct aac_fib *fib;
729cbfd045bSScott Long 	struct aac_close_command *cc;
73035863739SMike Smith 
731914da7d0SScott Long 	sc = device_get_softc(dev);
73231a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
733914da7d0SScott Long 
73435863739SMike Smith 	sc->aac_state |= AAC_STATE_SUSPEND;
73535863739SMike Smith 
73635863739SMike Smith 	/*
73735863739SMike Smith 	 * Send a Container shutdown followed by a HostShutdown FIB to the
73835863739SMike Smith 	 * controller to convince it that we don't want to talk to it anymore.
73935863739SMike Smith 	 * We've been closed and all I/O completed already
74035863739SMike Smith 	 */
74135863739SMike Smith 	device_printf(sc->aac_dev, "shutting down controller...");
74235863739SMike Smith 
7437cb209f5SScott Long 	mtx_lock(&sc->aac_io_lock);
74403b5fe51SScott Long 	aac_alloc_sync_fib(sc, &fib);
745cbfd045bSScott Long 	cc = (struct aac_close_command *)&fib->data[0];
746cbfd045bSScott Long 
74739ee03c3SScott Long 	bzero(cc, sizeof(struct aac_close_command));
748cbfd045bSScott Long 	cc->Command = VM_CloseAll;
749cbfd045bSScott Long 	cc->ContainerId = 0xffffffff;
750cbfd045bSScott Long 	if (aac_sync_fib(sc, ContainerCommand, 0, fib,
751cbfd045bSScott Long 	    sizeof(struct aac_close_command)))
75235863739SMike Smith 		printf("FAILED.\n");
75370545d1aSScott Long 	else
75470545d1aSScott Long 		printf("done\n");
75570545d1aSScott Long #if 0
756914da7d0SScott Long 	else {
757cbfd045bSScott Long 		fib->data[0] = 0;
75836e0bf6eSScott Long 		/*
759914da7d0SScott Long 		 * XXX Issuing this command to the controller makes it shut down
76036e0bf6eSScott Long 		 * but also keeps it from coming back up without a reset of the
76136e0bf6eSScott Long 		 * PCI bus.  This is not desirable if you are just unloading the
76236e0bf6eSScott Long 		 * driver module with the intent to reload it later.
76336e0bf6eSScott Long 		 */
764cbfd045bSScott Long 		if (aac_sync_fib(sc, FsaHostShutdown, AAC_FIBSTATE_SHUTDOWN,
765cbfd045bSScott Long 		    fib, 1)) {
76635863739SMike Smith 			printf("FAILED.\n");
76735863739SMike Smith 		} else {
76835863739SMike Smith 			printf("done.\n");
76935863739SMike Smith 		}
77035863739SMike Smith 	}
77170545d1aSScott Long #endif
77235863739SMike Smith 
77335863739SMike Smith 	AAC_MASK_INTERRUPTS(sc);
7743576af8fSScott Long 	aac_release_sync_fib(sc);
7757cb209f5SScott Long 	mtx_unlock(&sc->aac_io_lock);
77635863739SMike Smith 
77735863739SMike Smith 	return(0);
77835863739SMike Smith }
77935863739SMike Smith 
780914da7d0SScott Long /*
78135863739SMike Smith  * Bring the controller to a quiescent state, ready for system suspend.
78235863739SMike Smith  */
78335863739SMike Smith int
78435863739SMike Smith aac_suspend(device_t dev)
78535863739SMike Smith {
786914da7d0SScott Long 	struct aac_softc *sc;
78735863739SMike Smith 
788914da7d0SScott Long 	sc = device_get_softc(dev);
789914da7d0SScott Long 
79031a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
79135863739SMike Smith 	sc->aac_state |= AAC_STATE_SUSPEND;
79235863739SMike Smith 
79335863739SMike Smith 	AAC_MASK_INTERRUPTS(sc);
79435863739SMike Smith 	return(0);
79535863739SMike Smith }
79635863739SMike Smith 
797914da7d0SScott Long /*
79835863739SMike Smith  * Bring the controller back to a state ready for operation.
79935863739SMike Smith  */
80035863739SMike Smith int
80135863739SMike Smith aac_resume(device_t dev)
80235863739SMike Smith {
803914da7d0SScott Long 	struct aac_softc *sc;
80435863739SMike Smith 
805914da7d0SScott Long 	sc = device_get_softc(dev);
806914da7d0SScott Long 
80731a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
80835863739SMike Smith 	sc->aac_state &= ~AAC_STATE_SUSPEND;
80935863739SMike Smith 	AAC_UNMASK_INTERRUPTS(sc);
81035863739SMike Smith 	return(0);
81135863739SMike Smith }
81235863739SMike Smith 
813914da7d0SScott Long /*
8147cb209f5SScott Long  * Interrupt handler for NEW_COMM interface.
81535863739SMike Smith  */
81635863739SMike Smith void
8177cb209f5SScott Long aac_new_intr(void *arg)
8187cb209f5SScott Long {
8197cb209f5SScott Long 	struct aac_softc *sc;
8207cb209f5SScott Long 	u_int32_t index, fast;
8217cb209f5SScott Long 	struct aac_command *cm;
8227cb209f5SScott Long 	struct aac_fib *fib;
8237cb209f5SScott Long 	int i;
8247cb209f5SScott Long 
8257cb209f5SScott Long 	sc = (struct aac_softc *)arg;
8267cb209f5SScott Long 
82731a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
8287cb209f5SScott Long 	mtx_lock(&sc->aac_io_lock);
8297cb209f5SScott Long 	while (1) {
8307cb209f5SScott Long 		index = AAC_GET_OUTB_QUEUE(sc);
8317cb209f5SScott Long 		if (index == 0xffffffff)
8327cb209f5SScott Long 			index = AAC_GET_OUTB_QUEUE(sc);
8337cb209f5SScott Long 		if (index == 0xffffffff)
8347cb209f5SScott Long 			break;
8357cb209f5SScott Long 		if (index & 2) {
8367cb209f5SScott Long 			if (index == 0xfffffffe) {
8377cb209f5SScott Long 				/* XXX This means that the controller wants
8387cb209f5SScott Long 				 * more work.  Ignore it for now.
8397cb209f5SScott Long 				 */
8407cb209f5SScott Long 				continue;
8417cb209f5SScott Long 			}
8427cb209f5SScott Long 			/* AIF */
8437cb209f5SScott Long 			fib = (struct aac_fib *)malloc(sizeof *fib, M_AACBUF,
8447cb209f5SScott Long 				   M_NOWAIT | M_ZERO);
8457cb209f5SScott Long 			if (fib == NULL) {
8467cb209f5SScott Long 				/* If we're really this short on memory,
8477cb209f5SScott Long 				 * hopefully breaking out of the handler will
8487cb209f5SScott Long 				 * allow something to get freed.  This
8497cb209f5SScott Long 				 * actually sucks a whole lot.
8507cb209f5SScott Long 				 */
8517cb209f5SScott Long 				break;
8527cb209f5SScott Long 			}
8537cb209f5SScott Long 			index &= ~2;
8547cb209f5SScott Long 			for (i = 0; i < sizeof(struct aac_fib)/4; ++i)
855ff0991c4SAttilio Rao 				((u_int32_t *)fib)[i] = AAC_MEM1_GETREG4(sc, index + i*4);
8567cb209f5SScott Long 			aac_handle_aif(sc, fib);
8577cb209f5SScott Long 			free(fib, M_AACBUF);
8587cb209f5SScott Long 
8597cb209f5SScott Long 			/*
8607cb209f5SScott Long 			 * AIF memory is owned by the adapter, so let it
8617cb209f5SScott Long 			 * know that we are done with it.
8627cb209f5SScott Long 			 */
8637cb209f5SScott Long 			AAC_SET_OUTB_QUEUE(sc, index);
8647cb209f5SScott Long 			AAC_CLEAR_ISTATUS(sc, AAC_DB_RESPONSE_READY);
8657cb209f5SScott Long 		} else {
8667cb209f5SScott Long 			fast = index & 1;
8677cb209f5SScott Long 			cm = sc->aac_commands + (index >> 2);
8687cb209f5SScott Long 			fib = cm->cm_fib;
8697cb209f5SScott Long 			if (fast) {
8707cb209f5SScott Long 				fib->Header.XferState |= AAC_FIBSTATE_DONEADAP;
8717cb209f5SScott Long 				*((u_int32_t *)(fib->data)) = AAC_ERROR_NORMAL;
8727cb209f5SScott Long 			}
8737cb209f5SScott Long 			aac_remove_busy(cm);
8747cb209f5SScott Long  			aac_unmap_command(cm);
8757cb209f5SScott Long 			cm->cm_flags |= AAC_CMD_COMPLETED;
8767cb209f5SScott Long 
8777cb209f5SScott Long 			/* is there a completion handler? */
8787cb209f5SScott Long 			if (cm->cm_complete != NULL) {
8797cb209f5SScott Long 				cm->cm_complete(cm);
8807cb209f5SScott Long 			} else {
8817cb209f5SScott Long 				/* assume that someone is sleeping on this
8827cb209f5SScott Long 				 * command
8837cb209f5SScott Long 				 */
8847cb209f5SScott Long 				wakeup(cm);
8857cb209f5SScott Long 			}
8867cb209f5SScott Long 			sc->flags &= ~AAC_QUEUE_FRZN;
8877cb209f5SScott Long 		}
8887cb209f5SScott Long 	}
8897cb209f5SScott Long 	/* see if we can start some more I/O */
8907cb209f5SScott Long 	if ((sc->flags & AAC_QUEUE_FRZN) == 0)
8917cb209f5SScott Long 		aac_startio(sc);
8927cb209f5SScott Long 
8937cb209f5SScott Long 	mtx_unlock(&sc->aac_io_lock);
8947cb209f5SScott Long }
8957cb209f5SScott Long 
896e46b9eeaSEd Maste /*
897e46b9eeaSEd Maste  * Interrupt filter for !NEW_COMM interface.
898e46b9eeaSEd Maste  */
899ef544f63SPaolo Pisati int
900e46b9eeaSEd Maste aac_filter(void *arg)
90135863739SMike Smith {
902914da7d0SScott Long 	struct aac_softc *sc;
90370545d1aSScott Long 	u_int16_t reason;
90435863739SMike Smith 
905914da7d0SScott Long 	sc = (struct aac_softc *)arg;
906914da7d0SScott Long 
90731a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
908f30ac74cSScott Long 	/*
9099148fa21SScott Long 	 * Read the status register directly.  This is faster than taking the
9109148fa21SScott Long 	 * driver lock and reading the queues directly.  It also saves having
9119148fa21SScott Long 	 * to turn parts of the driver lock into a spin mutex, which would be
9129148fa21SScott Long 	 * ugly.
913f30ac74cSScott Long 	 */
91435863739SMike Smith 	reason = AAC_GET_ISTATUS(sc);
915f30ac74cSScott Long 	AAC_CLEAR_ISTATUS(sc, reason);
916f30ac74cSScott Long 
9179c3a7fceSScott Long 	/* handle completion processing */
9189148fa21SScott Long 	if (reason & AAC_DB_RESPONSE_READY)
919cbc4d2dbSJohn Baldwin 		taskqueue_enqueue(taskqueue_fast, &sc->aac_task_complete);
92035863739SMike Smith 
9219148fa21SScott Long 	/* controller wants to talk to us */
9229148fa21SScott Long 	if (reason & (AAC_DB_PRINTF | AAC_DB_COMMAND_READY)) {
92370545d1aSScott Long 		/*
9249148fa21SScott Long 		 * XXX Make sure that we don't get fooled by strange messages
9259148fa21SScott Long 		 * that start with a NULL.
92670545d1aSScott Long 		 */
9279148fa21SScott Long 		if ((reason & AAC_DB_PRINTF) &&
9289148fa21SScott Long 			(sc->aac_common->ac_printf[0] == 0))
9299148fa21SScott Long 			sc->aac_common->ac_printf[0] = 32;
93070545d1aSScott Long 
9319148fa21SScott Long 		/*
9329148fa21SScott Long 		 * This might miss doing the actual wakeup.  However, the
933a32a982dSScott Long 		 * msleep that this is waking up has a timeout, so it will
9349148fa21SScott Long 		 * wake up eventually.  AIFs and printfs are low enough
9359148fa21SScott Long 		 * priority that they can handle hanging out for a few seconds
9369148fa21SScott Long 		 * if needed.
9379148fa21SScott Long 		 */
93836e0bf6eSScott Long 		wakeup(sc->aifthread);
93936e0bf6eSScott Long 	}
940ef544f63SPaolo Pisati 	return (FILTER_HANDLED);
9419148fa21SScott Long }
94235863739SMike Smith 
943c6eafcf2SScott Long /*
944914da7d0SScott Long  * Command Processing
945914da7d0SScott Long  */
94635863739SMike Smith 
947914da7d0SScott Long /*
94835863739SMike Smith  * Start as much queued I/O as possible on the controller
94935863739SMike Smith  */
950fe3cb0e1SScott Long void
95135863739SMike Smith aac_startio(struct aac_softc *sc)
95235863739SMike Smith {
95335863739SMike Smith 	struct aac_command *cm;
954397fa34fSScott Long 	int error;
95535863739SMike Smith 
95631a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
95735863739SMike Smith 
95835863739SMike Smith 	for (;;) {
959914da7d0SScott Long 		/*
960397fa34fSScott Long 		 * This flag might be set if the card is out of resources.
961397fa34fSScott Long 		 * Checking it here prevents an infinite loop of deferrals.
962397fa34fSScott Long 		 */
963397fa34fSScott Long 		if (sc->flags & AAC_QUEUE_FRZN)
964397fa34fSScott Long 			break;
965397fa34fSScott Long 
966397fa34fSScott Long 		/*
967914da7d0SScott Long 		 * Try to get a command that's been put off for lack of
968914da7d0SScott Long 		 * resources
969914da7d0SScott Long 		 */
97035863739SMike Smith 		cm = aac_dequeue_ready(sc);
97135863739SMike Smith 
972914da7d0SScott Long 		/*
973914da7d0SScott Long 		 * Try to build a command off the bio queue (ignore error
974914da7d0SScott Long 		 * return)
975914da7d0SScott Long 		 */
9760b94a66eSMike Smith 		if (cm == NULL)
97735863739SMike Smith 			aac_bio_command(sc, &cm);
97835863739SMike Smith 
97935863739SMike Smith 		/* nothing to do? */
98035863739SMike Smith 		if (cm == NULL)
98135863739SMike Smith 			break;
98235863739SMike Smith 
983cd481291SScott Long 		/* don't map more than once */
984cd481291SScott Long 		if (cm->cm_flags & AAC_CMD_MAPPED)
9854102d44bSScott Long 			panic("aac: command %p already mapped", cm);
98635863739SMike Smith 
987397fa34fSScott Long 		/*
988397fa34fSScott Long 		 * Set up the command to go to the controller.  If there are no
989397fa34fSScott Long 		 * data buffers associated with the command then it can bypass
990397fa34fSScott Long 		 * busdma.
991397fa34fSScott Long 		 */
992cd481291SScott Long 		if (cm->cm_datalen != 0) {
9938fce673cSMarius Strobl 			if (cm->cm_flags & AAC_REQ_BIO)
9948fce673cSMarius Strobl 				error = bus_dmamap_load_bio(
9958fce673cSMarius Strobl 				    sc->aac_buffer_dmat, cm->cm_datamap,
9968fce673cSMarius Strobl 				    (struct bio *)cm->cm_private,
9978fce673cSMarius Strobl 				    aac_map_command_sg, cm, 0);
9988fce673cSMarius Strobl 			else
999397fa34fSScott Long 				error = bus_dmamap_load(sc->aac_buffer_dmat,
1000397fa34fSScott Long 				    cm->cm_datamap, cm->cm_data,
10018fce673cSMarius Strobl 				    cm->cm_datalen, aac_map_command_sg, cm, 0);
1002cd481291SScott Long 			if (error == EINPROGRESS) {
100331a0399eSEd Maste 				fwprintf(sc, HBA_FLAGS_DBG_COMM_B, "freezing queue\n");
1004cd481291SScott Long 				sc->flags |= AAC_QUEUE_FRZN;
1005614c22b2SScott Long 			} else if (error != 0)
1006397fa34fSScott Long 				panic("aac_startio: unexpected error %d from "
1007a620bad0SEd Maste 				      "busdma", error);
1008397fa34fSScott Long 		} else
10098778f63dSScott Long 			aac_map_command_sg(cm, NULL, 0, 0);
1010cd481291SScott Long 	}
101135863739SMike Smith }
101235863739SMike Smith 
1013914da7d0SScott Long /*
101435863739SMike Smith  * Handle notification of one or more FIBs coming from the controller.
101535863739SMike Smith  */
101635863739SMike Smith static void
101770545d1aSScott Long aac_command_thread(struct aac_softc *sc)
101835863739SMike Smith {
101935863739SMike Smith 	struct aac_fib *fib;
102035863739SMike Smith 	u_int32_t fib_size;
10219148fa21SScott Long 	int size, retval;
102235863739SMike Smith 
102331a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
102435863739SMike Smith 
1025bb6fe253SScott Long 	mtx_lock(&sc->aac_io_lock);
1026a32a982dSScott Long 	sc->aifflags = AAC_AIFFLAGS_RUNNING;
102736e0bf6eSScott Long 
1028a32a982dSScott Long 	while ((sc->aifflags & AAC_AIFFLAGS_EXIT) == 0) {
1029a32a982dSScott Long 
1030a32a982dSScott Long 		retval = 0;
1031a32a982dSScott Long 		if ((sc->aifflags & AAC_AIFFLAGS_PENDING) == 0)
1032a32a982dSScott Long 			retval = msleep(sc->aifthread, &sc->aac_io_lock, PRIBIO,
1033a32a982dSScott Long 					"aifthd", AAC_PERIODIC_INTERVAL * hz);
103436e0bf6eSScott Long 
10359148fa21SScott Long 		/*
10369148fa21SScott Long 		 * First see if any FIBs need to be allocated.  This needs
10379148fa21SScott Long 		 * to be called without the driver lock because contigmalloc
10381bd320ecSAttilio Rao 		 * can sleep.
10399148fa21SScott Long 		 */
10409148fa21SScott Long 		if ((sc->aifflags & AAC_AIFFLAGS_ALLOCFIBS) != 0) {
1041bb6fe253SScott Long 			mtx_unlock(&sc->aac_io_lock);
1042a32a982dSScott Long 			aac_alloc_commands(sc);
1043bb6fe253SScott Long 			mtx_lock(&sc->aac_io_lock);
10444102d44bSScott Long 			sc->aifflags &= ~AAC_AIFFLAGS_ALLOCFIBS;
1045a32a982dSScott Long 			aac_startio(sc);
1046a32a982dSScott Long 		}
10479148fa21SScott Long 
10489148fa21SScott Long 		/*
10499148fa21SScott Long 		 * While we're here, check to see if any commands are stuck.
10509148fa21SScott Long 		 * This is pretty low-priority, so it's ok if it doesn't
10519148fa21SScott Long 		 * always fire.
10529148fa21SScott Long 		 */
10539148fa21SScott Long 		if (retval == EWOULDBLOCK)
105470545d1aSScott Long 			aac_timeout(sc);
105570545d1aSScott Long 
105670545d1aSScott Long 		/* Check the hardware printf message buffer */
10579148fa21SScott Long 		if (sc->aac_common->ac_printf[0] != 0)
105870545d1aSScott Long 			aac_print_printf(sc);
105970545d1aSScott Long 
10609148fa21SScott Long 		/* Also check to see if the adapter has a command for us. */
10617cb209f5SScott Long 		if (sc->flags & AAC_FLAGS_NEW_COMM)
10627cb209f5SScott Long 			continue;
10637cb209f5SScott Long 		for (;;) {
10647cb209f5SScott Long 			if (aac_dequeue_fib(sc, AAC_HOST_NORM_CMD_QUEUE,
10657cb209f5SScott Long 					   &fib_size, &fib))
10667cb209f5SScott Long 				break;
106735863739SMike Smith 
106836e0bf6eSScott Long 			AAC_PRINT_FIB(sc, fib);
106936e0bf6eSScott Long 
107035863739SMike Smith 			switch (fib->Header.Command) {
107135863739SMike Smith 			case AifRequest:
107236e0bf6eSScott Long 				aac_handle_aif(sc, fib);
107335863739SMike Smith 				break;
107435863739SMike Smith 			default:
1075914da7d0SScott Long 				device_printf(sc->aac_dev, "unknown command "
1076914da7d0SScott Long 					      "from controller\n");
107735863739SMike Smith 				break;
107835863739SMike Smith 			}
107935863739SMike Smith 
108036e0bf6eSScott Long 			if ((fib->Header.XferState == 0) ||
10817cb209f5SScott Long 			    (fib->Header.StructType != AAC_FIBTYPE_TFIB)) {
108236e0bf6eSScott Long 				break;
10837cb209f5SScott Long 			}
108436e0bf6eSScott Long 
108570545d1aSScott Long 			/* Return the AIF to the controller. */
108636e0bf6eSScott Long 			if (fib->Header.XferState & AAC_FIBSTATE_FROMADAP) {
108736e0bf6eSScott Long 				fib->Header.XferState |= AAC_FIBSTATE_DONEHOST;
108836e0bf6eSScott Long 				*(AAC_FSAStatus*)fib->data = ST_OK;
108936e0bf6eSScott Long 
109036e0bf6eSScott Long 				/* XXX Compute the Size field? */
109136e0bf6eSScott Long 				size = fib->Header.Size;
109236e0bf6eSScott Long 				if (size > sizeof(struct aac_fib)) {
109336e0bf6eSScott Long 					size = sizeof(struct aac_fib);
109436e0bf6eSScott Long 					fib->Header.Size = size;
109536e0bf6eSScott Long 				}
109636e0bf6eSScott Long 				/*
1097914da7d0SScott Long 				 * Since we did not generate this command, it
1098914da7d0SScott Long 				 * cannot go through the normal
1099914da7d0SScott Long 				 * enqueue->startio chain.
110036e0bf6eSScott Long 				 */
1101914da7d0SScott Long 				aac_enqueue_response(sc,
1102914da7d0SScott Long 						 AAC_ADAP_NORM_RESP_QUEUE,
1103914da7d0SScott Long 						 fib);
110436e0bf6eSScott Long 			}
110536e0bf6eSScott Long 		}
110636e0bf6eSScott Long 	}
110736e0bf6eSScott Long 	sc->aifflags &= ~AAC_AIFFLAGS_RUNNING;
1108bb6fe253SScott Long 	mtx_unlock(&sc->aac_io_lock);
110936e0bf6eSScott Long 	wakeup(sc->aac_dev);
111036e0bf6eSScott Long 
11113745c395SJulian Elischer 	kproc_exit(0);
111235863739SMike Smith }
111335863739SMike Smith 
1114914da7d0SScott Long /*
11159c3a7fceSScott Long  * Process completed commands.
111635863739SMike Smith  */
111735863739SMike Smith static void
11189c3a7fceSScott Long aac_complete(void *context, int pending)
111935863739SMike Smith {
11209c3a7fceSScott Long 	struct aac_softc *sc;
112135863739SMike Smith 	struct aac_command *cm;
112235863739SMike Smith 	struct aac_fib *fib;
112335863739SMike Smith 	u_int32_t fib_size;
112435863739SMike Smith 
11259c3a7fceSScott Long 	sc = (struct aac_softc *)context;
112631a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
11279c3a7fceSScott Long 
1128bb6fe253SScott Long 	mtx_lock(&sc->aac_io_lock);
1129ae543596SScott Long 
11309c3a7fceSScott Long 	/* pull completed commands off the queue */
113135863739SMike Smith 	for (;;) {
113235863739SMike Smith 		/* look for completed FIBs on our queue */
1133914da7d0SScott Long 		if (aac_dequeue_fib(sc, AAC_HOST_NORM_RESP_QUEUE, &fib_size,
1134914da7d0SScott Long 							&fib))
113535863739SMike Smith 			break;	/* nothing to do */
113635863739SMike Smith 
1137ecd1c51fSScott Long 		/* get the command, unmap and hand off for processing */
1138cb0d64b9SScott Long 		cm = sc->aac_commands + fib->Header.SenderData;
113935863739SMike Smith 		if (cm == NULL) {
114035863739SMike Smith 			AAC_PRINT_FIB(sc, fib);
11419c3a7fceSScott Long 			break;
11429c3a7fceSScott Long 		}
11433e507710SEd Maste 		if ((cm->cm_flags & AAC_CMD_TIMEDOUT) != 0)
11443e507710SEd Maste 			device_printf(sc->aac_dev,
11453e507710SEd Maste 			    "COMMAND %p COMPLETED AFTER %d SECONDS\n",
11463e507710SEd Maste 			    cm, (int)(time_uptime-cm->cm_timestamp));
11473e507710SEd Maste 
11480b94a66eSMike Smith 		aac_remove_busy(cm);
11497cb209f5SScott Long 
1150ecd1c51fSScott Long  		aac_unmap_command(cm);
115135863739SMike Smith 		cm->cm_flags |= AAC_CMD_COMPLETED;
115235863739SMike Smith 
115335863739SMike Smith 		/* is there a completion handler? */
115435863739SMike Smith 		if (cm->cm_complete != NULL) {
115535863739SMike Smith 			cm->cm_complete(cm);
115635863739SMike Smith 		} else {
115735863739SMike Smith 			/* assume that someone is sleeping on this command */
115835863739SMike Smith 			wakeup(cm);
115935863739SMike Smith 		}
116035863739SMike Smith 	}
11610b94a66eSMike Smith 
11620b94a66eSMike Smith 	/* see if we can start some more I/O */
1163cd481291SScott Long 	sc->flags &= ~AAC_QUEUE_FRZN;
11640b94a66eSMike Smith 	aac_startio(sc);
1165ae543596SScott Long 
1166bb6fe253SScott Long 	mtx_unlock(&sc->aac_io_lock);
116735863739SMike Smith }
116835863739SMike Smith 
1169914da7d0SScott Long /*
117035863739SMike Smith  * Handle a bio submitted from a disk device.
117135863739SMike Smith  */
117235863739SMike Smith void
117335863739SMike Smith aac_submit_bio(struct bio *bp)
117435863739SMike Smith {
1175914da7d0SScott Long 	struct aac_disk *ad;
1176914da7d0SScott Long 	struct aac_softc *sc;
117735863739SMike Smith 
11787540e65eSScott Long 	ad = (struct aac_disk *)bp->bio_disk->d_drv1;
1179914da7d0SScott Long 	sc = ad->ad_controller;
118031a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
1181914da7d0SScott Long 
118235863739SMike Smith 	/* queue the BIO and try to get some work done */
11830b94a66eSMike Smith 	aac_enqueue_bio(sc, bp);
118435863739SMike Smith 	aac_startio(sc);
118535863739SMike Smith }
118635863739SMike Smith 
1187914da7d0SScott Long /*
118835863739SMike Smith  * Get a bio and build a command to go with it.
118935863739SMike Smith  */
119035863739SMike Smith static int
119135863739SMike Smith aac_bio_command(struct aac_softc *sc, struct aac_command **cmp)
119235863739SMike Smith {
119335863739SMike Smith 	struct aac_command *cm;
119435863739SMike Smith 	struct aac_fib *fib;
119535863739SMike Smith 	struct aac_disk *ad;
119635863739SMike Smith 	struct bio *bp;
119735863739SMike Smith 
119831a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
119935863739SMike Smith 
120035863739SMike Smith 	/* get the resources we will need */
120135863739SMike Smith 	cm = NULL;
1202a32a982dSScott Long 	bp = NULL;
120335863739SMike Smith 	if (aac_alloc_command(sc, &cm))	/* get a command */
120435863739SMike Smith 		goto fail;
1205a32a982dSScott Long 	if ((bp = aac_dequeue_bio(sc)) == NULL)
1206a32a982dSScott Long 		goto fail;
120735863739SMike Smith 
120835863739SMike Smith 	/* fill out the command */
12090b94a66eSMike Smith 	cm->cm_datalen = bp->bio_bcount;
12100b94a66eSMike Smith 	cm->cm_complete = aac_bio_complete;
12118fce673cSMarius Strobl 	cm->cm_flags = AAC_REQ_BIO;
121235863739SMike Smith 	cm->cm_private = bp;
12132b3b0f17SScott Long 	cm->cm_timestamp = time_uptime;
121435863739SMike Smith 
121535863739SMike Smith 	/* build the FIB */
121635863739SMike Smith 	fib = cm->cm_fib;
1217b85f5808SScott Long 	fib->Header.Size = sizeof(struct aac_fib_header);
121835863739SMike Smith 	fib->Header.XferState =
121935863739SMike Smith 		AAC_FIBSTATE_HOSTOWNED   |
122035863739SMike Smith 		AAC_FIBSTATE_INITIALISED |
1221f30ac74cSScott Long 		AAC_FIBSTATE_EMPTY	 |
122235863739SMike Smith 		AAC_FIBSTATE_FROMHOST	 |
122335863739SMike Smith 		AAC_FIBSTATE_REXPECTED   |
1224f30ac74cSScott Long 		AAC_FIBSTATE_NORM	 |
1225f30ac74cSScott Long 		AAC_FIBSTATE_ASYNC	 |
1226f30ac74cSScott Long 		AAC_FIBSTATE_FAST_RESPONSE;
122735863739SMike Smith 
122835863739SMike Smith 	/* build the read/write request */
12297540e65eSScott Long 	ad = (struct aac_disk *)bp->bio_disk->d_drv1;
1230b85f5808SScott Long 
12317cb209f5SScott Long 	if (sc->flags & AAC_FLAGS_RAW_IO) {
12327cb209f5SScott Long 		struct aac_raw_io *raw;
12337cb209f5SScott Long 		raw = (struct aac_raw_io *)&fib->data[0];
12347cb209f5SScott Long 		fib->Header.Command = RawIo;
12357cb209f5SScott Long 		raw->BlockNumber = (u_int64_t)bp->bio_pblkno;
12367cb209f5SScott Long 		raw->ByteCount = bp->bio_bcount;
12377cb209f5SScott Long 		raw->ContainerId = ad->ad_container->co_mntobj.ObjectId;
12387cb209f5SScott Long 		raw->BpTotal = 0;
12397cb209f5SScott Long 		raw->BpComplete = 0;
12407cb209f5SScott Long 		fib->Header.Size += sizeof(struct aac_raw_io);
12417cb209f5SScott Long 		cm->cm_sgtable = (struct aac_sg_table *)&raw->SgMapRaw;
12427cb209f5SScott Long 		if (bp->bio_cmd == BIO_READ) {
12437cb209f5SScott Long 			raw->Flags = 1;
12447cb209f5SScott Long 			cm->cm_flags |= AAC_CMD_DATAIN;
12457cb209f5SScott Long 		} else {
12467cb209f5SScott Long 			raw->Flags = 0;
12477cb209f5SScott Long 			cm->cm_flags |= AAC_CMD_DATAOUT;
12487cb209f5SScott Long 		}
12497cb209f5SScott Long 	} else if ((sc->flags & AAC_FLAGS_SG_64BIT) == 0) {
1250b85f5808SScott Long 		fib->Header.Command = ContainerCommand;
12519e2e96d8SScott Long 		if (bp->bio_cmd == BIO_READ) {
1252b85f5808SScott Long 			struct aac_blockread *br;
125335863739SMike Smith 			br = (struct aac_blockread *)&fib->data[0];
125435863739SMike Smith 			br->Command = VM_CtBlockRead;
125535863739SMike Smith 			br->ContainerId = ad->ad_container->co_mntobj.ObjectId;
125635863739SMike Smith 			br->BlockNumber = bp->bio_pblkno;
125735863739SMike Smith 			br->ByteCount = bp->bio_bcount;
125835863739SMike Smith 			fib->Header.Size += sizeof(struct aac_blockread);
125935863739SMike Smith 			cm->cm_sgtable = &br->SgMap;
126035863739SMike Smith 			cm->cm_flags |= AAC_CMD_DATAIN;
126135863739SMike Smith 		} else {
1262b85f5808SScott Long 			struct aac_blockwrite *bw;
126335863739SMike Smith 			bw = (struct aac_blockwrite *)&fib->data[0];
126435863739SMike Smith 			bw->Command = VM_CtBlockWrite;
126535863739SMike Smith 			bw->ContainerId = ad->ad_container->co_mntobj.ObjectId;
126635863739SMike Smith 			bw->BlockNumber = bp->bio_pblkno;
126735863739SMike Smith 			bw->ByteCount = bp->bio_bcount;
1268b85f5808SScott Long 			bw->Stable = CUNSTABLE;
126935863739SMike Smith 			fib->Header.Size += sizeof(struct aac_blockwrite);
127035863739SMike Smith 			cm->cm_flags |= AAC_CMD_DATAOUT;
127135863739SMike Smith 			cm->cm_sgtable = &bw->SgMap;
127235863739SMike Smith 		}
1273b85f5808SScott Long 	} else {
1274b85f5808SScott Long 		fib->Header.Command = ContainerCommand64;
1275b85f5808SScott Long 		if (bp->bio_cmd == BIO_READ) {
1276b85f5808SScott Long 			struct aac_blockread64 *br;
1277b85f5808SScott Long 			br = (struct aac_blockread64 *)&fib->data[0];
1278b85f5808SScott Long 			br->Command = VM_CtHostRead64;
1279b85f5808SScott Long 			br->ContainerId = ad->ad_container->co_mntobj.ObjectId;
1280b85f5808SScott Long 			br->SectorCount = bp->bio_bcount / AAC_BLOCK_SIZE;
1281b85f5808SScott Long 			br->BlockNumber = bp->bio_pblkno;
1282b85f5808SScott Long 			br->Pad = 0;
1283b85f5808SScott Long 			br->Flags = 0;
1284b85f5808SScott Long 			fib->Header.Size += sizeof(struct aac_blockread64);
128554e2ebdfSEd Maste 			cm->cm_flags |= AAC_CMD_DATAIN;
1286eec256deSAlexander Kabaev 			cm->cm_sgtable = (struct aac_sg_table *)&br->SgMap64;
1287b85f5808SScott Long 		} else {
1288b85f5808SScott Long 			struct aac_blockwrite64 *bw;
1289b85f5808SScott Long 			bw = (struct aac_blockwrite64 *)&fib->data[0];
1290b85f5808SScott Long 			bw->Command = VM_CtHostWrite64;
1291b85f5808SScott Long 			bw->ContainerId = ad->ad_container->co_mntobj.ObjectId;
1292b85f5808SScott Long 			bw->SectorCount = bp->bio_bcount / AAC_BLOCK_SIZE;
1293b85f5808SScott Long 			bw->BlockNumber = bp->bio_pblkno;
1294b85f5808SScott Long 			bw->Pad = 0;
1295b85f5808SScott Long 			bw->Flags = 0;
1296b85f5808SScott Long 			fib->Header.Size += sizeof(struct aac_blockwrite64);
129754e2ebdfSEd Maste 			cm->cm_flags |= AAC_CMD_DATAOUT;
1298eec256deSAlexander Kabaev 			cm->cm_sgtable = (struct aac_sg_table *)&bw->SgMap64;
1299b85f5808SScott Long 		}
1300b85f5808SScott Long 	}
130135863739SMike Smith 
130235863739SMike Smith 	*cmp = cm;
130335863739SMike Smith 	return(0);
130435863739SMike Smith 
130535863739SMike Smith fail:
13067cb209f5SScott Long 	if (bp != NULL)
13077cb209f5SScott Long 		aac_enqueue_bio(sc, bp);
130835863739SMike Smith 	if (cm != NULL)
130935863739SMike Smith 		aac_release_command(cm);
131035863739SMike Smith 	return(ENOMEM);
131135863739SMike Smith }
131235863739SMike Smith 
1313914da7d0SScott Long /*
131435863739SMike Smith  * Handle a bio-instigated command that has been completed.
131535863739SMike Smith  */
131635863739SMike Smith static void
131735863739SMike Smith aac_bio_complete(struct aac_command *cm)
131835863739SMike Smith {
131935863739SMike Smith 	struct aac_blockread_response *brr;
132035863739SMike Smith 	struct aac_blockwrite_response *bwr;
132135863739SMike Smith 	struct bio *bp;
132235863739SMike Smith 	AAC_FSAStatus status;
132335863739SMike Smith 
132435863739SMike Smith 	/* fetch relevant status and then release the command */
132535863739SMike Smith 	bp = (struct bio *)cm->cm_private;
13269e2e96d8SScott Long 	if (bp->bio_cmd == BIO_READ) {
132735863739SMike Smith 		brr = (struct aac_blockread_response *)&cm->cm_fib->data[0];
132835863739SMike Smith 		status = brr->Status;
132935863739SMike Smith 	} else {
133035863739SMike Smith 		bwr = (struct aac_blockwrite_response *)&cm->cm_fib->data[0];
133135863739SMike Smith 		status = bwr->Status;
133235863739SMike Smith 	}
133335863739SMike Smith 	aac_release_command(cm);
133435863739SMike Smith 
133535863739SMike Smith 	/* fix up the bio based on status */
133635863739SMike Smith 	if (status == ST_OK) {
133735863739SMike Smith 		bp->bio_resid = 0;
133835863739SMike Smith 	} else {
133935863739SMike Smith 		bp->bio_error = EIO;
134035863739SMike Smith 		bp->bio_flags |= BIO_ERROR;
134135863739SMike Smith 	}
13420b94a66eSMike Smith 	aac_biodone(bp);
134335863739SMike Smith }
134435863739SMike Smith 
1345914da7d0SScott Long /*
134635863739SMike Smith  * Submit a command to the controller, return when it completes.
1347b3457b51SScott Long  * XXX This is very dangerous!  If the card has gone out to lunch, we could
1348b3457b51SScott Long  *     be stuck here forever.  At the same time, signals are not caught
1349d8a0a473SScott Long  *     because there is a risk that a signal could wakeup the sleep before
1350d8a0a473SScott Long  *     the card has a chance to complete the command.  Since there is no way
1351d8a0a473SScott Long  *     to cancel a command that is in progress, we can't protect against the
1352d8a0a473SScott Long  *     card completing a command late and spamming the command and data
1353d8a0a473SScott Long  *     memory.  So, we are held hostage until the command completes.
135435863739SMike Smith  */
135535863739SMike Smith static int
1356d8a0a473SScott Long aac_wait_command(struct aac_command *cm)
135735863739SMike Smith {
1358ae543596SScott Long 	struct aac_softc *sc;
1359d8a0a473SScott Long 	int error;
136035863739SMike Smith 
1361ae543596SScott Long 	sc = cm->cm_sc;
136231a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
1363ae543596SScott Long 
136435863739SMike Smith 	/* Put the command on the ready queue and get things going */
136535863739SMike Smith 	aac_enqueue_ready(cm);
1366ae543596SScott Long 	aac_startio(sc);
1367ae543596SScott Long 	error = msleep(cm, &sc->aac_io_lock, PRIBIO, "aacwait", 0);
136835863739SMike Smith 	return(error);
136935863739SMike Smith }
137035863739SMike Smith 
1371914da7d0SScott Long /*
1372914da7d0SScott Long  *Command Buffer Management
1373914da7d0SScott Long  */
137435863739SMike Smith 
1375914da7d0SScott Long /*
137635863739SMike Smith  * Allocate a command.
137735863739SMike Smith  */
1378fe3cb0e1SScott Long int
137935863739SMike Smith aac_alloc_command(struct aac_softc *sc, struct aac_command **cmp)
138035863739SMike Smith {
138135863739SMike Smith 	struct aac_command *cm;
138235863739SMike Smith 
138331a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
138435863739SMike Smith 
1385ffb37f33SScott Long 	if ((cm = aac_dequeue_free(sc)) == NULL) {
1386b85f5808SScott Long 		if (sc->total_fibs < sc->aac_max_fibs) {
13871bd320ecSAttilio Rao 			mtx_lock(&sc->aac_io_lock);
1388ae543596SScott Long 			sc->aifflags |= AAC_AIFFLAGS_ALLOCFIBS;
13891bd320ecSAttilio Rao 			mtx_unlock(&sc->aac_io_lock);
1390ae543596SScott Long 			wakeup(sc->aifthread);
1391b85f5808SScott Long 		}
1392ae543596SScott Long 		return (EBUSY);
1393ffb37f33SScott Long 	}
139435863739SMike Smith 
13950b94a66eSMike Smith 	*cmp = cm;
13960b94a66eSMike Smith 	return(0);
13970b94a66eSMike Smith }
13980b94a66eSMike Smith 
1399914da7d0SScott Long /*
14000b94a66eSMike Smith  * Release a command back to the freelist.
14010b94a66eSMike Smith  */
1402fe3cb0e1SScott Long void
14030b94a66eSMike Smith aac_release_command(struct aac_command *cm)
14040b94a66eSMike Smith {
14057cb209f5SScott Long 	struct aac_event *event;
14067cb209f5SScott Long 	struct aac_softc *sc;
14077cb209f5SScott Long 
140831a0399eSEd Maste 	sc = cm->cm_sc;
140931a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
14100b94a66eSMike Smith 
14114109ba51SEd Maste 	/* (re)initialize the command/FIB */
1412b2b39b04SJohn Baldwin 	cm->cm_datalen = 0;
141335863739SMike Smith 	cm->cm_sgtable = NULL;
141435863739SMike Smith 	cm->cm_flags = 0;
141535863739SMike Smith 	cm->cm_complete = NULL;
141635863739SMike Smith 	cm->cm_private = NULL;
1417dbfc5960SEd Maste 	cm->cm_queue = AAC_ADAP_NORM_CMD_QUEUE;
141835863739SMike Smith 	cm->cm_fib->Header.XferState = AAC_FIBSTATE_EMPTY;
141935863739SMike Smith 	cm->cm_fib->Header.StructType = AAC_FIBTYPE_TFIB;
142035863739SMike Smith 	cm->cm_fib->Header.Flags = 0;
14217cb209f5SScott Long 	cm->cm_fib->Header.SenderSize = cm->cm_sc->aac_max_fib_size;
142235863739SMike Smith 
142335863739SMike Smith 	/*
142435863739SMike Smith 	 * These are duplicated in aac_start to cover the case where an
142535863739SMike Smith 	 * intermediate stage may have destroyed them.  They're left
14264109ba51SEd Maste 	 * initialized here for debugging purposes only.
142735863739SMike Smith 	 */
1428f30ac74cSScott Long 	cm->cm_fib->Header.ReceiverFibAddress = (u_int32_t)cm->cm_fibphys;
1429f30ac74cSScott Long 	cm->cm_fib->Header.SenderData = 0;
143035863739SMike Smith 
143135863739SMike Smith 	aac_enqueue_free(cm);
14327cb209f5SScott Long 
14332ad1c92dSEd Maste 	if ((event = TAILQ_FIRST(&sc->aac_ev_cmfree)) != NULL) {
14347cb209f5SScott Long 		TAILQ_REMOVE(&sc->aac_ev_cmfree, event, ev_links);
14357cb209f5SScott Long 		event->ev_callback(sc, event, event->ev_arg);
14367cb209f5SScott Long 	}
143735863739SMike Smith }
143835863739SMike Smith 
1439914da7d0SScott Long /*
14400b94a66eSMike Smith  * Map helper for command/FIB allocation.
144135863739SMike Smith  */
144235863739SMike Smith static void
14430b94a66eSMike Smith aac_map_command_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error)
144435863739SMike Smith {
14457cb209f5SScott Long 	uint64_t	*fibphys;
1446914da7d0SScott Long 
14477cb209f5SScott Long 	fibphys = (uint64_t *)arg;
144835863739SMike Smith 
1449ffb37f33SScott Long 	*fibphys = segs[0].ds_addr;
145035863739SMike Smith }
145135863739SMike Smith 
1452914da7d0SScott Long /*
14534109ba51SEd Maste  * Allocate and initialize commands/FIBs for this adapter.
145435863739SMike Smith  */
14550b94a66eSMike Smith static int
14560b94a66eSMike Smith aac_alloc_commands(struct aac_softc *sc)
145735863739SMike Smith {
145835863739SMike Smith 	struct aac_command *cm;
1459ffb37f33SScott Long 	struct aac_fibmap *fm;
14607cb209f5SScott Long 	uint64_t fibphys;
1461ffb37f33SScott Long 	int i, error;
146235863739SMike Smith 
146331a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
146435863739SMike Smith 
14657cb209f5SScott Long 	if (sc->total_fibs + sc->aac_max_fibs_alloc > sc->aac_max_fibs)
1466ffb37f33SScott Long 		return (ENOMEM);
1467ffb37f33SScott Long 
14688480cc63SScott Long 	fm = malloc(sizeof(struct aac_fibmap), M_AACBUF, M_NOWAIT|M_ZERO);
1469a6d35632SScott Long 	if (fm == NULL)
1470a6d35632SScott Long 		return (ENOMEM);
1471ffb37f33SScott Long 
14720b94a66eSMike Smith 	/* allocate the FIBs in DMAable memory and load them */
1473ffb37f33SScott Long 	if (bus_dmamem_alloc(sc->aac_fib_dmat, (void **)&fm->aac_fibs,
1474ffb37f33SScott Long 			     BUS_DMA_NOWAIT, &fm->aac_fibmap)) {
147570545d1aSScott Long 		device_printf(sc->aac_dev,
147670545d1aSScott Long 			      "Not enough contiguous memory available.\n");
14778480cc63SScott Long 		free(fm, M_AACBUF);
14780b94a66eSMike Smith 		return (ENOMEM);
147935863739SMike Smith 	}
1480128aa5a0SScott Long 
1481cd481291SScott Long 	/* Ignore errors since this doesn't bounce */
1482cd481291SScott Long 	(void)bus_dmamap_load(sc->aac_fib_dmat, fm->aac_fibmap, fm->aac_fibs,
14837cb209f5SScott Long 			      sc->aac_max_fibs_alloc * sc->aac_max_fib_size,
1484ffb37f33SScott Long 			      aac_map_command_helper, &fibphys, 0);
1485128aa5a0SScott Long 
14864109ba51SEd Maste 	/* initialize constant fields in the command structure */
14877cb209f5SScott Long 	bzero(fm->aac_fibs, sc->aac_max_fibs_alloc * sc->aac_max_fib_size);
14887cb209f5SScott Long 	for (i = 0; i < sc->aac_max_fibs_alloc; i++) {
14898480cc63SScott Long 		cm = sc->aac_commands + sc->total_fibs;
1490ffb37f33SScott Long 		fm->aac_commands = cm;
149135863739SMike Smith 		cm->cm_sc = sc;
14927cb209f5SScott Long 		cm->cm_fib = (struct aac_fib *)
14937cb209f5SScott Long 			((u_int8_t *)fm->aac_fibs + i*sc->aac_max_fib_size);
14947cb209f5SScott Long 		cm->cm_fibphys = fibphys + i*sc->aac_max_fib_size;
1495cb0d64b9SScott Long 		cm->cm_index = sc->total_fibs;
149635863739SMike Smith 
1497ffb37f33SScott Long 		if ((error = bus_dmamap_create(sc->aac_buffer_dmat, 0,
149893cfca22SScott Long 					       &cm->cm_datamap)) != 0)
14998480cc63SScott Long 			break;
150093cfca22SScott Long 		mtx_lock(&sc->aac_io_lock);
150193cfca22SScott Long 		aac_release_command(cm);
15028480cc63SScott Long 		sc->total_fibs++;
150393cfca22SScott Long 		mtx_unlock(&sc->aac_io_lock);
150435863739SMike Smith 	}
1505ffb37f33SScott Long 
15068480cc63SScott Long 	if (i > 0) {
150793cfca22SScott Long 		mtx_lock(&sc->aac_io_lock);
1508ffb37f33SScott Long 		TAILQ_INSERT_TAIL(&sc->aac_fibmap_tqh, fm, fm_link);
150931a0399eSEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_COMM_B, "total_fibs= %d\n", sc->total_fibs);
1510bb6fe253SScott Long 		mtx_unlock(&sc->aac_io_lock);
15110b94a66eSMike Smith 		return (0);
151235863739SMike Smith 	}
151335863739SMike Smith 
15148480cc63SScott Long 	bus_dmamap_unload(sc->aac_fib_dmat, fm->aac_fibmap);
15158480cc63SScott Long 	bus_dmamem_free(sc->aac_fib_dmat, fm->aac_fibs, fm->aac_fibmap);
15168480cc63SScott Long 	free(fm, M_AACBUF);
15178480cc63SScott Long 	return (ENOMEM);
15188480cc63SScott Long }
15198480cc63SScott Long 
1520914da7d0SScott Long /*
15210b94a66eSMike Smith  * Free FIBs owned by this adapter.
152235863739SMike Smith  */
152335863739SMike Smith static void
15248480cc63SScott Long aac_free_commands(struct aac_softc *sc)
152535863739SMike Smith {
15268480cc63SScott Long 	struct aac_fibmap *fm;
1527ffb37f33SScott Long 	struct aac_command *cm;
152835863739SMike Smith 	int i;
152935863739SMike Smith 
153031a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
153135863739SMike Smith 
15328480cc63SScott Long 	while ((fm = TAILQ_FIRST(&sc->aac_fibmap_tqh)) != NULL) {
15338480cc63SScott Long 
15348480cc63SScott Long 		TAILQ_REMOVE(&sc->aac_fibmap_tqh, fm, fm_link);
15358480cc63SScott Long 		/*
15368480cc63SScott Long 		 * We check against total_fibs to handle partially
15378480cc63SScott Long 		 * allocated blocks.
15388480cc63SScott Long 		 */
15397cb209f5SScott Long 		for (i = 0; i < sc->aac_max_fibs_alloc && sc->total_fibs--; i++) {
1540ffb37f33SScott Long 			cm = fm->aac_commands + i;
1541ffb37f33SScott Long 			bus_dmamap_destroy(sc->aac_buffer_dmat, cm->cm_datamap);
1542ffb37f33SScott Long 		}
1543ffb37f33SScott Long 		bus_dmamap_unload(sc->aac_fib_dmat, fm->aac_fibmap);
1544ffb37f33SScott Long 		bus_dmamem_free(sc->aac_fib_dmat, fm->aac_fibs, fm->aac_fibmap);
15458480cc63SScott Long 		free(fm, M_AACBUF);
15468480cc63SScott Long 	}
154735863739SMike Smith }
154835863739SMike Smith 
1549914da7d0SScott Long /*
155035863739SMike Smith  * Command-mapping helper function - populate this command's s/g table.
155135863739SMike Smith  */
155235863739SMike Smith static void
155335863739SMike Smith aac_map_command_sg(void *arg, bus_dma_segment_t *segs, int nseg, int error)
155435863739SMike Smith {
1555cd481291SScott Long 	struct aac_softc *sc;
1556914da7d0SScott Long 	struct aac_command *cm;
1557914da7d0SScott Long 	struct aac_fib *fib;
155835863739SMike Smith 	int i;
155935863739SMike Smith 
1560914da7d0SScott Long 	cm = (struct aac_command *)arg;
1561cd481291SScott Long 	sc = cm->cm_sc;
1562914da7d0SScott Long 	fib = cm->cm_fib;
156331a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
1564914da7d0SScott Long 
156535863739SMike Smith 	/* copy into the FIB */
1566b85f5808SScott Long 	if (cm->cm_sgtable != NULL) {
15677cb209f5SScott Long 		if (fib->Header.Command == RawIo) {
15687cb209f5SScott Long 			struct aac_sg_tableraw *sg;
15697cb209f5SScott Long 			sg = (struct aac_sg_tableraw *)cm->cm_sgtable;
15707cb209f5SScott Long 			sg->SgCount = nseg;
15717cb209f5SScott Long 			for (i = 0; i < nseg; i++) {
15727cb209f5SScott Long 				sg->SgEntryRaw[i].SgAddress = segs[i].ds_addr;
15737cb209f5SScott Long 				sg->SgEntryRaw[i].SgByteCount = segs[i].ds_len;
15747cb209f5SScott Long 				sg->SgEntryRaw[i].Next = 0;
15757cb209f5SScott Long 				sg->SgEntryRaw[i].Prev = 0;
15767cb209f5SScott Long 				sg->SgEntryRaw[i].Flags = 0;
15777cb209f5SScott Long 			}
15787cb209f5SScott Long 			/* update the FIB size for the s/g count */
15797cb209f5SScott Long 			fib->Header.Size += nseg*sizeof(struct aac_sg_entryraw);
15807cb209f5SScott Long 		} else if ((cm->cm_sc->flags & AAC_FLAGS_SG_64BIT) == 0) {
1581b85f5808SScott Long 			struct aac_sg_table *sg;
1582b85f5808SScott Long 			sg = cm->cm_sgtable;
158335863739SMike Smith 			sg->SgCount = nseg;
158435863739SMike Smith 			for (i = 0; i < nseg; i++) {
158535863739SMike Smith 				sg->SgEntry[i].SgAddress = segs[i].ds_addr;
158635863739SMike Smith 				sg->SgEntry[i].SgByteCount = segs[i].ds_len;
158735863739SMike Smith 			}
158835863739SMike Smith 			/* update the FIB size for the s/g count */
158935863739SMike Smith 			fib->Header.Size += nseg*sizeof(struct aac_sg_entry);
1590b85f5808SScott Long 		} else {
1591b85f5808SScott Long 			struct aac_sg_table64 *sg;
1592b85f5808SScott Long 			sg = (struct aac_sg_table64 *)cm->cm_sgtable;
1593b85f5808SScott Long 			sg->SgCount = nseg;
1594b85f5808SScott Long 			for (i = 0; i < nseg; i++) {
1595b85f5808SScott Long 				sg->SgEntry64[i].SgAddress = segs[i].ds_addr;
1596b85f5808SScott Long 				sg->SgEntry64[i].SgByteCount = segs[i].ds_len;
159735863739SMike Smith 			}
1598b85f5808SScott Long 			/* update the FIB size for the s/g count */
1599b85f5808SScott Long 			fib->Header.Size += nseg*sizeof(struct aac_sg_entry64);
1600b85f5808SScott Long 		}
1601b85f5808SScott Long 	}
160235863739SMike Smith 
1603cd481291SScott Long 	/* Fix up the address values in the FIB.  Use the command array index
1604cd481291SScott Long 	 * instead of a pointer since these fields are only 32 bits.  Shift
16057cb209f5SScott Long 	 * the SenderFibAddress over to make room for the fast response bit
16067cb209f5SScott Long 	 * and for the AIF bit
160735863739SMike Smith 	 */
16087cb209f5SScott Long 	cm->cm_fib->Header.SenderFibAddress = (cm->cm_index << 2);
16097cb209f5SScott Long 	cm->cm_fib->Header.ReceiverFibAddress = (u_int32_t)cm->cm_fibphys;
161035863739SMike Smith 
1611cd481291SScott Long 	/* save a pointer to the command for speedy reverse-lookup */
1612cd481291SScott Long 	cm->cm_fib->Header.SenderData = cm->cm_index;
161335863739SMike Smith 
161435863739SMike Smith 	if (cm->cm_flags & AAC_CMD_DATAIN)
1615c6eafcf2SScott Long 		bus_dmamap_sync(sc->aac_buffer_dmat, cm->cm_datamap,
1616c6eafcf2SScott Long 				BUS_DMASYNC_PREREAD);
161735863739SMike Smith 	if (cm->cm_flags & AAC_CMD_DATAOUT)
1618c6eafcf2SScott Long 		bus_dmamap_sync(sc->aac_buffer_dmat, cm->cm_datamap,
1619c6eafcf2SScott Long 				BUS_DMASYNC_PREWRITE);
162035863739SMike Smith 	cm->cm_flags |= AAC_CMD_MAPPED;
1621cd481291SScott Long 
16227cb209f5SScott Long 	if (sc->flags & AAC_FLAGS_NEW_COMM) {
16237cb209f5SScott Long 		int count = 10000000L;
16247cb209f5SScott Long 		while (AAC_SEND_COMMAND(sc, cm) != 0) {
16257cb209f5SScott Long 			if (--count == 0) {
16267cb209f5SScott Long 				aac_unmap_command(cm);
16277cb209f5SScott Long 				sc->flags |= AAC_QUEUE_FRZN;
16287cb209f5SScott Long 				aac_requeue_ready(cm);
16297cb209f5SScott Long 			}
16307cb209f5SScott Long 			DELAY(5);			/* wait 5 usec. */
16317cb209f5SScott Long 		}
16327cb209f5SScott Long 	} else {
1633397fa34fSScott Long 		/* Put the FIB on the outbound queue */
16344102d44bSScott Long 		if (aac_enqueue_fib(sc, cm->cm_queue, cm) == EBUSY) {
16354102d44bSScott Long 			aac_unmap_command(cm);
1636397fa34fSScott Long 			sc->flags |= AAC_QUEUE_FRZN;
1637cd481291SScott Long 			aac_requeue_ready(cm);
16384102d44bSScott Long 		}
16397cb209f5SScott Long 	}
164035863739SMike Smith }
164135863739SMike Smith 
1642914da7d0SScott Long /*
164335863739SMike Smith  * Unmap a command from controller-visible space.
164435863739SMike Smith  */
164535863739SMike Smith static void
164635863739SMike Smith aac_unmap_command(struct aac_command *cm)
164735863739SMike Smith {
1648914da7d0SScott Long 	struct aac_softc *sc;
164935863739SMike Smith 
1650914da7d0SScott Long 	sc = cm->cm_sc;
165131a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
1652914da7d0SScott Long 
165335863739SMike Smith 	if (!(cm->cm_flags & AAC_CMD_MAPPED))
165435863739SMike Smith 		return;
165535863739SMike Smith 
165635863739SMike Smith 	if (cm->cm_datalen != 0) {
165735863739SMike Smith 		if (cm->cm_flags & AAC_CMD_DATAIN)
1658c6eafcf2SScott Long 			bus_dmamap_sync(sc->aac_buffer_dmat, cm->cm_datamap,
1659c6eafcf2SScott Long 					BUS_DMASYNC_POSTREAD);
166035863739SMike Smith 		if (cm->cm_flags & AAC_CMD_DATAOUT)
1661c6eafcf2SScott Long 			bus_dmamap_sync(sc->aac_buffer_dmat, cm->cm_datamap,
1662c6eafcf2SScott Long 					BUS_DMASYNC_POSTWRITE);
166335863739SMike Smith 
166435863739SMike Smith 		bus_dmamap_unload(sc->aac_buffer_dmat, cm->cm_datamap);
166535863739SMike Smith 	}
166635863739SMike Smith 	cm->cm_flags &= ~AAC_CMD_MAPPED;
166735863739SMike Smith }
166835863739SMike Smith 
1669914da7d0SScott Long /*
1670914da7d0SScott Long  * Hardware Interface
1671914da7d0SScott Long  */
167235863739SMike Smith 
1673914da7d0SScott Long /*
16744109ba51SEd Maste  * Initialize the adapter.
167535863739SMike Smith  */
167635863739SMike Smith static void
167735863739SMike Smith aac_common_map(void *arg, bus_dma_segment_t *segs, int nseg, int error)
167835863739SMike Smith {
1679914da7d0SScott Long 	struct aac_softc *sc;
168035863739SMike Smith 
1681914da7d0SScott Long 	sc = (struct aac_softc *)arg;
168231a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
1683914da7d0SScott Long 
168435863739SMike Smith 	sc->aac_common_busaddr = segs[0].ds_addr;
168535863739SMike Smith }
168635863739SMike Smith 
1687a6d35632SScott Long static int
1688a6d35632SScott Long aac_check_firmware(struct aac_softc *sc)
1689a6d35632SScott Long {
169004f4d586SEd Maste 	u_int32_t code, major, minor, options = 0, atu_size = 0;
1691da4882c2SMarius Strobl 	int rid, status;
169204f4d586SEd Maste 	time_t then;
1693a6d35632SScott Long 
169431a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
169504f4d586SEd Maste 	/*
169604f4d586SEd Maste 	 * Wait for the adapter to come ready.
169704f4d586SEd Maste 	 */
169804f4d586SEd Maste 	then = time_uptime;
169904f4d586SEd Maste 	do {
170004f4d586SEd Maste 		code = AAC_GET_FWSTATUS(sc);
170104f4d586SEd Maste 		if (code & AAC_SELF_TEST_FAILED) {
170204f4d586SEd Maste 			device_printf(sc->aac_dev, "FATAL: selftest failed\n");
170304f4d586SEd Maste 			return(ENXIO);
170404f4d586SEd Maste 		}
170504f4d586SEd Maste 		if (code & AAC_KERNEL_PANIC) {
170604f4d586SEd Maste 			device_printf(sc->aac_dev,
1707a620bad0SEd Maste 				      "FATAL: controller kernel panic");
170804f4d586SEd Maste 			return(ENXIO);
170904f4d586SEd Maste 		}
171004f4d586SEd Maste 		if (time_uptime > (then + AAC_BOOT_TIMEOUT)) {
171104f4d586SEd Maste 			device_printf(sc->aac_dev,
171204f4d586SEd Maste 				      "FATAL: controller not coming ready, "
171304f4d586SEd Maste 					   "status %x\n", code);
171404f4d586SEd Maste 			return(ENXIO);
171504f4d586SEd Maste 		}
171604f4d586SEd Maste 	} while (!(code & AAC_UP_AND_RUNNING));
1717a6d35632SScott Long 
1718fe94b852SScott Long 	/*
1719fe94b852SScott Long 	 * Retrieve the firmware version numbers.  Dell PERC2/QC cards with
1720fe94b852SScott Long 	 * firmware version 1.x are not compatible with this driver.
1721fe94b852SScott Long 	 */
1722a6d35632SScott Long 	if (sc->flags & AAC_FLAGS_PERC2QC) {
1723fe94b852SScott Long 		if (aac_sync_command(sc, AAC_MONKER_GETKERNVER, 0, 0, 0, 0,
1724fe94b852SScott Long 				     NULL)) {
1725fe94b852SScott Long 			device_printf(sc->aac_dev,
1726fe94b852SScott Long 				      "Error reading firmware version\n");
1727fe94b852SScott Long 			return (EIO);
1728fe94b852SScott Long 		}
1729fe94b852SScott Long 
1730fe94b852SScott Long 		/* These numbers are stored as ASCII! */
1731a6d35632SScott Long 		major = (AAC_GET_MAILBOX(sc, 1) & 0xff) - 0x30;
1732a6d35632SScott Long 		minor = (AAC_GET_MAILBOX(sc, 2) & 0xff) - 0x30;
1733fe94b852SScott Long 		if (major == 1) {
1734fe94b852SScott Long 			device_printf(sc->aac_dev,
1735fe94b852SScott Long 			    "Firmware version %d.%d is not supported.\n",
1736fe94b852SScott Long 			    major, minor);
1737fe94b852SScott Long 			return (EINVAL);
1738fe94b852SScott Long 		}
1739fe94b852SScott Long 	}
1740fe94b852SScott Long 
1741a6d35632SScott Long 	/*
1742a6d35632SScott Long 	 * Retrieve the capabilities/supported options word so we know what
1743a441b3fcSScott Long 	 * work-arounds to enable.  Some firmware revs don't support this
1744a441b3fcSScott Long 	 * command.
1745a6d35632SScott Long 	 */
1746a441b3fcSScott Long 	if (aac_sync_command(sc, AAC_MONKER_GETINFO, 0, 0, 0, 0, &status)) {
1747a441b3fcSScott Long 		if (status != AAC_SRB_STS_INVALID_REQUEST) {
1748a441b3fcSScott Long 			device_printf(sc->aac_dev,
1749a441b3fcSScott Long 			     "RequestAdapterInfo failed\n");
1750a6d35632SScott Long 			return (EIO);
1751a6d35632SScott Long 		}
1752a441b3fcSScott Long 	} else {
1753a6d35632SScott Long 		options = AAC_GET_MAILBOX(sc, 1);
17547cb209f5SScott Long 		atu_size = AAC_GET_MAILBOX(sc, 2);
1755a6d35632SScott Long 		sc->supported_options = options;
1756a6d35632SScott Long 
1757a6d35632SScott Long 		if ((options & AAC_SUPPORTED_4GB_WINDOW) != 0 &&
1758a6d35632SScott Long 		    (sc->flags & AAC_FLAGS_NO4GB) == 0)
1759a6d35632SScott Long 			sc->flags |= AAC_FLAGS_4GB_WINDOW;
1760a6d35632SScott Long 		if (options & AAC_SUPPORTED_NONDASD)
1761a6d35632SScott Long 			sc->flags |= AAC_FLAGS_ENABLE_CAM;
1762cd481291SScott Long 		if ((options & AAC_SUPPORTED_SGMAP_HOST64) != 0
1763cd481291SScott Long 		     && (sizeof(bus_addr_t) > 4)) {
1764a441b3fcSScott Long 			device_printf(sc->aac_dev,
1765a441b3fcSScott Long 			    "Enabling 64-bit address support\n");
1766a6d35632SScott Long 			sc->flags |= AAC_FLAGS_SG_64BIT;
1767a6d35632SScott Long 		}
1768a441b3fcSScott Long 		if ((options & AAC_SUPPORTED_NEW_COMM)
1769da4882c2SMarius Strobl 		 && sc->aac_if->aif_send_command)
17707cb209f5SScott Long 			sc->flags |= AAC_FLAGS_NEW_COMM;
17717cb209f5SScott Long 		if (options & AAC_SUPPORTED_64BIT_ARRAYSIZE)
17727cb209f5SScott Long 			sc->flags |= AAC_FLAGS_ARRAY_64BIT;
1773a441b3fcSScott Long 	}
1774a6d35632SScott Long 
1775a6d35632SScott Long 	/* Check for broken hardware that does a lower number of commands */
17767cb209f5SScott Long 	sc->aac_max_fibs = (sc->flags & AAC_FLAGS_256FIBS ? 256:512);
17777cb209f5SScott Long 
17787cb209f5SScott Long 	/* Remap mem. resource, if required */
17797cb209f5SScott Long 	if ((sc->flags & AAC_FLAGS_NEW_COMM) &&
1780ff0991c4SAttilio Rao 	    atu_size > rman_get_size(sc->aac_regs_res1)) {
1781da4882c2SMarius Strobl 		rid = rman_get_rid(sc->aac_regs_res1);
1782da4882c2SMarius Strobl 		bus_release_resource(sc->aac_dev, SYS_RES_MEMORY, rid,
1783da4882c2SMarius Strobl 		    sc->aac_regs_res1);
1784c47476d7SJustin Hibbits 		sc->aac_regs_res1 = bus_alloc_resource_anywhere(sc->aac_dev,
1785c47476d7SJustin Hibbits 		    SYS_RES_MEMORY, &rid, atu_size, RF_ACTIVE);
1786ff0991c4SAttilio Rao 		if (sc->aac_regs_res1 == NULL) {
1787ff0991c4SAttilio Rao 			sc->aac_regs_res1 = bus_alloc_resource_any(
1788da4882c2SMarius Strobl 			    sc->aac_dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
1789ff0991c4SAttilio Rao 			if (sc->aac_regs_res1 == NULL) {
17907cb209f5SScott Long 				device_printf(sc->aac_dev,
17917cb209f5SScott Long 				    "couldn't allocate register window\n");
17927cb209f5SScott Long 				return (ENXIO);
17937cb209f5SScott Long 			}
17947cb209f5SScott Long 			sc->flags &= ~AAC_FLAGS_NEW_COMM;
17957cb209f5SScott Long 		}
1796ff0991c4SAttilio Rao 		sc->aac_btag1 = rman_get_bustag(sc->aac_regs_res1);
1797ff0991c4SAttilio Rao 		sc->aac_bhandle1 = rman_get_bushandle(sc->aac_regs_res1);
1798ff0991c4SAttilio Rao 
1799ff0991c4SAttilio Rao 		if (sc->aac_hwif == AAC_HWIF_NARK) {
1800ff0991c4SAttilio Rao 			sc->aac_regs_res0 = sc->aac_regs_res1;
1801ff0991c4SAttilio Rao 			sc->aac_btag0 = sc->aac_btag1;
1802ff0991c4SAttilio Rao 			sc->aac_bhandle0 = sc->aac_bhandle1;
1803ff0991c4SAttilio Rao 		}
18047cb209f5SScott Long 	}
18057cb209f5SScott Long 
18067cb209f5SScott Long 	/* Read preferred settings */
18077cb209f5SScott Long 	sc->aac_max_fib_size = sizeof(struct aac_fib);
18087cb209f5SScott Long 	sc->aac_max_sectors = 128;				/* 64KB */
18097cb209f5SScott Long 	if (sc->flags & AAC_FLAGS_SG_64BIT)
1810a441b3fcSScott Long 		sc->aac_sg_tablesize = (AAC_FIB_DATASIZE
18117e7a458eSEd Maste 		 - sizeof(struct aac_blockwrite64))
18127e7a458eSEd Maste 		 / sizeof(struct aac_sg_entry64);
1813a6d35632SScott Long 	else
1814a441b3fcSScott Long 		sc->aac_sg_tablesize = (AAC_FIB_DATASIZE
18157e7a458eSEd Maste 		 - sizeof(struct aac_blockwrite))
18167e7a458eSEd Maste 		 / sizeof(struct aac_sg_entry);
1817a441b3fcSScott Long 
18187cb209f5SScott Long 	if (!aac_sync_command(sc, AAC_MONKER_GETCOMMPREF, 0, 0, 0, 0, NULL)) {
18197cb209f5SScott Long 		options = AAC_GET_MAILBOX(sc, 1);
18207cb209f5SScott Long 		sc->aac_max_fib_size = (options & 0xFFFF);
18217cb209f5SScott Long 		sc->aac_max_sectors = (options >> 16) << 1;
18227cb209f5SScott Long 		options = AAC_GET_MAILBOX(sc, 2);
18237cb209f5SScott Long 		sc->aac_sg_tablesize = (options >> 16);
18247cb209f5SScott Long 		options = AAC_GET_MAILBOX(sc, 3);
18257cb209f5SScott Long 		sc->aac_max_fibs = (options & 0xFFFF);
18267cb209f5SScott Long 	}
18277cb209f5SScott Long 	if (sc->aac_max_fib_size > PAGE_SIZE)
18287cb209f5SScott Long 		sc->aac_max_fib_size = PAGE_SIZE;
18297cb209f5SScott Long 	sc->aac_max_fibs_alloc = PAGE_SIZE / sc->aac_max_fib_size;
1830a6d35632SScott Long 
1831f355c0e0SEd Maste 	if (sc->aac_max_fib_size > sizeof(struct aac_fib)) {
1832f355c0e0SEd Maste 		sc->flags |= AAC_FLAGS_RAW_IO;
1833f355c0e0SEd Maste 		device_printf(sc->aac_dev, "Enable Raw I/O\n");
1834f355c0e0SEd Maste 	}
1835523da39bSEd Maste 	if ((sc->flags & AAC_FLAGS_RAW_IO) &&
1836523da39bSEd Maste 	    (sc->flags & AAC_FLAGS_ARRAY_64BIT)) {
1837523da39bSEd Maste 		sc->flags |= AAC_FLAGS_LBA_64BIT;
1838523da39bSEd Maste 		device_printf(sc->aac_dev, "Enable 64-bit array\n");
1839523da39bSEd Maste 	}
1840f355c0e0SEd Maste 
1841fe94b852SScott Long 	return (0);
1842fe94b852SScott Long }
1843fe94b852SScott Long 
184435863739SMike Smith static int
184535863739SMike Smith aac_init(struct aac_softc *sc)
184635863739SMike Smith {
184735863739SMike Smith 	struct aac_adapter_init	*ip;
184804f4d586SEd Maste 	u_int32_t qoffset;
1849a6d35632SScott Long 	int error;
185035863739SMike Smith 
185131a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
1852ffb37f33SScott Long 
185335863739SMike Smith 	/*
1854914da7d0SScott Long 	 * Fill in the init structure.  This tells the adapter about the
1855914da7d0SScott Long 	 * physical location of various important shared data structures.
185635863739SMike Smith 	 */
185735863739SMike Smith 	ip = &sc->aac_common->ac_init;
185835863739SMike Smith 	ip->InitStructRevision = AAC_INIT_STRUCT_REVISION;
18597cb209f5SScott Long 	if (sc->aac_max_fib_size > sizeof(struct aac_fib)) {
18607cb209f5SScott Long 		ip->InitStructRevision = AAC_INIT_STRUCT_REVISION_4;
18617cb209f5SScott Long 		sc->flags |= AAC_FLAGS_RAW_IO;
18627cb209f5SScott Long 	}
1863f30ac74cSScott Long 	ip->MiniPortRevision = AAC_INIT_STRUCT_MINIPORT_REVISION;
186435863739SMike Smith 
1865c6eafcf2SScott Long 	ip->AdapterFibsPhysicalAddress = sc->aac_common_busaddr +
1866c6eafcf2SScott Long 					 offsetof(struct aac_common, ac_fibs);
1867149af931SScott Long 	ip->AdapterFibsVirtualAddress = 0;
186835863739SMike Smith 	ip->AdapterFibsSize = AAC_ADAPTER_FIBS * sizeof(struct aac_fib);
186935863739SMike Smith 	ip->AdapterFibAlign = sizeof(struct aac_fib);
187035863739SMike Smith 
1871c6eafcf2SScott Long 	ip->PrintfBufferAddress = sc->aac_common_busaddr +
1872c6eafcf2SScott Long 				  offsetof(struct aac_common, ac_printf);
187335863739SMike Smith 	ip->PrintfBufferSize = AAC_PRINTF_BUFSIZE;
187435863739SMike Smith 
18754b00f859SScott Long 	/*
18764b00f859SScott Long 	 * The adapter assumes that pages are 4K in size, except on some
18774b00f859SScott Long  	 * broken firmware versions that do the page->byte conversion twice,
18784b00f859SScott Long 	 * therefore 'assuming' that this value is in 16MB units (2^24).
18794b00f859SScott Long 	 * Round up since the granularity is so high.
18804b00f859SScott Long 	 */
1881f30ac74cSScott Long 	ip->HostPhysMemPages = ctob(physmem) / AAC_PAGE_SIZE;
18824b00f859SScott Long 	if (sc->flags & AAC_FLAGS_BROKEN_MEMMAP) {
18834b00f859SScott Long 		ip->HostPhysMemPages =
18844b00f859SScott Long 		    (ip->HostPhysMemPages + AAC_PAGE_SIZE) / AAC_PAGE_SIZE;
1885204c0befSScott Long 	}
18862b3b0f17SScott Long 	ip->HostElapsedSeconds = time_uptime;	/* reset later if invalid */
188735863739SMike Smith 
18887cb209f5SScott Long 	ip->InitFlags = 0;
18897cb209f5SScott Long 	if (sc->flags & AAC_FLAGS_NEW_COMM) {
1890e71d3b9cSEd Maste 		ip->InitFlags |= AAC_INITFLAGS_NEW_COMM_SUPPORTED;
18917cb209f5SScott Long 		device_printf(sc->aac_dev, "New comm. interface enabled\n");
18927cb209f5SScott Long 	}
18937cb209f5SScott Long 
18947cb209f5SScott Long 	ip->MaxIoCommands = sc->aac_max_fibs;
18957cb209f5SScott Long 	ip->MaxIoSize = sc->aac_max_sectors << 9;
18967cb209f5SScott Long 	ip->MaxFibSize = sc->aac_max_fib_size;
18977cb209f5SScott Long 
189835863739SMike Smith 	/*
18994109ba51SEd Maste 	 * Initialize FIB queues.  Note that it appears that the layout of the
1900c6eafcf2SScott Long 	 * indexes and the segmentation of the entries may be mandated by the
1901c6eafcf2SScott Long 	 * adapter, which is only told about the base of the queue index fields.
190235863739SMike Smith 	 *
190335863739SMike Smith 	 * The initial values of the indices are assumed to inform the adapter
1904914da7d0SScott Long 	 * of the sizes of the respective queues, and theoretically it could
1905914da7d0SScott Long 	 * work out the entire layout of the queue structures from this.  We
1906914da7d0SScott Long 	 * take the easy route and just lay this area out like everyone else
1907914da7d0SScott Long 	 * does.
190835863739SMike Smith 	 *
1909914da7d0SScott Long 	 * The Linux driver uses a much more complex scheme whereby several
1910914da7d0SScott Long 	 * header records are kept for each queue.  We use a couple of generic
1911914da7d0SScott Long 	 * list manipulation functions which 'know' the size of each list by
1912914da7d0SScott Long 	 * virtue of a table.
191335863739SMike Smith 	 */
1914b88ffdc8SScott Long 	qoffset = offsetof(struct aac_common, ac_qbuf) + AAC_QUEUE_ALIGN;
19150bcbebd6SScott Long 	qoffset &= ~(AAC_QUEUE_ALIGN - 1);
19160bcbebd6SScott Long 	sc->aac_queues =
19170bcbebd6SScott Long 	    (struct aac_queue_table *)((uintptr_t)sc->aac_common + qoffset);
1918b88ffdc8SScott Long 	ip->CommHeaderAddress = sc->aac_common_busaddr + qoffset;
191935863739SMike Smith 
1920c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_NORM_CMD_QUEUE][AAC_PRODUCER_INDEX] =
1921c6eafcf2SScott Long 		AAC_HOST_NORM_CMD_ENTRIES;
1922c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_NORM_CMD_QUEUE][AAC_CONSUMER_INDEX] =
1923c6eafcf2SScott Long 		AAC_HOST_NORM_CMD_ENTRIES;
1924c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_HIGH_CMD_QUEUE][AAC_PRODUCER_INDEX] =
1925c6eafcf2SScott Long 		AAC_HOST_HIGH_CMD_ENTRIES;
1926c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_HIGH_CMD_QUEUE][AAC_CONSUMER_INDEX] =
1927c6eafcf2SScott Long 		AAC_HOST_HIGH_CMD_ENTRIES;
1928c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_NORM_CMD_QUEUE][AAC_PRODUCER_INDEX] =
1929c6eafcf2SScott Long 		AAC_ADAP_NORM_CMD_ENTRIES;
1930c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_NORM_CMD_QUEUE][AAC_CONSUMER_INDEX] =
1931c6eafcf2SScott Long 		AAC_ADAP_NORM_CMD_ENTRIES;
1932c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_HIGH_CMD_QUEUE][AAC_PRODUCER_INDEX] =
1933c6eafcf2SScott Long 		AAC_ADAP_HIGH_CMD_ENTRIES;
1934c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_HIGH_CMD_QUEUE][AAC_CONSUMER_INDEX] =
1935c6eafcf2SScott Long 		AAC_ADAP_HIGH_CMD_ENTRIES;
1936c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_NORM_RESP_QUEUE][AAC_PRODUCER_INDEX]=
1937c6eafcf2SScott Long 		AAC_HOST_NORM_RESP_ENTRIES;
1938c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_NORM_RESP_QUEUE][AAC_CONSUMER_INDEX]=
1939c6eafcf2SScott Long 		AAC_HOST_NORM_RESP_ENTRIES;
1940c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_HIGH_RESP_QUEUE][AAC_PRODUCER_INDEX]=
1941c6eafcf2SScott Long 		AAC_HOST_HIGH_RESP_ENTRIES;
1942c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_HOST_HIGH_RESP_QUEUE][AAC_CONSUMER_INDEX]=
1943c6eafcf2SScott Long 		AAC_HOST_HIGH_RESP_ENTRIES;
1944c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_NORM_RESP_QUEUE][AAC_PRODUCER_INDEX]=
1945c6eafcf2SScott Long 		AAC_ADAP_NORM_RESP_ENTRIES;
1946c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_NORM_RESP_QUEUE][AAC_CONSUMER_INDEX]=
1947c6eafcf2SScott Long 		AAC_ADAP_NORM_RESP_ENTRIES;
1948c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_HIGH_RESP_QUEUE][AAC_PRODUCER_INDEX]=
1949c6eafcf2SScott Long 		AAC_ADAP_HIGH_RESP_ENTRIES;
1950c6eafcf2SScott Long 	sc->aac_queues->qt_qindex[AAC_ADAP_HIGH_RESP_QUEUE][AAC_CONSUMER_INDEX]=
1951c6eafcf2SScott Long 		AAC_ADAP_HIGH_RESP_ENTRIES;
1952c6eafcf2SScott Long 	sc->aac_qentries[AAC_HOST_NORM_CMD_QUEUE] =
1953c6eafcf2SScott Long 		&sc->aac_queues->qt_HostNormCmdQueue[0];
1954c6eafcf2SScott Long 	sc->aac_qentries[AAC_HOST_HIGH_CMD_QUEUE] =
1955c6eafcf2SScott Long 		&sc->aac_queues->qt_HostHighCmdQueue[0];
1956c6eafcf2SScott Long 	sc->aac_qentries[AAC_ADAP_NORM_CMD_QUEUE] =
1957c6eafcf2SScott Long 		&sc->aac_queues->qt_AdapNormCmdQueue[0];
1958c6eafcf2SScott Long 	sc->aac_qentries[AAC_ADAP_HIGH_CMD_QUEUE] =
1959c6eafcf2SScott Long 		&sc->aac_queues->qt_AdapHighCmdQueue[0];
1960c6eafcf2SScott Long 	sc->aac_qentries[AAC_HOST_NORM_RESP_QUEUE] =
1961c6eafcf2SScott Long 		&sc->aac_queues->qt_HostNormRespQueue[0];
1962c6eafcf2SScott Long 	sc->aac_qentries[AAC_HOST_HIGH_RESP_QUEUE] =
1963c6eafcf2SScott Long 		&sc->aac_queues->qt_HostHighRespQueue[0];
1964c6eafcf2SScott Long 	sc->aac_qentries[AAC_ADAP_NORM_RESP_QUEUE] =
1965c6eafcf2SScott Long 		&sc->aac_queues->qt_AdapNormRespQueue[0];
1966c6eafcf2SScott Long 	sc->aac_qentries[AAC_ADAP_HIGH_RESP_QUEUE] =
1967c6eafcf2SScott Long 		&sc->aac_queues->qt_AdapHighRespQueue[0];
196835863739SMike Smith 
196935863739SMike Smith 	/*
197035863739SMike Smith 	 * Do controller-type-specific initialisation
197135863739SMike Smith 	 */
197235863739SMike Smith 	switch (sc->aac_hwif) {
197335863739SMike Smith 	case AAC_HWIF_I960RX:
1974ff0991c4SAttilio Rao 		AAC_MEM0_SETREG4(sc, AAC_RX_ODBR, ~0);
197535863739SMike Smith 		break;
19764afedc31SScott Long 	case AAC_HWIF_RKT:
1977ff0991c4SAttilio Rao 		AAC_MEM0_SETREG4(sc, AAC_RKT_ODBR, ~0);
19784afedc31SScott Long 		break;
19794afedc31SScott Long 	default:
19804afedc31SScott Long 		break;
198135863739SMike Smith 	}
198235863739SMike Smith 
198335863739SMike Smith 	/*
198435863739SMike Smith 	 * Give the init structure to the controller.
198535863739SMike Smith 	 */
198635863739SMike Smith 	if (aac_sync_command(sc, AAC_MONKER_INITSTRUCT,
1987914da7d0SScott Long 			     sc->aac_common_busaddr +
1988914da7d0SScott Long 			     offsetof(struct aac_common, ac_init), 0, 0, 0,
1989914da7d0SScott Long 			     NULL)) {
1990914da7d0SScott Long 		device_printf(sc->aac_dev,
1991914da7d0SScott Long 			      "error establishing init structure\n");
1992a6d35632SScott Long 		error = EIO;
1993a6d35632SScott Long 		goto out;
199435863739SMike Smith 	}
199535863739SMike Smith 
1996a6d35632SScott Long 	error = 0;
1997a6d35632SScott Long out:
1998a6d35632SScott Long 	return(error);
199935863739SMike Smith }
200035863739SMike Smith 
200104f4d586SEd Maste static int
200204f4d586SEd Maste aac_setup_intr(struct aac_softc *sc)
200304f4d586SEd Maste {
2004da4882c2SMarius Strobl 
200504f4d586SEd Maste 	if (sc->flags & AAC_FLAGS_NEW_COMM) {
200604f4d586SEd Maste 		if (bus_setup_intr(sc->aac_dev, sc->aac_irq,
200704f4d586SEd Maste 				   INTR_MPSAFE|INTR_TYPE_BIO, NULL,
200804f4d586SEd Maste 				   aac_new_intr, sc, &sc->aac_intr)) {
200904f4d586SEd Maste 			device_printf(sc->aac_dev, "can't set up interrupt\n");
201004f4d586SEd Maste 			return (EINVAL);
201104f4d586SEd Maste 		}
201204f4d586SEd Maste 	} else {
201304f4d586SEd Maste 		if (bus_setup_intr(sc->aac_dev, sc->aac_irq,
2014e46b9eeaSEd Maste 				   INTR_TYPE_BIO, aac_filter, NULL,
201504f4d586SEd Maste 				   sc, &sc->aac_intr)) {
201604f4d586SEd Maste 			device_printf(sc->aac_dev,
2017e46b9eeaSEd Maste 				      "can't set up interrupt filter\n");
201804f4d586SEd Maste 			return (EINVAL);
201904f4d586SEd Maste 		}
202004f4d586SEd Maste 	}
202104f4d586SEd Maste 	return (0);
202204f4d586SEd Maste }
202304f4d586SEd Maste 
2024914da7d0SScott Long /*
202535863739SMike Smith  * Send a synchronous command to the controller and wait for a result.
20267cb209f5SScott Long  * Indicate if the controller completed the command with an error status.
202735863739SMike Smith  */
202835863739SMike Smith static int
202935863739SMike Smith aac_sync_command(struct aac_softc *sc, u_int32_t command,
203035863739SMike Smith 		 u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3,
203135863739SMike Smith 		 u_int32_t *sp)
203235863739SMike Smith {
203335863739SMike Smith 	time_t then;
203435863739SMike Smith 	u_int32_t status;
203535863739SMike Smith 
203631a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
203735863739SMike Smith 
203835863739SMike Smith 	/* populate the mailbox */
203935863739SMike Smith 	AAC_SET_MAILBOX(sc, command, arg0, arg1, arg2, arg3);
204035863739SMike Smith 
204135863739SMike Smith 	/* ensure the sync command doorbell flag is cleared */
204235863739SMike Smith 	AAC_CLEAR_ISTATUS(sc, AAC_DB_SYNC_COMMAND);
204335863739SMike Smith 
204435863739SMike Smith 	/* then set it to signal the adapter */
204535863739SMike Smith 	AAC_QNOTIFY(sc, AAC_DB_SYNC_COMMAND);
204635863739SMike Smith 
204735863739SMike Smith 	/* spin waiting for the command to complete */
20482b3b0f17SScott Long 	then = time_uptime;
204935863739SMike Smith 	do {
20502b3b0f17SScott Long 		if (time_uptime > (then + AAC_IMMEDIATE_TIMEOUT)) {
205131a0399eSEd Maste 			fwprintf(sc, HBA_FLAGS_DBG_ERROR_B, "timed out");
205235863739SMike Smith 			return(EIO);
205335863739SMike Smith 		}
205435863739SMike Smith 	} while (!(AAC_GET_ISTATUS(sc) & AAC_DB_SYNC_COMMAND));
205535863739SMike Smith 
205635863739SMike Smith 	/* clear the completion flag */
205735863739SMike Smith 	AAC_CLEAR_ISTATUS(sc, AAC_DB_SYNC_COMMAND);
205835863739SMike Smith 
205935863739SMike Smith 	/* get the command status */
2060a6d35632SScott Long 	status = AAC_GET_MAILBOX(sc, 0);
206135863739SMike Smith 	if (sp != NULL)
206235863739SMike Smith 		*sp = status;
20637cb209f5SScott Long 
2064a441b3fcSScott Long 	if (status != AAC_SRB_STS_SUCCESS)
20657cb209f5SScott Long 		return (-1);
20660b94a66eSMike Smith 	return(0);
206735863739SMike Smith }
206835863739SMike Smith 
2069cbfd045bSScott Long int
207035863739SMike Smith aac_sync_fib(struct aac_softc *sc, u_int32_t command, u_int32_t xferstate,
2071cbfd045bSScott Long 		 struct aac_fib *fib, u_int16_t datasize)
207235863739SMike Smith {
207331a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
20747cb209f5SScott Long 	mtx_assert(&sc->aac_io_lock, MA_OWNED);
207535863739SMike Smith 
207635863739SMike Smith 	if (datasize > AAC_FIB_DATASIZE)
207735863739SMike Smith 		return(EINVAL);
207835863739SMike Smith 
207935863739SMike Smith 	/*
208035863739SMike Smith 	 * Set up the sync FIB
208135863739SMike Smith 	 */
2082914da7d0SScott Long 	fib->Header.XferState = AAC_FIBSTATE_HOSTOWNED |
2083914da7d0SScott Long 				AAC_FIBSTATE_INITIALISED |
2084c6eafcf2SScott Long 				AAC_FIBSTATE_EMPTY;
208535863739SMike Smith 	fib->Header.XferState |= xferstate;
208635863739SMike Smith 	fib->Header.Command = command;
208735863739SMike Smith 	fib->Header.StructType = AAC_FIBTYPE_TFIB;
208842ef13a2SEd Maste 	fib->Header.Size = sizeof(struct aac_fib_header) + datasize;
208935863739SMike Smith 	fib->Header.SenderSize = sizeof(struct aac_fib);
2090b88ffdc8SScott Long 	fib->Header.SenderFibAddress = 0;	/* Not needed */
2091c6eafcf2SScott Long 	fib->Header.ReceiverFibAddress = sc->aac_common_busaddr +
2092914da7d0SScott Long 					 offsetof(struct aac_common,
2093914da7d0SScott Long 						  ac_sync_fib);
209435863739SMike Smith 
209535863739SMike Smith 	/*
209635863739SMike Smith 	 * Give the FIB to the controller, wait for a response.
209735863739SMike Smith 	 */
2098914da7d0SScott Long 	if (aac_sync_command(sc, AAC_MONKER_SYNCFIB,
2099914da7d0SScott Long 			     fib->Header.ReceiverFibAddress, 0, 0, 0, NULL)) {
210031a0399eSEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_ERROR_B, "IO error");
210135863739SMike Smith 		return(EIO);
210235863739SMike Smith 	}
210335863739SMike Smith 
210435863739SMike Smith 	return (0);
210535863739SMike Smith }
210635863739SMike Smith 
2107914da7d0SScott Long /*
210835863739SMike Smith  * Adapter-space FIB queue manipulation
210935863739SMike Smith  *
211035863739SMike Smith  * Note that the queue implementation here is a little funky; neither the PI or
211135863739SMike Smith  * CI will ever be zero.  This behaviour is a controller feature.
211235863739SMike Smith  */
2113da4882c2SMarius Strobl static const struct {
211435863739SMike Smith 	int		size;
211535863739SMike Smith 	int		notify;
211635863739SMike Smith } aac_qinfo[] = {
211735863739SMike Smith 	{AAC_HOST_NORM_CMD_ENTRIES, AAC_DB_COMMAND_NOT_FULL},
211835863739SMike Smith 	{AAC_HOST_HIGH_CMD_ENTRIES, 0},
211935863739SMike Smith 	{AAC_ADAP_NORM_CMD_ENTRIES, AAC_DB_COMMAND_READY},
212035863739SMike Smith 	{AAC_ADAP_HIGH_CMD_ENTRIES, 0},
212135863739SMike Smith 	{AAC_HOST_NORM_RESP_ENTRIES, AAC_DB_RESPONSE_NOT_FULL},
212235863739SMike Smith 	{AAC_HOST_HIGH_RESP_ENTRIES, 0},
212335863739SMike Smith 	{AAC_ADAP_NORM_RESP_ENTRIES, AAC_DB_RESPONSE_READY},
212435863739SMike Smith 	{AAC_ADAP_HIGH_RESP_ENTRIES, 0}
212535863739SMike Smith };
212635863739SMike Smith 
212735863739SMike Smith /*
2128c6eafcf2SScott Long  * Atomically insert an entry into the nominated queue, returns 0 on success or
2129c6eafcf2SScott Long  * EBUSY if the queue is full.
213035863739SMike Smith  *
21310b94a66eSMike Smith  * Note: it would be more efficient to defer notifying the controller in
2132914da7d0SScott Long  *	 the case where we may be inserting several entries in rapid succession,
2133914da7d0SScott Long  *	 but implementing this usefully may be difficult (it would involve a
2134c6eafcf2SScott Long  *	 separate queue/notify interface).
213535863739SMike Smith  */
213635863739SMike Smith static int
2137f6c4dd3fSScott Long aac_enqueue_fib(struct aac_softc *sc, int queue, struct aac_command *cm)
213835863739SMike Smith {
213935863739SMike Smith 	u_int32_t pi, ci;
21409e2e96d8SScott Long 	int error;
2141f6c4dd3fSScott Long 	u_int32_t fib_size;
2142f6c4dd3fSScott Long 	u_int32_t fib_addr;
2143f6c4dd3fSScott Long 
214431a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
214536e0bf6eSScott Long 
2146f6c4dd3fSScott Long 	fib_size = cm->cm_fib->Header.Size;
2147f6c4dd3fSScott Long 	fib_addr = cm->cm_fib->Header.ReceiverFibAddress;
214835863739SMike Smith 
214935863739SMike Smith 	/* get the producer/consumer indices */
215035863739SMike Smith 	pi = sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX];
215135863739SMike Smith 	ci = sc->aac_queues->qt_qindex[queue][AAC_CONSUMER_INDEX];
215235863739SMike Smith 
215335863739SMike Smith 	/* wrap the queue? */
215435863739SMike Smith 	if (pi >= aac_qinfo[queue].size)
215535863739SMike Smith 		pi = 0;
215635863739SMike Smith 
215735863739SMike Smith 	/* check for queue full */
215835863739SMike Smith 	if ((pi + 1) == ci) {
215935863739SMike Smith 		error = EBUSY;
216035863739SMike Smith 		goto out;
216135863739SMike Smith 	}
216235863739SMike Smith 
2163614c22b2SScott Long 	/*
2164614c22b2SScott Long 	 * To avoid a race with its completion interrupt, place this command on
2165614c22b2SScott Long 	 * the busy queue prior to advertising it to the controller.
2166614c22b2SScott Long 	 */
2167614c22b2SScott Long 	aac_enqueue_busy(cm);
2168614c22b2SScott Long 
216935863739SMike Smith 	/* populate queue entry */
217035863739SMike Smith 	(sc->aac_qentries[queue] + pi)->aq_fib_size = fib_size;
217135863739SMike Smith 	(sc->aac_qentries[queue] + pi)->aq_fib_addr = fib_addr;
217235863739SMike Smith 
217335863739SMike Smith 	/* update producer index */
217435863739SMike Smith 	sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX] = pi + 1;
217535863739SMike Smith 
217635863739SMike Smith 	/* notify the adapter if we know how */
217735863739SMike Smith 	if (aac_qinfo[queue].notify != 0)
217835863739SMike Smith 		AAC_QNOTIFY(sc, aac_qinfo[queue].notify);
217935863739SMike Smith 
218035863739SMike Smith 	error = 0;
218135863739SMike Smith 
218235863739SMike Smith out:
218335863739SMike Smith 	return(error);
218435863739SMike Smith }
218535863739SMike Smith 
218635863739SMike Smith /*
218736e0bf6eSScott Long  * Atomically remove one entry from the nominated queue, returns 0 on
218836e0bf6eSScott Long  * success or ENOENT if the queue is empty.
218935863739SMike Smith  */
219035863739SMike Smith static int
2191c6eafcf2SScott Long aac_dequeue_fib(struct aac_softc *sc, int queue, u_int32_t *fib_size,
2192c6eafcf2SScott Long 		struct aac_fib **fib_addr)
219335863739SMike Smith {
219435863739SMike Smith 	u_int32_t pi, ci;
2195149af931SScott Long 	u_int32_t fib_index;
21969e2e96d8SScott Long 	int error;
2197f6c4dd3fSScott Long 	int notify;
219835863739SMike Smith 
219931a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
220035863739SMike Smith 
220135863739SMike Smith 	/* get the producer/consumer indices */
220235863739SMike Smith 	pi = sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX];
220335863739SMike Smith 	ci = sc->aac_queues->qt_qindex[queue][AAC_CONSUMER_INDEX];
220435863739SMike Smith 
220535863739SMike Smith 	/* check for queue empty */
220635863739SMike Smith 	if (ci == pi) {
220735863739SMike Smith 		error = ENOENT;
220835863739SMike Smith 		goto out;
220935863739SMike Smith 	}
221035863739SMike Smith 
22117753acd2SScott Long 	/* wrap the pi so the following test works */
22127753acd2SScott Long 	if (pi >= aac_qinfo[queue].size)
22137753acd2SScott Long 		pi = 0;
22147753acd2SScott Long 
2215f6c4dd3fSScott Long 	notify = 0;
2216f6c4dd3fSScott Long 	if (ci == pi + 1)
2217f6c4dd3fSScott Long 		notify++;
2218f6c4dd3fSScott Long 
221935863739SMike Smith 	/* wrap the queue? */
222035863739SMike Smith 	if (ci >= aac_qinfo[queue].size)
222135863739SMike Smith 		ci = 0;
222235863739SMike Smith 
222335863739SMike Smith 	/* fetch the entry */
222435863739SMike Smith 	*fib_size = (sc->aac_qentries[queue] + ci)->aq_fib_size;
2225149af931SScott Long 
2226149af931SScott Long 	switch (queue) {
2227149af931SScott Long 	case AAC_HOST_NORM_CMD_QUEUE:
2228149af931SScott Long 	case AAC_HOST_HIGH_CMD_QUEUE:
2229149af931SScott Long 		/*
2230149af931SScott Long 		 * The aq_fib_addr is only 32 bits wide so it can't be counted
2231149af931SScott Long 		 * on to hold an address.  For AIF's, the adapter assumes
2232149af931SScott Long 		 * that it's giving us an address into the array of AIF fibs.
2233149af931SScott Long 		 * Therefore, we have to convert it to an index.
2234149af931SScott Long 		 */
2235149af931SScott Long 		fib_index = (sc->aac_qentries[queue] + ci)->aq_fib_addr /
2236149af931SScott Long 			sizeof(struct aac_fib);
2237149af931SScott Long 		*fib_addr = &sc->aac_common->ac_fibs[fib_index];
2238149af931SScott Long 		break;
2239149af931SScott Long 
2240149af931SScott Long 	case AAC_HOST_NORM_RESP_QUEUE:
2241149af931SScott Long 	case AAC_HOST_HIGH_RESP_QUEUE:
2242149af931SScott Long 	{
2243149af931SScott Long 		struct aac_command *cm;
2244149af931SScott Long 
2245149af931SScott Long 		/*
2246149af931SScott Long 		 * As above, an index is used instead of an actual address.
2247149af931SScott Long 		 * Gotta shift the index to account for the fast response
2248149af931SScott Long 		 * bit.  No other correction is needed since this value was
2249149af931SScott Long 		 * originally provided by the driver via the SenderFibAddress
2250149af931SScott Long 		 * field.
2251149af931SScott Long 		 */
2252149af931SScott Long 		fib_index = (sc->aac_qentries[queue] + ci)->aq_fib_addr;
22537cb209f5SScott Long 		cm = sc->aac_commands + (fib_index >> 2);
2254149af931SScott Long 		*fib_addr = cm->cm_fib;
225535863739SMike Smith 
2256f30ac74cSScott Long 		/*
2257f30ac74cSScott Long 		 * Is this a fast response? If it is, update the fib fields in
2258149af931SScott Long 		 * local memory since the whole fib isn't DMA'd back up.
2259f30ac74cSScott Long 		 */
2260149af931SScott Long 		if (fib_index & 0x01) {
2261f30ac74cSScott Long 			(*fib_addr)->Header.XferState |= AAC_FIBSTATE_DONEADAP;
2262f30ac74cSScott Long 			*((u_int32_t*)((*fib_addr)->data)) = AAC_ERROR_NORMAL;
2263f30ac74cSScott Long 		}
2264149af931SScott Long 		break;
2265149af931SScott Long 	}
2266149af931SScott Long 	default:
2267149af931SScott Long 		panic("Invalid queue in aac_dequeue_fib()");
2268149af931SScott Long 		break;
2269149af931SScott Long 	}
2270149af931SScott Long 
227135863739SMike Smith 	/* update consumer index */
227235863739SMike Smith 	sc->aac_queues->qt_qindex[queue][AAC_CONSUMER_INDEX] = ci + 1;
227335863739SMike Smith 
227435863739SMike Smith 	/* if we have made the queue un-full, notify the adapter */
2275f6c4dd3fSScott Long 	if (notify && (aac_qinfo[queue].notify != 0))
227635863739SMike Smith 		AAC_QNOTIFY(sc, aac_qinfo[queue].notify);
227735863739SMike Smith 	error = 0;
227835863739SMike Smith 
227935863739SMike Smith out:
228035863739SMike Smith 	return(error);
228135863739SMike Smith }
228235863739SMike Smith 
2283914da7d0SScott Long /*
228436e0bf6eSScott Long  * Put our response to an Adapter Initialed Fib on the response queue
228536e0bf6eSScott Long  */
228636e0bf6eSScott Long static int
228736e0bf6eSScott Long aac_enqueue_response(struct aac_softc *sc, int queue, struct aac_fib *fib)
228836e0bf6eSScott Long {
228936e0bf6eSScott Long 	u_int32_t pi, ci;
22909e2e96d8SScott Long 	int error;
229136e0bf6eSScott Long 	u_int32_t fib_size;
229236e0bf6eSScott Long 	u_int32_t fib_addr;
229336e0bf6eSScott Long 
229431a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
229536e0bf6eSScott Long 
229636e0bf6eSScott Long 	/* Tell the adapter where the FIB is */
229736e0bf6eSScott Long 	fib_size = fib->Header.Size;
229836e0bf6eSScott Long 	fib_addr = fib->Header.SenderFibAddress;
229936e0bf6eSScott Long 	fib->Header.ReceiverFibAddress = fib_addr;
230036e0bf6eSScott Long 
230136e0bf6eSScott Long 	/* get the producer/consumer indices */
230236e0bf6eSScott Long 	pi = sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX];
230336e0bf6eSScott Long 	ci = sc->aac_queues->qt_qindex[queue][AAC_CONSUMER_INDEX];
230436e0bf6eSScott Long 
230536e0bf6eSScott Long 	/* wrap the queue? */
230636e0bf6eSScott Long 	if (pi >= aac_qinfo[queue].size)
230736e0bf6eSScott Long 		pi = 0;
230836e0bf6eSScott Long 
230936e0bf6eSScott Long 	/* check for queue full */
231036e0bf6eSScott Long 	if ((pi + 1) == ci) {
231136e0bf6eSScott Long 		error = EBUSY;
231236e0bf6eSScott Long 		goto out;
231336e0bf6eSScott Long 	}
231436e0bf6eSScott Long 
231536e0bf6eSScott Long 	/* populate queue entry */
231636e0bf6eSScott Long 	(sc->aac_qentries[queue] + pi)->aq_fib_size = fib_size;
231736e0bf6eSScott Long 	(sc->aac_qentries[queue] + pi)->aq_fib_addr = fib_addr;
231836e0bf6eSScott Long 
231936e0bf6eSScott Long 	/* update producer index */
232036e0bf6eSScott Long 	sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX] = pi + 1;
232136e0bf6eSScott Long 
232236e0bf6eSScott Long 	/* notify the adapter if we know how */
232336e0bf6eSScott Long 	if (aac_qinfo[queue].notify != 0)
232436e0bf6eSScott Long 		AAC_QNOTIFY(sc, aac_qinfo[queue].notify);
232536e0bf6eSScott Long 
232636e0bf6eSScott Long 	error = 0;
232736e0bf6eSScott Long 
232836e0bf6eSScott Long out:
232936e0bf6eSScott Long 	return(error);
233036e0bf6eSScott Long }
233136e0bf6eSScott Long 
2332914da7d0SScott Long /*
23330b94a66eSMike Smith  * Check for commands that have been outstanding for a suspiciously long time,
23340b94a66eSMike Smith  * and complain about them.
23350b94a66eSMike Smith  */
23360b94a66eSMike Smith static void
23370b94a66eSMike Smith aac_timeout(struct aac_softc *sc)
23380b94a66eSMike Smith {
23390b94a66eSMike Smith 	struct aac_command *cm;
23400b94a66eSMike Smith 	time_t deadline;
234115c37be0SScott Long 	int timedout, code;
23420b94a66eSMike Smith 
2343f6c4dd3fSScott Long 	/*
234470545d1aSScott Long 	 * Traverse the busy command list, bitch about late commands once
2345914da7d0SScott Long 	 * only.
2346914da7d0SScott Long 	 */
234715c37be0SScott Long 	timedout = 0;
23482b3b0f17SScott Long 	deadline = time_uptime - AAC_CMD_TIMEOUT;
23490b94a66eSMike Smith 	TAILQ_FOREACH(cm, &sc->aac_busy, cm_link) {
2350f6c4dd3fSScott Long 		if ((cm->cm_timestamp  < deadline)
23513e507710SEd Maste 		    && !(cm->cm_flags & AAC_CMD_TIMEDOUT)) {
23520b94a66eSMike Smith 			cm->cm_flags |= AAC_CMD_TIMEDOUT;
2353914da7d0SScott Long 			device_printf(sc->aac_dev,
23545aa4bb5bSEd Maste 			    "COMMAND %p (TYPE %d) TIMEOUT AFTER %d SECONDS\n",
23555aa4bb5bSEd Maste 			    cm, cm->cm_fib->Header.Command,
23565aa4bb5bSEd Maste 			    (int)(time_uptime-cm->cm_timestamp));
23570b94a66eSMike Smith 			AAC_PRINT_FIB(sc, cm->cm_fib);
235815c37be0SScott Long 			timedout++;
23590b94a66eSMike Smith 		}
23600b94a66eSMike Smith 	}
23610b94a66eSMike Smith 
236215c37be0SScott Long 	if (timedout) {
236315c37be0SScott Long 		code = AAC_GET_FWSTATUS(sc);
236415c37be0SScott Long 		if (code != AAC_UP_AND_RUNNING) {
236515c37be0SScott Long 			device_printf(sc->aac_dev, "WARNING! Controller is no "
236615c37be0SScott Long 				      "longer running! code= 0x%x\n", code);
236715c37be0SScott Long 		}
236815c37be0SScott Long 	}
23690b94a66eSMike Smith }
23700b94a66eSMike Smith 
2371914da7d0SScott Long /*
2372914da7d0SScott Long  * Interface Function Vectors
2373914da7d0SScott Long  */
237435863739SMike Smith 
2375914da7d0SScott Long /*
237635863739SMike Smith  * Read the current firmware status word.
237735863739SMike Smith  */
237835863739SMike Smith static int
237935863739SMike Smith aac_sa_get_fwstatus(struct aac_softc *sc)
238035863739SMike Smith {
238131a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
238235863739SMike Smith 
2383ff0991c4SAttilio Rao 	return(AAC_MEM0_GETREG4(sc, AAC_SA_FWSTATUS));
238435863739SMike Smith }
238535863739SMike Smith 
238635863739SMike Smith static int
238735863739SMike Smith aac_rx_get_fwstatus(struct aac_softc *sc)
238835863739SMike Smith {
238931a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
239035863739SMike Smith 
23914824be88SEd Maste 	return(AAC_MEM0_GETREG4(sc, sc->flags & AAC_FLAGS_NEW_COMM ?
23924824be88SEd Maste 	    AAC_RX_OMR0 : AAC_RX_FWSTATUS));
239335863739SMike Smith }
239435863739SMike Smith 
2395b3457b51SScott Long static int
23964afedc31SScott Long aac_rkt_get_fwstatus(struct aac_softc *sc)
23974afedc31SScott Long {
239831a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
23994afedc31SScott Long 
24004824be88SEd Maste 	return(AAC_MEM0_GETREG4(sc, sc->flags & AAC_FLAGS_NEW_COMM ?
24014824be88SEd Maste 	    AAC_RKT_OMR0 : AAC_RKT_FWSTATUS));
24024afedc31SScott Long }
24034afedc31SScott Long 
2404914da7d0SScott Long /*
240535863739SMike Smith  * Notify the controller of a change in a given queue
240635863739SMike Smith  */
240735863739SMike Smith 
240835863739SMike Smith static void
240935863739SMike Smith aac_sa_qnotify(struct aac_softc *sc, int qbit)
241035863739SMike Smith {
241131a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
241235863739SMike Smith 
2413ff0991c4SAttilio Rao 	AAC_MEM0_SETREG2(sc, AAC_SA_DOORBELL1_SET, qbit);
241435863739SMike Smith }
241535863739SMike Smith 
241635863739SMike Smith static void
241735863739SMike Smith aac_rx_qnotify(struct aac_softc *sc, int qbit)
241835863739SMike Smith {
241931a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
242035863739SMike Smith 
2421ff0991c4SAttilio Rao 	AAC_MEM0_SETREG4(sc, AAC_RX_IDBR, qbit);
242235863739SMike Smith }
242335863739SMike Smith 
2424b3457b51SScott Long static void
24254afedc31SScott Long aac_rkt_qnotify(struct aac_softc *sc, int qbit)
24264afedc31SScott Long {
242731a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
24284afedc31SScott Long 
2429ff0991c4SAttilio Rao 	AAC_MEM0_SETREG4(sc, AAC_RKT_IDBR, qbit);
24304afedc31SScott Long }
24314afedc31SScott Long 
2432914da7d0SScott Long /*
243335863739SMike Smith  * Get the interrupt reason bits
243435863739SMike Smith  */
243535863739SMike Smith static int
243635863739SMike Smith aac_sa_get_istatus(struct aac_softc *sc)
243735863739SMike Smith {
243831a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
243935863739SMike Smith 
2440ff0991c4SAttilio Rao 	return(AAC_MEM0_GETREG2(sc, AAC_SA_DOORBELL0));
244135863739SMike Smith }
244235863739SMike Smith 
244335863739SMike Smith static int
244435863739SMike Smith aac_rx_get_istatus(struct aac_softc *sc)
244535863739SMike Smith {
244631a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
244735863739SMike Smith 
2448ff0991c4SAttilio Rao 	return(AAC_MEM0_GETREG4(sc, AAC_RX_ODBR));
244935863739SMike Smith }
245035863739SMike Smith 
2451b3457b51SScott Long static int
24524afedc31SScott Long aac_rkt_get_istatus(struct aac_softc *sc)
24534afedc31SScott Long {
245431a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
24554afedc31SScott Long 
2456ff0991c4SAttilio Rao 	return(AAC_MEM0_GETREG4(sc, AAC_RKT_ODBR));
24574afedc31SScott Long }
24584afedc31SScott Long 
2459914da7d0SScott Long /*
246035863739SMike Smith  * Clear some interrupt reason bits
246135863739SMike Smith  */
246235863739SMike Smith static void
246335863739SMike Smith aac_sa_clear_istatus(struct aac_softc *sc, int mask)
246435863739SMike Smith {
246531a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
246635863739SMike Smith 
2467ff0991c4SAttilio Rao 	AAC_MEM0_SETREG2(sc, AAC_SA_DOORBELL0_CLEAR, mask);
246835863739SMike Smith }
246935863739SMike Smith 
247035863739SMike Smith static void
247135863739SMike Smith aac_rx_clear_istatus(struct aac_softc *sc, int mask)
247235863739SMike Smith {
247331a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
247435863739SMike Smith 
2475ff0991c4SAttilio Rao 	AAC_MEM0_SETREG4(sc, AAC_RX_ODBR, mask);
247635863739SMike Smith }
247735863739SMike Smith 
2478b3457b51SScott Long static void
24794afedc31SScott Long aac_rkt_clear_istatus(struct aac_softc *sc, int mask)
24804afedc31SScott Long {
248131a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
24824afedc31SScott Long 
2483ff0991c4SAttilio Rao 	AAC_MEM0_SETREG4(sc, AAC_RKT_ODBR, mask);
24844afedc31SScott Long }
24854afedc31SScott Long 
2486914da7d0SScott Long /*
248735863739SMike Smith  * Populate the mailbox and set the command word
248835863739SMike Smith  */
248935863739SMike Smith static void
249035863739SMike Smith aac_sa_set_mailbox(struct aac_softc *sc, u_int32_t command,
249135863739SMike Smith 		u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3)
249235863739SMike Smith {
249331a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
249435863739SMike Smith 
2495ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, AAC_SA_MAILBOX, command);
2496ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, AAC_SA_MAILBOX + 4, arg0);
2497ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, AAC_SA_MAILBOX + 8, arg1);
2498ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, AAC_SA_MAILBOX + 12, arg2);
2499ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, AAC_SA_MAILBOX + 16, arg3);
250035863739SMike Smith }
250135863739SMike Smith 
250235863739SMike Smith static void
250335863739SMike Smith aac_rx_set_mailbox(struct aac_softc *sc, u_int32_t command,
250435863739SMike Smith 		u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3)
250535863739SMike Smith {
250631a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
250735863739SMike Smith 
2508ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, AAC_RX_MAILBOX, command);
2509ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, AAC_RX_MAILBOX + 4, arg0);
2510ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, AAC_RX_MAILBOX + 8, arg1);
2511ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, AAC_RX_MAILBOX + 12, arg2);
2512ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, AAC_RX_MAILBOX + 16, arg3);
251335863739SMike Smith }
251435863739SMike Smith 
2515b3457b51SScott Long static void
25164afedc31SScott Long aac_rkt_set_mailbox(struct aac_softc *sc, u_int32_t command, u_int32_t arg0,
25174afedc31SScott Long 		    u_int32_t arg1, u_int32_t arg2, u_int32_t arg3)
25184afedc31SScott Long {
251931a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
25204afedc31SScott Long 
2521ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, AAC_RKT_MAILBOX, command);
2522ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, AAC_RKT_MAILBOX + 4, arg0);
2523ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, AAC_RKT_MAILBOX + 8, arg1);
2524ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, AAC_RKT_MAILBOX + 12, arg2);
2525ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, AAC_RKT_MAILBOX + 16, arg3);
25264afedc31SScott Long }
25274afedc31SScott Long 
2528914da7d0SScott Long /*
252935863739SMike Smith  * Fetch the immediate command status word
253035863739SMike Smith  */
253135863739SMike Smith static int
2532a6d35632SScott Long aac_sa_get_mailbox(struct aac_softc *sc, int mb)
253335863739SMike Smith {
253431a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
253535863739SMike Smith 
2536ff0991c4SAttilio Rao 	return(AAC_MEM1_GETREG4(sc, AAC_SA_MAILBOX + (mb * 4)));
253735863739SMike Smith }
253835863739SMike Smith 
253935863739SMike Smith static int
2540a6d35632SScott Long aac_rx_get_mailbox(struct aac_softc *sc, int mb)
254135863739SMike Smith {
254231a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
254335863739SMike Smith 
2544ff0991c4SAttilio Rao 	return(AAC_MEM1_GETREG4(sc, AAC_RX_MAILBOX + (mb * 4)));
254535863739SMike Smith }
254635863739SMike Smith 
2547b3457b51SScott Long static int
25484afedc31SScott Long aac_rkt_get_mailbox(struct aac_softc *sc, int mb)
25494afedc31SScott Long {
255031a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
25514afedc31SScott Long 
2552ff0991c4SAttilio Rao 	return(AAC_MEM1_GETREG4(sc, AAC_RKT_MAILBOX + (mb * 4)));
25534afedc31SScott Long }
25544afedc31SScott Long 
2555914da7d0SScott Long /*
255635863739SMike Smith  * Set/clear interrupt masks
255735863739SMike Smith  */
255835863739SMike Smith static void
255935863739SMike Smith aac_sa_set_interrupts(struct aac_softc *sc, int enable)
256035863739SMike Smith {
256131a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "%sable interrupts", enable ? "en" : "dis");
256235863739SMike Smith 
256335863739SMike Smith 	if (enable) {
2564ff0991c4SAttilio Rao 		AAC_MEM0_SETREG2((sc), AAC_SA_MASK0_CLEAR, AAC_DB_INTERRUPTS);
256535863739SMike Smith 	} else {
2566ff0991c4SAttilio Rao 		AAC_MEM0_SETREG2((sc), AAC_SA_MASK0_SET, ~0);
256735863739SMike Smith 	}
256835863739SMike Smith }
256935863739SMike Smith 
257035863739SMike Smith static void
257135863739SMike Smith aac_rx_set_interrupts(struct aac_softc *sc, int enable)
257235863739SMike Smith {
257331a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "%sable interrupts", enable ? "en" : "dis");
257435863739SMike Smith 
257535863739SMike Smith 	if (enable) {
25767cb209f5SScott Long 		if (sc->flags & AAC_FLAGS_NEW_COMM)
2577ff0991c4SAttilio Rao 			AAC_MEM0_SETREG4(sc, AAC_RX_OIMR, ~AAC_DB_INT_NEW_COMM);
25787cb209f5SScott Long 		else
2579ff0991c4SAttilio Rao 			AAC_MEM0_SETREG4(sc, AAC_RX_OIMR, ~AAC_DB_INTERRUPTS);
258035863739SMike Smith 	} else {
2581ff0991c4SAttilio Rao 		AAC_MEM0_SETREG4(sc, AAC_RX_OIMR, ~0);
258235863739SMike Smith 	}
258335863739SMike Smith }
258435863739SMike Smith 
2585b3457b51SScott Long static void
25864afedc31SScott Long aac_rkt_set_interrupts(struct aac_softc *sc, int enable)
25874afedc31SScott Long {
258831a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "%sable interrupts", enable ? "en" : "dis");
25894afedc31SScott Long 
25904afedc31SScott Long 	if (enable) {
25917cb209f5SScott Long 		if (sc->flags & AAC_FLAGS_NEW_COMM)
2592ff0991c4SAttilio Rao 			AAC_MEM0_SETREG4(sc, AAC_RKT_OIMR, ~AAC_DB_INT_NEW_COMM);
25937cb209f5SScott Long 		else
2594ff0991c4SAttilio Rao 			AAC_MEM0_SETREG4(sc, AAC_RKT_OIMR, ~AAC_DB_INTERRUPTS);
25954afedc31SScott Long 	} else {
2596ff0991c4SAttilio Rao 		AAC_MEM0_SETREG4(sc, AAC_RKT_OIMR, ~0);
25974afedc31SScott Long 	}
25984afedc31SScott Long }
25994afedc31SScott Long 
2600914da7d0SScott Long /*
26017cb209f5SScott Long  * New comm. interface: Send command functions
26027cb209f5SScott Long  */
26037cb209f5SScott Long static int
26047cb209f5SScott Long aac_rx_send_command(struct aac_softc *sc, struct aac_command *cm)
26057cb209f5SScott Long {
26067cb209f5SScott Long 	u_int32_t index, device;
26077cb209f5SScott Long 
260831a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "send command (new comm.)");
26097cb209f5SScott Long 
2610ff0991c4SAttilio Rao 	index = AAC_MEM0_GETREG4(sc, AAC_RX_IQUE);
26117cb209f5SScott Long 	if (index == 0xffffffffL)
2612ff0991c4SAttilio Rao 		index = AAC_MEM0_GETREG4(sc, AAC_RX_IQUE);
26137cb209f5SScott Long 	if (index == 0xffffffffL)
26147cb209f5SScott Long 		return index;
26157cb209f5SScott Long 	aac_enqueue_busy(cm);
26167cb209f5SScott Long 	device = index;
2617ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, device, (u_int32_t)(cm->cm_fibphys & 0xffffffffUL));
26187cb209f5SScott Long 	device += 4;
2619ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, device, (u_int32_t)(cm->cm_fibphys >> 32));
26207cb209f5SScott Long 	device += 4;
2621ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, device, cm->cm_fib->Header.Size);
2622ff0991c4SAttilio Rao 	AAC_MEM0_SETREG4(sc, AAC_RX_IQUE, index);
26237cb209f5SScott Long 	return 0;
26247cb209f5SScott Long }
26257cb209f5SScott Long 
26267cb209f5SScott Long static int
26277cb209f5SScott Long aac_rkt_send_command(struct aac_softc *sc, struct aac_command *cm)
26287cb209f5SScott Long {
26297cb209f5SScott Long 	u_int32_t index, device;
26307cb209f5SScott Long 
263131a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "send command (new comm.)");
26327cb209f5SScott Long 
2633ff0991c4SAttilio Rao 	index = AAC_MEM0_GETREG4(sc, AAC_RKT_IQUE);
26347cb209f5SScott Long 	if (index == 0xffffffffL)
2635ff0991c4SAttilio Rao 		index = AAC_MEM0_GETREG4(sc, AAC_RKT_IQUE);
26367cb209f5SScott Long 	if (index == 0xffffffffL)
26377cb209f5SScott Long 		return index;
26387cb209f5SScott Long 	aac_enqueue_busy(cm);
26397cb209f5SScott Long 	device = index;
2640ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, device, (u_int32_t)(cm->cm_fibphys & 0xffffffffUL));
26417cb209f5SScott Long 	device += 4;
2642ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, device, (u_int32_t)(cm->cm_fibphys >> 32));
26437cb209f5SScott Long 	device += 4;
2644ff0991c4SAttilio Rao 	AAC_MEM1_SETREG4(sc, device, cm->cm_fib->Header.Size);
2645ff0991c4SAttilio Rao 	AAC_MEM0_SETREG4(sc, AAC_RKT_IQUE, index);
26467cb209f5SScott Long 	return 0;
26477cb209f5SScott Long }
26487cb209f5SScott Long 
26497cb209f5SScott Long /*
26507cb209f5SScott Long  * New comm. interface: get, set outbound queue index
26517cb209f5SScott Long  */
26527cb209f5SScott Long static int
26537cb209f5SScott Long aac_rx_get_outb_queue(struct aac_softc *sc)
26547cb209f5SScott Long {
265531a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
26567cb209f5SScott Long 
2657ff0991c4SAttilio Rao 	return(AAC_MEM0_GETREG4(sc, AAC_RX_OQUE));
26587cb209f5SScott Long }
26597cb209f5SScott Long 
26607cb209f5SScott Long static int
26617cb209f5SScott Long aac_rkt_get_outb_queue(struct aac_softc *sc)
26627cb209f5SScott Long {
266331a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
26647cb209f5SScott Long 
2665ff0991c4SAttilio Rao 	return(AAC_MEM0_GETREG4(sc, AAC_RKT_OQUE));
26667cb209f5SScott Long }
26677cb209f5SScott Long 
26687cb209f5SScott Long static void
26697cb209f5SScott Long aac_rx_set_outb_queue(struct aac_softc *sc, int index)
26707cb209f5SScott Long {
267131a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
26727cb209f5SScott Long 
2673ff0991c4SAttilio Rao 	AAC_MEM0_SETREG4(sc, AAC_RX_OQUE, index);
26747cb209f5SScott Long }
26757cb209f5SScott Long 
26767cb209f5SScott Long static void
26777cb209f5SScott Long aac_rkt_set_outb_queue(struct aac_softc *sc, int index)
26787cb209f5SScott Long {
267931a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
26807cb209f5SScott Long 
2681ff0991c4SAttilio Rao 	AAC_MEM0_SETREG4(sc, AAC_RKT_OQUE, index);
26827cb209f5SScott Long }
26837cb209f5SScott Long 
26847cb209f5SScott Long /*
2685914da7d0SScott Long  * Debugging and Diagnostics
2686914da7d0SScott Long  */
268735863739SMike Smith 
2688914da7d0SScott Long /*
268935863739SMike Smith  * Print some information about the controller.
269035863739SMike Smith  */
269135863739SMike Smith static void
269235863739SMike Smith aac_describe_controller(struct aac_softc *sc)
269335863739SMike Smith {
2694cbfd045bSScott Long 	struct aac_fib *fib;
269535863739SMike Smith 	struct aac_adapter_info	*info;
26967ea2d558SEd Maste 	char *adapter_type = "Adaptec RAID controller";
269735863739SMike Smith 
269831a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
269935863739SMike Smith 
270081b3da08SScott Long 	mtx_lock(&sc->aac_io_lock);
270103b5fe51SScott Long 	aac_alloc_sync_fib(sc, &fib);
2702cbfd045bSScott Long 
2703cbfd045bSScott Long 	fib->data[0] = 0;
2704cbfd045bSScott Long 	if (aac_sync_fib(sc, RequestAdapterInfo, 0, fib, 1)) {
270535863739SMike Smith 		device_printf(sc->aac_dev, "RequestAdapterInfo failed\n");
2706fe3cb0e1SScott Long 		aac_release_sync_fib(sc);
270781b3da08SScott Long 		mtx_unlock(&sc->aac_io_lock);
270835863739SMike Smith 		return;
270935863739SMike Smith 	}
271035863739SMike Smith 
2711bd971c49SScott Long 	/* save the kernel revision structure for later use */
2712bd971c49SScott Long 	info = (struct aac_adapter_info *)&fib->data[0];
2713bd971c49SScott Long 	sc->aac_revision = info->KernelRevision;
2714bd971c49SScott Long 
2715bd971c49SScott Long 	if (bootverbose) {
2716b1c56c68SScott Long 		device_printf(sc->aac_dev, "%s %dMHz, %dMB memory "
2717b1c56c68SScott Long 		    "(%dMB cache, %dMB execution), %s\n",
2718c6eafcf2SScott Long 		    aac_describe_code(aac_cpu_variant, info->CpuVariant),
2719b1c56c68SScott Long 		    info->ClockSpeed, info->TotalMem / (1024 * 1024),
2720b1c56c68SScott Long 		    info->BufferMem / (1024 * 1024),
2721b1c56c68SScott Long 		    info->ExecutionMem / (1024 * 1024),
2722914da7d0SScott Long 		    aac_describe_code(aac_battery_platform,
2723914da7d0SScott Long 		    info->batteryPlatform));
272435863739SMike Smith 
2725bd971c49SScott Long 		device_printf(sc->aac_dev,
2726bd971c49SScott Long 		    "Kernel %d.%d-%d, Build %d, S/N %6X\n",
272735863739SMike Smith 		    info->KernelRevision.external.comp.major,
272835863739SMike Smith 		    info->KernelRevision.external.comp.minor,
272935863739SMike Smith 		    info->KernelRevision.external.comp.dash,
273036e0bf6eSScott Long 		    info->KernelRevision.buildNumber,
273136e0bf6eSScott Long 		    (u_int32_t)(info->SerialNumber & 0xffffff));
2732fe3cb0e1SScott Long 
2733a6d35632SScott Long 		device_printf(sc->aac_dev, "Supported Options=%b\n",
2734a6d35632SScott Long 			      sc->supported_options,
2735a6d35632SScott Long 			      "\20"
2736a6d35632SScott Long 			      "\1SNAPSHOT"
2737a6d35632SScott Long 			      "\2CLUSTERS"
2738a6d35632SScott Long 			      "\3WCACHE"
2739a6d35632SScott Long 			      "\4DATA64"
2740a6d35632SScott Long 			      "\5HOSTTIME"
2741a6d35632SScott Long 			      "\6RAID50"
2742a6d35632SScott Long 			      "\7WINDOW4GB"
2743a6d35632SScott Long 			      "\10SCSIUPGD"
2744a6d35632SScott Long 			      "\11SOFTERR"
2745a6d35632SScott Long 			      "\12NORECOND"
2746a6d35632SScott Long 			      "\13SGMAP64"
2747a6d35632SScott Long 			      "\14ALARM"
27487cb209f5SScott Long 			      "\15NONDASD"
27497cb209f5SScott Long 			      "\16SCSIMGT"
27507cb209f5SScott Long 			      "\17RAIDSCSI"
27517cb209f5SScott Long 			      "\21ADPTINFO"
27527cb209f5SScott Long 			      "\22NEWCOMM"
27537cb209f5SScott Long 			      "\23ARRAY64BIT"
27547cb209f5SScott Long 			      "\24HEATSENSOR");
2755a6d35632SScott Long 	}
275655aa1136SEd Maste 
275755aa1136SEd Maste 	if (sc->supported_options & AAC_SUPPORTED_SUPPLEMENT_ADAPTER_INFO) {
275855aa1136SEd Maste 		fib->data[0] = 0;
275955aa1136SEd Maste 		if (aac_sync_fib(sc, RequestSupplementAdapterInfo, 0, fib, 1))
276055aa1136SEd Maste 			device_printf(sc->aac_dev,
276155aa1136SEd Maste 			    "RequestSupplementAdapterInfo failed\n");
276255aa1136SEd Maste 		else
276355aa1136SEd Maste 			adapter_type = ((struct aac_supplement_adapter_info *)
276455aa1136SEd Maste 			    &fib->data[0])->AdapterTypeText;
276555aa1136SEd Maste 	}
276655aa1136SEd Maste 	device_printf(sc->aac_dev, "%s, aac driver %d.%d.%d-%d\n",
276755aa1136SEd Maste 		adapter_type,
27688e7e6335SEd Maste 		AAC_DRIVER_MAJOR_VERSION, AAC_DRIVER_MINOR_VERSION,
27698e7e6335SEd Maste 		AAC_DRIVER_BUGFIX_LEVEL, AAC_DRIVER_BUILD);
277055aa1136SEd Maste 
2771bd971c49SScott Long 	aac_release_sync_fib(sc);
277281b3da08SScott Long 	mtx_unlock(&sc->aac_io_lock);
277335863739SMike Smith }
277435863739SMike Smith 
2775914da7d0SScott Long /*
277635863739SMike Smith  * Look up a text description of a numeric error code and return a pointer to
277735863739SMike Smith  * same.
277835863739SMike Smith  */
2779da4882c2SMarius Strobl static const char *
2780da4882c2SMarius Strobl aac_describe_code(const struct aac_code_lookup *table, u_int32_t code)
278135863739SMike Smith {
278235863739SMike Smith 	int i;
278335863739SMike Smith 
278435863739SMike Smith 	for (i = 0; table[i].string != NULL; i++)
278535863739SMike Smith 		if (table[i].code == code)
278635863739SMike Smith 			return(table[i].string);
278735863739SMike Smith 	return(table[i + 1].string);
278835863739SMike Smith }
278935863739SMike Smith 
2790914da7d0SScott Long /*
2791914da7d0SScott Long  * Management Interface
2792914da7d0SScott Long  */
279335863739SMike Smith 
279435863739SMike Smith static int
279500b4e54aSWarner Losh aac_open(struct cdev *dev, int flags, int fmt, struct thread *td)
279635863739SMike Smith {
2797914da7d0SScott Long 	struct aac_softc *sc;
279835863739SMike Smith 
2799914da7d0SScott Long 	sc = dev->si_drv1;
280031a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
280104f798ecSAttilio Rao 	device_busy(sc->aac_dev);
2802dfe2c294SAttilio Rao 	devfs_set_cdevpriv(sc, aac_cdevpriv_dtor);
280335863739SMike Smith 
280435863739SMike Smith 	return 0;
280535863739SMike Smith }
280635863739SMike Smith 
280735863739SMike Smith static int
280800b4e54aSWarner Losh aac_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td)
280935863739SMike Smith {
2810914da7d0SScott Long 	union aac_statrequest *as;
2811914da7d0SScott Long 	struct aac_softc *sc;
28120b94a66eSMike Smith 	int error = 0;
281335863739SMike Smith 
2814914da7d0SScott Long 	as = (union aac_statrequest *)arg;
2815914da7d0SScott Long 	sc = dev->si_drv1;
281631a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
2817914da7d0SScott Long 
281835863739SMike Smith 	switch (cmd) {
28190b94a66eSMike Smith 	case AACIO_STATS:
28200b94a66eSMike Smith 		switch (as->as_item) {
28210b94a66eSMike Smith 		case AACQ_FREE:
28220b94a66eSMike Smith 		case AACQ_BIO:
28230b94a66eSMike Smith 		case AACQ_READY:
28240b94a66eSMike Smith 		case AACQ_BUSY:
2825c6eafcf2SScott Long 			bcopy(&sc->aac_qstat[as->as_item], &as->as_qstat,
2826c6eafcf2SScott Long 			      sizeof(struct aac_qstat));
28270b94a66eSMike Smith 			break;
28280b94a66eSMike Smith 		default:
28290b94a66eSMike Smith 			error = ENOENT;
28300b94a66eSMike Smith 			break;
28310b94a66eSMike Smith 		}
28320b94a66eSMike Smith 	break;
28330b94a66eSMike Smith 
283435863739SMike Smith 	case FSACTL_SENDFIB:
2835f355c0e0SEd Maste 	case FSACTL_SEND_LARGE_FIB:
2836fb0c27d7SScott Long 		arg = *(caddr_t*)arg;
2837fb0c27d7SScott Long 	case FSACTL_LNX_SENDFIB:
2838f355c0e0SEd Maste 	case FSACTL_LNX_SEND_LARGE_FIB:
283931a0399eSEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_SENDFIB");
284035863739SMike Smith 		error = aac_ioctl_sendfib(sc, arg);
284135863739SMike Smith 		break;
2842f355c0e0SEd Maste 	case FSACTL_SEND_RAW_SRB:
2843f355c0e0SEd Maste 		arg = *(caddr_t*)arg;
2844f355c0e0SEd Maste 	case FSACTL_LNX_SEND_RAW_SRB:
284531a0399eSEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_SEND_RAW_SRB");
2846f355c0e0SEd Maste 		error = aac_ioctl_send_raw_srb(sc, arg);
2847f355c0e0SEd Maste 		break;
284835863739SMike Smith 	case FSACTL_AIF_THREAD:
2849fb0c27d7SScott Long 	case FSACTL_LNX_AIF_THREAD:
285031a0399eSEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_AIF_THREAD");
285135863739SMike Smith 		error = EINVAL;
285235863739SMike Smith 		break;
285335863739SMike Smith 	case FSACTL_OPEN_GET_ADAPTER_FIB:
2854fb0c27d7SScott Long 		arg = *(caddr_t*)arg;
2855fb0c27d7SScott Long 	case FSACTL_LNX_OPEN_GET_ADAPTER_FIB:
285631a0399eSEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_OPEN_GET_ADAPTER_FIB");
2857a723a548SEd Maste 		error = aac_open_aif(sc, arg);
285835863739SMike Smith 		break;
285935863739SMike Smith 	case FSACTL_GET_NEXT_ADAPTER_FIB:
2860fb0c27d7SScott Long 		arg = *(caddr_t*)arg;
2861fb0c27d7SScott Long 	case FSACTL_LNX_GET_NEXT_ADAPTER_FIB:
286231a0399eSEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_GET_NEXT_ADAPTER_FIB");
2863fb0c27d7SScott Long 		error = aac_getnext_aif(sc, arg);
286435863739SMike Smith 		break;
286535863739SMike Smith 	case FSACTL_CLOSE_GET_ADAPTER_FIB:
2866a723a548SEd Maste 		arg = *(caddr_t*)arg;
2867fb0c27d7SScott Long 	case FSACTL_LNX_CLOSE_GET_ADAPTER_FIB:
286831a0399eSEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_CLOSE_GET_ADAPTER_FIB");
2869a723a548SEd Maste 		error = aac_close_aif(sc, arg);
287035863739SMike Smith 		break;
287135863739SMike Smith 	case FSACTL_MINIPORT_REV_CHECK:
2872fb0c27d7SScott Long 		arg = *(caddr_t*)arg;
2873fb0c27d7SScott Long 	case FSACTL_LNX_MINIPORT_REV_CHECK:
287431a0399eSEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_MINIPORT_REV_CHECK");
2875fb0c27d7SScott Long 		error = aac_rev_check(sc, arg);
287635863739SMike Smith 		break;
287736e0bf6eSScott Long 	case FSACTL_QUERY_DISK:
287836e0bf6eSScott Long 		arg = *(caddr_t*)arg;
287936e0bf6eSScott Long 	case FSACTL_LNX_QUERY_DISK:
288031a0399eSEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_QUERY_DISK");
288136e0bf6eSScott Long 		error = aac_query_disk(sc, arg);
288236e0bf6eSScott Long 		break;
288336e0bf6eSScott Long 	case FSACTL_DELETE_DISK:
288436e0bf6eSScott Long 	case FSACTL_LNX_DELETE_DISK:
2885914da7d0SScott Long 		/*
2886914da7d0SScott Long 		 * We don't trust the underland to tell us when to delete a
2887914da7d0SScott Long 		 * container, rather we rely on an AIF coming from the
2888914da7d0SScott Long 		 * controller
2889914da7d0SScott Long 		 */
289036e0bf6eSScott Long 		error = 0;
289136e0bf6eSScott Long 		break;
28927cb209f5SScott Long 	case FSACTL_GET_PCI_INFO:
28937cb209f5SScott Long 		arg = *(caddr_t*)arg;
28947cb209f5SScott Long 	case FSACTL_LNX_GET_PCI_INFO:
289531a0399eSEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_GET_PCI_INFO");
28967cb209f5SScott Long 		error = aac_get_pci_info(sc, arg);
28977cb209f5SScott Long 		break;
28986d307336SEd Maste 	case FSACTL_GET_FEATURES:
28996d307336SEd Maste 		arg = *(caddr_t*)arg;
29006d307336SEd Maste 	case FSACTL_LNX_GET_FEATURES:
29016d307336SEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "FSACTL_GET_FEATURES");
29026d307336SEd Maste 		error = aac_supported_features(sc, arg);
29036d307336SEd Maste 		break;
290435863739SMike Smith 	default:
290531a0399eSEd Maste 		fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "unsupported cmd 0x%lx\n", cmd);
290635863739SMike Smith 		error = EINVAL;
290735863739SMike Smith 		break;
290835863739SMike Smith 	}
290935863739SMike Smith 	return(error);
291035863739SMike Smith }
291135863739SMike Smith 
2912b3457b51SScott Long static int
291300b4e54aSWarner Losh aac_poll(struct cdev *dev, int poll_events, struct thread *td)
2914b3457b51SScott Long {
2915b3457b51SScott Long 	struct aac_softc *sc;
2916ef0b687cSEd Maste 	struct aac_fib_context *ctx;
2917b3457b51SScott Long 	int revents;
2918b3457b51SScott Long 
2919b3457b51SScott Long 	sc = dev->si_drv1;
2920b3457b51SScott Long 	revents = 0;
2921b3457b51SScott Long 
2922bb6fe253SScott Long 	mtx_lock(&sc->aac_aifq_lock);
2923b3457b51SScott Long 	if ((poll_events & (POLLRDNORM | POLLIN)) != 0) {
2924ef0b687cSEd Maste 		for (ctx = sc->fibctx; ctx; ctx = ctx->next) {
2925ef0b687cSEd Maste 			if (ctx->ctx_idx != sc->aifq_idx || ctx->ctx_wrap) {
2926b3457b51SScott Long 				revents |= poll_events & (POLLIN | POLLRDNORM);
2927ef0b687cSEd Maste 				break;
2928ef0b687cSEd Maste 			}
2929ef0b687cSEd Maste 		}
2930b3457b51SScott Long 	}
2931bb6fe253SScott Long 	mtx_unlock(&sc->aac_aifq_lock);
2932b3457b51SScott Long 
2933b3457b51SScott Long 	if (revents == 0) {
2934b3457b51SScott Long 		if (poll_events & (POLLIN | POLLRDNORM))
2935b3457b51SScott Long 			selrecord(td, &sc->rcv_select);
2936b3457b51SScott Long 	}
2937b3457b51SScott Long 
2938b3457b51SScott Long 	return (revents);
2939b3457b51SScott Long }
2940b3457b51SScott Long 
29417cb209f5SScott Long static void
29427cb209f5SScott Long aac_ioctl_event(struct aac_softc *sc, struct aac_event *event, void *arg)
29437cb209f5SScott Long {
29447cb209f5SScott Long 
29457cb209f5SScott Long 	switch (event->ev_type) {
29467cb209f5SScott Long 	case AAC_EVENT_CMFREE:
29470c40d5beSEd Maste 		mtx_assert(&sc->aac_io_lock, MA_OWNED);
29481a681311SLuoqi Chen 		if (aac_alloc_command(sc, (struct aac_command **)arg)) {
29497cb209f5SScott Long 			aac_add_event(sc, event);
29507cb209f5SScott Long 			return;
29517cb209f5SScott Long 		}
29527cb209f5SScott Long 		free(event, M_AACBUF);
29538eeb2ca6SScott Long 		wakeup(arg);
29547cb209f5SScott Long 		break;
29557cb209f5SScott Long 	default:
29567cb209f5SScott Long 		break;
29577cb209f5SScott Long 	}
29587cb209f5SScott Long }
29597cb209f5SScott Long 
2960914da7d0SScott Long /*
296135863739SMike Smith  * Send a FIB supplied from userspace
296235863739SMike Smith  */
296335863739SMike Smith static int
296435863739SMike Smith aac_ioctl_sendfib(struct aac_softc *sc, caddr_t ufib)
296535863739SMike Smith {
296635863739SMike Smith 	struct aac_command *cm;
296735863739SMike Smith 	int size, error;
296835863739SMike Smith 
296931a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
297035863739SMike Smith 
297135863739SMike Smith 	cm = NULL;
297235863739SMike Smith 
297335863739SMike Smith 	/*
297435863739SMike Smith 	 * Get a command
297535863739SMike Smith 	 */
2976bb6fe253SScott Long 	mtx_lock(&sc->aac_io_lock);
297735863739SMike Smith 	if (aac_alloc_command(sc, &cm)) {
29787cb209f5SScott Long 		struct aac_event *event;
29797cb209f5SScott Long 
29807cb209f5SScott Long 		event = malloc(sizeof(struct aac_event), M_AACBUF,
29817cb209f5SScott Long 		    M_NOWAIT | M_ZERO);
29827cb209f5SScott Long 		if (event == NULL) {
298335863739SMike Smith 			error = EBUSY;
2984f16627aaSEd Maste 			mtx_unlock(&sc->aac_io_lock);
298535863739SMike Smith 			goto out;
298635863739SMike Smith 		}
29877cb209f5SScott Long 		event->ev_type = AAC_EVENT_CMFREE;
29887cb209f5SScott Long 		event->ev_callback = aac_ioctl_event;
29897cb209f5SScott Long 		event->ev_arg = &cm;
29907cb209f5SScott Long 		aac_add_event(sc, event);
29918eeb2ca6SScott Long 		msleep(&cm, &sc->aac_io_lock, 0, "sendfib", 0);
29927cb209f5SScott Long 	}
299393cfca22SScott Long 	mtx_unlock(&sc->aac_io_lock);
299435863739SMike Smith 
299535863739SMike Smith 	/*
299635863739SMike Smith 	 * Fetch the FIB header, then re-copy to get data as well.
299735863739SMike Smith 	 */
2998914da7d0SScott Long 	if ((error = copyin(ufib, cm->cm_fib,
2999914da7d0SScott Long 			    sizeof(struct aac_fib_header))) != 0)
300035863739SMike Smith 		goto out;
300135863739SMike Smith 	size = cm->cm_fib->Header.Size + sizeof(struct aac_fib_header);
3002f355c0e0SEd Maste 	if (size > sc->aac_max_fib_size) {
3003f355c0e0SEd Maste 		device_printf(sc->aac_dev, "incoming FIB oversized (%d > %d)\n",
3004f355c0e0SEd Maste 			      size, sc->aac_max_fib_size);
3005f355c0e0SEd Maste 		size = sc->aac_max_fib_size;
300635863739SMike Smith 	}
300735863739SMike Smith 	if ((error = copyin(ufib, cm->cm_fib, size)) != 0)
300835863739SMike Smith 		goto out;
300935863739SMike Smith 	cm->cm_fib->Header.Size = size;
30102b3b0f17SScott Long 	cm->cm_timestamp = time_uptime;
301135863739SMike Smith 
301235863739SMike Smith 	/*
301335863739SMike Smith 	 * Pass the FIB to the controller, wait for it to complete.
301435863739SMike Smith 	 */
301593cfca22SScott Long 	mtx_lock(&sc->aac_io_lock);
3016f16627aaSEd Maste 	error = aac_wait_command(cm);
3017f16627aaSEd Maste 	mtx_unlock(&sc->aac_io_lock);
3018f16627aaSEd Maste 	if (error != 0) {
301970545d1aSScott Long 		device_printf(sc->aac_dev,
302070545d1aSScott Long 			      "aac_wait_command return %d\n", error);
302135863739SMike Smith 		goto out;
3022b3457b51SScott Long 	}
302335863739SMike Smith 
302435863739SMike Smith 	/*
302535863739SMike Smith 	 * Copy the FIB and data back out to the caller.
302635863739SMike Smith 	 */
302735863739SMike Smith 	size = cm->cm_fib->Header.Size;
3028f355c0e0SEd Maste 	if (size > sc->aac_max_fib_size) {
3029f355c0e0SEd Maste 		device_printf(sc->aac_dev, "outbound FIB oversized (%d > %d)\n",
3030f355c0e0SEd Maste 			      size, sc->aac_max_fib_size);
3031f355c0e0SEd Maste 		size = sc->aac_max_fib_size;
303235863739SMike Smith 	}
303335863739SMike Smith 	error = copyout(cm->cm_fib, ufib, size);
303435863739SMike Smith 
303535863739SMike Smith out:
3036f6c4dd3fSScott Long 	if (cm != NULL) {
3037f16627aaSEd Maste 		mtx_lock(&sc->aac_io_lock);
303835863739SMike Smith 		aac_release_command(cm);
3039bb6fe253SScott Long 		mtx_unlock(&sc->aac_io_lock);
3040f16627aaSEd Maste 	}
304135863739SMike Smith 	return(error);
304235863739SMike Smith }
304335863739SMike Smith 
3044914da7d0SScott Long /*
3045f355c0e0SEd Maste  * Send a passthrough FIB supplied from userspace
3046f355c0e0SEd Maste  */
3047f355c0e0SEd Maste static int
3048f355c0e0SEd Maste aac_ioctl_send_raw_srb(struct aac_softc *sc, caddr_t arg)
3049f355c0e0SEd Maste {
30507b90e5ecSAttilio Rao 	struct aac_command *cm;
30517b90e5ecSAttilio Rao 	struct aac_event *event;
30527b90e5ecSAttilio Rao 	struct aac_fib *fib;
30537b90e5ecSAttilio Rao 	struct aac_srb *srbcmd, *user_srb;
30547b90e5ecSAttilio Rao 	struct aac_sg_entry *sge;
30557b90e5ecSAttilio Rao 	struct aac_sg_entry64 *sge64;
30567b90e5ecSAttilio Rao 	void *srb_sg_address, *ureply;
30577b90e5ecSAttilio Rao 	uint32_t fibsize, srb_sg_bytecount;
30587b90e5ecSAttilio Rao 	int error, transfer_data;
30597b90e5ecSAttilio Rao 
30607b90e5ecSAttilio Rao 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
30617b90e5ecSAttilio Rao 
30627b90e5ecSAttilio Rao 	cm = NULL;
30637b90e5ecSAttilio Rao 	transfer_data = 0;
30647b90e5ecSAttilio Rao 	fibsize = 0;
30657b90e5ecSAttilio Rao 	user_srb = (struct aac_srb *)arg;
30667b90e5ecSAttilio Rao 
30677b90e5ecSAttilio Rao 	mtx_lock(&sc->aac_io_lock);
30687b90e5ecSAttilio Rao 	if (aac_alloc_command(sc, &cm)) {
30697b90e5ecSAttilio Rao 		 event = malloc(sizeof(struct aac_event), M_AACBUF,
30707b90e5ecSAttilio Rao 		    M_NOWAIT | M_ZERO);
30717b90e5ecSAttilio Rao 		if (event == NULL) {
30727b90e5ecSAttilio Rao 			error = EBUSY;
30737b90e5ecSAttilio Rao 			mtx_unlock(&sc->aac_io_lock);
30747b90e5ecSAttilio Rao 			goto out;
30757b90e5ecSAttilio Rao 		}
30767b90e5ecSAttilio Rao 		event->ev_type = AAC_EVENT_CMFREE;
30777b90e5ecSAttilio Rao 		event->ev_callback = aac_ioctl_event;
30787b90e5ecSAttilio Rao 		event->ev_arg = &cm;
30797b90e5ecSAttilio Rao 		aac_add_event(sc, event);
30807b90e5ecSAttilio Rao 		msleep(cm, &sc->aac_io_lock, 0, "aacraw", 0);
30817b90e5ecSAttilio Rao 	}
30827b90e5ecSAttilio Rao 	mtx_unlock(&sc->aac_io_lock);
30837b90e5ecSAttilio Rao 
30847b90e5ecSAttilio Rao 	cm->cm_data = NULL;
30857b90e5ecSAttilio Rao 	fib = cm->cm_fib;
30867b90e5ecSAttilio Rao 	srbcmd = (struct aac_srb *)fib->data;
30877b90e5ecSAttilio Rao 	error = copyin(&user_srb->data_len, &fibsize, sizeof(uint32_t));
30887b90e5ecSAttilio Rao 	if (error != 0)
30897b90e5ecSAttilio Rao 		goto out;
30907b90e5ecSAttilio Rao 	if (fibsize > (sc->aac_max_fib_size - sizeof(struct aac_fib_header))) {
30917b90e5ecSAttilio Rao 		error = EINVAL;
30927b90e5ecSAttilio Rao 		goto out;
30937b90e5ecSAttilio Rao 	}
30947b90e5ecSAttilio Rao 	error = copyin(user_srb, srbcmd, fibsize);
30957b90e5ecSAttilio Rao 	if (error != 0)
30967b90e5ecSAttilio Rao 		goto out;
30977b90e5ecSAttilio Rao 	srbcmd->function = 0;
30987b90e5ecSAttilio Rao 	srbcmd->retry_limit = 0;
30997b90e5ecSAttilio Rao 	if (srbcmd->sg_map.SgCount > 1) {
31007b90e5ecSAttilio Rao 		error = EINVAL;
31017b90e5ecSAttilio Rao 		goto out;
31027b90e5ecSAttilio Rao 	}
31037b90e5ecSAttilio Rao 
31047b90e5ecSAttilio Rao 	/* Retrieve correct SG entries. */
31057b90e5ecSAttilio Rao 	if (fibsize == (sizeof(struct aac_srb) +
31067b90e5ecSAttilio Rao 	    srbcmd->sg_map.SgCount * sizeof(struct aac_sg_entry))) {
3107f4a18258SSean Bruno 		struct aac_sg_entry sg;
3108f4a18258SSean Bruno 
31097b90e5ecSAttilio Rao 		sge = srbcmd->sg_map.SgEntry;
31107b90e5ecSAttilio Rao 		sge64 = NULL;
3111f4a18258SSean Bruno 
3112f4a18258SSean Bruno 		if ((error = copyin(sge, &sg, sizeof(sg))) != 0)
3113f4a18258SSean Bruno 			goto out;
3114f4a18258SSean Bruno 
3115f4a18258SSean Bruno 		srb_sg_bytecount = sg.SgByteCount;
3116f4a18258SSean Bruno 		srb_sg_address = (void *)(uintptr_t)sg.SgAddress;
31177b90e5ecSAttilio Rao 	}
31187b90e5ecSAttilio Rao #ifdef __amd64__
31197b90e5ecSAttilio Rao 	else if (fibsize == (sizeof(struct aac_srb) +
31207b90e5ecSAttilio Rao 	    srbcmd->sg_map.SgCount * sizeof(struct aac_sg_entry64))) {
3121f4a18258SSean Bruno 		struct aac_sg_entry64 sg;
3122f4a18258SSean Bruno 
31237b90e5ecSAttilio Rao 		sge = NULL;
31247b90e5ecSAttilio Rao 		sge64 = (struct aac_sg_entry64 *)srbcmd->sg_map.SgEntry;
3125f4a18258SSean Bruno 
3126f4a18258SSean Bruno 		if ((error = copyin(sge64, &sg, sizeof(sg))) != 0)
3127f4a18258SSean Bruno 			goto out;
3128f4a18258SSean Bruno 
3129f4a18258SSean Bruno 		srb_sg_bytecount = sg.SgByteCount;
3130f4a18258SSean Bruno 		srb_sg_address = (void *)sg.SgAddress;
31317b90e5ecSAttilio Rao 		if (sge64->SgAddress > 0xffffffffull &&
31327b90e5ecSAttilio Rao 		    (sc->flags & AAC_FLAGS_SG_64BIT) == 0) {
31337b90e5ecSAttilio Rao 			error = EINVAL;
31347b90e5ecSAttilio Rao 			goto out;
31357b90e5ecSAttilio Rao 		}
31367b90e5ecSAttilio Rao 	}
31377b90e5ecSAttilio Rao #endif
31387b90e5ecSAttilio Rao 	else {
31397b90e5ecSAttilio Rao 		error = EINVAL;
31407b90e5ecSAttilio Rao 		goto out;
31417b90e5ecSAttilio Rao 	}
31427b90e5ecSAttilio Rao 	ureply = (char *)arg + fibsize;
31437b90e5ecSAttilio Rao 	srbcmd->data_len = srb_sg_bytecount;
31447b90e5ecSAttilio Rao 	if (srbcmd->sg_map.SgCount == 1)
31457b90e5ecSAttilio Rao 		transfer_data = 1;
31467b90e5ecSAttilio Rao 
31477b90e5ecSAttilio Rao 	cm->cm_sgtable = (struct aac_sg_table *)&srbcmd->sg_map;
31487b90e5ecSAttilio Rao 	if (transfer_data) {
31497b90e5ecSAttilio Rao 		cm->cm_datalen = srb_sg_bytecount;
31507b90e5ecSAttilio Rao 		cm->cm_data = malloc(cm->cm_datalen, M_AACBUF, M_NOWAIT);
31517b90e5ecSAttilio Rao 		if (cm->cm_data == NULL) {
31527b90e5ecSAttilio Rao 			error = ENOMEM;
31537b90e5ecSAttilio Rao 			goto out;
31547b90e5ecSAttilio Rao 		}
31557b90e5ecSAttilio Rao 		if (srbcmd->flags & AAC_SRB_FLAGS_DATA_IN)
31567b90e5ecSAttilio Rao 			cm->cm_flags |= AAC_CMD_DATAIN;
31577b90e5ecSAttilio Rao 		if (srbcmd->flags & AAC_SRB_FLAGS_DATA_OUT) {
31587b90e5ecSAttilio Rao 			cm->cm_flags |= AAC_CMD_DATAOUT;
31597b90e5ecSAttilio Rao 			error = copyin(srb_sg_address, cm->cm_data,
31607b90e5ecSAttilio Rao 			    cm->cm_datalen);
31617b90e5ecSAttilio Rao 			if (error != 0)
31627b90e5ecSAttilio Rao 				goto out;
31637b90e5ecSAttilio Rao 		}
31647b90e5ecSAttilio Rao 	}
31657b90e5ecSAttilio Rao 
31667b90e5ecSAttilio Rao 	fib->Header.Size = sizeof(struct aac_fib_header) +
31677b90e5ecSAttilio Rao 	    sizeof(struct aac_srb);
31687b90e5ecSAttilio Rao 	fib->Header.XferState =
31697b90e5ecSAttilio Rao 	    AAC_FIBSTATE_HOSTOWNED   |
31707b90e5ecSAttilio Rao 	    AAC_FIBSTATE_INITIALISED |
31717b90e5ecSAttilio Rao 	    AAC_FIBSTATE_EMPTY       |
31727b90e5ecSAttilio Rao 	    AAC_FIBSTATE_FROMHOST    |
31737b90e5ecSAttilio Rao 	    AAC_FIBSTATE_REXPECTED   |
31747b90e5ecSAttilio Rao 	    AAC_FIBSTATE_NORM        |
31757b90e5ecSAttilio Rao 	    AAC_FIBSTATE_ASYNC       |
31767b90e5ecSAttilio Rao 	    AAC_FIBSTATE_FAST_RESPONSE;
31777b90e5ecSAttilio Rao 	fib->Header.Command = (sc->flags & AAC_FLAGS_SG_64BIT) != 0 ?
31787b90e5ecSAttilio Rao 	    ScsiPortCommandU64 : ScsiPortCommand;
31797b90e5ecSAttilio Rao 
31807b90e5ecSAttilio Rao 	mtx_lock(&sc->aac_io_lock);
31817b90e5ecSAttilio Rao 	aac_wait_command(cm);
31827b90e5ecSAttilio Rao 	mtx_unlock(&sc->aac_io_lock);
31837b90e5ecSAttilio Rao 
31847b90e5ecSAttilio Rao 	if (transfer_data && (srbcmd->flags & AAC_SRB_FLAGS_DATA_IN) != 0) {
31857b90e5ecSAttilio Rao 		error = copyout(cm->cm_data, srb_sg_address, cm->cm_datalen);
31867b90e5ecSAttilio Rao 		if (error != 0)
31877b90e5ecSAttilio Rao 			goto out;
31887b90e5ecSAttilio Rao 	}
31897b90e5ecSAttilio Rao 	error = copyout(fib->data, ureply, sizeof(struct aac_srb_response));
31907b90e5ecSAttilio Rao out:
31917b90e5ecSAttilio Rao 	if (cm != NULL) {
31927b90e5ecSAttilio Rao 		if (cm->cm_data != NULL)
31937b90e5ecSAttilio Rao 			free(cm->cm_data, M_AACBUF);
31947b90e5ecSAttilio Rao 		mtx_lock(&sc->aac_io_lock);
31957b90e5ecSAttilio Rao 		aac_release_command(cm);
31967b90e5ecSAttilio Rao 		mtx_unlock(&sc->aac_io_lock);
31977b90e5ecSAttilio Rao 	}
31987b90e5ecSAttilio Rao 	return(error);
3199f355c0e0SEd Maste }
3200f355c0e0SEd Maste 
3201f355c0e0SEd Maste /*
3202dfe2c294SAttilio Rao  * cdevpriv interface private destructor.
3203dfe2c294SAttilio Rao  */
3204dfe2c294SAttilio Rao static void
3205dfe2c294SAttilio Rao aac_cdevpriv_dtor(void *arg)
3206dfe2c294SAttilio Rao {
3207dfe2c294SAttilio Rao 	struct aac_softc *sc;
3208dfe2c294SAttilio Rao 
3209dfe2c294SAttilio Rao 	sc = arg;
3210dfe2c294SAttilio Rao 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
3211dfe2c294SAttilio Rao 	mtx_lock(&Giant);
3212dfe2c294SAttilio Rao 	device_unbusy(sc->aac_dev);
3213dfe2c294SAttilio Rao 	mtx_unlock(&Giant);
3214dfe2c294SAttilio Rao }
3215dfe2c294SAttilio Rao 
3216dfe2c294SAttilio Rao /*
321735863739SMike Smith  * Handle an AIF sent to us by the controller; queue it for later reference.
321836e0bf6eSScott Long  * If the queue fills up, then drop the older entries.
321935863739SMike Smith  */
322035863739SMike Smith static void
322136e0bf6eSScott Long aac_handle_aif(struct aac_softc *sc, struct aac_fib *fib)
322235863739SMike Smith {
322336e0bf6eSScott Long 	struct aac_aif_command *aif;
322436e0bf6eSScott Long 	struct aac_container *co, *co_next;
3225a723a548SEd Maste 	struct aac_fib_context *ctx;
322604f4d586SEd Maste 	struct aac_mntinforesp *mir;
3227a723a548SEd Maste 	int next, current, found;
3228795d7dc0SScott Long 	int count = 0, added = 0, i = 0;
3229851f59d7SEd Maste 	uint32_t channel;
323035863739SMike Smith 
323131a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
323235863739SMike Smith 
323336e0bf6eSScott Long 	aif = (struct aac_aif_command*)&fib->data[0];
323436e0bf6eSScott Long 	aac_print_aif(sc, aif);
323536e0bf6eSScott Long 
323636e0bf6eSScott Long 	/* Is it an event that we should care about? */
323736e0bf6eSScott Long 	switch (aif->command) {
323836e0bf6eSScott Long 	case AifCmdEventNotify:
323936e0bf6eSScott Long 		switch (aif->data.EN.type) {
324036e0bf6eSScott Long 		case AifEnAddContainer:
324136e0bf6eSScott Long 		case AifEnDeleteContainer:
324236e0bf6eSScott Long 			/*
3243914da7d0SScott Long 			 * A container was added or deleted, but the message
3244914da7d0SScott Long 			 * doesn't tell us anything else!  Re-enumerate the
3245914da7d0SScott Long 			 * containers and sort things out.
324636e0bf6eSScott Long 			 */
324703b5fe51SScott Long 			aac_alloc_sync_fib(sc, &fib);
324836e0bf6eSScott Long 			do {
324936e0bf6eSScott Long 				/*
3250914da7d0SScott Long 				 * Ask the controller for its containers one at
3251914da7d0SScott Long 				 * a time.
3252914da7d0SScott Long 				 * XXX What if the controller's list changes
3253914da7d0SScott Long 				 * midway through this enumaration?
325436e0bf6eSScott Long 				 * XXX This should be done async.
325536e0bf6eSScott Long 				 */
325604f4d586SEd Maste 				if ((mir = aac_get_container_info(sc, fib, i)) == NULL)
325736e0bf6eSScott Long 					continue;
325804f4d586SEd Maste 				if (i == 0)
3259795d7dc0SScott Long 					count = mir->MntRespCount;
326036e0bf6eSScott Long 				/*
3261914da7d0SScott Long 				 * Check the container against our list.
3262914da7d0SScott Long 				 * co->co_found was already set to 0 in a
3263914da7d0SScott Long 				 * previous run.
326436e0bf6eSScott Long 				 */
3265cbfd045bSScott Long 				if ((mir->Status == ST_OK) &&
3266cbfd045bSScott Long 				    (mir->MntTable[0].VolType != CT_NONE)) {
326736e0bf6eSScott Long 					found = 0;
3268914da7d0SScott Long 					TAILQ_FOREACH(co,
3269914da7d0SScott Long 						      &sc->aac_container_tqh,
3270914da7d0SScott Long 						      co_link) {
327136e0bf6eSScott Long 						if (co->co_mntobj.ObjectId ==
3272cbfd045bSScott Long 						    mir->MntTable[0].ObjectId) {
327336e0bf6eSScott Long 							co->co_found = 1;
327436e0bf6eSScott Long 							found = 1;
327536e0bf6eSScott Long 							break;
327636e0bf6eSScott Long 						}
327736e0bf6eSScott Long 					}
3278914da7d0SScott Long 					/*
3279914da7d0SScott Long 					 * If the container matched, continue
3280914da7d0SScott Long 					 * in the list.
3281914da7d0SScott Long 					 */
328236e0bf6eSScott Long 					if (found) {
328336e0bf6eSScott Long 						i++;
328436e0bf6eSScott Long 						continue;
328536e0bf6eSScott Long 					}
328636e0bf6eSScott Long 
328736e0bf6eSScott Long 					/*
3288914da7d0SScott Long 					 * This is a new container.  Do all the
328970545d1aSScott Long 					 * appropriate things to set it up.
329070545d1aSScott Long 					 */
3291cbfd045bSScott Long 					aac_add_container(sc, mir, 1);
329236e0bf6eSScott Long 					added = 1;
329336e0bf6eSScott Long 				}
329436e0bf6eSScott Long 				i++;
3295795d7dc0SScott Long 			} while ((i < count) && (i < AAC_MAX_CONTAINERS));
3296cbfd045bSScott Long 			aac_release_sync_fib(sc);
329736e0bf6eSScott Long 
329836e0bf6eSScott Long 			/*
3299914da7d0SScott Long 			 * Go through our list of containers and see which ones
3300914da7d0SScott Long 			 * were not marked 'found'.  Since the controller didn't
3301914da7d0SScott Long 			 * list them they must have been deleted.  Do the
3302914da7d0SScott Long 			 * appropriate steps to destroy the device.  Also reset
3303914da7d0SScott Long 			 * the co->co_found field.
330436e0bf6eSScott Long 			 */
330536e0bf6eSScott Long 			co = TAILQ_FIRST(&sc->aac_container_tqh);
330636e0bf6eSScott Long 			while (co != NULL) {
330736e0bf6eSScott Long 				if (co->co_found == 0) {
33087cb209f5SScott Long 					mtx_unlock(&sc->aac_io_lock);
3309a56fe095SJohn Baldwin 					mtx_lock(&Giant);
3310914da7d0SScott Long 					device_delete_child(sc->aac_dev,
3311914da7d0SScott Long 							    co->co_disk);
3312a56fe095SJohn Baldwin 					mtx_unlock(&Giant);
33137cb209f5SScott Long 					mtx_lock(&sc->aac_io_lock);
331436e0bf6eSScott Long 					co_next = TAILQ_NEXT(co, co_link);
3315bb6fe253SScott Long 					mtx_lock(&sc->aac_container_lock);
3316914da7d0SScott Long 					TAILQ_REMOVE(&sc->aac_container_tqh, co,
3317914da7d0SScott Long 						     co_link);
3318bb6fe253SScott Long 					mtx_unlock(&sc->aac_container_lock);
3319ba1d57e7SScott Long 					free(co, M_AACBUF);
332036e0bf6eSScott Long 					co = co_next;
332136e0bf6eSScott Long 				} else {
332236e0bf6eSScott Long 					co->co_found = 0;
332336e0bf6eSScott Long 					co = TAILQ_NEXT(co, co_link);
332436e0bf6eSScott Long 				}
332536e0bf6eSScott Long 			}
332636e0bf6eSScott Long 
332736e0bf6eSScott Long 			/* Attach the newly created containers */
33287cb209f5SScott Long 			if (added) {
33297cb209f5SScott Long 				mtx_unlock(&sc->aac_io_lock);
3330a56fe095SJohn Baldwin 				mtx_lock(&Giant);
333136e0bf6eSScott Long 				bus_generic_attach(sc->aac_dev);
3332a56fe095SJohn Baldwin 				mtx_unlock(&Giant);
33337cb209f5SScott Long 				mtx_lock(&sc->aac_io_lock);
33347cb209f5SScott Long 			}
333536e0bf6eSScott Long 
333636e0bf6eSScott Long 			break;
333736e0bf6eSScott Long 
3338851f59d7SEd Maste 		case AifEnEnclosureManagement:
3339851f59d7SEd Maste 			switch (aif->data.EN.data.EEE.eventType) {
3340851f59d7SEd Maste 			case AIF_EM_DRIVE_INSERTION:
3341851f59d7SEd Maste 			case AIF_EM_DRIVE_REMOVAL:
3342851f59d7SEd Maste 				channel = aif->data.EN.data.EEE.unitID;
3343851f59d7SEd Maste 				if (sc->cam_rescan_cb != NULL)
3344851f59d7SEd Maste 					sc->cam_rescan_cb(sc,
3345851f59d7SEd Maste 					    (channel >> 24) & 0xF,
3346851f59d7SEd Maste 					    (channel & 0xFFFF));
3347851f59d7SEd Maste 				break;
3348851f59d7SEd Maste 			}
3349851f59d7SEd Maste 			break;
3350851f59d7SEd Maste 
3351851f59d7SEd Maste 		case AifEnAddJBOD:
3352851f59d7SEd Maste 		case AifEnDeleteJBOD:
3353851f59d7SEd Maste 			channel = aif->data.EN.data.ECE.container;
3354851f59d7SEd Maste 			if (sc->cam_rescan_cb != NULL)
3355851f59d7SEd Maste 				sc->cam_rescan_cb(sc, (channel >> 24) & 0xF,
3356851f59d7SEd Maste 				    AAC_CAM_TARGET_WILDCARD);
3357851f59d7SEd Maste 			break;
3358851f59d7SEd Maste 
335936e0bf6eSScott Long 		default:
336036e0bf6eSScott Long 			break;
336136e0bf6eSScott Long 		}
336236e0bf6eSScott Long 
336336e0bf6eSScott Long 	default:
336436e0bf6eSScott Long 		break;
336536e0bf6eSScott Long 	}
336636e0bf6eSScott Long 
336736e0bf6eSScott Long 	/* Copy the AIF data to the AIF queue for ioctl retrieval */
3368bb6fe253SScott Long 	mtx_lock(&sc->aac_aifq_lock);
3369a723a548SEd Maste 	current = sc->aifq_idx;
3370a723a548SEd Maste 	next = (current + 1) % AAC_AIFQ_LENGTH;
3371a723a548SEd Maste 	if (next == 0)
3372a723a548SEd Maste 		sc->aifq_filled = 1;
3373a723a548SEd Maste 	bcopy(fib, &sc->aac_aifq[current], sizeof(struct aac_fib));
3374a723a548SEd Maste 	/* modify AIF contexts */
3375a723a548SEd Maste 	if (sc->aifq_filled) {
3376a723a548SEd Maste 		for (ctx = sc->fibctx; ctx; ctx = ctx->next) {
3377a723a548SEd Maste 			if (next == ctx->ctx_idx)
3378a723a548SEd Maste 				ctx->ctx_wrap = 1;
3379a723a548SEd Maste 			else if (current == ctx->ctx_idx && ctx->ctx_wrap)
3380a723a548SEd Maste 				ctx->ctx_idx = next;
3381a723a548SEd Maste 		}
3382a723a548SEd Maste 	}
3383a723a548SEd Maste 	sc->aifq_idx = next;
3384b3457b51SScott Long 	/* On the off chance that someone is sleeping for an aif... */
338535863739SMike Smith 	if (sc->aac_state & AAC_STATE_AIF_SLEEPER)
338635863739SMike Smith 		wakeup(sc->aac_aifq);
3387b3457b51SScott Long 	/* Wakeup any poll()ers */
3388512824f8SSeigo Tanimura 	selwakeuppri(&sc->rcv_select, PRIBIO);
3389bb6fe253SScott Long 	mtx_unlock(&sc->aac_aifq_lock);
339035863739SMike Smith }
339135863739SMike Smith 
3392914da7d0SScott Long /*
33930b94a66eSMike Smith  * Return the Revision of the driver to userspace and check to see if the
339436e0bf6eSScott Long  * userspace app is possibly compatible.  This is extremely bogus since
339536e0bf6eSScott Long  * our driver doesn't follow Adaptec's versioning system.  Cheat by just
339636e0bf6eSScott Long  * returning what the card reported.
339735863739SMike Smith  */
339835863739SMike Smith static int
3399fb0c27d7SScott Long aac_rev_check(struct aac_softc *sc, caddr_t udata)
340035863739SMike Smith {
340135863739SMike Smith 	struct aac_rev_check rev_check;
340235863739SMike Smith 	struct aac_rev_check_resp rev_check_resp;
340335863739SMike Smith 	int error = 0;
340435863739SMike Smith 
340531a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
340635863739SMike Smith 
340735863739SMike Smith 	/*
340835863739SMike Smith 	 * Copyin the revision struct from userspace
340935863739SMike Smith 	 */
3410c6eafcf2SScott Long 	if ((error = copyin(udata, (caddr_t)&rev_check,
3411c6eafcf2SScott Long 			sizeof(struct aac_rev_check))) != 0) {
341235863739SMike Smith 		return error;
341335863739SMike Smith 	}
341435863739SMike Smith 
341531a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_IOCTL_COMMANDS_B, "Userland revision= %d\n",
3416914da7d0SScott Long 	      rev_check.callingRevision.buildNumber);
341735863739SMike Smith 
341835863739SMike Smith 	/*
341935863739SMike Smith 	 * Doctor up the response struct.
342035863739SMike Smith 	 */
342135863739SMike Smith 	rev_check_resp.possiblyCompatible = 1;
34228e7e6335SEd Maste 	rev_check_resp.adapterSWRevision.external.comp.major =
34238e7e6335SEd Maste 	    AAC_DRIVER_MAJOR_VERSION;
34248e7e6335SEd Maste 	rev_check_resp.adapterSWRevision.external.comp.minor =
34258e7e6335SEd Maste 	    AAC_DRIVER_MINOR_VERSION;
34268e7e6335SEd Maste 	rev_check_resp.adapterSWRevision.external.comp.type =
34278e7e6335SEd Maste 	    AAC_DRIVER_TYPE;
34288e7e6335SEd Maste 	rev_check_resp.adapterSWRevision.external.comp.dash =
34298e7e6335SEd Maste 	    AAC_DRIVER_BUGFIX_LEVEL;
3430914da7d0SScott Long 	rev_check_resp.adapterSWRevision.buildNumber =
34318e7e6335SEd Maste 	    AAC_DRIVER_BUILD;
343235863739SMike Smith 
3433c6eafcf2SScott Long 	return(copyout((caddr_t)&rev_check_resp, udata,
3434c6eafcf2SScott Long 			sizeof(struct aac_rev_check_resp)));
343535863739SMike Smith }
343635863739SMike Smith 
3437914da7d0SScott Long /*
3438a723a548SEd Maste  * Pass the fib context to the caller
3439a723a548SEd Maste  */
3440a723a548SEd Maste static int
3441a723a548SEd Maste aac_open_aif(struct aac_softc *sc, caddr_t arg)
3442a723a548SEd Maste {
3443a723a548SEd Maste 	struct aac_fib_context *fibctx, *ctx;
3444a723a548SEd Maste 	int error = 0;
3445a723a548SEd Maste 
344631a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
3447a723a548SEd Maste 
3448a723a548SEd Maste 	fibctx = malloc(sizeof(struct aac_fib_context), M_AACBUF, M_NOWAIT|M_ZERO);
3449a723a548SEd Maste 	if (fibctx == NULL)
3450a723a548SEd Maste 		return (ENOMEM);
3451a723a548SEd Maste 
3452a723a548SEd Maste 	mtx_lock(&sc->aac_aifq_lock);
3453a723a548SEd Maste 	/* all elements are already 0, add to queue */
3454a723a548SEd Maste 	if (sc->fibctx == NULL)
3455a723a548SEd Maste 		sc->fibctx = fibctx;
3456a723a548SEd Maste 	else {
3457a723a548SEd Maste 		for (ctx = sc->fibctx; ctx->next; ctx = ctx->next)
3458a723a548SEd Maste 			;
3459a723a548SEd Maste 		ctx->next = fibctx;
3460a723a548SEd Maste 		fibctx->prev = ctx;
3461a723a548SEd Maste 	}
3462a723a548SEd Maste 
3463a723a548SEd Maste 	/* evaluate unique value */
3464a723a548SEd Maste 	fibctx->unique = (*(u_int32_t *)&fibctx & 0xffffffff);
3465a723a548SEd Maste 	ctx = sc->fibctx;
3466a723a548SEd Maste 	while (ctx != fibctx) {
3467a723a548SEd Maste 		if (ctx->unique == fibctx->unique) {
3468a723a548SEd Maste 			fibctx->unique++;
3469a723a548SEd Maste 			ctx = sc->fibctx;
3470a723a548SEd Maste 		} else {
3471a723a548SEd Maste 			ctx = ctx->next;
3472a723a548SEd Maste 		}
3473a723a548SEd Maste 	}
3474a723a548SEd Maste 	mtx_unlock(&sc->aac_aifq_lock);
3475a723a548SEd Maste 
3476a723a548SEd Maste 	error = copyout(&fibctx->unique, (void *)arg, sizeof(u_int32_t));
3477a723a548SEd Maste 	if (error)
3478a723a548SEd Maste 		aac_close_aif(sc, (caddr_t)ctx);
3479a723a548SEd Maste 	return error;
3480a723a548SEd Maste }
3481a723a548SEd Maste 
3482a723a548SEd Maste /*
3483a723a548SEd Maste  * Close the caller's fib context
3484a723a548SEd Maste  */
3485a723a548SEd Maste static int
3486a723a548SEd Maste aac_close_aif(struct aac_softc *sc, caddr_t arg)
3487a723a548SEd Maste {
3488a723a548SEd Maste 	struct aac_fib_context *ctx;
3489a723a548SEd Maste 
349031a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
3491a723a548SEd Maste 
3492a723a548SEd Maste 	mtx_lock(&sc->aac_aifq_lock);
3493a723a548SEd Maste 	for (ctx = sc->fibctx; ctx; ctx = ctx->next) {
3494a723a548SEd Maste 		if (ctx->unique == *(uint32_t *)&arg) {
3495a723a548SEd Maste 			if (ctx == sc->fibctx)
3496a723a548SEd Maste 				sc->fibctx = NULL;
3497a723a548SEd Maste 			else {
3498a723a548SEd Maste 				ctx->prev->next = ctx->next;
3499a723a548SEd Maste 				if (ctx->next)
3500a723a548SEd Maste 					ctx->next->prev = ctx->prev;
3501a723a548SEd Maste 			}
3502a723a548SEd Maste 			break;
3503a723a548SEd Maste 		}
3504a723a548SEd Maste 	}
3505a723a548SEd Maste 	mtx_unlock(&sc->aac_aifq_lock);
3506a723a548SEd Maste 	if (ctx)
3507a723a548SEd Maste 		free(ctx, M_AACBUF);
3508a723a548SEd Maste 
3509a723a548SEd Maste 	return 0;
3510a723a548SEd Maste }
3511a723a548SEd Maste 
3512a723a548SEd Maste /*
351335863739SMike Smith  * Pass the caller the next AIF in their queue
351435863739SMike Smith  */
351535863739SMike Smith static int
3516fb0c27d7SScott Long aac_getnext_aif(struct aac_softc *sc, caddr_t arg)
351735863739SMike Smith {
351835863739SMike Smith 	struct get_adapter_fib_ioctl agf;
3519a723a548SEd Maste 	struct aac_fib_context *ctx;
35209e2e96d8SScott Long 	int error;
352135863739SMike Smith 
352231a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
352335863739SMike Smith 
352435863739SMike Smith 	if ((error = copyin(arg, &agf, sizeof(agf))) == 0) {
3525a723a548SEd Maste 		for (ctx = sc->fibctx; ctx; ctx = ctx->next) {
3526a723a548SEd Maste 			if (agf.AdapterFibContext == ctx->unique)
3527a723a548SEd Maste 				break;
3528a723a548SEd Maste 		}
3529a723a548SEd Maste 		if (!ctx)
3530a723a548SEd Maste 			return (EFAULT);
353135863739SMike Smith 
3532a723a548SEd Maste 		error = aac_return_aif(sc, ctx, agf.AifFib);
3533a723a548SEd Maste 		if (error == EAGAIN && agf.Wait) {
353431a0399eSEd Maste 			fwprintf(sc, HBA_FLAGS_DBG_AIF_B, "aac_getnext_aif(): waiting for AIF");
353535863739SMike Smith 			sc->aac_state |= AAC_STATE_AIF_SLEEPER;
353635863739SMike Smith 			while (error == EAGAIN) {
3537914da7d0SScott Long 				error = tsleep(sc->aac_aifq, PRIBIO |
3538914da7d0SScott Long 					       PCATCH, "aacaif", 0);
353935863739SMike Smith 				if (error == 0)
3540a723a548SEd Maste 					error = aac_return_aif(sc, ctx, agf.AifFib);
354135863739SMike Smith 			}
354235863739SMike Smith 			sc->aac_state &= ~AAC_STATE_AIF_SLEEPER;
354335863739SMike Smith 		}
354435863739SMike Smith 	}
354535863739SMike Smith 	return(error);
354635863739SMike Smith }
354735863739SMike Smith 
3548914da7d0SScott Long /*
35490b94a66eSMike Smith  * Hand the next AIF off the top of the queue out to userspace.
35500b94a66eSMike Smith  */
35510b94a66eSMike Smith static int
3552a723a548SEd Maste aac_return_aif(struct aac_softc *sc, struct aac_fib_context *ctx, caddr_t uptr)
35530b94a66eSMike Smith {
3554a723a548SEd Maste 	int current, error;
35550b94a66eSMike Smith 
355631a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
35570b94a66eSMike Smith 
3558bb6fe253SScott Long 	mtx_lock(&sc->aac_aifq_lock);
3559a723a548SEd Maste 	current = ctx->ctx_idx;
3560a723a548SEd Maste 	if (current == sc->aifq_idx && !ctx->ctx_wrap) {
3561a723a548SEd Maste 		/* empty */
3562bb6fe253SScott Long 		mtx_unlock(&sc->aac_aifq_lock);
35633df780cfSScott Long 		return (EAGAIN);
35643df780cfSScott Long 	}
3565a723a548SEd Maste 	error =
3566a723a548SEd Maste 		copyout(&sc->aac_aifq[current], (void *)uptr, sizeof(struct aac_fib));
356736e0bf6eSScott Long 	if (error)
356870545d1aSScott Long 		device_printf(sc->aac_dev,
356970545d1aSScott Long 		    "aac_return_aif: copyout returned %d\n", error);
3570a723a548SEd Maste 	else {
3571a723a548SEd Maste 		ctx->ctx_wrap = 0;
3572a723a548SEd Maste 		ctx->ctx_idx = (current + 1) % AAC_AIFQ_LENGTH;
3573a723a548SEd Maste 	}
3574bb6fe253SScott Long 	mtx_unlock(&sc->aac_aifq_lock);
35750b94a66eSMike Smith 	return(error);
35760b94a66eSMike Smith }
357736e0bf6eSScott Long 
35787cb209f5SScott Long static int
35797cb209f5SScott Long aac_get_pci_info(struct aac_softc *sc, caddr_t uptr)
35807cb209f5SScott Long {
35817cb209f5SScott Long 	struct aac_pci_info {
35827cb209f5SScott Long 		u_int32_t bus;
35837cb209f5SScott Long 		u_int32_t slot;
35847cb209f5SScott Long 	} pciinf;
35857cb209f5SScott Long 	int error;
35867cb209f5SScott Long 
358731a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
35887cb209f5SScott Long 
35897cb209f5SScott Long 	pciinf.bus = pci_get_bus(sc->aac_dev);
35907cb209f5SScott Long 	pciinf.slot = pci_get_slot(sc->aac_dev);
35917cb209f5SScott Long 
35927cb209f5SScott Long 	error = copyout((caddr_t)&pciinf, uptr,
35937cb209f5SScott Long 			sizeof(struct aac_pci_info));
35947cb209f5SScott Long 
35957cb209f5SScott Long 	return (error);
35967cb209f5SScott Long }
35977cb209f5SScott Long 
35986d307336SEd Maste static int
35996d307336SEd Maste aac_supported_features(struct aac_softc *sc, caddr_t uptr)
36006d307336SEd Maste {
36016d307336SEd Maste 	struct aac_features f;
36026d307336SEd Maste 	int error;
36036d307336SEd Maste 
36046d307336SEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
36056d307336SEd Maste 
36066d307336SEd Maste 	if ((error = copyin(uptr, &f, sizeof (f))) != 0)
36076d307336SEd Maste 		return (error);
36086d307336SEd Maste 
36096d307336SEd Maste 	/*
36106d307336SEd Maste 	 * When the management driver receives FSACTL_GET_FEATURES ioctl with
36116d307336SEd Maste 	 * ALL zero in the featuresState, the driver will return the current
36126d307336SEd Maste 	 * state of all the supported features, the data field will not be
36136d307336SEd Maste 	 * valid.
36146d307336SEd Maste 	 * When the management driver receives FSACTL_GET_FEATURES ioctl with
36156d307336SEd Maste 	 * a specific bit set in the featuresState, the driver will return the
36166d307336SEd Maste 	 * current state of this specific feature and whatever data that are
36176d307336SEd Maste 	 * associated with the feature in the data field or perform whatever
36186d307336SEd Maste 	 * action needed indicates in the data field.
36196d307336SEd Maste 	 */
36206d307336SEd Maste 	if (f.feat.fValue == 0) {
36216d307336SEd Maste 		f.feat.fBits.largeLBA =
36226d307336SEd Maste 		    (sc->flags & AAC_FLAGS_LBA_64BIT) ? 1 : 0;
36236d307336SEd Maste 		/* TODO: In the future, add other features state here as well */
36246d307336SEd Maste 	} else {
36256d307336SEd Maste 		if (f.feat.fBits.largeLBA)
36266d307336SEd Maste 			f.feat.fBits.largeLBA =
36276d307336SEd Maste 			    (sc->flags & AAC_FLAGS_LBA_64BIT) ? 1 : 0;
36286d307336SEd Maste 		/* TODO: Add other features state and data in the future */
36296d307336SEd Maste 	}
36306d307336SEd Maste 
36316d307336SEd Maste 	error = copyout(&f, uptr, sizeof (f));
36326d307336SEd Maste 	return (error);
36336d307336SEd Maste }
36346d307336SEd Maste 
3635914da7d0SScott Long /*
363636e0bf6eSScott Long  * Give the userland some information about the container.  The AAC arch
363736e0bf6eSScott Long  * expects the driver to be a SCSI passthrough type driver, so it expects
363836e0bf6eSScott Long  * the containers to have b:t:l numbers.  Fake it.
363936e0bf6eSScott Long  */
364036e0bf6eSScott Long static int
364136e0bf6eSScott Long aac_query_disk(struct aac_softc *sc, caddr_t uptr)
364236e0bf6eSScott Long {
364336e0bf6eSScott Long 	struct aac_query_disk query_disk;
364436e0bf6eSScott Long 	struct aac_container *co;
3645914da7d0SScott Long 	struct aac_disk	*disk;
364636e0bf6eSScott Long 	int error, id;
364736e0bf6eSScott Long 
364831a0399eSEd Maste 	fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
364936e0bf6eSScott Long 
3650914da7d0SScott Long 	disk = NULL;
3651914da7d0SScott Long 
3652914da7d0SScott Long 	error = copyin(uptr, (caddr_t)&query_disk,
3653914da7d0SScott Long 		       sizeof(struct aac_query_disk));
365436e0bf6eSScott Long 	if (error)
365536e0bf6eSScott Long 		return (error);
365636e0bf6eSScott Long 
365736e0bf6eSScott Long 	id = query_disk.ContainerNumber;
365836e0bf6eSScott Long 	if (id == -1)
365936e0bf6eSScott Long 		return (EINVAL);
366036e0bf6eSScott Long 
3661bb6fe253SScott Long 	mtx_lock(&sc->aac_container_lock);
366236e0bf6eSScott Long 	TAILQ_FOREACH(co, &sc->aac_container_tqh, co_link) {
366336e0bf6eSScott Long 		if (co->co_mntobj.ObjectId == id)
366436e0bf6eSScott Long 			break;
366536e0bf6eSScott Long 		}
366636e0bf6eSScott Long 
366736e0bf6eSScott Long 	if (co == NULL) {
366836e0bf6eSScott Long 			query_disk.Valid = 0;
366936e0bf6eSScott Long 			query_disk.Locked = 0;
367036e0bf6eSScott Long 			query_disk.Deleted = 1;		/* XXX is this right? */
367136e0bf6eSScott Long 	} else {
367236e0bf6eSScott Long 		disk = device_get_softc(co->co_disk);
367336e0bf6eSScott Long 		query_disk.Valid = 1;
3674914da7d0SScott Long 		query_disk.Locked =
3675914da7d0SScott Long 		    (disk->ad_flags & AAC_DISK_OPEN) ? 1 : 0;
367636e0bf6eSScott Long 		query_disk.Deleted = 0;
3677b3457b51SScott Long 		query_disk.Bus = device_get_unit(sc->aac_dev);
367836e0bf6eSScott Long 		query_disk.Target = disk->unit;
367936e0bf6eSScott Long 		query_disk.Lun = 0;
368036e0bf6eSScott Long 		query_disk.UnMapped = 0;
36817540e65eSScott Long 		sprintf(&query_disk.diskDeviceName[0], "%s%d",
36820b7ed341SPoul-Henning Kamp 			disk->ad_disk->d_name, disk->ad_disk->d_unit);
368336e0bf6eSScott Long 	}
3684bb6fe253SScott Long 	mtx_unlock(&sc->aac_container_lock);
368536e0bf6eSScott Long 
3686914da7d0SScott Long 	error = copyout((caddr_t)&query_disk, uptr,
3687914da7d0SScott Long 			sizeof(struct aac_query_disk));
368836e0bf6eSScott Long 
368936e0bf6eSScott Long 	return (error);
369036e0bf6eSScott Long }
369136e0bf6eSScott Long 
3692fe3cb0e1SScott Long static void
3693fe3cb0e1SScott Long aac_get_bus_info(struct aac_softc *sc)
3694fe3cb0e1SScott Long {
3695fe3cb0e1SScott Long 	struct aac_fib *fib;
3696fe3cb0e1SScott Long 	struct aac_ctcfg *c_cmd;
3697fe3cb0e1SScott Long 	struct aac_ctcfg_resp *c_resp;
3698fe3cb0e1SScott Long 	struct aac_vmioctl *vmi;
3699fe3cb0e1SScott Long 	struct aac_vmi_businf_resp *vmi_resp;
3700fe3cb0e1SScott Long 	struct aac_getbusinf businfo;
370170545d1aSScott Long 	struct aac_sim *caminf;
3702fe3cb0e1SScott Long 	device_t child;
3703fe3cb0e1SScott Long 	int i, found, error;
3704fe3cb0e1SScott Long 
37051ffe41c1SChristian S.J. Peron 	mtx_lock(&sc->aac_io_lock);
370603b5fe51SScott Long 	aac_alloc_sync_fib(sc, &fib);
3707fe3cb0e1SScott Long 	c_cmd = (struct aac_ctcfg *)&fib->data[0];
370839ee03c3SScott Long 	bzero(c_cmd, sizeof(struct aac_ctcfg));
3709fe3cb0e1SScott Long 
3710fe3cb0e1SScott Long 	c_cmd->Command = VM_ContainerConfig;
3711fe3cb0e1SScott Long 	c_cmd->cmd = CT_GET_SCSI_METHOD;
3712fe3cb0e1SScott Long 	c_cmd->param = 0;
3713fe3cb0e1SScott Long 
3714fe3cb0e1SScott Long 	error = aac_sync_fib(sc, ContainerCommand, 0, fib,
3715fe3cb0e1SScott Long 	    sizeof(struct aac_ctcfg));
3716fe3cb0e1SScott Long 	if (error) {
3717fe3cb0e1SScott Long 		device_printf(sc->aac_dev, "Error %d sending "
3718fe3cb0e1SScott Long 		    "VM_ContainerConfig command\n", error);
3719fe3cb0e1SScott Long 		aac_release_sync_fib(sc);
37201ffe41c1SChristian S.J. Peron 		mtx_unlock(&sc->aac_io_lock);
3721fe3cb0e1SScott Long 		return;
3722fe3cb0e1SScott Long 	}
3723fe3cb0e1SScott Long 
3724fe3cb0e1SScott Long 	c_resp = (struct aac_ctcfg_resp *)&fib->data[0];
3725fe3cb0e1SScott Long 	if (c_resp->Status != ST_OK) {
3726fe3cb0e1SScott Long 		device_printf(sc->aac_dev, "VM_ContainerConfig returned 0x%x\n",
3727fe3cb0e1SScott Long 		    c_resp->Status);
3728fe3cb0e1SScott Long 		aac_release_sync_fib(sc);
37291ffe41c1SChristian S.J. Peron 		mtx_unlock(&sc->aac_io_lock);
3730fe3cb0e1SScott Long 		return;
3731fe3cb0e1SScott Long 	}
3732fe3cb0e1SScott Long 
3733fe3cb0e1SScott Long 	sc->scsi_method_id = c_resp->param;
3734fe3cb0e1SScott Long 
3735fe3cb0e1SScott Long 	vmi = (struct aac_vmioctl *)&fib->data[0];
373639ee03c3SScott Long 	bzero(vmi, sizeof(struct aac_vmioctl));
373739ee03c3SScott Long 
3738fe3cb0e1SScott Long 	vmi->Command = VM_Ioctl;
3739fe3cb0e1SScott Long 	vmi->ObjType = FT_DRIVE;
3740fe3cb0e1SScott Long 	vmi->MethId = sc->scsi_method_id;
3741fe3cb0e1SScott Long 	vmi->ObjId = 0;
3742fe3cb0e1SScott Long 	vmi->IoctlCmd = GetBusInfo;
3743fe3cb0e1SScott Long 
3744fe3cb0e1SScott Long 	error = aac_sync_fib(sc, ContainerCommand, 0, fib,
374542ef13a2SEd Maste 	    sizeof(struct aac_vmi_businf_resp));
3746fe3cb0e1SScott Long 	if (error) {
3747fe3cb0e1SScott Long 		device_printf(sc->aac_dev, "Error %d sending VMIoctl command\n",
3748fe3cb0e1SScott Long 		    error);
3749fe3cb0e1SScott Long 		aac_release_sync_fib(sc);
37501ffe41c1SChristian S.J. Peron 		mtx_unlock(&sc->aac_io_lock);
3751fe3cb0e1SScott Long 		return;
3752fe3cb0e1SScott Long 	}
3753fe3cb0e1SScott Long 
3754fe3cb0e1SScott Long 	vmi_resp = (struct aac_vmi_businf_resp *)&fib->data[0];
3755fe3cb0e1SScott Long 	if (vmi_resp->Status != ST_OK) {
3756fe3cb0e1SScott Long 		device_printf(sc->aac_dev, "VM_Ioctl returned %d\n",
3757fe3cb0e1SScott Long 		    vmi_resp->Status);
3758fe3cb0e1SScott Long 		aac_release_sync_fib(sc);
37591ffe41c1SChristian S.J. Peron 		mtx_unlock(&sc->aac_io_lock);
3760fe3cb0e1SScott Long 		return;
3761fe3cb0e1SScott Long 	}
3762fe3cb0e1SScott Long 
3763fe3cb0e1SScott Long 	bcopy(&vmi_resp->BusInf, &businfo, sizeof(struct aac_getbusinf));
3764fe3cb0e1SScott Long 	aac_release_sync_fib(sc);
37651ffe41c1SChristian S.J. Peron 	mtx_unlock(&sc->aac_io_lock);
3766fe3cb0e1SScott Long 
3767fe3cb0e1SScott Long 	found = 0;
3768fe3cb0e1SScott Long 	for (i = 0; i < businfo.BusCount; i++) {
3769fe3cb0e1SScott Long 		if (businfo.BusValid[i] != AAC_BUS_VALID)
3770fe3cb0e1SScott Long 			continue;
3771fe3cb0e1SScott Long 
3772a761a1caSScott Long 		caminf = (struct aac_sim *)malloc( sizeof(struct aac_sim),
3773a761a1caSScott Long 		    M_AACBUF, M_NOWAIT | M_ZERO);
3774b5f516cdSScott Long 		if (caminf == NULL) {
3775b5f516cdSScott Long 			device_printf(sc->aac_dev,
3776b5f516cdSScott Long 			    "No memory to add passthrough bus %d\n", i);
3777b5f516cdSScott Long 			break;
377874b8d63dSPedro F. Giffuni 		}
3779fe3cb0e1SScott Long 
3780fe3cb0e1SScott Long 		child = device_add_child(sc->aac_dev, "aacp", -1);
3781fe3cb0e1SScott Long 		if (child == NULL) {
3782b5f516cdSScott Long 			device_printf(sc->aac_dev,
3783b5f516cdSScott Long 			    "device_add_child failed for passthrough bus %d\n",
3784b5f516cdSScott Long 			    i);
3785b5f516cdSScott Long 			free(caminf, M_AACBUF);
3786b5f516cdSScott Long 			break;
3787fe3cb0e1SScott Long 		}
3788fe3cb0e1SScott Long 
3789fe3cb0e1SScott Long 		caminf->TargetsPerBus = businfo.TargetsPerBus;
3790fe3cb0e1SScott Long 		caminf->BusNumber = i;
3791fe3cb0e1SScott Long 		caminf->InitiatorBusId = businfo.InitiatorBusId[i];
3792fe3cb0e1SScott Long 		caminf->aac_sc = sc;
3793ddb8683eSScott Long 		caminf->sim_dev = child;
3794fe3cb0e1SScott Long 
3795fe3cb0e1SScott Long 		device_set_ivars(child, caminf);
3796fe3cb0e1SScott Long 		device_set_desc(child, "SCSI Passthrough Bus");
379770545d1aSScott Long 		TAILQ_INSERT_TAIL(&sc->aac_sim_tqh, caminf, sim_link);
3798fe3cb0e1SScott Long 
3799fe3cb0e1SScott Long 		found = 1;
3800fe3cb0e1SScott Long 	}
3801fe3cb0e1SScott Long 
3802fe3cb0e1SScott Long 	if (found)
3803fe3cb0e1SScott Long 		bus_generic_attach(sc->aac_dev);
3804fe3cb0e1SScott Long }
3805