2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include <sys/types.h>
2N/A#include <sys/scsi/generic/commands.h>
2N/A#include <sys/scsi/impl/spc3_types.h>
2N/A
2N/A#include <stddef.h>
2N/A#include <stdlib.h>
2N/A#include <string.h>
2N/A#include <strings.h>
2N/A#include <alloca.h>
2N/A#include <stdio.h>
2N/A#include <unistd.h>
2N/A#include <dlfcn.h>
2N/A
2N/A#include <scsi/libscsi.h>
2N/A#include "libscsi_impl.h"
2N/A
2N/Aint
2N/Alibscsi_assert(const char *expr, const char *file, int line)
2N/A{
2N/A char *msg;
2N/A size_t len;
2N/A
2N/A len = snprintf(NULL, 0,
2N/A "ABORT: \"%s\", line %d: assertion failed: %s\n", file, line, expr);
2N/A
2N/A msg = alloca(len + 1);
2N/A
2N/A (void) snprintf(msg, len + 1,
2N/A "ABORT: \"%s\", line %d: assertion failed: %s\n", file, line, expr);
2N/A
2N/A (void) write(STDERR_FILENO, msg, strlen(msg));
2N/A
2N/A abort();
2N/A
2N/A /*NOTREACHED*/
2N/A _exit(1);
2N/A
2N/A /*NOTREACHED*/
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/Alibscsi_set_errno(libscsi_hdl_t *hp, libscsi_errno_t err)
2N/A{
2N/A hp->lsh_errno = err;
2N/A hp->lsh_errmsg[0] = '\0';
2N/A
2N/A return (-1);
2N/A}
2N/A
2N/A/*
2N/A * Internal routine for setting both _ue_errno and _ue_errmsg. We save
2N/A * and restore the UNIX errno across this routing so the caller can use either
2N/A * libscsi_set_errno(), libscsi_error(), or libscsi_verror() without this value
2N/A * changing.
2N/A */
2N/Aint
2N/Alibscsi_verror(libscsi_hdl_t *hp, libscsi_errno_t err, const char *fmt,
2N/A va_list ap)
2N/A{
2N/A size_t n;
2N/A char *errmsg;
2N/A
2N/A /*
2N/A * To allow the existing error message to itself be used in an error
2N/A * message, we put the new error message into a buffer on the stack,
2N/A * and then copy it into lsh_errmsg. We also need to set the errno,
2N/A * but because the call to libscsi_set_errno() is destructive to
2N/A * lsh_errmsg, we do this after we print into our temporary buffer
2N/A * (in case _libscsi_errmsg is part of the error message) and before we
2N/A * copy the temporary buffer on to _libscsi_errmsg (to prevent our new
2N/A * message from being nuked by the call to libscsi_set_errno()).
2N/A */
2N/A errmsg = alloca(sizeof (hp->lsh_errmsg));
2N/A (void) vsnprintf(errmsg, sizeof (hp->lsh_errmsg), fmt, ap);
2N/A (void) libscsi_set_errno(hp, err);
2N/A
2N/A n = strlen(errmsg);
2N/A
2N/A if (n != 0 && errmsg[n - 1] == '\n')
2N/A errmsg[n - 1] = '\0';
2N/A
2N/A bcopy(errmsg, hp->lsh_errmsg, n + 1);
2N/A
2N/A return (-1);
2N/A}
2N/A
2N/A/*PRINTFLIKE3*/
2N/Aint
2N/Alibscsi_error(libscsi_hdl_t *hp, libscsi_errno_t err, const char *fmt, ...)
2N/A{
2N/A va_list ap;
2N/A
2N/A if (fmt == NULL)
2N/A return (libscsi_set_errno(hp, err));
2N/A
2N/A va_start(ap, fmt);
2N/A err = libscsi_verror(hp, err, fmt, ap);
2N/A va_end(ap);
2N/A
2N/A return (err);
2N/A}
2N/A
2N/Alibscsi_errno_t
2N/Alibscsi_errno(libscsi_hdl_t *hp)
2N/A{
2N/A return (hp->lsh_errno);
2N/A}
2N/A
2N/Aconst char *
2N/Alibscsi_errmsg(libscsi_hdl_t *hp)
2N/A{
2N/A if (hp->lsh_errmsg[0] == '\0')
2N/A (void) strlcpy(hp->lsh_errmsg, libscsi_strerror(hp->lsh_errno),
2N/A sizeof (hp->lsh_errmsg));
2N/A
2N/A return (hp->lsh_errmsg);
2N/A}
2N/A
2N/Avoid *
2N/Alibscsi_alloc(libscsi_hdl_t *hp, size_t size)
2N/A{
2N/A void *mem;
2N/A
2N/A if (size == 0) {
2N/A (void) libscsi_set_errno(hp, ESCSI_ZERO_LENGTH);
2N/A return (NULL);
2N/A }
2N/A
2N/A if ((mem = malloc(size)) == NULL)
2N/A (void) libscsi_set_errno(hp, ESCSI_NOMEM);
2N/A
2N/A return (mem);
2N/A}
2N/A
2N/Avoid *
2N/Alibscsi_zalloc(libscsi_hdl_t *hp, size_t size)
2N/A{
2N/A void *mem;
2N/A
2N/A if ((mem = libscsi_alloc(hp, size)) == NULL)
2N/A return (NULL);
2N/A
2N/A bzero(mem, size);
2N/A
2N/A return (mem);
2N/A}
2N/A
2N/Achar *
2N/Alibscsi_strdup(libscsi_hdl_t *hp, const char *str)
2N/A{
2N/A size_t len = strlen(str);
2N/A char *dup = libscsi_alloc(hp, len + 1);
2N/A
2N/A if (dup == NULL)
2N/A return (NULL);
2N/A
2N/A return (strcpy(dup, str));
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/Avoid
2N/Alibscsi_free(libscsi_hdl_t *hp, void *ptr)
2N/A{
2N/A free(ptr);
2N/A}
2N/A
2N/Alibscsi_hdl_t *
2N/Alibscsi_init(uint_t version, libscsi_errno_t *errp)
2N/A{
2N/A libscsi_hdl_t *hp;
2N/A
2N/A if ((hp = malloc(sizeof (libscsi_hdl_t))) == NULL) {
2N/A if (errp != NULL)
2N/A *errp = ESCSI_NOMEM;
2N/A return (NULL);
2N/A }
2N/A
2N/A bzero(hp, sizeof (libscsi_hdl_t));
2N/A hp->lsh_version = version;
2N/A
2N/A return (hp);
2N/A}
2N/A
2N/Avoid
2N/Alibscsi_fini(libscsi_hdl_t *hp)
2N/A{
2N/A libscsi_engine_impl_t *eip, *neip;
2N/A
2N/A if (hp == NULL)
2N/A return;
2N/A
2N/A ASSERT(hp->lsh_targets == 0);
2N/A
2N/A for (eip = hp->lsh_engines; eip != NULL; eip = neip) {
2N/A neip = eip->lsei_next;
2N/A (void) dlclose(eip->lsei_dl_hdl);
2N/A libscsi_free(hp, eip);
2N/A }
2N/A
2N/A free(hp);
2N/A}
2N/A
2N/Asize_t
2N/Alibscsi_cmd_cdblen(libscsi_hdl_t *hp, uint8_t cmd)
2N/A{
2N/A size_t sz;
2N/A
2N/A switch (CDB_GROUPID(cmd)) {
2N/A case CDB_GROUPID_0:
2N/A sz = CDB_GROUP0;
2N/A break;
2N/A case CDB_GROUPID_1:
2N/A sz = CDB_GROUP1;
2N/A break;
2N/A case CDB_GROUPID_2:
2N/A sz = CDB_GROUP2;
2N/A break;
2N/A case CDB_GROUPID_3:
2N/A sz = CDB_GROUP3;
2N/A break;
2N/A case CDB_GROUPID_4:
2N/A sz = CDB_GROUP4;
2N/A break;
2N/A case CDB_GROUPID_5:
2N/A sz = CDB_GROUP5;
2N/A break;
2N/A case CDB_GROUPID_6:
2N/A sz = CDB_GROUP6;
2N/A break;
2N/A case CDB_GROUPID_7:
2N/A sz = CDB_GROUP7;
2N/A break;
2N/A default:
2N/A sz = 0;
2N/A }
2N/A
2N/A if (sz == 0)
2N/A (void) libscsi_error(hp, ESCSI_BADCMD,
2N/A "unknown or unsupported command %u", cmd);
2N/A
2N/A return (sz);
2N/A}
2N/A
2N/Astatic char *
2N/Alibscsi_process_inquiry_string(libscsi_hdl_t *hp, const char *raw, size_t len)
2N/A{
2N/A char *buf;
2N/A
2N/A buf = alloca(len + 1);
2N/A bcopy(raw, buf, len);
2N/A
2N/A for (; len > 0; len--) {
2N/A if (buf[len - 1] != ' ')
2N/A break;
2N/A }
2N/A
2N/A buf[len] = '\0';
2N/A
2N/A return (libscsi_strdup(hp, buf));
2N/A}
2N/A
2N/A/*
2N/A * As part of basic initialization, we always retrieve the INQUIRY information
2N/A * to have the vendor/product/revision information available for all consumers.
2N/A */
2N/Aint
2N/Alibscsi_get_inquiry(libscsi_hdl_t *hp, libscsi_target_t *tp)
2N/A{
2N/A libscsi_action_t *ap;
2N/A spc3_inquiry_cdb_t *cp;
2N/A spc3_inquiry_data_t data;
2N/A size_t len;
2N/A
2N/A if ((ap = libscsi_action_alloc(hp, SPC3_CMD_INQUIRY,
2N/A LIBSCSI_AF_READ | LIBSCSI_AF_SILENT, &data,
2N/A offsetof(spc3_inquiry_data_t, id_vs_36[0]))) == NULL)
2N/A return (-1);
2N/A
2N/A cp = (spc3_inquiry_cdb_t *)libscsi_action_get_cdb(ap);
2N/A
2N/A SCSI_WRITE16(&cp->ic_allocation_length,
2N/A offsetof(spc3_inquiry_data_t, id_vs_36[0]));
2N/A
2N/A if (libscsi_exec(ap, tp) != 0 ||
2N/A libscsi_action_get_status(ap) != 0) {
2N/A libscsi_action_free(ap);
2N/A return (libscsi_set_errno(hp, ESCSI_INQUIRY_FAILED));
2N/A }
2N/A
2N/A (void) libscsi_action_get_buffer(ap, NULL, NULL, &len);
2N/A libscsi_action_free(ap);
2N/A
2N/A if (len < offsetof(spc3_inquiry_data_t, id_vs_36))
2N/A return (libscsi_set_errno(hp, ESCSI_INQUIRY_FAILED));
2N/A
2N/A if ((tp->lst_vendor = libscsi_process_inquiry_string(hp,
2N/A data.id_vendor_id, sizeof (data.id_vendor_id))) == NULL ||
2N/A (tp->lst_product = libscsi_process_inquiry_string(hp,
2N/A data.id_product_id, sizeof (data.id_product_id))) == NULL ||
2N/A (tp->lst_revision = libscsi_process_inquiry_string(hp,
2N/A data.id_product_revision,
2N/A sizeof (data.id_product_revision))) == NULL) {
2N/A return (-1);
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Aconst char *
2N/Alibscsi_vendor(libscsi_target_t *tp)
2N/A{
2N/A return (tp->lst_vendor);
2N/A}
2N/A
2N/Aconst char *
2N/Alibscsi_product(libscsi_target_t *tp)
2N/A{
2N/A return (tp->lst_product);
2N/A}
2N/A
2N/Aconst char *
2N/Alibscsi_revision(libscsi_target_t *tp)
2N/A{
2N/A return (tp->lst_revision);
2N/A}