1 /* @(#)scsiopen.c	1.101 09/07/11 Copyright 1995-2009 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static	UConst char sccsid[] =
5 	"@(#)scsiopen.c	1.101 09/07/11 Copyright 1995-2009 J. Schilling";
6 #endif
7 /*
8  *	SCSI command functions for cdrecord
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  * The following exceptions apply:
23  * CDDL �3.6 needs to be replaced by: "You may create a Larger Work by
24  * combining Covered Software with other code if all other code is governed by
25  * the terms of a license that is OSI approved (see www.opensource.org) and
26  * you may distribute the Larger Work as a single product. In such a case,
27  * You must make sure the requirements of this License are fulfilled for
28  * the Covered Software."
29  *
30  * When distributing Covered Code, include this CDDL HEADER in each
31  * file and include the License file CDDL.Schily.txt from this distribution.
32  */
33 
34 /*
35  * NOTICE:	The Philips CDD 521 has several firmware bugs.
36  *		One of them is not to respond to a SCSI selection
37  *		within 200ms if the general load on the
38  *		SCSI bus is high. To deal with this problem
39  *		most of the SCSI commands are send with the
40  *		SCG_CMD_RETRY flag enabled.
41  *
42  *		Note that the only legal place to assign
43  *		values to scg_scsibus() scg_target() and scg_lun()
44  *		is scg_settarget().
45  */
46 
47 #include <schily/stdio.h>
48 #include <schily/standard.h>
49 #include <schily/stdlib.h>
50 #include <schily/unistd.h>
51 #include <schily/fcntl.h>
52 #include <schily/errno.h>
53 #include <schily/string.h>
54 #include <schily/time.h>
55 
56 #include <schily/utypes.h>
57 #include <schily/btorder.h>
58 #include <schily/schily.h>
59 
60 #include <scg/scgcmd.h>
61 #include <scg/scsidefs.h>
62 #include <scg/scsireg.h>
63 #include <scg/scsitransp.h>
64 
65 #define	strbeg(s1, s2)	(strstr((s2), (s1)) == (s2))
66 
67 extern	int	lverbose;
68 
69 EXPORT	SCSI	*scg_open	__PR((char *scsidev, char *errs, int slen, int debug,
70 								int be_verbose));
71 EXPORT	int	scg_help	__PR((FILE *f));
72 LOCAL	int	scg_scandev	__PR((char *devp, char *errs, int slen,
73 							int *busp, int *tgtp, int *lunp));
74 EXPORT	int	scg_close	__PR((SCSI * scgp));
75 
76 EXPORT	void	scg_settimeout	__PR((SCSI * scgp, int timeout));
77 
78 EXPORT	SCSI	*scg_smalloc	__PR((void));
79 EXPORT	void	scg_sfree	__PR((SCSI *scgp));
80 
81 /*
82  * Open a SCSI device.
83  *
84  * Possible syntax is:
85  *
86  * Preferred:
87  *	dev=target,lun / dev=scsibus,target,lun
88  *
89  * Needed on some systems:
90  *	dev=devicename:target,lun / dev=devicename:scsibus,target,lun
91  *
92  * On systems that don't support SCSI Bus scanning this syntax helps:
93  *	dev=devicename:@ / dev=devicename:@,lun
94  * or	dev=devicename (undocumented)
95  *
96  * NOTE: As the 'lun' is part of the SCSI command descriptor block, it
97  *	 must always be known. If the OS cannot map it, it must be
98  *	 specified on command line.
99  */
100 EXPORT SCSI *
scg_open(scsidev,errs,slen,debug,be_verbose)101 scg_open(scsidev, errs, slen, debug, be_verbose)
102 	char	*scsidev;
103 	char	*errs;
104 	int	slen;
105 	int	debug;
106 	int	be_verbose;
107 {
108 	char	sdevname[256];
109 	char	*devp = NULL;
110 	char	*sdev = NULL;
111 	int	x1;
112 	int	bus = 0;
113 	int	tgt = 0;
114 	int	lun = 0;
115 	int	n = 0;
116 	SCSI	*scgp;
117 
118 	if (errs)
119 		errs[0] = '\0';
120 	scgp = scg_smalloc();
121 	if (scgp == NULL) {
122 		if (errs)
123 			js_snprintf(errs, slen, "No memory for SCSI structure");
124 		return ((SCSI *)0);
125 	}
126 	scgp->debug = debug;
127 	scgp->overbose = be_verbose;
128 
129 	sdevname[0] = '\0';
130 	if (scsidev != NULL && scsidev[0] != '\0') {
131 		sdev = scsidev;
132 		if ((strncmp(scsidev, "HELP", 4) == 0) ||
133 		    (strncmp(scsidev, "help", 4) == 0)) {
134 
135 			return ((SCSI *)0);
136 		}
137 		if (strncmp(scsidev, "REMOTE", 6) == 0) {
138 			/*
139 			 * REMOTE:user@host:scsidev or
140 			 * REMOTE(transp):user@host:scsidev
141 			 * e.g.: REMOTE(/usr/bin/ssh):user@host:scsidev
142 			 *
143 			 * We must send the complete device spec to the remote
144 			 * site to allow parsing on both sites.
145 			 */
146 			strncpy(sdevname, scsidev, sizeof (sdevname)-1);
147 			sdevname[sizeof (sdevname)-1] = '\0';
148 			if (sdev[6] == '(' || sdev[6] == ':')
149 				sdev = strchr(sdev, ':');
150 			else
151 				sdev = NULL;
152 
153 			if (sdev == NULL) {
154 				/*
155 				 * This seems to be an illegal remote dev spec.
156 				 * Give it a chance with a standard parsing.
157 				 */
158 				sdev = scsidev;
159 				sdevname[0] = '\0';
160 			} else {
161 				/*
162 				 * Now try to go past user@host spec.
163 				 */
164 				if (sdev)
165 					sdev = strchr(&sdev[1], ':');
166 				if (sdev)
167 					sdev++;	/* Device name follows ... */
168 				else
169 					goto nulldevice;
170 			}
171 		}
172 		if ((devp = strchr(sdev, ':')) == NULL) {
173 			if (strchr(sdev, ',') == NULL) {
174 				/* Notation form: 'devname' (undocumented)  */
175 				/* Forward complete name to scg__open()	    */
176 				/* Fetch bus/tgt/lun values from OS	    */
177 				/* We may come here too with 'USCSI'	    */
178 				n = -1;
179 				lun  = -2;	/* Lun must be known	    */
180 				if (sdevname[0] == '\0') {
181 					strncpy(sdevname, scsidev,
182 							sizeof (sdevname)-1);
183 					sdevname[sizeof (sdevname)-1] = '\0';
184 				}
185 			} else {
186 				/* Basic notation form: 'bus,tgt,lun'	    */
187 				devp = sdev;
188 			}
189 		} else {
190 			/* Notation form: 'devname:bus,tgt,lun'/'devname:@' */
191 			/* We may come here too with 'USCSI:'		    */
192 			if (sdevname[0] == '\0') {
193 				/*
194 				 * Copy over the part before the ':'
195 				 */
196 				x1 = devp - scsidev;
197 				if (x1 >= (int)sizeof (sdevname))
198 					x1 = sizeof (sdevname)-1;
199 				strncpy(sdevname, scsidev, x1);
200 				sdevname[x1] = '\0';
201 			}
202 			devp++;
203 			/* Check for a notation in the form 'devname:@'	    */
204 			if (devp[0] == '@') {
205 				if (devp[1] == '\0') {
206 					lun = -2;
207 				} else if (devp[1] == ',') {
208 					if (*astoi(&devp[2], &lun) != '\0') {
209 						errno = EINVAL;
210 						if (errs)
211 							js_snprintf(errs, slen,
212 								"Invalid lun specifier '%s'",
213 										&devp[2]);
214 						return ((SCSI *)0);
215 					}
216 				}
217 				n = -1;
218 				/*
219 				 * Got device:@ or device:@,lun
220 				 * Make sure not to call scg_scandev()
221 				 */
222 				devp = NULL;
223 			} else if (devp[0] == '\0') {
224 				/*
225 				 * Got USCSI: or ATAPI:
226 				 * Make sure not to call scg_scandev()
227 				 */
228 				devp = NULL;
229 			} else if (strchr(sdev, ',') == NULL) {
230 				/* We may come here with 'ATAPI:/dev/hdc'   */
231 				strncpy(sdevname, scsidev,
232 						sizeof (sdevname)-1);
233 				sdevname[sizeof (sdevname)-1] = '\0';
234 				n = -1;
235 				lun  = -2;	/* Lun must be known	    */
236 				/*
237 				 * Make sure not to call scg_scandev()
238 				 */
239 				devp = NULL;
240 			}
241 		}
242 	}
243 nulldevice:
244 
245 /*error("10 scsidev '%s' sdev '%s' devp '%s' b: %d t: %d l: %d\n", scsidev, sdev, devp, bus, tgt, lun);*/
246 
247 	if (devp != NULL) {
248 		n = scg_scandev(devp, errs, slen, &bus, &tgt, &lun);
249 		if (n < 0) {
250 			errno = EINVAL;
251 			return ((SCSI *)0);
252 		}
253 	}
254 	if (n >= 1 && n <= 3) {	/* Got bus,target,lun or target,lun or tgt*/
255 		scg_settarget(scgp, bus, tgt, lun);
256 	} else if (n == -1) {	/* Got device:@, fetch bus/lun from OS	*/
257 		scg_settarget(scgp, -2, -2, lun);
258 	} else if (devp != NULL) {
259 		/*
260 		 * XXX May this happen after we allow tgt to repesent tgt,0 ?
261 		 */
262 		js_fprintf(stderr, "WARNING: device not valid, trying to use default target...\n");
263 		scg_settarget(scgp, 0, 6, 0);
264 	}
265 	if (be_verbose && scsidev != NULL) {
266 		js_fprintf(stderr, "scsidev: '%s'\n", scsidev);
267 		if (sdevname[0] != '\0')
268 			js_fprintf(stderr, "devname: '%s'\n", sdevname);
269 		js_fprintf(stderr, "scsibus: %d target: %d lun: %d\n",
270 					scg_scsibus(scgp), scg_target(scgp), scg_lun(scgp));
271 	}
272 	if (debug > 0) {
273 		js_fprintf(stderr, "scg__open(%s) %d,%d,%d\n",
274 			sdevname,
275 			scg_scsibus(scgp), scg_target(scgp), scg_lun(scgp));
276 	}
277 	if (scg__open(scgp, sdevname) <= 0) {
278 		if (errs && scgp->errstr)
279 			js_snprintf(errs, slen, "%s", scgp->errstr);
280 		scg_sfree(scgp);
281 		return ((SCSI *)0);
282 	}
283 	return (scgp);
284 }
285 
286 EXPORT int
scg_help(f)287 scg_help(f)
288 	FILE	*f;
289 {
290 	SCSI	*scgp;
291 
292 	scgp = scg_smalloc();
293 	if (scgp != NULL) {
294 extern	scg_ops_t scg_std_ops;
295 
296 		scgp->ops = &scg_std_ops;
297 
298 		printf("Supported SCSI transports for this platform:\n");
299 		SCGO_HELP(scgp, f);
300 		scg_remote()->scgo_help(scgp, f);
301 		scg_sfree(scgp);
302 	}
303 	return (0);
304 }
305 
306 /*
307  * Convert target,lun or scsibus,target,lun syntax.
308  * Check for bad syntax and invalid values.
309  * This is definitely better than using scanf() as it checks for syntax errors.
310  */
311 LOCAL int
scg_scandev(devp,errs,slen,busp,tgtp,lunp)312 scg_scandev(devp, errs, slen, busp, tgtp, lunp)
313 	char	*devp;
314 	char	*errs;
315 	int	slen;
316 	int	*busp;
317 	int	*tgtp;
318 	int	*lunp;
319 {
320 	int	x1, x2, x3;
321 	int	n = 0;
322 	char	*p = devp;
323 
324 	x1 = x2 = x3 = 0;
325 	*busp = *tgtp = *lunp = 0;
326 
327 	if (*p != '\0') {
328 		p = astoi(p, &x1);
329 		if (*p == ',') {
330 			p++;
331 			n++;
332 		} else {
333 			if (errs)
334 				js_snprintf(errs, slen, "Invalid bus or target specifier in '%s'", devp);
335 			return (-1);
336 		}
337 	}
338 	if (*p != '\0') {
339 		p = astoi(p, &x2);
340 		if (*p == ',' || *p == '\0') {
341 			if (*p != '\0')
342 				p++;
343 			n++;
344 		} else {
345 			if (errs)
346 				js_snprintf(errs, slen, "Invalid target or lun specifier in '%s'", devp);
347 			return (-1);
348 		}
349 	}
350 	if (*p != '\0') {
351 		p = astoi(p, &x3);
352 		if (*p == '\0') {
353 			n++;
354 		} else {
355 			if (errs)
356 				js_snprintf(errs, slen, "Invalid lun specifier in '%s'", devp);
357 			return (-1);
358 		}
359 	}
360 	if (n == 3) {
361 		*busp = x1;
362 		*tgtp = x2;
363 		*lunp = x3;
364 	}
365 	if (n == 2) {
366 		*tgtp = x1;
367 		*lunp = x2;
368 	}
369 	if (n == 1) {
370 		*tgtp = x1;
371 	}
372 
373 	if (x1 < 0 || x2 < 0 || x3 < 0) {
374 		if (errs)
375 			js_snprintf(errs, slen, "Invalid value for bus, target or lun (%d,%d,%d)",
376 				*busp, *tgtp, *lunp);
377 		return (-1);
378 	}
379 	return (n);
380 }
381 
382 EXPORT int
scg_close(scgp)383 scg_close(scgp)
384 	SCSI	*scgp;
385 {
386 	scg__close(scgp);
387 	scg_sfree(scgp);
388 	return (0);
389 }
390 
391 EXPORT void
scg_settimeout(scgp,timeout)392 scg_settimeout(scgp, timeout)
393 	SCSI	*scgp;
394 	int	timeout;
395 {
396 #ifdef	nonono
397 	if (timeout >= 0)
398 		scgp->deftimeout = timeout;
399 #else
400 	scgp->deftimeout = timeout;
401 #endif
402 }
403 
404 EXPORT SCSI *
scg_smalloc()405 scg_smalloc()
406 {
407 	SCSI	*scgp;
408 extern	scg_ops_t scg_dummy_ops;
409 
410 	scgp = (SCSI *)malloc(sizeof (*scgp));
411 	if (scgp == NULL)
412 		return ((SCSI *)0);
413 
414 	fillbytes(scgp, sizeof (*scgp), 0);
415 	scgp->ops	= &scg_dummy_ops;
416 	scg_settarget(scgp, -1, -1, -1);
417 	scgp->fd	= -1;
418 	scgp->deftimeout = 20;
419 	scgp->running	= FALSE;
420 
421 	scgp->cmdstart = (struct timeval *)malloc(sizeof (struct timeval));
422 	if (scgp->cmdstart == NULL)
423 		goto err;
424 	scgp->cmdstop = (struct timeval *)malloc(sizeof (struct timeval));
425 	if (scgp->cmdstop == NULL)
426 		goto err;
427 	scgp->scmd = (struct scg_cmd *)malloc(sizeof (struct scg_cmd));
428 	if (scgp->scmd == NULL)
429 		goto err;
430 	scgp->errstr = malloc(SCSI_ERRSTR_SIZE);
431 	if (scgp->errstr == NULL)
432 		goto err;
433 	scgp->errptr = scgp->errbeg = scgp->errstr;
434 	scgp->errstr[0] = '\0';
435 	scgp->errfile = (void *)stderr;
436 	scgp->inq = (struct scsi_inquiry *)malloc(sizeof (struct scsi_inquiry));
437 	if (scgp->inq == NULL)
438 		goto err;
439 	scgp->cap = (struct scsi_capacity *)malloc(sizeof (struct scsi_capacity));
440 	if (scgp->cap == NULL)
441 		goto err;
442 
443 	return (scgp);
444 err:
445 	scg_sfree(scgp);
446 	return ((SCSI *)0);
447 }
448 
449 EXPORT void
scg_sfree(scgp)450 scg_sfree(scgp)
451 	SCSI	*scgp;
452 {
453 	if (scgp->cmdstart)
454 		free(scgp->cmdstart);
455 	if (scgp->cmdstop)
456 		free(scgp->cmdstop);
457 	if (scgp->scmd)
458 		free(scgp->scmd);
459 	if (scgp->inq)
460 		free(scgp->inq);
461 	if (scgp->cap)
462 		free(scgp->cap);
463 	if (scgp->local)
464 		free(scgp->local);
465 	scg_freebuf(scgp);
466 	if (scgp->errstr)
467 		free(scgp->errstr);
468 	free(scgp);
469 }
470