aac.c revision 126080
1135446Strhodes/*-
2234010Sdougb * Copyright (c) 2000 Michael Smith
3135446Strhodes * Copyright (c) 2001 Scott Long
4135446Strhodes * Copyright (c) 2000 BSDi
5174187Sdougb * Copyright (c) 2001 Adaptec, Inc.
6135446Strhodes * All rights reserved.
7135446Strhodes *
8135446Strhodes * Redistribution and use in source and binary forms, with or without
9135446Strhodes * modification, are permitted provided that the following conditions
10135446Strhodes * are met:
11135446Strhodes * 1. Redistributions of source code must retain the above copyright
12135446Strhodes *    notice, this list of conditions and the following disclaimer.
13135446Strhodes * 2. Redistributions in binary form must reproduce the above copyright
14135446Strhodes *    notice, this list of conditions and the following disclaimer in the
15135446Strhodes *    documentation and/or other materials provided with the distribution.
16135446Strhodes *
17135446Strhodes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18234010Sdougb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19135446Strhodes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20170222Sdougb * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21170222Sdougb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22135446Strhodes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23135446Strhodes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24135446Strhodes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25186462Sdougb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26224092Sdougb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27224092Sdougb * SUCH DAMAGE.
28224092Sdougb */
29224092Sdougb
30135446Strhodes#include <sys/cdefs.h>
31135446Strhodes__FBSDID("$FreeBSD: head/sys/dev/aac/aac.c 126080 2004-02-21 21:10:55Z phk $");
32135446Strhodes
33135446Strhodes/*
34135446Strhodes * Driver for the Adaptec 'FSA' family of PCI/SCSI RAID adapters.
35135446Strhodes */
36135446Strhodes
37193149Sdougb#include "opt_aac.h"
38135446Strhodes
39135446Strhodes/* #include <stddef.h> */
40186462Sdougb#include <sys/param.h>
41135446Strhodes#include <sys/systm.h>
42135446Strhodes#include <sys/malloc.h>
43224092Sdougb#include <sys/kernel.h>
44186462Sdougb#include <sys/kthread.h>
45224092Sdougb#include <sys/sysctl.h>
46193149Sdougb#include <sys/poll.h>
47135446Strhodes#include <sys/ioccom.h>
48135446Strhodes
49135446Strhodes#include <sys/bus.h>
50135446Strhodes#include <sys/conf.h>
51135446Strhodes#include <sys/signalvar.h>
52193149Sdougb#include <sys/time.h>
53135446Strhodes#include <sys/eventhandler.h>
54135446Strhodes
55135446Strhodes#include <machine/bus_memio.h>
56135446Strhodes#include <machine/bus.h>
57135446Strhodes#include <machine/resource.h>
58170222Sdougb
59135446Strhodes#include <dev/aac/aacreg.h>
60135446Strhodes#include <dev/aac/aac_ioctl.h>
61135446Strhodes#include <dev/aac/aacvar.h>
62135446Strhodes#include <dev/aac/aac_tables.h>
63170222Sdougb
64224092Sdougbstatic void	aac_startup(void *arg);
65135446Strhodesstatic void	aac_add_container(struct aac_softc *sc,
66135446Strhodes				  struct aac_mntinforesp *mir, int f);
67135446Strhodesstatic void	aac_get_bus_info(struct aac_softc *sc);
68224092Sdougb
69170222Sdougb/* Command Processing */
70135446Strhodesstatic void	aac_timeout(struct aac_softc *sc);
71135446Strhodesstatic int	aac_map_command(struct aac_command *cm);
72135446Strhodesstatic void	aac_complete(void *context, int pending);
73135446Strhodesstatic int	aac_bio_command(struct aac_softc *sc, struct aac_command **cmp);
74135446Strhodesstatic void	aac_bio_complete(struct aac_command *cm);
75193149Sdougbstatic int	aac_wait_command(struct aac_command *cm, int timeout);
76135446Strhodesstatic void	aac_command_thread(struct aac_softc *sc);
77135446Strhodes
78135446Strhodes/* Command Buffer Management */
79135446Strhodesstatic void	aac_map_command_sg(void *arg, bus_dma_segment_t *segs,
80135446Strhodes				   int nseg, int error);
81135446Strhodesstatic void	aac_map_command_helper(void *arg, bus_dma_segment_t *segs,
82135446Strhodes				       int nseg, int error);
83135446Strhodesstatic int	aac_alloc_commands(struct aac_softc *sc);
84193149Sdougbstatic void	aac_free_commands(struct aac_softc *sc);
85135446Strhodesstatic void	aac_unmap_command(struct aac_command *cm);
86135446Strhodes
87135446Strhodes/* Hardware Interface */
88135446Strhodesstatic void	aac_common_map(void *arg, bus_dma_segment_t *segs, int nseg,
89135446Strhodes			       int error);
90135446Strhodesstatic int	aac_check_firmware(struct aac_softc *sc);
91135446Strhodesstatic int	aac_init(struct aac_softc *sc);
92135446Strhodesstatic int	aac_sync_command(struct aac_softc *sc, u_int32_t command,
93135446Strhodes				 u_int32_t arg0, u_int32_t arg1, u_int32_t arg2,
94135446Strhodes				 u_int32_t arg3, u_int32_t *sp);
95135446Strhodesstatic int	aac_enqueue_fib(struct aac_softc *sc, int queue,
96135446Strhodes				struct aac_command *cm);
97135446Strhodesstatic int	aac_dequeue_fib(struct aac_softc *sc, int queue,
98135446Strhodes				u_int32_t *fib_size, struct aac_fib **fib_addr);
99135446Strhodesstatic int	aac_enqueue_response(struct aac_softc *sc, int queue,
100135446Strhodes				     struct aac_fib *fib);
101135446Strhodes
102193149Sdougb/* Falcon/PPC interface */
103135446Strhodesstatic int	aac_fa_get_fwstatus(struct aac_softc *sc);
104135446Strhodesstatic void	aac_fa_qnotify(struct aac_softc *sc, int qbit);
105135446Strhodesstatic int	aac_fa_get_istatus(struct aac_softc *sc);
106153816Sdougbstatic void	aac_fa_clear_istatus(struct aac_softc *sc, int mask);
107153816Sdougbstatic void	aac_fa_set_mailbox(struct aac_softc *sc, u_int32_t command,
108153816Sdougb				   u_int32_t arg0, u_int32_t arg1,
109153816Sdougb				   u_int32_t arg2, u_int32_t arg3);
110135446Strhodesstatic int	aac_fa_get_mailbox(struct aac_softc *sc, int mb);
111224092Sdougbstatic void	aac_fa_set_interrupts(struct aac_softc *sc, int enable);
112224092Sdougb
113224092Sdougbstruct aac_interface aac_fa_interface = {
114224092Sdougb	aac_fa_get_fwstatus,
115170222Sdougb	aac_fa_qnotify,
116135446Strhodes	aac_fa_get_istatus,
117135446Strhodes	aac_fa_clear_istatus,
118135446Strhodes	aac_fa_set_mailbox,
119135446Strhodes	aac_fa_get_mailbox,
120193149Sdougb	aac_fa_set_interrupts
121193149Sdougb};
122135446Strhodes
123135446Strhodes/* StrongARM interface */
124135446Strhodesstatic int	aac_sa_get_fwstatus(struct aac_softc *sc);
125193149Sdougbstatic void	aac_sa_qnotify(struct aac_softc *sc, int qbit);
126135446Strhodesstatic int	aac_sa_get_istatus(struct aac_softc *sc);
127135446Strhodesstatic void	aac_sa_clear_istatus(struct aac_softc *sc, int mask);
128135446Strhodesstatic void	aac_sa_set_mailbox(struct aac_softc *sc, u_int32_t command,
129135446Strhodes				   u_int32_t arg0, u_int32_t arg1,
130135446Strhodes				   u_int32_t arg2, u_int32_t arg3);
131135446Strhodesstatic int	aac_sa_get_mailbox(struct aac_softc *sc, int mb);
132135446Strhodesstatic void	aac_sa_set_interrupts(struct aac_softc *sc, int enable);
133135446Strhodes
134135446Strhodesstruct aac_interface aac_sa_interface = {
135135446Strhodes	aac_sa_get_fwstatus,
136135446Strhodes	aac_sa_qnotify,
137135446Strhodes	aac_sa_get_istatus,
138193149Sdougb	aac_sa_clear_istatus,
139135446Strhodes	aac_sa_set_mailbox,
140135446Strhodes	aac_sa_get_mailbox,
141135446Strhodes	aac_sa_set_interrupts
142135446Strhodes};
143135446Strhodes
144135446Strhodes/* i960Rx interface */
145135446Strhodesstatic int	aac_rx_get_fwstatus(struct aac_softc *sc);
146135446Strhodesstatic void	aac_rx_qnotify(struct aac_softc *sc, int qbit);
147135446Strhodesstatic int	aac_rx_get_istatus(struct aac_softc *sc);
148135446Strhodesstatic void	aac_rx_clear_istatus(struct aac_softc *sc, int mask);
149135446Strhodesstatic void	aac_rx_set_mailbox(struct aac_softc *sc, u_int32_t command,
150135446Strhodes				   u_int32_t arg0, u_int32_t arg1,
151193149Sdougb				   u_int32_t arg2, u_int32_t arg3);
152135446Strhodesstatic int	aac_rx_get_mailbox(struct aac_softc *sc, int mb);
153135446Strhodesstatic void	aac_rx_set_interrupts(struct aac_softc *sc, int enable);
154135446Strhodes
155135446Strhodesstruct aac_interface aac_rx_interface = {
156224092Sdougb	aac_rx_get_fwstatus,
157224092Sdougb	aac_rx_qnotify,
158224092Sdougb	aac_rx_get_istatus,
159224092Sdougb	aac_rx_clear_istatus,
160224092Sdougb	aac_rx_set_mailbox,
161224092Sdougb	aac_rx_get_mailbox,
162224092Sdougb	aac_rx_set_interrupts
163224092Sdougb};
164135446Strhodes
165135446Strhodes/* Debugging and Diagnostics */
166135446Strhodesstatic void	aac_describe_controller(struct aac_softc *sc);
167135446Strhodesstatic char	*aac_describe_code(struct aac_code_lookup *table,
168135446Strhodes				   u_int32_t code);
169135446Strhodes
170135446Strhodes/* Management Interface */
171224092Sdougbstatic d_open_t		aac_open;
172224092Sdougbstatic d_close_t	aac_close;
173224092Sdougbstatic d_ioctl_t	aac_ioctl;
174224092Sdougbstatic d_poll_t		aac_poll;
175224092Sdougbstatic int		aac_ioctl_sendfib(struct aac_softc *sc, caddr_t ufib);
176224092Sdougbstatic void		aac_handle_aif(struct aac_softc *sc,
177224092Sdougb					   struct aac_fib *fib);
178224092Sdougbstatic int		aac_rev_check(struct aac_softc *sc, caddr_t udata);
179135446Strhodesstatic int		aac_getnext_aif(struct aac_softc *sc, caddr_t arg);
180135446Strhodesstatic int		aac_return_aif(struct aac_softc *sc, caddr_t uptr);
181135446Strhodesstatic int		aac_query_disk(struct aac_softc *sc, caddr_t uptr);
182135446Strhodes
183135446Strhodesstatic struct cdevsw aac_cdevsw = {
184135446Strhodes	.d_version =	D_VERSION,
185135446Strhodes	.d_flags =	D_NEEDGIANT,
186135446Strhodes	.d_open =	aac_open,
187135446Strhodes	.d_close =	aac_close,
188135446Strhodes	.d_ioctl =	aac_ioctl,
189135446Strhodes	.d_poll =	aac_poll,
190135446Strhodes	.d_name =	"aac",
191135446Strhodes};
192135446Strhodes
193135446StrhodesMALLOC_DEFINE(M_AACBUF, "aacbuf", "Buffers for the AAC driver");
194135446Strhodes
195135446Strhodes/* sysctl node */
196135446StrhodesSYSCTL_NODE(_hw, OID_AUTO, aac, CTLFLAG_RD, 0, "AAC driver parameters");
197135446Strhodes
198135446Strhodes/*
199135446Strhodes * Device Interface
200135446Strhodes */
201135446Strhodes
202135446Strhodes/*
203135446Strhodes * Initialise the controller and softc
204135446Strhodes */
205224092Sdougbint
206224092Sdougbaac_attach(struct aac_softc *sc)
207225361Sdougb{
208224092Sdougb	int error, unit;
209224092Sdougb
210224092Sdougb	debug_called(1);
211225361Sdougb
212224092Sdougb	/*
213225361Sdougb	 * Initialise per-controller queues.
214225361Sdougb	 */
215225361Sdougb	aac_initq_free(sc);
216224092Sdougb	aac_initq_ready(sc);
217224092Sdougb	aac_initq_busy(sc);
218170222Sdougb	aac_initq_bio(sc);
219170222Sdougb
220170222Sdougb	/*
221170222Sdougb	 * Initialise command-completion task.
222170222Sdougb	 */
223170222Sdougb	TASK_INIT(&sc->aac_task_complete, 0, aac_complete, sc);
224170222Sdougb
225170222Sdougb	/* disable interrupts before we enable anything */
226170222Sdougb	AAC_MASK_INTERRUPTS(sc);
227170222Sdougb
228170222Sdougb	/* mark controller as suspended until we get ourselves organised */
229170222Sdougb	sc->aac_state |= AAC_STATE_SUSPEND;
230170222Sdougb
231170222Sdougb	/*
232170222Sdougb	 * Check that the firmware on the card is supported.
233170222Sdougb	 */
234170222Sdougb	if ((error = aac_check_firmware(sc)) != 0)
235170222Sdougb		return(error);
236170222Sdougb
237170222Sdougb	/*
238170222Sdougb	 * Initialize locks
239170222Sdougb	 */
240170222Sdougb	AAC_LOCK_INIT(&sc->aac_sync_lock, "AAC sync FIB lock");
241170222Sdougb	AAC_LOCK_INIT(&sc->aac_aifq_lock, "AAC AIF lock");
242170222Sdougb	AAC_LOCK_INIT(&sc->aac_io_lock, "AAC I/O lock");
243170222Sdougb	AAC_LOCK_INIT(&sc->aac_container_lock, "AAC container lock");
244170222Sdougb	TAILQ_INIT(&sc->aac_container_tqh);
245218384Sdougb
246186462Sdougb	/* Initialize the local AIF queue pointers */
247170222Sdougb	sc->aac_aifq_head = sc->aac_aifq_tail = AAC_AIFQ_LENGTH;
248170222Sdougb
249170222Sdougb	/*
250218384Sdougb	 * Initialise the adapter.
251218384Sdougb	 */
252170222Sdougb	if ((error = aac_init(sc)) != 0)
253170222Sdougb		return(error);
254170222Sdougb
255170222Sdougb	/*
256170222Sdougb	 * Print a little information about the controller.
257193149Sdougb	 */
258170222Sdougb	aac_describe_controller(sc);
259170222Sdougb
260170222Sdougb	/*
261170222Sdougb	 * Register to probe our containers later.
262170222Sdougb	 */
263170222Sdougb	sc->aac_ich.ich_func = aac_startup;
264218384Sdougb	sc->aac_ich.ich_arg = sc;
265218384Sdougb	if (config_intrhook_establish(&sc->aac_ich) != 0) {
266218384Sdougb		device_printf(sc->aac_dev,
267170222Sdougb			      "can't establish configuration hook\n");
268170222Sdougb		return(ENXIO);
269170222Sdougb	}
270224092Sdougb
271224092Sdougb	/*
272135446Strhodes	 * Make the control device.
273135446Strhodes	 */
274135446Strhodes	unit = device_get_unit(sc->aac_dev);
275135446Strhodes	sc->aac_dev_t = make_dev(&aac_cdevsw, unit, UID_ROOT, GID_OPERATOR,
276135446Strhodes				 0640, "aac%d", unit);
277165071Sdougb	(void)make_dev_alias(sc->aac_dev_t, "afa%d", unit);
278170222Sdougb	(void)make_dev_alias(sc->aac_dev_t, "hpn%d", unit);
279135446Strhodes	sc->aac_dev_t->si_drv1 = sc;
280135446Strhodes
281165071Sdougb	/* Create the AIF thread */
282170222Sdougb	if (kthread_create((void(*)(void *))aac_command_thread, sc,
283135446Strhodes			   &sc->aifthread, 0, 0, "aac%daif", unit))
284135446Strhodes		panic("Could not create AIF thread\n");
285135446Strhodes
286165071Sdougb	/* Register the shutdown method to only be called post-dump */
287165071Sdougb	if ((sc->eh = EVENTHANDLER_REGISTER(shutdown_final, aac_shutdown,
288135446Strhodes	    sc->aac_dev, SHUTDOWN_PRI_DEFAULT)) == NULL)
289135446Strhodes		device_printf(sc->aac_dev,
290165071Sdougb			      "shutdown event registration failed\n");
291165071Sdougb
292135446Strhodes	/* Register with CAM for the non-DASD devices */
293135446Strhodes	if ((sc->flags & AAC_FLAGS_ENABLE_CAM) != 0) {
294165071Sdougb		TAILQ_INIT(&sc->aac_sim_tqh);
295165071Sdougb		aac_get_bus_info(sc);
296224092Sdougb	}
297135446Strhodes
298224092Sdougb	return(0);
299224092Sdougb}
300224092Sdougb
301135446Strhodes/*
302135446Strhodes * Probe for containers, create disks.
303135446Strhodes */
304224092Sdougbstatic void
305225361Sdougbaac_startup(void *arg)
306224092Sdougb{
307170222Sdougb	struct aac_softc *sc;
308193149Sdougb	struct aac_fib *fib;
309193149Sdougb	struct aac_mntinfo *mi;
310135446Strhodes	struct aac_mntinforesp *mir = NULL;
311135446Strhodes	int count = 0, i = 0;
312165071Sdougb
313224092Sdougb	debug_called(1);
314224092Sdougb
315135446Strhodes	sc = (struct aac_softc *)arg;
316135446Strhodes
317165071Sdougb	/* disconnect ourselves from the intrhook chain */
318165071Sdougb	config_intrhook_disestablish(&sc->aac_ich);
319135446Strhodes
320135446Strhodes	aac_alloc_sync_fib(sc, &fib, 0);
321135446Strhodes	mi = (struct aac_mntinfo *)&fib->data[0];
322135446Strhodes
323135446Strhodes	/* loop over possible containers */
324135446Strhodes	do {
325135446Strhodes		/* request information on this container */
326165071Sdougb		bzero(mi, sizeof(struct aac_mntinfo));
327135446Strhodes		mi->Command = VM_NameServe;
328135446Strhodes		mi->MntType = FT_FILESYS;
329135446Strhodes		mi->MntCount = i;
330135446Strhodes		if (aac_sync_fib(sc, ContainerCommand, 0, fib,
331135446Strhodes				 sizeof(struct aac_mntinfo))) {
332135446Strhodes			printf("error probing container %d", i);
333165071Sdougb			continue;
334135446Strhodes		}
335135446Strhodes
336193149Sdougb		mir = (struct aac_mntinforesp *)&fib->data[0];
337135446Strhodes		/* XXX Need to check if count changed */
338135446Strhodes		count = mir->MntRespCount;
339135446Strhodes		aac_add_container(sc, mir, 0);
340224092Sdougb		i++;
341224092Sdougb	} while ((i < count) && (i < AAC_MAX_CONTAINERS));
342224092Sdougb
343224092Sdougb	aac_release_sync_fib(sc);
344224092Sdougb
345224092Sdougb	/* poke the bus to actually attach the child devices */
346224092Sdougb	if (bus_generic_attach(sc->aac_dev))
347224092Sdougb		device_printf(sc->aac_dev, "bus_generic_attach failed\n");
348224092Sdougb
349170222Sdougb	/* mark the controller up */
350193149Sdougb	sc->aac_state &= ~AAC_STATE_SUSPEND;
351135446Strhodes
352135446Strhodes	/* enable interrupts now */
353135446Strhodes	AAC_UNMASK_INTERRUPTS(sc);
354135446Strhodes}
355193149Sdougb
356193149Sdougb/*
357193149Sdougb * Create a device to respresent a new container
358193149Sdougb */
359193149Sdougbstatic void
360135446Strhodesaac_add_container(struct aac_softc *sc, struct aac_mntinforesp *mir, int f)
361193149Sdougb{
362193149Sdougb	struct aac_container *co;
363193149Sdougb	device_t child;
364193149Sdougb
365193149Sdougb	/*
366193149Sdougb	 * Check container volume type for validity.  Note that many of
367193149Sdougb	 * the possible types may never show up.
368193149Sdougb	 */
369193149Sdougb	if ((mir->Status == ST_OK) && (mir->MntTable[0].VolType != CT_NONE)) {
370193149Sdougb		co = (struct aac_container *)malloc(sizeof *co, M_AACBUF,
371193149Sdougb		       M_NOWAIT | M_ZERO);
372193149Sdougb		if (co == NULL)
373193149Sdougb			panic("Out of memory?!\n");
374193149Sdougb		debug(1, "id %x  name '%.16s'  size %u  type %d",
375193149Sdougb		      mir->MntTable[0].ObjectId,
376193149Sdougb		      mir->MntTable[0].FileSystemName,
377193149Sdougb		      mir->MntTable[0].Capacity, mir->MntTable[0].VolType);
378193149Sdougb
379193149Sdougb		if ((child = device_add_child(sc->aac_dev, "aacd", -1)) == NULL)
380193149Sdougb			device_printf(sc->aac_dev, "device_add_child failed\n");
381193149Sdougb		else
382193149Sdougb			device_set_ivars(child, co);
383193149Sdougb		device_set_desc(child, aac_describe_code(aac_container_types,
384193149Sdougb				mir->MntTable[0].VolType));
385193149Sdougb		co->co_disk = child;
386193149Sdougb		co->co_found = f;
387193149Sdougb		bcopy(&mir->MntTable[0], &co->co_mntobj,
388193149Sdougb		      sizeof(struct aac_mntobj));
389193149Sdougb		AAC_LOCK_ACQUIRE(&sc->aac_container_lock);
390193149Sdougb		TAILQ_INSERT_TAIL(&sc->aac_container_tqh, co, co_link);
391193149Sdougb		AAC_LOCK_RELEASE(&sc->aac_container_lock);
392193149Sdougb	}
393193149Sdougb}
394193149Sdougb
395193149Sdougb/*
396193149Sdougb * Free all of the resources associated with (sc)
397193149Sdougb *
398193149Sdougb * Should not be called if the controller is active.
399224092Sdougb */
400224092Sdougbvoid
401224092Sdougbaac_free(struct aac_softc *sc)
402135446Strhodes{
403224092Sdougb
404224092Sdougb	debug_called(1);
405224092Sdougb
406224092Sdougb	/* remove the control device */
407224092Sdougb	if (sc->aac_dev_t != NULL)
408224092Sdougb		destroy_dev(sc->aac_dev_t);
409224092Sdougb
410224092Sdougb	/* throw away any FIB buffers, discard the FIB DMA tag */
411224092Sdougb	aac_free_commands(sc);
412224092Sdougb	if (sc->aac_fib_dmat)
413224092Sdougb		bus_dma_tag_destroy(sc->aac_fib_dmat);
414224092Sdougb
415224092Sdougb	free(sc->aac_commands, M_AACBUF);
416224092Sdougb
417224092Sdougb	/* destroy the common area */
418224092Sdougb	if (sc->aac_common) {
419224092Sdougb		bus_dmamap_unload(sc->aac_common_dmat, sc->aac_common_dmamap);
420224092Sdougb		bus_dmamem_free(sc->aac_common_dmat, sc->aac_common,
421224092Sdougb				sc->aac_common_dmamap);
422224092Sdougb	}
423224092Sdougb	if (sc->aac_common_dmat)
424224092Sdougb		bus_dma_tag_destroy(sc->aac_common_dmat);
425224092Sdougb
426224092Sdougb	/* disconnect the interrupt handler */
427224092Sdougb	if (sc->aac_intr)
428224092Sdougb		bus_teardown_intr(sc->aac_dev, sc->aac_irq, sc->aac_intr);
429224092Sdougb	if (sc->aac_irq != NULL)
430224092Sdougb		bus_release_resource(sc->aac_dev, SYS_RES_IRQ, sc->aac_irq_rid,
431224092Sdougb				     sc->aac_irq);
432224092Sdougb
433224092Sdougb	/* destroy data-transfer DMA tag */
434224092Sdougb	if (sc->aac_buffer_dmat)
435224092Sdougb		bus_dma_tag_destroy(sc->aac_buffer_dmat);
436224092Sdougb
437224092Sdougb	/* destroy the parent DMA tag */
438224092Sdougb	if (sc->aac_parent_dmat)
439224092Sdougb		bus_dma_tag_destroy(sc->aac_parent_dmat);
440224092Sdougb
441224092Sdougb	/* release the register window mapping */
442224092Sdougb	if (sc->aac_regs_resource != NULL)
443224092Sdougb		bus_release_resource(sc->aac_dev, SYS_RES_MEMORY,
444224092Sdougb				     sc->aac_regs_rid, sc->aac_regs_resource);
445224092Sdougb}
446224092Sdougb
447224092Sdougb/*
448224092Sdougb * Disconnect from the controller completely, in preparation for unload.
449224092Sdougb */
450224092Sdougbint
451224092Sdougbaac_detach(device_t dev)
452224092Sdougb{
453224092Sdougb	struct aac_softc *sc;
454224092Sdougb	struct aac_container *co;
455224092Sdougb	struct aac_sim	*sim;
456224092Sdougb	int error;
457224092Sdougb
458224092Sdougb	debug_called(1);
459224092Sdougb
460224092Sdougb	sc = device_get_softc(dev);
461224092Sdougb
462224092Sdougb	if (sc->aac_state & AAC_STATE_OPEN)
463224092Sdougb		return(EBUSY);
464224092Sdougb
465224092Sdougb	/* Remove the child containers */
466224092Sdougb	while ((co = TAILQ_FIRST(&sc->aac_container_tqh)) != NULL) {
467224092Sdougb		error = device_delete_child(dev, co->co_disk);
468224092Sdougb		if (error)
469224092Sdougb			return (error);
470224092Sdougb		TAILQ_REMOVE(&sc->aac_container_tqh, co, co_link);
471224092Sdougb		free(co, M_AACBUF);
472224092Sdougb	}
473224092Sdougb
474224092Sdougb	/* Remove the CAM SIMs */
475224092Sdougb	while ((sim = TAILQ_FIRST(&sc->aac_sim_tqh)) != NULL) {
476224092Sdougb		TAILQ_REMOVE(&sc->aac_sim_tqh, sim, sim_link);
477224092Sdougb		error = device_delete_child(dev, sim->sim_dev);
478224092Sdougb		if (error)
479224092Sdougb			return (error);
480224092Sdougb		free(sim, M_AACBUF);
481224092Sdougb	}
482135446Strhodes
483135446Strhodes	if (sc->aifflags & AAC_AIFFLAGS_RUNNING) {
484135446Strhodes		sc->aifflags |= AAC_AIFFLAGS_EXIT;
485165071Sdougb		wakeup(sc->aifthread);
486135446Strhodes		tsleep(sc->aac_dev, PUSER | PCATCH, "aacdch", 30 * hz);
487135446Strhodes	}
488135446Strhodes
489135446Strhodes	if (sc->aifflags & AAC_AIFFLAGS_RUNNING)
490135446Strhodes		panic("Cannot shutdown AIF thread\n");
491135446Strhodes
492135446Strhodes	if ((error = aac_shutdown(dev)))
493135446Strhodes		return(error);
494135446Strhodes
495135446Strhodes	EVENTHANDLER_DEREGISTER(shutdown_final, sc->eh);
496135446Strhodes
497224092Sdougb	aac_free(sc);
498224092Sdougb
499135446Strhodes	return(0);
500135446Strhodes}
501135446Strhodes
502135446Strhodes/*
503135446Strhodes * Bring the controller down to a dormant state and detach all child devices.
504135446Strhodes *
505224092Sdougb * This function is called before detach or system shutdown.
506224092Sdougb *
507224092Sdougb * Note that we can assume that the bioq on the controller is empty, as we won't
508224092Sdougb * allow shutdown if any device is open.
509224092Sdougb */
510224092Sdougbint
511224092Sdougbaac_shutdown(device_t dev)
512224092Sdougb{
513224092Sdougb	struct aac_softc *sc;
514224092Sdougb	struct aac_fib *fib;
515224092Sdougb	struct aac_close_command *cc;
516224092Sdougb
517224092Sdougb	debug_called(1);
518224092Sdougb
519135446Strhodes	sc = device_get_softc(dev);
520135446Strhodes
521135446Strhodes	sc->aac_state |= AAC_STATE_SUSPEND;
522165071Sdougb
523135446Strhodes	/*
524135446Strhodes	 * Send a Container shutdown followed by a HostShutdown FIB to the
525135446Strhodes	 * controller to convince it that we don't want to talk to it anymore.
526135446Strhodes	 * We've been closed and all I/O completed already
527135446Strhodes	 */
528135446Strhodes	device_printf(sc->aac_dev, "shutting down controller...");
529135446Strhodes
530135446Strhodes	aac_alloc_sync_fib(sc, &fib, AAC_SYNC_LOCK_FORCE);
531135446Strhodes	cc = (struct aac_close_command *)&fib->data[0];
532135446Strhodes
533135446Strhodes	bzero(cc, sizeof(struct aac_close_command));
534135446Strhodes	cc->Command = VM_CloseAll;
535135446Strhodes	cc->ContainerId = 0xffffffff;
536135446Strhodes	if (aac_sync_fib(sc, ContainerCommand, 0, fib,
537135446Strhodes	    sizeof(struct aac_close_command)))
538135446Strhodes		printf("FAILED.\n");
539135446Strhodes	else
540135446Strhodes		printf("done\n");
541135446Strhodes#if 0
542135446Strhodes	else {
543135446Strhodes		fib->data[0] = 0;
544135446Strhodes		/*
545135446Strhodes		 * XXX Issuing this command to the controller makes it shut down
546135446Strhodes		 * but also keeps it from coming back up without a reset of the
547135446Strhodes		 * PCI bus.  This is not desirable if you are just unloading the
548135446Strhodes		 * driver module with the intent to reload it later.
549135446Strhodes		 */
550135446Strhodes		if (aac_sync_fib(sc, FsaHostShutdown, AAC_FIBSTATE_SHUTDOWN,
551135446Strhodes		    fib, 1)) {
552135446Strhodes			printf("FAILED.\n");
553135446Strhodes		} else {
554170222Sdougb			printf("done.\n");
555170222Sdougb		}
556170222Sdougb	}
557170222Sdougb#endif
558224092Sdougb
559224092Sdougb	AAC_MASK_INTERRUPTS(sc);
560170222Sdougb
561170222Sdougb	return(0);
562135446Strhodes}
563135446Strhodes
564135446Strhodes/*
565135446Strhodes * Bring the controller to a quiescent state, ready for system suspend.
566135446Strhodes */
567135446Strhodesint
568135446Strhodesaac_suspend(device_t dev)
569224092Sdougb{
570135446Strhodes	struct aac_softc *sc;
571135446Strhodes
572135446Strhodes	debug_called(1);
573224092Sdougb
574135446Strhodes	sc = device_get_softc(dev);
575135446Strhodes
576135446Strhodes	sc->aac_state |= AAC_STATE_SUSPEND;
577135446Strhodes
578135446Strhodes	AAC_MASK_INTERRUPTS(sc);
579224092Sdougb	return(0);
580224092Sdougb}
581135446Strhodes
582224092Sdougb/*
583224092Sdougb * Bring the controller back to a state ready for operation.
584224092Sdougb */
585224092Sdougbint
586224092Sdougbaac_resume(device_t dev)
587135446Strhodes{
588135446Strhodes	struct aac_softc *sc;
589224092Sdougb
590224092Sdougb	debug_called(1);
591135446Strhodes
592135446Strhodes	sc = device_get_softc(dev);
593135446Strhodes
594135446Strhodes	sc->aac_state &= ~AAC_STATE_SUSPEND;
595135446Strhodes	AAC_UNMASK_INTERRUPTS(sc);
596135446Strhodes	return(0);
597135446Strhodes}
598135446Strhodes
599135446Strhodes/*
600135446Strhodes * Take an interrupt.
601224092Sdougb */
602224092Sdougbvoid
603224092Sdougbaac_intr(void *arg)
604224092Sdougb{
605224092Sdougb	struct aac_softc *sc;
606224092Sdougb	u_int16_t reason;
607224092Sdougb
608224092Sdougb	debug_called(2);
609224092Sdougb
610224092Sdougb	sc = (struct aac_softc *)arg;
611224092Sdougb
612224092Sdougb	/*
613224092Sdougb	 * Read the status register directly.  This is faster than taking the
614224092Sdougb	 * driver lock and reading the queues directly.  It also saves having
615224092Sdougb	 * to turn parts of the driver lock into a spin mutex, which would be
616224092Sdougb	 * ugly.
617224092Sdougb	 */
618224092Sdougb	reason = AAC_GET_ISTATUS(sc);
619224092Sdougb	AAC_CLEAR_ISTATUS(sc, reason);
620224092Sdougb
621224092Sdougb	/* handle completion processing */
622224092Sdougb	if (reason & AAC_DB_RESPONSE_READY)
623224092Sdougb		taskqueue_enqueue_fast(taskqueue_fast, &sc->aac_task_complete);
624224092Sdougb
625224092Sdougb	/* controller wants to talk to us */
626224092Sdougb	if (reason & (AAC_DB_PRINTF | AAC_DB_COMMAND_READY)) {
627224092Sdougb		/*
628224092Sdougb		 * XXX Make sure that we don't get fooled by strange messages
629224092Sdougb		 * that start with a NULL.
630224092Sdougb		 */
631224092Sdougb		if ((reason & AAC_DB_PRINTF) &&
632224092Sdougb		    (sc->aac_common->ac_printf[0] == 0))
633224092Sdougb			sc->aac_common->ac_printf[0] = 32;
634224092Sdougb
635224092Sdougb		/*
636224092Sdougb		 * This might miss doing the actual wakeup.  However, the
637224092Sdougb		 * msleep that this is waking up has a timeout, so it will
638224092Sdougb		 * wake up eventually.  AIFs and printfs are low enough
639224092Sdougb		 * priority that they can handle hanging out for a few seconds
640224092Sdougb		 * if needed.
641224092Sdougb		 */
642224092Sdougb		wakeup(sc->aifthread);
643224092Sdougb	}
644224092Sdougb}
645224092Sdougb
646224092Sdougb/*
647224092Sdougb * Command Processing
648224092Sdougb */
649224092Sdougb
650224092Sdougb/*
651224092Sdougb * Start as much queued I/O as possible on the controller
652224092Sdougb */
653224092Sdougbvoid
654224092Sdougbaac_startio(struct aac_softc *sc)
655224092Sdougb{
656170222Sdougb	struct aac_command *cm;
657224092Sdougb
658135446Strhodes	debug_called(2);
659135446Strhodes
660224092Sdougb	if (sc->flags & AAC_QUEUE_FRZN)
661135446Strhodes		return;
662135446Strhodes
663224092Sdougb	for (;;) {
664224092Sdougb		/*
665224092Sdougb		 * Try to get a command that's been put off for lack of
666224092Sdougb		 * resources
667135446Strhodes		 */
668224092Sdougb		cm = aac_dequeue_ready(sc);
669224092Sdougb
670224092Sdougb		/*
671224092Sdougb		 * Try to build a command off the bio queue (ignore error
672224092Sdougb		 * return)
673224092Sdougb		 */
674165071Sdougb		if (cm == NULL)
675224092Sdougb			aac_bio_command(sc, &cm);
676224092Sdougb
677224092Sdougb		/* nothing to do? */
678224092Sdougb		if (cm == NULL)
679135446Strhodes			break;
680224092Sdougb
681224092Sdougb		/*
682224092Sdougb		 * Try to give the command to the controller.  Any error is
683224092Sdougb		 * catastrophic since it means that bus_dmamap_load() failed.
684224092Sdougb		 */
685135446Strhodes		if (aac_map_command(cm) != 0)
686224092Sdougb			panic("aac: error mapping command %p\n", cm);
687135446Strhodes	}
688224092Sdougb}
689224092Sdougb
690224092Sdougb/*
691224092Sdougb * Deliver a command to the controller; allocate controller resources at the
692224092Sdougb * last moment when possible.
693224092Sdougb */
694224092Sdougbstatic int
695224092Sdougbaac_map_command(struct aac_command *cm)
696135446Strhodes{
697224092Sdougb	struct aac_softc *sc;
698224092Sdougb	int error;
699224092Sdougb
700224092Sdougb	debug_called(2);
701224092Sdougb
702224092Sdougb	sc = cm->cm_sc;
703224092Sdougb	error = 0;
704224092Sdougb
705135446Strhodes	/* don't map more than once */
706224092Sdougb	if (cm->cm_flags & AAC_CMD_MAPPED)
707224092Sdougb		panic("aac: command %p already mapped", cm);
708224092Sdougb
709224092Sdougb	if (cm->cm_datalen != 0) {
710224092Sdougb		error = bus_dmamap_load(sc->aac_buffer_dmat, cm->cm_datamap,
711224092Sdougb					cm->cm_data, cm->cm_datalen,
712224092Sdougb					aac_map_command_sg, cm, 0);
713224092Sdougb		if (error == EINPROGRESS) {
714224092Sdougb			debug(1, "freezing queue\n");
715224092Sdougb			sc->flags |= AAC_QUEUE_FRZN;
716224092Sdougb			error = 0;
717224092Sdougb		}
718224092Sdougb	} else {
719224092Sdougb		aac_map_command_sg(cm, NULL, 0, 0);
720224092Sdougb	}
721224092Sdougb	return (error);
722224092Sdougb}
723224092Sdougb
724224092Sdougb/*
725224092Sdougb * Handle notification of one or more FIBs coming from the controller.
726224092Sdougb */
727224092Sdougbstatic void
728224092Sdougbaac_command_thread(struct aac_softc *sc)
729224092Sdougb{
730224092Sdougb	struct aac_fib *fib;
731224092Sdougb	u_int32_t fib_size;
732224092Sdougb	int size, retval;
733224092Sdougb
734224092Sdougb	debug_called(2);
735224092Sdougb
736224092Sdougb	AAC_LOCK_ACQUIRE(&sc->aac_io_lock);
737224092Sdougb	sc->aifflags = AAC_AIFFLAGS_RUNNING;
738224092Sdougb
739224092Sdougb	while ((sc->aifflags & AAC_AIFFLAGS_EXIT) == 0) {
740135446Strhodes
741224092Sdougb		retval = 0;
742224092Sdougb		if ((sc->aifflags & AAC_AIFFLAGS_PENDING) == 0)
743224092Sdougb			retval = msleep(sc->aifthread, &sc->aac_io_lock, PRIBIO,
744224092Sdougb					"aifthd", AAC_PERIODIC_INTERVAL * hz);
745224092Sdougb
746224092Sdougb		/*
747224092Sdougb		 * First see if any FIBs need to be allocated.  This needs
748135446Strhodes		 * to be called without the driver lock because contigmalloc
749135446Strhodes		 * will grab Giant, and would result in an LOR.
750224092Sdougb		 */
751224092Sdougb		if ((sc->aifflags & AAC_AIFFLAGS_ALLOCFIBS) != 0) {
752224092Sdougb			AAC_LOCK_RELEASE(&sc->aac_io_lock);
753186462Sdougb			aac_alloc_commands(sc);
754224092Sdougb			AAC_LOCK_ACQUIRE(&sc->aac_io_lock);
755224092Sdougb			sc->aifflags &= ~AAC_AIFFLAGS_ALLOCFIBS;
756224092Sdougb			aac_startio(sc);
757224092Sdougb		}
758224092Sdougb
759224092Sdougb		/*
760224092Sdougb		 * While we're here, check to see if any commands are stuck.
761224092Sdougb		 * This is pretty low-priority, so it's ok if it doesn't
762224092Sdougb		 * always fire.
763224092Sdougb		 */
764224092Sdougb		if (retval == EWOULDBLOCK)
765224092Sdougb			aac_timeout(sc);
766224092Sdougb
767224092Sdougb		/* Check the hardware printf message buffer */
768224092Sdougb		if (sc->aac_common->ac_printf[0] != 0)
769224092Sdougb			aac_print_printf(sc);
770224092Sdougb
771224092Sdougb		/* Also check to see if the adapter has a command for us. */
772224092Sdougb		while (aac_dequeue_fib(sc, AAC_HOST_NORM_CMD_QUEUE,
773224092Sdougb				       &fib_size, &fib) == 0) {
774224092Sdougb
775224092Sdougb			AAC_PRINT_FIB(sc, fib);
776224092Sdougb
777224092Sdougb			switch (fib->Header.Command) {
778224092Sdougb			case AifRequest:
779224092Sdougb				aac_handle_aif(sc, fib);
780224092Sdougb				break;
781224092Sdougb			default:
782224092Sdougb				device_printf(sc->aac_dev, "unknown command "
783224092Sdougb					      "from controller\n");
784224092Sdougb				break;
785224092Sdougb			}
786224092Sdougb
787224092Sdougb			if ((fib->Header.XferState == 0) ||
788224092Sdougb			    (fib->Header.StructType != AAC_FIBTYPE_TFIB))
789224092Sdougb				break;
790224092Sdougb
791224092Sdougb			/* Return the AIF to the controller. */
792224092Sdougb			if (fib->Header.XferState & AAC_FIBSTATE_FROMADAP) {
793224092Sdougb				fib->Header.XferState |= AAC_FIBSTATE_DONEHOST;
794224092Sdougb				*(AAC_FSAStatus*)fib->data = ST_OK;
795224092Sdougb
796224092Sdougb				/* XXX Compute the Size field? */
797224092Sdougb				size = fib->Header.Size;
798224092Sdougb				if (size > sizeof(struct aac_fib)) {
799224092Sdougb					size = sizeof(struct aac_fib);
800224092Sdougb					fib->Header.Size = size;
801224092Sdougb				}
802224092Sdougb				/*
803224092Sdougb				 * Since we did not generate this command, it
804224092Sdougb				 * cannot go through the normal
805135446Strhodes				 * enqueue->startio chain.
806135446Strhodes				 */
807135446Strhodes				aac_enqueue_response(sc,
808135446Strhodes						     AAC_ADAP_NORM_RESP_QUEUE,
809224092Sdougb						     fib);
810165071Sdougb			}
811165071Sdougb		}
812135446Strhodes	}
813135446Strhodes	sc->aifflags &= ~AAC_AIFFLAGS_RUNNING;
814135446Strhodes	AAC_LOCK_RELEASE(&sc->aac_io_lock);
815135446Strhodes	wakeup(sc->aac_dev);
816135446Strhodes
817135446Strhodes	mtx_lock(&Giant);
818186462Sdougb	kthread_exit(0);
819135446Strhodes}
820135446Strhodes
821135446Strhodes/*
822135446Strhodes * Process completed commands.
823135446Strhodes */
824135446Strhodesstatic void
825135446Strhodesaac_complete(void *context, int pending)
826135446Strhodes{
827135446Strhodes	struct aac_softc *sc;
828135446Strhodes	struct aac_command *cm;
829224092Sdougb	struct aac_fib *fib;
830135446Strhodes	u_int32_t fib_size;
831135446Strhodes
832135446Strhodes	debug_called(2);
833135446Strhodes
834135446Strhodes	sc = (struct aac_softc *)context;
835186462Sdougb
836135446Strhodes	AAC_LOCK_ACQUIRE(&sc->aac_io_lock);
837135446Strhodes
838135446Strhodes	/* pull completed commands off the queue */
839135446Strhodes	for (;;) {
840170222Sdougb		/* look for completed FIBs on our queue */
841135446Strhodes		if (aac_dequeue_fib(sc, AAC_HOST_NORM_RESP_QUEUE, &fib_size,
842135446Strhodes				    &fib))
843135446Strhodes			break;	/* nothing to do */
844165071Sdougb
845186462Sdougb		/* get the command, unmap and hand off for processing */
846186462Sdougb		cm = sc->aac_commands + fib->Header.SenderData;
847135446Strhodes		if (cm == NULL) {
848225361Sdougb			AAC_PRINT_FIB(sc, fib);
849135446Strhodes			break;
850135446Strhodes		}
851135446Strhodes
852165071Sdougb		aac_remove_busy(cm);
853186462Sdougb		aac_unmap_command(cm);
854135446Strhodes		cm->cm_flags |= AAC_CMD_COMPLETED;
855135446Strhodes
856135446Strhodes		/* is there a completion handler? */
857135446Strhodes		if (cm->cm_complete != NULL) {
858135446Strhodes			cm->cm_complete(cm);
859135446Strhodes		} else {
860135446Strhodes			/* assume that someone is sleeping on this command */
861135446Strhodes			wakeup(cm);
862135446Strhodes		}
863135446Strhodes	}
864135446Strhodes
865135446Strhodes	/* see if we can start some more I/O */
866135446Strhodes	sc->flags &= ~AAC_QUEUE_FRZN;
867135446Strhodes	aac_startio(sc);
868135446Strhodes
869135446Strhodes	AAC_LOCK_RELEASE(&sc->aac_io_lock);
870135446Strhodes}
871135446Strhodes
872135446Strhodes/*
873135446Strhodes * Handle a bio submitted from a disk device.
874135446Strhodes */
875135446Strhodesvoid
876135446Strhodesaac_submit_bio(struct bio *bp)
877135446Strhodes{
878135446Strhodes	struct aac_disk *ad;
879135446Strhodes	struct aac_softc *sc;
880135446Strhodes
881135446Strhodes	debug_called(2);
882135446Strhodes
883135446Strhodes	ad = (struct aac_disk *)bp->bio_disk->d_drv1;
884135446Strhodes	sc = ad->ad_controller;
885135446Strhodes
886135446Strhodes	/* queue the BIO and try to get some work done */
887135446Strhodes	aac_enqueue_bio(sc, bp);
888135446Strhodes	aac_startio(sc);
889135446Strhodes}
890135446Strhodes
891135446Strhodes/*
892135446Strhodes * Get a bio and build a command to go with it.
893135446Strhodes */
894135446Strhodesstatic int
895135446Strhodesaac_bio_command(struct aac_softc *sc, struct aac_command **cmp)
896135446Strhodes{
897135446Strhodes	struct aac_command *cm;
898135446Strhodes	struct aac_fib *fib;
899135446Strhodes	struct aac_disk *ad;
900186462Sdougb	struct bio *bp;
901186462Sdougb
902186462Sdougb	debug_called(2);
903186462Sdougb
904180477Sdougb	/* get the resources we will need */
905186462Sdougb	cm = NULL;
906186462Sdougb	bp = NULL;
907186462Sdougb	if (aac_alloc_command(sc, &cm))	/* get a command */
908186462Sdougb		goto fail;
909186462Sdougb	if ((bp = aac_dequeue_bio(sc)) == NULL)
910186462Sdougb		goto fail;
911186462Sdougb
912180477Sdougb	/* fill out the command */
913180477Sdougb	cm->cm_data = (void *)bp->bio_data;
914135446Strhodes	cm->cm_datalen = bp->bio_bcount;
915135446Strhodes	cm->cm_complete = aac_bio_complete;
916135446Strhodes	cm->cm_private = bp;
917135446Strhodes	cm->cm_timestamp = time_second;
918135446Strhodes	cm->cm_queue = AAC_ADAP_NORM_CMD_QUEUE;
919135446Strhodes
920135446Strhodes	/* build the FIB */
921135446Strhodes	fib = cm->cm_fib;
922135446Strhodes	fib->Header.Size = sizeof(struct aac_fib_header);
923186462Sdougb	fib->Header.XferState =
924135446Strhodes		AAC_FIBSTATE_HOSTOWNED   |
925135446Strhodes		AAC_FIBSTATE_INITIALISED |
926135446Strhodes		AAC_FIBSTATE_EMPTY	 |
927135446Strhodes		AAC_FIBSTATE_FROMHOST	 |
928135446Strhodes		AAC_FIBSTATE_REXPECTED   |
929135446Strhodes		AAC_FIBSTATE_NORM	 |
930135446Strhodes		AAC_FIBSTATE_ASYNC	 |
931135446Strhodes		AAC_FIBSTATE_FAST_RESPONSE;
932135446Strhodes
933135446Strhodes	/* build the read/write request */
934135446Strhodes	ad = (struct aac_disk *)bp->bio_disk->d_drv1;
935135446Strhodes
936135446Strhodes	if ((sc->flags & AAC_FLAGS_SG_64BIT) == 0) {
937135446Strhodes		fib->Header.Command = ContainerCommand;
938135446Strhodes		if (bp->bio_cmd == BIO_READ) {
939135446Strhodes			struct aac_blockread *br;
940135446Strhodes			br = (struct aac_blockread *)&fib->data[0];
941135446Strhodes			br->Command = VM_CtBlockRead;
942135446Strhodes			br->ContainerId = ad->ad_container->co_mntobj.ObjectId;
943135446Strhodes			br->BlockNumber = bp->bio_pblkno;
944135446Strhodes			br->ByteCount = bp->bio_bcount;
945135446Strhodes			fib->Header.Size += sizeof(struct aac_blockread);
946135446Strhodes			cm->cm_sgtable = &br->SgMap;
947135446Strhodes			cm->cm_flags |= AAC_CMD_DATAIN;
948135446Strhodes		} else {
949135446Strhodes			struct aac_blockwrite *bw;
950135446Strhodes			bw = (struct aac_blockwrite *)&fib->data[0];
951135446Strhodes			bw->Command = VM_CtBlockWrite;
952135446Strhodes			bw->ContainerId = ad->ad_container->co_mntobj.ObjectId;
953165071Sdougb			bw->BlockNumber = bp->bio_pblkno;
954135446Strhodes			bw->ByteCount = bp->bio_bcount;
955135446Strhodes			bw->Stable = CUNSTABLE;
956165071Sdougb			fib->Header.Size += sizeof(struct aac_blockwrite);
957135446Strhodes			cm->cm_flags |= AAC_CMD_DATAOUT;
958135446Strhodes			cm->cm_sgtable = &bw->SgMap;
959135446Strhodes		}
960135446Strhodes	} else {
961135446Strhodes		fib->Header.Command = ContainerCommand64;
962143731Sdougb		if (bp->bio_cmd == BIO_READ) {
963135446Strhodes			struct aac_blockread64 *br;
964135446Strhodes			br = (struct aac_blockread64 *)&fib->data[0];
965135446Strhodes			br->Command = VM_CtHostRead64;
966135446Strhodes			br->ContainerId = ad->ad_container->co_mntobj.ObjectId;
967135446Strhodes			br->SectorCount = bp->bio_bcount / AAC_BLOCK_SIZE;
968135446Strhodes			br->BlockNumber = bp->bio_pblkno;
969135446Strhodes			br->Pad = 0;
970135446Strhodes			br->Flags = 0;
971135446Strhodes			fib->Header.Size += sizeof(struct aac_blockread64);
972135446Strhodes			cm->cm_flags |= AAC_CMD_DATAOUT;
973135446Strhodes			(struct aac_sg_table64 *)cm->cm_sgtable = &br->SgMap64;
974135446Strhodes		} else {
975186462Sdougb			struct aac_blockwrite64 *bw;
976135446Strhodes			bw = (struct aac_blockwrite64 *)&fib->data[0];
977135446Strhodes			bw->Command = VM_CtHostWrite64;
978135446Strhodes			bw->ContainerId = ad->ad_container->co_mntobj.ObjectId;
979143731Sdougb			bw->SectorCount = bp->bio_bcount / AAC_BLOCK_SIZE;
980135446Strhodes			bw->BlockNumber = bp->bio_pblkno;
981135446Strhodes			bw->Pad = 0;
982135446Strhodes			bw->Flags = 0;
983135446Strhodes			fib->Header.Size += sizeof(struct aac_blockwrite64);
984224092Sdougb			cm->cm_flags |= AAC_CMD_DATAIN;
985135446Strhodes			(struct aac_sg_table64 *)cm->cm_sgtable = &bw->SgMap64;
986135446Strhodes		}
987135446Strhodes	}
988135446Strhodes
989135446Strhodes	*cmp = cm;
990135446Strhodes	return(0);
991135446Strhodes
992135446Strhodesfail:
993135446Strhodes	if (bp != NULL)
994135446Strhodes		aac_enqueue_bio(sc, bp);
995135446Strhodes	if (cm != NULL)
996135446Strhodes		aac_release_command(cm);
997135446Strhodes	return(ENOMEM);
998135446Strhodes}
999135446Strhodes
1000143731Sdougb/*
1001143731Sdougb * Handle a bio-instigated command that has been completed.
1002143731Sdougb */
1003165071Sdougbstatic void
1004143731Sdougbaac_bio_complete(struct aac_command *cm)
1005143731Sdougb{
1006143731Sdougb	struct aac_blockread_response *brr;
1007143731Sdougb	struct aac_blockwrite_response *bwr;
1008143731Sdougb	struct bio *bp;
1009143731Sdougb	AAC_FSAStatus status;
1010143731Sdougb
1011143731Sdougb	/* fetch relevant status and then release the command */
1012135446Strhodes	bp = (struct bio *)cm->cm_private;
1013135446Strhodes	if (bp->bio_cmd == BIO_READ) {
1014135446Strhodes		brr = (struct aac_blockread_response *)&cm->cm_fib->data[0];
1015135446Strhodes		status = brr->Status;
1016135446Strhodes	} else {
1017165071Sdougb		bwr = (struct aac_blockwrite_response *)&cm->cm_fib->data[0];
1018135446Strhodes		status = bwr->Status;
1019135446Strhodes	}
1020165071Sdougb	aac_release_command(cm);
1021165071Sdougb
1022135446Strhodes	/* fix up the bio based on status */
1023170222Sdougb	if (status == ST_OK) {
1024135446Strhodes		bp->bio_resid = 0;
1025170222Sdougb	} else {
1026135446Strhodes		bp->bio_error = EIO;
1027135446Strhodes		bp->bio_flags |= BIO_ERROR;
1028186462Sdougb		/* pass an error string out to the disk layer */
1029135446Strhodes		bp->bio_driver1 = aac_describe_code(aac_command_status_table,
1030135446Strhodes						    status);
1031135446Strhodes	}
1032135446Strhodes	aac_biodone(bp);
1033135446Strhodes}
1034135446Strhodes
1035135446Strhodes/*
1036135446Strhodes * Submit a command to the controller, return when it completes.
1037135446Strhodes * XXX This is very dangerous!  If the card has gone out to lunch, we could
1038135446Strhodes *     be stuck here forever.  At the same time, signals are not caught
1039135446Strhodes *     because there is a risk that a signal could wakeup the tsleep before
1040135446Strhodes *     the card has a chance to complete the command.  The passed in timeout
1041135446Strhodes *     is ignored for the same reason.  Since there is no way to cancel a
1042135446Strhodes *     command in progress, we should probably create a 'dead' queue where
1043135446Strhodes *     commands go that have been interrupted/timed-out/etc, that keeps them
1044135446Strhodes *     out of the free pool.  That way, if the card is just slow, it won't
1045135446Strhodes *     spam the memory of a command that has been recycled.
1046135446Strhodes */
1047135446Strhodesstatic int
1048193149Sdougbaac_wait_command(struct aac_command *cm, int timeout)
1049193149Sdougb{
1050193149Sdougb	struct aac_softc *sc;
1051193149Sdougb	int error = 0;
1052193149Sdougb
1053135446Strhodes	debug_called(2);
1054135446Strhodes
1055135446Strhodes	sc = cm->cm_sc;
1056135446Strhodes
1057135446Strhodes	/* Put the command on the ready queue and get things going */
1058170222Sdougb	cm->cm_queue = AAC_ADAP_NORM_CMD_QUEUE;
1059170222Sdougb	aac_enqueue_ready(cm);
1060170222Sdougb	aac_startio(sc);
1061170222Sdougb	while (!(cm->cm_flags & AAC_CMD_COMPLETED) && (error != EWOULDBLOCK)) {
1062170222Sdougb		error = msleep(cm, &sc->aac_io_lock, PRIBIO, "aacwait", 0);
1063170222Sdougb	}
1064170222Sdougb	return(error);
1065170222Sdougb}
1066170222Sdougb
1067170222Sdougb/*
1068170222Sdougb *Command Buffer Management
1069170222Sdougb */
1070170222Sdougb
1071170222Sdougb/*
1072170222Sdougb * Allocate a command.
1073170222Sdougb */
1074170222Sdougbint
1075170222Sdougbaac_alloc_command(struct aac_softc *sc, struct aac_command **cmp)
1076170222Sdougb{
1077170222Sdougb	struct aac_command *cm;
1078170222Sdougb
1079170222Sdougb	debug_called(3);
1080135446Strhodes
1081135446Strhodes	if ((cm = aac_dequeue_free(sc)) == NULL) {
1082135446Strhodes		if (sc->total_fibs < sc->aac_max_fibs) {
1083135446Strhodes			sc->aifflags |= AAC_AIFFLAGS_ALLOCFIBS;
1084135446Strhodes			wakeup(sc->aifthread);
1085135446Strhodes		}
1086135446Strhodes		return (EBUSY);
1087135446Strhodes	}
1088135446Strhodes
1089135446Strhodes	*cmp = cm;
1090135446Strhodes	return(0);
1091135446Strhodes}
1092135446Strhodes
1093135446Strhodes/*
1094135446Strhodes * Release a command back to the freelist.
1095135446Strhodes */
1096135446Strhodesvoid
1097135446Strhodesaac_release_command(struct aac_command *cm)
1098135446Strhodes{
1099135446Strhodes	debug_called(3);
1100135446Strhodes
1101135446Strhodes	/* (re)initialise the command/FIB */
1102135446Strhodes	cm->cm_sgtable = NULL;
1103135446Strhodes	cm->cm_flags = 0;
1104135446Strhodes	cm->cm_complete = NULL;
1105135446Strhodes	cm->cm_private = NULL;
1106135446Strhodes	cm->cm_fib->Header.XferState = AAC_FIBSTATE_EMPTY;
1107170222Sdougb	cm->cm_fib->Header.StructType = AAC_FIBTYPE_TFIB;
1108135446Strhodes	cm->cm_fib->Header.Flags = 0;
1109135446Strhodes	cm->cm_fib->Header.SenderSize = sizeof(struct aac_fib);
1110135446Strhodes
1111135446Strhodes	/*
1112135446Strhodes	 * These are duplicated in aac_start to cover the case where an
1113135446Strhodes	 * intermediate stage may have destroyed them.  They're left
1114135446Strhodes	 * initialised here for debugging purposes only.
1115135446Strhodes	 */
1116170222Sdougb	cm->cm_fib->Header.ReceiverFibAddress = (u_int32_t)cm->cm_fibphys;
1117135446Strhodes	cm->cm_fib->Header.SenderData = 0;
1118170222Sdougb
1119170222Sdougb	aac_enqueue_free(cm);
1120170222Sdougb}
1121170222Sdougb
1122170222Sdougb/*
1123170222Sdougb * Map helper for command/FIB allocation.
1124170222Sdougb */
1125170222Sdougbstatic void
1126170222Sdougbaac_map_command_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error)
1127170222Sdougb{
1128170222Sdougb	uint32_t	*fibphys;
1129170222Sdougb
1130170222Sdougb	fibphys = (uint32_t *)arg;
1131170222Sdougb
1132170222Sdougb	debug_called(3);
1133170222Sdougb
1134170222Sdougb	*fibphys = segs[0].ds_addr;
1135170222Sdougb}
1136170222Sdougb
1137170222Sdougb/*
1138170222Sdougb * Allocate and initialise commands/FIBs for this adapter.
1139170222Sdougb */
1140170222Sdougbstatic int
1141170222Sdougbaac_alloc_commands(struct aac_softc *sc)
1142170222Sdougb{
1143170222Sdougb	struct aac_command *cm;
1144170222Sdougb	struct aac_fibmap *fm;
1145135446Strhodes	uint32_t fibphys;
1146135446Strhodes	int i, error;
1147135446Strhodes
1148135446Strhodes	debug_called(2);
1149135446Strhodes
1150135446Strhodes	if (sc->total_fibs + AAC_FIB_COUNT > sc->aac_max_fibs)
1151135446Strhodes		return (ENOMEM);
1152135446Strhodes
1153135446Strhodes	fm = malloc(sizeof(struct aac_fibmap), M_AACBUF, M_NOWAIT|M_ZERO);
1154165071Sdougb	if (fm == NULL)
1155135446Strhodes		return (ENOMEM);
1156165071Sdougb
1157165071Sdougb	/* allocate the FIBs in DMAable memory and load them */
1158135446Strhodes	if (bus_dmamem_alloc(sc->aac_fib_dmat, (void **)&fm->aac_fibs,
1159135446Strhodes			     BUS_DMA_NOWAIT, &fm->aac_fibmap)) {
1160135446Strhodes		device_printf(sc->aac_dev,
1161135446Strhodes			      "Not enough contiguous memory available.\n");
1162135446Strhodes		free(fm, M_AACBUF);
1163135446Strhodes		return (ENOMEM);
1164135446Strhodes	}
1165135446Strhodes
1166135446Strhodes	/* Ignore errors since this doesn't bounce */
1167135446Strhodes	(void)bus_dmamap_load(sc->aac_fib_dmat, fm->aac_fibmap, fm->aac_fibs,
1168224092Sdougb			      AAC_FIB_COUNT * sizeof(struct aac_fib),
1169135446Strhodes			      aac_map_command_helper, &fibphys, 0);
1170135446Strhodes
1171135446Strhodes	/* initialise constant fields in the command structure */
1172135446Strhodes	AAC_LOCK_ACQUIRE(&sc->aac_io_lock);
1173135446Strhodes	bzero(fm->aac_fibs, AAC_FIB_COUNT * sizeof(struct aac_fib));
1174135446Strhodes	for (i = 0; i < AAC_FIB_COUNT; i++) {
1175135446Strhodes		cm = sc->aac_commands + sc->total_fibs;
1176135446Strhodes		fm->aac_commands = cm;
1177135446Strhodes		cm->cm_sc = sc;
1178165071Sdougb		cm->cm_fib = fm->aac_fibs + i;
1179135446Strhodes		cm->cm_fibphys = fibphys + (i * sizeof(struct aac_fib));
1180135446Strhodes		cm->cm_index = sc->total_fibs;
1181135446Strhodes
1182135446Strhodes		if ((error = bus_dmamap_create(sc->aac_buffer_dmat, 0,
1183135446Strhodes					       &cm->cm_datamap)) == 0)
1184135446Strhodes			aac_release_command(cm);
1185135446Strhodes		else
1186135446Strhodes			break;
1187135446Strhodes		sc->total_fibs++;
1188135446Strhodes	}
1189135446Strhodes
1190135446Strhodes	if (i > 0) {
1191135446Strhodes		TAILQ_INSERT_TAIL(&sc->aac_fibmap_tqh, fm, fm_link);
1192135446Strhodes		debug(1, "total_fibs= %d\n", sc->total_fibs);
1193135446Strhodes		AAC_LOCK_RELEASE(&sc->aac_io_lock);
1194135446Strhodes		return (0);
1195135446Strhodes	}
1196135446Strhodes
1197135446Strhodes	AAC_LOCK_RELEASE(&sc->aac_io_lock);
1198135446Strhodes	bus_dmamap_unload(sc->aac_fib_dmat, fm->aac_fibmap);
1199170222Sdougb	bus_dmamem_free(sc->aac_fib_dmat, fm->aac_fibs, fm->aac_fibmap);
1200170222Sdougb	free(fm, M_AACBUF);
1201170222Sdougb	return (ENOMEM);
1202170222Sdougb}
1203170222Sdougb
1204170222Sdougb/*
1205170222Sdougb * Free FIBs owned by this adapter.
1206170222Sdougb */
1207170222Sdougbstatic void
1208170222Sdougbaac_free_commands(struct aac_softc *sc)
1209170222Sdougb{
1210170222Sdougb	struct aac_fibmap *fm;
1211186462Sdougb	struct aac_command *cm;
1212170222Sdougb	int i;
1213170222Sdougb
1214170222Sdougb	debug_called(1);
1215170222Sdougb
1216170222Sdougb	while ((fm = TAILQ_FIRST(&sc->aac_fibmap_tqh)) != NULL) {
1217170222Sdougb
1218170222Sdougb		TAILQ_REMOVE(&sc->aac_fibmap_tqh, fm, fm_link);
1219170222Sdougb		/*
1220170222Sdougb		 * We check against total_fibs to handle partially
1221224092Sdougb		 * allocated blocks.
1222170222Sdougb		 */
1223170222Sdougb		for (i = 0; i < AAC_FIB_COUNT && sc->total_fibs--; i++) {
1224170222Sdougb			cm = fm->aac_commands + i;
1225170222Sdougb			bus_dmamap_destroy(sc->aac_buffer_dmat, cm->cm_datamap);
1226170222Sdougb		}
1227170222Sdougb		bus_dmamap_unload(sc->aac_fib_dmat, fm->aac_fibmap);
1228170222Sdougb		bus_dmamem_free(sc->aac_fib_dmat, fm->aac_fibs, fm->aac_fibmap);
1229170222Sdougb		free(fm, M_AACBUF);
1230170222Sdougb	}
1231170222Sdougb}
1232170222Sdougb
1233170222Sdougb/*
1234170222Sdougb * Command-mapping helper function - populate this command's s/g table.
1235170222Sdougb */
1236170222Sdougbstatic void
1237170222Sdougbaac_map_command_sg(void *arg, bus_dma_segment_t *segs, int nseg, int error)
1238170222Sdougb{
1239170222Sdougb	struct aac_softc *sc;
1240170222Sdougb	struct aac_command *cm;
1241170222Sdougb	struct aac_fib *fib;
1242170222Sdougb	int i;
1243170222Sdougb
1244170222Sdougb	debug_called(3);
1245170222Sdougb
1246170222Sdougb	cm = (struct aac_command *)arg;
1247170222Sdougb	sc = cm->cm_sc;
1248170222Sdougb	fib = cm->cm_fib;
1249170222Sdougb
1250170222Sdougb	/* copy into the FIB */
1251170222Sdougb	if (cm->cm_sgtable != NULL) {
1252170222Sdougb		if ((cm->cm_sc->flags & AAC_FLAGS_SG_64BIT) == 0) {
1253170222Sdougb			struct aac_sg_table *sg;
1254170222Sdougb			sg = cm->cm_sgtable;
1255170222Sdougb			sg->SgCount = nseg;
1256170222Sdougb			for (i = 0; i < nseg; i++) {
1257170222Sdougb				sg->SgEntry[i].SgAddress = segs[i].ds_addr;
1258170222Sdougb				sg->SgEntry[i].SgByteCount = segs[i].ds_len;
1259170222Sdougb			}
1260193149Sdougb			/* update the FIB size for the s/g count */
1261193149Sdougb			fib->Header.Size += nseg * sizeof(struct aac_sg_entry);
1262193149Sdougb		} else {
1263193149Sdougb			struct aac_sg_table64 *sg;
1264170222Sdougb			sg = (struct aac_sg_table64 *)cm->cm_sgtable;
1265193149Sdougb			sg->SgCount = nseg;
1266193149Sdougb			for (i = 0; i < nseg; i++) {
1267193149Sdougb				sg->SgEntry64[i].SgAddress = segs[i].ds_addr;
1268193149Sdougb				sg->SgEntry64[i].SgByteCount = segs[i].ds_len;
1269193149Sdougb			}
1270193149Sdougb			/* update the FIB size for the s/g count */
1271193149Sdougb			fib->Header.Size += nseg*sizeof(struct aac_sg_entry64);
1272193149Sdougb		}
1273193149Sdougb	}
1274193149Sdougb
1275193149Sdougb	/* Fix up the address values in the FIB.  Use the command array index
1276193149Sdougb	 * instead of a pointer since these fields are only 32 bits.  Shift
1277193149Sdougb	 * the SenderFibAddress over to make room for the fast response bit.
1278193149Sdougb	 */
1279224092Sdougb	cm->cm_fib->Header.SenderFibAddress = (cm->cm_index << 1);
1280224092Sdougb	cm->cm_fib->Header.ReceiverFibAddress = cm->cm_fibphys;
1281224092Sdougb
1282224092Sdougb	/* save a pointer to the command for speedy reverse-lookup */
1283224092Sdougb	cm->cm_fib->Header.SenderData = cm->cm_index;
1284224092Sdougb
1285224092Sdougb	if (cm->cm_flags & AAC_CMD_DATAIN)
1286224092Sdougb		bus_dmamap_sync(sc->aac_buffer_dmat, cm->cm_datamap,
1287224092Sdougb				BUS_DMASYNC_PREREAD);
1288224092Sdougb	if (cm->cm_flags & AAC_CMD_DATAOUT)
1289224092Sdougb		bus_dmamap_sync(sc->aac_buffer_dmat, cm->cm_datamap,
1290224092Sdougb				BUS_DMASYNC_PREWRITE);
1291224092Sdougb	cm->cm_flags |= AAC_CMD_MAPPED;
1292224092Sdougb
1293193149Sdougb	/* put the FIB on the outbound queue */
1294193149Sdougb	if (aac_enqueue_fib(sc, cm->cm_queue, cm) == EBUSY) {
1295193149Sdougb		aac_remove_busy(cm);
1296193149Sdougb		aac_unmap_command(cm);
1297193149Sdougb		aac_requeue_ready(cm);
1298193149Sdougb	}
1299193149Sdougb
1300193149Sdougb	return;
1301193149Sdougb}
1302193149Sdougb
1303193149Sdougb/*
1304193149Sdougb * Unmap a command from controller-visible space.
1305193149Sdougb */
1306193149Sdougbstatic void
1307193149Sdougbaac_unmap_command(struct aac_command *cm)
1308193149Sdougb{
1309193149Sdougb	struct aac_softc *sc;
1310224092Sdougb
1311224092Sdougb	debug_called(2);
1312224092Sdougb
1313224092Sdougb	sc = cm->cm_sc;
1314224092Sdougb
1315224092Sdougb	if (!(cm->cm_flags & AAC_CMD_MAPPED))
1316224092Sdougb		return;
1317224092Sdougb
1318224092Sdougb	if (cm->cm_datalen != 0) {
1319224092Sdougb		if (cm->cm_flags & AAC_CMD_DATAIN)
1320224092Sdougb			bus_dmamap_sync(sc->aac_buffer_dmat, cm->cm_datamap,
1321224092Sdougb					BUS_DMASYNC_POSTREAD);
1322224092Sdougb		if (cm->cm_flags & AAC_CMD_DATAOUT)
1323224092Sdougb			bus_dmamap_sync(sc->aac_buffer_dmat, cm->cm_datamap,
1324224092Sdougb					BUS_DMASYNC_POSTWRITE);
1325224092Sdougb
1326224092Sdougb		bus_dmamap_unload(sc->aac_buffer_dmat, cm->cm_datamap);
1327224092Sdougb	}
1328224092Sdougb	cm->cm_flags &= ~AAC_CMD_MAPPED;
1329224092Sdougb}
1330224092Sdougb
1331224092Sdougb/*
1332224092Sdougb * Hardware Interface
1333224092Sdougb */
1334224092Sdougb
1335224092Sdougb/*
1336135446Strhodes * Initialise the adapter.
1337224092Sdougb */
1338224092Sdougbstatic void
1339224092Sdougbaac_common_map(void *arg, bus_dma_segment_t *segs, int nseg, int error)
1340224092Sdougb{
1341224092Sdougb	struct aac_softc *sc;
1342224092Sdougb
1343224092Sdougb	debug_called(1);
1344224092Sdougb
1345224092Sdougb	sc = (struct aac_softc *)arg;
1346224092Sdougb
1347224092Sdougb	sc->aac_common_busaddr = segs[0].ds_addr;
1348224092Sdougb}
1349224092Sdougb
1350224092Sdougbstatic int
1351224092Sdougbaac_check_firmware(struct aac_softc *sc)
1352224092Sdougb{
1353224092Sdougb	u_int32_t major, minor, options;
1354224092Sdougb
1355224092Sdougb	debug_called(1);
1356224092Sdougb
1357224092Sdougb	/*
1358224092Sdougb	 * Retrieve the firmware version numbers.  Dell PERC2/QC cards with
1359224092Sdougb	 * firmware version 1.x are not compatible with this driver.
1360224092Sdougb	 */
1361236374Sdougb	if (sc->flags & AAC_FLAGS_PERC2QC) {
1362224092Sdougb		if (aac_sync_command(sc, AAC_MONKER_GETKERNVER, 0, 0, 0, 0,
1363224092Sdougb				     NULL)) {
1364224092Sdougb			device_printf(sc->aac_dev,
1365224092Sdougb				      "Error reading firmware version\n");
1366224092Sdougb			return (EIO);
1367224092Sdougb		}
1368224092Sdougb
1369224092Sdougb		/* These numbers are stored as ASCII! */
1370224092Sdougb		major = (AAC_GET_MAILBOX(sc, 1) & 0xff) - 0x30;
1371224092Sdougb		minor = (AAC_GET_MAILBOX(sc, 2) & 0xff) - 0x30;
1372224092Sdougb		if (major == 1) {
1373224092Sdougb			device_printf(sc->aac_dev,
1374224092Sdougb			    "Firmware version %d.%d is not supported.\n",
1375224092Sdougb			    major, minor);
1376224092Sdougb			return (EINVAL);
1377224092Sdougb		}
1378224092Sdougb	}
1379224092Sdougb
1380224092Sdougb	/*
1381224092Sdougb	 * Retrieve the capabilities/supported options word so we know what
1382224092Sdougb	 * work-arounds to enable.
1383224092Sdougb	 */
1384224092Sdougb	if (aac_sync_command(sc, AAC_MONKER_GETINFO, 0, 0, 0, 0, NULL)) {
1385224092Sdougb		device_printf(sc->aac_dev, "RequestAdapterInfo failed\n");
1386224092Sdougb		return (EIO);
1387224092Sdougb	}
1388224092Sdougb	options = AAC_GET_MAILBOX(sc, 1);
1389224092Sdougb	sc->supported_options = options;
1390224092Sdougb
1391224092Sdougb	if ((options & AAC_SUPPORTED_4GB_WINDOW) != 0 &&
1392224092Sdougb	    (sc->flags & AAC_FLAGS_NO4GB) == 0)
1393224092Sdougb		sc->flags |= AAC_FLAGS_4GB_WINDOW;
1394224092Sdougb	if (options & AAC_SUPPORTED_NONDASD)
1395224092Sdougb		sc->flags |= AAC_FLAGS_ENABLE_CAM;
1396224092Sdougb	if ((options & AAC_SUPPORTED_SGMAP_HOST64) != 0
1397224092Sdougb	     && (sizeof(bus_addr_t) > 4)) {
1398224092Sdougb		device_printf(sc->aac_dev, "Enabling 64-bit address support\n");
1399224092Sdougb		sc->flags |= AAC_FLAGS_SG_64BIT;
1400224092Sdougb	}
1401224092Sdougb
1402224092Sdougb	/* Check for broken hardware that does a lower number of commands */
1403224092Sdougb	if ((sc->flags & AAC_FLAGS_256FIBS) == 0)
1404224092Sdougb		sc->aac_max_fibs = AAC_MAX_FIBS;
1405224092Sdougb	else
1406224092Sdougb		sc->aac_max_fibs = 256;
1407224092Sdougb
1408224092Sdougb	return (0);
1409224092Sdougb}
1410224092Sdougb
1411224092Sdougbstatic int
1412224092Sdougbaac_init(struct aac_softc *sc)
1413224092Sdougb{
1414224092Sdougb	struct aac_adapter_init	*ip;
1415224092Sdougb	time_t then;
1416224092Sdougb	u_int32_t code, qoffset;
1417224092Sdougb	int error;
1418224092Sdougb
1419224092Sdougb	debug_called(1);
1420224092Sdougb
1421224092Sdougb	/*
1422224092Sdougb	 * First wait for the adapter to come ready.
1423224092Sdougb	 */
1424224092Sdougb	then = time_second;
1425224092Sdougb	do {
1426224092Sdougb		code = AAC_GET_FWSTATUS(sc);
1427224092Sdougb		if (code & AAC_SELF_TEST_FAILED) {
1428224092Sdougb			device_printf(sc->aac_dev, "FATAL: selftest failed\n");
1429224092Sdougb			return(ENXIO);
1430224092Sdougb		}
1431224092Sdougb		if (code & AAC_KERNEL_PANIC) {
1432224092Sdougb			device_printf(sc->aac_dev,
1433224092Sdougb				      "FATAL: controller kernel panic\n");
1434224092Sdougb			return(ENXIO);
1435224092Sdougb		}
1436224092Sdougb		if (time_second > (then + AAC_BOOT_TIMEOUT)) {
1437224092Sdougb			device_printf(sc->aac_dev,
1438224092Sdougb				      "FATAL: controller not coming ready, "
1439224092Sdougb					   "status %x\n", code);
1440224092Sdougb			return(ENXIO);
1441224092Sdougb		}
1442224092Sdougb	} while (!(code & AAC_UP_AND_RUNNING));
1443224092Sdougb
1444224092Sdougb	error = ENOMEM;
1445224092Sdougb	/*
1446224092Sdougb	 * Create DMA tag for mapping buffers into controller-addressable space.
1447224092Sdougb	 */
1448224092Sdougb	if (bus_dma_tag_create(sc->aac_parent_dmat, 	/* parent */
1449224092Sdougb			       1, 0, 			/* algnmnt, boundary */
1450224092Sdougb			       (sc->flags & AAC_FLAGS_SG_64BIT) ?
1451224092Sdougb			       BUS_SPACE_MAXADDR :
1452224092Sdougb			       BUS_SPACE_MAXADDR_32BIT,	/* lowaddr */
1453224092Sdougb			       BUS_SPACE_MAXADDR, 	/* highaddr */
1454224092Sdougb			       NULL, NULL, 		/* filter, filterarg */
1455224092Sdougb			       MAXBSIZE,		/* maxsize */
1456224092Sdougb			       AAC_MAXSGENTRIES,	/* nsegments */
1457224092Sdougb			       MAXBSIZE,		/* maxsegsize */
1458224092Sdougb			       BUS_DMA_ALLOCNOW,	/* flags */
1459224092Sdougb			       busdma_lock_mutex,	/* lockfunc */
1460224092Sdougb			       &sc->aac_io_lock,	/* lockfuncarg */
1461224092Sdougb			       &sc->aac_buffer_dmat)) {
1462224092Sdougb		device_printf(sc->aac_dev, "can't allocate buffer DMA tag\n");
1463224092Sdougb		goto out;
1464224092Sdougb	}
1465224092Sdougb
1466224092Sdougb	/*
1467224092Sdougb	 * Create DMA tag for mapping FIBs into controller-addressable space..
1468224092Sdougb	 */
1469224092Sdougb	if (bus_dma_tag_create(sc->aac_parent_dmat,	/* parent */
1470224092Sdougb			       1, 0, 			/* algnmnt, boundary */
1471224092Sdougb			       (sc->flags & AAC_FLAGS_4GB_WINDOW) ?
1472224092Sdougb			       BUS_SPACE_MAXADDR_32BIT :
1473224092Sdougb			       0x7fffffff,		/* lowaddr */
1474224092Sdougb			       BUS_SPACE_MAXADDR, 	/* highaddr */
1475224092Sdougb			       NULL, NULL, 		/* filter, filterarg */
1476224092Sdougb			       AAC_FIB_COUNT *
1477224092Sdougb			       sizeof(struct aac_fib),  /* maxsize */
1478224092Sdougb			       1,			/* nsegments */
1479224092Sdougb			       AAC_FIB_COUNT *
1480224092Sdougb			       sizeof(struct aac_fib),	/* maxsegsize */
1481224092Sdougb			       BUS_DMA_ALLOCNOW,	/* flags */
1482224092Sdougb			       NULL, NULL,		/* No locking needed */
1483224092Sdougb			       &sc->aac_fib_dmat)) {
1484224092Sdougb		device_printf(sc->aac_dev, "can't allocate FIB DMA tag\n");;
1485224092Sdougb		goto out;
1486224092Sdougb	}
1487224092Sdougb
1488224092Sdougb	/*
1489224092Sdougb	 * Create DMA tag for the common structure and allocate it.
1490224092Sdougb	 */
1491224092Sdougb	if (bus_dma_tag_create(sc->aac_parent_dmat, 	/* parent */
1492224092Sdougb			       1, 0,			/* algnmnt, boundary */
1493224092Sdougb			       (sc->flags & AAC_FLAGS_4GB_WINDOW) ?
1494224092Sdougb			       BUS_SPACE_MAXADDR_32BIT :
1495224092Sdougb			       0x7fffffff,		/* lowaddr */
1496224092Sdougb			       BUS_SPACE_MAXADDR, 	/* highaddr */
1497224092Sdougb			       NULL, NULL, 		/* filter, filterarg */
1498224092Sdougb			       8192 + sizeof(struct aac_common), /* maxsize */
1499224092Sdougb			       1,			/* nsegments */
1500224092Sdougb			       BUS_SPACE_MAXSIZE_32BIT,	/* maxsegsize */
1501224092Sdougb			       BUS_DMA_ALLOCNOW,	/* flags */
1502224092Sdougb			       NULL, NULL,		/* No locking needed */
1503224092Sdougb			       &sc->aac_common_dmat)) {
1504224092Sdougb		device_printf(sc->aac_dev,
1505224092Sdougb			      "can't allocate common structure DMA tag\n");
1506224092Sdougb		goto out;
1507224092Sdougb	}
1508224092Sdougb	if (bus_dmamem_alloc(sc->aac_common_dmat, (void **)&sc->aac_common,
1509224092Sdougb			     BUS_DMA_NOWAIT, &sc->aac_common_dmamap)) {
1510224092Sdougb		device_printf(sc->aac_dev, "can't allocate common structure\n");
1511224092Sdougb		goto out;
1512224092Sdougb	}
1513224092Sdougb
1514224092Sdougb	/*
1515224092Sdougb	 * Work around a bug in the 2120 and 2200 that cannot DMA commands
1516224092Sdougb	 * below address 8192 in physical memory.
1517224092Sdougb	 * XXX If the padding is not needed, can it be put to use instead
1518224092Sdougb	 * of ignored?
1519224092Sdougb	 */
1520224092Sdougb	(void)bus_dmamap_load(sc->aac_common_dmat, sc->aac_common_dmamap,
1521224092Sdougb			sc->aac_common, 8192 + sizeof(*sc->aac_common),
1522224092Sdougb			aac_common_map, sc, 0);
1523224092Sdougb
1524224092Sdougb	if (sc->aac_common_busaddr < 8192) {
1525224092Sdougb		(uint8_t *)sc->aac_common += 8192;
1526224092Sdougb		sc->aac_common_busaddr += 8192;
1527224092Sdougb	}
1528224092Sdougb	bzero(sc->aac_common, sizeof(*sc->aac_common));
1529224092Sdougb
1530224092Sdougb	/* Allocate some FIBs and associated command structs */
1531224092Sdougb	TAILQ_INIT(&sc->aac_fibmap_tqh);
1532224092Sdougb	sc->aac_commands = malloc(AAC_MAX_FIBS * sizeof(struct aac_command),
1533224092Sdougb				  M_AACBUF, M_WAITOK|M_ZERO);
1534224092Sdougb	while (sc->total_fibs < AAC_PREALLOCATE_FIBS) {
1535224092Sdougb		if (aac_alloc_commands(sc) != 0)
1536224092Sdougb			break;
1537224092Sdougb	}
1538224092Sdougb	if (sc->total_fibs == 0)
1539224092Sdougb		goto out;
1540224092Sdougb
1541135446Strhodes	/*
1542135446Strhodes	 * Fill in the init structure.  This tells the adapter about the
1543135446Strhodes	 * physical location of various important shared data structures.
1544135446Strhodes	 */
1545135446Strhodes	ip = &sc->aac_common->ac_init;
1546135446Strhodes	ip->InitStructRevision = AAC_INIT_STRUCT_REVISION;
1547135446Strhodes	ip->MiniPortRevision = AAC_INIT_STRUCT_MINIPORT_REVISION;
1548225361Sdougb
1549224092Sdougb	ip->AdapterFibsPhysicalAddress = sc->aac_common_busaddr +
1550224092Sdougb					 offsetof(struct aac_common, ac_fibs);
1551224092Sdougb	ip->AdapterFibsVirtualAddress = 0;
1552135446Strhodes	ip->AdapterFibsSize = AAC_ADAPTER_FIBS * sizeof(struct aac_fib);
1553165071Sdougb	ip->AdapterFibAlign = sizeof(struct aac_fib);
1554165071Sdougb
1555224092Sdougb	ip->PrintfBufferAddress = sc->aac_common_busaddr +
1556165071Sdougb				  offsetof(struct aac_common, ac_printf);
1557165071Sdougb	ip->PrintfBufferSize = AAC_PRINTF_BUFSIZE;
1558165071Sdougb
1559165071Sdougb	/*
1560165071Sdougb	 * The adapter assumes that pages are 4K in size, except on some
1561165071Sdougb 	 * broken firmware versions that do the page->byte conversion twice,
1562186462Sdougb	 * therefore 'assuming' that this value is in 16MB units (2^24).
1563186462Sdougb	 * Round up since the granularity is so high.
1564186462Sdougb	 */
1565165071Sdougb	ip->HostPhysMemPages = ctob(physmem) / AAC_PAGE_SIZE;
1566165071Sdougb	if (sc->flags & AAC_FLAGS_BROKEN_MEMMAP) {
1567165071Sdougb		ip->HostPhysMemPages =
1568135446Strhodes		    (ip->HostPhysMemPages + AAC_PAGE_SIZE) / AAC_PAGE_SIZE;
1569135446Strhodes	}
1570135446Strhodes	ip->HostElapsedSeconds = time_second;	/* reset later if invalid */
1571135446Strhodes
1572224092Sdougb	/*
1573135446Strhodes	 * Initialise FIB queues.  Note that it appears that the layout of the
1574170222Sdougb	 * indexes and the segmentation of the entries may be mandated by the
1575135446Strhodes	 * adapter, which is only told about the base of the queue index fields.
1576224092Sdougb	 *
1577135446Strhodes	 * The initial values of the indices are assumed to inform the adapter
1578225361Sdougb	 * of the sizes of the respective queues, and theoretically it could
1579135446Strhodes	 * work out the entire layout of the queue structures from this.  We
1580135446Strhodes	 * take the easy route and just lay this area out like everyone else
1581135446Strhodes	 * does.
1582224092Sdougb	 *
1583224092Sdougb	 * The Linux driver uses a much more complex scheme whereby several
1584135446Strhodes	 * header records are kept for each queue.  We use a couple of generic
1585224092Sdougb	 * list manipulation functions which 'know' the size of each list by
1586135446Strhodes	 * virtue of a table.
1587135446Strhodes	 */
1588193149Sdougb	qoffset = offsetof(struct aac_common, ac_qbuf) + AAC_QUEUE_ALIGN;
1589170222Sdougb	qoffset &= ~(AAC_QUEUE_ALIGN - 1);
1590170222Sdougb	sc->aac_queues =
1591170222Sdougb	    (struct aac_queue_table *)((uintptr_t)sc->aac_common + qoffset);
1592170222Sdougb	ip->CommHeaderAddress = sc->aac_common_busaddr + qoffset;
1593170222Sdougb
1594170222Sdougb	sc->aac_queues->qt_qindex[AAC_HOST_NORM_CMD_QUEUE][AAC_PRODUCER_INDEX] =
1595170222Sdougb		AAC_HOST_NORM_CMD_ENTRIES;
1596170222Sdougb	sc->aac_queues->qt_qindex[AAC_HOST_NORM_CMD_QUEUE][AAC_CONSUMER_INDEX] =
1597193149Sdougb		AAC_HOST_NORM_CMD_ENTRIES;
1598193149Sdougb	sc->aac_queues->qt_qindex[AAC_HOST_HIGH_CMD_QUEUE][AAC_PRODUCER_INDEX] =
1599224092Sdougb		AAC_HOST_HIGH_CMD_ENTRIES;
1600224092Sdougb	sc->aac_queues->qt_qindex[AAC_HOST_HIGH_CMD_QUEUE][AAC_CONSUMER_INDEX] =
1601224092Sdougb		AAC_HOST_HIGH_CMD_ENTRIES;
1602193149Sdougb	sc->aac_queues->qt_qindex[AAC_ADAP_NORM_CMD_QUEUE][AAC_PRODUCER_INDEX] =
1603224092Sdougb		AAC_ADAP_NORM_CMD_ENTRIES;
1604224092Sdougb	sc->aac_queues->qt_qindex[AAC_ADAP_NORM_CMD_QUEUE][AAC_CONSUMER_INDEX] =
1605225361Sdougb		AAC_ADAP_NORM_CMD_ENTRIES;
1606135446Strhodes	sc->aac_queues->qt_qindex[AAC_ADAP_HIGH_CMD_QUEUE][AAC_PRODUCER_INDEX] =
1607135446Strhodes		AAC_ADAP_HIGH_CMD_ENTRIES;
1608135446Strhodes	sc->aac_queues->qt_qindex[AAC_ADAP_HIGH_CMD_QUEUE][AAC_CONSUMER_INDEX] =
1609135446Strhodes		AAC_ADAP_HIGH_CMD_ENTRIES;
1610135446Strhodes	sc->aac_queues->qt_qindex[AAC_HOST_NORM_RESP_QUEUE][AAC_PRODUCER_INDEX]=
1611135446Strhodes		AAC_HOST_NORM_RESP_ENTRIES;
1612224092Sdougb	sc->aac_queues->qt_qindex[AAC_HOST_NORM_RESP_QUEUE][AAC_CONSUMER_INDEX]=
1613224092Sdougb		AAC_HOST_NORM_RESP_ENTRIES;
1614224092Sdougb	sc->aac_queues->qt_qindex[AAC_HOST_HIGH_RESP_QUEUE][AAC_PRODUCER_INDEX]=
1615224092Sdougb		AAC_HOST_HIGH_RESP_ENTRIES;
1616224092Sdougb	sc->aac_queues->qt_qindex[AAC_HOST_HIGH_RESP_QUEUE][AAC_CONSUMER_INDEX]=
1617135446Strhodes		AAC_HOST_HIGH_RESP_ENTRIES;
1618135446Strhodes	sc->aac_queues->qt_qindex[AAC_ADAP_NORM_RESP_QUEUE][AAC_PRODUCER_INDEX]=
1619135446Strhodes		AAC_ADAP_NORM_RESP_ENTRIES;
1620224092Sdougb	sc->aac_queues->qt_qindex[AAC_ADAP_NORM_RESP_QUEUE][AAC_CONSUMER_INDEX]=
1621224092Sdougb		AAC_ADAP_NORM_RESP_ENTRIES;
1622135446Strhodes	sc->aac_queues->qt_qindex[AAC_ADAP_HIGH_RESP_QUEUE][AAC_PRODUCER_INDEX]=
1623224092Sdougb		AAC_ADAP_HIGH_RESP_ENTRIES;
1624135446Strhodes	sc->aac_queues->qt_qindex[AAC_ADAP_HIGH_RESP_QUEUE][AAC_CONSUMER_INDEX]=
1625224092Sdougb		AAC_ADAP_HIGH_RESP_ENTRIES;
1626224092Sdougb	sc->aac_qentries[AAC_HOST_NORM_CMD_QUEUE] =
1627224092Sdougb		&sc->aac_queues->qt_HostNormCmdQueue[0];
1628135446Strhodes	sc->aac_qentries[AAC_HOST_HIGH_CMD_QUEUE] =
1629135446Strhodes		&sc->aac_queues->qt_HostHighCmdQueue[0];
1630224092Sdougb	sc->aac_qentries[AAC_ADAP_NORM_CMD_QUEUE] =
1631135446Strhodes		&sc->aac_queues->qt_AdapNormCmdQueue[0];
1632224092Sdougb	sc->aac_qentries[AAC_ADAP_HIGH_CMD_QUEUE] =
1633224092Sdougb		&sc->aac_queues->qt_AdapHighCmdQueue[0];
1634135446Strhodes	sc->aac_qentries[AAC_HOST_NORM_RESP_QUEUE] =
1635170222Sdougb		&sc->aac_queues->qt_HostNormRespQueue[0];
1636170222Sdougb	sc->aac_qentries[AAC_HOST_HIGH_RESP_QUEUE] =
1637170222Sdougb		&sc->aac_queues->qt_HostHighRespQueue[0];
1638170222Sdougb	sc->aac_qentries[AAC_ADAP_NORM_RESP_QUEUE] =
1639225361Sdougb		&sc->aac_queues->qt_AdapNormRespQueue[0];
1640170222Sdougb	sc->aac_qentries[AAC_ADAP_HIGH_RESP_QUEUE] =
1641170222Sdougb		&sc->aac_queues->qt_AdapHighRespQueue[0];
1642135446Strhodes
1643135446Strhodes	/*
1644135446Strhodes	 * Do controller-type-specific initialisation
1645135446Strhodes	 */
1646135446Strhodes	switch (sc->aac_hwif) {
1647135446Strhodes	case AAC_HWIF_I960RX:
1648135446Strhodes		AAC_SETREG4(sc, AAC_RX_ODBR, ~0);
1649170222Sdougb		break;
1650170222Sdougb	}
1651170222Sdougb
1652170222Sdougb	/*
1653170222Sdougb	 * Give the init structure to the controller.
1654170222Sdougb	 */
1655170222Sdougb	if (aac_sync_command(sc, AAC_MONKER_INITSTRUCT,
1656170222Sdougb			     sc->aac_common_busaddr +
1657170222Sdougb			     offsetof(struct aac_common, ac_init), 0, 0, 0,
1658170222Sdougb			     NULL)) {
1659170222Sdougb		device_printf(sc->aac_dev,
1660193149Sdougb			      "error establishing init structure\n");
1661170222Sdougb		error = EIO;
1662170222Sdougb		goto out;
1663170222Sdougb	}
1664170222Sdougb
1665170222Sdougb	error = 0;
1666170222Sdougbout:
1667170222Sdougb	return(error);
1668170222Sdougb}
1669170222Sdougb
1670170222Sdougb/*
1671170222Sdougb * Send a synchronous command to the controller and wait for a result.
1672170222Sdougb */
1673170222Sdougbstatic int
1674170222Sdougbaac_sync_command(struct aac_softc *sc, u_int32_t command,
1675170222Sdougb		 u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3,
1676170222Sdougb		 u_int32_t *sp)
1677170222Sdougb{
1678170222Sdougb	time_t then;
1679170222Sdougb	u_int32_t status;
1680170222Sdougb
1681170222Sdougb	debug_called(3);
1682170222Sdougb
1683170222Sdougb	/* populate the mailbox */
1684170222Sdougb	AAC_SET_MAILBOX(sc, command, arg0, arg1, arg2, arg3);
1685170222Sdougb
1686170222Sdougb	/* ensure the sync command doorbell flag is cleared */
1687170222Sdougb	AAC_CLEAR_ISTATUS(sc, AAC_DB_SYNC_COMMAND);
1688170222Sdougb
1689170222Sdougb	/* then set it to signal the adapter */
1690170222Sdougb	AAC_QNOTIFY(sc, AAC_DB_SYNC_COMMAND);
1691170222Sdougb
1692170222Sdougb	/* spin waiting for the command to complete */
1693170222Sdougb	then = time_second;
1694170222Sdougb	do {
1695224092Sdougb		if (time_second > (then + AAC_IMMEDIATE_TIMEOUT)) {
1696216175Sdougb			debug(1, "timed out");
1697216175Sdougb			return(EIO);
1698224092Sdougb		}
1699224092Sdougb	} while (!(AAC_GET_ISTATUS(sc) & AAC_DB_SYNC_COMMAND));
1700224092Sdougb
1701216175Sdougb	/* clear the completion flag */
1702216175Sdougb	AAC_CLEAR_ISTATUS(sc, AAC_DB_SYNC_COMMAND);
1703170222Sdougb
1704135446Strhodes	/* get the command status */
1705135446Strhodes	status = AAC_GET_MAILBOX(sc, 0);
1706135446Strhodes	if (sp != NULL)
1707135446Strhodes		*sp = status;
1708135446Strhodes	return(0);
1709135446Strhodes}
1710135446Strhodes
1711225361Sdougb/*
1712225361Sdougb * Grab the sync fib area.
1713225361Sdougb */
1714225361Sdougbint
1715135446Strhodesaac_alloc_sync_fib(struct aac_softc *sc, struct aac_fib **fib, int flags)
1716135446Strhodes{
1717135446Strhodes
1718135446Strhodes	/*
1719165071Sdougb	 * If the force flag is set, the system is shutting down, or in
1720135446Strhodes	 * trouble.  Ignore the mutex.
1721224092Sdougb	 */
1722135446Strhodes	if (!(flags & AAC_SYNC_LOCK_FORCE))
1723135446Strhodes		AAC_LOCK_ACQUIRE(&sc->aac_sync_lock);
1724224092Sdougb
1725224092Sdougb	*fib = &sc->aac_common->ac_sync_fib;
1726224092Sdougb
1727224092Sdougb	return (1);
1728224092Sdougb}
1729225361Sdougb
1730225361Sdougb/*
1731224092Sdougb * Release the sync fib area.
1732224092Sdougb */
1733224092Sdougbvoid
1734224092Sdougbaac_release_sync_fib(struct aac_softc *sc)
1735224092Sdougb{
1736225361Sdougb
1737225361Sdougb	AAC_LOCK_RELEASE(&sc->aac_sync_lock);
1738225361Sdougb}
1739225361Sdougb
1740225361Sdougb/*
1741225361Sdougb * Send a synchronous FIB to the controller and wait for a result.
1742225361Sdougb */
1743225361Sdougbint
1744225361Sdougbaac_sync_fib(struct aac_softc *sc, u_int32_t command, u_int32_t xferstate,
1745225361Sdougb		 struct aac_fib *fib, u_int16_t datasize)
1746225361Sdougb{
1747224092Sdougb	debug_called(3);
1748224092Sdougb
1749224092Sdougb	if (datasize > AAC_FIB_DATASIZE)
1750135446Strhodes		return(EINVAL);
1751170222Sdougb
1752170222Sdougb	/*
1753170222Sdougb	 * Set up the sync FIB
1754170222Sdougb	 */
1755170222Sdougb	fib->Header.XferState = AAC_FIBSTATE_HOSTOWNED |
1756170222Sdougb				AAC_FIBSTATE_INITIALISED |
1757170222Sdougb				AAC_FIBSTATE_EMPTY;
1758170222Sdougb	fib->Header.XferState |= xferstate;
1759170222Sdougb	fib->Header.Command = command;
1760170222Sdougb	fib->Header.StructType = AAC_FIBTYPE_TFIB;
1761170222Sdougb	fib->Header.Size = sizeof(struct aac_fib) + datasize;
1762170222Sdougb	fib->Header.SenderSize = sizeof(struct aac_fib);
1763170222Sdougb	fib->Header.SenderFibAddress = 0;	/* Not needed */
1764170222Sdougb	fib->Header.ReceiverFibAddress = sc->aac_common_busaddr +
1765170222Sdougb					 offsetof(struct aac_common,
1766170222Sdougb						  ac_sync_fib);
1767170222Sdougb
1768170222Sdougb	/*
1769186462Sdougb	 * Give the FIB to the controller, wait for a response.
1770170222Sdougb	 */
1771170222Sdougb	if (aac_sync_command(sc, AAC_MONKER_SYNCFIB,
1772170222Sdougb			     fib->Header.ReceiverFibAddress, 0, 0, 0, NULL)) {
1773170222Sdougb		debug(2, "IO error");
1774170222Sdougb		return(EIO);
1775170222Sdougb	}
1776170222Sdougb
1777170222Sdougb	return (0);
1778170222Sdougb}
1779170222Sdougb
1780170222Sdougb/*
1781170222Sdougb * Adapter-space FIB queue manipulation
1782170222Sdougb *
1783170222Sdougb * Note that the queue implementation here is a little funky; neither the PI or
1784224092Sdougb * CI will ever be zero.  This behaviour is a controller feature.
1785224092Sdougb */
1786224092Sdougbstatic struct {
1787224092Sdougb	int		size;
1788224092Sdougb	int		notify;
1789224092Sdougb} aac_qinfo[] = {
1790224092Sdougb	{AAC_HOST_NORM_CMD_ENTRIES, AAC_DB_COMMAND_NOT_FULL},
1791224092Sdougb	{AAC_HOST_HIGH_CMD_ENTRIES, 0},
1792170222Sdougb	{AAC_ADAP_NORM_CMD_ENTRIES, AAC_DB_COMMAND_READY},
1793170222Sdougb	{AAC_ADAP_HIGH_CMD_ENTRIES, 0},
1794170222Sdougb	{AAC_HOST_NORM_RESP_ENTRIES, AAC_DB_RESPONSE_NOT_FULL},
1795170222Sdougb	{AAC_HOST_HIGH_RESP_ENTRIES, 0},
1796193149Sdougb	{AAC_ADAP_NORM_RESP_ENTRIES, AAC_DB_RESPONSE_READY},
1797193149Sdougb	{AAC_ADAP_HIGH_RESP_ENTRIES, 0}
1798193149Sdougb};
1799224092Sdougb
1800224092Sdougb/*
1801224092Sdougb * Atomically insert an entry into the nominated queue, returns 0 on success or
1802224092Sdougb * EBUSY if the queue is full.
1803224092Sdougb *
1804224092Sdougb * Note: it would be more efficient to defer notifying the controller in
1805224092Sdougb *	 the case where we may be inserting several entries in rapid succession,
1806224092Sdougb *	 but implementing this usefully may be difficult (it would involve a
1807224092Sdougb *	 separate queue/notify interface).
1808224092Sdougb */
1809224092Sdougbstatic int
1810224092Sdougbaac_enqueue_fib(struct aac_softc *sc, int queue, struct aac_command *cm)
1811224092Sdougb{
1812224092Sdougb	u_int32_t pi, ci;
1813224092Sdougb	int error;
1814224092Sdougb	u_int32_t fib_size;
1815224092Sdougb	u_int32_t fib_addr;
1816224092Sdougb
1817224092Sdougb	debug_called(3);
1818224092Sdougb
1819224092Sdougb	fib_size = cm->cm_fib->Header.Size;
1820224092Sdougb	fib_addr = cm->cm_fib->Header.ReceiverFibAddress;
1821224092Sdougb
1822224092Sdougb	/* get the producer/consumer indices */
1823224092Sdougb	pi = sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX];
1824224092Sdougb	ci = sc->aac_queues->qt_qindex[queue][AAC_CONSUMER_INDEX];
1825193149Sdougb
1826193149Sdougb	/* wrap the queue? */
1827193149Sdougb	if (pi >= aac_qinfo[queue].size)
1828193149Sdougb		pi = 0;
1829193149Sdougb
1830193149Sdougb	/* check for queue full */
1831193149Sdougb	if ((pi + 1) == ci) {
1832193149Sdougb		error = EBUSY;
1833193149Sdougb		goto out;
1834193149Sdougb	}
1835193149Sdougb
1836193149Sdougb	/* populate queue entry */
1837193149Sdougb	(sc->aac_qentries[queue] + pi)->aq_fib_size = fib_size;
1838193149Sdougb	(sc->aac_qentries[queue] + pi)->aq_fib_addr = fib_addr;
1839193149Sdougb
1840193149Sdougb	/* update producer index */
1841193149Sdougb	sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX] = pi + 1;
1842193149Sdougb
1843193149Sdougb	/*
1844193149Sdougb	 * To avoid a race with its completion interrupt, place this command on
1845193149Sdougb	 * the busy queue prior to advertising it to the controller.
1846193149Sdougb	 */
1847193149Sdougb	aac_enqueue_busy(cm);
1848193149Sdougb
1849224092Sdougb	/* notify the adapter if we know how */
1850224092Sdougb	if (aac_qinfo[queue].notify != 0)
1851224092Sdougb		AAC_QNOTIFY(sc, aac_qinfo[queue].notify);
1852224092Sdougb
1853224092Sdougb	error = 0;
1854224092Sdougb
1855224092Sdougbout:
1856224092Sdougb	return(error);
1857224092Sdougb}
1858224092Sdougb
1859224092Sdougb/*
1860224092Sdougb * Atomically remove one entry from the nominated queue, returns 0 on
1861224092Sdougb * success or ENOENT if the queue is empty.
1862224092Sdougb */
1863224092Sdougbstatic int
1864224092Sdougbaac_dequeue_fib(struct aac_softc *sc, int queue, u_int32_t *fib_size,
1865224092Sdougb		struct aac_fib **fib_addr)
1866224092Sdougb{
1867224092Sdougb	u_int32_t pi, ci;
1868224092Sdougb	u_int32_t fib_index;
1869224092Sdougb	int error;
1870224092Sdougb	int notify;
1871224092Sdougb
1872224092Sdougb	debug_called(3);
1873224092Sdougb
1874224092Sdougb	/* get the producer/consumer indices */
1875224092Sdougb	pi = sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX];
1876224092Sdougb	ci = sc->aac_queues->qt_qindex[queue][AAC_CONSUMER_INDEX];
1877224092Sdougb
1878224092Sdougb	/* check for queue empty */
1879224092Sdougb	if (ci == pi) {
1880224092Sdougb		error = ENOENT;
1881224092Sdougb		goto out;
1882224092Sdougb	}
1883224092Sdougb
1884224092Sdougb	/* wrap the pi so the following test works */
1885224092Sdougb	if (pi >= aac_qinfo[queue].size)
1886224092Sdougb		pi = 0;
1887224092Sdougb
1888224092Sdougb	notify = 0;
1889224092Sdougb	if (ci == pi + 1)
1890224092Sdougb		notify++;
1891224092Sdougb
1892224092Sdougb	/* wrap the queue? */
1893224092Sdougb	if (ci >= aac_qinfo[queue].size)
1894224092Sdougb		ci = 0;
1895224092Sdougb
1896224092Sdougb	/* fetch the entry */
1897224092Sdougb	*fib_size = (sc->aac_qentries[queue] + ci)->aq_fib_size;
1898224092Sdougb
1899224092Sdougb	switch (queue) {
1900224092Sdougb	case AAC_HOST_NORM_CMD_QUEUE:
1901224092Sdougb	case AAC_HOST_HIGH_CMD_QUEUE:
1902224092Sdougb		/*
1903224092Sdougb		 * The aq_fib_addr is only 32 bits wide so it can't be counted
1904224092Sdougb		 * on to hold an address.  For AIF's, the adapter assumes
1905224092Sdougb		 * that it's giving us an address into the array of AIF fibs.
1906224092Sdougb		 * Therefore, we have to convert it to an index.
1907224092Sdougb		 */
1908224092Sdougb		fib_index = (sc->aac_qentries[queue] + ci)->aq_fib_addr /
1909224092Sdougb			sizeof(struct aac_fib);
1910224092Sdougb		*fib_addr = &sc->aac_common->ac_fibs[fib_index];
1911224092Sdougb		break;
1912224092Sdougb
1913224092Sdougb	case AAC_HOST_NORM_RESP_QUEUE:
1914224092Sdougb	case AAC_HOST_HIGH_RESP_QUEUE:
1915224092Sdougb	{
1916224092Sdougb		struct aac_command *cm;
1917224092Sdougb
1918224092Sdougb		/*
1919224092Sdougb		 * As above, an index is used instead of an actual address.
1920224092Sdougb		 * Gotta shift the index to account for the fast response
1921224092Sdougb		 * bit.  No other correction is needed since this value was
1922224092Sdougb		 * originally provided by the driver via the SenderFibAddress
1923224092Sdougb		 * field.
1924224092Sdougb		 */
1925224092Sdougb		fib_index = (sc->aac_qentries[queue] + ci)->aq_fib_addr;
1926224092Sdougb		cm = sc->aac_commands + (fib_index >> 1);
1927224092Sdougb		*fib_addr = cm->cm_fib;
1928224092Sdougb
1929224092Sdougb		/*
1930224092Sdougb		 * Is this a fast response? If it is, update the fib fields in
1931224092Sdougb		 * local memory since the whole fib isn't DMA'd back up.
1932224092Sdougb		 */
1933224092Sdougb		if (fib_index & 0x01) {
1934224092Sdougb			(*fib_addr)->Header.XferState |= AAC_FIBSTATE_DONEADAP;
1935224092Sdougb			*((u_int32_t*)((*fib_addr)->data)) = AAC_ERROR_NORMAL;
1936224092Sdougb		}
1937224092Sdougb		break;
1938224092Sdougb	}
1939224092Sdougb	default:
1940224092Sdougb		panic("Invalid queue in aac_dequeue_fib()");
1941224092Sdougb		break;
1942224092Sdougb	}
1943224092Sdougb
1944224092Sdougb	/* update consumer index */
1945224092Sdougb	sc->aac_queues->qt_qindex[queue][AAC_CONSUMER_INDEX] = ci + 1;
1946224092Sdougb
1947224092Sdougb	/* if we have made the queue un-full, notify the adapter */
1948224092Sdougb	if (notify && (aac_qinfo[queue].notify != 0))
1949224092Sdougb		AAC_QNOTIFY(sc, aac_qinfo[queue].notify);
1950224092Sdougb	error = 0;
1951224092Sdougb
1952193149Sdougbout:
1953193149Sdougb	return(error);
1954193149Sdougb}
1955193149Sdougb
1956193149Sdougb/*
1957193149Sdougb * Put our response to an Adapter Initialed Fib on the response queue
1958193149Sdougb */
1959224092Sdougbstatic int
1960224092Sdougbaac_enqueue_response(struct aac_softc *sc, int queue, struct aac_fib *fib)
1961224092Sdougb{
1962224092Sdougb	u_int32_t pi, ci;
1963224092Sdougb	int error;
1964224092Sdougb	u_int32_t fib_size;
1965224092Sdougb	u_int32_t fib_addr;
1966193149Sdougb
1967193149Sdougb	debug_called(1);
1968193149Sdougb
1969193149Sdougb	/* Tell the adapter where the FIB is */
1970193149Sdougb	fib_size = fib->Header.Size;
1971193149Sdougb	fib_addr = fib->Header.SenderFibAddress;
1972193149Sdougb	fib->Header.ReceiverFibAddress = fib_addr;
1973193149Sdougb
1974193149Sdougb	/* get the producer/consumer indices */
1975193149Sdougb	pi = sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX];
1976193149Sdougb	ci = sc->aac_queues->qt_qindex[queue][AAC_CONSUMER_INDEX];
1977193149Sdougb
1978193149Sdougb	/* wrap the queue? */
1979193149Sdougb	if (pi >= aac_qinfo[queue].size)
1980224092Sdougb		pi = 0;
1981135446Strhodes
1982224092Sdougb	/* check for queue full */
1983224092Sdougb	if ((pi + 1) == ci) {
1984224092Sdougb		error = EBUSY;
1985224092Sdougb		goto out;
1986224092Sdougb	}
1987224092Sdougb
1988224092Sdougb	/* populate queue entry */
1989224092Sdougb	(sc->aac_qentries[queue] + pi)->aq_fib_size = fib_size;
1990224092Sdougb	(sc->aac_qentries[queue] + pi)->aq_fib_addr = fib_addr;
1991224092Sdougb
1992224092Sdougb	/* update producer index */
1993193149Sdougb	sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX] = pi + 1;
1994193149Sdougb
1995224092Sdougb	/* notify the adapter if we know how */
1996224092Sdougb	if (aac_qinfo[queue].notify != 0)
1997224092Sdougb		AAC_QNOTIFY(sc, aac_qinfo[queue].notify);
1998224092Sdougb
1999224092Sdougb	error = 0;
2000224092Sdougb
2001135446Strhodesout:
2002224092Sdougb	return(error);
2003224092Sdougb}
2004224092Sdougb
2005224092Sdougb/*
2006224092Sdougb * Check for commands that have been outstanding for a suspiciously long time,
2007224092Sdougb * and complain about them.
2008224092Sdougb */
2009224092Sdougbstatic void
2010224092Sdougbaac_timeout(struct aac_softc *sc)
2011224092Sdougb{
2012224092Sdougb	struct aac_command *cm;
2013193149Sdougb	time_t deadline;
2014224092Sdougb
2015224092Sdougb	/*
2016193149Sdougb	 * Traverse the busy command list, bitch about late commands once
2017224092Sdougb	 * only.
2018224092Sdougb	 */
2019224092Sdougb	deadline = time_second - AAC_CMD_TIMEOUT;
2020193149Sdougb	TAILQ_FOREACH(cm, &sc->aac_busy, cm_link) {
2021224092Sdougb		if ((cm->cm_timestamp  < deadline)
2022224092Sdougb			/* && !(cm->cm_flags & AAC_CMD_TIMEDOUT) */) {
2023224092Sdougb			cm->cm_flags |= AAC_CMD_TIMEDOUT;
2024224092Sdougb			device_printf(sc->aac_dev,
2025224092Sdougb				      "COMMAND %p TIMEOUT AFTER %d SECONDS\n",
2026224092Sdougb				      cm, (int)(time_second-cm->cm_timestamp));
2027224092Sdougb			AAC_PRINT_FIB(sc, cm->cm_fib);
2028224092Sdougb		}
2029224092Sdougb	}
2030224092Sdougb
2031224092Sdougb	return;
2032224092Sdougb}
2033224092Sdougb
2034224092Sdougb/*
2035224092Sdougb * Interface Function Vectors
2036224092Sdougb */
2037224092Sdougb
2038224092Sdougb/*
2039224092Sdougb * Read the current firmware status word.
2040224092Sdougb */
2041224092Sdougbstatic int
2042224092Sdougbaac_sa_get_fwstatus(struct aac_softc *sc)
2043224092Sdougb{
2044224092Sdougb	debug_called(3);
2045224092Sdougb
2046224092Sdougb	return(AAC_GETREG4(sc, AAC_SA_FWSTATUS));
2047224092Sdougb}
2048224092Sdougb
2049224092Sdougbstatic int
2050224092Sdougbaac_rx_get_fwstatus(struct aac_softc *sc)
2051224092Sdougb{
2052224092Sdougb	debug_called(3);
2053224092Sdougb
2054224092Sdougb	return(AAC_GETREG4(sc, AAC_RX_FWSTATUS));
2055224092Sdougb}
2056224092Sdougb
2057224092Sdougbstatic int
2058224092Sdougbaac_fa_get_fwstatus(struct aac_softc *sc)
2059224092Sdougb{
2060224092Sdougb	int val;
2061224092Sdougb
2062224092Sdougb	debug_called(3);
2063224092Sdougb
2064224092Sdougb	val = AAC_GETREG4(sc, AAC_FA_FWSTATUS);
2065225361Sdougb	return (val);
2066225361Sdougb}
2067225361Sdougb
2068225361Sdougb/*
2069224092Sdougb * Notify the controller of a change in a given queue
2070224092Sdougb */
2071224092Sdougb
2072225361Sdougbstatic void
2073225361Sdougbaac_sa_qnotify(struct aac_softc *sc, int qbit)
2074225361Sdougb{
2075224092Sdougb	debug_called(3);
2076224092Sdougb
2077224092Sdougb	AAC_SETREG2(sc, AAC_SA_DOORBELL1_SET, qbit);
2078225361Sdougb}
2079225361Sdougb
2080224092Sdougbstatic void
2081224092Sdougbaac_rx_qnotify(struct aac_softc *sc, int qbit)
2082224092Sdougb{
2083224092Sdougb	debug_called(3);
2084224092Sdougb
2085224092Sdougb	AAC_SETREG4(sc, AAC_RX_IDBR, qbit);
2086224092Sdougb}
2087224092Sdougb
2088224092Sdougbstatic void
2089224092Sdougbaac_fa_qnotify(struct aac_softc *sc, int qbit)
2090224092Sdougb{
2091224092Sdougb	debug_called(3);
2092224092Sdougb
2093193149Sdougb	AAC_SETREG2(sc, AAC_FA_DOORBELL1, qbit);
2094224092Sdougb	AAC_FA_HACK(sc);
2095135446Strhodes}
2096135446Strhodes
2097135446Strhodes/*
2098135446Strhodes * Get the interrupt reason bits
2099135446Strhodes */
2100135446Strhodesstatic int
2101135446Strhodesaac_sa_get_istatus(struct aac_softc *sc)
2102135446Strhodes{
2103135446Strhodes	debug_called(3);
2104224092Sdougb
2105135446Strhodes	return(AAC_GETREG2(sc, AAC_SA_DOORBELL0));
2106135446Strhodes}
2107135446Strhodes
2108224092Sdougbstatic int
2109135446Strhodesaac_rx_get_istatus(struct aac_softc *sc)
2110135446Strhodes{
2111135446Strhodes	debug_called(3);
2112135446Strhodes
2113135446Strhodes	return(AAC_GETREG4(sc, AAC_RX_ODBR));
2114135446Strhodes}
2115135446Strhodes
2116135446Strhodesstatic int
2117135446Strhodesaac_fa_get_istatus(struct aac_softc *sc)
2118186462Sdougb{
2119186462Sdougb	int val;
2120186462Sdougb
2121186462Sdougb	debug_called(3);
2122186462Sdougb
2123186462Sdougb	val = AAC_GETREG2(sc, AAC_FA_DOORBELL0);
2124135446Strhodes	return (val);
2125135446Strhodes}
2126135446Strhodes
2127135446Strhodes/*
2128135446Strhodes * Clear some interrupt reason bits
2129135446Strhodes */
2130135446Strhodesstatic void
2131135446Strhodesaac_sa_clear_istatus(struct aac_softc *sc, int mask)
2132135446Strhodes{
2133193149Sdougb	debug_called(3);
2134135446Strhodes
2135135446Strhodes	AAC_SETREG2(sc, AAC_SA_DOORBELL0_CLEAR, mask);
2136193149Sdougb}
2137193149Sdougb
2138193149Sdougbstatic void
2139193149Sdougbaac_rx_clear_istatus(struct aac_softc *sc, int mask)
2140193149Sdougb{
2141193149Sdougb	debug_called(3);
2142193149Sdougb
2143193149Sdougb	AAC_SETREG4(sc, AAC_RX_ODBR, mask);
2144193149Sdougb}
2145135446Strhodes
2146224092Sdougbstatic void
2147224092Sdougbaac_fa_clear_istatus(struct aac_softc *sc, int mask)
2148135446Strhodes{
2149135446Strhodes	debug_called(3);
2150135446Strhodes
2151135446Strhodes	AAC_SETREG2(sc, AAC_FA_DOORBELL0_CLEAR, mask);
2152135446Strhodes	AAC_FA_HACK(sc);
2153135446Strhodes}
2154224092Sdougb
2155224092Sdougb/*
2156224092Sdougb * Populate the mailbox and set the command word
2157224092Sdougb */
2158224092Sdougbstatic void
2159224092Sdougbaac_sa_set_mailbox(struct aac_softc *sc, u_int32_t command,
2160224092Sdougb		u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3)
2161224092Sdougb{
2162224092Sdougb	debug_called(4);
2163135446Strhodes
2164135446Strhodes	AAC_SETREG4(sc, AAC_SA_MAILBOX, command);
2165135446Strhodes	AAC_SETREG4(sc, AAC_SA_MAILBOX + 4, arg0);
2166135446Strhodes	AAC_SETREG4(sc, AAC_SA_MAILBOX + 8, arg1);
2167135446Strhodes	AAC_SETREG4(sc, AAC_SA_MAILBOX + 12, arg2);
2168135446Strhodes	AAC_SETREG4(sc, AAC_SA_MAILBOX + 16, arg3);
2169135446Strhodes}
2170135446Strhodes
2171135446Strhodesstatic void
2172135446Strhodesaac_rx_set_mailbox(struct aac_softc *sc, u_int32_t command,
2173135446Strhodes		u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3)
2174135446Strhodes{
2175135446Strhodes	debug_called(4);
2176170222Sdougb
2177135446Strhodes	AAC_SETREG4(sc, AAC_RX_MAILBOX, command);
2178224092Sdougb	AAC_SETREG4(sc, AAC_RX_MAILBOX + 4, arg0);
2179224092Sdougb	AAC_SETREG4(sc, AAC_RX_MAILBOX + 8, arg1);
2180224092Sdougb	AAC_SETREG4(sc, AAC_RX_MAILBOX + 12, arg2);
2181224092Sdougb	AAC_SETREG4(sc, AAC_RX_MAILBOX + 16, arg3);
2182224092Sdougb}
2183224092Sdougb
2184224092Sdougbstatic void
2185224092Sdougbaac_fa_set_mailbox(struct aac_softc *sc, u_int32_t command,
2186224092Sdougb		u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3)
2187224092Sdougb{
2188224092Sdougb	debug_called(4);
2189224092Sdougb
2190135446Strhodes	AAC_SETREG4(sc, AAC_FA_MAILBOX, command);
2191135446Strhodes	AAC_FA_HACK(sc);
2192135446Strhodes	AAC_SETREG4(sc, AAC_FA_MAILBOX + 4, arg0);
2193135446Strhodes	AAC_FA_HACK(sc);
2194135446Strhodes	AAC_SETREG4(sc, AAC_FA_MAILBOX + 8, arg1);
2195135446Strhodes	AAC_FA_HACK(sc);
2196135446Strhodes	AAC_SETREG4(sc, AAC_FA_MAILBOX + 12, arg2);
2197135446Strhodes	AAC_FA_HACK(sc);
2198135446Strhodes	AAC_SETREG4(sc, AAC_FA_MAILBOX + 16, arg3);
2199135446Strhodes	AAC_FA_HACK(sc);
2200135446Strhodes}
2201186462Sdougb
2202135446Strhodes/*
2203170222Sdougb * Fetch the immediate command status word
2204170222Sdougb */
2205170222Sdougbstatic int
2206170222Sdougbaac_sa_get_mailbox(struct aac_softc *sc, int mb)
2207170222Sdougb{
2208170222Sdougb	debug_called(4);
2209170222Sdougb
2210170222Sdougb	return(AAC_GETREG4(sc, AAC_SA_MAILBOX + (mb * 4)));
2211170222Sdougb}
2212170222Sdougb
2213170222Sdougbstatic int
2214170222Sdougbaac_rx_get_mailbox(struct aac_softc *sc, int mb)
2215170222Sdougb{
2216135446Strhodes	debug_called(4);
2217135446Strhodes
2218135446Strhodes	return(AAC_GETREG4(sc, AAC_RX_MAILBOX + (mb * 4)));
2219135446Strhodes}
2220135446Strhodes
2221135446Strhodesstatic int
2222135446Strhodesaac_fa_get_mailbox(struct aac_softc *sc, int mb)
2223135446Strhodes{
2224135446Strhodes	int val;
2225135446Strhodes
2226135446Strhodes	debug_called(4);
2227135446Strhodes
2228135446Strhodes	val = AAC_GETREG4(sc, AAC_FA_MAILBOX + (mb * 4));
2229135446Strhodes	return (val);
2230135446Strhodes}
2231135446Strhodes
2232135446Strhodes/*
2233135446Strhodes * Set/clear interrupt masks
2234135446Strhodes */
2235135446Strhodesstatic void
2236135446Strhodesaac_sa_set_interrupts(struct aac_softc *sc, int enable)
2237135446Strhodes{
2238186462Sdougb	debug(2, "%sable interrupts", enable ? "en" : "dis");
2239135446Strhodes
2240135446Strhodes	if (enable) {
2241135446Strhodes		AAC_SETREG2((sc), AAC_SA_MASK0_CLEAR, AAC_DB_INTERRUPTS);
2242135446Strhodes	} else {
2243135446Strhodes		AAC_SETREG2((sc), AAC_SA_MASK0_SET, ~0);
2244135446Strhodes	}
2245135446Strhodes}
2246135446Strhodes
2247135446Strhodesstatic void
2248135446Strhodesaac_rx_set_interrupts(struct aac_softc *sc, int enable)
2249135446Strhodes{
2250135446Strhodes	debug(2, "%sable interrupts", enable ? "en" : "dis");
2251135446Strhodes
2252135446Strhodes	if (enable) {
2253135446Strhodes		AAC_SETREG4(sc, AAC_RX_OIMR, ~AAC_DB_INTERRUPTS);
2254135446Strhodes	} else {
2255135446Strhodes		AAC_SETREG4(sc, AAC_RX_OIMR, ~0);
2256135446Strhodes	}
2257135446Strhodes}
2258186462Sdougb
2259135446Strhodesstatic void
2260135446Strhodesaac_fa_set_interrupts(struct aac_softc *sc, int enable)
2261135446Strhodes{
2262135446Strhodes	debug(2, "%sable interrupts", enable ? "en" : "dis");
2263135446Strhodes
2264135446Strhodes	if (enable) {
2265135446Strhodes		AAC_SETREG2((sc), AAC_FA_MASK0_CLEAR, AAC_DB_INTERRUPTS);
2266135446Strhodes		AAC_FA_HACK(sc);
2267135446Strhodes	} else {
2268135446Strhodes		AAC_SETREG2((sc), AAC_FA_MASK0, ~0);
2269135446Strhodes		AAC_FA_HACK(sc);
2270135446Strhodes	}
2271135446Strhodes}
2272135446Strhodes
2273135446Strhodes/*
2274135446Strhodes * Debugging and Diagnostics
2275135446Strhodes */
2276135446Strhodes
2277135446Strhodes/*
2278135446Strhodes * Print some information about the controller.
2279135446Strhodes */
2280224092Sdougbstatic void
2281224092Sdougbaac_describe_controller(struct aac_softc *sc)
2282224092Sdougb{
2283224092Sdougb	struct aac_fib *fib;
2284135446Strhodes	struct aac_adapter_info	*info;
2285224092Sdougb
2286135446Strhodes	debug_called(2);
2287135446Strhodes
2288224092Sdougb	aac_alloc_sync_fib(sc, &fib, 0);
2289224092Sdougb
2290224092Sdougb	fib->data[0] = 0;
2291224092Sdougb	if (aac_sync_fib(sc, RequestAdapterInfo, 0, fib, 1)) {
2292224092Sdougb		device_printf(sc->aac_dev, "RequestAdapterInfo failed\n");
2293224092Sdougb		aac_release_sync_fib(sc);
2294224092Sdougb		return;
2295224092Sdougb	}
2296224092Sdougb	info = (struct aac_adapter_info *)&fib->data[0];
2297224092Sdougb
2298224092Sdougb	device_printf(sc->aac_dev, "%s %dMHz, %dMB cache memory, %s\n",
2299224092Sdougb		      aac_describe_code(aac_cpu_variant, info->CpuVariant),
2300224092Sdougb		      info->ClockSpeed, info->BufferMem / (1024 * 1024),
2301224092Sdougb		      aac_describe_code(aac_battery_platform,
2302224092Sdougb					info->batteryPlatform));
2303224092Sdougb
2304135446Strhodes	/* save the kernel revision structure for later use */
2305135446Strhodes	sc->aac_revision = info->KernelRevision;
2306135446Strhodes	device_printf(sc->aac_dev, "Kernel %d.%d-%d, Build %d, S/N %6X\n",
2307165071Sdougb		      info->KernelRevision.external.comp.major,
2308165071Sdougb		      info->KernelRevision.external.comp.minor,
2309135446Strhodes		      info->KernelRevision.external.comp.dash,
2310135446Strhodes		      info->KernelRevision.buildNumber,
2311135446Strhodes		      (u_int32_t)(info->SerialNumber & 0xffffff));
2312135446Strhodes
2313135446Strhodes	aac_release_sync_fib(sc);
2314135446Strhodes
2315135446Strhodes	if (1 || bootverbose) {
2316135446Strhodes		device_printf(sc->aac_dev, "Supported Options=%b\n",
2317165071Sdougb			      sc->supported_options,
2318135446Strhodes			      "\20"
2319135446Strhodes			      "\1SNAPSHOT"
2320135446Strhodes			      "\2CLUSTERS"
2321135446Strhodes			      "\3WCACHE"
2322135446Strhodes			      "\4DATA64"
2323135446Strhodes			      "\5HOSTTIME"
2324135446Strhodes			      "\6RAID50"
2325135446Strhodes			      "\7WINDOW4GB"
2326135446Strhodes			      "\10SCSIUPGD"
2327135446Strhodes			      "\11SOFTERR"
2328135446Strhodes			      "\12NORECOND"
2329135446Strhodes			      "\13SGMAP64"
2330135446Strhodes			      "\14ALARM"
2331135446Strhodes			      "\15NONDASD");
2332165071Sdougb	}
2333165071Sdougb}
2334135446Strhodes
2335135446Strhodes/*
2336135446Strhodes * Look up a text description of a numeric error code and return a pointer to
2337135446Strhodes * same.
2338135446Strhodes */
2339135446Strhodesstatic char *
2340135446Strhodesaac_describe_code(struct aac_code_lookup *table, u_int32_t code)
2341165071Sdougb{
2342135446Strhodes	int i;
2343135446Strhodes
2344135446Strhodes	for (i = 0; table[i].string != NULL; i++)
2345135446Strhodes		if (table[i].code == code)
2346135446Strhodes			return(table[i].string);
2347135446Strhodes	return(table[i + 1].string);
2348135446Strhodes}
2349135446Strhodes
2350135446Strhodes/*
2351135446Strhodes * Management Interface
2352135446Strhodes */
2353135446Strhodes
2354135446Strhodesstatic int
2355135446Strhodesaac_open(dev_t dev, int flags, int fmt, d_thread_t *td)
2356135446Strhodes{
2357135446Strhodes	struct aac_softc *sc;
2358224092Sdougb
2359135446Strhodes	debug_called(2);
2360224092Sdougb
2361224092Sdougb	sc = dev->si_drv1;
2362135446Strhodes
2363135446Strhodes	/* Check to make sure the device isn't already open */
2364135446Strhodes	if (sc->aac_state & AAC_STATE_OPEN) {
2365135446Strhodes		return EBUSY;
2366135446Strhodes	}
2367165071Sdougb	sc->aac_state |= AAC_STATE_OPEN;
2368135446Strhodes
2369135446Strhodes	return 0;
2370135446Strhodes}
2371135446Strhodes
2372135446Strhodesstatic int
2373135446Strhodesaac_close(dev_t dev, int flags, int fmt, d_thread_t *td)
2374135446Strhodes{
2375135446Strhodes	struct aac_softc *sc;
2376135446Strhodes
2377135446Strhodes	debug_called(2);
2378135446Strhodes
2379135446Strhodes	sc = dev->si_drv1;
2380135446Strhodes
2381135446Strhodes	/* Mark this unit as no longer open  */
2382135446Strhodes	sc->aac_state &= ~AAC_STATE_OPEN;
2383135446Strhodes
2384135446Strhodes	return 0;
2385135446Strhodes}
2386135446Strhodes
2387135446Strhodesstatic int
2388135446Strhodesaac_ioctl(dev_t dev, u_long cmd, caddr_t arg, int flag, d_thread_t *td)
2389135446Strhodes{
2390135446Strhodes	union aac_statrequest *as;
2391135446Strhodes	struct aac_softc *sc;
2392135446Strhodes	int error = 0;
2393135446Strhodes	uint32_t cookie;
2394135446Strhodes
2395135446Strhodes	debug_called(2);
2396135446Strhodes
2397135446Strhodes	as = (union aac_statrequest *)arg;
2398135446Strhodes	sc = dev->si_drv1;
2399135446Strhodes
2400135446Strhodes	switch (cmd) {
2401186462Sdougb	case AACIO_STATS:
2402135446Strhodes		switch (as->as_item) {
2403135446Strhodes		case AACQ_FREE:
2404135446Strhodes		case AACQ_BIO:
2405135446Strhodes		case AACQ_READY:
2406135446Strhodes		case AACQ_BUSY:
2407135446Strhodes			bcopy(&sc->aac_qstat[as->as_item], &as->as_qstat,
2408135446Strhodes			      sizeof(struct aac_qstat));
2409135446Strhodes			break;
2410135446Strhodes		default:
2411135446Strhodes			error = ENOENT;
2412135446Strhodes			break;
2413135446Strhodes		}
2414135446Strhodes	break;
2415135446Strhodes
2416135446Strhodes	case FSACTL_SENDFIB:
2417135446Strhodes		arg = *(caddr_t*)arg;
2418135446Strhodes	case FSACTL_LNX_SENDFIB:
2419135446Strhodes		debug(1, "FSACTL_SENDFIB");
2420135446Strhodes		error = aac_ioctl_sendfib(sc, arg);
2421135446Strhodes		break;
2422135446Strhodes	case FSACTL_AIF_THREAD:
2423135446Strhodes	case FSACTL_LNX_AIF_THREAD:
2424135446Strhodes		debug(1, "FSACTL_AIF_THREAD");
2425135446Strhodes		error = EINVAL;
2426135446Strhodes		break;
2427135446Strhodes	case FSACTL_OPEN_GET_ADAPTER_FIB:
2428171577Sdougb		arg = *(caddr_t*)arg;
2429193149Sdougb	case FSACTL_LNX_OPEN_GET_ADAPTER_FIB:
2430193149Sdougb		debug(1, "FSACTL_OPEN_GET_ADAPTER_FIB");
2431171577Sdougb		/*
2432171577Sdougb		 * Pass the caller out an AdapterFibContext.
2433224092Sdougb		 *
2434216175Sdougb		 * Note that because we only support one opener, we
2435224092Sdougb		 * basically ignore this.  Set the caller's context to a magic
2436216175Sdougb		 * number just in case.
2437216175Sdougb		 *
2438193149Sdougb		 * The Linux code hands the driver a pointer into kernel space,
2439224092Sdougb		 * and then trusts it when the caller hands it back.  Aiee!
2440216175Sdougb		 * Here, we give it the proc pointer of the per-adapter aif
2441193149Sdougb		 * thread. It's only used as a sanity check in other calls.
2442135446Strhodes		 */
2443224092Sdougb		cookie = (uint32_t)(uintptr_t)sc->aifthread;
2444193149Sdougb		error = copyout(&cookie, arg, sizeof(cookie));
2445193149Sdougb		break;
2446224092Sdougb	case FSACTL_GET_NEXT_ADAPTER_FIB:
2447193149Sdougb		arg = *(caddr_t*)arg;
2448193149Sdougb	case FSACTL_LNX_GET_NEXT_ADAPTER_FIB:
2449135446Strhodes		debug(1, "FSACTL_GET_NEXT_ADAPTER_FIB");
2450135446Strhodes		error = aac_getnext_aif(sc, arg);
2451171577Sdougb		break;
2452171577Sdougb	case FSACTL_CLOSE_GET_ADAPTER_FIB:
2453171577Sdougb	case FSACTL_LNX_CLOSE_GET_ADAPTER_FIB:
2454171577Sdougb		debug(1, "FSACTL_CLOSE_GET_ADAPTER_FIB");
2455170222Sdougb		/* don't do anything here */
2456216175Sdougb		break;
2457216175Sdougb	case FSACTL_MINIPORT_REV_CHECK:
2458224092Sdougb		arg = *(caddr_t*)arg;
2459224092Sdougb	case FSACTL_LNX_MINIPORT_REV_CHECK:
2460224092Sdougb		debug(1, "FSACTL_MINIPORT_REV_CHECK");
2461224092Sdougb		error = aac_rev_check(sc, arg);
2462224092Sdougb		break;
2463224092Sdougb	case FSACTL_QUERY_DISK:
2464216175Sdougb		arg = *(caddr_t*)arg;
2465224092Sdougb	case FSACTL_LNX_QUERY_DISK:
2466216175Sdougb		debug(1, "FSACTL_QUERY_DISK");
2467193149Sdougb		error = aac_query_disk(sc, arg);
2468216175Sdougb			break;
2469216175Sdougb	case FSACTL_DELETE_DISK:
2470171577Sdougb	case FSACTL_LNX_DELETE_DISK:
2471171577Sdougb		/*
2472193149Sdougb		 * We don't trust the underland to tell us when to delete a
2473193149Sdougb		 * container, rather we rely on an AIF coming from the
2474171577Sdougb		 * controller
2475170222Sdougb		 */
2476171577Sdougb		error = 0;
2477224092Sdougb		break;
2478193149Sdougb	default:
2479193149Sdougb		debug(1, "unsupported cmd 0x%lx\n", cmd);
2480193149Sdougb		error = EINVAL;
2481193149Sdougb		break;
2482224092Sdougb	}
2483193149Sdougb	return(error);
2484193149Sdougb}
2485216175Sdougb
2486193149Sdougbstatic int
2487193149Sdougbaac_poll(dev_t dev, int poll_events, d_thread_t *td)
2488224092Sdougb{
2489224092Sdougb	struct aac_softc *sc;
2490224092Sdougb	int revents;
2491216175Sdougb
2492224092Sdougb	sc = dev->si_drv1;
2493193149Sdougb	revents = 0;
2494170222Sdougb
2495193149Sdougb	AAC_LOCK_ACQUIRE(&sc->aac_aifq_lock);
2496224092Sdougb	if ((poll_events & (POLLRDNORM | POLLIN)) != 0) {
2497224092Sdougb		if (sc->aac_aifq_tail != sc->aac_aifq_head)
2498224092Sdougb			revents |= poll_events & (POLLIN | POLLRDNORM);
2499224092Sdougb	}
2500224092Sdougb	AAC_LOCK_RELEASE(&sc->aac_aifq_lock);
2501224092Sdougb
2502224092Sdougb	if (revents == 0) {
2503224092Sdougb		if (poll_events & (POLLIN | POLLRDNORM))
2504224092Sdougb			selrecord(td, &sc->rcv_select);
2505224092Sdougb	}
2506224092Sdougb
2507224092Sdougb	return (revents);
2508224092Sdougb}
2509224092Sdougb
2510224092Sdougb/*
2511224092Sdougb * Send a FIB supplied from userspace
2512224092Sdougb */
2513224092Sdougbstatic int
2514224092Sdougbaac_ioctl_sendfib(struct aac_softc *sc, caddr_t ufib)
2515193149Sdougb{
2516193149Sdougb	struct aac_command *cm;
2517193149Sdougb	int size, error;
2518193149Sdougb
2519135446Strhodes	debug_called(2);
2520193149Sdougb
2521193149Sdougb	cm = NULL;
2522193149Sdougb
2523193149Sdougb	/*
2524193149Sdougb	 * Get a command
2525193149Sdougb	 */
2526193149Sdougb	AAC_LOCK_ACQUIRE(&sc->aac_io_lock);
2527224092Sdougb	if (aac_alloc_command(sc, &cm)) {
2528193149Sdougb		error = EBUSY;
2529193149Sdougb		goto out;
2530193149Sdougb	}
2531224092Sdougb
2532193149Sdougb	/*
2533193149Sdougb	 * Fetch the FIB header, then re-copy to get data as well.
2534193149Sdougb	 */
2535224092Sdougb	if ((error = copyin(ufib, cm->cm_fib,
2536193149Sdougb			    sizeof(struct aac_fib_header))) != 0)
2537193149Sdougb		goto out;
2538193149Sdougb	size = cm->cm_fib->Header.Size + sizeof(struct aac_fib_header);
2539224092Sdougb	if (size > sizeof(struct aac_fib)) {
2540193149Sdougb		device_printf(sc->aac_dev, "incoming FIB oversized (%d > %zd)\n",
2541193149Sdougb			      size, sizeof(struct aac_fib));
2542135446Strhodes		size = sizeof(struct aac_fib);
2543135446Strhodes	}
2544135446Strhodes	if ((error = copyin(ufib, cm->cm_fib, size)) != 0)
2545135446Strhodes		goto out;
2546135446Strhodes	cm->cm_fib->Header.Size = size;
2547135446Strhodes	cm->cm_timestamp = time_second;
2548135446Strhodes
2549135446Strhodes	/*
2550135446Strhodes	 * Pass the FIB to the controller, wait for it to complete.
2551170222Sdougb	 */
2552170222Sdougb	if ((error = aac_wait_command(cm, 30)) != 0) {	/* XXX user timeout? */
2553193149Sdougb		device_printf(sc->aac_dev,
2554193149Sdougb			      "aac_wait_command return %d\n", error);
2555193149Sdougb		goto out;
2556193149Sdougb	}
2557193149Sdougb
2558170222Sdougb	/*
2559170222Sdougb	 * Copy the FIB and data back out to the caller.
2560170222Sdougb	 */
2561170222Sdougb	size = cm->cm_fib->Header.Size;
2562170222Sdougb	if (size > sizeof(struct aac_fib)) {
2563170222Sdougb		device_printf(sc->aac_dev, "outbound FIB oversized (%d > %zd)\n",
2564170222Sdougb			      size, sizeof(struct aac_fib));
2565170222Sdougb		size = sizeof(struct aac_fib);
2566170222Sdougb	}
2567170222Sdougb	error = copyout(cm->cm_fib, ufib, size);
2568186462Sdougb
2569224092Sdougbout:
2570135446Strhodes	if (cm != NULL) {
2571224092Sdougb		aac_release_command(cm);
2572224092Sdougb	}
2573224092Sdougb
2574224092Sdougb	AAC_LOCK_RELEASE(&sc->aac_io_lock);
2575224092Sdougb	return(error);
2576224092Sdougb}
2577224092Sdougb
2578224092Sdougb/*
2579224092Sdougb * Handle an AIF sent to us by the controller; queue it for later reference.
2580224092Sdougb * If the queue fills up, then drop the older entries.
2581224092Sdougb */
2582224092Sdougbstatic void
2583224092Sdougbaac_handle_aif(struct aac_softc *sc, struct aac_fib *fib)
2584224092Sdougb{
2585224092Sdougb	struct aac_aif_command *aif;
2586224092Sdougb	struct aac_container *co, *co_next;
2587224092Sdougb	struct aac_mntinfo *mi;
2588224092Sdougb	struct aac_mntinforesp *mir = NULL;
2589224092Sdougb	u_int16_t rsize;
2590135446Strhodes	int next, found;
2591135446Strhodes	int count = 0, added = 0, i = 0;
2592135446Strhodes
2593135446Strhodes	debug_called(2);
2594135446Strhodes
2595224092Sdougb	aif = (struct aac_aif_command*)&fib->data[0];
2596135446Strhodes	aac_print_aif(sc, aif);
2597224092Sdougb
2598224092Sdougb	/* Is it an event that we should care about? */
2599234010Sdougb	switch (aif->command) {
2600224092Sdougb	case AifCmdEventNotify:
2601234010Sdougb		switch (aif->data.EN.type) {
2602234010Sdougb		case AifEnAddContainer:
2603234010Sdougb		case AifEnDeleteContainer:
2604234010Sdougb			/*
2605234010Sdougb			 * A container was added or deleted, but the message
2606234010Sdougb			 * doesn't tell us anything else!  Re-enumerate the
2607234010Sdougb			 * containers and sort things out.
2608234010Sdougb			 */
2609234010Sdougb			aac_alloc_sync_fib(sc, &fib, 0);
2610234010Sdougb			mi = (struct aac_mntinfo *)&fib->data[0];
2611234010Sdougb			do {
2612224092Sdougb				/*
2613224092Sdougb				 * Ask the controller for its containers one at
2614224092Sdougb				 * a time.
2615224092Sdougb				 * XXX What if the controller's list changes
2616135446Strhodes				 * midway through this enumaration?
2617135446Strhodes				 * XXX This should be done async.
2618135446Strhodes				 */
2619135446Strhodes				bzero(mi, sizeof(struct aac_mntinfo));
2620135446Strhodes				mi->Command = VM_NameServe;
2621135446Strhodes				mi->MntType = FT_FILESYS;
2622135446Strhodes				mi->MntCount = i;
2623135446Strhodes				rsize = sizeof(mir);
2624135446Strhodes				if (aac_sync_fib(sc, ContainerCommand, 0, fib,
2625135446Strhodes						 sizeof(struct aac_mntinfo))) {
2626135446Strhodes					printf("Error probing container %d\n",
2627135446Strhodes					      i);
2628135446Strhodes					continue;
2629135446Strhodes				}
2630135446Strhodes				mir = (struct aac_mntinforesp *)&fib->data[0];
2631224092Sdougb				/* XXX Need to check if count changed */
2632135446Strhodes				count = mir->MntRespCount;
2633135446Strhodes				/*
2634135446Strhodes				 * Check the container against our list.
2635135446Strhodes				 * co->co_found was already set to 0 in a
2636135446Strhodes				 * previous run.
2637135446Strhodes				 */
2638135446Strhodes				if ((mir->Status == ST_OK) &&
2639135446Strhodes				    (mir->MntTable[0].VolType != CT_NONE)) {
2640135446Strhodes					found = 0;
2641224092Sdougb					TAILQ_FOREACH(co,
2642224092Sdougb						      &sc->aac_container_tqh,
2643170222Sdougb						      co_link) {
2644170222Sdougb						if (co->co_mntobj.ObjectId ==
2645170222Sdougb						    mir->MntTable[0].ObjectId) {
2646170222Sdougb							co->co_found = 1;
2647170222Sdougb							found = 1;
2648135446Strhodes							break;
2649135446Strhodes						}
2650135446Strhodes					}
2651135446Strhodes					/*
2652135446Strhodes					 * If the container matched, continue
2653135446Strhodes					 * in the list.
2654135446Strhodes					 */
2655135446Strhodes					if (found) {
2656135446Strhodes						i++;
2657135446Strhodes						continue;
2658135446Strhodes					}
2659135446Strhodes
2660135446Strhodes					/*
2661135446Strhodes					 * This is a new container.  Do all the
2662135446Strhodes					 * appropriate things to set it up.
2663135446Strhodes					 */
2664135446Strhodes					aac_add_container(sc, mir, 1);
2665135446Strhodes					added = 1;
2666135446Strhodes				}
2667135446Strhodes				i++;
2668135446Strhodes			} while ((i < count) && (i < AAC_MAX_CONTAINERS));
2669135446Strhodes			aac_release_sync_fib(sc);
2670165071Sdougb
2671165071Sdougb			/*
2672135446Strhodes			 * Go through our list of containers and see which ones
2673135446Strhodes			 * were not marked 'found'.  Since the controller didn't
2674135446Strhodes			 * list them they must have been deleted.  Do the
2675135446Strhodes			 * appropriate steps to destroy the device.  Also reset
2676135446Strhodes			 * the co->co_found field.
2677135446Strhodes			 */
2678135446Strhodes			co = TAILQ_FIRST(&sc->aac_container_tqh);
2679135446Strhodes			while (co != NULL) {
2680135446Strhodes				if (co->co_found == 0) {
2681135446Strhodes					device_delete_child(sc->aac_dev,
2682135446Strhodes							    co->co_disk);
2683224092Sdougb					co_next = TAILQ_NEXT(co, co_link);
2684135446Strhodes					AAC_LOCK_ACQUIRE(&sc->
2685135446Strhodes							aac_container_lock);
2686135446Strhodes					TAILQ_REMOVE(&sc->aac_container_tqh, co,
2687135446Strhodes						     co_link);
2688135446Strhodes					AAC_LOCK_RELEASE(&sc->
2689135446Strhodes							 aac_container_lock);
2690135446Strhodes					FREE(co, M_AACBUF);
2691170222Sdougb					co = co_next;
2692170222Sdougb				} else {
2693170222Sdougb					co->co_found = 0;
2694170222Sdougb					co = TAILQ_NEXT(co, co_link);
2695170222Sdougb				}
2696170222Sdougb			}
2697170222Sdougb
2698170222Sdougb			/* Attach the newly created containers */
2699170222Sdougb			if (added)
2700170222Sdougb				bus_generic_attach(sc->aac_dev);
2701170222Sdougb
2702170222Sdougb			break;
2703170222Sdougb
2704170222Sdougb		default:
2705170222Sdougb			break;
2706170222Sdougb		}
2707170222Sdougb
2708170222Sdougb	default:
2709170222Sdougb		break;
2710170222Sdougb	}
2711170222Sdougb
2712234010Sdougb	/* Copy the AIF data to the AIF queue for ioctl retrieval */
2713170222Sdougb	AAC_LOCK_ACQUIRE(&sc->aac_aifq_lock);
2714170222Sdougb	next = (sc->aac_aifq_head + 1) % AAC_AIFQ_LENGTH;
2715170222Sdougb	if (next != sc->aac_aifq_tail) {
2716170222Sdougb		bcopy(aif, &sc->aac_aifq[next], sizeof(struct aac_aif_command));
2717170222Sdougb		sc->aac_aifq_head = next;
2718170222Sdougb
2719170222Sdougb		/* On the off chance that someone is sleeping for an aif... */
2720170222Sdougb		if (sc->aac_state & AAC_STATE_AIF_SLEEPER)
2721170222Sdougb			wakeup(sc->aac_aifq);
2722170222Sdougb		/* Wakeup any poll()ers */
2723170222Sdougb		selwakeuppri(&sc->rcv_select, PRIBIO);
2724170222Sdougb	}
2725193149Sdougb	AAC_LOCK_RELEASE(&sc->aac_aifq_lock);
2726170222Sdougb
2727170222Sdougb	return;
2728170222Sdougb}
2729170222Sdougb
2730170222Sdougb/*
2731170222Sdougb * Return the Revision of the driver to userspace and check to see if the
2732170222Sdougb * userspace app is possibly compatible.  This is extremely bogus since
2733170222Sdougb * our driver doesn't follow Adaptec's versioning system.  Cheat by just
2734170222Sdougb * returning what the card reported.
2735170222Sdougb */
2736224092Sdougbstatic int
2737224092Sdougbaac_rev_check(struct aac_softc *sc, caddr_t udata)
2738170222Sdougb{
2739170222Sdougb	struct aac_rev_check rev_check;
2740170222Sdougb	struct aac_rev_check_resp rev_check_resp;
2741170222Sdougb	int error = 0;
2742170222Sdougb
2743170222Sdougb	debug_called(2);
2744170222Sdougb
2745170222Sdougb	/*
2746170222Sdougb	 * Copyin the revision struct from userspace
2747170222Sdougb	 */
2748170222Sdougb	if ((error = copyin(udata, (caddr_t)&rev_check,
2749170222Sdougb			sizeof(struct aac_rev_check))) != 0) {
2750170222Sdougb		return error;
2751224092Sdougb	}
2752224092Sdougb
2753170222Sdougb	debug(2, "Userland revision= %d\n",
2754170222Sdougb	      rev_check.callingRevision.buildNumber);
2755170222Sdougb
2756170222Sdougb	/*
2757170222Sdougb	 * Doctor up the response struct.
2758170222Sdougb	 */
2759170222Sdougb	rev_check_resp.possiblyCompatible = 1;
2760193149Sdougb	rev_check_resp.adapterSWRevision.external.ul =
2761193149Sdougb	    sc->aac_revision.external.ul;
2762193149Sdougb	rev_check_resp.adapterSWRevision.buildNumber =
2763193149Sdougb	    sc->aac_revision.buildNumber;
2764193149Sdougb
2765170222Sdougb	return(copyout((caddr_t)&rev_check_resp, udata,
2766170222Sdougb			sizeof(struct aac_rev_check_resp)));
2767170222Sdougb}
2768170222Sdougb
2769170222Sdougb/*
2770170222Sdougb * Pass the caller the next AIF in their queue
2771170222Sdougb */
2772170222Sdougbstatic int
2773170222Sdougbaac_getnext_aif(struct aac_softc *sc, caddr_t arg)
2774170222Sdougb{
2775170222Sdougb	struct get_adapter_fib_ioctl agf;
2776170222Sdougb	int error;
2777170222Sdougb
2778224092Sdougb	debug_called(2);
2779224092Sdougb
2780170222Sdougb	if ((error = copyin(arg, &agf, sizeof(agf))) == 0) {
2781170222Sdougb
2782170222Sdougb		/*
2783170222Sdougb		 * Check the magic number that we gave the caller.
2784170222Sdougb		 */
2785170222Sdougb		if (agf.AdapterFibContext != (int)(uintptr_t)sc->aifthread) {
2786170222Sdougb			error = EFAULT;
2787170222Sdougb		} else {
2788170222Sdougb			error = aac_return_aif(sc, agf.AifFib);
2789193149Sdougb			if ((error == EAGAIN) && (agf.Wait)) {
2790170222Sdougb				sc->aac_state |= AAC_STATE_AIF_SLEEPER;
2791170222Sdougb				while (error == EAGAIN) {
2792170222Sdougb					error = tsleep(sc->aac_aifq, PRIBIO |
2793170222Sdougb						       PCATCH, "aacaif", 0);
2794170222Sdougb					if (error == 0)
2795170222Sdougb						error = aac_return_aif(sc,
2796170222Sdougb						    agf.AifFib);
2797170222Sdougb				}
2798170222Sdougb				sc->aac_state &= ~AAC_STATE_AIF_SLEEPER;
2799170222Sdougb			}
2800170222Sdougb		}
2801170222Sdougb	}
2802170222Sdougb	return(error);
2803186462Sdougb}
2804170222Sdougb
2805170222Sdougb/*
2806170222Sdougb * Hand the next AIF off the top of the queue out to userspace.
2807170222Sdougb */
2808170222Sdougbstatic int
2809170222Sdougbaac_return_aif(struct aac_softc *sc, caddr_t uptr)
2810186462Sdougb{
2811170222Sdougb	int next, error;
2812170222Sdougb
2813170222Sdougb	debug_called(2);
2814170222Sdougb
2815170222Sdougb	AAC_LOCK_ACQUIRE(&sc->aac_aifq_lock);
2816170222Sdougb	if (sc->aac_aifq_tail == sc->aac_aifq_head) {
2817170222Sdougb		AAC_LOCK_RELEASE(&sc->aac_aifq_lock);
2818170222Sdougb		return (EAGAIN);
2819170222Sdougb	}
2820170222Sdougb
2821170222Sdougb	next = (sc->aac_aifq_tail + 1) % AAC_AIFQ_LENGTH;
2822170222Sdougb	error = copyout(&sc->aac_aifq[next], uptr,
2823170222Sdougb			sizeof(struct aac_aif_command));
2824170222Sdougb	if (error)
2825170222Sdougb		device_printf(sc->aac_dev,
2826170222Sdougb		    "aac_return_aif: copyout returned %d\n", error);
2827170222Sdougb	else
2828170222Sdougb		sc->aac_aifq_tail = next;
2829170222Sdougb
2830170222Sdougb	AAC_LOCK_RELEASE(&sc->aac_aifq_lock);
2831170222Sdougb	return(error);
2832170222Sdougb}
2833170222Sdougb
2834170222Sdougb/*
2835170222Sdougb * Give the userland some information about the container.  The AAC arch
2836170222Sdougb * expects the driver to be a SCSI passthrough type driver, so it expects
2837170222Sdougb * the containers to have b:t:l numbers.  Fake it.
2838170222Sdougb */
2839174187Sdougbstatic int
2840193149Sdougbaac_query_disk(struct aac_softc *sc, caddr_t uptr)
2841193149Sdougb{
2842170222Sdougb	struct aac_query_disk query_disk;
2843170222Sdougb	struct aac_container *co;
2844170222Sdougb	struct aac_disk	*disk;
2845170222Sdougb	int error, id;
2846170222Sdougb
2847170222Sdougb	debug_called(2);
2848170222Sdougb
2849170222Sdougb	disk = NULL;
2850234010Sdougb
2851234010Sdougb	error = copyin(uptr, (caddr_t)&query_disk,
2852170222Sdougb		       sizeof(struct aac_query_disk));
2853170222Sdougb	if (error)
2854193149Sdougb		return (error);
2855170222Sdougb
2856186462Sdougb	id = query_disk.ContainerNumber;
2857170222Sdougb	if (id == -1)
2858170222Sdougb		return (EINVAL);
2859193149Sdougb
2860193149Sdougb	AAC_LOCK_ACQUIRE(&sc->aac_container_lock);
2861170222Sdougb	TAILQ_FOREACH(co, &sc->aac_container_tqh, co_link) {
2862170222Sdougb		if (co->co_mntobj.ObjectId == id)
2863170222Sdougb			break;
2864170222Sdougb		}
2865193149Sdougb
2866170222Sdougb	if (co == NULL) {
2867170222Sdougb			query_disk.Valid = 0;
2868170222Sdougb			query_disk.Locked = 0;
2869170222Sdougb			query_disk.Deleted = 1;		/* XXX is this right? */
2870170222Sdougb	} else {
2871170222Sdougb		disk = device_get_softc(co->co_disk);
2872170222Sdougb		query_disk.Valid = 1;
2873170222Sdougb		query_disk.Locked =
2874186462Sdougb		    (disk->ad_flags & AAC_DISK_OPEN) ? 1 : 0;
2875224092Sdougb		query_disk.Deleted = 0;
2876224092Sdougb		query_disk.Bus = device_get_unit(sc->aac_dev);
2877224092Sdougb		query_disk.Target = disk->unit;
2878224092Sdougb		query_disk.Lun = 0;
2879224092Sdougb		query_disk.UnMapped = 0;
2880224092Sdougb		sprintf(&query_disk.diskDeviceName[0], "%s%d",
2881224092Sdougb		        disk->ad_disk->d_name, disk->ad_disk->d_unit);
2882224092Sdougb	}
2883224092Sdougb	AAC_LOCK_RELEASE(&sc->aac_container_lock);
2884224092Sdougb
2885224092Sdougb	error = copyout((caddr_t)&query_disk, uptr,
2886225361Sdougb			sizeof(struct aac_query_disk));
2887224092Sdougb
2888224092Sdougb	return (error);
2889224092Sdougb}
2890224092Sdougb
2891224092Sdougbstatic void
2892224092Sdougbaac_get_bus_info(struct aac_softc *sc)
2893224092Sdougb{
2894224092Sdougb	struct aac_fib *fib;
2895224092Sdougb	struct aac_ctcfg *c_cmd;
2896224092Sdougb	struct aac_ctcfg_resp *c_resp;
2897224092Sdougb	struct aac_vmioctl *vmi;
2898135446Strhodes	struct aac_vmi_businf_resp *vmi_resp;
2899135446Strhodes	struct aac_getbusinf businfo;
2900135446Strhodes	struct aac_sim *caminf;
2901224092Sdougb	device_t child;
2902224092Sdougb	int i, found, error;
2903224092Sdougb
2904224092Sdougb	aac_alloc_sync_fib(sc, &fib, 0);
2905224092Sdougb	c_cmd = (struct aac_ctcfg *)&fib->data[0];
2906224092Sdougb	bzero(c_cmd, sizeof(struct aac_ctcfg));
2907224092Sdougb
2908224092Sdougb	c_cmd->Command = VM_ContainerConfig;
2909170222Sdougb	c_cmd->cmd = CT_GET_SCSI_METHOD;
2910170222Sdougb	c_cmd->param = 0;
2911135446Strhodes
2912135446Strhodes	error = aac_sync_fib(sc, ContainerCommand, 0, fib,
2913135446Strhodes	    sizeof(struct aac_ctcfg));
2914135446Strhodes	if (error) {
2915193149Sdougb		device_printf(sc->aac_dev, "Error %d sending "
2916193149Sdougb		    "VM_ContainerConfig command\n", error);
2917193149Sdougb		aac_release_sync_fib(sc);
2918193149Sdougb		return;
2919135446Strhodes	}
2920135446Strhodes
2921135446Strhodes	c_resp = (struct aac_ctcfg_resp *)&fib->data[0];
2922135446Strhodes	if (c_resp->Status != ST_OK) {
2923225361Sdougb		device_printf(sc->aac_dev, "VM_ContainerConfig returned 0x%x\n",
2924225361Sdougb		    c_resp->Status);
2925135446Strhodes		aac_release_sync_fib(sc);
2926135446Strhodes		return;
2927135446Strhodes	}
2928135446Strhodes
2929135446Strhodes	sc->scsi_method_id = c_resp->param;
2930135446Strhodes
2931135446Strhodes	vmi = (struct aac_vmioctl *)&fib->data[0];
2932135446Strhodes	bzero(vmi, sizeof(struct aac_vmioctl));
2933135446Strhodes
2934135446Strhodes	vmi->Command = VM_Ioctl;
2935135446Strhodes	vmi->ObjType = FT_DRIVE;
2936135446Strhodes	vmi->MethId = sc->scsi_method_id;
2937135446Strhodes	vmi->ObjId = 0;
2938135446Strhodes	vmi->IoctlCmd = GetBusInfo;
2939135446Strhodes
2940135446Strhodes	error = aac_sync_fib(sc, ContainerCommand, 0, fib,
2941135446Strhodes	    sizeof(struct aac_vmioctl));
2942135446Strhodes	if (error) {
2943135446Strhodes		device_printf(sc->aac_dev, "Error %d sending VMIoctl command\n",
2944135446Strhodes		    error);
2945135446Strhodes		aac_release_sync_fib(sc);
2946135446Strhodes		return;
2947135446Strhodes	}
2948165071Sdougb
2949165071Sdougb	vmi_resp = (struct aac_vmi_businf_resp *)&fib->data[0];
2950135446Strhodes	if (vmi_resp->Status != ST_OK) {
2951165071Sdougb		device_printf(sc->aac_dev, "VM_Ioctl returned %d\n",
2952165071Sdougb		    vmi_resp->Status);
2953165071Sdougb		aac_release_sync_fib(sc);
2954135446Strhodes		return;
2955135446Strhodes	}
2956135446Strhodes
2957135446Strhodes	bcopy(&vmi_resp->BusInf, &businfo, sizeof(struct aac_getbusinf));
2958135446Strhodes	aac_release_sync_fib(sc);
2959135446Strhodes
2960135446Strhodes	found = 0;
2961135446Strhodes	for (i = 0; i < businfo.BusCount; i++) {
2962135446Strhodes		if (businfo.BusValid[i] != AAC_BUS_VALID)
2963135446Strhodes			continue;
2964135446Strhodes
2965135446Strhodes		caminf = (struct aac_sim *)malloc( sizeof(struct aac_sim),
2966135446Strhodes		    M_AACBUF, M_NOWAIT | M_ZERO);
2967135446Strhodes		if (caminf == NULL)
2968135446Strhodes			continue;
2969135446Strhodes
2970135446Strhodes		child = device_add_child(sc->aac_dev, "aacp", -1);
2971135446Strhodes		if (child == NULL) {
2972135446Strhodes			device_printf(sc->aac_dev, "device_add_child failed\n");
2973135446Strhodes			continue;
2974135446Strhodes		}
2975135446Strhodes
2976135446Strhodes		caminf->TargetsPerBus = businfo.TargetsPerBus;
2977135446Strhodes		caminf->BusNumber = i;
2978135446Strhodes		caminf->InitiatorBusId = businfo.InitiatorBusId[i];
2979135446Strhodes		caminf->aac_sc = sc;
2980135446Strhodes		caminf->sim_dev = child;
2981135446Strhodes
2982135446Strhodes		device_set_ivars(child, caminf);
2983135446Strhodes		device_set_desc(child, "SCSI Passthrough Bus");
2984135446Strhodes		TAILQ_INSERT_TAIL(&sc->aac_sim_tqh, caminf, sim_link);
2985135446Strhodes
2986165071Sdougb		found = 1;
2987135446Strhodes	}
2988135446Strhodes
2989135446Strhodes	if (found)
2990135446Strhodes		bus_generic_attach(sc->aac_dev);
2991135446Strhodes
2992165071Sdougb	return;
2993165071Sdougb}
2994135446Strhodes