1 /* @(#)modesense.c	1.158 09/07/10 Copyright 1995-2009 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static	UConst char sccsid[] =
5 	"@(#)modesense.c	1.158 09/07/10 Copyright 1995-2009 J. Schilling";
6 #endif
7 /*
8  *	SCSI command functions for mode sense/mode select handling.
9  *
10  *	Copyright (c) 1995-2009 J. Schilling
11  */
12 /*
13  * The contents of this file are subject to the terms of the
14  * Common Development and Distribution License, Version 1.0 only
15  * (the "License").  You may not use this file except in compliance
16  * with the License.
17  *
18  * See the file CDDL.Schily.txt in this distribution for details.
19  * A copy of the CDDL is also available via the Internet at
20  * http://www.opensource.org/licenses/cddl1.txt
21  *
22  * When distributing Covered Code, include this CDDL HEADER in each
23  * file and include the License file CDDL.Schily.txt from this distribution.
24  */
25 
26 #include <schily/stdio.h>
27 #include <schily/standard.h>
28 
29 #include <schily/utypes.h>
30 #include <schily/btorder.h>
31 #include <schily/intcvt.h>
32 #include <schily/schily.h>
33 
34 #include <scg/scgcmd.h>
35 #include <scg/scsidefs.h>
36 #include <scg/scsireg.h>
37 #include <scg/scsitransp.h>
38 
39 #include "libscgcmd.h"
40 
41 EXPORT	BOOL	__is_atapi	__PR((void));
42 EXPORT	BOOL	allow_atapi	__PR((SCSI *scgp, BOOL new));
43 EXPORT	int	mode_select	__PR((SCSI *scgp, Uchar *, int, int, int));
44 EXPORT	int	mode_sense	__PR((SCSI *scgp, Uchar *dp, int cnt, int page, int pcf));
45 EXPORT	int	mode_select_sg0	__PR((SCSI *scgp, Uchar *, int, int, int));
46 EXPORT	int	mode_sense_sg0	__PR((SCSI *scgp, Uchar *dp, int cnt, int page, int pcf));
47 EXPORT	int	mode_select_g0	__PR((SCSI *scgp, Uchar *, int, int, int));
48 EXPORT	int	mode_select_g1	__PR((SCSI *scgp, Uchar *, int, int, int));
49 EXPORT	int	mode_sense_g0	__PR((SCSI *scgp, Uchar *dp, int cnt, int page, int pcf));
50 EXPORT	int	mode_sense_g1	__PR((SCSI *scgp, Uchar *dp, int cnt, int page, int pcf));
51 
52 /*
53  * XXX First try to handle ATAPI:
54  * XXX ATAPI cannot handle SCSI 6 byte commands.
55  * XXX We try to simulate 6 byte mode sense/select.
56  */
57 LOCAL BOOL	is_atapi;
58 
59 EXPORT BOOL
__is_atapi()60 __is_atapi()
61 {
62 	return (is_atapi);
63 }
64 
65 EXPORT BOOL
allow_atapi(scgp,new)66 allow_atapi(scgp, new)
67 	SCSI	*scgp;
68 	BOOL	new;
69 {
70 	BOOL	old = is_atapi;
71 	Uchar	mode[256];
72 
73 	if (new == old)
74 		return (old);
75 
76 	scgp->silent++;
77 	/*
78 	 * If a bad drive has been reset before, we may need to fire up two
79 	 * test unit ready commands to clear status.
80 	 */
81 	(void) unit_ready(scgp);
82 	if (new &&
83 	    mode_sense_g1(scgp, mode, 8, 0x3F, 0) < 0) {	/* All pages current */
84 		new = FALSE;
85 	}
86 	scgp->silent--;
87 
88 	is_atapi = new;
89 	return (old);
90 }
91 
92 EXPORT int
mode_select(scgp,dp,cnt,smp,pf)93 mode_select(scgp, dp, cnt, smp, pf)
94 	SCSI	*scgp;
95 	Uchar	*dp;
96 	int	cnt;
97 	int	smp;
98 	int	pf;
99 {
100 	if (is_atapi)
101 		return (mode_select_sg0(scgp, dp, cnt, smp, pf));
102 	return (mode_select_g0(scgp, dp, cnt, smp, pf));
103 }
104 
105 EXPORT int
mode_sense(scgp,dp,cnt,page,pcf)106 mode_sense(scgp, dp, cnt, page, pcf)
107 	SCSI	*scgp;
108 	Uchar	*dp;
109 	int	cnt;
110 	int	page;
111 	int	pcf;
112 {
113 	if (is_atapi)
114 		return (mode_sense_sg0(scgp, dp, cnt, page, pcf));
115 	return (mode_sense_g0(scgp, dp, cnt, page, pcf));
116 }
117 
118 /*
119  * Simulate mode select g0 with mode select g1.
120  */
121 EXPORT int
mode_select_sg0(scgp,dp,cnt,smp,pf)122 mode_select_sg0(scgp, dp, cnt, smp, pf)
123 	SCSI	*scgp;
124 	Uchar	*dp;
125 	int	cnt;
126 	int	smp;
127 	int	pf;
128 {
129 	Uchar	xmode[256+4];
130 	int	amt = cnt;
131 
132 	if (amt < 1 || amt > 255) {
133 		/* XXX clear SCSI error codes ??? */
134 		return (-1);
135 	}
136 
137 	if (amt < 4) {		/* Data length. medium type & VU */
138 		amt += 1;
139 	} else {
140 		amt += 4;
141 		movebytes(&dp[4], &xmode[8], cnt-4);
142 	}
143 	xmode[0] = 0;
144 	xmode[1] = 0;
145 	xmode[2] = dp[1];
146 	xmode[3] = dp[2];
147 	xmode[4] = 0;
148 	xmode[5] = 0;
149 	i_to_2_byte(&xmode[6], (unsigned int)dp[3]);
150 
151 	if (scgp->verbose) scg_prbytes("Mode Parameters (un-converted)", dp, cnt);
152 
153 	return (mode_select_g1(scgp, xmode, amt, smp, pf));
154 }
155 
156 /*
157  * Simulate mode sense g0 with mode sense g1.
158  */
159 EXPORT int
mode_sense_sg0(scgp,dp,cnt,page,pcf)160 mode_sense_sg0(scgp, dp, cnt, page, pcf)
161 	SCSI	*scgp;
162 	Uchar	*dp;
163 	int	cnt;
164 	int	page;
165 	int	pcf;
166 {
167 	Uchar	xmode[256+4];
168 	int	amt = cnt;
169 	int	len;
170 
171 	if (amt < 1 || amt > 255) {
172 		/* XXX clear SCSI error codes ??? */
173 		return (-1);
174 	}
175 
176 	fillbytes((caddr_t)xmode, sizeof (xmode), '\0');
177 	if (amt < 4) {		/* Data length. medium type & VU */
178 		amt += 1;
179 	} else {
180 		amt += 4;
181 	}
182 	if (mode_sense_g1(scgp, xmode, amt, page, pcf) < 0)
183 		return (-1);
184 
185 	amt = cnt - scg_getresid(scgp);
186 /*
187  * For tests: Solaris 8 & LG CD-ROM always returns resid == amt
188  */
189 /*	amt = cnt;*/
190 	if (amt > 4)
191 		movebytes(&xmode[8], &dp[4], amt-4);
192 	len = a_to_u_2_byte(xmode);
193 	if (len == 0) {
194 		dp[0] = 0;
195 	} else if (len < 6) {
196 		if (len > 2)
197 			len = 2;
198 		dp[0] = len;
199 	} else {
200 		dp[0] = len - 3;
201 	}
202 	dp[1] = xmode[2];
203 	dp[2] = xmode[3];
204 	len = a_to_u_2_byte(&xmode[6]);
205 	dp[3] = len;
206 
207 	if (scgp->verbose) scg_prbytes("Mode Sense Data (converted)", dp, amt);
208 	return (0);
209 }
210 
211 EXPORT int
mode_select_g0(scgp,dp,cnt,smp,pf)212 mode_select_g0(scgp, dp, cnt, smp, pf)
213 	SCSI	*scgp;
214 	Uchar	*dp;
215 	int	cnt;
216 	int	smp;
217 	int	pf;
218 {
219 	register struct	scg_cmd	*scmd = scgp->scmd;
220 
221 	fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
222 	scmd->addr = (caddr_t)dp;
223 	scmd->size = cnt;
224 	scmd->flags = SCG_DISRE_ENA;
225 	scmd->cdb_len = SC_G0_CDBLEN;
226 	scmd->sense_len = CCS_SENSE_LEN;
227 	scmd->cdb.g0_cdb.cmd = SC_MODE_SELECT;
228 	scmd->cdb.g0_cdb.lun = scg_lun(scgp);
229 	scmd->cdb.g0_cdb.high_addr = smp ? 1 : 0 | pf ? 0x10 : 0;
230 	scmd->cdb.g0_cdb.count = cnt;
231 
232 	if (scgp->verbose) {
233 		error("%s ", smp?"Save":"Set ");
234 		scg_prbytes("Mode Parameters", dp, cnt);
235 	}
236 
237 	scgp->cmdname = "mode select g0";
238 
239 	return (scg_cmd(scgp));
240 }
241 
242 EXPORT int
mode_select_g1(scgp,dp,cnt,smp,pf)243 mode_select_g1(scgp, dp, cnt, smp, pf)
244 	SCSI	*scgp;
245 	Uchar	*dp;
246 	int	cnt;
247 	int	smp;
248 	int	pf;
249 {
250 	register struct	scg_cmd	*scmd = scgp->scmd;
251 
252 	fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
253 	scmd->addr = (caddr_t)dp;
254 	scmd->size = cnt;
255 	scmd->flags = SCG_DISRE_ENA;
256 	scmd->cdb_len = SC_G1_CDBLEN;
257 	scmd->sense_len = CCS_SENSE_LEN;
258 	scmd->cdb.g1_cdb.cmd = 0x55;
259 	scmd->cdb.g1_cdb.lun = scg_lun(scgp);
260 	scmd->cdb.g0_cdb.high_addr = smp ? 1 : 0 | pf ? 0x10 : 0;
261 	g1_cdblen(&scmd->cdb.g1_cdb, cnt);
262 
263 	if (scgp->verbose) {
264 		printf("%s ", smp?"Save":"Set ");
265 		scg_prbytes("Mode Parameters", dp, cnt);
266 	}
267 
268 	scgp->cmdname = "mode select g1";
269 
270 	return (scg_cmd(scgp));
271 }
272 
273 EXPORT int
mode_sense_g0(scgp,dp,cnt,page,pcf)274 mode_sense_g0(scgp, dp, cnt, page, pcf)
275 	SCSI	*scgp;
276 	Uchar	*dp;
277 	int	cnt;
278 	int	page;
279 	int	pcf;
280 {
281 	register struct	scg_cmd	*scmd = scgp->scmd;
282 
283 	fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
284 	scmd->addr = (caddr_t)dp;
285 	scmd->size = 0xFF;
286 	scmd->size = cnt;
287 	scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA;
288 	scmd->cdb_len = SC_G0_CDBLEN;
289 	scmd->sense_len = CCS_SENSE_LEN;
290 	scmd->cdb.g0_cdb.cmd = SC_MODE_SENSE;
291 	scmd->cdb.g0_cdb.lun = scg_lun(scgp);
292 #ifdef	nonono
293 	scmd->cdb.g0_cdb.high_addr = 1<<4;	/* DBD Disable Block desc. */
294 #endif
295 	scmd->cdb.g0_cdb.mid_addr = (page&0x3F) | ((pcf<<6)&0xC0);
296 	scmd->cdb.g0_cdb.count = page ? 0xFF : 24;
297 	scmd->cdb.g0_cdb.count = cnt;
298 
299 	scgp->cmdname = "mode sense g0";
300 
301 	if (scg_cmd(scgp) < 0)
302 		return (-1);
303 	if (scgp->verbose) scg_prbytes("Mode Sense Data", dp, cnt - scg_getresid(scgp));
304 	return (0);
305 }
306 
307 EXPORT int
mode_sense_g1(scgp,dp,cnt,page,pcf)308 mode_sense_g1(scgp, dp, cnt, page, pcf)
309 	SCSI	*scgp;
310 	Uchar	*dp;
311 	int	cnt;
312 	int	page;
313 	int	pcf;
314 {
315 	register struct	scg_cmd	*scmd = scgp->scmd;
316 
317 	fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
318 	scmd->addr = (caddr_t)dp;
319 	scmd->size = cnt;
320 	scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA;
321 	scmd->cdb_len = SC_G1_CDBLEN;
322 	scmd->sense_len = CCS_SENSE_LEN;
323 	scmd->cdb.g1_cdb.cmd = 0x5A;
324 	scmd->cdb.g1_cdb.lun = scg_lun(scgp);
325 #ifdef	nonono
326 	scmd->cdb.g0_cdb.high_addr = 1<<4;	/* DBD Disable Block desc. */
327 #endif
328 	scmd->cdb.g1_cdb.addr[0] = (page&0x3F) | ((pcf<<6)&0xC0);
329 	g1_cdblen(&scmd->cdb.g1_cdb, cnt);
330 
331 	scgp->cmdname = "mode sense g1";
332 
333 	if (scg_cmd(scgp) < 0)
334 		return (-1);
335 	if (scgp->verbose) scg_prbytes("Mode Sense Data", dp, cnt - scg_getresid(scgp));
336 	return (0);
337 }
338