Commit 079d056f authored by Justin T. Gibbs's avatar Justin T. Gibbs

Add a failsafe mechanism to configure devices that have inquiry data

but somehow are not handled by the DV state machine.  This ensures that
the behavior seen before DV is restored in the event of a DV state machine
failure.
parent e409b3d0
/* /*
* Adaptec AIC79xx device driver for Linux. * Adaptec AIC79xx device driver for Linux.
* *
* $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic79xx_osm.c#102 $ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic79xx_osm.c#103 $
* *
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
* Copyright (c) 1994-2000 Justin T. Gibbs. * Copyright (c) 1994-2000 Justin T. Gibbs.
...@@ -2640,6 +2640,7 @@ ahd_linux_dv_target(struct ahd_softc *ahd, u_int target_offset) ...@@ -2640,6 +2640,7 @@ ahd_linux_dv_target(struct ahd_softc *ahd, u_int target_offset)
AHD_TRANS_GOAL, /*paused*/FALSE); AHD_TRANS_GOAL, /*paused*/FALSE);
ahd_unlock(ahd, &s); ahd_unlock(ahd, &s);
timeout = 10 * HZ; timeout = 10 * HZ;
targ->flags &= ~AHD_INQ_VALID;
/* FALLTHROUGH */ /* FALLTHROUGH */
case AHD_DV_STATE_INQ_VERIFY: case AHD_DV_STATE_INQ_VERIFY:
{ {
...@@ -2721,6 +2722,25 @@ ahd_linux_dv_target(struct ahd_softc *ahd, u_int target_offset) ...@@ -2721,6 +2722,25 @@ ahd_linux_dv_target(struct ahd_softc *ahd, u_int target_offset)
} }
out: out:
if ((targ->flags & AHD_INQ_VALID) != 0
&& ahd_linux_get_device(ahd, devinfo.channel - 'A',
devinfo.target, devinfo.lun,
/*alloc*/FALSE) == NULL) {
/*
* The DV state machine failed to configure this device.
* This is normal if DV is disabled. Since we have inquiry
* data, filter it and use the "optimistic" negotiation
* parameters found in the inquiry string.
*/
ahd_linux_filter_inquiry(ahd, &devinfo);
if ((targ->flags & (AHD_BASIC_DV|AHD_ENHANCED_DV)) != 0) {
ahd_print_devinfo(ahd, &devinfo);
printf("DV failed to configure device. "
"Please file a bug report against "
"this driver.\n");
}
}
if (cmd != NULL) if (cmd != NULL)
free(cmd, M_DEVBUF); free(cmd, M_DEVBUF);
...@@ -2806,24 +2826,21 @@ ahd_linux_dv_transition(struct ahd_softc *ahd, struct scsi_cmnd *cmd, ...@@ -2806,24 +2826,21 @@ ahd_linux_dv_transition(struct ahd_softc *ahd, struct scsi_cmnd *cmd,
break; break;
} }
if (ahd_linux_user_dv_setting(ahd) == 0) { AHD_SET_DV_STATE(ahd, targ, targ->dv_state+1);
ahd_linux_filter_inquiry(ahd, devinfo); targ->flags |= AHD_INQ_VALID;
AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT); if (ahd_linux_user_dv_setting(ahd) == 0)
break; break;
}
spi3data = targ->inq_data->spi3data; spi3data = targ->inq_data->spi3data;
switch (spi3data & SID_SPI_CLOCK_DT_ST) { switch (spi3data & SID_SPI_CLOCK_DT_ST) {
default: default:
case SID_SPI_CLOCK_ST: case SID_SPI_CLOCK_ST:
/* Assume only basic DV is supported. */ /* Assume only basic DV is supported. */
ahd_linux_filter_inquiry(ahd, devinfo); targ->flags |= AHD_BASIC_DV;
AHD_SET_DV_STATE(ahd, targ,
AHD_DV_STATE_INQ_VERIFY);
break; break;
case SID_SPI_CLOCK_DT: case SID_SPI_CLOCK_DT:
case SID_SPI_CLOCK_DT_ST: case SID_SPI_CLOCK_DT_ST:
AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_REBD); targ->flags |= AHD_ENHANCED_DV;
break; break;
} }
break; break;
...@@ -2919,8 +2936,15 @@ ahd_linux_dv_transition(struct ahd_softc *ahd, struct scsi_cmnd *cmd, ...@@ -2919,8 +2936,15 @@ ahd_linux_dv_transition(struct ahd_softc *ahd, struct scsi_cmnd *cmd,
case AHD_DV_STATE_TUR: case AHD_DV_STATE_TUR:
switch (status & SS_MASK) { switch (status & SS_MASK) {
case SS_NOP: case SS_NOP:
AHD_SET_DV_STATE(ahd, targ, if ((targ->flags & AHD_BASIC_DV) != 0) {
AHD_DV_STATE_INQ_ASYNC); ahd_linux_filter_inquiry(ahd, devinfo);
AHD_SET_DV_STATE(ahd, targ,
AHD_DV_STATE_INQ_VERIFY);
} else if ((targ->flags & AHD_ENHANCED_DV) != 0) {
AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_REBD);
} else {
AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT);
}
break; break;
case SS_RETRY: case SS_RETRY:
case SS_TUR: case SS_TUR:
......
...@@ -36,7 +36,7 @@ ...@@ -36,7 +36,7 @@
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES. * POSSIBILITY OF SUCH DAMAGES.
* *
* $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic79xx_osm.h#97 $ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic79xx_osm.h#98 $
* *
*/ */
#ifndef _AIC79XX_LINUX_H_ #ifndef _AIC79XX_LINUX_H_
...@@ -425,16 +425,19 @@ struct ahd_linux_device { ...@@ -425,16 +425,19 @@ struct ahd_linux_device {
}; };
typedef enum { typedef enum {
AHD_DV_REQUIRED = 0x01 AHD_DV_REQUIRED = 0x01,
AHD_INQ_VALID = 0x02,
AHD_BASIC_DV = 0x04,
AHD_ENHANCED_DV = 0x08
} ahd_linux_targ_flags; } ahd_linux_targ_flags;
/* DV States */ /* DV States */
typedef enum { typedef enum {
AHD_DV_STATE_EXIT = 0, AHD_DV_STATE_EXIT = 0,
AHD_DV_STATE_INQ_SHORT_ASYNC, AHD_DV_STATE_INQ_SHORT_ASYNC,
AHD_DV_STATE_TUR,
AHD_DV_STATE_INQ_ASYNC, AHD_DV_STATE_INQ_ASYNC,
AHD_DV_STATE_INQ_ASYNC_VERIFY, AHD_DV_STATE_INQ_ASYNC_VERIFY,
AHD_DV_STATE_TUR,
AHD_DV_STATE_REBD, AHD_DV_STATE_REBD,
AHD_DV_STATE_INQ_VERIFY, AHD_DV_STATE_INQ_VERIFY,
AHD_DV_STATE_WEB, AHD_DV_STATE_WEB,
......
/* /*
* Adaptec AIC7xxx device driver for Linux. * Adaptec AIC7xxx device driver for Linux.
* *
* $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic7xxx_osm.c#165 $ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic7xxx_osm.c#166 $
* *
* Copyright (c) 1994 John Aycock * Copyright (c) 1994 John Aycock
* The University of Calgary Department of Computer Science. * The University of Calgary Department of Computer Science.
...@@ -2492,6 +2492,7 @@ ahc_linux_dv_target(struct ahc_softc *ahc, u_int target_offset) ...@@ -2492,6 +2492,7 @@ ahc_linux_dv_target(struct ahc_softc *ahc, u_int target_offset)
AHC_TRANS_GOAL, /*paused*/FALSE); AHC_TRANS_GOAL, /*paused*/FALSE);
ahc_unlock(ahc, &s); ahc_unlock(ahc, &s);
timeout = 10 * HZ; timeout = 10 * HZ;
targ->flags &= ~AHC_INQ_VALID;
/* FALLTHROUGH */ /* FALLTHROUGH */
case AHC_DV_STATE_INQ_VERIFY: case AHC_DV_STATE_INQ_VERIFY:
{ {
...@@ -2573,6 +2574,25 @@ ahc_linux_dv_target(struct ahc_softc *ahc, u_int target_offset) ...@@ -2573,6 +2574,25 @@ ahc_linux_dv_target(struct ahc_softc *ahc, u_int target_offset)
} }
out: out:
if ((targ->flags & AHC_INQ_VALID) != 0
&& ahc_linux_get_device(ahc, devinfo.channel - 'A',
devinfo.target, devinfo.lun,
/*alloc*/FALSE) == NULL) {
/*
* The DV state machine failed to configure this device.
* This is normal if DV is disabled. Since we have inquiry
* data, filter it and use the "optimistic" negotiation
* parameters found in the inquiry string.
*/
ahc_linux_filter_inquiry(ahc, &devinfo);
if ((targ->flags & (AHC_BASIC_DV|AHC_ENHANCED_DV)) != 0) {
ahc_print_devinfo(ahc, &devinfo);
printf("DV failed to configure device. "
"Please file a bug report against "
"this driver.\n");
}
}
if (cmd != NULL) if (cmd != NULL)
free(cmd, M_DEVBUF); free(cmd, M_DEVBUF);
...@@ -2658,24 +2678,21 @@ ahc_linux_dv_transition(struct ahc_softc *ahc, struct scsi_cmnd *cmd, ...@@ -2658,24 +2678,21 @@ ahc_linux_dv_transition(struct ahc_softc *ahc, struct scsi_cmnd *cmd,
break; break;
} }
if (ahc_linux_user_dv_setting(ahc) == 0) { AHC_SET_DV_STATE(ahc, targ, targ->dv_state+1);
ahc_linux_filter_inquiry(ahc, devinfo); targ->flags |= AHC_INQ_VALID;
AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_EXIT); if (ahc_linux_user_dv_setting(ahc) == 0)
break; break;
}
spi3data = targ->inq_data->spi3data; spi3data = targ->inq_data->spi3data;
switch (spi3data & SID_SPI_CLOCK_DT_ST) { switch (spi3data & SID_SPI_CLOCK_DT_ST) {
default: default:
case SID_SPI_CLOCK_ST: case SID_SPI_CLOCK_ST:
/* Assume only basic DV is supported. */ /* Assume only basic DV is supported. */
ahc_linux_filter_inquiry(ahc, devinfo); targ->flags |= AHC_BASIC_DV;
AHC_SET_DV_STATE(ahc, targ,
AHC_DV_STATE_INQ_VERIFY);
break; break;
case SID_SPI_CLOCK_DT: case SID_SPI_CLOCK_DT:
case SID_SPI_CLOCK_DT_ST: case SID_SPI_CLOCK_DT_ST:
AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_REBD); targ->flags |= AHC_ENHANCED_DV;
break; break;
} }
break; break;
...@@ -2771,8 +2788,15 @@ ahc_linux_dv_transition(struct ahc_softc *ahc, struct scsi_cmnd *cmd, ...@@ -2771,8 +2788,15 @@ ahc_linux_dv_transition(struct ahc_softc *ahc, struct scsi_cmnd *cmd,
case AHC_DV_STATE_TUR: case AHC_DV_STATE_TUR:
switch (status & SS_MASK) { switch (status & SS_MASK) {
case SS_NOP: case SS_NOP:
AHC_SET_DV_STATE(ahc, targ, if ((targ->flags & AHC_BASIC_DV) != 0) {
AHC_DV_STATE_INQ_ASYNC); ahc_linux_filter_inquiry(ahc, devinfo);
AHC_SET_DV_STATE(ahc, targ,
AHC_DV_STATE_INQ_VERIFY);
} else if ((targ->flags & AHC_ENHANCED_DV) != 0) {
AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_REBD);
} else {
AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_EXIT);
}
break; break;
case SS_RETRY: case SS_RETRY:
case SS_TUR: case SS_TUR:
......
...@@ -53,7 +53,7 @@ ...@@ -53,7 +53,7 @@
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES. * POSSIBILITY OF SUCH DAMAGES.
* *
* $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic7xxx_osm.h#112 $ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic7xxx_osm.h#113 $
* *
*/ */
#ifndef _AIC7XXX_LINUX_H_ #ifndef _AIC7XXX_LINUX_H_
...@@ -438,16 +438,19 @@ struct ahc_linux_device { ...@@ -438,16 +438,19 @@ struct ahc_linux_device {
}; };
typedef enum { typedef enum {
AHC_DV_REQUIRED = 0x01 AHC_DV_REQUIRED = 0x01,
AHC_INQ_VALID = 0x02,
AHC_BASIC_DV = 0x04,
AHC_ENHANCED_DV = 0x08
} ahc_linux_targ_flags; } ahc_linux_targ_flags;
/* DV States */ /* DV States */
typedef enum { typedef enum {
AHC_DV_STATE_EXIT = 0, AHC_DV_STATE_EXIT = 0,
AHC_DV_STATE_INQ_SHORT_ASYNC, AHC_DV_STATE_INQ_SHORT_ASYNC,
AHC_DV_STATE_TUR,
AHC_DV_STATE_INQ_ASYNC, AHC_DV_STATE_INQ_ASYNC,
AHC_DV_STATE_INQ_ASYNC_VERIFY, AHC_DV_STATE_INQ_ASYNC_VERIFY,
AHC_DV_STATE_TUR,
AHC_DV_STATE_REBD, AHC_DV_STATE_REBD,
AHC_DV_STATE_INQ_VERIFY, AHC_DV_STATE_INQ_VERIFY,
AHC_DV_STATE_WEB, AHC_DV_STATE_WEB,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment