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