📄 sd.c
字号:
makecom_g0(pkt, devp, flags, com, (int) blkno, count); } pkt->pkt_pmon = un->un_dkn; if (DEBUGGING) { sdprintf(devp, "%s blk %d amt 0x%x (%d) resid %d", scsi_cmd_decode((u_char) com, sd_cmds), blkno, count << SECDIV, count << SECDIV, resid); } } else { caddr_t cdb; int cdblen; /* * stored in bp->b_forw is a pointer to cdb */ cdb = (caddr_t) bp->b_forw; switch (GETCMD((union scsi_cdb *)cdb)) { case SCMD_FORMAT: tval = sd_fmt_time; break; } BP_PKT(bp) = NULL; switch (GETGROUP((union scsi_cdb *)cdb)) { case CDB_GROUPID_0: cdblen = CDB_GROUP0; break; case CDB_GROUPID_1: cdblen = CDB_GROUP1; break; case CDB_GROUPID_5: cdblen = CDB_GROUP5; break; default: sdlog(devp, LOG_ERR, "unknown group for special cmd"); u.u_error = EINVAL; return; } pkt = scsi_resalloc(ROUTE, cdblen, 1, (bp->b_bcount > 0)? (opaque_t) bp : (opaque_t) 0, func); if (pkt == (struct scsi_pkt *) NULL) { u.u_error = ENOMEM; return; } if (kcopy(cdb, (caddr_t) pkt->pkt_cdbp, (u_int)cdblen) != 0) { scsi_resfree(pkt); u.u_error = EFAULT; return; } pkt->pkt_pmon = (char) -1; pkt->pkt_flags = flags; } if ((scsi_options&SCSI_OPTIONS_DR) == 0 || (un->un_dp->options & SD_NODISC) != 0) { pkt->pkt_flags |= FLAG_NODISCON; } pkt->pkt_comp = sdintr; pkt->pkt_time = tval; pkt->pkt_private = (opaque_t) bp; BP_PKT(bp) = pkt;}/* * These two routines provide set and test for the * link bit in disk read or write cdb's */static intsd_testlink(pkt)register struct scsi_pkt *pkt;{ switch (CDB_GROUPID(pkt->pkt_cdbp[0])) { case CDB_GROUPID_0: return ((CDBP(pkt)->g0_link == 0) ? 0: 1); case CDB_GROUPID_1: return ((CDBP(pkt)->g1_link != 0) ? 0: 1); default: return (0); }}static voidsd_setlink(pkt)register struct scsi_pkt *pkt;{ switch (CDB_GROUPID(pkt->pkt_cdbp[0])) { case CDB_GROUPID_0: CDBP(pkt)->g0_link = 1; break; case CDB_GROUPID_1: CDBP(pkt)->g1_link = 1; break; default: break; } return;}/* * This routine called to see whether unit is (still) there. Must not * be called when un->un_sbufp is in use, and must not be called with * an unattached disk. Soft state of disk is restored to what it was * upon entry- up to caller to set the correct state. * * Warning: bzero for non-sun4c's will crash on bootup sdopen. */static intsd_unit_ready(dev)dev_t dev;{ struct scsi_device *devp = sdunits[SDUNIT(dev)]; struct scsi_disk *un = UPTR; auto struct uscsi_cmd scmd, *com = &scmd; auto char cmdblk[CDB_GROUP0]; register i; u_char state = un->un_last_state; New_state(un, SD_STATE_OPENING); com->uscsi_bufaddr = 0; com->uscsi_buflen = 0; cmdblk[0] = (char) SCMD_TEST_UNIT_READY; for (i = 1; i < CDB_GROUP0; i++) cmdblk[i] = 0; com->uscsi_flags = USCSI_DIAGNOSE|USCSI_SILENT|USCSI_WRITE; com->uscsi_cdb = cmdblk; com->uscsi_cdblen = CDB_GROUP0; /* * allow for trying it twice... */ if (sdioctl_cmd(dev, (caddr_t)com, SD_USCSI_CDB_KERNEL) && sdioctl_cmd(dev, (caddr_t)com, SD_USCSI_CDB_KERNEL)) { un->un_state = un->un_last_state; un->un_last_state = state; return (0); } un->un_state = un->un_last_state; un->un_last_state = state; return (1);}/* * Lock or Unlock the door for removable media devices */static voidsd_lock_unlock(dev, flag)dev_t dev;int flag;{ struct scsi_device *devp = sdunits[SDUNIT(dev)]; auto struct uscsi_cmd scmd, *com = &scmd; auto char cmdblk[CDB_GROUP0]; register i; com->uscsi_bufaddr = 0; com->uscsi_buflen = 0; for (i = 0; i < CDB_GROUP0; i++) cmdblk[i] = 0; cmdblk[0] = SCMD_DOORLOCK; cmdblk[4] = flag; com->uscsi_flags = USCSI_DIAGNOSE|USCSI_SILENT|USCSI_WRITE; com->uscsi_cdb = cmdblk; com->uscsi_cdblen = CDB_GROUP0; if (sdioctl_cmd(dev, (caddr_t)com, SD_USCSI_CDB_KERNEL)) { sdlog(devp, LOG_INFO, "%s media removal failed", flag ? "prevent" : "allow"); }}/* * Interrupt Service Routines */staticsdrestart(arg)caddr_t arg;{ struct scsi_device *devp = (struct scsi_device *) arg; register struct buf *bp; register s = splr(sdpri); bp = UPTR->un_utab.b_forw; if (bp) { register struct scsi_pkt *pkt; if (UPTR->un_state == SD_STATE_SENSING) { pkt = UPTR->un_rqs; } else { pkt = BP_PKT(bp); } bp->b_resid = 0; if (pkt_transport(pkt) != TRAN_ACCEPT) { sdlog(devp, LOG_ERR, "sdrestart transport failed"); UPTR->un_state = UPTR->un_last_state; bp->b_resid = bp->b_bcount; bp->b_flags |= B_ERROR; sddone(devp); } } (void) splx(s);}/* * Command completion processing */static voidsdintr(pkt)struct scsi_pkt *pkt;{ register struct scsi_device *devp; register struct scsi_disk *un; register struct buf *bp; register action; bp = (struct buf *) pkt->pkt_private; devp = sdunits[SDUNIT(bp->b_dev)]; un = UPTR; DPRINTF(devp, "sdintr"); if (pkt->pkt_reason != CMD_CMPLT) { action = sd_handle_incomplete(devp); } else if (un->un_state == SD_STATE_SENSING) { /* * We were running a REQUEST SENSE. Find out what to do next. */ Restore_state(un); pkt = BP_PKT(bp); action = sd_handle_sense(devp); } else { /* * Okay, we weren't running a REQUEST SENSE. Call a routine * to see if the status bits we're okay. As a side effect, * clear state for this device, set non-error b_resid values, * etc. If a request sense is to be run, that will happen by * sd_check_error() returning a QUE_SENSE action. */ action = sd_check_error(devp); } /* * If a WRITE command is completing, and we're s'posed to verify * that it worked okay, we call make_sd_cmd() again here. make_sd_cmd() * will note that we've already gotten resources and that the current * command is a WRITE command that needs to be turned into a VERIFY * command. * * If a VERIFY command fails with KEY_MISCOMPARE, sd_handle_sense * will convert the VERIFY back to a WRITE (by calling make_sd_cmd() * again) and retry the the WRITE (modulo retries being exhausted). * */ if (action == COMMAND_DONE && bp != un->un_sbufp && (bp->b_flags & B_READ) == 0) { if (un->un_wchkmap & (1<<SDPART(bp->b_dev))) { if (pkt->pkt_cdbp[0] == SCMD_WRITE || pkt->pkt_cdbp[0] == SCMD_WRITE_G1) { action = QUE_COMMAND; make_sd_cmd(devp, bp, NULL_FUNC); } } } switch (action) { case COMMAND_DONE_ERROR:error: un->un_err_severe = DK_FATAL; un->un_err_resid = bp->b_resid = bp->b_bcount; bp->b_flags |= B_ERROR; /* FALL THRU */ case COMMAND_DONE: sddone(devp); break; case QUE_SENSE: New_state(un, SD_STATE_SENSING); un->un_rqs->pkt_private = (opaque_t) bp; bzero((caddr_t)devp->sd_sense, SENSE_LENGTH); if (pkt_transport(un->un_rqs) != TRAN_ACCEPT) { sdlog(devp, LOG_ERR, "transport of request sense fails"); Restore_state(un); goto error; } break; case QUE_COMMAND: if (pkt_transport(BP_PKT(bp)) != TRAN_ACCEPT) { sdlog(devp, LOG_ERR, "requeue of command fails"); un->un_err_resid = bp->b_resid = bp->b_bcount; bp->b_flags |= B_ERROR; sddone(devp); } break; case JUST_RETURN: break; }}static intsd_handle_incomplete(devp)register struct scsi_device *devp;{ static char *fail = "SCSI transport failed: reason '%s': %s"; static char *notresp = "disk not responding to selection"; register rval = COMMAND_DONE_ERROR; register struct scsi_disk *un = UPTR; register struct buf *bp = un->un_utab.b_forw; register struct scsi_pkt *pkt = BP_PKT(bp); int be_chatty = (bp != un->un_sbufp || !(un->un_soptions & DK_SILENT)); int perr = (pkt->pkt_statistics & STAT_PERR); if (un->un_state == SD_STATE_SENSING) { pkt = un->un_rqs; Restore_state(un); } else if (un->un_state == SD_STATE_DUMPING) { return (rval); } if (pkt->pkt_reason == CMD_TIMEOUT) { /* * If the command timed out, we must assume that * the target may still be running that command, * so we should try and reset that target. */ if (scsi_reset(ROUTE, RESET_TARGET) == 0) { (void) scsi_reset(ROUTE, RESET_ALL); } } else if (pkt->pkt_reason == CMD_UNX_BUS_FREE) { /* * If we had a parity error that caused the target to * drop BSY*, don't be chatty about it. */ if (perr && be_chatty) be_chatty = 0; } /* * If we were running a request sense, try it again if possible. */ if (pkt == un->un_rqs) { if (un->un_retry_ct++ < sd_retry_count) { rval = QUE_SENSE; } } else if (bp == un->un_sbufp && (un->un_soptions & DK_ISOLATE)) { rval = COMMAND_DONE_ERROR; } else if (un->un_retry_ct++ < sd_retry_count) { rval = QUE_COMMAND; } if (pkt->pkt_state == STATE_GOT_BUS && rval == COMMAND_DONE_ERROR) { /* * Looks like someone turned off this shoebox. */ sdlog(devp, LOG_ERR, notresp); New_state(un, SD_STATE_DETACHING); } else if (be_chatty) { sdlog(devp, LOG_ERR, fail, scsi_rname(pkt->pkt_reason), (rval == COMMAND_DONE_ERROR) ? "giving up": "retrying command"); } return (rval);}static intsd_handle_sense(devp)register struct scsi_device *devp;{ register struct scsi_disk *un = UPTR; register struct buf *bp = un->un_utab.b_forw; struct scsi_pkt *pkt = BP_PKT(bp), *rqpkt = un->un_rqs; register rval = COMMAND_DONE_ERROR; int severity, amt, i; char *p; static char *hex = " 0x%x"; char *rq_err_msg; if (SCBP(rqpkt)->sts_busy) { sdlog (devp, LOG_ERR, "Busy Status on REQUEST SENSE"); if (un->un_retry_ct++ < sd_retry_count) { timeout (sdrestart, (caddr_t)devp, SD_BSY_TIMEOUT); rval = JUST_RETURN; } return (rval); } if (SCBP_C(rqpkt)) { rq_err_msg = "Check Condition on REQUEST SENSE";sense_failed: /* * If the request sense failed, for any of a number * of reasons, allow the original command to be * retried. Only log error on our last gasp. */ if (un->un_retry_ct++ < sd_retry_count) { un->un_err_severe = DK_NOERROR; rval = QUE_COMMAND; } else { if (rq_err_msg) { sdlog (devp, LOG_ERR, rq_err_msg); } un->un_err_severe = DK_FATAL; rval = COMMAND_DONE_ERROR; } return (rval); } amt = SENSE_LENGTH - rqpkt->pkt_resid; if ((rqpkt->pkt_state & STATE_XFERRED_DATA) == 0 || amt == 0) { rq_err_msg = "Request Sense couldn't get sense data"; goto sense_failed; } /* * Now, check to see whether we got enough sense data to make any * sense out if it (heh-heh). */ if (amt < SUN_MIN_SENSE_LENGTH) { rq_err_msg = "Not enough sense information"; goto sense_failed; } if (devp->sd_sense->es_class != CLASS_EXTENDED_SENSE) {#ifdef ADAPTEC if (un->un_dp->ctype == CTYPE_ACB4000) { sdintr_adaptec(devp); } else#endif /* ADAPTEC */ { auto char tmp[8]; auto char buf[128]; p = (char *) devp->sd_sense; (void) strcpy(buf, "undecodable sense information:"); for (i = 0; i < amt; i++) { (void) sprintf(tmp, hex, *(p++)&0xff); (void) strcpy(&buf[strlen(buf)], tmp); } sdlog(devp, LOG_ERR, buf); rq_err_msg = NULL; goto sense_failed; } } un->un_status = devp->sd_sense->es_key; un->un_err_code = devp->sd_sense->es_code; if (devp->sd_sense->es_valid) { un->un_err_blkno = (devp->sd_sense->es_info_1 << 24) | (devp->sd_sense->es_info_2 << 16) | (devp->sd_sense->es_info_3 << 8) | (devp->sd_sense->es_info_4); } else { /* * With the valid bit not being set, we have * to figure out by hand as close as possible * what the real block number might have been */ un->un_err_blkno = bp->b_blkno; if (un->un_gvalid) { struct dk_map *lp = &un->un_map[SDPART(bp->b_dev)]; un->un_err_blkno = dkblock(bp) + (lp->dkl_cylno * un->un_g.dkg_nhead * un->un_g.dkg_nsect); } } if (DEBUGGING_ALL || sd_error_level == SDERR_ALL) { printf("%s%d: cmd:", DNAME, DUNIT); p = pkt->pkt_cdbp; for (i = 0; i < CDB_SIZE; i++) printf(hex, *(p++)&0xff); p = (char *) devp->sd_sense; printf("\nsdata:"); for (i = 0; i < amt; i++) { printf(hex, *(p++)&0xff); } printf("\n"); } switch (un->un_status) { case KEY_NOT_READY: /* * If we get a not-ready indication, wait a bit and * try it again. Some drives pump this out for about * 2-3 seconds after a reset. */ if (un->un_retry_ct++ < sd_retry_count) { un->un_err_severe = DK_NOERROR; severity = SDERR_RETRYABLE; timeout(sdrestart, (caddr_t)devp, SD_BSY_TIMEOUT); rval = JUST_RETURN; } else { un->un_err_severe = DK_FATAL; rval = COMMAND_DONE_ERROR; severity = SDERR_FATAL; } break; case KEY_NO_SENSE: if (un->un_retry_ct++ < sd_retry_count) { un->un_err_severe = DK_NOERROR; severity = SDERR_RETRYABLE; rval = QUE_COMMAND; } else { un->un_err_severe = DK_FATAL; rval = COMMAND_DONE_ERROR; severity = SDERR_FATAL; } break; case KEY_RECOVERABLE_ERROR: un->un_err_severe = DK_CORRECTED; severity = SDERR_RECOVERED; rval = COMMAND_DONE; break; case KEY_MEDIUM_ERROR: /* * This really ought to be a fatal error, but we'll retry * it upon a read, as some drives seem to report spurious * media errors. */ if (bp != un->un_sbufp && (bp->b_flags & B_READ) != 0) { if (un->un_retry_ct++ < sd_retry_count) { rval = QUE_COMMAND; severity = SDERR_RETRYABLE; break; } } /* FALLTHROUGH */ case KEY_HARDWARE_ERROR: case KEY_VOLUME_OVERFLOW: case KEY_WRITE_PROTECT: case KEY_BLANK_CHECK:totally_bad: un->un_err_severe = DK_FATAL; severity = SDERR_FATAL; rval = COMMAND_DONE_ERROR; break; case KEY_ILLEGAL_REQUEST: /* * If the command is a VERIFY command that the drive * apparently doesn't support, whine to
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -