/*
* 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);
}