Imported Upstream version 1.1.11
[platform/upstream/cdrkit.git] / libusal / scsiopen.c
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 /* @(#)scsiopen.c       1.95 04/01/14 Copyright 1995,2000 J. Schilling */
14 /*
15  *      SCSI command functions for cdrecord
16  *
17  *      Copyright (c) 1995,2000 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 /*
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 usal_scsibus() usal_target() and usal_lun()
44  *              is usal_settarget().
45  */
46 #include <mconfig.h>
47
48 #include <stdio.h>
49 #include <standard.h>
50 #include <stdxlib.h>
51 #include <unixstd.h>
52 #include <fctldefs.h>
53 #include <errno.h>
54 #include <strdefs.h>
55 #include <timedefs.h>
56
57 #include <utypes.h>
58 #include <btorder.h>
59 #include <schily.h>
60
61 #include <usal/usalcmd.h>
62 #include <usal/scsidefs.h>
63 #include <usal/scsireg.h>
64 #include <usal/scsitransp.h>
65
66 #if    defined(linux) || defined(__linux) || defined(__linux__)
67 extern BOOL check_linux_26();
68 #endif
69
70 #define strbeg(s1, s2)  (strstr((s2), (s1)) == (s2))
71
72 extern  int     lverbose;
73
74 SCSI    *usal_open(char *scsidev, char *errs, int slen, int debug, int be_verbose);
75 int     usal_help(FILE *f);
76 static  int     usal_scandev(char *devp, char *errs, int slen, int *busp, 
77                                                                         int *tgtp, int *lunp);
78 int     usal_close(SCSI * usalp);
79
80 void    usal_settimeout(SCSI * usalp, int timeout);
81
82 SCSI    *usal_smalloc(void);
83 void    usal_sfree(SCSI *usalp);
84
85 /*
86  * Open a SCSI device.
87  *
88  * Possible syntax is:
89  *
90  * Preferred:
91  *      dev=target,lun / dev=scsibus,target,lun
92  *
93  * Needed on some systems:
94  *      dev=devicename:target,lun / dev=devicename:scsibus,target,lun
95  *
96  * On systems that don't support SCSI Bus scanning this syntax helps:
97  *      dev=devicename:@ / dev=devicename:@,lun
98  * or   dev=devicename (undocumented)
99  *
100  * NOTE: As the 'lun' is part of the SCSI command descriptor block, it
101  *       must always be known. If the OS cannot map it, it must be
102  *       specified on command line.
103  */
104 SCSI *
105 usal_open(char *scsidev, char *errs, int slen, int debug, int be_verbose)
106 {
107         char    devname[256];
108         char    *devp = NULL;
109         char    *sdev = NULL;
110         int     x1;
111         int     bus = 0;
112         int     tgt = 0;
113         int     lun = 0;
114         int     n = 0;
115         SCSI    *usalp;
116
117         if (errs)
118                 errs[0] = '\0';
119         usalp = usal_smalloc();
120         if (usalp == NULL) {
121                 if (errs)
122                         snprintf(errs, slen, "No memory for SCSI structure");
123                 return ((SCSI *)0);
124         }
125         usalp->debug = debug;
126         usalp->overbose = be_verbose;
127         devname[0] = '\0';
128         if (scsidev != NULL && scsidev[0] != '\0') {
129                 sdev = scsidev;
130                 if ((strncmp(scsidev, "HELP", 4) == 0) ||
131                     (strncmp(scsidev, "help", 4) == 0)) {
132
133                         return ((SCSI *)0);
134                 }
135                 if (strncmp(scsidev, "REMOTE", 6) == 0) {
136                         /*
137                          * REMOTE:user@host:scsidev or
138                          * REMOTE(transp):user@host:scsidev
139                          * e.g.: REMOTE(/usr/bin/ssh):user@host:scsidev
140                          *
141                          * We must send the complete device spec to the remote
142                          * site to allow parsing on both sites.
143                          */
144                         strncpy(devname, scsidev, sizeof (devname)-1);
145                         devname[sizeof (devname)-1] = '\0';
146                         if (sdev[6] == '(' || sdev[6] == ':')
147                                 sdev = strchr(sdev, ':');
148                         else
149                                 sdev = NULL;
150
151                         if (sdev == NULL) {
152                                 /*
153                                  * This seems to be an illegal remote dev spec.
154                                  * Give it a chance with a standard parsing.
155                                  */
156                                 sdev = scsidev;
157                                 devname[0] = '\0';
158                         } else {
159                                 /*
160                                  * Now try to go past user@host spec.
161                                  */
162                                 if (sdev)
163                                         sdev = strchr(&sdev[1], ':');
164                                 if (sdev)
165                                         sdev++; /* Device name follows ... */
166                                 else
167                                         goto nulldevice;
168                         }
169                 }
170                 if ((devp = strchr(sdev, ':')) == NULL) {
171                         if (strchr(sdev, ',') == NULL) {
172                                 /* Notation form: 'devname' (undocumented)  */
173                                 /* Forward complete name to usal__open()            */
174                                 /* Fetch bus/tgt/lun values from OS         */
175                                 /* We may come here too with 'USCSI'        */
176                                 n = -1;
177                                 lun  = -2;      /* Lun must be known        */
178                                 if (devname[0] == '\0') {
179                                         strncpy(devname, scsidev,
180                                                         sizeof (devname)-1);
181                                         devname[sizeof (devname)-1] = '\0';
182                                 }
183                         } else {
184                                 /* Basic notation form: 'bus,tgt,lun'       */
185                                 devp = sdev;
186                         }
187                 } else {
188                         /* Notation form: 'devname:bus,tgt,lun'/'devname:@' */
189                         /* We may come here too with 'USCSI:'               */
190                         if (devname[0] == '\0') {
191                                 /*
192                                  * Copy over the part before the ':'
193                                  */
194                                 x1 = devp - scsidev;
195                                 if (x1 >= (int)sizeof (devname))
196                                         x1 = sizeof (devname)-1;
197                                 strncpy(devname, scsidev, x1);
198                                 devname[x1] = '\0';
199                         }
200                         devp++;
201                         /* Check for a notation in the form 'devname:@'     */
202                         if (devp[0] == '@') {
203                                 if (devp[1] == '\0') {
204                                         lun = -2;
205                                 } else if (devp[1] == ',') {
206                                         if (*astoi(&devp[2], &lun) != '\0') {
207                                                 errno = EINVAL;
208                                                 if (errs)
209                                                         snprintf(errs, slen,
210                                                                 "Invalid lun specifier '%s'",
211                                                                                 &devp[2]);
212                                                 return ((SCSI *)0);
213                                         }
214                                 }
215                                 n = -1;
216                                 /*
217                                  * Got device:@ or device:@,lun
218                                  * Make sure not to call usal_scandev()
219                                  */
220                                 devp = NULL;
221                         } else if (devp[0] == '\0') {
222                                 /*
223                                  * Got USCSI: or ATAPI:
224                                  * Make sure not to call usal_scandev()
225                                  */
226                                 devp = NULL;
227                         } else if (strchr(sdev, ',') == NULL) {
228                                 /* We may come here with 'ATAPI:/dev/hdc'   */
229                                 strncpy(devname, scsidev,
230                                                 sizeof (devname)-1);
231                                 devname[sizeof (devname)-1] = '\0';
232                                 n = -1;
233                                 lun  = -2;      /* Lun must be known        */
234                                 /*
235                                  * Make sure not to call usal_scandev()
236                                  */
237                                 devp = NULL;
238                         }
239                 }
240         }
241 nulldevice:
242
243 /*fprintf(stderr, "10 scsidev '%s' sdev '%s' devp '%s' b: %d t: %d l: %d\n", scsidev, sdev, devp, bus, tgt, lun);*/
244
245         if (devp != NULL) {
246                 n = usal_scandev(devp, errs, slen, &bus, &tgt, &lun);
247                 if (n < 0) {
248                         errno = EINVAL;
249                         return ((SCSI *)0);
250                 }
251         }
252         if (n >= 1 && n <= 3) { /* Got bus,target,lun or target,lun or tgt*/
253                 usal_settarget(usalp, bus, tgt, lun);
254         } else if (n == -1) {   /* Got device:@, fetch bus/lun from OS  */
255                 usal_settarget(usalp, -2, -2, lun);
256         } else if (devp != NULL) {
257                 /*
258                  * XXX May this happen after we allow tgt to repesent tgt,0 ?
259                  */
260                 fprintf(stderr, "WARNING: device not valid, trying to use default target...\n");
261                 usal_settarget(usalp, 0, 6, 0);
262         }
263         if (be_verbose && scsidev != NULL) {
264                 fprintf(stderr, "scsidev: '%s'\n", scsidev);
265                 if (devname[0] != '\0')
266                         fprintf(stderr, "devname: '%s'\n", devname);
267                 fprintf(stderr, "scsibus: %d target: %d lun: %d\n",
268                                         usal_scsibus(usalp), usal_target(usalp), usal_lun(usalp));
269         }
270         if (debug > 0) {
271                 fprintf(stderr, "usal__open(%s) %d,%d,%d\n",
272                         devname,
273                         usal_scsibus(usalp), usal_target(usalp), usal_lun(usalp));
274         }
275         if (usal__open(usalp, devname) <= 0) {
276                 if (errs && usalp->errstr)
277                         snprintf(errs, slen, "%s", usalp->errstr);
278                 usal_sfree(usalp);
279                 return ((SCSI *)0);
280         }
281         return (usalp);
282 }
283
284 int
285 usal_help(FILE *f)
286 {
287         SCSI    *usalp;
288
289         usalp = usal_smalloc();
290         if (usalp != NULL) {
291 extern  usal_ops_t usal_std_ops;
292
293                 usalp->ops = &usal_std_ops;
294
295                 printf("Supported SCSI transports for this platform:\n");
296                 SCGO_HELP(usalp, f);
297                 usal_remote()->usalo_help(usalp, f);
298                 usal_sfree(usalp);
299         }
300         return (0);
301 }
302
303 /*
304  * Convert target,lun or scsibus,target,lun syntax.
305  * Check for bad syntax and invalid values.
306  * This is definitely better than using scanf() as it checks for syntax errors.
307  */
308 static int
309 usal_scandev(char *devp, char *errs, int slen, int *busp, int *tgtp, int *lunp)
310 {
311         int     x1, x2, x3;
312         int     n = 0;
313         char    *p = devp;
314
315         x1 = x2 = x3 = 0;
316         *busp = *tgtp = *lunp = 0;
317
318         if (*p != '\0') {
319                 p = astoi(p, &x1);
320                 if (*p == ',') {
321                         p++;
322                         n++;
323                 } else {
324                         if (errs)
325                                 snprintf(errs, slen, "Invalid bus or target specifier in '%s'", devp);
326                         return (-1);
327                 }
328         }
329         if (*p != '\0') {
330                 p = astoi(p, &x2);
331                 if (*p == ',' || *p == '\0') {
332                         if (*p != '\0')
333                                 p++;
334                         n++;
335                 } else {
336                         if (errs)
337                                 snprintf(errs, slen, "Invalid target or lun specifier in '%s'", devp);
338                         return (-1);
339                 }
340         }
341         if (*p != '\0') {
342                 p = astoi(p, &x3);
343                 if (*p == '\0') {
344                         n++;
345                 } else {
346                         if (errs)
347                                 snprintf(errs, slen, "Invalid lun specifier in '%s'", devp);
348                         return (-1);
349                 }
350         }
351         if (n == 3) {
352                 *busp = x1;
353                 *tgtp = x2;
354                 *lunp = x3;
355         }
356         if (n == 2) {
357                 *tgtp = x1;
358                 *lunp = x2;
359         }
360         if (n == 1) {
361                 *tgtp = x1;
362         }
363
364         if (x1 < 0 || x2 < 0 || x3 < 0) {
365                 if (errs)
366                         snprintf(errs, slen, "Invalid value for bus, target or lun (%d,%d,%d)",
367                                 *busp, *tgtp, *lunp);
368                 return (-1);
369         }
370         return (n);
371 }
372
373 int
374 usal_close(SCSI *usalp)
375 {
376         usal__close(usalp);
377         usal_sfree(usalp);
378         return (0);
379 }
380
381 char * usal_natname(SCSI *usalp, int busno, int tgt, int tlun) {
382         return usalp->ops->usalo_natname(usalp, busno, tgt, tlun);
383 }
384
385 int usal_fileno(SCSI *usalp, int busno, int tgt, int tlun) {
386         return usalp->ops->usalo_fileno(usalp, busno, tgt, tlun);
387 }
388
389 void
390 usal_settimeout(SCSI *usalp, int timeout)
391 {
392 #ifdef  nonono
393         if (timeout >= 0)
394                 usalp->deftimeout = timeout;
395 #else
396         usalp->deftimeout = timeout;
397 #endif
398 }
399
400 SCSI *
401 usal_smalloc()
402 {
403         SCSI    *usalp;
404 extern  usal_ops_t usal_dummy_ops;
405
406         usalp = (SCSI *)malloc(sizeof (*usalp));
407         if (usalp == NULL)
408                 return ((SCSI *)0);
409
410         fillbytes(usalp, sizeof (*usalp), 0);
411         usalp->ops      = &usal_dummy_ops;
412         usal_settarget(usalp, -1, -1, -1);
413         usalp->fd       = -1;
414         usalp->deftimeout = 20;
415         usalp->running  = FALSE;
416
417         usalp->cmdstart = (struct timeval *)malloc(sizeof (struct timeval));
418         if (usalp->cmdstart == NULL)
419                 goto err;
420         usalp->cmdstop = (struct timeval *)malloc(sizeof (struct timeval));
421         if (usalp->cmdstop == NULL)
422                 goto err;
423         usalp->scmd = (struct usal_cmd *)malloc(sizeof (struct usal_cmd));
424         if (usalp->scmd == NULL)
425                 goto err;
426         usalp->errstr = malloc(SCSI_ERRSTR_SIZE);
427         if (usalp->errstr == NULL)
428                 goto err;
429         usalp->errptr = usalp->errbeg = usalp->errstr;
430         usalp->errstr[0] = '\0';
431         usalp->errfile = (void *)stderr;
432         usalp->inq = (struct scsi_inquiry *)malloc(sizeof (struct scsi_inquiry));
433         if (usalp->inq == NULL)
434                 goto err;
435         usalp->cap = (struct scsi_capacity *)malloc(sizeof (struct scsi_capacity));
436         if (usalp->cap == NULL)
437                 goto err;
438
439         return (usalp);
440 err:
441         usal_sfree(usalp);
442         return ((SCSI *)0);
443 }
444
445 void
446 usal_sfree(SCSI *usalp)
447 {
448         if (usalp->cmdstart)
449                 free(usalp->cmdstart);
450         if (usalp->cmdstop)
451                 free(usalp->cmdstop);
452         if (usalp->scmd)
453                 free(usalp->scmd);
454         if (usalp->inq)
455                 free(usalp->inq);
456         if (usalp->cap)
457                 free(usalp->cap);
458         if (usalp->local)
459                 free(usalp->local);
460         usal_freebuf(usalp);
461         if (usalp->errstr)
462                 free(usalp->errstr);
463         free(usalp);
464 }