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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
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 /*
30  * Utility DCD configuration routines.
31  */
32 
33 #include	<sys/dada/dada.h>
34 #include 	<sys/modctl.h>
35 
36 extern struct mod_ops mod_miscops;
37 
38 static struct modlmisc modlmisc = {
39 	&mod_miscops, 	/* Type of module */
40 	" ATA Bus Utility Routines"
41 };
42 
43 static struct modlinkage modlinkage = {
44 	MODREV_1, (void *)&modlmisc, NULL
45 };
46 
47 
48 
49 static int dcd_test(struct dcd_pkt *);
50 void makecommand(struct dcd_pkt *, int, uchar_t, uint32_t,
51 				uchar_t, uint32_t, uchar_t, uchar_t);
52 
53 int
_init()54 _init()
55 {
56 
57 	(void) dcd_initialize_hba_interface();
58 
59 	return (mod_install(&modlinkage));
60 }
61 
62 
63 /*
64  * There is no _fini() routine because this module is never unloaded.
65  */
66 int
_info(modinfop)67 _info(modinfop)
68 struct modinfo *modinfop;
69 {
70 
71 	return (mod_info(&modlinkage, modinfop));
72 }
73 
74 /*
75  * The implementation of dcd_probe allows a particular HBA to intercept the call
76  * for any post or pre-processing it may need. The default, if the HBA does not
77  * override it, is to call dcd_hba_probe.
78  */
79 int
dcd_probe(struct dcd_device * devp,int (* callback)())80 dcd_probe(struct dcd_device *devp, int (*callback)())
81 {
82 	dcd_hba_tran_t	*hba_tran = devp->dcd_address->a_hba_tran;
83 
84 	if (hba_tran->tran_tgt_probe != NULL) {
85 		return ((*hba_tran->tran_tgt_probe)(devp, callback));
86 	} else {
87 		return (dcd_hba_probe(devp, callback));
88 	}
89 }
90 
91 /*
92  * Undo the dcd_probe
93  */
94 void
dcd_unprobe(struct dcd_device * devp)95 dcd_unprobe(struct dcd_device *devp)
96 {
97 	if (devp->dcd_ident) {
98 		kmem_free((caddr_t)devp->dcd_ident, SUN_IDENTSIZE);
99 		devp->dcd_ident = (struct dcd_identify *)NULL;
100 	}
101 }
102 
103 #define	ROUTE   (devp->dcd_address)
104 
105 int
dcd_hba_probe(struct dcd_device * devp,int (* callback)())106 dcd_hba_probe(struct dcd_device *devp, int (*callback)())
107 {
108 
109 	struct dcd_pkt *ident_pkt = NULL;
110 	int rval = DCDPROBE_NOMEM;
111 	struct buf *ident_bp = NULL;
112 	int  (*cb_flag)();
113 
114 	if (devp->dcd_ident == NULL) {
115 #ifdef DEBUG1
116 		printf("Dcd_ident is NULL\n");
117 #endif
118 
119 		devp->dcd_ident = (struct dcd_identify *)
120 			kmem_alloc(SUN_IDENTSIZE, ((callback == SLEEP_FUNC)?
121 						KM_SLEEP : KM_NOSLEEP));
122 		if (devp->dcd_ident == NULL) {
123 				goto out;
124 		}
125 	}
126 
127 	if (callback != SLEEP_FUNC && callback != NULL_FUNC) {
128 		cb_flag = NULL_FUNC;
129 	} else {
130 		cb_flag = callback;
131 	}
132 
133 	ident_bp = dcd_alloc_consistent_buf(ROUTE, (struct buf *)NULL,
134 			(uint_t)SUN_IDENTSIZE, B_READ, cb_flag, NULL);
135 	if (ident_bp == NULL) {
136 		goto out;
137 	}
138 
139 	ident_pkt = dcd_init_pkt(ROUTE, (struct dcd_pkt *)NULL,
140 			ident_bp, sizeof (struct dcd_cmd), 2, 0,
141 			PKT_CONSISTENT,
142 			callback, NULL);
143 
144 	if (ident_pkt == NULL) {
145 		if (ident_bp->b_error == 0)
146 			rval = DCDPROBE_NOMEM_CB;
147 		goto out;
148 	}
149 
150 	bp_mapin(ident_bp);
151 
152 	bzero((caddr_t)devp->dcd_ident, SUN_IDENTSIZE);
153 
154 	makecommand(ident_pkt, FLAG_NOINTR, IDENTIFY, 0, ADD_LBA_MODE,
155 		SUN_IDENTSIZE, DATA_READ, 0);
156 
157 	/*
158 	 * The first identify will tell us whether the target responded
159 	 * or not.
160 	 */
161 
162 	if (dcd_test(ident_pkt) < 0) {
163 #ifdef DEBUG1
164 	printf("dcd_test: failed\n");
165 #endif
166 		if (ident_pkt->pkt_reason == CMD_INCOMPLETE) {
167 			rval = DCDPROBE_NORESP;
168 			goto out;
169 		} else {
170 			/*
171 			 * retry one more time
172 			 */
173 			if (dcd_test(ident_pkt) < 0) {
174 				rval = DCDPROBE_FAILURE;
175 				goto out;
176 			}
177 		}
178 	}
179 
180 #ifdef DEBUG1
181 	printf("Pkt reason %x, scsbp %x\n", ident_pkt->pkt_reason,
182 		*ident_pkt->pkt_scbp);
183 #endif
184 	/*
185 	 * If we are lucky, this identify succeeded
186 	 */
187 	if ((ident_pkt->pkt_reason == CMD_CMPLT) &&
188 		(((*ident_pkt->pkt_scbp) & STATUS_ATA_MASK) == 0)) {
189 		goto done;
190 	}
191 
192 	/*
193 	 * the second inquiry, allows the host adapters to try again.
194 	 */
195 	if (dcd_test(ident_pkt) < 0) {
196 		if (ident_pkt->pkt_reason == CMD_INCOMPLETE)
197 			rval = DCDPROBE_NORESP;
198 		else
199 			rval = DCDPROBE_FAILURE;
200 		goto out;
201 	}
202 
203 	/*
204 	 * At this point we are guarenteed that something responded
205 	 * to this target. We don't know yest what kind of device it is.
206 	 */
207 
208 	if (dcd_test(ident_pkt) < 0) {
209 		rval = DCDPROBE_FAILURE;
210 		goto out;
211 	}
212 
213 done:
214 	/*
215 	 * If we got no error then receive the indentify data,
216 	 */
217 	if ((ident_pkt->pkt_state & STATE_XFERRED_DATA) == 0 &&
218 		ident_pkt->pkt_resid > 0) {
219 		rval = DCDPROBE_NONCCS;
220 	} else {
221 		bcopy((caddr_t)ident_bp->b_un.b_addr,
222 			(caddr_t)devp->dcd_ident, SUN_IDENTSIZE);
223 		rval = DCDPROBE_EXISTS;
224 	}
225 
226 out:
227 	if (ident_pkt) {
228 		dcd_destroy_pkt(ident_pkt);
229 	}
230 	if (ident_bp) {
231 		dcd_free_consistent_buf(ident_bp);
232 	}
233 	return (rval);
234 }
235 
236 
237 static int
dcd_test(struct dcd_pkt * pkt)238 dcd_test(struct dcd_pkt *pkt)
239 {
240 
241 	int rval = -1;
242 
243 	pkt->pkt_flags |= FLAG_NOINTR;
244 	pkt->pkt_time = DCD_POLL_TIMEOUT;
245 
246 #ifdef DEBUG1
247 	printf("flags %x: timeout %x\n", pkt->pkt_flags, pkt->pkt_time);
248 #endif
249 
250 	if (dcd_transport(pkt) != TRAN_ACCEPT) {
251 		goto error;
252 	} else if (pkt->pkt_reason == CMD_INCOMPLETE &&
253 			pkt->pkt_state == 0) {
254 		goto error;
255 	} else if (pkt->pkt_reason != CMD_CMPLT) {
256 		goto error;
257 	} else if (((*pkt->pkt_scbp) & STATUS_ATA_MASK) == STATUS_ATA_BUSY) {
258 		rval = 0;
259 	} else {
260 		rval = 0;
261 	}
262 error:
263 #ifdef DEBUG1
264 	printf("dcd_test: rval is %x\n", rval);
265 #endif
266 
267 	return (rval);
268 }
269 
270 void
makecommand(struct dcd_pkt * pkt,int flags,uchar_t command,uint32_t block,uchar_t address_mode,uint32_t size,uchar_t direction,uchar_t features)271 makecommand(struct dcd_pkt *pkt,
272 		int	flags,
273 		uchar_t	command,
274 		uint32_t block,
275 		uchar_t	address_mode,
276 		uint32_t size,
277 		uchar_t	direction,
278 		uchar_t	features)
279 {
280 
281 	struct	dcd_cmd *cdbp = (struct dcd_cmd *)pkt->pkt_cdbp;
282 
283 	cdbp->cmd = command;
284 	cdbp->sector_num.lba_num = block;
285 	cdbp->address_mode = address_mode;
286 	cdbp->direction = direction;
287 	cdbp->size = size;	/* Size in bytes */
288 	cdbp->features = features;
289 
290 	pkt->pkt_flags = flags;
291 #ifdef DEBUG1
292 	printf("pkt flags set in dada %x\n", pkt->pkt_flags);
293 
294 	printf("command %x, flags %x, block %x, address_mode %x, size %x\n",
295 		command, flags, block, address_mode, size);
296 #endif
297 
298 
299 }
300