2 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
8 * Copyright (c) 2007, The Storage Networking Industry Association.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * - Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
16 * - Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in
18 * the documentation and/or other materials provided with the
21 * - Neither the name of The Storage Networking Industry Association (SNIA)
22 * nor the names of its contributors may be used to endorse or promote
23 * products derived from this software without specific prior written
26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
27 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
30 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
38 #include <sys/errno.h>
39 #include <sys/types.h>
43 #include <sys/byteorder.h>
44 #include <sys/scsi/impl/uscsi.h>
45 #include <sys/scsi/scsi.h>
48 #include "tlm_proto.h"
51 * generic routine to read a SCSI page
54 read_scsi_page(scsi_link_t
*slink
, union scsi_cdb
*cdb
,
55 int command_size
, caddr_t data
, int size
)
57 struct uscsi_cmd uscsi_cmd
;
61 if (slink
== 0 || slink
->sl_sa
== 0)
64 (void) memset(&uscsi_cmd
, 0, sizeof (uscsi_cmd
));
66 /* Lun is in the 5th bit */
67 cdb
->scc_lun
= slink
->sl_lun
;
68 uscsi_cmd
.uscsi_flags
|= USCSI_READ
| USCSI_ISOLATE
;
69 uscsi_cmd
.uscsi_bufaddr
= data
;
70 uscsi_cmd
.uscsi_buflen
= size
;
71 uscsi_cmd
.uscsi_timeout
= 1000;
72 uscsi_cmd
.uscsi_cdb
= (char *)cdb
;
74 if (cdb
->scc_cmd
== SCMD_READ_ELEMENT_STATUS
) {
75 uscsi_cmd
.uscsi_flags
|= USCSI_RQENABLE
;
76 uscsi_cmd
.uscsi_rqbuf
= data
;
77 uscsi_cmd
.uscsi_rqlen
= size
;
79 uscsi_cmd
.uscsi_cdblen
= command_size
;
81 dname
= sasd_slink_name(slink
);
82 dev
= open(dname
, O_RDWR
| O_NDELAY
);
84 NDMP_LOG(LOG_DEBUG
, "Open failed for %s err=%d",
88 if (tlm_ioctl(dev
, USCSICMD
, &uscsi_cmd
) < 0) {
89 NDMP_LOG(LOG_DEBUG
, "SCSI cmd %d failed for %s err=%d",
90 cdb
->scc_cmd
, dname
, errno
);
95 return (uscsi_cmd
.uscsi_status
);
99 * Read the Inquiry Page.
102 read_inquiry_page(scsi_link_t
*slink
, struct scsi_inquiry
*inq
)
106 (void) memset(&cdb
, 0, sizeof (union scsi_cdb
));
107 cdb
.scc_cmd
= SCMD_INQUIRY
;
108 cdb
.g0_count0
= sizeof (struct scsi_inquiry
);
110 return (read_scsi_page(slink
, &cdb
, CDB_GROUP0
,
111 (caddr_t
)inq
, sizeof (*inq
)) ? -1 : 0);
115 * Read the Product Data Page.
118 read_data_page(scsi_link_t
*slink
, int pcode
, char *snum
, int size
)
120 char cmd
[CDB_GROUP0
];
122 (void) memset(cmd
, 0, sizeof (cmd
));
124 cmd
[0] = SCMD_INQUIRY
;
125 cmd
[1] = pcode
? 0x01 : 0x00;
129 /* LINTED improper alignment */
130 return (read_scsi_page(slink
, (union scsi_cdb
*)&cmd
, CDB_GROUP0
,
131 (caddr_t
)snum
, size
) == -1 ? -1 : 0);
136 * Read the Serial Number Page.
139 read_serial_num_page(scsi_link_t
*slink
, char *snum
, int size
)
141 scsi_serial_t serial
;
144 (void) memset(&serial
, 0, sizeof (scsi_serial_t
));
145 rv
= read_data_page(slink
, SCSI_SERIAL_PAGE
, (caddr_t
)&serial
,
146 sizeof (scsi_serial_t
));
147 (void) strlcpy(snum
, serial
.sr_num
, size
);
149 return (rv
== -1 ? -1 : 0);
154 * Read the Device Name Page.
157 read_dev_name_page(scsi_link_t
*slink
, device_ident_header_t
*devp
, int len
)
159 (void) memset(devp
, 0, len
);
161 if (read_data_page(slink
, SCSI_DEVICE_IDENT_PAGE
, (caddr_t
)devp
,
165 if (devp
->di_page_code
!= SCSI_DEVICE_IDENT_PAGE
)
172 * Formatted print of WWN
175 snprintf_wwn(char *buf
, int size
, uint8_t *wwn
)
177 if (wwn
== NULL
|| buf
== NULL
)
180 (void) snprintf(buf
, size
, "0x%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X",
181 wwn
[0], wwn
[1], wwn
[2], wwn
[3], wwn
[4], wwn
[5], wwn
[6], wwn
[7]);
186 * Extract and print the world wide name (WWN)
189 read_device_wwn(scsi_link_t
*slink
, char *wwnp
, int wsize
)
191 device_ident_header_t
*header
;
193 uint16_t page_len
= sizeof (device_ident_header_t
);
196 uint8_t *designator_data
;
198 (void) memset(wwnp
, 0, wsize
);
200 header
= malloc(page_len
);
204 if (read_dev_name_page(slink
, header
, page_len
) == -1) {
209 act_len
= BE_16(header
->di_page_length
);
210 if (act_len
> page_len
) {
216 ident
= (name_ident_t
*)&header
[1];
217 accessed
= sizeof (device_ident_header_t
);
219 while (accessed
< act_len
) {
221 accessed
+= sizeof (name_ident_t
);
222 accessed
+= ident
->ni_ident_length
;
223 designator_data
= (uint8_t *)&ident
[1];
225 * Looking for code set 1 (Binary) ident type NAA 64 bit
226 * address that is associated with the node (0).
228 if ((ident
->ni_code_set
== 1) &&
229 (ident
->ni_ident_type
== 3)) {
230 snprintf_wwn(wwnp
, wsize
, designator_data
);
232 * If assc is zero (Node) this is the one we want.
233 * If we find that we're done.
235 if (ident
->ni_asso
== 0)
239 * If we find a EUI-64 we can use that also.
241 if ((ident
->ni_code_set
== 2) &&
242 (ident
->ni_ident_type
== 1) &&
243 (ident
->ni_asso
== 0) &&
244 (isprint(wwnp
[0] == 0))) { /* Don't overwrite */
246 * This isn't our first choice but we'll print it
247 * in case there is nothing else to use.
249 (void) snprintf(wwnp
, wsize
, "%.*s",
250 ident
->ni_ident_length
, designator_data
);
253 (name_ident_t
*)&designator_data
[ident
->ni_ident_length
];
257 * See if we found something.
258 * Memset above would leave wwnp not printable.
260 if (isprint(wwnp
[0]))
266 * Add the tape library call back function (used while scanning the bus)
269 add_lib(scsi_link_t
*slink
, struct scsi_inquiry
*sd
, void *arg
)
272 int *nlp
; /* pointer to library counter */
276 NDMP_LOG(LOG_DEBUG
, "Invalid argument %x %x %x",
278 return (-TLM_INVALID
);
281 if (sd
->inq_dtype
== DTYPE_CHANGER
) {
282 /* This is a robot, which means this is also a library */
285 l
= tlm_insert_new_library(slink
);
286 tlm_enable_barcode(l
);
288 NDMP_LOG(LOG_DEBUG
, "lib %d sid %d lun %d",
289 l
, slink
->sl_sid
, slink
->sl_lun
);
291 if ((ssd
= sasd_slink_drive(slink
)) != NULL
) {
292 (void) strlcpy(ssd
->sd_vendor
, sd
->inq_vid
,
293 sizeof (ssd
->sd_vendor
));
294 (void) strlcpy(ssd
->sd_id
, sd
->inq_pid
,
295 sizeof (ssd
->sd_id
));
296 (void) strlcpy(ssd
->sd_rev
, sd
->inq_revision
,
297 sizeof (ssd
->sd_rev
));
298 (void) read_serial_num_page(slink
, ssd
->sd_serial
,
299 sizeof (ssd
->sd_serial
));
300 (void) read_device_wwn(slink
, ssd
->sd_wwn
,
301 sizeof (ssd
->sd_wwn
));
305 return (TLM_NO_ERRORS
);
309 * Create some virutal slots
312 make_virtual_slot(int l
, tlm_drive_t
*dp
)
318 NDMP_LOG(LOG_DEBUG
, "Invalid argument %d, %x", l
, dp
);
319 return (-TLM_INVALID
);
322 if ((s
= tlm_insert_new_slot(l
)) <= 0)
323 return (-TLM_NO_MEMORY
);
325 if (!(sp
= tlm_slot(l
, s
))) {
326 NDMP_LOG(LOG_DEBUG
, "Internal error: slot not found %d", s
);
327 return (-TLM_ERROR_INTERNAL
);
330 * For virtual slots element number is 0 and they are always full.
333 sp
->ts_status_full
= TRUE
;
334 return (TLM_NO_ERRORS
);
338 * Make the tape drive not part of a tape library (stand alone)
341 make_stand_alone_drive(scsi_link_t
*slink
, int l
)
346 if (!slink
|| l
<= 0) {
347 NDMP_LOG(LOG_DEBUG
, "Invalid argument %x %d", slink
, l
);
348 return (-TLM_INVALID
);
351 d
= tlm_insert_new_drive(l
);
352 if (!(dp
= tlm_drive(l
, d
))) {
353 NDMP_LOG(LOG_DEBUG
, "Internal error: drive not found %d", d
);
354 return (-TLM_ERROR_INTERNAL
);
357 /* For stand-alone drives, the element number is the drive number. */
359 dp
->td_slink
= slink
;
360 dp
->td_scsi_id
= slink
->sl_sid
;
361 dp
->td_lun
= slink
->sl_lun
;
362 dp
->td_exists
= TRUE
;
365 * Note: There is no way to remove library elements. We cannot clean
366 * up if make_virtual_slot() fails.
368 (void) make_virtual_slot(l
, dp
);
373 * Find the LIBRARY structure that has control of this DRIVE.
376 new_drive(scsi_link_t
*slink
, int *lib
)
382 /* Walk through all libraries. */
383 for (*lib
= 1; *lib
<= tlm_library_count(); (*lib
)++) {
384 if (!(lp
= tlm_library(*lib
)))
386 /* Walk through drives that are already found. */
387 for (d
= 1; d
<= lp
->tl_drive_count
; d
++) {
388 if (!(dp
= tlm_drive(*lib
, d
)))
390 if (dp
->td_scsi_id
== slink
->sl_sid
&&
391 dp
->td_lun
== slink
->sl_lun
)
396 /* Not part of any library, this is a newly found tape drive. */
402 * Add the tape library call back function (used while scanning the bus)
405 add_drv(scsi_link_t
*slink
, struct scsi_inquiry
*sd
, void *arg
)
408 int *vlp
; /* pointer to virtual library number */
410 tlm_library_t
*library
;
414 NDMP_LOG(LOG_DEBUG
, "Invalid argument %x %x %x",
416 return (-TLM_INVALID
);
419 if (sd
->inq_dtype
== DTYPE_SEQUENTIAL
) {
421 d
= new_drive(slink
, &l
);
423 /* This tape drive was not found inside any robot. */
426 * First, create a virtual library if it's not
429 *vlp
= tlm_insert_new_library(slink
);
430 if ((library
= tlm_library(*vlp
)) != NULL
)
431 library
->tl_capability_robot
= FALSE
;
433 if ((d
= make_stand_alone_drive(slink
, *vlp
)) < 0) {
434 /* sorry, we can not clean up the vlib now * */
435 return (-TLM_INVALID
);
438 NDMP_LOG(LOG_DEBUG
, "vlib(%d, %d) sid %d lun %d",
439 l
, d
, slink
->sl_sid
, slink
->sl_lun
);
441 NDMP_LOG(LOG_DEBUG
, "(%d, %d) sid %d lun %d",
442 l
, d
, slink
->sl_sid
, slink
->sl_lun
);
444 if ((drive
= tlm_drive(l
, d
)) != NULL
) {
445 drive
->td_exists
= TRUE
;
446 drive
->td_slink
= slink
;
448 if ((ssd
= sasd_slink_drive(slink
)) != NULL
) {
449 (void) strlcpy(ssd
->sd_vendor
,
450 sd
->inq_vid
, sizeof (ssd
->sd_vendor
));
451 (void) strlcpy(ssd
->sd_id
, sd
->inq_pid
,
452 sizeof (ssd
->sd_id
));
453 (void) strlcpy(ssd
->sd_rev
, sd
->inq_revision
,
454 sizeof (ssd
->sd_rev
));
455 (void) read_serial_num_page(slink
, ssd
->sd_serial
,
456 sizeof (ssd
->sd_serial
));
457 (void) read_device_wwn(slink
, ssd
->sd_wwn
,
458 sizeof (ssd
->sd_wwn
));
462 return (TLM_NO_ERRORS
);
466 * Scan the specified bus and call the handler function.
469 scan_bus(scsi_adapter_t
*sa
, int(*hndlr
)(), void *args
)
473 struct scsi_inquiry scsi_data
;
476 slink
= sa
->sa_link_head
.sl_next
;
477 for (; slink
!= &sa
->sa_link_head
; slink
= slink
->sl_next
) {
478 (void) memset(&scsi_data
, 0, sizeof (struct scsi_inquiry
));
479 if (read_inquiry_page(slink
, &scsi_data
) == -1)
482 if ((*hndlr
)(slink
, &scsi_data
, args
) != TLM_NO_ERRORS
)
490 * Marks the library/slots inaccessible if there are not enough drives
491 * available on the library
494 inaccbl_drv_warn(int start
, int max
)
500 for (l
= start
; l
< max
; l
++) {
501 if (!(lp
= tlm_library(l
)))
503 if (lp
->tl_drive_count
<= 0)
507 "Warning: The following drives are not accessible:");
508 for (d
= 1; d
<= lp
->tl_drive_count
; d
++)
509 if (!(dname
= tlm_get_tape_name(l
, d
))) {
511 "Error getting drive(%d, %d)", l
, d
);
513 NDMP_LOG(LOG_DEBUG
, "%s", dname
);
516 * Note: Make the slots inaccessible to prevent running
517 * discovery on these libraries. The better idea is
518 * removing these libraries, but we don't have that
519 * feature available now.
521 lp
->tl_slot_count
= 0;
526 * Initialize the tape library data structure, asks the libraries what
527 * equipments they have.
532 static int nlibs
; /* number of found libraries */
540 /* Search through all SCSI adapters, look for tape robots. */
544 * We probe both changers and tape drives here
545 * but later on this needs to be removed as the
546 * probe will happen somewhere else.
548 if (probe_scsi() < 0)
551 nsa
= scsi_get_adapter_count();
552 for (i
= 0; i
< nsa
; i
++)
553 if ((sa
= scsi_get_adapter(i
)))
554 (void) scan_bus(sa
, add_lib
, (void *)&nlibs
);
556 NDMP_LOG(LOG_DEBUG
, "nlibs %d", nlibs
);
558 /* Search through all SCSI adapters, look for tape drives. */
560 for (i
= 0; i
< nsa
; i
++)
561 if ((sa
= scsi_get_adapter(i
)))
562 (void) scan_bus(sa
, add_drv
, (void *)&vlibs
);
564 NDMP_LOG(LOG_DEBUG
, "vlibs %d", vlibs
);
566 if (nlibs
> 0 && vlibs
> 0)
567 inaccbl_drv_warn(nlibs
+ 1, vlibs
+ nlibs
+ 1);
569 for (l
= 1; l
<= tlm_library_count(); l
++) {
570 if (!(lp
= tlm_library(l
))) {
571 NDMP_LOG(LOG_DEBUG
, "can't find lib %d", l
);
576 * Make sure all libraries have tape drives.
578 if (lp
->tl_drive_count
== 0)
582 * Make sure all tape drives exist. A drive that is not
583 * linked into the SCSI chain will be seen by the library
584 * but we cannot talk to it.
586 for (d
= 1; d
<= lp
->tl_drive_count
; d
++) {
587 dp
= tlm_drive(l
, d
);
588 if (dp
&& !dp
->td_exists
) {
589 NDMP_LOG(LOG_DEBUG
, "Ghost drive found %d.%d",
591 lp
->tl_ghost_drives
= TRUE
;
598 rv
= (vlibs
> 0) ? 0 : nlibs
;