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/impl/uscsi.h>
2N/A#include <sys/scsi/generic/commands.h>
2N/A#include <sys/scsi/scsi_pkt.h>
2N/A
2N/A#include <unistd.h>
2N/A#include <fcntl.h>
2N/A#include <errno.h>
2N/A#include <string.h>
2N/A#include <strings.h>
2N/A#include <stdio.h>
2N/A#include <limits.h>
2N/A
2N/A#include <scsi/libscsi.h>
2N/A#include "libscsi_impl.h"
2N/A
2N/Astruct uscsi_dev {
2N/A int fd;
2N/A char *dev;
2N/A};
2N/A
2N/Astatic void *
2N/Auscsi_open(libscsi_hdl_t *hp, const void *target)
2N/A{
2N/A struct uscsi_dev *dp;
2N/A const char *target_name = (const char *)target;
2N/A
2N/A if ((dp = libscsi_zalloc(hp, sizeof (struct uscsi_dev))) == NULL)
2N/A return (NULL);
2N/A
2N/A if ((dp->dev = libscsi_strdup(hp, target_name)) == NULL) {
2N/A libscsi_free(hp, dp);
2N/A return (NULL);
2N/A }
2N/A
2N/A if ((dp->fd = open(target_name, O_RDONLY)) < 0) {
2N/A (void) libscsi_error(hp, ESCSI_BADTARGET, "failed to open %s "
2N/A "for reading: %s", target_name, strerror(errno));
2N/A libscsi_free(hp, dp->dev);
2N/A libscsi_free(hp, dp);
2N/A return (NULL);
2N/A }
2N/A
2N/A return (dp);
2N/A}
2N/A
2N/Astatic void
2N/Auscsi_close(libscsi_hdl_t *hp, void *private)
2N/A{
2N/A struct uscsi_dev *dp = (struct uscsi_dev *)private;
2N/A
2N/A if (dp == NULL)
2N/A return;
2N/A
2N/A if (dp->fd > 0)
2N/A (void) close(dp->fd);
2N/A
2N/A libscsi_free(hp, dp->dev);
2N/A libscsi_free(hp, dp);
2N/A}
2N/A
2N/Astatic int
2N/Axlate_flags(libscsi_hdl_t *hp, uint_t flags, int *uf)
2N/A{
2N/A uint_t f;
2N/A int i;
2N/A
2N/A f = USCSI_REASON; /* we support uscsi_reason */
2N/A
2N/A for (i = 0; i < sizeof (flags) * 8; i++) {
2N/A switch (flags & (1 << i)) {
2N/A case 0:
2N/A continue;
2N/A case LIBSCSI_AF_READ:
2N/A f |= USCSI_READ;
2N/A break;
2N/A case LIBSCSI_AF_WRITE:
2N/A f |= USCSI_WRITE;
2N/A break;
2N/A case LIBSCSI_AF_SILENT:
2N/A f |= USCSI_SILENT;
2N/A break;
2N/A case LIBSCSI_AF_DIAGNOSE:
2N/A f |= USCSI_DIAGNOSE;
2N/A break;
2N/A case LIBSCSI_AF_ISOLATE:
2N/A f |= USCSI_ISOLATE;
2N/A break;
2N/A case LIBSCSI_AF_RQSENSE:
2N/A f |= USCSI_RQENABLE;
2N/A break;
2N/A default:
2N/A return (libscsi_error(hp, ESCSI_BOGUSFLAGS,
2N/A "flag 0x%x is unknown", 1 << i));
2N/A }
2N/A }
2N/A
2N/A *uf = f;
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/* translate uscsi_reason value to libscsi_reason_t */
2N/Astatic libscsi_reason_t
2N/Axlate_reason(uchar_t uscsi_reason)
2N/A{
2N/A switch (uscsi_reason) {
2N/A case USCSI_REASON_RESERVED:
2N/A return ((libscsi_reason_t)LIBSCSI_REASON_INVALID);
2N/A case USCSI_REASON_NONE:
2N/A return (REASON_NONE);
2N/A case USCSI_REASON_TIMEOUT:
2N/A return (REASON_TIMEOUT);
2N/A case USCSI_REASON_DEVGONE:
2N/A return (REASON_DEVGONE);
2N/A case USCSI_REASON_TRANSPORT:
2N/A return (REASON_TRANSPORT);
2N/A case USCSI_REASON_PROTOCOL:
2N/A return (REASON_PROTOCOL);
2N/A case USCSI_REASON_NONSPECIFIC:
2N/A default:
2N/A return (REASON_NONSPECIFIC);
2N/A }
2N/A}
2N/A
2N/Astatic int
2N/Auscsi_exec(libscsi_hdl_t *hp, void *private, libscsi_action_t *ap)
2N/A{
2N/A struct uscsi_dev *dp = (struct uscsi_dev *)private;
2N/A struct uscsi_cmd cmd;
2N/A size_t data_a, data_v;
2N/A uint8_t *cp;
2N/A uint_t flags;
2N/A
2N/A bzero(&cmd, sizeof (cmd));
2N/A
2N/A cp = libscsi_action_get_cdb(ap);
2N/A if (cp == NULL)
2N/A return (-1);
2N/A
2N/A flags = libscsi_action_get_flags(ap);
2N/A if (xlate_flags(hp, flags, &cmd.uscsi_flags) != 0)
2N/A return (-1);
2N/A
2N/A cmd.uscsi_status = (uchar_t)-1;
2N/A cmd.uscsi_reason = USCSI_REASON_RESERVED;
2N/A cmd.uscsi_timeout = (short)libscsi_action_get_timeout(ap);
2N/A
2N/A cmd.uscsi_cdb = (caddr_t)cp;
2N/A cmd.uscsi_cdblen = libscsi_cmd_cdblen(hp, *cp);
2N/A if (cmd.uscsi_cdblen == 0)
2N/A return (-1);
2N/A
2N/A if (flags & (LIBSCSI_AF_READ | LIBSCSI_AF_WRITE)) {
2N/A if (libscsi_action_get_buffer(ap,
2N/A (uint8_t **)&cmd.uscsi_bufaddr, &data_a, &data_v) != 0)
2N/A return (-1);
2N/A if (flags & LIBSCSI_AF_READ)
2N/A cmd.uscsi_buflen = data_a;
2N/A else
2N/A cmd.uscsi_buflen = data_v;
2N/A }
2N/A if (flags & LIBSCSI_AF_RQSENSE) {
2N/A if (libscsi_action_get_sense(ap, (uint8_t **)&cmd.uscsi_rqbuf,
2N/A &data_a, NULL) != 0)
2N/A return (-1);
2N/A if (data_a > UCHAR_MAX)
2N/A data_a = UCHAR_MAX;
2N/A cmd.uscsi_rqlen = (uchar_t)data_a;
2N/A cmd.uscsi_rqstatus = (uchar_t)-1;
2N/A }
2N/A
2N/A if (ioctl(dp->fd, USCSICMD, &cmd) < 0) {
2N/A ASSERT(errno != EFAULT);
2N/A switch (errno) {
2N/A case EINVAL:
2N/A return (libscsi_error(hp, ESCSI_BADCMD, "internal "
2N/A "uscsi error"));
2N/A case EPERM:
2N/A return (libscsi_error(hp, ESCSI_PERM, "insufficient "
2N/A "privileges "));
2N/A case EIO:
2N/A /* Command never executed at all */
2N/A if (cmd.uscsi_reason != USCSI_REASON_RESERVED)
2N/A libscsi_action_set_reason(ap,
2N/A xlate_reason(cmd.uscsi_reason));
2N/A return (libscsi_error(hp, ESCSI_IO, "I/O error, "
2N/A "reason %d", cmd.uscsi_reason));
2N/A default:
2N/A return (libscsi_error(hp, ESCSI_SYS, "uscsi ioctl "
2N/A "failed: %s", strerror(errno)));
2N/A }
2N/A }
2N/A
2N/A libscsi_action_set_status(ap, cmd.uscsi_status);
2N/A if ((flags & LIBSCSI_AF_READ) && libscsi_action_set_datalen(ap,
2N/A cmd.uscsi_buflen - cmd.uscsi_resid) != 0)
2N/A return (-1);
2N/A if ((flags & LIBSCSI_AF_RQSENSE) && libscsi_action_set_senselen(ap,
2N/A cmd.uscsi_rqlen - cmd.uscsi_rqresid) != 0)
2N/A return (-1);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/Astatic const char *
2N/Auscsi_target_name(libscsi_hdl_t *hp, void *private)
2N/A{
2N/A struct uscsi_dev *dp = (struct uscsi_dev *)private;
2N/A
2N/A return (dp->dev);
2N/A}
2N/A
2N/Astatic const libscsi_engine_ops_t uscsi_ops = {
2N/A .lseo_open = uscsi_open,
2N/A .lseo_close = uscsi_close,
2N/A .lseo_exec = uscsi_exec,
2N/A .lseo_target_name = uscsi_target_name
2N/A};
2N/A
2N/Astatic const libscsi_engine_t uscsi_engine = {
2N/A .lse_name = "uscsi",
2N/A .lse_libversion = LIBSCSI_VERSION,
2N/A .lse_ops = &uscsi_ops
2N/A};
2N/A
2N/A/*ARGSUSED*/
2N/Aconst libscsi_engine_t *
2N/Alibscsi_uscsi_init(libscsi_hdl_t *hp)
2N/A{
2N/A return (&uscsi_engine);
2N/A}