aac_ioctl.c revision 7100:9d60065f44d9
1/*
2 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
3 * Use is subject to license terms.
4 */
5
6/*
7 * Copyright 2005-06 Adaptec, Inc.
8 * Copyright (c) 2005-06 Adaptec Inc., Achim Leubner
9 * Copyright (c) 2000 Michael Smith
10 * Copyright (c) 2001 Scott Long
11 * Copyright (c) 2000 BSDi
12 * All rights reserved.
13 *
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions
16 * are met:
17 * 1. Redistributions of source code must retain the above copyright
18 *    notice, this list of conditions and the following disclaimer.
19 * 2. Redistributions in binary form must reproduce the above copyright
20 *    notice, this list of conditions and the following disclaimer in the
21 *    documentation and/or other materials provided with the distribution.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35#pragma ident	"%Z%%M%	%I%	%E% SMI"
36
37#include <sys/modctl.h>
38#include <sys/conf.h>
39#include <sys/cmn_err.h>
40#include <sys/ddi.h>
41#include <sys/devops.h>
42#include <sys/pci.h>
43#include <sys/types.h>
44#include <sys/ddidmareq.h>
45#include <sys/scsi/scsi.h>
46#include <sys/ksynch.h>
47#include <sys/sunddi.h>
48#include <sys/byteorder.h>
49#include <sys/kmem.h>
50#include "aac_regs.h"
51#include "aac.h"
52#include "aac_ioctl.h"
53
54struct aac_umem_sge {
55	uint32_t bcount;
56	caddr_t addr;
57	struct aac_cmd acp;
58};
59
60/*
61 * External functions
62 */
63extern int aac_sync_mbcommand(struct aac_softstate *, uint32_t, uint32_t,
64    uint32_t, uint32_t, uint32_t, uint32_t *);
65extern int aac_cmd_dma_alloc(struct aac_softstate *, struct aac_cmd *,
66    struct buf *, int, int (*)(), caddr_t);
67extern void aac_free_dmamap(struct aac_cmd *);
68extern int aac_do_io(struct aac_softstate *, struct aac_cmd *);
69extern void aac_cmd_fib_copy(struct aac_softstate *, struct aac_cmd *);
70extern void aac_ioctl_complete(struct aac_softstate *, struct aac_cmd *);
71
72extern ddi_device_acc_attr_t aac_acc_attr;
73extern int aac_check_dma_handle(ddi_dma_handle_t);
74
75/*
76 * IOCTL command handling functions
77 */
78static int aac_check_revision(struct aac_softstate *, intptr_t, int);
79static int aac_ioctl_send_fib(struct aac_softstate *, intptr_t, int);
80static int aac_open_getadapter_fib(struct aac_softstate *, intptr_t, int);
81static int aac_next_getadapter_fib(struct aac_softstate *, intptr_t, int);
82static int aac_close_getadapter_fib(struct aac_softstate *, intptr_t);
83static int aac_send_raw_srb(struct aac_softstate *, dev_t, intptr_t, int);
84static int aac_get_pci_info(struct aac_softstate *, intptr_t, int);
85static int aac_query_disk(struct aac_softstate *, intptr_t, int);
86static int aac_delete_disk(struct aac_softstate *, intptr_t, int);
87static int aac_supported_features(struct aac_softstate *, intptr_t, int);
88
89/*
90 * Warlock directives
91 */
92_NOTE(SCHEME_PROTECTS_DATA("unique to each handling function", aac_features
93    aac_pci_info aac_query_disk aac_revision aac_umem_sge))
94
95int
96aac_do_ioctl(struct aac_softstate *softs, dev_t dev, int cmd, intptr_t arg,
97    int mode)
98{
99	int status;
100
101	switch (cmd) {
102	case FSACTL_MINIPORT_REV_CHECK:
103		AACDB_PRINT_IOCTL(softs, "FSACTL_MINIPORT_REV_CHECK");
104		status = aac_check_revision(softs, arg, mode);
105		break;
106	case FSACTL_SENDFIB:
107		AACDB_PRINT_IOCTL(softs, "FSACTL_SEND_LARGE_FIB");
108		goto send_fib;
109	case FSACTL_SEND_LARGE_FIB:
110		AACDB_PRINT_IOCTL(softs, "FSACTL_SEND_LARGE_FIB");
111send_fib:
112		status = aac_ioctl_send_fib(softs, arg, mode);
113		break;
114	case FSACTL_OPEN_GET_ADAPTER_FIB:
115		AACDB_PRINT_IOCTL(softs, "FSACTL_OPEN_GET_ADAPTER_FIB");
116		status = aac_open_getadapter_fib(softs, arg, mode);
117		break;
118	case FSACTL_GET_NEXT_ADAPTER_FIB:
119		AACDB_PRINT_IOCTL(softs, "FSACTL_GET_NEXT_ADAPTER_FIB");
120		status = aac_next_getadapter_fib(softs, arg, mode);
121		break;
122	case FSACTL_CLOSE_GET_ADAPTER_FIB:
123		AACDB_PRINT_IOCTL(softs, "FSACTL_CLOSE_GET_ADAPTER_FIB");
124		status = aac_close_getadapter_fib(softs, arg);
125		break;
126	case FSACTL_SEND_RAW_SRB:
127		AACDB_PRINT_IOCTL(softs, "FSACTL_SEND_RAW_SRB");
128		status = aac_send_raw_srb(softs, dev, arg, mode);
129		break;
130	case FSACTL_GET_PCI_INFO:
131		AACDB_PRINT_IOCTL(softs, "FSACTL_GET_PCI_INFO");
132		status = aac_get_pci_info(softs, arg, mode);
133		break;
134	case FSACTL_QUERY_DISK:
135		AACDB_PRINT_IOCTL(softs, "FSACTL_QUERY_DISK");
136		status = aac_query_disk(softs, arg, mode);
137		break;
138	case FSACTL_DELETE_DISK:
139		AACDB_PRINT_IOCTL(softs, "FSACTL_DELETE_DISK");
140		status = aac_delete_disk(softs, arg, mode);
141		break;
142	case FSACTL_GET_FEATURES:
143		AACDB_PRINT_IOCTL(softs, "FSACTL_GET_FEATURES");
144		status = aac_supported_features(softs, arg, mode);
145		break;
146	default:
147		status = ENOTTY;
148		AACDB_PRINT(softs, CE_WARN,
149		    "!IOCTL cmd 0x%x not supported", cmd);
150		break;
151	}
152
153	return (status);
154}
155
156/*ARGSUSED*/
157static int
158aac_check_revision(struct aac_softstate *softs, intptr_t arg, int mode)
159{
160	union aac_revision_align un;
161	struct aac_revision *aac_rev = &un.d;
162
163	DBCALLED(softs, 2);
164
165	/* Copyin the revision struct from userspace */
166	if (ddi_copyin((void *)arg, aac_rev,
167	    sizeof (struct aac_revision), mode) != 0)
168		return (EFAULT);
169
170	/* Doctor up the response struct */
171	aac_rev->compat = 1;
172	aac_rev->version =
173	    ((uint32_t)AAC_DRIVER_MAJOR_VERSION << 24) |
174	    ((uint32_t)AAC_DRIVER_MINOR_VERSION << 16) |
175	    ((uint32_t)AAC_DRIVER_TYPE << 8) |
176	    ((uint32_t)AAC_DRIVER_BUGFIX_LEVEL);
177	aac_rev->build = (uint32_t)AAC_DRIVER_BUILD;
178
179	if (ddi_copyout(aac_rev, (void *)arg,
180	    sizeof (struct aac_revision), mode) != 0)
181		return (EFAULT);
182
183	return (0);
184}
185
186static int
187aac_send_fib(struct aac_softstate *softs, struct aac_cmd *acp)
188{
189	int rval;
190
191	acp->flags |= AAC_CMD_NO_CB | AAC_CMD_SYNC;
192	acp->ac_comp = aac_ioctl_complete;
193
194	mutex_enter(&softs->io_lock);
195	if (softs->state & AAC_STATE_DEAD) {
196		mutex_exit(&softs->io_lock);
197		return (ENXIO);
198	}
199
200	rval = aac_do_io(softs, acp);
201	if (rval == TRAN_ACCEPT) {
202		rval = 0;
203	} else if (rval == TRAN_BADPKT) {
204		AACDB_PRINT(softs, CE_CONT, "User SendFib failed ENXIO");
205		rval = ENXIO;
206	} else if (rval == TRAN_BUSY) {
207		AACDB_PRINT(softs, CE_CONT, "User SendFib failed EBUSY");
208		rval = EBUSY;
209	}
210	mutex_exit(&softs->io_lock);
211
212	return (rval);
213}
214
215static int
216aac_ioctl_send_fib(struct aac_softstate *softs, intptr_t arg, int mode)
217{
218	int hbalen;
219	struct aac_cmd *acp;
220	struct aac_fib *fibp;
221	uint16_t fib_command;
222	uint32_t fib_xfer_state;
223	uint16_t fib_data_size, fib_size;
224	uint16_t fib_sender_size;
225	int rval;
226
227	DBCALLED(softs, 2);
228
229	/* Copy in FIB header */
230	hbalen = sizeof (struct aac_cmd) + softs->aac_max_fib_size;
231	if ((acp = kmem_zalloc(hbalen, KM_NOSLEEP)) == NULL)
232		return (ENOMEM);
233
234	fibp = (struct aac_fib *)(acp + 1);
235	acp->fibp = fibp;
236	if (ddi_copyin((void *)arg, fibp,
237	    sizeof (struct aac_fib_header), mode) != 0) {
238		rval = EFAULT;
239		goto finish;
240	}
241
242	fib_xfer_state = LE_32(fibp->Header.XferState);
243	fib_command = LE_16(fibp->Header.Command);
244	fib_data_size = LE_16(fibp->Header.Size);
245	fib_sender_size = LE_16(fibp->Header.SenderSize);
246
247	fib_size = fib_data_size + sizeof (struct aac_fib_header);
248	if (fib_size < fib_sender_size)
249		fib_size = fib_sender_size;
250	if (fib_size > softs->aac_max_fib_size) {
251		rval = EFAULT;
252		goto finish;
253	}
254
255	/* Copy in FIB data */
256	if (ddi_copyin(((struct aac_fib *)arg)->data, fibp->data,
257	    fib_data_size, mode) != 0) {
258		rval = EFAULT;
259		goto finish;
260	}
261	acp->fib_size = fib_size;
262	fibp->Header.Size = LE_16(fib_size);
263
264	AACDB_PRINT_FIB(softs, fibp);
265
266	/* Process FIB */
267	if (fib_command == TakeABreakPt) {
268		(void) aac_sync_mbcommand(softs, AAC_BREAKPOINT_REQ,
269		    0, 0, 0, 0, NULL);
270		fibp->Header.XferState = LE_32(0);
271	} else {
272		ASSERT(!(fib_xfer_state & AAC_FIBSTATE_ASYNC));
273		fibp->Header.XferState = LE_32(fib_xfer_state | \
274		    (AAC_FIBSTATE_FROMHOST | AAC_FIBSTATE_REXPECTED));
275
276		acp->timeout = AAC_IOCTL_TIMEOUT;
277		acp->aac_cmd_fib = aac_cmd_fib_copy;
278		if ((rval = aac_send_fib(softs, acp)) != 0)
279			goto finish;
280	}
281
282	if (acp->flags & AAC_CMD_ERR) {
283		AACDB_PRINT(softs, CE_CONT, "FIB data corrupt");
284		rval = EIO;
285		goto finish;
286	}
287
288	if (ddi_copyout(fibp, (void *)arg, acp->fib_size, mode) != 0) {
289		AACDB_PRINT(softs, CE_CONT, "FIB copyout failed");
290		rval = EFAULT;
291		goto finish;
292	}
293
294	rval = 0;
295finish:
296	kmem_free(acp, hbalen);
297	return (rval);
298}
299
300static int
301aac_open_getadapter_fib(struct aac_softstate *softs, intptr_t arg, int mode)
302{
303	struct aac_fib_context *fibctx, *ctx;
304
305	DBCALLED(softs, 2);
306
307	fibctx = kmem_zalloc(sizeof (struct aac_fib_context), KM_NOSLEEP);
308	if (fibctx == NULL)
309		return (ENOMEM);
310
311	mutex_enter(&softs->aifq_mutex);
312	/* All elements are already 0, add to queue */
313	if (softs->fibctx == NULL) {
314		softs->fibctx = fibctx;
315	} else {
316		for (ctx = softs->fibctx; ctx->next; ctx = ctx->next)
317			;
318		ctx->next = fibctx;
319		fibctx->prev = ctx;
320	}
321
322	/* Evaluate unique value */
323	fibctx->unique = (unsigned long)fibctx & 0xfffffffful;
324	ctx = softs->fibctx;
325	while (ctx != fibctx) {
326		if (ctx->unique == fibctx->unique) {
327			fibctx->unique++;
328			ctx = softs->fibctx;
329		} else {
330			ctx = ctx->next;
331		}
332	}
333
334	/* Set ctx_idx to the oldest AIF */
335	if (softs->aifq_wrap) {
336		fibctx->ctx_idx = softs->aifq_idx;
337		fibctx->ctx_filled = 1;
338	}
339	mutex_exit(&softs->aifq_mutex);
340
341	if (ddi_copyout(&fibctx->unique, (void *)arg,
342	    sizeof (uint32_t), mode) != 0)
343		return (EFAULT);
344
345	return (0);
346}
347
348static int
349aac_return_aif(struct aac_softstate *softs,
350    struct aac_fib_context *ctx, caddr_t uptr, int mode)
351{
352	int current;
353
354	current = ctx->ctx_idx;
355	if (current == softs->aifq_idx && !ctx->ctx_filled)
356		return (EAGAIN); /* Empty */
357	if (ddi_copyout(&softs->aifq[current].d, (void *)uptr,
358	    sizeof (struct aac_fib), mode) != 0)
359		return (EFAULT);
360
361	ctx->ctx_filled = 0;
362	ctx->ctx_idx = (current + 1) % AAC_AIFQ_LENGTH;
363
364	return (0);
365}
366
367static int
368aac_next_getadapter_fib(struct aac_softstate *softs, intptr_t arg, int mode)
369{
370	union aac_get_adapter_fib_align un;
371	struct aac_get_adapter_fib *af = &un.d;
372	struct aac_fib_context *ctx;
373	int rval;
374
375	DBCALLED(softs, 2);
376
377	if (ddi_copyin((void *)arg, af, sizeof (*af), mode) != 0)
378		return (EFAULT);
379
380	mutex_enter(&softs->aifq_mutex);
381	for (ctx = softs->fibctx; ctx; ctx = ctx->next) {
382		if (af->context == ctx->unique)
383			break;
384	}
385	if (ctx) {
386#ifdef	_LP64
387		rval = aac_return_aif(softs, ctx,
388		    (caddr_t)(uint64_t)af->aif_fib, mode);
389#else
390		rval = aac_return_aif(softs, ctx,
391		    (caddr_t)af->aif_fib, mode);
392#endif
393		if (rval == EAGAIN && af->wait) {
394			AACDB_PRINT(softs, CE_NOTE,
395			    "aac_next_getadapter_fib(): waiting for AIF");
396			rval = cv_wait_sig(&softs->aifv, &softs->aifq_mutex);
397			if (rval > 0) {
398#ifdef	_LP64
399				rval = aac_return_aif(softs, ctx,
400				    (caddr_t)(uint64_t)af->aif_fib, mode);
401#else
402				rval = aac_return_aif(softs, ctx,
403				    (caddr_t)af->aif_fib, mode);
404#endif
405			} else {
406				rval = EINTR;
407			}
408		}
409	} else {
410		rval = EFAULT;
411	}
412	mutex_exit(&softs->aifq_mutex);
413
414	return (rval);
415}
416
417static int
418aac_close_getadapter_fib(struct aac_softstate *softs, intptr_t arg)
419{
420	struct aac_fib_context *ctx;
421
422	DBCALLED(softs, 2);
423
424	mutex_enter(&softs->aifq_mutex);
425	for (ctx = softs->fibctx; ctx; ctx = ctx->next) {
426		if (ctx->unique != (uint32_t)arg)
427			continue;
428
429		if (ctx == softs->fibctx)
430			softs->fibctx = ctx->next;
431		else
432			ctx->prev->next = ctx->next;
433		if (ctx->next)
434			ctx->next->prev = ctx->prev;
435		break;
436	}
437	mutex_exit(&softs->aifq_mutex);
438	if (ctx)
439		kmem_free(ctx, sizeof (struct aac_fib_context));
440
441	return (0);
442}
443
444/*
445 * The following function comes from Adaptec:
446 *
447 * SRB is required for the new management tools
448 * Note: SRB passed down from IOCTL is always in CPU endianness.
449 */
450static int
451aac_send_raw_srb(struct aac_softstate *softs, dev_t dev, intptr_t arg, int mode)
452{
453	struct aac_cmd *acp;
454	struct aac_fib *fibp;
455	struct aac_srb *srb;
456	uint32_t usr_fib_size;
457	uint32_t srb_sgcount;
458	struct aac_umem_sge *usgt = NULL;
459	struct aac_umem_sge *usge;
460	ddi_umem_cookie_t cookie;
461	int umem_flags = 0;
462	int direct = 0;
463	int locked = 0;
464	caddr_t addrlo = (caddr_t)-1;
465	caddr_t addrhi = 0;
466	struct aac_sge *sge, *sge0;
467	int sg64;
468	int rval;
469
470	DBCALLED(softs, 2);
471
472	/* Read srb size */
473	if (ddi_copyin(&((struct aac_srb *)arg)->count, &usr_fib_size,
474	    sizeof (uint32_t), mode) != 0)
475		return (EFAULT);
476	if (usr_fib_size > (softs->aac_max_fib_size - \
477	    sizeof (struct aac_fib_header)))
478		return (EINVAL);
479
480	if ((acp = kmem_zalloc(sizeof (struct aac_cmd) + usr_fib_size + \
481	    sizeof (struct aac_fib_header), KM_NOSLEEP)) == NULL)
482		return (ENOMEM);
483
484	acp->fibp = (struct aac_fib *)(acp + 1);
485	fibp = acp->fibp;
486	srb = (struct aac_srb *)fibp->data;
487
488	/* Copy in srb */
489	if (ddi_copyin((void *)arg, srb, usr_fib_size, mode) != 0) {
490		rval = EFAULT;
491		goto finish;
492	}
493
494	srb_sgcount = srb->sg.SgCount; /* No endianness conversion needed */
495	if (srb_sgcount == 0)
496		goto send_fib;
497
498	/* Check FIB size */
499	if (usr_fib_size == (sizeof (struct aac_srb) + \
500	    srb_sgcount * sizeof (struct aac_sg_entry64) - \
501	    sizeof (struct aac_sg_entry))) {
502		sg64 = 1;
503	} else if (usr_fib_size == (sizeof (struct aac_srb) + \
504	    (srb_sgcount - 1) * sizeof (struct aac_sg_entry))) {
505		sg64 = 0;
506	} else {
507		rval = EINVAL;
508		goto finish;
509	}
510
511	/* Read user SG table */
512	if ((usgt = kmem_zalloc(sizeof (struct aac_umem_sge) * srb_sgcount,
513	    KM_NOSLEEP)) == NULL) {
514		rval = ENOMEM;
515		goto finish;
516	}
517	for (usge = usgt; usge < &usgt[srb_sgcount]; usge++) {
518		if (sg64) {
519			struct aac_sg_entry64 *sg64p =
520			    (struct aac_sg_entry64 *)srb->sg.SgEntry;
521
522			usge->bcount = sg64p->SgByteCount;
523			usge->addr = (caddr_t)
524#ifndef _LP64
525			    (uint32_t)
526#endif
527			    sg64p->SgAddress;
528		} else {
529			struct aac_sg_entry *sgp = srb->sg.SgEntry;
530
531			usge->bcount = sgp->SgByteCount;
532			usge->addr = (caddr_t)
533#ifdef _LP64
534			    (uint64_t)
535#endif
536			    sgp->SgAddress;
537		}
538		acp->bcount += usge->bcount;
539		if (usge->addr < addrlo)
540			addrlo = usge->addr;
541		if ((usge->addr + usge->bcount) > addrhi)
542			addrhi = usge->addr + usge->bcount;
543	}
544	if (acp->bcount > softs->buf_dma_attr.dma_attr_maxxfer) {
545		AACDB_PRINT(softs, CE_NOTE,
546		    "large srb xfer size received %d\n", acp->bcount);
547		rval = EINVAL;
548		goto finish;
549	}
550
551	/* Lock user buffers */
552	if (srb->flags & SRB_DataIn) {
553		umem_flags |= DDI_UMEMLOCK_READ;
554		direct |= B_READ;
555	}
556	if (srb->flags & SRB_DataOut) {
557		umem_flags |= DDI_UMEMLOCK_WRITE;
558		direct |= B_WRITE;
559	}
560	addrlo = (caddr_t)((uintptr_t)addrlo & (uintptr_t)PAGEMASK);
561	rval = ddi_umem_lock(addrlo, (((size_t)addrhi + PAGEOFFSET) & \
562	    PAGEMASK) - (size_t)addrlo, umem_flags, &cookie);
563	if (rval != 0) {
564		AACDB_PRINT(softs, CE_NOTE, "ddi_umem_lock failed: %d",
565		    rval);
566		goto finish;
567	}
568	locked = 1;
569
570	/* Allocate DMA for user buffers */
571	for (usge = usgt; usge < &usgt[srb_sgcount]; usge++) {
572		struct buf *bp;
573
574		bp = ddi_umem_iosetup(cookie, (uintptr_t)usge->addr - \
575		    (uintptr_t)addrlo, usge->bcount, direct, dev, 0, NULL,
576		    DDI_UMEM_SLEEP);
577		if (bp == NULL) {
578			AACDB_PRINT(softs, CE_NOTE, "ddi_umem_iosetup failed");
579			rval = EFAULT;
580			goto finish;
581		}
582		if (aac_cmd_dma_alloc(softs, &usge->acp, bp, 0, NULL_FUNC,
583		    0) != AACOK) {
584			rval = EFAULT;
585			goto finish;
586		}
587		acp->left_cookien += usge->acp.left_cookien;
588		if (acp->left_cookien > softs->aac_sg_tablesize) {
589			AACDB_PRINT(softs, CE_NOTE, "large cookiec received %d",
590			    acp->left_cookien);
591			rval = EINVAL;
592			goto finish;
593		}
594	}
595
596	/* Construct aac cmd SG table */
597	if ((sge = kmem_zalloc(sizeof (struct aac_sge) * acp->left_cookien,
598	    KM_NOSLEEP)) == NULL) {
599		rval = ENOMEM;
600		goto finish;
601	}
602	acp->sgt = sge;
603	for (usge = usgt; usge < &usgt[srb_sgcount]; usge++) {
604		for (sge0 = usge->acp.sgt;
605		    sge0 < &usge->acp.sgt[usge->acp.left_cookien];
606		    sge0++, sge++)
607			*sge = *sge0;
608	}
609
610send_fib:
611	acp->cmdlen = srb->cdb_size;
612	acp->timeout = srb->timeout;
613
614	/* Send FIB command */
615	AACDB_PRINT_FIB(softs, fibp);
616	acp->aac_cmd_fib = softs->aac_cmd_fib_scsi;
617	if ((rval = aac_send_fib(softs, acp)) != 0)
618		goto finish;
619
620	/* Status struct */
621	if (ddi_copyout((struct aac_srb_reply *)fibp->data,
622	    ((uint8_t *)arg + usr_fib_size),
623	    sizeof (struct aac_srb_reply), mode) != 0) {
624		rval = EFAULT;
625		goto finish;
626	}
627
628	rval = 0;
629finish:
630	if (acp->sgt)
631		kmem_free(acp->sgt, sizeof (struct aac_sge) * \
632		    acp->left_cookien);
633	if (usgt) {
634		for (usge = usgt; usge < &usgt[srb_sgcount]; usge++) {
635			if (usge->acp.sgt)
636				kmem_free(usge->acp.sgt,
637				    sizeof (struct aac_sge) * \
638				    usge->acp.left_cookien);
639			aac_free_dmamap(&usge->acp);
640			if (usge->acp.bp)
641				freerbuf(usge->acp.bp);
642		}
643		kmem_free(usgt, sizeof (struct aac_umem_sge) * srb_sgcount);
644	}
645	if (locked)
646		ddi_umem_unlock(cookie);
647	kmem_free(acp, sizeof (struct aac_cmd) + usr_fib_size + \
648	    sizeof (struct aac_fib_header));
649	return (rval);
650}
651
652/*ARGSUSED*/
653static int
654aac_get_pci_info(struct aac_softstate *softs, intptr_t arg, int mode)
655{
656	union aac_pci_info_align un;
657	struct aac_pci_info *resp = &un.d;
658
659	DBCALLED(softs, 2);
660
661	resp->bus = 0;
662	resp->slot = 0;
663
664	if (ddi_copyout(resp, (void *)arg,
665	    sizeof (struct aac_pci_info), mode) != 0)
666		return (EFAULT);
667	return (0);
668}
669
670static int
671aac_query_disk(struct aac_softstate *softs, intptr_t arg, int mode)
672{
673	union aac_query_disk_align un;
674	struct aac_query_disk *qdisk = &un.d;
675	struct aac_container *dvp;
676
677	DBCALLED(softs, 2);
678
679	if (ddi_copyin((void *)arg, qdisk, sizeof (*qdisk), mode) != 0)
680		return (EFAULT);
681
682	if (qdisk->container_no == -1) {
683		qdisk->container_no = qdisk->target * 16 + qdisk->lun;
684	} else if (qdisk->bus == -1 && qdisk->target == -1 &&
685	    qdisk->lun == -1) {
686		if (qdisk->container_no >= AAC_MAX_CONTAINERS)
687			return (EINVAL);
688		qdisk->bus = 0;
689		qdisk->target = (qdisk->container_no & 0xf);
690		qdisk->lun = (qdisk->container_no >> 4);
691	} else {
692		return (EINVAL);
693	}
694
695	mutex_enter(&softs->io_lock);
696	dvp = &softs->containers[qdisk->container_no];
697	qdisk->valid = dvp->valid;
698	qdisk->locked = dvp->locked;
699	qdisk->deleted = dvp->deleted;
700	mutex_exit(&softs->io_lock);
701
702	if (ddi_copyout(qdisk, (void *)arg, sizeof (*qdisk), mode) != 0)
703		return (EFAULT);
704	return (0);
705}
706
707static int
708aac_delete_disk(struct aac_softstate *softs, intptr_t arg, int mode)
709{
710	union aac_delete_disk_align un;
711	struct aac_delete_disk *ddisk = &un.d;
712	struct aac_container *dvp;
713	int rval = 0;
714
715	DBCALLED(softs, 2);
716
717	if (ddi_copyin((void *)arg, ddisk, sizeof (*ddisk), mode) != 0)
718		return (EFAULT);
719
720	if (ddisk->container_no >= AAC_MAX_CONTAINERS)
721		return (EINVAL);
722
723	mutex_enter(&softs->io_lock);
724	dvp = &softs->containers[ddisk->container_no];
725	/*
726	 * We don't trust the userland to tell us when to delete
727	 * a container, rather we rely on an AIF coming from the
728	 * controller.
729	 */
730	if (dvp->valid) {
731		if (dvp->locked)
732			rval = EBUSY;
733	}
734	mutex_exit(&softs->io_lock);
735
736	return (rval);
737}
738
739/*
740 * The following function comes from Adaptec to support creation of arrays
741 * bigger than 2TB.
742 */
743static int
744aac_supported_features(struct aac_softstate *softs, intptr_t arg, int mode)
745{
746	union aac_features_align un;
747	struct aac_features *f = &un.d;
748
749	DBCALLED(softs, 2);
750
751	if (ddi_copyin((void *)arg, f, sizeof (*f), mode) != 0)
752		return (EFAULT);
753
754	/*
755	 * When the management driver receives FSACTL_GET_FEATURES ioctl with
756	 * ALL zero in the featuresState, the driver will return the current
757	 * state of all the supported features, the data field will not be
758	 * valid.
759	 * When the management driver receives FSACTL_GET_FEATURES ioctl with
760	 * a specific bit set in the featuresState, the driver will return the
761	 * current state of this specific feature and whatever data that are
762	 * associated with the feature in the data field or perform whatever
763	 * action needed indicates in the data field.
764	 */
765	if (f->feat.fValue == 0) {
766		f->feat.fBits.largeLBA =
767		    (softs->flags & AAC_FLAGS_LBA_64BIT) ? 1 : 0;
768		/* TODO: In the future, add other features state here as well */
769	} else {
770		if (f->feat.fBits.largeLBA)
771			f->feat.fBits.largeLBA =
772			    (softs->flags & AAC_FLAGS_LBA_64BIT) ? 1 : 0;
773		/* TODO: Add other features state and data in the future */
774	}
775
776	if (ddi_copyout(f, (void *)arg, sizeof (*f), mode) != 0)
777		return (EFAULT);
778	return (0);
779}
780