📄 libata-scsi.c
字号:
* @ptr_io: (input/output) Location to store more output data * @last: End of output data buffer * * Generate a generic MODE SENSE r/w error recovery page. * * LOCKING: * None. */static unsigned int ata_msense_rw_recovery(u8 **ptr_io, const u8 *last){ ata_msense_push(ptr_io, last, def_rw_recovery_mpage, sizeof(def_rw_recovery_mpage)); return sizeof(def_rw_recovery_mpage);}/* * We can turn this into a real blacklist if it's needed, for now just * blacklist any Maxtor BANC1G10 revision firmware */static int ata_dev_supports_fua(u16 *id){ unsigned char model[ATA_ID_PROD_LEN + 1], fw[ATA_ID_FW_REV_LEN + 1]; if (!libata_fua) return 0; if (!ata_id_has_fua(id)) return 0; ata_id_c_string(id, model, ATA_ID_PROD, sizeof(model)); ata_id_c_string(id, fw, ATA_ID_FW_REV, sizeof(fw)); if (strcmp(model, "Maxtor")) return 1; if (strcmp(fw, "BANC1G10")) return 1; return 0; /* blacklisted */}/** * ata_scsiop_mode_sense - Simulate MODE SENSE 6, 10 commands * @args: device IDENTIFY data / SCSI command of interest. * @rbuf: Response buffer, to which simulated SCSI cmd output is sent. * @buflen: Response buffer length. * * Simulate MODE SENSE commands. Assume this is invoked for direct * access devices (e.g. disks) only. There should be no block * descriptor for other device types. * * LOCKING: * spin_lock_irqsave(host lock) */unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf, unsigned int buflen){ struct ata_device *dev = args->dev; u8 *scsicmd = args->cmd->cmnd, *p, *last; const u8 sat_blk_desc[] = { 0, 0, 0, 0, /* number of blocks: sat unspecified */ 0, 0, 0x2, 0x0 /* block length: 512 bytes */ }; u8 pg, spg; unsigned int ebd, page_control, six_byte, output_len, alloc_len, minlen; u8 dpofua; VPRINTK("ENTER\n"); six_byte = (scsicmd[0] == MODE_SENSE); ebd = !(scsicmd[1] & 0x8); /* dbd bit inverted == edb */ /* * LLBA bit in msense(10) ignored (compliant) */ page_control = scsicmd[2] >> 6; switch (page_control) { case 0: /* current */ break; /* supported */ case 3: /* saved */ goto saving_not_supp; case 1: /* changeable */ case 2: /* defaults */ default: goto invalid_fld; } if (six_byte) { output_len = 4 + (ebd ? 8 : 0); alloc_len = scsicmd[4]; } else { output_len = 8 + (ebd ? 8 : 0); alloc_len = (scsicmd[7] << 8) + scsicmd[8]; } minlen = (alloc_len < buflen) ? alloc_len : buflen; p = rbuf + output_len; last = rbuf + minlen - 1; pg = scsicmd[2] & 0x3f; spg = scsicmd[3]; /* * No mode subpages supported (yet) but asking for _all_ * subpages may be valid */ if (spg && (spg != ALL_SUB_MPAGES)) goto invalid_fld; switch(pg) { case RW_RECOVERY_MPAGE: output_len += ata_msense_rw_recovery(&p, last); break; case CACHE_MPAGE: output_len += ata_msense_caching(args->id, &p, last); break; case CONTROL_MPAGE: { output_len += ata_msense_ctl_mode(&p, last); break; } case ALL_MPAGES: output_len += ata_msense_rw_recovery(&p, last); output_len += ata_msense_caching(args->id, &p, last); output_len += ata_msense_ctl_mode(&p, last); break; default: /* invalid page code */ goto invalid_fld; } if (minlen < 1) return 0; dpofua = 0; if (ata_dev_supports_fua(args->id) && (dev->flags & ATA_DFLAG_LBA48) && (!(dev->flags & ATA_DFLAG_PIO) || dev->multi_count)) dpofua = 1 << 4; if (six_byte) { output_len--; rbuf[0] = output_len; if (minlen > 2) rbuf[2] |= dpofua; if (ebd) { if (minlen > 3) rbuf[3] = sizeof(sat_blk_desc); if (minlen > 11) memcpy(rbuf + 4, sat_blk_desc, sizeof(sat_blk_desc)); } } else { output_len -= 2; rbuf[0] = output_len >> 8; if (minlen > 1) rbuf[1] = output_len; if (minlen > 3) rbuf[3] |= dpofua; if (ebd) { if (minlen > 7) rbuf[7] = sizeof(sat_blk_desc); if (minlen > 15) memcpy(rbuf + 8, sat_blk_desc, sizeof(sat_blk_desc)); } } return 0;invalid_fld: ata_scsi_set_sense(args->cmd, ILLEGAL_REQUEST, 0x24, 0x0); /* "Invalid field in cbd" */ return 1;saving_not_supp: ata_scsi_set_sense(args->cmd, ILLEGAL_REQUEST, 0x39, 0x0); /* "Saving parameters not supported" */ return 1;}/** * ata_scsiop_read_cap - Simulate READ CAPACITY[ 16] commands * @args: device IDENTIFY data / SCSI command of interest. * @rbuf: Response buffer, to which simulated SCSI cmd output is sent. * @buflen: Response buffer length. * * Simulate READ CAPACITY commands. * * LOCKING: * None. */unsigned int ata_scsiop_read_cap(struct ata_scsi_args *args, u8 *rbuf, unsigned int buflen){ u64 last_lba = args->dev->n_sectors - 1; /* LBA of the last block */ VPRINTK("ENTER\n"); if (args->cmd->cmnd[0] == READ_CAPACITY) { if (last_lba >= 0xffffffffULL) last_lba = 0xffffffff; /* sector count, 32-bit */ ATA_SCSI_RBUF_SET(0, last_lba >> (8 * 3)); ATA_SCSI_RBUF_SET(1, last_lba >> (8 * 2)); ATA_SCSI_RBUF_SET(2, last_lba >> (8 * 1)); ATA_SCSI_RBUF_SET(3, last_lba); /* sector size */ ATA_SCSI_RBUF_SET(6, ATA_SECT_SIZE >> 8); ATA_SCSI_RBUF_SET(7, ATA_SECT_SIZE); } else { /* sector count, 64-bit */ ATA_SCSI_RBUF_SET(0, last_lba >> (8 * 7)); ATA_SCSI_RBUF_SET(1, last_lba >> (8 * 6)); ATA_SCSI_RBUF_SET(2, last_lba >> (8 * 5)); ATA_SCSI_RBUF_SET(3, last_lba >> (8 * 4)); ATA_SCSI_RBUF_SET(4, last_lba >> (8 * 3)); ATA_SCSI_RBUF_SET(5, last_lba >> (8 * 2)); ATA_SCSI_RBUF_SET(6, last_lba >> (8 * 1)); ATA_SCSI_RBUF_SET(7, last_lba); /* sector size */ ATA_SCSI_RBUF_SET(10, ATA_SECT_SIZE >> 8); ATA_SCSI_RBUF_SET(11, ATA_SECT_SIZE); } return 0;}/** * ata_scsiop_report_luns - Simulate REPORT LUNS command * @args: device IDENTIFY data / SCSI command of interest. * @rbuf: Response buffer, to which simulated SCSI cmd output is sent. * @buflen: Response buffer length. * * Simulate REPORT LUNS command. * * LOCKING: * spin_lock_irqsave(host lock) */unsigned int ata_scsiop_report_luns(struct ata_scsi_args *args, u8 *rbuf, unsigned int buflen){ VPRINTK("ENTER\n"); rbuf[3] = 8; /* just one lun, LUN 0, size 8 bytes */ return 0;}/** * ata_scsi_set_sense - Set SCSI sense data and status * @cmd: SCSI request to be handled * @sk: SCSI-defined sense key * @asc: SCSI-defined additional sense code * @ascq: SCSI-defined additional sense code qualifier * * Helper function that builds a valid fixed format, current * response code and the given sense key (sk), additional sense * code (asc) and additional sense code qualifier (ascq) with * a SCSI command status of %SAM_STAT_CHECK_CONDITION and * DRIVER_SENSE set in the upper bits of scsi_cmnd::result . * * LOCKING: * Not required */void ata_scsi_set_sense(struct scsi_cmnd *cmd, u8 sk, u8 asc, u8 ascq){ cmd->result = (DRIVER_SENSE << 24) | SAM_STAT_CHECK_CONDITION; cmd->sense_buffer[0] = 0x70; /* fixed format, current */ cmd->sense_buffer[2] = sk; cmd->sense_buffer[7] = 18 - 8; /* additional sense length */ cmd->sense_buffer[12] = asc; cmd->sense_buffer[13] = ascq;}/** * ata_scsi_badcmd - End a SCSI request with an error * @cmd: SCSI request to be handled * @done: SCSI command completion function * @asc: SCSI-defined additional sense code * @ascq: SCSI-defined additional sense code qualifier * * Helper function that completes a SCSI command with * %SAM_STAT_CHECK_CONDITION, with a sense key %ILLEGAL_REQUEST * and the specified additional sense codes. * * LOCKING: * spin_lock_irqsave(host lock) */void ata_scsi_badcmd(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *), u8 asc, u8 ascq){ DPRINTK("ENTER\n"); ata_scsi_set_sense(cmd, ILLEGAL_REQUEST, asc, ascq); done(cmd);}static void atapi_sense_complete(struct ata_queued_cmd *qc){ if (qc->err_mask && ((qc->err_mask & AC_ERR_DEV) == 0)) { /* FIXME: not quite right; we don't want the * translation of taskfile registers into * a sense descriptors, since that's only * correct for ATA, not ATAPI */ ata_gen_passthru_sense(qc); } qc->scsidone(qc->scsicmd); ata_qc_free(qc);}/* is it pointless to prefer PIO for "safety reasons"? */static inline int ata_pio_use_silly(struct ata_port *ap){ return (ap->flags & ATA_FLAG_PIO_DMA);}static void atapi_request_sense(struct ata_queued_cmd *qc){ struct ata_port *ap = qc->ap; struct scsi_cmnd *cmd = qc->scsicmd; DPRINTK("ATAPI request sense\n"); /* FIXME: is this needed? */ memset(cmd->sense_buffer, 0, sizeof(cmd->sense_buffer)); ap->ops->tf_read(ap, &qc->tf); /* fill these in, for the case where they are -not- overwritten */ cmd->sense_buffer[0] = 0x70; cmd->sense_buffer[2] = qc->tf.feature >> 4; ata_qc_reinit(qc); ata_sg_init_one(qc, cmd->sense_buffer, sizeof(cmd->sense_buffer)); qc->dma_dir = DMA_FROM_DEVICE; memset(&qc->cdb, 0, qc->dev->cdb_len); qc->cdb[0] = REQUEST_SENSE; qc->cdb[4] = SCSI_SENSE_BUFFERSIZE; qc->tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; qc->tf.command = ATA_CMD_PACKET; if (ata_pio_use_silly(ap)) { qc->tf.protocol = ATA_PROT_ATAPI_DMA; qc->tf.feature |= ATAPI_PKT_DMA; } else { qc->tf.protocol = ATA_PROT_ATAPI; qc->tf.lbam = SCSI_SENSE_BUFFERSIZE; qc->tf.lbah = 0; } qc->nbytes = SCSI_SENSE_BUFFERSIZE; qc->complete_fn = atapi_sense_complete; ata_qc_issue(qc); DPRINTK("EXIT\n");}static void atapi_qc_complete(struct ata_queued_cmd *qc){ struct scsi_cmnd *cmd = qc->scsicmd; unsigned int err_mask = qc->err_mask; VPRINTK("ENTER, err_mask 0x%X\n", err_mask); /* handle completion from new EH */ if (unlikely(qc->ap->ops->error_handler && (err_mask || qc->flags & ATA_QCFLAG_SENSE_VALID))) { if (!(qc->flags & ATA_QCFLAG_SENSE_VALID)) { /* FIXME: not quite right; we don't want the * translation of taskfile registers into a * sense descriptors, since that's only * correct for ATA, not ATAPI */ ata_gen_passthru_sense(qc); } /* SCSI EH automatically locks door if sdev->locked is * set. Sometimes door lock request continues to * fail, for example, when no media is present. This * creates a loop - SCSI EH issues door lock which * fails and gets invoked again to acquire sense data * for the failed command. * * If door lock fails, always clear sdev->locked to * avoid this infinite loop. */ if (qc->cdb[0] == ALLOW_MEDIUM_REMOVAL) qc->dev->sdev->locked = 0; qc->scsicmd->result = SAM_STAT_CHECK_CONDITION; qc->scsidone(cmd); ata_qc_free(qc); return; } /* successful completion or old EH failure path */ if (unlikely(err_mask & AC_ERR_DEV)) { cmd->result = SAM_STAT_CHECK_CONDITION; atapi_request_sense(qc); return; } else if (unlikely(err_mask)) { /* FIXME: not quite right; we don't want the * translation of taskfile registers into * a sense descriptors, since that's only * correct for ATA, not ATAPI */ ata_gen_passthru_sense(qc); } else { u8 *scsicmd = cmd->cmnd; if ((scsicmd[0] == INQUIRY) && ((scsicmd[1] & 0x03) == 0)) { u8 *buf = NULL; unsigned int buflen; buflen = ata_scsi_rbuf_get(cmd, &buf); /* ATAPI devices typically report zero for their SCSI version, * and sometimes deviate from the spec WRT response data * format. If SCSI version is reported as zero like normal, * then we make the following fixups: 1) Fake MMC-5 version, * to indicate to the Linux scsi midlayer this is a modern * device. 2) Ensure response data format / ATAPI information * are always correct. */ if (buf[2] == 0) { buf[2] = 0x5; buf[3] = 0x32; } ata_scsi_rbuf_put(cmd, buf); } cmd->result = SAM_STAT_GOOD; } qc->scsidone(cmd); ata_qc_free(qc);}/** * atapi_xlat - Initialize PACKET taskfile * @qc: command structure to be initialized * * LOCKING: * spin_lock_irqsave(host lock) * * RETURNS: * Zero on success, non-zero on failure. */static unsigned int atapi_xlat(struct ata_queued_cmd *qc){ struct scsi_cmnd *scmd = qc->scsicmd; struct ata_device *dev = qc->dev; int using_pio = (dev->flags & ATA_DFLAG_PIO); int nodata = (scmd->sc_data_direction == DMA_NONE); unsigned int nbytes; memset(qc->cdb, 0, dev->cdb_len); memcpy(qc->cdb, scmd->cmnd, scmd->cmd_len); qc->complete_fn = atapi_qc_complete; qc->tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; if (scmd->sc_data_direction == DMA_TO_DEVICE) { qc->tf.flags |= ATA_TFLAG_WRITE; DPRINTK("direction: write\n"); } qc->tf.command = ATA_CMD_PACKET; qc->nbytes = scsi_bufflen(scmd); /* check whether ATAPI DMA is safe */ if (!using_pio && ata_check_atapi_dma(qc)) using_pio = 1; /* Some controller variants snoop this value for Packet * transfers to do state machine and FIFO management. Thus we * want to set it properly, and for DMA where it is * effectively meaningless. */ nbytes = min(qc->nbytes, (unsigned int)63 * 1024); /* Most ATAPI devices which honor transfer chunk size don't * behave according to the spec when odd chunk size which * matches the transfer length is specified. If the number of
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -