root/usr/src/uts/intel/io/dktp/dcdev/gda.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <sys/scsi/scsi.h>

#include <sys/dktp/dadev.h>
#include <sys/dktp/gda.h>

/*
 *      Generic Direct Attached Device
 */

static char *gda_name(uchar_t cmd, char **cmdvec);

#ifdef  GDA_DEBUG
#define DENT    0x0001
#define DPKT    0x0002
#define DERR    0x0004
static  int     gda_debug = DERR|DENT|DPKT;

#endif  /* GDA_DEBUG */

/*
 * Local static data
 */

/*
 *      global data
 */

/*
 *      This is the loadable module wrapper
 */
#include <sys/modctl.h>

extern struct mod_ops mod_miscops;

static struct modlmisc modlmisc = {
        &mod_miscops,   /* Type of module */
        "Generic Direct Attached Device Utilities"
};

static struct modlinkage modlinkage = {
        MODREV_1, (void *)&modlmisc, NULL
};

int
_init(void)
{
        return (mod_install(&modlinkage));
}

int
_fini(void)
{
#ifdef GDA_DEBUG
        if (gda_debug & DENT)
                PRF("gda_fini: call\n");
#endif
        return (mod_remove(&modlinkage));
}

int
_info(struct modinfo *modinfop)
{
        return (mod_info(&modlinkage, modinfop));
}


void
gda_inqfill(char *p, int l, char *s)
{
        register unsigned i = 0, c;

        if (!p)
                return;
        while (i++ < l) {
/*              clean strings of non-printing chars                     */
                if ((c = *p++) < ' ' || c > 0176) {
                        c = ' ';
                }
                *s++ = (char)c;
        }
        *s++ = 0;
}

static char *
gda_name(uchar_t cmd, char **cmdvec)
{
        while (*cmdvec != NULL) {
                if (cmd == **cmdvec) {
                        return (*cmdvec + 1);
                }
                cmdvec++;
        }
        return ("<undecoded cmd>");
}


struct cmpkt *
gda_pktprep(opaque_t objp, struct cmpkt *in_pktp, opaque_t dmatoken,
        int (*callback)(caddr_t), caddr_t arg)
{
        register struct cmpkt *pktp;
        register struct buf *bp = (struct buf *)dmatoken;

        if (in_pktp) {
                pktp = in_pktp;
        } else {
                pktp = CTL_PKTALLOC(objp, callback, arg);
                if (pktp == NULL)
                        return (NULL);
        }

        if (bp) {
                if (bp->b_bcount) {
                        if (CTL_MEMSETUP(objp, pktp, bp, callback, arg) ==
                            NULL) {
                                if (!in_pktp)
                                        CTL_PKTFREE(objp, pktp);
                                return (NULL);
                        }
                }
                bp->av_back = (struct buf *)pktp;
                pktp->cp_bp = bp;
        }
        pktp->cp_retry = 0;
        pktp->cp_objp  = objp;


#ifdef GDA_DEBUG
        if (gda_debug & DPKT)
                PRF("gda_pktprep: pktp=0x%x \n", pktp);
#endif
        return (pktp);
}

void
gda_free(opaque_t objp, struct cmpkt *pktp, struct buf *bp)
{
        if (pktp) {
                CTL_MEMFREE(objp, pktp);
                CTL_PKTFREE(objp, pktp);
        }

        if (bp) {
                if (bp->b_un.b_addr)
                        i_ddi_mem_free((caddr_t)bp->b_un.b_addr, NULL);
                freerbuf(bp);
        }
}

void
gda_log(dev_info_t *dev, char *label, uint_t level, const char *fmt, ...)
{
        auto char name[256];
        auto char buf [256];
        va_list ap;
        int log_only = 0;
        int boot_only = 0;
        int console_only = 0;

        switch (*fmt) {
        case '!':
                log_only = 1;
                fmt++;
                break;
        case '?':
                boot_only = 1;
                fmt++;
                break;
        case '^':
                console_only = 1;
                fmt++;
                break;
        }


        if (dev) {
                if (level == CE_PANIC || level == CE_WARN) {
                        (void) sprintf(name, "%s (%s%d):\n",
                            ddi_pathname(dev, buf), label,
                            ddi_get_instance(dev));
                } else if (level == CE_NOTE ||
                    level >= (uint_t)SCSI_DEBUG) {
                        (void) sprintf(name,
                            "%s%d:", label, ddi_get_instance(dev));
                } else if (level == CE_CONT) {
                        name[0] = '\0';
                }
        } else {
                (void) sprintf(name, "%s:", label);
        }

        va_start(ap, fmt);
        (void) vsprintf(buf, fmt, ap);
        va_end(ap);

        switch (level) {
                case CE_NOTE:
                        level = CE_CONT;
                        /* FALLTHROUGH */
                case CE_CONT:
                case CE_WARN:
                case CE_PANIC:
                        if (boot_only) {
                                cmn_err(level, "?%s\t%s", name, buf);
                        } else if (console_only) {
                                cmn_err(level, "^%s\t%s", name, buf);
                        } else if (log_only) {
                                cmn_err(level, "!%s\t%s", name, buf);
                        } else {
                                cmn_err(level, "%s\t%s", name, buf);
                        }
                        break;
                default:
                        cmn_err(CE_CONT, "^DEBUG: %s\t%s", name, buf);
                        break;
        }
}

void
gda_errmsg(struct scsi_device *devp, struct cmpkt *pktp, char *label,
    int severity, daddr_t blkno, daddr_t err_blkno,
    char **cmdvec, char **senvec)
{
        auto char buf[256];
        dev_info_t *dev = devp->sd_dev;
        static char *error_classes[] = {
                "All", "Unknown", "Informational",
                "Recovered", "Retryable", "Fatal"
        };

        bzero((caddr_t)buf, 256);
        (void) sprintf(buf, "Error for command '%s'\tError Level: %s",
            gda_name(*(uchar_t *)pktp->cp_cdbp, cmdvec),
            error_classes[severity]);
        gda_log(dev, label, CE_WARN, buf);

        bzero((caddr_t)buf, 256);
        if ((blkno != -1) && (err_blkno != -1)) {
                (void) sprintf(buf, "Requested Block %ld, Error Block: %ld\n",
                    blkno, err_blkno);
                gda_log(dev, label, CE_CONT, buf);
        }

        bzero((caddr_t)buf, 256);
        (void) sprintf(buf, "Sense Key: %s\n",
            gda_name(*(uchar_t *)pktp->cp_scbp, senvec));

        gda_log(dev, label, CE_CONT, buf);
        bzero((caddr_t)buf, 256);
        (void) strcpy(buf, "Vendor '");
        gda_inqfill(devp->sd_inq->inq_vid, 8, &buf[strlen(buf)]);
        (void) sprintf(&buf[strlen(buf)],
            "' error code: 0x%x",
            *(uchar_t *)pktp->cp_scbp);
        gda_log(dev, label, CE_CONT, "%s\n", buf);
}