1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include "ghd.h"
30 
31 
32 /*
33  * Local Function Prototypes
34  */
35 
36 static	struct scsi_pkt	*ghd_pktalloc(ccc_t *cccp, struct scsi_address *ap,
37 				int cmdlen, int statuslen, int tgtlen,
38 				int (*callback)(), caddr_t arg, int ccblen);
39 
40 /*
41  * Round up all allocations so that we can guarantee
42  * long-long alignment.  This is the same alignment
43  * provided by kmem_alloc().
44  */
45 #define	ROUNDUP(x)	(((x) + 0x07) & ~0x07)
46 
47 /*
48  * Private wrapper for gcmd_t
49  */
50 
51 /*
52  * round up the size so the HBA private area is on a 8 byte boundary
53  */
54 #define	GW_PADDED_LENGTH	ROUNDUP(sizeof (gcmd_t))
55 
56 typedef struct gcmd_padded_wrapper {
57 	union {
58 		gcmd_t	gw_gcmd;
59 		char	gw_pad[GW_PADDED_LENGTH];
60 
61 	} gwrap;
62 } gwrap_t;
63 
64 
65 
66 
67 /*ARGSUSED*/
68 void
69 ghd_tran_sync_pkt(struct scsi_address *ap, struct scsi_pkt *pktp)
70 {
71 	gcmd_t *gcmdp = PKTP2GCMDP(pktp);
72 	int	status;
73 
74 	if (gcmdp->cmd_dma_handle) {
75 		status = ddi_dma_sync(gcmdp->cmd_dma_handle, 0, 0,
76 		    (gcmdp->cmd_dma_flags & DDI_DMA_READ) ?
77 		    DDI_DMA_SYNC_FORCPU : DDI_DMA_SYNC_FORDEV);
78 		if (status != DDI_SUCCESS) {
79 			cmn_err(CE_WARN, "ghd_tran_sync_pkt() fail\n");
80 		}
81 	}
82 }
83 
84 
85 static struct scsi_pkt *
86 ghd_pktalloc(ccc_t	*cccp,
87 	struct scsi_address *ap,
88 	int	 cmdlen,
89 	int	 statuslen,
90 	int	 tgtlen,
91 	int	(*callback)(),
92 	caddr_t	 arg,
93 	int	ccblen)
94 {
95 	gtgt_t		*gtgtp =  ADDR2GTGTP(ap);
96 	struct scsi_pkt	*pktp;
97 	gcmd_t		*gcmdp;
98 	gwrap_t		*gwp;
99 	int		 gwrap_len;
100 
101 	gwrap_len = sizeof (gwrap_t) + ROUNDUP(ccblen);
102 
103 	/* allocate everything from kmem pool */
104 	pktp = scsi_hba_pkt_alloc(cccp->ccc_hba_dip, ap, cmdlen, statuslen,
105 	    tgtlen, gwrap_len, callback, arg);
106 	if (pktp == NULL) {
107 		return (NULL);
108 	}
109 
110 	/* get the ptr to the HBA specific buffer */
111 	gwp = (gwrap_t *)(pktp->pkt_ha_private);
112 
113 	/* get the ptr to the GHD specific buffer */
114 	gcmdp = &gwp->gwrap.gw_gcmd;
115 
116 	ASSERT((caddr_t)gwp == (caddr_t)gcmdp);
117 
118 	/*
119 	 * save the ptr to HBA private area and initialize the rest
120 	 * of the gcmd_t members
121 	 */
122 	GHD_GCMD_INIT(gcmdp, (void *)(gwp + 1), gtgtp);
123 
124 	/*
125 	 * save the the scsi_pkt ptr in gcmd_t.
126 	 */
127 	gcmdp->cmd_pktp = pktp;
128 
129 	/*
130 	 * callback to the HBA driver so it can initalize its
131 	 * buffer and return the ptr to my cmd_t structure which is
132 	 * probably embedded in its buffer.
133 	 */
134 
135 	if (!(*cccp->ccc_ccballoc)(gtgtp, gcmdp, cmdlen, statuslen, tgtlen,
136 	    ccblen)) {
137 		scsi_hba_pkt_free(ap, pktp);
138 		return (NULL);
139 	}
140 
141 	return (pktp);
142 }
143 
144 
145 
146 /*
147  * packet free
148  */
149 /*ARGSUSED*/
150 void
151 ghd_pktfree(ccc_t		*cccp,
152 	struct scsi_address	*ap,
153 	struct scsi_pkt		*pktp)
154 {
155 	GDBG_PKT(("ghd_pktfree: cccp 0x%p ap 0x%p pktp 0x%p\n",
156 	    (void *)cccp, (void *)ap, (void *)pktp));
157 
158 	/* free any extra resources allocated by the HBA */
159 	(*cccp->ccc_ccbfree)(PKTP2GCMDP(pktp));
160 
161 	/* free the scsi_pkt and the GHD and HBA private areas */
162 	scsi_hba_pkt_free(ap, pktp);
163 }
164 
165 
166 struct scsi_pkt *
167 ghd_tran_init_pkt_attr(ccc_t	*cccp,
168 			struct scsi_address *ap,
169 			struct scsi_pkt	*pktp,
170 			struct buf	*bp,
171 			int	 cmdlen,
172 			int	 statuslen,
173 			int	 tgtlen,
174 			int	 flags,
175 			int	(*callback)(),
176 			caddr_t	 arg,
177 			int	 ccblen,
178 			ddi_dma_attr_t	*sg_attrp)
179 {
180 	gcmd_t	*gcmdp;
181 	int	 new_pkt;
182 	uint_t	xfercount;
183 
184 	ASSERT(callback == NULL_FUNC || callback == SLEEP_FUNC);
185 
186 	/*
187 	 * Allocate a pkt
188 	 */
189 	if (pktp == NULL) {
190 		pktp = ghd_pktalloc(cccp, ap, cmdlen, statuslen, tgtlen,
191 		    callback, arg, ccblen);
192 		if (pktp == NULL)
193 			return (NULL);
194 		new_pkt = TRUE;
195 
196 	} else {
197 		new_pkt = FALSE;
198 
199 	}
200 
201 	gcmdp = PKTP2GCMDP(pktp);
202 
203 	GDBG_PKT(("ghd_tran_init_pkt_attr: gcmdp 0x%p dma_handle 0x%p\n",
204 	    (void *)gcmdp, (void *)gcmdp->cmd_dma_handle));
205 
206 	/*
207 	 * free stale DMA window if necessary.
208 	 */
209 
210 	if (cmdlen && gcmdp->cmd_dma_handle) {
211 		/* release the old DMA resources */
212 		ghd_dmafree_attr(gcmdp);
213 	}
214 
215 	/*
216 	 * Set up dma info if there's any data and
217 	 * if the device supports DMA.
218 	 */
219 
220 	GDBG_PKT(("ghd_tran_init_pkt: gcmdp 0x%p bp 0x%p limp 0x%p\n",
221 	    (void *)gcmdp, (void *)bp, (void *)sg_attrp));
222 
223 	if (bp && bp->b_bcount && sg_attrp) {
224 		int	dma_flags;
225 
226 		/* check direction for data transfer */
227 		if (bp->b_flags & B_READ)
228 			dma_flags = DDI_DMA_READ;
229 		else
230 			dma_flags = DDI_DMA_WRITE;
231 
232 		/* check dma option flags */
233 		if (flags & PKT_CONSISTENT)
234 			dma_flags |= DDI_DMA_CONSISTENT;
235 		if (flags & PKT_DMA_PARTIAL)
236 			dma_flags |= DDI_DMA_PARTIAL;
237 
238 		if (gcmdp->cmd_dma_handle == NULL) {
239 			if (!ghd_dma_buf_bind_attr(cccp, gcmdp, bp, dma_flags,
240 			    callback, arg, sg_attrp)) {
241 				if (new_pkt)
242 					ghd_pktfree(cccp, ap, pktp);
243 				return (NULL);
244 			}
245 		}
246 
247 		/* map the buffer and/or create the scatter/gather list */
248 		if (!ghd_dmaget_attr(cccp, gcmdp,
249 		    bp->b_bcount - gcmdp->cmd_totxfer,
250 		    sg_attrp->dma_attr_sgllen, &xfercount)) {
251 			if (new_pkt)
252 				ghd_pktfree(cccp, ap, pktp);
253 			return (NULL);
254 		}
255 		pktp->pkt_resid = bp->b_bcount - gcmdp->cmd_totxfer;
256 	} else {
257 		pktp->pkt_resid = 0;
258 	}
259 
260 	return (pktp);
261 }
262