/* @(#)modes.c 1.29 09/07/10 Copyright 1988, 1997-2001, 2004-2009 J. Schilling */ #include #ifndef lint static UConst char sccsid[] = "@(#)modes.c 1.29 09/07/10 Copyright 1988, 1997-2001, 2004-2009 J. Schilling"; #endif /* * SCSI mode page handling * * Copyright (c) 1988, 1997-2001, 2004-2009 J. Schilling */ /* * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * See the file CDDL.Schily.txt in this distribution for details. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file CDDL.Schily.txt from this distribution. */ #include #include #include #include #include #include #include "libscgcmd.h" EXPORT int scsi_compliant; LOCAL BOOL has_mode_page __PR((SCSI *scgp, int page, char *pagename, int *lenp)); EXPORT BOOL get_mode_params __PR((SCSI *scgp, int page, char *pagename, Uchar *modep, Uchar *cmodep, Uchar *dmodep, Uchar *smodep, int *lenp)); EXPORT BOOL set_mode_params __PR((SCSI *scgp, char *pagename, Uchar *modep, int len, int save, int secsize)); #define XXX #ifdef XXX LOCAL BOOL has_mode_page(scgp, page, pagename, lenp) SCSI *scgp; int page; char *pagename; int *lenp; { Uchar mode[0x100]; int hdlen; int len = 1; /* Nach SCSI Norm */ int try = 0; struct scsi_mode_page_header *mp; /* * ATAPI drives (used e.g. by IOMEGA) from y2k have the worst firmware * I've seen. They create DMA buffer overruns if we request less than * 3 bytes with 6 byte mode sense which equals 4 byte with 10 byte mode * sense. In order to prevent repeated bus resets, we remember this * bug. * * IOMEGA claims that they are using Philips clone drives but a Philips * drive I own does not have the problem. */ if ((scgp->dflags & DRF_MODE_DMA_OVR) != 0) len = sizeof (struct scsi_mode_header); again: fillbytes((caddr_t)mode, sizeof (mode), '\0'); if (lenp) *lenp = 0; scgp->silent++; (void) unit_ready(scgp); /* Maxoptix bringt Aborted cmd 0x0B mit code 0x4E (overlapping cmds)*/ /* * The Matsushita CW-7502 will sometimes deliver a zeroed * mode page 2A if "Page n default" is used instead of "current". */ if (mode_sense(scgp, mode, len, page, 0) < 0) { /* Page n current */ scgp->silent--; if (len < (int)sizeof (struct scsi_mode_header) && try == 0) { len = sizeof (struct scsi_mode_header); goto again; } return (FALSE); } else { if (len > 1 && try == 0) { /* * If we come here, we got a hard failure with the * fist try. Remember this (IOMEGA USB) firmware bug. */ if ((scgp->dflags & DRF_MODE_DMA_OVR) == 0) { /* XXX if (!nowarn) */ errmsgno(EX_BAD, "Warning: controller creates hard SCSI failure when retrieving %s page.\n", pagename); scgp->dflags |= DRF_MODE_DMA_OVR; } } len = ((struct scsi_mode_header *)mode)->sense_data_len + 1; } /* * ATAPI drives as used by IOMEGA may receive a SCSI bus device reset * in between these two mode sense commands. */ (void) unit_ready(scgp); if (mode_sense(scgp, mode, len, page, 0) < 0) { /* Page n current */ scgp->silent--; return (FALSE); } scgp->silent--; if (scgp->verbose) scg_prbytes("Mode Sense Data", mode, len - scg_getresid(scgp)); hdlen = sizeof (struct scsi_mode_header) + ((struct scsi_mode_header *)mode)->blockdesc_len; mp = (struct scsi_mode_page_header *)(mode + hdlen); if (scgp->verbose) scg_prbytes("Mode Page Data", (Uchar *)mp, mp->p_len+2); if (mp->p_len == 0) { if (!scsi_compliant && try == 0) { len = hdlen; /* * add sizeof page header (page # + len byte) * (should normaly result in len == 14) * this allowes to work with: * Quantum Q210S (wants at least 13) * MD2x (wants at least 4) */ len += 2; try++; goto again; } /* XXX if (!nowarn) */ errmsgno(EX_BAD, "Warning: controller returns zero sized %s page.\n", pagename); } if (!scsi_compliant && (len < (int)(mp->p_len + hdlen + 2))) { len = mp->p_len + hdlen + 2; /* XXX if (!nowarn) */ errmsgno(EX_BAD, "Warning: controller returns wrong size for %s page.\n", pagename); } if (mp->p_code != page) { /* XXX if (!nowarn) */ errmsgno(EX_BAD, "Warning: controller returns wrong page %X for %s page (%X).\n", mp->p_code, pagename, page); return (FALSE); } if (lenp) *lenp = len; return (mp->p_len > 0); } #endif EXPORT BOOL get_mode_params(scgp, page, pagename, modep, cmodep, dmodep, smodep, lenp) SCSI *scgp; int page; char *pagename; Uchar *modep; Uchar *cmodep; Uchar *dmodep; Uchar *smodep; int *lenp; { int len; BOOL ret = TRUE; #ifdef XXX if (lenp) *lenp = 0; if (!has_mode_page(scgp, page, pagename, &len)) { if (!scgp->silent) errmsgno(EX_BAD, "Warning: controller does not support %s page.\n", pagename); return (FALSE); } if (lenp) *lenp = len; #else if (lenp == 0) len = 0xFF; #endif if (modep) { fillbytes(modep, 0x100, '\0'); scgp->silent++; (void) unit_ready(scgp); scgp->silent--; if (mode_sense(scgp, modep, len, page, 0) < 0) { /* Page x current */ errmsgno(EX_BAD, "Cannot get %s data.\n", pagename); ret = FALSE; } else if (scgp->verbose) { scg_prbytes("Mode Sense Data", modep, len - scg_getresid(scgp)); } } if (cmodep) { fillbytes(cmodep, 0x100, '\0'); scgp->silent++; (void) unit_ready(scgp); scgp->silent--; if (mode_sense(scgp, cmodep, len, page, 1) < 0) { /* Page x change */ errmsgno(EX_BAD, "Cannot get %s mask.\n", pagename); ret = FALSE; } else if (scgp->verbose) { scg_prbytes("Mode Sense Data", cmodep, len - scg_getresid(scgp)); } } if (dmodep) { fillbytes(dmodep, 0x100, '\0'); scgp->silent++; (void) unit_ready(scgp); scgp->silent--; if (mode_sense(scgp, dmodep, len, page, 2) < 0) { /* Page x default */ errmsgno(EX_BAD, "Cannot get default %s data.\n", pagename); ret = FALSE; } else if (scgp->verbose) { scg_prbytes("Mode Sense Data", dmodep, len - scg_getresid(scgp)); } } if (smodep) { fillbytes(smodep, 0x100, '\0'); scgp->silent++; (void) unit_ready(scgp); scgp->silent--; if (mode_sense(scgp, smodep, len, page, 3) < 0) { /* Page x saved */ errmsgno(EX_BAD, "Cannot get saved %s data.\n", pagename); ret = FALSE; } else if (scgp->verbose) { scg_prbytes("Mode Sense Data", smodep, len - scg_getresid(scgp)); } } return (ret); } EXPORT BOOL set_mode_params(scgp, pagename, modep, len, save, secsize) SCSI *scgp; char *pagename; Uchar *modep; int len; int save; int secsize; { int i; ((struct scsi_modesel_header *)modep)->sense_data_len = 0; ((struct scsi_modesel_header *)modep)->res2 = 0; i = ((struct scsi_mode_header *)modep)->blockdesc_len; if (i > 0) { i_to_3_byte( ((struct scsi_mode_data *)modep)->blockdesc.nlblock, 0); if (secsize >= 0) i_to_3_byte(((struct scsi_mode_data *)modep)->blockdesc.lblen, secsize); } scgp->silent++; (void) unit_ready(scgp); scgp->silent--; if (save == 0 || mode_select(scgp, modep, len, save, scgp->inq->data_format >= 2) < 0) { scgp->silent++; (void) unit_ready(scgp); scgp->silent--; if (mode_select(scgp, modep, len, 0, scgp->inq->data_format >= 2) < 0) { if (scgp->silent == 0) { errmsgno(EX_BAD, "Warning: using default %s data.\n", pagename); scg_prbytes("Mode Select Data", modep, len); } return (FALSE); } } return (TRUE); }