1 /*
2  * This file has been modified for the cdrkit suite.
3  *
4  * The behaviour and appearence of the program code below can differ to a major
5  * extent from the version distributed by the original author(s).
6  *
7  * For details, see Changelog file distributed with the cdrkit package. If you
8  * received this file from another source then ask the distributing person for
9  * a log of modifications.
10  *
11  */
12 
13 /* @(#)modes.c	1.25 04/03/02 Copyright 1988, 1997-2001, 2004 J. Schilling */
14 /*
15  *	SCSI mode page handling
16  *
17  *	Copyright (c) 1988, 1997-2001, 2004 J. Schilling
18  */
19 /*
20  * This program is free software; you can redistribute it and/or modify
21  * it under the terms of the GNU General Public License version 2
22  * as published by the Free Software Foundation.
23  *
24  * This program is distributed in the hope that it will be useful,
25  * but WITHOUT ANY WARRANTY; without even the implied warranty of
26  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
27  * GNU General Public License for more details.
28  *
29  * You should have received a copy of the GNU General Public License along with
30  * this program; see the file COPYING.  If not, write to the Free Software
31  * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
32  */
33 
34 #include <mconfig.h>
35 #include <utypes.h>
36 #include <standard.h>
37 #include <schily.h>
38 #include <usal/usalcmd.h>
39 #include <usal/scsireg.h>
40 #include <usal/scsitransp.h>
41 
42 #include "wodim.h"
43 
44 int	scsi_compliant;
45 
46 static	BOOL	has_mode_page(SCSI *usalp, int page, char *pagename, int *lenp);
47 BOOL	get_mode_params(SCSI *usalp, int page, char *pagename, Uchar *modep,
48 							 Uchar *cmodep, Uchar *dmodep, Uchar *smodep, int *lenp);
49 BOOL	set_mode_params(SCSI *usalp, char *pagename, Uchar *modep, int len,
50 							 int save, int secsize);
51 
52 #define	XXX
53 
54 #ifdef	XXX
55 static BOOL
has_mode_page(SCSI * usalp,int page,char * pagename,int * lenp)56 has_mode_page(SCSI *usalp, int page, char *pagename, int *lenp)
57 {
58 	Uchar	mode[0x100];
59 	int	hdlen;
60 	int	len = 1;				/* Nach SCSI Norm */
61 	int	try = 0;
62 	struct	scsi_mode_page_header *mp;
63 
64 	/*
65 	 * ATAPI drives (used e.g. by IOMEGA) from y2k have the worst firmware
66 	 * I've seen. They create DMA buffer overruns if we request less than
67 	 * 3 bytes with 6 byte mode sense which equals 4 byte with 10 byte mode
68 	 * sense. In order to prevent repeated bus resets, we remember this
69 	 * bug.
70 	 *
71 	 * IOMEGA claims that they are using Philips clone drives but a Philips
72 	 * drive I own does not have the problem.
73 	 */
74 	if ((usalp->dflags & DRF_MODE_DMA_OVR) != 0)
75 		len = sizeof (struct scsi_mode_header);
76 again:
77 	fillbytes((caddr_t)mode, sizeof (mode), '\0');
78 	if (lenp)
79 		*lenp = 0;
80 
81 	usalp->silent++;
82 	(void) unit_ready(usalp);
83 /* Maxoptix bringt Aborted cmd 0x0B mit code 0x4E (overlapping cmds)*/
84 
85 	/*
86 	 * The Matsushita CW-7502 will sometimes deliver a zeroed
87 	 * mode page 2A if "Page n default" is used instead of "current".
88 	 */
89 	if (mode_sense(usalp, mode, len, page, 0) < 0) {	/* Page n current */
90 		usalp->silent--;
91 		if (len < (int)sizeof (struct scsi_mode_header) && try == 0) {
92 			len = sizeof (struct scsi_mode_header);
93 			goto again;
94 		}
95 		return (FALSE);
96 	} else {
97 		if (len > 1 && try == 0) {
98 			/*
99 			 * If we come here, we got a hard failure with the
100 			 * fist try. Remember this (IOMEGA USB) firmware bug.
101 			 */
102 			if ((usalp->dflags & DRF_MODE_DMA_OVR) == 0) {
103 				/* XXX if (!nowarn) */
104 				errmsgno(EX_BAD,
105 				"Warning: controller creates hard SCSI failure when retrieving %s page.\n",
106 								pagename);
107 				usalp->dflags |= DRF_MODE_DMA_OVR;
108 			}
109 		}
110 		len = ((struct scsi_mode_header *)mode)->sense_data_len + 1;
111 	}
112 	/*
113 	 * ATAPI drives as used by IOMEGA may receive a SCSI bus device reset
114 	 * in between these two mode sense commands.
115 	 */
116 	(void) unit_ready(usalp);
117 	if (mode_sense(usalp, mode, len, page, 0) < 0) {	/* Page n current */
118 		usalp->silent--;
119 		return (FALSE);
120 	}
121 	usalp->silent--;
122 
123 	if (usalp->verbose)
124 		usal_prbytes("Mode Sense Data", mode, len - usal_getresid(usalp));
125 	hdlen = sizeof (struct scsi_mode_header) +
126 			((struct scsi_mode_header *)mode)->blockdesc_len;
127 	mp = (struct scsi_mode_page_header *)(mode + hdlen);
128 	if (usalp->verbose)
129 		usal_prbytes("Mode Page  Data", (Uchar *)mp, mp->p_len+2);
130 
131 	if (mp->p_len == 0) {
132 		if (!scsi_compliant && try == 0) {
133 			len = hdlen;
134 			/*
135 			 * add sizeof page header (page # + len byte)
136 			 * (should normaly result in len == 14)
137 			 * this allowes to work with:
138 			 * 	Quantum Q210S	(wants at least 13)
139 			 * 	MD2x		(wants at least 4)
140 			 */
141 			len += 2;
142 			try++;
143 			goto again;
144 		}
145 		/* XXX if (!nowarn) */
146 		errmsgno(EX_BAD,
147 			"Warning: controller returns zero sized %s page.\n",
148 								pagename);
149 	}
150 	if (!scsi_compliant &&
151 	    (len < (int)(mp->p_len + hdlen + 2))) {
152 		len = mp->p_len + hdlen + 2;
153 
154 		/* XXX if (!nowarn) */
155 		errmsgno(EX_BAD,
156 			"Warning: controller returns wrong size for %s page.\n",
157 								pagename);
158 	}
159 	if (mp->p_code != page) {
160 		/* XXX if (!nowarn) */
161 		errmsgno(EX_BAD,
162 			"Warning: controller returns wrong page %X for %s page (%X).\n",
163 						mp->p_code, pagename, page);
164 		return (FALSE);
165 	}
166 
167 	if (lenp)
168 		*lenp = len;
169 	return (mp->p_len > 0);
170 }
171 #endif
172 
173 BOOL
get_mode_params(SCSI * usalp,int page,char * pagename,Uchar * modep,Uchar * cmodep,Uchar * dmodep,Uchar * smodep,int * lenp)174 get_mode_params(SCSI *usalp, int page, char *pagename, Uchar *modep,
175                 Uchar *cmodep, Uchar *dmodep, Uchar *smodep, int *lenp)
176 {
177 	int	len;
178 	BOOL	ret = TRUE;
179 
180 #ifdef	XXX
181 	if (lenp)
182 		*lenp = 0;
183 	if (!has_mode_page(usalp, page, pagename, &len)) {
184 		if (!usalp->silent) errmsgno(EX_BAD,
185 			"Warning: controller does not support %s page.\n",
186 								pagename);
187 		return (FALSE);
188 	}
189 	if (lenp)
190 		*lenp = len;
191 #else
192 	if (lenp == 0)
193 		len = 0xFF;
194 #endif
195 
196 	if (modep) {
197 		fillbytes(modep, 0x100, '\0');
198 		usalp->silent++;
199 		(void) unit_ready(usalp);
200 		usalp->silent--;
201 		if (mode_sense(usalp, modep, len, page, 0) < 0) { /* Page x current */
202 			errmsgno(EX_BAD, "Cannot get %s data.\n", pagename);
203 			ret = FALSE;
204 		} else if (usalp->verbose) {
205 			usal_prbytes("Mode Sense Data", modep, len - usal_getresid(usalp));
206 		}
207 	}
208 
209 	if (cmodep) {
210 		fillbytes(cmodep, 0x100, '\0');
211 		usalp->silent++;
212 		(void) unit_ready(usalp);
213 		usalp->silent--;
214 		if (mode_sense(usalp, cmodep, len, page, 1) < 0) { /* Page x change */
215 			errmsgno(EX_BAD, "Cannot get %s mask.\n", pagename);
216 			ret = FALSE;
217 		} else if (usalp->verbose) {
218 			usal_prbytes("Mode Sense Data", cmodep, len - usal_getresid(usalp));
219 		}
220 	}
221 
222 	if (dmodep) {
223 		fillbytes(dmodep, 0x100, '\0');
224 		usalp->silent++;
225 		(void) unit_ready(usalp);
226 		usalp->silent--;
227 		if (mode_sense(usalp, dmodep, len, page, 2) < 0) { /* Page x default */
228 			errmsgno(EX_BAD, "Cannot get default %s data.\n",
229 								pagename);
230 			ret = FALSE;
231 		} else if (usalp->verbose) {
232 			usal_prbytes("Mode Sense Data", dmodep, len - usal_getresid(usalp));
233 		}
234 	}
235 
236 	if (smodep) {
237 		fillbytes(smodep, 0x100, '\0');
238 		usalp->silent++;
239 		(void) unit_ready(usalp);
240 		usalp->silent--;
241 		if (mode_sense(usalp, smodep, len, page, 3) < 0) { /* Page x saved */
242 			errmsgno(EX_BAD, "Cannot get saved %s data.\n", pagename);
243 			ret = FALSE;
244 		} else if (usalp->verbose) {
245 			usal_prbytes("Mode Sense Data", smodep, len - usal_getresid(usalp));
246 		}
247 	}
248 
249 	return (ret);
250 }
251 
252 BOOL
set_mode_params(SCSI * usalp,char * pagename,Uchar * modep,int len,int save,int secsize)253 set_mode_params(SCSI *usalp, char *pagename, Uchar *modep, int len, int save,
254                 int secsize)
255 {
256 	int	i;
257 
258 	((struct scsi_modesel_header *)modep)->sense_data_len	= 0;
259 	((struct scsi_modesel_header *)modep)->res2		= 0;
260 
261 	i = ((struct scsi_mode_header *)modep)->blockdesc_len;
262 	if (i > 0) {
263 		i_to_3_byte(
264 			((struct scsi_mode_data *)modep)->blockdesc.nlblock,
265 								0);
266 		if (secsize >= 0)
267 		i_to_3_byte(((struct scsi_mode_data *)modep)->blockdesc.lblen,
268 							secsize);
269 	}
270 
271 	usalp->silent++;
272 	(void) unit_ready(usalp);
273 	usalp->silent--;
274 	if (save == 0 || mode_select(usalp, modep, len, save, usalp->inq->data_format >= 2) < 0) {
275 		usalp->silent++;
276 		(void) unit_ready(usalp);
277 		usalp->silent--;
278 		if (mode_select(usalp, modep, len, 0, usalp->inq->data_format >= 2) < 0) {
279 			if (usalp->silent == 0) {
280 				errmsgno(EX_BAD,
281 					"Warning: using default %s data.\n",
282 					pagename);
283 				usal_prbytes("Mode Select Data", modep, len);
284 			}
285 			return (FALSE);
286 		}
287 	}
288 	return (TRUE);
289 }
290