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 2005 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 void 32 ghd_dmafree_attr(gcmd_t *gcmdp) 33 { 34 GDBG_DMA(("ghd_dma_attr_free: gcmdp 0x%p\n", gcmdp)); 35 36 if (gcmdp->cmd_dma_handle != NULL) { 37 if (ddi_dma_unbind_handle(gcmdp->cmd_dma_handle) != 38 DDI_SUCCESS) 39 cmn_err(CE_WARN, "ghd dma free attr: " 40 "unbind handle failed"); 41 ddi_dma_free_handle(&gcmdp->cmd_dma_handle); 42 GDBG_DMA(("ghd_dma_attr_free: ddi_dma_free 0x%p\n", gcmdp)); 43 gcmdp->cmd_dma_handle = NULL; 44 gcmdp->cmd_ccount = 0; 45 gcmdp->cmd_totxfer = 0; 46 } 47 } 48 49 50 int 51 ghd_dma_buf_bind_attr(ccc_t *cccp, 52 gcmd_t *gcmdp, 53 struct buf *bp, 54 int dma_flags, 55 int (*callback)(), 56 caddr_t arg, 57 ddi_dma_attr_t *sg_attrp) 58 { 59 int status; 60 61 GDBG_DMA(("ghd_dma_attr_get: start: gcmdp 0x%p sg_attrp 0x%p\n", 62 gcmdp, sg_attrp)); 63 64 65 /* 66 * First time, need to establish the handle. 67 */ 68 69 ASSERT(gcmdp->cmd_dma_handle == NULL); 70 71 status = ddi_dma_alloc_handle(cccp->ccc_hba_dip, sg_attrp, callback, 72 arg, &gcmdp->cmd_dma_handle); 73 74 if (status != DDI_SUCCESS) { 75 bp->b_error = 0; 76 return (FALSE); 77 } 78 79 status = ddi_dma_buf_bind_handle(gcmdp->cmd_dma_handle, bp, dma_flags, 80 callback, arg, &gcmdp->cmd_first_cookie, 81 &gcmdp->cmd_ccount); 82 83 GDBG_DMA(("ghd_dma_attr_get: setup: gcmdp 0x%p status %d h 0x%p " 84 "c 0x%d\n", gcmdp, status, gcmdp->cmd_dma_handle, 85 gcmdp->cmd_ccount)); 86 87 switch (status) { 88 case DDI_DMA_MAPPED: 89 /* enable first (and only) call to ddi_dma_getwin */ 90 gcmdp->cmd_wcount = 1; 91 break; 92 93 case DDI_DMA_PARTIAL_MAP: 94 /* enable first call to ddi_dma_getwin */ 95 if (ddi_dma_numwin(gcmdp->cmd_dma_handle, &gcmdp->cmd_wcount) != 96 DDI_SUCCESS) { 97 bp->b_error = 0; 98 ddi_dma_free_handle(&gcmdp->cmd_dma_handle); 99 gcmdp->cmd_dma_handle = NULL; 100 return (FALSE); 101 } 102 break; 103 104 case DDI_DMA_NORESOURCES: 105 bp->b_error = 0; 106 ddi_dma_free_handle(&gcmdp->cmd_dma_handle); 107 gcmdp->cmd_dma_handle = NULL; 108 return (FALSE); 109 110 case DDI_DMA_TOOBIG: 111 bioerror(bp, EINVAL); 112 ddi_dma_free_handle(&gcmdp->cmd_dma_handle); 113 gcmdp->cmd_dma_handle = NULL; 114 return (FALSE); 115 116 case DDI_DMA_NOMAPPING: 117 case DDI_DMA_INUSE: 118 default: 119 bioerror(bp, EFAULT); 120 ddi_dma_free_handle(&gcmdp->cmd_dma_handle); 121 gcmdp->cmd_dma_handle = NULL; 122 return (FALSE); 123 } 124 125 /* initialize the loop controls for ghd_dmaget_next_attr() */ 126 gcmdp->cmd_windex = 0; 127 gcmdp->cmd_cindex = 0; 128 gcmdp->cmd_totxfer = 0; 129 gcmdp->cmd_dma_flags = dma_flags; 130 gcmdp->use_first = 1; 131 return (TRUE); 132 } 133 134 135 uint_t 136 ghd_dmaget_next_attr(ccc_t *cccp, gcmd_t *gcmdp, long max_transfer_cnt, 137 int sg_size, ddi_dma_cookie_t cookie) 138 { 139 ulong_t toxfer = 0; 140 int num_segs = 0; 141 int single_seg; 142 143 GDBG_DMA(("ghd_dma_attr_get: start: gcmdp 0x%p h 0x%p c 0x%x\n", 144 gcmdp, gcmdp->cmd_dma_handle, gcmdp->cmd_ccount)); 145 146 /* 147 * Disable single-segment Scatter/Gather option 148 * if can't do this transfer in a single segment, 149 */ 150 if (gcmdp->cmd_cindex + 1 < gcmdp->cmd_ccount) { 151 single_seg = FALSE; 152 } else { 153 single_seg = TRUE; 154 } 155 156 157 for (;;) { 158 /* 159 * call the controller specific S/G function 160 */ 161 (*cccp->ccc_sg_func)(gcmdp, &cookie, single_seg, num_segs); 162 163 /* take care of the loop-bookkeeping */ 164 toxfer += cookie.dmac_size; 165 num_segs++; 166 gcmdp->cmd_cindex++; 167 168 /* 169 * if this was the last cookie in the current window 170 * set the loop controls start the next window and 171 * exit so the HBA can do this partial transfer 172 */ 173 if (gcmdp->cmd_cindex >= gcmdp->cmd_ccount) { 174 gcmdp->cmd_windex++; 175 gcmdp->cmd_cindex = 0; 176 break; 177 } 178 ASSERT(single_seg == FALSE); 179 180 if (toxfer >= max_transfer_cnt) 181 break; 182 183 if (num_segs >= sg_size) 184 break; 185 186 ddi_dma_nextcookie(gcmdp->cmd_dma_handle, &cookie); 187 } 188 189 gcmdp->cmd_totxfer += toxfer; 190 191 return (toxfer); 192 } 193 194 195 196 int 197 ghd_dmaget_attr(ccc_t *cccp, 198 gcmd_t *gcmdp, 199 long count, 200 int sg_size, 201 uint_t *xfer) 202 { 203 int status; 204 ddi_dma_cookie_t cookie; 205 206 *xfer = 0; 207 208 209 if (gcmdp->use_first == 1) { 210 cookie = gcmdp->cmd_first_cookie; 211 gcmdp->use_first = 0; 212 } else if (gcmdp->cmd_windex >= gcmdp->cmd_wcount) { 213 /* 214 * reached the end of buffer. This should not happen. 215 */ 216 ASSERT(gcmdp->cmd_windex < gcmdp->cmd_wcount); 217 return (FALSE); 218 219 } else if (gcmdp->cmd_cindex == 0) { 220 off_t offset; 221 size_t length; 222 223 /* 224 * start the next window, and get its first cookie 225 */ 226 status = ddi_dma_getwin(gcmdp->cmd_dma_handle, 227 gcmdp->cmd_windex, &offset, &length, 228 &cookie, &gcmdp->cmd_ccount); 229 if (status != DDI_SUCCESS) 230 return (FALSE); 231 232 } else { 233 /* 234 * get the next cookie in the current window 235 */ 236 ddi_dma_nextcookie(gcmdp->cmd_dma_handle, &cookie); 237 } 238 239 /* 240 * start the Scatter/Gather loop passing in the first 241 * cookie obtained above 242 */ 243 *xfer = ghd_dmaget_next_attr(cccp, gcmdp, count, sg_size, cookie); 244 return (TRUE); 245 } 246