Imported Upstream version 1.1.11
[platform/upstream/cdrkit.git] / libusal / scsi-sun.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 /* @(#)scsi-sun.c       1.83 05/11/20 Copyright 1988,1995,2000-2004 J. Schilling */
14 /*
15  *      SCSI user level command transport routines for
16  *      the SCSI general driver 'usal'.
17  *
18  *      Warning: you may change this source, but if you do that
19  *      you need to change the _usal_version and _usal_auth* string below.
20  *      You may not return "schily" for an SCG_AUTHOR request anymore.
21  *      Choose your name instead of "schily" and make clear that the version
22  *      string is related to a modified source.
23  *
24  *      Copyright (c) 1988,1995,2000-2004 J. Schilling
25  */
26 /*
27  * This program is free software; you can redistribute it and/or modify
28  * it under the terms of the GNU General Public License version 2
29  * as published by the Free Software Foundation.
30  *
31  * This program is distributed in the hope that it will be useful,
32  * but WITHOUT ANY WARRANTY; without even the implied warranty of
33  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
34  * GNU General Public License for more details.
35  *
36  * You should have received a copy of the GNU General Public License along with
37  * this program; see the file COPYING.  If not, write to the Free Software
38  * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
39  */
40
41 #include <usal/usalio.h>
42
43 #include <libport.h>            /* Needed for gethostid() */
44 #ifdef  HAVE_SUN_DKIO_H
45 #       include <sun/dkio.h>
46
47 #       define  dk_cinfo        dk_conf
48 #       define  dki_slave       dkc_slave
49 #       define  DKIO_GETCINFO   DKIOCGCONF
50 #endif
51 #ifdef  HAVE_SYS_DKIO_H
52 #       include <sys/dkio.h>
53
54 #       define  DKIO_GETCINFO   DKIOCINFO
55 #endif
56
57 #define TARGET(slave)   ((slave) >> 3)
58 #define LUN(slave)      ((slave) & 07)
59
60 /*
61  * Tht USCSI ioctl() is not usable on SunOS 4.x
62  */
63 #ifdef  __SVR4
64 /*#define       VOLMGT_DEBUG*/
65 #include <volmgt.h>
66 #include <statdefs.h>
67 #       define  USE_USCSI
68 #endif
69
70 static  char    _usal_trans_version[] = "usal-1.83";    /* The version for /dev/usal    */
71 static  char    _usal_utrans_version[] = "uscsi-1.83";  /* The version for USCSI        */
72
73 #ifdef  USE_USCSI
74 static  int     usalo_uhelp(SCSI *usalp, FILE *f);
75 static  int     usalo_uopen(SCSI *usalp, char *device);
76 static  int     usalo_volopen(SCSI *usalp, char *devname);
77 static  int     usalo_openmedia(SCSI *usalp, char *mname);
78 static  int     usalo_uclose(SCSI *usalp);
79 static  int     usalo_ucinfo(int f, struct dk_cinfo *cp, int debug);
80 static  int     usalo_ugettlun(int f, int *tgtp, int *lunp);
81 static  long    usalo_umaxdma(SCSI *usalp, long amt);
82 static  int     usalo_openide(void);
83 static  BOOL    usalo_uhavebus(SCSI *usalp, int);
84 static  int     usalo_ufileno(SCSI *usalp, int, int, int);
85 static  int     usalo_uinitiator_id(SCSI *usalp);
86 static  int     usalo_uisatapi(SCSI *usalp);
87 static  int     usalo_ureset(SCSI *usalp, int what);
88 static  int     usalo_usend(SCSI *usalp);
89
90 static  int     have_volmgt = -1;
91
92 static usal_ops_t sun_uscsi_ops = {
93         usalo_usend,
94         usalo_version,          /* Shared with SCG driver */
95         usalo_uhelp,
96         usalo_uopen,
97         usalo_uclose,
98         usalo_umaxdma,
99         usalo_getbuf,           /* Shared with SCG driver */
100         usalo_freebuf,          /* Shared with SCG driver */
101         usalo_uhavebus,
102         usalo_ufileno,
103         usalo_uinitiator_id,
104         usalo_uisatapi,
105         usalo_ureset,
106 };
107 #endif
108
109 /*
110  * Need to move this into an usal driver ioctl.
111  */
112 /*#define       MAX_DMA_SUN4M   (1024*1024)*/
113 #define MAX_DMA_SUN4M   (124*1024)      /* Currently max working size */
114 /*#define       MAX_DMA_SUN4C   (126*1024)*/
115 #define MAX_DMA_SUN4C   (124*1024)      /* Currently max working size */
116 #define MAX_DMA_SUN3    (63*1024)
117 #define MAX_DMA_SUN386  (56*1024)
118 #define MAX_DMA_OTHER   (32*1024)
119
120 #define ARCH_MASK       0xF0
121 #define ARCH_SUN2       0x00
122 #define ARCH_SUN3       0x10
123 #define ARCH_SUN4       0x20
124 #define ARCH_SUN386     0x30
125 #define ARCH_SUN3X      0x40
126 #define ARCH_SUN4C      0x50
127 #define ARCH_SUN4E      0x60
128 #define ARCH_SUN4M      0x70
129 #define ARCH_SUNX       0x80
130
131 /*
132  * We are using a "real" /dev/usal?
133  */
134 #define scsi_xsend(usalp)       ioctl((usalp)->fd, SCGIO_CMD, (usalp)->scmd)
135 #define MAX_SCG         16      /* Max # of SCSI controllers */
136 #define MAX_TGT         16
137 #define MAX_LUN         8
138
139 struct usal_local {
140         union {
141                 int     SCG_files[MAX_SCG];
142 #ifdef  USE_USCSI
143                 short   usal_files[MAX_SCG][MAX_TGT][MAX_LUN];
144 #endif
145         } u;
146 };
147 #define usallocal(p)    ((struct usal_local *)((p)->local))
148 #define usalfiles(p)    (usallocal(p)->u.SCG_files)
149
150 /*
151  * Return version information for the low level SCSI transport code.
152  * This has been introduced to make it easier to trace down problems
153  * in applications.
154  */
155 static char *
156 usalo_version(SCSI *usalp, int what)
157 {
158         if (usalp != (SCSI *)0) {
159                 switch (what) {
160
161                 case SCG_VERSION:
162 #ifdef  USE_USCSI
163                         if (usalp->ops == &sun_uscsi_ops)
164                                 return (_usal_utrans_version);
165 #endif
166                         return (_usal_trans_version);
167                 /*
168                  * If you changed this source, you are not allowed to
169                  * return "schily" for the SCG_AUTHOR request.
170                  */
171                 case SCG_AUTHOR:
172                         return (_usal_auth_cdrkit);
173                 case SCG_SCCS_ID:
174                         return (__sccsid);
175                 }
176         }
177         return ((char *)0);
178 }
179
180 static int
181 usalo_help(SCSI *usalp, FILE *f)
182 {
183         __usal_help(f, "usal", "Generic transport independent SCSI",
184                 "", "bus,target,lun", "1,2,0", TRUE, FALSE);
185 #ifdef  USE_USCSI
186         usalo_uhelp(usalp, f);
187 #endif
188         return (0);
189 }
190
191 static int
192 usalo_open(SCSI *usalp, char *device)
193 {
194                 int     busno   = usal_scsibus(usalp);
195                 int     tgt     = usal_target(usalp);
196 /*              int     tlun    = usal_lun(usalp);*/
197         register int    f;
198         register int    i;
199         register int    nopen = 0;
200         char            devname[32];
201
202         if (busno >= MAX_SCG) {
203                 errno = EINVAL;
204                 if (usalp->errstr)
205                         snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
206                                 "Illegal value for busno '%d'", busno);
207                 return (-1);
208         }
209
210         if ((device != NULL && *device != '\0') || (busno == -2 && tgt == -2)) {
211 #ifdef  USE_USCSI
212                 usalp->ops = &sun_uscsi_ops;
213                 return (SCGO_OPEN(usalp, device));
214 #else
215                 errno = EINVAL;
216                 if (usalp->errstr)
217                         snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
218                                 "Open by 'devname' not supported on this OS");
219                 return (-1);
220 #endif
221         }
222
223         if (usalp->local == NULL) {
224                 usalp->local = malloc(sizeof (struct usal_local));
225                 if (usalp->local == NULL) {
226                         if (usalp->errstr)
227                                 snprintf(usalp->errstr, SCSI_ERRSTR_SIZE, "No memory for usal_local");
228                         return (0);
229                 }
230
231                 for (i = 0; i < MAX_SCG; i++) {
232                         usalfiles(usalp)[i] = -1;
233                 }
234         }
235
236
237         for (i = 0; i < MAX_SCG; i++) {
238                 /*
239                  * Skip unneeded devices if not in SCSI Bus scan open mode
240                  */
241                 if (busno >= 0 && busno != i)
242                         continue;
243                 snprintf(devname, sizeof (devname), "/dev/usal%d", i);
244                 f = open(devname, O_RDWR);
245                 if (f < 0) {
246                         if (errno != ENOENT && errno != ENXIO) {
247                                 if (usalp->errstr)
248                                         snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
249                                                 "Cannot open '%s'", devname);
250                                 return (-1);
251                         }
252                 } else {
253                         nopen++;
254                 }
255                 usalfiles(usalp)[i] = f;
256         }
257 #ifdef  USE_USCSI
258         if (nopen <= 0) {
259                 if (usalp->local != NULL) {
260                         free(usalp->local);
261                         usalp->local = NULL;
262                 }
263                 usalp->ops = &sun_uscsi_ops;
264                 return (SCGO_OPEN(usalp, device));
265         }
266 #endif
267         return (nopen);
268 }
269
270 static int
271 usalo_close(SCSI *usalp)
272 {
273         register int    i;
274
275         if (usalp->local == NULL)
276                 return (-1);
277
278         for (i = 0; i < MAX_SCG; i++) {
279                 if (usalfiles(usalp)[i] >= 0)
280                         close(usalfiles(usalp)[i]);
281                 usalfiles(usalp)[i] = -1;
282         }
283         return (0);
284 }
285
286 static long
287 usalo_maxdma(SCSI *usalp, long amt)
288 {
289         long    maxdma = MAX_DMA_OTHER;
290 #if     !defined(__i386_) && !defined(i386)
291         int     cpu_type;
292 #endif
293
294 #if     defined(__i386_) || defined(i386)
295         maxdma = MAX_DMA_SUN386;
296 #else
297         cpu_type = gethostid() >> 24;
298
299         switch (cpu_type & ARCH_MASK) {
300
301         case ARCH_SUN4C:
302         case ARCH_SUN4E:
303                 maxdma = MAX_DMA_SUN4C;
304                 break;
305
306         case ARCH_SUN4M:
307         case ARCH_SUNX:
308                 maxdma = MAX_DMA_SUN4M;
309                 break;
310
311         default:
312                 maxdma = MAX_DMA_SUN3;
313         }
314 #endif
315
316 #ifndef __SVR4
317         /*
318          * SunOS 4.x allows esp hardware on VME boards and thus
319          * limits DMA on esp to 64k-1
320          */
321         if (maxdma > MAX_DMA_SUN3)
322                 maxdma = MAX_DMA_SUN3;
323 #endif
324         return (maxdma);
325 }
326
327 static BOOL
328 usalo_havebus(SCSI *usalp, int busno)
329 {
330         if (usalp->local == NULL)
331                 return (FALSE);
332
333         return (busno < 0 || busno >= MAX_SCG) ? FALSE : (usalfiles(usalp)[busno] >= 0);
334 }
335
336 static int
337 usalo_fileno(SCSI *usalp, int busno, int tgt, int tlun)
338 {
339         if (usalp->local == NULL)
340                 return (-1);
341
342         return ((busno < 0 || busno >= MAX_SCG) ? -1 : usalfiles(usalp)[busno]);
343 }
344
345 static int
346 usalo_initiator_id(SCSI *usalp)
347 {
348         int             id = -1;
349 #ifdef  DKIO_GETCINFO
350         struct dk_cinfo conf;
351 #endif
352
353 #ifdef  DKIO_GETCINFO
354         if (usalp->fd < 0)
355                 return (id);
356         if (ioctl(usalp->fd, DKIO_GETCINFO, &conf) < 0)
357                 return (id);
358         if (TARGET(conf.dki_slave) != -1)
359                 id = TARGET(conf.dki_slave);
360 #endif
361         return (id);
362 }
363
364 static int
365 usalo_isatapi(SCSI *usalp)
366 {
367         return (FALSE);
368 }
369
370 static int
371 usalo_reset(SCSI *usalp, int what)
372 {
373         if (what == SCG_RESET_NOP)
374                 return (0);
375         if (what != SCG_RESET_BUS) {
376                 errno = EINVAL;
377                 return (-1);
378         }
379         return (ioctl(usalp->fd, SCGIORESET, 0));
380 }
381
382 static void *
383 usalo_getbuf(SCSI *usalp, long amt)
384 {
385         usalp->bufbase = (void *)valloc((size_t)amt);
386         return (usalp->bufbase);
387 }
388
389 static void
390 usalo_freebuf(SCSI *usalp)
391 {
392         if (usalp->bufbase)
393                 free(usalp->bufbase);
394         usalp->bufbase = NULL;
395 }
396
397 static int
398 usalo_send(SCSI *usalp)
399 {
400         usalp->scmd->target = usal_target(usalp);
401         return (ioctl(usalp->fd, SCGIO_CMD, usalp->scmd));
402 }
403
404 /*--------------------------------------------------------------------------*/
405 /*
406  *      This is Sun USCSI interface code ...
407  */
408 #ifdef  USE_USCSI
409 #include <sys/scsi/impl/uscsi.h>
410
411 /*
412  * Bit Mask definitions, for use accessing the status as a byte.
413  */
414 #define STATUS_MASK                     0x3E
415 #define STATUS_GOOD                     0x00
416 #define STATUS_CHECK                    0x02
417
418 #define STATUS_RESERVATION_CONFLICT     0x18
419 #define STATUS_TERMINATED               0x22
420
421 #ifdef  nonono
422 #define STATUS_MASK                     0x3E
423 #define STATUS_GOOD                     0x00
424 #define STATUS_CHECK                    0x02
425
426 #define STATUS_MET                      0x04
427 #define STATUS_BUSY                     0x08
428 #define STATUS_INTERMEDIATE             0x10
429 #define STATUS_SCSI2                    0x20
430 #define STATUS_INTERMEDIATE_MET         0x14
431 #define STATUS_RESERVATION_CONFLICT     0x18
432 #define STATUS_TERMINATED               0x22
433 #define STATUS_QFULL                    0x28
434 #define STATUS_ACA_ACTIVE               0x30
435 #endif
436
437 static int
438 usalo_uhelp(SCSI *usalp, FILE *f)
439 {
440         __usal_help(f, "USCSI", "SCSI transport for targets known by Solaris drivers",
441                 "USCSI:", "bus,target,lun", "USCSI:1,2,0", TRUE, TRUE);
442         return (0);
443 }
444
445 static int
446 usalo_uopen(SCSI *usalp, char *device)
447 {
448                 int     busno   = usal_scsibus(usalp);
449                 int     tgt     = usal_target(usalp);
450                 int     tlun    = usal_lun(usalp);
451         register int    f;
452         register int    b;
453         register int    t;
454         register int    l;
455         register int    nopen = 0;
456         char            devname[32];
457
458         if (have_volmgt < 0)
459                 have_volmgt = volmgt_running();
460
461         if (usalp->overbose) {
462                 fprintf((FILE *)usalp->errfile,
463                                 "Warning: Using USCSI interface.\n");
464         }
465 #ifndef SEEK_HOLE
466         /*
467          * SEEK_HOLE first appears in Solaris 11 Build 14, volmgt supports
468          * medialess drives since Build 21. Using SEEK_HOLD as indicator
469          * seems to be the best guess.
470          */
471         if (usalp->overbose > 0 && have_volmgt) {
472                 fprintf((FILE *)usalp->errfile,
473                 "Warning: Volume management is running, medialess managed drives are invisible.\n");
474         }
475 #endif
476
477         if (busno >= MAX_SCG || tgt >= MAX_TGT || tlun >= MAX_LUN) {
478                 errno = EINVAL;
479                 if (usalp->errstr) {
480                         snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
481                                 "Illegal value for busno, target or lun '%d,%d,%d'",
482                                         busno, tgt, tlun);
483                 }
484                 return (-1);
485         }
486         if (usalp->local == NULL) {
487                 usalp->local = malloc(sizeof (struct usal_local));
488                 if (usalp->local == NULL) {
489                         if (usalp->errstr)
490                                 snprintf(usalp->errstr, SCSI_ERRSTR_SIZE, "No memory for usal_local");
491                         return (0);
492                 }
493
494                 for (b = 0; b < MAX_SCG; b++) {
495                         for (t = 0; t < MAX_TGT; t++) {
496                                 for (l = 0; l < MAX_LUN; l++)
497                                         usallocal(usalp)->u.usal_files[b][t][l] = (short)-1;
498                         }
499                 }
500         }
501
502         if (device != NULL && strcmp(device, "USCSI") == 0)
503                 goto uscsiscan;
504
505         if ((device != NULL && *device != '\0') || (busno == -2 && tgt == -2))
506                 goto openbydev;
507
508 uscsiscan:
509         if (busno >= 0 && tgt >= 0 && tlun >= 0) {
510
511                 if (busno >= MAX_SCG || tgt >= MAX_TGT || tlun >= MAX_LUN)
512                         return (-1);
513
514                 snprintf(devname, sizeof (devname), "/dev/rdsk/c%dt%dd%ds2",
515                         busno, tgt, tlun);
516                 f = open(devname, O_RDONLY | O_NDELAY);
517                 if (f < 0 && geterrno() == EBUSY)
518                         f = usalo_volopen(usalp, devname);
519                 if (f < 0) {
520                         snprintf(usalp->errstr,
521                                     SCSI_ERRSTR_SIZE,
522                                 "Cannot open '%s'", devname);
523                         return (0);
524                 }
525                 usallocal(usalp)->u.usal_files[busno][tgt][tlun] = f;
526                 return (1);
527         } else {
528
529                 for (b = 0; b < MAX_SCG; b++) {
530                         for (t = 0; t < MAX_TGT; t++) {
531                                 for (l = 0; l < MAX_LUN; l++) {
532                                         snprintf(devname, sizeof (devname),
533                                                 "/dev/rdsk/c%dt%dd%ds2",
534                                                 b, t, l);
535                                         f = open(devname, O_RDONLY | O_NDELAY);
536                                         if (f < 0 && geterrno() == EBUSY) {
537                                                 f = usalo_volopen(usalp, devname);
538                                                 /*
539                                                  * Hack to mark inaccessible
540                                                  * drives with fd == -2
541                                                  */
542                                                 if (f < 0 &&
543                                                     usallocal(usalp)->u.usal_files[b][t][l] < 0)
544                                                         usallocal(usalp)->u.usal_files[b][t][l] = f;
545                                         }
546                                         if (f < 0 && errno != ENOENT &&
547                                                     errno != ENXIO &&
548                                                     errno != ENODEV) {
549                                                 if (usalp->errstr)
550                                                         snprintf(usalp->errstr,
551                                                             SCSI_ERRSTR_SIZE,
552                                                             "Cannot open '%s'", devname);
553                                         }
554                                         if (f < 0 && l == 0)
555                                                 break;
556                                         if (f >= 0) {
557                                                 nopen ++;
558                                                 if (usallocal(usalp)->u.usal_files[b][t][l] == -1)
559                                                         usallocal(usalp)->u.usal_files[b][t][l] = f;
560                                                 else
561                                                         close(f);
562                                         }
563                                 }
564                         }
565                 }
566         }
567 openbydev:
568         if (nopen == 0) {
569                 int     target;
570                 int     lun;
571
572                 if (device != NULL && strncmp(device, "USCSI:", 6) == 0)
573                         device += 6;
574                 if (device == NULL || device[0] == '\0')
575                         return (0);
576
577                 f = open(device, O_RDONLY | O_NDELAY);
578                 if (f < 0)
579                         f = usalo_volopen(usalp, device);
580                 if (f < 0) {
581                         snprintf(usalp->errstr,
582                                     SCSI_ERRSTR_SIZE,
583                                 "Cannot open '%s'", device);
584                         return (0);
585                 }
586
587                 if (busno < 0)
588                         busno = 0;      /* Use Fake number if not specified */
589
590                 if (usalo_ugettlun(f, &target, &lun) >= 0) {
591                         if (tgt >= 0 && tlun >= 0) {
592                                 if (tgt != target || tlun != lun) {
593                                         close(f);
594                                         return (0);
595                                 }
596                         }
597                         tgt = target;
598                         tlun = lun;
599                 } else {
600                         if (tgt < 0 || tlun < 0) {
601                                 close(f);
602                                 return (0);
603                         }
604                 }
605
606                 if (usallocal(usalp)->u.usal_files[busno][tgt][tlun] == -1)
607                         usallocal(usalp)->u.usal_files[busno][tgt][tlun] = f;
608                 usal_settarget(usalp, busno, tgt, tlun);
609
610                 return (++nopen);
611         }
612         return (nopen);
613 }
614
615 static int
616 usalo_volopen(SCSI *usalp, char *devname)
617 {
618         int     oerr = geterrno();
619         int     f = -1;
620         char    *name   = NULL; /* Volume symbolic device name          */
621         char    *symdev = NULL; /* /dev/... name based on "name"        */
622         char    *mname  = NULL; /* Volume media name based on "name"    */
623
624         if (!have_volmgt)
625                 return (-1);
626
627 #ifdef  VOLMGT_DEBUG
628         usalp->debug++;
629 #endif
630         if (usalp->debug > 0) {
631                 fprintf((FILE *)usalp->errfile,
632                         "usalo_volopen(%s)\n", devname);
633         }
634
635         /*
636          * We come here because trying to open "devname" did not work.
637          * First try to translate between a symbolic name and a /dev/...
638          * based device name. Then translate back to a symbolic name.
639          */
640         symdev = volmgt_symdev(devname);
641         if (symdev)
642                 name = volmgt_symname(symdev);
643         if (usalp->debug > 0) {
644                 fprintf((FILE *)usalp->errfile,
645                         "volmgt_symdev(%s)=%s -> %s\n", devname, symdev, name);
646         }
647
648         /*
649          * If "devname" is not a symbolic device name, then it must be
650          * a /dev/... based device name. Try to translate it into a
651          * symbolic name. Then translate back to a /dev/... name.
652          */
653         if (name == NULL) {
654                 name = volmgt_symname(devname);
655                 if (name)
656                         symdev = volmgt_symdev(name);
657         }
658         if (usalp->debug > 0) {
659                 fprintf((FILE *)usalp->errfile,
660                         "volmgt_symdev(%s)=%s -> %s\n", devname, symdev, name);
661         }
662
663         /*
664          * If we have been able to translate to a symbolic device name,
665          * translate this name into a volume management media name that
666          * may be used for opening.
667          */
668         if (name)
669                 mname = media_findname(name);
670         if (usalp->debug > 0) {
671                 fprintf((FILE *)usalp->errfile,
672                         "symdev %s name %s mname %s\n", symdev, name, mname);
673         }
674
675         /*
676          * Das scheint nur mit dem normierten /dev/rdsk/ *s2 Namen zu gehen.
677          */
678         if (usalp->debug > 0) {
679                 fprintf((FILE *)usalp->errfile,
680                         "volmgt_inuse(%s) %d\n", symdev, volmgt_inuse(symdev));
681         }
682         if (mname)
683                 f = usalo_openmedia(usalp, mname);
684         else if (name)
685                 f = -2; /* Mark inaccessible drives with fd == -2 */
686
687         /*
688          * Besonderen Fehlertext oder fprintf/errfile bei non-scanbus Open und
689          * wenn errrno == EBUSY && kein Mapping?
690          */
691         if (name)
692                 free(name);
693         if (symdev)
694                 free(symdev);
695         if (mname)
696                 free(mname);
697         seterrno(oerr);
698 #ifdef  VOLMGT_DEBUG
699         usalp->debug--;
700 #endif
701         return (f);
702 }
703
704 static int
705 usalo_openmedia(SCSI *usalp, char *mname)
706 {
707         int     f = -1;
708         char    *device = NULL;
709         struct  stat sb;
710
711         if (mname == NULL)
712                 return (-1);
713
714         /*
715          * Check whether the media name refers to a directory.
716          * In this case, the medium is partitioned and we need to
717          * check all partitions.
718          */
719         if (stat(mname, &sb) >= 0) {
720                 if (S_ISDIR(sb.st_mode)) {
721                         char    name[128];
722                         int     i;
723
724                         /*
725                          * First check partition '2', the whole disk.
726                          */
727                         snprintf(name, sizeof (name), "%s/s2", mname);
728                         f = open(name, O_RDONLY | O_NDELAY);
729                         if (f >= 0)
730                                 return (f);
731                         /*
732                          * Now try all other partitions.
733                          */
734                         for (i = 0; i < 16; i++) {
735                                 if (i == 2)
736                                         continue;
737                                 snprintf(name, sizeof (name),
738                                                         "%s/s%d", mname, i);
739                                 if (stat(name, &sb) >= 0)
740                                         break;
741                         }
742                         if (i < 16) {
743                                 device = mname;
744                         }
745                 } else {
746                         device = mname;
747                 }
748         }
749         if (device)
750                 f = open(device, O_RDONLY | O_NDELAY);
751         return (f);
752 }
753
754 static int
755 usalo_uclose(SCSI *usalp)
756 {
757         register int    f;
758         register int    b;
759         register int    t;
760         register int    l;
761
762         if (usalp->local == NULL)
763                 return (-1);
764
765         for (b = 0; b < MAX_SCG; b++) {
766                 for (t = 0; t < MAX_TGT; t++) {
767                         for (l = 0; l < MAX_LUN; l++) {
768                                 f = usallocal(usalp)->u.usal_files[b][t][l];
769                                 if (f >= 0)
770                                         close(f);
771                                 usallocal(usalp)->u.usal_files[b][t][l] = (short)-1;
772                         }
773                 }
774         }
775         return (0);
776 }
777
778 static int
779 usalo_ucinfo(int f, struct dk_cinfo *cp, int debug)
780 {
781         fillbytes(cp, sizeof (*cp), '\0');
782
783         if (ioctl(f, DKIOCINFO, cp) < 0)
784                 return (-1);
785
786         if (debug <= 0)
787                 return (0);
788
789         fprintf(stderr, "cname:         '%s'\n", cp->dki_cname);
790         fprintf(stderr, "ctype:         0x%04hX %hd\n", cp->dki_ctype, cp->dki_ctype);
791         fprintf(stderr, "cflags:                0x%04hX\n", cp->dki_flags);
792         fprintf(stderr, "cnum:          %hd\n", cp->dki_cnum);
793 #ifdef  __EVER__
794         fprintf(stderr, "addr:          %d\n", cp->dki_addr);
795         fprintf(stderr, "space:         %d\n", cp->dki_space);
796         fprintf(stderr, "prio:          %d\n", cp->dki_prio);
797         fprintf(stderr, "vec:           %d\n", cp->dki_vec);
798 #endif
799         fprintf(stderr, "dname:         '%s'\n", cp->dki_dname);
800         fprintf(stderr, "unit:          %d\n", cp->dki_unit);
801         fprintf(stderr, "slave:         %d %04o Tgt: %d Lun: %d\n",
802                                 cp->dki_slave, cp->dki_slave,
803                                 TARGET(cp->dki_slave), LUN(cp->dki_slave));
804         fprintf(stderr, "partition:     %hd\n", cp->dki_partition);
805         fprintf(stderr, "maxtransfer:   %d (%d)\n",
806                                 cp->dki_maxtransfer,
807                                 cp->dki_maxtransfer * DEV_BSIZE);
808         return (0);
809 }
810
811 static int
812 usalo_ugettlun(int f, int *tgtp, int *lunp)
813 {
814         struct dk_cinfo ci;
815
816         if (usalo_ucinfo(f, &ci, 0) < 0)
817                 return (-1);
818         if (tgtp)
819                 *tgtp = TARGET(ci.dki_slave);
820         if (lunp)
821                 *lunp = LUN(ci.dki_slave);
822         return (0);
823 }
824
825 static long
826 usalo_umaxdma(SCSI *usalp, long amt)
827 {
828         register int    b;
829         register int    t;
830         register int    l;
831         long            maxdma = -1L;
832         int             f;
833         struct dk_cinfo ci;
834         BOOL            found_ide = FALSE;
835
836         if (usalp->local == NULL)
837                 return (-1L);
838
839         for (b = 0; b < MAX_SCG; b++) {
840                 for (t = 0; t < MAX_TGT; t++) {
841                         for (l = 0; l < MAX_LUN; l++) {
842                                 if ((f = usallocal(usalp)->u.usal_files[b][t][l]) < 0)
843                                         continue;
844                                 if (usalo_ucinfo(f, &ci, usalp->debug) < 0)
845                                         continue;
846                                 if (maxdma < 0)
847                                         maxdma = (long)(ci.dki_maxtransfer * DEV_BSIZE);
848                                 if (maxdma > (long)(ci.dki_maxtransfer * DEV_BSIZE))
849                                         maxdma = (long)(ci.dki_maxtransfer * DEV_BSIZE);
850                                 if (streql(ci.dki_cname, "ide"))
851                                         found_ide = TRUE;
852                         }
853                 }
854         }
855
856 #if     defined(__i386_) || defined(i386)
857         /*
858          * At least on Solaris 9 x86, DKIOCINFO returns a wrong value
859          * for dki_maxtransfer if the target is an ATAPI drive.
860          * Without DMA, it seems to work if we use 256 kB DMA size for ATAPI,
861          * but if we allow DMA, only 68 kB will work (for more we get a silent
862          * DMA residual count == DMA transfer count).
863          * For this reason, we try to figure out the correct value for 'ide'
864          * by retrieving the (correct) value from a ide hard disk.
865          */
866         if (found_ide) {
867                 if ((f = usalo_openide()) >= 0) {
868 #ifdef  sould_we
869                         long omaxdma = maxdma;
870 #endif
871
872                         if (usalo_ucinfo(f, &ci, usalp->debug) >= 0) {
873                                 if (maxdma < 0)
874                                         maxdma = (long)(ci.dki_maxtransfer * DEV_BSIZE);
875                                 if (maxdma > (long)(ci.dki_maxtransfer * DEV_BSIZE))
876                                         maxdma = (long)(ci.dki_maxtransfer * DEV_BSIZE);
877                         }
878                         close(f);
879 #ifdef  sould_we
880                         /*
881                          * The kernel returns 56 kB but we tested that 68 kB works.
882                          */
883                         if (omaxdma > maxdma && maxdma == (112 * DEV_BSIZE))
884                                 maxdma = 136 * DEV_BSIZE;
885 #endif
886                 } else {
887                         /*
888                          * No IDE disk on this system?
889                          */
890                         if (maxdma == (512 * DEV_BSIZE))
891                                 maxdma = MAX_DMA_SUN386;
892                 }
893         }
894 #endif
895         /*
896          * The Sun tape driver does not support to retrieve the max DMA count.
897          * Use the knwoledge about default DMA sizes in this case.
898          */
899         if (maxdma < 0)
900                 maxdma = usalo_maxdma(usalp, amt);
901
902         return (maxdma);
903 }
904
905 #if     defined(__i386_) || defined(i386)
906 static int
907 usalo_openide()
908 {
909         char    buf[20];
910         int     b;
911         int     t;
912         int     f = -1;
913
914         for (b = 0; b < 5; b++) {
915                 for (t = 0; t < 2; t++) {
916                         snprintf(buf, sizeof (buf),
917                                 "/dev/rdsk/c%dd%dp0", b, t);
918                         if ((f = open(buf, O_RDONLY | O_NDELAY)) >= 0)
919                                 goto out;
920                 }
921         }
922 out:
923         return (f);
924 }
925 #endif
926
927 static BOOL
928 usalo_uhavebus(SCSI *usalp, int busno)
929 {
930         register int    t;
931         register int    l;
932
933         if (usalp->local == NULL || busno < 0 || busno >= MAX_SCG)
934                 return (FALSE);
935
936         for (t = 0; t < MAX_TGT; t++) {
937                 for (l = 0; l < MAX_LUN; l++)
938                         if (usallocal(usalp)->u.usal_files[busno][t][l] >= 0)
939                                 return (TRUE);
940         }
941         return (FALSE);
942 }
943
944 static int
945 usalo_ufileno(SCSI *usalp, int busno, int tgt, int tlun)
946 {
947         if (usalp->local == NULL ||
948             busno < 0 || busno >= MAX_SCG ||
949             tgt < 0 || tgt >= MAX_TGT ||
950             tlun < 0 || tlun >= MAX_LUN)
951                 return (-1);
952
953         return ((int)usallocal(usalp)->u.usal_files[busno][tgt][tlun]);
954 }
955
956 static int
957 usalo_uinitiator_id(SCSI *usalp)
958 {
959         return (-1);
960 }
961
962 static int
963 usalo_uisatapi(SCSI *usalp)
964 {
965         char            devname[32];
966         char            symlinkname[MAXPATHLEN];
967         int             len;
968         struct dk_cinfo ci;
969
970         if (ioctl(usalp->fd, DKIOCINFO, &ci) < 0)
971                 return (-1);
972
973         snprintf(devname, sizeof (devname), "/dev/rdsk/c%dt%dd%ds2",
974                 usal_scsibus(usalp), usal_target(usalp), usal_lun(usalp));
975
976         symlinkname[0] = '\0';
977         len = readlink(devname, symlinkname, sizeof (symlinkname));
978         if (len > 0)
979                 symlinkname[len] = '\0';
980
981         if (len >= 0 && strstr(symlinkname, "ide") != NULL)
982                 return (TRUE);
983         else
984                 return (FALSE);
985 }
986
987 static int
988 usalo_ureset(SCSI *usalp, int what)
989 {
990         struct uscsi_cmd req;
991
992         if (what == SCG_RESET_NOP)
993                 return (0);
994
995         fillbytes(&req, sizeof (req), '\0');
996
997         if (what == SCG_RESET_TGT) {
998                 req.uscsi_flags = USCSI_RESET | USCSI_SILENT;   /* reset target */
999         } else if (what != SCG_RESET_BUS) {
1000                 req.uscsi_flags = USCSI_RESET_ALL | USCSI_SILENT; /* reset bus */
1001         } else {
1002                 errno = EINVAL;
1003                 return (-1);
1004         }
1005
1006         return (ioctl(usalp->fd, USCSICMD, &req));
1007 }
1008
1009 static int
1010 usalo_usend(SCSI *usalp)
1011 {
1012         struct usal_cmd *sp = usalp->scmd;
1013         struct uscsi_cmd req;
1014         int             ret;
1015 static  uid_t           cureuid = 0;    /* XXX Hack until we have uid management */
1016
1017         if (usalp->fd < 0) {
1018                 sp->error = SCG_FATAL;
1019                 return (0);
1020         }
1021
1022         fillbytes(&req, sizeof (req), '\0');
1023
1024         req.uscsi_flags = USCSI_SILENT | USCSI_DIAGNOSE | USCSI_RQENABLE;
1025
1026         if (sp->flags & SCG_RECV_DATA) {
1027                 req.uscsi_flags |= USCSI_READ;
1028         } else if (sp->size > 0) {
1029                 req.uscsi_flags |= USCSI_WRITE;
1030         }
1031         req.uscsi_buflen        = sp->size;
1032         req.uscsi_bufaddr       = sp->addr;
1033         req.uscsi_timeout       = sp->timeout;
1034         req.uscsi_cdblen        = sp->cdb_len;
1035         req.uscsi_rqbuf         = (caddr_t) sp->u_sense.cmd_sense;
1036         req.uscsi_rqlen         = sp->sense_len;
1037         req.uscsi_cdb           = (caddr_t) &sp->cdb;
1038
1039         if (cureuid != 0)
1040                 seteuid(0);
1041 again:
1042         errno = 0;
1043         ret = ioctl(usalp->fd, USCSICMD, &req);
1044
1045         if (ret < 0 && geterrno() == EPERM) {   /* XXX Hack until we have uid management */
1046                 cureuid = geteuid();
1047                 if (seteuid(0) >= 0)
1048                         goto again;
1049         }
1050         if (cureuid != 0)
1051                 seteuid(cureuid);
1052
1053         if (usalp->debug > 0) {
1054                 fprintf((FILE *)usalp->errfile, "ret: %d errno: %d (%s)\n", ret, errno, errmsgstr(errno));
1055                 fprintf((FILE *)usalp->errfile, "uscsi_flags:     0x%x\n", req.uscsi_flags);
1056                 fprintf((FILE *)usalp->errfile, "uscsi_status:    0x%x\n", req.uscsi_status);
1057                 fprintf((FILE *)usalp->errfile, "uscsi_timeout:   %d\n", req.uscsi_timeout);
1058                 fprintf((FILE *)usalp->errfile, "uscsi_bufaddr:   0x%lx\n", (long)req.uscsi_bufaddr);
1059                                                                 /*
1060                                                                  * Cast auf int OK solange sp->size
1061                                                                  * auch ein int bleibt.
1062                                                                  */
1063                 fprintf((FILE *)usalp->errfile, "uscsi_buflen:    %d\n", (int)req.uscsi_buflen);
1064                 fprintf((FILE *)usalp->errfile, "uscsi_resid:     %d\n", (int)req.uscsi_resid);
1065                 fprintf((FILE *)usalp->errfile, "uscsi_rqlen:     %d\n", req.uscsi_rqlen);
1066                 fprintf((FILE *)usalp->errfile, "uscsi_rqstatus:  0x%x\n", req.uscsi_rqstatus);
1067                 fprintf((FILE *)usalp->errfile, "uscsi_rqresid:   %d\n", req.uscsi_rqresid);
1068                 fprintf((FILE *)usalp->errfile, "uscsi_rqbuf ptr: 0x%lx\n", (long)req.uscsi_rqbuf);
1069                 fprintf((FILE *)usalp->errfile, "uscsi_rqbuf:     ");
1070                 if (req.uscsi_rqbuf != NULL && req.uscsi_rqlen > req.uscsi_rqresid) {
1071                         int     i;
1072                         int     len = req.uscsi_rqlen - req.uscsi_rqresid;
1073
1074                         for (i = 0; i < len; i++) {
1075                                 fprintf((FILE *)usalp->errfile, "0x%02X ", ((char *)req.uscsi_rqbuf)[i]);
1076                         }
1077                         fprintf((FILE *)usalp->errfile, "\n");
1078                 } else {
1079                         fprintf((FILE *)usalp->errfile, "<data not available>\n");
1080                 }
1081         }
1082         if (ret < 0) {
1083                 sp->ux_errno = geterrno();
1084                 /*
1085                  * Check if SCSI command cound not be send at all.
1086                  */
1087                 if (sp->ux_errno == ENOTTY && usalo_uisatapi(usalp) == TRUE) {
1088                         if (usalp->debug > 0) {
1089                                 fprintf((FILE *)usalp->errfile,
1090                                         "ENOTTY atapi: %d\n", usalo_uisatapi(usalp));
1091                         }
1092                         sp->error = SCG_FATAL;
1093                         return (0);
1094                 }
1095                 if (errno == ENXIO) {
1096                         sp->error = SCG_FATAL;
1097                         return (0);
1098                 }
1099                 if (errno == ENOTTY || errno == EINVAL || errno == EACCES) {
1100                         return (-1);
1101                 }
1102         } else {
1103                 sp->ux_errno = 0;
1104         }
1105         ret                     = 0;
1106         sp->sense_count         = req.uscsi_rqlen - req.uscsi_rqresid;
1107         sp->resid               = req.uscsi_resid;
1108         sp->u_scb.cmd_scb[0]    = req.uscsi_status;
1109
1110         if ((req.uscsi_status & STATUS_MASK) == STATUS_GOOD) {
1111                 sp->error = SCG_NO_ERROR;
1112                 return (0);
1113         }
1114         if (req.uscsi_rqstatus == 0 &&
1115             ((req.uscsi_status & STATUS_MASK) == STATUS_CHECK)) {
1116                 sp->error = SCG_NO_ERROR;
1117                 return (0);
1118         }
1119         if (req.uscsi_status & (STATUS_TERMINATED |
1120             STATUS_RESERVATION_CONFLICT)) {
1121                 sp->error = SCG_FATAL;
1122         }
1123         if (req.uscsi_status != 0) {
1124                 /*
1125                  * This is most likely wrong. There seems to be no way
1126                  * to produce SCG_RETRYABLE with USCSI.
1127                  */
1128                 sp->error = SCG_RETRYABLE;
1129         }
1130
1131         return (ret);
1132 }
1133 #endif  /* USE_USCSI */