root/sbin/scsictl/scsi_subr.c
/*      $NetBSD: scsi_subr.c,v 1.14 2012/02/21 02:22:54 jakllsch Exp $  */

/*-
 * Copyright (c) 1998 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Charles M. Hannum; Jason R. Thorpe of the Numerical Aerospace
 * Simulation Facility, NASA Ames Research Center.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * SCSI support subroutines.
 *
 * XXX THESE SHOULD BE IN A LIBRARY!
 */
#include <sys/cdefs.h>

#ifndef lint
__RCSID("$NetBSD: scsi_subr.c,v 1.14 2012/02/21 02:22:54 jakllsch Exp $");
#endif


#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/scsiio.h>
#include <err.h> 
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <dev/scsipi/scsi_spc.h>

#include "extern.h"

#define STRVIS_ISWHITE(x) ((x) == ' ' || (x) == '\0' || (x) == (u_char)'\377')

void
scsi_command(int fd, const void *cmd, size_t cmdlen,
        void *data, size_t datalen, int timeout, int flags)
{
        scsireq_t req;

        memset(&req, 0, sizeof(req));

        cmdlen = MIN(cmdlen, sizeof(req.cmd));
        memcpy(req.cmd, cmd, cmdlen);
        req.cmdlen = cmdlen;
        req.databuf = data;
        req.datalen = datalen;
        req.timeout = timeout;
        req.flags = flags;
        req.senselen = SENSEBUFLEN;

        if (ioctl(fd, SCIOCCOMMAND, &req) == -1)
                err(1, "SCIOCCOMMAND");

        if (req.retsts == SCCMD_OK)
                return;

        /* Some problem; report it and exit. */
        if (req.retsts == SCCMD_TIMEOUT)
                fprintf(stderr, "%s: SCSI command timed out\n", dvname);
        else if (req.retsts == SCCMD_BUSY)
                fprintf(stderr, "%s: device is busy\n", dvname);
        else if (req.retsts == SCCMD_SENSE)
                scsi_print_sense(dvname, &req, 1);
        else
                fprintf(stderr, "%s: device had unknown status %x\n", dvname,
                    req.retsts);

        exit(1);
}

void
scsi_mode_sense(int fd, u_int8_t pgcode, u_int8_t pctl, void *buf, size_t len)
{
        struct scsi_mode_sense_6 cmd;

        memset(&cmd, 0, sizeof(cmd));
        memset(buf, 0, len);

        cmd.opcode = SCSI_MODE_SENSE_6;
        cmd.page = pgcode | pctl;
        cmd.length = len;

        scsi_command(fd, &cmd, sizeof(cmd), buf, len, 10000, SCCMD_READ);
}

void
scsi_mode_select(int fd, u_int8_t byte2, void *buf, size_t len)
{
        struct scsi_mode_select_6 cmd;

        memset(&cmd, 0, sizeof(cmd));

        cmd.opcode = SCSI_MODE_SELECT_6;
        cmd.byte2 = SMS_PF | byte2;
        cmd.length = len;

        scsi_command(fd, &cmd, sizeof(cmd), buf, len, 10000, SCCMD_WRITE);
}

void
scsi_request_sense(int fd, void *buf, size_t len)
{
        struct scsi_request_sense cmd;

        memset(&cmd, 0, sizeof(cmd));
        memset(buf, 0, len);

        cmd.opcode = SCSI_REQUEST_SENSE;
        cmd.length = len;
        scsi_command(fd, &cmd, sizeof(cmd), buf, len, 10000, SCCMD_READ);
}

void
scsi_strvis(char *sdst, size_t dlen, const char *ssrc, size_t slen)
{
        u_char *dst = (u_char *)sdst;
        const u_char *src = (const u_char *)ssrc;

        /* Trim leading and trailing blanks and NULs. */
        while (slen > 0 && STRVIS_ISWHITE(src[0]))
                ++src, --slen;
        while (slen > 0 && STRVIS_ISWHITE(src[slen - 1]))
                --slen;

        while (slen > 0) {
                if (*src < 0x20 || *src >= 0x80) {
                        /* non-printable characters */ 
                        dlen -= 4;
                        if (dlen < 1)
                                break;
                        *dst++ = '\\';
                        *dst++ = ((*src & 0300) >> 6) + '0';
                        *dst++ = ((*src & 0070) >> 3) + '0';
                        *dst++ = ((*src & 0007) >> 0) + '0';
                } else if (*src == '\\') {
                        /* quote characters */
                        dlen -= 2;
                        if (dlen < 1)
                                break;
                        *dst++ = '\\';
                        *dst++ = '\\';
                } else {
                        /* normal characters */
                        if (--dlen < 1)
                                break;
                        *dst++ = *src;
                }
                ++src, --slen;
        }

        *dst++ = 0;
}